/* main code that implements the capa login shell
   Copyright (C) 1992-2000 Michigan State University

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

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

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

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

/* version 4.6 */

/* Jan 28  1997  I.T. */
/* July 23 1998  I.T. */

#ifdef NeXT
#include <stdlib.h>
#include <objc/zone.h>
#include <mach/mach.h>
#else
#include <malloc.h>
double atof();
#endif

#include <ctype.h>

#ifdef TRUE
#undef TRUE
#endif
#ifdef FALSE
#undef FALSE
#endif

#include <curses.h>

#if defined(__alpha) || defined(linux) 

#ifdef LOGIN_DBUG

#define NO_PIN
#define NO_DATE_CHECK
#define NO_DUP_CHECK

#endif

#include <curses.h>
#else
#if defined(__sun) || defined(hpux) || defined(AIX) || defined(IRIX)
#include <curses.h>  /* #include <stdio.h> */
#include <math.h>   /* MAXFLOAT */

#else

#include <bsd/curses.h>

#endif
#endif

#include <signal.h>
#include <time.h>
#include <math.h>
#include <string.h>
#include <unistd.h>
#include "capaToken.h"
#include "capaParser.h"
#include "capaCommon.h"

FILE      *dfp;

#define   TERM_SUMMARY    1
#define   EXAM_SUMMARY    2
#define   QUIZ_SUMMARY    3

#define   TRY_BOUND       99




#define   TYR_SET_MENU_MACRO(xxx)   {					\
 sprintf(aLine,"Total %d problems", num_questions); \
 if(xxx) { \
  mvaddstr(20,1,"Enter command  M,  A,  #,  T, or  X.");           mvaddstr(20,67,"COMMAND:"); \
  mvaddstr(21,1,"M=go to Main Menu  A=Answer    T=Time   RETURN=execute command"); \
 } else { \
  mvaddstr(20,1,"Enter command  M,  #,  T,  or  X.    ");          mvaddstr(20,67,"COMMAND:"); \
  mvaddstr(21,1,"M=go to Main Menu  T=Time               RETURN=execute command"); \
 }  \
 mvaddstr(22,1, "#=go to problem #  X=eXit CAPA"); \
 mvaddstr(23,1,aLine); }


#define   REVIEW_SET_MENU_MACRO()   {					\
 sprintf(aLine,"Total %d problems", num_questions); \
 mvaddstr(20,1,"Enter command  M,  #,  or  X.");              mvaddstr(20,67,"COMMAND:"); \
 mvaddstr(21,1,"M=go to Main Menu                    RETURN=execute command"); \
 mvaddstr(22,1,"#=go to problem #     X=eXit CAPA"); \
 mvaddstr(23,1,aLine); } 

#define   TYRSET_MENU( )   {					\
  mvaddstr(22,0,"Commands  :M = Main Menu   :7 = go to Problem 7            RETURN = Enter/Execute"); \
 }


#define   REVIEW_SET_MENU_MACRO()   {					\
 sprintf(aLine,"Total %d problems", num_questions); \
 mvaddstr(20,1,"Enter command  M,  #,  or  X.");              mvaddstr(20,67,"COMMAND:"); \
 mvaddstr(21,1,"M=go to Main Menu                    RETURN=execute command"); \
 mvaddstr(22,1,"#=go to problem #     X=eXit CAPA"); \
 mvaddstr(23,1,aLine); } 


#define DBUG_TSUMMARY    0

#define CLEAR()         clear(); refresh()
#define ADDCH(c)        addch(c); refresh()
#define CLRTOEOL()      clrtoeol(); refresh()
#define CR 13
#define LF 10
#define SCREEN_BUFFER_SIZE   2048

time_t   log_in_time, log_out_time;
char     in_t[32],    in_tty[32];
char     Orig_path[FILE_NAME_LENGTH], Exam_path[FILE_NAME_LENGTH], 
  Quiz_path[FILE_NAME_LENGTH];
int      Exam_set, Quiz_set;
int      g_inhibit_response;
int      g_delay; /* delay when logging out */
int      g_max_delay; /* max number of minutes to wait for input, kick_out()
                         after this much time */
/* Note: be careful to free entry answers */

/* ------------------------------------------------------------------------- */
/* WRITE OUTPUT (NICELY) TO THE SCREEN                                       */
/* ------------------------------------------------------------------------- */
void              /* RETURNS: (nothing)         */
wrap(str)         /* ARGUMENTS:                 */
char *str;        /*    Block of text to output */
{                 /* LOCAL VARIABLES:           */
   int y,x,len;   /*    Row,Col of screen       */
   int i,         /*    Next space              */
       j;         /*    Next char to print      */
  len=strlen(str);
  for (i=j=0; i<len; i++) {
    getyx(stdscr,y,x); 
    while (i<len && !isspace(str[i]))    i++;
    if (x+i-j > 78)  addch('\n');
    while (j<=i)     addch(str[j++]);
  }
}
int
total_lines(char *str)
{
  int  len,  lines_cnt=1;
  int  i, j, x=0;
  
  len=strlen(str);
  for(i=j=0;i<len;i++) {
    while (i<len && !isspace(str[i]))    i++;
    if (x+i-j > 78)  { lines_cnt++; x = 0; }
    while (j<=i)     { x++; if(str[j] == '\n') {lines_cnt++; x=0; }  j++; }
  }
  return (lines_cnt);
}

/* --------------------------------------------- */
/* */
#define    LINES_PER_SCREEN     20

int  display_prob_scr(char *str,int scr_idx)
{
  int  len, lines_cnt=0;
  int  i,j,y=0,x=0;
  int  break_pt, onscreen_pr;
  int  second_scr=0;

  if( str != NULL ) {
    lines_cnt = total_lines(str);
    if( lines_cnt > LINES_PER_SCREEN ) {
      second_scr = 1;
    } else {
      scr_idx = 1;
    }
    if( scr_idx == 1 ) {
      break_pt = LINES_PER_SCREEN + 1;
    } else { /* which line to break the problem text into two screens */
      if(lines_cnt>=40) { break_pt = LINES_PER_SCREEN; } else {
        if(lines_cnt==39) { break_pt = LINES_PER_SCREEN - 1; } else {
          if(lines_cnt==38) { break_pt = LINES_PER_SCREEN - 2; } else {
            break_pt = LINES_PER_SCREEN - 3;
          }
        }
      }
    }

#ifdef LOGIN_DBUG
   fprintf(dfp,"DISPLAY SCR IDX=%d total LineCnt=%d Line Break= %d:\n",scr_idx,lines_cnt,break_pt); fflush(dfp);
#endif

  /*  start to display the text on screen */

    lines_cnt = 1; x = y =0;
    len=strlen(str);
#ifdef LOGIN_DBUG
  fprintf(dfp,"SCR IDX=%d,leng=%d[[\n",scr_idx,len);
  fflush(dfp);
#endif  
    for(i=j=0;i<len;i++) {
      if( ( (scr_idx==1) && (lines_cnt < break_pt)) ||
          ((scr_idx==2) && (lines_cnt > break_pt) && (lines_cnt <= (break_pt+LINES_PER_SCREEN))) ) {
        getyx(stdscr,y,x);
	/*	if (x2>=x) x=x2; else x=x2+80;*/
      }
      while (i<len && !isspace(str[i]))    i++;
      onscreen_pr = 0;
#ifdef LOGIN_DBUG         
      fprintf(dfp,"\n[NewWord line=%d,x=%d,i=%d,j=%d,y=%d]",lines_cnt,x,i,j,y);
#endif         
      if (x+i-j > 78)  { /* line break  */
         if( (scr_idx==1) && (lines_cnt < break_pt) ) {
           addch('\n'); onscreen_pr = 1;
#ifdef LOGIN_DBUG         
           fprintf(dfp,"\n[LineCnt=%d,x=%d,i=%d,j=%d]",lines_cnt,x,i,j);
#endif         
         }
         if( (scr_idx==2) && (lines_cnt > break_pt) && (lines_cnt <= (break_pt+LINES_PER_SCREEN)) ) {
         
           addch('\n'); onscreen_pr = 1;
#ifdef LOGIN_DBUG         
           fprintf(dfp,"\n[LineCnt=%d,x=%d,i=%d,j=%d]",lines_cnt,x,i,j);
#endif         
         }
         lines_cnt++;
         if(onscreen_pr == 0 ) {
           x=0;
         }
      }
      while (j<=i)     { /* display on screen */
         onscreen_pr = 0;
         if( (scr_idx==1) && (lines_cnt < break_pt) ) {
           addch(str[j]);   /* display that character */
           onscreen_pr = 1;
#ifdef LOGIN_DBUG            
           fprintf(dfp,"%c",str[j]);
#endif 
         } 
         if( (scr_idx==2) && (lines_cnt > break_pt) && (lines_cnt <= (break_pt+LINES_PER_SCREEN)) ) {

           addch(str[j]); onscreen_pr = 1;
#ifdef LOGIN_DBUG            
           fprintf(dfp,"%c",str[j]);
#endif 
         }
         if( str[j] == '\n' )  {
          
#ifdef LOGIN_DBUG         
         fprintf(dfp,"<LineCnt=%d>[j=%d]",lines_cnt,j);
#endif
           if(onscreen_pr == 0 ) {
             x = 0;
           }
           lines_cnt++; 
         }
         if(onscreen_pr == 0 ) {
           x++;
         }
         j++;
       }
    }
#ifdef LOGIN_DBUG
  fprintf(dfp,"\n]]"); fflush(dfp);
#endif

  }
  return (second_scr);

}

/* ------------------------------------------------------------------------- */
/* DISPLAY FAREWELL MESSAGE WHEN USER GOT KICKED OUT                         */
/* ------------------------------------------------------------------------- */
void               /* RETURNS: (nothing)      */
#ifdef __sun
kick_out(int sig)
#else
kick_out()
#endif

{                  /* LOCAL VARIABLES:        */
   FILE *fp;       /*    Goodbye file pointer */
   char  buf[255]; /*    Input buffer         */

   /* DISPLAY EXIT MESSAGE */
   CLEAR();
   if ((fp=fopen("goodbye.msg","r"))!=NULL) {
      while (fgets(buf,255,fp))
         addstr(buf);
      fclose(fp);
   }
   sprintf(buf, "This message will last for only %d seconds.",g_delay);
   mvaddstr(22,20,buf); refresh();
   sleep(g_delay);
   /* mypause(22,20); */

   /* CURSES RESTORATION */
   resetty(); endwin();
   exit(1);
}


/* ------------------------------------------------------------------------- */
/* GET INPUT (NICELY) FROM A PLACE ON THE SCREEN                             */
/* ------------------------------------------------------------------------- */
void                     /* RETURNS: (nothing)             */
get_input(y,x,str,inmax) /* ARGUMENTS:                     */
int   y,x;               /*   Row,Col of screen to start   */
char *str;               /*   String buffer to fill in     */
int   inmax;             /*   Maximum number of characters */
{                        /* LOCAL VARIABLES:               */
   int  i=0,cx,cy;       /*   Position in buffer           */
   char c;               /*   Input character              */
   
   if (y && x)  move(y,x);
   CLRTOEOL();
   cx = x; cy = y;
#if defined( __alpha) || defined(__sun)
   while (1) {
      alarm(g_max_delay*60);
      c=getch();
      if (c==10 || c==13)   break;
      else if (c==8 || c==16 || c==127) {
         if (i>0) {
            i--;  cx--;  echo(); move(cy,cx);
            delch();  insch(' '); refresh(); noecho();
         } else
         beep();
      } else if (i>=inmax) { beep(); } else {
         str[i++] = c; cx++;
         echo(); ADDCH(c); noecho();
      }
   }
#else  
   while (1) {
      alarm(g_max_delay*60);
      c=getch();
      if (c==10 || c==13) break;
      else if (c==8 || c==16 || c==127) {
         if (i>0) {
            i--;  printf("%c %c",8,8); refresh();
         } else   printf("%c",7);
      } else if (i>=inmax) { printf("%c",7);
      } else {
         str[i++] = c;  ADDCH(c);
      }
   }
#endif
   str[i]=0;
}


