Logo Search packages:      
Sourcecode: wims version File versions  Download package

calc.c

/*    Copyright (C) 1998 XIAO, Gang of Universite de Nice - Sophia Antipolis
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

      /* This is part of wims source.
       * This file does computations and output. */

int _sort_numeric, _sort_nocase;
00022 typedef struct SORT_STRUCT {char *str; double val; int serial;} SORT_STRUCT;
struct SORT_STRUCT sort_sep[MAX_SORT_ITEM+1];

void secure_exec(void)
{
    if((untrust&6)==0) return;
    error2("Illegal_command");
}

      /* internal comparers */
int __sorter(const void *p1, const void *p2)
{
    struct SORT_STRUCT *pp1, *pp2;
    
    (const void *) pp1=p1; (const void *) pp2=p2;
    if(_sort_numeric) {
      double dd=(pp1->val)-(pp2->val);
      if(dd>0) return 1;
      if(dd<0) return -1;
      return 0;
    }
    if(_sort_nocase) return strcasecmp(pp1->str,pp2->str);
    else return strcmp(pp1->str,pp2->str);
}

int _char_sorter(const void *c1, const void *c2)
{
    char *cc1,*cc2;
    (const char *) cc1=c1; (const char *) cc2=c2;
    if(_sort_nocase) return tolower(*cc1)-tolower(*cc2);
    else return *cc1-*cc2;
}

enum {sort_char, sort_word, sort_item, sort_line, sort_row,
      sort_numeric, sort_nocase, sort_reverse
};
struct {char *name; int type;
} sort_keyw[]={
          {"char",sort_char},
        {"chars",sort_char},
        {"character",sort_char},
        {"characters",sort_char},
        {"word",sort_word},
        {"words",sort_word},
        {"item",sort_item},
        {"items",sort_item},
        {"line",sort_line},
        {"lines",sort_line},
        {"list",sort_item},
        {"numeric",sort_numeric},
        {"numerical",sort_numeric},
        {"nocase",sort_nocase},
        {"ignorecase",sort_nocase},
        {"reverse",sort_reverse},
        {"row",sort_row},
        {"rows",sort_row}
};
#define sort_keyw_no (sizeof(sort_keyw)/sizeof(sort_keyw[0]))

      /* Print a debug output */
void calc_debug(char *p)
{
    secure_exec(); setenv("debug",p,1); error2("debug");
}

      /* string sort */
void calc_sort(char *p)
{
    char *p1, *p2, buf[MAX_LINELEN+1], *csep[MAX_LINELEN+1];
    char fs=0, fsbuf[4];
    int nocase=0, reverse=0, numeric=0, type;
    int i,t,total;
    
    for(i=0,p1=find_word_start(p);i>=0;p1=find_word_start(p2)) {
      p2=find_word_end(p1); if(*p2!=0) *p2++=0;
      for(i=0;i<sort_keyw_no && strcasecmp(p1,sort_keyw[i].name)!=0;i++);
      if(i>=sort_keyw_no) error2("syntax_error");
      switch(type=sort_keyw[i].type) {
          case sort_nocase: nocase=1; break;
          case sort_reverse: reverse=1; break;
          case sort_numeric: numeric=1; break;
          case sort_char: fs=0; i=-1; break;
          case sort_word: fs=' '; i=-1; break;
          case sort_item: fs=','; i=-1; break;
          case sort_row:
          case sort_line: fs='\n'; i=-1; break;
      }
    }
    if(*p1=='o' && *(p1+1)=='f' && isspace(*(p1+2))) p1=find_word_start(p1+2);
    snprintf(buf,sizeof(buf),"%s",p1); substit(buf); *p=0;
    t=0; if(type==sort_row) t=rows2lines(buf);
    snprintf(fsbuf,sizeof(fsbuf),"%c",fs);
    _sort_nocase=nocase; _sort_numeric=numeric;
    if(fs!=0) {
      char ordbuf[MAX_LINELEN+1]="";
      total=_separator(buf,csep,MAX_LINELEN,fs);
      if(total<=0 || total>MAX_SORT_ITEM) return;
      for(i=0;i<total;i++) {
          sort_sep[i].str=csep[i];
          if(numeric) sort_sep[i].val=evalue(csep[i]);
      }
      for(i=0;i<total;i++) sort_sep[i].serial=i+1;
      qsort(sort_sep,total,sizeof(sort_sep[0]),__sorter);
      if(reverse) {
          for(i=total-1;i>=0;i--) {
            strcat(p,sort_sep[i].str);    
            if(i>0) strcat(p,fsbuf);
            if(strlen(ordbuf)<MAX_LINELEN-20) {
                snprintf(ordbuf+strlen(ordbuf),15,"%d",sort_sep[i].serial);
                if(i>0) strcat(ordbuf,",");
            }
          }
      }
      else {
          for(i=0;i<total;i++) {
            strcat(p,sort_sep[i].str);
            if(i<total-1) strcat(p,fsbuf);
            if(strlen(ordbuf)<MAX_LINELEN-20) {
                snprintf(ordbuf+strlen(ordbuf),15,"%d",sort_sep[i].serial);
                if(i<total-1) strcat(ordbuf,",");
            }
          }
      }
      setvar("wims_sort_order",ordbuf);
    }
    else { /* case of chars */
      qsort(buf,strlen(buf),1,_char_sorter);
      total=strlen(buf);
      if(reverse) {
          for(i=total-1;i>=0;i--) *(p+total-i-1)=buf[i];
          *(p+total)=0;
      }
      else strcpy(p,buf);
    }
    if(t) lines2rows(p);
}

      /* execute an external program */
      /* The output of the external program should be put into
       * a file session_directory/session/cmd.tmp. */
void calc_exec(char *p)
{
    int i,j,k;
    char *cmd, *parm;
    char namebuf[MAX_EXEC_NAME+1], cmdstr[MAX_LINELEN+256], obuf[MAX_LINELEN+1];
    char outfname[230], errorfname[230], typefname[230], varfname[230];
    char *abuf[2]={NULL,NULL};
    struct stat st;
    FILE *tmpf;
    WORKING_FILE wf;

    if(robot_access || time(0)>=limtimex) {
      *p=0;return;
    }
    cmd=find_word_start(p); if(*cmd==0) return; /* No command. */
    parm=find_word_end(cmd);j=parm-cmd;parm=find_word_start(parm);
    if(j>MAX_EXEC_NAME) {
      wrong_command:
      setenv(error_data_string,namebuf,1); error2("bad_cmd");
      *p=0; return; /* should not occur */
    }
    memmove(namebuf,cmd,j); namebuf[j]=0;
      /* Specifying parent directory in command name is of course
       * prohibited.
       * The module developper cannot start from root, for bin_dir
       * will be prefixed to cmd. */
    if(strstr(namebuf,parent_dir_string)!=NULL) {
      setenv(error_data_string,namebuf,1);      error2("illegal_cmd");
      *p=0; return;
    }
    snprintf(cmdstr,sizeof(cmdstr),"%s/%s",bin_dir,namebuf);
    i=stat(cmdstr,&st);
    if(i!=0 || !S_ISREG(st.st_mode) || (st.st_mode&S_IXUSR)==0)
      goto wrong_command;
    snprintf(outfname,sizeof(outfname),"%s/exec.out",session_prefix);
    snprintf(errorfname,sizeof(errorfname),"%s/exec.err",session_prefix);
    snprintf(typefname,sizeof(typefname),"%s/exec.type",session_prefix);
    unlink(typefname);
    snprintf(varfname,sizeof(varfname),"%s/exec.var",session_prefix);
    unlink(varfname);
    if(!trusted_module()) setenv("untrust","yes",1); else unsetenv("untrust");
    if(multiexec(namebuf)==0) exportall();
    setenv("wims_exec_parm",parm,1); abuf[0]=cmdstr;
    execredirected(cmdstr,NULL,outfname,errorfname,abuf);
    if(open_working_file(&wf,varfname)==0) {
      char *pn,*pv;
      while(wgetline(obuf,MAX_LINELEN, &wf)!=EOF) { 
          pv=strchr(obuf,'=');
          if(pv==NULL || pv<=obuf) continue;
          *pv=0; pv=find_word_start(++pv);
          pn=find_word_start(obuf);*find_word_end(pn)=0;
          strip_trailing_spaces(pv);
          setvar(pn,pv);
      }
      close_working_file(&wf);
    }
    read_tmp_file(p,"exec.out");
    read_tmp_file(obuf,"exec.type");
    if(obuf[0]) k=atoi(obuf); else k=0;
    for(i=2;i<=k;i++) {
      char nbuf[64];
      snprintf(nbuf,sizeof(nbuf),"exec.out.%d",i);
      obuf[0]=0;read_tmp_file(obuf,nbuf);
      if(obuf[0]) {
          snprintf(nbuf,sizeof(nbuf),"wims_exec_out_%d",i);
          setvar(nbuf,obuf);
      }
    }
    strip_trailing_spaces(p);
    if((tmpf=fopen(errorfname,"r"))!=NULL) {
      char *p2,buf[MAX_LINELEN+1];
      size_t n;
      n=fread(buf,1,MAX_LINELEN,tmpf); fclose(tmpf);
      if(n<=0) buf[0]=0;
      else {
          buf[n]=0; setvar("wims_exec_error",buf);
          p2=find_word_end(buf);
          if(strncmp(p2," not_INStalled",strlen(" not_INStalled"))==0) {
            *p2=0; setvar("missing_software",buf);
            snprintf(p2+1,MAX_LINELEN-strlen(buf)-8,"missing_%s",buf);
            if(!outputing) user_error(p2+1);
          }
      }
    }
    return;
}

      /* execute external program in the module directory. 
       * For privileged modules only */
