/* Library of useful functions
   Copyright (C) 1992-2000 Michigan State University

   The CAPA system is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License as
   published by the Free Software Foundation; either version 2 of the
   License, or (at your option) any later version.

   The CAPA system is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   General Public License for more details.

   You should have received a copy of the GNU General Public
   License along with the CAPA system; see the file COPYING.  If not,
   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.

   As a special exception, you have permission to link this program
   with the TtH/TtM library and distribute executables, as long as you
   follow the requirements of the GNU GPL in regard to all of the
   software in the executable aside from TtH/TtM.
*/

/* =||>|===================== capaCommon.c =====================|<||= */
/* created 1994 by Isaac Tsai                                         */
/* TODO: restructure capa_check_ans*() calls into one                 */
/* =||>|===================== capaCommon.c =====================|<||= */
#include <ctype.h>
#if defined(__sun) || defined(linux) || defined(__alpha) || defined(hpux) || defined(AIX) || defined(IRIX) 
#include <unistd.h>  /* lockf() */
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "capaParser.h"
#include "capaToken.h"
#include "capaCommon.h"
#include "ranlib.h"


/*----------------------------------------------------------*/
/*  flock() in SUN is in BSD compatibility lib              */
/*  #include <sys/file.h>                                   */
/*----------------------------------------------------------*/

int     yyparse();
extern  FILE *yyin;
extern  void yyrestart();

extern  FILE *dfp;

/*----------------------------------------------------------*/
/*  RETURN: -1 file error                                   */
/*           0 success                                      */
/*  INPUT:     if section == 0, for all students in class   */
/*             set : 1, 2, 3, ...                           */
/*             prob: 1, 2, 3, ...                           */
/*----------------------------------------------------------*/
int
capa_excuse(int set,int  prob,int  section)
{
   int        nq;        
   T_header   header;
   T_entry    entry;          
   T_student *student_p, *s1_p;
   char       tmp_number[MAX_STUDENT_NUMBER+1];
   long       offset;
   int        num_students;
   
   /* Calculate parameters */
   if (capa_get_header(&header,set))   return (-1);
   sscanf(header.num_questions,"%d", &nq);

   if( ( prob > nq ) || (section < 0 ) || (section > MAX_SECTION_COUNT) )   return (-1);
   num_students= 0;
   if ( (num_students = capa_get_section(&student_p, section))== -1 ) return (-1);
   for ( s1_p = student_p ; s1_p ; s1_p = (s1_p->s_next)) {
     sprintf(tmp_number,"%s",s1_p->s_sn);
     offset = capa_get_entry(&entry,tmp_number,set);
     if(offset < 0 ) offset = -offset;
     switch(entry.answers[prob-1]) { 
       case '0':
       case '-':  entry.answers[prob-1] = 'E'; break;             
       case 'N':  entry.answers[prob-1] = 'E'; break;             
       case 'n':  entry.answers[prob-1] = 'e'; break;             
       case '1': case '2': case '3': case '4': case '5':
       case '6': case '7': case '8': case '9': 
	 if (entry.answers[prob-1] < header.weight[prob-1]) {
	   entry.answers[prob-1] = 'E'; 
	 } 
	 break;
       default :  break;
     } 
     capa_set_entry(&entry,tmp_number,set,offset);
     capa_mfree(entry.answers); entry.answers = NULL;
     capa_mfree(entry.tries);   entry.tries = NULL;
   }
   capa_mfree(header.weight);
   capa_mfree(header.partial_credit);
   free_students(student_p);
   return (0);
}

/*----------------------------------------------------------*/
/*  Lock file shared                                        */
/*  lock the file specified by file stream pointer sp       */
/*----------------------------------------------------------*/
int
flockstream_sh(sp) FILE *sp;
{
  int fd;
  
  fd = fileno(sp);
  
#if defined(__sun) || defined(hpux) || defined(AIX)
  return ( lockf(fd,F_LOCK, 0L) );
#else
  return (flock(fd,LOCK_SH));
#endif
}
/*----------------------------------------------------------*/
int
flockstream(sp) FILE *sp;
{
  int fd;
  
  fd = fileno(sp);
  
#if defined(__sun) || defined(hpux) || defined(AIX)
  return ( lockf(fd,F_LOCK, 0L) );
#else
  return (flock(fd,LOCK_EX));
#endif
}
/*----------------------------------------------------------*/
int
funlockstream(sp) FILE *sp;
{
  int fd;
  
  fd = fileno(sp);
  
#if defined(__sun) || defined(hpux) || defined(AIX)
  return ( lockf(fd,F_ULOCK, 0L) );
#else
  return ( flock(fd,LOCK_UN) );
#endif
}

int
inquery_a_lock(sp,cmd,type,offset,whence,len)
FILE *sp;int cmd;off_t offset;int whence;off_t len;
{
  struct flock lock;
  int    fd;
  lock.l_type   = type;   lock.l_start = offset;
  lock.l_whence = whence; lock.l_len   = len;
  fd=fileno(sp);
  return (fcntl(fd,cmd,&lock));
}
#define  Blocked_Write_Lock(sp) \
          inquery_a_lock(sp,F_SETLK,F_WRLCK,0,0,0)

#define  Blocked_Write_Lock(sp) \
          inquery_a_lock(sp,F_SETLK,F_WRLCK,0,0,0)

#define  Un_Lock(sp) \
          inquery_a_lock(sp,F_SETLK,F_UNLCK,0,0,0)

         
/*----------------------------------------------------------*/
/*  INPUT:   set     problem set number                     */
/*  RETURN:  -1      no records/setX.db file or file error  */
/*            0      success                                */
/*  OUTPUT:  header  pointer to T_header structure          */
/*----------------------------------------------------------*/
int
capa_set_header(header, set) T_header *header;int  set;
{
   FILE    *fp;
   char    *big_buf=NULL, filename[FILE_NAME_LENGTH], cr=0, oneline[TMP_LINE_LENGTH];
   int      nq, len, errcode, fd, item_cnt, exists=1;
   T_header oldheader;
   long     orig_size, new_size, big_buf_size;

   sprintf(filename,"records/set%d.db",set);
   if ((fp=fopen(filename,"r+"))==NULL) {
     exists=0;
     if ((fp=fopen(filename,"w"))==NULL) {   /* create if non-existant */
	/*printf("Error: can't open %s\n",filename);*/  return (-1); 
      }
   }
   sscanf(header->num_questions,"%d",&nq);
   /*   sprintf(oneline,"%d,%s,%s,%s\n",nq,header->open_date,header->due_date,header->answer_date); */
   sprintf(oneline,"%d\n",nq);
   len = strlen(oneline);
   flockstream(fp);   errcode = 0;

   if (exists) {
     /*get file size*/
     fseek(fp,0L,SEEK_END);
     orig_size = ftell(fp);
     big_buf_size = orig_size; /*the amt we need to read is less then the file's size*/
     big_buf = capa_malloc(big_buf_size,1);
     fseek(fp,0L,SEEK_SET);
     
     /*read in old header*/
     fscanf(fp,"%d",&nq);
     while ( cr != '\n' && cr != EOF ) { cr=fgetc(fp); }
     oldheader.weight=capa_malloc(nq+1,1);
     oldheader.partial_credit=capa_malloc(nq+1,1);
     if (!fread(oldheader.weight, sizeof(char), nq, fp))  errcode = -2;
     if (EOF==(cr=fgetc(fp)))  errcode = -3;
     if (!fread(oldheader.partial_credit, sizeof(char),  nq, fp))  errcode = -4;
     if (EOF==(cr=fgetc(fp)))  errcode = -5;
     capa_mfree(oldheader.weight);
     capa_mfree(oldheader.partial_credit);
     
     /*read rest of file*/
     item_cnt=fread(big_buf,1,big_buf_size, fp); 
   }

   /*write new header*/
   fseek(fp,0L,SEEK_SET);
   if (!fwrite(oneline, len, 1, fp)) {errcode = -6;}
   if ( header->weight != NULL ) {
     if (!fwrite((char *)(header->weight), strlen(header->weight), 1, fp)) errcode=-7;
   }
  
   if (EOF==(!fputc('\n', fp))) errcode = -8;
   if (header->partial_credit != NULL) {
     if (!fwrite((char *)(header->partial_credit), strlen(header->partial_credit), 
		 1, fp)) { errcode = -9; }
   } 
   if (EOF==(!fputc('\n', fp))) errcode = -10;
   
   if (exists) {
     /*write out rest of file*/
     if (item_cnt >= 0 ) {
       big_buf[item_cnt]=0;
       if (!fwrite(big_buf,item_cnt,1,fp) ) errcode= -11;
       new_size = ftell(fp);
       if (new_size < orig_size ) {
	 fd = fileno(fp);
	 ftruncate(fd,new_size);
       }
     } 
   }

   fflush(fp);
   funlockstream(fp); fclose(fp);
   if (big_buf!=NULL) capa_mfree(big_buf);
   return (errcode);
}

/*----------------------------------------------------------*/
/*  INPUT:   set     problem set number                     */
/*  RETURN:  -1  file error                                 */
/*            0  success                                    */
/*  OUTPUT:  header  pointer to T_header structure          */
/* Note: allocates space for the partial_credit and weight  */
/*       fields of the passed in header, caller needs to    */
/*       eventually free them with capa_mfree               */
/*----------------------------------------------------------*/
int
capa_get_header(header, set) T_header *header;int  set;
{
   FILE    *fp;
   char     filename[FILE_NAME_LENGTH];
   char     cr;
   int      nq=0, errcode;
   
   sprintf(filename,"records/set%d.db",set); 
   if ((fp=fopen(filename,"r"))==NULL) {
     /*printf("Error capa_get_header(): can't open %s\n",filename);*/
      return (-1); 
   }
   
   flockstream(fp);    errcode = 0;
   fscanf(fp,"%d",&nq);
   cr='\0';
   while ( cr != '\n' && cr != EOF ) { cr=fgetc(fp); }
   header->weight=capa_malloc(nq+1,1);
   header->partial_credit=capa_malloc(nq+1,1);
   if (nq > 0 ) {
     if (!fread(header->weight, sizeof(char), nq, fp))  errcode = -1;
   }
   if (EOF==(cr=fgetc(fp)))  errcode = -1;
   if (nq > 0 ) {
     if (!fread(header->partial_credit, sizeof(char),  nq, fp))  errcode = -1;
   }
   
   funlockstream(fp);    fclose(fp);
   header->weight[nq]='\0'; header->partial_credit[nq]='\0';
   sprintf(header->num_questions,"%d",nq);
   return (errcode);
}
/*----------------------------------------------------------*/
/* ----------- previous version, obsolete as of May 1997
int
capa_set_entry(entry, student_number, set, offset) 
T_entry   *entry;
char      *student_number;
int        set;
long       offset;
{
   FILE    *fp;
   int      errcode=0;
   int      len;
   char     filename[FILE_NAME_LENGTH];
   char     a_line[TMP_LINE_LENGTH];

   sprintf(filename,"records/set%d.db",set);
   if ((fp=fopen(filename,"r+"))==NULL) {
      return (-1);
   }
   sprintf(a_line,"%s %s,%s\n",entry->student_number,entry->answers,entry->tries);
   len = strlen(a_line);
   flockstream(fp);
   fseek(fp,offset,0);
     if (!fwrite(a_line,len,1,fp) ) {
       printf("Error writing data to file\n");
       errcode= (-1);
     }
   fflush(fp);
   funlockstream(fp);
   fclose(fp);
   return (errcode);
}
  ---------- */

int
capa_set_entry(entry, student_number, set, offset) 
T_entry   *entry;
char      *student_number;
int        set;
long       offset;
{
   FILE    *fp;
   int      fd;
   int      errcode=0;
   int      done, len, new_len, item_cnt;
   long     next_r, orig_size, new_size, big_buf_size;
   char     filename[FILE_NAME_LENGTH];
   char     *a_line, tmpline[TMP_LINE_LENGTH], 
     tmp_sn[MAX_STUDENT_NUMBER+1], fmtbuf[SMALL_LINE_BUFFER];
   char    *big_buf;
   
   sprintf(filename,"records/set%d.db",set);
   if ((fp=fopen(filename,"r+"))==NULL) {
      /* printf("Error capa_set_entry(): can't open %s\n",filename); */  return (-1);
   }
   /* entry->answers*3 == entry->tries, + fudge factor*/
   a_line=capa_malloc(strlen(entry->tries)*5+MAX_STUDENT_NUMBER,1);
   sprintf(a_line,"%s %s,%s\n",entry->student_number,entry->answers,entry->tries);
   new_len = strlen(a_line);
   sprintf(fmtbuf, "%%%dc",MAX_STUDENT_NUMBER);
   flockstream(fp);  /* <==== lock the setX.db file */
   fseek(fp,0L,SEEK_END);
   orig_size = ftell(fp);
   big_buf_size = orig_size + new_len;
   big_buf = capa_malloc(big_buf_size,1);
   fseek(fp,0L,SEEK_SET); /* rewind to beginning of file */
   fgets(tmpline,TMP_LINE_LENGTH-1,fp); /* skip weight line, including \n */
   fgets(tmpline,TMP_LINE_LENGTH-1,fp); /* hand grading */
   done = 0;
   while(!done) {
     done = !fgets(tmpline,TMP_LINE_LENGTH-1,fp); len = strlen(tmpline);
     if( !done ) {
       sscanf(tmpline,fmtbuf,tmp_sn);
       if( !strncasecmp(tmp_sn,student_number,MAX_STUDENT_NUMBER) ) { /* Found */
         next_r = ftell(fp); offset = next_r - len; done = 1;
         item_cnt=fread(big_buf,1,big_buf_size, fp); /* read remaining lines into buffer */
         if(item_cnt >= 0 ) { /* no error reading remaining lines */
           big_buf[item_cnt]=0;   /* terminate the buffer, for sure */
           fseek(fp,offset,SEEK_SET);  /* reposition file pointer to the record */
           if (!fwrite(a_line,new_len,1,fp) ) {       /* write out the records */
             /* printf("Error writing data to file\n"); */
             errcode= (-1);
           }
	   if (item_cnt != 0) {
	     if (!fwrite(big_buf,item_cnt,1,fp) ){/*write out the remainings*/
	       /* printf("Error writing data to file\n"); */
	       errcode= (-1);
	     }
	   }
	   new_size = ftell(fp);
	   if(new_size < orig_size ) {
	     fd = fileno(fp);
	     ftruncate(fd,new_size);
	   }
         }
       }
     } else { /* end of file */
       fseek(fp,0L,SEEK_END);
       offset = ftell(fp);  /* last byte, if last byte is cr, back up one */
       fseek(fp,-1L,SEEK_END);
       while(fgetc(fp) == '\n' ) { offset--; fseek(fp,offset,SEEK_SET); }
       offset = offset +2; /* last char and cr */
       done=1;
       fseek(fp,offset,SEEK_SET);
       if (!fwrite(a_line,new_len,1,fp) ) {       /* write out the records */
         /* printf("Error writing data to file\n"); */
         errcode= (-1);
       }
     }
   }
   fflush(fp);
   funlockstream(fp);   /* <======= unlock the file */
   fclose(fp);
   capa_mfree(big_buf);  /* free up the buffer */
   return (errcode);
}

/* for all elements in entry->answers that aren't ? it changes the
   file to have the new values (except if student has a Y and for all
   entries in entry->tries that don't have the value -1 it changes the
   file to have the new value */

int
capa_change_entry(entry, student_number, set) 
T_entry   *entry;
char      *student_number;
int        set;
{
   FILE    *fp;
   int      fd;
   int      errcode=0,offset=0;
   int      done, len, new_len, item_cnt,i,j;
   long     next_r, orig_size, new_size, big_buf_size;
   char     filename[FILE_NAME_LENGTH];
   char     *a_line, tmpline[TMP_LINE_LENGTH], 
     tmp_sn[MAX_STUDENT_NUMBER+1], fmtbuf[SMALL_LINE_BUFFER];
   char    *big_buf;
   
   sprintf(filename,"records/set%d.db",set);
   if ((fp=fopen(filename,"r+"))==NULL) {
      /* printf("Error capa_set_entry(): can't open %s\n",filename); */  return (-1);
   }
   /* entry->answers*3 == entry->tries, + fudge factor*/
   a_line=capa_malloc(strlen(entry->tries)*5+MAX_STUDENT_NUMBER,1);
   sprintf(a_line,"%s %s,%s\n",entry->student_number,entry->answers,entry->tries);
   new_len = strlen(a_line);
   sprintf(fmtbuf, "%%%dc",MAX_STUDENT_NUMBER);
   flockstream(fp);  /* <==== lock the setX.db file */
   fseek(fp,0L,SEEK_END);
   orig_size = ftell(fp);
   big_buf_size = orig_size + new_len;
   big_buf = capa_malloc(big_buf_size,1);
   fseek(fp,0L,SEEK_SET); /* rewind to beginning of file */
   fgets(tmpline,TMP_LINE_LENGTH-1,fp); /* skip weight line, including \n */
   fgets(tmpline,TMP_LINE_LENGTH-1,fp); /* hand grading */
   done = 0;
   while(!done) {
     done = !fgets(tmpline,TMP_LINE_LENGTH-1,fp); len = strlen(tmpline);
     if( !done ) {
       sscanf(tmpline,fmtbuf,tmp_sn);
       if( !strncasecmp(tmp_sn,student_number,MAX_STUDENT_NUMBER) ) { /* Found */
         next_r = ftell(fp); offset = next_r - len; done = 1;
         item_cnt=fread(big_buf,1,big_buf_size,fp); /*read remaining lines into buffer*/
         if(item_cnt >= 0 ) { /* no error reading remaining lines */
           big_buf[item_cnt]=0;   /* terminate the buffer, for sure */
           fseek(fp,offset,SEEK_SET);  /* reposition file pointer to the record */
	   for(i=0;i<entry->e_probs;i++) {
	     /*if entry has been updated, and student doesn't have a Y yet*/
	     if ((entry->answers[i]!='?') && 
		 (tmpline[MAX_STUDENT_NUMBER+1+i]!='Y')) 
	       tmpline[MAX_STUDENT_NUMBER+1+i]=entry->answers[i];
	     j = atoi(&(entry->tries[i*3]));
	     if ( j > -1 ) {
	       tmpline[MAX_STUDENT_NUMBER+1+entry->e_probs+1+i*3]=entry->tries[i*3];
	       tmpline[MAX_STUDENT_NUMBER+1+entry->e_probs+1+i*3+1]=entry->tries[i*3+1];
	     }
	   }
           if (!fwrite(tmpline,new_len,1,fp) ) { errcode= (-1); }
	   if (item_cnt != 0) {
	     if (!fwrite(big_buf,item_cnt,1,fp) ){/*write out the remainings*/
	       /* printf("Error writing data to file\n"); */
	       errcode= (-1);
	     }
	   }
	   new_size = ftell(fp);
	   if(new_size < orig_size ) {
	     fd = fileno(fp);
	     ftruncate(fd,new_size);
	   }
         }
       }
     } else { /* end of file */
       fseek(fp,0L,SEEK_END);
       offset = ftell(fp);  /* last byte, if last byte is cr, back up one */
       fseek(fp,-1L,SEEK_END);
       while(fgetc(fp) == '\n' ) { offset--; fseek(fp,offset,SEEK_SET); }
       offset = offset +2; /* last char and cr */
       done=1;
       fseek(fp,offset,SEEK_SET);
       if (!fwrite(a_line,new_len,1,fp) ) {       /* write out the records */
         /* printf("Error writing data to file\n"); */
         errcode= (-1);
       }
     }
   }
   fflush(fp);
   funlockstream(fp);   /* <======= unlock the file */
   fclose(fp);
   capa_mfree(big_buf);  /* free up the buffer */
   return (errcode);
}