void                     /* RETURNS: (nothing)             */
get_xinput(y,x,str,inmax)/* ARGUMENTS:                     */
int   y,x;               /*   Row,Col of screen to start   */
char *str;               /*   String buffer to fill in     */
int   inmax;             /*   Maximum number of characters */
{                        /* LOCAL VARIABLES:               */
   int  i=0,cx,cy;       /*   Position in buffer           */
   char c;               /*   Input character              */

   
   for(i=0;i<inmax;i++) { move(y,x+i); ADDCH(' '); }
   i=0;
   if (y && x)  move(y,x);refresh();
   cx = x; cy = y;
#if defined( __alpha) || defined(__sun)
   while (1) {      
     alarm(g_max_delay*60);
     c=getch();
     if (c==10 || c==13)   break;
     else if (c==8 || c==16 || c==127) {
       if (i>0) {
	 i--;  cx--;  echo(); move(cy,cx);
	 delch();  insch(' '); refresh(); noecho();
       } else
         beep();
     } else if (i>=inmax) { beep(); } else {
       str[i++] = c; cx++;
       echo(); ADDCH(c); noecho();
     }
   }
#else  
   while (1) {
     alarm(g_max_delay*60);
     c=getch();
     if (c==10 || c==13) break;
     else if (c==8 || c==16 || c==127) {
       if (i>0) {
	 i--;  printf("%c %c",8,8); refresh();
       } else   printf("%c",7);
     } else if (i>=inmax) { printf("%c",7);
     } else {
       str[i++] = c;  ADDCH(c);
     }
   }
#endif
   str[i]=0;
}

/*
void                     
input_pin(y,x,str,inmax) 
int   y,x;               
char *str;               
int   inmax;             
{                        
   int  i=0,cx,cy;       
   char c;               

   if (y && x)  move(y,x);
   cx = x; cy = y;
   CLRTOEOL();
#ifdef __alpha
   while (1) {
      c=getch();
      if (c==10 || c==13)   break;
      else if (c==8 || c==16 || c==127) {
         if (i>0) {
            i--;  cx--;  echo(); move(cy,cx);
            delch();  insch(' '); refresh(); noecho();
         } else
         beep();
      } else if (i>=inmax) { beep(); } else {
         str[i++] = c; cx++;
         echo(); ADDCH('*'); noecho();
      }
   }
#else  
   while (1) {
      c=getch();
      if (c==10 || c==13) break;
      else if (c==8 || c==16 || c==127) {
         if (i>0) {
            i--;  printf("%c %c",8,8); refresh();
         } else   printf("%c",7);
      } else if (i>=inmax) { printf("%c",7);
      } else {
         str[i++] = c;  ADDCH('*');
      }
   }
#endif
   str[i]=0;
}
*/

/* ------------------------------------------------------------------------- */
/* PAUSE UNTIL USER HITS A KEY                                               */
/* ------------------------------------------------------------------------- */
void         /* RETURNS: (nothing)   */
mypause(y,x) /* ARGUMENTS:           */
int y,x;     /*    Row,Col of screen */
{            /* LOCAL VARIABLES:     */
   char c;   /*    Input character   */

   mvaddstr(y,x,"Press ENTER/RETURN to continue");
   get_input(y,x+30,&c,0);
}

/* ------------------------------------------------------------------------- */
/* DISPLAY FAREWELL MESSAGE WHEN USER LOGS OUT                               */
/* ------------------------------------------------------------------------- */
void               /* RETURNS: (nothing)      */
properly_logout(student_number)       /* ARGUMENTS:      */
char *student_number;
{                  /* LOCAL VARIABLES:        */
   FILE  *fp;       /*    Goodbye file pointer */
   char   buf[255]; /*    Input buffer         */
   char  *out_t;
   char   filename[FILE_NAME_LENGTH];
   
   /* DISPLAY EXIT MESSAGE */
   CLEAR();
   time(&log_out_time);
   out_t=ctime(&log_out_time);
   out_t[ strlen(out_t)-1 ]=0; /* Trash newline */

   sprintf(filename,"records/duration.db");
   if ((fp=fopen(filename,"a"))==NULL) {
      printf("Error: can't open duration file\n");
      return; 
   }
   flockstream(fp);
   fprintf(fp,"%s\t%s\t%s\t%s\n",student_number,in_tty,in_t,out_t);
   funlockstream(fp);
   fclose(fp);


   if ((fp=fopen("goodbye.msg","r"))!=NULL) {
      while (fgets(buf,255,fp))
         addstr(buf);
      fclose(fp);
   }
   /* mypause(22,20); */
#ifndef NO_DUP_CHECK
   logout_check(student_number);
#endif

#ifndef LOGIN_DBUG
   sprintf(buf, "This message will last for only %d seconds.",g_delay);
   mvaddstr(22,20,buf); refresh();
   sleep(g_delay);
#endif

   /* CURSES RESTORATION */
   resetty(); endwin();
   exit(1);
}
/* ------------------------------------------------------------------------- */
/* Forbid duplicate login                                                    */
/* ------------------------------------------------------------------------- */
void               /* RETURNS: (nothing)      */
dup_login_out()       /* ARGUMENTS:                */
{                  /* LOCAL VARIABLES:        */
   FILE *fp;       /*    Goodbye file pointer */
   char  buf[255]; /*    Input buffer         */

   /* DISPLAY EXIT MESSAGE */
   CLEAR();
   if ((fp=fopen("third-login.msg","r"))!=NULL) {
      while (fgets(buf,255,fp))   addstr(buf);
      fclose(fp);
   }
   /* mypause(22,20);*/
   /* CURSES RESTORATION */
   sprintf(buf, "This message will last for only %d seconds.",g_delay);
   mvaddstr(22,20,buf); refresh();
   sleep(g_delay);
   resetty(); endwin();
   exit(1);
}

void               /* RETURNS: (nothing)      */
dup_login_warning()/* ARGUMENTS:              */
{                  /* LOCAL VARIABLES:        */
   FILE *fp;       /*    Welcome file pointer */
   char  buf[255]; /*    Input buffer         */

   CLEAR();
   if ((fp=fopen("second-login.msg","r"))!=NULL) {
      while (fgets(buf,255,fp))
         addstr(buf);
      fclose(fp);
   }
   mypause(22,20);
}

/* ------------------------------------------------------------------------- */
/* ALLOW USER TO LOG IN                                                      */
/* ------------------------------------------------------------------------- */
char                          /* RETURNS: Student number                     */
*login(maxset,section)        /* ARGUMENTS:                                  */
int *maxset;                  /*    Set number                               */
int *section;                 /*    Section number                           */
{                             /* LOCAL VARIABLES:                            */
   char    *student_number;   /*    Student number                           */
   int      guess,            /*    User-entered PIN                         */
            login_set;        /*    Set for which PIN is valid               */
   int      login_section = 0;
   char     buff[20];         /*    Input buffer                             */ 
   T_entry  entry;
   time_t   curtime;          /*    Current time                             */
   int      leng;
   T_student student_data;

#define    D_S_NUM_Y    11
#define    D_S_NUM_X    13

#define    D_PIN_Y      (D_S_NUM_Y + 2)
#define    D_PIN_X      (D_S_NUM_X + 10)
#define    D_EXIT_Y     (D_S_NUM_Y + 5)
#define    D_EXIT_X     (D_S_NUM_X + 6)
#define    IN_S_NUM_Y   (D_S_NUM_Y)
#define    IN_S_NUM_X   (D_S_NUM_X + 16)
#define    IN_PIN_Y     (D_PIN_Y)
#define    IN_PIN_X     (D_PIN_X + 9)
#define    M_INVALID_Y  (IN_PIN_Y + 1)
#define    M_INVALID_X  (IN_PIN_X)

   student_number = (char *)malloc( (MAX_STUDENT_NUMBER+4)*sizeof(char));
   /* LOOP UNTIL WE ARE LEGALLY LOGGED IN */
   do {
      mvaddstr(D_S_NUM_Y,D_S_NUM_X,"STUDENT NUMBER: ");
      mvaddstr(D_PIN_Y,D_PIN_X,"CAPA ID: ");
      mvaddstr(D_EXIT_Y,D_EXIT_X,"To exit system, just hit ENTER/RETURN");

#ifndef  NO_PIN
      /* LOOP UNTIL WE HAVE A STUDENT NUMBER AND PIN */
      do {
#endif  /* NO_PIN */

         /* LOOP UNTIL A LEGAL STUDENT NUMBER IS ENTERED */
         do {
            get_input(IN_S_NUM_Y,IN_S_NUM_X,buff, MAX_STUDENT_NUMBER);
#ifdef __sun
            if (!strlen(buff))    kick_out(0);
#else
            if (!strlen(buff))    kick_out();
#endif
            sscanf(buff,"%s",student_number); leng = strlen(student_number);
         } while (leng < MAX_STUDENT_NUMBER);

#ifndef  NO_PIN
         get_input(IN_PIN_Y,IN_PIN_X,buff,MAX_PIN_CHAR);
#ifdef __sun
         if (!strlen(buff))       kick_out(0);
#else
         if (!strlen(buff))       kick_out();
#endif
         sscanf(buff,"%d",&guess);
      } while (guess<1);
#endif   /* NO_PIN */

      student_number[strlen(student_number)] = 0;
      /* VERIFY PIN */

#ifdef  NO_PIN
login_set = 1;
#else 
login_set = capa_PIN(student_number,999,guess);
#endif  /* No_PIN */
      
#ifdef LOGIN_DBUG
  fprintf(dfp,"LOGIN:S=%s,Guess=%04d,Actual Pin=%04d,set=%d\n",
   student_number,guess,capa_PIN(student_number,1, 0), login_set);
  fprintf(dfp," PIN=%04d,%04d,%04d,%04d,%04d\n",
   capa_PIN(student_number,1, 0), capa_PIN(student_number,2, 0),capa_PIN(student_number,3, 0),
   capa_PIN(student_number,4, 0), capa_PIN(student_number,5, 0));
  fflush(dfp);
#endif
      if (!login_set) {
         mvaddstr(M_INVALID_Y,M_INVALID_X,   "INVALID LOGIN  ");
      } else {
         if ( login_set > 99 )  {
           mvaddstr(M_INVALID_Y,M_INVALID_X, "INCORRECT PIN  ");  login_set = 0;
         }
         if ( capa_get_student(student_number,&student_data) == 0 ) {
            mvaddstr(M_INVALID_Y,M_INVALID_X,"NO SUCH STUDENT");  login_set=0;
         } else {
            login_section = student_data.s_sec;
#ifdef LOGIN_DBUG
  fprintf(dfp, " Student in section %d\n",login_section);fflush(dfp);
#endif
            time(&curtime);
            if( capa_check_date(CHECK_OPEN_DATE,student_number,
				login_section,login_set) < 0 ) {
               mvaddstr(M_INVALID_Y,M_INVALID_X,"NOT YET OPEN!");  login_set=0;
            }
         }
      }
    } while ( !login_set );
#ifdef LOGIN_DBUG
  fprintf(dfp, "DEBUG:%s Access granted through set %d section %d\n",
    student_number, login_set, login_section);  fflush(dfp);
#endif
#ifndef NO_DUP_CHECK
    switch( login_check(student_number))  {
      case 0:
         mvaddstr(M_INVALID_Y,M_INVALID_X,"CANNOT LOGIN");  dup_login_out();
         break;
      case 1:
         mvaddstr(M_INVALID_Y,M_INVALID_X,"FIRST TIME LOGIN");
         break;
      case 2:
         mvaddstr(M_INVALID_Y,M_INVALID_X,"SECOND TIME LOGIN"); dup_login_warning( );
         break;
      case -1:
#ifdef __sun
        mvaddstr(M_INVALID_Y,M_INVALID_X,"FILE ERROR"); kick_out(0);
#else
        mvaddstr(M_INVALID_Y,M_INVALID_X,"FILE ERROR"); kick_out();
#endif
         break;
    }
#endif /* NO_DUP_CHECK */
   capa_get_entry(&entry,student_number,login_set);
   (*maxset) = login_set;
   (*section) = login_section;
   capa_mfree(entry.answers);
   capa_mfree(entry.tries);
   return (student_number);
}

