Subversion Repositories wimsdev

Rev

Rev 8338 | Rev 10303 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

  1. /*    Copyright (C) 1998-2003 XIAO, Gang of Universite de Nice - Sophia Antipolis
  2.  *
  3.  *  This program is free software; you can redistribute it and/or modify
  4.  *  it under the terms of the GNU General Public License as published by
  5.  *  the Free Software Foundation; either version 2 of the License, or
  6.  *  (at your option) any later version.
  7.  *
  8.  *  This program is distributed in the hope that it will be useful,
  9.  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  10.  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  11.  *  GNU General Public License for more details.
  12.  *
  13.  *  You should have received a copy of the GNU General Public License
  14.  *  along with this program; if not, write to the Free Software
  15.  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  16.  */
  17.  
  18. /* Routines treating user scores. */
  19. #include "wims.h"
  20.  
  21. double oldfactor=0.85;     /* quality factor, should remain stable. */
  22.  
  23. enum {sr_require, sr_weight, sr_score, sr_mean, sr_remain, sr_last, sr_try, sr_best, sr_level};
  24. char scorebuf[MAX_CLASSEXOS*sizeof(scoreresult)+32];
  25. struct scoreresult *rscore;
  26. int scorecnt;
  27. double scoresum[MAX_SHEETS];
  28. int sheetstart[MAX_SHEETS], shexocnt[MAX_SHEETS];
  29. int examstart, examcnt;
  30. char rscore_class[MAX_CLASSLEN+1];
  31. char rscore_user[MAX_NAMELEN+1];
  32.  
  33. int totsheets=0;
  34. int score_ispublic=0;
  35. int score_status=-1;          /* save of score status */
  36. int score_statussheet=-1;
  37. int score_statusisexam=-1;
  38. double scorerecfactor=0.9;
  39.  
  40. /* gather user score, core routine. */
  41. int getscoreuser(char *classe, char *user)
  42. {
  43.   int i, j, osh, sh;
  44.   double s;
  45.   char *nowuser, *nowsheet, *nowexo, *nowscore;
  46.   char *pp;
  47.   if(*user==0) {
  48.     user=getvar("wims_user");
  49.     if(user==NULL || *user==0) return -1;
  50.   }
  51.   if(strcmp(classe,rscore_class)==0 && strcmp(user,rscore_user)==0) return 0;
  52.   nowsheet=nowexo=nowscore="";
  53.   nowuser=getvar("wims_user"); if(nowuser!=NULL && strcmp(user,nowuser)==0) {
  54.     nowscore=getvar("module_score"); if(nowscore!=NULL && *nowscore!=0) {
  55.       nowsheet=getvar("wims_sheet"); if(nowsheet==NULL) nowsheet="";
  56.       nowexo=getvar("wims_exo"); if(nowexo==NULL) nowexo="";
  57.     }
  58.     else nowscore="";
  59.   }
  60.   snprintf(scorebuf+sizeof(int),sizeof(scorebuf)-sizeof(int),
  61.        "-c%s -u%s getscore %s %s %s",
  62.        classe,user,nowsheet,nowexo,nowscore);
  63.   i=kerneld(scorebuf,sizeof(scorebuf));
  64.   if(i<0)  internal_error("getscoreuser(): daemon failure.");
  65.   if(memcmp(scorebuf+sizeof(int),"OK",2)!=0) {
  66.     if(memcmp(scorebuf+sizeof(int),"ERROR",5)==0) {
  67.       module_error(find_word_start(scorebuf+sizeof(int)+8));
  68.     }
  69.     else internal_error("getscoreuser(): communication error with wimslogd.");
  70.   }
  71. /* 3 is here the length of "OK " */
  72.   pp=scorebuf+sizeof(int)+3; rscore=(struct scoreresult *) pp;
  73.   scorecnt=(i-sizeof(int)-3)/sizeof(scoreresult);
  74.   if(scorecnt>MAX_CLASSEXOS) module_error("too_many_exercises");
  75.   s=0; for(i=osh=0;i<scorecnt;i++) {
  76.     sh=(rscore[i].num>>8)+1;
  77.     if(sh<1 || sh>MAX_SHEETS) break;
  78.     if(osh>0 && osh<sh) {scoresum[osh-1]=s; s=0;}
  79.     for(;osh<sh && osh<MAX_SHEETS;osh++) sheetstart[osh]=i;
  80.     s+=rscore[i].require*rscore[i].weight;
  81.   }
  82.   if(osh>0) scoresum[osh-1]=s;
  83.   totsheets=osh;
  84.   for(j=0;j<totsheets-1;j++) shexocnt[j]=sheetstart[j+1]-sheetstart[j];
  85.   shexocnt[totsheets-1]=i-sheetstart[totsheets-1];
  86.   examstart=i; examcnt=scorecnt-examstart;
  87.   mystrncpy(rscore_class,classe,sizeof(rscore_class));
  88.   mystrncpy(rscore_user,user,sizeof(rscore_user));
  89.   return 0;
  90. }
  91. /* work : exo */
  92. char *scorepname[]={
  93.      "class","user","sheet","work","exam"
  94. };
  95. #define scorepname_no (sizeof(scorepname)/sizeof(scorepname[0]))
  96. char score_class[MAX_CLASSLEN+1];
  97. int score_sheet,score_exo,score_isexam;
  98. char score_user[256];
  99.  
  100. /* Uniformed treatment of score command parameters
  101.  * format: class=? user=? sheet=? work=?
  102.  * all are optional.
  103.  */
  104. void _scoreparm(char *p)
  105. {
  106.     int i;
  107.     char *pn, *pe, *pd, *pf;
  108.     char sav;
  109.  
  110.     score_sheet=score_exo=score_isexam=score_ispublic=0; *score_class=0;
  111.     score_user[0]=0;
  112.     for(i=0;i<scorepname_no;i++) {
  113.      pf=p;
  114.      ahead:
  115.      pn=strstr(pf,scorepname[i]); pf=pn+1;
  116.      if(pn==NULL) continue;
  117.      if(pn>p && !isspace(*(pn-1))) goto ahead;
  118.      pe=find_word_start(pn+strlen(scorepname[i]));
  119.      if(*pe!='=') goto ahead;
  120.      pd=find_word_start(pe+1);
  121.      pf=find_word_end(pd);
  122.      if(pf<=pd) continue;
  123.      sav=*pf; *pf=0;
  124.      switch(i) {
  125.          case 0: /* class */
  126.            mystrncpy(score_class,pd,sizeof(score_class)); break;
  127.          case 1: /* user */
  128.            mystrncpy(score_user,pd,sizeof(score_user)); break;
  129.          case 2: { /* sheet */
  130.           if(*pd=='P') {pd++; score_ispublic=1;}
  131.           score_sheet=atoi(pd);
  132.           break;
  133.          }
  134.          case 3: /* work=exo */
  135.            score_exo=atoi(pd); break;
  136.          case 4: /* exam */
  137.            score_isexam=1; break;
  138.      }
  139.      *pf=sav; ovlstrcpy(pn, pf);
  140.     }
  141.     *p=0;
  142.     if((*score_class!=0 || score_user[0]!=0) && !trusted_module()) {
  143.      module_error("not_trusted"); return;
  144.     }
  145.     if(*score_class==0) {
  146.      char *classe;
  147.      classe=getvar("wims_class");
  148.      if(classe==NULL || *classe==0) return;
  149.      else mystrncpy(score_class,classe,sizeof(score_class));
  150.     }
  151.     if(score_user[0]==0) {
  152.      char *user;
  153.      user=getvar("wims_user");
  154.      if(user!=NULL) mystrncpy(score_user,user,sizeof(score_user));
  155.     }
  156. }
  157.  
  158. /* gather score: relatif to some of the
  159.  *  user class work sheet exam
  160.  *  dtype can be require weight score mean remain best last level try
  161.  */
  162.  
  163. void _getscore(char *p,int dtype)
  164. {
  165.     int i,sh,ex,osh;
  166.     float d;
  167.     char *p1;
  168.  
  169.     _scoreparm(p);
  170.     if(*score_class==0 || *score_user==0) return;
  171.     if(getscoreuser(score_class,score_user)<0) return;
  172. /*  2^8 no_sheet + no_exo_in_the_sheet so limit no_exo to 2^8 */
  173.     for(i=osh=0,p1=p;i<scorecnt && p1-p<MAX_LINELEN-32;i++) {
  174.      sh=(rscore[i].num>>8)+1; if(sh<1 || sh>MAX_SHEETS) break;
  175.      if(score_sheet!=0) {
  176.          if(sh<score_sheet) continue;
  177.          if(sh>score_sheet || sh>MAX_SHEETS) break;
  178.      }
  179.      ex=((rscore[i].num)&255)+1;
  180.      if(score_exo!=0 && ex!=score_exo) continue;
  181.      if(osh!=0 && sh!=osh) *p1++='\n';
  182.      switch(dtype) {
  183.          case sr_require: {d=rscore[i].require; break;}
  184.          case sr_weight: {d=rscore[i].weight; break;}
  185.          case sr_score: {d=rscore[i].score; break;}
  186.          case sr_mean: {d=rscore[i].mean; break;}
  187.          case sr_remain: {d=rscore[i].require-rscore[i].score; break;}
  188.          case sr_last: {d=rscore[i].last; break;}
  189.          case sr_try: {d=rscore[i].try; break;}
  190.          case sr_best: {d=rscore[i].best; break;}
  191.          case sr_level: {d=rscore[i].level ;break;}
  192.          default: {d=0; break;}
  193.      }
  194.      p1=moneyprint(p1,d); *p1++=' ';
  195.      osh=sh;
  196.     }
  197.     *p1++='\n'; *p1=0;
  198. }
  199.  
  200. /* gather user score. */
  201. void calc_getscore(char *p)
  202. {
  203.   _getscore(p,sr_score);
  204. }
  205.  
  206. /* gather user score average. */
  207. void calc_getscoremean(char *p)
  208. {
  209.   _getscore(p,sr_mean);
  210. }
  211.  
  212. /* gather remaining of score to get for user. */
  213. void calc_getscoreremain(char *p)
  214. {
  215.   _getscore(p,sr_remain);
  216. }
  217.  
  218. /* Require score table. */
  219. void calc_getscorerequire(char *p)
  220. {
  221.   _getscore(p,sr_require);
  222. }
  223.  
  224. /* Score weight table. */
  225. void calc_getscoreweight(char *p)
  226. {
  227.   _getscore(p,sr_weight);
  228. }
  229. /* user last score*/
  230. void calc_getscorelast(char *p)
  231. {
  232.   _getscore(p,sr_last);
  233. }
  234.  
  235. /* gather user score try numbers. */
  236. void calc_getscoretry(char *p)
  237. {
  238.   _getscore(p,sr_try);
  239. }
  240.  
  241. /* if the required points are 10* N, gather N user best scores */
  242. void calc_getscorebest(char *p)
  243. {
  244.   _getscore(p,sr_best);
  245. }
  246.  
  247. /* gather user score average. */
  248. void calc_getscorelevel(char *p)
  249. {
  250.   _getscore(p,sr_level);
  251. }
  252.  
  253. /* percentage of work done for each sheet:
  254.  * score mean best level
  255.  * as follows
  256.  * score=100*cumulative_points/required_points) (< 100)
  257.  * mean=quality (<10)
  258.  * best=10*(required/10 best_scores)/required_points (< 100)
  259.  * level=minimum of the required/10 best_scores (< 10)
  260.  */
  261.  
  262.  void calc_getscorepercent(char *p)
  263. {
  264.   int i,j,jend;
  265.   double tot, mean, totb, totl, totw, d;
  266.   char *p1;
  267.  
  268.   _scoreparm(p);
  269.   if(*score_class==0 || *score_user==0) return;
  270.   if(getscoreuser(score_class,score_user)<0) return;
  271.   for(p1=p,i=0;i<totsheets && p1-p<MAX_LINELEN-32;i++) {
  272.     if(scoresum[i]==0) {
  273.       ovlstrcpy(p1,"0 0 0 0\n"); p1+=strlen(p1); continue;
  274.     }
  275.     if(score_sheet!=0 && i!=score_sheet-1) continue;
  276.     if(scoresum[i]<=0) *p1++='\n';
  277.     tot=mean=totb=totl=totw=0; jend=sheetstart[i]+shexocnt[i];
  278.     for(j=sheetstart[i];j<jend;j++) {
  279. /* if mean<1 then ignore score.
  280.  * if mean<2 then half score.
  281.  */
  282.       if(rscore[j].mean>=1) {
  283.         double dt=rscore[j].score;
  284.         float db=rscore[j].best;
  285.         float dl=rscore[j].level;
  286.         if(rscore[j].mean<2) {dt=dt/2; db=db/2; dl=dl/2;}
  287.         d=dt*rscore[j].weight;
  288. /* quality */
  289.         mean+=rscore[j].mean*d;
  290. /* cumulative score */
  291.         tot+=d;
  292. /* best */
  293.         totb+=db*rscore[j].weight;
  294. /* level */
  295.         totl+=dl*rscore[j].weight;
  296.         totw+=rscore[j].weight;
  297.       }
  298.     }
  299.     if(tot>0) {d=mean/tot;} else d=0;
  300. /* cumulative score */
  301.     p1=moneyprint(p1,rint(100*tot/scoresum[i])); *p1++=' ';
  302. /* quality */
  303.     p1=moneyprint(p1,d); *p1++=' ';
  304. /* best */
  305.     p1=moneyprint(p1,rint(100*totb/scoresum[i])); *p1++=' ';
  306. /* level */
  307.     p1=moneyprint(p1,rint(10*totl/totw));
  308.  
  309.    *p1++='\n';
  310.   }
  311.   *p1=0;
  312. }
  313.  
  314. /* Returns the status of a sheet, or -1 if error */
  315. int getsheetstatus(char *classe, int sheet)
  316. {
  317.   char *p, *st, buf[MAX_LINELEN+1], namebuf[MAX_FNAME+1];
  318.   int i;
  319.  
  320.   if(isexam || score_isexam) st="exam"; else st="sheet";
  321.   mkfname(namebuf,"%s/%s/%ss/.%ss",class_base,classe,st,st);
  322.   direct_datafile=1;datafile_fnd_record(namebuf,sheet,buf);direct_datafile=0;
  323.   p=find_word_start(buf); if(*p==0) return -1;
  324.   i=*p-'0'; if(i>5 || i<0) i=-1;
  325.   if((isexam || score_isexam) && i==0) {
  326.     p=getvar("wims_user"); if(p!=NULL && strcmp(p,"supervisor")==0) i=1;
  327.   }
  328.   return i;
  329. }
  330.  
  331. /* return 1 if a word of bf2 is a substring of host.
  332.  * Content of bf2 is destroyed.
  333.  */
  334. int _subword(char bf2[])
  335. {
  336.   char *p1, *p2;
  337.   for(p1=strchr(bf2,'\\'); p1!=NULL; p1=strchr(p1+1,'\\')) {
  338.     char buf[MAX_LINELEN+1], buf2[MAX_LINELEN+1], fbuf[MAX_FNAME+1];
  339.     char *classp, *classp2, *userp, *scp;
  340.     classp=getvar("wims_class"); userp=getvar("wims_user");
  341.     if(classp==NULL || userp==NULL || *classp==0 || *userp==0) break;
  342.     scp=getvar("wims_superclass");
  343.     if(scp!=NULL && *scp!=0) classp2=scp; else classp2=classp;
  344.     if(p1>bf2 && !isspace(*(p1-1))) continue;
  345.     if(!isalnum(*(p1+1))) continue;
  346.     p2=find_word_end(p1); if(p2>=p1+MAX_NAMELEN) continue;
  347.     memmove(buf2, p1+1, p2-p1-1); buf2[p2-p1-1]=0;
  348.     snprintf(buf,sizeof(buf),"user__%s",buf2);
  349.     if(strcmp(userp,"supervisor")==0)
  350.       mkfname(fbuf,"%s/%s/supervisor",class_base,classp);
  351.     else
  352.       mkfname(fbuf,"%s/%s/.users/%s",class_base,classp2,userp);
  353.     getdef(fbuf,buf,buf2); if(buf2[0]==0) ovlstrcpy(buf2,"none");
  354.     string_modify(bf2,p1,p2,buf2);
  355.     p1+=strlen(buf2);
  356.   }
  357.   if((isexam || score_isexam) && bf2[0]=='#') return 1;
  358.   if(wordchr(bf2,"none")!=NULL) return 0;
  359.   if(wordchr(bf2,"all")!=NULL) return 1;
  360.   p1=find_word_start(bf2); if(*p1==0) return 1;
  361.   return checkhostt(p1);
  362. }
  363.  
  364. /* Returns 1 if score registration is open, 0 otherwise. */
  365. int _getscorestatus(char *classe, int sheet)
  366. {
  367.   char nbuf[MAX_LINELEN+1], gbuf[MAX_LINELEN+1];
  368.   char *es;
  369.  
  370.   if(classe==NULL || *classe==0 || sheet<=0) return 1;
  371.   if(getsheetstatus(classe,sheet)!=1) return 0;
  372.   if(*remote_addr==0) return 0;
  373.   if(isexam || score_isexam) {     /* exam simulation */
  374.     accessfile(nbuf,"r","%s/%s/.E%d",class_base,classe,sheet);
  375.     if(nbuf[0]=='#') return 1;
  376.   }
  377. /* Global restriction data */
  378.   accessfile(nbuf,"r","%s/%s/.security",class_base,classe);
  379.   if(nbuf[0]) {
  380.     _getdef(nbuf,"allow",gbuf);
  381.     if(*find_word_start(gbuf)!=0 && _subword(gbuf)==0)
  382.       return 0;
  383.     _getdef(nbuf,"except",gbuf);
  384.     if(*find_word_start(gbuf)!=0 && _subword(gbuf)==1)
  385.       return 0;
  386.   }
  387.  
  388. /* Sheet restriction data */
  389.   if(isexam || score_isexam) es="E"; else es="";
  390.   accessfile(nbuf,"r","%s/%s/.%s%d",class_base,classe,es,sheet);
  391.   if(*find_word_start(nbuf)==0) return 1;
  392.   return _subword(nbuf);
  393. }
  394.  
  395. /* Returns 1 if score registration is open, 0 otherwise. */
  396. int getscorestatus(char *classe, int sheet)
  397. {
  398.   if(score_status<0 || score_statussheet!=sheet
  399.      || score_statusisexam!=isexam) {
  400.     score_statussheet=sheet; score_statusisexam=isexam;
  401.     score_status=_getscorestatus(classe,sheet); score_isexam=0;
  402.     if(score_status==1 && (cmd_type==cmd_new || cmd_type==cmd_renew
  403.                || isexam)) {
  404.       char *p;
  405.       p=getvar("wims_scorereg");
  406.       if(p==NULL || strcmp(p,"suspend")!=0)
  407.          setvar("wims_scoring","pending");
  408.       else setvar("wims_scoring","");
  409.     }
  410.   }
  411.   if(isexam && score_status==0) {
  412.     char *p;
  413.     p=getvar("wims_user"); if(p==NULL || strcmp(p,"supervisor")!=0)
  414.                  user_error("exam_closed");
  415.   }
  416.   return score_status;
  417. }
  418.  
  419. /* Whether score registering is open */
  420. void calc_getscorestatus(char *p)
  421. {
  422.   _scoreparm(p);
  423.   if(*score_class==0 || score_sheet==0 || *score_user==0) {
  424.     *p=0; return;
  425.   }
  426.   if(getscorestatus(score_class, score_sheet))
  427.     ovlstrcpy(p,"yes");
  428.   else ovlstrcpy(p,"no");
  429. }
  430.  
  431. double exam_scoredata[MAX_EXOS];
  432.  
  433. /* get current exam score */
  434. void exam_currscore(int esh)
  435. {
  436.   char *p, *bf, pb[MAX_FNAME+1];
  437.   char *s, *p1, *p2, *e1, *e2;
  438.   int i;
  439.  
  440.   for(i=0;i<MAX_EXOS;i++) exam_scoredata[i]=-1000;
  441. /* session_prefix is not yet defined here */
  442.   s=getvar("wims_session"); if(s==NULL || *s==0) return;
  443.   mystrncpy(pb,s,sizeof(pb));
  444.   p=strchr(pb,'_'); if(p!=NULL) *p=0;
  445.   bf=readfile(mkfname(NULL,"%s/%s/examscore.%d",session_dir,pb,esh),NULL,WORKFILE_LIMIT);
  446.   if(bf==NULL) return;
  447.   for(p1=bf;*p1;p1=p2) {
  448.     p2=strchr(p1,'\n'); if(*p2) *p2++=0;
  449.     else p2=p1+strlen(p1);
  450.     p1=find_word_start(find_word_end(find_word_start(p1)));
  451.     e1=find_word_end(p1); if(*e1) *e1++=0;
  452.     e1=find_word_start(e1); e2=find_word_start(find_word_end(e1));
  453.     *find_word_end(e1)=0;
  454.     i=atoi(p1);
  455.     if(i>=1 && i<=MAX_EXOS &&
  456.        exam_scoredata[i-1]==-1000 && strcmp(e1,"score")==0) {
  457.       *find_word_end(e2)=0;
  458.       exam_scoredata[i-1]=atof(e2);
  459.     }
  460.   }
  461.   free(bf);
  462. }
  463.  
  464. /* Gather exam score. */
  465. void calc_examscore(char *p)
  466. {
  467.   char *p1;
  468.   int i;
  469.  
  470.   _scoreparm(p); *p=0;
  471.   if(*score_class==0 || *score_user==0) return;
  472.   if(getscoreuser(score_class,score_user)<0) return;
  473.   p1=p;
  474.   for(i=0; i<examcnt && p1-p<MAX_LINELEN-32; i++) {
  475.     p1=moneyprint(p1,rscore[examstart+i].score); *p1++=' ';
  476.   }
  477.   *p1++='\n';
  478.   for(i=0; i<examcnt && p1-p<MAX_LINELEN-32; i++) {
  479.     p1=moneyprint(p1,rscore[examstart+i].require); *p1++=' ';
  480.     p1=moneyprint(p1,floor(rscore[examstart+i].mean/2)); *p1++=' ';
  481.     p1=moneyprint(p1,(int) rscore[examstart+i].mean%2); *p1++='\n';
  482.   }
  483.   *p1=0;
  484. }
  485.  
  486. /* check score dependency.
  487.  * returns 1 if requirements are met.
  488.  */
  489. int _depcheck(char *ds, struct scoreresult *rs, int ecnt)
  490. {
  491.   char *p1, *p2, *p3, *p4, *pp;
  492.   int perc, t, sum;
  493.   double tgot, ttot, tmean;
  494.  
  495.   for(p1=ds; *p1; p1=p3) {
  496.     p2=strchr(p1,':'); if(p2==NULL) break;
  497.     *p2++=0; p2=find_word_start(p2);
  498.     for(p3=p2; myisdigit(*p3); p3++);
  499.     if(p3<=p2) break;
  500.     *p3++=0; perc=atoi(p2);
  501.     if(perc<=0 || perc>100) break;
  502.     for(pp=p1; *pp; pp++) if(!myisdigit(*pp)) *pp=' ';
  503.     tgot=ttot=tmean=0; sum=0;
  504.     for(pp=find_word_start(p1); *pp; pp=find_word_start(p4)) {
  505.       p4=find_word_end(pp); if(*p4) *p4++=0;
  506.       t=atoi(pp); if(t<=0 || t>ecnt) goto lend;
  507.       t--;
  508.       ttot+=rs[t].require; tgot+=rs[t].score; tmean+=rs[t].mean;
  509.       sum++;
  510.     }
  511.     if(ttot<10) continue;
  512.     if(tgot/ttot*sqrt(tmean/(sum*10))*100<perc) {
  513.       for(pp=p1;pp<p2-1;pp++) if(!*pp) *pp=',';
  514.       *pp=0; setvar("dep_list",p1);
  515.       return 0;
  516.     }
  517.   lend: ;
  518.   }
  519.   return 1;
  520. }
  521.  
  522. int depcheck(char *sh, int exo, char *deps)
  523. {
  524.   char buf[MAX_LINELEN+1];
  525.   char *s, sbuf[64];
  526.   int i, is;
  527.  
  528.   s=getvar("wims_session");
  529.   if(s==NULL || *s==0 || strstr(s,"robot")!=NULL) return 0;
  530.   mystrncpy(sbuf,s,sizeof(sbuf));
  531.   s=strchr(sbuf,'_'); if(s) *s=0;
  532.   accessfile(buf,"r","../sessions/%s/exodep.%s",sbuf,sh);
  533.   if(buf[0]==0) {     /* no dep file found */
  534.     is=atoi(sh); if(is<=0 || is>totsheets) return 0;
  535.     s=getvar("wims_class"); if(s==NULL || *s==0) return 0;
  536.     getscoreuser(s,"");
  537.     return _depcheck(deps,rscore+sheetstart[is-1],shexocnt[is-1]);
  538.   }
  539.   for(i=1,s=strchr(buf,':'); s && i<exo; i++, s=strchr(s+1,':'));
  540.   if(s==NULL) return 0;     /* bad file or exo number */
  541.   if(myisdigit(*++s)) return 0; else return 1;
  542. }
  543.  
  544. int exam_depcheck(char *deps, int exam)
  545. {
  546.   struct scoreresult esc[MAX_EXOS];
  547.   int i;
  548.   exam_currscore(exam);
  549.   for(i=0;i<MAX_EXOS;i++) {
  550.     esc[i].require=esc[i].mean=10;
  551.     if(exam_scoredata[i]==-1000) esc[i].score=0;
  552.     else esc[i].score=exam_scoredata[i];
  553.   }
  554.   return _depcheck(deps,esc,MAX_EXOS);
  555. }
  556.