Subversion Repositories wimsdev

Rev

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