/* ------------------------------------------------------------------------- */
/* LOG ANSWERS TO A FILE WITH TIMESTAMP                                      */
/* ------------------------------------------------------------------------- */
int                                                /* RETURNS: error code    */
log_attempt(student_number,set,section,log_string) /* ARGUMENTS:             */
char     student_number[MAX_STUDENT_NUMBER+1];     /*   Student number       */
int   set;                                         /*   Set number           */
int   section;                                     /*   Section number       */
char *log_string;                                  /*   Answer string to log */
{                                                  /* LOCAL VARIABLES:       */
   char   filename[FILE_NAME_LENGTH],              /*   Log filename buffer  */
         *ct;                                      /*   Current time string  */
   FILE  *fp;                                      /*   Log file pointer     */
   time_t t;                                       /*   Timestamp for log    */

   /* OPEN LOG FILE */

   sprintf(filename,"records/log%d.db",set);
   if ((fp=fopen(filename,"a"))==NULL) {
      printf("Error: can't open log file\n");
      return -1; 
   }

   /* CREATE LOG ENTRY */
   time(&t);
   ct=ctime(&t);
   ct[ strlen(ct)-1 ]=0; /* Trash newline */
   fprintf(fp,"%s %s %s\n",student_number,ct,log_string); fflush(fp);
   fclose(fp);
   return 0;
}

int  log_submissions(student_number,set,log_string)
char  student_number[MAX_STUDENT_NUMBER+1];     
int   set;  
char *log_string;                                
{                                                  
   char   filename[FILE_NAME_LENGTH], timeStr[FILE_NAME_LENGTH],buf2[MAX_BUFFER_SIZE];
   FILE  *fp;                                     
   time_t t;            
   struct tm     *tmtime;
   int do_log_submissions=1,result;
   char buf[MAX_BUFFER_SIZE];

   result=read_capa_config("do_log_submissions",buf);
   if (result != 0 && result != -1) {
     if (strcasecmp(buf2,"no")==0) {
       do_log_submissions=0;
     } 
   }
   if (!do_log_submissions) return 0;

   sprintf(filename,"records/submissions%d.db",set);
   if ((fp=fopen(filename,"a"))==NULL) {
     return (-1);
   }

   /* CREATE LOG ENTRY */
   time(&t);
   tmtime=localtime(&t);
   strftime(timeStr,FILE_NAME_LENGTH,"%d/%m %X",tmtime);
   /*ct[ strlen(ct)-1 ]=0;*/ /* Trash newline */
   /*protect_log_string(log_string);*/
   fprintf(fp,"%s\t%s\t%s\n",student_number,timeStr,log_string); fflush(fp);
   fclose(fp);
   return (0);
}

#define   C_FORWARD    1
#define   C_EXIT       2
#define   C_MENU       3
#define   C_HINT       4
#define   C_EXPLAIN    5
#define   C_ANSWER     6
#define   C_JUMP       7
#define   C_DONTCARE   8
#define   C_BACKWARD   9
#define   C_TIME       10
#define   C_NEXTSCR    11
#define   C_PREVSCR    12
#define   C_SUBJANS    13

/* ------------------------------------------------------------------------- */
/* DISPLAY SUMMARY OF SCORES FOR THE TERM                                    */
/* ------------------------------------------------------------------------- */
void                                     /* RETURNS: (nothing)          */
term_summary(student_number,set,section,type) /* ARGUMENTS:             */
char  *student_number;                   /*    Student Number           */
int    set;                              /*    Set number               */
int   *section;                          /*    Section Number           */
int    type;
{                                        /* LOCAL VARIABLES:            */
   int      set_idx,                     /*    Set counter              */
            i,                           /*    Question counter         */
            tmp,                         /*    Question correct flag    */
            set_score,                   /*    Score on a set           */
            term_score=0,                /*    Total points received    */
            term_total=0,                /*    Total points possible    */
            result,
            tot_num_sets=0;
   T_entry  entry;                       /*    Database entry for a set */
   char     buf[MAX_BUFFER_SIZE], buf2[MAX_BUFFER_SIZE];
   T_header header;                      /*    Problem set header       */
   int      topset=1,                    /*    First displayed set      */
            bottomset,                   /*    Last displayed set       */
            done=0,                      /*    Done flag                */
            line, col;
   int      probs_in_set[MAX_BUFFER_SIZE],/*    # problem set questions  */
            start_at[MAX_BUFFER_SIZE],
            valid_wgt[SMALL_LINE_BUFFER],
            a_valid_wgt,set_start_line,
	    usr_command,inhibit_response;

   /* CALCULATE TERM TOTALS */
  start_at[0] = -2;
  probs_in_set[0]= 0;
  for (set_idx=1; set_idx<=set; set_idx++) {
    if (capa_get_header(&header,set_idx))  return;
    if ( capa_check_date(CHECK_OPEN_DATE,student_number,*section,set_idx) < 0 ) 
      continue;
    tot_num_sets++;
    capa_get_entry(&entry,student_number,set_idx);
    sscanf(header.num_questions,"%d", &(probs_in_set[set_idx]) );
    start_at[set_idx] = start_at[set_idx-1]+2*(1+probs_in_set[set_idx-1]/50);
    if ((start_at[set_idx]%12)+2*(1+probs_in_set[set_idx]/50) > 12)
         start_at[set_idx] = 12*(1+start_at[set_idx]/12);
    valid_wgt[set_idx] = 0;
    for (i=0; i<probs_in_set[set_idx]; i++) {
      valid_wgt[set_idx] +=  (header.weight[i] - '0');
      if((entry.answers[i]=='Y') || (entry.answers[i]=='y'))  
	term_score += (header.weight[i]-'0');
      if((entry.answers[i]=='E') || (entry.answers[i]=='e'))  
	valid_wgt[set_idx] -= (header.weight[i] - '0');
      if((entry.answers[i]>='0') && (entry.answers[i]<='9'))  
	term_score += (entry.answers[i] - '0');
    }
    term_total += valid_wgt[set_idx];
    capa_mfree(header.weight);
    capa_mfree(header.partial_credit);
    capa_mfree(entry.answers);
    capa_mfree(entry.tries);
  }

   /* FIND TOPSET */
   line = 12*(start_at[set]/12);      /* Top line # of last screen */
   for (topset=set; topset>1 && start_at[topset-1]>=line; topset--);

   /* SHOW HEADER */
   CLEAR();
   switch(type) {
     case TERM_SUMMARY:    mvaddstr(1,30,"TERM SUMMARY"); break;
     case EXAM_SUMMARY:    mvaddstr(1,30,"EXAM SUMMARY"); break;
     case QUIZ_SUMMARY:    mvaddstr(1,30,"QUIZ SUMMARY"); break;
   }
   mvaddstr(3,22,"         1         2         3         4         5");
   mvaddstr(4,22,"12345678901234567890123456789012345678901234567890");

   /* DISPLAY COMMAND MENU */
   mvaddstr(21,1,"Enter a command from the list below and press ENTER/RETURN    COMMAND:");
   mvaddstr(22,1,"M =Go to main menu  N =Next Page  P =Prev Page");
   /* mvaddstr(22,1,"X =eXit M =Go to main menu  N =Next Page  P =Prev Page"); */
   refresh();

   /* SHOW TOTALS */
   /* if capalogin_show_summary_score is set to none don't show it */
   if (term_total > 0 ) {
     sprintf(buf,"%d sets, total=%3d/%3d (%d%%)", tot_num_sets, term_score, term_total,
	     100*term_score/term_total);
   } else {
     sprintf(buf,"%d sets, total=%3d/%3d", tot_num_sets, term_score, term_total);
   }
   result=read_capa_config("capalogin_show_summary_score",buf2);
   if (result != 0 && result != -1) {
     if (strcasecmp(buf2,"none")==0) {
     } else {
       mvaddstr(19,1,buf);
     }
   } else {
     mvaddstr(19,1,buf);
   }

   /* LOOP UNTIL DONE */
  while (!done) {
    /* PRINT 1 LINE SUMMARY PER SET */
    line=5;
    for (set_idx=topset; set_idx<=set; set_idx++) {
      /* don't show summary for set if inhibit response is set*/
      inhibit_response=capa_check_option(OPTION_INHIBIT_RESPONSE,set_idx,*section);
      if (inhibit_response > 0) continue;
      if ( capa_check_date(CHECK_OPEN_DATE,student_number,*section,set_idx) < 0 ) 
	continue;
      set_score=0;
      set_start_line=line;
    /* Stop if not enough lines to summarize set */
      if (line+2*(probs_in_set[set_idx]/50)>16)   break;
      capa_get_header(&header,set_idx);
      capa_get_entry(&entry,student_number,set_idx);
      a_valid_wgt = 0;
       for (i=0, col=0; i<probs_in_set[set_idx]; i++) {
         tmp=0; a_valid_wgt += (header.weight[i] - '0');
         move(line,  22+col); addch(entry.answers[i]);
         move(line+1,22+col); addch(header.weight[i]);
         switch(entry.answers[i]) {
           case 'Y': tmp=header.weight[i] -'0'; break; /* Answer correct */
           case 'y': tmp=header.weight[i] -'0'; break; /* Grading correct */
           case '-': break;        /* Not answered    */
           case 'N': break;        /* Answer incorrect */
           case 'n': break;        /* Grading incorrect */
           case 'e': a_valid_wgt -= (header.weight[i] - '0'); break;  /* Excuse    */
           case 'E': a_valid_wgt -= (header.weight[i] - '0'); break;  /* Excuse    */
           default : if( entry.answers[i] >= '0' && entry.answers[i] <= '9' ) {
                       tmp = entry.answers[i] - '0';
                     }
                     break;
         }
         set_score  += tmp; col++;
         if (!((i+1)%50)) { line += 2; col = 0; }
       }
       capa_mfree(header.weight);
       capa_mfree(header.partial_credit);
       capa_mfree(entry.answers);
       capa_mfree(entry.tries);
       move(line, 22+col);   CLRTOEOL();
       move(line+1, 22+col); CLRTOEOL();
       if(a_valid_wgt == 0) {
         set_score=0;
         sprintf(buf,"%3d:%3d/%3d(%3d%%)  ",set_idx,set_score,a_valid_wgt,set_score);
         mvaddstr(set_start_line,1,buf);
       } else {
         sprintf(buf,"%3d:%3d/%3d(%3d%%)  ",set_idx,set_score,a_valid_wgt,100*set_score/a_valid_wgt);
         mvaddstr(set_start_line,1,buf);
       }
       line += 2;
    }
    bottomset=set_idx-1;

      /* Blank out any extra lines */
    if (line < 16) {
     for (set_idx=line; set_idx<=16; set_idx++) {
       move(set_idx,1);
       CLRTOEOL();
     }
    }

      /* PROCESS USER COMMAND */
      get_input(21,72,buf,1);
      if(!strlen(buf)) { usr_command = C_FORWARD; } else {
        
          switch(toupper(buf[0])) {
           /* case 'X': usr_command=C_EXIT;    break; */
           case 'M': usr_command=C_MENU;    break;
	   case 'P': usr_command=C_BACKWARD; break;
           default : usr_command=C_FORWARD;    break;
          }
      }

      
      switch(usr_command) {
      case C_DONTCARE: break;
      case C_FORWARD: /* Forwards */
                if (bottomset<set) { topset=bottomset+1; } else { done=1; }
                break;
      
      case C_BACKWARD: /* Backwards */
                if (topset<2) break;
                line = 12*(start_at[topset-1]/12); /* Top line # of prev screen */
                for (; topset>1 && start_at[topset-1]>=line; topset--);
                break;

      case C_MENU: /* Menu */
                done=1;
                break;
      case C_EXIT: /* Exit */
                properly_logout(student_number);
                break;
      default:  /* Invalid command */
                break;
      }
   }
}

void
display_hint(char *h)
{

  CLEAR();

  wrap(h);
  mypause(22,20);
}

#define   A_ROW    20
#define   S_ROW    21
#define   O_ROW    22
#define   X_ROW    23

#define   A_COL    14
#define   S_COL    46
#define   H_COL    24
#define   E_COL    39
#define   X_COL    8
#define   R_COL    57
#define   U_ANS_CHAR  32

