Subversion Repositories wimsdev

Rev

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