Subversion Repositories wimsdev

Rev

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