/* =============================================================================
0001234567890123456789012345678901234567890123456789012345678901234567890123456789
A
S1OPTION/ANSWER 12345678901234 -----            *Unanswered
O2Options :M = Main Menu  :7 = go to #7  :N = Next screen  RETURN = Enter/Execute
X3        :X = eXit       :H = Show Hint :E = Explain      RETURN = Next Problem
  0123456789012345678901234567890123456789012345678901234567890
          ^     ^         ^              ^      ^          ^
          X     A         H              E      S          R
*/
int  show_prior_response(Problem_t *p,int hgr,int prev_ans,int tried,int *allow_h)
{
  char     *c_answer_str, tmp_str[MAX_BUFFER_SIZE];
  char     *response="Incorrect",*answered="Answered";
  int       can_answer;
  
  if( hgr == '0' || p->ans_type==ANSWER_IS_SUBJECTIVE) {
    switch(prev_ans) {
      case 'Y': can_answer=NAY; *allow_h=1;
                c_answer_str = answers_string(ANSWER_STRING_MODE,p);
                move(A_ROW,A_COL); clrtoeol();  
                mvaddstr(A_ROW,A_COL,c_answer_str); capa_mfree(c_answer_str);
                move(S_ROW,S_COL); clrtoeol();
                mvaddstr(S_ROW,S_COL,"**Correct              "); break;
      case 'y': can_answer=NAY; *allow_h=1;
                c_answer_str = answers_string(ANSWER_STRING_MODE,p);
                move(A_ROW,A_COL); clrtoeol();
                mvaddstr(A_ROW,A_COL,c_answer_str); capa_mfree(c_answer_str);
                move(S_ROW,S_COL); clrtoeol();
                mvaddstr(S_ROW,S_COL,"*Hand-graded Correct      "); break;
      case '-': can_answer=YAK; move(S_ROW,S_COL); clrtoeol();
                mvaddstr(S_ROW,S_COL,"*Unanswered               "); break;
      case 'E': can_answer=NAY; move(S_ROW,S_COL); clrtoeol();
                mvaddstr(S_ROW,S_COL,"*Excused                  "); break;
      case 'e': can_answer=NAY; move(S_ROW,S_COL); clrtoeol();
                mvaddstr(S_ROW,S_COL,"*Excused                  "); break;
      case 'n': can_answer=NAY; move(S_ROW,S_COL); clrtoeol();
                mvaddstr(S_ROW,S_COL,"*Hand-graded Incorrect    "); break;
    case '0': case '1': case '2': case '3': case '4': case '5': 
    case '6': case '7': case '8': case '9':
      response=answered;
    case 'N':   if ( tried < p->tries ) {
                  can_answer=YAK;
		  if( (p->tries - tried) == 1 ) {
		    sprintf(tmp_str,"*%s, ONE try left!!",response);
		  } else {
		    sprintf(tmp_str,"*%s, tries %2d/%2d   ",response,tried,p->tries);
		  }
                } else {
		  can_answer=NAY;
		  sprintf(tmp_str,  "*%s, no more tries",response);
		}
                move(S_ROW,S_COL); clrtoeol();
                mvaddstr(S_ROW,S_COL,tmp_str); 
                if( (can_answer == YAK) && (p->ans_op == ANS_AND) && (p->ans_cnt > 1)) {
                   sprintf(tmp_str, " Entering answer   1 of %3d     ",p->ans_cnt);
                   mvaddstr(A_ROW,S_COL,tmp_str);
                }
                break;
    }
  } else {  /* hand graded question */
    can_answer=NAY;
    move(S_ROW,S_COL); clrtoeol();
    mvaddstr(S_ROW,S_COL,"*Hand-graded question     ");
  }
  /* ------------------------------------------------------------------ */
  if (*allow_h && 
      p->hint && 
      (
       ( p->show_hint <= tried ) || 
       ( prev_ans == 'y' ) ||
       ( prev_ans == 'Y' )
       )
      ) {
    mvaddstr(X_ROW,H_COL,":H = Show Hint");
  } else {
    *allow_h = 0;
  }
  if (p->next)
    mvaddstr(X_ROW,R_COL,"RETURN = Next Problem");
  else
    mvaddstr(X_ROW,R_COL,"RETURN = Main Menu   ");
  
  return (can_answer);
  
}
int  show_prior_inhibited_response(Problem_t *p,int hgr,int prev_ans,int tried,
				   int *allow_h)
{
  char     tmp_str[MAX_BUFFER_SIZE];
  int      can_answer;
  
  if( hgr == '0' ) {
    switch(prev_ans) {
      case '-': can_answer=YAK; move(S_ROW,S_COL); clrtoeol();
                mvaddstr(S_ROW,S_COL,"*Unanswered               "); break;
      case 'E':
      case 'e':
      case 'n':
      case 'y': 
      case 'Y': 
      case 'N': if ( tried < p->tries ) {
	          can_answer=YAK;
		  if( (p->tries - tried) == 1 ) {
		    sprintf(tmp_str,"*Answered, ONE try left!! ");
		  } else {
		    sprintf(tmp_str,"*Answered, tries %2d/%2d    ",tried,p->tries);
		  }
                } else {
		  can_answer=NAY;
		  sprintf(tmp_str,  "*Answered, no more tries ");
		}
                move(S_ROW,S_COL); clrtoeol();
                mvaddstr(S_ROW,S_COL,tmp_str); break;
           
    }
  } else {  /* hand graded question */
    can_answer=NAY;
    move(S_ROW,S_COL); clrtoeol();
    mvaddstr(S_ROW,S_COL,"*Hand-graded question     ");
  }
  /* ------------------------------------------------------------------ */
  if (*allow_h && p->hint && ( p->show_hint <= tried)){
    mvaddstr(X_ROW,H_COL,":H = Show Hint");
  } else {
    *allow_h = 0;
  }
  if (p->next)
    mvaddstr(X_ROW,R_COL,"RETURN = Next Problem");
  else
    mvaddstr(X_ROW,R_COL,"RETURN = Main Menu   ");
  
  return (can_answer);
  
}
/* -------------------------------------------- dbug --------------------- */
void
print_unit_components(FILE *fp,Unit_t *t) 
{
  Unit_E  *ue_p;

  fprintf(fp,"  Unit::[%s] = %g * ", t->u_symbol, t->u_scale);
  for(ue_p=t->u_list; ue_p ; ue_p = ue_p->ue_nextp) {
    fprintf(fp,"(%g*%s^%g) ",ue_p->ue_scale,ue_p->ue_symbol,ue_p->ue_exp);
  }
  fprintf(fp,"\n"); fflush(fp);

}


/*#define    ANSWER_STRING_LENG       64*/
#define    UNIT_STRING_LENG         64
#define    FORMAT_STRING_LENG       32

/* ------------------------------------------------------------------- */
int  give_response(Problem_t *p,char **a,int cnt,int *tried,int *log_char)
{
  int      can_answer;
  char     tmp_str[MAX_BUFFER_SIZE], *c_answer_str;
  char    *error=NULL;

  switch( capa_check_answers(p,a,cnt,&error) ) {

    case  EXACT_ANS:  move(A_ROW,S_COL); clrtoeol();
                      mvaddstr(A_ROW,S_COL,"*Yes Computer gets:"); 
                      c_answer_str = answers_string(ANSWER_STRING_MODE, p);
                      move(S_ROW,S_COL); clrtoeol();
                      mvaddstr(S_ROW,S_COL,c_answer_str);
                      capa_mfree((char *)c_answer_str);
                     *log_char='Y'; can_answer=NAY;
                      if( *tried < TRY_BOUND)  (*tried)++;
                      break;
    case  APPROX_ANS: 
                      move(A_ROW,S_COL); clrtoeol();
                      mvaddstr(A_ROW,S_COL,"*Yes Computer gets:");
                      c_answer_str = answers_string(ANSWER_STRING_MODE, p);
                      if(cnt == 1 ) {
                        move(S_ROW,S_COL); clrtoeol();
                        mvaddstr(S_ROW,S_COL,c_answer_str);
                      } else {  /* more than one answer to check ANS_AND */
                        move(S_ROW,S_COL); clrtoeol();
                        mvaddstr(S_ROW,S_COL,"*Yes Correct Answers See Above");
                        move(A_ROW,A_COL); clrtoeol();
                        mvaddstr(A_ROW,A_COL,c_answer_str);
                      }
                      capa_mfree((char *)c_answer_str);
                     *log_char='Y'; can_answer=NAY;
                      if(*tried < TRY_BOUND)  (*tried)++;
                      break;
    case  WANTED_NUMERIC:   move(S_ROW,S_COL); clrtoeol();  
                      mvaddstr(S_ROW,S_COL,"*Enter a Number Ans");
                     *log_char='S'; can_answer=YAK;
                      break;
    case  SIG_FAIL:   move(S_ROW,S_COL); clrtoeol();  
                      mvaddstr(S_ROW,S_COL,"*Adjust Sig. Figs. ");
                     *log_char='S'; can_answer=YAK;
		      capa_mfree(error);
                      break;
    case  UNIT_FAIL:  move(S_ROW,S_COL); clrtoeol();  
                      mvaddstr(S_ROW,S_COL,"*Units incorrect   ");
                     *log_char='U'; can_answer=YAK;
		      capa_mfree(error);
                      break;
    case  UNIT_NOTNEEDED:  move(S_ROW,S_COL); clrtoeol();  
                      mvaddstr(S_ROW,S_COL,"*Only a number required");
                     *log_char='U'; can_answer=YAK;
		      capa_mfree(error);
                      break;
    case  NO_UNIT:    move(S_ROW,S_COL); clrtoeol();  
                      mvaddstr(S_ROW,S_COL,"*Units required    ");
                     *log_char='u'; can_answer=YAK;
                      break;
    case  BAD_FORMULA:move(S_ROW,S_COL); clrtoeol();  
                      mvaddstr(S_ROW,S_COL,"*Unable to interpret formula");
                     *log_char='F'; can_answer=YAK;
                      break;
    case  ANS_CNT_NOT_MATCH:
                      move(S_ROW,S_COL); clrtoeol();  
                      mvaddstr(S_ROW,S_COL,"*Invalid number of answers");
                     *log_char='C'; can_answer=YAK;
                      break;
    case  INCORRECT: 
                      if(*tried < TRY_BOUND)  (*tried)++;
		      if ( *tried < p->tries ) {
			can_answer=YAK;
			if( (p->tries - *tried) == 1 ) {
			  sprintf(tmp_str,"*Incorrect, ONE try left!!");
			} else {
			  sprintf(tmp_str,"*Incorrect, tries %2d/%2d   ",*tried,p->tries);
			}
		      } else {
			can_answer=NAY;
			sprintf(tmp_str,  "*Incorrect, no more tries");
		      }
                      move(S_ROW,S_COL); clrtoeol(); 
                      mvaddstr(S_ROW,S_COL, tmp_str);
                      if( (can_answer == YAK) && (p->ans_op == ANS_AND) && (p->ans_cnt > 1)  ) {
                         sprintf(tmp_str, " Entering answer   1 of %3d     ",p->ans_cnt);
                         mvaddstr(A_ROW,S_COL,tmp_str);
                      }
	             *log_char='N';
	              break;
  }
   
  return (can_answer);
}

int  give_inhibited_response(Problem_t *p,char **a,int cnt,int *tried,int *log_char)
{
  int      can_answer;
  char     tmp_str[MAX_BUFFER_SIZE];
  char    *error=NULL;

  switch( capa_check_answers(p,a,cnt,&error) ) {


    case  EXACT_ANS:  *log_char='Y'; break;
    case  APPROX_ANS: *log_char='Y'; break;
    case  SIG_FAIL:   *log_char='S'; capa_mfree(error); break;
    case  UNIT_FAIL:  *log_char='U'; capa_mfree(error); break;
    case  UNIT_NOTNEEDED: *log_char='U'; capa_mfree(error); break;
    case  NO_UNIT:    *log_char='u'; break;
    case  BAD_FORMULA:*log_char='F'; break;
    case  INCORRECT:  *log_char='N'; break;
    case  WANTED_NUMERIC:  *log_char='s'; break;
    case ANS_CNT_NOT_MATCH: *log_char='C'; break;
  }
  
  if(*tried < TRY_BOUND)  (*tried)++;
  if ( *tried < p->tries ) {
    can_answer=YAK;
    if( (p->tries - *tried) == 1 ) {
      sprintf(tmp_str,"*Answered, ONE try left!! ");
    } else {
      sprintf(tmp_str,"*Answered, tries %2d/%2d    ",*tried,p->tries);
    }
  } else {
    can_answer=NAY;
    sprintf(tmp_str,  "*Answered, no more tries ");
  }
  move(S_ROW,S_COL); clrtoeol(); 
  mvaddstr(S_ROW,S_COL, tmp_str);
  return (can_answer);
}