/* -------------------------------------------------- */
/* append a entry record to the very end of the file */
long
capa_append_entry(entry, student_number, set)
T_entry   *entry;
char      *student_number;
int        set;
{
   FILE    *fp;
   int      len;
   char     filename[FILE_NAME_LENGTH];
   char     a_line[TMP_LINE_LENGTH];
   long     offset;
   
   sprintf(filename,"records/set%d.db",set);
   if ((fp=fopen(filename,"r+"))==NULL) {
      /* printf("Error capa_set_entry(): can't open %s\n",filename); */  
      return (-1);
   }
   sprintf(a_line,"%s %s,%s\n",entry->student_number,entry->answers,entry->tries);
   len = strlen(a_line);
   
   flockstream(fp);  /* <==== lock the setX.db file */
   fseek(fp,0L,SEEK_END); 
   offset = ftell(fp);
   fseek(fp,-1L,SEEK_END);
   while(fgetc(fp) == '\n' ) { offset--; fseek(fp,offset,SEEK_SET); }
   offset = offset +2;
   fseek(fp,offset,SEEK_SET);
   if (!fwrite(a_line,len,1,fp) ) {
     /* printf("Error writing data to file\n"); */
     return (-1);
   }
   fflush(fp);
   funlockstream(fp);
   fclose(fp);
   return (offset);
}

/*----------------------------------------------------------*/

long /* RETURNS: byte offset to start of record, 0 if error,
                    -offset if not found & newly created  */
capa_get_entry(entry, student_number, set) 
T_entry   *entry;           
char      *student_number;  
int        set;            
{
   char      filename[FILE_NAME_LENGTH];
   FILE     *fp;
   int       len, nq;          
   char     *ans_p, *tries_p, oneline[TMP_LINE_LENGTH],fmtbuf[SMALL_LINE_BUFFER];
   long      offset = 0, next_r;             
   int       ii, done, found = 0;
   char      a_sn[MAX_STUDENT_NUMBER+1];
   
   sprintf(filename,"records/set%d.db",set); 
   if ((fp=fopen(filename,"r"))==NULL) {
     /*printf("Error capa_get_entry(): can't open %s\n",filename);*/
      return (0); 
   }
   sprintf(entry->student_number,"%s",student_number);
   sprintf(fmtbuf, "%%%dc",MAX_STUDENT_NUMBER);
   flockstream(fp);
   fgets(oneline,TMP_LINE_LENGTH-1,fp); len = strlen(oneline); sscanf(oneline,"%d",&nq);
   fgets(oneline,TMP_LINE_LENGTH-1,fp); /* skip weight line, including \n */
   fgets(oneline,TMP_LINE_LENGTH-1,fp); /* hand grading */
   done = 0;
   while(!done) {
     done = !fgets(oneline,TMP_LINE_LENGTH-1,fp); len = strlen(oneline);
     if( !done ) {
       sscanf(oneline,fmtbuf,a_sn);
       if( !strncasecmp(a_sn,student_number,MAX_STUDENT_NUMBER) ) { /* Found */
         next_r = ftell(fp); offset = next_r - len; done = 1; found = 1;
       }
     } else { /* end of file */
       fseek(fp,0L,SEEK_END);
       offset = ftell(fp);  /* last byte, if last bye is cr, back up one */
       fseek(fp,-1L,SEEK_END);
       while(fgetc(fp) == '\n' ) { offset--; fseek(fp,offset,SEEK_SET); }
       offset = offset +2; /* last char and cr */
       found = 0; done=1;
     }
   }
   funlockstream(fp); fclose(fp);
   if(!found) {
     ans_p = capa_malloc(nq+2,1); 
     tries_p = capa_malloc(3*nq+3,1); /* space and \0 */
     for(ii=0;ii<nq;ii++) { /* Initialize answer string and tries string */
       ans_p[ii] = '-'; tries_p[3*ii] = ' '; tries_p[3*ii + 1] = '0';
       if(ii < nq-1) tries_p[3*ii + 2] = ',';
     }
     entry->answers = ans_p;
     entry->tries   = tries_p;
     entry->e_probs = nq;
     offset = capa_append_entry(entry,student_number,set);
   } else { /* grows records shorter than nq to nq in length*/
     char *comma;
     int length;
     comma=index(oneline,',');
     length=((int)(comma-oneline))-(MAX_STUDENT_NUMBER+1);
     if (length < nq) {
       ans_p = capa_malloc(nq+2,1); 
       tries_p = capa_malloc(3*nq+3,1); /* space and \0 */
     } else {
       ans_p = capa_malloc(length+2,1); 
       tries_p = capa_malloc(3*length+3,1); /* space and \0 */
     }
     sprintf(fmtbuf, "%%%dc",length);
     sscanf(oneline + MAX_STUDENT_NUMBER+1,fmtbuf,ans_p);
     ans_p[length]='\0';
     sprintf(fmtbuf, "%%%dc",(3*length-1));
     sscanf(oneline + MAX_STUDENT_NUMBER+1+length+1,fmtbuf,tries_p);
     tries_p[3*length-1]='\0';
     entry->answers = ans_p;
     entry->tries   = tries_p;
     entry->e_probs = nq;
     if (length < nq) {
       for(ii=length;ii<nq;ii++) {
	 ans_p[ii] = '-'; tries_p[3*ii] = ' '; tries_p[3*ii + 1] = '0';
	 if(ii < nq-1) tries_p[3*ii + 2] = ',';
       }
       ans_p[nq]='\0';tries_p[3*(nq)+2]='\0';tries_p[3*length-1]=',';
     }
   }
   return (offset);
}
/*----------------------------------------------------------*/
/*   Constants defining the column number for each fields   */
/*    in classl file                                        */
/*----------------------------------------------------------*/
#define   SECTION_BEGIN_COLUMN  10
#define   SN_BEGIN_COLUMN       14
#define   NAME_BEGIN_COLUMN     24
#define   EMAIL_BEGIN_COLUMN    60
#define   CAPAID_BEGIN_COLUMN   100
/*----------------------------------------------------------*/
/* Retrieve a linked list of students with specified        */
/*  section in classl file                                  */
/*  If the input section number is 0, students in the whole */
/*  classl will be retrieved.                               */
/*  The resulting linked list is ordered according to the   */
/*  order in classl                                         */
/*  Remember to use free_students() to free up the linked   */
/*  list once done with it.                                 */
/*----------------------------------------------------------*/
/* INPUT:  section    section number                        */
/*                    0 means to load the whole class       */
/* OUTPUT: student_pp                                       */
/*                pointer to pointer of structure T_student */
/* RETURN: number of students in specified section          */
/*----------------------------------------------------------*/
int
capa_get_section(student_pp, section)         
T_student **student_pp;
int         section;      
{
  FILE      *fp;
  char       line[SMALL_LINE_BUFFER],fmtbuf[SMALL_LINE_BUFFER], *cp;
  int        i, line_len;
  int        tmp_sec, student_cnt, tmp_capaid;
  T_student *s1_p, *s2_p, *s3_p;
  
  if ((fp=fopen("classl","r"))==NULL) {
    /*printf("Error: Can't open classlist file\n");*/
    return (-1);
  }
  student_cnt = 0; s1_p = s2_p = s3_p = (T_student *)NULL;
  while (fgets(line,SMALL_LINE_BUFFER-1,fp)) {
    line_len = strlen(line);
    if(line_len > 25 ) { /* contains valid information, instead of only cr */
      sscanf(line+SECTION_BEGIN_COLUMN,"%d", &tmp_sec);
      if (!section || tmp_sec == section)  {
        s3_p = (T_student *)capa_malloc(sizeof(T_student),1);
        s3_p->s_sec = tmp_sec;
        sprintf(fmtbuf, "%%%dc",MAX_STUDENT_NUMBER); 
        sscanf(line+SN_BEGIN_COLUMN,fmtbuf,s3_p->s_sn);
        cp = (char *)&line[NAME_BEGIN_COLUMN];
        for(i=0;i<MAX_NAME_CHAR;i++)  s3_p->s_nm[i]=' ';
        i=0;
        while( (i < MAX_NAME_CHAR) && 
               ( isalnum(cp[i]) || cp[i] == ',' || cp[i] == '.' || cp[i] == '\'' ||
                 cp[i] == ' '   || cp[i] == '\t'|| cp[i] == '-' ) ) {
          s3_p->s_nm[i]=cp[i]; i++;
        }
        s3_p->s_nm[MAX_NAME_CHAR]='\0';
        s3_p->s_email[0]=0;
        if( line_len > EMAIL_BEGIN_COLUMN ) {  /* contains email */
          cp = (char *)&line[EMAIL_BEGIN_COLUMN];
          for(i=0;i<MAX_EMAIL_CHAR;i++)  s3_p->s_email[i]=' ';
          i=0;
          while( (i < MAX_EMAIL_CHAR) && 
                 ( isalnum(cp[i]) || cp[i] == '@' || cp[i] == '.' ) ) {
             s3_p->s_email[i]=cp[i]; i++;
          }
          s3_p->s_email[i]='\0'; /* terminate the email string with the string length */
          if( line_len > CAPAID_BEGIN_COLUMN ) {  /* contains  capa id */
            sscanf(line+CAPAID_BEGIN_COLUMN,"%d", &tmp_capaid);
            if(tmp_capaid > 0 ) {
              s3_p->s_capaid = tmp_capaid;
            }
          }
        }
        if( student_cnt == 0 ) {
          s1_p = s3_p;
          s3_p->s_prev = s3_p;  s3_p->s_next = (T_student *)NULL;
          s2_p = s3_p;
        } else {
          s3_p->s_prev = s2_p;  s3_p->s_next = (T_student *)NULL;
          s2_p->s_next = s3_p;
          s2_p = s3_p;
        }
        student_cnt++;
      }
    }
  }
  fclose(fp);
 *student_pp = s1_p;
  return (student_cnt);
}
/*----------------------------------------------------------*/
/*----------------------------------------------------------*/
/* Retrieve a linked list of students with specified        */
/*  section in classl file                                  */
/*  If the input section number is 0, students in the whole */
/*  classl will be retrieved.                               */
/*  The resulting linked list is ordered according to       */
/*  student name                                            */
/*  Remember to use free_students() to free up the linked   */
/*  list once done with it.                                 */
/*----------------------------------------------------------*/
/* INPUT:  section    section number                        */
/*                    0 means to load the whole class       */
/* OUTPUT: student_pp                                       */
/*                pointer to pointer of structure T_student */
/* RETURN: number of students in specified section          */
/*----------------------------------------------------------*/

int
capa_sorted_section(student_pp, section)         
T_student **student_pp;
int         section;      
{
  int         student_cnt;
  T_student  *student_p, *c_sp;
  
   student_cnt = capa_get_section(&student_p,section);
   if( student_cnt > 1 ) {
     for(c_sp=student_p;c_sp;c_sp=c_sp->s_next){
       strcpy(c_sp->s_key,c_sp->s_nm);
     }
     msort_main(&student_p);
   } 
   *student_pp = student_p;
   return (student_cnt);
}

/*----------------------------------------------------------*/
/* INPUT:  address of a pointer to T_student structure      */
/* OUTPUT: address of a pointer to T_student structure      */
/*                                                          */
/* RETURN: none                                             */
/*----------------------------------------------------------*/
void
msort_main(head_pp) T_student  **head_pp;
{
  T_student   *right_p, *out_sp;
  
   if( (*head_pp != NULL) && ((*head_pp)->s_next != NULL) ) {
     msort_split( *head_pp, &right_p);
     msort_main(head_pp);  msort_main(&right_p);
     msort_merge(*head_pp, right_p, &out_sp);
     *head_pp = out_sp;
   }
}
/*----------------------------------------------------------*/
/* INPUT:  a_sp    pointer to T_student structur            */
/* OUTPUT: a_sp                                             */
/*         b_pp address of a pointer to T_student structure */
/*                                                          */
/* RETURN: none                                             */
/*----------------------------------------------------------*/
void
msort_split(a_sp, b_pp) T_student *a_sp; T_student **b_pp;
{
   T_student  *c_sp;
   
   *b_pp = a_sp;
   c_sp = a_sp->s_next;
   c_sp = c_sp->s_next;
   while( c_sp != NULL ) {
     c_sp = c_sp->s_next;
     *b_pp = (*b_pp)->s_next;
     if( c_sp != NULL ) c_sp = c_sp->s_next;
   }
   c_sp = (*b_pp)->s_next;
   (*b_pp)->s_next = NULL;
   *b_pp = c_sp;
}
/*----------------------------------------------------------*/
/* INPUT:  a_sp    pointer to T_student structur            */
/*         b_sp    pointer to T_student structur            */
/* OUTPUT: c_pp address of a pointer to T_student structure */
/*                                                          */
/* RETURN: none                                             */
/*----------------------------------------------------------*/
void
msort_merge(a_sp, b_sp, c_pp) T_student *a_sp, *b_sp, **c_pp;
{
  T_student  *d_sp;
  
  if( a_sp == NULL || b_sp == NULL ) {
  
  } else {
    if( strcmp(a_sp->s_key, b_sp->s_key) <= 0 ) {
       *c_pp = a_sp;
       a_sp = a_sp->s_next;
    } else {
       *c_pp = b_sp;
       b_sp = b_sp->s_next;
    }
    d_sp = *c_pp;
    while( (a_sp != NULL) && (b_sp != NULL) ) {
      if( strcmp(a_sp->s_key, b_sp->s_key) <= 0) {
        d_sp->s_next = a_sp;
        d_sp = a_sp;
        a_sp = a_sp->s_next;
      } else {
        d_sp->s_next = b_sp;
        d_sp = b_sp;
        b_sp = b_sp->s_next;
      }
    }
    if( a_sp == NULL ) {
      d_sp->s_next = b_sp;
    } else {
      d_sp->s_next = a_sp;
    }
  }
}

/*----------------------------------------------------------*/
/* INPUT:  head_p pointer to T_student structure            */
/* OUTPUT: head_p pointed to sorted linked list             */
/*                                                          */
/* RETURN: none                                             */
/*----------------------------------------------------------*/
void
insert_sort(head_p) T_student  *head_p;
{
  T_student  *tail_p, *i_p, *p_p, *r_p;
  
  if( head_p != NULL ) {
    tail_p = head_p;
    while( tail_p->s_next != NULL ) {
      i_p = tail_p->s_next;
      if( strcmp( i_p->s_key, head_p->s_key) < 0 ) {
        tail_p->s_next = i_p->s_next;
        i_p->s_next = head_p;
        head_p = i_p;
      } else {
        r_p = head_p;
        p_p = r_p->s_next;
        while( strcmp(i_p->s_key, p_p->s_key) > 0 ) {
          r_p = p_p;
          p_p = r_p->s_next;
        }
        if( i_p == p_p ) {
          tail_p = i_p;
        } else {
          tail_p->s_next = i_p->s_next;
          i_p->s_next = p_p;
          r_p->s_next = i_p;
        }
      }
    }
  }
}
/*----------------------------------------------------------*/
/*  The purpose of this routine is to free up all spaces    */
/*  in the linked list                                      */
/*----------------------------------------------------------*/
/* INPUT:  student_p pointer to T_student structure         */
/* OUTPUT: none                                             */
/*                                                          */
/* RETURN: none                                             */
/*----------------------------------------------------------*/

void
free_students(student_p) T_student *student_p;
{
  T_student *s_p,*next_p;
  
   for (s_p=student_p; s_p; s_p=next_p) {
      next_p = (s_p->s_next);
      capa_mfree((char *)s_p);
   }
}

