Subversion Repositories wimsdev

Rev

Rev 15517 | Rev 15704 | 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 server source.
  19.  * This file does execution commands.
  20.  */
  21. #include "wims.h"
  22.  
  23. static void _skip_contents(int isif);
  24.  
  25. /* common routine for the two if's. */
  26. static void _exec_if_while(char *p, int numerical, int isif)
  27. {
  28.   if(compare(p,numerical,0)==0) _skip_contents(isif); /* skip if false */
  29.   else if(!isif) m_file.for_idx++;
  30.   return;
  31. }
  32.  
  33.     /* 'if' non-numerical (unless comparisons are < or >, etc.) */
  34. void exec_if(char *p)
  35. {
  36.    _exec_if_while(p,0,1);
  37. }
  38.  
  39. /* 'if' numerical. */
  40. void exec_ifval(char *p)
  41. {
  42.   _exec_if_while(p,1,1);
  43. }
  44.  
  45. void _exec_while(char *p, int numerical)
  46. {
  47.   FOR_STACK *stk;
  48.   if(m_file.for_idx>=MAX_FOR_LEVEL) module_error("too_many_fors");
  49.   stk=&(m_file.for_stack[m_file.for_idx]);
  50.   stk->lineno=m_file.l;
  51.   memmove(stk->varname,"?",2);
  52.   _exec_if_while(p,numerical,0);
  53. }
  54.  
  55. /* 'while' non-numerical (unless comparisons are < or >, etc.) */
  56. void exec_while(char *p)
  57. {
  58.   _exec_while(p,0);
  59. }
  60.  
  61. /* 'while' numerical. */
  62. void exec_whileval(char *p)
  63. {
  64.   _exec_while(p,1);
  65. }
  66.  
  67. void exec_endwhile(char *p)
  68. {
  69.   FOR_STACK *stk;
  70.   if(m_file.for_idx<=0) module_error("next_without_for");
  71.   stk=&(m_file.for_stack[m_file.for_idx-1]);
  72.   if(stk->varname[0]!='?') module_error("next_without_for");
  73.   m_file.for_idx--; *p=0;
  74.   m_file.linepointer=stk->lineno;
  75.   executed_gotos++;
  76.   if(executed_gotos>=GOTO_LIMIT) module_error("too_many_gotos");
  77. }
  78.  
  79. /* Should provide a method to stop infinite loop. */
  80. void exec_goto(char *p)
  81. {
  82.   char lbuf[MAX_NAMELEN+17];
  83.   char *label_start, *line_start;
  84.   int old_line;
  85.   int i;
  86.   executed_gotos++;
  87.   if(executed_gotos>=GOTO_LIMIT) module_error("too_many_gotos");
  88.   old_line=m_file.l;
  89.   label_start=find_word_start(p);
  90.   *find_word_end(label_start)=0;
  91.   m_file.l=m_file.linepointer=0;
  92.   for(i=0;i<m_file.linecnt;i++) {
  93.     if(((m_file.lines[i].isstart)&4)==0) continue;
  94.     line_start=find_word_start((m_file.lines[i]).address);
  95.     if(line_start>(m_file.lines[i+1]).address && i<m_file.linecnt)
  96.       continue;
  97.     m_file.linepointer=i;
  98.     wgetline(lbuf,MAX_NAMELEN+8,&m_file);
  99.     line_start=find_word_start(lbuf);
  100.     if(*line_start!=label_prefix_char) continue;
  101.     line_start++;*find_word_end(line_start)=0;
  102.     if(line_start[0]!='*' && strcmp(line_start,label_start)!=0) continue;
  103.     *p=0; i++; return;
  104.   }
  105.   m_file.l=old_line;
  106.   setvar(error_data_string,label_start); module_error("label_not_found");
  107. /*    m_file.linepointer=m_file.linecnt; *p=0; return;
  108. */
  109. }
  110.  
  111. /* 'else' with or without 'if'.
  112.  * Philosophy: we implement a loosely checked grammar.
  113.  * We cannot check 'if' grammar if we want to use 'goto'
  114.  * together with 'if'.
  115.  */
  116. void exec_else(char *p)
  117. {
  118.   _skip_contents(1); return;
  119. }
  120.  
  121. /* 'endif': nothing needs to be done here. */
  122. void exec_endif(char *p)
  123. {
  124.   return;
  125. }
  126.  
  127. /* find out the end of a for loop */
  128. void goto_for_end(void)
  129. {
  130.   int inner;
  131.   char buf[16];
  132.   inner=0;
  133.   while(m_file.linepointer<m_file.linecnt) {
  134.     if((m_file.lines[m_file.linepointer].isstart&2)==0) {
  135.       m_file.linepointer++; continue;
  136.     }
  137.     if(wgetline(buf,8,&m_file)==EOF) break;
  138.     *find_word_end(buf)=0;
  139.     if(strcmp(buf,"!for")==0) {
  140.       inner++; continue;
  141.     }
  142.     if(strcmp(buf,"!next")==0) {
  143.       if(inner>0) {
  144.         inner--; continue;
  145.       }
  146.       else break;
  147.     }
  148.   }
  149. }
  150.  
  151. /* for */
  152. void exec_for(char *p)
  153. {
  154.   char *p1, *p2, *p3;
  155.   double v1, v2, step;
  156.   char buf[MAX_LINELEN+1];
  157.   FOR_STACK *stk;
  158.  
  159.   if(m_file.for_idx>=MAX_FOR_LEVEL) module_error("too_many_fors");
  160.   stk=&(m_file.for_stack[m_file.for_idx]);
  161.   stk->lineno=m_file.l;
  162.   p1=find_word_start(p);
  163.   for(p2=p1; !isspace(*p2) && *p2!=0 && *p2!='=' ; p2++);
  164.   if(*p2==0) syntax: module_error("for_syntax");
  165.   if(p2-p1>MAX_NAMELEN) module_error("name_too_long");
  166.   memmove(stk->varname,p1,p2-p1);
  167.   stk->varname[p2-p1]=0;
  168.   p1=find_word_start(p2); if(*p1==0) goto syntax;
  169.   if(*p1=='=') {
  170.     p1++;
  171.     assign:
  172.     stk->from=0;
  173.     p2=wordchr(p1,"to");
  174.     if(p2==NULL) p2=strstr(p1,"..");
  175.     if(p2==NULL) goto syntax;
  176.     *p2=0; p2+=2;
  177.     p3=wordchr(p2,"step");
  178.     if(p3!=NULL) {
  179.       *p3=0; p3+=strlen("step");
  180.       step=evalue(p3);
  181.       if(step==0) module_error("zero_step");
  182.     }
  183.     else step=1;
  184.       v1=evalue(p1); v2=evalue(p2);
  185.       float2str(v1,buf); setvar(stk->varname, buf);
  186.       if((step>0 && v1>v2) || (step<0 && v1<v2) ) {  /* condition not met */
  187.   loop_out:
  188.         goto_for_end();
  189.       }
  190.     else {
  191.       stk->varval=v1; stk->varend=v2; stk->step=step;
  192.       stk->lineno=m_file.linepointer;
  193.       m_file.for_idx++;
  194.     }
  195.     *p=0; return;
  196.   }
  197.   if(memcmp(p1,"from",strlen("from"))==0 && isspace(*(p1+strlen("from")))) {
  198.     p1+=strlen("from"); goto assign;
  199.   }
  200.   if(memcmp(p1,"in",strlen("in"))==0 && isspace(*(p1+strlen("in")))) {
  201.     stk->from=1; p1+=strlen("in");
  202.     p1=find_word_start(p1); if(*p1==0) goto loop_out;
  203.     p2=xmalloc(MAX_LINELEN+1);
  204.     mystrncpy(p2,p1,MAX_LINELEN);
  205.     substit(p2);
  206.     strip_trailing_spaces(p2);
  207.     if(*p2==0) goto loop_out;
  208.     p1=strparchr(p2,','); stk->bufpt=p2;
  209.     if(p1!=NULL) {
  210.       *p1=0; stk->list_pt=find_word_start(p1+1);
  211.     }
  212.     else stk->list_pt=NULL;
  213.     strip_trailing_spaces(p2); setvar(stk->varname,p2);
  214.     stk->lineno=m_file.linepointer;
  215.     m_file.for_idx++; *p=0; return;
  216.   }
  217.   goto syntax;
  218. }
  219.  
  220. /* break a for loop */
  221. void exec_break(char *p)
  222. {
  223.   FOR_STACK *stk;
  224.   if(m_file.for_idx>0) {
  225.     stk=&(m_file.for_stack[m_file.for_idx-1]);
  226.     if(stk->varname[0]=='?') _skip_contents(0);
  227.     else goto_for_end();
  228.     m_file.for_idx--;
  229.   }
  230.   *p=0; return;
  231. }
  232.  
  233. /* next */
  234. void exec_next(char *p)
  235. {
  236.   double v1, v2, step;
  237.   char *p1, *p2;
  238.   char buf[MAX_LINELEN+1];
  239.   FOR_STACK *stk;
  240.   if(m_file.for_idx<=0) module_error("next_without_for");
  241.   stk=&(m_file.for_stack[m_file.for_idx-1]);
  242.   if(stk->varname[0]=='?') module_error("next_without_for");
  243.   if(stk->from) { /* list style */
  244.     if(stk->list_pt==NULL) {
  245.       free(stk->bufpt);
  246.       m_file.for_idx--;
  247.       *p=0; return;
  248.     }
  249.     p1=strchr(stk->list_pt,',');
  250.     if(p1!=NULL) {
  251.       *p1=0; p2=find_word_start(p1+1);
  252.     }
  253.     else p2=NULL;
  254.     strip_trailing_spaces(stk->list_pt);
  255.     setvar(stk->varname,stk->list_pt);
  256.     stk->list_pt=p2; goto loop_back;
  257.   }
  258.   v1=stk->varval; v2=stk->varend; step=stk->step;
  259.   v1+=step; stk->varval=v1;
  260.   float2str(v1,buf);
  261.   setvar(stk->varname, buf);
  262.   if((step>0 && v1<=v2) || (step<0 && v1>=v2)) { /* loop */
  263.     loop_back:
  264.     m_file.linepointer=stk->lineno;
  265.     executed_gotos++;
  266.     if(executed_gotos>=GOTO_LIMIT) module_error("too_many_gotos");
  267.   }
  268.   else m_file.for_idx--;
  269.   *p=0; return;
  270. }
  271.  
  272. /* Execution of a file in the module directory,
  273.  * only for trusted modules.
  274.  */
  275. void exec_mexec(char *p)
  276. {
  277.   direct_exec=1;
  278.   calc_mexec(p);
  279.   direct_exec=0;
  280. }
  281.  
  282. /* call shell. */
  283. void _exec_ex(char *p, char *arg0, char *arg1, int n)
  284. {
  285.   char *abuf[8];
  286.   char errorfname[MAX_FNAME+1];
  287.  
  288.   if(robot_access) {
  289.     *p=0; return;
  290.   }
  291.   if(!noout || !trusted_module() || is_class_module) {
  292.     _calc_exec(p,arg0,arg1,n); if(outputing) output0(p);
  293.     return;
  294.   }
  295.   mkfname(errorfname,"%s/exec.err",tmp_dir);
  296.   abuf[0]=arg0; abuf[1]=arg1; abuf[n]=p; abuf[n+1]=NULL;
  297.   wrapexec=1; exportall();
  298.   execredirected(abuf[0],NULL,NULL,errorfname,abuf);
  299. }
  300.  
  301. /* call shell. */
  302. void exec_sh(char *p)
  303. {
  304.   _exec_ex(p,"sh","-c",2);
  305. }
  306.  
  307. /* call perl. */
  308. void exec_perl(char *p)
  309. {
  310.   _exec_ex(p,"perl","-e",2);
  311. }
  312.  
  313. /* file should not be cached */
  314. void exec_nocache(char *p)
  315. {    m_file.nocache|=1; *p=0; }
  316.  
  317. /*
  318.  * Read another file.
  319.  * Read a file in order to perform tests.
  320.  * Relevant global variables:
  321.  * - m_file, a WORKING_FILE structure (should be restored to its original
  322.  *   value at the end of the procedure)
  323.  * - untrust, an integer (should be restored to its original
  324.  *   value at the end of the procedure)
  325.  * - outputing, an integer; its value is juste read.
  326.  * - readnest, an integer, which is incremented during the function
  327.  *   and decremented when it finishes.
  328.  * Relevant global variables used in subprograms:
  329.  * - module_prefix, a const char * which will be preprended before
  330.  *   the filename, (see lines.c, label = openit).
  331.  * Relevant wims_environment parameters:
  332.  * - wims_read_parm  (should be restored to its original
  333.  *   value at the end of the procedure)
  334.  * - wims_trustfile: this parameter is only read.
  335.  * Important procedures called by this one:
  336.  * - phtml_put(char*, int)
  337.  * - var_proc(char*, int)
  338.  * - module_error(const char*)
  339.  * @param p pointer to the filename, possibly followed by parameters
  340.  */
  341.  
  342. void exec_read(char *p)
  343. {
  344.   WORKING_FILE save;
  345.   char parmsave[MAX_LINELEN+1];
  346.   char *pp,*ps;
  347.   int t,cache;
  348.  
  349.   strip_trailing_spaces(p);p=find_word_start(p);
  350.   if(*p==0) return;
  351.   pp=find_word_end(p); if(*pp) *pp++=0;
  352.   pp=find_word_start(pp);
  353.   ps=getvar("wims_read_parm"); if(ps==NULL) ps="";
  354.   mystrncpy(parmsave,ps,sizeof(parmsave));
  355.   setvar("wims_read_parm",pp);
  356.   memmove(&save,&m_file,sizeof(WORKING_FILE));
  357.   cache=1; if(p[0]=='.' && p[1]=='/') {p+=2; cache=0;}
  358.   t=untrust;
  359.   if(strncmp(p,"wimshome/",9)==0) {
  360.     if((untrust&255)!=0 &&
  361.         (m_file.name[0]=='/' || strncmp(m_file.name,"wimshome/",9)==0))
  362.       module_error("Illegal_file_access");
  363.     untrust|=0x200;
  364.   }
  365.   if((untrust&0x202)!=0) {
  366.     pp=getvar("wims_trustfile");
  367.     if(pp!=NULL && *pp!=0 && wordchr(pp,p)!=NULL)
  368.       untrust&=0xfdfd;
  369.   }
  370.   readnest++; if(readnest>MAX_READNEST) module_error("too_many_nested_read");
  371.   if(outputing) phtml_put(p,cache); else var_proc(p,cache);
  372.   readnest--; untrust=t;
  373.   memmove(&m_file,&save,sizeof(WORKING_FILE));
  374.   if (trace_file){
  375.     int i;
  376.     putc('\n',trace_file);
  377.     for(i=1; i<=2*trace_indent-2; i++) putc(' ',trace_file);
  378.     fprintf(trace_file,"%s:",m_file.filepath); fflush(trace_file);
  379.   }
  380.   setvar("wims_read_parm",parmsave);
  381. }
  382.  
  383. /* read a variable processing file */
  384. void exec_readproc(char *p)
  385. {
  386.   int o=outputing; outputing=0; exec_read(p); outputing=o;
  387. }
  388.  
  389. void exec_defread(char *p)
  390. {
  391.   int t,o;
  392.   secure_exec();
  393.   t=untrust; untrust|=0x400;
  394.   o=outputing; outputing=0;
  395.   exec_read(p); untrust=t; outputing=o;
  396. }
  397.  
  398. /* Change to another file (no return) */
  399. void exec_changeto(char *p)
  400. {
  401.   m_file.linepointer=m_file.linecnt;
  402.   exec_read(p);
  403. }
  404.  
  405. int header_executed=0, tail_executed=0;
  406.  
  407. /* internal routine: get other language versions */
  408. void other_langs(void)
  409. {
  410.   int i,j,k;
  411.   char *p, lbuf[4], pbuf[MAX_FNAME+1], *phtml;
  412.   char namebuf[MAX_FNAME+1], listbuf[4*MAX_LANGUAGES];
  413.   static int otherlangs_got=0;
  414.  
  415.   if(otherlangs_got) return;
  416.   mystrncpy(namebuf,module_prefix,sizeof(namebuf));
  417.   listbuf[0]=0;j=strlen(namebuf)-3; p=listbuf;
  418.   if(j>=2 && strcmp("light",namebuf+j-2)==0) {
  419.     setvar("wims_light_module","yes");
  420.     phtml=getvar("phtml");
  421.     if(phtml==NULL || *phtml==0) return;
  422.     mystrncpy(pbuf,phtml,sizeof(pbuf));
  423.     j=strlen(pbuf)-3;
  424.     if(pbuf[j]!='.') return;
  425.     j++; ovlstrcpy(lbuf,pbuf+j); pbuf[j]=0;
  426.     j=strlen(namebuf);
  427.     snprintf(namebuf+j,MAX_FNAME-j-10,"/pages/%s",pbuf);
  428.     j=strlen(namebuf);
  429.     for(i=k=0;i<available_lang_no;i++) {
  430.       if(strcmp(lbuf,available_lang[i])==0) continue;
  431.       mystrncpy(namebuf+j,available_lang[i],MAX_FNAME-j);
  432.       if(ftest(namebuf)<0) continue;
  433.       if(k>0) *p++=',';
  434.       mystrncpy(p,available_lang[i],sizeof(listbuf)-4-(p-listbuf));
  435.       p+=strlen(p);
  436.       k++;
  437.     }
  438.   goto end;
  439.   }
  440.   if(j>0 && namebuf[j]=='.') {
  441.     setvar("wims_light_module","");
  442.     j++; ovlstrcpy(lbuf,namebuf+j); namebuf[j]=0;
  443.     for(i=k=0;i<available_lang_no;i++) {
  444.       if(strcmp(lbuf,available_lang[i])==0) continue;
  445.       snprintf(namebuf+j,MAX_FNAME-j,"%s/main.phtml",
  446.            available_lang[i]);
  447.       if(ftest(namebuf)<0) continue;
  448.       if(k>0) *p++=',';
  449.       mystrncpy(p,available_lang[i],sizeof(listbuf)-4-(p-listbuf));
  450.       p+=strlen(p);
  451.       k++;
  452.     }
  453.   }
  454.   end: setvar("wims_otherlangs",listbuf); otherlangs_got=1;
  455. }
  456.  
  457. /* Standardised reference to wims home */
  458. void exec_homeref(char *p)
  459. {
  460.   char *ref, *user;
  461.  
  462.   if(ismhelp || tail_executed) return;
  463.   setvar("wims_homeref_parm",p); *p=0;
  464.   user=getvar("wims_user");
  465.   if(user==NULL) user="";
  466.   if(*user==0 || robot_access) ref=home_referer;
  467.   else {
  468.     if(strcmp(user,"supervisor")==0) ref=home_referer_supervisor;
  469.     else ref=home_referer_user;
  470.   }
  471.   if(user[0]==0 && !robot_access) other_langs();
  472.   phtml_put_base(ref,0); tail_executed=1;
  473. }
  474.  
  475. /* Standardised header menu */
  476. void exec_headmenu(char *p)
  477. {
  478.   char *ref, *user;
  479.  
  480.   if(header_executed) return;
  481.   setvar("wims_headmenu_parm",p); *p=0;
  482.   user=getvar("wims_user");
  483.   if(user==NULL) user="";
  484.   if(*user==0 || robot_access) ref=header_menu;
  485.   else {
  486.     if(strcmp(user,"supervisor")==0) ref=header_menu_supervisor;
  487.     else ref=header_menu_user;
  488.   }
  489.   if(user[0]==0 && !robot_access) other_langs();
  490.   phtml_put_base(ref,0); header_executed=1;
  491. }
  492.  
  493. /* uniformized title */
  494. void exec_title(char *p)
  495. {
  496.   char *s;
  497.   *p=0;
  498.   if(!outputing) return;
  499.   s=getvar("wims_title_title");
  500.   if(s==NULL || *s==0) {
  501.     s=getvar("module_title");
  502.     if(s==NULL || *s==0) return;
  503.     force_setvar("wims_title_title",s);
  504.   }
  505.   phtml_put_base(title_page,0);
  506. }
  507.  
  508. /* standardized html tail */
  509. void exec_tail(char *p)
  510. {
  511.   if(!outputing || tail_executed) {
  512.     *p=0; return;
  513.   }
  514.   if(!ismhelp) exec_homeref(p);
  515.   *p=0;
  516.   _output_("</body></html>");
  517.   tail_executed=1;
  518. }
  519.  
  520. void exec_formend(char *p)
  521. {
  522.   _output_("\n</div></form>");
  523. }
  524.  
  525. void determine_font(char *l);
  526.  
  527. /* see changelog wims_mathml for history */
  528. void _headmathjax (char *p)
  529. {
  530.   _output_("\n<script>/*<![CDATA[*/\
  531. function wims_mathml_zoom(id){\
  532. var math = document.getElementById(id);\
  533. if(math.getAttribute(\"mathsize\") == \"100%\"){\
  534.  math.setAttribute(\"mathsize\",\"200%\");}else{\
  535.  math.setAttribute(\"mathsize\",\"100%\");};\
  536. };\
  537. window.addEventListener(\"load\", function() {\
  538.  var ua = navigator.userAgent.toLowerCase();\
  539.  var gecko = ua.indexOf(\"gecko\") > -1 && ua.indexOf(\"khtml\") === -1 && ua.indexOf(\"trident\") === -1;\
  540.  if(! gecko ){\
  541.    var script = document.createElement(\"script\");\
  542.    script.src  = \"scripts/js/mathjax/MathJax.js?config=MML_CHTML-full\";\
  543.    document.head.appendChild(script);\
  544.  };\
  545. });/*]]>*/</script>\n");
  546.  
  547.   // Safari/MathJax bugfix : refresh display when document ready.
  548.   _output_("\n<script>\
  549. reload_mathML=function(){var t=document.getElementsByClassName(\"wims_mathml\");Array.prototype.filter.call(t,function(t){var e=t.style.display;t.style.display=\"none\",t.style.opacity=\"0\",setTimeout(function(){t.style.display=e},1e3),setTimeout(function(){t.style.opacity=\"1\"},1100)})},document.onreadystatechange=function(){\"complete\"===document.readyState&&(/^((?!chrome|android).)*safari/i.test(navigator.userAgent)&&reload_mathML())};\
  550. </script>");
  551. }
  552.  
  553. /* standardized header */
  554. void _header(char *p, int option)
  555. {
  556.   char *s1, *s2, hbuf[MAX_LINELEN+1], *ws="", *ws2="", *bo, *ol;
  557.   char wsbuf[MAX_LINELEN+1],wsbuf2[MAX_LINELEN+1];
  558.   setvar("wims_header_parm",p); *p=0;
  559.   if(!outputing || header_executed) return;
  560.   s1=getvar("wims_window");
  561.   if(mode==mode_popup) {
  562.     if(s1!=NULL && *s1!=0) {
  563.       char *p1, *p2;
  564.       int t1,t2/*,t3,t4*/;
  565.       p1=find_word_start(s1);
  566.       for(p2=p1; myisdigit(*p2); p2++);
  567.       *p2=0; t1=atoi(p1); p1=p2+1;
  568.       while(!myisdigit(*p1) && *p1) p1++;
  569.       for(p2=p1; myisdigit(*p2); p2++);
  570.       *p2=0; t2=atoi(p1); p1=p2+1;
  571. /*        while(!myisdigit(*p1) && *p1) p1++;
  572.         for(p2=p1; myisdigit(*p2); p2++);
  573.         *p2=0; t3=atoi(p1); p1=p2+1;
  574.         while(!myisdigit(*p1) && *p1) p1++;
  575.         for(p2=p1; myisdigit(*p2); p2++);
  576.         *p2=0; t4=atoi(p1); p1=p2+1;
  577.         while(!myisdigit(*p1) && *p1) p1++;
  578.         for(p2=p1; myisdigit(*p2); p2++);
  579.         if(t3<5) t3=5; if(t4<20) t4=20;
  580. */     snprintf(wsbuf,sizeof(wsbuf),
  581.           "window.focus();window.resizeTo(%d,%d);",t1,t2); ws=wsbuf;
  582. /*      snprintf(wsbuf,sizeof(wsbuf),
  583.              "window.focus();window.resizeto(%d,%d);window.moveto(%d,%d);",
  584.              t1,t2,t3,t4); ws=wsbuf;
  585. */   }
  586.   }
  587.   else {
  588.     if(s1!=NULL && strcmp(s1,"new")==0)
  589.       ws="window.focus();window.resizeTo(800,640);window.moveTo(15,35);";
  590.   }
  591.   if(strstr(session_prefix,"_exam")!=NULL) {
  592. /*    char buf[64]; */
  593.     if(*ws==0) ws="window.focus();";
  594.     else ws= "window.focus();window.moveTo(5,70);";
  595. /*    snprintf(buf,sizeof(buf),"name.phtml.%s",lang);
  596.       phtml_put_base(buf);
  597.       phtml_put_base("jsclock.phtml"); */
  598.   }
  599.   s1=getvar("wims_html_header"); if(s1==NULL) s1="";
  600.   determine_font(getvar("module_language"));
  601.   s2=getvar("module_title"); if(s2!=NULL && *s2!=0) {
  602.     mystrncpy(hbuf,s2,sizeof(hbuf)); calc_detag(hbuf);
  603.     setvar("module_title2",hbuf);
  604.   }
  605.   mystrncpy(hbuf,s1,sizeof(hbuf)); substit(hbuf);
  606.   s2=getvar("wims_htmlbody"); if(s2==NULL) s2="";
  607.   bo=getvar("wims_html_bodyoption"); if(bo==NULL) bo="";
  608.   ws2=getvar("wims_html_onload"); if(ws2==NULL) ws2="";
  609.   snprintf(wsbuf2,sizeof(wsbuf2),"%s%s",ws,ws2);
  610.   setvar("wims_html_onload",wsbuf2);
  611.   if(wsbuf2[0]) ol=" onload="; else ol="";
  612.  
  613.   output("<!DOCTYPE html>\
  614.    \n<html lang=\"%s\"><head>\n\
  615.    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, minimum-scale=1.0\"/>\n\
  616.    %s\n", lang, hbuf);
  617.   if(robot_access){
  618.     output("<link rel=\"stylesheet\" href=\"html/themes/_css/robots.css\" />");
  619.   }
  620.  
  621.   _headmathjax(p);
  622.  
  623.   if(ol[0]) {
  624.     output("</head>\n<body %s %s\"%s\" %s>", s2, ol, wsbuf2, bo);}
  625.   else {
  626.     output("</head>\n<body %s %s%s %s>", s2, ol, wsbuf2, bo);
  627.   }
  628.   exec_headmenu(p);
  629.   if(option) exec_title(p);
  630.   if(cmd_type==cmd_help) {
  631.     char *s=getvar("special_parm");
  632.     if(s==NULL) s="";
  633.     m_file.linepointer=m_file.linecnt;
  634.     if(strcmp(s,"about")==0) ovlstrcpy(hbuf,"about.phtml");
  635.     else ovlstrcpy(hbuf,"help.phtml");
  636.     exec_read(hbuf); exec_tail(p); /* param of exec_...() must be readable */
  637.     return;
  638.   }
  639.   header_executed=1;
  640. }
  641.  
  642. void exec_header(char *p) { _header(p,1);}
  643. void exec_header1(char *p) { _header(p,0);}
  644. void exec_headmathjax(char *p) { _headmathjax(p);}
  645.  
  646. char href_target[128];
  647. char jsbuf[512];
  648. #define jsstr " onclick=\"%s=window.open('','%s','status=no,toolbar=no,location=no,menubar=no,scrollbars=yes,resizable=yes')\""
  649. int ref_mhelp=0;
  650. int follow_list[]={
  651.   ro_session, ro_lang, ro_useropts, ro_module
  652. };
  653. #define follow_no (sizeof(follow_list)/sizeof(follow_list[0]))
  654.  
  655. void _httpfollow(char b1[], char *wn, int new)
  656. {
  657.   int i;
  658.   char *p1, *s, *ss, sb[MAX_LINELEN+1], qbuf[MAX_LINELEN+1];
  659.  
  660.   sb[0]=0;
  661.   for(i=0;i<follow_no;i++) {
  662.     if(robot_access && follow_list[i]==ro_session) continue;
  663.     if(!new && follow_list[i]!=ro_session
  664.         && follow_list[i]!=ro_module && follow_list[i]!=ro_lang)
  665.       continue;
  666.     if(follow_list[i]==ro_module) {
  667.       char *pp;
  668.       if(new) continue;
  669.       pp=strstr(b1,"cmd=");
  670.       if(pp==NULL) continue;
  671.       pp+=strlen("cmd=");
  672.       if(memcmp(pp,"intro",strlen("intro"))==0 ||
  673.          memcmp(pp,"new",strlen("new"))==0) continue;
  674.     }
  675.     s=getvar(ro_name[follow_list[i]]);
  676.     ss=strstr(b1,ro_name[follow_list[i]]);
  677.     if(s!=NULL && *s!=0 &&
  678.       (ss==NULL || (ss>b1 && *(ss-1)!='&')
  679.        || *(ss+strlen(ro_name[follow_list[i]]))!='=')) {
  680.       if(follow_list[i]==ro_session && memcmp(href_target,"wims_",5)==0) {
  681.         char st[MAX_LINELEN+1];
  682.         char *s1;
  683.         s1=getvar("wims_session");
  684.         if(s1==NULL) internal_error("exec_href() error.\n");
  685.         if(ref_mhelp) {
  686.           if(strstr(s1,"_mhelp")!=0)
  687.             snprintf(st,sizeof(st),"%s",s1);
  688.           else
  689.             snprintf(st,sizeof(st),"%s_mhelp",s1);
  690.         }
  691.         else snprintf(st,sizeof(st),"%.10s%s",s1,href_target+4);
  692.           s=st;
  693.       }
  694.       snprintf(sb+strlen(sb),MAX_LINELEN-strlen(sb),"%s=%s&",
  695.              ro_name[follow_list[i]],s);
  696.       if(ismhelp && follow_list[i]==ro_session &&
  697.            strstr(sb,"_mhelp")==NULL)
  698.         snprintf(sb+strlen(sb)-1,MAX_LINELEN-strlen(sb)+1,"_mhelp&");
  699.       }
  700.   }
  701.   snprintf(qbuf,MAX_LINELEN,"%s%s%s",wn,sb,b1);
  702.   /* cleaning up query string */
  703.   for(p1=qbuf;*p1;p1++) {
  704.     if(*p1=='"') string_modify(qbuf,p1,p1+1,"%22");
  705.     if(*p1=='&' && isalpha(*(p1+1))) {
  706.       p1++; string_modify(qbuf,p1,p1,"+");
  707.     }
  708.   }
  709.   mystrncpy(b1,qbuf,MAX_LINELEN);
  710. }
  711.  
  712. /* Restart with a new module, using http code 302. */
  713. void exec_restart(char *p)
  714. {
  715.   /* buf will contain all the url parameters*/
  716.   /* buf2 will be the final URL */
  717.   char buf[MAX_LINELEN+1], *rfn, buf2[MAX_LINELEN+1];
  718.   /* long int t; */
  719.  
  720.   if(robot_access || outputing || !trusted_module() || is_class_module) return;
  721.   /* accessfile(buf,"r","%s/restart.time",s2_prefix);
  722.     t=atoi(buf); if(t==nowtime) return;    */ /* possible looping */
  723.   mystrncpy(buf,find_word_start(p),sizeof(buf));
  724.   *find_word_end(buf)=0;
  725.   _httpfollow(buf,"",0);
  726.   nph_header(302);
  727.   rfn=strchr(ref_name,':');
  728.   if(rfn==NULL) {
  729.     usual: snprintf(buf2,sizeof(buf2),"%s?%s",ref_name,buf);
  730.   }
  731.   else {
  732.     char *p;
  733.     p=getvar("wims_protocol");
  734.     if(p!=NULL && strcmp(p,"https")==0) {
  735.       snprintf(buf2,sizeof(buf2),"https%s?%s",rfn,buf);
  736.     }
  737.     else goto usual;
  738.   }
  739.   printf("Location: %s\r\n\r\n\
  740. <!DOCTYPE html>\
  741. <html><body><a href=\"%s\">%s</a></body></html>",buf2,buf2,buf2);
  742.   close_working_file(&m_file,0); write_logs();
  743.   snprintf(buf,sizeof(buf),"%ld",(long)nowtime);
  744. /*    accessfile(buf,"w","%s/restart.time",s2_prefix);*/
  745.   delete_pid(); exit(0);
  746. }
  747.  
  748. /*  extract target tag from parm string. */
  749. void href_find_target(char *p)
  750. {
  751.   char *pp, *pe,buf1[MAX_LINELEN+1];
  752.   href_target[0]=0; jsbuf[0]=0; ref_mhelp=0;
  753.   for(pp=find_word_start(p);*pp!=0;pp=find_word_start(pe)) {
  754.     pe=find_word_end(pp);
  755.     if(strncasecmp(pp,"target=wims_",strlen("target=wims_"))!=0) continue;
  756.     memmove(buf1,pp,pe-pp); buf1[pe-pp]=0; substit(buf1);
  757.     if(strncasecmp(buf1,"target=wims_mhelp",strlen("target=wims_mhelp"))==0) {
  758.       if(*pe!=0) *pe++=0;
  759.       ovlstrcpy(href_target,"wims_help");
  760.       ref_mhelp=1;
  761.     }
  762.     else {
  763.       if(*pe!=0) *pe++=0;
  764.         mystrncpy(href_target,buf1+strlen("target="),sizeof(href_target));
  765.     }
  766.     snprintf(jsbuf,sizeof(jsbuf),jsstr,href_target,href_target);
  767.     ovlstrcpy(pp,pe);return;
  768.   }
  769.   pp=getvar("module_help");
  770.   if(href_target[0]==0 && pp!=NULL && strcmp(pp,"popup")==0 &&
  771.      (pe=strstr(p,"cmd=help"))!=NULL) {
  772.     if(pe==p || *(pe-1)=='&') {
  773.       ovlstrcpy(href_target,"wims_help"); ref_mhelp=1;
  774.       snprintf(jsbuf,sizeof(jsbuf),jsstr,href_target,href_target);
  775.     }
  776.   }
  777. }
  778.  
  779. void _href_getdef(char src[], char vname[], char buf[], int buflen)
  780. {
  781.   char *p1, *p2, *p3;
  782.   buf[0]=0;
  783.   for(p1=strstr(src,vname); p1; p1=strstr(p2,vname)) {
  784.     p2=p1+strlen(vname); if(*p2!='=') continue;
  785.     if(p1>src && *(p1-1)!='&') continue;
  786.     p2++; p3=strchr(p2,'&'); if(p3==NULL) p3=p2+strlen(p2);
  787.     if(p3-p2>=buflen) return; /* too long */
  788.     memmove(buf,p2, p3-p2); buf[p3-p2]=0; return;
  789.   }
  790. }
  791.  
  792. /* Create href to wims requests. subst() is not done. */
  793. void exec_href(char *p)
  794. {
  795.   char *s, st[128], sti[128], stc[128], stt[128], *p1, *p2, *p3, *wn="";
  796.   char *U="<span class=\"disabled_link\">%s</span>";
  797.   char b1[MAX_LINELEN+1], b2[MAX_LINELEN+1];
  798.   int new=0;
  799.   if(!outputing) return;
  800.   href_find_target(p);
  801.   p1=find_word_start(p);
  802.   p2=find_word_end(p1); if(*p2) *(p2++)=0;
  803.   mystrncpy(b1,p1,sizeof(b1));
  804.   mystrncpy(b2,find_word_start(p2),sizeof(b2));
  805.   substit(b1); substit(b2);
  806.   /* standard reference */
  807.   if(*b2==0 && strchr(b1,'=')==NULL) {
  808.     char b[MAX_LINELEN+1], *ll;
  809.     p1=find_word_start(b1); *find_word_end(p1)=0;
  810.     if(*p1==0 || strlen(p1)>64) return;
  811.     ll=getvar("module_language");
  812.     if(ll==NULL || *ll==0 || *(ll+1)==0 || *(ll+2)!=0) ll=lang;
  813.     accessfile(b,"r","html/href.%s",ll);
  814.     memmove(p1+1,p1,64); *p1='\n'; strcat(p1,"  ");
  815.     p2=strstr(b,p1); if(p2==NULL) return;
  816.     p1=find_word_start(p2+strlen(p1)); p2=find_word_end(p1);
  817.     if(*p2) *(p2++)=0;
  818.     p3=strchr(p2,'\n'); if(p3!=NULL) *p3=0;
  819.     mystrncpy(b1,p1,sizeof(b1));
  820.     mystrncpy(b2,find_word_start(p2),sizeof(b2));
  821.     substit(b1); substit(b2);
  822.   }
  823.   /* for robots: only references without defining cmd. */
  824.   if(robot_access && strstr(b1,"cmd=")!=NULL &&
  825.       strstr(b1,"module=adm/doc")==NULL) {
  826.     _output_(b2); return;
  827.   }
  828.   if(robot_access && strstr(aliased_cgi,"yes")!=NULL) {
  829.     char mbuf[256], lbuf[16];
  830.     _href_getdef(b1,"module",mbuf,sizeof(mbuf));
  831.     if(mbuf[0]==0) mystrncpy(mbuf,home_module,sizeof(mbuf));
  832.     _href_getdef(b1,"lang",lbuf,sizeof(lbuf));
  833.     if(strlen(lbuf)!=2) {mystrncpy(lbuf,lang,4);lbuf[2]=0;}
  834.     if(strncmp(mbuf,"adm/doc",strlen("adm/doc"))==0) {
  835.       char dbuf[256], bbuf[256];
  836.       _href_getdef(b1,"doc",dbuf,sizeof(dbuf));
  837.       _href_getdef(b1,"block",bbuf,sizeof(bbuf));
  838.       if(!myisdigit(dbuf[0])) dbuf[0]=0;
  839.       if(dbuf[0]!=0 && bbuf[0]==0) snprintf(bbuf,sizeof(bbuf),"main");
  840.       if(dbuf[0]==0)
  841.         output("<a href=\"%s%s_doc~.html\">%s</a>", ref_base,lbuf,b2);
  842.       else
  843.         output("<a href=\"%s%s_doc~%s~%s.html\">%s</a>",
  844.            ref_base,lbuf,dbuf,bbuf,b2);
  845.     }
  846.     else {
  847.       for(s=strchr(mbuf,'/'); s!=NULL; s=strchr(s+1,'/')) *s='~';
  848.       output("<a href=\"%s%s_%s.html\">%s</a>", ref_base,lbuf,mbuf,b2);
  849.     }
  850.     return;
  851.   }
  852.   s=getvar("wims_ref_id");
  853.   if(s!=NULL && *s!=0 && !isspace(*s)) {
  854.     snprintf(sti,sizeof(sti)," id=\"%s\"",s);
  855.   }
  856.   else sti[0]=0;
  857.  
  858.   s=getvar("wims_ref_class");
  859.   if(s!=NULL && *s!=0 && !isspace(*s)) {
  860.     snprintf(stc,sizeof(stc)," class=\"%s\"",s);
  861.   }
  862.   else stc[0]=0;
  863.  
  864.   s=getvar("wims_ref_title");
  865.   if(s!=NULL && *s!=0 && !isspace(*s)) {
  866.     snprintf(stt,sizeof(stt)," title=\"%s\"",s);
  867.   }
  868.   else stt[0]=0;
  869.  
  870.   s=getvar("wims_ref_target");
  871.   if(href_target[0]!=0) s=href_target;
  872.   if(s!=NULL && *s!=0 && !isspace(*s)) {
  873.     snprintf(st,sizeof(st)," target=\"%s\"",s);
  874.     if(strcmp(s,"_parent")!=0) {
  875.       new=1; wn="wims_window=new&";
  876.     }
  877.   }
  878.   else st[0]=0;
  879.   _httpfollow(b1,wn,new);
  880.   tohttpquery(b1);
  881.   if(strstr(session_prefix,"_check")!=NULL) {
  882.     if(*b2) output(U,b2);
  883.   else _output_("<a id=\"0\"></a>");
  884.   return;
  885.   }
  886.   if(jsbuf[0]==0 && st[0]==0 && strstr(session_prefix,"_exam")!=NULL) {
  887.     p1=strstr(b1,"cmd=");
  888.     if(p1!=NULL) {
  889.       p1+=strlen("cmd=");
  890.       if(strncmp(p1,"new",3)==0 || strncmp(p1,"renew",5)==0 ||
  891.           strncmp(p1,"intro",5)==0) {
  892.         if(*b2) output(U,b2);
  893.         else _output_("<a id=\"#\"></a>");
  894.         return;
  895.       }
  896.     }
  897.   }
  898.   if(*b2)
  899.     output("<a href=\"%s?%s\"%s%s %s %s %s>%s</a>",
  900.        ref_name, b1, st, jsbuf,sti,stc,stt,b2);
  901.   else
  902.     output("<a href=\"%s?%s\"%s%s %s %s %s>",ref_name, b1, st, jsbuf,sti,stc,stt);
  903.   setvar("wims_ref_id","");
  904.   setvar("wims_ref_class","");
  905.   setvar("wims_ref_title","");
  906. }
  907.  
  908. /* Create form refering to the page. */
  909. void exec_form(char *p)
  910. {
  911.   char *s, *p1, *p2, *a, *m, *opt, st[128], *wn="", *form_id;
  912.   char abuf[128], idbuf[128];
  913.   int i, new=0;
  914.   if(!outputing) return;
  915.   href_find_target(p);
  916.   s=getvar("wims_ref_target");
  917.   if(href_target[0]!=0) s=href_target;
  918.   if(s!=NULL && *s!=0 && !isspace(*s)) {
  919.     snprintf(st,sizeof(st)," target=\"%s\"",s);
  920.     if(strcmp(s,"_parent")!=0) {
  921.       new=1; wn="<input type=\"hidden\" name=\"wims_window\" value=\"yes\" />\n";
  922.     }
  923.   }
  924.   else st[0]=0;
  925.   a=getvar("wims_ref_anchor"); if(a==NULL) a="";
  926.   opt=find_word_start(find_word_end(find_word_start(p)));
  927.   m=getvar("wims_form_method");
  928.   if(m!=NULL) {
  929.     m=find_word_start(m);
  930.     if(strncasecmp(m,"post",4)==0) m="post";
  931.     else if(strncasecmp(m,"get",3)==0) m="get";
  932.       else if(strncasecmp(m,"file",4)==0) {
  933.        m="post\" enctype=\"multipart/form-data";
  934.       snprintf(abuf,sizeof(abuf),"?form-data%ld%s",random(),a); a=abuf;
  935.       force_setvar("wims_form_method","");
  936.     }
  937.     else m=default_form_method;
  938.   }
  939.   else m=default_form_method;
  940.  
  941.   if(strstr(session_prefix,"_check")!=NULL) {
  942.     output("<form action=\"NON_EXISTING_PAGE\" onsubmit=\"window.close();\" %s>\n",
  943.            opt);
  944.     return;
  945.   }
  946.  
  947.   form_id=getvar("wims_form_id");
  948.   if(form_id==NULL || *form_id==0 || isspace(*form_id)){
  949.     form_id="";
  950.   } else {
  951.     snprintf(idbuf,sizeof(idbuf),"id=\"%s\"",find_word_start(form_id));form_id=idbuf;
  952.   }
  953.   force_setvar("wims_form_id","");
  954.  
  955.   output("<form action=\"%s%s\"%s method=\"%s\" %s %s>\n<div class='wims_form'>%s",ref_name,a,st,m,opt,form_id,wn);
  956.   if(a!=abuf && a[0]) force_setvar("wims_ref_anchor","");
  957.   for(i=0;i<follow_no;i++) {
  958.     if(robot_access && follow_list[i]==ro_session) continue;
  959.     if(!new && follow_list[i]!=ro_session
  960.      && follow_list[i]!=ro_module && follow_list[i]!=ro_lang)
  961.       continue;
  962.     if(follow_list[i]==ro_module) continue;
  963.     s=getvar(ro_name[follow_list[i]]);
  964.     if(s!=NULL && *s!=0) {
  965.       if(follow_list[i]==ro_session && memcmp(href_target,"wims_",5)==0) {
  966.         char st[MAX_LINELEN+1];
  967.         char *s1;
  968.         s1=getvar("wims_session");
  969.         if(s1==NULL) internal_error("exec_form() error.\n");
  970.         snprintf(st,sizeof(st),"%.10s%s",s1,href_target+4);
  971.         s=st;
  972.       }
  973.       output("<input type=\"hidden\" name=\"%s\" value=\"%s\" />\n",
  974.            ro_name[follow_list[i]],s);
  975.     }
  976.   }
  977.   p1=find_word_start(p);p2=find_word_end(p1);
  978.   if(p2>p1) {
  979.     char buf[64];
  980.     int i;
  981.     i=p2-p1; if(i>60) i=60;
  982.     memmove(buf,p1,i);buf[i]=0;
  983.     for(i=0;i<CMD_NO && strcmp(buf,commands[i]);i++);
  984.     if(i<CMD_NO) {
  985.       output("<input type=\"hidden\" name=\"cmd\" value=\"%s\" />\n",buf);
  986.       if(i!=cmd_intro && i!=cmd_new)
  987.         output("<input type=\"hidden\" name=\"module\" value=\"%s\" />\n",
  988.             getvar(ro_name[ro_module]));
  989.     }
  990.   }
  991. }
  992.  
  993. /* Creat link to trap robot access, an internal command
  994.  * which should not be documented
  995.  */
  996. void exec_robottrap(char *p)
  997. {
  998.   char buf[MAX_LINELEN+1];
  999.   if(robot_access) return;
  1000.   ovlstrcpy(buf,"session=$wims_session.1&module=adm/trap");
  1001.   _output_("<!-- "); exec_href(buf);_output_("Robot trapper, do not click!</a> -->");
  1002.   _output_("<div class='wimstrap hide'>");exec_href(buf); _output_("<span>Robot trapper, do not click!</span></a></div>");
  1003. }
  1004.  
  1005. /* set definitions in a file. Trusted modules only. */
  1006. void exec_setdef(char *p)
  1007. {
  1008.    char *p1, *pp;
  1009.    char nbuf[MAX_LINELEN+1], fbuf[MAX_LINELEN+1], tbuf[MAX_LINELEN+1];
  1010.    if(robot_access || !trusted_module() || is_class_module) return;
  1011.    p1=wordchr(p,"in"); if(p1==NULL) module_error("syntax_error");
  1012.    *p1=0; p1=find_word_start(p1+strlen("in"));
  1013.    ovlstrcpy(nbuf,p);
  1014.    mystrncpy(tbuf,p1,sizeof(tbuf));
  1015.    substit(nbuf); substit(tbuf);
  1016.    if(find_module_file(tbuf,fbuf,1)) return;
  1017.    pp=find_word_start(nbuf); p1=find_word_start(fbuf); *find_word_end(p1)=0;
  1018.    strip_trailing_spaces(pp);
  1019.    setdef(p1,pp);
  1020. }
  1021.  
  1022. /* Set a variable. */
  1023. void exec_set(char *name)
  1024. {
  1025.   char *p, *defn, *parm;
  1026.   char tbuf2[MAX_LINELEN+1], namebuf[MAX_LINELEN+1];
  1027.   int i;
  1028.  
  1029.   p=strchr(name,'=');
  1030.   if(p==NULL) return; /* warning or error! */
  1031.   *p=0; defn=find_word_start(p+1);
  1032.   *find_word_end(name)=0;
  1033.   mystrncpy(namebuf,find_word_start(name),sizeof(namebuf));
  1034.   /* we allow substit in names, to implement array */
  1035.   substit(namebuf); *find_word_end(namebuf)=0;
  1036.   if(*defn!=calc_prefix_char) {
  1037.   /* substitute by default */
  1038.     mystrncpy(tbuf2,defn,sizeof(tbuf2));
  1039.     substit(tbuf2); setvar(namebuf,tbuf2); return;
  1040.   }
  1041.   /* called from !readdef */
  1042.   if((untrust&4)!=0) module_error("not_trusted");
  1043.   /* definition is a command  */
  1044.   parm=find_word_end(defn+1);
  1045.   if( *parm != 0 ) { *parm=0; parm=find_word_start(parm+1); }
  1046.   i=m_file.lines[m_file.l].varcode;
  1047.   if(i<0) {
  1048.     i=search_list(calc_routine,CALC_FN_NO,sizeof(calc_routine[0]),defn+1);
  1049.     m_file.lines[m_file.l].varcode=i;
  1050.   }
  1051.   if(i<0) {
  1052.       /* replace by warning? */
  1053.     setvar(error_data_string,defn+1); module_error("bad_cmd");
  1054.     return;
  1055.   }
  1056.   mystrncpy(tbuf2,parm,sizeof(tbuf2)); execnt++;
  1057.   if(calc_routine[i].tag==0) substit(tbuf2);
  1058.   tbuf2[sizeof(tbuf2)-1]=0; calc_routine[i].routine(tbuf2);
  1059.       /* remove trailing new line */
  1060.   tbuf2[sizeof(tbuf2)-1]=0;
  1061.   if(tbuf2[strlen(tbuf2)-1]=='\n') tbuf2[strlen(tbuf2)-1]=0;
  1062.     setvar(namebuf,tbuf2);
  1063. }
  1064.  
  1065. /* set but do not overwrite. */
  1066. void exec_default(char *p)
  1067. {
  1068.   char *start, *end, c, *pp;
  1069.   char namebuf[MAX_LINELEN+1];
  1070.   start=find_word_start(p);
  1071.   for(end=start;*end!=0 && !isspace(*end) && *end!='='; end++);
  1072.   c=*end; *end=0;
  1073.   if(end-start<=MAX_LINELEN-1) {
  1074.     memmove(namebuf,start,end-start+1); substit(namebuf);
  1075.     pp=getvar(namebuf);
  1076.     if(pp!=NULL && *pp!=0) return;
  1077.   }
  1078.   *end=c; exec_set(p);
  1079. }
  1080.  
  1081. /* Does nothing; just a comment. */
  1082. void exec_comment(char *p)
  1083. {
  1084.   return;
  1085. }
  1086.  
  1087. /* Exit the file under interpretation */
  1088. void exec_exit(char *p)
  1089. {
  1090.   m_file.linepointer=m_file.linecnt;
  1091.   return;
  1092. }
  1093.  
  1094. /* output a file. Aliases:
  1095.      * getfile, outfile, fileout */
  1096. void exec_getfile(char *p)
  1097. {
  1098.   char *s, sti[128], stc[128], stt[128], sgf[128], *p1, url[MAX_LINELEN+1];
  1099.   char *prompt;
  1100.  
  1101.   p=find_word_start(p); prompt=find_word_end(p);
  1102.   if(*prompt!=0) *prompt++=0;
  1103.   prompt=find_word_start(prompt);
  1104.   if(*p==0 || !outputing) return;
  1105.   if(!trusted_module() || is_class_module) return;
  1106.   s=getvar(ro_name[ro_session]);
  1107.   if(s==NULL || *s==0 || strstr(s,"robot")!=NULL) return;
  1108.   mystrncpy(url,ref_name,sizeof(url));
  1109.   for(p1=url+strlen(url);p1>url && *(p1-1)!='/'; p1--);
  1110.   if(good_httpd) snprintf(p1,sizeof(url)+p1-url,
  1111.               "getfile/%s?&+session=%s&+modif=%ld",
  1112.               p,s,(long)nowtime);
  1113.   else snprintf(url,sizeof(url),
  1114.       "%s?cmd=getfile&+session=%s&+special_parm=%s&+modif=%ld",
  1115.           ref_name,s,p,(long)nowtime);
  1116.   snprintf(jsbuf,sizeof(jsbuf),jsstr,"wims_file","wims_file");
  1117.  
  1118.   s=getvar("wims_ref_id");
  1119.   if(s!=NULL && *s!=0 && !isspace(*s)) {
  1120.     snprintf(sti,sizeof(sti)," id=\"%s\"",s);
  1121.   }
  1122.   else sti[0]=0;
  1123.  
  1124.   s=getvar("wims_ref_class");
  1125.   if(s!=NULL && *s!=0 && !isspace(*s)) {
  1126.     snprintf(stc,sizeof(stc)," class=\"%s\"",s);
  1127.   }
  1128.   else stc[0]=0;
  1129.  
  1130.   s=getvar("wims_ref_title");
  1131.   if(s!=NULL && *s!=0 && !isspace(*s)) {
  1132.     snprintf(stt,sizeof(stt)," title=\"%s\"",s);
  1133.   }
  1134.   else stt[0]=0;
  1135.  
  1136.   s=getvar("wims_getfile_fname");
  1137.   if(s!=NULL && *s!=0 && !isspace(*s)) {
  1138.     snprintf(sgf,sizeof(sgf)," download=\"%s\"",s);
  1139.   }
  1140.   else sgf[0]=0;
  1141.  
  1142.   if(*prompt) output("<a href=\"%s\" %s %s %s %s>%s</a>\n", url,sti,stc,stt,sgf,prompt);
  1143.   else output("<a href=\"%s\" %s %s %s></a>",url,sti,stc,stt);
  1144.  
  1145.   setvar("wims_ref_id","");
  1146.   setvar("wims_ref_class","");
  1147.   setvar("wims_ref_title","");
  1148.   setvar("wims_getfile_fname","");
  1149. }
  1150.  
  1151. /* internal */
  1152. void count_insert(void)
  1153. {
  1154.   insert_no++;
  1155.   if(insert_no>=INS_LIMIT) module_error("too_many_ins");
  1156. }
  1157.  
  1158. int animated_ins=0;
  1159. int grouped_ins=0;
  1160.  
  1161. /* generic insertion */
  1162. void _exec_ins(char *p, char *script_name,char *format)
  1163. {
  1164.   char *s, *b, *at, *tag, *tag2, *al, *fmt, *mh;
  1165.   char *p1, *pt;
  1166.   char buf[1024],buf2[1024],url[MAX_LINELEN+1],altbuf[1024];
  1167.   char outbuf[1024];
  1168.   int border, middle, vspace;
  1169.   long int tel;
  1170.  
  1171.   if(robot_access) return;
  1172.   count_insert(); outbuf[0]=0;
  1173.   setenv("ins_source",p,1); /* value kept from user tamper */
  1174.   if(animated_ins) fmt=getvar("anim_format"); else fmt=format;
  1175.   if(fmt==NULL) fmt="gif";
  1176.   if(ismhelp) mh="mh"; else mh="";
  1177.   snprintf(buf,sizeof(buf),"%s/insert%s-%d.%s",s2_prefix,mh,insert_no,fmt);
  1178.   if(grouped_ins) {unlink(buf); goto grouped;}
  1179.   exportall();
  1180.   call_ssh("%s/%s %d %s >%s/ins.out 2>%s/ins.err",
  1181.       bin_dir,script_name,insert_no,tmp_dir,tmp_dir,tmp_dir);
  1182.   unlink(buf); wrapexec=1;
  1183.   if(trusted_module()) setenv("trusted_module","yes",1);
  1184.   else if(untrust) setenv("trusted_module","no",1);
  1185.   call_ssh("mv %s/insert%s-%d.%s %s >/dev/null 2>/dev/null",
  1186.        tmp_dir,mh,insert_no,fmt,s2_prefix);
  1187.   tel=filelength("%s", buf);
  1188.   if(tel<=5) {
  1189.     char bbuf[MAX_LINELEN+1];
  1190.     accessfile(bbuf,"r","%s/ins.err",tmp_dir);
  1191.     snprintf(url,sizeof(url),"gifs/badins.gif");
  1192.     for(p1=bbuf;p1<bbuf+512 && *p1;p1++)
  1193.       if(*p1=='<' || *p1=='>') *p1='?';
  1194.     *p1=0;
  1195.     if(bbuf[0]==0) snprintf(bbuf,sizeof(bbuf),"Fail");
  1196.     snprintf(outbuf+strlen(outbuf),sizeof(outbuf)-strlen(outbuf),
  1197.        " <img src=\"%s\" alt=\"Error\" /> <small><pre>%s</pre></small> <br /> ",
  1198.        url,bbuf);
  1199.     setvar("ins_warn","fail");
  1200.     setvar("ins_cnt","0");
  1201.     goto reset;
  1202.   }
  1203.   grouped:
  1204.   s=getvar(ro_name[ro_session]);
  1205.   b=getvar("ins_border"); at=getvar("ins_attr");
  1206.   tag=getvar("ins_tag");  al=getvar("ins_align");
  1207.   if(at==NULL) at="";
  1208.   if(tag==NULL) tag="";
  1209.   if(al==NULL) al="";
  1210.   al=find_word_start(al);
  1211.   if(*al!=0) snprintf(buf2,sizeof(buf2),"vertical-align:%s",al); else buf2[0]=0;
  1212.   if(strcasecmp(al,"middle")==0) middle=1; else middle=0;
  1213.   tag2=""; vspace=0;
  1214.   if(*tag!=0) {
  1215.     mystrncpy(buf,tag,sizeof(buf)); tag=find_word_start(buf);
  1216.     tag2=find_word_end(tag);
  1217.     if(*tag2!=0) *tag2++=0;
  1218.     tag2=find_word_start(tag2);
  1219.   }
  1220.   if(b==NULL || *b==0) border=0;
  1221.   else border=atoi(b);
  1222.   if(border<0) border=0;
  1223.   if(border>100) border=100;
  1224.   if(middle) {
  1225.     snprintf(outbuf+strlen(outbuf),
  1226.        sizeof(outbuf)-strlen(outbuf),"%s",mathalign_sup1);
  1227.     vspace=2;
  1228.   }
  1229.   mystrncpy(url,ref_name,sizeof(url));
  1230.   for(p1=url+strlen(url);p1>url && *(p1-1)!='/'; p1--);
  1231.   snprintf(p1,sizeof(url)+p1-url,
  1232.          "wims.%s?cmd=getins&+session=%s&+special_parm=insert%s-%d.%s&+modif=%ld",
  1233.          fmt,s,mh,insert_no,fmt,(long)nowtime);
  1234.   if(strchr(ins_alt,'"')!=NULL || strlen(ins_alt)>256) ins_alt[0]=0;
  1235.   pt=getvar("wims_ins_alt"); if(pt==NULL) pt="";
  1236.   if(strcmp(pt,"empty")==0) altbuf[0]=0;
  1237.   else
  1238.     if(ins_alt[0] && strcmp(pt,"none")!=0)
  1239.       snprintf(altbuf,sizeof(altbuf)," alt=\"\"");
  1240.     else
  1241.       snprintf(altbuf,sizeof(altbuf)," alt=\"%s\"",ins_alt);
  1242.   if(strcasecmp(tag,"form")!=0) {
  1243.     snprintf(outbuf+strlen(outbuf),sizeof(outbuf)-strlen(outbuf),
  1244.        "<img src=\"%s\" style=\"border:solid;border-width:%dpx;margin-bottom:%dpx;%s\" %s %s />",
  1245.        url, border, vspace, buf2, at, altbuf);
  1246.   }
  1247.   else {
  1248.     char *n, *nend;
  1249. /* fix: add quotes at name=" " */
  1250.     if(*tag2!=0) {n="name=\"" ; nend="\"";} else {n=""; nend="";}
  1251.     snprintf(outbuf+strlen(outbuf),sizeof(outbuf)-strlen(outbuf),
  1252.         "<input type=\"image\" %s%s%s src=\"%s\" style=\"border:solid;border-width:%dpx;margin-bottom:%dpx;%s\" %s %s />",
  1253.         n,tag2,nend,url,border,vspace,buf2,at,altbuf);
  1254.   }
  1255.   if(middle) snprintf(outbuf+strlen(outbuf),
  1256.            sizeof(outbuf)-strlen(outbuf),"%s",mathalign_sup2);
  1257.   setvar("ins_warn",""); ins_alt[0]=0;
  1258.   setvar("ins_cnt",int2str(insert_no));
  1259.   reset:
  1260.   if(outputing) _output_(outbuf);
  1261.   setvar("ins_out",outbuf);
  1262.   setvar("ins_attr",""); setvar("ins_tag","");
  1263.   setvar("ins_url",url);
  1264.   snprintf(buf2,sizeof(buf2),"insert%s-%d.%s",mh,insert_no,fmt);
  1265.   setvar("ins_filename",buf2);
  1266.   animated_ins=0;
  1267. }
  1268.  
  1269. /* instex: dynamically insert tex outputs */
  1270. void exec_instex(char *p)
  1271. {
  1272.   char *ts, *tc, *f, *mh, buf[MAX_FNAME+1];
  1273.  
  1274.   if(robot_access) {
  1275.     *p=0; return;
  1276.   }
  1277.   f=instex_check_static(p); substit(p);
  1278.   if(f==NULL) {
  1279. /* Use static instex if there is no real substitution
  1280.  * and the source file is not in sessions directory.
  1281.  */
  1282.     calc_instexst(p); if(outputing) _output_(p);
  1283.     return;
  1284.   }
  1285.   if(ismhelp) mh="mh"; else mh="";
  1286.   fix_tex_size(); f="gif";
  1287.   setenv("texgif_style",instex_style,1);
  1288.   tc=getvar("instex_texheader"); if (tc) { setenv("texgif_texheader",tc,1);}
  1289.   setenv("texgif_tmpdir",tmp_dir,1);
  1290.   setenv("texgif_src",p,1);
  1291.   if(ins_alt[0]==0) mystrncpy(ins_alt,p,sizeof(ins_alt));
  1292.   mkfname(buf,"%s/insert%s-%d.gif",tmp_dir,mh,insert_no+1);
  1293.   setenv("texgif_outfile",buf,1);
  1294.   ts=getvar("wims_texsize"); tc=getvar("instex_color");
  1295.   if(lastout_file!=-1 && (tc==NULL || *tc==0) &&
  1296.       (ts==NULL || *ts==0 || strcmp(ts,"0")==0) &&
  1297.      strstr(p,"\\begin{")==NULL) {
  1298.     int ls, ln;
  1299.     char *pagebreak;
  1300.     ls=strlen(instex_src); ln=strlen(instex_fname);
  1301.     if(ls+strlen(p)>=MAX_LINELEN-256 ||
  1302.      ln+strlen(buf)>=MAX_LINELEN-16) {
  1303.         instex_flush(); ls=ln=0;
  1304.     }
  1305.     if(instex_cnt>0) pagebreak="\\pagebreak\n"; else pagebreak="";
  1306.     snprintf(instex_src+ls,MAX_LINELEN-ls,"%s %s %s %s\n",
  1307.          pagebreak,instex_style,p,instex_style);
  1308.     snprintf(instex_fname+ln,MAX_LINELEN-ln,"%s\n",buf);
  1309.       grouped_ins=1;
  1310.   }
  1311.   mkfname(buf,"%s/texgif.dvi",tmp_dir); unlink(buf);
  1312.   wrapexec=0; _exec_ins(p,instex_processor,f);
  1313.   if(grouped_ins) instex_cnt++;
  1314.   grouped_ins=0;
  1315. }
  1316.  
  1317. /* patches the gnuplot integer division (mis)feature. */
  1318. void gnuplot_patch(char *p,int oneline)
  1319. {
  1320.   char *pp;
  1321.   for(pp=strchr(p,'/');pp!=NULL;pp=strchr(pp+1,'/')) {
  1322.     char *p1;
  1323.     if(pp<=p || !myisdigit(*(pp-1)) || !myisdigit(*(pp+1))) continue;
  1324.     for(p1=pp-2;p1>=p && myisdigit(*p1);p1--);
  1325.     if(p1>=p && *p1=='.') continue;
  1326.     for(p1=pp+2;*p1 && myisdigit(*p1);p1++);
  1327.     if(*p1=='.') continue;
  1328.     string_modify(p,p1,p1,".0");
  1329.   }
  1330.   for(pp=strchr(p,'^');pp!=NULL;pp=strchr(pp+1,'^'))
  1331.     string_modify(p,pp,pp+1,"**");
  1332. /* disallow new lines and ';' */
  1333.   if(oneline)
  1334.     for(pp=p;*pp!=0;pp++) if(*pp==';' || *pp=='\n') *pp=' ';
  1335. }
  1336.  
  1337. /* This is to disable pipe in the gnuplot plotting function.
  1338.  * We do not allow ' followed by < .
  1339.  */
  1340. void prepare_insplot_parm(char *p)
  1341. {
  1342.   int i,j,multanim; char *pp, *s;
  1343.   double d;
  1344.   char setbuf[MAX_LINELEN+10],buf[MAX_LINELEN+1];
  1345.  
  1346.   j=strlen(p);
  1347. /* pipe in plot command */
  1348.   for(i=0;i<j;i++) {
  1349.     if(*(p+i)!='\'' && *(p+i)!='"') continue;
  1350.     pp=find_word_start(p+i+1); if(*pp=='<') module_error("illegal_plot_cmd");
  1351.   }
  1352.   gnuplot_patch(p,1);
  1353. /* multiplot */
  1354.   multanim=0;
  1355.   pp=getvar("insplot_split");
  1356.   if(pp!=NULL) i=linenum(pp); else i=0;
  1357. /* arbitrary limit: 16 multiplots */
  1358.   if(i>16) i=16;
  1359.   if(i>1) {
  1360.     char tbuf[MAX_LINELEN*(i+1)+100], bbuf[MAX_LINELEN+1];
  1361.     tbuf[0]=0;
  1362.     if(*p!=0) snprintf(tbuf,sizeof(tbuf),"%s\n",p);
  1363.     snprintf(buf,sizeof(buf),"%d",i); setenv("multiplot",buf,1);
  1364.     for(j=1;j<=i;j++) {
  1365.       snprintf(buf,sizeof(buf),"insplot_parm_%d",j);
  1366.       pp=getvar(buf);
  1367.       if(pp==NULL || *pp==0) {
  1368.         if(j==1 && *p!=0) continue;
  1369.         pp="";
  1370.       }
  1371.       else {
  1372.         mystrncpy(bbuf,pp,sizeof(bbuf));
  1373.         gnuplot_patch(bbuf,1);
  1374.       }
  1375.       strcat(tbuf,bbuf);strcat(tbuf,"\n");
  1376.     }
  1377.     setenv("insplot_source",tbuf,1);
  1378.     if(varchr(tbuf,"s")!=NULL) multanim=1;
  1379.   }
  1380. /* no illegal chaining */
  1381.   pp=getvar("insplot_font"); if(pp!=NULL) {
  1382.     for(s=pp;s<pp+MAX_LINELEN && *s;s++)
  1383.     if(*s==';' || *s=='\n' || *s==' ') *s=0;
  1384.     if(s>=pp+MAX_LINELEN) *s=0;
  1385.     setvar("insplot_font",pp);
  1386.   }
  1387.   pp=getvar("insplot_set");
  1388.   if(pp!=NULL) {
  1389.     char tbuf[MAX_LINELEN+1];
  1390.     mystrncpy(tbuf,pp,sizeof(tbuf));
  1391.     i=strlen(tbuf)-1;
  1392.     while(i>0 && isspace(tbuf[i])) i--;
  1393.     if(tbuf[i]==';') tbuf[i]=0;
  1394.     gnuplot_patch(tbuf,0);pp=tbuf;
  1395.     ovlstrcpy(setbuf,"set "); j=strlen("set ");
  1396.     for(i=0; *(pp+i)!=0 && j<MAX_LINELEN; i++) {
  1397.       if(*(pp+i)=='\n') {setbuf[j++]=' '; continue;}
  1398.       if(*(pp+i)!=';') {setbuf[j++]=*(pp+i); continue;}
  1399.       ovlstrcpy(setbuf+j,"\nset "); j+=strlen("\nset ");
  1400.     }
  1401.     setbuf[j]=0;
  1402.     setenv("insplot_set",setbuf,1);
  1403.   }
  1404.   else setenv("insplot_set","",1);
  1405. /* frames of animation */
  1406.   pp=getvar("ins_anim_frames");
  1407.   if(pp!=NULL) i=evalue(pp); else i=1;
  1408.   if(i>=ANIM_LIMIT) i=ANIM_LIMIT-1;
  1409.   if(i<1) i=1;
  1410.   if(strstr(setbuf,"step")==NULL && strstr(p,"step")==NULL
  1411.      && varchr(setbuf,"s")==NULL && varchr(p,"s")==NULL && !multanim) i=1;
  1412.   setenv("ins_anim_frames",int2str(i),1);
  1413.   setvar("ins_anim_frames","");
  1414.   if(i>1) {setvar("ins_animation","yes");animated_ins=1;}
  1415.   else setvar("ins_animation","no");
  1416. /* delay of animation */
  1417.   pp=getvar("ins_anim_delay");
  1418.   if(pp!=NULL) d=evalue(pp); else d=0;
  1419.   if(d>=10) d=10;
  1420.   if(d<0) d=0;
  1421.   setenv("ins_anim_delay",int2str(d*100),1);
  1422. }
  1423.  
  1424. /* Insert dynamic 2d plot */
  1425. void exec_insplot(char *p)
  1426. {
  1427.   char *fmt;
  1428.   if(robot_access) {
  1429.     *p=0; return;
  1430.   }
  1431.   fmt=getvar("ins_format"); if(fmt==NULL || *fmt==0) fmt=DEFAULT_INS_FORMAT;
  1432.   prepare_insplot_parm(p); setenv("insplot_method","2D",1);
  1433.   _exec_ins(p,insplot_processor,fmt);
  1434.   wrapexec=1;
  1435. /*    call_ssh("mv %s/insplot_cmd %s 2>/dev/null",tmp_dir,s2_prefix); */
  1436.    unsetenv("multiplot"); setvar("insplot_split","");
  1437. }
  1438.  
  1439. /* Insert dynamic 3d plot */
  1440. void exec_insplot3d(char *p)
  1441. {
  1442.   char *fmt;
  1443.   if(robot_access) {
  1444.     *p=0; return;
  1445.   }
  1446.   fmt=getvar("ins_format"); if(fmt==NULL || *fmt==0) fmt=DEFAULT_INS_FORMAT;
  1447.   prepare_insplot_parm(p); setenv("insplot_method","3D",1);
  1448.   _exec_ins(p,insplot_processor,fmt);
  1449.   wrapexec=1;
  1450. /*     call_ssh("mv %s/insplot_cmd %s 2>/dev/null",tmp_dir,s2_prefix); */
  1451.   unsetenv("multiplot");setvar("insplot_split","");
  1452. }
  1453.  
  1454. /* Insert dynamic gif draw. The parm preparation is specific to fly. */
  1455. void exec_insdraw(char *p)
  1456. {
  1457.   char *pp, *fmt;
  1458.   int i;
  1459.   double d;
  1460.  
  1461.   if(robot_access) {
  1462.     *p=0; return;
  1463.   }
  1464. /*    calc_tolower(p);*/
  1465.   fmt=getvar("ins_format"); if(fmt==NULL || *fmt==0) fmt=DEFAULT_INS_FORMAT;
  1466.   while((pp=wordchr(p,"output"))!=NULL) memmove(pp,"zqkwfx",6);
  1467. /* frames of animation */
  1468.   pp=getvar("ins_anim_frames");
  1469.   if(pp!=NULL) i=evalue(pp); else i=1;
  1470.   if(i>=ANIM_LIMIT) i=ANIM_LIMIT-1;
  1471.   if(i<1) i=1;
  1472.   if(i>1 && varchr(p,"s")==NULL && varchr(p,"animstep")==NULL
  1473.      && varchr(p,"step")==NULL) i=1;
  1474.   setenv("ins_anim_frames",int2str(i),1);
  1475.   setvar("ins_anim_frames","");
  1476.   if(i>1) {setvar("ins_animation","yes");animated_ins=1;}
  1477.   else setvar("ins_animation","no");
  1478. /* delay of animation */
  1479.   pp=getvar("ins_anim_delay");
  1480.   if(pp!=NULL) d=evalue(pp); else d=0;
  1481.   if(d>=10) d=10;
  1482.   if(d<0) d=0;
  1483.   setenv("ins_anim_delay",int2str(d*100),1);
  1484.   pp=getvar("insdraw_filebase");
  1485.   if(pp!=NULL && strstr(pp,parent_dir_string)!=NULL)
  1486.     setvar("insdraw_filebase","");
  1487.   _exec_ins(p,insdraw_processor,fmt);
  1488. }
  1489.  
  1490. void exec_increase(char *p)
  1491. {
  1492.   char *p1, *p2;
  1493.   p1=find_word_start(p); p2=find_word_end(p1);
  1494.   if(p2<=p1) {
  1495.     *p=0; return;
  1496.   }
  1497.   *p2=0;p2=getvar(p1);
  1498.   if(p2==NULL) p2="";
  1499.   setvar(p1,int2str(atoi(p2)+1)); *p=0;
  1500. }
  1501.  
  1502. void exec_setseed(char *p)
  1503. {
  1504.   char buf[64];
  1505.   snprintf(buf,sizeof(buf),"%s",p);
  1506.   force_setvar("wims_seed",buf);
  1507.   double d=evalue(p);
  1508.   srandom(d); *p=0;
  1509. }
  1510.  
  1511. /* bound a variable */
  1512. void exec_bound(char *p)
  1513. {
  1514.   char *p1, *p2, *p3;
  1515.   int doub,i,bcnt,defaulted;
  1516.   double d1,d2,dd,val;
  1517.   char nbuf[MAX_LINELEN+1],lbuf[MAX_LINELEN+1],dbuf[MAX_LINELEN+1];
  1518.   char vbuf[MAX_LINELEN+1];
  1519.   char *blist[2048];
  1520.  
  1521.   p1=find_word_start(p); p2=find_word_end(p1);
  1522.   if(*p2==0) {
  1523.     syntax: module_error("syntax_error");
  1524.   }
  1525.   *p2=0; ovlstrcpy(nbuf,p1);substit(nbuf); p1=find_word_start(p2+1);
  1526.   p2=getvar(nbuf);if(p2==NULL) p2="";
  1527.   mystrncpy(vbuf,find_word_start(p2),sizeof(vbuf));
  1528.   strip_trailing_spaces(vbuf);
  1529.   p2=find_word_end(p1); if(*p2==0) goto syntax;
  1530.   *p2=0;p2++;
  1531.   p3=wordchr(p2,"default");
  1532.   if(p3!=NULL) {
  1533.     *p3=0; defaulted=1;
  1534.     p3=find_word_start(p3+strlen("default"));
  1535.     ovlstrcpy(dbuf,p3); substit(dbuf);
  1536.   }
  1537.   else defaulted=0;
  1538.   if(strcmp(p1,"between")==0) {
  1539.     p1=find_word_start(p2);
  1540.     i=strlen("integer");
  1541.     if(strncmp(p1,"integer",i)==0 &&
  1542.         (isspace(*(p1+i)) || (*(p1+i)=='s' && isspace(*(p1+i+1))))) {
  1543.       doub=0; p1=find_word_start(find_word_end(p1));
  1544.       val=rint(evalue(vbuf));
  1545.       if(vbuf[0]) float2str(val,vbuf);
  1546.     }
  1547.     else {
  1548.       doub=1;val=evalue(vbuf);
  1549.     }
  1550.     p2=wordchr(p1,"and"); p3=p2+strlen("and");
  1551.     if(p2==NULL) {
  1552.       p2=strchr(p1,','); p3=p2+1;
  1553.     }
  1554.     if(p2==NULL) goto syntax;
  1555.    *p2=0;p2=find_word_start(p3);
  1556.     if(*p1==0 || *p2==0) goto syntax;
  1557.     d1=evalue(p1);d2=evalue(p2);
  1558.     if(!isfinite(d1) || !isfinite(d2) ||
  1559.       fabs(d1)>(double)(1E10) || fabs(d2)>(double)(1E10)) goto syntax;
  1560.     if(d1>d2) {
  1561.       dd=d1;d1=d2;d2=dd;
  1562.     }
  1563.     if(vbuf[0] && val<=d2 && val>=d1) {
  1564.       if(!doub) setvar(nbuf,vbuf);
  1565.       *p=0; return;
  1566.     }
  1567.     if(defaulted) ovlstrcpy(p,dbuf);
  1568.     else {
  1569.       if(!doub) {
  1570.         d1=ceil(d1);d2=floor(d2);
  1571.       }
  1572.       if(vbuf[0]==0 || val<d1) val=d1;
  1573.       else val=d2;
  1574.       float2str(val,p);
  1575.     }
  1576.     setvar(nbuf,p); *p=0; return;
  1577.   }
  1578.   else {
  1579.     if(strcmp(p1,"within")==0 || strcmp(p1,"among")==0) {
  1580.       ovlstrcpy(lbuf,p2);substit(lbuf);
  1581.       bcnt=cutitems(lbuf,blist,2048);
  1582.       if(bcnt<=0) {
  1583.         *p=0; return;
  1584.       }
  1585.       for(i=0;i<bcnt;i++) {
  1586.         if(strcmp(blist[i],vbuf)==0) {
  1587.           *p=0; return;
  1588.         }
  1589.       }
  1590.       if(defaulted) ovlstrcpy(p,dbuf); else ovlstrcpy(p,blist[0]);
  1591.       setvar(nbuf,p); *p=0;
  1592.       return;
  1593.     }
  1594.   else goto syntax;
  1595.   }
  1596. }
  1597.  
  1598. /* detrust the module. */
  1599. void exec_detrust(char *p)
  1600. {
  1601.   untrust|=1; *p=0;
  1602. }
  1603.  
  1604. void exec_warn(char *p)
  1605. {
  1606.   char *p1,*p2;
  1607.   char buf[MAX_FNAME+1];
  1608.   WORKING_FILE save;
  1609.  
  1610.   if(!outputing) goto end;
  1611.   p1=find_word_start(p);p2=find_word_end(p1);
  1612.   if(p2<=p1) goto end;
  1613.   *p2=0;
  1614.   snprintf(buf,sizeof(buf),"wims_warn_%s",p1);
  1615.   p2=getvar(buf);
  1616.   if(p2==NULL || *p2==0) goto end;
  1617.   p2=getvar("module_language");if(p2==NULL) p2="en";
  1618.   mkfname(buf,"msg/warn_%s.phtml.%s",p1,p2);
  1619.   memmove(&save,&m_file,sizeof(WORKING_FILE));
  1620.   if(open_working_file(&m_file,buf)==0) phtml_put(NULL,0);
  1621.   memmove(&m_file,&save,sizeof(WORKING_FILE));
  1622.   end:
  1623.   *p=0; return;
  1624. }
  1625.  
  1626. /* write an error message. */
  1627. void exec_msg(char *p)
  1628. {
  1629.   char *p1,*p2, buf[64], *l;
  1630.   secure_exec();
  1631.   p1=find_word_start(p); p2=find_word_end(p1);
  1632.   if(*p2) {
  1633.     *p2=0; p2=find_word_start(p2+1);
  1634.   }
  1635.   force_setvar("wims_error",p1); force_setvar("wims_error_parm",p2);
  1636.   l=getvar("module_language");
  1637.   if(l!=NULL && strlen(l)==2) {
  1638.     snprintf(buf,sizeof(buf),"msg.phtml.%s",l);
  1639.     phtml_put_base(buf,0);
  1640.   }
  1641.   *p=0;
  1642. }
  1643.  
  1644. struct distr_cmd distr_cmd[]={
  1645.   {"char",      NULL},
  1646.   {"charof",    NULL},
  1647.   {"chars",     NULL},
  1648.   {"charsof",   NULL},
  1649.   {"item",      cutitems},
  1650.   {"itemof",    cutitems},
  1651.   {"items",     cutitems},
  1652.   {"itemsof",   cutitems},
  1653.   {"line",      cutlines},
  1654.   {"lineof",    cutlines},
  1655.   {"lines",     cutlines},
  1656.   {"linesof",   cutlines},
  1657.   {"list",      cutitems},
  1658.   {"word",      cutwords},
  1659.   {"wordof",    cutwords},
  1660.   {"words",     cutwords},
  1661.   {"wordsof",   cutwords}
  1662. };
  1663.  
  1664. int distr_cmd_no=(sizeof(distr_cmd)/sizeof(distr_cmd[0]));
  1665.  
  1666. /* distribute a number of lines, items, etc. into a list of vars. */
  1667. void exec_distribute(char *p)
  1668. {
  1669.   int i,k,n;
  1670.   char *p1, *p2;
  1671.   char bf1[MAX_LINELEN+1],bf2[MAX_LINELEN+1];
  1672.   char *names[4096],*vals[4096];
  1673.   p1=find_word_start(p); p2=find_word_end(p1);
  1674.   if(p2<=p1 || *p2==0) module_error("syntax_error");
  1675.   *p2++=0;
  1676.   i=search_list(distr_cmd,distr_cmd_no,sizeof(distr_cmd[0]),p1);
  1677.   if(i<0) module_error("syntax_error");
  1678.   p2=find_word_start(p2); p1=wordchr(p2,"into");
  1679.   if(p1==NULL) module_error("syntax_error");
  1680.   *p1=0;mystrncpy(bf1,p2,sizeof(bf1));
  1681.   p1=find_word_start(p1+strlen("into"));
  1682.   mystrncpy(bf2,p1,sizeof(bf2));
  1683.   substit(bf1);substit(bf2);
  1684.   strip_trailing_spaces(bf1);
  1685.   items2words(bf2); n=cutwords(bf2,names,4096);
  1686.   if(distr_cmd[i].routine!=NULL) {
  1687.     k=distr_cmd[i].routine(bf1,vals,n);
  1688.     for(i=0;i<k;i++) setvar(names[i],vals[i]);
  1689.     for(;i<n;i++) setvar(names[i],"");
  1690.   }
  1691.   else {
  1692.     char buf[2];
  1693.     buf[1]=0;
  1694.     for(p1=bf1,i=0;i<n;i++) {
  1695.       buf[0]=*p1; if(*p1) p1++;
  1696.       setvar(names[i],buf);
  1697.     }
  1698.   }
  1699. }
  1700.  
  1701. /* reset variables - the syntax name[10] is allowed*/
  1702. void exec_reset(char *p)
  1703. {
  1704.   char *p1, *p2, *p3, *p4, buf[100];
  1705.   int i, n;
  1706.  
  1707.   items2words(p);
  1708.   for(p1=find_word_start(p); *p1; p1=find_word_start(p2)) {
  1709.     p2=find_word_end(p1); if(*p2) *p2++=0;
  1710.     substit(p1);
  1711.     p3=strchr(p1,'[');
  1712.     if (p3 != NULL) {
  1713.       *p3++ = 0;
  1714.       p4 = find_matching(p3, ']');
  1715.       if (p4 == NULL) module_error("nomatching");
  1716.       *p4=0;
  1717.       n = atoi(p3);
  1718.       for (i = 1; i<=n; ++i) {
  1719.         snprintf(buf, sizeof(buf),"%s%d",p1,i);
  1720.         setvar(buf,"");
  1721.       }
  1722.     }
  1723.     else setvar(p1,"");
  1724.   }
  1725. }
  1726.  
  1727. /* exchange the values of two variables */
  1728. void exec_exchange(char *p)
  1729. {
  1730.   char buf[MAX_LINELEN+1],b1[MAX_LINELEN+1],b2[MAX_LINELEN+1];
  1731.   char *p1,*p2,*pb;
  1732.   p1=wordchr(p,"and");
  1733.   if(p1!=NULL) {
  1734.     *p1=0; p2=find_word_start(p1+strlen("and"));
  1735.   }
  1736.   else {
  1737.     p1=strchr(p,',');
  1738.     if(p1==NULL) module_error("syntax_error");
  1739.     *p1=0; p2=find_word_start(p1+1);
  1740.   }
  1741.   p1=find_word_start(p);
  1742.   mystrncpy(b1,p1,sizeof(b1)); substit(b1); *find_word_end(b1)=0;
  1743.   mystrncpy(b2,p2,sizeof(b2)); substit(b2); *find_word_end(b2)=0;
  1744.   if(*b1==0 || *b2==0) module_error("syntax_error");
  1745.   pb=getvar(b1);if(pb==NULL) pb="";
  1746.   mystrncpy(buf,pb,sizeof(buf));
  1747.   pb=getvar(b2);if(pb==NULL) pb="";
  1748.   setvar(b1,pb); setvar(b2,buf);
  1749. }
  1750.  
  1751. /**
  1752.  * Send a mail
  1753.  * p contains 4 lines :
  1754.     $To\
  1755.     $from\
  1756.     $subject\
  1757.     $body\
  1758.    The first line ($mailheader) must contain the recipient address.
  1759.  */
  1760. void exec_mailto(char *p)
  1761. {
  1762.   char *from,*subject,*body,*pp,*charset;
  1763.   char mail_content[MAX_LINELEN+1],buf[MAX_LINELEN+1];
  1764.   /*int status;
  1765.   FILE *mailer;// need stdio.h */
  1766.  
  1767.   if(!trusted_module() || is_class_module) return;
  1768.   // searches for the first occurrence of \n
  1769.   from=strchr(p,'\n'); if(from==NULL) return;
  1770.   // stops the 'p' string here.
  1771.   *from++=0; p=find_word_start(p);
  1772.   if(*p==0) return;
  1773.  
  1774.   subject=strchr(from,'\n'); if(subject==NULL) return;
  1775.   // delimitate the 'from' string.
  1776.   *subject++=0; from=find_word_start(from);
  1777.   if(*from!=0) snprintf(buf,sizeof(buf),"From:%s\n",from);
  1778.     else buf[0]=0;
  1779.   body=strchr(subject,'\n'); if(body==NULL) return;
  1780.   // // delimitate the 'subject' string.
  1781.   *body++=0; subject=find_word_start(subject);
  1782.  
  1783.   for(pp=subject;*pp;pp++) if(*pp=='"' || *pp=='\n') *pp=' ';
  1784.  
  1785.   charset=getvar("wims_main_font");if(charset==NULL) charset="windows-1252";
  1786.   snprintf(mail_content,sizeof(mail_content),
  1787.           "To:%s\n%sSubject:%s\nContent-Type: text/html;charset=\"%s\"\n\n%s\n",p,buf,subject,charset,body);
  1788.  
  1789.   accessfile(mail_content,"w","%s/mail.eml",tmp_dir);
  1790.   wrapexec=1;
  1791.   // you can add option '-vv' to make sendmail more verbose.
  1792.   call_sh("cat %s/mail.eml | /usr/sbin/sendmail -t 2> %s/sendmail.log",tmp_dir,tmp_dir);
  1793.  
  1794.   mail_log(p);
  1795.   *p=0;
  1796. }
  1797.  
  1798. /* Generates a user error. Internal and undocumented. */
  1799. void exec_usererror(char *p)
  1800. {
  1801.   if(trusted_module()) user_error(p);
  1802. }
  1803.  
  1804. /* stop output. */
  1805. void exec_directout(char *p)
  1806. {
  1807.   if(outputing || !trusted_module()) return;
  1808.   printf("%s",p);
  1809.   noout=1;
  1810. }
  1811.  
  1812. enum {
  1813.   EXEC_IF, EXEC_JUMP, EXEC_ELSE, EXEC_ENDIF, EXEC_EXEC,
  1814.   EXEC_WHILE, EXEC_ENDWHILE,
  1815.   EXEC_FOR, EXEC_VAR, EXEC_DEBUG, EXEC_DAEMON,
  1816.   EXEC_SET, EXEC_DEFAULT, EXEC_COMMENT, EXEC_READ, EXEC_HREF,
  1817.   EXEC_INS, EXEC_STRING, EXEC_PEDIA, EXEC_DIR,
  1818.   EXEC_TRUST, EXEC_WARN, EXEC_ERROR, EXEC_SQL, EXEC_SCORE,
  1819.   EXEC_MAIL, EXEC_OTHER
  1820. } EXEC_TYPES;
  1821. #define EXEC_SUBST 0x1000
  1822. #define EXEC_USECALC 0x2000
  1823. #define EXEC_PROCTOO 0x4000
  1824. MYFUNCTION exec_routine[]={
  1825.   {"!",        EXEC_COMMENT,        exec_comment},
  1826.   {"TeXmath",  EXEC_STRING|EXEC_SUBST|EXEC_USECALC,texmath},
  1827.   {"add",      EXEC_STRING|EXEC_USECALC,calc_sum},
  1828.   {"advance",  EXEC_VAR|EXEC_SUBST,   exec_increase},
  1829.   {"append",   EXEC_STRING|EXEC_USECALC,calc_append},
  1830.   {"appendfile",EXEC_DIR|EXEC_SUBST,    fileappend},
  1831.   {"bound",     EXEC_STRING,       exec_bound},
  1832.   {"break",     EXEC_FOR,        exec_break},
  1833.   {"call",      EXEC_EXEC|EXEC_SUBST|EXEC_PROCTOO|EXEC_USECALC,    calc_exec},
  1834.   {"changeto",  EXEC_READ|EXEC_SUBST,    exec_changeto},
  1835.   {"char",      EXEC_STRING|EXEC_USECALC,calc_charof},
  1836.   {"chars",     EXEC_STRING|EXEC_USECALC,calc_charof},
  1837.   {"checkhost", EXEC_STRING|EXEC_USECALC|EXEC_SUBST,calc_checkhost},
  1838.   {"column",    EXEC_STRING|EXEC_USECALC,calc_columnof},
  1839.   {"columns",   EXEC_STRING|EXEC_USECALC,calc_columnof},
  1840.   {"comment",   EXEC_COMMENT,        exec_comment},
  1841.   {"daemon",    EXEC_DAEMON|EXEC_USECALC|EXEC_SUBST,calc_daemon},
  1842.   {"date",      EXEC_STRING|EXEC_USECALC|EXEC_SUBST,calc_date},
  1843.   {"deaccent",  EXEC_STRING|EXEC_USECALC|EXEC_SUBST,deaccent},
  1844.   {"debug",     EXEC_DEBUG,        calc_debug},
  1845.   {"declosing", EXEC_STRING|EXEC_USECALC|EXEC_SUBST,calc_declosing},
  1846.   {"def",       EXEC_SET,        exec_set},
  1847.   {"default",   EXEC_SET,        exec_default},
  1848.   {"define",    EXEC_SET,        exec_set},
  1849.   {"definitionof", EXEC_SCORE|EXEC_USECALC,calc_defof},
  1850.   {"defof",     EXEC_SCORE|EXEC_USECALC,calc_defof},
  1851.   {"defread",   EXEC_READ|EXEC_SUBST,    exec_defread},
  1852.   {"detag",     EXEC_STRING|EXEC_USECALC|EXEC_SUBST,calc_detag},
  1853.   {"detrust",   EXEC_TRUST,        exec_detrust},
  1854. /*      {"dictionary",EXEC_STRING|EXEC_USECALC,calc_dictionary},    */
  1855.   {"dir",       EXEC_DIR|EXEC_SUBST|EXEC_USECALC,calc_listfile},
  1856.   {"distribute",EXEC_STRING,        exec_distribute},
  1857.   {"distrust",  EXEC_TRUST,        exec_detrust},
  1858.   {"else",      EXEC_ELSE,        exec_else},
  1859.   {"embraced",  EXEC_STRING|EXEC_USECALC,calc_embraced},
  1860.   {"encyclo",   EXEC_PEDIA|EXEC_SUBST|EXEC_USECALC,pedia},
  1861.   {"encyclopedia",EXEC_PEDIA|EXEC_SUBST|EXEC_USECALC,pedia},
  1862.   {"endif",     EXEC_ENDIF,        exec_endif},
  1863.   {"endwhile",  EXEC_ENDWHILE,        exec_endwhile},
  1864.   {"evalsubst", EXEC_STRING|EXEC_USECALC,calc_evalsubst},
  1865.   {"evalsubstit",EXEC_STRING|EXEC_USECALC,calc_evalsubst},
  1866.   {"evalsubstitute",EXEC_STRING|EXEC_USECALC,calc_evalsubst},
  1867.   {"evaluesubst",EXEC_STRING|EXEC_USECALC,calc_evalsubst},
  1868.   {"evaluesubstit",EXEC_STRING|EXEC_USECALC,calc_evalsubst},
  1869.   {"evaluesubstitute",EXEC_STRING|EXEC_USECALC,calc_evalsubst},
  1870.   {"examscore", EXEC_SCORE|EXEC_SUBST|EXEC_USECALC,calc_examscore},
  1871.   {"exchange",  EXEC_STRING,        exec_exchange},
  1872.   {"exec",      EXEC_EXEC|EXEC_SUBST|EXEC_PROCTOO|EXEC_USECALC,    calc_exec},
  1873.   {"execute",   EXEC_EXEC|EXEC_SUBST|EXEC_PROCTOO|EXEC_USECALC,    calc_exec},
  1874.   {"exit",      EXEC_JUMP,        exec_exit},
  1875.   {"fileappend",EXEC_DIR|EXEC_SUBST,    fileappend},
  1876.   {"fileexists",EXEC_STRING|EXEC_USECALC, calc_fileexists},
  1877.   {"filelist",  EXEC_DIR|EXEC_SUBST|EXEC_USECALC,calc_listfile},
  1878.   {"fileout",   EXEC_HREF|EXEC_SUBST,    exec_getfile},
  1879.   {"filewrite", EXEC_DIR|EXEC_SUBST,    filewrite},
  1880.   {"filexists",EXEC_STRING|EXEC_USECALC, calc_fileexists},
  1881.   {"for",       EXEC_FOR,        exec_for},
  1882.   {"form",      EXEC_HREF|EXEC_SUBST,    exec_form},
  1883.   {"formbar",   EXEC_HREF,        exec_formbar},
  1884.   {"formcheckbox",EXEC_HREF,        exec_formcheckbox},
  1885.   {"formend",   EXEC_STRING,    exec_formend},
  1886.   {"formradio", EXEC_HREF,        exec_formradio},
  1887.   {"formradiobar",EXEC_HREF,        exec_formbar},
  1888.   {"formselect",EXEC_HREF,        exec_formselect},
  1889.   {"getdef",    EXEC_SCORE|EXEC_USECALC,calc_defof},
  1890.   {"getfile",   EXEC_HREF|EXEC_SUBST,    exec_getfile},
  1891.   {"getscore",  EXEC_SCORE|EXEC_SUBST|EXEC_USECALC,calc_getscore},
  1892.   {"getscorebest",EXEC_SCORE|EXEC_SUBST|EXEC_USECALC,calc_getscorebest},
  1893.   {"getscorelast",EXEC_SCORE|EXEC_SUBST|EXEC_USECALC,calc_getscorelast},
  1894.   {"getscorelevel",EXEC_SCORE|EXEC_SUBST|EXEC_USECALC,calc_getscorelevel},
  1895.   {"getscoremean",EXEC_SCORE|EXEC_SUBST|EXEC_USECALC,calc_getscoremean},
  1896.   {"getscorepercent",EXEC_SCORE|EXEC_SUBST|EXEC_USECALC,calc_getscorepercent},
  1897.   {"getscorequality",EXEC_SCORE|EXEC_SUBST|EXEC_USECALC,calc_getscoremean},
  1898.   {"getscoreremain",EXEC_SCORE|EXEC_SUBST|EXEC_USECALC,calc_getscoreremain},
  1899.   {"getscorerequire",EXEC_SCORE|EXEC_SUBST|EXEC_USECALC,calc_getscorerequire},
  1900.   {"getscoretry",EXEC_SCORE|EXEC_SUBST|EXEC_USECALC,calc_getscoretry},
  1901.   {"getscoreweight",EXEC_SCORE|EXEC_SUBST|EXEC_USECALC,calc_getscoreweight},
  1902.   {"goto",      EXEC_JUMP|EXEC_SUBST,    exec_goto},
  1903.   {"header",    EXEC_HREF,        exec_header},
  1904.   {"header1",   EXEC_HREF,        exec_header1},
  1905.   {"headmathjax",EXEC_HREF,  exec_headmathjax},
  1906.   {"headmenu",  EXEC_HREF,        exec_headmenu},
  1907.   {"hex",       EXEC_STRING|EXEC_SUBST|EXEC_USECALC,calc_hex},
  1908.   {"homeref",   EXEC_HREF,        exec_homeref},
  1909.   {"href",      EXEC_HREF,        exec_href},
  1910.   {"html2iso",  EXEC_STRING|EXEC_USECALC|EXEC_SUBST,calc_html2iso},
  1911.   {"htmlbar",   EXEC_HREF,        exec_formbar},
  1912.   {"htmlcheckbox",EXEC_HREF,        exec_formcheckbox},
  1913.   {"htmlheader",EXEC_HREF,        exec_header},
  1914.   {"htmlmath",  EXEC_STRING|EXEC_SUBST|EXEC_USECALC,htmlmath},
  1915.   {"htmlradio", EXEC_HREF,        exec_formradio},
  1916.   {"htmlradiobar",EXEC_HREF,        exec_formbar},
  1917.   {"htmlselect",EXEC_HREF,        exec_formselect},
  1918.   {"htmltail",  EXEC_HREF,        exec_tail},
  1919.   {"htmltitle", EXEC_HREF,        exec_title},
  1920.   {"if",        EXEC_IF,        exec_if},
  1921.   {"ifval",     EXEC_IF,        exec_ifval},
  1922.   {"ifvalue",   EXEC_IF,        exec_ifval},
  1923.   {"imgrename", EXEC_STRING|EXEC_USECALC|EXEC_SUBST,calc_imgrename},
  1924.   {"include",   EXEC_READ|EXEC_SUBST,   exec_read},
  1925.   {"increase",  EXEC_VAR|EXEC_SUBST,    exec_increase},
  1926.   {"input",     EXEC_READ|EXEC_SUBST,    exec_read},
  1927.   {"insdraw",   EXEC_INS|EXEC_SUBST,    exec_insdraw},
  1928.   {"insmath",   EXEC_INS,        insmath},
  1929.   {"inspaint",  EXEC_INS|EXEC_SUBST,    exec_insdraw},
  1930.   {"insplot",   EXEC_INS|EXEC_SUBST,    exec_insplot},
  1931.   {"insplot3d", EXEC_INS|EXEC_SUBST,    exec_insplot3d},
  1932.   {"instex",    EXEC_INS,        exec_instex},
  1933.   {"instexst",  EXEC_INS|EXEC_USECALC,    calc_instexst},
  1934.   {"instexstatic",EXEC_INS|EXEC_USECALC,    calc_instexst},
  1935.   {"item",        EXEC_STRING|EXEC_USECALC,calc_itemof},
  1936.   {"items",     EXEC_STRING|EXEC_USECALC,calc_itemof},
  1937.   {"items2lines", EXEC_STRING|EXEC_SUBST|EXEC_USECALC,items2lines},
  1938.   {"items2words",EXEC_STRING|EXEC_SUBST|EXEC_USECALC,items2words},
  1939.   {"itemstolines",EXEC_STRING|EXEC_SUBST|EXEC_USECALC,items2lines},
  1940.   {"itemstowords",EXEC_STRING|EXEC_SUBST|EXEC_USECALC,items2words},
  1941.   {"let",        EXEC_SET,        exec_set},
  1942.   {"leveldata",  EXEC_STRING|EXEC_USECALC,calc_leveldata},
  1943.   {"levelpoints",EXEC_STRING|EXEC_USECALC,calc_leveldata},
  1944.   {"line",       EXEC_STRING|EXEC_USECALC,calc_lineof},
  1945.   {"lines",      EXEC_STRING|EXEC_USECALC,calc_lineof},
  1946.   {"lines2items",EXEC_STRING|EXEC_SUBST|EXEC_USECALC,lines2items},
  1947.   {"lines2list", EXEC_STRING|EXEC_SUBST|EXEC_USECALC,lines2items},
  1948.   {"lines2words",EXEC_STRING|EXEC_SUBST|EXEC_USECALC,lines2words},
  1949.   {"linestoitems",EXEC_STRING|EXEC_SUBST|EXEC_USECALC,lines2items},
  1950.   {"linestolist",EXEC_STRING|EXEC_SUBST|EXEC_USECALC,lines2items},
  1951.   {"linestowords",EXEC_STRING|EXEC_SUBST|EXEC_USECALC,lines2words},
  1952.   {"list2lines", EXEC_STRING|EXEC_SUBST|EXEC_USECALC,items2lines},
  1953.   {"list2words", EXEC_STRING|EXEC_SUBST|EXEC_USECALC,items2words},
  1954.   {"listfile",   EXEC_DIR|EXEC_SUBST|EXEC_USECALC,calc_listfile},
  1955.   {"listfiles",  EXEC_DIR|EXEC_SUBST|EXEC_USECALC,calc_listfile},
  1956.   {"listintersect",EXEC_STRING|EXEC_USECALC,calc_listintersect},
  1957.   {"listintersection", EXEC_STRING|EXEC_USECALC,calc_listintersect},
  1958.   {"listtolines",EXEC_STRING|EXEC_SUBST|EXEC_USECALC,items2lines},
  1959.   {"listtowords",EXEC_STRING|EXEC_SUBST|EXEC_USECALC,items2words},
  1960.   {"listunion",  EXEC_STRING|EXEC_USECALC,calc_listunion},
  1961.   {"listuniq",   EXEC_STRING|EXEC_SUBST|EXEC_USECALC,calc_listuniq},
  1962.   {"listunique", EXEC_STRING|EXEC_SUBST|EXEC_USECALC,calc_listuniq},
  1963.   {"listvar",    EXEC_STRING|EXEC_SUBST|EXEC_USECALC,mathvarlist},
  1964.   {"lookup",     EXEC_STRING|EXEC_USECALC,    calc_lookup},
  1965.   {"lower",      EXEC_STRING|EXEC_SUBST|EXEC_USECALC,calc_tolower},
  1966.   {"lowercase",  EXEC_STRING|EXEC_SUBST|EXEC_USECALC,calc_tolower},
  1967.   {"ls",         EXEC_DIR|EXEC_SUBST|EXEC_USECALC,calc_listfile},
  1968.   {"mailto",     EXEC_MAIL|EXEC_SUBST,    exec_mailto},
  1969.   {"mailurl",    EXEC_MAIL|EXEC_SUBST|EXEC_USECALC,calc_mailurl},
  1970.   {"makelist",   EXEC_STRING|EXEC_USECALC,calc_makelist},
  1971.   {"math2html",  EXEC_STRING|EXEC_SUBST|EXEC_USECALC,htmlmath},
  1972.   {"math2mathml",EXEC_STRING|EXEC_SUBST|EXEC_USECALC,mathmlmath},
  1973.   {"math2tex",   EXEC_STRING|EXEC_SUBST|EXEC_USECALC,texmath},
  1974.   {"mathmlmath", EXEC_STRING|EXEC_SUBST|EXEC_USECALC,mathmlmath},
  1975.   {"mathsubst",  EXEC_STRING|EXEC_USECALC,calc_mathsubst},
  1976.   {"mathsubstit",EXEC_STRING|EXEC_USECALC,calc_mathsubst},
  1977.   {"mathsubstitute",EXEC_STRING|EXEC_USECALC,calc_mathsubst},
  1978.   {"mexec",     EXEC_EXEC|EXEC_SUBST,    exec_mexec},
  1979.   {"module",    EXEC_STRING|EXEC_SUBST|EXEC_USECALC,calc_module},
  1980.   {"msg",       EXEC_EXEC|EXEC_SUBST,exec_msg},
  1981.   {"multiply",  EXEC_STRING|EXEC_USECALC,calc_product},
  1982.   {"next",      EXEC_FOR,        exec_next},
  1983.   {"nocache",   EXEC_READ,        exec_nocache},
  1984.   {"non_empty", EXEC_STRING|EXEC_SUBST|EXEC_USECALC,calc_nonempty},
  1985.   {"nonempty",  EXEC_STRING|EXEC_SUBST|EXEC_USECALC,calc_nonempty},
  1986.   {"nospace",   EXEC_STRING|EXEC_SUBST|EXEC_USECALC,nospace},
  1987.   {"outfile",   EXEC_HREF|EXEC_SUBST,    exec_getfile},
  1988.   {"pedia",     EXEC_PEDIA|EXEC_SUBST|EXEC_USECALC,pedia},
  1989.   {"perl",      EXEC_EXEC|EXEC_PROCTOO|EXEC_SUBST,exec_perl},
  1990.   {"position",  EXEC_STRING|EXEC_USECALC,calc_pos},
  1991.   {"positionof",EXEC_STRING|EXEC_USECALC,calc_pos},
  1992.   {"positions", EXEC_STRING|EXEC_USECALC,calc_pos},
  1993.   {"prod",      EXEC_STRING|EXEC_USECALC,calc_product},
  1994.   {"product",   EXEC_STRING|EXEC_USECALC,calc_product},
  1995.   {"rawmath",   EXEC_STRING|EXEC_SUBST|EXEC_USECALC,rawmath},
  1996.   {"rawmatrix", EXEC_STRING|EXEC_SUBST|EXEC_USECALC,rawmatrix},
  1997.   {"reaccent",  EXEC_STRING|EXEC_USECALC|EXEC_SUBST,reaccent},
  1998.   {"read",      EXEC_READ|EXEC_SUBST,    exec_read},
  1999.   {"readdef",   EXEC_READ|EXEC_SUBST,    exec_defread},
  2000.   {"readproc",  EXEC_READ|EXEC_SUBST,    exec_readproc},
  2001.   {"record",    EXEC_STRING|EXEC_USECALC,calc_recordof},
  2002.   {"records",   EXEC_STRING|EXEC_USECALC,calc_recordof},
  2003.   {"recursion", EXEC_STRING|EXEC_USECALC,calc_recursion},
  2004.   {"reinput",   EXEC_STRING|EXEC_USECALC|EXEC_SUBST,calc_reinput},
  2005.   {"rem",       EXEC_COMMENT,        exec_comment},
  2006.   {"remark",    EXEC_COMMENT,        exec_comment},
  2007.   {"replace",   EXEC_STRING|EXEC_USECALC,calc_replace},
  2008.   {"reset",     EXEC_SET|EXEC_SUBST,    exec_reset},
  2009.   {"restart",   EXEC_JUMP|EXEC_SUBST,    exec_restart},
  2010.   {"return",    EXEC_JUMP,        exec_exit},
  2011.   {"robotrap",  EXEC_HREF|EXEC_SUBST,    exec_robottrap},
  2012.   {"robottrap", EXEC_HREF|EXEC_SUBST,    exec_robottrap},
  2013.   {"rootof",    EXEC_STRING|EXEC_USECALC,calc_solve},
  2014.   {"row",       EXEC_STRING|EXEC_USECALC,calc_rowof},
  2015.   {"rows",      EXEC_STRING|EXEC_USECALC,calc_rowof},
  2016.   {"rows2lines",EXEC_STRING|EXEC_USECALC|EXEC_SUBST,calc_rows2lines},
  2017.   {"run",       EXEC_EXEC|EXEC_SUBST|EXEC_PROCTOO|EXEC_USECALC,    calc_exec},
  2018.   {"select",    EXEC_STRING|EXEC_USECALC,calc_select},
  2019.   {"set",       EXEC_SET,        exec_set},
  2020.   {"setdef",    EXEC_OTHER,        exec_setdef},
  2021.   {"setseed",   EXEC_STRING|EXEC_SUBST,        exec_setseed},
  2022.   {"sh",        EXEC_EXEC|EXEC_PROCTOO|EXEC_SUBST,exec_sh},
  2023.   {"shortout",  EXEC_JUMP|EXEC_SUBST,    exec_directout},
  2024.   {"singlespace",EXEC_STRING|EXEC_SUBST|EXEC_USECALC,singlespace},
  2025.   {"solve",     EXEC_STRING|EXEC_USECALC,calc_solve},
  2026.   {"sort",      EXEC_STRING|EXEC_USECALC, calc_sort},
  2027. /*      {"sql",     EXEC_SQL|EXEC_SUBST|EXEC_USECALC, calc_sql}, */
  2028.   {"staticinstex",EXEC_INS|EXEC_USECALC,    calc_instexst},
  2029.   {"stinstex",  EXEC_INS|EXEC_USECALC,    calc_instexst},
  2030.   {"sum",       EXEC_STRING|EXEC_USECALC,calc_sum},
  2031.   {"system",    EXEC_EXEC|EXEC_PROCTOO|EXEC_SUBST,exec_sh},
  2032.   {"tail",      EXEC_HREF,        exec_tail},
  2033.   {"test",      EXEC_DEBUG,        exec_test},
  2034.   {"texmath",   EXEC_STRING|EXEC_SUBST|EXEC_USECALC,texmath},
  2035.   {"text",      EXEC_STRING|EXEC_USECALC,text},
  2036.   {"title",     EXEC_HREF,        exec_title},
  2037.   {"tohex",     EXEC_STRING|EXEC_SUBST|EXEC_USECALC,calc_hex},
  2038.   {"tolower",   EXEC_STRING|EXEC_SUBST|EXEC_USECALC,calc_tolower},
  2039.   {"toupper",   EXEC_STRING|EXEC_SUBST|EXEC_USECALC,calc_toupper},
  2040.   {"translate", EXEC_STRING|EXEC_USECALC,calc_translate},
  2041.   {"trim",      EXEC_STRING|EXEC_USECALC,calc_trim},
  2042.   {"upper",     EXEC_STRING|EXEC_SUBST|EXEC_USECALC,calc_toupper},
  2043.   {"uppercase", EXEC_STRING|EXEC_SUBST|EXEC_USECALC,calc_toupper},
  2044.   {"usererror", EXEC_WARN|EXEC_SUBST,    exec_usererror},
  2045.   {"values",    EXEC_STRING|EXEC_USECALC,calc_values},
  2046.   {"varlist",   EXEC_STRING|EXEC_SUBST|EXEC_USECALC,mathvarlist},
  2047.   {"warn",      EXEC_WARN|EXEC_SUBST,    exec_warn},
  2048.   {"warning",   EXEC_WARN|EXEC_SUBST,    exec_warn},
  2049.   {"while",     EXEC_WHILE,        exec_while},
  2050.   {"whileval",  EXEC_WHILE,        exec_whileval},
  2051.   {"whilevalue",EXEC_WHILE,        exec_whileval},
  2052.   {"wimsheader",EXEC_HREF,        exec_header},
  2053.   {"wimsref",   EXEC_HREF,        exec_homeref},
  2054.   {"wimstail",  EXEC_HREF,        exec_tail},
  2055.   {"wimstitle", EXEC_HREF,        exec_title},
  2056.   {"word",      EXEC_STRING|EXEC_USECALC,calc_wordof},
  2057.   {"words",     EXEC_STRING|EXEC_USECALC,calc_wordof},
  2058.   {"words2items",EXEC_STRING|EXEC_SUBST|EXEC_USECALC,words2items},
  2059.   {"words2lines",EXEC_STRING|EXEC_SUBST|EXEC_USECALC,words2lines},
  2060.   {"words2list",EXEC_STRING|EXEC_SUBST|EXEC_USECALC,words2items},
  2061.   {"wordstoitems",EXEC_STRING|EXEC_SUBST|EXEC_USECALC,words2items},
  2062.   {"wordstolines",EXEC_STRING|EXEC_SUBST|EXEC_USECALC,words2lines},
  2063.   {"wordstolist",EXEC_STRING|EXEC_SUBST|EXEC_USECALC,words2items},
  2064.   {"writefile", EXEC_DIR|EXEC_SUBST,    filewrite},
  2065. };
  2066. int EXEC_FN_NO=(sizeof(exec_routine)/sizeof(exec_routine[0]));
  2067.  
  2068. /* internal: to skip the content of a false if/while. */
  2069. static void _skip_contents(int isif)
  2070. {
  2071.   char buf[MAX_NAMELEN+8], *p1;
  2072.   int i,j,loop;
  2073.   loop=0;
  2074.   while(m_file.linepointer<m_file.linecnt) {
  2075.     j=m_file.linepointer;
  2076.     if((m_file.lines[j].isstart&2)==0) {
  2077.       m_file.linepointer++; continue;
  2078.     }
  2079.     i=m_file.lines[j].execcode;
  2080.     if(i<0) {
  2081.       if(wgetline(buf,MAX_NAMELEN+4,&m_file)==EOF) return;
  2082.       p1=buf+1; if(*p1!='i' && *p1!='e' && *p1!='w') continue;
  2083.       *find_word_end(p1)=0;
  2084.       i=search_list(exec_routine,EXEC_FN_NO,sizeof(exec_routine[0]),p1);
  2085.       if(i>=0) m_file.lines[j].execcode=i;
  2086.     }
  2087.     else m_file.linepointer++;
  2088.     if(i<0) continue;
  2089.     switch(exec_routine[i].tag & 0xffff) {
  2090.       case EXEC_WHILE:
  2091.         if(!isif) loop++;
  2092.         break;
  2093.       case EXEC_IF:
  2094.         if(isif) loop++;
  2095.         break;
  2096.       case EXEC_ELSE: {
  2097.         if(!isif) break;
  2098.         if(loop<=0) return; else break;
  2099.       }
  2100.       case EXEC_ENDIF: {
  2101.         if(!isif) break;
  2102.         if(loop>0) {
  2103.           loop--; break;
  2104.         }
  2105.         else return;
  2106.       }
  2107.       case EXEC_ENDWHILE: {
  2108.         if(isif) break;
  2109.         if(loop>0) {
  2110.           loop--; break;
  2111.         }
  2112.         else return;
  2113.       }
  2114.       default: break;
  2115.     }
  2116.   }
  2117. }
  2118.  
  2119. /* Execute a command defined by !. Returns 0 if OK. */
  2120. void exec_main(char *p)
  2121. {
  2122.   int i,j;
  2123.   char *pp;
  2124.   char tbuf2[MAX_LINELEN+1];
  2125.  
  2126.   pp=find_word_end(p);
  2127.   if(*pp!=0) {
  2128.     *(pp++)=0; pp=find_word_start(pp);
  2129.   }
  2130.   i=m_file.lines[m_file.l].execcode;
  2131.   if(i<0) {
  2132.     i=search_list(exec_routine,EXEC_FN_NO,sizeof(exec_routine[0]),p);
  2133.     m_file.lines[m_file.l].execcode=i;
  2134.   }
  2135.   if(i<0) {
  2136.     setvar(error_data_string,p); module_error("bad_cmd");
  2137.   }
  2138. /* called from !readdef, no right other than set; bail out */
  2139.   execnt++;
  2140.   if((untrust&4)!=0 && (j=(exec_routine[i].tag&0xffff))!=EXEC_SET) {
  2141.     tbuf2[0]=0; exec_exit(tbuf2);
  2142.   }
  2143.   ovlstrcpy(tbuf2,pp); j=exec_routine[i].tag;
  2144.   if(j&EXEC_SUBST) substit(tbuf2);
  2145.   if(j&EXEC_USECALC) {
  2146.     if(!outputing && (j&EXEC_PROCTOO)==0) return;
  2147.     exec_routine[i].routine(tbuf2); if(outputing) output0(tbuf2);
  2148.   }
  2149.   else exec_routine[i].routine(tbuf2);
  2150.   return;
  2151. }
  2152.