Diff for /loncom/cgi/mimeTeX/mimetex.c between versions 1.4 and 1.5

version 1.4, 2008/12/04 12:17:13 version 1.5, 2012/06/09 00:58:11
Line 1 Line 1
 /****************************************************************************  /****************************************************************************
  *   *
  * Copyright(c) 2002-2008, John Forkosh Associates, Inc. All rights reserved.   * Copyright(c) 2002-2012, John Forkosh Associates, Inc. All rights reserved.
  *           http://www.forkosh.com   mailto: john@forkosh.com   *           http://www.forkosh.com   mailto: john@forkosh.com
  * --------------------------------------------------------------------------   * --------------------------------------------------------------------------
  * This file is part of mimeTeX, which is free software. You may redistribute   * This file is part of mimeTeX, which is free software. You may redistribute
Line 49 Line 49
  * usage is available on its homepage,   * usage is available on its homepage,
  *  http://www.forkosh.com/mimetex.html   *  http://www.forkosh.com/mimetex.html
  * and similarly in mimetex.html included with your mimetex.zip   * and similarly in mimetex.html included with your mimetex.zip
  * distribution file.   * distribution file. (Note: http://www.forkosh.com/mimetex.html
    * is a "quickstart" version of the the full mimetex.html manual
    * included in your mimetex.zip distribution file.)
  *   *
  * Functions: ===================== Raster Functions ======================   * Functions: The following "table of contents" lists each function
    * comprising mimeTeX in the order it appears in this file.
    * See individual function entry points for specific comments
    * about its purpose, calling sequence, side effects, etc.
    * (All these functions eventually belong in several
    * different modules, possibly along the lines suggested
    * by the divisions below.  But until the best decomposition
    * becomes clear, it seems better to keep mimetex.c
    * neatly together, avoiding a bad decomposition that
    * becomes permanent by default.)
    * ===================== Raster Functions ======================
  * PART2 --- raster constructor functions ---   * PART2 --- raster constructor functions ---
  * new_raster(width,height,pixsz)   allocation (and constructor)   * new_raster(width,height,pixsz)   allocation (and constructor)
  * new_subraster(width,height,pixsz)allocation (and constructor)   * new_subraster(width,height,pixsz)allocation (and constructor)
Line 63 Line 75
  * rastcpy(rp)                           allocate new copy of rp   * rastcpy(rp)                           allocate new copy of rp
  * subrastcpy(sp)                        allocate new copy of sp   * subrastcpy(sp)                        allocate new copy of sp
  * rastrot(rp)         new raster rotated right 90 degrees to rp   * rastrot(rp)         new raster rotated right 90 degrees to rp
    * rastmag(rp,magstep)   new raster magnified by "magstep" to rp
    * bytemapmag(bytemap,width,height,magstep)      magnify bytemap
  * rastref(rp,axis)    new raster reflected (axis 1=horz,2=vert)   * rastref(rp,axis)    new raster reflected (axis 1=horz,2=vert)
  * rastput(target,source,top,left,isopaque)  overlay src on trgt   * rastput(target,source,top,left,isopaque)  overlay src on trgt
  * rastcompose(sp1,sp2,offset2,isalign,isfree) sp2 on top of sp1   * rastcompose(sp1,sp2,offset2,isalign,isfree) sp2 on top of sp1
Line 72 Line 86
  * rastsmash(sp1,sp2,xmin,ymin)      calc #smash pixels sp1||sp2   * rastsmash(sp1,sp2,xmin,ymin)      calc #smash pixels sp1||sp2
  * rastsmashcheck(term)         check if term is "safe" to smash   * rastsmashcheck(term)         check if term is "safe" to smash
  * --- raster "drawing" functions ---   * --- raster "drawing" functions ---
  * accent_subraster(accent,width,height)       draw \hat\vec\etc   * accent_subraster(accent,width,height,direction,pixsz)\hat\vec
  * arrow_subraster(width,height,drctn,isBig)    left/right arrow   * arrow_subraster(width,height,drctn,isBig)    left/right arrow
  * uparrow_subraster(width,height,drctn,isBig)     up/down arrow   * uparrow_subraster(width,height,drctn,isBig)     up/down arrow
  * rule_raster(rp,top,left,width,height,type)    draw rule in rp   * rule_raster(rp,top,left,width,height,type)    draw rule in rp
Line 117 Line 131
  * strchange(nfirst,from,to)   change nfirst chars of from to to   * strchange(nfirst,from,to)   change nfirst chars of from to to
  * strreplace(string,from,to,nreplace)  change from to to in str   * strreplace(string,from,to,nreplace)  change from to to in str
  * strwstr(string,substr,white,sublen)     find substr in string   * strwstr(string,substr,white,sublen)     find substr in string
    * strdetex(s,mode)    replace math chars like \^_{} for display
  * strtexchr(string,texchr)                find texchr in string   * strtexchr(string,texchr)                find texchr in string
  * findbraces(expression,command)    find opening { or closing }   * findbraces(expression,command)    find opening { or closing }
    * strpspn(s,reject,segment)     non-() chars of s not in reject
    * isstrstr(string,snippets,iscase)  are any snippets in string?
    * isnumeric(s)                     determine if s is an integer
    * evalterm(store,term)     evaluate numeric value of expression
    * getstore(store,identifier)return value corresponding to ident
    * unescape_url(url,isescape), x2c(what)   xlate %xx url-encoded
  * PART3 =========== Rasterize an Expression (recursively) ===========   * PART3 =========== Rasterize an Expression (recursively) ===========
  * --- here's the primary entry point for all of mimeTeX ---   * --- here's the primary entry point for all of mimeTeX ---
  * rasterize(expression,size)     parse and rasterize expression   * rasterize(expression,size)     parse and rasterize expression
Line 152 Line 173
  * rastbezier(expression,size,basesp,arg1,arg2,arg3)     \bezier   * rastbezier(expression,size,basesp,arg1,arg2,arg3)     \bezier
  * rastraise(expression,size,basesp,arg1,arg2,arg3)    \raisebox   * rastraise(expression,size,basesp,arg1,arg2,arg3)    \raisebox
  * rastrotate(expression,size,basesp,arg1,arg2,arg3)  \rotatebox   * rastrotate(expression,size,basesp,arg1,arg2,arg3)  \rotatebox
    * rastmagnify(expression,size,basesp,arg1,arg2,arg3)   \magnify
  * rastreflect(expression,size,basesp,arg1,arg2,arg3)\reflectbox   * rastreflect(expression,size,basesp,arg1,arg2,arg3)\reflectbox
  * rastfbox(expression,size,basesp,arg1,arg2,arg3)         \fbox   * rastfbox(expression,size,basesp,arg1,arg2,arg3)         \fbox
  * rastinput(expression,size,basesp,arg1,arg2,arg3)       \input   * rastinput(expression,size,basesp,arg1,arg2,arg3)       \input
  * rastcounter(expression,size,basesp,arg1,arg2,arg3)   \counter   * rastcounter(expression,size,basesp,arg1,arg2,arg3)   \counter
    * rasteval(expression,size,basesp,arg1,arg2,arg3)         \eval
  * rasttoday(expression,size,basesp,arg1,arg2,arg3)       \today   * rasttoday(expression,size,basesp,arg1,arg2,arg3)       \today
  * rastcalendar(expression,size,basesp,arg1,arg2,arg3) \calendar   * rastcalendar(expression,size,basesp,arg1,arg2,arg3) \calendar
    * rastenviron(expression,size,basesp,arg1,arg2,arg3)   \environ
    * rastmessage(expression,size,basesp,arg1,arg2,arg3)   \message
  * rastnoop(expression,size,basesp,arg1,arg2,arg3) flush \escape   * rastnoop(expression,size,basesp,arg1,arg2,arg3) flush \escape
  * --- helper functions for handlers ---   * --- helper functions for handlers ---
  * rastopenfile(filename,mode)      opens filename[.tex] in mode   * rastopenfile(filename,mode)      opens filename[.tex] in mode
Line 168 Line 193
  * timestamp(tzdelta,ifmt)              formats timestamp string   * timestamp(tzdelta,ifmt)              formats timestamp string
  * tzadjust(tzdelta,year,month,day,hour)        adjust date/time   * tzadjust(tzdelta,year,month,day,hour)        adjust date/time
  * daynumber(year,month,day)     #days since Monday, Jan 1, 1973   * daynumber(year,month,day)     #days since Monday, Jan 1, 1973
    * strwrap(s,linelen,tablen)insert \n's and spaces to wrap lines
    * strnlower(s,n)        lowercase the first n chars of string s
    * urlprune(url,n)  http://abc.def.ghi.com/etc-->abc.def.ghi.com
    * urlncmp(url1,url2,n)   compares topmost n levels of two url's
  * dbltoa(d,npts)                double to comma-separated ascii   * dbltoa(d,npts)                double to comma-separated ascii
  * === Anti-alias completed raster (lowpass) or symbols (ss) ===   * === Anti-alias completed raster (lowpass) or symbols (ss) ===
  * aalowpass(rp,bytemap,grayscale)     lowpass grayscale bytemap   * aalowpass(rp,bytemap,grayscale)     lowpass grayscale bytemap
Line 192 Line 221
  * PART1 ========================== Driver ===========================   * PART1 ========================== Driver ===========================
  * main(argc,argv) parses math expression and emits mime xbitmap   * main(argc,argv) parses math expression and emits mime xbitmap
  * CreateGifFromEq(expression,gifFileName)  entry pt for win dll   * CreateGifFromEq(expression,gifFileName)  entry pt for win dll
  * isstrstr(string,snippets,iscase)  are any snippets in string?  
  * ismonth(month)          is month current month ("jan"-"dec")?   * ismonth(month)          is month current month ("jan"-"dec")?
  * unescape_url(url,isescape), x2c(what)   xlate %xx url-encoded  
  * logger(fp,msglevel,logvars)        logs environment variables   * logger(fp,msglevel,logvars)        logs environment variables
  * emitcache(cachefile,maxage,valign,isbuffer)    emit cachefile   * emitcache(cachefile,maxage,valign,isbuffer)    emit cachefile
  * readcachefile(cachefile,buffer)    read cachefile into buffer   * readcachefile(cachefile,buffer)    read cachefile into buffer
    * advertisement(expression,mode)  wrap expression in ad message
    * crc16(s)                               16-bit crc of string s
  * md5str(instr)                      md5 hash library functions   * md5str(instr)                      md5 hash library functions
  * GetPixel(x,y)           callback function for gifsave library   * GetPixel(x,y)           callback function for gifsave library
  *   *
Line 205 Line 234
  * and also needs gifsave.c when compiled with -DAA or -DGIF)   * and also needs gifsave.c when compiled with -DAA or -DGIF)
  *   *
  * --------------------------------------------------------------------------   * --------------------------------------------------------------------------
  * Notes      o See bottom of file for main() driver (and "friends"),   * Notes      o See individual function entry points for specific comments
    * about the purpose, calling sequence, side effects, etc
    * of each mimeTeX function listed above.
    *      o See bottom of file for main() driver (and "friends"),
  * and compile as   * and compile as
  *   cc -DAA mimetex.c gifsave.c -lm -o mimetex.cgi   *   cc -DAA mimetex.c gifsave.c -lm -o mimetex.cgi
  * to produce an executable that emits gif images with   * to produce an executable that emits gif images with
Line 217 Line 249
  * to produce an executable that just emits mime xbitmaps.   * to produce an executable that just emits mime xbitmaps.
  * In either case you'll need mimetex.h and texfonts.h,   * In either case you'll need mimetex.h and texfonts.h,
  * and with -DAA or -DGIF you'll also need gifsave.c   * and with -DAA or -DGIF you'll also need gifsave.c
    *      o The font information in texfonts.h was produced by multiple
    * runs of gfuntype, one run per struct (i.e., one run per font
    * family at a particular size).  Compile gfuntype as
    *   cc gfuntype.c mimetex.c -lm -o gfuntype
    * See gfuntype.c, and also mimetex.html#fonts, for details.
  *      o For gif images, the gifsave.c library by Sverre H. Huseby   *      o For gif images, the gifsave.c library by Sverre H. Huseby
  * <http://shh.thathost.com> slightly modified by me to allow   * <http://shh.thathost.com> slightly modified by me to allow
  * (a)sending output to stdout or returning it in memory,   * (a)sending output to stdout or returning it in memory,
  * and (b)specifying a transparent background color index,   * and (b)specifying a transparent background color index,
  * is included with mimeTeX, and it's documented in   * is included with mimeTeX, and it's documented in
  * mimetex.html#gifsave .   * mimetex.html#gifsave
    *      o MimeTeX's principal reusable function is rasterize(),
    * which takes a string like "f(x)=\int_{-\infty}^xe^{-t^2}dt"
    * and returns a (sub)raster representing it as a bit or bytemap.
    * Your application can do anything it likes with this pixel map.
    * MimeTeX just outputs it, either as a mime xbitmap or as a gif.
    * See  mimetex.html#makeraster  for further discussion
    * and examples.
    *      o File mimetex.c also contains library functions implementing
    * a raster datatype, functions to manipulate rasterized .mf
    * fonts (see gfuntype.c which rasterizes .mf fonts), functions
    * to parse LaTeX expressions, etc.  As already mentioned,
    * a complete list of mimetex.c functions is above.  See their
    * individual entry points below for further comments.
    *   As also mentioned, these functions eventually belong in
    * several different modules, possibly along the lines suggested
    * by the divisions above.  But until the best decomposition
    * becomes clear, it seems better to keep mimetex.c
    * neatly together, avoiding a bad decomposition that
    * becomes permanent by default.
  *      o Optional compile-line -D defined symbols are documented   *      o Optional compile-line -D defined symbols are documented
  * in mimetex.html#options .  They include (additional -D   * in mimetex.html#options .  They include (additional -D
  * switches are discussed in mimetex.html#options)...   * switches are discussed at mimetex.html#options)...
  * -DAA   * -DAA
  *    Turns on gif anti-aliasing with default values   *    Turns on gif anti-aliasing with default values
  *    (CENTERWT=32, ADJACENTWT=3, CORNERWT=1)   *    (CENTERWT=32, ADJACENTWT=3, CORNERWT=1)
Line 233 Line 289
  * -DCENTERWT=n   * -DCENTERWT=n
  * -DADJACENTWT=j   * -DADJACENTWT=j
  * -DCORNERWT=k   * -DCORNERWT=k
    * *** Note: Ignore these three switches because
    * *** mimeTeX's current anti-aliasing algorithm
    * *** no longer uses them (as of version 1.60).
  *    MimeTeX currently provides a lowpass filtering   *    MimeTeX currently provides a lowpass filtering
  *    algorithm for anti-aliasing, which is applied to the   *    algorithm for anti-aliasing, which is applied to the
  *    existing set of bitmap fonts.  This lowpass filter   *    existing set of bitmap fonts.  This lowpass filter
Line 262 Line 321
  *    be writable by it.  Files created under  path/  are   *    be writable by it.  Files created under  path/  are
  *    named filename.gif, where filename is the 32-character   *    named filename.gif, where filename is the 32-character
  *    MD5 hash of the LaTeX expression.   *    MD5 hash of the LaTeX expression.
    * -DDEFAULTSIZE=n
    *    MimeTeX currently has eight font sizes numbered 0-7,
    *    and always starts in DEFAULTSIZE whose default value
    *    is 3 (corresponding to \large). Specify -DDEFAULTSIZE=4
    *    on the compile line if you prefer mimeTeX to start in
    *    larger default size 4 (corresponding to \Large), etc.
  * -DDISPLAYSIZE=n   * -DDISPLAYSIZE=n
  *    By default, operator limits like \int_a^b are rendered   *    By default, operator limits like \int_a^b are rendered
  *    \textstyle at font sizes \normalsize and smaller,   *    \textstyle at font sizes \normalsize and smaller,
Line 273 Line 338
  *    \textstyle, \displaystyle, \limits or \nolimits   *    \textstyle, \displaystyle, \limits or \nolimits
  *    directives in an expression always override   *    directives in an expression always override
  *    the DISPLAYSIZE default.   *    the DISPLAYSIZE default.
  * -NORMALSIZE=n   * -DERRORSTATUS=n
  *    MimeTeX currently has six font sizes numbered 0-5,   *    The default, 0, means mimeTeX always exits with status 0,
  *    and always starts in NORMALSIZE whose default value   *    regardless of whether or not it detects error(s) while
  *    is 2.  Specify -DNORMALSIZE=3 on the compile line if   *    trying to render your expression.  Specify any non-zero
  *    you prefer mimeTeX to start in default size 3, etc.   *    value (typically -1) if you write a script/plugin for
    *    mimeTeX that traps non-zero exit statuses.  MimeTeX then
    *    exits with its own non-zero status when it detects an
    *    error it can identify, or with your ERRORSTATUS value
    *    for errors it can't specifically identify.
  * -DREFERER=\"domain\"   -or-   * -DREFERER=\"domain\"   -or-
  * -DREFERER=\"domain1,domain2,etc\"   * -DREFERER=\"domain1,domain2,etc\"
  *    Blocks mimeTeX requests from unauthorized domains that   *    Blocks mimeTeX requests from unauthorized domains that
Line 308 Line 377
  *    MimeTeX usually renders black symbols on a white   *    MimeTeX usually renders black symbols on a white
  *    background.  This option renders white symbols on   *    background.  This option renders white symbols on
  *    a black background instead.   *    a black background instead.
  *      o See individual function entry points for further comments.  
  *      o The font information in texfonts.h was produced by multiple  
  * runs of gfuntype, one run per struct (i.e., one run per font  
  * family at a particular size).  See gfuntype.c, and also  
  * mimetex.html#fonts, for details.  
  *      o mimetex.c contains library functions implementing a raster  
  * datatype, functions to manipulate rasterized .mf fonts  
  * (see gfuntype.c which rasterizes .mf fonts), functions  
  * to parse LaTeX expressions, etc.  A complete list of  
  * mimetex.c functions is above.  See their individual entry  
  * points below for further comments.  
  *   All these functions eventually belong in several  
  * different modules, possibly along the lines suggested  
  * by the divisions above.  But until the best decomposition  
  * becomes clear, it seems better to keep mimetex.c  
  * neatly together, avoiding a bad decomposition that  
  * becomes permanent by default.  
  *      o The "main" reusable function is rasterize(),  
  * which takes a string like "f(x)=\int_{-\infty}^xe^{-t^2}dt"  
  * and returns a (sub)raster representing it as a bit or bytemap.  
  * Your application can do anything it likes with this pixel map.  
  * MimeTeX just outputs it, either as a mime xbitmap or as a gif.  
  * --------------------------------------------------------------------------   * --------------------------------------------------------------------------
  * Revision History:   * Revision History:
  * 09/18/02 J.Forkosh Installation.   * 09/18/02 J.Forkosh Installation.
Line 343 Line 390
  * 10/11/05 J.Forkosh Version 1.64 released.   * 10/11/05 J.Forkosh Version 1.64 released.
  * 11/30/06 J.Forkosh Version 1.65 released.   * 11/30/06 J.Forkosh Version 1.65 released.
  * 09/06/08 J.Forkosh Version 1.70 released.   * 09/06/08 J.Forkosh Version 1.70 released.
    * 03/23/09 J.Forkosh Version 1.71 released.
    * 11/18/09 J.Forkosh Version 1.72 released.
    * 11/15/11 J.Forkosh Version 1.73 released.
    * 02/15/12 J.Forkosh Version 1.74 released.
    * 03/31/12 J.Forkosh Most recent revision (also see REVISIONDATE)
    * See  http://www.forkosh.com/mimetexchangelog.html  for further details.
  *   *
  ****************************************************************************/   ****************************************************************************/
   
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
   Program id
   -------------------------------------------------------------------------- */
   #define VERSION "1.74" /* mimeTeX version number */
   #define REVISIONDATE "31 March 2012" /* date of most recent revision */
   #define COPYRIGHTTEXT "Copyright(c) 2002-2012, John Forkosh Associates, Inc"
   
   /* -------------------------------------------------------------------------
 header files and macros  header files and macros
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 /* --- standard headers --- */  /* --- standard headers --- */
Line 357  header files and macros Line 417  header files and macros
 #include <ctype.h>  #include <ctype.h>
 #include <math.h>  #include <math.h>
 #include <time.h>  #include <time.h>
   extern char **environ; /* for \environment directive */
   
   /* -------------------------------------------------------------------------
   messages (used mostly by main() and also by rastmessage())
   -------------------------------------------------------------------------- */
   static char *copyright1 = /* copyright, gnu/gpl notice */
    "+-----------------------------------------------------------------------+\n"
    "|mimeTeX vers " VERSION ", " COPYRIGHTTEXT                             "|\n"
    "+-----------------------------------------------------------------------+\n"
    "| mimeTeX is free software, licensed to you under terms of the GNU/GPL, |\n"
    "|           and comes with absolutely no warranty whatsoever.           |",
   *copyright2 =
    "|          See http://www.forkosh.com/mimetex.html for details.         |\n"
    "+-----------------------------------------------------------------------+";
   static int maxmsgnum = 3, /* maximum msgtable[] index */
    /* --- keep these message numbers updated if table changes --- */
    invmsgnum = 0, /* general invalid message */
    refmsgnum = 3; /* urlncmp() failed to validate */
   static char *msgtable[] = { /* messages referenced by [index] */
    "\\red\\small\\rm\\fbox{\\array{" /* [0] is invalid_referer_msg */
      "Please~read~www.forkosh.com/mimetex.html\\\\and~install~mimetex.cgi~"
      "on~your~own~server.\\\\Thank~you,~John~Forkosh}}",
    "\\red\\small\\rm\\fbox{\\array{" /* [1] */
      "Please~provide~your~{\\tiny~HTTP-REFERER}~to~access~the~public\\\\"
      "mimetex~server.~~Or~please~read~~www.forkosh.com/mimetex.html\\\\"
      "and~install~mimetex.cgi~on~your~own~server.~~Thank~you,~John~Forkosh}}",
    "\\red\\small\\rm\\fbox{\\array{" /* [2] */
      "The~public~mimetex~server~is~for~testing.~~For~production,\\\\"
      "please~read~~www.forkosh.com/mimetex.html~~and~install\\\\"
      "mimetex.cgi~on~your~own~server.~~Thank~you,~John~Forkosh}}",
    "\\red\\small\\rm\\fbox{\\array{" /* [3] */
      "Only~SERVER_NAME~may~use~mimetex~on~this~server.\\\\"
      "Please~read~~www.forkosh.com/mimetex.html~~and~install\\\\"
      "mimetex.cgi~on~your~own~server.~~Thank~you,~John~Forkosh}}",
    NULL } ; /* trailer */
   
 /* --- windows-specific header info --- */  /* -------------------------------------------------------------------------
   additional symbols
   -------------------------------------------------------------------------- */
   /* ---
    * windows-specific header info
    * ---------------------------- */
 #ifndef WINDOWS /* -DWINDOWS not supplied by user */  #ifndef WINDOWS /* -DWINDOWS not supplied by user */
   #if defined(_WINDOWS) || defined(_WIN32) || defined(WIN32) \    #if defined(_WINDOWS) || defined(_WIN32) || defined(WIN32) \
   ||  defined(DJGPP) /* try to recognize windows compilers */ \    ||  defined(DJGPP) /* try to recognize windows compilers */ \
Line 388  header files and macros Line 488  header files and macros
   #define ISWINDOWS 0    #define ISWINDOWS 0
 #endif  #endif
   
 /* --- check for supersampling or low-pass anti-aliasing --- */  /* ---
    * check for supersampling or low-pass anti-aliasing
    * ------------------------------------------------- */
 #ifdef SS  #ifdef SS
   #define ISSUPERSAMPLING 1    #define ISSUPERSAMPLING 1
   #ifndef AAALGORITHM    #ifndef AAALGORITHM
Line 410  header files and macros Line 512  header files and macros
   #define MAXFOLLOW 8 /* aafollowline() maxturn default */    #define MAXFOLLOW 8 /* aafollowline() maxturn default */
 #endif  #endif
   
 /* --- set aa (and default gif) if any anti-aliasing options specified --- */  /* ---
    * set aa (and default gif) if any anti-aliasing options specified
    * --------------------------------------------------------------- */
 #if defined(AA) || defined(GIF) || defined(PNG) \  #if defined(AA) || defined(GIF) || defined(PNG) \
 ||  defined(CENTERWT) || defined(ADJACENTWT) || defined(CORNERWT) \  ||  defined(CENTERWT) || defined(ADJACENTWT) || defined(CORNERWT) \
 ||  defined(MINADJACENT) || defined(MAXADJACENT)  ||  defined(MINADJACENT) || defined(MAXADJACENT)
Line 434  header files and macros Line 538  header files and macros
   #endif    #endif
 #endif  #endif
   
 /* --- decide whether to compile main() --- */  /* ---
    * decide whether or not to compile main()
    * --------------------------------------- */
 #if defined(XBITMAP) || defined(GIF) || defined(PNG)  #if defined(XBITMAP) || defined(GIF) || defined(PNG)
   #define DRIVER /* driver will be compiled */    /* --- yes, compile main() --- */
   /* --- check whether or not to perform http_referer check --- */    #define DRIVER /* main() driver will be compiled */
   #ifndef REFERER /* all http_referer's allowed */  #else /* --- main() won't be compiled (e.g., for gfuntype.c) --- */
     #define REFERER NULL  
   #endif  
   /* --- max query_string length if no http_referer supplied --- */  
   #ifndef NOREFMAXLEN  
     #define NOREFMAXLEN 9999 /* default to any length query */  
   #endif  
 #else  
   #ifndef TEXFONTS    #ifndef TEXFONTS
     #define NOTEXFONTS /* texfonts not required */      #define NOTEXFONTS /* texfonts not required */
   #endif    #endif
 #endif  #endif
   
 /* --- application headers --- */  /* ---
    * application headers
    * ------------------- */
 #if !defined(NOTEXFONTS) && !defined(TEXFONTS)  #if !defined(NOTEXFONTS) && !defined(TEXFONTS)
   #define TEXFONTS /* to include texfonts.h */    #define TEXFONTS /* to include texfonts.h */
 #endif  #endif
 #include "mimetex.h"  #include "mimetex.h"
 /* --- info needed when gif image returned in memory buffer --- */  
   /* ---
    * info needed when gif image returned in memory buffer
    * ---------------------------------------------------- */
 #ifdef GIF /* compiling along with gifsave.c */  #ifdef GIF /* compiling along with gifsave.c */
   extern int gifSize;    extern int gifSize;
   extern int maxgifSize;    extern int maxgifSize;
Line 476  header files and macros Line 580  header files and macros
 #else  #else
   #define ISTRANSPARENT 0    #define ISTRANSPARENT 0
 #endif  #endif
 /* --- internal buffer sizes --- */  
   /* ---
    * internal buffer sizes
    * --------------------- */
 #if !defined(MAXEXPRSZ)  #if !defined(MAXEXPRSZ)
   #define MAXEXPRSZ (32768-1) /*max #bytes in input tex expression*/    #define MAXEXPRSZ (32768-1) /*max #bytes in input tex expression*/
 #endif  #endif
Line 499  header files and macros Line 606  header files and macros
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 adjustable default values  adjustable default values
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 /* --- anti-aliasing parameters --- */  /* ---
    * anti-aliasing parameters
    * ------------------------ */
 #ifndef CENTERWT  #ifndef CENTERWT
   /*#define CENTERWT 32*/ /* anti-aliasing centerwt default */    /*#define CENTERWT 32*/ /* anti-aliasing centerwt default */
   /*#define CENTERWT 10*/ /* anti-aliasing centerwt default */    /*#define CENTERWT 10*/ /* anti-aliasing centerwt default */
Line 574  other variables Line 683  other variables
 #ifndef FGBLUE  #ifndef FGBLUE
   #define FGBLUE  (ISBLACKONWHITE?0:255)    #define FGBLUE  (ISBLACKONWHITE?0:255)
 #endif  #endif
   /* --- advertisement
      one image in every ADFREQUENCY is wrapped in "advertisement" --- */
   #if !defined(ADFREQUENCY)
     #define ADFREQUENCY 0 /* never show advertisement if 0 */
   #endif
   #ifndef HOST_SHOWAD
     #define HOST_SHOWAD "\000" /* show ads on all hosts */
   #endif
 /* --- "smash" margin (0 means no smashing) --- */  /* --- "smash" margin (0 means no smashing) --- */
 #ifndef SMASHMARGIN  #ifndef SMASHMARGIN
   #ifdef NOSMASH    #ifdef NOSMASH
Line 615  other variables Line 732  other variables
 #if !defined(NODUMPENVP) && !defined(DUMPENVP)  #if !defined(NODUMPENVP) && !defined(DUMPENVP)
   #define DUMPENVP /* assume char *envp[] available */    #define DUMPENVP /* assume char *envp[] available */
 #endif  #endif
   /* --- max query_string length if no http_referer supplied --- */
   #ifndef NOREFMAXLEN
     #define NOREFMAXLEN 9999 /* default to any length query */
   #endif
   #ifndef NOREFSAFELEN
     #define NOREFSAFELEN 24 /* too small for hack exploit */
   #endif
   /* --- check whether or not to perform http_referer check --- */
   #ifdef REFERER /* only specified referers allowed */
     #undef NOREFMAXLEN
     #define NOREFMAXLEN NOREFSAFELEN
   #else /* all http_referer's allowed */
     #define REFERER NULL
   #endif
   /* --- check top levels of http_referer against server_name --- */
   #ifdef REFLEVELS /* #topmost levels to check */
     #undef NOREFMAXLEN
     #define NOREFMAXLEN NOREFSAFELEN
   #else
     #ifdef NOREFCHECK
       #define REFLEVELS 0 /* don't match host and referer */
     #else
       #define REFLEVELS 3 /* default matches abc.def.com */
     #endif
   #endif
   /* --- check whether or not \input, \counter, \environment permitted --- */
   #ifdef DEFAULTSECURITY /* default security specified */
     #define EXPLICITDEFSECURITY /* don't override explicit default */
   #else /* defualt security not specified */
     #define DEFAULTSECURITY (8) /* so set default security level */
   #endif
   #ifdef INPUTREFERER /*http_referer's permitted to \input*/
     #ifndef INPUTSECURITY /* so we need to permit \input{} */
       #define INPUTSECURITY (99999) /* make sure SECURITY<INPUTSECURITY */
     #endif
   #else /* no INPUTREFERER list supplied */
     #define INPUTREFERER NULL /* so init it as NULL pointer */
   #endif
   #ifndef INPUTPATH /* \input{} paths permitted for... */
     #define INPUTPATH NULL /* ...any referer */
   #endif
   #ifndef INPUTSECURITY /* \input{} security not specified */
     #ifdef INPUTOK /* but INPUTOK flag specified */
       #define INPUTSECURITY (99999) /* so enable \input{} */
       #ifndef EXPLICITDEFSECURITY /* don't override explicit default */
         #undef  DEFAULTSECURITY /* but we'll override our default */
         #define DEFAULTSECURITY (99999) /*let -DINPUTOK enable \counter,etc*/
       #endif
     #else /* else no \input{} specified */
       #define INPUTSECURITY DEFAULTSECURITY /* set default \input security */
     #endif
   #endif
   #ifndef COUNTERSECURITY /*\counter{} security not specified*/
     #ifdef COUNTEROK /* but COUNTEROK flag specified */
       #define COUNTERSECURITY (99999) /* so enable \counter{} */
     #else /* else no \counter{} specified */
       #define COUNTERSECURITY DEFAULTSECURITY /*set default \counter security*/
     #endif
   #endif
   #ifndef ENVIRONSECURITY /* \environ security not specified */
     #ifdef ENVIRONOK /* but ENVIRONOK flag specified */
       #define ENVIRONSECURITY (99999) /* so enable \environ */
     #else /* else no \environ specified */
       #define ENVIRONSECURITY DEFAULTSECURITY /*set default \environ security*/
     #endif
   #endif
 /* --- image caching (cache images if given -DCACHEPATH=\"path\") --- */  /* --- image caching (cache images if given -DCACHEPATH=\"path\") --- */
 #ifndef CACHEPATH  #ifndef CACHEPATH
   #define ISCACHING 0 /* no caching */    #define ISCACHING 0 /* no caching */
Line 653  debugging and logging / error reporting Line 836  debugging and logging / error reporting
 #ifndef FORMLEVEL  #ifndef FORMLEVEL
   #define FORMLEVEL LOGLEVEL /*msglevel if called from html form*/    #define FORMLEVEL LOGLEVEL /*msglevel if called from html form*/
 #endif  #endif
   #ifndef ERRORSTATUS /* exit(ERRORSTATUS) for any error */
     #define ERRORSTATUS 0 /* default doesn't signal errors */
   #endif
 GLOBAL(int,seclevel,SECURITY); /* security level */  GLOBAL(int,seclevel,SECURITY); /* security level */
   GLOBAL(int,inputseclevel,INPUTSECURITY); /* \input{} security level */
   GLOBAL(int,counterseclevel,COUNTERSECURITY); /* \counter{} security level */
   GLOBAL(int,environseclevel,ENVIRONSECURITY); /* \environ{} security level */
 GLOBAL(int,msglevel,MSGLEVEL); /* message level for verbose/debug */  GLOBAL(int,msglevel,MSGLEVEL); /* message level for verbose/debug */
   GLOBAL(int,errorstatus,ERRORSTATUS); /* exit status if error encountered*/
   GLOBAL(int,exitstatus,0); /* exit status (0=success) */
 STATIC FILE *msgfp; /* output in command-line mode */  STATIC FILE *msgfp; /* output in command-line mode */
 /* --- embed warnings in rendered expressions, [\xxx?] if \xxx unknown --- */  /* --- embed warnings in rendered expressions, [\xxx?] if \xxx unknown --- */
 #ifdef WARNINGS  #ifdef WARNINGS
Line 671  GLOBAL(int,warninglevel,WARNINGLEVEL); / Line 862  GLOBAL(int,warninglevel,WARNINGLEVEL); /
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 control flags and values  control flags and values
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
   GLOBAL(int,daemonlevel,0); /* incremented in main() */
 GLOBAL(int,recurlevel,0); /* inc/decremented in rasterize() */  GLOBAL(int,recurlevel,0); /* inc/decremented in rasterize() */
 GLOBAL(int,scriptlevel,0); /* inc/decremented in rastlimits() */  GLOBAL(int,scriptlevel,0); /* inc/decremented in rastlimits() */
 GLOBAL(int,isstring,0); /*pixmap is ascii string, not raster*/  GLOBAL(int,isstring,0); /*pixmap is ascii string, not raster*/
Line 679  GLOBAL(char,*subexprptr,(char *)NULL); / Line 871  GLOBAL(char,*subexprptr,(char *)NULL); /
 /*SHARED(int,imageformat,1);*/ /* image is 1=bitmap, 2=.gf-like */  /*SHARED(int,imageformat,1);*/ /* image is 1=bitmap, 2=.gf-like */
 GLOBAL(int,isdisplaystyle,1); /* displaystyle mode (forced if 2) */  GLOBAL(int,isdisplaystyle,1); /* displaystyle mode (forced if 2) */
 GLOBAL(int,ispreambledollars,0); /* displaystyle mode set by $$...$$ */  GLOBAL(int,ispreambledollars,0); /* displaystyle mode set by $$...$$ */
   GLOBAL(int,ninputcmds,0); /* # of \input commands processed */
 GLOBAL(int,fontnum,0); /* cal=1,scr=2,rm=3,it=4,bb=5,bf=6 */  GLOBAL(int,fontnum,0); /* cal=1,scr=2,rm=3,it=4,bb=5,bf=6 */
 GLOBAL(int,fontsize,NORMALSIZE); /* current size */  GLOBAL(int,fontsize,NORMALSIZE); /* current size */
   GLOBAL(int,magstep,1); /* magstep (1=no change) */
 GLOBAL(int,displaysize,DISPLAYSIZE); /* use \displaystyle when fontsize>=*/  GLOBAL(int,displaysize,DISPLAYSIZE); /* use \displaystyle when fontsize>=*/
 GLOBAL(int,shrinkfactor,3); /* shrinkfactors[fontsize] */  GLOBAL(int,shrinkfactor,3); /* shrinkfactors[fontsize] */
   GLOBAL(int,rastlift,0); /* rastraise() lift parameter */
   GLOBAL(int,rastlift1,0); /* rastraise() lift for base exprssn*/
 GLOBAL(double,unitlength,1.0); /* #pixels per unit (may be <1.0) */  GLOBAL(double,unitlength,1.0); /* #pixels per unit (may be <1.0) */
   GLOBAL(int,iunitlength,1); /* #pixels per unit as int for store*/
 /*GLOBAL(int,textwidth,TEXTWIDTH);*/ /* #pixels across line */  /*GLOBAL(int,textwidth,TEXTWIDTH);*/ /* #pixels across line */
   GLOBAL(int,adfrequency,ADFREQUENCY); /* advertisement frequency */
 GLOBAL(int,isnocatspace,0); /* >0 to not add space in rastcat()*/  GLOBAL(int,isnocatspace,0); /* >0 to not add space in rastcat()*/
 GLOBAL(int,smashmargin,SMASHMARGIN); /* minimum "smash" margin */  GLOBAL(int,smashmargin,SMASHMARGIN); /* minimum "smash" margin */
 GLOBAL(int,mathsmashmargin,SMASHMARGIN); /* needed for \text{if $n-m$ even}*/  GLOBAL(int,mathsmashmargin,SMASHMARGIN); /* needed for \text{if $n-m$ even}*/
 GLOBAL(int,issmashdelta,1); /* true if smashmargin is a delta */  GLOBAL(int,issmashdelta,1); /* true if smashmargin is a delta */
 GLOBAL(int,isexplicitsmash,0); /* true if \smash explicitly given */  GLOBAL(int,isexplicitsmash,0); /* true if \smash explicitly given */
 GLOBAL(int,smashcheck,SMASHCHECK); /* check if terms safe to smash */  GLOBAL(int,smashcheck,SMASHCHECK); /* check if terms safe to smash */
   GLOBAL(int,isnomath,0); /* true to inhibit math mode */
 GLOBAL(int,isscripted,0); /* is (lefthand) term text-scripted*/  GLOBAL(int,isscripted,0); /* is (lefthand) term text-scripted*/
 GLOBAL(int,isdelimscript,0); /* is \right delim text-scripted */  GLOBAL(int,isdelimscript,0); /* is \right delim text-scripted */
 GLOBAL(int,issmashokay,0); /*is leading char okay for smashing*/  GLOBAL(int,issmashokay,0); /*is leading char okay for smashing*/
Line 732  GLOBAL(char,pathprefix[256],PATHPREFIX); Line 931  GLOBAL(char,pathprefix[256],PATHPREFIX);
 /*GLOBAL(int,iswindows,ISWINDOWS);*/ /* true if compiled for ms windows */  /*GLOBAL(int,iswindows,ISWINDOWS);*/ /* true if compiled for ms windows */
   
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
   store for evalterm() [n.b., these are stripped-down funcs from nutshell]
   -------------------------------------------------------------------------- */
   #define STORE struct store_struct /* "typedef" for store struct */
   #define MAXSTORE 100 /* max 100 identifiers */
   STORE {
     char *identifier; /* identifier */
     int *value; /* address of corresponding value */
     } ; /* --- end-of-store_struct --- */
   static STORE mimestore[MAXSTORE] = {
       { "fontsize", &fontsize }, { "fs", &fontsize }, /* font size */
       { "fontnum", &fontnum }, { "fn", &fontnum }, /* font number */
       { "unitlength", &iunitlength }, /* unitlength */
       /*{ "mytestvar", &mytestvar },*/
       { NULL, NULL } /* end-of-store */
     } ; /* --- end-of-mimestore[] --- */
   
   /* -------------------------------------------------------------------------
 miscellaneous macros  miscellaneous macros
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
   #if 0 /* --- these are now #define'd in mimetex.h --- */
 #define max2(x,y)  ((x)>(y)? (x):(y)) /* larger of 2 arguments */  #define max2(x,y)  ((x)>(y)? (x):(y)) /* larger of 2 arguments */
 #define min2(x,y)  ((x)<(y)? (x):(y)) /* smaller of 2 arguments */  #define min2(x,y)  ((x)<(y)? (x):(y)) /* smaller of 2 arguments */
 #define max3(x,y,z) max2(max2(x,y),(z)) /* largest of 3 arguments */  #define max3(x,y,z) max2(max2(x,y),(z)) /* largest of 3 arguments */
Line 741  miscellaneous macros Line 958  miscellaneous macros
 #define absval(x)  ((x)>=0?(x):(-(x))) /* absolute value */  #define absval(x)  ((x)>=0?(x):(-(x))) /* absolute value */
 #define iround(x)  ((int)((x)>=0?(x)+0.5:(x)-0.5)) /* round double to int */  #define iround(x)  ((int)((x)>=0?(x)+0.5:(x)-0.5)) /* round double to int */
 #define dmod(x,y)  ((x)-((y)*((double)((int)((x)/(y)))))) /*x%y for doubles*/  #define dmod(x,y)  ((x)-((y)*((double)((int)((x)/(y)))))) /*x%y for doubles*/
   #endif
 #define compress(s,c) if((s)!=NULL) /* remove embedded c's from s */ \  #define compress(s,c) if((s)!=NULL) /* remove embedded c's from s */ \
  { char *p; while((p=strchr((s),(c)))!=NULL) strcpy(p,p+1); } else   { char *p; while((p=strchr((s),(c)))!=NULL) {strsqueeze(p,1);} } else
 #define slower(s)  if ((s)!=NULL) /* lowercase all chars in s */ \  #define slower(s)  if ((s)!=NULL) /* lowercase all chars in s */ \
  { char *p=(s); while(*p!='\000'){*p=tolower(*p); p++;} } else   { char *p=(s); while(*p!='\000'){*p=tolower(*p); p++;} } else
 /*subraster *subrastcpy();*/ /* need global module declaration */  /*subraster *subrastcpy();*/ /* need global module declaration */
 /*#define spnosmash(sp) if (sp->type==CHARASTER) sp=subrastcpy(sp); \*/  /*#define spnosmash(sp) if (sp->type==CHARASTER) sp=subrastcpy(sp); \ */
 /* sp->type=blanksignal*/  /* sp->type=blanksignal */
   /* ---evaluate \directive[arg] or \directive{arg} scaled by unitlength--- */
   #define eround(arg) (iround(unitlength*((double)evalterm(mimestore,(arg)))))
   /* --- check if a string is empty --- */
   #define isempty(s)  ((s)==NULL?1:(*(s)=='\000'?1:0))
   /* --- last char of a string --- */
   #define lastchar(s) (isempty(s)?'\000':*((s)+(strlen(s)-1)))
   /* --- lowercase a string --- */
   #define strlower(s) strnlower((s),0) /* lowercase an entire string */
   /* --- strip leading and trailing whitespace (including ~) --- */
   #define trimwhite(thisstr) if ( (thisstr) != NULL ) { \
    int thislen = strlen(thisstr); \
    while ( --thislen >= 0 ) \
     if ( isthischar((thisstr)[thislen]," \t\n\r\f\v") ) \
       (thisstr)[thislen] = '\000'; \
     else break; \
    if ( (thislen = strspn((thisstr)," \t\n\r\f\v")) > 0 ) \
     {strsqueeze((thisstr),thislen);} } else
   /* --- strncpy() n bytes and make sure it's null-terminated --- */
   #define strninit(target,source,n) if( (target)!=NULL && (n)>=0 ) { \
     char *thissource = (source); \
     (target)[0] = '\000'; \
     if ( (n)>0 && thissource!=NULL ) { \
       strncpy((target),thissource,(n)); \
       (target)[(n)] = '\000'; } }
   /* --- strcpy(s,s+n) using memmove() (also works for negative n) --- */
   #define strsqueeze(s,n) if((n)!=0) { if(!isempty((s))) { \
    int thislen3=strlen(s); \
    if ((n) >= thislen3) *(s) = '\000'; \
    else memmove(s,s+(n),1+thislen3-(n)); }} else/*user supplies final;*/
   /* --- strsqueeze(s,t) with two pointers --- */
   #define strsqueezep(s,t) if(!isempty((s))&&!isempty((t))) { \
    int sqlen=strlen((s))-strlen((t)); \
    if (sqlen>0 && sqlen<=999) {strsqueeze((s),sqlen);} } else
   
 /* ---  /* ---
  * PART2   * PART2
Line 1139  return ( rotated );   /* return rotated Line 1390  return ( rotated );   /* return rotated
   
   
 /* ==========================================================================  /* ==========================================================================
    * Function: rastmag ( rp, magstep )
    * Purpose: magnifies rp by integer magstep,
    * e.g., double-height and double-width if magstep=2
    * --------------------------------------------------------------------------
    * Arguments: rp (I) ptr to raster struct to be "magnified"
    * magstep (I) int containing magnification scale,
    * e.g., 2 to double the width and height of rp
    * --------------------------------------------------------------------------
    * Returns: ( raster * ) ptr to new raster magnified relative to rp,
    * or NULL for any error.
    * --------------------------------------------------------------------------
    * Notes:     o
    * ======================================================================= */
   /* --- entry point --- */
   raster *rastmag ( raster *rp, int magstep )
   {
   /* -------------------------------------------------------------------------
   Allocations and Declarations
   -------------------------------------------------------------------------- */
   raster *new_raster(), *magnified=NULL; /* magnified raster back to caller */
   int height = rp->height, irow, /* height, row index */
    width = rp->width, icol, /* width, column index */
    mrow = 0, mcol = 0, /* dup pixels magstep*magstep times*/
    pixsz = rp->pixsz; /* #bits per pixel */
   /* -------------------------------------------------------------------------
   check args
   -------------------------------------------------------------------------- */
   if ( rp == NULL ) goto end_of_job; /* no input raster supplied */
   if ( magstep<1 || magstep>10 ) goto end_of_job; /* sanity check */
   /* -------------------------------------------------------------------------
   allocate magnified raster and fill it
   -------------------------------------------------------------------------- */
   /* --- allocate magnified raster with magstep*width, magstep*height --- */
   if ( (magnified = new_raster(magstep*width,magstep*height,pixsz))/*allocate*/
   !=   NULL ) /* check that allocation succeeded */
     /* --- fill reflected raster --- */
     for ( irow=0; irow<height; irow++ ) /* for each row of rp */
       for ( mrow=0; mrow<magstep; mrow++ ) /* dup row magstep times */
         for ( icol=0; icol<width; icol++ ) /* and for each column of rp */
           for ( mcol=0; mcol<magstep; mcol++ ) { /* dup col magstep times */
            int value = getpixel(rp,irow,icol);
    int row1 = irow*magstep, col1 = icol*magstep;
            setpixel(magnified,(row1+mrow),(col1+mcol),value); }
   end_of_job:
     return ( magnified ); /*return magnified raster to caller*/
   } /* --- end-of-function rastmag() --- */
   
   
   /* ==========================================================================
    * Function: bytemapmag ( bytemap, width, height, magstep )
    * Purpose: magnifies a bytemap by integer magstep,
    * e.g., double-height and double-width if magstep=2
    * --------------------------------------------------------------------------
    * Arguments: bytemap (I) intbyte * ptr to byte map to be "magnified"
    * width (I) int containing #cols in original bytemap
    * height (I) int containing #rows in original bytemap
    * magstep (I) int containing magnification scale,
    * e.g., 2 to double the width and height of rp
    * --------------------------------------------------------------------------
    * Returns: ( intbyte * ) ptr to new bytemap magnified relative to
    * original bytemap, or NULL for any error.
    * --------------------------------------------------------------------------
    * Notes:     o Apply EPX/Scale2x/AdvMAME2x  for magstep 2,
    * and Scale3x/AdvMAME3x  for magstep 3,
    * as described by http://en.wikipedia.org/wiki/2xSaI
    * ======================================================================= */
   /* --- entry point --- */
   intbyte *bytemapmag ( intbyte *bytemap, int width, int height, int magstep )
   {
   /* -------------------------------------------------------------------------
   Allocations and Declarations
   -------------------------------------------------------------------------- */
   intbyte *magnified=NULL; /* magnified bytemap back to caller*/
   int irow, icol, /* original height, width indexes */
    mrow=0, mcol=0; /* dup bytes magstep*magstep times */
   int imap = (-1), /* original bytemap[] index */
    byteval = 0; /* byteval=bytemap[imap] */
   int isAdvMAME = 1; /* true to apply AdvMAME2x and 3x */
   int icell[10], /* bytemap[] nearest neighbors */
    bmmdiff = 64; /* nearest neighbor diff allowed */
   #define bmmeq(i,j) ((absval((icell[i]-icell[j]))<=bmmdiff)) /*approx equal*/
   /* -------------------------------------------------------------------------
   check args
   -------------------------------------------------------------------------- */
   if ( bytemap == NULL ) goto end_of_job; /* no input bytemap supplied */
   if ( width<1 || height<1 ) goto end_of_job; /* invalid bytemap dimensions */
   if ( width*height>100000 ) goto end_of_job; /* sanity check */
   if ( magstep<1 || magstep>10 ) goto end_of_job; /* sanity check */
   /* -------------------------------------------------------------------------
   allocate magnified bytemap and fill it
   -------------------------------------------------------------------------- */
   /* --- allocate bytemap for magstep*width, magstep*height --- */
   if ( (magnified = (intbyte *)(malloc(magstep*width*magstep*height)))/*alloc*/
   !=   NULL ) /* check that allocation succeeded */
     /* --- fill reflected raster --- */
     for ( irow=0; irow<height; irow++ ) /* for each row of bytemap */
      for ( icol=0; icol<width; icol++ ) { /* and for each column of bytemap */
       int imag1 = (icol + irow*(width*magstep))*magstep; /*upper-left corner*/
       imap++; /* bump bytemap[] index */
       byteval = (int)(bytemap[imap]); /* grayscale value at this pixel */
       for ( mrow=0; mrow<magstep; mrow++ ) /* dup row magstep times */
        for ( mcol=0; mcol<magstep; mcol++ ) { /* dup col magstep times */
         int idup = mcol + mrow*(width*magstep); /* offset from imag1 */
         int imag = imag1+idup; /* adjust magnified[imag] */
         magnified[imag] = (intbyte)(byteval);
         /* --- apply AdvMAME2x and 3x (if desired) --- */
         if ( isAdvMAME ) { /* AdvMAME2x and 3x wanted */
          int mcell = 1 + mcol + magstep*mrow; /*1,2,3,4 or 1,2,3,4,5,6,7,8,9*/
          icell[5]= byteval, /* center cell of 3x3 bytemap[] */
          icell[4]= (icol>0?(int)(bytemap[imap-1]):byteval), /*left of center*/
          icell[6]= (icol<width?(int)(bytemap[imap+1]):byteval), /*right*/
          icell[2]= (irow>0?(int)(bytemap[imap-width]):byteval),/*above center*/
          icell[8]= (irow<height?(int)(bytemap[imap+width]):byteval), /*below*/
          icell[1]= (irow>0&&icol>0?(int)(bytemap[imap-width-1]):byteval),
          icell[3]= (irow>0&&icol<width?(int)(bytemap[imap-width+1]):byteval),
          icell[7]= (irow<height&&icol>0?(int)(bytemap[imap+width-1]):byteval),
         icell[9]=(irow<height&&icol<width?(int)(bytemap[imap+width+1]):byteval);
          switch ( magstep ) { /* 2x magstep=2, 3x magstep=3 */
           default: break; /* no AdvMAME at other magsteps */
           case 2: /* AdvMAME2x */
            if ( mcell == 1 )
              if ( bmmeq(4,2) && !bmmeq(4,8) && !bmmeq(2,6) )
                magnified[imag] = icell[2];
            if ( mcell == 2 )
              if ( bmmeq(2,6) && !bmmeq(2,4) && !bmmeq(6,8) )
                magnified[imag] = icell[6];
            if ( mcell == 4 )
              if ( bmmeq(6,8) && !bmmeq(6,2) && !bmmeq(8,4) )
                magnified[imag] = icell[8];
            if ( mcell == 3 )
              if ( bmmeq(8,4) && !bmmeq(8,6) && !bmmeq(4,2) )
                magnified[imag] = icell[4];
            break;
           case 3: /* AdvMAME3x */
            if ( mcell == 1 )
              if ( bmmeq(4,2) && !bmmeq(4,8) && !bmmeq(2,6) )
                magnified[imag] = icell[4];
            if ( mcell == 2 )
              if ( (bmmeq(4,2) && !bmmeq(4,8) && !bmmeq(2,6) && !bmmeq(5,3))
                || (bmmeq(2,6) && !bmmeq(2,4) && !bmmeq(6,8) && !bmmeq(5,1)) )
                magnified[imag] = icell[2];
            if ( mcell == 3 )
              if ( bmmeq(2,6) && !bmmeq(2,4) && !bmmeq(6,8) )
                magnified[imag] = icell[6];
            if ( mcell == 4 )
              if ( (bmmeq(8,4) && !bmmeq(8,6) && !bmmeq(4,2) && !bmmeq(5,1))
                || (bmmeq(4,2) && !bmmeq(4,8) && !bmmeq(2,6) && !bmmeq(5,7)) )
                magnified[imag] = icell[4];
            if ( mcell == 6 )
              if ( (bmmeq(2,6) && !bmmeq(2,4) && !bmmeq(6,8) && !bmmeq(5,9))
                || (bmmeq(6,8) && !bmmeq(6,2) && !bmmeq(8,4) && !bmmeq(5,3)) )
                magnified[imag] = icell[6];
            if ( mcell == 7 )
              if ( bmmeq(8,4) && !bmmeq(8,6) && !bmmeq(4,2) )
                magnified[imag] = icell[4];
            if ( mcell == 8 )
              if ( (bmmeq(6,8) && !bmmeq(6,2) && !bmmeq(8,4) && !bmmeq(5,7))
                || (bmmeq(8,4) && !bmmeq(8,6) && !bmmeq(4,2) && !bmmeq(5,9)) )
                magnified[imag] = icell[8];
            if ( mcell == 9 )
              if ( bmmeq(6,8) && !bmmeq(6,2) && !bmmeq(8,4) )
                magnified[imag] = icell[6];
            break;
           } } /* --- end-of-switch(magstep) --- */
         } /* --- end-of-for(mrow,mcol) --- */
       } /* --- end-of-for(irow,icol) --- */
   end_of_job:
     return ( magnified ); /*return magnified raster to caller*/
   } /* --- end-of-function bytemapmag() --- */
   
   
   /* ==========================================================================
  * Function: rastref ( rp, axis )   * Function: rastref ( rp, axis )
  * Purpose: reflects rp, horizontally about y-axis |_ becomes _| if axis=1   * Purpose: reflects rp, horizontally about y-axis |_ becomes _| if axis=1
  * or vertically about x-axis M becomes W if axis=2.   * or vertically about x-axis M becomes W if axis=2.
Line 1259  end_of_job: Line 1682  end_of_job:
  * to horizontally shift sp2 relative to sp1,   * to horizontally shift sp2 relative to sp1,
  * either positive (right) or negative   * either positive (right) or negative
  * isalign (I) int containing 1 to align baselines,   * isalign (I) int containing 1 to align baselines,
  * or 0 to vertically center sp2 over sp1   * or 0 to vertically center sp2 over sp1.
    * For isalign=2, images are vertically
    * centered, but then adjusted by \raisebox
    * lifts, using global variables rastlift1
    * for sp1 and rastlift for sp2.
  * isfree (I) int containing 1=free sp1 before return,   * isfree (I) int containing 1=free sp1 before return,
  * 2=free sp2, 3=free both, 0=free none.   * 2=free sp2, 3=free both, 0=free none.
  * --------------------------------------------------------------------------   * --------------------------------------------------------------------------
  * Returns: ( subraster * ) pointer to constructed subraster   * Returns: ( subraster * ) pointer to constructed subraster
  * or  NULL for any error   * or  NULL for any error
  * --------------------------------------------------------------------------   * --------------------------------------------------------------------------
  * Notes:   * Notes:     o The top-left corner of each raster box has coords (0,0),
    * down to (h-1,w-1) for a box of height h and width w.
    *      o A raster's baseline, b, is typically 0 <= b < h.
    * But b can actually go out-of-bounds, b>=h or b<0, for
    * an image additionally lifted (b>=h) or lowered (b<0)
    * with respect to the surrounding expression.
    *      o Note that b=h-1 means no descenders and the bottom
    * of the symbol rests exactly on the baseline,
    * whereas b=0 means the top pixel of the symbol rests
    * on the baseline, and all other pixels are descenders.
    *      o The composite raster is constructed as follows...
    * The base image is labelled height h1 and baseline b1,
    * the overlay h2 and b2, and the composite H and B.
    *     base       overlay
    *    --- +------------------------+ ---   For the overlay to be
    *     ^  |   ^        +----------+|  ^    vertically centered with
    *     |  |   |        |          ||  |    respect to the base,
    *     |  |   |B-b1    |          ||  |      B - b1 = H-B -(h1-b1), so
    *     |  |   v        |          ||  |      2*B = H-h1 + 2*b1
    *     |  |+----------+|          ||  |      B = b1 + (H-h1)/2
    *     B  ||  ^    ^  ||          ||  |    And when the base image is
    *     |  ||  |    |  ||          ||  |    bigger, H=h1 and B=b1 is
    *     |  ||  b1   |  ||          ||  |    the obvious correct answer.
    *     |  ||  |    h1 ||          || H=h2
    *     v  ||  v    |  ||          ||  |
    *    ----------||-------|--||          ||--|--------
    *    baseline  || h1-b1 v  || overlay  ||  |
    *    for base  |+----------+| baseline ||  |
    *    and com-  |   ^        | ignored  ||  |
    *    posite    |   |H-B-    |----------||  |
    * |   | (h1-b1)|          ||  |
    * |   v        +----------+|  v
    * +------------------------+ ---
  * ======================================================================= */   * ======================================================================= */
 /* --- entry point --- */  /* --- entry point --- */
 subraster *rastcompose ( subraster *sp1, subraster *sp2, int offset2,  subraster *rastcompose ( subraster *sp1, subraster *sp2, int offset2,
Line 1287  int base1   = sp1->baseline, /*baseline Line 1746  int base1   = sp1->baseline, /*baseline
  height2 = (sp2->image)->height, /* height for overlaid subraster */   height2 = (sp2->image)->height, /* height for overlaid subraster */
  width2  = (sp2->image)->width, /* width for overlaid subraster */   width2  = (sp2->image)->width, /* width for overlaid subraster */
  pixsz2  = (sp2->image)->pixsz; /* pixsz for overlaid subraster */   pixsz2  = (sp2->image)->pixsz; /* pixsz for overlaid subraster */
 int height=0, width=0, pixsz=0, base=0; /* overlaid composite */  int height  = max2(height1,height2), /*composite height if sp2 centered*/
    base    = base1 + (height-height1)/2, /* and composite baseline */
    tlc2    = (height-height2)/2, /* top-left corner for overlay */
    width=0, pixsz=0; /* other params for composite */
   int lift1   = rastlift1, /* vertical \raisebox lift for sp1 */
    lift2   = rastlift; /* vertical \raisebox lift for sp2 */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 Initialization  Initialization
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 /* --- determine height, width and baseline of composite raster --- */  /* --- determine height, width and baseline of composite raster --- */
 if ( isalign ) /* baselines of sp1,sp2 aligned */  switch ( isalign ) {
   { height = max2(base1+1,base2+1) /* max height above baseline */    default:
            + max2(height1-base1-1,height2-base2-1); /*+ max descending below*/    case 0: /* centered, baselines not aligned */
     base   = max2(base1,base2); } /* max space above baseline */      height = max2(height1,height2); /* max height */
 else /* baselines not aligned */      base   = base1 + (height-height1)/2; /* baseline for sp1 */
   { height = max2(height1,height2); /* max height */      break;
     base   = base1 + (height-height1)/2; } /* baseline for sp1 */    case 1: /* baselines of sp1,sp2 aligned */
 width      = max2(width1,width2+abs(offset2)); /* max width */      height = max2(base1+1,base2+1) /* max height above baseline */
 pixsz      = max2(pixsz1,pixsz2); /* bitmap,bytemap becomes bytemap */             + max2(height1-base1-1,height2-base2-1); /*+max descending below*/
       base   = max2(base1,base2); /* max space above baseline */
       break;
     case 2: /* centered +/- \raisebox lifts */
       base1 -= lift1;  base2 -= lift2; /* reset to unlifted images */
       /* --- start with default for centered, unlifted images --- */
       height2 += 2*absval(lift2); /* "virtual" height of overlay */
       height = max2(height1,height2); /* max height */
       base   = base1 + (height-height1)/2; /* baseline for sp1 */
       tlc2   = (height-height2)/2 /* top-left corner for overlay */
              + (lift2>=0?0:2*absval(lift2)); /* "reflect" overlay below base */
       break;
     } /* --- end-of-switch(isalign) --- */
   width = max2(width1,width2+abs(offset2)); /* max width */
   pixsz = max2(pixsz1,pixsz2); /* bitmap,bytemap becomes bytemap */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 allocate concatted composite subraster  allocate concatted composite subraster
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
Line 1311  if ( (sp=new_subraster(width,height,pixs Line 1789  if ( (sp=new_subraster(width,height,pixs
 sp->type = IMAGERASTER; /* image */  sp->type = IMAGERASTER; /* image */
 sp->baseline = base; /* composite baseline */  sp->baseline = base; /* composite baseline */
 sp->size = sp1->size; /* underlying char is sp1 */  sp->size = sp1->size; /* underlying char is sp1 */
   if ( isalign == 2 ) sp->baseline += lift1; /* adjust baseline */
 /* --- extract raster from subraster --- */  /* --- extract raster from subraster --- */
 rp = sp->image; /* raster allocated in subraster */  rp = sp->image; /* raster allocated in subraster */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 overlay sp1 and sp2 in new composite raster  overlay sp1 and sp2 in new composite raster
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 if ( isalign )  switch ( isalign ) {
   { rastput (rp, sp1->image, base-base1, (width-width1)/2, 1); /*underlying*/    default:
     rastput (rp, sp2->image, base-base2, /*overlaid*/    case 0: /* centered, baselines not aligned */
  (width-width2)/2+offset2, 0); }      rastput (rp, sp1->image, base-base1, (width-width1)/2, 1); /*underlying*/
 else  
   { rastput (rp, sp1->image, base-base1, (width-width1)/2, 1); /*underlying*/  
     rastput (rp, sp2->image, (height-height2)/2, /*overlaid*/      rastput (rp, sp2->image, (height-height2)/2, /*overlaid*/
  (width-width2)/2+offset2, 0); }   (width-width2)/2+offset2, 0);
       break;
     case 1: /* baselines of sp1,sp2 aligned */
       rastput (rp, sp1->image, base-base1, (width-width1)/2, 1); /*underlying*/
       rastput (rp, sp2->image, base-base2, /*overlaid*/
    (width-width2)/2+offset2, 0);
       break;
     case 2: if(1){ /* centered +/- \raisebox lifts */
       rastput (rp, sp1->image, base-base1, (width-width1)/2, 1);
       rastput (rp, sp2->image, tlc2, (width-width2)/2+offset2, 0); }
       break;
     } /* --- end-of-switch(isalign) --- */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 free input if requested  free input if requested
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
Line 1846  if ( smashcheck < 1 ) {  /* no smash che Line 2334  if ( smashcheck < 1 ) {  /* no smash che
 skip leading white and gray space  skip leading white and gray space
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 /* --- first check input --- */  /* --- first check input --- */
 if ( term == NULL )    goto end_of_job; /* no input so return 0 to caller */  if ( isempty(term) ) goto end_of_job; /* no input so return 0 to caller */
 if ( *term == '\000' ) goto end_of_job; /* ditto for empty string */  
 /* --- skip leading white space --- */  /* --- skip leading white space --- */
 skipwhite(term); /* skip leading white sapce */  skipwhite(term); /* skip leading white space */
 if ( *term == '\000' ) goto end_of_job; /* nothing but white space */  if ( *term == '\000' ) goto end_of_job; /* nothing but white space */
 /* --- skip leading gray space --- */  /* --- skip leading gray space --- */
 skipgray:  skipgray:
  for ( i=0; (token=grayspace[i]) != NULL; i++ ) /* check each grayspace */   for ( i=0; (token=grayspace[i]) != NULL; i++ ) /* check each grayspace */
   if ( strncmp(term,token,strlen(token)) == 0 ) { /* found grayspace */    if ( strncmp(term,token,strlen(token)) == 0 ) { /* found grayspace */
    term += strlen(token); /* skip past this grayspace token */     term += strlen(token); /* skip past this grayspace token */
      skipwhite(term); /* and skip any subsequent white space */
    if ( *term == '\000' ) { /* nothing left so quit */     if ( *term == '\000' ) { /* nothing left so quit */
      if ( msgfp!=NULL && msglevel >= 99 ) /* display for debugging */       if ( msgfp!=NULL && msglevel >= 99 ) /* display for debugging */
        fprintf(msgfp,"rastsmashcheck> only grayspace in %.32s\n",expression);         fprintf(msgfp,"rastsmashcheck> only grayspace in %.32s\n",expression);
Line 1890  end_of_job: Line 2378  end_of_job:
   
   
 /* ==========================================================================  /* ==========================================================================
  * Function: accent_subraster ( accent, width, height, pixsz )   * Function: accent_subraster ( accent, width, height, direction, pixsz )
  * Purpose: Allocate a new subraster of width x height   * Purpose: Allocate a new subraster of width x height
  * (or maybe different dimensions, depending on accent),   * (or maybe different dimensions, depending on accent),
  * and draw an accent (\hat or \vec or \etc) that fills it   * and draw an accent (\hat or \vec or \etc) that fills it
Line 1899  end_of_job: Line 2387  end_of_job:
  * etc, indicating the type of accent desired   * etc, indicating the type of accent desired
  * width (I) int containing desired width of accent (#cols)   * width (I) int containing desired width of accent (#cols)
  * height (I) int containing desired height of accent(#rows)   * height (I) int containing desired height of accent(#rows)
    * direction (I) int containing desired direction of accent,
    * +1=right, -1=left, 0=left/right
  * pixsz (I) int containing 1 for bitmap, 8 for bytemap   * pixsz (I) int containing 1 for bitmap, 8 for bytemap
  * --------------------------------------------------------------------------   * --------------------------------------------------------------------------
  * Returns: ( subraster * ) ptr to newly-allocated subraster with accent,   * Returns: ( subraster * ) ptr to newly-allocated subraster with accent,
Line 1908  end_of_job: Line 2398  end_of_job:
  * and caller should check dimensions in returned subraster   * and caller should check dimensions in returned subraster
  * ======================================================================= */   * ======================================================================= */
 /* --- entry point --- */  /* --- entry point --- */
 subraster *accent_subraster (  int accent, int width, int height, int pixsz )  subraster *accent_subraster (  int accent, int width, int height,
   int direction, int pixsz )
 {  {
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 Allocations and Declarations  Allocations and Declarations
Line 1931  raster *rastrot(),   /* rotate { for ove Line 2422  raster *rastrot(),   /* rotate { for ove
  *rastcpy(); /* may need copy of original */   *rastcpy(); /* may need copy of original */
 subraster *arrow_subraster(); /* rightarrow for vec */  subraster *arrow_subraster(); /* rightarrow for vec */
 subraster *rastack(); /* stack accent atop extra space */  subraster *rastack(); /* stack accent atop extra space */
   int iswidthneg = 0; /* set true if width<0 arg passed */
   int serifwidth=0; /* serif for surd */
   int isBig=0; /* true for ==>arrow, false for -->*/
   /* -------------------------------------------------------------------------
   initialization
   -------------------------------------------------------------------------- */
   if ( width < 0 ) { width=(-width); iswidthneg=1; } /* set neg width flag */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 outer switch() traps accents that may change caller's height,width  outer switch() traps accents that may change caller's height,width
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
Line 1987  switch ( accent ) Line 2485  switch ( accent )
  break;   break;
     /* --- sqrt request --- */      /* --- sqrt request --- */
     case SQRTACCENT:      case SQRTACCENT:
  col1 = SQRTWIDTH(height) - 1; /* right col of sqrt symbol */   serifwidth = SURDSERIFWIDTH(height); /* leading serif on surd */
  col0 = (col1+2)/3; /* midpoint col of sqrt */   col1 = SQRTWIDTH(height,(iswidthneg?1:2)) - 1; /*right col of sqrt*/
  row0 = (height+1)/2; /* midpoint row of sqrt */   /*col0 = (col1-serifwidth+2)/3;*/ /* midpoint col of sqrt */
    col0 = (col1-serifwidth+1)/2; /* midpoint col of sqrt */
    row0 = max2(1,((height+1)/2)-2); /* midpoint row of sqrt */
  row1 = height-1; /* bottom row of sqrt */   row1 = height-1; /* bottom row of sqrt */
  line_raster(rp,row0,0,row1,col0,thickness); /* descending portion */   /*line_raster(rp,row0,0,row1,col0,thickness);*/ /*descending portion*/
    line_raster(rp,row0+serifwidth,0,row0,serifwidth,thickness);
    line_raster(rp,row0,serifwidth,row1,col0,thickness); /* descending */
  line_raster(rp,row1,col0,0,col1,thickness); /* ascending portion */   line_raster(rp,row1,col0,0,col1,thickness); /* ascending portion */
  line_raster(rp,0,col1,0,width-1,thickness); /*overbar of thickness 1*/   line_raster(rp,0,col1,0,width-1,thickness); /*overbar of thickness 1*/
  break;   break;
Line 2018  switch ( accent ) Line 2520  switch ( accent )
  /* --- vec request --- */   /* --- vec request --- */
  case VECACCENT:   case VECACCENT:
     height = 2*(height/2) + 1; /* force height odd */      height = 2*(height/2) + 1; /* force height odd */
     if ( (accsp=arrow_subraster(width,height,pixsz,1,0)) /*build rightarrow*/      if ( absval(direction) >= 9 ) { /* want ==> arrow rather than --> */
         isBig = 1; /* signal "Big" arrow */
         direction -= 10; } /* reset direction = +1, -1, or 0 */
       if ((accsp=arrow_subraster(width,height,pixsz,direction,isBig)) /*arrow*/
     !=  NULL ) /* succeeded */      !=  NULL ) /* succeeded */
  { rp = accsp->image; /* "extract" raster with bitmap */   { rp = accsp->image; /* "extract" raster with bitmap */
   free((void *)accsp); } /* and free subraster "envelope" */    free((void *)accsp); } /* and free subraster "envelope" */
Line 2810  return ( status ); Line 3315  return ( status );
  * if negative, abs(nbot) used, and same   * if negative, abs(nbot) used, and same
  * number of extra cols added at right.   * number of extra cols added at right.
  * isline (I) int containing 0 to leave border pixels clear   * isline (I) int containing 0 to leave border pixels clear
  * or >0 to draw a line around border of width   * or >0 to draw a line around border of
  * isline.   * thickness isline pixels.  See Notes below.
  * isfree (I) int containing true to free rp before return   * isfree (I) int containing true to free rp before return
  * --------------------------------------------------------------------------   * --------------------------------------------------------------------------
  * Returns: ( raster * ) ptr to bordered raster,   * Returns: ( raster * ) ptr to bordered raster,
  * or NULL for any error.   * or NULL for any error.
  * --------------------------------------------------------------------------   * --------------------------------------------------------------------------
  * Notes:     o   * Notes:     o The isline arg also controls which sides border lines
    * are drawn for.  To do this, isline is interpreted as
    * thickness + 100*sides  so that, e.g., passing isline=601
    * is interpreted as sides=6 and thickness=1.  And
    * sides is further interpreted as 1=left side, 2=top,
    * 4=right and 8=bottom.  For example, sides=6 where 6=2+4
    * draws the top and right borders only.  15 draws all four
    * sides.  And 0 (no sides value embedded in isline)
    * draws all four sides, too.
  * ======================================================================= */   * ======================================================================= */
 /* --- entry point --- */  /* --- entry point --- */
 raster *border_raster ( raster *rp, int ntop, int nbot,  raster *border_raster ( raster *rp, int ntop, int nbot,
Line 2832  int width  = (rp==NULL?0:rp->width),  /* Line 3345  int width  = (rp==NULL?0:rp->width),  /*
  height = (rp==NULL?0:rp->height), /* height of raster */   height = (rp==NULL?0:rp->height), /* height of raster */
  istopneg=0, isbotneg=0, /* true if ntop or nbot negative */   istopneg=0, isbotneg=0, /* true if ntop or nbot negative */
  leftmargin = 0; /* adjust width to whole number of bytes */   leftmargin = 0; /* adjust width to whole number of bytes */
   int left=1, top=1, right=1, bot=1; /* frame sides to draw */
 int delete_raster(); /* free input rp if isfree is true */  int delete_raster(); /* free input rp if isfree is true */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 Initialization  Initialization
Line 2854  else Line 3368  else
     leftmargin = (width%8==0? 0 : 8-(width%8)); /*makes width multiple of 8*/      leftmargin = (width%8==0? 0 : 8-(width%8)); /*makes width multiple of 8*/
     width += leftmargin; /* width now multiple of 8 */      width += leftmargin; /* width now multiple of 8 */
     leftmargin /= 2; } /* center original raster */      leftmargin /= 2; } /* center original raster */
   /* --- check which sides to draw --- */
   if ( isline > 100 ) { /* sides arg embedded in isline */
     int iside=0, sides=isline/100; /* index, sides=1-15 from 101-1599 */
     isline -= 100*sides; /* and remove sides from isline */
     for ( iside=1; iside<=4; iside++ ) { /* check left, top, right, bot */
       int shift = sides/2; /* shift sides left one bit */
       if ( sides == 2*shift ) /* low-order bit is >>not<< set */
         switch ( iside ) { /* don't draw corresponding side */
           default: break; /* internal error */
           case 1: left = 0; break; /* 1 = left side */
           case 2: top  = 0; break; /* 2 = top side */
           case 3: right= 0; break; /* 4 = tight side */
           case 4: bot  = 0; break; } /* 8 = bottom side */
       sides = shift; /* ready for next side */
       } /* --- end-of-for(iside) --- */
     } /* --- end-of-if(isline>100) --- */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 allocate bordered raster, and embed rp within it  allocate bordered raster, and embed rp within it
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
Line 2870  if ( isline ) Line 3400  if ( isline )
   /* --- draw left- and right-borders --- */    /* --- draw left- and right-borders --- */
   for ( irow=0; irow<height; irow++ ) /* for each row of bp */    for ( irow=0; irow<height; irow++ ) /* for each row of bp */
     for ( icol=0; icol<nthick; icol++ ) /* and each pixel of thickness */      for ( icol=0; icol<nthick; icol++ ) /* and each pixel of thickness */
       { setpixel(bp,irow,icol,255); /* left border */        { if(left){setpixel(bp,irow,icol,255);} /* left border */
  setpixel(bp,irow,width-1-icol,255); } /* right border */   if(right){setpixel(bp,irow,width-1-icol,255);} } /* right border */
   /* --- draw top- and bottom-borders --- */    /* --- draw top- and bottom-borders --- */
   for ( icol=0; icol<width; icol++ ) /* for each col of bp */    for ( icol=0; icol<width; icol++ ) /* for each col of bp */
     for ( irow=0; irow<nthick; irow++ ) /* and each pixel of thickness */      for ( irow=0; irow<nthick; irow++ ) /* and each pixel of thickness */
       { setpixel(bp,irow,icol,255); /* top border */        { if(top){setpixel(bp,irow,icol,255);} /* top border */
  setpixel(bp,height-1-irow,icol,255); } /* bottom border */   if(bot){setpixel(bp,height-1-irow,icol,255);} } /* bottom border */
  } /* --- end-of-if(isline) --- */   } /* --- end-of-if(isline) --- */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 free rp if no longer needed  free rp if no longer needed
Line 2953  allocate new raster and fill it with lef Line 3483  allocate new raster and fill it with lef
 if ( (bp=new_raster(newwidth,height,rp->pixsz)) /*allocate backspaced raster*/  if ( (bp=new_raster(newwidth,height,rp->pixsz)) /*allocate backspaced raster*/
 ==   (raster *)NULL ) goto end_of_job; /* and quit if failed */  ==   (raster *)NULL ) goto end_of_job; /* and quit if failed */
 /* --- fill new raster --- */  /* --- fill new raster --- */
 if ( width-nback > 0 ) /* don't fill 1-pixel wide empty bp*/  if ( 1 || width-nback > 0 ) /* don't fill 1-pixel wide empty bp*/
  for ( icol=0; icol<newwidth; icol++ ) /* find first non-empty col in row */   for ( icol=0; icol<newwidth; icol++ ) /* find first non-empty col in row */
   for ( irow=0; irow<height; irow++ ) /* for each row inside rp */    for ( irow=0; irow<height; irow++ ) /* for each row inside rp */
     { int value = getpixel(rp,irow,icol); /* original pixel at irow,icol */      { int value = getpixel(rp,irow,icol); /* original pixel at irow,icol */
Line 3666  int idef = 0,   /* symdefs[] index */ Line 4196  int idef = 0,   /* symdefs[] index */
 int symlen = strlen(symbol), /* length of input symbol */  int symlen = strlen(symbol), /* length of input symbol */
  deflen, minlen=9999; /*length of shortest matching symdef*/   deflen, minlen=9999; /*length of shortest matching symdef*/
 int /*alnumsym = (symlen==1 && isalnum(*symbol)),*/ /*alphanumeric sym*/  int /*alnumsym = (symlen==1 && isalnum(*symbol)),*/ /*alphanumeric sym*/
  alphasym = (symlen==1 && isalpha(*symbol)); /* or alpha symbol */   alphasym = (symlen==1 && isalpha(*symbol)), /* or alpha symbol */
    slashsym = (*symbol=='\\'); /* or \backslashed symbol */
 int family = fontinfo[fontnum].family; /* current font family */  int family = fontinfo[fontnum].family; /* current font family */
 static char *displaysyms[][2] = { /*xlate to Big sym for \displaystyle*/  static char *displaysyms[][2] = { /*xlate to Big sym for \displaystyle*/
  /* --- see table on page 536 in TLC2 --- */   /* --- see table on page 536 in TLC2 --- */
Line 3723  for ( idef=0; ;idef++ )   /* until trail Line 4254  for ( idef=0; ;idef++ )   /* until trail
   else /* check against caller's symbol */    else /* check against caller's symbol */
     if ( strncmp(symbol,symdefs[idef].symbol,symlen) == 0 ) /* found match */      if ( strncmp(symbol,symdefs[idef].symbol,symlen) == 0 ) /* found match */
      if ( (fontnum==0||family==CYR10) /* mathmode, so check every match */       if ( (fontnum==0||family==CYR10) /* mathmode, so check every match */
        || (1 && symdefs[idef].handler!=NULL) /* or check every directive */
        || (1 && istextmode && slashsym) /*text mode and \backslashed symbol*/
      || (0 && istextmode && (!alphasym /* text mode and not alpha symbol */       || (0 && istextmode && (!alphasym /* text mode and not alpha symbol */
  || symdefs[idef].handler!=NULL))   /* or text mode and directive */   || symdefs[idef].handler!=NULL))   /* or text mode and directive */
      || (symdefs[idef].family==family /* have correct family */       || (symdefs[idef].family==family /* have correct family */
Line 4158  for ( idef=0; ;idef++ )   /* until trail Line 4691  for ( idef=0; ;idef++ )   /* until trail
     {      {
     strcpy(lcsymbol,defsym); /* local copy of symdefs[] symbol */      strcpy(lcsymbol,defsym); /* local copy of symdefs[] symbol */
     if ( isunesc && *lcsymbol=='\\' ) /* ignored leading \ in symbol */      if ( isunesc && *lcsymbol=='\\' ) /* ignored leading \ in symbol */
      strcpy(lcsymbol,lcsymbol+1); /* so squeeze it out of lcsymbol too*/       {strsqueeze(lcsymbol,1);} /*so squeeze it out of lcsymbol too*/
     if ( 0 ) /* don't ignore case */      if ( 0 ) /* don't ignore case */
      for ( symptr=lcsymbol; *symptr!='\000'; symptr++ ) /*for each symbol ch*/       for ( symptr=lcsymbol; *symptr!='\000'; symptr++ )/*for each symbol ch*/
       if ( isalpha(*symptr) ) *symptr=tolower(*symptr); /*lowercase the char*/        if ( isalpha(*symptr) ) *symptr=tolower(*symptr);/*lowercase the char*/
     deflen = strlen(lcsymbol); /* #chars in symbol we're checking */      deflen = strlen(lcsymbol); /* #chars in symbol we're checking */
     if ((symptr=strstr(lcsymbol,unescsymbol)) != NULL) /*found caller's sym*/      if ((symptr=strstr(lcsymbol,unescsymbol)) != NULL) /*found caller's sym*/
      if ( (isoint || strstr(lcsymbol,"oint")==NULL) /* skip unwanted "oint"*/       if ( (isoint || strstr(lcsymbol,"oint")==NULL) /* skip unwanted "oint"*/
Line 5144  if ( (dollar=strchr(expression,'$')) /* Line 5677  if ( (dollar=strchr(expression,'$')) /*
  *size = (isdelta? *size+sizevalue : sizevalue); /* so reset size */   *size = (isdelta? *size+sizevalue : sizevalue); /* so reset size */
       /* --- finally, set flag and shift size parameter out of preamble --- */        /* --- finally, set flag and shift size parameter out of preamble --- */
       isfontsize = 1; /*set flag showing font size present*/        isfontsize = 1; /*set flag showing font size present*/
       if ( comma != NULL ) strcpy(pretext,comma+1);/*leading size param gone*/        if ( comma != NULL ) /*2/15/12-isn't this superfluous???*/
           {strsqueezep(pretext,comma+1);} /* squeeze out leading size param */
      } /* --- end-of-if(comma!=NULL||etc) --- */       } /* --- end-of-if(comma!=NULL||etc) --- */
     /* --- copy any preamble params following size to caller's subexpr --- */      /* --- copy any preamble params following size to caller's subexpr --- */
     if ( comma != NULL || !isfontsize ) /*preamb contains params past size*/      if ( comma != NULL || !isfontsize ) /*preamb contains params past size*/
Line 5209  char *expptr=expression,  /* ptr within Line 5743  char *expptr=expression,  /* ptr within
  *tokptr=NULL, /*ptr to token found in expression*/   *tokptr=NULL, /*ptr to token found in expression*/
  *texsubexpr(), argval[8192]; /*parse for macro args after token*/   *texsubexpr(), argval[8192]; /*parse for macro args after token*/
 char *strchange(); /* change leading chars of string */  char *strchange(); /* change leading chars of string */
   int strreplace(); /* replace nnn with actual num, etc*/
 char *strwstr(); /*use strwstr() instead of strstr()*/  char *strwstr(); /*use strwstr() instead of strstr()*/
 char *findbraces(); /*find left { and right } for \atop*/  char *findbraces(); /*find left { and right } for \atop*/
 int idelim=0, /* left- or right-index */  int idelim=0, /* left- or right-index */
Line 5266  static char *atopdelims[] =  /* delims f Line 5801  static char *atopdelims[] =  /* delims f
  * -------------------------------------------------------- */   * -------------------------------------------------------- */
 char *htmlsym=NULL; /* symbols[isymbol].html */  char *htmlsym=NULL; /* symbols[isymbol].html */
 static struct { char *html; char *args; char *latex; } symbols[] =  static struct { char *html; char *args; char *latex; } symbols[] =
  { /* ---------------------------------------   { /* --------------------------------------------
      user-supplied newcommands       user-supplied newcommands
    --------------------------------------- */     -------------------------------------------- */
  #ifdef NEWCOMMANDS /* -DNEWCOMMANDS=\"filename.h\" */     #ifdef NEWCOMMANDS /* -DNEWCOMMANDS=\"filename.h\" */
    #include NEWCOMMANDS       #include NEWCOMMANDS
  #endif     #endif
    /* ---------------------------------------     /* --------------------------------------------
      Cyrillic termchar  mimeTeX equivalent...       Specials        termchar  value...
    --------------------------------------- */     -------------------------------------------- */
      { "\\version", NULL, "{\\small\\red\\text \\fbox{\\begin{gather}"
    "mime\\TeX version \\versionnumber \\\\"
    "last revised \\revisiondate \\\\ \\copyrighttext \\\\"
    "see \\homepagetext for details \\end{gather}}}" },
      { "\\copyright", NULL,
    "{\\small\\red\\text \\fbox{\\begin{gather}"
    "mimeTeX \\copyrighttext \\\\"
    "see \\homepagetext for details \\end{gather}}}" },
      { "\\versionnumber", NULL, "{\\text " VERSION "}" },
      { "\\revisiondate", NULL, "{\\text " REVISIONDATE "}" },
      { "\\copyrighttext", NULL, "{\\text " COPYRIGHTTEXT ".}" },
      { "\\homepagetext", NULL,
    "{\\text http://www.forkosh.com/mimetex.html}" },
      /* --------------------------------------------
        Cyrillic  termchar  mimeTeX equivalent...
      -------------------------------------------- */
    { "\\\'G", "embed\\","{\\acute{G}}" },     { "\\\'G", "embed\\","{\\acute{G}}" },
    { "\\\'g", "embed\\","{\\acute{g}}" },     { "\\\'g", "embed\\","{\\acute{g}}" },
    { "\\\'K", "embed\\","{\\acute{K}}" },     { "\\\'K", "embed\\","{\\acute{K}}" },
Line 5285  static struct { char *html; char *args; Line 5836  static struct { char *html; char *args;
    /*{ "\\\"e", "embed\\","{\\ddot{e}}" },*/     /*{ "\\\"e", "embed\\","{\\ddot{e}}" },*/
    { "\\\"I", "embed\\","{\\ddot{\\=I}}" },     { "\\\"I", "embed\\","{\\ddot{\\=I}}" },
    { "\\\"\\i", "embed\\","{\\ddot{\\=\\i}}" },     { "\\\"\\i", "embed\\","{\\ddot{\\=\\i}}" },
    /* ------------------------------------------     /* --------------------------------------------
    LaTeX Macro  #args,default   template...       LaTeX Macro #args,default  template...
    ------------------------------------------ */     -------------------------------------------- */
    { "\\lvec", "2n", "{#2_1,\\cdots,#2_{#1}}" },     { "\\lvec", "2n", "{#2_1,\\cdots,#2_{#1}}" },
    { "\\grave", "1", "{\\stackrel{\\Huge\\gravesym}{#1}}" }, /* \grave */     { "\\grave", "1", "{\\stackrel{\\Huge\\gravesym}{#1}}" }, /* \grave */
    { "\\acute", "1", "{\\stackrel{\\Huge\\acutesym}{#1}}" }, /* \acute */     { "\\acute", "1", "{\\stackrel{\\Huge\\acutesym}{#1}}" }, /* \acute */
Line 5296  static struct { char *html; char *args; Line 5847  static struct { char *html; char *args;
    { "\\buildrel","3", "{\\stackrel{#1}{#3}}" }, /* ignore #2 = \over */     { "\\buildrel","3", "{\\stackrel{#1}{#3}}" }, /* ignore #2 = \over */
    { "\\overset", NULL, "\\stackrel" }, /* just an alias */     { "\\overset", NULL, "\\stackrel" }, /* just an alias */
    { "\\underset", "2", "\\relstack{#2}{#1}" }, /* reverse args */     { "\\underset", "2", "\\relstack{#2}{#1}" }, /* reverse args */
    /* ---------------------------------------     { "\\dfrac", "2", "{\\frac{#1}{#2}}" },
     html char termchar  LaTeX equivalent...     { "\\binom", "2", "{\\begin{pmatrix}{#1}\\\\{#2}\\end{pmatrix}}" },
    --------------------------------------- */     { "\\aangle","26", "{\\boxaccent{#1}{#2}}" },
      { "\\actuarial","2 ","{#1\\boxaccent{6}{#2}}" }, /*comprehensive sym list*/
      { "\\boxaccent","2", "{\\fbox[,#1]{#2}}" },
      /* --------------------------------------------
        html char termchar  LaTeX equivalent...
      -------------------------------------------- */
    { "&quot", ";", "\"" }, /* &quot; is first, &#034; */     { "&quot", ";", "\"" }, /* &quot; is first, &#034; */
    { "&amp", ";", "&" },     { "&amp", ";", "&" },
    { "&lt", ";", "<" },     { "&lt", ";", "<" },
    { "&gt", ";", ">" },     { "&gt", ";", ">" },
    { "&#092", ";", "\\" }, /* backslash */     /*{ "&#092", ";", "\\" },*/ /* backslash */
    { "&backslash",";", "\\" },     { "&backslash",";", "\\" },
    { "&nbsp", ";", "~" },     { "&nbsp", ";", "~" },
    { "&iexcl", ";", "{\\raisebox{-2}{\\rotatebox{180}{!}}}" },     { "&iexcl", ";", "{\\raisebox{-2}{\\rotatebox{180}{!}}}" },
Line 5322  static struct { char *html; char *args; Line 5878  static struct { char *html; char *args;
    { "&Auml", ";", "{\\rm~\\ddot~A}" },     { "&Auml", ";", "{\\rm~\\ddot~A}" },
    { "&Aring", ";", "{\\rm~A\\limits^{-1$o}}" },     { "&Aring", ";", "{\\rm~A\\limits^{-1$o}}" },
    { "&atilde", ";", "{\\rm~\\tilde~a}" },     { "&atilde", ";", "{\\rm~\\tilde~a}" },
    { "&yuml", ";", "{\\rm~\\ddot~y}" },  /* &yuml; is last, &#255; */     { "&yuml", ";", "{\\rm~\\ddot~y}" }, /* &yuml; is last, &#255; */
    /* ---------------------------------------     { "&#", ";", "{[\\&\\#nnn?]}" },  /* all other explicit &#nnn's */
     html tag  termchar  LaTeX equivalent...     /* --------------------------------------------
    --------------------------------------- */       html tag     termchar    LaTeX equivalent...
    { "<br>", "embed\\i","\\\\" },     -------------------------------------------- */
    { "<br/>", "embed\\i","\\\\" },     { "< br >",    "embed\\i", "\\\\" },
    /* ---------------------------------------     { "< br / >",  "embed\\i", "\\\\" },
     garbage  termchar  LaTeX equivalent...     { "< dd >",    "embed\\i", " \000" },
    --------------------------------------- */     { "< / dd >",  "embed\\i", " \000" },
    { "< TEX >", "embed\\i","\000" },     { "< dl >",    "embed\\i", " \000" },
    { "< / TEX >","embed\\i","\000" },     { "< / dl >",  "embed\\i", " \000" },
    { "<br / >", "embed\\i","\000" },     { "< p >",     "embed\\i", " \000" },
    /* ---------------------------------------     { "< / p >",   "embed\\i", " \000" },
      /* --------------------------------------------
        garbage      termchar  LaTeX equivalent...
      -------------------------------------------- */
      { "< tex >",   "embed\\i", " \000" },
      { "< / tex >", "embed\\i", " \000" },
      /* --------------------------------------------
      LaTeX   termchar   mimeTeX equivalent...       LaTeX   termchar   mimeTeX equivalent...
    --------------------------------------- */     -------------------------------------------- */
    { "\\AA", NULL, "{\\rm~A\\limits^{-1$o}}" },     { "\\AA", NULL, "{\\rm~A\\limits^{-1$o}}" },
    { "\\aa", NULL, "{\\rm~a\\limits^{-1$o}}" },     { "\\aa", NULL, "{\\rm~a\\limits^{-1$o}}" },
    { "\\bmod", NULL, "{\\hspace2{\\rm~mod}\\hspace2}" },     { "\\bmod", NULL, "{\\hspace2{\\rm~mod}\\hspace2}" },
Line 5344  static struct { char *html; char *args; Line 5906  static struct { char *html; char *args;
    { "\\dots", NULL, "{\\cdots}" },     { "\\dots", NULL, "{\\cdots}" },
    { "\\cdots", NULL, "{\\raisebox3{\\ldots}}" },     { "\\cdots", NULL, "{\\raisebox3{\\ldots}}" },
    { "\\ldots", NULL, "{\\fs4.\\hspace1.\\hspace1.}" },     { "\\ldots", NULL, "{\\fs4.\\hspace1.\\hspace1.}" },
    { "\\ddots", NULL, "{\\fs4\\raisebox8.\\hspace1\\raisebox4.\\hspace1.}"},     { "\\ddots", NULL, "{\\fs4\\raisebox8.\\hspace1\\raisebox4."
    "\\hspace1\\raisebox0.}"},
    { "\\notin", NULL, "{\\not\\in}" },     { "\\notin", NULL, "{\\not\\in}" },
    { "\\neq", NULL, "{\\not=}" },     { "\\neq", NULL, "{\\not=}" },
    { "\\ne", NULL, "{\\not=}" },     { "\\ne", NULL, "{\\not=}" },
      { "\\mapsto", NULL, "{\\rule[fs/2]{1}{5+fs}\\hspace{-99}\\to}" },
    { "\\hbar", NULL, "{\\compose~h{{\\fs{-1}-\\atop\\vspace3}}}" },     { "\\hbar", NULL, "{\\compose~h{{\\fs{-1}-\\atop\\vspace3}}}" },
    { "\\angle", NULL, "{\\compose{\\hspace{3}\\lt}{\\circle(10,15;-80,80)}}"},     { "\\angle", NULL, "{\\compose{\\hspace{3}\\lt}{\\circle(10,15;-80,80)}}"},
    { "\\textcelsius", NULL, "{\\textdegree C}"},     { "\\textcelsius", NULL, "{\\textdegree C}"},
    { "\\textdegree", NULL, "{\\Large^{^{\\tiny\\mathbf o}}}"},     { "\\textdegree", NULL, "{\\Large^{^{\\tiny\\mathbf o}}}"},
    { "\\cr", NULL, "\\\\" },     { "\\cr", NULL, "\\\\" },
      /*{ "\\colon", NULL, "{:}" },*/
    { "\\iiint", NULL, "{\\int\\int\\int}\\limits" },     { "\\iiint", NULL, "{\\int\\int\\int}\\limits" },
    { "\\iint", NULL, "{\\int\\int}\\limits" },     { "\\iint", NULL, "{\\int\\int}\\limits" },
    { "\\Bigiint", NULL, "{\\Bigint\\Bigint}\\limits" },     { "\\Bigiint", NULL, "{\\Bigint\\Bigint}\\limits" },
    { "\\bigsqcap",NULL, "{\\fs{+4}\\sqcap}" },     { "\\bigsqcap",NULL, "{\\fs{+4}\\sqcap}" },
      { "\\_", "embed","{\\underline{\\ }}" }, /* displayed underscore */
    { "!`", NULL, "{\\raisebox{-2}{\\rotatebox{180}{!}}}" },     { "!`", NULL, "{\\raisebox{-2}{\\rotatebox{180}{!}}}" },
    { "?`", NULL, "{\\raisebox{-2}{\\rotatebox{180}{?}}}" },     { "?`", NULL, "{\\raisebox{-2}{\\rotatebox{180}{?}}}" },
    { "^\'", "embed","\'" }, /* avoid ^^ when re-xlating \' below */     { "^\'", "embed","\'" }, /* avoid ^^ when re-xlating \' below */
Line 5374  static struct { char *html; char *args; Line 5940  static struct { char *html; char *args;
    { "\\cancel",NULL, "\\Not" },     { "\\cancel",NULL, "\\Not" },
    { "\\hhline",NULL, "\\Hline" },     { "\\hhline",NULL, "\\Hline" },
    { "\\Hline", NULL, "\\hline\\,\\\\\\hline" },     { "\\Hline", NULL, "\\hline\\,\\\\\\hline" },
    /* ---------------------------------------------------------     /* -----------------------------------------------------------------------
        As per emails with Zbigniew Fiedorowicz <fiedorow@math.ohio-state.edu>
      "Algebra Syntax"  termchar   mimeTeX/LaTeX equivalent...       "Algebra Syntax"  termchar   mimeTeX/LaTeX equivalent...
    ------------------------------------------------------------ */     ----------------------------------------------------------------------- */
    { "sqrt", "1", "{\\sqrt{#1}}" },     { "sqrt", "1", "{\\sqrt{#1}}" },
    { "sin", "1", "{\\sin{#1}}" },     { "sin", "1", "{\\sin{#1}}" },
    { "cos", "1", "{\\cos{#1}}" },     { "cos", "1", "{\\cos{#1}}" },
Line 5384  static struct { char *html; char *args; Line 5951  static struct { char *html; char *args;
    { "acos", "1", "{\\cos^{-1}{#1}}" },     { "acos", "1", "{\\cos^{-1}{#1}}" },
    { "exp", "1", "{{\\rm~e}^{#1}}" },     { "exp", "1", "{{\\rm~e}^{#1}}" },
    { "det", "1", "{\\left|{#1}\\right|}" },     { "det", "1", "{\\left|{#1}\\right|}" },
    /* ---------------------------------------     /* --------------------------------------------
    LaTeX Constant    termchar   value...       LaTeX Constant    termchar   value...
    --------------------------------------- */     -------------------------------------------- */
    { "\\thinspace", NULL, "2" },     { "\\thinspace", NULL, "\\," },
    { "\\thinmathspace", NULL, "2" },     { "\\thinmathspace", NULL, "\\," },
    { "\\textwidth", NULL, "400" },     { "\\textwidth", NULL, "400" },
      /* --- end-of-table indicator --- */
    { NULL, NULL, NULL }     { NULL, NULL, NULL }
  } ; /* --- end-of-symbols[] --- */   } ; /* --- end-of-symbols[] --- */
   /* ---
    * html &#nn chars converted to latex equivalents
    * ---------------------------------------------- */
   int htmlnum=0; /* numbers[inum].html */
   static struct { int html; char *latex; } numbers[] =
    { /* ---------------------------------------
       html num  LaTeX equivalent...
      --------------------------------------- */
      { 9, " " }, /* horizontal tab */
      { 10, " " }, /* line feed */
      { 13, " " }, /* carriage return */
      { 32, " " }, /* space */
      { 33, "!" }, /* exclamation point */
      { 34, "\"" }, /* &quot; */
      { 35, "#" }, /* hash mark */
      { 36, "$" }, /* dollar */
      { 37, "%" }, /* percent */
      { 38, "&" }, /* &amp; */
      { 39, "\'" }, /* apostrophe (single quote) */
      { 40, ")" }, /* left parenthesis */
      { 41, ")" }, /* right parenthesis */
      { 42, "*" }, /* asterisk */
      { 43, "+" }, /* plus */
      { 44, "," }, /* comma */
      { 45, "-" }, /* hyphen (minus) */
      { 46, "." }, /* period */
      { 47, "/" }, /* slash */
      { 58, ":" }, /* colon */
      { 59, ";" }, /* semicolon */
      { 60, "<" }, /* &lt; */
      { 61, "=" }, /* = */
      { 62, ">" }, /* &gt; */
      { 63, "\?" }, /* question mark */
      { 64, "@" }, /* commercial at sign */
      { 91, "[" }, /* left square bracket */
      { 92, "\\" }, /* backslash */
      { 93, "]" }, /* right square bracket */
      { 94, "^" }, /* caret */
      { 95, "_" }, /* underscore */
      { 96, "`" }, /* grave accent */
      { 123, "{" }, /* left curly brace */
      { 124, "|" }, /* vertical bar */
      { 125, "}" }, /* right curly brace */
      { 126, "~" }, /* tilde */
      { 160, "~" }, /* &nbsp; (use tilde for latex) */
      { 166, "|" }, /* &brvbar; (broken vertical bar) */
      { 173, "-" }, /* &shy; (soft hyphen) */
      { 177, "{\\pm}" }, /* &plusmn; (plus or minus) */
      { 215, "{\\times}" }, /* &times; (plus or minus) */
      { -999, NULL }
    } ; /* --- end-of-numbers[] --- */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 first remove comments  first remove comments
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
Line 5409  while ( (leftptr=strstr(expptr,leftcomme Line 6028  while ( (leftptr=strstr(expptr,leftcomme
  { *leftptr = '\000'; /*so terminate expr at leftcomment*/   { *leftptr = '\000'; /*so terminate expr at leftcomment*/
   break; } /* and stop looking for comments */    break; } /* and stop looking for comments */
        *leftptr = '~'; /* replace entire comment by ~ */         *leftptr = '~'; /* replace entire comment by ~ */
        strcpy(leftptr+1,tokptr); /* and squeeze out comment */         strsqueezep(leftptr+1,tokptr); /* squeeze out comment */
        goto next_comment; } /* stop looking for rightcomment */         goto next_comment; } /* stop looking for rightcomment */
   /* --- no rightcomment after opening leftcomment --- */    /* --- no rightcomment after opening leftcomment --- */
   *leftptr = '\000'; /* so terminate expression */    *leftptr = '\000'; /* so terminate expression */
Line 5426  for(isymbol=0; (htmlsym=symbols[isymbol] Line 6045  for(isymbol=0; (htmlsym=symbols[isymbol]
   int htmllen = strlen(htmlsym); /* length of escape, _without_ ; */    int htmllen = strlen(htmlsym); /* length of escape, _without_ ; */
   int isalgebra = isalpha((int)(*htmlsym)); /* leading char alphabetic */    int isalgebra = isalpha((int)(*htmlsym)); /* leading char alphabetic */
   int isembedded = 0, /* true to xlate even if embedded */    int isembedded = 0, /* true to xlate even if embedded */
    istag=0, isamp=0, /* true for <tag>, &char; symbols */
  isstrwstr = 0, /* true to use strwstr() */   isstrwstr = 0, /* true to use strwstr() */
  wstrlen = 0; /* length of strwstr() match */   wstrlen = 0; /* length of strwstr() match */
   char *aleft="{([<|", *aright="})]>|"; /*left,right delims for alg syntax*/    char *aleft="{([<|", *aright="})]>|"; /*left,right delims for alg syntax*/
Line 5434  for(isymbol=0; (htmlsym=symbols[isymbol] Line 6054  for(isymbol=0; (htmlsym=symbols[isymbol]
   int embedlen = strlen(embedkeywd); /* #chars in embedkeywd */    int embedlen = strlen(embedkeywd); /* #chars in embedkeywd */
   char *args = symbols[isymbol].args, /* number {}-args, optional []-arg */    char *args = symbols[isymbol].args, /* number {}-args, optional []-arg */
  *htmlterm = args, /*if *args nonumeric, then html term*/   *htmlterm = args, /*if *args nonumeric, then html term*/
  *latexsym = symbols[isymbol].latex; /*latex replacement for htmlsym*/   *latexsym = symbols[isymbol].latex, /*latex replacement for htmlsym*/
    errorsym[256]; /*or latexsym may point to error msg*/
   char abuff[8192];  int iarg,nargs=0; /* macro expansion params */    char abuff[8192];  int iarg,nargs=0; /* macro expansion params */
   char wstrwhite[99]; /* whitespace chars for strwstr() */    char wstrwhite[99]; /* whitespace chars for strwstr() */
     skipwhite(htmlsym); /*skip any bogus leading whitespace*/
     htmllen = strlen(htmlsym); /* reset length of html token */
     istag = (isthischar(*htmlsym,"<")?1:0); /* html <tag> starts with < */
     isamp = (isthischar(*htmlsym,"&")?1:0); /* html &char; starts with & */
   if ( args != NULL ) /*we have args (or htmlterm) param*/    if ( args != NULL ) /*we have args (or htmlterm) param*/
    if ( *args != '\000' ) { /* and it's not an empty string */     if ( *args != '\000' ) { /* and it's not an empty string */
     if ( strchr("0123456789",*args) != NULL ) /* is 1st char #args=0-9 ? */      if ( strchr("0123456789",*args) != NULL ) /* is 1st char #args=0-9 ? */
Line 5444  for(isymbol=0; (htmlsym=symbols[isymbol] Line 6069  for(isymbol=0; (htmlsym=symbols[isymbol]
        *abuff = *args;  abuff[1] = '\000'; /* #args char in ascii buffer */         *abuff = *args;  abuff[1] = '\000'; /* #args char in ascii buffer */
        nargs = atoi(abuff); } /* interpret #args to numeric */         nargs = atoi(abuff); } /* interpret #args to numeric */
     else if ( strncmp(args,embedkeywd,embedlen) == 0 )/*xlate embedded token*/      else if ( strncmp(args,embedkeywd,embedlen) == 0 )/*xlate embedded token*/
      { htmlterm = NULL; /* if so, then we have no htmlterm */       { int arglen = strlen(args); /* length of "embed..." string */
          htmlterm = NULL; /* if so, then we have no htmlterm */
        isembedded = 1 ; /* turn on embedded flag */         isembedded = 1 ; /* turn on embedded flag */
        embedterm = args[embedlen]; /* char immediately after embed */         if ( arglen > embedlen ) /* have embed "allow escape" flag */
        if (strlen(args) > embedlen+1) { /* have embed,white for strwstr() */           embedterm = args[embedlen]; /* char immediately after "embed" */
  isstrwstr = 1; /* turn on strwtsr flag */         if (arglen > embedlen+1) { /* have embed,flag,white for strwstr*/
  strcpy(wstrwhite,args+6); } } /* and set its whitespace arg */   isstrwstr = 1; /* turn on strwtsr flag */
    strcpy(wstrwhite,args+embedlen+1); } } /*and set its whitespace arg*/
     } /* --- end-of-if(*args!='\000') --- */      } /* --- end-of-if(*args!='\000') --- */
   expptr = expression; /* re-start search at beginning */    expptr = expression; /* re-start search at beginning */
   while ( ( tokptr=(!isstrwstr?strstr(expptr,htmlsym): /* just use strtsr */    while ( ( tokptr=(!isstrwstr?strstr(expptr,htmlsym): /* just use strtsr */
   strwstr(expptr,htmlsym,wstrwhite,&wstrlen)) ) /* or use our strwstr */    strwstr(expptr,htmlsym,wstrwhite,&wstrlen)) ) /* or use our strwstr */
   != NULL ) /* found another sym */    != NULL ) { /* found another sym */
     { int  toklen = (!isstrwstr?htmllen:wstrlen); /* length of matched sym */        int  toklen = (!isstrwstr?htmllen:wstrlen); /* length of matched sym */
       char termchar = *(tokptr+toklen), /* char terminating html sequence */        char termchar = *(tokptr+toklen), /* char terminating html sequence */
            prevchar = (tokptr==expptr?' ':*(tokptr-1)); /*char preceding html*/             prevchar = (tokptr==expptr?' ':*(tokptr-1));/*char preceding html*/
         int  isescaped = (isthischar(prevchar,ESCAPE)?1:0); /* token escaped?*/
       int  escapelen = toklen; /* total length of escape sequence */        int  escapelen = toklen; /* total length of escape sequence */
         int  isflush = 0; /* true to flush (don't xlate) */
         /* --- check odd/even backslashes preceding tokens --- */
         if ( isescaped ) { /* have one preceding backslash */
    char *p = tokptr-1; /* ptr to that preceding backslash */
    while ( p != expptr ) { /* and we may have more preceding */
     p--; if(!isthischar(*p,ESCAPE))break; /* but we don't, so quit */
     isescaped = 1-isescaped; } } /* or flip isescaped flag if we do */
         /* --- init with "trivial" abuff,escapelen from symbols[] table --- */
       *abuff = '\000'; /* default to empty string */        *abuff = '\000'; /* default to empty string */
       if ( latexsym != NULL ) /* table has .latex xlation */        if ( latexsym != NULL ) /* table has .latex xlation */
        if ( *latexsym != '\000' ) /* and it's not an empty string */         if ( *latexsym != '\000' ) /* and it's not an empty string */
  strcpy(abuff,latexsym); /* so get local copy */   strcpy(abuff,latexsym); /* so get local copy */
       if ( htmlterm != NULL ) /* sequence may have terminator */        if ( !isembedded ) /*embedded sequences not terminated*/
          if ( htmlterm != NULL ) /* sequence may have terminator */
  escapelen += (isthischar(termchar,htmlterm)?1:0); /*add terminator*/   escapelen += (isthischar(termchar,htmlterm)?1:0); /*add terminator*/
       if ( !isembedded ) /* don't xlate embedded sequence */        /* --- don't xlate if we just found prefix of longer symbol, etc --- */
        if ( isalpha((int)termchar) ) /*we just have prefix of longer sym*/        if ( !isembedded ) { /* not embedded */
  { expptr = tokptr+toklen; /* just resume search after prefix */   if ( isescaped ) /* escaped */
     isflush = 1; /* set flag to flush escaped token */
    if ( !istag && isalpha((int)termchar) ) /* followed by alpha */
     isflush = 1; /* so just a prefix of longer symbol*/
    if ( isalpha((int)(*htmlsym)) ) /* symbol starts with alpha */
             if ( (!isspace(prevchar)&&isalpha(prevchar)) ) /* just a suffix*/
       isflush = 1; } /* set flag to flush token */
         if ( isembedded ) /* for embedded token */
          if ( isescaped ) /* and embedded \token escaped */
    if ( !isthischar(embedterm,ESCAPE) ) /* don't xlate escaped \token */
     isflush = 1; /* set flag to flush token */
         if ( isflush ) /* don't xlate this token */
    { expptr = tokptr+1;/*toklen;*/ /* just resume search after token */
   continue; } /* but don't replace it */    continue; } /* but don't replace it */
       if ( isembedded ) /* for embedded sequence */        /* --- check for &# prefix signalling &#nnn; --- */
  if ( !isthischar(embedterm,ESCAPE)  /* don't xlate escaped \token */        if ( strcmp(htmlsym,"&#") == 0 ) { /* replacing special &#nnn; chars */
  &&    isthischar(prevchar,ESCAPE) ) /* and we have escaped \token */         /* --- accumulate chars comprising number following &# --- */
   { expptr = tokptr+toklen; /*just resume search after literal*/         char anum[32]; /* chars comprising number after &# */
     continue; } /* but don't replace it */         int  inum = 0; /* no chars accumulated yet */
       if ( !isthischar(*htmlsym,ESCAPE) /* our symbol isn't escaped */         while ( termchar != '\000' ) { /* don't go past end-of-string */
       &&   isalpha(*htmlsym) /* and our symbol starts with alpha*/           if ( !isdigit((int)termchar) ) break; /* and don't go past digits */
       &&   !isthischar(*htmlsym,"&") ) /* and not an &html; special char */           if ( inum > 10 ) break; /* some syntax error in expression */
        if ( tokptr != expression ) /* then if we're past beginning */           anum[inum] = termchar; /* accumulate this digit */
  if ( isthischar(*(tokptr-1),ESCAPE) /*and if inline symbol escaped*/           inum++;  toklen++; /* bump field length, token length */
  ||   (isalpha(*(tokptr-1))) ) /* or if suffix of longer string */           termchar = *(tokptr+toklen); } /* char terminating html sequence */
   { expptr = tokptr+escapelen; /*just resume search after literal*/         anum[inum] = '\000'; /* null-terminate anum */
     continue; } /* but don't replace it */         escapelen = toklen; /* length of &#nnn; sequence */
          if ( htmlterm != NULL ) /* sequence may have terminator */
            escapelen += (isthischar(termchar,htmlterm)?1:0); /*add terminator*/
          /* --- look up &#nnn in number[] table --- */
          htmlnum = atoi(anum); /* convert anum[] to an integer */
          strninit(errorsym,latexsym,128); /* init error message */
          latexsym = errorsym; /* init latexsym as error message */
          strreplace(latexsym,"nnn",anum,1); /*place actual &#num in message*/
          for ( inum=0; numbers[inum].html>=0; inum++ ) /* run thru numbers[] */
            if ( htmlnum ==  numbers[inum].html ) { /* till we find a match */
              latexsym = numbers[inum].latex; /* latex replacement */
              break; } /* no need to look any further */
          if ( latexsym != NULL ) /* table has .latex xlation */
           if ( *latexsym != '\000' ) /* and it's not an empty string */
    strcpy(abuff,latexsym); /* so get local copy */
          } /* --- end-of-if(strcmp(htmlsym,"&#")==0) --- */
         /* --- substitute macro arguments --- */
       if ( nargs > 0 ) /*substitute #1,#2,... in latexsym*/        if ( nargs > 0 ) /*substitute #1,#2,... in latexsym*/
        {         {
        char *arg1ptr = tokptr+escapelen;/* nargs begin after macro literal */         char *arg1ptr = tokptr+escapelen;/* nargs begin after macro literal */
Line 5498  for(isymbol=0; (htmlsym=symbols[isymbol] Line 6163  for(isymbol=0; (htmlsym=symbols[isymbol]
  &&   !isalgebra ) /* but not in "algebra syntax" */   &&   !isalgebra ) /* but not in "algebra syntax" */
  { strcpy(argval,optarg); /* init with default value */   { strcpy(argval,optarg); /* init with default value */
    if ( *expptr == '[' ) /* but user gave us [argval] */     if ( *expptr == '[' ) /* but user gave us [argval] */
     expptr = texsubexpr(expptr,argval,0,"[","]",0,0); } /*so get it*/       expptr = texsubexpr(expptr,argval,0,"[","]",0,0); } /*so get it*/
  else /* not optional, so get {argval} */   else /* not optional, so get {argval} */
  if ( *expptr != '\000' ) { /* check that some argval provided */   if ( *expptr != '\000' ) { /* check that some argval provided */
   if ( !isalgebra ) /* only { } delims for latex macro */     if ( !isalgebra ) /* only { } delims for latex macro */
     expptr = texsubexpr(expptr,argval,0,"{","}",0,0); /*get {argval}*/       expptr = texsubexpr(expptr,argval,0,"{","}",0,0);/*get {argval}*/
   else /*any delim for algebra syntax macro*/     else { /*any delim for algebra syntax macro*/
    { expptr = texsubexpr(expptr,argval,0,aleft,aright,0,1);       expptr = texsubexpr(expptr,argval,0,aleft,aright,0,1);
      if ( isthischar(*argval,aleft) ) /* have delim-enclosed arg */       if ( isthischar(*argval,aleft) ) /* have delim-enclosed arg */
       if ( *argval != '{' ) /* and it's not { }-enclosed */         if ( *argval != '{' ) { /* and it's not { }-enclosed */
        { strchange(0,argval,"\\left"); /* insert opening \left, */           strchange(0,argval,"\\left"); /* insert opening \left, */
  strchange(0,argval+strlen(argval)-1,"\\right"); } }/*\right*/           strchange(0,argval+strlen(argval)-1,"\\right"); } }/*\right*/
    } /* --- end-of-if(*expptr!='\000') --- */    } /* --- end-of-if(*expptr!='\000') --- */
    /* --- (recursively) call mimeprep() to prep the argument --- */
    if ( !isempty(argval) ) /* have an argument */
     mimeprep(argval); /* so (recursively) prep it */
  /* --- replace #`iarg` in macro with argval --- */   /* --- replace #`iarg` in macro with argval --- */
  sprintf(argsignal,"#%d",iarg); /* #1...#9 signals argument */   sprintf(argsignal,"#%d",iarg); /* #1...#9 signals argument */
  while ( (argsigptr=strstr(argval,argsignal)) != NULL ) /* #1...#9 */   while ( (argsigptr=strstr(argval,argsignal)) != NULL ) /* #1...#9 */
  strcpy(argsigptr,argsigptr+strlen(argsignal)); /*can't be in argval*/   {strsqueeze(argsigptr,strlen(argsignal));} /* can't be in argval */
  while ( (argsigptr=strstr(abuff,argsignal)) != NULL ) /* #1...#9 */   while ( (argsigptr=strstr(abuff,argsignal)) != NULL ) /* #1...#9 */
  strchange(strlen(argsignal),argsigptr,argval); /*replaced by argval*/   strchange(strlen(argsignal),argsigptr,argval); /*replaced by argval*/
  } /* --- end-of-for(iarg) --- */   } /* --- end-of-for(iarg) --- */
Line 5521  for(isymbol=0; (htmlsym=symbols[isymbol] Line 6189  for(isymbol=0; (htmlsym=symbols[isymbol]
        } /* --- end-of-if(nargs>0) --- */         } /* --- end-of-if(nargs>0) --- */
       strchange(escapelen,tokptr,abuff); /*replace macro or html symbol*/        strchange(escapelen,tokptr,abuff); /*replace macro or html symbol*/
       expptr = tokptr + strlen(abuff); /*resume search after macro / html*/        expptr = tokptr + strlen(abuff); /*resume search after macro / html*/
     } /* --- end-of-while(tokptr!=NULL) --- */        } /* --- end-of-while(tokptr!=NULL) --- */
   } /* --- end-of-for(isymbol) --- */    } /* --- end-of-for(isymbol) --- */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 convert \left( to \(  and  \right) to \),  etc.  convert \left( to \(  and  \right) to \),  etc.
Line 5539  if ( xlateleft )   /* \left...\right xla Line 6207  if ( xlateleft )   /* \left...\right xla
   while ( (tokptr=strstr(expptr,lrstr)) != NULL ) /* found \left or \right */    while ( (tokptr=strstr(expptr,lrstr)) != NULL ) /* found \left or \right */
     {      {
     if ( isthischar(*(tokptr+lrlen),braces) ) /* followed by a 1-char brace*/      if ( isthischar(*(tokptr+lrlen),braces) ) /* followed by a 1-char brace*/
       { strcpy(tokptr+1,tokptr+lrlen); /* so squeeze out "left" or "right"*/        { strsqueeze((tokptr+1),(lrlen-1));/*so squeeze out "left" or "right"*/
  expptr = tokptr+2; } /* and resume search past brace */   expptr = tokptr+2; } /* and resume search past brace */
     else /* may be a "long" brace like \| */      else /* may be a "long" brace like \| */
       {        {
Line 5547  if ( xlateleft )   /* \left...\right xla Line 6215  if ( xlateleft )   /* \left...\right xla
       for(isymbol=0; (lrsym=lrfrom[isymbol]) != NULL; isymbol++)        for(isymbol=0; (lrsym=lrfrom[isymbol]) != NULL; isymbol++)
  { int symlen = strlen(lrsym); /* #chars in delim, e.g., 2 for \| */   { int symlen = strlen(lrsym); /* #chars in delim, e.g., 2 for \| */
   if ( memcmp(tokptr+lrlen,lrsym,symlen) == 0 ) /* found long delim*/    if ( memcmp(tokptr+lrlen,lrsym,symlen) == 0 ) /* found long delim*/
     { strcpy(tokptr+1,tokptr+lrlen+symlen-1); /* squeeze out delim */      { strsqueeze((tokptr+1),(lrlen+symlen-2)); /*squeeze out delim*/
       *(tokptr+1) = *(lrto[isymbol]); /* last char now 1-char delim*/        *(tokptr+1) = *(lrto[isymbol]); /* last char now 1-char delim*/
       expptr = tokptr+2 - lrlen; /* resume search past 1-char delim*/        expptr = tokptr+2 - lrlen; /* resume search past 1-char delim*/
       break; } /* no need to check more lrsym's */        break; } /* no need to check more lrsym's */
Line 5595  for(isymbol=0; (atopsym=atopcommands[isy Line 6263  for(isymbol=0; (atopsym=atopcommands[isy
  arg[rightlen] = '}'; /* add closing } */   arg[rightlen] = '}'; /* add closing } */
  arg[rightlen+1] = '\000'; /* and null terminate it */   arg[rightlen+1] = '\000'; /* and null terminate it */
  if ( isthischar(*arg,WHITEMATH) ) /* 1st char was mandatory space */   if ( isthischar(*arg,WHITEMATH) ) /* 1st char was mandatory space */
   strcpy(arg,arg+1); /* so squeeze it out */    {strsqueeze(arg,1);} /* so squeeze it out */
  strcat(command,arg); /* concatanate right-arg} */   strcat(command,arg); /* concatanate right-arg} */
  if (close!=NULL) strcat(command,close); /* add close delim if needed*/   if (close!=NULL) strcat(command,close); /* add close delim if needed*/
  strchange(totlen-2,leftbrace+1,command); /* {\atop} --> {\atop{}{}} */   strchange(totlen-2,leftbrace+1,command); /* {\atop} --> {\atop{}{}} */
Line 5644  int tolen = (to==NULL?0:strlen(to)), /* Line 6312  int tolen = (to==NULL?0:strlen(to)), /*
 shift from left or right to accommodate replacement of its nfirst chars by to  shift from left or right to accommodate replacement of its nfirst chars by to
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 if ( tolen < nfirst ) /* shift left is easy */  if ( tolen < nfirst ) /* shift left is easy */
   strcpy(from,from+nshift); /* because memory doesn't overlap */    {strsqueeze(from,nshift);} /* memmove avoids overlap memory */
 if ( tolen > nfirst ) /* need more room at start of from */  if ( tolen > nfirst ) /* need more room at start of from */
   { char *pfrom = from+strlen(from); /* ptr to null terminating from */    { char *pfrom = from+strlen(from); /* ptr to null terminating from */
     for ( ; pfrom>=from; pfrom-- ) /* shift all chars including null */      for ( ; pfrom>=from; pfrom-- ) /* shift all chars including null */
Line 5784  if ( white != NULL )   /*user provided p Line 6452  if ( white != NULL )   /*user provided p
  if ( *white != '\000' ) { /*and it's not just an empty string*/   if ( *white != '\000' ) { /*and it's not just an empty string*/
    strcpy(whitespace,white); /* so use caller's white spaces */     strcpy(whitespace,white); /* so use caller's white spaces */
    while ( (pwhite=strchr(whitespace,'i')) != NULL ) /* have an embedded i */     while ( (pwhite=strchr(whitespace,'i')) != NULL ) /* have an embedded i */
      strcpy(pwhite,pwhite+1); /* so squeeze it out */       {strsqueeze(pwhite,1);} /* so squeeze it out */
    while ( (pwhite=strchr(whitespace,'I')) != NULL ) /* have an embedded I */     while ( (pwhite=strchr(whitespace,'I')) != NULL ) /* have an embedded I */
      strcpy(pwhite,pwhite+1); /* so squeeze it out */       {strsqueeze(pwhite,1);} /* so squeeze it out */
    if ( *whitespace == '\000' ) /* caller's white just had i,I */     if ( *whitespace == '\000' ) /* caller's white just had i,I */
      strcpy(whitespace,WHITEMATH); } /* so revert back to default */       strcpy(whitespace,WHITEMATH); } /* so revert back to default */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
Line 5855  end_of_job: Line 6523  end_of_job:
   
   
 /* ==========================================================================  /* ==========================================================================
    * Function: strdetex ( s, mode )
    * Purpose: Removes/replaces any LaTeX math chars in s
    * so that s can be displayed "verbatim",
    * e.g., for error messages.
    * --------------------------------------------------------------------------
    * Arguments: s (I) char * to null-terminated string
    * whose math chars are to be removed/replaced
    * mode (I) int containing 0 to _not_ use macros (i.e.,
    * mimeprep won't be called afterwards),
    * or containing 1 to use macros that will
    * be expanded by a subsequent call to mimeprep.
    * --------------------------------------------------------------------------
    * Returns: ( char * ) ptr to "cleaned" copy of s
    * or "" (empty string) for any error.
    * --------------------------------------------------------------------------
    * Notes:     o The returned pointer addresses a static buffer,
    * so don't call strdetex() again until you're finished
    * with output from the preceding call.
    * ======================================================================= */
   /* --- entry point --- */
   char *strdetex ( char *s, int mode )
   {
   /* -------------------------------------------------------------------------
   Allocations and Declarations
   -------------------------------------------------------------------------- */
   static char sbuff[4096]; /* copy of s with no math chars */
   int strreplace(); /* replace _ with -, etc */
   /* -------------------------------------------------------------------------
   Make a clean copy of s
   -------------------------------------------------------------------------- */
   /* --- check input --- */
   *sbuff = '\000'; /* initialize in case of error */
   if ( isempty(s) ) goto end_of_job; /* no input */
   /* --- start with copy of s --- */
   strninit(sbuff,s,2048); /* leave room for replacements */
   /* --- make some replacements -- we *must* replace \ { } first --- */
   strreplace(sbuff,"\\","\\backslash~\\!\\!",0); /*change all \'s to text*/
   strreplace(sbuff,"{", "\\lbrace~\\!\\!",0); /*change all {'s to \lbrace*/
   strreplace(sbuff,"}", "\\rbrace~\\!\\!",0); /*change all }'s to \rbrace*/
   /* --- now our further replacements may contain \directives{args} --- */
   if( mode >= 1 ) strreplace(sbuff,"_","\\_",0); /* change all _'s to \_ */
   else strreplace(sbuff,"_","\\underline{\\qquad}",0); /*change them to text*/
   if(0)strreplace(sbuff,"<","\\textlangle ",0); /* change all <'s to text */
   if(0)strreplace(sbuff,">","\\textrangle ",0); /* change all >'s to text */
   if(0)strreplace(sbuff,"$","\\textdollar ",0); /* change all $'s to text */
   strreplace(sbuff,"$","\\$",0); /* change all $'s to \$ */
   strreplace(sbuff,"&","\\&",0); /* change all &'s to \& */
   strreplace(sbuff,"%","\\%",0); /* change all %'s to \% */
   strreplace(sbuff,"#","\\#",0); /* change all #'s to \# */
   /*strreplace(sbuff,"~","\\~",0);*/ /* change all ~'s to \~ */
   strreplace(sbuff,"^","{\\fs{+2}\\^}",0); /* change all ^'s to \^ */
   end_of_job:
     return ( sbuff ); /* back with clean copy of s */
   } /* --- end-of-function strdetex() --- */
   
   
   /* ==========================================================================
  * Function: strtexchr ( char *string, char *texchr )   * Function: strtexchr ( char *string, char *texchr )
  * Purpose: Find first texchr in string, but texchr must be followed   * Purpose: Find first texchr in string, but texchr must be followed
  * by non-alpha   * by non-alpha
Line 5960  end_of_job: Line 6685  end_of_job:
       brace = ptr; /* { before expressn, } after cmmnd*/        brace = ptr; /* { before expressn, } after cmmnd*/
   return ( brace ); /*back to caller with delim or NULL*/    return ( brace ); /*back to caller with delim or NULL*/
 } /* --- end-of-function findbraces() --- */  } /* --- end-of-function findbraces() --- */
   
   
   /* ==========================================================================
    * Function: strpspn ( char *s, char *reject, char *segment )
    * Purpose: finds the initial segment of s containing no chars
    * in reject that are outside (), [] and {} parens, e.g.,
    *   strpspn("abc(---)def+++","+-",segment) returns
    *   segment="abc(---)def" and a pointer to the first '+' in s
    * because the -'s are enclosed in () parens.
    * --------------------------------------------------------------------------
    * Arguments: s (I) (char *)pointer to null-terminated string
    * whose initial segment is desired
    * reject (I) (char *)pointer to null-terminated string
    * containing the "reject chars"
    * segment (O) (char *)pointer returning null-terminated
    * string comprising the initial segment of s
    * that contains non-rejected chars outside
    * (),[],{} parens, i.e., all the chars up to
    * but not including the returned pointer.
    * (That's the entire string if no non-rejected
    * chars are found.)
    * --------------------------------------------------------------------------
    * Returns: ( char * ) pointer to first reject-char found in s
    * outside parens, or a pointer to the
    * terminating '\000' of s if there are
    * no reject chars in s outside all () parens.
    * --------------------------------------------------------------------------
    * Notes:     o the return value is _not_ like strcspn()'s
    *      o improperly nested (...[...)...] are not detected,
    * but are considered "balanced" after the ]
    *      o if reject not found, segment returns the entire string s
    *      o leading/trailing whitespace is trimmed from returned segment
    * ======================================================================= */
   /* --- entry point --- */
   char *strpspn ( char *s, char *reject, char *segment )
   {
   /* -------------------------------------------------------------------------
   Allocations and Declarations
   -------------------------------------------------------------------------- */
   char *ps = s; /* current pointer into s */
   int depth = 0; /* () paren nesting level */
   int seglen=0, maxseg=2047; /* segment length, max allowed */
   /* -------------------------------------------------------------------------
   initialization
   -------------------------------------------------------------------------- */
   /* --- check arguments --- */
   if ( isempty(s) ) /* no input string supplied */
     goto end_of_job; /* no reject chars supplied */
   /* -------------------------------------------------------------------------
   find first char from s outside () parens (and outside ""'s) and in reject
   -------------------------------------------------------------------------- */
   while ( *ps != '\000' ) { /* search till end of input string */
     if ( isthischar(*ps,"([{") ) depth++; /* push another paren */
     if ( isthischar(*ps,")]}") ) depth--; /* or pop another paren */
     if ( depth < 1 ) { /* we're outside all parens */
       if ( isempty(reject) ) break; /* no reject so break immediately */
       if ( isthischar(*ps,reject) ) break; } /* only break on a reject char */
     if ( segment != NULL ) /* caller gave us segment */
       if ( seglen < maxseg ) /* don't overflow segment buffer */
         memcpy(segment+seglen,ps,1); /* so copy non-reject char */
     seglen += 1;  ps += 1; /* bump to next char */
     } /* --- end-of-while(*ps!=0) --- */
   end_of_job:
     if ( segment != NULL ) { /* caller gave us segment */
       if ( isempty(reject) ) { /* no reject char */
         segment[min2(seglen,maxseg)] = *ps;  seglen++; } /*closing )]} to seg*/
       segment[min2(seglen,maxseg)] = '\000'; /* null-terminate the segment */
       trimwhite(segment); } /* trim leading/trailing whitespace*/
     return ( ps ); /* back to caller */
   } /* --- end-of-function strpspn() --- */
   
   
   /* ==========================================================================
    * Function: isstrstr ( char *string, char *snippets, int iscase )
    * Purpose: determine whether any substring of 'string'
    * matches any of the comma-separated list of 'snippets',
    * ignoring case if iscase=0.
    * --------------------------------------------------------------------------
    * Arguments: string (I) char * containing null-terminated
    * string that will be searched for
    * any one of the specified snippets
    * snippets (I) char * containing null-terminated,
    * comma-separated list of snippets
    * to be searched for in string
    * iscase (I) int containing 0 for case-insensitive
    * comparisons, or 1 for case-sensitive
    * --------------------------------------------------------------------------
    * Returns: ( int ) 1 if any snippet is a substring of
    * string, 0 if not
    * --------------------------------------------------------------------------
    * Notes:     o
    * ======================================================================= */
   /* --- entry point --- */
   int isstrstr ( char *string, char *snippets, int iscase )
   {
   /* -------------------------------------------------------------------------
   Allocations and Declarations
   -------------------------------------------------------------------------- */
   int status = 0; /*1 if any snippet found in string*/
   char snip[256], *snipptr = snippets, /* munge through each snippet */
    delim = ',', *delimptr = NULL; /* separated by delim's */
   char stringcp[4096], *cp = stringcp; /*maybe lowercased copy of string*/
   /* -------------------------------------------------------------------------
   initialization
   -------------------------------------------------------------------------- */
   /* --- arg check --- */
   if ( string==NULL || snippets==NULL ) goto end_of_job; /* missing arg */
   if ( *string=='\000' || *snippets=='\000' ) goto end_of_job; /* empty arg */
   /* --- copy string and lowercase it if case-insensitive --- */
   strninit(stringcp,string,4064); /* local copy of string */
   if ( !iscase ) /* want case-insensitive compares */
     for ( cp=stringcp; *cp != '\000'; cp++ ) /* so for each string char */
       if ( isupper(*cp) ) *cp = tolower(*cp); /*lowercase any uppercase chars*/
   /* -------------------------------------------------------------------------
   extract each snippet and see if it's a substring of string
   -------------------------------------------------------------------------- */
   while ( snipptr != NULL ) /* while we still have snippets */
     {
     /* --- extract next snippet --- */
     if ( (delimptr = strchr(snipptr,delim)) /* locate next comma delim */
     ==   NULL ) /*not found following last snippet*/
       { strninit(snip,snipptr,255); /* local copy of last snippet */
         snipptr = NULL; } /* signal end-of-string */
     else /* snippet ends just before delim */
       { int sniplen = (int)(delimptr-snipptr) - 1;  /* #chars in snippet */
         memcpy(snip,snipptr,sniplen); /* local copy of snippet chars */
         snip[sniplen] = '\000'; /* null-terminated snippet */
         snipptr = delimptr + 1; } /* next snippet starts after delim */
     /* --- lowercase snippet if case-insensitive --- */
     if ( !iscase ) /* want case-insensitive compares */
       for ( cp=snip; *cp != '\000'; cp++ ) /* so for each snippet char */
         if ( isupper(*cp) ) *cp=tolower(*cp); /*lowercase any uppercase chars*/
     /* --- check if snippet in string --- */
     if ( strstr(stringcp,snip) != NULL ) /* found snippet in string */
       { status = 1; /* so reset return status */
         break; } /* no need to check any further */
     } /* --- end-of-while(*snipptr!=0) --- */
   end_of_job: return ( status ); /*1 if snippet found in list, else 0*/
   } /* --- end-of-function isstrstr() --- */
   
   
   /* ==========================================================================
    * Function: isnumeric ( s )
    * Purpose: determine if s is an integer
    * --------------------------------------------------------------------------
    * Arguments: s (I) (char *)pointer to null-terminated string
    * that's checked for a leading + or -
    * followed by digits
    * --------------------------------------------------------------------------
    * Returns: ( int ) 1 if s is numeric, 0 if it is not
    * --------------------------------------------------------------------------
    * Notes:     o
    * ======================================================================= */
   /* --- entry point --- */
   int isnumeric ( char *s )
   {
   /* -------------------------------------------------------------------------
   determine whether s is an integer
   -------------------------------------------------------------------------- */
   int status = 0; /* return 0 if not numeric, 1 if is*/
   char *p = s; /* pointer into s */
   if ( isempty(s) ) goto end_of_job; /* missing arg or empty string */
   skipwhite(p); /*check for leading +or- after space*/
   if ( *p=='+' || *p=='-' ) p++; /* skip leading + or - */
   for ( ; *p != '\000'; p++ ) { /* check rest of s for digits */
     if ( isdigit(*p) ) continue; /* still got uninterrupted digits */
     if ( !isthischar(*p,WHITESPACE) ) goto end_of_job; /* non-numeric char */
     skipwhite(p); /* skip all subsequent whitespace */
     if ( *p == '\000' ) break; /* trailing whitespace okay */
     goto end_of_job; /* embedded whitespace non-numeric */
     } /* --- end-of-for(*p) --- */
   status = 1; /* numeric after checks succeeded */
   end_of_job:
     return ( status ); /*back to caller with 1=string, 0=no*/
   } /* --- end-of-function isnumeric() --- */
   
   
   /* ==========================================================================
    * Function: evalterm ( STORE *store, char *term )
    * Purpose: evaluates a term
    * --------------------------------------------------------------------------
    * Arguments: store (I/O) STORE * containing environment
    * in which term is to be evaluated
    * term (I) char * containing null-terminated string
    * with a term like "3" or "a" or "a+3"
    * whose value is to be determined
    * --------------------------------------------------------------------------
    * Returns: ( int ) value of term,
    * or NOVALUE for any error
    * --------------------------------------------------------------------------
    * Notes:     o Also evaluates index?a:b:c:etc, returning a if index<=0,
    * b if index=1, etc, and the last value if index is too large.
    * Each a:b:c:etc can be another expression, including another
    * (index?a:b:c:etc) which must be enclosed in parentheses.
    * ======================================================================= */
   /* --- entry point --- */
   int evalterm ( STORE *store, char *term )
   {
   /* -------------------------------------------------------------------------
   Allocations and Declarations
   -------------------------------------------------------------------------- */
   int termval = 0; /* term value returned to caller */
   char token[2048] = "\000", /* copy term */
    *delim = NULL; /* delim '(' or '?' in token */
   /*int evalwff(),*/ /* recurse to evaluate terms */
   /* evalfunc();*/ /* evaluate function(arg1,arg2,...)*/
   char *strpspn(); /* span delims */
   int getstore(); /* lookup variables */
   int isnumeric(); /* numeric=constant, else variable */
   static int evaltermdepth = 0; /* recursion depth */
   int novalue = (-89123456); /* dummy (for now) error signal */
   /* -------------------------------------------------------------------------
   Initialization
   -------------------------------------------------------------------------- */
   if ( ++evaltermdepth > 99 ) goto end_of_job; /*probably recursing forever*/
   if ( store==NULL || isempty(term) ) goto end_of_job; /*check for missing arg*/
   skipwhite(term); /* skip any leading whitespace */
   /* -------------------------------------------------------------------------
   First look for conditional of the form term?term:term:...
   -------------------------------------------------------------------------- */
   /* ---left-hand part of conditional is chars preceding "?" outside ()'s--- */
   delim = strpspn(term,"?",token); /* chars preceding ? outside () */
   if ( *delim != '\000' ) { /* found conditional expression */
     int ncolons = 0; /* #colons we've found so far */
     if ( *token != '\000' ) /* evaluate "index" value on left */
       if ( (termval=evalterm(store,token)) /* evaluate left-hand term */
       == novalue ) goto end_of_job; /* return error if failed */
     while ( *delim != '\000' ) { /* still have chars in term */
       delim++; *token='\000'; /* initialize for next "value:" */
       if ( *delim == '\000' ) break; /* no more values */
       delim = strpspn(delim,":",token); /* chars preceding : outside () */
       if ( ncolons++ >= termval ) break; /* have corresponding term */
       } /* --- end-of-while(*delim!='\000')) --- */
     if ( *token != '\000' ) /* have x:x:value:x:x on right */
       termval=evalterm(store,token); /* so evaluate it */
     goto end_of_job; /* return result to caller */
     } /* --- end-of-if(*delim!='\000')) --- */
   /* -------------------------------------------------------------------------
   evaluate a+b recursively
   -------------------------------------------------------------------------- */
   /* --- left-hand part of term is chars preceding "/+-*%" outside ()'s --- */
   term = strpspn(term,"/+-*%",token); /* chars preceding /+-*% outside ()*/
   /* --- evaluate a+b, a-b, etc --- */
   if ( *term != '\000' ) { /* found arithmetic operation */
     int leftval=0, rightval=0; /* init leftval for unary +a or -a */
     if ( *token != '\000' ) /* or eval for binary a+b or a-b */
       if ( (leftval=evalterm(store,token)) /* evaluate left-hand term */
       == novalue ) goto end_of_job; /* return error if failed */
     if ( (rightval=evalterm(store,term+1)) /* evaluate right-hand term */
     == novalue ) goto end_of_job; /* return error if failed */
     switch ( *term ) { /* perform requested arithmetic */
       default: break; /* internal error */
       case '+': termval = leftval+rightval;  break;  /* addition */
       case '-': termval = leftval-rightval;  break;  /* subtraction */
       case '*': termval = leftval*rightval;  break;  /* multiplication */
       case '/': if ( rightval != 0 ) /* guard against divide by zero */
                   termval = leftval/rightval;  break; /* integer division */
       case '%': if ( rightval != 0 ) /* guard against divide by zero */
                   termval = leftval%rightval;  break; /*left modulo right */
       } /* --- end-of-switch(*relation) --- */
     goto end_of_job; /* return result to caller */
     } /* --- end-of-if(*term!='\000')) --- */
   /* -------------------------------------------------------------------------
   check for parenthesized expression or term of the form function(arg1,arg2,...)
   -------------------------------------------------------------------------- */
   if ( (delim = strchr(token,'(')) != NULL ) { /* token contains a ( */
     /* --- strip trailing paren (if there hopefully is one) --- */
     int  toklen = strlen(token); /* total #chars in token */
     if ( token[toklen-1] == ')' ) /* found matching ) at end of token*/
       token[--toklen] = '\000'; /* remove trailing ) */
     /* --- handle parenthesized subexpression --- */
     if ( *token == '(' ) { /* have parenthesized expression */
       strsqueeze(token,1); /* so squeeze out leading ( */
       /* --- evaluate edited term --- */
       trimwhite(token); /* trim leading/trailing whitespace*/
       termval = evalterm(store,token); } /* evaluate token recursively */
     /* --- handle function(arg1,arg2,...) --- */
     else { /* have function(arg1,arg2,...) */
       *delim = '\000'; /* separate function name and args */
       /*termval = evalfunc(store,token,delim+1);*/ } /* evaluate function */
     goto end_of_job; } /* return result to caller */
   /* -------------------------------------------------------------------------
   evaluate constants directly, or recursively evaluate variables, etc
   -------------------------------------------------------------------------- */
   if ( *token != '\000' ) { /* empty string */
     if ( isnumeric(token) ) /* have a constant */
       termval = atoi(token); /* convert ascii-to-int */
     else { /* variable or "stored proposition"*/
       termval = getstore(store,token); } /* look up token */
     } /* --- end-of-if(*token!=0) --- */
   /* -------------------------------------------------------------------------
   back to caller with truth value of proposition
   -------------------------------------------------------------------------- */
   end_of_job:
     /* --- back to caller --- */
     if ( evaltermdepth > 0 ) evaltermdepth--;  /* pop recursion depth */
     return ( termval ); /* back to caller with value */
   } /* --- end-of-function evalterm() --- */
   
   
   /* ==========================================================================
    * Function: getstore ( store, identifier )
    * Purpose: finds identifier in store and returns corresponding value
    * --------------------------------------------------------------------------
    * Arguments: store (I) (STORE *)pointer to store containing
    * the desired identifier
    * identifier (I) (char *)pointer to null-terminated string
    * containing the identifier whose value
    * is to be returned
    * --------------------------------------------------------------------------
    * Returns: ( int ) identifier's corresponding value,
    * or 0 if identifier not found (or any error)
    * --------------------------------------------------------------------------
    * Notes:     o
    * ======================================================================= */
   /* --- entry point --- */
   int getstore ( STORE *store, char *identifier )
   {
   /* -------------------------------------------------------------------------
   Allocations and Declarations
   -------------------------------------------------------------------------- */
   int value = 0; /* store[istore].value for identifier */
   int istore=0; /* store[] index containing identifier */
   char seek[512], hide[512]; /* identifier arg, identifier in store */
   /* --- first check args --- */
   if ( store==NULL || isempty(identifier)) goto end_of_job; /* missing arg */
   strninit(seek,identifier,500); /* local copy of caller's identifier */
   trimwhite(seek); /* remove leading/trailing whitespace */
   /* --- loop over store --- */
   for ( istore=0; istore<MAXSTORE; istore++ ) { /* until end-of-table */
     char *idstore = store[istore].identifier; /* ptr to identifier in store */
     if ( isempty(idstore) ) /* empty id signals eot */
       break; /* de-reference any default/error value */
     strninit(hide,idstore,500); /* local copy of store[] identifier */
     trimwhite(hide); /* remove leading/trailing whitespace */
     if ( !strcmp(hide,seek) ) /* found match */
       break; /* de-reference corresponding value */
     } /* --- end-of-for(istore) --- */
   if ( store[istore].value != NULL ) /* address of int supplied */
     value = *(store[istore].value);  /* return de-referenced int */
   end_of_job:
     return ( value ); /* store->values[istore] or NULL */
   } /* --- end-of-function getstore() --- */
   
   
   /* ==========================================================================
    * Functions: int  unescape_url ( char *url, int isescape )
    * char x2c ( char *what )
    * Purpose: unescape_url replaces 3-character sequences %xx in url
    *    with the single character represented by hex xx.
    * x2c returns the single character represented by hex xx
    *    passed as a 2-character sequence in what.
    * --------------------------------------------------------------------------
    * Arguments: url (I) char * containing null-terminated
    * string with embedded %xx sequences
    * to be converted.
    * isescape (I) int containing 1 to _not_ unescape
    * \% sequences (0 would be NCSA default)
    * what (I) char * whose first 2 characters are
    * interpreted as ascii representations
    * of hex digits.
    * --------------------------------------------------------------------------
    * Returns: ( int ) unescape_url always returns 0.
    * ( char ) x2c returns the single char
    * corresponding to hex xx passed in what.
    * --------------------------------------------------------------------------
    * Notes:     o These two functions were taken verbatim from util.c in
    *   ftp://ftp.ncsa.uiuc.edu/Web/httpd/Unix/ncsa_httpd/cgi/ncsa-default.tar.Z
    *      o Not quite "verbatim" -- I added the "isescape logic" 4-Dec-03
    * so unescape_url() can be safely applied to input which may or
    * may not have been url-encoded.  (Note: currently, all calls
    * to unescape_url() pass iescape=0, so it's not used.)
    *      o Added +++'s to blank xlation on 24-Sep-06
    *      o Added ^M,^F,etc to blank xlation 0n 01-Oct-06
    * ======================================================================= */
   /* --- entry point --- */
   int unescape_url(char *url, int isescape) {
       int x=0,y=0,prevescape=0,gotescape=0;
       int xlateplus = (isplusblank==1?1:0); /* true to xlate plus to blank */
       int strreplace(); /* replace + with blank, if needed */
       char x2c();
       static char *hex="0123456789ABCDEFabcdef";
       /* ---
        * xlate ctrl chars to blanks
        * -------------------------- */
       if ( 1 ) { /* xlate ctrl chars to blanks */
         char *ctrlchars = "\n\t\v\b\r\f\a\015";
         int  seglen = strspn(url,ctrlchars); /*initial segment with ctrlchars*/
         int  urllen = strlen(url); /* total length of url string */
         /* --- first, entirely remove ctrlchars from beginning and end --- */
         if ( seglen > 0 ) { /*have ctrlchars at start of string*/
    strsqueeze(url,seglen); /* squeeze out initial ctrlchars */
    urllen -= seglen; } /* string is now shorter */
         while ( --urllen >= 0 ) /* now remove ctrlchars from end */
    if ( isthischar(url[urllen],ctrlchars) ) /* ctrlchar at end */
     url[urllen] = '\000'; /* re-terminate string before it */
    else break; /* or we're done */
         urllen++; /* length of url string */
         /* --- now, replace interior ctrlchars with ~ blanks --- */
         while ( (seglen=strcspn(url,ctrlchars)) < urllen ) /*found a ctrlchar*/
    url[seglen] = '~'; /* replace ctrlchar with ~ */
         } /* --- end-of-if(1) --- */
       /* ---
        * xlate +'s to blanks if requested or if deemed necessary
        * ------------------------------------------------------- */
       if ( isplusblank == (-1) ) { /*determine whether or not to xlate*/
         char *searchfor[] = { " ","%20", "%2B","%2b", "+++","++",
    "+=+","+-+", NULL };
         int  isearch = 0, /* searchfor[] index */
      nfound[11] = {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}; /*#occurrences*/
         /* --- locate occurrences of searchfor[] strings in url --- */
         for ( isearch=0; searchfor[isearch] != NULL; isearch++ ) {
    char *psearch = url; /* start search at beginning */
    nfound[isearch] = 0; /* init #occurrences count */
    while ( (psearch=strstr(psearch,searchfor[isearch])) != NULL ) {
     nfound[isearch] += 1; /* count another occurrence */
     psearch += strlen(searchfor[isearch]); } /*resume search after it*/
    } /* --- end-of-for(isearch) --- */
         /* --- apply some common-sense logic --- */
         if ( nfound[0] + nfound[1] > 0 ) /* we have actual " "s or "%20"s */
    isplusblank = xlateplus = 0; /* so +++'s aren't blanks */
         if ( nfound[2] + nfound[3] > 0 ) { /* we have "%2B" for +++'s */
           if ( isplusblank != 0 ) /* and haven't disabled xlation */
     isplusblank = xlateplus = 1; /* so +++'s are blanks */
    else /* we have _both_ "%20" and "%2b" */
     xlateplus = 0; } /* tough call */
         if ( nfound[4] + nfound[5] > 0 /* we have multiple ++'s */
         ||   nfound[6] + nfound[7] > 0 ) /* or we have a +=+ or +-+ */
    if ( isplusblank != 0 ) /* and haven't disabled xlation */
     xlateplus = 1; /* so xlate +++'s to blanks */
         } /* --- end-of-if(isplusblank==-1) --- */
       if ( xlateplus > 0 ) { /* want +'s xlated to blanks */
         char *xlateto[] = { ""," "," "," + "," "," "," "," "," " };
         while ( xlateplus > 0 ) { /* still have +++'s to xlate */
    char plusses[99] = "++++++++++++++++++++"; /* longest +++ string */
    plusses[xlateplus] = '\000'; /* null-terminate +++'s */
    strreplace(url,plusses,xlateto[xlateplus],0); /* xlate +++'s */
    xlateplus--; /* next shorter +++ string */
    } /* --- end-of-while(xlateplus>0) --- */
         } /* --- end-of-if(xlateplus) --- */
       isplusblank = 0; /* don't iterate this xlation */
       /* ---
        * xlate %nn to corresponding char
        * ------------------------------- */
       for(;url[y];++x,++y) {
    gotescape = prevescape;
    prevescape = (url[x]=='\\');
    if((url[x] = url[y]) == '%')
    if(!isescape || !gotescape)
     if(isthischar(url[y+1],hex)
     && isthischar(url[y+2],hex))
       { url[x] = x2c(&url[y+1]);
         y+=2; }
       }
       url[x] = '\0';
       return 0;
   } /* --- end-of-function unescape_url() --- */
   /* --- entry point --- */
   char x2c(char *what) {
       char digit;
       digit = (what[0] >= 'A' ? ((what[0] & 0xdf) - 'A')+10 : (what[0] - '0'));
       digit *= 16;
       digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A')+10 : (what[1] - '0'));
       return(digit);
   } /* --- end-of-function x2c() --- */
 #endif /* PART2 */  #endif /* PART2 */
   
 /* ---  /* ---
Line 6012  int isleftscript = 0,  /* true if left-h Line 7202  int isleftscript = 0,  /* true if left-h
  wasscripted = 0, /* true if preceding token scripted*/   wasscripted = 0, /* true if preceding token scripted*/
  wasdelimscript = 0; /* true if preceding delim scripted*/   wasdelimscript = 0; /* true if preceding delim scripted*/
 /*int pixsz = 1;*/ /*default #bits per pixel, 1=bitmap*/  /*int pixsz = 1;*/ /*default #bits per pixel, 1=bitmap*/
   char *strdetex(); /* detex token for error message */
 /* --- global values saved/restored at each recursive iteration --- */  /* --- global values saved/restored at each recursive iteration --- */
 int wasstring = isstring, /* initial isstring mode flag */  int wasstring = isstring, /* initial isstring mode flag */
  wasdisplaystyle = isdisplaystyle, /*initial displaystyle mode flag*/   wasdisplaystyle = isdisplaystyle, /*initial displaystyle mode flag*/
Line 6037  isreplaceleft = 0;   /* reset replacelef Line 7228  isreplaceleft = 0;   /* reset replacelef
 if(1)fraccenterline = NOVALUE; /* reset \frac baseline signal */  if(1)fraccenterline = NOVALUE; /* reset \frac baseline signal */
 /* shrinkfactor = shrinkfactors[max2(0,min2(size,LARGESTSIZE))];*/ /*set sf*/  /* shrinkfactor = shrinkfactors[max2(0,min2(size,LARGESTSIZE))];*/ /*set sf*/
 shrinkfactor = shrinkfactors[max2(0,min2(size,16))]; /* have 17 sf's */  shrinkfactor = shrinkfactors[max2(0,min2(size,16))]; /* have 17 sf's */
   rastlift = 0; /* reset global rastraise() lift */
 if ( msgfp!=NULL && msglevel >= 9 ) { /*display expression for debugging*/  if ( msgfp!=NULL && msglevel >= 9 ) { /*display expression for debugging*/
  fprintf(msgfp,   fprintf(msgfp,
  "rasterize> recursion#%d, size=%d,\n\texpression=\"%s\"\n",   "rasterize> recursion#%d, size=%d,\n\texpression=\"%s\"\n",
Line 6126  while ( 1 ) Line 7318  while ( 1 )
         fontnum = 0; /* reset from \mathbb, etc */          fontnum = 0; /* reset from \mathbb, etc */
         if ( isthischar(*chartoken,ESCAPE) ) /* we got unrecognized \escape*/          if ( isthischar(*chartoken,ESCAPE) ) /* we got unrecognized \escape*/
  { /* --- so display literal {\rm~[\backslash~chartoken?]} ---  */   { /* --- so display literal {\rm~[\backslash~chartoken?]} ---  */
    strcpy(literal,"{\\rm~[\\backslash~"); /* init token */     strcpy(literal,"{\\rm~["); /* init error message token */
    strcat(literal,chartoken+1); /* add chars following leading \ */     strcat(literal,strdetex(chartoken,0)); /* detex the token */
    strcat(literal,"?]}"); } /* add closing brace */     strcat(literal,"?]}"); } /* add closing ? and brace */
         sp = rasterize(literal,size-1); /* rasterize literal token */          sp = rasterize(literal,size-1); /* rasterize literal token */
         fontnum = oldfontnum; /* reset font family */          fontnum = oldfontnum; /* reset font family */
         if ( sp == (subraster *)NULL ) continue; }/*flush if rasterize fails*/          if ( sp == (subraster *)NULL ) continue; }/*flush if rasterize fails*/
Line 6227  end_of_job: Line 7419  end_of_job:
   leftexpression = oldleftexpression; /* leftexpression reset */    leftexpression = oldleftexpression; /* leftexpression reset */
   leftsymdef = oldleftsymdef; /* leftsymdef reset */    leftsymdef = oldleftsymdef; /* leftsymdef reset */
   unitlength = oldunitlength; /* unitlength reset */    unitlength = oldunitlength; /* unitlength reset */
     iunitlength = (int)(unitlength+0.5); /* iunitlength reset */
   recurlevel--; /* unwind one recursion level */    recurlevel--; /* unwind one recursion level */
   /* --- return final subraster to caller --- */    /* --- return final subraster to caller --- */
   return ( expraster );    return ( expraster );
Line 6241  end_of_job: Line 7434  end_of_job:
  * Arguments: subexpr (I) char **  to first char of null-terminated   * Arguments: subexpr (I) char **  to first char of null-terminated
  * string beginning with a LEFTBRACES   * string beginning with a LEFTBRACES
  * to be rasterized   * to be rasterized
  * size (I) int containing 0-5 default font size   * size (I) int containing 0-7 default font size
  * basesp (I) subraster *  to character (or subexpression)   * basesp (I) subraster *  to character (or subexpression)
  * immediately preceding leading left{   * immediately preceding leading left{
  * (unused, but passed for consistency)   * (unused, but passed for consistency)
Line 6285  if ( isthischar(*expression,ESCAPE) ) /* Line 7478  if ( isthischar(*expression,ESCAPE) ) /*
 /* --- get expression *without* enclosing parens --- */  /* --- get expression *without* enclosing parens --- */
 strcpy(noparens,expression); /* get local copy of expression */  strcpy(noparens,expression); /* get local copy of expression */
 noparens[explen-(1+isescape)] = '\000'; /* null-terminate before right} */  noparens[explen-(1+isescape)] = '\000'; /* null-terminate before right} */
 strcpy(noparens,noparens+(1+isescape)); /* and then squeeze out left{ */  strsqueeze(noparens,(1+isescape)); /* and then squeeze out left{ */
 /* --- rasterize it --- */  /* --- rasterize it --- */
 if ( (sp = rasterize(noparens,size)) /*rasterize "interior" of expression*/  if ( (sp = rasterize(noparens,size)) /*rasterize "interior" of expression*/
 ==   NULL ) goto end_of_job; /* quit if failed */  ==   NULL ) goto end_of_job; /* quit if failed */
Line 6389  if ( msgfp!=NULL && msglevel>=999 ) Line 7582  if ( msgfp!=NULL && msglevel>=999 )
 if ( isstring ) goto end_of_job; /* no scripts for ascii string */  if ( isstring ) goto end_of_job; /* no scripts for ascii string */
 /* --- check for \limits or \nolimits --- */  /* --- check for \limits or \nolimits --- */
 skipwhite(exprptr); /* skip white space before \limits */  skipwhite(exprptr); /* skip white space before \limits */
 if ( exprptr != NULL ) /* expression ptr supplied */  if ( !isempty(exprptr) ) /* non-empty expression supplied */
  if ( *exprptr != '\000' ) /* something in expression */  
   exprptr = texchar(exprptr,limtoken); /* retrieve next token */    exprptr = texchar(exprptr,limtoken); /* retrieve next token */
 if ( *limtoken != '\000' ) /* have token */  if ( *limtoken != '\000' ) /* have token */
  if ( (toklen=strlen(limtoken)) >= 3 ) /* which may be \[no]limits */   if ( (toklen=strlen(limtoken)) >= 3 ) /* which may be \[no]limits */
Line 6474  end_of_job: Line 7666  end_of_job:
  * string beginning with a super/subscript,   * string beginning with a super/subscript,
  * and returning ptr immediately following   * and returning ptr immediately following
  * last script character processed.   * last script character processed.
  * size (I) int containing 0-4 default font size   * size (I) int containing 0-7 default font size
  * basesp (I) subraster *  to character (or subexpression)   * basesp (I) subraster *  to character (or subexpression)
  * immediately preceding leading script   * immediately preceding leading script
  * (scripts will be placed relative to base)   * (scripts will be placed relative to base)
Line 6627  end_of_job: Line 7819  end_of_job:
  * rasterized along with its super/subscripts,   * rasterized along with its super/subscripts,
  * and returning ptr immediately following last   * and returning ptr immediately following last
  * character processed.   * character processed.
  * size (I) int containing 0-4 default font size   * size (I) int containing 0-7 default font size
  * sp (I) subraster *  to display math operator   * sp (I) subraster *  to display math operator
  * to which super/subscripts will be added   * to which super/subscripts will be added
  * --------------------------------------------------------------------------   * --------------------------------------------------------------------------
Line 6701  end_of_job: Line 7893  end_of_job:
  * Arguments: expression (I) char **  to first char of null-terminated   * Arguments: expression (I) char **  to first char of null-terminated
  * string beginning with a \left   * string beginning with a \left
  * to be rasterized   * to be rasterized
  * size (I) int containing 0-5 default font size   * size (I) int containing 0-7 default font size
  * basesp (I) subraster *  to character (or subexpression)   * basesp (I) subraster *  to character (or subexpression)
  * immediately preceding leading left{   * immediately preceding leading left{
  * (unused, but passed for consistency)   * (unused, but passed for consistency)
Line 6858  for ( idelim=0; opdelims[idelim]!=NULL; Line 8050  for ( idelim=0; opdelims[idelim]!=NULL;
   if ( strstr(ldelim,opdelims[idelim]) != NULL ) /* found operator */    if ( strstr(ldelim,opdelims[idelim]) != NULL ) /* found operator */
     { margin += opmargin; /* extra height for operator */      { margin += opmargin; /* extra height for operator */
       if ( *ldelim == '\\' ) /* have leading escape */        if ( *ldelim == '\\' ) /* have leading escape */
  strcpy(ldelim,ldelim+1); /* squeeze it out */   {strsqueeze(ldelim,1);} /* squeeze it out */
       break; } /* no need to check rest of table */        break; } /* no need to check rest of table */
 /* --- xlate delimiters and check for textstyle --- */  /* --- xlate delimiters and check for textstyle --- */
 for ( idelim=1; idelim<=2; idelim++ ) { /* 1=left, 2=right */  for ( idelim=1; idelim<=2; idelim++ ) { /* 1=left, 2=right */
Line 6950  end_of_job: Line 8142  end_of_job:
  * Arguments: expression (I) char **  to first char of null-terminated   * Arguments: expression (I) char **  to first char of null-terminated
  * string beginning with a \right   * string beginning with a \right
  * to be rasterized   * to be rasterized
  * size (I) int containing 0-5 default font size   * size (I) int containing 0-7 default font size
  * basesp (I) subraster *  to character (or subexpression)   * basesp (I) subraster *  to character (or subexpression)
  * immediately preceding leading left{   * immediately preceding leading left{
  * (unused, but passed for consistency)   * (unused, but passed for consistency)
Line 6989  return ( sp ); Line 8181  return ( sp );
  * string immediately following \middle to be   * string immediately following \middle to be
  * rasterized, and returning ptr immediately   * rasterized, and returning ptr immediately
  * to terminating null.   * to terminating null.
  * size (I) int containing 0-5 default font size   * size (I) int containing 0-7 default font size
  * basesp (I) subraster *  to character (or subexpression)   * basesp (I) subraster *  to character (or subexpression)
  * immediately preceding \middle   * immediately preceding \middle
  * (unused, but passed for consistency)   * (unused, but passed for consistency)
Line 7169  switch ( flag ) Line 8361  switch ( flag )
     fonttable = (issupersampling?ssfonttable:aafonttable); /* set fonts */      fonttable = (issupersampling?ssfonttable:aafonttable); /* set fonts */
     break;      break;
   case ISFONTSIZE: /* set fontsize */    case ISFONTSIZE: /* set fontsize */
     case ISMAGSTEP: /* set magstep */
   case ISDISPLAYSIZE: /* set displaysize */    case ISDISPLAYSIZE: /* set displaysize */
   case ISCONTENTTYPE: /*enable/disable content-type lines*/    case ISCONTENTTYPE: /*enable/disable content-type lines*/
     case ISCONTENTCACHED: /* write content-type to cache file*/
   case ISSHRINK: /* set shrinkfactor */    case ISSHRINK: /* set shrinkfactor */
   case ISAAALGORITHM: /* set anti-aliasing algorithm */    case ISAAALGORITHM: /* set anti-aliasing algorithm */
   case ISWEIGHT: /* set font weight */    case ISWEIGHT: /* set font weight */
Line 7190  switch ( flag ) Line 8384  switch ( flag )
   if ( !isthischar(*valuearg,"?") ) /*leading ? is query for value*/    if ( !isthischar(*valuearg,"?") ) /*leading ? is query for value*/
    { isdelta = isthischar(*valuearg,"+-"); /* leading + or - */     { isdelta = isthischar(*valuearg,"+-"); /* leading + or - */
      if ( memcmp(valuearg,"--",2) == 0 ) /* leading -- signals...*/       if ( memcmp(valuearg,"--",2) == 0 ) /* leading -- signals...*/
        { isdelta=0; strcpy(valuearg,valuearg+1); } /* ...not delta */         { isdelta=0; strsqueeze(valuearg,1); } /* ...not delta */
      switch ( flag ) { /* convert to double or int */       switch ( flag ) { /* convert to double or int */
       default: argvalue = atoi(valuearg); break; /* convert to int */        default: argvalue = atoi(valuearg); break; /* convert to int */
       case ISGAMMA:        case ISGAMMA:
Line 7238  switch ( flag ) Line 8432  switch ( flag )
      { *expression = (char *)(*expression-valuelen); /*back up buff*/       { *expression = (char *)(*expression-valuelen); /*back up buff*/
        memcpy(*expression,valuearg,valuelen); } } /*and put in size*/         memcpy(*expression,valuearg,valuelen); } } /*and put in size*/
  break;   break;
         case ISMAGSTEP: /* set magstep */
    if ( argvalue != NOVALUE ) { /* got a value */
     int largestmag = 10;
     magstep = (isdelta? magstep+argvalue : argvalue);
     magstep = max2(1,min2(magstep,largestmag)); }
    break;
       case ISDISPLAYSIZE: /* set displaysize */        case ISDISPLAYSIZE: /* set displaysize */
  if ( argvalue != NOVALUE ) /* got a value */   if ( argvalue != NOVALUE ) /* got a value */
     displaysize = (isdelta? displaysize+argvalue : argvalue);      displaysize = (isdelta? displaysize+argvalue : argvalue);
Line 7246  switch ( flag ) Line 8446  switch ( flag )
  if ( argvalue != NOVALUE ) /* got a value */   if ( argvalue != NOVALUE ) /* got a value */
     isemitcontenttype = (argvalue>0?1:0);      isemitcontenttype = (argvalue>0?1:0);
  break;   break;
         case ISCONTENTCACHED: /* write content-type to cache file*/
    if ( argvalue != NOVALUE ) /* got a value */
       iscachecontenttype = (argvalue>0?1:0);
    break;
       case ISSMASH: /* set (minimum) "smash" margin */        case ISSMASH: /* set (minimum) "smash" margin */
  if ( argvalue != NOVALUE ) /* got a value */   if ( argvalue != NOVALUE ) /* got a value */
   { smashmargin = argvalue; /* set value */    { smashmargin = argvalue; /* set value */
Line 7318  switch ( flag ) Line 8522  switch ( flag )
       { *expression = texsubexpr(*expression,valuearg,1023,"{","}",0,0);        { *expression = texsubexpr(*expression,valuearg,1023,"{","}",0,0);
  if ( *valuearg != '\000' ) /* guard against empty string */   if ( *valuearg != '\000' ) /* guard against empty string */
   unitlength = strtod(valuearg,NULL); } /* convert to double */    unitlength = strtod(valuearg,NULL); } /* convert to double */
       iunitlength = (int)(unitlength+0.5); /* iunitlength reset */
     break;      break;
   } /* --- end-of-switch(flag) --- */    } /* --- end-of-switch(flag) --- */
 return ( NULL ); /*just set value, nothing to display*/  return ( NULL ); /*just set value, nothing to display*/
Line 7363  int baseht=1, baseln=0;  /* height,basel Line 8568  int baseht=1, baseln=0;  /* height,basel
 int pixsz = 1; /*default #bits per pixel, 1=bitmap*/  int pixsz = 1; /*default #bits per pixel, 1=bitmap*/
 int isstar=0, minspace=0; /* defaults for negative hspace */  int isstar=0, minspace=0; /* defaults for negative hspace */
 char *texsubexpr(), widtharg[256]; /* parse for optional {width} */  char *texsubexpr(), widtharg[256]; /* parse for optional {width} */
   int evalterm(), evalue=0; /* evaluate [args], {args} */
 subraster *rasterize(), *rightsp=NULL; /*rasterize right half of expression*/  subraster *rasterize(), *rightsp=NULL; /*rasterize right half of expression*/
 subraster *rastcat(); /* cat rightsp after \hfill */  subraster *rastcat(); /* cat rightsp after \hfill */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
Line 7380  if ( width == 0 ) {   /* width specified Line 8586  if ( width == 0 ) {   /* width specified
   int minwidth = (isfill||isheight?1:-600); /* \hspace allows negative */    int minwidth = (isfill||isheight?1:-600); /* \hspace allows negative */
   /* --- check if optional [minspace] given for negative \hspace --- */    /* --- check if optional [minspace] given for negative \hspace --- */
   if ( *(*expression) == '[' ) { /* [minspace] if leading char is [ */    if ( *(*expression) == '[' ) { /* [minspace] if leading char is [ */
     /* ---parse [minspace], bump expression past it, interpret as double--- */      /* ---parse [minspace], bump expression past it, evaluate expression--- */
     *expression = texsubexpr(*expression,widtharg,127,"[","]",0,0);      *expression = texsubexpr(*expression,widtharg,127,"[","]",0,0);
     if ( *widtharg != '\000' ) /* got [minspace] */      if ( !isempty(widtharg) ) { /* got [minspace] */
       minspace = iround(unitlength*strtod(widtharg,NULL)); /* in pixels */        evalue = evalterm(mimestore,widtharg); /* evaluate widtharg expr */
         minspace = iround(unitlength*((double)evalue)); } /* in pixels */
     } /* --- end-of-if(*(*expression)=='[') --- */      } /* --- end-of-if(*(*expression)=='[') --- */
   width = 1; /* set default width */    width = 1; /* set default width */
   *expression = texsubexpr(*expression,widtharg,255,"{","}",0,0);    *expression = texsubexpr(*expression,widtharg,255,"{","}",0,0);
   dwidth = unitlength*strtod(widtharg,NULL); /* scaled width value */    dwidth = unitlength*((double)evalterm(mimestore,widtharg)); /* scaled */
   widthval = /* convert {width} to integer */    widthval = /* convert {width} to integer */
  (int)( dwidth + (dwidth>=0.0?0.5:(-0.5)) );   (int)( dwidth + (dwidth>=0.0?0.5:(-0.5)) );
   if ( widthval>=minwidth && widthval<=600 ) /* sanity check */    if ( widthval>=minwidth && widthval<=600 ) /* sanity check */
Line 7466  end_of_job: Line 8673  end_of_job:
  * string immediately following \\ to be   * string immediately following \\ to be
  * rasterized, and returning ptr immediately   * rasterized, and returning ptr immediately
  * to terminating null.   * to terminating null.
  * size (I) int containing 0-5 default font size   * size (I) int containing 0-7 default font size
  * basesp (I) subraster *  to character (or subexpression)   * basesp (I) subraster *  to character (or subexpression)
  * immediately preceding \\   * immediately preceding \\
  * (unused, but passed for consistency)   * (unused, but passed for consistency)
Line 7490  Allocations and Declarations Line 8697  Allocations and Declarations
 subraster *rastack(), *newlsp=NULL; /* subraster for both lines */  subraster *rastack(), *newlsp=NULL; /* subraster for both lines */
 subraster *rasterize(), *rightsp=NULL; /*rasterize right half of expression*/  subraster *rasterize(), *rightsp=NULL; /*rasterize right half of expression*/
 char *texsubexpr(), spacexpr[129]/*, *xptr=spacexpr*/; /*for \\[vspace]*/  char *texsubexpr(), spacexpr[129]/*, *xptr=spacexpr*/; /*for \\[vspace]*/
 double strtod(); /* convert ascii param to double */  int evalterm(), evalue=0; /* evaluate [arg], {arg} */
 int vspace = size+2; /* #pixels between lines */  int vspace = size+2; /* #pixels between lines */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 obtain optional [vspace] argument immediately following \\ command  obtain optional [vspace] argument immediately following \\ command
Line 7501  if ( *(*expression) == '[' )  /*have [vs Line 8708  if ( *(*expression) == '[' )  /*have [vs
   /* ---parse [vspace] and bump expression past it, interpret as double--- */    /* ---parse [vspace] and bump expression past it, interpret as double--- */
   *expression = texsubexpr(*expression,spacexpr,127,"[","]",0,0);    *expression = texsubexpr(*expression,spacexpr,127,"[","]",0,0);
   if ( *spacexpr == '\000' ) goto end_of_job; /* couldn't get [vspace] */    if ( *spacexpr == '\000' ) goto end_of_job; /* couldn't get [vspace] */
   vspace = iround(unitlength*strtod(spacexpr,NULL)); /* vspace in pixels */    evalue = evalterm(mimestore,spacexpr); /* evaluate [space] arg */
     vspace = iround(unitlength*((double)evalue)); /* vspace in pixels */
   } /* --- end-of-if(*(*expression)=='[') --- */    } /* --- end-of-if(*(*expression)=='[') --- */
 if ( leftexpression == NULL ) goto end_of_job; /* nothing preceding \\ */  if ( leftexpression == NULL ) goto end_of_job; /* nothing preceding \\ */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
Line 7567  char *texscripts(), sub[1024],super[1024 Line 8775  char *texscripts(), sub[1024],super[1024
 subraster *rasterize(), *subsp=NULL,*supsp=NULL; /*rasterize limits*/  subraster *rasterize(), *subsp=NULL,*supsp=NULL; /*rasterize limits*/
 subraster *new_subraster(), *rastack(), *spacesp=NULL; /*space below arrow*/  subraster *new_subraster(), *rastack(), *spacesp=NULL; /*space below arrow*/
 int delete_subraster(); /*free work areas in case of error*/  int delete_subraster(); /*free work areas in case of error*/
 double strtod(); /* convert ascii [width] to value */  int evalterm(); /* evaluate [arg], {arg} */
 int width = 10 + 8*size,  height; /* width, height for \longxxxarrow */  int width = 10 + 8*size,  height; /* width, height for \longxxxarrow */
 int islimits = 1; /*true to handle limits internally*/  int islimits = 1; /*true to handle limits internally*/
 int limsize = size-1; /* font size for limits */  int limsize = size-1; /* font size for limits */
Line 7581  if ( *(*expression) == '[' )  /*check fo Line 8789  if ( *(*expression) == '[' )  /*check fo
   { int widthval; /* test [width] before using it */    { int widthval; /* test [width] before using it */
     *expression = texsubexpr(*expression,widtharg,255,"[","]",0,0);      *expression = texsubexpr(*expression,widtharg,255,"[","]",0,0);
     widthval = /* convert [width] to integer */      widthval = /* convert [width] to integer */
  (int)((unitlength*strtod(widtharg,NULL))+0.5);   (int)((unitlength*((double)evalterm(mimestore,widtharg)))+0.5);
     if ( widthval>=2 && widthval<=600 ) /* sanity check */      if ( widthval>=2 && widthval<=600 ) /* sanity check */
       width = widthval; } /* replace deafault width */        width = widthval; } /* replace deafault width */
 /* --- now parse for limits, and bump expression past it(them) --- */  /* --- now parse for limits, and bump expression past it(them) --- */
Line 7661  char *texsubexpr(), heightarg[256]; /* p Line 8869  char *texsubexpr(), heightarg[256]; /* p
 char *texscripts(), sub[1024],super[1024]; /* and _^limits after [width]*/  char *texscripts(), sub[1024],super[1024]; /* and _^limits after [width]*/
 subraster *rasterize(), *subsp=NULL,*supsp=NULL; /*rasterize limits*/  subraster *rasterize(), *subsp=NULL,*supsp=NULL; /*rasterize limits*/
 subraster *rastcat(); /* cat superscript left, sub right */  subraster *rastcat(); /* cat superscript left, sub right */
 double strtod(); /* convert ascii [height] to value */  int evalterm(); /* evaluate [arg], {arg} */
 int height = 8 + 2*size,  width; /* height, width for \longxxxarrow */  int height = 8 + 2*size,  width; /* height, width for \longxxxarrow */
 int islimits = 1; /*true to handle limits internally*/  int islimits = 1; /*true to handle limits internally*/
 int limsize = size-1; /* font size for limits */  int limsize = size-1; /* font size for limits */
Line 7674  if ( *(*expression) == '[' )  /*check fo Line 8882  if ( *(*expression) == '[' )  /*check fo
   { int heightval; /* test height before using it */    { int heightval; /* test height before using it */
     *expression = texsubexpr(*expression,heightarg,255,"[","]",0,0);      *expression = texsubexpr(*expression,heightarg,255,"[","]",0,0);
     heightval = /* convert [height] to integer */      heightval = /* convert [height] to integer */
  (int)((unitlength*strtod(heightarg,NULL))+0.5);   (int)((unitlength*((double)evalterm(mimestore,heightarg)))+0.5);
     if ( heightval>=2 && heightval<=600 ) /* sanity check */      if ( heightval>=2 && heightval<=600 ) /* sanity check */
       height = heightval; } /* replace deafault height */        height = heightval; } /* replace deafault height */
 /* --- now parse for limits, and bump expression past it(them) --- */  /* --- now parse for limits, and bump expression past it(them) --- */
Line 7728  end_of_job: Line 8936  end_of_job:
  * string immediately following overlay \cmd to   * string immediately following overlay \cmd to
  * be rasterized, and returning ptr immediately   * be rasterized, and returning ptr immediately
  * following last character processed.   * following last character processed.
  * size (I) int containing 0-5 default font size   * size (I) int containing 0-7 default font size
  * basesp (I) subraster *  to character (or subexpression)   * basesp (I) subraster *  to character (or subexpression)
  * immediately preceding overlay \cmd   * immediately preceding overlay \cmd
  * (unused, but passed for consistency)   * (unused, but passed for consistency)
Line 7757  char *texsubexpr(),   /*parse expression Line 8965  char *texsubexpr(),   /*parse expression
 subraster *rasterize(), *sp1=NULL, *sp2=NULL, /*rasterize 1=base, 2=overlay*/  subraster *rasterize(), *sp1=NULL, *sp2=NULL, /*rasterize 1=base, 2=overlay*/
  *new_subraster(); /*explicitly alloc sp2 if necessary*/   *new_subraster(); /*explicitly alloc sp2 if necessary*/
 subraster *rastcompose(), *overlaysp=NULL; /*subraster for composite overlay*/  subraster *rastcompose(), *overlaysp=NULL; /*subraster for composite overlay*/
   int isalign = 0; /* true to align baselines */
 int line_raster(); /* draw diagonal for \Not */  int line_raster(); /* draw diagonal for \Not */
   int evalterm(); /* evaluate [arg], {arg} */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 Obtain base, and maybe overlay, and rasterize them  Obtain base, and maybe overlay, and rasterize them
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
Line 7766  if ( offset2 == NOVALUE )  /* only if no Line 8976  if ( offset2 == NOVALUE )  /* only if no
  if ( *(*expression) == '[' ) /*check for []-enclosed optional arg*/   if ( *(*expression) == '[' ) /*check for []-enclosed optional arg*/
   { int offsetval; /* test before using it */    { int offsetval; /* test before using it */
     *expression = texsubexpr(*expression,expr2,511,"[","]",0,0);      *expression = texsubexpr(*expression,expr2,511,"[","]",0,0);
     offsetval = (int)(strtod(expr2,NULL)+0.5); /* convert [offset2] to int */      offsetval = /* convert [offset2] to int */
    (int)(((double)evalterm(mimestore,expr2))+0.5);
     if ( abs(offsetval) <= 25 ) /* sanity check */      if ( abs(offsetval) <= 25 ) /* sanity check */
       offset2 = offsetval; } /* replace deafault */        offset2 = offsetval; } /* replace deafault */
 if ( offset2 == NOVALUE ) offset2 = 0; /* novalue means no offset */  if ( offset2 == NOVALUE ) offset2 = 0; /* novalue means no offset */
 /* --- parse for base, bump expression past it, and rasterize it --- */  /* --- parse for base, bump expression past it, and rasterize it --- */
 *expression = texsubexpr(*expression,expr1,511,"{","}",0,0);  *expression = texsubexpr(*expression,expr1,511,"{","}",0,0);
 if ( *expr1 == '\000' ) goto end_of_job; /* nothing to overlay, so quit */  if ( isempty(expr1) ) goto end_of_job; /* nothing to overlay, so quit */
   rastlift1 = rastlift = 0; /* reset all raisebox() lifts */
   if ( strstr(expr1,"\\raise") != NULL ) /* have a \raisebox */
     isalign = 2; /* so align baselines */
 if ( (sp1=rasterize(expr1,size)) /* rasterize base expression */  if ( (sp1=rasterize(expr1,size)) /* rasterize base expression */
 ==   NULL ) goto end_of_job; /* quit if failed to rasterize */  ==   NULL ) goto end_of_job; /* quit if failed to rasterize */
 overlaysp = sp1; /*in case we return with no overlay*/  overlaysp = sp1; /*in case we return with no overlay*/
   rastlift1 = rastlift; /* save lift for base expression */
 /* --- get overlay expression, and rasterize it --- */  /* --- get overlay expression, and rasterize it --- */
 if ( overlay == NOVALUE ) /* get overlay from input stream */  if ( overlay == NOVALUE ) /* get overlay from input stream */
   { *expression = texsubexpr(*expression,expr2,511,"{","}",0,0);    { *expression = texsubexpr(*expression,expr2,511,"{","}",0,0);
     if ( *expr2 != '\000' ) /* have an overlay */      if ( !isempty(expr2) ) { /* have an overlay */
       sp2 = rasterize(expr2,size); } /* so rasterize overlay expression */        if ( strstr(expr2,"\\raise") != NULL ) /* have a \raisebox */
 else /* specific overlay */   isalign = 2; /* so align baselines */
         sp2 = rasterize(expr2,size); } } /* rasterize overlay expression */
   else /* use specific built-in overlay */
   switch ( overlay )    switch ( overlay )
     {      {
     default: break;      default: break;
     case 1: /* e.g., \not overlays slash */      case 1: /* e.g., \not overlays slash */
       sp2 = rasterize("/",size+1); /* rasterize overlay expression */        sp2 = rasterize("/",size+1); /* rasterize overlay expression */
         isalign = 0; /* automatically handled correctly */
       offset2 = max2(1,size-3); /* push / right a bit */        offset2 = max2(1,size-3); /* push / right a bit */
       offset2 = 0;        offset2 = 0;
       break;        break;
     case 2: /* e.g., \Not draws diagonal */      case 2: /* e.g., \Not draws diagonal */
       sp2 = NULL; /* no overlay required */        sp2 = NULL; /* no overlay required */
         isalign = 0; /* automatically handled correctly */
       if ( overlaysp != NULL ) /* check that we have raster */        if ( overlaysp != NULL ) /* check that we have raster */
  { raster *rp = overlaysp->image; /* raster to be \Not-ed */   { raster *rp = overlaysp->image; /* raster to be \Not-ed */
   int width=rp->width, height=rp->height; /* raster dimensions */    int width=rp->width, height=rp->height; /* raster dimensions */
Line 7806  else     /* specific overlay */ Line 9025  else     /* specific overlay */
     case 3: /* e.g., \sout for strikeout */      case 3: /* e.g., \sout for strikeout */
       sp2 = NULL; /* no overlay required */        sp2 = NULL; /* no overlay required */
       if ( overlaysp != NULL ) /* check that we have raster */        if ( overlaysp != NULL ) /* check that we have raster */
  { raster *rp = overlaysp->image; /* raster to be \Not-ed */   { raster *rp = overlaysp->image; /* raster to be \sout-ed */
   int width=rp->width, height=rp->height; /* raster dimensions */    int width=rp->width, height=rp->height; /* raster dimensions */
   int baseline = overlaysp->baseline; /* we'll ignore descenders */    int baseline = (overlaysp->baseline)-rastlift; /*skip descenders*/
   int midrow = max2(0,min2(height-1,offset2+((baseline+1)/2)));    int midrow = max2(0,min2(height-1,offset2+((baseline+1)/2)));
   if ( 1 ) /* strikeout within bounding box */    if ( 1 ) /* strikeout within bounding box */
     line_raster(rp,midrow,0,midrow,width-1,1); } /*draw strikeout*/      line_raster(rp,midrow,0,midrow,width-1,1); } /*draw strikeout*/
Line 7818  if ( sp2 == NULL ) goto end_of_job; /*re Line 9037  if ( sp2 == NULL ) goto end_of_job; /*re
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 construct composite overlay  construct composite overlay
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 overlaysp = rastcompose(sp1,sp2,offset2,0,3);  overlaysp = rastcompose(sp1,sp2,offset2,isalign,3);
 end_of_job:  end_of_job:
   return ( overlaysp );    return ( overlaysp );
 } /* --- end-of-function rastoverlay() --- */  } /* --- end-of-function rastoverlay() --- */
Line 7833  end_of_job: Line 9052  end_of_job:
  * string immediately following \frac to be   * string immediately following \frac to be
  * rasterized, and returning ptr immediately   * rasterized, and returning ptr immediately
  * following last character processed.   * following last character processed.
  * size (I) int containing 0-5 default font size   * size (I) int containing 0-7 default font size
  * basesp (I) subraster *  to character (or subexpression)   * basesp (I) subraster *  to character (or subexpression)
  * immediately preceding \frac   * immediately preceding \frac
  * (unused, but passed for consistency)   * (unused, but passed for consistency)
Line 7945  end_of_job: Line 9164  end_of_job:
  * string immediately following \stackrel to be   * string immediately following \stackrel to be
  * rasterized, and returning ptr immediately   * rasterized, and returning ptr immediately
  * following last character processed.   * following last character processed.
  * size (I) int containing 0-4 default font size   * size (I) int containing 0-7 default font size
  * basesp (I) subraster *  to character (or subexpression)   * basesp (I) subraster *  to character (or subexpression)
  * immediately preceding \stackrel   * immediately preceding \stackrel
  * (unused, but passed for consistency)   * (unused, but passed for consistency)
Line 8017  end_of_job: Line 9236  end_of_job:
  * string immediately following \mathfunc to be   * string immediately following \mathfunc to be
  * rasterized, and returning ptr immediately   * rasterized, and returning ptr immediately
  * following last character processed.   * following last character processed.
  * size (I) int containing 0-4 default font size   * size (I) int containing 0-7 default font size
  * basesp (I) subraster *  to character (or subexpression)   * basesp (I) subraster *  to character (or subexpression)
  * immediately preceding \mathfunc   * immediately preceding \mathfunc
  * (unused, but passed for consistency)   * (unused, but passed for consistency)
Line 8124  end_of_job: Line 9343  end_of_job:
  * string immediately following \sqrt to be   * string immediately following \sqrt to be
  * rasterized, and returning ptr immediately   * rasterized, and returning ptr immediately
  * following last character processed.   * following last character processed.
  * size (I) int containing 0-4 default font size   * size (I) int containing 0-7 default font size
  * basesp (I) subraster *  to character (or subexpression)   * basesp (I) subraster *  to character (or subexpression)
  * immediately preceding \accent   * immediately preceding \accent
  * (unused, but passed for consistency)   * (unused, but passed for consistency)
Line 8184  subwidth  = (subsp->image)->width; /* an Line 9403  subwidth  = (subsp->image)->width; /* an
 pixsz     = (subsp->image)->pixsz; /* pixsz remains constant */  pixsz     = (subsp->image)->pixsz; /* pixsz remains constant */
 /* --- determine height and width of sqrt to contain subexpr --- */  /* --- determine height and width of sqrt to contain subexpr --- */
 sqrtheight = subheight + overspace; /* subexpr + blank line + overbar */  sqrtheight = subheight + overspace; /* subexpr + blank line + overbar */
 surdwidth  = SQRTWIDTH(sqrtheight); /* width of surd */  surdwidth  = SQRTWIDTH(sqrtheight,(rootheight<1?2:1)); /* width of surd */
 sqrtwidth  = subwidth + surdwidth + 1; /* total width */  sqrtwidth  = subwidth + surdwidth + 1; /* total width */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 construct sqrt (with room to move in subexpr) and embed subexpr in it  construct sqrt (with room to move in subexpr) and embed subexpr in it
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 /* --- construct sqrt --- */  /* --- construct sqrt --- */
 if ( (sqrtsp=accent_subraster(SQRTACCENT,sqrtwidth,sqrtheight,pixsz))  if ( (sqrtsp=accent_subraster(SQRTACCENT,
   (rootheight<1?sqrtwidth:(-sqrtwidth)),sqrtheight,0,pixsz))
 ==   NULL ) goto end_of_job; /* quit if failed to build sqrt */  ==   NULL ) goto end_of_job; /* quit if failed to build sqrt */
 /* --- embed subexpr in sqrt at lower-right corner--- */  /* --- embed subexpr in sqrt at lower-right corner--- */
 rastput(sqrtsp->image,subsp->image,overspace,sqrtwidth-subwidth,1);  rastput(sqrtsp->image,subsp->image,overspace,sqrtwidth-subwidth,1);
Line 8234  end_of_job: Line 9454  end_of_job:
  * string immediately following \accent to be   * string immediately following \accent to be
  * rasterized, and returning ptr immediately   * rasterized, and returning ptr immediately
  * following last character processed.   * following last character processed.
  * size (I) int containing 0-4 default font size   * size (I) int containing 0-7 default font size
  * basesp (I) subraster *  to character (or subexpression)   * basesp (I) subraster *  to character (or subexpression)
  * immediately preceding \accent   * immediately preceding \accent
  * (unused, but passed for consistency)   * (unused, but passed for consistency)
Line 8267  char *texscripts(), *script=NULL, /* \un Line 9487  char *texscripts(), *script=NULL, /* \un
 subraster *rasterize(), *subsp=NULL, *scrsp=NULL; /*rasterize subexpr,script*/  subraster *rasterize(), *subsp=NULL, *scrsp=NULL; /*rasterize subexpr,script*/
 subraster *rastack(), *accsubsp=NULL; /* stack accent, subexpr, script */  subraster *rastack(), *accsubsp=NULL; /* stack accent, subexpr, script */
 subraster *accent_subraster(), *accsp=NULL; /*raster for the accent itself*/  subraster *accent_subraster(), *accsp=NULL; /*raster for the accent itself*/
 int accheight=0, accwidth=0, /* height, width of accent */  int accheight=0, accwidth=0, accdir=0,/*accent height, width, direction*/
  subheight=0, subwidth=0, pixsz=0; /* height,width,pixsz of subexpr */   subheight=0, subwidth=0, pixsz=0; /* height,width,pixsz of subexpr */
 int delete_subraster(); /*free work areas in case of error*/  int delete_subraster(); /*free work areas in case of error*/
 int vspace = 0; /*vertical space between accent,sub*/  int vspace = 0; /*vertical space between accent,sub*/
Line 8298  switch ( accent ) Line 9518  switch ( accent )
     break;      break;
   case VECACCENT:    case VECACCENT:
     vspace = 1; /* set 1-pixel vertical space */      vspace = 1; /* set 1-pixel vertical space */
       accdir = isscript; /* +1=right,-1=left,0=lr; +10for==>*/
       isscript = 0; /* >>don't<< signal sub/supscript */
   case HATACCENT:    case HATACCENT:
     accheight = 7; /* default */      accheight = 7; /* default */
     if ( subwidth < 10 ) accheight = 5; /* unless small width */      if ( subwidth < 10 ) accheight = 5; /* unless small width */
Line 8309  accheight = min2(accheight,subheight); / Line 9531  accheight = min2(accheight,subheight); /
 construct accent, and construct subraster with accent over (or under) subexpr  construct accent, and construct subraster with accent over (or under) subexpr
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 /* --- first construct accent --- */  /* --- first construct accent --- */
 if ( (accsp = accent_subraster(accent,accwidth,accheight,pixsz)) /* accent */  if ( (accsp = accent_subraster(accent,accwidth,accheight,accdir,pixsz))
 ==   NULL ) goto end_of_job; /* quit if failed to build accent */  ==   NULL ) goto end_of_job; /* quit if failed to build accent */
 /* --- now stack accent above (or below) subexpr, and free both args --- */  /* --- now stack accent above (or below) subexpr, and free both args --- */
 accsubsp = (isabove? rastack(subsp,accsp,1,vspace,1,3)/*accent above subexpr*/  accsubsp = (isabove? rastack(subsp,accsp,1,vspace,1,3)/*accent above subexpr*/
Line 8352  end_of_job: Line 9574  end_of_job:
  * string immediately following \font to be   * string immediately following \font to be
  * rasterized, and returning ptr immediately   * rasterized, and returning ptr immediately
  * following last character processed.   * following last character processed.
  * size (I) int containing 0-5 default font size   * size (I) int containing 0-7 default font size
  * basesp (I) subraster *  to character (or subexpression)   * basesp (I) subraster *  to character (or subexpression)
  * immediately preceding \accent   * immediately preceding \accent
  * (unused, but passed for consistency)   * (unused, but passed for consistency)
Line 8384  subraster *rasterize(), *fontsp=NULL, /* Line 9606  subraster *rasterize(), *fontsp=NULL, /*
 int oldsmashmargin = smashmargin; /* turn off smash in text mode */  int oldsmashmargin = smashmargin; /* turn off smash in text mode */
 #if 0  #if 0
 /* --- fonts recognized by rastfont --- */  /* --- fonts recognized by rastfont --- */
 static int  nfonts = 6; /* legal font #'s are 1...nfonts */  static int  nfonts = 11; /* legal font #'s are 1...nfonts */
 static struct {char *name; int class;}  static struct {char *name; int class;}
   fonts[] =    fonts[] =
     { /* --- name  class 1=upper,2=alpha,3=alnum,4=lower,5=digit,9=all --- */      { /* --- name  class 1=upper,2=alpha,3=alnum,4=lower,5=digit,9=all --- */
Line 8397  static struct {char *name; int class;} Line 9619  static struct {char *name; int class;}
  { "\\mathbf", -1 }, /*(6) \bf,\mathbf{abc}-->{\bf~abc} */   { "\\mathbf", -1 }, /*(6) \bf,\mathbf{abc}-->{\bf~abc} */
  { "\\mathrm",   -1 }, /*(7) \mathrm */   { "\\mathrm",   -1 }, /*(7) \mathrm */
  { "\\cyr",      -1 }, /*(8) \cyr */   { "\\cyr",      -1 }, /*(8) \cyr */
    { "\\textgreek",-1 }, /*(9) \textgreek */
    { "\\textbfgreek",CMMI10BGR,1,-1 },/*(10) \textbfgreek{ab} */
    { "\\textbbgreek",BBOLD10GR,1,-1 },/*(11) \textbbgreek{ab} */
  { NULL, 0 }   { NULL, 0 }
     } ; /* --- end-of-fonts[] --- */      } ; /* --- end-of-fonts[] --- */
 #endif  #endif
Line 8513  end_of_job: Line 9738  end_of_job:
  * string immediately following \begin to be   * string immediately following \begin to be
  * rasterized, and returning ptr immediately   * rasterized, and returning ptr immediately
  * following last character processed.   * following last character processed.
  * size (I) int containing 0-4 default font size   * size (I) int containing 0-7 default font size
  * basesp (I) subraster *  to character (or subexpression)   * basesp (I) subraster *  to character (or subexpression)
  * immediately preceding \begin   * immediately preceding \begin
  * (unused, but passed for consistency)   * (unused, but passed for consistency)
Line 8574  blevel++;    /* count \begin...\begin... Line 9799  blevel++;    /* count \begin...\begin...
 exprptr = texsubexpr(*expression,subexpr,0,"{","}",0,0);  exprptr = texsubexpr(*expression,subexpr,0,"{","}",0,0);
 if ( *subexpr == '\000' ) goto end_of_job; /* no environment given */  if ( *subexpr == '\000' ) goto end_of_job; /* no environment given */
 while ( (delims=strchr(subexpr,'*')) != NULL ) /* have environment* */  while ( (delims=strchr(subexpr,'*')) != NULL ) /* have environment* */
   strcpy(delims,delims+1); /* treat it as environment */    {strsqueeze(delims,1);} /* treat it as environment */
 /* --- look up environment in our table --- */  /* --- look up environment in our table --- */
 for ( ienviron=0; ;ienviron++ ) /* search table till NULL */  for ( ienviron=0; ;ienviron++ ) /* search table till NULL */
   if ( environs[ienviron] == NULL ) /* found NULL before match */    if ( environs[ienviron] == NULL ) /* found NULL before match */
Line 8744  end_of_job: Line 9969  end_of_job:
  * string immediately following \array to be   * string immediately following \array to be
  * rasterized, and returning ptr immediately   * rasterized, and returning ptr immediately
  * following last character processed.   * following last character processed.
  * size (I) int containing 0-4 default font size   * size (I) int containing 0-7 default font size
  * basesp (I) subraster *  to character (or subexpression)   * basesp (I) subraster *  to character (or subexpression)
  * immediately preceding \array   * immediately preceding \array
  * (unused, but passed for consistency)   * (unused, but passed for consistency)
Line 8783  char *texsubexpr(), subexpr[MAXSUBXSZ+1] Line 10008  char *texsubexpr(), subexpr[MAXSUBXSZ+1]
  token[MAXTOKNSZ+1],  *tokptr=token, /* subexpr token to rasterize */   token[MAXTOKNSZ+1],  *tokptr=token, /* subexpr token to rasterize */
  *preamble(),   *preptr=token; /*process optional size,lcr preamble*/   *preamble(),   *preptr=token; /*process optional size,lcr preamble*/
 char *coldelim="&", *rowdelim="\\"; /* need escaped rowdelim */  char *coldelim="&", *rowdelim="\\"; /* need escaped rowdelim */
 int maxarraysz = 64; /* max #rows, cols */  int maxarraysz = 63; /* max #rows, cols */
 int justify[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* -1,0,+1 = l,c,r */  int justify[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* -1,0,+1 = l,c,r */
                0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,                 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},                 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
Line 8808  int justify[65]={0,0,0,0,0,0,0,0,0,0,0,0 Line 10033  int justify[65]={0,0,0,0,0,0,0,0,0,0,0,0
       rowbaseln[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* baseline for row */        rowbaseln[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* baseline for row */
                0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,                 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},                 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
         vrowspace[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*extra //[len]space*/
                  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
       rowcenter[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*true = vcenter row*/        rowcenter[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*true = vcenter row*/
                0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,                 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};                 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
Line 8856  char *texchar(), hltoken[1025]; /* extra Line 10084  char *texchar(), hltoken[1025]; /* extra
 int ishonly=0, hltoklen, minhltoklen=3; /*flag, token must be \hl or \hd*/  int ishonly=0, hltoklen, minhltoklen=3; /*flag, token must be \hl or \hd*/
 int isnewrow=1; /* true for new row */  int isnewrow=1; /* true for new row */
 int pixsz = 1; /*default #bits per pixel, 1=bitmap*/  int pixsz = 1; /*default #bits per pixel, 1=bitmap*/
   int evalterm(), evalue=0; /* evaluate [arg], {arg} */
   static int mydaemonlevel = 0; /* check against global daemonlevel*/
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 Macros to determine extra raster space required for vline/hline  Macros to determine extra raster space required for vline/hline
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
Line 8876  if ( msglevel>=29 && msgfp!=NULL ) /* de Line 10106  if ( msglevel>=29 && msgfp!=NULL ) /* de
 if ( *(subexpr+2)=='\000' ) /* couldn't get subexpression */  if ( *(subexpr+2)=='\000' ) /* couldn't get subexpression */
   goto end_of_job; /* nothing to do, so quit */    goto end_of_job; /* nothing to do, so quit */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
   reset static arrays if main re-entered as daemon (or dll)
   -------------------------------------------------------------------------- */
   if ( mydaemonlevel != daemonlevel ) { /* main re-entered */
     for ( icol=0; icol<=maxarraysz; icol++ ) /* for each array[] index */
       gjustify[icol]    = gcolwidth[icol]   = growheight[icol] =
       gfixcolsize[icol] = gfixrowsize[icol] = growcenter[icol] = 0;
     mydaemonlevel = daemonlevel; } /* update mydaemonlevel */
   /* -------------------------------------------------------------------------
 process optional size,lcr preamble if present  process optional size,lcr preamble if present
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 /* --- reset size, get lcr's, and push exprptr past preamble --- */  /* --- reset size, get lcr's, and push exprptr past preamble --- */
Line 8981  if ( msglevel>=29 && msgfp!=NULL ) /* de Line 10219  if ( msglevel>=29 && msgfp!=NULL ) /* de
 tokenize and rasterize components  a & b \\ c & d \\ etc  of subexpr  tokenize and rasterize components  a & b \\ c & d \\ etc  of subexpr
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 /* --- rasterize tokens one at a time, and maintain row,col counts --- */  /* --- rasterize tokens one at a time, and maintain row,col counts --- */
   nrows = 0; /* start with top row */
 ncols[nrows] = 0; /* no tokens/cols in top row yet */  ncols[nrows] = 0; /* no tokens/cols in top row yet */
 while ( 1 ) /* scan chars till end */  while ( 1 ) /* scan chars till end */
   {    {
Line 9026  while ( 1 )    /* scan chars till end */ Line 10265  while ( 1 )    /* scan chars till end */
   if ( iseoc ) /* we have a completed token */    if ( iseoc ) /* we have a completed token */
     {      {
     *tokptr = '\000'; /* first, null-terminate token */      *tokptr = '\000'; /* first, null-terminate token */
     /* --- check first token in row for \hline or \hdash --- */      /* --- check first token in row for [len] and/or \hline or \hdash --- */
     ishonly = 0; /*init for token not only an \hline*/      ishonly = 0; /*init for token not only an \hline*/
     if ( ncols[nrows] == 0 ) /*\hline must be first token in row*/      if ( ncols[nrows] == 0 ) /*\hline must be first token in row*/
       {        {
       tokptr=token; skipwhite(tokptr); /* skip whitespace after // */        tokptr=token; skipwhite(tokptr); /* skip whitespace after // */
         /* --- first check for optional [len] --- */
         if ( *tokptr == '[' ) { /* have [len] if leading char is [ */
           /* ---parse [len] and bump tokptr past it, interpret as double--- */
           char lenexpr[128];  int len; /* chars between [...] as int */
           tokptr = texsubexpr(tokptr,lenexpr,127,"[","]",0,0);
           if ( *lenexpr != '\000' ) { /* got [len] expression */
     evalue = evalterm(mimestore,lenexpr); /* evaluate len expression */
             len = iround(unitlength*((double)evalue)); /* len in pixels */
             if ( len>=(-63) && len<=255 ) { /* sanity check */
               vrowspace[nrows] = len; /* extra vspace before this row */
       strsqueezep(token,tokptr); /* flush [len] from token */
               tokptr=token; skipwhite(tokptr); } } /* reset ptr, skip white */
           } /* --- end-of-if(*tokptr=='[') --- */
         /* --- now check for \hline or \hdash --- */
       tokptr = texchar(tokptr,hltoken); /* extract first char from token */        tokptr = texchar(tokptr,hltoken); /* extract first char from token */
       hltoklen = strlen(hltoken); /* length of first char */        hltoklen = strlen(hltoken); /* length of first char */
       if ( hltoklen >= minhltoklen ) { /*token must be at least \hl or \hd*/        if ( hltoklen >= minhltoklen ) { /*token must be at least \hl or \hd*/
Line 9045  while ( 1 )    /* scan chars till end */ Line 10298  while ( 1 )    /* scan chars till end */
     { istokwhite = 1; /* so token contains \hline only */      { istokwhite = 1; /* so token contains \hline only */
       if ( iseox ) ishonly = 1; } /* ignore entire row at eox */        if ( iseox ) ishonly = 1; } /* ignore entire row at eox */
   else /* token contains more than \hline */    else /* token contains more than \hline */
     strcpy(token,tokptr); } /* so flush \hline from token */      {strsqueezep(token,tokptr);} } /* so flush \hline */
       } /* --- end-of-if(ncols[nrows]==0) --- */        } /* --- end-of-if(ncols[nrows]==0) --- */
     /* --- rasterize completed token --- */      /* --- rasterize completed token --- */
     toksp[ntokens] = (istokwhite? NULL : /* don't rasterize empty token */      toksp[ntokens] = (istokwhite? NULL : /* don't rasterize empty token */
Line 9073  while ( 1 )    /* scan chars till end */ Line 10326  while ( 1 )    /* scan chars till end */
       } /* --- end-of-if(toksp[]!=NULL) --- */        } /* --- end-of-if(toksp[]!=NULL) --- */
     /* --- bump counters --- */      /* --- bump counters --- */
     if ( !ishonly ) /* don't count only an \hline */      if ( !ishonly ) /* don't count only an \hline */
       { ntokens++; /* bump total token count */        if ( ncols[nrows] < maxarraysz ) /* don't overflow arrays */
  ncols[nrows] += 1; } /* and bump #cols in current row */   { ntokens++; /* bump total token count */
     ncols[nrows] += 1; } /* and bump #cols in current row */
     /* --- get ready for next token --- */      /* --- get ready for next token --- */
     tokptr = token; /* reset ptr for next token */      tokptr = token; /* reset ptr for next token */
     istokwhite = 1; /* next token starts all white */      istokwhite = 1; /* next token starts all white */
Line 9086  while ( 1 )    /* scan chars till end */ Line 10340  while ( 1 )    /* scan chars till end */
     {      {
     maxcols = max2(maxcols,ncols[nrows]); /* max# cols in array */      maxcols = max2(maxcols,ncols[nrows]); /* max# cols in array */
     if ( ncols[nrows]>0 || hline[nrows]==0 ) /*ignore row with only \hline*/      if ( ncols[nrows]>0 || hline[nrows]==0 ) /*ignore row with only \hline*/
       nrows++; /* bump row count */        if ( nrows < maxarraysz ) /* don't overflow arrays */
           nrows++; /* bump row count */
     ncols[nrows] = 0; /* no cols in this row yet */      ncols[nrows] = 0; /* no cols in this row yet */
     if ( !iseox ) /* don't have a null yet */      if ( !iseox ) /* don't have a null yet */
       { exprptr++; /* bump past extra \ in \\ delim */        { exprptr++; /* bump past extra \ in \\ delim */
Line 9133  if ( msglevel>=29 && msgfp!=NULL ) /* de Line 10388  if ( msglevel>=29 && msgfp!=NULL ) /* de
   fprintf(msgfp,"\nrastarray> %d rows, heights: ",nrows);    fprintf(msgfp,"\nrastarray> %d rows, heights: ",nrows);
 for ( irow=0; irow<=nrows; irow++ ) /* and for each row */  for ( irow=0; irow<=nrows; irow++ ) /* and for each row */
   { height += rowheight[irow]; /*height of this row (0 for nrows)*/    { height += rowheight[irow]; /*height of this row (0 for nrows)*/
       height += vrowspace[irow]; /*plus extra //[len], if present*/
     height += hlinespace(irow); /*plus space for hline, if present*/      height += hlinespace(irow); /*plus space for hline, if present*/
     if ( msglevel>=29 && msgfp!=NULL ) /* debugging */      if ( msglevel>=29 && msgfp!=NULL ) /* debugging */
      fprintf(msgfp," %d=%2d+%d",irow+1,rowheight[irow],(hlinespace(irow))); }       fprintf(msgfp," %d=%2d+%d",irow+1,rowheight[irow],(hlinespace(irow))); }
Line 9162  for ( irow=0; irow<=nrows; irow++ ) /*to Line 10418  for ( irow=0; irow<=nrows; irow++ ) /*to
       if ( irow >= nrows ) hrow = height-1; /* row for bottom hline */        if ( irow >= nrows ) hrow = height-1; /* row for bottom hline */
       rule_raster(arrayrp,hrow,0,width,1,(hline[irow]<0?1:0)); } /* hline */        rule_raster(arrayrp,hrow,0,width,1,(hline[irow]<0?1:0)); } /* hline */
   if ( irow >= nrows ) break; /*just needed \hline for irow=nrows*/    if ( irow >= nrows ) break; /*just needed \hline for irow=nrows*/
     toprow += vrowspace[irow]; /* extra //[len] space above irow */
     if ( toprow < 0 ) toprow = 0; /* check for large negative [-len] */
   toprow += hlinespace(irow); /* space for hline above irow */    toprow += hlinespace(irow); /* space for hline above irow */
   leftcol = 0; /* start at leftmost column */    leftcol = 0; /* start at leftmost column */
   for ( icol=0; icol<ncols[irow]; icol++ ) /* go through cells in this row */    for ( icol=0; icol<ncols[irow]; icol++ ) /* go through cells in this row */
Line 9236  end_of_job: Line 10494  end_of_job:
  * string immediately following \picture to be   * string immediately following \picture to be
  * rasterized, and returning ptr immediately   * rasterized, and returning ptr immediately
  * following last character processed.   * following last character processed.
  * size (I) int containing 0-4 default font size   * size (I) int containing 0-7 default font size
  * basesp (I) subraster *  to character (or subexpression)   * basesp (I) subraster *  to character (or subexpression)
  * immediately preceding \picture   * immediately preceding \picture
  * (unused, but passed for consistency)   * (unused, but passed for consistency)
Line 9260  Allocations and Declarations Line 10518  Allocations and Declarations
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 char *texsubexpr(), picexpr[2049], *picptr=picexpr, /* picture {expre} */  char *texsubexpr(), picexpr[2049], *picptr=picexpr, /* picture {expre} */
  putexpr[256], *putptr,*multptr, /*[multi]put (x,y[;xinc,yinc;num])*/   putexpr[256], *putptr,*multptr, /*[multi]put (x,y[;xinc,yinc;num])*/
  pream[64], *preptr, /* optional put preamble */   pream[96], *preptr, /* optional put preamble */
  picelem[1025]; /* picture element following put */   picelem[1025]; /* picture element following put */
 subraster   *rasterize(), *picelemsp=NULL, /* rasterize picture elements */  subraster   *rasterize(), *picelemsp=NULL, /* rasterize picture elements */
  *new_subraster(), *picturesp=NULL, /* subraster for entire picture */   *new_subraster(), *picturesp=NULL, /* subraster for entire picture */
Line 9268  subraster   *rasterize(), *picelemsp=NUL Line 10526  subraster   *rasterize(), *picelemsp=NUL
 raster *picturerp=NULL; /* raster for entire picture */  raster *picturerp=NULL; /* raster for entire picture */
 int delete_subraster(); /* free picelemsp[] workspace */  int delete_subraster(); /* free picelemsp[] workspace */
 int pixsz = 1; /* pixels are one bit each */  int pixsz = 1; /* pixels are one bit each */
 double strtod(), /* convert ascii params to doubles */  double x=0.0,y=0.0, /* x,y-coords for put,multiput*/
  x=0.0,y=0.0, /* x,y-coords for put,multiput*/  
  xinc=0.0,yinc=0.0; /* x,y-incrementss for multiput*/   xinc=0.0,yinc=0.0; /* x,y-incrementss for multiput*/
 int width=0,  height=0, /* #pixels width,height of picture */  int width=0,  height=0, /* #pixels width,height of picture */
  ewidth=0, eheight=0, /* pic element width,height */   ewidth=0, eheight=0, /* pic element width,height */
  ix=0,xpos=0, iy=0,ypos=0, /* mimeTeX x,y pixel coords */   ix=0,xpos=0, iy=0,ypos=0, /* mimeTeX x,y pixel coords */
  num=1, inum; /* number reps, index of element */   num=1, inum; /* number reps, index of element */
   int evalterm(); /* evaluate [arg] and {arg}'s */
 int iscenter=0; /* center or lowerleft put position*/  int iscenter=0; /* center or lowerleft put position*/
 int *oldworkingparam = workingparam, /* save working param on entry */  int *oldworkingparam = workingparam, /* save working param on entry */
  origin = 0; /* x,yinc ++=00 +-=01 -+=10 --=11 */   origin = 0; /* x,yinc ++=00 +-=01 -+=10 --=11 */
Line 9289  if ( *putexpr == '\000' ) goto end_of_jo Line 10547  if ( *putexpr == '\000' ) goto end_of_jo
 /* --- now interpret width,height returned in putexpr --- */  /* --- now interpret width,height returned in putexpr --- */
 if ( (putptr=strchr(putexpr,',')) != NULL ) /* look for ',' in width,height*/  if ( (putptr=strchr(putexpr,',')) != NULL ) /* look for ',' in width,height*/
   *putptr = '\000'; /* found it, so replace ',' by '\0'*/    *putptr = '\000'; /* found it, so replace ',' by '\0'*/
 width=height = iround(unitlength*strtod(putexpr,NULL)); /*width pixels*/  width=height = eround(putexpr); /*width pixels*/
 if ( putptr != NULL ) /* 2nd arg, if present, is height */  if ( putptr != NULL ) /* 2nd arg, if present, is height */
   height = iround(unitlength*strtod(putptr+1,NULL)); /*in pixels*/    height = eround(putptr+1); /*in pixels*/
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 Then obtain entire picture {...} subexpression following (width,height)  Then obtain entire picture {...} subexpression following (width,height)
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
Line 9337  while ( *picptr != '\000' )  /* until we Line 10595  while ( *picptr != '\000' )  /* until we
   *pream = '\000'; /* init preamble as empty string */    *pream = '\000'; /* init preamble as empty string */
   if ( (putptr=strchr(putexpr,'$')) != NULL ) /*check for $ pream terminator*/    if ( (putptr=strchr(putexpr,'$')) != NULL ) /*check for $ pream terminator*/
     { *putptr++ = '\000'; /* replace $ by '\0', bump past $ */      { *putptr++ = '\000'; /* replace $ by '\0', bump past $ */
       strcpy(pream,putexpr); } /* copy leading preamble from put */        strninit(pream,putexpr,92); } /* copy leading preamble from put */
   else /* look for any non-digit preamble */    else /* look for any non-digit preamble */
     { for ( preptr=pream,putptr=putexpr; ; putptr++ )      { int npream = 0; /* #chars in preamble */
         for ( preptr=pream,putptr=putexpr; ; npream++,putptr++ )
  if ( *putptr == '\000' /* end-of-putdata signalled */   if ( *putptr == '\000' /* end-of-putdata signalled */
  ||   !isalpha((int)(*putptr)) ) break; /* or found non-alpha char */   ||   !isalpha((int)(*putptr)) /* or found non-alpha char */
    ||   npream > 92 ) break; /* or preamble too long */
  else *preptr++ = *putptr; /* copy alpha char to preamble */   else *preptr++ = *putptr; /* copy alpha char to preamble */
       *preptr = '\000'; } /* null-terminate preamble */        *preptr = '\000'; } /* null-terminate preamble */
   /* --- interpret preamble --- */    /* --- interpret preamble --- */
Line 9356  while ( *picptr != '\000' )  /* until we Line 10616  while ( *picptr != '\000' )  /* until we
   if ( *putptr != '\000' ) /*check for put data after preamble*/    if ( *putptr != '\000' ) /*check for put data after preamble*/
    {     {
    /* --- first squeeze preamble out of put expression --- */     /* --- first squeeze preamble out of put expression --- */
    if ( *pream != '\000' ) strcpy(putexpr,putptr); /* squeeze out preamble */     if ( *pream != '\000' ) /* have preamble */
        {strsqueezep(putexpr,putptr);} /* squeeze it out */
    /* --- interpret x,y --- */     /* --- interpret x,y --- */
    if ( (multptr=strchr(putexpr,';')) != NULL ) /*semicolon signals multiput*/     if ( (multptr=strchr(putexpr,';')) != NULL ) /*semicolon signals multiput*/
      *multptr = '\000'; /* replace semicolon by '\0' */       *multptr = '\000'; /* replace semicolon by '\0' */
    if ( (putptr=strchr(putexpr,',')) != NULL ) /* comma separates x,y */     if ( (putptr=strchr(putexpr,',')) != NULL ) /* comma separates x,y */
      *putptr = '\000'; /* replace comma by '\0'  */       *putptr = '\000'; /* replace comma by '\0'  */
    if ( *putexpr != '\000' ) /* leading , may be placeholder */     if ( *putexpr != '\000' ) /* leading , may be placeholder */
      x = unitlength*strtod(putexpr,NULL); /* x coord in pixels*/       x = (double)(eround(putexpr)); /* x coord in pixels*/
    if ( putptr != NULL ) /* 2nd arg, if present, is y coord */     if ( putptr != NULL ) /* 2nd arg, if present, is y coord */
      y = unitlength*strtod(putptr+1,NULL); /* in pixels */       y = (double)(eround(putptr+1)); /* in pixels */
    /* --- interpret xinc,yinc,num if we have a multiput --- */     /* --- interpret xinc,yinc,num if we have a multiput --- */
    if ( multptr != NULL ) /* found ';' signalling multiput */     if ( multptr != NULL ) /* found ';' signalling multiput */
      {       {
Line 9374  while ( *picptr != '\000' )  /* until we Line 10635  while ( *picptr != '\000' )  /* until we
      if ( (putptr=strchr(multptr+1,',')) != NULL ) /* ',' between xinc,yinc*/       if ( (putptr=strchr(multptr+1,',')) != NULL ) /* ',' between xinc,yinc*/
        *putptr = '\000'; /* replace ',' by '\0' */         *putptr = '\000'; /* replace ',' by '\0' */
      if ( *(multptr+1) != '\000' ) /* leading , may be placeholder */       if ( *(multptr+1) != '\000' ) /* leading , may be placeholder */
        xinc = unitlength*strtod(multptr+1,NULL); /* xinc in pixels */         xinc = (double)(eround(multptr+1)); /* xinc in pixels */
      if ( putptr != NULL ) /* 2nd arg, if present, is yinc */       if ( putptr != NULL ) /* 2nd arg, if present, is yinc */
        yinc = unitlength*strtod(putptr+1,NULL); /* in user pixels */         yinc = (double)(eround(putptr+1)); /* in user pixels */
      num = (preptr==NULL? 999 : atoi(preptr+1)); /*explicit num val or 999*/       num = (preptr==NULL? 999 : atoi(preptr+1)); /*explicit num val or 999*/
      } /* --- end-of-if(multptr!=NULL) --- */       } /* --- end-of-if(multptr!=NULL) --- */
    } /* --- end-of-if(*preptr!='\000') --- */     } /* --- end-of-if(*preptr!='\000') --- */
Line 9480  subraster *new_subraster(), *linesp=NULL Line 10741  subraster *new_subraster(), *linesp=NULL
 /*char *origexpression = *expression;*/ /*original expression after \line*/  /*char *origexpression = *expression;*/ /*original expression after \line*/
 int pixsz = 1; /* pixels are one bit each */  int pixsz = 1; /* pixels are one bit each */
 int thickness = 1; /* line thickness */  int thickness = 1; /* line thickness */
 double strtod(), /* convert ascii params to doubles */  double xinc=0.0, yinc=0.0, /* x,y-increments for line, */
  xinc=0.0, yinc=0.0, /* x,y-increments for line, */  
  xlen=0.0, ylen=0.0; /* x,y lengths for line */   xlen=0.0, ylen=0.0; /* x,y lengths for line */
 int width=0,  height=0, /* #pixels width,height of line */  int width=0,  height=0, /* #pixels width,height of line */
  rwidth=0, rheight=0; /*alloc width,height plus thickness*/   rwidth=0, rheight=0; /*alloc width,height plus thickness*/
   int evalterm(); /* evaluate [arg] and {arg}'s */
 int istop=0,  isright=0, /* origin at bot-left if x,yinc>=0 */  int istop=0,  isright=0, /* origin at bot-left if x,yinc>=0 */
  origin = 0; /* x,yinc: ++=00 +-=01 -+=10 --=11 */   origin = 0; /* x,yinc: ++=00 +-=01 -+=10 --=11 */
 int line_raster(); /* draw line in linesp->image */  int line_raster(); /* draw line in linesp->image */
Line 9497  if ( *linexpr == '\000' ) goto end_of_jo Line 10758  if ( *linexpr == '\000' ) goto end_of_jo
 /* --- now interpret xinc,yinc;thickness returned in linexpr --- */  /* --- now interpret xinc,yinc;thickness returned in linexpr --- */
 if ( (xptr=strchr(linexpr,';')) != NULL ) /* look for ';' after xinc,yinc */  if ( (xptr=strchr(linexpr,';')) != NULL ) /* look for ';' after xinc,yinc */
   { *xptr = '\000'; /* terminate linexpr at ; */    { *xptr = '\000'; /* terminate linexpr at ; */
     thickness = (int)strtol(xptr+1,NULL,10); } /* get int thickness */      thickness = evalterm(mimestore,xptr+1); } /* get int thickness */
 if ( (xptr=strchr(linexpr,',')) != NULL ) /* look for ',' in xinc,yinc */  if ( (xptr=strchr(linexpr,',')) != NULL ) /* look for ',' in xinc,yinc */
   *xptr = '\000'; /* found it, so replace ',' by '\0'*/    *xptr = '\000'; /* found it, so replace ',' by '\0'*/
 if ( *linexpr != '\000' ) /* check against missing 1st arg */  if ( *linexpr != '\000' ) /* check against missing 1st arg */
   xinc = xlen = strtod(linexpr,NULL); /* xinc in user units */    xinc = xlen = (double)evalterm(mimestore,linexpr); /* xinc in user units */
 if ( xptr != NULL ) /* 2nd arg, if present, is yinc */  if ( xptr != NULL ) /* 2nd arg, if present, is yinc */
   yinc = ylen = strtod(xptr+1,NULL); /* in user units */    yinc = ylen = (double)evalterm(mimestore,xptr+1); /* in user units */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 obtain optional {xlen} following (xinc,yinc), and calculate ylen  obtain optional {xlen} following (xinc,yinc), and calculate ylen
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
Line 9513  if ( *(*expression) == '{' )  /*have {xl Line 10774  if ( *(*expression) == '{' )  /*have {xl
   /* --- parse {xlen} and bump expression past it, interpret as double --- */    /* --- parse {xlen} and bump expression past it, interpret as double --- */
   *expression = texsubexpr(*expression,linexpr,253,"{","}",0,0);    *expression = texsubexpr(*expression,linexpr,253,"{","}",0,0);
   if ( *linexpr == '\000' ) goto end_of_job; /* couldn't get {xlen} */    if ( *linexpr == '\000' ) goto end_of_job; /* couldn't get {xlen} */
   xlen = strtod(linexpr,NULL); /* xlen in user units */    xlen = (double)evalterm(mimestore,linexpr); /* xlen in user units */
   /* --- set other values accordingly --- */    /* --- set other values accordingly --- */
   if ( xlen  < 0.0 ) xinc = -xinc; /* if xlen negative, flip xinc sign*/    if ( xlen  < 0.0 ) xinc = -xinc; /* if xlen negative, flip xinc sign*/
   if ( xinc != 0.0 ) ylen = xlen*yinc/xinc; /* set ylen from xlen and slope*/    if ( xinc != 0.0 ) ylen = xlen*yinc/xinc; /* set ylen from xlen and slope*/
Line 9608  char *texsubexpr(), rulexpr[257]; /* rul Line 10869  char *texsubexpr(), rulexpr[257]; /* rul
 subraster *new_subraster(), *rulesp=NULL; /* subraster for rule */  subraster *new_subraster(), *rulesp=NULL; /* subraster for rule */
 int pixsz = 1; /* pixels are one bit each */  int pixsz = 1; /* pixels are one bit each */
 int lift=0, width=0, height=0; /* default rule parameters */  int lift=0, width=0, height=0; /* default rule parameters */
 double strtod(), dval; /* convert ascii params to doubles */  double dval; /* convert ascii params to doubles */
 int rwidth=0, rheight=0; /* alloc width, height plus lift */  int rwidth=0, rheight=0; /* alloc width, height plus lift */
 int rule_raster(); /* draw rule in rulesp->image */  int rule_raster(); /* draw rule in rulesp->image */
   int evalterm(); /* evaluate args */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 Obtain lift,width,height  Obtain lift,width,height
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 /* --- check for optional lift arg  --- */  /* --- check for optional lift arg  --- */
 if ( *(*expression) == '[' ) /*check for []-enclosed optional arg*/  if ( *(*expression) == '[' ) /*check for []-enclosed optional arg*/
   { *expression = texsubexpr(*expression,rulexpr,255,"[","]",0,0);    { *expression = texsubexpr(*expression,rulexpr,255,"[","]",0,0);
     dval = (int)(strtod(rulexpr,NULL)+0.5); /* convert [lift] to int */      dval = evalterm(mimestore,rulexpr); /* convert [lift] to int */
     if ( dval <= 99 && dval >= (-99) ) /* sanity check */      if ( dval <= 99 && dval >= (-99) ) /* sanity check */
       lift = iround(unitlength*dval); } /* scale by unitlength and round */        lift = iround(unitlength*dval); } /* scale by unitlength and round */
 /* --- parse for width --- */  /* --- parse for width --- */
 *expression = texsubexpr(*expression,rulexpr,255,"{","}",0,0);  *expression = texsubexpr(*expression,rulexpr,255,"{","}",0,0);
 if ( *rulexpr == '\000' ) goto end_of_job; /* quit if args missing */  if ( *rulexpr == '\000' ) goto end_of_job; /* quit if args missing */
 dval = (int)(strtod(rulexpr,NULL)+0.5); /* convert {width} to int */  dval = evalterm(mimestore,rulexpr); /* convert {width} to int */
 if ( dval <= 500 && dval >= 0 ) /* sanity check */  if ( dval <= 500 && dval >= 0 ) /* sanity check */
   width = max2(0,iround(unitlength*dval)); /* scale by unitlength and round*/    width = max2(0,iround(unitlength*dval)); /* scale by unitlength and round*/
 /* --- parse for height --- */  /* --- parse for height --- */
 *expression = texsubexpr(*expression,rulexpr,255,"{","}",0,0);  *expression = texsubexpr(*expression,rulexpr,255,"{","}",0,0);
 if ( *rulexpr == '\000' ) goto end_of_job; /* quit if args missing */  if ( *rulexpr == '\000' ) goto end_of_job; /* quit if args missing */
 dval = (int)(strtod(rulexpr,NULL)+0.5); /* convert {height} to int */  dval = evalterm(mimestore,rulexpr); /* convert {height} to int */
 if ( dval <= 500 && dval > 0 ) /* sanity check */  if ( dval <= 500 && dval > 0 ) /* sanity check */
   height= max2(1,iround(unitlength*dval)); /* scale by unitlength and round*/    height= max2(1,iround(unitlength*dval)); /* scale by unitlength and round*/
 /* --- raster width,height in pixels --- */  /* --- raster width,height in pixels --- */
Line 9676  end_of_job: Line 10938  end_of_job:
  * string immediately following \circle to be   * string immediately following \circle to be
  * rasterized, and returning ptr immediately   * rasterized, and returning ptr immediately
  * following last character processed.   * following last character processed.
  * size (I) int containing 0-4 default font size   * size (I) int containing 0-7 default font size
  * basesp (I) subraster *  to character (or subexpression)   * basesp (I) subraster *  to character (or subexpression)
  * immediately preceding \circle   * immediately preceding \circle
  * (unused, but passed for consistency)   * (unused, but passed for consistency)
Line 9703  char *qptr=NULL, quads[256]="1234"; /* d Line 10965  char *qptr=NULL, quads[256]="1234"; /* d
 double theta0=0.0, theta1=0.0; /* ;theta0,theta1 instead of ;quads*/  double theta0=0.0, theta1=0.0; /* ;theta0,theta1 instead of ;quads*/
 subraster *new_subraster(), *circsp=NULL; /* subraster for ellipse */  subraster *new_subraster(), *circsp=NULL; /* subraster for ellipse */
 int pixsz = 1; /* pixels are one bit each */  int pixsz = 1; /* pixels are one bit each */
 double strtod(), /* convert ascii params to doubles */  double xdiam=0.0, ydiam=0.0; /* x,y major/minor axes/diameters */
  xdiam=0.0, ydiam=0.0; /* x,y major/minor axes/diameters */  
 int width=0,  height=0; /* #pixels width,height of ellipse */  int width=0,  height=0; /* #pixels width,height of ellipse */
 int thickness = 1; /* drawn lines are one pixel thick */  int thickness = 1; /* drawn lines are one pixel thick */
   int evalterm(); /* evaluate [arg],{arg} expressions*/
 int origin = 55; /* force origin centered */  int origin = 55; /* force origin centered */
 int circle_raster(), /* draw ellipse in circsp->image */  int circle_raster(), /* draw ellipse in circsp->image */
  circle_recurse(); /* for theta0,theta1 args */   circle_recurse(); /* for theta0,theta1 args */
Line 9714  int circle_raster(),  /* draw ellipse in Line 10976  int circle_raster(),  /* draw ellipse in
 obtain (xdiam[,ydiam]) arguments immediately following \circle command  obtain (xdiam[,ydiam]) arguments immediately following \circle command
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 /* --- parse for (xdiam[,ydiam]) args, and bump expression past it --- */  /* --- parse for (xdiam[,ydiam]) args, and bump expression past it --- */
 *expression = texsubexpr(*expression,circexpr,511,"(",")",0,0);  *expression = texsubexpr(*expression,circexpr,500,"(",")",0,0);
 if ( *circexpr == '\000' ) goto end_of_job; /* couldn't get (xdiam[,ydiam])*/  if ( *circexpr == '\000' ) goto end_of_job; /* couldn't get (xdiam[,ydiam])*/
 /* --- now interpret xdiam[,ydiam] returned in circexpr --- */  /* --- now interpret xdiam[,ydiam] returned in circexpr --- */
 if ( (qptr=strchr(circexpr,';')) != NULL ) /* semicolon signals quads data */  if ( (qptr=strchr(circexpr,';')) != NULL ) /* semicolon signals quads data */
   { *qptr = '\000'; /* replace semicolon by '\0' */    { *qptr = '\000'; /* replace semicolon by '\0' */
     strcpy(quads,qptr+1); /* save user-requested quads */      strninit(quads,qptr+1,128); /* save user-requested quads */
     if ( (qptr=strchr(quads,',')) != NULL ) /* have theta0,theta1 instead */      if ( (qptr=strchr(quads,',')) != NULL ) /* have theta0,theta1 instead */
       { *qptr = '\000'; /* replace , with null */        { *qptr = '\000'; /* replace , with null */
  theta0 = strtod(quads,NULL); /* theta0 precedes , */   theta0 = (double)evalterm(mimestore,quads);  /* theta0 precedes , */
  theta1 = strtod(qptr+1,NULL); /* theta1 follows , */   theta1 = (double)evalterm(mimestore,qptr+1); /* theta1 follows , */
  qptr = NULL; } /* signal thetas instead of quads */   qptr = NULL; } /* signal thetas instead of quads */
     else      else
  qptr = quads; } /* set qptr arg for circle_raster()*/   qptr = quads; } /* set qptr arg for circle_raster()*/
Line 9731  else     /* no ;quads at all */ Line 10993  else     /* no ;quads at all */
   qptr = quads; /* default to all 4 quadrants */    qptr = quads; /* default to all 4 quadrants */
 if ( (xptr=strchr(circexpr,',')) != NULL ) /* look for ',' in xdiam[,ydiam]*/  if ( (xptr=strchr(circexpr,',')) != NULL ) /* look for ',' in xdiam[,ydiam]*/
   *xptr = '\000'; /* found it, so replace ',' by '\0'*/    *xptr = '\000'; /* found it, so replace ',' by '\0'*/
 xdiam = ydiam = strtod(circexpr,NULL); /* xdiam=ydiam in user units */  xdiam = ydiam = /* xdiam=ydiam in user units */
     (double)evalterm(mimestore,circexpr); /* evaluate expression */
 if ( xptr != NULL ) /* 2nd arg, if present, is ydiam */  if ( xptr != NULL ) /* 2nd arg, if present, is ydiam */
   ydiam = strtod(xptr+1,NULL); /* in user units */    ydiam = (double)evalterm(mimestore,xptr+1); /* in user units */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 calculate width,height, etc  calculate width,height, etc
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
Line 9791  end_of_job: Line 11054  end_of_job:
  * string immediately following \bezier to be   * string immediately following \bezier to be
  * rasterized, and returning ptr immediately   * rasterized, and returning ptr immediately
  * following last character processed.   * following last character processed.
  * size (I) int containing 0-5 default font size   * size (I) int containing 0-7 default font size
  * basesp (I) subraster *  to character (or subexpression)   * basesp (I) subraster *  to character (or subexpression)
  * immediately preceding \bezier   * immediately preceding \bezier
  * (unused, but passed for consistency)   * (unused, but passed for consistency)
Line 9816  Allocations and Declarations Line 11079  Allocations and Declarations
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 subraster *new_subraster(), *bezsp=NULL; /* subraster for bezier */  subraster *new_subraster(), *bezsp=NULL; /* subraster for bezier */
 char *texsubexpr(), bezexpr[129],*xptr=bezexpr; /*\bezier(r,c)(r,c)(r,c)*/  char *texsubexpr(), bezexpr[129],*xptr=bezexpr; /*\bezier(r,c)(r,c)(r,c)*/
 double strtod(); /* convert ascii params to doubles */  
 double r0=0.0,c0=0.0, r1=0.0,c1=0.0, rt=0.0,ct=0.0, /* bezier points */  double r0=0.0,c0=0.0, r1=0.0,c1=0.0, rt=0.0,ct=0.0, /* bezier points */
  rmid=0.0, cmid=0.0, /* coords at parameterized midpoint*/   rmid=0.0, cmid=0.0, /* coords at parameterized midpoint*/
  rmin=0.0, cmin=0.0, /* minimum r,c */   rmin=0.0, cmin=0.0, /* minimum r,c */
  rmax=0.0, cmax=0.0, /* maximum r,c */   rmax=0.0, cmax=0.0, /* maximum r,c */
  rdelta=0.0, cdelta=0.0, /* rmax-rmin, cmax-cmin */   rdelta=0.0, cdelta=0.0, /* rmax-rmin, cmax-cmin */
  r=0.0, c=0.0; /* some point */   r=0.0, c=0.0; /* some point */
   int evalterm(); /* evaluate [arg],{arg} expressions*/
 int iarg=0; /* 0=r0,c0 1=r1,c1 2=rt,ct */  int iarg=0; /* 0=r0,c0 1=r1,c1 2=rt,ct */
 int width=0, height=0; /* dimensions of bezier raster */  int width=0, height=0; /* dimensions of bezier raster */
 int pixsz = 1; /* pixels are one bit each */  int pixsz = 1; /* pixels are one bit each */
Line 9841  for ( iarg=1; iarg<=2; iarg++ )  /* 0=c0 Line 11104  for ( iarg=1; iarg<=2; iarg++ )  /* 0=c0
   c = r = 0.0; /* init x-coord=col, y-coord=row */    c = r = 0.0; /* init x-coord=col, y-coord=row */
   if ( (xptr=strchr(bezexpr,',')) != NULL ) /* comma separates row,col */    if ( (xptr=strchr(bezexpr,',')) != NULL ) /* comma separates row,col */
     { *xptr = '\000'; /* found it, so replace ',' by '\0'*/      { *xptr = '\000'; /* found it, so replace ',' by '\0'*/
       r = unitlength*strtod(xptr+1,NULL); } /* row=y-coord in pixels */        /* --- row=y-coord in pixels --- */
   c = unitlength*strtod(bezexpr,NULL); /* col=x-coord in pixels */        r = unitlength*((double)evalterm(mimestore,xptr+1)); }
     /* --- col=x-coord in pixels --- */
     c = unitlength*((double)evalterm(mimestore,bezexpr));
   /* --- store r,c --- */    /* --- store r,c --- */
   switch ( iarg )    switch ( iarg )
     { case 0: r0=r; c0=c; break;      { case 0: r0=r; c0=c; break;
Line 9940  Allocations and Declarations Line 11205  Allocations and Declarations
 char *texsubexpr(), subexpr[MAXSUBXSZ+1], *liftexpr=subexpr; /* args */  char *texsubexpr(), subexpr[MAXSUBXSZ+1], *liftexpr=subexpr; /* args */
 subraster *rasterize(), *raisesp=NULL; /* rasterize subexpr to be raised */  subraster *rasterize(), *raisesp=NULL; /* rasterize subexpr to be raised */
 int lift=0; /* amount to raise/lower baseline */  int lift=0; /* amount to raise/lower baseline */
   int evalterm(); /* evaluate [arg],{arg} expressions*/
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 obtain {lift} argument immediately following \raisebox command  obtain {lift} argument immediately following \raisebox command
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
   rastlift = 0; /* reset global lift adjustment */
 /* --- parse for {lift} arg, and bump expression past it --- */  /* --- parse for {lift} arg, and bump expression past it --- */
 *expression = texsubexpr(*expression,liftexpr,0,"{","}",0,0);  *expression = texsubexpr(*expression,liftexpr,0,"{","}",0,0);
 if ( *liftexpr == '\000' ) goto end_of_job; /* couldn't get {lift} */  if ( *liftexpr == '\000' ) goto end_of_job; /* couldn't get {lift} */
 lift = (int)((unitlength*strtod(liftexpr,NULL))+0.0); /*{lift} to integer*/  lift = eround(liftexpr); /* {lift} to integer */
 if ( abs(lift) > 200 ) lift=0; /* sanity check */  if ( abs(lift) > 200 ) lift=0; /* sanity check */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 obtain {subexpr} argument after {lift}, and rasterize it  obtain {subexpr} argument after {lift}, and rasterize it
Line 9961  raise/lower baseline and return it to ca Line 11228  raise/lower baseline and return it to ca
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 /* --- raise/lower baseline --- */  /* --- raise/lower baseline --- */
 raisesp->baseline += lift; /* new baseline (no height checks) */  raisesp->baseline += lift; /* new baseline (no height checks) */
   rastlift = lift; /* set global to signal adjustment */
 /* --- return raised subexpr to caller --- */  /* --- return raised subexpr to caller --- */
 end_of_job:  end_of_job:
   return ( raisesp ); /* return raised subexpr to caller */    return ( raisesp ); /* return raised subexpr to caller */
Line 10004  subraster *rasterize(), *rotsp=NULL; /* Line 11272  subraster *rasterize(), *rotsp=NULL; /*
 raster *rastrot(), *rotrp=NULL; /* rotate subraster->image 90 degs */  raster *rastrot(), *rotrp=NULL; /* rotate subraster->image 90 degs */
 int delete_raster(); /* delete intermediate rasters */  int delete_raster(); /* delete intermediate rasters */
 int baseline=0; /* baseline of rasterized image */  int baseline=0; /* baseline of rasterized image */
 double strtod(), /* convert ascii params to doubles */  double degrees=0.0, ipart,fpart; /* degrees to be rotated */
  degrees=0.0, ipart,fpart; /* degrees to be rotated */  
 int idegrees=0, isneg=0; /* positive ipart, isneg=1 if neg */  int idegrees=0, isneg=0; /* positive ipart, isneg=1 if neg */
 int n90=0, isn90=1; /* degrees is n90 multiples of 90 */  int n90=0, isn90=1; /* degrees is n90 multiples of 90 */
   int evalterm(); /* evaluate [arg],{arg} expressions*/
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 obtain {degrees} argument immediately following \rotatebox command  obtain {degrees} argument immediately following \rotatebox command
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 /* --- parse for {degrees} arg, and bump expression past it --- */  /* --- parse for {degrees} arg, and bump expression past it --- */
 *expression = texsubexpr(*expression,degexpr,0,"{","}",0,0);  *expression = texsubexpr(*expression,degexpr,0,"{","}",0,0);
 if ( *degexpr == '\000' ) goto end_of_job; /* couldn't get {degrees} */  if ( *degexpr == '\000' ) goto end_of_job; /* couldn't get {degrees} */
 degrees = strtod(degexpr,NULL); /* degrees to be rotated */  degrees = (double)evalterm(mimestore,degexpr); /* degrees to be rotated */
 if ( degrees < 0.0 ) /* clockwise rotation desired */  if ( degrees < 0.0 ) /* clockwise rotation desired */
   { degrees = -degrees; /* flip sign so degrees positive */    { degrees = -degrees; /* flip sign so degrees positive */
     isneg = 1; } /* and set flag to indicate flip */      isneg = 1; } /* and set flag to indicate flip */
Line 10081  end_of_job: Line 11349  end_of_job:
   
   
 /* ==========================================================================  /* ==========================================================================
    * Function: rastmagnify ( expression, size, basesp, arg1, arg2, arg3 )
    * Purpose: \magnify{magstep}{subexpression} handler, returns subraster
    * containing magnified subexpression
    * --------------------------------------------------------------------------
    * Arguments: expression (I/O) char **  to first char of null-terminated
    * string immediately following \reflectbox to
    * be rasterized, and returning ptr immediately
    * following last character processed.
    * size (I) int containing 0-7 default font size
    * basesp (I) subraster *  to character (or subexpression)
    * immediately preceding \reflectbox
    * (unused, but passed for consistency)
    * arg1 (I) int unused
    * arg2 (I) int unused
    * arg3 (I) int unused
    * --------------------------------------------------------------------------
    * Returns: ( subraster * ) ptr to subraster corresponding to \magnify
    * requested, or NULL for any parsing error
    * --------------------------------------------------------------------------
    * Notes:     o Summary of syntax...
    *  \magnify{magstep}{subexpression}
    *      o
    * ======================================================================= */
   /* --- entry point --- */
   subraster *rastmagnify ( char **expression, int size, subraster *basesp,
    int arg1, int arg2, int arg3 )
   {
   /* -------------------------------------------------------------------------
   Allocations and Declarations
   -------------------------------------------------------------------------- */
   char *texsubexpr(), subexpr[MAXSUBXSZ+1], *magexpr=subexpr; /* args */
   subraster *rasterize(), *magsp=NULL; /* subraster for magnified subexpr */
   raster *rastmag(), *magrp=NULL; /* magnify subraster->image */
   int magstep = 1; /* default magnification */
   int delete_raster(); /* delete intermediate raster */
   int baseline=0; /* baseline of rasterized image */
   /* -------------------------------------------------------------------------
   obtain {magstep} argument immediately following \magnify command
   -------------------------------------------------------------------------- */
   /* --- parse for {magstep} arg, and bump expression past it --- */
   *expression = texsubexpr(*expression,magexpr,255,"{","}",0,0);
   magstep = atoi(magexpr); /* convert {magstep} to int */
   if ( magstep<1 || magstep>10 ) /* check magstep input */
     magstep = 1; /* back to default if illegal */
   /* -------------------------------------------------------------------------
   obtain {subexpr} argument after {magstep}, and rasterize it
   -------------------------------------------------------------------------- */
   /* --- parse for {subexpr} arg, and bump expression past it --- */
   *expression = texsubexpr(*expression,subexpr,0,"{","}",0,0);
   /* --- rasterize subexpression to be reflected --- */
   if ( (magsp = rasterize(subexpr,size)) /* rasterize subexpression */
   ==   NULL ) goto end_of_job; /* and quit if failed */
   /* --- return unmodified image if no magnification requested --- */
   if ( magstep<=1 ) goto end_of_job; /* don't bother magnifying image */
   /* --- extract params for image to be magnified --- */
   magrp = magsp->image; /* unmagnified rasterized image */
   baseline = magsp->baseline; /* and baseline of that image */
   /* -------------------------------------------------------------------------
   magnify image and adjust its parameters
   -------------------------------------------------------------------------- */
   /* --- magnify image --- */
   magrp = rastmag(magsp->image,magstep); /* magnify raster image */
   if ( magrp == NULL ) goto end_of_job; /* failed to magnify image */
   delete_raster(magsp->image); /* free original raster image */
   magsp->image = magrp; /*and replace it with magnified one*/
   /* --- adjust parameters --- */
   baseline *= magstep; /* scale baseline */
   if ( baseline > 0 ) baseline += 1; /* adjust for no descenders */
   magsp->baseline = baseline; /*reset baseline of magnified image*/
   /* --- return magnified subexpr to caller --- */
   end_of_job:
     return ( magsp ); /*back to caller with magnified expr*/
   } /* --- end-of-function rastmagnify() --- */
   
   
   /* ==========================================================================
  * Function: rastreflect ( expression, size, basesp, arg1, arg2, arg3 )   * Function: rastreflect ( expression, size, basesp, arg1, arg2, arg3 )
  * Purpose: \reflectbox[axis]{subexpression} handler, returns subraster   * Purpose: \reflectbox[axis]{subexpression} handler, returns subraster
  * containing subexpression reflected horizontally (i.e., around   * containing subexpression reflected horizontally (i.e., around
Line 10168  end_of_job: Line 11512  end_of_job:
  * string immediately following \fbox to be   * string immediately following \fbox to be
  * rasterized, and returning ptr immediately   * rasterized, and returning ptr immediately
  * following last character processed.   * following last character processed.
  * size (I) int containing 0-5 default font size   * size (I) int containing 0-7 default font size
  * basesp (I) subraster *  to character (or subexpression)   * basesp (I) subraster *  to character (or subexpression)
  * immediately preceding \fbox   * immediately preceding \fbox
  * (unused, but passed for consistency)   * (unused, but passed for consistency)
Line 10193  Allocations and Declarations Line 11537  Allocations and Declarations
 char *texsubexpr(), subexpr[MAXSUBXSZ+1], widtharg[512]; /* args */  char *texsubexpr(), subexpr[MAXSUBXSZ+1], widtharg[512]; /* args */
 subraster *rasterize(), *framesp=NULL; /* rasterize subexpr to be framed */  subraster *rasterize(), *framesp=NULL; /* rasterize subexpr to be framed */
 raster *border_raster(), *bp=NULL; /* framed image raster */  raster *border_raster(), *bp=NULL; /* framed image raster */
 double strtod(); /* interpret [width][height] */  int evalterm(), evalue=0; /* interpret [width][height] */
 int fwidth=6, fthick=1; /*extra frame width, line thickness*/  int fwidth=6, fthick=1, /*extra frame width, line thickness*/
    fsides=0; /* frame sides: 1=left,2=top,4=right,8=bot */
 int width=(-1), height=(-1), /* optional [width][height] args */  int width=(-1), height=(-1), /* optional [width][height] args */
  iscompose = 0; /* set true if optional args given */   iscompose = 0; /* set true if optional args given */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 obtain optional [width][height] arguments immediately following \fbox  obtain optional [width][height] arguments immediately following \fbox
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 /* --- first check for optional \fbox[width] --- */  /* --- first check for optional \fbox[width] --- */
 if ( *(*expression) == '[' ) /* check for []-enclosed width arg */  if ( *(*expression) == '[' ) { /* check for []-enclosed width arg */
   { *expression = texsubexpr(*expression,widtharg,511,"[","]",0,0);    *expression = texsubexpr(*expression,widtharg,511,"[","]",0,0);
     if ( *widtharg != '\000' ) /* got widtharg */    if ( !isempty(widtharg) ) { /* got widtharg */
      { width = max2(1,iround(unitlength*strtod(widtharg,NULL)));      char *comma = strchr(widtharg,','); /* look for [width,sides] */
        height = 1;  fwidth = 2; iscompose = 1; }      if ( comma == (char *)NULL ) /* no comma */
         comma = strchr(widtharg,';'); /* permit semicolon [width;sides] */
       if ( comma != (char *)NULL ) { /* optional [width,fsides] found */
         fsides = atoi(comma+1); /* interpret fsides after comma */
         if ( size < 5 ) /* for smaller fonts */
           { fwidth = 2;  fthick = 1; } /* tighten frame, thinner accent */
         else { fwidth = 3;  fthick = 2; } /* loosen frame, thicken accent */
         *comma = '\000'; /* null-terminate width at comma */
         trimwhite(widtharg); } /*remove leading/trailing whitespace*/
       if ( comma==(char *)NULL || !isempty(widtharg) ) { /* have a width */
         height = 1; /* default explicit height, too */
         if ( fsides == 0 ) { /* a normal framebox */
    evalue = eround(widtharg); /* interpret and scale width */
           width = max2(1,evalue); /* must be >0 */
           fwidth = 2; iscompose = 1; }
         else /* absolute pixels for "accents" */
    width = evalterm(mimestore,widtharg); }
       } /* --- end-of-if(!isempty(widtharg)) --- */
   } /* --- end-of-if(**expression=='[') --- */    } /* --- end-of-if(**expression=='[') --- */
 if ( width > 0 ) /* found leading [width], so... */  if ( width > 0 || fsides > 0) /* found leading [width], so... */
  if ( *(*expression) == '[' ) /* check for []-enclosed height arg */   if ( *(*expression) == '[' ) /* check for []-enclosed height arg */
   { *expression = texsubexpr(*expression,widtharg,511,"[","]",0,0);    { *expression = texsubexpr(*expression,widtharg,511,"[","]",0,0);
     if ( *widtharg != '\000' ) /* got widtharg */      if ( !isempty(widtharg) ) { /* got widtharg */
      { height = max2(1,iround(unitlength*strtod(widtharg,NULL)));        if ( fsides == 0 ) { /* a normal framebox */
        fwidth = 0; } /* no extra border */   evalue = eround(widtharg); /* interpret and scale height */
           height = max2(1,evalue); /* must be >0 */
           fwidth = 0; } /* no extra border */
         else /* absolute pixels for "accents" */
    height = evalterm(mimestore,widtharg); }
   } /* --- end-of-if(**expression=='[') --- */    } /* --- end-of-if(**expression=='[') --- */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 obtain {subexpr} argument  obtain {subexpr} argument
Line 10225  if ( width<0 || height<0 )  /* no explic Line 11591  if ( width<0 || height<0 )  /* no explic
     ==   NULL ) goto end_of_job; } /* and quit if failed */      ==   NULL ) goto end_of_job; } /* and quit if failed */
 else  else
   { char composexpr[8192]; /* compose subexpr with empty box */    { char composexpr[8192]; /* compose subexpr with empty box */
     sprintf(composexpr,"\\compose{\\hspace{%d}\\vspace{%d}}{%s}",      sprintf(composexpr,"\\compose{\\hspace{%d}\\vspace{%d}}{%.8000s}",
     width,height,subexpr);      width,height,subexpr);
     if ( (framesp = rasterize(composexpr,size)) /* rasterize subexpression */      if ( (framesp = rasterize(composexpr,size)) /* rasterize subexpression */
     ==   NULL ) goto end_of_job; } /* and quit if failed */      ==   NULL ) goto end_of_job; } /* and quit if failed */
Line 10233  else Line 11599  else
 draw frame, reset params, and return it to caller  draw frame, reset params, and return it to caller
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 /* --- draw border --- */  /* --- draw border --- */
   if ( fsides > 0 ) fthick += (100*fsides); /* embed fsides in fthick arg */
 if ( (bp = border_raster(framesp->image,-fwidth,-fwidth,fthick,1))  if ( (bp = border_raster(framesp->image,-fwidth,-fwidth,fthick,1))
 ==   NULL ) goto end_of_job; /* draw border and quit if failed */  ==   NULL ) goto end_of_job; /* draw border and quit if failed */
 /* --- replace original image and raise baseline to accommodate frame --- */  /* --- replace original image and raise baseline to accommodate frame --- */
Line 10256  end_of_job: Line 11623  end_of_job:
  * string immediately following \input to be   * string immediately following \input to be
  * rasterized, and returning ptr immediately   * rasterized, and returning ptr immediately
  * following last character processed.   * following last character processed.
  * size (I) int containing 0-5 default font size   * size (I) int containing 0-7 default font size
  * basesp (I) subraster *  to character (or subexpression)   * basesp (I) subraster *  to character (or subexpression)
  * immediately preceding \input   * immediately preceding \input
  * (unused, but passed for consistency)   * (unused, but passed for consistency)
Line 10268  end_of_job: Line 11635  end_of_job:
  * in filename, or NULL for any parsing error   * in filename, or NULL for any parsing error
  * --------------------------------------------------------------------------   * --------------------------------------------------------------------------
  * Notes:     o Summary of syntax...   * Notes:     o Summary of syntax...
  *  \input{filename}   *  \input{filename}     reads entire file named filename
    *  \input{filename:tag} reads filename, but returns only
    *  those characters between <tag>...</tag> in that file.
  *      o   *      o
  * ======================================================================= */   * ======================================================================= */
 /* --- entry point --- */  /* --- entry point --- */
Line 10282  char *texsubexpr(), tag[1024]="\000", fi Line 11651  char *texsubexpr(), tag[1024]="\000", fi
 subraster *rasterize(), *inputsp=NULL; /* rasterized input image */  subraster *rasterize(), *inputsp=NULL; /* rasterized input image */
 int status, rastreadfile(); /* read input file */  int status, rastreadfile(); /* read input file */
 int format=0, npts=0; /* don't reformat (numerical) input */  int format=0, npts=0; /* don't reformat (numerical) input */
   int isinput = (seclevel<=inputseclevel?1:0); /*true if \input permitted*/
   /*int evalterm();*/ /* evaluate expressions */
   char *inputpath = INPUTPATH; /* permitted \input{} paths for any user */
   int isstrstr(); /* search for valid inputpath in filename */
 char subexpr[MAXFILESZ+1] = "\000", /*concatanated lines from input file*/  char subexpr[MAXFILESZ+1] = "\000", /*concatanated lines from input file*/
  *mimeprep(), /* preprocess inputted data */   *mimeprep(), /* preprocess inputted data */
  *dbltoa(), *reformat=NULL; /* reformat numerical input */   *dbltoa(), *reformat=NULL; /* reformat numerical input */
Line 10291  obtain [tag]{filename} argument Line 11664  obtain [tag]{filename} argument
 /* --- parse for optional [tag] or [fmt] arg, bump expression past it --- */  /* --- parse for optional [tag] or [fmt] arg, bump expression past it --- */
 if ( *(*expression) == '[' ) /* check for []-enclosed value */  if ( *(*expression) == '[' ) /* check for []-enclosed value */
   { char argfld[MAXTOKNSZ+1]; /* optional argument field */    { char argfld[MAXTOKNSZ+1]; /* optional argument field */
     *expression = texsubexpr(*expression,argfld,MAXTOKNSZ,"[","]",0,0);      *expression = texsubexpr(*expression,argfld,MAXTOKNSZ-1,"[","]",0,0);
     if ( (reformat=strstr(argfld,"dtoa")) != NULL ) /*dtoa/dbltoa requested*/      if ( (reformat=strstr(argfld,"dtoa")) != NULL ) /*dtoa/dbltoa requested*/
       { format = 1; /* signal dtoa()/dbltoa() format */        { format = 1; /* signal dtoa()/dbltoa() format */
  if ( (reformat=strchr(reformat,'=')) != NULL ) /* have dtoa= */   if ( (reformat=strchr(reformat,'=')) != NULL ) /* have dtoa= */
   npts = (int)strtol(reformat+1,NULL,0); } /* so set npts */    npts = (int)strtol(reformat+1,NULL,0); } /* so set npts */
     if ( format == 0 ) /* reformat not requested */      if ( format == 0 ) { /* reformat not requested */
       strcpy(tag,argfld); } /* so interpret arg as tag */        strninit(tag,argfld,1020); } } /* so interpret arg as tag */
 /* --- parse for {filename} arg, and bump expression past it --- */  /* --- parse for {filename} arg, and bump expression past it --- */
 *expression = texsubexpr(*expression,filename,1023,"{","}",0,0);  *expression = texsubexpr(*expression,filename,1020,"{","}",0,0);
 /* --- check for alternate filename:tag --- */  /* --- check for alternate filename:tag --- */
 if ( *filename != '\000' /* got filename */  if ( !isempty(filename) /* got filename */
 /*&& *tag == '\000'*/ ) /* but no [tag] */  /*&& isempty(tag)*/ ) /* but no [tag] */
  { char *delim = strchr(filename,':'); /* look for : in filename:tag */   { char *delim = strchr(filename,':'); /* look for : in filename:tag */
    if ( delim != (char *)NULL ) /* found it */     if ( delim != (char *)NULL ) /* found it */
     { *delim = '\000'; /* null-terminate filename at : */      { *delim = '\000'; /* null-terminate filename at : */
       strcpy(tag,delim+1); } } /* and stuff after : is tag */        strninit(tag,delim+1,1020); } } /* and stuff after : is tag */
   /* --- check filename for an inputpath valid for all users --- */
   if ( !isinput /* if this user can't \input{} */
   &&   !isempty(filename) /* and we got a filename */
   &&   !isempty(inputpath) ) /* and an inputpath */
     if ( isstrstr(filename,inputpath,0) ) /* filename has allowed inputpath */
       isinput = 1; /* okay to \input{} this filename */
   /* --- guard against recursive runaway (e.g., file \input's itself) --- */
   if ( ++ninputcmds > 8 ) /* max \input's per expression */
     isinput = 0; /* flip flag off after the max */
   /* --------------------------------------------------------------------------
   Read file (and convert to numeric if [dtoa] option was given)
   -------------------------------------------------------------------------- */
   if ( isinput ) { /* user permitted to use \input{} */
     status = rastreadfile(filename,0,tag,subexpr); /* read file */
     if ( *subexpr == '\000' ) goto end_of_job;   /* quit if problem */
     /* --- rasterize input subexpression  --- */
     mimeprep(subexpr); /* preprocess subexpression */
     if ( format == 1 ) { /* dtoa()/dbltoa() */
       double d = strtod(subexpr,NULL); /* interpret subexpr as double */
       if ( d != 0.0 ) /* conversion to double successful */
         if ( (reformat=dbltoa(d,npts)) != NULL ) /* reformat successful */
           strcpy(subexpr,reformat); } /*replace subexpr with reformatted*/
     } /* --- end-of-if(isinput) --- */
 /* --------------------------------------------------------------------------  /* --------------------------------------------------------------------------
 Read file and rasterize constructed subexpression  emit error message for unauthorized users trying to use \input{}
   -------------------------------------------------------------------------- */
   else { /* inputseclevel > seclevel */
     sprintf(subexpr,
     "\\ \\text{[\\backslash input\\lbrace %.128s\\rbrace\\ not permitted]}\\ ",
     (isempty(filename)?"???":filename));
     } /* --- end-of-if/else(isinput) --- */
   /* --------------------------------------------------------------------------
   Rasterize constructed subexpression
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 status = rastreadfile(filename,0,tag,subexpr); /* read file */  
 if ( *subexpr == '\000' ) goto end_of_job;   /* quit if problem */  
 /* --- rasterize input subexpression  --- */  
 mimeprep(subexpr); /* preprocess subexpression */  
 if ( format == 1 ) /* dtoa()/dbltoa() */  
  { double d = strtod(subexpr,NULL); /* interpret subexpr as double */  
    if ( d != 0.0 ) /* conversion to double successful */  
     if ( (reformat=dbltoa(d,npts)) != NULL ) /* reformat successful */  
      strcpy(subexpr,reformat); } /*replace subexpr with reformatted*/  
 inputsp = rasterize(subexpr,size); /* rasterize subexpression */  inputsp = rasterize(subexpr,size); /* rasterize subexpression */
 /* --- return input image to caller --- */  /* --- return input image to caller --- */
 end_of_job:  end_of_job:
Line 10336  end_of_job: Line 11731  end_of_job:
  * string immediately following \counter to be   * string immediately following \counter to be
  * rasterized, and returning ptr immediately   * rasterized, and returning ptr immediately
  * following last character processed.   * following last character processed.
  * size (I) int containing 0-5 default font size   * size (I) int containing 0-7 default font size
  * basesp (I) subraster *  to character (or subexpression)   * basesp (I) subraster *  to character (or subexpression)
  * immediately preceding \counter   * immediately preceding \counter
  * (unused, but passed for consistency)   * (unused, but passed for consistency)
Line 10363  char *texsubexpr(), filename[1024]="\000 Line 11758  char *texsubexpr(), filename[1024]="\000
 subraster *rasterize(), *countersp=NULL; /* rasterized counter image */  subraster *rasterize(), *countersp=NULL; /* rasterized counter image */
 FILE /* *fp=NULL,*/ *logfp=NULL; /* counter and log file pointers */  FILE /* *fp=NULL,*/ *logfp=NULL; /* counter and log file pointers */
 int status=0,rastreadfile(),rastwritefile(), /*read,write counter file*/  int status=0,rastreadfile(),rastwritefile(), /*read,write counter file*/
    iscounter = (seclevel<=counterseclevel?1:0), /*is \counter permitted*/
  isstrict = 1; /* true to only write to existing files */   isstrict = 1; /* true to only write to existing files */
 char text[MAXFILESZ] = "1_", /* only line in counter file without tags */  char text[MAXFILESZ] = "1_", /* only line in counter file without tags */
  *delim = NULL, /* delimiter in text */   *delim = NULL, /* delimiter in text */
Line 10425  if ( *filename != '\000' )  /* got filen Line 11821  if ( *filename != '\000' )  /* got filen
   { *delim = '\000'; /* null-terminate filename at : */    { *delim = '\000'; /* null-terminate filename at : */
     strcpy(tag,delim+1); } /* and stuff after : is tag */      strcpy(tag,delim+1); } /* and stuff after : is tag */
 /* --------------------------------------------------------------------------  /* --------------------------------------------------------------------------
   emit error message for unauthorized users trying to use \counter{}
   -------------------------------------------------------------------------- */
   if ( !iscounter ) { /* counterseclevel > seclevel */
    sprintf(text,
    "\\ \\text{[\\backslash counter\\lbrace %.128s\\rbrace\\ not permitted]}\\ ",
    (isempty(filename)?"???":filename));
    goto rasterize_counter; /* rasterize error message */
    } /* --- end-of-if(!iscounter) --- */
   /* --------------------------------------------------------------------------
 Read and parse file, increment and rewrite counter (with optional underscore)  Read and parse file, increment and rewrite counter (with optional underscore)
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 if ( strlen(filename) > 1 ) /* make sure we got {filename} arg */  if ( strlen(filename) > 1 ) /* make sure we got {filename} arg */
Line 10502  if ( ordindex >= 0 )   /* need to tack o Line 11907  if ( ordindex >= 0 )   /* need to tack o
     strcat(text,ordinal[ordindex]); /* then st,nd,rd, or th */      strcat(text,ordinal[ordindex]); /* then st,nd,rd, or th */
     strcat(text,"}}"); } /* finish with }} */      strcat(text,"}}"); } /* finish with }} */
 /* --- rasterize it --- */  /* --- rasterize it --- */
 countersp = rasterize(text,size); /* rasterize counter subexpression */  rasterize_counter:
     countersp = rasterize(text,size); /* rasterize counter subexpression */
 /* --- return counter image to caller --- */  /* --- return counter image to caller --- */
 /*end_of_job:*/  /*end_of_job:*/
   return ( countersp ); /* return counter image to caller */    return ( countersp ); /* return counter image to caller */
Line 10510  countersp = rasterize(text,size); /* ras Line 11916  countersp = rasterize(text,size); /* ras
   
   
 /* ==========================================================================  /* ==========================================================================
    * Function: rasteval ( expression, size, basesp, arg1, arg2, arg3 )
    * Purpose: handle \eval
    * --------------------------------------------------------------------------
    * Arguments: expression (I/O) char **  to first char of null-terminated
    * string immediately following \eval,
    * and returning ptr immediately
    * following last character processed.
    * size (I) int containing 0-7 default font size
    * basesp (I) subraster *  to character (or subexpression)
    * immediately preceding \eval
    * (unused, but passed for consistency)
    * arg1 (I) int unused
    * arg2 (I) int unused
    * arg3 (I) int unused
    * --------------------------------------------------------------------------
    * Returns: ( subraster * ) subraster ptr to date stamp
    * --------------------------------------------------------------------------
    * Notes:     o
    * ======================================================================= */
   /* --- entry point --- */
   subraster *rasteval ( char **expression, int size, subraster *basesp,
    int arg1, int arg2, int arg3 )
   {
   /* -------------------------------------------------------------------------
   Allocations and Declarations
   -------------------------------------------------------------------------- */
   char *texsubexpr(), subexpr[MAXSUBXSZ]; /* arg to be evaluated */
   subraster *rasterize(), *evalsp=NULL; /* rasterize evaluated expression */
   int evalterm(), value=0; /* evaluate expression */
   /* -------------------------------------------------------------------------
   Parse for subexpr to be \eval-uated, and bump expression past it
   -------------------------------------------------------------------------- */
   *expression = texsubexpr(*expression,subexpr,0,"{","}",0,0);
   if ( *subexpr == '\000' ) /* couldn't get subexpression */
     goto end_of_job; /* nothing to do, so quit */
   /* -------------------------------------------------------------------------
   Evaluate expression, ascii-ize integer result, rasterize it
   -------------------------------------------------------------------------- */
   /* --- evaluate expression --- */
   value = evalterm(mimestore,subexpr); /* evaluate expression */
   /* --- ascii-ize it --- */
   sprintf(subexpr,"%d",value); /* ascii version of value */
   /* --- rasterize ascii-ized expression value --- */
   evalsp = rasterize(subexpr,size); /* rasterize evaluated expression */
   /* --- return evaluated expression raster to caller --- */
   end_of_job:
     return ( evalsp ); /* return evaluated expr to caller */
   } /* --- end-of-function rasteval() --- */
   
   
   /* ==========================================================================
  * Function: rasttoday ( expression, size, basesp, arg1, arg2, arg3 )   * Function: rasttoday ( expression, size, basesp, arg1, arg2, arg3 )
  * Purpose: handle \today   * Purpose: handle \today
  * --------------------------------------------------------------------------   * --------------------------------------------------------------------------
Line 10517  countersp = rasterize(text,size); /* ras Line 11974  countersp = rasterize(text,size); /* ras
  * string immediately following \today,   * string immediately following \today,
  * and returning ptr immediately   * and returning ptr immediately
  * following last character processed.   * following last character processed.
  * size (I) int containing 0-5 default font size   * size (I) int containing 0-7 default font size
  * basesp (I) subraster *  to character (or subexpression)   * basesp (I) subraster *  to character (or subexpression)
  * immediately preceding \today   * immediately preceding \today
  * (unused, but passed for consistency)   * (unused, but passed for consistency)
Line 10578  todaysp = rasterize(today,size); /* rast Line 12035  todaysp = rasterize(today,size); /* rast
  * string immediately following \calendar   * string immediately following \calendar
  * and returning ptr immediately   * and returning ptr immediately
  * following last character processed.   * following last character processed.
  * size (I) int containing 0-5 default font size   * size (I) int containing 0-7 default font size
  * basesp (I) subraster *  to character (or subexpression)   * basesp (I) subraster *  to character (or subexpression)
  * immediately preceding \calendar   * immediately preceding \calendar
  * (unused, but passed for consistency)   * (unused, but passed for consistency)
Line 10639  calendarsp = rasterize(calstr,size); /* Line 12096  calendarsp = rasterize(calstr,size); /*
   
   
 /* ==========================================================================  /* ==========================================================================
    * Function: rastenviron ( expression, size, basesp, arg1, arg2, arg3 )
    * Purpose: handle \environment
    * --------------------------------------------------------------------------
    * Arguments: expression (I/O) char **  to first char of null-terminated
    * string immediately following \environment
    * and returning ptr immediately
    * following last character processed (in this
    * case, \environment takes no arguments, so
    * expression is returned unchanged).
    * size (I) int containing 0-7 default font size
    * basesp (I) subraster *  to character (or subexpression)
    * immediately preceding \environment
    * (unused, but passed for consistency)
    * arg1 (I) int unused
    * arg2 (I) int unused
    * arg3 (I) int unused
    * --------------------------------------------------------------------------
    * Returns: ( subraster * ) subraster ptr to rendered environment image
    * --------------------------------------------------------------------------
    * Notes:     o
    * ======================================================================= */
   /* --- entry point --- */
   subraster *rastenviron ( char **expression, int size, subraster *basesp,
    int arg1, int arg2, int arg3 )
   {
   /* -------------------------------------------------------------------------
   Allocations and Declarations
   -------------------------------------------------------------------------- */
   char *texsubexpr(), optarg[255]; /* optional [...] args (for future)*/
   char environstr[8192] = "\000", /* string for all environment vars */
    environvar[1024] = "\000"; /* one environment variable */
   char *strwrap(), /* wrap long lines */
    *strdetex(), /* removes/replaces any math chars */
    *environptr = NULL; /* ptr to preprocessed environvar */
   char *mimeprep(); /* preprocess environvar string */
   int unescape_url(); /* convert all %xx's to chars */
   int isenviron = (seclevel<=environseclevel?1:0); /*is \environ permitted*/
   int maxvarlen = 512, /* max chars in environment var */
    maxenvlen = 6400, /* max chars in entire string */
    wraplen = 48; /* strwrap() wrap lines at 48 chars*/
   int ienv = 0; /* environ[] index */
   subraster *rasterize(), *environsp=NULL; /* rasterize environment string */
   /* -------------------------------------------------------------------------
   Get args 
   -------------------------------------------------------------------------- */
   /* --- check for optional \environment args --- */
   if ( 1 ) /* there aren't any args (yet) */
    if ( *(*expression) == '[' ) { /* check for []-enclosed value */
      *expression = texsubexpr(*expression,optarg,250,"[","]",0,0);
      if ( *optarg != '\000' ) { ; /* got optional arg, so process it */
        wraplen = atoi(optarg); /* interpret \environment[wraplen] */
        if ( wraplen < 1 ) wraplen = 8; /* set minimum */
        } /* --- end-of-if(*optarg!='\0') --- */
      } /* --- end-of-if(**expression=='[') --- */
   /* --------------------------------------------------------------------------
   emit error message for unauthorized users trying to use \environ
   -------------------------------------------------------------------------- */
   if ( !isenviron ) { /* environseclevel > seclevel */
     sprintf(environstr,
     "\\ \\text{[\\backslash environment\\ not permitted]}\\ ");
     goto rasterize_environ; /* rasterize error message */
     } /* --- end-of-if(!isenviron) --- */
   /* -------------------------------------------------------------------------
   Accumulate environment variables and rasterize string containing them
   -------------------------------------------------------------------------- */
   *environstr = '\000'; /* reset environment string */
   strcat(environstr,"\\nocaching\\fbox{\\normalsize\\text{"); /*init string*/
   for ( ienv=0; ; ienv++ ) { /* loop over environ[] strings */
     if ( environ[ienv] == (char *)NULL ) break; /* null terminates list */
     if ( *(environ[ienv]) == '\000' ) break; /* double-check empty string */
     strninit(environvar,environ[ienv],maxvarlen); /* max length displayed */
     if ( strlen(environ[ienv]) > maxvarlen ) /* we truncated the variable */
       strcat(environvar,"..."); /* so add an ellipsis */
     unescape_url(environvar,0); /* convert all %xx's to chars */
     environptr = strdetex(environvar,1); /* remove/replace any math chars */
     strninit(environvar,environptr,maxvarlen); /*de-tex'ed/nomath environvar*/
     environptr = strwrap(environvar,wraplen,-6); /* wrap long lines */
     strninit(environvar,environptr,maxvarlen); /* line-wrapped environvar */
     mimeprep(environvar); /* preprocess environvar string */
     if ( strlen(environstr) + strlen(environvar) > maxenvlen ) break;
     sprintf(environstr+strlen(environstr), /* display environment string */
     " %2d. %s\\\\\n", ienv+1,environvar);
     if ( msgfp!= NULL && msglevel>=9 )
       fprintf(msgfp,"rastenviron> %2d. %.256s\n",
       ienv+1,/*environ[ienv]*/environvar);
     if ( strlen(environstr) >= 7200 ) break; /* don't overflow buffer */
     } /* --- end-of-for(ienv) --- */
   strcat(environstr,"}}"); /* end {\text{...}} mode */
   rasterize_environ:
     environsp = rasterize(environstr,size); /* rasterize environment string */
   /* --- return environment raster to caller --- */
   /*end_of_job:*/
     return ( environsp ); /* return environment to caller */
   } /* --- end-of-function rastenviron() --- */
   
   
   /* ==========================================================================
    * Function: rastmessage ( expression, size, basesp, arg1, arg2, arg3 )
    * Purpose: handle \message
    * --------------------------------------------------------------------------
    * Arguments: expression (I/O) char **  to first char of null-terminated
    * string immediately following \message
    * and returning ptr immediately
    * following last character processed.
    * size (I) int containing 0-7 default font size
    * basesp (I) subraster *  to character (or subexpression)
    * immediately preceding \mesasge
    * (unused, but passed for consistency)
    * arg1 (I) int unused
    * arg2 (I) int unused
    * arg3 (I) int unused
    * --------------------------------------------------------------------------
    * Returns: ( subraster * ) subraster ptr to rendered message image
    * --------------------------------------------------------------------------
    * Notes:     o
    * ======================================================================= */
   /* --- entry point --- */
   subraster *rastmessage ( char **expression, int size, subraster *basesp,
    int arg1, int arg2, int arg3 )
   {
   /* -------------------------------------------------------------------------
   Allocations and Declarations
   -------------------------------------------------------------------------- */
   char *texsubexpr(), amsg[256]="\000"; /* message number text */
   int imsg = 0; /* default message number */
   char msg[4096];
   subraster *rasterize(), *messagesp=NULL; /* rasterize requested message */
   int strreplace(); /*replace SERVER_NAME in refmsgnum*/
   int reflevels = REFLEVELS; /* #topmost levels to match */
   char *urlprune(); /*prune referer_match in refmsgnum*/
   char *strdetex(); /* remove math chars from messages */
   char *http_host    = getenv("HTTP_HOST"), /* http host for mimeTeX */
    *server_name  = getenv("SERVER_NAME"), /* server hosting mimeTeX */
    *referer_match = (!isempty(http_host)?http_host: /*match http_host*/
     (!isempty(server_name)?server_name:(NULL))); /* or server_name */
   /* -------------------------------------------------------------------------
   obtain message {amsg} argument
   -------------------------------------------------------------------------- */
   /* --- parse for {amsg} arg, and bump expression past it --- */
   *expression = texsubexpr(*expression,amsg,255,"{","}",0,0);
   /* --- interpret argument --- */
   if ( *amsg != '\000' ) { /* got amsg arg */
     imsg = atoi(amsg); /* interpret as an int */
     if ( imsg < 0 /* if too small */
     ||   imsg > maxmsgnum ) /* or too big */
       imsg = 0; } /* default to first message */
   /* --- retrieve requested message --- */
   strninit(msg,msgtable[imsg],4095); /* local copy of message */
   /* --- process as necessary --- */
   if ( imsg == refmsgnum) { /* urlncmp() failed to validate */
     if ( reflevels > 0 ) /* have #levels to validate */
      strreplace(msg,"SERVER_NAME", /* replace SERVER_NAME */
       strdetex(urlprune(referer_match,reflevels),1),0); /*with referer_match*/
     } /* --- end-of-switch(imsg) --- */
   /* --- rasterize requested message --- */
   messagesp = rasterize(msg,size); /* rasterize message string */
   /* --- return message raster to caller --- */
   /*end_of_job:*/
     return ( messagesp ); /* return message to caller */
   } /* --- end-of-function rastmessage() --- */
   
   
   /* ==========================================================================
  * Function: rastnoop ( expression, size, basesp, nargs, arg2, arg3 )   * Function: rastnoop ( expression, size, basesp, nargs, arg2, arg3 )
  * Purpose: no op -- flush \escape without error   * Purpose: no op -- flush \escape without error
  * --------------------------------------------------------------------------   * --------------------------------------------------------------------------
Line 10778  while ( strreplace(editname,"....",NULL, Line 12398  while ( strreplace(editname,"....",NULL,
 /* --- remove leading / and \ and dots (and blanks) --- */  /* --- remove leading / and \ and dots (and blanks) --- */
 if ( *editname != '\000' ) /* still have chars in filename */  if ( *editname != '\000' ) /* still have chars in filename */
  while ( isthischar(*editname," ./\\") ) /* absolute paths invalid */   while ( isthischar(*editname," ./\\") ) /* absolute paths invalid */
    strcpy(editname,editname+1); /* so flush leading / or \ (or ' ')*/     {strsqueeze(editname,1);} /* so flush leading / or \ (or ' ')*/
 if ( *editname == '\000' ) goto end_of_job; /* no chars left in filename */  if ( *editname == '\000' ) goto end_of_job; /* no chars left in filename */
 /* --- remove leading or embedded ../'s and ..\'s --- */  /* --- remove leading or embedded ../'s and ..\'s --- */
 while ( strreplace(editname,"../",NULL,0) > 0 ) ;  /* squeeze out ../'s */  while ( strreplace(editname,"../",NULL,0) > 0 ) ;  /* squeeze out ../'s */
Line 10862  while ( fgets(text,MAXLINESZ-1,fp) != (c Line 12482  while ( fgets(text,MAXLINESZ-1,fp) != (c
     case 0: status = 1; break; /* no tag to look for */      case 0: status = 1; break; /* no tag to look for */
     case 1: /* looking for opening left <tag> */      case 1: /* looking for opening left <tag> */
       if ( (tagp=strstr(text,tag1)) == NULL ) break; /*haven't found it yet*/        if ( (tagp=strstr(text,tag1)) == NULL ) break; /*haven't found it yet*/
       strcpy(text,tagp+strlen(tag1)); /* shift out preceding text */        tagp += strlen(tag1); /* first char past tag */
         strsqueezep(text,tagp); /*shift out preceding text and tag*/
       tagnum = 2; /*now looking for closing right tag*/        tagnum = 2; /*now looking for closing right tag*/
     case 2: /* looking for closing right </tag> */      case 2: /* looking for closing right </tag> */
       if ( (tagp=strstr(text,tag2)) == NULL ) break; /*haven't found it yet*/        if ( (tagp=strstr(text,tag2)) == NULL ) break; /*haven't found it yet*/
Line 10971  if ( istag )    /* only replacing tag in Line 12592  if ( istag )    /* only replacing tag in
   {    {
   /* --- preprocess filebuff --- */    /* --- preprocess filebuff --- */
   if ( tagp2 != (char *)NULL ) /* apparently have ...</tag> */    if ( tagp2 != (char *)NULL ) /* apparently have ...</tag> */
     strcpy(filebuff,tagp2+tlen2); /* so get rid of leading ...</tag> */      {strsqueezep(filebuff,tagp2+tlen2);} /* remove ...</tag> */
   if ( (flen = strlen(filebuff)) /* #chars currently in buffer */    if ( (flen = strlen(filebuff)) /* #chars currently in buffer */
   > 0 ) /* we have non-empty buffer */    > 0 ) /* we have non-empty buffer */
    if (!isthischar(*(filebuff+flen-1),"\n\r")) /*no newline at end of file*/     if (!isthischar(*(filebuff+flen-1),"\n\r")) /*no newline at end of file*/
Line 11157  static char timebuff[256];  /* date:time Line 12778  static char timebuff[256];  /* date:time
 time_t time_val = (time_t)(0); /* binary value returned by time() */  time_t time_val = (time_t)(0); /* binary value returned by time() */
 struct tm *tmstruct=(struct tm *)NULL, *localtime(); /* interpret time_val */  struct tm *tmstruct=(struct tm *)NULL, *localtime(); /* interpret time_val */
 int year=0, hour=0,ispm=1, /* adjust year, and set am/pm hour */  int year=0, hour=0,ispm=1, /* adjust year, and set am/pm hour */
  month=0, day=0; /* adjust day and month for delta  */   month=0, day=0, /* adjust day and month for delta  */
    minute=0,second=0; /* minute and second not adjusted  */
 int tzadjust(); /* time zone adjustment function */  int tzadjust(); /* time zone adjustment function */
 int daynumber(); /* #days since Jan 1, 1973 */  int daynumber(); /* #days since Jan 1, 1973 */
 static char *daynames[] = { "Monday", "Tuesday", "Wednesday",  static char *daynames[] = { "Monday", "Tuesday", "Wednesday",
Line 11178  year  = (int)(tmstruct->tm_year); /* loc Line 12800  year  = (int)(tmstruct->tm_year); /* loc
 month = (int)(tmstruct->tm_mon) + 1; /* local copy of month, 1-12 */  month = (int)(tmstruct->tm_mon) + 1; /* local copy of month, 1-12 */
 day   = (int)(tmstruct->tm_mday); /* local copy of day,   1-31 */  day   = (int)(tmstruct->tm_mday); /* local copy of day,   1-31 */
 hour  = (int)(tmstruct->tm_hour); /* local copy of hour,  0-23 */  hour  = (int)(tmstruct->tm_hour); /* local copy of hour,  0-23 */
   minute= (int)(tmstruct->tm_min); /* local copy of minute,0-59 */
   second= (int)(tmstruct->tm_sec); /* local copy of second,0-59 */
 /* --- adjust year --- */  /* --- adjust year --- */
 year += 1900; /* set century in year */  year += 1900; /* set century in year */
 /* --- adjust for timezone --- */  /* --- adjust for timezone --- */
Line 11204  switch ( ifmt ) Line 12828  switch ( ifmt )
   default:    default:
   case 0:  /* --- 2005-03-05:11:49:59am --- */    case 0:  /* --- 2005-03-05:11:49:59am --- */
     sprintf(timebuff,"%04d-%02d-%02d:%02d:%02d:%02d%s", year,month,day,      sprintf(timebuff,"%04d-%02d-%02d:%02d:%02d:%02d%s", year,month,day,
     hour,(int)(tmstruct->tm_min),(int)(tmstruct->tm_sec),((ispm)?"pm":"am"));      hour,minute,second,((ispm)?"pm":"am"));
     break;      break;
   case 1:  /* --- Saturday, March 5, 2005 --- */    case 1:  /* --- Saturday, March 5, 2005 --- */
     sprintf(timebuff,"%s, %s %d, %d",      sprintf(timebuff,"%s, %s %d, %d",
Line 11213  switch ( ifmt ) Line 12837  switch ( ifmt )
   case 2: /* --- Saturday, March 5, 2005, 11:49:59am --- */    case 2: /* --- Saturday, March 5, 2005, 11:49:59am --- */
     sprintf(timebuff,"%s, %s %d, %d, %d:%02d:%02d%s",      sprintf(timebuff,"%s, %s %d, %d, %d:%02d:%02d%s",
     daynames[daynumber(year,month,day)%7],monthnames[month],day,year,      daynames[daynumber(year,month,day)%7],monthnames[month],day,year,
     hour,(int)(tmstruct->tm_min),(int)(tmstruct->tm_sec),((ispm)?"pm":"am"));      hour,minute,second,((ispm)?"pm":"am"));
     break;      break;
   case 3: /* --- 11:49:59am --- */    case 3: /* --- 11:49:59am --- */
     sprintf(timebuff,"%d:%02d:%02d%s",      sprintf(timebuff,"%d:%02d:%02d%s",
     hour,(int)(tmstruct->tm_min),(int)(tmstruct->tm_sec),((ispm)?"pm":"am"));      hour,minute,second,((ispm)?"pm":"am"));
       break;
     case 4: /* --- 1231235959 (mmddhhmmss time as integer) --- */
       sprintf(timebuff,"%d%02d%02d%02d%02d",
       month,day,hour,minute,second);
     break;      break;
   } /* --- end-of-switch(ifmt) --- */    } /* --- end-of-switch(ifmt) --- */
 end_of_job:  end_of_job:
Line 11349  return ( (int)(ndays) );  /* #days back Line 12977  return ( (int)(ndays) );  /* #days back
   
   
 /* ==========================================================================  /* ==========================================================================
    * Function: strwrap ( s, linelen, tablen )
    * Purpose: Inserts \n's and spaces in (a copy of) s to wrap lines
    * at linelen and indent them by tablen.
    * --------------------------------------------------------------------------
    * Arguments: s (I) char * to null-terminated string
    * to be wrapped.
    * linelen (I) int containing maximum linelen
    * between \\'s.
    * tablen (I) int containing number of spaces to indent
    * lines.  0=no indent.  Positive means
    * only indent first line and not others.
    * Negative means indent all lines except first.
    * --------------------------------------------------------------------------
    * Returns: ( char * ) ptr to "line-wrapped" copy of s
    * or "" (empty string) for any error.
    * --------------------------------------------------------------------------
    * Notes:     o The returned copy of s has embedded \\'s as necessary
    * to wrap lines at linelen.  Any \\'s in the input copy
    * are removed first.  If (and only if) the input s contains
    * a terminating \\ then so does the returned copy.
    *      o The returned pointer addresses a static buffer,
    * so don't call strwrap() again until you're finished
    * with output from the preceding call.
    *      o Modified for mimetex from original version written
    * for mathtex (where \n in verbatim mode instead of \\
    * produced linebreaks).
    * ======================================================================= */
   /* --- entry point --- */
   char *strwrap ( char *s, int linelen, int tablen )
   {
   /* -------------------------------------------------------------------------
   Allocations and Declarations
   -------------------------------------------------------------------------- */
   static char sbuff[4096]; /* line-wrapped copy of s */
   char *sol = sbuff; /* ptr to start of current line*/
   char tab[32] = "                 "; /* tab string */
   int strreplace(); /* remove \n's */
   char *strchange(); /* add \n's and indent space */
   int finalnewline = (lastchar(s)=='\n'?1:0); /*newline at end of string?*/
   int istab = (tablen>0?1:0), /* init true to indent first line */
    iswhite = 0; /* true if line break on whitespace*/
   int rhslen  = 0, /* remaining right hand side length*/
    thislen = 0, /* length of current line segment */
    thistab = 0, /* length of tab on current line */
    wordlen = 0; /* length to next whitespace char */
   /* -------------------------------------------------------------------------
   Make a clean copy of s
   -------------------------------------------------------------------------- */
   /* --- check input --- */
   *sbuff = '\000'; /* initialize in case of error */
   if ( isempty(s) ) goto end_of_job; /* no input */
   if ( tablen < 0 ) tablen = (-tablen); /* set positive tablen */
   if ( tablen >= linelen ) tablen = linelen-1; /* tab was longer than line */
   tab[min2(tablen,16)] = '\000'; /* null-terminate tab string */
   tablen = strlen(tab); /* reset to actual tab length */
   finalnewline = 0; /* turned off for mimetex version */
   /* --- start with copy of s --- */
   strninit(sbuff,s,3000); /* leave room for \n's and tabs */
   if ( linelen < 1 ) goto end_of_job; /* can't do anything */
   trimwhite(sbuff); /*remove leading/trailing whitespace*/
   strreplace(sbuff,"\n"," ",0); /* remove any original \n's */
   strreplace(sbuff,"\r"," ",0); /* remove any original \r's */
   strreplace(sbuff,"\t"," ",0); /* remove any original \t's */
   strreplace(sbuff,"\f"," ",0); /* remove any original \f's */
   strreplace(sbuff,"\v"," ",0); /* remove any original \v's */
   strreplace(sbuff,"\\\\"," ",0); /* remove any original \\'s */
   /* -------------------------------------------------------------------------
   Insert \\'s and spaces as needed
   -------------------------------------------------------------------------- */
   while ( 1 ) { /* till end-of-line */
     /* --- init --- */
     trimwhite(sol); /*remove leading/trailing whitespace*/
     thislen = thistab = 0; /* no chars in current line yet */
     if ( istab && tablen>0 ) { /* need to indent this line */
       strchange(0,sol,tab); /* insert indent at start of line */
       thistab = tablen; } /* line starts with whitespace tab */
     if ( sol == sbuff ) istab = 1-istab; /* flip tab flag after first line */
     sol += thistab; /* skip tab */
     rhslen = strlen(sol); /* remaining right hand side chars */
     if ( rhslen+thistab <= linelen ) break; /* no more \\'s needed */
     if ( 0 && msgfp!=NULL && msglevel >= 99 ) {
       fprintf(msgfp,"strwrap> rhslen=%d, sol=\"\"%s\"\"\n",rhslen,sol);
       fflush(msgfp); }
     /* --- look for last whitespace preceding linelen --- */
     while ( 1 ) { /* till we exceed linelen */
       wordlen = strcspn(sol+thislen," \t\n\r\f\v :;.,"); /*ptr to next white/break*/
       if ( sol[thislen+wordlen] == '\000' ) /* no more whitespace in string */
         goto end_of_job; /* so nothing more we can do */
       if ( thislen+thistab+wordlen >= linelen ) /* next word won't fit */
         if ( thislen > 0 ) break; /* but make sure line has one word */
       thislen += (wordlen+1); } /* ptr past next whitespace char */
     if ( thislen < 1 ) break; /* line will have one too-long word*/
     /*sol[thislen-1] = '\n';*/ /* replace last space with newline */
     /*sol += thislen;*/ /* next line starts after newline */
     iswhite = (isthischar(sol[thislen-1],":;.,")?0:1); /*linebreak on space?*/
     strchange(iswhite,sol+thislen-iswhite,"\\\\"); /* put \\ at end of line */
     sol += (thislen+2-iswhite); /* next line starts after \\ */
     } /* --- end-of-while(1) --- */
   end_of_job:
     if ( finalnewline ) strcat(sbuff,"\\\\"); /* replace final newline */
     return ( sbuff ); /* back with clean copy of s */
   } /* --- end-of-function strwrap() --- */
   
   
   /* ==========================================================================
    * Function: strnlower ( s, n )
    * Purpose: lowercase the first n chars of string s
    * --------------------------------------------------------------------------
    * Arguments: s (I/O) (char *)pointer to null-terminated string
    * whose chars are to be lowercased
    * n (I) int containing max number of chars to be
    * lowercased (less than n will be lowercased
    * if terminating '\000' found first)
    * If n<=0 (or n>=strlen(s)) then the entire
    * string s will be lowercased
    * --------------------------------------------------------------------------
    * Returns: ( char * ) s (always same as input)
    * --------------------------------------------------------------------------
    * Notes:     o
    * ======================================================================= */
   /* --- entry point --- */
   char *strnlower ( char *s, int n )
   {
   /* -------------------------------------------------------------------------
   lowercase s
   -------------------------------------------------------------------------- */
   char *p = s; /* save s for return to caller */
   if ( !isempty(s) ) /* check for valid input */
     while ( *p != '\000' ) { /* lowercase each char till end */
       *p = tolower(*p); /* lowercase this char */
       if ( n > 0 ) /* only lowercase first n chars */
         if ( --n < 1 ) break; /* quit when we're done */
       p++; } /* proceed to next char */
   return ( s ); /* back to caller with s */
   } /* --- end-of-function strnlower() --- */
   
   
   /* ==========================================================================
    * Function: urlprune ( url, n )
    * Purpose: Prune http://abc.def.ghi.com/etc into abc.def.ghi.com
    * (if n=2 only ghi.com is returned, or if n=-1 only "ghi")
    * --------------------------------------------------------------------------
    * Arguments: url (I) char * to null-terminated string
    * containing url to be pruned
    * n (i) int containing number of levels retained
    * in pruned url.  If n<0 its abs() is used,
    * but the topmost level (usually .com, .org,
    * etc) is omitted.  That is, if n=2 would
    * return "ghi.com" then n=-1 returns "ghi".
    * n=0 retains all levels.
    * --------------------------------------------------------------------------
    * Returns: ( char * ) pointer to (static) null-terminated string
    * containing pruned url with the first n
    * top-level domain, e.g., for n=2,
    * http://abc.def.ghi.com/etc returns ghi.com,
    * or an empty string "\000" for any error
    * --------------------------------------------------------------------------
    * Notes:     o
    * ======================================================================= */
   /* --- entry point --- */
   char *urlprune ( char *url, int n )
   {
   /* -------------------------------------------------------------------------
   Allocations and Declarations
   -------------------------------------------------------------------------- */
   static char pruned[2048]; /* pruned url returned to caller */
   char *purl = /*NULL*/pruned; /* ptr to pruned, init for error */
   char *delim = NULL; /* delimiter separating components */
   char *strnlower(); /* lowercase a string */
   int istruncate = (n<0?1:0); /*true to truncate .com from pruned*/
   int ndots = 0; /* number of dots found in url */
   /* -------------------------------------------------------------------------
   prune the url
   -------------------------------------------------------------------------- */
   /* --- first check input --- */
   *pruned = '\000'; /* init for error */
   if ( isempty(url) ) goto end_of_job; /* missing input, so return NULL */
   if ( n < 0 ) n = (-n); /* flip n positive */
   if ( n == 0 ) n = 999; /* retain all levels of url */
   /* --- preprocess url --- */
   strninit(pruned,url,2032); /* copy url to our static buffer */
   strlower(pruned); /* lowercase it and... */
   trimwhite(pruned); /*remove leading/trailing whitespace*/
   /* --- first remove leading http:// --- */
   if ( (delim=strstr(pruned,"://")) != NULL ) /* found http:// or ftp:// etc */
     if ( ((int)(delim-pruned)) <= 8 ) { /* make sure it's a prefix */
       strsqueezep(pruned,delim+3); /* squeeze out leading http:// */
       trimwhite(pruned); } /*remove leading/trailing whitespace*/
   /* --- next remove leading www. --- */
   if ( (delim=strstr(pruned,"www.")) != NULL ) /* found www. */
     if ( ((int)(delim-pruned)) == 0 ) { /* make sure it's the leading chars*/
       strsqueezep(pruned,delim+4); /* squeeze out leading www. */
       trimwhite(pruned); } /*remove leading/trailing whitespace*/
   /* --- finally remove leading / and everything following it --- */
   if ( (delim=strchr(pruned,'/')) != NULL ) /* found first / */
     *delim = '\000'; /* null-terminate url at first / */
   if ( isempty(pruned) ) goto end_of_job; /* nothing left in url */
   /* --- count dots from back of url --- */
   delim = pruned + strlen(pruned); /*ptr to '\000' terminating pruned*/
   while ( ((int)(delim-pruned)) > 0 ) { /* don't back up before first char */
     delim--; /* ptr to preceding character */
     if ( *delim != '.' ) continue; /* not a dot, so keep looking */
     ndots++; /* count another dot found */
     if ( istruncate ) { /* remove trailing .com */
       istruncate = 0; /* don't truncate any more dots */
       *delim = '\000'; /* truncate pruned url */
       ndots = 0; } /* and reset dot count */
     if ( ndots >= n ) { /* have all requested levels */
       strsqueezep(pruned,delim+1); /* squeeze out leading levels */
       break; } /* and we're done */
     } /* --- end-of-while() --- */
   purl = pruned; /*completed okay, return pruned url*/
   end_of_job:
     return ( purl ); /* back with pruned url */
   } /* --- end-of-function urlprune() --- */
   
   
   /* ==========================================================================
    * Function: urlncmp ( url1, url2, n )
    * Purpose: Compares the n topmost levels of two urls
    * --------------------------------------------------------------------------
    * Arguments: url1 (I) char * to null-terminated string
    * containing url to be compared with url2
    * url2 (I) char * to null-terminated string
    * containing url to be compared with url1
    * n (I) int containing number of top levels
    * to compare, or 0 to compare them all.
    * n<0 compares that many top levels excluding
    * the last, i.e., for n=-1, xxx.com and xxx.org
    * would be considered a match
    * --------------------------------------------------------------------------
    * Returns: ( int ) 1 if url's match, or
    * 0 if not.
    * --------------------------------------------------------------------------
    * Notes:     o
    * ======================================================================= */
   /* --- entry point --- */
   int urlncmp ( char *url1, char *url2, int n )
   {
   /* -------------------------------------------------------------------------
   Allocations and Declarations
   -------------------------------------------------------------------------- */
   char *urlprune(), *prune=NULL, /* prune url's */
    prune1[4096], prune2[4096]; /* pruned copies of url1,url2 */
   int ismatch = 0; /* true if url's match */
   /* -------------------------------------------------------------------------
   prune url's and compare the pruned results
   -------------------------------------------------------------------------- */
   /* --- check input --- */
   if ( isempty(url1) /*make sure both url1,url2 supplied*/
   ||   isempty(url2) ) goto end_of_job; /* missing input, so return 0 */
   /* --- prune url's --- */
   prune = urlprune(url1,n); /* ptr to pruned version of url1 */
   if ( isempty(prune) ) goto end_of_job; /* some problem with url1 */
   strninit(prune1,prune,4064); /* local copy of pruned url1 */
   prune = urlprune(url2,n); /* ptr to pruned version of url2 */
   if ( isempty(prune) ) goto end_of_job; /* some problem with url2 */
   strninit(prune2,prune,4064); /* local copy of pruned url2 */
   /* --- compare pruned url's --- */
   if ( strcmp(prune1,prune2) == 0 ) /* pruned url's are identical */
     ismatch = 1; /* signal match to caller */
   end_of_job:
     return ( ismatch ); /*back with #matching url components*/
   } /* --- end-of-function urlncmp() --- */
   
   
   /* ==========================================================================
  * Function: dbltoa ( dblval, npts )   * Function: dbltoa ( dblval, npts )
  * Purpose: Converts double to ascii, in financial format   * Purpose: Converts double to ascii, in financial format
  * (e.g., comma-separated and negatives enclosed in ()'s).   * (e.g., comma-separated and negatives enclosed in ()'s).
Line 12399  Initialization Line 14294  Initialization
 /* --- check input --- */  /* --- check input --- */
 if ( irow<0 || irow>=height /* irow out-of-bounds */  if ( irow<0 || irow>=height /* irow out-of-bounds */
 ||   icol<0 || icol>=width ) goto end_of_job; /* icol out-of-bounds */  ||   icol<0 || icol>=width ) goto end_of_job; /* icol out-of-bounds */
   /* --- adjust maxturn for magstep --- */
   if ( 1 ) /* guard */
     if ( magstep > 1 && magstep <= 10 ) /* sanity check */
       maxturn *= magstep; /* factor in magnification */
 /* --- starting bit -- see if we're following a fg (usual), or bg line --- */  /* --- starting bit -- see if we're following a fg (usual), or bg line --- */
 bitval = getlongbit(bitmap,(icol+irow*width)); /* starting pixel (bg or fg)*/  bitval = getlongbit(bitmap,(icol+irow*width)); /* starting pixel (bg or fg)*/
 fgval = bitval;  bgval = (1-bitval); /* define "fg" as whatever bitval is*/  fgval = bitval;  bgval = (1-bitval); /* define "fg" as whatever bitval is*/
Line 13063  end_of_job: Line 14962  end_of_job:
  * Function: aacolormap ( bytemap, nbytes, colors, colormap )   * Function: aacolormap ( bytemap, nbytes, colors, colormap )
  * Purpose: searches bytemap, returning a list of its discrete values   * Purpose: searches bytemap, returning a list of its discrete values
  * in ascending order in colors[], and returning an "image"   * in ascending order in colors[], and returning an "image"
  * of bytemap (where vales are replaced by colors[]   * of bytemap (where values are replaced by colors[]
  * indexes) in colormap[].   * indexes) in colormap[].
  * --------------------------------------------------------------------------   * --------------------------------------------------------------------------
  * Arguments: bytemap (I) intbyte *  to bytemap containing   * Arguments: bytemap (I) intbyte *  to bytemap containing
Line 13442  globals for gif and png callback functio Line 15341  globals for gif and png callback functio
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 GLOBAL(raster,*bitmap_raster,NULL); /* use 0/1 bitmap image or */  GLOBAL(raster,*bitmap_raster,NULL); /* use 0/1 bitmap image or */
 GLOBAL(intbyte,*colormap_raster,NULL); /* anti-aliased color indexes */  GLOBAL(intbyte,*colormap_raster,NULL); /* anti-aliased color indexes */
   GLOBAL(int,raster_width,0); /* width of final/displayed image */
   GLOBAL(int,raster_height,0); /* height of final/displayed image */
   GLOBAL(int,raster_baseline,0); /* baseline of final/displayed image*/
 /* --- anti-aliasing flags (needed by GetPixel() as well as main()) --- */  /* --- anti-aliasing flags (needed by GetPixel() as well as main()) --- */
 #ifdef AA /* if anti-aliasing requested */  #ifdef AA /* if anti-aliasing requested */
   #define ISAAVALUE 1 /* turn flag on */    #define ISAAVALUE 1 /* turn flag on */
Line 13481  STATIC logdata mimelog[] Line 15383  STATIC logdata mimelog[]
 #endif  #endif
   ;    ;
   
 /* -------------------------------------------------------------------------  
 messages  
 -------------------------------------------------------------------------- */  
 static char *copyright = /* copyright, gnu/gpl notice */  
  "+-----------------------------------------------------------------------+\n"  
  "|mimeTeX vers 1.70, Copyright(c) 2002-2008, John Forkosh Associates, Inc|\n"  
  "+-----------------------------------------------------------------------+\n"  
  "| mimeTeX is free software, licensed to you under terms of the GNU/GPL, |\n"  
  "|           and comes with absolutely no warranty whatsoever.           |\n"  
  "+-----------------------------------------------------------------------+";  
 static int maxmsgnum = 2; /* maximum msgtable[] index */  
 static char *msgtable[] = { /* messages referenced by [index] */  
  "\\red\\small\\rm\\fbox{\\array{" /* [0] is invalid_referer_msg */  
    "Please~read~www.forkosh.com/mimetex.html\\\\and~install~mimetex.cgi~"  
    "on~your~own~server.\\\\Thank~you,~John~Forkosh}}",  
  "\\red\\small\\rm\\fbox{\\array{" /* [1] */  
    "Please~provide~your~{\\tiny~HTTP-REFERER}~to~access~the~public\\\\"  
    "mimetex~server.~~Or~please~read~~www.forkosh.com/mimetex.html\\\\"  
    "and~install~mimetex.cgi~on~your~own~server.~~Thank~you,~John~Forkosh}}",  
  "\\red\\small\\rm\\fbox{\\array{" /* [2] */  
    "The~public~mimetex~server~is~for~testing.~~For~production,\\\\"  
    "please~read~~www.forkosh.com/mimetex.html~~and~install\\\\"  
    "mimetex.cgi~on~your~own~server.~~Thank~you,~John~Forkosh}}",  
  NULL } ; /* trailer */  
   
   
 /* --- entry point --- */  /* --- entry point --- */
 int main ( int argc, char *argv[]  int main ( int argc, char *argv[]
Line 13541  int type_raster(), type_bytemap(), /* sc Line 15418  int type_raster(), type_bytemap(), /* sc
  xbitmap_raster(); /* mime xbitmap output function */   xbitmap_raster(); /* mime xbitmap output function */
 /* --- http_referer --- */  /* --- http_referer --- */
 char *referer = REFERER; /* http_referer must contain this */  char *referer = REFERER; /* http_referer must contain this */
   char *inputreferer = INPUTREFERER; /*http_referer's permitted to \input*/
   int reflevels = REFLEVELS, urlncmp(); /* cmp http_referer,server_name */
   int strreplace(); /* replace SERVER_NAME in errmsg */
   char *urlprune(); /* prune referer_match */
 struct { char *referer; int msgnum; } /* http_referer can't contain this */  struct { char *referer; int msgnum; } /* http_referer can't contain this */
  denyreferer[] = { /* referer table to deny access to */   denyreferer[] = { /* referer table to deny access to */
  #ifdef DENYREFERER   #ifdef DENYREFERER
   #include DENYREFERER /* e.g.,  {"",1},  for no referer */    #include DENYREFERER /* e.g.,  {"",1},  for no referer */
  #endif   #endif
  { NULL, -999 } }; /* trailer */   { NULL, -999 } }; /* trailer */
 char *http_referer = getenv("HTTP_REFERER"); /* referer using mimeTeX */  char *http_referer = getenv("HTTP_REFERER"), /* referer using mimeTeX */
 int ishttpreferer = (http_referer==NULL?0:(*http_referer=='\000'?0:1));   *http_host    = getenv("HTTP_HOST"), /* http host for mimeTeX */
    *server_name  = getenv("SERVER_NAME"), /* server hosting mimeTeX */
    *referer_match = (!isempty(http_host)?http_host: /*match http_host*/
     (!isempty(server_name)?server_name:(NULL))); /* or server_name */
   int ishttpreferer = (isempty(http_referer)?0:1);
 int isstrstr(); /* search http_referer for referer */  int isstrstr(); /* search http_referer for referer */
 int isinvalidreferer = 0; /* true for inavlid referer */  int isinvalidreferer = 0; /* true for inavlid referer */
 int norefmaxlen = NOREFMAXLEN; /*max query_string len if no referer*/  int norefmaxlen = NOREFMAXLEN; /*max query_string len if no referer*/
Line 13576  int aalowpass(), aapnm(),  /*lowpass fil Line 15461  int aalowpass(), aapnm(),  /*lowpass fil
 int ncolors=2, /* #colors (2=b&w) */  int ncolors=2, /* #colors (2=b&w) */
  aacolormap(); /* build colormap from bytemap */   aacolormap(); /* build colormap from bytemap */
 int ipattern; /*patternnumcount[] index diagnostic*/  int ipattern; /*patternnumcount[] index diagnostic*/
   /* --- advertisement preprocessing --- */
   int advertisement(), crc16(); /*wrap expression in advertisement*/
   char *adtemplate = NULL; /* usually use default message */
   char *host_showad = HOST_SHOWAD; /* show ads only on this host */
 /* --- messages --- */  /* --- messages --- */
 char logfile[256] = LOGFILE, /*log queries if msglevel>=LOGLEVEL*/  char logfile[256] = LOGFILE, /*log queries if msglevel>=LOGLEVEL*/
  cachelog[256] = CACHELOG; /* cached image log in cachepath/ */   cachelog[256] = CACHELOG; /* cached image log in cachepath/ */
 char *timestamp(); /* time stamp for logged messages */  char *timestamp(); /* time stamp for logged messages */
   char *strdetex(); /* remove math chars from messages */
 int logger(); /* logs environ variables */  int logger(); /* logs environ variables */
 int ismonth(); /* check argv[0] for current month */  int ismonth(); /* check argv[0] for current month */
 char *progname = (argc>0?argv[0]:"noname"); /* name program executed as */  char *progname = (argc>0?argv[0]:"noname"); /* name program executed as */
 char *dashes = /* separates logfile entries */  char *dashes = /* separates logfile entries */
  "--------------------------------------------------------------------------";   "--------------------------------------------------------------------------";
 char *invalid_referer_msg = msgtable[0]; /* msg to invalid http_referer */  char *invalid_referer_msg = msgtable[invmsgnum]; /*msg to invalid referer*/
   char *invalid_referer_match = msgtable[refmsgnum]; /*referer isn't host*/
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 initialization  initialization
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
Line 13594  initialization Line 15485  initialization
   system(SYSTEM);    system(SYSTEM);
 #endif  #endif
 /* --- set global variables --- */  /* --- set global variables --- */
   daemonlevel++; /* signal other funcs to reset */
 msgfp = stdout; /* for comamnd-line mode output */  msgfp = stdout; /* for comamnd-line mode output */
 isss = issupersampling; /* set supersampling flag */  isss = issupersampling; /* set supersampling flag */
 isemitcontenttype = 1; /* true to emit mime content-type */  isemitcontenttype = 1; /* true to emit mime content-type */
 iscachecontenttype = 0; /* true to cache mime content-type */  iscachecontenttype = 0; /* true to cache mime content-type */
 *contenttype = '\000'; /* reset content-type:, etc. cache */  *contenttype = '\000'; /* reset content-type:, etc. cache */
   isnomath = 0; /* true to inhibit math mode */
   seclevel = SECURITY; /* overall security level */
   inputseclevel = INPUTSECURITY; /* security level for \input{} */
   counterseclevel = COUNTERSECURITY; /* security level for \counter{} */
   environseclevel = ENVIRONSECURITY; /* security level for \environ */
   ninputcmds = 0; /* reset count of \input commands */
   exitstatus=0; errorstatus=ERRORSTATUS; /* reset exit/error status */
 iscaching = ISCACHING; /* true if caching images */  iscaching = ISCACHING; /* true if caching images */
 if ( iscaching ) { /* images are being cached */  if ( iscaching ) { /* images are being cached */
   strcpy(cachepath,CACHEPATH); /* relative path to cached files */    strcpy(cachepath,CACHEPATH); /* relative path to cached files */
   if ( *cachepath == '%' ) { /* leading % signals cache headers */    if ( *cachepath == '%' ) { /* leading % signals cache headers */
     iscachecontenttype = 1; /* signal caching mime content-type*/      iscachecontenttype = 1; /* signal caching mime content-type*/
     strcpy(cachepath,cachepath+1); } } /* and squeeze out leading % char */      strsqueeze(cachepath,1); } } /* and squeeze out leading % char */
 gifSize = 0; /* signal that image not in memory */  gifSize = 0; /* signal that image not in memory */
 fgred=FGRED; fggreen=FGGREEN; fgblue=FGBLUE; /* default foreground colors */  fgred=FGRED; fggreen=FGGREEN; fgblue=FGBLUE; /* default foreground colors */
 bgred=BGRED; bggreen=BGGREEN; bgblue=BGBLUE; /* default background colors */  bgred=BGRED; bggreen=BGGREEN; bgblue=BGBLUE; /* default background colors */
Line 13615  for ( ipattern=1; ipattern<=51; ipattern Line 15514  for ( ipattern=1; ipattern<=51; ipattern
  * check QUERY_STRING query for expression overriding command-line arg   * check QUERY_STRING query for expression overriding command-line arg
  * ------------------------------------------------------------------- */   * ------------------------------------------------------------------- */
 if ( query != NULL ) /* check query string from environ */  if ( query != NULL ) /* check query string from environ */
   if ( strlen(query) >= 1 ) /* caller gave us a query string */    if ( strlen(query) >= 1 ) { /* caller gave us a query string */
     { strncpy(expression,query,MAXEXPRSZ); /* so use it as expression */      strncpy(expression,query,MAXEXPRSZ); /* so use it as expression */
       expression[MAXEXPRSZ] = '\000'; /* make sure it's null terminated */      expression[MAXEXPRSZ] = '\000'; /* make sure it's null terminated */
       if ( 0 ) /*true to remove leading whitespace*/      if ( 0 ) /*true to remove leading whitespace*/
         while ( isspace(*expression) && *expression!='\000' )        while ( isspace(*expression) && *expression!='\000' )
           strcpy(expression,expression+1); /* squeeze out white space */          {strsqueeze(expression,1);} /* squeeze out white space */
       isquery = 1; } /* and set isquery flag */      isquery = 1; } /* and set isquery flag */
 if ( !isquery ) /* empty query string */  if ( !isquery ) { /* empty query string */
   { char *host = getenv("HTTP_HOST"), /* additional getenv("") results */    char *host = getenv("HTTP_HOST"), /* additional getenv("") results */
     *name = getenv("SERVER_NAME"), *addr = getenv("SERVER_ADDR");    *name = getenv("SERVER_NAME"), *addr = getenv("SERVER_ADDR");
     if ( host!=NULL || name!=NULL || addr!=NULL ) /* assume http query */    if ( host!=NULL || name!=NULL || addr!=NULL ) { /* assume http query */
       { isquery = 1; /* set flag to signal query */      isquery = 1; /* set flag to signal query */
  strcpy(expression,"\\red\\small\\fbox{\\rm~no~query~string}"); }      if ( exitstatus == 0 ) exitstatus = errorstatus; /* signal error */
     isqempty = 1; /* signal empty query string */      strcpy(expression, /* and give user an error message */
       "\\red\\small\\rm\\fbox{\\begin{gather}\\LaTeX~expression~not~supplied"
       "\\\\i.e.,~no~?query\\_string~given~to~mimetex.cgi\\end{gather}}"); }
     isqempty = 1; /* signal empty query string */
   } /* --- end-of-if(!isquery) --- */    } /* --- end-of-if(!isquery) --- */
 /* ---  /* ---
  * process command-line input args (if not a query)   * process command-line input args (if not a query)
Line 13683  if ( !isquery    /* don't have an html q Line 15585  if ( !isquery    /* don't have an html q
      if ( arglen > 1 ) ptype = atoi(field+1); /* -g2 ==> ptype=2 */       if ( arglen > 1 ) ptype = atoi(field+1); /* -g2 ==> ptype=2 */
      if ( 1 || *argv[argnum]=='-' ) argnum--; /*next arg is -switch*/       if ( 1 || *argv[argnum]=='-' ) argnum--; /*next arg is -switch*/
      else pbm_outfile = argv[argnum]; break; /*next arg is filename*/       else pbm_outfile = argv[argnum]; break; /*next arg is filename*/
  case 'm': msglevel = atoi(argv[argnum]);                      break;   case 'm': if ( argnum < argc ) msglevel = atoi(argv[argnum]); break;
  case 'o': istransparent = (istransparent?0:1);     argnum--;  break;   case 'o': istransparent = (istransparent?0:1);     argnum--;  break;
  case 'q': isqforce = 1;                            argnum--;  break;   case 'q': isqforce = 1;                            argnum--;  break;
  case 's': size = atoi(argv[argnum]);                          break;   case 's': if ( argnum < argc ) size = atoi(argv[argnum]);     break;
  } /* --- end-of-switch(flag) --- */   } /* --- end-of-switch(flag) --- */
       } /* --- end-of-if(*argv[argnum]=='-') --- */        } /* --- end-of-if(*argv[argnum]=='-') --- */
     else /* expression if arg not a -flag */      else /* expression if arg not a -flag */
Line 13760  if ( isquery ) {    /* must be <form met Line 15662  if ( isquery ) {    /* must be <form met
  if ( !memcmp(expression,"formdata",8) ) /*must be <input name="formdata"> */   if ( !memcmp(expression,"formdata",8) ) /*must be <input name="formdata"> */
   { char *delim=strchr(expression,'='); /* find equal following formdata */    { char *delim=strchr(expression,'='); /* find equal following formdata */
     if ( delim != (char *)NULL ) /* found unescaped equal sign */      if ( delim != (char *)NULL ) /* found unescaped equal sign */
       strcpy(expression,delim+1); /* so shift name= out of expression*/        {strsqueezep(expression,delim+1);} /* so shift name= out */
     while ( (delim=strchr(expression,'+')) != NULL ) /*unescaped plus sign*/      while ( (delim=strchr(expression,'+')) != NULL ) /*unescaped plus sign*/
       *delim = ' '; /* is "shorthand" for blank space */        *delim = ' '; /* is "shorthand" for blank space */
     /*unescape_url(expression,1);*/ /* convert unescaped %xx's to chars */      /*unescape_url(expression,1);*/ /* convert unescaped %xx's to chars */
Line 13771  if ( isquery ) {    /* must be <form met Line 15673  if ( isquery ) {    /* must be <form met
  else /* --- query, but not <form> input --- */   else /* --- query, but not <form> input --- */
     unescape_url(expression,0); } /* convert _all_ %xx's to chars */      unescape_url(expression,0); } /* convert _all_ %xx's to chars */
 /* ---  /* ---
    * check queries for prefixes/suffixes/embedded that might cause problems
    * ---------------------------------------------------------------------- */
   /* --- expression whose last char is \ --- */
   if ( lastchar(expression) == '\\' ) /* last char is backslash */
     strcat(expression," "); /* assume "\ " lost the final space*/
   /* ---
  * check queries for embedded prefixes signalling special processing   * check queries for embedded prefixes signalling special processing
  * ----------------------------------------------------------------- */   * ----------------------------------------------------------------- */
 if ( isquery ) /* only check queries */  if ( isquery ) /* only check queries */
Line 13782  if ( isquery )    /* only check queries Line 15690  if ( isquery )    /* only check queries
       { *delim = '\000'; /* replace delim with null */        { *delim = '\000'; /* replace delim with null */
  if ( seclevel <= 9 ) /* permit msglevel specification */   if ( seclevel <= 9 ) /* permit msglevel specification */
   msglevel = atoi(expression+9); /* interpret ### in msglevel###$ */    msglevel = atoi(expression+9); /* interpret ### in msglevel###$ */
  strcpy(expression,delim+1); } } /* shift out prefix and delim */   strsqueezep(expression,delim+1); } } /* squeeze out prefix & delim */
  /* --- next check for logfile=xxx$ prefix (must follow msglevel) --- */   /* --- next check for logfile=xxx$ prefix (must follow msglevel) --- */
  if ( !memcmp(expression,"logfile=",8) ) /* query has logfile= prefix */   if ( !memcmp(expression,"logfile=",8) ) /* query has logfile= prefix */
    { char *delim=strchr(expression,'$'); /* find $ delim following logfile=*/     { char *delim=strchr(expression,'$'); /* find $ delim following logfile=*/
Line 13790  if ( isquery )    /* only check queries Line 15698  if ( isquery )    /* only check queries
       { *delim = '\000'; /* replace delim with null */        { *delim = '\000'; /* replace delim with null */
  if ( seclevel <= 3 ) /* permit logfile specification */   if ( seclevel <= 3 ) /* permit logfile specification */
   strcpy(logfile,expression+8); /* interpret xxx in logfile=xxx$ */    strcpy(logfile,expression+8); /* interpret xxx in logfile=xxx$ */
  strcpy(expression,delim+1); } } /* shift out prefix and delim */   strsqueezep(expression,delim+1); } } /* squeeze out prefix & delim */
  } /* --- end-of-if(isquery) --- */   } /* --- end-of-if(isquery) --- */
 /* ---  /* ---
  * log query (e.g., for debugging)   * log query (e.g., for debugging)
Line 13857  if ( isquery )    /* only log query_stri Line 15765  if ( isquery )    /* only log query_stri
 if ( 1 || isquery ) /* queries or command-line */  if ( 1 || isquery ) /* queries or command-line */
  if ( *exprprefix != '\000' ) /* we have a prefix string */   if ( *exprprefix != '\000' ) /* we have a prefix string */
   { int npref = strlen(exprprefix); /* #chars in prefix */    { int npref = strlen(exprprefix); /* #chars in prefix */
     memmove(expression+npref+1,expression,strlen(expression)+1); /*make room*/      memmove(expression+npref+1,expression,strlen(expression)+1);/*make room*/
     memcpy(expression,exprprefix,npref); /* copy prefix into expression */      memcpy(expression,exprprefix,npref); /* copy prefix into expression */
     expression[npref] = '{'; /* followed by { */      expression[npref] = '{'; /* followed by { */
     strcat(expression,"}"); } /* and terminating } to balance { */      strcat(expression,"}"); } /* and terminating } to balance { */
 /* ---  /* ---
  * check if http_referer is allowed to use this image   * check if http_referer is allowed to use this image and to use \input{}
  * -------------------------------------------------- */   * ---------------------------------------------------------------------- */
 if ( isquery ) /* not relevant if "interactive" */  if ( isquery ) { /* not relevant if "interactive" */
  if ( referer != NULL ) /* nor if compiled w/o -DREFERER= */   /* --- check -DREFERER=\"comma,separated,list\" of valid referers --- */
   if ( strcmp(referer,"month") != 0 ) /* nor if it's *only* "month" */   if ( referer != NULL ) { /* compiled with -DREFERER=\"...\" */
    if ( http_referer != NULL ) /* nor if called "standalone" */    if ( strcmp(referer,"month") != 0 ) /* but it's *only* "month" signal */
     if ( !isstrstr(http_referer,referer,0) ) /* invalid http_referer */     if ( ishttpreferer ) /* or called "standalone" */
      { expression = invalid_referer_msg; /* so give user error message */      if ( !isstrstr(http_referer,referer,0) ) { /* invalid http_referer */
        isinvalidreferer = 1; } /* and signal invalid referer */        expression = invalid_referer_msg; /* so give user error message */
         isinvalidreferer = 1; } } /* and signal invalid referer */
    else /* compiled without -DREFERER= */
     if ( reflevels > 0 ) { /*match referer unless -DREFLEVELS=0*/
      /* --- check topmost levels of http_referer against http_host --- */
      if ( ishttpreferer /* have http_referer */
      &&   !isempty(referer_match) ) /* and something to match it with */
       if ( !urlncmp(http_referer,referer_match,reflevels) ) { /*match failed*/
        strcpy(exprbuffer,invalid_referer_match); /* init error message */
        strreplace(exprbuffer,"SERVER_NAME", /* and then replace SERVER_NAME */
          strdetex(urlprune(referer_match,reflevels),1),0);/*with referer_match*/
        isinvalidreferer = 1; } /* and signal invalid referer */
      } /* --- end-of-if(reflevels>0) --- */
    /* --- check -DINPUTREFERER=\"comma,separated,list\" of \input users --- */
    inputseclevel = INPUTSECURITY; /* set default input security */
    if ( inputreferer != NULL ) { /* compiled with -DINPUTREFERER= */
     if ( http_referer == NULL ) /* but no http_referer given */
      inputseclevel = (-1); /* unknown user can't \input{} */
     else /*have inputreferer and http_referer*/
      if ( !isstrstr(http_referer,inputreferer,0) ) /*http_referer can't \input*/
       inputseclevel = (-1); /* this known user can't \input{} */
     } /* --- end-of-if(inputreferer!=NULL) --- */
    } /* --- end-of-if(isquery) --- */
 /* ---  /* ---
  * check if referer contains "month" signal   * check if referer contains "month" signal
  * ---------------------------------------- */   * ---------------------------------------- */
Line 13910  if ( isquery )    /* not relevant if "in Line 15840  if ( isquery )    /* not relevant if "in
 if ( isquery ) /* not relevant if "interactive" */  if ( isquery ) /* not relevant if "interactive" */
  if ( !isinvalidreferer ) /* nor if already invalid referer */   if ( !isinvalidreferer ) /* nor if already invalid referer */
   if ( !ishttpreferer ) /* no http_referer supplied */    if ( !ishttpreferer ) /* no http_referer supplied */
    if ( strlen(expression) > norefmaxlen ) /* query_string too long */     if ( strlen(expression) > norefmaxlen ) { /* query_string too long */
     { expression = invalid_referer_msg; /* set invalid http_referer message*/      if ( isempty(referer_match) ) /* no referer_match to display */
       isinvalidreferer = 1; } /* and signal invalid referer */       expression = invalid_referer_msg; /* set invalid http_referer message*/
       else { /* error with referer_match display*/
        strcpy(exprbuffer,invalid_referer_match); /* init error message */
        strreplace(exprbuffer,"SERVER_NAME", /* and then replace SERVER_NAME */
          strdetex(urlprune(referer_match,reflevels),1),0); } /*with host_http*/
        isinvalidreferer = 1; } /* and signal invalid referer */
 /* ---  /* ---
  * check for image caching   * check for "advertisement"
  * ----------------------- */   * ------------------------- */
   /* --- check if advertisement messages only for one particular host --- */
   if ( !isempty(host_showad) ) /* messages only for this referer */
    if ( !isempty(referer_match) ) /* have HTTP_HOST or SERVER_NAME */
      if ( strstr(referer_match,host_showad) /* see if this host sees ad */
      == NULL ) /* not mimetex host for ad */
        adfrequency = 0; /* turn off advertisements */
   /* --- check for advertisement directive (\advertisement) --- */
   if ( strreplace(expression,"\\advertisement","",0) /*remove \advertisement*/
   >=   1 ) adfrequency = 1; /* force advertisement display */
   if ( adfrequency > 0 ) { /* advertising enabled */
     int npump = crc16(expression)%16; /* #times, 0-15, to pump rand() */
     srand(atoi(timestamp(TZDELTA,4))); /* init rand() with mmddhhmmss */
     while ( npump-- >= 0 ) rand(); /* pre-pump rand() before use */
     if ( (1+rand())%adfrequency == 0 ) { /* once every adfrequency calls */
       advertisement(expression,adtemplate); } } /*wrap expression in advert*/
   /* ---
    * check for image caching (and whether or not caching content type)
    * ----------------------------------------------------------------- */
 if ( strstr(expression,"\\counter")  != NULL /* can't cache \counter{} */  if ( strstr(expression,"\\counter")  != NULL /* can't cache \counter{} */
 ||   strstr(expression,"\\input")    != NULL /* can't cache \input{} */  ||   strstr(expression,"\\input")    != NULL /* can't cache \input{} */
 ||   strstr(expression,"\\today")    != NULL /* can't cache \today */  ||   strstr(expression,"\\today")    != NULL /* can't cache \today */
Line 13924  if ( strstr(expression,"\\counter")  != Line 15877  if ( strstr(expression,"\\counter")  !=
 ||   isformdata /* don't cache user form input */  ||   isformdata /* don't cache user form input */
  ) { iscaching = 0; /* so turn caching off */   ) { iscaching = 0; /* so turn caching off */
      maxage = 5; } /* and set max-age to 5 seconds */       maxage = 5; } /* and set max-age to 5 seconds */
   if ( strstr(expression,"\\depth")    != NULL ) /* cache content-type lines */
        iscachecontenttype = 1; /* set flag to cache content-type */
   if ( strstr(expression,"\\nodepth")  != NULL ) /* don't cache content-type */
        iscachecontenttype = 0; /*set flag to not cache content-type*/
 if ( isquery ) /* don't cache command-line images */  if ( isquery ) /* don't cache command-line images */
  if ( iscaching ) /* image caching enabled */   if ( iscaching ) /* image caching enabled */
   {    {
Line 13981  if ( isquery )    /* don't cache command Line 15938  if ( isquery )    /* don't cache command
  * emit copyright, gnu/gpl notice (if "interactive")   * emit copyright, gnu/gpl notice (if "interactive")
  * ------------------------------------------------- */   * ------------------------------------------------- */
 if ( !isdumpimage ) /* don't mix ascii with image dump */  if ( !isdumpimage ) /* don't mix ascii with image dump */
  if ( (!isquery||isqlogging) && msgfp!=NULL ) /* called from command line */   if ( (!isquery||isqlogging) && msgfp!=NULL ) { /* called from command line */
    fprintf(msgfp,"%s\n",copyright); /* display copyright, gnu/gpl info */     fprintf(msgfp,"%s\n%s\n",copyright1,copyright2); /* display copyright */
      fprintf(msgfp,"Most recent revision: %s\n",REVISIONDATE); /*revision date*/
      } /* --- end-of-if(!isquery...) --- */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 rasterize expression and put a border around it  rasterize expression and put a border around it
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
Line 13990  rasterize expression and put a border ar Line 15949  rasterize expression and put a border ar
 if ( expression != NULL ) { /* have expression to rasterize */  if ( expression != NULL ) { /* have expression to rasterize */
   expression = mimeprep(expression); } /* preprocess expression */    expression = mimeprep(expression); } /* preprocess expression */
 /* --- double-check that we actually have an expression to rasterize --- */  /* --- double-check that we actually have an expression to rasterize --- */
 if ( expression == NULL ) /* nothing to rasterize */  if ( expression == NULL ) { /* nothing to rasterize */
  { if ( (!isquery||isqlogging) && msgfp!=NULL ) /*emit error if not a query*/    if ( exitstatus == 0 ) exitstatus = errorstatus; /*signal error to parent*/
      fprintf(msgfp,"No expression to rasterize\n");    if ( (!isquery||isqlogging) && msgfp!=NULL ) { /*emit error if not query*/
    goto end_of_job; } /* and then quit */      if ( exitstatus != 0 ) fprintf(msgfp,"Exit code = %d,\n",exitstatus);
       fprintf(msgfp,"No LaTeX expression to rasterize\n"); }
     goto end_of_job; } /* and then quit */
 /* --- rasterize expression --- */  /* --- rasterize expression --- */
 if ( (sp = rasterize(expression,size)) == NULL ) /* failed to rasterize */  if ( (sp = rasterize(expression,size)) == NULL ) { /* failed to rasterize */
  { if ( (!isquery||isqlogging) && msgfp!=NULL ) /*emit error if not a query*/    if ( exitstatus == 0 ) exitstatus = errorstatus; /*signal error to parent*/
      fprintf(msgfp,"Failed to rasterize %s\n",expression);    if ( (!isquery||isqlogging) && msgfp!=NULL ) { /*emit error if not query*/
    if ( isquery ) sp = rasterize( /* or emit error raster if query */      if ( exitstatus != 0 ) fprintf(msgfp,"Exit code = %d,\n",exitstatus);
      "\\red\\rm~\\fbox{mimeTeX~failed~to~render\\\\your~expression}",1);      fprintf(msgfp,"Failed to rasterize %.2048s\n",expression); }
    if ( sp ==  NULL ) goto end_of_job; } /* re-check for failure */    if ( isquery ) { /* try to display failed expression*/
       char errormsg[4096]; /* buffer for failed expression */
       strcpy(errormsg, /* init error message */
       "\\red\\fbox{\\begin{gather}"
       "{\\rm~mi\\underline{meTeX~failed~to~render~your~expressi}on}\\\\[5]");
       strcat(errormsg,"{\\rm\\hspace{10}{"); /*render expression as \rm*/
       strcat(errormsg,strdetex(expression,0));/*add detexed expression to msg*/
       strcat(errormsg,"}\\hspace{10}}\\end{gather}}"); /* finish up */
       if ( (sp = rasterize(errormsg,1)) == NULL ) /*couldn't rasterize errmsg*/
         sp = rasterize( /* so rasterize generic error */
         "\\red\\rm~\\fbox{mimeTeX~failed~to~render\\\\your~expression}",1); }
     if ( sp ==  NULL ) goto end_of_job; /* re-check for err message failure*/
     magstep = 1; /* don't magstep error msgs */
     } /* --- end-of-if((sp=rasterize())==NULL) --- */
   /* --- magnify entire image here if we need >>bit<<map for pbm output --- */
   if ( !isaa || (ispbmpgm && ptype<2) ) { /*or use bytemapmag() below instead*/
    if ( magstep > 1 && magstep <= 10 ) { /* magnify entire bitmap image */
     raster *rastmag(), *magrp=NULL; /* bitmap magnify function */
     int baseline = sp->baseline; /* original image baseline */
     magrp = rastmag(sp->image,magstep); /* magnify raster image */
     if ( magrp != NULL ) { /* succeeded to magnify image */
       delete_raster(sp->image); /* free original raster image */
       sp->image = magrp; /*and replace it with magnified one*/
       /* --- adjust parameters --- */
       baseline *= magstep; /* scale baseline */
       if ( baseline > 0 ) baseline += 1; /* adjust for no descenders */
       sp->baseline = baseline; } /*reset baseline of magnified image*/
     magstep = (-1); /*done, don't also use bytemapmag()*/
     } /* --- end-of-if(magstep) --- */
    } /* --- end-of-if(1||(ispbmpgm&&ptype<2)) --- */
 /* ---no border requested, but this adjusts width to multiple of 8 bits--- */  /* ---no border requested, but this adjusts width to multiple of 8 bits--- */
 if ( issupersampling ) /* no border needed for gifs */  if ( issupersampling ) /* no border needed for gifs */
   bp = sp->image; /* so just extract pixel map */    bp = sp->image; /* so just extract pixel map */
 else /* for mime xbitmaps must have... */  else /* for mime xbitmaps must have... */
   bp = border_raster(sp->image,0,0,0,1); /* image width multiple of 8 bits */    bp = border_raster(sp->image,0,0,0,1); /* image width multiple of 8 bits */
 sp->image = bitmap_raster = bp; /* global copy for gif,png output */  sp->image = bitmap_raster = bp; /* global copy for gif,png output */
   raster_width = bp->width; raster_height = bp->height; /* global copy */
   raster_baseline = sp->baseline; /* global copy (not needed) */
 if ( sp!=NULL && bp!=NULL ) { /* have raster */  if ( sp!=NULL && bp!=NULL ) { /* have raster */
   valign = sp->baseline - (bp->height - 1); /* #pixels for Vertical-Align: */    valign= raster_baseline -(raster_height -1);/*#pixels for Vertical-Align:*/
   if ( abs(valign) > 255 ) valign = (-9999); } /* sanity check */    if ( abs(valign) > 255 ) valign = (-9999); } /* sanity check */
 if ( ispbmpgm && ptype<2 ) /* -g switch or -g1 switch */  if ( ispbmpgm && ptype<2 ) /* -g switch or -g1 switch */
   type_pbmpgm(bp,ptype,pbm_outfile); /* emit b/w pbm file */    type_pbmpgm(bp,ptype,pbm_outfile); /* emit b/w pbm file */
Line 14020  if ( isaa )    /* we want anti-aliased b Line 16012  if ( isaa )    /* we want anti-aliased b
   /* ---    /* ---
    * allocate bytemap and colormap as per width*height of bitmap     * allocate bytemap and colormap as per width*height of bitmap
    * ----------------------------------------------------------- */     * ----------------------------------------------------------- */
   int nbytes = (bp->width)*(bp->height); /*#bytes needed in byte,colormap*/    int nbytes = (raster_width)*(raster_height); /*#bytes for byte,colormap*/
   if ( isss ) /* anti-aliasing by supersampling */    if ( isss ) /* anti-aliasing by supersampling */
     bytemap_raster = (intbyte *)(bitmap_raster->pixmap); /*bytemap in raster*/      bytemap_raster = (intbyte *)(bitmap_raster->pixmap); /*bytemap in raster*/
   else /* need to allocate bytemap */    else /* need to allocate bytemap */
Line 14029  if ( isaa )    /* we want anti-aliased b Line 16021  if ( isaa )    /* we want anti-aliased b
     else /* anti-aliasing wanted */      else /* anti-aliasing wanted */
       if ( (bytemap_raster = (intbyte *)malloc(nbytes)) /* malloc bytemap */        if ( (bytemap_raster = (intbyte *)malloc(nbytes)) /* malloc bytemap */
       ==   NULL ) isaa = 0; /* reset flag if malloc failed */        ==   NULL ) isaa = 0; /* reset flag if malloc failed */
   if ( isaa ) /* have bytemap, so... */  
     if ( (colormap_raster = (intbyte *)malloc(nbytes)) /* malloc colormap */  
     ==   NULL ) isaa = 0; /* reset flag if malloc failed */  
   /* ---    /* ---
    * now generate anti-aliased bytemap and colormap from bitmap     * now generate anti-aliased bytemap and colormap from bitmap
    * ---------------------------------------------------------- */     * ---------------------------------------------------------- */
Line 14088  if ( isaa )    /* we want anti-aliased b Line 16077  if ( isaa )    /* we want anti-aliased b
        "all patterns: %7d          +%7d          =%7d  total pixels\n",         "all patterns: %7d          +%7d          =%7d  total pixels\n",
        pcount0,pcount1,pcount0+pcount1); }         pcount0,pcount1,pcount0+pcount1); }
     /* ---      /* ---
        * apply magstep if requested and not already done to bitmap above
        * --------------------------------------------------------------- */
         if ( 1 ) { /* or use rastmag() above instead */
          if ( magstep > 1 && magstep <= 10 ) { /*magnify entire bytemap image*/
           intbyte *bytemapmag(), *magmap=NULL; /* bytemap magnify function */
           magmap=bytemapmag(bytemap_raster,raster_width,raster_height,magstep);
           if ( magmap != NULL ) { /* succeeded to magnify image */
             free(bytemap_raster); /* free original bytemap image */
             bytemap_raster = magmap; /*and replace it with magnified one*/
             /* --- adjust parameters --- */
             raster_width *= magstep; raster_height *= magstep; /*scale raster*/
             nbytes *= (magstep*magstep); /* scale total image size */
             if ( abs(valign) < 255 ) { /* valign okay */
               valign *= magstep; /* scale by magstep */
               if ( abs(valign) > 512 ) valign = (-9999); } /* sanity check */
             } /* --- end-of-if(magmap!=NULL) --- */
           magstep = (-1); /*done, don't also use bytemapmag()*/
           } /* --- end-of-if(magstep) --- */
          } /* --- end-of-if(1) --- */
       /* ---
      * finally, generate colors and colormap       * finally, generate colors and colormap
      * ------------------------------------- */       * ------------------------------------- */
     if ( isaa ) { /* we have bytemap_raster */      if ( isaa ) /* have bytemap, so... */
         if ( (colormap_raster = (intbyte *)malloc(nbytes)) /*malloc colormap*/
         ==   NULL ) isaa = 0; /* reset flag if malloc failed */
       if ( isaa ) { /* we have byte/colormap_raster */
       ncolors = aacolormap(bytemap_raster,nbytes,colors,colormap_raster);        ncolors = aacolormap(bytemap_raster,nbytes,colors,colormap_raster);
       if ( ncolors < 2 ) /* failed */        if ( ncolors < 2 ) /* failed */
  { isaa = 0; /* so turn off anti-aliasing */   { isaa = 0; /* so turn off anti-aliasing */
Line 14098  if ( isaa )    /* we want anti-aliased b Line 16110  if ( isaa )    /* we want anti-aliased b
       } /* --- end-of-if(isaa) --- */        } /* --- end-of-if(isaa) --- */
      if ( isaa && ispbmpgm && ptype>1 ) { /* -g2 switch  */       if ( isaa && ispbmpgm && ptype>1 ) { /* -g2 switch  */
       raster pbm_raster; /*construct arg for write_pbmpgm()*/        raster pbm_raster; /*construct arg for write_pbmpgm()*/
       pbm_raster.width  = bp->width;  pbm_raster.height = bp->height;        pbm_raster.width  = raster_width;  pbm_raster.height = raster_height;
       pbm_raster.pixsz  = 8;  pbm_raster.pixmap = (pixbyte *)bytemap_raster;        pbm_raster.pixsz  = 8;  pbm_raster.pixmap = (pixbyte *)bytemap_raster;
       type_pbmpgm(&pbm_raster,ptype,pbm_outfile); } /*write grayscale file*/        type_pbmpgm(&pbm_raster,ptype,pbm_outfile); } /*write grayscale file*/
     } /* --- end-of-if(isaa) --- */      } /* --- end-of-if(isaa) --- */
Line 14125  if ( (!isquery||isqlogging) || msglevel Line 16137  if ( (!isquery||isqlogging) || msglevel
     if ( msgfp!=NULL && msglevel>=9 ) /* don't usually emit raw bytemap */      if ( msgfp!=NULL && msglevel>=9 ) /* don't usually emit raw bytemap */
       { fprintf(msgfp,"\nHex dump of anti-aliased bytemap, " /*emit bytemap*/        { fprintf(msgfp,"\nHex dump of anti-aliased bytemap, " /*emit bytemap*/
  "asterisks denote \"black\" bytes (value=%d)...\n",grayscale-1);   "asterisks denote \"black\" bytes (value=%d)...\n",grayscale-1);
  type_bytemap(bytemap_raster,grayscale,bp->width,bp->height,msgfp); }   type_bytemap(bytemap_raster,grayscale,
           raster_width,raster_height,msgfp); }
     /* --- colormap image --- */      /* --- colormap image --- */
     fprintf(msgfp,"\nHex dump of colormap indexes, "  /* emit colormap */      fprintf(msgfp,"\nHex dump of colormap indexes, "  /* emit colormap */
       "asterisks denote \"black\" bytes (index=%d)...\n",ncolors-1);        "asterisks denote \"black\" bytes (index=%d)...\n",ncolors-1);
     type_bytemap(colormap_raster,ncolors,bp->width,bp->height,msgfp);      type_bytemap(colormap_raster,ncolors,
       raster_width,raster_height,msgfp);
     /* --- rgb values corresponding to colormap indexes */      /* --- rgb values corresponding to colormap indexes */
     fprintf(msgfp,"\nThe %d colormap indexes denote rgb values...",ncolors);      fprintf(msgfp,"\nThe %d colormap indexes denote rgb values...",ncolors);
     for ( igray=0; igray<ncolors; igray++ ) /* show colors[] values */      for ( igray=0; igray<ncolors; igray++ ) /* show colors[] values */
Line 14182  if (  isquery    /* called from browser Line 16196  if (  isquery    /* called from browser
   /* --- initialize gifsave library and colors --- */    /* --- initialize gifsave library and colors --- */
   if ( msgfp!=NULL && msglevel>=999 )    if ( msgfp!=NULL && msglevel>=999 )
     { fprintf(msgfp,"main> calling GIF_Create(*,%d,%d,%d,8)\n",      { fprintf(msgfp,"main> calling GIF_Create(*,%d,%d,%d,8)\n",
       bp->width,bp->height,ncolors); fflush(msgfp); }        raster_width,raster_height,ncolors); fflush(msgfp); }
   while ( 1 ) /* init gifsave lib, and retry if caching fails */    while ( 1 ) /* init gifsave lib, and retry if caching fails */
     { int status = GIF_Create(gif_outfile, bp->width,bp->height, ncolors, 8);      { int status = GIF_Create(gif_outfile,
           raster_width,raster_height, ncolors, 8);
       if ( status == 0 ) break; /* continue if succeeded */        if ( status == 0 ) break; /* continue if succeeded */
       if ( iscaching == 0 ) goto end_of_job; /* quit if failed */        if ( iscaching == 0 ) goto end_of_job; /* quit if failed */
       iscaching = 0; /* retry without cache file */        iscaching = 0; /* retry without cache file */
Line 14269  end_of_job: Line 16284  end_of_job:
   #endif    #endif
   /* --- exit() if not running as Windows DLL (see CreateGifFromEq()) --- */    /* --- exit() if not running as Windows DLL (see CreateGifFromEq()) --- */
   #if !defined(_USRDLL)    #if !defined(_USRDLL)
     exit ( 0 );      if ( errorstatus == 0 ) /*user doesn't want errors signalled*/
         exitstatus = 0; /* so reset error status */
       exit ( exitstatus );
   #endif    #endif
 } /* --- end-of-function main() --- */  } /* --- end-of-function main() --- */
   
Line 14324  return main ( argc, argv Line 16341  return main ( argc, argv
  ) ;   ) ;
 } /* --- end-of-function CreateGifFromEq() --- */  } /* --- end-of-function CreateGifFromEq() --- */
   
 /* ==========================================================================  
  * Function: isstrstr ( char *string, char *snippets, int iscase )  
  * Purpose: determine whether any substring of 'string'  
  * matches any of the comma-separated list of 'snippets',  
  * ignoring case if iscase=0.  
  * --------------------------------------------------------------------------  
  * Arguments: string (I) char * containing null-terminated  
  * string that will be searched for  
  * any one of the specified snippets  
  * snippets (I) char * containing null-terminated,  
  * comma-separated list of snippets  
  * to be searched for in string  
  * iscase (I) int containing 0 for case-insensitive  
  * comparisons, or 1 for case-sensitive  
  * --------------------------------------------------------------------------  
  * Returns: ( int ) 1 if any snippet is a substring of  
  * string, 0 if not  
  * --------------------------------------------------------------------------  
  * Notes:     o  
  * ======================================================================= */  
 /* --- entry point --- */  
 int isstrstr ( char *string, char *snippets, int iscase )  
 {  
 /* -------------------------------------------------------------------------  
 Allocations and Declarations  
 -------------------------------------------------------------------------- */  
 int status = 0; /*1 if any snippet found in string*/  
 char snip[99], *snipptr = snippets, /* munge through each snippet */  
  delim = ',', *delimptr = NULL; /* separated by delim's */  
 char stringcp[999], *cp = stringcp; /*maybe lowercased copy of string*/  
 /* -------------------------------------------------------------------------  
 initialization  
 -------------------------------------------------------------------------- */  
 /* --- arg check --- */  
 if ( string==NULL || snippets==NULL ) goto end_of_job; /* missing arg */  
 if ( *string=='\000' || *snippets=='\000' ) goto end_of_job; /* empty arg */  
 /* --- copy string and lowercase it if case-insensitive --- */  
 strcpy(stringcp,string); /* local copy of string */  
 if ( !iscase ) /* want case-insensitive compares */  
   for ( cp=stringcp; *cp != '\000'; cp++ ) /* so for each string char */  
     if ( isupper(*cp) ) *cp = tolower(*cp); /*lowercase any uppercase chars*/  
 /* -------------------------------------------------------------------------  
 extract each snippet and see if it's a substring of string  
 -------------------------------------------------------------------------- */  
 while ( snipptr != NULL ) /* while we still have snippets */  
   {  
   /* --- extract next snippet --- */  
   if ( (delimptr = strchr(snipptr,delim)) /* locate next comma delim */  
   ==   NULL ) /*not found following last snippet*/  
     { strcpy(snip,snipptr); /* local copy of last snippet */  
       snipptr = NULL; } /* signal end-of-string */  
   else /* snippet ends just before delim */  
     { int sniplen = (int)(delimptr-snipptr) - 1;  /* #chars in snippet */  
       memcpy(snip,snipptr,sniplen); /* local copy of snippet chars */  
       snip[sniplen] = '\000'; /* null-terminated snippet */  
       snipptr = delimptr + 1; } /* next snippet starts after delim */  
   /* --- lowercase snippet if case-insensitive --- */  
   if ( !iscase ) /* want case-insensitive compares */  
     for ( cp=snip; *cp != '\000'; cp++ ) /* so for each snippet char */  
       if ( isupper(*cp) ) *cp=tolower(*cp); /*lowercase any uppercase chars*/  
   /* --- check if snippet in string --- */  
   if ( strstr(stringcp,snip) != NULL ) /* found snippet in string */  
     { status = 1; /* so reset return status */  
       break; } /* no need to check any further */  
   } /* --- end-of-while(*snipptr!=0) --- */  
 end_of_job: return ( status ); /*1 if snippet found in list, else 0*/  
 } /* --- end-of-function isstrstr() --- */  
   
 /* ==========================================================================  /* ==========================================================================
  * Function: ismonth ( char *month )   * Function: ismonth ( char *month )
Line 14448  end_of_job: Line 16398  end_of_job:
   return ( isokay ); /*1 if month contains current month*/    return ( isokay ); /*1 if month contains current month*/
 } /* --- end-of-function ismonth() --- */  } /* --- end-of-function ismonth() --- */
   
 /* ==========================================================================  
  * Functions: int  unescape_url ( char *url, int isescape )  
  * char x2c ( char *what )  
  * Purpose: unescape_url replaces 3-character sequences %xx in url  
  *    with the single character represented by hex xx.  
  * x2c returns the single character represented by hex xx  
  *    passed as a 2-character sequence in what.  
  * --------------------------------------------------------------------------  
  * Arguments: url (I) char * containing null-terminated  
  * string with embedded %xx sequences  
  * to be converted.  
  * isescape (I) int containing 1 to _not_ unescape  
  * \% sequences (0 would be NCSA default)  
  * what (I) char * whose first 2 characters are  
  * interpreted as ascii representations  
  * of hex digits.  
  * --------------------------------------------------------------------------  
  * Returns: ( int ) unescape_url always returns 0.  
  * ( char ) x2c returns the single char  
  * corresponding to hex xx passed in what.  
  * --------------------------------------------------------------------------  
  * Notes:     o These two functions were taken verbatim from util.c in  
  *   ftp://ftp.ncsa.uiuc.edu/Web/httpd/Unix/ncsa_httpd/cgi/ncsa-default.tar.Z  
  *      o Not quite "verbatim" -- I added the "isescape logic" 4-Dec-03  
  * so unescape_url() can be safely applied to input which may or  
  * may not have been url-encoded.  (Note: currently, all calls  
  * to unescape_url() pass iescape=0, so it's not used.)  
  *      o Added +++'s to blank xlation on 24-Sep-06  
  *      o Added ^M,^F,etc to blank xlation 0n 01-Oct-06  
  * ======================================================================= */  
 /* --- entry point --- */  
 int unescape_url(char *url, int isescape) {  
     int x=0,y=0,prevescape=0,gotescape=0;  
     int xlateplus = (isplusblank==1?1:0); /* true to xlate plus to blank */  
     int strreplace(); /* replace + with blank, if needed */  
     char x2c();  
     static char *hex="0123456789ABCDEFabcdef";  
     /* ---  
      * xlate ctrl chars to blanks  
      * -------------------------- */  
     if ( 1 ) { /* xlate ctrl chars to blanks */  
       char *ctrlchars = "\n\t\v\b\r\f\a\015";  
       int  seglen = strspn(url,ctrlchars); /*initial segment with ctrlchars*/  
       int  urllen = strlen(url); /* total length of url string */  
       /* --- first, entirely remove ctrlchars from beginning and end --- */  
       if ( seglen > 0 ) { /*have ctrlchars at start of string*/  
  strcpy(url,url+seglen); /* squeeze out initial ctrlchars */  
  urllen -= seglen; } /* string is now shorter */  
       while ( --urllen >= 0 ) /* now remove ctrlchars from end */  
  if ( isthischar(url[urllen],ctrlchars) ) /* ctrlchar at end */  
   url[urllen] = '\000'; /* re-terminate string before it */  
  else break; /* or we're done */  
       urllen++; /* length of url string */  
       /* --- now, replace interior ctrlchars with ~ blanks --- */  
       while ( (seglen=strcspn(url,ctrlchars)) < urllen ) /*found a ctrlchar*/  
  url[seglen] = '~'; /* replace ctrlchar with ~ */  
       } /* --- end-of-if(1) --- */  
     /* ---  
      * xlate +'s to blanks if requested or if deemed necessary  
      * ------------------------------------------------------- */  
     if ( isplusblank == (-1) ) { /*determine whether or not to xlate*/  
       char *searchfor[] = { " ","%20", "%2B","%2b", "+++","++",  
  "+=+","+-+", NULL };  
       int  isearch = 0, /* searchfor[] index */  
    nfound[11] = {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}; /*#occurrences*/  
       /* --- locate occurrences of searchfor[] strings in url --- */  
       for ( isearch=0; searchfor[isearch] != NULL; isearch++ ) {  
  char *psearch = url; /* start search at beginning */  
  nfound[isearch] = 0; /* init #occurrences count */  
  while ( (psearch=strstr(psearch,searchfor[isearch])) != NULL ) {  
   nfound[isearch] += 1; /* count another occurrence */  
   psearch += strlen(searchfor[isearch]); } /*resume search after it*/  
  } /* --- end-of-for(isearch) --- */  
       /* --- apply some common-sense logic --- */  
       if ( nfound[0] + nfound[1] > 0 ) /* we have actual " "s or "%20"s */  
  isplusblank = xlateplus = 0; /* so +++'s aren't blanks */  
       if ( nfound[2] + nfound[3] > 0 ) { /* we have "%2B" for +++'s */  
         if ( isplusblank != 0 ) /* and haven't disabled xlation */  
   isplusblank = xlateplus = 1; /* so +++'s are blanks */  
  else /* we have _both_ "%20" and "%2b" */  
   xlateplus = 0; } /* tough call */  
       if ( nfound[4] + nfound[5] > 0 /* we have multiple ++'s */  
       ||   nfound[6] + nfound[7] > 0 ) /* or we have a +=+ or +-+ */  
  if ( isplusblank != 0 ) /* and haven't disabled xlation */  
   xlateplus = 1; /* so xlate +++'s to blanks */  
       } /* --- end-of-if(isplusblank==-1) --- */  
     if ( xlateplus > 0 ) { /* want +'s xlated to blanks */  
       char *xlateto[] = { ""," "," "," + "," "," "," "," "," " };  
       while ( xlateplus > 0 ) { /* still have +++'s to xlate */  
  char plusses[99] = "++++++++++++++++++++"; /* longest +++ string */  
  plusses[xlateplus] = '\000'; /* null-terminate +++'s */  
  strreplace(url,plusses,xlateto[xlateplus],0); /* xlate +++'s */  
  xlateplus--; /* next shorter +++ string */  
  } /* --- end-of-while(xlateplus>0) --- */  
       } /* --- end-of-if(xlateplus) --- */  
     isplusblank = 0; /* don't iterate this xlation */  
     /* ---  
      * xlate %nn to corresponding char  
      * ------------------------------- */  
     for(;url[y];++x,++y) {  
  gotescape = prevescape;  
  prevescape = (url[x]=='\\');  
  if((url[x] = url[y]) == '%')  
  if(!isescape || !gotescape)  
   if(isthischar(url[y+1],hex)  
   && isthischar(url[y+2],hex))  
     { url[x] = x2c(&url[y+1]);  
       y+=2; }  
     }  
     url[x] = '\0';  
     return 0;  
 } /* --- end-of-function unescape_url() --- */  
 /* --- entry point --- */  
 char x2c(char *what) {  
     char digit;  
     digit = (what[0] >= 'A' ? ((what[0] & 0xdf) - 'A')+10 : (what[0] - '0'));  
     digit *= 16;  
     digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A')+10 : (what[1] - '0'));  
     return(digit);  
 } /* --- end-of-function x2c() --- */  
   
 /* ==========================================================================  /* ==========================================================================
  * Function: logger ( fp, msglevel, message, logvars )   * Function: logger ( fp, msglevel, message, logvars )
Line 14613  if ( logvars != (logdata *)NULL ) /* hav Line 16443  if ( logvars != (logdata *)NULL ) /* hav
 return ( nlogged ); /* back to caller */  return ( nlogged ); /* back to caller */
 } /* --- end-of-function logger() --- */  } /* --- end-of-function logger() --- */
   
   
 /* ==========================================================================  /* ==========================================================================
  * Function: emitcache ( cachefile, maxage, valign, isbuffer )   * Function: emitcache ( cachefile, maxage, valign, isbuffer )
  * Purpose: dumps bytes from cachefile to stdout   * Purpose: dumps bytes from cachefile to stdout
Line 14693  end_of_job: Line 16524  end_of_job:
   return ( nbytes ); /* back with #bytes emitted */    return ( nbytes ); /* back with #bytes emitted */
 } /* --- end-of-function emitcache() --- */  } /* --- end-of-function emitcache() --- */
   
   
 /* ==========================================================================  /* ==========================================================================
  * Function: readcachefile ( cachefile, buffer )   * Function: readcachefile ( cachefile, buffer )
  * Purpose: read cachefile into buffer   * Purpose: read cachefile into buffer
Line 14748  end_of_job: Line 16580  end_of_job:
   return ( nbytes ); /* back with #bytes emitted */    return ( nbytes ); /* back with #bytes emitted */
 } /* --- end-of-function readcachefile() --- */  } /* --- end-of-function readcachefile() --- */
   
   
   /* ==========================================================================
    * Function: advertisement ( expression, message )
    * Purpose: wrap expression in advertisement message
    * --------------------------------------------------------------------------
    * Arguments: expression (I/O) pointer to null-terminated char string
    * containing expression to be "wrapped",
    * and returning wrapped expression
    * message (I) pointer to null-terminated char string
    * containing template for advertisement
    * message, or NULL to use default message
    * --------------------------------------------------------------------------
    * Returns: ( int ) 1 if successful, 0=error
    * --------------------------------------------------------------------------
    * Notes:     o
    * ======================================================================= */
   /* --- entry point --- */
   int advertisement ( char *expression, char *message )
   {
   /* -------------------------------------------------------------------------
   Allocations and Declarations
   -------------------------------------------------------------------------- */
   /* --- advertisement template --- */
   char  *adtemplate =
    #if defined(ADVERTISEMENT) /* cc -DADVERTISEMENT=\"filename\" */
     #include ADVERTISEMENT /* filename with advertisement */
    #else /* formatted as illustrated below */
    "\\begin{gather} {\\small\\text \\fbox{\\begin{gather}"
    "mime\\TeX rendering courtesy of\\\\"
    "\\homepagetext \\end{gather}}}\\\\"
    " %%beginmath%% %%expression%% %%endmath%% \\end{gather}"
    #endif
    ; /* terminating semicolon */
   /* --- other variables --- */
   char adbuffer[MAXEXPRSZ+2048]; /*construct wrapped expression here*/
   char *beginmath = " ", /* start math mode */
    *endmath =   " "; /* end math mode */
   int strreplace(); /* replace %%keywords%% with values*/
   /* -------------------------------------------------------------------------
   wrap expression in advertisement
   -------------------------------------------------------------------------- */
   /* --- start with template --- */
   if ( isempty(message) ) /* caller didn't supply message */
     message = adtemplate; /* so use default message */
   strcpy(adbuffer,message); /* copy message template to buffer */
   /* --- replace %%beginmath%%...%%endmath%% --- */
     strreplace(adbuffer,"%%beginmath%%",beginmath,0);
     strreplace(adbuffer,"%%endmath%%",endmath,0);
   /* --- replace %%expression%% in template with expression --- */
     strreplace(adbuffer,"%%expression%%",expression,0);
   /* --- replace original expression --- */
   strcpy(expression,adbuffer); /* expression now wrapped in ad */
   return ( 1 ); /* always just return 1 */
   } /* --- end-of-function advertisement() --- */
   
   
   /* ==========================================================================
    * Function: crc16 ( s )
    * Purpose: 16-bit crc of string s
    * --------------------------------------------------------------------------
    * Arguments: s (I) pointer to null-terminated char string
    * whose crc is desired
    * --------------------------------------------------------------------------
    * Returns: ( int ) 16-bit crc of s
    * --------------------------------------------------------------------------
    * Notes:     o From Numerical Recipes in C, 2nd ed, page 900.
    * ======================================================================= */
   /* --- entry point --- */
   int crc16 ( char *s )
   {
   /* -------------------------------------------------------------------------
   Compute the crc
   -------------------------------------------------------------------------- */
   unsigned short crc = 0; /* returned crc */
   int ibit; /* for(ibit) eight one-bit shifts */
   while ( !isempty(s) ) { /* while there are still more chars*/
     crc = (crc ^ (*s)<<8); /* add next char */
     for ( ibit=0; ibit<8; ibit++ ) /* generator polynomial */
       if ( crc & 0x8000 ) { crc<<=1; crc=crc^4129; }
       else crc <<= 1;
     s++; /* next xhar */
     } /* --- end-of-while(!isempty(s)) --- */
   return ( (int)crc ); /* back to caller with crc */
   } /* --- end-of-function crc16() --- */
   
   
 /* ==========================================================================  /* ==========================================================================
  * Function: md5str ( instr )   * Function: md5str ( instr )
  * Purpose: returns null-terminated character string containing   * Purpose: returns null-terminated character string containing
Line 14974  void md5_finish( md5_context *ctx, uint8 Line 16892  void md5_finish( md5_context *ctx, uint8
     PUT_UINT32( ctx->state[3], digest, 12 ); }      PUT_UINT32( ctx->state[3], digest, 12 ); }
 /* --- end-of-function md5str() and "friends" --- */  /* --- end-of-function md5str() and "friends" --- */
   
   
 #if defined(GIF)  #if defined(GIF)
 /* ==========================================================================  /* ==========================================================================
  * Function: GetPixel ( int x, int y )   * Function: GetPixel ( int x, int y )
Line 14992  void md5_finish( md5_context *ctx, uint8 Line 16911  void md5_finish( md5_context *ctx, uint8
 /* --- entry point --- */  /* --- entry point --- */
 int GetPixel ( int x, int y )  int GetPixel ( int x, int y )
 {  {
 int ipixel = y*bitmap_raster->width + x; /* pixel index for x,y-coords*/  int ipixel = y*raster_width + x; /* pixel index for x,y-coords*/
 int pixval =0; /* value of pixel */  int pixval =0; /* value of pixel */
 if ( !isaa ) /* use bitmap if not anti-aliased */  if ( !isaa ) /* use bitmap if not anti-aliased */
   pixval = (int)getlongbit(bitmap_raster->pixmap,ipixel); /*pixel = 0 or 1*/    pixval = (int)getlongbit(bitmap_raster->pixmap,ipixel); /*pixel = 0 or 1*/

Removed from v.1.4  
changed lines
  Added in v.1.5


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>