void calc_mexec(char *p)
{
    char *public_bin;
    if(robot_access) return;
    if(trusted_module()!=1) {
      error2("not_trusted"); *p=0; return;
    }
    public_bin=bin_dir; bin_dir=module_prefix;
    exec_is_module=1;
    calc_exec(p); bin_dir=public_bin;
    exec_is_module=0;
}

      /* call shell. */
void calc_sh(char *p)
{
    char *abuf[8];
    char outfname[230], errorfname[230];

    if(robot_access || !trusted_module()) {
      *p=0; return;
    }
    snprintf(outfname,sizeof(outfname),"%s/exec.out",session_prefix);
    snprintf(errorfname,sizeof(errorfname),"%s/exec.err",session_prefix);
    abuf[0]="sh"; abuf[1]="-c"; abuf[2]=p; abuf[3]=NULL;
    wrapexec=1; exportall();
    execredirected(abuf[0],NULL,outfname,errorfname,abuf);
    read_tmp_file(p,"exec.out");
}

      /* sql database interface. For privileged modules only. */
void calc_sql(char *p)
{
    char errorfname[MAX_LINELEN+1];
    char *pp;
    FILE *tmpf;
    
    singlespace(p); strip_trailing_spaces(p);
      /* replace '|' by a html equivalent */
    for(pp=strchr(p,'|');pp!=NULL;pp=strchr(pp,'|')) {
      string_modify(p,pp,pp+1,"&#122;");
    }
    if(internal_sql==0) {
      if(!trusted_module()) {
          error2("not_trusted"); *p=0; return;
      }
    }
    setenv("wims_exec_parm",p,1);
    snprintf(errorfname,sizeof(errorfname),"%s/sql.err",
           session_prefix);
    call_ssh("%s/sql..processor >%s/sql.out 2>%s",bin_dir,session_prefix,errorfname);
    read_tmp_file(p,"sql.out");
    strip_trailing_spaces(p);
    if((tmpf=fopen(errorfname,"r"))!=NULL) {
      char buf[MAX_LINELEN+1];
      size_t n;
      n=fread(buf,1,MAX_LINELEN,tmpf);
      if(n<0) n=0; buf[n]=0;
      setvar("wims_exec_error",buf);
      fclose(tmpf);
    }
}

      /* simple evaluation of string */
void calc_evalue(char *p)
{
    double d;
    d=evalue(p);
    float2str(d,p);
    return;
}

      /* substitute math variables */
void calc_mathsubst(char *p)
{
    char *expr, *val, *nam, *pp;
    char buf[MAX_LINELEN+1];
    expr=wordchr(p,"in"); if(expr==NULL) goto error;
    strcpy(buf,find_word_start(expr+strlen("in")));substit(buf);
    *expr=0; substit(p);
    val=strchr(p,'=');
    if(val==NULL) {
      error:
      error2("mathsubst_syntax"); *p=0;return; 
    }
    nam=find_word_start(p); *val=0;
    val=find_word_start(val+1);
    for(pp=val+strlen(val)-1;pp>=val && isspace(*pp);*pp--=0);
    *find_word_end(nam)=0;
    if(*nam==0) goto error;
    if(*nam) for(pp=varchr(buf,nam);pp!=NULL;pp=varchr(pp,nam)) {
      string_modify(buf,pp,pp+strlen(nam),"%s",val);
      pp+=strlen(val);
    }
    strcpy(p,buf);
}

      /* substitute and evaluate. */
void calc_evalsubst(char *p)
{
    calc_mathsubst(p);
    calc_evalue(p);
}

      /* Nothing needs to be done in the function; 
       * substitution is done or not by tag in the structure. */
void calc_subst(char *p)
{
}

int _randrep(char *p)
{
    char *rep, buf[MAX_LINELEN+1];
    int i;
    rep=wordchr(p,"repeat"); if(rep==NULL) return 1;
    *rep=0; rep+=strlen("repeat"); rep=find_word_start(rep);
    snprintf(buf,sizeof(buf),"%s",rep); substit(buf);
    i=evalue(buf); if(i<1) i=1; if(i>MAX_VALUE_LIST) i=MAX_VALUE_LIST;
    return i;
}

      /* integer random */
void calc_randint(char *p)
{
    int lbound, ubound, n, i, t;
    char *p1, *parm[2];

    t=_randrep(p);
    n=separate_item(p,parm,2);
    if(n<=0) {
      snprintf(p,3,"0"); return; /* Random without bound: return 0. */
    }
    lbound=evalue(parm[0]);
    if(n<=1) ubound=0;  /* Missing ubound: random between 0 and lbound. */
    else ubound=evalue(parm[1]);
    for(i=0,p1=p;i<t;i++) {
      snprintf(p1,MAX_LINELEN-(p1-p),"%d",(int) irand(ubound-lbound+1)+lbound);
      p1+=strlen(p1);
      if(i<t-1 && p1-p<MAX_LINELEN) *p1++=',';
    }
    return;
}

      /* floating random */
void calc_randdouble(char *p)
{
    double lbound, ubound;
    int n, i, t;
    char *parm[2], *p1, buf[MAX_LINELEN+1];
    
    t=_randrep(p);
    n=separate_item(p,parm,2);
    if(n<=0) {
      snprintf(p,3,"0"); return; /* Random without bound: return 0. */
    }
    lbound=evalue(parm[0]);
    if(n<=1) ubound=0; /* Missing ubound: random between 0 and lbound. */
    else ubound=evalue(parm[1]);
    for(i=0,p1=p;i<t;i++) {
      float2str(drand(ubound-lbound)+lbound,buf);
      snprintf(p1,MAX_LINELEN-(p1-p),"%s",buf);
      p1+=strlen(p1);
      if(i<t-1 && p1-p<MAX_LINELEN) *p1++=',';
    }
    return;
}

      /* Takes randomly a record in a datafile */
void calc_randfile(char *n)
{
    char *p, buf[MAX_LINELEN+1];
    int i, j;
    
    p=find_word_start(n); *find_word_end(p)=0;
    i=datafile_recordnum(p);
    if(i<=0) {  /* zero records */
      *n=0; return;
    }
    j=irand(i); p=datafile_fnd_record(p,j+1,buf);
    snprintf(n,MAX_LINELEN,"%s",p);
    return;
}

      /* random char, word, line, item in a string */
void calc_randchar(char *p)
{
    int i,j;
    i=strlen(p);
    j=irand(i);
    p[0]=p[j];p[1]=0;
}

void calc_randitem(char *p)
{
    int i; char *b, buf[MAX_LINELEN+1];
    i=itemnum(p);
    b=fnd_item(p,irand(i)+1,buf);
    strcpy(p,b);
}

void calc_randline(char *p)
{
    int i; char *b, buf[MAX_LINELEN+1];
    i=linenum(p); b=fnd_line(p,irand(i)+1,buf);
    strcpy(p,b);
}

void calc_randrow(char *p)
{
    int i, t; char *b, buf[MAX_LINELEN+1];
    t=rows2lines(p);
    i=linenum(p); b=fnd_line(p,irand(i)+1,buf);
    strcpy(p,b);
}

void calc_randword(char *p)
{
    int i; char *b, buf[MAX_LINELEN+1];
    i=wordnum(p); b=fnd_word(p,irand(i)+1,buf);
    strcpy(p,b);
}

      /* random permutation of {1,...,n} */
void calc_randperm(char *p)
{
    int n, i, j, k, t, pt, type;
    char buf1[MAX_LINELEN+1], buf2[MAX_LINELEN+1];
    int table[1024];
    char *p1,*p2,*pp;
    
    t=0; pt=0; pp=p;
    p1=find_word_start(p); p2=find_word_end(p1);
    if(p2-p1==strlen("even") && strncasecmp(p1,"even",strlen("even"))==0) {
      t=1; pp=p2;
    }
    if(p2-p1==strlen("odd") && strncasecmp(p1,"odd",strlen("odd"))==0) {
      t=-1; pp=p2;
    }
    
    n=itemnum(pp); if(n>1) {
      type=1; memmove(p,pp,strlen(pp)+1);
    }
    else {
      n=evalue(pp); *p=0; type=0;
    }
    if(n<=0) return;
    if(n==1) {
      strcpy(p,"1"); return;
    }
    if(n>1024) n=1024;
    for(i=0;i<n;i++) table[i]=i+1;
    for(i=0;i<n;i++) {
      j=irand(n-i)+i;
      k=table[j];
      memmove(table+i+1,table+i,(j-i)*sizeof(table[0]));
      table[i]=k;
      pt=(pt+j-i)%2;
    }
    if((t==1 && pt==1) || (t==-1 && pt==0)) {
      t=table[0]; table[0]=table[1]; table[1]=t;
    }
    if(type==0) {
      snprintf(p,MAX_LINELEN,"%d",table[0]);
      for(i=1;i<n;i++) snprintf(p+strlen(p),MAX_LINELEN-strlen(p),",%d",table[i]);
      setvar("wims_shuffle_order",p);
    }
    else {
      fnd_item(p,table[0],buf2);
      for(i=1;i<n;i++) {
          fnd_item(p,table[i],buf1);
          snprintf(buf2+strlen(buf2),MAX_LINELEN-strlen(buf2),",%s",buf1);
      }
      snprintf(p,MAX_LINELEN,"%s",buf2);
      snprintf(buf2,MAX_LINELEN,"%d",table[0]);
      for(i=1;i<n;i++) snprintf(buf2+strlen(buf2),
                          MAX_LINELEN-strlen(buf2),",%d",table[i]);
      setvar("wims_shuffle_order",buf2);
    }
}

      /* Computes number of lines in the parm. */