int  ask_what_prob(int q_cnt, char *ans)
{
  int  not_ok=1,num,anslength,i,j;
  char buf[5],buf2[MAX_BUFFER_SIZE];
  
  move(14,35); clrtoeol();
  move(17,5);  clrtoeol();
  do {
     move(14,35); clrtoeol();
     move(15,0);  clrtoeol();
     mvaddstr(15,13,"What problem number:");
     move(17,0);  clrtoeol();
     mvaddstr(17,16,"         1         2         3         4         5");
     mvaddstr(18,16,"12345678901234567890123456789012345678901234567890");
     anslength=strlen(ans);
     for(i=0;i<=(anslength/50);i++) {
       if ( g_inhibit_response ) {
	 for(j=50*i;(j<((i+1)*50))&&(j<anslength);j++) {
	   if (ans[j]=='-') 
	     buf2[j-(50*i)]='-';
	   else
	     buf2[j-(50*i)]='A';
	 }
	 buf2[j-(50*i)]='\0';
       } else {
	 strncpy(buf2,&(ans[50*i]),50);
       }
       buf2[50]='\0';
       mvaddstr(19+i,16,buf2);
       if (anslength > 50 ) {
	 sprintf(buf2,"%3d-%3d",i*50+1,(i+1)*50);
	 mvaddstr(19+i,5,buf2);
       }
     }
     do { get_input(15,34,buf,4); } while(!strlen(buf));
     sscanf(buf,"%d",&num);
     if (num<1 || num>q_cnt) {
        move(21,5); clrtoeol();
        mvaddstr(21,15,"  Error: Invalid problem number\n");
     } else {
        not_ok = 0;
     }
  } while (not_ok);

  return (num);
}

/* gather subjective answers from student */

#define    BS    8
#define    DEL   127
#define    ESC   27

#define    COLON 58

#define EDIT_HEIGHT 21
#define EDIT_WIDTH 80
#define MENULINE EDIT_HEIGHT

void refresh_editor (char **sbuf_pp,int cx,int cy) {
  int i;
  CLEAR();
  echo();
  mvaddstr(MENULINE,0,"Type in the answer, use up, down, left, right keys to move curser");
  mvaddstr(MENULINE+1,0,"Enter ctrl-e to exit and submit answer");
  mvaddstr(MENULINE+2,0,"Enter ctrl-f to forget answer");
  for(i=0;i<EDIT_HEIGHT;i++) { mvaddstr(i,0,sbuf_pp[i]); }
  move(cy,cx); refresh(); noecho();
}

void init_editor(char*** sbuf_pp)
{
  int   ww=EDIT_WIDTH, hh=EDIT_HEIGHT,i;
  *sbuf_pp = (char **)capa_malloc(sizeof(char *),hh);
  for(i=0;i<hh;i++) {
    (*sbuf_pp)[i] = (char *)capa_malloc(sizeof(char)*ww+1,1);
  }
  CLEAR();echo();
  mvaddstr(MENULINE,0,"Type in the answer, use up, down, left, right keys to move cursor");
  mvaddstr(MENULINE+1,0,"Enter ctrl-e to exit and submit answer");
  mvaddstr(MENULINE+2,0,"Enter ctrl-f to forget answer");
  move(0,0); refresh(); noecho();
}

void remove_character(char** sbuf_pp,int *cx,int *cy)
{
  int sx=(*cx)-1,sy=*cy;
  char temp,*temp_p;
  if (*cx==0) { 
    int abovelen,curlen,diff,i,j;
    if (*cy==0) { beep();return;}
    abovelen=strlen(sbuf_pp[(*cy-1)]);
    curlen=strlen(sbuf_pp[*cy]);
    if (abovelen > 0) sbuf_pp[(*cy)-1][abovelen-1]='\0';
    if ((abovelen+curlen) < EDIT_WIDTH) {
      strcat(sbuf_pp[(*cy)-1],sbuf_pp[*cy]);
      memset(sbuf_pp[(*cy)],'\0',EDIT_WIDTH+1);
      temp_p=sbuf_pp[*cy];
      i=*cy;
      while(i<EDIT_HEIGHT-1) {
	sbuf_pp[i]=sbuf_pp[i+1];
	echo();move(i,0);CLRTOEOL();mvaddstr(i,0,sbuf_pp[i]);noecho();
	i++;
      }
      sbuf_pp[EDIT_HEIGHT-1]=temp_p;
      echo();move(EDIT_HEIGHT-1,0);CLRTOEOL();noecho();
    } else {
      diff=EDIT_WIDTH-abovelen;
      strncat(sbuf_pp[(*cy)-1],sbuf_pp[*cy],diff);
      i=diff;j=0;
      while(sbuf_pp[*cy][i]!='\0') {
	sbuf_pp[*cy][j]=sbuf_pp[*cy][i];
	i++;j++;
      }
      memset(&(sbuf_pp[(*cy)][j]),'\0',EDIT_WIDTH+1-j);
    }
    echo();move(*cy,0); CLRTOEOL(); mvaddstr(*cy,0,sbuf_pp[*cy]);noecho();
    (*cy)--;
    echo();move(*cy,0); CLRTOEOL(); mvaddstr(*cy,0,sbuf_pp[*cy]);noecho();
    if ( EDIT_WIDTH == ((*cx)=(abovelen-1))) (*cx)--;
    if (abovelen==0) *cx=0;
    echo();move(*cy,*cx);noecho();
  } else {
    echo();move(sy,sx);noecho();
    temp=sbuf_pp[sy][sx]=sbuf_pp[sy][sx+1];
    sx++;
    while(temp!='\0') {
      echo(); ADDCH(temp); noecho();
      temp=sbuf_pp[sy][sx]=sbuf_pp[sy][sx+1];
      sx++;
    }
    echo(); ADDCH(' '); noecho();
    (*cx)--;
  }
  echo();move(*cy,*cx);noecho();
}

void break_line      (char** sbuf_pp,int *cx,int *cy)
{
  int sx=*cx,sy=*cy,i;
  if (sy < EDIT_HEIGHT-1) {
    capa_mfree(sbuf_pp[EDIT_HEIGHT-1]);
    i=EDIT_HEIGHT-1;
    while (i-1 > sy) {
      sbuf_pp[i]=sbuf_pp[i-1];
      move(i,0);CLRTOEOL();mvaddstr(i,0,sbuf_pp[i]);
      i--;
    }
    sbuf_pp[sy+1]=capa_malloc(sizeof(char)*EDIT_WIDTH+1,1);
  }
  strcat(sbuf_pp[sy+1],&(sbuf_pp[sy][sx]));
  memset(&(sbuf_pp[sy][sx]),'\0',EDIT_WIDTH+1-sx);
  *cx=0;
  (*cy)++;
  move(sy,0);CLRTOEOL();mvaddstr(sy,0,sbuf_pp[sy]);
  move(sy+1,0);CLRTOEOL();mvaddstr(sy+1,0,sbuf_pp[sy+1]);
}

/* FIXME catch funtion keys and others? */
void handle_esc      (unsigned char ca,unsigned char cb,char** sbuf_pp,int *cx,int *cy)
{
  if( ca!='[') return;
  switch (cb) {
  case 'A':/* KEY_UP */
    if(*cy>0){
      (*cy)--;
      while(*cx>0 && sbuf_pp[*cy][(*cx)-1]=='\0') (*cx)--; /* goto end of line */
    } else {
      beep();
    }
    break;
  case 'B': /* KEY_DOWN */
    if (*cy<(EDIT_HEIGHT-1)) {
      (*cy)++;
      while(*cx>0 && sbuf_pp[*cy][(*cx)-1]=='\0') (*cx)--; /* goto end of line */
    } else {
      beep();
    }
    break;
  case 'C': /* KEY_RIGHT */
    if ( *cx<(EDIT_WIDTH-1) && sbuf_pp[*cy][(*cx)]!='\0' ) { 
      (*cx)++; 
    } else {
      if (*cy<(EDIT_HEIGHT-1)) {
	(*cy)++; *cx=0; 
      } else {
	beep();
      }
    }
    break;
  case 'D': /* KEY_LEFT */
    if(*cx>0) {
      (*cx)--;
    } else {
      if(*cy>0) { 
	(*cy)--;
	*cx=strlen(sbuf_pp[*cy]);
	if (*cx==EDIT_WIDTH) (*cx)--;
      } else { 
	beep(); 
      }
    }
    break;
  default: beep(); return; break;
  }
  echo(); move(*cy,*cx); refresh(); noecho();
}

void handle_error    (unsigned char c,char** sbuf_pp,int cx,int cy) 
{
  beep();
}

/*FIXME Slower than whale shit*/
void insert_character(unsigned char c,char** sbuf_pp,int *cx,int *cy) 
{
  int sx=*cx,sy=*cy;
  unsigned char temp;
  while(c!='\0') {
    if (sx == EDIT_WIDTH) {
      sx=0;sy++;
      if (sy == EDIT_HEIGHT) {
	sy--;sx=EDIT_WIDTH;c='\0';break;
      }
    }	
    echo(); ADDCH(c); noecho();
    temp=sbuf_pp[sy][sx];
    sbuf_pp[sy][sx]=c;
    c=temp;
    sx++;
  }
  sbuf_pp[sy][sx]=c;
  (*cx)++;
  if (*cx == EDIT_WIDTH) {
      *cx=0;(*cy)++;
      if (*cy == EDIT_HEIGHT) {
	(*cy)--;*cx=EDIT_WIDTH-1;
      }
  }
  move(*cy,*cx);refresh();
}

int handle_keystrokes_editor(char** sbuf_pp)
{
  int   done = 0, forget = 0, cx=0,cy=0;
  unsigned char c,ca,cb;

  while (!done) {
    move(cy,cx);refresh();
    c=getch();
    switch(c) {
    case BS: case DEL:
      remove_character(sbuf_pp,&cx,&cy);
      break;
    case CR: case LF:
      break_line(sbuf_pp,&cx,&cy);
      break;
    case ESC:
      ca=getch();cb=getch();
      handle_esc(ca,cb,sbuf_pp,&cx,&cy);
      break;
    case 5: /*ctrl-e*/
      done=1;
      break;
    case 6: /*ctrl-f*/
      done=1;
      forget=1;
      break;
    case 12:
      refresh_editor(sbuf_pp,cx,cy);
      break;
    default:
      if (c < 32 || c>126) {
	handle_error(c,sbuf_pp,cx,cy);
      } else {
	insert_character(c,sbuf_pp,&cx,&cy);
      }
      break;
    }
  }
  return forget;
}

int editor(char*** sbuf_pp)
{
  init_editor(sbuf_pp);
  return handle_keystrokes_editor(*sbuf_pp);
}


int
answer_subjective(student_number,set,section,prob)
char  *student_number; 
int    set; 
int   *section;
int    prob;
{
  int i,length;
  char date_str[DATE_LENGTH],*tmp;
  char **sbuf_pp,answer[(EDIT_HEIGHT*(EDIT_WIDTH+1))+1];
  char submissions_str[(EDIT_HEIGHT*(EDIT_WIDTH+1))+MAX_BUFFER_SIZE];
  time_t     curtime;

  time(&curtime);
  if( capa_check_date(CHECK_DUE_DATE,student_number,*section,set) > 0 ) {
    capa_get_date(CHECK_DUE_DATE,student_number,*section,set,date_str);
    sprintf(answer,"Sorry, the due date was: %s",date_str);
    move(20,1); clrtobot(); addstr(answer); mypause(23,1);
    return 0;
  }

  if (editor(&sbuf_pp)) { return 0; }

  answer[0]='\0';
  for(i=0;i<EDIT_HEIGHT;i++) {
    if (strlen(sbuf_pp[i]) > 0) {
      strcat(answer,sbuf_pp[i]);
      length=strlen(answer);
      answer[length]='\n';
      answer[length+1]='\0';
    }
    capa_mfree((char *)sbuf_pp[i]);
  }
  capa_set_subjective(set,prob,student_number,answer);
  tmp=strsave(answer);
  protect_log_string(tmp);
  sprintf(submissions_str,"%d\t%s\t",prob,tmp);
  capa_mfree(tmp);
  log_submissions(student_number,set,submissions_str);
  capa_mfree((char *)sbuf_pp);
  return 1;
}