/*----------------------------------------------------------*/
/*  The purpose of this routine is to free up all spaces    */
/*  in the linked list                                      */
/*----------------------------------------------------------*/
/* INPUT:  student_p pointer to T_student structur          */
/* OUTPUT: none                                             */
/*                                                          */
/* RETURN:  -2    file error                                */
/*          -1    never login                               */
/*          >= 0  score for that set                        */
/*----------------------------------------------------------*/
int
capa_get_score(student_number,set,valid_scores,answers_pp)
char  *student_number;int  set;int *valid_scores;char  **answers_pp;
{
  T_entry   a_record;
  T_header  a_header;
  long      offset;
  int       set_scores, set_valids=0, probs_cnt;
  int       ii=0, never_login=1;
  
  if( (offset = capa_get_entry(&a_record,student_number,set) ) == 0 ) { 
     return (-2);
  }
  probs_cnt = a_record.e_probs;
  if( capa_get_header(&a_header,set) < 0 ) { return (-2); }
  if( offset < 0 ) {
    for(set_valids=0,ii=0;ii<probs_cnt;ii++) {
      set_valids    += (a_header.weight[ii] - '0');
    }
    set_scores = -1;
    *answers_pp = a_record.answers;
    capa_mfree(a_record.tries); a_record.tries = NULL;
  } else {
    for(set_scores=0,ii=0, never_login=1;ii<probs_cnt;ii++) {
      switch(a_record.answers[ii]) { 
        case 'Y': case 'y':  never_login = 0;
                  set_scores    += (a_header.weight[ii] - '0'); 
                  set_valids    += (a_header.weight[ii] - '0');  break; 
        case '-': case 'N': case 'n': 
                  set_valids    += (a_header.weight[ii] - '0');  break; 
        case 'E': case 'e':                          break;
        default : if( a_record.answers[ii]  >= '0' && 
                      a_record.answers[ii] <= '9' ) {
                    set_scores    += (a_record.answers[ii] - '0');
                    set_valids    += (a_header.weight[ii] - '0');
                    never_login = 0;
                  } break;
      }
    }
    *answers_pp = a_record.answers;
    capa_mfree(a_record.tries); a_record.tries = NULL;
  }
  capa_mfree(a_header.weight);
  capa_mfree(a_header.partial_credit);
  *valid_scores = set_valids;
  if(never_login) set_scores = -1;
  return (set_scores);
}
/*----------------------------------------------------------*/
/* Reads the classl file and returns the number of students */
/*  for each section in cnt_arry[]                          */
/*  In cnt_arry[0] is the maximum section number that       */
/*  has at least one student                                */
/*----------------------------------------------------------*/
/* INPUT:  cnt_arry[]  integer array                        */
/* OUTPUT: cnt_arry[]                                       */
/*                                                          */
/*                                                          */
/* RETURN:  -1    file error                                */
/*----------------------------------------------------------*/
int
capa_get_section_count(cnt_arry) int cnt_arry[]; 
{
  FILE   *fp;
  char    filename[FILE_NAME_LENGTH];
  char    line[TMP_LINE_LENGTH];
  int     sec_num, cnt, i, max;

  sprintf(filename,"classl");
  if ((fp=fopen(filename,"r"))==NULL) {
    /*printf("Error: can't open %s\n",filename);*/
      return (-1);
  }
  for(i=0;i<MAX_SECTION_COUNT;i++) cnt_arry[i]=0;
  max = 0;
  while ( fgets(line,TMP_LINE_LENGTH-1,fp) && (strlen(line) != 0) ) {
   sec_num=0;
   sscanf(line+SECTION_BEGIN_COLUMN,"%d", &sec_num);
   if( sec_num != 0 ) {
     max = MAX(sec_num,max);
     cnt_arry[sec_num]++;
   }
  }
  fclose(fp);
  cnt = 0;
  for(i=1; i <= max; i++) {
    if(cnt_arry[i]) cnt++;
  }
  cnt_arry[0] = max;
  return (cnt);
}
/*----------------------------------------------------------*/
/*  lookup student information from classl file by          */
/*  student number                                          */
/*----------------------------------------------------------*/
/* INPUT:  student_number   char array of student number    */
/* OUTPUT: student_p     pointer to a T_student structure   */
/*                       that contains the student name,    */
/*                       student number, section number     */
/*                       of the student inquired            */
/*                                                          */
/* RETURN:  -1    file error                                */
/*           0    no such student                           */
/*           >0   success (line number in classl of student)*/
/*----------------------------------------------------------*/
int
capa_get_student(student_number, student_p) 
char      *student_number; 
T_student *student_p;
{
 FILE  *fp;
 char   line[SMALL_LINE_BUFFER],fmtbuf[SMALL_LINE_BUFFER],
        sNumber[MAX_STUDENT_NUMBER+1],
        aNumber[MAX_STUDENT_NUMBER+1];
 int    i,found,line_len,tmp_capaid,linenum=0;
 char  *cp;
 
  strncpy(sNumber, student_number,MAX_STUDENT_NUMBER+1);
  if ((fp=fopen("classl","r"))==NULL) {
    /*printf("Error: Can't open classlist file\n");*/
    return (-1);
  }
  found = 0;
  while (!found && fgets(line,SMALL_LINE_BUFFER-1,fp)) {
    linenum++;
    line_len = strlen(line);
    sprintf(fmtbuf, "%%%dc",MAX_STUDENT_NUMBER);
    sscanf(line+SN_BEGIN_COLUMN,fmtbuf,aNumber);
    if(!strncasecmp(sNumber,aNumber, MAX_STUDENT_NUMBER)) {
       found = 1;
       i=0;
       strncpy(student_p->s_sn, student_number,MAX_STUDENT_NUMBER+1);
       cp = (char *)&line[NAME_BEGIN_COLUMN];
       while( (i< MAX_NAME_CHAR) &&  
              (isalnum(cp[i]) || cp[i] == ',' || cp[i] == '.' || cp[i] == '\'' ||
              cp[i] == ' '    || cp[i] == '\t'|| cp[i] == '-' || cp[i] == '_'  || cp[i] == '~' ) ) {
         student_p->s_nm[i]=cp[i]; i++;
       }
       student_p->s_nm[i]='\0';
       sscanf(line+SECTION_BEGIN_COLUMN,"%d", &(student_p->s_sec));
       student_p->s_email[0]=0;
       if( line_len > EMAIL_BEGIN_COLUMN ) {  /* contains email */
          cp = (char *)&line[EMAIL_BEGIN_COLUMN];
          for(i=0;i<MAX_EMAIL_CHAR;i++)  student_p->s_email[i]=' ';
          i=0;
          while( (i < MAX_EMAIL_CHAR) && 
                 ( isalnum(cp[i]) || cp[i] == '@' || cp[i] == '.'  || cp[i] == '!' || cp[i] == '=' ||
                    cp[i] == '_'  || cp[i] == '-' || cp[i] == '+'  || cp[i] == '^' || cp[i] == '|' ) ) {
             student_p->s_email[i]=cp[i]; i++;
          }
          student_p->s_email[i]='\0'; /* terminate the email string with the string length */
          if( line_len > CAPAID_BEGIN_COLUMN ) {  /* contains  capa id */
            sscanf(line+CAPAID_BEGIN_COLUMN,"%d", &tmp_capaid);
            if(tmp_capaid > 0 ) {
              student_p->s_capaid = tmp_capaid;
            }
          }
       }
    }
  }
  fclose(fp);
  if (found > 0) found=linenum;
  return (found);
}
/*----------------------------------------------------------*/
/*  lookup student information from classl file by          */
/*  student name                                            */
/*----------------------------------------------------------*/
/* INPUT:  student_name   char array of student name        */
/* OUTPUT: student_p     pointer to a T_student structure   */
/*                       that contains the student name,    */
/*                       student number, section number     */
/*                       of the student inquired            */
/*                                                          */
/* RETURN:  -1    file error                                */
/*           0    no such student                           */
/*           1    success                                   */
/*----------------------------------------------------------*/
int       
capa_student_byname(student_name, student_p) 
char      *student_name; 
T_student *student_p;
{
 FILE  *fp;
 char   line[SMALL_LINE_BUFFER],fmtbuf[SMALL_LINE_BUFFER],
        sName[MAX_NAME_CHAR+1],
        aName[MAX_NAME_CHAR+1];
 int    i,found, len, line_len,tmp_capaid;
 char  *cp;
 
  len = strlen(student_name);
  strncpy(sName, student_name,MAX_NAME_CHAR+1);
  if ((fp=fopen("classl","r"))==NULL) {
    /*printf("Error: Can't open classlist file\n");*/
    return (-1);
  }
  found = 0;
  while (fgets(line,SMALL_LINE_BUFFER-1,fp)) {
    i=0;
    cp = (char *)&line[NAME_BEGIN_COLUMN];
    while( (i < MAX_NAME_CHAR) && 
           (isalnum(cp[i]) || cp[i] == ','  || cp[i] == '.' || cp[i] == '\'' ||
            cp[i] == ' '   || cp[i] == '\t' || cp[i] == '-' || cp[i] == '_'  || cp[i] == '~') ) {
        aName[i]=cp[i]; i++;
    }
    aName[i]='\0';
    if(!strncasecmp(sName,aName,len)) {
       found = 1;
       strcpy(student_p->s_nm,aName); 
       sscanf(line+SECTION_BEGIN_COLUMN,"%d", &(student_p->s_sec));
       sprintf(fmtbuf, "%%%dc",MAX_STUDENT_NUMBER);
       sscanf(line+SN_BEGIN_COLUMN,fmtbuf,student_p->s_sn);
       student_p->s_email[0]=0;
       line_len=strlen(line);
       if( line_len > EMAIL_BEGIN_COLUMN ) {  /* contains email */
          cp = (char *)&line[EMAIL_BEGIN_COLUMN];
          for(i=0;i<MAX_EMAIL_CHAR;i++)  student_p->s_email[i]=' ';
          i=0;
          while( (i < MAX_EMAIL_CHAR) && 
                 ( isalnum(cp[i]) || cp[i] == '@' || cp[i] == '.'  || cp[i] == '!' || cp[i] == '=' ||
                    cp[i] == '_'  || cp[i] == '-' || cp[i] == '+'  || cp[i] == '^' || cp[i] == '|' ) ) {  
             student_p->s_email[i]=cp[i]; i++;
          }
          student_p->s_email[i]='\0'; /* terminate the email string with the string length */
          if( line_len > CAPAID_BEGIN_COLUMN ) {  /* contains  capa id */
            sscanf(line+CAPAID_BEGIN_COLUMN,"%d", &tmp_capaid);
            if(tmp_capaid > 0 ) {
              student_p->s_capaid = tmp_capaid;
            }
          }
       }
    }
  }
  fclose(fp);
  return (found);
}
/*----------------------------------------------------------*/
/*  Randomly selected a student from classl file specified  */
/*  by section                                              */
/*----------------------------------------------------------*/
/* INPUT:  section   section number                         */
/* OUTPUT: student_p     pointer to a T_student structure   */
/*                       that contains the student name,    */
/*                       student number, section number     */
/*                       of the student inquired            */
/*                                                          */
/* RETURN:  -1    file error                                */
/*           0    no such student                           */
/*           1    success                                   */
/*----------------------------------------------------------*/
int
capa_pick_student(section, student_p) 
int        section; 
T_student *student_p;
{
  T_student  *s1_p, *s2_p;
  int         student_cnt, idx, pick=-1;
  
  student_cnt = capa_get_section(&s1_p,section);
  if(student_cnt > 0 ) {
    srand(time(NULL));
    pick = rand() % student_cnt;
    for(s2_p = s1_p, idx=0; s2_p && idx < pick; idx++) {
      s2_p = s2_p->s_next;
    }
    strcpy(student_p->s_sn,s2_p->s_sn);
    strcpy(student_p->s_nm,s2_p->s_nm);
    strcpy(student_p->s_email,s2_p->s_email);
    student_p->s_capaid=s2_p->s_capaid;
    student_p->s_sec = s2_p->s_sec;
    free_students(s1_p);
  }
  return (pick);
}
/* -------------------------------------------------- */
/*  add a student to the class file                   */
/*  check duplicated student id                       */
/*        student name?                               */
/*  Returns: 0   success                              */
/*           1   there is an duplicate entry          */
/* -------------------------------------------------- */
int
capa_add_student(student_p)
T_student *student_p;
{
 FILE  *fp;
 char   line[SMALL_LINE_BUFFER], fmtbuf1[SMALL_LINE_BUFFER],
        tmp_nm[MAX_STUDENT_NUMBER+1],
        tmp_nu[MAX_NAME_CHAR+1];
 int    i,found, len;
 char  *class, *cp;
 char   cid[4],cn[4];
#if defined(NeXT)
 char   cwd[FILE_NAME_LENGTH]; 
#endif

#if defined(NeXT) 
   class = getwd(cwd);
   if( class == NULL ) { class = cwd; }
#else
   class = getcwd(NULL,255);
#endif
  
  if( class == NULL ) { printf("capa_add_student(): Current working directory unknown!\n"); }
  len=strlen(class); cp=class; cp=cp+(len-8);
  for(i=0;i<3;i++) {
   cid[i] = cp[i+3];
   cn[i] = cp[i];
  }
  cid[3]=cn[3]=0;
  if ((fp=fopen("classl","r+"))==NULL) {
    /*printf("Error: Can't open classlist file\n");*/
    return (-1);
  }
  found = 0;
  sprintf(fmtbuf1, "%%%dc",MAX_STUDENT_NUMBER);
  while (fgets(line,SMALL_LINE_BUFFER-1,fp)) {
    sscanf(line+SN_BEGIN_COLUMN,fmtbuf1,tmp_nu);
    tmp_nu[MAX_STUDENT_NUMBER]='\0';
    if(!strncasecmp(student_p->s_sn,tmp_nu, MAX_STUDENT_NUMBER)) {
       found = 1; break;
    }
    i=0; cp = (char *)&line[NAME_BEGIN_COLUMN];
    while( (i < MAX_NAME_CHAR) && 
           (isalnum(cp[i]) || cp[i] == ',' || cp[i] == '.' || cp[i] == '\'' ||
           cp[i] == ' '    || cp[i] == '\t'|| cp[i] == '-' || cp[i] == '_' || cp[i] == '~') ) {
      tmp_nm[i]=cp[i]; i++;
    }
    tmp_nm[i]='\0';
    len=strlen(tmp_nm);
    if(!strncasecmp(student_p->s_nm,tmp_nm,len)) {
       found = 1;  break;
    }
  }
  if(!found) {
    sprintf(line,"%s %s   %03d %s %s\n",
       cn,cid,student_p->s_sec,student_p->s_sn,student_p->s_nm);
    len = strlen(line);
    fseek(fp,0L,SEEK_END);
    if (!fwrite(line,len,1,fp) ) {
      /*printf("Error writing data to file\n");*/
       found = -1;
    }
    fflush(fp);
  }
  fclose(fp);
  free(class);
  return (found);
}
/*----------------------------------------------------------*/
/* INPUT:  student_number  char array of student number     */
/*         set             the X in logX.db                 */
/* OUTPUT: none                                             */
/*                                                          */
/* RETURN:  -1    file error                                */
/*           0    no login                                  */
/*           >0   number of logins in that logX.db file     */
/*----------------------------------------------------------*/
int               
capa_get_login_count(student_number,set)
char  *student_number;
int    set;
{
   char     filename[FILE_NAME_LENGTH],
            sNumber[MAX_STUDENT_NUMBER+1],
            aNumber[MAX_STUDENT_NUMBER+1];
   FILE    *fp;
   char     line[MAX_BUFFER_SIZE],fmtbuf[MAX_BUFFER_SIZE];
   int      num;
   
  strncpy(sNumber, student_number,MAX_STUDENT_NUMBER+1);
  sprintf(filename,"records/log%d.db",set);
  if( !capa_access(filename, F_OK) ) {
    if ((fp=fopen(filename,"r"))==NULL) {
      /*printf("Error: can't open %s\n",filename);*/
      return (-1);
    }
  } else {
    return (-1);
  }
  num = 0;
  while (fgets(line,MAX_BUFFER_SIZE-1,fp)) {
    sprintf(fmtbuf, "%%%dc",MAX_STUDENT_NUMBER);
    sscanf(line,fmtbuf,aNumber);
    if(!strncasecmp(sNumber,aNumber, MAX_STUDENT_NUMBER)) num++;
  }
  fclose(fp);
  return (num);
}
/*----------------------------------------------------------*/
/* INPUT:  set             the X in logX.db                 */
/* OUTPUT: none                                             */
/*                                                          */
/* RETURN:  -1    file error                                */
/*           0    no login                                  */
/*           >0   number of logins in that logX.db file     */
/*----------------------------------------------------------*/
#define    ANSWER_BEGIN_COLUMN     35
int     
capa_get_login_db(login_item,num_probs,set) 
T_entry  login_item[];
int     *num_probs;
int      set;
{
 FILE    *fp;
 char     filename[FILE_NAME_LENGTH];
 char     line[MAX_BUFFER_SIZE],fmtbuf[SMALL_LINE_BUFFER];
 T_header header;
 int      num_q, count, len;
 
  if(capa_get_header(&header,set)) return (0);
  sscanf(header.num_questions,"%d",&num_q);
  *num_probs = num_q;
  capa_mfree(header.weight);
  capa_mfree(header.partial_credit);

  sprintf(filename,"records/log%d.db",set);
  if((fp=fopen(filename,"r"))==NULL) {
    /*printf("Error: can't open %s\n",filename);*/
    return (-1);
  }
  count=0;
  while ( fgets(line,MAX_BUFFER_SIZE,fp) && (strlen(line) != 0 )) {
    len = strlen(line);
    if(len > ANSWER_BEGIN_COLUMN ) {
      sprintf(fmtbuf, "%%%dc",MAX_STUDENT_NUMBER);
      sscanf(line,fmtbuf, login_item[count].student_number);
      login_item[count].answers = capa_malloc(num_q,1);
      sprintf(fmtbuf, "%%%dc",num_q);
      sscanf(line+ANSWER_BEGIN_COLUMN,fmtbuf, login_item[count].answers);
      count++;
    }
  }
  fclose(fp);
  return (count);
}

/*----------------------------------------------------------*/
/* INPUT:  option   the option the check                    */
/*         set      the X in dateX.db                       */
/*         section  which section to check for              */
/*                                                          */
/* OUTPUT: none                                             */
/*                                                          */
/* RETURN:  -2    invalid option                            */
/*          -1    capa_get_section_dates error              */
/*           0    no                                        */
/*           1    yes                                       */
/*----------------------------------------------------------*/
int capa_check_option(int option,int set,int section)
{
  T_dates* dates;
  int result;

  result=capa_get_section_dates(section,set,&dates);
  if (result < 0) return -1;
  switch(option) {
  case OPTION_INHIBIT_RESPONSE: result=dates->inhibit_response;break;
  case OPTION_VIEW_PROBLEMS_AFTER_DUE: result=dates->view_problems_after_due;break;
  default: result=-2;
  }
  capa_mfree((char*)dates);
  return result;
}

/*----------------------------------------------------------*/
/* INPUT:  time             the current time                */
/*         datetime         the datetime string to compare  */
/* OUTPUT: none                                             */
/*                                                          */
/* RETURN:  -1    time is earlier then datetime             */
/*           1    time is later than datetime               */
/*----------------------------------------------------------*/
int
compare_datetime(time,datetime)
time_t  time;
char   *datetime;
{
  char    dateStr[16], timeStr[16];
  time_t  time2;
  char   *t_ptr;
  int     idx;
  
/*  sscanf(datetime,"%s %s",dateStr,timeStr); */
  t_ptr = index(datetime,' '); t_ptr++;    /*** hpux complained */
  for(idx=0;idx<10;idx++) dateStr[idx] = datetime[idx];
  dateStr[10] = 0;
  for(idx=0;idx<5;idx++) timeStr[idx] = t_ptr[idx];
  timeStr[5] = 0;
  time2 = convertTime(dateStr,timeStr);
  if(time == time2 ) return (0);
  return ( (time >  time2)? 1 : -1 );
}