void calc_linenum(char *p)
{
    int i=linenum(p); float2str(i,p);
}

      /* Computes number of lines in the parm. */
void calc_rownum(char *p)
{
    int i=rownum(p); float2str(i,p);
}

      /* Computes number of items in the list p. */
void calc_itemnum(char *p)
{
    int i=itemnum(p); float2str(i,p);
}

      /* Computes number of records in the datafile p. */
void calc_recordnum(char *p)
{
    int i=datafile_recordnum(p); float2str(i,p);
}

      /* Computes number of words in the parm. */
void calc_wordnum(char *p)
{
    int i=wordnum(p); float2str(i,p);
}

      /* string length */
void calc_lengthof(char *p)
{
    int i;
/* Strip leading and trailing spaces? */  
    i=strlen(p); float2str(i,p);
}

char *_blockof_one(char *buf,
              unsigned int (len_fn)(char *pl),
              char *(fnd_fn)(char *pl, int n, char bbuf[]),
              int i, char bbuf[])
{
    if(i<0 || i>len_fn(buf) || (i==0 && fnd_fn!=datafile_fnd_record) ) {
      bbuf[0]=0; return bbuf;
    }
    return fnd_fn(buf,i,bbuf);
}

void _blockof(char *p,
            unsigned int (len_fn)(char *pl),
            char *(fnd_fn)(char *pl, int n, char bbuf[]),
            char *append_char,
            char *msg_str)
{
    int i,j,started;
    char *pp, *pe;
    char buf[MAX_LINELEN+1], obuf[MAX_LINELEN+1], bbuf[MAX_LINELEN+1];
    pp=wordchr(p,"of");
    if(pp==NULL) {
      setenv(error_data_string,msg_str,1);
      error2("no_of"); *p=0; return;
    }
    *pp=0; pp+=strlen("of")+1;
    pp=find_word_start(pp);
    snprintf(buf,sizeof(buf),"%s",pp); substit(buf);
    obuf[0]=0; started=0;
    pp=wordchr(p,"to");
    if(pp!=NULL) {
      int t=len_fn(buf);
      *pp=0; pe=pp+strlen("to")+1;
      i=evalue(p); j=evalue(pe);
      if(i<0) i=t+i+1; if(i<1) i=1;
      if(j<0) j=t+j+1; if(j>t) j=t;
      for(; i<=j; i++) {
          pp=_blockof_one(buf,len_fn,fnd_fn,i,bbuf);
          if(strlen(obuf)+strlen(pp)>=MAX_LINELEN-3) {
  too_long:
            /* error or warning? */
            error2("parm_too_long"); return;
          }
          if(started) strcat(obuf,append_char);
          strcat(obuf,pp); started++;
      }
    }
    else {
      char *p1,*p2,ibuf[MAX_LINELEN+1];
      int t=len_fn(buf);
      strncpy(ibuf,p,MAX_LINELEN); substit(ibuf);
      for(p1=ibuf;*p1;p1=p2) {
          p2=find_item_end(p1); if(*p2) *p2++=0;
          i=evalue(p1);
          if(i<0) i=t+i+1;
          if(i>t || i<0) continue;
          pp=_blockof_one(buf,len_fn,fnd_fn,i,bbuf);
          if(strlen(obuf)+strlen(pp)>=MAX_LINELEN-3) goto too_long;
          if(started) strcat(obuf,append_char);
          strcat(obuf,pp); started++;
      }
    }
    strncpy(p,obuf,sizeof(obuf)); /* Length should be sufficiently checked already. */
}

      /* pick lines */
void calc_lineof(char *p)
{
    _blockof(p,linenum,fnd_line,"\n","line");
}

      /* pick rows */
void calc_rowof(char *p)
{
    char *cc;
    if(strchr(p,'\n')==NULL && strchr(p,';')!=NULL) cc=";";
    else cc="\n";
    _blockof(p,rownum,fnd_row,cc,"row");
}

      /* pick items */
void calc_itemof(char *p)
{
    _blockof(p,itemnum,fnd_item,", ","item");
}

      /* pick records in datafile */
void calc_recordof(char *p)
{
    _blockof(p,datafile_recordnum,datafile_fnd_record,"\n:","record");
}

      /* pick words */
void calc_wordof(char *p)
{
    _blockof(p,wordnum,fnd_word," ","word");
}

      /* pick characters */
void calc_charof(char *p)
{
    _blockof(p,charnum,fnd_char,"","char");
}

char *append_obj[]={
    "item","line","word"
};
char *apch_list[]={",","\n"," "};
#define append_obj_no (sizeof(append_obj)/sizeof(append_obj[0]))

      /* append object to string */
void calc_append(char *p)
{
    char *append_char, buf1[MAX_LINELEN+1],buf2[MAX_LINELEN+1];
    char *p1,*p2,*p3,*p4;
    int i;
    p1=find_word_start(p);p2=find_word_end(p1);
    if(*p2!=0) *p2++=0;
    for(i=0;i<append_obj_no && strcmp(p1,append_obj[i])!=0;i++);
    if(i>=append_obj_no) {
      synterr:
      error2("append_syntax");
      *p=0; return;
    }
    append_char=apch_list[i];
    p3=wordchr(p2,"to");
    if(p3==NULL) goto synterr;
    for(p4=p3-1;p4>p2 && isspace(*(p4-1));p4--);
    *p4=0;p3=find_word_start(p3+strlen("to"));
    strcpy(buf1,p2);strcpy(buf2,p3);
    substit(buf1);substit(buf2);
    p3=find_word_start(buf2);
    if(*p3==0) append_char="";
    snprintf(buf2+strlen(buf2),sizeof(buf2)-strlen(buf2),"%s%s",
           append_char,buf1);
    strcpy(p,buf2);
}

      /* character translation */
void calc_translate(char *p)
{
    int i, internal;
    char *q[3];
    char bf[3][MAX_LINELEN+1];
    char fn[3][256], *arglist[8];

    q[0]=find_word_start(p); internal=0;
    if(strncasecmp(q[0],"internal",strlen("internal"))==0 &&
       isspace(*(q[0]+strlen("internal")))) {
      q[0]=find_word_start(q[0]+strlen("internal"));
      internal=1;
    }
    q[1]=wordchr(q[0],"to"); q[2]=wordchr(q[0],"in");
    if(q[1]==NULL || q[2]==NULL) {
      error2("tr_syntax"); *p=0; return;
    }
    *q[1]=0; *q[2]=0;
    q[1]=find_word_start(q[1]+strlen("to"));
    q[2]=find_word_start(q[2]+strlen("in"));
    for(i=0;i<3;i++) {
      strip_trailing_spaces(q[i]);
      strncpy(bf[i],q[i],MAX_LINELEN);
      bf[i][MAX_LINELEN]=0;
      substit(bf[i]);
    }
    if(bf[0][0]==0) goto bailout;
    if(internal || (strpbrk(bf[0],"\\[-")==NULL && 
       strpbrk(bf[1],"\\[-")==NULL)) {    /* direct internal translation */
      char *pp;
      if(strlen(bf[1])<strlen(bf[0])) bf[0][strlen(bf[1])]=0;
      for(pp=strpbrk(bf[2],bf[0]);pp!=NULL && *pp!=0 && pp<bf[2]+MAX_LINELEN;
          pp=strpbrk(pp+1,bf[0])) {
          for(i=0;bf[0][i]!=*pp && bf[0][i]!=0;i++);
          if(bf[0][i]!=0) *pp=bf[1][i];
      }         
      bailout: snprintf(p,MAX_LINELEN,"%s",bf[2]);
      return;
    }
    snprintf(fn[0],sizeof(fn[0]),"%s/tr.in",session_prefix);
    snprintf(fn[1],sizeof(fn[1]),"%s/tr.out",session_prefix);
    accessfile(bf[2],"w",fn[0]);
    arglist[0]=tr_prog; arglist[1]=bf[0]; arglist[2]=bf[1];
    arglist[3]=NULL; exportall();
    execredirected(tr_prog,fn[0],fn[1],"/dev/null",arglist);
    read_tmp_file(p,"tr.out");
    strip_trailing_spaces(p);
}

      /* internal common routine for positionof */
void _pos(char *hay, char *stitch, int style, char *out,
        char *(fnd_obj)(char *p, int n, char bf[]),
        unsigned int (objnum)(char *p))
{
    int i,n,t;
    char buf[MAX_LINELEN+1], nbuf[10];
    
    n=objnum(hay);
    for(i=1;i<=n;i++) {
      fnd_obj(hay,i,buf);
      if(strcmp(buf,stitch)!=0) continue;
      t=strlen(out); if(t>MAX_LINELEN-12) return;
      if(t>0) strcat(out,",");
      snprintf(nbuf,sizeof(nbuf),"%d",i); strcat(out,nbuf);
    }
}

      /* return positions of searched for objects */
