Annotation of capa/capa51/pProj/capalogin.c, revision 1.1.1.1

1.1       albertel    1: /* Copyright 1992-1997 Michigan State University, Board of Trustee  */
                      2: /* version 4.6 */
                      3: 
                      4: /* Jan 28  1997  I.T. */
                      5: /* July 23 1998  I.T. */
                      6: 
                      7: #ifdef NeXT
                      8: #include <stdlib.h>
                      9: #include <objc/zone.h>
                     10: #include <mach/mach.h>
                     11: #else
                     12: #include <malloc.h>
                     13: double atof();
                     14: #endif
                     15: 
                     16: #include <ctype.h>
                     17: 
                     18: #ifdef TRUE
                     19: #undef TRUE
                     20: #endif
                     21: #ifdef FALSE
                     22: #undef FALSE
                     23: #endif
                     24: 
                     25: #include <curses.h>
                     26: 
                     27: #if defined(__alpha) || defined(linux) 
                     28: 
                     29: #ifdef LOGIN_DBUG
                     30: 
                     31: #define NO_PIN
                     32: #define NO_DATE_CHECK
                     33: #define NO_DUP_CHECK
                     34: 
                     35: #endif
                     36: 
                     37: #include <curses.h>
                     38: #else
                     39: #if defined(__sun) || defined(hpux) || defined(AIX) || defined(IRIX)
                     40: #include <curses.h>  /* #include <stdio.h> */
                     41: #include <math.h>   /* MAXFLOAT */
                     42: 
                     43: #else
                     44: 
                     45: #include <bsd/curses.h>
                     46: 
                     47: #endif
                     48: #endif
                     49: 
                     50: #include <signal.h>
                     51: #include <time.h>
                     52: #include <math.h>
                     53: #include <string.h>
                     54: #include <unistd.h>
                     55: #include "capaToken.h"
                     56: #include "capaParser.h"
                     57: #include "capaCommon.h"
                     58: 
                     59: FILE      *dfp;
                     60: 
                     61: #define   TERM_SUMMARY    1
                     62: #define   EXAM_SUMMARY    2
                     63: #define   QUIZ_SUMMARY    3
                     64: 
                     65: #define   TRY_BOUND       99
                     66: 
                     67: 
                     68: 
                     69: 
                     70: #define   TYR_SET_MENU_MACRO(xxx)   {					\
                     71:  sprintf(aLine,"Total %d problems", num_questions); \
                     72:  if(xxx) { \
                     73:   mvaddstr(20,1,"Enter command  M,  A,  #,  T, or  X.");           mvaddstr(20,67,"COMMAND:"); \
                     74:   mvaddstr(21,1,"M=go to Main Menu  A=Answer    T=Time   RETURN=execute command"); \
                     75:  } else { \
                     76:   mvaddstr(20,1,"Enter command  M,  #,  T,  or  X.    ");          mvaddstr(20,67,"COMMAND:"); \
                     77:   mvaddstr(21,1,"M=go to Main Menu  T=Time               RETURN=execute command"); \
                     78:  }  \
                     79:  mvaddstr(22,1, "#=go to problem #  X=eXit CAPA"); \
                     80:  mvaddstr(23,1,aLine); }
                     81: 
                     82: 
                     83: #define   REVIEW_SET_MENU_MACRO()   {					\
                     84:  sprintf(aLine,"Total %d problems", num_questions); \
                     85:  mvaddstr(20,1,"Enter command  M,  #,  or  X.");              mvaddstr(20,67,"COMMAND:"); \
                     86:  mvaddstr(21,1,"M=go to Main Menu                    RETURN=execute command"); \
                     87:  mvaddstr(22,1,"#=go to problem #     X=eXit CAPA"); \
                     88:  mvaddstr(23,1,aLine); } 
                     89: 
                     90: #define   TYRSET_MENU( )   {					\
                     91:   mvaddstr(22,0,"Commands  :M = Main Menu   :7 = go to Problem 7            RETURN = Enter/Execute"); \
                     92:  }
                     93: 
                     94: 
                     95: #define   REVIEW_SET_MENU_MACRO()   {					\
                     96:  sprintf(aLine,"Total %d problems", num_questions); \
                     97:  mvaddstr(20,1,"Enter command  M,  #,  or  X.");              mvaddstr(20,67,"COMMAND:"); \
                     98:  mvaddstr(21,1,"M=go to Main Menu                    RETURN=execute command"); \
                     99:  mvaddstr(22,1,"#=go to problem #     X=eXit CAPA"); \
                    100:  mvaddstr(23,1,aLine); } 
                    101: 
                    102: 
                    103: #define DBUG_TSUMMARY    0
                    104: 
                    105: #define CLEAR()         clear(); refresh()
                    106: #define ADDCH(c)        addch(c); refresh()
                    107: #define CLRTOEOL()      clrtoeol(); refresh()
                    108: #define CR 13
                    109: #define LF 10
                    110: #define SCREEN_BUFFER_SIZE   2048
                    111: 
                    112: time_t   log_in_time, log_out_time;
                    113: char     in_t[32],    in_tty[32];
                    114: char     Orig_path[FILE_NAME_LENGTH], Exam_path[FILE_NAME_LENGTH], 
                    115:   Quiz_path[FILE_NAME_LENGTH];
                    116: int      Exam_set, Quiz_set;
                    117: int      g_inhibit_response;
                    118: int      g_delay; /* delay when logging out */
                    119: int      g_max_delay; /* max number of minutes to wait for input, kick_out()
                    120:                          after this much time */
                    121: /* Note: be careful to free entry answers */
                    122: 
                    123: /* ------------------------------------------------------------------------- */
                    124: /* WRITE OUTPUT (NICELY) TO THE SCREEN                                       */
                    125: /* ------------------------------------------------------------------------- */
                    126: void              /* RETURNS: (nothing)         */
                    127: wrap(str)         /* ARGUMENTS:                 */
                    128: char *str;        /*    Block of text to output */
                    129: {                 /* LOCAL VARIABLES:           */
                    130:    int y,x,len;   /*    Row,Col of screen       */
                    131:    int i,         /*    Next space              */
                    132:        j;         /*    Next char to print      */
                    133:   len=strlen(str);
                    134:   for (i=j=0; i<len; i++) {
                    135:     getyx(stdscr,y,x); 
                    136:     while (i<len && !isspace(str[i]))    i++;
                    137:     if (x+i-j > 78)  addch('\n');
                    138:     while (j<=i)     addch(str[j++]);
                    139:   }
                    140: }
                    141: int
                    142: total_lines(char *str)
                    143: {
                    144:   int  len,  lines_cnt=1;
                    145:   int  i, j, x=0;
                    146:   
                    147:   len=strlen(str);
                    148:   for(i=j=0;i<len;i++) {
                    149:     while (i<len && !isspace(str[i]))    i++;
                    150:     if (x+i-j > 78)  { lines_cnt++; x = 0; }
                    151:     while (j<=i)     { x++; if(str[j] == '\n') {lines_cnt++; x=0; }  j++; }
                    152:   }
                    153:   return (lines_cnt);
                    154: }
                    155: 
                    156: /* --------------------------------------------- */
                    157: /* */
                    158: #define    LINES_PER_SCREEN     20
                    159: 
                    160: int  display_prob_scr(char *str,int scr_idx)
                    161: {
                    162:   int  len, lines_cnt=0;
                    163:   int  i,j,y=0,x=0;
                    164:   int  break_pt, onscreen_pr;
                    165:   int  second_scr=0;
                    166: 
                    167:   if( str != NULL ) {
                    168:     lines_cnt = total_lines(str);
                    169:     if( lines_cnt > LINES_PER_SCREEN ) {
                    170:       second_scr = 1;
                    171:     } else {
                    172:       scr_idx = 1;
                    173:     }
                    174:     if( scr_idx == 1 ) {
                    175:       break_pt = LINES_PER_SCREEN + 1;
                    176:     } else { /* which line to break the problem text into two screens */
                    177:       if(lines_cnt>=40) { break_pt = LINES_PER_SCREEN; } else {
                    178:         if(lines_cnt==39) { break_pt = LINES_PER_SCREEN - 1; } else {
                    179:           if(lines_cnt==38) { break_pt = LINES_PER_SCREEN - 2; } else {
                    180:             break_pt = LINES_PER_SCREEN - 3;
                    181:           }
                    182:         }
                    183:       }
                    184:     }
                    185: 
                    186: #ifdef LOGIN_DBUG
                    187:    fprintf(dfp,"DISPLAY SCR IDX=%d total LineCnt=%d Line Break= %d:\n",scr_idx,lines_cnt,break_pt); fflush(dfp);
                    188: #endif
                    189: 
                    190:   /*  start to display the text on screen */
                    191: 
                    192:     lines_cnt = 1; x = y =0;
                    193:     len=strlen(str);
                    194: #ifdef LOGIN_DBUG
                    195:   fprintf(dfp,"SCR IDX=%d,leng=%d[[\n",scr_idx,len);
                    196:   fflush(dfp);
                    197: #endif  
                    198:     for(i=j=0;i<len;i++) {
                    199:       if( ( (scr_idx==1) && (lines_cnt < break_pt)) ||
                    200:           ((scr_idx==2) && (lines_cnt > break_pt) && (lines_cnt <= (break_pt+LINES_PER_SCREEN))) ) {
                    201:         getyx(stdscr,y,x);
                    202: 	/*	if (x2>=x) x=x2; else x=x2+80;*/
                    203:       }
                    204:       while (i<len && !isspace(str[i]))    i++;
                    205:       onscreen_pr = 0;
                    206: #ifdef LOGIN_DBUG         
                    207:       fprintf(dfp,"\n[NewWord line=%d,x=%d,i=%d,j=%d,y=%d]",lines_cnt,x,i,j,y);
                    208: #endif         
                    209:       if (x+i-j > 78)  { /* line break  */
                    210:          if( (scr_idx==1) && (lines_cnt < break_pt) ) {
                    211:            addch('\n'); onscreen_pr = 1;
                    212: #ifdef LOGIN_DBUG         
                    213:            fprintf(dfp,"\n[LineCnt=%d,x=%d,i=%d,j=%d]",lines_cnt,x,i,j);
                    214: #endif         
                    215:          }
                    216:          if( (scr_idx==2) && (lines_cnt > break_pt) && (lines_cnt <= (break_pt+LINES_PER_SCREEN)) ) {
                    217:          
                    218:            addch('\n'); onscreen_pr = 1;
                    219: #ifdef LOGIN_DBUG         
                    220:            fprintf(dfp,"\n[LineCnt=%d,x=%d,i=%d,j=%d]",lines_cnt,x,i,j);
                    221: #endif         
                    222:          }
                    223:          lines_cnt++;
                    224:          if(onscreen_pr == 0 ) {
                    225:            x=0;
                    226:          }
                    227:       }
                    228:       while (j<=i)     { /* display on screen */
                    229:          onscreen_pr = 0;
                    230:          if( (scr_idx==1) && (lines_cnt < break_pt) ) {
                    231:            addch(str[j]);   /* display that character */
                    232:            onscreen_pr = 1;
                    233: #ifdef LOGIN_DBUG            
                    234:            fprintf(dfp,"%c",str[j]);
                    235: #endif 
                    236:          } 
                    237:          if( (scr_idx==2) && (lines_cnt > break_pt) && (lines_cnt <= (break_pt+LINES_PER_SCREEN)) ) {
                    238: 
                    239:            addch(str[j]); onscreen_pr = 1;
                    240: #ifdef LOGIN_DBUG            
                    241:            fprintf(dfp,"%c",str[j]);
                    242: #endif 
                    243:          }
                    244:          if( str[j] == '\n' )  {
                    245:           
                    246: #ifdef LOGIN_DBUG         
                    247:          fprintf(dfp,"<LineCnt=%d>[j=%d]",lines_cnt,j);
                    248: #endif
                    249:            if(onscreen_pr == 0 ) {
                    250:              x = 0;
                    251:            }
                    252:            lines_cnt++; 
                    253:          }
                    254:          if(onscreen_pr == 0 ) {
                    255:            x++;
                    256:          }
                    257:          j++;
                    258:        }
                    259:     }
                    260: #ifdef LOGIN_DBUG
                    261:   fprintf(dfp,"\n]]"); fflush(dfp);
                    262: #endif
                    263: 
                    264:   }
                    265:   return (second_scr);
                    266: 
                    267: }
                    268: 
                    269: /* ------------------------------------------------------------------------- */
                    270: /* DISPLAY FAREWELL MESSAGE WHEN USER GOT KICKED OUT                         */
                    271: /* ------------------------------------------------------------------------- */
                    272: void               /* RETURNS: (nothing)      */
                    273: #ifdef __sun
                    274: kick_out(int sig)
                    275: #else
                    276: kick_out()
                    277: #endif
                    278: 
                    279: {                  /* LOCAL VARIABLES:        */
                    280:    FILE *fp;       /*    Goodbye file pointer */
                    281:    char  buf[255]; /*    Input buffer         */
                    282: 
                    283:    /* DISPLAY EXIT MESSAGE */
                    284:    CLEAR();
                    285:    if ((fp=fopen("goodbye.msg","r"))!=NULL) {
                    286:       while (fgets(buf,255,fp))
                    287:          addstr(buf);
                    288:       fclose(fp);
                    289:    }
                    290:    sprintf(buf, "This message will last for only %d seconds.",g_delay);
                    291:    mvaddstr(22,20,buf); refresh();
                    292:    sleep(g_delay);
                    293:    /* mypause(22,20); */
                    294: 
                    295:    /* CURSES RESTORATION */
                    296:    resetty(); endwin();
                    297:    exit(1);
                    298: }
                    299: 
                    300: 
                    301: /* ------------------------------------------------------------------------- */
                    302: /* GET INPUT (NICELY) FROM A PLACE ON THE SCREEN                             */
                    303: /* ------------------------------------------------------------------------- */
                    304: void                     /* RETURNS: (nothing)             */
                    305: get_input(y,x,str,inmax) /* ARGUMENTS:                     */
                    306: int   y,x;               /*   Row,Col of screen to start   */
                    307: char *str;               /*   String buffer to fill in     */
                    308: int   inmax;             /*   Maximum number of characters */
                    309: {                        /* LOCAL VARIABLES:               */
                    310:    int  i=0,cx,cy;       /*   Position in buffer           */
                    311:    char c;               /*   Input character              */
                    312:    
                    313:    if (y && x)  move(y,x);
                    314:    CLRTOEOL();
                    315:    cx = x; cy = y;
                    316: #if defined( __alpha) || defined(__sun)
                    317:    while (1) {
                    318:       alarm(g_max_delay*60);
                    319:       c=getch();
                    320:       if (c==10 || c==13)   break;
                    321:       else if (c==8 || c==16 || c==127) {
                    322:          if (i>0) {
                    323:             i--;  cx--;  echo(); move(cy,cx);
                    324:             delch();  insch(' '); refresh(); noecho();
                    325:          } else
                    326:          beep();
                    327:       } else if (i>=inmax) { beep(); } else {
                    328:          str[i++] = c; cx++;
                    329:          echo(); ADDCH(c); noecho();
                    330:       }
                    331:    }
                    332: #else  
                    333:    while (1) {
                    334:       alarm(g_max_delay*60);
                    335:       c=getch();
                    336:       if (c==10 || c==13) break;
                    337:       else if (c==8 || c==16 || c==127) {
                    338:          if (i>0) {
                    339:             i--;  printf("%c %c",8,8); refresh();
                    340:          } else   printf("%c",7);
                    341:       } else if (i>=inmax) { printf("%c",7);
                    342:       } else {
                    343:          str[i++] = c;  ADDCH(c);
                    344:       }
                    345:    }
                    346: #endif
                    347:    str[i]=0;
                    348: }
                    349: 
                    350: 
                    351: void                     /* RETURNS: (nothing)             */
                    352: get_xinput(y,x,str,inmax)/* ARGUMENTS:                     */
                    353: int   y,x;               /*   Row,Col of screen to start   */
                    354: char *str;               /*   String buffer to fill in     */
                    355: int   inmax;             /*   Maximum number of characters */
                    356: {                        /* LOCAL VARIABLES:               */
                    357:    int  i=0,cx,cy;       /*   Position in buffer           */
                    358:    char c;               /*   Input character              */
                    359: 
                    360:    
                    361:    for(i=0;i<inmax;i++) { move(y,x+i); ADDCH(' '); }
                    362:    i=0;
                    363:    if (y && x)  move(y,x);refresh();
                    364:    cx = x; cy = y;
                    365: #if defined( __alpha) || defined(__sun)
                    366:    while (1) {      
                    367:      alarm(g_max_delay*60);
                    368:      c=getch();
                    369:      if (c==10 || c==13)   break;
                    370:      else if (c==8 || c==16 || c==127) {
                    371:        if (i>0) {
                    372: 	 i--;  cx--;  echo(); move(cy,cx);
                    373: 	 delch();  insch(' '); refresh(); noecho();
                    374:        } else
                    375:          beep();
                    376:      } else if (i>=inmax) { beep(); } else {
                    377:        str[i++] = c; cx++;
                    378:        echo(); ADDCH(c); noecho();
                    379:      }
                    380:    }
                    381: #else  
                    382:    while (1) {
                    383:      alarm(g_max_delay*60);
                    384:      c=getch();
                    385:      if (c==10 || c==13) break;
                    386:      else if (c==8 || c==16 || c==127) {
                    387:        if (i>0) {
                    388: 	 i--;  printf("%c %c",8,8); refresh();
                    389:        } else   printf("%c",7);
                    390:      } else if (i>=inmax) { printf("%c",7);
                    391:      } else {
                    392:        str[i++] = c;  ADDCH(c);
                    393:      }
                    394:    }
                    395: #endif
                    396:    str[i]=0;
                    397: }
                    398: 
                    399: /*
                    400: void                     
                    401: input_pin(y,x,str,inmax) 
                    402: int   y,x;               
                    403: char *str;               
                    404: int   inmax;             
                    405: {                        
                    406:    int  i=0,cx,cy;       
                    407:    char c;               
                    408: 
                    409:    if (y && x)  move(y,x);
                    410:    cx = x; cy = y;
                    411:    CLRTOEOL();
                    412: #ifdef __alpha
                    413:    while (1) {
                    414:       c=getch();
                    415:       if (c==10 || c==13)   break;
                    416:       else if (c==8 || c==16 || c==127) {
                    417:          if (i>0) {
                    418:             i--;  cx--;  echo(); move(cy,cx);
                    419:             delch();  insch(' '); refresh(); noecho();
                    420:          } else
                    421:          beep();
                    422:       } else if (i>=inmax) { beep(); } else {
                    423:          str[i++] = c; cx++;
                    424:          echo(); ADDCH('*'); noecho();
                    425:       }
                    426:    }
                    427: #else  
                    428:    while (1) {
                    429:       c=getch();
                    430:       if (c==10 || c==13) break;
                    431:       else if (c==8 || c==16 || c==127) {
                    432:          if (i>0) {
                    433:             i--;  printf("%c %c",8,8); refresh();
                    434:          } else   printf("%c",7);
                    435:       } else if (i>=inmax) { printf("%c",7);
                    436:       } else {
                    437:          str[i++] = c;  ADDCH('*');
                    438:       }
                    439:    }
                    440: #endif
                    441:    str[i]=0;
                    442: }
                    443: */
                    444: 
                    445: /* ------------------------------------------------------------------------- */
                    446: /* PAUSE UNTIL USER HITS A KEY                                               */
                    447: /* ------------------------------------------------------------------------- */
                    448: void         /* RETURNS: (nothing)   */
                    449: mypause(y,x) /* ARGUMENTS:           */
                    450: int y,x;     /*    Row,Col of screen */
                    451: {            /* LOCAL VARIABLES:     */
                    452:    char c;   /*    Input character   */
                    453: 
                    454:    mvaddstr(y,x,"Press ENTER/RETURN to continue");
                    455:    get_input(y,x+30,&c,0);
                    456: }
                    457: 
                    458: /* ------------------------------------------------------------------------- */
                    459: /* DISPLAY FAREWELL MESSAGE WHEN USER LOGS OUT                               */
                    460: /* ------------------------------------------------------------------------- */
                    461: void               /* RETURNS: (nothing)      */
                    462: properly_logout(student_number)       /* ARGUMENTS:      */
                    463: char *student_number;
                    464: {                  /* LOCAL VARIABLES:        */
                    465:    FILE  *fp;       /*    Goodbye file pointer */
                    466:    char   buf[255]; /*    Input buffer         */
                    467:    char  *out_t;
                    468:    char   filename[FILE_NAME_LENGTH];
                    469:    
                    470:    /* DISPLAY EXIT MESSAGE */
                    471:    CLEAR();
                    472:    time(&log_out_time);
                    473:    out_t=ctime(&log_out_time);
                    474:    out_t[ strlen(out_t)-1 ]=0; /* Trash newline */
                    475: 
                    476:    sprintf(filename,"records/duration.db");
                    477:    if ((fp=fopen(filename,"a"))==NULL) {
                    478:       printf("Error: can't open duration file\n");
                    479:       return; 
                    480:    }
                    481:    flockstream(fp);
                    482:    fprintf(fp,"%s\t%s\t%s\t%s\n",student_number,in_tty,in_t,out_t);
                    483:    funlockstream(fp);
                    484:    fclose(fp);
                    485: 
                    486: 
                    487:    if ((fp=fopen("goodbye.msg","r"))!=NULL) {
                    488:       while (fgets(buf,255,fp))
                    489:          addstr(buf);
                    490:       fclose(fp);
                    491:    }
                    492:    /* mypause(22,20); */
                    493: #ifndef NO_DUP_CHECK
                    494:    logout_check(student_number);
                    495: #endif
                    496: 
                    497: #ifndef LOGIN_DBUG
                    498:    sprintf(buf, "This message will last for only %d seconds.",g_delay);
                    499:    mvaddstr(22,20,buf); refresh();
                    500:    sleep(g_delay);
                    501: #endif
                    502: 
                    503:    /* CURSES RESTORATION */
                    504:    resetty(); endwin();
                    505:    exit(1);
                    506: }
                    507: /* ------------------------------------------------------------------------- */
                    508: /* Forbid duplicate login                                                    */
                    509: /* ------------------------------------------------------------------------- */
                    510: void               /* RETURNS: (nothing)      */
                    511: dup_login_out()       /* ARGUMENTS:                */
                    512: {                  /* LOCAL VARIABLES:        */
                    513:    FILE *fp;       /*    Goodbye file pointer */
                    514:    char  buf[255]; /*    Input buffer         */
                    515: 
                    516:    /* DISPLAY EXIT MESSAGE */
                    517:    CLEAR();
                    518:    if ((fp=fopen("third-login.msg","r"))!=NULL) {
                    519:       while (fgets(buf,255,fp))   addstr(buf);
                    520:       fclose(fp);
                    521:    }
                    522:    /* mypause(22,20);*/
                    523:    /* CURSES RESTORATION */
                    524:    sprintf(buf, "This message will last for only %d seconds.",g_delay);
                    525:    mvaddstr(22,20,buf); refresh();
                    526:    sleep(g_delay);
                    527:    resetty(); endwin();
                    528:    exit(1);
                    529: }
                    530: 
                    531: void               /* RETURNS: (nothing)      */
                    532: dup_login_warning()/* ARGUMENTS:              */
                    533: {                  /* LOCAL VARIABLES:        */
                    534:    FILE *fp;       /*    Welcome file pointer */
                    535:    char  buf[255]; /*    Input buffer         */
                    536: 
                    537:    CLEAR();
                    538:    if ((fp=fopen("second-login.msg","r"))!=NULL) {
                    539:       while (fgets(buf,255,fp))
                    540:          addstr(buf);
                    541:       fclose(fp);
                    542:    }
                    543:    mypause(22,20);
                    544: }
                    545: 
                    546: /* ------------------------------------------------------------------------- */
                    547: /* ALLOW USER TO LOG IN                                                      */
                    548: /* ------------------------------------------------------------------------- */
                    549: char                          /* RETURNS: Student number                     */
                    550: *login(maxset,section)        /* ARGUMENTS:                                  */
                    551: int *maxset;                  /*    Set number                               */
                    552: int *section;                 /*    Section number                           */
                    553: {                             /* LOCAL VARIABLES:                            */
                    554:    char    *student_number;   /*    Student number                           */
                    555:    int      guess,            /*    User-entered PIN                         */
                    556:             login_set;        /*    Set for which PIN is valid               */
                    557:    int      login_section = 0;
                    558:    char     buff[20];         /*    Input buffer                             */ 
                    559:    T_entry  entry;
                    560:    time_t   curtime;          /*    Current time                             */
                    561:    int      leng;
                    562:    T_student student_data;
                    563: 
                    564: #define    D_S_NUM_Y    11
                    565: #define    D_S_NUM_X    13
                    566: 
                    567: #define    D_PIN_Y      (D_S_NUM_Y + 2)
                    568: #define    D_PIN_X      (D_S_NUM_X + 10)
                    569: #define    D_EXIT_Y     (D_S_NUM_Y + 5)
                    570: #define    D_EXIT_X     (D_S_NUM_X + 6)
                    571: #define    IN_S_NUM_Y   (D_S_NUM_Y)
                    572: #define    IN_S_NUM_X   (D_S_NUM_X + 16)
                    573: #define    IN_PIN_Y     (D_PIN_Y)
                    574: #define    IN_PIN_X     (D_PIN_X + 9)
                    575: #define    M_INVALID_Y  (IN_PIN_Y + 1)
                    576: #define    M_INVALID_X  (IN_PIN_X)
                    577: 
                    578:    student_number = (char *)malloc( (MAX_STUDENT_NUMBER+4)*sizeof(char));
                    579:    /* LOOP UNTIL WE ARE LEGALLY LOGGED IN */
                    580:    do {
                    581:       mvaddstr(D_S_NUM_Y,D_S_NUM_X,"STUDENT NUMBER: ");
                    582:       mvaddstr(D_PIN_Y,D_PIN_X,"CAPA ID: ");
                    583:       mvaddstr(D_EXIT_Y,D_EXIT_X,"To exit system, just hit ENTER/RETURN");
                    584: 
                    585: #ifndef  NO_PIN
                    586:       /* LOOP UNTIL WE HAVE A STUDENT NUMBER AND PIN */
                    587:       do {
                    588: #endif  /* NO_PIN */
                    589: 
                    590:          /* LOOP UNTIL A LEGAL STUDENT NUMBER IS ENTERED */
                    591:          do {
                    592:             get_input(IN_S_NUM_Y,IN_S_NUM_X,buff, MAX_STUDENT_NUMBER);
                    593: #ifdef __sun
                    594:             if (!strlen(buff))    kick_out(0);
                    595: #else
                    596:             if (!strlen(buff))    kick_out();
                    597: #endif
                    598:             sscanf(buff,"%s",student_number); leng = strlen(student_number);
                    599:          } while (leng < MAX_STUDENT_NUMBER);
                    600: 
                    601: #ifndef  NO_PIN
                    602:          get_input(IN_PIN_Y,IN_PIN_X,buff,MAX_PIN_CHAR);
                    603: #ifdef __sun
                    604:          if (!strlen(buff))       kick_out(0);
                    605: #else
                    606:          if (!strlen(buff))       kick_out();
                    607: #endif
                    608:          sscanf(buff,"%d",&guess);
                    609:       } while (guess<1);
                    610: #endif   /* NO_PIN */
                    611: 
                    612:       student_number[strlen(student_number)] = 0;
                    613:       /* VERIFY PIN */
                    614: 
                    615: #ifdef  NO_PIN
                    616: login_set = 1;
                    617: #else 
                    618: login_set = capa_PIN(student_number,999,guess);
                    619: #endif  /* No_PIN */
                    620:       
                    621: #ifdef LOGIN_DBUG
                    622:   fprintf(dfp,"LOGIN:S=%s,Guess=%04d,Actual Pin=%04d,set=%d\n",
                    623:    student_number,guess,capa_PIN(student_number,1, 0), login_set);
                    624:   fprintf(dfp," PIN=%04d,%04d,%04d,%04d,%04d\n",
                    625:    capa_PIN(student_number,1, 0), capa_PIN(student_number,2, 0),capa_PIN(student_number,3, 0),
                    626:    capa_PIN(student_number,4, 0), capa_PIN(student_number,5, 0));
                    627:   fflush(dfp);
                    628: #endif
                    629:       if (!login_set) {
                    630:          mvaddstr(M_INVALID_Y,M_INVALID_X,   "INVALID LOGIN  ");
                    631:       } else {
                    632:          if ( login_set > 99 )  {
                    633:            mvaddstr(M_INVALID_Y,M_INVALID_X, "INCORRECT PIN  ");  login_set = 0;
                    634:          }
                    635:          if ( capa_get_student(student_number,&student_data) == 0 ) {
                    636:             mvaddstr(M_INVALID_Y,M_INVALID_X,"NO SUCH STUDENT");  login_set=0;
                    637:          } else {
                    638:             login_section = student_data.s_sec;
                    639: #ifdef LOGIN_DBUG
                    640:   fprintf(dfp, " Student in section %d\n",login_section);fflush(dfp);
                    641: #endif
                    642:             time(&curtime);
                    643:             if( capa_check_date(CHECK_OPEN_DATE,student_number,
                    644: 				login_section,login_set) < 0 ) {
                    645:                mvaddstr(M_INVALID_Y,M_INVALID_X,"NOT YET OPEN!");  login_set=0;
                    646:             }
                    647:          }
                    648:       }
                    649:     } while ( !login_set );
                    650: #ifdef LOGIN_DBUG
                    651:   fprintf(dfp, "DEBUG:%s Access granted through set %d section %d\n",
                    652:     student_number, login_set, login_section);  fflush(dfp);
                    653: #endif
                    654: #ifndef NO_DUP_CHECK
                    655:     switch( login_check(student_number))  {
                    656:       case 0:
                    657:          mvaddstr(M_INVALID_Y,M_INVALID_X,"CANNOT LOGIN");  dup_login_out();
                    658:          break;
                    659:       case 1:
                    660:          mvaddstr(M_INVALID_Y,M_INVALID_X,"FIRST TIME LOGIN");
                    661:          break;
                    662:       case 2:
                    663:          mvaddstr(M_INVALID_Y,M_INVALID_X,"SECOND TIME LOGIN"); dup_login_warning( );
                    664:          break;
                    665:       case -1:
                    666: #ifdef __sun
                    667:         mvaddstr(M_INVALID_Y,M_INVALID_X,"FILE ERROR"); kick_out(0);
                    668: #else
                    669:         mvaddstr(M_INVALID_Y,M_INVALID_X,"FILE ERROR"); kick_out();
                    670: #endif
                    671:          break;
                    672:     }
                    673: #endif /* NO_DUP_CHECK */
                    674:    capa_get_entry(&entry,student_number,login_set);
                    675:    (*maxset) = login_set;
                    676:    (*section) = login_section;
                    677:    capa_mfree(entry.answers);
                    678:    capa_mfree(entry.tries);
                    679:    return (student_number);
                    680: }
                    681: 
                    682: /* ------------------------------------------------------------------------- */
                    683: /* LOG ANSWERS TO A FILE WITH TIMESTAMP                                      */
                    684: /* ------------------------------------------------------------------------- */
                    685: int                                                /* RETURNS: error code    */
                    686: log_attempt(student_number,set,section,log_string) /* ARGUMENTS:             */
                    687: char     student_number[MAX_STUDENT_NUMBER+1];     /*   Student number       */
                    688: int   set;                                         /*   Set number           */
                    689: int   section;                                     /*   Section number       */
                    690: char *log_string;                                  /*   Answer string to log */
                    691: {                                                  /* LOCAL VARIABLES:       */
                    692:    char   filename[FILE_NAME_LENGTH],              /*   Log filename buffer  */
                    693:          *ct;                                      /*   Current time string  */
                    694:    FILE  *fp;                                      /*   Log file pointer     */
                    695:    time_t t;                                       /*   Timestamp for log    */
                    696: 
                    697:    /* OPEN LOG FILE */
                    698: 
                    699:    sprintf(filename,"records/log%d.db",set);
                    700:    if ((fp=fopen(filename,"a"))==NULL) {
                    701:       printf("Error: can't open log file\n");
                    702:       return -1; 
                    703:    }
                    704: 
                    705:    /* CREATE LOG ENTRY */
                    706:    time(&t);
                    707:    ct=ctime(&t);
                    708:    ct[ strlen(ct)-1 ]=0; /* Trash newline */
                    709:    fprintf(fp,"%s %s %s\n",student_number,ct,log_string); fflush(fp);
                    710:    fclose(fp);
                    711:    return 0;
                    712: }
                    713: 
                    714: int  log_submissions(student_number,set,log_string)
                    715: char  student_number[MAX_STUDENT_NUMBER+1];     
                    716: int   set;  
                    717: char *log_string;                                
                    718: {                                                  
                    719:    char   filename[FILE_NAME_LENGTH], timeStr[FILE_NAME_LENGTH],buf2[MAX_BUFFER_SIZE];
                    720:    FILE  *fp;                                     
                    721:    time_t t;            
                    722:    struct tm     *tmtime;
                    723:    int do_log_submissions=1,result;
                    724:    char buf[MAX_BUFFER_SIZE];
                    725: 
                    726:    result=read_capa_config("do_log_submissions",buf);
                    727:    if (result != 0 && result != -1) {
                    728:      if (strcasecmp(buf2,"no")==0) {
                    729:        do_log_submissions=0;
                    730:      } 
                    731:    }
                    732:    if (!do_log_submissions) return 0;
                    733: 
                    734:    sprintf(filename,"records/submissions%d.db",set);
                    735:    if ((fp=fopen(filename,"a"))==NULL) {
                    736:      return (-1);
                    737:    }
                    738: 
                    739:    /* CREATE LOG ENTRY */
                    740:    time(&t);
                    741:    tmtime=localtime(&t);
                    742:    strftime(timeStr,FILE_NAME_LENGTH,"%d/%m %X",tmtime);
                    743:    /*ct[ strlen(ct)-1 ]=0;*/ /* Trash newline */
                    744:    protect_log_string(log_string);
                    745:    fprintf(fp,"%s\t%s\t%s\n",student_number,timeStr,log_string); fflush(fp);
                    746:    fclose(fp);
                    747:    return (0);
                    748: }
                    749: 
                    750: #define   C_FORWARD    1
                    751: #define   C_EXIT       2
                    752: #define   C_MENU       3
                    753: #define   C_HINT       4
                    754: #define   C_EXPLAIN    5
                    755: #define   C_ANSWER     6
                    756: #define   C_JUMP       7
                    757: #define   C_DONTCARE   8
                    758: #define   C_BACKWARD   9
                    759: #define   C_TIME       10
                    760: #define   C_NEXTSCR    11
                    761: #define   C_PREVSCR    12
                    762: #define   C_SUBJANS    13
                    763: 
                    764: /* ------------------------------------------------------------------------- */
                    765: /* DISPLAY SUMMARY OF SCORES FOR THE TERM                                    */
                    766: /* ------------------------------------------------------------------------- */
                    767: void                                     /* RETURNS: (nothing)          */
                    768: term_summary(student_number,set,section,type) /* ARGUMENTS:             */
                    769: char  *student_number;                   /*    Student Number           */
                    770: int    set;                              /*    Set number               */
                    771: int   *section;                          /*    Section Number           */
                    772: int    type;
                    773: {                                        /* LOCAL VARIABLES:            */
                    774:    int      set_idx,                     /*    Set counter              */
                    775:             i,                           /*    Question counter         */
                    776:             tmp,                         /*    Question correct flag    */
                    777:             set_score,                   /*    Score on a set           */
                    778:             term_score=0,                /*    Total points received    */
                    779:             term_total=0,                /*    Total points possible    */
                    780:             result;
                    781:    T_entry  entry;                       /*    Database entry for a set */
                    782:    char     buf[MAX_BUFFER_SIZE], buf2[MAX_BUFFER_SIZE];
                    783:    T_header header;                      /*    Problem set header       */
                    784:    int      topset=1,                    /*    First displayed set      */
                    785:             bottomset,                   /*    Last displayed set       */
                    786:             done=0,                      /*    Done flag                */
                    787:             line, col;
                    788:    int      probs_in_set[MAX_BUFFER_SIZE],/*    # problem set questions  */
                    789:             start_at[MAX_BUFFER_SIZE],
                    790:             valid_wgt[SMALL_LINE_BUFFER],
                    791:             a_valid_wgt,set_start_line,
                    792: 	    usr_command,inhibit_response;
                    793: 
                    794:    /* CALCULATE TERM TOTALS */
                    795:   start_at[0] = -2;
                    796:   probs_in_set[0]= 0;
                    797:   for (set_idx=1; set_idx<=set; set_idx++) {
                    798:     if (capa_get_header(&header,set_idx))  return;
                    799:     capa_get_entry(&entry,student_number,set_idx);
                    800:     sscanf(header.num_questions,"%d", &(probs_in_set[set_idx]) );
                    801:     start_at[set_idx] = start_at[set_idx-1]+2*(1+probs_in_set[set_idx-1]/50);
                    802:     if ((start_at[set_idx]%12)+2*(1+probs_in_set[set_idx]/50) > 12)
                    803:          start_at[set_idx] = 12*(1+start_at[set_idx]/12);
                    804:     valid_wgt[set_idx] = 0;
                    805:     for (i=0; i<probs_in_set[set_idx]; i++) {
                    806:       valid_wgt[set_idx] +=  (header.weight[i] - '0');
                    807:       if((entry.answers[i]=='Y') || (entry.answers[i]=='y'))  
                    808: 	term_score += (header.weight[i]-'0');
                    809:       if((entry.answers[i]=='E') || (entry.answers[i]=='e'))  
                    810: 	valid_wgt[set_idx] -= (header.weight[i] - '0');
                    811:       if((entry.answers[i]>='0') && (entry.answers[i]<='9'))  
                    812: 	term_score += (entry.answers[i] - '0');
                    813:     }
                    814:     term_total += valid_wgt[set_idx];
                    815:     capa_mfree(header.weight);
                    816:     capa_mfree(header.partial_credit);
                    817:     capa_mfree(entry.answers);
                    818:     capa_mfree(entry.tries);
                    819:   }
                    820: 
                    821:    /* FIND TOPSET */
                    822:    line = 12*(start_at[set]/12);      /* Top line # of last screen */
                    823:    for (topset=set; topset>1 && start_at[topset-1]>=line; topset--);
                    824: 
                    825:    /* SHOW HEADER */
                    826:    CLEAR();
                    827:    switch(type) {
                    828:      case TERM_SUMMARY:    mvaddstr(1,30,"TERM SUMMARY"); break;
                    829:      case EXAM_SUMMARY:    mvaddstr(1,30,"EXAM SUMMARY"); break;
                    830:      case QUIZ_SUMMARY:    mvaddstr(1,30,"QUIZ SUMMARY"); break;
                    831:    }
                    832:    mvaddstr(3,22,"         1         2         3         4         5");
                    833:    mvaddstr(4,22,"12345678901234567890123456789012345678901234567890");
                    834: 
                    835:    /* DISPLAY COMMAND MENU */
                    836:    mvaddstr(21,1,"Enter a command from the list below and press ENTER/RETURN    COMMAND:");
                    837:    mvaddstr(22,1,"M =Go to main menu  N =Next Page  P =Prev Page");
                    838:    /* mvaddstr(22,1,"X =eXit M =Go to main menu  N =Next Page  P =Prev Page"); */
                    839:    refresh();
                    840: 
                    841:    /* SHOW TOTALS */
                    842:    /* if capalogin_show_summary_score is set to none don't show it */
                    843:    sprintf(buf,"%d sets, total=%3d/%3d (%d%%)", set, term_score, term_total,
                    844:       100*term_score/term_total);
                    845:    result=read_capa_config("capalogin_show_summary_score",buf2);
                    846:    if (result != 0 && result != -1) {
                    847:      if (strcasecmp(buf2,"none")==0) {
                    848:      } else {
                    849:        mvaddstr(19,1,buf);
                    850:      }
                    851:    } else {
                    852:      mvaddstr(19,1,buf);
                    853:    }
                    854: 
                    855:    /* LOOP UNTIL DONE */
                    856:   while (!done) {
                    857:     /* PRINT 1 LINE SUMMARY PER SET */
                    858:     line=5;
                    859:     for (set_idx=topset; set_idx<=set; set_idx++) {
                    860:       /* don't show summary for set if inhibit response is set*/
                    861:       inhibit_response=capa_check_option(OPTION_INHIBIT_RESPONSE,set_idx,*section);
                    862:       if (inhibit_response > 0) continue;
                    863: 
                    864:       set_score=0;
                    865:       set_start_line=line;
                    866:     /* Stop if not enough lines to summarize set */
                    867:       if (line+2*(probs_in_set[set_idx]/50)>16)   break;
                    868:       capa_get_header(&header,set_idx);
                    869:       capa_get_entry(&entry,student_number,set_idx);
                    870:       a_valid_wgt = 0;
                    871:        for (i=0, col=0; i<probs_in_set[set_idx]; i++) {
                    872:          tmp=0; a_valid_wgt += (header.weight[i] - '0');
                    873:          move(line,  22+col); addch(entry.answers[i]);
                    874:          move(line+1,22+col); addch(header.weight[i]);
                    875:          switch(entry.answers[i]) {
                    876:            case 'Y': tmp=header.weight[i] -'0'; break; /* Answer correct */
                    877:            case 'y': tmp=header.weight[i] -'0'; break; /* Grading correct */
                    878:            case '-': break;        /* Not answered    */
                    879:            case 'N': break;        /* Answer incorrect */
                    880:            case 'n': break;        /* Grading incorrect */
                    881:            case 'e': a_valid_wgt -= (header.weight[i] - '0'); break;  /* Excuse    */
                    882:            case 'E': a_valid_wgt -= (header.weight[i] - '0'); break;  /* Excuse    */
                    883:            default : if( entry.answers[i] >= '0' && entry.answers[i] <= '9' ) {
                    884:                        tmp = entry.answers[i] - '0';
                    885:                      }
                    886:                      break;
                    887:          }
                    888:          set_score  += tmp; col++;
                    889:          if (!((i+1)%50)) { line += 2; col = 0; }
                    890:        }
                    891:        capa_mfree(header.weight);
                    892:        capa_mfree(header.partial_credit);
                    893:        capa_mfree(entry.answers);
                    894:        capa_mfree(entry.tries);
                    895:        move(line, 22+col);   CLRTOEOL();
                    896:        move(line+1, 22+col); CLRTOEOL();
                    897:        if(a_valid_wgt == 0) {
                    898:          set_score=0;
                    899:          sprintf(buf,"%3d:%3d/%3d(%3d%%)  ",set_idx,set_score,a_valid_wgt,set_score);
                    900:          mvaddstr(set_start_line,1,buf);
                    901:        } else {
                    902:          sprintf(buf,"%3d:%3d/%3d(%3d%%)  ",set_idx,set_score,a_valid_wgt,100*set_score/a_valid_wgt);
                    903:          mvaddstr(set_start_line,1,buf);
                    904:        }
                    905:        line += 2;
                    906:     }
                    907:     bottomset=set_idx-1;
                    908: 
                    909:       /* Blank out any extra lines */
                    910:     if (line < 16) {
                    911:      for (set_idx=line; set_idx<=16; set_idx++) {
                    912:        move(set_idx,1);
                    913:        CLRTOEOL();
                    914:      }
                    915:     }
                    916: 
                    917:       /* PROCESS USER COMMAND */
                    918:       get_input(21,72,buf,1);
                    919:       if(!strlen(buf)) { usr_command = C_FORWARD; } else {
                    920:         
                    921:           switch(toupper(buf[0])) {
                    922:            /* case 'X': usr_command=C_EXIT;    break; */
                    923:            case 'M': usr_command=C_MENU;    break;
                    924: 	   case 'P': usr_command=C_BACKWARD; break;
                    925:            default : usr_command=C_FORWARD;    break;
                    926:           }
                    927:       }
                    928: 
                    929:       
                    930:       switch(usr_command) {
                    931:       case C_DONTCARE: break;
                    932:       case C_FORWARD: /* Forwards */
                    933:                 if (bottomset<set) { topset=bottomset+1; } else { done=1; }
                    934:                 break;
                    935:       
                    936:       case C_BACKWARD: /* Backwards */
                    937:                 if (topset<2) break;
                    938:                 line = 12*(start_at[topset-1]/12); /* Top line # of prev screen */
                    939:                 for (; topset>1 && start_at[topset-1]>=line; topset--);
                    940:                 break;
                    941: 
                    942:       case C_MENU: /* Menu */
                    943:                 done=1;
                    944:                 break;
                    945:       case C_EXIT: /* Exit */
                    946:                 properly_logout(student_number);
                    947:                 break;
                    948:       default:  /* Invalid command */
                    949:                 break;
                    950:       }
                    951:    }
                    952: }
                    953: 
                    954: void
                    955: display_hint(char *h)
                    956: {
                    957: 
                    958:   CLEAR();
                    959: 
                    960:   wrap(h);
                    961:   mypause(22,20);
                    962: }
                    963: 
                    964: #define   A_ROW    20
                    965: #define   S_ROW    21
                    966: #define   O_ROW    22
                    967: #define   X_ROW    23
                    968: 
                    969: #define   A_COL    14
                    970: #define   S_COL    46
                    971: #define   H_COL    24
                    972: #define   E_COL    39
                    973: #define   X_COL    8
                    974: #define   R_COL    57
                    975: #define   U_ANS_CHAR  32
                    976: 
                    977: /* =============================================================================
                    978: 0001234567890123456789012345678901234567890123456789012345678901234567890123456789
                    979: A
                    980: S1OPTION/ANSWER 12345678901234 -----            *Unanswered
                    981: O2Options :M = Main Menu  :7 = go to #7  :N = Next screen  RETURN = Enter/Execute
                    982: X3        :X = eXit       :H = Show Hint :E = Explain      RETURN = Next Problem
                    983:   0123456789012345678901234567890123456789012345678901234567890
                    984:           ^     ^         ^              ^      ^          ^
                    985:           X     A         H              E      S          R
                    986: */
                    987: int  show_prior_response(Problem_t *p,int hgr,int prev_ans,int tried,int *allow_h)
                    988: {
                    989:   char     *c_answer_str, tmp_str[MAX_BUFFER_SIZE];
                    990:   char     *response="Incorrect",*answered="Answered";
                    991:   int       can_answer;
                    992:   
                    993:   if( hgr == '0' || p->ans_type==ANSWER_IS_SUBJECTIVE) {
                    994:     switch(prev_ans) {
                    995:       case 'Y': can_answer=NAY; *allow_h=1;
                    996:                 c_answer_str = answers_string(ANSWER_STRING_MODE,p);
                    997:                 move(A_ROW,A_COL); clrtoeol();  
                    998:                 mvaddstr(A_ROW,A_COL,c_answer_str); capa_mfree(c_answer_str);
                    999:                 move(S_ROW,S_COL); clrtoeol();
                   1000:                 mvaddstr(S_ROW,S_COL,"**Correct              "); break;
                   1001:       case 'y': can_answer=NAY; *allow_h=1;
                   1002:                 c_answer_str = answers_string(ANSWER_STRING_MODE,p);
                   1003:                 move(A_ROW,A_COL); clrtoeol();
                   1004:                 mvaddstr(A_ROW,A_COL,c_answer_str); capa_mfree(c_answer_str);
                   1005:                 move(S_ROW,S_COL); clrtoeol();
                   1006:                 mvaddstr(S_ROW,S_COL,"*Hand-graded Correct      "); break;
                   1007:       case '-': can_answer=YAK; move(S_ROW,S_COL); clrtoeol();
                   1008:                 mvaddstr(S_ROW,S_COL,"*Unanswered               "); break;
                   1009:       case 'E': can_answer=NAY; move(S_ROW,S_COL); clrtoeol();
                   1010:                 mvaddstr(S_ROW,S_COL,"*Excused                  "); break;
                   1011:       case 'e': can_answer=NAY; move(S_ROW,S_COL); clrtoeol();
                   1012:                 mvaddstr(S_ROW,S_COL,"*Excused                  "); break;
                   1013:       case 'n': can_answer=NAY; move(S_ROW,S_COL); clrtoeol();
                   1014:                 mvaddstr(S_ROW,S_COL,"*Hand-graded Incorrect    "); break;
                   1015:     case '0': case '1': case '2': case '3': case '4': case '5': 
                   1016:     case '6': case '7': case '8': case '9':
                   1017:       response=answered;
                   1018:     case 'N':   if ( tried < p->tries ) {
                   1019:                   can_answer=YAK;
                   1020: 		  if( (p->tries - tried) == 1 ) {
                   1021: 		    sprintf(tmp_str,"*%s, ONE try left!!",response);
                   1022: 		  } else {
                   1023: 		    sprintf(tmp_str,"*%s, tries %2d/%2d   ",response,tried,p->tries);
                   1024: 		  }
                   1025:                 } else {
                   1026: 		  can_answer=NAY;
                   1027: 		  sprintf(tmp_str,  "*%s, no more tries",response);
                   1028: 		}
                   1029:                 move(S_ROW,S_COL); clrtoeol();
                   1030:                 mvaddstr(S_ROW,S_COL,tmp_str); 
                   1031:                 if( (can_answer == YAK) && (p->ans_op == ANS_AND) && (p->ans_cnt > 1)) {
                   1032:                    sprintf(tmp_str, " Entering answer   1 of %3d     ",p->ans_cnt);
                   1033:                    mvaddstr(A_ROW,S_COL,tmp_str);
                   1034:                 }
                   1035:                 break;
                   1036:     }
                   1037:   } else {  /* hand graded question */
                   1038:     can_answer=NAY;
                   1039:     move(S_ROW,S_COL); clrtoeol();
                   1040:     mvaddstr(S_ROW,S_COL,"*Hand-graded question     ");
                   1041:   }
                   1042:   /* ------------------------------------------------------------------ */
                   1043:   if (*allow_h && 
                   1044:       p->hint && 
                   1045:       (
                   1046:        ( p->show_hint <= tried ) || 
                   1047:        ( prev_ans == 'y' ) ||
                   1048:        ( prev_ans == 'Y' )
                   1049:        )
                   1050:       ) {
                   1051:     mvaddstr(X_ROW,H_COL,":H = Show Hint");
                   1052:   } else {
                   1053:     *allow_h = 0;
                   1054:   }
                   1055:   if (p->next)
                   1056:     mvaddstr(X_ROW,R_COL,"RETURN = Next Problem");
                   1057:   else
                   1058:     mvaddstr(X_ROW,R_COL,"RETURN = Main Menu   ");
                   1059:   
                   1060:   return (can_answer);
                   1061:   
                   1062: }
                   1063: int  show_prior_inhibited_response(Problem_t *p,int hgr,int prev_ans,int tried,
                   1064: 				   int *allow_h)
                   1065: {
                   1066:   char     tmp_str[MAX_BUFFER_SIZE];
                   1067:   int      can_answer;
                   1068:   
                   1069:   if( hgr == '0' ) {
                   1070:     switch(prev_ans) {
                   1071:       case '-': can_answer=YAK; move(S_ROW,S_COL); clrtoeol();
                   1072:                 mvaddstr(S_ROW,S_COL,"*Unanswered               "); break;
                   1073:       case 'E':
                   1074:       case 'e':
                   1075:       case 'n':
                   1076:       case 'y': 
                   1077:       case 'Y': 
                   1078:       case 'N': if ( tried < p->tries ) {
                   1079: 	          can_answer=YAK;
                   1080: 		  if( (p->tries - tried) == 1 ) {
                   1081: 		    sprintf(tmp_str,"*Answered, ONE try left!! ");
                   1082: 		  } else {
                   1083: 		    sprintf(tmp_str,"*Answered, tries %2d/%2d    ",tried,p->tries);
                   1084: 		  }
                   1085:                 } else {
                   1086: 		  can_answer=NAY;
                   1087: 		  sprintf(tmp_str,  "*Answered, no more tries ");
                   1088: 		}
                   1089:                 move(S_ROW,S_COL); clrtoeol();
                   1090:                 mvaddstr(S_ROW,S_COL,tmp_str); break;
                   1091:            
                   1092:     }
                   1093:   } else {  /* hand graded question */
                   1094:     can_answer=NAY;
                   1095:     move(S_ROW,S_COL); clrtoeol();
                   1096:     mvaddstr(S_ROW,S_COL,"*Hand-graded question     ");
                   1097:   }
                   1098:   /* ------------------------------------------------------------------ */
                   1099:   if (*allow_h && p->hint && ( p->show_hint <= tried)){
                   1100:     mvaddstr(X_ROW,H_COL,":H = Show Hint");
                   1101:   } else {
                   1102:     *allow_h = 0;
                   1103:   }
                   1104:   if (p->next)
                   1105:     mvaddstr(X_ROW,R_COL,"RETURN = Next Problem");
                   1106:   else
                   1107:     mvaddstr(X_ROW,R_COL,"RETURN = Main Menu   ");
                   1108:   
                   1109:   return (can_answer);
                   1110:   
                   1111: }
                   1112: /* -------------------------------------------- dbug --------------------- */
                   1113: void
                   1114: print_unit_components(FILE *fp,Unit_t *t) 
                   1115: {
                   1116:   Unit_E  *ue_p;
                   1117: 
                   1118:   fprintf(fp,"  Unit::[%s] = %g * ", t->u_symbol, t->u_scale);
                   1119:   for(ue_p=t->u_list; ue_p ; ue_p = ue_p->ue_nextp) {
                   1120:     fprintf(fp,"(%g*%s^%g) ",ue_p->ue_scale,ue_p->ue_symbol,ue_p->ue_exp);
                   1121:   }
                   1122:   fprintf(fp,"\n"); fflush(fp);
                   1123: 
                   1124: }
                   1125: 
                   1126: 
                   1127: #define    ANSWER_STRING_LENG       64
                   1128: #define    UNIT_STRING_LENG         64
                   1129: #define    FORMAT_STRING_LENG       32
                   1130: 
                   1131: /* ------------------------------------------------------------------- */
                   1132: int  give_response(Problem_t *p,char **a,int cnt,int *tried,int *log_char)
                   1133: {
                   1134:   int      can_answer;
                   1135:   char     tmp_str[MAX_BUFFER_SIZE], *c_answer_str;
                   1136: 
                   1137: 
                   1138:   switch( capa_check_answers(p,a,cnt) ) {
                   1139: 
                   1140:     case  EXACT_ANS:  move(A_ROW,S_COL); clrtoeol();
                   1141:                       mvaddstr(A_ROW,S_COL,"*Yes Computer gets:"); 
                   1142:                       c_answer_str = answers_string(ANSWER_STRING_MODE, p);
                   1143:                       move(S_ROW,S_COL); clrtoeol();
                   1144:                       mvaddstr(S_ROW,S_COL,c_answer_str);
                   1145:                       capa_mfree((char *)c_answer_str);
                   1146:                      *log_char='Y'; can_answer=NAY;
                   1147:                       if( *tried < TRY_BOUND)  (*tried)++;
                   1148:                       break;
                   1149:     case  APPROX_ANS: 
                   1150:                       move(A_ROW,S_COL); clrtoeol();
                   1151:                       mvaddstr(A_ROW,S_COL,"*Yes Computer gets:");
                   1152:                       c_answer_str = answers_string(ANSWER_STRING_MODE, p);
                   1153:                       if(cnt == 1 ) {
                   1154:                         move(S_ROW,S_COL); clrtoeol();
                   1155:                         mvaddstr(S_ROW,S_COL,c_answer_str);
                   1156:                       } else {  /* more than one answer to check ANS_AND */
                   1157:                         move(S_ROW,S_COL); clrtoeol();
                   1158:                         mvaddstr(S_ROW,S_COL,"*Yes Correct Answers See Above");
                   1159:                         move(A_ROW,A_COL); clrtoeol();
                   1160:                         mvaddstr(A_ROW,A_COL,c_answer_str);
                   1161:                       }
                   1162:                       capa_mfree((char *)c_answer_str);
                   1163:                      *log_char='Y'; can_answer=NAY;
                   1164:                       if(*tried < TRY_BOUND)  (*tried)++;
                   1165:                       break;
                   1166:     case  SIG_FAIL:   move(S_ROW,S_COL); clrtoeol();  
                   1167:                       mvaddstr(S_ROW,S_COL,"*Adjust Sig. Figs. ");
                   1168:                      *log_char='S'; can_answer=YAK;
                   1169:                       break;
                   1170:     case  UNIT_FAIL:  move(S_ROW,S_COL); clrtoeol();  
                   1171:                       mvaddstr(S_ROW,S_COL,"*Units incorrect   ");
                   1172:                      *log_char='U'; can_answer=YAK;
                   1173:                       break;
                   1174:     case  UNIT_NOTNEEDED:  move(S_ROW,S_COL); clrtoeol();  
                   1175:                       mvaddstr(S_ROW,S_COL,"*Only a number required");
                   1176:                      *log_char='U'; can_answer=YAK;
                   1177:                       break;
                   1178:     case  NO_UNIT:    move(S_ROW,S_COL); clrtoeol();  
                   1179:                       mvaddstr(S_ROW,S_COL,"*Units required    ");
                   1180:                      *log_char='u'; can_answer=YAK;
                   1181:                       break;
                   1182:     case  BAD_FORMULA:move(S_ROW,S_COL); clrtoeol();  
                   1183:                       mvaddstr(S_ROW,S_COL,"*Unable to interpret formula");
                   1184:                      *log_char='F'; can_answer=YAK;
                   1185:                       break;
                   1186:     case  ANS_CNT_NOT_MATCH:
                   1187:                       move(S_ROW,S_COL); clrtoeol();  
                   1188:                       mvaddstr(S_ROW,S_COL,"*Invalid number of answers");
                   1189:                      *log_char='C'; can_answer=YAK;
                   1190:                       break;
                   1191:     case  INCORRECT: 
                   1192:                       if(*tried < TRY_BOUND)  (*tried)++;
                   1193: 		      if ( *tried < p->tries ) {
                   1194: 			can_answer=YAK;
                   1195: 			if( (p->tries - *tried) == 1 ) {
                   1196: 			  sprintf(tmp_str,"*Incorrect, ONE try left!!");
                   1197: 			} else {
                   1198: 			  sprintf(tmp_str,"*Incorrect, tries %2d/%2d   ",*tried,p->tries);
                   1199: 			}
                   1200: 		      } else {
                   1201: 			can_answer=NAY;
                   1202: 			sprintf(tmp_str,  "*Incorrect, no more tries");
                   1203: 		      }
                   1204:                       move(S_ROW,S_COL); clrtoeol(); 
                   1205:                       mvaddstr(S_ROW,S_COL, tmp_str);
                   1206:                       if( (can_answer == YAK) && (p->ans_op == ANS_AND) && (p->ans_cnt > 1)  ) {
                   1207:                          sprintf(tmp_str, " Entering answer   1 of %3d     ",p->ans_cnt);
                   1208:                          mvaddstr(A_ROW,S_COL,tmp_str);
                   1209:                       }
                   1210: 	             *log_char='N';
                   1211: 	              break;
                   1212:   }
                   1213:    
                   1214:   return (can_answer);
                   1215: }
                   1216: 
                   1217: int  give_inhibited_response(Problem_t *p,char **a,int cnt,int *tried,int *log_char)
                   1218: {
                   1219:   int      can_answer;
                   1220:   char     tmp_str[MAX_BUFFER_SIZE];
                   1221: 
                   1222: 
                   1223:   switch( capa_check_answers(p,a,cnt) ) {
                   1224: 
                   1225: 
                   1226:     case  EXACT_ANS:  *log_char='Y'; break;
                   1227:     case  APPROX_ANS: *log_char='Y'; break;
                   1228:     case  SIG_FAIL:   *log_char='S'; break;
                   1229:     case  UNIT_FAIL:  *log_char='U'; break;
                   1230:     case  UNIT_NOTNEEDED: *log_char='U'; break;
                   1231:     case  NO_UNIT:    *log_char='u'; break;
                   1232:     case  BAD_FORMULA:*log_char='F'; break;
                   1233:     case  INCORRECT:  *log_char='N'; break;
                   1234:     case ANS_CNT_NOT_MATCH: *log_char='C'; break;
                   1235:   }
                   1236:   
                   1237:   if(*tried < TRY_BOUND)  (*tried)++;
                   1238:   if ( *tried < p->tries ) {
                   1239:     can_answer=YAK;
                   1240:     if( (p->tries - *tried) == 1 ) {
                   1241:       sprintf(tmp_str,"*Answered, ONE try left!! ");
                   1242:     } else {
                   1243:       sprintf(tmp_str,"*Answered, tries %2d/%2d    ",*tried,p->tries);
                   1244:     }
                   1245:   } else {
                   1246:     can_answer=NAY;
                   1247:     sprintf(tmp_str,  "*Answered, no more tries ");
                   1248:   }
                   1249:   move(S_ROW,S_COL); clrtoeol(); 
                   1250:   mvaddstr(S_ROW,S_COL, tmp_str);
                   1251:   return (can_answer);
                   1252: }
                   1253: 
                   1254: int  ask_what_prob(int q_cnt, char *ans)
                   1255: {
                   1256:   int  not_ok=1,num,anslength,i,j;
                   1257:   char buf[5],buf2[MAX_BUFFER_SIZE];
                   1258:   
                   1259:   move(14,35); clrtoeol();
                   1260:   move(17,5);  clrtoeol();
                   1261:   do {
                   1262:      move(14,35); clrtoeol();
                   1263:      move(15,0);  clrtoeol();
                   1264:      mvaddstr(15,13,"What problem number:");
                   1265:      move(17,0);  clrtoeol();
                   1266:      mvaddstr(17,16,"         1         2         3         4         5");
                   1267:      mvaddstr(18,16,"12345678901234567890123456789012345678901234567890");
                   1268:      anslength=strlen(ans);
                   1269:      for(i=0;i<=(anslength/50);i++) {
                   1270:        if ( g_inhibit_response ) {
                   1271: 	 for(j=50*i;(j<((i+1)*50))&&(j<anslength);j++) {
                   1272: 	   if (ans[j]=='-') 
                   1273: 	     buf2[j-(50*i)]='-';
                   1274: 	   else
                   1275: 	     buf2[j-(50*i)]='A';
                   1276: 	 }
                   1277: 	 buf2[j-(50*i)]='\0';
                   1278:        } else {
                   1279: 	 strncpy(buf2,&(ans[50*i]),50);
                   1280:        }
                   1281:        buf2[50]='\0';
                   1282:        mvaddstr(19+i,16,buf2);
                   1283:        if (anslength > 50 ) {
                   1284: 	 sprintf(buf2,"%3d-%3d",i*50+1,(i+1)*50);
                   1285: 	 mvaddstr(19+i,5,buf2);
                   1286:        }
                   1287:      }
                   1288:      do { get_input(15,34,buf,4); } while(!strlen(buf));
                   1289:      sscanf(buf,"%d",&num);
                   1290:      if (num<1 || num>q_cnt) {
                   1291:         move(21,5); clrtoeol();
                   1292:         mvaddstr(21,15,"  Error: Invalid problem number\n");
                   1293:      } else {
                   1294:         not_ok = 0;
                   1295:      }
                   1296:   } while (not_ok);
                   1297: 
                   1298:   return (num);
                   1299: }
                   1300: 
                   1301: /* gather subjective answers from student */
                   1302: 
                   1303: #define    BS    8
                   1304: #define    DEL   127
                   1305: #define    ESC   27
                   1306: 
                   1307: #define    COLON 58
                   1308: 
                   1309: #define EDIT_HEIGHT 21
                   1310: #define EDIT_WIDTH 80
                   1311: #define MENULINE EDIT_HEIGHT
                   1312: 
                   1313: void refresh_editor (char **sbuf_pp,int cx,int cy) {
                   1314:   int i;
                   1315:   CLEAR();
                   1316:   echo();
                   1317:   mvaddstr(MENULINE,0,"Type in the answer, use up, down, left, right keys to move curser");
                   1318:   mvaddstr(MENULINE+1,0,"Enter ctrl-e to exit and submit answer");
                   1319:   mvaddstr(MENULINE+2,0,"Enter ctrl-f to forget answer");
                   1320:   for(i=0;i<EDIT_HEIGHT;i++) { mvaddstr(i,0,sbuf_pp[i]); }
                   1321:   move(cy,cx); refresh(); noecho();
                   1322: }
                   1323: 
                   1324: void init_editor(char*** sbuf_pp)
                   1325: {
                   1326:   int   ww=EDIT_WIDTH, hh=EDIT_HEIGHT,i;
                   1327:   *sbuf_pp = (char **)capa_malloc(sizeof(char *),hh);
                   1328:   for(i=0;i<hh;i++) {
                   1329:     (*sbuf_pp)[i] = (char *)capa_malloc(sizeof(char)*ww+1,1);
                   1330:   }
                   1331:   CLEAR();echo();
                   1332:   mvaddstr(MENULINE,0,"Type in the answer, use up, down, left, right keys to move cursor");
                   1333:   mvaddstr(MENULINE+1,0,"Enter ctrl-e to exit and submit answer");
                   1334:   mvaddstr(MENULINE+2,0,"Enter ctrl-f to forget answer");
                   1335:   move(0,0); refresh(); noecho();
                   1336: }
                   1337: 
                   1338: void remove_character(char** sbuf_pp,int *cx,int *cy)
                   1339: {
                   1340:   int sx=(*cx)-1,sy=*cy;
                   1341:   char temp,*temp_p;
                   1342:   if (*cx==0) { 
                   1343:     int abovelen,curlen,diff,i,j;
                   1344:     if (*cy==0) { beep();return;}
                   1345:     abovelen=strlen(sbuf_pp[(*cy-1)]);
                   1346:     curlen=strlen(sbuf_pp[*cy]);
                   1347:     if (abovelen > 0) sbuf_pp[(*cy)-1][abovelen-1]='\0';
                   1348:     if ((abovelen+curlen) < EDIT_WIDTH) {
                   1349:       strcat(sbuf_pp[(*cy)-1],sbuf_pp[*cy]);
                   1350:       memset(sbuf_pp[(*cy)],'\0',EDIT_WIDTH+1);
                   1351:       temp_p=sbuf_pp[*cy];
                   1352:       i=*cy;
                   1353:       while(i<EDIT_HEIGHT-1) {
                   1354: 	sbuf_pp[i]=sbuf_pp[i+1];
                   1355: 	echo();move(i,0);CLRTOEOL();mvaddstr(i,0,sbuf_pp[i]);noecho();
                   1356: 	i++;
                   1357:       }
                   1358:       sbuf_pp[EDIT_HEIGHT-1]=temp_p;
                   1359:       echo();move(EDIT_HEIGHT-1,0);CLRTOEOL();noecho();
                   1360:     } else {
                   1361:       diff=EDIT_WIDTH-abovelen;
                   1362:       strncat(sbuf_pp[(*cy)-1],sbuf_pp[*cy],diff);
                   1363:       i=diff;j=0;
                   1364:       while(sbuf_pp[*cy][i]!='\0') {
                   1365: 	sbuf_pp[*cy][j]=sbuf_pp[*cy][i];
                   1366: 	i++;j++;
                   1367:       }
                   1368:       memset(&(sbuf_pp[(*cy)][j]),'\0',EDIT_WIDTH+1-j);
                   1369:     }
                   1370:     echo();move(*cy,0); CLRTOEOL(); mvaddstr(*cy,0,sbuf_pp[*cy]);noecho();
                   1371:     (*cy)--;
                   1372:     echo();move(*cy,0); CLRTOEOL(); mvaddstr(*cy,0,sbuf_pp[*cy]);noecho();
                   1373:     if ( EDIT_WIDTH == ((*cx)=(abovelen-1))) (*cx)--;
                   1374:     if (abovelen==0) *cx=0;
                   1375:     echo();move(*cy,*cx);noecho();
                   1376:   } else {
                   1377:     echo();move(sy,sx);noecho();
                   1378:     temp=sbuf_pp[sy][sx]=sbuf_pp[sy][sx+1];
                   1379:     sx++;
                   1380:     while(temp!='\0') {
                   1381:       echo(); ADDCH(temp); noecho();
                   1382:       temp=sbuf_pp[sy][sx]=sbuf_pp[sy][sx+1];
                   1383:       sx++;
                   1384:     }
                   1385:     echo(); ADDCH(' '); noecho();
                   1386:     (*cx)--;
                   1387:   }
                   1388:   echo();move(*cy,*cx);noecho();
                   1389: }
                   1390: 
                   1391: void break_line      (char** sbuf_pp,int *cx,int *cy)
                   1392: {
                   1393:   int sx=*cx,sy=*cy,i;
                   1394:   if (sy < EDIT_HEIGHT-1) {
                   1395:     capa_mfree(sbuf_pp[EDIT_HEIGHT-1]);
                   1396:     i=EDIT_HEIGHT-1;
                   1397:     while (i-1 > sy) {
                   1398:       sbuf_pp[i]=sbuf_pp[i-1];
                   1399:       move(i,0);CLRTOEOL();mvaddstr(i,0,sbuf_pp[i]);
                   1400:       i--;
                   1401:     }
                   1402:     sbuf_pp[sy+1]=capa_malloc(sizeof(char)*EDIT_WIDTH+1,1);
                   1403:   }
                   1404:   strcat(sbuf_pp[sy+1],&(sbuf_pp[sy][sx]));
                   1405:   memset(&(sbuf_pp[sy][sx]),'\0',EDIT_WIDTH+1-sx);
                   1406:   *cx=0;
                   1407:   (*cy)++;
                   1408:   move(sy,0);CLRTOEOL();mvaddstr(sy,0,sbuf_pp[sy]);
                   1409:   move(sy+1,0);CLRTOEOL();mvaddstr(sy+1,0,sbuf_pp[sy+1]);
                   1410: }
                   1411: 
                   1412: /* FIXME catch funtion keys and others? */
                   1413: void handle_esc      (unsigned char ca,unsigned char cb,char** sbuf_pp,int *cx,int *cy)
                   1414: {
                   1415:   if( ca!='[') return;
                   1416:   switch (cb) {
                   1417:   case 'A':/* KEY_UP */
                   1418:     if(*cy>0){
                   1419:       (*cy)--;
                   1420:       while(*cx>0 && sbuf_pp[*cy][(*cx)-1]=='\0') (*cx)--; /* goto end of line */
                   1421:     } else {
                   1422:       beep();
                   1423:     }
                   1424:     break;
                   1425:   case 'B': /* KEY_DOWN */
                   1426:     if (*cy<(EDIT_HEIGHT-1)) {
                   1427:       (*cy)++;
                   1428:       while(*cx>0 && sbuf_pp[*cy][(*cx)-1]=='\0') (*cx)--; /* goto end of line */
                   1429:     } else {
                   1430:       beep();
                   1431:     }
                   1432:     break;
                   1433:   case 'C': /* KEY_RIGHT */
                   1434:     if ( *cx<(EDIT_WIDTH-1) && sbuf_pp[*cy][(*cx)]!='\0' ) { 
                   1435:       (*cx)++; 
                   1436:     } else {
                   1437:       if (*cy<(EDIT_HEIGHT-1)) {
                   1438: 	(*cy)++; *cx=0; 
                   1439:       } else {
                   1440: 	beep();
                   1441:       }
                   1442:     }
                   1443:     break;
                   1444:   case 'D': /* KEY_LEFT */
                   1445:     if(*cx>0) {
                   1446:       (*cx)--;
                   1447:     } else {
                   1448:       if(*cy>0) { 
                   1449: 	(*cy)--;
                   1450: 	*cx=strlen(sbuf_pp[*cy]);
                   1451: 	if (*cx==EDIT_WIDTH) (*cx)--;
                   1452:       } else { 
                   1453: 	beep(); 
                   1454:       }
                   1455:     }
                   1456:     break;
                   1457:   default: beep(); return; break;
                   1458:   }
                   1459:   echo(); move(*cy,*cx); refresh(); noecho();
                   1460: }
                   1461: 
                   1462: void handle_error    (unsigned char c,char** sbuf_pp,int cx,int cy) 
                   1463: {
                   1464:   beep();
                   1465: }
                   1466: 
                   1467: /*FIXME Slower than whale shit*/
                   1468: void insert_character(unsigned char c,char** sbuf_pp,int *cx,int *cy) 
                   1469: {
                   1470:   int sx=*cx,sy=*cy;
                   1471:   unsigned char temp;
                   1472:   while(c!='\0') {
                   1473:     if (sx == EDIT_WIDTH) {
                   1474:       sx=0;sy++;
                   1475:       if (sy == EDIT_HEIGHT) {
                   1476: 	sy--;sx=EDIT_WIDTH;c='\0';break;
                   1477:       }
                   1478:     }	
                   1479:     echo(); ADDCH(c); noecho();
                   1480:     temp=sbuf_pp[sy][sx];
                   1481:     sbuf_pp[sy][sx]=c;
                   1482:     c=temp;
                   1483:     sx++;
                   1484:   }
                   1485:   sbuf_pp[sy][sx]=c;
                   1486:   (*cx)++;
                   1487:   if (*cx == EDIT_WIDTH) {
                   1488:       *cx=0;(*cy)++;
                   1489:       if (*cy == EDIT_HEIGHT) {
                   1490: 	(*cy)--;*cx=EDIT_WIDTH-1;
                   1491:       }
                   1492:   }
                   1493:   move(*cy,*cx);refresh();
                   1494: }
                   1495: 
                   1496: int handle_keystrokes_editor(char** sbuf_pp)
                   1497: {
                   1498:   int   done = 0, forget = 0, cx=0,cy=0;
                   1499:   unsigned char c,ca,cb;
                   1500: 
                   1501:   while (!done) {
                   1502:     move(cy,cx);refresh();
                   1503:     c=getch();
                   1504:     switch(c) {
                   1505:     case BS: case DEL:
                   1506:       remove_character(sbuf_pp,&cx,&cy);
                   1507:       break;
                   1508:     case CR: case LF:
                   1509:       break_line(sbuf_pp,&cx,&cy);
                   1510:       break;
                   1511:     case ESC:
                   1512:       ca=getch();cb=getch();
                   1513:       handle_esc(ca,cb,sbuf_pp,&cx,&cy);
                   1514:       break;
                   1515:     case 5: /*ctrl-e*/
                   1516:       done=1;
                   1517:       break;
                   1518:     case 6: /*ctrl-f*/
                   1519:       done=1;
                   1520:       forget=1;
                   1521:       break;
                   1522:     case 12:
                   1523:       refresh_editor(sbuf_pp,cx,cy);
                   1524:       break;
                   1525:     default:
                   1526:       if (c < 32 || c>126) {
                   1527: 	handle_error(c,sbuf_pp,cx,cy);
                   1528:       } else {
                   1529: 	insert_character(c,sbuf_pp,&cx,&cy);
                   1530:       }
                   1531:       break;
                   1532:     }
                   1533:   }
                   1534:   return forget;
                   1535: }
                   1536: 
                   1537: int editor(char*** sbuf_pp)
                   1538: {
                   1539:   init_editor(sbuf_pp);
                   1540:   return handle_keystrokes_editor(*sbuf_pp);
                   1541: }
                   1542: 
                   1543: 
                   1544: int
                   1545: answer_subjective(student_number,set,section,prob)
                   1546: char  *student_number; 
                   1547: int    set; 
                   1548: int   *section;
                   1549: int    prob;
                   1550: {
                   1551:   int i,length;
                   1552:   char date_str[DATE_LENGTH];
                   1553:   char **sbuf_pp,answer[(EDIT_HEIGHT*(EDIT_WIDTH+1))+1];
                   1554:   char submissions_str[(EDIT_HEIGHT*(EDIT_WIDTH+1))+MAX_BUFFER_SIZE];
                   1555:   time_t     curtime;
                   1556: 
                   1557:   time(&curtime);
                   1558:   if( capa_check_date(CHECK_DUE_DATE,student_number,*section,set) > 0 ) {
                   1559:     capa_get_date(CHECK_DUE_DATE,student_number,*section,set,date_str);
                   1560:     sprintf(answer,"Sorry, the due date was: %s",date_str);
                   1561:     move(20,1); clrtobot(); addstr(answer); mypause(23,1);
                   1562:     return 0;
                   1563:   }
                   1564: 
                   1565:   if (editor(&sbuf_pp)) { return 0; }
                   1566: 
                   1567:   answer[0]='\0';
                   1568:   for(i=0;i<EDIT_HEIGHT;i++) {
                   1569:     if (strlen(sbuf_pp[i]) > 0) {
                   1570:       strcat(answer,sbuf_pp[i]);
                   1571:       length=strlen(answer);
                   1572:       answer[length]='\n';
                   1573:       answer[length+1]='\0';
                   1574:     }
                   1575:     capa_mfree((char *)sbuf_pp[i]);
                   1576:   }
                   1577:   capa_set_subjective(set,prob,student_number,answer);
                   1578:   sprintf(submissions_str,"%d\t%s\t",prob,answer);
                   1579:   log_submissions(student_number,set,submissions_str);
                   1580:   capa_mfree((char *)sbuf_pp);
                   1581:   return 1;
                   1582: }
                   1583: 
                   1584: void set_entry_tries(int *tried, char *tries, int num, int num_questions) {
                   1585:   if((tried[num] >=0) && (tried[num] <= TRY_BOUND) ) {
                   1586:     if(tried[num] < 10 ) {
                   1587:       tries[3*num]   = ' ';
                   1588:       tries[3*num+1] = tried[num] + '0';
                   1589:       if(num < num_questions-1)  tries[3*num+2] = ',';
                   1590:     } else {
                   1591:       tries[3*num]   = (int)(tried[num]/10) + '0';
                   1592:       tries[3*num+1] = (tried[num] % 10) + '0';
                   1593:       if(num < num_questions-1)  tries[3*num+2] = ',';
                   1594:     }
                   1595:   } else {
                   1596:     tries[3*num]   = ' ';
                   1597:     tries[3*num+1] = 1 + '0';
                   1598:     if(num < num_questions-1)  tries[3*num+2] = ',';
                   1599:   }
                   1600: }
                   1601: 
                   1602: /* -------------------------------------------------------------------------- */
                   1603: /* LET THE USER ANSWER THE CURRENT PROBLEM SET QUESTIONS                      */
                   1604: /* -------------------------------------------------------------------------- */
                   1605: void                                
                   1606: try_set(student_number,set,section) 
                   1607: char  *student_number; 
                   1608: int    set; 
                   1609: int   *section;
                   1610: {
                   1611:    char       a_student_number[MAX_STUDENT_NUMBER+1];
                   1612:    time_t     curtime;
                   1613:    T_header   header;
                   1614:    Problem_t *first_problem, *p;
                   1615:    T_entry    entry;
                   1616:    char       answer[256], *a_str, **ans_strs;
                   1617:    int        num, offset, num_questions, start_from, leng;
                   1618:    char      *log_string,submissions_str[MAX_BUFFER_SIZE];
                   1619:    int       *tried,answered;
                   1620:    int        scr_idx=1, display=1, second_scr, canAnswer;
                   1621:    int        usr_command, whereto, allow_hint=0, ex=0;
                   1622:    char       u_input[64], date_str[DATE_LENGTH], one_line[81];
                   1623:    int        log_char, i, j, allow_n, allow_p, allow_subj;
                   1624:    
                   1625:    strncpy(a_student_number,student_number,MAX_STUDENT_NUMBER+1);
                   1626:    time(&curtime); /* Is due date past? */
                   1627:    /* ---------------------------------------- check due date */
                   1628: #ifndef NO_DATE_CHECK
                   1629:    /* ===> if ( compare_datetime(curtime,header.due_date) > 0) { */
                   1630:    if( capa_check_date(CHECK_DUE_DATE,student_number,*section,set) > 0 ) {
                   1631:       capa_get_date(CHECK_DUE_DATE,student_number,*section,set,date_str);
                   1632:       sprintf(answer,"  Sorry, the due date was: %s",date_str); 
                   1633:       move(17,1); clrtoeol(); mvaddstr(17,15,answer);   mypause(19,17);
                   1634:       return;
                   1635:    }
                   1636: #ifdef LOGIN_DBUG
                   1637:   fprintf(dfp,"Tryset():(sec=%d,set=%d)[%s]\n",*section,set,date_str); fflush(dfp);
                   1638: #endif /* LOGIN_DBUG */
                   1639: #endif /* NO_DATE_CHECK */
                   1640: 
                   1641:    offset=capa_get_entry(&entry,student_number,set);
                   1642:    capa_get_header(&header,set);
                   1643:    if (offset<0) offset = -offset;  /* newly created entry */
                   1644:    
                   1645: #ifdef LOGIN_DBUG
                   1646:    fprintf(dfp,"P set=%d,SN=%s,ANS=%s,TRY=%s\n",set,a_student_number,entry.answers,entry.tries); fflush(dfp);
                   1647: #endif
                   1648:    num = capa_parse(set,&first_problem,a_student_number,&num_questions,NULL);
                   1649:    
                   1650: #ifdef LOGIN_DBUG
                   1651:   fprintf(dfp,"ParseSource:=%d\n",num); fflush(dfp);
                   1652: #endif /* LOGIN_DBUG */
                   1653: 
                   1654:    /* DEBUGGING: make sure num_questions is plausible */
                   1655:    if (num_questions>1000 || num_questions<=0)   properly_logout(student_number);
                   1656:    
                   1657:    start_from=ask_what_prob(num_questions,entry.answers);
                   1658:    
                   1659:    /* initialize log string to all '-' */
                   1660:    tried = (int *)capa_malloc(num_questions+1,sizeof(int));
                   1661:    log_string = (char *)capa_malloc(num_questions+1,sizeof(char));
                   1662:    for (num=0; num<num_questions; num++)  {
                   1663:      log_string[num]='-';
                   1664:      sscanf(entry.tries + 3*num,"%d,",&(tried[num]) );
                   1665:    }
                   1666:    log_string[num_questions]=0;
                   1667:    capa_set_login_time(student_number,set);
                   1668:    for (num=0,p=first_problem; p; ){
                   1669:       if( start_from > 1 ) {
                   1670:         num=start_from-1;
                   1671:         for (p=first_problem; start_from > 1 && p->next; start_from--)
                   1672:              p=p->next;
                   1673:         start_from = 0;
                   1674:       }
                   1675:       if (display) {
                   1676:          /* DISPLAY QUESTION */
                   1677:          CLEAR();
                   1678:          second_scr = display_prob_scr(p->question,scr_idx);
                   1679:          allow_subj = 0;
                   1680:          if( p->ans_type == ANSWER_IS_SUBJECTIVE ) {
                   1681:            allow_subj = 1;
                   1682:            move(A_ROW,A_COL); clrtoeol();
                   1683:            mvaddstr(A_ROW,A_COL,"Enter :A to answer subjective question");
                   1684:          }
                   1685:          mvaddstr(S_ROW,0,"OPTION/ANSWER");
                   1686:          mvaddstr(O_ROW,0,"Options :M = Main Menu  :7 = go to # 7");
                   1687:          allow_n = allow_p = 0;
                   1688:          if( second_scr && (scr_idx == 1) ) {
                   1689:            mvaddstr(O_ROW,E_COL,":N = Next screen");
                   1690:            allow_n=1;
                   1691:          }
                   1692:          if( second_scr && (scr_idx == 2) ) {
                   1693:            mvaddstr(O_ROW,E_COL,":P = Prev screen");
                   1694:            allow_p=1;
                   1695:          }
                   1696:          
                   1697:          mvaddstr(O_ROW,R_COL,"RETURN = Enter/Execute");
                   1698: 	 
                   1699: 	 if (g_inhibit_response ) {
                   1700: 	   canAnswer = show_prior_inhibited_response(p,header.partial_credit[num],entry.answers[num],tried[num],&allow_hint);
                   1701: 	 } else {
                   1702: 	   canAnswer = show_prior_response(p,header.partial_credit[num],entry.answers[num],tried[num],&allow_hint);
                   1703: 	 }
                   1704: 	 
                   1705:       }
                   1706:       mvaddstr(X_ROW,X_COL,":X = eXit");
                   1707:       
                   1708:       /* <= */
                   1709:       
                   1710:       
                   1711:       
                   1712:         get_xinput(S_ROW,A_COL,u_input,U_ANS_CHAR);
                   1713:         display=0;  usr_command=C_DONTCARE;
                   1714:         /* DEFAULT ACTIONS on empty input */
                   1715:         if(!strlen(u_input)) { usr_command = (p->next? C_FORWARD : C_MENU); } else {
                   1716:           if( u_input[0] == ':' ) {
                   1717:            switch(toupper( u_input[1] )) {
                   1718:              case 'H': if( allow_hint ) { usr_command=C_HINT; } break;
                   1719:              case 'M': usr_command=C_MENU;    break;
                   1720:              case 'N': if( allow_n ) { usr_command=C_NEXTSCR; } break;
                   1721:              case 'P': if( allow_p ) { usr_command=C_PREVSCR; } break;
                   1722:              case 'X': usr_command=C_EXIT;    break;
                   1723:              case 'A': if( allow_subj ) { usr_command=C_SUBJANS; } break;
                   1724:              default : sscanf(u_input,":%d",&whereto);
                   1725:                     if(whereto >0 && whereto <= num_questions) usr_command=C_JUMP;
                   1726: 		    break;
                   1727:            }
                   1728:           } else { /* user entered some answer */
                   1729:             if( p->ans_op == ANS_AND ) {
                   1730:               if(canAnswer) { usr_command=C_ANSWER;
                   1731:                 ans_strs = (char **)capa_malloc(sizeof(char *), p->ans_cnt);
                   1732:                 ans_strs[0] = (char *)capa_malloc(strlen(u_input)+1,1);
                   1733:                 strcpy(ans_strs[0],u_input);
                   1734:                 for(i=1;i<p->ans_cnt;i++) {
                   1735:                   mvaddstr(A_ROW,A_COL,"                                ");
                   1736: 	          mvaddstr(A_ROW,A_COL,ans_strs[i-1]);
                   1737:                   sprintf(one_line,    " Entering answer %3d of %3d     ", i+1,p->ans_cnt);
                   1738:                   mvaddstr(A_ROW,S_COL,one_line);
                   1739:                   mvaddstr(S_ROW,A_COL,"                                ");
                   1740:                   get_xinput(S_ROW,A_COL,u_input,U_ANS_CHAR);
                   1741:                   ans_strs[i] = (char *)capa_malloc(strlen(u_input)+1,1);
                   1742:                   strcpy(ans_strs[i],u_input);
                   1743:                   
                   1744:                 }
                   1745:                 
                   1746:                 /* now in ans_strs[][] are user inputs */
                   1747:                 
                   1748:               }
                   1749:             } else { /* one answer or ANS_OR */
                   1750:               ans_strs = (char **)capa_malloc(sizeof(char *), 1);
                   1751:               ans_strs[0] = (char *)capa_malloc(strlen(u_input)+1,1);
                   1752:               strcpy(ans_strs[0], u_input);
                   1753:               if(canAnswer)  { usr_command=C_ANSWER; 
                   1754: 	         mvaddstr(S_ROW,A_COL,"                                ");
                   1755: 	         mvaddstr(A_ROW,A_COL,"                                ");
                   1756: 	         mvaddstr(A_ROW,A_COL,ans_strs[0]);  }
                   1757: 	    }
                   1758:           } /* end if  u_input[0] == ':' */
                   1759:         } /* end if !strlen(u_input) */
                   1760:       
                   1761:         
                   1762:       
                   1763:       
                   1764:       
                   1765:       /* PROCESS USER COMMAND */
                   1766:       switch(usr_command) {
                   1767:         case C_FORWARD: /* Forwards */
                   1768:                 if (p->next) {
                   1769:                    p=p->next; num++;
                   1770:                    display=1; allow_hint=0; scr_idx=1;
                   1771:                 } else
                   1772:                    mvaddstr(X_ROW,R_COL,"RETURN = Main Menu   ");
                   1773:                 break;
                   1774:         case C_NEXTSCR:  scr_idx = 2; display=1;
                   1775:                 break;
                   1776:         case C_PREVSCR:  scr_idx = 1; display=1;
                   1777:                 break;
                   1778:         case C_EXIT: /* Exit */ 
                   1779:                 ex=1; p=0; break;
                   1780:         case C_MENU: /* Return to main menu */
                   1781:                 p=0;  break;
                   1782:         case C_HINT: /* Hint */
                   1783:                 if (! p->hint)    break;
                   1784:                 display_hint(p->hint);
                   1785:                 display=1;
                   1786:                 break;
                   1787:         case C_ANSWER: /* Answer question */
                   1788:               { 
                   1789: 		if(p->ans_type== ANSWER_IS_SUBJECTIVE) {
                   1790: 		  move(A_ROW,A_COL); clrtoeol();
                   1791: 		  mvaddstr(A_ROW,A_COL,"Enter :A to answer subjective question");
                   1792: 		  capa_mfree(ans_strs[0]);
                   1793: 		  break;
                   1794: 		}
                   1795: 		if( p->ans_op == ANS_AND ) {
                   1796: 		    leng = 0;
                   1797: 		    for(i=0;i<p->ans_cnt;i++) {
                   1798: 		       leng += (strlen((char *)ans_strs[i]) + 2);
                   1799: 		    }
                   1800: 		    a_str = (char *)capa_malloc(leng+1,1);
                   1801: 		    a_str[0]=0;
                   1802: 		    strcat(a_str,ans_strs[0]);
                   1803: 		    if ( is_all_ws(ans_strs[0]) )  break;
                   1804: 		    trim_response_ws(ans_strs[0]);
                   1805: 		    for(i=1;i<p->ans_cnt;i++) {
                   1806: 		       strcat(a_str,"\t");
                   1807: 		       strcat(a_str,ans_strs[i]);
                   1808: 		       if ( is_all_ws(ans_strs[i]) )  break;
                   1809: 		       trim_response_ws(ans_strs[i]);
                   1810: 		    }
                   1811: 		    if (i < p->ans_cnt) {
                   1812: 		      display=1; /*handle early breaks out of the*/
                   1813: 		      break; 	 /*loop which mean typed only ws */
                   1814: 		    }
                   1815: 		} else { /* only one answer */
                   1816: 		  leng = (strlen((char *)ans_strs[0]) + 2);
                   1817: 		  a_str = (char *)capa_malloc(leng+1,1);
                   1818: 		  a_str[0]=0;
                   1819: 		  strcat(a_str,ans_strs[0]);
                   1820: 		  if ( is_all_ws(ans_strs[0]) )  break;
                   1821: 		  trim_response_ws(ans_strs[0]);
                   1822: 		}
                   1823: 		
                   1824: 		sprintf(submissions_str,"%d\t%s\t",num+1,a_str);
                   1825: 		log_submissions(student_number,set,submissions_str);
                   1826: 
                   1827: 		{
                   1828: 		  int cnt=((p->ans_op==ANS_AND)?p->ans_cnt:1);
                   1829:      		  if (g_inhibit_response) {
                   1830: 		    canAnswer = give_inhibited_response(p, ans_strs,cnt,
                   1831: 							&(tried[num]),&log_char);
                   1832: 		  } else {
                   1833: 		    canAnswer = give_response(p, ans_strs,cnt, &(tried[num]),&log_char);
                   1834: 		  }
                   1835: 		}
                   1836: 		if( p->ans_op == ANS_AND ) {
                   1837: 		  for(i=0;i<p->ans_cnt;i++) {
                   1838: 		    capa_mfree( (char *)ans_strs[i] );
                   1839: 		  }
                   1840: 		  
                   1841: 		} else { /* there is only one user answer */
                   1842: 		  capa_mfree( (char *)ans_strs[0] );
                   1843: 		  
                   1844: 		}
                   1845: 		capa_mfree((char *)ans_strs);
                   1846: 		capa_mfree( (char *)a_str );
                   1847: 		
                   1848:                 if (p->hint && 
                   1849: 		    (
                   1850: 		     (p->show_hint<=tried[num])||
                   1851: 		     (log_char == 'y') ||
                   1852: 		     (log_char == 'Y')
                   1853: 		     )
                   1854: 		    ){
                   1855: 		  allow_hint=1;
                   1856: 		  mvaddstr(X_ROW,H_COL,":H = Show Hint");
                   1857:                 }
                   1858:                 switch(log_char) {
                   1859:                   case 'U': case 'u': case 'S': 
                   1860:                             entry.answers[num]='N';      break;
                   1861:                   case 'Y': allow_hint=1; mvaddstr(X_ROW,H_COL,":H = Show Hint");  /* fall through here */
                   1862:                    default: entry.answers[num]=log_char; break;
                   1863:                 }
                   1864:                 log_string[num]=log_char;
                   1865:                 
                   1866:                 log_attempt(student_number,set,*section,log_string);
                   1867:                 /* for (i=0; i<num_questions; i++) { log_string[i] = '-' ;  } */
                   1868: 		set_entry_tries(tried,entry.tries,num,num_questions);
                   1869: 		log_string[num]='-';
                   1870:                 /* ------------------------------ check due date */
                   1871:                 time(&curtime);
                   1872:                 /* ===> if (compare_datetime(curtime,header.due_date) > 0) { */
                   1873:                 if( capa_check_date(CHECK_DUE_DATE,student_number,*section,set) > 0 ) {
                   1874:                   capa_get_date(CHECK_DUE_DATE,student_number,*section,set,date_str);
                   1875:                   sprintf(answer,"Sorry, the due date was: %s",date_str);
                   1876:                   move(20,1); clrtobot(); addstr(answer); mypause(23,1);
                   1877:                 } else {
                   1878:                   capa_set_entry(&entry,student_number,set,offset);
                   1879:                 }
                   1880:               } break;
                   1881:         case C_JUMP: /* Jump to specific question number */
                   1882:                 num=whereto-1;
                   1883:                 for (p=first_problem; whereto > 1 && p->next; whereto--)
                   1884:                    p=p->next;
                   1885:                 display=1;  allow_hint=0; scr_idx=1;
                   1886:                 break;
                   1887:         case C_SUBJANS:  
                   1888:                 answered=answer_subjective(student_number,set,section,num+1); 
                   1889: 		if (answered) {
                   1890: 		  tried[num]++;
                   1891: 		  if (p->hint && ((p->show_hint<=tried[num]))) { allow_hint=1; }
                   1892: 		  entry.answers[num]='0';
                   1893: 		  log_string[num]='A';
                   1894: 		  log_attempt(student_number,set,*section,log_string);
                   1895: 		  log_string[num]='-';
                   1896: 		  set_entry_tries(tried,entry.tries,num,num_questions);
                   1897:                   capa_set_entry(&entry,student_number,set,offset);
                   1898: 		}
                   1899:                 display=1;
                   1900:                 break;
                   1901:         case C_DONTCARE:  break;
                   1902:       }
                   1903:    }
                   1904:    for (i=0,j=0, num=0; num<num_questions; num++) {
                   1905:      j = j + (header.weight[num] - '0');
                   1906:      if((entry.answers[num]=='Y') || (entry.answers[num]=='y')) 
                   1907:        i = i + (header.weight[num] - '0');
                   1908:      if( entry.answers[num] >= '0' && entry.answers[num] <= '9' ) {
                   1909:         i = i + (entry.answers[num] - '0');
                   1910:      }
                   1911:      if((entry.answers[num]=='E') || (entry.answers[num]=='e')) 
                   1912:        j = j - (header.weight[num] - '0');
                   1913:      if((tried[num] >=0) && (tried[num] <= TRY_BOUND) ) {
                   1914:        if(tried[num] < 10 ) {
                   1915:          entry.tries[3*num]   = ' ';
                   1916:          entry.tries[3*num+1] = tried[num] + '0';
                   1917:          if(num < num_questions-1)  entry.tries[3*num+2] = ',';
                   1918:        } else {
                   1919:          entry.tries[3*num]   = (int)(tried[num]/10) + '0';
                   1920:          entry.tries[3*num+1] = (tried[num] % 10) + '0';
                   1921:          if(num < num_questions-1)  entry.tries[3*num+2] = ',';
                   1922:        }
                   1923:      } else {
                   1924:        entry.tries[3*num]   = ' ';
                   1925:        entry.tries[3*num+1] = 1 + '0';
                   1926:        if(num < num_questions-1)  entry.tries[3*num+2] = ',';
                   1927:      }
                   1928:    }
                   1929:    capa_mfree(header.weight);
                   1930:    capa_mfree(header.partial_credit);
                   1931: 
                   1932:    sprintf(answer,"Your score for this set is now: %d/%d",i,j);
                   1933:    move(20,1); clrtobot(); addstr(answer); mypause(23,1);
                   1934:    /* ------- original code , should check due date before save it
                   1935:    
                   1936:    time(&curtime);
                   1937:    if (compare_datetime(curtime,header.due_date) > 0) {
                   1938:    if( capa_check_date(CHECK_DUE_DATE,*section,set) > 0 ) {
                   1939:       need to deal with due_date 
                   1940:       sprintf(answer,"Sorry, the due date was: %s",header.due_date);
                   1941:       move(20,1); clrtobot(); addstr(answer); mypause(23,1);
                   1942:    } else {
                   1943:       sprintf(answer,"Your score for this set is now: %d/%d",i,j);
                   1944:       move(20,1); clrtobot(); addstr(answer); mypause(23,1);
                   1945:       
                   1946:       capa_set_entry(&entry,student_number,set,offset);
                   1947:    }
                   1948:    ------ */
                   1949:    /* FREE UP MALLOC'ED SPACE (VERY IMPORTANT) */
                   1950:    capa_mfree(entry.answers);
                   1951:    capa_mfree(entry.tries);
                   1952:    free_problems(first_problem);
                   1953:    /* log_attempt(student_number,set,*section,log_string); */
                   1954:    capa_mfree(log_string);
                   1955:    capa_mfree((char*)tried);
                   1956:    if (ex) properly_logout(student_number);
                   1957:    
                   1958: }
                   1959: 
                   1960: #define   COL_ONE    1
                   1961: #define   COL_TWO    17
                   1962: #define   COL_THREE  34
                   1963: #define   COL_FOUR   43
                   1964: #define   COL_FIVE   69
                   1965: 
                   1966: /* ------------------------------------------------------------------------- */
                   1967: /* REVIEW PREVIOUS PROBLEM SETS                                              */
                   1968: /* ------------------------------------------------------------------------- */
                   1969: void                                      /* RETURNS: (nothing)              */
                   1970: view_previous(student_number,set,section) /* ARGUMENTS:                      */
                   1971: char  *student_number;                    /*    Student number               */
                   1972: int  set;                                 /*    Set number                   */
                   1973: int *section;                             /*    Section number               */
                   1974: {                                         /* LOCAL VARIABLES:                */
                   1975:    T_entry   entry;                       /*    Database entry               */
                   1976:    Problem_t *first_problem,              /*    Pointer to first problem     */
                   1977:              *problem;                    /*    Previous problem             */
                   1978:    int        num_questions,              /*    Total # of questions         */
                   1979:               ex=0,                       /*    Exit system flag             */
                   1980:               display=1,                  /*    Redraw flag                  */
                   1981: 	      usr_command,
                   1982: 	      whereto, 
                   1983: 	      allow_hint=0, allow_explain=0;
                   1984:    int        num;                        /*    Temporary variable           */
                   1985:    char       buf[4],                     /*    Command input buffer         */
                   1986:               aLine[MAX_BUFFER_SIZE];
                   1987:    T_header   header;                     /*    Set header                   */
                   1988:    time_t     curtime;                    /*    Current time                 */
                   1989:    double     upper_ans;
                   1990:    char       fmt_ans[ANSWER_STRING_LENG], goto_str[ANSWER_STRING_LENG], 
                   1991:               tmp_str[ANSWER_STRING_LENG];
                   1992:    int        scr_idx=1, second_scr, allow_n, allow_p;
                   1993:    
                   1994:    /* QUERY USER FOR SET */
                   1995:    move(15,5);  /* deleteln(); */
                   1996:    addstr("            Which set would you like to view?");
                   1997:    mvaddstr(16,15,   "Enter a set number and press ENTER/RETURN");
                   1998:    move(17,1); clrtoeol(); /* erase Enter a command ... */
                   1999:    do { get_input(15,51,buf,3); } while(!strlen(buf));
                   2000:    sscanf(buf,"%d",&num);
                   2001:    if (num<1 || num>set) {
                   2002:       move(17,5); clrtoeol();
                   2003:       mvaddstr(17,15,"   Error: Invalid previous set number\n");
                   2004:       mypause(19,17);   return;
                   2005:    }
                   2006:    /* ------------------------------------ answer date */
                   2007:    time(&curtime);
                   2008:    /* ===> if (compare_datetime(curtime,header.answer_date) < 0) { */
                   2009:    if ( capa_check_date(CHECK_ANS_DATE,student_number,*section,num) < 0 ) {
                   2010:       move(16,1); clrtoeol();
                   2011:       move(17,5); clrtoeol();
                   2012:       mvaddstr(17,15,"  Answers are not yet available\n");  mypause(19,17);  return;
                   2013:    }
                   2014: 
                   2015:    /* LOAD IN THE INFO NEEDED */
                   2016:    capa_get_header(&header,num);
                   2017:    capa_get_entry(&entry,student_number,num);
                   2018:    capa_parse(num,&first_problem,student_number,&num_questions,NULL);
                   2019:    sprintf(goto_str,"#=go to problem #, [%d problems]", num_questions);
                   2020:    for (num=0,problem=first_problem; problem; ) {
                   2021:       if (display) {
                   2022:          allow_hint = allow_explain=0;
                   2023:          allow_n = allow_p = 0;
                   2024:          CLEAR();
                   2025:          second_scr = display_prob_scr(problem->question,scr_idx);
                   2026:          if( problem->ans_type == ANSWER_IS_FLOAT ) {
                   2027:              upper_ans = (double)atof(problem->answer);
                   2028:              sprintf(fmt_ans, problem->ans_fmt, upper_ans);
                   2029:          } else {
                   2030:              sprintf(fmt_ans, "%s", problem->answer);
                   2031:          }
                   2032:          if( problem->ans_unit ) {
                   2033:              sprintf(tmp_str, "Answer: %s %s",fmt_ans,problem->unit_str);
                   2034:          } else {
                   2035:              sprintf(tmp_str, "Answer: %s",fmt_ans);
                   2036:          }
                   2037:          mvaddstr(S_ROW,COL_ONE,tmp_str);
                   2038:          
                   2039:          switch(entry.answers[num]) {
                   2040:            case 'Y': mvaddstr(S_ROW,COL_FOUR,"CORRECT         "); break;
                   2041:            case 'y': mvaddstr(S_ROW,COL_FOUR,"HANDIN CORRECT  "); break;
                   2042:            case '-': mvaddstr(S_ROW,COL_FOUR,"UNANSWERED      "); break;
                   2043:            case 'e': mvaddstr(S_ROW,COL_FOUR,"EXCUSED         "); break;
                   2044:            case 'E': mvaddstr(S_ROW,COL_FOUR,"EXCUSED         "); break;
                   2045:            case 'n': mvaddstr(S_ROW,COL_FOUR,"HANDIN INCORRECT"); break;
                   2046:            case 'N': mvaddstr(S_ROW,COL_FOUR,"INCORRECT       "); break;
                   2047:            default : if(entry.answers[num] >= '0' && entry.answers[num] <= '9') {
                   2048:                       sprintf(aLine,"HAND-GRADED %c/%c ",entry.answers[num],
                   2049: 			      header.weight[num]);
                   2050:                       mvaddstr(S_ROW,COL_FOUR,aLine);
                   2051:                      }
                   2052:                      break;
                   2053:          }
                   2054:          mvaddstr(S_ROW,COL_FIVE,"OPTION:");
                   2055:          
                   2056:          mvaddstr(O_ROW,COL_ONE,"M=Main menu");
                   2057:          if( second_scr && scr_idx == 1) {
                   2058:            mvaddstr(O_ROW,COL_TWO,"N=Next screen");
                   2059:            allow_n = 1;
                   2060:          }
                   2061:          if( second_scr && scr_idx == 2) {
                   2062:            mvaddstr(O_ROW,COL_TWO,"P=Prev screen");
                   2063:            allow_p = 1;
                   2064:          }
                   2065:          mvaddstr(O_ROW,COL_THREE,"X=eXit");
                   2066:          mvaddstr(O_ROW,COL_FOUR, "RETURN=Enter/Execute");
                   2067:          if ( problem->hint && 
                   2068: 	      ( 
                   2069: 	       (problem->show_hint <= problem->tries) || 
                   2070: 	       (entry.answers[num] == 'Y') || 
                   2071: 	       (entry.answers[num] == 'y')
                   2072: 	       ) 
                   2073: 	      )    { 
                   2074: 	   allow_hint=1;    mvaddstr(O_ROW,COL_FIVE,"H=Hint"); 
                   2075: 	 }
                   2076:          mvaddstr(X_ROW,COL_ONE,goto_str);
                   2077:          if (problem->next)
                   2078:                mvaddstr(X_ROW,COL_FOUR,"RETURN=next problem");
                   2079:             else
                   2080:                mvaddstr(X_ROW,COL_FOUR,"RETURN=main menu   ");
                   2081:          if ( problem->explain ) { allow_explain=1; mvaddstr(X_ROW,COL_FIVE,"E=Explain"); }
                   2082:    
                   2083:       }
                   2084:       get_input(S_ROW,COL_FIVE+7,buf,3);
                   2085:       display=0; usr_command=C_DONTCARE;
                   2086:       /* DEFAULT ACTIONS on empty input */
                   2087:       if(!strlen(buf)) { usr_command = (problem->next? C_FORWARD : C_MENU); } else {
                   2088:         switch(toupper(buf[0])) {
                   2089:          case 'X': usr_command=C_EXIT;    break;
                   2090:          case 'M': usr_command=C_MENU;    break;
                   2091: 	 case 'H': usr_command=C_HINT;    break;
                   2092:          case 'E': usr_command=C_EXPLAIN; break;
                   2093:          case 'N': if( allow_n ) { usr_command=C_NEXTSCR; } break;
                   2094:          case 'P': if( allow_p ) { usr_command=C_PREVSCR; } break;
                   2095:          default : sscanf(buf,"%d",&whereto);
                   2096:                   if(whereto >0 && whereto <= num_questions) usr_command=C_JUMP;
                   2097: 		  break;
                   2098:         }
                   2099:       }
                   2100: 
                   2101: 
                   2102:       /* PROCESS USER COMMAND */
                   2103:       switch(usr_command) {
                   2104:       case C_FORWARD: /* FORWARDS ONE */
                   2105:                 if (problem->next) {
                   2106:                    problem=problem->next; display=1; scr_idx = 1; num++;
                   2107:                 } else
                   2108:                    mvaddstr(X_ROW,COL_FOUR,"RETURN=main menu   ");
                   2109:                 break;
                   2110:       case C_HINT: /* HINT */
                   2111:                 if(allow_hint) {
                   2112:                   display_hint(problem->hint);
                   2113:                   display=1;
                   2114:                   allow_hint = 0;
                   2115:                 }
                   2116: 		break;
                   2117:       case C_EXPLAIN: /* Explain */
                   2118:                 if(allow_explain) {
                   2119:                   display_hint(problem->explain); display=1;
                   2120: 		  allow_explain=0;
                   2121: 		}
                   2122: 		break;
                   2123:       case C_NEXTSCR:  scr_idx = 2; display=1;
                   2124:                 break;
                   2125:       case C_PREVSCR:  scr_idx = 1; display=1;
                   2126:                 break;
                   2127:       case C_EXIT: /* EXIT SYSTEM */
                   2128:                 ex=1; problem=0; break;
                   2129: 
                   2130:       case C_MENU: /* RETURN TO MAIN MENU */
                   2131:                 problem=0;  break;
                   2132: 
                   2133:       case C_JUMP: /* JUMP TO SPECIFIC PROBLEM # */
                   2134:                    num=whereto-1;
                   2135:                    for (problem=first_problem; whereto > 1 && problem->next; whereto--)
                   2136:                       problem=problem->next;
                   2137:                    display=1;
                   2138:                    scr_idx = 1;
                   2139:                  break;
                   2140:       case C_TIME:     break;
                   2141:       case C_DONTCARE: break;
                   2142:       }
                   2143:    }
                   2144: 
                   2145:    /* FREE UP MALLOC'ED SPACE - VERY IMPORTANT */
                   2146:    capa_mfree(header.weight);
                   2147:    capa_mfree(header.partial_credit);
                   2148:    capa_mfree(entry.answers);
                   2149:    capa_mfree(entry.tries);
                   2150:    free_problems(first_problem);
                   2151: 
                   2152:    if (ex) properly_logout(student_number);
                   2153: }
                   2154: 
                   2155: /* -------------------------------------------------------------------------- */
                   2156: /* DISPLAY HELP SCREEN                                                        */
                   2157: /* -------------------------------------------------------------------------- */
                   2158: void                  /* RETURNS: (nothing)      */
                   2159: display_help()        /* ARGUMENTS: (none)       */
                   2160: {                     /* LOCAL VARIABLES:        */
                   2161:    FILE *fp;          /*    Welcome file pointer */
                   2162:    char  buf[255];    /*    Input buffer         */
                   2163: 
                   2164:    CLEAR();
                   2165:    if ((fp=fopen("help.msg","r"))!=NULL) {
                   2166:       while (fgets(buf,255,fp))  addstr(buf);
                   2167:       fclose(fp);
                   2168:    }
                   2169:    mypause(22,20);
                   2170: }
                   2171: 
                   2172: 
                   2173: /*   A class directory must have   */
                   2174: /*     records/                    */
                   2175: /*                                 */
                   2176: /*  returns: 0  structure is correct, but no set.db files */
                   2177: /*          -1  structure is not correct                  */
                   2178: /*          >=1 the last set.db                           */
                   2179: 
                   2180: int
                   2181: check_class_get_set(dir_path) char  *dir_path;
                   2182: {
                   2183:   char   f_name[1024];
                   2184:   int    set;
                   2185:   
                   2186:   if( capa_access(dir_path, F_OK) == 0 ) { /* class dir exists */
                   2187:     sprintf(f_name,"%s/records",dir_path);
                   2188:     if( capa_access(f_name, F_OK) == 0 ) { /* class/records dir exists */
                   2189:       for(set = 1; ; set++ ) {
                   2190:         sprintf(f_name,"%s/records/set%d.db",dir_path,set);
                   2191:         if(capa_access(f_name, F_OK) == -1 )  break;
                   2192:       }
                   2193:       set--;
                   2194:     } else {
                   2195:       set = -1;
                   2196:     }
                   2197:   } else {
                   2198:     set = -1;
                   2199:   } 
                   2200:   return (set);
                   2201: }
                   2202: /* -------------------------------------------------------------------------- */
                   2203: /* Get Exam and Quiz Path                                                     */
                   2204: /*   return  0, 1, 2, 3         */
                   2205: /* -------------------------------------------------------------------------- */
                   2206: int
                   2207: check_exam_quiz_f()
                   2208: {
                   2209:    char  buf[MAX_BUFFER_SIZE];
                   2210:    int   result = 0, configResult=0;
                   2211: 
                   2212: #ifdef LOGIN_DBUG
                   2213:    fprintf(dfp,"CHECK EXAM Access() success,and open(),%s\n",buf); fflush(dfp);
                   2214: #endif
                   2215:    configResult=read_capa_config("exam_path",buf);
                   2216:    if (configResult != 0 && configResult != -1) {
                   2217:      Exam_set = check_class_get_set(buf);
                   2218:      if(Exam_set > 0 )  {
                   2219:        result = 1;
                   2220:        sprintf(Exam_path,buf);
                   2221:      }
                   2222:    }
                   2223: #ifdef LOGIN_DBUG
                   2224:    fprintf(dfp,"CHECK EXAM = %d,%s\n", result,Exam_path); fflush(dfp);
                   2225: #endif
                   2226:    configResult=read_capa_config("quiz_path",buf);
                   2227:    if (configResult != 0 && configResult != -1) {
                   2228:      Quiz_set = check_class_get_set(buf);
                   2229:      if(Quiz_set > 0 )  {
                   2230:        result = (result | 2);
                   2231:        sprintf(Quiz_path,buf);
                   2232:      }
                   2233:    }
                   2234:    
                   2235:    return (result);
                   2236: }
                   2237: 
                   2238: /* -------------------------------------------------------------------------- */
                   2239: /* DISPLAY MAIN MENU                                                          */
                   2240: /* -------------------------------------------------------------------------- */
                   2241: void                  /* RETURNS: (nothing) */
                   2242: display_menu(student, exam_f, quiz_f) 
                   2243: T_student *student;
                   2244: int        exam_f, quiz_f;
                   2245: {
                   2246:    char buff[MAX_BUFFER_SIZE];
                   2247:    int  c_y,configResult,term_summary_button=1;
                   2248:    
                   2249:    configResult=read_capa_config("term_summary_button",buff);
                   2250:    if (configResult != 0 && configResult != -1 ) {
                   2251:      if (strcasecmp(buff,"no")==0) {
                   2252:        term_summary_button=0;
                   2253:      }
                   2254:    }
                   2255: 
                   2256:    CLEAR();
                   2257: 
                   2258:    mvaddstr(1,10,student->s_nm);
                   2259:    sprintf(buff,"Section: %d",student->s_sec);
                   2260:    mvaddstr(1,50,buff);
                   2261: 
                   2262:    mvaddstr( 4,25,"  MAIN MENU"); c_y = 6;
                   2263:    mvaddstr( c_y,25,"H=Help");    c_y++;
                   2264:    if (term_summary_button) { mvaddstr( c_y,25,"S=Summary"); c_y++; }
                   2265:    mvaddstr( c_y,25,"T=Try set"); c_y++;
                   2266:    mvaddstr( c_y,25,"V=View previous set"); c_y++;
                   2267:    if(exam_f) { mvaddstr( c_y,25,"E=view Exam summary"); c_y++; }
                   2268:    if(quiz_f) { mvaddstr( c_y,25,"Q=view Quiz summary"); c_y++; }
                   2269:    mvaddstr( c_y,25,"X=eXit system");
                   2270: 
                   2271:    mvaddstr(14,25,"COMMAND:");
                   2272: 
                   2273:    mvaddstr(17, 5,"Enter a command from the list above and press ENTER/RETURN");
                   2274: }
                   2275: 
                   2276: /* -------------------------------------------------------------------------- */
                   2277: /* CONTROL MAIN MENU SELECTIONS                                               */
                   2278: /* -------------------------------------------------------------------------- */
                   2279: void                                    /* RETURNS: (nothing)     */
                   2280: menu_main(student_number,set,section)   /* ARGUMENTS:             */
                   2281: char *student_number;                   /*    Student number      */
                   2282: int set;                                /*    Set number          */
                   2283: int section;                            /*    Section number      */
                   2284: {                                       /* LOCAL VARIABLES:       */
                   2285:    int       ex=0,                      /*    Exit system flag    */
                   2286:              cmd;                       /*    User command        */
                   2287:    char      buff[MAX_BUFFER_SIZE];     /*    User command buffer */
                   2288:    T_student a_student;
                   2289:    int       had_exam, had_quiz, outcome,configResult;
                   2290: 
                   2291: #ifdef LOGIN_DBUG
                   2292:    fprintf(dfp,"MENU in %s sec=%d\n", student_number,section); fflush(dfp);
                   2293: #endif
                   2294: 
                   2295:    outcome = check_exam_quiz_f();
                   2296:    had_exam = outcome & 1;
                   2297:    had_quiz = outcome & 2;
                   2298: 
                   2299: #ifdef LOGIN_DBUG
                   2300:    fprintf(dfp,"After check %d\n", outcome); fflush(dfp);
                   2301: #endif
                   2302: 
                   2303:    capa_get_student(student_number,&a_student);
                   2304:    
                   2305:    g_inhibit_response=capa_check_option(OPTION_INHIBIT_RESPONSE,set,section);
                   2306:    if (g_inhibit_response < 0 ) g_inhibit_response=0;
                   2307: 
                   2308:    display_menu(&a_student,had_exam, had_quiz);
                   2309:    while (!ex) {
                   2310:       do {
                   2311:          buff[0] = ' '; buff[1] = 0;
                   2312:          get_input(14,34,buff,1);  cmd=toupper(buff[0]);
                   2313:       } while (isspace(cmd));
                   2314:       move(14,35); clrtoeol();
                   2315:       /* PROCESS USER COMMAND */
                   2316:       switch(cmd) {
                   2317: 
                   2318:       case 'H': /* DISPLAY HELP */
                   2319:                 display_help();
                   2320:                 display_menu(&a_student,had_exam, had_quiz);
                   2321:                 break;
                   2322:  
                   2323:       case 'T': /* TRY CURRENT SET */
                   2324:                 try_set(student_number,set,&section);       
                   2325:                 display_menu(&a_student,had_exam, had_quiz);
                   2326:                 break;
                   2327: 
                   2328:       case 'V': /* VIEW PREVIOUS SET */
                   2329:                 view_previous(student_number,set,&section); 
                   2330:                 display_menu(&a_student,had_exam, had_quiz);
                   2331:                 break;
                   2332: 
                   2333:       case 'S': /* DISPLAY TERM SUMMARY */
                   2334: 	        configResult=read_capa_config("term_summary_button",buff);
                   2335: 		if (configResult != 0 && configResult != -1 ) {
                   2336: 		  if ((strcasecmp(buff,"no")==0)) {
                   2337: 		    break;
                   2338: 		  }
                   2339: 		}
                   2340: 		term_summary(student_number,set,&section,TERM_SUMMARY);  
                   2341: 		display_menu(&a_student,had_exam, had_quiz);
                   2342: 		break;
                   2343:       case 'E': /* VIEW EXAM SUMMARY */
                   2344:                 if( had_exam ) {
                   2345:                   chdir(Exam_path);
                   2346:                   term_summary(student_number,Exam_set,&section,EXAM_SUMMARY);  
                   2347:                   display_menu(&a_student,had_exam, had_quiz);
                   2348:                   chdir(Orig_path);
                   2349:                 }
                   2350:                 break;
                   2351:       case 'Q': /* VIEW QUIZ SUMMARY */
                   2352:                 if( had_quiz ) {
                   2353:                   chdir(Quiz_path);
                   2354:                   term_summary(student_number,Quiz_set,&section,QUIZ_SUMMARY);  
                   2355:                   display_menu(&a_student,had_exam, had_quiz);
                   2356:                   chdir(Orig_path);
                   2357:                 }
                   2358:                 break;
                   2359:       case EOF: /* EXIT SYSTEM */
                   2360:       case 'X': ex=1; break;
                   2361: 
                   2362:       default:  /* INVALID COMMAND */
                   2363:                 /* printf("Invalid choice\n"); */
                   2364:                 break;
                   2365:       }
                   2366:    }
                   2367: }
                   2368: 
                   2369: /* -------------------------------------------------------------------------- */
                   2370: /* DISPLAY WELCOME MESSAGE WHEN USER LOGS IN                                  */
                   2371: /* -------------------------------------------------------------------------- */
                   2372: void               /* RETURNS: (nothing)      */
                   2373: welcome()          /* ARGUMENTS:         */
                   2374: {                  /* LOCAL VARIABLES:        */
                   2375:    FILE *fp;       /*    Welcome file pointer */
                   2376:    char  buf[TMP_LINE_LENGTH]; /*    Input buffer         */
                   2377: 
                   2378:    CLEAR();
                   2379:    /* sprintf(buf,"This is your %d-time login to this set, good luck!",tries);
                   2380:    addstr(buf);
                   2381:    */
                   2382:    if ((fp=fopen("welcome.msg","r"))!=NULL) {
                   2383:       while (fgets(buf,TMP_LINE_LENGTH-1,fp)) addstr(buf);
                   2384:       fclose(fp);
                   2385:    }
                   2386: }
                   2387: 
                   2388: void print_version()
                   2389: {
                   2390:   printf("capalogin\n");
                   2391:   printf("       CAPA version %s, %s\n",CAPA_VER,COMPILE_DATE);
                   2392: }
                   2393: 
                   2394: /* ------------------------------------------------------------------------- */
                   2395: /* DRIVER: INITIALIZE AND GO TO LOGIN                                        */
                   2396: /* ------------------------------------------------------------------------- */
                   2397: int
                   2398: main(int argc, char **argv)
                   2399: {                            /* LOCAL VARIABLES:            */
                   2400:    char     student_number[MAX_STUDENT_NUMBER+1]; /* Student number         */
                   2401:    int      set,             /*    Set number               */
                   2402:             section=0,       /*    Section number           */
                   2403:             result;          /* stores result from read_capa_config */
                   2404:    char     filename[FILE_NAME_LENGTH];   /*    Question filename buffer */
                   2405: #if defined(NeXT)
                   2406:    char     cwd[FILE_NAME_LENGTH];
                   2407: #endif
                   2408:    char *class_path, buf[MAX_BUFFER_SIZE],*tty;
                   2409:    
                   2410:    
                   2411:    if (argc > 1) { if (strcmp(argv[1],"-v") == 0) {print_version(); exit(0); } }
                   2412: #ifdef LOGIN_DBUG
                   2413:    printf("Create login.DBUG file:: argc = %d\n",argc);
                   2414:    sprintf(filename,"login.DBUG");
                   2415:    if ((dfp=fopen(filename,"a"))==NULL) { printf("Error: can't open login debug\n"); return; }
                   2416: #endif /* LOGIN_DBUG */
                   2417:    /* GET CURRENT SET NUMBER */
                   2418:   for(set = 1; ; set++ ) {
                   2419:     sprintf(filename,"set%d.qz",set);
                   2420:     if(capa_access(filename, F_OK) == -1 )   break;
                   2421:   }
                   2422:   set--;
                   2423: #if defined(NeXT) 
                   2424:    class_path = getwd(cwd);
                   2425:    if( class_path ==  NULL ) class_path = cwd;
                   2426: #else
                   2427:    class_path = getcwd(NULL,512);
                   2428:    
                   2429: #endif 
                   2430:    sprintf(Orig_path,"%s",class_path);
                   2431:    free(class_path);
                   2432:    /* ---------------------------------------------- CURSES INITIALIZATION */
                   2433:    signal(SIGINT , kick_out);
                   2434:    signal(SIGALRM, kick_out);
                   2435:    signal(SIGFPE, SIG_IGN);
                   2436:    initscr(); savetty(); cbreak(); noecho();
                   2437:    time(&log_in_time);
                   2438:    strncpy(in_t,ctime(&log_in_time),31);
                   2439:    in_t[ strlen(in_t)-1 ]=0;    /* Trash newline */
                   2440:    tty=ttyname(0);
                   2441:    if ( tty == NULL ) {
                   2442:      strcpy(in_tty,"UNKNOWN");
                   2443:    } else {
                   2444:      strcpy(in_tty,tty);
                   2445:    }
                   2446:    result=read_capa_config("capalogin_goodbye_delay",buf);
                   2447:    if (result != 0 && result != -1) {
                   2448:      g_delay=atoi(buf);
                   2449:    } else {
                   2450:      g_delay=5;
                   2451:    }
                   2452:    result=read_capa_config("capalogin_inactivity_delay",buf);
                   2453:    if (result != 0 && result != -1) {
                   2454:      g_max_delay=atoi(buf);
                   2455:    } else {
                   2456:      g_max_delay=60;
                   2457:    }
                   2458:    welcome();
                   2459:    strcpy(student_number, login(&set,&section)); student_number[MAX_STUDENT_NUMBER] = 0;
                   2460: #ifdef LOGIN_DBUG
                   2461:    fprintf(dfp,"login return:SNum=%s, set=%d, sec=%d\n", student_number,set, section); fflush(dfp);
                   2462: #endif
                   2463:    menu_main(student_number,set,section);
                   2464: #ifdef LOGIN_DBUG
                   2465:    fclose(dfp);
                   2466: #endif
                   2467:    properly_logout(student_number);
                   2468:    return 0;
                   2469: }
                   2470: 

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