void set_entry_tries(int *tried, char *tries, int num, int num_questions) {
  if((tried[num] >=0) && (tried[num] <= TRY_BOUND) ) {
    if(tried[num] < 10 ) {
      tries[3*num]   = ' ';
      tries[3*num+1] = tried[num] + '0';
      if(num < num_questions-1)  tries[3*num+2] = ',';
    } else {
      tries[3*num]   = (int)(tried[num]/10) + '0';
      tries[3*num+1] = (tried[num] % 10) + '0';
      if(num < num_questions-1)  tries[3*num+2] = ',';
    }
  } else {
    tries[3*num]   = ' ';
    tries[3*num+1] = 1 + '0';
    if(num < num_questions-1)  tries[3*num+2] = ',';
  }
}

/* -------------------------------------------------------------------------- */
/* LET THE USER ANSWER THE CURRENT PROBLEM SET QUESTIONS                      */
/* -------------------------------------------------------------------------- */
void                                
try_set(student_number,set,section) 
char  *student_number; 
int    set; 
int   *section;
{
   char       a_student_number[MAX_STUDENT_NUMBER+1];
   time_t     curtime;
   T_header   header;
   Problem_t *first_problem, *p;
   T_entry    entry;
   char       answer[256], *a_str, **ans_strs;
   int        num, offset, num_questions, start_from, leng;
   char      *log_string,submissions_str[MAX_BUFFER_SIZE],*tmp;
   int       *tried,answered;
   int        scr_idx=1, display=1, second_scr, canAnswer;
   int        usr_command, whereto, allow_hint=0, ex=0;
   char       u_input[64], date_str[DATE_LENGTH], one_line[81];
   int        log_char, i, j, allow_n, allow_p, allow_subj;
   
   strncpy(a_student_number,student_number,MAX_STUDENT_NUMBER+1);
   time(&curtime); /* Is due date past? */
   /* ---------------------------------------- check due date */
#ifndef NO_DATE_CHECK
   /* ===> if ( compare_datetime(curtime,header.due_date) > 0) { */
   if( capa_check_date(CHECK_DUE_DATE,student_number,*section,set) > 0 ) {
      capa_get_date(CHECK_DUE_DATE,student_number,*section,set,date_str);
      sprintf(answer,"  Sorry, the due date was: %s",date_str); 
      move(17,1); clrtoeol(); mvaddstr(17,15,answer);   mypause(19,17);
      return;
   }
#ifdef LOGIN_DBUG
  fprintf(dfp,"Tryset():(sec=%d,set=%d)[%s]\n",*section,set,date_str); fflush(dfp);
#endif /* LOGIN_DBUG */
#endif /* NO_DATE_CHECK */

   offset=capa_get_entry(&entry,student_number,set);
   capa_get_header(&header,set);
   if (offset<0) offset = -offset;  /* newly created entry */
   
#ifdef LOGIN_DBUG
   fprintf(dfp,"P set=%d,SN=%s,ANS=%s,TRY=%s\n",set,a_student_number,entry.answers,entry.tries); fflush(dfp);
#endif
   num = capa_parse(set,&first_problem,a_student_number,&num_questions,NULL);
   
#ifdef LOGIN_DBUG
  fprintf(dfp,"ParseSource:=%d\n",num); fflush(dfp);
#endif /* LOGIN_DBUG */

   /* DEBUGGING: make sure num_questions is plausible */
   if (num_questions>1000 || num_questions<=0)   properly_logout(student_number);
   
   start_from=ask_what_prob(num_questions,entry.answers);
   
   /* initialize log string to all '-' */
   tried = (int *)capa_malloc(num_questions+1,sizeof(int));
   log_string = (char *)capa_malloc(num_questions+1,sizeof(char));
   for (num=0; num<num_questions; num++)  {
     log_string[num]='-';
     sscanf(entry.tries + 3*num,"%d,",&(tried[num]) );
   }
   log_string[num_questions]=0;
   capa_set_login_time(student_number,set);
   for (num=0,p=first_problem; p; ){
      if( start_from > 1 ) {
        num=start_from-1;
        for (p=first_problem; start_from > 1 && p->next; start_from--)
             p=p->next;
        start_from = 0;
      }
      if (display) {
         /* DISPLAY QUESTION */
         CLEAR();
         second_scr = display_prob_scr(p->question,scr_idx);
         allow_subj = 0;
         if( p->ans_type == ANSWER_IS_SUBJECTIVE ) {
           allow_subj = 1;
           move(A_ROW,A_COL); clrtoeol();
           mvaddstr(A_ROW,A_COL,"Enter :A to answer subjective question");
         }
         mvaddstr(S_ROW,0,"OPTION/ANSWER");
         mvaddstr(O_ROW,0,"Options :M = Main Menu  :7 = go to # 7");
         allow_n = allow_p = 0;
         if( second_scr && (scr_idx == 1) ) {
           mvaddstr(O_ROW,E_COL,":N = Next screen");
           allow_n=1;
         }
         if( second_scr && (scr_idx == 2) ) {
           mvaddstr(O_ROW,E_COL,":P = Prev screen");
           allow_p=1;
         }
         
         mvaddstr(O_ROW,R_COL,"RETURN = Enter/Execute");
	 
	 if (g_inhibit_response ) {
	   canAnswer = show_prior_inhibited_response(p,header.partial_credit[num],entry.answers[num],tried[num],&allow_hint);
	 } else {
	   canAnswer = show_prior_response(p,header.partial_credit[num],entry.answers[num],tried[num],&allow_hint);
	 }
	 
      }
      mvaddstr(X_ROW,X_COL,":X = eXit");
      
      /* <= */
      
      
      
        get_xinput(S_ROW,A_COL,u_input,U_ANS_CHAR);
        display=0;  usr_command=C_DONTCARE;
        /* DEFAULT ACTIONS on empty input */
        if(!strlen(u_input)) { usr_command = (p->next? C_FORWARD : C_MENU); } else {
          if( u_input[0] == ':' ) {
           switch(toupper( u_input[1] )) {
             case 'H': if( allow_hint ) { usr_command=C_HINT; } break;
             case 'M': usr_command=C_MENU;    break;
             case 'N': if( allow_n ) { usr_command=C_NEXTSCR; } break;
             case 'P': if( allow_p ) { usr_command=C_PREVSCR; } break;
             case 'X': usr_command=C_EXIT;    break;
             case 'A': if( allow_subj ) { usr_command=C_SUBJANS; } break;
             default : sscanf(u_input,":%d",&whereto);
                    if(whereto >0 && whereto <= num_questions) usr_command=C_JUMP;
		    break;
           }
          } else { /* user entered some answer */
            if( p->ans_op == ANS_AND ) {
              if(canAnswer) { usr_command=C_ANSWER;
                ans_strs = (char **)capa_malloc(sizeof(char *), p->ans_cnt);
                ans_strs[0] = (char *)capa_malloc(strlen(u_input)+1,1);
                strcpy(ans_strs[0],u_input);
                for(i=1;i<p->ans_cnt;i++) {
                  mvaddstr(A_ROW,A_COL,"                                ");
	          mvaddstr(A_ROW,A_COL,ans_strs[i-1]);
                  sprintf(one_line,    " Entering answer %3d of %3d     ", i+1,p->ans_cnt);
                  mvaddstr(A_ROW,S_COL,one_line);
                  mvaddstr(S_ROW,A_COL,"                                ");
                  get_xinput(S_ROW,A_COL,u_input,U_ANS_CHAR);
                  ans_strs[i] = (char *)capa_malloc(strlen(u_input)+1,1);
                  strcpy(ans_strs[i],u_input);
                  
                }
                
                /* now in ans_strs[][] are user inputs */
                
              }
            } else { /* one answer or ANS_OR */
              ans_strs = (char **)capa_malloc(sizeof(char *), 1);
              ans_strs[0] = (char *)capa_malloc(strlen(u_input)+1,1);
              strcpy(ans_strs[0], u_input);
              if(canAnswer)  { usr_command=C_ANSWER; 
	         mvaddstr(S_ROW,A_COL,"                                ");
	         mvaddstr(A_ROW,A_COL,"                                ");
	         mvaddstr(A_ROW,A_COL,ans_strs[0]);  }
	    }
          } /* end if  u_input[0] == ':' */
        } /* end if !strlen(u_input) */
      
        
      
      
      
      /* PROCESS USER COMMAND */
      switch(usr_command) {
        case C_FORWARD: /* Forwards */
                if (p->next) {
                   p=p->next; num++;
                   display=1; allow_hint=0; scr_idx=1;
                } else
                   mvaddstr(X_ROW,R_COL,"RETURN = Main Menu   ");
                break;
        case C_NEXTSCR:  scr_idx = 2; display=1;
                break;
        case C_PREVSCR:  scr_idx = 1; display=1;
                break;
        case C_EXIT: /* Exit */ 
                ex=1; p=0; break;
        case C_MENU: /* Return to main menu */
                p=0;  break;
        case C_HINT: /* Hint */
                if (! p->hint)    break;
                display_hint(p->hint);
                display=1;
                break;
        case C_ANSWER: /* Answer question */
              { 
		if(p->ans_type== ANSWER_IS_SUBJECTIVE) {
		  move(A_ROW,A_COL); clrtoeol();
		  mvaddstr(A_ROW,A_COL,"Enter :A to answer subjective question");
		  capa_mfree(ans_strs[0]);
		  break;
		}
		if( p->ans_op == ANS_AND ) {
		    leng = 0;
		    for(i=0;i<p->ans_cnt;i++) {
		       leng += (strlen((char *)ans_strs[i]) + 2);
		    }
		    a_str = (char *)capa_malloc(leng+1,1);
		    a_str[0]=0;
		    strcat(a_str,ans_strs[0]);
		    if ( is_all_ws(ans_strs[0]) )  break;
		    trim_response_ws(ans_strs[0]);
		    for(i=1;i<p->ans_cnt;i++) {
		       strcat(a_str,"\t");
		       strcat(a_str,ans_strs[i]);
		       if ( is_all_ws(ans_strs[i]) )  break;
		       trim_response_ws(ans_strs[i]);
		    }
		    if (i < p->ans_cnt) {
		      display=1; /*handle early breaks out of the*/
		      break; 	 /*loop which mean typed only ws */
		    }
		} else { /* only one answer */
		  leng = (strlen((char *)ans_strs[0]) + 2);
		  a_str = (char *)capa_malloc(leng+1,1);
		  a_str[0]=0;
		  strcat(a_str,ans_strs[0]);
		  if ( is_all_ws(ans_strs[0]) )  break;
		  trim_response_ws(ans_strs[0]);
		}
		tmp=strsave(a_str);
		protect_log_string(tmp);
		sprintf(submissions_str,"%d\t%s\t",num+1,tmp);
		capa_mfree(tmp);
		log_submissions(student_number,set,submissions_str);

		{
		  int cnt=((p->ans_op==ANS_AND)?p->ans_cnt:1);
     		  if (g_inhibit_response) {
		    canAnswer = give_inhibited_response(p, ans_strs,cnt,
							&(tried[num]),&log_char);
		  } else {
		    canAnswer = give_response(p, ans_strs,cnt, &(tried[num]),&log_char);
		  }
		}
		if( p->ans_op == ANS_AND ) {
		  for(i=0;i<p->ans_cnt;i++) {
		    capa_mfree( (char *)ans_strs[i] );
		  }
		  
		} else { /* there is only one user answer */
		  capa_mfree( (char *)ans_strs[0] );
		  
		}
		capa_mfree((char *)ans_strs);
		capa_mfree( (char *)a_str );
		
                if (p->hint && 
		    (
		     (p->show_hint<=tried[num])||
		     (log_char == 'y') ||
		     (log_char == 'Y')
		     )
		    ){
		  allow_hint=1;
		  mvaddstr(X_ROW,H_COL,":H = Show Hint");
                }
                switch(log_char) {
		  case 'U': case 'u': case 'S': case 'F':
                            entry.answers[num]='N';      break;
                  case 'Y': allow_hint=1; mvaddstr(X_ROW,H_COL,":H = Show Hint");  /* fall through here */
                   default: entry.answers[num]=log_char; break;
                }
                log_string[num]=log_char;
                
                log_attempt(student_number,set,*section,log_string);
                /* for (i=0; i<num_questions; i++) { log_string[i] = '-' ;  } */
		set_entry_tries(tried,entry.tries,num,num_questions);
		log_string[num]='-';
                /* ------------------------------ check due date */
                time(&curtime);
                /* ===> if (compare_datetime(curtime,header.due_date) > 0) { */
#ifndef NO_DATE_CHECK
                if( capa_check_date(CHECK_DUE_DATE,student_number,*section,set) > 0 ) {
                  capa_get_date(CHECK_DUE_DATE,student_number,*section,set,date_str);
                  sprintf(answer,"Sorry, the due date was: %s",date_str);
                  move(20,1); clrtobot(); addstr(answer); mypause(23,1);
                } else
#endif
                {
                  capa_set_entry(&entry,student_number,set,offset);
                }
              } break;
        case C_JUMP: /* Jump to specific question number */
                num=whereto-1;
                for (p=first_problem; whereto > 1 && p->next; whereto--)
                   p=p->next;
                display=1;  allow_hint=0; scr_idx=1;
                break;
        case C_SUBJANS:  
                answered=answer_subjective(student_number,set,section,num+1); 
		if (answered) {
		  tried[num]++;
		  if (p->hint && ((p->show_hint<=tried[num]))) { allow_hint=1; }
		  entry.answers[num]='0';
		  log_string[num]='A';
		  log_attempt(student_number,set,*section,log_string);
		  log_string[num]='-';
		  set_entry_tries(tried,entry.tries,num,num_questions);
                  capa_set_entry(&entry,student_number,set,offset);
		}
                display=1;
                break;
        case C_DONTCARE:  break;
      }
   }
   for (i=0,j=0, num=0; num<num_questions; num++) {
     j = j + (header.weight[num] - '0');
     if((entry.answers[num]=='Y') || (entry.answers[num]=='y')) 
       i = i + (header.weight[num] - '0');
     if( entry.answers[num] >= '0' && entry.answers[num] <= '9' ) {
        i = i + (entry.answers[num] - '0');
     }
     if((entry.answers[num]=='E') || (entry.answers[num]=='e')) 
       j = j - (header.weight[num] - '0');
     if((tried[num] >=0) && (tried[num] <= TRY_BOUND) ) {
       if(tried[num] < 10 ) {
         entry.tries[3*num]   = ' ';
         entry.tries[3*num+1] = tried[num] + '0';
         if(num < num_questions-1)  entry.tries[3*num+2] = ',';
       } else {
         entry.tries[3*num]   = (int)(tried[num]/10) + '0';
         entry.tries[3*num+1] = (tried[num] % 10) + '0';
         if(num < num_questions-1)  entry.tries[3*num+2] = ',';
       }
     } else {
       entry.tries[3*num]   = ' ';
       entry.tries[3*num+1] = 1 + '0';
       if(num < num_questions-1)  entry.tries[3*num+2] = ',';
     }
   }
   capa_mfree(header.weight);
   capa_mfree(header.partial_credit);

   sprintf(answer,"Your score for this set is now: %d/%d",i,j);
   move(20,1); clrtobot(); addstr(answer); mypause(23,1);
   /* ------- original code , should check due date before save it
   
   time(&curtime);
   if (compare_datetime(curtime,header.due_date) > 0) {
   if( capa_check_date(CHECK_DUE_DATE,*section,set) > 0 ) {
      need to deal with due_date 
      sprintf(answer,"Sorry, the due date was: %s",header.due_date);
      move(20,1); clrtobot(); addstr(answer); mypause(23,1);
   } else {
      sprintf(answer,"Your score for this set is now: %d/%d",i,j);
      move(20,1); clrtobot(); addstr(answer); mypause(23,1);
      
      capa_set_entry(&entry,student_number,set,offset);
   }
   ------ */
   /* FREE UP MALLOC'ED SPACE (VERY IMPORTANT) */
   capa_mfree(entry.answers);
   capa_mfree(entry.tries);
   free_problems(first_problem);
   /* log_attempt(student_number,set,*section,log_string); */
   capa_mfree(log_string);
   capa_mfree((char*)tried);
   if (ex) properly_logout(student_number);
   
}