void calc_pos(char *p)
{
    char buf[2][MAX_LINELEN+1];
    char *p1, *p2;
    int  style;
    
    p1=find_word_start(p); p2=wordchr(p1,"in");
    if(p2==NULL) error2("syntax_error");
    *p2=0;p2=find_word_start(p2+strlen("in"));
    strip_trailing_spaces(p1);
    strcpy(buf[0],p1);*find_word_end(buf[0])=0; style=0;
    if(strcmp(buf[0],"word")==0) style=1;
    else {
      if(strcmp(buf[0],"item")==0) style=2;
      else {
          if(strcmp(buf[0],"line")==0) style=3;
          else {
            if(strcmp(buf[0],"char")==0) style=4;
          }
      }
    }
    if(style>0) p1=find_word_start(find_word_end(p1));
    strcpy(buf[0],p1); strcpy(buf[1],p2);
    substit(buf[0]); substit(buf[1]); *p=0;
    switch(style) {
      case 0: {   /* string */
          char *pp, nbuf[10];
          int i,t;
          for(pp=strstr(buf[1],buf[0]);pp!=NULL;pp=strstr(pp+1,buf[0])) {
            i=pp-buf[1]; t=strlen(p); if(t>MAX_LINELEN-12) return;
            if(t>0) strcat(p,",");
            snprintf(nbuf,sizeof(nbuf),"%d",i); strcat(p,nbuf);
          }
          return;
      }
      case 1: {   /* word */
          _pos(buf[1],buf[0],style,p,fnd_word,wordnum);
          return;
      }
      case 2: {   /* item */
          _pos(buf[1],buf[0],style,p,fnd_item,itemnum);
          return;
      }
      case 3: {   /* line */
          _pos(buf[1],buf[0],style,p,fnd_line,linenum);
          return;
      }
      case 4: {   /* char */
          _pos(buf[1],buf[0],style,p,fnd_char,charnum);
          return;
      }
    }
}

      /* internal routine for calc_replace. */
void _obj_replace(char *orig, char *by, char *in, int num,
              char separator, char *result,
              char *(fnd_obj)(char *p, int n, char bf[]),
              unsigned int (objnum)(char *p),
              char *(objchr)(char *p,char *w))
{
    int i;
    char *p1, *p2;
    
    strcpy(result,in);
    if(num!=0) {
      num=objnum(in); i=evalue(orig);
      if(i==0) error2("bad_index");
      if(i<0) i=num+i+1;
      if(i>num || i<1) return;
      if(separator==0) {  /* char */
          result[i-1]=by[0]; return;
      }
      fnd_obj(result,i,orig); p1=fnd_position;
      if(i<num) {
          fnd_obj(result,i+1,orig); p2=fnd_position;
      }
      else p2=result+strlen(result);
      if(p1==NULL || p2==NULL) internal_error("_obj_replace() error.");
      if(i<num) {
          i=strlen(by); by[i++]=separator;by[i]=0;
      }
      string_modify(result,p1,p2,"%s",by);
    }
    else {
      if(separator==0) {
          if(orig[0]==0 || by[0]==0) return;
          for(p1=strchr(result,orig[0]);p1!=NULL;p1=strchr(p1+1,orig[0]))
            *p1=by[0];
          return;
      }
      if(strlen(orig)+strlen(by)==0) return;
      for(p1=objchr(result,orig);p1!=NULL;p1=objchr(p1+strlen(by)+1,orig)) {
          string_modify(result,p1,p1+strlen(orig),"%s",by);
      }
    }
}

      /* replacement */
void calc_replace(char *p)
{
    int i,style,num,internal;
    char *q[3], *pp;
    char bf[4][MAX_LINELEN+17];
    char fn[3][256], *arglist[8];
    char regexp_char='/';

    style=num=0;
    q[0]=find_word_start(p); internal=0;
    if(strncasecmp(q[0],"internal",strlen("internal"))==0 &&
       isspace(*(q[0]+strlen("internal")))) {
      q[0]=find_word_start(q[0]+strlen("internal"));
      internal=1;
    }
    q[1]=wordchr(q[0],"by"); q[2]=wordchr(q[0],"in");
    if(q[1]==NULL || q[2]==NULL) {
      error2("replace_syntax"); *p=0; return;
    }
    *q[1]=0; *q[2]=0;
    q[1]=find_word_start(q[1]+strlen("by"));
    q[2]=find_word_start(q[2]+strlen("in"));
    strncpy(bf[0],q[0],MAX_LINELEN);bf[0][MAX_LINELEN]=0;
    pp=find_word_end(bf[0]); if(*pp) *(pp++)=0;
    if(strcmp(bf[0],"word")==0) style=1;
    else {
      if(strcmp(bf[0],"item")==0) style=2;
      else {
          if(strcmp(bf[0],"line")==0) style=3;
          else {
            if(strcmp(bf[0],"char")==0) style=4;
          }
      }
    }
    if(style>0) {
      q[0]=find_word_start(find_word_end(q[0]));
      strncpy(bf[0],q[0],MAX_LINELEN);bf[0][MAX_LINELEN]=0;
      pp=find_word_end(bf[0]); if(*pp) *(pp++)=0;
      if(strcmp(bf[0],"number")==0) {
          num=1; q[0]=find_word_start(pp);
      }
    }
    for(i=0;i<3;i++) {
      strip_trailing_spaces(q[i]);
      strncpy(bf[i],q[i],MAX_LINELEN);bf[i][MAX_LINELEN]=0;
      substit(bf[i]);
    }
    if(bf[0][0]==0) {snprintf(p,MAX_LINELEN,"%s",bf[2]); return;}
    switch(style) {
      case 1: { /* word */
          _obj_replace(bf[0],bf[1],bf[2],num,' ',p,
                   fnd_word,wordnum,wordchr);
          return;
      }
      case 2: { /* item */
          _obj_replace(bf[0],bf[1],bf[2],num,',',p,
                   fnd_item,itemnum,itemchr);
          return;
      }
      case 3: { /* line */
          _obj_replace(bf[0],bf[1],bf[2],num,'\n',p,
                   fnd_line,linenum,linechr);
          return;
      }
      case 4: { /* char */
          if(bf[1][0]==0) bf[1][0]=' ';
          if(bf[0][0]==0) return;
          _obj_replace(bf[0],bf[1],bf[2],num,0,p,
                   fnd_char,charnum,charchr);
          return;
      }
      default: break;
    }
    if(internal || (strpbrk(bf[0],"\\[^.*$")==NULL &&
       strpbrk(bf[1],"\\[^.*$")==NULL)) {
      /* No regexp, direct replace */     
      char *pp;
      for(pp=strstr(bf[2],bf[0]);pp<bf[2]+MAX_LINELEN && pp!=NULL;
          pp=strstr(pp+strlen(bf[1]),bf[0])) {
          string_modify(bf[2],pp,pp+strlen(bf[0]),"%s",bf[1]);
      }
      snprintf(p,MAX_LINELEN,"%s",bf[2]);
      return;
    }
    snprintf(fn[0],sizeof(fn[0]),"%s/sed.in",session_prefix);
    snprintf(fn[1],sizeof(fn[1]),"%s/sed.out",session_prefix);
    accessfile(bf[2],"w",fn[0]);
    snprintf(bf[3],sizeof(bf[3]),"s%c%s%c%s%cg",
           regexp_char,bf[0],regexp_char,bf[1],regexp_char);
    arglist[0]=sed_prog; arglist[1]=bf[3]; arglist[2]=NULL;
    execredirected(sed_prog,fn[0],fn[1],"/dev/null",arglist);
    read_tmp_file(p,"sed.out");
    strip_trailing_spaces(p);
}

      /* transforms to lower/upper cases */
void calc_tolower(char *p)
{
    int i,n;
    n=strlen(p);
    for(i=0;i<n;i++) *(p+i)=tolower(*(p+i));
}

void calc_toupper(char *p)
{
    int i,n;
    n=strlen(p);
    for(i=0;i<n;i++) *(p+i)=toupper(*(p+i));
}

      /* strip leading and trailing spaces */
void calc_trim(char *p)
{
    char *s;
    s=find_word_start(p);
    if(s>p) memmove(p,s,MAX_LINELEN-(s-p)+1);
    strip_trailing_spaces(p);
}

      /* output date. Uses Linux 'date' utility. */
void calc_date(char *p)
{
      /* set date is illegal. Clear attempt to attack, no message. */
    if(strstr(p,"-s")!=NULL) {
      *p=0; return;
    }
    call_ssh("date %s >%s/date.out 2>/dev/null",p,session_prefix);
    read_tmp_file(p,"date.out");
}

      /* ls, or dir */
void calc_listfile(char *p)
{
    char *pp;
    
      /* only for trusted modules */
    if(!trusted_module()) {
      error2("not_trusted"); *p=0; return; 
    }
      /* security measures. */
    for(pp=p;*pp;pp++) if(isspace(*pp) || *pp==';') *pp=' ';
    if(strstr(p,parent_dir_string)!=NULL) {
      setenv(error_data_string,p,1);
      error2("illegal_fname"); return;
    }
    wrapexec=1;
    call_sh("ls %s >%s/ls.out 2>%s/ls.err",
          p,session_prefix,session_prefix);
    read_tmp_file(p,"ls.out");
}

      /* instex static: static tex inserts */
