Subversion Repositories wimsdev

Rev

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