#define   COL_ONE    1
#define   COL_TWO    17
#define   COL_THREE  34
#define   COL_FOUR   43
#define   COL_FIVE   69

/* ------------------------------------------------------------------------- */
/* REVIEW PREVIOUS PROBLEM SETS                                              */
/* ------------------------------------------------------------------------- */
void                                      /* RETURNS: (nothing)              */
view_previous(student_number,set,section) /* ARGUMENTS:                      */
char  *student_number;                    /*    Student number               */
int  set;                                 /*    Set number                   */
int *section;                             /*    Section number               */
{                                         /* LOCAL VARIABLES:                */
   T_entry   entry;                       /*    Database entry               */
   Problem_t *first_problem,              /*    Pointer to first problem     */
             *problem;                    /*    Previous problem             */
   int        num_questions,              /*    Total # of questions         */
              ex=0,                       /*    Exit system flag             */
              display=1,                  /*    Redraw flag                  */
	      usr_command,
	      whereto, 
	      allow_hint=0, allow_explain=0;
   int        num;                        /*    Temporary variable           */
   char       buf[4],                     /*    Command input buffer         */
              aLine[MAX_BUFFER_SIZE];
   T_header   header;                     /*    Set header                   */
   time_t     curtime;                    /*    Current time                 */
   double     upper_ans;
   char       fmt_ans[ANSWER_STRING_LENG], goto_str[ANSWER_STRING_LENG], 
              tmp_str[ANSWER_STRING_LENG];
   int        scr_idx=1, second_scr, allow_n, allow_p;
   
   /* QUERY USER FOR SET */
   move(15,5);  /* deleteln(); */
   addstr("            Which set would you like to view?");
   mvaddstr(16,15,   "Enter a set number and press ENTER/RETURN");
   move(17,1); clrtoeol(); /* erase Enter a command ... */
   do { get_input(15,51,buf,3); } while(!strlen(buf));
   sscanf(buf,"%d",&num);
   if (num<1 || num>set) {
      move(17,5); clrtoeol();
      mvaddstr(17,15,"   Error: Invalid previous set number\n");
      mypause(19,17);   return;
   }
   /* ------------------------------------ answer date */
   time(&curtime);
   /* ===> if (compare_datetime(curtime,header.answer_date) < 0) { */
   if ( capa_check_date(CHECK_ANS_DATE,student_number,*section,num) < 0 ) {
      move(16,1); clrtoeol();
      move(17,5); clrtoeol();
      mvaddstr(17,15,"  Answers are not yet available\n");  mypause(19,17);  return;
   }

   /* LOAD IN THE INFO NEEDED */
   capa_get_header(&header,num);
   capa_get_entry(&entry,student_number,num);
   capa_parse(num,&first_problem,student_number,&num_questions,NULL);
   sprintf(goto_str,"#=go to problem #, [%d problems]", num_questions);
   for (num=0,problem=first_problem; problem; ) {
      if (display) {
         allow_hint = allow_explain=0;
         allow_n = allow_p = 0;
         CLEAR();
         second_scr = display_prob_scr(problem->question,scr_idx);
         if( problem->ans_type == ANSWER_IS_FLOAT ) {
             upper_ans = (double)atof(problem->answer);
             sprintf(fmt_ans, problem->ans_fmt, upper_ans);
         } else {
             sprintf(fmt_ans, "%s", problem->answer);
         }
         if( problem->ans_unit ) {
             sprintf(tmp_str, "Answer: %s %s",fmt_ans,problem->unit_str);
         } else {
             sprintf(tmp_str, "Answer: %s",fmt_ans);
         }
         mvaddstr(S_ROW,COL_ONE,tmp_str);
         
         switch(entry.answers[num]) {
           case 'Y': mvaddstr(S_ROW,COL_FOUR,"CORRECT         "); break;
           case 'y': mvaddstr(S_ROW,COL_FOUR,"HANDIN CORRECT  "); break;
           case '-': mvaddstr(S_ROW,COL_FOUR,"UNANSWERED      "); break;
           case 'e': mvaddstr(S_ROW,COL_FOUR,"EXCUSED         "); break;
           case 'E': mvaddstr(S_ROW,COL_FOUR,"EXCUSED         "); break;
           case 'n': mvaddstr(S_ROW,COL_FOUR,"HANDIN INCORRECT"); break;
           case 'N': mvaddstr(S_ROW,COL_FOUR,"INCORRECT       "); break;
           default : if(entry.answers[num] >= '0' && entry.answers[num] <= '9') {
                      sprintf(aLine,"HAND-GRADED %c/%c ",entry.answers[num],
			      header.weight[num]);
                      mvaddstr(S_ROW,COL_FOUR,aLine);
                     }
                     break;
         }
         mvaddstr(S_ROW,COL_FIVE,"OPTION:");
         
         mvaddstr(O_ROW,COL_ONE,"M=Main menu");
         if( second_scr && scr_idx == 1) {
           mvaddstr(O_ROW,COL_TWO,"N=Next screen");
           allow_n = 1;
         }
         if( second_scr && scr_idx == 2) {
           mvaddstr(O_ROW,COL_TWO,"P=Prev screen");
           allow_p = 1;
         }
         mvaddstr(O_ROW,COL_THREE,"X=eXit");
         mvaddstr(O_ROW,COL_FOUR, "RETURN=Enter/Execute");
         if ( problem->hint && 
	      ( 
	       (problem->show_hint <= problem->tries) || 
	       (entry.answers[num] == 'Y') || 
	       (entry.answers[num] == 'y')
	       ) 
	      )    { 
	   allow_hint=1;    mvaddstr(O_ROW,COL_FIVE,"H=Hint"); 
	 }
         mvaddstr(X_ROW,COL_ONE,goto_str);
         if (problem->next)
               mvaddstr(X_ROW,COL_FOUR,"RETURN=next problem");
            else
               mvaddstr(X_ROW,COL_FOUR,"RETURN=main menu   ");
         if ( problem->explain ) { allow_explain=1; mvaddstr(X_ROW,COL_FIVE,"E=Explain"); }
   
      }
      get_input(S_ROW,COL_FIVE+7,buf,3);
      display=0; usr_command=C_DONTCARE;
      /* DEFAULT ACTIONS on empty input */
      if(!strlen(buf)) { usr_command = (problem->next? C_FORWARD : C_MENU); } else {
        switch(toupper(buf[0])) {
         case 'X': usr_command=C_EXIT;    break;
         case 'M': usr_command=C_MENU;    break;
	 case 'H': usr_command=C_HINT;    break;
         case 'E': usr_command=C_EXPLAIN; break;
         case 'N': if( allow_n ) { usr_command=C_NEXTSCR; } break;
         case 'P': if( allow_p ) { usr_command=C_PREVSCR; } break;
         default : sscanf(buf,"%d",&whereto);
                  if(whereto >0 && whereto <= num_questions) usr_command=C_JUMP;
		  break;
        }
      }


      /* PROCESS USER COMMAND */
      switch(usr_command) {
      case C_FORWARD: /* FORWARDS ONE */
                if (problem->next) {
                   problem=problem->next; display=1; scr_idx = 1; num++;
                } else
                   mvaddstr(X_ROW,COL_FOUR,"RETURN=main menu   ");
                break;
      case C_HINT: /* HINT */
                if(allow_hint) {
                  display_hint(problem->hint);
                  display=1;
                  allow_hint = 0;
                }
		break;
      case C_EXPLAIN: /* Explain */
                if(allow_explain) {
                  display_hint(problem->explain); display=1;
		  allow_explain=0;
		}
		break;
      case C_NEXTSCR:  scr_idx = 2; display=1;
                break;
      case C_PREVSCR:  scr_idx = 1; display=1;
                break;
      case C_EXIT: /* EXIT SYSTEM */
                ex=1; problem=0; break;

      case C_MENU: /* RETURN TO MAIN MENU */
                problem=0;  break;

      case C_JUMP: /* JUMP TO SPECIFIC PROBLEM # */
                   num=whereto-1;
                   for (problem=first_problem; whereto > 1 && problem->next; whereto--)
                      problem=problem->next;
                   display=1;
                   scr_idx = 1;
                 break;
      case C_TIME:     break;
      case C_DONTCARE: break;
      }
   }

   /* FREE UP MALLOC'ED SPACE - VERY IMPORTANT */
   capa_mfree(header.weight);
   capa_mfree(header.partial_credit);
   capa_mfree(entry.answers);
   capa_mfree(entry.tries);
   free_problems(first_problem);

   if (ex) properly_logout(student_number);
}