/* --------------------------------- */
/* check if records/dateX.db exists  */
/* read the section open date, answer date, due date info */
/* What if: [7,3] date_info  */
/*          [3,7] date_info  */
/*          4 date_info      */
/* RETURN:  -1    if not pass time                          */
/*           1    if pass time (or no time available        */
int capa_check_date(int which,char *student_number, int section,int set)
{
  int result;
  char date_str[TMP_LINE_LENGTH];
  time_t curtime;
  
  time(&curtime);
  
  result = capa_get_date(which,student_number,section,set,date_str);
  if ( result == 1 ) { result = compare_datetime(curtime,date_str); }
  return result;
}

time_t capa_convert_duration(char *duration)
{
  int hour, minute;
  sscanf(duration,"%d:%d",&hour,&minute);
  return ((hour*60)+minute)*60;
}

void capa_get_due_date(char *date_str,T_dates *current,char *student_number,int set)
{
  time_t duration=capa_convert_duration(current->duration);
  time_t duetime=0;
  time_t logintime;
  struct tm *due_time_tm;
  if ((duration > 0) && (student_number!=NULL)) {
    if (capa_get_login_time(student_number,set,&logintime)==1) {
      duetime=logintime+duration;
      if (compare_datetime(duetime,current->due_date)==-1) {
	due_time_tm=localtime(&duetime);
	sprintf(date_str,"%04d/%02d/%02d %02d:%02d",((due_time_tm->tm_year)+1900),
		due_time_tm->tm_mon+1,due_time_tm->tm_mday,due_time_tm->tm_hour,
		due_time_tm->tm_min);
      } else {
	strncpy(date_str,current->due_date,DATE_BUFFER); 
      }
      return;
    }
  } 
  strncpy(date_str,current->due_date,DATE_BUFFER); 
}

/* student_number can be NULL, if it isn't NULL it is used by get_due_date */
/* to assign a due date based on the time the student first accessed the   */
/* set if duration is non-zero                                             */
int
capa_get_date(int which,char *student_number,int section,int set,char *date_str)
{
  T_dates *current;
  int result;
  result=capa_get_section_dates(section,set,&current);
  if (result<0) return result;
  result=1;
  switch(which) {
  case CHECK_OPEN_DATE:  strncpy(date_str,current->open_date,DATE_BUFFER);  break;
  case CHECK_DUE_DATE:   capa_get_due_date(date_str,current,student_number,set); break;
  case CHECK_ANS_DATE:   strncpy(date_str,current->answer_date,DATE_BUFFER); break;
  default: strncpy(date_str,current->open_date,DATE_BUFFER);  result=-4;break;
  }
  capa_mfree((char*)current);
  return result;
}

int
capa_get_duration(char *student_number,int section,int set)
{
  T_dates *current;
  int duration=0,result;
  result=capa_get_section_dates(section,set,&current);
  if (result<0) return result;  
  duration=capa_convert_duration(current->duration);
  capa_mfree((char*)current);
  return duration;
}

int
capa_get_section_dates(int section,int set,T_dates** dates)
{
  register   int  c;
  FILE      *fp;          
  int        result = 0, done;
  int        tmp_low, tmp_high, sec_mode;
  char       filename[FILE_NAME_LENGTH], tmpline[TMP_LINE_LENGTH],
    default_line[TMP_LINE_LENGTH];
  
  sprintf(filename,"records/date%d.db",set);
  if( capa_access(filename, F_OK) != 0 ) { result = -2; } else {
    if ((fp=fopen(filename,"r")) == NULL) { result = -2; } else { 
      done = 0; result = -3;
      /* first non-comment line is assumed to be the default line */
      c_gettype(fp); c_ignorewhite(fp); fgets(default_line,TMP_LINE_LENGTH-1,fp);
      do {
	c_ignorewhite(fp); c = getc(fp); ungetc(c,fp);
	if( c != EOF ) {
	  sec_mode = c_getsec_range(fp,&tmp_low,&tmp_high);
	  if( sec_mode > 0 ) { /* no error */
	    c_ignorewhite(fp); fgets(tmpline,TMP_LINE_LENGTH-1,fp);
	    if( sec_mode == 1 ) { /* form: sec date_info */
	      if((result == -3) && (tmp_low == section)) {
		result=-4;
		*dates=add_date_info(tmp_low,tmp_low,tmpline);
		if ( *dates != NULL ) { done = 1; result = 1; }
	      }
	    } else { /* form : [low,high] date_info */
	      if( (result == -3) && ((section>=tmp_low)&&(section<=tmp_high)) ) { 
		result = -4;
		*dates=add_date_info(tmp_low,tmp_high,tmpline);
		if ( *dates != NULL ) { done = 1; result = 1; }
	      }
	    }
	  } else { /* error in specify section date */
	    while ( ((c = getc(fp)) != '\n') && ( c != EOF) ); /* skip to end of line */
	  }
	} else { /* EOF encountered */
	  done = 1;
	}
      } while (!done);
      /* need to deal with those sections not show up in the dateX.db file */
      if( result == -3 ) { /* section not in the dateX.db file, use default */
	result = -4;
	*dates=add_date_info(DATE_DEFAULTS,DATE_DEFAULTS,default_line);
	if ( *dates != NULL ) { result = 1; }
      }
    }
    fclose(fp);
  } 
  return (result); 
}

T_dates* add_date_info(int lowsec,int highsec, char *dateinfo)
{
  T_dates *dates;
  int result;
  dates=(T_dates*)capa_malloc(sizeof(T_dates),1);
  result=sscanf(dateinfo,"%16c,%16c,%16c,%s %d,%d",dates->open_date,dates->due_date,
		dates->answer_date,dates->duration,&(dates->inhibit_response),
		&(dates->view_problems_after_due));
  if (result != 6) {
    capa_mfree((char*)dates);
    dates=NULL;
  } else {
    dates->section_start=lowsec;
    dates->section_end=highsec;
    dates->s_next=NULL;
  }
  return dates;
}
/*----------------------------------------------------------*/
/* INPUT:  set             the X in dateX.db                */
/*         dates           a pointer to the dates pointer   */
/* ACTION: builds a linked list of T_dates containing       */
/*         the login information                            */
/* RETURN:  <0    file error                                */
/*          >0    success (number of lines successfully     */
/*                         proccessed)                      */
/*----------------------------------------------------------*/
int capa_get_all_dates (int set,T_dates **dates) 
{
  T_dates    *current;
  FILE      *fp;          
  int        result = 0, done, tmp_low, tmp_high, sec_mode, c, num=0;
  char       filename[FILE_NAME_LENGTH], tmpline[TMP_LINE_LENGTH];
  
  sprintf(filename,"records/date%d.db",set);
  if ( ((fp=fopen(filename,"r")) == NULL)) { result = -2; } else {
    done = 0; result = -3;
    /* first non-comment line is assumed to be the default line */
    c_gettype(fp); c_ignorewhite(fp); fgets(tmpline,TMP_LINE_LENGTH-1,fp);
    *dates=add_date_info(DATE_DEFAULTS,DATE_DEFAULTS,tmpline);
    num++;
    if ( NULL == (current=*dates) ) {
      result = -3; 
    } else {
      while(1) {
	c_ignorewhite(fp); c = getc(fp); ungetc(c,fp);
	if( c == EOF ) { break; } 
	sec_mode = c_getsec_range(fp,&tmp_low,&tmp_high);
	if( sec_mode > 0 ) { /* no error */
	  c_ignorewhite(fp); fgets(tmpline,TMP_LINE_LENGTH-1,fp);
	  if( sec_mode == 1 ) { /* form: sec date_info */
	    current->s_next=add_date_info(tmp_low,tmp_low,tmpline);
	  } else { /* form : [low,high] date_info */
	    current->s_next=add_date_info(tmp_low,tmp_high,tmpline);
	  }
	  current=current->s_next;
	  num++;
	} else { /* error in specify section date */
	  while ( ((c = getc(fp)) != '\n') && ( c != EOF) ); /* skip to end of line */
	}
      }
      result=num;
    }
    fclose(fp);
  }
  return (result); 
}

/*----------------------------------------------------------*/
/* INPUT:  dates           a pointer to the dates pointer   */
/* ACTION: frees a linked list of T_dates                   */ 
/*----------------------------------------------------------*/
void free_dates(T_dates *dates)
{
  T_dates *current = dates,*next;
  while ( current != NULL ) { 
    next = current->s_next;
    capa_mfree((char*)current);
    current = next;
  }
}

/*----------------------------------------------------------*/
/* INPUT:  set             the X in dateX.db                */
/*         dates           a pointer to the dates pointer   */
/* ACTION: takes a linked list of date information and      */
/*         and writes the info to a file                    */
/* RETURN:  -1    file error                                */
/*           1    success                                   */
/*----------------------------------------------------------*/
int capa_set_all_dates (int set,T_dates *dates) 
{
  T_dates *current = dates;
  int result;
  FILE* fp;
  char filename[FILE_NAME_LENGTH];
  
  sprintf(filename,"records/date%d.db",set);
  if ( ((fp=fopen(filename,"w")) == NULL) ) { result = -1; } else {
    result=1;
    while ( current != NULL ) { 
      if ( current->section_start == DATE_DEFAULTS ) {
	fprintf(fp,"<< DEFAULTS >> ");
      } else {
	fprintf(fp,"[%d, %d] ", current->section_start,current->section_end);
      }
      fprintf(fp,"%s,%s,%s,%s %d,%d\n", current->open_date,current->due_date,
	      current->answer_date,current->duration,current->inhibit_response,
	      current->view_problems_after_due);
      current = current->s_next;
    }
    fclose(fp);
  }
  return result;
}

/*----------------------------------------------------------*/
/* INPUT:  set             the X in logX.db                 */
/* OUTPUT: none                                             */
/*                                                          */
/* RETURN:  -1    file error                                */
/*           0    no login                                  */
/*           >0   number of logins in that logX.db file     */
/*----------------------------------------------------------*/

#define     FIFTEEN_MINUTES     (15*60)
#define     TEN_MINUTES         (600)
#define     ONE_DAY             (86400)
int         /* RETURNS:  1 first time login, 2 second time login, 0 not ok, -1 file error    */
login_check(student_number)     /* ARGUMENTS:             */
char *student_number;           /*    Student #           */
{                               /* LOCALS:                */
   FILE    *fp; 

   int      errcode=0;
   int      found;
   char     filename[FILE_NAME_LENGTH];
   char     line[SMALL_LINE_BUFFER], new_line[SMALL_LINE_BUFFER];
   struct tm  *theDT;
   time_t   login_time, record_time;
   long     offsetL, offsetR, offsetEnd,left_leng;
   char      s_number[MAX_STUDENT_NUMBER+1];
   char      tty_name[FILE_NAME_LENGTH];
   int       log_tries, p_id;
   int       month, day, year, hour, min, sec;
   char     *tmp_buffer;

   sprintf(filename,"records/active.log");

   if( capa_access(filename,F_OK) < 0 ) {  /*<------------- File not exist */
      if ((fp=fopen(filename,"w"))==NULL) { /* create if non-existant */
	/*printf("Error: can't create %s\n",filename);*/
         return (-1); 
      }
      fclose(fp);
   }
   if ((fp=fopen(filename,"r"))==NULL) {
     /*printf("Error: can't open %s\n",filename);*/
      return (-1);
   }
   flockstream(fp); /* lock exclusively and perform read/write operation */
    
   found = 0;
   while( (!found) && ( fgets(line,SMALL_LINE_BUFFER-1,fp) != NULL) ) {
       if( !strncasecmp(line,student_number,MAX_STUDENT_NUMBER) )  found = 1;
   }
   offsetR = ftell(fp);
   offsetL = offsetR - strlen(line);
   funlockstream(fp); fclose(fp);   
   
   if( found && (strlen(line) != 0) ) {
     /* printf("FOUND:%slen=%d\n",line,strlen(line)); */
     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);
     record_time = time(NULL);
     theDT = localtime(&record_time);
     theDT->tm_sec = sec;     theDT->tm_min = min;
     theDT->tm_hour = hour;   theDT->tm_mday = day;
     theDT->tm_mon = month-1; theDT->tm_year = year;
     theDT->tm_wday = weekday(year,month,day);
     theDT->tm_yday = julianday(year,month,day); 
     record_time = mktime( theDT );
     time(&login_time);       theDT = localtime(&login_time);
     switch(log_tries) {
        case 0: log_tries = 1; errcode = 1;  break;
        case 1: log_tries = 2; errcode = 2; 
                break;
        case 2: log_tries = 3; errcode = 0;  break;
        case 3: if( (login_time - record_time) >= TEN_MINUTES ) {
                   log_tries = 1; errcode = 1;
                } else {
                   log_tries = 3; errcode = 2;
                   return (0);
                }
                break;
       default: printf("ERROR: Number of logins UNKNOWN\n");
                log_tries = 1; errcode = 1;
              break;
     }
     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);
     if ((fp=fopen(filename,"r+"))==NULL) {
       /*printf("Error: can't open %s\n",filename);*/
        return (-1);
     }
     flockstream(fp);
     tmp_buffer = (char *)malloc(8*1024*56);
     found = 0;
     while( (!found) && ( fgets(line,SMALL_LINE_BUFFER-1,fp) != NULL) ) {
       if( !strncasecmp(line,student_number,MAX_STUDENT_NUMBER) )  found = 1;
     }
     offsetR = ftell(fp);   offsetL = offsetR - strlen(line);
     fseek(fp,0L,SEEK_END); offsetEnd = ftell(fp);
     left_leng = offsetEnd - offsetR;
     fseek(fp,offsetR,SEEK_SET);
     left_leng = fread(tmp_buffer, 1, left_leng+1, fp);
     tmp_buffer[left_leng] = 0;
     fseek(fp,offsetL,SEEK_SET);
      if ( fprintf(fp,"%s%s",new_line,tmp_buffer) < 0  ) {
	/*printf("Error: cannot write active.log\n");*/
           errcode = -1;
      }
     fflush(fp);
     free( (char *)tmp_buffer);
     funlockstream(fp);
     fclose(fp);
   } else { /********************************** First time login */
     if ((fp=fopen(filename,"a+"))==NULL) {
       /*printf("Error: can't open %s\n",filename);*/
        return (-1);
     }
     log_tries = 1;
     time(&login_time);
     theDT = localtime(&login_time);
     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);
/*
     leng = strlen(line);
     for(idx = 0, len_idx = 0; idx<(leng-1); idx++) {
       if(line[idx] == '\n' && line[idx+1] == '\n') {
          line[idx+1] = 0;
       }
     }
*/
     flockstream(fp);
     if ( !fwrite((char *)line, strlen(line), 1, fp) ) {
       /*printf("ERROR: cannot write active.log\n");*/
           errcode = -1;
     } else {
           errcode = 1;
     }
     fflush(fp);
     funlockstream(fp);
     fclose(fp);
   }
    return (errcode);
 
}

/******************************************************************************/
/* Logout check                                                               */
/******************************************************************************/
int                /* RETURNS:   1 successful, 0  otherwise, -1 file error  */
logout_check(student_number)     /* ARGUMENTS:             */
char *student_number;             /*    Student #           */
{                                /* LOCALS:                */
   FILE    *fp; 

   int      errcode=0;
   int      found;
   char     filename[FILE_NAME_LENGTH];
   char     line[SMALL_LINE_BUFFER];
   long     offsetL, offsetR, offsetEnd,left_leng;
   char     s_number[MAX_STUDENT_NUMBER+1];
   char     tty_name[FILE_NAME_LENGTH];
   int      log_tries, p_id;
   int      month, day, year, hour, min, sec;
   char     *tmp_buffer;


   sprintf(filename,"records/active.log");
   if ((fp=fopen(filename,"r"))==NULL) {
     /*printf("Error: can't open %s\n",filename);*/
      return (-1);
   }
   flockstream(fp); /* lock exclusively and perform read/write operation */
    
   found = 0;
   while( (!found) && ( fgets(line,SMALL_LINE_BUFFER-1,fp) != NULL) ) {
       if( !strncasecmp(line,student_number,MAX_STUDENT_NUMBER) )  found = 1;
   }
   offsetR = ftell(fp);
   offsetL = offsetR - strlen(line);
    
   funlockstream(fp);
   fclose(fp);   
   
   if( found ) {
#ifdef __alpha
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);
#else
     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);
#endif
     switch(log_tries) {
        case 0: log_tries = 0;
              break;
        case 1: log_tries = 0;
              break;
        case 2: log_tries = 0;
              break;
        default: printf("ERROR: Number of logins UNKNOWN\n");
                log_tries = 0;
              break;
     }
#ifdef __alpha
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);
#else
     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);
#endif

     if ((fp=fopen(filename,"r+"))==NULL) {
       /*printf("Error: can't open %s\n",filename);*/
        return (-1);
     }
     flockstream(fp);
     tmp_buffer = (char *)malloc(8*1024*56);
     fseek(fp,0L,SEEK_END);
     offsetEnd = ftell(fp);
     left_leng = offsetEnd - offsetR;
     fseek(fp,offsetR,SEEK_SET);
     fread(tmp_buffer, left_leng, 1, fp);
     tmp_buffer[left_leng] = 0;
