Subversion Repositories wimsdev

Rev

Rev 13101 | 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. #include "wims.h"
  18.  
  19. /* internal variable */
  20. int rawmath_easy=0;
  21.  
  22. struct hmname hmname[]={
  23.       {"CC",  "", ""},
  24.       {"Delta", "", ""},
  25.       {"Gamma","", ""},
  26.       {"Inf",  "$(m_infty)","\\infty"},
  27.       {"Lambda","", ""},
  28.       {"NN",  "", ""},
  29.       {"Omega",  "", ""},
  30.       {"Phi",  "", ""},
  31.       {"Pi",  "", ""},
  32.       {"Psi",  "", ""},
  33.       {"QQ",  "",""},
  34.       {"RR",  "",""},
  35.       {"Sigma",  "",""},
  36.       {"Xi",  "",""},
  37.       {"ZZ",  "",""},
  38.       {"alpha",  "", ""},
  39.       {"beta",  "", ""},
  40.       {"cap",  "",""},
  41.       {"chi",  "",""},
  42.       {"cup",  "",""},
  43.       {"delta",  "",""},
  44.       {"div",  "÷", "÷"},
  45.       {"divide","÷","÷"},
  46.       {"epsilon","$(m_varepsilon)","\\varepsilon"},
  47.       {"eta",  "",""},
  48.       {"exist",  "$(m_exists)","\\exists"},
  49.       {"exists","", ""},
  50.       {"forall","",""},
  51.       {"gamma",  "",""},
  52.       {"in",  "",""},
  53.       {"inf",  "$(m_infty)","\\infty"},
  54.       {"infinity","$(m_infty)","\\infty"},
  55.       {"infty",  "",""},
  56.       {"intersect","$(m_cap)", "\\cap"},
  57.       {"intersection","$(m_cap)", "\\cap"},
  58.       {"iota",  "",""},
  59.       {"kappa",  "",""},
  60.       {"lambda","", ""},
  61.       {"mu",  "",""},
  62.       {"nabla",  "",""},
  63.       {"neq",  "",""},
  64.       {"nu",  "",""},
  65.       {"omega",  "",""},
  66.       {"pi",  "",""},
  67.       {"pm",  "",""},
  68.       {"psi",  "",""},
  69.       {"rho",  "",""},
  70.       {"sigma",  "",""},
  71.       {"subset","", ""},
  72.       {"subseteq","",""},
  73.       {"tau",  "",""},
  74.       {"theta",  "",""},
  75.       {"times",  "×", "\\times"},
  76.       {"union",  "$(m_cup)", "\\cup"},
  77.       {"varepsilon","",""},
  78.       {"varphi","", ""},
  79.       {"x",  "","x"},
  80.       {"xi","",""},
  81.       {"y",  "","y"},
  82.       {"z",  "","z"},
  83.       {"zeta","",""},
  84. };
  85. int hmname_no=(sizeof(hmname)/sizeof(hmname[0]));
  86.  
  87. enum {RM_UNKNOWN, RM_FN, RM_VAR, RM_PREFIX};
  88.  
  89. struct mathname mathname[]={
  90.       {"Arc",     RM_PREFIX, "arc"},
  91.       {"Arg",     RM_PREFIX, "arg"},
  92.       {"Ci",      RM_FN,        ""},
  93.       {"E",       RM_VAR,       ""},
  94.       {"Euler",   RM_VAR,       ""},
  95.       {"I",       RM_VAR,       ""},
  96.       {"Int",     RM_FN,        ""},
  97.       {"PI",      RM_VAR,       ""},
  98.       {"Pi",      RM_VAR,       ""},
  99.       {"Prod",    RM_FN,        ""},
  100.       {"Si",      RM_FN,        ""},
  101.       {"Sum",     RM_FN,        ""},
  102.       {"arc",     RM_PREFIX,    ""},
  103.       {"arg",     RM_PREFIX,    ""},
  104.       {"binomial",RM_FN,        ""},
  105.       {"diff",    RM_FN,        ""},
  106.       {"e",       RM_VAR,       ""},
  107.       {"erf",     RM_FN,        ""},
  108.       {"euler",   RM_VAR,       ""},
  109.       {"i",       RM_VAR,       ""},
  110.       {"infinity",RM_VAR,       ""},
  111.       {"int",     RM_FN,        ""},
  112.       {"integrate",RM_FN,       ""},
  113.       {"neq",     RM_VAR,       ""},
  114.       {"pi",      RM_VAR,       ""},
  115.       {"prod",    RM_FN,        ""},
  116.       {"product", RM_FN,        ""},
  117.       {"psi",     RM_FN,        ""},
  118.       {"sum",     RM_FN,        ""},
  119.       {"theta",   RM_FN,        ""},
  120.       {"x",       RM_VAR,       ""},
  121.       {"y",       RM_VAR,       ""},
  122.       {"z",       RM_VAR,       ""},
  123.       {"zeta",    RM_FN,        ""},
  124. };
  125. int mathname_no=(sizeof(mathname)/sizeof(mathname[0]));
  126. char rm_vbuf[MAX_LINELEN+1],rm_fbuf[MAX_LINELEN+1];
  127. char *rm_uservar[MAX_LINELEN+1],*rm_userfn[MAX_LINELEN+1];
  128. int  rm_uservars,rm_userfns;
  129.  
  130. /* add user-defined variables and function names,
  131.  * internal, only called by rawmath().
  132.  */
  133. void getuservar(void)
  134. {
  135.   char *p1, *p2, *p;
  136.   rm_uservars=rm_userfns=0;
  137.   p=getvar("wims_rawmath_variables");
  138.   if(p!=NULL && *p!=0) {
  139.     ovlstrcpy(rm_vbuf,p);
  140.     for(p=rm_vbuf;*p;p++) if(*p==',') *p=' ';
  141.     for(p1=find_word_start(rm_vbuf);*p1;p1=find_word_start(p2)) {
  142.       rm_uservar[rm_uservars++]=p1;
  143.       p2=find_word_end(p1);
  144.       if(*p2!=0) *(p2++)=0;
  145.     }
  146.   }
  147.   p=getvar("wims_rawmath_functions");
  148.   if(p!=NULL && *p!=0) {
  149.     ovlstrcpy(rm_fbuf,p);
  150.     for(p=rm_fbuf;*p;p++) if(*p==',') *p=' ';
  151.     for(p1=find_word_start(rm_fbuf);*p1;p1=find_word_start(p2)) {
  152.       rm_userfn[rm_userfns++]=p1;
  153.       p2=find_word_end(p1);
  154.       if(*p2!=0) *(p2++)=0;
  155.     }
  156.   }
  157. }
  158.  
  159. /* Try to split a word into recognizable variables */
  160. char *mathname_split(char *p)
  161. {
  162.   int c,i,j,type;
  163.  
  164.   c=0;
  165.   beg: for(i=get_evalcnt()-1;
  166.   i>=0 && strncmp(p,get_evalname(i),strlen(get_evalname(i)))!=0;
  167.   i--);
  168.   if(i>=0 && get_evaltype(i)>0) {
  169.     type=RM_FN;
  170.     j=strlen(get_evalname(i));
  171.     gotit:
  172.     if(!*(p+j)) return p;
  173.     if(myisdigit(*(p+j)) && type!=RM_FN) return NULL;
  174.     if(!c) {string_modify(p,p+j,p+j," "); p+=j+1;}
  175.     else p+=j;
  176.     c++; goto beg;
  177.   }
  178.   for(i=mathname_no-1;
  179.       i>=0 &&
  180.       (strncmp(p,mathname[i].name,strlen(mathname[i].name))!=0
  181.       || mathname[i].style==RM_PREFIX);
  182.     i--);
  183.   if(i>=0) {
  184.     type=mathname[i].style;
  185.     j=strlen(mathname[i].name);
  186.     goto gotit;
  187.   }
  188.   for(i=0;i<rm_uservars &&
  189.     strncmp(rm_uservar[i],p,strlen(rm_uservar[i]));i++);
  190.   if(i<rm_uservars) {
  191.     type=RM_VAR;
  192.     j=strlen(rm_uservar[i]); goto gotit;
  193.   }
  194.   for(i=0;i<rm_userfns &&
  195.       strncmp(p,rm_userfn[i],strlen(rm_userfn[i]));i++);
  196.   if(i<rm_userfns) {
  197.     type=RM_FN;
  198.     j=strlen(rm_userfn[i]); goto gotit;
  199.   }
  200.   return NULL;
  201. }
  202.  
  203. int __replace_badchar (char *p, char *old, char *new)
  204. {
  205.   int cnt = 0;
  206.   char *p1 ;
  207.   while((p1=strstr(p,old))!=NULL) {
  208.     string_modify(p,p1,p1+strlen(old),"%s",new);
  209.     cnt++;
  210.   }
  211.   return cnt ;
  212. }
  213.  
  214. /* translate |x| into abs(x)*/
  215. int __replace_abs ( char *p )
  216. {
  217.   char *p1, *p2 ;
  218.   while((p1=strchr(p,'|'))!=NULL) {
  219.     p2=find_matching(p1+1,'|');
  220.     if(p2==NULL) { return 1; break;} /* error; drop it. */
  221.     *p2=')'; string_modify(p,p1,p1+1,"abs(");
  222.   }
  223.   return 0;
  224. }
  225.  
  226. /* signs: translate ++, +-, -+, ... into one sign. */
  227. void __replace_plusminus ( char *p )
  228. {
  229.   char *p1, *p2;
  230.   for(p1=p;*p1!=0;p1++) {
  231.     int sign, redundant;
  232.     if(*p1!='+' && *p1!='-') continue;
  233.     if(*p1=='+') sign=1; else sign=-1;
  234.     redundant=0;
  235.     for(p2=find_word_start(p1+1);*p2=='+' || *p2=='-';
  236.     p2=find_word_start(p2+1)) {
  237.       if(*p2=='-') sign*=-1;
  238.       redundant=1;
  239.     }
  240.     if(redundant && *p2!='>' && strncmp(p2,"&gt;",4)!=0) {
  241.       if(sign==1) *p1='+'; else *p1='-';
  242.       ovlstrcpy(p1+1,p2);
  243.     }
  244.   }
  245. }
  246.  
  247. /* dangling decimal points
  248.  * 4. --> 4.0  4.x -> 4.0x
  249.  * .5 -> 0.5
  250.  * another treatment is done in insmath (will replace .. by , )
  251.  */
  252. void __treat_decimal(char *p)
  253. {
  254.   char *p1 ;
  255.   for(p1=strchr(p,'.'); p1!=NULL; p1=strchr(p1+1,'.')) {
  256.         /* multiple .. is conserved */
  257.     if(*(p1+1)=='.') {
  258.       do p1++; while(*p1=='.'); continue;
  259.     }
  260.     if(p1>p && myisdigit(*(p1-1)) && myisdigit(*(p1+1))) continue;
  261.         /* Non-digit dangling '.' is removed */
  262.     if((p1<=p || !myisdigit(*(p1-1))) && !myisdigit(*(p1+1))) {
  263.       ovlstrcpy(p1,p1+1); p1--; continue;
  264.     }
  265.     if(p1==p || !myisdigit(*(p1-1))) { // nondigit.digit
  266.       string_modify(p,p1,p1,"0"); p1++; //add zero before point
  267.     }
  268.     if(!myisdigit(*(p1+1))) string_modify(p,p1+1,p1+1,"0"); //add zero after point
  269.   }
  270. }
  271.  
  272. /* replace new-lines, tabs, " */
  273. void __replace_space(char *p)
  274. {
  275.   char *p1 ;
  276.   for(p1=p;*p1!=0; p1++) {
  277.     if(*p1=='\\' || isspace(*p1)) *p1=' ';// replace \ and all spaces by a simple space -
  278.     if(*p1=='\"') { string_modify(p,p1,p1+1,"''"); p1++;} // replace " by ''
  279.   }
  280. }
  281.  
  282. /* Error-tolerant raw math translation routine
  283.  * Translate error-laden raw math into machine-understandable form.
  284.  * do nothing if there is some { or \\
  285.  */
  286. void rawmath(char *p)
  287. {
  288.   char *p1, *p2, *p3, *p4;
  289.   char warnbuf[1024];
  290.   int ambiguous=0,unknown=0,flatpower=0,badprec=0,unmatch=0;// for warning
  291.  
  292. /* looks like a TeX source : do nothing */
  293.   if( (strchr(p,'\\')!=NULL || strchr(p,'{')!=NULL)) return;
  294.   if(strchr(p,'^')==NULL) flatpower=-1;
  295.   if(strlen(p)>=MAX_LINELEN) {*p=0; return;}
  296.   p1=find_word_start(p);if(*p1==0) return;
  297.   while(*p1=='+') p1++;
  298.   if(p1>p) ovlstrcpy(p,p1);
  299.   (void)__replace_badchar(p,"**", "^");
  300.   (void)__replace_badchar(p,"\xa0", " ");
  301.  
  302. /*if (__replace_badchar(p,"²", "^2 ")) flatpower=1;
  303.   if (__replace_badchar(p,"³", "^3 ")) flatpower=1;
  304. */
  305.   if (__replace_badchar(p,"\xB2", "^2 ")) flatpower=1;
  306.   if (__replace_badchar(p,"\xB3", "^3 ")) flatpower=1;
  307.   unmatch=__replace_abs(p);
  308.   __replace_plusminus(p) ;
  309.   __replace_space(p);
  310.   __treat_decimal(p);
  311.   if (rawmath_easy) return;
  312.  
  313. /* Principal translation: justapositions to multiplications */
  314.   if(strstr(p,"^1/")!=NULL) badprec=1;
  315.   getuservar();
  316.   for(p1=p;*p1;p1++) {
  317.     if(!isalnum(*p1) && *p1!=')' && *p1!=']') continue;
  318.     if(*p1==')' || *p1==']') {
  319.       p2=find_word_start(++p1);
  320.       add_star:
  321.       if(isalnum(*p2) || *p2=='(' || *p2=='[') {
  322.         if(*p2=='(' && *p1==')') ambiguous=1;
  323.         if(p2>p1) *p1='*';
  324.         else string_modify(p,p1,p1,"*");
  325.       }
  326.       p1--;continue;
  327.     }
  328.     p2=find_mathvar_end(p1); p3=find_word_start(p2);
  329.     if(myisdigit(*p1)) {
  330.       p1=p2; p2=p3; goto add_star;
  331.     }
  332.     else {
  333.       char buf[MAX_LINELEN+1];
  334.       int i;
  335.       if(p2-p2>30) goto ambig;
  336.       memcpy(buf,p1,p2-p1);buf[p2-p1]=0;
  337.       i=search_evaltab(buf);
  338.       if(i>=0 && get_evaltype(i)>0) {
  339.         fnname1:
  340.         p1=p2;p2=p3;
  341. /*fnname:*/
  342.         if(*p2 && *p2!='(' && *p2!='*' && *p2!='/') {
  343.           char hatbuf[MAX_LINELEN+1];
  344.           hatbuf[0]=')'; hatbuf[1]=0;
  345.           if(*p2=='^') {
  346.             p3=p2+1;while(*p3==' ' || *p3=='+' || *p3=='-') p3++;
  347.             if(*p3=='(') {
  348.               p3=find_matching(p3+1,')');
  349.               if(p3==NULL) {unmatch=1; p3=p+strlen(p);}
  350.               else p3++;
  351.             }
  352.             else p3=find_mathvar_end(p3);
  353.             memmove(hatbuf+1,p2,p3-p2);hatbuf[p3-p2+1]=0;
  354.             ovlstrcpy(p2,p3);
  355.             while(*p2==' ') p2++;
  356.             if(*p2=='*' || *p2=='/') {
  357.               p1--;continue;
  358.             }
  359.             if(*p2=='(') {
  360.               p3=find_matching(p2+1,')');
  361.               if(p3==NULL) {unmatch=1; p3=p+strlen(p);}
  362.               else p3++;
  363.               string_modify(p,p3,p3,"%s",hatbuf+1);
  364.               goto dd2;
  365.             }
  366.           }
  367.           p3=p2;if(*p3=='+' || *p3=='-') p3++;
  368.           while(isalnum(*p3) || *p3=='*' || *p3=='/' || *p3=='.')
  369.             p3++;
  370.           for(p4=p2; p4<p3 && !isalnum(*p4); p4++);
  371.           if(p4>=p3) {
  372.             if(hatbuf[1]) string_modify(p,p2,p2,"%s",hatbuf+1);
  373.           }
  374.           else {
  375.             string_modify(p,p3,p3,"%s",hatbuf);
  376.             if(p1==p2) string_modify(p,p2,p2,"(");
  377.             else *p1='(';
  378.           }
  379.           dd2:
  380.           ambiguous=1;
  381.         }
  382.         p1--;continue;
  383.       }
  384.       i=search_list(mathname,mathname_no,sizeof(mathname[0]),buf);
  385.       if(i>=0) {
  386.         if(mathname[i].replace[0]!=0) {
  387.           string_modify(p,p1,p2,mathname[i].replace);
  388.           p2=p1+strlen(mathname[i].replace);
  389.           p3=find_word_start(p2);
  390.         }
  391.         switch(mathname[i].style) {
  392.           case RM_FN:
  393.             goto fnname1;
  394.  
  395.           case RM_VAR:
  396.             p1=p2;p2=p3; goto add_star;
  397.  
  398.           case RM_PREFIX:
  399.             if(*p3!='c' && *p3!='s' && *p3!='t' &&
  400.               *p3!='C' && *p3!='S' && *p3!='T') break;
  401.             ambiguous=1;
  402.             ovlstrcpy(p2,p3); p1--; continue;
  403.         }
  404.       }
  405.       i=search_list(hmname,hmname_no,sizeof(hmname[0]),buf);
  406.       if(i>=0) {
  407.         p1=p2; p2=p3; goto add_star;
  408.       }
  409.       for(i=0;i<rm_uservars && strcmp(buf,rm_uservar[i]);i++);
  410.       if(i<rm_uservars) {
  411.           p1=p2;p2=p3;goto add_star;
  412.       }
  413.       for(i=0;i<rm_userfns && strcmp(buf,rm_userfn[i]);i++);
  414.       if(i<rm_userfns) goto fnname1;
  415.       if(p2-p1>8) goto ambig;
  416.       if(mathname_split(buf)!=NULL) {
  417.         ambiguous=1;
  418.         string_modify(p,p1,p2,"%s",buf);
  419.         p1--; continue;
  420.       }
  421. /* unknown name */
  422.       ambig: p1=p2;p2=p3;
  423.       if(strlen(buf)>1) {
  424.         for(p3=buf;*p3!=0 && !myisdigit(*p3);p3++);
  425.         if(*p3!=0 && flatpower!=0) flatpower=1;
  426.         else {
  427.           unknown=1;
  428.           force_setvar("wims_warn_rawmath_parm",buf);
  429.         }
  430.       }
  431.       else {
  432.         if(*p2=='(') ambiguous=1;
  433.       }
  434.       if(isalnum(*p2)) {
  435.         if(p2>p1) *p1='*';
  436.         else string_modify(p,p1,p1,"*");
  437.       }
  438.       p1--;continue;
  439.     }
  440.   }
  441.   warnbuf[0]=0;
  442.   if(ambiguous) strcat(warnbuf," ambiguous");
  443.   if(unknown) strcat(warnbuf," unknown");
  444.   if(flatpower>0) strcat(warnbuf," flatpower");
  445.   if(badprec>0) strcat(warnbuf," badprec");
  446.   if(unmatch>0) strcat(warnbuf," unmatched_parentheses");
  447.   if(warnbuf[0]) {
  448.     char buf[MAX_LINELEN+1],*p;
  449.     p=getvar("wims_warn_rawmath");
  450.     if(p!=NULL && *p!=0) {
  451.       snprintf(buf,sizeof(buf),"%s %s",p,warnbuf);
  452.       force_setvar("wims_warn_rawmath",buf);
  453.     }
  454.     else force_setvar("wims_warn_rawmath",warnbuf);
  455.   }
  456. }
  457.  
  458.  /* replace < and > by html code if htmlmath_gtlt=yes
  459.   * is only used in deduc - not documented
  460.   */
  461. void __replace_htmlmath_gtlt (char *p)
  462. {
  463.   char *pp;
  464.   char *p1=getvar("htmlmath_gtlt");
  465.   if(p1!=NULL && strcmp(p1,"yes")==0) {
  466.     for(pp=strchr(p,'<'); pp!=NULL; pp=strchr(pp+1,'<'))
  467.       string_modify(p,pp,pp+1,"&lt;");
  468.     for(pp=strchr(p,'>'); pp!=NULL; pp=strchr(pp+1,'>'))
  469.       string_modify(p,pp,pp+1,"&gt;");
  470.   }
  471. }
  472.  
  473. /* exponents or indices :
  474.  *  all digits or + or - following a ^ or _ are considered as in exponent/subscript
  475.  *  expression with ( ) following a ^ or _ are  considered as in exponent/subscript
  476.  *  the parenthesis are suppressed except in case of exponent and only digits.
  477.  *  if int n != 0, use html code, else use tex code
  478.  */
  479. void __replace_exponent(char *p, int n)
  480. {
  481.   char *p1;
  482.   char *SUPBEG, *SUPEND;
  483.   if (n) { SUPBEG = "<sup>"; SUPEND = "</sup>";}
  484.   else { SUPBEG = "^{"; SUPEND = "}";}
  485.  
  486.   for(p1=strchr(p,'^');p1!=NULL;p1=strchr(p1+1,'^')) {
  487.     char *p2, *p3, *pp;
  488.     char c;
  489.     p3 = p2 = find_word_start(p1+1);
  490.     if(*p2=='+' || *p2=='-') p2++;
  491.     p2 = find_word_start(p2);
  492. /* add '}' to recognized parenthesis in exponent
  493.  * !mathmlmath 2 \cdot x^{3} will now produce correct exponent...
  494.  * !mathmlmath should convert LaTeX input into correct MathML
  495.  */
  496.     if(*p2=='(' || *p2 == '{') { /* ^[+-]( */
  497.       if(*p2 == '('){ p2 = find_matching(p2+1,')');}else { if(*p2 == '{'){ p2 = find_matching(p2+1,'}');}}
  498.             /* no matching ')' : p2 = end of line; otherwise just after ')' */
  499.       if (p2==NULL) p2=p+strlen(p); else p2++;
  500.             /* ^( followed by any number of digits/letters, up to p2
  501.              * [FIXME: no sign?] */
  502.             /* do we remove parentheses containing exponent group ? */
  503.       if (*p3=='(') for(pp=p3+1;pp<p2-1;pp++) {
  504.         if(!isalnum(*pp)) {
  505.                     /* not a digit/letter. Remove (). */
  506.           p3++;*(p2-1)=0;break;
  507.         }
  508.                 /* x^(2), parentheses not removed */
  509.       }
  510. /* p3: start of word after ^
  511.  * matching parentheses from exponent group. : f^(4) 4-derivative
  512.  * don't  ignore / remove a leading + sign in exponent group : Cu^+
  513.  */
  514.     }
  515.     else { /* ^[+-] */
  516.       char *ptt=p2;
  517.       p2=find_word_start(find_mathvar_end(p2));
  518.       if(*p2=='(' && isalpha(*ptt)) {
  519.               /* ^[+-]var( */
  520.         char *p2t;
  521.         p2t=find_matching(p2+1,')'); if(p2t!=NULL) p2=p2t+1;
  522.               /* FIXME: what if no matching ) ? */
  523.       }
  524. /* ^[+-]var(...): p2 points after closing ')'
  525.  * FIXME: I don't think this 'else' branch is sensible. One
  526.  * should NOT accept x^b(c+1) as meaning x^[b(c+1)]. I would
  527.  * remove it altogether.
  528.  */
  529.     }
  530. /* p1 points at ^ before exponent group
  531.  * p2 points at end of exponent group
  532.  * p3 = exponent group (sometimes stripped, without parentheses)
  533.  *
  534.  * truncate string p at p2 [ c = char deleted by truncation ]
  535.  */
  536.     c = *p2;if(c!=0) *p2++=0;
  537.         /* replace ^<exponent group>. Add back missing character 'c' */
  538.     string_modify(p,p1,p2, "%s%s%s%c",SUPBEG,p3,SUPEND,c);
  539.   }
  540. }
  541.  
  542. /* if int n != 0, use html code, else use tex code */
  543. void __replace_subscript(char *p, int n)
  544. {
  545.    char *p1, *p2;
  546.    char *SUBBEG, *SUBEND;
  547.    if (n) {SUBBEG = "<sub>"; SUBEND = "</sub>";}
  548.    else {SUBBEG = "_{"; SUBEND = "}";}
  549.    for(p1=strchr(p,'_');p1!=NULL;p1=strchr(p1+1,'_')) {
  550.      char buff[256];
  551.      p2=p1+1;
  552.      if(*p2=='(') p2=find_matching(p2+1,')');
  553.      else p2=find_mathvar_end(p2);
  554.      if(p2==NULL || p2>p1+64) continue;
  555.      if(*(p1+1)=='(') p2++;
  556.      memmove(buff,p1+1,p2-p1-1); buff[p2-p1-1]=0;
  557.      strip_enclosing_par(buff);
  558.      string_modify(p,p1,p2,"%s%s%s",SUBBEG,buff,SUBEND);}
  559. }
  560.  
  561. /* get rid of 1*.. ..*1  exemple : 1 * x, x/1 */
  562. void __replace_getrid1 (char *p)
  563. {
  564.   char *p1, *p2, *p3 ;
  565.   for(p1=p;*p1;p1++) {
  566.     if(*p1!='1') continue;
  567.     if(myisdigit(*(p1+1)) || *(p1+1)=='.' ||
  568.       (p1>p && (isalnum(*(p1-1)) || *(p1-1)=='.')) ) continue;
  569.     p2=find_word_start(p1+1);
  570.     if(p1>p+2 && (*(p1-1)=='-' || *(p1-1)=='+')) {
  571.       for(p3=p1-2; p3>p && isspace(*p3); p3--);
  572.       if(p3>p+1 && (*p3=='E' || *p3=='e')) { /* ??? */
  573.         p3--; while(p3>p && isspace(*p3)) p3--;
  574.         if(myisdigit(*p3) || *p3=='.') continue;
  575.       }
  576.     }
  577.     if(p1==p) p3="+";
  578.     else for(p3=p1-1;p3>p && isspace(*p3);p3--);
  579.     if(*p2=='*' && *p3!='/') {/* delete 1 if 1* or /1 */
  580.       ovlstrcpy(p1,p2+1);continue;
  581.     }
  582.     if(isalpha(*p2) && *p3!='/') {
  583.       ovlstrcpy(p1,p2);continue;
  584.     }
  585.     if(*p3=='/' && *p2!='<') ovlstrcpy(p3,p2);
  586.  
  587.   }
  588. }
  589.  
  590. /* get rid of '*' */
  591. void __replace_getridstar (char *p)
  592. {
  593.   char *p1 ;
  594.   for(p1=strchr(p,'*');p1!=NULL;p1=strchr(p1+1,'*')) {
  595.     char *pq;
  596.     pq=find_word_start(p1+1);
  597.     if(myisdigit(*pq)) {
  598.       string_modify(p,p1,pq,"&times;");
  599.       continue;
  600.     }
  601.     if(p1>p && (isalpha(*(p1-1)) || *(p1-1)==')' || *(p1-1)=='>')
  602.        && (isalnum(*pq) || *pq=='$')) *p1=' ';
  603.     else {
  604.         ovlstrcpy(p1,p1+1);p1--;
  605.     }
  606.   }
  607. }
  608.  
  609. /* <=, >=, ->, =>, <=>, !=, <-> <-->, <-
  610.  * if int n != 0, use html code, else use tex code
  611.  */
  612.  
  613. void __replace_arrow ( char *p, int n)
  614. {
  615.   char *p1, *p2, *m_prefix;
  616.   if (n) m_prefix="$m_"; else m_prefix="\\";
  617.  
  618.   for(p1=strstr(p,"&lt;="); p1!=NULL; p1=strstr(p1+1,"&lt;=")) {
  619.     if(*(p1+5)!='&' && *(p1+5)!='=')
  620.       string_modify(p,p1,p1+5, "%sle",m_prefix);
  621.     else {
  622.       for(p2=p1+5; *p2=='='; p2++);
  623.       if(strncmp(p2,"&gt;",4)==0) {
  624.         if(p2>p1+5) { string_modify(p,p1,p2+4,"%sLongleftrightarrow",m_prefix);}
  625.         else { string_modify(p,p1,p2+4,"%sLeftrightarrow",m_prefix);}
  626.       }
  627.       else { string_modify(p,p1,p2,"%sLongleftarrow",m_prefix);}
  628.     }
  629.   }
  630.   for(p1=strstr(p,"&gt;="); p1!=NULL; p1=strstr(p1+1,"&gt;=")) {
  631.     if(*(p1+5)!='=') { string_modify(p,p1,p1+5,"%sge",m_prefix);}
  632.   }
  633.   for(p1=strstr(p,"-&gt;"); p1; p1=strstr(p1+1,"-&gt;")) {
  634.     for(p2=p1; p2>p && *(p2-1)=='-'; p2--);
  635.     if(p2>p && *(p2-1)==';') continue;
  636.     if(p2<p1) { string_modify(p,p2,p1+5,"%slongrightarrow",m_prefix);}
  637.     else {  string_modify(p,p1,p1+5,"%srightarrow",m_prefix);}
  638.   }
  639.   for(p1=strstr(p,"=&gt;"); p1; p1=strstr(p1+1,"=&gt;")) {
  640.     for(p2=p1; p2>p && *(p2-1)=='='; p2--);
  641.     if(p2>p && *(p2-1)==';') continue;
  642.     if(p2<p1) { string_modify(p,p2,p1+5,"%sLongrightarrow",m_prefix);}
  643.     else { string_modify(p,p1,p1+5,"%sRightarrow",m_prefix) ;}
  644.   }
  645.   /* <->, <-->, <- */
  646.   /* <-- not implemented because -- is replaced by + elsewhere */
  647.   for(p1=strstr(p,"&lt;-"); p1!=NULL; p1=strstr(p1+1,"&lt;-")) {
  648.     for(p2=p1+5; *p2=='-'; p2++);
  649.     if(strncmp(p2,"&gt;",4)==0)
  650.       if ((p2-p1)%2)
  651.         string_modify(p,p1,p2+4,(p2>p1+5) ? "%slongleftrightarrow" : "%sleftrightarrow",m_prefix);
  652.       else
  653.         string_modify(p,p1,p2+4,(p2>p1+6) ? "%sleftrightharpoons" : "%sleftrightharpoons",m_prefix);
  654.     else
  655.       string_modify(p,p1,p2,(p2>p1+5) ? "%slongleftarrow " : "%sleftarrow ",m_prefix);
  656.   }
  657.  
  658.   /* Not equal */
  659.   for(p1=strstr(p,"!="); p1; p1=strstr(p1+1,"!=")) {
  660.     if(p1>p && !isspace(*(p1-1))) continue;
  661.     string_modify(p,p1,p1+2,"%sneq",m_prefix);
  662.   }
  663. }
  664.  
  665. /* why <tt> is used sometimes ? replace single characters by italics one
  666.  * is it useful in mathml ?
  667.  */
  668. void __replace_italics (char *p, int n)
  669. {
  670.   char *p1, *p2, *p3, pbuf[16];
  671.   char *ITBEG, *ITEND, *ITtBEG, *ITtEND;
  672.    if (n) {
  673.      ITBEG = "<i>";ITtBEG = "<i><tt>";
  674.      ITEND = "</i>";ITtEND = "</tt></i>";
  675.   } else {;
  676.      ITBEG = "" ; ITtBEG = "";
  677.      ITEND = ""; ITtEND = "";
  678.   }
  679.  
  680.   for(p1=p;*p1;p1++) {
  681.     if(*p1=='<') {
  682.       p1=strchr(p1,'>');
  683.       if(p1==NULL) break; //recognize an html tag < >
  684.       else continue;
  685.     }
  686.     if(*p1=='=' && *(p1+1)=='-') {
  687.         string_modify(p,p1+1,p1+1," "); p1+=2; continue;
  688.     }
  689.     if(*p1=='\'') {
  690.       for(p2=p1+1;*p2=='\'';p2++);
  691.       memmove(pbuf,p1,p2-p1); pbuf[p2-p1]=0;
  692.       string_modify(p,p1,p2,"%s%s%s",ITtBEG,pbuf,ITtEND);
  693.       p1=p2+strlen(ITtBEG)+strlen(ITtEND)-1;
  694.       continue;
  695.     }
  696.     if(!isalpha(*p1)) continue;
  697. /* unique letter.*/
  698.     for(p2=p1+1;isalpha(*p2);p2++);
  699.     p3=find_word_start(p2);
  700.     if(p2>p1+5 ||
  701.        (p2>p1+1 && (*p3==';' || *p3=='(' || myisdigit(*p2))))
  702.       {p1=p2-1; continue;}
  703.     if(strncasecmp(p3,"</i>",strlen("</i>"))==0) continue;
  704.     memmove(pbuf,p1,p2-p1); pbuf[p2-p1]=0;
  705.     string_modify(p,p1,p2,"%s%s%s",ITBEG,pbuf,ITEND);
  706.     p1=p2+strlen(ITBEG)+strlen(ITEND)-1;
  707.   }
  708. }
  709.  
  710. /* float (1.2 E-03) : 3E+021 -> 3 × 10^{21} - 3E-21 -> 3 × 10^{-21}
  711.  * or replace variable name (alpha)
  712.  * if int n != 0, use html code, else use tex code
  713.  */
  714. void __replace_mathvar(char *p,int n)
  715. {
  716.   char *p1, *p2, *p3;
  717.   char *EXPBEG, *EXPEND, *EXPBEGMINUS, *SUBBEG, *SUBEND, *m_prefix;
  718.  
  719.   if ( n ) {
  720.     EXPBEG = " &times; 10<sup>";
  721.     EXPBEGMINUS = " &times; 10<sup>-";
  722.     EXPEND = "</sup>";
  723.     SUBBEG = "<sub>";
  724.     SUBEND = "</sub>";
  725.     m_prefix="$m_";
  726.   } else {
  727.     EXPBEG = " \\times 10^{" ;
  728.     EXPBEGMINUS = " \\times 10^{-" ;
  729.     EXPEND = "}";
  730.     SUBBEG = "_{";
  731.     SUBEND = "}";
  732.     m_prefix="\\";
  733.   }
  734.   for (p1=find_mathvar_start(p);*p1!=0;p1=find_mathvar_start(p2)) {
  735.     char buf[MAX_LINELEN+1];
  736. /* if the variable is preceded by \ do nothing
  737.  * in fact this should not arrive
  738.  */
  739.     if (p1>p && *(p1-1) == '\\' ) break ;
  740.     p2 = find_mathvar_end(p1);
  741.     if (p1 == p2) break;
  742.     memmove(buf,p1,p2-p1);buf[p2-p1]=0;
  743.  
  744.     if(myisdigit(buf[0])) {
  745. /* number : 3E+021 -> 3 x 10^{21} - 3E-21 -> 3 x 10^{-21} */
  746.       int k = 1, minus = 0;
  747.  
  748. /* see putnumber in texmath.c*/
  749.       if( (p3 = strpbrk(buf, "Ee")) == NULL) continue;
  750.       p1 += p3-buf;
  751.             //count the number of 0, +, -
  752.       if (*(p1+k) == '-') { minus = 1; k++; }
  753.       while(*(p1+k)=='0' || *(p1+k)=='+') k++;
  754.  
  755.       string_modify(p,p1,p1+k, minus? EXPBEGMINUS: EXPBEG);
  756.       p2 += strlen(minus? EXPBEGMINUS: EXPBEG)-k;
  757.       string_modify(p,p2,p2, EXPEND);
  758.       p2 += strlen(EXPEND);
  759.     }
  760.     else {
  761. /* alphabetic name, replace greek letters and symbols in hmname
  762.  * not done in texmath.c
  763.  */
  764.       int i = search_list(hmname,hmname_no,sizeof(hmname[0]),buf);
  765.       char *n, *r;
  766.       if(i<0) { /* not in list */
  767.         int k = strlen(buf)-1;
  768.         if(myisdigit(buf[k])) {
  769.           while(k>0 && myisdigit(buf[k-1])) k--;
  770.           string_modify(buf,buf+k,buf+k,SUBBEG);
  771.           string_modify(p,p1,p2,"%s%s",buf,SUBEND);
  772.           p2+=strlen(SUBBEG)+strlen(SUBEND);
  773.         }
  774.         continue;
  775.       }
  776.       n = hmname[i].name;
  777.       r = mathalign_base < 2? hmname[i].replace: hmname[i].replacem;
  778.       if(r[0]==0) { /* replace = "" */
  779.         string_modify(p,p1,p2,"%s%s",m_prefix, n);
  780.         p2 = p1+strlen(n)+strlen(m_prefix);
  781.       } else {
  782.         string_modify(p,p1,p2,"%s",r);
  783.         p2 = p1+strlen(r);
  784.       }
  785.     }
  786.   }
  787. }
  788.  
  789. /* translate raw math expression coming from calculators into best html way
  790.  * if int n != 0, use html code, else use tex code
  791.  */
  792. void __htmlmath(char *p,int n)
  793. {
  794.   if(!rawmath_easy) { rawmath_easy=1; rawmath(p); rawmath_easy=0;}
  795.   __replace_htmlmath_gtlt(p); //only used in deductio
  796.   __replace_exponent(p,n);
  797.   __replace_subscript(p,n);
  798.   __replace_getrid1(p);
  799.   __replace_mathvar(p,n);
  800.   __replace_getridstar(p);
  801.   __replace_arrow(p,n);
  802. /* Now make substitutions */
  803.   substit(p);
  804. /* Make single names italic - done automatically by mathml */
  805.    __replace_italics(p,n);
  806.    strip_trailing_spaces(p);
  807. }
  808.  
  809. void htmlmath(char *p)
  810. {
  811.  __htmlmath(p,1) ;
  812. }
  813.  
  814. /* if mathml is closed, it will be just htmlmath*/
  815.  
  816. void mathmlmath(char *p)
  817. {
  818.     /*
  819.      if force_mathml variable is set to "yes", do not (never) use 'htmlmath'
  820.      in output, so command: !mathmlmath some_LaTeX_string will produce mathml
  821.      (mathml \input inputfields may be included)
  822.     */
  823.   if( strcmp( getvar("force_mathml"),"yes") == 0 ){ mathalign_base = 2;mathml(p,1);return; }
  824.   if (mathalign_base == 2) { __htmlmath(p,0) ; mathml(p,1);} else { __htmlmath(p,1) ;}
  825. }
  826.