/* -------------------------------------------------------------------------- */
/* DISPLAY HELP SCREEN                                                        */
/* -------------------------------------------------------------------------- */
void                  /* RETURNS: (nothing)      */
display_help()        /* ARGUMENTS: (none)       */
{                     /* LOCAL VARIABLES:        */
   FILE *fp;          /*    Welcome file pointer */
   char  buf[255];    /*    Input buffer         */

   CLEAR();
   if ((fp=fopen("help.msg","r"))!=NULL) {
      while (fgets(buf,255,fp))  addstr(buf);
      fclose(fp);
   }
   mypause(22,20);
}


/*   A class directory must have   */
/*     records/                    */
/*                                 */
/*  returns: 0  structure is correct, but no set.db files */
/*          -1  structure is not correct                  */
/*          >=1 the last set.db                           */

int
check_class_get_set(dir_path) char  *dir_path;
{
  char   f_name[1024];
  int    set;
  
  if( capa_access(dir_path, F_OK) == 0 ) { /* class dir exists */
    sprintf(f_name,"%s/records",dir_path);
    if( capa_access(f_name, F_OK) == 0 ) { /* class/records dir exists */
      for(set = 1; ; set++ ) {
        sprintf(f_name,"%s/records/set%d.db",dir_path,set);
        if(capa_access(f_name, F_OK) == -1 )  break;
      }
      set--;
    } else {
      set = -1;
    }
  } else {
    set = -1;
  } 
  return (set);
}
/* -------------------------------------------------------------------------- */
/* Get Exam and Quiz Path                                                     */
/*   return  0, 1, 2, 3         */
/* -------------------------------------------------------------------------- */
int
check_exam_quiz_f()
{
   char  buf[MAX_BUFFER_SIZE];
   int   result = 0, configResult=0;

#ifdef LOGIN_DBUG
   fprintf(dfp,"CHECK EXAM Access() success,and open(),%s\n",buf); fflush(dfp);
#endif
   configResult=read_capa_config("exam_path",buf);
   if (configResult != 0 && configResult != -1) {
     Exam_set = check_class_get_set(buf);
     if(Exam_set > 0 )  {
       result = 1;
       sprintf(Exam_path,buf);
     }
   }
#ifdef LOGIN_DBUG
   fprintf(dfp,"CHECK EXAM = %d,%s\n", result,Exam_path); fflush(dfp);
#endif
   configResult=read_capa_config("quiz_path",buf);
   if (configResult != 0 && configResult != -1) {
     Quiz_set = check_class_get_set(buf);
     if(Quiz_set > 0 )  {
       result = (result | 2);
       sprintf(Quiz_path,buf);
     }
   }
   
   return (result);
}

/* -------------------------------------------------------------------------- */
/* DISPLAY MAIN MENU                                                          */
/* -------------------------------------------------------------------------- */
void                  /* RETURNS: (nothing) */
display_menu(student, exam_f, quiz_f) 
T_student *student;
int        exam_f, quiz_f;
{
   char buff[MAX_BUFFER_SIZE];
   int  c_y,configResult,term_summary_button=1;
   
   configResult=read_capa_config("term_summary_button",buff);
   if (configResult != 0 && configResult != -1 ) {
     if (strcasecmp(buff,"no")==0) {
       term_summary_button=0;
     }
   }

   CLEAR();

   mvaddstr(1,10,student->s_nm);
   sprintf(buff,"Section: %d",student->s_sec);
   mvaddstr(1,50,buff);

   mvaddstr( 4,25,"  MAIN MENU"); c_y = 6;
   mvaddstr( c_y,25,"H=Help");    c_y++;
   if (term_summary_button) { mvaddstr( c_y,25,"S=Summary"); c_y++; }
   mvaddstr( c_y,25,"T=Try set"); c_y++;
   mvaddstr( c_y,25,"V=View previous set"); c_y++;
   if(exam_f) { mvaddstr( c_y,25,"E=view Exam summary"); c_y++; }
   if(quiz_f) { mvaddstr( c_y,25,"Q=view Quiz summary"); c_y++; }
   mvaddstr( c_y,25,"X=eXit system");

   mvaddstr(14,25,"COMMAND:");

   mvaddstr(17, 5,"Enter a command from the list above and press ENTER/RETURN");
}

/* -------------------------------------------------------------------------- */
/* CONTROL MAIN MENU SELECTIONS                                               */
/* -------------------------------------------------------------------------- */
void                                    /* RETURNS: (nothing)     */
menu_main(student_number,set,section)   /* ARGUMENTS:             */
char *student_number;                   /*    Student number      */
int set;                                /*    Set number          */
int section;                            /*    Section number      */
{                                       /* LOCAL VARIABLES:       */
   int       ex=0,                      /*    Exit system flag    */
             cmd;                       /*    User command        */
   char      buff[MAX_BUFFER_SIZE];     /*    User command buffer */
   T_student a_student;
   int       had_exam, had_quiz, outcome,configResult;

#ifdef LOGIN_DBUG
   fprintf(dfp,"MENU in %s sec=%d\n", student_number,section); fflush(dfp);
#endif

   outcome = check_exam_quiz_f();
   had_exam = outcome & 1;
   had_quiz = outcome & 2;

#ifdef LOGIN_DBUG
   fprintf(dfp,"After check %d\n", outcome); fflush(dfp);
#endif

   capa_get_student(student_number,&a_student);
   
   g_inhibit_response=capa_check_option(OPTION_INHIBIT_RESPONSE,set,section);
   if (g_inhibit_response < 0 ) g_inhibit_response=0;

   display_menu(&a_student,had_exam, had_quiz);
   while (!ex) {
      do {
         buff[0] = ' '; buff[1] = 0;
         get_input(14,34,buff,1);  cmd=toupper(buff[0]);
      } while (isspace(cmd));
      move(14,35); clrtoeol();
      /* PROCESS USER COMMAND */
      switch(cmd) {

      case 'H': /* DISPLAY HELP */
                display_help();
                display_menu(&a_student,had_exam, had_quiz);
                break;
 
      case 'T': /* TRY CURRENT SET */
                try_set(student_number,set,&section);       
                display_menu(&a_student,had_exam, had_quiz);
                break;

      case 'V': /* VIEW PREVIOUS SET */
                view_previous(student_number,set,&section); 
                display_menu(&a_student,had_exam, had_quiz);
                break;

      case 'S': /* DISPLAY TERM SUMMARY */
	        configResult=read_capa_config("term_summary_button",buff);
		if (configResult != 0 && configResult != -1 ) {
		  if ((strcasecmp(buff,"no")==0)) {
		    break;
		  }
		}
		term_summary(student_number,set,&section,TERM_SUMMARY);  
		display_menu(&a_student,had_exam, had_quiz);
		break;
      case 'E': /* VIEW EXAM SUMMARY */
                if( had_exam ) {
                  chdir(Exam_path);
                  term_summary(student_number,Exam_set,&section,EXAM_SUMMARY);  
                  display_menu(&a_student,had_exam, had_quiz);
                  chdir(Orig_path);
                }
                break;
      case 'Q': /* VIEW QUIZ SUMMARY */
                if( had_quiz ) {
                  chdir(Quiz_path);
                  term_summary(student_number,Quiz_set,&section,QUIZ_SUMMARY);  
                  display_menu(&a_student,had_exam, had_quiz);
                  chdir(Orig_path);
                }
                break;
      case EOF: /* EXIT SYSTEM */
      case 'X': ex=1; break;

      default:  /* INVALID COMMAND */
                /* printf("Invalid choice\n"); */
                break;
      }
   }
}

/* -------------------------------------------------------------------------- */
/* DISPLAY WELCOME MESSAGE WHEN USER LOGS IN                                  */
/* -------------------------------------------------------------------------- */
void               /* RETURNS: (nothing)      */
welcome()          /* ARGUMENTS:         */
{                  /* LOCAL VARIABLES:        */
   FILE *fp;       /*    Welcome file pointer */
   char  buf[TMP_LINE_LENGTH]; /*    Input buffer         */

   CLEAR();
   /* sprintf(buf,"This is your %d-time login to this set, good luck!",tries);
   addstr(buf);
   */
   if ((fp=fopen("welcome.msg","r"))!=NULL) {
      while (fgets(buf,TMP_LINE_LENGTH-1,fp)) addstr(buf);
      fclose(fp);
   }
}

void print_version()
{
  printf("capalogin\n");
  printf("       CAPA version %s, %s\n",CAPA_VER,COMPILE_DATE);
  printf("       CAPA is released under the GNU GPL v2 see COPYING for details.\n");
}

/* ------------------------------------------------------------------------- */
/* DRIVER: INITIALIZE AND GO TO LOGIN                                        */
/* ------------------------------------------------------------------------- */
int
main(int argc, char **argv)
{                            /* LOCAL VARIABLES:            */
   char     student_number[MAX_STUDENT_NUMBER+1]; /* Student number         */
   int      set,             /*    Set number               */
            section=0,       /*    Section number           */
            result;          /* stores result from read_capa_config */
   char     filename[FILE_NAME_LENGTH];   /*    Question filename buffer */
#if defined(NeXT)
   char     cwd[FILE_NAME_LENGTH];
#endif
   char *class_path, buf[MAX_BUFFER_SIZE],*tty;
   
   
   if (argc > 1) { if (strcmp(argv[1],"-v") == 0) {print_version(); exit(0); } }
#ifdef LOGIN_DBUG
   printf("Create login.DBUG file:: argc = %d\n",argc);
   sprintf(filename,"login.DBUG");
   if ((dfp=fopen(filename,"a"))==NULL) { printf("Error: can't open login debug\n"); return; }
#endif /* LOGIN_DBUG */
   /* GET CURRENT SET NUMBER */
  for(set = 1; ; set++ ) {
    sprintf(filename,"set%d.qz",set);
    if(capa_access(filename, F_OK) == -1 )   break;
  }
  set--;
#if defined(NeXT) 
   class_path = getwd(cwd);
   if( class_path ==  NULL ) class_path = cwd;
#else
   class_path = getcwd(NULL,512);
   
#endif 
   sprintf(Orig_path,"%s",class_path);
   free(class_path);
   /* ---------------------------------------------- CURSES INITIALIZATION */
   signal(SIGINT , kick_out);
   signal(SIGALRM, kick_out);
   signal(SIGFPE, SIG_IGN);
   initscr(); savetty(); cbreak(); noecho();
   time(&log_in_time);
   strncpy(in_t,ctime(&log_in_time),31);
   in_t[ strlen(in_t)-1 ]=0;    /* Trash newline */
   tty=ttyname(0);
   if ( tty == NULL ) {
     strcpy(in_tty,"UNKNOWN");
   } else {
     strcpy(in_tty,tty);
   }
   result=read_capa_config("capalogin_goodbye_delay",buf);
   if (result != 0 && result != -1) {
     g_delay=atoi(buf);
   } else {
     g_delay=5;
   }
   result=read_capa_config("capalogin_inactivity_delay",buf);
   if (result != 0 && result != -1) {
     g_max_delay=atoi(buf);
   } else {
     g_max_delay=60;
   }
   welcome();
   strcpy(student_number, login(&set,&section)); student_number[MAX_STUDENT_NUMBER] = 0;
#ifdef LOGIN_DBUG
   fprintf(dfp,"login return:SNum=%s, set=%d, sec=%d\n", student_number,set, section); fflush(dfp);
#endif
   menu_main(student_number,set,section);
#ifdef LOGIN_DBUG
   fclose(dfp);
#endif
   properly_logout(student_number);
   return 0;
}