void calc_instexst(char *p)
{
    char nbuf[MAX_LINELEN+1], bufc[MAX_LINELEN+1];
    char buf2[1024], buf[MAX_LINELEN+1], urlbuf[MAX_LINELEN+1];
    char *b, *at, *al, *md1, *md2;
    int border, vspace;
    struct stat st,stc;
    char *p1, *p2, *p3, *ppp;

    if(robot_access) {*p=0; return;}
    p1=find_word_start(p); p2=find_word_end(p1);
    p3=p2-4; vspace=0;
    fix_tex_size();
    snprintf(bufc,sizeof(bufc),"%s/%s",module_prefix,m_file.name);
    if(stat(bufc,&stc)!=0) internal_error("calc_instexst(): stat error.");
    if(*p3=='.' && memcmp(p3+1,"png",3)==0) {
      char mbuf[MAX_LINELEN+1];
      if(*p2!=0) *p2++=0;
      p2=find_word_start(p2);
      strcpy(mbuf,p1); substit(mbuf);
      if(strstr(mbuf,parent_dir_string)!=NULL) {
          setenv(error_data_string,mbuf,1);
          error2("illegal_fname"); return;
      }
      snprintf(nbuf,sizeof(nbuf),"%s/%s",module_prefix,mbuf);
    }
    else {
      ppp=getvar(ro_name[ro_module]);
      if(ppp==NULL) internal_error("calc_instexst(): module name vanishes.");
      p2=p1;
      snprintf(nbuf,sizeof(nbuf),"instex/%d/%s/%s_%d.png",
             current_tex_size,ppp,m_file.name,m_file.l);
    }
    snprintf(urlbuf,sizeof(urlbuf),"%s%s?%X",ref_base,nbuf,
           (unsigned short int) stc.st_mtime);
    strcpy(buf,nbuf);
    if((ppp=strrchr(buf,'/'))!=NULL) {
      *ppp=0;mkdirs(buf);
    }
    b=getvar("ins_border");
    at=getvar("ins_attr");
    al=getvar("ins_align");
    if(at==NULL) at="";
    if(al==NULL) al="";al=find_word_start(al);
    if(*al!=0) snprintf(buf2,sizeof(buf2),"align=%s",al); else buf2[0]=0;
    if(b==NULL || *b==0) border=0;
    else border=atoi(b);
    if(border<0) border=0; if(border>100) border=100;
    if(instex_ready(p2,urlbuf)) goto prt;
    if(stat(nbuf,&st)!=0 || st.st_mtime<stc.st_mtime || st.st_size<45) {
      setenv("texgd_style",instex_style,1);
      setenv("texgd_src",p2,1);
      setenv("texgd_outfile",nbuf,1);
      setenv("texgd_tmpdir",tmp_dir,1); exportall();
      wrapexec=0; call_ssh("%s &>%s/instexst.log",tex2png,tmp_dir);
      setenv("instexst_src","",1);
    }
    prt: md1=md2="";
    if(strcasecmp(al,"middle")==0) {
      md1=mathalign_sup1; md2=mathalign_sup2;
      vspace=5;
    }
    if(ins_alt[0]==0) snprintf(ins_alt,sizeof(ins_alt),"%s",p2);
    if(strchr(ins_alt,'"')!=NULL) ins_alt[0]=0;
    snprintf(p,MAX_LINELEN,"%s<IMG SRC=\"%s\" border=%d vspace=%d %s %s alt=\"%s\">%s",
           md1,urlbuf,border,vspace,at,buf2,ins_alt,md2);
    setvar("ins_attr",""); ins_alt[0]=0;
    setvar("ins_url",urlbuf);
}

      /* Read message of the day (site as well as class). */
void calc_readmotd(char *p)
{
    char namebuf[1024];
    char *l, *cl;
    FILE *motdf;
    int re, le, sim, clm;

    secure_exec();
    if(wordchr(p,"site")!=NULL) sim=1; else sim=0;
    if(wordchr(p,"class")!=NULL) clm=1; else clm=0;
    if(*p==0) sim=clm=1;
    *p=0;
    if(sim==0) goto cls;
    l=getvar(ro_name[ro_lang]); if(l==NULL || *l==0) goto cls;
    snprintf(namebuf,sizeof(namebuf),"html/motd.phtml.%s",l);
    motdf=fopen(namebuf,"r");
    if(motdf!=NULL) {
      re=fread(p,1,MAX_LINELEN-10,motdf);
      if(re>=0) *(p+re)=0;
      fclose(motdf);
      if(re>0) strcat(p,"<p>");
    }
    cls:
    if(clm==0) goto end;
    cl=getvar("wims_class"); if(cl==NULL || *cl==0) return;
    snprintf(namebuf,sizeof(namebuf),"%s/classes/%s/.motd",log_dir,cl);
    motdf=fopen(namebuf,"r");
    if(motdf!=NULL) {
      le=strlen(p);
      re=fread(p+le,1,MAX_LINELEN-le-10,motdf);
      if(re>=0) *(p+le+re)=0;
      fclose(motdf); 
      if(re>0) strcat(p,"\n<p>");
    }
    end:
    substit(p);
}

      /* extract non-empty lines or items */
void calc_nonempty(char *p)
{
    int type, i, cnt;
    char *p1, *p2, buf[MAX_LINELEN+1], out[MAX_LINELEN+1];
    p1=find_word_start(p); p2=find_word_end(p1);
    if(*p2) *p2++=0; else {
      *p=0; return;
    }
    type=0;
    if(strcasecmp(p1,"item")==0 || strcasecmp(p1,"items")==0) type=1;
    if(type==0 && (strcasecmp(p1,"line")==0 || strcasecmp(p1,"lines")==0))
      type=2;
    if(type==0 && (strcasecmp(p1,"row")==0 || strcasecmp(p1,"rows")==0))
      type=3;
    if(type==0) error2("syntax_error");
    out[0]=out[1]=0;
    switch(type) {
      case 1: {   /* items */
          cnt=itemnum(p2);
          for(i=1; i<=cnt; i++) {
            fnd_item(p2,i,buf);
            if(*find_word_start(buf)) {
                strcat(out,",");strcat(out,buf);
            }
          }
          break;
      }
      case 2: {   /* lines */
          lines: cnt=linenum(p2);
          for(i=1; i<=cnt; i++) {
            fnd_line(p2,i,buf);
            if(*find_word_start(buf)) {
                strcat(out,"\n");strcat(out,buf);
            }
          }
          break;
      }
      case 3: {   /* rows */
          int t=rows2lines(p2);
          if(t==0) goto lines;
          cnt=linenum(p2);
          for(i=1; i<=cnt; i++) {
            fnd_line(p2,i,buf);
            if(*find_word_start(buf)) {
                strcat(out,";");strcat(out,buf);
            }
          }
          break;
      }
      default: break;
    }
    strcpy(p,out+1);
}

      /* returns a list with unique items */
void calc_listuniq(char *p)
{
    int i,n;
    char lout[MAX_LINELEN+2], ll[MAX_LINELEN+1];
    lout[0]=lout[1]=0;
    n=itemnum(p); for(i=1;i<=n;i++) {
      fnd_item(p,i,ll);
      if(ll[0] && itemchr(lout,ll)==NULL &&
         strlen(lout)+strlen(ll)<MAX_LINELEN-2) {
          strcat(lout,",");strcat(lout,ll);
      }
    }
    strcpy(p,lout+1);
}

      /* returns intersection of 2 lists */
void calc_listintersect(char *p)
{
    char l1[MAX_LINELEN+1],l2[MAX_LINELEN+1],lout[MAX_LINELEN+2];
    char ll[MAX_LINELEN+1];
    char *pp;
    int i,n;
    
    pp=wordchr(p,"and");
    if(pp==NULL) error2("syntax_error");
    *pp=0;strcpy(l1,p); strcpy(l2,pp+strlen("and"));
    lout[0]=lout[1]=0; substit(l1); substit(l2);
    n=itemnum(l1); if(n<=0) {*p=0; return;}
    for(i=1;i<=n;i++) {
      fnd_item(l1,i,ll);
      if(ll[0] && itemchr(l2,ll)!=NULL && itemchr(lout,ll)==NULL &&
         strlen(lout)+strlen(ll)<MAX_LINELEN-2) {
          strcat(lout,",");strcat(lout,ll);
      }
    }
    strcpy(p,lout+1);
}

      /* returns union of 2 lists */
void calc_listunion(char *p)
{
    char l1[MAX_LINELEN+1],l2[MAX_LINELEN+1],lout[MAX_LINELEN+2];
    char ll[MAX_LINELEN+1];
    char *pp;
    int i,n;
    
    pp=wordchr(p,"and");
    if(pp==NULL) error2("syntax_error");
    *pp=0;strcpy(l1,p); strcpy(l2,pp+strlen("and"));
    lout[0]=lout[1]=0; substit(l1); substit(l2);
    n=itemnum(l1); for(i=1;i<=n;i++) {
      fnd_item(l1,i,ll);
      if(ll[0] && itemchr(lout,ll)==NULL &&
         strlen(lout)+strlen(ll)<MAX_LINELEN-2) {
          strcat(lout,",");strcat(lout,ll);
      }
    }
    n=itemnum(l2); for(i=1;i<=n;i++) {
      fnd_item(l2,i,ll);
      if(ll[0] && itemchr(lout,ll)==NULL &&
         strlen(lout)+strlen(ll)<MAX_LINELEN-2) {
          strcat(lout,",");strcat(lout,ll);
      }
    }
    strcpy(p,lout+1);
}

      /* returns items of list2 not in list1 */
