Subversion Repositories wimsdev

Rev

Rev 382 | 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.         /* This is part of wims source.
  19.          * This file does computations and output. */
  20.  
  21. int _sort_numeric, _sort_nocase;
  22. typedef struct SORT_STRUCT {char *str; double val; int serial;} SORT_STRUCT;
  23. struct SORT_STRUCT sort_sep[MAX_SORT_ITEM+1];
  24.  
  25. void secure_exec(void)
  26. {
  27.     if((untrust&6)==0) return;
  28.     module_error("Illegal_command");
  29. }
  30.  
  31.         /* internal comparers */
  32. int __sorter(const void *p1, const void *p2)
  33. {
  34.     struct SORT_STRUCT *pp1, *pp2;
  35.    
  36.     pp1=(struct SORT_STRUCT *) p1; pp2=(struct SORT_STRUCT *) p2;
  37.     if(_sort_numeric) {
  38.         double dd=(pp1->val)-(pp2->val);
  39.         if(dd>0) return 1;
  40.         if(dd<0) return -1;
  41.         return 0;
  42.     }
  43.     if(_sort_nocase) return strcasecmp(pp1->str,pp2->str);
  44.     else return strcmp(pp1->str,pp2->str);
  45. }
  46.  
  47. int _char_sorter(const void *c1, const void *c2)
  48. {
  49.     if(_sort_nocase) return tolower(*(char *)c1)-tolower(*(char *)c2);
  50.     else return *(char *)c1-*(char *)c2;
  51. }
  52.  
  53. enum {sort_char, sort_word, sort_item, sort_line, sort_row,
  54.       sort_numeric, sort_nocase, sort_reverse
  55. };
  56. struct {char *name; int type;
  57. } sort_keyw[]={
  58.             {"char",sort_char},
  59.           {"chars",sort_char},
  60.           {"character",sort_char},
  61.           {"characters",sort_char},
  62.           {"word",sort_word},
  63.           {"words",sort_word},
  64.           {"item",sort_item},
  65.           {"items",sort_item},
  66.           {"line",sort_line},
  67.           {"lines",sort_line},
  68.           {"list",sort_item},
  69.           {"numeric",sort_numeric},
  70.           {"numerical",sort_numeric},
  71.           {"nocase",sort_nocase},
  72.           {"ignorecase",sort_nocase},
  73.           {"reverse",sort_reverse},
  74.           {"row",sort_row},
  75.           {"rows",sort_row}
  76. };
  77. #define sort_keyw_no (sizeof(sort_keyw)/sizeof(sort_keyw[0]))
  78.  
  79.         /* string sort */
  80. void calc_sort(char *p)
  81. {
  82.     char *p1, *p2, buf[MAX_LINELEN+1], *csep[MAX_SORT_ITEM+1];
  83.     char fs=0;
  84.     int (*cutfn)(char *p,char *list[],int max);
  85.     int nocase=0, reverse=0, numeric=0, type;
  86.     int i,j,l,t,total;
  87.    
  88.     cutfn=NULL;
  89.     for(i=0,p1=find_word_start(p);i>=0;p1=find_word_start(p2)) {
  90.         p2=find_word_end(p1); if(*p2!=0) *p2++=0;
  91.         for(i=0;i<sort_keyw_no && strcasecmp(p1,sort_keyw[i].name)!=0;i++);
  92.         if(i>=sort_keyw_no) module_error("syntax_error");
  93.         switch(type=sort_keyw[i].type) {
  94.             case sort_nocase: nocase=1; break;
  95.             case sort_reverse: reverse=1; break;
  96.             case sort_numeric: numeric=1; break;
  97.             case sort_char: i=-1; break;
  98.             case sort_word: cutfn=cutwords; fs=' '; i=-1; break;
  99.             case sort_item: cutfn=cutitems; fs=','; i=-1; break;
  100.             case sort_row:
  101.             case sort_line: cutfn=cutlines; fs='\n'; i=-1; break;
  102.         }
  103.     }
  104.     if(p1[0]=='o' && p1[1]=='f' && myisspace(p1[2])) p1=find_word_start(p1+2);
  105.     mystrncpy(buf,p1,sizeof(buf)); substit(buf); *p=0;
  106.     t=0; if(type==sort_row) t=rows2lines(buf);
  107.     _sort_nocase=nocase; _sort_numeric=numeric;
  108.     if(cutfn) {
  109.         char ordbuf[MAX_LINELEN+1];
  110.         total=cutfn(buf,csep,MAX_SORT_ITEM+1);
  111.         if(total<=0 || total>MAX_SORT_ITEM) return;
  112.         for(i=0;i<total;i++) {
  113.             sort_sep[i].str=csep[i];
  114.             if(numeric) sort_sep[i].val=evalue(csep[i]);
  115.         }
  116.         for(i=0;i<total;i++) sort_sep[i].serial=i+1;
  117.         qsort(sort_sep,total,sizeof(sort_sep[0]),__sorter);
  118.         p1=p;p2=ordbuf;ordbuf[0]=0;
  119.         for(i=0;i<total;i++) {
  120.             if(reverse) j=total-1-i; else j=i;
  121.             l=strlen(sort_sep[j].str);
  122.             if(p1-p+l>=MAX_LINELEN-1) module_error("cmd_output_too_long");
  123.             if(p1>p) *p1++=fs;
  124.             memmove(p1,sort_sep[j].str,l+1); p1+=l;
  125.             if(i>0) *p2++=',';
  126.             mystrncpy(p2,int2str(sort_sep[j].serial),
  127.                       MAX_LINELEN-(p2-ordbuf)-1);
  128.             p2+=strlen(p2);
  129.         }
  130.         force_setvar("wims_sort_order",ordbuf);
  131.     }
  132.     else { /* case of chars */
  133.         qsort(buf,strlen(buf),1,_char_sorter);
  134.         total=strlen(buf);
  135.         if(reverse) {
  136.             for(i=total-1;i>=0;i--) *(p+total-i-1)=buf[i];
  137.             *(p+total)=0;
  138.         }
  139.         else memmove(p,buf,total+1);
  140.     }
  141.     if(t) lines2rows(p);
  142. }
  143.  
  144.         /* Print a debug output */
  145. void calc_debug(char *p)
  146. {
  147.     secure_exec(); setvar("debug",p); module_error("debug");
  148. }
  149.  
  150.         /* execute a command thru wimslogd */
  151. void calc_daemon(char *p)
  152. {
  153.     if(!trusted_module() || is_class_module) {*p=0; return;}
  154.     _daemoncmd(p);
  155. }
  156.  
  157. void _get_exec_error(char *errorfname, char *cmdname)
  158. {
  159.     char *p2;
  160.     char obuf[MAX_LINELEN+1];
  161.     int l;
  162.     if(errorfname) accessfile(obuf,"e","%s",errorfname);
  163.     else {
  164.         l=read(mxtab[multiexec_index].pipe_stderr[0],obuf,MAX_LINELEN/4);
  165.         if(l<0 || l>MAX_LINELEN) l=0;
  166.         obuf[l]=0;
  167.     }
  168.     if(obuf[0]) {
  169.         p2=find_word_end(obuf);
  170.         if((manageable<1 || strcasecmp(tmp_debug,"yes")!=0) &&
  171.            !trusted_module() &&
  172.            strncmp(p2," not_INStalled",strlen(" not_INStalled"))==0) {
  173.             *p2=0; setvar("missing_software",obuf);
  174.             snprintf(p2+1,MAX_LINELEN-strlen(obuf)-8,"missing_%s",obuf);
  175.             if(!outputing) user_error(p2+1);
  176.         }
  177.         p2=getvar("wims_exec_error"); if(p2==NULL) p2="";
  178.         snprintf(tmplbuf,sizeof(tmplbuf),"%s\nERROR from %s:\n%s",p2,cmdname,obuf);
  179.         force_setvar("wims_exec_error",tmplbuf);
  180.     }
  181. }
  182.  
  183.         /* execute an external program */
  184.         /* The output of the external program should be put into
  185.          * a file session_directory/session/cmd.tmp. */
  186. void calc_exec(char *p)
  187. {
  188.     int i,j,k;
  189.     char *cmd, *parm, *pp;
  190.     char namebuf[MAX_EXEC_NAME+1], cmdstr[MAX_LINELEN+256], obuf[MAX_LINELEN+1];
  191.     char outfname[MAX_FNAME+1], errorfname[MAX_FNAME+1],
  192.       typefname[MAX_FNAME+1], varfname[MAX_FNAME+1];
  193.     char *abuf[2]={NULL,NULL};
  194.     WORKING_FILE wf;
  195.     if(robot_access || time(0)>=limtimex-1) {
  196.         *p=0;return;
  197.     }
  198.     cmd=find_word_start(p); if(*cmd==0) return; /* No command. */
  199.     parm=find_word_end(cmd);j=parm-cmd;parm=find_word_start(parm);
  200.     if(j>MAX_EXEC_NAME) {
  201.         wrong_command:
  202.         setvar(error_data_string,namebuf); module_error("bad_cmd");
  203.         *p=0; return; /* should not occur */
  204.     }
  205.     memmove(namebuf,cmd,j); namebuf[j]=0;
  206.         /* Specifying parent directory in command name is of course
  207.          * prohibited.
  208.          * The module developper cannot start from root, for bin_dir
  209.          * will be prefixed to cmd. */
  210.     if(strstr(namebuf,parent_dir_string)!=NULL) {
  211.         setvar(error_data_string,namebuf); module_error("illegal_cmd");
  212.         *p=0; return;
  213.     }
  214.     snprintf(cmdstr,sizeof(cmdstr),"%s/%s",bin_dir,namebuf);
  215.     if(ftest(cmdstr)!=is_exec) goto wrong_command;
  216.     abuf[0]=cmdstr;
  217.     mkfname(outfname,"%s/exec.out",tmp_dir);
  218.     mkfname(errorfname,"%s/exec.err",tmp_dir);
  219.     mkfname(typefname,"%s/exec.type",tmp_dir);
  220.     unlink(typefname);
  221.     mkfname(varfname,"%s/exec.var",tmp_dir);
  222.     unlink(varfname);
  223.     if(!trusted_module()) setenv("untrust","yes",1); else unsetenv("untrust");
  224.     if(strcmp(parm,"about")!=0 && multiexec(namebuf,abuf)) {
  225.         fd_set rset;
  226.         struct timeval t;
  227.         int v1, v2, fd;
  228.        
  229.         i=strlen(parm);
  230.         fd=mxtab[multiexec_index].pipe_stdin[1];
  231.         write(fd,parm,i);
  232.         write(fd,multiexec_random,strlen(multiexec_random));
  233.         *p=0; i=0; k=MAX_LINELEN; fd=mxtab[multiexec_index].pipe_stdout[0];
  234.         while(k>0) {
  235.             FD_ZERO(&rset); FD_SET(fd,&rset);
  236.             t.tv_sec=0; t.tv_usec=100000; v1=0; v2=300; /* 3 seconds */
  237.             if(select(fd+1,&rset,NULL,NULL,&t)<=0) {
  238.                 v1++; if(v1>=v2) break;
  239.                 else continue;
  240.             }
  241.             j=read(fd,p+i,k);
  242.             if(j<0 || j>k) break;
  243.             i+=j; k-=j; p[i]=0;
  244.             pp=strstr(p,multiexec_random);
  245.             if(pp) {*pp=0; break;}
  246.         }
  247.         fd=mxtab[multiexec_index].pipe_stderr[0];
  248.         FD_ZERO(&rset); FD_SET(fd,&rset);
  249.         t.tv_sec=0; t.tv_usec=0;
  250.         if(select(fd+1,&rset,NULL,NULL,&t)>0)
  251.           _get_exec_error(NULL,namebuf);
  252.         strip_trailing_spaces(p);
  253.         return;
  254.     }
  255.     exportall(); setenv("wims_exec_parm",parm,1);
  256.     execredirected(cmdstr,NULL,outfname,errorfname,abuf);
  257.     if(open_working_file(&wf,varfname)==0) {
  258.         char *pt, *pv;
  259.         while(wgetline(tmplbuf,MAX_LINELEN, &wf)!=EOF) {
  260.             pt=find_word_start(tmplbuf); pv=strchr(pt,'=');
  261.             if(pv==NULL || pv<=tmplbuf) continue;
  262.             *pv=0; pv=find_word_start(++pv);
  263.             *find_word_end(pt)=0;
  264.             strip_trailing_spaces(pv);
  265.             setvar(pt,pv);
  266.         }
  267.         close_working_file(&wf,0);
  268.     }
  269.     read_tmp_file(p,"exec.out");
  270.     read_tmp_file(obuf,"exec.type");
  271.     if(obuf[0]) k=atoi(obuf); else k=0;
  272.     for(i=2;i<=k && i<256;i++) {
  273.         char nbuf[32];
  274.         snprintf(nbuf,sizeof(nbuf),"exec.out.%d",i);
  275.         read_tmp_file(obuf,nbuf);
  276.         if(obuf[0]) {
  277.             snprintf(nbuf,sizeof(nbuf),"wims_exec_out_%d",i);
  278.             force_setvar(nbuf,obuf);
  279.         }
  280.     }
  281.     strip_trailing_spaces(p);
  282.     _get_exec_error(errorfname,namebuf);
  283.     return;
  284. }
  285.  
  286.         /* execute external program in the module directory.
  287.          * For privileged modules only */
  288. void calc_mexec(char *p)
  289. {
  290.     char *public_bin;
  291.     if(robot_access) return;
  292.     if(trusted_module()!=1 || is_class_module) {
  293.         module_error("not_trusted"); *p=0; return;
  294.     }
  295.         /* The following is useless, because mexec content is often given
  296.          * by variables */
  297. /*    if(strstr(p,PARENT_DIR_STRING)!=NULL) {
  298.         setvar("wims_error_data",p);
  299.         module_error("illegal_fname"); *p=0; return;
  300.     }
  301. */    public_bin=bin_dir; bin_dir=module_prefix;
  302.     exec_is_module=1;
  303.     calc_exec(p); bin_dir=public_bin;
  304.     exec_is_module=0;
  305. }
  306.  
  307. void _calc_exec(char *p, char *arg0, char *arg1, int n)
  308. {
  309.     char *abuf[8];
  310.     char outfname[MAX_FNAME+1], errorfname[MAX_FNAME+1];
  311.  
  312.     if(robot_access) {*p=0; return;}
  313.     if(!trusted_module() || is_class_module) {
  314.         if(strcasecmp(tmp_debug,"yes")==0)
  315.           accessfile(p,"w","%s/%s.cmd",tmp_dir,arg0);
  316.         abuf[0]="bin/ch..root";
  317.         abuf[1]="&"; abuf[2]=arg0; abuf[3]=p;
  318.         abuf[4]=NULL;
  319.     }
  320.     else {
  321. /*      if(strstr(p,PARENT_DIR_STRING)!=NULL) {
  322.             setvar("wims_error_data",p);
  323.             module_error("illegal_fname"); *p=0; return;
  324.         }
  325. */      abuf[0]=arg0; abuf[1]=arg1; abuf[n]=p; abuf[n+1]=NULL;
  326.     }
  327.     mkfname(outfname,"%s/exec.out",tmp_dir);
  328.     mkfname(errorfname,"%s/exec.err",tmp_dir);
  329.     wrapexec=1; exportall();
  330.     execredirected(abuf[0],NULL,outfname,errorfname,abuf);
  331.     read_tmp_file(p,"exec.out");
  332.     _get_exec_error(errorfname,arg0);
  333. }
  334.  
  335.         /* call shell. */
  336. void calc_sh(char *p)
  337. {
  338.     _calc_exec(p,"sh","-c",2);
  339. }
  340.  
  341.         /* call perl. */
  342. void calc_perl(char *p)
  343. {
  344.     _calc_exec(p,"perl","-e",2);
  345. }
  346.  
  347.         /* simple evaluation of string */
  348. void calc_evalue(char *p)
  349. {
  350.     double d;
  351.     d=evalue(p); float2str(d,p); return;
  352. }
  353.  
  354.         /* substitute math variables */
  355. void calc_mathsubst(char *p)
  356. {
  357.     char *expr, *val, *nam, *pp;
  358.     char buf[MAX_LINELEN+1];
  359.     expr=wordchr(p,"in"); if(expr==NULL) goto error;
  360.     strcpy(buf,find_word_start(expr+strlen("in")));substit(buf);
  361.     *expr=0; substit(p);
  362.     val=strchr(p,'=');
  363.     if(val==NULL) {
  364.         error:
  365.         module_error("mathsubst_syntax"); *p=0;return;
  366.     }
  367.     nam=find_word_start(p); *val=0;
  368.     val=find_word_start(val+1);
  369.     for(pp=val+strlen(val)-1;pp>=val && isspace(*pp);*pp--=0);
  370.     *find_word_end(nam)=0;
  371.     if(*nam==0) goto error;
  372.     if(*nam) for(pp=varchr(buf,nam);pp!=NULL;pp=varchr(pp,nam)) {
  373.         string_modify(buf,pp,pp+strlen(nam),"%s",val);
  374.         pp+=strlen(val);
  375.     }
  376.     strcpy(p,buf);
  377. }
  378.  
  379.         /* substitute and evaluate. */
  380. void calc_evalsubst(char *p)
  381. {
  382.     calc_mathsubst(p);
  383.     calc_evalue(p);
  384. }
  385.  
  386.         /* Nothing needs to be done in the function;
  387.          * substitution is done or not by tag in the structure. */
  388. void calc_subst(char *p)
  389. {
  390. }
  391.  
  392. int _randrep(char *p)
  393. {
  394.     char *rep;
  395.     int i;
  396.     rep=wordchr(p,"repeat"); if(rep==NULL) return 1;
  397.     *rep=0; rep+=strlen("repeat"); i=evalue(rep);
  398.     if(i<1) i=1; if(i>MAX_VALUE_LIST) i=MAX_VALUE_LIST;
  399.     return i;
  400. }
  401.  
  402.         /* integer random */
  403. void calc_randint(char *p)
  404. {
  405.     int lbound, ubound, n, i, t;
  406.     char *p1, *parm[2];
  407.  
  408.     t=_randrep(p); n=cutitems(p,parm,2);
  409.     if(n<=0) {
  410.         snprintf(p,3,"0"); return; /* Random without bound: return 0. */
  411.     }
  412.     lbound=evalue(parm[0]);
  413.         /* Missing ubound: random between +-1 and lbound. */
  414.     if(n<=1) {if(lbound<0) ubound=-1; else ubound=1;}
  415.     else ubound=evalue(parm[1]);
  416.     if(lbound>ubound) {i=lbound; lbound=ubound; ubound=i;}
  417.     for(i=0,p1=p;i<t;i++) {
  418.         if(i>0) *p1++=',';
  419.         mystrncpy(p1,int2str((int) irand(ubound-lbound+1)+lbound),
  420.                   MAX_LINELEN-(p1-p)-1);
  421.         p1+=strlen(p1);
  422.     }
  423.     return;
  424. }
  425.  
  426.         /* floating random */
  427. void calc_randdouble(char *p)
  428. {
  429.     double lbound, ubound;
  430.     int n, i, t;
  431.     char *parm[2], *p1;
  432.    
  433.     t=_randrep(p); n=cutitems(p,parm,2);
  434.     if(n<=0) {
  435.         snprintf(p,3,"0"); return; /* Random without bound: return 0. */
  436.     }
  437.     lbound=evalue(parm[0]);
  438.     if(n<=1) ubound=0; /* Missing ubound: random between 0 and lbound. */
  439.     else ubound=evalue(parm[1]);
  440.     for(i=0,p1=p;i<t;i++) {
  441.         float2str(drand(ubound-lbound)+lbound,tmplbuf);
  442.         mystrncpy(p1,tmplbuf,MAX_LINELEN-(p1-p));
  443.         p1+=strlen(p1);
  444.         if(i<t-1 && p1-p<MAX_LINELEN) *p1++=',';
  445.     }
  446.     *p1=0; return;
  447. }
  448.  
  449.         /* Takes randomly a record in a datafile */
  450. void calc_randfile(char *p)
  451. {
  452.     char *pp, n[MAX_FNAME+1];
  453.     int i, j;
  454.    
  455.     pp=find_word_start(p); *find_word_end(pp)=0;
  456.     mystrncpy(n,pp,sizeof(n));
  457.     i=datafile_recordnum(pp);
  458.     if(i<=0) {  /* zero records */
  459.         *p=0; return;
  460.     }
  461.     j=irand(i); datafile_fnd_record(n,j+1,p);
  462.     return;
  463. }
  464.  
  465.         /* random char, word, line, item in a string */
  466. void calc_randchar(char *p)
  467. {
  468.     p[0]=p[(int) irand(strlen(p))];p[1]=0;
  469. }
  470.  
  471. void calc_randitem(char *p)
  472. {
  473.     int i; char *b;
  474.     i=itemnum(p);
  475.     b=fnd_item(p,irand(i)+1,tmplbuf);
  476.     mystrncpy(p,tmplbuf,MAX_LINELEN);
  477. }
  478.  
  479. void calc_randline(char *p)
  480. {
  481.     int i;
  482.     i=linenum(p); fnd_line(p,irand(i)+1,tmplbuf);
  483.     mystrncpy(p,tmplbuf,MAX_LINELEN);
  484. }
  485.  
  486. void calc_randrow(char *p)
  487. {
  488.     rows2lines(p); calc_randline(p);
  489. }
  490.  
  491. void calc_randword(char *p)
  492. {
  493.     int i;
  494.     i=wordnum(p); fnd_word(p,irand(i)+1,tmplbuf);
  495.     mystrncpy(p,tmplbuf,MAX_LINELEN);
  496. }
  497.  
  498.         /* random permutation of {1,...,n} */
  499. void calc_randperm(char *p)
  500. {
  501.     int n, i, j, k, t, pt, type;
  502.     double v;
  503.     char buf1[MAX_LINELEN+1],buf2[MAX_LINELEN+1];
  504.     char *list[MAX_RANDPERM];
  505.     int table[MAX_RANDPERM];
  506.     char *p1,*p2,*pp;
  507.    
  508.     t=0; pt=0; pp=p;
  509.     p1=find_word_start(p); p2=find_word_end(p1);
  510.     if(p2-p1==strlen("even") && strncasecmp(p1,"even",strlen("even"))==0) {
  511.         t=1; pp=p2;
  512.     }
  513.     if(p2-p1==strlen("odd") && strncasecmp(p1,"odd",strlen("odd"))==0) {
  514.         t=-1; pp=p2;
  515.     }
  516.    
  517.     p1=find_item_end(pp); if(*p1==',') {
  518.         type=1; n=cutitems(pp,list,MAX_RANDPERM); v=0;
  519.     }
  520.     else {
  521.         v=evalue(pp); n=v; type=0;
  522.     }
  523.     if(n==1 && !finite(v)) {strcpy(p,pp); goto shorder;}
  524.     if(n<=0) {*p=0; return;}
  525.     if(n==1 && type==0) {
  526.         strcpy(p,"1");
  527.         shorder: force_setvar("wims_shuffle_order","1"); return;
  528.     }
  529.     if(n>MAX_RANDPERM) n=MAX_RANDPERM;
  530.     for(i=0;i<n;i++) table[i]=i;
  531.         /* Uniformity of this algorithm is easy to show by induction. */
  532.     for(i=0;i<n-1;i++) {
  533.         j=irand(n-i)+i; if(j==i) continue;
  534.         k=table[i]; table[i]=table[j]; table[j]=k;
  535.         pt++;
  536.     }
  537.     pt&=1;
  538.     if((t==1 && pt==1) || (t==-1 && pt==0)) {
  539.         k=table[0]; table[0]=table[1]; table[1]=k;
  540.     }
  541.     if(type) {mystrncpy(buf1,list[table[0]],MAX_LINELEN); p1=buf1+strlen(buf1);}
  542.     else buf1[0]=0;
  543.     mystrncpy(buf2,int2str(table[0]+1),MAX_LINELEN); p2=buf2+strlen(buf2);
  544.     for(i=1;i<n;i++) {
  545.         if(type) {
  546.             j=strlen(list[table[i]]);
  547.             if(p1-buf1+j>=MAX_LINELEN-1) module_error("cmd_output_too_long");
  548.             *p1++=','; memmove(p1,list[table[i]],j+1); p1+=j;
  549.         }
  550.         *p2++=',';
  551.         mystrncpy(p2,int2str(table[i]+1), MAX_LINELEN-(p2-buf2)-1);
  552.         p2+=strlen(p2);
  553.     }
  554.     if(type) mystrncpy(p,buf1,MAX_LINELEN);
  555.     else mystrncpy(p,buf2,MAX_LINELEN);
  556.     force_setvar("wims_shuffle_order",buf2);
  557. }
  558.  
  559.         /* Computes number of lines in the parm. */
  560. void calc_linenum(char *p)
  561. {
  562.     int i=linenum(p); mystrncpy(p,int2str(i),MAX_LINELEN);
  563. }
  564.  
  565.         /* Computes number of lines in the parm. */
  566. void calc_rownum(char *p)
  567. {
  568.     int i=rownum(p); mystrncpy(p,int2str(i),MAX_LINELEN);
  569. }
  570.  
  571.         /* Computes number of items in the list p. */
  572. void calc_itemnum(char *p)
  573. {
  574.     int i=itemnum(p); mystrncpy(p,int2str(i),MAX_LINELEN);
  575. }
  576.  
  577.         /* Computes number of records in the datafile p. */
  578. void calc_recordnum(char *p)
  579. {
  580.     int i;
  581.     i=datafile_recordnum(p); mystrncpy(p,int2str(i),MAX_LINELEN);
  582. }
  583.  
  584.         /* Computes number of words in the parm. */
  585. void calc_wordnum(char *p)
  586. {
  587.     int i=wordnum(p); mystrncpy(p,int2str(i),MAX_LINELEN);
  588. }
  589.  
  590.         /* string length */
  591. void calc_lengthof(char *p)
  592. {
  593.     int i;
  594. /* Strip leading and trailing spaces? */  
  595.     i=strlen(p); mystrncpy(p,int2str(i),MAX_LINELEN);
  596. }
  597.  
  598. char *_blockof_one(char *buf,
  599.                   unsigned int len,
  600.                   char *(fnd_fn)(char *pl, int n, char bbuf[]),
  601.                   int i, char bbuf[])
  602. {
  603.     if(i<0 || i>len || (i==0 && fnd_fn!=datafile_fnd_record) ) {
  604.         bbuf[0]=0; return bbuf;
  605.     }
  606.     return fnd_fn(buf,i,bbuf);
  607. }
  608.  
  609. void _blockof(char *p,
  610.               unsigned int (len_fn)(char *pl),
  611.               char *(fnd_fn)(char *pl, int n, char bbuf[]),
  612.               char *append_char,
  613.               char *msg_str)
  614. {
  615.     int i,j,l,started;
  616.     char *pp, *pe, *po, *pnext;
  617.     char buf[MAX_LINELEN+1], obuf[MAX_LINELEN+1], bbuf[MAX_LINELEN+1];
  618.     pp=wordchr(p,"of");
  619.     if(pp==NULL) {
  620.         setvar(error_data_string,msg_str);
  621.         module_error("no_of"); *p=0; return;
  622.     }
  623.     *pp=0; pp=find_word_start(pp+strlen("of")+1);
  624.     mystrncpy(buf,pp,sizeof(buf)); substit(buf);
  625.     obuf[0]=0; started=0; po=obuf;
  626.     pp=strparstr(p,"to");
  627.     while(*pp!=0 && ((pp>p && !isspace(*(pp-1))) || !isspace(*(pp+2))))
  628.       pp=strparstr(pp+2,"to");
  629.     if(*pp==0) pp=strparstr(p,"..");
  630.     if(*pp!=0) {
  631.         int t=len_fn(buf);
  632.         *pp=0; pe=find_word_start(pp+2);
  633.         i=evalue(p); j=evalue(pe); pnext=buf;
  634.         if(i<0) i=t+i+1; if(i<1) i=1;
  635.         if(j<0) j=t+j+1; if(j>t) j=t;
  636.         for(; i<=j; i++) {
  637.             if(started==0 || fnd_fn==datafile_fnd_record)
  638.               pp=_blockof_one(buf,t,fnd_fn,i,bbuf);
  639.             else
  640.               pp=_blockof_one(pnext,1,fnd_fn,1,bbuf);
  641.             l=strlen(pp); pnext=fnd_nextpos;
  642.             if(l+(po-obuf)>=MAX_LINELEN-3) {
  643.                 too_long: user_error("cmd_output_too_long"); return;
  644.             }
  645.             if(started) {strcpy(po,append_char); po+=strlen(po);}
  646.             memmove(po,pp,l); po+=l; *po=0; started++;
  647.         }
  648.     }
  649.     else {
  650.         char *p1,*p2,ibuf[MAX_LINELEN+1];
  651.         int t=len_fn(buf);
  652.         mystrncpy(ibuf,p,sizeof(ibuf)); substit(ibuf);
  653.         for(p1=ibuf;*p1;p1=find_word_start(p2)) {
  654.             p2=find_item_end(p1); if(*p2) *p2++=0;
  655.             i=evalue(p1);
  656.             if(i<0) i=t+i+1;
  657.             if(i>t || i<0) continue;
  658.             pp=_blockof_one(buf,t,fnd_fn,i,bbuf);l=strlen(pp);
  659.             if(l+(po-obuf)>=MAX_LINELEN-3) goto too_long;
  660.             if(started) {strcpy(po,append_char); po+=strlen(po);}
  661.             memmove(po,pp,l); po+=l; *po=0; started++;
  662.         }
  663.     }
  664.     mystrncpy(p,obuf,MAX_LINELEN);
  665. }
  666.  
  667.         /* pick lines */
  668. void calc_lineof(char *p)
  669. {
  670.     _blockof(p,linenum,fnd_line,"\n","line");
  671. }
  672.  
  673.         /* pick rows */
  674. void calc_rowof(char *p)
  675. {
  676.     char *cc, tbuf[MAX_LINELEN+1];      /* called by substit(); no right to use tmplbuf */
  677.     mystrncpy(tbuf,p,sizeof(tbuf)); substit(tbuf);
  678.     if(strchr(tbuf,'\n')==NULL && strchr(tbuf,';')!=NULL) cc=";";
  679.     else cc="\n";
  680.     _blockof(p,rownum,fnd_row,cc,"row");
  681. }
  682.  
  683.         /* pick items */
  684. void calc_itemof(char *p)
  685. {
  686.     _blockof(p,itemnum,fnd_item,", ","item");
  687. }
  688.  
  689.         /* pick records in datafile */
  690. void calc_recordof(char *p)
  691. {
  692.     _blockof(p,datafile_recordnum,datafile_fnd_record,"\n:","record");
  693. }
  694.  
  695.         /* pick words */
  696. void calc_wordof(char *p)
  697. {
  698.     _blockof(p,wordnum,fnd_word," ","word");
  699. }
  700.  
  701.         /* pick characters */
  702. void calc_charof(char *p)
  703. {
  704.     _blockof(p,charnum,fnd_char,"","char");
  705. }
  706.  
  707. char *append_obj[]={
  708.     "item","line","word"
  709. };
  710. char apch_list[]={',','\n',' '};
  711. #define append_obj_no (sizeof(append_obj)/sizeof(append_obj[0]))
  712.  
  713.         /* append object to string */
  714. void calc_append(char *p)
  715. {
  716.     char append_char, *p1,*p2,*p3,*p4;
  717.     int i,l1,l2;
  718.     p1=find_word_start(p);p2=find_word_end(p1);
  719.     if(*p2!=0) *p2++=0;
  720.     for(i=0;i<append_obj_no && strcmp(p1,append_obj[i])!=0;i++);
  721.     if(i>=append_obj_no) {
  722.         synterr:
  723.         module_error("append_syntax");
  724.         *p=0; return;
  725.     }
  726.     append_char=apch_list[i];
  727.     p3=wordchr(p2,"to");
  728.     if(p3==NULL) goto synterr;
  729.     for(p4=p3-1;p4>p2 && isspace(*(p4-1));p4--);
  730.     *p4=0;p3=find_word_start(p3+strlen("to"));
  731.     memmove(tmplbuf,p2,p4-p2); tmplbuf[p4-p2]=0; strcpy(p,p3);
  732.     substit(tmplbuf);substit(p);
  733.     l1=strlen(p); l2=strlen(tmplbuf);
  734.     if(l1+l2>=MAX_LINELEN-1) user_error("cmd_output_too_long");
  735.     p3=find_word_start(p); p4=p+l1;
  736.     if(*p3) *p4++=append_char;
  737.     memmove(p4,tmplbuf,l2); p4[l2]=0;
  738. }
  739.  
  740.         /* character translation */
  741. void calc_translate(char *p)
  742. {
  743.     int i, internal;
  744.     char *q[3];
  745.     char bf[3][MAX_LINELEN+1];
  746.     char fn[3][MAX_FNAME+1], *arglist[8];
  747.  
  748.     q[0]=find_word_start(p); internal=0;
  749.     if(strncasecmp(q[0],"internal",strlen("internal"))==0 &&
  750.        isspace(*(q[0]+strlen("internal")))) {
  751.         q[0]=find_word_start(q[0]+strlen("internal"));
  752.         internal=1;
  753.     }
  754.     q[1]=wordchr(q[0],"to"); q[2]=wordchr(q[0],"in");
  755.     if(q[1]==NULL || q[2]==NULL) {
  756.         module_error("tr_syntax"); *p=0; return;
  757.     }
  758.     *q[1]=0; *q[2]=0;
  759.     q[1]=find_word_start(q[1]+strlen("to"));
  760.     q[2]=find_word_start(q[2]+strlen("in"));
  761.     for(i=0;i<3;i++) {
  762.         strip_trailing_spaces(q[i]);
  763.         mystrncpy(bf[i],q[i],sizeof(bf[i]));substit(bf[i]);
  764.     }
  765.     if(bf[0][0]==0) goto bailout;
  766.     if(internal || strstr(tmp_dir,"sessions")==NULL || (strpbrk(bf[0],"\\[-")==NULL &&
  767.        strpbrk(bf[1],"\\[-")==NULL)) {  /* direct internal translation */
  768.         char *pp;
  769.         if(strlen(bf[1])<strlen(bf[0])) bf[0][strlen(bf[1])]=0;
  770.         for(pp=strpbrk(bf[2],bf[0]);pp!=NULL && *pp!=0 && pp<bf[2]+MAX_LINELEN;
  771.             pp=strpbrk(pp+1,bf[0])) {
  772.             for(i=0;bf[0][i]!=*pp && bf[0][i]!=0;i++);
  773.             if(bf[0][i]!=0) *pp=bf[1][i];
  774.         }          
  775.         bailout: mystrncpy(p,bf[2],MAX_LINELEN);
  776.         return;
  777.     }
  778.     mkfname(fn[0],"%s/tr.in",tmp_dir);
  779.     mkfname(fn[1],"%s/tr.out",tmp_dir);
  780.     accessfile(bf[2],"w",fn[0]);
  781.     arglist[0]=tr_prog; arglist[1]=bf[0]; arglist[2]=bf[1];
  782.     arglist[3]=NULL; exportall();
  783.     execredirected(tr_prog,fn[0],fn[1],"/dev/null",arglist);
  784.     read_tmp_file(p,"tr.out");
  785.     strip_trailing_spaces(p);
  786. }
  787.  
  788.         /* internal common routine for positionof */
  789. void _pos(char *hay, char *stitch, int style, char *out,
  790.           char *(fnd_obj)(char *p, int n, char bf[]),
  791.           unsigned int (objnum)(char *p))
  792. {
  793.     int i,n,t;
  794.     char buf[MAX_LINELEN+1], nbuf[10];
  795.    
  796.     n=objnum(hay);
  797.     for(i=1;i<=n;i++) {
  798.         fnd_obj(hay,i,buf);
  799.         if(strcmp(buf,stitch)!=0) continue;
  800.         t=strlen(out); if(t>MAX_LINELEN-12) return;
  801.         if(t>0) strcat(out,",");
  802.         snprintf(nbuf,sizeof(nbuf),"%d",i); strcat(out,nbuf);
  803.     }
  804. }
  805.  
  806.         /* return positions of searched for objects */
  807. void calc_pos(char *p)
  808. {
  809.     char buf[2][MAX_LINELEN+1];
  810.     char *p1, *p2;
  811.     int  style;
  812.    
  813.     p1=find_word_start(p); p2=wordchr(p1,"in");
  814.     if(p2==NULL) module_error("syntax_error");
  815.     *p2=0;p2=find_word_start(p2+strlen("in"));
  816.     strip_trailing_spaces(p1);
  817.     strcpy(buf[0],p1);*find_word_end(buf[0])=0; style=0;
  818.     if(strcmp(buf[0],"word")==0) style=1;
  819.     else {
  820.         if(strcmp(buf[0],"item")==0) style=2;
  821.         else {
  822.             if(strcmp(buf[0],"line")==0) style=3;
  823.             else {
  824.                 if(strcmp(buf[0],"char")==0) style=4;
  825.             }
  826.         }
  827.     }
  828.     if(style>0) p1=find_word_start(find_word_end(p1));
  829.     strcpy(buf[0],p1); strcpy(buf[1],p2);
  830.     substit(buf[0]); substit(buf[1]); *p=0;
  831.     switch(style) {
  832.         case 0: {       /* string */
  833.             char *pp, nbuf[10];
  834.             int i,t;
  835.             for(pp=strstr(buf[1],buf[0]);pp!=NULL;pp=strstr(pp+1,buf[0])) {
  836.                 i=pp-buf[1]; t=strlen(p); if(t>MAX_LINELEN-12) return;
  837.                 if(t>0) strcat(p,",");
  838.                 snprintf(nbuf,sizeof(nbuf),"%d",i); strcat(p,nbuf);
  839.             }
  840.             return;
  841.         }
  842.         case 1: {       /* word */
  843.             _pos(buf[1],buf[0],style,p,fnd_word,wordnum);
  844.             return;
  845.         }
  846.         case 2: {       /* item */
  847.             _pos(buf[1],buf[0],style,p,fnd_item,itemnum);
  848.             return;
  849.         }
  850.         case 3: {       /* line */
  851.             _pos(buf[1],buf[0],style,p,fnd_line,linenum);
  852.             return;
  853.         }
  854.         case 4: {       /* char */
  855.             _pos(buf[1],buf[0],style,p,fnd_char,charnum);
  856.             return;
  857.         }
  858.     }
  859. }
  860.  
  861.         /* internal routine for calc_replace. */
  862. void _obj_replace(char *orig, char *by, char *in, int num,
  863.                   char separator, char *result,
  864.                   char *(fnd_obj)(char *p, int n, char bf[]),
  865.                   unsigned int (objnum)(char *p),
  866.                   char *(objchr)(char *p,char *w))
  867. {
  868.     int i;
  869.     char *p1, *p2;
  870.    
  871.     strcpy(result,in);
  872.     if(num!=0) {
  873.         num=objnum(in); i=evalue(orig);
  874.         if(i==0) module_error("bad_index");
  875.         if(i<0) i=num+i+1;
  876.         if(i>num || i<1) return;
  877.         if(separator==0) {  /* char */
  878.             result[i-1]=by[0]; return;
  879.         }
  880.         fnd_obj(result,i,orig); p1=fnd_position;
  881.         if(i<num) {
  882.             fnd_obj(result,i+1,orig); p2=fnd_position;
  883.         }
  884.         else p2=result+strlen(result);
  885.         if(p1==NULL || p2==NULL) internal_error("_obj_replace() error.");
  886.         if(i<num) {
  887.             i=strlen(by); by[i++]=separator;by[i]=0;
  888.         }
  889.         string_modify(result,p1,p2,"%s",by);
  890.     }
  891.     else {
  892.         if(separator==0) {
  893.             if(orig[0]==0 || by[0]==0) return;
  894.             for(p1=strchr(result,orig[0]);p1!=NULL;p1=strchr(p1+1,orig[0]))
  895.                 *p1=by[0];
  896.             return;
  897.         }
  898.         if(strlen(orig)+strlen(by)==0) return;
  899.         for(p1=objchr(result,orig);p1!=NULL;p1=objchr(p1+strlen(by)+1,orig)) {
  900.             string_modify(result,p1,p1+strlen(orig),"%s",by);
  901.         }
  902.     }
  903. }
  904.  
  905.         /* replacement */
  906. void calc_replace(char *p)
  907. {
  908.     int i,style,num,internal;
  909.     char *q[3], *pp;
  910.     char bf[4][MAX_LINELEN+17];
  911.     char fn[3][MAX_FNAME+1], *arglist[8];
  912.     char regexp_char='/';
  913.  
  914.     style=num=0;
  915.     q[0]=find_word_start(p); internal=0;
  916.     if(strncasecmp(q[0],"internal",strlen("internal"))==0 &&
  917.        isspace(*(q[0]+strlen("internal")))) {
  918.         q[0]=find_word_start(q[0]+strlen("internal"));
  919.         internal=1;
  920.     }
  921.     q[1]=wordchr(q[0],"by"); q[2]=wordchr(q[0],"in");
  922.     if(q[1]==NULL || q[2]==NULL) {
  923.         module_error("replace_syntax"); *p=0; return;
  924.     }
  925.     *q[1]=0; *q[2]=0;
  926.     q[1]=find_word_start(q[1]+strlen("by"));
  927.     q[2]=find_word_start(q[2]+strlen("in"));
  928.     mystrncpy(bf[0],q[0],sizeof(bf[0]));
  929.     pp=find_word_end(bf[0]); if(*pp) *(pp++)=0;
  930.     if(strcmp(bf[0],"word")==0) style=1;
  931.     else {
  932.         if(strcmp(bf[0],"item")==0) style=2;
  933.         else {
  934.             if(strcmp(bf[0],"line")==0) style=3;
  935.             else {
  936.                 if(strcmp(bf[0],"char")==0) style=4;
  937.             }
  938.         }
  939.     }
  940.     if(style>0) {
  941.         q[0]=find_word_start(find_word_end(q[0]));
  942.         mystrncpy(bf[0],q[0],sizeof(bf[0]));
  943.         pp=find_word_end(bf[0]); if(*pp) *(pp++)=0;
  944.         if(strcmp(bf[0],"number")==0) {
  945.             num=1; q[0]=find_word_start(pp);
  946.         }
  947.     }
  948.     for(i=0;i<3;i++) {
  949.         strip_trailing_spaces(q[i]);
  950.         mystrncpy(bf[i],q[i],sizeof(bf[i]));
  951.         substit(bf[i]);
  952.     }
  953.     if(bf[0][0]==0) {mystrncpy(p,bf[2],MAX_LINELEN); return;}
  954.     switch(style) {
  955.         case 1: { /* word */
  956.             _obj_replace(bf[0],bf[1],bf[2],num,' ',p,
  957.                          fnd_word,wordnum,wordchr);
  958.             return;
  959.         }
  960.         case 2: { /* item */
  961.             _obj_replace(bf[0],bf[1],bf[2],num,',',p,
  962.                          fnd_item,itemnum,itemchr);
  963.             return;
  964.         }
  965.         case 3: { /* line */
  966.             _obj_replace(bf[0],bf[1],bf[2],num,'\n',p,
  967.                          fnd_line,linenum,linechr);
  968.             return;
  969.         }
  970.         case 4: { /* char */
  971.             if(bf[1][0]==0) bf[1][0]=' ';
  972.             if(bf[0][0]==0) return;
  973.             _obj_replace(bf[0],bf[1],bf[2],num,0,p,
  974.                          fnd_char,charnum,charchr);
  975.             return;
  976.         }
  977.         default: break;
  978.     }
  979.     if(internal || strstr(tmp_dir,"sessions")==NULL || (strpbrk(bf[0],"\\[^.*$")==NULL &&
  980.        strpbrk(bf[1],"\\[^.*$")==NULL)) {
  981.         /* No regexp, direct replace */
  982.         char *pp;
  983.         for(pp=strstr(bf[2],bf[0]);pp<bf[2]+MAX_LINELEN && pp!=NULL;
  984.             pp=strstr(pp+strlen(bf[1]),bf[0])) {
  985.             string_modify(bf[2],pp,pp+strlen(bf[0]),"%s",bf[1]);
  986.         }
  987.         mystrncpy(p,bf[2],MAX_LINELEN);return;
  988.     }
  989.     mkfname(fn[0],"%s/sed.in",tmp_dir);
  990.     mkfname(fn[1],"%s/sed.out",tmp_dir);
  991.     accessfile(bf[2],"w",fn[0]);
  992.     snprintf(bf[3],sizeof(bf[3]),"s%c%s%c%s%cg",
  993.              regexp_char,bf[0],regexp_char,bf[1],regexp_char);
  994.     arglist[0]=sed_prog; arglist[1]=bf[3]; arglist[2]=NULL;
  995.     execredirected(sed_prog,fn[0],fn[1],"/dev/null",arglist);
  996.     read_tmp_file(p,"sed.out");
  997.     strip_trailing_spaces(p);
  998. }
  999.  
  1000.         /* transforms a string to hexadecimal code, upper-case */
  1001. void calc_hex(char *p)
  1002. {
  1003.     unsigned char *p1, orig[MAX_LINELEN+1];
  1004.     char *p2;
  1005.     char *hex="0123456789ABCDEF";
  1006.    
  1007.     mystrncpy(orig,p,MAX_LINELEN);
  1008.     for(p1=orig, p2=p; *p1 && p2-p<MAX_LINELEN-2; p1++) {
  1009.         *p2++=hex[(*p1>>4)&15]; *p2++=hex[*p1&15];
  1010.     }
  1011.     *p2=0;    
  1012. }
  1013.  
  1014.         /* transforms to lower/upper cases */
  1015. void calc_tolower(char *p)
  1016. {
  1017.     char *pp;
  1018.     for(pp=p;*pp;pp++) *pp=tolower(*pp);
  1019. }
  1020.  
  1021. void calc_toupper(char *p)
  1022. {
  1023.     char *pp;
  1024.     for(pp=p;*pp;pp++) *pp=toupper(*pp);
  1025. }
  1026.  
  1027.         /* strip leading and trailing spaces */
  1028. void calc_trim(char *p)
  1029. {
  1030.     char *s;
  1031.     s=find_word_start(p);
  1032.     if(s>p) memmove(p,s,MAX_LINELEN-(s-p)+1);
  1033.     strip_trailing_spaces(p);
  1034. }
  1035.  
  1036.         /* output date. Uses Linux 'date' utility. */
  1037. void calc_date(char *p)
  1038. {
  1039.     char *p1, *p2, *p3;
  1040.     if(!trusted_module() || is_class_module) {
  1041.         if(strstr(p,"..")!=NULL) return;
  1042.         for(p1=find_word_start(p); *p1; p1=find_word_start(p2)) {
  1043.             p2=find_word_end(p1);
  1044.             while(*p1=='\'' || *p1=='"') p1++;
  1045.             if(*p1!='-') continue;
  1046.             for(p3=p1+1;p3<p2;p3++) if(strchr("rs",*p3)!=NULL) return;
  1047.         }
  1048.     }
  1049.     wrapexec=1;
  1050.     call_ssh("date %s >%s/date.out 2>/dev/null",p,tmp_dir);
  1051.     read_tmp_file(p,"date.out");
  1052. }
  1053.  
  1054.         /* ls, or dir */
  1055. void calc_listfile(char *p)
  1056. {
  1057.     char *pp;
  1058.    
  1059.         /* only for trusted modules */
  1060.     if(!trusted_module() || is_class_module) {
  1061.         module_error("not_trusted"); *p=0; return;
  1062.     }
  1063.         /* security measures. */
  1064.     for(pp=p;*pp;pp++) if(isspace(*pp) || *pp==';') *pp=' ';
  1065.     if(strstr(p,parent_dir_string)!=NULL) {
  1066.         setvar(error_data_string,p);
  1067.         module_error("illegal_fname"); return;
  1068.     }
  1069.     wrapexec=1;
  1070.     call_sh("ls %s >%s/ls.out 2>%s/ls.err",
  1071.             p,tmp_dir,tmp_dir);
  1072.     read_tmp_file(p,"ls.out");
  1073. }
  1074.  
  1075.         /* instex static: static tex inserts */
  1076. void calc_instexst(char *p)
  1077. {
  1078.     char nbuf[MAX_FNAME+1], bufc[MAX_LINELEN+1];
  1079.     char buf2[1024], altbuf[1024], buf[MAX_LINELEN+1], urlbuf[MAX_LINELEN+1];
  1080.     char *b, *at, *al, *md1, *md2;
  1081.     int t, border, vspace;
  1082.     struct stat st,stc;
  1083.     char *p1, *p2, *p3, *ppp, *pt;
  1084.  
  1085.     if(robot_access) {*p=0; return;}
  1086.     p1=find_word_start(p); p2=find_word_end(p1);
  1087.     p3=p2-4; vspace=0;
  1088.     fix_tex_size();
  1089.     t=untrust; untrust=0;
  1090.     if(find_module_file(m_file.name,bufc,0)) module_error(m_file.name);
  1091.     else stat(bufc,&stc);
  1092.     untrust=t;
  1093.     if(*p3=='.' && (memcmp(p3+1,"gif",3)==0 || memcmp(p3+1,"png",3)==0)) {
  1094.         char mbuf[MAX_LINELEN+1];
  1095.         if(*p2!=0) *p2++=0;
  1096.         p2=find_word_start(p2);
  1097.         strcpy(mbuf,p1); substit(mbuf);
  1098.         if(strstr(mbuf,parent_dir_string)!=NULL) {
  1099.             setvar(error_data_string,mbuf);
  1100.             module_error("illegal_fname"); return;
  1101.         }
  1102.         mkfname(nbuf,"%s/%s",module_prefix,mbuf);
  1103.     }
  1104.     else {
  1105.         ppp=getvar(ro_name[ro_module]);
  1106.         if(ppp==NULL) internal_error("calc_instexst(): module name vanishes.");
  1107.         p2=p1;
  1108.         mkfname(nbuf,"w/instex/%d/%s/%s_%d.gif",
  1109.                  current_tex_size,ppp,m_file.name,m_file.l);
  1110.     }
  1111.     snprintf(urlbuf,sizeof(urlbuf),"%s%s?%X",ref_base,nbuf,
  1112.              (unsigned short int) stc.st_mtime);
  1113.     mystrncpy(buf,nbuf,sizeof(buf));
  1114.     if((ppp=strrchr(buf,'/'))!=NULL) {
  1115.         *ppp=0;mkdirs(buf);
  1116.     }
  1117.     b=getvar("ins_border");
  1118.     at=getvar("ins_attr");
  1119.     al=getvar("ins_align");
  1120.     if(at==NULL) at="";
  1121.     if(al==NULL) al="";al=find_word_start(al);
  1122.     if(*al!=0) snprintf(buf2,sizeof(buf2),"align=%s",al); else buf2[0]=0;
  1123.     if(b==NULL || *b==0) border=0;
  1124.     else border=atoi(b);
  1125.     if(border<0) border=0; if(border>100) border=100;
  1126.     if(instex_ready(p2,urlbuf)) goto prt;
  1127.     if(stat(nbuf,&st)!=0 || st.st_mtime<stc.st_mtime || st.st_size<45) {
  1128.         setenv("texgif_style",instex_style,1);
  1129.         setenv("texgif_src",p2,1);
  1130.         setenv("texgif_outfile",nbuf,1);
  1131.         setenv("texgif_tmpdir",tmp_dir,1); exportall();
  1132.         wrapexec=0; call_ssh("%s &>%s/instexst.log",tex2gif,tmp_dir);
  1133.         setenv("instexst_src","",1);
  1134.     }
  1135.     prt: md1=md2="";
  1136.     if(strcasecmp(al,"middle")==0) {
  1137.         md1=mathalign_sup1; md2=mathalign_sup2;
  1138.         vspace=5;
  1139.     }
  1140.     if(ins_alt[0]==0) mystrncpy(ins_alt,p2,sizeof(ins_alt));
  1141.     if(strchr(ins_alt,'"')!=NULL || strlen(ins_alt)>256) ins_alt[0]=0;
  1142.     pt=getvar("wims_ins_alt"); if(pt==NULL) pt="";
  1143.     if(ins_alt[0] && strcmp(pt,"none")!=0)
  1144.       snprintf(altbuf,sizeof(altbuf)," alt=\"%s\"",ins_alt);
  1145.     else altbuf[0]=0;
  1146.     snprintf(p,MAX_LINELEN,"%s<img src=\"%s\" border=%d vspace=%d %s %s%s>%s",
  1147.              md1,urlbuf,border,vspace,at,buf2,altbuf,md2);
  1148.     setvar("ins_attr",""); ins_alt[0]=0;
  1149.     setvar("ins_url",urlbuf);
  1150. }
  1151.  
  1152.         /* extract non-empty lines or items */
  1153. void calc_nonempty(char *p)
  1154. {
  1155.     int type, i, cnt;
  1156.     char *p1, *p2, buf[MAX_LINELEN+1], out[MAX_LINELEN+1];
  1157.     p1=find_word_start(p); p2=find_word_end(p1);
  1158.     if(*p2) *p2++=0; else {
  1159.         *p=0; return;
  1160.     }
  1161.     type=0;
  1162.     if(strcasecmp(p1,"item")==0 || strcasecmp(p1,"items")==0) type=1;
  1163.     if(type==0 && (strcasecmp(p1,"line")==0 || strcasecmp(p1,"lines")==0))
  1164.       type=2;
  1165.     if(type==0 && (strcasecmp(p1,"row")==0 || strcasecmp(p1,"rows")==0))
  1166.       type=3;
  1167.     if(type==0) module_error("syntax_error");
  1168.     out[0]=out[1]=0;
  1169.     switch(type) {
  1170.         case 1: {       /* items */
  1171.             cnt=itemnum(p2);
  1172.             for(i=1; i<=cnt; i++) {
  1173.                 fnd_item(p2,i,buf);
  1174.                 if(*find_word_start(buf)) {
  1175.                     strcat(out,",");strcat(out,buf);
  1176.                 }
  1177.             }
  1178.             break;
  1179.         }
  1180.         case 2: {       /* lines */
  1181.             lines: cnt=linenum(p2);
  1182.             for(i=1; i<=cnt; i++) {
  1183.                 fnd_line(p2,i,buf);
  1184.                 if(*find_word_start(buf)) {
  1185.                     strcat(out,"\n");strcat(out,buf);
  1186.                 }
  1187.             }
  1188.             break;
  1189.         }
  1190.         case 3: {       /* rows */
  1191.             int t=rows2lines(p2);
  1192.             if(t==0) goto lines;
  1193.             cnt=linenum(p2);
  1194.             for(i=1; i<=cnt; i++) {
  1195.                 fnd_line(p2,i,buf);
  1196.                 if(*find_word_start(buf)) {
  1197.                     strcat(out,";");strcat(out,buf);
  1198.                 }
  1199.             }
  1200.             break;
  1201.         }
  1202.         default: break;
  1203.     }
  1204.     strcpy(p,out+1);
  1205. }
  1206.  
  1207.         /* returns a list with unique items */
  1208. void calc_listuniq(char *p)
  1209. {
  1210.     int i,n;
  1211.     char lout[MAX_LINELEN+2], *ll;
  1212.     char *cut[MAX_LIST];
  1213.     lout[0]=lout[1]=0;
  1214.     n=cutitems(p,cut,MAX_LIST); for(i=0;i<n;i++) {
  1215.         ll=cut[i];
  1216.         if(*ll && itemchr(lout,ll)==NULL &&
  1217.            strlen(lout)+strlen(ll)<MAX_LINELEN-2) {
  1218.             strcat(lout,",");strcat(lout,ll);
  1219.         }
  1220.     }
  1221.     strcpy(p,lout+1);
  1222. }
  1223.  
  1224.         /* returns intersection of 2 lists */
  1225. void calc_listintersect(char *p)
  1226. {
  1227.     char l1[MAX_LINELEN+1],l2[MAX_LINELEN+1],lout[MAX_LINELEN+2];
  1228.     char *cut[MAX_LIST];
  1229.     char *pp, *ll;
  1230.     int i,n;
  1231.    
  1232.     pp=wordchr(p,"and");
  1233.     if(pp==NULL) module_error("syntax_error");
  1234.     *pp=0;strcpy(l1,p); strcpy(l2,pp+strlen("and"));
  1235.     lout[0]=lout[1]=0; substit(l1); substit(l2);
  1236.     n=cutitems(l1,cut,MAX_LIST); if(n<=0) {*p=0; return;}
  1237.     for(i=0;i<n;i++) {
  1238.         ll=cut[i];
  1239.         if(ll[0] && itemchr(l2,ll)!=NULL && itemchr(lout,ll)==NULL &&
  1240.            strlen(lout)+strlen(ll)<MAX_LINELEN-2) {
  1241.             strcat(lout,",");strcat(lout,ll);
  1242.         }
  1243.     }
  1244.     strcpy(p,lout+1);
  1245. }
  1246.  
  1247.         /* returns union of 2 lists */
  1248. void calc_listunion(char *p)
  1249. {
  1250.     char l1[MAX_LINELEN+1],l2[MAX_LINELEN+1],lout[MAX_LINELEN+2];
  1251.     char *cut[MAX_LIST];
  1252.     char *pp, *ll;
  1253.     int i,n;
  1254.    
  1255.     pp=wordchr(p,"and");
  1256.     if(pp==NULL) module_error("syntax_error");
  1257.     *pp=0;strcpy(l1,p); strcpy(l2,pp+strlen("and"));
  1258.     lout[0]=lout[1]=0; substit(l1); substit(l2);
  1259.     n=cutitems(l1,cut,MAX_LIST); for(i=0;i<n;i++) {
  1260.         ll=cut[i];
  1261.         if(ll[0] && itemchr(lout,ll)==NULL &&
  1262.            strlen(lout)+strlen(ll)<MAX_LINELEN-2) {
  1263.             strcat(lout,",");strcat(lout,ll);
  1264.         }
  1265.     }
  1266.     n=cutitems(l2,cut,MAX_LIST); for(i=0;i<n;i++) {
  1267.         ll=cut[i];
  1268.         if(ll[0] && itemchr(lout,ll)==NULL &&
  1269.            strlen(lout)+strlen(ll)<MAX_LINELEN-2) {
  1270.             strcat(lout,",");strcat(lout,ll);
  1271.         }
  1272.     }
  1273.     strcpy(p,lout+1);
  1274. }
  1275.  
  1276.         /* returns items of list2 not in list1 */
  1277. void calc_listcomplement(char *p)
  1278. {
  1279.     char l1[MAX_LINELEN+1],l2[MAX_LINELEN+1],lout[MAX_LINELEN+2];
  1280.     char *cut[MAX_LIST];
  1281.     char *pp, *ll;
  1282.     int i,n;
  1283.    
  1284.     pp=wordchr(p,"in");
  1285.     if(pp==NULL) module_error("syntax_error");
  1286.     *pp=0;strcpy(l1,p); strcpy(l2,pp+strlen("in"));
  1287.     lout[0]=lout[1]=0; substit(l1); substit(l2);
  1288.     n=cutitems(l2,cut,MAX_LIST); if(n<=0) {*p=0; return;}
  1289.     for(i=0;i<n;i++) {
  1290.         ll=cut[i];
  1291.         if(ll[0] && itemchr(l1,ll)==NULL && itemchr(lout,ll)==NULL &&
  1292.            strlen(lout)+strlen(ll)<MAX_LINELEN-2) {
  1293.             strcat(lout,",");strcat(lout,ll);
  1294.         }
  1295.     }
  1296.     strcpy(p,lout+1);
  1297. }
  1298.  
  1299.         /* Consult a dictionary to translate a string */
  1300. /*
  1301. void calc_dictionary(char *p)
  1302. {
  1303.     char *pp;
  1304.     char name[MAX_LINELEN+1], str[MAX_LINELEN+1];
  1305.     int t;
  1306.     pp=wordchr(p,"for");
  1307.     if(pp==NULL || pp<=p) module_error("syntax_error");
  1308.     *(find_word_end(pp-1))=0;
  1309.     strcpy(name,p); substit(name);
  1310.     pp=find_word_start(pp+strlen("for")); t=0;
  1311.     if(memcmp(pp,"word",strlen("word"))==0 &&
  1312.        (isspace(*(pp+strlen("word"))) ||
  1313.         (*(pp+strlen("word"))=='s' && isspace(*(pp+strlen("words")))))) {
  1314.         pp=find_word_start(pp+strlen("words")); t=1;
  1315.     }
  1316.     if(memcmp(pp,"line",strlen("line"))==0 &&
  1317.        (isspace(*(pp+strlen("line"))) ||
  1318.         (*(pp+strlen("line"))=='s' && isspace(*(pp+strlen("lines")))))) {
  1319.         pp=find_word_start(pp+strlen("lines")); t=1;
  1320.     }
  1321.     if(memcmp(pp,"list",strlen("list"))==0 && isspace(*(pp+strlen("list")))) {
  1322.         pp=find_word_start(pp+strlen("list"));
  1323.     }
  1324.     if(memcmp(pp,"items",strlen("items"))==0 && isspace(*(pp+strlen("items")))) {
  1325.         pp=find_word_start(pp+strlen("items"));
  1326.     }
  1327.     if(memcmp(pp,"item",strlen("item"))==0 && isspace(*(pp+strlen("item")))) {
  1328.         pp=find_word_start(pp+strlen("item"));
  1329.     }
  1330.     strcpy(str,pp); substit(str);
  1331.     switch(t) {
  1332.         case 1: {
  1333.             calc_words2items(str); break;
  1334.         }
  1335.         case 2: {
  1336.             calc_lines2items(str); break;
  1337.         }
  1338.         default: break;
  1339.     }
  1340. }
  1341. */
  1342.  
  1343. void calc_module(char *p)
  1344. {
  1345.     char *p1,*p2, ind_buf[MAX_LINELEN+1], buf[MAX_FNAME+1];
  1346.     char *tp;
  1347.    
  1348.     p1=find_word_start(p); p2=find_word_end(p1);
  1349.     if(*p2!=0) *p2++=0;
  1350.     p2=find_word_start(p2); *find_word_end(p2)=0;
  1351.     if(*p1==0) {empty: *p=0; return;}
  1352.     if(*p2==0) {
  1353.         snprintf(buf,sizeof(buf),"module_%s",p1);
  1354.         p1=getvar(buf); if(p1==NULL) p1="";
  1355.         mystrncpy(p,p1,MAX_LINELEN); return;
  1356.     }
  1357.     mkfname(buf,"%s/%s/INDEX",module_dir,p2);
  1358.     tp=readfile(buf,ind_buf,MAX_LINELEN);
  1359.     if(tp==NULL) {
  1360.         mkfname(buf,"%s/%s/index",module_dir,p2);
  1361.         tp=readfile(buf,ind_buf,MAX_LINELEN);
  1362.     }
  1363.     if(tp==NULL && p2[strlen(p2)-3]!='.') {
  1364.         mkfname(buf,"%s/%s.%s/INDEX",module_dir,p2,lang);
  1365.         tp=readfile(buf,ind_buf,MAX_LINELEN); if(tp==NULL) {
  1366.             mkfname(buf,"%s/%s.%s/index",module_dir,p2,lang);
  1367.             tp=readfile(buf,ind_buf,MAX_LINELEN);
  1368.         }
  1369.         if(tp==NULL) {
  1370.             int i;
  1371.             for(i=0;i<available_lang_no;i++) {
  1372.                 mkfname(buf,"%s/%s.%s/INDEX",module_dir,p2,available_lang[i]);
  1373.                 tp=readfile(buf,ind_buf,MAX_LINELEN); if(tp) break;
  1374.                 mkfname(buf,"%s/%s.%s/index",module_dir,p2,
  1375.                          available_lang[i]);
  1376.                 tp=readfile(buf,ind_buf,MAX_LINELEN); if(tp) break;
  1377.             }
  1378.         }
  1379.     }
  1380.     if(tp==NULL) goto empty;    /* module not found */
  1381.     _getdef(ind_buf,p1,p);
  1382. }
  1383.  
  1384.         /* strip enclosing parentheses */
  1385. void calc_declosing(char *p)
  1386. {
  1387.     strip_enclosing_par(p);
  1388. }
  1389.  
  1390.         /* remove html tag, very rudimentary. */
  1391. void calc_detag(char *p)
  1392. {
  1393.     char *p1, *p2;
  1394.     for(p1=strchr(p,'<'); p1!=NULL; p1=strchr(p1,'<')) {
  1395.         p2=strchr(p1,'>'); if(p2==NULL) p1++;
  1396.         else strcpy(p1,p2+1);
  1397.     }
  1398. }
  1399.  
  1400.         /* prepare a string to be inserted into a form input
  1401.          * or textarea as is */
  1402. void calc_reinput(char *p)
  1403. {
  1404.     char *p1;
  1405.     for(p1=strchr(p,'&'); p1!=NULL; p1=strchr(p1,'&')) {
  1406.         p1++; string_modify(p,p1,p1,"amp;");
  1407.     }
  1408.     for(p1=strchr(p,'<'); p1!=NULL; p1=strchr(++p1,'<'))
  1409.       string_modify(p,p1,p1+1,"&lt;");
  1410. }
  1411.  
  1412.         /* get a definition from a file. Trusted modules only. */
  1413. void calc_defof(char *p)
  1414. {
  1415.     char *p1;
  1416.     char fbuf[MAX_FNAME+1], nbuf[MAX_LINELEN+1], tbuf[MAX_LINELEN+1];
  1417.    
  1418.     secure_exec();
  1419.     p1=wordchr(p,"in"); if(p1==NULL) module_error("syntax_error");
  1420.     *p1=0; p1=find_word_start(p1+strlen("in"));
  1421.     mystrncpy(nbuf,p,sizeof(nbuf));  mystrncpy(tbuf,p1,sizeof(tbuf));
  1422.     substit(nbuf); substit(tbuf);
  1423.     p1=find_word_start(tbuf); *find_word_end(p1)=0;
  1424.     if(find_module_file(p1,fbuf,0)) {*p=0; return;}
  1425.     p1=find_word_start(nbuf); strip_trailing_spaces(p1);
  1426.     getdef(fbuf,p1,p);
  1427. }
  1428.  
  1429.         /* check host */
  1430. void calc_checkhost(char *p)
  1431. {
  1432.     int t;
  1433.     if(robot_access || !trusted_module()) strcpy(p,"0");
  1434.     else {
  1435.         t=checkhost(p); mystrncpy(p,int2str(t),MAX_LINELEN);
  1436.     }
  1437. }
  1438.  
  1439. #define MAX_COLUMNS 256
  1440.  
  1441.         /* A rudimentary database facility */
  1442. void calc_select(char *p)
  1443. {
  1444.     char *p1, *p2, *p3, *bufp, c, sep;
  1445.     char buf1[MAX_LINELEN+1], buf2[MAX_LINELEN+1];
  1446.     char buf[MAX_LINELEN+1];
  1447.     int i,j,lmax;
  1448.     p2=wordchr(p,"where"); if(p2==NULL) module_error("syntax_error");
  1449.     *p2=0; p2+=strlen("where");
  1450.     p2=find_word_start(p2); strip_trailing_spaces(p2);
  1451.     p1=find_word_start(p) ; strip_trailing_spaces(p1);
  1452.     if(*p1==0 || *p2==0) module_error("syntax_error");
  1453.     strcpy(buf1,p1); strcpy(buf2,p2); sep='\n';
  1454.         /* buf1: data; buf2: condition. */
  1455.     substit(buf1);
  1456.     if(strstr(buf2,"column")!=NULL) {   /* matrix */
  1457.         lmax=0; if(rows2lines(buf1)) sep=';';
  1458.         for(p1=strstr(buf2,"column"); p1; p1=strstr(p1+1,"column")) {
  1459.             if(p1>buf2 && isalnum(*(p1-1))) continue;
  1460.             p2=find_word_start(p1+strlen("column"));
  1461.             for(p3=p2; myisdigit(*p3); p3++);
  1462.             if(p3==p2) continue;
  1463.             c=*p3; *p3=0; i=atoi(p2); *p3=c;
  1464.             if(i<=0 || i>MAX_COLUMNS) continue;
  1465.             if(i>lmax) lmax=i;
  1466.             string_modify(buf2,p1,p3,"$(wims_select_col_%d)",i);
  1467.         }
  1468.         buf[0]=0; bufp=buf;
  1469.         for(p1=buf1; *p1; p1=p2) {
  1470.             char ibuf[MAX_LINELEN+1], nbuf[MAX_NAMELEN+1];
  1471.             p2=strchr(p1,'\n');
  1472.             if(p2==NULL) p2=p1+strlen(p1); else *p2++=0;
  1473.             if(*find_word_start(p1)==0) continue;
  1474.             for(j=1;j<=lmax;j++) {
  1475.                 snprintf(nbuf,sizeof(nbuf),"wims_select_col_%d",j);
  1476.                 fnd_item(p1,j,ibuf); force_setvar(nbuf,ibuf);
  1477.             }
  1478.             strcpy(ibuf,buf2); if(compare(ibuf,0,0)) {
  1479.                 snprintf(bufp,MAX_LINELEN-(bufp-buf),"%s%c",p1,sep);
  1480.                 bufp+=strlen(bufp);
  1481.             }
  1482.         }
  1483.     }
  1484.     else {      /* datafile */
  1485.         module_error("syntax_error");
  1486.        
  1487.     }
  1488.     if(buf[0]!=0) buf[strlen(buf)-1]=0;
  1489.     mystrncpy(p,buf,MAX_LINELEN);
  1490. }
  1491.  
  1492.         /* Extract a column from a matrix */
  1493. void calc_columnof(char *p)
  1494. {
  1495.     char *p1, *p2, *bufp, sep;
  1496.     char buf1[MAX_LINELEN+1], buf2[MAX_LINELEN+1];
  1497.     char buf[MAX_LINELEN+1];
  1498.     p2=wordchr(p,"of"); if(p2==NULL) module_error("syntax_error");
  1499.     *p2=0; p2+=strlen("of");
  1500.     p2=find_word_start(p2); strip_trailing_spaces(p2);
  1501.     p1=find_word_start(p) ; strip_trailing_spaces(p1);
  1502.     if(*p1==0) module_error("syntax_error");
  1503.     if(*p2==0) {*p=0; return;}
  1504.     strcpy(buf1,p1); strcpy(buf2,p2); sep='\n';
  1505.         /* buf1: number(s); buf2: matrix. */
  1506.     substit(buf1); substit(buf2);
  1507.     if(rows2lines(buf2)) sep=';';
  1508.     if(strchr(buf1,',')==NULL && wordchr(buf1,"to")==NULL
  1509.        && strstr(buf1,"..")==NULL) sep=',';
  1510.     buf[0]=0; bufp=buf;
  1511.     for(p1=buf2; *p1; p1=p2) {
  1512.         char ibuf[MAX_LINELEN+1];
  1513.         p2=strchr(p1,'\n');
  1514.         if(p2==NULL) p2=p1+strlen(p1); else *p2++=0;
  1515.         snprintf(ibuf,sizeof(ibuf),"%s of %s",buf1,p1);
  1516.         calc_itemof(ibuf);
  1517.         snprintf(bufp,MAX_LINELEN-(bufp-buf),"%s%c",ibuf,sep);
  1518.         bufp+=strlen(bufp);
  1519.     }
  1520.     if(buf[0]!=0) buf[strlen(buf)-1]=0;
  1521.     mystrncpy(p,buf,MAX_LINELEN);
  1522. }
  1523.  
  1524.         /* find roots of a function or equation in a given zone */
  1525. void calc_solve(char *p)
  1526. {
  1527.     char *pp, *fp, *forp;
  1528.     char buf[MAX_LINELEN+1], vbuf[MAX_LINELEN+1];
  1529.     double v, dd, start, stop, step, old, v1, v2, v3, d1, d2, d3;
  1530.     int i, pos;
  1531.    
  1532.     forp=wordchr(p,"for");
  1533.     if(forp==NULL) { syntax: module_error("syntax_error"); *p=0; return; }
  1534.     *forp=0; forp+=strlen("for");
  1535.     fp=find_word_start(p); strip_trailing_spaces(fp);
  1536.     if(*fp==0) goto syntax;
  1537.     i=cutfor(forp,NULL); if(i<0 || forstruct.type==for_in) goto syntax;
  1538.     if(i>0) {*p=0; return;}
  1539.     start=forstruct.from; stop=forstruct.to; forp=forstruct.var;
  1540.     mystrncpy(buf,fp,sizeof(buf)); substitute(buf);
  1541.     *p=0; pp=strchr(buf,'='); if(pp!=NULL) {
  1542.         if(strlen(buf)>=MAX_LINELEN-16) return;
  1543.         strcat(buf,")");
  1544.         string_modify(buf,pp,pp+1,"-(");
  1545.     }
  1546.     i=0; for(fp=varchr(buf,forp); fp!=NULL; fp=varchr(fp,forp)) {
  1547.         string_modify(buf,fp,fp+strlen(forp),EV_X); fp+=strlen(EV_X); i++;
  1548.     }
  1549.     if(i==0 || start==stop) return;
  1550.     evalue_compile(buf); pos=eval_getpos(EV_X);
  1551.     if(start>stop) {dd=start; start=stop; stop=dd;}
  1552.     step=(stop-start)/100; if(step==0) return;
  1553.     pp=p; old=0;
  1554.     for(v=start; v<=stop; v+=step, old=dd) {
  1555.         eval_setval(pos,v);
  1556.         set_evalue_error(0); set_evalue_pointer(buf); dd=_evalue(10);
  1557.         if(v==start) continue;
  1558.         if(!finite(old) || !finite(dd) || (old>0 && dd>0) || (old<0 && dd<0))
  1559.           continue;
  1560.         if(dd==0 && v<stop) continue;
  1561.         v1=v-step; v2=v; d1=old; d2=dd;
  1562.         for(i=0;i<30;i++) {
  1563.             v3=(v1+v2)/2; eval_setval(pos,v3);
  1564.             set_evalue_error(0); set_evalue_pointer(buf); d3=_evalue(10);
  1565.             if(!finite(d3)) goto next;
  1566.             if((d1>0 && d3>0) || (d1<0 && d3<0)) {d1=d3; v1=v3;}
  1567.             else {d2=d3; v2=v3;}
  1568.         }
  1569.         float2str(v3,vbuf); if(pp-p+strlen(vbuf)<MAX_LINELEN-1) {
  1570.             if(pp>p) *pp++=','; strcpy(pp,vbuf);
  1571.             pp+=strlen(pp);
  1572.         }
  1573.         else break;
  1574.         next: ;
  1575.     }
  1576. }
  1577.  
  1578.         /* type: 1=values, 2=sum, 3=product, 4=recursion, 5=subst */
  1579. void _values(char *p, int type)
  1580. {
  1581.     char *pp, *fp, *forp;
  1582.     char vbuf[MAX_LINELEN+1], buf[MAX_LINELEN+1], fbuf[MAX_LINELEN+1], tbuf[MAX_LINELEN+1];
  1583.     char *f[64];
  1584.     double v, dd, start, stop, step, v0;
  1585.     int i, k, fcnt, pos, posr;
  1586.    
  1587.     forp=wordchr(p,"for");
  1588.     if(forp==NULL) { syntax: module_error("syntax_error"); *p=0; return; }
  1589.     *forp=0; forp+=strlen("for"); forp=find_word_start(forp);
  1590.     fp=find_word_start(p); strip_trailing_spaces(fp);
  1591.     if(*fp==0) goto syntax;
  1592.     if(type<5) i=cutfor(forp,NULL); else i=cutfor(forp,tbuf);
  1593.     if(i<0) goto syntax; if(i>0) {*p=0; return;}
  1594.     start=forstruct.from; stop=forstruct.to; step=forstruct.step;
  1595.     forp=forstruct.var;
  1596.     mystrncpy(buf,fp,sizeof(buf)); substitute(buf);
  1597.     for(fp=varchr(buf,forp); fp!=NULL; fp=varchr(fp,forp)) {
  1598.         string_modify(buf,fp,fp+strlen(forp),EV_X); fp+=strlen(EV_X);
  1599.     }
  1600.     for(fp=varchr(buf,"last"); fp!=NULL; fp=varchr(fp,"last")) {
  1601.         string_modify(buf,fp,fp+strlen("last"),EV_S); fp+=strlen(EV_S);
  1602.     }
  1603.     fcnt=pos=posr=0;
  1604.     if(type==5) goto skip;
  1605.     fcnt=itemnum(buf); if(fcnt>64) fcnt=64; pp=fbuf;
  1606.     for(k=0; k<fcnt; k++) {
  1607.         fnd_item(buf,k+1,vbuf); evalue_compile(vbuf);
  1608.         if(pp-fbuf+strlen(vbuf)<MAX_LINELEN-1) {
  1609.             f[k]=pp; strcpy(pp,vbuf); pp+=strlen(pp)+1;
  1610.         }
  1611.         else f[k]="";
  1612.     }
  1613.     pos=eval_getpos(EV_X); posr=eval_getpos(EV_S);
  1614.     skip:
  1615.     if(step==0) step=1;/* if(step<0) step=-step;
  1616.     if(stop<start) {dd=start; start=stop; stop=dd;} */
  1617.     *p=0; v0=0;
  1618.     switch(type) {
  1619.         case 4:
  1620.         case 1: {
  1621.             pp=getvar("recursion_start");
  1622.             if(pp==NULL || *pp==0) v0=0;
  1623.             else {
  1624.                 v0=evalue(pp); if(!finite(v0)) return;
  1625.             }
  1626.             break;
  1627.         }
  1628.         case 2: v0=0; break;
  1629.         case 3: v0=1; break;
  1630.         case 5: break;
  1631.     }
  1632.     pp=p;
  1633.     if(type==5) {
  1634.         char *ps, *pt, buf2[MAX_LINELEN+1];
  1635.         int l,ln;
  1636.         *p=0; l=strlen(buf); if(l>=MAX_LINELEN) return;
  1637.         for(i=0,v=start; i<MAX_VALUE_LIST && v*step<=stop*step; v+=step, i++) {
  1638.             if(forstruct.type==for_from) {
  1639.                 float2str(v,vbuf); ps=vbuf;
  1640.             }
  1641.             else ps=forstruct.pos[i];
  1642.             strcpy(buf2,buf); l=strlen(ps);ln=strlen(EV_X);
  1643.             for(pt=varchr(buf2,EV_X);pt!=NULL;pt=varchr(pt+l,EV_X))
  1644.               string_modify(buf2,pt,pt+ln,"%s",ps);
  1645.             if(pp-p+strlen(buf2)>=MAX_LINELEN-1) return;
  1646.             if(pp>p) *pp++=','; strcpy(pp,buf2);
  1647.             pp+=strlen(pp);
  1648.         }
  1649.         return;
  1650.     }
  1651.     for(i=0,v=start; i<MAX_VALUE_LIST && v*step<=stop*step; v+=step, i++) {
  1652.         if(forstruct.type==for_from) eval_setval(pos,v);
  1653.         else eval_setval(pos,forstruct.list[i]);
  1654.         eval_setval(posr,v0);
  1655.         for(k=0; k<fcnt; k++) {
  1656.             set_evalue_error(0); set_evalue_pointer(f[k]); dd=_evalue(10);
  1657.             switch(type) {
  1658.                 case 1: {       /* values */
  1659.                     float2str(dd,vbuf);
  1660.                     if(pp-p+strlen(vbuf)<MAX_LINELEN-1) {
  1661.                         if(pp>p) *pp++=','; strcpy(pp,vbuf);
  1662.                         pp+=strlen(pp);
  1663.                     }
  1664.                     v0=dd; break;
  1665.                 }
  1666.                 case 2: {       /* sum */
  1667.                     v0=v0+dd; break;
  1668.                 }
  1669.                 case 3: {       /* product */
  1670.                     v0=v0*dd; break;
  1671.                 }
  1672.                 case 4: {       /* recursion */
  1673.                     v0=dd; break;
  1674.                 }
  1675.             }
  1676.         }
  1677.     }
  1678.     if(type!=1) float2str(v0,p);
  1679. }
  1680.  
  1681.         /* cut a function into values */
  1682. void calc_values(char *p)
  1683. { _values(p,1); }
  1684.  
  1685.         /* compute sum */
  1686. void calc_sum(char *p)
  1687. { _values(p,2); }
  1688.  
  1689.         /* compute product */
  1690. void calc_product(char *p)
  1691. { _values(p,3); }
  1692.  
  1693.         /* simple recursion */
  1694. void calc_recursion(char *p)
  1695. { _values(p,4); }
  1696.  
  1697.         /* List substitution */
  1698. void calc_makelist(char *p)
  1699. { _values(p,5); }
  1700.  
  1701.         /* level curve data */
  1702. void calc_leveldata(char *p)
  1703. {
  1704.     leveldata ld;
  1705.     char *sizep, *rangep, *fp, *levelp, *stepp;
  1706.     char *pp,*p2,fbuf[MAX_LINELEN+1],buf[MAX_LINELEN+1];
  1707.     double d[4];
  1708.     int i;
  1709.    
  1710.     sizep=wordchr(p,"size");
  1711.     rangep=wordchr(p,"ranges");
  1712.     fp=wordchr(p,"function");
  1713.     levelp=wordchr(p,"levels");
  1714.     stepp=wordchr(p,"step");
  1715.     if(sizep==NULL || rangep==NULL || fp==NULL) {
  1716.         syntax: module_error("syntax_error"); *p=0; return;
  1717.     }
  1718.     *sizep=0; sizep+=strlen("size");
  1719.     *rangep=0; rangep+=strlen("ranges");
  1720.     *fp=0; fp+=strlen("function");
  1721.     if(levelp!=NULL) {*levelp=0; levelp+=strlen("levels");}
  1722.     else levelp="0";
  1723.     if(stepp!=NULL) {*stepp=0; stepp+=strlen("step");}
  1724.     else stepp="0";
  1725.     mystrncpy(fbuf,fp,sizeof(fbuf)); substitute(fbuf);
  1726.     ld.fn=fbuf;
  1727.     ld.xname="x"; ld.yname="y"; ld.grain=evalue(stepp);
  1728.     mystrncpy(buf,sizep,sizeof(buf)); substitute(buf);
  1729.     for(i=0,pp=buf;i<2;i++,pp=p2) {
  1730.         if(*pp==0) goto syntax;
  1731.         p2=find_item_end(pp); if(*p2) *p2++=0;
  1732.         d[i]=evalue(pp);
  1733.     }
  1734.     ld.xsize=d[0]; ld.ysize=d[1];
  1735.     mystrncpy(buf,rangep,sizeof(buf)); substitute(buf);
  1736.     for(i=0,pp=buf;i<4;i++,pp=p2) {
  1737.         if(*pp==0) goto syntax;
  1738.         p2=find_item_end(pp); if(*p2) *p2++=0;
  1739.         d[i]=evalue(pp);
  1740.     }
  1741.     ld.xrange[0]=d[0]; ld.xrange[1]=d[1]; ld.yrange[0]=d[3]; ld.yrange[1]=d[2];
  1742.     mystrncpy(buf,levelp,sizeof(buf)); substitute(buf);
  1743.     ld.levelcnt=itemnum(buf); if(ld.levelcnt>LEVEL_LIM) ld.levelcnt=LEVEL_LIM;
  1744.     for(i=0,pp=buf;i<ld.levelcnt;i++,pp=p2) {
  1745.         if(*pp==0) goto syntax;
  1746.         p2=find_item_end(pp); if(*p2) *p2++=0;
  1747.         ld.levels[i]=evalue(pp);
  1748.     }
  1749.     levelcurve(&ld);
  1750.     for(i=0, pp=p; i<ld.datacnt && pp<p+MAX_LINELEN-16; i++) {
  1751.         float2str(ld.xdata[i],buf);
  1752.         if(pp-p+strlen(buf)<MAX_LINELEN-1) {
  1753.             if(pp>p) *pp++=';'; strcpy(pp,buf); pp+=strlen(pp);
  1754.         }
  1755.         float2str(ld.ydata[i],buf);
  1756.         if(pp-p+strlen(buf)<MAX_LINELEN-1) {
  1757.             if(pp>p) *pp++=','; strcpy(pp,buf); pp+=strlen(pp);
  1758.         }
  1759.     }
  1760. }
  1761.  
  1762.         /* internal routine with no security check */
  1763. void _lookup(char *p, char *fname)
  1764. {
  1765.     char buf1[MAX_LINELEN+1];
  1766.     char *p1, *p2, *mbuf;
  1767.  
  1768.     mbuf=readfile(fname,NULL,WORKFILE_LIMIT);
  1769.     if(mbuf==NULL) {abort: *p=0; return;}
  1770.     p1=find_word_start(p); strip_trailing_spaces(p1);
  1771.     snprintf(buf1,sizeof(buf1),"%s:",p1); substit(buf1);
  1772.     for(p1=strstr(mbuf,buf1);
  1773.         p1!=NULL && p1>mbuf && *(p1-1)!='\n';
  1774.         p1=strstr(p1+1,buf1));
  1775.     if(p1==NULL) {free(mbuf); goto abort;}
  1776.     p1+=strlen(buf1);
  1777.     for(p2=strchr(p1,'\n'); p2!=NULL; p2=strchr(p2+1,'\n')) {
  1778.         if(p2>p1 && *(p2-1)=='\\') {*(p2-1)=' '; continue;}
  1779.         else break;
  1780.     }
  1781.     if(p2==NULL) p2=p1+strlen(p1); else *p2=0;
  1782.     mystrncpy(p,p1,MAX_LINELEN); free(mbuf);
  1783. }
  1784.  
  1785.         /* lookup a definition in a definition file */
  1786. void calc_lookup(char *p)
  1787. {
  1788.     char buf2[MAX_LINELEN+1], buf3[MAX_FNAME+1];
  1789.     char *p2;
  1790.    
  1791.     p2=wordchr(p,"in"); if(p2==NULL) {abort: *p=0; return;}
  1792.     *p2=0;p2=find_word_start(p2+2);
  1793.     mystrncpy(buf2,p2,sizeof(buf2)); substit(buf2);
  1794.     *find_word_end(buf2)=0;
  1795.     if(strstr(buf2,parent_dir_string)!=NULL) {
  1796.         force_setvar("wims_error_data",buf2); module_error("illegal_cmd");
  1797.     }
  1798.     if(strncmp(buf2,"bases/",strlen("bases/"))!=0) {
  1799.         if(datafile_check(buf2)!=0 || find_module_file(buf2,buf3,0)!=0)
  1800.           goto abort;
  1801.         _lookup(p,buf3);
  1802.     }
  1803.     else _lookup(p,buf2);
  1804. }
  1805.  
  1806.         /* Hide name of a file. Only in module directory or in gifs/ */
  1807. void calc_rename(char *p)
  1808. {
  1809.     char buf1[MAX_LINELEN+1], buf2[MAX_LINELEN+1];
  1810.     char *p1, *ext, *s;
  1811.     int i,t;
  1812.    
  1813.     if(robot_access || strstr(p,"getfile")!=NULL) return;
  1814.     p1=find_word_start(p); *find_word_end(p1)=0;
  1815.     if(strncmp(p1,ref_name,strlen(ref_name))==0) p1+=strlen(ref_name);
  1816.     if(p1>p) strcpy(p,p1);
  1817.     if(strstr(p,parent_dir_string)!=NULL ||
  1818.        strncmp(p,"modules/adm/",strlen("modules/adm/"))==0) {
  1819.         badfile: force_setvar("wims_error_data",p); module_error("illegal_cmd");
  1820.     }
  1821.     if(strncmp(p,module_dir,strlen(module_dir))!=0 &&
  1822.        strncmp(p,"modules/data/",strlen("modules/data/"))!=0 &&
  1823.        strncmp(p,"gifs",strlen("gifs"))!=0) goto badfile;
  1824.     mkfname(buf1,"%s/getfile",session_prefix); mkdirs(buf1);
  1825.     mkfname(buf1,"%s/.rename",session_prefix);
  1826.     mystrncpy(buf2,p,sizeof(buf2)); _lookup(buf2,buf1);
  1827.     if(buf2[0]!=0) {    /* already */
  1828.         mystrncpy(p,buf2,MAX_LINELEN); return;
  1829.     }
  1830.     if(cwdbuf[0]==0) return;
  1831.     p1=p+strlen(p)-1;
  1832.     while(p1>p && isalnum(*p1)) p1--;
  1833.     if(p1>p && *p1=='.') ext=p1; else ext="";
  1834.     rerand: t=random();
  1835.     mkfname(buf1,"%s/%s",cwdbuf,p);
  1836.     mkfname(buf2,"%s/getfile/rename-%u%s",session_prefix,t,ext);
  1837.     if(ftest(buf2)>=0) goto rerand;
  1838.     i=symlink(buf1,buf2);
  1839.     s=getvar("wims_session"); if(s==NULL) return;
  1840.     if(good_httpd) snprintf(buf1,sizeof(buf1),
  1841.                             "getfile/rename-%u%s?session=%s", t,ext,s);
  1842.     else snprintf(buf1,sizeof(buf1),"%s?cmd=getfile&session=%s&special_parm=rename-%u%s",
  1843.                   ref_name, s, t,ext);
  1844.     snprintf(buf2,sizeof(buf2),"%s:%s\n",p,buf1);
  1845.     accessfile(buf2,"a","%s/.rename",session_prefix);
  1846.     mystrncpy(p,buf1,MAX_LINELEN);
  1847. }
  1848.  
  1849.         /* Pick up and translate imgrename(...) within a string */
  1850. void calc_imgrename(char *p)
  1851. {
  1852.     char buf[MAX_LINELEN+1], buf2[MAX_LINELEN+1];
  1853.     char *p1, *p2, *p3, *p4;
  1854.    
  1855.     for(p1=varchr(p,"imgrename"); p1!=NULL; p1=varchr(p1,"imgrename")) {
  1856.         p2=find_word_start(p1+strlen("imgrename"));
  1857.         if(*p2!='(') {p1=p2; continue;}
  1858.         p2++; p3=find_matching(p2,')');
  1859.         if(*p3!=')') {p1=p2-1; continue;}
  1860.         p2=find_word_start(p2); p4=find_word_end(p2);
  1861.         memmove(buf,p2,p4-p2); buf[p4-p2]=0;
  1862.         calc_rename(buf); *p3=0;
  1863.         snprintf(buf2,sizeof(buf2),"<img src=\"%s\"%s>",buf, p4);
  1864.         *p3=')'; p3++;
  1865.         string_modify(p,p1,p3,"%s",buf2);
  1866.         p1+=strlen(buf2);
  1867.     }
  1868.    
  1869. }
  1870.  
  1871.         /* internal use only */
  1872. void calc_unhttp(char *p)
  1873. {
  1874.     _http2env(tmplbuf,p); mystrncpy(p,tmplbuf,MAX_LINELEN);
  1875. }
  1876.  
  1877. /*
  1878. char *saltchar="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789./";
  1879. */
  1880.  
  1881. void calc_passcheck(char *p)
  1882. {
  1883.    
  1884. }
  1885.  
  1886. void calc_passcrypt(char *p)
  1887. {
  1888. #ifdef HAVE_CRYPT
  1889.     char saltstr[4];
  1890.     char *p1, *p2, *pp, *s, buf[MAX_LINELEN+1];
  1891.     saltstr[0]='N'; saltstr[1]='v'; saltstr[2]=0; buf[0]=0; pp=buf;
  1892.     for(p1=find_word_start(p);*p1;p1=find_word_start(p2)) {
  1893.         p2=find_word_end(p1); if(*p2) *p2++=0;
  1894.         pp=pp+strlen(pp);
  1895.         if(pp>buf) s=" "; else s="";
  1896.         if(*p1=='*')
  1897.           snprintf(pp,MAX_LINELEN-(pp-buf),"%s%s",s,p1);
  1898.         else
  1899.           snprintf(pp,MAX_LINELEN-(pp-buf),"%s*%s",s,crypt(p1,saltstr));
  1900.     }
  1901.     strcpy(p,buf);
  1902. #endif
  1903. }
  1904.  
  1905. void exec_readproc(char *p);
  1906.  
  1907.         /* crypted mail interface */
  1908. void calc_mailurl(char *p)
  1909. {
  1910.     char *p0, buf[MAX_LINELEN+1];
  1911.    
  1912.     if(robot_access) {*p=0; return;}
  1913.     snprintf(buf,sizeof(buf),"mailurl.proc %s",p);
  1914.     exec_readproc(buf);
  1915.     p0=getvar("mailurl_"); if(p0==NULL) p0="";
  1916.     mystrncpy(p,p0,MAX_LINELEN);
  1917. }
  1918.  
  1919.         /* get option word in a string */
  1920. void calc_getopt(char *p)
  1921. {
  1922.     char *p1, *p2, *p3, *p4;
  1923.     char buf1[MAX_LINELEN+1], buf2[MAX_LINELEN+1];
  1924.    
  1925.     p1=wordchr(p,"in"); if(p1==NULL) module_error("syntax error");
  1926.     *p1=0; p1=find_word_start(p1+3);
  1927.     mystrncpy(buf1,p,MAX_LINELEN); mystrncpy(buf2,p1,MAX_LINELEN);
  1928.     substitute(buf1); substitute(buf2);
  1929.     p1=find_word_start(buf1); *find_word_end(p1)=0;
  1930.     for(p2=buf2;*p2;p2++) {
  1931.         if(myisspace(*p2)) *p2=' ';
  1932.         if(*p2=='=') *p2='      ';
  1933.     }
  1934.     *p=0;
  1935.     p2=wordchr(buf2,p1); if(p2==NULL) return;
  1936.     for(p3=find_word_end(p2);myisspace(*p3);p3++) {
  1937.         if(*p3=='       ') {
  1938.             p3=find_word_start(p3);
  1939.             switch(*p3) {
  1940.                 case '"': {
  1941.                     p4=strchr(p3+1,'"');
  1942.                     goto tested;
  1943.                 }
  1944.                 case '(': {
  1945.                     p4=find_matching(p3+1,')');
  1946.                     goto tested;
  1947.                 }
  1948.                 case '[': {
  1949.                     p4=find_matching(p3+1,']');
  1950.                     goto tested;
  1951.                 }
  1952.                 case '{': {
  1953.                     p4=find_matching(p3+1,'}');
  1954.                     tested:
  1955.                     if(p4) {
  1956.                         p3++; *p4=0; break;
  1957.                     }
  1958.                     else goto nomatch;
  1959.                 }
  1960.                 default: {
  1961.                     nomatch:
  1962.                     *find_word_end(p3)=0;
  1963.                 }
  1964.             }
  1965.             mystrncpy(p,p3,MAX_LINELEN);
  1966.             return;
  1967.         }
  1968.     }
  1969.     *find_word_end(p2)=0;
  1970.     mystrncpy(p,p2,MAX_LINELEN);
  1971. }
  1972.  
  1973.         /* internal */
  1974. void _embraced(char buf[], void (app)(char *p))
  1975. {
  1976.     char *p1, *p2, buf2[MAX_LINELEN+1];
  1977.     for(p1=strchr(buf,'{'); p1; p1=strchr(p1,'{')) {
  1978.         p2=find_matching(p1+1,'}');
  1979.         if(p2==NULL) module_error("unmatched_parentheses");
  1980.         *p2=0; mystrncpy(buf2,p1+1,sizeof(buf2)); app(buf2);
  1981.         string_modify(buf,p1,p2+1,buf2);
  1982.     }
  1983. }
  1984.  
  1985.         /* embraced operations */
  1986. void calc_embraced(char *p)
  1987. {
  1988.     char *p1, *p2, buf[MAX_LINELEN+1];
  1989.  
  1990.     p1=find_word_start(p); p2=find_word_end(p1);
  1991.     if(*p2==0) {*p=0; return;}
  1992.     *p2++=0; p2=find_word_start(p2);
  1993.     mystrncpy(buf,p2,sizeof(buf)); substit(buf);
  1994.     if(p1>p) strcpy(p,p1); substit(p);
  1995.     if(strcmp(p,"randitem")==0) {_embraced(buf,calc_randitem); goto end;}
  1996.     if(strcmp(p,"extract")==0) {
  1997.         p1=strchr(buf,'{'); if(p1!=NULL) {
  1998.             p2=find_matching(++p1,'}');
  1999.             if(p2!=NULL) {
  2000.                 memmove(buf,p1,p2-p1); buf[p2-p1]=0;
  2001.             }
  2002.             else buf[0]=0;
  2003.         }
  2004.         else buf[0]=0;
  2005.         goto end;
  2006.     }
  2007.     if(strcmp(p,"delete")==0) {
  2008.         for(p1=strchr(buf,'{'); p1; p1=strchr(p1,'{')) {
  2009.             p2=find_matching(p1+1,'}');
  2010.             if(p2) strcpy(p1,p2+1); else p1++;
  2011.         }
  2012.         goto end;
  2013.     }
  2014.     module_error("syntax_error");
  2015.    
  2016.     end:
  2017.     mystrncpy(p,buf,MAX_LINELEN);
  2018. }
  2019.  
  2020. void calc_rows2lines(char *p)
  2021. {       rows2lines(p);  }
  2022.  
  2023. void calc_lines2rows(char *p)
  2024. {       lines2rows(p);  }
  2025.  
  2026.         /* check whether datamodules exist */
  2027. void calc_checkdata(char *p)
  2028. {
  2029.     char *p1, *p2, buf[MAX_LINELEN+1], nbuf[MAX_FNAME+1];
  2030.     struct stat st;
  2031.    
  2032.     memmove(p,"yes",4);
  2033.     p1=getvar("module_data");
  2034.     if(p1==NULL || *p1==0) return;
  2035.     snprintf(buf,sizeof(buf),"%s",p1);
  2036.     for(p2=buf; *p2; p2++) if(*p2==',' || *p2==';') *p2=' ';
  2037.     if(strstr(buf,"..")!=NULL) {
  2038.         snprintf(p,MAX_LINELEN,"badly_defined_data_module");
  2039.         return;
  2040.     }
  2041.     for(p1=find_word_start(buf); *p1; p1=find_word_start(p2)) {
  2042.         p2=find_word_end(p1); if(*p2) *p2++=0;
  2043.         snprintf(nbuf,sizeof(nbuf),"%s/%s/INDEX",module_dir,p1);
  2044.         if(stat(nbuf,&st)<0) {
  2045.             snprintf(nbuf,sizeof(nbuf),"%s/%s/index",module_dir,p1);
  2046.             if(stat(nbuf,&st)<0) {
  2047.                 snprintf(p,MAX_LINELEN,"%s",p1);
  2048.                 return;
  2049.             }
  2050.         }
  2051.     }
  2052. }
  2053.  
  2054. typedef struct {
  2055.     char *name;
  2056.     int tag;
  2057.     void (*routine) (char *p);
  2058. } MYFUNCTION;
  2059.  
  2060.         /* tag!=0 if we don't want automatic substit(). */
  2061. MYFUNCTION calc_routine[]={
  2062.       {"TeXmath",       0,      texmath},
  2063.       {"add",           1,      calc_sum},
  2064.       {"append",        1,      calc_append},
  2065.       {"call",          0,      calc_exec},
  2066.       {"char",          1,      calc_charof},
  2067.       {"charcnt",       0,      calc_lengthof},
  2068.       {"charcount",     0,      calc_lengthof},
  2069.       {"charno",        0,      calc_lengthof},
  2070.       {"charnum",       0,      calc_lengthof},
  2071.       {"chars",         1,      calc_charof},
  2072.       {"checkdata",     0,      calc_checkdata},
  2073.       {"checkdatamodule",0,     calc_checkdata},
  2074.       {"checkhost",     0,      calc_checkhost},
  2075.       {"column",        1,      calc_columnof},
  2076.       {"columns",       1,      calc_columnof},
  2077.       {"daemon",        0,      calc_daemon},
  2078.       {"date",          0,      calc_date},
  2079.       {"deaccent",      0,      deaccent},
  2080.       {"debug",         0,      calc_debug},
  2081.       {"declosing",     0,      calc_declosing},
  2082.       {"definitionof",  1,      calc_defof},
  2083.       {"defof",         1,      calc_defof},
  2084.       {"detag",         0,      calc_detag},
  2085. /*      {"dictionary",  1,      calc_dictionary},       */
  2086.       {"dir",           0,      calc_listfile},
  2087.       {"embraced",      1,      calc_embraced},
  2088.       {"encyclo",       0,      pedia},
  2089.       {"encyclopedia",  0,      pedia},
  2090.       {"eval",          0,      calc_evalue},
  2091.       {"evalsubst",     1,      calc_evalsubst},
  2092.       {"evalsubstit",   1,      calc_evalsubst},
  2093.       {"evalsubstitute",1,      calc_evalsubst},
  2094.       {"evalue",        0,      calc_evalue},
  2095.       {"evaluesubst",   1,      calc_evalsubst},
  2096.       {"evaluesubstit", 1,      calc_evalsubst},
  2097.       {"evaluesubstitute",1,    calc_evalsubst},
  2098.       {"examdep",       0,      calc_examdep},
  2099.       {"examscore",     0,      calc_examscore},
  2100.       {"exec",          0,      calc_exec},
  2101.       {"execute",       0,      calc_exec},
  2102.       {"filelist",      0,      calc_listfile},
  2103.       {"getdef",        1,      calc_defof},
  2104.       {"getopt",        1,      calc_getopt},
  2105.       {"getscore",      0,      calc_getscore},
  2106.       {"getscoremean",  0,      calc_getscoremean},
  2107.       {"getscorepercent",0,     calc_getscorepercent},
  2108.       {"getscoreremain",0,      calc_getscoreremain},
  2109.       {"getscorerequire",0,     calc_getscorerequire},
  2110.       {"getscorestatus",0,      calc_getscorestatus},
  2111.       {"getscoreweight",0,      calc_getscoreweight},
  2112.       {"hex",           0,      calc_hex},
  2113.       {"htmlmath",      0,      htmlmath},
  2114.       {"httpquery",     0,      tohttpquery},
  2115.       {"imgrename",     0,      calc_imgrename},
  2116.       {"instexst",      1,      calc_instexst},
  2117.       {"instexstatic",  1,      calc_instexst},
  2118.       {"item",          1,      calc_itemof},
  2119.       {"itemcnt",       0,      calc_itemnum},
  2120.       {"itemcount",     0,      calc_itemnum},
  2121.       {"itemno",        0,      calc_itemnum},
  2122.       {"itemnum",       0,      calc_itemnum},
  2123.       {"items",         1,      calc_itemof},
  2124.       {"items2lines",   0,      items2lines},
  2125.       {"items2words",   0,      items2words},
  2126.       {"itemstolines",  0,      items2lines},
  2127.       {"itemstowords",  0,      items2words},
  2128.       {"lengthof",      0,      calc_lengthof},
  2129.       {"leveldata",     1,      calc_leveldata},
  2130.       {"levelpoints",   1,      calc_leveldata},
  2131.       {"line",          1,      calc_lineof},
  2132.       {"linecnt",       0,      calc_linenum},
  2133.       {"linecount",     0,      calc_linenum},
  2134.       {"lineno",        0,      calc_linenum},
  2135.       {"linenum",       0,      calc_linenum},
  2136.       {"lines",         1,      calc_lineof},
  2137.       {"lines2items",   0,      lines2items},
  2138.       {"lines2list",    0,      lines2items},
  2139.       {"lines2rows",    0,      calc_lines2rows},
  2140.       {"lines2words",   0,      lines2words},
  2141.       {"linestoitems",  0,      lines2items},
  2142.       {"linestolist",   0,      lines2items},
  2143.       {"linestowords",  0,      lines2words},
  2144.       {"list2lines",    0,      items2lines},
  2145.       {"list2words",    0,      items2words},
  2146.       {"listcomplement",1,      calc_listcomplement},
  2147.       {"listfile",      0,      calc_listfile},
  2148.       {"listfiles",     0,      calc_listfile},
  2149.       {"listintersect", 1,      calc_listintersect},
  2150.       {"listintersection",1,    calc_listintersect},
  2151.       {"listtolines",   0,      items2lines},
  2152.       {"listtowords",   0,      items2words},
  2153.       {"listunion",     1,      calc_listunion},
  2154.       {"listuniq",      0,      calc_listuniq},
  2155.       {"listunique",    0,      calc_listuniq},
  2156.       {"listvar",       0,      mathvarlist},
  2157.       {"lookup",        1,      calc_lookup},
  2158.       {"lower",         0,      calc_tolower},
  2159.       {"lowercase",     0,      calc_tolower},
  2160.       {"ls",            0,      calc_listfile},
  2161.       {"mailurl",       0,      calc_mailurl},
  2162.       {"makelist",      1,      calc_makelist},
  2163.       {"mathsubst",     1,      calc_mathsubst},
  2164.       {"mathsubstit",   1,      calc_mathsubst},
  2165.       {"mathsubstitute",1,      calc_mathsubst},
  2166.       {"mexec",         0,      calc_mexec},
  2167.       {"module",        0,      calc_module},
  2168.       {"multiply",      1,      calc_product},
  2169.       {"non_empty",     0,      calc_nonempty},
  2170.       {"nonempty",      0,      calc_nonempty},
  2171.       {"nospace",       0,      nospace},
  2172.       {"nosubst",       1,      calc_subst},
  2173.       {"nosubstit",     1,      calc_subst},
  2174.       {"nosubstitute",  1,      calc_subst},
  2175.       {"passcheck",     0,      calc_passcheck},
  2176.       {"passcrypt",     0,      calc_passcrypt},
  2177.       {"pedia",         0,      pedia},
  2178.       {"perl",          0,      calc_perl},
  2179.       {"position",      1,      calc_pos},
  2180.       {"positionof",    1,      calc_pos},
  2181.       {"positions",     1,      calc_pos},
  2182.       {"prod",          1,      calc_product},
  2183.       {"product",       1,      calc_product},
  2184.       {"randchar",      0,      calc_randchar},
  2185.       {"randdouble",    0,      calc_randdouble},
  2186.       {"randfile",      0,      calc_randfile},
  2187.       {"randfloat",     0,      calc_randdouble},
  2188.       {"randint",       0,      calc_randint},
  2189.       {"randitem",      0,      calc_randitem},
  2190.       {"randline",      0,      calc_randline},
  2191.       {"random",        0,      calc_randdouble},
  2192.       {"randperm",      0,      calc_randperm},
  2193.       {"randpermute",   0,      calc_randperm},
  2194.       {"randreal",      0,      calc_randdouble},
  2195.       {"randrecord",    0,      calc_randfile},
  2196.       {"randrow",       0,      calc_randrow},
  2197.       {"randword",      0,      calc_randword},
  2198.       {"rawmath",       0,      rawmath},
  2199.       {"rawmatrix",     0,      rawmatrix},
  2200.       {"reaccent",      0,      reaccent},
  2201.       {"record",        1,      calc_recordof},
  2202.       {"recordcnt",     0,      calc_recordnum},
  2203.       {"recordcount",   0,      calc_recordnum},
  2204.       {"recordno",      0,      calc_recordnum},
  2205.       {"recordnum",     0,      calc_recordnum},
  2206.       {"records",       1,      calc_recordof},
  2207.       {"recursion",     1,      calc_recursion},
  2208.       {"reinput",       0,      calc_reinput},
  2209.       {"rename",        0,      calc_rename},
  2210.       {"replace",       1,      calc_replace},
  2211.       {"rootof",        1,      calc_solve},
  2212.       {"row",           1,      calc_rowof},
  2213.       {"rowcnt",        0,      calc_rownum},
  2214.       {"rowcount",      0,      calc_rownum},
  2215.       {"rowno",         0,      calc_rownum},
  2216.       {"rownum",        0,      calc_rownum},
  2217.       {"rows",          1,      calc_rowof},
  2218.       {"rows2lines",    0,      calc_rows2lines},
  2219.       {"run",           0,      calc_exec},
  2220.       {"select",        1,      calc_select},
  2221.       {"sh",            0,      calc_sh},
  2222.       {"shuffle",       0,      calc_randperm},
  2223.       {"singlespace",   0,      singlespace},
  2224.       {"slashsubst",    0,      slashsubst},
  2225.       {"solve",         1,      calc_solve},
  2226.       {"sort",          1,      calc_sort},
  2227. /*      {"sql",         0,      calc_sql}, */
  2228.       {"staticinstex",  1,      calc_instexst},
  2229.       {"stinstex",      1,      calc_instexst},
  2230.       {"subst",         0,      calc_subst},
  2231.       {"substit",       0,      calc_subst},
  2232.       {"substitute",    0,      calc_subst},
  2233.       {"sum",           1,      calc_sum},
  2234.       {"system",        0,      calc_sh},
  2235.       {"texmath",       0,      texmath},
  2236.       {"text",          1,      text},
  2237.       {"tohex",         0,      calc_hex},
  2238.       {"tolower",       0,      calc_tolower},
  2239.       {"toupper",       0,      calc_toupper},
  2240.       {"translate",     1,      calc_translate},
  2241.       {"trim",          0,      calc_trim},
  2242.       {"unhttp",        0,      calc_unhttp},
  2243.       {"upper",         0,      calc_toupper},
  2244.       {"uppercase",     0,      calc_toupper},
  2245.       {"values",        1,      calc_values},
  2246.       {"varlist",       0,      mathvarlist},
  2247.       {"word",          1,      calc_wordof},
  2248.       {"wordcnt",       0,      calc_wordnum},
  2249.       {"wordcount",     0,      calc_wordnum},
  2250.       {"wordno",        0,      calc_wordnum},
  2251.       {"wordnum",       0,      calc_wordnum},
  2252.       {"words",         1,      calc_wordof},
  2253.       {"words2items",   0,      words2items},
  2254.       {"words2lines",   0,      words2lines},
  2255.       {"words2list",    0,      words2items},
  2256.       {"wordstoitems",  0,      words2items},
  2257.       {"wordstolines",  0,      words2lines},
  2258.       {"wordstolist",   0,      words2items}
  2259. };
  2260. #define CALC_FN_NO (sizeof(calc_routine)/sizeof(calc_routine[0]))
  2261.  
  2262.