/* * scorer.c * Copyright Guy Albertelli II 1997 */ #include #include #include #include #ifdef NeXT #endif #ifdef linux #include #include #include #include #include #endif #include "Capa/capaCommon.h" #include "scorer.h" extern int Parsemode_f; void initScreen() { printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"); printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"); printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"); printf("Welcome to Scorer, the Automated Scoring Office converter.\n"); printf("By: Guy Albertelli II\n"); printf("Version %s.%s for %s\n\n\n",MAJORVER,MINORVER,ARCHSTR); } void getClassInfo(Flags *flags,FILE** scantron) { char buffer[MAX_LINE_LENGTH],buffer2[MAX_LINE_LENGTH]; int done=FALSE; printf("What is the class name?"); scanf("%s",flags->Class); flags->Class[8]='\0'; printf("What is the SetId?"); scanf("%d",&(flags->SetId)); while(!done) { printf("What is the name of the scoring offices report file?"); scanf("%s",buffer); *scantron=fopen(buffer,"r"); if (*scantron != NULL) { rewind(*scantron); if (EOF == fscanf(*scantron,"%s",buffer2)) printf("This file appears to be empty. Please enter a new name.\n"); else { rewind(*scantron); done=1; } } else printf("Unable to open the report file %s\n",buffer); } } int openOutputFile(char filename[MAX_LINE_LENGTH], FILE ** outputFile) { char buf[MAX_LINE_LENGTH]; *outputFile=fopen(filename,"r+"); if (*outputFile != NULL) { rewind(*outputFile); if (EOF == fscanf(*outputFile,"%s",buf)) buf[0]='\0'; } if (*outputFile==NULL || buf[0]=='\0') { if (*outputFile != NULL) fclose(*outputFile); *outputFile=fopen(filename,"w+"); if (*outputFile==NULL) { fprintf(stderr,"Unable to open the output file %s\n",filename); return SCO_FATALERROR; } else return SCO_NEW; } else { rewind(*outputFile); return SCO_EXIST; } } void initValues(Question questions[MAX_QUEST],Flags *flags) { int i; flags->NumQuestions=0; flags->CheckPIN=TRUE; flags->AnonMode=FALSE; flags->CheckSpaces=FALSE; flags->SurveyMode=FALSE; flags->SurveyHeader=FALSE; flags->Pause=FALSE; flags->PauseTime=0.0; flags->IdFormat=ANUMBER_FORMAT; flags->CheckMultipleMarks=FALSE; flags->QueryAboutPID=FALSE; flags->log=TRUE; for(i=0;iClass)!=0) { printf("Classname as found in the current scorer.output file: %s\n", prevClass); printf("Classname as entered: %s\n",flags->Class); printf("Please select one as correct. previous or new (p/n).\n"); done=FALSE; while(!done) { scanf("%s",buf); switch(buf[0]) { case 'n':case'N': done=TRUE; break; case 'p':case'P': done=TRUE; strcpy(flags->Class,prevClass); break; default: printf("Please enter one of previous(p) or new(n).\n"); break; } } } if ( prevSetId != flags->SetId ) { printf("SetId as found in the current scorer.output file: %d\n", prevSetId); printf("SetId as entered: %d\n",flags->SetId); printf("Please select one as correct. previous or new (p/n).\n"); done=FALSE; while(!done) { scanf("%s",buf); switch(buf[0]) { case 'n':case'N': done=TRUE; break; case 'p':case'P': done=TRUE; flags->SetId=prevSetId; break; default: printf("Please enter one of previous(p) or new(n).\n"); break; } } } flags->NumQuestions=prevNumQuestions; flags->CheckPIN=(int)(prevFlags[0]-'0'); flags->AnonMode=(int)(prevFlags[1]-'0'); flags->CheckSpaces=(int)(prevFlags[2]-'0'); flags->SurveyMode=(int)(prevFlags[3]-'0'); flags->SurveyHeader=(int)(prevFlags[4]-'0'); flags->Pause=(int)(prevFlags[5]-'0'); flags->PauseTime=pauseTime; flags->IdFormat=(int)(prevFlags[6]-'0'); flags->CheckMultipleMarks=(int)(prevFlags[7]-'0'); flags->QueryAboutPID=(int)(prevFlags[8]-'0'); flags->log=(int)(prevFlags[9]-'0'); for(i=0;iNumQuestions;i++) { questions[i].type=prevQuestions[3*i]; questions[i].points=(int)(prevQuestions[3*i+1]-'0'); questions[i].leafs=(int)(prevQuestions[3*i+2]-'0'); } for(i=flags->NumQuestions;iSurveyMode) { printf("%d. The Paper being graded is a Survey.\n",++i); flagToModify[i]=SURVEY_MODE; if(flags->SurveyHeader) printf("%d. The Survey has a header.\n",++i); else printf("%d. The Survey does not have a header.\n",++i); flagToModify[i]=SURVEY_HEADER; } else { printf("%d. The paper being graded is an Exam or Quiz.\n",++i); flagToModify[i]=SURVEY_MODE; if(flags->CheckPIN) { printf("%d. The coded CAPA ID will be checked for validity.\n",++i); flagToModify[i]=CHECK_PIN; if(flags->AnonMode) { printf("%d. Scorer will run in Anonymous Mode. (search for \n\tthe correct Student Number based on the CAPA ID)\n",++i); flagToModify[i]=ANON_MODE; if(flags->QueryAboutPID) printf("%d. Scorer will ask which PID to use if multiple are found.\n",++i); else printf("%d. Scorer will pick highest score if multiple PIDs found.\n",++i); flagToModify[i]=QUERY_ABOUT_PID; } else { printf("%d. Scorer will not run in Anonymous Mode. (Bubbled \n\tstudent number is correct for the CAPA ID.)\n",++i); flagToModify[i]=ANON_MODE; } } else { printf("%d. The coded CAPA ID will be ignored.\n",++i); flagToModify[i]=CHECK_PIN; } if(flags->CheckSpaces) { if (flags->log) printf("%d. Scorer will log sheets containing blank answers.\n",++i); else printf("%d. Scorer will issue a warning and pause when encountering blank answers.\n",++i); } else { printf("%d. Scorer will ignore blank answers.\n",++i); } flagToModify[i]=CHECK_SPACES; if(flags->CheckMultipleMarks) { if (flags->log) printf("%d. Scorer will log answer sheets containing multiple marks.\n",++i); else printf("%d. Scorer will issue a warning and pause when encountering multiple marks.\n",++i); } else { printf("%d. Scorer will mark multiple marks wrong.\n",++i); } flagToModify[i]=MULTIPLE_MARKS; switch(flags->IdFormat) { case ANUMBER_FORMAT: printf("%d. The StudentId is in A format.\n",++i); break; case SOC_SEC_FORMAT: printf("%d. The StudentId is in Social Security format.\n",++i); break; default: flags->IdFormat=ANUMBER_FORMAT; printf("%d. The StudentId is in A format.\n",++i); break; } flagToModify[i]=ID_FORMAT; } if (flags->Pause) printf("%d. The program will pause %.2f seconds between papers.\n",++i, flags->PauseTime); else printf("%d. The program will process papers as quickly as possible.\n", ++i); flagToModify[i]=PAUSE_TIME; if (flags->log) printf("%d. The program will log all errors.\n",++i); else printf("%d. The program will query user on all errors.\n",++i); flagToModify[i]=LOGGING; return i; } int getYesOrNo() { int done=FALSE; char buf[MAX_LINE_LENGTH]; while(!done) { scanf("%s",buf); switch(buf[0]) { case 'n':case'N': return NO; done=TRUE; break; case 'y':case'Y': return YES; done=TRUE; break; default: printf("Please enter y or n."); done=FALSE; break; } } exit(E_GETYESNO); return -1; } void modifyFlag(Flags *flags,int *flagToModify,int i) { int valid=FALSE; int done=FALSE; char buf[MAX_LINE_LENGTH]; switch(flagToModify[i]) { case CHECK_PIN: printf("Should the student coded CAPA ID be checked for correctness?\n"); printf("(y or n)"); flags->CheckPIN=getYesOrNo(); break; case ANON_MODE: printf("Should scorer run in Anonymous mode (search for correct \n"); printf("Student Number based on the CAPA ID)? (y or n)"); flags->AnonMode=getYesOrNo(); break; case QUERY_ABOUT_PID: printf("Should scorer Query you when multiple PIDs match a single PIN.(y or n)\n"); flags->QueryAboutPID=getYesOrNo(); break; case CHECK_SPACES: printf("Should scorer check for blank answers and issue a warning \n"); printf("when one is encountered? (y or n)"); flags->CheckSpaces=getYesOrNo(); break; case MULTIPLE_MARKS: printf("Should scorer check for multiple marks and issue a warning \n"); printf("when encountered? (y or n)"); flags->CheckMultipleMarks=getYesOrNo(); break; case SURVEY_MODE: printf("Is this a survey?(y or n)"); flags->SurveyMode=getYesOrNo(); break; case SURVEY_HEADER: printf("Does the form include a Header?(y or n)"); flags->SurveyHeader=getYesOrNo(); break; case PAUSE_TIME: printf("Do you wish for there to be a pause between papers?(y or n)"); flags->Pause=getYesOrNo(); if (flags->Pause) { printf("Please enter the minimum delay between papers in seconds.\n"); valid=FALSE; while(!valid) { scanf("%s",buf); flags->PauseTime=(float)atof(buf); if (flags->PauseTime < 0.00001) { printf("You have entered a rather short time, are you sure you meant %f seconds?",flags->PauseTime); switch(getYesOrNo()) { case NO: printf("Please enter the minimum delay between papers.\n"); break; case YES: valid=TRUE; break; default: fprintf(stderr,"getYesOrNo returned a bad result\n"); exit(E_GETYESNO); break; } } else valid=TRUE; } } else flags->PauseTime=0.0; break; case ID_FORMAT: printf("Is the StudentID Format A or Social Security? (a or s)"); while(!done) { scanf("%s",buf); switch(buf[0]) { case 'a':case'A': flags->IdFormat=ANUMBER_FORMAT; done=TRUE; break; case 's':case'S': flags->IdFormat=SOC_SEC_FORMAT; done=TRUE; break; default: printf("Please enter a or s."); done=FALSE; break; } } break; case LOGGING: printf("Should Scorer log errors?(y or n)"); flags->log=getYesOrNo(); break; default: fprintf(stderr,"Corrupted information in modifyFlag, i=%d\n",i); fprintf(stderr,"flagToModify[i]=%d",flagToModify[i]); exit(E_MODIFYFLAG); break; } } void checkFlagConsistency(Flags *flags) { if(flags->SurveyMode) { flags->CheckPIN=FALSE; flags->AnonMode=FALSE; flags->QueryAboutPID=FALSE; flags->CheckSpaces=FALSE; flags->CheckMultipleMarks=FALSE; } else { flags->SurveyHeader=FALSE; if(flags->CheckPIN); else flags->AnonMode=FALSE; } if (!flags->AnonMode) flags->QueryAboutPID=FALSE; } void getFlags(Flags *flags) { int done=FALSE,valid=FALSE,i,flagToModify[MAX_NUM_FLAG]; char buf[MAX_LINE_LENGTH]; while(!done) { i=printFlagMenu(flags,flagToModify); printf("Please select which one of the above (1-%d) you wish to \n",i); printf("change. Otherwise enter 0 (zero) to continue.\n"); valid=FALSE; while(!valid) { scanf("%s",buf); if (isdigit(buf[0])) { i=atoi(buf); valid=TRUE; } else printf("Please enter a number between (0-%d)\n",i); } if (i) modifyFlag(flags,flagToModify,i); else done=TRUE; } checkFlagConsistency(flags); } void printQuestions(Question questions[MAX_QUEST],int numQuestions) { int i; for(i=0;i, <, =, "); break; case TF: printf("True or False. "); break; case ASSIGNED: printf("Assigned Score. "); break; case N_OUT_OF_M: printf("Pick N out of M. "); break; case SINGLE_DIGIT: printf("Single digit answer. "); break; case STRING_MATCH: printf("Exact string matching.(10 or less bubbles) "); break; default: fprintf(stderr,"\nCorrupt data in Questions struct while inside printQuestions. Dying\n"); exit(E_PRINTQUESTIONS); break; } printf(" %d leafs, Worth: %d points\n",questions[i].leafs, questions[i].points); } } int wantToChangeQuestions() { int done=FALSE; char buf[MAX_LINE_LENGTH]; printf("Do you want to change any of the above?(y or n)\n"); while(!done) { scanf("%s",buf); switch(buf[0]) { case 'n':case'N': return 0; done=TRUE; break; case 'y':case 'Y': return 1; done=TRUE; break; default: printf("Please enter either (y or n).\n"); break; } } return -1; } void printQuestionMenu() { printf("For Each Question enter \"%c\" for a one out of 8\n",ONE_OUT_OF_8); printf(" \"%c\" for a GLE type\n",GLE); printf(" \"%c\" for a TF type.\n",TF); printf(" \"%c\" for an assigned score.\n",ASSIGNED); printf(" \"%c\" for an n out of m.\n",N_OUT_OF_M); printf(" \"%c\" for single digit answer.\n", SINGLE_DIGIT); printf(" \"%c\" for exact string matching (8 or less bubbles)\n", STRING_MATCH); printf(" \"%c\" to print this menu again\n", QUESTION_MENU); printf(" \"%c\" to stop entering questions\n", QUESTION_STOP); } int getSingleDigit() { int done=FALSE; char buf[MAX_LINE_LENGTH]; while(!done) { scanf("%s",buf); if (isdigit(buf[0]) && (atoi(buf) >=1) && (atoi(buf) <=9)) { done=TRUE; return atoi(buf); } else printf("Please enter a digit between 1 and 9\n"); } return 0; } int getNewQuestion(Question questions[MAX_QUEST],int numQuestions) { int done=FALSE; char buf[MAX_LINE_LENGTH]; printf("Question#%2d: (enter %c to see menu): ",numQuestions+1,QUESTION_MENU); while(!done) { scanf("%s",buf); questions[numQuestions].leafs=1; switch(buf[0]) { case GLE: case TF: printf("How many parts to the problem?"); if (!(questions[numQuestions].leafs=getSingleDigit())) { fprintf(stderr,"Weird result from getSingleDigit\n"); exit(E_SINGLEDIGIT); } case ONE_OUT_OF_8: case ASSIGNED: case SINGLE_DIGIT: case STRING_MATCH: printf("How many points are possible?"); done=TRUE; break; case N_OUT_OF_M: printf("What is m in the problem?"); if (!(questions[numQuestions].leafs=getSingleDigit())) { fprintf(stderr,"Weird result from getSingleDigit\n"); exit(E_SINGLEDIGIT); } printf("How many points are possible?"); done=TRUE; break; case QUESTION_MENU: printQuestionMenu(); printf("Question#%2d: ",numQuestions+1); break; case QUESTION_STOP: return 0; break; default: printf("Please enter one of %c,%c,%c,%c,%c,%c,%c,%c,%c.\n", ONE_OUT_OF_8,GLE,TF,ASSIGNED,N_OUT_OF_M,SINGLE_DIGIT, STRING_MATCH,QUESTION_MENU,QUESTION_STOP); printf("Question#%2d: ",numQuestions+1); break; } } questions[numQuestions].type=buf[0]; if (!(questions[numQuestions].points=getSingleDigit())) { fprintf(stderr,"Weird result from getSingleDigit\n"); exit(E_SINGLEDIGIT); } return 1; } void printModifyQuestionMenu() { printf("(a)dd a question\n"); printf("(c)hange a question\n"); printf("(d)elete a question\n"); printf("(f)inished changing\n"); } void addQuestion(Question questions[MAX_QUEST],int *numQuestions) { printQuestionMenu(); if (getNewQuestion(questions,*numQuestions)) (*numQuestions)=(*numQuestions)+1; } void changeQuestion(Question questions[MAX_QUEST],int numQuestions) { int done=FALSE; char buf[MAX_LINE_LENGTH]; printf("Which question do you wish to change?(1-%d) ",numQuestions); printf("Or 0 for none\n"); while(!done) { scanf("%s",buf); if(isdigit(buf[0]) && (atoi(buf) >= 0) && (atoi(buf) <= numQuestions)) done=TRUE; else printf("Please enter a number between 0 and %d.\n",numQuestions); } if (atoi(buf) !=0) { printQuestionMenu(); getNewQuestion(questions,atoi(buf)-1); } } void deleteQuestion(Question questions[MAX_QUEST],int *numQuestions) { int done=FALSE,i; char buf[MAX_LINE_LENGTH]; printf("Which question do you wish to delete?(1-%d) ",*numQuestions); printf("Or 0 for none\n"); while(!done) { scanf("%s",buf); if(isdigit(buf[0]) && (atoi(buf) >= 0) && (atoi(buf) <= (*numQuestions))) done=TRUE; else printf("Please enter a number between 0 and %d.\n",*numQuestions); } if (atoi(buf) !=0) { for(i=atoi(buf)-1;i<(*numQuestions)-1;i++) { questions[i].type=questions[i+1].type; questions[i].points=questions[i+1].points; questions[i].leafs=questions[i+1].leafs; } (*numQuestions)=(*numQuestions)-1; } } void modifyQuestions(Question questions[MAX_QUEST],int *numQuestions) { int done=FALSE; char buf[MAX_LINE_LENGTH]; while(!done) { printQuestions(questions,*numQuestions); printModifyQuestionMenu(); scanf("%s",buf); switch(buf[0]) { case 'a': addQuestion(questions,numQuestions); break; case 'c': changeQuestion(questions,*numQuestions); break; case 'd': deleteQuestion(questions,numQuestions); break; case 'f': done=TRUE; break; default: printf("Please enter one of a, c, d, f\n"); break; } } } int getNumSurveyQuest() { int numQuestions=0,done=FALSE; char buf[MAX_LINE_LENGTH]; printf("Please enter the number of questions on the Survey.\n"); while(!done) { scanf("%s",buf); if(isdigit(buf[0])) { numQuestions=atoi(buf); done=TRUE; } else { printf("Please enter a number.\n"); } } return numQuestions; } void getQuestionInfo(Question questions[MAX_QUEST],Flags *flags) { int done=FALSE,i; if (flags->SurveyMode) { flags->NumQuestions=getNumSurveyQuest(); for (i=0;iNumQuestions;i++) { questions[i].type=ASSIGNED; questions[i].points=9; questions[i].leafs=1; } } else { if (flags->NumQuestions!=0) { printQuestions(questions,flags->NumQuestions); if (!wantToChangeQuestions()) return; } else { printQuestionMenu(); while(!done) if (getNewQuestion(questions,flags->NumQuestions)) flags->NumQuestions++; else done=TRUE; } modifyQuestions(questions,&(flags->NumQuestions)); } } void getInfo(FILE ** outputFile,FILE **scantron, Question questions[MAX_QUEST],Flags *flags) { char filename[MAX_LINE_LENGTH]; getClassInfo(flags,scantron); sprintf(filename,"records/scorer.output.%d",flags->SetId); switch(openOutputFile(filename,outputFile)) { case SCO_FATALERROR: exit (E_OPENOUTPUT); break; case SCO_NEW: initValues(questions,flags); break; case SCO_EXIST: getPreviousValues(*outputFile,questions,flags); break; default: fprintf(stderr,"openOutputFile returned an invalid code. Dying\n"); exit(E_OPENOUTPUTRETURN); break; } getFlags(flags); getQuestionInfo(questions,flags); } void saveInfo(FILE *outputFile,Question questions[MAX_QUEST],Flags *flags) { char configLine[CONFIG_LINE_LENGTH+2],buf[MAX_LINE_LENGTH], buf2[MAX_LINE_LENGTH],logname[MAX_LINE_LENGTH]; int i; for(i=0;iNumQuestions;i++) { buf[3*i]=questions[i].type; buf[3*i+1]=((char)questions[i].points)+'0'; buf[3*i+2]=((char)questions[i].leafs)+'0'; } buf[3*i]='\0'; buf2[0]=((char)flags->CheckPIN)+'0'; buf2[1]=((char)flags->AnonMode)+'0'; buf2[2]=((char)flags->CheckSpaces)+'0'; buf2[3]=((char)flags->SurveyMode)+'0'; buf2[4]=((char)flags->SurveyHeader)+'0'; buf2[5]=((char)flags->Pause)+'0'; buf2[6]=((char)flags->IdFormat)+'0'; buf2[7]=((char)flags->CheckMultipleMarks)+'0'; buf2[8]=((char)flags->QueryAboutPID)+'0'; buf2[9]=((char)flags->log)+'0'; buf2[10]='\0'; sprintf(configLine,"%s %d %d %s %f %s",flags->Class,flags->SetId, flags->NumQuestions,buf2,flags->PauseTime,buf); for(i=strlen(configLine);ilog) { sprintf(logname,"records/scorer.log.%d",flags->SetId); flags->logFile=fopen(logname,"w"); if (flags->logFile == NULL) { fprintf(stderr,"Unable to open the logFile %s\n",logname); exit(E_LOGERROR); } } } void logMultipleAnon(PIDPINlist PIDandPINlist[MAX_SECTION_SIZE], int matches[MAX_PID_MATCHES],Student* student, Flags* flags,int maxTotal,int total[MAX_PID_MATCHES]) { int i; fprintf(flags->logFile,"#Multiple matching PINs for PID %s :", student->answerPID); for(i=0;ilogFile," %s score: %d,", PIDandPINlist[matches[i]].PID,total[i]); } flags->loggedErrors++; fprintf(flags->logFile," picked: %s, serialNumber: %s\n", PIDandPINlist[matches[maxTotal]].PID,student->serialNumber); fprintf(flags->logFile,"%s",student->origString); } void logError(Student *student,Flags *flags,int errorCode) { flags->loggedErrors++; student->error=1; switch(errorCode) { case LOG_NOPINFOUND: fprintf(flags->logFile,"#In Anonymous Mode no Student ID could be found for the PIN encoded, PIN=%s (not graded)",student->PIN); break; case LOG_PINWRONG: if (flags->AnonMode) fprintf(flags->logFile,"#The coded PIN had errors (not graded)"); else fprintf(flags->logFile,"#The coded PIN had errors, assumed zero for bad responses"); break; case LOG_STUDENTNOTEXIST: fprintf(flags->logFile,"#The Student ID %s was not found in the classl file(not graded)",student->answerPID); break; case LOG_PINNOTMATCH: fprintf(flags->logFile,"#The student coded an incorrect PIN(%s) for the specified PID(%s) using the classl PIN, %04d",student->PIN,student->answerPID,student->classlPIN); break; case LOG_BADPID: fprintf(flags->logFile,"#The coded Student ID(%s) was not found in the classl file (not graded)",student->answerPID); break; case LOG_BADPIN: fprintf(flags->logFile,"#The coded PIN (%s) was incorrect(notGraded)",student->PIN); break; case LOG_SPACES: fprintf(flags->logFile,"#The student had blank answers"); break; case LOG_MULTIPLEMARKS: fprintf(flags->logFile,"#The student had multiple marks on single mark questions"); break; default: fprintf(flags->logFile,"#And unknown error %d occured with this student",errorCode); break; } fprintf(flags->logFile," serialNumber: %s.\n",student->serialNumber); } /* stolen from allpin.c and modified by Guy Albertelli*/ int buildPIDandPINlist(int setId, PIDPINlist PIDandPINlist[MAX_SECTION_SIZE] ) { int i=0,numStudents; T_student *curStudent,*headStudent; printf("Building lists of CapaIDs and PIDs\n"); numStudents=capa_get_section(&headStudent, 0); curStudent=headStudent; for(i=0;curStudent;curStudent=curStudent->s_next,i++) { strcpy(PIDandPINlist[i].PID,curStudent->s_sn); PIDandPINlist[i].PIN=capa_PIN(curStudent->s_sn,setId,0); } free_students(headStudent); printf("\nDone\n"); return numStudents; } int getForm(Student **newStudent,FILE *scantron, Question questions[MAX_QUEST],Flags *flags) { char buffer[MAX_LINE_LENGTH]; char *eof; int i=0,h=0,j=0,q=0,space=0,found,stepsize,pin,result=0,multiplemarks=0; int done=FALSE,pinWrong=FALSE; static int formNumber; char * array; if (flags->Pause) usleep(flags->PauseTime*1000000); while(!done) { eof=fgets(buffer,MAX_LINE_LENGTH,scantron); if (eof==NULL) return GF_EOF; if (buffer[0]!='#') done=TRUE; } flags->linesRead++; if (!flags->log) printf("The Next line is:\n%s\n",buffer); #ifdef DEBUG printf("Interpreting the line.\n"); #endif *newStudent=(Student *)malloc(sizeof(Student)); if (flags->SurveyMode) /* assign a unique student number since there isn't one on the sheet*/ switch(flags->IdFormat) { case ANUMBER_FORMAT: sprintf((*newStudent)->answerPID,"a%08d",formNumber++); break; case SOC_SEC_FORMAT: sprintf((*newStudent)->answerPID,"%09d",formNumber++); break; default: fprintf(stderr,"Invalid IdFormat data in interpretForm,%d\n", flags->IdFormat); exit(E_INVALIDIDFORMAT); break; } else strncpy((*newStudent)->answerPID,&buffer[56],9); (*newStudent)->answerPID[9]='\0'; strncpy((*newStudent)->Name,&buffer[40],16); (*newStudent)->Name[16]='\0'; strncpy((*newStudent)->serialNumber,&buffer[3],6); (*newStudent)->serialNumber[6]='\0'; strncpy((*newStudent)->origString,buffer,SCAN_INPUT_LENGTH); (*newStudent)->origString[SCAN_INPUT_LENGTH]='\0'; (*newStudent)->error=0; /* i is the current position in buffer and h is the current question*/ for(i=0;iNumQuestions;i++) { array=LETTER; stepsize=2; switch(questions[i].type) { case ASSIGNED: case SINGLE_DIGIT: /*the first 5 are special for Anon mode, only first 4 for *otherwise */ if (flags->AnonMode) if (i > 4) array=NUMBER; else if (i > 3) array=NUMBER; case ONE_OUT_OF_8: found=0; for(j=(i*10)+76;j<((i+1)*10)+76;j++) { if (buffer[j]=='1') { h=j; found++; } } if (found > 1) { (*newStudent)->Answers[i][0]=' '; multiplemarks++; } if (found < 1) { (*newStudent)->Answers[i][0]=' '; space++; } if (found == 1) (*newStudent)->Answers[i][0]=array[h-(i*10+76)]; (*newStudent)->Answers[i][1]='\0'; break; case GLE: stepsize=3; case TF: for(j=0; j 1) { (*newStudent)->Answers[i][j]=' '; multiplemarks++; } if (found < 1) { (*newStudent)->Answers[i][j]=' '; space++; } if (found == 1) (*newStudent)->Answers[i][j]=array[q-(i*10+76)]; } } (*newStudent)->Answers[i][questions[i].leafs]='\0'; break; case N_OUT_OF_M: case STRING_MATCH: found=0; for(j=(i*10)+76;j<((i+1)*10)+76;j++) { if (buffer[j]=='1') { (*newStudent)->Answers[i][found]=array[j-(i*10+76)]; found++; } } if (found==0) space++; (*newStudent)->Answers[i][found]='\0'; break; default: fprintf(stderr,"Invalid question type %c for question %d in Interpret form.\n Dying\n",questions[h].type,h); exit(E_INTREPRETFORM); break; } } pin=0; for(i=0;i<4;i++) { pin*=10; switch((*newStudent)->Answers[i][0]) { case 'A': pin+=1;break; case 'B': pin+=2;break; case 'C': pin+=3;break; case 'D': pin+=4;break; case 'E': pin+=5;break; case 'F': pin+=6;break; case 'G': pin+=7;break; case 'H': pin+=8;break; case 'I': pin+=9;break; case 'J': pin+=0;break; case ' ': default: if (!flags->log) fprintf(stderr,"Garbage answer '%c' for PIN question %d", (*newStudent)->Answers[i][0],i); result|=GF_PINWRONG; pinWrong=TRUE; pin+=0;break; } } if (pinWrong && flags->log) logError((*newStudent),flags,LOG_PINWRONG); sprintf((*newStudent)->PIN,"%04d",pin); if (space!=0) result|=GF_SPACES; if (multiplemarks!=0) result|=GF_MULTIPLEMARKS; return result; } int gradeQuestion(Question questions[MAX_QUEST],int questionIndex, Problem_t *problem,Student *student,Flags* flags) { int numRight=0,leafs; char one=1,zero=0; char *ansOn[20],*stuOn[20]; int i,j; int sortArray[256]; char newAnswer[MAX_LINE_LENGTH],*oldAnswer; switch(questions[questionIndex].type) { case ONE_OUT_OF_8: case GLE: case TF: case SINGLE_DIGIT: if (!flags->log) printf("The correct answer:%10s The student's answer:%10s, \t", problem->answer,student->Answers[questionIndex]); for(leafs=0;problem->answer[leafs]!='\0';leafs++) if (problem->answer[leafs]==student->Answers[questionIndex][leafs]) numRight++; if (!flags->log) printf("%d right\n",numRight); break; case ASSIGNED: if (!flags->log) printf("The student got a %s out of %d\n", student->Answers[questionIndex], questions[questionIndex].points); if (isspace(student->Answers[questionIndex][0])) numRight=0; else numRight=(int)(student->Answers[questionIndex][0]-'0'); break; case N_OUT_OF_M: if (!flags->log) printf("The correct answer:%10s The student's answer:%10s, \t", problem->answer,student->Answers[questionIndex]); if (problem->ans_type == ANSWER_IS_CHOICE) { for(i=0;i<255;i++) sortArray[i]=0; for(i=0;i< strlen(problem->answer);i++) sortArray[(int)problem->answer[i]]=1; for(i=0,j=0;i<255;i++) { if (sortArray[i]) { newAnswer[j]=i; j++; } } newAnswer[j]='\0'; if (!flags->log) printf("\nThe sorted correct answer:%10s\t\t\t",newAnswer); oldAnswer=problem->answer; problem->answer=newAnswer; } for(leafs=0;questions[questionIndex].leafs>leafs;leafs++) { ansOn[leafs]=strchr(problem->answer,('A'+(char)leafs)); } for(leafs=0;questions[questionIndex].leafs>leafs;leafs++) { if (ansOn[leafs] != NULL ) ansOn[leafs]=&one; else ansOn[leafs]=&zero; } for(leafs=0;questions[questionIndex].leafs>leafs;leafs++) { stuOn[leafs]=strchr(student->Answers[questionIndex], ('A'+(char)leafs)); } for(leafs=0;questions[questionIndex].leafs>leafs;leafs++) { if (stuOn[leafs] != NULL) stuOn[leafs]=&one; else stuOn[leafs]=&zero; } for(leafs=0;questions[questionIndex].leafs>leafs;leafs++) if (ansOn[leafs] == stuOn[leafs]) numRight++; if (!flags->log) printf("%d right\n",numRight); if (problem->ans_type == ANSWER_IS_CHOICE) problem->answer=oldAnswer; break; case STRING_MATCH: if (!flags->log) printf("The correct answer:%10s The student's answer:%10s, ", problem->answer,student->Answers[questionIndex]); if (problem->ans_type == ANSWER_IS_CHOICE) { for(i=0;i<255;i++) sortArray[i]=0; for(i=0;i< strlen(problem->answer);i++) sortArray[(int)problem->answer[i]]=1; for(i=0,j=0;i<255;i++) { if (sortArray[i]) { newAnswer[j]=i; j++; } } newAnswer[j]='\0'; if (!flags->log) printf("\nThe sorted correct answer:%10s\t\t\t",newAnswer); oldAnswer=problem->answer; problem->answer=newAnswer; } if (!(strcasecmp(problem->answer,student->Answers[questionIndex]))) { if (!flags->log) printf("Answer is correct\n"); numRight=questions[questionIndex].points; } else { if (!flags->log) printf("Answer is wrong\n"); numRight=0; } if (problem->ans_type == ANSWER_IS_CHOICE) problem->answer=oldAnswer; break; default: fprintf(stderr,"Unknown question type while grading, %c.\nDying.\n", questions[questionIndex].type); exit(E_UNKNOWN_QTYPE); break; } return numRight; } int surveyQuestion(Question questions[MAX_QUEST],int questionIndex, Problem_t *problem,Student *student,Flags* flags) { int numRight=0; switch(questions[questionIndex].type) { case ASSIGNED: if (!flags->log) printf("The student responded with %s\n",student->Answers[questionIndex]); if (isspace(student->Answers[questionIndex][0])) numRight=0; else numRight=(int)(student->Answers[questionIndex][0]-'0'); break; case ONE_OUT_OF_8: case GLE: case TF: case SINGLE_DIGIT: case N_OUT_OF_M: case STRING_MATCH: default: fprintf(stderr,"Illegal question type while in SurveyMode, %c.\nDying.\n", questions[questionIndex].type); exit(E_UNKNOWN_QTYPE); break; } return numRight; } long getScorerEntry(FILE *outputFile,char *PID) { char oneline[MAX_LINE_LENGTH],fmtbuf[MAX_LINE_LENGTH], studentNumber[MAX_STUDENT_NUMBER]; int done=FALSE,found=FALSE,offset=0,len=0,next_r=0; rewind(outputFile); sprintf(fmtbuf,"%%%dc",MAX_STUDENT_NUMBER); while(!done) { done=!fgets(oneline,MAX_LINE_LENGTH-1,outputFile); len=strlen(oneline); if (!done) { sscanf(oneline,fmtbuf,studentNumber); if (strncasecmp(studentNumber,PID,MAX_STUDENT_NUMBER)==0) { next_r=ftell(outputFile); offset = next_r-len; done=TRUE; found=TRUE; } } else { fseek(outputFile,0L,SEEK_END); offset=ftell(outputFile); fseek(outputFile,-1L,SEEK_END); while (fgetc(outputFile)=='\n') { offset--; fseek(outputFile,offset,SEEK_SET); } offset= offset+2; found=FALSE; done=TRUE; } } if(!found) offset=-offset; return offset; } void setScorerEntry(FILE * outputFile,char* answerPID,char* name, char* answers,int score, int section, char* answerstring,char* questionPID, char* serialNumber,int offset) { int len=0; char buf[MAX_LINE_LENGTH]; rewind(outputFile); sprintf(buf,"%s %-30s %s %3d %2d %s %s %s\n",answerPID,name,answers, score,section,answerstring,questionPID,serialNumber); len=strlen(buf); fseek(outputFile,abs(offset),0); if(!fwrite(buf,len,1,outputFile)) fprintf(stderr,"Failed write.\n"); } void saveForm(Student *student,FILE *outputFile, Question questions[MAX_QUEST],Flags *flags) { int result,capaQuestions,questionIndex,numRight,total=0; int offset; Problem_t *curProblem,*headProblem; char answerstring[MAX_LINE_LENGTH],grade[MAX_LINE_LENGTH]; char buf[MAX_LINE_LENGTH]; T_student capaStudent; if (!flags->SurveyMode) { switch(capa_get_student(student->answerPID, &capaStudent)) { case 1: break; case 0: if (flags->log) logError(student,flags,LOG_STUDENTNOTEXIST); else { fprintf(stderr,"Student %s was not found.\n",student->answerPID); printf("Type start to continue"); scanf("%s",buf); } return; break; case -1: fprintf(stderr,"Unable to find the classl file while grading\n"); exit(E_CAPA_GET_STUDENT); break; default: fprintf(stderr,"Unknow error from capa_get_student while grading\n"); exit(E_CAPA_GET_STUDENT); break; } result=capa_parse(flags->SetId,&headProblem,student->questionPID, &capaQuestions); curProblem=headProblem; if (result==0 || result == -1) { fprintf(stderr,"The Parse failed: %d\nDying\n",result); exit(E_PARSER); } else if (result != flags->NumQuestions) { fprintf(stderr,"The parser found %d questions, there were supposed to be %d questions.\nDying\n",result,flags->NumQuestions); exit(E_PARSER_DIFFERENT); } for(questionIndex=0;questionIndexNumQuestions;questionIndex++) { numRight=gradeQuestion(questions,questionIndex,curProblem, student,flags); total+=numRight; grade[questionIndex]='0'+(char)numRight; curProblem=curProblem->next; } grade[questionIndex]='\0'; printf("Total right for student %s is %d\n\n",capaStudent.s_nm,total); free_problems(headProblem); } else { strcpy(capaStudent.s_nm,"Unknown "); capaStudent.s_sec=0; headProblem=curProblem=NULL; for(questionIndex=0;questionIndexNumQuestions;questionIndex++) { numRight=surveyQuestion(questions,questionIndex,curProblem, student,flags); total+=numRight; grade[questionIndex]='0'+(char)numRight; } grade[questionIndex]='\0'; } answerstring[0]='\0'; for(questionIndex=0;questionIndexNumQuestions;questionIndex++) strcat(answerstring,student->Answers[questionIndex]); offset=getScorerEntry(outputFile,student->answerPID); setScorerEntry(outputFile,student->answerPID,capaStudent.s_nm,grade, total,capaStudent.s_sec,answerstring,student->questionPID, student->serialNumber,offset); } int getScore(Student *student,Question questions[MAX_QUEST],Flags *flags, char* grade,int gradingMethod) { int score=0,numRight=0,tempScore,i,leafs,points,unit; for(i=0;iNumQuestions;i++) { switch(questions[i].type) { case ONE_OUT_OF_8: case SINGLE_DIGIT: numRight= (int) (grade[i]-'0'); score+=numRight*questions[i].points; break; case STRING_MATCH: /*for STRING_MATCH the score is stroed as the NumRight*/ numRight= (int) (grade[i]-'0'); score+=numRight; break; case GLE: case TF: case N_OUT_OF_M: numRight=(int) (grade[i]-'0'); leafs=questions[i].leafs; points=questions[i].points; unit=(int)ceil((double)points/(double)leafs); if (unit==0) unit=points; switch (gradingMethod) { case CAPA_METHOD: tempScore=points-(2*unit*(leafs-numRight)); break; case LENIENT_METHOD: tempScore=points-(unit*(leafs-numRight)); break; case STRICT: if (numRight==leafs) tempScore=points; else tempScore=0; break; default: fprintf(stderr,"Unknown grading Method. %d\n",gradingMethod); exit(E_GRADINGMETHOD); break; } if (tempScore<0) tempScore=0; score+=tempScore; break; case ASSIGNED: numRight= (int) (grade[i]-'0'); score+=numRight; break; default: fprintf(stderr,"Unknown question type %c\n",questions[i].type); break; } } return score; } void saveAnonForm(Student *student,FILE *outputFile, Question questions[MAX_QUEST], PIDPINlist PIDandPINlist[MAX_SECTION_SIZE], Flags *flags) { int i,j=0,matches[MAX_PID_MATCHES],done=FALSE; int result,capaQuestions,questionIndex,numRight,total[MAX_PID_MATCHES]; int offset,maxTotal=0,score,correctID=-1; Problem_t *curProblem,*headProblem; char answerstring[MAX_LINE_LENGTH],buf[MAX_LINE_LENGTH], grade[MAX_PID_MATCHES][MAX_LINE_LENGTH],buffer[MAX_LINE_LENGTH]; T_student capaStudent; if (!flags->log) printf("Attempting to find a student with CAPA PIN %s.\n",student->PIN); for(i=0;iNumOfStudents;i++) { if (atoi(student->PIN)==PIDandPINlist[i].PIN) { matches[j]=i; j++; } } matches[j]=-1; switch(j) { case 0: if(flags->log) logError(student,flags,LOG_NOPINFOUND); else { printf("No match for PIN %s\n",student->PIN); printf("The current form's PIN is incorrect.\n"); printf("Please type start to continue.\n"); scanf("%s",buf); } return; break; case 1: if (!flags->log) printf("Only one match assuming PID %s\n", PIDandPINlist[matches[0]].PID); strcpy(student->questionPID,PIDandPINlist[matches[0]].PID); saveForm(student,outputFile,questions,flags); return; break; default: break; } /* Only get here if there is more than one valid PID*/ if (!flags->log) printf("Found %d matches.\n",j); maxTotal=-1; for(i=0;ianswerPID, &capaStudent)) { case 1: break; case 0: if (flags->log) logError(student,flags,LOG_STUDENTNOTEXIST); else { fprintf(stderr,"Student %s was not found.\n",student->answerPID); printf("Type start to continue"); scanf("%s",buf); } return; break; case -1: fprintf(stderr,"Unable to find the classl file while grading\n"); exit(E_CAPA_GET_STUDENT); break; default: fprintf(stderr,"Unknown error from capa_get_student while grading\n"); exit(E_CAPA_GET_STUDENT); break; } strcpy(student->questionPID,PIDandPINlist[matches[i]].PID); result=capa_parse(flags->SetId,&headProblem,student->questionPID, &capaQuestions); curProblem=headProblem; if (result==0 || result == -1) { fprintf(stderr,"The Parse failed: %d\nDying\n",result); exit(E_PARSER); } else if (result != flags->NumQuestions) { fprintf(stderr,"The parser found %d questions, there were supposed to be %d questions.\nDying\n",result,flags->NumQuestions); exit(E_PARSER_DIFFERENT); } for(questionIndex=0;questionIndexNumQuestions;questionIndex++) { numRight=gradeQuestion(questions,questionIndex,curProblem, student,flags); if (questionIndex==4) if (numRight==1) correctID=i; total[i]+=numRight; grade[i][questionIndex]='0'+(char)numRight; curProblem=curProblem->next; } if (total[i] > total[maxTotal]) maxTotal=i; grade[i][questionIndex]='\0'; /*printf("Total right for PID %s is %d\n",PIDandPINlist[matches[i]].PID, total[i]);*/ free_problems(headProblem); } if (correctID!=-1) { answerstring[0]='\0'; for(questionIndex=0;questionIndexNumQuestions;questionIndex++) strcat(answerstring,student->Answers[questionIndex]); offset=getScorerEntry(outputFile,student->answerPID); printf("Total right for student %s is %d\n\n",capaStudent.s_nm, total[correctID]); setScorerEntry(outputFile,student->answerPID,capaStudent.s_nm, grade[correctID],total[correctID],capaStudent.s_sec, answerstring,student->questionPID,student->serialNumber, offset); return; } if (!flags->QueryAboutPID && flags->log) { logMultipleAnon(PIDandPINlist,matches,student,flags,maxTotal,total); answerstring[0]='\0'; for(questionIndex=0;questionIndexNumQuestions;questionIndex++) strcat(answerstring,student->Answers[questionIndex]); offset=getScorerEntry(outputFile,student->answerPID); printf("Total right for student %s is %d\n\n",capaStudent.s_nm, total[maxTotal]); setScorerEntry(outputFile,student->answerPID,capaStudent.s_nm, grade[maxTotal],total[maxTotal],capaStudent.s_sec, answerstring,student->questionPID,student->serialNumber, offset); } else { while(!done) { printf("Please press\n"); for(i=0;iquestionPID,PIDandPINlist[matches[i]].PID); score=getScore(student,questions,flags,grade[i], LENIENT_METHOD); printf(" Lenient: %3d ",score); score=getScore(student,questions,flags,grade[i], CAPA_METHOD); printf(" CAPA: %3d ",score); score=getScore(student,questions,flags,grade[i], STRICT); printf(" Strict: %3d \n",score); } scanf("%s",buffer); if (isdigit(buffer[0]) && (atoi(buffer) < (j+1)) && (atoi(buffer) > 0)) { strcpy(student->questionPID, PIDandPINlist[matches[atoi(buffer)-1]].PID); saveForm(student,outputFile,questions,flags); done=TRUE; } } } } int checkForm(Student * student,Flags *flags) { int error,pin; T_student capaStudent; #ifdef DEBUG int i,j; printf("PID:\t%s\nPIN:\t%s\nName:\t%s\n", student->answerPID,student->PIN,student->Name); for(i=0,j=0;iNumQuestions;i++) { printf("Answer %d: %s\n",i+1,student->Answers[i]); } #endif /*DEBUG*/ error=capa_get_student(student->answerPID, &capaStudent); switch(error) { case 1: if (!flags->log) printf("The current student is %s from section %d\n", capaStudent.s_nm,capaStudent.s_sec); break; case 0: if (!flags->log) { printf("Error Finding the student.\n"); printf("The Scantron reported the PID:%s\n",student->answerPID); printf("But this was not found in the classl file.\n"); } return CF_STID; break; case -1: return CF_CLASSL; break; default: fprintf(stderr,"capa_get_student returned an invalid result"); fprintf(stderr,"in CheckForm.\n Error=%d. Dying\n",error); exit(E_CAPA_GET_STUDENT); break; } if (flags->CheckPIN && !flags->AnonMode) { pin=capa_PIN(student->answerPID,flags->SetId,0); if (pin!=atoi(student->PIN)) { if (flags->log) { student->classlPIN=pin; logError(student,flags,LOG_PINNOTMATCH); sprintf(student->PIN,"%04d",pin); } else { printf("There is an error with the students PIN:\n"); printf("The Scantron reported:%s, The Classl file has:%d\n", student->PIN,pin); fprintf(stderr,"If you wish to use what the Classl file reports\n"); fprintf(stderr,"enter y, else enter n.\n"); if (getYesOrNo()==YES) sprintf(student->PIN,"%04d",pin); else return CF_PIN; } } } return CF_NOERROR; } int handleCheckForm(Student *student,FILE *outputFile, PIDPINlist PIDandPINlist[MAX_SECTION_SIZE], Question questions[MAX_QUEST],Flags *flags) { int done=FALSE,error; char buf[MAX_LINE_LENGTH]; switch(error=checkForm(student,flags)) { case CF_STID: if (flags->log) logError(student,flags,LOG_BADPID); else { printf("The current form's Student Id is incorrect.\n"); printf("Please type start to continue.\n"); scanf("%s",buf); } break; case CF_CLASSL: fprintf(stderr,"The classl file was not found in the"); fprintf(stderr," current directory.\n"); fprintf(stderr,"Please try again.\n"); done=TRUE; break; case CF_PIN: if (flags->log) logError(student,flags,LOG_BADPIN); else { fprintf(stderr,"The current form's PIN is incorrect.\n"); fprintf(stderr,"Please type start to continue.\n"); scanf("%s",buf); } break; case CF_NOERROR: printf("Grading and Saving Student %s Serial#: %s\n", student->answerPID,student->serialNumber); if (flags->AnonMode) saveAnonForm(student,outputFile,questions,PIDandPINlist,flags); else { strcpy(student->questionPID,student->answerPID); saveForm(student,outputFile,questions,flags); } break; default: fprintf(stderr,"Unimplemented error in checkForm %d\n",error); exit(E_CHECKFORM); break; } return done; } int handleGetFormStatus(int status,Student *student,FILE *outputFile, PIDPINlist PIDandPINlist[MAX_SECTION_SIZE], Question questions[MAX_QUEST],Flags *flags) { int done=FALSE,save=TRUE; if (status & GF_SPACES) if (flags->CheckSpaces) { if (flags->log) logError(student,flags,LOG_SPACES); else { printf("The current form appears to have some questions left\n"); printf("blank. Please enter yes if you wish to continue \n"); printf("grading of this form.\n"); if (getYesOrNo()==YES) ; else save=FALSE; } } if(status & GF_MULTIPLEMARKS) if (flags->CheckMultipleMarks) { if (flags->log) logError(student,flags,LOG_MULTIPLEMARKS); else { printf("The current form appears to have some questions with\n"); printf("multiple marks on lines that should have only one.\n"); printf("Please enter yes if you wish to continue grading of this form.\n"); if (getYesOrNo()==YES) ; else save=FALSE; } } if (status == GF_EOF) done=TRUE; if ((status & GF_PINWRONG) && flags->AnonMode) return done; if (!done && save) if (!flags->SurveyMode) { #ifdef DEBUG printf("Checking form.\n"); #endif done=handleCheckForm(student,outputFile,PIDandPINlist,questions, flags); } else if (flags->AnonMode) saveAnonForm(student,outputFile,questions,PIDandPINlist,flags); else { strcpy(student->questionPID,student->answerPID); saveForm(student,outputFile,questions,flags); } return done; } void processForms(FILE *outputFile, FILE *scantron, Question questions[MAX_QUEST], Flags *flags) { int done=FALSE; int status; Student *student=NULL; PIDPINlist PIDandPINlist[MAX_SECTION_SIZE]; if (flags->AnonMode) { flags->NumOfStudents=buildPIDandPINlist(flags->SetId,PIDandPINlist); if (flags->NumOfStudents==0) { fprintf(stderr,"buildPIDandPINlists returned 0 students."); exit(E_BUILDPIDPIN); } } while(!done) { #ifdef DEBUG printf("Reading in a new form.\n"); #endif status=getForm(&student,scantron,questions,flags); done=handleGetFormStatus(status,student,outputFile,PIDandPINlist, questions,flags); if (student != NULL) { if (student->error) fprintf(flags->logFile,"%s",student->origString); free(student); } student=NULL; } } void printResults(Flags* flags) { printf("Number scanned %d, number errors logged %d\n.", flags->linesRead,flags->loggedErrors); } int main(int argc, char **argv) { FILE *outputFile; Question questions[MAX_QUEST]; Flags flags; FILE *scantron; Parsemode_f=ASCII_MODE; flags.linesRead=0; flags.loggedErrors=0; initScreen(); getInfo(&outputFile,&scantron,questions,&flags); saveInfo(outputFile,questions,&flags); processForms(outputFile,scantron,questions,&flags); printResults(&flags); return 0; }