void calc_listcomplement(char *p)
{
    char l1[MAX_LINELEN+1],l2[MAX_LINELEN+1],lout[MAX_LINELEN+2];
    char ll[MAX_LINELEN+1];
    char *pp;
    int i,n;
    
    pp=wordchr(p,"in");
    if(pp==NULL) error2("syntax_error");
    *pp=0;strcpy(l1,p); strcpy(l2,pp+strlen("in"));
    lout[0]=lout[1]=0; substit(l1); substit(l2);
    n=itemnum(l2); if(n<=0) {*p=0; return;}
    for(i=1;i<=n;i++) {
      fnd_item(l2,i,ll);
      if(ll[0] && itemchr(l1,ll)==NULL && itemchr(lout,ll)==NULL &&
         strlen(lout)+strlen(ll)<MAX_LINELEN-2) {
          strcat(lout,",");strcat(lout,ll);
      }
    }
    strcpy(p,lout+1);
}

      /* Consult a dictionary to translate a string */
/*
void calc_dictionary(char *p)
{
    char *pp;
    char name[MAX_LINELEN+1], str[MAX_LINELEN+1];
    int t;
    pp=wordchr(p,"for");
    if(pp==NULL || pp<=p) error2("syntax_error");
    *(find_word_end(pp-1))=0; 
    strcpy(name,p); substit(name);
    pp=find_word_start(pp+strlen("for")); t=0;
    if(memcmp(pp,"word",strlen("word"))==0 && 
       (isspace(*(pp+strlen("word"))) || 
      (*(pp+strlen("word"))=='s' && isspace(*(pp+strlen("words")))))) {
      pp=find_word_start(pp+strlen("words")); t=1;
    }
    if(memcmp(pp,"line",strlen("line"))==0 && 
       (isspace(*(pp+strlen("line"))) || 
      (*(pp+strlen("line"))=='s' && isspace(*(pp+strlen("lines")))))) {
      pp=find_word_start(pp+strlen("lines")); t=1;
    }
    if(memcmp(pp,"list",strlen("list"))==0 && isspace(*(pp+strlen("list")))) {
      pp=find_word_start(pp+strlen("list"));
    }
    if(memcmp(pp,"items",strlen("items"))==0 && isspace(*(pp+strlen("items")))) {
      pp=find_word_start(pp+strlen("items"));
    }
    if(memcmp(pp,"item",strlen("item"))==0 && isspace(*(pp+strlen("item")))) {
      pp=find_word_start(pp+strlen("item"));
    }
    strcpy(str,pp); substit(str);
    switch(t) {
      case 1: {
          calc_words2items(str); break;
      }
      case 2: {
          calc_lines2items(str); break;
      }
      default: break;
    }
}
*/

void calc_module(char *p)
{
    char *p1,*p2, *pp, *pt, *e, *ind_buf, buf[1024];
    FILE *indf;
    int len,read_len,n;
    
    p1=find_word_start(p); p2=find_word_end(p1);
    if(*p2!=0) *p2++=0;
    p2=find_word_start(p2); *find_word_end(p2)=0;
    if(*p1==0) {
      empty: *p=0; return;
    }
    if(*p2==0) {
      currentmod:
      snprintf(buf,sizeof(buf),"module_%s",p1);
      p1=getvar(buf); if(p1==NULL) p1="";
      snprintf(p,MAX_LINELEN,"%s",p1); return;
    }
    snprintf(buf,sizeof(buf),"%s/%s/INDEX",module_dir,p2);
    indf=fopen(buf,"r"); if(indf==NULL) {
      snprintf(buf,sizeof(buf),"%s/%s/index",module_dir,p2);
      indf=fopen(buf,"r");
    }
    if(indf==NULL && p2[strlen(p2)-3]!='.') {
      snprintf(buf,sizeof(buf),"%s/%s.%s/INDEX",module_dir,p2,lang);
      indf=fopen(buf,"r"); if(indf==NULL) {
          snprintf(buf,sizeof(buf),"%s/%s.%s/index",module_dir,p2,lang);
          indf=fopen(buf,"r");
      }
      if(indf==NULL) {
          int i;
          for(i=0;i<available_lang_no;i++) {
            snprintf(buf,sizeof(buf),"%s/%s.%s/INDEX",module_dir,p2,
                   available_lang[i]);
            indf=fopen(buf,"r"); if(indf!=NULL) break;
            snprintf(buf,sizeof(buf),"%s/%s.%s/index",module_dir,p2,
                   available_lang[i]);
            indf=fopen(buf,"r"); if(indf!=NULL) break;
          }
      }
    }
    if(indf==NULL) goto currentmod;
    fseek(indf,0,SEEK_END);
    len=ftell(indf); if(len<=0) return;
    ind_buf=xmalloc(len+2);
    fseek(indf,0,SEEK_SET);
    read_len=fread(ind_buf,1,len,indf);
    fclose(indf);
    if(read_len>len || read_len<=0) goto empty;
    *(ind_buf+read_len)=0;
    e=ind_buf-1; n=strlen(p1);
    while(e<ind_buf+read_len && e!=NULL) {
      pp=e+1; e=strchr(pp,'\n');
      if(e!=NULL) *e=0;   /* make string out of a line */
      pp=find_word_start(pp);
      if(strncmp(pp,p1,n)==0) {
          pt=find_word_start(pp+n);
          if(*pt!='=') continue;
          pt=find_word_start(pt+1);
          snprintf(p,MAX_LINELEN,"%s",pt);
          free(ind_buf); return;
      }
    }
    free(ind_buf); goto empty;
}

      /* strip enclosing parentheses */
void calc_declosing(char *p)
{
    strip_enclosing_par(p);
}

      /* remove html tag, very rudimentary. */
void calc_detag(char *p)
{
    char *p1, *p2;
    for(p1=strchr(p,'<'); p1!=NULL; p1=strchr(p1,'<')) {
      p2=strchr(p1,'>'); if(p2==NULL) p1++;
      else strcpy(p1,p2+1);
    }
}

      /* prepare a string to be inserted into a form input 
       * or textarea as is */
void calc_reinput(char *p)
{
    char *p1;
    for(p1=strchr(p,'&'); p1!=NULL; p1=strchr(p1,'&')) {
      p1++; string_modify(p,p1,p1,"amp;");
    }
    for(p1=strchr(p,'<'); p1!=NULL; p1=strchr(++p1,'<'))
      string_modify(p,p1,p1+1,"&lt;");
}

      /* get a definition from a file. Trusted modules only. */
void calc_defof(char *p)
{
    char *p1, *pp, buf[MAX_LINELEN+1];
    char nbuf[MAX_LINELEN+1], fbuf[MAX_LINELEN+1];
    
    secure_exec();
    p1=wordchr(p,"in"); if(p1==NULL) error2("syntax_error");
    *p1=0; p1=find_word_start(p1+strlen("in"));
    strcpy(nbuf,p);
    snprintf(fbuf,sizeof(fbuf),"%s/%s",module_prefix,p1);
    substit(nbuf); substit(fbuf);
    pp=find_word_start(nbuf); p1=find_word_start(fbuf);
    *find_word_end(p1)=0; if(strstr(p1,parent_dir_string)!=NULL) {
      setenv(error_data_string,p1,1);
      error2("illegal_fname"); return;
    }
    strip_trailing_spaces(pp);
    getdef(p1,pp,buf); snprintf(p,MAX_LINELEN,"%s",buf);
}

      /* check host */
void calc_checkhost(char *p)
{
    char buf[MAX_LINELEN+1];
    if(robot_access || !trusted_module()) strcpy(p,"0");
    else {
      strcpy(buf,p);snprintf(p,MAX_LINELEN,"%d",checkhost(buf));
    }
}

#define MAX_COLUMNS 256

      /* A rudimentary database facility */
void calc_select(char *p)
{
    char *p1, *p2, *p3, *bufp, c, sep;
    char buf1[MAX_LINELEN+1], buf2[MAX_LINELEN+1];
    char buf[MAX_LINELEN+1];
    int i,j,lmax;
    p2=wordchr(p,"where"); if(p2==NULL) error2("syntax_error");
    *p2=0; p2+=strlen("where");
    p2=find_word_start(p2); strip_trailing_spaces(p2);
    p1=find_word_start(p) ; strip_trailing_spaces(p1);
    if(*p1==0 || *p2==0) error2("syntax_error");
    strcpy(buf1,p1); strcpy(buf2,p2); sep='\n';
      /* buf1: data; buf2: condition. */
    substit(buf1); 
    if(strstr(buf2,"column")!=NULL) {     /* matrix */
      lmax=0; if(rows2lines(buf1)) sep=';';
      for(p1=strstr(buf2,"column"); p1; p1=strstr(p1+1,"column")) {
          if(p1>buf2 && isalnum(*(p1-1))) continue;
          p2=find_word_start(p1+strlen("column"));
          for(p3=p2; isdigit(*p3); p3++);
          if(p3==p2) continue;
          c=*p3; *p3=0; i=atoi(p2); *p3=c;
          if(i<=0 || i>MAX_COLUMNS) continue;
          if(i>lmax) lmax=i;
          string_modify(buf2,p1,p3,"$(wims_select_col_%d)",i);
      }
      buf[0]=0; bufp=buf;
      for(p1=buf1; *p1; p1=p2) {
          char ibuf[MAX_LINELEN+1], nbuf[128];
          p2=strchr(p1,'\n');
          if(p2==NULL) p2=p1+strlen(p1); else *p2++=0;
          if(*find_word_start(p1)==0) continue;
          for(j=1;j<=lmax;j++) {
            snprintf(nbuf,sizeof(nbuf),"wims_select_col_%d",j);
            fnd_item(p1,j,ibuf); setvar(nbuf,ibuf);
          }
          strcpy(ibuf,buf2); if(compare(ibuf,0)) {
            snprintf(bufp,MAX_LINELEN-(bufp-buf),"%s%c",p1,sep);
            bufp+=strlen(bufp);
          }
      }
    }
    else {  /* datafile */
      error2("syntax_error");
      
    }
    if(buf[0]!=0) buf[strlen(buf)-1]=0;
    snprintf(p,MAX_LINELEN,"%s",buf);
}

      /* Extract a column from a matrix */
