File:  [LON-CAPA] / capa / capa51 / pProj / capaCgiUtils.c
Revision 1.21: download - view: text, annotated - select for diffs
Tue Oct 10 20:03:23 2000 UTC (23 years, 6 months ago) by albertel
Branches: MAIN
CVS tags: version_2_9_99_0, version_2_8_X, version_2_8_99_1, version_2_8_99_0, version_2_8_2, version_2_8_1, version_2_8_0, version_2_7_X, version_2_7_99_1, version_2_7_99_0, version_2_7_1, version_2_7_0, version_2_6_X, version_2_6_99_1, version_2_6_99_0, version_2_6_3, version_2_6_2, version_2_6_1, version_2_6_0, version_2_5_X, version_2_5_99_1, version_2_5_99_0, version_2_5_2, version_2_5_1, version_2_5_0, version_2_4_X, version_2_4_99_0, version_2_4_2, version_2_4_1, version_2_4_0, version_2_3_X, version_2_3_99_0, version_2_3_2, version_2_3_1, version_2_3_0, version_2_2_X, version_2_2_99_1, version_2_2_99_0, version_2_2_2, version_2_2_1, version_2_2_0, version_2_1_X, version_2_1_99_3, version_2_1_99_2, version_2_1_99_1, version_2_1_99_0, version_2_1_3, version_2_1_2, version_2_1_1, version_2_1_0, version_2_0_X, version_2_0_99_1, version_2_0_2, version_2_0_1, version_2_0_0, version_1_99_3, version_1_99_2, version_1_99_1_tmcc, version_1_99_1, version_1_99_0_tmcc, version_1_99_0, version_1_3_X, version_1_3_3, version_1_3_2, version_1_3_1, version_1_3_0, version_1_2_X, version_1_2_99_1, version_1_2_99_0, version_1_2_1, version_1_2_0, version_1_1_X, version_1_1_99_5, version_1_1_99_4, version_1_1_99_3, version_1_1_99_2, version_1_1_99_1, version_1_1_99_0, version_1_1_3, version_1_1_2, version_1_1_1, version_1_1_0, version_1_0_99_3, version_1_0_99_2, version_1_0_99_1, version_1_0_99, version_1_0_3, version_1_0_2, version_1_0_1, version_1_0_0, version_0_99_5, version_0_99_4, version_0_99_3, version_0_99_2, version_0_99_1, version_0_99_0, version_0_6_2, version_0_6, version_0_5_1, version_0_5, version_0_4, stable_2002_spring, stable_2002_july, stable_2002_april, stable_2001_fall, conference_2003, bz6209-base, bz6209, STABLE, HEAD, GCI_3, GCI_2, GCI_1, CAPA_5-1-6, CAPA_5-1-5, CAPA_5-1-4_RC1
- was stripping needed tabs from submission strings

/* Most of the web output generation routines.
   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.
*/

/* ===================================================================== */
/* ===================================================================== */
#include <stdio.h>
#include <ctype.h>
#ifndef NO_STDLIB_H
#include <stdlib.h>
#else
char *getenv();
#endif
#include <stdio.h>

#include "capaToken.h"
#include "capaParser.h"
#include "capaCommon.h"
#include "ranlib.h"

#ifdef _MAIN_PROGRAM_
#undef _MAIN_PROGRAM_
#endif

#include "capaCGI.h"

void getword
CAPA_ARG((char *word, char *line, char stop))
{
    int x = 0,y;

    for(x=0;((line[x]) && (line[x] != stop));x++)
        word[x] = line[x];

    word[x] = '\0';
    if(line[x]) ++x;
    y=0;

    while((line[y++] = line[x++]));
}

char *makeword
CAPA_ARG((char *line, char stop))
{
    int x = 0,y;
    char *word = (char *) malloc(sizeof(char) * (strlen(line) + 1));

    for(x=0;((line[x]) && (line[x] != stop));x++)
        word[x] = line[x];

    word[x] = '\0';
    if(line[x]) ++x;
    y=0;

    while((line[y++] = line[x++]));
    return word;
}

char *fmakeword
CAPA_ARG((FILE *f,char  stop,int * cl))
{
    int wsize;
    char *word;
    int ll;

    wsize = 102400;
    ll=0;
    word = (char *) malloc(sizeof(char) * (wsize + 1));

    while(1) {
        word[ll] = (char)fgetc(f);
        if(ll==wsize) {
            word[ll+1] = '\0';
            wsize+=102400;
            word = (char *)realloc(word,sizeof(char)*(wsize+1));
        }
        --(*cl);
        if((word[ll] == stop) || (feof(f)) || (!(*cl))) {
            if(word[ll] != stop) ll++;
            word[ll] = '\0';
            return word;
        }
        ++ll;
    }
}

char x2c
CAPA_ARG((char *what))
{
    register char digit;

    digit = (what[0] >= 'A' ? ((what[0] & 0xdf) - 'A')+10 : (what[0] - '0'));
    digit *= 16;
    digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A')+10 : (what[1] - '0'));
    return(digit);
}

void unescape_url
CAPA_ARG((char *url))
{
    register int x,y;

    for(x=0,y=0;url[y];++x,++y) {
        if((url[x] = url[y]) == '%') {
            url[x] = x2c(&url[y+1]);
            y+=2;
        }
    }
    url[x] = '\0';
}

void plustospace
CAPA_ARG((char *str))
{
    register int x;

    for(x=0;str[x];x++) if(str[x] == '+') str[x] = ' ';
}

int rind
CAPA_ARG((char *s,char c))
{
    register int x;
    for(x=strlen(s) - 1;x != -1; x--)
        if(s[x] == c) return x;
    return -1;
}

int getline
CAPA_ARG((char *s,int n,FILE *f)) 
{
    register int i=0;

    while(1) {
        s[i] = (char)fgetc(f);

        if(s[i] == CR)
            s[i] = fgetc(f);

        if((s[i] == 0x4) || (s[i] == LF) || (i == (n-1))) {
            s[i] = '\0';
            return (feof(f) ? 1 : 0);
        }
        ++i;
    }
}

void send_fd
CAPA_ARG((FILE *f, FILE *fd))
{
    char c;

    while (1) {
        c = fgetc(f);
        if(feof(f)) return;
        fputc(c,fd);
    }
}

int ind
CAPA_ARG((char *s,char c))
{
    register int x;

    for(x=0;s[x];x++)
        if(s[x] == c) return x;

    return -1;
}

void escape_shell_cmd
CAPA_ARG((char *cmd))
{
    register int x,y,l;

    l=strlen(cmd);
    for(x=0;cmd[x];x++) {
        if(ind("&;`'\"|*?~<>^()[]{}$\\",cmd[x]) != -1){
            for(y=l+1;y>x;y--)
                cmd[y] = cmd[y-1];
            l++; /* length has been increased */
            cmd[x] = '\\';
            x++; /* skip the character */
        }
    }
}

/* ==========================================  Updated according to Frank Wolfs 
   description on July 7 1997 */
char *c_getpath
CAPA_ARG((FILE *f)) 
{
  register int c;
  register int idx;
  char     tmp_string[MAX_BUFFER_SIZE];
  char     *new_string;

  idx = 0;
  tmp_string[0]='\0';
  c_ignorewhite(f);
  do {  
    c = getc(f);
    tmp_string[idx] = c;
    idx++;
  } while (isalnum(c) || c == '{' || c == '}' || c == '-' || c == '\\' ||
	   c == '^'   || c == '_' || c == '/' || c == '.' || c == ':' ||
	   c == '+'   || c == '*' || c == '#' || c == '!' || c == '=' || 
	   c == ';'   || c == '$' || c == '(' || c == ')' || c == '[' ||
	   c == ']'   || c == '?' || c == '>' || c == '<' || c == ',');
  ungetc(c,f); idx--;
  tmp_string[idx] = 0;
  new_string = (char *)malloc( (idx+1)*sizeof(char) );
  strncpy(new_string,tmp_string, (idx+1) );
  return (new_string);
}

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

void web_printheader(FILE *out)
{
  FILE *header;
  char *buf[MAX_BUFFER_SIZE];
  int amt=0;

  if ((capa_access("HTMLheader",F_OK|R_OK)!=-1) &&
      (NULL!=(header=fopen("HTMLheader","r")))) {
    while(0 < (amt=fread(buf,1,MAX_BUFFER_SIZE,header))) {
      fwrite(buf,1,amt,out);
    }
    fclose(header);
  } else {
    fprintf(out,"<HTML><HEAD>\n");
    fprintf(out,"<BODY BGCOLOR=\"#FFFFFF\" LINK=\"#0000EE\" VLINK=\"#EE1100\">\n");
  }
}

void web_printfooter(FILE *out)
{
  FILE *footer;
  char *buf[MAX_BUFFER_SIZE];
  int amt=0;

  if ((capa_access("HTMLfooter",F_OK|R_OK)!=-1) &&
      (NULL!=(footer=fopen("HTMLfooter","r")))) {
    while(0 < (amt=fread(buf,1,MAX_BUFFER_SIZE,footer))) {
      fwrite(buf,1,amt,out);
    }
    fclose(footer);
  } else {
    fprintf(out,"</BODY></HTML>\n");
  }
}

int  web_getclassdir
CAPA_ARG((char **cpath_p, char **cown_p, char *class))
{
    FILE     *fp;
    char      filename[FILE_NAME_LENGTH];
    char     *cname_p;
    int       done;
    char      c;
    
    sprintf(filename,"class.conf");
    if ((fp=fopen(filename,"r"))==NULL) {
      sprintf(filename,"../class.conf");
      if ((fp=fopen(filename,"r"))==NULL) {
        fprintf(stdout,"<!-- Error: can't open %s --> \n",filename); fflush(stdout);
        return (2);
      }
    }
    do {
      c_ignorewhite(fp);
      c = getc(fp); ungetc(c,fp);
      if( c != EOF ) {
        cname_p = c_getword(fp);
       *cpath_p = c_getpath(fp);
       *cown_p  = c_getword(fp);
        throwaway_line(fp);
        if( ! strcasecmp(cname_p, class) ) {
          done = 1;
        } else {
          free(cname_p); free(*cpath_p); free(*cown_p);
          done = 0;
        }
      } else {
        done = 1;
      }
    } while ( ! done );
    fclose(fp);
    free(cname_p);
    return (1);
}

int web_log(log_str)char *log_str;
{
   FILE  *fp;       
   char   filename[FILE_NAME_LENGTH];

   sprintf(filename,"web_access.log");
   if ((fp=fopen(filename,"a"))==NULL) {
     return -1; 
   }
   flockstream(fp);
   fprintf(fp,"%s",log_str);fflush(fp);
   funlockstream(fp);
   fclose(fp);
   return 0;
}

