Back to index

wims  3.65+svn20090927
calc.c
Go to the documentation of this file.
00001 /*    Copyright (C) 1998-2003 XIAO, Gang of Universite de Nice - Sophia Antipolis
00002  *
00003  *  This program is free software; you can redistribute it and/or modify
00004  *  it under the terms of the GNU General Public License as published by
00005  *  the Free Software Foundation; either version 2 of the License, or
00006  *  (at your option) any later version.
00007  *
00008  *  This program is distributed in the hope that it will be useful,
00009  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00010  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00011  *  GNU General Public License for more details.
00012  *
00013  *  You should have received a copy of the GNU General Public License
00014  *  along with this program; if not, write to the Free Software
00015  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00016  */
00017 
00018        /* This is part of wims source.
00019         * This file does computations and output. */
00020 
00021 int _sort_numeric, _sort_nocase;
00022 typedef struct SORT_STRUCT {char *str; double val; int serial;} SORT_STRUCT;
00023 struct SORT_STRUCT sort_sep[MAX_SORT_ITEM+1];
00024 
00025 void secure_exec(void)
00026 {
00027     if((untrust&6)==0) return;
00028     module_error("Illegal_command");
00029 }
00030 
00031        /* internal comparers */
00032 int __sorter(const void *p1, const void *p2)
00033 {
00034     struct SORT_STRUCT *pp1, *pp2;
00035     
00036     pp1=(struct SORT_STRUCT *) p1; pp2=(struct SORT_STRUCT *) p2;
00037     if(_sort_numeric) {
00038        double dd=(pp1->val)-(pp2->val);
00039        if(dd>0) return 1;
00040        if(dd<0) return -1;
00041        return 0;
00042     }
00043     if(_sort_nocase) return strcasecmp(pp1->str,pp2->str);
00044     else return strcmp(pp1->str,pp2->str);
00045 }
00046 
00047 int _char_sorter(const void *c1, const void *c2)
00048 {
00049     if(_sort_nocase) return tolower(*(char *)c1)-tolower(*(char *)c2);
00050     else return *(char *)c1-*(char *)c2;
00051 }
00052 
00053 enum {sort_char, sort_word, sort_item, sort_line, sort_row,
00054       sort_numeric, sort_nocase, sort_reverse
00055 };
00056 struct {char *name; int type;
00057 } sort_keyw[]={
00058            {"char",sort_char},
00059          {"chars",sort_char},
00060          {"character",sort_char},
00061          {"characters",sort_char},
00062          {"word",sort_word},
00063          {"words",sort_word},
00064          {"item",sort_item},
00065          {"items",sort_item},
00066          {"line",sort_line},
00067          {"lines",sort_line},
00068          {"list",sort_item},
00069          {"numeric",sort_numeric},
00070          {"numerical",sort_numeric},
00071          {"nocase",sort_nocase},
00072          {"ignorecase",sort_nocase},
00073          {"reverse",sort_reverse},
00074          {"row",sort_row},
00075          {"rows",sort_row}
00076 };
00077 #define sort_keyw_no (sizeof(sort_keyw)/sizeof(sort_keyw[0]))
00078 
00079        /* string sort */
00080 void calc_sort(char *p)
00081 {
00082     char *p1, *p2, buf[MAX_LINELEN+1], *csep[MAX_SORT_ITEM+1];
00083     char fs=0;
00084     int (*cutfn)(char *p,char *list[],int max);
00085     int nocase=0, reverse=0, numeric=0, type;
00086     int i,j,l,t,total;
00087     
00088     cutfn=NULL;
00089     for(i=0,p1=find_word_start(p);i>=0;p1=find_word_start(p2)) {
00090        p2=find_word_end(p1); if(*p2!=0) *p2++=0;
00091        for(i=0;i<sort_keyw_no && strcasecmp(p1,sort_keyw[i].name)!=0;i++);
00092        if(i>=sort_keyw_no) module_error("syntax_error");
00093        switch(type=sort_keyw[i].type) {
00094            case sort_nocase: nocase=1; break;
00095            case sort_reverse: reverse=1; break;
00096            case sort_numeric: numeric=1; break;
00097            case sort_char: i=-1; break;
00098            case sort_word: cutfn=cutwords; fs=' '; i=-1; break;
00099            case sort_item: cutfn=cutitems; fs=','; i=-1; break;
00100            case sort_row:
00101            case sort_line: cutfn=cutlines; fs='\n'; i=-1; break;
00102        }
00103     }
00104     if(p1[0]=='o' && p1[1]=='f' && myisspace(p1[2])) p1=find_word_start(p1+2);
00105     mystrncpy(buf,p1,sizeof(buf)); substit(buf); *p=0;
00106     t=0; if(type==sort_row) t=rows2lines(buf);
00107     _sort_nocase=nocase; _sort_numeric=numeric;
00108     if(cutfn) {
00109        char ordbuf[MAX_LINELEN+1];
00110        total=cutfn(buf,csep,MAX_SORT_ITEM+1);
00111        if(total<=0 || total>MAX_SORT_ITEM) return;
00112        for(i=0;i<total;i++) {
00113            sort_sep[i].str=csep[i];
00114            if(numeric) sort_sep[i].val=evalue(csep[i]);
00115        }
00116        for(i=0;i<total;i++) sort_sep[i].serial=i+1;
00117        qsort(sort_sep,total,sizeof(sort_sep[0]),__sorter);
00118        p1=p;p2=ordbuf;ordbuf[0]=0;
00119        for(i=0;i<total;i++) {
00120            if(reverse) j=total-1-i; else j=i;
00121            l=strlen(sort_sep[j].str);
00122            if(p1-p+l>=MAX_LINELEN-1) module_error("cmd_output_too_long");
00123            if(p1>p) *p1++=fs;
00124            memmove(p1,sort_sep[j].str,l+1); p1+=l;
00125            if(i>0) *p2++=',';
00126            mystrncpy(p2,int2str(sort_sep[j].serial),
00127                     MAX_LINELEN-(p2-ordbuf)-1);
00128            p2+=strlen(p2);
00129        }
00130        force_setvar("wims_sort_order",ordbuf);
00131     }
00132     else { /* case of chars */
00133        qsort(buf,strlen(buf),1,_char_sorter);
00134        total=strlen(buf);
00135        if(reverse) {
00136            for(i=total-1;i>=0;i--) *(p+total-i-1)=buf[i];
00137            *(p+total)=0;
00138        }
00139        else memmove(p,buf,total+1);
00140     }
00141     if(t) lines2rows(p);
00142 }
00143 
00144        /* Print a debug output */
00145 void calc_debug(char *p)
00146 {
00147     secure_exec(); setvar("debug",p); module_error("debug");
00148 }
00149 
00150        /* execute a command thru wimslogd */
00151 void calc_daemon(char *p)
00152 {
00153     if(!trusted_module() || is_class_module) {*p=0; return;}
00154     _daemoncmd(p);
00155 }
00156 
00157 void _get_exec_error(char *errorfname, char *cmdname)
00158 {
00159     char *p2;
00160     char obuf[MAX_LINELEN+1];
00161     int l;
00162     if(errorfname) accessfile(obuf,"e","%s",errorfname);
00163     else {
00164        l=read(mxtab[multiexec_index].pipe_stderr[0],obuf,MAX_LINELEN/4);
00165        if(l<0 || l>MAX_LINELEN) l=0;
00166        obuf[l]=0;
00167     }
00168     if(obuf[0]) {
00169        p2=find_word_end(obuf);
00170        if((manageable<1 || strcasecmp(tmp_debug,"yes")!=0) && 
00171           !trusted_module() &&
00172           strncmp(p2," not_INStalled",strlen(" not_INStalled"))==0) {
00173            *p2=0; setvar("missing_software",obuf);
00174            snprintf(p2+1,MAX_LINELEN-strlen(obuf)-8,"missing_%s",obuf);
00175            if(!outputing) user_error(p2+1);
00176        }
00177        p2=getvar("wims_exec_error"); if(p2==NULL) p2="";
00178        snprintf(tmplbuf,sizeof(tmplbuf),"%s\nERROR from %s:\n%s",p2,cmdname,obuf);
00179        force_setvar("wims_exec_error",tmplbuf);
00180     }
00181 }
00182 
00183        /* execute an external program */
00184        /* The output of the external program should be put into
00185         * a file session_directory/session/cmd.tmp. */
00186 void calc_exec(char *p)
00187 {
00188     int i,j,k;
00189     char *cmd, *parm, *pp;
00190     char namebuf[MAX_EXEC_NAME+1], cmdstr[MAX_LINELEN+256], obuf[MAX_LINELEN+1];
00191     char outfname[MAX_FNAME+1], errorfname[MAX_FNAME+1],
00192       typefname[MAX_FNAME+1], varfname[MAX_FNAME+1];
00193     char *abuf[2]={NULL,NULL};
00194     WORKING_FILE wf;
00195     if(robot_access || time(0)>=limtimex-1) {
00196        *p=0;return;
00197     }
00198     cmd=find_word_start(p); if(*cmd==0) return; /* No command. */
00199     parm=find_word_end(cmd);j=parm-cmd;parm=find_word_start(parm);
00200     if(j>MAX_EXEC_NAME) {
00201        wrong_command:
00202        setvar(error_data_string,namebuf); module_error("bad_cmd");
00203        *p=0; return; /* should not occur */
00204     }
00205     memmove(namebuf,cmd,j); namebuf[j]=0;
00206        /* Specifying parent directory in command name is of course
00207         * prohibited.
00208         * The module developper cannot start from root, for bin_dir
00209         * will be prefixed to cmd. */
00210     if(strstr(namebuf,parent_dir_string)!=NULL) {
00211        setvar(error_data_string,namebuf); module_error("illegal_cmd");
00212        *p=0; return;
00213     }
00214     snprintf(cmdstr,sizeof(cmdstr),"%s/%s",bin_dir,namebuf);
00215     if(ftest(cmdstr)!=is_exec) goto wrong_command;
00216     abuf[0]=cmdstr;
00217     mkfname(outfname,"%s/exec.out",tmp_dir);
00218     mkfname(errorfname,"%s/exec.err",tmp_dir);
00219     mkfname(typefname,"%s/exec.type",tmp_dir);
00220     unlink(typefname);
00221     mkfname(varfname,"%s/exec.var",tmp_dir);
00222     unlink(varfname);
00223     if(!trusted_module()) setenv("untrust","yes",1); else unsetenv("untrust");
00224     if(strcmp(parm,"about")!=0 && multiexec(namebuf,abuf)) {
00225        fd_set rset;
00226        struct timeval t;
00227        int v1, v2, fd;
00228        
00229        i=strlen(parm);
00230        fd=mxtab[multiexec_index].pipe_stdin[1];
00231        write(fd,parm,i);
00232        write(fd,multiexec_random,strlen(multiexec_random));
00233        *p=0; i=0; k=MAX_LINELEN; fd=mxtab[multiexec_index].pipe_stdout[0];
00234        while(k>0) {
00235            FD_ZERO(&rset); FD_SET(fd,&rset);
00236            t.tv_sec=0; t.tv_usec=100000; v1=0; v2=300;  /* 3 seconds */
00237            if(select(fd+1,&rset,NULL,NULL,&t)<=0) {
00238               v1++; if(v1>=v2) break;
00239               else continue;
00240            }
00241            j=read(fd,p+i,k);
00242            if(j<0 || j>k) break;
00243            i+=j; k-=j; p[i]=0;
00244            pp=strstr(p,multiexec_random);
00245            if(pp) {*pp=0; break;}
00246        }
00247        fd=mxtab[multiexec_index].pipe_stderr[0];
00248        FD_ZERO(&rset); FD_SET(fd,&rset);
00249        t.tv_sec=0; t.tv_usec=0;
00250        if(select(fd+1,&rset,NULL,NULL,&t)>0)
00251          _get_exec_error(NULL,namebuf);
00252        strip_trailing_spaces(p);
00253        return;
00254     }
00255     exportall(); setenv("wims_exec_parm",parm,1);
00256     execredirected(cmdstr,NULL,outfname,errorfname,abuf);
00257     if(open_working_file(&wf,varfname)==0) {
00258        char *pt, *pv;
00259        while(wgetline(tmplbuf,MAX_LINELEN, &wf)!=EOF) { 
00260            pt=find_word_start(tmplbuf); pv=strchr(pt,'=');
00261            if(pv==NULL || pv<=tmplbuf) continue;
00262            *pv=0; pv=find_word_start(++pv);
00263            *find_word_end(pt)=0;
00264            strip_trailing_spaces(pv);
00265            setvar(pt,pv);
00266        }
00267        close_working_file(&wf,0);
00268     }
00269     read_tmp_file(p,"exec.out");
00270     read_tmp_file(obuf,"exec.type");
00271     if(obuf[0]) k=atoi(obuf); else k=0;
00272     for(i=2;i<=k && i<256;i++) {
00273        char nbuf[32];
00274        snprintf(nbuf,sizeof(nbuf),"exec.out.%d",i);
00275        read_tmp_file(obuf,nbuf);
00276        if(obuf[0]) {
00277            snprintf(nbuf,sizeof(nbuf),"wims_exec_out_%d",i);
00278            force_setvar(nbuf,obuf);
00279        }
00280     }
00281     strip_trailing_spaces(p);
00282     _get_exec_error(errorfname,namebuf);
00283     return;
00284 }
00285 
00286        /* execute external program in the module directory. 
00287         * For privileged modules only */
00288 void calc_mexec(char *p)
00289 {
00290     char *public_bin;
00291     if(robot_access) return;
00292     if(trusted_module()!=1 || is_class_module) {
00293        module_error("not_trusted"); *p=0; return;
00294     }
00295        /* The following is useless, because mexec content is often given
00296         * by variables */
00297 /*    if(strstr(p,PARENT_DIR_STRING)!=NULL) {
00298        setvar("wims_error_data",p);
00299        module_error("illegal_fname"); *p=0; return;
00300     }
00301 */    public_bin=bin_dir; bin_dir=module_prefix;
00302     exec_is_module=1;
00303     calc_exec(p); bin_dir=public_bin;
00304     exec_is_module=0;
00305 }
00306 
00307 void _calc_exec(char *p, char *arg0, char *arg1, int n)
00308 {
00309     char *abuf[8];
00310     char outfname[MAX_FNAME+1], errorfname[MAX_FNAME+1];
00311 
00312     if(robot_access) {*p=0; return;}
00313     if(!trusted_module() || is_class_module) {
00314        if(strcasecmp(tmp_debug,"yes")==0)
00315          accessfile(p,"w","%s/%s.cmd",tmp_dir,arg0);
00316        abuf[0]="bin/ch..root";
00317        abuf[1]="&"; abuf[2]=arg0; abuf[3]=p;
00318        abuf[4]=NULL;
00319     }
00320     else {
00321 /*     if(strstr(p,PARENT_DIR_STRING)!=NULL) {
00322            setvar("wims_error_data",p);
00323            module_error("illegal_fname"); *p=0; return;
00324        }
00325 */     abuf[0]=arg0; abuf[1]=arg1; abuf[n]=p; abuf[n+1]=NULL;
00326     }
00327     mkfname(outfname,"%s/exec.out",tmp_dir);
00328     mkfname(errorfname,"%s/exec.err",tmp_dir);
00329     wrapexec=1; exportall();
00330     execredirected(abuf[0],NULL,outfname,errorfname,abuf);
00331     read_tmp_file(p,"exec.out");
00332     _get_exec_error(errorfname,arg0);
00333 }
00334 
00335        /* call shell. */
00336 void calc_sh(char *p)
00337 {
00338     _calc_exec(p,"sh","-c",2);
00339 }
00340 
00341        /* call perl. */
00342 void calc_perl(char *p)
00343 {
00344     _calc_exec(p,"perl","-e",2);
00345 }
00346 
00347        /* simple evaluation of string */
00348 void calc_evalue(char *p)
00349 {
00350     double d;
00351     d=evalue(p); float2str(d,p); return;
00352 }
00353 
00354        /* substitute math variables */
00355 void calc_mathsubst(char *p)
00356 {
00357     char *expr, *val, *nam, *pp;
00358     char buf[MAX_LINELEN+1];
00359     expr=wordchr(p,"in"); if(expr==NULL) goto error;
00360     strcpy(buf,find_word_start(expr+strlen("in")));substit(buf);
00361     *expr=0; substit(p);
00362     val=strchr(p,'=');
00363     if(val==NULL) {
00364        error:
00365        module_error("mathsubst_syntax"); *p=0;return; 
00366     }
00367     nam=find_word_start(p); *val=0;
00368     val=find_word_start(val+1);
00369     for(pp=val+strlen(val)-1;pp>=val && isspace(*pp);*pp--=0);
00370     *find_word_end(nam)=0;
00371     if(*nam==0) goto error;
00372     if(*nam) for(pp=varchr(buf,nam);pp!=NULL;pp=varchr(pp,nam)) {
00373        string_modify(buf,pp,pp+strlen(nam),"%s",val);
00374        pp+=strlen(val);
00375     }
00376     strcpy(p,buf);
00377 }
00378 
00379        /* substitute and evaluate. */
00380 void calc_evalsubst(char *p)
00381 {
00382     calc_mathsubst(p);
00383     calc_evalue(p);
00384 }
00385 
00386        /* Nothing needs to be done in the function; 
00387         * substitution is done or not by tag in the structure. */
00388 void calc_subst(char *p)
00389 {
00390 }
00391 
00392 int _randrep(char *p)
00393 {
00394     char *rep;
00395     int i;
00396     rep=wordchr(p,"repeat"); if(rep==NULL) return 1;
00397     *rep=0; rep+=strlen("repeat"); i=evalue(rep);
00398     if(i<1) i=1; if(i>MAX_VALUE_LIST) i=MAX_VALUE_LIST;
00399     return i;
00400 }
00401 
00402        /* integer random */
00403 void calc_randint(char *p)
00404 {
00405     int lbound, ubound, n, i, t;
00406     char *p1, *parm[2];
00407 
00408     t=_randrep(p); n=cutitems(p,parm,2);
00409     if(n<=0) {
00410        snprintf(p,3,"0"); return; /* Random without bound: return 0. */
00411     }
00412     lbound=evalue(parm[0]);
00413        /* Missing ubound: random between +-1 and lbound. */
00414     if(n<=1) {if(lbound<0) ubound=-1; else ubound=1;}
00415     else ubound=evalue(parm[1]);
00416     if(lbound>ubound) {i=lbound; lbound=ubound; ubound=i;}
00417     for(i=0,p1=p;i<t;i++) {
00418        if(i>0) *p1++=',';
00419        mystrncpy(p1,int2str((int) irand(ubound-lbound+1)+lbound),
00420                 MAX_LINELEN-(p1-p)-1);
00421        p1+=strlen(p1);
00422     }
00423     return;
00424 }
00425 
00426        /* floating random */
00427 void calc_randdouble(char *p)
00428 {
00429     double lbound, ubound;
00430     int n, i, t;
00431     char *parm[2], *p1;
00432     
00433     t=_randrep(p); n=cutitems(p,parm,2);
00434     if(n<=0) {
00435        snprintf(p,3,"0"); return; /* Random without bound: return 0. */
00436     }
00437     lbound=evalue(parm[0]);
00438     if(n<=1) ubound=0; /* Missing ubound: random between 0 and lbound. */
00439     else ubound=evalue(parm[1]);
00440     for(i=0,p1=p;i<t;i++) {
00441        float2str(drand(ubound-lbound)+lbound,tmplbuf);
00442        mystrncpy(p1,tmplbuf,MAX_LINELEN-(p1-p));
00443        p1+=strlen(p1);
00444        if(i<t-1 && p1-p<MAX_LINELEN) *p1++=',';
00445     }
00446     *p1=0; return;
00447 }
00448 
00449        /* Takes randomly a record in a datafile */
00450 void calc_randfile(char *p)
00451 {
00452     char *pp, n[MAX_FNAME+1];
00453     int i, j;
00454     
00455     pp=find_word_start(p); *find_word_end(pp)=0;
00456     mystrncpy(n,pp,sizeof(n));
00457     i=datafile_recordnum(pp);
00458     if(i<=0) {  /* zero records */
00459        *p=0; return;
00460     }
00461     j=irand(i); datafile_fnd_record(n,j+1,p);
00462     return;
00463 }
00464 
00465        /* random char, word, line, item in a string */
00466 void calc_randchar(char *p)
00467 {
00468     p[0]=p[(int) irand(strlen(p))];p[1]=0;
00469 }
00470 
00471 void calc_randitem(char *p)
00472 {
00473     int i; char *b;
00474     i=itemnum(p);
00475     b=fnd_item(p,irand(i)+1,tmplbuf);
00476     mystrncpy(p,tmplbuf,MAX_LINELEN);
00477 }
00478 
00479 void calc_randline(char *p)
00480 {
00481     int i;
00482     i=linenum(p); fnd_line(p,irand(i)+1,tmplbuf);
00483     mystrncpy(p,tmplbuf,MAX_LINELEN);
00484 }
00485 
00486 void calc_randrow(char *p)
00487 {
00488     rows2lines(p); calc_randline(p);
00489 }
00490 
00491 void calc_randword(char *p)
00492 {
00493     int i;
00494     i=wordnum(p); fnd_word(p,irand(i)+1,tmplbuf);
00495     mystrncpy(p,tmplbuf,MAX_LINELEN);
00496 }
00497 
00498        /* random permutation of {1,...,n} */
00499 void calc_randperm(char *p)
00500 {
00501     int n, i, j, k, t, pt, type;
00502     double v;
00503     char buf1[MAX_LINELEN+1],buf2[MAX_LINELEN+1];
00504     char *list[MAX_RANDPERM];
00505     int table[MAX_RANDPERM];
00506     char *p1,*p2,*pp;
00507     
00508     t=0; pt=0; pp=p;
00509     p1=find_word_start(p); p2=find_word_end(p1);
00510     if(p2-p1==strlen("even") && strncasecmp(p1,"even",strlen("even"))==0) {
00511        t=1; pp=p2;
00512     }
00513     if(p2-p1==strlen("odd") && strncasecmp(p1,"odd",strlen("odd"))==0) {
00514        t=-1; pp=p2;
00515     }
00516     
00517     p1=find_item_end(pp); if(*p1==',') {
00518        type=1; n=cutitems(pp,list,MAX_RANDPERM); v=0;
00519     }
00520     else {
00521        v=evalue(pp); n=v; type=0;
00522     }
00523     if(n==1 && !finite(v)) {strcpy(p,pp); goto shorder;}
00524     if(n<=0) {*p=0; return;}
00525     if(n==1 && type==0) {
00526        strcpy(p,"1"); 
00527        shorder: force_setvar("wims_shuffle_order","1"); return;
00528     }
00529     if(n>MAX_RANDPERM) n=MAX_RANDPERM;
00530     for(i=0;i<n;i++) table[i]=i;
00531        /* Uniformity of this algorithm is easy to show by induction. */
00532     for(i=0;i<n-1;i++) {
00533        j=irand(n-i)+i;      if(j==i) continue;
00534        k=table[i]; table[i]=table[j]; table[j]=k;
00535        pt++;
00536     }
00537     pt&=1;
00538     if((t==1 && pt==1) || (t==-1 && pt==0)) {
00539        k=table[0]; table[0]=table[1]; table[1]=k;
00540     }
00541     if(type) {mystrncpy(buf1,list[table[0]],MAX_LINELEN); p1=buf1+strlen(buf1);}
00542     else buf1[0]=0;
00543     mystrncpy(buf2,int2str(table[0]+1),MAX_LINELEN); p2=buf2+strlen(buf2);
00544     for(i=1;i<n;i++) {
00545        if(type) {
00546            j=strlen(list[table[i]]);
00547            if(p1-buf1+j>=MAX_LINELEN-1) module_error("cmd_output_too_long");
00548            *p1++=','; memmove(p1,list[table[i]],j+1); p1+=j;
00549        }
00550        *p2++=',';
00551        mystrncpy(p2,int2str(table[i]+1), MAX_LINELEN-(p2-buf2)-1);
00552        p2+=strlen(p2);
00553     }
00554     if(type) mystrncpy(p,buf1,MAX_LINELEN);
00555     else mystrncpy(p,buf2,MAX_LINELEN);
00556     force_setvar("wims_shuffle_order",buf2);
00557 }
00558 
00559        /* Computes number of lines in the parm. */
00560 void calc_linenum(char *p)
00561 {
00562     int i=linenum(p); mystrncpy(p,int2str(i),MAX_LINELEN);
00563 }
00564 
00565        /* Computes number of lines in the parm. */
00566 void calc_rownum(char *p)
00567 {
00568     int i=rownum(p); mystrncpy(p,int2str(i),MAX_LINELEN);
00569 }
00570 
00571        /* Computes number of items in the list p. */
00572 void calc_itemnum(char *p)
00573 {
00574     int i=itemnum(p); mystrncpy(p,int2str(i),MAX_LINELEN);
00575 }
00576 
00577        /* Computes number of records in the datafile p. */
00578 void calc_recordnum(char *p)
00579 {
00580     int i;
00581     i=datafile_recordnum(p); mystrncpy(p,int2str(i),MAX_LINELEN);
00582 }
00583 
00584        /* Computes number of words in the parm. */
00585 void calc_wordnum(char *p)
00586 {
00587     int i=wordnum(p); mystrncpy(p,int2str(i),MAX_LINELEN);
00588 }
00589 
00590        /* string length */
00591 void calc_lengthof(char *p)
00592 {
00593     int i;
00594 /* Strip leading and trailing spaces? */  
00595     i=strlen(p); mystrncpy(p,int2str(i),MAX_LINELEN);
00596 }
00597 
00598 char *_blockof_one(char *buf,
00599                 unsigned int len,
00600                 char *(fnd_fn)(char *pl, int n, char bbuf[]),
00601                 int i, char bbuf[])
00602 {
00603     if(i<0 || i>len || (i==0 && fnd_fn!=datafile_fnd_record) ) {
00604        bbuf[0]=0; return bbuf;
00605     }
00606     return fnd_fn(buf,i,bbuf);
00607 }
00608 
00609 void _blockof(char *p,
00610              unsigned int (len_fn)(char *pl),
00611              char *(fnd_fn)(char *pl, int n, char bbuf[]),
00612              char *append_char,
00613              char *msg_str)
00614 {
00615     int i,j,l,started;
00616     char *pp, *pe, *po, *pnext;
00617     char buf[MAX_LINELEN+1], obuf[MAX_LINELEN+1], bbuf[MAX_LINELEN+1];
00618     pp=wordchr(p,"of");
00619     if(pp==NULL) {
00620        setvar(error_data_string,msg_str);
00621        module_error("no_of"); *p=0; return;
00622     }
00623     *pp=0; pp=find_word_start(pp+strlen("of")+1);
00624     mystrncpy(buf,pp,sizeof(buf)); substit(buf);
00625     obuf[0]=0; started=0; po=obuf;
00626     pp=strparstr(p,"to");
00627     while(*pp!=0 && ((pp>p && !isspace(*(pp-1))) || !isspace(*(pp+2))))
00628       pp=strparstr(pp+2,"to");
00629     if(*pp==0) pp=strparstr(p,"..");
00630     if(*pp!=0) {
00631        int t=len_fn(buf);
00632        *pp=0; pe=find_word_start(pp+2);
00633        i=evalue(p); j=evalue(pe); pnext=buf;
00634        if(i<0) i=t+i+1; if(i<1) i=1;
00635        if(j<0) j=t+j+1; if(j>t) j=t;
00636        for(; i<=j; i++) {
00637            if(started==0 || fnd_fn==datafile_fnd_record)
00638              pp=_blockof_one(buf,t,fnd_fn,i,bbuf);
00639            else
00640              pp=_blockof_one(pnext,1,fnd_fn,1,bbuf);
00641            l=strlen(pp); pnext=fnd_nextpos;
00642            if(l+(po-obuf)>=MAX_LINELEN-3) {
00643               too_long: user_error("cmd_output_too_long"); return;
00644            }
00645            if(started) {strcpy(po,append_char); po+=strlen(po);}
00646            memmove(po,pp,l); po+=l; *po=0; started++;
00647        }
00648     }
00649     else {
00650        char *p1,*p2,ibuf[MAX_LINELEN+1];
00651        int t=len_fn(buf);
00652        mystrncpy(ibuf,p,sizeof(ibuf)); substit(ibuf);
00653        for(p1=ibuf;*p1;p1=find_word_start(p2)) {
00654            p2=find_item_end(p1); if(*p2) *p2++=0;
00655            i=evalue(p1);
00656            if(i<0) i=t+i+1;
00657            if(i>t || i<0) continue;
00658            pp=_blockof_one(buf,t,fnd_fn,i,bbuf);l=strlen(pp);
00659            if(l+(po-obuf)>=MAX_LINELEN-3) goto too_long;
00660            if(started) {strcpy(po,append_char); po+=strlen(po);}
00661            memmove(po,pp,l); po+=l; *po=0; started++;
00662        }
00663     }
00664     mystrncpy(p,obuf,MAX_LINELEN);
00665 }
00666 
00667        /* pick lines */
00668 void calc_lineof(char *p)
00669 {
00670     _blockof(p,linenum,fnd_line,"\n","line");
00671 }
00672 
00673        /* pick rows */
00674 void calc_rowof(char *p)
00675 {
00676     char *cc, tbuf[MAX_LINELEN+1]; /* called by substit(); no right to use tmplbuf */
00677     mystrncpy(tbuf,p,sizeof(tbuf)); substit(tbuf);
00678     if(strchr(tbuf,'\n')==NULL && strchr(tbuf,';')!=NULL) cc=";";
00679     else cc="\n";
00680     _blockof(p,rownum,fnd_row,cc,"row");
00681 }
00682 
00683        /* pick items */
00684 void calc_itemof(char *p)
00685 {
00686     _blockof(p,itemnum,fnd_item,", ","item");
00687 }
00688 
00689        /* pick records in datafile */
00690 void calc_recordof(char *p)
00691 {
00692     _blockof(p,datafile_recordnum,datafile_fnd_record,"\n:","record");
00693 }
00694 
00695        /* pick words */
00696 void calc_wordof(char *p)
00697 {
00698     _blockof(p,wordnum,fnd_word," ","word");
00699 }
00700 
00701        /* pick characters */
00702 void calc_charof(char *p)
00703 {
00704     _blockof(p,charnum,fnd_char,"","char");
00705 }
00706 
00707 char *append_obj[]={
00708     "item","line","word"
00709 };
00710 char apch_list[]={',','\n',' '};
00711 #define append_obj_no (sizeof(append_obj)/sizeof(append_obj[0]))
00712 
00713        /* append object to string */
00714 void calc_append(char *p)
00715 {
00716     char append_char, *p1,*p2,*p3,*p4;
00717     int i,l1,l2;
00718     p1=find_word_start(p);p2=find_word_end(p1);
00719     if(*p2!=0) *p2++=0;
00720     for(i=0;i<append_obj_no && strcmp(p1,append_obj[i])!=0;i++);
00721     if(i>=append_obj_no) {
00722        synterr:
00723        module_error("append_syntax");
00724        *p=0; return;
00725     }
00726     append_char=apch_list[i];
00727     p3=wordchr(p2,"to");
00728     if(p3==NULL) goto synterr;
00729     for(p4=p3-1;p4>p2 && isspace(*(p4-1));p4--);
00730     *p4=0;p3=find_word_start(p3+strlen("to"));
00731     memmove(tmplbuf,p2,p4-p2); tmplbuf[p4-p2]=0; strcpy(p,p3);
00732     substit(tmplbuf);substit(p);
00733     l1=strlen(p); l2=strlen(tmplbuf);
00734     if(l1+l2>=MAX_LINELEN-1) user_error("cmd_output_too_long");
00735     p3=find_word_start(p); p4=p+l1;
00736     if(*p3) *p4++=append_char;
00737     memmove(p4,tmplbuf,l2); p4[l2]=0;
00738 }
00739 
00740        /* character translation */
00741 void calc_translate(char *p)
00742 {
00743     int i, internal;
00744     char *q[3];
00745     char bf[3][MAX_LINELEN+1];
00746     char fn[3][MAX_FNAME+1], *arglist[8];
00747 
00748     q[0]=find_word_start(p); internal=0;
00749     if(strncasecmp(q[0],"internal",strlen("internal"))==0 &&
00750        isspace(*(q[0]+strlen("internal")))) {
00751        q[0]=find_word_start(q[0]+strlen("internal"));
00752        internal=1;
00753     }
00754     q[1]=wordchr(q[0],"to"); q[2]=wordchr(q[0],"in");
00755     if(q[1]==NULL || q[2]==NULL) {
00756        module_error("tr_syntax"); *p=0; return;
00757     }
00758     *q[1]=0; *q[2]=0;
00759     q[1]=find_word_start(q[1]+strlen("to"));
00760     q[2]=find_word_start(q[2]+strlen("in"));
00761     for(i=0;i<3;i++) {
00762        strip_trailing_spaces(q[i]);
00763        mystrncpy(bf[i],q[i],sizeof(bf[i]));substit(bf[i]);
00764     }
00765     if(bf[0][0]==0) goto bailout;
00766     if(internal || strstr(tmp_dir,"sessions")==NULL || (strpbrk(bf[0],"\\[-")==NULL && 
00767        strpbrk(bf[1],"\\[-")==NULL)) {    /* direct internal translation */
00768        char *pp;
00769        if(strlen(bf[1])<strlen(bf[0])) bf[0][strlen(bf[1])]=0;
00770        for(pp=strpbrk(bf[2],bf[0]);pp!=NULL && *pp!=0 && pp<bf[2]+MAX_LINELEN;
00771            pp=strpbrk(pp+1,bf[0])) {
00772            for(i=0;bf[0][i]!=*pp && bf[0][i]!=0;i++);
00773            if(bf[0][i]!=0) *pp=bf[1][i];
00774        }          
00775        bailout: mystrncpy(p,bf[2],MAX_LINELEN);
00776        return;
00777     }
00778     mkfname(fn[0],"%s/tr.in",tmp_dir);
00779     mkfname(fn[1],"%s/tr.out",tmp_dir);
00780     accessfile(bf[2],"w",fn[0]);
00781     arglist[0]=tr_prog; arglist[1]=bf[0]; arglist[2]=bf[1];
00782     arglist[3]=NULL; exportall();
00783     execredirected(tr_prog,fn[0],fn[1],"/dev/null",arglist);
00784     read_tmp_file(p,"tr.out");
00785     strip_trailing_spaces(p);
00786 }
00787 
00788        /* internal common routine for positionof */
00789 void _pos(char *hay, char *stitch, int style, char *out,
00790          char *(fnd_obj)(char *p, int n, char bf[]),
00791          unsigned int (objnum)(char *p))
00792 {
00793     int i,n,t;
00794     char buf[MAX_LINELEN+1], nbuf[10];
00795     
00796     n=objnum(hay);
00797     for(i=1;i<=n;i++) {
00798        fnd_obj(hay,i,buf);
00799        if(strcmp(buf,stitch)!=0) continue;
00800        t=strlen(out); if(t>MAX_LINELEN-12) return;
00801        if(t>0) strcat(out,",");
00802        snprintf(nbuf,sizeof(nbuf),"%d",i); strcat(out,nbuf);
00803     }
00804 }
00805 
00806        /* return positions of searched for objects */
00807 void calc_pos(char *p)
00808 {
00809     char buf[2][MAX_LINELEN+1];
00810     char *p1, *p2;
00811     int  style;
00812     
00813     p1=find_word_start(p); p2=wordchr(p1,"in");
00814     if(p2==NULL) module_error("syntax_error");
00815     *p2=0;p2=find_word_start(p2+strlen("in"));
00816     strip_trailing_spaces(p1);
00817     strcpy(buf[0],p1);*find_word_end(buf[0])=0; style=0;
00818     if(strcmp(buf[0],"word")==0) style=1;
00819     else {
00820        if(strcmp(buf[0],"item")==0) style=2;
00821        else {
00822            if(strcmp(buf[0],"line")==0) style=3;
00823            else {
00824               if(strcmp(buf[0],"char")==0) style=4;
00825            }
00826        }
00827     }
00828     if(style>0) p1=find_word_start(find_word_end(p1));
00829     strcpy(buf[0],p1); strcpy(buf[1],p2);
00830     substit(buf[0]); substit(buf[1]); *p=0;
00831     switch(style) {
00832        case 0: {     /* string */
00833            char *pp, nbuf[10];
00834            int i,t;
00835            for(pp=strstr(buf[1],buf[0]);pp!=NULL;pp=strstr(pp+1,buf[0])) {
00836               i=pp-buf[1]; t=strlen(p); if(t>MAX_LINELEN-12) return;
00837               if(t>0) strcat(p,",");
00838               snprintf(nbuf,sizeof(nbuf),"%d",i); strcat(p,nbuf);
00839            }
00840            return;
00841        }
00842        case 1: {     /* word */
00843            _pos(buf[1],buf[0],style,p,fnd_word,wordnum);
00844            return;
00845        }
00846        case 2: {     /* item */
00847            _pos(buf[1],buf[0],style,p,fnd_item,itemnum);
00848            return;
00849        }
00850        case 3: {     /* line */
00851            _pos(buf[1],buf[0],style,p,fnd_line,linenum);
00852            return;
00853        }
00854        case 4: {     /* char */
00855            _pos(buf[1],buf[0],style,p,fnd_char,charnum);
00856            return;
00857        }
00858     }
00859 }
00860 
00861        /* internal routine for calc_replace. */
00862 void _obj_replace(char *orig, char *by, char *in, int num,
00863                 char separator, char *result,
00864                 char *(fnd_obj)(char *p, int n, char bf[]),
00865                 unsigned int (objnum)(char *p),
00866                 char *(objchr)(char *p,char *w))
00867 {
00868     int i;
00869     char *p1, *p2;
00870     
00871     strcpy(result,in);
00872     if(num!=0) {
00873        num=objnum(in); i=evalue(orig);
00874        if(i==0) module_error("bad_index");
00875        if(i<0) i=num+i+1;
00876        if(i>num || i<1) return;
00877        if(separator==0) {  /* char */
00878            result[i-1]=by[0]; return;
00879        }
00880        fnd_obj(result,i,orig); p1=fnd_position;
00881        if(i<num) {
00882            fnd_obj(result,i+1,orig); p2=fnd_position;
00883        }
00884        else p2=result+strlen(result);
00885        if(p1==NULL || p2==NULL) internal_error("_obj_replace() error.");
00886        if(i<num) {
00887            i=strlen(by); by[i++]=separator;by[i]=0;
00888        }
00889        string_modify(result,p1,p2,"%s",by);
00890     }
00891     else {
00892        if(separator==0) {
00893            if(orig[0]==0 || by[0]==0) return;
00894            for(p1=strchr(result,orig[0]);p1!=NULL;p1=strchr(p1+1,orig[0]))
00895               *p1=by[0];
00896            return;
00897        }
00898        if(strlen(orig)+strlen(by)==0) return;
00899        for(p1=objchr(result,orig);p1!=NULL;p1=objchr(p1+strlen(by)+1,orig)) {
00900            string_modify(result,p1,p1+strlen(orig),"%s",by);
00901        }
00902     }
00903 }
00904 
00905        /* replacement */
00906 void calc_replace(char *p)
00907 {
00908     int i,style,num,internal;
00909     char *q[3], *pp;
00910     char bf[4][MAX_LINELEN+17];
00911     char fn[3][MAX_FNAME+1], *arglist[8];
00912     char regexp_char='/';
00913 
00914     style=num=0;
00915     q[0]=find_word_start(p); internal=0;
00916     if(strncasecmp(q[0],"internal",strlen("internal"))==0 &&
00917        isspace(*(q[0]+strlen("internal")))) {
00918        q[0]=find_word_start(q[0]+strlen("internal"));
00919        internal=1;
00920     }
00921     q[1]=wordchr(q[0],"by"); q[2]=wordchr(q[0],"in");
00922     if(q[1]==NULL || q[2]==NULL) {
00923        module_error("replace_syntax"); *p=0; return;
00924     }
00925     *q[1]=0; *q[2]=0;
00926     q[1]=find_word_start(q[1]+strlen("by"));
00927     q[2]=find_word_start(q[2]+strlen("in"));
00928     mystrncpy(bf[0],q[0],sizeof(bf[0]));
00929     pp=find_word_end(bf[0]); if(*pp) *(pp++)=0;
00930     if(strcmp(bf[0],"word")==0) style=1;
00931     else {
00932        if(strcmp(bf[0],"item")==0) style=2;
00933        else {
00934            if(strcmp(bf[0],"line")==0) style=3;
00935            else {
00936               if(strcmp(bf[0],"char")==0) style=4;
00937            }
00938        }
00939     }
00940     if(style>0) {
00941        q[0]=find_word_start(find_word_end(q[0]));
00942        mystrncpy(bf[0],q[0],sizeof(bf[0]));
00943        pp=find_word_end(bf[0]); if(*pp) *(pp++)=0;
00944        if(strcmp(bf[0],"number")==0) {
00945            num=1; q[0]=find_word_start(pp);
00946        }
00947     }
00948     for(i=0;i<3;i++) {
00949        strip_trailing_spaces(q[i]);
00950        mystrncpy(bf[i],q[i],sizeof(bf[i]));
00951        substit(bf[i]);
00952     }
00953     if(bf[0][0]==0) {mystrncpy(p,bf[2],MAX_LINELEN); return;}
00954     switch(style) {
00955        case 1: { /* word */
00956            _obj_replace(bf[0],bf[1],bf[2],num,' ',p,
00957                       fnd_word,wordnum,wordchr);
00958            return;
00959        }
00960        case 2: { /* item */
00961            _obj_replace(bf[0],bf[1],bf[2],num,',',p,
00962                       fnd_item,itemnum,itemchr);
00963            return;
00964        }
00965        case 3: { /* line */
00966            _obj_replace(bf[0],bf[1],bf[2],num,'\n',p,
00967                       fnd_line,linenum,linechr);
00968            return;
00969        }
00970        case 4: { /* char */
00971            if(bf[1][0]==0) bf[1][0]=' ';
00972            if(bf[0][0]==0) return;
00973            _obj_replace(bf[0],bf[1],bf[2],num,0,p,
00974                       fnd_char,charnum,charchr);
00975            return;
00976        }
00977        default: break;
00978     }
00979     if(internal || strstr(tmp_dir,"sessions")==NULL || (strpbrk(bf[0],"\\[^.*$")==NULL &&
00980        strpbrk(bf[1],"\\[^.*$")==NULL)) {
00981        /* No regexp, direct replace */    
00982        char *pp;
00983        for(pp=strstr(bf[2],bf[0]);pp<bf[2]+MAX_LINELEN && pp!=NULL;
00984            pp=strstr(pp+strlen(bf[1]),bf[0])) {
00985            string_modify(bf[2],pp,pp+strlen(bf[0]),"%s",bf[1]);
00986        }
00987        mystrncpy(p,bf[2],MAX_LINELEN);return;
00988     }
00989     mkfname(fn[0],"%s/sed.in",tmp_dir);
00990     mkfname(fn[1],"%s/sed.out",tmp_dir);
00991     accessfile(bf[2],"w",fn[0]);
00992     snprintf(bf[3],sizeof(bf[3]),"s%c%s%c%s%cg",
00993             regexp_char,bf[0],regexp_char,bf[1],regexp_char);
00994     arglist[0]=sed_prog; arglist[1]=bf[3]; arglist[2]=NULL;
00995     execredirected(sed_prog,fn[0],fn[1],"/dev/null",arglist);
00996     read_tmp_file(p,"sed.out");
00997     strip_trailing_spaces(p);
00998 }
00999 
01000        /* transforms a string to hexadecimal code, upper-case */
01001 void calc_hex(char *p)
01002 {
01003     unsigned char *p1, orig[MAX_LINELEN+1];
01004     char *p2;
01005     char *hex="0123456789ABCDEF";
01006     
01007     mystrncpy(orig,p,MAX_LINELEN);
01008     for(p1=orig, p2=p; *p1 && p2-p<MAX_LINELEN-2; p1++) {
01009        *p2++=hex[(*p1>>4)&15]; *p2++=hex[*p1&15];
01010     }
01011     *p2=0;    
01012 }
01013 
01014        /* transforms to lower/upper cases */
01015 void calc_tolower(char *p)
01016 {
01017     char *pp;
01018     for(pp=p;*pp;pp++) *pp=tolower(*pp);
01019 }
01020 
01021 void calc_toupper(char *p)
01022 {
01023     char *pp;
01024     for(pp=p;*pp;pp++) *pp=toupper(*pp);
01025 }
01026 
01027        /* strip leading and trailing spaces */
01028 void calc_trim(char *p)
01029 {
01030     char *s;
01031     s=find_word_start(p);
01032     if(s>p) memmove(p,s,MAX_LINELEN-(s-p)+1);
01033     strip_trailing_spaces(p);
01034 }
01035 
01036        /* output date. Uses Linux 'date' utility. */
01037 void calc_date(char *p)
01038 {
01039     char *p1, *p2, *p3;
01040     if(!trusted_module() || is_class_module) {
01041        if(strstr(p,"..")!=NULL) return;
01042        for(p1=find_word_start(p); *p1; p1=find_word_start(p2)) {
01043            p2=find_word_end(p1);
01044            while(*p1=='\'' || *p1=='"') p1++;
01045            if(*p1!='-') continue;
01046            for(p3=p1+1;p3<p2;p3++) if(strchr("rs",*p3)!=NULL) return;
01047        }
01048     }
01049     wrapexec=1;
01050     call_ssh("date %s >%s/date.out 2>/dev/null",p,tmp_dir);
01051     read_tmp_file(p,"date.out");
01052 }
01053 
01054        /* ls, or dir */
01055 void calc_listfile(char *p)
01056 {
01057     char *pp;
01058     
01059        /* only for trusted modules */
01060     if(!trusted_module() || is_class_module) {
01061        module_error("not_trusted"); *p=0; return; 
01062     }
01063        /* security measures. */
01064     for(pp=p;*pp;pp++) if(isspace(*pp) || *pp==';') *pp=' ';
01065     if(strstr(p,parent_dir_string)!=NULL) {
01066        setvar(error_data_string,p);
01067        module_error("illegal_fname"); return;
01068     }
01069     wrapexec=1;
01070     call_sh("ls %s >%s/ls.out 2>%s/ls.err",
01071            p,tmp_dir,tmp_dir);
01072     read_tmp_file(p,"ls.out");
01073 }
01074 
01075        /* instex static: static tex inserts */
01076 void calc_instexst(char *p)
01077 {
01078     char nbuf[MAX_FNAME+1], bufc[MAX_LINELEN+1];
01079     char buf2[1024], altbuf[1024], buf[MAX_LINELEN+1], urlbuf[MAX_LINELEN+1];
01080     char *b, *at, *al, *md1, *md2;
01081     int t, border, vspace;
01082     struct stat st,stc;
01083     char *p1, *p2, *p3, *ppp, *pt;
01084 
01085     if(robot_access) {*p=0; return;}
01086     p1=find_word_start(p); p2=find_word_end(p1);
01087     p3=p2-4; vspace=0;
01088     fix_tex_size();
01089     t=untrust; untrust=0; 
01090     if(find_module_file(m_file.name,bufc,0)) module_error(m_file.name);
01091     else stat(bufc,&stc);
01092     untrust=t;
01093     if(*p3=='.' && (memcmp(p3+1,"gif",3)==0 || memcmp(p3+1,"png",3)==0)) {
01094        char mbuf[MAX_LINELEN+1];
01095        if(*p2!=0) *p2++=0;
01096        p2=find_word_start(p2);
01097        strcpy(mbuf,p1); substit(mbuf);
01098        if(strstr(mbuf,parent_dir_string)!=NULL) {
01099            setvar(error_data_string,mbuf);
01100            module_error("illegal_fname"); return;
01101        }
01102        mkfname(nbuf,"%s/%s",module_prefix,mbuf);
01103     }
01104     else {
01105        ppp=getvar(ro_name[ro_module]);
01106        if(ppp==NULL) internal_error("calc_instexst(): module name vanishes.");
01107        p2=p1;
01108        mkfname(nbuf,"w/instex/%d/%s/%s_%d.gif",
01109                current_tex_size,ppp,m_file.name,m_file.l);
01110     }
01111     snprintf(urlbuf,sizeof(urlbuf),"%s%s?%X",ref_base,nbuf,
01112             (unsigned short int) stc.st_mtime);
01113     mystrncpy(buf,nbuf,sizeof(buf));
01114     if((ppp=strrchr(buf,'/'))!=NULL) {
01115        *ppp=0;mkdirs(buf);
01116     }
01117     b=getvar("ins_border");
01118     at=getvar("ins_attr");
01119     al=getvar("ins_align");
01120     if(at==NULL) at="";
01121     if(al==NULL) al="";al=find_word_start(al);
01122     if(*al!=0) snprintf(buf2,sizeof(buf2),"align=%s",al); else buf2[0]=0;
01123     if(b==NULL || *b==0) border=0;
01124     else border=atoi(b);
01125     if(border<0) border=0; if(border>100) border=100;
01126     if(instex_ready(p2,urlbuf)) goto prt;
01127     if(stat(nbuf,&st)!=0 || st.st_mtime<stc.st_mtime || st.st_size<45) {
01128        setenv("texgif_style",instex_style,1);
01129        setenv("texgif_src",p2,1);
01130        setenv("texgif_outfile",nbuf,1);
01131        setenv("texgif_tmpdir",tmp_dir,1); exportall();
01132        wrapexec=0; call_ssh("%s &>%s/instexst.log",tex2gif,tmp_dir);
01133        setenv("instexst_src","",1);
01134     }
01135     prt: md1=md2="";
01136     if(strcasecmp(al,"middle")==0) {
01137        md1=mathalign_sup1; md2=mathalign_sup2;
01138        vspace=5;
01139     }
01140     if(ins_alt[0]==0) mystrncpy(ins_alt,p2,sizeof(ins_alt));
01141     if(strchr(ins_alt,'"')!=NULL || strlen(ins_alt)>256) ins_alt[0]=0;
01142     pt=getvar("wims_ins_alt"); if(pt==NULL) pt="";
01143     if(ins_alt[0] && strcmp(pt,"none")!=0)
01144       snprintf(altbuf,sizeof(altbuf)," alt=\"%s\"",ins_alt);
01145     else altbuf[0]=0;
01146     snprintf(p,MAX_LINELEN,"%s<img src=\"%s\" border=%d vspace=%d %s %s%s>%s",
01147             md1,urlbuf,border,vspace,at,buf2,altbuf,md2);
01148     setvar("ins_attr",""); ins_alt[0]=0;
01149     setvar("ins_url",urlbuf);
01150 }
01151 
01152        /* extract non-empty lines or items */
01153 void calc_nonempty(char *p)
01154 {
01155     int type, i, cnt;
01156     char *p1, *p2, buf[MAX_LINELEN+1], out[MAX_LINELEN+1];
01157     p1=find_word_start(p); p2=find_word_end(p1);
01158     if(*p2) *p2++=0; else {
01159        *p=0; return;
01160     }
01161     type=0;
01162     if(strcasecmp(p1,"item")==0 || strcasecmp(p1,"items")==0) type=1;
01163     if(type==0 && (strcasecmp(p1,"line")==0 || strcasecmp(p1,"lines")==0))
01164       type=2;
01165     if(type==0 && (strcasecmp(p1,"row")==0 || strcasecmp(p1,"rows")==0))
01166       type=3;
01167     if(type==0) module_error("syntax_error");
01168     out[0]=out[1]=0;
01169     switch(type) {
01170        case 1: {     /* items */
01171            cnt=itemnum(p2);
01172            for(i=1; i<=cnt; i++) {
01173               fnd_item(p2,i,buf);
01174               if(*find_word_start(buf)) {
01175                   strcat(out,",");strcat(out,buf);
01176               }
01177            }
01178            break;
01179        }
01180        case 2: {     /* lines */
01181            lines: cnt=linenum(p2);
01182            for(i=1; i<=cnt; i++) {
01183               fnd_line(p2,i,buf);
01184               if(*find_word_start(buf)) {
01185                   strcat(out,"\n");strcat(out,buf);
01186               }
01187            }
01188            break;
01189        }
01190        case 3: {     /* rows */
01191            int t=rows2lines(p2);
01192            if(t==0) goto lines;
01193            cnt=linenum(p2);
01194            for(i=1; i<=cnt; i++) {
01195               fnd_line(p2,i,buf);
01196               if(*find_word_start(buf)) {
01197                   strcat(out,";");strcat(out,buf);
01198               }
01199            }
01200            break;
01201        }
01202        default: break;
01203     }
01204     strcpy(p,out+1);
01205 }
01206 
01207        /* returns a list with unique items */
01208 void calc_listuniq(char *p)
01209 {
01210     int i,n;
01211     char lout[MAX_LINELEN+2], *ll;
01212     char *cut[MAX_LIST];
01213     lout[0]=lout[1]=0;
01214     n=cutitems(p,cut,MAX_LIST); for(i=0;i<n;i++) {
01215        ll=cut[i];
01216        if(*ll && itemchr(lout,ll)==NULL &&
01217           strlen(lout)+strlen(ll)<MAX_LINELEN-2) {
01218            strcat(lout,",");strcat(lout,ll);
01219        }
01220     }
01221     strcpy(p,lout+1);
01222 }
01223 
01224        /* returns intersection of 2 lists */
01225 void calc_listintersect(char *p)
01226 {
01227     char l1[MAX_LINELEN+1],l2[MAX_LINELEN+1],lout[MAX_LINELEN+2];
01228     char *cut[MAX_LIST];
01229     char *pp, *ll;
01230     int i,n;
01231     
01232     pp=wordchr(p,"and");
01233     if(pp==NULL) module_error("syntax_error");
01234     *pp=0;strcpy(l1,p); strcpy(l2,pp+strlen("and"));
01235     lout[0]=lout[1]=0; substit(l1); substit(l2);
01236     n=cutitems(l1,cut,MAX_LIST); if(n<=0) {*p=0; return;}
01237     for(i=0;i<n;i++) {
01238        ll=cut[i];
01239        if(ll[0] && itemchr(l2,ll)!=NULL && itemchr(lout,ll)==NULL &&
01240           strlen(lout)+strlen(ll)<MAX_LINELEN-2) {
01241            strcat(lout,",");strcat(lout,ll);
01242        }
01243     }
01244     strcpy(p,lout+1);
01245 }
01246 
01247        /* returns union of 2 lists */
01248 void calc_listunion(char *p)
01249 {
01250     char l1[MAX_LINELEN+1],l2[MAX_LINELEN+1],lout[MAX_LINELEN+2];
01251     char *cut[MAX_LIST];
01252     char *pp, *ll;
01253     int i,n;
01254     
01255     pp=wordchr(p,"and");
01256     if(pp==NULL) module_error("syntax_error");
01257     *pp=0;strcpy(l1,p); strcpy(l2,pp+strlen("and"));
01258     lout[0]=lout[1]=0; substit(l1); substit(l2);
01259     n=cutitems(l1,cut,MAX_LIST); for(i=0;i<n;i++) {
01260        ll=cut[i];
01261        if(ll[0] && itemchr(lout,ll)==NULL &&
01262           strlen(lout)+strlen(ll)<MAX_LINELEN-2) {
01263            strcat(lout,",");strcat(lout,ll);
01264        }
01265     }
01266     n=cutitems(l2,cut,MAX_LIST); for(i=0;i<n;i++) {
01267        ll=cut[i];
01268        if(ll[0] && itemchr(lout,ll)==NULL &&
01269           strlen(lout)+strlen(ll)<MAX_LINELEN-2) {
01270            strcat(lout,",");strcat(lout,ll);
01271        }
01272     }
01273     strcpy(p,lout+1);
01274 }
01275 
01276        /* returns items of list2 not in list1 */
01277 void calc_listcomplement(char *p)
01278 {
01279     char l1[MAX_LINELEN+1],l2[MAX_LINELEN+1],lout[MAX_LINELEN+2];
01280     char *cut[MAX_LIST];
01281     char *pp, *ll;
01282     int i,n;
01283     
01284     pp=wordchr(p,"in");
01285     if(pp==NULL) module_error("syntax_error");
01286     *pp=0;strcpy(l1,p); strcpy(l2,pp+strlen("in"));
01287     lout[0]=lout[1]=0; substit(l1); substit(l2);
01288     n=cutitems(l2,cut,MAX_LIST); if(n<=0) {*p=0; return;}
01289     for(i=0;i<n;i++) {
01290        ll=cut[i];
01291        if(ll[0] && itemchr(l1,ll)==NULL && itemchr(lout,ll)==NULL &&
01292           strlen(lout)+strlen(ll)<MAX_LINELEN-2) {
01293            strcat(lout,",");strcat(lout,ll);
01294        }
01295     }
01296     strcpy(p,lout+1);
01297 }
01298 
01299        /* Consult a dictionary to translate a string */
01300 /*
01301 void calc_dictionary(char *p)
01302 {
01303     char *pp;
01304     char name[MAX_LINELEN+1], str[MAX_LINELEN+1];
01305     int t;
01306     pp=wordchr(p,"for");
01307     if(pp==NULL || pp<=p) module_error("syntax_error");
01308     *(find_word_end(pp-1))=0; 
01309     strcpy(name,p); substit(name);
01310     pp=find_word_start(pp+strlen("for")); t=0;
01311     if(memcmp(pp,"word",strlen("word"))==0 && 
01312        (isspace(*(pp+strlen("word"))) || 
01313        (*(pp+strlen("word"))=='s' && isspace(*(pp+strlen("words")))))) {
01314        pp=find_word_start(pp+strlen("words")); t=1;
01315     }
01316     if(memcmp(pp,"line",strlen("line"))==0 && 
01317        (isspace(*(pp+strlen("line"))) || 
01318        (*(pp+strlen("line"))=='s' && isspace(*(pp+strlen("lines")))))) {
01319        pp=find_word_start(pp+strlen("lines")); t=1;
01320     }
01321     if(memcmp(pp,"list",strlen("list"))==0 && isspace(*(pp+strlen("list")))) {
01322        pp=find_word_start(pp+strlen("list"));
01323     }
01324     if(memcmp(pp,"items",strlen("items"))==0 && isspace(*(pp+strlen("items")))) {
01325        pp=find_word_start(pp+strlen("items"));
01326     }
01327     if(memcmp(pp,"item",strlen("item"))==0 && isspace(*(pp+strlen("item")))) {
01328        pp=find_word_start(pp+strlen("item"));
01329     }
01330     strcpy(str,pp); substit(str);
01331     switch(t) {
01332        case 1: {
01333            calc_words2items(str); break;
01334        }
01335        case 2: {
01336            calc_lines2items(str); break;
01337        }
01338        default: break;
01339     }
01340 }
01341 */
01342 
01343 void calc_module(char *p)
01344 {
01345     char *p1,*p2, ind_buf[MAX_LINELEN+1], buf[MAX_FNAME+1];
01346     char *tp;
01347     
01348     p1=find_word_start(p); p2=find_word_end(p1);
01349     if(*p2!=0) *p2++=0;
01350     p2=find_word_start(p2); *find_word_end(p2)=0;
01351     if(*p1==0) {empty: *p=0; return;}
01352     if(*p2==0) {
01353        snprintf(buf,sizeof(buf),"module_%s",p1);
01354        p1=getvar(buf); if(p1==NULL) p1="";
01355        mystrncpy(p,p1,MAX_LINELEN); return;
01356     }
01357     mkfname(buf,"%s/%s/INDEX",module_dir,p2);
01358     tp=readfile(buf,ind_buf,MAX_LINELEN);
01359     if(tp==NULL) {
01360        mkfname(buf,"%s/%s/index",module_dir,p2);
01361        tp=readfile(buf,ind_buf,MAX_LINELEN);
01362     }
01363     if(tp==NULL && p2[strlen(p2)-3]!='.') {
01364        mkfname(buf,"%s/%s.%s/INDEX",module_dir,p2,lang);
01365        tp=readfile(buf,ind_buf,MAX_LINELEN); if(tp==NULL) {
01366            mkfname(buf,"%s/%s.%s/index",module_dir,p2,lang);
01367            tp=readfile(buf,ind_buf,MAX_LINELEN);
01368        }
01369        if(tp==NULL) {
01370            int i;
01371            for(i=0;i<available_lang_no;i++) {
01372               mkfname(buf,"%s/%s.%s/INDEX",module_dir,p2,available_lang[i]);
01373               tp=readfile(buf,ind_buf,MAX_LINELEN); if(tp) break;
01374               mkfname(buf,"%s/%s.%s/index",module_dir,p2,
01375                       available_lang[i]);
01376               tp=readfile(buf,ind_buf,MAX_LINELEN); if(tp) break;
01377            }
01378        }
01379     }
01380     if(tp==NULL) goto empty;       /* module not found */
01381     _getdef(ind_buf,p1,p);
01382 }
01383 
01384        /* strip enclosing parentheses */
01385 void calc_declosing(char *p)
01386 {
01387     strip_enclosing_par(p);
01388 }
01389 
01390        /* remove html tag, very rudimentary. */
01391 void calc_detag(char *p)
01392 {
01393     char *p1, *p2;
01394     for(p1=strchr(p,'<'); p1!=NULL; p1=strchr(p1,'<')) {
01395        p2=strchr(p1,'>'); if(p2==NULL) p1++;
01396        else strcpy(p1,p2+1);
01397     }
01398 }
01399 
01400        /* prepare a string to be inserted into a form input 
01401         * or textarea as is */
01402 void calc_reinput(char *p)
01403 {
01404     char *p1;
01405     for(p1=strchr(p,'&'); p1!=NULL; p1=strchr(p1,'&')) {
01406        p1++; string_modify(p,p1,p1,"amp;");
01407     }
01408     for(p1=strchr(p,'<'); p1!=NULL; p1=strchr(++p1,'<'))
01409       string_modify(p,p1,p1+1,"&lt;");
01410 }
01411 
01412        /* get a definition from a file. Trusted modules only. */
01413 void calc_defof(char *p)
01414 {
01415     char *p1;
01416     char fbuf[MAX_FNAME+1], nbuf[MAX_LINELEN+1], tbuf[MAX_LINELEN+1];
01417     
01418     secure_exec();
01419     p1=wordchr(p,"in"); if(p1==NULL) module_error("syntax_error");
01420     *p1=0; p1=find_word_start(p1+strlen("in"));
01421     mystrncpy(nbuf,p,sizeof(nbuf));  mystrncpy(tbuf,p1,sizeof(tbuf));
01422     substit(nbuf); substit(tbuf);
01423     p1=find_word_start(tbuf); *find_word_end(p1)=0;
01424     if(find_module_file(p1,fbuf,0)) {*p=0; return;}
01425     p1=find_word_start(nbuf); strip_trailing_spaces(p1);
01426     getdef(fbuf,p1,p);
01427 }
01428 
01429        /* check host */
01430 void calc_checkhost(char *p)
01431 {
01432     int t;
01433     if(robot_access || !trusted_module()) strcpy(p,"0");
01434     else {
01435        t=checkhost(p); mystrncpy(p,int2str(t),MAX_LINELEN);
01436     }
01437 }
01438 
01439 #define MAX_COLUMNS 256
01440 
01441        /* A rudimentary database facility */
01442 void calc_select(char *p)
01443 {
01444     char *p1, *p2, *p3, *bufp, c, sep;
01445     char buf1[MAX_LINELEN+1], buf2[MAX_LINELEN+1];
01446     char buf[MAX_LINELEN+1];
01447     int i,j,lmax;
01448     p2=wordchr(p,"where"); if(p2==NULL) module_error("syntax_error");
01449     *p2=0; p2+=strlen("where");
01450     p2=find_word_start(p2); strip_trailing_spaces(p2);
01451     p1=find_word_start(p) ; strip_trailing_spaces(p1);
01452     if(*p1==0 || *p2==0) module_error("syntax_error");
01453     strcpy(buf1,p1); strcpy(buf2,p2); sep='\n';
01454        /* buf1: data; buf2: condition. */
01455     substit(buf1); 
01456     if(strstr(buf2,"column")!=NULL) {     /* matrix */
01457        lmax=0; if(rows2lines(buf1)) sep=';';
01458        for(p1=strstr(buf2,"column"); p1; p1=strstr(p1+1,"column")) {
01459            if(p1>buf2 && isalnum(*(p1-1))) continue;
01460            p2=find_word_start(p1+strlen("column"));
01461            for(p3=p2; myisdigit(*p3); p3++);
01462            if(p3==p2) continue;
01463            c=*p3; *p3=0; i=atoi(p2); *p3=c;
01464            if(i<=0 || i>MAX_COLUMNS) continue;
01465            if(i>lmax) lmax=i;
01466            string_modify(buf2,p1,p3,"$(wims_select_col_%d)",i);
01467        }
01468        buf[0]=0; bufp=buf;
01469        for(p1=buf1; *p1; p1=p2) {
01470            char ibuf[MAX_LINELEN+1], nbuf[MAX_NAMELEN+1];
01471            p2=strchr(p1,'\n');
01472            if(p2==NULL) p2=p1+strlen(p1); else *p2++=0;
01473            if(*find_word_start(p1)==0) continue;
01474            for(j=1;j<=lmax;j++) {
01475               snprintf(nbuf,sizeof(nbuf),"wims_select_col_%d",j);
01476               fnd_item(p1,j,ibuf); force_setvar(nbuf,ibuf);
01477            }
01478            strcpy(ibuf,buf2); if(compare(ibuf,0,0)) {
01479               snprintf(bufp,MAX_LINELEN-(bufp-buf),"%s%c",p1,sep);
01480               bufp+=strlen(bufp);
01481            }
01482        }
01483     }
01484     else {    /* datafile */
01485        module_error("syntax_error");
01486        
01487     }
01488     if(buf[0]!=0) buf[strlen(buf)-1]=0;
01489     mystrncpy(p,buf,MAX_LINELEN);
01490 }
01491 
01492        /* Extract a column from a matrix */
01493 void calc_columnof(char *p)
01494 {
01495     char *p1, *p2, *bufp, sep;
01496     char buf1[MAX_LINELEN+1], buf2[MAX_LINELEN+1];
01497     char buf[MAX_LINELEN+1];
01498     p2=wordchr(p,"of"); if(p2==NULL) module_error("syntax_error");
01499     *p2=0; p2+=strlen("of");
01500     p2=find_word_start(p2); strip_trailing_spaces(p2);
01501     p1=find_word_start(p) ; strip_trailing_spaces(p1);
01502     if(*p1==0) module_error("syntax_error");
01503     if(*p2==0) {*p=0; return;}
01504     strcpy(buf1,p1); strcpy(buf2,p2); sep='\n';
01505        /* buf1: number(s); buf2: matrix. */
01506     substit(buf1); substit(buf2); 
01507     if(rows2lines(buf2)) sep=';';
01508     if(strchr(buf1,',')==NULL && wordchr(buf1,"to")==NULL
01509        && strstr(buf1,"..")==NULL) sep=',';
01510     buf[0]=0; bufp=buf;
01511     for(p1=buf2; *p1; p1=p2) {
01512        char ibuf[MAX_LINELEN+1];
01513        p2=strchr(p1,'\n');
01514        if(p2==NULL) p2=p1+strlen(p1); else *p2++=0;
01515        snprintf(ibuf,sizeof(ibuf),"%s of %s",buf1,p1);
01516        calc_itemof(ibuf);
01517        snprintf(bufp,MAX_LINELEN-(bufp-buf),"%s%c",ibuf,sep);
01518        bufp+=strlen(bufp);
01519     }
01520     if(buf[0]!=0) buf[strlen(buf)-1]=0;
01521     mystrncpy(p,buf,MAX_LINELEN);
01522 }
01523 
01524        /* find roots of a function or equation in a given zone */
01525 void calc_solve(char *p)
01526 {
01527     char *pp, *fp, *forp;
01528     char buf[MAX_LINELEN+1], vbuf[MAX_LINELEN+1];
01529     double v, dd, start, stop, step, old, v1, v2, v3, d1, d2, d3;
01530     int i, pos;
01531     
01532     forp=wordchr(p,"for");
01533     if(forp==NULL) { syntax: module_error("syntax_error"); *p=0; return; }
01534     *forp=0; forp+=strlen("for");
01535     fp=find_word_start(p); strip_trailing_spaces(fp);
01536     if(*fp==0) goto syntax;
01537     i=cutfor(forp,NULL); if(i<0 || forstruct.type==for_in) goto syntax;
01538     if(i>0) {*p=0; return;}
01539     start=forstruct.from; stop=forstruct.to; forp=forstruct.var;
01540     mystrncpy(buf,fp,sizeof(buf)); substitute(buf);
01541     *p=0; pp=strchr(buf,'='); if(pp!=NULL) {
01542        if(strlen(buf)>=MAX_LINELEN-16) return;
01543        strcat(buf,")");
01544        string_modify(buf,pp,pp+1,"-(");
01545     }
01546     i=0; for(fp=varchr(buf,forp); fp!=NULL; fp=varchr(fp,forp)) {
01547        string_modify(buf,fp,fp+strlen(forp),EV_X); fp+=strlen(EV_X); i++;
01548     }
01549     if(i==0 || start==stop) return;
01550     evalue_compile(buf); pos=eval_getpos(EV_X);
01551     if(start>stop) {dd=start; start=stop; stop=dd;}
01552     step=(stop-start)/100; if(step==0) return;
01553     pp=p; old=0;
01554     for(v=start; v<=stop; v+=step, old=dd) {
01555        eval_setval(pos,v); 
01556        set_evalue_error(0); set_evalue_pointer(buf); dd=_evalue(10);
01557        if(v==start) continue;
01558        if(!finite(old) || !finite(dd) || (old>0 && dd>0) || (old<0 && dd<0))
01559          continue;
01560        if(dd==0 && v<stop) continue;
01561        v1=v-step; v2=v; d1=old; d2=dd;
01562        for(i=0;i<30;i++) {
01563            v3=(v1+v2)/2; eval_setval(pos,v3);
01564            set_evalue_error(0); set_evalue_pointer(buf); d3=_evalue(10);
01565            if(!finite(d3)) goto next;
01566            if((d1>0 && d3>0) || (d1<0 && d3<0)) {d1=d3; v1=v3;}
01567            else {d2=d3; v2=v3;}
01568        }
01569        float2str(v3,vbuf); if(pp-p+strlen(vbuf)<MAX_LINELEN-1) {
01570            if(pp>p) *pp++=','; strcpy(pp,vbuf);
01571            pp+=strlen(pp);
01572        }
01573        else break;
01574        next: ;
01575     }
01576 }
01577 
01578        /* type: 1=values, 2=sum, 3=product, 4=recursion, 5=subst */
01579 void _values(char *p, int type)
01580 {
01581     char *pp, *fp, *forp;
01582     char vbuf[MAX_LINELEN+1], buf[MAX_LINELEN+1], fbuf[MAX_LINELEN+1], tbuf[MAX_LINELEN+1];
01583     char *f[64];
01584     double v, dd, start, stop, step, v0;
01585     int i, k, fcnt, pos, posr;
01586     
01587     forp=wordchr(p,"for");
01588     if(forp==NULL) { syntax: module_error("syntax_error"); *p=0; return; }
01589     *forp=0; forp+=strlen("for"); forp=find_word_start(forp);
01590     fp=find_word_start(p); strip_trailing_spaces(fp);
01591     if(*fp==0) goto syntax;
01592     if(type<5) i=cutfor(forp,NULL); else i=cutfor(forp,tbuf);
01593     if(i<0) goto syntax; if(i>0) {*p=0; return;}
01594     start=forstruct.from; stop=forstruct.to; step=forstruct.step;
01595     forp=forstruct.var;
01596     mystrncpy(buf,fp,sizeof(buf)); substitute(buf);
01597     for(fp=varchr(buf,forp); fp!=NULL; fp=varchr(fp,forp)) {
01598        string_modify(buf,fp,fp+strlen(forp),EV_X); fp+=strlen(EV_X);
01599     }
01600     for(fp=varchr(buf,"last"); fp!=NULL; fp=varchr(fp,"last")) {
01601        string_modify(buf,fp,fp+strlen("last"),EV_S); fp+=strlen(EV_S);
01602     }
01603     fcnt=pos=posr=0;
01604     if(type==5) goto skip;
01605     fcnt=itemnum(buf); if(fcnt>64) fcnt=64; pp=fbuf;
01606     for(k=0; k<fcnt; k++) {
01607        fnd_item(buf,k+1,vbuf); evalue_compile(vbuf);
01608        if(pp-fbuf+strlen(vbuf)<MAX_LINELEN-1) {
01609            f[k]=pp; strcpy(pp,vbuf); pp+=strlen(pp)+1;
01610        }
01611        else f[k]="";
01612     }
01613     pos=eval_getpos(EV_X); posr=eval_getpos(EV_S);
01614     skip:
01615     if(step==0) step=1;/* if(step<0) step=-step;
01616     if(stop<start) {dd=start; start=stop; stop=dd;} */
01617     *p=0; v0=0;
01618     switch(type) {
01619        case 4:
01620        case 1: {
01621            pp=getvar("recursion_start");
01622            if(pp==NULL || *pp==0) v0=0;
01623            else {
01624               v0=evalue(pp); if(!finite(v0)) return;
01625            }
01626            break;
01627        }
01628        case 2: v0=0; break;
01629        case 3: v0=1; break;
01630        case 5: break;
01631     }
01632     pp=p;
01633     if(type==5) {
01634        char *ps, *pt, buf2[MAX_LINELEN+1];
01635        int l,ln;
01636        *p=0; l=strlen(buf); if(l>=MAX_LINELEN) return;
01637        for(i=0,v=start; i<MAX_VALUE_LIST && v*step<=stop*step; v+=step, i++) {
01638            if(forstruct.type==for_from) {
01639               float2str(v,vbuf); ps=vbuf;
01640            }
01641            else ps=forstruct.pos[i];
01642            strcpy(buf2,buf); l=strlen(ps);ln=strlen(EV_X);
01643            for(pt=varchr(buf2,EV_X);pt!=NULL;pt=varchr(pt+l,EV_X)) 
01644              string_modify(buf2,pt,pt+ln,"%s",ps);
01645            if(pp-p+strlen(buf2)>=MAX_LINELEN-1) return;
01646            if(pp>p) *pp++=','; strcpy(pp,buf2);
01647            pp+=strlen(pp);
01648        }
01649        return;
01650     }
01651     for(i=0,v=start; i<MAX_VALUE_LIST && v*step<=stop*step; v+=step, i++) {
01652        if(forstruct.type==for_from) eval_setval(pos,v);
01653        else eval_setval(pos,forstruct.list[i]);
01654        eval_setval(posr,v0);
01655        for(k=0; k<fcnt; k++) {
01656            set_evalue_error(0); set_evalue_pointer(f[k]); dd=_evalue(10);
01657            switch(type) {
01658               case 1: {     /* values */
01659                   float2str(dd,vbuf);
01660                   if(pp-p+strlen(vbuf)<MAX_LINELEN-1) {
01661                      if(pp>p) *pp++=','; strcpy(pp,vbuf);
01662                      pp+=strlen(pp);
01663                   }
01664                   v0=dd; break;
01665               }
01666               case 2: {     /* sum */
01667                   v0=v0+dd; break;
01668               }
01669               case 3: {     /* product */
01670                   v0=v0*dd; break;
01671               }
01672               case 4: {     /* recursion */
01673                   v0=dd; break;
01674               }
01675            }
01676        }
01677     }
01678     if(type!=1) float2str(v0,p);
01679 }
01680 
01681        /* cut a function into values */
01682 void calc_values(char *p)
01683 { _values(p,1); }
01684 
01685        /* compute sum */
01686 void calc_sum(char *p)
01687 { _values(p,2); }
01688 
01689        /* compute product */
01690 void calc_product(char *p)
01691 { _values(p,3); }
01692 
01693        /* simple recursion */
01694 void calc_recursion(char *p)
01695 { _values(p,4); }
01696 
01697        /* List substitution */
01698 void calc_makelist(char *p)
01699 { _values(p,5); }
01700 
01701        /* level curve data */
01702 void calc_leveldata(char *p)
01703 {
01704     leveldata ld;
01705     char *sizep, *rangep, *fp, *levelp, *stepp;
01706     char *pp,*p2,fbuf[MAX_LINELEN+1],buf[MAX_LINELEN+1];
01707     double d[4];
01708     int i;
01709     
01710     sizep=wordchr(p,"size");
01711     rangep=wordchr(p,"ranges");
01712     fp=wordchr(p,"function");
01713     levelp=wordchr(p,"levels");
01714     stepp=wordchr(p,"step");
01715     if(sizep==NULL || rangep==NULL || fp==NULL) {
01716        syntax: module_error("syntax_error"); *p=0; return;
01717     }
01718     *sizep=0; sizep+=strlen("size");
01719     *rangep=0; rangep+=strlen("ranges");
01720     *fp=0; fp+=strlen("function");
01721     if(levelp!=NULL) {*levelp=0; levelp+=strlen("levels");}
01722     else levelp="0";
01723     if(stepp!=NULL) {*stepp=0; stepp+=strlen("step");}
01724     else stepp="0";
01725     mystrncpy(fbuf,fp,sizeof(fbuf)); substitute(fbuf);
01726     ld.fn=fbuf;
01727     ld.xname="x"; ld.yname="y"; ld.grain=evalue(stepp);
01728     mystrncpy(buf,sizep,sizeof(buf)); substitute(buf);
01729     for(i=0,pp=buf;i<2;i++,pp=p2) {
01730        if(*pp==0) goto syntax;
01731        p2=find_item_end(pp); if(*p2) *p2++=0;
01732        d[i]=evalue(pp);
01733     }
01734     ld.xsize=d[0]; ld.ysize=d[1];
01735     mystrncpy(buf,rangep,sizeof(buf)); substitute(buf);
01736     for(i=0,pp=buf;i<4;i++,pp=p2) {
01737        if(*pp==0) goto syntax;
01738        p2=find_item_end(pp); if(*p2) *p2++=0;
01739        d[i]=evalue(pp);
01740     }
01741     ld.xrange[0]=d[0]; ld.xrange[1]=d[1]; ld.yrange[0]=d[3]; ld.yrange[1]=d[2];
01742     mystrncpy(buf,levelp,sizeof(buf)); substitute(buf);
01743     ld.levelcnt=itemnum(buf); if(ld.levelcnt>LEVEL_LIM) ld.levelcnt=LEVEL_LIM;
01744     for(i=0,pp=buf;i<ld.levelcnt;i++,pp=p2) {
01745        if(*pp==0) goto syntax;
01746        p2=find_item_end(pp); if(*p2) *p2++=0;
01747        ld.levels[i]=evalue(pp);
01748     }
01749     levelcurve(&ld);
01750     for(i=0, pp=p; i<ld.datacnt && pp<p+MAX_LINELEN-16; i++) {
01751        float2str(ld.xdata[i],buf);
01752        if(pp-p+strlen(buf)<MAX_LINELEN-1) {
01753            if(pp>p) *pp++=';'; strcpy(pp,buf); pp+=strlen(pp);
01754        }
01755        float2str(ld.ydata[i],buf);
01756        if(pp-p+strlen(buf)<MAX_LINELEN-1) {
01757            if(pp>p) *pp++=','; strcpy(pp,buf); pp+=strlen(pp);
01758        }
01759     }
01760 }
01761 
01762        /* internal routine with no security check */
01763 void _lookup(char *p, char *fname)
01764 {
01765     char buf1[MAX_LINELEN+1];
01766     char *p1, *p2, *mbuf;
01767 
01768     mbuf=readfile(fname,NULL,WORKFILE_LIMIT);
01769     if(mbuf==NULL) {abort: *p=0; return;}
01770     p1=find_word_start(p); strip_trailing_spaces(p1);
01771     snprintf(buf1,sizeof(buf1),"%s:",p1); substit(buf1);
01772     for(p1=strstr(mbuf,buf1);
01773        p1!=NULL && p1>mbuf && *(p1-1)!='\n';
01774        p1=strstr(p1+1,buf1));
01775     if(p1==NULL) {free(mbuf); goto abort;}
01776     p1+=strlen(buf1);
01777     for(p2=strchr(p1,'\n'); p2!=NULL; p2=strchr(p2+1,'\n')) {
01778        if(p2>p1 && *(p2-1)=='\\') {*(p2-1)=' '; continue;}
01779        else break;
01780     }
01781     if(p2==NULL) p2=p1+strlen(p1); else *p2=0;
01782     mystrncpy(p,p1,MAX_LINELEN); free(mbuf);
01783 }
01784 
01785        /* lookup a definition in a definition file */
01786 void calc_lookup(char *p)
01787 {
01788     char buf2[MAX_LINELEN+1], buf3[MAX_FNAME+1];
01789     char *p2;
01790     
01791     p2=wordchr(p,"in"); if(p2==NULL) {abort: *p=0; return;}
01792     *p2=0;p2=find_word_start(p2+2);
01793     mystrncpy(buf2,p2,sizeof(buf2)); substit(buf2);
01794     *find_word_end(buf2)=0;
01795     if(strstr(buf2,parent_dir_string)!=NULL) {
01796        force_setvar("wims_error_data",buf2); module_error("illegal_cmd");
01797     }
01798     if(strncmp(buf2,"bases/",strlen("bases/"))!=0) {
01799        if(datafile_check(buf2)!=0 || find_module_file(buf2,buf3,0)!=0)
01800          goto abort;
01801        _lookup(p,buf3);
01802     }
01803     else _lookup(p,buf2); 
01804 }
01805 
01806        /* Hide name of a file. Only in module directory or in gifs/ */
01807 void calc_rename(char *p)
01808 {
01809     char buf1[MAX_LINELEN+1], buf2[MAX_LINELEN+1];
01810     char *p1, *ext, *s;
01811     int i,t;
01812     
01813     if(robot_access || strstr(p,"getfile")!=NULL) return;
01814     p1=find_word_start(p); *find_word_end(p1)=0;
01815     if(strncmp(p1,ref_name,strlen(ref_name))==0) p1+=strlen(ref_name);
01816     if(p1>p) strcpy(p,p1);
01817     if(strstr(p,parent_dir_string)!=NULL ||
01818        strncmp(p,"modules/adm/",strlen("modules/adm/"))==0) {
01819        badfile: force_setvar("wims_error_data",p); module_error("illegal_cmd");
01820     }
01821     if(strncmp(p,module_dir,strlen(module_dir))!=0 &&
01822        strncmp(p,"modules/data/",strlen("modules/data/"))!=0 &&
01823        strncmp(p,"scripts/data/",strlen("scripts/data/"))!=0 &&
01824        strncmp(p,"gifs",strlen("gifs"))!=0) goto badfile;
01825     mkfname(buf1,"%s/getfile",session_prefix); mkdirs(buf1);
01826     mkfname(buf1,"%s/.rename",session_prefix);
01827     mystrncpy(buf2,p,sizeof(buf2)); _lookup(buf2,buf1);
01828     if(buf2[0]!=0) { /* already */
01829        mystrncpy(p,buf2,MAX_LINELEN); return;
01830     }
01831     if(cwdbuf[0]==0) return;
01832     p1=p+strlen(p)-1;
01833     while(p1>p && isalnum(*p1)) p1--;
01834     if(p1>p && *p1=='.') ext=p1; else ext="";
01835     rerand: t=random();
01836     mkfname(buf1,"%s/%s",cwdbuf,p);
01837     mkfname(buf2,"%s/getfile/rename-%u%s",session_prefix,t,ext);
01838     if(ftest(buf2)>=0) goto rerand;
01839     i=symlink(buf1,buf2);
01840     s=getvar("wims_session"); if(s==NULL) return;
01841     if(good_httpd) snprintf(buf1,sizeof(buf1),
01842                          "getfile/rename-%u%s?session=%s", t,ext,s);
01843     else snprintf(buf1,sizeof(buf1),"%s?cmd=getfile&+session=%s&+special_parm=rename-%u%s",
01844                 ref_name, s, t,ext);
01845     snprintf(buf2,sizeof(buf2),"%s:%s\n",p,buf1);
01846     accessfile(buf2,"a","%s/.rename",session_prefix);
01847     mystrncpy(p,buf1,MAX_LINELEN);
01848 }
01849 
01850        /* Pick up and translate imgrename(...) within a string */
01851 void calc_imgrename(char *p)
01852 {
01853     char buf[MAX_LINELEN+1], buf2[MAX_LINELEN+1];
01854     char *p1, *p2, *p3, *p4;
01855     
01856     for(p1=varchr(p,"imgrename"); p1!=NULL; p1=varchr(p1,"imgrename")) {
01857        p2=find_word_start(p1+strlen("imgrename"));
01858        if(*p2!='(') {p1=p2; continue;}
01859        p2++; p3=find_matching(p2,')');
01860        if(*p3!=')') {p1=p2-1; continue;}
01861        p2=find_word_start(p2); p4=find_word_end(p2);
01862        memmove(buf,p2,p4-p2); buf[p4-p2]=0;
01863        calc_rename(buf); *p3=0;
01864        snprintf(buf2,sizeof(buf2),"<img src=\"%s\"%s>",buf, p4);
01865        *p3=')'; p3++;
01866        string_modify(p,p1,p3,"%s",buf2);
01867        p1+=strlen(buf2);
01868     }
01869     
01870 }
01871 
01872        /* internal use only */
01873 void calc_unhttp(char *p)
01874 {
01875     _http2env(tmplbuf,p); mystrncpy(p,tmplbuf,MAX_LINELEN);
01876 }
01877 
01878 /*
01879 char *saltchar="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789./";
01880 */
01881 
01882 void calc_passcheck(char *p)
01883 {
01884     
01885 }
01886 
01887 void calc_passcrypt(char *p)
01888 {
01889 #ifdef HAVE_CRYPT
01890     char saltstr[4];
01891     char *p1, *p2, *pp, *s, buf[MAX_LINELEN+1];
01892     saltstr[0]='N'; saltstr[1]='v'; saltstr[2]=0; buf[0]=0; pp=buf;
01893     for(p1=find_word_start(p);*p1;p1=find_word_start(p2)) {
01894        p2=find_word_end(p1); if(*p2) *p2++=0;
01895        pp=pp+strlen(pp);
01896        if(pp>buf) s=" "; else s="";
01897        if(*p1=='*')
01898          snprintf(pp,MAX_LINELEN-(pp-buf),"%s%s",s,p1);
01899        else
01900          snprintf(pp,MAX_LINELEN-(pp-buf),"%s*%s",s,crypt(p1,saltstr));
01901     }
01902     strcpy(p,buf);
01903 #endif
01904 }
01905 
01906 void exec_readproc(char *p);
01907 
01908        /* crypted mail interface */
01909 void calc_mailurl(char *p)
01910 {
01911     char *p0, buf[MAX_LINELEN+1];
01912     
01913     if(robot_access) {*p=0; return;}
01914     snprintf(buf,sizeof(buf),"mailurl.proc %s",p);
01915     exec_readproc(buf);
01916     p0=getvar("mailurl_"); if(p0==NULL) p0="";
01917     mystrncpy(p,p0,MAX_LINELEN);
01918 }
01919 
01920        /* get option word in a string */
01921 void calc_getopt(char *p)
01922 {
01923     char *p1, *p2, *p3, *p4;
01924     char buf1[MAX_LINELEN+1], buf2[MAX_LINELEN+1];
01925     
01926     p1=wordchr(p,"in"); if(p1==NULL) module_error("syntax error");
01927     *p1=0; p1=find_word_start(p1+3);
01928     mystrncpy(buf1,p,MAX_LINELEN); mystrncpy(buf2,p1,MAX_LINELEN);
01929     substitute(buf1); substitute(buf2);
01930     p1=find_word_start(buf1); *find_word_end(p1)=0;
01931     for(p2=buf2;*p2;p2++) {
01932        if(myisspace(*p2)) *p2=' ';
01933        if(*p2=='=') *p2='   ';
01934     }
01935     *p=0;
01936     p2=wordchr(buf2,p1); if(p2==NULL) return;
01937     for(p3=find_word_end(p2);myisspace(*p3);p3++) {
01938        if(*p3=='     ') {
01939            p3=find_word_start(p3);
01940            switch(*p3) {
01941               case '"': {
01942                   p4=strchr(p3+1,'"');
01943                   goto tested;
01944               }
01945               case '(': {
01946                   p4=find_matching(p3+1,')');
01947                   goto tested;
01948               }
01949               case '[': {
01950                   p4=find_matching(p3+1,']');
01951                   goto tested;
01952               }
01953               case '{': {
01954                   p4=find_matching(p3+1,'}');
01955                   tested:
01956                   if(p4) {
01957                      p3++; *p4=0; break;
01958                   }
01959                   else goto nomatch;
01960               }
01961               default: {
01962                   nomatch: 
01963                   *find_word_end(p3)=0;
01964               }
01965            }
01966            mystrncpy(p,p3,MAX_LINELEN);
01967            return;
01968        }
01969     }
01970     *find_word_end(p2)=0;
01971     mystrncpy(p,p2,MAX_LINELEN);
01972 }
01973 
01974        /* internal */
01975 void _embraced(char buf[], void (app)(char *p))
01976 {
01977     char *p1, *p2, buf2[MAX_LINELEN+1];
01978     for(p1=strchr(buf,'{'); p1; p1=strchr(p1,'{')) {
01979        p2=find_matching(p1+1,'}');
01980        if(p2==NULL) module_error("unmatched_parentheses");
01981        *p2=0; mystrncpy(buf2,p1+1,sizeof(buf2)); app(buf2);
01982        string_modify(buf,p1,p2+1,buf2);
01983     }
01984 }
01985 
01986        /* embraced operations */
01987 void calc_embraced(char *p)
01988 {
01989     char *p1, *p2, buf[MAX_LINELEN+1];
01990 
01991     p1=find_word_start(p); p2=find_word_end(p1);
01992     if(*p2==0) {*p=0; return;}
01993     *p2++=0; p2=find_word_start(p2);
01994     mystrncpy(buf,p2,sizeof(buf)); substit(buf);
01995     if(p1>p) strcpy(p,p1); substit(p);
01996     if(strcmp(p,"randitem")==0) {_embraced(buf,calc_randitem); goto end;}
01997     if(strcmp(p,"extract")==0) {
01998        p1=strchr(buf,'{'); if(p1!=NULL) {
01999            p2=find_matching(++p1,'}');
02000            if(p2!=NULL) {
02001               memmove(buf,p1,p2-p1); buf[p2-p1]=0;
02002            }
02003            else buf[0]=0;
02004        }
02005        else buf[0]=0;
02006        goto end;
02007     }
02008     if(strcmp(p,"delete")==0) {
02009        for(p1=strchr(buf,'{'); p1; p1=strchr(p1,'{')) {
02010            p2=find_matching(p1+1,'}');
02011            if(p2) strcpy(p1,p2+1); else p1++;
02012        }
02013        goto end;
02014     }
02015     module_error("syntax_error");
02016     
02017     end:
02018     mystrncpy(p,buf,MAX_LINELEN);
02019 }
02020 
02021 void calc_rows2lines(char *p)
02022 {      rows2lines(p);       }
02023 
02024 void calc_lines2rows(char *p)
02025 {      lines2rows(p);       }
02026 
02027        /* check whether datamodules exist */
02028 void calc_checkdata(char *p)
02029 {
02030     char *p1, *p2, buf[MAX_LINELEN+1], nbuf[MAX_FNAME+1];
02031     struct stat st;
02032     
02033     memmove(p,"yes",4);
02034     p1=getvar("module_data");
02035     if(p1==NULL || *p1==0) return;
02036     snprintf(buf,sizeof(buf),"%s",p1);
02037     for(p2=buf; *p2; p2++) if(*p2==',' || *p2==';') *p2=' ';
02038     if(strstr(buf,"..")!=NULL) {
02039        snprintf(p,MAX_LINELEN,"badly_defined_data_module");
02040        return;
02041     }
02042     for(p1=find_word_start(buf); *p1; p1=find_word_start(p2)) {
02043        p2=find_word_end(p1); if(*p2) *p2++=0;
02044        snprintf(nbuf,sizeof(nbuf),"%s/%s/INDEX",module_dir,p1);
02045        if(stat(nbuf,&st)<0) {
02046            snprintf(nbuf,sizeof(nbuf),"%s/%s/index",module_dir,p1);
02047            if(stat(nbuf,&st)<0) {
02048               snprintf(p,MAX_LINELEN,"%s",p1);
02049               return;
02050            }
02051        }
02052     }
02053 }
02054 
02055 typedef struct {
02056     char *name;
02057     int tag;
02058     void (*routine) (char *p);
02059 } MYFUNCTION;
02060 
02061        /* tag!=0 if we don't want automatic substit(). */
02062 MYFUNCTION calc_routine[]={
02063       {"TeXmath",    0,     texmath},
02064       {"add",        1,     calc_sum},
02065       {"append",     1,     calc_append},
02066       {"call",              0,     calc_exec},
02067       {"char",              1,     calc_charof},
02068       {"charcnt",    0,      calc_lengthof},
02069       {"charcount",  0,      calc_lengthof},
02070       {"charno",     0,      calc_lengthof},
02071       {"charnum",    0,      calc_lengthof},
02072       {"chars",             1,     calc_charof},
02073       {"checkdata",  0,     calc_checkdata},
02074       {"checkdatamodule",0, calc_checkdata},
02075       {"checkhost",  0,     calc_checkhost},
02076       {"column",     1,     calc_columnof},
02077       {"columns",    1,     calc_columnof},
02078       {"daemon",     0,     calc_daemon},
02079       {"date",              0,     calc_date},
02080       {"deaccent",   0,     deaccent},
02081       {"debug",             0,     calc_debug},
02082       {"declosing",  0,     calc_declosing},
02083       {"definitionof",      1,     calc_defof},
02084       {"defof",             1,     calc_defof},
02085       {"detag",             0,     calc_detag},
02086 /*      {"dictionary",      1,     calc_dictionary},    */
02087       {"dir",        0,     calc_listfile},
02088       {"embraced",   1,     calc_embraced},
02089       {"encyclo",    0,     pedia},
02090       {"encyclopedia",      0,     pedia},
02091       {"eval",              0,     calc_evalue},
02092       {"evalsubst",  1,     calc_evalsubst},
02093       {"evalsubstit",       1,     calc_evalsubst},
02094       {"evalsubstitute",1,  calc_evalsubst},
02095       {"evalue",     0,     calc_evalue},
02096       {"evaluesubst",       1,     calc_evalsubst},
02097       {"evaluesubstit",     1,     calc_evalsubst},
02098       {"evaluesubstitute",1,       calc_evalsubst},
02099       {"examdep",    0,     calc_examdep},
02100       {"examscore",  0,     calc_examscore},
02101       {"exec",              0,     calc_exec},
02102       {"execute",    0,     calc_exec},
02103       {"filelist",   0,     calc_listfile},
02104       {"getdef",     1,     calc_defof},
02105       {"getopt",     1,     calc_getopt},
02106       {"getscore",   0,     calc_getscore},
02107       {"getscoremean",      0,     calc_getscoremean},
02108       {"getscorepercent",0, calc_getscorepercent},
02109       {"getscoreremain",0,  calc_getscoreremain},
02110       {"getscorerequire",0, calc_getscorerequire},
02111       {"getscorestatus",0,  calc_getscorestatus},
02112       {"getscoreweight",0,  calc_getscoreweight},
02113       {"hex",        0,     calc_hex},
02114       {"htmlmath",   0,     htmlmath},
02115       {"httpquery",  0,     tohttpquery},
02116       {"imgrename",  0,     calc_imgrename},
02117       {"instexst",   1,     calc_instexst},
02118       {"instexstatic",      1,     calc_instexst},
02119       {"item",              1,     calc_itemof},
02120       {"itemcnt",    0,      calc_itemnum},
02121       {"itemcount",  0,      calc_itemnum},
02122       {"itemno",     0,      calc_itemnum},
02123       {"itemnum",    0,      calc_itemnum},
02124       {"items",             1,     calc_itemof},
02125       {"items2lines",       0,      items2lines},
02126       {"items2words",       0,      items2words},
02127       {"itemstolines",      0,      items2lines},
02128       {"itemstowords",      0,      items2words},
02129       {"lengthof",   0,      calc_lengthof},
02130       {"leveldata",  1,     calc_leveldata},
02131       {"levelpoints",       1,     calc_leveldata},
02132       {"line",              1,     calc_lineof},
02133       {"linecnt",    0,      calc_linenum},
02134       {"linecount",  0,      calc_linenum},
02135       {"lineno",     0,      calc_linenum},
02136       {"linenum",    0,      calc_linenum},
02137       {"lines",             1,     calc_lineof},
02138       {"lines2items",       0,      lines2items},
02139       {"lines2list", 0,      lines2items},
02140       {"lines2rows", 0,      calc_lines2rows},
02141       {"lines2words",       0,      lines2words},
02142       {"linestoitems",      0,      lines2items},
02143       {"linestolist",       0,      lines2items},
02144       {"linestowords",      0,      lines2words},
02145       {"list2lines", 0,      items2lines},
02146       {"list2words", 0,      items2words},
02147       {"listcomplement",1,  calc_listcomplement},
02148       {"listfile",   0,     calc_listfile},
02149       {"listfiles",  0,     calc_listfile},
02150       {"listintersect",     1,     calc_listintersect},
02151       {"listintersection",1,       calc_listintersect},
02152       {"listtolines",       0,      items2lines},
02153       {"listtowords",       0,      items2words},
02154       {"listunion",  1,     calc_listunion},
02155       {"listuniq",   0,     calc_listuniq},
02156       {"listunique", 0,     calc_listuniq},
02157       {"listvar",    0,     mathvarlist},
02158       {"lookup",     1,     calc_lookup},
02159       {"lower",             0,     calc_tolower},
02160       {"lowercase",  0,     calc_tolower},
02161       {"ls",         0,     calc_listfile},
02162       {"mailurl",    0,     calc_mailurl},
02163       {"makelist",   1,     calc_makelist},
02164       {"mathsubst",  1,     calc_mathsubst},
02165       {"mathsubstit",       1,     calc_mathsubst},
02166       {"mathsubstitute",1,  calc_mathsubst},
02167       {"mexec",             0,     calc_mexec},
02168       {"module",     0,     calc_module},
02169       {"multiply",   1,     calc_product},
02170       {"non_empty",  0,     calc_nonempty},
02171       {"nonempty",   0,     calc_nonempty},
02172       {"nospace",    0,     nospace},
02173       {"nosubst",    1,     calc_subst},
02174       {"nosubstit",  1,     calc_subst},
02175       {"nosubstitute",      1,     calc_subst},
02176       {"passcheck",  0,     calc_passcheck},
02177       {"passcrypt",  0,     calc_passcrypt},
02178       {"pedia",             0,     pedia},
02179       {"perl",              0,     calc_perl},
02180       {"position",   1,     calc_pos},
02181       {"positionof", 1,     calc_pos},
02182       {"positions",  1,     calc_pos},
02183       {"prod",              1,     calc_product},
02184       {"product",    1,     calc_product},
02185       {"randchar",   0,     calc_randchar},
02186       {"randdouble", 0,     calc_randdouble},
02187       {"randfile",   0,     calc_randfile},
02188       {"randfloat",  0,     calc_randdouble},
02189       {"randint",    0,     calc_randint},
02190       {"randitem",   0,     calc_randitem},
02191       {"randline",   0,     calc_randline},
02192       {"random",     0,     calc_randdouble},
02193       {"randperm",   0,     calc_randperm},
02194       {"randpermute",       0,     calc_randperm},
02195       {"randreal",   0,     calc_randdouble},
02196       {"randrecord", 0,     calc_randfile},
02197       {"randrow",    0,     calc_randrow},
02198       {"randword",   0,     calc_randword},
02199       {"rawmath",    0,     rawmath},
02200       {"rawmatrix",  0,     rawmatrix},
02201       {"reaccent",   0,     reaccent},
02202       {"record",     1,     calc_recordof},
02203       {"recordcnt",  0,     calc_recordnum},
02204       {"recordcount",       0,     calc_recordnum},
02205       {"recordno",   0,     calc_recordnum},
02206       {"recordnum",  0,     calc_recordnum},
02207       {"records",    1,     calc_recordof},
02208       {"recursion",  1,     calc_recursion},
02209       {"reinput",    0,     calc_reinput},
02210       {"rename",     0,     calc_rename},
02211       {"replace",    1,     calc_replace},
02212       {"rootof",     1,     calc_solve},
02213       {"row",        1,     calc_rowof},
02214       {"rowcnt",     0,      calc_rownum},
02215       {"rowcount",   0,      calc_rownum},
02216       {"rowno",             0,      calc_rownum},
02217       {"rownum",     0,      calc_rownum},
02218       {"rows",              1,     calc_rowof},
02219       {"rows2lines", 0,     calc_rows2lines},
02220       {"run",               0,     calc_exec},
02221       {"select",     1,     calc_select},
02222       {"sh",         0,     calc_sh},
02223       {"shuffle",    0,     calc_randperm},
02224       {"singlespace",       0,     singlespace},
02225       {"slashsubst", 0,     slashsubst},
02226       {"solve",             1,     calc_solve},
02227       {"sort",              1,     calc_sort},
02228 /*      {"sql",             0,     calc_sql}, */
02229       {"staticinstex",      1,     calc_instexst},
02230       {"stinstex",   1,     calc_instexst},
02231       {"subst",             0,     calc_subst},
02232       {"substit",    0,     calc_subst},
02233       {"substitute", 0,     calc_subst},
02234       {"sum",        1,     calc_sum},
02235       {"system",     0,     calc_sh},
02236       {"texmath",    0,     texmath},
02237       {"text",              1,     text},
02238       {"tohex",             0,     calc_hex},
02239       {"tolower",    0,     calc_tolower},
02240       {"toupper",    0,     calc_toupper},
02241       {"translate",  1,     calc_translate},
02242       {"trim",              0,     calc_trim},
02243       {"unhttp",     0,     calc_unhttp},
02244       {"upper",             0,     calc_toupper},
02245       {"uppercase",  0,     calc_toupper},
02246       {"values",     1,     calc_values},
02247       {"varlist",    0,     mathvarlist},
02248       {"word",              1,      calc_wordof},
02249       {"wordcnt",    0,      calc_wordnum},
02250       {"wordcount",  0,      calc_wordnum},
02251       {"wordno",     0,      calc_wordnum},
02252       {"wordnum",    0,      calc_wordnum},
02253       {"words",             1,      calc_wordof},
02254       {"words2items",       0,      words2items},
02255       {"words2lines",       0,      words2lines},
02256       {"words2list", 0,      words2items},
02257       {"wordstoitems",      0,      words2items},
02258       {"wordstolines",      0,      words2lines},
02259       {"wordstolist",       0,      words2items}
02260 };
02261 #define CALC_FN_NO (sizeof(calc_routine)/sizeof(calc_routine[0]))
02262