/*
     for(idx=0, l_idx = 0; idx< (left_leng-1); idx++) {
       if( tmp_buffer[idx] == '/n' && tmp_buffer[idx+1] == '/n' ) {
       }
     }
*/
     fseek(fp,offsetL,SEEK_SET);
      if ( fprintf(fp,"%s%s",line,tmp_buffer) < 0  ) {
	/*printf("Error: write error\n");*/
           errcode = -1;
      } else {
           errcode = 1;
      }
     fflush(fp);
     free( (char *)tmp_buffer);
     funlockstream(fp);
     fclose(fp);
   } else {
     errcode = -1;
   }
    return (errcode);
 
}
/*********************************************/
/*  SIZE of char =1, int=4, long=4, double=8 */
void
capa_seed(seed1,seed2,student_number)long *seed1;long *seed2;char *student_number;
{
  int   class_pin1, class_pin2;
  int   s_pin1, s_pin2, s_pin3;
  int   i;
  char  dest[16], tmpSN[MAX_STUDENT_NUMBER+1];
  char *class,*capadefault="capadefault";
  long  part1, part2;
#if defined(NeXT)
  char  cwd[FILE_NAME_LENGTH];
#endif
  int   big_endian;

  big_endian = endian();  /* determine what type of CPU we are on */
  
#if defined(NeXT) 
   class = getwd(cwd);
   if( class == NULL ) { class = cwd; }
#else
   class = getcwd(NULL,255);
#endif
     class_pin1 = class_pin2 = 2;
     s_pin1 =  s_pin2 = s_pin3 = 2;

  if( class == NULL ) { 
    printf("capa_seed(): Current working directory unknown! Using capadefault\n"); 
    class=capa_malloc(strlen(capadefault)+1,1);
    strcpy(class,capadefault);
  }
  if( big_endian ) {
      for(i=0;i<4;i++) dest[i] = class[strlen(class)-8+i];
      for(i=4;i<8;i++) dest[i] = 0;
      memcpy((char *)(&class_pin1), dest, 4);
      for(i=0;i<4;i++) dest[i] = class[strlen(class)-4+i];
      for(i=4;i<8;i++) dest[i] = 0;
      memcpy((char *)(&class_pin2), dest, 4);
  } else { 
      for(i=0;i<4;i++) dest[i] = class[strlen(class)-i-5];
      for(i=4;i<8;i++) dest[i] = 0;
      memcpy((char *)(&class_pin1), dest, 4);
      for(i=0;i<4;i++) dest[i] = class[strlen(class)-i-1];
      for(i=4;i<8;i++) dest[i] = 0;
      memcpy((char *)(&class_pin2), dest, 4);
  }
  for(i=0;i<MAX_STUDENT_NUMBER;i++) {
    if(islower(student_number[i])) {
      tmpSN[i] = toupper(student_number[i]);
    } else {
      tmpSN[i] = student_number[i];
    }
  }
  tmpSN[MAX_STUDENT_NUMBER] = 0;
 
  if( big_endian ) {  /* big endian ** SUN, BlackNeXT 68xxx , PowerPC */
    for(i=0;i<4;i++) dest[i] = tmpSN[i]; 
    for(i=4;i<8;i++) dest[i] = 0;
    memcpy((char *)(&s_pin1), dest, 4);       /* 012345678 -> "0123" */
    for(i=0;i<4;i++) dest[i] = tmpSN[i+2]; 
    for(i=4;i<8;i++) dest[i] = 0;
    memcpy((char *)(&s_pin2), dest, 4);   /* 012345678 -> "2345" */
    for(i=0;i<4;i++) dest[i] = tmpSN[i+5]; 
    for(i=4;i<8;i++) dest[i] = 0;
    memcpy((char *)(&s_pin3), dest, 4);   /* 012345678 -> "5678" */
  } else {  /* Intel 386, 486 */
    for(i=0;i<4;i++) dest[i] = tmpSN[3-i]; 
    for(i=4;i<8;i++) dest[i] = 0;
    memcpy((char *)(&s_pin1), dest, 4);       /* 012345678 -> "0123" */
    for(i=0;i<4;i++) dest[i] = tmpSN[5-i]; 
    for(i=4;i<8;i++) dest[i] = 0;
    memcpy((char *)(&s_pin2), dest, 4);   /* 012345678 -> "2345" */
    for(i=0;i<4;i++) dest[i] = tmpSN[8-i]; 
    for(i=4;i<8;i++) dest[i] = 0;
    memcpy((char *)(&s_pin3), dest, 4);   /* 012345678 -> "5678" */
  }


  part1 = s_pin1 + s_pin3+ class_pin2;    if(part1 < 0) part1 = part1 * (-1);
  part2 = s_pin2 + class_pin1;            if(part2 < 0) part2 = part2 * (-1);

#ifdef SEED_DBG 
  printf("S_PIN(%d,%d, %d) C_PIN(%d,%d)\n",s_pin1, s_pin2, s_pin3, class_pin1, class_pin2);
  printf("SEED(%ld,%ld)\n",part1, part2);
#endif 
  {
    extern void gsrgs(long getset,long *qvalue);
    static long qrgnin;
    gsrgs(0L,&qrgnin);
    if(!qrgnin) 
      setall(part1, part2);
  }
  (*seed1) = part1;
  (*seed2) = part2;
  free(class);
}
/* ======================================================= PIN number */
/* pin should only called once for each student. */
/* if not called at the beginning of problem set, try to call it automatically */

int                             /* RETURNS: pin number  for login set  */
capa_PIN(student_number, set, guess) /* ARGUMENTS:             */
char *student_number;             
int set;                        /*    Problem set number  */
int guess;
{                               /* LOCALS:                */
   int        current=0,          /*    Current pin for set */
              i,j,              /*    Array indices       */
              nope,
              correct=0,
	      all_pins[ONE_K];
   long       part1, part2;
   long       orig_gen, new_gen;
   
  capa_seed(&part1, &part2, student_number);
  gscgn(GET_GENERATOR, &orig_gen);
  new_gen = PIN_G;
  gscgn(SET_GENERATOR, &new_gen);
  setsd(part1, part2);
  /* Generate 4-digit pin (1000-9999) */
  for (i=1; i<=set; i++) {
    current=1000+ignlgi()%9000;  
    do {
      nope=0;
      for (j=1; j<i; j++) {
        if (current == all_pins[j]) {
          current=1000+ignlgi()%9000;
          nope++;
        }
      }
    } while (nope);
    all_pins[i]=current;
    if (guess && guess==current)   correct=i;
  }
  gscgn(SET_GENERATOR, &orig_gen);
  if (guess) return (correct);
  return (current);
}

/* -------------------------------------------------------------------- */
/* returns a longer pin, the first four characters are the same as the  */
/* normal capaId, additionally the result is a capa_malloc string       */
/* containing the number                                                */
/* NOTE!-> the result is a string of letters where A=1..I=9,J=0         */
/* -------------------------------------------------------------------- */
char*
capa_id_plus(student_number, set, plus)
char *student_number;
int set;
int plus;
{
  long part1,part2,orig_gen,new_gen,pin,rout,k,i;
  char *result;
  char       letters[10]={'J','A','B','C','D','E','F','G','H','I'};
  capa_seed(&part1, &part2, student_number);
  gscgn(GET_GENERATOR, &orig_gen);
  new_gen = PIN_G;
  gscgn(SET_GENERATOR, &new_gen);
  setsd(part1, part2);
  pin=capa_PIN(student_number,set,0);
  result=(char *)capa_malloc(sizeof(char), plus+MAX_PIN_CHAR+1);
  k=1;
  for(i=1;i<=MAX_PIN_CHAR;i++) {
    result[MAX_PIN_CHAR-i] = letters[(pin%(k*10))/k];
    k*=10;
  }
  for(i=MAX_PIN_CHAR;i<MAX_PIN_CHAR+plus;i++) {
    rout = ignlgi()%10;
    result[i] = letters[rout];
  }
  result[i] = '\0';
  gscgn(SET_GENERATOR, &orig_gen);
  return result;
}
/* -------------------------------------------------------------------- */
/* need to set problem_p = NULL after this call */
/* -------------------------------------------------------------------- */
void
free_problems(problem_p) Problem_t *problem_p;
{
   Problem_t *p, *next;
 
   for (p=problem_p; p!=NULL ; p=next) {
      next=p->next;
      if (p->question != NULL) capa_mfree(p->question);
      if (p->answer != NULL)   capa_mfree(p->answer);
      if (p->ans_cnt > 1 ) { AnswerInfo_t  *a,*b;
        for(a = p->ans_list; a != NULL ; a = b) {
          b = a->ans_next;
          if (a->ans_str != NULL)       capa_mfree(a->ans_str);
          if (a->ans_id_list != NULL )  capa_mfree(a->ans_id_list);
          if (a->ans_pts_list) {
            free_ptslist(a->ans_pts_list);
          }
          if (a->ans_unit) {
            freelist_unit_e(a->ans_unit->u_list);
            if (a->ans_unit != NULL) capa_mfree((char *)a->ans_unit);
          }
          capa_mfree((char *)a);
        }
      }
      if (p->id_list != NULL )  capa_mfree(p->id_list);
      if (p->pts_list!= NULL ) {
        free_ptslist(p->pts_list);
      }
      if (p->hint != NULL )     capa_mfree(p->hint);
      if (p->explain != NULL )  capa_mfree(p->explain);
      if (p->ans_unit !=NULL ) { 
        freelist_unit_e(p->ans_unit->u_list);
        capa_mfree((char *)p->ans_unit);
      }
      capa_mfree((char *)p);
      p=NULL;
   }
   problem_p=NULL;
}

/******************************************************************************/
/* PARSE SOURCE FILE AND RETURN BLOCKS OF TEXT                                */
/******************************************************************************/
int  
capa_parse(set,problem,student_number,num_questions,func_ptr)
int  set;Problem_t **problem;char  *student_number;int  *num_questions;
void (*func_ptr)();
{
  char  filename[QUARTER_K];
  int   errcode;
  FILE *fp;
extern  FILE      *Input_stream[MAX_OPENED_FILE];
extern  char       Opened_filename[MAX_OPENED_FILE][QUARTER_K];
extern  int        Lexi_line;
extern  int        Lexi_qnum;
extern  Problem_t *FirstProblem_p;
extern  Problem_t *LastProblem_p;
extern  Problem_t *LexiProblem_p;
extern  char      *StartText_p;
extern  char      *EndText_p;
extern  char      *ErrorMsg_p;
extern  int        ErrorMsg_count;
extern  char       Parse_class[QUARTER_K];
extern  int        Parse_section;
extern  int        Parse_set;
extern  char       Parse_name[MAX_NAME_CHAR+1];
extern  char       Parse_student_number[MAX_STUDENT_NUMBER+1];
extern  int        Symb_count;
extern  int        first_run;
extern  int        Stop_Parser;
extern  void       (*Status_Func)();
#ifdef TTH
extern void tth_restart();
extern char* tth_err;
#endif /*TTH*/
  long  seed1,     seed2;
  T_student        a_student;
  char            *class, *classname, warn_msg[WARN_MSG_LENGTH];
#if defined(NeXT) 
   char    cwd[FILE_NAME_LENGTH];

   class = getwd(cwd);
   if( class == NULL ) { class = cwd; }
#else
   class = getcwd(NULL,255);
   
#endif

  if(class == NULL) { /* printf("capa_parse(): Current working directory unknown!"); */  return (-1); }
  classname = rindex(class,'/');  classname++; /*** hpux complained */
  sprintf(Parse_class,"%s", classname);
  free(class);
  if( capa_get_student(student_number, &a_student) < 1 )  {
 /*printf("Error: capa_parse() encountered a student which is not in classl file\n"); */
    return (-1);
  }
  sprintf(filename,"capa.config");
  if ((fp=fopen(filename,"r"))==NULL) {
    /* printf("Error: can't open %s\n",filename);*/
      return (-1); 
  }
  u_getunit(fp);
  fclose(fp);
#ifdef TTH
  if(tth_err) { free(tth_err); tth_err=NULL; }
  tth_restart();
#endif /*TTH*/
  strncpy(Parse_name,a_student.s_nm,MAX_NAME_CHAR+1);
  strncpy(Parse_student_number,student_number,MAX_STUDENT_NUMBER+1);
  Parse_section = a_student.s_sec;
  if(ErrorMsg_p) { capa_mfree(ErrorMsg_p); ErrorMsg_p = NULL; }
  if(EndText_p)  { capa_mfree(EndText_p);  EndText_p  = NULL; }
  if(StartText_p)  { capa_mfree(StartText_p);  StartText_p  = NULL; }
  ErrorMsg_p = NULL; first_run = 1; EndText_p = NULL;
  free_symtree();    Symb_count = ErrorMsg_count = Lexi_line = Lexi_qnum = 0;
  FirstProblem_p = LastProblem_p = NULL;
  LexiProblem_p = (Problem_t *)capa_malloc(sizeof(Problem_t),1);
  problem_default(LexiProblem_p);
  /*LexiProblem_p->capaidplus=NULL;*/
  Parse_set = set;
  Status_Func=func_ptr;
  sprintf(filename,"set%d.qz",set);
#ifdef AVOIDYYINPUT
  yyin=fopen(filename,"r");
#else
 if ( (Input_stream[0]=fopen(filename,"r")) == NULL) {
     /* printf("Error: can't open %s\n",filename);*/
     sprintf(warn_msg,"capa_parse(): CANNOT OPEN FILE\"%s\", file does not exist or is not readable.\n", filename);
     capa_msg(MESSAGE_ERROR,warn_msg);
     return (-1);
  }
#endif
  sprintf(Opened_filename[0],"%s",filename);
  
  capa_seed(&seed1, &seed2, student_number);   setall(seed1,seed2);

  yyrestart(yyin);
  Stop_Parser=0;
  if ( !yyparse() )  { errcode = Lexi_qnum; } else { errcode = 0; }
  /* fclose(Input_stream[0]);*/ /* The Lexer handles closing this */
  /* print_symb_stat(); */
  free_symtree();
  /*
  capa_mfree((char *)LexiProblem_p);
  LexiProblem_p = NULL;
  */
 (*problem) = FirstProblem_p;
 (*num_questions) = Lexi_qnum;
  return (errcode);
}

/******************************************************************************/
/* PARSE SOURCE FILE AND RETURN BLOCKS OF TEXT, unlike capa_parse_student     */
/******************************************************************************/
int  
capa_parse_student(set,problem,a_student,num_questions,func_ptr)
int  set;Problem_t **problem;T_student  *a_student;int  *num_questions;
void (*func_ptr)();
{
  char  filename[QUARTER_K];
  int   errcode;
  FILE *fp;
extern  FILE      *Input_stream[MAX_OPENED_FILE];
extern  char       Opened_filename[MAX_OPENED_FILE][QUARTER_K];
extern  int        Lexi_line;
extern  int        Lexi_qnum;
extern  Problem_t *FirstProblem_p;
extern  Problem_t *LastProblem_p;
extern  Problem_t *LexiProblem_p;
extern  char      *StartText_p;
extern  char      *EndText_p;
extern  char      *ErrorMsg_p;
extern  int        ErrorMsg_count;
extern  char       Parse_class[QUARTER_K];
extern  int        Parse_section;
extern  int        Parse_set;
extern  char       Parse_name[MAX_NAME_CHAR+1];
extern  char       Parse_student_number[MAX_STUDENT_NUMBER+1];
extern  int        Symb_count;
extern  int        first_run;
extern  void       (*Status_Func)();
  long  seed1,     seed2;
  char            *class, *classname, warn_msg[WARN_MSG_LENGTH];
  
#if defined(NeXT) 
   char    cwd[FILE_NAME_LENGTH];

   class = getwd(cwd);
   if( class == NULL ) { class = cwd; }
#else
   class = getcwd(NULL,255);
   
#endif

  if(class == NULL) { /* printf("capa_parse(): Current working directory unknown!"); */  return (-1); }
  classname = rindex(class,'/');  classname++; /*** hpux complained */
  sprintf(Parse_class,"%s", classname);
  free(class);

  sprintf(filename,"capa.config");
  if ((fp=fopen(filename,"r"))==NULL) {
    /* printf("Error: can't open %s\n",filename);*/
    sprintf(warn_msg,"capa_parse(): CANNOT OPEN FILE\"%s\", file does not exist or is not readable.\n", filename);
    capa_msg(MESSAGE_ERROR,warn_msg);
    return (-1); 
  }
  u_getunit(fp);
  fclose(fp);
  strncpy(Parse_name,a_student->s_nm,MAX_NAME_CHAR+1);
  strncpy(Parse_student_number,a_student->s_sn,MAX_STUDENT_NUMBER+1);
  Parse_section = a_student->s_sec;
  if(ErrorMsg_p) { capa_mfree(ErrorMsg_p); ErrorMsg_p = NULL; }
  if(EndText_p)  { capa_mfree(EndText_p);  EndText_p  = NULL; }
  if(StartText_p)  { capa_mfree(StartText_p);  StartText_p  = NULL; }
  ErrorMsg_p = NULL; first_run = 1; EndText_p = NULL;
  free_symtree();    Symb_count = ErrorMsg_count = Lexi_line = Lexi_qnum = 0;
  FirstProblem_p = LastProblem_p = NULL;
  LexiProblem_p = (Problem_t *)capa_malloc(sizeof(Problem_t),1);
  problem_default(LexiProblem_p);
  /*LexiProblem_p->capaidplus=NULL;*/
  Parse_set = set;
  Status_Func=func_ptr;
  sprintf(filename,"set%d.qz",set);  
#ifdef AVOIDYYINPUT
  yyin=fopen(filename,"r");
#else
 if ( (Input_stream[0]=fopen(filename,"r")) == NULL) {
     /* printf("Error: can't open %s\n",filename);*/
     sprintf(warn_msg,"capa_parse(): CANNOT OPEN FILE\"%s\", file does not exist or is not readable.\n", filename);
     capa_msg(MESSAGE_ERROR,warn_msg);
     return (-1);
  }
#endif
  sprintf(Opened_filename[0],"%s",filename);
  
  capa_seed(&seed1, &seed2, a_student->s_sn);   setall(seed1,seed2);

  yyrestart(yyin);
  if ( !yyparse() )  { errcode = Lexi_qnum; } else { errcode = 0; }
  /* fclose(Input_stream[0]);*/ /*The Lexer handles closing this*/
  /* print_symb_stat(); */
  free_symtree();
  /*
  capa_mfree((char *)LexiProblem_p);
  LexiProblem_p = NULL;
  */
 (*problem) = FirstProblem_p;
 (*num_questions) = Lexi_qnum;
  return (errcode);
}

/* =================================================================== */
/* A utility method to convert a date string and time string to time_t */
/*  dateStr:    yyyy/mm/dd */
/*  timeStr:    hh:mm    */
time_t
convertTime(dateStr,timeStr)char *dateStr; char *timeStr;
{
  struct   tm  *theTimeData;
  time_t   result;
  int      dateTime[5];
  int      year, month, day, mm, hh;

  sscanf(dateStr,"%4d/%2d/%2d",&year,&month,&day);
  dateTime[0] = month;
  dateTime[1] = day;
  dateTime[2] = year;
  sscanf(timeStr,"%2d:%2d",&hh,&mm);
  dateTime[3] = hh;
  dateTime[4] = mm;
  result = time(NULL);
  theTimeData = localtime(&result);
  theTimeData->tm_sec = 0;
  theTimeData->tm_min = dateTime[4];
  theTimeData->tm_hour = dateTime[3];
  theTimeData->tm_mday = dateTime[1];
  theTimeData->tm_mon = dateTime[0]-1;
  theTimeData->tm_year = dateTime[2]-1900;/* tm_year is years since 1900 */
  /* these fields are ignored by mktime
  theTimeData->tm_wday = weekday(year,month,day);
  theTimeData->tm_yday = julianday(year,month,day); 
  */
  result = mktime( theTimeData );
  return (result);
}