void calc_columnof(char *p)
{
    char *p1, *p2, *bufp, sep;
    char buf1[MAX_LINELEN+1], buf2[MAX_LINELEN+1];
    char buf[MAX_LINELEN+1];
    p2=wordchr(p,"of"); if(p2==NULL) error2("syntax_error");
    *p2=0; p2+=strlen("of");
    p2=find_word_start(p2); strip_trailing_spaces(p2);
    p1=find_word_start(p) ; strip_trailing_spaces(p1);
    if(*p1==0) error2("syntax_error");
    if(*p2==0) {*p=0; return;}
    strcpy(buf1,p1); strcpy(buf2,p2); sep='\n';
      /* buf1: number(s); buf2: matrix. */
    substit(buf1); substit(buf2); 
    if(rows2lines(buf2)) sep=';';
    if(strchr(buf1,',')==NULL && wordchr(buf1,"to")==NULL) sep=',';
    buf[0]=0; bufp=buf;
    for(p1=buf2; *p1; p1=p2) {
      char ibuf[MAX_LINELEN+1];
      p2=strchr(p1,'\n');
      if(p2==NULL) p2=p1+strlen(p1); else *p2++=0;
      snprintf(ibuf,sizeof(ibuf),"%s of %s",buf1,p1);
      calc_itemof(ibuf);
      snprintf(bufp,MAX_LINELEN-(bufp-buf),"%s%c",ibuf,sep);
      bufp+=strlen(bufp);
    }
    if(buf[0]!=0) buf[strlen(buf)-1]=0;
    snprintf(p,MAX_LINELEN,"%s",buf);
}

      /* find roots of a function or equation in a given zone */
void calc_solve(char *p)
{
    char *pp, *fp, *forp;
    char buf[MAX_LINELEN+1], vbuf[MAX_LINELEN+1];
    double v, dd, start, stop, step, old, v1, v2, v3, d1, d2, d3;
    int i, pos;
    
    forp=wordchr(p,"for");
    if(forp==NULL) { syntax: error2("syntax_error"); *p=0; return; }
    *forp=0; forp+=strlen("for");
    fp=find_word_start(p); strip_trailing_spaces(fp);
    if(*fp==0) goto syntax;
    i=cutfor(forp); if(i<0 || forstruct.type==for_in) goto syntax;
    if(i>0) {*p=0; return;}
    start=forstruct.from; stop=forstruct.to; forp=forstruct.var;
    snprintf(buf,sizeof(buf),"%s",fp); substitute(buf);
    *p=0; pp=strchr(buf,'='); if(pp!=NULL) {
      if(strlen(buf)>=MAX_LINELEN-16) return;
      strcat(buf,")");
      string_modify(buf,pp,pp+1,"-(");
    }
    i=0; for(fp=varchr(buf,forp); fp!=NULL; fp=varchr(fp,forp)) {
      string_modify(buf,fp,fp+strlen(forp),EV_X); fp+=strlen(EV_X); i++;
    }
    if(i==0 || start==stop) return;
    evalue_compile(buf); pos=eval_getpos(EV_X);
    if(start>stop) {dd=start; start=stop; stop=dd;}
    step=(stop-start)/100; if(step==0) return;
    pp=p; old=0;
    for(v=start; v<=stop; v+=step, old=dd) {
      eval_setval(pos,v); 
      set_evalue_error(0); set_evalue_pointer(buf); dd=_evalue(10);
      if(v==start) continue;
      if(!finite(old) || !finite(dd) || (old>0 && dd>0) || (old<0 && dd<0))
        continue;
      v1=v-step; v2=v; d1=old; d2=dd;
      for(i=0;i<30;i++) {
          v3=(v1+v2)/2; eval_setval(pos,v3);
          set_evalue_error(0); set_evalue_pointer(buf); d3=_evalue(10);
          if(!finite(d3)) goto next;
          if((d1>0 && d3>0) || (d1<0 && d3<0)) {d1=d3; v1=v3;}
          else {d2=d3; v2=v3;}
      }
      float2str(v3,vbuf); if(pp-p+strlen(vbuf)<MAX_LINELEN-1) {
          if(pp>p) *pp++=','; strcpy(pp,vbuf);
          pp+=strlen(pp);
      }
      else break;
      next: ;
    }
}

      /* cut a function into values */
void calc_values(char *p)
{
    char *pp, *fp, *forp;
    char vbuf[MAX_LINELEN+1], buf[MAX_LINELEN+1], fbuf[MAX_LINELEN+1];
    char *f[64];
    double v, dd, start, stop, step;
    int i, k, fcnt, pos;
    
    forp=wordchr(p,"for");
    if(forp==NULL) { syntax: error2("syntax_error"); *p=0; return; }
    *forp=0; forp+=strlen("for"); forp=find_word_start(forp);
    fp=find_word_start(p); strip_trailing_spaces(fp);
    if(*fp==0) goto syntax;
    i=cutfor(forp); if(i<0) goto syntax; if(i>0) {*p=0; return;}
    start=forstruct.from; stop=forstruct.to; step=forstruct.step;
    forp=forstruct.var;
    snprintf(buf,sizeof(buf),"%s",fp); substitute(buf);
    for(fp=varchr(buf,forp); fp!=NULL; fp=varchr(fp,forp)) {
      string_modify(buf,fp,fp+strlen(forp),EV_X); fp+=strlen(EV_X);
    }
    fcnt=itemnum(buf); if(fcnt>64) fcnt=64; pp=fbuf;
    for(k=0; k<fcnt; k++) {
      fnd_item(buf,k+1,vbuf); evalue_compile(vbuf);
      if(pp-fbuf+strlen(vbuf)<MAX_LINELEN-1) {
          f[k]=pp; strcpy(pp,vbuf); pp+=strlen(pp)+1;
      }
      else f[k]="";
    }
    pos=eval_getpos(EV_X);
    if(step==0) step=1; if(step<0) step=-step;
    if(stop<start) {dd=start; start=stop; stop=dd;}
    *p=0; pp=p;
    for(i=0,v=start; i<MAX_VALUE_LIST && v<=stop; v+=step, i++) {
      if(forstruct.type==for_from) eval_setval(pos,v);
      else eval_setval(pos,forstruct.list[i]);     
      for(k=0; k<fcnt; k++) {
          set_evalue_error(0); set_evalue_pointer(f[k]); dd=_evalue(10);
          float2str(dd,vbuf); if(pp-p+strlen(vbuf)<MAX_LINELEN-1) {
            if(pp>p) *pp++=','; strcpy(pp,vbuf);
            pp+=strlen(pp);
          }
      }
    }
}

      /* level curve data */
void calc_leveldata(char *p)
{
    leveldata ld;
    char *sizep, *rangep, *fp, *levelp, *stepp;
    char *pp,*p2,fbuf[MAX_LINELEN+1],buf[MAX_LINELEN+1];
    double d[4];
    int i;
    
    sizep=wordchr(p,"size");
    rangep=wordchr(p,"ranges");
    fp=wordchr(p,"function");
    levelp=wordchr(p,"levels");
    stepp=wordchr(p,"step");
    if(sizep==NULL || rangep==NULL || fp==NULL) {
      syntax: error2("syntax_error"); *p=0; return;
    }
    *sizep=0; sizep+=strlen("size");
    *rangep=0; rangep+=strlen("ranges");
    *fp=0; fp+=strlen("function");
    if(levelp!=NULL) {*levelp=0; levelp+=strlen("levels");}
    else levelp="0";
    if(stepp!=NULL) {*stepp=0; stepp+=strlen("step");}
    else stepp="0";
    snprintf(fbuf,sizeof(fbuf),"%s",fp); substitute(fbuf);
    ld.fn=fbuf;
    ld.xname="x"; ld.yname="y"; ld.grain=evalue(stepp);
    snprintf(buf,sizeof(buf),"%s",sizep); substitute(buf);
    for(i=0,pp=buf;i<2;i++,pp=p2) {
      if(*pp==0) goto syntax;
      p2=find_item_end(pp); if(*p2) *p2++=0;
      d[i]=evalue(pp);
    }
    ld.xsize=d[0]; ld.ysize=d[1];
    snprintf(buf,sizeof(buf),"%s",rangep); substitute(buf);
    for(i=0,pp=buf;i<4;i++,pp=p2) {
      if(*pp==0) goto syntax;
      p2=find_item_end(pp); if(*p2) *p2++=0;
      d[i]=evalue(pp);
    }
    ld.xrange[0]=d[0]; ld.xrange[1]=d[1]; ld.yrange[0]=d[3]; ld.yrange[1]=d[2];
    snprintf(buf,sizeof(buf),"%s",levelp); substitute(buf);
    ld.levelcnt=itemnum(buf); if(ld.levelcnt>LEVEL_LIM) ld.levelcnt=LEVEL_LIM;
    for(i=0,pp=buf;i<ld.levelcnt;i++,pp=p2) {
      if(*pp==0) goto syntax;
      p2=find_item_end(pp); if(*p2) *p2++=0;
      ld.levels[i]=evalue(pp);
    }
    levelcurve(&ld);
    for(i=0, pp=p; i<ld.datacnt && pp<p+MAX_LINELEN-16; i++) {
      float2str(ld.xdata[i],buf);
      if(pp-p+strlen(buf)<MAX_LINELEN-1) {
          if(pp>p) *pp++=';'; strcpy(pp,buf); pp+=strlen(pp);
      }
      float2str(ld.ydata[i],buf);
      if(pp-p+strlen(buf)<MAX_LINELEN-1) {
          if(pp>p) *pp++=','; strcpy(pp,buf); pp+=strlen(pp);
      }
    }
}

