--- capa/capa51/pProj/capaCommon.c 1999/10/13 18:45:28 1.2 +++ capa/capa51/pProj/capaCommon.c 2002/09/20 21:27:49 1.22 @@ -1,6 +1,29 @@ +/* Library of useful functions + Copyright (C) 1992-2000 Michigan State University + + The CAPA system is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The CAPA system is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public + License along with the CAPA system; see the file COPYING. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + As a special exception, you have permission to link this program + with the TtH/TtM library and distribute executables, as long as you + follow the requirements of the GNU GPL in regard to all of the + software in the executable aside from TtH/TtM. +*/ + /* =||>|===================== capaCommon.c =====================|<||= */ /* created 1994 by Isaac Tsai */ -/* 1994, 1995, 1996, 1997, 1998, 1999 copyrighted by Isaac Tsai */ /* TODO: restructure capa_check_ans*() calls into one */ /* =||>|===================== capaCommon.c =====================|<||= */ #include @@ -25,6 +48,8 @@ int yyparse(); extern FILE *yyin; extern void yyrestart(); +extern FILE *dfp; + /*----------------------------------------------------------*/ /* RETURN: -1 file error */ /* 0 success */ @@ -569,8 +594,8 @@ int set; } funlockstream(fp); fclose(fp); if(!found) { - ans_p = capa_malloc(nq+1,1); - tries_p = capa_malloc(3*nq+2,1); /* space and \0 */ + ans_p = capa_malloc(nq+2,1); + tries_p = capa_malloc(3*nq+3,1); /* space and \0 */ for(ii=0;iianswers = ans_p; entry->tries = tries_p; entry->e_probs = nq; @@ -1357,12 +1384,12 @@ int capa_check_option(int option,int set } /*----------------------------------------------------------*/ -/* INPUT: set the X in logX.db */ +/* INPUT: time the current time */ +/* datetime the datetime string to compare */ /* OUTPUT: none */ /* */ -/* RETURN: -1 file error */ -/* 0 no login */ -/* >0 number of logins in that logX.db file */ +/* RETURN: -1 time is earlier then datetime */ +/* 1 time is later than datetime */ /*----------------------------------------------------------*/ int compare_datetime(time,datetime) @@ -1391,7 +1418,8 @@ char *datetime; /* What if: [7,3] date_info */ /* [3,7] date_info */ /* 4 date_info */ - +/* RETURN: -1 if not pass time */ +/* 1 if pass time (or no time available */ int capa_check_date(int which,char *student_number, int section,int set) { int result; @@ -1421,10 +1449,14 @@ void capa_get_due_date(char *date_str,T_ if ((duration > 0) && (student_number!=NULL)) { if (capa_get_login_time(student_number,set,&logintime)==1) { duetime=logintime+duration; - due_time_tm=localtime(&duetime); - sprintf(date_str,"%04d/%02d/%02d %02d:%02d",((due_time_tm->tm_year)+1900), - due_time_tm->tm_mon+1,due_time_tm->tm_mday,due_time_tm->tm_hour, - due_time_tm->tm_min); + if (compare_datetime(duetime,current->due_date)==-1) { + due_time_tm=localtime(&duetime); + sprintf(date_str,"%04d/%02d/%02d %02d:%02d",((due_time_tm->tm_year)+1900), + due_time_tm->tm_mon+1,due_time_tm->tm_mday,due_time_tm->tm_hour, + due_time_tm->tm_min); + } else { + strncpy(date_str,current->due_date,DATE_BUFFER); + } return; } } @@ -2121,6 +2153,10 @@ extern int Symb_count; extern int first_run; extern int Stop_Parser; extern void (*Status_Func)(); +#ifdef TTH +extern void tth_restart(); +extern char* tth_err; +#endif /*TTH*/ long seed1, seed2; T_student a_student; char *class, *classname, warn_msg[WARN_MSG_LENGTH]; @@ -2149,6 +2185,10 @@ extern void (*Status_Func)(); } u_getunit(fp); fclose(fp); +#ifdef TTH + if(tth_err) { free(tth_err); tth_err=NULL; } + tth_restart(); +#endif /*TTH*/ strncpy(Parse_name,a_student.s_nm,MAX_NAME_CHAR+1); strncpy(Parse_student_number,student_number,MAX_STUDENT_NUMBER+1); Parse_section = a_student.s_sec; @@ -2459,7 +2499,7 @@ char *buf;double *num; char *num_p; char while( isspace(buf[idx]) ) { idx++; } sscanf(num_str, "%lg", &result); /* put the numerical value into a double variable */ errcode = errcode | 1; - } else if( buf[idx] == 'x' || buf[idx] == 'X') { /* optional x or X part */ + } else if( buf[idx] == 'x' || buf[idx] == 'X' || buf[idx] == '*') { /* optional x or X part */ idx++; /* skip x or X */ while( isspace(buf[idx]) ) { idx++; } @@ -2609,7 +2649,7 @@ answers_string(mode, p)int mode;Problem_ str_bb = format_toTeX(lower); } else { /* answer could be string, choice */ str_bb = (char *)capa_malloc(strlen(lower)+MAX_BUFFER_SIZE,1); - if (p->ans_type == ANSWER_IS_FORMULA || 1) + if (p->verbatim == DO_VERBATIM) sprintf(str_bb,"\\begin{verbatim}%s\\end{verbatim}",lower); else strcpy(str_bb,lower); @@ -2864,7 +2904,7 @@ answers_string(mode, p)int mode;Problem_ /* <== This routine checks user input string *ans against correct answer *s ==> */ int -capa_check_ans(ai,ans) AnswerInfo_t *ai; char *ans; +capa_check_ans(ai,ans, error) AnswerInfo_t *ai; char *ans; char **error; { int t; /* ans_type */ char *s; /* ans_str */ @@ -2881,7 +2921,7 @@ capa_check_ans(ai,ans) AnswerInfo_t *ai int sig, corr_len; int choice[ANSWER_STRING_LENG]; char num_str[ANSWER_STRING_LENG], unit_str[ANSWER_STRING_LENG]; - char fmted[ANSWER_STRING_LENG], correct[ANSWER_STRING_LENG], answer[ANSWER_STRING_LENG]; + char fmted[ANSWER_STRING_LENG], correctans[ANSWER_STRING_LENG], answer[ANSWER_STRING_LENG]; double n_part; double given, target, ratio, fmted_target, target_u, target_l, scale=1.0; double delta; @@ -2907,9 +2947,12 @@ capa_check_ans(ai,ans) AnswerInfo_t *ai outcome = split_num_unit(ans,&n_part,num_str,unit_str); if( outcome > 1 ) { /* with both num and unit parts or only unit part */ if( u_p != NULL ) { - result = check_correct_unit(unit_str,u_p,&scale); + if (UNIT_FAIL == (result = check_correct_unit(unit_str,u_p,&scale))) { + *error=strsave(unit_str); + } } else { /* what to do when no unit is specified but student entered a unit? */ result = UNIT_NOTNEEDED; + *error=strsave(unit_str); } } else { if( u_p != NULL ) { @@ -2924,8 +2967,10 @@ capa_check_ans(ai,ans) AnswerInfo_t *ai } given = n_part * scale; /* convert the given answer into proper scale for units */ sig = calc_sig( num_str ); - if( (sig < sl) || (sig > su) ) { - result = SIG_FAIL; + if( ((sig < sl ) || (sig > su )) && (sig!=0)) { + result = SIG_FAIL; + *error=capa_malloc(1,ANSWER_STRING_LENG); + sprintf(*error,"%d",sig); } else { switch( tt ) { /* tolerence type */ case TOL_ABSOLUTE: @@ -2967,7 +3012,7 @@ capa_check_ans(ai,ans) AnswerInfo_t *ai } /* end sig check */ } /* end if unit check */ } else { /* user entered alphabets, but no number */ - result = INCORRECT; + result = WANTED_NUMERIC; } } break; @@ -2975,14 +3020,18 @@ capa_check_ans(ai,ans) AnswerInfo_t *ai { corr_len = strlen(s); input_len = strlen(ans); if( corr_len == input_len ) { - for(idx=0;idxans_id_list,ai->ans_pts_list,tt,to); break; case ANSWER_IS_EXTERNAL: /* Not yet implemented */ + + + break; } return (result); @@ -3019,7 +3071,7 @@ capa_check_ans(ai,ans) AnswerInfo_t *ai int -capa_check_answer(p, answer) Problem_t *p; char *answer; +capa_check_answer(p, answer, error) Problem_t *p; char *answer; char **error; { int type; char *correct; @@ -3029,7 +3081,8 @@ capa_check_answer(p, answer) Problem_t * int sig_l; int sig_u; char *fmt; - int choice[ANSWER_STRING_LENG], ii, idx, corr_len, input_len; + int choice[ANSWER_STRING_LENG], correctans[ANSWER_STRING_LENG]; + int ii, idx, corr_len, input_len; int result = INCORRECT, sig, outcome, all_alphabet; char fmted[FORMAT_STRING_LENG]; double given, target, ratio, fmted_target, target_u, target_l, scale=1.0; @@ -3060,10 +3113,12 @@ capa_check_answer(p, answer) Problem_t * outcome = split_num_unit(answer,&n_part,input,unit_str); if( outcome > 1 ) { /* with both num and unit parts or only unit part */ if( p->ans_unit != NULL ) { - result = check_correct_unit(unit_str,p->ans_unit,&scale); - + if ( UNIT_FAIL == ( result = check_correct_unit(unit_str,p->ans_unit,&scale) ) ) { + *error=strsave(unit_str); + } } else { /* what to do when no unit is specified but student entered a unit? */ result = UNIT_NOTNEEDED; + *error=strsave(unit_str); } } else { if( p->ans_unit != NULL ) { @@ -3078,8 +3133,10 @@ capa_check_answer(p, answer) Problem_t * } given = n_part * scale; /* convert the given answer into proper scale for units */ sig = calc_sig( input ); - if( (sig < sig_l) || (sig > sig_u) ) { + if( ((sig < sig_l) || (sig > sig_u)) && (sig!=0)) { result = SIG_FAIL; + *error=capa_malloc(1,ANSWER_STRING_LENG); + sprintf(*error,"%d",sig); } else { switch( tol_type ) { case TOL_ABSOLUTE: @@ -3121,7 +3178,7 @@ capa_check_answer(p, answer) Problem_t * } /* end sig check */ } /* end if unit check */ } else { /* user entered alphabet, but no number */ - result = INCORRECT; + result = WANTED_NUMERIC; } } break; @@ -3129,13 +3186,18 @@ capa_check_answer(p, answer) Problem_t * { corr_len = strlen(correct); input_len = strlen(answer); if( corr_len == input_len ) { - for(ii=0;iiid_list,p->pts_list,tol_type,tol); break; case ANSWER_IS_EXTERNAL: /* not yet implemented */ + /* we assume the external program is called through popen() */ + /* and the result will be given back as 0 or 1 to indicate the */ + /* given answer is correct or not */ + /* arguments are given to the program as */ + /* before running the program, check its existance first */ + /* should we specify a time out period in capa.config file? */ + /* set up a timer for this purpose */ + /* FILE *popen (const char *command,const char *type ); */ + break; } return (result); } -/* ----------------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ /* assumming the formula is *fml_str and the student input is *input_str */ /* according to the type of tolerance, we form the final formula as */ /* absolute tolerance: (*fml_str) - (*input_str) */ @@ -3191,7 +3262,7 @@ char *fml_str;char *input_str;char *var_ } } else { diff = abs(1.0 - formula_val) * 100.0 ; - if( diff < tol ) { + if( diff > tol ) { outcome = INCORRECT; } } @@ -3301,20 +3372,30 @@ int tol_type;double tol;char *lower;char /* New check answers routine checks the /AND and /OR group of answers */ /* use array of char pointers char **a */ int -capa_check_answers(p,answers,cnt) Problem_t *p; char **answers; int cnt; +capa_check_answers(p,answers,cnt,error) +Problem_t *p; char **answers; int cnt; char **error; { AnswerInfo_t *ai; int ii, done, result; int *outcomes; + char **errormsg; + errormsg=(char**)capa_malloc(cnt,sizeof(char*)); if(p->ans_op == ANS_AND) { /* ans /and ans */ if( (cnt != p->ans_cnt) ) { return (ANS_CNT_NOT_MATCH); } - if( cnt == 1 ) { return (capa_check_answer(p, answers[0])); } /* there is only one answer */ + if( cnt == 1 ) { return (capa_check_answer(p, answers[0], error)); } /* there is only one answer */ outcomes = (int *)capa_malloc(sizeof(int),cnt); for(ii=0;iianswer,answers[0],outcomes[0]); +#endif for(ii=1, ai = p->ans_list; ai; ii++,ai = ai->ans_next ) { - outcomes[ii] = capa_check_ans(ai,answers[ii]); + outcomes[ii] = capa_check_ans(ai,answers[ii],&(errormsg[ii])); +#ifdef COMMON_DBUG + printf("CAPA_CHECK_ANS(%s,%s): outcome[%d]=%d\n", ai->ans_str,answers[ii],ii,outcomes[ii]); +#endif } done = ii = 0; result = 0; @@ -3323,16 +3404,31 @@ capa_check_answers(p,answers,cnt) Proble (outcomes[ii] == NO_UNIT) || (outcomes[ii] == UNIT_NOTNEEDED) ) { result = outcomes[ii]; + if (result != NO_UNIT) { *error=strsave(errormsg[ii]); } done = 1; } ii++; if(ii==cnt) done = 1; } - if( result == 0 ) { /* check if any of the outcome has failed on sig figs */ + if( result == 0 ) { + /* check if any of the outcome has failed to be a numeric + or was a malformed equation */ + done = ii = 0; + while( !done ) { + if( outcomes[ii] == WANTED_NUMERIC || outcomes[ii] == BAD_FORMULA ) { + result = outcomes[ii]; + done = 1; + } + ii++; + if(ii==cnt) done = 1; + } + } + if( result == 0 ) {/*check if any of the outcome has failed on sig figs*/ done = ii = 0; while( !done ) { if( outcomes[ii] == SIG_FAIL ) { result = outcomes[ii]; + *error = strsave(errormsg[ii]); done = 1; } ii++; @@ -3350,18 +3446,30 @@ capa_check_answers(p,answers,cnt) Proble if(ii==cnt) done = 1; } } + for (ii=0;iians_cnt == 1 ) { return (capa_check_answer(p, answers[0])); } - result = capa_check_answer(p, answers[0]); + if( p->ans_cnt == 1 ) { return (capa_check_answer(p, answers[0], error)); } + result = capa_check_answer(p, answers[0], error); ii = 1; ai = p->ans_list; while( (iians_cnt) && ( (result != EXACT_ANS) && (result != APPROX_ANS) ) ) { - result = capa_check_ans(ai,answers[0]); - ai = ai->ans_next; ii++; + if((ii!=1)&&((result==UNIT_FAIL)||(result==SIG_FAIL)||(result==UNIT_NOTNEEDED))) { + capa_mfree((char*)error); + } + result = capa_check_ans(ai,answers[0],error); + ai = ai->ans_next; ii++; } } return (result); @@ -3458,7 +3566,7 @@ char *key_word;char *value; } } if (c=='\n') found=1; - if (((char)c)==((char)EOF)) break; + if (((char)c)==((char)EOF)) {failed=1;break;} } } } while (!done && !failed); @@ -3466,6 +3574,7 @@ char *key_word;char *value; fclose(fp); if (done) { + trim_response_ws(right); /*get rid of leading and trailing spaces*/ for(i=0,j=0;i<(strlen(right)+1);i++) { value[j]='\0'; if (right[i] == '\\' && (i < (strlen(right))) ) {