int
weekday( year, month, day) int year; int month; int day;
{
  register int dow;
#if defined(hpux)
  int  juldays[13];
       juldays[0]=0;juldays[1]=0;juldays[2]=31;juldays[3]=59;
       juldays[4]=90;juldays[5]=120;juldays[6]=151;juldays[7]=181;
       juldays[8]=212;juldays[9]=243;juldays[10]=273;juldays[11]=304;juldays[12]=334;
#else 
  int  juldays[13] = {0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
#endif

  dow = 7 +  year +  year/4 - year/100 + year/400 + juldays[month] + day;
  if( (month < 3) && ( leap_year(year) ) )   dow--;
  dow %= 7;
  return (dow); 
} /* weekday */

int
julianday( year, month, day)int year;int month;int day;
{
  register int doy;
#if defined(hpux)
  int  juldays[13];
       juldays[0]=0;juldays[1]=0;juldays[2]=31;juldays[3]=59;
       juldays[4]=90;juldays[5]=120;juldays[6]=151;juldays[7]=181;
       juldays[8]=212;juldays[9]=243;juldays[10]=273;juldays[11]=304;juldays[12]=334;
#else 
  int  juldays[13] = {0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
#endif  
    
  doy = juldays[month];
  if((month > 2) && (leap_year(year)) )  doy++;
  doy += day;
  return (doy);
} /* julianday */

int
check_int( an_int ) char *an_int;
{
  int  ii, leng;
  int  result=0;
  
  if( (an_int[0] == '-') || (an_int[0]== '+') ) an_int++;
  leng = strlen(an_int);
  for(ii=0;ii<leng;ii++) {
    if( !isdigit(an_int[ii]) )  result = 1;
  }
  return (result);
}
/* --------------------------- */
/*  0 OK , 1 NOT a real number */
int
check_real( a_real ) char *a_real;
{
  int  ii, leng;
  int  result=0;
  
  if( (a_real[0] == '-') || (a_real[0]== '+') ) a_real++;
  leng = strlen(a_real);
  for(ii=0;ii<leng;ii++) {
    if( (!isdigit(a_real[ii])) && (a_real[ii]!='e') && (a_real[ii]!='E') && 
        (a_real[ii]!='.')      && (a_real[ii]!='+') && (a_real[ii]!='-') )  result = 1;
  }
  return (result);
}

int
check_input_usymb(u_symb)char *u_symb;
{
  int  result=0;
  
  
  return (result);
}

/* <== This routine determine emperically where to split a character */
/*       string into two portions, one for numerical answer and the  */
/*       other for units */
/* ----------------------------------------------------------------- */
/* inputs : buf    : answer string  */
/* outputs: num    : the numerical part */
/*          num_p  : the numerical part in string */
/*          unit_p : the unit string */
/* num_p is used to calculate sig figs */
/* return :   0  empty string          */
/*            1  number without units  */
/*            2  empty number with units */
/*            3  number with units       */
/*                                       */
int  split_num_unit(buf,num,num_p,unit_p)
char *buf;double *num; char *num_p; char *unit_p;
{
  char    num_str[ANSWER_STRING_LENG], unit_str[ANSWER_STRING_LENG];
  char    base_str[ANSWER_STRING_LENG], exp_str[ANSWER_STRING_LENG];
  int     idx=0, errcode=0;
  int     len, ii, n_idx=0, b_idx=0, e_idx=0, u_idx=0;
  double  n_part, x_part, b_part, e_part, result;
  
  unit_str[0]=0;  /* initialize unit_str[] */
  len = strlen(buf);
  while( isspace(buf[idx]) ) { idx++; } /* ignore white spaces */
  num_str[n_idx]=0; /* initialize number string */
  if( buf[idx] == '+' || buf[idx] == '-' ) { 
  /* the first char is either '+' or '-' */
    num_str[n_idx++]= buf[idx++];
  }
  /* skip over alphabet char and those that are not '.' */
  while( (!isdigit( buf[idx] )) && buf[idx] != '.' )  {  idx++; }
  while( isdigit( buf[idx] ) || buf[idx] == '.' ) { /* collect decimal numbers */
    num_str[n_idx++]= buf[idx++];
  }
  num_str[n_idx] = 0; /* end the string with a '\0' */
  sprintf(num_p,"%s",num_str);   /* put the collected numerical string in num_p */
  /* skip over white spaces */
  while( isspace(buf[idx]) ) { idx++;  }
  if( buf[idx] == 'e' || buf[idx] == 'E' ) { /* optional e or E part */
    /* peek further one char to see if it belongs to one of the following */
    if( buf[idx+1] == '-' || buf[idx+1] == '+' || isspace(buf[idx+1]) || isdigit(buf[idx+1])) {
      num_str[n_idx++] = buf[idx++]; /* should be 'e' or 'E' */
      while( isspace(buf[idx]) ) {  idx++;  } /* skip over spaces */
      num_str[n_idx++] = buf[idx++]; /* the second char '-', '+' or digit */
      while( isdigit(buf[idx]) ) {   /* only integer is allowed, not '.' */
        num_str[n_idx++] = buf[idx++];
      }
    }
    num_str[n_idx] = 0;  /* terminiate the numerical string */
    while( isspace(buf[idx]) ) { idx++; }
    sscanf(num_str, "%lg", &result);  /* put the numerical value into a double variable */
    errcode = errcode | 1;
  } else if( buf[idx] == 'x' || buf[idx] == 'X') { /* optional x or X part */
    idx++; /* skip x or X */
    while( isspace(buf[idx]) ) { idx++; }
    
    e_part = 1.0;  /* default power */
    base_str[b_idx] = 0; /* initialize base_str[] */
    while( isdigit(buf[idx]) || buf[idx] == '.' ) { /* should start with a digit or . */
      base_str[b_idx++] = buf[idx++];
    }
    base_str[b_idx] = 0; /* terminate base_str[] */
    while( isspace(buf[idx]) ) { idx++; } /* skip over white spaces */
    if( buf[idx] == '^' ) {   /* power */
      idx++;
      exp_str[e_idx] = 0; /* initialize exp_str[] */
      while( isspace(buf[idx]) ) { idx++; }
      if( buf[idx] == '{'|| buf[idx] == '(' ) {  /* matching right bracket */
        idx++;
      }
      while( isspace(buf[idx]) ) { idx++; }
      if( isdigit(buf[idx]) || buf[idx] == '+' || buf[idx] == '-' )  {
        exp_str[e_idx] = 0;  /* initialize exp_str[], again */
        while( isdigit(buf[idx]) || buf[idx] == '.' || buf[idx] == '+' || buf[idx] == '-' ) {
          exp_str[e_idx++] = buf[idx++];
        }
        exp_str[e_idx] = 0;  /* terminate exp_str[] */
      }
      while( isspace(buf[idx]) ) { idx++; }
      if( buf[idx] == '}' || buf[idx] == ')' ) {
        idx++;
      }
      sscanf(exp_str, "%lg", &e_part);
    }
    if( strlen(base_str) > 0 ) {
      sscanf(base_str, "%lg", &b_part);
      x_part = pow(b_part, e_part);
    } else {
      x_part = 0.0;
    }
    if( strlen(num_str) > 0 ) {
      sscanf(num_str, "%lg", &n_part);
    } else {
      n_part=0.0;
    }
    result = n_part * x_part;
    errcode = errcode | 1;
  } else if ( buf[idx] == '^' ) {  /* number ^ */
    idx++;
    e_part = 1.0;
    while( isspace(buf[idx]) ) { idx++; }
    if( buf[idx] == '{'|| buf[idx] == '(' ) {  /* matching right bracket */
        idx++;
    }
    while( isspace(buf[idx]) ) { idx++; }
    if( isdigit(buf[idx]) || buf[idx] == '+' || buf[idx] == '-' )  {
        exp_str[e_idx] = 0;
        while( isdigit(buf[idx]) || buf[idx] == '.' || buf[idx] == '+' || buf[idx] == '-' ) {
          exp_str[e_idx++] = buf[idx++];
        }
        exp_str[e_idx] = 0;
    }
    while( isspace(buf[idx]) ) { idx++; }
    if( buf[idx] == '}' || buf[idx] == ')' ) {
        idx++;
    }
    sscanf(exp_str, "%lg", &e_part);
    sscanf(num_str, "%lg", &n_part);
    result = pow(n_part,e_part);
    errcode = errcode | 1;
  } else {  /* number unit */
    if( strlen(num_str) > 0 ) {
      sscanf(num_str, "%lg", &result);
      errcode = errcode | 1;
    }
  }
  
  if( idx < len ) { /* collect the rest as unit string */
    for(ii=idx;ii< len; ii++ ) {
      
      unit_str[u_idx++] = buf[ii];
    }
    unit_str[u_idx]=0; /* terminate unit_str[] */
    if(u_idx>0) {
      errcode = errcode | 2;
    }
  }
  if( strlen(num_str) > 0 ) {
    *num = result;
  } else {  /* empty number string */
    *num = 0.0;
  }
  sprintf(unit_p,"%s", unit_str);
  return (errcode);
}

char *
answers_string(mode, p)int mode;Problem_t  *p;
{
  char    *fmted_ans, lower[ANSWER_STRING_LENG], upper[ANSWER_STRING_LENG];
  char    *str_aa, *str_bb, *str_cc, *ans_str, *sub_ans_str, *tmp_str;
  int     len_aa=0, len_bb=0, len_cc=0,len_dd=0, total_len, num_answer;
  double  d_answer;
  
  if( p->ans_type == ANSWER_IS_SUBJECTIVE ) {
    char *a="Subjective Answer\n";
    ans_str = (char *)capa_malloc(strlen(a)+1,1);
    strcpy(ans_str,a);
    return (ans_str);
  }
  num_answer = calc_ansrange(p->ans_type,p->calc, p->answer, p->ans_fmt, p->tol_type,
  p->tolerance, lower, upper);
  
  if( p->ans_type == ANSWER_IS_FLOAT ) {
    fmted_ans = capa_malloc(SMALL_LINE_BUFFER,1);
    d_answer = (double)atof(p->answer);
    sprintf(fmted_ans,p->ans_fmt,d_answer);
  } else {
    fmted_ans = capa_malloc(strlen(p->answer)+2,1);
    strcpy(fmted_ans,p->answer);
  }
  
  len_aa = strlen(fmted_ans);
  if (lower != NULL ) len_bb = strlen(lower); else len_bb = 0;
  if (upper != NULL ) len_cc = strlen(upper); else len_cc = 0;
  if ( p->unit_str != NULL ) len_dd = strlen(p->unit_str); else len_dd = 0;

  switch(mode) {
    case TeX_MODE:
           if(num_answer==2)  {
             /* 16 is by adding up characters '  [,] \n\n' and others */
             str_aa = format_toTeX(fmted_ans);
             total_len = strlen(str_aa) + len_dd + 16;
             str_bb = format_toTeX(lower);
             total_len += strlen(str_bb);
             str_cc = format_toTeX(upper);
             total_len += strlen(str_cc);
             ans_str = (char *)capa_malloc(total_len,1);
             if(len_dd == 0 ) {  /* no unit_str */
               sprintf(ans_str," %s [%s,%s]\n\n", str_aa,str_bb,str_cc);
             } else {
               sprintf(ans_str," %s [%s,%s] $%s$\n\n", str_aa,str_bb,str_cc,p->unit_str);
             }
             capa_mfree((char *)str_aa); 
	     capa_mfree((char *)str_bb); 
	     capa_mfree((char *)str_cc);
           } else {  /* only one answer */
             if ( (p->ans_type == ANSWER_IS_INTEGER) || 
                  (p->ans_type == ANSWER_IS_FLOAT )) {
               str_bb = format_toTeX(lower);  
             } else { /* answer could be string, choice */
               str_bb = (char *)capa_malloc(strlen(lower)+MAX_BUFFER_SIZE,1);
	       if (p->verbatim == DO_VERBATIM)
		 sprintf(str_bb,"\\begin{verbatim}%s\\end{verbatim}",lower);
	       else
		 strcpy(str_bb,lower);
             }
             total_len = strlen(str_bb) + len_dd + 8;
               /* 4 is by adding up characters ' \\\n' plus four */
             ans_str = (char *)capa_malloc(sizeof(char),total_len);
             if(len_dd == 0 ) {  /* no unit_str */
               sprintf(ans_str," %s\n", str_bb);
             } else {
               sprintf(ans_str," %s $%s$\n", str_bb,p->unit_str);
             }
             capa_mfree((char *)str_bb);
           }
           break;
     case ASCII_MODE:
           total_len = len_aa + len_bb + len_cc + len_dd + 8;
           /* 8 is by adding up characters ' [,] \n\n' plus one */
           ans_str = (char *)capa_malloc(sizeof(char),total_len);
           if(num_answer==2)  {
             if(len_dd == 0 ) {  /* no unit_str */
               sprintf(ans_str,"%s [%s,%s]\n\n", fmted_ans,lower,upper);
             } else {
               sprintf(ans_str,"%s [%s,%s] %s\n\n", fmted_ans,lower,upper,p->unit_str);
             }
           } else {
             if(len_dd == 0 ) {  /* no unit_str */
               sprintf(ans_str,"%s\n\n", lower);
             } else {
               sprintf(ans_str,"%s %s\n\n", lower,p->unit_str);
             }
           }
	   break;
      case ANSWER_STRING_MODE:
           total_len = len_aa + len_bb + len_cc + len_dd + 8;
           /* 8 is by adding up characters ' [,] \n\n' plus one */
           ans_str = (char *)capa_malloc(sizeof(char),total_len);
           if(num_answer==2)  {
             if(len_dd == 0 ) {  /* no unit_str */
               sprintf(ans_str,"%s", fmted_ans);
             } else {
               sprintf(ans_str,"%s %s", fmted_ans,p->unit_str);
             }
           } else {
             if(len_dd == 0 ) {  /* no unit_str */
               sprintf(ans_str,"%s", lower);
             } else {
               sprintf(ans_str,"%s %s", lower,p->unit_str);
             }
           }
	   break;   
     case HTML_MODE: 
           if(num_answer==2)  { /* this indicates that answer should be either float or int
           */
             str_aa = format_toHTML(fmted_ans);
             total_len = strlen(str_aa) + len_dd + 8;
             str_bb = format_toHTML(lower);
             total_len += strlen(str_bb);
             str_cc = format_toHTML(upper);
             total_len += strlen(str_cc);
             /* 8 is by adding up characters ' [,] \n\n' plus one */
             ans_str = (char *)capa_malloc(sizeof(char),total_len);
             if(len_dd == 0 ) {  /* no unit_str */
               sprintf(ans_str,"%s [%s,%s]\n\n", str_aa,str_bb,str_cc);
             } else {
               sprintf(ans_str,"%s [%s,%s] %s\n\n", str_aa,str_bb,str_cc,p->unit_str);
             }
             capa_mfree((char *)str_aa); capa_mfree((char *)str_bb); capa_mfree((char
             *)str_cc);
           } else  {
             if ( (p->ans_type == ANSWER_IS_INTEGER) || 
                  (p->ans_type == ANSWER_IS_FLOAT )) {
               str_bb = format_toHTML(lower);  
             } else { /* answer could be string, choice */
               str_bb = (char *)capa_malloc(strlen(lower)+MAX_BUFFER_SIZE,1);
	       if (p->ans_type == ANSWER_IS_FORMULA || 1)
		 sprintf(str_bb,"<PRE>\n%s\n</PRE>",lower);
	       else
		 strcpy(str_bb,lower);
             }
             total_len = strlen(str_bb) + len_dd + 4;
               /* 4 is by adding up characters ' \n\n' plus one */
             ans_str = (char *)capa_malloc(sizeof(char),total_len);
             if(len_dd == 0 ) {  /* no unit_str */
               sprintf(ans_str,"%s\n\n", str_bb);
             } else {
               sprintf(ans_str,"%s %s\n\n", str_bb,p->unit_str);
             }
             capa_mfree((char *)str_bb);
           }
	   break;
  }
  capa_mfree(fmted_ans);

  if( p->ans_cnt > 1 ) {  
    AnswerInfo_t   *ai;
     for( ai = p->ans_list; ai; ai = ai->ans_next) {
       num_answer = calc_ansrange(ai->ans_type,ai->ans_calc, ai->ans_str, ai->ans_fmt, 
                                  ai->ans_tol_type,ai->ans_tol,lower,upper);
       if( ai->ans_type == ANSWER_IS_FLOAT ) {
 	  fmted_ans = capa_malloc(SMALL_LINE_BUFFER,1);
          d_answer = (double)atof(ai->ans_str);
          sprintf(fmted_ans,ai->ans_fmt,d_answer);
       } else {
          fmted_ans = capa_malloc(strlen(ai->ans_str)+2,1);
          strcpy(fmted_ans,ai->ans_str);
       }
       len_aa = strlen(fmted_ans);
       len_bb = strlen(lower);
       len_cc = strlen(upper);
       len_dd = strlen(ai->ans_unit_str);
       switch(mode) {
         case TeX_MODE:
               if(num_answer==2)  {
                /* 16 is by adding up characters '  [,] \n\n' and others */
             
                 str_aa = format_toTeX(fmted_ans);
                 total_len = strlen(str_aa) + len_dd + 16;
                 str_bb = format_toTeX(lower);
                 total_len += strlen(str_bb);
                 str_cc = format_toTeX(upper);
                 total_len += strlen(str_cc);
             
                 sub_ans_str = (char *)capa_malloc(sizeof(char),total_len);
             
                 if(len_dd == 0 ) {  /* no unit_str */
                   sprintf(sub_ans_str," %s [%s,%s]\n\n", str_aa,str_bb,str_cc);
                 } else {
                   sprintf(sub_ans_str," %s [%s,%s] $%s$\n\n",
                   str_aa,str_bb,str_cc,ai->ans_unit_str);
                 }
                 capa_mfree((char *)str_aa); capa_mfree((char *)str_bb); capa_mfree((char *)str_cc);
               } else {  /* only one answer */
                  if ( (ai->ans_type == ANSWER_IS_INTEGER) || 
                       (ai->ans_type == ANSWER_IS_FLOAT )) {
                    str_bb = format_toTeX(lower);  
                  } else { /* answer could be string, choice */
		    str_bb = (char *)capa_malloc(strlen(lower)+MAX_BUFFER_SIZE,1);
		    if (ai->ans_type == ANSWER_IS_FORMULA || 1)
		      sprintf(str_bb,"\\begin{verbatim}%s\\end{verbatim}",lower);
		    else
		      strcpy(str_bb,lower);
                  }
                  total_len = strlen(str_bb) + len_dd + 8;
                 /* 4 is by adding up characters ' \\\n' plus four */
                  sub_ans_str = (char *)capa_malloc(sizeof(char),total_len);
                  if(len_dd == 0 ) {  /* no unit_str */
                    sprintf(sub_ans_str," %s\n", str_bb);
                  } else {
                    sprintf(sub_ans_str," %s $%s$\n", str_bb,ai->ans_unit_str);
                  }
                  capa_mfree((char *)str_bb);
                }
                break;
          case ASCII_MODE:
                total_len = len_aa + len_bb + len_cc + len_dd + 8;
                /* 8 is by adding up characters ' [,] \n\n' plus one */
                sub_ans_str = (char *)capa_malloc(sizeof(char),total_len);
                if(num_answer==2)  {
                  if(len_dd == 0 ) {  /* no unit_str */
                    sprintf(sub_ans_str,"%s [%s,%s]\n\n", fmted_ans,lower,upper);
                  } else {
                    sprintf(sub_ans_str,"%s [%s,%s] %s\n\n",
                    fmted_ans,lower,upper,ai->ans_unit_str);
                  }
                } else {
                  if(len_dd == 0 ) {  /* no unit_str */
                    sprintf(sub_ans_str,"%s\n\n", lower);
                  } else {
                    sprintf(sub_ans_str,"%s %s\n\n", lower,ai->ans_unit_str);
                  }
                }
	        break;
	  case ANSWER_STRING_MODE:
                total_len = len_aa + len_bb + len_cc + len_dd + 8;
                /* 8 is by adding up characters ' [,] \n\n' plus one */
                sub_ans_str = (char *)capa_malloc(sizeof(char),total_len);
                if(num_answer==2)  {
                  if(len_dd == 0 ) {  /* no unit_str */
                    sprintf(sub_ans_str,", %s", fmted_ans);
                  } else {
                    sprintf(sub_ans_str,", %s %s", fmted_ans,ai->ans_unit_str);
                  }
                } else {
                  if(len_dd == 0 ) {  /* no unit_str */
                    sprintf(sub_ans_str,", %s", lower);
                  } else {
                    sprintf(sub_ans_str,", %s %s", lower,ai->ans_unit_str);
                  }
                }
	        break;      
          case HTML_MODE: 
                if(num_answer==2)  {
                  str_aa = format_toHTML(fmted_ans);
                  total_len = strlen(str_aa) + len_dd + 8;
                  str_bb = format_toHTML(lower);
                  total_len += strlen(str_bb);
                  str_cc = format_toHTML(upper);
                  total_len += strlen(str_cc);
                  /* 8 is by adding up characters ' [,] \n\n' plus one */
                  sub_ans_str = (char *)capa_malloc(sizeof(char),total_len);
                  if(len_dd == 0 ) {  /* no unit_str */
                    sprintf(sub_ans_str,"%s [%s,%s]\n\n", str_aa,str_bb,str_cc);
                  } else {
                    sprintf(sub_ans_str,"%s [%s,%s] %s\n\n",
                    str_aa,str_bb,str_cc,ai->ans_unit_str);
                  }
                  capa_mfree((char *)str_aa); capa_mfree((char *)str_bb); capa_mfree((char
                  *)str_cc);
                } else  {
                  if ( (ai->ans_type == ANSWER_IS_INTEGER) || 
                       (ai->ans_type == ANSWER_IS_FLOAT )) {
                    str_bb = format_toHTML(lower);  
                  } else { /* answer could be string, choice */
		    str_bb = (char *)capa_malloc(strlen(lower)+MAX_BUFFER_SIZE,1);
		    if (ai->ans_type == ANSWER_IS_FORMULA || 1)
		      sprintf(str_bb,"<PRE>\n%s\n</PRE>",lower);
		    else
		      strcpy(str_bb,lower);
                  }
                  total_len = strlen(str_bb) + len_dd + 4;
                   /* 4 is by adding up characters ' \n\n' plus one */
                  sub_ans_str = (char *)capa_malloc(sizeof(char),total_len);
                  if(len_dd == 0 ) {  /* no unit_str */
                    sprintf(sub_ans_str,"%s\n\n", str_bb);
                  } else {
                    sprintf(sub_ans_str,"%s %s\n\n", str_bb,ai->ans_unit_str);
                  }
                  capa_mfree((char *)str_bb);
                }
	        break;
       } /* end of switch */
       total_len =  strlen(ans_str);
       total_len += (strlen(sub_ans_str) + 1);
       tmp_str = (char *)capa_malloc(sizeof(char),total_len);
       strncpy(tmp_str, ans_str, strlen(ans_str)+1);
       strcat(tmp_str, sub_ans_str);
       capa_mfree(ans_str);  capa_mfree(sub_ans_str);
       capa_mfree(fmted_ans);
       ans_str = tmp_str;
     } /* end of for */
   } /* end of if */

   return (ans_str);  /* the calling routing needs to deallocate it */
}



/* ------------------------------ called from capalogin */
/*  impose stronger checking on the user input string *answer   */
/*  */

/* <== This routine checks user input string *ans against correct answer *s ==> */
int
capa_check_ans(ai,ans, error) AnswerInfo_t  *ai; char *ans; char **error;
{
  int      t;     /* ans_type  */
  char    *s;     /* ans_str   */
  int      c;     /* ans_calc  */
  int      tt;    /* ans_tol_type */
  double   to;    /* ans_tol      */
  int      su;    /* ans_sig_ub   */
  int      sl;    /* ans_sig_lb   */
  char    *fm;    /* ans_fmt      */
  char    *us;    /* ans_unit_str */
  Unit_t  *u_p;   /* ans_unit     */
  int      input_len, all_alphabet = 1, idx, ii, type;
  int      outcome, result = INCORRECT;
  int      sig, corr_len;
  int      choice[ANSWER_STRING_LENG];
  char     num_str[ANSWER_STRING_LENG], unit_str[ANSWER_STRING_LENG];
  char     fmted[ANSWER_STRING_LENG], correct[ANSWER_STRING_LENG], answer[ANSWER_STRING_LENG];
  double   n_part;
  double   given, target, ratio, fmted_target, target_u, target_l, scale=1.0;
  double   delta;
  
  t = ai->ans_type; s = ai->ans_str; c = ai->ans_calc;
  tt = ai->ans_tol_type; to = ai->ans_tol;
  su = ai->ans_sig_ub; sl = ai->ans_sig_lb;
  fm = ai->ans_fmt;
  us = ai->ans_unit_str; u_p = ai->ans_unit;
  
  switch(t) {
    case ANSWER_IS_INTEGER:
    case ANSWER_IS_FLOAT:
          {
            input_len = strlen(ans);
            all_alphabet = 1;
            for(idx=0;idx<input_len;idx++) {
              if( isdigit(ans[idx]) ) {
                all_alphabet = 0;
              }
            }
            if( !all_alphabet ) { /* answer string is not all alphabets */
              outcome = split_num_unit(ans,&n_part,num_str,unit_str);
              if( outcome > 1 ) { /* with both num and unit parts or only unit part */
                if( u_p != NULL ) {
		  if (UNIT_FAIL == (result = check_correct_unit(unit_str,u_p,&scale))) {
		    *error=strsave(unit_str);
		  }
                } else { /* what to do when no unit is specified but student entered a unit? */
                  result = UNIT_NOTNEEDED;
		  *error=strsave(unit_str);
                }
              } else {
                if( u_p != NULL ) {
                  result = NO_UNIT;
                }
              }
              if( (result != NO_UNIT) && (result != UNIT_FAIL) && ( result != UNIT_NOTNEEDED) ) {
                if( t == ANSWER_IS_FLOAT ) {
                  target = (double)atof(s); /* real number */
                } else {
                  target = (double)atol(s); /* Integer answer */
                }
                given = n_part * scale; /* convert the given answer into proper scale for units */
                sig = calc_sig( num_str );
                if( ((sig < sl   ) || (sig > su   )) && (sig!=0)) {
		  result = SIG_FAIL;
		  *error=capa_malloc(1,ANSWER_STRING_LENG);
		  sprintf(*error,"%d",sig);
                } else {
                  switch( tt ) { /* tolerence type */
                   case TOL_ABSOLUTE:
                         fmted_target = target; /* if answer type is integer */
                         if( type == ANSWER_IS_FLOAT) { /* format the exact answer as specified */
                           sprintf(fmted, fm, target); 
			   fmted_target = (double)atof(fmted);
                         }
                         to = fabs(to); /* tol must be positive */
                         if( c == CALC_FORMATED) {  
                            target_l = fmted_target - to;  target_u = fmted_target + to;
                         } else {
                            target_l = target - to;        target_u = target + to;
                         }
                         if( (given >= target_l) && (given <= target_u) ) {
			   result = APPROX_ANS;
                         } else { result = INCORRECT; }
                         break;
                   case TOL_PERCENTAGE:
                         if( target != 0.0 ) {
                           ratio = (double)(to / 100.0);
                           fmted_target = target; /* if answer type is integer */
                           if( type == ANSWER_IS_FLOAT) { /* format the exact answer as specified */
                             sprintf(fmted, fm, target); 
			     fmted_target = (double)atof(fmted);
                           }
			   delta = (double)fabs((double)ratio*target);
                           if( c == CALC_FORMATED) {
                             target_l = fmted_target - delta;   target_u = fmted_target + delta;
                           } else {
                             target_l = target - delta;         target_u = target + delta;
                           }
                         } else { target_l = target_u = target; }
                         if ( (given >= target_l) && (given <= target_u) ) {
			   result = APPROX_ANS;
	                 } else {  result = INCORRECT; }
		         break;
                  }
                } /* end sig check */
              } /* end if unit check */
            } else { /* user entered alphabets, but no number */
              result = WANTED_NUMERIC;
            } 
          }
          break;
    case ANSWER_IS_CHOICE:
          {
            corr_len = strlen(s); input_len = strlen(ans);
            if( corr_len == input_len ) {
              for(idx=0;idx<ANSWER_STRING_LENG;idx++) choice[idx] = 0;
              result = EXACT_ANS;
              for(ii=0;ii<corr_len; ii++) {
                idx = toupper(s[ii]) - 'A'; choice[idx] =  1;
              }
              for(ii=0;ii<input_len;ii++) {
                idx = toupper(ans[ii]) - 'A';
                if(choice[idx] != 1 )  result = INCORRECT;
              }
            } else { result = INCORRECT; }
            break;
          }
    case ANSWER_IS_STRING_CI:
          if (strcasecmp(ans, s)) { result = INCORRECT; } else { result = EXACT_ANS; }
          break;
    case ANSWER_IS_STRING_CS:
          if (strcmp(ans, s)) { result = INCORRECT; } else { result = EXACT_ANS; }
          break;
    case ANSWER_IS_FORMULA:
          result = check_formula_ans(s,ans,ai->ans_id_list,ai->ans_pts_list,tt,to);
          break;
    case ANSWER_IS_EXTERNAL: /* Not yet implemented */
          
          
          
          break;
  }
  return (result);
}

/* =============================================================================== */
/* calling sequence capa_check_answers() --> capa_check_answer()                   */
/*                                       --> capa_check_ans()                      */


/* How we check the correct answer against user input string */
/*
   If the correct answer is a number (either integer or real number)
       check if user input string consists of only alphabet characters
       use split_num_unit() heuristic function to split the input string into two parts
           numerical part and units part. 
         if the outcome contains units, check if the units is correct or not
         
*/


int
capa_check_answer(p, answer, error) Problem_t *p; char *answer; char **error;
{
  int     type; 
  char   *correct;
  char    input[ANSWER_STRING_LENG], unit_str[ANSWER_STRING_LENG];
  int     tol_type, calc_type;
  double  tol, n_part; 
  int     sig_l; 
  int     sig_u;
  char   *fmt;
  int     choice[ANSWER_STRING_LENG], ii, idx, corr_len, input_len;
  int     result = INCORRECT, sig, outcome, all_alphabet;
  char    fmted[FORMAT_STRING_LENG];
  double  given, target, ratio, fmted_target, target_u, target_l, scale=1.0;
  double  delta;
  
  type     = p->ans_type;
  correct  = p->answer;
  tol_type = p->tol_type;
  tol      = p->tolerance;
  sig_l    = p->sig_lbound;
  sig_u    = p->sig_ubound;
  fmt      = p->ans_fmt;
  calc_type = p->calc;
  unit_str[0]=0;
  
  switch(type) {
    case ANSWER_IS_INTEGER:
    case ANSWER_IS_FLOAT:
          {
            input_len = strlen(answer);
            all_alphabet = 1;
            for(idx=0;idx<input_len;idx++) {
              if( isdigit(answer[idx]) ) {
                all_alphabet = 0;
              }
            }
            if( !all_alphabet ) {
              outcome = split_num_unit(answer,&n_part,input,unit_str);
              if( outcome > 1 ) { /* with both num and unit parts or only unit part */
                if( p->ans_unit != NULL ) {
		  if ( UNIT_FAIL == ( result = check_correct_unit(unit_str,p->ans_unit,&scale) ) ) {
		    *error=strsave(unit_str);
		  }
                } else { /* what to do when no unit is specified but student entered a unit? */
                  result = UNIT_NOTNEEDED;
		  *error=strsave(unit_str);
                }
              } else {
                if( p->ans_unit != NULL ) {
                  result = NO_UNIT;
                }
              }
              if( (result != NO_UNIT) && (result != UNIT_FAIL) && ( result != UNIT_NOTNEEDED) ) {
                if( type == ANSWER_IS_FLOAT ) {
                  target = (double)atof(correct); /* real number */
                } else {
                  target = (double)atol(correct); /* Integer answer */
                }
                given = n_part * scale; /* convert the given answer into proper scale for units */
                sig = calc_sig( input );
                if( ((sig < sig_l) || (sig > sig_u)) && (sig!=0)) {
                  result = SIG_FAIL;
		  *error=capa_malloc(1,ANSWER_STRING_LENG);
		  sprintf(*error,"%d",sig);
                } else {
                  switch( tol_type ) {
                   case TOL_ABSOLUTE:
                         fmted_target = target; /* if answer type is integer */
                         if( type == ANSWER_IS_FLOAT) { /* format the exact answer as specified */
                           sprintf(fmted, fmt, target); 
			   fmted_target = (double)atof(fmted);
                         }
                         tol = fabs(tol); /* tol must be positive */
                         if( calc_type == CALC_FORMATED) {  
                            target_l = fmted_target - tol;  target_u = fmted_target + tol;
                         } else {
                            target_l = target - tol;        target_u = target + tol;
                         }
                         if( (given >= target_l) && (given <= target_u) ) {
			   result = APPROX_ANS;
                         } else { result = INCORRECT; }
                         break;
                   case TOL_PERCENTAGE:
                         if( target != 0.0 ) {
                           ratio = (double)(tol / 100.0);
                           fmted_target = target; /* if answer type is integer */
                           if( type == ANSWER_IS_FLOAT) { /* format the exact answer as specified */
                             sprintf(fmted, fmt, target); 
			     fmted_target = (double)atof(fmted);
                           }
                           delta = (double)fabs((double)ratio*target);
                           if( calc_type == CALC_FORMATED) {
                             target_l = fmted_target - delta;   target_u = fmted_target + delta;
                           } else {
                             target_l = target - delta;         target_u = target + delta;
                           }
                         } else { target_l = target_u = target; }
                         if ( (given >= target_l) && (given <= target_u) ) {
			   result = APPROX_ANS;
	                 } else {  result = INCORRECT; }
		         break;
                  }
                } /* end sig check */
              } /* end if unit check */
            } else { /* user entered alphabet, but no number */
              result = WANTED_NUMERIC;
            } 
          }
          break;
    case ANSWER_IS_CHOICE:
          {
            corr_len = strlen(correct); input_len = strlen(answer);
            if( corr_len == input_len ) {
              for(ii=0;ii<ANSWER_STRING_LENG;ii++) choice[ii] = 0;  result = EXACT_ANS;
              for(ii=0;ii<corr_len; ii++) {
                idx = toupper(correct[ii]) - 'A'; choice[idx] =  1;
              }
              for(ii=0;ii<input_len;ii++) {
                idx = toupper(answer[ii]) - 'A';
                if(choice[idx] != 1 )  result = INCORRECT;
              }
            } else { result = INCORRECT; }
            break;
          }
    case ANSWER_IS_STRING_CI:
          if (strcasecmp(answer, correct)) { result = INCORRECT; } else { result = EXACT_ANS; }
          break;
    case ANSWER_IS_STRING_CS:
          if (strcmp(answer, correct)) { result = INCORRECT; } else { result = EXACT_ANS; }
          break;
    case ANSWER_IS_FORMULA:
          result = check_formula_ans(correct,answer,p->id_list,p->pts_list,tol_type,tol);
          break;
    case ANSWER_IS_EXTERNAL: /* not yet implemented */
         /* we assume the external program is called through popen() */
         /* and the result will be given back as 0 or 1 to indicate the */
         /* given answer is correct or not */
         /* arguments are given to the program as */
         /* before running the program, check its existance first */
         /* should we specify a time out period in capa.config file? */
         /* set up a timer for this purpose */
         /* FILE *popen (const char *command,const char *type ); */

          break;
    
  }
  return (result);
}

/* -------------------------------------------------------------------------- */
/*   assumming the formula is *fml_str and the student input is *input_str             */
/*   according to the type of tolerance, we form the final formula as                  */
/*      absolute tolerance:  (*fml_str) - (*input_str)                                 */
/*      relative tolerance:  (*input_str) / (*fml_str)                                 */
int
check_formula_ans(fml_str,input_str,var_list,pts_list,tol_type,tol) 
char *fml_str;char *input_str;char *var_list;PointsList_t *pts_list;int tol_type; double tol;
{
  char         *check_fml_str;
  int           f_len, i_len, outcome, error_code;
  PointsList_t *pt, *next;
  double        formula_val, diff;
  
  f_len = strlen(fml_str);
  i_len = strlen(input_str);
  
  check_fml_str = (char *)capa_malloc((f_len + i_len + 16), sizeof(char));
  if( tol_type == TOL_ABSOLUTE ) {
    sprintf(check_fml_str,"abs((%s) - (%s))",fml_str, input_str);
  } else {
    sprintf(check_fml_str,"(%s) / (%s)",input_str,fml_str);
  }
  outcome = APPROX_ANS;
  for(pt= pts_list; pt!=NULL ; pt=next) {
    next=pt->pts_next;
    error_code = f_eval_formula(&formula_val,check_fml_str, var_list, pt->pts_str);
    if( ! error_code ) {
      if( tol_type == TOL_ABSOLUTE ) {
        diff = tol - formula_val;
        if( diff < 0.0 )  {
          outcome = INCORRECT;
        }
      } else {
        diff = abs(1.0 - formula_val) * 100.0 ;
        if( diff < tol ) {
          outcome = INCORRECT;
        }
      }
    } else {
      outcome = BAD_FORMULA;
      break;
    }
  }
  capa_mfree((char *)check_fml_str);
  
  return (outcome);
  
}
/* inputs: type :: answer type, calc_type :: input string format string tolerance type */
/* returns: lower upper */

int
calc_ansrange(type, calc_type, input, fmt, tol_type, tol, lower, upper)
int type;int calc_type;char *input;char *fmt;
int tol_type;double tol;char *lower;char *upper;
{
  int     result = 2, leng;
  char    fmted[ANSWER_STRING_LENG]; 
  double  target, fmted_target, ratio, target_l, target_u, tmp, delta;
  
  
  if( (type == ANSWER_IS_FORMULA) || 
      (type == ANSWER_IS_EXTERNAL ) ) {
      strcpy(lower, input);
      result = 1;
      return (result);
  } else {
    if( tol == 0.0 ) {  /* answer could be ANSWER_IS_FLOAT     ANSWER_IS_INTEGER, 
                                         ANSWER_IS_STRING_CI ANSWER_IS_STRING_CS 
                                         ANSWER_IS_CHOICE 
                      */
      result = 1;    /* only one answer */
      if( type == ANSWER_IS_FLOAT ) {
        target = (double)atof(input);
        sprintf(fmted, fmt, target);
        leng = strlen(fmted)+1;
      
        strcpy(lower, fmted);
      } else { /* could be integer, choice, string ci, string cs */
        strcpy(lower, input);
      }
    } else {  /* we have tolerence */
    
      target = (double)atof(input);
      switch( tol_type ) {
       case TOL_ABSOLUTE:
           fmted_target = target; /* if answer type is integer */
           if( type == ANSWER_IS_FLOAT) {   /* format the exact answer as specified */
               sprintf(fmted, fmt, target);
               fmted_target = (double)atof(fmted);
           }
           tol = fabs(tol); /* tol must be positive */
           if( calc_type == CALC_FORMATED) {  
               target_l = fmted_target - tol;  target_u = fmted_target + tol;
           } else {
               target_l = target - tol;        target_u = target + tol;
           }
           if(type == ANSWER_IS_FLOAT) {
             sprintf(fmted, fmt, target_l ); strcpy(lower, fmted);
             sprintf(fmted, fmt, target_u ); strcpy(upper, fmted);
           } else {
             sprintf(fmted, "%.15g", target_l ); strcpy(lower, fmted);
             sprintf(fmted, "%.15g", target_u ); strcpy(upper, fmted);
           }
           break;
       case TOL_PERCENTAGE:
           if( target != 0.0 ) {
              ratio = (double)(tol / 100.0);
              fmted_target = target; /* if answer type is integer */
              if( type == ANSWER_IS_FLOAT) { /* format the exact answer as specified */
                  sprintf(fmted, fmt, target); 
		  fmted_target = (double)atof(fmted);
              }
              delta = (double)fabs((double)ratio*target);
              if( calc_type == CALC_FORMATED) {
                  target_l = fmted_target - delta;   target_u = fmted_target + delta;
              } else {
                  target_l = target - delta;         target_u = target + delta;
              }
              if( target_l > target_u ) { tmp = target_u; target_u = target_l; target_l = tmp; }
              if(type == ANSWER_IS_FLOAT) {
                sprintf(fmted, fmt, target_l ); strcpy(lower, fmted);
                sprintf(fmted, fmt, target_u ); strcpy(upper, fmted);
              } else {
                sprintf(fmted, "%.15g", target_l ); strcpy(lower, fmted);
                sprintf(fmted, "%.15g", target_u ); strcpy(upper, fmted);
              }
           } else { strcpy(lower, "0.0"); strcpy(upper, "0.0"); result = 1;}
           break;
      }
    }
  }
  
  return (result);
}

/* Algorithms : check ALL units first */
/*              sig figs second  */
/*              numerical, string comparisons last */
/* result from capa_check_ans() could be */

/* New check answers routine checks the /AND and /OR group of answers */
/*   use array of char pointers  char **a */
int
capa_check_answers(p,answers,cnt,error) 
Problem_t *p; char **answers; int cnt; char **error;
{
  AnswerInfo_t  *ai;
  int            ii, done, result;
  int           *outcomes;
  char          **errormsg;
  
  errormsg=(char**)capa_malloc(cnt,sizeof(char*));
  if(p->ans_op == ANS_AND) {  /* ans /and ans */
    if( (cnt != p->ans_cnt) ) { return (ANS_CNT_NOT_MATCH); }
    if( cnt == 1 ) { return (capa_check_answer(p, answers[0], error)); } /* there is only one answer */
    outcomes = (int *)capa_malloc(sizeof(int),cnt);
    for(ii=0;ii<cnt;ii++) outcomes[ii]=0;  /* initialize the outcomes array */
    outcomes[0] = capa_check_answer(p, answers[0], &errormsg[0]);
#ifdef    COMMON_DBUG
    printf("CAPA_CHECK_ANSWER(%s,%s):: outcome[0]=%d\n", 
       p->answer,answers[0],outcomes[0]); 
#endif
    for(ii=1, ai = p->ans_list; ai; ii++,ai = ai->ans_next  ) {
       outcomes[ii] =  capa_check_ans(ai,answers[ii],&(errormsg[ii]));
#ifdef    COMMON_DBUG
    printf("CAPA_CHECK_ANS(%s,%s): outcome[%d]=%d\n", ai->ans_str,answers[ii],ii,outcomes[ii]); 
#endif
    }
    done = ii = 0;
    result = 0;
    while( !done ) { /* check if any of the outcome has failed on units */
      if( (outcomes[ii] == UNIT_FAIL) ||
          (outcomes[ii] == NO_UNIT)   ||
          (outcomes[ii] == UNIT_NOTNEEDED) ) {
         result = outcomes[ii];
	 if (result != NO_UNIT) { *error=strsave(errormsg[ii]); }
         done = 1;
      }
      ii++;
      if(ii==cnt) done = 1;
    }
    if( result == 0 ) { 
    /* check if any of the outcome has failed to be a numeric 
       or was a malformed equation */
      done = ii = 0;
      while( !done ) {
        if( outcomes[ii] == WANTED_NUMERIC || outcomes[ii] == BAD_FORMULA ) {
          result = outcomes[ii];
          done = 1;
        }
        ii++;
        if(ii==cnt) done = 1;
      }
    }
    if( result == 0 ) {/*check if any of the outcome has failed on sig figs*/
      done = ii = 0;
      while( !done ) {
        if( outcomes[ii] == SIG_FAIL ) {
          result = outcomes[ii];
	  *error = strsave(errormsg[ii]);
          done = 1;
        }
        ii++;
        if(ii==cnt) done = 1;
      }
    }
    if( result == 0 ) { /* check if any of the outcome is incorrect */
      done = ii = 0;
      while( !done ) {
        if( outcomes[ii] == INCORRECT ) {
          result = outcomes[ii];
          done = 1;
        }
        ii++;
        if(ii==cnt) done = 1;
      }
    }
    for (ii=0;ii<cnt;ii++) {
      if( (outcomes[ii] == UNIT_FAIL) ||
          (outcomes[ii] == SIG_FAIL)   ||
          (outcomes[ii] == UNIT_NOTNEEDED) ) {
	capa_mfree(errormsg[ii]);
      }
    }
    capa_mfree((char *)errormsg);
    capa_mfree((char *)outcomes);
    if( result == 0 ) {
      result = APPROX_ANS; /* all answers are correct */
    }
      
  } else { /* should be ANS_OR , user answer count should always be 1 */
     if( cnt != 1 ) { return (ANS_CNT_NOT_MATCH); }
     if( p->ans_cnt == 1 ) { return (capa_check_answer(p, answers[0], error)); }
     result = capa_check_answer(p, answers[0], error);
     ii = 1;  ai = p->ans_list;
     while( (ii<p->ans_cnt) && ( (result != EXACT_ANS) && (result != APPROX_ANS) ) ) {
       if((ii!=1)&&((result==UNIT_FAIL)||(result==SIG_FAIL)||(result==UNIT_NOTNEEDED))) {
	 capa_mfree((char*)error);
       }
       result =  capa_check_ans(ai,answers[0],error);
       ai = ai->ans_next; ii++;
     }
  }
  return (result);
}








/* ========================================================================= */
int  w_getclassdir(cpath_p, cown_p, class)
char **cpath_p; char **cown_p; char *class;
{
    FILE     *fp;
    char      filename[SMALL_LINE_BUFFER];
    char     *cname_p;
    int       done;
    char      c;
    
    sprintf(filename,"class.conf");
    if ((fp=fopen(filename,"r"))==NULL) {
      sprintf(filename,"../class.conf");
      if ((fp=fopen(filename,"r"))==NULL) {
        printf("Error: can't open %s\n",filename);
        exit (1);
      }
    }
    do {
      c_ignorewhite(fp);
      c = getc(fp); ungetc(c,fp);
      if( c != EOF ) {
        cname_p = c_getword(fp);
       *cpath_p = c_getword(fp);
       *cown_p  = c_getword(fp);
        throwaway_line(fp);
        if( ! strcasecmp(cname_p, class) ) {
          done = 1;
        } else {
          free(cname_p); free(*cpath_p); free(*cown_p);
          done = 0;
        }
      } else {
        done = 1;
      }
    } while ( ! done );
    fclose(fp);
    free(cname_p);
    return (1);
}

/* ----------------------------------------------------------------- */
/* read_capa_config gets a value out of the capa.config file 
   in the read resultant string all " are removed expect for \" occurances"
   in fact all case of \ then another character become just the last
   character

 inputs : key_word - a string that is searched for on the lefthand side
                     of an equals sign
 outputs : value - a char pointer that the value of the key_word as defined
                  in the config file is copied into
  return : -1 - unable to find or acces the capa.config file
            0 - the requested keyword was not found
            >0 - length of the string that was returned in value
*/
/* ----------------------------------------------------------------- */
int read_capa_config(key_word,value)
char *key_word;char *value;
{
  FILE     *fp;
  char      filename[SMALL_LINE_BUFFER];
  char      left[MAX_BUFFER_SIZE],right[MAX_BUFFER_SIZE],c;
  int       failed=0,done=0,num=0,result=-1,found=0,returnVal=0,i,j;
  
  sprintf(filename,"capa.config");
  if ((fp=fopen(filename,"r"))==NULL) {
    return (-1);
  }
  do {
    num = fscanf(fp,"%[^ \n\t#] = %[^\n]",left,right);
    if (num == 2) { result = strcasecmp(left,key_word); }
    if (result==0) { done=1; }
    if (num==EOF) { failed=1; }
    if (num!=2) {
      found=0;
      while(1) {
	c=fgetc(fp);
	if (found) {
	  if (c!='\n') {
	    ungetc(c,fp);
	    break;
	  }
	}
	if (c=='\n') found=1;
	if (((char)c)==((char)EOF)) {failed=1;break;}
      }
    }
  } while (!done && !failed); 

  fclose(fp);

  if (done) {
    trim_response_ws(right); /*get rid of leading and trailing spaces*/
    for(i=0,j=0;i<(strlen(right)+1);i++) {
      value[j]='\0';
      if (right[i] == '\\' && (i < (strlen(right))) ) {
	i++;value[j]=right[i];j++;
      } else if (right[i] != '\"' ) {
	value[j]=right[i];j++;
      }
    }
    value[j]='\0';
    returnVal=j;
  }
  return returnVal;
}

int capa_access(const char *pathname, int mode)
{
  pid_t euid,egid;
  struct stat status;

  euid=geteuid();
  egid=getegid();
  if ( -1 == stat(pathname,&status) ) { return -1; }
  /*printf("mode:%x F_OK:%x mode&F_OK:%x\n",mode,F_OK,(mode&F_OK));*/
  /*printf("st_mode:%x S_IFMT:%x st_mode&S_IFMT:%x\n",
    status.st_mode,S_IFMT,(status.st_mode&S_IFMT));*/
  if (!(status.st_mode & S_IFMT)) { return -1; }
  /*printf("euid: %d\t egid: %d\tstatus.st_uid: %d\tstatus.st_gid: %d\n",
    euid,egid,status.st_uid,status.st_gid);*/
  /*printf("mode:%x R_OK:%x mode&R_OK:%x\n",mode,R_OK,(mode&R_OK));*/
  /*printf("mode:%x W_OK:%x mode&W_OK:%x\n",mode,R_OK,(mode&R_OK));*/
  /*printf("mode:%x X_OK:%x mode&X_OK:%x\n",mode,R_OK,(mode&R_OK));*/
  if (euid==status.st_uid) {
    /*printf("here1\n");*/
    if ((mode & R_OK) && (!(status.st_mode & S_IRUSR))) { return -1; }
    if ((mode & W_OK) && (!(status.st_mode & S_IWUSR))) { return -1; }
    if ((mode & X_OK) && (!(status.st_mode & S_IXUSR))) { return -1; }
  } else {
    if (egid==status.st_gid) {
      /*printf("here2\n");*/
      if ((mode & R_OK) && (!(status.st_mode & S_IRGRP))) { return -1; }
      if ((mode & W_OK) && (!(status.st_mode & S_IWGRP))) { return -1; }
      if ((mode & X_OK) && (!(status.st_mode & S_IXGRP))) { return -1; }
    } else {
      /*printf("here3\n");*/
      if ((mode & R_OK) && (!(status.st_mode & S_IROTH))) { return -1; }
      if ((mode & W_OK) && (!(status.st_mode & S_IWOTH))) { return -1; }
      if ((mode & X_OK) && (!(status.st_mode & S_IXOTH))) { return -1; }
    }
  }
  return 0;
}

/*checks if the string is all whitespace*/
/*returns 0 if it isn't                 */
/*returns 1 if it is                    */
int is_all_ws(char* answer)
{
  int length,result=1,i;
  if (answer!=NULL) {
    length=strlen(answer);
    for(i=0;i<length;i++) {
      if (!isspace(answer[i])) {result=0;break;}
    }
  }
  return result;
}

void trim_response_ws(char* answer)
{
  char *temp;
  int i,j=0,length;
  length=strlen(answer);
  temp=capa_malloc(length+1,1);
  strcpy(temp,answer);
  for(i=0; i<length;i++) if (!(isspace(temp[i]))) break;
  for(j=length-1;j>=0;j--) if (!(isspace(temp[j]))) break;
  temp[++j]='\0';
  strcpy(answer,&temp[i]);
}

void throwaway_line(FILE* fp)
{
  int c;
  do {  
    c = getc(fp);
  } while ( (c != '\n') && (c != EOF) );
}

char* capa_get_seat(char* studentnum,char* seatfile)
{
  FILE* fp;
  T_student student;
  char *result,*defaultseatfile="seatingchart";
  char line[TMP_LINE_LENGTH],*lineend;
  int stuline=0,seatline=0;

  stuline = capa_get_student(studentnum,&student);
  if (stuline < 1 ) goto error;
  if (seatfile == NULL) seatfile=defaultseatfile;
  if ((fp=fopen(seatfile,"r"))==NULL) goto error;
  while( (stuline>seatline) && (fgets(line,TMP_LINE_LENGTH-1,fp)) ) seatline++;
  if (seatline< stuline) goto error;
  if ((lineend=index(line,' '))!=NULL) lineend='\0';
  result=capa_malloc(strlen(line)+1,1);
  strcpy(result,line);
  return result;

 error:
  result= capa_malloc(8,1);
  sprintf(result,"No Seat");
  return result;
  
}

void protect_log_string(char* log_string)
{
  int i,len=strlen(log_string);
  for(i=0;i<len;i++) {
    switch (log_string[i]) {
    case '\n': case '\t': case '\r':
      log_string[i]=' ';
      break;
    }
  }
}

int capa_get_login_time(char *student_number,int set,time_t *logintime) 
{
   FILE    *fp; 
   int      found,temp=0;
   char     filename[FILE_NAME_LENGTH],line[SMALL_LINE_BUFFER];
   
   *logintime=0;
   sprintf(filename,"records/access%d.log",set);

   if ((fp=fopen(filename,"r"))==NULL) return (-1);    
   found = 0;
   while( (!found) && ( fgets(line,SMALL_LINE_BUFFER-1,fp) != NULL) ) {
       if( !strncasecmp(line,student_number,MAX_STUDENT_NUMBER) )  found = 1;
   }
   fclose(fp);
   if (found) sscanf(rindex(line,':'),":%d",&temp);
   *logintime=(time_t)temp;
   return found;
}

/*----------------------------------------------------------*/
/* INPUT:  set             the X in logX.db                 */
/* OUTPUT: creates the accessX.log file if it doesn't exist */
/*         and inserts stunum:time(NULL) if stunum isn't    */
/*         in ther already.                                 */
/*                                                          */
/* RETURN:  -1    file error                                */
/*           0    already set                               */
/*           1    succesfully logged a login time           */
/*----------------------------------------------------------*/
int capa_set_login_time(char *student_number,int set)
{
   FILE    *fp; 
   int      errcode=0;
   int      found=0;
   char     filename[FILE_NAME_LENGTH],line[SMALL_LINE_BUFFER];
         
   sprintf(filename,"records/access%d.log",set);

   if ((fp=fopen(filename,"r"))!=NULL) {
     while( (!found) && ( fgets(line,SMALL_LINE_BUFFER-1,fp) != NULL) ) {
       if( !strncasecmp(line,student_number,MAX_STUDENT_NUMBER) )  found = 1;
     }
     fclose(fp);
   }

   if (found) return 0;

   sprintf(line,"%s:%d\n", student_number,(int)time(NULL));
   if ((fp=fopen(filename,"a+"))==NULL) return (-1);
   flockstream(fp);
   fseek(fp,0L,SEEK_END);
   if ( !fwrite((char *)line, strlen(line), 1, fp) ) {
     errcode = -1;
   } else {
     errcode = 1;
   }
   fflush(fp);
   funlockstream(fp);
   fclose(fp);
   return (errcode);
}
/* =||>|===================== End of capaCommon.c =====================|<||= */