01695 typedef struct {
    char *name;
    int tag;
    void (*routine) (char *p);
} MYFUNCTION;

      /* tag!=0 if we don't want automatic substit(). */
MYFUNCTION calc_routine[]={
      {"TeXmath", 0,    texmath},
      {"append",  1,    calc_append},
      {"call",          0,    calc_exec},
      {"char",          1,    calc_charof},
      {"charcnt", 0,      calc_lengthof},
      {"charcount",     0,      calc_lengthof},
      {"charno",  0,      calc_lengthof},
      {"charnum", 0,      calc_lengthof},
      {"chars",         1,    calc_charof},
      {"checkhost",     0,    calc_checkhost},
      {"column",  1,    calc_columnof},
      {"columns", 1,    calc_columnof},
      {"date",          0,    calc_date},
      {"deaccent",      0,    deaccent},
      {"debug",         0,    calc_debug},
      {"declosing",     0,    calc_declosing},
      {"definitionof",  1,    calc_defof},
      {"defof",         1,    calc_defof},
      {"detag",         0,    calc_detag},
/*      {"dictionary",  1,    calc_dictionary}, */
      {"dir",           0,    calc_listfile},
      {"encyclo", 0,    pedia},
      {"encyclopedia",  0,    pedia},
      {"eval",          0,    calc_evalue},
      {"evalsubst",     1,    calc_evalsubst},
      {"evalsubstit",   1,    calc_evalsubst},
      {"evalsubstitute",1,    calc_evalsubst},
      {"evalue",  0,    calc_evalue},
      {"evaluesubst",   1,    calc_evalsubst},
      {"evaluesubstit", 1,    calc_evalsubst},
      {"evaluesubstitute",1,  calc_evalsubst},
      {"examdep", 0,    calc_examdep},
      {"examscore",     0,    calc_examscore},
      {"exec",          0,    calc_exec},
      {"execute", 0,    calc_exec},
      {"filelist",      0,    calc_listfile},
      {"getdef",  1,    calc_defof},
      {"getscore",      0,    calc_getscore},
      {"getscoremean",  0,    calc_getscoremean},
      {"getscorepercent",0,   calc_getscorepercent},
      {"getscoreremain",0,    calc_getscoreremain},
      {"getscorerequire",0,   calc_getscorerequire},
      {"getscorestatus",0,    calc_getscorestatus},
      {"getscoreweight",0,    calc_getscoreweight},
      {"htmlmath",      0,    htmlmath},
      {"httpquery",     0,    tohttpquery},
      {"instexst",      1,    calc_instexst},
      {"instexstatic",  1,    calc_instexst},
      {"item",          1,    calc_itemof},
      {"itemcnt", 0,      calc_itemnum},
      {"itemcount",     0,      calc_itemnum},
      {"itemno",  0,      calc_itemnum},
      {"itemnum", 0,      calc_itemnum},
      {"items",         1,    calc_itemof},
      {"items2lines",   0,      items2lines},
      {"items2words",   0,      items2words},
      {"itemstolines",  0,      items2lines},
      {"itemstowords",  0,      items2words},
      {"lengthof",      0,      calc_lengthof},
      {"leveldata",     1,    calc_leveldata},
      {"levelpoints",   1,    calc_leveldata},
      {"line",          1,    calc_lineof},
      {"linecnt", 0,      calc_linenum},
      {"linecount",     0,      calc_linenum},
      {"lineno",  0,      calc_linenum},
      {"linenum", 0,      calc_linenum},
      {"lines",         1,    calc_lineof},
      {"lines2items",   0,      lines2items},
      {"lines2list",    0,      lines2items},
      {"lines2words",   0,      lines2words},
      {"linestoitems",  0,      lines2items},
      {"linestolist",   0,      lines2items},
      {"linestowords",  0,      lines2words},
      {"list2lines",    0,      items2lines},
      {"list2words",    0,      items2words},
      {"listcomplement",1,    calc_listcomplement},
      {"listfile",      0,    calc_listfile},
      {"listfiles",     0,    calc_listfile},
      {"listintersect", 1,    calc_listintersect},
      {"listintersection",1,  calc_listintersect},
      {"listtolines",   0,      items2lines},
      {"listtowords",   0,      items2words},
      {"listunion",     1,    calc_listunion},
      {"listuniq",      0,    calc_listuniq},
      {"listunique",    0,    calc_listuniq},
      {"listvar", 0,    mathvarlist},
      {"lower",         0,    calc_tolower},
      {"lowercase",     0,    calc_tolower},
      {"ls",            0,    calc_listfile},
      {"mathsubst",     1,    calc_mathsubst},
      {"mathsubstit",   1,    calc_mathsubst},
      {"mathsubstitute",1,    calc_mathsubst},
      {"mexec",         0,    calc_mexec},
      {"module",  0,    calc_module},
      {"non_empty",     0,    calc_nonempty},
      {"nonempty",      0,    calc_nonempty},
      {"nospace", 0,    nospace},
      {"nosubst", 1,    calc_subst},
      {"nosubstit",     1,    calc_subst},
      {"nosubstitute",  1,    calc_subst},
      {"pedia",         0,    pedia},
      {"position",      1,    calc_pos},
      {"positionof",    1,    calc_pos},
      {"positions",     1,    calc_pos},
      {"randchar",      0,    calc_randchar},
      {"randdouble",    0,    calc_randdouble},
      {"randfile",      0,    calc_randfile},
      {"randfloat",     0,    calc_randdouble},
      {"randint",       0,    calc_randint},
      {"randitem",      0,    calc_randitem},
      {"randline",      0,    calc_randline},
      {"random",  0,    calc_randdouble},
      {"randperm",      0,    calc_randperm},
      {"randpermute",   0,    calc_randperm},
      {"randreal",      0,    calc_randdouble},
      {"randrecord",    0,    calc_randfile},
      {"randrow", 0,    calc_randrow},
      {"randword",      0,    calc_randword},
      {"rawmath", 0,    rawmath},
      {"rawmatrix",     0,    rawmatrix},
      {"readmotd",      0,    calc_readmotd},
      {"record",  1,    calc_recordof},
      {"recordcnt",     0,    calc_recordnum},
      {"recordcount",   0,    calc_recordnum},
      {"recordno",      0,    calc_recordnum},
      {"recordnum",     0,    calc_recordnum},
      {"records", 1,    calc_recordof},
      {"reinput", 0,    calc_reinput},
      {"replace", 1,    calc_replace},
      {"rootof",  1,    calc_solve},
      {"row",           1,    calc_rowof},
      {"rowcnt",  0,      calc_rownum},
      {"rowcount",      0,      calc_rownum},
      {"rowno",         0,      calc_rownum},
      {"rownum",  0,      calc_rownum},
      {"rows",          1,    calc_rowof},
      {"run",           0,    calc_exec},
      {"select",  1,    calc_select},
      {"sh",            0,    calc_sh},
      {"shuffle", 0,    calc_randperm},
      {"singlespace",   0,    singlespace},
      {"solve",         1,    calc_solve},
      {"sort",          1,    calc_sort},
      {"sql",           0,    calc_sql},
      {"staticinstex",  1,    calc_instexst},
      {"stinstex",      1,    calc_instexst},
      {"subst",         0,    calc_subst},
      {"substit", 0,    calc_subst},
      {"substitute",    0,    calc_subst},
      {"system",  0,    calc_sh},
      {"texmath", 0,    texmath},
      {"text",          1,    text},
      {"tolower", 0,    calc_tolower},
      {"toupper", 0,    calc_toupper},
      {"translate",     1,    calc_translate},
      {"trim",          0,    calc_trim},
      {"upper",         0,    calc_toupper},
      {"uppercase",     0,    calc_toupper},
      {"values",  1,    calc_values},
      {"varlist", 0,    mathvarlist},
      {"word",          1,      calc_wordof},
      {"wordcnt", 0,      calc_wordnum},
      {"wordcount",     0,      calc_wordnum},
      {"wordno",  0,      calc_wordnum},
      {"wordnum", 0,      calc_wordnum},
      {"words",         1,      calc_wordof},
      {"words2items",   0,      words2items},
      {"words2lines",   0,      words2lines},
      {"words2list",    0,      words2items},
      {"wordstoitems",  0,      words2items},
      {"wordstolines",  0,      words2lines},
      {"wordstolist",   0,      words2items}
};
#define CALC_FN_NO (sizeof(calc_routine)/sizeof(calc_routine[0]))


Generated by  Doxygen 1.6.0   Back to index