int  w_log_timing(student_number,set,section,log_string)
char  student_number[MAX_STUDENT_NUMBER+1];     
int   set;  
int   section;                                    
char *log_string;                                
{                                                  
   char   filename[FILE_NAME_LENGTH],            
         *ct;                                    
   FILE  *fp;                                     
   time_t t;                                 

   sprintf(filename,"records/webtiming%d.log",set);
   if ((fp=fopen(filename,"a"))==NULL) {
      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  w_log_attempt(student_number,set,log_string)
char  student_number[MAX_STUDENT_NUMBER+1];     
int   set;  
char *log_string;                                
{                                                  
   char   filename[FILE_NAME_LENGTH],            
         *ct;                                    
   FILE  *fp;                                     
   time_t t;                                 

   sprintf(filename,"records/weblog%d.db",set);
   if ((fp=fopen(filename,"a"))==NULL) {
      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  w_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/websubmissions%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);*/ /* done on indiviual answers now*/
   fprintf(fp,"%s\t%s\t%s\n",student_number,timeStr,log_string); fflush(fp);
   fclose(fp);
   return (0);
}


void w_get_responses(int x,int q_idx,char* submissions_str)
{
  int leng, sub_idx;
  char buf[MAX_BUFFER_SIZE],*tmp;
  if( !strncmp(g_entries[x].name,"INPUT",5) ) {
    if( index(g_entries[x].name, ',' ) == NULL ) {  /* only one answer */
      sscanf(g_entries[x].name,"INPUT%d",&q_idx);
      if( q_idx > 0 && q_idx < MAX_PROBLEM_CNT ) {
	if ( ! is_all_ws(g_entries[x].val) ) {
	  g_stu_ans_pp[q_idx] = (StudentAnswer_t *)capa_malloc(sizeof(StudentAnswer_t),1);
	  (g_stu_ans_pp[q_idx])->a_idx  = 1;
	  (g_stu_ans_pp[q_idx])->a_str  = strsave(g_entries[x].val);
	  (g_stu_ans_pp[q_idx])->a_next = NULL;
	  trim_response_ws((g_stu_ans_pp[q_idx])->a_str);
	}
	leng = strlen( g_entries[x].val );
	if ( leng > 0 ) {
          tmp=strsave(g_entries[x].val);
	  protect_log_string(tmp);
	  sprintf(buf,"%d\t%s\t",q_idx,tmp);
	  capa_mfree(tmp);
	  strcat(submissions_str,buf);
	}
      }
    } else { /* this answer belongs to /AND answers */
      sscanf(g_entries[x].name,"INPUT%d,%d",&q_idx,&sub_idx);
      if( q_idx > 0 && q_idx < MAX_PROBLEM_CNT ) {
	if ( ! is_all_ws(g_entries[x].val) ) { 
	  StudentAnswer_t *sa_p;
	  sa_p = (StudentAnswer_t *)capa_malloc(sizeof(StudentAnswer_t),1);
	  sa_p->a_idx  = sub_idx;
	  sa_p->a_str  = strsave(g_entries[x].val);
	  sa_p->a_next = NULL;
	  trim_response_ws(sa_p->a_str);
	  if( g_stu_ans_pp[q_idx] == NULL ) {
	    g_stu_ans_pp[q_idx] = sa_p;
	  } else { 
	    StudentAnswer_t *sb_p;
	    for(sb_p=g_stu_ans_pp[q_idx]; sb_p->a_next; sb_p=sb_p->a_next);
	    sb_p->a_next = sa_p;
	  }
	}
	leng = strlen( g_entries[x].val );
	if ( leng > 0 ) {
          tmp=strsave(g_entries[x].val);
	  protect_log_string(tmp);
	  sprintf(buf,"%d\t%s\t",q_idx,tmp);
	  capa_mfree(tmp);
	  strcat(submissions_str,buf);
	}
      }
    }
  }
  if( !strncmp(g_entries[x].name,"LAST",4) ) {  
    StudentAnswer_t *sa_p;  
    if( index(g_entries[x].name, ',' ) == NULL ) {  /* only one answer */
      sscanf(g_entries[x].name,"LAST%d",&q_idx);
      if( q_idx > 0 && q_idx < MAX_PROBLEM_CNT ) {
	sa_p = (StudentAnswer_t *)capa_malloc(sizeof(StudentAnswer_t),1);
	sa_p->a_idx  = 1;
	sa_p->a_str  = strsave(g_entries[x].val);
	sa_p->a_next = NULL;
	g_last_ans_pp[q_idx] = sa_p;
      }
    } else {
      sscanf(g_entries[x].name,"LAST%d,%d",&q_idx,&sub_idx);
      if( q_idx > 0 && q_idx < MAX_PROBLEM_CNT ) {
	sa_p = (StudentAnswer_t *)capa_malloc(sizeof(StudentAnswer_t),1);
	sa_p->a_idx  = sub_idx;
	sa_p->a_str  = strsave(g_entries[x].val);
	sa_p->a_next = NULL;
	if( g_last_ans_pp[q_idx] == NULL) {
	  g_last_ans_pp[q_idx] = sa_p;
	} else {
	    StudentAnswer_t *sb_p;
	    for(sb_p=g_last_ans_pp[q_idx]; sb_p->a_next; sb_p=sb_p->a_next);
	    sb_p->a_next = sa_p;
	}
      }
    }
  }
}

/* ========================================================================= */
/* Check in:      Class name     (CLASS)
                  Student Number (SNUM)
                  CAPA ID        (CAPAID)
                  M=1
   Try set:       Class name     (CLASS)
                  Student Number (SNUM)
                  CAPA ID        (CAPAID)
		  First question to print (STARTNUM)
                  M=2
   View previous: Class name     (CLASS)
                  Student Number (SNUM)
                  CAPA ID        (CAPAID)
		  First question to print (STARTNUM)
                  View set       (VSET)
                  M=3
   Set summary:   
                  
 --------------------------------------------------------------------------- */
             
int  w_get_input() 
{
  register  int   x,m=0;
  int             cl, q_idx, result = 1;
  T_header        header; 
  char            log_str[MAX_BUFFER_SIZE],buf[MAX_BUFFER_SIZE];
  char            submissions_str[MAX_BUFFER_SIZE];
  time_t          curtime;
  char           *time_str,*UNKNOWN="UNKNOWN";
  int             error=0;
  char           *envPtr=NULL,*envPtr2=NULL;
#ifdef CGI_DBUG
  fprintf(g_cgi,"Entered get_input()\n"); fflush(g_cgi);
#endif /* CGI_DBUG */

  envPtr=getenv("REQUEST_METHOD");
  if(!envPtr) { 
    fprintf(stdout,"Enviroment variable REQUEST_METHOD not set.\n");
    fprintf(stdout,"CAPA is improperly installed or run, please let your \
instructor know about this error.\n");
    fprintf(stdout,"No tries have been deducted for the last submission.\n");
    error |= 1;
    return error;
  }
  if(strcmp(envPtr,"POST")) {
        fprintf(stdout,"This script should be referenced with a METHOD of POST.\n");
	fprintf(stdout,"CAPA is improperly installed or run, please let your \
instructor know about this error.\n");
	fprintf(stdout,"No tries have been deducted for the last submission.\n");
        fflush(stdout);
#ifdef CGI_DBUG
  fprintf(g_cgi,"Request_method is not POST\n"); fflush(g_cgi);
#endif /* CGI_DBUG */        
        error |= 2; return (error);
  }
  
  envPtr=getenv("CONTENT_TYPE");
  if(!envPtr) { 
    fprintf(stdout,"Enviroment variable CONTENT_TYPE not set.\n");
    fprintf(stdout,"CAPA is improperly installed or run, please let your \
instructor know about this error.\n");
    fprintf(stdout,"No tries have been deducted for the last submission.\n");
    fflush(stdout);
    error |= 4;
    return error;
  }
  if(strncmp(envPtr,"application/x-www-form-urlencoded",33)) {
        fprintf(stdout,"This script can only be used to decode form results. \n");
	fprintf(stdout,"CAPA is improperly installed or run, please let your \
instructor know about this error.\n");
	fprintf(stdout,"No tries have been deducted for the last submission.\n");
        fflush(stdout);
#ifdef CGI_DBUG
	fprintf(g_cgi,"CONTENT_TYPE is not application/x-www-form-urlencoded\n"); fflush(g_cgi);
#endif /* CGI_DBUG */              
        error |= 8; return (error);
  }

  envPtr=getenv("CONTENT_LENGTH");
  if(!envPtr) { 
    fprintf(stdout,"Enviroment variable CONTENT_LENGTH not set.\n");
    fprintf(stdout,"CAPA is improperly installed or run, please let your \
instructor know about this error.\n");
    fprintf(stdout,"No tries have been deducted for the last submission.\n");
    error |= 16;
    return error;
  }
  cl = atoi(envPtr);
#ifdef CGI_DBUG
  fprintf(g_cgi,"CONTENT_LENGTH is %d\n",cl); fflush(g_cgi);
#endif /* CGI_DBUG */
  for(x=0;cl && (!feof(stdin));x++) {
    m=x;
    g_entries[x].val = fmakeword(stdin,'&',&cl);
    plustospace(g_entries[x].val);
    unescape_url(g_entries[x].val);
    g_entries[x].name = makeword(g_entries[x].val,'=');
  }
  /* ---------------------------------------------------- */
  g_entered_pin = 0; g_run_mode =0;
  submissions_str[0]='\0';
  for(x=0; x <= m; x++) {
      if( !strcmp(g_entries[x].name,"CLASS") ) {
        strncpy(g_class_name,g_entries[x].val,MAX_CLASS_CHAR);
      }
      if( !strcmp(g_entries[x].name,"M") ) {  /* run mode */
        sscanf(g_entries[x].val,"%d",&g_run_mode);
      }
      if( !strcmp(g_entries[x].name,"STARTNUM")) {
	sscanf(g_entries[x].val,"%d",&g_start_question);
      }
      if( !strcmp(g_entries[x].name,"SNUM") ) {
        strncpy(g_student_number,g_entries[x].val,MAX_STUDENT_NUMBER+4);
      }
      if( !strcmp(g_entries[x].name,"CAPAID") ) {
        sscanf(g_entries[x].val,"%d",&g_entered_pin);
      }
      if( !strcmp(g_entries[x].name,"SET") ) {
        sscanf(g_entries[x].val,"%d",&g_set);
      }
      if( !strcmp(g_entries[x].name,"VSET") ) {
	if (g_entries[x].val[0] == '\0') {
	  g_vset=0;
	} else {
	  sscanf(g_entries[x].val,"%d",&g_vset);
	}
      }
      if( !strcmp(g_entries[x].name,"KND") ) {
        sscanf(g_entries[x].val,"%d",&g_skind);
      }
      w_get_responses(x,q_idx,submissions_str);
      free(g_entries[x].val);
      free(g_entries[x].name);
  }
  
#ifdef CGI_DBUG
  fprintf(g_cgi,"DONE: Parse input\n"); fflush(g_cgi);
#endif /* CGI_DBUG */
  /* --------------------------------------------------------- */
  /* if( strcasecmp(g_prog_name,"capacheckin") == 0 ) { */
  if( g_run_mode == M_CHECKIN ) { /* capa_checkin */
    time(&curtime); time_str = ctime(&curtime);
    time_str[ strlen(time_str)-1 ] = 0;
    envPtr=getenv("REMOTE_HOST");
    if(!envPtr) { 
      fprintf(stdout,"<!-- Enviroment variable REMOTE_HOST not set.-->\n");
      envPtr=getenv("REMOTE_ADDR");
      if(!envPtr) { 
	fprintf(stdout,"<!-- Enviroment variable REMOTE_ADDR not set.-->\n");
	envPtr=UNKNOWN;
	error |= 32;
      }
    }
#ifdef CGI_DBUG
  fprintf(g_cgi,"DONE: REMOTE_HOST\n"); fflush(g_cgi);
#endif /* CGI_DBUG */

    envPtr2=getenv("HTTP_USER_AGENT");
    if(!envPtr2) { 
      fprintf(stdout,"<!-- Enviroment variable HTTP_USER_AGENT not set. -->\n");
      envPtr2=UNKNOWN;
      error |= 64;
    }
    sprintf(log_str,"%s\t%s\t%s\t%s\t%s\n",g_class_name,g_student_number,time_str,envPtr,envPtr2);
    if (web_log(log_str) == -1 ) {
      fprintf(stdout,"Unable to log access to the system. Please notify instructor\n.");
      fprintf(stdout,"No tries have been deducted for the last submission.\n");
      error |= 128;
      return error;
    }
    
  }
#if defined(NeXT) 
   getwd(g_cwd);
#else
   getcwd(g_cwd,255);
#endif
  web_getclassdir(&g_cpath, &g_cowner, g_class_name);
  sprintf(g_class_fullpath,"%s/%s",g_cpath,g_class_name);
#ifdef CGI_DBUG
  fprintf(g_cgi,"DONE: getclassdir() [%s]\n",g_class_fullpath); fflush(g_cgi);
#endif /* CGI_DBUG */
  if( !capa_access(g_class_fullpath, F_OK) == 0 ) {
    fprintf(stdout,"ACCESS: could not access the class directory [%s]!\n",g_class_fullpath);
    fprintf(stdout,"Please exit the web browser and try accessing the system again\n");
    fprintf(stdout,"No tries have been deducted for the last submission.\n");
    fflush(stdout);
    sprintf(log_str,"Failed to access class dir, g_class_fullpath: %s\tg_cpath: %s\tg_class_name: %s\tg_cowner: %s\tg_cwd: %s\t",
	    g_class_fullpath,g_cpath,g_class_name,g_cowner,g_cwd);
    if (web_log(log_str) == -1 ) {
      fprintf(stdout,"Unable to log access to the system. Please notify instructor\n.");
      fflush(stdout);
      error |= 256;
      return error;
    } 
#ifdef CGI_DBUG
    fprintf(g_cgi,"NO ACCESS: cannot access() [%s]\n",g_class_fullpath); fflush(g_cgi);
#endif /* CGI_DBUG */      
    error |= 512;
    return (error);
  }
  /* ---------------------------------------------------- */
  /*      change working directory to the class           */
  /* ---------------------------------------------------- */
  chdir(g_class_fullpath); /* before performing any capa*() calls */
#ifdef CGI_DBUG
  fprintf(g_cgi,"DONE cd to [%s]\n",g_class_fullpath); fflush(g_cgi);
#endif /* CGI_DBUG */

  /* Now in proper directory, can log submissions */
  if ( g_run_mode == M_CHECKANS) {
    if (w_log_submissions(g_student_number,g_set,submissions_str) == -1 ) {
      fprintf(stdout,"Unable to log submissions. Please notify instructor\n.");
      fprintf(stdout,"No tries have been deducted for the last submission.\n");
      error |= 1024;
      return error; 
    }
  }

  result=read_capa_config("capaweb_cgibin_path",buf);
  if (result != 0 && result != -1) {
    g_cgibin_path=capa_malloc(strlen(buf)+1,1);
    strcpy(g_cgibin_path,buf);
  } else {
    g_cgibin_path=capa_malloc(strlen("capa-bin")+1,1);
    strcpy(g_cgibin_path,"capa-bin");
  }

  if( g_entered_pin != 0 ) {
      g_login_set = capa_PIN(g_student_number,999,g_entered_pin);
#ifdef CGI_DBUG
  fprintf(g_cgi,"Succeed in login to set %d with pin %d\n",g_login_set,g_entered_pin); fflush(g_cgi);
#endif /* CGI_DBUG */   
    /* ---------------------------------------------------- 
      printf("Your entered capa id %d, login to set %d\n", g_entered_pin, g_login_set);
      printf("The real capa id for %s is %d\n",g_student_number,capa_PIN(g_student_number,1,0));
    ---------------------------------------------------- */
    
  } else {
    fprintf(stdout,"CAPA ID entered was zero, this is not valid.\n");
    fprintf(stdout,"No tries have been deducted for the last submission.\n");
    fflush(stdout);
    error |= 2048; return (error);
  }

  if (!g_login_set) { 
        fprintf(stdout,"The student ID (%s) or CAPA ID (%d) that you entered \
is not listed for the class (%s) that you have selected. Please check that \
you have selected the correct class on the CAPA logon page and that the \
student ID and CAPA ID are correct.\n", 
		g_student_number, g_entered_pin, g_class_name);
        fflush(stdout);
#ifdef CGI_DBUG
	fprintf(g_cgi,"CAPA ID or student number is not valid.\n"); 
	fflush(g_cgi);
#endif /* CGI_DBUG */   
        error |= 4096;  return (error);
  } else {
      if ( g_login_set > 99 )  { 
        fprintf(stdout,"The student ID (%s) or CAPA ID (%d) that you entered \
is not listed for the class (%s) that you have selected. Please check that \
you have selected the correct class on the CAPA logon page and that the \
student ID and CAPA ID are correct.\n", 
		g_student_number, g_entered_pin, g_class_name);
           fflush(stdout);
#ifdef CGI_DBUG
	   fprintf(g_cgi,"CAPA ID is not valid.\n"); fflush(g_cgi);
#endif /* CGI_DBUG */            
           error |= 8192; return (error);
      }
      if(g_login_set < g_vset ) {
           fprintf(stdout,"Your CAPA ID (for set %d) does not allow you to view set %d.\n",g_login_set, g_vset);
           fflush(stdout);
#ifdef CGI_DBUG
	   fprintf(g_cgi,"Login set %d is less than view set %d.\n",g_login_set,g_vset); fflush(g_cgi);
#endif /* CGI_DBUG */ 
           error |= 16384; return (error);
      }
      chdir(g_class_fullpath);  /* again, to make sure */
      
      if ( capa_get_student(g_student_number,&g_student_data) == 0 ) {
           fprintf(stdout,"Entered student id is not in the class list.\n");
	   fprintf(stdout,"Please check that have selected the correct class \
and have entered you student id correctly.\n");
           fflush(stdout);
#ifdef CGI_DBUG
	   fprintf(g_cgi,"get_student(): Student id not in the classl file.\n"); fflush(g_cgi);
#endif /* CGI_DBUG */               
            error |= 32768; return (error);
      } else {
	 time(&curtime);
	 if (capa_get_header(&header, g_login_set))  {
	   fprintf(stdout,"This problem set is not ready yet.\n"); 
	   fflush(stdout);
	  
#ifdef CGI_DBUG
	   fprintf(g_cgi,"get_header(): Problem set not ready.\n"); fflush(g_cgi);
#endif /* CGI_DBUG */            
	   error |= 65536; return (error);
 	 }
	 capa_mfree(header.weight);
	 capa_mfree(header.partial_credit);
         /* ===> if (compare_datetime(curtime,header.open_date) < 0 ) { */
         if( capa_check_date(CHECK_OPEN_DATE,g_student_number,
			     g_student_data.s_sec,g_login_set) < 0 ) {
            fprintf(stdout,"This set(%d) is not yet open. Please try back later.\n",g_login_set); 
	    fflush(stdout);
#ifdef CGI_DBUG
	    fprintf(g_cgi,"check_date(): Problem set not open.\n"); 
	    fflush(g_cgi);
#endif /* CGI_DBUG */    
            error |= 131072; return (error);
         }
      }
  }
  return (error);
}

void append_qtext_addbr(new_str) char *new_str;
{
  int ii,jj,len;
  char *br_added;
  if (new_str==NULL) return;
  len=strlen(new_str);
  br_added=capa_malloc(len*5,sizeof(char));
  for (ii=0,jj=0;ii<len;ii++) {
    if (new_str[ii] == '\n') {
      br_added[jj]='<';jj++;br_added[jj]='b';jj++;br_added[jj]='r';jj++;
      br_added[jj]='>';jj++;br_added[jj]='\n';jj++;
    } else {
      br_added[jj]=new_str[ii];jj++;
    }
  }
  br_added[jj]='\0';
  append_qtext(br_added);
  capa_mfree(br_added);
}
void append_qtext_addbr2(new_str) char *new_str;
{
  char *br="<br>\n";
  char single[2];
  int ii,len;
  single[0]='\0';single[1]='\0';
  if (new_str==NULL) return;
  len=strlen(new_str);
  for (ii=0;ii<len;ii++) {
    if (new_str[ii] == '\n') {
      append_qtext(br);
    } else {
      single[0]=new_str[ii];
      append_qtext(single);
    }
  }
}
/* ============================================================================= */
void        append_qtext(new_str) char *new_str;
{
  int ii,len;
  if (new_str==NULL) return;
  len=strlen(new_str);
#ifdef CGI_DBUG
  fprintf(g_cgi,"before: len %d; g_qchar_cnt %d; g_qsize %d\n",
	  len,g_qchar_cnt,g_qsize);
  fflush(g_cgi);
#endif /* CGI_DBUG */    
  if (g_qchar_cnt+len>g_qsize-3) {
    char *temp_text;
    g_qsize=(g_qchar_cnt+len)*2;
    temp_text=capa_malloc(g_qsize,sizeof(char));
    strncpy(temp_text,g_question_txt,g_qchar_cnt);
    temp_text[g_qchar_cnt]='\0';
    capa_mfree(g_question_txt);
    g_question_txt=temp_text;
    /*
    g_qsize=(g_qchar_cnt+len)*2;
    g_question_txt=realloc(g_question_txt,g_qsize);
    */
  }
  for(ii=0;ii<len;ii++) {
    g_question_txt[g_qchar_cnt+ii]=new_str[ii];
  }
  g_qchar_cnt += len;
  g_question_txt[g_qchar_cnt]='\0';
#ifdef CGI_DBUG
  fprintf(g_cgi,"after: len %d; g_qchar_cnt %d; g_qsize %d; strlen(g_question_txt):%d\n",len,g_qchar_cnt,g_qsize,strlen(g_question_txt));
  fflush(g_cgi);
#endif /* CGI_DBUG */    
}
void        append_stext(new_str) char *new_str;
{
  int ii,len;
  if (new_str==NULL) return;
  len=strlen(new_str);
#ifdef CGI_DBUG
  fprintf(g_cgi,"appending status{%s}\nlen %d; g_schar_cnt %d; g_ssize %d\n",
	  new_str,len,g_schar_cnt,g_ssize);
  fflush(g_cgi);
#endif /* CGI_DBUG */    
  if (g_schar_cnt+len>g_ssize-2) {
    char *temp_text;
    g_ssize=(g_schar_cnt+len)*2;
    temp_text=capa_malloc(g_ssize,sizeof(char));
    strncpy(temp_text,g_status_txt,g_schar_cnt);
    capa_mfree(g_status_txt);
    g_status_txt=temp_text;
  }
  for(ii=0;ii<len;ii++) {
    g_status_txt[g_schar_cnt+ii]=new_str[ii];
  }
  g_schar_cnt += len;
  g_status_txt[g_schar_cnt+1]='\0';
#ifdef CGI_DBUG
  fprintf(g_cgi,"len %d; g_schar_cnt %d; g_ssize %d\n",len,g_schar_cnt,g_ssize);
  fflush(g_cgi);
#endif /* CGI_DBUG */    
}
/* ============================================================================= */
/* ------------------------------------------------------------
  printf("ENV<br>\n");
  printf("SERVER_PROTOCOL:%s<br>",getenv("SERVER_PROTOCOL"));
  printf("PATH_INFO:%s<br>",getenv("PATH_INFO"));
  printf("PATH_TRANSLATED:%s<br>\n",getenv("PATH_TRANSLATED"));
  printf("SCRIPT_NAME:%s<br>\n",getenv("SCRIPT_NAME"));
  printf("QUERY_STRING:%s<br>\n",getenv("QUERY_STRING"));
  printf("REMOTE_HOST:%s<br>\n",getenv("REMOTE_HOST"));
  printf("REMOTE_USER:%s<br>\n",getenv("REMOTE_USER"));
  printf("REMOTE_IDENT:%s<br>\n",getenv("REMOTE_IDENT"));
  printf("USER_AGENT:%s<br>\n",getenv("USER_AGENT"));
  printf("HTTP_USER_AGENT:%s<br>\n",getenv("HTTP_USER_AGENT"));
  ------------------------------------------------------------
*/

/* ------------------------------------------------------ */
/*   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_maxset(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                                                      */
/* 0: Neither exist                                                          */
/* 1: Exam.path exists                                                       */
/* 2: Quiz.path exists                                                       */
/* 3: Both Exam.path and Quiz.path exists                                    */
/* ------------------------------------------------------------------------- */
int
check_exam_quiz_path()
{
  char  buf[MAX_BUFFER_SIZE];
  int   result = 0, configResult=0;
  
  configResult=read_capa_config("exam_path",buf);
  if (configResult != 0 && configResult != -1) {
    g_exam_set = check_class_get_maxset(buf);
    if(g_exam_set > 0 )  {
      result = 1;
      sprintf(g_exam_path,buf);
    }
  }
  configResult=read_capa_config("quiz_path",buf);
  if (configResult != 0 && configResult != -1) {
    g_quiz_set = check_class_get_maxset(buf);
    if(g_quiz_set > 0 )  {
      result = (result | 2);
      sprintf(g_quiz_path,buf);
    }
  }
  return (result);
}


int
check_termscore_option()
{
  char  buf[MAX_BUFFER_SIZE];
  int   result = 0, configResult=0;
  
  configResult=read_capa_config("term_score_applet",buf);
  if (configResult != 0 && configResult != -1) {
    fprintf(stdout,"<!-- term_score_applet is in capa.config file -->\n");
    if (strcasecmp(buf,"yes")==0) {
      fprintf(stdout,"<!-- term_score_applet is YES -->\n");
      result=1;
    }
  }
  return (result);
}




/* ============================================================================= */
void
print_mainmenu(class,sn,pin)char *class; char *sn;int pin;
{
  char  buf[MAX_BUFFER_SIZE];
  int   outcome,configResult,term_summary_button=1;
  char *serverName;

  serverName=getenv("SERVER_NAME");
  if (!serverName) {
    fprintf(stdout,"Enviroment variable SERVER_NAME not set.\n");
    fprintf(stdout,"Unable to complete actions.\n");
    return;
  }
  
  fprintf(stdout,"<TITLE>%s main menu</TITLE>\n",g_class_name);
  fprintf(stdout,"<h3>%s</h3><br>\n",g_student_name);
  fprintf(stdout,"<menu>\n");
  fprintf(stdout,"<li><form method=\"get\" action=\"http://%s/CAPA/help.html\">",serverName);
  fprintf(stdout,"<input type=\"submit\" value=\"Help\" ></form>\n");

  fprintf(stdout,"<li><form method=\"post\" ");
  /* fprintf(stdout," target=\"right_frame\" "); */
  sprintf(buf,"action=\"http://%s/%s/%s/capasbin\">",serverName,g_cgibin_path,g_cowner);
  fprintf(stdout,"%s\n", buf);
  fprintf(stdout,"<input type=\"hidden\" name=\"CLASS\" value=\"%s\">\n",class);
  fprintf(stdout,"<input type=\"hidden\" name=\"SNUM\" value=\"%s\">\n",sn);
  fprintf(stdout,"<input type=\"hidden\" name=\"CAPAID\" value=\"%d\">\n",pin);
  fprintf(stdout,"<input type=\"hidden\" name=\"M\" value=\"%d\">\n",M_TRYSET);
  fprintf(stdout,"<input type=\"hidden\" name=\"STARTNUM\" value=\"%d\">\n",1);
  fprintf(stdout,"<input type=\"submit\" value=\"Try current set\" ></form>\n");
  
  fprintf(stdout,"<li><form method=\"post\" ");
  /* fprintf(stdout," target=\"right_frame\" "); */
  sprintf(buf,"action=\"http://%s/%s/%s/capasbin\">",serverName,g_cgibin_path,g_cowner);
  fprintf(stdout,"%s\n", buf);
  fprintf(stdout,"<input type=\"hidden\" name=\"CLASS\" value=\"%s\">\n",class);
  fprintf(stdout,"<input type=\"hidden\" name=\"SNUM\" value=\"%s\">\n",sn);
  fprintf(stdout,"<input type=\"hidden\" name=\"CAPAID\" value=\"%d\">\n",pin);
  fprintf(stdout,"<input type=\"hidden\" name=\"M\" value=\"%d\">\n",M_VIEWPREV);
  fprintf(stdout,"<input type=\"hidden\" name=\"STARTNUM\" value=\"%d\">\n",1);
  fprintf(stdout,"<input type=\"submit\" value=\"View previous set:\" >");
  fprintf(stdout,"<b> set:</b><input name=\"VSET\" value=\"\" size=4></form>\n");
  
  /*Term Summary*/
  configResult=read_capa_config("term_summary_button",buf);
  if (configResult != 0 && configResult != -1 ) {
    if (strcasecmp(buf,"no")==0) {
      term_summary_button=0;
    }
  }
  if (term_summary_button) {
    fprintf(stdout,"<li><form method=\"post\" ");
    /* fprintf(stdout," target=\"right_frame\" "); */
    sprintf(buf,"action=\"http://%s/%s/capahtml\">",serverName,g_cgibin_path);
    fprintf(stdout,"%s\n", buf);
    fprintf(stdout,"<input type=\"hidden\" name=\"CLASS\" value=\"%s\">\n",class);
    fprintf(stdout,"<input type=\"hidden\" name=\"SNUM\" value=\"%s\">\n",sn);
    fprintf(stdout,"<input type=\"hidden\" name=\"CAPAID\" value=\"%d\">\n",pin);
    fprintf(stdout,"<input type=\"hidden\" name=\"M\" value=\"%d\">\n",M_VIEWSUMM);
    fprintf(stdout,"<input type=\"submit\" value=\"Display term summary\" ></form>\n");
  }

  outcome = check_exam_quiz_path();
  if( outcome & 1 ) {  /* exam summary */
    fprintf(stdout,"<li><form method=\"post\" ");
    /* fprintf(stdout," target=\"right_frame\" "); */
    sprintf(buf,"action=\"http://%s/%s/capahtml\">",serverName,g_cgibin_path);
    fprintf(stdout,"%s\n", buf);
    fprintf(stdout,"<input type=\"hidden\" name=\"CLASS\" value=\"%s\">\n",class);
    fprintf(stdout,"<input type=\"hidden\" name=\"SNUM\" value=\"%s\">\n",sn);
    fprintf(stdout,"<input type=\"hidden\" name=\"CAPAID\" value=\"%d\">\n",pin);
    fprintf(stdout,"<input type=\"hidden\" name=\"M\" value=\"%d\">\n",M_EXAMSUMM);
    fprintf(stdout,"<input type=\"submit\" value=\"Display Exam summary\" ></form>\n");
  }
  if( outcome & 2 ) { /* Quiz summary */
    fprintf(stdout,"<li><form method=\"post\" ");
    /* fprintf(stdout," target=\"right_frame\" "); */
    sprintf(buf,"action=\"http://%s/%s/capahtml\">",serverName,g_cgibin_path);
    fprintf(stdout,"%s\n", buf);
    fprintf(stdout,"<input type=\"hidden\" name=\"CLASS\" value=\"%s\">\n",class);
    fprintf(stdout,"<input type=\"hidden\" name=\"SNUM\" value=\"%s\">\n",sn);
    fprintf(stdout,"<input type=\"hidden\" name=\"CAPAID\" value=\"%d\">\n",pin);
    fprintf(stdout,"<input type=\"hidden\" name=\"M\" value=\"%d\" >\n",M_QUIZSUMM );
    fprintf(stdout,"<input type=\"submit\" value=\"Display Quiz summary\" ></form>\n");
  }
  outcome = check_termscore_option();
  fprintf(stdout,"<!-- Outcome of check_termscore_option()=%d -->\n",outcome);
  /*Termscore Button*/
  if( outcome ) {
    fprintf(stdout,"<li><form method=\"post\" ");
   sprintf(buf,"action=\"http://%s/%s/%s/capasbin\">",serverName,g_cgibin_path,g_cowner);
    fprintf(stdout,"%s\n", buf);
    fprintf(stdout,"<input type=\"hidden\" name=\"CLASS\" value=\"%s\">\n",class);
    fprintf(stdout,"<input type=\"hidden\" name=\"SNUM\" value=\"%s\">\n",sn);
    fprintf(stdout,"<input type=\"hidden\" name=\"CAPAID\" value=\"%d\">\n",pin);
    fprintf(stdout,"<input type=\"hidden\" name=\"M\" value=\"%d\" >\n",M_TERMSCORE );
    fprintf(stdout,"<input type=\"submit\" value=\"Extrapolate Term score\" ></form>\n");
  }
  /*Exit Button*/
  fprintf(stdout,"<TD><form method=\"get\" action=\"http://%s/CAPA/class.html\">",serverName);
  fprintf(stdout,"<input type=\"button\" value=\"Exit\" onclick=\"window.close()\"></form></TD>");

  fprintf(stdout,"</menu>\n"); fflush(stdout);
  
}

/* ====================================================================================== */
void
print_page_header(mode,num_quest) int mode;int num_quest;
{
  char   buf[MAX_BUFFER_SIZE], discussdir[MAX_BUFFER_SIZE];
  char *serverName;
  int    configResult,term_summary_button=1;

  buf[0]='\0';
  discussdir[0]='\0';
  serverName=getenv("SERVER_NAME");
  if (!serverName) {
    fprintf(stdout,"Enviroment variable SERVER_NAME not set.\n");
    fprintf(stdout,"Unable to complete actions.\n");
    return;
  }
  
  /* now done in the .qz file
  fprintf(stdout,"<TITLE>%s set%d</TITLE>",g_class_name,g_login_set);
  if( mode == VIEW_PREVIOUS_MODE ) {
    fprintf(stdout,"<H2>%s set%d</H2>\n",g_class_name,g_vset);
  } else {
    fprintf(stdout,"<H2>%s set%d</H2>\n",g_class_name,g_login_set);
  }
  fprintf(stdout,"<H3>%s</H3>\n",g_student_data.s_nm);
  */
  
  fprintf(stdout,"<A NAME=\"TOP\"></A>");
  fprintf(stdout,"<TABLE cellpadding=0 cellspacing=0 border=0>\n<TR><TD>");


  /*Term summary button*/
  configResult=read_capa_config("term_summary_button",buf);
  if (configResult != 0 && configResult != -1 ) {
    if (strcasecmp(buf,"no")==0) {
      term_summary_button=0;
    }
  }
#ifdef CGI_DBUG
  fprintf(g_cgi,"buf: %s\ntermsum: %d\n",buf,term_summary_button); fflush(g_cgi);
#endif /* CGI_DBUG */ 

  if (term_summary_button) {
    fprintf(stdout,"<form method=\"post\" ");
    sprintf(buf,"action=\"http://%s/%s/capahtml\">",serverName,g_cgibin_path);
    fprintf(stdout,"%s\n", buf);
    fprintf(stdout,"<input type=\"hidden\" name=\"CLASS\" value=\"%s\">\n",g_class_name);
    fprintf(stdout,"<input type=\"hidden\" name=\"SNUM\" value=\"%s\">\n",g_student_number);
    fprintf(stdout,"<input type=\"hidden\" name=\"CAPAID\" value=\"%d\">\n",g_entered_pin);
    fprintf(stdout,"<input type=\"hidden\" name=\"M\" value=\"%d\">\n",M_VIEWSUMM);
    fprintf(stdout,"<input type=\"submit\" value=\"Term summary\" ></form></TD>\n");
  }

  /*Exit Button*/
  fprintf(stdout,"<TD><form method=\"get\" action=\"http://%s/CAPA/class.html\">",serverName);
  fprintf(stdout,"<input type=\"button\" value=\"Exit\" onclick=\"window.close()\"></form></TD>");
  /*help button*/
  if (mode != VIEW_PREVIOUS_MODE) {
    fprintf(stdout,"<TD><form method=\"get\" action=\"http://%s/CAPA/help.html\">",serverName);
    fprintf(stdout,"<input type=\"submit\" value=\"Help\"></form></TD>");
  }

  /*Reload button*/
  fprintf(stdout,"<TD><form method=\"post\" ");
  sprintf(buf,"action=\"http://%s/%s/%s/capasbin\">",serverName,g_cgibin_path,g_cowner);
  fprintf(stdout,"%s\n", buf);
  fprintf(stdout,"<input type=\"hidden\" name=\"CLASS\" value=\"%s\">\n",g_class_name);
  fprintf(stdout,"<input type=\"hidden\" name=\"SNUM\" value=\"%s\">\n",g_student_number);
  fprintf(stdout,"<input type=\"hidden\" name=\"CAPAID\" value=\"%d\">\n",g_entered_pin);
  fprintf(stdout,"<input type=\"hidden\" name=\"STARTNUM\" value=\"%d\">\n",g_start_question);
  if (mode == VIEW_PREVIOUS_MODE ) {
    fprintf(stdout,"<input type=\"hidden\" name=\"M\" value=\"%d\">\n",M_VIEWPREV);
    fprintf(stdout,"<input type=\"hidden\" name=\"VSET\" value=\"%d\" size=4>\n",g_vset);
  } else {
    fprintf(stdout,"<input type=\"hidden\" name=\"M\" value=\"%d\">\n",M_TRYSET);
  }
  fprintf(stdout,"<input type=\"submit\" value=\"Reload\" >\n</form></TD>");
#ifdef NOT_DEFINED
  /* Next Button */
  if ( (!(g_num_questions_per_page==ALL_QUESTIONS)) && 
       ((g_num_questions_per_page+g_start_question)<num_quest)) {
    fprintf(stdout,"<TD><form method=\"post\" ");
    sprintf(buf,"action=\"http://%s/%s/%s/capasbin\">",serverName,g_cgibin_path,g_cowner);
    fprintf(stdout,"%s\n", buf);
    fprintf(stdout,"<input type=\"hidden\" name=\"CLASS\" value=\"%s\">\n",g_class_name);
    fprintf(stdout,"<input type=\"hidden\" name=\"SNUM\" value=\"%s\">\n",g_student_number);
    fprintf(stdout,"<input type=\"hidden\" name=\"CAPAID\" value=\"%d\">\n",g_entered_pin);
    fprintf(stdout,"<input type=\"hidden\" name=\"STARTNUM\" value=\"%d\">\n",g_start_question+g_num_questions_per_page);
    if (mode == VIEW_PREVIOUS_MODE ) {
      fprintf(stdout,"<input type=\"hidden\" name=\"M\" value=\"%d\">\n",M_VIEWPREV);
      fprintf(stdout,"<input type=\"hidden\" name=\"VSET\" value=\"%d\" size=4>\n",g_vset);
    } else {
      fprintf(stdout,"<input type=\"hidden\" name=\"M\" value=\"%d\">\n",M_TRYSET);
    }
    fprintf(stdout,"<input type=\"submit\" value=\"Next Page\" >\n</form></TD>");
  }

  /* Previous Button */
  if ( (!(g_num_questions_per_page==ALL_QUESTIONS)) && (g_start_question > 1)) {
    fprintf(stdout,"<TD><form method=\"post\" ");
    sprintf(buf,"action=\"http://%s/%s/%s/capasbin\">",serverName,g_cgibin_path,g_cowner);
    fprintf(stdout,"%s\n", buf);
    fprintf(stdout,"<input type=\"hidden\" name=\"CLASS\" value=\"%s\">\n",g_class_name);
    fprintf(stdout,"<input type=\"hidden\" name=\"SNUM\" value=\"%s\">\n",g_student_number);
    fprintf(stdout,"<input type=\"hidden\" name=\"CAPAID\" value=\"%d\">\n",g_entered_pin);
    fprintf(stdout,"<input type=\"hidden\" name=\"STARTNUM\" value=\"%d\">\n",g_start_question-g_num_questions_per_page);
    if (mode == VIEW_PREVIOUS_MODE ) {
      fprintf(stdout,"<input type=\"hidden\" name=\"M\" value=\"%d\">\n",M_VIEWPREV);
      fprintf(stdout,"<input type=\"hidden\" name=\"VSET\" value=\"%d\" size=4>\n",g_vset);
    } else {
      fprintf(stdout,"<input type=\"hidden\" name=\"M\" value=\"%d\">\n",M_TRYSET);
    }
    fprintf(stdout,"<input type=\"submit\" value=\"Previous Page\" >\n</form></TD>");
  }
#endif
  /* Goto Button */
  if (!(g_num_questions_per_page==ALL_QUESTIONS)) {
    int idx,numquest;
    T_header header;
    fprintf(stdout,"<TD><form method=\"post\" ");
    sprintf(buf,"action=\"http://%s/%s/%s/capasbin\">",serverName,g_cgibin_path,g_cowner);
    fprintf(stdout,"%s\n", buf);
    fprintf(stdout,"<input type=\"hidden\" name=\"CLASS\" value=\"%s\">\n",g_class_name);
    fprintf(stdout,"<input type=\"hidden\" name=\"SNUM\" value=\"%s\">\n",g_student_number);
    fprintf(stdout,"<input type=\"hidden\" name=\"CAPAID\" value=\"%d\">\n",g_entered_pin);
    fprintf(stdout,"<input type=\"submit\" value=\"Goto\" >");
    fprintf(stdout,"<b>Problem:</b><input name=\"STARTNUM\" value=\"\" size=4>\n");
    if (mode == VIEW_PREVIOUS_MODE ) {
      fprintf(stdout,"<input type=\"hidden\" name=\"M\" value=\"%d\">\n",M_VIEWPREV);
      fprintf(stdout,"<input type=\"hidden\" name=\"VSET\" value=\"%d\" size=4>\n",g_vset);
    } else {
      fprintf(stdout,"<input type=\"hidden\" name=\"M\" value=\"%d\">\n",M_TRYSET);
    }

    if (!capa_get_header(&header, g_login_set)) {
      numquest=atoi(header.num_questions);
      capa_mfree(header.weight);
      capa_mfree(header.partial_credit);
      for(idx=0;idx<numquest;idx++) {
	preserve_last_answer(idx,1);
      }
    }
    fprintf(stdout,"</form></TD>");
  }

  /*Discuss Button*/

  sprintf(discussdir,"%s/discussion/%d",g_class_fullpath,g_login_set);
  if ( access(discussdir,F_OK) == 0 ) {
    fprintf(stdout,"<TD><form method=\"post\" ");
    sprintf(buf,"action=\"http://%s/%s/%s/capadiscuss\">",serverName,g_cgibin_path,g_cowner);
    fprintf(stdout,"%s\n", buf);
    fprintf(stdout,"<input type=\"hidden\" name=\"CLASS\" value=\"%s\">\n",g_class_name);
    fprintf(stdout,"<input type=\"hidden\" name=\"SNUM\" value=\"%s\">\n",g_student_number);
    fprintf(stdout,"<input type=\"hidden\" name=\"CAPAID\" value=\"%d\">\n",g_entered_pin);
    fprintf(stdout,"<input type=\"hidden\" name=\"SETID\" value=\"%d\">\n",g_login_set);
    fprintf(stdout,"<input type=\"submit\" value=\"Discuss\" >\n</form></TD>");
  }

  fprintf(stdout,"\n</TR></TABLE>\n");
  fflush(stdout);    
}

void create_status_line(int mode,int question_cnt, T_entry* entry)
{
  char          buf[MAX_BUFFER_SIZE];
  int           idx,configResult,status_line_length;

  configResult=read_capa_config("web_status_line_length",buf);
  if (configResult != 0 && configResult != -1 ) {
    if (sscanf(buf,"%d",&status_line_length)==0) {
      status_line_length=question_cnt;
    }
  } else {
    status_line_length=question_cnt;
  }

  append_stext("<TABLE cellpadding=0 cellspacing=0 border=0><TR>");
  append_stext("<TD><b><u>Go to problem</u>  </b></TD><TD></TD>");
  for(idx=0; idx < status_line_length;idx++ ) {
    sprintf(buf,"<TD ALIGN=center VALIGN=bottom>[%d]</TD>",idx+1);
    append_stext(buf);
  }
  for(idx = 0; idx < question_cnt; idx++ ) {
    if ( !(idx%status_line_length) ) {
      sprintf(buf,"</TR><TR><TD ALIGN=left>%d-%d</TD><TD ALIGN=right>Status: </TD>",
		   idx+1,idx+status_line_length);
      append_stext(buf);
    }
    if ((idx >= g_start_question-1) && 
	 (((g_num_questions_per_page == ALL_QUESTIONS)) || 
	   (idx < (g_start_question+g_num_questions_per_page-1)))) {
      sprintf(buf,"<TD ALIGN=center VALIGN=bottom><A href=\"#P%d\">",idx+1);
    } else {
      sprintf(buf,"<TD ALIGN=center VALIGN=bottom>");
    }
    append_stext(buf);
    if ( (mode == CHECK_ANSWER_MODE) && g_log_string[idx] == '-') {
      if (g_inhibit_response && (entry->answers[idx]!='-')) {
	sprintf(buf,"A");
      } else {
	sprintf(buf,"%c",entry->answers[idx]); 
      }
    } else {
      if (g_inhibit_response && (entry->answers[idx]!='-')) {
	sprintf(buf,"<b>A</b>");
      } else {
	if ( mode == CHECK_ANSWER_MODE ) {
	  sprintf(buf,"<b>%c</b>",g_log_string[idx]);
	} else {
	  sprintf(buf,"<b>%c</b>",entry->answers[idx]);
	}
      }
    }
    append_stext(buf);
    if ((idx >= g_start_question-1) && 
	 (((g_num_questions_per_page == ALL_QUESTIONS)) || 
	   (idx < (g_start_question+g_num_questions_per_page-1)))) {
      sprintf(buf,"</A></TD>");
    } else {
      sprintf(buf,"</TD>");
    }
    append_stext(buf);
  }
  append_stext("</TR></TABLE>\n");
}

/* -------------------------------- called by try set, view previous, check answer */
void
print_quizz(class_dir,c_owner,class,sn,pin,set,mode)
char *class_dir; char *c_owner;char *class;char *sn;int pin;int set;int mode;
{
  extern int    Parsemode_f;
  extern char  *StartText_p;
  extern char  *EndText_p;
  Problem_t    *first_prob, *prob_idx;
  int           result, question_idx, question_cnt, idx, view_assignment_after_due=1;
  int           q_leng, display_ans=1, configResult;
  int           view_assignments_after_due=1;
  char          buf[MAX_BUFFER_SIZE];
  char          class_fullpath[FILE_NAME_LENGTH];
  T_entry       entry;
  T_header      header;
  long          offset;
  double        a;
  char          cmp_ans[MAX_BUFFER_SIZE],date_str[DATE_BUFFER];
  time_t        curtime;
  char         *serverName;
  char         *c_ans;

  serverName=getenv("SERVER_NAME");
  if (!serverName) {
    fprintf(stdout,"Enviroment variable SERVER_NAME not set.\n");
    fprintf(stdout,"Unable to complete actions.\n");
    return;
  }

  sprintf(class_fullpath,"%s/%s",class_dir,class);
  
  /*
  chdir(class_fullpath);
  */
#ifdef CGI_DBUG
  fprintf(g_cgi,"enter print_quizz() %s, mode:%d\n",class_fullpath,mode); fflush(g_cgi);
#endif /* CGI_DBUG */ 

  /* get configuration options */
  configResult=read_capa_config("num_questions_per_page",buf);
  if (configResult != 0 && configResult != -1 ) {
    if (sscanf(buf,"%d",&g_num_questions_per_page)==0) {
      g_num_questions_per_page=ALL_QUESTIONS;
    }
  } else {
    g_num_questions_per_page=ALL_QUESTIONS;
  }

  view_assignments_after_due=capa_check_option(OPTION_VIEW_PROBLEMS_AFTER_DUE,
					       set,g_student_data.s_sec);
  if (view_assignments_after_due < 0 ) view_assignments_after_due=1;
  g_inhibit_response=capa_check_option(OPTION_INHIBIT_RESPONSE,set,g_student_data.s_sec);
  if (g_inhibit_response < 0 ) g_inhibit_response=0;

#ifdef CGI_DBUG
  fprintf(g_cgi,"Set %d, Section%d, ViewAssign? %d, Inhibit Resp? %d\n",set,
	  g_student_data.s_sec,view_assignments_after_due,
	  g_inhibit_response); 
  fflush(g_cgi);
#endif /* CGI_DBUG */ 

  time(&curtime);
  offset=capa_get_entry(&entry,sn,set);          /* <-------- capa*() call  ---- */
  if( mode == VIEW_PREVIOUS_MODE ) {
    if( view_assignment_after_due ) {
      if( capa_check_date(CHECK_OPEN_DATE,g_student_number,
			  g_student_data.s_sec,set) < 0 ) {
	append_qtext("This set is not yet open.\n");
	return ;
      }
    } else {
      if( (capa_check_date(CHECK_ANS_DATE,g_student_number,
			   g_student_data.s_sec,set) < 0) &&
	  (capa_check_date(CHECK_DUE_DATE,g_student_number,
			   g_student_data.s_sec,set) > 0) ) {
	append_qtext("This set is not yet available to be viewed.\n");
	return ;
      }
    }
    if( capa_check_date(CHECK_ANS_DATE,g_student_number,
			g_student_data.s_sec,set) < 0 ) {
      display_ans = 0;
    }
  } 
  g_passdue = 0;
  if( mode == CHECK_ANSWER_MODE || 
      ( (!view_assignment_after_due) && mode == TRY_SET_MODE)) {
    if( capa_check_date(CHECK_DUE_DATE,g_student_number,
			g_student_data.s_sec,set) > 0 ) {
       capa_get_date(CHECK_DUE_DATE,g_student_number,
		     g_student_data.s_sec,set,date_str);
       sprintf(buf,"SORRY, the due date was: %s\n",date_str);
       append_qtext(buf);
       g_passdue = 1;
    }
  }

  if ((mode==CHECK_ANSWER_MODE) || (mode== TRY_SET_MODE) || (mode==VIEW_PREVIOUS_MODE))
    capa_set_login_time(g_student_number,set);

  capa_get_header(&header,set);

  sscanf(header.num_questions,"%d",&question_cnt);
  print_page_header(mode,question_cnt);

#ifdef CGI_DBUG
  fprintf(g_cgi,"DONE page header\n"); fflush(g_cgi);
#endif /* CGI_DBUG */

  if(offset < 0 ) offset = - offset;
  
  Parsemode_f = HTML_MODE; /* WEB_MODE */
  result = capa_parse(set, &first_prob, sn, &question_cnt,NULL);  /* <-- capa*() call */

#ifdef CGI_DBUG
  fprintf(g_cgi,"DONE capa_parse() [%d], pass due=%d\n",result,g_passdue); fflush(g_cgi);
#endif /* CGI_DBUG */

  if (StartText_p) printf(StartText_p);
 
#ifdef CGI_DBUG
  fprintf(g_cgi,"DONE Start Text\n"); fflush(g_cgi);
#endif /* CGI_DBUG */

  if ( result != 0 ) {
     if( !g_passdue ) {
       append_qtext("<FORM NAME=\"CAPA\" method=\"post\" ");
       sprintf(buf,"action=\"http://%s/%s/%s/capasbin\">",serverName,
	       g_cgibin_path,c_owner);
       append_qtext(buf);
       sprintf(buf,"<input type=\"hidden\" name=\"CLASS\" value=\"%s\">\n",class); append_qtext(buf);
       sprintf(buf,"<input type=\"hidden\" name=\"SNUM\" value=\"%s\">\n",sn);          append_qtext(buf);
       sprintf(buf,"<input type=\"hidden\" name=\"CAPAID\" value=\"%d\">\n",pin);       append_qtext(buf);
       sprintf(buf,"<input type=\"hidden\" name=\"SET\" value=\"%d\">\n",set);          append_qtext(buf);
       sprintf(buf,"<input type=\"hidden\" name=\"M\" value=\"%d\">\n",M_CHECKANS); append_qtext(buf);
       sprintf(buf,"<input type=\"hidden\" name=\"STARTNUM\" value=\"%d\">\n",g_start_question); append_qtext(buf);
       append_qtext("\n<OL>\n");
     }

     for(idx=0;idx<question_cnt;idx++) {         /* prepare log string and new database entry */
       g_new_answerdb[idx] = entry.answers[idx];
       g_log_string[idx]   = '-';
       sscanf(entry.tries + 3*idx,"%d,",&(g_tried[idx]) );
     }
     g_new_answerdb[question_cnt]=0;  g_log_string[question_cnt]=0;
     prob_idx = first_prob;
     for( question_idx = 0; question_idx < question_cnt; 
	  question_idx++,prob_idx = prob_idx->next ) {
#ifdef CGI_DBUG
  fprintf(g_cgi,"quetion_idx: %d, g_start_question:%d, g_num_que: %d\n",
	  question_idx,g_start_question,g_num_questions_per_page); fflush(g_cgi);
#endif /* CGI_DBUG */        
         if ((question_idx < g_start_question-1) 
	     ||
	     (((!(g_num_questions_per_page == ALL_QUESTIONS)) 
	       && 
	       (question_idx >= (g_start_question+g_num_questions_per_page-1))))) {
	   preserve_last_answer(question_idx,0);
	   continue;
	 }
         if( !g_passdue ) {
           sprintf(buf,"<A NAME=\"P%d\"></A>",question_idx+1);  append_qtext(buf);
	   /*	   if (!((question_idx == (g_start_question-1)) || (question_idx == 0))) {
	     append_qtext("<A href=\"#TOP\">Top</A>");
	     sprintf(buf,"&nbsp;&nbsp;<A href=\"#P%d\">Next</A>",question_idx+2);  append_qtext(buf);
	     }*/
	   if (prob_idx->question != NULL) {
	     q_leng = strlen(prob_idx->question);
	     if ( !prob_idx->show_br ) {
	       append_qtext(prob_idx->question);
	     } else {
	       append_qtext_addbr(prob_idx->question);
	       /*
	       for(idx=0;idx<q_leng;idx++) {
		 if ( g_qchar_cnt+2 > g_qsize-2 ) {
		   char *temp_text;
		   g_qsize=(g_qchar_cnt+2)*2;
		   temp_text=capa_malloc(g_qsize,sizeof(char));
		   strncpy(temp_text,g_question_txt,g_qsize);
		   capa_mfree(g_question_txt);
		   g_question_txt=temp_text;		 
		 }
		 g_question_txt[g_qchar_cnt]=prob_idx->question[idx];
		 g_qchar_cnt++;
		 if(prob_idx->question[idx] == '\n' ) {
		   append_qtext("<br>\n");
		 }
	       }
	       */
	     }
	   }
         }
         if(mode == VIEW_PREVIOUS_MODE) { /* VIEW_PREVIOUS_MODE */
	   /*
             if( prob_idx->ans_type == ANSWER_IS_FLOAT ) {
                 a = (double)atof(prob_idx->answer);
                 sprintf(cmp_ans,prob_idx->ans_fmt, a);
             } else {
	       if ( prob_idx->ans_type == ANSWER_IS_SUBJECTIVE) {
		 strcpy(cmp_ans,"Subjective Answer");
	       } else { 
		 if (prob_idx->answer) {
		   strcpy(cmp_ans,prob_idx->answer);
		 } else {
		   strcpy(cmp_ans,"No Answer");
		 }
	       }
             }
             if( prob_idx->ans_unit ) {
                 sprintf(buf,"<p><tt><b>Answer:</b> %s %s</tt><br>\n",cmp_ans, prob_idx->unit_str); 
             } else {
                 sprintf(buf,"<p><tt><b>Answer:</b> %s</tt><br>\n",cmp_ans); 
             }
	   */
           if( display_ans ) { 
	     c_ans=answers_string(ANSWER_STRING_MODE, prob_idx);
	     sprintf(buf,"<p><tt><b>Answer:</b> %s</tt><br>",c_ans);
             append_qtext(buf);
	     capa_mfree(c_ans);
	     if ( prob_idx->explain) {
	       sprintf(buf,"<p><b>Explanation: </b>\n<p>%s<br>\n",prob_idx->explain);
	       append_qtext(buf);
	     }
	   }
	 } else { /* could be TRY_SET_MODE, CHECK_ANSWER_MODE */
           if( g_passdue ) {
             get_response(header.partial_credit[question_idx],entry.answers[question_idx],question_idx,prob_idx);
           }else{
	     if (g_inhibit_response) {
	       print_inhibited_response(header.partial_credit[question_idx],entry.answers[question_idx],question_idx,prob_idx);
	     } else {
	       print_response(header.partial_credit[question_idx],entry.answers[question_idx],question_idx,prob_idx); 
	     }
             append_qtext("<br>\n");
             if( (g_tried[question_idx] >= prob_idx->show_hint) || 
		 (entry.answers[question_idx]=='Y') ||
		 (entry.answers[question_idx]=='y')) {
               if( prob_idx->hint ) {
                 sprintf(buf,"<p><B>Hint: </B>%s\n<br>\n", prob_idx->hint);
                 append_qtext(buf);
               }
             }
           }
         }
     }   /* ------------------------------------- end displaying each problem */
     append_qtext("\n</OL>\n");
     if( EndText_p )   append_qtext(EndText_p);
     free_problems(first_prob);
     free_units();
#ifdef CGI_DBUG
  fprintf(g_cgi,"End display each problem\n"); fflush(g_cgi);
#endif /* CGI_DBUG */

     if( mode == CHECK_ANSWER_MODE ) {  /* update the record database */
       if( !g_passdue ) {
         for(idx=0;idx<question_cnt;idx++){
           if( g_new_answerdb[idx] != entry.answers[idx]) { 
             entry.answers[idx] = g_new_answerdb[idx];
           }
           if(g_tried[idx] < 10 ) {
             entry.tries[3*idx]   = ' ';
             entry.tries[3*idx+1] = g_tried[idx] + '0';
             if(idx < question_cnt-1)  entry.tries[3*idx+2] = ',';
           } else {
             entry.tries[3*idx]   = (int)(g_tried[idx]/10) + '0';
             entry.tries[3*idx+1] = (g_tried[idx] % 10) + '0';
             if(idx < question_cnt-1)  entry.tries[3*idx+2] = ',';
           }
         }
         capa_set_entry(&entry,sn,set,offset);                     /* <-------- capa*() call */

#ifdef CGI_DBUG
	 fprintf(g_cgi,"DONE set db entry\n"); fflush(g_cgi);
#endif /* CGI_DBUG */

	 create_status_line(mode,question_cnt,&entry);
       } 
       if (w_log_attempt(g_student_number,set,g_log_string) == -1 ) {
	 fprintf(stdout,"<BOLD>Unable to log attempt. Please notify instructor.</BOLD>\n");
       }
     }
#ifdef CGI_DBUG
     fprintf(g_cgi,"DONE check answer mode\n"); fflush(g_cgi);
#endif /* CGI_DBUG */
     
     if( (mode == TRY_SET_MODE && !g_passdue)  || 
	 mode == VIEW_PREVIOUS_MODE) {
       create_status_line(mode,question_cnt,&entry);
     }
     
     if( !g_passdue ) {
       sprintf(buf,"</ul></form>\n"); append_qtext(buf);
     }
  }
}

/*if the assignment is passedue we come here to get what the answer was just in case*/
void
get_response(char pcr,char u_db,int q_idx,Problem_t *p)
{
  if( pcr == '0' || p->ans_type==ANSWER_IS_SUBJECTIVE ) { /* not hand-graded question */
     switch(u_db) {  /* what's from the user record */
       case 'Y': break;
       case 'y': break;
       case '-':
               if(g_stu_ans_pp[q_idx+1] != NULL ) log_user_ans(q_idx,p);
               break;
       case 'E':
       case 'e': break;
       case 'n': break;
       case 'N':
       case '0': case '1': case '2': case '3': case '4': 
       case '5': case '6': case '7': case '8': case '9':
               if(g_stu_ans_pp[q_idx+1] != NULL ) log_user_ans(q_idx,p);
               break;
       default:
	      break;
     }
   }
}

void display_last_answer(int q_idx)
{
  char       buf[MAX_BUFFER_SIZE];  
  StudentAnswer_t  *sa_p;
#ifdef CGI_DBUG
  fprintf(g_cgi,"Enter display_last_answer() [%d]\n",q_idx); fflush(g_cgi);
#endif /* CGI_DBUG */
  if(g_stu_ans_pp[q_idx+1] != NULL) {
    sa_p=g_stu_ans_pp[q_idx+1];
  } else {
    if (g_last_ans_pp[q_idx+1] != NULL) {
      sa_p=g_last_ans_pp[q_idx+1];
    } else {
      return;
    }
  }
  if (sa_p->a_next == NULL) {
    sprintf(buf,"<input type=\"hidden\" name=\"LAST%02d\" value=\"%s\">\n",
	    q_idx+1,sa_p->a_str);
    append_qtext(buf);
    sprintf(buf," <b>Last Answer:</b> %s\n",sa_p->a_str);
    append_qtext(buf);
  } else {
    while(sa_p) {
      sprintf(buf,"<input type=\"hidden\" name=\"LAST%02d,%02d\" value=\"%s\">\n",
	      q_idx+1,sa_p->a_idx,sa_p->a_str);
      append_qtext(buf);
      sprintf(buf," <b>Last Answer %d:</b> %s\n",sa_p->a_idx,sa_p->a_str);
      append_qtext(buf);
      sa_p=sa_p->a_next;
    }
  }
}

void preserve_last_answer(int q_idx,int print)
{
  char       buf[MAX_BUFFER_SIZE];  
  StudentAnswer_t  *sa_p;
#ifdef CGI_DBUG
  fprintf(g_cgi,"Enter preserve_last_answer() [%d]\n",q_idx); fflush(g_cgi);
#endif /* CGI_DBUG */
  if(g_stu_ans_pp[q_idx+1] != NULL) {
    sa_p=g_stu_ans_pp[q_idx+1];
  } else {
    if (g_last_ans_pp[q_idx+1] != NULL) {
      sa_p=g_last_ans_pp[q_idx+1];
    } else {
      return;
    }
  }
  if (sa_p->a_next == NULL) {
    sprintf(buf,"<input type=\"hidden\" name=\"LAST%02d\" value=\"%s\">\n",
	    q_idx+1,sa_p->a_str);
    if (print) printf(buf); else append_qtext(buf);
  } else {
    while(sa_p) {
      sprintf(buf,"<input type=\"hidden\" name=\"LAST%02d,%02d\" value=\"%s\">\n",
	      q_idx+1,sa_p->a_idx,sa_p->a_str);
      if (print) printf(buf); else append_qtext(buf);
      sa_p=sa_p->a_next;
    }
  }
}

void display_last_subjective(int q_idx)
{
  char      *buf;
  char      *answer;
#ifdef CGI_DBUG
  fprintf(g_cgi,"Enter display_last_subjective() [%d]\n",q_idx); fflush(g_cgi);
#endif /* CGI_DBUG */
  answer=capa_get_subjective(g_login_set,q_idx+1,g_student_number);
  if (answer==NULL) return;
#ifdef CGI_DBUG
  fprintf(g_cgi,"Found answer %s\n",answer); fflush(g_cgi);
#endif /* CGI_DBUG */
  buf=capa_malloc(MAX_BUFFER_SIZE+strlen(answer),1);
  /* don't need to stick in a last since we always get it from the files */
  /*  sprintf(buf,"<input type=\"hidden\" name=\"LAST%02d\" value=\"%s\">\n",q_idx+1,answer);*/
  append_qtext(buf);
  append_qtext("<b>Current Submission:</b><br>\n");
  append_qtext("<TABLE BORDER=1 CELLSPACING=0>\n<TR><TD>\n");
  append_qtext("<PRE>");
  append_qtext(answer);
  append_qtext("</PRE>");
  append_qtext("</TD></TR></TABLE>\n");
  capa_mfree(buf);
  capa_mfree(answer);
}

void create_answer_area(Problem_t *p,int q_idx) 
{
  int ii=0;
  char       buf[MAX_BUFFER_SIZE];  
  AnswerInfo_t *ai;
#ifdef CGI_DBUG
  fprintf(g_cgi,"Enter create_answer_area() [%d]\n",q_idx); fflush(g_cgi);
#endif /* CGI_DBUG */

  if ( p->ans_type==ANSWER_IS_SUBJECTIVE ) {
    display_last_subjective(q_idx);
  }
  if ( p->show_ans_box ) { 
    if ( p->ans_op == ANS_AND ) {
      if (p->ans_type == ANSWER_IS_FORMULA) {
	/* first answer is stored in p, the rest are linked off of p->ans_list */
	sprintf(buf,"<p><B>Answer %d of %d:</B><input size=80 name=\"INPUT%02d,%02d\" value=\"\">\n",ii+1,p->ans_cnt,q_idx+1,ii+1);
      } else {
	sprintf(buf,"<p><B>Answer %d of %d:</B><input name=\"INPUT%02d,%02d\" value=\"\">\n",ii+1,p->ans_cnt,q_idx+1,ii+1);
      }
      append_qtext(buf);
      for(ii=1, ai=p->ans_list;ii<p->ans_cnt;ai=ai->ans_next,ii++) {
	if (ai->ans_type == ANSWER_IS_FORMULA) {
	  sprintf(buf,"<p><B>Answer %d of %d:</B><input size=80 name=\"INPUT%02d,%02d\" value=\"\">\n",ii+1,p->ans_cnt,q_idx+1,ii+1);
	} else {
	  sprintf(buf,"<p><B>Answer %d of %d:</B><input name=\"INPUT%02d,%02d\" value=\"\">\n",ii+1,p->ans_cnt,q_idx+1,ii+1);
	}
	append_qtext(buf);
      }
    } else { /* single answer, or /OR answers, or subjective answer */
      if (p->ans_type == ANSWER_IS_SUBJECTIVE ) {
	sprintf(buf,"<p><B>Answer:</B><br><TEXTAREA name=\"INPUT%02d\" rows=\"15\" cols=\"80\"></TEXTAREA>\n",q_idx+1);
      } else {
	if (p->ans_type == ANSWER_IS_FORMULA) {
	  sprintf(buf,"<p><B>Answer:</B><input size=80 name=\"INPUT%02d\" value=\"\">\n",q_idx+1);
	} else {
	  sprintf(buf,"<p><B>Answer:</B><input name=\"INPUT%02d\" value=\"\">\n",q_idx+1);
	}
      }
      append_qtext(buf);
    }
  }
  append_qtext("<input type=\"submit\" value=\"Submit All Answers\" >\n");
  if ( p->ans_type!=ANSWER_IS_SUBJECTIVE ) {
    display_last_answer(q_idx);
  }
}

void
print_response(char pcr,char u_db,int q_idx,Problem_t *p)
{
int   a_tpe;
char *c_ans,*response,*answered="Answered",*nycorrect="Not yet correct";
int   t_tpe;
double tol;
int    sig_l;
int    sig_u;
char  *a_fmt, *c_answer_str;
int    tries;

    char       buf[MAX_BUFFER_SIZE];

 a_tpe = p->ans_type;
 c_ans = p->answer;
 t_tpe = p->tol_type;
 tol   = p->tolerance;
 sig_l = p->sig_lbound;
 sig_u = p->sig_ubound;
 a_fmt = p->ans_fmt;
 tries = p->tries;
 response=nycorrect;

#ifdef CGI_DBUG
  fprintf(g_cgi,"Enter print_response() [%c]\n",u_db); fflush(g_cgi);
#endif /* CGI_DBUG */
  if( pcr == '0' || a_tpe==ANSWER_IS_SUBJECTIVE ) { /* not hand-graded question */
     switch(u_db) {  /* this is from the user record */
       case 'Y':
               c_answer_str = answers_string(ANSWER_STRING_MODE, p);
               sprintf(buf,"<p><tt>Correct, computer gets: %s</tt>\n", c_answer_str);
               append_qtext(buf);
               capa_mfree((char *)c_answer_str);
               break;
       case 'y':
               append_qtext("<p><tt>Hand-graded Correct</tt>\n");
               break;
       case '-':
               if(g_stu_ans_pp[q_idx+1] == NULL ) {
		 create_answer_area(p,q_idx);
	       } else {
                 check_user_ans(q_idx,p);
               }
               break;
       case 'E':
       case 'e': append_qtext("<p><tt>Excused</tt>\n");               break;
       case 'n': append_qtext("<p><tt>Hand-graded Incorrect</tt>\n"); 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(g_stu_ans_pp[q_idx+1] == NULL ) {
		 if( g_tried[q_idx] < tries) {
		   create_answer_area(p,q_idx);
		   if( tries - g_tried[q_idx] == 1) {
		     sprintf(buf,"<br><tt>%s, ONE try left!!</tt>\n",response);
		   }else{
		     sprintf(buf,"<br><tt>%s, tries %d/%d</tt>\n",response,
			     g_tried[q_idx],tries);
		   }  
		   append_qtext(buf);
		 }else{
		   if (p->ans_type==ANSWER_IS_SUBJECTIVE)
		     display_last_answer(q_idx);
		   else
		     display_last_subjective(q_idx);
		   append_qtext("<br><tt>No more tries.</tt>\n");
		 } 
               } else { /* answering this question */
		 if( g_tried[q_idx] < tries) {
		   check_user_ans(q_idx,p);
		 } else {
		   if (p->ans_type==ANSWER_IS_SUBJECTIVE)
		     display_last_answer(q_idx);
		   else
		     display_last_subjective(q_idx);
		   append_qtext("<br><tt>No more tries.</tt>\n");
		 }
	       }
               break;
     }
   } else {
     append_qtext("<p><tt>Question to be Graded Manually.</tt>\n");
     
   }

}

void
print_inhibited_response(char pcr,char u_db,int q_idx,Problem_t *p)
{
int   a_tpe;
char *c_ans;
int   t_tpe;
double tol;
int    sig_l;
int    sig_u;
char  *a_fmt;
int    tries;
char       buf[MAX_BUFFER_SIZE];

 a_tpe = p->ans_type;
 c_ans = p->answer;
 t_tpe = p->tol_type;
 tol   = p->tolerance;
 sig_l = p->sig_lbound;
 sig_u = p->sig_ubound;
 a_fmt = p->ans_fmt;
 tries = p->tries;

#ifdef CGI_DBUG
  fprintf(g_cgi,"Enter print_inhibited_response() [%c]\n",u_db); fflush(g_cgi);
#endif /* CGI_DBUG */

  if( pcr == '0' || a_tpe==ANSWER_IS_SUBJECTIVE ) { /* not hand-graded question */
     switch(u_db) {  /* this is from the user record */
       case '-': 
               if(g_stu_ans_pp[q_idx+1] == NULL ) {
		 create_answer_area(p,q_idx);
               } else {
                 check_inhibited_user_ans(q_idx,p);
               }
               break;
       case 'Y': case 'y':
       case 'E': case 'e': 
       case 'n': case 'N': 
       case '0': case '1': case '2': case '3': case '4': 
       case '5': case '6': case '7': case '8': case '9':
               if(g_stu_ans_pp[q_idx+1] == NULL ) {
		 if( g_tried[q_idx] < tries) {
		   create_answer_area(p,q_idx);	
		   if( tries - g_tried[q_idx] == 1) {
		     append_qtext("<br><tt>Answered, ONE try left!!</tt>\n");
		   }else{
		     sprintf(buf,"<br><tt>Answered, tries %d/%d</tt>\n",g_tried[q_idx],tries);
		     append_qtext(buf);
		   }  
		 }else{
		   if (p->ans_type==ANSWER_IS_SUBJECTIVE)
		     display_last_answer(q_idx);
		   else
		     display_last_subjective(q_idx);
		   append_qtext("<br><tt>Answered,No more tries.</tt>\n");
		 } 
	       } else { /* answering this question */
		 if( g_tried[q_idx] < tries) {
		   check_inhibited_user_ans(q_idx,p);
		 } else {
		   if (p->ans_type==ANSWER_IS_SUBJECTIVE)
		     display_last_answer(q_idx);
		   else
		     display_last_subjective(q_idx);
		   append_qtext("<br><tt>Answered, No more tries.</tt>\n");
		 }
               }
               break;
     }
   } else {
     append_qtext("<p><tt>Question to be Graded Manually.</tt>\n"); 
   }
}

/* returns a -1 if there were not enough answers, otherwise the number of responses
   for the question is returned
   !!!!!AS A SIDEEFFECT IT ALSO CROPS ANSWERS TO ANSWER_STRING_LENG!!!!!!!
*/
int gather_answers(char ***ans,int q_idx,Problem_t *p)
{
  int cnt;
  if(p->ans_op==ANS_AND) {
    int i; StudentAnswer_t *sa_p;
    *ans=(char**)capa_malloc(p->ans_cnt,sizeof(char*));
    sa_p= g_stu_ans_pp[q_idx+1];
    for(i=0;((i<p->ans_cnt)&&(sa_p));i++){
      ans[0][i]=sa_p->a_str;
      if ((strlen(ans[0][i])+1) > ANSWER_STRING_LENG) ans[0][i][ANSWER_STRING_LENG]='\0';
      sa_p=sa_p->a_next;
    }
    cnt=p->ans_cnt;
    if (i<p->ans_cnt) return -1;
  } else {
    *ans=(char**)capa_malloc(p->ans_cnt,sizeof(char*));
    ans[0][0]=g_stu_ans_pp[q_idx+1]->a_str;
    if ((strlen(ans[0][0])+1) > ANSWER_STRING_LENG) ans[0][0][ANSWER_STRING_LENG]='\0';
    cnt=1;
  }
  return cnt;
}

/*logging user's answer when it is passed due.*/
void
log_user_ans(int q_idx,Problem_t *p)
{
  char **ans;
  char *error;
  int cnt;
  if (p->ans_type==ANSWER_IS_SUBJECTIVE) {
      /*capa_set_subjective(g_login_set,q_idx+1,g_student_number,
	g_stu_ans_pp[q_idx+1]->a_str);*/
  } else {
    if (-1 != (cnt=gather_answers(&ans,q_idx,p))) {
      switch( capa_check_answers(p,ans,cnt,&error) ) {
        case  EXACT_ANS:  g_log_string[q_idx]='Y'; break;
        case  APPROX_ANS: g_log_string[q_idx]='Y'; break;
        case  SIG_FAIL:   g_log_string[q_idx]='S'; capa_mfree(error); break;
        case  UNIT_FAIL:  g_log_string[q_idx]='U'; capa_mfree(error); break;
        case  UNIT_NOTNEEDED:  g_log_string[q_idx]='U'; capa_mfree(error); break;
        case  NO_UNIT:    g_log_string[q_idx]='u'; break;
        case  BAD_FORMULA:  g_log_string[q_idx]='F'; break;
        case  INCORRECT:  g_log_string[q_idx]='N'; break;
        case  WANTED_NUMERIC:  g_log_string[q_idx]='s'; break;
      }
    }
  }
}

void submit_subjective(int q_idx,Problem_t *p)
{
  char buf[MAX_BUFFER_SIZE];
  if (capa_set_subjective(g_login_set,q_idx+1,g_student_number,
			  g_stu_ans_pp[q_idx+1]->a_str)<0){
    sprintf(buf,"<p><tt>Falied to record last submission.</tt><br>\n");
    g_tried[q_idx]--;
  } else {
    sprintf(buf,"<p><tt>Current submission recorded.</tt><br>\n");
    g_new_answerdb[q_idx] = '0';
    g_log_string[q_idx]='A';
  }
  append_qtext(buf);
  if (g_tried[q_idx]<p->tries) {
    create_answer_area(p,q_idx);
    if( p->tries - g_tried[q_idx] == 1) {
      append_qtext("<br><tt>ONE try left</tt>\n");
    }else{
      sprintf(buf,"<br><tt>tries %d/%d</tt>\n",g_tried[q_idx],p->tries);
      append_qtext(buf);
    }  
  }else{
    display_last_answer(q_idx);
    append_qtext("<br><tt>No more tries.</tt>\n");
  } 
}

void
check_user_ans(int q_idx,Problem_t *p)
{
int a_tpe,cnt;
char *c_ans,**ans;
int   t_tpe;
double tol;
int    sig_l;
int    sig_u;
char  *a_fmt;
int    tries;
  char       buf[MAX_BUFFER_SIZE];
  char *error;

  a_tpe = p->ans_type;
  t_tpe = p->tol_type;
  tol   = p->tolerance;
  sig_l = p->sig_lbound;
  sig_u = p->sig_ubound;
  a_fmt = p->ans_fmt;
  tries = p->tries;

#ifdef CGI_DBUG
  fprintf(g_cgi,"Enter check_user_ans() anstype=%d\n",a_tpe); fflush(g_cgi);
#endif /* CGI_DBUG */

  g_tried[q_idx]++;

#ifdef CGI_DBUG
  fprintf(g_cgi,"Call capa_check_answer() with [%s]\n",g_stu_ans_pp[q_idx+1]->a_str); fflush(g_cgi);
#endif /* CGI_DBUG */
  if (a_tpe==ANSWER_IS_SUBJECTIVE) {
    submit_subjective(q_idx,p);
    return;
  }
  
  cnt=gather_answers(&ans,q_idx,p);
  if (cnt == -1) {
    g_tried[q_idx]--;
    create_answer_area(p,q_idx);
    if( (tries - g_tried[q_idx]) == 1 ) {
      append_qtext("<br><tt>Not all answers submitted, ONE try left!!</tt>\n");
    }else{
      sprintf(buf,"<br><tt>Not all answers submitted, tries %d/%d.</tt>\n",
	      g_tried[q_idx],tries);
      append_qtext(buf);
    }
    return;
  }

  switch( capa_check_answers(p,ans,cnt,&error) ) {
    case  EXACT_ANS:    
    case  APPROX_ANS: 
                   c_ans=answers_string(ANSWER_STRING_MODE, p);
                   sprintf(buf,"<p><tt>Yes, Computer gets: %s</tt>\n", c_ans);
                   append_qtext(buf);
                   g_new_answerdb[q_idx] = 'Y';
                   g_log_string[q_idx]='Y';
		   capa_mfree(c_ans);
		   break;
    case  WANTED_NUMERIC:
                   create_answer_area(p,q_idx);
		   g_tried[q_idx]--;  /* don't count as a try */
		   sprintf(buf,"<br><tt>This question expects a numeric answer, tries %d/%d.</tt>\n",g_tried[q_idx],tries);
		   append_qtext(buf);
		   g_new_answerdb[q_idx] = 'N';
                   g_log_string[q_idx]='s';
                   break;
    case  SIG_FAIL:
                   create_answer_area(p,q_idx);
		   g_tried[q_idx]--;  /* don't count as a try */
		   sprintf(buf,"<br><tt>Please adjust significant figures, you provided %s significant figures, tries %d/%d.</tt>\n",error,g_tried[q_idx],tries);
		   append_qtext(buf);
		   capa_mfree(error);
		   g_new_answerdb[q_idx] = 'N';
                   g_log_string[q_idx]='S';
                   break;
    case  UNIT_FAIL:
                   create_answer_area(p,q_idx);
                   g_tried[q_idx]--;  /* don't count as a try */
		   sprintf(buf,"<br><tt>Units incorrect, Computer reads units as %s, tries %d/%d.</tt>\n",error,g_tried[q_idx],tries);
		   capa_mfree(error);
		   append_qtext(buf);
		   g_new_answerdb[q_idx] = 'N';
                   g_log_string[q_idx]='U';
                   break;
    case  UNIT_NOTNEEDED:
                   create_answer_area(p,q_idx);
                   g_tried[q_idx]--;  /* don't count as a try */
                   if(tries > 0) {
                     sprintf(buf,"<br><tt>Only a number required, Computer reads units of %s, tries %d/%d.</tt>\n",error,g_tried[q_idx],tries);
                     append_qtext(buf);
                   }
		   capa_mfree(error);
                   g_new_answerdb[q_idx] = 'N';
                   g_log_string[q_idx]='U';
                   break;
    case  NO_UNIT: 
                   create_answer_area(p,q_idx);
                   g_tried[q_idx]--;  /* don't count as a try */
		   sprintf(buf,"<br><tt>Units required, tries %d/%d.</tt>\n",g_tried[q_idx],tries);
		   append_qtext(buf);
		   g_new_answerdb[q_idx] = 'N';
                   g_log_string[q_idx]='u';
                   break;
    case  BAD_FORMULA: 
                   create_answer_area(p,q_idx);
                   g_tried[q_idx]--;  /* don't count as a try */
		   sprintf(buf,"<br><tt>Unable to understand formula, tries %d/%d.</tt>\n",g_tried[q_idx],tries);
		   append_qtext(buf);
		   g_new_answerdb[q_idx] = 'N';
                   g_log_string[q_idx]='F';
                   break;
    case  INCORRECT:
                   if( g_tried[q_idx] < tries ) {
		     create_answer_area(p,q_idx);
		     if( (tries - g_tried[q_idx]) == 1 ) {
		       append_qtext("<br><tt>Incorrect, ONE try left!!</tt>\n");
		     }else{
		       sprintf(buf,"<br><tt>Incorrect, tries %d/%d.</tt>\n",g_tried[q_idx],tries);
		       append_qtext(buf);
		     }
		   } else {
		     display_last_answer(q_idx);
		     append_qtext("<br><tt>Incorrect, no more tries.</tt>\n");
		   }
                   g_new_answerdb[q_idx] = 'N';
                   g_log_string[q_idx]='N';
                   break;
  }
}

void
check_inhibited_user_ans(int q_idx,Problem_t *p)
{
int a_tpe,cnt;
char *c_ans,**ans;
int   t_tpe;
double tol;
int    sig_l;
int    sig_u;
char  *a_fmt;
int    tries;
  char       buf[MAX_BUFFER_SIZE];
  char *error;

  a_tpe = p->ans_type;
  c_ans = p->answer;
  t_tpe = p->tol_type;
  tol   = p->tolerance;
  sig_l = p->sig_lbound;
  sig_u = p->sig_ubound;
  a_fmt = p->ans_fmt;
  tries = p->tries;
  
#ifdef CGI_DBUG
  fprintf(g_cgi,"Enter check_inhibited_user_ans() anstype=%d\n",a_tpe); fflush(g_cgi);
#endif /* CGI_DBUG */

  g_tried[q_idx]++;

#ifdef CGI_DBUG
  fprintf(g_cgi,"Call capa_check_answer() with [%s]\n",g_stu_ans_pp[q_idx+1]->a_str); 
  fflush(g_cgi);
#endif /* CGI_DBUG */
  if (a_tpe==ANSWER_IS_SUBJECTIVE) {
    submit_subjective(q_idx,p);
    return;
  }

  cnt=gather_answers(&ans,q_idx,p);
  if (cnt == -1) {
    g_tried[q_idx]--;
    create_answer_area(p,q_idx);
    if( (tries - g_tried[q_idx]) == 1 ) {
      append_qtext("<br><tt>Not all answers submitted, ONE try left!!</tt>\n");
    }else{
      sprintf(buf,"<br><tt>Not all answers submitted, tries %d/%d.</tt>\n",
	      g_tried[q_idx],tries);
      append_qtext(buf);
    }
    return;
  }

  switch( capa_check_answers(p,ans,cnt,&error) ) {
    case  EXACT_ANS:
    case  APPROX_ANS: 
                   g_new_answerdb[q_idx] = 'Y';
                   g_log_string[q_idx]='Y';
                   break;
    case  WANTED_NUMERIC:
                   g_new_answerdb[q_idx] = 'N';
                   g_log_string[q_idx]='s';
                   break;
    case  SIG_FAIL:
                   g_new_answerdb[q_idx] = 'N';
                   g_log_string[q_idx]='S';
		   capa_mfree(error);
                   break;
    case  UNIT_FAIL:
                   g_new_answerdb[q_idx] = 'N';
                   g_log_string[q_idx]='U';
		   capa_mfree(error);
                   break;
    case  UNIT_NOTNEEDED:
                   g_new_answerdb[q_idx] = 'N';
                   g_log_string[q_idx]='U';
		   capa_mfree(error);
                   break;
    case  NO_UNIT:
                   g_new_answerdb[q_idx] = 'N';
                   g_log_string[q_idx]='u';
                   break;
    case  BAD_FORMULA:
                   g_new_answerdb[q_idx] = 'N';
                   g_log_string[q_idx]='F';
                   break;
    case  INCORRECT:
                   g_new_answerdb[q_idx] = 'N';
                   g_log_string[q_idx]='N';
                   break;
  }

  if( g_tried[q_idx] < tries ) {
    create_answer_area(p,q_idx);
    if( (tries - g_tried[q_idx]) == 1 ) {
      append_qtext("<br><tt>Answered, ONE try left!!</tt>\n");
    }else{
      sprintf(buf,"<br><tt>Answered, tries %d/%d.</tt>\n",g_tried[q_idx],tries);
      append_qtext(buf);
    }
  } else {
    display_last_answer(q_idx);
    append_qtext("<br><tt>Answered, no more tries.</tt>\n");
  }
}

void                                     /* RETURNS: (nothing)          */
print_summary(class_dir,class,student_number,pin,set)
char *class_dir;char *class;char *student_number;int pin;int set;
{                                       /* LOCAL VARIABLES:            */
  int      set_idx,                     /*    Set counter              */
           i,                           /*    Question counter         */
           set_score,                   /*    Score on a set           */
           term_score=0,                /*    Total points received    */
           term_valid=0,                /*    Total points possible    */
           result,
           tot_num_sets=0;
  T_entry  entry;                       /*    Database entry for a set */
  char     buf[MAX_BUFFER_SIZE]; /* Output line buffer  */
  char     buf2[MAX_BUFFER_SIZE]; /* Output line buffer  */
  T_header header;                      /*    Problem set header       */
  int      question_cnt,valid_wgt, rate,configResult,
    status_line_length=DEFAULT_STATUS_LINE_LENGTH,row;
  char     class_fullpath[ONE_K],*serverName;

  serverName=getenv("SERVER_NAME");
  if (!serverName) {
    fprintf(stdout,"Enviroment variable SERVER_NAME not set.\n");
    fprintf(stdout,"Unable to complete actions.\n");
    return;
  }
  printf("<!--print_summary-->");
  sprintf(class_fullpath,"%s/%s",class_dir,class);
  chdir(class_fullpath);
  configResult=read_capa_config("web_status_line_length",buf);
  if (configResult != 0 && configResult != -1 ) {
    if (sscanf(buf,"%d",&status_line_length)==0) {
      status_line_length=DEFAULT_STATUS_LINE_LENGTH;
    }
  } else {
    status_line_length=DEFAULT_STATUS_LINE_LENGTH;
  }
  
  printf("<TABLE>\n<TR><TD></TD>\n");
  for(i=0;i<status_line_length;i++) {
    printf("<TD align=center valign=bottom>%d</TD>\n",i+1);
  }
  printf("</TR>");

  for (set_idx=1; set_idx<=set; set_idx++) {
    g_inhibit_response=capa_check_option(OPTION_INHIBIT_RESPONSE,set_idx,
					 g_student_data.s_sec);
    if (g_inhibit_response > 0) {
      printf("<!-- Set %d is inhibited -->\n",set_idx);
      continue;
    }
    if ( capa_check_date(CHECK_OPEN_DATE,g_student_number,
			     g_student_data.s_sec,set_idx) < 0 ){
      printf("<!-- Set %d is not open -->\n",set_idx);
      continue;
    }

    if (capa_get_header(&header,set_idx))  return;
    tot_num_sets++;
    capa_get_entry(&entry,student_number,set_idx);
    sscanf(header.num_questions,"%d", &(question_cnt) );
    valid_wgt = 0; set_score = 0;
    header.weight[question_cnt] = '\0';
    header.partial_credit[question_cnt] = '\0';
    for (i=0; i<question_cnt; i++) {
      valid_wgt +=  (header.weight[i] - '0');
      if((entry.answers[i]=='Y') || (entry.answers[i]=='y'))  
	set_score += (header.weight[i]-'0');
      if((entry.answers[i]=='E') || (entry.answers[i]=='e'))  
	valid_wgt -= (header.weight[i] - '0');
      if((entry.answers[i]>='0') && (entry.answers[i]<='9'))  
	set_score += (entry.answers[i] - '0');
    }
    term_valid += valid_wgt;
    term_score += set_score;

    if( valid_wgt != 0 ) {
      rate = 100*set_score / valid_wgt;
      printf("<TR><TD nowrap align=center valign=bottom>set <B>%d</B>, %d/%d(%d %%)  </TD>",set_idx,set_score,valid_wgt,rate);
    } else {
      printf("<TR><TD nowrap align=center valign=bottom>set <B>%d</B>,   0/0(0 %%)   </TD>",set_idx);
    }
    for(row=0;row<=(question_cnt/status_line_length);row++) {
      for(i=(row*status_line_length);
	  ((i<question_cnt)&&(i<((row+1)*status_line_length))); i++) {
	if (i != 0 && (!(i%status_line_length))) { printf("</TR><TD></TD>"); }
	printf("<TD align=center valign=bottom><tt>%c</tt></TD>\n",entry.answers[i]);
      }
      printf("</TR>\n<TR><TD></TD>");
      for(i=(row*status_line_length);
	  ((i<question_cnt)&&(i<((row+1)*status_line_length))); i++) {
	if (i != 0 && (!(i%status_line_length))) { printf("</TR><TD></TD>"); }
	printf("<TD align=center valign=bottom><tt>%c</tt></TD>\n",header.weight[i]);
      }
    }
    printf("</TR>");
    capa_mfree(header.weight);
    capa_mfree(header.partial_credit);
  }
  printf("\n</TABLE>\n<hr>\n");
  /* SHOW TOTALS */
  /* if capalogin_show_summary_score is set to none don't show it */
  if (term_valid > 0) {
    sprintf(buf,"%d sets, total = %3d/%3d (%d%%)\n", tot_num_sets, term_score, term_valid, 100*term_score/term_valid);
  } else {
    sprintf(buf,"%d sets, total = %3d/%3d\n", tot_num_sets, term_score, term_valid);
  }
  result=read_capa_config("capalogin_show_summary_score",buf2);
  if (result != 0 && result != -1) {
    if (strcasecmp(buf2,"none")==0) {
    } else {
      printf("%s",buf);
    }
  } else {
    printf("%s",buf);
  }

  printf("<TABLE cellpadding=0 cellspacing=0 border=0>\n<TR><TD>");
  printf("<form method=\"post\" ");
  sprintf(buf,"action=\"http://%s/%s/%s/capasbin\">",serverName,g_cgibin_path,g_cowner);
  printf("%s\n", buf);
  printf("<input type=\"hidden\" name=\"CLASS\" value=\"%s\">\n",g_class_name);
  printf("<input type=\"hidden\" name=\"SNUM\" value=\"%s\">\n",g_student_number);
  printf("<input type=\"hidden\" name=\"CAPAID\" value=\"%d\">\n",g_entered_pin);
  printf("<input type=\"hidden\" name=\"M\" value=\"%d\">\n",M_CHECKIN);
  printf("<input type=\"submit\" value=\"Main menu\" ></form></TD>\n");
  printf("<TD><form method=\"get\" action=\"http://%s/CAPA/class.html\">",serverName); 
  printf("<input type=\"button\" value=\"Exit\" onclick=\"window.close()\"></form></TD>");
  printf("\n</TABLE>\n");
}


void
process_mode(int mode) {

#ifdef CGI_DBUG
  fprintf(g_cgi,"entered process_mode[%d]\n",mode); fflush(g_cgi);
#endif /* CGI_DBUG */
  g_qchar_cnt=g_schar_cnt=0;
  g_qsize=TEXT_BUF_SIZE*sizeof(char);
  g_ssize=STATUS_BUF_SIZE*sizeof(char);
  g_question_txt=capa_malloc(TEXT_BUF_SIZE,sizeof(char));
  g_status_txt  =capa_malloc(STATUS_BUF_SIZE,sizeof(char));
#ifdef CGI_DBUG
  fprintf(g_cgi,"alloced everything\n"); fflush(g_cgi);
#endif /* CGI_DBUG */
  if( mode == VIEW_PREVIOUS_MODE ) {
    print_quizz(g_cpath,g_cowner,g_class_name,g_student_number, g_entered_pin, g_vset,mode); 
  } else if( mode == TRY_SET_MODE ) {
    print_quizz(g_cpath,g_cowner,g_class_name,g_student_number, g_entered_pin, g_login_set,mode);
  } else {
    print_quizz(g_cpath,g_cowner,g_class_name,g_student_number,g_entered_pin,g_login_set,CHECK_ANSWER_MODE);
  }
  g_status_txt[g_schar_cnt]=0;
  g_question_txt[g_qchar_cnt]=0;
  if( g_schar_cnt != 0 ) {
         fprintf(stdout,"%s",g_status_txt);
#ifdef CGI_DBUG
  fprintf(g_cgi,"print status [%s]\n",g_status_txt); fflush(g_cgi);
#endif /* CGI_DBUG */
  }
  if( g_qchar_cnt != 0) {
         fprintf(stdout,"%s",g_question_txt);
#ifdef CGI_DBUG
  fprintf(g_cgi,"print question [%s]\n",g_question_txt); fflush(g_cgi);
#endif /* CGI_DBUG */
  }
  if( g_schar_cnt != 0 ) {
         fprintf(stdout,"%s",g_status_txt);
  }
  fflush(stdout);
  capa_mfree(g_status_txt);
  capa_mfree(g_question_txt);

}

/*  mode could be exam summary, show or not show percentage */
/*                quiz summary, show or not show percentage */
void
process_summary(int mode)
{
  int   outcome;
  int   i, len;
  char *c_name;
  char  c_path[512];
  
  outcome = check_exam_quiz_path();
  if( (mode == M_EXAMSUMM) && (outcome & 1) ) {  /* exam summary */
    c_name = rindex(g_exam_path,'/');
    c_name++;
    i = strlen(c_name);
    len = strlen(g_exam_path) - i - 1;
    for(i=0;i<len;i++) {
      c_path[i]=g_exam_path[i];
    }
    c_path[len]=0;
    print_summary(c_path,c_name,g_student_number, g_entered_pin, g_exam_set);
  }
  if( (mode == M_QUIZSUMM) && (outcome & 2) ) {  /* quiz summary */
    c_name = rindex(g_quiz_path,'/');
    c_name++;
    i = strlen(c_name);
    len = strlen(g_quiz_path) - i - 1;
    for(i=0;i<len;i++) {
      c_path[i]=g_quiz_path[i];
    }
    c_path[len]=0;
    print_summary(c_path,c_name,g_student_number, g_entered_pin, g_quiz_set);
  }
  
}

/* ---------------- JAVA TScore.class page print out ----------------- */
void                                     /* RETURNS: (nothing)          */
print_termscore_page(class_dir,class,student_number,pin,set,out)
char *class_dir;char *class;char *student_number;int pin;int set; /* student login set */
FILE *out;
{                                       /* LOCAL VARIABLES:            */
  int      set_idx,                     /*    Set counter              */
           i,                           /*    Question counter         */
           set_score,                   /*    Score on a set           */
           term_score=0,                /*    Total points received    */
           term_valid=0;                /*    Total points possible    */
  T_entry  entry;                       /*    Database entry for a set */
  char     buf[MAX_BUFFER_SIZE]; /* Output line buffer  */
  T_header header;                      /*    Problem set header       */
  int      question_cnt,valid_wgt,configResult;
  char     class_fullpath[ONE_K],*serverName;
  int      hw_c, hw_r, qz_c, qz_r, fs, homework_count, quiz_count;
  float    hw_w, qz_w, ex_w, fe_w, pc_w;
  int      idx, entry_count, tmp_len;
  float    *S, *F;
  int      *X; /* array controlling whether to extrapolate scores */
  char     *capa_server;
  int      max_set[4], width=600,height=750; /* width and height of applet*/
  char     **c_path_pp;

  /*Unused Vars  
    char     buf2[MAX_BUFFER_SIZE]; 
    char *qz_p, *ex_p, *epc_p; 
    int      ex_c, epc_c, result;
    int  rate, status_line_length=DEFAULT_STATUS_LINE_LENGTH,row;
  */

  serverName=getenv("SERVER_NAME");
  if (!serverName) {
    fprintf(out,"Enviroment variable SERVER_NAME not set.\n");
    fprintf(out,"Unable to complete actions.\n");
    return;
  }

  sprintf(class_fullpath,"%s/%s",class_dir,class);
  chdir(class_fullpath);
  
  /*
     change the working director to the major homework directory and begin to
     read off the remaining path informations from this capa.config file 
     homework_path   = 
     quiz_path   = 
     exam_path   = 
     correction_path = 
     homework_weight   = 0.3
     quiz_weight       = 0.7
     exam_weight       = 0.3
     final_weight      = 0.35
     correction_weight = 0.3
     final_exam_set_number = 4
     homework_count    = 12
     quiz_count        = 24
     
  */
  
  configResult=read_capa_config("capa_server",buf);
  if (configResult != 0 && configResult != -1 ) {
    tmp_len = strlen(buf) + 1;
    capa_server =   (char *)capa_malloc( tmp_len, sizeof(char));
    sprintf(capa_server,"%s",buf);
  } else { /* if capa_server is not set then we won't do anything further */
    fprintf(out,"Parameter: capa_server in capa.config file are not properly set.\n");
    return ;
  }
  if( get_termscore_params(&hw_w,&qz_w,&ex_w,&fe_w,&pc_w,&homework_count,&quiz_count,&fs) == -1 ) {
    fprintf(out,"Parameters in capa.config file are not properly set.\n");
    fprintf(out," such as homework_weight, quiz_weight, exam_weight, final_weight, correction_weight.\n");
    
    return;
  }

  get_tscore_width_height(&width,&height);

  c_path_pp = (char **)capa_malloc( 4, sizeof(char *));
  tmp_len = strlen(class_fullpath) + 1;
  c_path_pp[0] = (char *)capa_malloc(tmp_len,sizeof(char));
  sprintf(c_path_pp[0],"%s",class_fullpath); /* c_path_pp[0] should always be there */
  
  entry_count = fs*2 + 1;
  S = (float *)capa_malloc( ((fs+1)*2), sizeof(float));
  F = (float *)capa_malloc( ((fs+1)*2), sizeof(float));
  X =   (int *)capa_malloc( ((fs+1)*2), sizeof(int));
  
  max_set[0] = set;  /* the login set number */
  hw_c = max_set[0];
  hw_r = homework_count - set;
  
  
  configResult=read_capa_config("quiz_path",buf);
  if (configResult != 0 && configResult != -1 ) {
    tmp_len = strlen(buf)+1;
    c_path_pp[1] = (char *)capa_malloc(tmp_len,sizeof(char));
    sprintf(c_path_pp[1],"%s",buf);
    max_set[1] = check_class_get_maxset(c_path_pp[1]);
    if( max_set[1] <= 0 ) {
      /* should we continue ? */
      max_set[1] = 1;
      X[1] = 1;
    }
    qz_c = max_set[1];
    qz_r = quiz_count - max_set[1];
  } else { /* if quiz_path is not in capa.config, then we will skip quizz */
    qz_c = 0;
    qz_r = 0;
  }
  
  
  configResult=read_capa_config("exam_path",buf);
  if (configResult != 0 && configResult != -1 ) {
    tmp_len = strlen(buf)+1;
    c_path_pp[2] = (char *)capa_malloc( (tmp_len),sizeof(char));
    sprintf(c_path_pp[2],"%s",buf);
    max_set[2] = check_class_get_maxset(c_path_pp[2]);
    printf("<!-- for %s max_set %d -->\n",c_path_pp[2],max_set[2]);
    if( max_set[2] <= 0 ) {
	/* no sets */
	max_set[2] = 0;
    }
    /* start extrapolation with sets that don't yet exist */
    for(idx=2+(max_set[2]*2);idx <= (fs*2); idx++) {
        X[idx] = 1;
    }
  } else { /* if exam_path is not in capa.config, then skip exams */
    fs = 0;
  }
  configResult=read_capa_config("correction_path",buf);
  if (configResult != 0 && configResult != -1 ) {
    tmp_len = strlen(buf)+1;
    c_path_pp[3] = (char *)capa_malloc(tmp_len,sizeof(char));
    sprintf(c_path_pp[3],"%s",buf);
    max_set[3] = check_class_get_maxset(c_path_pp[3]);
    if( max_set[3] <= 0 ) {
      /* should we continue ? */
      max_set[3] = 0;
      
    }
  } else { /* if correction_path is not in capa.config, then skip corrections */
    pc_w = 0.0;
  }
  
  
  
  for( idx = 0; idx < 4; idx++) {
     if( c_path_pp[idx] != NULL ) {
       chdir(c_path_pp[idx]);
       term_score=0;
       term_valid=0;
       for (set_idx=1; set_idx<=max_set[idx]; set_idx++) {
          if (capa_get_header(&header,set_idx))  return;
          capa_get_entry(&entry,student_number,set_idx);
          sscanf(header.num_questions,"%d", &(question_cnt) );
          valid_wgt = 0; set_score = 0;
          header.weight[question_cnt] = '\0';
          header.partial_credit[question_cnt] = '\0';
          for (i=0; i<question_cnt; i++) {
            valid_wgt +=  (header.weight[i] - '0');
            if((entry.answers[i]=='Y') || (entry.answers[i]=='y'))  
	       set_score += (header.weight[i]-'0');
            if((entry.answers[i]=='E') || (entry.answers[i]=='e'))  
	       valid_wgt -= (header.weight[i] - '0');
            if((entry.answers[i]>='0') && (entry.answers[i]<='9'))  
	       set_score += (entry.answers[i] - '0');
          }
          term_valid += valid_wgt;
          term_score += set_score;
          capa_mfree(header.weight);
          capa_mfree(header.partial_credit);
	  printf("<!-- %s %d %d -->\n",c_path_pp[idx],set_score,valid_wgt);
          if(idx==2) { /* exam sets */
            S[set_idx*2] = (float)set_score;
            F[set_idx*2] = (float)valid_wgt;
            if (valid_wgt == 0) {
		X[set_idx*2] = 1;
	    } else {
		X[set_idx*2] = 0;
	    }
          }
          if(idx==3) { /* correction sets */
            S[set_idx*2+1] = (float)set_score;
            F[set_idx*2+1] = (float)valid_wgt;
	    if (valid_wgt == 0 ) {
		X[set_idx*2+1] = 1;
	    } else {
		X[set_idx*2+1] = 0;
	    }
          }
       }
       if( (idx == 0) || (idx==1) ) { /* homeworks and quizzes */
         S[idx] = (float)term_score;
         F[idx] = (float)term_valid;
         X[idx] = 1;
       }
     }
  }
  
  

  
  fprintf(out,"<CENTER>\n");
  fprintf(out,"<APPLET CODE=TScore.class CODEBASE=\"http://%s/CAPA/\" width=%d height=%d>\n",capa_server,width,height);
  fprintf(out,"<PARAM NAME=\"HW_W\"  VALUE=\"%f\">\n", hw_w);
  fprintf(out,"<PARAM NAME=\"QZ_W\"  VALUE=\"%f\">\n", qz_w);
  fprintf(out,"<PARAM NAME=\"EX_W\"  VALUE=\"%f\">\n", ex_w);
  fprintf(out,"<PARAM NAME=\"FE_W\"  VALUE=\"%f\">\n", fe_w);
  fprintf(out,"<PARAM NAME=\"PC_W\"  VALUE=\"%f\">\n", pc_w);
  fprintf(out,"<PARAM NAME=\"HW_C\"  VALUE=\"%d\">\n", hw_c);
  fprintf(out,"<PARAM NAME=\"HW_R\"  VALUE=\"%d\">\n", hw_r);
  fprintf(out,"<PARAM NAME=\"FS\"    VALUE=\"%d\">\n", fs);
  fprintf(out,"<PARAM NAME=\"QZ_C\"  VALUE=\"%d\">\n", qz_c);
  fprintf(out,"<PARAM NAME=\"QZ_R\"  VALUE=\"%d\">\n", qz_r);
  

  for(idx=0;idx<entry_count;idx++) {
    fprintf(out,"<PARAM NAME=\"S%d\"  VALUE=\"%f\">\n",idx,S[idx]);
    fprintf(out,"<PARAM NAME=\"F%d\"  VALUE=\"%f\">\n",idx,F[idx]);
    fprintf(out,"<PARAM NAME=\"X%d\"  VALUE=\"%d\">\n",idx,X[idx]);
  }
  
  fprintf(out,"</APPLET> </CENTER>\n");
  
  fprintf(out,"<TABLE cellpadding=0 cellspacing=0 border=0>\n<TR><TD>");
  fprintf(out,"<form method=\"post\" ");
  sprintf(buf,"action=\"http://%s/%s/%s/capasbin\">",serverName,g_cgibin_path,g_cowner);
  fprintf(out,"%s\n", buf);
  fprintf(out,"<input type=\"hidden\" name=\"CLASS\" value=\"%s\">\n",g_class_name);
  fprintf(out,"<input type=\"hidden\" name=\"SNUM\" value=\"%s\">\n",g_student_number);
  fprintf(out,"<input type=\"hidden\" name=\"CAPAID\" value=\"%d\">\n",g_entered_pin);
  fprintf(out,"<input type=\"hidden\" name=\"M\" value=\"%d\">\n",M_CHECKIN);
  fprintf(out,"<input type=\"submit\" value=\"Main menu\" ></form></TD>\n");
  fprintf(out,"<TD><form method=\"get\" action=\"http://%s/CAPA/class.html\">",serverName); 
  fprintf(out,"<input type=\"button\" value=\"Exit\" onclick=\"window.close()\"></form></TD>");
  fprintf(out,"\n</TABLE>\n");
  
  capa_mfree((char *)S);
  capa_mfree((char *)F);
  capa_mfree((char *)X);
  for(idx=0;idx<4;idx++) {
    if( c_path_pp[idx] != NULL )  capa_mfree((char *)c_path_pp[idx]);
  }
  capa_mfree((char *)c_path_pp);
  capa_mfree((char *)capa_server);
}

void
get_tscore_width_height(width,height) 
int *width;int *height;
{
  char     buf[MAX_BUFFER_SIZE]; 
  int      configResult;
  
  configResult=read_capa_config("tscore_width",buf);
  if (configResult != 0 && configResult != -1 ) {
    sscanf(buf,"%d", width);
    if (*width <= 0 )  { *width = DEFAULT_WIDTH; }
  } else {
      printf("<!-- tscore_width not found. %d-->\n",configResult);
  }
  configResult=read_capa_config("tscore_height",buf);
  if (configResult != 0 && configResult != -1 ) {
    sscanf(buf,"%d", height);
    if (*height <= 0 )  { *height = DEFAULT_HEIGHT; }
  } else {
      printf("<!-- tscore_height not found. %d-->\n",configResult);
  }
}

int
get_termscore_params(hw,qw,ew,fw,pw,hc,qc,fs) 
float *hw;float *qw;float *ew;float *fw;float *pw;
int   *hc;int   *qc;int   *fs;
{
  char     buf[MAX_BUFFER_SIZE]; /* Output line buffer  */
  int      hw_c, qz_c, fe_s;
  float    hw_w, qz_w, ex_w, fe_w, pc_w;
  int      configResult;
  
  configResult=read_capa_config("homework_weight",buf);
  if (configResult != 0 && configResult != -1 ) {
    sscanf(buf,"%f", &hw_w);
    if(hw_w < 0.0 )  {
      hw_w = DEFAULT_HW_W;
    }
  } else {
    return (-1);
  }
  configResult=read_capa_config("quiz_weight",buf);
  if (configResult != 0 && configResult != -1 ) {
    sscanf(buf,"%f", &qz_w);
    if(qz_w < 0.0 )  {
      qz_w = DEFAULT_QZ_W;
    }
  } else {
    return (-1);
  }
  configResult=read_capa_config("exam_weight",buf);
  if (configResult != 0 && configResult != -1 ) {
    sscanf(buf,"%f", &ex_w);
    if(ex_w < 0.0 )  {
      ex_w = DEFAULT_EX_W;
    }
  } else {
    return (-1);
  }
  configResult=read_capa_config("final_weight",buf);
  if (configResult != 0 && configResult != -1 ) {
    sscanf(buf,"%f", &fe_w);
    if(fe_w < 0.0 )  {
      fe_w = DEFAULT_FE_W;
    }
  } else {
    return (-1);
  }
  configResult=read_capa_config("correction_weight",buf);
  if (configResult != 0 && configResult != -1 ) {
    sscanf(buf,"%f", &pc_w);
    if(pc_w < 0.0 )  {
      pc_w = DEFAULT_PC_W;
    }
  } else {
    return (-1);
  }
  configResult=read_capa_config("final_exam_set_number",buf);
  if (configResult != 0 && configResult != -1 ) {
    sscanf(buf,"%d", &fe_s);
    if(fe_s <= 0 )  {
      fe_s = DEFAULT_FE_NUMBER;
    }
  } else {
    return (-1);
  }
  configResult=read_capa_config("homework_count",buf);
  if (configResult != 0 && configResult != -1 ) {
    sscanf(buf,"%d", &hw_c);
    if(hw_c <= 0 )  {
      hw_c = DEFAULT_HW_COUNT;
    }
  } else {
    return (-1);
  }
  configResult=read_capa_config("quiz_count",buf);
  if (configResult != 0 && configResult != -1 ) {
    sscanf(buf,"%d", &qz_c);
    if(qz_c <= 0 )  {
      qz_c = DEFAULT_QZ_COUNT;
    }
  } else {
    return (-1);
  }
  *hw = hw_w; *qw = qz_w; *ew = ex_w; *fw = fe_w; *pw = pc_w;
  *hc = hw_c; *qc = qz_c; *fs = fe_s;
  return (0);

}

/* =================================================================================================== */

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