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

version 1.1, 2005/02/28 19:08:11 version 1.5, 2012/06/09 00:58:11
Line 1 Line 1
 /****************************************************************************  /****************************************************************************
  *   *
  * Copyright(c) 2002-2005, 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
  * --------------------------------------------------------------------------   * --------------------------------------------------------------------------
  * 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
  * and/or modify it under the terms of the GNU General Public License,   * and/or modify it under the terms of the GNU General Public License,
  * version 2 or later, as published by the Free Software Foundation.   * version 3 or later, as published by the Free Software Foundation.
  *      MimeTeX is distributed in the hope that it will be useful, but   *      MimeTeX is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY, not even the implied warranty of MERCHANTABILITY.   * WITHOUT ANY WARRANTY, not even the implied warranty of MERCHANTABILITY.
  * See the GNU General Public License for specific details.   * See the GNU General Public License for specific details.
Line 12 Line 13
  * agreed to these terms and conditions, and that you possess the legal   * agreed to these terms and conditions, and that you possess the legal
  * right and ability to enter into this agreement and to use mimeTeX   * right and ability to enter into this agreement and to use mimeTeX
  * in accordance with it.   * in accordance with it.
  *      Your mimeTeX distribution should contain a copy of the GNU General   *      Your mimetex.zip distribution file should contain the file COPYING,
  * Public License.  If not, write to the Free Software Foundation, Inc.,   * an ascii text copy of the GNU General Public License, version 3.
  * 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA,   * If not, point your browser to  http://www.gnu.org/licenses/
  * or point your browser to  http://www.gnu.org/licenses/gpl.html   * or write to the Free Software Foundation, Inc.,
    * 59 Temple Place, Suite 330,  Boston, MA 02111-1307 USA.
  * --------------------------------------------------------------------------   * --------------------------------------------------------------------------
  *   *
  * Purpose:   o MimeTeX, licensed under the gpl, lets you easily embed   * Purpose:   o MimeTeX, licensed under the gpl, lets you easily embed
Line 25 Line 27
  * entirely separate little program that doesn't use TeX or   * entirely separate little program that doesn't use TeX or
  * its fonts in any way.  It's just one cgi that you put in   * its fonts in any way.  It's just one cgi that you put in
  * your site's cgi-bin/ directory, with no other dependencies.   * your site's cgi-bin/ directory, with no other dependencies.
  * So mimeTeX is very easy to install.  And it's equally easy   *     So mimeTeX is very easy to install.  And it's equally
  * to use.  Just place an html <img> tag in your document   * easy to use.  Just place an html <img> tag in your document
  * wherever you want to see the corresponding LaTeX expression.   * wherever you want to see the corresponding LaTeX expression.
  * For example,   * For example,
  * <img src="../cgi-bin/mimetex.cgi?\int_{-\infty}^xe^{-t^2}dt"   * <img src="../cgi-bin/mimetex.cgi?\int_{-\infty}^xe^{-t^2}dt"
  *  alt="" border=0 align=middle>   *  alt="" border=0 align=middle>
  * immediately generates the corresponding gif image on-the-fly,   * immediately generates the corresponding gif image on-the-fly,
  * displaying the rendered expression wherever you put that   * displaying the rendered expression wherever you put that
  * <img> tag.  MimeTeX doesn't need intermediate dvi-to-gif   * <img> tag.
  * conversion, and it doesn't clutter up your filesystem with   *     MimeTeX doesn't need intermediate dvi-to-gif conversion,
  * separate little gif files for each converted expression.   * and it doesn't clutter up your filesystem with separate
    * little gif files for each converted expression.
    * But image caching is available by using mimeTeX's
    * -DCACHEPATH=\"path/\" compile option (see below).
  * There's also no inherent need to repeatedly write the   * There's also no inherent need to repeatedly write the
  * cumbersome <img> tag illustrated above.  You can write   * cumbersome <img> tag illustrated above.  You can write
  * your own custom tags, or write a wrapper script around   * your own custom tags, or write a wrapper script around
  * mimeTeX to simplify the necessary notation.   * mimeTeX to simplify the notation.
    *     Further discussion about mimeTeX's features and
    * usage is available on its homepage,
    *  http://www.forkosh.com/mimetex.html
    * and similarly in mimetex.html included with your mimetex.zip
    * 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 53 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)
  * 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
  * rastcat(sp1,sp2,isfree)                  concatanate sp1||sp2   * rastcat(sp1,sp2,isfree)                  concatanate sp1||sp2
  * rastack(sp1,sp2,base,space,iscenter,isfree)stack sp2 atop sp1   * rastack(sp1,sp2,base,space,iscenter,isfree)stack sp2 atop sp1
  * rastile(tiles,ntiles)      create composite raster from tiles   * rastile(tiles,ntiles)      create composite raster from tiles
  * rastsquash(sp1,sp2,xmin,ymin)    calc #squash pixels sp1||sp2   * rastsmash(sp1,sp2,xmin,ymin)      calc #smash pixels sp1||sp2
    * 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 70 Line 96
  * circle_recurse(rp,row0,col0,row1,col1,thickness,theta0,theta1)   * circle_recurse(rp,row0,col0,row1,col1,thickness,theta0,theta1)
  * bezier_raster(rp,r0,c0,r1,c1,rt,ct)   draw bezier recursively   * bezier_raster(rp,r0,c0,r1,c1,rt,ct)   draw bezier recursively
  * border_raster(rp,ntop,nbot,isline,isfree)put border around rp   * border_raster(rp,ntop,nbot,isline,isfree)put border around rp
    * backspace_raster(rp,nback,pback,minspace,isfree)    neg space
  * --- raster (and chardef) output functions ---   * --- raster (and chardef) output functions ---
  * type_raster(rp,fp)       emit ascii dump of rp on file ptr fp   * type_raster(rp,fp)       emit ascii dump of rp on file ptr fp
  * type_bytemap(bp,grayscale,width,height,fp) dump bytemap on fp   * type_bytemap(bp,grayscale,width,height,fp) dump bytemap on fp
  * xbitmap_raster(rp,fp)           emit mime xbitmap of rp on fp   * xbitmap_raster(rp,fp)           emit mime xbitmap of rp on fp
    * type_pbmpgm(rp,ptype,file)     pbm or pgm image of rp to file
  * cstruct_chardef(cp,fp,col1)         emit C struct of cp on fp   * cstruct_chardef(cp,fp,col1)         emit C struct of cp on fp
  * cstruct_raster(rp,fp,col1)          emit C struct of rp on fp   * cstruct_raster(rp,fp,col1)          emit C struct of rp on fp
  * hex_bitmap(rp,fp,col1,isstr)emit hex dump of rp->pixmap on fp   * hex_bitmap(rp,fp,col1,isstr)emit hex dump of rp->pixmap on fp
  * --- ancillary output functions ---   * --- ancillary output functions ---
  * emit_string(fp,col1,string,comment) emit string and C comment   * emit_string(fp,col1,string,comment) emit string and C comment
    * gftobitmap(rp)        convert .gf-like pixmap to bitmap image
  * ====================== Font Functions =======================   * ====================== Font Functions =======================
  * --- font lookup functions ---   * --- font lookup functions ---
  * get_symdef(symbol)             returns mathchardef for symbol   * get_symdef(symbol)              return mathchardef for symbol
  * get_chardef(symdef,size)      returns chardef for symdef,size   * get_ligature(expr,family)  return symtable index for ligature
    * get_chardef(symdef,size)       return chardef for symdef,size
  * get_charsubraster(symdef,size)  wrap subraster around chardef   * get_charsubraster(symdef,size)  wrap subraster around chardef
    * get_symsubraster(symbol,size)    returns subraster for symbol
  * --- ancillary font functions ---   * --- ancillary font functions ---
  * get_baseline(gfdata)       determine baseline (in our coords)   * get_baseline(gfdata)       determine baseline (in our coords)
  * get_delim(symbol,height,family) delim just larger than height   * get_delim(symbol,height,family) delim just larger than height
Line 91 Line 122
  * ================= Tokenize/Parse Functions ==================   * ================= Tokenize/Parse Functions ==================
  * texchar(expression,chartoken)  retruns next char or \sequence   * texchar(expression,chartoken)  retruns next char or \sequence
  * texsubexpr(expr,subexpr,maxsubsz,left,right,isescape,isdelim)   * texsubexpr(expr,subexpr,maxsubsz,left,right,isescape,isdelim)
    * texleft(expr,subexpr,maxsubsz,ldelim,rdelim)   \left...\right
  * texscripts(expression,subscript,superscript,which)get scripts   * texscripts(expression,subscript,superscript,which)get scripts
  * --- ancillary parse functions ---   * --- ancillary parse functions ---
  * isbrace(expression,braces,isescape)   check for leading brace   * isbrace(expression,braces,isescape)   check for leading brace
Line 98 Line 130
  * mimeprep(expression) preprocessor converts \left( to \(, etc.   * mimeprep(expression) preprocessor converts \left( to \(, etc.
  * 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
    * 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 110 Line 150
  * rastdispmath(expression,size,sp)      scripts for displaymath   * rastdispmath(expression,size,sp)      scripts for displaymath
  * --- table-driven handlers that rasterize... ---   * --- table-driven handlers that rasterize... ---
  * rastleft(expression,size,basesp,ildelim,arg2,arg3)\left\right   * rastleft(expression,size,basesp,ildelim,arg2,arg3)\left\right
    * rastright(expression,size,basesp,ildelim,arg2,arg3) ...\right
    * rastmiddle(expression,size,basesp,arg1,arg2,arg3)     \middle
  * rastflags(expression,size,basesp,flag,value,arg3)    set flag   * rastflags(expression,size,basesp,flag,value,arg3)    set flag
  * rastspace(expression,size,basesp,width,isfill,isheight)\,\:\;   * rastspace(expression,size,basesp,width,isfill,isheight)\,\:\;
  * rastnewline(expression,size,basesp,arg1,arg2,arg3)         \\   * rastnewline(expression,size,basesp,arg1,arg2,arg3)         \\
Line 126 Line 168
  * rastarray(expression,size,basesp,arg1,arg2,arg3)       \array   * rastarray(expression,size,basesp,arg1,arg2,arg3)       \array
  * rastpicture(expression,size,basesp,arg1,arg2,arg3)   \picture   * rastpicture(expression,size,basesp,arg1,arg2,arg3)   \picture
  * rastline(expression,size,basesp,arg1,arg2,arg3)         \line   * rastline(expression,size,basesp,arg1,arg2,arg3)         \line
    * rastrule(expression,size,basesp,arg1,arg2,arg3)         \rule
  * rastcircle(expression,size,basesp,arg1,arg2,arg3)     \circle   * rastcircle(expression,size,basesp,arg1,arg2,arg3)     \circle
  * 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
  * 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
    * 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
  * rastreadfile(filename,tag,value)  read between <tag>...</tag>   * rasteditfilename(filename)       edit filename (for security)
    * rastreadfile(filename,islock,tag,value)   read <tag>...</tag>
  * rastwritefile(filename,tag,value,isstrict)write<tag>...</tag>   * rastwritefile(filename,tag,value,isstrict)write<tag>...</tag>
  * timestamp( )   formats timestamp string (used by rastcounter)   * calendar(year,month,day)    formats one-month calendar string
  * dtoa(d,npts)                  double to comma-separated ascii   * timestamp(tzdelta,ifmt)              formats timestamp string
    * tzadjust(tzdelta,year,month,day,hour)        adjust date/time
    * 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
  * === 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
  * aapnm(rp,bytemap,grayscale)       lowpass based on pnmalias.c   * aapnm(rp,bytemap,grayscale)       lowpass based on pnmalias.c
    * aapnmlookup(rp,bytemap,grayscale)  aapnm based on aagridnum()
    * aapatterns(rp,irow,icol,gridnum,patternum,grayscale) call 19,
    * aapattern1124(rp,irow,icol,gridnum,grayscale)antialias pattrn
    * aapattern19(rp,irow,icol,gridnum,grayscale) antialias pattern
    * aapattern20(rp,irow,icol,gridnum,grayscale) antialias pattern
    * aapattern39(rp,irow,icol,gridnum,grayscale) antialias pattern
    * aafollowline(rp,irow,icol,direction)       looks for a "turn"
    * aagridnum(rp,irow,icol)             calculates gridnum, 0-511
    * aapatternnum(gridnum)    looks up pattern#, 1-51, for gridnum
    * aalookup(gridnum)     table lookup for all possible 3x3 grids
    * aalowpasslookup(rp,bytemap,grayscale)   driver for aalookup()
  * aasupsamp(rp,aa,sf,grayscale)             or by supersampling   * aasupsamp(rp,aa,sf,grayscale)             or by supersampling
  * aacolormap(bytemap,nbytes,colors,colormap)make colors,colormap   * aacolormap(bytemap,nbytes,colors,colormap)make colors,colormap
  * aaweights(width,height)      builds "canonical" weight matrix   * aaweights(width,height)      builds "canonical" weight matrix
  * aawtpixel(image,ipixel,weights,rotate) weight image at ipixel   * aawtpixel(image,ipixel,weights,rotate) weight image at ipixel
    * === miscellaneous ===
    * mimetexsetmsg(newmsglevel,newmsgfp)    set msglevel and msgfp
  * PART1 ========================== Driver ===========================   * PART1 ========================== Driver ===========================
  * main(argc,argv) parses math expression and emits mime xbitmap   * main(argc,argv) parses math expression and emits mime xbitmap
  * isstrstr(string,snippets,iscase)  are any snippets in string?   * CreateGifFromEq(expression,gifFileName)  entry pt for win dll
  * 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)  read cachefile and emit bytes to stdout   * emitcache(cachefile,maxage,valign,isbuffer)    emit cachefile
    * 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
  *   *
  * Source: mimetex.c  (needs mimetex.h and texfonts.h to compile,   * Source: mimetex.c  (needs mimetex.h and texfonts.h to compile,
  * and also needs gifsave.c if 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 173 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 and (b)specifying a transparent   * (a)sending output to stdout or returning it in memory,
  * background color index, is included with mimeTeX,   * and (b)specifying a transparent background color index,
  * and it's documented in mimetex.html#gifsave .   * is included with mimeTeX, and it's documented in
    * 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...   * in mimetex.html#options .  They include (additional -D
    * 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 187 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
  *    applies default weights   *    applies default weights
  * 1   3   1   * 1   2   1
  * 3  32   3   * 2   8   2
  * 1   3   1   * 1   2   1
  *    to neighboring pixels. The defaults weights are   *    to neighboring pixels. The defaults weights are
  *    CENTERWT=32, ADJACENTWT=3 and CORNERWT=1,   *    CENTERWT=8, ADJACENTWT=2 and CORNERWT=1,
  *    which you can adjust to control anti-aliasing.   *    which you can adjust to control anti-aliasing.
  *    Lower CENTERWT values will blur/spread out lines   *    Lower CENTERWT values will blur/spread out lines
  *    while higher values will tend to sharpen lines.   *    while higher values will tend to sharpen lines.
Line 216 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 227 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 262 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 294 Line 387
  * 02/01/04 J.Forkosh Version 1.40 released.   * 02/01/04 J.Forkosh Version 1.40 released.
  * 10/02/04 J.Forkosh Version 1.50 released.   * 10/02/04 J.Forkosh Version 1.50 released.
  * 11/30/04 J.Forkosh Version 1.60 released.   * 11/30/04 J.Forkosh Version 1.60 released.
    * 10/11/05 J.Forkosh Version 1.64 released.
    * 11/30/06 J.Forkosh Version 1.65 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 308  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(_WIN32) || defined(WIN32) \    #if defined(_WINDOWS) || defined(_WIN32) || defined(WIN32) \
   ||  defined(DJGPP) /* try to recognize windows */    ||  defined(DJGPP) /* try to recognize windows compilers */ \
     ||  defined(_USRDLL) /* must be WINDOWS if compiling for DLL */
     #define WINDOWS /* signal windows */      #define WINDOWS /* signal windows */
   #endif    #endif
 #endif  #endif
Line 328  header files and macros Line 478  header files and macros
   #if defined(_O_BINARY) || defined(O_BINARY)  /* setmode() now available */    #if defined(_O_BINARY) || defined(O_BINARY)  /* setmode() now available */
     #define HAVE_SETMODE /* so we'll use setmode() */      #define HAVE_SETMODE /* so we'll use setmode() */
   #endif    #endif
     #if defined(_MSC_VER) && defined(_DEBUG) /* MS VC++ in debug mode */
       /* to show source file and line numbers where memory leaks occur... */
       #define _CRTDBG_MAP_ALLOC /* ...include this debug macro */
       #include <crtdbg.h> /* and this debug library */
     #endif
   #define ISWINDOWS 1    #define ISWINDOWS 1
 #else  #else
   #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 348  header files and macros Line 505  header files and macros
 #else  #else
   #define ISSUPERSAMPLING 0    #define ISSUPERSAMPLING 0
   #ifndef AAALGORITHM    #ifndef AAALGORITHM
     #define AAALGORITHM 2 /* default lowpass algorithm */      #define AAALGORITHM 3 /*2*/ /* default lowpass algorithm */
   #endif    #endif
 #endif  #endif
   #ifndef MAXFOLLOW
     #define MAXFOLLOW 8 /* aafollowline() maxturn default */
   #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 376  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    #ifndef TEXFONTS
   #endif      #define NOTEXFONTS /* texfonts not required */
   /* --- max query_string length if no http_referer supplied --- */  
   #ifndef NOREFMAXLEN  
     #define NOREFMAXLEN 9999 /* default to any length query */  
   #endif    #endif
 #else  
   #define NOTEXFONTS /* texfonts not required */  
 #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
    * ---------------------------------------------------- */
   #ifdef GIF /* compiling along with gifsave.c */
     extern int gifSize;
     extern int maxgifSize;
   #else /* or just set dummy values */
     static int gifSize=0, maxgifSize=0;
   #endif
   /* --- gamma correction --- */
   #ifndef GAMMA
     #define GAMMA 1.25 /*1.75*/ /*2.2*/
   #endif
   #ifndef REVERSEGAMMA
     #define REVERSEGAMMA 0.5 /* for \reverse white-on-black */
   #endif
   /* --- opaque background (default to transparent) --- */
   #ifndef OPAQUE
     #define ISTRANSPARENT 1
   #else
     #define ISTRANSPARENT 0
   #endif
   
   /* ---
    * internal buffer sizes
    * --------------------- */
   #if !defined(MAXEXPRSZ)
     #define MAXEXPRSZ (32768-1) /*max #bytes in input tex expression*/
   #endif
   #if !defined(MAXSUBXSZ)
     #define MAXSUBXSZ (((MAXEXPRSZ+1)/2)-1)/*max #bytes in input subexpression*/
   #endif
   #if !defined(MAXTOKNSZ)
     #define MAXTOKNSZ (((MAXSUBXSZ+1)/4)-1) /* max #bytes in input token */
   #endif
   #if !defined(MAXFILESZ)
     #define MAXFILESZ (65536-1) /*max #bytes in input (output) file*/
   #endif
   #if !defined(MAXLINESZ)
     #define MAXLINESZ (4096-1) /* max #chars in line from file */
   #endif
   #if !defined(MAXGIFSZ)
     #define MAXGIFSZ 131072 /* max #bytes in output GIF image */
   #endif
   
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 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 10*/ /* anti-aliasing centerwt default */    /*#define CENTERWT 10*/ /* anti-aliasing centerwt default */
   /*#define CENTERWT 6*/ /* anti-aliasing centerwt default */    #define CENTERWT 8 /* anti-aliasing centerwt default */
   #define CENTERWT 32 /* anti-aliasing centerwt default */  
 #endif  #endif
 #ifndef ADJACENTWT  #ifndef ADJACENTWT
   /*#define ADJACENTWT 3*/ /* anti-aliasing adjacentwt default*/    /*#define ADJACENTWT 3*/ /* anti-aliasing adjacentwt default*/
   /*#define ADJACENTWT 2*/ /* anti-aliasing adjacentwt default*/    #define ADJACENTWT 2 /* anti-aliasing adjacentwt default*/
   #define ADJACENTWT 3 /* anti-aliasing adjacentwt default*/  
 #endif  #endif
 #ifndef CORNERWT  #ifndef CORNERWT
   #define CORNERWT 1 /* anti-aliasing cornerwt default*/    #define CORNERWT 1 /* anti-aliasing cornerwt default*/
Line 428  GLOBAL(int,minadjacent,MINADJACENT); /* Line 635  GLOBAL(int,minadjacent,MINADJACENT); /*
 GLOBAL(int,maxadjacent,MAXADJACENT); /* darken if<=adjacent pts black */  GLOBAL(int,maxadjacent,MAXADJACENT); /* darken if<=adjacent pts black */
 GLOBAL(int,weightnum,1); /* font wt, */  GLOBAL(int,weightnum,1); /* font wt, */
 GLOBAL(int,maxaaparams,4); /* #entries in table */  GLOBAL(int,maxaaparams,4); /* #entries in table */
 /* --- parameter values by font weight --- */  /* --- anti-aliasing parameter values by font weight --- */
 #define aaparameters struct aaparameters_struct /* typedef */  #define aaparameters struct aaparameters_struct /* typedef */
 aaparameters  aaparameters
   { int centerwt; /* lowpass matrix center   pixel wt*/    { int centerwt; /* lowpass matrix center   pixel wt*/
Line 450  STATIC aaparameters aaparams[]  /* set p Line 657  STATIC aaparameters aaparams[]  /* set p
   } /* --- end-of-aaparams[] --- */    } /* --- end-of-aaparams[] --- */
   #endif    #endif
   ;    ;
   /* --- anti-aliasing diagnostics (to help improve algorithm) --- */
   STATIC int patternnumcount0[99], patternnumcount1[99], /*aalookup() counts*/
    ispatternnumcount = 1; /* true to accumulate counts */
   
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 other variables  other variables
Line 473  other variables Line 683  other variables
 #ifndef FGBLUE  #ifndef FGBLUE
   #define FGBLUE  (ISBLACKONWHITE?0:255)    #define FGBLUE  (ISBLACKONWHITE?0:255)
 #endif  #endif
 /* --- "squash" margin (0 means no squashing) --- */  /* --- advertisement
 #ifndef SQUASHMARGIN     one image in every ADFREQUENCY is wrapped in "advertisement" --- */
   #ifdef NOSQUASH  #if !defined(ADFREQUENCY)
     #define SQUASHMARGIN 0    #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) --- */
   #ifndef SMASHMARGIN
     #ifdef NOSMASH
       #define SMASHMARGIN 0
   #else    #else
     #define SQUASHMARGIN 3      #define SMASHMARGIN 3
   #endif    #endif
 #endif  #endif
   #ifndef SMASHCHECK
     #define SMASHCHECK 0
   #endif
 /* --- textwidth --- */  /* --- textwidth --- */
 #ifndef TEXTWIDTH  #ifndef TEXTWIDTH
   #define TEXTWIDTH (400)    #define TEXTWIDTH (400)
 #endif  #endif
 /* --- font "combinations" --- */  /* --- font "combinations" --- */
 #define CMSYEX (107) /* select CMSY10 _or_ CMEX10 */  #define CMSYEX (109) /*select CMSY10, CMEX10 or STMARY10*/
 /* --- prefix prepended to all expressions --- */  /* --- prefix prepended to all expressions --- */
 #ifndef PREFIX  #ifndef PREFIX
   #define PREFIX "\000" /* default no prepended prefix */    #define PREFIX "\000" /* default no prepended prefix */
Line 511  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 522  other variables Line 809  other variables
 #ifndef PATHPREFIX  #ifndef PATHPREFIX
   #define PATHPREFIX "\000" /* paths relative mimetex.cgi */    #define PATHPREFIX "\000" /* paths relative mimetex.cgi */
 #endif  #endif
   /* --- time zone delta t (in hours) --- */
   #ifndef TZDELTA
     #define TZDELTA 0
   #endif
   /* --- treat +'s in query string as blanks? --- */
   #ifdef PLUSBLANK /* + always interpreted as blank */
     #define ISPLUSBLANK 1
   #else
     #ifdef PLUSNOTBLANK /* + never interpreted as blank */
       #define ISPLUSBLANK 0
     #else /* program tries to determine */
       #define ISPLUSBLANK (-1)
     #endif
   #endif
   
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 debugging and logging / error reporting  debugging and logging / error reporting
Line 535  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 553  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,istext,0); /* textmode if true,italics=2,bb=3 */  GLOBAL(int,isstring,0); /*pixmap is ascii string, not raster*/
 GLOBAL(int,isstring ,0); /*pixmap is ascii string, not raster*/  GLOBAL(int,isligature,0); /* true if ligature found */
   GLOBAL(char,*subexprptr,(char *)NULL); /* ptr within expression to subexpr*/
   /*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,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,squashmargin,SQUASHMARGIN); /* minimum "squash" margin */  GLOBAL(int,adfrequency,ADFREQUENCY); /* advertisement frequency */
 GLOBAL(int,issquashdelta,1); /* true if squashmargin is a delta */  GLOBAL(int,isnocatspace,0); /* >0 to not add space in rastcat()*/
 GLOBAL(int,istransparent,1); /*true to set background transparent*/  GLOBAL(int,smashmargin,SMASHMARGIN); /* minimum "smash" margin */
   GLOBAL(int,mathsmashmargin,SMASHMARGIN); /* needed for \text{if $n-m$ even}*/
   GLOBAL(int,issmashdelta,1); /* true if smashmargin is a delta */
   GLOBAL(int,isexplicitsmash,0); /* true if \smash explicitly given */
   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,isdelimscript,0); /* is \right delim text-scripted */
   GLOBAL(int,issmashokay,0); /*is leading char okay for smashing*/
   #define BLANKSIGNAL (-991234) /*rastsmash signal right-hand blank*/
   GLOBAL(int,blanksignal,BLANKSIGNAL); /*rastsmash signal right-hand blank*/
   GLOBAL(int,blanksymspace,0); /* extra (or too much) space wanted*/
   GLOBAL(int,istransparent,ISTRANSPARENT);/* true sets background transparent*/
 GLOBAL(int,fgred,FGRED);  GLOBAL(int,fgred,FGRED);
   GLOBAL(int,fggreen,FGGREEN);    GLOBAL(int,fggreen,FGGREEN);
   GLOBAL(int,fgblue,FGBLUE); /* fg r,g,b */    GLOBAL(int,fgblue,FGBLUE); /* fg r,g,b */
 GLOBAL(int,bgred,BGRED);  GLOBAL(int,bgred,BGRED);
   GLOBAL(int,bggreen,BGGREEN);    GLOBAL(int,bggreen,BGGREEN);
   GLOBAL(int,bgblue,BGBLUE); /* bg r,g,b */    GLOBAL(int,bgblue,BGBLUE); /* bg r,g,b */
   GLOBAL(double,gammacorrection,GAMMA); /* gamma correction */
   GLOBAL(int,isplusblank,ISPLUSBLANK); /*interpret +'s in query as blanks?*/
 GLOBAL(int,isblackonwhite,ISBLACKONWHITE); /*1=black on white,0=reverse*/  GLOBAL(int,isblackonwhite,ISBLACKONWHITE); /*1=black on white,0=reverse*/
 GLOBAL(char,exprprefix[256],PREFIX); /* prefix prepended to expressions */  GLOBAL(char,exprprefix[256],PREFIX); /* prefix prepended to expressions */
 GLOBAL(int,aaalgorithm,AAALGORITHM); /* for lp, 1=aalowpass, 2 =aapnm */  GLOBAL(int,aaalgorithm,AAALGORITHM); /* for lp, 1=aalowpass, 2 =aapnm */
   GLOBAL(int,maxfollow,MAXFOLLOW); /* aafollowline() maxturn parameter*/
 GLOBAL(int,fgalias,1);  GLOBAL(int,fgalias,1);
   GLOBAL(int,fgonly,0);    GLOBAL(int,fgonly,0);
   GLOBAL(int,bgalias,0);    GLOBAL(int,bgalias,0);
Line 587  GLOBAL(subraster,*workingbox,(subraster Line 920  GLOBAL(subraster,*workingbox,(subraster
 GLOBAL(int,isreplaceleft,0); /* true to replace leftexpression */  GLOBAL(int,isreplaceleft,0); /* true to replace leftexpression */
 GLOBAL(subraster,*leftexpression,(subraster *)NULL); /*rasterized so far*/  GLOBAL(subraster,*leftexpression,(subraster *)NULL); /*rasterized so far*/
 GLOBAL(mathchardef,*leftsymdef,NULL); /* mathchardef for preceding symbol*/  GLOBAL(mathchardef,*leftsymdef,NULL); /* mathchardef for preceding symbol*/
   GLOBAL(int,fraccenterline,NOVALUE); /* baseline for punct. after \frac */
   /*GLOBAL(int,currentcharclass,NOVALUE);*/ /*primarily to check for PUNCTION*/
 GLOBAL(int,iscaching,ISCACHING); /* true if caching images */  GLOBAL(int,iscaching,ISCACHING); /* true if caching images */
 GLOBAL(char,cachepath[256],CACHEPATH); /* relative path to cached files */  GLOBAL(char,cachepath[256],CACHEPATH); /* relative path to cached files */
   GLOBAL(int,isemitcontenttype,1); /* true to emit mime content-type */
   int iscachecontenttype = 0; /* true to cache mime content-type */
   char contenttype[2048] = "\000"; /* content-type:, etc buffer */
 GLOBAL(char,pathprefix[256],PATHPREFIX); /*prefix for \input,\counter paths*/  GLOBAL(char,pathprefix[256],PATHPREFIX); /*prefix for \input,\counter paths*/
 /*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 602  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 */
   /*#define spnosmash(sp) if (sp->type==CHARASTER) sp=subrastcpy(sp); \ */
   /* 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 655  if ( rp == (raster *)NULL )  /* malloc f Line 1048  if ( rp == (raster *)NULL )  /* malloc f
   goto end_of_job; /* return error to caller */    goto end_of_job; /* return error to caller */
 rp->width = width; /* store width in raster struct */  rp->width = width; /* store width in raster struct */
 rp->height = height; /* and store height */  rp->height = height; /* and store height */
   rp->format = 1; /* initialize as bitmap format */
 rp->pixsz = pixsz; /* store #bits per pixel */  rp->pixsz = pixsz; /* store #bits per pixel */
 rp->pixmap = (pixbyte *)NULL; /* init bitmap as null ptr */  rp->pixmap = (pixbyte *)NULL; /* init bitmap as null ptr */
 /* --- allocate and initialize bitmap array --- */  /* --- allocate and initialize bitmap array --- */
Line 775  cp->charnum = cp->location = 0;  /* init Line 1169  cp->charnum = cp->location = 0;  /* init
 cp->toprow = cp->topleftcol = 0; /* init upper-left corner */  cp->toprow = cp->topleftcol = 0; /* init upper-left corner */
 cp->botrow = cp->botleftcol = 0; /* init lower-left corner */  cp->botrow = cp->botleftcol = 0; /* init lower-left corner */
 cp->image.width = cp->image.height = 0; /* init raster dimensions */  cp->image.width = cp->image.height = 0; /* init raster dimensions */
   cp->image.format = 0; /* init raster format */
 cp->image.pixsz = 0; /* and #bits per pixel */  cp->image.pixsz = 0; /* and #bits per pixel */
 cp->image.pixmap = NULL; /* init raster pixmap as null */  cp->image.pixmap = NULL; /* init raster pixmap as null */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
Line 947  newsp->image = newrp;   /* new raster im Line 1342  newsp->image = newrp;   /* new raster im
 switch ( sp->type ) /* set new raster image type */  switch ( sp->type ) /* set new raster image type */
   { case STRINGRASTER: case CHARASTER: newsp->type = STRINGRASTER; break;    { case STRINGRASTER: case CHARASTER: newsp->type = STRINGRASTER; break;
     case ASCIISTRING:                  newsp->type = ASCIISTRING;  break;      case ASCIISTRING:                  newsp->type = ASCIISTRING;  break;
       case FRACRASTER:                   newsp->type = FRACRASTER;   break;
       case BLANKSIGNAL:                  newsp->type = blanksignal;  break;
     case IMAGERASTER:  default:        newsp->type = IMAGERASTER;  break; }      case IMAGERASTER:  default:        newsp->type = IMAGERASTER;  break; }
 /* --- return copy of sp to caller --- */  /* --- return copy of sp to caller --- */
 end_of_job:  end_of_job:
Line 960  end_of_job: Line 1357  end_of_job:
  * --------------------------------------------------------------------------   * --------------------------------------------------------------------------
  * Arguments: rp (I) ptr to raster struct to be rotated   * Arguments: rp (I) ptr to raster struct to be rotated
  * --------------------------------------------------------------------------   * --------------------------------------------------------------------------
  * Returns: ( raster * ) ptr to new raster rotated ralative to rp,   * Returns: ( raster * ) ptr to new raster rotated relative to rp,
  * or NULL for any error.   * or NULL for any error.
  * --------------------------------------------------------------------------   * --------------------------------------------------------------------------
  * Notes:     o An underbrace is } rotated 90 degrees clockwise,   * Notes:     o An underbrace is } rotated 90 degrees clockwise,
Line 993  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 )
    * Purpose: reflects rp, horizontally about y-axis |_ becomes _| if axis=1
    * or vertically about x-axis M becomes W if axis=2.
    * --------------------------------------------------------------------------
    * Arguments: rp (I) ptr to raster struct to be reflected
    * axis (I) int containing 1 for horizontal reflection,
    * or 2 for vertical
    * --------------------------------------------------------------------------
    * Returns: ( raster * ) ptr to new raster reflected relative to rp,
    * or NULL for any error.
    * --------------------------------------------------------------------------
    * Notes:     o
    * ======================================================================= */
   /* --- entry point --- */
   raster *rastref ( raster *rp, int axis )
   {
   /* -------------------------------------------------------------------------
   Allocations and Declarations
   -------------------------------------------------------------------------- */
   raster *new_raster(), *reflected=NULL; /* reflected raster back to caller */
   int height = rp->height, irow, /* height, row index */
    width = rp->width, icol, /* width, column index */
    pixsz = rp->pixsz; /* #bits per pixel */
   /* -------------------------------------------------------------------------
   allocate reflected raster and fill it
   -------------------------------------------------------------------------- */
   /* --- allocate reflected raster with same width, height --- */
   if ( axis==1 || axis==2 ) /* first validate axis arg */
    if ( (reflected = new_raster(width,height,pixsz)) /* same width, height */
    !=   NULL ) /* check that allocation succeeded */
     /* --- fill reflected raster --- */
     for ( irow=0; irow<height; irow++ ) /* for each row of rp */
       for ( icol=0; icol<width; icol++ ) { /* and each column of rp */
         int value = getpixel(rp,irow,icol);
         if ( axis == 1 ) { setpixel(reflected,irow,width-1-icol,value); }
         if ( axis == 2 ) { setpixel(reflected,height-1-irow,icol,value); } }
   return ( reflected ); /*return reflected raster to caller*/
   } /* --- end-of-function rastref() --- */
   
   
   /* ==========================================================================
  * Function: rastput ( target, source, top, left, isopaque )   * Function: rastput ( target, source, top, left, isopaque )
  * Purpose: Overlays source onto target,   * Purpose: Overlays source onto target,
  * with the 0,0-bit of source onto the top,left-bit of target.   * with the 0,0-bit of source onto the top,left-bit of target.
Line 1046  else Line 1656  else
  if ( isfatal ) goto end_of_job; /* abort if error is fatal */   if ( isfatal ) goto end_of_job; /* abort if error is fatal */
  else break; } /*or just go on to next row*/   else break; } /*or just go on to next row*/
     if ( tpix >= 0 ) /* bounds check okay */      if ( tpix >= 0 ) /* bounds check okay */
      if ( svalue!=0 || isopaque ) /*got dark or opaque source*/       if ( svalue!=0 || isopaque ) { /*got dark or opaque source*/
       setpixel(target,irow+top,icol+left,svalue); /*overlay source on target*/        setpixel(target,irow+top,icol+left,svalue); }/*overlay source on targ*/
     } /* --- end-of-for(icol) --- */      } /* --- end-of-for(icol) --- */
   } /* --- end-of-for(irow) --- */    } /* --- end-of-for(irow) --- */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
Line 1072  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 1100  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 1124  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 1190  int base1   = sp1->baseline, /*baseline Line 1865  int base1   = sp1->baseline, /*baseline
  pixsz2  = (sp2->image)->pixsz, /* pixsz for right-hand subraster */   pixsz2  = (sp2->image)->pixsz, /* pixsz for right-hand subraster */
  type2   = sp2->type; /* image type for right-hand */   type2   = sp2->type; /* image type for right-hand */
 int height=0, width=0, pixsz=0, base=0; /*concatted sp1||sp2 composite*/  int height=0, width=0, pixsz=0, base=0; /*concatted sp1||sp2 composite*/
 int issquash = (squashmargin!=0?1:0), /* true to "squash" sp1||sp2 */  int issmash = (smashmargin!=0?1:0), /* true to "squash" sp1||sp2 */
  isopaque = (issquash?0:1), /* not oppaque if squashing */   isopaque = (issmash?0:1), /* not oppaque if smashing */
  rastsquash(), isblank=0, nsquash=0, /* #cols to squash */   rastsmash(), isblank=0, nsmash=0, /* #cols to smash */
  oldsquashmargin = squashmargin; /* save original squashmargin */   oldsmashmargin = smashmargin, /* save original smashmargin */
 int blanksignal = (-991234); /*rastsquash signal right-hand blank*/   oldblanksymspace = blanksymspace, /* save original blanksymspace */
    oldnocatspace = isnocatspace; /* save original isnocatspace */
 mathchardef *symdef1 = sp1->symdef, /*mathchardef of last left-hand char*/  mathchardef *symdef1 = sp1->symdef, /*mathchardef of last left-hand char*/
  *symdef2 = sp2->symdef; /* mathchardef of right-hand char */   *symdef2 = sp2->symdef; /* mathchardef of right-hand char */
 int class1 = (symdef1==NULL?ORDINARY:symdef1->class), /* symdef->class */  int class1 = (symdef1==NULL?ORDINARY:symdef1->class), /* symdef->class */
Line 1204  int class1 = (symdef1==NULL?ORDINARY:sym Line 1880  int class1 = (symdef1==NULL?ORDINARY:sym
  smash2 = (symdef2!=NULL)&&(class2==ORDINARY||class2==VARIABLE||   smash2 = (symdef2!=NULL)&&(class2==ORDINARY||class2==VARIABLE||
   class2==OPENING||class2==CLOSING||class2==PUNCTION),    class2==OPENING||class2==CLOSING||class2==PUNCTION),
  space = fontsize/2+1; /* #cols between sp1 and sp2 */   space = fontsize/2+1; /* #cols between sp1 and sp2 */
   int isfrac = (type1 == FRACRASTER /* sp1 is a \frac */
     && class2 == PUNCTION); /* and sp2 is punctuation */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 Initialization  Initialization
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
Line 1211  Initialization Line 1889  Initialization
 if ( !isstring )  if ( !isstring )
   space = max2(2,(symspace[class1][class2] + fontsize-3)); /* space */    space = max2(2,(symspace[class1][class2] + fontsize-3)); /* space */
 else space = 1; /* space for ascii string */  else space = 1; /* space for ascii string */
 /* --- determine squash --- */  if ( isnocatspace > 0 ) { /* spacing explicitly turned off */
 if ( !isstring ) /* don't squash strings */    space = 0; /* reset space */
  if ( issquash ) { /* raster squash wanted */    isnocatspace--; } /* and decrement isnocatspace flag */
    int maxsquash = rastsquash(sp1,sp2), /* calculate max squash space */  if ( 0 && sp1->type == BLANKSIGNAL ) space=0; /*implicitly turn off spacing*/
  margin = squashmargin; /* init margin without delta */  if ( sp1->type==BLANKSIGNAL && sp2->type==BLANKSIGNAL ) /* both blank */
     space = 0; /* no extra space between spaces */
   if ( sp2->type != BLANKSIGNAL ) /* not a blank space signal */
     if ( blanksymspace != 0 ) { /* and we have a space adjustment */
       space = max2(0,space+blanksymspace); /* adjust as much as possible */
       blanksymspace = 0; } /* and reset adjustment */
   if ( msgfp!=NULL && msglevel>=999 ) /* display space results */
     { fprintf(msgfp,"rastcat> space=%d, blanksymspace=%d, isnocatspace=%d\n",
       space,oldblanksymspace,oldnocatspace);  fflush(msgfp); }
   /* --- determine smash --- */
   if ( !isstring && !isfrac ) /* don't smash strings or \frac's */
    if ( issmash ) { /* raster smash wanted */
      int maxsmash = rastsmash(sp1,sp2), /* calculate max smash space */
    margin = smashmargin; /* init margin without delta */
    if ( (1 && smash1 && smash2) /* concatanating two chars */     if ( (1 && smash1 && smash2) /* concatanating two chars */
    ||   (1 && type1!=IMAGERASTER && type2!=IMAGERASTER) )     ||   (1 && type1!=IMAGERASTER && type2!=IMAGERASTER
      /*maxsquash = 0;*/ /* turn off squash */     && type1!=FRACRASTER  && type2!=FRACRASTER ) )
      margin = max2(space-1,0); /* force small squashmargin */       /*maxsmash = 0;*/ /* turn off smash */
        margin = max2(space-1,0); /* force small smashmargin */
    else /* adjust for delta if images */     else /* adjust for delta if images */
      if ( issquashdelta ) /* squashmargin is a delta value */       if ( issmashdelta ) /* smashmargin is a delta value */
        margin += fontsize; /* add displaystyle base to margin */         margin += fontsize; /* add displaystyle base to margin */
    if ( maxsquash == blanksignal ) /* sp2 is intentional blank */     if ( maxsmash == blanksignal ) /* sp2 is intentional blank */
      isblank = 1; /* set blank flag signal */       isblank = 1; /* set blank flag signal */
    else /* see how much extra space we have*/     else /* see how much extra space we have*/
      if ( maxsquash > margin ) /* enough space for adjustment */       if ( maxsmash > margin ) /* enough space for adjustment */
        nsquash = maxsquash-margin; /* make adjustment */         nsmash = maxsmash-margin; /* make adjustment */
    if ( msgfp!=NULL && msglevel>=99 ) /* display squash results */     if ( msgfp!=NULL && msglevel>=99 ) /* display smash results */
      { fprintf(msgfp,"rastcat> maxsquash=%d, margin=%d, nsquash=%d\n",       { fprintf(msgfp,"rastcat> maxsmash=%d, margin=%d, nsmash=%d\n",
        maxsquash,margin,nsquash);         maxsmash,margin,nsmash);
        fprintf(msgfp,"rastcat> type1=%d,2=%d, class1=%d,2=%d\n", type1,type2,         fprintf(msgfp,"rastcat> type1=%d,2=%d, class1=%d,2=%d\n", type1,type2,
        (symdef1==NULL?-999:class1),(symdef2==NULL?-999:class2));         (symdef1==NULL?-999:class1),(symdef2==NULL?-999:class2));
        fflush(msgfp); }         fflush(msgfp); }
    } /* --- end-of-if(issquash) --- */     } /* --- end-of-if(issmash) --- */
 /* --- determine height, width and baseline of composite raster --- */  /* --- determine height, width and baseline of composite raster --- */
 if ( !isstring )  if ( !isstring )
  { height = max2(base1+1,base2+1) /* max height above baseline */   { height = max2(base1+1,base2+1) /* max height above baseline */
           + max2(height1-base1-1,height2-base2-1); /*+ max descending below*/            + max2(height1-base1-1,height2-base2-1); /*+ max descending below*/
    width  = width1+width2 + space-nsquash; /*add widths and space-squash*/     width  = width1+width2 + space-nsmash; /*add widths and space-smash*/
    width  = max3(width,width1,width2); } /* don't "over-squash" composite */     width  = max3(width,width1,width2); } /* don't "over-smash" composite */
 else /* ascii string */  else /* ascii string */
  { height = 1; /* default */   { height = 1; /* default */
    width  = width1 + width2 + space - 1; } /* no need for two nulls */     width  = width1 + width2 + space - 1; } /* no need for two nulls */
Line 1254  if ( msgfp!=NULL && msglevel>=9999 ) /* Line 1946  if ( msgfp!=NULL && msglevel>=9999 ) /*
     height2,width2,pixsz2,base2);      height2,width2,pixsz2,base2);
     type_raster(sp2->image,msgfp); /* display right-hand raster */      type_raster(sp2->image,msgfp); /* display right-hand raster */
     fprintf(msgfp,      fprintf(msgfp,
     "rastcat> Composite ht,width,squash,pixsz,base = %d,%d,%d,%d,%d\n",      "rastcat> Composite ht,width,smash,pixsz,base = %d,%d,%d,%d,%d\n",
     height,width,nsquash,pixsz,base);      height,width,nsmash,pixsz,base);
     fflush(msgfp); } /* flush msgfp buffer */      fflush(msgfp); } /* flush msgfp buffer */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 allocate concatted composite subraster  allocate concatted composite subraster
Line 1273  if ( (sp=new_subraster(width,height,pixs Line 1965  if ( (sp=new_subraster(width,height,pixs
 /* --- initialize subraster parameters --- */  /* --- initialize subraster parameters --- */
 /* sp->type = (!isstring?STRINGRASTER:ASCIISTRING); */  /*concatted string*/  /* sp->type = (!isstring?STRINGRASTER:ASCIISTRING); */  /*concatted string*/
 if ( !isstring )  if ( !isstring )
   sp->type = type2;/*(type1==type2?type2:IMAGERASTER);*/ /*string or image*/    sp->type = /*type2;*//*(type1==type2?type2:IMAGERASTER);*/
    (type2!=CHARASTER? type2 :
    (type1!=CHARASTER&&type1!=BLANKSIGNAL
    &&type1!=FRACRASTER?type1:IMAGERASTER));
 else  else
   sp->type = ASCIISTRING; /* concatted ascii string */    sp->type = ASCIISTRING; /* concatted ascii string */
 sp->symdef = symdef2; /* rightmost char is sp2 */  sp->symdef = symdef2; /* rightmost char is sp2 */
Line 1291  if ( msgfp!=NULL && msglevel>=9999 ) Line 1986  if ( msgfp!=NULL && msglevel>=9999 )
     fflush(msgfp); } /* flush msgfp buffer */      fflush(msgfp); } /* flush msgfp buffer */
 if ( !isstring )  if ( !isstring )
  rastput (rp, sp1->image, base-base1, /* overlay left-hand */   rastput (rp, sp1->image, base-base1, /* overlay left-hand */
  max2(0,nsquash-width1), 1); /* plus any residual squash space */   max2(0,nsmash-width1), 1); /* plus any residual smash space */
 else  else
  memcpy(rp->pixmap,(sp1->image)->pixmap,width1-1);  /*init left string*/   memcpy(rp->pixmap,(sp1->image)->pixmap,width1-1);  /*init left string*/
 if ( msgfp!=NULL && msglevel>=9999 )  if ( msgfp!=NULL && msglevel>=9999 )
   { type_raster(sp->image,msgfp); /* display composite raster */    { type_raster(sp->image,msgfp); /* display composite raster */
     fflush(msgfp); } /* flush msgfp buffer */      fflush(msgfp); } /* flush msgfp buffer */
 if ( !isstring )  if ( !isstring )
  rastput (rp, sp2->image, base-base2, /* overlay right-hand */   { int fracbase = ( isfrac? /* baseline for punc after \frac */
  max2(0,width1+space-nsquash), isopaque); /* minus any squashed space */   max2(fraccenterline,base2):base ); /*adjust baseline or use original*/
      rastput (rp, sp2->image, fracbase-base2, /* overlay right-hand */
      max2(0,width1+space-nsmash), isopaque); /* minus any smashed space */
      if ( 1 && type1 == FRACRASTER /* we're done with \frac image */
      &&   type2 != FRACRASTER ) /* unless we have \frac\frac */
        fraccenterline = NOVALUE; /* so reset centerline signal */
      if ( fraccenterline != NOVALUE ) /* sp2 is a fraction */
        fraccenterline += (base-base2); } /* so adjust its centerline */
 else  else
  { strcpy((char *)(rp->pixmap)+width1-1+space,(char *)((sp2->image)->pixmap));   { strcpy((char *)(rp->pixmap)+width1-1+space,(char *)((sp2->image)->pixmap));
    ((char *)(rp->pixmap))[width1+width2+space-2] = '\000'; } /*null-term*/     ((char *)(rp->pixmap))[width1+width2+space-2] = '\000'; } /*null-term*/
Line 1316  if ( isfree > 0 )   /* caller wants inpu Line 2018  if ( isfree > 0 )   /* caller wants inpu
 Back to caller with pointer to concatted subraster or with null for error  Back to caller with pointer to concatted subraster or with null for error
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 end_of_job:  end_of_job:
   squashmargin = oldsquashmargin; /* reset original squashmargin */    smashmargin = oldsmashmargin; /* reset original smashmargin */
   return ( sp ); /* back with subraster or null ptr */    return ( sp ); /* back with subraster or null ptr */
 } /* --- end-of-function rastcat() --- */  } /* --- end-of-function rastcat() --- */
   
Line 1487  end_of_job: Line 2189  end_of_job:
   
   
 /* ==========================================================================  /* ==========================================================================
  * Function: rastsquash ( sp1, sp2 )   * Function: rastsmash ( sp1, sp2 )
  * Purpose: When concatanating sp1||sp2, calculate #pixels   * Purpose: When concatanating sp1||sp2, calculate #pixels
  * we can "squash sp2 left"   * we can "smash sp2 left"
  * --------------------------------------------------------------------------   * --------------------------------------------------------------------------
  * Arguments: sp1 (I) subraster *  to left-hand raster   * Arguments: sp1 (I) subraster *  to left-hand raster
  * sp2 (I) subraster *  to right-hand raster   * sp2 (I) subraster *  to right-hand raster
  * --------------------------------------------------------------------------   * --------------------------------------------------------------------------
  * Returns: ( int ) max #pixels we can squash sp1||sp2,   * Returns: ( int ) max #pixels we can smash sp1||sp2,
  * or "blanksignal" if sp2 intentionally blank,   * or "blanksignal" if sp2 intentionally blank,
  * or 0 for any error.   * or 0 for any error.
  * --------------------------------------------------------------------------   * --------------------------------------------------------------------------
  * Notes:     o   * Notes:     o
  * ======================================================================= */   * ======================================================================= */
 /* --- entry point --- */  /* --- entry point --- */
 int rastsquash ( subraster *sp1, subraster *sp2 )  int rastsmash ( subraster *sp1, subraster *sp2 )
 {  {
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 Allocations and Declarations  Allocations and Declarations
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 int nsquash = 0; /* #pixels to squash sp1||sp2 */  int nsmash = 0; /* #pixels to smash sp1||sp2 */
 int base1   = sp1->baseline, /*baseline for left-hand subraster*/  int base1   = sp1->baseline, /*baseline for left-hand subraster*/
  height1 = (sp1->image)->height, /* height for left-hand subraster */   height1 = (sp1->image)->height, /* height for left-hand subraster */
  width1  = (sp1->image)->width, /* width for left-hand subraster */   width1  = (sp1->image)->width, /* width for left-hand subraster */
Line 1520  int base = max2(base1,base2), /* max asc Line 2222  int base = max2(base1,base2), /* max asc
 int irow1=0,irow2=0, icol=0; /* row,col indexes */  int irow1=0,irow2=0, icol=0; /* row,col indexes */
 int firstcol1[1025], nfirst1=0, /* 1st sp1 col containing set pixel*/  int firstcol1[1025], nfirst1=0, /* 1st sp1 col containing set pixel*/
  firstcol2[1025], nfirst2=0; /* 1st sp2 col containing set pixel*/   firstcol2[1025], nfirst2=0; /* 1st sp2 col containing set pixel*/
 int blanksignal = (-991234); /*rastsquash signal right-hand blank*/  
 int smin=9999, xmin=9999,ymin=9999; /* min separation (s=x+y) */  int smin=9999, xmin=9999,ymin=9999; /* min separation (s=x+y) */
 int type_raster(); /* display debugging output */  int type_raster(); /* display debugging output */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
Line 1528  find right edge of sp1 and left edge of Line 2229  find right edge of sp1 and left edge of
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 /* --- check args --- */  /* --- check args --- */
 if ( isstring ) goto end_of_job; /* ignore string rasters */  if ( isstring ) goto end_of_job; /* ignore string rasters */
 if ( height > 1023 ) goto end_of_job; /* don't try to squash huge image */  if ( 0 && istextmode ) goto end_of_job; /* don't smash in text mode */
   if ( height > 1023 ) goto end_of_job; /* don't try to smash huge image */
 if ( sp2->type == blanksignal ) /*blanksignal was propagated to us*/  if ( sp2->type == blanksignal ) /*blanksignal was propagated to us*/
   goto end_of_job; /* don't squash intentional blank */    goto end_of_job; /* don't smash intentional blank */
 /* --- init firstcol1[], firstcol2[] --- */  /* --- init firstcol1[], firstcol2[] --- */
 for ( irow1=0; irow1<height; irow1++ ) /* for each row */  for ( irow1=0; irow1<height; irow1++ ) /* for each row */
   firstcol1[irow1] = firstcol2[irow1] = blanksignal; /* signal empty rows */    firstcol1[irow1] = firstcol2[irow1] = blanksignal; /* signal empty rows */
Line 1542  for ( irow2=top2; irow2<=bot2; irow2++ ) Line 2244  for ( irow2=top2; irow2<=bot2; irow2++ )
  nfirst2++; /* bump #rows containing set pixels*/   nfirst2++; /* bump #rows containing set pixels*/
  break; } /* and go on to next row */   break; } /* and go on to next row */
 if ( nfirst2 < 1 ) /*right-hand sp2 is completely blank*/  if ( nfirst2 < 1 ) /*right-hand sp2 is completely blank*/
   { nsquash = blanksignal; /* signal intentional blanks */    { nsmash = blanksignal; /* signal intentional blanks */
     goto end_of_job; } /* don't squash intentional blanks */      goto end_of_job; } /* don't smash intentional blanks */
 /* --- now check if preceding image in sp1 was an intentional blank --- */  /* --- now check if preceding image in sp1 was an intentional blank --- */
 if ( sp1->type == blanksignal ) /*blanksignal was propagated to us*/  if ( sp1->type == blanksignal ) /*blanksignal was propagated to us*/
   goto end_of_job; /* don't squash intentional blank */    goto end_of_job; /* don't smash intentional blank */
 /* --- set firstcol1[] indicating right edge of sp1 --- */  /* --- set firstcol1[] indicating right edge of sp1 --- */
 for ( irow1=top1; irow1<=bot1; irow1++ ) /* for each row inside sp1 */  for ( irow1=top1; irow1<=bot1; irow1++ ) /* for each row inside sp1 */
   for ( icol=width1-1; icol>=0; icol-- ) /* find last non-empty col in row */    for ( icol=width1-1; icol>=0; icol-- ) /* find last non-empty col in row */
Line 1555  for ( irow1=top1; irow1<=bot1; irow1++ ) Line 2257  for ( irow1=top1; irow1<=bot1; irow1++ )
  nfirst1++; /* bump #rows containing set pixels*/   nfirst1++; /* bump #rows containing set pixels*/
  break; } /* and go on to next row */   break; } /* and go on to next row */
 if ( nfirst1 < 1 ) /*left-hand sp1 is completely blank*/  if ( nfirst1 < 1 ) /*left-hand sp1 is completely blank*/
   goto end_of_job; /* don't squash intentional blanks */    goto end_of_job; /* don't smash intentional blanks */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 find minimum separation  find minimum separation
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 for ( irow2=top2; irow2<=bot2; irow2++ ) { /* check each row inside sp2 */  for ( irow2=top2; irow2<=bot2; irow2++ ) { /* check each row inside sp2 */
  int margin1, margin2=firstcol2[irow2]; /* #cols to first set pixel */   int margin1, margin2=firstcol2[irow2]; /* #cols to first set pixel */
  if ( margin2 != blanksignal ) /* irow2 not an empty/blank row */   if ( margin2 != blanksignal ) { /* irow2 not an empty/blank row */
   for ( irow1=max2(irow2-smin,top1); ; irow1++ )    for ( irow1=max2(irow2-smin,top1); ; irow1++ )
    if ( irow1 > min2(irow2+smin,bot1) ) break; /* upper bound check */     if ( irow1 > min2(irow2+smin,bot1) ) break; /* upper bound check */
    else     else
     if ( (margin1=firstcol1[irow1]) != blanksignal ) { /*have non-blank row*/      if ( (margin1=firstcol1[irow1]) != blanksignal ) { /*have non-blank row*/
      int dx=(margin1+margin2), dy=absval(irow2-irow1), ds=dx+dy; /* deltas */       int dx=(margin1+margin2), dy=absval(irow2-irow1), ds=dx+dy; /* deltas */
      if ( ds >= smin ) continue; /* min unchanged */       if ( ds >= smin ) continue; /* min unchanged */
      if ( dy>squashmargin && dx<xmin && smin<9999 ) continue; /* dy alone */       if ( dy>smashmargin && dx<xmin && smin<9999 ) continue; /* dy alone */
      smin=ds; xmin=dx; ymin=dy; /* set new min */       smin=ds; xmin=dx; ymin=dy; /* set new min */
      } /* --- end-of-if(margin1!=blanksignal) --- */       } /* --- end-of-if(margin1!=blanksignal) --- */
  if ( smin<2 ) goto end_of_job; /* can't squash */    } /* --- end-of-if(margin2!=blanksignal) --- */
    if ( smin<2 ) goto end_of_job; /* can't smash */
  } /* --- end-of-for(irow2) --- */   } /* --- end-of-for(irow2) --- */
 /*nsquash = min2(xmin,width2);*/ /* permissible squash */  /*nsmash = min2(xmin,width2);*/ /* permissible smash */
 nsquash = xmin; /* permissible squash */  nsmash = xmin; /* permissible smash */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 Back to caller with #pixels to squash sp1||sp2  Back to caller with #pixels to smash sp1||sp2
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 end_of_job:  end_of_job:
   /* --- debugging output --- */    /* --- debugging output --- */
   if ( msgfp!=NULL && msglevel >= 99 ) /* display for debugging */    if ( msgfp!=NULL && msglevel >= 99 ) /* display for debugging */
     { fprintf(msgfp,"rastsquash> nsquash=%d, squashmargin=%d\n",      { fprintf(msgfp,"rastsmash> nsmash=%d, smashmargin=%d\n",
       nsquash,squashmargin);        nsmash,smashmargin);
       if ( msglevel >= 999 ) /* also display rasters */        if ( msglevel >= 999 ) /* also display rasters */
  { fprintf(msgfp,"rastsquash>left-hand image...\n");   { fprintf(msgfp,"rastsmash>left-hand image...\n");
   if(sp1!=NULL) type_raster(sp1->image,msgfp); /* left image */    if(sp1!=NULL) type_raster(sp1->image,msgfp); /* left image */
   fprintf(msgfp,"rastsquash>right-hand image...\n");    fprintf(msgfp,"rastsmash>right-hand image...\n");
   if(sp2!=NULL) type_raster(sp2->image,msgfp); } /* right image */    if(sp2!=NULL) type_raster(sp2->image,msgfp); } /* right image */
       fflush(msgfp); }        fflush(msgfp); }
   return ( nsquash ); /* back with #squash pixels */    return ( nsmash ); /* back with #smash pixels */
 } /* --- end-of-function rastsquash() --- */  } /* --- end-of-function rastsmash() --- */
   
   
   /* ==========================================================================
    * Function: rastsmashcheck ( term )
    * Purpose: Check an exponent term to see if its leading symbol
    * would make smashing dangerous
    * --------------------------------------------------------------------------
    * Arguments: term (I) char *  to null-terminated string
    * containing right-hand exponent term about to
    * be smashed against existing left-hand.
    * --------------------------------------------------------------------------
    * Returns: ( int ) 1 if it's okay to smash term, or
    * 0 if smash is dangerous.
    * --------------------------------------------------------------------------
    * Notes:     o
    * ======================================================================= */
   /* --- entry point --- */
   int rastsmashcheck ( char *term )
   {
   /* -------------------------------------------------------------------------
   Allocations and Declarations
   -------------------------------------------------------------------------- */
   int isokay = 0; /* 1 to signal okay to caller */
   static char nosmashchars[64] = "-.,="; /* don't smash these leading chars */
   static char *nosmashstrs[64] = { "\\frac", NULL }; /* or leading strings */
   static char *grayspace[64] = { "\\tiny", "\\small", "\\normalsize",
    "\\large", "\\Large", "\\LARGE", "\\huge", "\\Huge", NULL };
   char *expression = term; /* local ptr to beginning of expression */
   char *token = NULL;  int i; /* token = nosmashstrs[i] or grayspace[i] */
   /* -------------------------------------------------------------------------
   see if smash check enabled
   -------------------------------------------------------------------------- */
   if ( smashcheck < 1 ) { /* no smash checking wanted */
     if ( smashcheck >= 0 ) /* -1 means check should always fail */
       isokay = 1; /* otherwise (if 0), signal okay to smash */
     goto end_of_job; } /* return to caller */
   /* -------------------------------------------------------------------------
   skip leading white and gray space
   -------------------------------------------------------------------------- */
   /* --- first check input --- */
   if ( isempty(term) ) goto end_of_job; /* no input so return 0 to caller */
   /* --- skip leading white space --- */
   skipwhite(term); /* skip leading white space */
   if ( *term == '\000' ) goto end_of_job; /* nothing but white space */
   /* --- skip leading gray space --- */
   skipgray:
    for ( i=0; (token=grayspace[i]) != NULL; i++ ) /* check each grayspace */
     if ( strncmp(term,token,strlen(token)) == 0 ) { /* found grayspace */
      term += strlen(token); /* skip past this grayspace token */
      skipwhite(term); /* and skip any subsequent white space */
      if ( *term == '\000' ) { /* nothing left so quit */
        if ( msgfp!=NULL && msglevel >= 99 ) /* display for debugging */
          fprintf(msgfp,"rastsmashcheck> only grayspace in %.32s\n",expression);
        goto end_of_job; }
      goto skipgray; } /* restart grayspace check from beginning */
   /* -------------------------------------------------------------------------
   check for leading no-smash single char
   -------------------------------------------------------------------------- */
   /* --- don't smash if term begins with a "nosmash" char --- */
   if ( (token=strchr(nosmashchars,*term)) != NULL ) {
     if ( msgfp!=NULL && msglevel >= 99 ) /* display for debugging */
       fprintf(msgfp,"rastsmashcheck> char %.1s found in %.32s\n",token,term);
     goto end_of_job; }
   /* -------------------------------------------------------------------------
   check for leading no-smash token
   -------------------------------------------------------------------------- */
   for ( i=0; (token=nosmashstrs[i]) != NULL; i++ ) /* check each nosmashstr */
    if ( strncmp(term,token,strlen(token)) == 0 ) { /* found a nosmashstr */
     if ( msgfp!=NULL && msglevel >= 99 ) /* display for debugging */
       fprintf(msgfp,"rastsmashcheck> token %s found in %.32s\n",token,term);
     goto end_of_job; } /* so don't smash term */
   /* -------------------------------------------------------------------------
   back to caller
   -------------------------------------------------------------------------- */
   isokay = 1; /* no problem, so signal okay to smash */
   end_of_job:
     if ( msgfp!=NULL && msglevel >= 999 ) /* display for debugging */
       fprintf(msgfp,"rastsmashcheck> returning isokay=%d for \"%.32s\"\n",
       isokay,(expression==NULL?"<no input>":expression));
     return ( isokay ); /* back to caller with 1 if okay to smash */
   } /* --- end-of-function rastsmashcheck() --- */
   
   
 /* ==========================================================================  /* ==========================================================================
  * 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 1603  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 1612  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 1622  raster *new_raster(), *rp=NULL; /*raster Line 2409  raster *new_raster(), *rp=NULL; /*raster
 subraster *new_subraster(), *sp=NULL; /* subraster returning accent */  subraster *new_subraster(), *sp=NULL; /* subraster returning accent */
 int delete_raster(), delete_subraster(); /*free allocated raster on err*/  int delete_raster(), delete_subraster(); /*free allocated raster on err*/
 int line_raster(), /* draws lines */  int line_raster(), /* draws lines */
    rule_raster(), /* draw solid boxes */
  thickness = 1; /* line thickness */   thickness = 1; /* line thickness */
 /*int pixval = (pixsz==1? 1 : (pixsz==8?255:(-1)));*/ /*black pixel value*/  /*int pixval = (pixsz==1? 1 : (pixsz==8?255:(-1)));*/ /*black pixel value*/
 /* --- other working info --- */  /* --- other working info --- */
Line 1634  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 1653  switch ( accent ) Line 2448  switch ( accent )
     /* --- bar request --- */      /* --- bar request --- */
     case UNDERBARACCENT:      case UNDERBARACCENT:
     case BARACCENT:      case BARACCENT:
  thickness = height-1; /* adjust thickness */   thickness = 1; /*height-1;*/ /* adjust thickness */
  if ( accent == BARACCENT ) /* bar is above expression */   if ( accent == BARACCENT ) /* bar is above expression */
   line_raster(rp,0,0,0,width-1,thickness); /*leave blank line at bot*/   { row0 = row1 = max2(height-3,0); /* row numbers for overbar */
      line_raster(rp,row0,0,row1,width-1,thickness); } /*blanks at bot*/
  else /* underbar is below expression */   else /* underbar is below expression */
   line_raster(rp,1,0,1,width-1,thickness); /*leave blank line at top*/   { row0 = row1 = min2(2,height-1); /* row numbers for underbar */
      line_raster(rp,row0,0,row1,width-1,thickness); } /*blanks at top*/
  break;   break;
     /* --- dot request --- */      /* --- dot request --- */
     case DOTACCENT:      case DOTACCENT:
  thickness = height-1; /* adjust thickness */   thickness = height-1; /* adjust thickness */
  line_raster(rp,0,width/2,1,(width/2)+1,thickness); /* centered dot */   /*line_raster(rp,0,width/2,1,(width/2)+1,thickness);*//*centered dot*/
    rule_raster(rp,0,(width+1-thickness)/2,thickness,thickness,3); /*box*/
  break;   break;
     /* --- ddot request --- */      /* --- ddot request --- */
     case DDOTACCENT:      case DDOTACCENT:
  thickness = height-1; /* adjust thickness */   thickness = height-1; /* adjust thickness */
  col0 = max2(width/3-(thickness-1),0); /* one-third of width */   col0 = max2((width+1)/3-(thickness/2)-1,0); /* one-third of width */
  col1 = min2((2*width)/3+(thickness-1),width-thickness); /*two thirds*/   col1 = min2((2*width+1)/3-(thickness/2)+1,width-thickness); /*2/3rds*/
  line_raster(rp,0,col0,1,col0+1,thickness); /* set a dot at 1st third*/   if ( col0+thickness >= col1 ) /* dots overlap */
  line_raster(rp,0,col1,1,col1+1,thickness); /* and another at 2nd */    { col0 = max2(col0-1,0); /* try moving left dot more left */
       col1 = min2(col1+1,width-thickness); } /* and right dot right */
    if ( col0+thickness >= col1 ) /* dots _still_ overlap */
     thickness = max2(thickness-1,1); /* so try reducing thickness */
    /*line_raster(rp,0,col0,1,col0+1,thickness);*//*set dot at 1st third*/
    /*line_raster(rp,0,col1,1,col1+1,thickness);*//*and another at 2nd*/
    rule_raster(rp,0,col0,thickness,thickness,3); /*box at 1st third*/
    rule_raster(rp,0,col1,thickness,thickness,3); /*box at 2nd third*/
  break;   break;
     /* --- hat request --- */      /* --- hat request --- */
     case HATACCENT:      case HATACCENT:
  thickness = (width<=12? 2 : 3); /* adjust thickness */   thickness = 1; /*(width<=12? 2 : 3);*/ /* adjust thickness */
  line_raster(rp,height-1,0,0,width/2,thickness);    /* / part of hat*/   line_raster(rp,height-1,0,0,width/2,thickness);    /* / part of hat*/
  line_raster(rp,0,(width-1)/2,height-1,width-1,thickness); /* \ part*/   line_raster(rp,0,(width-1)/2,height-1,width-1,thickness); /* \ part*/
  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 1711  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 1724  switch ( accent ) Line 2536  switch ( accent )
       if ( (sp=rastack(new_subraster(1,1,pixsz),accsp,1,0,1,3))/*space below*/        if ( (sp=rastack(new_subraster(1,1,pixsz),accsp,1,0,1,3))/*space below*/
       !=  NULL ) /* have tilde with space below it */        !=  NULL ) /* have tilde with space below it */
  { rp = sp->image; /* "extract" raster with bitmap */   { rp = sp->image; /* "extract" raster with bitmap */
   free((void *)sp); } /* and free subraster "envelope" */    free((void *)sp); /* and free subraster "envelope" */
     leftsymdef = NULL; } /* so \tilde{x}^2 works properly */
     break;      break;
  } /* --- end-of-outer-switch(accent) --- */   } /* --- end-of-outer-switch(accent) --- */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 if we constructed accent raster okay, embed it in a subraster and return it  if we constructed accent raster okay, embed it in a subraster and return it
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 /* --- if all okay, allocate subraster to contain constructed raster --- */  /* --- if all okay, allocate subraster to contain constructed raster --- */
 if ( rp != NULL ) /* accent raster constructed okay */  if ( rp != NULL ) { /* accent raster constructed okay */
   if ( (sp=new_subraster(0,0,0)) /* allocate subraster "envelope" */    if ( (sp=new_subraster(0,0,0)) /* allocate subraster "envelope" */
   ==   NULL ) /* and if we fail to allocate */    ==   NULL ) /* and if we fail to allocate */
     delete_raster(rp); /* free now-unneeded raster */      delete_raster(rp); /* free now-unneeded raster */
Line 1741  if ( rp != NULL )   /* accent raster con Line 2554  if ( rp != NULL )   /* accent raster con
       sp->image = rp; /* raster we just constructed */        sp->image = rp; /* raster we just constructed */
       sp->size = (-1); /* can't set font size here */        sp->size = (-1); /* can't set font size here */
       sp->baseline = 0; } /* can't set baseline here */        sp->baseline = 0; } /* can't set baseline here */
     } /* --- end-of-if(rp!=NULL) --- */
 /* --- return subraster containing desired accent to caller --- */  /* --- return subraster containing desired accent to caller --- */
 return ( sp ); /* return accent or NULL to caller */  return ( sp ); /* return accent or NULL to caller */
 } /* --- end-of-function accent_subraster() --- */  } /* --- end-of-function accent_subraster() --- */
Line 1774  Allocations and Declarations Line 2588  Allocations and Declarations
 subraster *new_subraster(), *arrowsp=NULL; /* allocate arrow subraster */  subraster *new_subraster(), *arrowsp=NULL; /* allocate arrow subraster */
 int rule_raster(); /* draw arrow line */  int rule_raster(); /* draw arrow line */
 int irow, midrow=height/2; /* index, midrow is arrowhead apex */  int irow, midrow=height/2; /* index, midrow is arrowhead apex */
 int icol, thickness=(height>15?2:1); /* arrowhead thickness and index */  int icol, thickness=(height>15?2:2); /* arrowhead thickness and index */
 int pixval = (pixsz==1? 1 : (pixsz==8?255:(-1))); /* black pixel value */  int pixval = (pixsz==1? 1 : (pixsz==8?255:(-1))); /* black pixel value */
 int ipix, /* raster pixmap[] index */  int ipix, /* raster pixmap[] index */
  npix = width*height; /* #pixels malloced in pixmap[] */   npix = width*height; /* #pixels malloced in pixmap[] */
Line 1800  for ( irow=0; irow<height; irow++ )  /* Line 2614  for ( irow=0; irow<height; irow++ )  /*
   if ( drctn >= 0 ) /* right arrowhead wanted */    if ( drctn >= 0 ) /* right arrowhead wanted */
     for ( icol=0; icol<thickness; icol++ ) /* for arrowhead thickness */      for ( icol=0; icol<thickness; icol++ ) /* for arrowhead thickness */
      { ipix = ((irow+1)*width - 1) - delta - icol; /* rightmost-delta-icol */       { ipix = ((irow+1)*width - 1) - delta - icol; /* rightmost-delta-icol */
        if ( ipix >= 0 ) /* bounds check */         if ( ipix >= 0 ) { /* bounds check */
  if ( pixsz == 1 ) /* have a bitmap */   if ( pixsz == 1 ) /* have a bitmap */
   setlongbit((arrowsp->image)->pixmap,ipix);/*turn on arrowhead bit*/    setlongbit((arrowsp->image)->pixmap,ipix);/*turn on arrowhead bit*/
  else /* should have a bytemap */   else /* should have a bytemap */
  if ( pixsz == 8 ) /* check pixsz for bytemap */   if ( pixsz == 8 ) /* check pixsz for bytemap */
   ((arrowsp->image)->pixmap)[ipix] = pixval; } /*set arrowhead byte*/    ((arrowsp->image)->pixmap)[ipix] = pixval; } }/*set arrowhead byte*/
   /* --- left arrowhead (same as right except for ipix calculation) --- */    /* --- left arrowhead (same as right except for ipix calculation) --- */
   if ( drctn <= 0 ) /* left arrowhead wanted */    if ( drctn <= 0 ) /* left arrowhead wanted */
     for ( icol=0; icol<thickness; icol++ ) /* for arrowhead thickness */      for ( icol=0; icol<thickness; icol++ ) /* for arrowhead thickness */
      { ipix = irow*width + delta + icol; /* leftmost bit+delta+icol */       { ipix = irow*width + delta + icol; /* leftmost bit+delta+icol */
        if ( ipix < npix ) /* bounds check */         if ( ipix < npix ) { /* bounds check */
  if ( pixsz == 1 ) /* have a bitmap */   if ( pixsz == 1 ) /* have a bitmap */
   setlongbit((arrowsp->image)->pixmap,ipix);/*turn on arrowhead bit*/    setlongbit((arrowsp->image)->pixmap,ipix);/*turn on arrowhead bit*/
  else /* should have a bytemap */   else /* should have a bytemap */
  if ( pixsz == 8 ) /* check pixsz for bytemap */   if ( pixsz == 8 ) /* check pixsz for bytemap */
   ((arrowsp->image)->pixmap)[ipix] = pixval; } /*set arrowhead byte*/    ((arrowsp->image)->pixmap)[ipix] = pixval; } }/*set arrowhead byte*/
   } /* --- end-of-for(irow) --- */    } /* --- end-of-for(irow) --- */
 end_of_job:  end_of_job:
   return ( arrowsp ); /*back to caller with arrow or NULL*/    return ( arrowsp ); /*back to caller with arrow or NULL*/
Line 1850  Allocations and Declarations Line 2664  Allocations and Declarations
 subraster *new_subraster(), *arrowsp=NULL; /* allocate arrow subraster */  subraster *new_subraster(), *arrowsp=NULL; /* allocate arrow subraster */
 int rule_raster(); /* draw arrow line */  int rule_raster(); /* draw arrow line */
 int icol, midcol=width/2; /* index, midcol is arrowhead apex */  int icol, midcol=width/2; /* index, midcol is arrowhead apex */
 int irow, thickness=(width>15?2:1); /* arrowhead thickness and index */  int irow, thickness=(width>15?2:2); /* arrowhead thickness and index */
 int pixval = (pixsz==1? 1 : (pixsz==8?255:(-1))); /* black pixel value */  int pixval = (pixsz==1? 1 : (pixsz==8?255:(-1))); /* black pixel value */
 int ipix, /* raster pixmap[] index */  int ipix, /* raster pixmap[] index */
  npix = width*height; /* #pixels malloced in pixmap[] */   npix = width*height; /* #pixels malloced in pixmap[] */
Line 1876  for ( icol=0; icol<width; icol++ )  /* f Line 2690  for ( icol=0; icol<width; icol++ )  /* f
   if ( drctn >= 0 ) /* up arrowhead wanted */    if ( drctn >= 0 ) /* up arrowhead wanted */
     for ( irow=0; irow<thickness; irow++ ) /* for arrowhead thickness */      for ( irow=0; irow<thickness; irow++ ) /* for arrowhead thickness */
      { ipix = (irow+delta)*width + icol; /* leftmost+icol */       { ipix = (irow+delta)*width + icol; /* leftmost+icol */
        if ( ipix < npix ) /* bounds check */         if ( ipix < npix ) { /* bounds check */
  if ( pixsz == 1 ) /* have a bitmap */   if ( pixsz == 1 ) /* have a bitmap */
   setlongbit((arrowsp->image)->pixmap,ipix);/*turn on arrowhead bit*/    setlongbit((arrowsp->image)->pixmap,ipix);/*turn on arrowhead bit*/
  else /* should have a bytemap */   else /* should have a bytemap */
  if ( pixsz == 8 ) /* check pixsz for bytemap */   if ( pixsz == 8 ) /* check pixsz for bytemap */
   ((arrowsp->image)->pixmap)[ipix] = pixval; } /*set arrowhead byte*/    ((arrowsp->image)->pixmap)[ipix] = pixval; } }/*set arrowhead byte*/
   /* --- down arrowhead (same as up except for ipix calculation) --- */    /* --- down arrowhead (same as up except for ipix calculation) --- */
   if ( drctn <= 0 ) /* down arrowhead wanted */    if ( drctn <= 0 ) /* down arrowhead wanted */
     for ( irow=0; irow<thickness; irow++ ) /* for arrowhead thickness */      for ( irow=0; irow<thickness; irow++ ) /* for arrowhead thickness */
      { ipix = (height-1-delta-irow)*width + icol; /* leftmost + icol */       { ipix = (height-1-delta-irow)*width + icol; /* leftmost + icol */
        if ( ipix > 0 ) /* bounds check */         if ( ipix > 0 ) { /* bounds check */
  if ( pixsz == 1 ) /* have a bitmap */   if ( pixsz == 1 ) /* have a bitmap */
   setlongbit((arrowsp->image)->pixmap,ipix);/*turn on arrowhead bit*/    setlongbit((arrowsp->image)->pixmap,ipix);/*turn on arrowhead bit*/
  else /* should have a bytemap */   else /* should have a bytemap */
  if ( pixsz == 8 ) /* check pixsz for bytemap */   if ( pixsz == 8 ) /* check pixsz for bytemap */
   ((arrowsp->image)->pixmap)[ipix] = pixval; } /*set arrowhead byte*/    ((arrowsp->image)->pixmap)[ipix] = pixval; } }/*set arrowhead byte*/
   } /* --- end-of-for(irow) --- */    } /* --- end-of-for(icol) --- */
 end_of_job:  end_of_job:
   return ( arrowsp ); /*back to caller with arrow or NULL*/    return ( arrowsp ); /*back to caller with arrow or NULL*/
 } /* --- end-of-function uparrow_subraster() --- */  } /* --- end-of-function uparrow_subraster() --- */
Line 1913  end_of_job: Line 2727  end_of_job:
  * height (I) int containing number of rows for rule   * height (I) int containing number of rows for rule
  * type (I) int containing 0 for solid rule,   * type (I) int containing 0 for solid rule,
  * 1 for horizontal dashes, 2 for vertical   * 1 for horizontal dashes, 2 for vertical
    * 3 for solid rule with corners removed (bevel)
    * 4 for strut (nothing drawn)
  * --------------------------------------------------------------------------   * --------------------------------------------------------------------------
  * Returns: ( int ) 1 if rule drawn okay,   * Returns: ( int ) 1 if rule drawn okay,
  * or 0 for any error.   * or 0 for any error.
Line 1928  int rule_raster ( raster *rp, int top, i Line 2744  int rule_raster ( raster *rp, int top, i
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 Allocations and Declarations  Allocations and Declarations
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 int irow, icol; /* indexes over rp raster */  int irow=0, icol=0; /* indexes over rp raster */
 int ipix, /* raster pixmap[] index */  int ipix = 0, /* raster pixmap[] index */
  npix = rp->width * rp->height; /* #pixels malloced in rp->pixmap[] */   npix = rp->width * rp->height; /* #pixels malloced in rp->pixmap[] */
 int isfatal = 0; /* true to abend on out-of-bounds error */  int isfatal = 0; /* true to abend on out-of-bounds error */
 int hdash=1, vdash=2; /* type for horizontal, vertical dashes */  int hdash=1, vdash=2, /* type for horizontal, vertical dashes */
    bevel=99/*3*/, strut=4; /* type for bevel (turned off), strut */
 int dashlen=3, spacelen=2, /* #pixels for dash followed by space */  int dashlen=3, spacelen=2, /* #pixels for dash followed by space */
  isdraw=1; /* true when drawing dash (init for solid) */   isdraw=1; /* true when drawing dash (init for solid) */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 Check args  Check args
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 if ( rp == (raster *)NULL ) /* no raster arg supplied */  if ( rp == (raster *)NULL ) { /* no raster arg supplied */
   if ( workingbox != (subraster *)NULL )  /* see if we have a workingbox */    if ( workingbox != (subraster *)NULL )  /* see if we have a workingbox */
     rp = workingbox->image; /* use workingbox if possible */      rp = workingbox->image; /* use workingbox if possible */
   else return ( 0 ); /* otherwise signal error to caller */    else return ( 0 ); } /* otherwise signal error to caller */
   if ( type == bevel ) /* remove corners of solid box */
     if ( width<3 || height<3 ) type=0; /* too small to remove corners */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 Fill line/box  Fill line/box
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 for ( irow=top; irow<top+height; irow++ ) /*each scan line*/  if ( width > 0 ) /* zero width implies strut*/
    for ( irow=top; irow<top+height; irow++ ) /* for each scan line */
   {    {
     if ( type == strut ) isdraw = 0; /* draw nothing for strut */
   if ( type == vdash ) /*set isdraw for vert dash*/    if ( type == vdash ) /*set isdraw for vert dash*/
     isdraw = (((irow-top)%(dashlen+spacelen)) < dashlen);      isdraw = (((irow-top)%(dashlen+spacelen)) < dashlen);
   ipix = irow*rp->width + left - 1; /*first pixel preceding icol*/    ipix = irow*rp->width + left - 1; /*first pixel preceding icol*/
   for ( icol=left; icol<left+width; icol++ ) /* each pixel in scan line */    for ( icol=left; icol<left+width; icol++ ) /* each pixel in scan line */
     {      {
       if ( type == bevel ) { /* remove corners of box */
         if ( (irow==top && icol==left) /* top-left corner */
         ||   (irow==top && icol>=left+width-1) /* top-right corner */
         ||   (irow>=top+height-1 && icol==left) /* bottom-left corner */
         ||   (irow>=top+height-1 && icol>=left+width-1) ) /* bottom-right */
    isdraw = 0;  else isdraw = 1; } /*set isdraw to skip corner*/
     if ( type == hdash ) /*set isdraw for horiz dash*/      if ( type == hdash ) /*set isdraw for horiz dash*/
       isdraw = (((icol-left)%(dashlen+spacelen)) < dashlen);        isdraw = (((icol-left)%(dashlen+spacelen)) < dashlen);
     if ( ++ipix >= npix ) /* bounds check failed */      if ( ++ipix >= npix ) /* bounds check failed */
          if ( isfatal ) goto end_of_job; /* abort if error is fatal */           if ( isfatal ) goto end_of_job; /* abort if error is fatal */
          else break; /*or just go on to next row*/           else break; /*or just go on to next row*/
     else /*ibit is within rp bounds*/      else /*ibit is within rp bounds*/
       if ( isdraw ) /*and we're drawing this bit*/        if ( isdraw ) { /*and we're drawing this bit*/
  if ( rp->pixsz == 1 ) /* have a bitmap */   if ( rp->pixsz == 1 ) /* have a bitmap */
   setlongbit(rp->pixmap,ipix); /* so turn on bit in line */    setlongbit(rp->pixmap,ipix); /* so turn on bit in line */
  else /* should have a bytemap */   else /* should have a bytemap */
  if ( rp->pixsz == 8 ) /* check pixsz for bytemap */   if ( rp->pixsz == 8 ) /* check pixsz for bytemap */
   ((unsigned char *)(rp->pixmap))[ipix] = 255; /* set black byte */    ((unsigned char *)(rp->pixmap))[ipix] = 255; } /* set black byte */
     } /* --- end-of-for(icol) --- */      } /* --- end-of-for(icol) --- */
   } /* --- end-of-for(irow) --- */    } /* --- end-of-for(irow) --- */
 end_of_job:  end_of_job:
Line 2008  int line_raster ( raster *rp, int row0, Line 2835  int line_raster ( raster *rp, int row0,
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 Allocations and Declarations  Allocations and Declarations
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 int irow, icol, /* indexes over rp raster */  int irow=0, icol=0, /* indexes over rp raster */
  locol=col0, hicol=col1, /* col limits at irow */   locol=col0, hicol=col1, /* col limits at irow */
  lorow=row0, hirow=row1; /* row limits at icol */   lorow=row0, hirow=row1; /* row limits at icol */
 int ipix, /* raster pixmap[] index */  int width=rp->width, height=rp->height; /* dimensions of input raster */
  npix = rp->width * rp->height; /* #pixels malloced in rp->pixmap[] */  int ipix = 0, /* raster pixmap[] index */
    npix = width*height; /* #pixels malloced in rp->pixmap[] */
 int isfatal = 0; /* true to abend on out-of-bounds error */  int isfatal = 0; /* true to abend on out-of-bounds error */
 int isline=(row1==row0), isbar=(col1==col0); /*true if slope a=0,\infty*/  int isline=(row1==row0), isbar=(col1==col0); /*true if slope a=0,\infty*/
 double dy = row1-row0 /* + (row1>=row0? +1.0 : -1.0) */, /* delta-x */  double dy = row1-row0 /* + (row1>=row0? +1.0 : -1.0) */, /* delta-x */
  dx = col1-col0 /* + (col1>=col0? +1.0 : -1.0) */, /* delta-y */   dx = col1-col0 /* + (col1>=col0? +1.0 : -1.0) */, /* delta-y */
  a= (isbar||isline? 0.0 : dy/dx), /* slope = tan(theta) = dy/dx */   a= (isbar||isline? 0.0 : dy/dx), /* slope = tan(theta) = dy/dx */
  xcol, xrow; /* calculated col at irow, or row at icol */   xcol=0, xrow=0; /* calculated col at irow, or row at icol */
 double ar = ASPECTRATIO, /* aspect ratio width/height of one pixel */  double ar = ASPECTRATIO, /* aspect ratio width/height of one pixel */
  xwidth= (isline? 0.0 : /*#pixels per row to get sloped line thcknss*/   xwidth= (isline? 0.0 : /*#pixels per row to get sloped line thcknss*/
  ((double)thickness)*sqrt((dx*dx)+(dy*dy*ar*ar))/fabs(dy*ar)),   ((double)thickness)*sqrt((dx*dx)+(dy*dy*ar*ar))/fabs(dy*ar)),
Line 2027  int line_recurse(), isrecurse=1; /* true Line 2855  int line_recurse(), isrecurse=1; /* true
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 Check args  Check args
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 if ( rp == (raster *)NULL ) /* no raster arg supplied */  if ( rp == (raster *)NULL ) { /* no raster arg supplied */
   if ( workingbox != (subraster *)NULL )  /* see if we have a workingbox */    if ( workingbox != (subraster *)NULL )  /* see if we have a workingbox */
     rp = workingbox->image; /* use workingbox if possible */      rp = workingbox->image; /* use workingbox if possible */
   else return ( 0 ); /* otherwise signal error to caller */    else return ( 0 ); } /* otherwise signal error to caller */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 Initialization  Initialization
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 if ( msgfp!=NULL && msglevel>=29 ) /* debugging */  if ( msgfp!=NULL && msglevel>=29 ) { /* debugging */
    fprintf(msgfp,"line_raster> row,col0=%d,%d row,col1=%d,%d, thickness=%d\n"     fprintf(msgfp,"line_raster> row,col0=%d,%d row,col1=%d,%d, thickness=%d\n"
    "\t dy,dx=%3.1f,%3.1f, a=%4.3f, xwidth=%4.3f\n",     "\t dy,dx=%3.1f,%3.1f, a=%4.3f, xwidth=%4.3f\n",
    row0,col0, row1,col1, thickness,  dy,dx, a, xwidth);     row0,col0, row1,col1, thickness,  dy,dx, a, xwidth); fflush(msgfp); }
 /* --- check for recursive line drawing --- */  /* --- check for recursive line drawing --- */
 if ( isrecurse ) /* drawing lines recursively */  if ( isrecurse ) { /* drawing lines recursively */
   { line_recurse(rp,(double)row0,(double)col0,   for ( irow=0; irow<thickness; irow++ ) /* each line 1 pixel thick */
     (double)row1,(double)col1,thickness);    { double xrow0=(double)row0, xcol0=(double)col0,
     return ( 1 ); }   xrow1=(double)row1, xcol1=(double)col1;
       if ( isline ) xrow0 = xrow1 = (double)(row0+irow);
       else if ( isbar ) xcol0 = xcol1 = (double)(col0+irow);
       if( xrow0>(-0.001) && xcol0>(-0.001) /*check line inside raster*/
       &&  xrow1<((double)(height-1)+0.001) && xcol1<((double)(width-1)+0.001) )
         line_recurse(rp,xrow0,xcol0,xrow1,xcol1,thickness); }
    return ( 1 ); }
 /* --- set params for horizontal line or vertical bar --- */  /* --- set params for horizontal line or vertical bar --- */
 if ( isline ) /*interpret row as top row*/  if ( isline ) /*interpret row as top row*/
   row1 = row0 + (thickness-1); /* set bottom row for line */    row1 = row0 + (thickness-1); /* set bottom row for line */
Line 2481  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 2499  Allocations and Declarations Line 3341  Allocations and Declarations
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 raster *new_raster(), *bp=(raster *)NULL;  /*raster back to caller*/  raster *new_raster(), *bp=(raster *)NULL;  /*raster back to caller*/
 int rastput(); /* overlay rp in new bordered raster */  int rastput(); /* overlay rp in new bordered raster */
 int width  = (rp==NULL?0:rp->width),  /* height of raster */  int width  = (rp==NULL?0:rp->width),  /* width of raster */
  height = (rp==NULL?0:rp->height), /* width  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 delete_raster(); /* to free input rp if isdelete is true */  int left=1, top=1, right=1, bot=1; /* frame sides to draw */
   int delete_raster(); /* free input rp if isfree is true */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 Initialization  Initialization
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
Line 2525  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 2541  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 2563  end_of_job: Line 3422  end_of_job:
   
   
 /* ==========================================================================  /* ==========================================================================
    * Function: backspace_raster ( rp, nback, pback, minspace, isfree )
    * Purpose: Allocate a new raster containing a copy of input rp,
    * but with trailing nback columns removed.
    * If minspace>=0 then (at least) that many columns
    * of whitespace will be left in place, regardless of nback.
    * If minspace<0 then existing black pixels will be deleted
    * as required.
    * --------------------------------------------------------------------------
    * Arguments: rp (I) raster *  to raster on which a border
    * is to be placed
    * nback (I) int containing number of columns to
    * backspace (>=0)
    * pback (O) ptr to int returning #pixels actually
    * backspaced (or NULL to not use)
    * minspace (I) int containing number of columns
    * of whitespace to be left in place
    * isfree (I) int containing true to free rp before return
    * --------------------------------------------------------------------------
    * Returns: ( raster * ) ptr to backspaced raster,
    * or NULL for any error.
    * --------------------------------------------------------------------------
    * Notes:     o For \! negative space, for \hspace{-10}, etc.
    * ======================================================================= */
   /* --- entry point --- */
   raster *backspace_raster ( raster *rp, int nback, int *pback, int minspace,
    int isfree )
   {
   /* -------------------------------------------------------------------------
   Allocations and Declarations
   -------------------------------------------------------------------------- */
   raster *new_raster(), *bp=(raster *)NULL;  /* raster returned to caller */
   int delete_raster(); /* free input rp if isfree is true */
   int width  = (rp==NULL?0:rp->width),  /* original width of raster */
    height = (rp==NULL?0:rp->height), /* height of raster */
    mback = nback, /* nback adjusted for minspace */
    newwidth = width, /* adjusted width after backspace */
    icol=0, irow=0; /* col,row index */
   if ( rp == NULL ) goto end_of_job; /* no input given */
   /* -------------------------------------------------------------------------
   locate rightmost column of rp containing ink, and determine backspaced width
   -------------------------------------------------------------------------- */
   /* --- locate rightmost column of rp containing ink --- */
   if ( minspace >= 0 ) /* only needed if given minspace */
    for ( icol=width-1; icol>=0; icol-- ) /* find first non-empty col in row */
     for ( irow=0; irow<height; irow++ ) /* for each row inside rp */
      if ( getpixel(rp,irow,icol) != 0 ) { /* found a set pixel */
        int whitecols = (width-1) - icol; /* #cols containing white space */
        mback = min2(nback,max2(0,whitecols-minspace)); /*leave minspace cols*/
        goto gotright; } /* no need to look further */
   /* --- determine width of new backspaced raster --- */
   gotright: /* found col with ink (or rp empty)*/
     if ( mback > width ) mback = width; /* can't backspace before beginning*/
     newwidth = max2(1,width-mback); /* #cols in backspaced raster */
     if ( pback != NULL ) *pback = width-newwidth; /* caller wants #pixels */
   /* -------------------------------------------------------------------------
   allocate new raster and fill it with leftmost cols of rp
   -------------------------------------------------------------------------- */
   /* --- allocate backspaced raster --- */
   if ( (bp=new_raster(newwidth,height,rp->pixsz)) /*allocate backspaced raster*/
   ==   (raster *)NULL ) goto end_of_job; /* and quit if failed */
   /* --- fill new raster --- */
   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 ( irow=0; irow<height; irow++ ) /* for each row inside rp */
       { int value = getpixel(rp,irow,icol); /* original pixel at irow,icol */
         setpixel(bp,irow,icol,value); } /* saved in backspaced raster */
   /* -------------------------------------------------------------------------
   Back to caller with backspaced raster (or null for any error)
   -------------------------------------------------------------------------- */
   end_of_job:
     if ( msgfp!=NULL && msglevel>=999 ) { fprintf(msgfp, /* diagnostics */
      "backspace_raster> nback=%d,minspace=%d,mback=%d, width:old=%d,new=%d\n",
      nback,minspace,mback,width,newwidth); fflush(msgfp); }
     if ( isfree && bp!=NULL ) delete_raster(rp); /* free original raster */
     return ( bp ); /* back with backspaced or null ptr*/
   } /* --- end-of-function backspace_raster() --- */
   
   
   /* ==========================================================================
  * Function: type_raster ( rp, fp )   * Function: type_raster ( rp, fp )
  * Purpose: Emit an ascii dump representing rp, on fp.   * Purpose: Emit an ascii dump representing rp, on fp.
  * --------------------------------------------------------------------------   * --------------------------------------------------------------------------
Line 2588  static char display_chars[16] = /* displ Line 3526  static char display_chars[16] = /* displ
 char scanline[133]; /* ascii image for one scan line */  char scanline[133]; /* ascii image for one scan line */
 int scan_width; /* #chars in scan (<=display_width)*/  int scan_width; /* #chars in scan (<=display_width)*/
 int irow, locol,hicol=(-1); /* height index, width indexes */  int irow, locol,hicol=(-1); /* height index, width indexes */
   raster *gftobitmap(), *bitmaprp=rp; /* convert .gf to bitmap if needed */
   int delete_raster(); /*free bitmap converted for display*/
 /* --------------------------------------------------------------------------  /* --------------------------------------------------------------------------
 initialization  initialization
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 /* --- redirect null fp --- */  /* --- redirect null fp --- */
 if ( fp == (FILE *)NULL ) fp = stdout; /* default fp to stdout if null */  if ( fp == (FILE *)NULL ) fp = stdout; /* default fp to stdout if null */
   if ( msglevel >= 999 ) { fprintf(fp, /* debugging diagnostics */
     "type_raster> width=%d height=%d ...\n",
     rp->width,rp->height); fflush(fp); }
 /* --- check for ascii string --- */  /* --- check for ascii string --- */
 if ( isstring /* pixmap has string, not raster */  if ( isstring /* pixmap has string, not raster */
 ||   (1 && rp->height==1) ) /* infer input rp is a string */  ||   (0 && rp->height==1) ) /* infer input rp is a string */
  {   {
  char *string = (char *)(rp->pixmap); /*interpret pixmap as ascii string*/   char *string = (char *)(rp->pixmap); /*interpret pixmap as ascii string*/
  int width = strlen(string); /* #chars in ascii string */   int width = strlen(string); /* #chars in ascii string */
Line 2609  if ( isstring    /* pixmap has string, n Line 3552  if ( isstring    /* pixmap has string, n
 /* --------------------------------------------------------------------------  /* --------------------------------------------------------------------------
 display ascii dump of bitmap image (in segments if display_width < rp->width)  display ascii dump of bitmap image (in segments if display_width < rp->width)
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 while ( (locol=hicol+1) < rp->width ) /*start where prev segment left off*/  if ( rp->format == 2 /* input is .gf-formatted */
   ||   rp->format == 3 )
     bitmaprp = gftobitmap(rp); /* so convert it for display */
   if ( bitmaprp != NULL ) /* if we have image for display */
    while ( (locol=hicol+1) < rp->width ) /*start where prev segment left off*/
   {    {
   /* --- set hicol for this pass (locol set above) --- */    /* --- set hicol for this pass (locol set above) --- */
   hicol += display_width; /* show as much as display allows */    hicol += display_width; /* show as much as display allows */
Line 2626  while ( (locol=hicol+1) < rp->width ) /* Line 3573  while ( (locol=hicol+1) < rp->width ) /*
  lopix = irow*rp->width + locol; /*first pixmap[] pixel in this scan*/   lopix = irow*rp->width + locol; /*first pixmap[] pixel in this scan*/
     /* --- set chars in scanline[] based on pixels in rp->pixmap[] --- */      /* --- set chars in scanline[] based on pixels in rp->pixmap[] --- */
     for ( ipix=0; ipix<scan_width; ipix++ ) /* set each char */      for ( ipix=0; ipix<scan_width; ipix++ ) /* set each char */
       if ( rp->pixsz == 1 ) /*' '=0 or '*'=1 to display bitmap*/        if ( bitmaprp->pixsz == 1 ) /*' '=0 or '*'=1 to display bitmap*/
  scanline[ipix] = (getlongbit(rp->pixmap,lopix+ipix)==1? '*':'.');   scanline[ipix]=(getlongbit(bitmaprp->pixmap,lopix+ipix)==1? '*':'.');
       else /* should have a bytemap */        else /* should have a bytemap */
        if ( rp->pixsz == 8 ) /* double-check pixsz for bytemap */         if ( bitmaprp->pixsz == 8 ) /* double-check pixsz for bytemap */
  { int pixval = (int)((rp->pixmap)[lopix+ipix]), /*pixel's byte value*/   { int pixval = (int)((bitmaprp->pixmap)[lopix+ipix]), /*byte value*/
   ichar = min2(15,pixval/16); /* index for ' ', '1'...'e', '*' */    ichar = min2(15,pixval/16); /* index for ' ', '1'...'e', '*' */
   scanline[ipix] = display_chars[ichar]; } /*set ' ' for 0-15, etc*/    scanline[ipix] = display_chars[ichar]; } /*set ' ' for 0-15, etc*/
     /* --- display completed scan line --- */      /* --- display completed scan line --- */
Line 2640  while ( (locol=hicol+1) < rp->width ) /* Line 3587  while ( (locol=hicol+1) < rp->width ) /*
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 Back to caller with 1=okay, 0=failed.  Back to caller with 1=okay, 0=failed.
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
   if ( rp->format == 2 /* input was .gf-format */
   ||   rp->format == 3 )
     if ( bitmaprp != NULL ) /* and we converted it for display */
       delete_raster(bitmaprp); /* no longer needed, so free it */
 return ( 1 );  return ( 1 );
 } /* --- end-of-function type_raster() --- */  } /* --- end-of-function type_raster() --- */
   
Line 2782  return ( 1 ); Line 3733  return ( 1 );
   
   
 /* ==========================================================================  /* ==========================================================================
    * Function: type_pbmpgm ( rp, ptype, file )
    * Purpose: Write pbm or pgm image of rp to file
    * --------------------------------------------------------------------------
    * Arguments: rp (I) ptr to raster struct for which
    * a pbm/pgm file is to be written.
    * ptype (I) int containing 1 for pbm, 2 for pgm, or
    * 0 to determine ptype from values in rp
    * file (I) ptr to null-terminated char string
    * containing name of fuke to be written
    * (see notes below).
    * --------------------------------------------------------------------------
    * Returns: ( int ) total #bytes written,
    * or 0 for any error.
    * --------------------------------------------------------------------------
    * Notes:     o (a) If file==NULL, output is written to stdout;
    * (b) if *file=='\000' then file is taken as the
    *    address of an output buffer to which output
    *    is written (and is followed by a terminating
    *    '\0' which is not counted in #bytes returned);
    * (c) otherwise file is the filename (opened and
    *    closed internally) to which output is written,
    *    except that any final .ext extension is replaced
    *    by .pbm or .pgm depending on ptype.
    * ======================================================================= */
   /* --- entry point --- */
   int type_pbmpgm ( raster *rp, int ptype, char *file )
   {
   /* -------------------------------------------------------------------------
   Allocations and Declarations
   -------------------------------------------------------------------------- */
   int isokay=0, nbytes=0; /* completion flag, total #bytes written */
   int irow=0, jcol=0; /*height(row), width(col) indexes in raster*/
   int pixmin=9999, pixmax=(-9999), /* min, max pixel value in raster */
    ngray = 0; /* #gray scale values */
   FILE /* *fopen(), */ *fp=NULL; /* pointer to output file (or NULL) */
   char outline[1024], outfield[256], /* output line, field */
    cr[16] = "\n\000"; /* cr at end-of-line */
   int maxlinelen = 70; /* maximum allowed line length */
   int pixfrac=6; /* use (pixmax-pixmin)/pixfrac as step */
   static char
    *suffix[] = { NULL, ".pbm", ".pgm" }, /* file.suffix[ptype] */
    *magic[] = { NULL, "P1", "P2" }, /*identifying "magic number"*/
    *mode[] = { NULL, "w", "w" }; /* fopen() mode[ptype] */
   /* -------------------------------------------------------------------------
   check input, determine grayscale,  and set up output file if necessary
   -------------------------------------------------------------------------- */
   /* --- check input args --- */
   if ( rp == NULL ) goto end_of_job; /* no input raster provided */
   if ( ptype != 0 ) /* we'll determine ptype below */
    if ( ptype<1 || ptype>2 ) goto end_of_job; /*invalid output graphic format*/
   /* --- determine largest (and smallest) value in pixmap --- */
   for ( irow=0; irow<rp->height; irow++ ) /* for each row, top-to-bottom */
    for ( jcol=0; jcol<rp->width; jcol++ ) /* for each col, left-to-right */
     { int pixval = getpixel(rp,irow,jcol);  /* value of pixel at irow,jcol */
       pixmin = min2(pixmin,pixval); /* new minimum */
       pixmax = max2(pixmax,pixval); } /* new maximum */
   ngray = 1 + (pixmax-pixmin); /* should be 2 for b/w bitmap */
   if ( ptype == 0 ) /* caller wants us to set ptype */
     ptype = (ngray>=3?2:1); /* use grayscale if >2 shades */
   /* --- open output file if necessary --- */
   if ( file == NULL ) fp = stdout; /*null ptr signals output to stdout*/
   else if ( *file != '\000' ) { /* explicit filename provided, so...*/
     char fname[512], *pdot=NULL; /* file.ext, ptr to last . in fname*/
     strncpy(fname,file,255); /* local copy of file name */
     fname[255] = '\000'; /* make sure it's null terminated */
     if ( (pdot=strrchr(fname,'.')) == NULL ) /*no extension on original name*/
       strcat(fname,suffix[ptype]); /* so add extension */
     else /* we already have an extension */
       strcpy(pdot,suffix[ptype]); /* so replace original extension */
     if ( (fp = fopen(fname,mode[ptype])) /* open output file */
     ==   (FILE *)NULL ) goto end_of_job; /* quit if failed to open */
     } /* --- ens-of-if(*file!='\0') --- */
   /* -------------------------------------------------------------------------
   format and write header
   -------------------------------------------------------------------------- */
   /* --- format header info --- */
   *outline = '\000'; /* initialize line buffer */
   strcat(outline,magic[ptype]); /* begin file with "magic number" */
   strcat(outline,cr); /* followed by cr to end line */
   sprintf(outfield,"%d %d",rp->width,rp->height); /* format width and height */
   strcat(outline,outfield); /* add width and height to header */
   strcat(outline,cr); /* followed by cr to end line */
   if ( ptype == 2 ) /* need max grayscale value */
     { sprintf(outfield,"%d",pixmax); /* format maximum pixel value */
       strcat(outline,outfield); /* add max value to header */
       strcat(outline,cr); } /* followed by cr to end line */
   /* --- write header to file or memory buffer --- */
   if ( fp == NULL ) /* if we have no open file... */
     strcat(file,outline); /* add header to caller's buffer */
   else /* or if we have an open file... */
     if ( fputs(outline,fp) /* try writing header to open file */
     ==   EOF ) goto end_of_job; /* return with error if failed */
   nbytes += strlen(outline); /* bump output byte count */
   /* -------------------------------------------------------------------------
   format and write pixels
   -------------------------------------------------------------------------- */
   *outline = '\000'; /* initialize line buffer */
   for ( irow=0; irow<=rp->height; irow++ ) /* for each row, top-to-bottom */
    for ( jcol=0; jcol<rp->width; jcol++ ) { /* for each col, left-to-right */
     /* --- format value at irow,jcol--- */
     *outfield = '\000'; /* init empty field */
     if ( irow < rp->height ) { /* check row index */
       int pixval = getpixel(rp,irow,jcol);  /* value of pixel at irow,jcol */
       if ( ptype == 1 ) /* pixval must be 1 or 0 */
         pixval = (pixval>pixmin+((pixmax-pixmin)/pixfrac)?1:0);
       sprintf(outfield,"%d ",pixval); } /* format pixel value */
     /* --- write line if this value won't fit on it (or last line) --- */
     if ( strlen(outline)+strlen(outfield)+strlen(cr) >= maxlinelen /*won't fit*/
     ||   irow >= rp->height ) { /* force writing last line */
       strcat(outline,cr); /* add cr to end current line */
       if ( fp == NULL ) /* if we have no open file... */
         strcat(file,outline); /* add header to caller's buffer */
       else /* or if we have an open file... */
         if ( fputs(outline,fp) /* try writing header to open file */
         ==   EOF ) goto end_of_job; /* return with error if failed */
       nbytes += strlen(outline); /* bump output byte count */
       *outline = '\000'; /* re-initialize line buffer */
       } /* --- end-of-if(strlen>=maxlinelen) --- */
     if ( irow >= rp->height ) break; /* done after writing last line */
     /* --- concatanate value to line -- */
     strcat(outline,outfield); /* concatanate value to line */
     } /* --- end-of-for(jcol,irow) --- */
   isokay = 1; /* signal successful completion */
   /* -------------------------------------------------------------------------
   Back to caller with total #bytes written, or 0=failed.
   -------------------------------------------------------------------------- */
   end_of_job:
     if ( fp != NULL /* output written to an open file */
     &&   fp != stdout ) /* and it's not just stdout */
       fclose(fp); /* so close file before returning */
     return ( (isokay?nbytes:0) ); /*back to caller with #bytes written*/
   } /* --- end-of-function type_pbmpgm() --- */
   
   
   /* ==========================================================================
  * Function: cstruct_chardef ( cp, fp, col1 )   * Function: cstruct_chardef ( cp, fp, col1 )
  * Purpose: Emit a C struct of cp on fp, starting in col1.   * Purpose: Emit a C struct of cp on fp, starting in col1.
  * --------------------------------------------------------------------------   * --------------------------------------------------------------------------
Line 2813  emit   charnum, location, name  /  hirow Line 3899  emit   charnum, location, name  /  hirow
 sprintf(field,"{ %3d,%5d,\n", cp->charnum,cp->location);  /*char#,location*/  sprintf(field,"{ %3d,%5d,\n", cp->charnum,cp->location);  /*char#,location*/
 emit_string ( fp, col1, field, "character number, location");  emit_string ( fp, col1, field, "character number, location");
 /* --- toprow, topleftcol,   botrow, botleftcol  --- */  /* --- toprow, topleftcol,   botrow, botleftcol  --- */
 sprintf(field,"  %3d,%2d,   %3d,%2d,\n", /* format... */  sprintf(field,"  %3d,%2d,  %3d,%2d,\n", /* format... */
   cp->toprow,cp->topleftcol, /* toprow, topleftcol, */    cp->toprow,cp->topleftcol, /* toprow, topleftcol, */
   cp->botrow,cp->botleftcol); /* and botrow, botleftcol */    cp->botrow,cp->botleftcol); /* and botrow, botleftcol */
 emit_string ( fp, col1, field, "topleft row,col, and botleft row,col");  emit_string ( fp, col1, field, "topleft row,col, and botleft row,col");
Line 2855  int emit_string();  /* emit a string and Line 3941  int emit_string();  /* emit a string and
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 emit width and height  emit width and height
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 sprintf(field,"{ %2d,   %3d,%2d,   %s\n", /* format width,height,pixsz */  sprintf(field,"{ %2d,  %3d,%2d,%2d, %s\n", /* format width,height,pixsz */
  rp->width,rp->height,rp->pixsz,typecast);   rp->width,rp->height,rp->format,rp->pixsz,typecast);
 emit_string ( fp, col1, field, "widthxheight, pixsz,map...");  emit_string ( fp, col1, field, "width,ht, fmt,pixsz,map...");
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 emit bitmap and closing brace, and return to caller  emit bitmap and closing brace, and return to caller
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
Line 2894  int hex_bitmap ( raster *rp, FILE *fp, i Line 3980  int hex_bitmap ( raster *rp, FILE *fp, i
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 Allocations and Declarations  Allocations and Declarations
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 int ibyte, nbytes=pixmapsz(rp); /* #bytes in raster */  int ibyte, /* pixmap[ibyte] index */
    nbytes = pixbytes(rp); /*#bytes in bitmap or .gf-formatted*/
 char stub[64]="                                ";/* col1 leading blanks */  char stub[64]="                                ";/* col1 leading blanks */
 int linewidth = 64, /* (roughly) rightmost column */  int linewidth = 64, /* (roughly) rightmost column */
  colwidth = (isstr? 4:5); /* #cols required for each byte */   colwidth = (isstr? 4:5); /* #cols required for each byte */
Line 2921  for ( ibyte=0; ibyte<nbytes; ibyte++ ) / Line 4008  for ( ibyte=0; ibyte<nbytes; ibyte++ ) /
   if ( ibyte < nbytes-1) /* not the last byte yet */    if ( ibyte < nbytes-1) /* not the last byte yet */
     {      {
     if ( !isstr ) fprintf(fp,","); /* follow hex number with comma */      if ( !isstr ) fprintf(fp,","); /* follow hex number with comma */
     if ( (ibyte+1)%ncols==0 ) /* need new line after every ncols */      if ( (ibyte+1)%ncols==0 ) { /* need new line after every ncols */
       if ( !isstr ) /* for hex numbers format ... */        if ( !isstr ) /* for hex numbers format ... */
  fprintf(fp,"\n%.*s",col1,stub); /* ...just need newline and stub */   fprintf(fp,"\n%.*s",col1,stub); /* ...just need newline and stub */
       else /* for string format... */        else /* for string format... */
  fprintf(fp,"\"\n%.*s\"",col1,stub); /* ...need closing, opening "s */   fprintf(fp,"\"\n%.*s\"",col1,stub); } /*...need closing, opening "s*/
     } /* --- end-of-if(ibyte<nbytes-1) --- */      } /* --- end-of-if(ibyte<nbytes-1) --- */
   } /* --- end-of-for(ibyte) --- */    } /* --- end-of-for(ibyte) --- */
 if ( isstr ) fprintf(fp,"\""); /* closing " after last line */  if ( isstr ) fprintf(fp,"\""); /* closing " after last line */
Line 3000  return ( 1 ); Line 4087  return ( 1 );
   
   
 /* ==========================================================================  /* ==========================================================================
    * Function: gftobitmap ( gf )
    * Purpose: convert .gf-like pixmap to bitmap image
    * --------------------------------------------------------------------------
    * Arguments: gf (I) raster * to struct in .gf-format
    * --------------------------------------------------------------------------
    * Returns: ( raster * ) image-format raster * if successful,
    * or NULL for any error.
    * --------------------------------------------------------------------------
    * Notes:     o
    * ======================================================================= */
   /* --- entry point --- */
   raster *gftobitmap ( raster *gf )
   {
   /* -------------------------------------------------------------------------
   Allocations and Declarations
   -------------------------------------------------------------------------- */
   raster *new_raster(), *rp=NULL; /* image raster retuned to caller */
   int width=0, height=0, totbits=0; /* gf->width, gf->height, #bits */
   int format=0, icount=0, ncounts=0, /*.gf format, count index, #counts*/
    ibit=0, bitval=0; /* bitmap index, bit value */
   int isrepeat = 1, /* true to process repeat counts */
    repeatcmds[2] = {255,15}, /*opcode for repeat/duplicate count*/
    nrepeats=0, irepeat=0, /* scan line repeat count,index */
    wbits = 0; /* count bits to width of scan line*/
   /* -------------------------------------------------------------------------
   initialization
   -------------------------------------------------------------------------- */
   /* --- check args --- */
   if ( gf == NULL ) goto end_of_job; /* input raster not provided */
   format = gf->format; /* 2 or 3 */
   if ( format!=2 && format!=3 ) goto end_of_job; /* invalid raster format */
   ncounts = gf->pixsz; /*pixsz is really #counts in pixmap*/
   /* --- allocate output raster with proper dimensions for bitmap --- */
   width=gf->width;  height=gf->height; /* dimensions of raster */
   if ( (rp = new_raster(width,height,1)) /* allocate new raster and bitmap */
   ==   NULL ) goto end_of_job; /* quit if failed to allocate */
   totbits = width*height; /* total #bits in image */
   /* -------------------------------------------------------------------------
   fill bitmap
   -------------------------------------------------------------------------- */
   for ( icount=0,bitval=0; icount<ncounts; icount++ )
     {
     int nbits = (int)(getbyfmt(format,gf->pixmap,icount)); /*#bits to set*/
     if ( isrepeat /* we're proxessing repeat counts */
     &&   nbits == repeatcmds[format-2] ) { /* and repeat opcode found */
      if ( nrepeats == 0 ) /* recursive repeat is error */
       { nrepeats = (int)(getbyfmt(format,gf->pixmap,icount+1));/*repeat count*/
         nbits = (int)(getbyfmt(format,gf->pixmap,icount+2)); /*#bits to set*/
         icount += 2; } /* bump byte/nibble count */
      else /* some internal error occurred */
       if ( msgfp!=NULL && msglevel>=1 ) /* report error */
        fprintf(msgfp,"gftobitmap> found embedded repeat command\n"); }
     if ( 0 )
       fprintf(stdout,
       "gftobitmap> icount=%d bitval=%d nbits=%d ibit=%d totbits=%d\n",
       icount,bitval,nbits,ibit,totbits);
     for ( ; nbits>0; nbits-- ) /* count down */
       { if ( ibit >= totbits ) goto end_of_job; /* overflow check */
         for ( irepeat=0; irepeat<=nrepeats; irepeat++ )
          if ( bitval == 1 ) /* set pixel */
    { setlongbit(rp->pixmap,(ibit+irepeat*width)); }
          else /* clear pixel */
    { unsetlongbit(rp->pixmap,(ibit+irepeat*width)); }
         if ( nrepeats > 0 ) wbits++; /* count another repeated bit */
         ibit++; } /* bump bit index */
     bitval = 1-bitval; /* flip bit value */
     if ( wbits >= width ) { /* completed repeats */
      ibit += nrepeats*width; /*bump bit count past repeated scans*/
      if ( wbits > width ) /* out-of alignment error */
       if ( msgfp!=NULL && msglevel>=1 ) /* report error */
        fprintf(msgfp,"gftobitmap> width=%d wbits=%d\n",width,wbits);
      wbits = nrepeats = 0; } /* reset repeat counts */
     } /* --- end-of-for(icount) --- */
   end_of_job:
     return ( rp ); /* back to caller with image */
   } /* --- end-of-function gftobitmap() --- */
   
   
   /* ==========================================================================
  * Function: get_symdef ( symbol )   * Function: get_symdef ( symbol )
  * Purpose: returns mathchardef struct for symbol   * Purpose: returns mathchardef struct for symbol
  * --------------------------------------------------------------------------   * --------------------------------------------------------------------------
Line 3024  mathchardef *get_symdef ( char *symbol ) Line 4190  mathchardef *get_symdef ( char *symbol )
 Allocations and Declarations  Allocations and Declarations
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 mathchardef *symdefs = symtable; /* table of mathchardefs */  mathchardef *symdefs = symtable; /* table of mathchardefs */
   int ligdef=0, get_ligature(); /* or we may have a ligature */
 int idef = 0, /* symdefs[] index */  int idef = 0, /* symdefs[] index */
  bestdef = (-9999); /*index of shortest matching symdef*/   bestdef = (-9999); /*index of shortest matching symdef*/
 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 */
 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 --- */
  {"\\int", "\\Bigint"},   {"\\int", "\\Bigint"},
  {"\\oint", "\\Bigoint"},   {"\\oint", "\\Bigoint"},
  {"\\sum", "\\Bigsum"},   {"\\sum", "\\Bigsum"},
  {"\\prod", "\\Bigprod"},   {"\\prod", "\\Bigprod"},
  {"\\coprod", "\\Bigcoprod"},   {"\\coprod", "\\Bigcoprod"},
  {"\\cup", "\\Bigcup"},   /* --- must be 'big' when related to similar binary operators --- */
  {"\\sqcup", "\\Bigsqcup"},   {"\\bigcup", "\\Bigcup"},
  {"\\cap", "\\Bigcap"},   {"\\bigsqcup", "\\Bigsqcup"},
  {"\\sqcap", "\\sqcap"}, /* don't have \Bigsqcap */   {"\\bigcap", "\\Bigcap"},
  {"\\odot", "\\Bigodot"},   /*{"\\bigsqcap", "\\sqcap"},*/ /* don't have \Bigsqcap */
  {"\\oplus", "\\Bigoplus"},   {"\\bigodot", "\\Bigodot"},
  {"\\otimes", "\\Bigotimes"},   {"\\bigoplus", "\\Bigoplus"},
  {"\\uplus", "\\Biguplus"},   {"\\bigominus", "\\ominus"},
  {"\\wedge", "\\Bigwedge"},   {"\\bigotimes", "\\Bigotimes"},
  {"\\vee", "\\Bigvee"},   {"\\bigoslash", "\\oslash"},
    {"\\biguplus", "\\Biguplus"},
    {"\\bigwedge", "\\Bigwedge"},
    {"\\bigvee", "\\Bigvee"},
  {NULL, NULL} };   {NULL, NULL} };
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
   First check for ligature
   -------------------------------------------------------------------------- */
   isligature = 0; /* init signal for no ligature */
   if ( family == CYR10 ) /*only check for cyrillic ligatures*/
    if ( (ligdef=get_ligature(subexprptr,family)) /* check for ligature */
    >=    0  ) /* found a ligature */
     { bestdef = ligdef; /* set bestdef for ligature */
       isligature = 1; /* signal we found a ligature */
       goto end_of_job; } /* so just give it to caller */
   /* -------------------------------------------------------------------------
 If in \displaystyle mode, first xlate int to Bigint, etc.  If in \displaystyle mode, first xlate int to Bigint, etc.
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 if ( isdisplaystyle > 1 ) /* we're in \displaystyle mode */  if ( isdisplaystyle > 1 ) /* we're in \displaystyle mode */
Line 3070  for ( idef=0; ;idef++ )   /* until trail Line 4253  for ( idef=0; ;idef++ )   /* until trail
   if ( symdefs[idef].symbol == NULL ) break; /* reached end-of-table */    if ( symdefs[idef].symbol == NULL ) break; /* reached end-of-table */
   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 ( symdefs[idef].handler != NULL /* mode irrelevant for directives */       if ( (fontnum==0||family==CYR10) /* mathmode, so check every match */
      ||  istext==0 /* mathmode, so use first match */       || (1 && symdefs[idef].handler!=NULL) /* or check every directive */
      || (istext==1 && symdefs[idef].family==CMR10)   /*textmode && rm text*/       || (1 && istextmode && slashsym) /*text mode and \backslashed symbol*/
      || (istext==2 && symdefs[idef].family==CMMI10)  /*textmode && it text*/       || (0 && istextmode && (!alphasym /* text mode and not alpha symbol */
      || (istext==3 && symdefs[idef].family==BBOLD10) /*textmode && bb text*/   || symdefs[idef].handler!=NULL))   /* or text mode and directive */
      || (istext!=3 && !alphasym) ) /* not bb and not alpha */       || (symdefs[idef].family==family /* have correct family */
    && symdefs[idef].handler==NULL) )  /* and not a handler collision */
   #if 0
        || (fontnum==1 && symdefs[idef].family==CMR10)   /*textmode && rm text*/
        || (fontnum==2 && symdefs[idef].family==CMMI10)  /*textmode && it text*/
        || (fontnum==3 && symdefs[idef].family==BBOLD10  /*textmode && bb text*/
    && symdefs[idef].handler==NULL)
        || (fontnum==4 && symdefs[idef].family==CMMIB10  /*textmode && bf text*/
    && symdefs[idef].handler==NULL) )
   #endif
       if ( (deflen=strlen(symdefs[idef].symbol)) < minlen ) /*new best match*/        if ( (deflen=strlen(symdefs[idef].symbol)) < minlen ) /*new best match*/
  { bestdef = idef; /* save index of new best match */   { bestdef = idef; /* save index of new best match */
   if ( (minlen = deflen) /* and save its len for next test */    if ( (minlen = deflen) /* and save its len for next test */
   ==  symlen ) break; } /*perfect match, so return with it*/    ==  symlen ) break; } /*perfect match, so return with it*/
 if ( bestdef < 0 ) /* failed to look up symbol */  if ( bestdef < 0 ) /* failed to look up symbol */
   if ( istext != 0 ) /* we're in a restricted font mode */    if ( fontnum != 0 ) /* we're in a restricted font mode */
     { int wastext = istext; /* save current mode */      { int oldfontnum = fontnum; /* save current font family */
       mathchardef *symdef = NULL; /* lookup result with istext=0 */        mathchardef *symdef = NULL; /* lookup result with fontnum=0 */
       istext = 0; /*try to look up symbol in any font*/        fontnum = 0; /*try to look up symbol in any font*/
       symdef = get_symdef(symbol); /* repeat lookup with istext=0 */        symdef = get_symdef(symbol); /* repeat lookup with fontnum=0 */
       istext = wastext; /* reset font mode */        fontnum = oldfontnum; /* reset font family */
       return symdef; } /* caller gets istext=0 lookup */        return symdef; } /* caller gets fontnum=0 lookup */
 if ( msgfp!=NULL && msglevel>=999 ) /* debugging output */  end_of_job:
   { fprintf(msgfp,"get_symdefs> symbol=%s matches symtable[%d]=%s\n",   if ( msgfp!=NULL && msglevel>=999 ) /* debugging output */
     symbol,bestdef,(bestdef<0?"NotFound":symdefs[bestdef].symbol));    { fprintf(msgfp,
       "get_symdef> symbol=%s matches symtable[%d]=%s (isligature=%d)\n",
       symbol,bestdef,(bestdef<0?"NotFound":symdefs[bestdef].symbol),isligature);
     fflush(msgfp); }      fflush(msgfp); }
 return ( (bestdef<0? NULL : &(symdefs[bestdef])) ); /*NULL or best symdef[]*/   return ( (bestdef<0? NULL : &(symdefs[bestdef])) );/*NULL or best symdef[]*/
 } /* --- end-of-function get_symdef() --- */  } /* --- end-of-function get_symdef() --- */
   
   
 /* ==========================================================================  /* ==========================================================================
    * Function: get_ligature ( expression, family )
    * Purpose: returns symtable[] index for ligature
    * --------------------------------------------------------------------------
    * Arguments: expression (I) char *  containing ligature
    * whose corresponding mathchardef is wanted
    * family (I) int containing NOVALUE for any family,
    * or, e.g., CYR10 for cyrillic, etc.
    * --------------------------------------------------------------------------
    * Returns: ( int ) symtable[] index defining ligature,
    * or -9999 if no ligature found or for any error
    * --------------------------------------------------------------------------
    * Notes:     o
    * ======================================================================= */
   /* --- entry point --- */
   int get_ligature ( char *expression, int family )
   {
   /* -------------------------------------------------------------------------
   Allocations and Declarations
   -------------------------------------------------------------------------- */
   mathchardef *symdefs = symtable; /* table of mathchardefs */
   char *ligature = expression /*- 1*/, /* expression ptr */
    *symbol = NULL; /* symdefs[idef].symbol */
   int liglen = strlen(ligature); /* #chars remaining in expression */
   int iscyrfam = (family==CYR10); /* true for cyrillic families */
   int idef = 0, /* symdefs[] index */
    bestdef = (-9999), /*index of longest matching symdef*/
    maxlen=(-9999); /*length of longest matching symdef*/
   /* -------------------------------------------------------------------------
   search symdefs[] in order for first occurrence of symbol
   -------------------------------------------------------------------------- */
   if ( !isstring ) { /* no ligatures in "string" mode */
    for ( idef=0; ;idef++ ) /* until trailer record found */
     if ( (symbol=symdefs[idef].symbol) == NULL ) break; /* end-of-table */
     else { /* check against caller's ligature */
       int symlen = strlen(symbol); /* #chars in symbol */
       if ( ( symlen>1 || iscyrfam ) /*ligature >1 char long or cyrillic*/
       &&   symlen <= liglen /* and enough remaining chars */
       &&   ( *symbol!='\\' || iscyrfam ) /* not escaped or cyrillic */
       &&   symdefs[idef].handler == NULL ) /* and not a handler */
        if ( strncmp(ligature,symbol,symlen) == 0 ) /* found match */
         if ( family < 0 /* no family specifies */
         ||   symdefs[idef].family == family ) /* or have correct family */
          if ( symlen > maxlen ) /* new longest ligature */
    { bestdef = idef; /* save index of new best match */
     maxlen = symlen; } /* and save its len for next test */
       } /* --- end-of-if/else(symbol==NULL) --- */
    if ( msgfp!=NULL && msglevel>=999 ) /* debugging output */
     { fprintf(msgfp,"get_ligature> ligature=%.4s matches symtable[%d]=%s\n",
       ligature,bestdef,(bestdef<0?"NotFound":symdefs[bestdef].symbol));
       fflush(msgfp); }
    } /* --- end-of-if(!isstring) --- */
   return ( bestdef ); /* -9999 or index of best symdef[] */
   } /* --- end-of-function get_ligature --- */
   
   
   /* ==========================================================================
  * Function: get_chardef ( symdef, size )   * Function: get_chardef ( symdef, size )
  * Purpose: returns chardef ptr containing data for symdef at given size   * Purpose: returns chardef ptr containing data for symdef at given size
  * --------------------------------------------------------------------------   * --------------------------------------------------------------------------
  * Arguments: symdef (I) mathchardef *  corresponding to symbol   * Arguments: symdef (I) mathchardef *  corresponding to symbol
  * whose corresponding chardef is wanted   * whose corresponding chardef is wanted
  * size (I) int containing 0-4 for desired size   * size (I) int containing 0-5 for desired size
  * --------------------------------------------------------------------------   * --------------------------------------------------------------------------
  * Returns: ( chardef * ) pointer to struct defining symbol at size,   * Returns: ( chardef * ) pointer to struct defining symbol at size,
  * or NULL for any error   * or NULL for any error
Line 3129  char *symptr = NULL;   /* look for 1st a Line 4379  char *symptr = NULL;   /* look for 1st a
 initialization  initialization
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 /* --- check symdef --- */  /* --- check symdef --- */
 if ( symdef == NULL ) return ( NULL ); /* get_symdef() probably failed */  if ( symdef == NULL ) goto end_of_job; /* get_symdef() probably failed */
 /* --- get local copy of indexes from symdef --- */  /* --- get local copy of indexes from symdef --- */
 family = symdef->family; /* font family containing symbol */  family = symdef->family; /* font family containing symbol */
 charnum = symdef->charnum; /* char# of symbol within font */  charnum = symdef->charnum; /* char# of symbol within font */
Line 3161  find font family in table of fonts[] Line 4411  find font family in table of fonts[]
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 /* --- look up font family --- */  /* --- look up font family --- */
 for ( ifont=0; ;ifont++ ) /* until trailer record found */  for ( ifont=0; ;ifont++ ) /* until trailer record found */
   if ( fonts[ifont].family < 0 ) return ( NULL ); /* error, no such family */    if ( fonts[ifont].family < 0 ) { /* error, no such family */
       if ( msgfp!=NULL && msglevel>=99 ) { /* emit error */
        fprintf(msgfp,"get_chardef> failed to find font family %d\n",
        family); fflush(msgfp); }
       goto end_of_job; } /* quit if can't find font family*/
   else if ( fonts[ifont].family == family ) break; /* found font family */    else if ( fonts[ifont].family == family ) break; /* found font family */
 /* --- get local copy of table for this family by size --- */  /* --- get local copy of table for this family by size --- */
 fontdef = fonts[ifont].fontdef; /* font by size */  fontdef = fonts[ifont].fontdef; /* font by size */
Line 3173  while ( 1 )    /* find size or closest a Line 4427  while ( 1 )    /* find size or closest a
   if ( fontdef[size] != NULL ) break; /* found available size */    if ( fontdef[size] != NULL ) break; /* found available size */
   else /* adjust size closer to normal */    else /* adjust size closer to normal */
     if ( size == NORMALSIZE /* already normal so no more sizes,*/      if ( size == NORMALSIZE /* already normal so no more sizes,*/
     || sizeinc == 0 ) return ( NULL); /* or must be supersampling */      || sizeinc == 0 ) { /* or must be supersampling */
         if ( msgfp!=NULL && msglevel>=99 ) { /* emit error */
    fprintf(msgfp,"get_chardef> failed to find font size %d\n",
    size); fflush(msgfp); }
         goto end_of_job; } /* quit if can't find desired size */
     else /*bump size 1 closer to NORMALSIZE*/      else /*bump size 1 closer to NORMALSIZE*/
       size += sizeinc; /* see if adjusted size available */        size += sizeinc; /* see if adjusted size available */
 /* --- ptr to chardef struct --- */  /* --- ptr to chardef struct --- */
Line 3188  if ( family == CMEX10 )   /* cmex10 need Line 4446  if ( family == CMEX10 )   /* cmex10 need
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 return subraster containing chardef data for symbol in requested size  return subraster containing chardef data for symbol in requested size
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 return ( gfdata ); /*ptr to chardef for symbol in size*/  end_of_job:
    if ( msgfp!=NULL && msglevel>=999 )
     { if (symdef == NULL) fprintf(msgfp,"get_chardef> input symdef==NULL\n");
       else
        fprintf(msgfp,"get_chardef> requested symbol=\"%s\" size=%d  %s\n",
        symdef->symbol,size,(gfdata==NULL?"FAILED":"Succeeded"));
       fflush(msgfp); }
    return ( gfdata ); /*ptr to chardef for symbol in size*/
 } /* --- end-of-function get_chardef() --- */  } /* --- end-of-function get_chardef() --- */
   
   
Line 3197  return ( gfdata );   /*ptr to chardef fo Line 4462  return ( gfdata );   /*ptr to chardef fo
  * Purpose: returns new subraster ptr containing   * Purpose: returns new subraster ptr containing
  * data for symdef at given size   * data for symdef at given size
  * --------------------------------------------------------------------------   * --------------------------------------------------------------------------
  * Arguments: symdef (I) mathchardef *  corresponding to symbol   * Arguments: symdef (I) mathchardef *  corresponding to symbol whose
  * whose corresponding chardef is wanted   * corresponding chardef subraster is wanted
  * size (I) int containing 0-4 for desired size   * size (I) int containing 0-5 for desired size
  * --------------------------------------------------------------------------   * --------------------------------------------------------------------------
  * Returns: ( subraster * ) pointer to struct defining symbol at size,   * Returns: ( subraster * ) pointer to struct defining symbol at size,
  * or NULL for any error   * or NULL for any error
Line 3215  Allocations and Declarations Line 4480  Allocations and Declarations
 chardef *get_chardef(), *gfdata=NULL; /* chardef struct for symdef,size */  chardef *get_chardef(), *gfdata=NULL; /* chardef struct for symdef,size */
 int get_baseline(); /* baseline of gfdata */  int get_baseline(); /* baseline of gfdata */
 subraster *new_subraster(), *sp=NULL; /* subraster containing gfdata */  subraster *new_subraster(), *sp=NULL; /* subraster containing gfdata */
   raster *bitmaprp=NULL, *gftobitmap(); /* convert .gf-format to bitmap */
   int delete_subraster(); /* in case gftobitmap() fails */
 int aasupsamp(), /*antialias char with supersampling*/  int aasupsamp(), /*antialias char with supersampling*/
  grayscale=256; /* aasupersamp() parameters */   grayscale=256; /* aasupersamp() parameters */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
Line 3225  if ( (gfdata=get_chardef(symdef,size)) / Line 4492  if ( (gfdata=get_chardef(symdef,size)) /
  if ( (sp=new_subraster(0,0,0)) /* allocate subraster "envelope" */   if ( (sp=new_subraster(0,0,0)) /* allocate subraster "envelope" */
  !=   NULL ) /* and check that we succeeded */   !=   NULL ) /* and check that we succeeded */
   {    {
   sp->type = CHARASTER; /* static character raster */    raster *image = &(gfdata->image); /* ptr to chardef's bitmap or .gf */
     int format = image->format; /* 1=bitmap, else .gf */
   sp->symdef = symdef; /* replace NULL with caller's arg */    sp->symdef = symdef; /* replace NULL with caller's arg */
   sp->size = size; /*replace default with caller's size*/    sp->size = size; /*replace default with caller's size*/
   sp->baseline = get_baseline(gfdata); /* get baseline of character */    sp->baseline = get_baseline(gfdata); /* get baseline of character */
   sp->image    = &(gfdata->image); /* store ptr to its bitmap */    if ( format == 1 ) /* already a bitmap */
      { sp->type = CHARASTER; /* static char raster */
        sp->image = image; } /* store ptr to its bitmap */
     else /* need to convert .gf-to-bitmap */
      if ( (bitmaprp = gftobitmap(image)) /* convert */
      !=   (raster *)NULL ) /* successful */
       { sp->type = IMAGERASTER; /* allocated raster will be freed */
         sp->image = bitmaprp; } /* store ptr to converted bitmap */
      else /* conversion failed */
       { delete_subraster(sp); /* free unneeded subraster */
         sp = (subraster *)NULL; /* signal error to caller */
         goto end_of_job; } /* quit */
   if ( issupersampling ) /* antialias character right here */    if ( issupersampling ) /* antialias character right here */
     {      {
     raster *aa = NULL; /* antialiased char raster */      raster *aa = NULL; /* antialiased char raster */
Line 3245  if ( (gfdata=get_chardef(symdef,size)) / Line 4524  if ( (gfdata=get_chardef(symdef,size)) /
  sp->type = IMAGERASTER; } /* character is an image raster */   sp->type = IMAGERASTER; } /* character is an image raster */
     } /* --- end-of-if(issupersampling) --- */      } /* --- end-of-if(issupersampling) --- */
   } /* --- end-of-if(sp!=NULL) --- */    } /* --- end-of-if(sp!=NULL) --- */
 if ( msgfp!=NULL && msglevel>=999 )  end_of_job:
   { fprintf(msgfp,"get_charsubraster> requested symbol=\"%s\" baseline=%d\n",   if ( msgfp!=NULL && msglevel>=999 )
     symdef->symbol, (sp==NULL?0:sp->baseline)); fflush(msgfp); }    { fprintf(msgfp,"get_charsubraster> requested symbol=\"%s\" baseline=%d"
       " %s %s\n", symdef->symbol, (sp==NULL?0:sp->baseline),
       (sp==NULL?"FAILED":"Succeeded"), (gfdata==NULL?"(gfdata=NULL)":" "));
       fflush(msgfp); }
 return ( sp ); /* back to caller */  return ( sp ); /* back to caller */
 } /* --- end-of-function get_charsubraster() --- */  } /* --- end-of-function get_charsubraster() --- */
   
   
 /* ==========================================================================  /* ==========================================================================
    * Function: get_symsubraster ( symbol, size )
    * Purpose: returns new subraster ptr containing
    * data for symbol at given size
    * --------------------------------------------------------------------------
    * Arguments: symbol (I) char *  corresponding to symbol
    * whose corresponding subraster is wanted
    * size (I) int containing 0-5 for desired size
    * --------------------------------------------------------------------------
    * Returns: ( subraster * ) pointer to struct defining symbol at size,
    * or NULL for any error
    * --------------------------------------------------------------------------
    * Notes:     o just combines get_symdef() and get_charsubraster()
    * ======================================================================= */
   /* --- entry point --- */
   subraster *get_symsubraster ( char *symbol, int size )
   {
   /* -------------------------------------------------------------------------
   Allocations and Declarations
   -------------------------------------------------------------------------- */
   subraster *sp=NULL, *get_charsubraster(); /* subraster containing gfdata */
   mathchardef *symdef=NULL, *get_symdef(); /* mathchardef lookup for symbol */
   /* -------------------------------------------------------------------------
   look up mathchardef for symbol
   -------------------------------------------------------------------------- */
   if ( symbol != NULL ) /* user supplied input symbol */
     symdef = get_symdef(symbol); /*look up corresponding mathchardef*/
   /* -------------------------------------------------------------------------
   look up chardef for mathchardef and wrap a subraster structure around data
   -------------------------------------------------------------------------- */
   if ( symdef != NULL ) /* lookup succeeded */
     sp = get_charsubraster(symdef,size); /* so get symbol data in subraster */
   return ( sp ); /* back to caller with sp or NULL */
   } /* --- end-of-function get_symsubraster() --- */
   
   
   /* ==========================================================================
  * Function: get_baseline ( gfdata )   * Function: get_baseline ( gfdata )
  * Purpose: returns baseline for a chardef struct   * Purpose: returns baseline for a chardef struct
  * --------------------------------------------------------------------------   * --------------------------------------------------------------------------
Line 3331  int defheight, bestheight=9999, /* heigh Line 4649  int defheight, bestheight=9999, /* heigh
 int iswidth = 0; /* true if best-fit width desired */  int iswidth = 0; /* true if best-fit width desired */
 int isunesc = 0, /* true if leading escape removed */  int isunesc = 0, /* true if leading escape removed */
  issq=0, isoint=0; /* true for \sqcup,etc, \oint,etc */   issq=0, isoint=0; /* true for \sqcup,etc, \oint,etc */
   int iscurly = 0; /* true for StMary's curly symbols */
 char *bigint="bigint", *bigoint="bigoint"; /* substitutes for int, oint */  char *bigint="bigint", *bigoint="bigoint"; /* substitutes for int, oint */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 determine if searching height or width, and search symdefs[] for best-fit  determine if searching height or width, and search symdefs[] for best-fit
Line 3338  determine if searching height or width, Line 4657  determine if searching height or width,
 /* --- arg checks --- */  /* --- arg checks --- */
 if ( symlen < 1 ) return (sp); /* no input symbol suplied */  if ( symlen < 1 ) return (sp); /* no input symbol suplied */
 if ( strcmp(symbol,"e") == 0 ) return(sp); /* e causes segfault??? */  if ( strcmp(symbol,"e") == 0 ) return(sp); /* e causes segfault??? */
   if ( strstr(symbol,"curly") != NULL ) iscurly=1; /* user wants curly delim */
 /* --- ignore leading escapes for CMEX10 --- */  /* --- ignore leading escapes for CMEX10 --- */
 if ( 1 ) /* ignore leading escape */  if ( 1 ) /* ignore leading escape */
  if ( (family==CMEX10 || family==CMSYEX) ) { /* for CMEX10 or CMSYEX */   if ( (family==CMEX10 || family==CMSYEX) ) { /* for CMEX10 or CMSYEX */
Line 3367  for ( idef=0; ;idef++ )   /* until trail Line 4687  for ( idef=0; ;idef++ )   /* until trail
  if ( defsym == NULL ) break; /* reached end-of-table */   if ( defsym == NULL ) break; /* reached end-of-table */
  else /* check against caller's symbol */   else /* check against caller's symbol */
   if ( family<0 || deffam == family /* if explicitly in caller's family*/    if ( family<0 || deffam == family /* if explicitly in caller's family*/
   ||  (family==CMSYEX && (deffam==CMSY10||deffam==CMEX10)) )    ||  (family==CMSYEX && (deffam==CMSY10||deffam==CMEX10||deffam==STMARY10)) )
     {      {
     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"*/
      &&   (issq || strstr(lcsymbol,"sq")==NULL) ) /* skip unwanted "sq" */       &&   (issq || strstr(lcsymbol,"sq")==NULL) ) /* skip unwanted "sq" */
       if ( (deffam == CMSY10 ? /* CMSY10 or not CMSY10 */        if ( ( deffam == CMSY10 ? /* CMSY10 or not CMSY10 */
   symptr == lcsymbol /* caller's sym is a prefix */    symptr == lcsymbol /* caller's sym is a prefix */
           && deflen == symlen: /* and same length */            && deflen == symlen: /* and same length */
   symptr == lcsymbol /* caller's sym is a prefix */    (iscurly || strstr(lcsymbol,"curly")==NULL) &&/*not unwanted curly*/
           || symptr == lcsymbol+deflen-symlen) ) /* or a suffix */    (symptr == lcsymbol /* caller's sym is a prefix */
             || symptr == lcsymbol+deflen-symlen) ) ) /* or a suffix */
        for ( size=0; size<=LARGESTSIZE; size++ ) /* check all font sizes */         for ( size=0; size<=LARGESTSIZE; size++ ) /* check all font sizes */
  if ( (gfdata=get_chardef(&(symdefs[idef]),size)) != NULL ) /*got one*/   if ( (gfdata=get_chardef(&(symdefs[idef]),size)) != NULL ) /*got one*/
   { defheight = gfdata->image.height; /* height of this character */    { defheight = gfdata->image.height; /* height of this character */
Line 3404  construct subraster for best fit charact Line 4725  construct subraster for best fit charact
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 if ( bestdef >= 0 ) /* found a best fit for caller */  if ( bestdef >= 0 ) /* found a best fit for caller */
   sp = get_charsubraster(&(symdefs[bestdef]),bestsize); /* best subraster */    sp = get_charsubraster(&(symdefs[bestdef]),bestsize); /* best subraster */
 if ( sp==NULL && height-bigheight>5 ) /* try to construct delim */  if ( (sp==NULL && height-bigheight>5) /* try to construct delim */
   ||   bigdef < 0 ) /* delim not in font tables */
   sp = make_delim(symbol,(iswidth?-height:height)); /* try to build delim */    sp = make_delim(symbol,(iswidth?-height:height)); /* try to build delim */
 if ( sp==NULL && bigdef>=0 ) /* just give biggest to caller */  if ( sp==NULL && bigdef>=0 ) /* just give biggest to caller */
   sp = get_charsubraster(&(symdefs[bigdef]),bigsize); /* biggest subraster */    sp = get_charsubraster(&(symdefs[bigdef]),bigsize); /* biggest subraster */
   if ( msgfp!=NULL && msglevel>=99 )
       fprintf(msgfp,"get_delim> symbol=%.50s, height=%d family=%d isokay=%s\n",
       (symbol==NULL?"null":symbol),height,family,(sp==NULL?"fail":"success"));
 return ( sp );  return ( sp );
 } /* --- end-of-function get_delim() --- */  } /* --- end-of-function get_delim() --- */
   
Line 3436  Allocations and Declarations Line 4761  Allocations and Declarations
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 subraster *sp = (subraster *)NULL, /* subraster returned to caller */  subraster *sp = (subraster *)NULL, /* subraster returned to caller */
  *new_subraster(); /* allocate subraster */   *new_subraster(); /* allocate subraster */
   subraster *get_symsubraster(), /* look up delim pieces in cmex10 */
    *symtop=NULL, *symbot=NULL, *symmid=NULL, *symbar=NULL, /* pieces */
    *topsym=NULL, *botsym=NULL, *midsym=NULL, *barsym=NULL, /* +filler */
    *rastack(), *rastcat(); /* stack pieces, concat filler */
   int isdrawparen = 0; /*1=draw paren, 0=build from pieces*/
 raster *rasp = (raster *)NULL; /* sp->image */  raster *rasp = (raster *)NULL; /* sp->image */
 int isokay=0, delete_subraster(); /* set true if delimiter drawn ok */  int isokay=0, delete_subraster(); /* set true if delimiter drawn ok */
 int pixsz = 1; /* pixels are one bit each */  int pixsz = 1, /* pixels are one bit each */
    symsize = 0; /* size arg for get_symsubraster() */
 int thickness = 1; /* drawn lines are one pixel thick */  int thickness = 1; /* drawn lines are one pixel thick */
 int aspectratio = 8; /* default height/width for parens */  int aspectratio = 8; /* default height/width for parens */
 int iswidth = 0, /*true if width specified by height*/  int iswidth = 0, /*true if width specified by height*/
  width = height; /* #pixels width (e.g., of ellipse)*/   width = height; /* #pixels width (e.g., of ellipse)*/
 char *lp=NULL,  *rp=NULL, *strchr(), /* check symbol for left or right */  char *lp=NULL,  *rp=NULL, /* check symbol for left or right */
  *lp2=NULL, *rp2=NULL; /* synonym for lp,rp */   *lp2=NULL, *rp2=NULL, /* synonym for lp,rp */
    *lp3=NULL, *rp3=NULL, /* synonym for lp,rp */
    *lp4=NULL, *rp4=NULL; /* synonym for lp,rp */
 int circle_raster(), /* ellipse for ()'s in sp->image */  int circle_raster(), /* ellipse for ()'s in sp->image */
  rule_rsater(), /* horizontal or vertical lines */   rule_rsater(), /* horizontal or vertical lines */
  line_raster(); /* line between two points */   line_raster(); /* line between two points */
   subraster *uparrow_subraster(); /* up/down arrows */
   int isprealloc = 1; /*pre-alloc subraster, except arrow*/
   int oldsmashmargin = smashmargin, /* save original smashmargin */
    wasnocatspace = isnocatspace; /* save original isnocatspace */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 initialization  initialization
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
Line 3460  if ( height < 3 ) goto end_of_job; /* to Line 4797  if ( height < 3 ) goto end_of_job; /* to
 if ( iswidth ) height =  (width+(aspectratio+1)/2)/aspectratio;  if ( iswidth ) height =  (width+(aspectratio+1)/2)/aspectratio;
 else            width = (height+(aspectratio+1)/2)/aspectratio;  else            width = (height+(aspectratio+1)/2)/aspectratio;
 if ( strchr(symbol,'=') != NULL /* left or right || bracket wanted */  if ( strchr(symbol,'=') != NULL /* left or right || bracket wanted */
 ||   strstr(symbol,"\\|") != NULL ) /* same || in standard tex notation*/  ||   strstr(symbol,"\\|") != NULL /* same || in standard tex notation*/
   width = max2(width,5); /* need space between two |'s */  ||   strstr(symbol,"dbl") != NULL ) /* semantic bracket with ||'s */
     width = max2(width,6); /* need space between two |'s */
 if ( width < 2 ) width=2; /* set min width */  if ( width < 2 ) width=2; /* set min width */
 if ( strchr(symbol,'(') != NULL /* if left ( */  if ( strchr(symbol,'(') != NULL /* if left ( */
 ||   strchr(symbol,')') != NULL ) /* or right ) paren wanted */  ||   strchr(symbol,')') != NULL ) /* or right ) paren wanted */
   width = (3*width)/2; /* adjust width */    { width = (3*width)/2; /* adjust width */
       if ( !isdrawparen ) isprealloc=0; } /* don't prealloc if building */
   if ( strchr(symbol,'/') != NULL /* left / */
   ||   strstr(symbol,"\\\\") != NULL /* or \\ for right \ */
   ||   strstr(symbol,"backsl") != NULL ) /* or \backslash for \ */
     width = max2(height/3,5);
   if ( strstr(symbol,"arrow") != NULL ) /* arrow wanted */
     { width = min2(height/3,20); /* adjust width */
       isprealloc = 0; } /* don't preallocate subraster */
   if ( strchr(symbol,'{') != NULL /* if left { */
   ||   strchr(symbol,'}') != NULL ) /* or right } brace wanted */
     { isprealloc = 0; } /* don't preallocate */
 /* --- allocate and initialize subraster for constructed delimiter --- */  /* --- allocate and initialize subraster for constructed delimiter --- */
 if ( (sp=new_subraster(width,height,pixsz)) /* allocate new subraster */  if ( isprealloc ) /* pre-allocation wanted */
 ==   NULL )  goto end_of_job; /* quit if failed */   { if ( (sp=new_subraster(width,height,pixsz)) /* allocate new subraster */
 /* --- initialize delimiter subraster parameters --- */     ==   NULL )  goto end_of_job; /* quit if failed */
 sp->type = IMAGERASTER; /* image */     /* --- initialize delimiter subraster parameters --- */
 sp->symdef = NULL; /* not applicable for image */     sp->type = IMAGERASTER; /* image */
 sp->baseline = height/2 + 2; /* is a little above center good? */     sp->symdef = NULL; /* not applicable for image */
 sp->size = NORMALSIZE; /* size (probably unneeded) */     sp->baseline = height/2 + 2; /* is a little above center good? */
 rasp = sp->image; /* pointer to image in subraster */     sp->size = NORMALSIZE; /* size (probably unneeded) */
      rasp = sp->image; } /* pointer to image in subraster */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 ( ) parens  ( ) parens
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 if ( (lp=strchr(symbol,'(')) != NULL /* left ( paren wanted */  if ( (lp=strchr(symbol,'(')) != NULL /* left ( paren wanted */
 ||   (rp=strchr(symbol,')')) != NULL ) /* right ) paren wanted */  ||   (rp=strchr(symbol,')')) != NULL ) /* right ) paren wanted */
   {    {
   int mywidth = min2(width,20); /* max width for ()'s */    if ( isdrawparen ) { /* draw the paren */
   circle_raster ( rasp, /* embedded raster image */     int mywidth = min2(width,20); /* max width for ()'s */
      circle_raster ( rasp, /* embedded raster image */
  0, 0, /* row0,col0 are upper-left corner */   0, 0, /* row0,col0 are upper-left corner */
  height-1, mywidth-1, /* row1,col1 are lower-right */   height-1, mywidth-1, /* row1,col1 are lower-right */
  thickness, /* line thickness is 1 pixel */   thickness, /* line thickness is 1 pixel */
  (rp==NULL?"23":"41") ); /* "1234" quadrants to be drawn */   (rp==NULL?"23":"41") ); /* "1234" quadrants to be drawn */
   isokay = 1; /* set flag */     isokay = 1; } /* set flag */
     else {
      int isleft = (lp!=NULL?1:0); /* true for left, false for right */
      char *parentop = (isleft?"\\leftparentop":"\\rightparentop"),
    *parenbot = (isleft?"\\leftparenbot":"\\rightparenbot"),
    *parenbar = (isleft?"\\leftparenbar":"\\rightparenbar");
      int baseht=0, barht=0, /* height of base=top+bot, bar */
    ibar=0, nbars=0; /* bar index, #bars between top&bot*/
      int largestsize = min2(2,LARGESTSIZE), /* largest size for parens */
    topfill=(isleft?0:0), botfill=(isleft?0:0),
    barfill=(isleft?0:7); /* alignment fillers */
      /* --- get pieces at largest size smaller than total height --- */
      for ( symsize=largestsize; symsize>=0; symsize-- ) /*largest to smallest*/
       {
       /* --- get pieces at current test size --- */
       isokay = 1; /* check for all pieces */
       if ( (symtop=get_symsubraster(parentop,symsize)) == NULL ) isokay=0;
       if ( (symbot=get_symsubraster(parenbot,symsize)) == NULL ) isokay=0;
       if ( (symbar=get_symsubraster(parenbar,symsize)) == NULL ) isokay=0;
       /* --- check sum of pieces against total desired height --- */
       if ( isokay ) { /* all pieces retrieved */
         baseht = (symtop->image)->height + (symbot->image)->height; /*top+bot*/
         barht  = (symbar->image)->height; /* bar height */
         if ( baseht < height+5 ) break; /* largest base that's not too big */
         if ( symsize < 1 ) break; /* or smallest available base */
         } /* --- end-of-if(isokay) --- */
       /* --- free test pieces that were too big --- */
       if ( symtop != NULL ) delete_subraster(symtop); /* free top */
       if ( symbot != NULL ) delete_subraster(symbot); /* free bot */
       if ( symbar != NULL ) delete_subraster(symbar); /* free bar */
       isokay = 0; /* nothing available */
       if ( symsize < 1 ) break; /* leave isokay=0 after smallest */
       } /* --- end-of-for(symsize) --- */
      /* --- construct brace from pieces --- */
      if ( isokay ) { /* we have the pieces */
       /* --- add alignment fillers --- */
       smashmargin=0;  isnocatspace=99; /*turn off rastcat smashing,space*/
       topsym = (topfill>0?rastcat(new_subraster(topfill,1,1),symtop,3):symtop);
       botsym = (botfill>0?rastcat(new_subraster(botfill,1,1),symbot,3):symbot);
       barsym = (barfill>0?rastcat(new_subraster(barfill,1,1),symbar,3):symbar);
       smashmargin = oldsmashmargin; /* reset smashmargin */
       isnocatspace = wasnocatspace; /* reset isnocatspace */
       /* --- #bars needed between top and bot --- */
       nbars = (barht<1?0:max2(0,1+(height-baseht)/barht)); /* #bars needed */
       /* --- stack pieces --- */
       sp = topsym; /* start with top piece */
       if ( nbars > 0 ) /* need nbars between top and bot */
         for ( ibar=1; ibar<=nbars; ibar++ ) sp = rastack(barsym,sp,1,0,0,2);
       sp = rastack(botsym,sp,1,0,0,3); /* bottom below bars or middle */
       delete_subraster(barsym); /* barsym no longer needed */
       } /* --- end-of-if(isokay) --- */
      } /* --- end-of-if/else(isdrawparen) --- */
   } /* --- end-of-if(left- or right-() paren wanted) --- */    } /* --- end-of-if(left- or right-() paren wanted) --- */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
   { } braces
   -------------------------------------------------------------------------- */
   else
    if ( (lp=strchr(symbol,'{')) != NULL /* left { brace wanted */
    ||   (rp=strchr(symbol,'}')) != NULL ) /* right } brace wanted */
     {
     int isleft = (lp!=NULL?1:0); /* true for left, false for right */
     char *bracetop = (isleft?"\\leftbracetop":"\\rightbracetop"),
    *bracebot = (isleft?"\\leftbracebot":"\\rightbracebot"),
    *bracemid = (isleft?"\\leftbracemid":"\\rightbracemid"),
    *bracebar = (isleft?"\\leftbracebar":"\\rightbracebar");
     int baseht=0, barht=0, /* height of base=top+bot+mid, bar */
    ibar=0, nbars=0; /* bar index, #bars above,below mid*/
     int largestsize = min2(2,LARGESTSIZE), /* largest size for braces */
    topfill=(isleft?4:0), botfill=(isleft?4:0),
    midfill=(isleft?0:4), barfill=(isleft?4:4); /* alignment fillers */
     /* --- get pieces at largest size smaller than total height --- */
     for ( symsize=largestsize; symsize>=0; symsize-- ) /*largest to smallest*/
       {
       /* --- get pieces at current test size --- */
       isokay = 1; /* check for all pieces */
       if ( (symtop=get_symsubraster(bracetop,symsize)) == NULL ) isokay=0;
       if ( (symbot=get_symsubraster(bracebot,symsize)) == NULL ) isokay=0;
       if ( (symmid=get_symsubraster(bracemid,symsize)) == NULL ) isokay=0;
       if ( (symbar=get_symsubraster(bracebar,symsize)) == NULL ) isokay=0;
       /* --- check sum of pieces against total desired height --- */
       if ( isokay ) { /* all pieces retrieved */
         baseht = (symtop->image)->height + (symbot->image)->height
    + (symmid->image)->height; /* top+bot+mid height */
         barht = (symbar->image)->height; /* bar height */
         if ( baseht < height+5 ) break; /* largest base that's not too big */
         if ( symsize < 1 ) break; /* or smallest available base */
         } /* --- end-of-if(isokay) --- */
       /* --- free test pieces that were too big --- */
       if ( symtop != NULL ) delete_subraster(symtop); /* free top */
       if ( symbot != NULL ) delete_subraster(symbot); /* free bot */
       if ( symmid != NULL ) delete_subraster(symmid); /* free mid */
       if ( symbar != NULL ) delete_subraster(symbar); /* free bar */
       isokay = 0; /* nothing available */
       if ( symsize < 1 ) break; /* leave isokay=0 after smallest */
       } /* --- end-of-for(symsize) --- */
     /* --- construct brace from pieces --- */
     if ( isokay ) { /* we have the pieces */
       /* --- add alignment fillers --- */
       smashmargin=0;  isnocatspace=99; /*turn off rastcat smashing,space*/
       topsym = (topfill>0?rastcat(new_subraster(topfill,1,1),symtop,3):symtop);
       botsym = (botfill>0?rastcat(new_subraster(botfill,1,1),symbot,3):symbot);
       midsym = (midfill>0?rastcat(new_subraster(midfill,1,1),symmid,3):symmid);
       barsym = (barfill>0?rastcat(new_subraster(barfill,1,1),symbar,3):symbar);
       smashmargin = oldsmashmargin; /* reset smashmargin */
       isnocatspace = wasnocatspace; /* reset isnocatspace */
       /* --- #bars needed on each side of mid piece --- */
       nbars = (barht<1?0:max2(0,1+(height-baseht)/barht/2)); /*#bars per side*/
       /* --- stack pieces --- */
       sp = topsym; /* start with top piece */
       if ( nbars > 0 ) /* need nbars above middle */
         for ( ibar=1; ibar<=nbars; ibar++ ) sp = rastack(barsym,sp,1,0,0,2);
       sp = rastack(midsym,sp,1,0,0,3); /*mid after top or bars*/
       if ( nbars > 0 ) /* need nbars below middle */
         for ( ibar=1; ibar<=nbars; ibar++ ) sp = rastack(barsym,sp,1,0,0,2);
       sp = rastack(botsym,sp,1,0,0,3); /* bottom below bars or middle */
       delete_subraster(barsym); /* barsym no longer needed */
       } /* --- end-of-if(isokay) --- */
     } /* --- end-of-if(left- or right-{} brace wanted) --- */
   /* -------------------------------------------------------------------------
 [ ] brackets  [ ] brackets
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 else  else
  if ( (lp=strchr(symbol,'[')) != NULL /* left [ bracket wanted */   if ( (lp=strchr(symbol,'[')) != NULL /* left [ bracket wanted */
  ||   (rp=strchr(symbol,']')) != NULL ) /* right ] bracket wanted */   ||   (rp=strchr(symbol,']')) != NULL /* right ] bracket wanted */
    ||   (lp2=strstr(symbol,"lceil")) != NULL /* left ceiling wanted */
    ||   (rp2=strstr(symbol,"rceil")) != NULL /* right ceiling wanted */
    ||   (lp3=strstr(symbol,"lfloor")) != NULL /* left floor wanted */
    ||   (rp3=strstr(symbol,"rfloor")) != NULL /* right floor wanted */
    ||   (lp4=strstr(symbol,"llbrack")) != NULL /* left semantic bracket */
    ||   (rp4=strstr(symbol,"rrbrack")) != NULL ) /* right semantic bracket */
   {    {
   /* --- rule_raster ( rasp, top, left, width, height, type=0 ) --- */    /* --- use rule_raster ( rasp, top, left, width, height, type=0 ) --- */
   int mywidth = min2(width,12); /* max width for horizontal bars */    int mywidth = min2(width,12), /* max width for horizontal bars */
   thickness = 1; /* set lines 1 or 2 pixels thick */   wthick = 1; /* thickness of top.bottom bars */
   rule_raster(rasp, 0,0, mywidth,thickness, 0); /* top horizontal bar */    thickness = (height<25?1:2); /* set lines 1 or 2 pixels thick */
   rule_raster(rasp, height-thickness,0, mywidth,thickness, 0); /* bottom */    if ( lp2!=NULL || rp2!=NULL || lp3!=NULL || rp3 !=NULL ) /*ceil or floor*/
   if ( lp != NULL ) /* left [ bracket wanted */      wthick = thickness; /* same thickness for top/bot bar */
     if ( lp3==NULL && rp3==NULL ) /* set top bar if floor not wanted */
       rule_raster(rasp, 0,0, mywidth,wthick, 0); /* top horizontal bar */
     if ( lp2==NULL && rp2==NULL ) /* set bot bar if ceil not wanted */
       rule_raster(rasp, height-wthick,0, mywidth,thickness, 0); /* bottom */
     if ( lp!=NULL || lp2!=NULL || lp3!=NULL || lp4!=NULL ) /* left bracket */
    rule_raster(rasp, 0,0, thickness,height, 0); /* left vertical bar */     rule_raster(rasp, 0,0, thickness,height, 0); /* left vertical bar */
   if ( rp != NULL ) /* right ] bracket wanted */    if ( lp4 != NULL ) /* 2nd left vertical bar needed */
      rule_raster(rasp, 0,thickness+1, 1,height, 0); /* 2nd left vertical bar */
     if ( rp!=NULL || rp2!=NULL || rp3!=NULL || rp4!=NULL ) /* right bracket */
    rule_raster(rasp, 0,mywidth-thickness, thickness,height, 0); /* right */     rule_raster(rasp, 0,mywidth-thickness, thickness,height, 0); /* right */
     if ( rp4 != NULL ) /* 2nd right vertical bar needed */
      rule_raster(rasp, 0,mywidth-thickness-2, 1,height, 0); /*2nd right vert*/
   isokay = 1; /* set flag */    isokay = 1; /* set flag */
   } /* --- end-of-if(left- or right-[] bracket wanted) --- */    } /* --- end-of-if(left- or right-[] bracket wanted) --- */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
Line 3514  else Line 4996  else
  if ( (lp=strchr(symbol,'<')) != NULL /* left < bracket wanted */   if ( (lp=strchr(symbol,'<')) != NULL /* left < bracket wanted */
  ||   (rp=strchr(symbol,'>')) != NULL ) /* right > bracket wanted */   ||   (rp=strchr(symbol,'>')) != NULL ) /* right > bracket wanted */
   {    {
   /* --- line_raster ( rasp,  row0, col0,  row1, col1,  thickness ) --- */    /* --- use line_raster( rasp,  row0, col0,  row1, col1,  thickness ) --- */
   int mywidth = min2(width,12); /* max width for brackets */    int mywidth = min2(width,12), /* max width for brackets */
   thickness = 1; /* set line pixel thickness */   mythick = 1; /* all lines one pixel thick */
     thickness = (height<25?1:2); /* set line pixel thickness */
   if ( lp != NULL ) /* left < bracket wanted */    if ( lp != NULL ) /* left < bracket wanted */
     { line_raster(rasp,height/2,0,0,mywidth-1,thickness);      { line_raster(rasp,height/2,0,0,mywidth-1,mythick);
       line_raster(rasp,height/2,0,height-1,mywidth-1,thickness); }        if ( thickness>1 )
    line_raster(rasp,height/2,1,0,mywidth-1,mythick);
         line_raster(rasp,height/2,0,height-1,mywidth-1,mythick);
         if ( thickness>1 )
    line_raster(rasp,height/2,1,height-1,mywidth-1,mythick); }
   if ( rp != NULL ) /* right > bracket wanted */    if ( rp != NULL ) /* right > bracket wanted */
     { line_raster(rasp,height/2,mywidth-1,0,0,thickness);      { line_raster(rasp,height/2,mywidth-1,0,0,mythick);
       line_raster(rasp,height/2,mywidth-1,height-1,0,thickness); }        if ( thickness>1 )
    line_raster(rasp,height/2,mywidth-2,0,0,mythick);
         line_raster(rasp,height/2,mywidth-1,height-1,0,mythick);
         if ( thickness>1 )
    line_raster(rasp,height/2,mywidth-2,height-1,0,mythick); }
   isokay = 1; /* set flag */    isokay = 1; /* set flag */
   } /* --- end-of-if(left- or right-<> bracket wanted) --- */    } /* --- end-of-if(left- or right-<> bracket wanted) --- */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
   / \ delimiters
   -------------------------------------------------------------------------- */
   else
    if ( (lp=strchr(symbol,'/')) != NULL /* left /  wanted */
    ||   (rp=strstr(symbol,"\\\\")) != NULL /* right \ wanted */
    ||   (rp2=strstr(symbol,"backsl")) != NULL ) /* right \ wanted */
     {
     /* --- use line_raster( rasp,  row0, col0,  row1, col1,  thickness ) --- */
     int mywidth = width; /* max width for / \ */
     thickness = 1; /* set line pixel thickness */
     if ( lp != NULL ) /* left / wanted */
       line_raster(rasp,0,mywidth-1,height-1,0,thickness);
     if ( rp!=NULL || rp2!=NULL ) /* right \ wanted */
       line_raster(rasp,0,0,height-1,mywidth-1,thickness);
     isokay = 1; /* set flag */
     } /* --- end-of-if(left- or right-/\ delimiter wanted) --- */
   /* -------------------------------------------------------------------------
   arrow delimiters
   -------------------------------------------------------------------------- */
   else
    if ( strstr(symbol,"arrow") != NULL ) /* arrow delimiter wanted */
     {
     /* --- use uparrow_subraster(width,height,pixsz,drctn,isBig) --- */
     int mywidth = width; /* max width for / \ */
     int isBig = (strstr(symbol,"Up")!=NULL /* isBig if we have an Up */
    || strstr(symbol,"Down")!=NULL); /* or a Down */
     int drctn = +1; /* init for uparrow */
     if ( strstr(symbol,"down")!=NULL /* down if we have down */
     ||   strstr(symbol,"Down")!=NULL ) /* or Down */
      { drctn = (-1); /* reset direction to down */
        if ( strstr(symbol,"up")!=NULL /* updown if we have up or Up */
        ||   strstr(symbol,"Up")!=NULL ) /* and down or Down */
         drctn = 0; } /* reset direction to updown */
     sp = uparrow_subraster(mywidth,height,pixsz,drctn,isBig);
     if ( sp != NULL )
      { sp->type = IMAGERASTER; /* image */
        sp->symdef = NULL; /* not applicable for image */
        sp->baseline = height/2 + 2; /* is a little above center good? */
        sp->size = NORMALSIZE; /* size (probably unneeded) */
        isokay = 1; } /* set flag */
     } /* --- end-of-if(arrow delimiter wanted) --- */
   /* -------------------------------------------------------------------------
 \- for | | brackets or \= for || || brackets  \- for | | brackets or \= for || || brackets
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 else  else
Line 3538  else Line 5071  else
   int midcol = width/2; /* middle col, left of mid if even */    int midcol = width/2; /* middle col, left of mid if even */
   if ( rp  != NULL /* left or right || bracket wanted */    if ( rp  != NULL /* left or right || bracket wanted */
   ||   rp2 != NULL ) /* or || in standard tex notation */    ||   rp2 != NULL ) /* or || in standard tex notation */
    { thickness = 1; /* each | of || one pixel thick */     { thickness = (height<75?1:2); /* each | of || 1 or 2 pixels thick*/
      rule_raster(rasp, 0,max2(0,midcol-2), thickness,height, 0); /* left */       rule_raster(rasp, 0,max2(0,midcol-2), thickness,height, 0); /* left */
      rule_raster(rasp, 0,min2(width,midcol+2), thickness,height, 0); }       rule_raster(rasp, 0,min2(width,midcol+2), thickness,height, 0); }
   else /*nb, lp2 spuriously set if rp2 set*/    else /*nb, lp2 spuriously set if rp2 set*/
    if ( lp  != NULL /* left or right | bracket wanted */     if ( lp  != NULL /* left or right | bracket wanted */
    ||   lp2 != NULL ) /* ditto for synomym */     ||   lp2 != NULL ) /* ditto for synomym */
     { thickness = 1; /* set | two pixels thick */      { thickness = (height<75?1:2); /* set | 1 or 2 pixels thick */
       rule_raster(rasp, 0,midcol, thickness,height, 0); } /*mid vertical bar*/        rule_raster(rasp, 0,midcol, thickness,height, 0); } /*mid vertical bar*/
   isokay = 1; /* set flag */    isokay = 1; /* set flag */
   } /* --- end-of-if(left- or right-[] bracket wanted) --- */    } /* --- end-of-if(left- or right-[] bracket wanted) --- */
Line 3552  else Line 5085  else
 back to caller  back to caller
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 end_of_job:  end_of_job:
     if ( msgfp!=NULL && msglevel>=99 )
       fprintf(msgfp,"make_delim> symbol=%.50s, isokay=%d\n",
       (symbol==NULL?"null":symbol),isokay);
   if ( !isokay ) /* don't have requested delimiter */    if ( !isokay ) /* don't have requested delimiter */
     { delete_subraster(sp); /* so free unneeded structure */      { if (sp!=NULL) delete_subraster(sp); /* so free unneeded structure */
       sp = NULL; } /* and signal error to caller */        sp = NULL; } /* and signal error to caller */
   return ( sp ); /*back to caller with delim or NULL*/    return ( sp ); /*back to caller with delim or NULL*/
 } /* --- end-of-function make_delim() --- */  } /* --- end-of-function make_delim() --- */
Line 3598  static char *prefixes[] =   /*e.g., \big Line 5134  static char *prefixes[] =   /*e.g., \big
   "\\big",  "\\Big",  "\\bigg",  "\\Bigg",    "\\big",  "\\Big",  "\\bigg",  "\\Bigg",
   "\\bigl", "\\Bigl", "\\biggl", "\\Biggl",    "\\bigl", "\\Bigl", "\\biggl", "\\Biggl",
   "\\bigr", "\\Bigr", "\\biggr", "\\Biggr", NULL };    "\\bigr", "\\Bigr", "\\biggr", "\\Biggr", NULL };
   static char *starred[] = /* may be followed by * */
    { "\\hspace",  "\\!",  NULL };
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 just return the next char if it's not \  just return the next char if it's not \
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
Line 3637  for ( iprefix=0; prefixes[iprefix] != NU Line 5175  for ( iprefix=0; prefixes[iprefix] != NU
 /* --- every \ must be followed by at least one char, e.g., \[ --- */  /* --- every \ must be followed by at least one char, e.g., \[ --- */
 if ( esclen < 1 ) /* \ followed by non-alpha */  if ( esclen < 1 ) /* \ followed by non-alpha */
   *ptoken++ = *expression++; /*copy non-alpha, bump ptrs*/    *ptoken++ = *expression++; /*copy non-alpha, bump ptrs*/
 else { /* normal alpha \sequence */  
   /* --- respect spaces in text mode, except first space after \escape --- */  
   if ( istext > 0 ) /* in \rm or \it text mode */  
    if ( istext != 3 ) /* but not \mathbb */  
     if ( isthischar(*expression,WHITEDELIM) ) /* delim follows \sequence */  
      expression++; } /* so flush delim */  
 *ptoken = '\000'; /* null-terminate token */  *ptoken = '\000'; /* null-terminate token */
   /* --- check for \hspace* or other starred commands --- */
   for ( iprefix=0; starred[iprefix] != NULL; iprefix++ ) /* run thru list */
    if ( strcmp(chartoken,starred[iprefix]) == 0 ) /* have an exact match */
     if ( *expression == '*' ) /* follows by a * */
      { *ptoken++ = *expression++; /* copy * and bump ptr */
        *ptoken = '\000'; /* null-terminate token */
        break; } /* stop checking */
   /* --- respect spaces in text mode, except first space after \escape --- */
   if ( esclen >= 1 ) { /*only for alpha \sequences*/
     if ( istextmode ) /* in \rm or \it text mode */
      if ( isthischar(*expression,WHITEDELIM) ) /* delim follows \sequence */
       expression++; } /* so flush delim */
 /* --- back to caller --- */  /* --- back to caller --- */
 end_of_job:  end_of_job:
   if ( msgfp!=NULL && msglevel>=999 )    if ( msgfp!=NULL && msglevel>=999 )
Line 3708  char *texsubexpr ( char *expression, cha Line 5252  char *texsubexpr ( char *expression, cha
 Allocations and Declarations  Allocations and Declarations
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 char *texchar(); /*next char (or \sequence) from expression*/  char *texchar(); /*next char (or \sequence) from expression*/
 char *leftptr, leftdelim[32] = "(\000", /* left( found in expression */  char *leftptr, leftdelim[256] = "(\000", /* left( found in expression */
  rightdelim[32] = ")\000"; /* and matching right) */   rightdelim[256] = ")\000"; /* and matching right) */
 char *origexpression=expression, *origsubexpr=subexpr; /*original inputs*/  char *origexpression=expression, *origsubexpr=subexpr; /*original inputs*/
   char *strtexchr(), *texleft(); /* check for \left, and get it */
 int gotescape = 0, /* true if leading char of expression is \ */  int gotescape = 0, /* true if leading char of expression is \ */
  prevescape = 0; /* while parsing, true if preceding char \ */   prevescape = 0; /* while parsing, true if preceding char \ */
 int isbrace(); /* check for left,right braces */  int isbrace(); /* check for left,right braces */
 int isanyright = 1; /* true matches any right with left, (...] */  int isanyright = 1; /* true matches any right with left, (...] */
 int isleftdot = 0; /* true if left brace is a \. */  int isleftdot = 0; /* true if left brace is a \. */
 int nestlevel = 1; /* current # of nested braces */  int nestlevel = 1; /* current # of nested braces */
 int subsz=0 /*, maxsubsz=8192*/; /* #chars in returned subexpr[] buffer*/  int subsz=0 /*,maxsubsz=MAXSUBXSZ*/; /*#chars in returned subexpr buffer*/
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 skip leading whitespace and just return the next char if it's not {  skip leading whitespace and just return the next char if it's not {
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
Line 3727  if ( expression == NULL ) return(NULL); Line 5272  if ( expression == NULL ) return(NULL);
 skipwhite(expression); /* leading whitespace gone */  skipwhite(expression); /* leading whitespace gone */
 if ( *expression == '\000' ) return(NULL); /* nothing left to scan */  if ( *expression == '\000' ) return(NULL); /* nothing left to scan */
 /* --- set maxsubsz --- */  /* --- set maxsubsz --- */
 if ( maxsubsz < 1 ) maxsubsz = 8192; /* input 0 means unlimited */  if ( maxsubsz < 1 ) maxsubsz = MAXSUBXSZ-2; /* input 0 means unlimited */
 /* --- check for escape --- */  /* --- check for escape --- */
 if ( isthischar(*expression,ESCAPE) ) /* expression is escaped */  if ( isthischar(*expression,ESCAPE) ) /* expression is escaped */
   gotescape = 1; /* so set flag accordingly */    gotescape = 1; /* so set flag accordingly */
   /* --- check for \left...\right --- */
   if ( gotescape ) /* begins with \ */
    if ( memcmp(expression+1,"left",4) ) /* and followed by left */
     if ( strchr(left,'l') != NULL ) /* caller wants \left's */
      if ( strtexchr(expression,"\\left") == expression ) /*expression=\left...*/
       { char *pright = texleft(expression,subexpr,maxsubsz, /* find ...\right*/
    (isdelim?NULL:leftdelim),rightdelim);
         if ( isdelim ) strcat(subexpr,rightdelim); /* caller wants delims */
         return ( pright ); /*back to caller past \right*/
       } /* --- end-of-if(expression=="\\left") --- */
 /* --- if first char isn't left{ or script, just return it to caller --- */  /* --- if first char isn't left{ or script, just return it to caller --- */
 if ( !isbrace(expression,left,isescape) ) /* not a left{ */  if ( !isbrace(expression,left,isescape) ) { /* not a left{ */
   if ( !isthischar(*expression,SCRIPTS) ) /* and not a script */    if ( !isthischar(*expression,SCRIPTS) ) /* and not a script */
     return ( texchar(expression,subexpr) ); /* next char to caller */      return ( texchar(expression,subexpr) ); /* next char to caller */
   else /* --- kludge for super/subscripts to accommodate texscripts() --- */    else /* --- kludge for super/subscripts to accommodate texscripts() --- */
     { *subexpr++ = *expression; /* signal script */      { *subexpr++ = *expression; /* signal script */
       *subexpr = '\000'; /* null-terminate subexpr */        *subexpr = '\000'; /* null-terminate subexpr */
       return ( expression ); } /* leave script in stream */        return ( expression ); } } /* leave script in stream */
 /* --- extract left and find matching right delimiter --- */  /* --- extract left and find matching right delimiter --- */
 *leftdelim  = *(expression+gotescape); /* the left( in expression */  *leftdelim  = *(expression+gotescape); /* the left( in expression */
 if ( (gotescape && *leftdelim == '.') /* we have a left \. */  if ( (gotescape && *leftdelim == '.') /* we have a left \. */
Line 3756  accumulate chars between balanced {}'s, Line 5311  accumulate chars between balanced {}'s,
 /* --- first initialize by bumping past left{ or \{ --- */  /* --- first initialize by bumping past left{ or \{ --- */
 if ( isdelim )   *subexpr++ = *expression++; /*caller wants { in subexpr*/  if ( isdelim )   *subexpr++ = *expression++; /*caller wants { in subexpr*/
   else expression++; /* always bump past left{ */    else expression++; /* always bump past left{ */
 if ( gotescape ) /*need to bump another char*/  if ( gotescape ) { /*need to bump another char*/
   if ( isdelim ) *subexpr++ = *expression++; /* caller wants char, too */    if ( isdelim ) *subexpr++ = *expression++; /* caller wants char, too */
   else expression++; /* else just bump past it */    else expression++; } /* else just bump past it */
 /* --- set maximum size for numerical arguments --- */  /* --- set maximum size for numerical arguments --- */
 if ( 0 ) /* check turned on or off? */  if ( 0 ) /* check turned on or off? */
  if ( !isescape && !isdelim ) /*looking for numerical arg*/   if ( !isescape && !isdelim ) /*looking for numerical arg*/
Line 3771  while ( 1 )     /*until balanced right} Line 5326  while ( 1 )     /*until balanced right}
     { if ( 0 && (!isescape && !isdelim) ) /*looking for numerical arg,*/      { if ( 0 && (!isescape && !isdelim) ) /*looking for numerical arg,*/
  { expression = origexpression; /* so end-of-string is error*/   { expression = origexpression; /* so end-of-string is error*/
   subexpr = origsubexpr; } /* so reset all ptrs */    subexpr = origsubexpr; } /* so reset all ptrs */
       if ( isdelim ) /* generate fake right */        if ( isdelim ) { /* generate fake right */
  if ( gotescape ) /* need escaped right */   if ( gotescape ) /* need escaped right */
   { *subexpr++ = '\\'; /* set escape char */    { *subexpr++ = '\\'; /* set escape char */
     *subexpr++ = '.'; } /* and fake \right. */      *subexpr++ = '.'; } /* and fake \right. */
  else /* escape not wanted */   else /* escape not wanted */
     *subexpr++ = *rightdelim; /* so fake actual right */      *subexpr++ = *rightdelim; } /* so fake actual right */
       *subexpr = '\000'; /* null-terminate subexpr */        *subexpr = '\000'; /* null-terminate subexpr */
       return ( expression ); } /* back with final token */        return ( expression ); } /* back with final token */
   /* --- check preceding char for escape --- */    /* --- check preceding char for escape --- */
Line 3811  while ( 1 )     /*until balanced right} Line 5366  while ( 1 )     /*until balanced right}
   
   
 /* ==========================================================================  /* ==========================================================================
    * Function: texleft (expression,subexpr,maxsubsz,ldelim,rdelim)
    * Purpose: scans expression, starting after opening \left,
    * and returning ptr after matching closing \right.
    * Everything between is returned in subexpr, if given.
    * Likewise, if given, ldelim returns delimiter after \left
    * and rdelim returns delimiter after \right.
    * If ldelim is given, the returned subexpr doesn't include it.
    * If rdelim is given, the returned pointer is after that delim.
    * --------------------------------------------------------------------------
    * Arguments: expression (I) char * to first char of null-terminated
    * string immediately following opening \left
    * subexpr (O) char * to null-terminated string returning
    * either everything between balanced
    * \left ... \right.  If leftdelim given,
    * subexpr does _not_ contain that delimiter.
    * maxsubsz (I) int containing max #bytes returned
    * in subexpr buffer (0 means unlimited)
    * ldelim (O) char * returning delimiter following
    * opening \left
    * rdelim (O) char * returning delimiter following
    * closing \right
    * --------------------------------------------------------------------------
    * Returns: ( char * ) ptr to the first char of expression
    * past closing \right, or past closing
    * right delimiter if rdelim!=NULL,
    * or NULL for any error.
    * --------------------------------------------------------------------------
    * Notes:     o
    * ======================================================================= */
   /* --- entry point --- */
   char *texleft ( char *expression, char *subexpr, int maxsubsz,
    char *ldelim, char *rdelim )
   {
   /* -------------------------------------------------------------------------
   Allocations and Declarations
   -------------------------------------------------------------------------- */
   char *texchar(), /* get delims after \left,\right */
    *strtexchr(), *pright=expression; /* locate matching \right */
   static char left[16]="\\left", right[16]="\\right"; /* tex delimiters */
   int sublen = 0; /* #chars between \left...\right */
   /* -------------------------------------------------------------------------
   initialization
   -------------------------------------------------------------------------- */
   /* --- init output --- */
   if ( subexpr != NULL ) *subexpr = '\000'; /* init subexpr, if given */
   if ( ldelim  != NULL ) *ldelim  = '\000'; /* init ldelim,  if given */
   if ( rdelim  != NULL ) *rdelim  = '\000'; /* init rdelim,  if given */
   /* --- check args --- */
   if ( expression == NULL ) goto end_of_job; /* no input supplied */
   if ( *expression == '\000' ) goto end_of_job; /* nothing after \left */
   /* --- determine left delimiter  --- */
   if ( ldelim != NULL ) /* caller wants left delim */
    { skipwhite(expression); /* interpret \left ( as \left( */
      expression = texchar(expression,ldelim); } /*delim from expression*/
   /* -------------------------------------------------------------------------
   locate \right balancing opening \left
   -------------------------------------------------------------------------- */
   /* --- first \right following \left --- */
   if ( (pright=strtexchr(expression,right)) /* look for \right after \left */
   !=   NULL ) { /* found it */
    /* --- find matching \right by pushing past any nested \left's --- */
    char *pleft = expression; /* start after first \left( */
    while ( 1 ) { /*break when matching \right found*/
     /* -- locate next nested \left if there is one --- */
     if ( (pleft=strtexchr(pleft,left)) /* find next \left */
     ==   NULL ) break; /*no more, so matching \right found*/
     pleft += strlen(left); /* push ptr past \left token */
     if ( pleft >= pright ) break; /* not nested if \left after \right*/
     /* --- have nested \left, so push forward to next \right --- */
     if ( (pright=strtexchr(pright+strlen(right),right)) /* find next \right */
     ==   NULL ) break; /* ran out of \right's */
     } /* --- end-of-while(1) --- */
    } /* --- end-of-if(pright!=NULL) --- */
   /* --- set subexpression length, push pright past \right --- */
   if ( pright != (char *)NULL ) /* found matching \right */
    { sublen = (int)(pright-expression); /* #chars between \left...\right */
      pright += strlen(right); } /* so push pright past \right */
   /* -------------------------------------------------------------------------
   get rightdelim and subexpr between \left...\right
   -------------------------------------------------------------------------- */
   /* --- get delimiter following \right --- */
   if ( rdelim != NULL ) { /* caller wants right delim */
    if ( pright == (char *)NULL ) /* assume \right. at end of exprssn*/
     { strcpy(rdelim,"."); /* set default \right. */
       sublen = strlen(expression); /* use entire remaining expression */
       pright = expression + sublen; } /* and push pright to end-of-string*/
    else /* have explicit matching \right */
     { skipwhite(pright); /* interpret \right ) as \right) */
       pright = texchar(pright,rdelim); /* pull delim from expression */
       if ( *rdelim == '\000' ) strcpy(rdelim,"."); } } /* or set \right. */
   /* --- get subexpression between \left...\right --- */
   if ( sublen > 0 ) /* have subexpr */
    if ( subexpr != NULL ) { /* and caller wants it */
     if ( maxsubsz > 0 ) sublen = min2(sublen,maxsubsz-1); /* max buffer size */
     memcpy(subexpr,expression,sublen); /* stuff between \left...\right */
     subexpr[sublen] = '\000'; } /* null-terminate subexpr */
   end_of_job:
     if ( msglevel>=99 && msgfp!=NULL )
       { fprintf(msgfp,"texleft> ldelim=%s, rdelim=%s, subexpr=%.128s\n",
         (ldelim==NULL?"none":ldelim),(rdelim==NULL?"none":rdelim),
         (subexpr==NULL?"none":subexpr)); fflush(msgfp); }
     return ( pright );
   } /* --- end-of-function texleft --- */
   
   
   /* ==========================================================================
  * Function: texscripts ( expression, subscript, superscript, which )   * Function: texscripts ( expression, subscript, superscript, which )
  * Purpose: scans expression, returning subscript and/or superscript   * Purpose: scans expression, returning subscript and/or superscript
  * if expression is of the form _x^y or ^{x}_{y},   * if expression is of the form _x^y or ^{x}_{y},
Line 3846  Allocations and Declarations Line 5507  Allocations and Declarations
 char *texsubexpr(); /* next subexpression from expression */  char *texsubexpr(); /* next subexpression from expression */
 int gotsub=0, gotsup=0; /* check that we don't eat, e.g., x_1_2 */  int gotsub=0, gotsup=0; /* check that we don't eat, e.g., x_1_2 */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 init "scripts" and skip leading whitespace  init "scripts"
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 /* --- skip leading whitespace and check for end-of-string --- */  if ( subscript != NULL ) *subscript = '\000'; /*init in case no subscript*/
 *subscript = *superscript = '\000'; /* init in case no scripts */  if ( superscript!=NULL ) *superscript = '\000'; /*init in case no super*/
 skipwhite(expression); /* leading whitespace gone */  
 if ( *expression == '\000' ) return(expression); /* nothing left to scan */  
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 get subscript and/or superscript from expression  get subscript and/or superscript from expression
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 while ( expression != NULL )  while ( expression != NULL ) {
     skipwhite(expression); /* leading whitespace gone */
     if ( *expression == '\000' ) return(expression); /* nothing left to scan */
   if ( isthischar(*expression,SUBSCRIPT) /* found _ */    if ( isthischar(*expression,SUBSCRIPT) /* found _ */
   &&   (which==1 || which>2 ) ) /* and caller wants it */    &&   (which==1 || which>2 ) ) /* and caller wants it */
     { if ( gotsub /* found 2nd subscript */      { if ( gotsub /* found 2nd subscript */
Line 3871  while ( expression != NULL ) Line 5532  while ( expression != NULL )
  expression = texsubexpr(expression+1,superscript,0,"{","}",0,0); }   expression = texsubexpr(expression+1,superscript,0,"{","}",0,0); }
     else /* neither _ nor ^ */      else /* neither _ nor ^ */
       return ( expression ); /*return ptr past "scripts"*/        return ( expression ); /*return ptr past "scripts"*/
     } /* --- end-of-while(expression!=NULL) --- */
 return ( expression );  return ( expression );
 } /* --- end-of-function texscripts() --- */  } /* --- end-of-function texscripts() --- */
   
Line 3913  int gotescape = 0,  /* true if leading c Line 5575  int gotescape = 0,  /* true if leading c
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 check for brace  check for brace
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 /* --- first check for end-of-string --- */  /* --- first check for end-of-string or \= ligature --- */
 if ( *expression == '\000' ) return(0); /* nothing to check */  if ( *expression == '\000' /* nothing to check */
   ||   isligature ) goto end_of_job; /* have a \= ligature */
 /* --- check leading char for escape --- */  /* --- check leading char for escape --- */
 if ( isthischar(*expression,ESCAPE) ) /* expression is escaped */  if ( isthischar(*expression,ESCAPE) ) /* expression is escaped */
   { gotescape = 1; /* so set flag accordingly */    { gotescape = 1; /* so set flag accordingly */
Line 3930  if ( gotbrace && isthischar(*expression, Line 5593  if ( gotbrace && isthischar(*expression,
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 back to caller  back to caller
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 if ( gotbrace && /* found a brace */  end_of_job:
    if ( msglevel>=999 && msgfp!=NULL )
     { fprintf(msgfp,"isbrace> expression=%.8s, gotbrace=%d (isligature=%d)\n",
       expression,gotbrace,isligature); fflush(msgfp); }
    if ( gotbrace && /* found a brace */
      ( isescape==2 || /* escape irrelevant */       ( isescape==2 || /* escape irrelevant */
        gotescape==isescape ) /* un/escaped as requested */         gotescape==isescape ) /* un/escaped as requested */
    ) return ( 1 );  return ( 0 ); /* return 1,0 accordingly */     ) return ( 1 );  return ( 0 ); /* return 1,0 accordingly */
Line 3985  process preamble if present Line 5652  process preamble if present
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 /*process_preamble:*/  /*process_preamble:*/
 if ( (dollar=strchr(expression,'$')) /* $ signals preceding preamble */  if ( (dollar=strchr(expression,'$')) /* $ signals preceding preamble */
 !=   NULL ) /* found embedded $ */  !=   NULL ) { /* found embedded $ */
  if ( (prelen = (int)(dollar-expression)) /*#chars in expression preceding $*/   if ( (prelen = (int)(dollar-expression)) /*#chars in expression preceding $*/
  > 0 ) { /* must have preamble preceding $ */   > 0 ) { /* must have preamble preceding $ */
   if ( prelen < 65 ) { /* too long for a prefix */    if ( prelen < 65 ) { /* too long for a prefix */
Line 4010  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 4039  if ( (dollar=strchr(expression,'$')) /* Line 5707  if ( (dollar=strchr(expression,'$')) /*
     isdisplaystyle = 2; /* so set \displaystyle */      isdisplaystyle = 2; /* so set \displaystyle */
   /*goto process_preamble;*/ /*check for preamble after leading $*/    /*goto process_preamble;*/ /*check for preamble after leading $*/
   } /* --- end-of-if/else(prelen>0) --- */    } /* --- end-of-if/else(prelen>0) --- */
    } /* --- end-of-if(dollar!=NULL) --- */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 back to caller  back to caller
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
Line 4074  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 *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 */
  isymbol=0; /*symbols[],rightcomment[],etc index*/   isymbol=0; /*symbols[],rightcomment[],etc index*/
Line 4130  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
    /* ------------------------------------------     /* --------------------------------------------
    LaTeX Macro  #args,default   template...       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}}" },
      { "\\\'K", "embed\\","{\\acute{K}}" },
      { "\\\'k", "embed\\","{\\acute{k}}" },
      { "\\u U", "embed\\","{\\breve{U}}" },
      { "\\u u", "embed\\","{\\breve{u}}" },
      /*{ "\\\"E", "embed\\","{\\ddot{E}}" },*/
      /*{ "\\\"e", "embed\\","{\\ddot{e}}" },*/
      { "\\\"I", "embed\\","{\\ddot{\\=I}}" },
      { "\\\"\\i", "embed\\","{\\ddot{\\=\\i}}" },
      /* --------------------------------------------
        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 */
      { "\\acute", "1", "{\\stackrel{\\Huge\\acutesym}{#1}}" }, /* \acute */
      { "\\check", "1", "{\\stackrel{\\Huge\\checksym}{#1}}" }, /* \check */
      { "\\breve", "1", "{\\stackrel{\\Huge\\brevesym}{#1}}" }, /* \breve */
      { "\\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    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 */
      { "&backslash",";", "\\" },
    { "&nbsp", ";", "~" },     { "&nbsp", ";", "~" },
    { "&iexcl", ";", "{\\raisebox{-2}{\\rotatebox{180}{!}}}" },     { "&iexcl", ";", "{\\raisebox{-2}{\\rotatebox{180}{!}}}" },
    { "&brvbar", ";", "|" },     { "&brvbar", ";", "|" },
Line 4166  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...
      -------------------------------------------- */
      { "< br >",    "embed\\i", "\\\\" },
      { "< br / >",  "embed\\i", "\\\\" },
      { "< dd >",    "embed\\i", " \000" },
      { "< / dd >",  "embed\\i", " \000" },
      { "< dl >",    "embed\\i", " \000" },
      { "< / dl >",  "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}" },
    { "\\vdots", NULL, "{\\raisebox3{\\rotatebox{90}{\\ldots}}}" },     { "\\vdots", NULL, "{\\raisebox3{\\rotatebox{90}{\\ldots}}}" },
      { "\\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=}" },
      { "\\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}"},
      { "\\textdegree", NULL, "{\\Large^{^{\\tiny\\mathbf o}}}"},
    { "\\cr", NULL, "\\\\" },     { "\\cr", NULL, "\\\\" },
      /*{ "\\colon", NULL, "{:}" },*/
      { "\\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}" },
      { "\\_", "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","^{\\fs{-1}\\prime\\prime\\prime\\prime}" },
      { "\'\'\'", "embed","^{\\fs{-1}\\prime\\prime\\prime}" },
      { "\'\'", "embed","^{\\fs{-1}\\prime\\prime}" },
      { "\'", "embed","^{\\fs{-1}\\prime}" },
    { "\\rightleftharpoons",NULL,"{\\rightharpoonup\\atop\\leftharpoondown}" },     { "\\rightleftharpoons",NULL,"{\\rightharpoonup\\atop\\leftharpoondown}" },
      { "\\therefore",NULL,"{\\Huge\\raisebox{-4}{.\\atop.\\,.}}" },
    { "\\LaTeX", NULL, "{\\rm~L\\raisebox{3}{\\fs{-1}A}\\TeX}" },     { "\\LaTeX", NULL, "{\\rm~L\\raisebox{3}{\\fs{-1}A}\\TeX}" },
    { "\\TeX", NULL, "{\\rm~T\\raisebox{-3}{E}X}" },     { "\\TeX", NULL, "{\\rm~T\\raisebox{-3}{E}X}" },
    { "\\cyan", NULL, "{\\reverse\\red\\reversebg}" },     { "\\cyan", NULL, "{\\reverse\\red\\reversebg}" },
    { "\\magenta",NULL, "{\\reverse\\green\\reversebg}" },     { "\\magenta",NULL, "{\\reverse\\green\\reversebg}" },
    { "\\yellow",NULL, "{\\reverse\\blue\\reversebg}" },     { "\\yellow",NULL, "{\\reverse\\blue\\reversebg}" },
      { "\\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}}" },
    { "asin", "1", "{\\sin^{-1}{#1}}" },     { "asin", "1", "{\\sin^{-1}{#1}}" },
    { "acos", "1", "{\\cos^{-1}{#1}}" },     { "acos", "1", "{\\cos^{-1}{#1}}" },
    { "exp", "1", "{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 4229  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 4239  while ( (leftptr=strstr(expptr,leftcomme Line 6038  while ( (leftptr=strstr(expptr,leftcomme
     expptr = leftptr+1; /*resume search after this comment*/      expptr = leftptr+1; /*resume search after this comment*/
   } /* --- end-of-while(leftptr!=NULL) --- */    } /* --- end-of-while(leftptr!=NULL) --- */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 convert \left( to \(  and  \right) to \),  etc.  
 -------------------------------------------------------------------------- */  
 if ( xlateleft ) /* \left...\right xlation wanted */  
  for ( idelim=0; idelim<2; idelim++ ) /* 0 for \left  and  1 for \right */  
   {  
   char *lrstr  = (idelim==0?"\\left":"\\right"); /* \left on 1st pass */  
   int lrlen   = (idelim==0?5:6); /* strlen() of \left or \right */  
   char *braces = (idelim==0?LEFTBRACES ".":RIGHTBRACES "."), /*([{<or)]}>*/  
  **lrfrom= (idelim==0?leftfrom:rightfrom), /* long braces like \| */  
  **lrto  = (idelim==0?leftto:rightto), /* xlated to 1-char like = */  
  *lrsym  = NULL; /* lrfrom[isymbol] */  
   expptr = expression; /* start search at beginning */  
   while ( (tokptr=strstr(expptr,lrstr)) != NULL ) /* found \left or \right */  
     {  
     if ( isthischar(*(tokptr+lrlen),braces) ) /* followed by a 1-char brace*/  
       { strcpy(tokptr+1,tokptr+lrlen); /* so squeeze out "left" or "right"*/  
  expptr = tokptr+2; } /* and resume search past brace */  
     else /* may be a "long" brace like \| */  
       {  
       expptr = tokptr+lrlen; /*init to resume search past\left\rt*/  
       for(isymbol=0; (lrsym=lrfrom[isymbol]) != NULL; isymbol++)  
  { int symlen = strlen(lrsym); /* #chars in delim, e.g., 2 for \| */  
   if ( memcmp(tokptr+lrlen,lrsym,symlen) == 0 ) /* found long delim*/  
     { strcpy(tokptr+1,tokptr+lrlen+symlen-1); /* squeeze out delim */  
       *(tokptr+1) = *(lrto[isymbol]); /* last char now 1-char delim*/  
       expptr = tokptr+2 - lrlen; /* resume search past 1-char delim*/  
       break; } /* no need to check more lrsym's */  
  } /* --- end-of-for(isymbol) --- */  
       } /* --- end-of-if/else(isthischar()) --- */  
     } /* --- end-of-while(tokptr!=NULL) --- */  
   } /* --- end-of-for(idelim) --- */  
 /* -------------------------------------------------------------------------  
 run thru table, converting all occurrences of each macro to its expansion  run thru table, converting all occurrences of each macro to its expansion
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 for(isymbol=0; (htmlsym=symbols[isymbol].html) != NULL; isymbol++)  for(isymbol=0; (htmlsym=symbols[isymbol].html) != NULL; 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 */
    istag=0, isamp=0, /* true for <tag>, &char; symbols */
    isstrwstr = 0, /* true to use strwstr() */
    wstrlen = 0; /* length of strwstr() match */
   char *aleft="{([<|", *aright="})]>|"; /*left,right delims for alg syntax*/    char *aleft="{([<|", *aright="})]>|"; /*left,right delims for alg syntax*/
     char embedkeywd[99] = "embed", /* keyword to signal embedded token*/
    embedterm = '\000'; /* char immediately after embed */
     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() */
     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 ? */
      { htmlterm = NULL; /* if so, then we have no htmlterm */       { htmlterm = NULL; /* if so, then we have no htmlterm */
        *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*/
        { int arglen = strlen(args); /* length of "embed..." string */
          htmlterm = NULL; /* if so, then we have no htmlterm */
          isembedded = 1 ; /* turn on embedded flag */
          if ( arglen > embedlen ) /* have embed "allow escape" flag */
            embedterm = args[embedlen]; /* char immediately after "embed" */
          if (arglen > embedlen+1) { /* have embed,flag,white for strwstr*/
    isstrwstr = 1; /* turn on strwtsr flag */
    strcpy(wstrwhite,args+embedlen+1); } } /*and set its whitespace arg*/
       } /* --- end-of-if(*args!='\000') --- */
   expptr = expression; /* re-start search at beginning */    expptr = expression; /* re-start search at beginning */
   while ( (tokptr=strstr(expptr,htmlsym)) != NULL ) /* found another sym */    while ( ( tokptr=(!isstrwstr?strstr(expptr,htmlsym): /* just use strtsr */
     { char termchar = *(tokptr+htmllen); /* char terminating html sequence */    strwstr(expptr,htmlsym,wstrwhite,&wstrlen)) ) /* or use our strwstr */
       int escapelen = htmllen; /* total length of escape sequence */    != NULL ) { /* found another sym */
         int  toklen = (!isstrwstr?htmllen:wstrlen); /* length of matched sym */
         char termchar = *(tokptr+toklen), /* char terminating html sequence */
              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  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 ( isalpha((int)termchar) ) /*we just have prefix of longer sym*/        /* --- don't xlate if we just found prefix of longer symbol, etc --- */
  { expptr = tokptr+htmllen; /* just resume search after prefix */        if ( !isembedded ) { /* not embedded */
    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 ( !isthischar(*htmlsym,ESCAPE) /* our symbol isn't escaped */        /* --- check for &# prefix signalling &#nnn; --- */
       &&   !isthischar(*htmlsym,"&") ) /* and not an &html; special char */        if ( strcmp(htmlsym,"&#") == 0 ) { /* replacing special &#nnn; chars */
        if ( tokptr != expression ) /* then if we're past beginning */         /* --- accumulate chars comprising number following &# --- */
  if ( isthischar(*(tokptr-1),ESCAPE) /*and if inline symbol escaped*/         char anum[32]; /* chars comprising number after &# */
  ||   (isalpha(*(tokptr-1))) ) /* or if suffix of longer string */         int  inum = 0; /* no chars accumulated yet */
   { expptr = tokptr+escapelen; /*just resume search after literal*/         while ( termchar != '\000' ) { /* don't go past end-of-string */
     continue; } /* but don't replace it */           if ( !isdigit((int)termchar) ) break; /* and don't go past digits */
            if ( inum > 10 ) break; /* some syntax error in expression */
            anum[inum] = termchar; /* accumulate this digit */
            inum++;  toklen++; /* bump field length, token length */
            termchar = *(tokptr+toklen); } /* char terminating html sequence */
          anum[inum] = '\000'; /* null-terminate anum */
          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 4324  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/else(!isalgebra) --- */    } /* --- 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 4347  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.
   -------------------------------------------------------------------------- */
   if ( xlateleft ) /* \left...\right xlation wanted */
    for ( idelim=0; idelim<2; idelim++ ) /* 0 for \left  and  1 for \right */
     {
     char *lrstr  = (idelim==0?"\\left":"\\right"); /* \left on 1st pass */
     int lrlen   = (idelim==0?5:6); /* strlen() of \left or \right */
     char *braces = (idelim==0?LEFTBRACES ".":RIGHTBRACES "."), /*([{<or)]}>*/
    **lrfrom= (idelim==0?leftfrom:rightfrom), /* long braces like \| */
    **lrto  = (idelim==0?leftto:rightto), /* xlated to 1-char like = */
    *lrsym  = NULL; /* lrfrom[isymbol] */
     expptr = expression; /* start search at beginning */
     while ( (tokptr=strstr(expptr,lrstr)) != NULL ) /* found \left or \right */
       {
       if ( isthischar(*(tokptr+lrlen),braces) ) /* followed by a 1-char brace*/
         { strsqueeze((tokptr+1),(lrlen-1));/*so squeeze out "left" or "right"*/
    expptr = tokptr+2; } /* and resume search past brace */
       else /* may be a "long" brace like \| */
         {
         expptr = tokptr+lrlen; /*init to resume search past\left\rt*/
         for(isymbol=0; (lrsym=lrfrom[isymbol]) != NULL; isymbol++)
    { int symlen = strlen(lrsym); /* #chars in delim, e.g., 2 for \| */
     if ( memcmp(tokptr+lrlen,lrsym,symlen) == 0 ) /* found long delim*/
       { strsqueeze((tokptr+1),(lrlen+symlen-2)); /*squeeze out delim*/
         *(tokptr+1) = *(lrto[isymbol]); /* last char now 1-char delim*/
         expptr = tokptr+2 - lrlen; /* resume search past 1-char delim*/
         break; } /* no need to check more lrsym's */
    } /* --- end-of-for(isymbol) --- */
         } /* --- end-of-if/else(isthischar()) --- */
       } /* --- end-of-while(tokptr!=NULL) --- */
     } /* --- end-of-for(idelim) --- */
   /* -------------------------------------------------------------------------
 run thru table, converting all {a+b\atop c+d} to \atop{a+b}{c+d}  run thru table, converting all {a+b\atop c+d} to \atop{a+b}{c+d}
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 for(isymbol=0; (atopsym=atopcommands[isymbol]) != NULL; isymbol++)  for(isymbol=0; (atopsym=atopcommands[isymbol]) != NULL; isymbol++)
Line 4389  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 4438  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 4507  return ( nreps );   /* #replacements bac Line 6381  return ( nreps );   /* #replacements bac
   
   
 /* ==========================================================================  /* ==========================================================================
  * Function: strtexchr (char *string, char *texchr )   * Function: strwstr (char *string, char *substr, char *white, int *sublen)
    * Purpose: Find first substr in string, but wherever substr contains
    * a whitespace char (in white), string may contain any number
    * (including 0) of whitespace chars. If white contains I or i,
    * then match is case-insensitive (and I,i _not_ whitespace).
    * --------------------------------------------------------------------------
    * Arguments: string (I) char * to null-terminated string in which
    * first occurrence of substr will be found
    * substr (I) char * to null-terminated string containing
    * "template" that will be searched for
    * white (I) char * to null-terminated string containing
    * whitespace chars.  If NULL or empty, then
    * "~ \t\n\r\f\v" (WHITEMATH in mimetex.h) used.
    * If white contains I or i, then match is
    * case-insensitive (and I,i _not_ considered
    * whitespace).
    * sublen (O) address of int returning "length" of substr
    * found in string (which may be longer or
    * shorter than substr itself).
    * --------------------------------------------------------------------------
    * Returns: ( char * ) ptr to first char of substr in string
    * or NULL if not found or for any error.
    * --------------------------------------------------------------------------
    * Notes:     o Wherever a single whitespace char appears in substr,
    * the corresponding position in string may contain any
    * number (including 0) of whitespace chars, e.g.,
    * string="abc   def" and string="abcdef" both match
    * substr="c d" at offset 2 of string.
    *      o If substr="c  d" (two spaces between c and d),
    * then string must have at least one space, so now "abcdef"
    * doesn't match.  In general, the minimum number of spaces
    * in string is the number of spaces in substr minus 1
    * (so 1 space in substr permits 0 spaces in string).
    *      o Embedded spaces are counted in sublen, e.g.,
    * string="c   d" (three spaces) matches substr="c d"
    * with sublen=5 returned.  But string="ab   c   d" will
    * also match substr="  c d" returning sublen=5 and
    * a ptr to the "c".  That is, the mandatory preceding
    * space is _not_ counted as part of the match.
    * But all the embedded space is counted.
    * (An inconsistent bug/feature is that mandatory
    * terminating space is counted.)
    *      o Moreover, string="c   d" matches substr="  c d", i.e.,
    * the very beginning of a string is assumed to be preceded
    * by "virtual blanks".
    * ======================================================================= */
   /* --- entry point --- */
   char *strwstr ( char *string, char *substr, char *white, int *sublen )
   {
   /* -------------------------------------------------------------------------
   Allocations and Declarations
   -------------------------------------------------------------------------- */
   char *psubstr=substr, *pstring=string,/*ptr to current char in substr,str*/
    *pfound = (char *)NULL; /*ptr to found substr back to caller*/
   char *pwhite=NULL, whitespace[256]; /* callers white whithout i,I */
   int iscase = (white==NULL?1: /* case-sensitive if i,I in white */
    strchr(white,'i')==NULL && strchr(white,'I')==NULL);
   int foundlen = 0; /* length of substr found in string*/
   int nstrwhite=0, nsubwhite=0, /* #leading white chars in str,sub */
    nminwhite=0; /* #mandatory leading white in str */
   int nstrchars=0, nsubchars=0, /* #non-white chars to be matched */
    isncmp=0; /*strncmp() or strncasecmp() result*/
   /* -------------------------------------------------------------------------
   Initialization
   -------------------------------------------------------------------------- */
   /* --- set up whitespace --- */
   strcpy(whitespace,WHITEMATH); /*default if no user input for white*/
   if ( white != NULL ) /*user provided ptr to white string*/
    if ( *white != '\000' ) { /*and it's not just an empty string*/
      strcpy(whitespace,white); /* so use caller's white spaces */
      while ( (pwhite=strchr(whitespace,'i')) != NULL ) /* have an embedded i */
        {strsqueeze(pwhite,1);} /* so squeeze it out */
      while ( (pwhite=strchr(whitespace,'I')) != NULL ) /* have an embedded I */
        {strsqueeze(pwhite,1);} /* so squeeze it out */
      if ( *whitespace == '\000' ) /* caller's white just had i,I */
        strcpy(whitespace,WHITEMATH); } /* so revert back to default */
   /* -------------------------------------------------------------------------
   Find first occurrence of substr in string
   -------------------------------------------------------------------------- */
   if ( string != NULL ) /* caller passed us a string ptr */
    while ( *pstring != '\000' ) { /* break when string exhausted */
     char *pstrptr = pstring; /* (re)start at next char in string*/
     int leadingwhite = 0; /* leading whitespace */
     psubstr = substr; /* start at beginning of substr */
     foundlen = 0; /* reset length of found substr */
     if ( substr != NULL ) /* caller passed us a substr ptr */
      while ( *psubstr != '\000' ) { /*see if pstring begins with substr*/
       /* --- check for end-of-string before finding match --- */
       if ( *pstrptr == '\000' ) /* end-of-string without a match */
         goto nextstrchar; /* keep trying with next char */
       /* --- actual amount of whitespace in string and substr --- */
       nsubwhite = strspn(psubstr,whitespace); /* #leading white chars in sub */
       nstrwhite = strspn(pstrptr,whitespace); /* #leading white chars in str */
       nminwhite = max2(0,nsubwhite-1); /* #mandatory leading white in str */
       /* --- check for mandatory leading whitespace in string --- */
       if ( pstrptr != string ) /*not mandatory at start of string*/
         if ( nstrwhite < nminwhite ) /* too little leading white space */
    goto nextstrchar; /* keep trying with next char */
       /* ---hold on to #whitespace chars in string preceding substr match--- */
       if ( pstrptr == pstring ) /* whitespace at start of substr */
         leadingwhite = nstrwhite; /* save it as leadingwhite */
       /* --- check for optional whitespace --- */
       if ( psubstr != substr ) /* always okay at start of substr */
         if ( nstrwhite>0 && nsubwhite<1 ) /* too much leading white space */
    goto nextstrchar; /* keep trying with next char */
       /* --- skip any leading whitespace in substr and string --- */
       psubstr += nsubwhite; /* push past leading sub whitespace*/
       pstrptr += nstrwhite; /* push past leading str whitespace*/
       /* --- now get non-whitespace chars that we have to match --- */
       nsubchars = strcspn(psubstr,whitespace); /* #non-white chars in sub */
       nstrchars = strcspn(pstrptr,whitespace); /* #non-white chars in str */
       if ( nstrchars < nsubchars ) /* too few chars for match */
         goto nextstrchar; /* keep trying with next char */
       /* --- see if next nsubchars are a match --- */
       isncmp = (iscase? strncmp(pstrptr,psubstr,nsubchars): /*case sensitive*/
    strncasecmp(pstrptr,psubstr,nsubchars)); /*case insensitive*/
       if ( isncmp != 0 ) /* no match */
         goto nextstrchar; /* keep trying with next char */
       /* --- push past matched chars --- */
       psubstr += nsubchars;  pstrptr += nsubchars;  /*nsubchars were matched*/
       } /* --- end-of-while(*psubstr!='\000') --- */
     pfound = pstring + leadingwhite; /* found match starting at pstring */
     foundlen = (int)(pstrptr-pfound); /* consisting of this many chars */
     goto end_of_job; /* back to caller */
     /* ---failed to find substr, continue trying with next char in string--- */
     nextstrchar: /* continue outer loop */
       pstring++; /* bump to next char in string */
     } /* --- end-of-while(*pstring!='\000') --- */
   /* -------------------------------------------------------------------------
   Back to caller with ptr to first occurrence of substr in string
   -------------------------------------------------------------------------- */
   end_of_job:
     if ( msglevel>=999 && msgfp!=NULL) { /* debugging/diagnostic output */
       fprintf(msgfp,"strwstr> str=\"%.72s\" sub=\"%s\" found at offset %d\n",
       string,substr,(pfound==NULL?(-1):(int)(pfound-string))); fflush(msgfp); }
     if ( sublen != NULL ) /*caller wants length of found substr*/
       *sublen = foundlen; /* give it to him along with ptr */
     return ( pfound ); /*ptr to first found substr, or NULL*/
   } /* --- end-of-function strwstr() --- */
   
   
   /* ==========================================================================
    * 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 )
  * 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
  * --------------------------------------------------------------------------   * --------------------------------------------------------------------------
  * Arguments: string (I) char * to null-terminated string in which   * Arguments: string (I) char * to null-terminated string in which
  * firstoccurrence of delim will be found   * first occurrence of delim will be found
  * texchr (I) char * to null-terminated string that   * texchr (I) char * to null-terminated string that
  * will be searched for   * will be searched for
  * --------------------------------------------------------------------------   * --------------------------------------------------------------------------
Line 4527  char *strtexchr ( char *string, char *te Line 6600  char *strtexchr ( char *string, char *te
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 Allocations and Declarations  Allocations and Declarations
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 char *strstr(), delim, *ptexchr=(char *)NULL;  /* ptr returned to caller*/  char delim, *ptexchr=(char *)NULL; /* ptr returned to caller*/
 char *pstring = string; /* start or continue up search here*/  char *pstring = string; /* start or continue up search here*/
 int texchrlen = (texchr==NULL?0:strlen(texchr)); /* #chars in texchr */  int texchrlen = (texchr==NULL?0:strlen(texchr)); /* #chars in texchr */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 locate texchr in string  locate texchr in string
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 if ( string != (char *)NULL /* check that we got input string */  if ( string != (char *)NULL /* check that we got input string */
 &&   texchrlen > 0 ) /* and a texchr to search for */  &&   texchrlen > 0 ) { /* and a texchr to search for */
  while ( (ptexchr=strstr(pstring,texchr)) /* look for texchr in string */   while ( (ptexchr=strstr(pstring,texchr)) /* look for texchr in string */
  != (char *)NULL ) /* found it */   != (char *)NULL ) /* found it */
   if ( (delim = ptexchr[texchrlen]) /* char immediately after texchr */    if ( (delim = ptexchr[texchrlen]) /* char immediately after texchr */
Line 4544  if ( string != (char *)NULL  /* check th Line 6617  if ( string != (char *)NULL  /* check th
    ||   0 ) /* other tests to be determined */     ||   0 ) /* other tests to be determined */
     pstring = ptexchr + texchrlen; /* continue search after texchr */      pstring = ptexchr + texchrlen; /* continue search after texchr */
    else /* passed all tests */     else /* passed all tests */
     break; /*so return ptr to texchr to caller*/      break; } /*so return ptr to texchr to caller*/
 return ( ptexchr ); /* ptr to texchar back to caller */  return ( ptexchr ); /* ptr to texchar back to caller */
 } /* --- end-of-function strtexchr() --- */  } /* --- end-of-function strtexchr() --- */
   
Line 4612  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 4641  subraster *rasterize ( char *expression, Line 7179  subraster *rasterize ( char *expression,
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 Allocations and Declarations  Allocations and Declarations
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 char *preamble(), pretext[256]; /* process preamble, if present */  char *preamble(), pretext[512]; /* process preamble, if present */
 char chartoken[8192], *texsubexpr(), /*get subexpression from expression*/  char chartoken[MAXSUBXSZ+1], *texsubexpr(), /*get subexpression from expr*/
  *subexpr = chartoken; /* token may be parenthesized expr */   *subexpr = chartoken; /* token may be parenthesized expr */
 int isbrace(); /* check subexpr for braces */  int isbrace(); /* check subexpr for braces */
 mathchardef *symdef, *get_symdef(); /*get mathchardef struct for symbol*/  mathchardef *symdef, *get_symdef(); /*get mathchardef struct for symbol*/
   int ligdef, get_ligature(); /*get symtable[] index for ligature*/
 int natoms=0; /* #atoms/tokens processed so far */  int natoms=0; /* #atoms/tokens processed so far */
 int type_raster(); /* display debugging output */  int type_raster(); /* display debugging output */
 subraster *rasterize(), /* recurse */  subraster *rasterize(), /* recurse */
Line 4658  subraster *get_charsubraster(),  /* char Line 7197  subraster *get_charsubraster(),  /* char
  *sp=NULL, *prevsp=NULL, /* raster for current, prev char */   *sp=NULL, *prevsp=NULL, /* raster for current, prev char */
  *expraster = (subraster *)NULL; /* raster returned to caller */   *expraster = (subraster *)NULL; /* raster returned to caller */
 int delete_subraster(); /* free everything before returning*/  int delete_subraster(); /* free everything before returning*/
   int family = fontinfo[fontnum].family; /* current font family */
   int isleftscript = 0, /* true if left-hand term scripted */
    wasscripted = 0, /* true if preceding token 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 wastext = istext, /* initial istext mode flag */  int wasstring = isstring, /* initial isstring mode flag */
  wasstring = isstring, /* initial isstring mode flag */  
  wasdisplaystyle = isdisplaystyle, /*initial displaystyle mode flag*/   wasdisplaystyle = isdisplaystyle, /*initial displaystyle mode flag*/
    oldfontnum = fontnum, /* initial font family */
  oldfontsize = fontsize, /* initial fontsize */   oldfontsize = fontsize, /* initial fontsize */
  olddisplaysize = displaysize, /* initial \displaystyle size */   olddisplaysize = displaysize, /* initial \displaystyle size */
  oldshrinkfactor = shrinkfactor, /* initial shrinkfactor */   oldshrinkfactor = shrinkfactor, /* initial shrinkfactor */
  oldsquashmargin = squashmargin, /* initial squashmargin */   oldsmashmargin = smashmargin, /* initial smashmargin */
  oldissquashdelta = issquashdelta, /* initial issquashdelta */   oldissmashdelta = issmashdelta, /* initial issmashdelta */
    oldisexplicitsmash = isexplicitsmash, /* initial isexplicitsmash */
    oldisscripted = isscripted, /* initial isscripted */
  *oldworkingparam = workingparam; /* initial working parameter */   *oldworkingparam = workingparam; /* initial working parameter */
 subraster *oldworkingbox = workingbox, /* initial working box */  subraster *oldworkingbox = workingbox, /* initial working box */
  *oldleftexpression = leftexpression; /*left half rasterized so far*/   *oldleftexpression = leftexpression; /*left half rasterized so far*/
Line 4679  initialization Line 7225  initialization
 recurlevel++; /* wind up one more recursion level*/  recurlevel++; /* wind up one more recursion level*/
 leftexpression = NULL; /* no leading left half yet */  leftexpression = NULL; /* no leading left half yet */
 isreplaceleft = 0; /* reset replaceleft flag */  isreplaceleft = 0; /* reset replaceleft flag */
   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 */
 if ( msgfp!=NULL && msglevel >= 29 ) /*display expression for debugging*/  rastlift = 0; /* reset global rastraise() lift */
  { fprintf(msgfp,  if ( msgfp!=NULL && msglevel >= 9 ) { /*display expression for debugging*/
    "rasterize> recursion level=%d, size=%d,\n\texpression=\"%s\"\n",   fprintf(msgfp,
    recurlevel,size,(expression==NULL?"null":expression)); fflush(msgfp); }   "rasterize> recursion#%d, size=%d,\n\texpression=\"%s\"\n",
    recurlevel,size,(expression==NULL?"<null>":expression)); fflush(msgfp); }
 if ( expression == NULL ) goto end_of_job; /* nothing given to do */  if ( expression == NULL ) goto end_of_job; /* nothing given to do */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 preocess optional $-terminated preamble preceding expression  preocess optional $-terminated preamble preceding expression
Line 4700  build up raster one character (or subexp Line 7248  build up raster one character (or subexp
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 while ( 1 )  while ( 1 )
   {    {
     /* --- kludge for \= cyrillic ligature --- */
     isligature = 0; /* no ligature found yet */
     family = fontinfo[fontnum].family; /* current font family */
     if ( family == CYR10 ) /* may have cyrillic \= ligature */
      if ( (ligdef = get_ligature(expression,family)) /*check for any ligature*/
      >=    0 ) /* got some ligature */
       if ( memcmp(symtable[ligdef].symbol,"\\=",2) == 0 ) /* starts with \= */
        isligature = 1; /* signal \= ligature */
   /* --- get next character/token or subexpression --- */    /* --- get next character/token or subexpression --- */
     subexprptr = expression; /* ptr within expression to subexpr*/
   expression = texsubexpr(expression,chartoken,0,LEFTBRACES,RIGHTBRACES,1,1);    expression = texsubexpr(expression,chartoken,0,LEFTBRACES,RIGHTBRACES,1,1);
   subexpr = chartoken; /* "local" copy of chartoken ptr */    subexpr = chartoken; /* "local" copy of chartoken ptr */
   leftsymdef = NULL; /* no character identified yet */    leftsymdef = NULL; /* no character identified yet */
   sp = NULL; /* no subraster yet */    sp = NULL; /* no subraster yet */
   size = fontsize; /* in case reset by \tiny, etc */    size = fontsize; /* in case reset by \tiny, etc */
     /*isleftscript = isdelimscript;*/ /*was preceding term scripted delim*/
     wasscripted = isscripted; /* true if preceding token scripted*/
     wasdelimscript = isdelimscript; /* preceding \right delim scripted */
     if(1)isscripted = 0; /* no subscripted expression yet */
     isdelimscript = 0; /* reset \right delim scripted flag*/
   /* --- debugging output --- */    /* --- debugging output --- */
   if ( msgfp!=NULL && msglevel >= 999 ) /* display chartoken for debugging */    if ( msgfp!=NULL && msglevel >= 9 ) { /* display chartoken for debugging */
     { fprintf(msgfp,"rasterize> recursion level=%d, atom#%d = \"%s\"\n",     fprintf(msgfp,
       recurlevel,natoms+1,chartoken); fflush(msgfp); }     "rasterize> recursion#%d,atom#%d=\"%s\" (isligature=%d,isleftscript=%d)\n",
      recurlevel,natoms+1,chartoken,isligature,isleftscript); fflush(msgfp); }
   if ( expression == NULL /* no more tokens */    if ( expression == NULL /* no more tokens */
   &&   *subexpr == '\000' ) break; /* and this token empty */    &&   *subexpr == '\000' ) break; /* and this token empty */
   if ( *subexpr == '\000' ) break; /* enough if just this token empty */    if ( *subexpr == '\000' ) break; /* enough if just this token empty */
Line 4720  while ( 1 ) Line 7283  while ( 1 )
   else /* --- single-character atomic token --- */    else /* --- single-character atomic token --- */
    if ( !isthischar(*subexpr,SCRIPTS) ) /* scripts handled below */     if ( !isthischar(*subexpr,SCRIPTS) ) /* scripts handled below */
     {      {
     /* --- first look up mathchardef for atomic token in table --- */      /* --- first check for opening $ in \text{ if $n-m$ even} --- */
     if ( (leftsymdef=symdef=get_symdef(chartoken)) /*mathchardef for token*/      if ( istextmode /* we're in \text mode */
     ==  NULL ) /* lookup failed */      &&   *subexpr=='$' && subexpr[1]=='\000' ) { /* and have an opening $ */
      { char literal[512] = "[?]"; /*display for unrecognized literal*/       char *endptr=NULL, mathexpr[MAXSUBXSZ+1]; /* $expression$ in \text{ }*/
        int  wastext = istext; /* error display in default mode */       int  exprlen = 0; /* length of $expression$ */
        if ( msgfp!=NULL && msglevel >= 29 ) /* display unrecognized symbol */       int  textfontnum = fontnum; /* current text font number */
        /*if ( (endptr=strrchr(expression,'$')) != NULL )*/ /*ptr to closing $*/
        if ( (endptr=strchr(expression,'$')) != NULL ) /* ptr to closing $ */
          exprlen = (int)(endptr-expression); /* #chars preceding closing $ */
        else { /* no closing $ found */
          exprlen = strlen(expression); /* just assume entire expression */
          endptr = expression + (exprlen-1); } /*and push expression to '\000'*/
        exprlen = min2(exprlen,MAXSUBXSZ); /* don't overflow mathexpr[] */
        if ( exprlen > 0 ) { /* have something between $$ */
          memcpy(mathexpr,expression,exprlen); /*local copy of math expression*/
          mathexpr[exprlen] = '\000'; /* null-terminate it */
          fontnum = 0; /* set math mode */
          sp = rasterize(mathexpr,size); /* and rasterize $expression$ */
          fontnum = textfontnum; } /* set back to text mode */
        expression = endptr+1; /* push expression past closing $ */
        } /* --- end-of-if(istextmode&&*subexpr=='$') --- */
       else
        /* --- otherwise, look up mathchardef for atomic token in table --- */
        if ( (leftsymdef=symdef=get_symdef(chartoken)) /*mathchardef for token*/
        ==  NULL ) /* lookup failed */
         { char literal[512] = "[?]"; /*display for unrecognized literal*/
           int  oldfontnum = fontnum; /* error display in default mode */
           if ( msgfp!=NULL && msglevel >= 29 ) /* display unrecognized symbol*/
  { fprintf(msgfp,"rasterize> get_symdef() failed for \"%s\"\n",   { fprintf(msgfp,"rasterize> get_symdef() failed for \"%s\"\n",
    chartoken); fflush(msgfp); }     chartoken); fflush(msgfp); }
        sp = (subraster *)NULL; /* init to signal failure */          sp = (subraster *)NULL; /* init to signal failure */
        if ( warninglevel < 1 ) continue; /* warnings not wanted */          if ( warninglevel < 1 ) continue; /* warnings not wanted */
        istext = 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 */
        istext = wastext; /* reset text mode */          fontnum = oldfontnum; /* reset font family */
        if ( sp == (subraster *)NULL ) continue; } /*flush if rasterize fails*/          if ( sp == (subraster *)NULL ) continue; }/*flush if rasterize fails*/
     else /* --- check if we have special handler to process this token --- */       else /* --- check if we have special handler to process this token --- */
      if ( symdef->handler != NULL ) /* have a handler for this token */        if ( symdef->handler != NULL ) /* have a handler for this token */
       { int arg1=symdef->charnum, arg2=symdef->family, arg3=symdef->class;         { int arg1=symdef->charnum, arg2=symdef->family, arg3=symdef->class;
  if ( (sp = (subraster *) /* returned void* is subraster* */           if ( (sp = (subraster *) /* returned void* is subraster* */
  (*(symdef->handler))(&expression,size,prevsp,arg1,arg2,arg3))== NULL )   (*(symdef->handler))(&expression,size,prevsp,arg1,arg2,arg3))==NULL)
   continue; } /* flush token if handler failed */     continue; } /* flush token if handler failed */
      else /* --- no handler, so just get subraster for this character --- */        else /* --- no handler, so just get subraster for this character --- */
       if ( !isstring ) /* rasterizing */         if ( !isstring ) /* rasterizing */
  { if ( (sp=get_charsubraster(symdef,size)) /* get subraster */   { if ( isligature ) /* found a ligature */
      expression = subexprptr + strlen(symdef->symbol); /*push past it*/
     if ( (sp=get_charsubraster(symdef,size)) /* get subraster */
   ==  NULL )  continue; } /* flush token if failed */    ==  NULL )  continue; } /* flush token if failed */
       else /* constructing ascii string */         else /* constructing ascii string */
  { char *symbol = symdef->symbol; /* symbol for ascii string */   { char *symbol = symdef->symbol; /* symbol for ascii string */
   int symlen = (symbol!=NULL?strlen(symbol):0); /*#chars in symbol*/    int symlen = (symbol!=NULL?strlen(symbol):0); /*#chars in symbol*/
   if ( symlen < 1 ) continue; /* no symbol for ascii string */    if ( symlen < 1 ) continue; /* no symbol for ascii string */
Line 4760  while ( 1 ) Line 7347  while ( 1 )
   sp->baseline = 1; /* default (should be unused) */    sp->baseline = 1; /* default (should be unused) */
   strcpy((char *)((sp->image)->pixmap),symbol); /* copy symbol */    strcpy((char *)((sp->image)->pixmap),symbol); /* copy symbol */
   /*((char *)((sp->image)->pixmap))[symlen] = '\000';*/ } /*null*/    /*((char *)((sp->image)->pixmap))[symlen] = '\000';*/ } /*null*/
     } /* --- end-of-if/else ... if/else --- */      } /* --- end-of-if(!isthischar(*subexpr,SCRIPTS)) --- */
   /* --- handle any super/subscripts following symbol or subexpression --- */    /* --- handle any super/subscripts following symbol or subexpression --- */
   sp = rastlimits(&expression,size,sp);    sp = rastlimits(&expression,size,sp);
     isleftscript = (wasscripted||wasdelimscript?1:0);/*preceding term scripted*/
   /* --- debugging output --- */    /* --- debugging output --- */
   if ( msgfp!=NULL && msglevel >= 999 ) /* display raster for debugging */    if ( msgfp!=NULL && msglevel >= 9 ) { /* display raster for debugging */
     { fprintf(msgfp,"rasterize> recursion level=%d, atom#%d%s\n",     fprintf(msgfp,"rasterize> recursion#%d,atom#%d%s\n",
       recurlevel,natoms+1,(sp==NULL?" = null":"..."));     recurlevel,natoms+1,(sp==NULL?" = <null>":"..."));
       if(sp!=NULL) type_raster(sp->image,msgfp); /* display raster */     if ( msglevel >= 9 ) fprintf(msgfp,
       fflush(msgfp); } /* flush msgfp buffer */      "  isleftscript=%d is/wasscripted=%d,%d is/wasdelimscript=%d,%d\n",
       isleftscript,isscripted,wasscripted,isdelimscript,wasdelimscript);
      if ( msglevel >= 99 )
       if(sp!=NULL) type_raster(sp->image,msgfp); /* display raster */
      fflush(msgfp); } /* flush msgfp buffer */
   /* --- accumulate atom or parenthesized subexpression --- */    /* --- accumulate atom or parenthesized subexpression --- */
   if ( natoms < 1 /* nothing previous to concat */    if ( natoms < 1 /* nothing previous to concat */
   ||   expraster == NULL /* or previous was complete error */    ||   expraster == NULL /* or previous was complete error */
   ||   isreplaceleft ) /* or we're replacing previous */    ||   isreplaceleft ) /* or we're replacing previous */
     { expraster = subrastcpy(sp); /* so just copy static CHARASTER */      { if ( 1 && expraster!=NULL ) /* probably replacing left */
    delete_subraster(expraster); /* so first free original left */
         expraster = subrastcpy(sp); /* copy static CHARASTER or left */
       isreplaceleft = 0; } /* reset replacement flag */        isreplaceleft = 0; } /* reset replacement flag */
   else /*we've already built up atoms so...*/    else /*we've already built up atoms so...*/
    if ( sp != NULL ) /* ...if we have a new component */     if ( sp != NULL ) { /* ...if we have a new term */
     expraster = rastcat(expraster,sp,1); /* concat new one, free previous */      int prevsmashmargin = smashmargin; /* save current smash margin */
       if ( isleftscript ) { /* don't smash against scripts */
        isdelimscript = 0; /* reset \right delim scripted flag*/
        if ( !isexplicitsmash ) smashmargin = 0; } /* signal no smash wanted */
       expraster = rastcat(expraster,sp,1); /* concat new term, free previous */
       smashmargin = prevsmashmargin; } /* restore current smash margin */
   delete_subraster(prevsp); /* free prev (if not a CHARASTER) */    delete_subraster(prevsp); /* free prev (if not a CHARASTER) */
   prevsp = sp; /* current becomes previous */    prevsp = sp; /* current becomes previous */
   leftexpression = expraster; /* left half rasterized so far */    leftexpression = expraster; /* left half rasterized so far */
Line 4796  end_of_job: Line 7395  end_of_job:
       if ( expraster != (subraster *)NULL ) /* i.e., if natoms>0 */        if ( expraster != (subraster *)NULL ) /* i.e., if natoms>0 */
  type_raster(expraster->image,msgfp); /* display completed raster */   type_raster(expraster->image,msgfp); /* display completed raster */
       fflush(msgfp); } /* flush msgfp buffer */        fflush(msgfp); } /* flush msgfp buffer */
     /* --- set final raster buffer --- */
     if ( 1 && expraster != (subraster *)NULL ) /* have an expression */
       { int type = expraster->type; /* type of constructed image */
         if ( type != FRACRASTER ) /* leave \frac alone */
    expraster->type = IMAGERASTER; /* set type to constructed image */
         if ( istextmode ) /* but in text mode */
           expraster->type = blanksignal; /* set type to avoid smash */
         expraster->size = fontsize; } /* set original input font size */
   /* --- restore flags/values to original saved values --- */    /* --- restore flags/values to original saved values --- */
   istext = wastext; /* text mode reset */  
   isstring = wasstring; /* string mode reset */    isstring = wasstring; /* string mode reset */
   isdisplaystyle = wasdisplaystyle; /* displaystyle mode reset */    isdisplaystyle = wasdisplaystyle; /* displaystyle mode reset */
     fontnum = oldfontnum; /* font family reset */
   fontsize = oldfontsize; /* fontsize reset */    fontsize = oldfontsize; /* fontsize reset */
   displaysize = olddisplaysize; /* \displaystyle size reset */    displaysize = olddisplaysize; /* \displaystyle size reset */
   shrinkfactor = oldshrinkfactor; /* shrinkfactor reset */    shrinkfactor = oldshrinkfactor; /* shrinkfactor reset */
   squashmargin = oldsquashmargin; /* squashmargin reset */    smashmargin = oldsmashmargin; /* smashmargin reset */
   issquashdelta = oldissquashdelta; /* issquashdelta reset */    issmashdelta = oldissmashdelta; /* issmashdelta reset */
     isexplicitsmash = oldisexplicitsmash; /* isexplicitsmash reset */
     isscripted = oldisscripted; /* isscripted reset */
   workingparam = oldworkingparam; /* working parameter reset */    workingparam = oldworkingparam; /* working parameter reset */
   workingbox = oldworkingbox; /* working box reset */    workingbox = oldworkingbox; /* working box reset */
   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 --- */
   if ( 1 && expraster != (subraster *)NULL ) /* have an expression */  
     { expraster->type = IMAGERASTER; /* set type to constructed image */  
       expraster->size = fontsize; } /* set original input font size */  
   return ( expraster );    return ( expraster );
 } /* --- end-of-function rasterize() --- */  } /* --- end-of-function rasterize() --- */
   
Line 4827  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 4852  int explen = strlen(expression); /* tota Line 7459  int explen = strlen(expression); /* tota
 int isescape = 0, /* true if parens \escaped */  int isescape = 0, /* true if parens \escaped */
  isrightdot = 0, /* true if right paren is \right. */   isrightdot = 0, /* true if right paren is \right. */
  isleftdot = 0; /* true if left paren is \left. */   isleftdot = 0; /* true if left paren is \left. */
 char left[16], right[16]; /* parens enclosing expresion */  char left[32], right[32]; /* parens enclosing expresion */
 char noparens[8192]; /* get subexpr without parens */  char noparens[MAXSUBXSZ+1]; /* get subexpr without parens */
 subraster *rasterize(), *sp=NULL; /* rasterize what's between ()'s */  subraster *rasterize(), *sp=NULL; /* rasterize what's between ()'s */
 int isheight = 1; /*true=full height, false=baseline*/  int isheight = 1; /*true=full height, false=baseline*/
 int height, /* height of rasterized noparens[] */  int height, /* height of rasterized noparens[] */
  baseline; /* and its baseline */   baseline; /* and its baseline */
 int family = CMEX10; /* family for paren chars */  int family = /*CMSYEX*/ CMEX10; /* family for paren chars */
 subraster *get_delim(), *lp=NULL, *rp=NULL; /* left and right paren chars */  subraster *get_delim(), *lp=NULL, *rp=NULL; /* left and right paren chars */
 subraster *rastcat(); /* concatanate subrasters */  subraster *rastcat(); /* concatanate subrasters */
 int delete_subraster(); /*in case of error after allocation*/  int delete_subraster(); /*in case of error after allocation*/
Line 4871  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 4950  Allocations and Declarations Line 7557  Allocations and Declarations
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 subraster *rastscripts(), *rastdispmath(), /*one of these will do the work*/  subraster *rastscripts(), *rastdispmath(), /*one of these will do the work*/
  *rastcat(), /* may need to concat scripts */   *rastcat(), /* may need to concat scripts */
  *scriptsp = basesp; /* and this will become the result */   *rasterize(), /* may need to construct dummy base*/
    *scriptsp = basesp, /* and this will become the result */
    *dummybase = basesp; /* for {}_i construct a dummy base */
 int isdisplay = (-1); /* set 1 for displaystyle, else 0 */  int isdisplay = (-1); /* set 1 for displaystyle, else 0 */
 int oldsquashmargin = squashmargin; /* save original squashmargin */  int oldsmashmargin = smashmargin; /* save original smashmargin */
 int type_raster(); /* display debugging output */  int type_raster(); /* display debugging output */
   int delete_subraster(); /* free dummybase, if necessary */
   int rastsmashcheck(); /* check if okay to smash scripts */
 /* --- to check for \limits or \nolimits preceding scripts --- */  /* --- to check for \limits or \nolimits preceding scripts --- */
 char *texchar(), *exprptr=*expression, limtoken[255]; /*check for \limits*/  char *texchar(), *exprptr=*expression, limtoken[255]; /*check for \limits*/
 int toklen=0; /* strlen(limtoken) */  int toklen=0; /* strlen(limtoken) */
Line 4964  determine whether or not to use displaym Line 7575  determine whether or not to use displaym
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 scriptlevel++; /* first, increment subscript level*/  scriptlevel++; /* first, increment subscript level*/
 *limtoken = '\000'; /* no token yet */  *limtoken = '\000'; /* no token yet */
   isscripted = 0; /* signal term not (text) scripted */
 if ( msgfp!=NULL && msglevel>=999 )  if ( msgfp!=NULL && msglevel>=999 )
  { fprintf(msgfp,"rastlimits> scriptlevel#%d exprptr=%.48s\n",   { fprintf(msgfp,"rastlimits> scriptlevel#%d exprptr=%.48s\n",
    scriptlevel,(exprptr==NULL?"null":exprptr));  fflush(msgfp); }     scriptlevel,(exprptr==NULL?"null":exprptr));  fflush(msgfp); }
 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 */
   if ( memcmp("\\limits",limtoken,toklen) == 0     /* may be \limits */    if ( memcmp("\\limits",limtoken,toklen) == 0     /* may be \limits */
   ||   memcmp("\\nolimits",limtoken,toklen) == 0 ) /* or may be \nolimits */    ||   memcmp("\\nolimits",limtoken,toklen) == 0 ) /* or may be \nolimits */
    if ( (tokdef= get_symdef(limtoken)) /* look up token to be sure */     if ( (tokdef= get_symdef(limtoken)) /* look up token to be sure */
    !=   NULL ) /* found token in table */     !=   NULL ) { /* found token in table */
     if ( strcmp("\\limits",tokdef->symbol) == 0 )  /* found \limits */      if ( strcmp("\\limits",tokdef->symbol) == 0 )  /* found \limits */
       isdisplay = 1; /* so explicitly set displaymath */        isdisplay = 1; /* so explicitly set displaymath */
     else /* wasn't \limits */      else /* wasn't \limits */
       if ( strcmp("\\nolimits",tokdef->symbol) == 0 ) /* found \nolimits */        if ( strcmp("\\nolimits",tokdef->symbol) == 0 ) /* found \nolimits */
  isdisplay = 0; /* so explicitly reset displaymath */   isdisplay = 0; } /* so explicitly reset displaymath */
 /* --- see if we found \[no]limits --- */  /* --- see if we found \[no]limits --- */
 if ( isdisplay != (-1) ) /* explicit directive found */  if ( isdisplay != (-1) ) /* explicit directive found */
   *expression = exprptr; /* so bump expression past it */    *expression = exprptr; /* so bump expression past it */
 else /* noexplicit directive */  else /* noexplicit directive */
   { isdisplay = 0; /* init displaymath flag off */    { isdisplay = 0; /* init displaymath flag off */
     if ( isdisplaystyle ) /* we're in displaystyle math mode */      if ( isdisplaystyle ) { /* we're in displaystyle math mode */
       if ( isdisplaystyle >= 5 ) /* and mode irrevocably forced true */        if ( isdisplaystyle >= 5 ) /* and mode irrevocably forced true */
  { if ( class!=OPENING && class!=CLOSING ) /*don't force ('s and )'s*/   { if ( class!=OPENING && class!=CLOSING ) /*don't force ('s and )'s*/
     isdisplay = 1; } /* set flag if mode forced true */      isdisplay = 1; } /* set flag if mode forced true */
Line 5002  else     /* noexplicit directive */ Line 7613  else     /* noexplicit directive */
     isdisplay = 1; } /* set flag if mode forced true */      isdisplay = 1; } /* set flag if mode forced true */
        else /* determine mode from base symbol */         else /* determine mode from base symbol */
  if ( class == DISPOPER ) /* it's a displaystyle operator */   if ( class == DISPOPER ) /* it's a displaystyle operator */
   isdisplay = 1; } /* so set flag */    isdisplay = 1; } } /* so set flag */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 dispatch call to create sub/superscripts  dispatch call to create sub/superscripts
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 if ( isdisplay ) /* scripts above/below base symbol */  if ( isdisplay ) /* scripts above/below base symbol */
   scriptsp = rastdispmath(expression,size,basesp); /* everything all done */    scriptsp = rastdispmath(expression,size,basesp); /* everything all done */
 else /* scripts alongside base symbol */  else { /* scripts alongside base symbol */
   if ( (scriptsp=rastscripts(expression,size,basesp)) == NULL ) /*no scripts*/    if ( dummybase == NULL ) /* no base symbol preceding scripts*/
       dummybase = rasterize("\\rule0{10}",size); /*guess a typical base symbol*/
     issmashokay = 1; /*haven't found a no-smash char yet*/
     if((scriptsp=rastscripts(expression,size,dummybase)) == NULL) /*no scripts*/
     scriptsp = basesp; /* so just return unscripted symbol*/      scriptsp = basesp; /* so just return unscripted symbol*/
   else /* symbols followed by scripts */    else { /* symbols followed by scripts */
       isscripted = 1; /*signal current term text-scripted*/
     if ( basesp != NULL ) /* have base symbol */      if ( basesp != NULL ) /* have base symbol */
      { squashmargin = 0; /* don't squash script */       { /*if(0)smashmargin = 0;*/ /*don't smash script (doesn't work)*/
        scriptsp = rastcat(basesp,scriptsp,2); /*concat scripts to base sym*/         /*scriptsp = rastcat(basesp,scriptsp,2);*//*concat scripts to base sym*/
        scriptsp->type = IMAGERASTER; /* flip type of composite object */         /* --- smash (or just concat) script raster against base symbol --- */
        scriptsp->size = size; } /* and set font size */         if ( !issmashokay ) /* don't smash leading - */
            if ( !isexplicitsmash ) scriptsp->type = blanksignal; /*don't smash*/
          scriptsp = rastcat(basesp,scriptsp,3); /*concat scripts to base sym*/
          if(1) scriptsp->type = IMAGERASTER; /* flip type of composite object */
          /* --- smash (or just concat) scripted term to stuff to its left --- */
          issmashokay = 1; /* okay to smash base expression */
          if ( 0 && smashcheck > 1 ) /* smashcheck=2 to check base */
            /* note -- we _don't_ have base expression available to check */
            issmashokay = rastsmashcheck(*expression); /*check if okay to smash*/
          if ( !issmashokay ) /* don't smash leading - */
            if ( !isexplicitsmash ) scriptsp->type = blanksignal; /*don't smash*/
          scriptsp->size = size; } } } /* and set font size */
 end_of_job:  end_of_job:
   squashmargin = oldsquashmargin; /* reset original squashmargin */    smashmargin = oldsmashmargin; /* reset original smashmargin */
     if ( dummybase != basesp ) delete_subraster(dummybase); /*free work area*/
   if ( msgfp!=NULL && msglevel>=99 )    if ( msgfp!=NULL && msglevel>=99 )
     { fprintf(msgfp,"rastlimits> scriptlevel#%d returning %s\n",      { fprintf(msgfp,"rastlimits> scriptlevel#%d returning %s\n",
  scriptlevel,(scriptsp==NULL?"null":"..."));   scriptlevel,(scriptsp==NULL?"null":"..."));
Line 5039  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 5077  int szval = min2(max2(size,0),LARGESTSIZ Line 7704  int szval = min2(max2(size,0),LARGESTSIZ
 /*int istweak = 1;*/ /* true to tweak script positioning */  /*int istweak = 1;*/ /* true to tweak script positioning */
 int rastput(); /*put scripts in constructed raster*/  int rastput(); /*put scripts in constructed raster*/
 int delete_subraster(); /* free work areas */  int delete_subraster(); /* free work areas */
   int rastsmashcheck(); /* check if okay to smash scripts */
 int pixsz = 1; /*default #bits per pixel, 1=bitmap*/  int pixsz = 1; /*default #bits per pixel, 1=bitmap*/
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 Obtain subscript and/or superscript expressions, and rasterize them/it  Obtain subscript and/or superscript expressions, and rasterize them/it
Line 5096  issub  = (subsp != (subraster *)NULL); / Line 7724  issub  = (subsp != (subraster *)NULL); /
 issup  = (supsp != (subraster *)NULL); /* true if we have superscript */  issup  = (supsp != (subraster *)NULL); /* true if we have superscript */
 isboth = (issub && issup); /* true if we have both */  isboth = (issub && issup); /* true if we have both */
 if (!issub && !issup) goto end_of_job; /* quit if we have neither */  if (!issub && !issup) goto end_of_job; /* quit if we have neither */
   /* --- check for leading no-smash chars (if enabled) --- */
   issmashokay = 0; /* default, don't smash scripts */
   if ( smashcheck > 0 ) { /* smash checking wanted */
    issmashokay = 1; /*haven't found a no-smash char yet*/
    if ( issub ) /* got a subscript */
     issmashokay = rastsmashcheck(subscript); /* check if okay to smash */
    if ( issmashokay ) /* clean sub, so check sup */
     if ( issup ) /* got a superscript */
      issmashokay = rastsmashcheck(supscript); /* check if okay to smash */
    } /* --- end-of-if(smashcheck>0) --- */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 get height, width, baseline of scripts,  and height, baseline of base symbol  get height, width, baseline of scripts,  and height, baseline of base symbol
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
Line 5136  if ( !issub )    /* we only have a super Line 7774  if ( !issub )    /* we only have a super
  supht+vabove-bdescend); /* sup's bot above base symbol bot */   supht+vabove-bdescend); /* sup's bot above base symbol bot */
     baseline = height-1; } /*sup's baseline at bottom of raster*/      baseline = height-1; } /*sup's baseline at bottom of raster*/
 /* --- subscript only --- */  /* --- subscript only --- */
 if ( !issup ) /* we only have a subscript */  if ( !issup ) { /* we only have a subscript */
   if ( subht > sdescend ) /*sub can descend below base bot...*/    if ( subht > sdescend ) /*sub can descend below base bot...*/
     { height = subht; /* ...without extra space on top */      { height = subht; /* ...without extra space on top */
       baseline = height-(sdescend+1); /* sub's bot below base symbol bot */        baseline = height-(sdescend+1); /* sub's bot below base symbol bot */
       baseline = min2(baseline,max2(baseln-vbelow,0)); }/*top below base top*/        baseline = min2(baseline,max2(baseln-vbelow,0)); }/*top below base top*/
   else /* sub's top will be below baseln */    else /* sub's top will be below baseln */
     { height = sdescend+1; /* sub's bot below base symbol bot */      { height = sdescend+1; /* sub's bot below base symbol bot */
       baseline = 0; } /* sub's baseline at top of raster */        baseline = 0; } } /* sub's baseline at top of raster */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 construct raster with superscript over subscript  construct raster with superscript over subscript
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
Line 5181  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 5224  if (!issub && !issup) goto end_of_job; / Line 7862  if (!issub && !issup) goto end_of_job; /
 stack operator and its script(s)  stack operator and its script(s)
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 /* --- stack superscript atop operator --- */  /* --- stack superscript atop operator --- */
 if ( issup ) /* we have a superscript */  if ( issup ) { /* we have a superscript */
  if ( sp == NULL ) /* but no base expression */   if ( sp == NULL ) /* but no base expression */
   sp = supsp; /* so just use superscript */    sp = supsp; /* so just use superscript */
  else /* have base and superscript */   else /* have base and superscript */
   if ( (sp=rastack(sp,supsp,1,vspace,1,3)) /* stack supsp atop base sp */    if ( (sp=rastack(sp,supsp,1,vspace,1,3)) /* stack supsp atop base sp */
   ==   NULL ) goto end_of_job; /* and quit if failed */    ==   NULL ) goto end_of_job; } /* and quit if failed */
 /* --- stack operator+superscript atop subscript --- */  /* --- stack operator+superscript atop subscript --- */
 if ( issub ) /* we have a subscript */  if ( issub ) { /* we have a subscript */
  if ( sp == NULL ) /* but no base expression */   if ( sp == NULL ) /* but no base expression */
   sp = subsp; /* so just use subscript */    sp = subsp; /* so just use subscript */
  else /* have base and subscript */   else /* have base and subscript */
   if ( (sp=rastack(subsp,sp,2,vspace,1,3)) /* stack sp atop base subsp */    if ( (sp=rastack(subsp,sp,2,vspace,1,3)) /* stack sp atop base subsp */
   ==   NULL ) goto end_of_job; /* and quit if failed */    ==   NULL ) goto end_of_job; } /* and quit if failed */
 sp->type = IMAGERASTER; /* flip type of composite object */  sp->type = IMAGERASTER; /* flip type of composite object */
 sp->size = size; /* and set font size */  sp->size = size; /* and set font size */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
Line 5255  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)
  * ildelim (I) int containing ldelims index of   * ildelim (I) int containing ldelims[] index of
  * left delimiter   * left delimiter
  * arg2 (I) int unused   * arg2 (I) int unused
  * arg3 (I) int unused   * arg3 (I) int unused
Line 5284  int family=CMSYEX,   /* get_delim() fami Line 7922  int family=CMSYEX,   /* get_delim() fami
  height=0, rheight=0, /* subexpr, right delim height */   height=0, rheight=0, /* subexpr, right delim height */
  margin=(size+1), /* delim height margin over subexpr*/   margin=(size+1), /* delim height margin over subexpr*/
  opmargin=(5); /* extra margin for \int,\sum,\etc */   opmargin=(5); /* extra margin for \int,\sum,\etc */
 char subexpr[8192]; /* chars between \left...\right */  char /* *texleft(),*/ subexpr[MAXSUBXSZ+1];/*chars between \left...\right*/
 char *texchar(), /* get delims after \left,\right */  char *texchar(), /* get delims after \left,\right */
  ldelim[256]=".", rdelim[256]="."; /* delims following \left,\right */   ldelim[256]=".", rdelim[256]="."; /* delims following \left,\right */
 char *strtexchr(), *pleft, *pright; /*locate \right matching our \left*/  char *strtexchr(), *pleft, *pright; /*locate \right matching our \left*/
 int isleftdot=0, isrightdot=0; /* true if \left. or \right. */  int isleftdot=0, isrightdot=0; /* true if \left. or \right. */
   int isleftscript=0, isrightscript=0; /* true if delims are scripted */
 int sublen=0; /* strlen(subexpr) */  int sublen=0; /* strlen(subexpr) */
 int idelim=0; /* 1=left,2=right */  int idelim=0; /* 1=left,2=right */
   /* int gotldelim = 0; */ /* true if ildelim given by caller */
 int delete_subraster(); /* free subraster if rastleft fails*/  int delete_subraster(); /* free subraster if rastleft fails*/
 int wasdisplaystyle = isdisplaystyle; /* save current displaystyle */  int wasdisplaystyle = isdisplaystyle; /* save current displaystyle */
   int istextleft=0, istextright=0; /* true for non-displaystyle delims*/
 /* --- recognized delimiters --- */  /* --- recognized delimiters --- */
 static char left[16]="\\left", right[16]="\\right"; /* tex delimiters */  static char left[16]="\\left", right[16]="\\right"; /* tex delimiters */
 static char *ldelims[] = {  static char *ldelims[] = {
Line 5329  static char *xto[] =   /* ...to this ins Line 7970  static char *xto[] =   /* ...to this ins
      "<", /* \langle to < */       "<", /* \langle to < */
      ">", /* \rangle to > */       ">", /* \rangle to > */
      NULL } ; /* --- end-of-xto[] --- */       NULL } ; /* --- end-of-xto[] --- */
   /* --- non-displaystyle delimiters --- */
   static char *textdelims[] = /* these delims _aren't_ display */
      { "|", "=",
        "(", ")",
        "[", "]",
        "<", ">",
        "{", "}",
        "dbl", /* \lbrackdbl and \rbrackdbl */
        NULL } ; /* --- end-of-textdelims[] --- */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 initialization  initialization
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
Line 5336  initialization Line 7986  initialization
 if ( *(*expression) == '\000' ) goto end_of_job; /* nothing after \left */  if ( *(*expression) == '\000' ) goto end_of_job; /* nothing after \left */
 /* --- determine left delimiter, and set default \right. delimiter --- */  /* --- determine left delimiter, and set default \right. delimiter --- */
 if ( ildelim!=NOVALUE && ildelim>=1 ) /* called with explicit left delim */  if ( ildelim!=NOVALUE && ildelim>=1 ) /* called with explicit left delim */
   strcpy(ldelim,ldelims[ildelim]); /* so just get a local copy */   { strcpy(ldelim,ldelims[ildelim]); /* so just get a local copy */
      /* gotldelim = 1; */ } /* and set flag that we got it */
 else /* trapped \left without delim */  else /* trapped \left without delim */
  { skipwhite(*expression); /* interpret \left ( as \left( */   { skipwhite(*expression); /* interpret \left ( as \left( */
    *expression = texchar(*expression,ldelim); } /*pull delim from expression*/     if ( *(*expression) == '\000' ) /* end-of-string after \left */
         goto end_of_job; /* so return NULL */
      *expression = texchar(*expression,ldelim); /*pull delim from expression*/
      if ( *expression == NULL /* probably invalid end-of-string */
      ||   *ldelim == '\000' ) goto end_of_job; } /* no delimiter */
 strcpy(rdelim,"."); /* init default \right. delim */  strcpy(rdelim,"."); /* init default \right. delim */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 locate \right balancing our opening \left  locate \right balancing our opening \left
Line 5364  if ( (pright=strtexchr(*expression,right Line 8019  if ( (pright=strtexchr(*expression,right
 push past \left(_a^b sub/superscripts, if present  push past \left(_a^b sub/superscripts, if present
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 pleft = *expression; /*reset pleft after opening \left( */  pleft = *expression; /*reset pleft after opening \left( */
 /*lp=*/ rastlimits(expression,size,lp); /*dummy call push expression past b*/  if ( (lp=rastlimits(expression,size,lp)) /*dummy call push expression past b*/
   !=   NULL ) /* found actual _a^b scripts, too */
     { delete_subraster(lp); /* but we don't need them */
       lp = NULL; } /* reset pointer, too */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 get \right delimiter and subexpression between \left...\right, xlate delims  get \right delimiter and subexpression between \left...\right, xlate delims
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
Line 5384  else {     /* have explicit matching \ri Line 8042  else {     /* have explicit matching \ri
 /* --- get subexpression between \left...\right --- */  /* --- get subexpression between \left...\right --- */
 if ( sublen < 1 ) goto end_of_job; /* nothing between delimiters */  if ( sublen < 1 ) goto end_of_job; /* nothing between delimiters */
 subexpr[sublen] = '\000'; /* and null-terminate it */  subexpr[sublen] = '\000'; /* and null-terminate it */
   /* --- adjust margin for expressions containing \middle's --- */
   if ( strtexchr(subexpr,"\\middle") != NULL ) /* have enclosed \middle's */
     margin = 1; /* so don't "overwhelm" them */
 /* --- check for operator delimiter --- */  /* --- check for operator delimiter --- */
 for ( idelim=0; opdelims[idelim]!=NULL; idelim++ )  for ( idelim=0; opdelims[idelim]!=NULL; idelim++ )
   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 --- */  /* --- 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 */
   char *lrdelim  = (idelim==1? ldelim:rdelim); /* ldelim or rdelim */    char *lrdelim  = (idelim==1? ldelim:rdelim); /* ldelim or rdelim */
   int ix;  char *xdelim; /* xfrom[] and xto[] index, delim */    int ix;  char *xdelim; /* xfrom[] and xto[] index, delim */
Line 5399  for ( idelim=1; idelim<=2; idelim++ ) { Line 8060  for ( idelim=1; idelim<=2; idelim++ ) {
     if ( strcmp(lrdelim,xdelim) == 0 ) /* found delim to xlate */      if ( strcmp(lrdelim,xdelim) == 0 ) /* found delim to xlate */
       { strcpy(lrdelim,xto[ix]); /* replace with corresponding xto[]*/        { strcpy(lrdelim,xto[ix]); /* replace with corresponding xto[]*/
  break; } /* no need to check further */   break; } /* no need to check further */
     for( ix=0; (xdelim=textdelims[ix]) != NULL; ix++ )
       if ( strstr(lrdelim,xdelim) != 0 ) /* found textstyle delim */
         { if ( idelim == 1 ) /* if it's the \left one */
     istextleft = 1; /* set left textstyle flag */
    else istextright = 1; /* else set right textstyle flag */
    break; } /* no need to check further */
   } /* --- end-of-for(idelim) --- */    } /* --- end-of-for(idelim) --- */
 /* --- debugging --- */  /* --- debugging --- */
 if ( msgfp!=NULL && msglevel>=99 )  if ( msgfp!=NULL && msglevel>=99 )
Line 5419  rasterize delimiters, reset baselines, a Line 8086  rasterize delimiters, reset baselines, a
 isleftdot  = (strchr(ldelim,'.')!=NULL); /* true if \left. */  isleftdot  = (strchr(ldelim,'.')!=NULL); /* true if \left. */
 isrightdot = (strchr(rdelim,'.')!=NULL); /* true if \right. */  isrightdot = (strchr(rdelim,'.')!=NULL); /* true if \right. */
 /* --- get rasters for best-fit delim characters, add sub/superscripts --- */  /* --- get rasters for best-fit delim characters, add sub/superscripts --- */
 isdisplaystyle = 9; /* force \displaystyle */  isdisplaystyle = (istextleft?0:9); /* force \displaystyle */
 if ( !isleftdot ) /* if not \left. */  if ( !isleftdot ) /* if not \left. */
  { /* --- first get requested \left delimiter --- */   { /* --- first get requested \left delimiter --- */
    lp = get_delim(ldelim,rheight,family); /* get \left delim char */     lp = get_delim(ldelim,rheight,family); /* get \left delim char */
Line 5430  if ( !isleftdot )   /* if not \left. */ Line 8097  if ( !isleftdot )   /* if not \left. */
       if ( lheight > rheight ) /* got bigger delim than requested */        if ( lheight > rheight ) /* got bigger delim than requested */
  rheight = lheight-1; } /* make sure right delim matches */   rheight = lheight-1; } /* make sure right delim matches */
    /* --- then add on any sub/superscripts attached to \left( --- */     /* --- then add on any sub/superscripts attached to \left( --- */
    lp = rastlimits(&pleft,size,lp); } /*\left(_a^b and push pleft past b*/     lp = rastlimits(&pleft,size,lp); /*\left(_a^b and push pleft past b*/
 isdisplaystyle = 9; /* force \displaystyle */     isleftscript = isscripted; } /* check if left delim scripted */
   isdisplaystyle = (istextright?0:9); /* force \displaystyle */
 if ( !isrightdot ) /* and if not \right. */  if ( !isrightdot ) /* and if not \right. */
  { /* --- first get requested \right delimiter --- */   { /* --- first get requested \right delimiter --- */
    rp = get_delim(rdelim,rheight,family); /* get \right delim char */     rp = get_delim(rdelim,rheight,family); /* get \right delim char */
Line 5439  if ( !isrightdot )   /* and if not \righ Line 8107  if ( !isrightdot )   /* and if not \righ
    if ( rp != NULL ) /* if get_delim() succeeded */     if ( rp != NULL ) /* if get_delim() succeeded */
      rp->baseline = sp->baseline + ((rp->image)->height - height)/2;       rp->baseline = sp->baseline + ((rp->image)->height - height)/2;
    /* --- then add on any sub/superscripts attached to \right) --- */     /* --- then add on any sub/superscripts attached to \right) --- */
    rp = rastlimits(expression,size,rp); } /*\right)_c^d, expression past d*/     rp = rastlimits(expression,size,rp); /*\right)_c^d, expression past d*/
      isrightscript = isscripted; } /* check if right delim scripted */
 isdisplaystyle = wasdisplaystyle; /* original \displystyle default */  isdisplaystyle = wasdisplaystyle; /* original \displystyle default */
 /* --- check that we got delimiters --- */  /* --- check that we got delimiters --- */
 if ( 0 )  if ( 0 )
Line 5461  if ( sp != NULL )   /* succeeded or igno Line 8130  if ( sp != NULL )   /* succeeded or igno
     sp = rastcat(sp,rp,3); /* concat sp||rp and free sp,rp */      sp = rastcat(sp,rp,3); /* concat sp||rp and free sp,rp */
 /* --- back to caller --- */  /* --- back to caller --- */
 end_of_job:  end_of_job:
     isdelimscript = isrightscript; /* signal if right delim scripted */
   return ( sp );    return ( sp );
 } /* --- end-of-function rastleft() --- */  } /* --- end-of-function rastleft() --- */
   
   
 /* ==========================================================================  /* ==========================================================================
    * Function: rastright ( expression, size, basesp, ildelim, arg2, arg3 )
    * Purpose: ...\right handler, intercepts an unexpected/unbalanced \right
    * --------------------------------------------------------------------------
    * Arguments: expression (I) char **  to first char of null-terminated
    * string beginning with a \right
    * to be rasterized
    * size (I) int containing 0-7 default font size
    * basesp (I) subraster *  to character (or subexpression)
    * immediately preceding leading left{
    * (unused, but passed for consistency)
    * ildelim (I) int containing rdelims[] index of
    * right delimiter
    * arg2 (I) int unused
    * arg3 (I) int unused
    * --------------------------------------------------------------------------
    * Returns: ( subraster * ) ptr to subraster corresponding to subexpr,
    * or NULL for any parsing error
    * --------------------------------------------------------------------------
    * Notes:     o
    * ======================================================================= */
   /* --- entry point --- */
   subraster *rastright ( char **expression, int size, subraster *basesp,
    int ildelim, int arg2, int arg3 )
   {
   /* -------------------------------------------------------------------------
   Allocations and Declarations
   -------------------------------------------------------------------------- */
   subraster /* *rasterize(),*/ *sp=NULL; /*rasterize \right subexpr's*/
     if ( sp != NULL ) /* returning entire expression */
       {
         isreplaceleft = 1; /* set flag to replace left half*/
       }
   return ( sp );
   } /* --- end-of-function rastright() --- */
   
   
   /* ==========================================================================
    * Function: rastmiddle ( expression, size, basesp,  arg1, arg2, arg3 )
    * Purpose: \middle handler, returns subraster corresponding to
    * entire expression with \middle delimiter(s) sized to fit.
    * --------------------------------------------------------------------------
    * Arguments: expression (I/O) char **  to first char of null-terminated
    * string immediately following \middle to be
    * rasterized, and returning ptr immediately
    * to terminating null.
    * size (I) int containing 0-7 default font size
    * basesp (I) subraster *  to character (or subexpression)
    * immediately preceding \middle
    * (unused, but passed for consistency)
    * arg1 (I) int unused
    * arg2 (I) int unused
    * arg3 (I) int unused
    * --------------------------------------------------------------------------
    * Returns: ( subraster * ) ptr to subraster corresponding to expression,
    * or NULL for any parsing error
    * (expression ptr unchanged if error occurs)
    * --------------------------------------------------------------------------
    * Notes:     o
    * ======================================================================= */
   /* --- entry point --- */
   subraster *rastmiddle ( char **expression, int size, subraster *basesp,
    int arg1, int arg2, int arg3 )
   {
   /* -------------------------------------------------------------------------
   Allocations and Declarations
   -------------------------------------------------------------------------- */
   subraster *rasterize(), *sp=NULL, *subsp[32]; /*rasterize \middle subexpr's*/
   char *exprptr = *expression, /* local copy of ptr to expression */
    *texchar(), delim[32][132], /* delimiters following \middle's */
    *strtexchr(), /* locate \middle's */
    subexpr[MAXSUBXSZ+1], *subptr=NULL;/*subexpression between \middle's*/
   int height=0, habove=0, hbelow=0; /* height, above & below baseline */
   int idelim, ndelims=0, /* \middle count (max 32) */
    family = CMSYEX; /* delims from CMSY10 or CMEX10 */
   subraster *subrastcpy(), /* copy subraster */
    *rastcat(), /* concatanate subraster */
    *get_delim(); /* get rasterized delimiter */
   int delete_subraster(); /* free work area subsp[]'s at eoj */
   /* -------------------------------------------------------------------------
   initialization
   -------------------------------------------------------------------------- */
   subsp[0] = leftexpression; /* expressn preceding 1st \middle */
   subsp[1] = NULL; /* set first null */
   /* -------------------------------------------------------------------------
   accumulate subrasters between consecutive \middle\delim...\middle\delim...'s
   -------------------------------------------------------------------------- */
   while ( ndelims < 30 ) /* max of 31 \middle's */
     {
     /* --- maintain max height above,below baseline --- */
     if ( subsp[ndelims] != NULL ) /*exprssn preceding current \middle*/
      { int baseline = (subsp[ndelims])->baseline;  /* #rows above baseline */
        height = ((subsp[ndelims])->image)->height; /* tot #rows (height) */
        habove = max2(habove,baseline); /* max #rows above baseline */
        hbelow = max2(hbelow,height-baseline); } /* max #rows below baseline */
     /* --- get delimter after \middle --- */
     skipwhite(exprptr); /*skip space betwn \middle & \delim*/
     exprptr = texchar(exprptr,delim[ndelims]); /* \delim after \middle */
     if ( *(delim[ndelims]) == '\000' ) /* \middle at end-of-expression */
       break; /* ignore it and consider job done */
     ndelims++; /* count another \middle\delim */
     /* --- get subexpression between \delim and next \middle --- */
     subsp[ndelims] = NULL; /* no subexpresion yet */
     if ( *exprptr == '\000' ) /* end-of-expression after \delim */
       break; /* so we have all subexpressions */
     if ( (subptr = strtexchr(exprptr,"\\middle")) /* find next \middle */
     ==   NULL ) /* no more \middle's */
      { strncpy(subexpr,exprptr,MAXSUBXSZ); /*get entire remaining expression*/
        subexpr[MAXSUBXSZ] = '\000'; /* make sure it's null-terminated */
        exprptr += strlen(exprptr); } /* push exprptr to terminating '\0'*/
     else /* have another \middle */
      { int sublen = (int)(subptr-exprptr); /* #chars between \delim...\middle*/
        memcpy(subexpr,exprptr,min2(sublen,MAXSUBXSZ)); /* get subexpression */
        subexpr[min2(sublen,MAXSUBXSZ)] = '\000'; /* and null-terminate it */
        exprptr += (sublen+strlen("\\middle")); } /* push exprptr past \middle*/
     /* --- rasterize subexpression --- */
     subsp[ndelims] = rasterize(subexpr,size); /* rasterize subexpresion */
     } /* --- end-of-while(1) --- */
   /* -------------------------------------------------------------------------
   construct \middle\delim's and concatanate them between subexpressions
   -------------------------------------------------------------------------- */
   if ( ndelims < 1 /* no delims */
   ||   (height=habove+hbelow) < 1 ) /* or no subexpressions? */
     goto end_of_job; /* just flush \middle directive */
   for ( idelim=0; idelim<=ndelims; idelim++ )
     {
     /* --- first add on subexpression preceding delim --- */
     if ( subsp[idelim] != NULL ) { /* have subexpr preceding delim */
       if ( sp == NULL ) /* this is first piece */
        { sp = subsp[idelim]; /* so just use it */
          if ( idelim == 0 ) sp = subrastcpy(sp); } /* or copy leftexpression */
       else sp = rastcat(sp,subsp[idelim],(idelim>0?3:1)); } /* or concat it */
     /* --- now construct delimiter --- */
     if ( *(delim[idelim]) != '\000' ) /* have delimter */
      { subraster *delimsp = get_delim(delim[idelim],height,family);
        if ( delimsp != NULL ) /* rasterized delim */
         { delimsp->baseline = habove; /* set baseline */
    if ( sp == NULL ) /* this is first piece */
     sp = delimsp; /* so just use it */
    else sp = rastcat(sp,delimsp,3); } } /*or concat to existing pieces*/
     } /* --- end-of-for(idelim) --- */
   /* --- back to caller --- */
   end_of_job:
     if ( 0 ) /* now handled above */
       for ( idelim=1; idelim<=ndelims; idelim++ ) /* free subsp[]'s (not 0) */
        if ( subsp[idelim] != NULL ) /* have allocated subraster */
         delete_subraster(subsp[idelim]); /* so free it */
     if ( sp != NULL ) /* returning entire expression */
       { int newht = (sp->image)->height; /* height of returned subraster */
         sp->baseline = min2(newht-1,newht/2+5); /* guess new baseline */
         isreplaceleft = 1; /* set flag to replace left half*/
         *expression += strlen(*expression); } /* and push to terminating null*/
     return ( sp );
   } /* --- end-of-function rastmiddle() --- */
   
   
   /* ==========================================================================
  * Function: rastflags ( expression, size, basesp,  flag, value, arg3 )   * Function: rastflags ( expression, size, basesp,  flag, value, arg3 )
  * Purpose: sets an internal flag, e.g., for \rm, or sets an internal   * Purpose: sets an internal flag, e.g., for \rm, or sets an internal
  * value, e.g., for \unitlength=<value>, and returns NULL   * value, e.g., for \unitlength=<value>, and returns NULL
Line 5476  end_of_job: Line 8302  end_of_job:
  * size (I) int containing base font size (not used,   * size (I) int containing base font size (not used,
  * just stored in subraster)   * just stored in subraster)
  * basesp (I) subraster *  to character (or subexpression)   * basesp (I) subraster *  to character (or subexpression)
  * immediately preceding space, whose baseline   * immediately preceding "flags" directive
  * and height params are transferred to space   * (unused but passed for consistency)
  * flag (I) int containing #define'd symbol specifying   * flag (I) int containing #define'd symbol specifying
  * internal flag to be set   * internal flag to be set
  * value (I) int containing new value of flag   * value (I) int containing new value of flag
Line 5499  char *texsubexpr(),   /* parse expressio Line 8325  char *texsubexpr(),   /* parse expressio
 int argvalue=NOVALUE, /* atoi(valuearg) */  int argvalue=NOVALUE, /* atoi(valuearg) */
  isdelta=0, /* true if + or - precedes valuearg */   isdelta=0, /* true if + or - precedes valuearg */
  valuelen=0; /* strlen(valuearg) */   valuelen=0; /* strlen(valuearg) */
 double strtod(); /*convert ascii {valuearg} to double*/  double dblvalue=(-99.), strtod(); /*convert ascii {valuearg} to double*/
 static int displaystylelevel = (-99); /* \displaystyle set at recurlevel */  static int displaystylelevel = (-99); /* \displaystyle set at recurlevel */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 set flag or value  set flag or value
Line 5507  set flag or value Line 8333  set flag or value
 switch ( flag )  switch ( flag )
   {    {
   default: break; /* unrecognized flag */    default: break; /* unrecognized flag */
   case ISTEXT:    case ISFONTFAM:
     if ( isthischar((*(*expression)),WHITEMATH) ) /* \rm followed by white */      if ( isthischar((*(*expression)),WHITEMATH) ) /* \rm followed by white */
       (*expression)++; /* skip leading ~ after \rm */        (*expression)++; /* skip leading ~ after \rm */
     istext=value; /* set text mode */      fontnum = value; /* set font family */
     break;      break;
   case ISSTRING: isstring=value; break; /* set string/image mode */    case ISSTRING: isstring=value; break; /* set string/image mode */
   case ISDISPLAYSTYLE: /* set \displaystyle mode */    case ISDISPLAYSTYLE: /* set \displaystyle mode */
Line 5524  switch ( flag ) Line 8350  switch ( flag )
       { bgred=255-bgred; bggreen=255-bggreen; bgblue=255-bgblue; }        { bgred=255-bgred; bggreen=255-bggreen; bgblue=255-bgblue; }
     if ( value==2 || value==NOVALUE )      if ( value==2 || value==NOVALUE )
       isblackonwhite = !isblackonwhite;        isblackonwhite = !isblackonwhite;
       if ( gammacorrection > 0.0001 ) /* have gamma correction */
         gammacorrection = REVERSEGAMMA; /* use reverse video gamma instead */
     break;      break;
   case ISSUPER: /* set supersampling/lowpass flag */    case ISSUPER: /* set supersampling/lowpass flag */
     #ifndef SSFONTS /* don't have ss fonts loaded */      #ifndef SSFONTS /* don't have ss fonts loaded */
Line 5533  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 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 5541  switch ( flag ) Line 8372  switch ( flag )
   case ISADJACENTWT: /* set lowpass adjacent weight */    case ISADJACENTWT: /* set lowpass adjacent weight */
   case ISCORNERWT: /* set lowpass corner weight */    case ISCORNERWT: /* set lowpass corner weight */
   case ISCOLOR: /* set red(1),green(2),blue(3) */    case ISCOLOR: /* set red(1),green(2),blue(3) */
   case ISSQUASH: /* set (minimum) "squash" margin */    case ISSMASH: /* set (minimum) "smash" margin */
     case ISGAMMA: /* set gamma correction */
     if ( value != NOVALUE ) /* passed a fixed value to be set */      if ( value != NOVALUE ) /* passed a fixed value to be set */
       argvalue = value; /* set given fixed value */        { argvalue = value; /* set given fixed int value */
    dblvalue = (double)value; } /* or maybe interpreted as double */
     else /* get value from expression */      else /* get value from expression */
       { *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 */
Line 5551  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 */
      argvalue = atoi(valuearg); } } /* convert to int */       switch ( flag ) { /* convert to double or int */
         default: argvalue = atoi(valuearg); break; /* convert to int */
         case ISGAMMA:
    dblvalue = strtod(valuearg,NULL); break; } /* or to double */
      } /* --- end-of-if(*valuearg!='?') --- */
         } /* --- end-of-if(value==NOVALUE) --- */
     switch ( flag )      switch ( flag )
       {        {
       default: break;        default: break;
Line 5578  switch ( flag ) Line 8416  switch ( flag )
     fontsize = (isdelta? fontsize+argvalue : argvalue);      fontsize = (isdelta? fontsize+argvalue : argvalue);
     fontsize = max2(0,min2(fontsize,largestsize));      fontsize = max2(0,min2(fontsize,largestsize));
     shrinkfactor = shrinkfactors[fontsize];      shrinkfactor = shrinkfactors[fontsize];
     if ( isdisplaystyle == 1 ) /* displaystyle enabled but not set*/      if ( isdisplaystyle == 1 /* displaystyle enabled but not set*/
       ||  (1 && isdisplaystyle==2) /* displaystyle enabled and set */
       ||  (0 && isdisplaystyle==0) )/*\textstyle disabled displaystyle*/
      if ( displaystylelevel != recurlevel ) /*respect \displaystyle*/       if ( displaystylelevel != recurlevel ) /*respect \displaystyle*/
       if ( !ispreambledollars ) /* respect $$...$$'s */        if ( !ispreambledollars ) { /* respect $$...$$'s */
        isdisplaystyle = (fontsize>=displaysize? 2:1); /* forced */         if ( fontsize >= displaysize )
    isdisplaystyle = 2; /* forced */
          else isdisplaystyle = 1; }
     /*displaystylelevel = (-99);*/ } /* reset \displaystyle level */      /*displaystylelevel = (-99);*/ } /* reset \displaystyle level */
  else /* embed font size in expression */   else /* embed font size in expression */
   { sprintf(valuearg,"%d",fontsize); /* convert size */    { sprintf(valuearg,"%d",fontsize); /* convert size */
Line 5590  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);
  break;   break;
       case ISSQUASH: /* set (minimum) "squash" margin */        case ISCONTENTTYPE: /*enable/disable content-type lines*/
    if ( argvalue != NOVALUE ) /* got a value */
       isemitcontenttype = (argvalue>0?1:0);
    break;
         case ISCONTENTCACHED: /* write content-type to cache file*/
  if ( argvalue != NOVALUE ) /* got a value */   if ( argvalue != NOVALUE ) /* got a value */
   { squashmargin = argvalue; /* set value */      iscachecontenttype = (argvalue>0?1:0);
    break;
         case ISSMASH: /* set (minimum) "smash" margin */
    if ( argvalue != NOVALUE ) /* got a value */
     { smashmargin = argvalue; /* set value */
     if ( arg3 != NOVALUE ) isdelta=arg3; /* hard-coded isdelta */      if ( arg3 != NOVALUE ) isdelta=arg3; /* hard-coded isdelta */
     issquashdelta = (isdelta?1:0); } /* and set delta flag */      issmashdelta = (isdelta?1:0); } /* and set delta flag */
  squashmargin = max2((isdelta?-5:0),min2(squashmargin,32)); /*sanity*/   smashmargin = max2((isdelta?-5:0),min2(smashmargin,32)); /*sanity*/
    isexplicitsmash = 1; /* signal explicit \smash directive*/
  break;   break;
       case ISSHRINK: /* set shrinkfactor */        case ISSHRINK: /* set shrinkfactor */
  if ( argvalue != NOVALUE ) /* got a value */   if ( argvalue != NOVALUE ) /* got a value */
Line 5607  switch ( flag ) Line 8464  switch ( flag )
  shrinkfactor = max2(1,min2(shrinkfactor,27)); /* sanity check */   shrinkfactor = max2(1,min2(shrinkfactor,27)); /* sanity check */
  break;   break;
       case ISAAALGORITHM: /* set anti-aliasing algorithm */        case ISAAALGORITHM: /* set anti-aliasing algorithm */
  if ( argvalue != NOVALUE ) /* got a value */   if ( argvalue != NOVALUE ) { /* got a value */
   aaalgorithm = argvalue; /* set algorithm number */    if ( argvalue >= 0 ) { /* non-negative to set algorithm */
  aaalgorithm = max2(0,min2(aaalgorithm,3)); /* bounds check */        aaalgorithm = argvalue; /* set algorithm number */
       aaalgorithm = max2(0,min2(aaalgorithm,4)); } /* bounds check */
     else maxfollow = abs(argvalue); } /* or maxfollow=abs(negative#) */
  break;   break;
       case ISWEIGHT: /* set font weight number */        case ISWEIGHT: /* set font weight number */
  value = (argvalue==NOVALUE? NOVALUE : /* don't have a value */   value = (argvalue==NOVALUE? NOVALUE : /* don't have a value */
Line 5638  switch ( flag ) Line 8497  switch ( flag )
  if ( argvalue != NOVALUE ) /* got a value */   if ( argvalue != NOVALUE ) /* got a value */
   cornerwt = argvalue; /* set lowpass corner weight */    cornerwt = argvalue; /* set lowpass corner weight */
  break;   break;
         case ISGAMMA: /* set gamma correction */
    if ( dblvalue >= 0.0 ) /* got a value */
     gammacorrection = dblvalue; /* set gamma correction */
    break;
       } /* --- end-of-switch() --- */        } /* --- end-of-switch() --- */
     break;      break;
   case PNMPARAMS: /*set fgalias,fgonly,bgalias,bgonly*/    case PNMPARAMS: /*set fgalias,fgonly,bgalias,bgonly*/
Line 5659  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 5680  return ( NULL );   /*just set value, not Line 8544  return ( NULL );   /*just set value, not
  * width (I) int containing #bits/pixels for space width   * width (I) int containing #bits/pixels for space width
  * isfill (I) int containing true to \hfill complete   * isfill (I) int containing true to \hfill complete
  * expression out to width   * expression out to width
    * (Kludge: isfill=99 signals \hspace*
    * for negative space)
  * isheight (I) int containing true (but not NOVALUE)   * isheight (I) int containing true (but not NOVALUE)
  * to treat width arg as height   * to treat width arg as height
  * --------------------------------------------------------------------------   * --------------------------------------------------------------------------
Line 5696  subraster *rastspace ( char **expression Line 8562  subraster *rastspace ( char **expression
 Allocations and Declarations  Allocations and Declarations
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 subraster *new_subraster(), *spacesp=NULL; /* subraster for space */  subraster *new_subraster(), *spacesp=NULL; /* subraster for space */
   raster *bp=NULL, *backspace_raster(); /* for negative space */
   int delete_subraster(); /* if fail, free unneeded subraster*/
 int baseht=1, baseln=0; /* height,baseline of base symbol */  int baseht=1, baseln=0; /* height,baseline of base symbol */
 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 */
 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 */
 int blanksignal = (-991234); /*rastsquash signal right-hand blank*/  
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 initialization  initialization
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
   if ( isfill > 1 ) { isstar=1; isfill=0; } /* large fill signals \hspace* */
 if ( isfill == NOVALUE ) isfill=0; /* novalue means false */  if ( isfill == NOVALUE ) isfill=0; /* novalue means false */
 if ( isheight == NOVALUE ) isheight=0; /* novalue means false */  if ( isheight == NOVALUE ) isheight=0; /* novalue means false */
   minspace = (isstar?(-1):0); /* reset default minspace */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 determine width if not given (e.g., \hspace{width}, \hfill{width})  determine width if not given (e.g., \hspace{width}, \hfill{width})
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 if ( width <= 0 ) /* width specified in expression */  if ( width == 0 ) { /* width specified in expression */
   { int widthval; /* test {width} before using it */    double dwidth;  int widthval; /* test {width} before using it */
     width = 1; /* set default width */    int minwidth = (isfill||isheight?1:-600); /* \hspace allows negative */
     *expression = texsubexpr(*expression,widtharg,255,"{","}",0,0);    /* --- check if optional [minspace] given for negative \hspace --- */
     widthval = /* convert {width} to integer */    if ( *(*expression) == '[' ) { /* [minspace] if leading char is [ */
  (int)((unitlength*strtod(widtharg,NULL))+0.5);      /* ---parse [minspace], bump expression past it, evaluate expression--- */
     if ( widthval>=2 && widthval<=600 ) /* sanity check */      *expression = texsubexpr(*expression,widtharg,127,"[","]",0,0);
       width = widthval; } /* replace deafault width */      if ( !isempty(widtharg) ) { /* got [minspace] */
         evalue = evalterm(mimestore,widtharg); /* evaluate widtharg expr */
         minspace = iround(unitlength*((double)evalue)); } /* in pixels */
       } /* --- end-of-if(*(*expression)=='[') --- */
     width = 1; /* set default width */
     *expression = texsubexpr(*expression,widtharg,255,"{","}",0,0);
     dwidth = unitlength*((double)evalterm(mimestore,widtharg)); /* scaled */
     widthval = /* convert {width} to integer */
    (int)( dwidth + (dwidth>=0.0?0.5:(-0.5)) );
     if ( widthval>=minwidth && widthval<=600 ) /* sanity check */
       width = widthval; /* replace deafault width */
     } /* --- end-of-if(width==0) --- */
   /* -------------------------------------------------------------------------
   first check for negative space
   -------------------------------------------------------------------------- */
   if ( width < 0 ) { /* have negative hspace */
    if ( leftexpression != (subraster *)NULL ) /* can't backspace */
     if ( (spacesp=new_subraster(0,0,0)) /* get new subraster for backspace */
     !=   NULL ) { /* and if we succeed... */
      int nback=(-width), pback; /*#pixels wanted,actually backspaced*/
      if ( (bp=backspace_raster(leftexpression->image,nback,&pback,minspace,0))
      !=    NULL ) { /* and if backspace succeeds... */
        spacesp->image = bp; /* save backspaced image */
        /*spacesp->type = leftexpression->type;*/ /* copy original type */
        spacesp->type = blanksignal; /* need to propagate blanks */
        spacesp->size = leftexpression->size; /* copy original font size */
        spacesp->baseline = leftexpression->baseline; /* and baseline */
        blanksymspace += -(nback-pback); /* wanted more than we got */
        isreplaceleft = 1; } /*signal to replace entire expressn*/
      else { /* backspace failed */
        delete_subraster(spacesp); /* free unneeded envelope */
        spacesp = (subraster *)NULL; } } /* and signal failure */
    goto end_of_job;
    } /* --- end-of-if(width<0) --- */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 see if width is "absolute" or fill width  see if width is "absolute" or fill width
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
Line 5745  if ( width > 0 )   /*make sure we have p Line 8649  if ( width > 0 )   /*make sure we have p
  !=   NULL ) /* and if we succeed... */   !=   NULL ) /* and if we succeed... */
   { /* --- ...re-init subraster parameters --- */    { /* --- ...re-init subraster parameters --- */
     spacesp->size = size; /*propagate base font size forward*/      spacesp->size = size; /*propagate base font size forward*/
       if(1)spacesp->type = blanksignal; /* need to propagate blanks (???) */
     spacesp->baseline = baseln; } /* ditto baseline */      spacesp->baseline = baseln; } /* ditto baseline */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 concat right half if \hfill-ing  concat right half if \hfill-ing
Line 5754  if ( rightsp != NULL )   /* we have a ri Line 8659  if ( rightsp != NULL )   /* we have a ri
  rastcat(spacesp,rightsp,3)); /* or cat right half after space */   rastcat(spacesp,rightsp,3)); /* or cat right half after space */
     spacesp->type = blanksignal; /* need to propagate blanks */      spacesp->type = blanksignal; /* need to propagate blanks */
     *expression += strlen((*expression)); } /* push expression to its null */      *expression += strlen((*expression)); } /* push expression to its null */
 return ( spacesp );  end_of_job:
     return ( spacesp );
 } /* --- end-of-function rastspace() --- */  } /* --- end-of-function rastspace() --- */
   
   
Line 5767  return ( spacesp ); Line 8673  return ( spacesp );
  * 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-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 \\
  * (unused, but passed for consistency)   * (unused, but passed for consistency)
  * arg1 (I) int unused   * arg1 (I) int unused
  * arg2 (I) int unused   * arg2 (I) int unused
Line 5791  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 5802  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 5812  rasterize right half of expression and s Line 8719  rasterize right half of expression and s
 if ( (rightsp=rasterize(*expression,size)) /* rasterize right half */  if ( (rightsp=rasterize(*expression,size)) /* rasterize right half */
 == NULL ) goto end_of_job; /* quit if failed */  == NULL ) goto end_of_job; /* quit if failed */
 /* --- stack left half above it --- */  /* --- stack left half above it --- */
 newlsp = rastack(rightsp,leftexpression,1,vspace,0,3); /*right under left*/  /*newlsp = rastack(rightsp,leftexpression,1,vspace,0,3);*//*right under left*/
   newlsp = rastack(rightsp,leftexpression,1,vspace,0,1); /*right under left*/
 /* --- back to caller --- */  /* --- back to caller --- */
 end_of_job:  end_of_job:
   if ( newlsp != NULL ) /* returning entire expression */    if ( newlsp != NULL ) /* returning entire expression */
Line 5867  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 5881  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 5961  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 5974  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 6028  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 6057  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 6066  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 6106  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 6118  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 6133  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 6156  subraster *rastfrac ( char **expression, Line 9075  subraster *rastfrac ( char **expression,
 Allocations and Declarations  Allocations and Declarations
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 char *texsubexpr(), /*parse expression for numer,denom*/  char *texsubexpr(), /*parse expression for numer,denom*/
  numer[8192], denom[8192]; /*numer,denom parsed from expression*/   numer[MAXSUBXSZ+1], denom[MAXSUBXSZ+1]; /* parsed numer, denom */
 subraster *rasterize(), *numsp=NULL, *densp=NULL; /*rasterize numer, denom*/  subraster *rasterize(), *numsp=NULL, *densp=NULL; /*rasterize numer, denom*/
 subraster *rastack(), *fracsp=NULL; /* subraster for numer/denom */  subraster *rastack(), *fracsp=NULL; /* subraster for numer/denom */
 subraster *new_subraster()/*, *spacesp=NULL*/; /* space for num or den */  subraster *new_subraster()/*, *spacesp=NULL*/; /* space for num or den */
Line 6166  int baseht=0, baseln=0;  /* height,basel Line 9085  int baseht=0, baseln=0;  /* height,basel
 /*int istweak = 1;*/ /*true to tweak baseline alignment*/  /*int istweak = 1;*/ /*true to tweak baseline alignment*/
 int rule_raster(), /* draw horizontal line for frac */  int rule_raster(), /* draw horizontal line for frac */
  lineheight = 1; /* thickness of fraction line */   lineheight = 1; /* thickness of fraction line */
 int vspace = 1; /*vertical space between components*/  int vspace = (size>2?2:1); /*vertical space between components*/
 int delete_subraster(); /*free work areas in case of error*/  int delete_subraster(); /*free work areas in case of error*/
 int type_raster(); /* display debugging output */  int type_raster(); /* display debugging output */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
Line 6213  width = (fracsp->image)->width;  /*just Line 9132  width = (fracsp->image)->width;  /*just
 /* --- initialize subraster parameters --- */  /* --- initialize subraster parameters --- */
 fracsp->size = size; /* propagate font size forward */  fracsp->size = size; /* propagate font size forward */
 fracsp->baseline = (numheight+vspace+lineheight)+(size+2);/*default baseline*/  fracsp->baseline = (numheight+vspace+lineheight)+(size+2);/*default baseline*/
   fracsp->type = FRACRASTER; /* signal \frac image */
 if ( basesp != (subraster *)NULL ) /* we have base symbol for frac */  if ( basesp != (subraster *)NULL ) /* we have base symbol for frac */
   { baseht = (basesp->image)->height; /* height of base symbol */    { baseht = (basesp->image)->height; /* height of base symbol */
     baseln =  basesp->baseline; /* and its baseline */      baseln =  basesp->baseline; /* and its baseline */
Line 6220  if ( basesp != (subraster *)NULL ) /* we Line 9140  if ( basesp != (subraster *)NULL ) /* we
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 draw horizontal line between numerator and denominator  draw horizontal line between numerator and denominator
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
   fraccenterline = numheight+vspace; /* signal that we have a \frac */
 if ( isfrac ) /*line for \frac, but not for \atop*/  if ( isfrac ) /*line for \frac, but not for \atop*/
   rule_raster(fracsp->image,numheight+vspace,0,width,lineheight,0);    rule_raster(fracsp->image,fraccenterline,0,width,lineheight,0);
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 return final result to caller  return final result to caller
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
Line 6243  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 6264  subraster *rastackrel ( char **expressio Line 9185  subraster *rastackrel ( char **expressio
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 Allocations and Declarations  Allocations and Declarations
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 char *texsubexpr(), /*parse expression for numer,denom*/  char *texsubexpr(), /*parse expression for upper,lower*/
  upper[8192], lower[8192]; /*upper,lower parsed from expression*/   upper[MAXSUBXSZ+1], lower[MAXSUBXSZ+1]; /* parsed upper, lower */
 subraster *rasterize(), *upsp=NULL, *lowsp=NULL; /* rasterize upper, lower */  subraster *rasterize(), *upsp=NULL, *lowsp=NULL; /* rasterize upper, lower */
 subraster *rastack(), *relsp=NULL; /* subraster for upper/lower */  subraster *rastack(), *relsp=NULL; /* subraster for upper/lower */
 int upsize  = (base==1? size:size-1), /* font size for upper component */  int upsize  = (base==1? size:size-1), /* font size for upper component */
Line 6315  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 6337  subraster *rastmathfunc ( char **express Line 9258  subraster *rastmathfunc ( char **express
 Allocations and Declarations  Allocations and Declarations
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 char *texscripts(), /* parse expression for _limits */  char *texscripts(), /* parse expression for _limits */
  func[4096], limits[8192]; /* func as {\rm func}, limits */   func[MAXTOKNSZ+1], limits[MAXSUBXSZ+1]; /*func as {\rm func}, limits*/
 char *texsubexpr(), /* parse expression for arg */  char *texsubexpr(), /* parse expression for arg */
  funcarg[2048]; /* optional func arg */   funcarg[MAXTOKNSZ+1]; /* optional func arg */
 subraster *rasterize(), *funcsp=NULL, *limsp=NULL; /*rasterize func,limits*/  subraster *rasterize(), *funcsp=NULL, *limsp=NULL; /*rasterize func,limits*/
 subraster *rastack(), *mathfuncsp=NULL; /* subraster for mathfunc/limits */  subraster *rastack(), *mathfuncsp=NULL; /* subraster for mathfunc/limits */
 int limsize = size-1; /* font size for limits */  int limsize = size-1; /* font size for limits */
Line 6422  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 6443  subraster *rastsqrt ( char **expression, Line 9364  subraster *rastsqrt ( char **expression,
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 Allocations and Declarations  Allocations and Declarations
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 char *texsubexpr(), subexpr[8192], /* parse subexpr to be sqrt-ed */  char *texsubexpr(), subexpr[MAXSUBXSZ+1], /*parse subexpr to be sqrt-ed*/
  rootarg[8192]; /* optional \sqrt[rootarg]{...} */   rootarg[MAXSUBXSZ+1]; /* optional \sqrt[rootarg]{...} */
 subraster *rasterize(), *subsp=NULL; /* rasterize subexpr */  subraster *rasterize(), *subsp=NULL; /* rasterize subexpr */
 subraster *accent_subraster(), *sqrtsp=NULL, /* subraster with the sqrt */  subraster *accent_subraster(), *sqrtsp=NULL, /* subraster with the sqrt */
  *new_subraster(), *rootsp=NULL; /* optionally preceded by [rootarg]*/   *new_subraster(), *rootsp=NULL; /* optionally preceded by [rootarg]*/
Line 6482  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 6532  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 6559  subraster *rastaccent ( char **expressio Line 9481  subraster *rastaccent ( char **expressio
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 Allocations and Declarations  Allocations and Declarations
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 char *texsubexpr(), subexpr[8192]; /* parse subexpr to be accented */  char *texsubexpr(), subexpr[MAXSUBXSZ+1]; /*parse subexpr to be accented*/
 char *texscripts(), *script=NULL, /* \under,overbrace allow scripts */  char *texscripts(), *script=NULL, /* \under,overbrace allow scripts */
  subscript[512], supscript[512]; /* scripts parsed from expression */   subscript[MAXTOKNSZ+1], supscript[MAXTOKNSZ+1]; /* parsed scripts */
 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 6588  subwidth  = (subsp->image)->width; /* an Line 9510  subwidth  = (subsp->image)->width; /* an
 pixsz     = (subsp->image)->pixsz; /* original pixsz remains constant */  pixsz     = (subsp->image)->pixsz; /* original pixsz remains constant */
 /* --- determine desired width, height of accent --- */  /* --- determine desired width, height of accent --- */
 accwidth = subwidth; /* same width as subexpr */  accwidth = subwidth; /* same width as subexpr */
 accheight = 3; /* default for bars */  accheight = 4; /* default for bars */
 switch ( accent )  switch ( accent )
   { default: break; /* default okay */    { default: break; /* default okay */
   case DOTACCENT: case DDOTACCENT:    case DOTACCENT: case DDOTACCENT:
     accheight = (size<3? 3:4); /* default for dots */      accheight = (size<4? 3:4); /* default for dots */
     break;      break;
   case HATACCENT: case VECACCENT:    case VECACCENT:
       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:
     accheight = 7; /* default */      accheight = 7; /* default */
     if ( subwidth < 10 ) accheight = 5; /* unless small width */      if ( subwidth < 10 ) accheight = 5; /* unless small width */
       else if ( subwidth > 25 ) accheight = 9; /* or large */        else if ( subwidth > 25 ) accheight = 9; /* or large */
Line 6605  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 6640  end_of_job: Line 9566  end_of_job:
   
   
 /* ==========================================================================  /* ==========================================================================
  * Function: rastfont (expression,size,basesp,font,arg2,arg3)   * Function: rastfont (expression,size,basesp,ifontnum,arg2,arg3)
  * Purpose: \cal{}, \scr{}, \etc handler, returns subraster corresponding   * Purpose: \cal{}, \scr{}, \etc handler, returns subraster corresponding
  * to char(s) within {}'s rendered at size   * to char(s) within {}'s rendered at size
  * --------------------------------------------------------------------------   * --------------------------------------------------------------------------
Line 6648  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)
  * font (I) int containing 1 for \cal{}, 2 for \scr{}   * ifontnum (I) int containing 1 for \cal{}, 2 for \scr{}
  * arg2 (I) int unused   * arg2 (I) int unused
  * arg3 (I) int unused   * arg3 (I) int unused
  * --------------------------------------------------------------------------   * --------------------------------------------------------------------------
Line 6663  end_of_job: Line 9589  end_of_job:
  * ======================================================================= */   * ======================================================================= */
 /* --- entry point --- */  /* --- entry point --- */
 subraster *rastfont ( char **expression, int size, subraster *basesp,  subraster *rastfont ( char **expression, int size, subraster *basesp,
  int font, int arg2, int arg3 )   int ifontnum, int arg2, int arg3 )
 {  {
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 Allocations and Declarations  Allocations and Declarations
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 char *texsubexpr(), fontchars[8192], /*parse chars to be rendered in font*/  char *texsubexpr(), fontchars[MAXSUBXSZ+1], /* chars to render in font */
  subexpr[8192]; /* turn \cal{AB} into \calA\calB */   subexpr[MAXSUBXSZ+1]; /* turn \cal{AB} into \calA\calB */
 char *pfchars=fontchars, fchar='\0'; /* run thru fontchars one at a time*/  char *pfchars=fontchars, fchar='\0'; /* run thru fontchars one at a time*/
 char *name = NULL; /* fonts[font].name */  char *name = NULL; /* fontinfo[ifontnum].name */
 int class = 0, /* fonts[font].class */  int family = 0, /* fontinfo[ifontnum].family */
  istext = 0; /* set true for text type */   istext = 0, /* fontinfo[ifontnum].istext */
    class = 0; /* fontinfo[ifontnum].class */
 subraster *rasterize(), *fontsp=NULL, /* rasterize chars in font */  subraster *rasterize(), *fontsp=NULL, /* rasterize chars in font */
  *rastflags(); /* or just set flag to switch font */   *rastflags(); /* or just set flag to switch font */
 int oldsquashmargin = squashmargin; /* turn off squash in text mode */  int oldsmashmargin = smashmargin; /* turn off smash in text mode */
 int blanksignal = (-991234); /*rastsquash signal right-hand blank*/  #if 0
 /* --- fonts recognized by rastfont --- */  /* --- fonts recognized by rastfont --- */
 static int  nfonts = 5; /* 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 --- */
  { "\\badfont", 0 },   { "\\math", 0 },
  { "\\cal", 1 }, /*(1) calligraphic, uppercase */   { "\\mathcal", 1 }, /*(1) calligraphic, uppercase */
  { "\\scr", 1 }, /*(2) rsfs/script, uppercase */   { "\\mathscr", 1 }, /*(2) rsfs/script, uppercase */
  { "\\rm", -1 }, /*(3) \rm,\text{abc} --> {\rm~abc} */   { "\\textrm", -1 }, /*(3) \rm,\text{abc} --> {\rm~abc} */
  { "\\it", -1 }, /*(4) \it,\textit{abc}-->{\it~abc} */   { "\\textit", -1 }, /*(4) \it,\textit{abc}-->{\it~abc} */
  { "\\bb", -1 }, /*(5) \bb,\mathbb{abc}-->{\bb~abc} */   { "\\mathbb", -1 }, /*(5) \bb,\mathbb{abc}-->{\bb~abc} */
    { "\\mathbf", -1 }, /*(6) \bf,\mathbf{abc}-->{\bf~abc} */
    { "\\mathrm",   -1 }, /*(7) \mathrm */
    { "\\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
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 first get font name and class to determine type of conversion desired  first get font name and class to determine type of conversion desired
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 if ( font<=0 || font>nfonts ) font=0; /* set error if out-of-bounds */  if (ifontnum<=0 || ifontnum>nfontinfo) ifontnum=0; /*math if out-of-bounds*/
 name  = fonts[font].name; /* font name */  name   = fontinfo[ifontnum].name; /* font name */
 class = fonts[font].class; /* font class */  family = fontinfo[ifontnum].family; /* font family */
 if ( font==3 || font==4 ) /* text (respect blanks) */  istext = fontinfo[ifontnum].istext; /*true in text mode (respect space)*/
  { istext = 1; /* signal text mode */  class  = fontinfo[ifontnum].class; /* font class */
    squashmargin = 0; } /* don't squash internal blanks */  if ( istext ) /* text (respect blanks) */
    { mathsmashmargin = smashmargin; /* needed for \text{if $n-m$ even} */
      smashmargin = 0; } /* don't smash internal blanks */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 now convert \font{abc} --> {\font~abc}, or convert ABC to \calA\calB\calC  now convert \font{abc} --> {\font~abc}, or convert ABC to \calA\calB\calC
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 if ( class < 0 ) /* not character-by-character */  if ( 1 || class<0 ) /* not character-by-character */
  {    { 
  /* ---   /* ---
  if \font not immediately followed by { then it has no arg, so just set flag   if \font not immediately followed by { then it has no arg, so just set flag
Line 6711  if ( class < 0 )   /* not character-by-c Line 9647  if ( class < 0 )   /* not character-by-c
  if ( *(*expression) != '{' ) /* no \font arg, so just set flag */   if ( *(*expression) != '{' ) /* no \font arg, so just set flag */
     {      {
     if ( msgfp!=NULL && msglevel>=99 )      if ( msgfp!=NULL && msglevel>=99 )
       fprintf(msgfp,"rastfont> \\%s rastflags() for font#%d\n",name,font);       fprintf(msgfp,"rastfont> \\%s rastflags() for font#%d\n",name,ifontnum);
     switch ( font ) /* set flag by our internal font# */      fontsp = rastflags(expression,size,basesp,ISFONTFAM,ifontnum,arg3);
       { case 3: /* \rm, \text sets flag istext=1 */  
   fontsp = rastflags(expression,size,basesp,ISTEXT,1,arg3); break;  
  case 4: /* \it, \text sets flag istext=2 */  
   fontsp = rastflags(expression,size,basesp,ISTEXT,2,arg3); break;  
  case 5: /* \bb, \mathbb sets flag istext=3 */  
   fontsp = rastflags(expression,size,basesp,ISTEXT,3,arg3); break;  
  default: break; } /* unrecognized, set no flags */  
     goto end_of_job;      goto end_of_job;
     } /* --- end-of-if(*(*expression)!='{') --- */      } /* --- end-of-if(*(*expression)!='{') --- */
  /* ---   /* ---
Line 6752  else     /* character-by-character */ Line 9681  else     /* character-by-character */
  for ( pfchars=fontchars; (fchar= *pfchars)!='\000'; pfchars++ )   for ( pfchars=fontchars; (fchar= *pfchars)!='\000'; pfchars++ )
   {    {
   if ( isthischar(fchar,WHITEMATH) ) /* some whitespace */    if ( isthischar(fchar,WHITEMATH) ) /* some whitespace */
     { if ( 0 || istext ) /* and we're in a text mode */      { if ( 0 || istext ) /* and we're in a text mode font */
  strcat(subexpr,"\\;"); } /* so respect whitespace */   strcat(subexpr,"\\;"); } /* so respect whitespace */
   else /* char to be displayed in font */    else /* char to be displayed in font */
     { int exprlen = 0; /* #chars in subexpr before fchar */      { int exprlen = 0; /* #chars in subexpr before fchar */
       int isinclass = 0; /* set true if fchar in font class */        int isinclass = 0; /* set true if fchar in font class */
         /* --- class: 1=upper, 2=alpha, 3=alnum, 4=lower, 5=digit, 9=all --- */
       switch ( class ) /* check if fchar is in font class */        switch ( class ) /* check if fchar is in font class */
  { default: break; /* no chars in unrecognized class */   { default: break; /* no chars in unrecognized class */
   case 1: if ( isupper((int)fchar) ) isinclass=1; break;    case 1: if ( isupper((int)fchar) ) isinclass=1; break;
Line 6790  if ( (fontsp = rasterize(subexpr,size)) Line 9720  if ( (fontsp = rasterize(subexpr,size))
 back to caller with chars rendered in font  back to caller with chars rendered in font
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 end_of_job:  end_of_job:
   squashmargin = oldsquashmargin; /* restore squash */    smashmargin = oldsmashmargin; /* restore smash */
   if ( istext && fontsp!=NULL ) /* raster contains text */    mathsmashmargin = SMASHMARGIN; /* this one probably not necessary */
     fontsp->type = blanksignal; /* signal nosquash */    if ( istext && fontsp!=NULL ) /* raster contains text mode font */
       fontsp->type = blanksignal; /* signal nosmash */
   return ( fontsp ); /* chars rendered in font */    return ( fontsp ); /* chars rendered in font */
 } /* --- end-of-function rastfont() --- */  } /* --- end-of-function rastfont() --- */
   
Line 6807  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 6827  subraster *rastbegin ( char **expression Line 9758  subraster *rastbegin ( char **expression
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 Allocations and Declarations  Allocations and Declarations
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 char *texsubexpr(), subexpr[8210], /* \begin{} environment paramaters */  char *texsubexpr(), subexpr[MAXSUBXSZ+1], /* \begin{} environment params*/
  *exprptr=NULL,*begptr=NULL,*endptr=NULL,*braceptr=NULL; /* ptrs */   *exprptr=NULL,*begptr=NULL,*endptr=NULL,*braceptr=NULL; /* ptrs */
 char *begtoken="\\begin{", *endtoken="\\end{"; /*tokens we're looking for*/  char *begtoken="\\begin{", *endtoken="\\end{"; /*tokens we're looking for*/
 int strreplace(); /* replace substring in string */  int strreplace(); /* replace substring in string */
Line 6840  int envlen=0, sublen=0;  /* #chars in en Line 9771  int envlen=0, sublen=0;  /* #chars in en
 static int blevel = 0; /* \begin...\end nesting level */  static int blevel = 0; /* \begin...\end nesting level */
 static char *mdelims[] = { NULL, NULL, NULL, NULL,  static char *mdelims[] = { NULL, NULL, NULL, NULL,
  "()","[]","{}","||","==", /* for pbBvVmatrix */   "()","[]","{}","||","==", /* for pbBvVmatrix */
  NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,   NULL, NULL, NULL, NULL, "{.", NULL, NULL, NULL, NULL, NULL, NULL,
  NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };   NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
 static char *environs[] = { /* types of environments we process*/  static char *environs[] = { /* types of environments we process*/
  "eqnarray", /* 0 eqnarray environment */   "eqnarray", /* 0 eqnarray environment */
Line 6856  static char *environs[] = {  /* types of Line 9787  static char *environs[] = {  /* types of
  "align", /* 10 align environment */   "align", /* 10 align environment */
  "verbatim", /* 11 verbatim environment */   "verbatim", /* 11 verbatim environment */
  "picture", /* 12 picture environment */   "picture", /* 12 picture environment */
    "cases", /* 13 cases environment */
    "equation", /* 14 for \begin{equation} */
  NULL }; /* trailer */   NULL }; /* trailer */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 determine type of environment we're beginning  determine type of environment we're beginning
Line 6866  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 6917  switch ( ienviron ) Line 9850  switch ( ienviron )
  if ( *(subexpr+8) == '\000' ) goto end_of_job; } /* quit if no arg */   if ( *(subexpr+8) == '\000' ) goto end_of_job; } /* quit if no arg */
     strcat(subexpr,"{"); /* opening {  after (width,height) */      strcat(subexpr,"{"); /* opening {  after (width,height) */
     break;      break;
     case 13: /* cases */
       strcat(subexpr,"\\array{ll$"); /* a&b \\ c&d etc */
       break;
     case 14: /* \begin{equation} */
       strcat(subexpr,"{"); /* just enclose expression in {}'s */
       break;
   } /* --- end-of-switch(ienviron) --- */    } /* --- end-of-switch(ienviron) --- */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 locate matching \end{...}  locate matching \end{...}
Line 7030  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 7064  subraster *rastarray ( char **expression Line 10003  subraster *rastarray ( char **expression
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 Allocations and Declarations  Allocations and Declarations
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 char *texsubexpr(),  subexpr[8210], *exprptr, /* parse array subexpr */  char *texsubexpr(), subexpr[MAXSUBXSZ+1], *exprptr, /*parse array subexpr*/
  subtok[4096], *subptr=subtok, /* & or \\ inside { }'s not a delim*/   subtok[MAXTOKNSZ+1], *subptr=subtok, /* &,\\ inside { } not a delim*/
  token[4096],  *tokptr=token, /* token from subexpr 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 7094  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 7142  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 7162  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 7213  while (  *preptr != '\000' )  /* check p Line 10165  while (  *preptr != '\000' )  /* check p
   if ( prepcase != 0 ) /* only check upper,lowercase */    if ( prepcase != 0 ) /* only check upper,lowercase */
    {     {
    int ispropagate = (*preptr=='+'?1:0); /* leading + propagates width/ht */     int ispropagate = (*preptr=='+'?1:0); /* leading + propagates width/ht */
    if ( ispropagate ) /* set row or col propagation */     if ( ispropagate ) { /* set row or col propagation */
      if ( prepcase == 1 ) colpropagate = 1; /* propagating col values */       if ( prepcase == 1 ) colpropagate = 1; /* propagating col values */
      else if ( prepcase == 2 ) rowpropagate = 1; /* propagating row values */       else if ( prepcase == 2 ) rowpropagate = 1; } /*propagating row values*/
    if ( !colpropagate && prepcase == 1 )     if ( !colpropagate && prepcase == 1 )
       { colwidth[icol] = 0; /* reset colwidth */        { colwidth[icol] = 0; /* reset colwidth */
  fixcolsize[icol] = 0; } /* reset width flag */   fixcolsize[icol] = 0; } /* reset width flag */
Line 7267  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 7312  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*/
  if ( memcmp(hlchar,hltoken,hltoklen) == 0 ) /* we have an \hline */   if ( memcmp(hlchar,hltoken,hltoklen) == 0 ) /* we have an \hline */
    hline[nrows] += 1; /* bump \hline count for row */     hline[nrows] += 1; /* bump \hline count for row */
  else if ( memcmp(hdchar,hltoken,hltoklen) == 0 ) /*we have an \hdash*/   else if ( memcmp(hdchar,hltoken,hltoklen) == 0 ) /*we have an \hdash*/
    hline[nrows] = (-1); /* set \hdash flag for row */     hline[nrows] = (-1); } /* set \hdash flag for row */
       if ( hline[nrows] != 0 ) /* \hline or \hdash prefixes token */        if ( hline[nrows] != 0 ) /* \hline or \hdash prefixes token */
  { skipwhite(tokptr); /* flush whitespace after \hline */   { skipwhite(tokptr); /* flush whitespace after \hline */
   if ( *tokptr == '\000' /* end-of-expression after \hline */    if ( *tokptr == '\000' /* end-of-expression after \hline */
   ||   isthischar(*tokptr,coldelim) ) /* or unescaped coldelim */    ||   isthischar(*tokptr,coldelim) ) /* or unescaped coldelim */
     istokwhite = ishonly = 1; /* so token contains \hline only */      { istokwhite = 1; /* so token contains \hline only */
         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 7358  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 7371  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 7418  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 7447  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 */
     {      {
     subraster *tsp = toksp[itoken]; /* token that belongs in this cell */      subraster *tsp = toksp[itoken]; /* token that belongs in this cell */
       /* --- first adjust leftcol for vline to left of icol, if present ---- */
       leftcol += vlinespace(icol); /* space for vline to left of col */
       /* --- now rasterize cell ---- */
     if ( tsp != NULL ) /* have a rasterized cell token */      if ( tsp != NULL ) /* have a rasterized cell token */
       {        {
       /* --- local parameters --- */        /* --- local parameters --- */
Line 7461  for ( irow=0; irow<=nrows; irow++ ) /*to Line 10437  for ( irow=0; irow<=nrows; irow++ ) /*to
   tokencol = 0, /*H offset (init for left justify)*/    tokencol = 0, /*H offset (init for left justify)*/
   tokenrow = baseline - tsp->baseline;/*V offset (init for baseline)*/    tokenrow = baseline - tsp->baseline;/*V offset (init for baseline)*/
       /* --- adjust leftcol for vline to left of icol, if present ---- */        /* --- adjust leftcol for vline to left of icol, if present ---- */
       leftcol += vlinespace(icol); /* space for vline to left of col */        /*leftcol += vlinespace(icol);*/ /* space for vline to left of col */
       /* --- reset justification (if not left-justified) --- */        /* --- reset justification (if not left-justified) --- */
       if ( justify[icol] == 0 ) /* but user wants it centered */        if ( justify[icol] == 0 ) /* but user wants it centered */
   tokencol = (cwidth-twidth+1)/2; /* so split margin left/right */    tokencol = (cwidth-twidth+1)/2; /* so split margin left/right */
Line 7518  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 7542  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 7550  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 7571  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 7619  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 7638  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 7656  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 7735  end_of_job: Line 10714  end_of_job:
  * string immediately following \line to be   * string immediately following \line 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 \line   * immediately preceding \line
  * (unused, but passed for consistency)   * (unused, but passed for consistency)
Line 7761  char *texsubexpr(),linexpr[257], *xptr=l Line 10740  char *texsubexpr(),linexpr[257], *xptr=l
 subraster *new_subraster(), *linesp=NULL; /* subraster for line */  subraster *new_subraster(), *linesp=NULL; /* subraster for line */
 /*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 */
 double strtod(), /* convert ascii params to doubles */  int thickness = 1; /* line thickness */
  xinc=0.0, yinc=0.0, /* x,y-increments for line, */  double 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*/
   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 7774  obtain (xinc,yinc) arguments immediately Line 10755  obtain (xinc,yinc) arguments immediately
 /* --- parse for (xinc,yinc) arguments, and bump expression past it --- */  /* --- parse for (xinc,yinc) arguments, and bump expression past it --- */
 *expression = texsubexpr(*expression,linexpr,253,"(",")",0,0);  *expression = texsubexpr(*expression,linexpr,253,"(",")",0,0);
 if ( *linexpr == '\000' ) goto end_of_job; /* couldn't get (xinc,yinc) */  if ( *linexpr == '\000' ) goto end_of_job; /* couldn't get (xinc,yinc) */
 /* --- now interpret xinc,yinc returned in linexpr --- */  /* --- now interpret xinc,yinc;thickness returned in linexpr --- */
   if ( (xptr=strchr(linexpr,';')) != NULL ) /* look for ';' after xinc,yinc */
     { *xptr = '\000'; /* terminate linexpr at ; */
       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 7790  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 7803  calculate width,height, etc, based on xl Line 10787  calculate width,height, etc, based on xl
 xlen = absval(xlen); /* force xlen positive */  xlen = absval(xlen); /* force xlen positive */
 ylen = absval(ylen); /* force ylen positive */  ylen = absval(ylen); /* force ylen positive */
 /* --- calculate corresponding lengths in pixels --- */  /* --- calculate corresponding lengths in pixels --- */
 width  = max2(1,iround(unitlength*xlen)); /*scale by unitlength and round,*/  width   = max2(1,iround(unitlength*xlen)); /*scale by unitlength and round,*/
 height = max2(1,iround(unitlength*ylen)); /* and must be at least 1 pixel */  height  = max2(1,iround(unitlength*ylen)); /* and must be at least 1 pixel */
   rwidth  = width  + (ylen<0.001?0:max2(0,thickness-1));
   rheight = height + (xlen<0.001?0:max2(0,thickness-1));
 /* --- set origin corner, x,yinc's: ++=0=(0,0) +-=1=(0,1) -+=10=(1,0) --- */  /* --- set origin corner, x,yinc's: ++=0=(0,0) +-=1=(0,1) -+=10=(1,0) --- */
 if ( xinc < 0.0 ) isright = 1; /*negative xinc, so corner is (1,?)*/  if ( xinc < 0.0 ) isright = 1; /*negative xinc, so corner is (1,?)*/
 if ( yinc < 0.0 ) istop = 1; /*negative yinc, so corner is (?,1)*/  if ( yinc < 0.0 ) istop = 1; /*negative yinc, so corner is (?,1)*/
Line 7813  if ( msgfp!=NULL && msglevel>=29 ) /* de Line 10799  if ( msgfp!=NULL && msglevel>=29 ) /* de
   fprintf(msgfp,"rastline> width,height,origin;x,yinc=%d,%d,%d;%g,%g\n",    fprintf(msgfp,"rastline> width,height,origin;x,yinc=%d,%d,%d;%g,%g\n",
   width,height,origin,xinc,yinc);    width,height,origin,xinc,yinc);
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 allocate subraster and raster for complete picture  allocate subraster and raster for line
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 /* --- sanity check on width,height args --- */  /* --- sanity check on width,height,thickness args --- */
 if ( width < 1 ||  width > 600  if ( width < 1 ||  width > 600
 ||  height < 1 || height > 600 ) goto end_of_job;  ||  height < 1 || height > 600
   ||  thickness<1||thickness>25 ) goto end_of_job;
 /* --- allocate and initialize subraster for constructed line --- */  /* --- allocate and initialize subraster for constructed line --- */
 if ( (linesp=new_subraster(width,height,pixsz)) /* allocate new subraster */  if ( (linesp=new_subraster(rwidth,rheight,pixsz)) /* alloc new subraster */
 ==   NULL )  goto end_of_job; /* quit if failed */  ==   NULL )  goto end_of_job; /* quit if failed */
 /* --- initialize line subraster parameters --- */  /* --- initialize line subraster parameters --- */
 linesp->type = IMAGERASTER; /* image */  linesp->type = IMAGERASTER; /* image */
 linesp->symdef = NULL; /* not applicable for image */  linesp->symdef = NULL; /* not applicable for image */
 linesp->baseline = height/2 + 2; /* is a little above center good? */  linesp->baseline = height/2 + 2 /* is a little above center good? */
    + (rheight-height)/2; /* account for line thickness too */
 linesp->size = size; /* size (probably unneeded) */  linesp->size = size; /* size (probably unneeded) */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 draw the line  draw the line
Line 7834  line_raster ( linesp->image,  /* embedde Line 10822  line_raster ( linesp->image,  /* embedde
  (isright?  width-1 : 0), /* col0, from left or right */   (isright?  width-1 : 0), /* col0, from left or right */
  (istop?   height-1 : 0), /* row1, to top or bottom */   (istop?   height-1 : 0), /* row1, to top or bottom */
  (isright? 0 :  width-1), /* col1, to right or left */   (isright? 0 :  width-1), /* col1, to right or left */
  1 ); /* line thickness is 1 pixel */   thickness ); /* line thickness (usually 1 pixel)*/
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 return constructed line to caller  return constructed line to caller
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
Line 7846  end_of_job: Line 10834  end_of_job:
   
   
 /* ==========================================================================  /* ==========================================================================
    * Function: rastrule ( expression, size, basesp, arg1, arg2, arg3 )
    * Purpose: \rule handler, returns subraster corresponding to rule
    * parameters [lift]{width}{height}
    * --------------------------------------------------------------------------
    * Arguments: expression (I/O) char **  to first char of null-terminated
    * string immediately following \rule 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 \rule
    * (unused, but passed for consistency)
    * arg1 (I) int unused
    * arg2 (I) int unused
    * arg3 (I) int unused
    * --------------------------------------------------------------------------
    * Returns: ( subraster * ) ptr to subraster corresponding to rule
    * requested, or NULL for any parsing error
    * --------------------------------------------------------------------------
    * Notes:     o Summary of syntax...
    *  \rule[lift]{width}{height}
    *      o if [lift] not given, then bottom of rule on baseline
    *      o if width=0 then you get an invisible strut 1 (one) pixel wide
    * ======================================================================= */
   /* --- entry point --- */
   subraster *rastrule ( char **expression, int size, subraster *basesp,
    int arg1, int arg2, int arg3 )
   {
   /* -------------------------------------------------------------------------
   Allocations and Declarations
   -------------------------------------------------------------------------- */
   char *texsubexpr(), rulexpr[257]; /* rule[lift]{wdth}{hgt} */
   subraster *new_subraster(), *rulesp=NULL; /* subraster for rule */
   int pixsz = 1; /* pixels are one bit each */
   int lift=0, width=0, height=0; /* default rule parameters */
   double dval; /* convert ascii params to doubles */
   int rwidth=0, rheight=0; /* alloc width, height plus lift */
   int rule_raster(); /* draw rule in rulesp->image */
   int evalterm(); /* evaluate args */
   /* -------------------------------------------------------------------------
   Obtain lift,width,height
   -------------------------------------------------------------------------- */
   /* --- check for optional lift arg  --- */
   if ( *(*expression) == '[' ) /*check for []-enclosed optional arg*/
     { *expression = texsubexpr(*expression,rulexpr,255,"[","]",0,0);
       dval = evalterm(mimestore,rulexpr); /* convert [lift] to int */
       if ( dval <= 99 && dval >= (-99) ) /* sanity check */
         lift = iround(unitlength*dval); } /* scale by unitlength and round */
   /* --- parse for width --- */
   *expression = texsubexpr(*expression,rulexpr,255,"{","}",0,0);
   if ( *rulexpr == '\000' ) goto end_of_job; /* quit if args missing */
   dval = evalterm(mimestore,rulexpr); /* convert {width} to int */
   if ( dval <= 500 && dval >= 0 ) /* sanity check */
     width = max2(0,iround(unitlength*dval)); /* scale by unitlength and round*/
   /* --- parse for height --- */
   *expression = texsubexpr(*expression,rulexpr,255,"{","}",0,0);
   if ( *rulexpr == '\000' ) goto end_of_job; /* quit if args missing */
   dval = evalterm(mimestore,rulexpr); /* convert {height} to int */
   if ( dval <= 500 && dval > 0 ) /* sanity check */
     height= max2(1,iround(unitlength*dval)); /* scale by unitlength and round*/
   /* --- raster width,height in pixels --- */
   rwidth  = max2(1,width); /* raster must be at least 1 pixel*/
   rheight = height + (lift>=0?lift: /* raster height plus lift */
     (-lift<height?0:-lift-height+1)); /* may need empty space above rule */
   /* -------------------------------------------------------------------------
   allocate subraster and raster for rule
   -------------------------------------------------------------------------- */
   /* --- sanity check on width,height,thickness args --- */
   if ( rwidth < 1 ||  rwidth > 600
   ||  rheight < 1 || rheight > 600 ) goto end_of_job;
   /* --- allocate and initialize subraster for constructed rule --- */
   if ( (rulesp=new_subraster(rwidth,rheight,pixsz)) /* alloc new subraster */
   ==   NULL )  goto end_of_job; /* quit if failed */
   /* --- initialize line subraster parameters --- */
   rulesp->type = IMAGERASTER; /* image */
   rulesp->symdef = NULL; /* not applicable for image */
   rulesp->baseline = rheight-1 + (lift>=0?0:lift); /*adjust baseline for lift*/
   rulesp->size = size; /* size (probably unneeded) */
   /* -------------------------------------------------------------------------
   draw the rule
   -------------------------------------------------------------------------- */
   rule_raster ( rulesp->image, /* embedded raster image */
    (-lift<height?0:rheight-height), /* topmost row for top-left corner*/
    0, /* leftmost col for top-left corner*/
    width, /* rule width */
    height, /* rule height */
    ( width>0? 0:4 ) ); /* rule type */
   /* -------------------------------------------------------------------------
   return constructed rule to caller
   -------------------------------------------------------------------------- */
   end_of_job:
     return ( rulesp ); /* return rule to caller */
   } /* --- end-of-function rastrule() --- */
   
   
   /* ==========================================================================
  * Function: rastcircle ( expression, size, basesp, arg1, arg2, arg3 )   * Function: rastcircle ( expression, size, basesp, arg1, arg2, arg3 )
  * Purpose: \circle handler, returns subraster corresponding to ellipse   * Purpose: \circle handler, returns subraster corresponding to ellipse
  * parameters (xdiam[,ydiam])   * parameters (xdiam[,ydiam])
Line 7854  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 7881  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 7892  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 7909  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 7969  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 7994  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 8019  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 8093  end_of_job: Line 11180  end_of_job:
  * string immediately following \raisebox to be   * string immediately following \raisebox 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 \rotatebox   * immediately preceding \raisebox
  * (unused, but passed for consistency)   * (unused, but passed for consistency)
  * arg1 (I) int unused   * arg1 (I) int unused
  * arg2 (I) int unused   * arg2 (I) int unused
Line 8115  subraster *rastraise ( char **expression Line 11202  subraster *rastraise ( char **expression
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 Allocations and Declarations  Allocations and Declarations
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 char *texsubexpr(), subexpr[8192], *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 8139  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 8155  end_of_job: Line 11245  end_of_job:
  * string immediately following \rotatebox to be   * string immediately following \rotatebox 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 \rotatebox   * immediately preceding \rotatebox
  * (unused, but passed for consistency)   * (unused, but passed for consistency)
Line 8177  subraster *rastrotate ( char **expressio Line 11267  subraster *rastrotate ( char **expressio
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 Allocations and Declarations  Allocations and Declarations
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 char *texsubexpr(), subexpr[8192], *degexpr=subexpr; /* args */  char *texsubexpr(), subexpr[MAXSUBXSZ+1], *degexpr=subexpr; /* args */
 subraster *rasterize(), *rotsp=NULL; /* subraster for rotated subexpr */  subraster *rasterize(), *rotsp=NULL; /* subraster for rotated subexpr */
 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 8245  if ( rotrp != NULL )   /* rotated raster Line 11335  if ( rotrp != NULL )   /* rotated raster
  { rotsp->type = IMAGERASTER; /* signal constructed image */   { rotsp->type = IMAGERASTER; /* signal constructed image */
    rotsp->image = rotrp; /* raster we just constructed */     rotsp->image = rotrp; /* raster we just constructed */
    /* --- now try to guess pleasing baseline --- */     /* --- now try to guess pleasing baseline --- */
    if ( idegrees > 2 ) /* leave unchanged if unrotated */     if ( idegrees > 2 ) { /* leave unchanged if unrotated */
     if ( strlen(subexpr) < 3 /* we rotated a short expression */      if ( strlen(subexpr) < 3 /* we rotated a short expression */
     ||   abs(idegrees-180) < 3 ) /* or just turned it upside-down */      ||   abs(idegrees-180) < 3 ) /* or just turned it upside-down */
       baseline = rotrp->height - 1; /* so set with nothing descending */        baseline = rotrp->height - 1; /* so set with nothing descending */
     else /* rotated a long expression */      else /* rotated a long expression */
       baseline = (65*(rotrp->height-1))/100; /* roughly center long expr */        baseline = (65*(rotrp->height-1))/100; } /* roughly center long expr */
    rotsp->baseline = baseline; } /* set baseline as calculated above*/     rotsp->baseline = baseline; } /* set baseline as calculated above*/
 /* --- return rotated subexpr to caller --- */  /* --- return rotated subexpr to caller --- */
 end_of_job:  end_of_job:
Line 8259  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 )
    * Purpose: \reflectbox[axis]{subexpression} handler, returns subraster
    * containing subexpression reflected horizontally (i.e., around
    * vertical axis, |_ becomes _|) if [axis] not given or axis=1,
    * or reflected vertically if axis=2 given.
    * --------------------------------------------------------------------------
    * 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 \reflectbox
    * requested, or NULL for any parsing error
    * --------------------------------------------------------------------------
    * Notes:     o Summary of syntax...
    *  \reflectbox[axis]{subexpression}
    *      o
    * ======================================================================= */
   /* --- entry point --- */
   subraster *rastreflect ( char **expression, int size, subraster *basesp,
    int arg1, int arg2, int arg3 )
   {
   /* -------------------------------------------------------------------------
   Allocations and Declarations
   -------------------------------------------------------------------------- */
   char *texsubexpr(), subexpr[MAXSUBXSZ+1], *axisexpr=subexpr; /* args */
   subraster *rasterize(), *refsp=NULL; /* subraster for reflected subexpr */
   raster *rastref(), *refrp=NULL; /* reflect subraster->image */
   int axis = 1; /* default horizontal reflection */
   int delete_raster(); /* delete intermediate raster */
   int baseline=0; /* baseline of rasterized image */
   /* -------------------------------------------------------------------------
   obtain [axis] argument immediately following \reflectbox command, if given
   -------------------------------------------------------------------------- */
   /* --- check for optional [axis] arg  --- */
   if ( *(*expression) == '[' ) /*check for []-enclosed optional arg*/
     { *expression = texsubexpr(*expression,axisexpr,255,"[","]",0,0);
       axis = atoi(axisexpr); /* convert [axis] to int */
       if ( axis<1 || axis>2 ) /* check axis input */
         axis = 1; } /* back to default if illegal */
   /* -------------------------------------------------------------------------
   obtain {subexpr} argument after optional [axis], 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 ( (refsp = rasterize(subexpr,size)) /* rasterize subexpression */
   ==   NULL ) goto end_of_job; /* and quit if failed */
   /* --- return unmodified image if no reflection requested --- */
   if ( axis<1 || axis>2 ) goto end_of_job; /* don't bother reflecting image */
   /* --- extract params for image to be reflected --- */
   refrp = refsp->image; /* unreflected rasterized image */
   baseline = refsp->baseline; /* and baseline of that image */
   /* -------------------------------------------------------------------------
   reflect image and adjust its parameters
   -------------------------------------------------------------------------- */
   /* --- reflect image --- */
   refrp = rastref(refsp->image,axis); /* reflect raster image */
   if ( refrp == NULL ) goto end_of_job; /* failed to reflect image */
   delete_raster(refsp->image); /* free original raster image */
   refsp->image = refrp; /*and replace it with reflected one*/
   /* --- adjust parameters --- */
   if ( axis == 2 ) /* for vertical reflection */
     baseline = refrp->height - 1; /* set with nothing descending */
   refsp->baseline = baseline; /* reset baseline of reflected image*/
   /* --- return reflected subexpr to caller --- */
   end_of_job:
     return ( refsp ); /*back to caller with reflected expr*/
   } /* --- end-of-function rastreflect() --- */
   
   
   /* ==========================================================================
  * Function: rastfbox ( expression, size, basesp, arg1, arg2, arg3 )   * Function: rastfbox ( expression, size, basesp, arg1, arg2, arg3 )
  * Purpose: \fbox{subexpression} handler, returns subraster   * Purpose: \fbox{subexpression} handler, returns subraster
  * containing subexpression with frame box drawn around it   * containing subexpression with frame box drawn around it
Line 8267  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 8289  subraster *rastfbox ( char **expression, Line 11534  subraster *rastfbox ( char **expression,
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 Allocations and Declarations  Allocations and Declarations
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 char *texsubexpr(), subexpr[8192], 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 8324  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 8332  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 8355  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 8367  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 8377  subraster *rastinput ( char **expression Line 11647  subraster *rastinput ( char **expression
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 Allocations and Declarations  Allocations and Declarations
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 char *texsubexpr(), tag[512]="\000", filename[1024]="\000"; /* args */  char *texsubexpr(), tag[1024]="\000", filename[1024]="\000"; /* args */
 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 */
 char subexpr[8192], /* concatanated lines from input file */  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*/
  *mimeprep(), /* preprocess inputted data */   *mimeprep(), /* preprocess inputted data */
  *dtoa(), *reformat=NULL; /* reformat numerical input */   *dbltoa(), *reformat=NULL; /* reformat numerical input */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 obtain [tag]{filename} argument  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[2048]; /* optional argument field */    { char argfld[MAXTOKNSZ+1]; /* optional argument field */
     *expression = texsubexpr(*expression,argfld,2047,"[","]",0,0);      *expression = texsubexpr(*expression,argfld,MAXTOKNSZ-1,"[","]",0,0);
     if ( (reformat=strstr(argfld,"dtoa")) != NULL ) /* dtoa requested */      if ( (reformat=strstr(argfld,"dtoa")) != NULL ) /*dtoa/dbltoa requested*/
       { format = 1; /* signal dtoa() 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 rasterize constructed subexpression  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) --- */
   /* --------------------------------------------------------------------------
   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,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() */  
  { double d = strtod(subexpr,NULL); /* interpret subexpr as double */  
    if ( d != 0.0 ) /* conversion to double successful */  
     if ( (reformat=dtoa(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 8435  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 8447  end_of_job: Line 11743  end_of_job:
  * requested, or NULL for any parsing error   * requested, or NULL for any parsing error
  * --------------------------------------------------------------------------   * --------------------------------------------------------------------------
  * Notes:     o Summary of syntax...   * Notes:     o Summary of syntax...
  *  \counter[value][logfile]{filename}   *  \counter[value][logfile]{filename:tag}
  *      o   *      o :tag is optional
  * ======================================================================= */   * ======================================================================= */
 /* --- entry point --- */  /* --- entry point --- */
 subraster *rastcounter ( char **expression, int size, subraster *basesp,  subraster *rastcounter ( char **expression, int size, subraster *basesp,
Line 8458  subraster *rastcounter ( char **expressi Line 11754  subraster *rastcounter ( char **expressi
 Allocations and Declarations  Allocations and Declarations
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 char *texsubexpr(), filename[1024]="\000", /* counter file */  char *texsubexpr(), filename[1024]="\000", /* counter file */
  logfile[1024]="\000", tag[512]="\000"; /* log file and tag */   logfile[1024]="\000", tag[1024]="\000"; /*optional log file and tag*/
 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 rastreadfile(), rastwritefile(); /* to read and write counter file */  int status=0,rastreadfile(),rastwritefile(), /*read,write counter file*/
 char text[2048] = "1_", /* first (and only) line in counter file */   iscounter = (seclevel<=counterseclevel?1:0), /*is \counter permitted*/
    isstrict = 1; /* true to only write to existing files */
   char text[MAXFILESZ] = "1_", /* only line in counter file without tags */
  *delim = NULL, /* delimiter in text */   *delim = NULL, /* delimiter in text */
  utext[32] = "1_", /* default delimiter */   utext[128] = "1_", /* default delimiter */
  *udelim = utext+1; /* underscore delimiter */   *udelim = utext+1; /* underscore delimiter */
 char *timestamp(), /* timestamp for logging */  char *rasteditfilename(), /* edit log file name */
  *dtoa(); /* double to comma-separated */   *timestamp(), /* timestamp for logging */
    *dbltoa(); /* double to comma-separated ascii */
 int counter = 1, /* atoi(text) (after _ removed, if present) */  int counter = 1, /* atoi(text) (after _ removed, if present) */
  gotcount = 0, /* set true once counter value determined */   value = 1, /* optional [value] argument */
    gotvalue = 0, /* set true if [value] supplied */
    isdelta = 0, /* set true if [+value] or [-value] is delta*/
  ordindex = (-1); /* ordinal[] index to append ordinal suffix */   ordindex = (-1); /* ordinal[] index to append ordinal suffix */
 /*--- ordinal suffixes based on units digit of counter ---*/  /*--- ordinal suffixes based on units digit of counter ---*/
 static char *ordinal[]={"th","st","nd","rd","th","th","th","th","th","th"};  static char *ordinal[]={"th","st","nd","rd","th","th","th","th","th","th"};
Line 8480  first obtain optional [value][logfile] a Line 11781  first obtain optional [value][logfile] a
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 /* --- first check for optional \counter[value] --- */  /* --- first check for optional \counter[value] --- */
 if ( *(*expression) == '[' ) /* check for []-enclosed value */  if ( *(*expression) == '[' ) /* check for []-enclosed value */
   { *expression = texsubexpr(*expression,text,2047,"[","]",0,0);    { *expression = texsubexpr(*expression,text,1023,"[","]",0,0);
     if ( *text != '\000' ) /* got counter value */      if ( *text != '\000' ) /* got counter value (or logfile) */
      if ( strlen(text) >= 1 ) /* and it's not an empty string */       if ( strlen(text) >= 1 ) { /* and it's not an empty string */
       if ( isdigit((int)(*text)) ) /* leading 0-9 digit signals value */        if ( isthischar(*text,"+-0123456789") ) /* check for leading +-digit */
        { counter = (int)(strtod(text,&udelim)+0.1); /* value and delim */   gotvalue = 1; /* signal we got optional value */
  gotcount = 1; } /* signal we got counter value */        else /* not +-digit, so must be logfile */
       else /* not a digit, so must be logfile */   strcpy(logfile,text); } /* so just copy it */
  strcpy(logfile,text); /* so just copy it */  
   } /* --- end-of-if(**expression=='[') --- */    } /* --- end-of-if(**expression=='[') --- */
 /* --- next check for optional \counter[][logfile] --- */  /* --- next check for optional \counter[][logfile] --- */
 if ( *(*expression) == '[' ) /* check for []-enclosed logfile */  if ( *(*expression) == '[' ) /* check for []-enclosed logfile */
   { *expression = texsubexpr(*expression,filename,1023,"[","]",0,0);    { *expression = texsubexpr(*expression,filename,1023,"[","]",0,0);
     if ( *text != '\000' ) /* got logfile value */      if ( *filename != '\000' ) /* got logfile (or counter value) */
      if ( strlen(filename) >= 1 ) /* and it's not an empty string */       if ( strlen(filename) >= 1 ) { /* and it's not an empty string */
       if ( !(isdigit((int)(*filename))) /*leading non-digit signals logfile*/        if ( !(isthischar(*text,"+-0123456789")) /* not a leading +-digit */
       ||   gotcount ) /* or we already got counter value */        ||   gotvalue ) /* or we already got counter value */
  strcpy(logfile,filename); /* so just copy it */   strcpy(logfile,filename); /* so just copy it */
       else /* 0-9 digit, so must be value */        else /* leading +-digit must be value */
        { strcpy(text,filename); /* copy value to text line */   { strcpy(text,filename); /* copy value to text line */
  counter = (int)(strtod(text,&udelim)+0.1); /* value and delim */    gotvalue = 1; } } /* and signal we got optional value*/
  gotcount = 1; } /* signal we got counter value */  
   } /* --- end-of-if(**expression=='[') --- */    } /* --- end-of-if(**expression=='[') --- */
   /* --- evaluate [value] if present --- */
   if ( gotvalue ) { /*leading +-digit should be in text*/
    if ( *text == '+' ) isdelta = (+1); /* signal adding */
    if ( *text == '-' ) isdelta = (-1); /* signal subtracting */
    value = (int)(strtod((isdelta==0?text:text+1),&udelim)+0.1); /*abs(value)*/
    if ( isdelta == (-1) ) value = (-value); /* set negative value if needed */
    counter = value; /* re-init counter */
    } /* --- end-of-if(gotvalue) --- */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 obtain counter {filename} argument  obtain counter {filename} argument
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
Line 8514  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 */
   {    {
   /* --- read and interpret first (and only) line from counter file --- */    /* --- read and interpret first (and only) line from counter file --- */
   if ( !gotcount ) /* if no [count] argument supplied */    if ( !gotvalue || (isdelta!=0) ) /*if no [count] arg or if delta arg*/
    if ( rastreadfile(filename,tag,text) != 0 ) /* try reading it from file */     if ( (status=rastreadfile(filename,1,tag,text)) > 0 ) /*try reading file*/
     { counter= 1 + (int)(strtod(text,&udelim)+0.1); /*counter val and delim*/      { char *vdelim = NULL; /* underscore delim from file */
       gotcount = 1; } /* signal we got counter value */        double fileval  = strtod(text,&vdelim); /* value and delim from file */
         counter = (int)(fileval<0.0?fileval-0.1:fileval+0.1); /* integerized */
         counter += value; /* bump count by 1 or add/sub delta*/
         if ( !gotvalue ) udelim=vdelim; } /* default to file's current delim */
   /* --- check for ordinal suffix --- */    /* --- check for ordinal suffix --- */
   if ( udelim != (char *)NULL ) /* have some delim after value */    if ( udelim != (char *)NULL ) /* have some delim after value */
    if ( *udelim == '_' ) /* underscore signals ordinal */     if ( *udelim == '_' ) /* underscore signals ordinal */
     { ordindex = counter%10; /* least significant digit */      { int abscount = (counter>=0?counter:(-counter)); /* abs(counter) */
       if ( counter >= 10 ) /* counter is 10 or greater */        ordindex = abscount%10; /* least significant digit */
        if ( (counter/10)%10 == 1 ) /* and the last two are 10-19 */        if ( abscount >= 10 ) /* counter is 10 or greater */
          if ( (abscount/10)%10 == 1 ) /* and the last two are 10-19 */
  ordindex = 0; } /* use th for 11,12,13 rather than st,nd,rd */   ordindex = 0; } /* use th for 11,12,13 rather than st,nd,rd */
   /* --- rewrite counter file --- */    /* --- rewrite counter file --- */
   sprintf(text,"%d",counter); /*build image of incremented counter*/    if ( status >= 0 ) /* file was read okay */
   if ( ordindex >= 0 ) strcat(text,"_"); /* tack on _ */     { sprintf(text,"%d",counter); /*build image of incremented counter*/
   if ( *tag == '\000' ) strcat(text,"\n"); /* and newline */       if ( ordindex >= 0 ) strcat(text,"_"); /* tack on _ */
   rastwritefile(filename,tag,text,1); /* rewrite incremented counter */       if ( *tag == '\000' ) strcat(text,"\n"); /* and newline */
        status = rastwritefile(filename,tag,text,isstrict); } /*rewrite counter*/
   } /* --- end-of-if(strlen(filename)>1) --- */    } /* --- end-of-if(strlen(filename)>1) --- */
 /* --------------------------------------------------------------------------  /* --------------------------------------------------------------------------
 log counter request  log counter request
Line 8543  if ( strlen(logfile) > 1 )  /* optional Line 11864  if ( strlen(logfile) > 1 )  /* optional
  {   {
  char comment[1024] = "\000", /* embedded comment, logfile:comment*/   char comment[1024] = "\000", /* embedded comment, logfile:comment*/
  *commptr = strchr(logfile,':'); /* check for : signalling comment */   *commptr = strchr(logfile,':'); /* check for : signalling comment */
    int islogokay = 1; /* logfile must exist if isstrict */
  if ( commptr != NULL ) /* have embedded comment */   if ( commptr != NULL ) /* have embedded comment */
   { strcpy(comment,commptr+1); /* comment follows : */    { strcpy(comment,commptr+1); /* comment follows : */
     *commptr = '\000'; } /* null-terminate actual logfile */      *commptr = '\000'; } /* null-terminate actual logfile */
  if ( (logfp = fopen(logfile,"a")) /* open logfile */   strcpy(logfile,rasteditfilename(logfile)); /* edit log file name */
  != (FILE *)NULL ) /* opened successfully */   if ( *logfile == '\000' ) islogokay = 0; /* given an invalid file name */
   {   else if ( isstrict ) { /*okay, but only write if it exists*/
   int ilog=0; /* logvars[] index */    if ( (logfp=fopen(logfile,"r")) == (FILE *)NULL ) /*doesn't already exist*/
   fprintf(logfp,"%s  ",timestamp()); /* first emit timestamp */      islogokay = 0; /* so don't write log file */
   if (*tag=='\000') fprintf(logfp,"%s",filename); /* emit counter filename */    else fclose(logfp); } /* close file opened for test read */
   else fprintf(logfp,"<%s>",tag); /* or tag if we have one */   if ( islogokay ) /* okay to write logfile */
   fprintf(logfp,"=%d",counter); /* emit counter value */    if ( (logfp = fopen(logfile,"a")) /* open logfile */
   for ( ilog=0; logvars[ilog] != NULL; ilog++ ) /* log till end-of-table */    != (FILE *)NULL ) { /* opened successfully for append */
    if ( ilog == commentvar /* replace with comment... */     int ilog=0; /* logvars[] index */
    &&   commptr != NULL ) /* ...if available */       fprintf(logfp,"%s  ",timestamp(TZDELTA,0)); /* first emit timestamp */
     fprintf(logfp,"  %.256s",comment); /* log embedded comment */     if (*tag=='\000') fprintf(logfp,"%s",filename); /* emit counter filename */
    else     else fprintf(logfp,"<%s>",tag); /* or tag if we have one */
     { char *logval = getenv(logvars[ilog]); /* getenv(variable) to be logged*/     fprintf(logfp,"=%d",counter); /* emit counter value */
       fprintf(logfp,"  %.64s", /* log variable */     if ( status < 1 ) /* read or re-write failed */
       fprintf(logfp,"(%s %d)","error status",status); /* emit error */
      for ( ilog=0; logvars[ilog] != NULL; ilog++ ) /* log till end-of-table */
       if ( ilog == commentvar /* replace with comment... */
       &&   commptr != NULL ) /* ...if available */  
        fprintf(logfp,"  %.256s",comment); /* log embedded comment */
       else
        { char *logval = getenv(logvars[ilog]); /*getenv(variable) to be logged*/
          fprintf(logfp,"  %.64s", /* log variable */
  (logval!=NULL?logval:"<unknown>")); } /* emit value or <unknown> */   (logval!=NULL?logval:"<unknown>")); } /* emit value or <unknown> */
   fprintf(logfp,"\n"); /* terminating newline */     fprintf(logfp,"\n"); /* terminating newline */
   fclose(logfp); /* close logfile */     fclose(logfp); /* close logfile */
   } /* --- end-of-if(logfp!=NULL) --- */     } /* --- end-of-if(islogokay&&logfp!=NULL) --- */
  } /* --- end-of-if(strlen(logfile)>1) --- */   } /* --- end-of-if(strlen(logfile)>1) --- */
 /* --------------------------------------------------------------------------  /* --------------------------------------------------------------------------
 construct counter expression and rasterize it  construct counter expression and rasterize it
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 /* --- construct expression --- */  /* --- construct expression --- */
 /*sprintf(text,"%d",counter);*/ /* start with counter */  /*sprintf(text,"%d",counter);*/ /* start with counter */
 strcpy(text,dtoa(((double)counter),0)); /* comma-separated counter value */  strcpy(text,dbltoa(((double)counter),0)); /* comma-separated counter value */
 if ( ordindex >= 0 ) /* need to tack on ordinal suffix */  if ( ordindex >= 0 ) /* need to tack on ordinal suffix */
   { strcat(text,"^{\\underline{\\rm~"); /* start with ^ and {\underline{\rm */    { strcat(text,"^{\\underline{\\rm~"); /* start with ^ and {\underline{\rm */
     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 8585  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 )
    * Purpose: handle \today
    * --------------------------------------------------------------------------
    * Arguments: expression (I/O) char **  to first char of null-terminated
    * string immediately following \today,
    * 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 \today
    * (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 *rasttoday ( char **expression, int size, subraster *basesp,
    int arg1, int arg2, int arg3 )
   {
   /* -------------------------------------------------------------------------
   Allocations and Declarations
   -------------------------------------------------------------------------- */
   char *texsubexpr(), optarg[2050]; /* optional [+/-tzdelta,ifmt] args */
   char *timestamp(), *today=optarg; /* timestamp to be rasterized */
   subraster *rasterize(), *todaysp=NULL; /* rasterize timestamp */
   int ifmt=1, tzdelta=0; /* default timestamp() args */
   /* -------------------------------------------------------------------------
   Get optional args \today[+/-tzdelta,ifmt]
   -------------------------------------------------------------------------- */
   /* --- check for optional \today[+/-tzdelta,ifmt] --- */
   if ( *(*expression) == '[' ) /* check for []-enclosed value */
     { *expression = texsubexpr(*expression,optarg,2047,"[","]",0,0);
       if ( *optarg != '\000' ) /* got optional arg */
        { char *comma = strchr(optarg,','); /* comma between +/-tzdelta,ifmt */
          int iarg, nargs=(comma==NULL?1:2); /* #optional args between []'s */
          if ( comma != NULL ) *comma = '\000'; /* null-terminate first arg */
          for ( iarg=1; iarg<=nargs; iarg++ ) /* process one or both args */
    { char *arg = (iarg==1?optarg:comma+1); /* choose 1st or 2nd arg */
     if ( isthischar(*arg,"+-") ) /* leading +/- signals tzdelta */
       tzdelta = atoi(arg); /* so interpret arg as tzdelta */
     else ifmt = atoi(arg); } /* else interpret args as ifmt */
        } /* --- end-of-if(*optarg!='\0') --- */
     } /* --- end-of-if(**expression=='[') --- */
   /* -------------------------------------------------------------------------
   Get timestamp and rasterize it
   -------------------------------------------------------------------------- */
   strcpy(today,"\\text{"); /* rasterize timestamp as text */
   strcat(today,timestamp(tzdelta,ifmt)); /* get timestamp */
   strcat(today,"}"); /* terminate \text{} braces */
   todaysp = rasterize(today,size); /* rasterize timestamp */
   /* --- return timestamp raster to caller --- */
   /*end_of_job:*/
     return ( todaysp ); /* return timestamp to caller */
   } /* --- end-of-function rasttoday() --- */
   
   
   /* ==========================================================================
    * Function: rastcalendar ( expression, size, basesp, arg1, arg2, arg3 )
    * Purpose: handle \calendar
    * --------------------------------------------------------------------------
    * Arguments: expression (I/O) char **  to first char of null-terminated
    * string immediately following \calendar
    * 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 \calendar
    * (unused, but passed for consistency)
    * arg1 (I) int unused
    * arg2 (I) int unused
    * arg3 (I) int unused
    * --------------------------------------------------------------------------
    * Returns: ( subraster * ) subraster ptr to rendered one-month calendar
    * --------------------------------------------------------------------------
    * Notes:     o
    * ======================================================================= */
   /* --- entry point --- */
   subraster *rastcalendar ( char **expression, int size, subraster *basesp,
    int arg1, int arg2, int arg3 )
   {
   /* -------------------------------------------------------------------------
   Allocations and Declarations
   -------------------------------------------------------------------------- */
   char *texsubexpr(), optarg[2050]; /* optional [year,month] args */
   char *calendar(), *calstr=NULL; /* calendar to be rasterized */
   subraster *rasterize(), *calendarsp=NULL; /* rasterize calendar string */
   int year=0,month=0,day=0, argval=0; /* default calendar() args */
   /* -------------------------------------------------------------------------
   Get optional args \today[+/-tzdelta,ifmt]
   -------------------------------------------------------------------------- */
   /* --- check for optional \calendar[year,month] --- */
   if ( *(*expression) == '[' ) /* check for []-enclosed value */
     { *expression = texsubexpr(*expression,optarg,2047,"[","]",0,0);
       if ( *optarg != '\000' ) /* got optional arg */
        { char *comma = strchr(optarg,','), /* comma between year,month */
          *comma2 = NULL; /* second comma before day */
          int iarg, nargs=(comma==NULL?1:2); /* #optional args between []'s */
          if ( comma != NULL ) { *comma = '\000'; /*null-terminate first arg*/
    if ( (comma2=strchr(comma+1,',')) != NULL ) /* have third arg */
    { *comma2 = '\000'; nargs++; } } /* null-term 2nd arg, bump count */
          for ( iarg=1; iarg<=nargs; iarg++ ) /* process one or both args */
    { char *arg= (iarg==1?optarg:(iarg==2?comma+1:comma2+1)); /*get arg*/
     argval = atoi(arg); /* interpret arg as integer */
     if ( iarg < 3 ) /* first two args are month,year */
      {if ( argval>1972 && argval<2100 ) year = argval; /* year value */
       else if ( argval>=1 && argval<=12 ) month = argval;} /*or month*/
     else /* only 3rd arg can be day */
      if ( argval>=1 && argval<=31 ) day = argval; } /* day value */
        } /* --- end-of-if(*optarg!='\0') --- */
     } /* --- end-of-if(**expression=='[') --- */
   /* -------------------------------------------------------------------------
   Get calendar string and rasterize it
   -------------------------------------------------------------------------- */
   if ( msgfp!= NULL && msglevel>=9 )
     fprintf(msgfp,"rastcalendar> year=%d, month=%d, day=%d\n",
     year,month,day);
   calstr = calendar(year,month,day); /* get calendar string */
   calendarsp = rasterize(calstr,size); /* rasterize calendar string */
   /* --- return calendar raster to caller --- */
   /*end_of_job:*/
     return ( calendarsp ); /* return calendar to caller */
   } /* --- end-of-function rastcalendar() --- */
   
   
   /* ==========================================================================
    * 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 8594  countersp = rasterize(text,size); /* ras Line 12268  countersp = rasterize(text,size); /* ras
  * following last character processed.   * following last character processed.
  * size (I) int containing 0-5 default font size   * size (I) int containing 0-5 default font size
  * basesp (I) subraster *  to character (or subexpression)   * basesp (I) subraster *  to character (or subexpression)
  * immediately preceding \fbox   * immediately preceding \escape
  * (unused, but passed for consistency)   * (unused, but passed for consistency)
  * nargs (I) int containing number of {}-args after   * nargs (I) int containing number of {}-args after
  * \escape to be flushed along with it   * \escape to be flushed along with it
Line 8612  subraster *rastnoop ( char **expression, Line 12286  subraster *rastnoop ( char **expression,
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 Allocations and Declarations  Allocations and Declarations
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 char *texsubexpr(), subexpr[8192]; /* flush dummy args eaten by \escape*/  char *texsubexpr(), subexpr[MAXSUBXSZ+1]; /*dummy args eaten by \escape*/
 subraster *rasterize(), *noopsp=NULL; /* rasterize subexpr */  subraster *rasterize(), *noopsp=NULL; /* rasterize subexpr */
 /* --- flush accompanying args if necessary --- */  /* --- flush accompanying args if necessary --- */
 if ( nargs != NOVALUE /* not unspecified */  if ( nargs != NOVALUE /* not unspecified */
Line 8648  FILE *rastopenfile ( char *filename, cha Line 12322  FILE *rastopenfile ( char *filename, cha
 Allocations and Declarations  Allocations and Declarations
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 FILE *fp = (FILE *)NULL /*,*fopen()*/; /*file pointer to opened filename*/  FILE *fp = (FILE *)NULL /*,*fopen()*/; /*file pointer to opened filename*/
 char texfile[2048] = "\000", /* local copy of input filename */  char texfile[2050] = "\000", /* local, edited copy of filename */
  amode[128] = "r"; /* test open mode if arg mode=NULL */   *rasteditfilename(), /* prepend pathprefix if necessary */
 int ismode = 0, /* true of mode!=NULL */   amode[512] = "r"; /* test open mode if arg mode=NULL */
  isprefix = (*pathprefix=='\000'?0:1); /* true if paths have prefix */  int ismode = 0; /* true of mode!=NULL */
 /* --------------------------------------------------------------------------  /* --------------------------------------------------------------------------
 Check mode and open file  Check mode and open file
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 /* --- check filename --- */  /* --- edit filename --- */
 if ( filename != (char *)NULL ) /* caller passed filename arg */  strncpy(texfile,rasteditfilename(filename),2047); /*edited copy of filename*/
  if ( strlen(filename) >= 1 ) /* make sure we got actual filename*/  texfile[2047] = '\000'; /* make sure it's null terminated */
   { char *pfilename = filename; /* ptr to 1st char of filename */  
     *texfile = '\000'; /* init filename as null string */  
     while ( isthischar(*pfilename," /\\") ) /* absolute paths invalid */  
       pfilename++; /* so flush leading / or \ (or ' ')*/  
     if ( isprefix && *pfilename!='\000' ) /* paths preceded by prefix */  
       { strcat(texfile,pathprefix); /* init filename with path */  
  while ( memcmp(pfilename,"../",3)==0 /* have leading ../ */  
   || memcmp(pfilename,"..\\",3)==0 ) /* or ..\ with prefix */  
     pfilename += 3; } /* flush leading ../ or ..\ */  
     strcat(texfile,pfilename); /* local copy of given filename */  
     compress(texfile,' '); } /* remove embedded blanks */  
 /* --- check mode --- */  /* --- check mode --- */
 if ( mode != (char *)NULL ) /* caller passed mode arg */  if ( mode != (char *)NULL ) /* caller passed mode arg */
  if ( *mode != '\000' ) /* and it's not an empty string */   if ( *mode != '\000' ) /* and it's not an empty string */
   { ismode = 1; /* so flip mode flag true */    { ismode = 1; /* so flip mode flag true */
     strcpy(amode,mode); /* and replace "r" with caller's */      strncpy(amode,mode,254); /* and replace "r" with caller's */
       amode[254] = '\000'; /* make sure it's null terminated */
     compress(amode,' '); } /* remove embedded blanks */      compress(amode,' '); } /* remove embedded blanks */
 /* --- open filename or filename.tex --- */  /* --- open filename or filename.tex --- */
 if ( strlen(texfile) > 1 ) /* make sure we got actual filename*/  if ( strlen(texfile) > 1 ) /* make sure we got actual filename*/
Line 8697  if ( !ismode && fp!=NULL )  /* no mode, Line 12361  if ( !ismode && fp!=NULL )  /* no mode,
   
   
 /* ==========================================================================  /* ==========================================================================
  * Function: rastreadfile ( filename, tag, value )   * Function: rasteditfilename ( filename )
    * Purpose: edits filename to remove security problems,
    * e.g., removes all ../'s and ..\'s.
    * --------------------------------------------------------------------------
    * Arguments: filename (I) char * to null-terminated string containing
    * name of file to be edited
    * --------------------------------------------------------------------------
    * Returns: ( char * ) pointer to edited filename,
    * or empty string "\000" if any problem
    * --------------------------------------------------------------------------
    * Notes:     o
    * ======================================================================= */
   /* --- entry point --- */
   char *rasteditfilename ( char *filename )
   {
   /* -------------------------------------------------------------------------
   Allocations and Declarations
   -------------------------------------------------------------------------- */
   static char editname[2050]; /*edited filename returned to caller*/
   char *strchange(); /* prepend pathprefix if necessary */
   int strreplace(), /* remove ../'s and ..\'s */
    isprefix = (*pathprefix=='\000'?0:1); /* true if paths have prefix */
   /* --------------------------------------------------------------------------
   edit filename
   -------------------------------------------------------------------------- */
   /* --- first check filename arg --- */
   *editname = '\000'; /* init edited name as empty string*/
   if ( filename == (char *)NULL ) goto end_of_job; /* no filename arg */
   if ( *filename == '\000' ) goto end_of_job; /* filename is an empty string */
   /* --- init edited filename --- */
   strcpy(editname,filename); /* init edited name as input name */
   compress(editname,' '); /* remove embedded blanks */
   /* --- remove leading or embedded ....'s --- */
   while ( strreplace(editname,"....",NULL,0) > 0 ) ;  /* squeeze out ....'s */
   /* --- remove leading / and \ and dots (and blanks) --- */
   if ( *editname != '\000' ) /* still have chars in filename */
    while ( isthischar(*editname," ./\\") ) /* absolute paths invalid */
      {strsqueeze(editname,1);} /* so flush leading / or \ (or ' ')*/
   if ( *editname == '\000' ) goto end_of_job; /* no chars left in filename */
   /* --- remove leading or embedded ../'s and ..\'s --- */
   while ( strreplace(editname,"../",NULL,0) > 0 ) ;  /* squeeze out ../'s */
   while ( strreplace(editname,"..\\",NULL,0) > 0 ) ; /* and ..\'s */
   while ( strreplace(editname,"../",NULL,0) > 0 ) ;  /* and ../'s again */
   /* --- prepend path prefix (if compiled with -DPATHPREFIX) --- */
   if ( isprefix && *editname!='\000' ) /* filename is preceded by prefix */
     strchange(0,editname,pathprefix); /* so prepend prefix */
   end_of_job:
     return ( editname ); /* back with edited filename */
   } /* --- end-of-function rasteditfilename() --- */
   
   
   /* ==========================================================================
    * Function: rastreadfile ( filename, islock, tag, value )
  * Purpose: Read filename, returning value as string   * Purpose: Read filename, returning value as string
  * between <tag>...</tag> or entire file if tag=NULL passed.   * between <tag>...</tag> or entire file if tag=NULL passed.
  * --------------------------------------------------------------------------   * --------------------------------------------------------------------------
  * Arguments: filename (I) char * to null-terminated string containing   * Arguments: filename (I) char * to null-terminated string containing
  * name of file to read (preceded by path   * name of file to read (preceded by path
  * relative to mimetex executable)   * relative to mimetex executable)
    * islock (I) int containing 1 to lock file while reading
    * (hopefully done by opening in "r+" mode)
  * tag (I) char * to null-terminated string containing   * tag (I) char * to null-terminated string containing
  * html-like tagname.  File contents between   * html-like tagname.  File contents between
  * <tag> and </tag> will be returned, or   * <tag> and </tag> will be returned, or
Line 8716  if ( !ismode && fp!=NULL )  /* no mode, Line 12434  if ( !ismode && fp!=NULL )  /* no mode,
  * Notes:     o   * Notes:     o
  * ======================================================================= */   * ======================================================================= */
 /* --- entry point --- */  /* --- entry point --- */
 int rastreadfile ( char *filename, char *tag, char *value )  int rastreadfile ( char *filename, int islock, char *tag, char *value )
 {  {
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 Allocations and Declarations  Allocations and Declarations
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 FILE *fp = (FILE *)NULL, *rastopenfile(); /* pointer to opened filename */  FILE *fp = (FILE *)NULL, *rastopenfile(); /* pointer to opened filename */
 char texfile[2048] = "\000", /* local copy of input filename */  char texfile[1024] = "\000", /* local copy of input filename */
  text[4096]; /* line from input file */   text[MAXLINESZ+1]; /* line from input file */
 char *tagp, tag1[512], tag2[512]; /* left <tag> and right <tag/> */  char *tagp, tag1[1024], tag2[1024]; /* left <tag> and right <tag/> */
 int vallen=0, maxvallen=8000; /* #chars in value, max allowed */  int vallen=0, maxvallen=MAXFILESZ; /* #chars in value, max allowed */
 int status = 0; /* status returned, 1=okay */  int status = (-1); /* status returned, 1=okay */
 int tagnum = 0; /* tag we're looking for */  int tagnum = 0; /* tag we're looking for */
   /*int islock = 1;*/ /* true to lock file */
 /* --------------------------------------------------------------------------  /* --------------------------------------------------------------------------
 Open file  Open file
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
Line 8736  if ( value == (char *)NULL ) goto end_of Line 12455  if ( value == (char *)NULL ) goto end_of
 *value = '\000'; /* init buffer with empty string */  *value = '\000'; /* init buffer with empty string */
 /* --- open filename or filename.tex --- */  /* --- open filename or filename.tex --- */
 if ( filename != (char *)NULL ) /* make sure we got filename arg */  if ( filename != (char *)NULL ) /* make sure we got filename arg */
   { strcpy(texfile,filename); /* local copy of filename */    { strncpy(texfile,filename,1023); /* local copy of filename */
     fp = rastopenfile(texfile,"r"); } /* try opening it */      texfile[1023] = '\000'; /* make sure it's null terminated */
       fp = rastopenfile(texfile,(islock?"r+":"r")); } /* try opening it */
 /* --- check that file opened --- */  /* --- check that file opened --- */
 if ( fp == (FILE *)NULL ) /* failed to open file */  if ( fp == (FILE *)NULL ) /* failed to open file */
   { sprintf(value,"{\\normalsize\\rm[file %s?]}",texfile);    { sprintf(value,"{\\normalsize\\rm[file %s?]}",texfile);
     goto end_of_job; } /* return error message to caller */      goto end_of_job; } /* return error message to caller */
   status = 0; /* file opened successfully */
   if ( islock ) rewind(fp); /* start at beginning of file */
 /* --------------------------------------------------------------------------  /* --------------------------------------------------------------------------
 construct <tag>'s  construct <tag>'s
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
Line 8755  if ( tag != (char *)NULL )  /* caller pa Line 12477  if ( tag != (char *)NULL )  /* caller pa
 /* --------------------------------------------------------------------------  /* --------------------------------------------------------------------------
 Read file, concatnate lines  Read file, concatnate lines
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 while ( fgets(text,4090,fp) != (char *)NULL ) { /* read input till eof */  while ( fgets(text,MAXLINESZ-1,fp) != (char *)NULL ) { /*read input till eof*/
   switch ( tagnum ) { /* look for left- or right-tag */    switch ( tagnum ) { /* look for left- or right-tag */
     case 0: 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*/
       *tagp = '\000'; /* terminate line at tag */        *tagp = '\000'; /* terminate line at tag */
       tagnum = 3; /* done after this line */        tagnum = 3; /* done after this line */
         status = 1; /* successfully read tag */
       break;        break;
     } /* ---end-of-switch(tagnum) --- */      } /* ---end-of-switch(tagnum) --- */
   if ( tagnum != 1 ) { /* no tag or left tag already found*/    if ( tagnum != 1 ) { /* no tag or left tag already found*/
Line 8812  int rastwritefile( char *filename, char Line 12536  int rastwritefile( char *filename, char
 Allocations and Declarations  Allocations and Declarations
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 FILE *fp = (FILE *)NULL, *rastopenfile(); /* pointer to opened filename */  FILE *fp = (FILE *)NULL, *rastopenfile(); /* pointer to opened filename */
 char texfile[2048] = "\000", /* local copy of input filename */  char texfile[1024] = "\000", /* local copy of input filename */
  filebuff[16384] = "\000", /* entire contents of file */   filebuff[MAXFILESZ+1] = "\000", /* entire contents of file */
  tag1[512], tag2[512], /* left <tag> and right <tag/> */   tag1[1024], tag2[1024], /* left <tag> and right <tag/> */
  *strchange(), /* put value between <tag>...</tag>*/   *strchange(), /* put value between <tag>...</tag>*/
  *timestamp(); /* log modification time */   *timestamp(); /* log modification time */
 int istag=0, rastreadfile(), /* read file if tag!=NULL */  int istag=0, rastreadfile(), /* read file if tag!=NULL */
  /*isstrict = (seclevel>5? 1:0),*/ /*true only writes existing files*/   /*isstrict = (seclevel>5? 1:0),*/ /*true only writes existing files*/
  isnewfile = 0, /* true if writing new file */   isnewfile = 0, /* true if writing new file */
  status = 0; /* status returned, 1=okay */   status = 0; /* status returned, 1=okay */
   int istimestamp = 0; /* true to update <timestamp> tag */
 /* --------------------------------------------------------------------------  /* --------------------------------------------------------------------------
 check args  check args
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
Line 8830  if ( filename == (char *)NULL  /* quit i Line 12555  if ( filename == (char *)NULL  /* quit i
 if ( strlen(filename) < 2 /* quit if unreasonable filename */  if ( strlen(filename) < 2 /* quit if unreasonable filename */
 ||   *value == '\000' ) goto end_of_job; /* or empty value string supplied */  ||   *value == '\000' ) goto end_of_job; /* or empty value string supplied */
 /* --- establish filename[.tex] --- */  /* --- establish filename[.tex] --- */
 strcpy(texfile,filename); /* local copy of input filename */  strncpy(texfile,filename,1023); /* local copy of input filename */
   texfile[1023] = '\000'; /* make sure it's null terminated */
 if ( rastopenfile(texfile,NULL) /* unchanged or .tex appended */  if ( rastopenfile(texfile,NULL) /* unchanged or .tex appended */
 ==   (FILE *)NULL ) /* can't open, so write new file */  ==   (FILE *)NULL ) /* can't open, so write new file */
   { if ( isstrict ) goto end_of_job; /* fail if new files not permitted */    { if ( isstrict ) goto end_of_job; /* fail if new files not permitted */
Line 8850  read existing file if just rewriting a s Line 12576  read existing file if just rewriting a s
 *filebuff = '\000'; /* init as empty file */  *filebuff = '\000'; /* init as empty file */
 if ( !isnewfile ) /* if file already exists */  if ( !isnewfile ) /* if file already exists */
  if ( istag ) /* and just rewriting one tag */   if ( istag ) /* and just rewriting one tag */
   if ( rastreadfile(texfile,NULL,filebuff) /* read entire existing file */    if ( rastreadfile(texfile,1,NULL,filebuff) /* read entire existing file */
   ==   0 ) goto end_of_job; /* signal error if failed to read */    <=   0 ) goto end_of_job; /* signal error if failed to read */
 /* --------------------------------------------------------------------------  /* --------------------------------------------------------------------------
 construct new file data if needed (entire file replaced by value if no tag)  construct new file data if needed (entire file replaced by value if no tag)
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
Line 8866  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 8888  if ( istag )    /* only replacing tag in Line 12614  if ( istag )    /* only replacing tag in
    >=   0 ) /* usually <tag> precedes </tag> */     >=   0 ) /* usually <tag> precedes </tag> */
     strchange(flen,tagp1+tlen1,value); /* change ...'s to value */      strchange(flen,tagp1+tlen1,value); /* change ...'s to value */
    else /* weirdly, </tag> precedes <tag> */     else /* weirdly, </tag> precedes <tag> */
     { char fbuff[2048]; /* field buff for <tag>value</tag> */      { char fbuff[4096]; /* field buff for <tag>value</tag> */
       if ( (flen = ((int)(tagp1-tagp2))+tlen1) /* strlen(</tag>...<tag>) */        if ( (flen = ((int)(tagp1-tagp2))+tlen1) /* strlen(</tag>...<tag>) */
       <=   0 ) goto end_of_job; /* must be internal error */        <=   0 ) goto end_of_job; /* must be internal error */
       strcpy(fbuff,tag1); /* set opening <tag> */        strcpy(fbuff,tag1); /* set opening <tag> */
Line 8908  if ( fputs((istag?filebuff:value),fp) /* Line 12634  if ( fputs((istag?filebuff:value),fp) /*
 !=  EOF ) status = 1; /* signal success if succeeded */  !=  EOF ) status = 1; /* signal success if succeeded */
 fclose ( fp ); /* close output file after writing */  fclose ( fp ); /* close output file after writing */
 /* --- modify timestamp --- */  /* --- modify timestamp --- */
 if ( istag ) /* log mod time in tagged file */  if ( status > 0 ) /*forget timestamp if write failed*/
  if ( strstr(tag,"timestamp") == (char *)NULL ) /* but avoid recursion */   if ( istimestamp ) /* if we're updating timestamp */
   { char fbuff[2048]; /* field buff <timestamp> value */    if ( istag ) /* only log time in tagged file */
     strcpy(fbuff,tag); /* tag modified */     if ( strstr(tag,"timestamp") == (char *)NULL ) /* but avoid recursion */
     strcat(fbuff," modified at "); /* spacer */      { char fbuff[2048]; /* field buff <timestamp> value */
     strcat(fbuff,timestamp()); /* start with timestamp */        strcpy(fbuff,tag); /* tag modified */
     rastwritefile(filename,"timestamp",fbuff,1); }        strcat(fbuff," modified at "); /* spacer */
         strcat(fbuff,timestamp(TZDELTA,0)); /* start with timestamp */
         status = rastwritefile(filename,"timestamp",fbuff,1); }
 /* --- return status to caller --- */  /* --- return status to caller --- */
 end_of_job:  end_of_job:
   return ( status ); /* return status to caller */    return ( status ); /* return status to caller */
Line 8922  end_of_job: Line 12650  end_of_job:
   
   
 /* ==========================================================================  /* ==========================================================================
  * Function: timestamp ( )   * Function: calendar ( year, month, day )
    * Purpose: returns null-terminated character string containing
    * \begin{array}...\end{array} for the one-month calendar
    * specified by year=1973...2099 and month=1...12.
    * If either arg out-of-range, today's value is used.
    * --------------------------------------------------------------------------
    * Arguments: year (I) int containing 1973...2099 or 0 for current
    * year
    * month (I) int containing 1...12 or 0 for current month
    * day (I) int containing day to emphasize or 0
    * --------------------------------------------------------------------------
    * Returns: ( char * ) char ptr to null-terminated buffer
    * containing \begin{array}...\end{array}
    * string that will render calendar for
    * requested month, or NULL for any error.
    * --------------------------------------------------------------------------
    * Notes:     o
    * ======================================================================= */
   /* --- entry point --- */
   char *calendar( int year, int month, int day )
   {
   /* -------------------------------------------------------------------------
   Allocations and Declarations
   -------------------------------------------------------------------------- */
   static char calbuff[4096]; /* calendar returned to caller */
   time_t time_val = (time_t)(0); /* binary value returned by time() */
   struct tm *tmstruct=(struct tm *)NULL, *localtime(); /* interpret time_val */
   int yy=0, mm=0, dd=0; /* today (emphasize today's dd) */
   int idd=1, iday=0, daynumber(); /* day-of-week for idd=1...31 */
   char aval[64]; /* ascii day or 4-digit year */
   /* --- calendar data --- */
   static char *monthnames[] = { "?", "January", "February", "March", "April",
    "May", "June", "July", "August", "September", "October",
    "November", "December", "?" } ;
   static int modays[] =
    { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0 };
   /* -------------------------------------------------------------------------
   initialization
   -------------------------------------------------------------------------- */
   /* --- get current date/time --- */
   time((time_t *)(&time_val)); /* get date and time */
   tmstruct = localtime((time_t *)(&time_val)); /* interpret time_val */
   yy  =  1900 + (int)(tmstruct->tm_year); /* current four-digit year */
   mm  =  1 + (int)(tmstruct->tm_mon); /* current month, 1-12 */
   dd  =  (int)(tmstruct->tm_mday); /* current day, 1-31 */
   /* --- check args --- */
   if ( year<1973 || year>2099 ) year  = yy; /* current year if out-of-bounds */
   if ( month<1 || month>12 ) month = mm; /* current month if out-of-bounds */
   if ( month==mm && year==yy && day==0 ) /* current month and default day */
     day = dd; /* emphasize current day */
   modays[2] = (year%4==0?29:28); /* Feb has 29 days in leap years */
   /* --- initialize calendar string --- */
   strcpy(calbuff,"{\\begin{gather}"); /* center `month year` above cal */
   strcat(calbuff,"\\small\\text{"); /* month set in roman */
   strcat(calbuff,monthnames[month]); /* insert month name */
   strcat(calbuff," }"); /* add a space */
   sprintf(aval,"%d",year); /* convert year to ascii */
   strcat(calbuff,aval); /* add year */
   strcat(calbuff,"\\\\"); /* end top row */
   strcat(calbuff, /* now begin calendar arrayr */
    "\\begin{array}{|c|c|c|c|c|c|c|CCCCCC} \\hline"
    "\\tiny\\text{Sun} & \\tiny\\text{Mon} & \\tiny\\text{Tue} &"
    "\\tiny\\text{Wed} & \\tiny\\text{Thu} & \\tiny\\text{Fri} &"
    "\\tiny\\text{Sat} \\\\ \\hline " );
   /* -------------------------------------------------------------------------
   generate calendar
   -------------------------------------------------------------------------- */
   for ( idd=1; idd<=modays[month]; idd++ ) /* run through days of month */
     {
     /* --- get day-of-week for this day --- */
     iday = 1 + (daynumber(year,month,idd)%7); /* 1=Monday...7=Sunday */
     if ( iday == 7 ) iday = 0; /* now 0=Sunday...6=Saturday */
     /* --- may need empty cells at beginning of month --- */
     if ( idd == 1 ) /* first day of month */
      if ( iday > 0 ) /* need to skip cells */
       { strcpy(aval,"\\ &\\ &\\ &\\ &\\ &\\ &\\ &\\ &\\ &\\"); /*cells to skip*/
         aval[3*iday] = '\000'; /*skip cells preceding 1st of month*/
         strcat(calbuff,aval); } /* add skip string to buffer */
     /* --- add idd to current cell --- */
     sprintf(aval,"%d",idd); /* convert idd to ascii */
     if ( idd == day /* emphasize today's date */
     /*&&   month==mm && year==yy*/ ) /* only if this month's calendar */
      { strcat(calbuff,"{\\fs{-1}\\left\\langle "); /*emphasize, 1 size smaller*/
        strcat(calbuff,aval); /* put in idd */
        strcat(calbuff,"\\right\\rangle}"); } /* finish emphasis */
     else /* not today's date */
       strcat(calbuff,aval); /* so just put in idd */
     /* --- terminate cell --- */
     if ( idd < modays[month] ) { /* not yet end-of-month */
      if ( iday < 6 ) /* still have days left in week */
       strcat(calbuff,"&"); /* new cell in same week */
      else /* reached end-of-week */
       strcat(calbuff,"\\\\ \\hline"); } /* so start new week */
     } /* --- end-of-for(idd) --- */
   strcat(calbuff,"\\\\ \\hline"); /* final underline at end-of-month */
   /* --- return calendar to caller --- */
   strcat(calbuff,"\\end{array}\\end{gather}}"); /* terminate array */
   return ( calbuff ); /* back to caller with calendar */
   } /* --- end-of-function calendar() --- */
   
   
   /* ==========================================================================
    * Function: timestamp ( tzdelta, ifmt )
  * Purpose: returns null-terminated character string containing   * Purpose: returns null-terminated character string containing
  * current date:time stamp as ccyy-mm-dd:hh:mm:ss{am,pm}   * current date:time stamp as ccyy-mm-dd:hh:mm:ss{am,pm}
  * --------------------------------------------------------------------------   * --------------------------------------------------------------------------
  * Arguments: ( none )   * Arguments: tzdelta (I) integer, positive or negative, containing
    * containing number of hours to be added or
    * subtracted from system time (to accommodate
    * your desired time zone).
    * ifmt (I) integer containing 0 for default format
  * --------------------------------------------------------------------------   * --------------------------------------------------------------------------
  * Returns: ( char * ) ptr to null-terminated buffer   * Returns: ( char * ) ptr to null-terminated buffer
  * containing current date:time stamp   * containing current date:time stamp
Line 8934  end_of_job: Line 12768  end_of_job:
  * Notes:     o   * Notes:     o
  * ======================================================================= */   * ======================================================================= */
 /* --- entry point --- */  /* --- entry point --- */
 char *timestamp( )  char *timestamp( int tzdelta, int ifmt )
 {  {
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 Allocations and Declarations  Allocations and Declarations
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 static char timebuff[64]; /* date:time buffer back to caller */  static char timebuff[256]; /* date:time buffer back to caller */
 /*long time_val = 0L;*/ /* binary value returned by time() */  /*long time_val = 0L;*/ /* binary value returned by 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  */
    minute=0,second=0; /* minute and second not adjusted  */
   int tzadjust(); /* time zone adjustment function */
   int daynumber(); /* #days since Jan 1, 1973 */
   static char *daynames[] = { "Monday", "Tuesday", "Wednesday",
    "Thursday", "Friday", "Saturday", "Sunday" } ;
   static char *monthnames[] = { "?", "January", "February", "March", "April",
    "May", "June", "July", "August", "September", "October",
    "November", "December", "?" } ;
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 get current date:time, adjust values, and and format stamp  get current date:time, adjust values, and and format stamp
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
   /* --- first init returned timebuff in case of any error --- */
   *timebuff = '\000';
 /* --- get current date:time --- */  /* --- get current date:time --- */
 time((time_t *)(&time_val)); /* get date and time */  time((time_t *)(&time_val)); /* get date and time */
 tmstruct = localtime((time_t *)(&time_val)); /* interpret time_val */  tmstruct = localtime((time_t *)(&time_val)); /* interpret time_val */
 /* --- adjust year and hour as necessary --- */  /* --- extract fields --- */
 year = (int)(tmstruct->tm_year); /* local copy of year */  year  = (int)(tmstruct->tm_year); /* local copy of year,  0=1900 */
 hour = (int)(tmstruct->tm_hour); /* local copy of hour */  month = (int)(tmstruct->tm_mon) + 1; /* local copy of month, 1-12 */
   day   = (int)(tmstruct->tm_mday); /* local copy of day,   1-31 */
   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 --- */
 year += 1900; /* set century in year */  year += 1900; /* set century in year */
 if ( hour < 12 ) /* am check */  /* --- adjust for timezone --- */
   { ispm=0; /* reset pm flag */  tzadjust(tzdelta,&year,&month,&day,&hour);
     if ( hour == 0 ) hour = 12; } /* set 00hrs = 12am */  /* --- check params --- */
 if ( hour > 12 ) hour -= 12; /* pm check sets 13hrs to 1pm, etc */  if ( hour<0  || hour>23
   ||   day<1   || day>31
   ||   month<1 || month>12
   ||   year<1973 ) goto end_of_job;
   /* --- adjust hour for am/pm --- */
   switch ( ifmt )
     {
     default:
     case 0:
       if ( hour < 12 ) /* am check */
        { ispm=0; /* reset pm flag */
          if ( hour == 0 ) hour = 12; } /* set 00hrs = 12am */
       if ( hour > 12 ) hour -= 12; /* pm check sets 13hrs to 1pm, etc */
       break;
     } /* --- end-of-switch(ifmt) --- */
 /* --- format date:time stamp --- */  /* --- format date:time stamp --- */
 sprintf(timebuff,"%04d-%02d-%02d:%02d:%02d:%02d%s",  switch ( ifmt )
   year,(int)((tmstruct->tm_mon)+1),(int)(tmstruct->tm_mday),    {
   hour,(int)(tmstruct->tm_min),(int)(tmstruct->tm_sec),((ispm)?"pm":"am"));    default:
 return ( timebuff ); /* return stamp to caller */    case 0:  /* --- 2005-03-05:11:49:59am --- */
       sprintf(timebuff,"%04d-%02d-%02d:%02d:%02d:%02d%s", year,month,day,
       hour,minute,second,((ispm)?"pm":"am"));
       break;
     case 1:  /* --- Saturday, March 5, 2005 --- */
       sprintf(timebuff,"%s, %s %d, %d",
       daynames[daynumber(year,month,day)%7],monthnames[month],day,year);
       break;
     case 2: /* --- Saturday, March 5, 2005, 11:49:59am --- */
       sprintf(timebuff,"%s, %s %d, %d, %d:%02d:%02d%s",
       daynames[daynumber(year,month,day)%7],monthnames[month],day,year,
       hour,minute,second,((ispm)?"pm":"am"));
       break;
     case 3: /* --- 11:49:59am --- */
       sprintf(timebuff,"%d:%02d:%02d%s",
       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;
     } /* --- end-of-switch(ifmt) --- */
   end_of_job:
     return ( timebuff ); /* return stamp to caller */
 } /* --- end-of-function timestamp() --- */  } /* --- end-of-function timestamp() --- */
   
   
 /* ==========================================================================  /* ==========================================================================
  * Function: dtoa ( dblval, npts )   * Function: tzadjust ( tzdelta, year, month, day, hour )
    * Purpose: Adjusts hour, and day,month,year if necessary,
    * by delta increment to accommodate your time zone.
    * --------------------------------------------------------------------------
    * Arguments: tzdelta (I) integer, positive or negative, containing
    * containing number of hours to be added or
    * subtracted from given time (to accommodate
    * your desired time zone).
    * year (I) addr of int containing        4-digit year
    * month (I) addr of int containing month  1=Jan - 12=Dec.
    * day (I) addr of int containing day    1-31 for Jan.
    * hour (I) addr of int containing hour   0-23
    * Returns: ( int ) 1 for success, or 0 for error
    * --------------------------------------------------------------------------
    * Notes:     o
    * ======================================================================= */
   /* --- entry point --- */
   int tzadjust ( int tzdelta, int *year, int *month, int *day, int *hour )
   {
   /* --------------------------------------------------------------------------
   Allocations and Declarations
   -------------------------------------------------------------------------- */
   int yy = *year, mm = *month, dd = *day, hh = *hour; /*dereference args*/
   /* --- calendar data --- */
   static int modays[] =
    { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0 };
   /* --------------------------------------------------------------------------
   check args
   -------------------------------------------------------------------------- */
   if ( mm<1 || mm>12 ) return(-1); /* bad month */
   if ( dd<1 || dd>modays[mm] ) return(-1); /* bad day */
   if ( hh<0 || hh>23 ) return(-1); /* bad hour */
   if ( tzdelta>23 || tzdelta<(-23) ) return(-1); /* bad tzdelta */
   /* --------------------------------------------------------------------------
   make adjustments
   -------------------------------------------------------------------------- */
   /* --- adjust hour --- */
   hh += tzdelta; /* apply caller's delta */
   /* --- adjust for feb 29 --- */
   modays[2] = (yy%4==0?29:28); /* Feb has 29 days in leap years */
   /* --- adjust day --- */
   if ( hh < 0 ) /* went to preceding day */
     { dd--;  hh += 24; }
   if ( hh > 23 ) /* went to next day */
     { dd++;  hh -= 24; }
   /* --- adjust month --- */
   if ( dd < 1 ) /* went to preceding month */
     { mm--;  dd = modays[mm]; }
   if ( dd > modays[mm] ) /* went to next month */
     { mm++;  dd = 1; }
   /* --- adjust year --- */
   if ( mm < 1 ) /* went to preceding year */
     { yy--;  mm = 12;  dd = modays[mm]; }
   if ( mm > 12 ) /* went to next year */
     { yy++;  mm = 1;   dd = 1; }
   /* --- back to caller --- */
   *year=yy; *month=mm; *day=dd; *hour=hh; /* reset adjusted args */
   return ( 1 );
   } /* --- end-of-function tzadjust() --- */
   
   
   /* ==========================================================================
    * Function: daynumber ( year, month, day )
    * Purpose: Returns number of actual calendar days from Jan 1, 1973
    * to the given date (e.g., bvdaynumber(1974,1,1)=365).
    * --------------------------------------------------------------------------
    * Arguments: year (I) int containing year -- may be either 1995 or
    * 95, or may be either 2010 or 110 for those
    * years.
    * month (I) int containing month, 1=Jan thru 12=Dec.
    * day (I) int containing day of month, 1-31 for Jan, etc.
    * Returns: ( int ) Number of days from Jan 1, 1973 to given date,
    * or -1 for error (e.g., year<1973).
    * --------------------------------------------------------------------------
    * Notes:     o
    * ======================================================================= */
   /* --- entry point --- */
   int daynumber ( int year, int month, int day )
   {
   /* --------------------------------------------------------------------------
   Allocations and Declarations
   -------------------------------------------------------------------------- */
   /* --- returned value (note: returned as a default "int") --- */
   int ndays; /* #days since jan 1, year0 */
   /* --- initial conditions --- */
   static int year0 = 73, /* jan 1 was a monday, 72 was a leap */
    days4yrs = 1461, /* #days in 4 yrs = 365*4 + 1 */
    days1yr  = 365;
   /* --- table of accumulated days per month (last index not used) --- */
   static int modays[] =
    { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };
   /* --- variables for #days since day0 --- */
   int nyears, nfouryrs; /*#years, #4-yr periods since year0*/
   /* --------------------------------------------------------------------------
   Check input
   -------------------------------------------------------------------------- */
   if ( month < 1 || month > 12 ) /*month used as index, so must be ok*/
    return ( -1 ); /* otherwise, forget it */
   if ( year >= 1900 ) year -= 1900; /*use two-digit years (3 after 2000)*/
   /* --------------------------------------------------------------------------
   Find #days since jan 1, 1973
   -------------------------------------------------------------------------- */
   /* --- figure #complete 4-year periods and #remaining yrs till current --- */
   nyears = year - year0; /* #years since year0 */
   if ( nyears < 0 ) return ( -1 ); /* we're not working backwards */
   nfouryrs = nyears/4; /* #complete four-year periods */
   nyears -= (4*nfouryrs); /* remainder excluding current year*/
   /* --- #days from jan 1, year0 till jan 1, this year --- */
   ndays = (days4yrs*nfouryrs) /* #days in 4-yr periods */
         +  (days1yr*nyears); /* +remaining days */
   /*if ( year > 100 ) ndays--;*/ /* subtract leap year for 2000AD */
   /* --- add #days within current year --- */
   ndays += (modays[month-1] + (day-1));
   /* --- may need an extra day if current year is a leap year --- */
   if ( nyears == 3 ) /*three preceding yrs so this is 4th*/
       { if ( month > 2 ) /* past feb so need an extra day */
    /*if ( year != 100 )*/ /* unless it's 2000AD */
     ndays++; } /* so add it in */
   return ( (int)(ndays) ); /* #days back to caller */
   } /* --- end-of-function daynumber() --- */
   
   
   /* ==========================================================================
    * 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 )
  * 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 8980  return ( timebuff );   /* return stamp t Line 13257  return ( timebuff );   /* return stamp t
  * Notes:     o   * Notes:     o
  * ======================================================================= */   * ======================================================================= */
 /* --- entry point --- */  /* --- entry point --- */
 char *dtoa ( dblval, npts )  char *dbltoa ( double dblval, int npts )
 double dblval;  /* double dblval;
 int npts;     int npts; */
 {  {
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 Allocations and Declarations  Allocations and Declarations
 ------------------------------------------------------------------------- */  ------------------------------------------------------------------------- */
 static char finval[128]; /* buffer returned to caller */  static char finval[256]; /* buffer returned to caller */
 static char digittbl[32]="0123456789*"; /* table of ascii decimal digits */  static char digittbl[32]="0123456789*"; /* table of ascii decimal digits */
 char *finptr = finval; /* ptr to next char being converted*/  char *finptr = finval; /* ptr to next char being converted*/
 double floor(); /* integer which is glb(double) */  double floor(); /* integer which is glb(double) */
Line 9048  End-of-Job Line 13325  End-of-Job
 if ( isneg ) *finptr++ = ')'; /*trailing paren for negative value*/  if ( isneg ) *finptr++ = ')'; /*trailing paren for negative value*/
 *finptr = '\000'; /* null-terminate converted double */  *finptr = '\000'; /* null-terminate converted double */
 return ( finval ); /* converted double back to caller */  return ( finval ); /* converted double back to caller */
 } /* --- end-of-function dtoa() --- */  } /* --- end-of-function dbltoa() --- */
   
   
 /* ==========================================================================  /* ==========================================================================
Line 9152  for ( irow=0; irow<rp->height; irow++ ) Line 13429  for ( irow=0; irow<rp->height; irow++ )
       &&   bytemap[ipixel] > blackscale ) /* weighted avg > blackscale */        &&   bytemap[ipixel] > blackscale ) /* weighted avg > blackscale */
  bytemap[ipixel] = grayscale-1; } /* so force it entirely black */   bytemap[ipixel] = grayscale-1; } /* so force it entirely black */
   /*--- only anti-alias pixels whose adjacent pixels fall within bounds ---*/    /*--- only anti-alias pixels whose adjacent pixels fall within bounds ---*/
   if ( !iscenter ) /* apply min/maxadjacent test */    if ( !iscenter ) { /* apply min/maxadjacent test */
    if ( isminmaxwts ) /* min/max refer to adjacent weights*/     if ( isminmaxwts ) /* min/max refer to adjacent weights*/
     { if ( wadjacent < minadjacent /* wts of adjacent points too low */      { if ( wadjacent < minadjacent /* wts of adjacent points too low */
       ||   wadjacent > maxadjacent ) /* or too high */        ||   wadjacent > maxadjacent ) /* or too high */
Line 9160  for ( irow=0; irow<rp->height; irow++ ) Line 13437  for ( irow=0; irow<rp->height; irow++ )
    else /* min/max refer to #adjacent points*/     else /* min/max refer to #adjacent points*/
     { if ( nadjacent < minadjacent /* too few adjacent points black */      { if ( nadjacent < minadjacent /* too few adjacent points black */
       ||   nadjacent > maxadjacent ) /* or too many */        ||   nadjacent > maxadjacent ) /* or too many */
  bytemap[ipixel] = 0; } /* so leave point white */   bytemap[ipixel] = 0; } } /* so leave point white */
   } /* --- end-of-for(irow,icol) --- */    } /* --- end-of-for(irow,icol) --- */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 Back to caller with gray-scale anti-aliased bytemap  Back to caller with gray-scale anti-aliased bytemap
Line 9202  int width=rp->width, height=rp->height, Line 13479  int width=rp->width, height=rp->height,
  icol = 0,        irow = 0, /* width, height indexes */   icol = 0,        irow = 0, /* width, height indexes */
  imap = (-1); /* pixel index = icol + irow*width */   imap = (-1); /* pixel index = icol + irow*width */
 int bgbitval=0, fgbitval=1; /* background, foreground bitval */  int bgbitval=0, fgbitval=1; /* background, foreground bitval */
   int isfirstaa = 1; /*debugging switch signals 1st pixel*/
 #if 0  #if 0
 int totwts=12, wts[9]={1,1,1, 1,4,1, 1,1,1}; /* pnmalias default wts */  int totwts=12, wts[9]={1,1,1, 1,4,1, 1,1,1}; /* pnmalias default wts */
 int totwts=16, wts[9]={1,2,1, 2,4,2, 1,2,1}; /* weights */  int totwts=16, wts[9]={1,2,1, 2,4,2, 1,2,1}; /* weights */
Line 9279  for ( irow=0; irow<height; irow++ ) Line 13557  for ( irow=0; irow<height; irow++ )
  (eebitval==fgbitval && ssbitval==fgbitval) || /*lower-right edge*/   (eebitval==fgbitval && ssbitval==fgbitval) || /*lower-right edge*/
  (ssbitval==fgbitval && wwbitval==fgbitval) || /*lower-left  edge*/   (ssbitval==fgbitval && wwbitval==fgbitval) || /*lower-left  edge*/
  (wwbitval==fgbitval && nnbitval==fgbitval) ; /*upper-left  edge*/   (wwbitval==fgbitval && nnbitval==fgbitval) ; /*upper-left  edge*/
     /* ---check top/bot left/right edges for corners (added by j.forkosh)--- */
     if ( 1 ) { /* true to perform test */
       int isbghorz=0, isfghorz=0, isbgvert=0, isfgvert=0; /* horz/vert edges */
       isbghorz = /* top or bottom edge is all bg */
    (nwbitval+nnbitval+nebitval == 3*bgbitval) || /* top edge bg */
    (swbitval+ssbitval+sebitval == 3*bgbitval) ; /* bottom edge bg */
       isfghorz = /* top or bottom edge is all fg */
    (nwbitval+nnbitval+nebitval == 3*fgbitval) || /* top edge fg */
    (swbitval+ssbitval+sebitval == 3*fgbitval) ; /* bottom edge fg */
       isbgvert = /* left or right edge is all bg */
    (nwbitval+wwbitval+swbitval == 3*bgbitval) || /* left edge bg */
    (nebitval+eebitval+sebitval == 3*bgbitval) ; /* right edge bg */
       isfgvert = /* left or right edge is all bg */
    (nwbitval+wwbitval+swbitval == 3*fgbitval) || /* left edge fg */
    (nebitval+eebitval+sebitval == 3*fgbitval) ; /* right edge fg */
       if ( (isbghorz && isbgvert && (bitval==fgbitval)) /* we're at an...*/
       ||   (isfghorz && isfgvert && (bitval==bgbitval)) ) /*...inside corner */
    continue; /* don't antialias */
       } /* --- end-of-if(1) --- */
     /* --- check #gaps for checkerboard (added by j.forkosh) --- */
     if ( 0 ) { /* true to perform test */
       int ngaps=0, mingaps=1,maxgaps=2; /* count #fg/bg flips (max=4 noop) */
       if ( nwbitval!=nnbitval ) ngaps++; /* upper-left =? upper */
       if ( nnbitval!=nebitval ) ngaps++; /* upper =? upper-right */
       if ( nebitval!=eebitval ) ngaps++; /* upper-right =? right */
       if ( eebitval!=sebitval ) ngaps++; /* right =? lower-right */
       if ( sebitval!=ssbitval ) ngaps++; /* lower-right =? lower */
       if ( ssbitval!=swbitval ) ngaps++; /* lower =? lower-left */
       if ( swbitval!=wwbitval ) ngaps++; /* lower-left =? left */
       if ( wwbitval!=nwbitval ) ngaps++; /* left =? upper-left */
       if ( ngaps > 0 ) ngaps /= 2; /* each gap has 2 bg/fg flips */
       if ( ngaps<mingaps || ngaps>maxgaps ) continue;
       } /* --- end-of-if(1) --- */
   /* --- antialias if necessary --- */    /* --- antialias if necessary --- */
   if ( (isbgalias && isbgedge) /* alias pixel surrounding bg */    if ( (isbgalias && isbgedge) /* alias pixel surrounding bg */
   ||   (isfgalias && isfgedge) /* alias pixel surrounding fg */    ||   (isfgalias && isfgedge) /* alias pixel surrounding fg */
Line 9291  for ( irow=0; irow<height; irow++ ) Line 13602  for ( irow=0; irow<height; irow++ )
     double aawtval = ((double)aasumval)/((double)totwts); /* weighted val */      double aawtval = ((double)aasumval)/((double)totwts); /* weighted val */
     aabyteval= (int)(((double)(grayscale-1))*aawtval+0.5); /*0...grayscale-1*/      aabyteval= (int)(((double)(grayscale-1))*aawtval+0.5); /*0...grayscale-1*/
     bytemap[imap] = (intbyte)(aabyteval); /* set antialiased pixel */      bytemap[imap] = (intbyte)(aabyteval); /* set antialiased pixel */
     if ( msglevel>=99 && msgfp!=NULL ) fprintf(msgfp, /* debugging */      if ( msglevel>=99 && msgfp!=NULL ) { fprintf(msgfp, /*diagnostic output*/
       "aapnm> irow,icol,imap=%d,%d,%d aawtval=%.4f aabyteval=%d\n",        "%s> irow,icol,imap=%d,%d,%d aawtval=%.4f aabyteval=%d\n",
         (isfirstaa?"aapnm algorithm":"aapnm"),
       irow,icol,imap, aawtval,aabyteval);        irow,icol,imap, aawtval,aabyteval);
         isfirstaa = 0; }
     } /* --- end-of-if(isedge) --- */      } /* --- end-of-if(isedge) --- */
   } /* --- end-of-for(irow,icol) --- */    } /* --- end-of-for(irow,icol) --- */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
Line 9305  Back to caller with gray-scale anti-alia Line 13618  Back to caller with gray-scale anti-alia
   
   
 /* ==========================================================================  /* ==========================================================================
    * Function: aapnmlookup ( rp, bytemap, grayscale )
    * Purpose: calculates a lowpass anti-aliased bytemap
    * for rp->bitmap, with each byte 0...grayscale-1,
    * based on the pnmalias.c algorithm.
    * This version uses aagridnum() and aapatternnum() lookups
    * to interpret 3x3 lowpass pixel grids.
    * --------------------------------------------------------------------------
    * Arguments: rp (I) raster *  to raster whose bitmap
    * is to be anti-aliased
    * bytemap (O) intbyte * to bytemap, calculated
    * by applying pnm-based filter to rp->bitmap,
    * and returned (as you'd expect) in 1-to-1
    * addressing correspondence with rp->bitmap
    * grayscale (I) int containing number of grayscales
    * to be calculated, 0...grayscale-1
    * (should typically be given as 256)
    * --------------------------------------------------------------------------
    * Returns: ( int ) 1=success, 0=any error
    * --------------------------------------------------------------------------
    * Notes:    o Based on the pnmalias.c algorithm in the netpbm package
    * on sourceforge.
    *     o This version uses aagridnum() and aapatternnum() lookups
    * to interpret 3x3 lowpass pixel grids.
    * ======================================================================= */
   /* --- entry point --- */
   int aapnmlookup (raster *rp, intbyte *bytemap, int grayscale)
   {
   /* -------------------------------------------------------------------------
   Allocations and Declarations
   -------------------------------------------------------------------------- */
   int width=rp->width, height=rp->height, /* width, height of raster */
    icol = 0,        irow = 0, /* width, height indexes */
    imap = (-1); /* pixel index = icol + irow*width */
   int bgbitval=0, fgbitval=1; /* background, foreground bitval */
   int isfirstaa = 1; /*debugging switch signals 1st pixel*/
   int aacenterwt=centerwt, aaadjacentwt=adjacentwt, aacornerwt=cornerwt,
    totwts = centerwt + 4*(adjacentwt+cornerwt); /*pnmalias default wts*/
   int isfgalias  = fgalias, /*(1) true to antialias fg bits */
    isfgonly   = fgonly, /*(0) true to only antialias fg bits*/
    isbgalias  = bgalias, /*(0) true to antialias bg bits */
    isbgonly   = bgonly; /*(0) true to only antialias bg bits*/
   int gridnum=(-1), aagridnum(), /* grid# for 3x3 grid at irow,icol */
    patternum=(-1), aapatternnum(); /*pattern#, 1-51, for input gridnum*/
   int aapatterns(); /* to antialias special patterns */
   /* ---
    * pattern number data
    * ------------------- */
   /* --- number of adjacent fg pixels set in pattern --- */
   static int nadjacents[] = { -1, /* #adjacent fg pixels for pattern */
      0,  4,  0,  1,  4,  3,  1,  0,  1,  0, /*  1-10 */
      2,  2,  3,  4,  3,  4,  2,  2,  1,  2, /* 11-20 */
      1,  2,  1,  2,  0,  1,  3,  2,  3,  2, /* 21-30 */
      3,  2,  3,  2,  4,  3,  1,  2,  2,  2, /* 31-40 */
      2,  1,  2,  2,  3,  0,  3,  2,  2,  1,  4, /* 41-51 */
      -1 } ; /* --- end-of-nadjacents[] --- */
   /* --- number of corner fg pixels set in pattern --- */
   static int ncorners[] = { -1, /* #corner fg pixels for pattern */
      0,  4,  1,  0,  3,  4,  1,  2,  1,  2, /*  1-10 */
      0,  0,  3,  2,  3,  2,  4,  4,  2,  1, /* 11-20 */
      2,  1,  2,  1,  3,  2,  0,  1,  2,  3, /* 21-30 */
      2,  3,  2,  3,  1,  2,  4,  3,  2,  2, /* 31-40 */
      2,  3,  2,  2,  1,  4,  1,  2,  2,  3,  0, /* 41-51 */
      -1 } ; /* --- end-of-ncorners[] --- */
   /* --- 0,1,2=pattern contains horizontal bg,fg,both edge; -1=no edge --- */
   static int horzedges[] = { -1, /* 0,1,2 = horz bg,fg,both edge */
      0,  1,  0,  0,  1,  1,  0,  0,  0, -1, /*  1-10 */
      0, -1,  1,  1,  1, -1,  1, -1,  2,  0, /* 11-20 */
     -1, -1, -1,  0, -1, -1, -1, -1,  2,  1, /* 21-30 */
     -1, -1, -1,  1, -1, -1, -1, -1,  2, -1, /* 31-40 */
     -1,  1,  1, -1, -1, -1,  0,  0, -1, -1, -1, /* 41-51 */
      -1 } ; /* --- end-of-horzedges[] --- */
   /* --- 0,1,2=pattern contains vertical bg,fg,both edge; -1=no edge --- */
   static int vertedges[] = { -1, /* 0,1,2 = vert bg,fg,both edge */
      0,  1,  0,  0,  1,  1,  0, -1, -1, -1, /*  1-10 */
      0,  0,  1, -1, -1, -1,  1,  1, -1, -1, /* 11-20 */
     -1,  0,  0,  0, -1, -1,  0, -1, -1, -1, /* 21-30 */
     -1,  1,  1,  1, -1, -1,  1, -1, -1, -1, /* 31-40 */
     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 41-51 */
      -1 } ; /* --- end-of-vertedges[] --- */
   /* --- 0,1,2=pattern contains diagonal bg,fg,both edge; -1=no edge --- */
   static int diagedges[] = { -1, /* 0,1,2 = diag bg,fg,both edge */
      0,  1,  0,  0,  1,  1,  0,  0,  0,  0, /*  1-10 */
      2, -1,  1,  1,  1,  1,  1, -1,  0,  2, /* 11-20 */
      0, -1,  0,  2,  0, -1,  1,  1,  1,  1, /* 21-30 */
      1, -1,  1,  2,  1,  1, -1,  1,  2, -1, /* 31-40 */
      1,  0, -1,  2,  1,  0,  1, -1,  1, -1,  1, /* 41-51 */
      -1 } ; /* --- end-of-diagedges[] --- */
   /* -------------------------------------------------------------------------
   Calculate bytemap as 9-point weighted average over bitmap
   -------------------------------------------------------------------------- */
   for ( irow=0; irow<height; irow++ )
    for ( icol=0; icol<width; icol++ )
     {
     /* --- local allocations and declarations --- */
     int bitval=0, /* value of rp bit at irow,icol */
    isbgdiag=0, isfgdiag=0, /*does pixel border a bg or fg edge*/
    aabyteval=0; /* antialiased (or unchanged) value*/
     /* --- get gridnum and center bit value, init aabyteval --- */
     imap++; /* first set imap=icol + irow*width*/
     gridnum = aagridnum(rp,irow,icol); /*grid# coding 3x3 grid at irow,icol*/
     bitval = (gridnum&1); /* center bit set if gridnum odd */
     aabyteval = (intbyte)(bitval==bgbitval?0:grayscale-1); /* default aa val */
     bytemap[imap] = (intbyte)(aabyteval); /* init antialiased pixel */
     if ( gridnum<0 || gridnum>511 ) continue; /* gridnum out of bounds*/
     /* --- check if we're antialiasing this pixel --- */
     if ( (isbgonly && bitval==fgbitval) /* only antialias background bit */
     ||   (isfgonly && bitval==bgbitval) ) /* only antialias foreground bit */
       continue; /* leave default and do next bit */
     /* --- look up pattern number, 1-51, corresponding to input gridnum --- */
     patternum = aapatternnum(gridnum); /* look up pattern number */
     if ( patternum<1 || patternum>51 ) continue; /* some internal error */
     /* --- special pattern number processing --- */
     if ( (aabyteval = aapatterns(rp,irow,icol,gridnum,patternum,grayscale))
     >=   0 ) { /* special processing for pattern */
       bytemap[imap] = (intbyte)(aabyteval); /* set antialiased pixel */
       continue; } /* and continue with next pixel */
     /* --- check for diagonal edges --- */
     isbgdiag = ( diagedges[patternum]==2 || /*current pixel borders a bg edge*/
                  diagedges[patternum]==0 );
     isfgdiag = ( diagedges[patternum]==2 || /*current pixel borders a fg edge*/
                  diagedges[patternum]==1 );
     /* ---check top/bot left/right edges for corners (added by j.forkosh)--- */
     if ( 1 ) { /* true to perform test */
       int isbghorz=0, isfghorz=0, isbgvert=0, isfgvert=0, /* horz/vert edges */
    horzedge=horzedges[patternum], vertedge=vertedges[patternum];
       isbghorz = (horzedge==2||horzedge==0); /* top or bottom edge is all bg */
       isfghorz = (horzedge==2||horzedge==1); /* top or bottom edge is all fg */
       isbgvert = (vertedge==2||vertedge==0); /* left or right edge is all bg */
       isfgvert = (vertedge==2||vertedge==1); /* left or right edge is all fg */
       if ( (isbghorz && isbgvert && (bitval==fgbitval)) /* we're at an...*/
       ||   (isfghorz && isfgvert && (bitval==bgbitval)) ) /*...inside corner */
    continue; /* don't antialias */
       } /* --- end-of-if(1) --- */
   #if 0
     /* --- check #gaps for checkerboard (added by j.forkosh) --- */
     if ( 0 ) { /* true to perform test */
       int ngaps=0, mingaps=1,maxgaps=2; /* count #fg/bg flips (max=4 noop) */
       if ( nwbitval!=nnbitval ) ngaps++; /* upper-left =? upper */
       if ( nnbitval!=nebitval ) ngaps++; /* upper =? upper-right */
       if ( nebitval!=eebitval ) ngaps++; /* upper-right =? right */
       if ( eebitval!=sebitval ) ngaps++; /* right =? lower-right */
       if ( sebitval!=ssbitval ) ngaps++; /* lower-right =? lower */
       if ( ssbitval!=swbitval ) ngaps++; /* lower =? lower-left */
       if ( swbitval!=wwbitval ) ngaps++; /* lower-left =? left */
       if ( wwbitval!=nwbitval ) ngaps++; /* left =? upper-left */
       if ( ngaps > 0 ) ngaps /= 2; /* each gap has 2 bg/fg flips */
       if ( ngaps<mingaps || ngaps>maxgaps ) continue;
       } /* --- end-of-if(1) --- */
   #endif
     /* --- antialias if necessary --- */
     if ( (isbgalias && isbgdiag) /* alias pixel surrounding bg */
     ||   (isfgalias && isfgdiag) /* alias pixel surrounding fg */
     ||   (isbgdiag  && isfgdiag) ) /* neighboring fg and bg pixel */
       {
       int aasumval = /* sum wts[]*bitmap[] */
    aacenterwt*bitval + /* apply centerwt to center pixel */
    aaadjacentwt*nadjacents[patternum] + /* similarly for adjacents */
    aacornerwt*ncorners[patternum]; /* and corners */
       double aawtval = ((double)aasumval)/((double)totwts); /* weighted val */
       aabyteval= (int)(((double)(grayscale-1))*aawtval+0.5); /*0...grayscale-1*/
       bytemap[imap] = (intbyte)(aabyteval); /* set antialiased pixel */
       if ( msglevel>=99 && msgfp!=NULL ) { fprintf(msgfp, /*diagnostic output*/
         "%s> irow,icol,imap=%d,%d,%d aawtval=%.4f aabyteval=%d",
         (isfirstaa?"aapnmlookup algorithm":"aapnm"),
         irow,icol,imap, aawtval,aabyteval);
         if ( msglevel < 100 ) fprintf(msgfp,"\n"); /* no more output */
         else fprintf(msgfp,", grid#,pattern#=%d,%d\n",gridnum,patternum);
         isfirstaa = 0; }
       } /* --- end-of-if(isedge) --- */
     } /* --- end-of-for(irow,icol) --- */
   /* -------------------------------------------------------------------------
   Back to caller with gray-scale anti-aliased bytemap
   -------------------------------------------------------------------------- */
   /*end_of_job:*/
     return ( 1 );
   } /* --- end-of-function aapnmlookup() --- */
   
   
   /* ==========================================================================
    * Function: aapatterns ( rp, irow, icol, gridnum, patternum, grayscale )
    * Purpose: For patterns requireing special processing,
    * calculates anti-aliased value for pixel at irow,icol,
    * whose surrounding 3x3 pixel grid is coded by gridnum
    * (which must correspond to a pattern requiring special
    * processing).
    * --------------------------------------------------------------------------
    * Arguments: rp (I) raster *  to raster whose bitmap
    * is to be anti-aliased
    * irow (I) int containing row, 0...height-1,
    * of pixel to be antialiased
    * icol (I) int containing col, 0...width-1,
    * of pixel to be antialiased
    * gridnum (I) int containing 0...511 corresponding to
    * 3x3 pixel grid surrounding irow,icol
    * patternum (I) int containing 1...51 pattern# of
    * the 3x3 grid surrounding irow,icol
    * grayscale (I) int containing number of grayscales
    * to be calculated, 0...grayscale-1
    * (should typically be given as 256)
    * --------------------------------------------------------------------------
    * Returns: ( int ) 0...grayscale-1 for success,
    * -1 = error, or no special processing required
    * --------------------------------------------------------------------------
    * Notes:    o
    * ======================================================================= */
   /* --- entry point --- */
   int aapatterns (raster *rp, int irow, int icol,
    int gridnum, int patternum, int grayscale)
   {
   /* -------------------------------------------------------------------------
   Allocations and Declarations
   -------------------------------------------------------------------------- */
   int aaval = (-1); /* antialiased value returned */
   int iscenter = (gridnum&1); /* true if center pixel set/black */
   int aapatternnum(), /* if patternum not supplied */
    aapattern1124(), /* routine for patterns #11,24 */
    aapattern19(), /* special routine for pattern #19 */
    aapattern20(), /* special routine for pattern #20 */
    aapattern39(); /* special routine for pattern #39 */
   /* -------------------------------------------------------------------------
   special pattern number processing
   -------------------------------------------------------------------------- */
   if ( 1 ) {
     if ( patternum < 1 ) /* pattern# not supplied by caller */
       patternum = aapatternnum(gridnum); /* so look it up ourselves */
     switch ( patternum ) {
       default: break; /* no special processing */
       case 11:
       case 24: aaval = aapattern1124(rp,irow,icol,gridnum,grayscale); break;
       case 19: aaval = aapattern19(rp,irow,icol,gridnum,grayscale); break;
       case 20: aaval = aapattern20(rp,irow,icol,gridnum,grayscale); break;
       case 39: aaval = aapattern39(rp,irow,icol,gridnum,grayscale); break;
       /* case 24: if ( (gridnum&1) == 0 ) aaval=0; break; */
       case 29: aaval = (iscenter?grayscale-1:0); break; /* no antialiasing */
       } /* --- end-of-switch(patternum) --- */
     } /* --- end-of-if() --- */
   return ( aaval ); /* return antialiased val to caller*/
   } /* --- end-of-function aapatterns() --- */
   
   
   /* ==========================================================================
    * Function: aapattern1124 ( rp, irow, icol, gridnum, grayscale )
    * Purpose: calculates anti-aliased value for pixel at irow,icol,
    * whose surrounding 3x3 pixel grid is coded by gridnum
    * (which must correspond to pattern #11 or #24).
    * --------------------------------------------------------------------------
    * Arguments: rp (I) raster *  to raster whose bitmap
    * is to be anti-aliased
    * irow (I) int containing row, 0...height-1,
    * of pixel to be antialiased
    * icol (I) int containing col, 0...width-1,
    * of pixel to be antialiased
    * gridnum (I) int containing 0...511 corresponding to
    * 3x3 pixel grid surrounding irow,icol
    * grayscale (I) int containing number of grayscales
    * to be calculated, 0...grayscale-1
    * (should typically be given as 256)
    * --------------------------------------------------------------------------
    * Returns: ( int ) 0...grayscale-1 for success, -1=any error
    * --------------------------------------------------------------------------
    * Notes:    o Handles the eight gridnum's
    * (gridnum/2 shown to eliminate irrelevant low-order bit)
    *  ---        ---         -*-          -*-
    *  --* = 10   *-- = 18    --* = 72     *-- = 80  (pattern$11)
    *  -*-        -*-         ---          ---
    *
    *  ---        ---         -**          **-
    *  --* = 11   *-- = 22    --* = 104    *-- = 208 (pattern$24)
    *  -**        **-         ---          ---
    *     o For black * center pixel, using grid#10 as an example,
    * pixel stays ---      antialiased  ---*
    * black if    -***     if part of  -**
    * part of a   -*-      a diagonal  -*- 
    * corner, eg,  *       line, eg,  *
    * ======================================================================= */
   /* --- entry point --- */
   int aapattern1124 (raster *rp, int irow, int icol,
    int gridnum, int grayscale)
   {
   /* -------------------------------------------------------------------------
   Allocations and Declarations
   -------------------------------------------------------------------------- */
   int aaval = (-1); /* antialiased value returned */
   int iscenter = gridnum&1; /* true if pixel at irow,icol black*/
   int patternum = 24; /* init for pattern#24 default */
   pixbyte *bitmap = rp->pixmap; /* local rp->pixmap ptr */
   int width=rp->width, height=rp->height; /* width, height of raster */
   int jrow=irow, jcol=icol; /* corner or diagonal row,col */
   int vertcornval=0, horzcornval=0, /* vertical, horizontal corner bits*/
    topdiagval=0,  botdiagval=0, /* upper,lower diagonal pixel bits */
    cornval=0, diagval=0; /* vert+horzcorn, top+botdiag */
   int hdirection=99, vdirection=99, /* horz,vert corner direction */
    hturn=99,vturn=99, aafollowline(); /* follow corner till turns */
   /* -------------------------------------------------------------------------
   Check corner and diagonal pixels
   -------------------------------------------------------------------------- */
   if ( 0 ) goto end_of_job; /* true to turn off pattern1124 */
   switch ( gridnum/2 ) { /* check pattern#11,24 corner, diag*/
     default: goto end_of_job; /* not a pattern#11,24 gridnum */
     case 10: patternum=11; case 11:
       hdirection = 2;  vdirection = -1; /* directions to follow corner */
       if ( (jrow=irow+2) < height ) { /* vert corner below center pixel */
         vertcornval = getlongbit(bitmap,(icol+jrow*width));
         if ( (icol-1) >= 0 ) /* lower diag left of center */
           botdiagval = getlongbit(bitmap,((icol-1)+jrow*width)); }
       if ( (jcol=icol+2) < width ) { /* horz corner right of center */
         horzcornval = getlongbit(bitmap,(jcol+irow*width));
         if ( (irow-1) >= 0 ) /* upper diag above center */
           topdiagval = getlongbit(bitmap,(jcol+(irow-1)*width)); }
       break;
     case 18: patternum=11; case 22:
       hdirection = -2;  vdirection = -1; /* directions to follow corner */
       if ( (jrow=irow+2) < height ) { /* vert corner below center pixel */
         vertcornval = getlongbit(bitmap,(icol+jrow*width));
         if ( (icol+1) < width ) /* lower diag right of center */
           botdiagval = getlongbit(bitmap,((icol+1)+jrow*width)); }
       if ( (jcol=icol-2) >= 0 ) { /* horz corner left of center */
         horzcornval = getlongbit(bitmap,(jcol+irow*width));
         if ( (irow-1) >= 0 ) /* upper diag above center */
           topdiagval = getlongbit(bitmap,(jcol+(irow-1)*width)); }
       break;
     case 72: patternum=11; case 104:
       hdirection = 2;  vdirection = 1; /* directions to follow corner */
       if ( (jrow=irow-2) >= 0 ) { /* vert corner above center pixel */
         vertcornval = getlongbit(bitmap,(icol+jrow*width));
         if ( (icol-1) >= 0 ) /* upper diag left of center */
           topdiagval = getlongbit(bitmap,((icol-1)+jrow*width)); }
       if ( (jcol=icol+2) < width ) { /* horz corner right of center */
         horzcornval = getlongbit(bitmap,(jcol+irow*width));
         if ( (irow+1) < height ) /* lower diag below center */
           botdiagval = getlongbit(bitmap,(jcol+(irow+1)*width)); }
       break;
     case 80: patternum=11; case 208:
       hdirection = -2;  vdirection = 1; /* directions to follow corner */
       if ( (jrow=irow-2) >= 0 ) { /* vert corner above center pixel */
         vertcornval = getlongbit(bitmap,(icol+jrow*width));
         if ( (icol+1) < width ) /* upper diag right of center */
           topdiagval = getlongbit(bitmap,((icol+1)+jrow*width)); }
       if ( (jcol=icol-2) >= 0 ) { /* horz corner left of center */
         horzcornval = getlongbit(bitmap,(jcol+irow*width));
         if ( (irow+1) < height ) /* lower diag below center */
           botdiagval = getlongbit(bitmap,(jcol+(irow+1)*width)); }
       break;
     } /* --- end-of-switch(gridnum/2) --- */
   cornval = vertcornval+horzcornval; /* 0=no corner bits, 1, 2=both */
   diagval = topdiagval+botdiagval; /* 0=no diag bits, 1, 2=both */
   /* -------------------------------------------------------------------------
   Handle white center
   -------------------------------------------------------------------------- */
   if ( 1 && !iscenter ) { aaval = (patternum==11?51:64);  goto end_of_job; }
   /* -------------------------------------------------------------------------
   Handle black center
   -------------------------------------------------------------------------- */
   if ( diagval > 1 ) aaval = ( patternum==24? 255:191 );
   else {
     hturn = aafollowline(rp,irow,icol,hdirection);
     vturn = aafollowline(rp,irow,icol,vdirection);
     if ( vturn*hdirection < 0  && hturn*vdirection < 0 )
          aaval = ( patternum==24? 255:191 );
     else aaval = grayscale-1; } /* actual corner */
   /* -------------------------------------------------------------------------
   Back to caller with grayscale antialiased value for pixel at irow,icol
   -------------------------------------------------------------------------- */
   end_of_job:
     if ( aaval >= 0 ) /* have antialiasing result */
      if ( msglevel>=99 && msgfp!=NULL ) fprintf(msgfp, /* diagnostic output */
       "aapattern1124> irow,icol,grid#/2=%d,%d,%d, top,botdiag=%d,%d, "
       "vert,horzcorn=%d,%d, v,hdir=%d,%d, v,hturn=%d,%d, aaval=%d\n",
       irow,icol,gridnum/2, topdiagval,botdiagval, vertcornval,horzcornval,
       vdirection,hdirection, vturn,hturn, aaval);
     return ( aaval ); /* back with antialiased value */
   } /* --- end-of-function aapattern1124() --- */
   
   
   /* ==========================================================================
    * Function: aapattern19 ( rp, irow, icol, gridnum, grayscale )
    * Purpose: calculates anti-aliased value for pixel at irow,icol,
    * whose surrounding 3x3 pixel grid is coded by gridnum
    * (which must correspond to pattern #19).
    * --------------------------------------------------------------------------
    * Arguments: rp (I) raster *  to raster whose bitmap
    * is to be anti-aliased
    * irow (I) int containing row, 0...height-1,
    * of pixel to be antialiased
    * icol (I) int containing col, 0...width-1,
    * of pixel to be antialiased
    * gridnum (I) int containing 0...511 corresponding to
    * 3x3 pixel grid surrounding irow,icol
    * grayscale (I) int containing number of grayscales
    * to be calculated, 0...grayscale-1
    * (should typically be given as 256)
    * --------------------------------------------------------------------------
    * Returns: ( int ) 0...grayscale-1 for success, -1=any error
    * --------------------------------------------------------------------------
    * Notes:    o Handles the four gridnum's
    * (gridnum/2 shown to eliminate irrelevant low-order bit)
    *  ---        --*         *--          ***
    *  --- = 7    --* = 41    *-- = 148    --- = 224
    *  ***        --*         *--          ---
    * ======================================================================= */
   /* --- entry point --- */
   int aapattern19 (raster *rp, int irow, int icol,
    int gridnum, int grayscale)
   {
   /* -------------------------------------------------------------------------
   Allocations and Declarations
   -------------------------------------------------------------------------- */
   int aaval = (-1); /* antialiased value returned */
   int iscenter = gridnum&1; /* true if pixel at irow,icol black*/
   int orientation = 1, /* 1=vertical, 2=horizontal */
    jrow=irow, jcol=icol; /* middle pixel of *** line */
   int turn1=0,turn2=0, aafollowline(); /* follow *** line till it turns */
   /* -------------------------------------------------------------------------
   Initialization and calculation of antialiased value
   -------------------------------------------------------------------------- */
   /* --- check input -- */
   if ( iscenter ) goto end_of_job; /* we only antialias white pixels */
   /* --- set params --- */
   switch ( gridnum/2 ) { /* check pattern#19 orientation */
     default: goto end_of_job; /* not a pattern#19 gridnum */
     case 7:   orientation=2; jrow++; break;
     case 41:  orientation=1; jcol++; break;
     case 148: orientation=1; jcol--; break;
     case 224: orientation=2; jrow--; break;
     } /* --- end-of-switch(gridnum/2) --- */
   /* --- get turns in both directions --- */
   if ( (turn1 = aafollowline(rp,jrow,jcol,orientation)) == 0 ) goto end_of_job;
   if ( (turn2 = aafollowline(rp,jrow,jcol,-orientation)) == 0) goto end_of_job;
   if ( turn1*turn2 >= 0 ) goto end_of_job; /* both turns in same direction */
   /* --- weight pixel --- */
   aaval = grayscale / ( 3 + min2(abs(turn1),abs(turn2)) );
   /* -------------------------------------------------------------------------
   Back to caller with grayscale antialiased value for pixel at irow,icol
   -------------------------------------------------------------------------- */
   end_of_job:
     if ( aaval >= 0 ) /* have antialiasing result */
      if ( msglevel>=99 && msgfp!=NULL ) fprintf(msgfp, /* diagnostic output */
       "aapattern19> irow,icol,grid#/2=%d,%d,%d, turn+%d,%d=%d,%d, aaval=%d\n",
       irow,icol,gridnum/2, orientation,-orientation,turn1,turn2, aaval);
     return ( aaval ); /* back with antialiased value */
   } /* --- end-of-function aapattern19() --- */
   
   
   /* ==========================================================================
    * Function: aapattern20 ( rp, irow, icol, gridnum, grayscale )
    * Purpose: calculates anti-aliased value for pixel at irow,icol,
    * whose surrounding 3x3 pixel grid is coded by gridnum
    * (which must correspond to pattern #20).
    * --------------------------------------------------------------------------
    * Arguments: rp (I) raster *  to raster whose bitmap
    * is to be anti-aliased
    * irow (I) int containing row, 0...height-1,
    * of pixel to be antialiased
    * icol (I) int containing col, 0...width-1,
    * of pixel to be antialiased
    * gridnum (I) int containing 0...511 corresponding to
    * 3x3 pixel grid surrounding irow,icol
    * grayscale (I) int containing number of grayscales
    * to be calculated, 0...grayscale-1
    * (should typically be given as 256)
    * --------------------------------------------------------------------------
    * Returns: ( int ) 0...grayscale-1 for success, -1=any error
    * --------------------------------------------------------------------------
    * Notes:    o Handles the eight gridnum's
    * (gridnum/2 shown to eliminate irrelevant low-order bit)
    *  ---        ---         --*          -*-      
    *  --* = 14   *-- = 19    --* = 42     --* = 73
    *  **-        -**         -*-          --*     
    *
    *  -*-        -**         *--          **-      
    *  *-- = 84   *-- = 112   *-- = 146    --* = 200
    *  *--        ---         -*-          ---     
    * ======================================================================= */
   /* --- entry point --- */
   int aapattern20 (raster *rp, int irow, int icol,
    int gridnum, int grayscale)
   {
   /* -------------------------------------------------------------------------
   Allocations and Declarations
   -------------------------------------------------------------------------- */
   int aaval = (-1); /* antialiased value returned */
   int iscenter = gridnum&1; /* true if pixel at irow,icol black*/
   int direction = 1, /* direction to follow ** line */
    jrow1=irow, jcol1=icol, /* coords of * */
    jrow2=irow, jcol2=icol; /* coords of adjacent ** pixel */
   int turn1=0,turn2=0, aafollowline(); /* follow *,** lines till turns */
   /* -------------------------------------------------------------------------
   Initialization and calculation of antialiased value
   -------------------------------------------------------------------------- */
   /* --- check input -- */
   if ( 1 ) goto end_of_job; /* don't want this one */
   if ( iscenter ) goto end_of_job; /* we only antialias white pixels */
   /* --- set params --- */
   switch ( gridnum/2 ) { /* check pattern#20 orientation */
     default: goto end_of_job; /* not a pattern#20 gridnum */
     case 14:  direction=-2; jcol1++; jrow2++; break;
     case 19:  direction=2;  jcol1--; jrow2++; break;
     case 42:  direction=1;  jrow1++; jcol2++; break;
     case 73:  direction=-1; jrow1--; jcol2++; break;
     case 84:  direction=-1; jrow1--; jcol2--; break;
     case 112: direction=2;  jcol1--; jrow2--; break;
     case 146: direction=1;  jrow1++; jcol2--; break;
     case 200: direction=-2; jcol1++; jrow2--; break;
     } /* --- end-of-switch(gridnum/2) --- */
   /* --- get turns in both directions --- */
   if ( (turn1=aafollowline(rp,jrow1,jcol1,-direction)) == 0 ) goto end_of_job;
   if ( (turn2=aafollowline(rp,jrow2,jcol2,direction))  == 0 ) goto end_of_job;
   if ( turn1*turn2 >= 0 ) goto end_of_job; /* both turns in same direction */
   /* --- weight pixel --- */
   aaval = grayscale / ( 3 + min2(abs(turn1),abs(turn2)) );
   /* -------------------------------------------------------------------------
   Back to caller with grayscale antialiased value for pixel at irow,icol
   -------------------------------------------------------------------------- */
   end_of_job:
     if ( aaval >= 0 ) /* have antialiasing result */
      if ( msglevel>=99 && msgfp!=NULL ) fprintf(msgfp, /* diagnostic output */
       "aapattern20> irow,icol,grid#/2=%d,%d,%d, turn%d,%d=%d,%d, aaval=%d\n",
       irow,icol,gridnum/2, -direction,direction,turn1,turn2, aaval);
     return ( aaval ); /* back with antialiased value */
   } /* --- end-of-function aapattern20() --- */
   
   
   /* ==========================================================================
    * Function: aapattern39 ( rp, irow, icol, gridnum, grayscale )
    * Purpose: calculates anti-aliased value for pixel at irow,icol,
    * whose surrounding 3x3 pixel grid is coded by gridnum
    * (which must correspond to pattern #39).
    * --------------------------------------------------------------------------
    * Arguments: rp (I) raster *  to raster whose bitmap
    * is to be anti-aliased
    * irow (I) int containing row, 0...height-1,
    * of pixel to be antialiased
    * icol (I) int containing col, 0...width-1,
    * of pixel to be antialiased
    * gridnum (I) int containing 0...511 corresponding to
    * 3x3 pixel grid surrounding irow,icol
    * grayscale (I) int containing number of grayscales
    * to be calculated, 0...grayscale-1
    * (should typically be given as 256)
    * --------------------------------------------------------------------------
    * Returns: ( int ) 0...grayscale-1 for success, -1=any error
    * --------------------------------------------------------------------------
    * Notes:    o Handles the eight gridnum's
    * (gridnum/2 shown to eliminate irrelevant low-order bit)
    *  ---        ---         --*          -**      
    *  --* = 15   *-- = 23    --* = 43     --* = 105
    *  ***        ***         -**          --*     
    *
    *  **-        ***         *--          ***      
    *  *-- = 212  *-- = 240   *-- = 150    --* = 232
    *  *--        ---         **-          ---     
    * ======================================================================= */
   /* --- entry point --- */
   int aapattern39 (raster *rp, int irow, int icol,
    int gridnum, int grayscale)
   {
   /* -------------------------------------------------------------------------
   Allocations and Declarations
   -------------------------------------------------------------------------- */
   int aaval = (-1); /* antialiased value returned */
   int iscenter = gridnum&1; /* true if pixel at irow,icol black*/
   int direction = 1, /* direction to follow ** line */
    jrow1=irow, jcol1=icol, /* coords of * */
    jrow2=irow, jcol2=icol; /* coords of adjacent ** pixel */
   int turn1=0,turn2=0, aafollowline(); /* follow *,** lines till turns */
   /* -------------------------------------------------------------------------
   Initialization and calculation of antialiased value
   -------------------------------------------------------------------------- */
   /* --- check input -- */
   if ( iscenter ) goto end_of_job; /* we only antialias white pixels */
   /* --- set params --- */
   switch ( gridnum/2 ) { /* check pattern#39 orientation */
     default: goto end_of_job; /* not a pattern#39 gridnum */
     case 15:  direction=-2; jcol1++; jrow2++; break;
     case 23:  direction=2;  jcol1--; jrow2++; break;
     case 43:  direction=1;  jrow1++; jcol2++; break;
     case 105: direction=-1; jrow1--; jcol2++; break;
     case 212: direction=-1; jrow1--; jcol2--; break;
     case 240: direction=2;  jcol1--; jrow2--; break;
     case 150: direction=1;  jrow1++; jcol2--; break;
     case 232: direction=-2; jcol1++; jrow2--; break;
     } /* --- end-of-switch(gridnum/2) --- */
   /* --- get turns directions (tunr1==1 signals inside corner) --- */
   if ( (turn1=aafollowline(rp,jrow1,jcol1,-direction)) == 1 )
     { aaval=0; goto end_of_job; }
   if ( 1 ) goto end_of_job;  /* stop here for now */
   if ( (turn2=aafollowline(rp,jrow2,jcol2,direction))  == 0 ) goto end_of_job;
   if ( turn1*turn2 >= 0 ) goto end_of_job; /* both turns in same direction */
   /* --- weight pixel --- */
   aaval = grayscale / ( 3 + min2(abs(turn1),abs(turn2)) );
   /* -------------------------------------------------------------------------
   Back to caller with grayscale antialiased value for pixel at irow,icol
   -------------------------------------------------------------------------- */
   end_of_job:
     if ( aaval >= 0 ) /* have antialiasing result */
      if ( msglevel>=99 && msgfp!=NULL ) fprintf(msgfp, /* diagnostic output */
       "aapattern39> irow,icol,grid#/2=%d,%d,%d, turn%d,%d=%d,%d, aaval=%d\n",
       irow,icol,gridnum/2, -direction,direction,turn1,turn2, aaval);
     return ( aaval ); /* back with antialiased value */
   } /* --- end-of-function aapattern39() --- */
   
   
   /* ==========================================================================
    * Function: aafollowline ( rp, irow, icol, direction )
    * Purpose: starting with pixel at irow,icol, moves in
    * specified direction looking for a "turn"
    * --------------------------------------------------------------------------
    * Arguments: rp (I) raster *  to raster containing pixel image
    * irow (I) int containing row, 0...height-1,
    * of first pixel
    * icol (I) int containing col, 0...width-1,
    * of first pixel
    * direction (I) int containing +1 to follow line up/north
    * (decreasing irow), -1 to follow line
    * down/south (increasing irow), +2 to follow
    * line right/east (increasing icol),
    * -2 to follow line left/west (decreasing icol)
    * --------------------------------------------------------------------------
    * Returns: ( int ) #rows or #cols traversed prior to turn,
    * or 0 if no turn detected (or for any error).
    * Sign is + if turn direction is right/east or
    * up/north, or is - if left/west or down/south.
    * --------------------------------------------------------------------------
    * Notes:     o Here are some examples illustrating turn detection in
    * +2 (right/east) direction.  Turns in other directions
    * are detected similarly/symmetrically.  * denotes black
    * bits (usually fg), - denotes white bits (usually bg),
    * and ? denotes "don't care" bit (won't affect outcome).
    * Arrow --> points to start pixel denoted by irow,icol.
    *
    *   *???         -??? turn=0 (no turn) is returned
    * -->*???   or -->-??? because the start pixel isn't
    *   *???         -??? on an edge to begin with
    *
    *   ----         **-- turn=0 returned because the
    * -->***-   or -->***- line ends abruptly without
    *   ----        ---- turning (even the second case)
    *
    *   ---*         ---* turn=0 returned because the
    * -->***-   or -->**** line forms a Y or T rather
    *   ---*        ---* than turning
    *
    *   ***-        **** turn=+3 returned
    * -->***-   or -->***- (outside corner)
    *   ----        ----
    *
    *   *****        ****- turn=-4 returned
    * -->*****  or -->****- (inside corner)
    *   ----*        ----*
    *
    *   ----*        ----* turn=+4 returned
    * -->****-  or -->***** (outside or inside corner)
    *   -----        -----
    * ======================================================================= */
   /* --- entry point --- */
   int aafollowline (raster *rp, int irow, int icol, int direction)
   {
   /* -------------------------------------------------------------------------
   Allocations and Declarations
   -------------------------------------------------------------------------- */
   pixbyte *bitmap = rp->pixmap; /* local rp->pixmap ptr */
   int width=rp->width, height=rp->height; /* width, height of raster */
   int drow=0, dcol=0, /* delta row,col to follow line */
    jrow=irow, jcol=icol; /* current row,col following line */
   int bitval=1, /* value of rp bit at irow,icol */
    fgval=1, bgval=0, /* "fg" is whatever bitval is */
    bitminus=0, bitplus=0; /* value of left/down, right/up bit*/
   int isline=1, isedge=0; /*isline signals one-pixel wide line*/
   int turn = 0, /* detected turn back to caller */
    maxturn = maxfollow; /* don't follow more than max pixels*/
   /* -------------------------------------------------------------------------
   Initialization
   -------------------------------------------------------------------------- */
   /* --- check input --- */
   if ( irow<0 || irow>=height /* irow 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 --- */
   bitval = getlongbit(bitmap,(icol+irow*width)); /* starting pixel (bg or fg)*/
   fgval = bitval;  bgval = (1-bitval); /* define "fg" as whatever bitval is*/
   /* --- set drow,dcol corresponding to desired direction --- */
   switch ( direction ) { /* determine drow,dcol for direction*/
     default: goto end_of_job; /* unrecognized direction arg */
     case  1: drow = (-1); break; /* follow line up/north */
     case -1: drow =   1;  break; /* down/south */
     case  2: dcol =   1;  break; /* right/east */
     case -2: dcol = (-1); break; } /* left/west */
   /* --- set bitminus and bitplus --- */
   if ( drow == 0 ) { /* we're following line right/left */
     if ( irow < height ) /* there's a pixel below current */
       bitminus = getlongbit(bitmap,(icol+(irow+1)*width)); /* get it */
     if ( irow > 0 ) /* there's a pixel above current */
       bitplus = getlongbit(bitmap,(icol+(irow-1)*width)); } /* get it */
   if ( dcol == 0 ) { /* we're following line up/down */
     if ( icol < width ) /* there's a pixel to the right */
       bitplus = getlongbit(bitmap,(icol+1+irow*width)); /* get it */
     if ( icol > 0 ) /* there's a pixel to the left */
       bitminus = getlongbit(bitmap,(icol-1+irow*width)); } /* get it */
   /* --- check for lack of line to follow --- */
   if ( bitval == bitplus /* starting pixel same as above */
   &&   bitval == bitminus ) /* and below (or right and left) */
     goto end_of_job; /* so there's no line to follow */
   /* --- set isline and isedge (already initted for isline) --- */
   if ( bitval == bitplus ) /* starting pixel same as above */
     { isedge = (-1);  isline = 0; } /* so we're at an edge below */
   if (  bitval == bitminus ) /* starting pixel same as below */
     { isedge = 1;  isline = 0; } /* so we're at an edge above */
   /* -------------------------------------------------------------------------
   follow line
   -------------------------------------------------------------------------- */
   while ( 1 ) { /* until turn found (or max) */
     /* --- local allocations and declarations --- */
     int dbitval=0, /* value of bit at jrow,jcol */
    dbitminus=0, dbitplus=0; /* value of left/down, right/up bit*/
     /* --- bump pixel count and indexes; check for max or end-of-raster --- */
     turn++; /* bump #pixels followed */
     jrow += drow;  jcol += dcol; /* indexes of next pixel to check */
     if ( turn > maxturn /* already followed max #pixels */
     ||   jrow<0 || jrow>=height /* or jrow past end-of-raster */
     ||   jcol<0 || jcol>=width ) /* or jcol past end-of-raster */
       { turn = 0;  goto end_of_job; } /* so quit without finding a turn */
     /* --- set current bit (dbitval) --- */
     dbitval = getlongbit(bitmap,(jcol+jrow*width)); /*value of jrow,jcol bit*/
     /* --- set dbitminus and dbitplus --- */
     if ( drow == 0 ) { /* we're following line right/left */
       if ( irow < height ) /* there's a pixel below current */
         dbitminus = getlongbit(bitmap,(jcol+(irow+1)*width)); /* get it */
       if ( irow > 0 ) /* there's a pixel above current */
         dbitplus = getlongbit(bitmap,(jcol+(irow-1)*width)); } /* get it */
     if ( dcol == 0 ) { /* we're following line up/down */
       if ( icol < width ) /* there's a pixel to the right */
         dbitplus = getlongbit(bitmap,(icol+1+jrow*width)); /* get it */
       if ( icol > 0 ) /* there's a pixel to the left */
         dbitminus = getlongbit(bitmap,(icol-1+jrow*width)); } /* get it */
     /* --- first check for abrupt end-of-line, or for T or Y --- */
     if ( isline != 0 ) /* abrupt end or T,Y must be a line*/
       if ( ( bgval == dbitval /* end-of-line if pixel flips to bg*/
              && bgval == dbitplus /* and bg same as above pixel */
              && bgval == dbitminus ) /* and below (or right and left) */
       ||   ( fgval == dbitplus /* T or Y if fg same as above pixel*/
              && fgval == dbitminus ) ) /* and below (or right and left) */
         { turn = 0;  goto end_of_job; } /* so we're at a T or Y */
     /* --- check for turning line --- */
     if ( isline != 0 ) { /* turning line must be a line */
       if ( fgval == dbitminus ) /* turning down */
         { turn = -turn;  goto end_of_job; } /* so return negative turn */
       else if ( fgval == dbitplus ) /* turning up */
         goto end_of_job; } /* so return positive turn */
     /* --- check for inside corner at edge --- */
     if ( isedge != 0 ) { /* inside corner must be a edge */
       if ( isedge < 0 && fgval == bitminus ) /* corner below */
         { turn = -turn;  goto end_of_job; } /* so return negative turn */
       if ( isedge > 0 && fgval == bitplus ) /* corner above */
         goto end_of_job; } /* so return positive turn */
     /* --- check for abrupt end at edge --- */
     if ( isedge != 0 /* abrupt edge end must be an edge */
     &&   fgval == dbitval ) /* and line must not end */
       if ( (isedge < 0 && bgval == bitplus) /* abrupt end above */
       ||   (isedge > 0 && bgval == bitminus) ) /* or abrupt end below */
         { turn = 0;  goto end_of_job; } /* so edge ended abruptly */
     /* --- check for outside corner at edge --- */
     if ( isedge != 0 /* outside corner must be a edge */
     &&   bgval == dbitval ) { /* and line must end */
       if ( isedge > 0 ) turn = -turn; /* outside turn down from edge above*/
       goto end_of_job; }
     } /* --- end-of-while(1) --- */
   /* -------------------------------------------------------------------------
   Back to caller with #rows or #cols traversed, and direction of detected turn
   -------------------------------------------------------------------------- */
   end_of_job:
     if ( msglevel>=99 && msgfp!=NULL ) /* debugging/diagnostic output */
       fprintf(msgfp,"aafollowline> irow,icol,direction=%d,%d,%d, turn=%d\n",
       irow,icol,direction,turn);
     return ( turn );
   } /* --- end-of-function aafollowline() --- */
   
   
   /* ==========================================================================
    * Function: aagridnum ( rp, irow, icol )
    * Purpose: calculates gridnum, 0-511 (see Notes below),
    * for 3x3 grid centered at irow,icol
    * --------------------------------------------------------------------------
    * Arguments: rp (I) raster *  to raster containing
    * bitmap image (to be anti-aliased)
    * irow (I) int containing row, 0...height-1,
    * at center of 3x3 grid
    * icol (I) int containing col, 0...width-1,
    * at center of 3x3 grid
    * --------------------------------------------------------------------------
    * Returns: ( int ) 0-511 grid number, or -1=any error
    * --------------------------------------------------------------------------
    * Notes:     o Input gridnum is a 9-bit int, 0-511, coding a 3x3 pixel grid
    * whose bit positions (and corresponding values) in gridnum are
    *  876     256 128  64
    *  504  =   32   1  16
    *  321       8   4   2
    * Thus, for example (*=pixel set/black, -=pixel not set/white),
    *  *--         *--  -**         (note that 209 is the
    *  -*- = 259   *-- = 302   -** = 209    inverse, set<-->unset,
    *  --*         ***         ---          of 302)
    *      o A set pixel is considered black, an unset pixel considered
    * white.
    * ======================================================================= */
   /* --- entry point --- */
   int aagridnum (raster *rp, int irow, int icol)
   {
   /* -------------------------------------------------------------------------
   Allocations and Declarations
   -------------------------------------------------------------------------- */
   pixbyte *bitmap = rp->pixmap; /* local rp->pixmap ptr */
   int width=rp->width, height=rp->height, /* width, height of raster */
    imap = icol + irow*width; /* pixel index = icol + irow*width */
   int bitval=0, /* value of rp bit at irow,icol */
    nnbitval=0, nebitval=0, eebitval=0, sebitval=0, /*adjacent vals*/
    ssbitval=0, swbitval=0, wwbitval=0, nwbitval=0, /*compass pt names*/
    gridnum = (-1); /* grid# 0-511 for above 9 bits */
   /* -------------------------------------------------------------------------
   check input
   -------------------------------------------------------------------------- */
   if ( irow<0 || irow>=height /* irow out-of-bounds */
   ||   icol<0 || icol>=width ) goto end_of_job; /* icol out-of-bounds */
   /* -------------------------------------------------------------------------
   get the 9 bits comprising the 3x3 grid centered at irow,icol
   -------------------------------------------------------------------------- */
   /* --- get center bit --- */
   bitval = getlongbit(bitmap,imap); /* value of rp input bit at imap */
   /* --- get 8 surrounding bits --- */
   if ( irow > 0 ) /* nn (north) bit available */
      nnbitval = getlongbit(bitmap,imap-width); /* nn bit value */
   if ( irow < height-1 ) /* ss (south) bit available */
      ssbitval = getlongbit(bitmap,imap+width); /* ss bit value */
   if ( icol > 0 ) /* ww (west) bit available */
    { wwbitval = getlongbit(bitmap,imap-1); /* ww bit value */
      if ( irow > 0 ) /* nw bit available */
        nwbitval = getlongbit(bitmap,imap-width-1); /* nw bit value */
      if ( irow < height-1 ) /* sw bit available */
        swbitval = getlongbit(bitmap,imap+width-1); } /* sw bit value */
   if ( icol < width-1 ) /* ee (east) bit available */
    { eebitval = getlongbit(bitmap,imap+1); /* ee bit value */
      if ( irow > 0 ) /* ne bit available */
        nebitval = getlongbit(bitmap,imap-width+1); /* ne bit value */
      if ( irow < height-1 ) /* se bit available */
        sebitval = getlongbit(bitmap,imap+width+1); } /* se bit value */
   /* --- set gridnum --- */
   gridnum = 0; /* clear all bits */
   if (   bitval ) gridnum = 1; /* set1bit(gridnum,0); */
   if ( nwbitval ) gridnum += 256; /* set1bit(gridnum,8); */
   if ( nnbitval ) gridnum += 128; /* set1bit(gridnum,7); */
   if ( nebitval ) gridnum += 64; /* set1bit(gridnum,6); */
   if ( wwbitval ) gridnum += 32; /* set1bit(gridnum,5); */
   if ( eebitval ) gridnum += 16; /* set1bit(gridnum,4); */
   if ( swbitval ) gridnum += 8; /* set1bit(gridnum,3); */
   if ( ssbitval ) gridnum += 4; /* set1bit(gridnum,2); */
   if ( sebitval ) gridnum += 2; /* set1bit(gridnum,1); */
   /* -------------------------------------------------------------------------
   Back to caller with gridnum coding 3x3 grid centered at irow,icol
   -------------------------------------------------------------------------- */
   end_of_job:
     return ( gridnum );
   } /* --- end-of-function aagridnum() --- */
   
   
   /* ==========================================================================
    * Function: aapatternnum ( gridnum )
    * Purpose: Looks up the pattern number 1...51
    * corresponding to the 3x3 pixel grid coded by gridnum 0=no
    * pixels set (white) to 511=all pixels set (black).
    * --------------------------------------------------------------------------
    * Arguments: gridnum (I) int containing 0-511 coding a 3x3 pixel grid
    * (see Notes below)
    * --------------------------------------------------------------------------
    * Returns: ( int ) 1 to 51, or -1=error
    * --------------------------------------------------------------------------
    * Notes:     o Input gridnum is a 9-bit int, 0-511, coding a 3x3 pixel grid
    * whose bit positions (and corresponding values) in gridnum are
    *  876     256 128  64
    *  504  =   32   1  16
    *  321       8   4   2
    * Thus, for example (*=pixel set/black, -=pixel not set/white),
    *  *--         *--  -**         (note that 209 is the
    *  -*- = 259   *-- = 302   -** = 209    inverse, set<-->unset,
    *  --*         ***         ---          of 302)
    *      o A set pixel is considered black, an unset pixel considered
    * white.
    *      o Ignoring whether the center pixel is set or unset, and
    * taking rotation, reflection and inversion (set<-->unset)
    * symmetries into account, there are 32 unique pixel patterns.
    * If inversions are listed separately, there are 51 patterns.
    *      o Here are the 51 unique patterns, with ? always denoting the
    * undetermined center pixel.  At the upper-left corner of each
    * pattern is the "pattern index number" assigned to it in this
    * function. At the upper-right is the pattern's multiplicity,
    * i.e., the number of different patterns obtained by rotations
    * and reflection of the illustrated one.  Inverse patters are
    * illustrated immediately beneath the original (the first three
    * four-pixel patterns have identical inverses).
    * -------------------------------------------------------------
    * No pixels set:
    * #1 1 (in this case, 1 signifies that rotation
    *  ---  and reflection give no different grids)
    *  -?-
    *  ---
    * Inverse, all eight pixels set
    * #2 1 (the inverse multiplicity is always the same)
    *  ***
    *  *?*
    *  ***
    * -------------------------------------------------------------
    * One pixel set:
    * #3 4  #4 4
    *  *--   -*-
    *  -?-   -?-
    *  ---   ---
    * Inverse, seven pixels set:
    * #5 4  #6 4
    *  -**   *-*
    *  *?*   *?*
    *  ***   ***
    * -------------------------------------------------------------
    * Two pixels set:
    * #7 8  #8 4  #9 8  10 2  11 4  12 2
    *  **-   *-*   *--   *--   -*-   -*-
    *  -?-   -?-   -?*   -?-   -?*   -?-
    *  ---   ---   ---   --*   ---   -*-
    * Inverse, six pixels set:
    * #13 8  14 4  15 8  16 2  17 4  18 2
    *  --*   -*-   -**   -**   *-*   *-*
    *  *?*   *?*   *?-   *?*   *?-   *?*
    *  ***   ***   ***   **-   ***   *-*
    * -------------------------------------------------------------
    * Three pixels set:
    * #19 4  20 8  21 8  22 8  23 8  24 4  25 4  26 4  27 4  28 4
    *  ***   **-   **-   **-   **-   **-   *-*   *-*   -*-   -*-
    *  -?-   -?*   -?-   -?-   -?-   *?-   -?-   -?-   -?*   -?*
    *  ---   ---   --*   -*-   *--   ---   --*   -*-   -*-   *--
    * Inverse, five pixels set:
    * #29 4  30 8  31 8  32 8  33 8  34 4  35 4  36 4  37 4  38 4
    *  ---   --*   --*   --*   --*   --*   -*-   -*-   *-*   *-*
    *  *?*   *?-   *?*   *?*   *?*   -?*   *?*   *?*   *?-   *?-
    *  ***   ***   **-   *-*   -**   ***   **-   *-*   *-*   -**
    * -------------------------------------------------------------
    * Four pixels set (including inverses):
    * #39 8  40 4  41 8  42 8  43 4  44 4  45 8  46 1
    *  ***   **-   **-   ***   ***   **-   **-   *-*
    *  -?*   -?-   -?*   -?-   -?-   -?*   -?*   -?-
    *  ---   -**   *--   --*   -*-   --*   -*-   *-*
    *
    *                  #47 8  48 4  49 4  50 8  51 1
    *                    ---   ---   --*   --*   -*-
    *                    *?*   *?*   *?-   *?-   *?*
    *                    **-   *-*   **-   *-*   -*-
    * ======================================================================= */
   /* --- entry point --- */
   int aapatternnum ( int gridnum )
   {
   /* -------------------------------------------------------------------------
   Allocations and Declarations
   -------------------------------------------------------------------------- */
   int pattern = (-1); /*pattern#, 1-51, for input gridnum*/
   /* ---
    * pattern number corresponding to input gridnum/2 code
    * ( gridnum/2 strips off gridnum's low bit because it's
    * the same pattern whether or not center pixel is set )
    * --- */
   static int patternnum[] = {
       1, 3, 4, 7, 3, 8, 7,19, 4, 7,11,24, 9,23,20,39,  /*   0- 15 */
       4, 9,11,20, 7,23,24,39,12,22,27,47,22,48,47,29,  /*  16- 31 */
       3, 8, 9,23,10,25,21,42, 7,19,20,39,21,42,44,34,  /*  32- 47 */
       9,26,28,41,21,50,49,30,22,43,45,33,40,32,31,13,  /*  48- 63 */
       4, 9,12,22, 9,26,22,43,11,20,27,47,28,41,45,33,  /*  64- 79 */
      11,28,27,45,20,41,47,33,27,45,51,35,45,36,35,14,  /*  80- 95 */
       7,23,22,48,21,50,40,32,24,39,47,29,49,30,31,13,  /*  96-111 */
      20,41,45,36,44,38,31,15,47,33,35,14,31,15,16, 5,  /* 112-127 */
       3,10, 9,21, 8,25,23,42, 9,21,28,49,26,50,41,30,  /* 128-143 */
       7,21,20,44,19,42,39,34,22,40,45,31,43,32,33,13,  /* 144-159 */
       8,25,26,50,25,46,50,37,23,42,41,30,50,37,38,17,  /* 160-175 */
      23,50,41,38,42,37,30,17,48,32,36,15,32,18,15, 6,  /* 176-191 */
       7,21,22,40,23,50,48,32,20,44,45,31,41,38,36,15,  /* 192-207 */
      24,49,47,31,39,30,29,13,47,31,35,16,33,15,14, 5,  /* 208-223 */
      19,42,43,32,42,37,32,18,39,34,33,13,30,17,15, 6,  /* 224-239 */
      39,30,33,15,34,17,13, 6,29,13,14, 5,13, 6, 5, 2,  /* 240-255 */
      -1 } ; /* --- end-of-patternnum[] --- */
   /* -------------------------------------------------------------------------
   look up pattern number for gridnum
   -------------------------------------------------------------------------- */
   /* --- first check input --- */
   if ( gridnum<0 || gridnum>511 ) goto end_of_job; /* gridnum out of bounds */
   /* --- look up pattern number, 1-51, corresponding to input gridnum --- */
   pattern = patternnum[gridnum/2]; /* /2 strips off gridnum's low bit */
   if ( pattern<1 || pattern>51 ) pattern = (-1); /* some internal error */
   end_of_job:
     return ( pattern ); /* back to caller with pattern# */
   } /* --- end-of-function aapatternnum() --- */
   
   
   /* ==========================================================================
    * Function: aalookup ( gridnum )
    * Purpose: Looks up the grayscale value 0=white to 255=black
    * corresponding to the 3x3 pixel grid coded by gridnum 0=no
    * pixels set (white) to 511=all pixels set (black).
    * --------------------------------------------------------------------------
    * Arguments: gridnum (I) int containing 0-511 coding a 3x3 pixel grid
    * --------------------------------------------------------------------------
    * Returns: ( int ) 0=white to 255=black, or -1=error
    * --------------------------------------------------------------------------
    * Notes:     o Input gridnum is a 9-bit int, 0-511, coding a 3x3 pixel grid
    *      o A set pixel is considered black, an unset pixel considered
    * white.  Likewise, the returned grayscale is 255 for black,
    * 0 for white.  You'd more typically want to use 255-grayscale
    * so that 255 is white and 0 is black.
    *      o The returned number is the (lowpass) antialiased grayscale
    * for the center pixel (gridnum bit 0) of the grid.
    * ======================================================================= */
   /* --- entry point --- */
   int aalookup ( int gridnum )
   {
   /* -------------------------------------------------------------------------
   Allocations and Declarations
   -------------------------------------------------------------------------- */
   int grayscale = (-1); /*returned grayscale, init for error*/
   int pattern = (-1), aapatternnum(); /*pattern#, 1-51, for input gridnum*/
   int iscenter = gridnum&1; /*low-order bit set for center pixel*/
   /* --- gray scales --- */
   #define WHT 0
   #define LGT 64
   #define GRY 128
   #define DRK 192
   #define BLK 255
   #if 1
   /* ---
    * modified aapnm() grayscales (second try)
    * --- */
   /* --- grayscale for each pattern when center pixel set/black --- */
   static int grayscale1[] = { -1, /* [0] index not used */
      BLK,BLK,BLK,BLK,242,230,BLK,BLK,BLK,160, /*  1-10 */
   /* BLK,BLK,BLK,BLK,242,230,BLK,BLK,BLK,BLK, */ /*  1-10 */
      BLK,BLK,217,230,217,230,204,BLK,BLK,166, /* 11-20 */
      BLK,BLK,BLK,BLK,BLK,BLK,178,166,204,191, /* 21-30 */
      204,BLK,204,191,217,204,BLK,191,178,BLK, /* 31-40 */
      178,BLK,BLK,178,191,BLK,191,BLK,178,BLK,204, /* 41-51 */
      -1 } ; /* --- end-of-grayscale1[] --- */
   /* --- grayscale for each pattern when center pixel not set/white --- */
   static int grayscale0[] = { -1, /* [0] index not used */
      WHT,WHT,WHT,WHT,WHT,WHT,WHT,WHT,WHT,WHT, /*  1-10 */
       64,WHT,WHT,128,115,128,WHT,WHT,WHT, 64, /* 11-20 */
    /* 51,WHT,WHT,128,115,128,WHT,WHT,WHT, 64, */ /* 11-20 */
      WHT,WHT,WHT, 64,WHT,WHT, 76, 64,102, 89, /* 21-30 */
      102,WHT,102,WHT,115,102,WHT, 89, 76,WHT, /* 31-40 */
       76,WHT,WHT, 76, 89,WHT, 89,WHT, 76,WHT,102, /* 41-51 */
      -1 } ; /* --- end-of-grayscale0[] --- */
   #endif
   #if 0
   /* ---
    * modified aapnm() grayscales (first try)
    * --- */
   /* --- grayscale for each pattern when center pixel set/black --- */
   static int grayscale1[] = { -1, /* [0] index not used */
      BLK,BLK,BLK,BLK,242,230,GRY,BLK,BLK,BLK, /*  1-10 */
   /* BLK,BLK,BLK,BLK,242,230,BLK,BLK,BLK,BLK, */ /*  1-10 */
      BLK,BLK,217,230,217,230,204,BLK,BLK,166, /* 11-20 */
      BLK,BLK,BLK,BLK,BLK,BLK,BLK,166,204,191, /* 21-30 */
   /* BLK,BLK,BLK,BLK,BLK,BLK,178,166,204,191, */ /* 21-30 */
      204,BLK,204,BLK,217,204,BLK,191,GRY,BLK, /* 31-40 */
   /* 204,BLK,204,191,217,204,BLK,191,178,BLK, */ /* 31-40 */
      178,BLK,BLK,178,191,BLK,BLK,BLK,178,BLK,204, /* 41-51 */
   /* 178,BLK,BLK,178,191,BLK,191,BLK,178,BLK,204, */ /* 41-51 */
      -1 } ; /* --- end-of-grayscale1[] --- */
   /* --- grayscale for each pattern when center pixel not set/white --- */
   static int grayscale0[] = { -1, /* [0] index not used */
      WHT,WHT,WHT,WHT,WHT,WHT,WHT,WHT,WHT,WHT, /*  1-10 */
      GRY,WHT,WHT,128,115,128,WHT,WHT,WHT,GRY, /* 11-20 */
   /*  51,WHT,WHT,128,115,128,WHT,WHT,WHT, 64, */ /* 11-20 */
      WHT,WHT,WHT,GRY,WHT,WHT, 76, 64,102, 89, /* 21-30 */
   /* WHT,WHT,WHT, 64,WHT,WHT, 76, 64,102, 89, */ /* 21-30 */
      102,WHT,102,WHT,115,102,WHT, 89,GRY,WHT, /* 31-40 */
   /* 102,WHT,102,WHT,115,102,WHT, 89, 76,WHT, */ /* 31-40 */
       76,WHT,WHT,GRY, 89,WHT, 89,WHT, 76,WHT,102, /* 41-51 */
   /*  76,WHT,WHT, 76, 89,WHT, 89,WHT, 76,WHT,102, */ /* 41-51 */
      -1 } ; /* --- end-of-grayscale0[] --- */
   #endif
   #if 0
   /* ---
    * these grayscales _exactly_ correspond to the aapnm() algorithm
    * --- */
   /* --- grayscale for each pattern when center pixel set/black --- */
   static int grayscale1[] = { -1, /* [0] index not used */
      BLK,BLK,BLK,BLK,242,230,BLK,BLK,BLK,BLK, /*  1-10 */
      BLK,BLK,217,230,217,230,204,BLK,BLK,166, /* 11-20 */
      BLK,BLK,BLK,BLK,BLK,BLK,178,166,204,191, /* 21-30 */
      204,BLK,204,191,217,204,BLK,191,178,BLK, /* 31-40 */
      178,BLK,BLK,178,191,BLK,191,BLK,178,BLK,204, /* 41-51 */
      -1 } ; /* --- end-of-grayscale1[] --- */
   /* --- grayscale for each pattern when center pixel not set/white --- */
   static int grayscale0[] = { -1, /* [0] index not used */
      WHT,WHT,WHT,WHT,WHT,WHT,WHT,WHT,WHT,WHT, /*  1-10 */
       51,WHT,WHT,128,115,128,WHT,WHT,WHT, 64, /* 11-20 */
      WHT,WHT,WHT, 64,WHT,WHT, 76, 64,102, 89, /* 21-30 */
      102,WHT,102,WHT,115,102,WHT, 89, 76,WHT, /* 31-40 */
       76,WHT,WHT, 76, 89,WHT, 89,WHT, 76,WHT,102, /* 41-51 */
      -1 } ; /* --- end-of-grayscale0[] --- */
   #endif
   /* -------------------------------------------------------------------------
   look up grayscale for gridnum
   -------------------------------------------------------------------------- */
   /* --- first check input --- */
   if ( gridnum<0 || gridnum>511 ) goto end_of_job; /* gridnum out of bounds */
   /* --- look up pattern number, 1-51, corresponding to input gridnum --- */
   pattern = aapatternnum(gridnum); /* look up pattern number */
   if ( pattern<1 || pattern>51 ) goto end_of_job; /* some internal error */
   if ( ispatternnumcount ) { /* counts being accumulated */
     if (iscenter) patternnumcount1[pattern] += 1; /* bump diagnostic count */
     else patternnumcount0[pattern] += 1; }
   /* --- look up grayscale for this pattern --- */
   grayscale = ( iscenter? grayscale1[pattern] : grayscale0[pattern] );
   end_of_job:
     return ( grayscale ); /* back to caller with grayscale */
   } /* --- end-of-function aalookup() --- */
   
   
   /* ==========================================================================
    * Function: aalowpasslookup ( rp, bytemap, grayscale )
    * Purpose: calls aalookup() for each pixel in rp->bitmap
    * to create anti-aliased bytemap
    * --------------------------------------------------------------------------
    * Arguments: rp (I) raster *  to raster whose bitmap
    * is to be anti-aliased
    * bytemap (O) intbyte * to bytemap, calculated
    * by calling aalookup() for each pixel
    * in rp->bitmap
    * grayscale (I) int containing number of grayscales
    * to be calculated, 0...grayscale-1
    * (should typically be given as 256)
    * --------------------------------------------------------------------------
    * Returns: ( int ) 1=success, 0=any error
    * --------------------------------------------------------------------------
    * Notes:    o
    * ======================================================================= */
   /* --- entry point --- */
   int aalowpasslookup (raster *rp, intbyte *bytemap, int grayscale)
   {
   /* -------------------------------------------------------------------------
   Allocations and Declarations
   -------------------------------------------------------------------------- */
   int width=rp->width, height=rp->height, /* width, height of raster */
    icol = 0, irow = 0, imap = (-1); /* width, height, bitmap indexes */
   int bgbitval=0 /*, fgbitval=1*/; /* background, foreground bitval */
   int bitval=0, /* value of rp bit at irow,icol */
    aabyteval=0; /* antialiased (or unchanged) value*/
   int gridnum=0, aagridnum(), /* grid# for 3x3 grid at irow,icol */
    aalookup(); /* table look up  antialiased value*/
   /* -------------------------------------------------------------------------
   generate bytemap by table lookup for each pixel of bitmap
   -------------------------------------------------------------------------- */
   for ( irow=0; irow<height; irow++ )
    for ( icol=0; icol<width; icol++ )
     {
     /* --- get gridnum and center bit value, init aabyteval --- */
     gridnum = aagridnum(rp,irow,icol); /*grid# coding 3x3 grid at irow,icol*/
     bitval = (gridnum&1); /* center bit set if gridnum odd */
     aabyteval = (intbyte)(bitval==bgbitval?0:grayscale-1); /* default aa val */
     imap++; /* first bump bitmap[] index */  
     bytemap[imap] = (intbyte)(aabyteval); /* init antialiased pixel */
     /* --- look up antialiased value for this grid --- */
     aabyteval = aalookup(gridnum); /* look up on grid# */
     if ( aabyteval>=0 && aabyteval<=255 ) /* check for success */
       bytemap[imap] = (intbyte)(aabyteval); /* init antialiased pixel */
     } /* --- end-of-for(irow,icol) --- */
   ispatternnumcount = 0; /* accumulate counts only once */
   /* -------------------------------------------------------------------------
   Back to caller with gray-scale anti-aliased bytemap
   -------------------------------------------------------------------------- */
   /*end_of_job:*/
     return ( 1 );
   } /* --- end-of-function aalowpasslookup() --- */
   
   
   /* ==========================================================================
  * Function: aasupsamp ( rp, aa, sf, grayscale )   * Function: aasupsamp ( rp, aa, sf, grayscale )
  * Purpose: calculates a supersampled anti-aliased bytemap   * Purpose: calculates a supersampled anti-aliased bytemap
  * for rp->bitmap, with each byte 0...grayscale-1   * for rp->bitmap, with each byte 0...grayscale-1
Line 9469  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 9480  end_of_job: Line 14973  end_of_job:
  * (usually just #rows * #cols)   * (usually just #rows * #cols)
  * colors (O) intbyte *  (to be interpreted as ints)   * colors (O) intbyte *  (to be interpreted as ints)
  * returning a list of the discrete/different   * returning a list of the discrete/different
  * values in bytemap, in ascending value order   * values in bytemap, in ascending value order,
    * and with gamma correction applied
  * colormap (O) intbyte *  returning a bytemap "image",   * colormap (O) intbyte *  returning a bytemap "image",
  * i.e., in one-to-one pixel correspondence   * i.e., in one-to-one pixel correspondence
  * with bytemap, but where the values have been   * with bytemap, but where the values have been
Line 9501  int ncolors = 0,   /* #different values Line 14995  int ncolors = 0,   /* #different values
  igray, grayscale = 256; /* bytemap contains intbyte's */   igray, grayscale = 256; /* bytemap contains intbyte's */
 intbyte *bytevalues = NULL; /* 1's where bytemap contains value*/  intbyte *bytevalues = NULL; /* 1's where bytemap contains value*/
 int ibyte; /* bytemap/colormap index */  int ibyte; /* bytemap/colormap index */
 int isscale = 0; /* true to scale largest val to 255*/  int isscale = 0, /* true to scale largest val to 255*/
    isgamma = 1; /* true to apply gamma correction */
 int maxcolors = 0; /* maximum ncolors */  int maxcolors = 0; /* maximum ncolors */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 Accumulate colors[] from values occurring in bytemap  Accumulate colors[] from values occurring in bytemap
Line 9532  if ( isscale )    /* only rescale if req Line 15027  if ( isscale )    /* only rescale if req
     { colors[igray] = min2(grayscale-1,(int)(scalefactor*colors[igray]+0.5));      { colors[igray] = min2(grayscale-1,(int)(scalefactor*colors[igray]+0.5));
       if (igray>5) colors[igray] = min2(grayscale-1,colors[igray]+2*igray); }        if (igray>5) colors[igray] = min2(grayscale-1,colors[igray]+2*igray); }
    } /* --- end-of-if(isscale) --- */     } /* --- end-of-if(isscale) --- */
   /* --- apply gamma correction --- */
   if ( isgamma /* only gamma correct if requested */
   &&   gammacorrection > 0.0001 ) /* and if we have gamma correction */
    if ( ncolors > 1 ) /* and if not a "blank" raster */
     if ( colors[ncolors-1] > 0 ) /*and at least one pixel non-white*/
      {
      for ( igray=1; igray<ncolors; igray++ ) { /*gamma correct each colors[]*/
       int grayval = colors[igray], /* original 0=white to 255=black */
    gmax = grayscale-1; /* should be 255 */
       double dgray=((double)(gmax-grayval))/((double)gmax); /*0=black 1=white*/
       dgray = pow(dgray,(1.0/gammacorrection)); /* apply gamma correction */
       grayval = (int)( gmax*(1.0-dgray) + 0.5 ); /* convert back to grayval */
       colors[igray] = grayval; } /* store back in colors[] */
      } /* --- end-of-if(isgamma) --- */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 Construct colormap  Construct colormap
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
Line 9726  else     /* center point not black */ Line 15235  else     /* center point not black */
 /*end_of_job:*/  /*end_of_job:*/
   return ( aaimgval );    return ( aaimgval );
 } /* --- end-of-function aawtpixel() --- */  } /* --- end-of-function aawtpixel() --- */
   
   
   /* ==========================================================================
    * Function: mimetexsetmsg ( newmsglevel, newmsgfp )
    * Purpose: Sets msglevel and msgfp, usually called from
    * an external driver (i.e., DRIVER not defined
    * in this module).
    * --------------------------------------------------------------------------
    * Arguments: newmsglevel (I) int containing new msglevel
    * (unchanged if newmsglevel<0)
    * newmsgfp (I) FILE * containing new msgfp
    * (unchanged if newmsgfp=NULL)
    * --------------------------------------------------------------------------
    * Returns: ( int ) always 1
    * --------------------------------------------------------------------------
    * Notes:     o
    * ======================================================================= */
   /* --- entry point --- */
   int mimetexsetmsg ( int newmsglevel, FILE *newmsgfp )
   {
   /* -------------------------------------------------------------------------
   set msglevel and msgfp
   -------------------------------------------------------------------------- */
   if ( newmsglevel >= 0 ) msglevel = newmsglevel;
   if ( newmsgfp != NULL ) msgfp = newmsgfp;
   return ( 1 );
   } /* --- end-of-function mimetexsetmsg() --- */
 #endif /* PART3 */  #endif /* PART3 */
   
 /* ---  /* ---
Line 9805  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 9844  STATIC logdata mimelog[] Line 15383  STATIC logdata mimelog[]
 #endif  #endif
   ;    ;
   
 /* -------------------------------------------------------------------------  
 messages  
 -------------------------------------------------------------------------- */  
 static char *copyright = /* copyright, gnu/gpl notice */  
  "+-----------------------------------------------------------------------+\n"  
  "|mimeTeX vers 1.61, Copyright(c) 2002-2005, 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 9881  int main ( int argc, char *argv[] Line 15395  int main ( int argc, char *argv[]
 Allocations and Declarations  Allocations and Declarations
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 /* --- expression to be emitted --- */  /* --- expression to be emitted --- */
 static char exprbuffer[16385] = "f(x)=x^2"; /* expression to be processed */  static char exprbuffer[MAXEXPRSZ+1] = "f(x)=x^2"; /* input TeX expression */
 char *expression = exprbuffer; /* ptr to expression */  char *expression = exprbuffer; /* ptr to expression */
 int size = NORMALSIZE; /* default font size */  int size = NORMALSIZE; /* default font size */
 char *query = getenv("QUERY_STRING"); /* getenv("QUERY_STRING") result */  char *query = getenv("QUERY_STRING"); /* getenv("QUERY_STRING") result */
Line 9890  int unescape_url();   /* convert %xx's t Line 15404  int unescape_url();   /* convert %xx's t
 int emitcache(); /* emit cached image if it exists */  int emitcache(); /* emit cached image if it exists */
 int isquery = 0, /* true if input from QUERY_STRING */  int isquery = 0, /* true if input from QUERY_STRING */
  isqempty = 0, /* true if query string empty */   isqempty = 0, /* true if query string empty */
    isqforce = 0, /* true to force query emulation */
  isqlogging = 0, /* true if logging in query mode */   isqlogging = 0, /* true if logging in query mode */
  isformdata = 0, /* true if input from html form */   isformdata = 0, /* true if input from html form */
  isdumpimage = 0; /* true to dump image on stdout */   isinmemory = 1, /* true to generate image in memory*/
    isdumpimage = 0, /* true to dump image on stdout */
    isdumpbuffer = 0; /* true to dump to memory buffer */
 /* --- rasterization --- */  /* --- rasterization --- */
 subraster *rasterize(), *sp; /* rasterize expression */  subraster *rasterize(), *sp=NULL; /* rasterize expression */
 raster *border_raster(), *bp; /* put a border around raster */  raster *border_raster(), *bp=NULL; /* put a border around raster */
   int delete_subraster(); /* for clean-up at end-of-job */
 int type_raster(), type_bytemap(), /* screen dump function prototypes */  int type_raster(), type_bytemap(), /* screen dump function prototypes */
  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 9918  int norefmaxlen = NOREFMAXLEN; /*max que Line 15444  int norefmaxlen = NOREFMAXLEN; /*max que
   void GIF_SetColor(),GIF_SetTransparent(); /* ...gifsave enntry points */    void GIF_SetColor(),GIF_SetTransparent(); /* ...gifsave enntry points */
 #endif  #endif
 char *gif_outfile = (char *)NULL, /* gif output defaults to stdout */  char *gif_outfile = (char *)NULL, /* gif output defaults to stdout */
    gif_buffer[MAXGIFSZ] = "\000", /* or gif written in memory buffer */
  cachefile[256] = "\000", /* full path and name to cache file*/   cachefile[256] = "\000", /* full path and name to cache file*/
  *md5str(); /* md5 has of expression */   *md5str(); /* md5 has of expression */
 int maxage = 7200; /* max-age is two hours */  int maxage = 7200; /* max-age is two hours */
   int valign = (-9999); /*Vertical-Align:baseline-(height-1)*/
   /* --- pbm/pgm (-g switch) --- */
   int ispbmpgm = 0; /* true to write pbm/pgm file */
   int type_pbmpgm(), ptype=0; /* entry point, graphic format */
   char *pbm_outfile = (char *)NULL; /* output file defaults to stdout */
 /* --- anti-aliasing --- */  /* --- anti-aliasing --- */
 intbyte *bytemap_raster = NULL, /* anti-aliased bitmap */  intbyte *bytemap_raster = NULL, /* anti-aliased bitmap */
  colors[256]; /* grayscale vals in bytemap */   colors[256]; /* grayscale vals in bytemap */
Line 9928  int aalowpass(), aapnm(),  /*lowpass fil Line 15460  int aalowpass(), aapnm(),  /*lowpass fil
  grayscale = 256; /* 0-255 grayscales in 8-bit bytes */   grayscale = 256; /* 0-255 grayscales in 8-bit bytes */
 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*/
   /* --- 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 9946  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 */
   iscachecontenttype = 0; /* true to cache mime content-type */
   *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 */
   if ( iscaching ) { /* images are being cached */
     strcpy(cachepath,CACHEPATH); /* relative path to cached files */
     if ( *cachepath == '%' ) { /* leading % signals cache headers */
       iscachecontenttype = 1; /* signal caching mime content-type*/
       strsqueeze(cachepath,1); } } /* and squeeze out leading % char */
   gifSize = 0; /* signal that image not in memory */
   fgred=FGRED; fggreen=FGGREEN; fgblue=FGBLUE; /* default foreground colors */
   bgred=BGRED; bggreen=BGGREEN; bgblue=BGBLUE; /* default background colors */
 shrinkfactor = shrinkfactors[NORMALSIZE]; /* set shrinkfactor */  shrinkfactor = shrinkfactors[NORMALSIZE]; /* set shrinkfactor */
   for ( ipattern=1; ipattern<=51; ipattern++ )
    patternnumcount0[ipattern] = patternnumcount1[ipattern] = 0;
 /* ---  /* ---
  * 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,16384); /* so use it as expression */      strncpy(expression,query,MAXEXPRSZ); /* so use it as expression */
       expression[16384] = '\000'; /* make sure it's null terminated */      expression[MAXEXPRSZ] = '\000'; /* make sure it's null terminated */
       isquery = 1; } /* and set isquery flag */      if ( 0 ) /*true to remove leading whitespace*/
 if ( !isquery ) /* empty query string */        while ( isspace(*expression) && *expression!='\000' )
   { char *host = getenv("HTTP_HOST"), /* additional getenv("") results */          {strsqueeze(expression,1);} /* squeeze out white space */
     *name = getenv("SERVER_NAME"), *addr = getenv("SERVER_ADDR");      isquery = 1; } /* and set isquery flag */
     if ( host!=NULL || name!=NULL || addr!=NULL ) /* assume http query */  if ( !isquery ) { /* empty query string */
       { isquery = 1; /* set flag to signal query */    char *host = getenv("HTTP_HOST"), /* additional getenv("") results */
  strcpy(expression,"\\red\\small\\rm~missing~query~string"); }    *name = getenv("SERVER_NAME"), *addr = getenv("SERVER_ADDR");
     isqempty = 1; /* signal empty query string */    if ( host!=NULL || name!=NULL || addr!=NULL ) { /* assume http query */
       isquery = 1; /* set flag to signal query */
       if ( exitstatus == 0 ) exitstatus = errorstatus; /* signal error */
       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 9997  if ( !isquery    /* don't have an html q Line 15564  if ( !isquery    /* don't have an html q
     if ( !isstopsignal /* haven't seen stopsignal switch */      if ( !isstopsignal /* haven't seen stopsignal switch */
     &&   *argv[argnum] == '-' ) /* and have some '-' switch */      &&   *argv[argnum] == '-' ) /* and have some '-' switch */
       {        {
       char flag = tolower(*(argv[argnum]+1)); /* single char following '-' */        char *field = argv[argnum] + 1; /* ptr to char(s) following - */
       int  arglen = strlen(argv[argnum]) - 1; /* #chars following - */        char flag = tolower(*field); /* single char following '-' */
         int  arglen = strlen(field); /* #chars following - */
       argnum++; /* arg following flag/switch is usually its value */        argnum++; /* arg following flag/switch is usually its value */
       nswitches++; /* another switch on command line */        nswitches++; /* another switch on command line */
       if ( isstrict && arglen!=1 ) /* only single-char switch allowed */        if ( isstrict && /* if strict checking then... */
         !isthischar(flag,"g") && arglen!=1 ) /*must be single-char switch*/
  { nbadargs++; argnum--; } /* so ignore longer -xxx switch */   { nbadargs++; argnum--; } /* so ignore longer -xxx switch */
       else /* process single-char -x switch */        else /* process single-char -x switch */
        switch ( flag ) { /* see what user wants to tell us */         switch ( flag ) { /* see what user wants to tell us */
  /* --- ignore uninterpreted flag --- */   /* --- ignore uninterpreted flag --- */
  default:  nbadargs++;                              argnum--;  break;   default:  nbadargs++;                              argnum--;  break;
  /* --- adjustable program parameters (not checking input) --- */   /* --- adjustable program parameters (not checking input) --- */
    case 'b': isdumpimage++; isdumpbuffer++;           argnum--;  break;
  case 'd': isdumpimage++;                           argnum--;  break;   case 'd': isdumpimage++;                           argnum--;  break;
  case 'e': isdumpimage++;           gif_outfile=argv[argnum];  break;   case 'e': isdumpimage++;           gif_outfile=argv[argnum];  break;
  case 'f': isdumpimage++;                   infilearg=argnum;  break;   case 'f': isdumpimage++;                   infilearg=argnum;  break;
  case 'm': msglevel = atoi(argv[argnum]);                      break;   case 'g': ispbmpgm++;
  case 'o': istransparent = 0;                       argnum--;  break;       if ( arglen > 1 ) ptype = atoi(field+1); /* -g2 ==> ptype=2 */
  case 's': size = atoi(argv[argnum]);                          break;       if ( 1 || *argv[argnum]=='-' ) argnum--; /*next arg is -switch*/
        else pbm_outfile = argv[argnum]; break; /*next arg is filename*/
    case 'm': if ( argnum < argc ) msglevel = atoi(argv[argnum]); break;
    case 'o': istransparent = (istransparent?0:1);     argnum--;  break;
    case 'q': isqforce = 1;                            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 10024  if ( !isquery    /* don't have an html q Line 15599  if ( !isquery    /* don't have an html q
       else nbadargs++; /* infile and expression invalid */        else nbadargs++; /* infile and expression invalid */
     } /* --- end-of-while(argc>++argnum) --- */      } /* --- end-of-while(argc>++argnum) --- */
  if ( msglevel>=999 && msgfp!=NULL ) /* display command-line info */   if ( msglevel>=999 && msgfp!=NULL ) /* display command-line info */
   fprintf(msgfp,"argc=%d, progname=%s, #args=%d, #badargs=%d\n",    { fprintf(msgfp,"argc=%d, progname=%s, #args=%d, #badargs=%d\n",
   argc,progname,nargs,nbadargs);      argc,progname,nargs,nbadargs);
       fprintf(msgfp,"cachepath=\"%.50s\" pathprefix=\"%.50s\"\n",
       cachepath,pathprefix); }
  /* ---   /* ---
   * decide whether command-line input overrides query_string    * decide whether command-line input overrides query_string
   * -------------------------------------------------------- */    * -------------------------------------------------------- */
Line 10040  if ( !isquery    /* don't have an html q Line 15617  if ( !isquery    /* don't have an html q
  &&   infilearg <= 0 ) /* and not given in input file */   &&   infilearg <= 0 ) /* and not given in input file */
   if ( !isquery /* no conflict if no query_string */    if ( !isquery /* no conflict if no query_string */
   ||   nswitches > 0 ) /* explicit -switch(es) also given */    ||   nswitches > 0 ) /* explicit -switch(es) also given */
    { strncpy(expression,argv[exprarg],16384); /*expression from command-line*/     { strncpy(expression,argv[exprarg],MAXEXPRSZ); /*expr from command-line*/
      expression[16384] = '\000'; /* make sure it's null terminated */       expression[MAXEXPRSZ] = '\000'; /* make sure it's null terminated */
      isquery = 0; } /* and not from a query_string */       isquery = 0; } /* and not from a query_string */
  /* ---   /* ---
   * or read expression from input file    * or read expression from input file
Line 10050  if ( !isquery    /* don't have an html q Line 15627  if ( !isquery    /* don't have an html q
   {    {
   FILE *infile = fopen(argv[infilearg],"r"); /* open input file for read */    FILE *infile = fopen(argv[infilearg],"r"); /* open input file for read */
   if ( infile != (FILE *)NULL ) /* opened input file successfully */    if ( infile != (FILE *)NULL ) /* opened input file successfully */
    { char instring[2049]; /* line from file */     { char instring[MAXLINESZ+1]; /* line from file */
        int  exprsz = 0; /* total #bytes read from file */
      isquery = 0; /* file input, not a query_string */       isquery = 0; /* file input, not a query_string */
      *expression = '\000'; /* start expresion as empty string */       *expression = '\000'; /* start expresion as empty string */
      while ( fgets(instring,2048,infile) != (char *)NULL ) /* read till eof*/       while ( fgets(instring,MAXLINESZ,infile) != (char *)NULL ) /*till eof*/
       strcat(expression,instring); /* concat line to end of expression*/        if ( exprsz + strlen(instring) < MAXEXPRSZ ) { /* have room for line */
    strcat(expression,instring); /* concat line to end of expression*/
    exprsz += strlen(instring); } /* update expression buffer length */
      fclose ( infile ); } /*close input file after reading expression*/       fclose ( infile ); } /*close input file after reading expression*/
   } /* --- end-of-if(infilearg>0) --- */    } /* --- end-of-if(infilearg>0) --- */
    /* ---
     * xlate +++'s to blanks only if query
     * ----------------------------------- */
    if ( !isquery ) isplusblank = 0; /* don't xlate +++'s to blanks */
    /* ---
     * check if emulating query (for testing)
     * -------------------------------------- */
    if ( isqforce ) isquery = 1; /* emulate query string processing */
    /* ---
     * check if emitting pbm/pgm graphic
     * --------------------------------- */
    if ( isgoodargs && ispbmpgm > 0 ) /* have a good -g arg */
     if ( 1 && gif_outfile != NULL ) /* had an -e switch with file */
      if ( *gif_outfile != '\000' ) /* make sure string isn't empty */
        { pbm_outfile = gif_outfile; /* use -e switch file for pbm/pgm */
          gif_outfile = (char *)NULL; /* reset gif output file */
          /*isdumpimage--;*/ } /* and decrement -e count */
  } /* --- end-of-if(!isquery) --- */   } /* --- end-of-if(!isquery) --- */
 /* ---  /* ---
  * check for <form> input   * check for <form> input
  * ---------------------- */   * ---------------------- */
 if ( isquery ) /* must be <form method="get"> */  if ( isquery ) { /* must be <form method="get"> */
  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 */
     unescape_url(expression,0); /* convert all %xx's to chars */      unescape_url(expression,0); /* convert all %xx's to chars */
     unescape_url(expression,0); /* repeat */      unescape_url(expression,0); /* repeat */
     msglevel = FORMLEVEL; /* msglevel for forms */      if(0) msglevel = FORMLEVEL; /* msglevel for forms */
     isformdata = 1; } /* set flag to signal form data */      isformdata = 1; } /* set flag to signal form data */
  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
  * ----------------------------------------------------------------- */   * ----------------------------------------------------------------- */
Line 10087  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 10095  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 10104  if ( isquery )    /* only log query_stri Line 15707  if ( isquery )    /* only log query_stri
  if ( msglevel >= LOGLEVEL /* check if logging */   if ( msglevel >= LOGLEVEL /* check if logging */
  &&   seclevel <= 5 ) /* and if logging permitted */   &&   seclevel <= 5 ) /* and if logging permitted */
   if ( logfile != NULL ) /* if a logfile is given */    if ( logfile != NULL ) /* if a logfile is given */
    if ( *logfile != '\000' ) /*and if it's not an empty string*/     if ( *logfile != '\000' ) { /*and if it's not an empty string*/
     if ( (msgfp=fopen(logfile,"a")) /* open logfile for append */      if ( (msgfp=fopen(logfile,"a")) /* open logfile for append */
     !=   NULL ) /* ignore logging if can't open */      !=   NULL ) /* ignore logging if can't open */
      {       {
Line 10155  if ( isquery )    /* only log query_stri Line 15758  if ( isquery )    /* only log query_stri
  isqlogging = 1; /* set query logging flag */   isqlogging = 1; /* set query logging flag */
      } /* --- end-of-if(msglevel>=LOGLEVEL) --- */       } /* --- end-of-if(msglevel>=LOGLEVEL) --- */
     else /* couldn't open logfile */      else /* couldn't open logfile */
      msglevel = 0; /* can't emit messages */       msglevel = 0; } /* can't emit messages */
 /* ---  /* ---
  * prepend prefix to submitted expression   * prepend prefix to submitted expression
  * -------------------------------------- */   * -------------------------------------- */
 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 10215  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"
  * ----------------------- */   * ------------------------- */
 if ( strstr(expression,"\\counter") != NULL /* can't cache \counter{} */  /* --- check if advertisement messages only for one particular host --- */
 ||   strstr(expression,"\\input")   != NULL /* can't cache \input{} */  if ( !isempty(host_showad) ) /* messages only for this referer */
 ||   strstr(expression,"\\nocach")  != NULL /* no caching requested */   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{} */
   ||   strstr(expression,"\\input")    != NULL /* can't cache \input{} */
   ||   strstr(expression,"\\today")    != NULL /* can't cache \today */
   ||   strstr(expression,"\\calendar") != NULL /* can't cache \calendar */
   ||   strstr(expression,"\\nocach")   != NULL /* no caching requested */
   ||   isformdata /* don't cache user form input */
  ) { iscaching = 0; /* so turn caching off */   ) { iscaching = 0; /* so turn caching off */
      maxage = 2; } /* and set max-age to two 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 10239  if ( isquery )    /* don't cache command Line 15894  if ( isquery )    /* don't cache command
    strcat(cachefile,md5hash); /* add md5 hash of expression */     strcat(cachefile,md5hash); /* add md5 hash of expression */
    strcat(cachefile,".gif"); /* finish with .gif extension */     strcat(cachefile,".gif"); /* finish with .gif extension */
    gif_outfile = cachefile; /* signal GIF_Create() to cache */     gif_outfile = cachefile; /* signal GIF_Create() to cache */
    /* --- (always) emit mime content-type line --- */     /* --- emit mime content-type line --- */
    fprintf( stdout, "Cache-Control: max-age=%d\n",maxage );     if ( 0 && isemitcontenttype ) /* now done in emitcache() */
    fprintf( stdout, "Content-type: image/gif\n\n" );      { fprintf( stdout, "Cache-Control: max-age=%d\n",maxage );
         if ( abs(valign) < 999 ) /* have vertical align */
           fprintf( stdout, "Vertical-Align: %d\n",valign );
         fprintf( stdout, "Content-type: image/gif\n\n" ); }
    /* --- emit cached image if it already exists --- */     /* --- emit cached image if it already exists --- */
    if ( emitcache(cachefile) > 0 ) /* cached image emitted */     if ( emitcache(cachefile,maxage,valign,0) > 0 ) /* cached image emitted */
     goto end_of_job; /* so nothing else to do */      goto end_of_job; /* so nothing else to do */
    /* --- log caching request --- */     /* --- log caching request --- */
    if ( msglevel >= 1 /* check if logging */     if ( msglevel >= 1 /* check if logging */
Line 10258  if ( isquery )    /* don't cache command Line 15916  if ( isquery )    /* don't cache command
         !=   NULL ) /* ignore logging if can't open */          !=   NULL ) /* ignore logging if can't open */
  { int isreflogged = 0; /* set true if http_referer logged */   { int isreflogged = 0; /* set true if http_referer logged */
    fprintf(filefp,"%s                 %s\n", /* timestamp, md5 file */     fprintf(filefp,"%s                 %s\n", /* timestamp, md5 file */
     timestamp(),cachefile+strlen(cachepath)); /* (path not shown) */      timestamp(TZDELTA,0),cachefile+strlen(cachepath)); /*skip path*/
    fprintf(filefp,"%s\n",expression); /* expression in filename */     fprintf(filefp,"%s\n",expression); /* expression in filename */
    if ( http_referer != NULL ) /* show referer if we have one */     if ( http_referer != NULL ) /* show referer if we have one */
     if ( *http_referer != '\000' )    /* and if not an empty string*/      if ( *http_referer != '\000' )    /* and if not an empty string*/
Line 10280  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
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 /* --- preprocess expression, converting LaTeX constructs for mimeTeX  --- */  /* --- preprocess expression, converting LaTeX constructs for mimeTeX  --- */
 expression = mimeprep(expression); /* preprocess expression */  if ( expression != NULL ) { /* have expression to rasterize */
     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 */
     valign= raster_baseline -(raster_height -1);/*#pixels for Vertical-Align:*/
     if ( abs(valign) > 255 ) valign = (-9999); } /* sanity check */
   if ( ispbmpgm && ptype<2 ) /* -g switch or -g1 switch */
     type_pbmpgm(bp,ptype,pbm_outfile); /* emit b/w pbm file */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 generate anti-aliased bytemap from (bordered) bitmap  generate anti-aliased bytemap from (bordered) bitmap
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
Line 10313  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 10322  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 10334  if ( isaa )    /* we want anti-aliased b Line 16030  if ( isaa )    /* we want anti-aliased b
      * select anti-aliasing algorithm       * select anti-aliasing algorithm
      * ------------------------------ */       * ------------------------------ */
     if ( !isss ) /* generate bytemap for lowpass */      if ( !isss ) /* generate bytemap for lowpass */
      if ( aaalgorithm == 1 ) /* 1 for aalowpass() */       switch ( aaalgorithm ) { /* choose antialiasing algorithm */
  { if ( aalowpass(bp,bytemap_raster,grayscale) /* my lowpass filter */         default: isaa = 0; break; /* unrecognized algorithm */
   ==   0 )  isaa = 0; } /*failed, so turn off anti-aliasing*/         case 1: /* 1 for aalowpass() */
      else /* or 2 for aapnm() */   if ( aalowpass(bp,bytemap_raster,grayscale) /*my own lowpass filter*/
       if ( aaalgorithm == 2 ) /*2 for netpbm pnmalias.c algorithm*/   ==   0 )  isaa = 0; /*failed, so turn off anti-aliasing*/
  { if ( aapnm(bp,bytemap_raster,grayscale) /* pnmalias.c filter */   break;
   ==   0 )  isaa = 0; } /*failed, so turn off anti-aliasing*/         case 2: /*2 for netpbm pnmalias.c algorithm*/
       else isaa = 0; /* unrecognized algorithm */   if ( aapnm(bp,bytemap_raster,grayscale) /* pnmalias.c filter */
    ==   0 )  isaa = 0; /*failed, so turn off anti-aliasing*/
    break;
          case 3: /*3 for aapnm() based on aagridnum()*/
    if ( aapnmlookup(bp,bytemap_raster,grayscale) /* pnmalias.c filter */
    ==   0 )  isaa = 0; /*failed, so turn off anti-aliasing*/
    break;
          case 4: /* 4 for aalookup() table lookup */
    if ( aalowpasslookup(bp,bytemap_raster,grayscale) /* aalookup() */
    ==   0 )  isaa = 0; /*failed, so turn off anti-aliasing*/
    break;
          } /* --- end-of-switch(aaalgorithm) --- */
       /* ---
        * emit aalookup() pattern# counts/percents diagnostics
        * ---------------------------------------------------- */
       if ( !isquery && msgfp!=NULL && msglevel>=99 ) { /*emit patternnumcounts*/
        int pcount0=0, pcount1=0; /* init total w,b center counts */
        for ( ipattern=1; ipattern<=51; ipattern++ ) { /*each possible pattern*/
         if ( ipattern > 1 ) /* ignore all-white squares */
          pcount0 += patternnumcount0[ipattern];  /* bump total white centers */
         pcount1 += patternnumcount1[ipattern]; } /* bump total black centers */
        if ( pcount0+pcount1 > 0 ) /* have pcounts (using aalookup) */
         fprintf(msgfp, "  aalookup() patterns excluding#1 white"
         " (%%'s are in tenths of a percent)...\n");
        for ( ipattern=1; ipattern<=51; ipattern++ ) { /*each possible pattern*/
         int tot = patternnumcount0[ipattern] + patternnumcount1[ipattern];
         if ( tot > 0 ) /* this pattern occurs in image */
          fprintf(msgfp,
          "  pattern#%2d: %7d(%6.2f%%) +%7d(%6.2f%%) =%7d(%6.2f%%)\n",
          ipattern, patternnumcount0[ipattern],  (ipattern<=1? 999.99:
          1000.*((double)patternnumcount0[ipattern])/((double)pcount0)),
          patternnumcount1[ipattern],
          1000.*((double)patternnumcount1[ipattern])/((double)pcount1),
          tot,  (ipattern<=1? 999.99:
          1000.*((double)tot)/((double)(pcount0+pcount1))) ); }
         if ( pcount0+pcount1 > 0 ) /* true when using aalookup() */
          fprintf(msgfp,
          "all patterns: %7d          +%7d          =%7d  total pixels\n",
          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 */
   ncolors = 2; } /* and reset for black&white */    ncolors = 2; } /* and reset for black&white */
       } /* --- end-of-if(isaa) --- */        } /* --- end-of-if(isaa) --- */
        if ( isaa && ispbmpgm && ptype>1 ) { /* -g2 switch  */
         raster pbm_raster; /*construct arg for write_pbmpgm()*/
         pbm_raster.width  = raster_width;  pbm_raster.height = raster_height;
         pbm_raster.pixsz  = 8;  pbm_raster.pixmap = (pixbyte *)bytemap_raster;
         type_pbmpgm(&pbm_raster,ptype,pbm_outfile); } /*write grayscale file*/
     } /* --- end-of-if(isaa) --- */      } /* --- end-of-if(isaa) --- */
   } /* --- end-of-if(isaa) --- */    } /* --- end-of-if(isaa) --- */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
Line 10375  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 10392  if ( (!isquery||isqlogging) || msglevel Line 16156  if ( (!isquery||isqlogging) || msglevel
 emit xbitmap or gif image, and exit  emit xbitmap or gif image, and exit
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 if (  isquery /* called from browser (usual) */  if (  isquery /* called from browser (usual) */
 ||    isdumpimage /* or to emit dump of image */  ||    (isdumpimage && !ispbmpgm) /* or to emit gif dump of image */
 ||    msglevel >= 99 ) /* or for debugging */  ||    msglevel >= 99 ) /* or for debugging */
  {   {
  int  igray = 0; /* grayscale index */   int  igray = 0; /* grayscale index */
Line 10400  if (  isquery    /* called from browser Line 16164  if (  isquery    /* called from browser
  /* ------------------------------------------------------------------------   /* ------------------------------------------------------------------------
  emit GIF image   emit GIF image
  ------------------------------------------------------------------------- */   ------------------------------------------------------------------------- */
     /* --- don't use memory buffer if outout file given --- */
     if ( gif_outfile != NULL ) isinmemory = 0; /* reset memory buffer flag */
     /* --- construct contenttype[] buffer containing mime headers --- */
     if ( 1 ) { /* always construct buffer */
       sprintf( contenttype, "Cache-Control: max-age=%d\n", maxage );
       /*sprintf(contenttype+strlen(contenttype),
          "Expires: Fri, 31 Oct 2003 23:59:59 GMT\n" );*/
       /*sprintf(contenttype+strlen(contenttype),
          "Last-Modified: Wed, 15 Oct 2003 01:01:01 GMT\n");*/
       if ( abs(valign) < 999 ) /* have Vertical-Align: header info*/
         sprintf( contenttype+strlen(contenttype),
          "Vertical-Align: %d\n", valign );
       sprintf( contenttype+strlen(contenttype),
         "Content-type: image/gif\n\n" ); }
   /* --- emit mime content-type line --- */    /* --- emit mime content-type line --- */
   if ( !isdumpimage /* don't mix ascii with image dump */    if ( isemitcontenttype /* content-type lines wanted */
   &&   !iscaching ) /* already emitted if caching */    &&   !isdumpimage /* don't mix ascii with image dump */
     { fprintf( stdout, "Cache-Control: max-age=%d\n",maxage );    &&   !isinmemory /* done below if in memory */
       /*fprintf( stdout, "Expires: Fri, 31 Oct 2003 23:59:59 GMT\n" );*/    &&   !iscaching ) /* done by emitcache() if caching */
       /*fprintf( stdout, "Last-Modified: Wed, 15 Oct 2003 01:01:01 GMT\n" );*/      { fputs(contenttype,stdout); } /* emit content-type: header buffer*/
       fprintf( stdout, "Content-type: image/gif\n\n" ); }    /* --- write output to memory buffer, possibly for testing --- */
     if ( isinmemory /* want gif written to memory */
     ||   isdumpbuffer ) /*or dump memory buffer for testing*/
      if ( gif_outfile == NULL ) /* and don't already have a file */
       { *gif_buffer = '\000'; /* init buffer as empty string */
         memset(gif_buffer,0,MAXGIFSZ); /* zero out buffer */
         gif_outfile = gif_buffer; /* and point outfile to buffer */
         if ( isdumpbuffer ) /* buffer dump test requested */
    isdumpbuffer = 999; } /* so signal dumping to buffer */
   /* --- 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);        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 */
       gif_outfile = (char *)NULL; } /* emit images to stdout */        isdumpbuffer = 0; /* reset isdumpbuffer signal */
         if ( isquery ) isinmemory = 1; /* force in-memory image generation*/
         if ( isinmemory ) { /* using memory buffer */
    gif_outfile = gif_buffer; /* emit images to memory buffer */
    *gif_outfile = '\000'; } /* empty string signals buffer */
         else { /* or */
    gif_outfile = (char *)NULL; /* emit images to stdout */
    if ( isemitcontenttype ) { /* content-type lines wanted */
     fprintf( stdout, "Cache-Control: max-age=%d\n",maxage );
     fprintf( stdout, "Content-type: image/gif\n\n" ); } }
       } /* --- end-of-while(1) --- */
   GIF_SetColor(0,bgred,bggreen,bgblue); /* background white if all 255 */    GIF_SetColor(0,bgred,bggreen,bgblue); /* background white if all 255 */
   if ( !isaa ) /* just b&w if not anti-aliased */    if ( !isaa ) /* just b&w if not anti-aliased */
     { GIF_SetColor(1,fgred,fggreen,fgblue); /* foreground black if all 0 */      { GIF_SetColor(1,fgred,fggreen,fgblue); /* foreground black if all 0 */
Line 10441  if (  isquery    /* called from browser Line 16238  if (  isquery    /* called from browser
   /* --- emit compressed gif image (to stdout or cache file) --- */    /* --- emit compressed gif image (to stdout or cache file) --- */
   GIF_CompressImage(0, 0, -1, -1, GetPixel); /* emit gif */    GIF_CompressImage(0, 0, -1, -1, GetPixel); /* emit gif */
   GIF_Close(); /* close file */    GIF_Close(); /* close file */
   /* --- may need to emit image from cached file --- */    if ( msgfp!=NULL && msglevel>=9 )
   if ( isquery && iscaching ) /* caching enabled */      { fprintf(msgfp,"main> created gifSize=%d\n", gifSize);
     emitcache(cachefile); /* cached image (hopefully) emitted*/        fflush(msgfp); }
     /* --- may need to emit image from cached file or from memory --- */
     if ( isquery /* have an actual query string */
     ||   isdumpimage /* or dumping image */
     ||   msglevel >= 99 ) { /* or debugging */
     int maxage2 = (isdumpimage?(-1):maxage); /* no headers if dumping image */
      if ( iscaching ) /* caching enabled */
        emitcache(cachefile,maxage2,valign,0); /*emit cached image (hopefully)*/
      else if ( isinmemory ) /* or emit image from memory buffer*/
        emitcache(gif_buffer,maxage2,valign,1); } /*emitted from memory buffer*/
     /* --- for testing, may need to write image buffer to file --- */
     if ( isdumpbuffer > 99 ) /* gif image in memory buffer */
      if ( gifSize > 0 ) /* and it's not an empty buffer */
       { FILE *dumpfp = fopen("mimetex.gif","wb"); /* dump to mimetex.gif */
         if ( dumpfp != NULL ) /* file opened successfully */
    { fwrite(gif_buffer,sizeof(unsigned char),gifSize,dumpfp); /*write*/
     fclose(dumpfp); } /* and close file */
       } /* --- end-of-if(isdumpbuffer>99) --- */
  #else   #else
  /* ------------------------------------------------------------------------   /* ------------------------------------------------------------------------
  emit mime XBITMAP image   emit mime XBITMAP image
Line 10453  if (  isquery    /* called from browser Line 16267  if (  isquery    /* called from browser
  } /* --- end-of-if(isquery) --- */   } /* --- end-of-if(isquery) --- */
 /* --- exit --- */  /* --- exit --- */
 end_of_job:  end_of_job:
   if ( bytemap_raster != NULL ) free(bytemap_raster); /*free bytemap_raster*/    if ( !isss ) /*bytemap raster in sp for supersamp*/
      if ( bytemap_raster != NULL ) free(bytemap_raster);/*free bytemap_raster*/
   if (colormap_raster != NULL )free(colormap_raster); /*and colormap_raster*/    if (colormap_raster != NULL )free(colormap_raster); /*and colormap_raster*/
     if ( 0 && gif_buffer != NULL ) free(gif_buffer); /* free malloced buffer */
     if ( 1 && sp != NULL ) delete_subraster(sp); /* and free expression */
   if ( msgfp != NULL /* have message/log file open */    if ( msgfp != NULL /* have message/log file open */
   &&   msgfp != stdout ) /* and it's not stdout */    &&   msgfp != stdout ) /* and it's not stdout */
    { fprintf(msgfp,"mimeTeX> successful end-of-job at %s\n",timestamp());     { fprintf(msgfp,"mimeTeX> successful end-of-job at %s\n",
          timestamp(TZDELTA,0));
      fprintf(msgfp,"%s\n",dashes); /* so log separator line */       fprintf(msgfp,"%s\n",dashes); /* so log separator line */
      fclose(msgfp); } /* and close logfile */       fclose(msgfp); } /* and close logfile */
   exit ( 0 );    /* --- dump memory leaks in debug window if in MS VC++ debug mode --- */
     #if defined(_CRTDBG_MAP_ALLOC)
       _CrtDumpMemoryLeaks();
     #endif
     /* --- exit() if not running as Windows DLL (see CreateGifFromEq()) --- */
     #if !defined(_USRDLL)
       if ( errorstatus == 0 ) /*user doesn't want errors signalled*/
         exitstatus = 0; /* so reset error status */
       exit ( exitstatus );
     #endif
 } /* --- end-of-function main() --- */  } /* --- end-of-function main() --- */
   
 /* ==========================================================================  /* ==========================================================================
  * Function: isstrstr ( char *string, char *snippets, int iscase )   * Function: CreateGifFromEq ( expression, gifFileName )
  * Purpose: determine whether any substring of 'string'   * Purpose: shortcut method to create GIF file for expression,
  * matches any of the comma-separated list of 'snippets',   * with antialising and all other capabilities
  * ignoring case if iscase=0.   * --------------------------------------------------------------------------
  * --------------------------------------------------------------------------   * Arguments: expression (I) char *ptr to null-terminated string
  * Arguments: string (I) char * containing null-terminated   * containing LaTeX expression to be rendred
  * string that will be searched for   * gifFileName (I) char *ptr to null-terminated string
  * any one of the specified snippets   * containing name of output gif file
  * snippets (I) char * containing null-terminated,   * --------------------------------------------------------------------------
  * comma-separated list of snippets   * Returns: ( int ) exit value from main (0 if successful)
  * to be searched for in string   * --------------------------------------------------------------------------
  * iscase (I) int containing 0 for case-insensitive   * Notes:     o This function is the entry point when mimeTeX is built
  * comparisons, or 1 for case-sensitive   * as a Win32 DLL rather then a standalone app or CGI
  * --------------------------------------------------------------------------   *      o Contributed to mimeTeX by Shital Shah.  See his homepage
  * Returns: ( int ) 1 if any snippet is a substring of   *  http://www.shitalshah.com
  * string, 0 if not   *      o Shital discusses the mimeTeX Win32 DLL project at
  * --------------------------------------------------------------------------   *  http://www.codeproject.com/dotnet/Eq2Img.asp
  * Notes:     o   * and you can download his latest code from
  * ======================================================================= */   *  http://www.shitalshah.com/dev/eq2img_all.zip
    * ======================================================================= */
   /* --- include function to expose Win32 DLL to outside world --- */
   #if defined(_USRDLL)
     extern _declspec(dllexport)int _cdecl
    CreateGifFromEq ( char *expression, char *gifFileName );
   #endif
 /* --- entry point --- */  /* --- entry point --- */
 int isstrstr ( char *string, char *snippets, int iscase )  int CreateGifFromEq ( char *expression, char *gifFileName )
 {  {
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 Allocations and Declarations  Allocations and Declarations
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 int status = 0; /*1 if any snippet found in string*/  int main(); /* main() akways returns an int */
 char snip[99], *snipptr = snippets, /* munge through each snippet */  /* --- set constants --- */
  delim = ',', *delimptr = NULL; /* separated by delim's */  int argc = 4; /* count of args supplied to main() */
 char stringcp[999], *cp = stringcp; /*maybe lowercased copy of string*/  char *argv[5] = /* command line args to run with -e option */
 /* -------------------------------------------------------------------------    { "MimeTeXWin32DLL", "-e", /* constant args */
 initialization      /*gifFileName, expression,*/ NULL, NULL, NULL };
 -------------------------------------------------------------------------- */  /* --- set argv[]'s not computable at load time --- */
 /* --- arg check --- */  argv[2] = gifFileName; /* args are -e gifFileName */
 if ( string==NULL || snippets==NULL ) goto end_of_job; /* missing arg */  argv[3] = expression; /* and now  -e gifFileName expression */
 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  Run mimeTeX in command-line mode with -e (export) option, and then return
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 while ( snipptr != NULL ) /* while we still have snippets */  return main ( argc, argv
   {    #ifdef DUMPENVP
   /* --- extract next snippet --- */      , NULL
   if ( (delimptr = strchr(snipptr,delim)) /* locate next comma delim */    #endif
   ==   NULL ) /*not found following last snippet*/   ) ;
     { strcpy(snip,snipptr); /* local copy of last snippet */  } /* --- end-of-function CreateGifFromEq() --- */
       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 10587  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.  
  * ======================================================================= */  
 /* --- entry point --- */  
 int unescape_url(char *url, int isescape) {  
     int x=0,y=0,prevescape=0,gotescape=0;  
     char x2c();  
     static char *hex="0123456789ABCDEFabcdef";  
     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 10669  char *value = NULL;   /* getenv(name) to Line 16427  char *value = NULL;   /* getenv(name) to
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 Log each variable  Log each variable
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 fprintf(fp,"%s\n",timestamp()); /* emit timestamp before first var */  fprintf(fp,"%s\n",timestamp(TZDELTA,0)); /*emit timestamp before first var*/
 if ( message != NULL ) /* optional message supplied */  if ( message != NULL ) /* optional message supplied */
  fprintf(fp,"  MESSAGE = %s\n",message); /* emit caller-supplied message */   fprintf(fp,"  MESSAGE = %s\n",message); /* emit caller-supplied message */
 if ( logvars != (logdata *)NULL ) /* have logvars */  if ( logvars != (logdata *)NULL ) /* have logvars */
Line 10685  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 )   * Function: emitcache ( cachefile, maxage, valign, isbuffer )
  * Purpose: dumps bytes from cachefile to stdout   * Purpose: dumps bytes from cachefile to stdout
  * --------------------------------------------------------------------------   * --------------------------------------------------------------------------
  * Arguments: cachefile (I) pointer to null-terminated char string   * Arguments: cachefile (I) pointer to null-terminated char string
  * containing full path to file to be dumped   * containing full path to file to be dumped,
    * or contains buffer of bytes to be dumped
    * maxage (I) int containing maxage, in seconds, for
    * http header, or -1 to not emit headers
    * valign (I) int containing Vertical-Align:, in pixels,
    * for http header, or <= -999 to not emit
    * isbuffer (I) 1 if cachefile is buffer of bytes to be
    * dumped
  * --------------------------------------------------------------------------   * --------------------------------------------------------------------------
  * Returns: ( int ) #bytes dumped (0 signals error)   * Returns: ( int ) #bytes dumped (0 signals error)
  * --------------------------------------------------------------------------   * --------------------------------------------------------------------------
  * Notes:     o   * Notes:     o
  * ======================================================================= */   * ======================================================================= */
 /* --- entry point --- */  /* --- entry point --- */
 int emitcache ( char *cachefile )  int emitcache ( char *cachefile, int maxage, int valign, int isbuffer )
 {  {
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 Allocations and Declarations  Allocations and Declarations
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 FILE *cacheptr = fopen(cachefile,"rb"), /*open cachefile for binary read*/  int nbytes=gifSize, readcachefile(); /* read cache file */
  *emitptr = stdout; /* emit cachefile to stdout */  FILE *emitptr = stdout; /* emit cachefile to stdout */
 unsigned char buffer[64]; /* characters from cachefile */  unsigned char buffer[MAXGIFSZ+1]; /* bytes from cachefile */
 int buflen = 32, /* #bytes we try to read from cache*/  unsigned char *buffptr = buffer; /* ptr to buffer */
  nread = 0, /* #bytes actually read */  int isvalign = (abs(valign)<999?1:0); /* true to emit Vertical-Align: */
  nbytes = 0; /* total #bytes emitted */  int iscontenttypecached = iscachecontenttype; /*true if headers cached*/
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 initialization  initialization
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 /* --- check that files opened okay --- */  /* --- check that files opened okay --- */
 if ( cacheptr == (FILE *)NULL /* failed to open cachefile */  if ( emitptr == (FILE *)NULL ) /* failed to open emit file */
 ||   emitptr == (FILE *)NULL ) /* or failed to open emit file */  
   goto end_of_job; /* so return 0 bytes to caller */    goto end_of_job; /* so return 0 bytes to caller */
 /* --- set stdout to binary mode (for Windows) --- */  /* --- read the file if necessary --- */
   if ( isbuffer ) { /* cachefile is buffer */
     buffptr = (unsigned char *)cachefile; /* so reset buffer pointer */
     iscontenttypecached = 0; } /* and iscontenttypecached flag */
   else { /* cachefile is file name */
     if ( (nbytes = readcachefile(cachefile,buffer)) /* read the file */
     < 1 ) goto end_of_job; } /* quit if file not read */
   /* --- first emit http headers if requested --- */
   if ( isemitcontenttype /* content-type lines enabled */
   &&   !iscontenttypecached /* and not in cached image */
   &&   maxage >= 0 ) /* caller wants http headers */
    { /* --- emit mime content-type line --- */
      fprintf( emitptr, "Cache-Control: max-age=%d\n",maxage );
      fprintf( emitptr, "Content-Length: %d\n",nbytes );
      if ( isvalign ) /* Vertical-Align: header wanted */
        fprintf( emitptr, "Vertical-Align: %d\n",valign );
      fprintf( emitptr, "Content-type: image/gif\n\n" ); }
   /* -------------------------------------------------------------------------
   set stdout to binary mode (for Windows)
   -------------------------------------------------------------------------- */
 /* emitptr = fdopen(STDOUT_FILENO,"wb"); */  /* doesn't work portably, */  /* emitptr = fdopen(STDOUT_FILENO,"wb"); */  /* doesn't work portably, */
 #ifdef WINDOWS /* so instead... */  #ifdef WINDOWS /* so instead... */
   #ifdef HAVE_SETMODE /* prefer (non-portable) setmode() */    #ifdef HAVE_SETMODE /* prefer (non-portable) setmode() */
Line 10732  if ( cacheptr == (FILE *)NULL  /* failed Line 16516  if ( cacheptr == (FILE *)NULL  /* failed
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 emit bytes from cachefile  emit bytes from cachefile
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
   /* --- write bytes to stdout --- */
   if ( fwrite(buffptr,sizeof(unsigned char),nbytes,emitptr) /* write buffer */
   <    nbytes ) /* failed to write all bytes */
     nbytes = 0; /* reset total count to 0 */
   end_of_job:
     return ( nbytes ); /* back with #bytes emitted */
   } /* --- end-of-function emitcache() --- */
   
   
   /* ==========================================================================
    * Function: readcachefile ( cachefile, buffer )
    * Purpose: read cachefile into buffer
    * --------------------------------------------------------------------------
    * Arguments: cachefile (I) pointer to null-terminated char string
    * containing full path to file to be read
    * buffer (O) pointer to unsigned char string
    * returning contents of cachefile
    * (max 64000 bytes)
    * --------------------------------------------------------------------------
    * Returns: ( int ) #bytes read (0 signals error)
    * --------------------------------------------------------------------------
    * Notes:     o
    * ======================================================================= */
   /* --- entry point --- */
   int readcachefile ( char *cachefile, unsigned char *buffer )
   {
   /* -------------------------------------------------------------------------
   Allocations and Declarations
   -------------------------------------------------------------------------- */
   FILE *cacheptr = fopen(cachefile,"rb"); /*open cachefile for binary read*/
   unsigned char cachebuff[64]; /* bytes from cachefile */
   int buflen = 32, /* #bytes we try to read from file */
    nread = 0, /* #bytes actually read from file */
    maxbytes = MAXGIFSZ, /* max #bytes returned in buffer */
    nbytes = 0; /* total #bytes read */
   /* -------------------------------------------------------------------------
   initialization
   -------------------------------------------------------------------------- */
   /* --- check that files opened okay --- */
   if ( cacheptr == (FILE *)NULL ) goto end_of_job; /*failed to open cachefile*/
   /* --- check that output buffer provided --- */
   if ( buffer == (unsigned char *)NULL ) goto end_of_job; /* no buffer */
   /* -------------------------------------------------------------------------
   read bytes from cachefile
   -------------------------------------------------------------------------- */
 while ( 1 )  while ( 1 )
   {    {
   /* --- read bytes from cachefile --- */    /* --- read bytes from cachefile --- */
   nread = fread(buffer,sizeof(unsigned char),buflen,cacheptr); /* read */    nread = fread(cachebuff,sizeof(unsigned char),buflen,cacheptr); /* read */
     if ( nbytes + nread > maxbytes ) /* block too big for buffer */
       nread = maxbytes - nbytes; /* so truncate it */
   if ( nread < 1 ) break; /* no bytes left in cachefile */    if ( nread < 1 ) break; /* no bytes left in cachefile */
   /* --- write bytes to stdout --- */    /* --- store bytes in buffer --- */
   if ( fwrite(buffer,sizeof(unsigned char),nread,emitptr) /* write buffer */    memcpy(buffer+nbytes,cachebuff,nread); /* copy current block to buffer */
   <    nread) /* failed to write all bytes */    /* --- ready to read next block --- */
     { nbytes = 0; /* reset total count to 0 */  
       goto end_of_job; } /* and signal error to caller */  
   nbytes += nread; /* bump total #bytes emitted */    nbytes += nread; /* bump total #bytes emitted */
   if ( nread < buflen ) break; /* no bytes left in cachefile */    if ( nread < buflen ) break; /* no bytes left in cachefile */
     if ( nbytes >= maxbytes ) break; /* avoid buffer overflow */
   } /* --- end-of-while(1) --- */    } /* --- end-of-while(1) --- */
 end_of_job:  end_of_job:
   if ( cacheptr != NULL ) fclose(cacheptr); /* close file if opened */    if ( cacheptr != NULL ) fclose(cacheptr); /* close file if opened */
   return ( nbytes ); /* back with #bytes emitted */    return ( nbytes ); /* back with #bytes emitted */
 } /* --- end-of-function emitcache() --- */  } /* --- 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 )
Line 10976  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 10994  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.1  
changed lines
  Added in v.1.5


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