#include <stdio.h>
#include <sgtty.h>
#include <libc.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <ctype.h>
#include "Capa/capaCommon.h"
#include "bubbler.h"
int serportfd;
int readportfd;
FILE * readport;
int CheckPIN;
int CompareClassName;
int Anon;
int CheckSpaces;
int SurveyMode;
int SurveyHeader;
int formNumber;
int GetProblems(Question questions[MAXQUEST],char * class,int * setId)
{
int i,q,length;
char clear[100];
printf("What is the class name?");
scanf("%s",class);
printf("What is the SetId?");
scanf("%d",setId);
printf("Is this a survey or exam/quiz?(s or e)");
scanf("%s",clear);
if (clear[0]=='s' || clear[0]=='S')
{
SurveyMode=1;
printf("Does the form include a Header?(y or n)");
scanf("%s",clear);
if (clear[0]=='n' || clear[0]=='N')
{
SurveyHeader=0;
}
else
{
SurveyHeader=1;
}
printf("How many Questions are on the form?");
scanf("%d",&q);
for (i=0;i<q;i++)
{
questions[i].type='d';
questions[i].leafs=questions[i].points=9;
}
}
else
{
SurveyMode=0;
printf("Should bubbler compare the entered class name and SetId with \n");
printf("the encoded information on each paper?(y or n)");
scanf("%s",clear);
if (clear[0]=='N' || clear[0]=='n')
CompareClassName=0;
else
CompareClassName=1;
printf("Should the student coded CAPA ID be checked for correctness?\n");
printf("(y or n)");
scanf("%s",clear);
if (clear[0]=='N' || clear[0]=='n')
CheckPIN=0;
else
{
CheckPIN=1;
printf("Should bubbler run in Anonymous mode (search for correct \n");
printf("Student Number based on the CAPA ID)? (y or n)");
scanf("%s",clear);
if (clear[0]=='N' || clear[0]=='n')
Anon=0;
else
Anon=1;
}
printf("Should bubbler check for blank answers and issue a warning when \n");
printf("one is encountered? (y or n)");
scanf("%s",clear);
if (clear[0]=='N' || clear[0]=='n')
CheckSpaces=0;
else
CheckSpaces=1;
length=strlen(class);
if (length < 8)
{
for(;length<8;length++)
{
class[length]=' ';
}
class[length]='\0';
}
printf("How many Questions are on the form?");
scanf("%d",&q);
printf("For Each Question enter \"a\" for a one out of 8\n");
printf(" \"b\" for a GLE type\n");
printf(" \"c\" for a TF type.\n");
printf(" \"d\" for an assigned score.\n");
printf(" \"e\" for a multiple out of 8.\n");
printf(" \"f\" for single digit string matching.\n");
printf(" \"g\" for exact string matching/\n");
for (i=0;i<q;i++)
{
printf("Question#%2d: ",i+1);
scanf("%1s",&questions[i].type);
fflush(stdin);
switch(questions[i].type)
{
case 'a':
case 'd':
case 'g':
case 'f':
printf("How many Points are possible?");
scanf("%d",&questions[i].points);
questions[i].leafs=questions[i].points;
break;
case 'b':
case 'c':
printf("How many parts to the problem?");
scanf("%d",&questions[i].points);
questions[i].leafs=questions[i].points;
break;
case 'e':
printf("How many possible answers are there?");
scanf("%d",&questions[i].points);
questions[i].leafs=questions[i].points;
break;
default:
printf("Unknown choice.\n");
break;
}
fflush(stdin);
}
}
return q;
}
void serial_open(FILE ** streamserport,char * device)
{
struct sgttyb one;
if ((serportfd=open(device,O_WRONLY,O_CREAT))==-1)
{
fprintf(stderr,"Unable to open serial port.\n");
exit(-1);
}
if ((readportfd=open(device,O_RDONLY,O_CREAT))==-1)
{
fprintf(stderr,"Unable to open serial port for reading.\n");
exit(-1);
}
one.sg_ispeed='\015';
one.sg_ospeed='\015';
one.sg_erase='\177';
one.sg_kill='\024';
one.sg_flags= (short) 3;
#ifdef DEBUG
ioctl(serportfd,TIOCGETP,&two);
printf("%d %d %d %d %d\n",two.sg_ispeed,two.sg_ospeed,
two.sg_erase,two.sg_kill,two.sg_flags);
#endif
ioctl(serportfd,TIOCSETN,&one);
ioctl(readportfd,TIOCSETN,&one);
#ifdef DEBUG
ioctl(serportfd,TIOCGETP,&two);
printf("%d %d %d %d %d\n",two.sg_ispeed,two.sg_ospeed,
two.sg_erase,two.sg_kill,two.sg_flags);
printf("%d %d %d %d %d\n",one.sg_ispeed,one.sg_ospeed,
one.sg_erase,one.sg_kill,one.sg_flags);
#endif
*streamserport=fdopen(serportfd,"w");
readport=fdopen(readportfd,"r");
}
void print(FILE** serport,char * out,int shouldread)
{
int readamount;
char buf[100];
#ifdef DEBUG
printf("%s\n",out);
#endif /*DEBUG*/
write(serportfd,out,strlen(out));
if (shouldread)
{
readamount=read(readportfd,buf,1);
#ifdef DEBUG
printf("%d %d\n",readamount,buf[0]);
#endif
}
}
void send_initform_strings(FILE ** serport,int numQuestions,
Question questions[MAXQUEST])
{
int i, scan,j,Points;
/* this sets up kermit and then sends the correct strings to the scantron
to define what the sheet looks like
*/
char buffer[1024],answerString[1024];
for(i=0;i<1000;i++)
{
buffer[i]='\0';
}
scan=11+numQuestions;
/* resets the scantron*/
sprintf(buffer,".srst\015");
print(serport,buffer, 1);
/* sets the error character to be the '"' character */
sprintf(buffer,".err=34\015");
print(serport,buffer, 1);
/* tell scantron how many scanlines there are*/
sprintf(buffer,".frm=fs %d 0 48 n n n\015",scan);
print(serport,buffer, 1);
/* encoded class info*/
sprintf(buffer,".frm=pa 1 2 44 11 32 l 10 7\015");
print(serport,buffer, 1);
/* the first letter of the student number*/
sprintf(buffer,".frm=mc n n 1 1 3 4 4 4 c 1 2 ab\015");
print(serport,buffer, 1);
/* the remaining digits of the student number*/
sprintf(buffer,".frm=mc n n 1 1 2 6 11 20 c 8 10 0123456789\015");
print(serport,buffer, 1);
/* the PIN number*/
sprintf(buffer,".frm=mc n n 1 1 2 24 11 30 c 4 10 0123456789\015");
print(serport,buffer, 1);
#ifdef DEBUG
printf("%d\n",numQuestions);
#endif /*DEBUG*/
/*sets each question line*/
for (i=0;i<numQuestions;i++)
{
#ifdef DEBUG
printf("%c\n",questions[i].type);
#endif /*DEBUG*/
switch (questions[i].type)
{
case 'a':
sprintf(buffer,".frm=mc n m 1 1 %d 6 %d 20 l 1 8 ABCDEFGH\015",
i+12,i+12);
print(serport,buffer,1);
break;
case 'b':
for(j=0;j<questions[i].points;j++)
{
sprintf(buffer,
".frm=mc n m 1 1 %d %d %d %d l 1 3 GLE\015",
i+12,(j*8)+6,i+12,(j*8)+10);
print(serport,buffer,1);
}
break;
case 'c':
for(j=0;j<questions[i].points;j++)
{
sprintf(buffer,
".frm=mc n m 1 1 %d %d %d %d l 1 2 TF\015",
i+12,(j*6)+6,i+12,(j*6)+8);
print(serport,buffer,1);
}
break;
case 'd':
answerString[0]='\0';
Points=questions[i].points;
for(j=0;j<=Points;j++)
{
sprintf(buffer,"%d",j);
strcat(answerString,buffer);
}
sprintf(buffer,".frm=mc n m 1 1 %d 6 %d %d l 1 %d %s\015",
i+12,i+12,(Points*2)+6,Points+1,answerString);
print(serport,buffer,1);
break;
case 'e':
case 'g':
sprintf(buffer,".frm=mc y n 1 1 %d 6 %d 20 l 1 8 ABCDEFGH\015",
i+12,i+12);
print(serport,buffer,1);
break;
case 'f':
sprintf(buffer,".frm=mc n m 1 1 %d 6 %d 24 l 1 10 0123456789\015",
i+12,i+12);
print(serport,buffer,1);
break;
default:
fprintf(stderr,"Booga Booga, couldn't find that question type to");
fprintf(stderr," tell Scantron about\n %c \n",questions[i].type);
break;
}
}
/* end of the form*/
sprintf(buffer,".frm=ls\015");
print(serport,buffer, 1);
#ifdef DEBUG
fprintf(stderr,"We have sent the data\n");
#endif /*DEBUG*/
}
void SetupScantron(FILE ** serport,int problems,
Question questions[MAXQUEST])
{
serial_open(serport,"/dev/ttyfb");
send_initform_strings(serport,problems,questions);
}
/* stolen from allpin.c and modified by Guy Albertelli*/
int buildPIDandPINlist(int setId, PIDPINlist PIDandPINlist[MAX_SECTION_SIZE] )
{
int i=0,j=0,count=0,numStudents;
int SecCntArry[MAX_SECTION_COUNT], sectionIdx;
student_t Students[2048];
if (count = capa_get_section_count(SecCntArry) != 0)
{
for (sectionIdx=1; sectionIdx <= SecCntArry[0]; sectionIdx++)
{
numStudents=0;
get_section(Students, &numStudents, sectionIdx);
for(i=0;i<numStudents;i++,j++)
{
strcpy(PIDandPINlist[j].PID,Students[i].student_number);
PIDandPINlist[j].PIN=
capa_PIN(Students[i].student_number,setId,0);
}
}
}
return j;
}
/*searches all the possible PIN's for all matches and then asks for
confirmation of which PID to use*/
int findPID(Student* student,PIDPINlist PIDandPINlist[MAX_SECTION_SIZE],
int numOfStudents)
{
int i,j=0,matches[30],selection,error=ENONE;
for(i=0;i<numOfStudents;i++)
{
if (atoi(student->PIN)==PIDandPINlist[i].PIN)
{
matches[j]=i;
j++;
}
}
matches[j]=-1;
switch(j)
{
case 0:
printf("No match for PIN %s\n",student->PIN);
error=ENOONE;
break;
case 1:
printf("Only one match assuming PID %s\n",PIDandPINlist[matches[0]].PID);
strcpy(student->questionPID,PIDandPINlist[matches[0]].PID);
break;
default:
printf("Please press\n");
for(i=0;i<30;i++)
{
if (matches[i]!=-1)
{
printf("%d) for student number %s\n",i,
PIDandPINlist[matches[i]].PID);
}
else
{
i=30;
}
}
scanf("%d",&selection);
if ((selection < j) && (selection > 0))
{
strcpy(student->questionPID,PIDandPINlist[matches[selection]].PID);
}
break;
}
return error;
}
Student * getForm(int * status,FILE ** serport,
Question questions[MAXQUEST])
{
Student *newStudent;
char buffer[1024],buf[1024],buf2[1024];
int readamount=0,done=0,i=0,j=0,h=0,q=0,space=0;
sprintf(buffer,".read 2\015");
print(serport,buffer,1);
while(!done)
{
readamount=read(readportfd,buf,100);
buf[readamount]='\0';
for(i=0;i<readamount;i++,j++)
{
if (buf[i]==13)
{
done=1;
break;
}
buf2[j]=buf[i];
}
buf2[j]='\0';
}
printf("%s\n",buf2);
switch(buf2[0])
{
case '\"':
*status=GFFAILED;
return NULL;
break;
default:
newStudent=(Student *)malloc(sizeof(Student));
if (SurveyMode)
sprintf(newStudent->answerPID,"a%08d",formNumber++);
else
strncpy(newStudent->answerPID,&buf2[10],9);
newStudent->answerPID[9]='\0';
strncpy(newStudent->class,&buf2[2],8);
newStudent->class[8]='\0';
strncpy(newStudent->SetId,&buf2[0],2);
newStudent->SetId[2]='\0';
strncpy(newStudent->PIN,&buf2[19],4);
newStudent->PIN[4]='\0';
i=23;h=0;
while(buf2[i]!='\0')
{
switch(questions[h].type)
{
case 'a':
case 'd':
case 'f':
newStudent->Answers[h][0]=buf2[i];
newStudent->Answers[h][1]='\0';
if (isspace(buf2[i])) space++;
i++;
break;
case 'b':
/*loop through each leaf*/
for(j=0;j<questions[h].points;j++)
{
newStudent->Answers[h][j]=buf2[i];
if (isspace(buf2[i])) space++;
i++;
}
newStudent->Answers[h][j]='\0';
break;
case 'c':
/*loop through each leaf*/
for(j=0;j<questions[h].points;j++)
{
newStudent->Answers[h][j]=buf2[i];
if (isspace(buf2[i])) space++;
i++;
}
newStudent->Answers[h][j]='\0';
break;
case 'e':
case 'g':
for (j=0,q=0;j<8;j++)
{
if (buf2[i]!=' ')
{
newStudent->Answers[h][q]=buf2[i];
q++;
}
i++;
}
if (isspace(buf2[i])) space++;
newStudent->Answers[h][q]='\0';
break;
default:
fprintf(stderr,"Wha? %c",questions[h].type);
i++;
break;
}
h++;
}
*status=GFSUCCESS;
break;
}
if (space!=0) *status=GFSPACES;
return newStudent;
}
int checkForm(Student * student,int numQuestions,char *class,int setId)
{
int error,pin;
student_t capaStudent;
#ifdef DEBUG
printf("PID:\t%s\nclass:\t%s\nSetId:\t%s\nPIN:\t%s\n",
student->answerPID,student->class,student->SetId,student->PIN);
for(i=0,j=0;i<numQuestions;i++)
{
printf("Answer %d: %s\n",i,student->Answers[i]);
}
#endif /*DEBUG*/
if (CompareClassName)
{
if (strncasecmp(student->class,class,8))
{
printf("Class: The Scantron reported:%s, You typed in:%s\n",
student->class,class);
return ECLASS;
}
}
else
{
strcpy(student->class,class);
sprintf(student->SetId,"%2d",setId);
}
error=capa_get_student_info(student->answerPID, &capaStudent);
switch(error)
{
case 1:
printf("%s %d\n\n",capaStudent.name,capaStudent.section);
break;
case 0:
printf("PID: The Scantron reported:%s\n",student->answerPID);
return ESTID;
break;
case -1:
return ECLASSL;
break;
default:
fprintf(stderr,"capa_get_student returned an invalid result ");
fprintf(stderr,"in CheckForm.\n");
break;
}
if (CheckPIN && !Anon)
{
pin=capa_PIN(student->answerPID,atoi(student->SetId),0);
if (pin!=atoi(student->PIN))
{
printf("PIN: The Scantron reported:%s, The Classl file has:%d\n",
student->PIN,pin);
return EPIN;
}
}
return 0;
}
long getBubblerEntry(FILE ** outputFile,char *PID)
{
char oneline[512],fmtbuf[16],a_sn[32];
int done=0,found=0,offset=0,len=0,next_r=0;
rewind(*outputFile);
sprintf(fmtbuf,"%%%dc",MAX_STUDENT_NUMBER);
while(!done)
{
done=!fgets(oneline,511,*outputFile);
len=strlen(oneline);
if (!done)
{
sscanf(oneline,fmtbuf,a_sn);
if (!strncasecmp(a_sn,PID,MAX_STUDENT_NUMBER))
{
next_r=ftell(*outputFile);
offset = next_r-len;
done=1;
found=1;
}
else
{
}
}
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=0;
done=1;
}
}
if(!found)
{
offset=-offset;
}
return offset;
}
void setBubblerEntry(FILE ** outputFile,char* answerPID,char* name,
char* answers,int score, int section,
char* answerstring,char* questionPID, int offset)
{
int len=0;
char buf[1024];
rewind(*outputFile);
sprintf(buf,"%s %s %s %3d %2d %s %s\n",answerPID,name,answers,score,section,
answerstring,questionPID);
len=strlen(buf);
fseek(*outputFile,abs(offset),0);
if(!fwrite(buf,len,1,*outputFile))
{
fprintf(stderr,"Failed write.\n");
}
}
/* Checks if answers are right and gives a point for each right.*/
void writeForm(Student * student,FILE ** outputFile,
Question questions[MAXQUEST],int numQuestions)
{
int result,capaQuestions,questionIndex,leafs,numRight,error,total=0;
int offset2;
char one=1,zero=0;
char *ansOn[20],*stuOn[20];
Problem_t *problems,*oldproblem;
char answerstring[1024],grade[1024];
student_t capaStudent;
if (!SurveyMode)
{
error=capa_get_student_info(student->answerPID, &capaStudent);
result=capa_parse(atoi(student->SetId),&problems,student->questionPID,
&capaQuestions);
oldproblem=problems;
if (result==0)
{
fprintf(stderr,"Parse failed: %d\n",result);
return;
}
}
else
{
strcpy(capaStudent.name,"Unknown ");
capaStudent.section=0;
oldproblem=problems=NULL;
}
offset2=getBubblerEntry(outputFile,student->answerPID);
for(questionIndex=0;questionIndex<numQuestions;questionIndex++)
{
switch(questions[questionIndex].type)
{
case 'a':
case 'b':
case 'c':
case 'f':
numRight=0;
printf("%s %s\n",problems->answer,student->Answers[questionIndex]);
for(leafs=0;problems->answer[leafs]!='\0';leafs++)
{
if (problems->answer[leafs]==
student->Answers[questionIndex][leafs])
{
numRight++;
}
}
total+=numRight;
grade[questionIndex]='0'+(char)numRight;
break;
case 'd':
printf("%s\n",student->Answers[questionIndex]);
grade[questionIndex]=student->Answers[questionIndex][0];
if (isspace(student->Answers[questionIndex][0]))
total+=0;
else
total+=(int)(student->Answers[questionIndex][0]-'0');
break;
case 'e':
printf("%s %s\n",problems->answer,student->Answers[questionIndex]);
numRight=0;
for(leafs=0;questions[questionIndex].leafs>leafs;leafs++)
{
ansOn[leafs]=strchr(problems->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++;
}
fprintf(stderr,"%d\n",numRight);
total+=numRight;
grade[questionIndex]='0'+(char)numRight;
break;
case 'g':
printf("%s %s\n",problems->answer,student->Answers[questionIndex]);
if (!(strcasecmp(problems->answer,student->Answers[questionIndex])))
{
total+=questions[questionIndex].points;
grade[questionIndex]='0'+questions[questionIndex].points;
}
else
{
grade[questionIndex]='0';
}
break;
default:
printf("No points since don't know question type.\n");
break;
}
if (!SurveyMode) problems=problems->next;
}
answerstring[0]='\0';
for(questionIndex=0;questionIndex<numQuestions;questionIndex++)
{
strcat(answerstring,student->Answers[questionIndex]);
}
setBubblerEntry(outputFile,student->answerPID,capaStudent.name,grade,
total,capaStudent.section,answerstring,student->questionPID,
offset2);
if (!SurveyMode)
{
problems=oldproblem;
for(questionIndex=0;problems!=NULL;questionIndex++)
{
oldproblem=problems;
problems=problems->next;
free(oldproblem);
}
#ifdef DEBUG
fprintf(stderr,"Freed: %d\n",questionIndex);
#endif /*DEBUG*/
}
}
void processForms(FILE ** serport,int numQuestions,
Question questions[MAXQUEST],char* class,int setId)
{
int done=0,error,i,numOfStudents=0;
int status;
char buf[128],filename[128];
Student * student;
FILE * outputFile;
PIDPINlist PIDandPINlist[MAX_SECTION_SIZE];
sprintf(filename,"bubbler.output.%d",setId);
outputFile=fopen(filename,"r+");
if (outputFile != NULL)
{
rewind(outputFile);
fscanf(outputFile,"%s",buf);
}
if (outputFile==NULL || buf[0]=='\0')
{
outputFile=fopen(filename,"w+");
fprintf(outputFile,"%s %d ",class,setId);
for(i=0;i<numQuestions;i++)
{
fprintf(outputFile,"%c%d",questions[i].type,questions[i].points);
}
fprintf(outputFile,"\n");
}
if (Anon)
{
numOfStudents=buildPIDandPINlist(setId,PIDandPINlist);
if (numOfStudents==0)
{
fprintf(stderr,"buildPIDandPINlists returned 0 students.");
exit(-2);
}
}
while(!done)
{
student=getForm(&status,serport,questions);
switch (status)
{
case GFSPACES:
if (CheckSpaces)
{
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");
scanf("%s",buf);
if (buf[0]=='y' || buf[0]=='Y') ;
else break;
}
case GFSUCCESS:
if ((!SurveyMode) &&
(error=checkForm(student,numQuestions,class,setId)))
{
switch(error)
{
case ECLASS:
printf("The current form has a class string that is \n");
printf("different from the one entered at the start.\n");
printf("Please place that form to the side and type ");
printf("start to continue.\n");
scanf("%s",buf);
break;
case ESTID:
printf("The current form's Student Id is incorrect.\n");
printf("Please set the form aside and type start to ");
printf("continue.\n");
scanf("%s",buf);
break;
case ECLASSL:
fprintf(stderr,"The classl file was not found in the");
fprintf(stderr," current directory.\n");
fprintf(stderr,"Please try again.\n");
done=1;
break;
case EPIN:
fprintf(stderr,"The current form's PIN is incorrect.\n");
fprintf(stderr,"Please set the form aside and type ");
fprintf(stderr,"start to continue.\n");
scanf("%s",buf);
break;
default:
fprintf(stderr,"Unimplemented error in checkForm %d\n",
error);
break;
}
}
else
{
if (Anon)
{
error=findPID(student,PIDandPINlist,numOfStudents);
}
else
{
error=ENONE;
strcpy(student->questionPID,student->answerPID);
}
switch(error)
{
case ENONE:
writeForm(student,&outputFile,questions,numQuestions);
break;
case ENOONE:
break;
default:
fprintf(stderr,"Unimplemented error in findPID %d\n",
error);
break;
}
}
break;
case GFFAILED:
printf("The Scantron has returned an error.\n");
printf("Are there still more forms to process?");
scanf("%s",buf);
if (buf[0]=='n')
{
done=1;
}
else
{
printf("Please put that last read form to the side.\n");
printf("Enter start to continue\n");
scanf("%s",buf);
}
break;
case GFEOF:
done=1;
break;
default:
printf("Unimplened return code in GetForm %d\n",status);
break;
}
if (student != NULL)
{
free(student);
}
}
}
void CloseScantron(FILE ** serport)
{
fprintf(*serport,".srst\r");
fclose(*serport);
}
int main(int argc, char *argv[])
{
FILE * serport;
Question questions[MAXQUEST];
char class[10];
int numQuestions,setId;
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 Bubbler, the Automated CAPA grader.\n");
printf("Version 0.05.02\n\n\n");
numQuestions=GetProblems(questions,class,&setId);
SetupScantron(&serport,numQuestions,questions);
processForms(&serport,numQuestions,questions,class,setId);
CloseScantron(&serport);
return 0;
}
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>