File:  [LON-CAPA] / capa / capa51 / pProj / capaCommon.c
Revision 1.25: download - view: text, annotated - select for diffs
Thu Dec 1 20:05:22 2005 UTC (18 years, 6 months ago) by albertel
Branches: MAIN
CVS tags: HEAD
- BUG#4475 making the number parser more picky
now only accepts

number (*|x|X) 10 ^ (-|+|) integer
or
10 ^ (-|+|) integer

rather than what it did previously which was:
number (*|x|X) number ^ (-|+|) number
or
number ^ (-|+|) number

    1: /* Library of useful functions
    2:    Copyright (C) 1992-2000 Michigan State University
    3: 
    4:    The CAPA system is free software; you can redistribute it and/or
    5:    modify it under the terms of the GNU General Public License as
    6:    published by the Free Software Foundation; either version 2 of the
    7:    License, or (at your option) any later version.
    8: 
    9:    The CAPA system is distributed in the hope that it will be useful,
   10:    but WITHOUT ANY WARRANTY; without even the implied warranty of
   11:    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   12:    General Public License for more details.
   13: 
   14:    You should have received a copy of the GNU General Public
   15:    License along with the CAPA system; see the file COPYING.  If not,
   16:    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   17:    Boston, MA 02111-1307, USA.
   18: 
   19:    As a special exception, you have permission to link this program
   20:    with the TtH/TtM library and distribute executables, as long as you
   21:    follow the requirements of the GNU GPL in regard to all of the
   22:    software in the executable aside from TtH/TtM.
   23: */
   24: 
   25: /* =||>|===================== capaCommon.c =====================|<||= */
   26: /* created 1994 by Isaac Tsai                                         */
   27: /* TODO: restructure capa_check_ans*() calls into one                 */
   28: /* =||>|===================== capaCommon.c =====================|<||= */
   29: #include <ctype.h>
   30: #if defined(__sun) || defined(linux) || defined(__alpha) || defined(hpux) || defined(AIX) || defined(IRIX) 
   31: #include <unistd.h>  /* lockf() */
   32: #endif
   33: #include <sys/types.h>
   34: #include <sys/stat.h>
   35: #include <fcntl.h>
   36: #include "capaParser.h"
   37: #include "capaToken.h"
   38: #include "capaCommon.h"
   39: #include "ranlib.h"
   40: 
   41: 
   42: /*----------------------------------------------------------*/
   43: /*  flock() in SUN is in BSD compatibility lib              */
   44: /*  #include <sys/file.h>                                   */
   45: /*----------------------------------------------------------*/
   46: 
   47: int     yyparse();
   48: extern  FILE *yyin;
   49: extern  void yyrestart();
   50: 
   51: extern  FILE *dfp;
   52: 
   53: /*----------------------------------------------------------*/
   54: /*  RETURN: -1 file error                                   */
   55: /*           0 success                                      */
   56: /*  INPUT:     if section == 0, for all students in class   */
   57: /*             set : 1, 2, 3, ...                           */
   58: /*             prob: 1, 2, 3, ...                           */
   59: /*----------------------------------------------------------*/
   60: int
   61: capa_excuse(int set,int  prob,int  section)
   62: {
   63:    int        nq;        
   64:    T_header   header;
   65:    T_entry    entry;          
   66:    T_student *student_p, *s1_p;
   67:    char       tmp_number[MAX_STUDENT_NUMBER+1];
   68:    long       offset;
   69:    int        num_students;
   70:    
   71:    /* Calculate parameters */
   72:    if (capa_get_header(&header,set))   return (-1);
   73:    sscanf(header.num_questions,"%d", &nq);
   74: 
   75:    if( ( prob > nq ) || (section < 0 ) || (section > MAX_SECTION_COUNT) )   return (-1);
   76:    num_students= 0;
   77:    if ( (num_students = capa_get_section(&student_p, section))== -1 ) return (-1);
   78:    for ( s1_p = student_p ; s1_p ; s1_p = (s1_p->s_next)) {
   79:      sprintf(tmp_number,"%s",s1_p->s_sn);
   80:      offset = capa_get_entry(&entry,tmp_number,set);
   81:      if(offset < 0 ) offset = -offset;
   82:      switch(entry.answers[prob-1]) { 
   83:        case '0':
   84:        case '-':  entry.answers[prob-1] = 'E'; break;             
   85:        case 'N':  entry.answers[prob-1] = 'E'; break;             
   86:        case 'n':  entry.answers[prob-1] = 'e'; break;             
   87:        case '1': case '2': case '3': case '4': case '5':
   88:        case '6': case '7': case '8': case '9': 
   89: 	 if (entry.answers[prob-1] < header.weight[prob-1]) {
   90: 	   entry.answers[prob-1] = 'E'; 
   91: 	 } 
   92: 	 break;
   93:        default :  break;
   94:      } 
   95:      capa_set_entry(&entry,tmp_number,set,offset);
   96:      capa_mfree(entry.answers); entry.answers = NULL;
   97:      capa_mfree(entry.tries);   entry.tries = NULL;
   98:    }
   99:    capa_mfree(header.weight);
  100:    capa_mfree(header.partial_credit);
  101:    free_students(student_p);
  102:    return (0);
  103: }
  104: 
  105: /*----------------------------------------------------------*/
  106: /*  Lock file shared                                        */
  107: /*  lock the file specified by file stream pointer sp       */
  108: /*----------------------------------------------------------*/
  109: int
  110: flockstream_sh(sp) FILE *sp;
  111: {
  112:   int fd;
  113:   
  114:   fd = fileno(sp);
  115:   
  116: #if defined(__sun) || defined(hpux) || defined(AIX)
  117:   return ( lockf(fd,F_LOCK, 0L) );
  118: #else
  119:   return (flock(fd,LOCK_SH));
  120: #endif
  121: }
  122: /*----------------------------------------------------------*/
  123: int
  124: flockstream(sp) FILE *sp;
  125: {
  126:   int fd;
  127:   
  128:   fd = fileno(sp);
  129:   
  130: #if defined(__sun) || defined(hpux) || defined(AIX)
  131:   return ( lockf(fd,F_LOCK, 0L) );
  132: #else
  133:   return (flock(fd,LOCK_EX));
  134: #endif
  135: }
  136: /*----------------------------------------------------------*/
  137: int
  138: funlockstream(sp) FILE *sp;
  139: {
  140:   int fd;
  141:   
  142:   fd = fileno(sp);
  143:   
  144: #if defined(__sun) || defined(hpux) || defined(AIX)
  145:   return ( lockf(fd,F_ULOCK, 0L) );
  146: #else
  147:   return ( flock(fd,LOCK_UN) );
  148: #endif
  149: }
  150: 
  151: int
  152: inquery_a_lock(sp,cmd,type,offset,whence,len)
  153: FILE *sp;int cmd;off_t offset;int whence;off_t len;
  154: {
  155:   struct flock lock;
  156:   int    fd;
  157:   lock.l_type   = type;   lock.l_start = offset;
  158:   lock.l_whence = whence; lock.l_len   = len;
  159:   fd=fileno(sp);
  160:   return (fcntl(fd,cmd,&lock));
  161: }
  162: #define  Blocked_Write_Lock(sp) \
  163:           inquery_a_lock(sp,F_SETLK,F_WRLCK,0,0,0)
  164: 
  165: #define  Blocked_Write_Lock(sp) \
  166:           inquery_a_lock(sp,F_SETLK,F_WRLCK,0,0,0)
  167: 
  168: #define  Un_Lock(sp) \
  169:           inquery_a_lock(sp,F_SETLK,F_UNLCK,0,0,0)
  170: 
  171:          
  172: /*----------------------------------------------------------*/
  173: /*  INPUT:   set     problem set number                     */
  174: /*  RETURN:  -1      no records/setX.db file or file error  */
  175: /*            0      success                                */
  176: /*  OUTPUT:  header  pointer to T_header structure          */
  177: /*----------------------------------------------------------*/
  178: int
  179: capa_set_header(header, set) T_header *header;int  set;
  180: {
  181:    FILE    *fp;
  182:    char    *big_buf=NULL, filename[FILE_NAME_LENGTH], cr=0, oneline[TMP_LINE_LENGTH];
  183:    int      nq, len, errcode, fd, item_cnt, exists=1;
  184:    T_header oldheader;
  185:    long     orig_size, new_size, big_buf_size;
  186: 
  187:    sprintf(filename,"records/set%d.db",set);
  188:    if ((fp=fopen(filename,"r+"))==NULL) {
  189:      exists=0;
  190:      if ((fp=fopen(filename,"w"))==NULL) {   /* create if non-existant */
  191: 	/*printf("Error: can't open %s\n",filename);*/  return (-1); 
  192:       }
  193:    }
  194:    sscanf(header->num_questions,"%d",&nq);
  195:    /*   sprintf(oneline,"%d,%s,%s,%s\n",nq,header->open_date,header->due_date,header->answer_date); */
  196:    sprintf(oneline,"%d\n",nq);
  197:    len = strlen(oneline);
  198:    flockstream(fp);   errcode = 0;
  199: 
  200:    if (exists) {
  201:      /*get file size*/
  202:      fseek(fp,0L,SEEK_END);
  203:      orig_size = ftell(fp);
  204:      big_buf_size = orig_size; /*the amt we need to read is less then the file's size*/
  205:      big_buf = capa_malloc(big_buf_size,1);
  206:      fseek(fp,0L,SEEK_SET);
  207:      
  208:      /*read in old header*/
  209:      fscanf(fp,"%d",&nq);
  210:      while ( cr != '\n' && cr != EOF ) { cr=fgetc(fp); }
  211:      oldheader.weight=capa_malloc(nq+1,1);
  212:      oldheader.partial_credit=capa_malloc(nq+1,1);
  213:      if (!fread(oldheader.weight, sizeof(char), nq, fp))  errcode = -2;
  214:      if (EOF==(cr=fgetc(fp)))  errcode = -3;
  215:      if (!fread(oldheader.partial_credit, sizeof(char),  nq, fp))  errcode = -4;
  216:      if (EOF==(cr=fgetc(fp)))  errcode = -5;
  217:      capa_mfree(oldheader.weight);
  218:      capa_mfree(oldheader.partial_credit);
  219:      
  220:      /*read rest of file*/
  221:      item_cnt=fread(big_buf,1,big_buf_size, fp); 
  222:    }
  223: 
  224:    /*write new header*/
  225:    fseek(fp,0L,SEEK_SET);
  226:    if (!fwrite(oneline, len, 1, fp)) {errcode = -6;}
  227:    if ( header->weight != NULL ) {
  228:      if (!fwrite((char *)(header->weight), strlen(header->weight), 1, fp)) errcode=-7;
  229:    }
  230:   
  231:    if (EOF==(!fputc('\n', fp))) errcode = -8;
  232:    if (header->partial_credit != NULL) {
  233:      if (!fwrite((char *)(header->partial_credit), strlen(header->partial_credit), 
  234: 		 1, fp)) { errcode = -9; }
  235:    } 
  236:    if (EOF==(!fputc('\n', fp))) errcode = -10;
  237:    
  238:    if (exists) {
  239:      /*write out rest of file*/
  240:      if (item_cnt >= 0 ) {
  241:        big_buf[item_cnt]=0;
  242:        if (!fwrite(big_buf,item_cnt,1,fp) ) errcode= -11;
  243:        new_size = ftell(fp);
  244:        if (new_size < orig_size ) {
  245: 	 fd = fileno(fp);
  246: 	 ftruncate(fd,new_size);
  247:        }
  248:      } 
  249:    }
  250: 
  251:    fflush(fp);
  252:    funlockstream(fp); fclose(fp);
  253:    if (big_buf!=NULL) capa_mfree(big_buf);
  254:    return (errcode);
  255: }
  256: 
  257: /*----------------------------------------------------------*/
  258: /*  INPUT:   set     problem set number                     */
  259: /*  RETURN:  -1  file error                                 */
  260: /*            0  success                                    */
  261: /*  OUTPUT:  header  pointer to T_header structure          */
  262: /* Note: allocates space for the partial_credit and weight  */
  263: /*       fields of the passed in header, caller needs to    */
  264: /*       eventually free them with capa_mfree               */
  265: /*----------------------------------------------------------*/
  266: int
  267: capa_get_header(header, set) T_header *header;int  set;
  268: {
  269:    FILE    *fp;
  270:    char     filename[FILE_NAME_LENGTH];
  271:    char     cr;
  272:    int      nq=0, errcode;
  273:    
  274:    sprintf(filename,"records/set%d.db",set); 
  275:    if ((fp=fopen(filename,"r"))==NULL) {
  276:      /*printf("Error capa_get_header(): can't open %s\n",filename);*/
  277:       return (-1); 
  278:    }
  279:    
  280:    flockstream(fp);    errcode = 0;
  281:    fscanf(fp,"%d",&nq);
  282:    cr='\0';
  283:    while ( cr != '\n' && cr != EOF ) { cr=fgetc(fp); }
  284:    header->weight=capa_malloc(nq+1,1);
  285:    header->partial_credit=capa_malloc(nq+1,1);
  286:    if (nq > 0 ) {
  287:      if (!fread(header->weight, sizeof(char), nq, fp))  errcode = -1;
  288:    }
  289:    if (EOF==(cr=fgetc(fp)))  errcode = -1;
  290:    if (nq > 0 ) {
  291:      if (!fread(header->partial_credit, sizeof(char),  nq, fp))  errcode = -1;
  292:    }
  293:    
  294:    funlockstream(fp);    fclose(fp);
  295:    header->weight[nq]='\0'; header->partial_credit[nq]='\0';
  296:    sprintf(header->num_questions,"%d",nq);
  297:    return (errcode);
  298: }
  299: /*----------------------------------------------------------*/
  300: /* ----------- previous version, obsolete as of May 1997
  301: int
  302: capa_set_entry(entry, student_number, set, offset) 
  303: T_entry   *entry;
  304: char      *student_number;
  305: int        set;
  306: long       offset;
  307: {
  308:    FILE    *fp;
  309:    int      errcode=0;
  310:    int      len;
  311:    char     filename[FILE_NAME_LENGTH];
  312:    char     a_line[TMP_LINE_LENGTH];
  313: 
  314:    sprintf(filename,"records/set%d.db",set);
  315:    if ((fp=fopen(filename,"r+"))==NULL) {
  316:       return (-1);
  317:    }
  318:    sprintf(a_line,"%s %s,%s\n",entry->student_number,entry->answers,entry->tries);
  319:    len = strlen(a_line);
  320:    flockstream(fp);
  321:    fseek(fp,offset,0);
  322:      if (!fwrite(a_line,len,1,fp) ) {
  323:        printf("Error writing data to file\n");
  324:        errcode= (-1);
  325:      }
  326:    fflush(fp);
  327:    funlockstream(fp);
  328:    fclose(fp);
  329:    return (errcode);
  330: }
  331:   ---------- */
  332: 
  333: int
  334: capa_set_entry(entry, student_number, set, offset) 
  335: T_entry   *entry;
  336: char      *student_number;
  337: int        set;
  338: long       offset;
  339: {
  340:    FILE    *fp;
  341:    int      fd;
  342:    int      errcode=0;
  343:    int      done, len, new_len, item_cnt;
  344:    long     next_r, orig_size, new_size, big_buf_size;
  345:    char     filename[FILE_NAME_LENGTH];
  346:    char     *a_line, tmpline[TMP_LINE_LENGTH], 
  347:      tmp_sn[MAX_STUDENT_NUMBER+1], fmtbuf[SMALL_LINE_BUFFER];
  348:    char    *big_buf;
  349:    
  350:    sprintf(filename,"records/set%d.db",set);
  351:    if ((fp=fopen(filename,"r+"))==NULL) {
  352:       /* printf("Error capa_set_entry(): can't open %s\n",filename); */  return (-1);
  353:    }
  354:    /* entry->answers*3 == entry->tries, + fudge factor*/
  355:    a_line=capa_malloc(strlen(entry->tries)*5+MAX_STUDENT_NUMBER,1);
  356:    sprintf(a_line,"%s %s,%s\n",entry->student_number,entry->answers,entry->tries);
  357:    new_len = strlen(a_line);
  358:    sprintf(fmtbuf, "%%%dc",MAX_STUDENT_NUMBER);
  359:    flockstream(fp);  /* <==== lock the setX.db file */
  360:    fseek(fp,0L,SEEK_END);
  361:    orig_size = ftell(fp);
  362:    big_buf_size = orig_size + new_len;
  363:    big_buf = capa_malloc(big_buf_size,1);
  364:    fseek(fp,0L,SEEK_SET); /* rewind to beginning of file */
  365:    fgets(tmpline,TMP_LINE_LENGTH-1,fp); /* skip weight line, including \n */
  366:    fgets(tmpline,TMP_LINE_LENGTH-1,fp); /* hand grading */
  367:    done = 0;
  368:    while(!done) {
  369:      done = !fgets(tmpline,TMP_LINE_LENGTH-1,fp); len = strlen(tmpline);
  370:      if( !done ) {
  371:        sscanf(tmpline,fmtbuf,tmp_sn);
  372:        if( !strncasecmp(tmp_sn,student_number,MAX_STUDENT_NUMBER) ) { /* Found */
  373:          next_r = ftell(fp); offset = next_r - len; done = 1;
  374:          item_cnt=fread(big_buf,1,big_buf_size, fp); /* read remaining lines into buffer */
  375:          if(item_cnt >= 0 ) { /* no error reading remaining lines */
  376:            big_buf[item_cnt]=0;   /* terminate the buffer, for sure */
  377:            fseek(fp,offset,SEEK_SET);  /* reposition file pointer to the record */
  378:            if (!fwrite(a_line,new_len,1,fp) ) {       /* write out the records */
  379:              /* printf("Error writing data to file\n"); */
  380:              errcode= (-1);
  381:            }
  382: 	   if (item_cnt != 0) {
  383: 	     if (!fwrite(big_buf,item_cnt,1,fp) ){/*write out the remainings*/
  384: 	       /* printf("Error writing data to file\n"); */
  385: 	       errcode= (-1);
  386: 	     }
  387: 	   }
  388: 	   new_size = ftell(fp);
  389: 	   if(new_size < orig_size ) {
  390: 	     fd = fileno(fp);
  391: 	     ftruncate(fd,new_size);
  392: 	   }
  393:          }
  394:        }
  395:      } else { /* end of file */
  396:        fseek(fp,0L,SEEK_END);
  397:        offset = ftell(fp);  /* last byte, if last byte is cr, back up one */
  398:        fseek(fp,-1L,SEEK_END);
  399:        while(fgetc(fp) == '\n' ) { offset--; fseek(fp,offset,SEEK_SET); }
  400:        offset = offset +2; /* last char and cr */
  401:        done=1;
  402:        fseek(fp,offset,SEEK_SET);
  403:        if (!fwrite(a_line,new_len,1,fp) ) {       /* write out the records */
  404:          /* printf("Error writing data to file\n"); */
  405:          errcode= (-1);
  406:        }
  407:      }
  408:    }
  409:    fflush(fp);
  410:    funlockstream(fp);   /* <======= unlock the file */
  411:    fclose(fp);
  412:    capa_mfree(big_buf);  /* free up the buffer */
  413:    return (errcode);
  414: }
  415: 
  416: /* for all elements in entry->answers that aren't ? it changes the
  417:    file to have the new values (except if student has a Y and for all
  418:    entries in entry->tries that don't have the value -1 it changes the
  419:    file to have the new value */
  420: 
  421: int
  422: capa_change_entry(entry, student_number, set) 
  423: T_entry   *entry;
  424: char      *student_number;
  425: int        set;
  426: {
  427:    FILE    *fp;
  428:    int      fd;
  429:    int      errcode=0,offset=0;
  430:    int      done, len, new_len, item_cnt,i,j;
  431:    long     next_r, orig_size, new_size, big_buf_size;
  432:    char     filename[FILE_NAME_LENGTH];
  433:    char     *a_line, tmpline[TMP_LINE_LENGTH], 
  434:      tmp_sn[MAX_STUDENT_NUMBER+1], fmtbuf[SMALL_LINE_BUFFER];
  435:    char    *big_buf;
  436:    
  437:    sprintf(filename,"records/set%d.db",set);
  438:    if ((fp=fopen(filename,"r+"))==NULL) {
  439:       /* printf("Error capa_set_entry(): can't open %s\n",filename); */  return (-1);
  440:    }
  441:    /* entry->answers*3 == entry->tries, + fudge factor*/
  442:    a_line=capa_malloc(strlen(entry->tries)*5+MAX_STUDENT_NUMBER,1);
  443:    sprintf(a_line,"%s %s,%s\n",entry->student_number,entry->answers,entry->tries);
  444:    new_len = strlen(a_line);
  445:    sprintf(fmtbuf, "%%%dc",MAX_STUDENT_NUMBER);
  446:    flockstream(fp);  /* <==== lock the setX.db file */
  447:    fseek(fp,0L,SEEK_END);
  448:    orig_size = ftell(fp);
  449:    big_buf_size = orig_size + new_len;
  450:    big_buf = capa_malloc(big_buf_size,1);
  451:    fseek(fp,0L,SEEK_SET); /* rewind to beginning of file */
  452:    fgets(tmpline,TMP_LINE_LENGTH-1,fp); /* skip weight line, including \n */
  453:    fgets(tmpline,TMP_LINE_LENGTH-1,fp); /* hand grading */
  454:    done = 0;
  455:    while(!done) {
  456:      done = !fgets(tmpline,TMP_LINE_LENGTH-1,fp); len = strlen(tmpline);
  457:      if( !done ) {
  458:        sscanf(tmpline,fmtbuf,tmp_sn);
  459:        if( !strncasecmp(tmp_sn,student_number,MAX_STUDENT_NUMBER) ) { /* Found */
  460:          next_r = ftell(fp); offset = next_r - len; done = 1;
  461:          item_cnt=fread(big_buf,1,big_buf_size,fp); /*read remaining lines into buffer*/
  462:          if(item_cnt >= 0 ) { /* no error reading remaining lines */
  463:            big_buf[item_cnt]=0;   /* terminate the buffer, for sure */
  464:            fseek(fp,offset,SEEK_SET);  /* reposition file pointer to the record */
  465: 	   for(i=0;i<entry->e_probs;i++) {
  466: 	     /*if entry has been updated, and student doesn't have a Y yet*/
  467: 	     if ((entry->answers[i]!='?') && 
  468: 		 (tmpline[MAX_STUDENT_NUMBER+1+i]!='Y')) 
  469: 	       tmpline[MAX_STUDENT_NUMBER+1+i]=entry->answers[i];
  470: 	     j = atoi(&(entry->tries[i*3]));
  471: 	     if ( j > -1 ) {
  472: 	       tmpline[MAX_STUDENT_NUMBER+1+entry->e_probs+1+i*3]=entry->tries[i*3];
  473: 	       tmpline[MAX_STUDENT_NUMBER+1+entry->e_probs+1+i*3+1]=entry->tries[i*3+1];
  474: 	     }
  475: 	   }
  476:            if (!fwrite(tmpline,new_len,1,fp) ) { errcode= (-1); }
  477: 	   if (item_cnt != 0) {
  478: 	     if (!fwrite(big_buf,item_cnt,1,fp) ){/*write out the remainings*/
  479: 	       /* printf("Error writing data to file\n"); */
  480: 	       errcode= (-1);
  481: 	     }
  482: 	   }
  483: 	   new_size = ftell(fp);
  484: 	   if(new_size < orig_size ) {
  485: 	     fd = fileno(fp);
  486: 	     ftruncate(fd,new_size);
  487: 	   }
  488:          }
  489:        }
  490:      } else { /* end of file */
  491:        fseek(fp,0L,SEEK_END);
  492:        offset = ftell(fp);  /* last byte, if last byte is cr, back up one */
  493:        fseek(fp,-1L,SEEK_END);
  494:        while(fgetc(fp) == '\n' ) { offset--; fseek(fp,offset,SEEK_SET); }
  495:        offset = offset +2; /* last char and cr */
  496:        done=1;
  497:        fseek(fp,offset,SEEK_SET);
  498:        if (!fwrite(a_line,new_len,1,fp) ) {       /* write out the records */
  499:          /* printf("Error writing data to file\n"); */
  500:          errcode= (-1);
  501:        }
  502:      }
  503:    }
  504:    fflush(fp);
  505:    funlockstream(fp);   /* <======= unlock the file */
  506:    fclose(fp);
  507:    capa_mfree(big_buf);  /* free up the buffer */
  508:    return (errcode);
  509: }
  510: 
  511: /* -------------------------------------------------- */
  512: /* append a entry record to the very end of the file */
  513: long
  514: capa_append_entry(entry, student_number, set)
  515: T_entry   *entry;
  516: char      *student_number;
  517: int        set;
  518: {
  519:    FILE    *fp;
  520:    int      len;
  521:    char     filename[FILE_NAME_LENGTH];
  522:    char     a_line[TMP_LINE_LENGTH];
  523:    long     offset;
  524:    
  525:    sprintf(filename,"records/set%d.db",set);
  526:    if ((fp=fopen(filename,"r+"))==NULL) {
  527:       /* printf("Error capa_set_entry(): can't open %s\n",filename); */  
  528:       return (-1);
  529:    }
  530:    sprintf(a_line,"%s %s,%s\n",entry->student_number,entry->answers,entry->tries);
  531:    len = strlen(a_line);
  532:    
  533:    flockstream(fp);  /* <==== lock the setX.db file */
  534:    fseek(fp,0L,SEEK_END); 
  535:    offset = ftell(fp);
  536:    fseek(fp,-1L,SEEK_END);
  537:    while(fgetc(fp) == '\n' ) { offset--; fseek(fp,offset,SEEK_SET); }
  538:    offset = offset +2;
  539:    fseek(fp,offset,SEEK_SET);
  540:    if (!fwrite(a_line,len,1,fp) ) {
  541:      /* printf("Error writing data to file\n"); */
  542:      return (-1);
  543:    }
  544:    fflush(fp);
  545:    funlockstream(fp);
  546:    fclose(fp);
  547:    return (offset);
  548: }
  549: 
  550: /*----------------------------------------------------------*/
  551: 
  552: long /* RETURNS: byte offset to start of record, 0 if error,
  553:                     -offset if not found & newly created  */
  554: capa_get_entry(entry, student_number, set) 
  555: T_entry   *entry;           
  556: char      *student_number;  
  557: int        set;            
  558: {
  559:    char      filename[FILE_NAME_LENGTH];
  560:    FILE     *fp;
  561:    int       len, nq;          
  562:    char     *ans_p, *tries_p, oneline[TMP_LINE_LENGTH],fmtbuf[SMALL_LINE_BUFFER];
  563:    long      offset = 0, next_r;             
  564:    int       ii, done, found = 0;
  565:    char      a_sn[MAX_STUDENT_NUMBER+1];
  566:    
  567:    sprintf(filename,"records/set%d.db",set); 
  568:    if ((fp=fopen(filename,"r"))==NULL) {
  569:      /*printf("Error capa_get_entry(): can't open %s\n",filename);*/
  570:       return (0); 
  571:    }
  572:    sprintf(entry->student_number,"%s",student_number);
  573:    sprintf(fmtbuf, "%%%dc",MAX_STUDENT_NUMBER);
  574:    flockstream(fp);
  575:    fgets(oneline,TMP_LINE_LENGTH-1,fp); len = strlen(oneline); sscanf(oneline,"%d",&nq);
  576:    fgets(oneline,TMP_LINE_LENGTH-1,fp); /* skip weight line, including \n */
  577:    fgets(oneline,TMP_LINE_LENGTH-1,fp); /* hand grading */
  578:    done = 0;
  579:    while(!done) {
  580:      done = !fgets(oneline,TMP_LINE_LENGTH-1,fp); len = strlen(oneline);
  581:      if( !done ) {
  582:        sscanf(oneline,fmtbuf,a_sn);
  583:        if( !strncasecmp(a_sn,student_number,MAX_STUDENT_NUMBER) ) { /* Found */
  584:          next_r = ftell(fp); offset = next_r - len; done = 1; found = 1;
  585:        }
  586:      } else { /* end of file */
  587:        fseek(fp,0L,SEEK_END);
  588:        offset = ftell(fp);  /* last byte, if last bye is cr, back up one */
  589:        fseek(fp,-1L,SEEK_END);
  590:        while(fgetc(fp) == '\n' ) { offset--; fseek(fp,offset,SEEK_SET); }
  591:        offset = offset +2; /* last char and cr */
  592:        found = 0; done=1;
  593:      }
  594:    }
  595:    funlockstream(fp); fclose(fp);
  596:    if(!found) {
  597:      ans_p = capa_malloc(nq+2,1); 
  598:      tries_p = capa_malloc(3*nq+3,1); /* space and \0 */
  599:      for(ii=0;ii<nq;ii++) { /* Initialize answer string and tries string */
  600:        ans_p[ii] = '-'; tries_p[3*ii] = ' '; tries_p[3*ii + 1] = '0';
  601:        if(ii < nq-1) tries_p[3*ii + 2] = ',';
  602:      }
  603:      entry->answers = ans_p;
  604:      entry->tries   = tries_p;
  605:      entry->e_probs = nq;
  606:      offset = capa_append_entry(entry,student_number,set);
  607:    } else { /* grows records shorter than nq to nq in length*/
  608:      char *comma;
  609:      int length;
  610:      comma=index(oneline,',');
  611:      length=((int)(comma-oneline))-(MAX_STUDENT_NUMBER+1);
  612:      if (length < nq) {
  613:        ans_p = capa_malloc(nq+2,1); 
  614:        tries_p = capa_malloc(3*nq+3,1); /* space and \0 */
  615:      } else {
  616:        ans_p = capa_malloc(length+2,1); 
  617:        tries_p = capa_malloc(3*length+3,1); /* space and \0 */
  618:      }
  619:      sprintf(fmtbuf, "%%%dc",length);
  620:      sscanf(oneline + MAX_STUDENT_NUMBER+1,fmtbuf,ans_p);
  621:      ans_p[length]='\0';
  622:      sprintf(fmtbuf, "%%%dc",(3*length-1));
  623:      sscanf(oneline + MAX_STUDENT_NUMBER+1+length+1,fmtbuf,tries_p);
  624:      tries_p[3*length-1]='\0';
  625:      entry->answers = ans_p;
  626:      entry->tries   = tries_p;
  627:      entry->e_probs = nq;
  628:      if (length < nq) {
  629:        for(ii=length;ii<nq;ii++) {
  630: 	 ans_p[ii] = '-'; tries_p[3*ii] = ' '; tries_p[3*ii + 1] = '0';
  631: 	 if(ii < nq-1) tries_p[3*ii + 2] = ',';
  632:        }
  633:        ans_p[nq]='\0';tries_p[3*(nq)+2]='\0';tries_p[3*length-1]=',';
  634:      }
  635:    }
  636:    return (offset);
  637: }
  638: /*----------------------------------------------------------*/
  639: /*   Constants defining the column number for each fields   */
  640: /*    in classl file                                        */
  641: /*----------------------------------------------------------*/
  642: #define   SECTION_BEGIN_COLUMN  10
  643: #define   SN_BEGIN_COLUMN       14
  644: #define   NAME_BEGIN_COLUMN     24
  645: #define   EMAIL_BEGIN_COLUMN    60
  646: #define   CAPAID_BEGIN_COLUMN   100
  647: /*----------------------------------------------------------*/
  648: /* Retrieve a linked list of students with specified        */
  649: /*  section in classl file                                  */
  650: /*  If the input section number is 0, students in the whole */
  651: /*  classl will be retrieved.                               */
  652: /*  The resulting linked list is ordered according to the   */
  653: /*  order in classl                                         */
  654: /*  Remember to use free_students() to free up the linked   */
  655: /*  list once done with it.                                 */
  656: /*----------------------------------------------------------*/
  657: /* INPUT:  section    section number                        */
  658: /*                    0 means to load the whole class       */
  659: /* OUTPUT: student_pp                                       */
  660: /*                pointer to pointer of structure T_student */
  661: /* RETURN: number of students in specified section          */
  662: /*----------------------------------------------------------*/
  663: int
  664: capa_get_section(student_pp, section)         
  665: T_student **student_pp;
  666: int         section;      
  667: {
  668:   FILE      *fp;
  669:   char       line[SMALL_LINE_BUFFER],fmtbuf[SMALL_LINE_BUFFER], *cp;
  670:   int        i, line_len;
  671:   int        tmp_sec, student_cnt, tmp_capaid;
  672:   T_student *s1_p, *s2_p, *s3_p;
  673:   
  674:   if ((fp=fopen("classl","r"))==NULL) {
  675:     /*printf("Error: Can't open classlist file\n");*/
  676:     return (-1);
  677:   }
  678:   student_cnt = 0; s1_p = s2_p = s3_p = (T_student *)NULL;
  679:   while (fgets(line,SMALL_LINE_BUFFER-1,fp)) {
  680:     line_len = strlen(line);
  681:     if(line_len > 25 ) { /* contains valid information, instead of only cr */
  682:       sscanf(line+SECTION_BEGIN_COLUMN,"%d", &tmp_sec);
  683:       if (!section || tmp_sec == section)  {
  684:         s3_p = (T_student *)capa_malloc(sizeof(T_student),1);
  685:         s3_p->s_sec = tmp_sec;
  686:         sprintf(fmtbuf, "%%%dc",MAX_STUDENT_NUMBER); 
  687:         sscanf(line+SN_BEGIN_COLUMN,fmtbuf,s3_p->s_sn);
  688:         cp = (char *)&line[NAME_BEGIN_COLUMN];
  689:         for(i=0;i<MAX_NAME_CHAR;i++)  s3_p->s_nm[i]=' ';
  690:         i=0;
  691:         while( (i < MAX_NAME_CHAR) && 
  692:                ( isalnum(cp[i]) || cp[i] == ',' || cp[i] == '.' || cp[i] == '\'' ||
  693:                  cp[i] == ' '   || cp[i] == '\t'|| cp[i] == '-' ) ) {
  694:           s3_p->s_nm[i]=cp[i]; i++;
  695:         }
  696:         s3_p->s_nm[MAX_NAME_CHAR]='\0';
  697:         s3_p->s_email[0]=0;
  698:         if( line_len > EMAIL_BEGIN_COLUMN ) {  /* contains email */
  699:           cp = (char *)&line[EMAIL_BEGIN_COLUMN];
  700:           for(i=0;i<MAX_EMAIL_CHAR;i++)  s3_p->s_email[i]=' ';
  701:           i=0;
  702:           while( (i < MAX_EMAIL_CHAR) && 
  703:                  ( isalnum(cp[i]) || cp[i] == '@' || cp[i] == '.' ) ) {
  704:              s3_p->s_email[i]=cp[i]; i++;
  705:           }
  706:           s3_p->s_email[i]='\0'; /* terminate the email string with the string length */
  707:           if( line_len > CAPAID_BEGIN_COLUMN ) {  /* contains  capa id */
  708:             sscanf(line+CAPAID_BEGIN_COLUMN,"%d", &tmp_capaid);
  709:             if(tmp_capaid > 0 ) {
  710:               s3_p->s_capaid = tmp_capaid;
  711:             }
  712:           }
  713:         }
  714:         if( student_cnt == 0 ) {
  715:           s1_p = s3_p;
  716:           s3_p->s_prev = s3_p;  s3_p->s_next = (T_student *)NULL;
  717:           s2_p = s3_p;
  718:         } else {
  719:           s3_p->s_prev = s2_p;  s3_p->s_next = (T_student *)NULL;
  720:           s2_p->s_next = s3_p;
  721:           s2_p = s3_p;
  722:         }
  723:         student_cnt++;
  724:       }
  725:     }
  726:   }
  727:   fclose(fp);
  728:  *student_pp = s1_p;
  729:   return (student_cnt);
  730: }
  731: /*----------------------------------------------------------*/
  732: /*----------------------------------------------------------*/
  733: /* Retrieve a linked list of students with specified        */
  734: /*  section in classl file                                  */
  735: /*  If the input section number is 0, students in the whole */
  736: /*  classl will be retrieved.                               */
  737: /*  The resulting linked list is ordered according to       */
  738: /*  student name                                            */
  739: /*  Remember to use free_students() to free up the linked   */
  740: /*  list once done with it.                                 */
  741: /*----------------------------------------------------------*/
  742: /* INPUT:  section    section number                        */
  743: /*                    0 means to load the whole class       */
  744: /* OUTPUT: student_pp                                       */
  745: /*                pointer to pointer of structure T_student */
  746: /* RETURN: number of students in specified section          */
  747: /*----------------------------------------------------------*/
  748: 
  749: int
  750: capa_sorted_section(student_pp, section)         
  751: T_student **student_pp;
  752: int         section;      
  753: {
  754:   int         student_cnt;
  755:   T_student  *student_p, *c_sp;
  756:   
  757:    student_cnt = capa_get_section(&student_p,section);
  758:    if( student_cnt > 1 ) {
  759:      for(c_sp=student_p;c_sp;c_sp=c_sp->s_next){
  760:        strcpy(c_sp->s_key,c_sp->s_nm);
  761:      }
  762:      msort_main(&student_p);
  763:    } 
  764:    *student_pp = student_p;
  765:    return (student_cnt);
  766: }
  767: 
  768: /*----------------------------------------------------------*/
  769: /* INPUT:  address of a pointer to T_student structure      */
  770: /* OUTPUT: address of a pointer to T_student structure      */
  771: /*                                                          */
  772: /* RETURN: none                                             */
  773: /*----------------------------------------------------------*/
  774: void
  775: msort_main(head_pp) T_student  **head_pp;
  776: {
  777:   T_student   *right_p, *out_sp;
  778:   
  779:    if( (*head_pp != NULL) && ((*head_pp)->s_next != NULL) ) {
  780:      msort_split( *head_pp, &right_p);
  781:      msort_main(head_pp);  msort_main(&right_p);
  782:      msort_merge(*head_pp, right_p, &out_sp);
  783:      *head_pp = out_sp;
  784:    }
  785: }
  786: /*----------------------------------------------------------*/
  787: /* INPUT:  a_sp    pointer to T_student structur            */
  788: /* OUTPUT: a_sp                                             */
  789: /*         b_pp address of a pointer to T_student structure */
  790: /*                                                          */
  791: /* RETURN: none                                             */
  792: /*----------------------------------------------------------*/
  793: void
  794: msort_split(a_sp, b_pp) T_student *a_sp; T_student **b_pp;
  795: {
  796:    T_student  *c_sp;
  797:    
  798:    *b_pp = a_sp;
  799:    c_sp = a_sp->s_next;
  800:    c_sp = c_sp->s_next;
  801:    while( c_sp != NULL ) {
  802:      c_sp = c_sp->s_next;
  803:      *b_pp = (*b_pp)->s_next;
  804:      if( c_sp != NULL ) c_sp = c_sp->s_next;
  805:    }
  806:    c_sp = (*b_pp)->s_next;
  807:    (*b_pp)->s_next = NULL;
  808:    *b_pp = c_sp;
  809: }
  810: /*----------------------------------------------------------*/
  811: /* INPUT:  a_sp    pointer to T_student structur            */
  812: /*         b_sp    pointer to T_student structur            */
  813: /* OUTPUT: c_pp address of a pointer to T_student structure */
  814: /*                                                          */
  815: /* RETURN: none                                             */
  816: /*----------------------------------------------------------*/
  817: void
  818: msort_merge(a_sp, b_sp, c_pp) T_student *a_sp, *b_sp, **c_pp;
  819: {
  820:   T_student  *d_sp;
  821:   
  822:   if( a_sp == NULL || b_sp == NULL ) {
  823:   
  824:   } else {
  825:     if( strcmp(a_sp->s_key, b_sp->s_key) <= 0 ) {
  826:        *c_pp = a_sp;
  827:        a_sp = a_sp->s_next;
  828:     } else {
  829:        *c_pp = b_sp;
  830:        b_sp = b_sp->s_next;
  831:     }
  832:     d_sp = *c_pp;
  833:     while( (a_sp != NULL) && (b_sp != NULL) ) {
  834:       if( strcmp(a_sp->s_key, b_sp->s_key) <= 0) {
  835:         d_sp->s_next = a_sp;
  836:         d_sp = a_sp;
  837:         a_sp = a_sp->s_next;
  838:       } else {
  839:         d_sp->s_next = b_sp;
  840:         d_sp = b_sp;
  841:         b_sp = b_sp->s_next;
  842:       }
  843:     }
  844:     if( a_sp == NULL ) {
  845:       d_sp->s_next = b_sp;
  846:     } else {
  847:       d_sp->s_next = a_sp;
  848:     }
  849:   }
  850: }
  851: 
  852: /*----------------------------------------------------------*/
  853: /* INPUT:  head_p pointer to T_student structure            */
  854: /* OUTPUT: head_p pointed to sorted linked list             */
  855: /*                                                          */
  856: /* RETURN: none                                             */
  857: /*----------------------------------------------------------*/
  858: void
  859: insert_sort(head_p) T_student  *head_p;
  860: {
  861:   T_student  *tail_p, *i_p, *p_p, *r_p;
  862:   
  863:   if( head_p != NULL ) {
  864:     tail_p = head_p;
  865:     while( tail_p->s_next != NULL ) {
  866:       i_p = tail_p->s_next;
  867:       if( strcmp( i_p->s_key, head_p->s_key) < 0 ) {
  868:         tail_p->s_next = i_p->s_next;
  869:         i_p->s_next = head_p;
  870:         head_p = i_p;
  871:       } else {
  872:         r_p = head_p;
  873:         p_p = r_p->s_next;
  874:         while( strcmp(i_p->s_key, p_p->s_key) > 0 ) {
  875:           r_p = p_p;
  876:           p_p = r_p->s_next;
  877:         }
  878:         if( i_p == p_p ) {
  879:           tail_p = i_p;
  880:         } else {
  881:           tail_p->s_next = i_p->s_next;
  882:           i_p->s_next = p_p;
  883:           r_p->s_next = i_p;
  884:         }
  885:       }
  886:     }
  887:   }
  888: }
  889: /*----------------------------------------------------------*/
  890: /*  The purpose of this routine is to free up all spaces    */
  891: /*  in the linked list                                      */
  892: /*----------------------------------------------------------*/
  893: /* INPUT:  student_p pointer to T_student structure         */
  894: /* OUTPUT: none                                             */
  895: /*                                                          */
  896: /* RETURN: none                                             */
  897: /*----------------------------------------------------------*/
  898: 
  899: void
  900: free_students(student_p) T_student *student_p;
  901: {
  902:   T_student *s_p,*next_p;
  903:   
  904:    for (s_p=student_p; s_p; s_p=next_p) {
  905:       next_p = (s_p->s_next);
  906:       capa_mfree((char *)s_p);
  907:    }
  908: }
  909: 
  910: /*----------------------------------------------------------*/
  911: /*  The purpose of this routine is to free up all spaces    */
  912: /*  in the linked list                                      */
  913: /*----------------------------------------------------------*/
  914: /* INPUT:  student_p pointer to T_student structur          */
  915: /* OUTPUT: none                                             */
  916: /*                                                          */
  917: /* RETURN:  -2    file error                                */
  918: /*          -1    never login                               */
  919: /*          >= 0  score for that set                        */
  920: /*----------------------------------------------------------*/
  921: int
  922: capa_get_score(student_number,set,valid_scores,answers_pp)
  923: char  *student_number;int  set;int *valid_scores;char  **answers_pp;
  924: {
  925:   T_entry   a_record;
  926:   T_header  a_header;
  927:   long      offset;
  928:   int       set_scores, set_valids=0, probs_cnt;
  929:   int       ii=0, never_login=1;
  930:   
  931:   if( (offset = capa_get_entry(&a_record,student_number,set) ) == 0 ) { 
  932:      return (-2);
  933:   }
  934:   probs_cnt = a_record.e_probs;
  935:   if( capa_get_header(&a_header,set) < 0 ) { return (-2); }
  936:   if( offset < 0 ) {
  937:     for(set_valids=0,ii=0;ii<probs_cnt;ii++) {
  938:       set_valids    += (a_header.weight[ii] - '0');
  939:     }
  940:     set_scores = -1;
  941:     *answers_pp = a_record.answers;
  942:     capa_mfree(a_record.tries); a_record.tries = NULL;
  943:   } else {
  944:     for(set_scores=0,ii=0, never_login=1;ii<probs_cnt;ii++) {
  945:       switch(a_record.answers[ii]) { 
  946:         case 'Y': case 'y':  never_login = 0;
  947:                   set_scores    += (a_header.weight[ii] - '0'); 
  948:                   set_valids    += (a_header.weight[ii] - '0');  break; 
  949:         case '-': case 'N': case 'n': 
  950:                   set_valids    += (a_header.weight[ii] - '0');  break; 
  951:         case 'E': case 'e':                          break;
  952:         default : if( a_record.answers[ii]  >= '0' && 
  953:                       a_record.answers[ii] <= '9' ) {
  954:                     set_scores    += (a_record.answers[ii] - '0');
  955:                     set_valids    += (a_header.weight[ii] - '0');
  956:                     never_login = 0;
  957:                   } break;
  958:       }
  959:     }
  960:     *answers_pp = a_record.answers;
  961:     capa_mfree(a_record.tries); a_record.tries = NULL;
  962:   }
  963:   capa_mfree(a_header.weight);
  964:   capa_mfree(a_header.partial_credit);
  965:   *valid_scores = set_valids;
  966:   if(never_login) set_scores = -1;
  967:   return (set_scores);
  968: }
  969: /*----------------------------------------------------------*/
  970: /* Reads the classl file and returns the number of students */
  971: /*  for each section in cnt_arry[]                          */
  972: /*  In cnt_arry[0] is the maximum section number that       */
  973: /*  has at least one student                                */
  974: /*----------------------------------------------------------*/
  975: /* INPUT:  cnt_arry[]  integer array                        */
  976: /* OUTPUT: cnt_arry[]                                       */
  977: /*                                                          */
  978: /*                                                          */
  979: /* RETURN:  -1    file error                                */
  980: /*----------------------------------------------------------*/
  981: int
  982: capa_get_section_count(cnt_arry) int cnt_arry[]; 
  983: {
  984:   FILE   *fp;
  985:   char    filename[FILE_NAME_LENGTH];
  986:   char    line[TMP_LINE_LENGTH];
  987:   int     sec_num, cnt, i, max;
  988: 
  989:   sprintf(filename,"classl");
  990:   if ((fp=fopen(filename,"r"))==NULL) {
  991:     /*printf("Error: can't open %s\n",filename);*/
  992:       return (-1);
  993:   }
  994:   for(i=0;i<MAX_SECTION_COUNT;i++) cnt_arry[i]=0;
  995:   max = 0;
  996:   while ( fgets(line,TMP_LINE_LENGTH-1,fp) && (strlen(line) != 0) ) {
  997:    sec_num=0;
  998:    sscanf(line+SECTION_BEGIN_COLUMN,"%d", &sec_num);
  999:    if( sec_num != 0 ) {
 1000:      max = MAX(sec_num,max);
 1001:      cnt_arry[sec_num]++;
 1002:    }
 1003:   }
 1004:   fclose(fp);
 1005:   cnt = 0;
 1006:   for(i=1; i <= max; i++) {
 1007:     if(cnt_arry[i]) cnt++;
 1008:   }
 1009:   cnt_arry[0] = max;
 1010:   return (cnt);
 1011: }
 1012: /*----------------------------------------------------------*/
 1013: /*  lookup student information from classl file by          */
 1014: /*  student number                                          */
 1015: /*----------------------------------------------------------*/
 1016: /* INPUT:  student_number   char array of student number    */
 1017: /* OUTPUT: student_p     pointer to a T_student structure   */
 1018: /*                       that contains the student name,    */
 1019: /*                       student number, section number     */
 1020: /*                       of the student inquired            */
 1021: /*                                                          */
 1022: /* RETURN:  -1    file error                                */
 1023: /*           0    no such student                           */
 1024: /*           >0   success (line number in classl of student)*/
 1025: /*----------------------------------------------------------*/
 1026: int
 1027: capa_get_student(student_number, student_p) 
 1028: char      *student_number; 
 1029: T_student *student_p;
 1030: {
 1031:  FILE  *fp;
 1032:  char   line[SMALL_LINE_BUFFER],fmtbuf[SMALL_LINE_BUFFER],
 1033:         sNumber[MAX_STUDENT_NUMBER+1],
 1034:         aNumber[MAX_STUDENT_NUMBER+1];
 1035:  int    i,found,line_len,tmp_capaid,linenum=0;
 1036:  char  *cp;
 1037:  
 1038:   strncpy(sNumber, student_number,MAX_STUDENT_NUMBER+1);
 1039:   if ((fp=fopen("classl","r"))==NULL) {
 1040:     /*printf("Error: Can't open classlist file\n");*/
 1041:     return (-1);
 1042:   }
 1043:   found = 0;
 1044:   while (!found && fgets(line,SMALL_LINE_BUFFER-1,fp)) {
 1045:     linenum++;
 1046:     line_len = strlen(line);
 1047:     sprintf(fmtbuf, "%%%dc",MAX_STUDENT_NUMBER);
 1048:     sscanf(line+SN_BEGIN_COLUMN,fmtbuf,aNumber);
 1049:     if(!strncasecmp(sNumber,aNumber, MAX_STUDENT_NUMBER)) {
 1050:        found = 1;
 1051:        i=0;
 1052:        strncpy(student_p->s_sn, student_number,MAX_STUDENT_NUMBER+1);
 1053:        cp = (char *)&line[NAME_BEGIN_COLUMN];
 1054:        while( (i< MAX_NAME_CHAR) &&  
 1055:               (isalnum(cp[i]) || cp[i] == ',' || cp[i] == '.' || cp[i] == '\'' ||
 1056:               cp[i] == ' '    || cp[i] == '\t'|| cp[i] == '-' || cp[i] == '_'  || cp[i] == '~' ) ) {
 1057:          student_p->s_nm[i]=cp[i]; i++;
 1058:        }
 1059:        student_p->s_nm[i]='\0';
 1060:        sscanf(line+SECTION_BEGIN_COLUMN,"%d", &(student_p->s_sec));
 1061:        student_p->s_email[0]=0;
 1062:        if( line_len > EMAIL_BEGIN_COLUMN ) {  /* contains email */
 1063:           cp = (char *)&line[EMAIL_BEGIN_COLUMN];
 1064:           for(i=0;i<MAX_EMAIL_CHAR;i++)  student_p->s_email[i]=' ';
 1065:           i=0;
 1066:           while( (i < MAX_EMAIL_CHAR) && 
 1067:                  ( isalnum(cp[i]) || cp[i] == '@' || cp[i] == '.'  || cp[i] == '!' || cp[i] == '=' ||
 1068:                     cp[i] == '_'  || cp[i] == '-' || cp[i] == '+'  || cp[i] == '^' || cp[i] == '|' ) ) {
 1069:              student_p->s_email[i]=cp[i]; i++;
 1070:           }
 1071:           student_p->s_email[i]='\0'; /* terminate the email string with the string length */
 1072:           if( line_len > CAPAID_BEGIN_COLUMN ) {  /* contains  capa id */
 1073:             sscanf(line+CAPAID_BEGIN_COLUMN,"%d", &tmp_capaid);
 1074:             if(tmp_capaid > 0 ) {
 1075:               student_p->s_capaid = tmp_capaid;
 1076:             }
 1077:           }
 1078:        }
 1079:     }
 1080:   }
 1081:   fclose(fp);
 1082:   if (found > 0) found=linenum;
 1083:   return (found);
 1084: }
 1085: /*----------------------------------------------------------*/
 1086: /*  lookup student information from classl file by          */
 1087: /*  student name                                            */
 1088: /*----------------------------------------------------------*/
 1089: /* INPUT:  student_name   char array of student name        */
 1090: /* OUTPUT: student_p     pointer to a T_student structure   */
 1091: /*                       that contains the student name,    */
 1092: /*                       student number, section number     */
 1093: /*                       of the student inquired            */
 1094: /*                                                          */
 1095: /* RETURN:  -1    file error                                */
 1096: /*           0    no such student                           */
 1097: /*           1    success                                   */
 1098: /*----------------------------------------------------------*/
 1099: int       
 1100: capa_student_byname(student_name, student_p) 
 1101: char      *student_name; 
 1102: T_student *student_p;
 1103: {
 1104:  FILE  *fp;
 1105:  char   line[SMALL_LINE_BUFFER],fmtbuf[SMALL_LINE_BUFFER],
 1106:         sName[MAX_NAME_CHAR+1],
 1107:         aName[MAX_NAME_CHAR+1];
 1108:  int    i,found, len, line_len,tmp_capaid;
 1109:  char  *cp;
 1110:  
 1111:   len = strlen(student_name);
 1112:   strncpy(sName, student_name,MAX_NAME_CHAR+1);
 1113:   if ((fp=fopen("classl","r"))==NULL) {
 1114:     /*printf("Error: Can't open classlist file\n");*/
 1115:     return (-1);
 1116:   }
 1117:   found = 0;
 1118:   while (fgets(line,SMALL_LINE_BUFFER-1,fp)) {
 1119:     i=0;
 1120:     cp = (char *)&line[NAME_BEGIN_COLUMN];
 1121:     while( (i < MAX_NAME_CHAR) && 
 1122:            (isalnum(cp[i]) || cp[i] == ','  || cp[i] == '.' || cp[i] == '\'' ||
 1123:             cp[i] == ' '   || cp[i] == '\t' || cp[i] == '-' || cp[i] == '_'  || cp[i] == '~') ) {
 1124:         aName[i]=cp[i]; i++;
 1125:     }
 1126:     aName[i]='\0';
 1127:     if(!strncasecmp(sName,aName,len)) {
 1128:        found = 1;
 1129:        strcpy(student_p->s_nm,aName); 
 1130:        sscanf(line+SECTION_BEGIN_COLUMN,"%d", &(student_p->s_sec));
 1131:        sprintf(fmtbuf, "%%%dc",MAX_STUDENT_NUMBER);
 1132:        sscanf(line+SN_BEGIN_COLUMN,fmtbuf,student_p->s_sn);
 1133:        student_p->s_email[0]=0;
 1134:        line_len=strlen(line);
 1135:        if( line_len > EMAIL_BEGIN_COLUMN ) {  /* contains email */
 1136:           cp = (char *)&line[EMAIL_BEGIN_COLUMN];
 1137:           for(i=0;i<MAX_EMAIL_CHAR;i++)  student_p->s_email[i]=' ';
 1138:           i=0;
 1139:           while( (i < MAX_EMAIL_CHAR) && 
 1140:                  ( isalnum(cp[i]) || cp[i] == '@' || cp[i] == '.'  || cp[i] == '!' || cp[i] == '=' ||
 1141:                     cp[i] == '_'  || cp[i] == '-' || cp[i] == '+'  || cp[i] == '^' || cp[i] == '|' ) ) {  
 1142:              student_p->s_email[i]=cp[i]; i++;
 1143:           }
 1144:           student_p->s_email[i]='\0'; /* terminate the email string with the string length */
 1145:           if( line_len > CAPAID_BEGIN_COLUMN ) {  /* contains  capa id */
 1146:             sscanf(line+CAPAID_BEGIN_COLUMN,"%d", &tmp_capaid);
 1147:             if(tmp_capaid > 0 ) {
 1148:               student_p->s_capaid = tmp_capaid;
 1149:             }
 1150:           }
 1151:        }
 1152:     }
 1153:   }
 1154:   fclose(fp);
 1155:   return (found);
 1156: }
 1157: /*----------------------------------------------------------*/
 1158: /*  Randomly selected a student from classl file specified  */
 1159: /*  by section                                              */
 1160: /*----------------------------------------------------------*/
 1161: /* INPUT:  section   section number                         */
 1162: /* OUTPUT: student_p     pointer to a T_student structure   */
 1163: /*                       that contains the student name,    */
 1164: /*                       student number, section number     */
 1165: /*                       of the student inquired            */
 1166: /*                                                          */
 1167: /* RETURN:  -1    file error                                */
 1168: /*           0    no such student                           */
 1169: /*           1    success                                   */
 1170: /*----------------------------------------------------------*/
 1171: int
 1172: capa_pick_student(section, student_p) 
 1173: int        section; 
 1174: T_student *student_p;
 1175: {
 1176:   T_student  *s1_p, *s2_p;
 1177:   int         student_cnt, idx, pick=-1;
 1178:   
 1179:   student_cnt = capa_get_section(&s1_p,section);
 1180:   if(student_cnt > 0 ) {
 1181:     srand(time(NULL));
 1182:     pick = rand() % student_cnt;
 1183:     for(s2_p = s1_p, idx=0; s2_p && idx < pick; idx++) {
 1184:       s2_p = s2_p->s_next;
 1185:     }
 1186:     strcpy(student_p->s_sn,s2_p->s_sn);
 1187:     strcpy(student_p->s_nm,s2_p->s_nm);
 1188:     strcpy(student_p->s_email,s2_p->s_email);
 1189:     student_p->s_capaid=s2_p->s_capaid;
 1190:     student_p->s_sec = s2_p->s_sec;
 1191:     free_students(s1_p);
 1192:   }
 1193:   return (pick);
 1194: }
 1195: /* -------------------------------------------------- */
 1196: /*  add a student to the class file                   */
 1197: /*  check duplicated student id                       */
 1198: /*        student name?                               */
 1199: /*  Returns: 0   success                              */
 1200: /*           1   there is an duplicate entry          */
 1201: /* -------------------------------------------------- */
 1202: int
 1203: capa_add_student(student_p)
 1204: T_student *student_p;
 1205: {
 1206:  FILE  *fp;
 1207:  char   line[SMALL_LINE_BUFFER], fmtbuf1[SMALL_LINE_BUFFER],
 1208:         tmp_nm[MAX_STUDENT_NUMBER+1],
 1209:         tmp_nu[MAX_NAME_CHAR+1];
 1210:  int    i,found, len;
 1211:  char  *class, *cp;
 1212:  char   cid[4],cn[4];
 1213: #if defined(NeXT)
 1214:  char   cwd[FILE_NAME_LENGTH]; 
 1215: #endif
 1216: 
 1217: #if defined(NeXT) 
 1218:    class = getwd(cwd);
 1219:    if( class == NULL ) { class = cwd; }
 1220: #else
 1221:    class = getcwd(NULL,255);
 1222: #endif
 1223:   
 1224:   if( class == NULL ) { printf("capa_add_student(): Current working directory unknown!\n"); }
 1225:   len=strlen(class); cp=class; cp=cp+(len-8);
 1226:   for(i=0;i<3;i++) {
 1227:    cid[i] = cp[i+3];
 1228:    cn[i] = cp[i];
 1229:   }
 1230:   cid[3]=cn[3]=0;
 1231:   if ((fp=fopen("classl","r+"))==NULL) {
 1232:     /*printf("Error: Can't open classlist file\n");*/
 1233:     return (-1);
 1234:   }
 1235:   found = 0;
 1236:   sprintf(fmtbuf1, "%%%dc",MAX_STUDENT_NUMBER);
 1237:   while (fgets(line,SMALL_LINE_BUFFER-1,fp)) {
 1238:     sscanf(line+SN_BEGIN_COLUMN,fmtbuf1,tmp_nu);
 1239:     tmp_nu[MAX_STUDENT_NUMBER]='\0';
 1240:     if(!strncasecmp(student_p->s_sn,tmp_nu, MAX_STUDENT_NUMBER)) {
 1241:        found = 1; break;
 1242:     }
 1243:     i=0; cp = (char *)&line[NAME_BEGIN_COLUMN];
 1244:     while( (i < MAX_NAME_CHAR) && 
 1245:            (isalnum(cp[i]) || cp[i] == ',' || cp[i] == '.' || cp[i] == '\'' ||
 1246:            cp[i] == ' '    || cp[i] == '\t'|| cp[i] == '-' || cp[i] == '_' || cp[i] == '~') ) {
 1247:       tmp_nm[i]=cp[i]; i++;
 1248:     }
 1249:     tmp_nm[i]='\0';
 1250:     len=strlen(tmp_nm);
 1251:     if(!strncasecmp(student_p->s_nm,tmp_nm,len)) {
 1252:        found = 1;  break;
 1253:     }
 1254:   }
 1255:   if(!found) {
 1256:     sprintf(line,"%s %s   %03d %s %s\n",
 1257:        cn,cid,student_p->s_sec,student_p->s_sn,student_p->s_nm);
 1258:     len = strlen(line);
 1259:     fseek(fp,0L,SEEK_END);
 1260:     if (!fwrite(line,len,1,fp) ) {
 1261:       /*printf("Error writing data to file\n");*/
 1262:        found = -1;
 1263:     }
 1264:     fflush(fp);
 1265:   }
 1266:   fclose(fp);
 1267:   free(class);
 1268:   return (found);
 1269: }
 1270: /*----------------------------------------------------------*/
 1271: /* INPUT:  student_number  char array of student number     */
 1272: /*         set             the X in logX.db                 */
 1273: /* OUTPUT: none                                             */
 1274: /*                                                          */
 1275: /* RETURN:  -1    file error                                */
 1276: /*           0    no login                                  */
 1277: /*           >0   number of logins in that logX.db file     */
 1278: /*----------------------------------------------------------*/
 1279: int               
 1280: capa_get_login_count(student_number,set)
 1281: char  *student_number;
 1282: int    set;
 1283: {
 1284:    char     filename[FILE_NAME_LENGTH],
 1285:             sNumber[MAX_STUDENT_NUMBER+1],
 1286:             aNumber[MAX_STUDENT_NUMBER+1];
 1287:    FILE    *fp;
 1288:    char     line[MAX_BUFFER_SIZE],fmtbuf[MAX_BUFFER_SIZE];
 1289:    int      num;
 1290:    
 1291:   strncpy(sNumber, student_number,MAX_STUDENT_NUMBER+1);
 1292:   sprintf(filename,"records/log%d.db",set);
 1293:   if( !capa_access(filename, F_OK) ) {
 1294:     if ((fp=fopen(filename,"r"))==NULL) {
 1295:       /*printf("Error: can't open %s\n",filename);*/
 1296:       return (-1);
 1297:     }
 1298:   } else {
 1299:     return (-1);
 1300:   }
 1301:   num = 0;
 1302:   while (fgets(line,MAX_BUFFER_SIZE-1,fp)) {
 1303:     sprintf(fmtbuf, "%%%dc",MAX_STUDENT_NUMBER);
 1304:     sscanf(line,fmtbuf,aNumber);
 1305:     if(!strncasecmp(sNumber,aNumber, MAX_STUDENT_NUMBER)) num++;
 1306:   }
 1307:   fclose(fp);
 1308:   return (num);
 1309: }
 1310: /*----------------------------------------------------------*/
 1311: /* INPUT:  set             the X in logX.db                 */
 1312: /* OUTPUT: none                                             */
 1313: /*                                                          */
 1314: /* RETURN:  -1    file error                                */
 1315: /*           0    no login                                  */
 1316: /*           >0   number of logins in that logX.db file     */
 1317: /*----------------------------------------------------------*/
 1318: #define    ANSWER_BEGIN_COLUMN     35
 1319: int     
 1320: capa_get_login_db(login_item,num_probs,set) 
 1321: T_entry  login_item[];
 1322: int     *num_probs;
 1323: int      set;
 1324: {
 1325:  FILE    *fp;
 1326:  char     filename[FILE_NAME_LENGTH];
 1327:  char     line[MAX_BUFFER_SIZE],fmtbuf[SMALL_LINE_BUFFER];
 1328:  T_header header;
 1329:  int      num_q, count, len;
 1330:  
 1331:   if(capa_get_header(&header,set)) return (0);
 1332:   sscanf(header.num_questions,"%d",&num_q);
 1333:   *num_probs = num_q;
 1334:   capa_mfree(header.weight);
 1335:   capa_mfree(header.partial_credit);
 1336: 
 1337:   sprintf(filename,"records/log%d.db",set);
 1338:   if((fp=fopen(filename,"r"))==NULL) {
 1339:     /*printf("Error: can't open %s\n",filename);*/
 1340:     return (-1);
 1341:   }
 1342:   count=0;
 1343:   while ( fgets(line,MAX_BUFFER_SIZE,fp) && (strlen(line) != 0 )) {
 1344:     len = strlen(line);
 1345:     if(len > ANSWER_BEGIN_COLUMN ) {
 1346:       sprintf(fmtbuf, "%%%dc",MAX_STUDENT_NUMBER);
 1347:       sscanf(line,fmtbuf, login_item[count].student_number);
 1348:       login_item[count].answers = capa_malloc(num_q,1);
 1349:       sprintf(fmtbuf, "%%%dc",num_q);
 1350:       sscanf(line+ANSWER_BEGIN_COLUMN,fmtbuf, login_item[count].answers);
 1351:       count++;
 1352:     }
 1353:   }
 1354:   fclose(fp);
 1355:   return (count);
 1356: }
 1357: 
 1358: /*----------------------------------------------------------*/
 1359: /* INPUT:  option   the option the check                    */
 1360: /*         set      the X in dateX.db                       */
 1361: /*         section  which section to check for              */
 1362: /*                                                          */
 1363: /* OUTPUT: none                                             */
 1364: /*                                                          */
 1365: /* RETURN:  -2    invalid option                            */
 1366: /*          -1    capa_get_section_dates error              */
 1367: /*           0    no                                        */
 1368: /*           1    yes                                       */
 1369: /*----------------------------------------------------------*/
 1370: int capa_check_option(int option,int set,int section)
 1371: {
 1372:   T_dates* dates;
 1373:   int result;
 1374: 
 1375:   result=capa_get_section_dates(section,set,&dates);
 1376:   if (result < 0) return -1;
 1377:   switch(option) {
 1378:   case OPTION_INHIBIT_RESPONSE: result=dates->inhibit_response;break;
 1379:   case OPTION_VIEW_PROBLEMS_AFTER_DUE: result=dates->view_problems_after_due;break;
 1380:   default: result=-2;
 1381:   }
 1382:   capa_mfree((char*)dates);
 1383:   return result;
 1384: }
 1385: 
 1386: /*----------------------------------------------------------*/
 1387: /* INPUT:  time             the current time                */
 1388: /*         datetime         the datetime string to compare  */
 1389: /* OUTPUT: none                                             */
 1390: /*                                                          */
 1391: /* RETURN:  -1    time is earlier then datetime             */
 1392: /*           1    time is later than datetime               */
 1393: /*----------------------------------------------------------*/
 1394: int
 1395: compare_datetime(time,datetime)
 1396: time_t  time;
 1397: char   *datetime;
 1398: {
 1399:   char    dateStr[16], timeStr[16];
 1400:   time_t  time2;
 1401:   char   *t_ptr;
 1402:   int     idx;
 1403:   
 1404: /*  sscanf(datetime,"%s %s",dateStr,timeStr); */
 1405:   t_ptr = index(datetime,' '); t_ptr++;    /*** hpux complained */
 1406:   for(idx=0;idx<10;idx++) dateStr[idx] = datetime[idx];
 1407:   dateStr[10] = 0;
 1408:   for(idx=0;idx<5;idx++) timeStr[idx] = t_ptr[idx];
 1409:   timeStr[5] = 0;
 1410:   time2 = convertTime(dateStr,timeStr);
 1411:   if(time == time2 ) return (0);
 1412:   return ( (time >  time2)? 1 : -1 );
 1413: }
 1414: 
 1415: /* --------------------------------- */
 1416: /* check if records/dateX.db exists  */
 1417: /* read the section open date, answer date, due date info */
 1418: /* What if: [7,3] date_info  */
 1419: /*          [3,7] date_info  */
 1420: /*          4 date_info      */
 1421: /* RETURN:  -1    if not pass time                          */
 1422: /*           1    if pass time (or no time available        */
 1423: int capa_check_date(int which,char *student_number, int section,int set)
 1424: {
 1425:   int result;
 1426:   char date_str[TMP_LINE_LENGTH];
 1427:   time_t curtime;
 1428:   
 1429:   time(&curtime);
 1430:   
 1431:   result = capa_get_date(which,student_number,section,set,date_str);
 1432:   if ( result == 1 ) { result = compare_datetime(curtime,date_str); }
 1433:   return result;
 1434: }
 1435: 
 1436: time_t capa_convert_duration(char *duration)
 1437: {
 1438:   int hour, minute;
 1439:   sscanf(duration,"%d:%d",&hour,&minute);
 1440:   return ((hour*60)+minute)*60;
 1441: }
 1442: 
 1443: void capa_get_due_date(char *date_str,T_dates *current,char *student_number,int set)
 1444: {
 1445:   time_t duration=capa_convert_duration(current->duration);
 1446:   time_t duetime=0;
 1447:   time_t logintime;
 1448:   struct tm *due_time_tm;
 1449:   if ((duration > 0) && (student_number!=NULL)) {
 1450:     if (capa_get_login_time(student_number,set,&logintime)==1) {
 1451:       duetime=logintime+duration;
 1452:       if (compare_datetime(duetime,current->due_date)==-1) {
 1453: 	due_time_tm=localtime(&duetime);
 1454: 	sprintf(date_str,"%04d/%02d/%02d %02d:%02d",((due_time_tm->tm_year)+1900),
 1455: 		due_time_tm->tm_mon+1,due_time_tm->tm_mday,due_time_tm->tm_hour,
 1456: 		due_time_tm->tm_min);
 1457:       } else {
 1458: 	strncpy(date_str,current->due_date,DATE_BUFFER); 
 1459:       }
 1460:       return;
 1461:     }
 1462:   } 
 1463:   strncpy(date_str,current->due_date,DATE_BUFFER); 
 1464: }
 1465: 
 1466: /* student_number can be NULL, if it isn't NULL it is used by get_due_date */
 1467: /* to assign a due date based on the time the student first accessed the   */
 1468: /* set if duration is non-zero                                             */
 1469: int
 1470: capa_get_date(int which,char *student_number,int section,int set,char *date_str)
 1471: {
 1472:   T_dates *current;
 1473:   int result;
 1474:   result=capa_get_section_dates(section,set,&current);
 1475:   if (result<0) return result;
 1476:   result=1;
 1477:   switch(which) {
 1478:   case CHECK_OPEN_DATE:  strncpy(date_str,current->open_date,DATE_BUFFER);  break;
 1479:   case CHECK_DUE_DATE:   capa_get_due_date(date_str,current,student_number,set); break;
 1480:   case CHECK_ANS_DATE:   strncpy(date_str,current->answer_date,DATE_BUFFER); break;
 1481:   default: strncpy(date_str,current->open_date,DATE_BUFFER);  result=-4;break;
 1482:   }
 1483:   capa_mfree((char*)current);
 1484:   return result;
 1485: }
 1486: 
 1487: int
 1488: capa_get_duration(char *student_number,int section,int set)
 1489: {
 1490:   T_dates *current;
 1491:   int duration=0,result;
 1492:   result=capa_get_section_dates(section,set,&current);
 1493:   if (result<0) return result;  
 1494:   duration=capa_convert_duration(current->duration);
 1495:   capa_mfree((char*)current);
 1496:   return duration;
 1497: }
 1498: 
 1499: int
 1500: capa_get_section_dates(int section,int set,T_dates** dates)
 1501: {
 1502:   register   int  c;
 1503:   FILE      *fp;          
 1504:   int        result = 0, done;
 1505:   int        tmp_low, tmp_high, sec_mode;
 1506:   char       filename[FILE_NAME_LENGTH], tmpline[TMP_LINE_LENGTH],
 1507:     default_line[TMP_LINE_LENGTH];
 1508:   
 1509:   sprintf(filename,"records/date%d.db",set);
 1510:   if( capa_access(filename, F_OK) != 0 ) { result = -2; } else {
 1511:     if ((fp=fopen(filename,"r")) == NULL) { result = -2; } else { 
 1512:       done = 0; result = -3;
 1513:       /* first non-comment line is assumed to be the default line */
 1514:       c_gettype(fp); c_ignorewhite(fp); fgets(default_line,TMP_LINE_LENGTH-1,fp);
 1515:       do {
 1516: 	c_ignorewhite(fp); c = getc(fp); ungetc(c,fp);
 1517: 	if( c != EOF ) {
 1518: 	  sec_mode = c_getsec_range(fp,&tmp_low,&tmp_high);
 1519: 	  if( sec_mode > 0 ) { /* no error */
 1520: 	    c_ignorewhite(fp); fgets(tmpline,TMP_LINE_LENGTH-1,fp);
 1521: 	    if( sec_mode == 1 ) { /* form: sec date_info */
 1522: 	      if((result == -3) && (tmp_low == section)) {
 1523: 		result=-4;
 1524: 		*dates=add_date_info(tmp_low,tmp_low,tmpline);
 1525: 		if ( *dates != NULL ) { done = 1; result = 1; }
 1526: 	      }
 1527: 	    } else { /* form : [low,high] date_info */
 1528: 	      if( (result == -3) && ((section>=tmp_low)&&(section<=tmp_high)) ) { 
 1529: 		result = -4;
 1530: 		*dates=add_date_info(tmp_low,tmp_high,tmpline);
 1531: 		if ( *dates != NULL ) { done = 1; result = 1; }
 1532: 	      }
 1533: 	    }
 1534: 	  } else { /* error in specify section date */
 1535: 	    while ( ((c = getc(fp)) != '\n') && ( c != EOF) ); /* skip to end of line */
 1536: 	  }
 1537: 	} else { /* EOF encountered */
 1538: 	  done = 1;
 1539: 	}
 1540:       } while (!done);
 1541:       /* need to deal with those sections not show up in the dateX.db file */
 1542:       if( result == -3 ) { /* section not in the dateX.db file, use default */
 1543: 	result = -4;
 1544: 	*dates=add_date_info(DATE_DEFAULTS,DATE_DEFAULTS,default_line);
 1545: 	if ( *dates != NULL ) { result = 1; }
 1546:       }
 1547:     }
 1548:     fclose(fp);
 1549:   } 
 1550:   return (result); 
 1551: }
 1552: 
 1553: T_dates* add_date_info(int lowsec,int highsec, char *dateinfo)
 1554: {
 1555:   T_dates *dates;
 1556:   int result;
 1557:   dates=(T_dates*)capa_malloc(sizeof(T_dates),1);
 1558:   result=sscanf(dateinfo,"%16c,%16c,%16c,%s %d,%d",dates->open_date,dates->due_date,
 1559: 		dates->answer_date,dates->duration,&(dates->inhibit_response),
 1560: 		&(dates->view_problems_after_due));
 1561:   if (result != 6) {
 1562:     capa_mfree((char*)dates);
 1563:     dates=NULL;
 1564:   } else {
 1565:     dates->section_start=lowsec;
 1566:     dates->section_end=highsec;
 1567:     dates->s_next=NULL;
 1568:   }
 1569:   return dates;
 1570: }
 1571: /*----------------------------------------------------------*/
 1572: /* INPUT:  set             the X in dateX.db                */
 1573: /*         dates           a pointer to the dates pointer   */
 1574: /* ACTION: builds a linked list of T_dates containing       */
 1575: /*         the login information                            */
 1576: /* RETURN:  <0    file error                                */
 1577: /*          >0    success (number of lines successfully     */
 1578: /*                         proccessed)                      */
 1579: /*----------------------------------------------------------*/
 1580: int capa_get_all_dates (int set,T_dates **dates) 
 1581: {
 1582:   T_dates    *current;
 1583:   FILE      *fp;          
 1584:   int        result = 0, done, tmp_low, tmp_high, sec_mode, c, num=0;
 1585:   char       filename[FILE_NAME_LENGTH], tmpline[TMP_LINE_LENGTH];
 1586:   
 1587:   sprintf(filename,"records/date%d.db",set);
 1588:   if ( ((fp=fopen(filename,"r")) == NULL)) { result = -2; } else {
 1589:     done = 0; result = -3;
 1590:     /* first non-comment line is assumed to be the default line */
 1591:     c_gettype(fp); c_ignorewhite(fp); fgets(tmpline,TMP_LINE_LENGTH-1,fp);
 1592:     *dates=add_date_info(DATE_DEFAULTS,DATE_DEFAULTS,tmpline);
 1593:     num++;
 1594:     if ( NULL == (current=*dates) ) {
 1595:       result = -3; 
 1596:     } else {
 1597:       while(1) {
 1598: 	c_ignorewhite(fp); c = getc(fp); ungetc(c,fp);
 1599: 	if( c == EOF ) { break; } 
 1600: 	sec_mode = c_getsec_range(fp,&tmp_low,&tmp_high);
 1601: 	if( sec_mode > 0 ) { /* no error */
 1602: 	  c_ignorewhite(fp); fgets(tmpline,TMP_LINE_LENGTH-1,fp);
 1603: 	  if( sec_mode == 1 ) { /* form: sec date_info */
 1604: 	    current->s_next=add_date_info(tmp_low,tmp_low,tmpline);
 1605: 	  } else { /* form : [low,high] date_info */
 1606: 	    current->s_next=add_date_info(tmp_low,tmp_high,tmpline);
 1607: 	  }
 1608: 	  current=current->s_next;
 1609: 	  num++;
 1610: 	} else { /* error in specify section date */
 1611: 	  while ( ((c = getc(fp)) != '\n') && ( c != EOF) ); /* skip to end of line */
 1612: 	}
 1613:       }
 1614:       result=num;
 1615:     }
 1616:     fclose(fp);
 1617:   }
 1618:   return (result); 
 1619: }
 1620: 
 1621: /*----------------------------------------------------------*/
 1622: /* INPUT:  dates           a pointer to the dates pointer   */
 1623: /* ACTION: frees a linked list of T_dates                   */ 
 1624: /*----------------------------------------------------------*/
 1625: void free_dates(T_dates *dates)
 1626: {
 1627:   T_dates *current = dates,*next;
 1628:   while ( current != NULL ) { 
 1629:     next = current->s_next;
 1630:     capa_mfree((char*)current);
 1631:     current = next;
 1632:   }
 1633: }
 1634: 
 1635: /*----------------------------------------------------------*/
 1636: /* INPUT:  set             the X in dateX.db                */
 1637: /*         dates           a pointer to the dates pointer   */
 1638: /* ACTION: takes a linked list of date information and      */
 1639: /*         and writes the info to a file                    */
 1640: /* RETURN:  -1    file error                                */
 1641: /*           1    success                                   */
 1642: /*----------------------------------------------------------*/
 1643: int capa_set_all_dates (int set,T_dates *dates) 
 1644: {
 1645:   T_dates *current = dates;
 1646:   int result;
 1647:   FILE* fp;
 1648:   char filename[FILE_NAME_LENGTH];
 1649:   
 1650:   sprintf(filename,"records/date%d.db",set);
 1651:   if ( ((fp=fopen(filename,"w")) == NULL) ) { result = -1; } else {
 1652:     result=1;
 1653:     while ( current != NULL ) { 
 1654:       if ( current->section_start == DATE_DEFAULTS ) {
 1655: 	fprintf(fp,"<< DEFAULTS >> ");
 1656:       } else {
 1657: 	fprintf(fp,"[%d, %d] ", current->section_start,current->section_end);
 1658:       }
 1659:       fprintf(fp,"%s,%s,%s,%s %d,%d\n", current->open_date,current->due_date,
 1660: 	      current->answer_date,current->duration,current->inhibit_response,
 1661: 	      current->view_problems_after_due);
 1662:       current = current->s_next;
 1663:     }
 1664:     fclose(fp);
 1665:   }
 1666:   return result;
 1667: }
 1668: 
 1669: /*----------------------------------------------------------*/
 1670: /* INPUT:  set             the X in logX.db                 */
 1671: /* OUTPUT: none                                             */
 1672: /*                                                          */
 1673: /* RETURN:  -1    file error                                */
 1674: /*           0    no login                                  */
 1675: /*           >0   number of logins in that logX.db file     */
 1676: /*----------------------------------------------------------*/
 1677: 
 1678: #define     FIFTEEN_MINUTES     (15*60)
 1679: #define     TEN_MINUTES         (600)
 1680: #define     ONE_DAY             (86400)
 1681: int         /* RETURNS:  1 first time login, 2 second time login, 0 not ok, -1 file error    */
 1682: login_check(student_number)     /* ARGUMENTS:             */
 1683: char *student_number;           /*    Student #           */
 1684: {                               /* LOCALS:                */
 1685:    FILE    *fp; 
 1686: 
 1687:    int      errcode=0;
 1688:    int      found;
 1689:    char     filename[FILE_NAME_LENGTH];
 1690:    char     line[SMALL_LINE_BUFFER], new_line[SMALL_LINE_BUFFER];
 1691:    struct tm  *theDT;
 1692:    time_t   login_time, record_time;
 1693:    long     offsetL, offsetR, offsetEnd,left_leng;
 1694:    char      s_number[MAX_STUDENT_NUMBER+1];
 1695:    char      tty_name[FILE_NAME_LENGTH];
 1696:    int       log_tries, p_id;
 1697:    int       month, day, year, hour, min, sec;
 1698:    char     *tmp_buffer;
 1699: 
 1700:    sprintf(filename,"records/active.log");
 1701: 
 1702:    if( capa_access(filename,F_OK) < 0 ) {  /*<------------- File not exist */
 1703:       if ((fp=fopen(filename,"w"))==NULL) { /* create if non-existant */
 1704: 	/*printf("Error: can't create %s\n",filename);*/
 1705:          return (-1); 
 1706:       }
 1707:       fclose(fp);
 1708:    }
 1709:    if ((fp=fopen(filename,"r"))==NULL) {
 1710:      /*printf("Error: can't open %s\n",filename);*/
 1711:       return (-1);
 1712:    }
 1713:    flockstream(fp); /* lock exclusively and perform read/write operation */
 1714:     
 1715:    found = 0;
 1716:    while( (!found) && ( fgets(line,SMALL_LINE_BUFFER-1,fp) != NULL) ) {
 1717:        if( !strncasecmp(line,student_number,MAX_STUDENT_NUMBER) )  found = 1;
 1718:    }
 1719:    offsetR = ftell(fp);
 1720:    offsetL = offsetR - strlen(line);
 1721:    funlockstream(fp); fclose(fp);   
 1722:    
 1723:    if( found && (strlen(line) != 0) ) {
 1724:      /* printf("FOUND:%slen=%d\n",line,strlen(line)); */
 1725:      sscanf(line,"%s , %d , %d , /dev/%s ,(%d/%d/%d %d:%d:%d)\n", s_number,&log_tries,&p_id,tty_name,&month,&day,&year,&hour,&min,&sec);
 1726:      record_time = time(NULL);
 1727:      theDT = localtime(&record_time);
 1728:      theDT->tm_sec = sec;     theDT->tm_min = min;
 1729:      theDT->tm_hour = hour;   theDT->tm_mday = day;
 1730:      theDT->tm_mon = month-1; theDT->tm_year = year;
 1731:      theDT->tm_wday = weekday(year,month,day);
 1732:      theDT->tm_yday = julianday(year,month,day); 
 1733:      record_time = mktime( theDT );
 1734:      time(&login_time);       theDT = localtime(&login_time);
 1735:      switch(log_tries) {
 1736:         case 0: log_tries = 1; errcode = 1;  break;
 1737:         case 1: log_tries = 2; errcode = 2; 
 1738:                 break;
 1739:         case 2: log_tries = 3; errcode = 0;  break;
 1740:         case 3: if( (login_time - record_time) >= TEN_MINUTES ) {
 1741:                    log_tries = 1; errcode = 1;
 1742:                 } else {
 1743:                    log_tries = 3; errcode = 2;
 1744:                    return (0);
 1745:                 }
 1746:                 break;
 1747:        default: printf("ERROR: Number of logins UNKNOWN\n");
 1748:                 log_tries = 1; errcode = 1;
 1749:               break;
 1750:      }
 1751:      sprintf(new_line,"%s , %2d , %5d , %s ,(%02d/%02d/%02d %02d:%02d:%02d)\n", student_number, log_tries, getpid(),ttyname(0), theDT->tm_mon + 1, theDT->tm_mday, theDT->tm_year, theDT->tm_hour, theDT->tm_min ,theDT->tm_sec);
 1752:      if ((fp=fopen(filename,"r+"))==NULL) {
 1753:        /*printf("Error: can't open %s\n",filename);*/
 1754:         return (-1);
 1755:      }
 1756:      flockstream(fp);
 1757:      tmp_buffer = (char *)malloc(8*1024*56);
 1758:      found = 0;
 1759:      while( (!found) && ( fgets(line,SMALL_LINE_BUFFER-1,fp) != NULL) ) {
 1760:        if( !strncasecmp(line,student_number,MAX_STUDENT_NUMBER) )  found = 1;
 1761:      }
 1762:      offsetR = ftell(fp);   offsetL = offsetR - strlen(line);
 1763:      fseek(fp,0L,SEEK_END); offsetEnd = ftell(fp);
 1764:      left_leng = offsetEnd - offsetR;
 1765:      fseek(fp,offsetR,SEEK_SET);
 1766:      left_leng = fread(tmp_buffer, 1, left_leng+1, fp);
 1767:      tmp_buffer[left_leng] = 0;
 1768:      fseek(fp,offsetL,SEEK_SET);
 1769:       if ( fprintf(fp,"%s%s",new_line,tmp_buffer) < 0  ) {
 1770: 	/*printf("Error: cannot write active.log\n");*/
 1771:            errcode = -1;
 1772:       }
 1773:      fflush(fp);
 1774:      free( (char *)tmp_buffer);
 1775:      funlockstream(fp);
 1776:      fclose(fp);
 1777:    } else { /********************************** First time login */
 1778:      if ((fp=fopen(filename,"a+"))==NULL) {
 1779:        /*printf("Error: can't open %s\n",filename);*/
 1780:         return (-1);
 1781:      }
 1782:      log_tries = 1;
 1783:      time(&login_time);
 1784:      theDT = localtime(&login_time);
 1785:      sprintf(line,"%s , %2d , %5d , %s ,(%02d/%02d/%02d %02d:%02d:%02d)\n", student_number, log_tries, getpid(),ttyname(0), theDT->tm_mon + 1, theDT->tm_mday, theDT->tm_year, theDT->tm_hour, theDT->tm_min ,theDT->tm_sec);
 1786: /*
 1787:      leng = strlen(line);
 1788:      for(idx = 0, len_idx = 0; idx<(leng-1); idx++) {
 1789:        if(line[idx] == '\n' && line[idx+1] == '\n') {
 1790:           line[idx+1] = 0;
 1791:        }
 1792:      }
 1793: */
 1794:      flockstream(fp);
 1795:      if ( !fwrite((char *)line, strlen(line), 1, fp) ) {
 1796:        /*printf("ERROR: cannot write active.log\n");*/
 1797:            errcode = -1;
 1798:      } else {
 1799:            errcode = 1;
 1800:      }
 1801:      fflush(fp);
 1802:      funlockstream(fp);
 1803:      fclose(fp);
 1804:    }
 1805:     return (errcode);
 1806:  
 1807: }
 1808: 
 1809: /******************************************************************************/
 1810: /* Logout check                                                               */
 1811: /******************************************************************************/
 1812: int                /* RETURNS:   1 successful, 0  otherwise, -1 file error  */
 1813: logout_check(student_number)     /* ARGUMENTS:             */
 1814: char *student_number;             /*    Student #           */
 1815: {                                /* LOCALS:                */
 1816:    FILE    *fp; 
 1817: 
 1818:    int      errcode=0;
 1819:    int      found;
 1820:    char     filename[FILE_NAME_LENGTH];
 1821:    char     line[SMALL_LINE_BUFFER];
 1822:    long     offsetL, offsetR, offsetEnd,left_leng;
 1823:    char     s_number[MAX_STUDENT_NUMBER+1];
 1824:    char     tty_name[FILE_NAME_LENGTH];
 1825:    int      log_tries, p_id;
 1826:    int      month, day, year, hour, min, sec;
 1827:    char     *tmp_buffer;
 1828: 
 1829: 
 1830:    sprintf(filename,"records/active.log");
 1831:    if ((fp=fopen(filename,"r"))==NULL) {
 1832:      /*printf("Error: can't open %s\n",filename);*/
 1833:       return (-1);
 1834:    }
 1835:    flockstream(fp); /* lock exclusively and perform read/write operation */
 1836:     
 1837:    found = 0;
 1838:    while( (!found) && ( fgets(line,SMALL_LINE_BUFFER-1,fp) != NULL) ) {
 1839:        if( !strncasecmp(line,student_number,MAX_STUDENT_NUMBER) )  found = 1;
 1840:    }
 1841:    offsetR = ftell(fp);
 1842:    offsetL = offsetR - strlen(line);
 1843:     
 1844:    funlockstream(fp);
 1845:    fclose(fp);   
 1846:    
 1847:    if( found ) {
 1848: #ifdef __alpha
 1849: sscanf(line,"%s , %d , %d , /dev/%s ,(%d/%d/%d %d:%d:%d)\n", s_number,&log_tries,&p_id,tty_name,&month,&day,&year,&hour,&min,&sec);
 1850: #else
 1851:      sscanf(line,"%s , %d , %d , /dev/%s ,(%d/%d/%d %d:%d:%d)\n", \
 1852:             s_number,&log_tries,&p_id,tty_name,&month,&day,&year,&hour,&min,&sec);
 1853: #endif
 1854:      switch(log_tries) {
 1855:         case 0: log_tries = 0;
 1856:               break;
 1857:         case 1: log_tries = 0;
 1858:               break;
 1859:         case 2: log_tries = 0;
 1860:               break;
 1861:         default: printf("ERROR: Number of logins UNKNOWN\n");
 1862:                 log_tries = 0;
 1863:               break;
 1864:      }
 1865: #ifdef __alpha
 1866: sprintf(line,"%s , %2d , %5d , /dev/%s ,(%02d/%02d/%02d %02d:%02d:%02d)\n", s_number,log_tries, p_id, tty_name, month, day, year, hour, min, sec);
 1867: #else
 1868:      sprintf(line,"%s , %2d , %5d , /dev/%s ,(%02d/%02d/%02d %02d:%02d:%02d)\n", \
 1869:            s_number,log_tries, p_id, tty_name, month, day, year, hour, min, sec);
 1870: #endif
 1871: 
 1872:      if ((fp=fopen(filename,"r+"))==NULL) {
 1873:        /*printf("Error: can't open %s\n",filename);*/
 1874:         return (-1);
 1875:      }
 1876:      flockstream(fp);
 1877:      tmp_buffer = (char *)malloc(8*1024*56);
 1878:      fseek(fp,0L,SEEK_END);
 1879:      offsetEnd = ftell(fp);
 1880:      left_leng = offsetEnd - offsetR;
 1881:      fseek(fp,offsetR,SEEK_SET);
 1882:      fread(tmp_buffer, left_leng, 1, fp);
 1883:      tmp_buffer[left_leng] = 0;
 1884: /*
 1885:      for(idx=0, l_idx = 0; idx< (left_leng-1); idx++) {
 1886:        if( tmp_buffer[idx] == '/n' && tmp_buffer[idx+1] == '/n' ) {
 1887:        }
 1888:      }
 1889: */
 1890:      fseek(fp,offsetL,SEEK_SET);
 1891:       if ( fprintf(fp,"%s%s",line,tmp_buffer) < 0  ) {
 1892: 	/*printf("Error: write error\n");*/
 1893:            errcode = -1;
 1894:       } else {
 1895:            errcode = 1;
 1896:       }
 1897:      fflush(fp);
 1898:      free( (char *)tmp_buffer);
 1899:      funlockstream(fp);
 1900:      fclose(fp);
 1901:    } else {
 1902:      errcode = -1;
 1903:    }
 1904:     return (errcode);
 1905:  
 1906: }
 1907: /*********************************************/
 1908: /*  SIZE of char =1, int=4, long=4, double=8 */
 1909: void
 1910: capa_seed(seed1,seed2,student_number)long *seed1;long *seed2;char *student_number;
 1911: {
 1912:   int   class_pin1, class_pin2;
 1913:   int   s_pin1, s_pin2, s_pin3;
 1914:   int   i;
 1915:   char  dest[16], tmpSN[MAX_STUDENT_NUMBER+1];
 1916:   char *class,*capadefault="capadefault";
 1917:   long  part1, part2;
 1918: #if defined(NeXT)
 1919:   char  cwd[FILE_NAME_LENGTH];
 1920: #endif
 1921:   int   big_endian;
 1922: 
 1923:   big_endian = endian();  /* determine what type of CPU we are on */
 1924:   
 1925: #if defined(NeXT) 
 1926:    class = getwd(cwd);
 1927:    if( class == NULL ) { class = cwd; }
 1928: #else
 1929:    class = getcwd(NULL,255);
 1930: #endif
 1931:      class_pin1 = class_pin2 = 2;
 1932:      s_pin1 =  s_pin2 = s_pin3 = 2;
 1933: 
 1934:   if( class == NULL ) { 
 1935:     printf("capa_seed(): Current working directory unknown! Using capadefault\n"); 
 1936:     class=capa_malloc(strlen(capadefault)+1,1);
 1937:     strcpy(class,capadefault);
 1938:   }
 1939:   if( big_endian ) {
 1940:       for(i=0;i<4;i++) dest[i] = class[strlen(class)-8+i];
 1941:       for(i=4;i<8;i++) dest[i] = 0;
 1942:       memcpy((char *)(&class_pin1), dest, 4);
 1943:       for(i=0;i<4;i++) dest[i] = class[strlen(class)-4+i];
 1944:       for(i=4;i<8;i++) dest[i] = 0;
 1945:       memcpy((char *)(&class_pin2), dest, 4);
 1946:   } else { 
 1947:       for(i=0;i<4;i++) dest[i] = class[strlen(class)-i-5];
 1948:       for(i=4;i<8;i++) dest[i] = 0;
 1949:       memcpy((char *)(&class_pin1), dest, 4);
 1950:       for(i=0;i<4;i++) dest[i] = class[strlen(class)-i-1];
 1951:       for(i=4;i<8;i++) dest[i] = 0;
 1952:       memcpy((char *)(&class_pin2), dest, 4);
 1953:   }
 1954:   for(i=0;i<MAX_STUDENT_NUMBER;i++) {
 1955:     if(islower(student_number[i])) {
 1956:       tmpSN[i] = toupper(student_number[i]);
 1957:     } else {
 1958:       tmpSN[i] = student_number[i];
 1959:     }
 1960:   }
 1961:   tmpSN[MAX_STUDENT_NUMBER] = 0;
 1962:  
 1963:   if( big_endian ) {  /* big endian ** SUN, BlackNeXT 68xxx , PowerPC */
 1964:     for(i=0;i<4;i++) dest[i] = tmpSN[i]; 
 1965:     for(i=4;i<8;i++) dest[i] = 0;
 1966:     memcpy((char *)(&s_pin1), dest, 4);       /* 012345678 -> "0123" */
 1967:     for(i=0;i<4;i++) dest[i] = tmpSN[i+2]; 
 1968:     for(i=4;i<8;i++) dest[i] = 0;
 1969:     memcpy((char *)(&s_pin2), dest, 4);   /* 012345678 -> "2345" */
 1970:     for(i=0;i<4;i++) dest[i] = tmpSN[i+5]; 
 1971:     for(i=4;i<8;i++) dest[i] = 0;
 1972:     memcpy((char *)(&s_pin3), dest, 4);   /* 012345678 -> "5678" */
 1973:   } else {  /* Intel 386, 486 */
 1974:     for(i=0;i<4;i++) dest[i] = tmpSN[3-i]; 
 1975:     for(i=4;i<8;i++) dest[i] = 0;
 1976:     memcpy((char *)(&s_pin1), dest, 4);       /* 012345678 -> "0123" */
 1977:     for(i=0;i<4;i++) dest[i] = tmpSN[5-i]; 
 1978:     for(i=4;i<8;i++) dest[i] = 0;
 1979:     memcpy((char *)(&s_pin2), dest, 4);   /* 012345678 -> "2345" */
 1980:     for(i=0;i<4;i++) dest[i] = tmpSN[8-i]; 
 1981:     for(i=4;i<8;i++) dest[i] = 0;
 1982:     memcpy((char *)(&s_pin3), dest, 4);   /* 012345678 -> "5678" */
 1983:   }
 1984: 
 1985: 
 1986:   part1 = s_pin1 + s_pin3+ class_pin2;    if(part1 < 0) part1 = part1 * (-1);
 1987:   part2 = s_pin2 + class_pin1;            if(part2 < 0) part2 = part2 * (-1);
 1988: 
 1989: #ifdef SEED_DBG 
 1990:   printf("S_PIN(%d,%d, %d) C_PIN(%d,%d)\n",s_pin1, s_pin2, s_pin3, class_pin1, class_pin2);
 1991:   printf("SEED(%ld,%ld)\n",part1, part2);
 1992: #endif 
 1993:   {
 1994:     extern void gsrgs(long getset,long *qvalue);
 1995:     static long qrgnin;
 1996:     gsrgs(0L,&qrgnin);
 1997:     if(!qrgnin) 
 1998:       setall(part1, part2);
 1999:   }
 2000:   (*seed1) = part1;
 2001:   (*seed2) = part2;
 2002:   free(class);
 2003: }
 2004: /* ======================================================= PIN number */
 2005: /* pin should only called once for each student. */
 2006: /* if not called at the beginning of problem set, try to call it automatically */
 2007: 
 2008: int                             /* RETURNS: pin number  for login set  */
 2009: capa_PIN(student_number, set, guess) /* ARGUMENTS:             */
 2010: char *student_number;             
 2011: int set;                        /*    Problem set number  */
 2012: int guess;
 2013: {                               /* LOCALS:                */
 2014:    int        current=0,          /*    Current pin for set */
 2015:               i,j,              /*    Array indices       */
 2016:               nope,
 2017:               correct=0,
 2018: 	      all_pins[ONE_K];
 2019:    long       part1, part2;
 2020:    long       orig_gen, new_gen;
 2021:    
 2022:   capa_seed(&part1, &part2, student_number);
 2023:   gscgn(GET_GENERATOR, &orig_gen);
 2024:   new_gen = PIN_G;
 2025:   gscgn(SET_GENERATOR, &new_gen);
 2026:   setsd(part1, part2);
 2027:   /* Generate 4-digit pin (1000-9999) */
 2028:   for (i=1; i<=set; i++) {
 2029:     current=1000+ignlgi()%9000;  
 2030:     do {
 2031:       nope=0;
 2032:       for (j=1; j<i; j++) {
 2033:         if (current == all_pins[j]) {
 2034:           current=1000+ignlgi()%9000;
 2035:           nope++;
 2036:         }
 2037:       }
 2038:     } while (nope);
 2039:     all_pins[i]=current;
 2040:     if (guess && guess==current)   correct=i;
 2041:   }
 2042:   gscgn(SET_GENERATOR, &orig_gen);
 2043:   if (guess) return (correct);
 2044:   return (current);
 2045: }
 2046: 
 2047: /* -------------------------------------------------------------------- */
 2048: /* returns a longer pin, the first four characters are the same as the  */
 2049: /* normal capaId, additionally the result is a capa_malloc string       */
 2050: /* containing the number                                                */
 2051: /* NOTE!-> the result is a string of letters where A=1..I=9,J=0         */
 2052: /* -------------------------------------------------------------------- */
 2053: char*
 2054: capa_id_plus(student_number, set, plus)
 2055: char *student_number;
 2056: int set;
 2057: int plus;
 2058: {
 2059:   long part1,part2,orig_gen,new_gen,pin,rout,k,i;
 2060:   char *result;
 2061:   char       letters[10]={'J','A','B','C','D','E','F','G','H','I'};
 2062:   capa_seed(&part1, &part2, student_number);
 2063:   gscgn(GET_GENERATOR, &orig_gen);
 2064:   new_gen = PIN_G;
 2065:   gscgn(SET_GENERATOR, &new_gen);
 2066:   setsd(part1, part2);
 2067:   pin=capa_PIN(student_number,set,0);
 2068:   result=(char *)capa_malloc(sizeof(char), plus+MAX_PIN_CHAR+1);
 2069:   k=1;
 2070:   for(i=1;i<=MAX_PIN_CHAR;i++) {
 2071:     result[MAX_PIN_CHAR-i] = letters[(pin%(k*10))/k];
 2072:     k*=10;
 2073:   }
 2074:   for(i=MAX_PIN_CHAR;i<MAX_PIN_CHAR+plus;i++) {
 2075:     rout = ignlgi()%10;
 2076:     result[i] = letters[rout];
 2077:   }
 2078:   result[i] = '\0';
 2079:   gscgn(SET_GENERATOR, &orig_gen);
 2080:   return result;
 2081: }
 2082: /* -------------------------------------------------------------------- */
 2083: /* need to set problem_p = NULL after this call */
 2084: /* -------------------------------------------------------------------- */
 2085: void
 2086: free_problems(problem_p) Problem_t *problem_p;
 2087: {
 2088:    Problem_t *p, *next;
 2089:  
 2090:    for (p=problem_p; p!=NULL ; p=next) {
 2091:       next=p->next;
 2092:       if (p->question != NULL) capa_mfree(p->question);
 2093:       if (p->answer != NULL)   capa_mfree(p->answer);
 2094:       if (p->ans_cnt > 1 ) { AnswerInfo_t  *a,*b;
 2095:         for(a = p->ans_list; a != NULL ; a = b) {
 2096:           b = a->ans_next;
 2097:           if (a->ans_str != NULL)       capa_mfree(a->ans_str);
 2098:           if (a->ans_id_list != NULL )  capa_mfree(a->ans_id_list);
 2099:           if (a->ans_pts_list) {
 2100:             free_ptslist(a->ans_pts_list);
 2101:           }
 2102:           if (a->ans_unit) {
 2103:             freelist_unit_e(a->ans_unit->u_list);
 2104:             if (a->ans_unit != NULL) capa_mfree((char *)a->ans_unit);
 2105:           }
 2106:           capa_mfree((char *)a);
 2107:         }
 2108:       }
 2109:       if (p->id_list != NULL )  capa_mfree(p->id_list);
 2110:       if (p->pts_list!= NULL ) {
 2111:         free_ptslist(p->pts_list);
 2112:       }
 2113:       if (p->hint != NULL )     capa_mfree(p->hint);
 2114:       if (p->explain != NULL )  capa_mfree(p->explain);
 2115:       if (p->ans_unit !=NULL ) { 
 2116:         freelist_unit_e(p->ans_unit->u_list);
 2117:         capa_mfree((char *)p->ans_unit);
 2118:       }
 2119:       capa_mfree((char *)p);
 2120:       p=NULL;
 2121:    }
 2122:    problem_p=NULL;
 2123: }
 2124: 
 2125: /******************************************************************************/
 2126: /* PARSE SOURCE FILE AND RETURN BLOCKS OF TEXT                                */
 2127: /******************************************************************************/
 2128: int  
 2129: capa_parse(set,problem,student_number,num_questions,func_ptr)
 2130: int  set;Problem_t **problem;char  *student_number;int  *num_questions;
 2131: void (*func_ptr)();
 2132: {
 2133:   char  filename[QUARTER_K];
 2134:   int   errcode;
 2135:   FILE *fp;
 2136: extern  FILE      *Input_stream[MAX_OPENED_FILE];
 2137: extern  char       Opened_filename[MAX_OPENED_FILE][QUARTER_K];
 2138: extern  int        Lexi_line;
 2139: extern  int        Lexi_qnum;
 2140: extern  Problem_t *FirstProblem_p;
 2141: extern  Problem_t *LastProblem_p;
 2142: extern  Problem_t *LexiProblem_p;
 2143: extern  char      *StartText_p;
 2144: extern  char      *EndText_p;
 2145: extern  char      *ErrorMsg_p;
 2146: extern  int        ErrorMsg_count;
 2147: extern  char       Parse_class[QUARTER_K];
 2148: extern  int        Parse_section;
 2149: extern  int        Parse_set;
 2150: extern  char       Parse_name[MAX_NAME_CHAR+1];
 2151: extern  char       Parse_student_number[MAX_STUDENT_NUMBER+1];
 2152: extern  int        Symb_count;
 2153: extern  int        first_run;
 2154: extern  int        Stop_Parser;
 2155: extern  void       (*Status_Func)();
 2156: #ifdef TTH
 2157: extern void tth_restart();
 2158: extern char* tth_err;
 2159: #endif /*TTH*/
 2160:   long  seed1,     seed2;
 2161:   T_student        a_student;
 2162:   char            *class, *classname, warn_msg[WARN_MSG_LENGTH];
 2163: #if defined(NeXT) 
 2164:    char    cwd[FILE_NAME_LENGTH];
 2165: 
 2166:    class = getwd(cwd);
 2167:    if( class == NULL ) { class = cwd; }
 2168: #else
 2169:    class = getcwd(NULL,255);
 2170:    
 2171: #endif
 2172: 
 2173:   if(class == NULL) { /* printf("capa_parse(): Current working directory unknown!"); */  return (-1); }
 2174:   classname = rindex(class,'/');  classname++; /*** hpux complained */
 2175:   sprintf(Parse_class,"%s", classname);
 2176:   free(class);
 2177:   if( capa_get_student(student_number, &a_student) < 1 )  {
 2178:  /*printf("Error: capa_parse() encountered a student which is not in classl file\n"); */
 2179:     return (-1);
 2180:   }
 2181:   sprintf(filename,"capa.config");
 2182:   if ((fp=fopen(filename,"r"))==NULL) {
 2183:     /* printf("Error: can't open %s\n",filename);*/
 2184:       return (-1); 
 2185:   }
 2186:   u_getunit(fp);
 2187:   fclose(fp);
 2188: #ifdef TTH
 2189:   if(tth_err) { free(tth_err); tth_err=NULL; }
 2190:   tth_restart();
 2191: #endif /*TTH*/
 2192:   strncpy(Parse_name,a_student.s_nm,MAX_NAME_CHAR+1);
 2193:   strncpy(Parse_student_number,student_number,MAX_STUDENT_NUMBER+1);
 2194:   Parse_section = a_student.s_sec;
 2195:   if(ErrorMsg_p) { capa_mfree(ErrorMsg_p); ErrorMsg_p = NULL; }
 2196:   if(EndText_p)  { capa_mfree(EndText_p);  EndText_p  = NULL; }
 2197:   if(StartText_p)  { capa_mfree(StartText_p);  StartText_p  = NULL; }
 2198:   ErrorMsg_p = NULL; first_run = 1; EndText_p = NULL;
 2199:   free_symtree();    Symb_count = ErrorMsg_count = Lexi_line = Lexi_qnum = 0;
 2200:   FirstProblem_p = LastProblem_p = NULL;
 2201:   LexiProblem_p = (Problem_t *)capa_malloc(sizeof(Problem_t),1);
 2202:   problem_default(LexiProblem_p);
 2203:   /*LexiProblem_p->capaidplus=NULL;*/
 2204:   Parse_set = set;
 2205:   Status_Func=func_ptr;
 2206:   sprintf(filename,"set%d.qz",set);
 2207: #ifdef AVOIDYYINPUT
 2208:   yyin=fopen(filename,"r");
 2209: #else
 2210:  if ( (Input_stream[0]=fopen(filename,"r")) == NULL) {
 2211:      /* printf("Error: can't open %s\n",filename);*/
 2212:      sprintf(warn_msg,"capa_parse(): CANNOT OPEN FILE\"%s\", file does not exist or is not readable.\n", filename);
 2213:      capa_msg(MESSAGE_ERROR,warn_msg);
 2214:      return (-1);
 2215:   }
 2216: #endif
 2217:   sprintf(Opened_filename[0],"%s",filename);
 2218:   
 2219:   capa_seed(&seed1, &seed2, student_number);   setall(seed1,seed2);
 2220: 
 2221:   yyrestart(yyin);
 2222:   Stop_Parser=0;
 2223:   if ( !yyparse() )  { errcode = Lexi_qnum; } else { errcode = 0; }
 2224:   /* fclose(Input_stream[0]);*/ /* The Lexer handles closing this */
 2225:   /* print_symb_stat(); */
 2226:   free_symtree();
 2227:   /*
 2228:   capa_mfree((char *)LexiProblem_p);
 2229:   LexiProblem_p = NULL;
 2230:   */
 2231:  (*problem) = FirstProblem_p;
 2232:  (*num_questions) = Lexi_qnum;
 2233:   return (errcode);
 2234: }
 2235: 
 2236: /******************************************************************************/
 2237: /* PARSE SOURCE FILE AND RETURN BLOCKS OF TEXT, unlike capa_parse_student     */
 2238: /******************************************************************************/
 2239: int  
 2240: capa_parse_student(set,problem,a_student,num_questions,func_ptr)
 2241: int  set;Problem_t **problem;T_student  *a_student;int  *num_questions;
 2242: void (*func_ptr)();
 2243: {
 2244:   char  filename[QUARTER_K];
 2245:   int   errcode;
 2246:   FILE *fp;
 2247: extern  FILE      *Input_stream[MAX_OPENED_FILE];
 2248: extern  char       Opened_filename[MAX_OPENED_FILE][QUARTER_K];
 2249: extern  int        Lexi_line;
 2250: extern  int        Lexi_qnum;
 2251: extern  Problem_t *FirstProblem_p;
 2252: extern  Problem_t *LastProblem_p;
 2253: extern  Problem_t *LexiProblem_p;
 2254: extern  char      *StartText_p;
 2255: extern  char      *EndText_p;
 2256: extern  char      *ErrorMsg_p;
 2257: extern  int        ErrorMsg_count;
 2258: extern  char       Parse_class[QUARTER_K];
 2259: extern  int        Parse_section;
 2260: extern  int        Parse_set;
 2261: extern  char       Parse_name[MAX_NAME_CHAR+1];
 2262: extern  char       Parse_student_number[MAX_STUDENT_NUMBER+1];
 2263: extern  int        Symb_count;
 2264: extern  int        first_run;
 2265: extern  void       (*Status_Func)();
 2266:   long  seed1,     seed2;
 2267:   char            *class, *classname, warn_msg[WARN_MSG_LENGTH];
 2268:   
 2269: #if defined(NeXT) 
 2270:    char    cwd[FILE_NAME_LENGTH];
 2271: 
 2272:    class = getwd(cwd);
 2273:    if( class == NULL ) { class = cwd; }
 2274: #else
 2275:    class = getcwd(NULL,255);
 2276:    
 2277: #endif
 2278: 
 2279:   if(class == NULL) { /* printf("capa_parse(): Current working directory unknown!"); */  return (-1); }
 2280:   classname = rindex(class,'/');  classname++; /*** hpux complained */
 2281:   sprintf(Parse_class,"%s", classname);
 2282:   free(class);
 2283: 
 2284:   sprintf(filename,"capa.config");
 2285:   if ((fp=fopen(filename,"r"))==NULL) {
 2286:     /* printf("Error: can't open %s\n",filename);*/
 2287:     sprintf(warn_msg,"capa_parse(): CANNOT OPEN FILE\"%s\", file does not exist or is not readable.\n", filename);
 2288:     capa_msg(MESSAGE_ERROR,warn_msg);
 2289:     return (-1); 
 2290:   }
 2291:   u_getunit(fp);
 2292:   fclose(fp);
 2293:   strncpy(Parse_name,a_student->s_nm,MAX_NAME_CHAR+1);
 2294:   strncpy(Parse_student_number,a_student->s_sn,MAX_STUDENT_NUMBER+1);
 2295:   Parse_section = a_student->s_sec;
 2296:   if(ErrorMsg_p) { capa_mfree(ErrorMsg_p); ErrorMsg_p = NULL; }
 2297:   if(EndText_p)  { capa_mfree(EndText_p);  EndText_p  = NULL; }
 2298:   if(StartText_p)  { capa_mfree(StartText_p);  StartText_p  = NULL; }
 2299:   ErrorMsg_p = NULL; first_run = 1; EndText_p = NULL;
 2300:   free_symtree();    Symb_count = ErrorMsg_count = Lexi_line = Lexi_qnum = 0;
 2301:   FirstProblem_p = LastProblem_p = NULL;
 2302:   LexiProblem_p = (Problem_t *)capa_malloc(sizeof(Problem_t),1);
 2303:   problem_default(LexiProblem_p);
 2304:   /*LexiProblem_p->capaidplus=NULL;*/
 2305:   Parse_set = set;
 2306:   Status_Func=func_ptr;
 2307:   sprintf(filename,"set%d.qz",set);  
 2308: #ifdef AVOIDYYINPUT
 2309:   yyin=fopen(filename,"r");
 2310: #else
 2311:  if ( (Input_stream[0]=fopen(filename,"r")) == NULL) {
 2312:      /* printf("Error: can't open %s\n",filename);*/
 2313:      sprintf(warn_msg,"capa_parse(): CANNOT OPEN FILE\"%s\", file does not exist or is not readable.\n", filename);
 2314:      capa_msg(MESSAGE_ERROR,warn_msg);
 2315:      return (-1);
 2316:   }
 2317: #endif
 2318:   sprintf(Opened_filename[0],"%s",filename);
 2319:   
 2320:   capa_seed(&seed1, &seed2, a_student->s_sn);   setall(seed1,seed2);
 2321: 
 2322:   yyrestart(yyin);
 2323:   if ( !yyparse() )  { errcode = Lexi_qnum; } else { errcode = 0; }
 2324:   /* fclose(Input_stream[0]);*/ /*The Lexer handles closing this*/
 2325:   /* print_symb_stat(); */
 2326:   free_symtree();
 2327:   /*
 2328:   capa_mfree((char *)LexiProblem_p);
 2329:   LexiProblem_p = NULL;
 2330:   */
 2331:  (*problem) = FirstProblem_p;
 2332:  (*num_questions) = Lexi_qnum;
 2333:   return (errcode);
 2334: }
 2335: 
 2336: /* =================================================================== */
 2337: /* A utility method to convert a date string and time string to time_t */
 2338: /*  dateStr:    yyyy/mm/dd */
 2339: /*  timeStr:    hh:mm    */
 2340: time_t
 2341: convertTime(dateStr,timeStr)char *dateStr; char *timeStr;
 2342: {
 2343:   struct   tm  *theTimeData;
 2344:   time_t   result;
 2345:   int      dateTime[5];
 2346:   int      year, month, day, mm, hh;
 2347: 
 2348:   sscanf(dateStr,"%4d/%2d/%2d",&year,&month,&day);
 2349:   dateTime[0] = month;
 2350:   dateTime[1] = day;
 2351:   dateTime[2] = year;
 2352:   sscanf(timeStr,"%2d:%2d",&hh,&mm);
 2353:   dateTime[3] = hh;
 2354:   dateTime[4] = mm;
 2355:   result = time(NULL);
 2356:   theTimeData = localtime(&result);
 2357:   theTimeData->tm_sec = 0;
 2358:   theTimeData->tm_min = dateTime[4];
 2359:   theTimeData->tm_hour = dateTime[3];
 2360:   theTimeData->tm_mday = dateTime[1];
 2361:   theTimeData->tm_mon = dateTime[0]-1;
 2362:   theTimeData->tm_year = dateTime[2]-1900;/* tm_year is years since 1900 */
 2363:   /* these fields are ignored by mktime
 2364:   theTimeData->tm_wday = weekday(year,month,day);
 2365:   theTimeData->tm_yday = julianday(year,month,day); 
 2366:   */
 2367:   result = mktime( theTimeData );
 2368:   return (result);
 2369: }
 2370: 
 2371: int
 2372: weekday( year, month, day) int year; int month; int day;
 2373: {
 2374:   register int dow;
 2375: #if defined(hpux)
 2376:   int  juldays[13];
 2377:        juldays[0]=0;juldays[1]=0;juldays[2]=31;juldays[3]=59;
 2378:        juldays[4]=90;juldays[5]=120;juldays[6]=151;juldays[7]=181;
 2379:        juldays[8]=212;juldays[9]=243;juldays[10]=273;juldays[11]=304;juldays[12]=334;
 2380: #else 
 2381:   int  juldays[13] = {0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
 2382: #endif
 2383: 
 2384:   dow = 7 +  year +  year/4 - year/100 + year/400 + juldays[month] + day;
 2385:   if( (month < 3) && ( leap_year(year) ) )   dow--;
 2386:   dow %= 7;
 2387:   return (dow); 
 2388: } /* weekday */
 2389: 
 2390: int
 2391: julianday( year, month, day)int year;int month;int day;
 2392: {
 2393:   register int doy;
 2394: #if defined(hpux)
 2395:   int  juldays[13];
 2396:        juldays[0]=0;juldays[1]=0;juldays[2]=31;juldays[3]=59;
 2397:        juldays[4]=90;juldays[5]=120;juldays[6]=151;juldays[7]=181;
 2398:        juldays[8]=212;juldays[9]=243;juldays[10]=273;juldays[11]=304;juldays[12]=334;
 2399: #else 
 2400:   int  juldays[13] = {0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
 2401: #endif  
 2402:     
 2403:   doy = juldays[month];
 2404:   if((month > 2) && (leap_year(year)) )  doy++;
 2405:   doy += day;
 2406:   return (doy);
 2407: } /* julianday */
 2408: 
 2409: int
 2410: check_int( an_int ) char *an_int;
 2411: {
 2412:   int  ii, leng;
 2413:   int  result=0;
 2414:   
 2415:   if( (an_int[0] == '-') || (an_int[0]== '+') ) an_int++;
 2416:   leng = strlen(an_int);
 2417:   for(ii=0;ii<leng;ii++) {
 2418:     if( !isdigit(an_int[ii]) )  result = 1;
 2419:   }
 2420:   return (result);
 2421: }
 2422: /* --------------------------- */
 2423: /*  0 OK , 1 NOT a real number */
 2424: int
 2425: check_real( a_real ) char *a_real;
 2426: {
 2427:   int  ii, leng;
 2428:   int  result=0;
 2429:   
 2430:   if( (a_real[0] == '-') || (a_real[0]== '+') ) a_real++;
 2431:   leng = strlen(a_real);
 2432:   for(ii=0;ii<leng;ii++) {
 2433:     if( (!isdigit(a_real[ii])) && (a_real[ii]!='e') && (a_real[ii]!='E') && 
 2434:         (a_real[ii]!='.')      && (a_real[ii]!='+') && (a_real[ii]!='-') )  result = 1;
 2435:   }
 2436:   return (result);
 2437: }
 2438: 
 2439: int
 2440: check_input_usymb(u_symb)char *u_symb;
 2441: {
 2442:   int  result=0;
 2443:   
 2444:   
 2445:   return (result);
 2446: }
 2447: 
 2448: /* <== This routine determine emperically where to split a character */
 2449: /*       string into two portions, one for numerical answer and the  */
 2450: /*       other for units */
 2451: /* ----------------------------------------------------------------- */
 2452: /* inputs : buf    : answer string  */
 2453: /* outputs: num    : the numerical part */
 2454: /*          num_p  : the numerical part in string */
 2455: /*          unit_p : the unit string */
 2456: /* num_p is used to calculate sig figs */
 2457: /* return :  -1  invalid string        */
 2458: /*            0  empty string          */
 2459: /*            1  number without units  */
 2460: /*            2  empty number with units */
 2461: /*            3  number with units       */
 2462: /*                                       */
 2463: int  split_num_unit(buf,num,num_p,unit_p)
 2464: char *buf;double *num; char *num_p; char *unit_p;
 2465: {
 2466:   char    num_str[ANSWER_STRING_LENG], unit_str[ANSWER_STRING_LENG];
 2467:   char    base_str[ANSWER_STRING_LENG], exp_str[ANSWER_STRING_LENG];
 2468:   int     idx=0, errcode=0;
 2469:   int     len, ii, n_idx=0, b_idx=0, e_idx=0, u_idx=0;
 2470:   double  n_part, x_part, b_part, e_part, result;
 2471:   
 2472:   unit_str[0]=0;  /* initialize unit_str[] */
 2473:   len = strlen(buf);
 2474:   while( isspace(buf[idx]) ) { idx++; } /* ignore white spaces */
 2475:   num_str[n_idx]=0; /* initialize number string */
 2476:   if( buf[idx] == '+' || buf[idx] == '-' ) { 
 2477:   /* the first char is either '+' or '-' */
 2478:     num_str[n_idx++]= buf[idx++];
 2479:   }
 2480:   /* skip over alphabet char and those that are not '.' */
 2481:   while( (!isdigit( buf[idx] )) && buf[idx] != '.' )  {  idx++; }
 2482:   while( isdigit( buf[idx] ) || buf[idx] == '.' ) { /* collect decimal numbers */
 2483:     num_str[n_idx++]= buf[idx++];
 2484:   }
 2485:   num_str[n_idx] = 0; /* end the string with a '\0' */
 2486:   sprintf(num_p,"%s",num_str);   /* put the collected numerical string in num_p */
 2487:   /* skip over white spaces */
 2488:   while( isspace(buf[idx]) ) { idx++;  }
 2489:   if( buf[idx] == 'e' || buf[idx] == 'E' ) { /* optional e or E part */
 2490:     /* peek further one char to see if it belongs to one of the following */
 2491:     if( buf[idx+1] == '-' || buf[idx+1] == '+' || isspace(buf[idx+1]) || isdigit(buf[idx+1])) {
 2492:       num_str[n_idx++] = buf[idx++]; /* should be 'e' or 'E' */
 2493:       while( isspace(buf[idx]) ) {  idx++;  } /* skip over spaces */
 2494:       num_str[n_idx++] = buf[idx++]; /* the second char '-', '+' or digit */
 2495:       while( isdigit(buf[idx]) ) {   /* only integer is allowed, not '.' */
 2496:         num_str[n_idx++] = buf[idx++];
 2497:       }
 2498:     }
 2499:     num_str[n_idx] = 0;  /* terminiate the numerical string */
 2500:     while( isspace(buf[idx]) ) { idx++; }
 2501:     sscanf(num_str, "%lg", &result);  /* put the numerical value into a double variable */
 2502:     errcode = errcode | 1;
 2503:   } else if( buf[idx] == 'x' || buf[idx] == 'X' || buf[idx] == '*') { /* optional x or X part */
 2504:     idx++; /* skip x or X */
 2505:     while( isspace(buf[idx]) ) { idx++; }
 2506:     
 2507:     e_part = 1.0;  /* default power */
 2508:     base_str[b_idx] = 0; /* initialize base_str[] */
 2509:     if( buf[idx] == '1' && buf[idx+1] == '0') {
 2510:       base_str[b_idx++] = buf[idx++];
 2511:       base_str[b_idx++] = buf[idx++];
 2512:     } else {
 2513:       return (-1);
 2514:     }
 2515:     base_str[b_idx] = 0; /* terminate base_str[] */
 2516:     while( isspace(buf[idx]) ) { idx++; } /* skip over white spaces */
 2517:     if( buf[idx] == '^' ) {   /* power */
 2518:       idx++;
 2519:       exp_str[e_idx] = 0; /* initialize exp_str[] */
 2520:       while( isspace(buf[idx]) ) { idx++; }
 2521:       if( buf[idx] == '{'|| buf[idx] == '(' ) {  /* matching right bracket */
 2522:         idx++;
 2523:       }
 2524:       while( isspace(buf[idx]) ) { idx++; }
 2525:       if( isdigit(buf[idx]) || buf[idx] == '+' || buf[idx] == '-' )  {
 2526:         exp_str[e_idx] = 0;  /* initialize exp_str[], again */
 2527:         while( isdigit(buf[idx]) /*|| buf[idx] == '.'*/ || buf[idx] == '+' || buf[idx] == '-' ) {
 2528:           exp_str[e_idx++] = buf[idx++];
 2529:         }
 2530:         exp_str[e_idx] = 0;  /* terminate exp_str[] */
 2531:       }
 2532:       while( isspace(buf[idx]) ) { idx++; }
 2533:       if( buf[idx] == '}' || buf[idx] == ')' ) {
 2534:         idx++;
 2535:       }
 2536:       sscanf(exp_str, "%lg", &e_part);
 2537:     }
 2538:     if( strlen(base_str) > 0 ) {
 2539:       sscanf(base_str, "%lg", &b_part);
 2540:       x_part = pow(b_part, e_part);
 2541:     } else {
 2542:       x_part = 0.0;
 2543:     }
 2544:     if( strlen(num_str) > 0 ) {
 2545:       sscanf(num_str, "%lg", &n_part);
 2546:     } else {
 2547:       n_part=0.0;
 2548:     }
 2549:     result = n_part * x_part;
 2550:     errcode = errcode | 1;
 2551:   } else if ( buf[idx] == '^' ) {  /* number ^ */
 2552:     idx++;
 2553:     e_part = 1.0;
 2554:     while( isspace(buf[idx]) ) { idx++; }
 2555:     if( buf[idx] == '{'|| buf[idx] == '(' ) {  /* matching right bracket */
 2556:         idx++;
 2557:     }
 2558:     while( isspace(buf[idx]) ) { idx++; }
 2559:     if( isdigit(buf[idx]) || buf[idx] == '+' || buf[idx] == '-' )  {
 2560:         exp_str[e_idx] = 0;
 2561:         while( isdigit(buf[idx]) /*|| buf[idx] == '.'*/ || buf[idx] == '+' || buf[idx] == '-' ) {
 2562:           exp_str[e_idx++] = buf[idx++];
 2563:         }
 2564:         exp_str[e_idx] = 0;
 2565:     }
 2566:     while( isspace(buf[idx]) ) { idx++; }
 2567:     if( buf[idx] == '}' || buf[idx] == ')' ) {
 2568:         idx++;
 2569:     }
 2570:     sscanf(exp_str, "%lg", &e_part);
 2571:     sscanf(num_str, "%lg", &n_part);
 2572:     if( n_part != 10 ) {
 2573:       return (-1);
 2574:     }
 2575:     result = pow(n_part,e_part);
 2576:     errcode = errcode | 1;
 2577:   } else {  /* number unit */
 2578:     if( strlen(num_str) > 0 ) {
 2579:       sscanf(num_str, "%lg", &result);
 2580:       errcode = errcode | 1;
 2581:     }
 2582:   }
 2583:   
 2584:   if( idx < len ) { /* collect the rest as unit string */
 2585:     for(ii=idx;ii< len; ii++ ) {
 2586:       
 2587:       unit_str[u_idx++] = buf[ii];
 2588:     }
 2589:     unit_str[u_idx]=0; /* terminate unit_str[] */
 2590:     if(u_idx>0) {
 2591:       errcode = errcode | 2;
 2592:     }
 2593:   }
 2594:   if( strlen(num_str) > 0 ) {
 2595:     *num = result;
 2596:   } else {  /* empty number string */
 2597:     *num = 0.0;
 2598:   }
 2599:   sprintf(unit_p,"%s", unit_str);
 2600:   return (errcode);
 2601: }
 2602: 
 2603: char *
 2604: answers_string(mode, p)int mode;Problem_t  *p;
 2605: {
 2606:   char    *fmted_ans, lower[ANSWER_STRING_LENG], upper[ANSWER_STRING_LENG];
 2607:   char    *str_aa, *str_bb, *str_cc, *ans_str, *sub_ans_str, *tmp_str;
 2608:   int     len_aa=0, len_bb=0, len_cc=0,len_dd=0, total_len, num_answer;
 2609:   double  d_answer;
 2610:   
 2611:   if( p->ans_type == ANSWER_IS_SUBJECTIVE ) {
 2612:     char *a="Subjective Answer\n";
 2613:     ans_str = (char *)capa_malloc(strlen(a)+1,1);
 2614:     strcpy(ans_str,a);
 2615:     return (ans_str);
 2616:   }
 2617:   num_answer = calc_ansrange(p->ans_type,p->calc, p->answer, p->ans_fmt, p->tol_type,
 2618:   p->tolerance, lower, upper);
 2619:   
 2620:   if( p->ans_type == ANSWER_IS_FLOAT ) {
 2621:     fmted_ans = capa_malloc(SMALL_LINE_BUFFER,1);
 2622:     d_answer = (double)atof(p->answer);
 2623:     sprintf(fmted_ans,p->ans_fmt,d_answer);
 2624:   } else {
 2625:     fmted_ans = capa_malloc(strlen(p->answer)+2,1);
 2626:     strcpy(fmted_ans,p->answer);
 2627:   }
 2628:   
 2629:   len_aa = strlen(fmted_ans);
 2630:   if (lower != NULL ) len_bb = strlen(lower); else len_bb = 0;
 2631:   if (upper != NULL ) len_cc = strlen(upper); else len_cc = 0;
 2632:   if ( p->unit_str != NULL ) len_dd = strlen(p->unit_str); else len_dd = 0;
 2633: 
 2634:   switch(mode) {
 2635:     case TeX_MODE:
 2636:            if(num_answer==2)  {
 2637:              /* 16 is by adding up characters '  [,] \n\n' and others */
 2638:              str_aa = format_toTeX(fmted_ans);
 2639:              total_len = strlen(str_aa) + len_dd + 16;
 2640:              str_bb = format_toTeX(lower);
 2641:              total_len += strlen(str_bb);
 2642:              str_cc = format_toTeX(upper);
 2643:              total_len += strlen(str_cc);
 2644:              ans_str = (char *)capa_malloc(total_len,1);
 2645:              if(len_dd == 0 ) {  /* no unit_str */
 2646:                sprintf(ans_str," %s [%s,%s]\n\n", str_aa,str_bb,str_cc);
 2647:              } else {
 2648:                sprintf(ans_str," %s [%s,%s] $\\mathrm{%s}$\n\n", str_aa,str_bb,str_cc,p->unit_str);
 2649:              }
 2650:              capa_mfree((char *)str_aa); 
 2651: 	     capa_mfree((char *)str_bb); 
 2652: 	     capa_mfree((char *)str_cc);
 2653:            } else {  /* only one answer */
 2654:              if ( (p->ans_type == ANSWER_IS_INTEGER) || 
 2655:                   (p->ans_type == ANSWER_IS_FLOAT )) {
 2656:                str_bb = format_toTeX(lower);  
 2657:              } else { /* answer could be string, choice */
 2658:                str_bb = (char *)capa_malloc(strlen(lower)+MAX_BUFFER_SIZE,1);
 2659: 	       if (p->verbatim == DO_VERBATIM)
 2660: 		 sprintf(str_bb,"\\begin{verbatim}%s\\end{verbatim}",lower);
 2661: 	       else
 2662: 		 strcpy(str_bb,lower);
 2663:              }
 2664:              total_len = strlen(str_bb) + len_dd + 8;
 2665:                /* 4 is by adding up characters ' \\\n' plus four */
 2666:              ans_str = (char *)capa_malloc(sizeof(char),total_len);
 2667:              if(len_dd == 0 ) {  /* no unit_str */
 2668:                sprintf(ans_str," %s\n", str_bb);
 2669:              } else {
 2670:                sprintf(ans_str," %s $\\mathrm{%s}$\n", str_bb,p->unit_str);
 2671:              }
 2672:              capa_mfree((char *)str_bb);
 2673:            }
 2674:            break;
 2675:      case ASCII_MODE:
 2676:            total_len = len_aa + len_bb + len_cc + len_dd + 8;
 2677:            /* 8 is by adding up characters ' [,] \n\n' plus one */
 2678:            ans_str = (char *)capa_malloc(sizeof(char),total_len);
 2679:            if(num_answer==2)  {
 2680:              if(len_dd == 0 ) {  /* no unit_str */
 2681:                sprintf(ans_str,"%s [%s,%s]\n\n", fmted_ans,lower,upper);
 2682:              } else {
 2683:                sprintf(ans_str,"%s [%s,%s] %s\n\n", fmted_ans,lower,upper,p->unit_str);
 2684:              }
 2685:            } else {
 2686:              if(len_dd == 0 ) {  /* no unit_str */
 2687:                sprintf(ans_str,"%s\n\n", lower);
 2688:              } else {
 2689:                sprintf(ans_str,"%s %s\n\n", lower,p->unit_str);
 2690:              }
 2691:            }
 2692: 	   break;
 2693:       case ANSWER_STRING_MODE:
 2694:            total_len = len_aa + len_bb + len_cc + len_dd + 8;
 2695:            /* 8 is by adding up characters ' [,] \n\n' plus one */
 2696:            ans_str = (char *)capa_malloc(sizeof(char),total_len);
 2697:            if(num_answer==2)  {
 2698:              if(len_dd == 0 ) {  /* no unit_str */
 2699:                sprintf(ans_str,"%s", fmted_ans);
 2700:              } else {
 2701:                sprintf(ans_str,"%s %s", fmted_ans,p->unit_str);
 2702:              }
 2703:            } else {
 2704:              if(len_dd == 0 ) {  /* no unit_str */
 2705:                sprintf(ans_str,"%s", lower);
 2706:              } else {
 2707:                sprintf(ans_str,"%s %s", lower,p->unit_str);
 2708:              }
 2709:            }
 2710: 	   break;   
 2711:      case HTML_MODE: 
 2712:            if(num_answer==2)  { /* this indicates that answer should be either float or int
 2713:            */
 2714:              str_aa = format_toHTML(fmted_ans);
 2715:              total_len = strlen(str_aa) + len_dd + 8;
 2716:              str_bb = format_toHTML(lower);
 2717:              total_len += strlen(str_bb);
 2718:              str_cc = format_toHTML(upper);
 2719:              total_len += strlen(str_cc);
 2720:              /* 8 is by adding up characters ' [,] \n\n' plus one */
 2721:              ans_str = (char *)capa_malloc(sizeof(char),total_len);
 2722:              if(len_dd == 0 ) {  /* no unit_str */
 2723:                sprintf(ans_str,"%s [%s,%s]\n\n", str_aa,str_bb,str_cc);
 2724:              } else {
 2725:                sprintf(ans_str,"%s [%s,%s] %s\n\n", str_aa,str_bb,str_cc,p->unit_str);
 2726:              }
 2727:              capa_mfree((char *)str_aa); capa_mfree((char *)str_bb); capa_mfree((char
 2728:              *)str_cc);
 2729:            } else  {
 2730:              if ( (p->ans_type == ANSWER_IS_INTEGER) || 
 2731:                   (p->ans_type == ANSWER_IS_FLOAT )) {
 2732:                str_bb = format_toHTML(lower);  
 2733:              } else { /* answer could be string, choice */
 2734:                str_bb = (char *)capa_malloc(strlen(lower)+MAX_BUFFER_SIZE,1);
 2735: 	       if (p->ans_type == ANSWER_IS_FORMULA || 1)
 2736: 		 sprintf(str_bb,"<PRE>\n%s\n</PRE>",lower);
 2737: 	       else
 2738: 		 strcpy(str_bb,lower);
 2739:              }
 2740:              total_len = strlen(str_bb) + len_dd + 4;
 2741:                /* 4 is by adding up characters ' \n\n' plus one */
 2742:              ans_str = (char *)capa_malloc(sizeof(char),total_len);
 2743:              if(len_dd == 0 ) {  /* no unit_str */
 2744:                sprintf(ans_str,"%s\n\n", str_bb);
 2745:              } else {
 2746:                sprintf(ans_str,"%s %s\n\n", str_bb,p->unit_str);
 2747:              }
 2748:              capa_mfree((char *)str_bb);
 2749:            }
 2750: 	   break;
 2751:   }
 2752:   capa_mfree(fmted_ans);
 2753: 
 2754:   if( p->ans_cnt > 1 ) {  
 2755:     AnswerInfo_t   *ai;
 2756:      for( ai = p->ans_list; ai; ai = ai->ans_next) {
 2757:        num_answer = calc_ansrange(ai->ans_type,ai->ans_calc, ai->ans_str, ai->ans_fmt, 
 2758:                                   ai->ans_tol_type,ai->ans_tol,lower,upper);
 2759:        if( ai->ans_type == ANSWER_IS_FLOAT ) {
 2760:  	  fmted_ans = capa_malloc(SMALL_LINE_BUFFER,1);
 2761:           d_answer = (double)atof(ai->ans_str);
 2762:           sprintf(fmted_ans,ai->ans_fmt,d_answer);
 2763:        } else {
 2764:           fmted_ans = capa_malloc(strlen(ai->ans_str)+2,1);
 2765:           strcpy(fmted_ans,ai->ans_str);
 2766:        }
 2767:        len_aa = strlen(fmted_ans);
 2768:        len_bb = strlen(lower);
 2769:        len_cc = strlen(upper);
 2770:        len_dd = strlen(ai->ans_unit_str);
 2771:        switch(mode) {
 2772:          case TeX_MODE:
 2773:                if(num_answer==2)  {
 2774:                 /* 16 is by adding up characters '  [,] \n\n' and others */
 2775:              
 2776:                  str_aa = format_toTeX(fmted_ans);
 2777:                  total_len = strlen(str_aa) + len_dd + 16;
 2778:                  str_bb = format_toTeX(lower);
 2779:                  total_len += strlen(str_bb);
 2780:                  str_cc = format_toTeX(upper);
 2781:                  total_len += strlen(str_cc);
 2782:              
 2783:                  sub_ans_str = (char *)capa_malloc(sizeof(char),total_len);
 2784:              
 2785:                  if(len_dd == 0 ) {  /* no unit_str */
 2786:                    sprintf(sub_ans_str," %s [%s,%s]\n\n", str_aa,str_bb,str_cc);
 2787:                  } else {
 2788:                    sprintf(sub_ans_str," %s [%s,%s] $%s$\n\n",
 2789:                    str_aa,str_bb,str_cc,ai->ans_unit_str);
 2790:                  }
 2791:                  capa_mfree((char *)str_aa); capa_mfree((char *)str_bb); capa_mfree((char *)str_cc);
 2792:                } else {  /* only one answer */
 2793:                   if ( (ai->ans_type == ANSWER_IS_INTEGER) || 
 2794:                        (ai->ans_type == ANSWER_IS_FLOAT )) {
 2795:                     str_bb = format_toTeX(lower);  
 2796:                   } else { /* answer could be string, choice */
 2797: 		    str_bb = (char *)capa_malloc(strlen(lower)+MAX_BUFFER_SIZE,1);
 2798: 		    if (ai->ans_type == ANSWER_IS_FORMULA || 1)
 2799: 		      sprintf(str_bb,"\\begin{verbatim}%s\\end{verbatim}",lower);
 2800: 		    else
 2801: 		      strcpy(str_bb,lower);
 2802:                   }
 2803:                   total_len = strlen(str_bb) + len_dd + 8;
 2804:                  /* 4 is by adding up characters ' \\\n' plus four */
 2805:                   sub_ans_str = (char *)capa_malloc(sizeof(char),total_len);
 2806:                   if(len_dd == 0 ) {  /* no unit_str */
 2807:                     sprintf(sub_ans_str," %s\n", str_bb);
 2808:                   } else {
 2809:                     sprintf(sub_ans_str," %s $%s$\n", str_bb,ai->ans_unit_str);
 2810:                   }
 2811:                   capa_mfree((char *)str_bb);
 2812:                 }
 2813:                 break;
 2814:           case ASCII_MODE:
 2815:                 total_len = len_aa + len_bb + len_cc + len_dd + 8;
 2816:                 /* 8 is by adding up characters ' [,] \n\n' plus one */
 2817:                 sub_ans_str = (char *)capa_malloc(sizeof(char),total_len);
 2818:                 if(num_answer==2)  {
 2819:                   if(len_dd == 0 ) {  /* no unit_str */
 2820:                     sprintf(sub_ans_str,"%s [%s,%s]\n\n", fmted_ans,lower,upper);
 2821:                   } else {
 2822:                     sprintf(sub_ans_str,"%s [%s,%s] %s\n\n",
 2823:                     fmted_ans,lower,upper,ai->ans_unit_str);
 2824:                   }
 2825:                 } else {
 2826:                   if(len_dd == 0 ) {  /* no unit_str */
 2827:                     sprintf(sub_ans_str,"%s\n\n", lower);
 2828:                   } else {
 2829:                     sprintf(sub_ans_str,"%s %s\n\n", lower,ai->ans_unit_str);
 2830:                   }
 2831:                 }
 2832: 	        break;
 2833: 	  case ANSWER_STRING_MODE:
 2834:                 total_len = len_aa + len_bb + len_cc + len_dd + 8;
 2835:                 /* 8 is by adding up characters ' [,] \n\n' plus one */
 2836:                 sub_ans_str = (char *)capa_malloc(sizeof(char),total_len);
 2837:                 if(num_answer==2)  {
 2838:                   if(len_dd == 0 ) {  /* no unit_str */
 2839:                     sprintf(sub_ans_str,", %s", fmted_ans);
 2840:                   } else {
 2841:                     sprintf(sub_ans_str,", %s %s", fmted_ans,ai->ans_unit_str);
 2842:                   }
 2843:                 } else {
 2844:                   if(len_dd == 0 ) {  /* no unit_str */
 2845:                     sprintf(sub_ans_str,", %s", lower);
 2846:                   } else {
 2847:                     sprintf(sub_ans_str,", %s %s", lower,ai->ans_unit_str);
 2848:                   }
 2849:                 }
 2850: 	        break;      
 2851:           case HTML_MODE: 
 2852:                 if(num_answer==2)  {
 2853:                   str_aa = format_toHTML(fmted_ans);
 2854:                   total_len = strlen(str_aa) + len_dd + 8;
 2855:                   str_bb = format_toHTML(lower);
 2856:                   total_len += strlen(str_bb);
 2857:                   str_cc = format_toHTML(upper);
 2858:                   total_len += strlen(str_cc);
 2859:                   /* 8 is by adding up characters ' [,] \n\n' plus one */
 2860:                   sub_ans_str = (char *)capa_malloc(sizeof(char),total_len);
 2861:                   if(len_dd == 0 ) {  /* no unit_str */
 2862:                     sprintf(sub_ans_str,"%s [%s,%s]\n\n", str_aa,str_bb,str_cc);
 2863:                   } else {
 2864:                     sprintf(sub_ans_str,"%s [%s,%s] %s\n\n",
 2865:                     str_aa,str_bb,str_cc,ai->ans_unit_str);
 2866:                   }
 2867:                   capa_mfree((char *)str_aa); capa_mfree((char *)str_bb); capa_mfree((char
 2868:                   *)str_cc);
 2869:                 } else  {
 2870:                   if ( (ai->ans_type == ANSWER_IS_INTEGER) || 
 2871:                        (ai->ans_type == ANSWER_IS_FLOAT )) {
 2872:                     str_bb = format_toHTML(lower);  
 2873:                   } else { /* answer could be string, choice */
 2874: 		    str_bb = (char *)capa_malloc(strlen(lower)+MAX_BUFFER_SIZE,1);
 2875: 		    if (ai->ans_type == ANSWER_IS_FORMULA || 1)
 2876: 		      sprintf(str_bb,"<PRE>\n%s\n</PRE>",lower);
 2877: 		    else
 2878: 		      strcpy(str_bb,lower);
 2879:                   }
 2880:                   total_len = strlen(str_bb) + len_dd + 4;
 2881:                    /* 4 is by adding up characters ' \n\n' plus one */
 2882:                   sub_ans_str = (char *)capa_malloc(sizeof(char),total_len);
 2883:                   if(len_dd == 0 ) {  /* no unit_str */
 2884:                     sprintf(sub_ans_str,"%s\n\n", str_bb);
 2885:                   } else {
 2886:                     sprintf(sub_ans_str,"%s %s\n\n", str_bb,ai->ans_unit_str);
 2887:                   }
 2888:                   capa_mfree((char *)str_bb);
 2889:                 }
 2890: 	        break;
 2891:        } /* end of switch */
 2892:        total_len =  strlen(ans_str);
 2893:        total_len += (strlen(sub_ans_str) + 1);
 2894:        tmp_str = (char *)capa_malloc(sizeof(char),total_len);
 2895:        strncpy(tmp_str, ans_str, strlen(ans_str)+1);
 2896:        strcat(tmp_str, sub_ans_str);
 2897:        capa_mfree(ans_str);  capa_mfree(sub_ans_str);
 2898:        capa_mfree(fmted_ans);
 2899:        ans_str = tmp_str;
 2900:      } /* end of for */
 2901:    } /* end of if */
 2902: 
 2903:    return (ans_str);  /* the calling routing needs to deallocate it */
 2904: }
 2905: 
 2906: 
 2907: int check_for_unit_fail(int result)
 2908: {
 2909:   int ret;
 2910:   ret = (result == UNIT_FAIL ||
 2911: 	 result == UNIT_IRRECONCIBLE ||
 2912: 	 result == UNIT_INVALID_STUDENT3 ||
 2913: 	 result == UNIT_INVALID_STUDENT2 ||
 2914: 	 result == UNIT_INVALID_STUDENT1);
 2915:   return ret;
 2916: }
 2917: 
 2918: /* ------------------------------ called from capalogin */
 2919: /*  impose stronger checking on the user input string *answer   */
 2920: /*  */
 2921: 
 2922: /* <== This routine checks user input string *ans against correct answer *s ==> */
 2923: int
 2924: capa_check_ans(ai,ans, error) AnswerInfo_t  *ai; char *ans; char **error;
 2925: {
 2926:   int      t;     /* ans_type  */
 2927:   char    *s;     /* ans_str   */
 2928:   int      c;     /* ans_calc  */
 2929:   int      tt;    /* ans_tol_type */
 2930:   double   to;    /* ans_tol      */
 2931:   int      su;    /* ans_sig_ub   */
 2932:   int      sl;    /* ans_sig_lb   */
 2933:   char    *fm;    /* ans_fmt      */
 2934:   char    *us;    /* ans_unit_str */
 2935:   Unit_t  *u_p;   /* ans_unit     */
 2936:   int      input_len, all_alphabet = 1, idx, ii, type;
 2937:   int      outcome, result = INCORRECT;
 2938:   int      sig, corr_len;
 2939:   int      choice[ANSWER_STRING_LENG];
 2940:   char     num_str[ANSWER_STRING_LENG], unit_str[ANSWER_STRING_LENG];
 2941:   char     fmted[ANSWER_STRING_LENG], correctans[ANSWER_STRING_LENG], answer[ANSWER_STRING_LENG];
 2942:   double   n_part;
 2943:   double   given, target, ratio, fmted_target, target_u, target_l, scale=1.0;
 2944:   double   delta;
 2945:   
 2946:   t = ai->ans_type; s = ai->ans_str; c = ai->ans_calc;
 2947:   tt = ai->ans_tol_type; to = ai->ans_tol;
 2948:   su = ai->ans_sig_ub; sl = ai->ans_sig_lb;
 2949:   fm = ai->ans_fmt;
 2950:   us = ai->ans_unit_str; u_p = ai->ans_unit;
 2951:   
 2952:   switch(t) {
 2953:     case ANSWER_IS_INTEGER:
 2954:     case ANSWER_IS_FLOAT:
 2955:           {
 2956:             input_len = strlen(ans);
 2957:             all_alphabet = 1;
 2958:             for(idx=0;idx<input_len;idx++) {
 2959:               if( isdigit(ans[idx]) ) {
 2960:                 all_alphabet = 0;
 2961:               }
 2962:             }
 2963:             if( !all_alphabet ) {
 2964:               outcome = split_num_unit(ans,&n_part,num_str,unit_str);
 2965: 	    }
 2966: 	    if( outcome > 0 ) {
 2967:               if( outcome > 1 ) { /* with both num and unit parts or only unit part */
 2968:                 if( u_p != NULL ) {
 2969: 		  result = check_correct_unit(unit_str,u_p,&scale);
 2970: 		  if (check_for_unit_fail(result)) {
 2971: 		    *error=strsave(unit_str);
 2972: 		  }
 2973:                 } else { /* what to do when no unit is specified but student entered a unit? */
 2974:                   result = UNIT_NOTNEEDED;
 2975: 		  *error=strsave(unit_str);
 2976:                 }
 2977:               } else {
 2978:                 if( u_p != NULL ) {
 2979:                   result = NO_UNIT;
 2980:                 }
 2981:               }
 2982:               if( (result != NO_UNIT) && (!check_for_unit_fail(result)) && ( result != UNIT_NOTNEEDED) ) {
 2983:                 if( t == ANSWER_IS_FLOAT ) {
 2984:                   target = (double)atof(s); /* real number */
 2985:                 } else {
 2986:                   target = (double)atol(s); /* Integer answer */
 2987:                 }
 2988:                 given = n_part * scale; /* convert the given answer into proper scale for units */
 2989:                 sig = calc_sig( num_str );
 2990:                 if( ((sig < sl   ) || (sig > su   )) && (sig!=0)) {
 2991: 		  result = SIG_FAIL;
 2992: 		  *error=capa_malloc(1,ANSWER_STRING_LENG);
 2993: 		  sprintf(*error,"%d",sig);
 2994:                 } else {
 2995:                   switch( tt ) { /* tolerence type */
 2996:                    case TOL_ABSOLUTE:
 2997:                          fmted_target = target; /* if answer type is integer */
 2998:                          if( type == ANSWER_IS_FLOAT) { /* format the exact answer as specified */
 2999:                            sprintf(fmted, fm, target); 
 3000: 			   fmted_target = (double)atof(fmted);
 3001:                          }
 3002:                          to = fabs(to); /* tol must be positive */
 3003:                          if( c == CALC_FORMATED) {  
 3004:                             target_l = fmted_target - to;  target_u = fmted_target + to;
 3005:                          } else {
 3006:                             target_l = target - to;        target_u = target + to;
 3007:                          }
 3008:                          if( (given >= target_l) && (given <= target_u) ) {
 3009: 			   result = APPROX_ANS;
 3010:                          } else { result = INCORRECT; }
 3011:                          break;
 3012:                    case TOL_PERCENTAGE:
 3013:                          if( target != 0.0 ) {
 3014:                            ratio = (double)(to / 100.0);
 3015:                            fmted_target = target; /* if answer type is integer */
 3016:                            if( type == ANSWER_IS_FLOAT) { /* format the exact answer as specified */
 3017:                              sprintf(fmted, fm, target); 
 3018: 			     fmted_target = (double)atof(fmted);
 3019:                            }
 3020: 			   delta = (double)fabs((double)ratio*target);
 3021:                            if( c == CALC_FORMATED) {
 3022:                              target_l = fmted_target - delta;   target_u = fmted_target + delta;
 3023:                            } else {
 3024:                              target_l = target - delta;         target_u = target + delta;
 3025:                            }
 3026:                          } else { target_l = target_u = target; }
 3027:                          if ( (given >= target_l) && (given <= target_u) ) {
 3028: 			   result = APPROX_ANS;
 3029: 	                 } else {  result = INCORRECT; }
 3030: 		         break;
 3031:                   }
 3032:                 } /* end sig check */
 3033:               } /* end if unit check */
 3034:             } else { /* user entered alphabets, but no number */
 3035:               result = WANTED_NUMERIC;
 3036:             } 
 3037:           }
 3038:           break;
 3039:     case ANSWER_IS_CHOICE:
 3040:           {
 3041:             corr_len = strlen(s); input_len = strlen(ans);
 3042:             if( corr_len == input_len ) {
 3043:               for(idx=0;idx<ANSWER_STRING_LENG;idx++) {
 3044: 		choice[idx] = 0;correctans[idx] = 0;
 3045: 	      }
 3046:               result = EXACT_ANS;
 3047:               for(ii=0;ii<corr_len; ii++) {
 3048:                 idx = toupper(s[ii]) - 'A'; choice[idx] =  1;
 3049:               }
 3050:               for(ii=0;ii<input_len;ii++) {
 3051:                 idx = toupper(ans[ii]) - 'A'; correctans[idx] = 1;
 3052: 	      }
 3053: 	      for(ii=0;ii<ANSWER_STRING_LENG;ii++) {
 3054: 		if(choice[ii] != correctans[ii] )  result = INCORRECT;
 3055:               }
 3056:             } else { result = INCORRECT; }
 3057:             break;
 3058:           }
 3059:     case ANSWER_IS_STRING_CI:
 3060:           if (strcasecmp(ans, s)) { result = INCORRECT; } else { result = EXACT_ANS; }
 3061:           break;
 3062:     case ANSWER_IS_STRING_CS:
 3063:           if (strcmp(ans, s)) { result = INCORRECT; } else { result = EXACT_ANS; }
 3064:           break;
 3065:     case ANSWER_IS_FORMULA:
 3066:           result = check_formula_ans(s,ans,ai->ans_id_list,ai->ans_pts_list,tt,to);
 3067:           break;
 3068:     case ANSWER_IS_EXTERNAL: /* Not yet implemented */
 3069:           
 3070:           
 3071:           
 3072:           break;
 3073:   }
 3074:   return (result);
 3075: }
 3076: 
 3077: /* =============================================================================== */
 3078: /* calling sequence capa_check_answers() --> capa_check_answer()                   */
 3079: /*                                       --> capa_check_ans()                      */
 3080: 
 3081: 
 3082: /* How we check the correct answer against user input string */
 3083: /*
 3084:    If the correct answer is a number (either integer or real number)
 3085:        check if user input string consists of only alphabet characters
 3086:        use split_num_unit() heuristic function to split the input string into two parts
 3087:            numerical part and units part. 
 3088:          if the outcome contains units, check if the units is correct or not
 3089:          
 3090: */
 3091: 
 3092: 
 3093: int
 3094: capa_check_answer(p, answer, error) Problem_t *p; char *answer; char **error;
 3095: {
 3096:   int     type; 
 3097:   char   *correct;
 3098:   char    input[ANSWER_STRING_LENG], unit_str[ANSWER_STRING_LENG];
 3099:   int     tol_type, calc_type;
 3100:   double  tol, n_part; 
 3101:   int     sig_l; 
 3102:   int     sig_u;
 3103:   char   *fmt;
 3104:   int     choice[ANSWER_STRING_LENG], correctans[ANSWER_STRING_LENG];
 3105:   int     ii, idx, corr_len, input_len;
 3106:   int     result = INCORRECT, sig, outcome, all_alphabet;
 3107:   char    fmted[FORMAT_STRING_LENG];
 3108:   double  given, target, ratio, fmted_target, target_u, target_l, scale=1.0;
 3109:   double  delta;
 3110:   
 3111:   type     = p->ans_type;
 3112:   correct  = p->answer;
 3113:   tol_type = p->tol_type;
 3114:   tol      = p->tolerance;
 3115:   sig_l    = p->sig_lbound;
 3116:   sig_u    = p->sig_ubound;
 3117:   fmt      = p->ans_fmt;
 3118:   calc_type = p->calc;
 3119:   unit_str[0]=0;
 3120: 
 3121:   switch(type) {
 3122:     case ANSWER_IS_INTEGER:
 3123:     case ANSWER_IS_FLOAT:
 3124:           {
 3125:             input_len = strlen(answer);
 3126:             all_alphabet = 1;
 3127:             for(idx=0;idx<input_len;idx++) {
 3128:               if( isdigit(answer[idx]) ) {
 3129:                 all_alphabet = 0;
 3130:               }
 3131:             }
 3132:             if( !all_alphabet ) {
 3133:               outcome = split_num_unit(answer,&n_part,input,unit_str);
 3134: 	    }
 3135: 	    if( outcome > 0 ) {
 3136:               if( outcome > 1 ) { /* with both num and unit parts or only unit part */
 3137:                 if( p->ans_unit != NULL ) {
 3138: 		  result = check_correct_unit(unit_str,p->ans_unit,&scale);
 3139: 		  if (check_for_unit_fail(result)) {
 3140: 		    *error=strsave(unit_str);
 3141: 		  }
 3142:                 } else { /* what to do when no unit is specified but student entered a unit? */
 3143:                   result = UNIT_NOTNEEDED;
 3144: 		  *error=strsave(unit_str);
 3145:                 }
 3146:               } else {
 3147:                 if( p->ans_unit != NULL ) {
 3148:                   result = NO_UNIT;
 3149:                 }
 3150:               }
 3151:               if( (result != NO_UNIT) && (!check_for_unit_fail(result)) && ( result != UNIT_NOTNEEDED) ) {
 3152:                 if( type == ANSWER_IS_FLOAT ) {
 3153:                   target = (double)atof(correct); /* real number */
 3154:                 } else {
 3155:                   target = (double)atol(correct); /* Integer answer */
 3156:                 }
 3157:                 given = n_part * scale; /* convert the given answer into proper scale for units */
 3158:                 sig = calc_sig( input );
 3159:                 if( ((sig < sig_l) || (sig > sig_u)) && (sig!=0)) {
 3160:                   result = SIG_FAIL;
 3161: 		  *error=capa_malloc(1,ANSWER_STRING_LENG);
 3162: 		  sprintf(*error,"%d",sig);
 3163:                 } else {
 3164:                   switch( tol_type ) {
 3165:                    case TOL_ABSOLUTE:
 3166:                          fmted_target = target; /* if answer type is integer */
 3167:                          if( type == ANSWER_IS_FLOAT) { /* format the exact answer as specified */
 3168:                            sprintf(fmted, fmt, target); 
 3169: 			   fmted_target = (double)atof(fmted);
 3170:                          }
 3171:                          tol = fabs(tol); /* tol must be positive */
 3172:                          if( calc_type == CALC_FORMATED) {  
 3173:                             target_l = fmted_target - tol;  target_u = fmted_target + tol;
 3174:                          } else {
 3175:                             target_l = target - tol;        target_u = target + tol;
 3176:                          }
 3177:                          if( (given >= target_l) && (given <= target_u) ) {
 3178: 			   result = APPROX_ANS;
 3179:                          } else { result = INCORRECT; }
 3180:                          break;
 3181:                    case TOL_PERCENTAGE:
 3182:                          if( target != 0.0 ) {
 3183:                            ratio = (double)(tol / 100.0);
 3184:                            fmted_target = target; /* if answer type is integer */
 3185:                            if( type == ANSWER_IS_FLOAT) { /* format the exact answer as specified */
 3186:                              sprintf(fmted, fmt, target); 
 3187: 			     fmted_target = (double)atof(fmted);
 3188:                            }
 3189:                            delta = (double)fabs((double)ratio*target);
 3190:                            if( calc_type == CALC_FORMATED) {
 3191:                              target_l = fmted_target - delta;   target_u = fmted_target + delta;
 3192:                            } else {
 3193:                              target_l = target - delta;         target_u = target + delta;
 3194:                            }
 3195:                          } else { target_l = target_u = target; }
 3196:                          if ( (given >= target_l) && (given <= target_u) ) {
 3197: 			   result = APPROX_ANS;
 3198: 	                 } else {  result = INCORRECT; }
 3199: 		         break;
 3200:                   }
 3201:                 } /* end sig check */
 3202:               } /* end if unit check */
 3203:             } else { /* user entered alphabet, but no number */
 3204:               result = WANTED_NUMERIC;
 3205:             } 
 3206:           }
 3207:           break;
 3208:     case ANSWER_IS_CHOICE:
 3209:           {
 3210:             corr_len = strlen(correct); input_len = strlen(answer);
 3211:             if( corr_len == input_len ) {
 3212:               for(idx=0;idx<ANSWER_STRING_LENG;idx++) {
 3213: 		choice[idx] = 0;correctans[idx] = 0;
 3214: 	      }
 3215:               result = EXACT_ANS;
 3216:               for(ii=0;ii<corr_len; ii++) {
 3217:                 idx = toupper(correct[ii]) - 'A'; choice[idx] =  1;
 3218:               }
 3219:               for(ii=0;ii<input_len;ii++) {
 3220:                 idx = toupper(answer[ii]) - 'A'; correctans[idx] = 1;
 3221: 	      }
 3222: 	      for(ii=0;ii<ANSWER_STRING_LENG;ii++) {
 3223: 		if(choice[ii] != correctans[ii] )  result = INCORRECT;
 3224:               }
 3225:             } else { result = INCORRECT; }
 3226:             break;
 3227:           }
 3228:     case ANSWER_IS_STRING_CI:
 3229:           if (strcasecmp(answer, correct)) { result = INCORRECT; } else { result = EXACT_ANS; }
 3230:           break;
 3231:     case ANSWER_IS_STRING_CS:
 3232:           if (strcmp(answer, correct)) { result = INCORRECT; } else { result = EXACT_ANS; }
 3233:           break;
 3234:     case ANSWER_IS_FORMULA:
 3235:           result = check_formula_ans(correct,answer,p->id_list,p->pts_list,tol_type,tol);
 3236:           break;
 3237:     case ANSWER_IS_EXTERNAL: /* not yet implemented */
 3238:          /* we assume the external program is called through popen() */
 3239:          /* and the result will be given back as 0 or 1 to indicate the */
 3240:          /* given answer is correct or not */
 3241:          /* arguments are given to the program as */
 3242:          /* before running the program, check its existance first */
 3243:          /* should we specify a time out period in capa.config file? */
 3244:          /* set up a timer for this purpose */
 3245:          /* FILE *popen (const char *command,const char *type ); */
 3246: 
 3247:           break;
 3248:     
 3249:   }
 3250:   return (result);
 3251: }
 3252: 
 3253: /* -------------------------------------------------------------------------- */
 3254: /*   assumming the formula is *fml_str and the student input is *input_str             */
 3255: /*   according to the type of tolerance, we form the final formula as                  */
 3256: /*      absolute tolerance:  (*fml_str) - (*input_str)                                 */
 3257: /*      relative tolerance:  (*input_str) / (*fml_str)                                 */
 3258: int
 3259: check_formula_ans(fml_str,input_str,var_list,pts_list,tol_type,tol) 
 3260: char *fml_str;char *input_str;char *var_list;PointsList_t *pts_list;int tol_type; double tol;
 3261: {
 3262:   char         *check_fml_str;
 3263:   int           f_len, i_len, outcome, error_code;
 3264:   PointsList_t *pt, *next;
 3265:   double        formula_val, diff;
 3266:   
 3267:   f_len = strlen(fml_str);
 3268:   i_len = strlen(input_str);
 3269:   
 3270:   check_fml_str = (char *)capa_malloc((f_len + i_len + 16), sizeof(char));
 3271:   if( tol_type == TOL_ABSOLUTE ) {
 3272:     sprintf(check_fml_str,"abs((%s) - (%s))",fml_str, input_str);
 3273:   } else {
 3274:     sprintf(check_fml_str,"(%s) / (%s)",input_str,fml_str);
 3275:   }
 3276:   outcome = APPROX_ANS;
 3277:   for(pt= pts_list; pt!=NULL ; pt=next) {
 3278:     next=pt->pts_next;
 3279:     error_code = f_eval_formula(&formula_val,check_fml_str, var_list, pt->pts_str);
 3280:     if( ! error_code ) {
 3281:       if( tol_type == TOL_ABSOLUTE ) {
 3282:         diff = tol - formula_val;
 3283:         if( diff < 0.0 )  {
 3284:           outcome = INCORRECT;
 3285:         }
 3286:       } else {
 3287:         diff = abs(1.0 - formula_val) * 100.0 ;
 3288:         if( diff > tol ) {
 3289:           outcome = INCORRECT;
 3290:         }
 3291:       }
 3292:     } else {
 3293:       outcome = BAD_FORMULA;
 3294:       break;
 3295:     }
 3296:   }
 3297:   capa_mfree((char *)check_fml_str);
 3298:   
 3299:   return (outcome);
 3300:   
 3301: }
 3302: /* inputs: type :: answer type, calc_type :: input string format string tolerance type */
 3303: /* returns: lower upper */
 3304: 
 3305: int
 3306: calc_ansrange(type, calc_type, input, fmt, tol_type, tol, lower, upper)
 3307: int type;int calc_type;char *input;char *fmt;
 3308: int tol_type;double tol;char *lower;char *upper;
 3309: {
 3310:   int     result = 2, leng;
 3311:   char    fmted[ANSWER_STRING_LENG]; 
 3312:   double  target, fmted_target, ratio, target_l, target_u, tmp, delta;
 3313:   
 3314:   
 3315:   if( (type == ANSWER_IS_FORMULA) || 
 3316:       (type == ANSWER_IS_EXTERNAL ) ) {
 3317:       strcpy(lower, input);
 3318:       result = 1;
 3319:       return (result);
 3320:   } else {
 3321:     if( tol == 0.0 ) {  /* answer could be ANSWER_IS_FLOAT     ANSWER_IS_INTEGER, 
 3322:                                          ANSWER_IS_STRING_CI ANSWER_IS_STRING_CS 
 3323:                                          ANSWER_IS_CHOICE 
 3324:                       */
 3325:       result = 1;    /* only one answer */
 3326:       if( type == ANSWER_IS_FLOAT ) {
 3327:         target = (double)atof(input);
 3328:         sprintf(fmted, fmt, target);
 3329:         leng = strlen(fmted)+1;
 3330:       
 3331:         strcpy(lower, fmted);
 3332:       } else { /* could be integer, choice, string ci, string cs */
 3333:         strcpy(lower, input);
 3334:       }
 3335:     } else {  /* we have tolerence */
 3336:     
 3337:       target = (double)atof(input);
 3338:       switch( tol_type ) {
 3339:        case TOL_ABSOLUTE:
 3340:            fmted_target = target; /* if answer type is integer */
 3341:            if( type == ANSWER_IS_FLOAT) {   /* format the exact answer as specified */
 3342:                sprintf(fmted, fmt, target);
 3343:                fmted_target = (double)atof(fmted);
 3344:            }
 3345:            tol = fabs(tol); /* tol must be positive */
 3346:            if( calc_type == CALC_FORMATED) {  
 3347:                target_l = fmted_target - tol;  target_u = fmted_target + tol;
 3348:            } else {
 3349:                target_l = target - tol;        target_u = target + tol;
 3350:            }
 3351:            if(type == ANSWER_IS_FLOAT) {
 3352:              sprintf(fmted, fmt, target_l ); strcpy(lower, fmted);
 3353:              sprintf(fmted, fmt, target_u ); strcpy(upper, fmted);
 3354:            } else {
 3355:              sprintf(fmted, "%.15g", target_l ); strcpy(lower, fmted);
 3356:              sprintf(fmted, "%.15g", target_u ); strcpy(upper, fmted);
 3357:            }
 3358:            break;
 3359:        case TOL_PERCENTAGE:
 3360:            if( target != 0.0 ) {
 3361:               ratio = (double)(tol / 100.0);
 3362:               fmted_target = target; /* if answer type is integer */
 3363:               if( type == ANSWER_IS_FLOAT) { /* format the exact answer as specified */
 3364:                   sprintf(fmted, fmt, target); 
 3365: 		  fmted_target = (double)atof(fmted);
 3366:               }
 3367:               delta = (double)fabs((double)ratio*target);
 3368:               if( calc_type == CALC_FORMATED) {
 3369:                   target_l = fmted_target - delta;   target_u = fmted_target + delta;
 3370:               } else {
 3371:                   target_l = target - delta;         target_u = target + delta;
 3372:               }
 3373:               if( target_l > target_u ) { tmp = target_u; target_u = target_l; target_l = tmp; }
 3374:               if(type == ANSWER_IS_FLOAT) {
 3375:                 sprintf(fmted, fmt, target_l ); strcpy(lower, fmted);
 3376:                 sprintf(fmted, fmt, target_u ); strcpy(upper, fmted);
 3377:               } else {
 3378:                 sprintf(fmted, "%.15g", target_l ); strcpy(lower, fmted);
 3379:                 sprintf(fmted, "%.15g", target_u ); strcpy(upper, fmted);
 3380:               }
 3381:            } else { strcpy(lower, "0.0"); strcpy(upper, "0.0"); result = 1;}
 3382:            break;
 3383:       }
 3384:     }
 3385:   }
 3386:   
 3387:   return (result);
 3388: }
 3389: 
 3390: /* Algorithms : check ALL units first */
 3391: /*              sig figs second  */
 3392: /*              numerical, string comparisons last */
 3393: /* result from capa_check_ans() could be */
 3394: 
 3395: /* New check answers routine checks the /AND and /OR group of answers */
 3396: /*   use array of char pointers  char **a */
 3397: int
 3398: capa_check_answers(p,answers,cnt,error) 
 3399: Problem_t *p; char **answers; int cnt; char **error;
 3400: {
 3401:   AnswerInfo_t  *ai;
 3402:   int            ii, done, result;
 3403:   int           *outcomes;
 3404:   char          **errormsg;
 3405:   
 3406:   errormsg=(char**)capa_malloc(cnt,sizeof(char*));
 3407:   if(p->ans_op == ANS_AND) {  /* ans /and ans */
 3408:     if( (cnt != p->ans_cnt) ) { return (ANS_CNT_NOT_MATCH); }
 3409:     if( cnt == 1 ) { return (capa_check_answer(p, answers[0], error)); } /* there is only one answer */
 3410:     outcomes = (int *)capa_malloc(sizeof(int),cnt);
 3411:     for(ii=0;ii<cnt;ii++) outcomes[ii]=0;  /* initialize the outcomes array */
 3412:     outcomes[0] = capa_check_answer(p, answers[0], &errormsg[0]);
 3413: #ifdef    COMMON_DBUG
 3414:     printf("CAPA_CHECK_ANSWER(%s,%s):: outcome[0]=%d\n", 
 3415:        p->answer,answers[0],outcomes[0]); 
 3416: #endif
 3417:     for(ii=1, ai = p->ans_list; ai; ii++,ai = ai->ans_next  ) {
 3418:        outcomes[ii] =  capa_check_ans(ai,answers[ii],&(errormsg[ii]));
 3419: #ifdef    COMMON_DBUG
 3420:     printf("CAPA_CHECK_ANS(%s,%s): outcome[%d]=%d\n", ai->ans_str,answers[ii],ii,outcomes[ii]); 
 3421: #endif
 3422:     }
 3423:     done = ii = 0;
 3424:     result = 0;
 3425:     while( !done ) { /* check if any of the outcome has failed on units */
 3426:       if( (check_for_unit_fail(outcomes[ii])) ||
 3427:           (outcomes[ii] == NO_UNIT)   ||
 3428:           (outcomes[ii] == UNIT_NOTNEEDED) ) {
 3429:          result = outcomes[ii];
 3430: 	 if (result != NO_UNIT) { *error=strsave(errormsg[ii]); }
 3431:          done = 1;
 3432:       }
 3433:       ii++;
 3434:       if(ii==cnt) done = 1;
 3435:     }
 3436:     if( result == 0 ) { 
 3437:     /* check if any of the outcome has failed to be a numeric 
 3438:        or was a malformed equation */
 3439:       done = ii = 0;
 3440:       while( !done ) {
 3441:         if( outcomes[ii] == WANTED_NUMERIC || outcomes[ii] == BAD_FORMULA ) {
 3442:           result = outcomes[ii];
 3443:           done = 1;
 3444:         }
 3445:         ii++;
 3446:         if(ii==cnt) done = 1;
 3447:       }
 3448:     }
 3449:     if( result == 0 ) {/*check if any of the outcome has failed on sig figs*/
 3450:       done = ii = 0;
 3451:       while( !done ) {
 3452:         if( outcomes[ii] == SIG_FAIL ) {
 3453:           result = outcomes[ii];
 3454: 	  *error = strsave(errormsg[ii]);
 3455:           done = 1;
 3456:         }
 3457:         ii++;
 3458:         if(ii==cnt) done = 1;
 3459:       }
 3460:     }
 3461:     if( result == 0 ) { /* check if any of the outcome is incorrect */
 3462:       done = ii = 0;
 3463:       while( !done ) {
 3464:         if( outcomes[ii] == INCORRECT ) {
 3465:           result = outcomes[ii];
 3466:           done = 1;
 3467:         }
 3468:         ii++;
 3469:         if(ii==cnt) done = 1;
 3470:       }
 3471:     }
 3472:     for (ii=0;ii<cnt;ii++) {
 3473:       if( (check_for_unit_fail(outcomes[ii])) ||
 3474:           (outcomes[ii] == SIG_FAIL)   ||
 3475:           (outcomes[ii] == UNIT_NOTNEEDED) ) {
 3476: 	capa_mfree(errormsg[ii]);
 3477:       }
 3478:     }
 3479:     capa_mfree((char *)errormsg);
 3480:     capa_mfree((char *)outcomes);
 3481:     if( result == 0 ) {
 3482:       result = APPROX_ANS; /* all answers are correct */
 3483:     }
 3484:       
 3485:   } else { /* should be ANS_OR , user answer count should always be 1 */
 3486:      if( cnt != 1 ) { return (ANS_CNT_NOT_MATCH); }
 3487:      if( p->ans_cnt == 1 ) { return (capa_check_answer(p, answers[0], error)); }
 3488:      result = capa_check_answer(p, answers[0], error);
 3489:      ii = 1;  ai = p->ans_list;
 3490:      while( (ii<p->ans_cnt) && ( (result != EXACT_ANS) && (result != APPROX_ANS) ) ) {
 3491:        if((ii!=1)&&((check_for_unit_fail(result))||(result==SIG_FAIL)||(result==UNIT_NOTNEEDED))) {
 3492: 	 capa_mfree((char*)error);
 3493:        }
 3494:        result =  capa_check_ans(ai,answers[0],error);
 3495:        ai = ai->ans_next; ii++;
 3496:      }
 3497:   }
 3498:   return (result);
 3499: }
 3500: 
 3501: 
 3502: 
 3503: 
 3504: 
 3505: 
 3506: 
 3507: 
 3508: /* ========================================================================= */
 3509: int  w_getclassdir(cpath_p, cown_p, class)
 3510: char **cpath_p; char **cown_p; char *class;
 3511: {
 3512:     FILE     *fp;
 3513:     char      filename[SMALL_LINE_BUFFER];
 3514:     char     *cname_p;
 3515:     int       done;
 3516:     char      c;
 3517:     
 3518:     sprintf(filename,"class.conf");
 3519:     if ((fp=fopen(filename,"r"))==NULL) {
 3520:       sprintf(filename,"../class.conf");
 3521:       if ((fp=fopen(filename,"r"))==NULL) {
 3522:         printf("Error: can't open %s\n",filename);
 3523:         exit (1);
 3524:       }
 3525:     }
 3526:     do {
 3527:       c_ignorewhite(fp);
 3528:       c = getc(fp); ungetc(c,fp);
 3529:       if( c != EOF ) {
 3530:         cname_p = c_getword(fp);
 3531:        *cpath_p = c_getword(fp);
 3532:        *cown_p  = c_getword(fp);
 3533:         throwaway_line(fp);
 3534:         if( ! strcasecmp(cname_p, class) ) {
 3535:           done = 1;
 3536:         } else {
 3537:           free(cname_p); free(*cpath_p); free(*cown_p);
 3538:           done = 0;
 3539:         }
 3540:       } else {
 3541:         done = 1;
 3542:       }
 3543:     } while ( ! done );
 3544:     fclose(fp);
 3545:     free(cname_p);
 3546:     return (1);
 3547: }
 3548: 
 3549: /* ----------------------------------------------------------------- */
 3550: /* read_capa_config gets a value out of the capa.config file 
 3551:    in the read resultant string all " are removed expect for \" occurances"
 3552:    in fact all case of \ then another character become just the last
 3553:    character
 3554: 
 3555:  inputs : key_word - a string that is searched for on the lefthand side
 3556:                      of an equals sign
 3557:  outputs : value - a char pointer that the value of the key_word as defined
 3558:                   in the config file is copied into
 3559:   return : -1 - unable to find or acces the capa.config file
 3560:             0 - the requested keyword was not found
 3561:             >0 - length of the string that was returned in value
 3562: */
 3563: /* ----------------------------------------------------------------- */
 3564: int read_capa_config(key_word,value)
 3565: char *key_word;char *value;
 3566: {
 3567:   FILE     *fp;
 3568:   char      filename[SMALL_LINE_BUFFER];
 3569:   char      left[MAX_BUFFER_SIZE],right[MAX_BUFFER_SIZE],c;
 3570:   int       failed=0,done=0,num=0,result=-1,found=0,returnVal=0,i,j;
 3571:   
 3572:   sprintf(filename,"capa.config");
 3573:   if ((fp=fopen(filename,"r"))==NULL) {
 3574:     return (-1);
 3575:   }
 3576:   do {
 3577:     num = fscanf(fp,"%[^ \n\t#] = %[^\n]",left,right);
 3578:     if (num == 2) { result = strcasecmp(left,key_word); }
 3579:     if (result==0) { done=1; }
 3580:     if (num==EOF) { failed=1; }
 3581:     if (num!=2) {
 3582:       found=0;
 3583:       while(1) {
 3584: 	c=fgetc(fp);
 3585: 	if (found) {
 3586: 	  if (c!='\n') {
 3587: 	    ungetc(c,fp);
 3588: 	    break;
 3589: 	  }
 3590: 	}
 3591: 	if (c=='\n') found=1;
 3592: 	if (((char)c)==((char)EOF)) {failed=1;break;}
 3593:       }
 3594:     }
 3595:   } while (!done && !failed); 
 3596: 
 3597:   fclose(fp);
 3598: 
 3599:   if (done) {
 3600:     trim_response_ws(right); /*get rid of leading and trailing spaces*/
 3601:     for(i=0,j=0;i<(strlen(right)+1);i++) {
 3602:       value[j]='\0';
 3603:       if (right[i] == '\\' && (i < (strlen(right))) ) {
 3604: 	i++;value[j]=right[i];j++;
 3605:       } else if (right[i] != '\"' ) {
 3606: 	value[j]=right[i];j++;
 3607:       }
 3608:     }
 3609:     value[j]='\0';
 3610:     returnVal=j;
 3611:   }
 3612:   return returnVal;
 3613: }
 3614: 
 3615: int capa_access(const char *pathname, int mode)
 3616: {
 3617:   pid_t euid,egid;
 3618:   struct stat status;
 3619: 
 3620:   euid=geteuid();
 3621:   egid=getegid();
 3622:   if ( -1 == stat(pathname,&status) ) { return -1; }
 3623:   /*printf("mode:%x F_OK:%x mode&F_OK:%x\n",mode,F_OK,(mode&F_OK));*/
 3624:   /*printf("st_mode:%x S_IFMT:%x st_mode&S_IFMT:%x\n",
 3625:     status.st_mode,S_IFMT,(status.st_mode&S_IFMT));*/
 3626:   if (!(status.st_mode & S_IFMT)) { return -1; }
 3627:   /*printf("euid: %d\t egid: %d\tstatus.st_uid: %d\tstatus.st_gid: %d\n",
 3628:     euid,egid,status.st_uid,status.st_gid);*/
 3629:   /*printf("mode:%x R_OK:%x mode&R_OK:%x\n",mode,R_OK,(mode&R_OK));*/
 3630:   /*printf("mode:%x W_OK:%x mode&W_OK:%x\n",mode,R_OK,(mode&R_OK));*/
 3631:   /*printf("mode:%x X_OK:%x mode&X_OK:%x\n",mode,R_OK,(mode&R_OK));*/
 3632:   if (euid==status.st_uid) {
 3633:     /*printf("here1\n");*/
 3634:     if ((mode & R_OK) && (!(status.st_mode & S_IRUSR))) { return -1; }
 3635:     if ((mode & W_OK) && (!(status.st_mode & S_IWUSR))) { return -1; }
 3636:     if ((mode & X_OK) && (!(status.st_mode & S_IXUSR))) { return -1; }
 3637:   } else {
 3638:     if (egid==status.st_gid) {
 3639:       /*printf("here2\n");*/
 3640:       if ((mode & R_OK) && (!(status.st_mode & S_IRGRP))) { return -1; }
 3641:       if ((mode & W_OK) && (!(status.st_mode & S_IWGRP))) { return -1; }
 3642:       if ((mode & X_OK) && (!(status.st_mode & S_IXGRP))) { return -1; }
 3643:     } else {
 3644:       /*printf("here3\n");*/
 3645:       if ((mode & R_OK) && (!(status.st_mode & S_IROTH))) { return -1; }
 3646:       if ((mode & W_OK) && (!(status.st_mode & S_IWOTH))) { return -1; }
 3647:       if ((mode & X_OK) && (!(status.st_mode & S_IXOTH))) { return -1; }
 3648:     }
 3649:   }
 3650:   return 0;
 3651: }
 3652: 
 3653: /*checks if the string is all whitespace*/
 3654: /*returns 0 if it isn't                 */
 3655: /*returns 1 if it is                    */
 3656: int is_all_ws(char* answer)
 3657: {
 3658:   int length,result=1,i;
 3659:   if (answer!=NULL) {
 3660:     length=strlen(answer);
 3661:     for(i=0;i<length;i++) {
 3662:       if (!isspace(answer[i])) {result=0;break;}
 3663:     }
 3664:   }
 3665:   return result;
 3666: }
 3667: 
 3668: void trim_response_ws(char* answer)
 3669: {
 3670:   char *temp;
 3671:   int i,j=0,length;
 3672:   length=strlen(answer);
 3673:   temp=capa_malloc(length+1,1);
 3674:   strcpy(temp,answer);
 3675:   for(i=0; i<length;i++) if (!(isspace(temp[i]))) break;
 3676:   for(j=length-1;j>=0;j--) if (!(isspace(temp[j]))) break;
 3677:   temp[++j]='\0';
 3678:   strcpy(answer,&temp[i]);
 3679: }
 3680: 
 3681: void throwaway_line(FILE* fp)
 3682: {
 3683:   int c;
 3684:   do {  
 3685:     c = getc(fp);
 3686:   } while ( (c != '\n') && (c != EOF) );
 3687: }
 3688: 
 3689: char* capa_get_seat(char* studentnum,char* seatfile)
 3690: {
 3691:   FILE* fp;
 3692:   T_student student;
 3693:   char *result,*defaultseatfile="seatingchart";
 3694:   char line[TMP_LINE_LENGTH],*lineend;
 3695:   int stuline=0,seatline=0;
 3696: 
 3697:   stuline = capa_get_student(studentnum,&student);
 3698:   if (stuline < 1 ) goto error;
 3699:   if (seatfile == NULL) seatfile=defaultseatfile;
 3700:   if ((fp=fopen(seatfile,"r"))==NULL) goto error;
 3701:   while( (stuline>seatline) && (fgets(line,TMP_LINE_LENGTH-1,fp)) ) seatline++;
 3702:   if (seatline< stuline) goto error;
 3703:   if ((lineend=index(line,' '))!=NULL) lineend='\0';
 3704:   result=capa_malloc(strlen(line)+1,1);
 3705:   strcpy(result,line);
 3706:   return result;
 3707: 
 3708:  error:
 3709:   result= capa_malloc(8,1);
 3710:   sprintf(result,"No Seat");
 3711:   return result;
 3712:   
 3713: }
 3714: 
 3715: void protect_log_string(char* log_string)
 3716: {
 3717:   int i,len=strlen(log_string);
 3718:   for(i=0;i<len;i++) {
 3719:     switch (log_string[i]) {
 3720:     case '\n': case '\t': case '\r':
 3721:       log_string[i]=' ';
 3722:       break;
 3723:     }
 3724:   }
 3725: }
 3726: 
 3727: int capa_get_login_time(char *student_number,int set,time_t *logintime) 
 3728: {
 3729:    FILE    *fp; 
 3730:    int      found,temp=0;
 3731:    char     filename[FILE_NAME_LENGTH],line[SMALL_LINE_BUFFER];
 3732:    
 3733:    *logintime=0;
 3734:    sprintf(filename,"records/access%d.log",set);
 3735: 
 3736:    if ((fp=fopen(filename,"r"))==NULL) return (-1);    
 3737:    found = 0;
 3738:    while( (!found) && ( fgets(line,SMALL_LINE_BUFFER-1,fp) != NULL) ) {
 3739:        if( !strncasecmp(line,student_number,MAX_STUDENT_NUMBER) )  found = 1;
 3740:    }
 3741:    fclose(fp);
 3742:    if (found) sscanf(rindex(line,':'),":%d",&temp);
 3743:    *logintime=(time_t)temp;
 3744:    return found;
 3745: }
 3746: 
 3747: /*----------------------------------------------------------*/
 3748: /* INPUT:  set             the X in logX.db                 */
 3749: /* OUTPUT: creates the accessX.log file if it doesn't exist */
 3750: /*         and inserts stunum:time(NULL) if stunum isn't    */
 3751: /*         in ther already.                                 */
 3752: /*                                                          */
 3753: /* RETURN:  -1    file error                                */
 3754: /*           0    already set                               */
 3755: /*           1    succesfully logged a login time           */
 3756: /*----------------------------------------------------------*/
 3757: int capa_set_login_time(char *student_number,int set)
 3758: {
 3759:    FILE    *fp; 
 3760:    int      errcode=0;
 3761:    int      found=0;
 3762:    char     filename[FILE_NAME_LENGTH],line[SMALL_LINE_BUFFER];
 3763:          
 3764:    sprintf(filename,"records/access%d.log",set);
 3765: 
 3766:    if ((fp=fopen(filename,"r"))!=NULL) {
 3767:      while( (!found) && ( fgets(line,SMALL_LINE_BUFFER-1,fp) != NULL) ) {
 3768:        if( !strncasecmp(line,student_number,MAX_STUDENT_NUMBER) )  found = 1;
 3769:      }
 3770:      fclose(fp);
 3771:    }
 3772: 
 3773:    if (found) return 0;
 3774: 
 3775:    sprintf(line,"%s:%d\n", student_number,(int)time(NULL));
 3776:    if ((fp=fopen(filename,"a+"))==NULL) return (-1);
 3777:    flockstream(fp);
 3778:    fseek(fp,0L,SEEK_END);
 3779:    if ( !fwrite((char *)line, strlen(line), 1, fp) ) {
 3780:      errcode = -1;
 3781:    } else {
 3782:      errcode = 1;
 3783:    }
 3784:    fflush(fp);
 3785:    funlockstream(fp);
 3786:    fclose(fp);
 3787:    return (errcode);
 3788: }
 3789: /* =||>|===================== End of capaCommon.c =====================|<||= */
 3790: 

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>