Annotation of loncom/cgi/mimeTeX/mimetex.c, revision 1.1

1.1     ! albertel    1: /****************************************************************************
        !             2:  *
        !             3:  * Copyright(c) 2002-2005, John Forkosh Associates, Inc. All rights reserved.
        !             4:  * --------------------------------------------------------------------------
        !             5:  * This file is part of mimeTeX, which is free software. You may redistribute
        !             6:  * and/or modify it under the terms of the GNU General Public License,
        !             7:  * version 2 or later, as published by the Free Software Foundation.
        !             8:  *      MimeTeX is distributed in the hope that it will be useful, but
        !             9:  * WITHOUT ANY WARRANTY, not even the implied warranty of MERCHANTABILITY.
        !            10:  * See the GNU General Public License for specific details.
        !            11:  *      By using mimeTeX, you warrant that you have read, understood and
        !            12:  * agreed to these terms and conditions, and that you possess the legal
        !            13:  * right and ability to enter into this agreement and to use mimeTeX
        !            14:  * in accordance with it.
        !            15:  *      Your mimeTeX distribution should contain a copy of the GNU General
        !            16:  * Public License.  If not, write to the Free Software Foundation, Inc.,
        !            17:  * 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA,
        !            18:  * or point your browser to  http://www.gnu.org/licenses/gpl.html
        !            19:  * --------------------------------------------------------------------------
        !            20:  *
        !            21:  * Purpose:   o	MimeTeX, licensed under the gpl, lets you easily embed
        !            22:  *		LaTeX math in your html pages.  It parses a LaTeX math
        !            23:  *		expression and immediately emits the corresponding gif
        !            24:  *		image, rather than the usual TeX dvi.  And mimeTeX is an
        !            25:  *		entirely separate little program that doesn't use TeX or
        !            26:  *		its fonts in any way.  It's just one cgi that you put in
        !            27:  *		your site's cgi-bin/ directory, with no other dependencies.
        !            28:  *		So mimeTeX is very easy to install.  And it's equally easy
        !            29:  *		to use.  Just place an html <img> tag in your document
        !            30:  *		wherever you want to see the corresponding LaTeX expression.
        !            31:  *		For example,
        !            32:  *		 <img src="../cgi-bin/mimetex.cgi?\int_{-\infty}^xe^{-t^2}dt"
        !            33:  *		  alt="" border=0 align=middle>
        !            34:  *		immediately generates the corresponding gif image on-the-fly,
        !            35:  *		displaying the rendered expression wherever you put that
        !            36:  *		<img> tag.  MimeTeX doesn't need intermediate dvi-to-gif
        !            37:  *		conversion, and it doesn't clutter up your filesystem with
        !            38:  *		separate little gif files for each converted expression.
        !            39:  *		There's also no inherent need to repeatedly write the
        !            40:  *		cumbersome <img> tag illustrated above.  You can write
        !            41:  *		your own custom tags, or write a wrapper script around
        !            42:  *		mimeTeX to simplify the necessary notation.
        !            43:  *
        !            44:  * Functions:	===================== Raster Functions ======================
        !            45:  *	PART2	--- raster constructor functions ---
        !            46:  *		new_raster(width,height,pixsz)   allocation (and constructor)
        !            47:  *		new_subraster(width,height,pixsz)allocation (and constructor)
        !            48:  *		new_chardef()                         allocate chardef struct
        !            49:  *		delete_raster(rp)        deallocate raster (rp =  raster ptr)
        !            50:  *		delete_subraster(sp)  deallocate subraster (sp=subraster ptr)
        !            51:  *		delete_chardef(cp)      deallocate chardef (cp = chardef ptr)
        !            52:  *		--- primitive (sub)raster functions ---
        !            53:  *		rastcpy(rp)                           allocate new copy of rp
        !            54:  *		subrastcpy(sp)                        allocate new copy of sp
        !            55:  *		rastrot(rp)         new raster rotated right 90 degrees to rp
        !            56:  *		rastput(target,source,top,left,isopaque)  overlay src on trgt
        !            57:  *		rastcompose(sp1,sp2,offset2,isalign,isfree) sp2 on top of sp1
        !            58:  *		rastcat(sp1,sp2,isfree)                  concatanate sp1||sp2
        !            59:  *		rastack(sp1,sp2,base,space,iscenter,isfree)stack sp2 atop sp1
        !            60:  *		rastile(tiles,ntiles)      create composite raster from tiles
        !            61:  *		rastsquash(sp1,sp2,xmin,ymin)    calc #squash pixels sp1||sp2
        !            62:  *		--- raster "drawing" functions ---
        !            63:  *		accent_subraster(accent,width,height)       draw \hat\vec\etc
        !            64:  *		arrow_subraster(width,height,drctn,isBig)    left/right arrow
        !            65:  *		uparrow_subraster(width,height,drctn,isBig)     up/down arrow
        !            66:  *		rule_raster(rp,top,left,width,height,type)    draw rule in rp
        !            67:  *		line_raster(rp,row0,col0,row1,col1,thickness) draw line in rp
        !            68:  *		line_recurse(rp,row0,col0,row1,col1,thickness)   recurse line
        !            69:  *		circle_raster(rp,row0,col0,row1,col1,thickness,quads) ellipse
        !            70:  *		circle_recurse(rp,row0,col0,row1,col1,thickness,theta0,theta1)
        !            71:  *		bezier_raster(rp,r0,c0,r1,c1,rt,ct)   draw bezier recursively
        !            72:  *		border_raster(rp,ntop,nbot,isline,isfree)put border around rp
        !            73:  *		--- raster (and chardef) output functions ---
        !            74:  *		type_raster(rp,fp)       emit ascii dump of rp on file ptr fp
        !            75:  *		type_bytemap(bp,grayscale,width,height,fp) dump bytemap on fp
        !            76:  *		xbitmap_raster(rp,fp)           emit mime xbitmap of rp on fp
        !            77:  *		cstruct_chardef(cp,fp,col1)         emit C struct of cp on fp
        !            78:  *		cstruct_raster(rp,fp,col1)          emit C struct of rp on fp
        !            79:  *		hex_bitmap(rp,fp,col1,isstr)emit hex dump of rp->pixmap on fp
        !            80:  *		--- ancillary output functions ---
        !            81:  *		emit_string(fp,col1,string,comment) emit string and C comment
        !            82:  *		====================== Font Functions =======================
        !            83:  *		--- font lookup functions ---
        !            84:  *		get_symdef(symbol)             returns mathchardef for symbol
        !            85:  *		get_chardef(symdef,size)      returns chardef for symdef,size
        !            86:  *		get_charsubraster(symdef,size)  wrap subraster around chardef
        !            87:  *		--- ancillary font functions ---
        !            88:  *		get_baseline(gfdata)       determine baseline (in our coords)
        !            89:  *		get_delim(symbol,height,family) delim just larger than height
        !            90:  *		make_delim(symbol,height) construct delim exactly height size
        !            91:  *		================= Tokenize/Parse Functions ==================
        !            92:  *		texchar(expression,chartoken)  retruns next char or \sequence
        !            93:  *		texsubexpr(expr,subexpr,maxsubsz,left,right,isescape,isdelim)
        !            94:  *		texscripts(expression,subscript,superscript,which)get scripts
        !            95:  *		--- ancillary parse functions ---
        !            96:  *		isbrace(expression,braces,isescape)   check for leading brace
        !            97:  *		preamble(expression,size,subexpr)              parse preamble
        !            98:  *		mimeprep(expression) preprocessor converts \left( to \(, etc.
        !            99:  *		strchange(nfirst,from,to)   change nfirst chars of from to to
        !           100:  *		strreplace(string,from,to,nreplace)  change from to to in str
        !           101:  *		strtexchr(string,texchr)                find texchr in string
        !           102:  *		findbraces(expression,command)    find opening { or closing }
        !           103:  *	PART3	=========== Rasterize an Expression (recursively) ===========
        !           104:  *		--- here's the primary entry point for all of mimeTeX ---
        !           105:  *		rasterize(expression,size)     parse and rasterize expression
        !           106:  *		--- explicitly called handlers that rasterize... ---
        !           107:  *		rastparen(subexpr,size,basesp)          parenthesized subexpr
        !           108:  *		rastlimits(expression,size,basesp)    dispatch super/sub call
        !           109:  *		rastscripts(expression,size,basesp) super/subscripted exprssn
        !           110:  *		rastdispmath(expression,size,sp)      scripts for displaymath
        !           111:  *		--- table-driven handlers that rasterize... ---
        !           112:  *		rastleft(expression,size,basesp,ildelim,arg2,arg3)\left\right
        !           113:  *		rastflags(expression,size,basesp,flag,value,arg3)    set flag
        !           114:  *		rastspace(expression,size,basesp,width,isfill,isheight)\,\:\;
        !           115:  *		rastnewline(expression,size,basesp,arg1,arg2,arg3)         \\
        !           116:  *		rastarrow(expression,size,basesp,width,height,drctn) \longarr
        !           117:  *		rastuparrow(expression,size,basesp,width,height,drctn)up/down
        !           118:  *		rastoverlay(expression,size,basesp,overlay,arg2,arg3)    \not
        !           119:  *		rastfrac(expression,size,basesp,isfrac,arg2,arg3) \frac \atop
        !           120:  *		rastackrel(expression,size,basesp,base,arg2,arg3)   \stackrel
        !           121:  *		rastmathfunc(expression,size,basesp,base,arg2,arg3) \lim,\etc
        !           122:  *		rastsqrt(expression,size,basesp,arg1,arg2,arg3)         \sqrt
        !           123:  *		rastaccent(expression,size,basesp,accent,isabove,isscript)
        !           124:  *		rastfont(expression,size,basesp,font,arg2,arg3) \cal{},\scr{}
        !           125:  *		rastbegin(expression,size,basesp,arg1,arg2,arg3)     \begin{}
        !           126:  *		rastarray(expression,size,basesp,arg1,arg2,arg3)       \array
        !           127:  *		rastpicture(expression,size,basesp,arg1,arg2,arg3)   \picture
        !           128:  *		rastline(expression,size,basesp,arg1,arg2,arg3)         \line
        !           129:  *		rastcircle(expression,size,basesp,arg1,arg2,arg3)     \circle
        !           130:  *		rastbezier(expression,size,basesp,arg1,arg2,arg3)     \bezier
        !           131:  *		rastraise(expression,size,basesp,arg1,arg2,arg3)    \raisebox
        !           132:  *		rastrotate(expression,size,basesp,arg1,arg2,arg3)  \rotatebox
        !           133:  *		rastfbox(expression,size,basesp,arg1,arg2,arg3)         \fbox
        !           134:  *		rastinput(expression,size,basesp,arg1,arg2,arg3)       \input
        !           135:  *		rastcounter(expression,size,basesp,arg1,arg2,arg3)   \counter
        !           136:  *		rastnoop(expression,size,basesp,arg1,arg2,arg3) flush \escape
        !           137:  *		--- helper functions for handlers ---
        !           138:  *		rastopenfile(filename,mode)      opens filename[.tex] in mode
        !           139:  *		rastreadfile(filename,tag,value)  read between <tag>...</tag>
        !           140:  *		rastwritefile(filename,tag,value,isstrict)write<tag>...</tag>
        !           141:  *		timestamp( )   formats timestamp string (used by rastcounter)
        !           142:  *		dtoa(d,npts)                  double to comma-separated ascii
        !           143:  *		=== Anti-alias completed raster (lowpass) or symbols (ss) ===
        !           144:  *		aalowpass(rp,bytemap,grayscale)     lowpass grayscale bytemap
        !           145:  *		aapnm(rp,bytemap,grayscale)       lowpass based on pnmalias.c
        !           146:  *		aasupsamp(rp,aa,sf,grayscale)             or by supersampling
        !           147:  *		aacolormap(bytemap,nbytes,colors,colormap)make colors,colormap
        !           148:  *		aaweights(width,height)      builds "canonical" weight matrix
        !           149:  *		aawtpixel(image,ipixel,weights,rotate) weight image at ipixel
        !           150:  *	PART1	========================== Driver ===========================
        !           151:  *		main(argc,argv) parses math expression and emits mime xbitmap
        !           152:  *		isstrstr(string,snippets,iscase)  are any snippets in string?
        !           153:  *		ismonth(month)          is month current month ("jan"-"dec")?
        !           154:  *		unescape_url(url,isescape), x2c(what)   xlate %xx url-encoded
        !           155:  *		logger(fp,msglevel,logvars)        logs environment variables
        !           156:  *		emitcache(cachefile)  read cachefile and emit bytes to stdout
        !           157:  *		md5str(instr)                      md5 hash library functions
        !           158:  *		GetPixel(x,y)           callback function for gifsave library
        !           159:  *
        !           160:  * Source:	mimetex.c  (needs mimetex.h and texfonts.h to compile,
        !           161:  *		and also needs gifsave.c if compiled with -DAA or -DGIF)
        !           162:  *
        !           163:  * --------------------------------------------------------------------------
        !           164:  * Notes      o	See bottom of file for main() driver (and "friends"),
        !           165:  *		and compile as
        !           166:  *		   cc -DAA mimetex.c gifsave.c -lm -o mimetex.cgi
        !           167:  *		to produce an executable that emits gif images with
        !           168:  *		anti-aliasing (see Notes below).  You may also compile
        !           169:  *		   cc -DGIF mimetex.c gifsave.c -lm -o mimetex.cgi
        !           170:  *		to produce an executable that emits gif images without
        !           171:  *		anti-aliasing.  Alternatively, compile mimeTeX as
        !           172:  *		   cc -DXBITMAP mimetex.c -lm -o mimetex.cgi
        !           173:  *		to produce an executable that just emits mime xbitmaps.
        !           174:  *		In either case you'll need mimetex.h and texfonts.h,
        !           175:  *		and with -DAA or -DGIF you'll also need gifsave.c
        !           176:  *	      o	For gif images, the gifsave.c library by Sverre H. Huseby
        !           177:  *		<http://shh.thathost.com> slightly modified by me to allow
        !           178:  *		(a)sending output to stdout and (b)specifying a transparent
        !           179:  *		background color index, is included with mimeTeX,
        !           180:  *		and it's documented in mimetex.html#gifsave .
        !           181:  *	      o	Optional compile-line -D defined symbols are documented
        !           182:  *		in mimetex.html#options .  They include...
        !           183:  *		-DAA
        !           184:  *		    Turns on gif anti-aliasing with default values
        !           185:  *		    (CENTERWT=32, ADJACENTWT=3, CORNERWT=1)
        !           186:  *		    for the following anti-aliasing parameters...
        !           187:  *		-DCENTERWT=n
        !           188:  *		-DADJACENTWT=j
        !           189:  *		-DCORNERWT=k
        !           190:  *		    MimeTeX currently provides a lowpass filtering
        !           191:  *		    algorithm for anti-aliasing, which is applied to the
        !           192:  *		    existing set of bitmap fonts.  This lowpass filter
        !           193:  *		    applies default weights
        !           194:  *				1   3   1
        !           195:  *				3  32   3
        !           196:  *				1   3   1
        !           197:  *		    to neighboring pixels. The defaults weights are
        !           198:  *		    CENTERWT=32, ADJACENTWT=3 and CORNERWT=1,
        !           199:  *		    which you can adjust to control anti-aliasing.
        !           200:  *		    Lower CENTERWT values will blur/spread out lines
        !           201:  *		    while higher values will tend to sharpen lines.
        !           202:  *		    Experimentation is recommended to determine
        !           203:  *		    what value works best for you.
        !           204:  *		-DCACHEPATH=\"path/\"
        !           205:  *		    This option saves each rendered image to a file
        !           206:  *		    in directory  path/  which mimeTeX reads rather than
        !           207:  *		    re-rendering the same image every time it's given
        !           208:  *		    the same LaTeX expression.  Sometimes mimeTeX disables
        !           209:  *		    caching, e.g., expressions containing \input{ } are
        !           210:  *		    re-rendered since the contents of the inputted file
        !           211:  *		    may have changed.  If compiled without -DCACHEPATH
        !           212:  *		    mimeTeX always re-renders expressions.  This usually
        !           213:  *		    isn't too cpu intensive, but if you have unusually
        !           214:  *		    high hit rates then image caching may be helpful.
        !           215:  *			The  path/  is relative to mimetex.cgi, and must
        !           216:  *		    be writable by it.  Files created under  path/  are
        !           217:  *		    named filename.gif, where filename is the 32-character
        !           218:  *		    MD5 hash of the LaTeX expression.
        !           219:  *		-DDISPLAYSIZE=n
        !           220:  *		    By default, operator limits like \int_a^b are rendered
        !           221:  *		    \textstyle at font sizes \normalsize and smaller,
        !           222:  *		    and rendered \displaystyle at font sizes \large and
        !           223:  *		    larger.  This default corresponds to -DDISPLAYSIZE=3,
        !           224:  *		    which you can adjust; e.g., -DDISPLAYSIZE=0 always
        !           225:  *		    defaults to \displaystyle, and 99 (or any large number)
        !           226:  *		    always defaults to \textstyle.  Note that explicit
        !           227:  *		    \textstyle, \displaystyle, \limits or \nolimits
        !           228:  *		    directives in an expression always override
        !           229:  *		    the DISPLAYSIZE default.
        !           230:  *		-NORMALSIZE=n
        !           231:  *		    MimeTeX currently has six font sizes numbered 0-5,
        !           232:  *		    and always starts in NORMALSIZE whose default value
        !           233:  *		    is 2.  Specify -DNORMALSIZE=3 on the compile line if
        !           234:  *		    you prefer mimeTeX to start in default size 3, etc.
        !           235:  *		-DREFERER=\"domain\"   -or-
        !           236:  *		-DREFERER=\"domain1,domain2,etc\"
        !           237:  *		    Blocks mimeTeX requests from unauthorized domains that
        !           238:  *		    may be using your server's mimetex.cgi without permission.
        !           239:  *		    If REFERER is defined, mimeTeX checks for the environment
        !           240:  *		    variable HTTP_REFERER and, if it exists, performs a
        !           241:  *		    case-insensitive test to make sure it contains 'domain'
        !           242:  *		    as a substring.  If given several 'domain's (second form)
        !           243:  *		    then HTTP_REFERER must contain either 'domain1' or
        !           244:  *		    'domain2', etc, as a (case-insensitive) substring.
        !           245:  *		    If HTTP_REFERER fails to contain a substring matching
        !           246:  *		    any of these domain(s), mimeTeX emits an error message
        !           247:  *		    image corresponding to the expression specified by
        !           248:  *		    the  invalid_referer_msg  string defined in main().
        !           249:  *		    Note: if HTTP_REFERER is not an environment variable,
        !           250:  *		    mimeTeX correctly generates the requested expression
        !           251:  *		    (i.e., no referer error).
        !           252:  *		-DWARNINGS=n  -or-
        !           253:  *		-DNOWARNINGS
        !           254:  *		    If an expression submitted to mimeTeX contains an
        !           255:  *		    unrecognzied escape sequence, e.g., "y=x+\abc+1", then
        !           256:  *		    mimeTeX generates a gif image containing an embedded
        !           257:  *		    warning in the form "y=x+[\abc?]+1".  If you want these
        !           258:  *		    warnings suppressed, -DWARNINGS=0 or -DNOWARNINGS tells
        !           259:  *		    mimeTeX to ignore unrecognized symbols, and the rendered
        !           260:  *		    image is "y=x++1" instead.
        !           261:  *		-DWHITE
        !           262:  *		    MimeTeX usually renders black symbols on a white
        !           263:  *		    background.  This option renders white symbols on
        !           264:  *		    a black background instead.
        !           265:  *	      o	See individual function entry points for further comments.
        !           266:  *	      o	The font information in texfonts.h was produced by multiple
        !           267:  *		runs of gfuntype, one run per struct (i.e., one run per font
        !           268:  *		family at a particular size).  See gfuntype.c, and also
        !           269:  *		mimetex.html#fonts, for details.
        !           270:  *	      o	mimetex.c contains library functions implementing a raster
        !           271:  *		datatype, functions to manipulate rasterized .mf fonts
        !           272:  *		(see gfuntype.c which rasterizes .mf fonts), functions
        !           273:  *		to parse LaTeX expressions, etc.  A complete list of
        !           274:  *		mimetex.c functions is above.  See their individual entry
        !           275:  *		points below for further comments.
        !           276:  *		   All these functions eventually belong in several
        !           277:  *		different modules, possibly along the lines suggested
        !           278:  *		by the divisions above.  But until the best decomposition
        !           279:  *		becomes clear, it seems better to keep mimetex.c
        !           280:  *		neatly together, avoiding a bad decomposition that
        !           281:  *		becomes permanent by default.
        !           282:  *	      o	The "main" reusable function is rasterize(),
        !           283:  *		which takes a string like "f(x)=\int_{-\infty}^xe^{-t^2}dt"
        !           284:  *		and returns a (sub)raster representing it as a bit or bytemap.
        !           285:  *		Your application can do anything it likes with this pixel map.
        !           286:  *		MimeTeX just outputs it, either as a mime xbitmap or as a gif.
        !           287:  * --------------------------------------------------------------------------
        !           288:  * Revision History:
        !           289:  * 09/18/02	J.Forkosh	Installation.
        !           290:  * 12/11/02	J.Forkosh	Version 1.00 released.
        !           291:  * 07/04/03	J.Forkosh	Version 1.01 released.
        !           292:  * 10/17/03	J.Forkosh	Version 1.20 released.
        !           293:  * 12/21/03	J.Forkosh	Version 1.30 released.
        !           294:  * 02/01/04	J.Forkosh	Version 1.40 released.
        !           295:  * 10/02/04	J.Forkosh	Version 1.50 released.
        !           296:  * 11/30/04	J.Forkosh	Version 1.60 released.
        !           297:  *
        !           298:  ****************************************************************************/
        !           299: 
        !           300: /* -------------------------------------------------------------------------
        !           301: header files and macros
        !           302: -------------------------------------------------------------------------- */
        !           303: /* --- standard headers --- */
        !           304: #include <stdio.h>
        !           305: #include <stdlib.h>
        !           306: /*#include <unistd.h>*/
        !           307: #include <string.h>
        !           308: #include <ctype.h>
        !           309: #include <math.h>
        !           310: #include <time.h>
        !           311: 
        !           312: /* --- windows-specific header info --- */
        !           313: #ifndef WINDOWS			/* -DWINDOWS not supplied by user */
        !           314:   #if defined(_WIN32) || defined(WIN32) \
        !           315:   ||  defined(DJGPP)		/* try to recognize windows */
        !           316:     #define WINDOWS		/* signal windows */
        !           317:   #endif
        !           318: #endif
        !           319: #ifdef WINDOWS			/* Windows opens stdout in char mode, and */
        !           320:   #include <fcntl.h>		/* precedes every 0x0A with spurious 0x0D.*/
        !           321:   #include <io.h>		/* So emitcache() issues a Win _setmode() */
        !           322: 				/* call to put stdout in binary mode. */
        !           323:   #if defined(_O_BINARY) && !defined(O_BINARY)  /* only have _O_BINARY */
        !           324:     #define O_BINARY _O_BINARY	/* make O_BINARY available, etc... */
        !           325:     #define setmode  _setmode
        !           326:     #define fileno   _fileno
        !           327:   #endif
        !           328:   #if defined(_O_BINARY) || defined(O_BINARY)  /* setmode() now available */
        !           329:     #define HAVE_SETMODE	/* so we'll use setmode() */
        !           330:   #endif
        !           331:   #define ISWINDOWS 1
        !           332: #else
        !           333:   #define ISWINDOWS 0
        !           334: #endif
        !           335: 
        !           336: /* --- check for supersampling or low-pass anti-aliasing --- */
        !           337: #ifdef SS
        !           338:   #define ISSUPERSAMPLING 1
        !           339:   #ifndef AAALGORITHM
        !           340:     #define AAALGORITHM 1		/* default supersampling algorithm */
        !           341:   #endif
        !           342:   #ifndef AA				/* anti-aliasing not explicitly set */
        !           343:     #define AA				/* so define it ourselves */
        !           344:   #endif
        !           345:   #ifndef SSFONTS			/* need supersampling fonts */
        !           346:     #define SSFONTS
        !           347:   #endif
        !           348: #else
        !           349:   #define ISSUPERSAMPLING 0
        !           350:   #ifndef AAALGORITHM
        !           351:     #define AAALGORITHM 2		/* default lowpass algorithm */
        !           352:   #endif
        !           353: #endif
        !           354: 
        !           355: /* --- set aa (and default gif) if any anti-aliasing options specified --- */
        !           356: #if defined(AA) || defined(GIF) || defined(PNG) \
        !           357: ||  defined(CENTERWT) || defined(ADJACENTWT) || defined(CORNERWT) \
        !           358: ||  defined(MINADJACENT) || defined(MAXADJACENT)
        !           359:   #if !defined(GIF) && !defined(AA)	/* aa not explicitly specified */
        !           360:     #define AA				/* so define it ourselves */
        !           361:   #endif
        !           362:   #if !defined(GIF) && !defined(PNG)	/* neither gif nor png specified */
        !           363:     #define GIF				/* so default to gif */
        !           364:   #endif
        !           365: #endif
        !           366: /* --- resolve output option inconsistencies --- */
        !           367: #if defined(XBITMAP)			/* xbitmap supercedes gif and png */
        !           368:   #ifdef AA
        !           369:     #undef AA
        !           370:   #endif
        !           371:   #ifdef GIF
        !           372:     #undef GIF
        !           373:   #endif
        !           374:   #ifdef PNG
        !           375:     #undef PNG
        !           376:   #endif
        !           377: #endif
        !           378: 
        !           379: /* --- decide whether to compile main() --- */
        !           380: #if defined(XBITMAP) || defined(GIF) || defined(PNG)
        !           381:   #define DRIVER			/* driver will be compiled */
        !           382:   /* --- check whether or not to perform http_referer check --- */
        !           383:   #ifndef REFERER			/* all http_referer's allowed */
        !           384:     #define REFERER NULL
        !           385:   #endif
        !           386:   /* --- max query_string length if no http_referer supplied --- */
        !           387:   #ifndef NOREFMAXLEN
        !           388:     #define NOREFMAXLEN 9999		/* default to any length query */
        !           389:   #endif
        !           390: #else
        !           391:   #define NOTEXFONTS			/* texfonts not required */
        !           392: #endif
        !           393: 
        !           394: /* --- application headers --- */
        !           395: #if !defined(NOTEXFONTS) && !defined(TEXFONTS)
        !           396:   #define TEXFONTS			/* to include texfonts.h */
        !           397: #endif
        !           398: #include "mimetex.h"
        !           399: 
        !           400: /* -------------------------------------------------------------------------
        !           401: adjustable default values
        !           402: -------------------------------------------------------------------------- */
        !           403: /* --- anti-aliasing parameters --- */
        !           404: #ifndef	CENTERWT
        !           405:   /*#define CENTERWT 10*/		/* anti-aliasing centerwt default */
        !           406:   /*#define CENTERWT 6*/		/* anti-aliasing centerwt default */
        !           407:   #define CENTERWT 32			/* anti-aliasing centerwt default */
        !           408: #endif
        !           409: #ifndef	ADJACENTWT
        !           410:   /*#define ADJACENTWT 3*/		/* anti-aliasing adjacentwt default*/
        !           411:   /*#define ADJACENTWT 2*/		/* anti-aliasing adjacentwt default*/
        !           412:   #define ADJACENTWT 3			/* anti-aliasing adjacentwt default*/
        !           413: #endif
        !           414: #ifndef	CORNERWT
        !           415:   #define CORNERWT 1			/* anti-aliasing cornerwt default*/
        !           416: #endif
        !           417: #ifndef	MINADJACENT
        !           418:   #define MINADJACENT 6			/*anti-aliasing minadjacent default*/
        !           419: #endif
        !           420: #ifndef	MAXADJACENT
        !           421:   #define MAXADJACENT 8			/*anti-aliasing maxadjacent default*/
        !           422: #endif
        !           423: /* --- variables for anti-aliasing parameters --- */
        !           424: GLOBAL(int,centerwt,CENTERWT);		/*lowpass matrix center pixel wt */
        !           425: GLOBAL(int,adjacentwt,ADJACENTWT);	/*lowpass matrix adjacent pixel wt*/
        !           426: GLOBAL(int,cornerwt,CORNERWT);		/*lowpass matrix corner pixel wt */
        !           427: GLOBAL(int,minadjacent,MINADJACENT);	/* darken if>=adjacent pts black*/
        !           428: GLOBAL(int,maxadjacent,MAXADJACENT);	/* darken if<=adjacent pts black */
        !           429: GLOBAL(int,weightnum,1);		/* font wt, */
        !           430: GLOBAL(int,maxaaparams,4);		/* #entries in table */
        !           431: /* --- parameter values by font weight --- */
        !           432: #define	aaparameters struct aaparameters_struct /* typedef */
        !           433: aaparameters
        !           434:   { int	centerwt;			/* lowpass matrix center   pixel wt*/
        !           435:     int	adjacentwt;			/* lowpass matrix adjacent pixel wt*/
        !           436:     int cornerwt;			/* lowpass matrix corner   pixel wt*/
        !           437:     int	minadjacent;			/* darken if >= adjacent pts black */
        !           438:     int	maxadjacent;			/* darken if <= adjacent pts black */
        !           439:     int fgalias,fgonly,bgalias,bgonly; } ; /* aapnm() params */
        !           440: STATIC aaparameters aaparams[]		/* set params by weight */
        !           441:   #ifdef INITVALS
        !           442:   =
        !           443:   { /* ----------------------------------------------------
        !           444:     centerwt adj corner minadj max  fgalias,only,bgalias,only
        !           445:     ------------------------------------------------------- */
        !           446: 	{ 64,  1,  1,    6,  8,     1,0,0,0 },	/* 0 = light */
        !           447: 	{ CENTERWT,ADJACENTWT,CORNERWT,MINADJACENT,MAXADJACENT,1,0,0,0 },
        !           448: 	{ 8,   1,  1,    5,  8,     1,0,0,0 },	/* 2 = semibold */
        !           449: 	{ 8,   2,  1,    4,  9,     1,0,0,0 }	/* 3 = bold */
        !           450:   } /* --- end-of-aaparams[] --- */
        !           451:   #endif
        !           452:   ;
        !           453: 
        !           454: /* -------------------------------------------------------------------------
        !           455: other variables
        !           456: -------------------------------------------------------------------------- */
        !           457: /* --- black on white background (default), or white on black --- */
        !           458: #ifdef WHITE
        !           459:   #define ISBLACKONWHITE 0		/* white on black background */
        !           460: #else
        !           461:   #define ISBLACKONWHITE 1		/* black on white background */
        !           462: #endif
        !           463: /* --- colors --- */
        !           464: #define	BGRED   (ISBLACKONWHITE?255:0)
        !           465: #define	BGGREEN (ISBLACKONWHITE?255:0)
        !           466: #define	BGBLUE  (ISBLACKONWHITE?255:0)
        !           467: #ifndef	FGRED
        !           468:   #define FGRED   (ISBLACKONWHITE?0:255)
        !           469: #endif
        !           470: #ifndef	FGGREEN
        !           471:   #define FGGREEN (ISBLACKONWHITE?0:255)
        !           472: #endif
        !           473: #ifndef	FGBLUE
        !           474:   #define FGBLUE  (ISBLACKONWHITE?0:255)
        !           475: #endif
        !           476: /* --- "squash" margin (0 means no squashing) --- */
        !           477: #ifndef SQUASHMARGIN
        !           478:   #ifdef NOSQUASH
        !           479:     #define SQUASHMARGIN 0
        !           480:   #else
        !           481:     #define SQUASHMARGIN 3
        !           482:   #endif
        !           483: #endif
        !           484: /* --- textwidth --- */
        !           485: #ifndef TEXTWIDTH
        !           486:   #define TEXTWIDTH (400)
        !           487: #endif
        !           488: /* --- font "combinations" --- */
        !           489: #define	CMSYEX (107)			/* select CMSY10 _or_ CMEX10 */
        !           490: /* --- prefix prepended to all expressions --- */
        !           491: #ifndef	PREFIX
        !           492:   #define PREFIX "\000"			/* default no prepended prefix */
        !           493: #endif
        !           494: /* --- skip argv[]'s preceding ARGSIGNAL when parsing command-line args --- */
        !           495: #ifdef NOARGSIGNAL
        !           496:   #define ARGSIGNAL NULL
        !           497: #endif
        !           498: #ifndef	ARGSIGNAL
        !           499:   #define ARGSIGNAL "++"
        !           500: #endif
        !           501: /* --- security and logging (inhibit message logging, etc) --- */
        !           502: #ifndef	SECURITY
        !           503:   #define SECURITY 999			/* default highest security level */
        !           504: #endif
        !           505: #ifndef	LOGFILE
        !           506:   #define LOGFILE "mimetex.log"		/* default log file */
        !           507: #endif
        !           508: #ifndef	CACHELOG
        !           509:   #define CACHELOG "mimetex.log"	/* default caching log file */
        !           510: #endif
        !           511: #if !defined(NODUMPENVP) && !defined(DUMPENVP)
        !           512:   #define DUMPENVP			/* assume char *envp[] available */
        !           513: #endif
        !           514: /* --- image caching (cache images if given -DCACHEPATH=\"path\") --- */
        !           515: #ifndef CACHEPATH
        !           516:   #define ISCACHING 0			/* no caching */
        !           517:   #define CACHEPATH "\000"		/* same directory as mimetex.cgi */
        !           518: #else
        !           519:   #define ISCACHING 1			/* caching if -DCACHEPATH="path" */
        !           520: #endif
        !           521: /* --- \input paths (prepend prefix if given -DPATHPREFIX=\"prefix\") --- */
        !           522: #ifndef PATHPREFIX
        !           523:   #define PATHPREFIX "\000"		/* paths relative mimetex.cgi */
        !           524: #endif
        !           525: 
        !           526: /* -------------------------------------------------------------------------
        !           527: debugging and logging / error reporting
        !           528: -------------------------------------------------------------------------- */
        !           529: /* --- debugging and error reporting --- */
        !           530: #ifndef	MSGLEVEL
        !           531:   #define MSGLEVEL 1
        !           532: #endif
        !           533: #define	DBGLEVEL 9			/* debugging if msglevel>=DBGLEVEL */
        !           534: #define	LOGLEVEL 3			/* logging if msglevel>=LOGLEVEL */
        !           535: #ifndef FORMLEVEL
        !           536:   #define FORMLEVEL LOGLEVEL		/*msglevel if called from html form*/
        !           537: #endif
        !           538: GLOBAL(int,seclevel,SECURITY);		/* security level */
        !           539: GLOBAL(int,msglevel,MSGLEVEL);		/* message level for verbose/debug */
        !           540: STATIC	FILE *msgfp;			/* output in command-line mode */
        !           541: /* --- embed warnings in rendered expressions, [\xxx?] if \xxx unknown --- */
        !           542: #ifdef WARNINGS
        !           543:   #define WARNINGLEVEL WARNINGS
        !           544: #else
        !           545:   #ifdef NOWARNINGS
        !           546:     #define WARNINGLEVEL 0
        !           547:   #else
        !           548:     #define WARNINGLEVEL 1
        !           549:   #endif
        !           550: #endif
        !           551: GLOBAL(int,warninglevel,WARNINGLEVEL);	/* warning level */
        !           552: 
        !           553: /* -------------------------------------------------------------------------
        !           554: control flags and values
        !           555: -------------------------------------------------------------------------- */
        !           556: GLOBAL(int,recurlevel,0);		/* inc/decremented in rasterize() */
        !           557: GLOBAL(int,scriptlevel,0);		/* inc/decremented in rastlimits() */
        !           558: GLOBAL(int,istext,0);			/* textmode if true,italics=2,bb=3 */
        !           559: GLOBAL(int,isstring ,0);		/*pixmap is ascii string, not raster*/
        !           560: GLOBAL(int,isdisplaystyle,1);		/* displaystyle mode (forced if 2) */
        !           561: GLOBAL(int,ispreambledollars,0);	/* displaystyle mode set by $$...$$ */
        !           562: GLOBAL(int,fontsize,NORMALSIZE);	/* current size */
        !           563: GLOBAL(int,displaysize,DISPLAYSIZE);	/* use \displaystyle when fontsize>=*/
        !           564: GLOBAL(int,shrinkfactor,3);		/* shrinkfactors[fontsize] */
        !           565: GLOBAL(double,unitlength,1.0);		/* #pixels per unit (may be <1.0) */
        !           566: /*GLOBAL(int,textwidth,TEXTWIDTH);*/	/* #pixels across line */
        !           567: GLOBAL(int,squashmargin,SQUASHMARGIN);	/* minimum "squash" margin */
        !           568: GLOBAL(int,issquashdelta,1);		/* true if squashmargin is a delta */
        !           569: GLOBAL(int,istransparent,1);		/*true to set background transparent*/
        !           570: GLOBAL(int,fgred,FGRED);
        !           571:   GLOBAL(int,fggreen,FGGREEN);
        !           572:   GLOBAL(int,fgblue,FGBLUE);		/* fg r,g,b */
        !           573: GLOBAL(int,bgred,BGRED);
        !           574:   GLOBAL(int,bggreen,BGGREEN);
        !           575:   GLOBAL(int,bgblue,BGBLUE);		/* bg r,g,b */
        !           576: GLOBAL(int,isblackonwhite,ISBLACKONWHITE); /*1=black on white,0=reverse*/
        !           577: GLOBAL(char,exprprefix[256],PREFIX);	/* prefix prepended to expressions */
        !           578: GLOBAL(int,aaalgorithm,AAALGORITHM);	/* for lp, 1=aalowpass, 2 =aapnm */
        !           579: GLOBAL(int,fgalias,1);
        !           580:   GLOBAL(int,fgonly,0);
        !           581:   GLOBAL(int,bgalias,0);
        !           582:   GLOBAL(int,bgonly,0);			/* aapnm() params */
        !           583: GLOBAL(int,issupersampling,ISSUPERSAMPLING); /*1=supersampling 0=lowpass*/
        !           584: GLOBAL(int,isss,ISSUPERSAMPLING);	/* supersampling flag for main() */
        !           585: GLOBAL(int,*workingparam,(int *)NULL);	/* working parameter */
        !           586: GLOBAL(subraster,*workingbox,(subraster *)NULL); /*working subraster box*/
        !           587: GLOBAL(int,isreplaceleft,0);		/* true to replace leftexpression */
        !           588: GLOBAL(subraster,*leftexpression,(subraster *)NULL); /*rasterized so far*/
        !           589: GLOBAL(mathchardef,*leftsymdef,NULL);	/* mathchardef for preceding symbol*/
        !           590: GLOBAL(int,iscaching,ISCACHING);	/* true if caching images */
        !           591: GLOBAL(char,cachepath[256],CACHEPATH);	/* relative path to cached files */
        !           592: GLOBAL(char,pathprefix[256],PATHPREFIX); /*prefix for \input,\counter paths*/
        !           593: /*GLOBAL(int,iswindows,ISWINDOWS);*/	/* true if compiled for ms windows */
        !           594: 
        !           595: /* -------------------------------------------------------------------------
        !           596: miscellaneous macros
        !           597: -------------------------------------------------------------------------- */
        !           598: #define	max2(x,y)  ((x)>(y)? (x):(y))	/* larger of 2 arguments */
        !           599: #define	min2(x,y)  ((x)<(y)? (x):(y))	/* smaller of 2 arguments */
        !           600: #define	max3(x,y,z) max2(max2(x,y),(z))	/* largest of 3 arguments */
        !           601: #define	min3(x,y,z) min2(min2(x,y),(z))	/* smallest of 3 arguments */
        !           602: #define absval(x)  ((x)>=0?(x):(-(x)))	/* absolute value */
        !           603: #define	iround(x)  ((int)((x)>=0?(x)+0.5:(x)-0.5)) /* round double to int */
        !           604: #define	dmod(x,y)  ((x)-((y)*((double)((int)((x)/(y)))))) /*x%y for doubles*/
        !           605: #define compress(s,c) if((s)!=NULL)	/* remove embedded c's from s */ \
        !           606: 	{ char *p; while((p=strchr((s),(c)))!=NULL) strcpy(p,p+1); } else
        !           607: #define	slower(s)  if ((s)!=NULL)	/* lowercase all chars in s */ \
        !           608: 	{ char *p=(s); while(*p!='\000'){*p=tolower(*p); p++;} } else
        !           609: 
        !           610: /* ---
        !           611:  * PART2
        !           612:  * ------ */
        !           613: #if !defined(PARTS) || defined(PART2)
        !           614: /* ==========================================================================
        !           615:  * Function:	new_raster ( width, height, pixsz )
        !           616:  * Purpose:	Allocation and constructor for raster.
        !           617:  *		mallocs and initializes memory for width*height pixels,
        !           618:  *		and returns raster struct ptr to caller.
        !           619:  * --------------------------------------------------------------------------
        !           620:  * Arguments:	width (I)	int containing width, in bits,
        !           621:  *				of raster pixmap to be allocated
        !           622:  *		height (I)	int containing height, in bits/scans,
        !           623:  *				of raster pixmap to be allocated
        !           624:  *		pixsz (I)	int containing #bits per pixel, 1 or 8
        !           625:  * --------------------------------------------------------------------------
        !           626:  * Returns:	( raster * )	ptr to allocated and initialized
        !           627:  *				raster struct, or NULL for any error.
        !           628:  * --------------------------------------------------------------------------
        !           629:  * Notes:
        !           630:  * ======================================================================= */
        !           631: /* --- entry point --- */
        !           632: raster	*new_raster ( int width, int height, int pixsz )
        !           633: {
        !           634: /* -------------------------------------------------------------------------
        !           635: Allocations and Declarations
        !           636: -------------------------------------------------------------------------- */
        !           637: raster	*rp = (raster *)NULL;		/* raster ptr returned to caller */
        !           638: pixbyte	*pixmap = NULL;			/* raster pixel map to be malloced */
        !           639: int	nbytes = pixsz*bitmapsz(width,height); /* #bytes needed for pixmap */
        !           640: int	filler = (isstring?' ':0);	/* pixmap filler */
        !           641: int	delete_raster();		/* in case pixmap malloc() fails */
        !           642: int	npadding = (0&&issupersampling?8+256:0); /* padding bytes */
        !           643: /* -------------------------------------------------------------------------
        !           644: allocate and initialize raster struct and embedded bitmap
        !           645: -------------------------------------------------------------------------- */
        !           646: if ( msgfp!=NULL && msglevel>=9999 )
        !           647:   { fprintf(msgfp,"new_raster(%d,%d,%d)> entry point\n",
        !           648:     width,height,pixsz); fflush(msgfp); }
        !           649: /* --- allocate and initialize raster struct --- */
        !           650: rp = (raster *)malloc(sizeof(raster));	/* malloc raster struct */
        !           651: if ( msgfp!=NULL && msglevel>=9999 )
        !           652:   { fprintf(msgfp,"new_raster> rp=malloc(%d) returned (%s)\n",
        !           653:     sizeof(raster),(rp==NULL?"null ptr":"success")); fflush(msgfp); }
        !           654: if ( rp == (raster *)NULL )		/* malloc failed */
        !           655:   goto end_of_job;			/* return error to caller */
        !           656: rp->width = width;			/* store width in raster struct */
        !           657: rp->height = height;			/* and store height */
        !           658: rp->pixsz = pixsz;			/* store #bits per pixel */
        !           659: rp->pixmap = (pixbyte *)NULL;		/* init bitmap as null ptr */
        !           660: /* --- allocate and initialize bitmap array --- */
        !           661: if ( msgfp!=NULL && msglevel>=9999 )
        !           662:   { fprintf(msgfp,"new_raster> calling pixmap=malloc(%d)\n",
        !           663:     nbytes); fflush(msgfp); }
        !           664: if ( nbytes>0 && nbytes<=pixsz*maxraster )  /* fail if width*height too big*/
        !           665:   pixmap = (pixbyte *)malloc(nbytes+npadding); /*bytes for width*height bits*/
        !           666: if ( msgfp!=NULL && msglevel>=9999 )
        !           667:   { fprintf(msgfp,"new_raster> pixmap=malloc(%d) returned (%s)\n",
        !           668:     nbytes,(pixmap==NULL?"null ptr":"success")); fflush(msgfp); }
        !           669: if ( pixmap == (pixbyte *)NULL )	/* malloc failed */
        !           670:   { delete_raster(rp);			/* so free everything */
        !           671:     rp = (raster *)NULL;		/* reset pointer */
        !           672:     goto end_of_job; }			/* and return error to caller */
        !           673: memset((void *)pixmap,filler,nbytes);	/* init bytes to binary 0's or ' 's*/
        !           674: *pixmap = (pixbyte)0;			/* and first byte alwasy 0 */
        !           675: rp->pixmap = pixmap;			/* store ptr to malloced memory */
        !           676: /* -------------------------------------------------------------------------
        !           677: Back to caller with address of raster struct, or NULL ptr for any error.
        !           678: -------------------------------------------------------------------------- */
        !           679: end_of_job:
        !           680:   if ( msgfp!=NULL && msglevel>=9999 )
        !           681:     { fprintf(msgfp,"new_raster(%d,%d,%d)> returning (%s)\n",
        !           682:       width,height,pixsz,(rp==NULL?"null ptr":"success")); fflush(msgfp); }
        !           683:   return ( rp );			/* back to caller with raster */
        !           684: } /* --- end-of-function new_raster() --- */
        !           685: 
        !           686: 
        !           687: /* ==========================================================================
        !           688:  * Function:	new_subraster ( width, height, pixsz )
        !           689:  * Purpose:	Allocate a new subraster along with
        !           690:  *		an embedded raster of width x height.
        !           691:  * --------------------------------------------------------------------------
        !           692:  * Arguments:	width (I)	int containing width of embedded raster
        !           693:  *		height (I)	int containing height of embedded raster
        !           694:  *		pixsz (I)	int containing #bits per pixel, 1 or 8
        !           695:  * --------------------------------------------------------------------------
        !           696:  * Returns:	( subraster * )	ptr to newly-allocated subraster,
        !           697:  *				or NULL for any error.
        !           698:  * --------------------------------------------------------------------------
        !           699:  * Notes:     o	if width or height <=0, embedded raster not allocated
        !           700:  * ======================================================================= */
        !           701: /* --- entry point --- */
        !           702: subraster *new_subraster ( int width, int height, int pixsz )
        !           703: {
        !           704: /* -------------------------------------------------------------------------
        !           705: Allocations and Declarations
        !           706: -------------------------------------------------------------------------- */
        !           707: subraster *sp=NULL;			/* subraster returned to caller */
        !           708: raster	*new_raster(), *rp=NULL;	/* image raster embedded in sp */
        !           709: int	delete_subraster();		/* in case new_raster() fails */
        !           710: int	size = NORMALSIZE,		/* default size */
        !           711: 	baseline = height-1;		/* and baseline */
        !           712: /* -------------------------------------------------------------------------
        !           713: allocate and initialize subraster struct
        !           714: -------------------------------------------------------------------------- */
        !           715: if ( msgfp!=NULL && msglevel>=9999 )
        !           716:   { fprintf(msgfp,"new_subraster(%d,%d,%d)> entry point\n",
        !           717:     width,height,pixsz); fflush(msgfp); }
        !           718: /* --- allocate subraster struct --- */
        !           719: sp = (subraster *)malloc(sizeof(subraster));  /* malloc subraster struct */
        !           720: if ( sp == (subraster *)NULL )		/* malloc failed */
        !           721:   goto end_of_job;			/* return error to caller */
        !           722: /* --- initialize subraster struct --- */
        !           723: sp->type = NOVALUE;			/* character or image raster */
        !           724: sp->symdef =  (mathchardef *)NULL;	/* mathchardef identifying image */
        !           725: sp->baseline = baseline;		/*0 if image is entirely descending*/
        !           726: sp->size = size;			/* font size 0-4 */
        !           727: sp->toprow = sp->leftcol = (-1);	/* upper-left corner of subraster */
        !           728: sp->image = (raster *)NULL;		/*ptr to bitmap image of subraster*/
        !           729: /* -------------------------------------------------------------------------
        !           730: allocate raster and embed it in subraster, and return to caller
        !           731: -------------------------------------------------------------------------- */
        !           732: /* --- allocate raster struct if desired --- */
        !           733: if ( width>0 && height>0 && pixsz>0 )	/* caller wants raster */
        !           734:   { if ( (rp=new_raster(width,height,pixsz)) /* allocate embedded raster */
        !           735:     !=   NULL )				/* if allocate succeeded */
        !           736:         sp->image = rp;			/* embed raster in subraster */
        !           737:     else				/* or if allocate failed */
        !           738:       { delete_subraster(sp);		/* free non-unneeded subraster */
        !           739: 	sp = NULL; } }			/* signal error */
        !           740: /* --- back to caller with new subraster or NULL --- */
        !           741: end_of_job:
        !           742:   if ( msgfp!=NULL && msglevel>=9999 )
        !           743:     { fprintf(msgfp,"new_subraster(%d,%d,%d)> returning (%s)\n",
        !           744:       width,height,pixsz,(sp==NULL?"null ptr":"success")); fflush(msgfp); }
        !           745:   return ( sp );
        !           746: } /* --- end-of-function new_subraster() --- */
        !           747: 
        !           748: 
        !           749: /* ==========================================================================
        !           750:  * Function:	new_chardef (  )
        !           751:  * Purpose:	Allocates and initializes a chardef struct,
        !           752:  *		but _not_ the embedded raster struct.
        !           753:  * --------------------------------------------------------------------------
        !           754:  * Arguments:	none
        !           755:  * --------------------------------------------------------------------------
        !           756:  * Returns:	( chardef * )	ptr to allocated and initialized
        !           757:  *				chardef struct, or NULL for any error.
        !           758:  * --------------------------------------------------------------------------
        !           759:  * Notes:
        !           760:  * ======================================================================= */
        !           761: /* --- entry point --- */
        !           762: chardef	*new_chardef (  )
        !           763: {
        !           764: /* -------------------------------------------------------------------------
        !           765: Allocations and Declarations
        !           766: -------------------------------------------------------------------------- */
        !           767: chardef	*cp = (chardef *)NULL;		/* chardef ptr returned to caller */
        !           768: /* -------------------------------------------------------------------------
        !           769: allocate and initialize chardef struct
        !           770: -------------------------------------------------------------------------- */
        !           771: cp = (chardef *)malloc(sizeof(chardef)); /* malloc chardef struct */
        !           772: if ( cp == (chardef *)NULL )		/* malloc failed */
        !           773:   goto end_of_job;			/* return error to caller */
        !           774: cp->charnum = cp->location = 0;		/* init character description */
        !           775: cp->toprow = cp->topleftcol = 0;	/* init upper-left corner */
        !           776: cp->botrow = cp->botleftcol = 0;	/* init lower-left corner */
        !           777: cp->image.width = cp->image.height = 0;	/* init raster dimensions */
        !           778: cp->image.pixsz = 0;			/* and #bits per pixel */
        !           779: cp->image.pixmap = NULL;		/* init raster pixmap as null */
        !           780: /* -------------------------------------------------------------------------
        !           781: Back to caller with address of chardef struct, or NULL ptr for any error.
        !           782: -------------------------------------------------------------------------- */
        !           783: end_of_job:
        !           784:   return ( cp );
        !           785: } /* --- end-of-function new_chardef() --- */
        !           786: 
        !           787: 
        !           788: /* ==========================================================================
        !           789:  * Function:	delete_raster ( rp )
        !           790:  * Purpose:	Destructor for raster.
        !           791:  *		Frees memory for raster bitmap and struct.
        !           792:  * --------------------------------------------------------------------------
        !           793:  * Arguments:	rp (I)		ptr to raster struct to be deleted.
        !           794:  * --------------------------------------------------------------------------
        !           795:  * Returns:	( int )		1 if completed successfully,
        !           796:  *				or 0 otherwise (for any error).
        !           797:  * --------------------------------------------------------------------------
        !           798:  * Notes:
        !           799:  * ======================================================================= */
        !           800: /* --- entry point --- */
        !           801: int	delete_raster ( raster *rp )
        !           802: {
        !           803: /* -------------------------------------------------------------------------
        !           804: free raster bitmap and struct
        !           805: -------------------------------------------------------------------------- */
        !           806: if ( rp != (raster *)NULL )		/* can't free null ptr */
        !           807:   {
        !           808:   if ( rp->pixmap != (pixbyte *)NULL )	/* can't free null ptr */
        !           809:     free((void *)rp->pixmap);		/* free pixmap within raster */
        !           810:   free((void *)rp);			/* lastly, free raster struct */
        !           811:   } /* --- end-of-if(rp!=NULL) --- */
        !           812: return ( 1 );				/* back to caller, 1=okay 0=failed */
        !           813: } /* --- end-of-function delete_raster() --- */
        !           814: 
        !           815: 
        !           816: /* ==========================================================================
        !           817:  * Function:	delete_subraster ( sp )
        !           818:  * Purpose:	Deallocates a subraster (and embedded raster)
        !           819:  * --------------------------------------------------------------------------
        !           820:  * Arguments:	sp (I)		ptr to subraster struct to be deleted.
        !           821:  * --------------------------------------------------------------------------
        !           822:  * Returns:	( int )		1 if completed successfully,
        !           823:  *				or 0 otherwise (for any error).
        !           824:  * --------------------------------------------------------------------------
        !           825:  * Notes:
        !           826:  * ======================================================================= */
        !           827: /* --- entry point --- */
        !           828: int	delete_subraster ( subraster *sp )
        !           829: {
        !           830: /* -------------------------------------------------------------------------
        !           831: free subraster struct
        !           832: -------------------------------------------------------------------------- */
        !           833: int	delete_raster();		/* to delete embedded raster */
        !           834: if ( sp != (subraster *)NULL )		/* can't free null ptr */
        !           835:   {
        !           836:   if ( sp->type != CHARASTER )		/* not static character data */
        !           837:     if ( sp->image != NULL )		/*raster allocated within subraster*/
        !           838:       delete_raster(sp->image);		/* so free embedded raster */
        !           839:   free((void *)sp);			/* and free subraster struct itself*/
        !           840:   } /* --- end-of-if(sp!=NULL) --- */
        !           841: return ( 1 );				/* back to caller, 1=okay 0=failed */
        !           842: } /* --- end-of-function delete_subraster() --- */
        !           843: 
        !           844: 
        !           845: /* ==========================================================================
        !           846:  * Function:	delete_chardef ( cp )
        !           847:  * Purpose:	Deallocates a chardef (and bitmap of embedded raster)
        !           848:  * --------------------------------------------------------------------------
        !           849:  * Arguments:	cp (I)		ptr to chardef struct to be deleted.
        !           850:  * --------------------------------------------------------------------------
        !           851:  * Returns:	( int )		1 if completed successfully,
        !           852:  *				or 0 otherwise (for any error).
        !           853:  * --------------------------------------------------------------------------
        !           854:  * Notes:
        !           855:  * ======================================================================= */
        !           856: /* --- entry point --- */
        !           857: int	delete_chardef ( chardef *cp )
        !           858: {
        !           859: /* -------------------------------------------------------------------------
        !           860: free chardef struct
        !           861: -------------------------------------------------------------------------- */
        !           862: if ( cp != (chardef *)NULL )		/* can't free null ptr */
        !           863:   {
        !           864:   if ( cp->image.pixmap != NULL )	/* pixmap allocated within raster */
        !           865:     free((void *)cp->image.pixmap);	/* so free embedded pixmap */
        !           866:   free((void *)cp);			/* and free chardef struct itself */
        !           867:   } /* --- end-of-if(cp!=NULL) --- */
        !           868: /* -------------------------------------------------------------------------
        !           869: Back to caller with 1=okay, 0=failed.
        !           870: -------------------------------------------------------------------------- */
        !           871: return ( 1 );
        !           872: } /* --- end-of-function delete_chardef() --- */
        !           873: 
        !           874: 
        !           875: /* ==========================================================================
        !           876:  * Function:	rastcpy ( rp )
        !           877:  * Purpose:	makes duplicate copy of rp
        !           878:  * --------------------------------------------------------------------------
        !           879:  * Arguments:	rp (I)		ptr to raster struct to be copied
        !           880:  * --------------------------------------------------------------------------
        !           881:  * Returns:	( raster * )	ptr to new copy rp,
        !           882:  *				or NULL for any error.
        !           883:  * --------------------------------------------------------------------------
        !           884:  * Notes:     o
        !           885:  * ======================================================================= */
        !           886: /* --- entry point --- */
        !           887: raster	*rastcpy ( raster *rp )
        !           888: {
        !           889: /* -------------------------------------------------------------------------
        !           890: Allocations and Declarations
        !           891: -------------------------------------------------------------------------- */
        !           892: raster	*new_raster(), *newrp=NULL;	/*copied raster returned to caller*/
        !           893: int	height= (rp==NULL?0:rp->height), /* original and copied height */
        !           894: 	width = (rp==NULL?0:rp->width),	/* original and copied width */
        !           895: 	pixsz = (rp==NULL?0:rp->pixsz),	/* #bits per pixel */
        !           896: 	nbytes= (rp==NULL?0:(pixmapsz(rp))); /* #bytes in rp's pixmap */
        !           897: /* -------------------------------------------------------------------------
        !           898: allocate rotated raster and fill it
        !           899: -------------------------------------------------------------------------- */
        !           900: /* --- allocate copied raster with same width,height, and copy bitmap --- */
        !           901: if ( rp != NULL )			/* nothing to copy if ptr null */
        !           902:   if ( (newrp = new_raster(width,height,pixsz)) /*same width,height in copy*/
        !           903:   !=   NULL )				/* check that allocate succeeded */
        !           904:     memcpy(newrp->pixmap,rp->pixmap,nbytes); /* fill copied raster pixmap */
        !           905: return ( newrp );			/* return copied raster to caller */
        !           906: } /* --- end-of-function rastcpy() --- */
        !           907: 
        !           908: 
        !           909: /* ==========================================================================
        !           910:  * Function:	subrastcpy ( sp )
        !           911:  * Purpose:	makes duplicate copy of sp
        !           912:  * --------------------------------------------------------------------------
        !           913:  * Arguments:	sp (I)		ptr to subraster struct to be copied
        !           914:  * --------------------------------------------------------------------------
        !           915:  * Returns:	( subraster * )	ptr to new copy sp,
        !           916:  *				or NULL for any error.
        !           917:  * --------------------------------------------------------------------------
        !           918:  * Notes:     o
        !           919:  * ======================================================================= */
        !           920: /* --- entry point --- */
        !           921: subraster *subrastcpy ( subraster *sp )
        !           922: {
        !           923: /* -------------------------------------------------------------------------
        !           924: Allocations and Declarations
        !           925: -------------------------------------------------------------------------- */
        !           926: subraster *new_subraster(), *newsp=NULL; /* allocate new subraster */
        !           927: raster	*rastcpy(), *newrp=NULL;	/* and new raster image within it */
        !           928: int	delete_subraster();		/* dealloc newsp if rastcpy() fails*/
        !           929: /* -------------------------------------------------------------------------
        !           930: make copy, and return it to caller
        !           931: -------------------------------------------------------------------------- */
        !           932: if ( sp == NULL ) goto end_of_job;	/* nothing to copy */
        !           933: /* --- allocate new subraster "envelope" for copy --- */
        !           934: if ( (newsp=new_subraster(0,0,0))	/* allocate subraster "envelope" */
        !           935: ==   NULL ) goto end_of_job;		/* and quit if we fail to allocate */
        !           936: /* --- transparently copy original envelope to new one --- */
        !           937: memcpy((void *)newsp,(void *)sp,sizeof(subraster)); /* copy envelope */
        !           938: /* --- make a copy of the rasterized image itself, if there is one --- */
        !           939: if ( sp->image != NULL )		/* there's an image embedded in sp */
        !           940:   if ( (newrp = rastcpy(sp->image))	/* so copy rasterized image in sp */
        !           941:   ==   NULL )				/* failed to copy successfully */
        !           942:     { delete_subraster(newsp);		/* won't need newsp any more */
        !           943:       newsp = NULL;			/* because we're returning error */
        !           944:       goto end_of_job; }		/* back to caller with error signal*/
        !           945: /* --- set new params in new envelope --- */
        !           946: newsp->image = newrp;			/* new raster image we just copied */
        !           947: switch ( sp->type )			/* set new raster image type */
        !           948:   { case STRINGRASTER: case CHARASTER: newsp->type = STRINGRASTER; break;
        !           949:     case ASCIISTRING:                  newsp->type = ASCIISTRING;  break;
        !           950:     case IMAGERASTER:  default:        newsp->type = IMAGERASTER;  break; }
        !           951: /* --- return copy of sp to caller --- */
        !           952: end_of_job:
        !           953:   return ( newsp );			/* copy back to caller */
        !           954: } /* --- end-of-function subrastcpy() --- */
        !           955: 
        !           956: 
        !           957: /* ==========================================================================
        !           958:  * Function:	rastrot ( rp )
        !           959:  * Purpose:	rotates rp image 90 degrees right/clockwise
        !           960:  * --------------------------------------------------------------------------
        !           961:  * Arguments:	rp (I)		ptr to raster struct to be rotated
        !           962:  * --------------------------------------------------------------------------
        !           963:  * Returns:	( raster * )	ptr to new raster rotated ralative to rp,
        !           964:  *				or NULL for any error.
        !           965:  * --------------------------------------------------------------------------
        !           966:  * Notes:     o	An underbrace is } rotated 90 degrees clockwise,
        !           967:  *		a hat is <, etc.
        !           968:  * ======================================================================= */
        !           969: /* --- entry point --- */
        !           970: raster	*rastrot ( raster *rp )
        !           971: {
        !           972: /* -------------------------------------------------------------------------
        !           973: Allocations and Declarations
        !           974: -------------------------------------------------------------------------- */
        !           975: raster	*new_raster(), *rotated=NULL;	/*rotated raster returned to caller*/
        !           976: int	height = rp->height, irow,	/* original height, row index */
        !           977: 	width = rp->width, icol,	/* original width, column index */
        !           978: 	pixsz = rp->pixsz;		/* #bits per pixel */
        !           979: /* -------------------------------------------------------------------------
        !           980: allocate rotated raster and fill it
        !           981: -------------------------------------------------------------------------- */
        !           982: /* --- allocate rotated raster with flipped width<-->height --- */
        !           983: if ( (rotated = new_raster(height,width,pixsz)) /* flip width,height */
        !           984: !=   NULL )				/* check that allocation succeeded */
        !           985:   /* --- fill rotated raster --- */
        !           986:   for ( irow=0; irow<height; irow++ )	/* for each row of rp */
        !           987:     for ( icol=0; icol<width; icol++ )	/* and each column of rp */
        !           988:       {	int value = getpixel(rp,irow,icol);
        !           989: 	/* setpixel(rotated,icol,irow,value); } */
        !           990: 	setpixel(rotated,icol,(height-1-irow),value); }
        !           991: return ( rotated );			/* return rotated raster to caller */
        !           992: } /* --- end-of-function rastrot() --- */
        !           993: 
        !           994: 
        !           995: /* ==========================================================================
        !           996:  * Function:	rastput ( target, source, top, left, isopaque )
        !           997:  * Purpose:	Overlays source onto target,
        !           998:  *		with the 0,0-bit of source onto the top,left-bit of target.
        !           999:  * --------------------------------------------------------------------------
        !          1000:  * Arguments:	target (I)	ptr to target raster struct
        !          1001:  *		source (I)	ptr to source raster struct
        !          1002:  *		top (I)		int containing 0 ... target->height - 1
        !          1003:  *		left (I)	int containing 0 ... target->width - 1
        !          1004:  *		isopaque (I)	int containing false (zero) to allow
        !          1005:  *				original 1-bits of target to "show through"
        !          1006:  *				0-bits of source.
        !          1007:  * --------------------------------------------------------------------------
        !          1008:  * Returns:	( int )		1 if completed successfully,
        !          1009:  *				or 0 otherwise (for any error).
        !          1010:  * --------------------------------------------------------------------------
        !          1011:  * Notes:
        !          1012:  * ======================================================================= */
        !          1013: /* --- entry point --- */
        !          1014: int	rastput ( raster *target, raster *source,
        !          1015: 		int top, int left, int isopaque )
        !          1016: {
        !          1017: /* -------------------------------------------------------------------------
        !          1018: Allocations and Declarations
        !          1019: -------------------------------------------------------------------------- */
        !          1020: int	irow, icol,		/* indexes over source raster */
        !          1021: 	twidth=target->width, theight=target->height, /*target width,height*/
        !          1022: 	tpix, ntpix = twidth*theight; /* #pixels in target */
        !          1023: int	isfatal = 0,		/* true to abend on out-of-bounds error */
        !          1024: 	isstrict = 0/*1*/,	/* true for strict bounds check - no "wrap"*/
        !          1025: 	isokay = 1;		/* true if no pixels out-of-bounds */
        !          1026: /* -------------------------------------------------------------------------
        !          1027: superimpose source onto target, one bit at a time
        !          1028: -------------------------------------------------------------------------- */
        !          1029: if ( isstrict && (top<0 || left<0) )		/* args fail strict test */
        !          1030:  isokay = 0;					/* so just return error */
        !          1031: else
        !          1032:  for ( irow=0; irow<source->height; irow++ )	/* for each scan line */
        !          1033:   {
        !          1034:   tpix = (top+irow)*target->width + left - 1;	/*first target pixel (-1)*/
        !          1035:   for ( icol=0; icol<source->width; icol++ )	/* each pixel in scan line */
        !          1036:     {
        !          1037:     int svalue = getpixel(source,irow,icol);	/* source pixel value */
        !          1038:     ++tpix;					/* bump target pixel */
        !          1039:     if ( msgfp!=NULL && msglevel>=9999 )	/* debugging output */
        !          1040:       {	fprintf(msgfp,"rastput> tpix,ntpix=%d,%d top,irow,theight=%d,%d,%d "
        !          1041: 	"left,icol,twidth=%d,%d,%d\n", tpix,ntpix, top,irow,theight,
        !          1042: 	left,icol,twidth);  fflush(msgfp); }
        !          1043:     if ( tpix >= ntpix				/* bounds check failed */
        !          1044:     ||   (isstrict && (irow+top>=theight || icol+left>=twidth)) )
        !          1045:       {	isokay = 0;				/* reset okay flag */
        !          1046: 	if ( isfatal ) goto end_of_job;		/* abort if error is fatal */
        !          1047: 	else break; }				/*or just go on to next row*/
        !          1048:     if ( tpix >= 0 )				/* bounds check okay */
        !          1049:      if ( svalue!=0 || isopaque )		/*got dark or opaque source*/
        !          1050:       setpixel(target,irow+top,icol+left,svalue); /*overlay source on target*/
        !          1051:     } /* --- end-of-for(icol) --- */
        !          1052:   } /* --- end-of-for(irow) --- */
        !          1053: /* -------------------------------------------------------------------------
        !          1054: Back to caller with 1=okay, 0=failed.
        !          1055: -------------------------------------------------------------------------- */
        !          1056: end_of_job:
        !          1057:   return ( isokay /*isfatal? (tpix<ntpix? 1:0) : 1*/ );
        !          1058: } /* --- end-of-function rastput() --- */
        !          1059: 
        !          1060: 
        !          1061: /* ==========================================================================
        !          1062:  * Function:	rastcompose ( sp1, sp2, offset2, isalign, isfree )
        !          1063:  * Purpose:	Overlays sp2 on top of sp1, leaving both unchanged
        !          1064:  *		and returning a newly-allocated composite subraster.
        !          1065:  *		Frees/deletes input sp1 and/or sp2 depending on value
        !          1066:  *		of isfree (0=none, 1=sp1, 2=sp2, 3=both).
        !          1067:  * --------------------------------------------------------------------------
        !          1068:  * Arguments:	sp1 (I)		subraster *  to "underneath" subraster,
        !          1069:  *				whose baseline is preserved
        !          1070:  *		sp2 (I)		subraster *  to "overlaid" subraster
        !          1071:  *		offset2 (I)	int containing 0 or number of pixels
        !          1072:  *				to horizontally shift sp2 relative to sp1,
        !          1073:  *				either positive (right) or negative
        !          1074:  *		isalign (I)	int containing 1 to align baselines,
        !          1075:  *				or 0 to vertically center sp2 over sp1
        !          1076:  *		isfree (I)	int containing 1=free sp1 before return,
        !          1077:  *				2=free sp2, 3=free both, 0=free none.
        !          1078:  * --------------------------------------------------------------------------
        !          1079:  * Returns:	( subraster * )	pointer to constructed subraster
        !          1080:  *				or  NULL for any error
        !          1081:  * --------------------------------------------------------------------------
        !          1082:  * Notes:
        !          1083:  * ======================================================================= */
        !          1084: /* --- entry point --- */
        !          1085: subraster *rastcompose ( subraster *sp1, subraster *sp2, int offset2,
        !          1086: 			int isalign, int isfree )
        !          1087: {
        !          1088: /* -------------------------------------------------------------------------
        !          1089: Allocations and Declarations
        !          1090: -------------------------------------------------------------------------- */
        !          1091: subraster *new_subraster(), *sp=(subraster *)NULL; /* returned subraster */
        !          1092: raster	*rp=(raster *)NULL;		/* new composite raster in sp */
        !          1093: int	delete_subraster();		/* in case isfree non-zero */
        !          1094: int	rastput();			/*place sp1,sp2 in composite raster*/
        !          1095: int	base1   = sp1->baseline,	/*baseline for underlying subraster*/
        !          1096: 	height1 = (sp1->image)->height,	/* height for underlying subraster */
        !          1097: 	width1  = (sp1->image)->width,	/* width for underlying subraster */
        !          1098: 	pixsz1  = (sp1->image)->pixsz,	/* pixsz for underlying subraster */
        !          1099: 	base2   = sp2->baseline,	/*baseline for overlaid subraster */
        !          1100: 	height2 = (sp2->image)->height,	/* height for overlaid subraster */
        !          1101: 	width2  = (sp2->image)->width,	/* width for overlaid subraster */
        !          1102: 	pixsz2  = (sp2->image)->pixsz;	/* pixsz for overlaid subraster */
        !          1103: int	height=0, width=0, pixsz=0, base=0; /* overlaid composite */
        !          1104: /* -------------------------------------------------------------------------
        !          1105: Initialization
        !          1106: -------------------------------------------------------------------------- */
        !          1107: /* --- determine height, width and baseline of composite raster --- */
        !          1108: if ( isalign )				/* baselines of sp1,sp2 aligned */
        !          1109:   { height = max2(base1+1,base2+1)	/* max height above baseline */
        !          1110:            + max2(height1-base1-1,height2-base2-1); /*+ max descending below*/
        !          1111:     base   = max2(base1,base2); }	/* max space above baseline */
        !          1112: else					/* baselines not aligned */
        !          1113:   { height = max2(height1,height2);	/* max height */
        !          1114:     base   = base1 + (height-height1)/2; } /* baseline for sp1 */
        !          1115: width      = max2(width1,width2+abs(offset2)); /* max width */
        !          1116: pixsz      = max2(pixsz1,pixsz2);	/* bitmap,bytemap becomes bytemap */
        !          1117: /* -------------------------------------------------------------------------
        !          1118: allocate concatted composite subraster
        !          1119: -------------------------------------------------------------------------- */
        !          1120: /* --- allocate returned subraster (and then initialize it) --- */
        !          1121: if ( (sp=new_subraster(width,height,pixsz)) /* allocate new subraster */
        !          1122: ==   (subraster *)NULL ) goto end_of_job; /* failed, so quit */
        !          1123: /* --- initialize subraster parameters --- */
        !          1124: sp->type = IMAGERASTER;			/* image */
        !          1125: sp->baseline = base;			/* composite baseline */
        !          1126: sp->size = sp1->size;			/* underlying char is sp1 */
        !          1127: /* --- extract raster from subraster --- */
        !          1128: rp = sp->image;				/* raster allocated in subraster */
        !          1129: /* -------------------------------------------------------------------------
        !          1130: overlay sp1 and sp2 in new composite raster
        !          1131: -------------------------------------------------------------------------- */
        !          1132: if ( isalign )
        !          1133:   { rastput (rp, sp1->image, base-base1, (width-width1)/2, 1);	/*underlying*/
        !          1134:     rastput (rp, sp2->image, base-base2,			/*overlaid*/
        !          1135: 		(width-width2)/2+offset2, 0); }
        !          1136: else
        !          1137:   { rastput (rp, sp1->image, base-base1, (width-width1)/2, 1);	/*underlying*/
        !          1138:     rastput (rp, sp2->image, (height-height2)/2,		/*overlaid*/
        !          1139: 		(width-width2)/2+offset2, 0); }
        !          1140: /* -------------------------------------------------------------------------
        !          1141: free input if requested
        !          1142: -------------------------------------------------------------------------- */
        !          1143: if ( isfree > 0 )			/* caller wants input freed */
        !          1144:   { if ( isfree==1 || isfree>2 ) delete_subraster(sp1);	/* free sp1 */
        !          1145:     if ( isfree >= 2 ) delete_subraster(sp2); }		/* and/or sp2 */
        !          1146: /* -------------------------------------------------------------------------
        !          1147: Back to caller with pointer to concatted subraster or with null for error
        !          1148: -------------------------------------------------------------------------- */
        !          1149: end_of_job:
        !          1150:   return ( sp );			/* back with subraster or null ptr */
        !          1151: } /* --- end-of-function rastcompose() --- */
        !          1152: 
        !          1153: 
        !          1154: /* ==========================================================================
        !          1155:  * Function:	rastcat ( sp1, sp2, isfree )
        !          1156:  * Purpose:	"Concatanates" subrasters sp1||sp2, leaving both unchanged
        !          1157:  *		and returning a newly-allocated subraster.
        !          1158:  *		Frees/deletes input sp1 and/or sp2 depending on value
        !          1159:  *		of isfree (0=none, 1=sp1, 2=sp2, 3=both).
        !          1160:  * --------------------------------------------------------------------------
        !          1161:  * Arguments:	sp1 (I)		subraster *  to left-hand subraster
        !          1162:  *		sp2 (I)		subraster *  to right-hand subraster
        !          1163:  *		isfree (I)	int containing 1=free sp1 before return,
        !          1164:  *				2=free sp2, 3=free both, 0=free none.
        !          1165:  * --------------------------------------------------------------------------
        !          1166:  * Returns:	( subraster * )	pointer to constructed subraster sp1||sp2
        !          1167:  *				or  NULL for any error
        !          1168:  * --------------------------------------------------------------------------
        !          1169:  * Notes:
        !          1170:  * ======================================================================= */
        !          1171: /* --- entry point --- */
        !          1172: subraster *rastcat ( subraster *sp1, subraster *sp2, int isfree )
        !          1173: {
        !          1174: /* -------------------------------------------------------------------------
        !          1175: Allocations and Declarations
        !          1176: -------------------------------------------------------------------------- */
        !          1177: subraster *new_subraster(), *sp=(subraster *)NULL; /* returned subraster */
        !          1178: raster	*rp=(raster *)NULL;		/* new concatted raster */
        !          1179: int	delete_subraster();		/* in case isfree non-zero */
        !          1180: int	rastput();			/*place sp1,sp2 in concatted raster*/
        !          1181: int	type_raster();			/* debugging display */
        !          1182: int	base1   = sp1->baseline,	/*baseline for left-hand subraster*/
        !          1183: 	height1 = (sp1->image)->height,	/* height for left-hand subraster */
        !          1184: 	width1  = (sp1->image)->width,	/* width for left-hand subraster */
        !          1185: 	pixsz1  = (sp1->image)->pixsz,	/* pixsz for left-hand subraster */
        !          1186: 	type1   = sp1->type,		/* image type for left-hand */
        !          1187: 	base2   = sp2->baseline,	/*baseline for right-hand subraster*/
        !          1188: 	height2 = (sp2->image)->height,	/* height for right-hand subraster */
        !          1189: 	width2  = (sp2->image)->width,	/* width for right-hand subraster */
        !          1190: 	pixsz2  = (sp2->image)->pixsz,	/* pixsz for right-hand subraster */
        !          1191: 	type2   = sp2->type;		/* image type for right-hand */
        !          1192: int	height=0, width=0, pixsz=0, base=0; /*concatted sp1||sp2 composite*/
        !          1193: int	issquash = (squashmargin!=0?1:0), /* true to "squash" sp1||sp2 */
        !          1194: 	isopaque = (issquash?0:1),	/* not oppaque if squashing */
        !          1195: 	rastsquash(), isblank=0, nsquash=0, /* #cols to squash */
        !          1196: 	oldsquashmargin = squashmargin;	/* save original squashmargin */
        !          1197: int	blanksignal = (-991234);	/*rastsquash signal right-hand blank*/
        !          1198: mathchardef *symdef1 = sp1->symdef,	/*mathchardef of last left-hand char*/
        !          1199: 	*symdef2 = sp2->symdef;		/* mathchardef of right-hand char */
        !          1200: int	class1 = (symdef1==NULL?ORDINARY:symdef1->class), /* symdef->class */
        !          1201: 	class2 = (symdef2==NULL?ORDINARY:symdef2->class), /* or default */
        !          1202: 	smash1 = (symdef1!=NULL)&&(class1==ORDINARY||class1==VARIABLE||
        !          1203: 		  class1==OPENING||class1==CLOSING||class1==PUNCTION),
        !          1204: 	smash2 = (symdef2!=NULL)&&(class2==ORDINARY||class2==VARIABLE||
        !          1205: 		  class2==OPENING||class2==CLOSING||class2==PUNCTION),
        !          1206: 	space = fontsize/2+1;		/* #cols between sp1 and sp2 */
        !          1207: /* -------------------------------------------------------------------------
        !          1208: Initialization
        !          1209: -------------------------------------------------------------------------- */
        !          1210: /* --- determine inter-character space from character class --- */
        !          1211: if ( !isstring )
        !          1212:   space = max2(2,(symspace[class1][class2] + fontsize-3)); /* space */
        !          1213: else space = 1;				/* space for ascii string */
        !          1214: /* --- determine squash --- */
        !          1215: if ( !isstring )			/* don't squash strings */
        !          1216:  if ( issquash ) {			/* raster squash wanted */
        !          1217:    int	maxsquash = rastsquash(sp1,sp2), /* calculate max squash space */
        !          1218: 	margin = squashmargin;		/* init margin without delta */
        !          1219:    if ( (1 && smash1 && smash2)		/* concatanating two chars */
        !          1220:    ||   (1 && type1!=IMAGERASTER && type2!=IMAGERASTER) )
        !          1221:      /*maxsquash = 0;*/			/* turn off squash */
        !          1222:      margin = max2(space-1,0);		/* force small squashmargin */
        !          1223:    else					/* adjust for delta if images */
        !          1224:      if ( issquashdelta )		/* squashmargin is a delta value */
        !          1225:        margin += fontsize;		/* add displaystyle base to margin */
        !          1226:    if ( maxsquash == blanksignal )	/* sp2 is intentional blank */
        !          1227:      isblank = 1;			/* set blank flag signal */
        !          1228:    else					/* see how much extra space we have*/
        !          1229:      if ( maxsquash > margin )		/* enough space for adjustment */
        !          1230:        nsquash = maxsquash-margin;	/* make adjustment */
        !          1231:    if ( msgfp!=NULL && msglevel>=99 )	/* display squash results */
        !          1232:      { fprintf(msgfp,"rastcat> maxsquash=%d, margin=%d, nsquash=%d\n",
        !          1233:        maxsquash,margin,nsquash);
        !          1234:        fprintf(msgfp,"rastcat> type1=%d,2=%d, class1=%d,2=%d\n", type1,type2,
        !          1235:        (symdef1==NULL?-999:class1),(symdef2==NULL?-999:class2));
        !          1236:        fflush(msgfp); }
        !          1237:    } /* --- end-of-if(issquash) --- */
        !          1238: /* --- determine height, width and baseline of composite raster --- */
        !          1239: if ( !isstring )
        !          1240:  { height = max2(base1+1,base2+1)	/* max height above baseline */
        !          1241:           + max2(height1-base1-1,height2-base2-1); /*+ max descending below*/
        !          1242:    width  = width1+width2 + space-nsquash; /*add widths and space-squash*/
        !          1243:    width  = max3(width,width1,width2); } /* don't "over-squash" composite */
        !          1244: else					/* ascii string */
        !          1245:  { height = 1;				/* default */
        !          1246:    width  = width1 + width2 + space - 1; } /* no need for two nulls */
        !          1247: pixsz  = max2(pixsz1,pixsz2);		/* bitmap||bytemap becomes bytemap */
        !          1248: base   = max2(base1,base2);		/* max space above baseline */
        !          1249: if ( msgfp!=NULL && msglevel>=9999 )	/* display components */
        !          1250:   { fprintf(msgfp,"rastcat> Left-hand ht,width,pixsz,base = %d,%d,%d,%d\n",
        !          1251:     height1,width1,pixsz1,base1);
        !          1252:     type_raster(sp1->image,msgfp);	/* display left-hand raster */
        !          1253:     fprintf(msgfp,"rastcat> Right-hand ht,width,pixsz,base = %d,%d,%d,%d\n",
        !          1254:     height2,width2,pixsz2,base2);
        !          1255:     type_raster(sp2->image,msgfp);	/* display right-hand raster */
        !          1256:     fprintf(msgfp,
        !          1257:     "rastcat> Composite ht,width,squash,pixsz,base = %d,%d,%d,%d,%d\n",
        !          1258:     height,width,nsquash,pixsz,base);
        !          1259:     fflush(msgfp); }			/* flush msgfp buffer */
        !          1260: /* -------------------------------------------------------------------------
        !          1261: allocate concatted composite subraster
        !          1262: -------------------------------------------------------------------------- */
        !          1263: /* --- allocate returned subraster (and then initialize it) --- */
        !          1264: if ( msgfp!=NULL && msglevel>=9999 )
        !          1265:   { fprintf(msgfp,"rastcat> calling new_subraster(%d,%d,%d)\n",
        !          1266:     width,height,pixsz); fflush(msgfp); }
        !          1267: if ( (sp=new_subraster(width,height,pixsz)) /* allocate new subraster */
        !          1268: ==   (subraster *)NULL )		/* failed */
        !          1269:   { if ( msgfp!=NULL && msglevel>=1 )	/* report failure */
        !          1270:       {	fprintf(msgfp,"rastcat> new_subraster(%d,%d,%d) failed\n",
        !          1271: 	width,height,pixsz); fflush(msgfp); }
        !          1272:     goto end_of_job; }			/* failed, so quit */
        !          1273: /* --- initialize subraster parameters --- */
        !          1274: /* sp->type = (!isstring?STRINGRASTER:ASCIISTRING); */  /*concatted string*/
        !          1275: if ( !isstring )
        !          1276:   sp->type = type2;/*(type1==type2?type2:IMAGERASTER);*/ /*string or image*/
        !          1277: else
        !          1278:   sp->type = ASCIISTRING;		/* concatted ascii string */
        !          1279: sp->symdef = symdef2;			/* rightmost char is sp2 */
        !          1280: sp->baseline = base;			/* composite baseline */
        !          1281: sp->size = sp2->size;			/* rightmost char is sp2 */
        !          1282: if ( isblank )				/* need to propagate blanksignal */
        !          1283:   sp->type = blanksignal;		/* may not be completely safe??? */
        !          1284: /* --- extract raster from subraster --- */
        !          1285: rp = sp->image;				/* raster allocated in subraster */
        !          1286: /* -------------------------------------------------------------------------
        !          1287: overlay sp1 and sp2 in new composite raster
        !          1288: -------------------------------------------------------------------------- */
        !          1289: if ( msgfp!=NULL && msglevel>=9999 )
        !          1290:   { fprintf(msgfp,"rastcat> calling rastput() to concatanate left||right\n");
        !          1291:     fflush(msgfp); }			/* flush msgfp buffer */
        !          1292: if ( !isstring )
        !          1293:  rastput (rp, sp1->image, base-base1,	/* overlay left-hand */
        !          1294:  max2(0,nsquash-width1), 1);		/* plus any residual squash space */
        !          1295: else
        !          1296:  memcpy(rp->pixmap,(sp1->image)->pixmap,width1-1);  /*init left string*/
        !          1297: if ( msgfp!=NULL && msglevel>=9999 )
        !          1298:   { type_raster(sp->image,msgfp);	/* display composite raster */
        !          1299:     fflush(msgfp); }			/* flush msgfp buffer */
        !          1300: if ( !isstring )
        !          1301:  rastput (rp, sp2->image, base-base2,	/* overlay right-hand */
        !          1302:  max2(0,width1+space-nsquash), isopaque); /* minus any squashed space */
        !          1303: else
        !          1304:  { strcpy((char *)(rp->pixmap)+width1-1+space,(char *)((sp2->image)->pixmap));
        !          1305:    ((char *)(rp->pixmap))[width1+width2+space-2] = '\000'; } /*null-term*/
        !          1306: if ( msgfp!=NULL && msglevel>=9999 )
        !          1307:   { type_raster(sp->image,msgfp);	/* display composite raster */
        !          1308:     fflush(msgfp); }			/* flush msgfp buffer */
        !          1309: /* -------------------------------------------------------------------------
        !          1310: free input if requested
        !          1311: -------------------------------------------------------------------------- */
        !          1312: if ( isfree > 0 )			/* caller wants input freed */
        !          1313:   { if ( isfree==1 || isfree>2 ) delete_subraster(sp1);	/* free sp1 */
        !          1314:     if ( isfree >= 2 ) delete_subraster(sp2); }		/* and/or sp2 */
        !          1315: /* -------------------------------------------------------------------------
        !          1316: Back to caller with pointer to concatted subraster or with null for error
        !          1317: -------------------------------------------------------------------------- */
        !          1318: end_of_job:
        !          1319:   squashmargin = oldsquashmargin;	/* reset original squashmargin */
        !          1320:   return ( sp );			/* back with subraster or null ptr */
        !          1321: } /* --- end-of-function rastcat() --- */
        !          1322: 
        !          1323: 
        !          1324: /* ==========================================================================
        !          1325:  * Function:	rastack ( sp1, sp2, base, space, iscenter, isfree )
        !          1326:  * Purpose:	Stack subrasters sp2 atop sp1, leaving both unchanged
        !          1327:  *		and returning a newly-allocated subraster,
        !          1328:  *		whose baseline is sp1's if base=1, or sp2's if base=2.
        !          1329:  *		Frees/deletes input sp1 and/or sp2 depending on value
        !          1330:  *		of isfree (0=none, 1=sp1, 2=sp2, 3=both).
        !          1331:  * --------------------------------------------------------------------------
        !          1332:  * Arguments:	sp1 (I)		subraster *  to lower subraster
        !          1333:  *		sp2 (I)		subraster *  to upper subraster
        !          1334:  *		base (I)	int containing 1 if sp1 is baseline,
        !          1335:  *				or 2 if sp2 is baseline.
        !          1336:  *		space (I)	int containing #rows blank space inserted
        !          1337:  *				between sp1's image and sp2's image.
        !          1338:  *		iscenter (I)	int containing 1 to center both sp1 and sp2
        !          1339:  *				in stacked array, 0 to left-justify both
        !          1340:  *		isfree (I)	int containing 1=free sp1 before return,
        !          1341:  *				2=free sp2, 3=free both, 0=free none.
        !          1342:  * --------------------------------------------------------------------------
        !          1343:  * Returns:	( subraster * )	pointer to constructed subraster sp2 atop sp1
        !          1344:  *				or  NULL for any error
        !          1345:  * --------------------------------------------------------------------------
        !          1346:  * Notes:
        !          1347:  * ======================================================================= */
        !          1348: /* --- entry point --- */
        !          1349: subraster *rastack ( subraster *sp1, subraster *sp2,
        !          1350: 			int base, int space, int iscenter, int isfree )
        !          1351: {
        !          1352: /* -------------------------------------------------------------------------
        !          1353: Allocations and Declarations
        !          1354: -------------------------------------------------------------------------- */
        !          1355: subraster *new_subraster(), *sp=(subraster *)NULL; /* returned subraster */
        !          1356: raster	*rp=(raster *)NULL;		/* new stacked raster in sp */
        !          1357: int	delete_subraster();		/* in case isfree non-zero */
        !          1358: int	rastput();			/* place sp1,sp2 in stacked raster */
        !          1359: int	base1   = sp1->baseline,	/* baseline for lower subraster */
        !          1360: 	height1 = (sp1->image)->height,	/* height for lower subraster */
        !          1361: 	width1  = (sp1->image)->width,	/* width for lower subraster */
        !          1362: 	pixsz1  = (sp1->image)->pixsz,	/* pixsz for lower subraster */
        !          1363: 	base2   = sp2->baseline,	/* baseline for upper subraster */
        !          1364: 	height2 = (sp2->image)->height,	/* height for upper subraster */
        !          1365: 	width2  = (sp2->image)->width,	/* width for upper subraster */
        !          1366: 	pixsz2  = (sp2->image)->pixsz;	/* pixsz for upper subraster */
        !          1367: int	height=0, width=0, pixsz=0, baseline=0;	/*for stacked sp2 atop sp1*/
        !          1368: mathchardef *symdef1 = sp1->symdef,	/* mathchardef of right lower char */
        !          1369: 	*symdef2 = sp2->symdef;		/* mathchardef of right upper char */
        !          1370: /* -------------------------------------------------------------------------
        !          1371: Initialization
        !          1372: -------------------------------------------------------------------------- */
        !          1373: /* --- determine height, width and baseline of composite raster --- */
        !          1374: height   = height1 + space + height2;	/* sum of heights plus space */
        !          1375: width    = max2(width1,width2);		/* max width is overall width */
        !          1376: pixsz    = max2(pixsz1,pixsz2);		/* bitmap||bytemap becomes bytemap */
        !          1377: baseline = (base==1? height2+space+base1 : (base==2? base2 : 0));
        !          1378: /* -------------------------------------------------------------------------
        !          1379: allocate stacked composite subraster (with embedded raster)
        !          1380: -------------------------------------------------------------------------- */
        !          1381: /* --- allocate returned subraster (and then initialize it) --- */
        !          1382: if ( (sp=new_subraster(width,height,pixsz)) /* allocate new subraster */
        !          1383: ==   (subraster *)NULL ) goto end_of_job; /* failed, so quit */
        !          1384: /* --- initialize subraster parameters --- */
        !          1385: sp->type = IMAGERASTER;			/* stacked rasters */
        !          1386: sp->symdef = (base==1? symdef1 : (base==2? symdef2 : NULL)); /* symdef */
        !          1387: sp->baseline = baseline;		/* composite baseline */
        !          1388: sp->size = (base==1? sp1->size : (base==2? sp2->size : NORMALSIZE)); /*size*/
        !          1389: /* --- extract raster from subraster --- */
        !          1390: rp = sp->image;				/* raster embedded in subraster */
        !          1391: /* -------------------------------------------------------------------------
        !          1392: overlay sp1 and sp2 in new composite raster
        !          1393: -------------------------------------------------------------------------- */
        !          1394: if ( iscenter == 1 )			/* center both sp1 and sp2 */
        !          1395:   { rastput (rp, sp2->image, 0, (width-width2)/2, 1);  /* overlay upper */
        !          1396:     rastput (rp, sp1->image, height2+space, (width-width1)/2, 1); } /*lower*/
        !          1397: else					/* left-justify both sp1 and sp2 */
        !          1398:   { rastput (rp, sp2->image, 0, 0, 1);  /* overlay upper */
        !          1399:     rastput (rp, sp1->image, height2+space, 0, 1); } /*lower*/
        !          1400: /* -------------------------------------------------------------------------
        !          1401: free input if requested
        !          1402: -------------------------------------------------------------------------- */
        !          1403: if ( isfree > 0 )			/* caller wants input freed */
        !          1404:   { if ( isfree==1 || isfree>2 ) delete_subraster(sp1);	/* free sp1 */
        !          1405:     if ( isfree>=2 ) delete_subraster(sp2); } /* and/or sp2 */
        !          1406: /* -------------------------------------------------------------------------
        !          1407: Back to caller with pointer to stacked subraster or with null for error
        !          1408: -------------------------------------------------------------------------- */
        !          1409: end_of_job:
        !          1410:   return ( sp );			/* back with subraster or null ptr */
        !          1411: } /* --- end-of-function rastack() --- */
        !          1412: 
        !          1413: 
        !          1414: /* ==========================================================================
        !          1415:  * Function:	rastile ( tiles, ntiles )
        !          1416:  * Purpose:	Allocate and build up a composite raster
        !          1417:  *		from the ntiles components/characters supplied in tiles.
        !          1418:  * --------------------------------------------------------------------------
        !          1419:  * Arguments:	tiles (I)	subraster *  to array of subraster structs
        !          1420:  *				describing the components and their locations
        !          1421:  *		ntiles (I)	int containing number of subrasters in tiles[]
        !          1422:  * --------------------------------------------------------------------------
        !          1423:  * Returns:	( raster * )	ptr to composite raster,
        !          1424:  *				or NULL for any error.
        !          1425:  * --------------------------------------------------------------------------
        !          1426:  * Notes:     o	The top,left corner of a raster is row=0,col=0
        !          1427:  *		with row# increasing as you move down,
        !          1428:  *		and col# increasing as you move right.
        !          1429:  *		Metafont numbers rows with the baseline=0,
        !          1430:  *		so the top row is a positive number that
        !          1431:  *		decreases as you move down.
        !          1432:  *	      o	rastile() is no longer used.
        !          1433:  *		It was used by an earlier rasterize() algorithm,
        !          1434:  *		and I've left it in place should it be needed again.
        !          1435:  *		But recent changes haven't been tested/exercised.
        !          1436:  * ======================================================================= */
        !          1437: /* --- entry point --- */
        !          1438: raster	*rastile ( subraster *tiles, int ntiles )
        !          1439: {
        !          1440: /* -------------------------------------------------------------------------
        !          1441: Allocations and Declarations
        !          1442: -------------------------------------------------------------------------- */
        !          1443: raster	*new_raster(), *composite=(raster *)NULL;  /*raster back to caller*/
        !          1444: int	width=0, height=0, pixsz=0, /*width,height,pixsz of composite raster*/
        !          1445: 	toprow=9999, rightcol=-999, /* extreme upper-right corner of tiles */
        !          1446: 	botrow=-999, leftcol=9999;  /* extreme lower-left corner of tiles */
        !          1447: int	itile;			/* tiles[] index */
        !          1448: int	rastput();		/* overlay each tile in composite raster */
        !          1449: /* -------------------------------------------------------------------------
        !          1450: run through tiles[] to determine dimensions for composite raster
        !          1451: -------------------------------------------------------------------------- */
        !          1452: /* --- determine row and column bounds of composite raster --- */
        !          1453: for ( itile=0; itile<ntiles; itile++ )
        !          1454:   {
        !          1455:   subraster *tile = &(tiles[itile]);		/* ptr to current tile */
        !          1456:   /* --- upper-left corner of composite --- */
        !          1457:   toprow = min2(toprow, tile->toprow);
        !          1458:   leftcol = min2(leftcol, tile->leftcol);
        !          1459:   /* --- lower-right corner of composite --- */
        !          1460:   botrow = max2(botrow, tile->toprow + (tile->image)->height - 1);
        !          1461:   rightcol = max2(rightcol, tile->leftcol + (tile->image)->width  - 1);
        !          1462:   /* --- pixsz of composite --- */
        !          1463:   pixsz = max2(pixsz,(tile->image)->pixsz);
        !          1464:   } /* --- end-of-for(itile) --- */
        !          1465: /* --- calculate width and height from bounds --- */
        !          1466: width  = rightcol - leftcol + 1;
        !          1467: height = botrow - toprow + 1;
        !          1468: /* --- sanity check (quit if bad dimensions) --- */
        !          1469: if ( width<1 || height<1 ) goto end_of_job;
        !          1470: /* -------------------------------------------------------------------------
        !          1471: allocate composite raster, and embed tiles[] within it
        !          1472: -------------------------------------------------------------------------- */
        !          1473: /* --- allocate composite raster --- */
        !          1474: if ( (composite=new_raster(width,height,pixsz))	/*allocate composite raster*/
        !          1475: ==   (raster *)NULL ) goto end_of_job;		/* and quit if failed */
        !          1476: /* --- embed tiles[] in composite --- */
        !          1477: for ( itile=0; itile<ntiles; itile++ )
        !          1478:   { subraster *tile = &(tiles[itile]);		/* ptr to current tile */
        !          1479:     rastput (composite, tile->image,		/* overlay tile image at...*/
        !          1480:       tile->toprow-toprow, tile->leftcol-leftcol, 1); } /*upper-left corner*/
        !          1481: /* -------------------------------------------------------------------------
        !          1482: Back to caller with composite raster (or null for any error)
        !          1483: -------------------------------------------------------------------------- */
        !          1484: end_of_job:
        !          1485:   return ( composite );			/* back with composite or null ptr */
        !          1486: } /* --- end-of-function rastile() --- */
        !          1487: 
        !          1488: 
        !          1489: /* ==========================================================================
        !          1490:  * Function:	rastsquash ( sp1, sp2 )
        !          1491:  * Purpose:	When concatanating sp1||sp2, calculate #pixels
        !          1492:  *		we can "squash sp2 left"
        !          1493:  * --------------------------------------------------------------------------
        !          1494:  * Arguments:	sp1 (I)		subraster *  to left-hand raster
        !          1495:  *		sp2 (I)		subraster *  to right-hand raster
        !          1496:  * --------------------------------------------------------------------------
        !          1497:  * Returns:	( int )		max #pixels we can squash sp1||sp2,
        !          1498:  *				or "blanksignal" if sp2 intentionally blank,
        !          1499:  *				or 0 for any error.
        !          1500:  * --------------------------------------------------------------------------
        !          1501:  * Notes:     o
        !          1502:  * ======================================================================= */
        !          1503: /* --- entry point --- */
        !          1504: int	rastsquash ( subraster *sp1, subraster *sp2 )
        !          1505: {
        !          1506: /* -------------------------------------------------------------------------
        !          1507: Allocations and Declarations
        !          1508: -------------------------------------------------------------------------- */
        !          1509: int	nsquash = 0;			/* #pixels to squash sp1||sp2 */
        !          1510: int	base1   = sp1->baseline,	/*baseline for left-hand subraster*/
        !          1511: 	height1 = (sp1->image)->height,	/* height for left-hand subraster */
        !          1512: 	width1  = (sp1->image)->width,	/* width for left-hand subraster */
        !          1513: 	base2   = sp2->baseline,	/*baseline for right-hand subraster*/
        !          1514: 	height2 = (sp2->image)->height,	/* height for right-hand subraster */
        !          1515: 	width2  = (sp2->image)->width;	/* width for right-hand subraster */
        !          1516: int	base = max2(base1,base2),	/* max ascenders - 1 above baseline*/
        !          1517: 	top1=base-base1, top2=base-base2, /* top irow indexes for sp1, sp2 */
        !          1518: 	bot1=top1+height1-1, bot2=top2+height2-1, /* bot irow indexes */
        !          1519: 	height = max2(bot1,bot2)+1;	/* total height */
        !          1520: int	irow1=0,irow2=0, icol=0;	/* row,col indexes */
        !          1521: int	firstcol1[1025], nfirst1=0,	/* 1st sp1 col containing set pixel*/
        !          1522: 	firstcol2[1025], nfirst2=0;	/* 1st sp2 col containing set pixel*/
        !          1523: int	blanksignal = (-991234);	/*rastsquash signal right-hand blank*/
        !          1524: int	smin=9999, xmin=9999,ymin=9999;	/* min separation (s=x+y) */
        !          1525: int	type_raster();			/* display debugging output */
        !          1526: /* -------------------------------------------------------------------------
        !          1527: find right edge of sp1 and left edge of sp2 (these will be abutting edges)
        !          1528: -------------------------------------------------------------------------- */
        !          1529: /* --- check args --- */
        !          1530: if ( isstring ) goto end_of_job;	/* ignore string rasters */
        !          1531: if ( height > 1023 ) goto end_of_job;	/* don't try to squash huge image */
        !          1532: if ( sp2->type == blanksignal )		/*blanksignal was propagated to us*/
        !          1533:   goto end_of_job;			/* don't squash intentional blank */
        !          1534: /* --- init firstcol1[], firstcol2[] --- */
        !          1535: for ( irow1=0; irow1<height; irow1++ )	/* for each row */
        !          1536:   firstcol1[irow1] = firstcol2[irow1] = blanksignal; /* signal empty rows */
        !          1537: /* --- set firstcol2[] indicating left edge of sp2 --- */
        !          1538: for ( irow2=top2; irow2<=bot2; irow2++ ) /* for each row inside sp2 */
        !          1539:   for ( icol=0; icol<width2; icol++ )	/* find first non-empty col in row */
        !          1540:     if ( getpixel(sp2->image,irow2-top2,icol) != 0 ) /* found a set pixel */
        !          1541:       {	firstcol2[irow2] = icol;	/* icol is #cols from left edge */
        !          1542: 	nfirst2++;			/* bump #rows containing set pixels*/
        !          1543: 	break; }			/* and go on to next row */
        !          1544: if ( nfirst2 < 1 )			/*right-hand sp2 is completely blank*/
        !          1545:   { nsquash = blanksignal;		/* signal intentional blanks */
        !          1546:     goto end_of_job; }			/* don't squash intentional blanks */
        !          1547: /* --- now check if preceding image in sp1 was an intentional blank --- */
        !          1548: if ( sp1->type == blanksignal )		/*blanksignal was propagated to us*/
        !          1549:   goto end_of_job;			/* don't squash intentional blank */
        !          1550: /* --- set firstcol1[] indicating right edge of sp1 --- */
        !          1551: for ( irow1=top1; irow1<=bot1; irow1++ ) /* for each row inside sp1 */
        !          1552:   for ( icol=width1-1; icol>=0; icol-- ) /* find last non-empty col in row */
        !          1553:     if ( getpixel(sp1->image,irow1-top1,icol) != 0 ) /* found a set pixel */
        !          1554:       {	firstcol1[irow1] = (width1-1)-icol; /* save #cols from right edge */
        !          1555: 	nfirst1++;			/* bump #rows containing set pixels*/
        !          1556: 	break; }			/* and go on to next row */
        !          1557: if ( nfirst1 < 1 )			/*left-hand sp1 is completely blank*/
        !          1558:   goto end_of_job;			/* don't squash intentional blanks */
        !          1559: /* -------------------------------------------------------------------------
        !          1560: find minimum separation
        !          1561: -------------------------------------------------------------------------- */
        !          1562: for ( irow2=top2; irow2<=bot2; irow2++ ) { /* check each row inside sp2 */
        !          1563:  int margin1, margin2=firstcol2[irow2];	/* #cols to first set pixel */
        !          1564:  if ( margin2 != blanksignal )		/* irow2 not an empty/blank row */
        !          1565:   for ( irow1=max2(irow2-smin,top1); ; irow1++ )
        !          1566:    if ( irow1 > min2(irow2+smin,bot1) ) break; /* upper bound check */
        !          1567:    else
        !          1568:     if ( (margin1=firstcol1[irow1]) != blanksignal ) { /*have non-blank row*/
        !          1569:      int dx=(margin1+margin2), dy=absval(irow2-irow1), ds=dx+dy; /* deltas */
        !          1570:      if ( ds >= smin ) continue;	/* min unchanged */
        !          1571:      if ( dy>squashmargin && dx<xmin && smin<9999 ) continue; /* dy alone */
        !          1572:      smin=ds; xmin=dx; ymin=dy;		/* set new min */
        !          1573:      } /* --- end-of-if(margin1!=blanksignal) --- */
        !          1574:  if ( smin<2 ) goto end_of_job;		/* can't squash */
        !          1575:  } /* --- end-of-for(irow2) --- */
        !          1576: /*nsquash = min2(xmin,width2);*/	/* permissible squash */
        !          1577: nsquash = xmin;				/* permissible squash */
        !          1578: /* -------------------------------------------------------------------------
        !          1579: Back to caller with #pixels to squash sp1||sp2
        !          1580: -------------------------------------------------------------------------- */
        !          1581: end_of_job:
        !          1582:   /* --- debugging output --- */
        !          1583:   if ( msgfp!=NULL && msglevel >= 99 )	/* display for debugging */
        !          1584:     { fprintf(msgfp,"rastsquash> nsquash=%d, squashmargin=%d\n",
        !          1585:       nsquash,squashmargin);
        !          1586:       if ( msglevel >= 999 )		/* also display rasters */
        !          1587: 	{ fprintf(msgfp,"rastsquash>left-hand image...\n");
        !          1588: 	  if(sp1!=NULL) type_raster(sp1->image,msgfp); /* left image */
        !          1589: 	  fprintf(msgfp,"rastsquash>right-hand image...\n");
        !          1590: 	  if(sp2!=NULL) type_raster(sp2->image,msgfp); } /* right image */
        !          1591:       fflush(msgfp); }
        !          1592:   return ( nsquash );			/* back with #squash pixels */
        !          1593: } /* --- end-of-function rastsquash() --- */
        !          1594: 
        !          1595: 
        !          1596: /* ==========================================================================
        !          1597:  * Function:	accent_subraster ( accent, width, height, pixsz )
        !          1598:  * Purpose:	Allocate a new subraster of width x height
        !          1599:  *		(or maybe different dimensions, depending on accent),
        !          1600:  *		and draw an accent (\hat or \vec or \etc) that fills it
        !          1601:  * --------------------------------------------------------------------------
        !          1602:  * Arguments:	accent (I)	int containing either HATACCENT or VECACCENT,
        !          1603:  *				etc, indicating the type of accent desired
        !          1604:  *		width (I)	int containing desired width of accent (#cols)
        !          1605:  *		height (I)	int containing desired height of accent(#rows)
        !          1606:  *		pixsz (I)	int containing 1 for bitmap, 8 for bytemap
        !          1607:  * --------------------------------------------------------------------------
        !          1608:  * Returns:	( subraster * )	ptr to newly-allocated subraster with accent,
        !          1609:  *				or NULL for any error.
        !          1610:  * --------------------------------------------------------------------------
        !          1611:  * Notes:     o	Some accents have internally-determined dimensions,
        !          1612:  *		and caller should check dimensions in returned subraster
        !          1613:  * ======================================================================= */
        !          1614: /* --- entry point --- */
        !          1615: subraster *accent_subraster (  int accent, int width, int height, int pixsz )
        !          1616: {
        !          1617: /* -------------------------------------------------------------------------
        !          1618: Allocations and Declarations
        !          1619: -------------------------------------------------------------------------- */
        !          1620: /* --- general info --- */
        !          1621: raster	*new_raster(), *rp=NULL;	/*raster containing desired accent*/
        !          1622: subraster *new_subraster(), *sp=NULL;	/* subraster returning accent */
        !          1623: int	delete_raster(), delete_subraster(); /*free allocated raster on err*/
        !          1624: int	line_raster(),			/* draws lines */
        !          1625: 	thickness = 1;			/* line thickness */
        !          1626: /*int	pixval = (pixsz==1? 1 : (pixsz==8?255:(-1)));*/ /*black pixel value*/
        !          1627: /* --- other working info --- */
        !          1628: int	col0, col1,			/* cols for line */
        !          1629: 	row0, row1;			/* rows for line */
        !          1630: subraster *get_delim(), *accsp=NULL;	/*find suitable cmex10 symbol/accent*/
        !          1631: /* --- info for under/overbraces, tildes, etc --- */
        !          1632: char	brace[16];			/*"{" for over, "}" for under, etc*/
        !          1633: raster	*rastrot(),			/* rotate { for overbrace, etc */
        !          1634: 	*rastcpy();			/* may need copy of original */
        !          1635: subraster *arrow_subraster();		/* rightarrow for vec */
        !          1636: subraster *rastack();			/* stack accent atop extra space */
        !          1637: /* -------------------------------------------------------------------------
        !          1638: outer switch() traps accents that may change caller's height,width
        !          1639: -------------------------------------------------------------------------- */
        !          1640: switch ( accent )
        !          1641:  {
        !          1642:  default:
        !          1643:   /* -----------------------------------------------------------------------
        !          1644:   inner switch() first allocates fixed-size raster for accents that don't
        !          1645:   ------------------------------------------------------------------------ */
        !          1646:   if ( (rp = new_raster(width,height,pixsz)) /* allocate fixed-size raster */
        !          1647:   !=   NULL )				/* and if we succeeded... */
        !          1648:    switch ( accent )			/* ...draw requested accent in it */
        !          1649:     {
        !          1650:     /* --- unrecognized request --- */
        !          1651:     default: delete_raster(rp);		/* unrecognized accent requested */
        !          1652: 	rp = NULL;  break;		/* so free raster and signal error */
        !          1653:     /* --- bar request --- */
        !          1654:     case UNDERBARACCENT:
        !          1655:     case BARACCENT:
        !          1656: 	thickness = height-1;		/* adjust thickness */
        !          1657: 	if ( accent == BARACCENT )	/* bar is above expression */
        !          1658: 	  line_raster(rp,0,0,0,width-1,thickness); /*leave blank line at bot*/
        !          1659: 	else				/* underbar is below expression */
        !          1660: 	  line_raster(rp,1,0,1,width-1,thickness); /*leave blank line at top*/
        !          1661: 	break;
        !          1662:     /* --- dot request --- */
        !          1663:     case DOTACCENT:
        !          1664: 	thickness = height-1;		/* adjust thickness */
        !          1665: 	line_raster(rp,0,width/2,1,(width/2)+1,thickness); /* centered dot */
        !          1666: 	break;
        !          1667:     /* --- ddot request --- */
        !          1668:     case DDOTACCENT:
        !          1669: 	thickness = height-1;		/* adjust thickness */
        !          1670: 	col0 = max2(width/3-(thickness-1),0);	/* one-third of width */
        !          1671: 	col1 = min2((2*width)/3+(thickness-1),width-thickness); /*two thirds*/
        !          1672: 	line_raster(rp,0,col0,1,col0+1,thickness); /* set a dot at 1st third*/
        !          1673: 	line_raster(rp,0,col1,1,col1+1,thickness); /* and another at 2nd */
        !          1674: 	break;
        !          1675:     /* --- hat request --- */
        !          1676:     case HATACCENT:
        !          1677: 	thickness = (width<=12? 2 : 3);	/* adjust thickness */
        !          1678: 	line_raster(rp,height-1,0,0,width/2,thickness);    /* / part of hat*/
        !          1679: 	line_raster(rp,0,(width-1)/2,height-1,width-1,thickness); /* \ part*/
        !          1680: 	break;
        !          1681:     /* --- sqrt request --- */
        !          1682:     case SQRTACCENT:
        !          1683: 	col1 = SQRTWIDTH(height) - 1;	/* right col of sqrt symbol */
        !          1684: 	col0 = (col1+2)/3;		/* midpoint col of sqrt */
        !          1685: 	row0 = (height+1)/2;		/* midpoint row of sqrt */
        !          1686: 	row1 = height-1;		/* bottom row of sqrt */
        !          1687: 	line_raster(rp,row0,0,row1,col0,thickness); /* descending portion */
        !          1688: 	line_raster(rp,row1,col0,0,col1,thickness); /* ascending portion */
        !          1689: 	line_raster(rp,0,col1,0,width-1,thickness); /*overbar of thickness 1*/
        !          1690: 	break;
        !          1691:     } /* --- end-of-inner-switch(accent) --- */
        !          1692:     break;				/* break from outer accent switch */
        !          1693:  /* --- underbrace, overbrace request --- */
        !          1694:  case UNDERBRACE:
        !          1695:  case OVERBRACE:
        !          1696:     if ( accent == UNDERBRACE ) strcpy(brace,"}"); /* start with } brace */
        !          1697:     if ( accent ==  OVERBRACE ) strcpy(brace,"{"); /* start with { brace */
        !          1698:     if ( (accsp=get_delim(brace,width,CMEX10)) /* use width for height */
        !          1699:     !=  NULL )				/* found desired brace */
        !          1700:       { rp = rastrot(accsp->image);	/* rotate 90 degrees clockwise */
        !          1701: 	delete_subraster(accsp); }	/* and free subraster "envelope" */
        !          1702:     break;
        !          1703:  /* --- hat request --- */
        !          1704:  case HATACCENT:
        !          1705:     if ( accent == HATACCENT ) strcpy(brace,"<"); /* start with < */
        !          1706:     if ( (accsp=get_delim(brace,width,CMEX10)) /* use width for height */
        !          1707:     !=  NULL )				/* found desired brace */
        !          1708:       { rp = rastrot(accsp->image);	/* rotate 90 degrees clockwise */
        !          1709: 	delete_subraster(accsp); }	/* and free subraster "envelope" */
        !          1710:     break;
        !          1711:  /* --- vec request --- */
        !          1712:  case VECACCENT:
        !          1713:     height = 2*(height/2) + 1;		/* force height odd */
        !          1714:     if ( (accsp=arrow_subraster(width,height,pixsz,1,0)) /*build rightarrow*/
        !          1715:     !=  NULL )				/* succeeded */
        !          1716: 	{ rp = accsp->image;		/* "extract" raster with bitmap */
        !          1717: 	  free((void *)accsp); }	/* and free subraster "envelope" */
        !          1718:     break;
        !          1719:  /* --- tilde request --- */
        !          1720:  case TILDEACCENT:
        !          1721:     accsp=(width<25? get_delim("\\sim",-width,CMSY10) :
        !          1722: 		     get_delim("~",-width,CMEX10)); /*width search for tilde*/
        !          1723:     if ( accsp !=  NULL )		/* found desired tilde */
        !          1724:       if ( (sp=rastack(new_subraster(1,1,pixsz),accsp,1,0,1,3))/*space below*/
        !          1725:       !=  NULL )			/* have tilde with space below it */
        !          1726: 	{ rp = sp->image;		/* "extract" raster with bitmap */
        !          1727: 	  free((void *)sp); }		/* and free subraster "envelope" */
        !          1728:     break;
        !          1729:  } /* --- end-of-outer-switch(accent) --- */
        !          1730: /* -------------------------------------------------------------------------
        !          1731: if we constructed accent raster okay, embed it in a subraster and return it
        !          1732: -------------------------------------------------------------------------- */
        !          1733: /* --- if all okay, allocate subraster to contain constructed raster --- */
        !          1734: if ( rp != NULL )			/* accent raster constructed okay */
        !          1735:   if ( (sp=new_subraster(0,0,0))	/* allocate subraster "envelope" */
        !          1736:   ==   NULL )				/* and if we fail to allocate */
        !          1737:     delete_raster(rp);			/* free now-unneeded raster */
        !          1738:   else					/* subraster allocated okay */
        !          1739:     { /* --- init subraster parameters, embedding raster in it --- */
        !          1740:       sp->type = IMAGERASTER;		/* constructed image */
        !          1741:       sp->image = rp;			/* raster we just constructed */
        !          1742:       sp->size = (-1);			/* can't set font size here */
        !          1743:       sp->baseline = 0; }		/* can't set baseline here */
        !          1744: /* --- return subraster containing desired accent to caller --- */
        !          1745: return ( sp );				/* return accent or NULL to caller */
        !          1746: } /* --- end-of-function accent_subraster() --- */
        !          1747: 
        !          1748: 
        !          1749: /* ==========================================================================
        !          1750:  * Function:	arrow_subraster ( width, height, pixsz, drctn, isBig )
        !          1751:  * Purpose:	Allocate a raster/subraster and draw left/right arrow in it
        !          1752:  * --------------------------------------------------------------------------
        !          1753:  * Arguments:	width (I)	int containing number of cols for arrow
        !          1754:  *		height (I)	int containing number of rows for arrow
        !          1755:  *		pixsz (I)	int containing 1 for bitmap, 8 for bytemap
        !          1756:  *		drctn (I)	int containing +1 for right arrow,
        !          1757:  *				or -1 for left, 0 for leftright
        !          1758:  *		isBig (I)	int containing 1/true for \Long arrows,
        !          1759:  *				or false for \long arrows, i.e.,
        !          1760:  *				true for ===> or false for --->.
        !          1761:  * --------------------------------------------------------------------------
        !          1762:  * Returns:	( subraster * )	ptr to constructed left/right arrow
        !          1763:  *				or NULL for any error.
        !          1764:  * --------------------------------------------------------------------------
        !          1765:  * Notes:     o
        !          1766:  * ======================================================================= */
        !          1767: /* --- entry point --- */
        !          1768: subraster *arrow_subraster ( int width, int height, int pixsz,
        !          1769: 				int drctn, int isBig )
        !          1770: {
        !          1771: /* -------------------------------------------------------------------------
        !          1772: Allocations and Declarations
        !          1773: -------------------------------------------------------------------------- */
        !          1774: subraster *new_subraster(), *arrowsp=NULL; /* allocate arrow subraster */
        !          1775: int	rule_raster();			/* draw arrow line */
        !          1776: int	irow, midrow=height/2;		/* index, midrow is arrowhead apex */
        !          1777: int	icol, thickness=(height>15?2:1); /* arrowhead thickness and index */
        !          1778: int	pixval = (pixsz==1? 1 : (pixsz==8?255:(-1))); /* black pixel value */
        !          1779: int	ipix,				/* raster pixmap[] index */
        !          1780: 	npix = width*height;		/* #pixels malloced in pixmap[] */
        !          1781: /* -------------------------------------------------------------------------
        !          1782: allocate raster/subraster and draw arrow line
        !          1783: -------------------------------------------------------------------------- */
        !          1784: if ( height < 3 ) { height=3; midrow=1; }	/* set minimum height */
        !          1785: if ( (arrowsp=new_subraster(width,height,pixsz)) /* allocate empty raster */
        !          1786: ==   NULL ) goto end_of_job;			/* and quit if failed */
        !          1787: if ( !isBig )					/* single line */
        !          1788:   rule_raster(arrowsp->image,midrow,0,width,1,0); /*draw line across midrow*/
        !          1789: else
        !          1790:   { int	delta = (width>6? (height>15? 3: (height>7? 2 : 1)) : 1);
        !          1791:     rule_raster(arrowsp->image,midrow-delta,delta,width-2*delta,1,0);
        !          1792:     rule_raster(arrowsp->image,midrow+delta,delta,width-2*delta,1,0); }
        !          1793: /* -------------------------------------------------------------------------
        !          1794: construct arrowhead(s)
        !          1795: -------------------------------------------------------------------------- */
        !          1796: for ( irow=0; irow<height; irow++ )		/* for each row of arrow */
        !          1797:   {
        !          1798:   int	delta = abs(irow-midrow);		/*arrowhead offset for irow*/
        !          1799:   /* --- right arrowhead --- */
        !          1800:   if ( drctn >= 0 )				/* right arrowhead wanted */
        !          1801:     for ( icol=0; icol<thickness; icol++ )	/* for arrowhead thickness */
        !          1802:      { ipix = ((irow+1)*width - 1) - delta - icol; /* rightmost-delta-icol */
        !          1803:        if ( ipix >= 0 )				/* bounds check */
        !          1804: 	if ( pixsz == 1 )			/* have a bitmap */
        !          1805: 	  setlongbit((arrowsp->image)->pixmap,ipix);/*turn on arrowhead bit*/
        !          1806: 	else					/* should have a bytemap */
        !          1807: 	 if ( pixsz == 8 )			/* check pixsz for bytemap */
        !          1808: 	  ((arrowsp->image)->pixmap)[ipix] = pixval; } /*set arrowhead byte*/
        !          1809:   /* --- left arrowhead (same as right except for ipix calculation) --- */
        !          1810:   if ( drctn <= 0 )				/* left arrowhead wanted */
        !          1811:     for ( icol=0; icol<thickness; icol++ )	/* for arrowhead thickness */
        !          1812:      { ipix = irow*width + delta + icol;	/* leftmost bit+delta+icol */
        !          1813:        if ( ipix < npix )			/* bounds check */
        !          1814: 	if ( pixsz == 1 )			/* have a bitmap */
        !          1815: 	  setlongbit((arrowsp->image)->pixmap,ipix);/*turn on arrowhead bit*/
        !          1816: 	else					/* should have a bytemap */
        !          1817: 	 if ( pixsz == 8 )			/* check pixsz for bytemap */
        !          1818: 	  ((arrowsp->image)->pixmap)[ipix] = pixval; } /*set arrowhead byte*/
        !          1819:   } /* --- end-of-for(irow) --- */
        !          1820: end_of_job:
        !          1821:   return ( arrowsp );			/*back to caller with arrow or NULL*/
        !          1822: } /* --- end-of-function arrow_subraster() --- */
        !          1823: 
        !          1824: 
        !          1825: /* ==========================================================================
        !          1826:  * Function:	uparrow_subraster ( width, height, pixsz, drctn, isBig )
        !          1827:  * Purpose:	Allocate a raster/subraster and draw up/down arrow in it
        !          1828:  * --------------------------------------------------------------------------
        !          1829:  * Arguments:	width (I)	int containing number of cols for arrow
        !          1830:  *		height (I)	int containing number of rows for arrow
        !          1831:  *		pixsz (I)	int containing 1 for bitmap, 8 for bytemap
        !          1832:  *		drctn (I)	int containing +1 for up arrow,
        !          1833:  *				or -1 for down, or 0 for updown
        !          1834:  *		isBig (I)	int containing 1/true for \Long arrows,
        !          1835:  *				or false for \long arrows, i.e.,
        !          1836:  *				true for ===> or false for --->.
        !          1837:  * --------------------------------------------------------------------------
        !          1838:  * Returns:	( subraster * )	ptr to constructed up/down arrow
        !          1839:  *				or NULL for any error.
        !          1840:  * --------------------------------------------------------------------------
        !          1841:  * Notes:     o
        !          1842:  * ======================================================================= */
        !          1843: /* --- entry point --- */
        !          1844: subraster *uparrow_subraster ( int width, int height, int pixsz,
        !          1845: 					int drctn, int isBig )
        !          1846: {
        !          1847: /* -------------------------------------------------------------------------
        !          1848: Allocations and Declarations
        !          1849: -------------------------------------------------------------------------- */
        !          1850: subraster *new_subraster(), *arrowsp=NULL; /* allocate arrow subraster */
        !          1851: int	rule_raster();			/* draw arrow line */
        !          1852: int	icol, midcol=width/2;		/* index, midcol is arrowhead apex */
        !          1853: int	irow, thickness=(width>15?2:1);	/* arrowhead thickness and index */
        !          1854: int	pixval = (pixsz==1? 1 : (pixsz==8?255:(-1))); /* black pixel value */
        !          1855: int	ipix,				/* raster pixmap[] index */
        !          1856: 	npix = width*height;		/* #pixels malloced in pixmap[] */
        !          1857: /* -------------------------------------------------------------------------
        !          1858: allocate raster/subraster and draw arrow line
        !          1859: -------------------------------------------------------------------------- */
        !          1860: if ( width < 3 ) { width=3; midcol=1; }		/* set minimum width */
        !          1861: if ( (arrowsp=new_subraster(width,height,pixsz)) /* allocate empty raster */
        !          1862: ==   NULL ) goto end_of_job;			/* and quit if failed */
        !          1863: if ( !isBig )					/* single line */
        !          1864:   rule_raster(arrowsp->image,0,midcol,1,height,0); /*draw line down midcol*/
        !          1865: else
        !          1866:   { int	delta = (height>6? (width>15? 3: (width>7? 2 : 1)) : 1);
        !          1867:     rule_raster(arrowsp->image,delta,midcol-delta,1,height-2*delta,0);
        !          1868:     rule_raster(arrowsp->image,delta,midcol+delta,1,height-2*delta,0); }
        !          1869: /* -------------------------------------------------------------------------
        !          1870: construct arrowhead(s)
        !          1871: -------------------------------------------------------------------------- */
        !          1872: for ( icol=0; icol<width; icol++ )		/* for each col of arrow */
        !          1873:   {
        !          1874:   int	delta = abs(icol-midcol);		/*arrowhead offset for icol*/
        !          1875:   /* --- up arrowhead --- */
        !          1876:   if ( drctn >= 0 )				/* up arrowhead wanted */
        !          1877:     for ( irow=0; irow<thickness; irow++ )	/* for arrowhead thickness */
        !          1878:      { ipix = (irow+delta)*width + icol;	/* leftmost+icol */
        !          1879:        if ( ipix < npix )			/* bounds check */
        !          1880: 	if ( pixsz == 1 )			/* have a bitmap */
        !          1881: 	  setlongbit((arrowsp->image)->pixmap,ipix);/*turn on arrowhead bit*/
        !          1882: 	else					/* should have a bytemap */
        !          1883: 	 if ( pixsz == 8 )			/* check pixsz for bytemap */
        !          1884: 	  ((arrowsp->image)->pixmap)[ipix] = pixval; } /*set arrowhead byte*/
        !          1885:   /* --- down arrowhead (same as up except for ipix calculation) --- */
        !          1886:   if ( drctn <= 0 )				/* down arrowhead wanted */
        !          1887:     for ( irow=0; irow<thickness; irow++ )	/* for arrowhead thickness */
        !          1888:      { ipix = (height-1-delta-irow)*width + icol; /* leftmost + icol */
        !          1889:        if ( ipix > 0 )				/* bounds check */
        !          1890: 	if ( pixsz == 1 )			/* have a bitmap */
        !          1891: 	  setlongbit((arrowsp->image)->pixmap,ipix);/*turn on arrowhead bit*/
        !          1892: 	else					/* should have a bytemap */
        !          1893: 	 if ( pixsz == 8 )			/* check pixsz for bytemap */
        !          1894: 	  ((arrowsp->image)->pixmap)[ipix] = pixval; } /*set arrowhead byte*/
        !          1895:   } /* --- end-of-for(irow) --- */
        !          1896: end_of_job:
        !          1897:   return ( arrowsp );			/*back to caller with arrow or NULL*/
        !          1898: } /* --- end-of-function uparrow_subraster() --- */
        !          1899: 
        !          1900: 
        !          1901: /* ==========================================================================
        !          1902:  * Function:	rule_raster ( rp, top, left, width, height, type )
        !          1903:  * Purpose:	Draw a solid or dashed line (or box) in existing raster rp,
        !          1904:  *		starting at top,left with dimensions width,height.
        !          1905:  * --------------------------------------------------------------------------
        !          1906:  * Arguments:	rp (I)		raster *  to raster in which rule
        !          1907:  *				will be drawn
        !          1908:  *		top (I)		int containing row at which top-left corner
        !          1909:  *				of rule starts (0 is topmost)
        !          1910:  *		left (I)	int containing col at which top-left corner
        !          1911:  *				of rule starts (0 is leftmost)
        !          1912:  *		width (I)	int containing number of cols for rule
        !          1913:  *		height (I)	int containing number of rows for rule
        !          1914:  *		type (I)	int containing 0 for solid rule,
        !          1915:  *				1 for horizontal dashes, 2 for vertical
        !          1916:  * --------------------------------------------------------------------------
        !          1917:  * Returns:	( int )		1 if rule drawn okay,
        !          1918:  *				or 0 for any error.
        !          1919:  * --------------------------------------------------------------------------
        !          1920:  * Notes:     o	Rule line is implicitly "horizontal" or "vertical" depending
        !          1921:  *		on relative width,height dimensions.  It's a box if they're
        !          1922:  *		more or less comparable.
        !          1923:  * ======================================================================= */
        !          1924: /* --- entry point --- */
        !          1925: int	rule_raster ( raster *rp, int top, int left,
        !          1926: 		int width, int height, int type )
        !          1927: {
        !          1928: /* -------------------------------------------------------------------------
        !          1929: Allocations and Declarations
        !          1930: -------------------------------------------------------------------------- */
        !          1931: int	irow, icol;		/* indexes over rp raster */
        !          1932: int	ipix,			/* raster pixmap[] index */
        !          1933: 	npix = rp->width * rp->height; /* #pixels malloced in rp->pixmap[] */
        !          1934: int	isfatal = 0;		/* true to abend on out-of-bounds error */
        !          1935: int	hdash=1, vdash=2;	/* type for horizontal, vertical dashes */
        !          1936: int	dashlen=3, spacelen=2,	/* #pixels for dash followed by space */
        !          1937: 	isdraw=1;		/* true when drawing dash (init for solid) */
        !          1938: /* -------------------------------------------------------------------------
        !          1939: Check args
        !          1940: -------------------------------------------------------------------------- */
        !          1941: if ( rp == (raster *)NULL )	/* no raster arg supplied */
        !          1942:   if ( workingbox != (subraster *)NULL )  /* see if we have a workingbox */
        !          1943:     rp = workingbox->image;	/* use workingbox if possible */
        !          1944:   else return ( 0 );		/* otherwise signal error to caller */
        !          1945: /* -------------------------------------------------------------------------
        !          1946: Fill line/box
        !          1947: -------------------------------------------------------------------------- */
        !          1948: for ( irow=top; irow<top+height; irow++ ) /*each scan line*/
        !          1949:   {
        !          1950:   if ( type == vdash )				/*set isdraw for vert dash*/
        !          1951:     isdraw = (((irow-top)%(dashlen+spacelen)) < dashlen);
        !          1952:   ipix = irow*rp->width + left - 1;		/*first pixel preceding icol*/
        !          1953:   for ( icol=left; icol<left+width; icol++ )	/* each pixel in scan line */
        !          1954:     {
        !          1955:     if ( type == hdash )			/*set isdraw for horiz dash*/
        !          1956:       isdraw = (((icol-left)%(dashlen+spacelen)) < dashlen);
        !          1957:     if ( ++ipix >= npix )			/* bounds check failed */
        !          1958:          if ( isfatal ) goto end_of_job;	/* abort if error is fatal */
        !          1959:          else break;				/*or just go on to next row*/
        !          1960:     else					/*ibit is within rp bounds*/
        !          1961:       if ( isdraw )				/*and we're drawing this bit*/
        !          1962: 	if ( rp->pixsz == 1 )			/* have a bitmap */
        !          1963: 	  setlongbit(rp->pixmap,ipix);		/* so turn on bit in line */
        !          1964: 	else					/* should have a bytemap */
        !          1965: 	 if ( rp->pixsz == 8 )			/* check pixsz for bytemap */
        !          1966: 	  ((unsigned char *)(rp->pixmap))[ipix] = 255; /* set black byte */
        !          1967:     } /* --- end-of-for(icol) --- */
        !          1968:   } /* --- end-of-for(irow) --- */
        !          1969: end_of_job:
        !          1970:   return ( isfatal? (ipix<npix? 1:0) : 1 );
        !          1971: } /* --- end-of-function rule_raster() --- */
        !          1972: 
        !          1973: 
        !          1974: /* ==========================================================================
        !          1975:  * Function:	line_raster ( rp,  row0, col0,  row1, col1,  thickness )
        !          1976:  * Purpose:	Draw a line from row0,col0 to row1,col1 of thickness
        !          1977:  *		in existing raster rp.
        !          1978:  * --------------------------------------------------------------------------
        !          1979:  * Arguments:	rp (I)		raster *  to raster in which a line
        !          1980:  *				will be drawn
        !          1981:  *		row0 (I)	int containing row at which
        !          1982:  *				line will start (0 is topmost)
        !          1983:  *		col0 (I)	int containing col at which
        !          1984:  *				line will start (0 is leftmost)
        !          1985:  *		row1 (I)	int containing row at which
        !          1986:  *				line will end (rp->height-1 is bottom-most)
        !          1987:  *		col1 (I)	int containing col at which
        !          1988:  *				line will end (rp->width-1 is rightmost)
        !          1989:  *		thickness (I)	int containing number of pixels/bits
        !          1990:  *				thick the line will be
        !          1991:  * --------------------------------------------------------------------------
        !          1992:  * Returns:	( int )		1 if line drawn okay,
        !          1993:  *				or 0 for any error.
        !          1994:  * --------------------------------------------------------------------------
        !          1995:  * Notes:     o	if row0==row1, a horizontal line is drawn
        !          1996:  *		between col0 and col1, with row0(==row1) the top row
        !          1997:  *		and row0+(thickness-1) the bottom row
        !          1998:  *	      o	if col0==col1, a vertical bar is drawn
        !          1999:  *		between row0 and row1, with col0(==col1) the left col
        !          2000:  *		and col0+(thickness-1) the right col
        !          2001:  *	      o	if both the above, you get a square thickness x thickness
        !          2002:  *		whose top-left corner is row0,col0.
        !          2003:  * ======================================================================= */
        !          2004: /* --- entry point --- */
        !          2005: int	line_raster ( raster *rp, int row0, int col0,
        !          2006: 	int row1, int col1, int thickness )
        !          2007: {
        !          2008: /* -------------------------------------------------------------------------
        !          2009: Allocations and Declarations
        !          2010: -------------------------------------------------------------------------- */
        !          2011: int	irow, icol,		/* indexes over rp raster */
        !          2012: 	locol=col0, hicol=col1,	/* col limits at irow */
        !          2013: 	lorow=row0, hirow=row1;	/* row limits at icol */
        !          2014: int	ipix,			/* raster pixmap[] index */
        !          2015: 	npix = rp->width * rp->height; /* #pixels malloced in rp->pixmap[] */
        !          2016: int	isfatal = 0;		/* true to abend on out-of-bounds error */
        !          2017: int	isline=(row1==row0), isbar=(col1==col0); /*true if slope a=0,\infty*/
        !          2018: double	dy = row1-row0 /* + (row1>=row0? +1.0 : -1.0) */, /* delta-x */
        !          2019: 	dx = col1-col0 /* + (col1>=col0? +1.0 : -1.0) */, /* delta-y */
        !          2020: 	a= (isbar||isline? 0.0 : dy/dx), /* slope = tan(theta) = dy/dx */
        !          2021: 	xcol, xrow;		/* calculated col at irow, or row at icol */
        !          2022: double	ar = ASPECTRATIO,	/* aspect ratio width/height of one pixel */
        !          2023: 	xwidth= (isline? 0.0 :	/*#pixels per row to get sloped line thcknss*/
        !          2024: 		((double)thickness)*sqrt((dx*dx)+(dy*dy*ar*ar))/fabs(dy*ar)),
        !          2025: 	xheight = 1.0;
        !          2026: int	line_recurse(), isrecurse=1; /* true to draw line recursively */
        !          2027: /* -------------------------------------------------------------------------
        !          2028: Check args
        !          2029: -------------------------------------------------------------------------- */
        !          2030: if ( rp == (raster *)NULL )	/* no raster arg supplied */
        !          2031:   if ( workingbox != (subraster *)NULL )  /* see if we have a workingbox */
        !          2032:     rp = workingbox->image;	/* use workingbox if possible */
        !          2033:   else return ( 0 );		/* otherwise signal error to caller */
        !          2034: /* -------------------------------------------------------------------------
        !          2035: Initialization
        !          2036: -------------------------------------------------------------------------- */
        !          2037: if ( msgfp!=NULL && msglevel>=29 )		/* debugging */
        !          2038:    fprintf(msgfp,"line_raster> row,col0=%d,%d row,col1=%d,%d, thickness=%d\n"
        !          2039:    "\t dy,dx=%3.1f,%3.1f, a=%4.3f, xwidth=%4.3f\n",
        !          2040:    row0,col0, row1,col1, thickness,  dy,dx, a, xwidth);
        !          2041: /* --- check for recursive line drawing --- */
        !          2042: if ( isrecurse )		/* drawing lines recursively */
        !          2043:   { line_recurse(rp,(double)row0,(double)col0,
        !          2044:     (double)row1,(double)col1,thickness);
        !          2045:     return ( 1 ); }
        !          2046: /* --- set params for horizontal line or vertical bar --- */
        !          2047: if ( isline )					/*interpret row as top row*/
        !          2048:   row1 = row0 + (thickness-1);			/* set bottom row for line */
        !          2049: if ( 0&&isbar )					/*interpret col as left col*/
        !          2050:   hicol = col0 + (thickness-1);			/* set right col for bar */
        !          2051: /* -------------------------------------------------------------------------
        !          2052: draw line one row at a time
        !          2053: -------------------------------------------------------------------------- */
        !          2054: for ( irow=min2(row0,row1); irow<=max2(row0,row1); irow++ ) /*each scan line*/
        !          2055:   {
        !          2056:   if ( !isbar && !isline )			/* neither vert nor horiz */
        !          2057:     { xcol  = col0 + ((double)(irow-row0))/a;	/* "middle" col in irow */
        !          2058:       locol = max2((int)(xcol-0.5*(xwidth-1.0)),0); /* leftmost col */
        !          2059:       hicol = min2((int)(xcol+0.5*(xwidth-0.0)),max2(col0,col1)); } /*right*/
        !          2060:   if ( msgfp!=NULL && msglevel>=29 )		/* debugging */
        !          2061:     fprintf(msgfp,"\t irow=%d, xcol=%4.2f, lo,hicol=%d,%d\n",
        !          2062:     irow,xcol,locol,hicol);
        !          2063:   ipix = irow*rp->width + min2(locol,hicol) - 1; /*first pix preceding icol*/
        !          2064:   for ( icol=min2(locol,hicol); icol<=max2(locol,hicol); icol++ ) /*each pix*/
        !          2065:     if ( ++ipix >= npix )			/* bounds check failed */
        !          2066: 	if ( isfatal ) goto end_of_job;	/* abort if error is fatal */
        !          2067: 	else break;				/*or just go on to next row*/
        !          2068:     else					/* turn on pixel in line */
        !          2069: 	if ( rp->pixsz == 1 )			/* have a pixel bitmap */
        !          2070: 	  setlongbit(rp->pixmap,ipix);		/* so turn on bit in line */
        !          2071: 	else					/* should have a bytemap */
        !          2072: 	 if ( rp->pixsz == 8 )			/* check pixsz for bytemap */
        !          2073: 	  ((unsigned char *)(rp->pixmap))[ipix] = 255; /* set black byte */
        !          2074:   } /* --- end-of-for(irow) --- */
        !          2075: /* -------------------------------------------------------------------------
        !          2076: now _redraw_ line one col at a time to avoid "gaps"
        !          2077: -------------------------------------------------------------------------- */
        !          2078: if ( 1 )
        !          2079:  for ( icol=min2(col0,col1); icol<=max2(col0,col1); icol++ )/*each scan line*/
        !          2080:   {
        !          2081:   if ( !isbar && !isline )			/* neither vert nor horiz */
        !          2082:     { xrow  = row0 + ((double)(icol-col0))*a;	/* "middle" row in icol */
        !          2083:       lorow = max2((int)(xrow-0.5*(xheight-1.0)),0); /* topmost row */
        !          2084:       hirow = min2((int)(xrow+0.5*(xheight-0.0)),max2(row0,row1)); } /*bot*/
        !          2085:   if ( msgfp!=NULL && msglevel>=29 )		/* debugging */
        !          2086:     fprintf(msgfp,"\t icol=%d, xrow=%4.2f, lo,hirow=%d,%d\n",
        !          2087:     icol,xrow,lorow,hirow);
        !          2088:   ipix = irow*rp->width + min2(locol,hicol) - 1; /*first pix preceding icol*/
        !          2089:   for ( irow=min2(lorow,hirow); irow<=max2(lorow,hirow); irow++ ) /*each pix*/
        !          2090:     if ( irow<0 || irow>=rp->height
        !          2091:     ||   icol<0 || icol>=rp->width )		/* bounds check */
        !          2092:       if ( isfatal ) goto end_of_job;		/* abort if error is fatal */
        !          2093:       else continue;				/*or just go on to next row*/
        !          2094:     else
        !          2095:       setpixel(rp,irow,icol,255);		/* set pixel at irow,icol */
        !          2096:   } /* --- end-of-for(irow) --- */
        !          2097: /* -------------------------------------------------------------------------
        !          2098: Back to caller with 1=okay, 0=failed.
        !          2099: -------------------------------------------------------------------------- */
        !          2100: end_of_job:
        !          2101:   return ( isfatal? (ipix<npix? 1:0) : 1 );
        !          2102: } /* --- end-of-function line_raster() --- */
        !          2103: 
        !          2104: 
        !          2105: /* ==========================================================================
        !          2106:  * Function:	line_recurse ( rp,  row0, col0,  row1, col1,  thickness )
        !          2107:  * Purpose:	Draw a line from row0,col0 to row1,col1 of thickness
        !          2108:  *		in existing raster rp.
        !          2109:  * --------------------------------------------------------------------------
        !          2110:  * Arguments:	rp (I)		raster *  to raster in which a line
        !          2111:  *				will be drawn
        !          2112:  *		row0 (I)	double containing row at which
        !          2113:  *				line will start (0 is topmost)
        !          2114:  *		col0 (I)	double containing col at which
        !          2115:  *				line will start (0 is leftmost)
        !          2116:  *		row1 (I)	double containing row at which
        !          2117:  *				line will end (rp->height-1 is bottom-most)
        !          2118:  *		col1 (I)	double containing col at which
        !          2119:  *				line will end (rp->width-1 is rightmost)
        !          2120:  *		thickness (I)	int containing number of pixels/bits
        !          2121:  *				thick the line will be
        !          2122:  * --------------------------------------------------------------------------
        !          2123:  * Returns:	( int )		1 if line drawn okay,
        !          2124:  *				or 0 for any error.
        !          2125:  * --------------------------------------------------------------------------
        !          2126:  * Notes:     o	Recurses, drawing left- and right-halves of line
        !          2127:  *		until a horizontal or vertical segment is found
        !          2128:  * ======================================================================= */
        !          2129: /* --- entry point --- */
        !          2130: int	line_recurse ( raster *rp, double row0, double col0,
        !          2131: 	double row1, double col1, int thickness )
        !          2132: {
        !          2133: /* -------------------------------------------------------------------------
        !          2134: Allocations and Declarations
        !          2135: -------------------------------------------------------------------------- */
        !          2136: double	delrow = fabs(row1-row0),	/* 0 if line horizontal */
        !          2137: 	delcol = fabs(col1-col0),	/* 0 if line vertical */
        !          2138: 	tolerance = 0.5;		/* draw line when it goes to point */
        !          2139: double	midrow = 0.5*(row0+row1),	/* midpoint row */
        !          2140: 	midcol = 0.5*(col0+col1);	/* midpoint col */
        !          2141: /* -------------------------------------------------------------------------
        !          2142: recurse if either delta > tolerance
        !          2143: -------------------------------------------------------------------------- */
        !          2144: if ( delrow > tolerance			/* row hasn't converged */
        !          2145: ||   delcol > tolerance )		/* col hasn't converged */
        !          2146:   { line_recurse(rp,row0,col0,midrow,midcol,thickness); /* left half */
        !          2147:     line_recurse(rp,midrow,midcol,row1,col1,thickness); /* right half */
        !          2148:     return ( 1 ); }
        !          2149: /* -------------------------------------------------------------------------
        !          2150: draw converged point
        !          2151: -------------------------------------------------------------------------- */
        !          2152: setpixel(rp,iround(midrow),iround(midcol),255); /*set pixel at midrow,midcol*/
        !          2153: return ( 1 );
        !          2154: } /* --- end-of-function line_recurse() --- */
        !          2155: 
        !          2156: 
        !          2157: /* ==========================================================================
        !          2158:  * Function:	circle_raster ( rp,  row0, col0,  row1, col1,
        !          2159:  *		thickness, quads )
        !          2160:  * Purpose:	Draw quad(rant)s of an ellipse in box determined by
        !          2161:  *		diagonally opposite corner points (row0,col0) and
        !          2162:  *		(row1,col1), of thickness pixels in existing raster rp.
        !          2163:  * --------------------------------------------------------------------------
        !          2164:  * Arguments:	rp (I)		raster *  to raster in which an ellipse
        !          2165:  *				will be drawn
        !          2166:  *		row0 (I)	int containing 1st corner row bounding ellipse
        !          2167:  *				(0 is topmost)
        !          2168:  *		col0 (I)	int containing 1st corner col bounding ellipse
        !          2169:  *				(0 is leftmost)
        !          2170:  *		row1 (I)	int containing 2nd corner row bounding ellipse
        !          2171:  *				(rp->height-1 is bottom-most)
        !          2172:  *		col1 (I)	int containing 2nd corner col bounding ellipse
        !          2173:  *				(rp->width-1 is rightmost)
        !          2174:  *		thickness (I)	int containing number of pixels/bits
        !          2175:  *				thick the ellipse arc line will be
        !          2176:  *		quads (I)	char * to null-terminated string containing
        !          2177:  *				any subset/combination of "1234" specifying
        !          2178:  *				which quadrant(s) of ellipse to draw.
        !          2179:  *				NULL ptr draws all four quadrants;
        !          2180:  *				otherwise 1=upper-right quadrant,
        !          2181:  *				2=uper-left, 3=lower-left, 4=lower-right,
        !          2182:  *				i.e., counterclockwise from 1=positive quad.
        !          2183:  * --------------------------------------------------------------------------
        !          2184:  * Returns:	( int )		1 if ellipse drawn okay,
        !          2185:  *				or 0 for any error.
        !          2186:  * --------------------------------------------------------------------------
        !          2187:  * Notes:     o	row0==row1 or col0==col1 are errors
        !          2188:  *	      o	using ellipse equation x^2/a^2 + y^2/b^2 = 1
        !          2189:  * ======================================================================= */
        !          2190: /* --- entry point --- */
        !          2191: int	circle_raster ( raster *rp, int row0, int col0,
        !          2192: 	int row1, int col1, int thickness, char *quads )
        !          2193: {
        !          2194: /* -------------------------------------------------------------------------
        !          2195: Allocations and Declarations
        !          2196: -------------------------------------------------------------------------- */
        !          2197: /* --- lower-left and upper-right bounding points (in our coords) --- */
        !          2198: int	lorow = min2(row0,row1),	/* lower bounding row (top of box) */
        !          2199: 	locol = min2(col0,col1),	/* lower bounding col (left of box)*/
        !          2200: 	hirow = max2(row0,row1),	/* upper bounding row (bot of box) */
        !          2201: 	hicol = max2(col0,col1);	/* upper bounding col (right of box)*/
        !          2202: /* --- a and b ellipse params --- */
        !          2203: int	width = hicol-locol+1,		/* width of bounding box */
        !          2204: 	height= hirow-lorow+1,		/* height of bounding box */
        !          2205: 	islandscape = (width>=height? 1:0); /*true if ellipse lying on side*/
        !          2206: double	a = ((double)width)/2.0,	/* x=a when y=0 */
        !          2207: 	b = ((double)height)/2.0,	/* y=b when x=0 */
        !          2208: 	abmajor = (islandscape? a : b),	/* max2(a,b) */
        !          2209: 	abminor = (islandscape? b : a),	/* min2(a,b) */
        !          2210: 	abmajor2 = abmajor*abmajor,	/* abmajor^2 */
        !          2211: 	abminor2 = abminor*abminor;	/* abminor^2 */
        !          2212: /* --- other stuff --- */
        !          2213: int	imajor=0, nmajor=max2(width,height), /*index, #pixels on major axis*/
        !          2214: 	iminor=0, nminor=min2(width,height); /* solved index on minor axis */
        !          2215: int	irow, icol,			/* raster indexes at circumference */
        !          2216: 	rsign=1, csign=1;		/* row,col signs, both +1 in quad 1*/
        !          2217: double	midrow= ((double)(row0+row1))/2.0, /* center row */
        !          2218: 	midcol= ((double)(col0+col1))/2.0; /* center col */
        !          2219: double	xy, xy2,			/* major axis ellipse coord */
        !          2220: 	yx2, yx;			/* solved minor ellipse coord */
        !          2221: int	isokay = 1;			/* true if no pixels out-of-bounds */
        !          2222: char	*qptr=NULL, *allquads="1234";	/* quadrants if input quads==NULL */
        !          2223: int	circle_recurse(), isrecurse=1;	/* true to draw ellipse recursively*/
        !          2224: /* -------------------------------------------------------------------------
        !          2225: pixel-by-pixel along positive major axis, quit when it goes negative
        !          2226: -------------------------------------------------------------------------- */
        !          2227: if ( quads == NULL ) quads = allquads;	/* draw all quads, or only user's */
        !          2228: if ( msgfp!=NULL && msglevel>=39 )	/* debugging */
        !          2229:   fprintf(msgfp,"circle_raster> width,height;quads=%d,%d,%s\n",
        !          2230:   width,height,quads);
        !          2231: if ( nmajor < 1 ) isokay = 0;		/* problem with input args */
        !          2232: else
        !          2233:  {
        !          2234:  if ( isrecurse )			/* use recursive algorithm */
        !          2235:   {
        !          2236:   for ( qptr=quads; *qptr!='\000'; qptr++ ) /* for each character in quads */
        !          2237:    {
        !          2238:    double theta0=0.0, theta1=0.0;	/* set thetas based on quadrant */
        !          2239:    switch ( *qptr )			/* check for quadrant 1,2,3,4 */
        !          2240:     { default:				/* unrecognized, assume quadrant 1 */
        !          2241:       case '1': theta0=  0.0; theta1= 90.0; break;   /* first quadrant */
        !          2242:       case '2': theta0= 90.0; theta1=180.0; break;   /* second quadrant */
        !          2243:       case '3': theta0=180.0; theta1=270.0; break;   /* third quadrant */
        !          2244:       case '4': theta0=270.0; theta1=360.0; break; } /* fourth quadrant */
        !          2245:    circle_recurse(rp,row0,col0,row1,col1,thickness,theta0,theta1);
        !          2246:    } /* --- end-of-for(qptr) --- */
        !          2247:   return ( 1 );
        !          2248:   } /* --- end-of-if(isrecurse) --- */
        !          2249:  for ( imajor=(nmajor+1)/2; ; imajor-- )
        !          2250:   {
        !          2251:   /* --- xy is coord along major axis, yx is "solved" along minor axis --- */
        !          2252:   xy  = ((double)imajor);		/* xy = abmajor ... 0 */
        !          2253:   if ( xy < 0.0 ) break;		/* negative side symmetrical */
        !          2254:   yx2 = abminor2*(1.0 - xy*xy/abmajor2); /* "solve" ellipse equation */
        !          2255:   yx  = (yx2>0.0? sqrt(yx2) : 0.0);	/* take sqrt if possible */
        !          2256:   iminor = iround(yx);			/* nearest integer */
        !          2257:   /* --- set pixels for each requested quadrant --- */
        !          2258:   for ( qptr=quads; *qptr!='\000'; qptr++ ) /* for each character in quads */
        !          2259:    {
        !          2260:    rsign = (-1);  csign = 1;		/* init row,col in user quadrant 1 */
        !          2261:    switch ( *qptr )			/* check for quadrant 1,2,3,4 */
        !          2262:     { default: break;			/* unrecognized, assume quadrant 1 */
        !          2263:       case '4': rsign = 1; break;	/* row,col both pos in quadrant 4 */
        !          2264:       case '3': rsign = 1;		/* row pos, col neg in quadrant 3 */
        !          2265:       case '2': csign = (-1); break; }	/* row,col both neg in quadrant 2 */
        !          2266:    irow = iround(midrow + (double)rsign*(islandscape?yx:xy));
        !          2267:    irow = min2(hirow,max2(lorow,irow));	/* keep irow in bounds */
        !          2268:    icol = iround(midcol + (double)csign*(islandscape?xy:yx));
        !          2269:    icol = min2(hicol,max2(locol,icol));	/* keep icol in bounds */
        !          2270:    if ( msgfp!=NULL && msglevel>=49 )	/* debugging */
        !          2271:      fprintf(msgfp,"\t...imajor=%d; iminor,quad,irow,icol=%d,%c,%d,%d\n",
        !          2272:      imajor,iminor,*qptr,irow,icol);
        !          2273:    if ( irow<0 || irow>=rp->height	/* row outside raster */
        !          2274:    ||   icol<0 || icol>=rp->width )	/* col outside raster */
        !          2275:       {	isokay = 0;			/* signal out-of-bounds pixel */
        !          2276: 	continue; }			/* but still try remaining points */
        !          2277:    setpixel(rp,irow,icol,255);		/* set pixel at irow,icol */
        !          2278:    } /* --- end-of-for(qptr) --- */
        !          2279:   } /* --- end-of-for(imajor) --- */
        !          2280:  /* ------------------------------------------------------------------------
        !          2281:  now do it _again_ along minor axis to avoid "gaps"
        !          2282:  ------------------------------------------------------------------------- */
        !          2283:  if ( 1 && iminor>0 )
        !          2284:   for ( iminor=(nminor+1)/2; ; iminor-- )
        !          2285:    {
        !          2286:    /* --- yx is coord along minor axis, xy is "solved" along major axis --- */
        !          2287:    yx  = ((double)iminor);		/* yx = abminor ... 0 */
        !          2288:    if ( yx < 0.0 ) break;		/* negative side symmetrical */
        !          2289:    xy2 = abmajor2*(1.0 - yx*yx/abminor2); /* "solve" ellipse equation */
        !          2290:    xy  = (xy2>0.0? sqrt(xy2) : 0.0);	/* take sqrt if possible */
        !          2291:    imajor = iround(xy);			/* nearest integer */
        !          2292:    /* --- set pixels for each requested quadrant --- */
        !          2293:    for ( qptr=quads; *qptr!='\000'; qptr++ ) /* for each character in quads */
        !          2294:     {
        !          2295:     rsign = (-1);  csign = 1;		/* init row,col in user quadrant 1 */
        !          2296:     switch ( *qptr )			/* check for quadrant 1,2,3,4 */
        !          2297:      { default: break;			/* unrecognized, assume quadrant 1 */
        !          2298:        case '4': rsign = 1; break;	/* row,col both pos in quadrant 4 */
        !          2299:        case '3': rsign = 1;		/* row pos, col neg in quadrant 3 */
        !          2300:        case '2': csign = (-1); break; }	/* row,col both neg in quadrant 2 */
        !          2301:     irow = iround(midrow + (double)rsign*(islandscape?yx:xy));
        !          2302:     irow = min2(hirow,max2(lorow,irow)); /* keep irow in bounds */
        !          2303:     icol = iround(midcol + (double)csign*(islandscape?xy:yx));
        !          2304:     icol = min2(hicol,max2(locol,icol)); /* keep icol in bounds */
        !          2305:     if ( msgfp!=NULL && msglevel>=49 )	/* debugging */
        !          2306:      fprintf(msgfp,"\t...iminor=%d; imajor,quad,irow,icol=%d,%c,%d,%d\n",
        !          2307:      iminor,imajor,*qptr,irow,icol);
        !          2308:     if ( irow<0 || irow>=rp->height	/* row outside raster */
        !          2309:     ||   icol<0 || icol>=rp->width )	/* col outside raster */
        !          2310:       {	isokay = 0;			/* signal out-of-bounds pixel */
        !          2311: 	continue; }			/* but still try remaining points */
        !          2312:     setpixel(rp,irow,icol,255);		/* set pixel at irow,icol */
        !          2313:     } /* --- end-of-for(qptr) --- */
        !          2314:    } /* --- end-of-for(iminor) --- */
        !          2315:  } /* --- end-of-if/else(nmajor<1) --- */
        !          2316: return ( isokay );
        !          2317: } /* --- end-of-function circle_raster() --- */
        !          2318: 
        !          2319: 
        !          2320: /* ==========================================================================
        !          2321:  * Function:	circle_recurse ( rp,  row0, col0,  row1, col1,
        !          2322:  *		thickness, theta0, theta1 )
        !          2323:  * Purpose:	Recursively draws arc theta0<=theta<=theta1 of the ellipse
        !          2324:  *		in box determined by diagonally opposite corner points
        !          2325:  *		(row0,col0) and (row1,col1), of thickness pixels in raster rp.
        !          2326:  * --------------------------------------------------------------------------
        !          2327:  * Arguments:	rp (I)		raster *  to raster in which an ellipse
        !          2328:  *				will be drawn
        !          2329:  *		row0 (I)	int containing 1st corner row bounding ellipse
        !          2330:  *				(0 is topmost)
        !          2331:  *		col0 (I)	int containing 1st corner col bounding ellipse
        !          2332:  *				(0 is leftmost)
        !          2333:  *		row1 (I)	int containing 2nd corner row bounding ellipse
        !          2334:  *				(rp->height-1 is bottom-most)
        !          2335:  *		col1 (I)	int containing 2nd corner col bounding ellipse
        !          2336:  *				(rp->width-1 is rightmost)
        !          2337:  *		thickness (I)	int containing number of pixels/bits
        !          2338:  *				thick the ellipse arc line will be
        !          2339:  *		theta0 (I)	double containing first angle -360 -> +360
        !          2340:  *		theta1 (I)	double containing second angle -360 -> +360
        !          2341:  *				0=x-axis, positive moving counterclockwise
        !          2342:  * --------------------------------------------------------------------------
        !          2343:  * Returns:	( int )		1 if ellipse drawn okay,
        !          2344:  *				or 0 for any error.
        !          2345:  * --------------------------------------------------------------------------
        !          2346:  * Notes:     o	row0==row1 or col0==col1 are errors
        !          2347:  *	      o	using ellipse equation x^2/a^2 + y^2/b^2 = 1
        !          2348:  *		Then, with x=r*cos(theta), y=r*sin(theta), ellipse
        !          2349:  *		equation is r = ab/sqrt(a^2*sin^2(theta)+b^2*cos^2(theta))
        !          2350:  * ======================================================================= */
        !          2351: /* --- entry point --- */
        !          2352: int	circle_recurse ( raster *rp, int row0, int col0,
        !          2353: 	int row1, int col1, int thickness, double theta0, double theta1 )
        !          2354: {
        !          2355: /* -------------------------------------------------------------------------
        !          2356: Allocations and Declarations
        !          2357: -------------------------------------------------------------------------- */
        !          2358: /* --- lower-left and upper-right bounding points (in our coords) --- */
        !          2359: int	lorow = min2(row0,row1),	/* lower bounding row (top of box) */
        !          2360: 	locol = min2(col0,col1),	/* lower bounding col (left of box)*/
        !          2361: 	hirow = max2(row0,row1),	/* upper bounding row (bot of box) */
        !          2362: 	hicol = max2(col0,col1);	/* upper bounding col (right of box)*/
        !          2363: /* --- a and b ellipse params --- */
        !          2364: int	width = hicol-locol+1,		/* width of bounding box */
        !          2365: 	height= hirow-lorow+1;		/* height of bounding box */
        !          2366: double	a = ((double)width)/2.0,	/* col x=a when row y=0 */
        !          2367: 	b = ((double)height)/2.0,	/* row y=b when col x=0 */
        !          2368: 	ab=a*b, a2=a*a, b2=b*b;		/* product and squares */
        !          2369: /* --- arc parameters --- */
        !          2370: double	rads = 0.017453292,		/* radians per degree = 1/57.29578 */
        !          2371: 	lotheta = rads*dmod(min2(theta0,theta1),360), /* smaller angle */
        !          2372: 	hitheta = rads*dmod(max2(theta0,theta1),360), /* larger angle */
        !          2373: 	locos=cos(lotheta), losin=sin(lotheta), /* trigs for lotheta */
        !          2374: 	hicos=cos(hitheta), hisin=sin(hitheta), /* trigs for hitheta */
        !          2375: 	rlo = ab/sqrt(b2*locos*locos+a2*losin*losin), /* r for lotheta */
        !          2376: 	rhi = ab/sqrt(b2*hicos*hicos+a2*hisin*hisin), /* r for hitheta */
        !          2377: 	xlo=rlo*locos, ylo=rlo*losin,	/*col,row pixel coords for lotheta*/
        !          2378: 	xhi=rhi*hicos, yhi=rhi*hisin,	/*col,row pixel coords for hitheta*/
        !          2379: 	xdelta=fabs(xhi-xlo), ydelta=fabs(yhi-ylo), /* col,row deltas */
        !          2380: 	tolerance = 0.5;		/* convergence tolerance */
        !          2381: /* -------------------------------------------------------------------------
        !          2382: recurse if either delta > tolerance
        !          2383: -------------------------------------------------------------------------- */
        !          2384: if ( ydelta > tolerance			/* row hasn't converged */
        !          2385: ||   xdelta > tolerance )		/* col hasn't converged */
        !          2386:   { double midtheta = 0.5*(theta0+theta1); /* mid angle for arc */
        !          2387:     circle_recurse(rp,row0,col0,row1,col1,thickness,theta0,midtheta);  /*lo*/
        !          2388:     circle_recurse(rp,row0,col0,row1,col1,thickness,midtheta,theta1); }/*hi*/
        !          2389: /* -------------------------------------------------------------------------
        !          2390: draw converged point
        !          2391: -------------------------------------------------------------------------- */
        !          2392: else
        !          2393:   { double xcol=0.5*(xlo+xhi), yrow=0.5*(ylo+yhi),    /* relative to center*/
        !          2394: 	centerrow = 0.5*((double)(lorow+hirow)),      /* ellipse y-center */
        !          2395: 	centercol = 0.5*((double)(locol+hicol)),      /* ellipse x-center */
        !          2396: 	midrow=centerrow-yrow, midcol=centercol+xcol; /* pixel coords */
        !          2397:     setpixel(rp,iround(midrow),iround(midcol),255); } /* set midrow,midcol */
        !          2398: return ( 1 );
        !          2399: } /* --- end-of-function circle_recurse() --- */
        !          2400: 
        !          2401: 
        !          2402: /* ==========================================================================
        !          2403:  * Function:	bezier_raster ( rp, r0,c0, r1,c1, rt,ct )
        !          2404:  * Purpose:	Recursively draw bezier from r0,c0 to r1,c1
        !          2405:  *		(with tangent point rt,ct) in existing raster rp.
        !          2406:  * --------------------------------------------------------------------------
        !          2407:  * Arguments:	rp (I)		raster *  to raster in which a line
        !          2408:  *				will be drawn
        !          2409:  *		r0 (I)		double containing row at which
        !          2410:  *				bezier will start (0 is topmost)
        !          2411:  *		c0 (I)		double containing col at which
        !          2412:  *				bezier will start (0 is leftmost)
        !          2413:  *		r1 (I)		double containing row at which
        !          2414:  *				bezier will end (rp->height-1 is bottom-most)
        !          2415:  *		c1 (I)		double containing col at which
        !          2416:  *				bezier will end (rp->width-1 is rightmost)
        !          2417:  *		rt (I)		double containing row for tangent point
        !          2418:  *		ct (I)		double containing col for tangent point
        !          2419:  * --------------------------------------------------------------------------
        !          2420:  * Returns:	( int )		1 if line drawn okay,
        !          2421:  *				or 0 for any error.
        !          2422:  * --------------------------------------------------------------------------
        !          2423:  * Notes:     o	Recurses, drawing left- and right-halves of bezier curve
        !          2424:  *		until a point is found
        !          2425:  * ======================================================================= */
        !          2426: /* --- entry point --- */
        !          2427: int	bezier_raster ( raster *rp, double r0, double c0,
        !          2428: 	double r1, double c1, double rt, double ct )
        !          2429: {
        !          2430: /* -------------------------------------------------------------------------
        !          2431: Allocations and Declarations
        !          2432: -------------------------------------------------------------------------- */
        !          2433: double	delrow = fabs(r1-r0),		/* 0 if same row */
        !          2434: 	delcol = fabs(c1-c0),		/* 0 if same col */
        !          2435: 	tolerance = 0.5;		/* draw curve when it goes to point*/
        !          2436: double	midrow = 0.5*(r0+r1),		/* midpoint row */
        !          2437: 	midcol = 0.5*(c0+c1);		/* midpoint col */
        !          2438: int	irow=0, icol=0;			/* point to be drawn */
        !          2439: int	status = 1;			/* return status */
        !          2440: /* -------------------------------------------------------------------------
        !          2441: recurse if either delta > tolerance
        !          2442: -------------------------------------------------------------------------- */
        !          2443: if ( delrow > tolerance			/* row hasn't converged */
        !          2444: ||   delcol > tolerance )		/* col hasn't converged */
        !          2445:   { bezier_raster(rp, r0,c0,		/* left half */
        !          2446: 	0.5*(rt+midrow), 0.5*(ct+midcol),
        !          2447: 	0.5*(r0+rt), 0.5*(c0+ct) );
        !          2448:     bezier_raster(rp, 0.5*(rt+midrow), 0.5*(ct+midcol), /* right half */
        !          2449: 	r1,c1,
        !          2450: 	0.5*(r1+rt), 0.5*(c1+ct) );
        !          2451:     return ( 1 ); }
        !          2452: /* -------------------------------------------------------------------------
        !          2453: draw converged point
        !          2454: -------------------------------------------------------------------------- */
        !          2455: /* --- get integer point --- */
        !          2456: irow = iround(midrow);			/* row pixel coord */
        !          2457: icol = iround(midcol);			/* col pixel coord */
        !          2458: /* --- bounds check --- */
        !          2459: if ( irow>=0 && irow<rp->height		/* row in bounds */
        !          2460: &&   icol>=0 && icol<rp->width )	/* col in bounds */
        !          2461: 	setpixel(rp,irow,icol,255);	/* so set pixel at irow,icol*/
        !          2462: else	status = 0;			/* bad status if out-of-bounds */
        !          2463: return ( status );
        !          2464: } /* --- end-of-function bezier_raster() --- */
        !          2465: 
        !          2466: 
        !          2467: /* ==========================================================================
        !          2468:  * Function:	border_raster ( rp, ntop, nbot, isline, isfree )
        !          2469:  * Purpose:	Allocate a new raster containing a copy of input rp,
        !          2470:  *		along with ntop extra rows at top and nbot at bottom,
        !          2471:  *		and whose width is either adjusted correspondingly,
        !          2472:  *		or is automatically enlarged to a multiple of 8
        !          2473:  *		with original bitmap centered
        !          2474:  * --------------------------------------------------------------------------
        !          2475:  * Arguments:	rp (I)		raster *  to raster on which a border
        !          2476:  *				is to be placed
        !          2477:  *		ntop (I)	int containing number extra rows at top.
        !          2478:  *				if negative, abs(ntop) used, and same
        !          2479:  *				number of extra cols added at left.
        !          2480:  *		nbot (I)	int containing number extra rows at bottom.
        !          2481:  *				if negative, abs(nbot) used, and same
        !          2482:  *				number of extra cols added at right.
        !          2483:  *		isline (I)	int containing 0 to leave border pixels clear
        !          2484:  *				or >0 to draw a line around border of width
        !          2485:  *				isline.
        !          2486:  *		isfree (I)	int containing true to free rp before return
        !          2487:  * --------------------------------------------------------------------------
        !          2488:  * Returns:	( raster * )	ptr to bordered raster,
        !          2489:  *				or NULL for any error.
        !          2490:  * --------------------------------------------------------------------------
        !          2491:  * Notes:     o
        !          2492:  * ======================================================================= */
        !          2493: /* --- entry point --- */
        !          2494: raster	*border_raster ( raster *rp, int ntop, int nbot,
        !          2495: 			int isline, int isfree )
        !          2496: {
        !          2497: /* -------------------------------------------------------------------------
        !          2498: Allocations and Declarations
        !          2499: -------------------------------------------------------------------------- */
        !          2500: raster	*new_raster(), *bp=(raster *)NULL;  /*raster back to caller*/
        !          2501: int	rastput();		/* overlay rp in new bordered raster */
        !          2502: int	width  = (rp==NULL?0:rp->width),  /* height of raster */
        !          2503: 	height = (rp==NULL?0:rp->height), /* width  of raster */
        !          2504: 	istopneg=0, isbotneg=0,	/* true if ntop or nbot negative */
        !          2505: 	leftmargin = 0;		/* adjust width to whole number of bytes */
        !          2506: int	delete_raster();	/* to free input rp if isdelete is true */
        !          2507: /* -------------------------------------------------------------------------
        !          2508: Initialization
        !          2509: -------------------------------------------------------------------------- */
        !          2510: if ( rp == NULL ) goto end_of_job;	/* no input raster provided */
        !          2511: if ( isstring || (1 && rp->height==1) )	/* explicit string signal or infer */
        !          2512:   { bp=rp; goto end_of_job; }		/* return ascii string unchanged */
        !          2513: /* --- check for negative args --- */
        !          2514: if ( ntop < 0 ) { ntop = -ntop; istopneg=1; } /*flip positive and set flag*/
        !          2515: if ( nbot < 0 ) { nbot = -nbot; isbotneg=1; } /*flip positive and set flag*/
        !          2516: /* --- adjust height for ntop and nbot margins --- */
        !          2517: height += (ntop+nbot);			/* adjust height for margins */
        !          2518: /* --- adjust width for left and right margins --- */
        !          2519: if ( istopneg || isbotneg )	/*caller wants nleft=ntop and/or nright=nbot*/
        !          2520:   { /* --- adjust width (and leftmargin) as requested by caller -- */
        !          2521:     if ( istopneg ) { width += ntop; leftmargin = ntop; }
        !          2522:     if ( isbotneg )   width += nbot;  }
        !          2523: else
        !          2524:   { /* --- or adjust width (and leftmargin) to whole number of bytes --- */
        !          2525:     leftmargin = (width%8==0? 0 : 8-(width%8)); /*makes width multiple of 8*/
        !          2526:     width += leftmargin;		/* width now multiple of 8 */
        !          2527:     leftmargin /= 2; }			/* center original raster */
        !          2528: /* -------------------------------------------------------------------------
        !          2529: allocate bordered raster, and embed rp within it
        !          2530: -------------------------------------------------------------------------- */
        !          2531: /* --- allocate bordered raster --- */
        !          2532: if ( (bp=new_raster(width,height,rp->pixsz))  /*allocate bordered raster*/
        !          2533: ==   (raster *)NULL ) goto end_of_job;	/* and quit if failed */
        !          2534: /* --- embed rp in it --- */
        !          2535: rastput(bp,rp,ntop,leftmargin,1);	/* rp embedded in bp */
        !          2536: /* -------------------------------------------------------------------------
        !          2537: draw border if requested
        !          2538: -------------------------------------------------------------------------- */
        !          2539: if ( isline )
        !          2540:  { int	irow, icol, nthick=isline;	/*height,width index, line thickness*/
        !          2541:   /* --- draw left- and right-borders --- */
        !          2542:   for ( irow=0; irow<height; irow++ )	/* for each row of bp */
        !          2543:     for ( icol=0; icol<nthick; icol++ )	/* and each pixel of thickness */
        !          2544:       {	setpixel(bp,irow,icol,255);	/* left border */
        !          2545: 	setpixel(bp,irow,width-1-icol,255); } /* right border */
        !          2546:   /* --- draw top- and bottom-borders --- */
        !          2547:   for ( icol=0; icol<width; icol++ )	/* for each col of bp */
        !          2548:     for ( irow=0; irow<nthick; irow++ )	/* and each pixel of thickness */
        !          2549:       {	setpixel(bp,irow,icol,255);	/* top border */
        !          2550: 	setpixel(bp,height-1-irow,icol,255); } /* bottom border */
        !          2551:  } /* --- end-of-if(isline) --- */
        !          2552: /* -------------------------------------------------------------------------
        !          2553: free rp if no longer needed
        !          2554: -------------------------------------------------------------------------- */
        !          2555: if ( isfree )					/*caller no longer needs rp*/
        !          2556:   delete_raster(rp);				/* so free it for him */
        !          2557: /* -------------------------------------------------------------------------
        !          2558: Back to caller with bordered raster (or null for any error)
        !          2559: -------------------------------------------------------------------------- */
        !          2560: end_of_job:
        !          2561:   return ( bp );			/* back with bordered or null ptr */
        !          2562: } /* --- end-of-function border_raster() --- */
        !          2563: 
        !          2564: 
        !          2565: /* ==========================================================================
        !          2566:  * Function:	type_raster ( rp, fp )
        !          2567:  * Purpose:	Emit an ascii dump representing rp, on fp.
        !          2568:  * --------------------------------------------------------------------------
        !          2569:  * Arguments:	rp (I)		ptr to raster struct for which an
        !          2570:  *				ascii dump is to be constructed.
        !          2571:  *		fp (I)		File ptr to output device (defaults to
        !          2572:  *				stdout if passed as NULL).
        !          2573:  * --------------------------------------------------------------------------
        !          2574:  * Returns:	( int )		1 if completed successfully,
        !          2575:  *				or 0 otherwise (for any error).
        !          2576:  * --------------------------------------------------------------------------
        !          2577:  * Notes:
        !          2578:  * ======================================================================= */
        !          2579: /* --- entry point --- */
        !          2580: int	type_raster ( raster *rp, FILE *fp )
        !          2581: {
        !          2582: /* -------------------------------------------------------------------------
        !          2583: Allocations and Declarations
        !          2584: -------------------------------------------------------------------------- */
        !          2585: static	int display_width = 72;		/* max columns for display */
        !          2586: static	char display_chars[16] =	/* display chars for bytemap */
        !          2587: 	{ ' ','1','2','3','4','5','6','7','8','9','A','B','C','D','E','*' };
        !          2588: char	scanline[133];			/* ascii image for one scan line */
        !          2589: int	scan_width;			/* #chars in scan (<=display_width)*/
        !          2590: int	irow, locol,hicol=(-1);		/* height index, width indexes */
        !          2591: /* --------------------------------------------------------------------------
        !          2592: initialization
        !          2593: -------------------------------------------------------------------------- */
        !          2594: /* --- redirect null fp --- */
        !          2595: if ( fp == (FILE *)NULL ) fp = stdout;	/* default fp to stdout if null */
        !          2596: /* --- check for ascii string --- */
        !          2597: if ( isstring				/* pixmap has string, not raster */
        !          2598: ||   (1 && rp->height==1) )		/* infer input rp is a string */
        !          2599:  {
        !          2600:  char *string = (char *)(rp->pixmap);	/*interpret pixmap as ascii string*/
        !          2601:  int width = strlen(string);		/* #chars in ascii string */
        !          2602:  while ( width > display_width-2 )	/* too big for one line */
        !          2603:   { fprintf(fp,"\"%.*s\"\n",display_width-2,string); /*display leading chars*/
        !          2604:     string += (display_width-2);	/* bump string past displayed chars*/
        !          2605:     width -= (display_width-2); }	/* decrement remaining width */
        !          2606:  fprintf(fp,"\"%.*s\"\n",width,string);	/* display trailing chars */
        !          2607:  return ( 1 );
        !          2608:  } /* --- end-of-if(isstring) --- */
        !          2609: /* --------------------------------------------------------------------------
        !          2610: display ascii dump of bitmap image (in segments if display_width < rp->width)
        !          2611: -------------------------------------------------------------------------- */
        !          2612: while ( (locol=hicol+1) < rp->width )	/*start where prev segment left off*/
        !          2613:   {
        !          2614:   /* --- set hicol for this pass (locol set above) --- */
        !          2615:   hicol += display_width;		/* show as much as display allows */
        !          2616:   if (hicol >= rp->width) hicol = rp->width - 1; /*but not more than raster*/
        !          2617:   scan_width = hicol-locol+1;		/* #chars in this scan */
        !          2618:   if ( locol > 0 ) fprintf(fp,"----------\n"); /*separator between segments*/
        !          2619:   /* ------------------------------------------------------------------------
        !          2620:   display all scan lines for this local...hicol segment range
        !          2621:   ------------------------------------------------------------------------ */
        !          2622:   for ( irow=0; irow<rp->height; irow++ )  /* all scan lines for col range */
        !          2623:     {
        !          2624:     /* --- allocations and declarations --- */
        !          2625:     int	ipix,				/* pixmap[] index for this scan */
        !          2626: 	lopix = irow*rp->width + locol;	/*first pixmap[] pixel in this scan*/
        !          2627:     /* --- set chars in scanline[] based on pixels in rp->pixmap[] --- */
        !          2628:     for ( ipix=0; ipix<scan_width; ipix++ ) /* set each char */
        !          2629:       if ( rp->pixsz == 1 )		/*' '=0 or '*'=1 to display bitmap*/
        !          2630: 	scanline[ipix] = (getlongbit(rp->pixmap,lopix+ipix)==1? '*':'.');
        !          2631:       else				/* should have a bytemap */
        !          2632:        if ( rp->pixsz == 8 )		/* double-check pixsz for bytemap */
        !          2633: 	{ int pixval = (int)((rp->pixmap)[lopix+ipix]), /*pixel's byte value*/
        !          2634: 	  ichar = min2(15,pixval/16);	/* index for ' ', '1'...'e', '*' */
        !          2635: 	  scanline[ipix] = display_chars[ichar]; } /*set ' ' for 0-15, etc*/
        !          2636:     /* --- display completed scan line --- */
        !          2637:     fprintf(fp,"%.*s\n",scan_width,scanline);	
        !          2638:     } /* --- end-of-for(irow) --- */
        !          2639:   } /* --- end-of-while(hicol<rp->width) --- */
        !          2640: /* -------------------------------------------------------------------------
        !          2641: Back to caller with 1=okay, 0=failed.
        !          2642: -------------------------------------------------------------------------- */
        !          2643: return ( 1 );
        !          2644: } /* --- end-of-function type_raster() --- */
        !          2645: 
        !          2646: 
        !          2647: /* ==========================================================================
        !          2648:  * Function:	type_bytemap ( bp, grayscale, width, height, fp )
        !          2649:  * Purpose:	Emit an ascii dump representing bp, on fp.
        !          2650:  * --------------------------------------------------------------------------
        !          2651:  * Arguments:	bp (I)		intbyte * to bytemap for which an
        !          2652:  *				ascii dump is to be constructed.
        !          2653:  *		grayscale (I)	int containing #gray shades, 256 for 8-bit
        !          2654:  *		width (I)	int containing #cols in bytemap
        !          2655:  *		height (I)	int containing #rows in bytemap
        !          2656:  *		fp (I)		File ptr to output device (defaults to
        !          2657:  *				stdout if passed as NULL).
        !          2658:  * --------------------------------------------------------------------------
        !          2659:  * Returns:	( int )		1 if completed successfully,
        !          2660:  *				or 0 otherwise (for any error).
        !          2661:  * --------------------------------------------------------------------------
        !          2662:  * Notes:
        !          2663:  * ======================================================================= */
        !          2664: /* --- entry point --- */
        !          2665: int	type_bytemap ( intbyte *bp, int grayscale,
        !          2666: 			int width, int height, FILE *fp )
        !          2667: {
        !          2668: /* -------------------------------------------------------------------------
        !          2669: Allocations and Declarations
        !          2670: -------------------------------------------------------------------------- */
        !          2671: static	int display_width = 72;		/* max columns for display */
        !          2672: int	byte_width = 3,			/* cols to display byte (ff+space) */
        !          2673: 	maxbyte = 0;			/* if maxbyte<16, set byte_width=2 */
        !          2674: int	white_byte = 0,			/* show dots for white_byte's */
        !          2675: 	black_byte = grayscale-1;	/* show stars for black_byte's */
        !          2676: char	scanline[133];			/* ascii image for one scan line */
        !          2677: int	scan_width,			/* #chars in scan (<=display_width)*/
        !          2678: 	scan_cols;			/* #cols in scan (hicol-locol+1) */
        !          2679: int	ibyte,				/* bp[] index */
        !          2680: 	irow, locol,hicol=(-1);		/* height index, width indexes */
        !          2681: /* --------------------------------------------------------------------------
        !          2682: initialization
        !          2683: -------------------------------------------------------------------------- */
        !          2684: /* --- redirect null fp --- */
        !          2685: if ( fp == (FILE *)NULL ) fp = stdout;	/* default fp to stdout if null */
        !          2686: /* --- check for ascii string --- */
        !          2687: if ( isstring )				/* bp has ascii string, not raster */
        !          2688:  { width = strlen((char *)bp);		/* #chars in ascii string */
        !          2689:    height = 1; }			/* default */
        !          2690: /* --- see if we can get away with byte_width=1 --- */
        !          2691: for ( ibyte=0; ibyte<width*height; ibyte++ )  /* check all bytes */
        !          2692:   { int	byteval = (int)bp[ibyte];	/* current byte value */
        !          2693:     if ( byteval < black_byte )		/* if it's less than black_byte */
        !          2694:       maxbyte = max2(maxbyte,byteval); } /* then find max non-black value */
        !          2695: if ( maxbyte < 16 )			/* bytevals will fit in one column */
        !          2696:   byte_width = 1;			/* so reset display byte_width */
        !          2697: /* --------------------------------------------------------------------------
        !          2698: display ascii dump of bitmap image (in segments if display_width < rp->width)
        !          2699: -------------------------------------------------------------------------- */
        !          2700: while ( (locol=hicol+1) < width )	/*start where prev segment left off*/
        !          2701:   {
        !          2702:   /* --- set hicol for this pass (locol set above) --- */
        !          2703:   hicol += display_width/byte_width;	/* show as much as display allows */
        !          2704:   if (hicol >= width) hicol = width - 1; /* but not more than bytemap */
        !          2705:   scan_cols = hicol-locol+1;		/* #cols in this scan */
        !          2706:   scan_width = byte_width*scan_cols;	/* #chars in this scan */
        !          2707:   if ( locol>0 && !isstring ) fprintf(fp,"----------\n"); /* separator */
        !          2708:   /* ------------------------------------------------------------------------
        !          2709:   display all scan lines for this local...hicol segment range
        !          2710:   ------------------------------------------------------------------------ */
        !          2711:   for ( irow=0; irow<height; irow++ )	/* all scan lines for col range */
        !          2712:     {
        !          2713:     /* --- allocations and declarations --- */
        !          2714:     int  lobyte = irow*width + locol;	/* first bp[] byte in this scan */
        !          2715:     char scanbyte[32];			/* sprintf() buffer for byte */
        !          2716:     /* --- set chars in scanline[] based on bytes in bytemap bp[] --- */
        !          2717:     memset(scanline,' ',scan_width);	/* blank out scanline */
        !          2718:     for ( ibyte=0; ibyte<scan_cols; ibyte++ ) /* set chars for each col */
        !          2719:       {	int byteval = (int)bp[lobyte+ibyte];  /* value of current byte */
        !          2720: 	memset(scanbyte,'.',byte_width); /* dot-fill scanbyte */
        !          2721: 	if ( byteval == black_byte )	/* but if we have a black byte */
        !          2722: 	  memset(scanbyte,'*',byte_width); /* star-fill scanbyte instead */
        !          2723: 	if ( byte_width > 1 )		/* don't blank out single char */
        !          2724: 	  scanbyte[byte_width-1] = ' ';	/* blank-fill rightmost character */
        !          2725: 	if ( byteval != white_byte	/* format bytes that are non-white */
        !          2726: 	&&   byteval != black_byte )	/* and that are non-black */
        !          2727: 	  sprintf(scanbyte,"%*x ",max2(1,byte_width-1),byteval); /*hex-format*/
        !          2728: 	memcpy(scanline+ibyte*byte_width,scanbyte,byte_width); } /*in line*/
        !          2729:     /* --- display completed scan line --- */
        !          2730:     fprintf(fp,"%.*s\n",scan_width,scanline);	
        !          2731:     } /* --- end-of-for(irow) --- */
        !          2732:   } /* --- end-of-while(hicol<width) --- */
        !          2733: /* -------------------------------------------------------------------------
        !          2734: Back to caller with 1=okay, 0=failed.
        !          2735: -------------------------------------------------------------------------- */
        !          2736: return ( 1 );
        !          2737: } /* --- end-of-function type_bytemap() --- */
        !          2738: 
        !          2739: 
        !          2740: /* ==========================================================================
        !          2741:  * Function:	xbitmap_raster ( rp, fp )
        !          2742:  * Purpose:	Emit a mime xbitmap representing rp, on fp.
        !          2743:  * --------------------------------------------------------------------------
        !          2744:  * Arguments:	rp (I)		ptr to raster struct for which a mime
        !          2745:  *				xbitmap is to be constructed.
        !          2746:  *		fp (I)		File ptr to output device (defaults to
        !          2747:  *				stdout if passed as NULL).
        !          2748:  * --------------------------------------------------------------------------
        !          2749:  * Returns:	( int )		1 if completed successfully,
        !          2750:  *				or 0 otherwise (for any error).
        !          2751:  * --------------------------------------------------------------------------
        !          2752:  * Notes:
        !          2753:  * ======================================================================= */
        !          2754: /* --- entry point --- */
        !          2755: int	xbitmap_raster ( raster *rp, FILE *fp )
        !          2756: {
        !          2757: /* -------------------------------------------------------------------------
        !          2758: Allocations and Declarations
        !          2759: -------------------------------------------------------------------------- */
        !          2760: char	*title = "image";		/* dummy title */
        !          2761: int	hex_bitmap();			/* dump bitmap as hex bytes */
        !          2762: /* --------------------------------------------------------------------------
        !          2763: emit text to display mime xbitmap representation of rp->bitmap image
        !          2764: -------------------------------------------------------------------------- */
        !          2765: /* --- first redirect null fp --- */
        !          2766: if ( fp == (FILE *)NULL ) fp = stdout;	/* default fp to stdout if null */
        !          2767: /* --- check for ascii string --- */
        !          2768: if ( isstring )				/* pixmap has string, not raster */
        !          2769:  return ( 0 );				/* can't handle ascii string */
        !          2770: /* --- emit prologue strings and hex dump of bitmap for mime xbitmap --- */
        !          2771: fprintf( fp, "Content-type: image/x-xbitmap\n\n" );
        !          2772: fprintf( fp, "#define %s_width %d\n#define %s_height %d\n",
        !          2773: 	title,rp->width, title,rp->height );
        !          2774: fprintf( fp, "static char %s_bits[] = {\n", title );
        !          2775: hex_bitmap(rp,fp,0,0);			/* emit hex dump of bitmap bytes */
        !          2776: fprintf (fp,"};\n");			/* ending with "};" for C array */
        !          2777: /* -------------------------------------------------------------------------
        !          2778: Back to caller with 1=okay, 0=failed.
        !          2779: -------------------------------------------------------------------------- */
        !          2780: return ( 1 );
        !          2781: } /* --- end-of-function xbitmap_raster() --- */
        !          2782: 
        !          2783: 
        !          2784: /* ==========================================================================
        !          2785:  * Function:	cstruct_chardef ( cp, fp, col1 )
        !          2786:  * Purpose:	Emit a C struct of cp on fp, starting in col1.
        !          2787:  * --------------------------------------------------------------------------
        !          2788:  * Arguments:	cp (I)		ptr to chardef struct for which
        !          2789:  *				a C struct is to be generated.
        !          2790:  *		fp (I)		File ptr to output device (defaults to
        !          2791:  *				stdout if passed as NULL).
        !          2792:  *		col1 (I)	int containing 0...65; output lines
        !          2793:  *				are preceded by col1 blanks.
        !          2794:  * --------------------------------------------------------------------------
        !          2795:  * Returns:	( int )		1 if completed successfully,
        !          2796:  *				or 0 otherwise (for any error).
        !          2797:  * --------------------------------------------------------------------------
        !          2798:  * Notes:
        !          2799:  * ======================================================================= */
        !          2800: /* --- entry point --- */
        !          2801: int	cstruct_chardef ( chardef *cp, FILE *fp, int col1 )
        !          2802: {
        !          2803: /* -------------------------------------------------------------------------
        !          2804: Allocations and Declarations
        !          2805: -------------------------------------------------------------------------- */
        !          2806: char	field[64];		/* field within output line */
        !          2807: int	cstruct_raster(),	/* emit a raster */
        !          2808: 	emit_string();		/* emit a string and comment */
        !          2809: /* -------------------------------------------------------------------------
        !          2810: emit   charnum, location, name  /  hirow, hicol,  lorow, locol
        !          2811: -------------------------------------------------------------------------- */
        !          2812: /* --- charnum, location, name --- */
        !          2813: sprintf(field,"{ %3d,%5d,\n", cp->charnum,cp->location);  /*char#,location*/
        !          2814: emit_string ( fp, col1, field, "character number, location");
        !          2815: /* --- toprow, topleftcol,   botrow, botleftcol  --- */
        !          2816: sprintf(field,"  %3d,%2d,   %3d,%2d,\n",	/* format... */
        !          2817:   cp->toprow,cp->topleftcol,			/* toprow, topleftcol, */
        !          2818:   cp->botrow,cp->botleftcol);			/* and botrow, botleftcol */
        !          2819: emit_string ( fp, col1, field, "topleft row,col, and botleft row,col");
        !          2820: /* -------------------------------------------------------------------------
        !          2821: emit raster and chardef's closing brace, and then return to caller
        !          2822: -------------------------------------------------------------------------- */
        !          2823: cstruct_raster(&cp->image,fp,col1+4);		/* emit raster */
        !          2824: emit_string ( fp, 0, "  }", NULL);		/* emit closing brace */
        !          2825: return ( 1 );			/* back to caller with 1=okay, 0=failed */
        !          2826: } /* --- end-of-function cstruct_chardef() --- */
        !          2827: 
        !          2828: 
        !          2829: /* ==========================================================================
        !          2830:  * Function:	cstruct_raster ( rp, fp, col1 )
        !          2831:  * Purpose:	Emit a C struct of rp on fp, starting in col1.
        !          2832:  * --------------------------------------------------------------------------
        !          2833:  * Arguments:	rp (I)		ptr to raster struct for which
        !          2834:  *				a C struct is to be generated.
        !          2835:  *		fp (I)		File ptr to output device (defaults to
        !          2836:  *				stdout if passed as NULL).
        !          2837:  *		col1 (I)	int containing 0...65; output lines
        !          2838:  *				are preceded by col1 blanks.
        !          2839:  * --------------------------------------------------------------------------
        !          2840:  * Returns:	( int )		1 if completed successfully,
        !          2841:  *				or 0 otherwise (for any error).
        !          2842:  * --------------------------------------------------------------------------
        !          2843:  * Notes:
        !          2844:  * ======================================================================= */
        !          2845: /* --- entry point --- */
        !          2846: int	cstruct_raster ( raster *rp, FILE *fp, int col1 )
        !          2847: {
        !          2848: /* -------------------------------------------------------------------------
        !          2849: Allocations and Declarations
        !          2850: -------------------------------------------------------------------------- */
        !          2851: char	field[64];		/* field within output line */
        !          2852: char	typecast[64] = "(pixbyte *)"; /* type cast for pixmap string */
        !          2853: int	hex_bitmap();		/* to emit raster bitmap */
        !          2854: int	emit_string();		/* emit a string and comment */
        !          2855: /* -------------------------------------------------------------------------
        !          2856: emit width and height
        !          2857: -------------------------------------------------------------------------- */
        !          2858: sprintf(field,"{ %2d,   %3d,%2d,   %s\n", /* format width,height,pixsz */
        !          2859: 	rp->width,rp->height,rp->pixsz,typecast);
        !          2860: emit_string ( fp, col1, field, "widthxheight, pixsz,map...");
        !          2861: /* -------------------------------------------------------------------------
        !          2862: emit bitmap and closing brace, and return to caller
        !          2863: -------------------------------------------------------------------------- */
        !          2864: hex_bitmap(rp,fp,col1+2,1);	/* emit bitmap */
        !          2865: emit_string ( fp, 0, " }", NULL); /* emit closing brace */
        !          2866: return ( 1 );			/* back to caller with 1=okay, 0=failed */
        !          2867: } /* --- end-of-function cstruct_raster() --- */
        !          2868: 
        !          2869: 
        !          2870: /* ==========================================================================
        !          2871:  * Function:	hex_bitmap ( rp, fp, col1, isstr )
        !          2872:  * Purpose:	Emit a hex dump of the bitmap of rp on fp, starting in col1.
        !          2873:  *		If isstr (is string) is true, the dump is of the form
        !          2874:  *			"\x01\x02\x03\x04\x05..."
        !          2875:  *		Otherwise, if isstr is false, the dump is of the form
        !          2876:  *			0x01,0x02,0x03,0x04,0x05...
        !          2877:  * --------------------------------------------------------------------------
        !          2878:  * Arguments:	rp (I)		ptr to raster struct for which
        !          2879:  *				a hex dump is to be constructed.
        !          2880:  *		fp (I)		File ptr to output device (defaults to
        !          2881:  *				stdout if passed as NULL).
        !          2882:  *		col1 (I)	int containing 0...65; output lines
        !          2883:  *				are preceded by col1 blanks.
        !          2884:  *		isstr (I)	int specifying dump format as described above
        !          2885:  * --------------------------------------------------------------------------
        !          2886:  * Returns:	( int )		1 if completed successfully,
        !          2887:  *				or 0 otherwise (for any error).
        !          2888:  * --------------------------------------------------------------------------
        !          2889:  * Notes:
        !          2890:  * ======================================================================= */
        !          2891: /* --- entry point --- */
        !          2892: int	hex_bitmap ( raster *rp, FILE *fp, int col1, int isstr )
        !          2893: {
        !          2894: /* -------------------------------------------------------------------------
        !          2895: Allocations and Declarations
        !          2896: -------------------------------------------------------------------------- */
        !          2897: int	ibyte, nbytes=pixmapsz(rp);	/* #bytes in raster */
        !          2898: char	stub[64]="                                ";/* col1 leading blanks */
        !          2899: int	linewidth = 64,			/* (roughly) rightmost column */
        !          2900: 	colwidth = (isstr? 4:5);	/* #cols required for each byte */
        !          2901: int	ncols = (linewidth-col1)/colwidth; /* new line after ncols bytes */
        !          2902: /* --------------------------------------------------------------------------
        !          2903: initialization
        !          2904: -------------------------------------------------------------------------- */
        !          2905: /* --- redirect null fp --- */
        !          2906: if ( fp == (FILE *)NULL ) fp = stdout;	/* default fp to stdout if null */
        !          2907: /* --- emit initial stub if wanted --- */
        !          2908: if ( col1 > 0 ) fprintf(fp,"%.*s",col1,stub); /* stub preceding 1st line */
        !          2909: /* --------------------------------------------------------------------------
        !          2910: emit hex dump of rp->bitmap image
        !          2911: -------------------------------------------------------------------------- */
        !          2912: if ( isstr ) fprintf(fp,"\"");		/* opening " before first line */
        !          2913: for ( ibyte=0; ibyte<nbytes; ibyte++ )	/* one byte at a time */
        !          2914:   {
        !          2915:   /* --- display a byte as hex char or number, depending on isstr --- */
        !          2916:   if ( isstr )				/* string format wanted */
        !          2917:     fprintf(fp,"\\x%02x",(rp->pixmap)[ibyte]);	/*print byte as hex char*/
        !          2918:   else					/* comma-separated format wanted */
        !          2919:     fprintf(fp,"0x%02x",(rp->pixmap)[ibyte]);	/*print byte as hex number*/
        !          2920:   /* --- add a separator and newline, etc, as necessary --- */
        !          2921:   if ( ibyte < nbytes-1)		/* not the last byte yet */
        !          2922:     {
        !          2923:     if ( !isstr ) fprintf(fp,",");	/* follow hex number with comma */
        !          2924:     if ( (ibyte+1)%ncols==0 )		/* need new line after every ncols */
        !          2925:       if ( !isstr )			/* for hex numbers format ... */
        !          2926: 	fprintf(fp,"\n%.*s",col1,stub);	/* ...just need newline and stub */
        !          2927:       else				/* for string format... */
        !          2928: 	fprintf(fp,"\"\n%.*s\"",col1,stub); /* ...need closing, opening "s */
        !          2929:     } /* --- end-of-if(ibyte<nbytes-1) --- */
        !          2930:   } /* --- end-of-for(ibyte) --- */
        !          2931: if ( isstr ) fprintf(fp,"\"");		/* closing " after last line */
        !          2932: return ( 1 );				/* back with 1=okay, 0=failed */
        !          2933: } /* --- end-of-function hex_bitmap() --- */
        !          2934: 
        !          2935: 
        !          2936: /* ==========================================================================
        !          2937:  * Function:	emit_string ( fp, col1, string, comment )
        !          2938:  * Purpose:	Emit string on fp, starting in col1,
        !          2939:  *		and followed by right-justified comment.
        !          2940:  * --------------------------------------------------------------------------
        !          2941:  * Arguments:	fp (I)		File ptr to output device (defaults to
        !          2942:  *				stdout if passed as NULL).
        !          2943:  *		col1 (I)	int containing 0 or #blanks preceding string
        !          2944:  *		string (I)	char *  containing string to be emitted.
        !          2945:  *				If last char of string is '\n',
        !          2946:  *				the emitted line ends with a newline,
        !          2947:  *				otherwise not.
        !          2948:  *		comment (I)	NULL or char * containing right-justified
        !          2949:  *				comment (we enclose between /star and star/)
        !          2950:  * --------------------------------------------------------------------------
        !          2951:  * Returns:	( int )		1 if completed successfully,
        !          2952:  *				or 0 otherwise (for any error).
        !          2953:  * --------------------------------------------------------------------------
        !          2954:  * Notes:     o
        !          2955:  * ======================================================================= */
        !          2956: /* --- entry point --- */
        !          2957: int	emit_string ( FILE *fp, int col1, char *string, char *comment )
        !          2958: {
        !          2959: /* -------------------------------------------------------------------------
        !          2960: Allocations and Declarations
        !          2961: -------------------------------------------------------------------------- */
        !          2962: char	line[256];		/* construct line with caller's fields */
        !          2963: int	fieldlen;		/* #chars in one of caller's fields */
        !          2964: int	linelen = 72;		/*line length (for right-justified comment)*/
        !          2965: int	isnewline = 0;		/* true to emit \n at end of line */
        !          2966: /* --------------------------------------------------------------------------
        !          2967: construct line containing prolog, string, epilog, and finally comment
        !          2968: -------------------------------------------------------------------------- */
        !          2969: /* --- init line --- */
        !          2970: memset(line,' ',255);			/* start line with blanks */
        !          2971: /* --- embed string into line --- */
        !          2972: if ( string != NULL )			/* if caller gave us a string... */
        !          2973:   { fieldlen = strlen(string);		/* #cols required for string */
        !          2974:     if ( string[fieldlen-1] == '\n' )	/* check last char for newline */
        !          2975:       {	isnewline = 1;			/* got it, so set flag */
        !          2976: 	fieldlen--; }			/* but don't print it yet */
        !          2977:     memcpy(line+col1,string,fieldlen);	/* embid string starting at col1 */
        !          2978:     col1 += fieldlen; }			/* bump col past epilog */
        !          2979: /* --- embed comment into line --- */
        !          2980: if ( comment != NULL )			/* if caller gave us a comment... */
        !          2981:   { fieldlen = 6 + strlen(comment);	/* plus  /star, star/, 2 spaces */
        !          2982:     if ( linelen-fieldlen < col1 )	/* comment won't fit */
        !          2983:       fieldlen -= (col1 - (linelen-fieldlen)); /* truncate comment to fit */
        !          2984:     if ( fieldlen > 6 )			/* can fit all or part of comment */
        !          2985:       sprintf(line+linelen-fieldlen,"/%c %.*s %c/", /* so embed it in line */
        !          2986: 	'*', fieldlen-6,comment, '*');
        !          2987:     col1 = linelen; }			/* indicate line filled */
        !          2988: /* --- line completed --- */
        !          2989: line[col1] = '\000';			/* null-terminate completed line */
        !          2990: /* -------------------------------------------------------------------------
        !          2991: emit line, then back to caller with 1=okay, 0=failed.
        !          2992: -------------------------------------------------------------------------- */
        !          2993: /* --- first redirect null fp --- */
        !          2994: if ( fp == (FILE *)NULL ) fp = stdout;	/* default fp to stdout if null */
        !          2995: /* --- emit line (and optional newline) --- */
        !          2996: fprintf(fp,"%.*s",linelen,line);	/* no more than linelen chars */
        !          2997: if ( isnewline ) fprintf(fp,"\n");	/*caller wants terminating newline*/
        !          2998: return ( 1 );
        !          2999: } /* --- end-of-function emit_string() --- */
        !          3000: 
        !          3001: 
        !          3002: /* ==========================================================================
        !          3003:  * Function:	get_symdef ( symbol )
        !          3004:  * Purpose:	returns mathchardef struct for symbol
        !          3005:  * --------------------------------------------------------------------------
        !          3006:  * Arguments:	symbol (I)	char *  containing symbol
        !          3007:  *				whose corresponding mathchardef is wanted
        !          3008:  * --------------------------------------------------------------------------
        !          3009:  * Returns:	( mathchardef * )  pointer to struct defining symbol,
        !          3010:  *				or NULL for any error
        !          3011:  * --------------------------------------------------------------------------
        !          3012:  * Notes:     o	Input symbol need only contain a leading substring to match,
        !          3013:  *		e.g., \gam passed in symbol will match \gamma in the table.
        !          3014:  *		If the table contains two or more possible matches,
        !          3015:  *		the shortest is returned, e.g., input \e will return with
        !          3016:  *		data for \eta rather than \epsilon.  To get \epsilon,
        !          3017:  *		you must pass a leading substring long enough to eliminate
        !          3018:  *		shorter table matches, i.e., in this case \ep
        !          3019:  * ======================================================================= */
        !          3020: /* --- entry point --- */
        !          3021: mathchardef *get_symdef ( char *symbol )
        !          3022: {
        !          3023: /* -------------------------------------------------------------------------
        !          3024: Allocations and Declarations
        !          3025: -------------------------------------------------------------------------- */
        !          3026: mathchardef *symdefs = symtable;	/* table of mathchardefs */
        !          3027: int	idef = 0,			/* symdefs[] index */
        !          3028: 	bestdef = (-9999);		/*index of shortest matching symdef*/
        !          3029: int	symlen = strlen(symbol),	/* length of input symbol */
        !          3030: 	deflen, minlen=9999;		/*length of shortest matching symdef*/
        !          3031: int	/*alnumsym = (symlen==1 && isalnum(*symbol)),*/ /*alphanumeric sym*/
        !          3032: 	alphasym = (symlen==1 && isalpha(*symbol)); /* or alpha symbol */
        !          3033: static	char *displaysyms[][2] = {	/*xlate to Big sym for \displaystyle*/
        !          3034: 	{"\\int",	"\\Bigint"},
        !          3035: 	{"\\oint",	"\\Bigoint"},
        !          3036: 	{"\\sum",	"\\Bigsum"},
        !          3037: 	{"\\prod",	"\\Bigprod"},
        !          3038: 	{"\\coprod",	"\\Bigcoprod"},
        !          3039: 	{"\\cup",	"\\Bigcup"},
        !          3040: 	{"\\sqcup",	"\\Bigsqcup"},
        !          3041: 	{"\\cap",	"\\Bigcap"},
        !          3042: 	{"\\sqcap",	"\\sqcap"},	/* don't have \Bigsqcap */
        !          3043: 	{"\\odot",	"\\Bigodot"},
        !          3044: 	{"\\oplus",	"\\Bigoplus"},
        !          3045: 	{"\\otimes",	"\\Bigotimes"},
        !          3046: 	{"\\uplus",	"\\Biguplus"},
        !          3047: 	{"\\wedge",	"\\Bigwedge"},
        !          3048: 	{"\\vee",	"\\Bigvee"},
        !          3049: 	{NULL, NULL} };
        !          3050: /* -------------------------------------------------------------------------
        !          3051: If in \displaystyle mode, first xlate int to Bigint, etc.
        !          3052: -------------------------------------------------------------------------- */
        !          3053: if ( isdisplaystyle > 1 )		/* we're in \displaystyle mode */
        !          3054:   for ( idef=0; ; idef++ ) {		/* lookup symbol in displaysyms */
        !          3055:     char *fromsym = displaysyms[idef][0], /* look for this symbol */
        !          3056: 	 *tosym = displaysyms[idef][1];	  /* and xlate it to this symbol */
        !          3057:     if ( fromsym == NULL ) break;	/* end-of-table */
        !          3058:     if ( !strcmp(symbol,fromsym) )	/* found a match */
        !          3059:       {	if ( msglevel>=99 && msgfp!=NULL ) /* debugging output */
        !          3060: 	 { fprintf(msgfp,"get_symdef> isdisplaystyle=%d, xlated %s to %s\n",
        !          3061: 	   isdisplaystyle,symbol,tosym); fflush(msgfp); }
        !          3062: 	symbol = tosym;			/* so look up tosym instead */
        !          3063: 	symlen = strlen(symbol);	/* reset symbol length */
        !          3064: 	break; }			/* no need to search further */
        !          3065:     } /* --- end-of-for(idef) --- */
        !          3066: /* -------------------------------------------------------------------------
        !          3067: search symdefs[] in order for first occurrence of symbol
        !          3068: -------------------------------------------------------------------------- */
        !          3069: for ( idef=0; ;idef++ )			/* until trailer record found */
        !          3070:   if ( symdefs[idef].symbol == NULL ) break; /* reached end-of-table */
        !          3071:   else					/* check against caller's symbol */
        !          3072:     if ( strncmp(symbol,symdefs[idef].symbol,symlen) == 0 ) /* found match */
        !          3073:      if ( symdefs[idef].handler != NULL	/* mode irrelevant for directives */
        !          3074:      ||  istext==0			/* mathmode, so use first match */
        !          3075:      || (istext==1 && symdefs[idef].family==CMR10)   /*textmode && rm text*/
        !          3076:      || (istext==2 && symdefs[idef].family==CMMI10)  /*textmode && it text*/
        !          3077:      || (istext==3 && symdefs[idef].family==BBOLD10) /*textmode && bb text*/
        !          3078:      || (istext!=3 && !alphasym) )	/* not bb and not alpha */
        !          3079:       if ( (deflen=strlen(symdefs[idef].symbol)) < minlen ) /*new best match*/
        !          3080: 	{ bestdef = idef;		/* save index of new best match */
        !          3081: 	  if ( (minlen = deflen)	/* and save its len for next test */
        !          3082: 	  ==  symlen ) break; }		/*perfect match, so return with it*/
        !          3083: if ( bestdef < 0 )			/* failed to look up symbol */
        !          3084:   if ( istext != 0 )			/* we're in a restricted font mode */
        !          3085:     { int wastext = istext;		/* save current mode */
        !          3086:       mathchardef *symdef = NULL;	/* lookup result with istext=0 */
        !          3087:       istext = 0;			/*try to look up symbol in any font*/
        !          3088:       symdef = get_symdef(symbol);	/* repeat lookup with istext=0 */
        !          3089:       istext = wastext;			/* reset font mode */
        !          3090:       return symdef; }			/* caller gets istext=0 lookup */
        !          3091: if ( msgfp!=NULL && msglevel>=999 )	/* debugging output */
        !          3092:   { fprintf(msgfp,"get_symdefs> symbol=%s matches symtable[%d]=%s\n",
        !          3093:     symbol,bestdef,(bestdef<0?"NotFound":symdefs[bestdef].symbol));
        !          3094:     fflush(msgfp); }
        !          3095: return ( (bestdef<0? NULL : &(symdefs[bestdef])) ); /*NULL or best symdef[]*/
        !          3096: } /* --- end-of-function get_symdef() --- */
        !          3097: 
        !          3098: 
        !          3099: /* ==========================================================================
        !          3100:  * Function:	get_chardef ( symdef, size )
        !          3101:  * Purpose:	returns chardef ptr containing data for symdef at given size
        !          3102:  * --------------------------------------------------------------------------
        !          3103:  * Arguments:	symdef (I)	mathchardef *  corresponding to symbol
        !          3104:  *				whose corresponding chardef is wanted
        !          3105:  *		size (I)	int containing 0-4 for desired size
        !          3106:  * --------------------------------------------------------------------------
        !          3107:  * Returns:	( chardef * )	pointer to struct defining symbol at size,
        !          3108:  *				or NULL for any error
        !          3109:  * --------------------------------------------------------------------------
        !          3110:  * Notes:     o	if size unavailable, the next-closer-to-normalsize
        !          3111:  *		is returned instead.
        !          3112:  * ======================================================================= */
        !          3113: /* --- entry point --- */
        !          3114: chardef	*get_chardef ( mathchardef *symdef, int size )
        !          3115: {
        !          3116: /* -------------------------------------------------------------------------
        !          3117: Allocations and Declarations
        !          3118: -------------------------------------------------------------------------- */
        !          3119: fontfamily  *fonts = fonttable;		/* table of font families */
        !          3120: chardef	**fontdef,			/*tables for desired font, by size*/
        !          3121: 	*gfdata = (chardef *)NULL;	/* chardef for symdef,size */
        !          3122: int	ifont;				/* fonts[] index */
        !          3123: int	family, charnum;		/* indexes retrieved from symdef */
        !          3124: int	sizeinc = 0,			/*+1 or -1 to get closer to normal*/
        !          3125: 	normalsize = 2;			/* this size always present */
        !          3126: int	isBig = 0;			/*true if symbol's 1st char is upper*/
        !          3127: char	*symptr = NULL;			/* look for 1st alpha of symbol */
        !          3128: /* -------------------------------------------------------------------------
        !          3129: initialization
        !          3130: -------------------------------------------------------------------------- */
        !          3131: /* --- check symdef --- */
        !          3132: if ( symdef == NULL ) return ( NULL );	/* get_symdef() probably failed */
        !          3133: /* --- get local copy of indexes from symdef --- */
        !          3134: family = symdef->family;		/* font family containing symbol */
        !          3135: charnum = symdef->charnum;		/* char# of symbol within font */
        !          3136: /* --- check for supersampling --- */
        !          3137: if ( issupersampling )			/* check for supersampling fonts */
        !          3138:  if ( fonts != ssfonttable )		/* uh oh--probably internal error */
        !          3139:   { fonts = ssfonttable; }		/* force it */
        !          3140: /* --- check requested size, and set size increment --- */
        !          3141: if ( 0 && issupersampling )		/* set size index for supersampling */
        !          3142:   size = LARGESTSIZE+1;			/* index 1 past largest size */
        !          3143: else					/* low pass indexes 0...LARGESTSIZE */
        !          3144:   {
        !          3145:   if( size<0 ) size = 0;		/* size was definitely too small */
        !          3146:   if( size>LARGESTSIZE ) size = LARGESTSIZE;  /* or definitely too large */
        !          3147:   if( size<normalsize ) sizeinc = (+1);	/*use next larger if size too small*/
        !          3148:   if( size>normalsize ) sizeinc = (-1);	/*or next smaller if size too large*/
        !          3149:   }
        !          3150: /* --- check for really big symbol (1st char of symbol name uppercase) --- */
        !          3151: for ( symptr=symdef->symbol; *symptr!='\000'; symptr++ ) /*skip leading \'s*/
        !          3152:   if ( isalpha(*symptr) )		/* found leading alpha char */
        !          3153:     { isBig = isupper(*symptr);		/* is 1st char of name uppercase? */
        !          3154:       if ( !isBig			/* 1st char lowercase */
        !          3155:       &&   strlen(symptr) >= 4 )	/* but followed by at least 3 chars */
        !          3156:        isBig = !memcmp(symptr,"big\\",4) /* isBig if name starts with big\ */
        !          3157: 	|| !memcmp(symptr,"bigg",4);	/* or with bigg */
        !          3158:       break; }				/* don't check beyond 1st char */
        !          3159: /* -------------------------------------------------------------------------
        !          3160: find font family in table of fonts[]
        !          3161: -------------------------------------------------------------------------- */
        !          3162: /* --- look up font family --- */
        !          3163: for ( ifont=0; ;ifont++ )		/* until trailer record found */
        !          3164:   if ( fonts[ifont].family < 0 ) return ( NULL ); /* error, no such family */
        !          3165:   else if ( fonts[ifont].family == family ) break; /* found font family */
        !          3166: /* --- get local copy of table for this family by size --- */
        !          3167: fontdef = fonts[ifont].fontdef;		/* font by size */
        !          3168: /* -------------------------------------------------------------------------
        !          3169: get font in desired size, or closest available size, and return symbol
        !          3170: -------------------------------------------------------------------------- */
        !          3171: /* --- get font in desired size --- */
        !          3172: while ( 1 )				/* find size or closest available */
        !          3173:   if ( fontdef[size] != NULL ) break;	/* found available size */
        !          3174:   else					/* adjust size closer to normal */
        !          3175:     if ( size == NORMALSIZE		/* already normal so no more sizes,*/
        !          3176:     || sizeinc == 0 ) return ( NULL);	/* or must be supersampling */
        !          3177:     else				/*bump size 1 closer to NORMALSIZE*/
        !          3178:       size += sizeinc;			/* see if adjusted size available */
        !          3179: /* --- ptr to chardef struct --- */
        !          3180: gfdata = &((fontdef[size])[charnum]);	/*ptr to chardef for symbol in size*/
        !          3181: /* -------------------------------------------------------------------------
        !          3182: kludge to tweak CMEX10 (which appears to have incorrect descenders)
        !          3183: -------------------------------------------------------------------------- */
        !          3184: if ( family == CMEX10 )			/* cmex10 needs tweak */
        !          3185:   { int height = gfdata->toprow - gfdata->botrow + 1; /*total height of char*/
        !          3186:     gfdata->botrow = (isBig? (-height/3) : (-height/4));
        !          3187:     gfdata->toprow = gfdata->botrow + gfdata->image.height; }
        !          3188: /* -------------------------------------------------------------------------
        !          3189: return subraster containing chardef data for symbol in requested size
        !          3190: -------------------------------------------------------------------------- */
        !          3191: return ( gfdata );			/*ptr to chardef for symbol in size*/
        !          3192: } /* --- end-of-function get_chardef() --- */
        !          3193: 
        !          3194: 
        !          3195: /* ==========================================================================
        !          3196:  * Function:	get_charsubraster ( symdef, size )
        !          3197:  * Purpose:	returns new subraster ptr containing
        !          3198:  *		data for symdef at given size
        !          3199:  * --------------------------------------------------------------------------
        !          3200:  * Arguments:	symdef (I)	mathchardef *  corresponding to symbol
        !          3201:  *				whose corresponding chardef is wanted
        !          3202:  *		size (I)	int containing 0-4 for desired size
        !          3203:  * --------------------------------------------------------------------------
        !          3204:  * Returns:	( subraster * )	pointer to struct defining symbol at size,
        !          3205:  *				or NULL for any error
        !          3206:  * --------------------------------------------------------------------------
        !          3207:  * Notes:     o	just wraps a subraster envelope around get_chardef()
        !          3208:  * ======================================================================= */
        !          3209: /* --- entry point --- */
        !          3210: subraster *get_charsubraster ( mathchardef *symdef, int size )
        !          3211: {
        !          3212: /* -------------------------------------------------------------------------
        !          3213: Allocations and Declarations
        !          3214: -------------------------------------------------------------------------- */
        !          3215: chardef	*get_chardef(), *gfdata=NULL;	/* chardef struct for symdef,size */
        !          3216: int	get_baseline();			/* baseline of gfdata */
        !          3217: subraster *new_subraster(), *sp=NULL;	/* subraster containing gfdata */
        !          3218: int	aasupsamp(),			/*antialias char with supersampling*/
        !          3219: 	grayscale=256;			/* aasupersamp() parameters */
        !          3220: /* -------------------------------------------------------------------------
        !          3221: look up chardef for symdef at size, and embed data (gfdata) in subraster
        !          3222: -------------------------------------------------------------------------- */
        !          3223: if ( (gfdata=get_chardef(symdef,size))	/* look up chardef for symdef,size */
        !          3224: !=   NULL )				/* and check that we found it */
        !          3225:  if ( (sp=new_subraster(0,0,0))		/* allocate subraster "envelope" */
        !          3226:  !=   NULL )				/* and check that we succeeded */
        !          3227:   {
        !          3228:   sp->type = CHARASTER;			/* static character raster */
        !          3229:   sp->symdef = symdef;			/* replace NULL with caller's arg */
        !          3230:   sp->size = size;			/*replace default with caller's size*/
        !          3231:   sp->baseline = get_baseline(gfdata);	/* get baseline of character */
        !          3232:   sp->image    = &(gfdata->image);	/* store ptr to its bitmap */
        !          3233:   if ( issupersampling )		/* antialias character right here */
        !          3234:     {
        !          3235:     raster *aa = NULL;			/* antialiased char raster */
        !          3236:     int status = aasupsamp(sp->image,&aa,shrinkfactor,grayscale);
        !          3237:     if ( status )			/* supersampled successfully */
        !          3238:       {	int baseline = sp->baseline;	/* baseline before supersampling */
        !          3239: 	int height = gfdata->image.height; /* #rows before supersampling */
        !          3240: 	sp->image = aa;			/* replace chardef with ss image */
        !          3241: 	if ( baseline >= height-1 )	/* baseline at bottom of char */
        !          3242: 	  sp->baseline = aa->height -1;	/* so keep it at bottom */
        !          3243: 	else				/* char has descenders */
        !          3244: 	  sp->baseline /= shrinkfactor;	/* rescale baseline */
        !          3245: 	sp->type = IMAGERASTER; }	/* character is an image raster */
        !          3246:     } /* --- end-of-if(issupersampling) --- */
        !          3247:   } /* --- end-of-if(sp!=NULL) --- */
        !          3248: if ( msgfp!=NULL && msglevel>=999 )
        !          3249:   { fprintf(msgfp,"get_charsubraster> requested symbol=\"%s\" baseline=%d\n",
        !          3250:     symdef->symbol, (sp==NULL?0:sp->baseline)); fflush(msgfp); }
        !          3251: return ( sp );				/* back to caller */
        !          3252: } /* --- end-of-function get_charsubraster() --- */
        !          3253: 
        !          3254: 
        !          3255: /* ==========================================================================
        !          3256:  * Function:	get_baseline ( gfdata )
        !          3257:  * Purpose:	returns baseline for a chardef struct
        !          3258:  * --------------------------------------------------------------------------
        !          3259:  * Arguments:	gfdata (I)	chardef *  containing chardef for symbol
        !          3260:  *				whose baseline is wanted
        !          3261:  * --------------------------------------------------------------------------
        !          3262:  * Returns:	( int )		baseline for symdef,
        !          3263:  *				or -1 for any error
        !          3264:  * --------------------------------------------------------------------------
        !          3265:  * Notes:     o	Unlike TeX, the top-left corners of our rasters are (0,0),
        !          3266:  *		with (row,col) increasing as you move down and right.
        !          3267:  *		Baselines are calculated with respect to this scheme,
        !          3268:  *		so 0 would mean the very top row is on the baseline
        !          3269:  *		and everything else descends below the baseline.
        !          3270:  * ======================================================================= */
        !          3271: /* --- entry point --- */
        !          3272: int	get_baseline ( chardef *gfdata )
        !          3273: {
        !          3274: /* -------------------------------------------------------------------------
        !          3275: Allocations and Declarations
        !          3276: -------------------------------------------------------------------------- */
        !          3277: int	/*toprow = gfdata->toprow,*/	/*TeX top row from .gf file info*/
        !          3278: 	botrow = gfdata->botrow,	/*TeX bottom row from .gf file info*/
        !          3279: 	height = gfdata->image.height;	/* #rows comprising symbol */
        !          3280: /* -------------------------------------------------------------------------
        !          3281: give caller baseline
        !          3282: -------------------------------------------------------------------------- */
        !          3283: return ( (height-1) + botrow );		/* note: descenders have botrow<0 */
        !          3284: } /* --- end-of-function get_baseline() --- */
        !          3285: 
        !          3286: 
        !          3287: /* ==========================================================================
        !          3288:  * Function:	get_delim ( char *symbol, int height, int family )
        !          3289:  * Purpose:	returns subraster corresponding to the samllest
        !          3290:  *		character containing symbol, but at least as large as height,
        !          3291:  *		and in caller's family (if specified).
        !          3292:  *		If no symbol character as large as height is available,
        !          3293:  *		then the largest availabale character is returned instead.
        !          3294:  * --------------------------------------------------------------------------
        !          3295:  * Arguments:	symbol (I)	char *  containing (substring of) desired
        !          3296:  *				symbol, e.g., if symbol="(", then any
        !          3297:  *				mathchardef like "(" or "\\(", etc, match.
        !          3298:  *		height (I)	int containing minimum acceptable height
        !          3299:  *				for returned character
        !          3300:  *		family (I)	int containing -1 to consider all families,
        !          3301:  *				or, e.g., CMEX10 for only that family
        !          3302:  * --------------------------------------------------------------------------
        !          3303:  * Returns:	( subraster * )	best matching character available,
        !          3304:  *				or NULL for any error
        !          3305:  * --------------------------------------------------------------------------
        !          3306:  * Notes:     o	If height is passed as negative, its absolute value is used
        !          3307:  *		but the best-fit width is searched for (rather than height)
        !          3308:  * ======================================================================= */
        !          3309: /* --- entry point --- */
        !          3310: subraster *get_delim ( char *symbol, int height, int family )
        !          3311: {
        !          3312: /* -------------------------------------------------------------------------
        !          3313: Allocations and Declarations
        !          3314: -------------------------------------------------------------------------- */
        !          3315: mathchardef *symdefs = symtable;	/* table of mathchardefs */
        !          3316: subraster *get_charsubraster(), *sp=(subraster *)NULL; /* best match char */
        !          3317: subraster *make_delim();		/* construct delim if can't find it*/
        !          3318: chardef	*get_chardef(), *gfdata=NULL;	/* get chardef struct for a symdef */
        !          3319: char	lcsymbol[256], *symptr,		/* lowercase symbol for comparison */
        !          3320: 	*unescsymbol = symbol;		/* unescaped symbol */
        !          3321: int	symlen = (symbol==NULL?0:strlen(symbol)), /* #chars in caller's sym*/
        !          3322: 	deflen = 0;			/* length of symdef (aka lcsymbol) */
        !          3323: int	idef = 0,			/* symdefs[] index */
        !          3324: 	bestdef = (-9999),		/* index of best fit symdef */
        !          3325: 	bigdef = (-9999);		/*index of biggest (in case no best)*/
        !          3326: int	size = 0,			/* size index 0...LARGESTSIZE */
        !          3327: 	bestsize = (-9999),		/* index of best fit size */
        !          3328: 	bigsize = (-9999);		/*index of biggest (in case no best)*/
        !          3329: int	defheight, bestheight=9999,	/* height of best fit symdef */
        !          3330: 	bigheight = (-9999);		/*height of biggest(in case no best)*/
        !          3331: int	iswidth = 0;			/* true if best-fit width desired */
        !          3332: int	isunesc = 0,			/* true if leading escape removed */
        !          3333: 	issq=0, isoint=0;		/* true for \sqcup,etc, \oint,etc */
        !          3334: char	*bigint="bigint", *bigoint="bigoint"; /* substitutes for int, oint */
        !          3335: /* -------------------------------------------------------------------------
        !          3336: determine if searching height or width, and search symdefs[] for best-fit
        !          3337: -------------------------------------------------------------------------- */
        !          3338: /* --- arg checks --- */
        !          3339: if ( symlen < 1 ) return (sp);		/* no input symbol suplied */
        !          3340: if ( strcmp(symbol,"e") == 0 ) return(sp); /* e causes segfault??? */
        !          3341: /* --- ignore leading escapes for CMEX10 --- */
        !          3342: if ( 1 )				/* ignore leading escape */
        !          3343:  if ( (family==CMEX10 || family==CMSYEX) ) { /* for CMEX10 or CMSYEX */
        !          3344:   if ( strstr(symbol,"sq") != NULL )	/* \sq symbol requested */
        !          3345:      issq = 1;				/* seq \sq signal */
        !          3346:   if ( strstr(symbol,"oint") != NULL )	/* \oint symbol requested */
        !          3347:      isoint = 1;			/* seq \oint signal */
        !          3348:   if ( *symbol=='\\' )			/* have leading \ */
        !          3349:    { unescsymbol = symbol+1;		/* push past leading \ */
        !          3350:      if ( --symlen < 1 ) return(sp);	/* one less char */
        !          3351:      if ( strcmp(unescsymbol,"int") == 0 ) /* \int requested by caller */
        !          3352:        unescsymbol = bigint;		/* but big version looks better */
        !          3353:      if ( strcmp(unescsymbol,"oint") == 0 ) /* \oint requested by caller */
        !          3354:        unescsymbol = bigoint;		/* but big version looks better */
        !          3355:      symlen = strlen(unescsymbol);	/* explicitly recalculate length */
        !          3356:      isunesc = 1; }			/* signal leading escape removed */
        !          3357:   } /* --- end-of-if(family) --- */
        !          3358: /* --- determine whether searching for best-fit height or width --- */
        !          3359: if ( height < 0 )			/* negative signals width search */
        !          3360:   { height = (-height);			/* flip "height" positive */
        !          3361:     iswidth = 1; }			/* set flag for width search */
        !          3362: /* --- search symdefs[] for best-fit height (or width) --- */
        !          3363: for ( idef=0; ;idef++ )			/* until trailer record found */
        !          3364:  {
        !          3365:  char *defsym = symdefs[idef].symbol;	/* local copies */
        !          3366:  int  deffam  = symdefs[idef].family;
        !          3367:  if ( defsym == NULL ) break;		/* reached end-of-table */
        !          3368:  else					/* check against caller's symbol */
        !          3369:   if ( family<0 || deffam == family	/* if explicitly in caller's family*/
        !          3370:   ||  (family==CMSYEX && (deffam==CMSY10||deffam==CMEX10)) )
        !          3371:     {
        !          3372:     strcpy(lcsymbol,defsym);		/* local copy of symdefs[] symbol */
        !          3373:     if ( isunesc && *lcsymbol=='\\' )	/* ignored leading \ in symbol */
        !          3374:      strcpy(lcsymbol,lcsymbol+1);	/* so squeeze it out of lcsymbol too*/
        !          3375:     if ( 0 )				/* don't ignore case */
        !          3376:      for ( symptr=lcsymbol; *symptr!='\000'; symptr++ ) /*for each symbol ch*/
        !          3377:       if ( isalpha(*symptr) ) *symptr=tolower(*symptr); /*lowercase the char*/
        !          3378:     deflen = strlen(lcsymbol);		/* #chars in symbol we're checking */
        !          3379:     if ((symptr=strstr(lcsymbol,unescsymbol)) != NULL) /*found caller's sym*/
        !          3380:      if ( (isoint || strstr(lcsymbol,"oint")==NULL) /* skip unwanted "oint"*/
        !          3381:      &&   (issq || strstr(lcsymbol,"sq")==NULL) ) /* skip unwanted "sq" */
        !          3382:       if ( (deffam == CMSY10 ?		/* CMSY10 or not CMSY10 */
        !          3383: 	  symptr == lcsymbol		/* caller's sym is a prefix */
        !          3384:           && deflen == symlen:		/* and same length */
        !          3385: 	  symptr == lcsymbol		/* caller's sym is a prefix */
        !          3386:           || symptr == lcsymbol+deflen-symlen) ) /* or a suffix */
        !          3387:        for ( size=0; size<=LARGESTSIZE; size++ ) /* check all font sizes */
        !          3388: 	if ( (gfdata=get_chardef(&(symdefs[idef]),size)) != NULL ) /*got one*/
        !          3389: 	  { defheight = gfdata->image.height;	/* height of this character */
        !          3390: 	    if ( iswidth )		/* width search wanted instead... */
        !          3391: 	      defheight = gfdata->image.width;	/* ...so substitute width */
        !          3392: 	    leftsymdef = &(symdefs[idef]);	/* set symbol class, etc */
        !          3393: 	    if ( defheight>=height && defheight<bestheight ) /*new best fit*/
        !          3394: 	      { bestdef=idef; bestsize=size;	/* save indexes of best fit */
        !          3395: 		bestheight = defheight; }	/* and save new best height */
        !          3396: 	    if ( defheight >= bigheight )	/* new biggest character */
        !          3397: 	      { bigdef=idef; bigsize=size;	/* save indexes of biggest */
        !          3398: 		bigheight = defheight; }	/* and save new big height */
        !          3399:           } /* --- end-of-if(gfdata!=NULL) --- */
        !          3400:     } /* --- end-of-if(family) --- */
        !          3401:  } /* --- end-of-for(idef) --- */
        !          3402: /* -------------------------------------------------------------------------
        !          3403: construct subraster for best fit character, and return it to caller
        !          3404: -------------------------------------------------------------------------- */
        !          3405: if ( bestdef >= 0 )			/* found a best fit for caller */
        !          3406:   sp = get_charsubraster(&(symdefs[bestdef]),bestsize); /* best subraster */
        !          3407: if ( sp==NULL && height-bigheight>5 )	/* try to construct delim */
        !          3408:   sp = make_delim(symbol,(iswidth?-height:height)); /* try to build delim */
        !          3409: if ( sp==NULL && bigdef>=0 )		/* just give biggest to caller */
        !          3410:   sp = get_charsubraster(&(symdefs[bigdef]),bigsize); /* biggest subraster */
        !          3411: return ( sp );
        !          3412: } /* --- end-of-function get_delim() --- */
        !          3413: 
        !          3414: 
        !          3415: /* ==========================================================================
        !          3416:  * Function:	make_delim ( char *symbol, int height )
        !          3417:  * Purpose:	constructs subraster corresponding to symbol
        !          3418:  *		exactly as large as height,
        !          3419:  * --------------------------------------------------------------------------
        !          3420:  * Arguments:	symbol (I)	char *  containing, e.g., if symbol="("
        !          3421:  *				for desired delimiter
        !          3422:  *		height (I)	int containing height
        !          3423:  *				for returned character
        !          3424:  * --------------------------------------------------------------------------
        !          3425:  * Returns:	( subraster * )	constructed delimiter
        !          3426:  *				or NULL for any error
        !          3427:  * --------------------------------------------------------------------------
        !          3428:  * Notes:     o	If height is passed as negative, its absolute value is used
        !          3429:  *		and interpreted as width (rather than height)
        !          3430:  * ======================================================================= */
        !          3431: /* --- entry point --- */
        !          3432: subraster *make_delim ( char *symbol, int height )
        !          3433: {
        !          3434: /* -------------------------------------------------------------------------
        !          3435: Allocations and Declarations
        !          3436: -------------------------------------------------------------------------- */
        !          3437: subraster *sp = (subraster *)NULL,	/* subraster returned to caller */
        !          3438: 	*new_subraster();		/* allocate subraster */
        !          3439: raster	*rasp = (raster *)NULL;		/* sp->image */
        !          3440: int	isokay=0, delete_subraster();	/* set true if delimiter drawn ok */
        !          3441: int	pixsz = 1;			/* pixels are one bit each */
        !          3442: int	thickness = 1;			/* drawn lines are one pixel thick */
        !          3443: int	aspectratio = 8;		/* default height/width for parens */
        !          3444: int	iswidth = 0,			/*true if width specified by height*/
        !          3445: 	width = height;			/* #pixels width (e.g., of ellipse)*/
        !          3446: char	*lp=NULL,  *rp=NULL, *strchr(),	/* check symbol for left or right */
        !          3447: 	*lp2=NULL, *rp2=NULL;		/* synonym for lp,rp */
        !          3448: int	circle_raster(),		/* ellipse for ()'s in sp->image */
        !          3449: 	rule_rsater(),			/* horizontal or vertical lines */
        !          3450: 	line_raster();			/* line between two points */
        !          3451: /* -------------------------------------------------------------------------
        !          3452: initialization
        !          3453: -------------------------------------------------------------------------- */
        !          3454: /* --- determine whether constructing height or width --- */
        !          3455: if ( height < 0 )			/* negative "height" signals width */
        !          3456:   { width = height = (-height);		/* flip height positive */
        !          3457:     iswidth = 1; }			/* set flag for width */
        !          3458: if ( height < 3 ) goto end_of_job;	/* too small, must be error */
        !          3459: /* --- set default width (or height) accordingly --- */
        !          3460: if ( iswidth ) height =  (width+(aspectratio+1)/2)/aspectratio;
        !          3461: else            width = (height+(aspectratio+1)/2)/aspectratio;
        !          3462: if ( strchr(symbol,'=') != NULL		/* left or right || bracket wanted */
        !          3463: ||   strstr(symbol,"\\|") != NULL )	/* same || in standard tex notation*/
        !          3464:   width = max2(width,5);		/* need space between two |'s */
        !          3465: if ( width < 2 ) width=2;		/* set min width */
        !          3466: if ( strchr(symbol,'(') != NULL		/* if left ( */
        !          3467: ||   strchr(symbol,')') != NULL )	/* or right ) paren wanted */
        !          3468:   width = (3*width)/2;			/* adjust width */
        !          3469: /* --- allocate and initialize subraster for constructed delimiter --- */
        !          3470: if ( (sp=new_subraster(width,height,pixsz)) /* allocate new subraster */
        !          3471: ==   NULL )  goto end_of_job;		/* quit if failed */
        !          3472: /* --- initialize delimiter subraster parameters --- */
        !          3473: sp->type = IMAGERASTER;			/* image */
        !          3474: sp->symdef = NULL;			/* not applicable for image */
        !          3475: sp->baseline = height/2 + 2;		/* is a little above center good? */
        !          3476: sp->size = NORMALSIZE;			/* size (probably unneeded) */
        !          3477: rasp = sp->image;			/* pointer to image in subraster */
        !          3478: /* -------------------------------------------------------------------------
        !          3479: ( ) parens
        !          3480: -------------------------------------------------------------------------- */
        !          3481: if ( (lp=strchr(symbol,'(')) != NULL	/* left ( paren wanted */
        !          3482: ||   (rp=strchr(symbol,')')) != NULL )	/* right ) paren wanted */
        !          3483:   {
        !          3484:   int	mywidth = min2(width,20);	/* max width for ()'s */
        !          3485:   circle_raster ( rasp,			/* embedded raster image */
        !          3486: 	0, 0,				/* row0,col0 are upper-left corner */
        !          3487: 	height-1, mywidth-1,		/* row1,col1 are lower-right */
        !          3488: 	thickness,			/* line thickness is 1 pixel */
        !          3489: 	(rp==NULL?"23":"41") );		/* "1234" quadrants to be drawn */
        !          3490:   isokay = 1;				/* set flag */
        !          3491:   } /* --- end-of-if(left- or right-() paren wanted) --- */
        !          3492: /* -------------------------------------------------------------------------
        !          3493: [ ] brackets
        !          3494: -------------------------------------------------------------------------- */
        !          3495: else
        !          3496:  if ( (lp=strchr(symbol,'[')) != NULL	/* left [ bracket wanted */
        !          3497:  ||   (rp=strchr(symbol,']')) != NULL )	/* right ] bracket wanted */
        !          3498:   {
        !          3499:   /* --- rule_raster ( rasp, top, left, width, height, type=0 ) --- */
        !          3500:   int	mywidth = min2(width,12);	/* max width for horizontal bars */
        !          3501:   thickness = 1;			/* set lines 1 or 2 pixels thick */
        !          3502:   rule_raster(rasp, 0,0, mywidth,thickness, 0); /* top horizontal bar */
        !          3503:   rule_raster(rasp, height-thickness,0, mywidth,thickness, 0); /* bottom */
        !          3504:   if ( lp != NULL )			/* left [ bracket wanted */
        !          3505:    rule_raster(rasp, 0,0, thickness,height, 0); /* left vertical bar */
        !          3506:   if ( rp != NULL )			/* right ] bracket wanted */
        !          3507:    rule_raster(rasp, 0,mywidth-thickness, thickness,height, 0); /* right */
        !          3508:   isokay = 1;				/* set flag */
        !          3509:   } /* --- end-of-if(left- or right-[] bracket wanted) --- */
        !          3510: /* -------------------------------------------------------------------------
        !          3511: < > brackets
        !          3512: -------------------------------------------------------------------------- */
        !          3513: else
        !          3514:  if ( (lp=strchr(symbol,'<')) != NULL	/* left < bracket wanted */
        !          3515:  ||   (rp=strchr(symbol,'>')) != NULL )	/* right > bracket wanted */
        !          3516:   {
        !          3517:   /* --- line_raster ( rasp,  row0, col0,  row1, col1,  thickness ) --- */
        !          3518:   int	mywidth = min2(width,12);	/* max width for brackets */
        !          3519:   thickness = 1;			/* set line pixel thickness */
        !          3520:   if ( lp != NULL )			/* left < bracket wanted */
        !          3521:     { line_raster(rasp,height/2,0,0,mywidth-1,thickness);
        !          3522:       line_raster(rasp,height/2,0,height-1,mywidth-1,thickness); }
        !          3523:   if ( rp != NULL )			/* right > bracket wanted */
        !          3524:     { line_raster(rasp,height/2,mywidth-1,0,0,thickness);
        !          3525:       line_raster(rasp,height/2,mywidth-1,height-1,0,thickness); }
        !          3526:   isokay = 1;				/* set flag */
        !          3527:   } /* --- end-of-if(left- or right-<> bracket wanted) --- */
        !          3528: /* -------------------------------------------------------------------------
        !          3529: \- for | | brackets or \= for || || brackets
        !          3530: -------------------------------------------------------------------------- */
        !          3531: else
        !          3532:  if ( (lp=strchr(symbol,'-')) != NULL	/* left or right | bracket wanted */
        !          3533:  ||  (lp2=strchr(symbol,'|')) != NULL	/* synonym for | bracket */
        !          3534:  ||   (rp=strchr(symbol,'=')) != NULL	/* left or right || bracket wanted */
        !          3535:  || (rp2=strstr(symbol,"\\|"))!= NULL )	/* || in standard tex notation */
        !          3536:   {
        !          3537:   /* --- rule_raster ( rasp, top, left, width, height, type=0 ) --- */
        !          3538:   int	midcol = width/2;		/* middle col, left of mid if even */
        !          3539:   if ( rp  != NULL			/* left or right || bracket wanted */
        !          3540:   ||   rp2 != NULL )			/* or || in standard tex notation */
        !          3541:    { thickness = 1;			/* each | of || one pixel thick */
        !          3542:      rule_raster(rasp, 0,max2(0,midcol-2), thickness,height, 0); /* left */
        !          3543:      rule_raster(rasp, 0,min2(width,midcol+2), thickness,height, 0); }
        !          3544:   else					/*nb, lp2 spuriously set if rp2 set*/
        !          3545:    if ( lp  != NULL			/* left or right | bracket wanted */
        !          3546:    ||   lp2 != NULL )			/* ditto for synomym */
        !          3547:     { thickness = 1;			/* set | two pixels thick */
        !          3548:       rule_raster(rasp, 0,midcol, thickness,height, 0); } /*mid vertical bar*/
        !          3549:   isokay = 1;				/* set flag */
        !          3550:   } /* --- end-of-if(left- or right-[] bracket wanted) --- */
        !          3551: /* -------------------------------------------------------------------------
        !          3552: back to caller
        !          3553: -------------------------------------------------------------------------- */
        !          3554: end_of_job:
        !          3555:   if ( !isokay )			/* don't have requested delimiter */
        !          3556:     { delete_subraster(sp);		/* so free unneeded structure */
        !          3557:       sp = NULL; }			/* and signal error to caller */
        !          3558:   return ( sp );			/*back to caller with delim or NULL*/
        !          3559: } /* --- end-of-function make_delim() --- */
        !          3560: 
        !          3561: 
        !          3562: /* ==========================================================================
        !          3563:  * Function:	texchar ( expression, chartoken )
        !          3564:  * Purpose:	scans expression, returning either its first character,
        !          3565:  *		or the next \sequence if that first char is \,
        !          3566:  *		and a pointer to the first expression char past that.
        !          3567:  * --------------------------------------------------------------------------
        !          3568:  * Arguments:	expression (I)	char * to first char of null-terminated
        !          3569:  *				string containing valid LaTeX expression
        !          3570:  *				to be scanned
        !          3571:  *		chartoken (O)	char * to null-terminated string returning
        !          3572:  *				either the first (non-whitespace) character
        !          3573:  *				of expression if that char isn't \, or else
        !          3574:  *				the \ and everything following it up to
        !          3575:  *				the next non-alphabetic character (but at
        !          3576:  *				least one char following the \ even if
        !          3577:  *				it's non-alpha)
        !          3578:  * --------------------------------------------------------------------------
        !          3579:  * Returns:	( char * )	ptr to the first char of expression
        !          3580:  *				past returned chartoken,
        !          3581:  *				or NULL for any parsing error.
        !          3582:  * --------------------------------------------------------------------------
        !          3583:  * Notes:     o	Does *not* skip leading whitespace, but simply
        !          3584:  *		returns any whitespace character as the next character.
        !          3585:  * ======================================================================= */
        !          3586: /* --- entry point --- */
        !          3587: char	*texchar ( char *expression, char *chartoken )
        !          3588: {
        !          3589: /* -------------------------------------------------------------------------
        !          3590: Allocations and Declarations
        !          3591: -------------------------------------------------------------------------- */
        !          3592: int	esclen = 0,				/*length of escape sequence*/
        !          3593: 	maxesclen = 128;			/* max len of esc sequence */
        !          3594: char	*ptoken = chartoken;			/* ptr into chartoken */
        !          3595: int	iprefix = 0;				/* prefix index */
        !          3596: static	char *prefixes[] =			/*e.g., \big followed by ( */
        !          3597: 	{ /* "\\left", "\\right", */
        !          3598: 	  "\\big",  "\\Big",  "\\bigg",  "\\Bigg",
        !          3599: 	  "\\bigl", "\\Bigl", "\\biggl", "\\Biggl",
        !          3600: 	  "\\bigr", "\\Bigr", "\\biggr", "\\Biggr", NULL };
        !          3601: /* -------------------------------------------------------------------------
        !          3602: just return the next char if it's not \
        !          3603: -------------------------------------------------------------------------- */
        !          3604: /* --- error check for end-of-string --- */
        !          3605: *ptoken = '\000';				/* init in case of error */
        !          3606: if ( expression == NULL ) return(NULL);		/* nothing to scan */
        !          3607: if ( *expression == '\000' ) return(NULL);	/* nothing to scan */
        !          3608: /* --- always returning first character (either \ or some other char) --- */
        !          3609: *ptoken++ = *expression++;			/* here's first character */
        !          3610: /* --- if first char isn't \, then just return it to caller --- */
        !          3611: if ( !isthischar(*(expression-1),ESCAPE) )	/* not a \, so return char */
        !          3612:   { *ptoken = '\000';				/* add a null terminator */
        !          3613:     goto end_of_job; }				/* ptr past returned char */
        !          3614: if ( *expression == '\000' )			/* \ is very last char */
        !          3615:   { *chartoken = '\000';			/* flush bad trailing \ */
        !          3616:     return(NULL); }				/* and signal end-of-job */
        !          3617: /* -------------------------------------------------------------------------
        !          3618: we have an escape sequence, so return all alpha chars following \
        !          3619: -------------------------------------------------------------------------- */
        !          3620: /* --- accumulate chars until first non-alpha char found --- */
        !          3621: for ( ; isalpha(*expression); esclen++ )	/* till first non-alpha... */
        !          3622:   { if ( esclen < maxesclen-3 )			/* more room in chartoken */
        !          3623:       *ptoken++ = *expression;			/*copy alpha char, bump ptr*/
        !          3624:     expression++; }				/* bump expression ptr */
        !          3625: /* --- if we have a prefix, append next texchar, e.g., \big( --- */
        !          3626: *ptoken = '\000';				/* set null for compare */
        !          3627: for ( iprefix=0; prefixes[iprefix] != NULL; iprefix++ ) /* run thru list */
        !          3628:  if ( strcmp(chartoken,prefixes[iprefix]) == 0 ) /* have an exact match */
        !          3629:   { char nextchar[256];  int nextlen=0;		/* texchar after prefix */
        !          3630:     skipwhite(expression);			/* skip space after prefix*/
        !          3631:     expression = texchar(expression,nextchar);	/* get nextchar */
        !          3632:     if ( (nextlen = strlen(nextchar)) > 0 )	/* #chars in nextchar */
        !          3633:       { strcpy(ptoken,nextchar);		/* append nextchar */
        !          3634:         ptoken += strlen(nextchar);		/* point to null terminator*/
        !          3635:         esclen += strlen(nextchar); }		/* and bump escape length */
        !          3636:     break; }					/* stop checking prefixes */
        !          3637: /* --- every \ must be followed by at least one char, e.g., \[ --- */
        !          3638: if ( esclen < 1 )				/* \ followed by non-alpha */
        !          3639:   *ptoken++ = *expression++;			/*copy non-alpha, bump ptrs*/
        !          3640: else {						/* normal alpha \sequence */
        !          3641:   /* --- respect spaces in text mode, except first space after \escape --- */
        !          3642:   if ( istext > 0 )				/* in \rm or \it text mode */
        !          3643:    if ( istext != 3 )				/* but not \mathbb */
        !          3644:     if ( isthischar(*expression,WHITEDELIM) )	/* delim follows \sequence */
        !          3645:      expression++; }				/* so flush delim */
        !          3646: *ptoken = '\000';				/* null-terminate token */
        !          3647: /* --- back to caller --- */
        !          3648: end_of_job:
        !          3649:   if ( msgfp!=NULL && msglevel>=999 )
        !          3650:     { fprintf(msgfp,"texchar> returning token = \"%s\"\n",chartoken);
        !          3651:       fflush(msgfp); }
        !          3652:   return ( expression );			/*ptr to 1st non-alpha char*/
        !          3653: } /* --- end-of-function texchar() --- */
        !          3654: 
        !          3655: 
        !          3656: /* ==========================================================================
        !          3657:  * Function:	texsubexpr (expression,subexpr,maxsubsz,
        !          3658:  *		left,right,isescape,isdelim)
        !          3659:  * Purpose:	scans expression, returning everything between a balanced
        !          3660:  *		left{...right} subexpression if the first non-whitespace
        !          3661:  *		char of expression is an (escaped or unescaped) left{,
        !          3662:  *		or just the next texchar() otherwise,
        !          3663:  *		and a pointer to the first expression char past that.
        !          3664:  * --------------------------------------------------------------------------
        !          3665:  * Arguments:	expression (I)	char * to first char of null-terminated
        !          3666:  *				string containing valid LaTeX expression
        !          3667:  *				to be scanned
        !          3668:  *		subexpr (O)	char * to null-terminated string returning
        !          3669:  *				either everything between a balanced {...}
        !          3670:  *				subexpression if the first char is {,
        !          3671:  *				or the next texchar() otherwise.
        !          3672:  *		maxsubsz (I)	int containing max #bytes returned
        !          3673:  *				in subexpr buffer (0 means unlimited)
        !          3674:  *		left (I)	char * specifying allowable left delimiters
        !          3675:  *				that begin subexpression, e.g., "{[(<"
        !          3676:  *		right (I)	char * specifying matching right delimiters
        !          3677:  *				in the same order as left, e.g., "}])>"
        !          3678:  *		isescape (I)	int controlling whether escaped and/or
        !          3679:  *				unescaped left,right are matched;
        !          3680:  *				see isbrace() comments below for details.
        !          3681:  *		isdelim (I)	int containing true (non-zero) to return
        !          3682:  *				the leading left and trailing right delims
        !          3683:  *				(if any were found) along with subexpr,
        !          3684:  *				or containing false=0 to return subexpr
        !          3685:  *				without its delimiters
        !          3686:  * --------------------------------------------------------------------------
        !          3687:  * Returns:	( char * )	ptr to the first char of expression
        !          3688:  *				past returned subexpr (see Notes),
        !          3689:  *				or NULL for any parsing error.
        !          3690:  * --------------------------------------------------------------------------
        !          3691:  * Notes:     o	If subexpr is of the form left{...right},
        !          3692:  *		the outer {}'s are returned as part of subexpr
        !          3693:  *		if isdelim is true; if isdelim is false the {}'s aren't
        !          3694:  *		returned.  In either case the returned pointer is
        !          3695:  *		*always* bumped past the closing right}, even if
        !          3696:  *		that closing right} isn't returned in subexpr.
        !          3697:  *	      o	If subexpr is not of the form left{...right},
        !          3698:  *		the returned pointer is on the character immediately
        !          3699:  *		following the last character returned in subexpr
        !          3700:  *	      o	\. acts as LaTeX \right. and matches any \left(
        !          3701:  *		And it also acts as a LaTeX \left. and matches any \right)
        !          3702:  * ======================================================================= */
        !          3703: /* --- entry point --- */
        !          3704: char	*texsubexpr ( char *expression, char *subexpr, int maxsubsz,
        !          3705: 	char *left, char *right, int isescape, int isdelim )
        !          3706: {
        !          3707: /* -------------------------------------------------------------------------
        !          3708: Allocations and Declarations
        !          3709: -------------------------------------------------------------------------- */
        !          3710: char	*texchar();		/*next char (or \sequence) from expression*/
        !          3711: char	*leftptr, leftdelim[32] = "(\000", /* left( found in expression */
        !          3712: 	rightdelim[32] = ")\000"; /* and matching right) */
        !          3713: char	*origexpression=expression, *origsubexpr=subexpr; /*original inputs*/
        !          3714: int	gotescape = 0,		/* true if leading char of expression is \ */
        !          3715: 	prevescape = 0;		/* while parsing, true if preceding char \ */
        !          3716: int	isbrace();		/* check for left,right braces */
        !          3717: int	isanyright = 1;		/* true matches any right with left, (...] */
        !          3718: int	isleftdot = 0;		/* true if left brace is a \. */
        !          3719: int	nestlevel = 1;		/* current # of nested braces */
        !          3720: int	subsz=0 /*, maxsubsz=8192*/; /* #chars in returned subexpr[] buffer*/
        !          3721: /* -------------------------------------------------------------------------
        !          3722: skip leading whitespace and just return the next char if it's not {
        !          3723: -------------------------------------------------------------------------- */
        !          3724: /* --- skip leading whitespace and error check for end-of-string --- */
        !          3725: *subexpr = '\000';				/* init in case of error */
        !          3726: if ( expression == NULL ) return(NULL);		/*can't dereference null ptr*/
        !          3727: skipwhite(expression);				/* leading whitespace gone */
        !          3728: if ( *expression == '\000' ) return(NULL);	/* nothing left to scan */
        !          3729: /* --- set maxsubsz --- */
        !          3730: if ( maxsubsz < 1 ) maxsubsz = 8192;		/* input 0 means unlimited */
        !          3731: /* --- check for escape --- */
        !          3732: if ( isthischar(*expression,ESCAPE) )		/* expression is escaped */
        !          3733:   gotescape = 1;				/* so set flag accordingly */
        !          3734: /* --- if first char isn't left{ or script, just return it to caller --- */
        !          3735: if ( !isbrace(expression,left,isescape) )	/* not a left{ */
        !          3736:   if ( !isthischar(*expression,SCRIPTS) )	/* and not a script */
        !          3737:     return ( texchar(expression,subexpr) );	/* next char to caller */
        !          3738:   else /* --- kludge for super/subscripts to accommodate texscripts() --- */
        !          3739:     { *subexpr++ = *expression;			/* signal script */
        !          3740:       *subexpr = '\000';			/* null-terminate subexpr */
        !          3741:       return ( expression ); }			/* leave script in stream */
        !          3742: /* --- extract left and find matching right delimiter --- */
        !          3743: *leftdelim  = *(expression+gotescape);		/* the left( in expression */
        !          3744: if ( (gotescape && *leftdelim == '.')		/* we have a left \. */
        !          3745: ||   (gotescape && isanyright) )		/*or are matching any right*/
        !          3746:   { isleftdot = 1;				/* so just set flag */
        !          3747:     *leftdelim = '\000'; }			/* and reset leftdelim */
        !          3748: else						/* find matching \right */
        !          3749:   if ( (leftptr=strchr(left,*leftdelim)) != NULL ) /* ptr to that left( */
        !          3750:     *rightdelim = right[(int)(leftptr-left)];	/* get the matching right) */
        !          3751:   else						/* can't happen -- pgm bug */
        !          3752:     return ( NULL );				/*just signal eoj to caller*/
        !          3753: /* -------------------------------------------------------------------------
        !          3754: accumulate chars between balanced {}'s, i.e., till nestlevel returns to 0
        !          3755: -------------------------------------------------------------------------- */
        !          3756: /* --- first initialize by bumping past left{ or \{ --- */
        !          3757: if ( isdelim )   *subexpr++ = *expression++;	/*caller wants { in subexpr*/
        !          3758:   else expression++;				/* always bump past left{ */
        !          3759: if ( gotescape )				/*need to bump another char*/
        !          3760:   if ( isdelim ) *subexpr++ = *expression++;	/* caller wants char, too */
        !          3761:   else expression++;				/* else just bump past it */
        !          3762: /* --- set maximum size for numerical arguments --- */
        !          3763: if ( 0 )					/* check turned on or off? */
        !          3764:  if ( !isescape && !isdelim )			/*looking for numerical arg*/
        !          3765:   maxsubsz = 96;				/* set max arg size */
        !          3766: /* --- search for matching right} --- */
        !          3767: while ( 1 )					/*until balanced right} */
        !          3768:   {
        !          3769:   /* --- error check for end-of-string --- */
        !          3770:   if ( *expression == '\000' )			/* premature end-of-string */
        !          3771:     { if ( 0 && (!isescape && !isdelim) )	/*looking for numerical arg,*/
        !          3772: 	{ expression = origexpression;		/* so end-of-string is error*/
        !          3773: 	  subexpr = origsubexpr; }		/* so reset all ptrs */
        !          3774:       if ( isdelim )				/* generate fake right */
        !          3775: 	if ( gotescape )			/* need escaped right */
        !          3776: 	  { *subexpr++ = '\\';			/* set escape char */
        !          3777: 	    *subexpr++ = '.'; }			/* and fake \right. */
        !          3778: 	else					/* escape not wanted */
        !          3779: 	    *subexpr++ = *rightdelim;		/* so fake actual right */
        !          3780:       *subexpr = '\000';			/* null-terminate subexpr */
        !          3781:       return ( expression ); }			/* back with final token */
        !          3782:   /* --- check preceding char for escape --- */
        !          3783:   if ( isthischar(*(expression-1),ESCAPE) )	/* previous char was \ */
        !          3784: 	prevescape = 1-prevescape;		/* so flip escape flag */
        !          3785:   else	prevescape = 0;				/* or turn flag off */
        !          3786:   /* --- check for { and } (un/escaped as per leading left) --- */
        !          3787:   if ( gotescape == prevescape )		/* escaped iff leading is */
        !          3788:     { /* --- check for (closing) right delim and see if we're done --- */
        !          3789:       if ( isthischar(*expression,rightdelim)	/* found a right} */
        !          3790:       ||   (isleftdot && isthischar(*expression,right)) /*\left. matches all*/
        !          3791:       ||   (prevescape && isthischar(*expression,".")) ) /*or found \right. */
        !          3792:         if ( --nestlevel < 1 )			/*\right balances 1st \left*/
        !          3793: 	  { if ( isdelim ) 			/*caller wants } in subexpr*/
        !          3794: 	      *subexpr++ = *expression;		/* so end subexpr with } */
        !          3795: 	    else				/*check for \ before right}*/
        !          3796: 	      if ( prevescape )			/* have unwanted \ */
        !          3797: 		*(subexpr-1) = '\000';		/* so replace it with null */
        !          3798: 	    *subexpr = '\000';			/* null-terminate subexpr */
        !          3799: 	    return ( expression+1 ); }		/* back with char after } */
        !          3800:       /* --- check for (another) left{ --- */
        !          3801:       if ( isthischar(*expression,leftdelim)	/* found another left{ */
        !          3802:       ||   (isleftdot && isthischar(*expression,left)) ) /* any left{ */
        !          3803: 	nestlevel++;
        !          3804:     } /* --- end-of-if(gotescape==prevescape) --- */
        !          3805:   /* --- not done, so copy char to subexpr and continue with next char --- */
        !          3806:   if ( ++subsz < maxsubsz-5 )			/* more room in subexpr */
        !          3807:     *subexpr++ = *expression;			/* so copy char and bump ptr*/
        !          3808:   expression++;					/* bump expression ptr */
        !          3809:   } /* --- end-of-while(1) --- */
        !          3810: } /* --- end-of-function texsubexpr() --- */
        !          3811: 
        !          3812: 
        !          3813: /* ==========================================================================
        !          3814:  * Function:	texscripts ( expression, subscript, superscript, which )
        !          3815:  * Purpose:	scans expression, returning subscript and/or superscript
        !          3816:  *		if expression is of the form _x^y or ^{x}_{y},
        !          3817:  *		or any (valid LaTeX) permutation of the above,
        !          3818:  *		and a pointer to the first expression char past "scripts"
        !          3819:  * --------------------------------------------------------------------------
        !          3820:  * Arguments:	expression (I)	char * to first char of null-terminated
        !          3821:  *				string containing valid LaTeX expression
        !          3822:  *				to be scanned
        !          3823:  *		subscript (O)	char * to null-terminated string returning
        !          3824:  *				subscript (without _), if found, or "\000"
        !          3825:  *		superscript (O)	char * to null-terminated string returning
        !          3826:  *				superscript (without ^), if found, or "\000"
        !          3827:  *		which (I)	int containing 1 for subscript only,
        !          3828:  *				2 for superscript only, >=3 for either/both
        !          3829:  * --------------------------------------------------------------------------
        !          3830:  * Returns:	( char * )	ptr to the first char of expression
        !          3831:  *				past returned "scripts" (unchanged
        !          3832:  *				except for skipped whitespace if
        !          3833:  *				neither subscript nor superscript found),
        !          3834:  *				or NULL for any parsing error.
        !          3835:  * --------------------------------------------------------------------------
        !          3836:  * Notes:     o	an input expression like ^a^b_c will return superscript="b",
        !          3837:  *		i.e., totally ignoring all but the last "script" encountered
        !          3838:  * ======================================================================= */
        !          3839: /* --- entry point --- */
        !          3840: char	*texscripts ( char *expression, char *subscript,
        !          3841: 			char *superscript, int which )
        !          3842: {
        !          3843: /* -------------------------------------------------------------------------
        !          3844: Allocations and Declarations
        !          3845: -------------------------------------------------------------------------- */
        !          3846: char	*texsubexpr();		/* next subexpression from expression */
        !          3847: int	gotsub=0, gotsup=0;	/* check that we don't eat, e.g., x_1_2 */
        !          3848: /* -------------------------------------------------------------------------
        !          3849: init "scripts" and skip leading whitespace
        !          3850: -------------------------------------------------------------------------- */
        !          3851: /* --- skip leading whitespace and check for end-of-string --- */
        !          3852: *subscript = *superscript = '\000';		/* init in case no scripts */
        !          3853: skipwhite(expression);				/* leading whitespace gone */
        !          3854: if ( *expression == '\000' ) return(expression); /* nothing left to scan */
        !          3855: /* -------------------------------------------------------------------------
        !          3856: get subscript and/or superscript from expression
        !          3857: -------------------------------------------------------------------------- */
        !          3858: while ( expression != NULL )
        !          3859:   if ( isthischar(*expression,SUBSCRIPT)	/* found _ */
        !          3860:   &&   (which==1 || which>2 ) )			/* and caller wants it */
        !          3861:     { if ( gotsub				/* found 2nd subscript */
        !          3862:       ||   subscript == NULL ) break;		/* or no subscript buffer */
        !          3863:       gotsub = 1;				/* set subscript flag */
        !          3864:       expression = texsubexpr(expression+1,subscript,0,"{","}",0,0); }
        !          3865:   else						/* no _, check for ^ */
        !          3866:     if ( isthischar(*expression,SUPERSCRIPT)	/* found ^ */
        !          3867:     &&   which>=2  )				/* and caller wants it */
        !          3868:       {	if ( gotsup				/* found 2nd superscript */
        !          3869: 	||   superscript == NULL ) break;	/* or no superscript buffer*/
        !          3870: 	gotsup = 1;				/* set superscript flag */
        !          3871: 	expression = texsubexpr(expression+1,superscript,0,"{","}",0,0); }
        !          3872:     else					/* neither _ nor ^ */
        !          3873:       return ( expression );			/*return ptr past "scripts"*/
        !          3874: return ( expression );
        !          3875: } /* --- end-of-function texscripts() --- */
        !          3876: 
        !          3877: 
        !          3878: /* ==========================================================================
        !          3879:  * Function:	isbrace ( expression, braces, isescape )
        !          3880:  * Purpose:	checks leading char(s) of expression for a brace,
        !          3881:  *		either escaped or unescaped depending on isescape,
        !          3882:  *		except that { and } are always matched, if they're
        !          3883:  *		in braces, regardless of isescape.
        !          3884:  * --------------------------------------------------------------------------
        !          3885:  * Arguments:	expression (I)	char * to first char of null-terminated
        !          3886:  *				string containing a valid LaTeX expression
        !          3887:  *				whose leading char(s) are checked for braces
        !          3888:  *				that begin subexpression, e.g., "{[(<"
        !          3889:  *		braces (I)	char * specifying matching brace delimiters
        !          3890:  *				to be checked for, e.g., "{[(<" or "}])>"
        !          3891:  *		isescape (I)	int containing 0 to match only unescaped
        !          3892:  *				braces, e.g., (...) or {...}, etc,
        !          3893:  *				or containing 1 to match only escaped
        !          3894:  *				braces, e.g., \(...\) or \[...\], etc,
        !          3895:  *				or containing 2 to match either.
        !          3896:  *				But note: if {,} are in braces
        !          3897:  *				then they're *always* matched whether
        !          3898:  *				escaped or not, regardless of isescape.
        !          3899:  * --------------------------------------------------------------------------
        !          3900:  * Returns:	( int )		1 if the leading char(s) of expression
        !          3901:  *				is a brace, or 0 if not.
        !          3902:  * --------------------------------------------------------------------------
        !          3903:  * Notes:     o
        !          3904:  * ======================================================================= */
        !          3905: /* --- entry point --- */
        !          3906: int	isbrace ( char *expression, char *braces, int isescape )
        !          3907: {
        !          3908: /* -------------------------------------------------------------------------
        !          3909: Allocations and Declarations
        !          3910: -------------------------------------------------------------------------- */
        !          3911: int	gotescape = 0,		/* true if leading char is an escape */
        !          3912: 	gotbrace = 0;		/*true if first non-escape char is a brace*/
        !          3913: /* -------------------------------------------------------------------------
        !          3914: check for brace
        !          3915: -------------------------------------------------------------------------- */
        !          3916: /* --- first check for end-of-string --- */
        !          3917: if ( *expression == '\000' ) return(0);		/* nothing to check */
        !          3918: /* --- check leading char for escape --- */
        !          3919: if ( isthischar(*expression,ESCAPE) )		/* expression is escaped */
        !          3920:   { gotescape = 1;				/* so set flag accordingly */
        !          3921:     expression++; }				/* and bump past escape */
        !          3922: /* --- check (maybe next char) for brace --- */
        !          3923: if ( isthischar(*expression,braces) )		/* expression is braced */
        !          3924:   gotbrace = 1;					/* so set flag accordingly */
        !          3925: if ( gotescape && *expression == '.' )		/* \. matches any brace */
        !          3926:   gotbrace = 1;					/* set flag */
        !          3927: /* --- check for TeX brace { or } --- */
        !          3928: if ( gotbrace && isthischar(*expression,"{}") )	/*expression has TeX brace*/
        !          3929:   if ( isescape ) isescape = 2;			/* reset escape flag */
        !          3930: /* -------------------------------------------------------------------------
        !          3931: back to caller
        !          3932: -------------------------------------------------------------------------- */
        !          3933: if ( gotbrace &&				/* found a brace */
        !          3934:      ( isescape==2 ||				/* escape irrelevant */
        !          3935:        gotescape==isescape )			/* un/escaped as requested */
        !          3936:    ) return ( 1 );  return ( 0 );		/* return 1,0 accordingly */
        !          3937: } /* --- end-of-function isbrace() --- */
        !          3938: 
        !          3939: 
        !          3940: /* ==========================================================================
        !          3941:  * Function:	preamble ( expression, size, subexpr )
        !          3942:  * Purpose:	parses $-terminated preamble, if present, at beginning
        !          3943:  *		of expression, re-setting size if necessary, and
        !          3944:  *		returning any other parameters besides size in subexpr.
        !          3945:  * --------------------------------------------------------------------------
        !          3946:  * Arguments:	expression (I)	char * to first char of null-terminated
        !          3947:  *				string containing LaTeX expression possibly
        !          3948:  *				preceded by $-terminated preamble
        !          3949:  *		size (I/O)	int *  containing 0-4 default font size,
        !          3950:  *				and returning size modified by first
        !          3951:  *				preamble parameter (or unchanged)
        !          3952:  *		subexpr(O)	char *  returning any remaining preamble
        !          3953:  *				parameters past size
        !          3954:  * --------------------------------------------------------------------------
        !          3955:  * Returns:	( char * )	ptr to first char past preamble in expression
        !          3956:  *				or NULL for any parsing error.
        !          3957:  * --------------------------------------------------------------------------
        !          3958:  * Notes:     o	size can be any number >=0. If preceded by + or -, it's
        !          3959:  *		interpreted as an increment to input size; otherwise
        !          3960:  *		it's interpreted as the size.
        !          3961:  *	      o	if subexpr is passed as NULL ptr, then returned expression
        !          3962:  *		ptr will have "flushed" and preamble parameters after size
        !          3963:  * ======================================================================= */
        !          3964: /* --- entry point --- */
        !          3965: char	*preamble ( char *expression, int *size, char *subexpr )
        !          3966: {
        !          3967: /* -------------------------------------------------------------------------
        !          3968: Allocations and Declarations
        !          3969: -------------------------------------------------------------------------- */
        !          3970: char	pretext[512], *prep=expression,	/*pream from expression, ptr into it*/
        !          3971: 	*dollar, *comma;		/* preamble delimiters */
        !          3972: int	prelen = 0,			/* preamble length */
        !          3973: 	sizevalue = 0,			/* value of size parameter */
        !          3974: 	isfontsize = 0,			/*true if leading fontsize present*/
        !          3975: 	isdelta = 0;			/*true to increment passed size arg*/
        !          3976: /* -------------------------------------------------------------------------
        !          3977: initialization
        !          3978: -------------------------------------------------------------------------- */
        !          3979: if ( subexpr != NULL )			/* caller passed us an address */
        !          3980:   *subexpr = '\000';			/* so init assuming no preamble */
        !          3981: if ( expression == NULL ) goto end_of_job; /* no input */
        !          3982: if ( *expression == '\000' ) goto end_of_job; /* input is an empty string */
        !          3983: /* -------------------------------------------------------------------------
        !          3984: process preamble if present
        !          3985: -------------------------------------------------------------------------- */
        !          3986: /*process_preamble:*/
        !          3987: if ( (dollar=strchr(expression,'$'))	/* $ signals preceding preamble */
        !          3988: !=   NULL )				/* found embedded $ */
        !          3989:  if ( (prelen = (int)(dollar-expression)) /*#chars in expression preceding $*/
        !          3990:  > 0 ) {				/* must have preamble preceding $ */
        !          3991:   if ( prelen < 65 ) {			/* too long for a prefix */
        !          3992:    memcpy(pretext,expression,prelen);	/* local copy of preamble */
        !          3993:    pretext[prelen] = '\000';		/* null-terminated */
        !          3994:    if ( strchr(pretext,*(ESCAPE))==NULL	/*shouldn't be an escape in preamble*/
        !          3995:    &&   strchr(pretext,'{') == NULL ) {	/*shouldn't be a left{ in preamble*/
        !          3996:     /* --- skip any leading whitespace  --- */
        !          3997:     prep = pretext;			/* start at beginning of preamble */
        !          3998:     skipwhite(prep);			/* skip any leading white space */
        !          3999:     /* --- check for embedded , or leading +/- (either signalling size) --- */
        !          4000:     if ( isthischar(*prep,"+-") )	/* have leading + or - */
        !          4001:      isdelta = 1;			/* so use size value as increment */
        !          4002:     comma = strchr(pretext,',');	/* , signals leading size param */
        !          4003:     /* --- process leading size parameter if present --- */
        !          4004:     if ( comma != NULL			/* size param explicitly signalled */
        !          4005:     ||   isdelta || isdigit(*prep) ) {	/* or inferred implicitly */
        !          4006:       /* --- parse size parameter and reset size accordingly --- */
        !          4007:       if( comma != NULL ) *comma = '\000';/*, becomes null, terminating size*/
        !          4008:       sizevalue = atoi(prep);		/* convert size string to integer */
        !          4009:       if ( size != NULL )		/* caller passed address for size */
        !          4010: 	*size = (isdelta? *size+sizevalue : sizevalue); /* so reset size */
        !          4011:       /* --- finally, set flag and shift size parameter out of preamble --- */
        !          4012:       isfontsize = 1;			/*set flag showing font size present*/
        !          4013:       if ( comma != NULL ) strcpy(pretext,comma+1);/*leading size param gone*/
        !          4014:      } /* --- end-of-if(comma!=NULL||etc) --- */
        !          4015:     /* --- copy any preamble params following size to caller's subexpr --- */
        !          4016:     if ( comma != NULL || !isfontsize )	/*preamb contains params past size*/
        !          4017:      if ( subexpr != NULL )		/* caller passed us an address */
        !          4018:       strcpy(subexpr,pretext);		/*so return extra params to caller*/
        !          4019:     /* --- finally, set prep to shift preamble out of expression --- */
        !          4020:     prep = expression + prelen+1;	/* set prep past $ in expression */
        !          4021:     } /* --- end-of-if(strchr(pretext,*ESCAPE)==NULL) --- */
        !          4022:    } /* --- end-of-if(prelen<65) --- */
        !          4023:   } /* --- end-of-if(prelen>0) --- */
        !          4024:  else {					/* $ is first char of expression */
        !          4025:   int ndollars = 0;			/* number of $...$ pairs removed */
        !          4026:   prep = expression;			/* start at beginning of expression*/
        !          4027:   while ( *prep == '$' ) {		/* remove all matching $...$'s */
        !          4028:    int	explen = strlen(prep)-1;	/* index of last char in expression*/
        !          4029:    if ( explen < 2 ) break;		/* no $...$'s left to remove */
        !          4030:    if ( prep[explen] != '$' ) break;	/* unmatched $ */
        !          4031:    prep[explen] = '\000';		/* remove trailing $ */
        !          4032:    prep++;				/* and remove matching leading $ */
        !          4033:    ndollars++;				/* count another pair removed */
        !          4034:    } /* --- end-of-while(*prep=='$') --- */
        !          4035:   ispreambledollars = ndollars;		/* set flag to fix \displaystyle */
        !          4036:   if ( ndollars == 1 )			/* user submitted $...$ expression */
        !          4037:     isdisplaystyle = 0;			/* so set \textstyle */
        !          4038:   if ( ndollars > 1 )			/* user submitted $$...$$ */
        !          4039:     isdisplaystyle = 2;			/* so set \displaystyle */
        !          4040:   /*goto process_preamble;*/		/*check for preamble after leading $*/
        !          4041:   } /* --- end-of-if/else(prelen>0) --- */
        !          4042: /* -------------------------------------------------------------------------
        !          4043: back to caller
        !          4044: -------------------------------------------------------------------------- */
        !          4045: end_of_job:
        !          4046:   return ( prep );			/*expression, or ptr past preamble*/
        !          4047: } /* --- end-of-function preamble() --- */
        !          4048: 
        !          4049: 
        !          4050: /* ==========================================================================
        !          4051:  * Function:	mimeprep ( expression )
        !          4052:  * Purpose:	preprocessor for mimeTeX input, e.g.,
        !          4053:  *		(a) removes comments,
        !          4054:  *		(b) converts \left( to \( and \right) to \),
        !          4055:  *		(c) xlates &html; special chars to equivalent latex
        !          4056:  *		Should only be called once (after unescape_url())
        !          4057:  * --------------------------------------------------------------------------
        !          4058:  * Arguments:	expression (I/O) char * to first char of null-terminated
        !          4059:  *				string containing mimeTeX/LaTeX expression,
        !          4060:  *				and returning preprocessed string
        !          4061:  * --------------------------------------------------------------------------
        !          4062:  * Returns:	( char * )	ptr to input expression,
        !          4063:  *				or NULL for any parsing error.
        !          4064:  * --------------------------------------------------------------------------
        !          4065:  * Notes:     o
        !          4066:  * ======================================================================= */
        !          4067: /* --- entry point --- */
        !          4068: char	*mimeprep ( char *expression )
        !          4069: {
        !          4070: /* -------------------------------------------------------------------------
        !          4071: Allocations and Declarations
        !          4072: -------------------------------------------------------------------------- */
        !          4073: char	*expptr=expression,		/* ptr within expression */
        !          4074: 	*tokptr=NULL,			/*ptr to token found in expression*/
        !          4075: 	*texsubexpr(), argval[8192];	/*parse for macro args after token*/
        !          4076: char	*strchange();			/* change leading chars of string */
        !          4077: char	*findbraces();			/*find left { and right } for \atop*/
        !          4078: int	idelim=0,			/* left- or right-index */
        !          4079: 	isymbol=0;			/*symbols[],rightcomment[],etc index*/
        !          4080: int	xlateleft = 0;			/* true to xlate \left and \right */
        !          4081: /* ---
        !          4082:  * comments
        !          4083:  * -------- */
        !          4084: char	*leftptr=NULL;			/* find leftcomment in expression */
        !          4085: static	char *leftcomment = "%%",	/* open comment */
        !          4086: 	*rightcomment[] = {"\n", "%%", NULL}; /* close comments */
        !          4087: /* ---
        !          4088:  * special long (more than 1-char) \left and \right delimiters
        !          4089:  * ----------------------------------------------------------- */
        !          4090: static	char *leftfrom[] =		/* xlate any \left suffix... */
        !          4091:    { "\\|",				/* \left\| */
        !          4092:      "\\{",				/* \left\{ */
        !          4093:      "\\langle",			/* \left\langle */
        !          4094:      NULL } ; /* --- end-of-leftfrom[] --- */
        !          4095: static	char *leftto[] =		/* ...to this instead */
        !          4096:    { "=",				/* = */
        !          4097:      "{",				/* { */
        !          4098:      "<",				/* < */
        !          4099:      NULL } ; /* --- end-of-leftto[] --- */
        !          4100: static	char *rightfrom[] =		/* xlate any \right suffix... */
        !          4101:    { "\\|",				/* \right\| */
        !          4102:      "\\}",				/* \right\} */
        !          4103:      "\\rangle",			/* \right\rangle */
        !          4104:      NULL } ; /* --- end-of-rightfrom[] --- */
        !          4105: static	char *rightto[] =		/* ...to this instead */
        !          4106:    { "=",				/* = */
        !          4107:      "}",				/* } */
        !          4108:      ">",				/* > */
        !          4109:      NULL } ; /* --- end-of-rightto[] --- */
        !          4110: /* ---
        !          4111:  * { \atop }-like commands
        !          4112:  * ----------------------- */
        !          4113: char	*atopsym=NULL;			/* atopcommands[isymbol] */
        !          4114: static	char *atopcommands[] =		/* list of {a+b\command c+d}'s */
        !          4115:    { "\\over",				/* plain tex for \frac */
        !          4116:      "\\choose",			/* binomial coefficient */
        !          4117:    #ifndef NOATOP			/*noatop preserves old mimeTeX rule*/
        !          4118:      "\\atop",
        !          4119:    #endif
        !          4120:      NULL } ; /* --- end-of-atopcommands[] --- */
        !          4121: static	char *atopdelims[] =		/* delims for atopcommands[] */
        !          4122:    { NULL, NULL,			/* \\over has no delims */
        !          4123:      "\\left(", "\\right)",		/* \\choose has ( ) delims*/
        !          4124:    #ifndef NOATOP			/*noatop preserves old mimeTeX rule*/
        !          4125:      NULL, NULL,			/* \\atop has no delims */
        !          4126:    #endif
        !          4127:      NULL, NULL } ; /* --- end-of-atopdelims[] --- */
        !          4128: /* ---
        !          4129:  * html special/escape chars converted to latex equivalents
        !          4130:  * -------------------------------------------------------- */
        !          4131: char	*htmlsym=NULL;			/* symbols[isymbol].html */
        !          4132: static	struct { char *html; char *args; char *latex; } symbols[] =
        !          4133:  { /* ---------------------------------------
        !          4134:      user-supplied newcommands
        !          4135:    --------------------------------------- */
        !          4136:  #ifdef NEWCOMMANDS			/* -DNEWCOMMANDS=\"filename.h\" */
        !          4137:    #include NEWCOMMANDS
        !          4138:  #endif
        !          4139:    /* ------------------------------------------
        !          4140:    LaTeX Macro  #args,default   template...
        !          4141:    ------------------------------------------ */
        !          4142:    { "\\lvec",	"2n",	"{#2_1,\\cdots,#2_{#1}}" },
        !          4143:    { "\\overset", NULL,	"\\stackrel" },		/* just an alias */
        !          4144:    { "\\underset", "2",	"\\relstack{#2}{#1}" },	/* reverse args */
        !          4145:    /* ---------------------------------------
        !          4146:      html    termchar   LaTeX equivalent...
        !          4147:    --------------------------------------- */
        !          4148:    { "&quot",	";",	"\"" },		/* &quot; is first, &#034; */
        !          4149:    { "&amp",	";",	"&" },
        !          4150:    { "&lt",	";",	"<" },
        !          4151:    { "&gt",	";",	">" },
        !          4152:    { "&nbsp",	";",	"~" },
        !          4153:    { "&iexcl",	";",	"{\\raisebox{-2}{\\rotatebox{180}{!}}}" },
        !          4154:    { "&brvbar",	";",	"|" },
        !          4155:    { "&plusmn",	";",	"\\pm" },
        !          4156:    { "&sup2",	";",	"{{}^2}" },
        !          4157:    { "&sup3",	";",	"{{}^3}" },
        !          4158:    { "&micro",	";",	"\\mu" },
        !          4159:    { "&sup1",	";",	"{{}^1}" },
        !          4160:    { "&frac14",	";",	"{\\frac14}" },
        !          4161:    { "&frac12",	";",	"{\\frac12}" },
        !          4162:    { "&frac34",	";",	"{\\frac34}" },
        !          4163:    { "&iquest",	";",	"{\\raisebox{-2}{\\rotatebox{180}{?}}}" },
        !          4164:    { "&Acirc",	";",	"{\\rm~\\hat~A}" },
        !          4165:    { "&Atilde",	";",	"{\\rm~\\tilde~A}" },
        !          4166:    { "&Auml",	";",	"{\\rm~\\ddot~A}" },
        !          4167:    { "&Aring",	";",	"{\\rm~A\\limits^{-1$o}}" },
        !          4168:    { "&atilde",	";",	"{\\rm~\\tilde~a}" },
        !          4169:    { "&yuml",	";",	"{\\rm~\\ddot~y}" },  /* &yuml; is last, &#255; */
        !          4170:    /* ---------------------------------------
        !          4171:      LaTeX   termchar   mimeTeX equivalent...
        !          4172:    --------------------------------------- */
        !          4173:    { "\\AA",	NULL,	"{\\rm~A\\limits^{-1$o}}" },
        !          4174:    { "\\aa",	NULL,	"{\\rm~a\\limits^{-1$o}}" },
        !          4175:    { "\\bmod",	NULL,	"{\\hspace2{\\rm~mod}\\hspace2}" },
        !          4176:    { "\\vdots",	NULL,	"{\\raisebox3{\\rotatebox{90}{\\ldots}}}" },
        !          4177:    { "\\cdots",	NULL,	"{\\raisebox3{\\ldots}}" },
        !          4178:    { "\\ldots",	NULL,	"{\\fs4.\\hspace1.\\hspace1.}" },
        !          4179:    { "\\ddots",	NULL,	"{\\fs4\\raisebox8.\\hspace1\\raisebox4.\\hspace1.}"},
        !          4180:    { "\\notin",	NULL,	"{\\not\\in}" },
        !          4181:    { "\\neq",	NULL,	"{\\not=}" },
        !          4182:    { "\\hbar",	NULL,	"{\\compose~h{{\\fs{-1}-\\atop\\vspace3}}}" },
        !          4183:    { "\\angle",	NULL, "{\\compose{\\hspace{3}\\lt}{\\circle(10,15;-80,80)}}"},
        !          4184:    { "\\cr",	NULL,	"\\\\" },
        !          4185:    { "\\iint",	NULL,	"{\\int\\int}\\limits" },
        !          4186:    { "\\Bigiint", NULL,	"{\\Bigint\\Bigint}\\limits" },
        !          4187:    { "!`",	NULL,	"{\\raisebox{-2}{\\rotatebox{180}{!}}}" },
        !          4188:    { "?`",	NULL,	"{\\raisebox{-2}{\\rotatebox{180}{?}}}" },
        !          4189:    { "\\rightleftharpoons",NULL,"{\\rightharpoonup\\atop\\leftharpoondown}" },
        !          4190:    { "\\LaTeX",	NULL,	"{\\rm~L\\raisebox{3}{\\fs{-1}A}\\TeX}" },
        !          4191:    { "\\TeX",	NULL,	"{\\rm~T\\raisebox{-3}{E}X}" },
        !          4192:    { "\\cyan",	NULL,	"{\\reverse\\red\\reversebg}" },
        !          4193:    { "\\magenta",NULL,	"{\\reverse\\green\\reversebg}" },
        !          4194:    { "\\yellow",NULL,	"{\\reverse\\blue\\reversebg}" },
        !          4195:    { "\\hhline",NULL,	"\\Hline" },
        !          4196:    { "\\Hline", NULL,	"\\hline\\,\\\\\\hline" },
        !          4197:    /* ---------------------------------------------------------
        !          4198:      "Algebra Syntax"  termchar   mimeTeX/LaTeX equivalent...
        !          4199:    ------------------------------------------------------------ */
        !          4200:    { "sqrt",	"1",	"{\\sqrt{#1}}" },
        !          4201:    { "sin",	"1",	"{\\sin{#1}}" },
        !          4202:    { "cos",	"1",	"{\\cos{#1}}" },
        !          4203:    { "asin",	"1",	"{\\sin^{-1}{#1}}" },
        !          4204:    { "acos",	"1",	"{\\cos^{-1}{#1}}" },
        !          4205:    { "exp",	"1",	"{e^{#1}}" },
        !          4206:    { "det",	"1",	"{\\left|{#1}\\right|}" },
        !          4207:    /* ---------------------------------------
        !          4208:    LaTeX Constant    termchar   value...
        !          4209:    --------------------------------------- */
        !          4210:    { "\\thinspace",	NULL,	"2" },
        !          4211:    { "\\thinmathspace",	NULL,	"2" },
        !          4212:    { "\\textwidth",	NULL,	"400" },
        !          4213:    { NULL,	NULL,	NULL }
        !          4214:  } ; /* --- end-of-symbols[] --- */
        !          4215: /* -------------------------------------------------------------------------
        !          4216: first remove comments
        !          4217: -------------------------------------------------------------------------- */
        !          4218: expptr = expression;			/* start search at beginning */
        !          4219: while ( (leftptr=strstr(expptr,leftcomment)) != NULL ) /*found leftcomment*/
        !          4220:   {
        !          4221:   char	*rightsym=NULL;			/* rightcomment[isymbol] */
        !          4222:   expptr = leftptr+strlen(leftcomment);	/* start rightcomment search here */
        !          4223:   /* --- check for any closing rightcomment, in given precedent order --- */
        !          4224:   if ( *expptr != '\000' )		/*have chars after this leftcomment*/
        !          4225:    for(isymbol=0; (rightsym=rightcomment[isymbol]) != NULL; isymbol++)
        !          4226:     if ( (tokptr=strstr(expptr,rightsym)) != NULL ) /*found rightcomment*/
        !          4227:      { tokptr += strlen(rightsym);	/* first char after rightcomment */
        !          4228:        if ( *tokptr == '\000' )		/*nothing after this rightcomment*/
        !          4229: 	{ *leftptr = '\000';		/*so terminate expr at leftcomment*/
        !          4230: 	  break; }			/* and stop looking for comments */
        !          4231:        *leftptr = '~';			/* replace entire comment by ~ */
        !          4232:        strcpy(leftptr+1,tokptr);	/* and squeeze out comment */
        !          4233:        goto next_comment; }		/* stop looking for rightcomment */
        !          4234:   /* --- no rightcomment after opening leftcomment --- */
        !          4235:   *leftptr = '\000';			/* so terminate expression */
        !          4236:   /* --- resume search past squeezed-out comment --- */
        !          4237:   next_comment:
        !          4238:     if ( *leftptr == '\000' ) break;	/* reached end of expression */
        !          4239:     expptr = leftptr+1;			/*resume search after this comment*/
        !          4240:   } /* --- end-of-while(leftptr!=NULL) --- */
        !          4241: /* -------------------------------------------------------------------------
        !          4242: convert \left( to \(  and  \right) to \),  etc.
        !          4243: -------------------------------------------------------------------------- */
        !          4244: if ( xlateleft )			/* \left...\right xlation wanted */
        !          4245:  for ( idelim=0; idelim<2; idelim++ )	/* 0 for \left  and  1 for \right */
        !          4246:   {
        !          4247:   char	*lrstr  = (idelim==0?"\\left":"\\right"); /* \left on 1st pass */
        !          4248:   int	lrlen   = (idelim==0?5:6);	/* strlen() of \left or \right */
        !          4249:   char	*braces = (idelim==0?LEFTBRACES ".":RIGHTBRACES "."), /*([{<or)]}>*/
        !          4250: 	**lrfrom= (idelim==0?leftfrom:rightfrom), /* long braces like \| */
        !          4251: 	**lrto  = (idelim==0?leftto:rightto), /* xlated to 1-char like = */
        !          4252: 	*lrsym  = NULL;			/* lrfrom[isymbol] */
        !          4253:   expptr = expression;			/* start search at beginning */
        !          4254:   while ( (tokptr=strstr(expptr,lrstr)) != NULL ) /* found \left or \right */
        !          4255:     {
        !          4256:     if ( isthischar(*(tokptr+lrlen),braces) ) /* followed by a 1-char brace*/
        !          4257:       {	strcpy(tokptr+1,tokptr+lrlen);	/* so squeeze out "left" or "right"*/
        !          4258: 	expptr = tokptr+2; }		/* and resume search past brace */
        !          4259:     else				/* may be a "long" brace like \| */
        !          4260:       {
        !          4261:       expptr = tokptr+lrlen;		/*init to resume search past\left\rt*/
        !          4262:       for(isymbol=0; (lrsym=lrfrom[isymbol]) != NULL; isymbol++)
        !          4263: 	{ int symlen = strlen(lrsym);	/* #chars in delim, e.g., 2 for \| */
        !          4264: 	  if ( memcmp(tokptr+lrlen,lrsym,symlen) == 0 ) /* found long delim*/
        !          4265: 	    { strcpy(tokptr+1,tokptr+lrlen+symlen-1); /* squeeze out delim */
        !          4266: 	      *(tokptr+1) = *(lrto[isymbol]); /* last char now 1-char delim*/
        !          4267: 	      expptr = tokptr+2 - lrlen; /* resume search past 1-char delim*/
        !          4268: 	      break; }			/* no need to check more lrsym's */
        !          4269: 	} /* --- end-of-for(isymbol) --- */
        !          4270:       } /* --- end-of-if/else(isthischar()) --- */
        !          4271:     } /* --- end-of-while(tokptr!=NULL) --- */
        !          4272:   } /* --- end-of-for(idelim) --- */
        !          4273: /* -------------------------------------------------------------------------
        !          4274: run thru table, converting all occurrences of each macro to its expansion
        !          4275: -------------------------------------------------------------------------- */
        !          4276: for(isymbol=0; (htmlsym=symbols[isymbol].html) != NULL; isymbol++)
        !          4277:   {
        !          4278:   int	htmllen = strlen(htmlsym);	/* length of escape, _without_ ; */
        !          4279:   int	isalgebra = isalpha((int)(*htmlsym)); /* leading char alphabetic */
        !          4280:   char	*aleft="{([<|", *aright="})]>|"; /*left,right delims for alg syntax*/
        !          4281:   char	*args = symbols[isymbol].args,	/* number {}-args, optional []-arg */
        !          4282: 	*htmlterm = args,		/*if *args nonumeric, then html term*/
        !          4283: 	*latexsym = symbols[isymbol].latex; /*latex replacement for htmlsym*/
        !          4284:   char	abuff[8192];  int iarg,nargs=0;	/* macro expansion params */
        !          4285:   if ( args != NULL )			/*we have args (or htmlterm) param*/
        !          4286:    if ( *args != '\000' )		/* and it's not an empty string */
        !          4287:     if ( strchr("0123456789",*args) != NULL ) /* is 1st char #args=0-9 ? */
        !          4288:      { htmlterm = NULL;			/* if so, then we have no htmlterm */
        !          4289:        *abuff = *args;  abuff[1] = '\000'; /* #args char in ascii buffer */
        !          4290:        nargs = atoi(abuff); }		/* interpret #args to numeric */
        !          4291:   expptr = expression;			/* re-start search at beginning */
        !          4292:   while ( (tokptr=strstr(expptr,htmlsym)) != NULL ) /* found another sym */
        !          4293:     { char termchar = *(tokptr+htmllen); /* char terminating html sequence */
        !          4294:       int escapelen = htmllen;		/* total length of escape sequence */
        !          4295:       *abuff = '\000';			/* default to empty string */
        !          4296:       if ( latexsym != NULL )		/* table has .latex xlation */
        !          4297:        if ( *latexsym != '\000' )	/* and it's not an empty string */
        !          4298: 	strcpy(abuff,latexsym);		/* so get local copy */
        !          4299:       if ( htmlterm != NULL )		/* sequence may have terminator */
        !          4300: 	escapelen += (isthischar(termchar,htmlterm)?1:0); /*add terminator*/
        !          4301:       if ( isalpha((int)termchar) )	/*we just have prefix of longer sym*/
        !          4302: 	{ expptr = tokptr+htmllen;	/* just resume search after prefix */
        !          4303: 	  continue; }			/* but don't replace it */
        !          4304:       if ( !isthischar(*htmlsym,ESCAPE)	/* our symbol isn't escaped */
        !          4305:       &&   !isthischar(*htmlsym,"&") )	/* and not an &html; special char */
        !          4306:        if ( tokptr != expression )	/* then if we're past beginning */
        !          4307: 	if ( isthischar(*(tokptr-1),ESCAPE) /*and if inline symbol escaped*/
        !          4308: 	||   (isalpha(*(tokptr-1))) )	/* or if suffix of longer string */
        !          4309: 	  { expptr = tokptr+escapelen;	/*just resume search after literal*/
        !          4310: 	    continue; }			/* but don't replace it */
        !          4311:       if ( nargs > 0 )			/*substitute #1,#2,... in latexsym*/
        !          4312:        {
        !          4313:        char *arg1ptr = tokptr+escapelen;/* nargs begin after macro literal */
        !          4314:        char *optarg = args+1;		/* ptr 1 char past #args digit 0-9 */
        !          4315:        expptr = arg1ptr;		/* ptr to beginning of next arg */
        !          4316:        for ( iarg=1; iarg<=nargs; iarg++ ) /* one #`iarg` arg at a time */
        !          4317: 	{
        !          4318: 	char argsignal[32] = "#1",	/* #1...#9 signals arg replacement */
        !          4319: 	*argsigptr = NULL;		/* ptr to argsignal in abuff[] */
        !          4320: 	/* --- get argument value --- */
        !          4321: 	*argval = '\000';		/* init arg as empty string */
        !          4322: 	skipwhite(expptr);		/* and skip leading white space */
        !          4323: 	if ( iarg==1 && *optarg!='\000'	/* check for optional [arg] */
        !          4324: 	&&   !isalgebra )		/* but not in "algebra syntax" */
        !          4325: 	 { strcpy(argval,optarg);	/* init with default value */
        !          4326: 	   if ( *expptr == '[' )	/* but user gave us [argval] */
        !          4327: 	    expptr = texsubexpr(expptr,argval,0,"[","]",0,0); } /*so get it*/
        !          4328: 	else				/* not optional, so get {argval} */
        !          4329: 	 if ( *expptr != '\000' )	/* check that some argval provided */
        !          4330: 	  if ( !isalgebra )		/* only { } delims for latex macro */
        !          4331: 	    expptr = texsubexpr(expptr,argval,0,"{","}",0,0); /*get {argval}*/
        !          4332: 	  else				/*any delim for algebra syntax macro*/
        !          4333: 	   { expptr = texsubexpr(expptr,argval,0,aleft,aright,0,1);
        !          4334: 	     if ( isthischar(*argval,aleft) ) /* have delim-enclosed arg */
        !          4335: 	      if ( *argval != '{' )	/* and it's not { }-enclosed */
        !          4336: 	       { strchange(0,argval,"\\left"); /* insert opening \left, */
        !          4337: 		 strchange(0,argval+strlen(argval)-1,"\\right"); } /*\right*/
        !          4338: 	   } /* --- end-of-if/else(!isalgebra) --- */
        !          4339: 	/* --- replace #`iarg` in macro with argval --- */
        !          4340: 	sprintf(argsignal,"#%d",iarg);	/* #1...#9 signals argument */
        !          4341: 	while ( (argsigptr=strstr(argval,argsignal)) != NULL ) /* #1...#9 */
        !          4342: 	 strcpy(argsigptr,argsigptr+strlen(argsignal)); /*can't be in argval*/
        !          4343: 	while ( (argsigptr=strstr(abuff,argsignal)) != NULL ) /* #1...#9 */
        !          4344: 	 strchange(strlen(argsignal),argsigptr,argval); /*replaced by argval*/
        !          4345: 	} /* --- end-of-for(iarg) --- */
        !          4346:        escapelen += ((int)(expptr-arg1ptr)); /* add in length of all args */
        !          4347:        } /* --- end-of-if(nargs>0) --- */
        !          4348:       strchange(escapelen,tokptr,abuff); /*replace macro or html symbol*/
        !          4349:       expptr = tokptr + strlen(abuff); /*resume search after macro / html*/
        !          4350:     } /* --- end-of-while(tokptr!=NULL) --- */
        !          4351:   } /* --- end-of-for(isymbol) --- */
        !          4352: /* -------------------------------------------------------------------------
        !          4353: run thru table, converting all {a+b\atop c+d} to \atop{a+b}{c+d}
        !          4354: -------------------------------------------------------------------------- */
        !          4355: for(isymbol=0; (atopsym=atopcommands[isymbol]) != NULL; isymbol++)
        !          4356:   {
        !          4357:   int	atoplen = strlen(atopsym);	/* #chars in \atop */
        !          4358:   expptr = expression;			/* re-start search at beginning */
        !          4359:   while ( (tokptr=strstr(expptr,atopsym)) != NULL ) /* found another atop */
        !          4360:     { char *leftbrace=NULL, *rightbrace=NULL; /*ptr to opening {, closing }*/
        !          4361:       char termchar = *(tokptr+atoplen); /* \atop followed by terminator */
        !          4362:       if ( msgfp!=NULL && msglevel>=999 )
        !          4363: 	{ fprintf(msgfp,"mimeprep> offset=%d rhs=\"%s\"\n",
        !          4364: 	  (int)(tokptr-expression),tokptr);
        !          4365: 	  fflush(msgfp); }
        !          4366:       if ( isalpha((int)termchar) )	/*we just have prefix of longer sym*/
        !          4367: 	{ expptr = tokptr+atoplen;	/* just resume search after prefix */
        !          4368: 	  continue; }			/* but don't process it */
        !          4369:       leftbrace  = findbraces(expression,tokptr);     /* find left { */
        !          4370:       rightbrace = findbraces(NULL,tokptr+atoplen-1); /* find right } */
        !          4371:       if ( leftbrace==NULL || rightbrace==NULL )
        !          4372: 	{ expptr += atoplen;  continue; } /* skip command if didn't find */
        !          4373:       else				/* we have bracketed { \atop } */
        !          4374: 	{
        !          4375: 	int  leftlen  = (int)(tokptr-leftbrace) - 1, /* #chars in left arg */
        !          4376: 	     rightlen = (int)(rightbrace-tokptr) - atoplen, /* and in right*/
        !          4377: 	     totlen   = (int)(rightbrace-leftbrace) + 1; /*tot in { \atop }*/
        !          4378: 	char *open=atopdelims[2*isymbol], *close=atopdelims[2*isymbol+1];
        !          4379: 	char arg[8192], command[8192];	/* left/right args, new \atop{}{} */
        !          4380: 	*command = '\000';		/* start with null string */
        !          4381: 	if (open!=NULL) strcat(command,open); /* add open delim if needed */
        !          4382: 	strcat(command,atopsym);	/* add command with \atop */
        !          4383: 	arg[0] = '{';			/* arg starts with { */
        !          4384: 	memcpy(arg+1,leftbrace+1,leftlen); /* extract left-hand arg */
        !          4385: 	arg[leftlen+1] = '\000';	/* and null terminate it */
        !          4386: 	strcat(command,arg);		/* concatanate {left-arg to \atop */
        !          4387: 	strcat(command,"}{");		/* close left-arg, open right-arg */
        !          4388: 	memcpy(arg,tokptr+atoplen,rightlen); /* right-hand arg */
        !          4389: 	arg[rightlen] = '}';		/* add closing } */
        !          4390: 	arg[rightlen+1] = '\000';	/* and null terminate it */
        !          4391: 	if ( isthischar(*arg,WHITEMATH) ) /* 1st char was mandatory space */
        !          4392: 	  strcpy(arg,arg+1);		/* so squeeze it out */
        !          4393: 	strcat(command,arg);		/* concatanate right-arg} */
        !          4394: 	if (close!=NULL) strcat(command,close); /* add close delim if needed*/
        !          4395: 	strchange(totlen-2,leftbrace+1,command); /* {\atop} --> {\atop{}{}} */
        !          4396: 	expptr = leftbrace+strlen(command); /*resume search past \atop{}{}*/
        !          4397: 	}
        !          4398:     } /* --- end-of-while(tokptr!=NULL) --- */
        !          4399:   } /* --- end-of-for(isymbol) --- */
        !          4400: /* -------------------------------------------------------------------------
        !          4401: back to caller with preprocessed expression
        !          4402: -------------------------------------------------------------------------- */
        !          4403: if ( msgfp!=NULL && msglevel>=99 )	/* display preprocessed expression */
        !          4404:   { fprintf(msgfp,"mimeprep> expression=\"\"%s\"\"\n",expression);
        !          4405:     fflush(msgfp); }
        !          4406: return ( expression );
        !          4407: } /* --- end-of-function mimeprep() --- */
        !          4408: 
        !          4409: 
        !          4410: /* ==========================================================================
        !          4411:  * Function:	strchange ( int nfirst, char *from, char *to )
        !          4412:  * Purpose:	Changes the nfirst leading chars of `from` to `to`.
        !          4413:  *		For example, to change char x[99]="12345678" to "123ABC5678"
        !          4414:  *		call strchange(1,x+3,"ABC")
        !          4415:  * --------------------------------------------------------------------------
        !          4416:  * Arguments:	nfirst (I)	int containing #leading chars of `from`
        !          4417:  *				that will be replace by `to`
        !          4418:  *		from (I/O)	char * to null-terminated string whose nfirst
        !          4419:  *				leading chars will be replaced by `to`
        !          4420:  *		to (I)		char * to null-terminated string that will
        !          4421:  *				replace the nfirst leading chars of `from`
        !          4422:  * --------------------------------------------------------------------------
        !          4423:  * Returns:	( char * )	ptr to first char of input `from`
        !          4424:  *				or NULL for any error.
        !          4425:  * --------------------------------------------------------------------------
        !          4426:  * Notes:     o	If strlen(to)>nfirst, from must have memory past its null
        !          4427:  *		(i.e., we don't do a realloc)
        !          4428:  * ======================================================================= */
        !          4429: /* --- entry point --- */
        !          4430: char	*strchange ( int nfirst, char *from, char *to )
        !          4431: {
        !          4432: /* -------------------------------------------------------------------------
        !          4433: Allocations and Declarations
        !          4434: -------------------------------------------------------------------------- */
        !          4435: int	tolen = (to==NULL?0:strlen(to)), /* #chars in replacement string */
        !          4436: 	nshift = abs(tolen-nfirst);	/*need to shift from left or right*/
        !          4437: /* -------------------------------------------------------------------------
        !          4438: shift from left or right to accommodate replacement of its nfirst chars by to
        !          4439: -------------------------------------------------------------------------- */
        !          4440: if ( tolen < nfirst )			/* shift left is easy */
        !          4441:   strcpy(from,from+nshift);		/* because memory doesn't overlap */
        !          4442: if ( tolen > nfirst )			/* need more room at start of from */
        !          4443:   { char *pfrom = from+strlen(from);	/* ptr to null terminating from */
        !          4444:     for ( ; pfrom>=from; pfrom-- )	/* shift all chars including null */
        !          4445:       *(pfrom+nshift) = *pfrom; }	/* shift chars nshift places right */
        !          4446: /* -------------------------------------------------------------------------
        !          4447: from has exactly the right number of free leading chars, so just put to there
        !          4448: -------------------------------------------------------------------------- */
        !          4449: if ( tolen != 0 )			/* make sure to not empty or null */
        !          4450:   memcpy(from,to,tolen);		/* chars moved into place */
        !          4451: return ( from );			/* changed string back to caller */
        !          4452: } /* --- end-of-function strchange() --- */
        !          4453: 
        !          4454: 
        !          4455: /* ==========================================================================
        !          4456:  * Function:	strreplace (char *string, char *from, char *to, int nreplace)
        !          4457:  * Purpose:	Changes the first nreplace occurrences of 'from' to 'to'
        !          4458:  *		in string, or all occurrences if nreplace=0.
        !          4459:  * --------------------------------------------------------------------------
        !          4460:  * Arguments:	string (I/0)	char * to null-terminated string in which
        !          4461:  *				occurrence of 'from' will be replaced by 'to'
        !          4462:  *		from (I)	char * to null-terminated string
        !          4463:  *				to be replaced by 'to'
        !          4464:  *		to (I)		char * to null-terminated string that will
        !          4465:  *				replace 'from'
        !          4466:  *		nreplace (I)	int containing (maximum) number of
        !          4467:  *				replacements, or 0 to replace all.
        !          4468:  * --------------------------------------------------------------------------
        !          4469:  * Returns:	( int )		number of replacements performed,
        !          4470:  *				or 0 for no replacements or -1 for any error.
        !          4471:  * --------------------------------------------------------------------------
        !          4472:  * Notes:     o
        !          4473:  * ======================================================================= */
        !          4474: /* --- entry point --- */
        !          4475: int	strreplace ( char *string, char *from, char *to, int nreplace )
        !          4476: {
        !          4477: /* -------------------------------------------------------------------------
        !          4478: Allocations and Declarations
        !          4479: -------------------------------------------------------------------------- */
        !          4480: int	fromlen = (from==NULL?0:strlen(from)), /* #chars to be replaced */
        !          4481: 	tolen = (to==NULL?0:strlen(to)); /* #chars in replacement string */
        !          4482: char	*pfrom = (char *)NULL,		/*ptr to 1st char of from in string*/
        !          4483: 	*pstring = string,		/*ptr past previously replaced from*/
        !          4484: 	*strchange();			/* change 'from' to 'to' */
        !          4485: int	nreps = 0;			/* #replacements returned to caller*/
        !          4486: /* -------------------------------------------------------------------------
        !          4487: repace occurrences of 'from' in string to 'to'
        !          4488: -------------------------------------------------------------------------- */
        !          4489: if ( string == (char *)NULL		/* no input string */
        !          4490: ||   (fromlen<1 && nreplace<=0) )	/* replacing empty string forever */
        !          4491:   nreps = (-1);				/* so signal error */
        !          4492: else					/* args okay */
        !          4493:   while (nreplace<1 || nreps<nreplace)	/* up to #replacements requested */
        !          4494:     {
        !          4495:     if ( fromlen > 0 )			/* have 'from' string */
        !          4496:       pfrom = strstr(pstring,from);	/*ptr to 1st char of from in string*/
        !          4497:     else  pfrom = pstring;		/*or empty from at start of string*/
        !          4498:     if ( pfrom == (char *)NULL ) break;	/*no more from's, so back to caller*/
        !          4499:     if ( strchange(fromlen,pfrom,to)	/* leading 'from' changed to 'to' */
        !          4500:     ==   (char *)NULL ) { nreps=(-1); break; } /* signal error to caller */
        !          4501:     nreps++;				/* count another replacement */
        !          4502:     pstring = pfrom+tolen;		/* pick up search after 'to' */
        !          4503:     if ( *pstring == '\000' ) break;	/* but quit at end of string */
        !          4504:     } /* --- end-of-while() --- */
        !          4505: return ( nreps );			/* #replacements back to caller */
        !          4506: } /* --- end-of-function strreplace() --- */
        !          4507: 
        !          4508: 
        !          4509: /* ==========================================================================
        !          4510:  * Function:	strtexchr (char *string, char *texchr )
        !          4511:  * Purpose:	Find first texchr in string, but texchr must be followed
        !          4512:  *		by non-alpha
        !          4513:  * --------------------------------------------------------------------------
        !          4514:  * Arguments:	string (I)	char * to null-terminated string in which
        !          4515:  *				firstoccurrence of delim will be found
        !          4516:  *		texchr (I)	char * to null-terminated string that
        !          4517:  *				will be searched for
        !          4518:  * --------------------------------------------------------------------------
        !          4519:  * Returns:	( char * )	ptr to first char of texchr in string
        !          4520:  *				or NULL if not found or for any error.
        !          4521:  * --------------------------------------------------------------------------
        !          4522:  * Notes:     o	texchr should contain its leading \, e.g., "\\left"
        !          4523:  * ======================================================================= */
        !          4524: /* --- entry point --- */
        !          4525: char	*strtexchr ( char *string, char *texchr )
        !          4526: {
        !          4527: /* -------------------------------------------------------------------------
        !          4528: Allocations and Declarations
        !          4529: -------------------------------------------------------------------------- */
        !          4530: char	*strstr(), delim, *ptexchr=(char *)NULL;  /* ptr returned to caller*/
        !          4531: char	*pstring = string;		/* start or continue up search here*/
        !          4532: int	texchrlen = (texchr==NULL?0:strlen(texchr)); /* #chars in texchr */
        !          4533: /* -------------------------------------------------------------------------
        !          4534: locate texchr in string
        !          4535: -------------------------------------------------------------------------- */
        !          4536: if ( string != (char *)NULL		/* check that we got input string */
        !          4537: &&   texchrlen > 0 )			/* and a texchr to search for */
        !          4538:  while ( (ptexchr=strstr(pstring,texchr)) /* look for texchr in string */
        !          4539:  != (char *)NULL )			/* found it */
        !          4540:   if ( (delim = ptexchr[texchrlen])	/* char immediately after texchr */
        !          4541:   ==   '\000' ) break;			/* texchr at very end of string */
        !          4542:   else					/* if there are chars after texchr */
        !          4543:    if ( isalpha(delim)			/*texchr is prefix of longer symbol*/
        !          4544:    ||   0 )				/* other tests to be determined */
        !          4545:     pstring = ptexchr + texchrlen;	/* continue search after texchr */
        !          4546:    else					/* passed all tests */
        !          4547:     break;				/*so return ptr to texchr to caller*/
        !          4548: return ( ptexchr );			/* ptr to texchar back to caller */
        !          4549: } /* --- end-of-function strtexchr() --- */
        !          4550: 
        !          4551: 
        !          4552: /* ==========================================================================
        !          4553:  * Function:	findbraces ( char *expression, char *command )
        !          4554:  * Purpose:	If expression!=NULL, finds opening left { preceding command;
        !          4555:  *		if expression==NULL, finds closing right } after command.
        !          4556:  *		For example, to parse out {a+b\over c+d} call findbraces()
        !          4557:  *		twice.
        !          4558:  * --------------------------------------------------------------------------
        !          4559:  * Arguments:	expression (I)	NULL to find closing right } after command,
        !          4560:  *				or char * to null-terminated string to find
        !          4561:  *				left opening { preceding command.
        !          4562:  *		command (I)	char * to null-terminated string whose
        !          4563:  *				first character is usually the \ of \command
        !          4564:  * --------------------------------------------------------------------------
        !          4565:  * Returns:	( char * )	ptr to either opening { or closing },
        !          4566:  *				or NULL for any error.
        !          4567:  * --------------------------------------------------------------------------
        !          4568:  * Notes:     o
        !          4569:  * ======================================================================= */
        !          4570: /* --- entry point --- */
        !          4571: char	*findbraces ( char *expression, char *command )
        !          4572: {
        !          4573: /* -------------------------------------------------------------------------
        !          4574: Allocations and Declarations
        !          4575: -------------------------------------------------------------------------- */
        !          4576: int	isopen = (expression==NULL?0:1); /* true to find left opening { */
        !          4577: char	*left="{", *right="}",		/* delims bracketing {x\command y} */
        !          4578: 	*delim = (isopen?left:right),	/* delim we want,  { if isopen */
        !          4579: 	*match = (isopen?right:left),	/* matching delim, } if isopen */
        !          4580: 	*brace = NULL;			/* ptr to delim returned to caller */
        !          4581: int	inc = (isopen?-1:+1);		/* pointer increment */
        !          4582: int	level = 1;			/* nesting level, for {{}\command} */
        !          4583: char	*ptr = command;			/* start search here */
        !          4584: int	setbrace = 1;			/* true to set {}'s if none found */
        !          4585: /* -------------------------------------------------------------------------
        !          4586: search for left opening { before command, or right closing } after command
        !          4587: -------------------------------------------------------------------------- */
        !          4588: while ( 1 )				/* search for brace, or until end */
        !          4589:   {
        !          4590:   /* --- next char to check for delim --- */
        !          4591:   ptr += inc;				/* bump ptr left or right */
        !          4592:   /* --- check for beginning or end of expression --- */
        !          4593:   if ( isopen )				/* going left, check for beginning */
        !          4594:        { if ( ptr < expression ) break;	} /* went before start of string */
        !          4595:   else { if ( *ptr == '\000' ) break; }	/* went past end of string */
        !          4596:   /* --- don't check this char if it's escaped --- */
        !          4597:   if ( !isopen || ptr>expression )	/* very first char can't be escaped*/
        !          4598:     if ( isthischar(*(ptr-1),ESCAPE) )	/* escape char precedes current */
        !          4599:       continue;				/* so don't check this char */
        !          4600:   /* --- check for delim --- */
        !          4601:   if ( isthischar(*ptr,delim) )		/* found delim */
        !          4602:     if ( --level == 0 )			/* and it's not "internally" nested*/
        !          4603:       {	brace = ptr;			/* set ptr to brace */
        !          4604: 	goto end_of_job; }		/* and return it to caller */
        !          4605:   /* --- check for matching delim --- */
        !          4606:   if ( isthischar(*ptr,match) )		/* found matching delim */
        !          4607:     level++;				/* so bump nesting level */
        !          4608:   } /* --- end-of-while(1) --- */
        !          4609: end_of_job:
        !          4610:   if ( brace == (char *)NULL )		/* open{ or close} not found */
        !          4611:     if ( setbrace )			/* want to force one at start/end? */
        !          4612:       brace = ptr;			/* { before expressn, } after cmmnd*/
        !          4613:   return ( brace );			/*back to caller with delim or NULL*/
        !          4614: } /* --- end-of-function findbraces() --- */
        !          4615: #endif /* PART2 */
        !          4616: 
        !          4617: /* ---
        !          4618:  * PART3
        !          4619:  * ------ */
        !          4620: #if !defined(PARTS) || defined(PART3)
        !          4621: /* ==========================================================================
        !          4622:  * Function:	rasterize ( expression, size )
        !          4623:  * Purpose:	returns subraster corresponding to (a valid LaTeX) expression
        !          4624:  *		at font size
        !          4625:  * --------------------------------------------------------------------------
        !          4626:  * Arguments:	expression (I)	char * to first char of null-terminated
        !          4627:  *				string containing valid LaTeX expression
        !          4628:  *				to be rasterized
        !          4629:  *		size (I)	int containing 0-4 default font size
        !          4630:  * --------------------------------------------------------------------------
        !          4631:  * Returns:	( subraster * )	ptr to subraster corresponding to expression,
        !          4632:  *				or NULL for any parsing error.
        !          4633:  * --------------------------------------------------------------------------
        !          4634:  * Notes:     o	This is mimeTeX's "main" reusable entry point.  Easy to use:
        !          4635:  *		just call it with a LaTeX expression, and get back a bitmap
        !          4636:  *		of that expression.  Then do what you want with the bitmap.
        !          4637:  * ======================================================================= */
        !          4638: /* --- entry point --- */
        !          4639: subraster *rasterize ( char *expression, int size )
        !          4640: {
        !          4641: /* -------------------------------------------------------------------------
        !          4642: Allocations and Declarations
        !          4643: -------------------------------------------------------------------------- */
        !          4644: char	*preamble(), pretext[256];	/* process preamble, if present */
        !          4645: char	chartoken[8192], *texsubexpr(),	/*get subexpression from expression*/
        !          4646: 	*subexpr = chartoken;		/* token may be parenthesized expr */
        !          4647: int	isbrace();			/* check subexpr for braces */
        !          4648: mathchardef *symdef, *get_symdef();	/*get mathchardef struct for symbol*/
        !          4649: int	natoms=0;			/* #atoms/tokens processed so far */
        !          4650: int	type_raster();			/* display debugging output */
        !          4651: subraster *rasterize(),			/* recurse */
        !          4652: 	*rastparen(),			/* handle parenthesized subexpr's */
        !          4653: 	*rastlimits();			/* handle sub/superscripted expr's */
        !          4654: subraster *rastcat(),			/* concatanate atom subrasters */
        !          4655: 	*subrastcpy(),			/* copy final result if a charaster*/
        !          4656: 	*new_subraster();		/* new subraster for isstring mode */
        !          4657: subraster *get_charsubraster(),		/* character subraster */
        !          4658: 	*sp=NULL, *prevsp=NULL,		/* raster for current, prev char */
        !          4659: 	*expraster = (subraster *)NULL;	/* raster returned to caller */
        !          4660: int	delete_subraster();		/* free everything before returning*/
        !          4661: /*int	pixsz = 1;*/			/*default #bits per pixel, 1=bitmap*/
        !          4662: /* --- global values saved/restored at each recursive iteration --- */
        !          4663: int	wastext = istext,		/* initial istext mode flag */
        !          4664: 	wasstring = isstring,		/* initial isstring mode flag */
        !          4665: 	wasdisplaystyle = isdisplaystyle, /*initial displaystyle mode flag*/
        !          4666: 	oldfontsize = fontsize,		/* initial fontsize */
        !          4667: 	olddisplaysize = displaysize,	/* initial \displaystyle size */
        !          4668: 	oldshrinkfactor = shrinkfactor,	/* initial shrinkfactor */
        !          4669: 	oldsquashmargin = squashmargin,	/* initial squashmargin */
        !          4670: 	oldissquashdelta = issquashdelta, /* initial issquashdelta */
        !          4671: 	*oldworkingparam = workingparam; /* initial working parameter */
        !          4672: subraster *oldworkingbox = workingbox,	/* initial working box */
        !          4673: 	*oldleftexpression = leftexpression; /*left half rasterized so far*/
        !          4674: double	oldunitlength = unitlength;	/* initial unitlength */
        !          4675: mathchardef *oldleftsymdef = leftsymdef; /* init oldleftsymdef */
        !          4676: /* -------------------------------------------------------------------------
        !          4677: initialization
        !          4678: -------------------------------------------------------------------------- */
        !          4679: recurlevel++;				/* wind up one more recursion level*/
        !          4680: leftexpression = NULL;			/* no leading left half yet */
        !          4681: isreplaceleft = 0;			/* reset replaceleft flag */
        !          4682: /* shrinkfactor = shrinkfactors[max2(0,min2(size,LARGESTSIZE))];*/ /*set sf*/
        !          4683: shrinkfactor = shrinkfactors[max2(0,min2(size,16))]; /* have 17 sf's */
        !          4684: if ( msgfp!=NULL && msglevel >= 29 )	/*display expression for debugging*/
        !          4685:  { fprintf(msgfp,
        !          4686:    "rasterize> recursion level=%d, size=%d,\n\texpression=\"%s\"\n",
        !          4687:    recurlevel,size,(expression==NULL?"null":expression)); fflush(msgfp); }
        !          4688: if ( expression == NULL ) goto end_of_job; /* nothing given to do */
        !          4689: /* -------------------------------------------------------------------------
        !          4690: preocess optional $-terminated preamble preceding expression
        !          4691: -------------------------------------------------------------------------- */
        !          4692: expression = preamble(expression,&size,pretext); /* size may be modified */
        !          4693: if ( *expression == '\000' ) goto end_of_job; /* nothing left to do */
        !          4694: fontsize = size;			/* start at requested size */
        !          4695: if ( isdisplaystyle == 1 )		/* displaystyle enabled but not set*/
        !          4696:  if ( !ispreambledollars )		/* style fixed by $$...$$'s */
        !          4697:   isdisplaystyle = (fontsize>=displaysize? 2:1); /*force at large fontsize*/
        !          4698: /* -------------------------------------------------------------------------
        !          4699: build up raster one character (or subexpression) at a time
        !          4700: -------------------------------------------------------------------------- */
        !          4701: while ( 1 )
        !          4702:   {
        !          4703:   /* --- get next character/token or subexpression --- */
        !          4704:   expression = texsubexpr(expression,chartoken,0,LEFTBRACES,RIGHTBRACES,1,1);
        !          4705:   subexpr = chartoken;			/* "local" copy of chartoken ptr */
        !          4706:   leftsymdef = NULL;			/* no character identified yet */
        !          4707:   sp = NULL;				/* no subraster yet */
        !          4708:   size = fontsize;			/* in case reset by \tiny, etc */
        !          4709:   /* --- debugging output --- */
        !          4710:   if ( msgfp!=NULL && msglevel >= 999 )	/* display chartoken for debugging */
        !          4711:     { fprintf(msgfp,"rasterize> recursion level=%d, atom#%d = \"%s\"\n",
        !          4712:       recurlevel,natoms+1,chartoken); fflush(msgfp); }
        !          4713:   if ( expression == NULL		/* no more tokens */
        !          4714:   &&   *subexpr == '\000' ) break;	/* and this token empty */
        !          4715:   if ( *subexpr == '\000' ) break;	/* enough if just this token empty */
        !          4716:   /* --- check for parenthesized subexpression --- */
        !          4717:   if ( isbrace(subexpr,LEFTBRACES,1) )	/* got parenthesized subexpression */
        !          4718:     { if ( (sp=rastparen(&subexpr,size,prevsp)) /* rasterize subexpression */
        !          4719:       ==   NULL )  continue; }		/* flush it if failed to rasterize */
        !          4720:   else /* --- single-character atomic token --- */
        !          4721:    if ( !isthischar(*subexpr,SCRIPTS) )	/* scripts handled below */
        !          4722:     {
        !          4723:     /* --- first look up mathchardef for atomic token in table --- */
        !          4724:     if ( (leftsymdef=symdef=get_symdef(chartoken)) /*mathchardef for token*/
        !          4725:     ==  NULL )				/* lookup failed */
        !          4726:      { char literal[512] = "[?]";	/*display for unrecognized literal*/
        !          4727:        int  wastext = istext;		/* error display in default mode */
        !          4728:        if ( msgfp!=NULL && msglevel >= 29 ) /* display unrecognized symbol */
        !          4729: 	 { fprintf(msgfp,"rasterize> get_symdef() failed for \"%s\"\n",
        !          4730: 	   chartoken); fflush(msgfp); }
        !          4731:        sp = (subraster *)NULL;		/* init to signal failure */
        !          4732:        if ( warninglevel < 1 ) continue; /* warnings not wanted */
        !          4733:        istext = 0;			/* reset from \mathbb, etc */
        !          4734:        if ( isthischar(*chartoken,ESCAPE) ) /* we got unrecognized \escape */
        !          4735: 	{ /* --- so display literal {\rm~[\backslash~chartoken?]} ---  */
        !          4736: 	  strcpy(literal,"{\\rm~[\\backslash~"); /* init token */
        !          4737: 	  strcat(literal,chartoken+1);	/* add chars following leading \ */
        !          4738: 	  strcat(literal,"?]}"); }	/* add closing brace */
        !          4739:        sp = rasterize(literal,size-1);	/* rasterize literal token */
        !          4740:        istext = wastext;		/* reset text mode */
        !          4741:        if ( sp == (subraster *)NULL ) continue; } /*flush if rasterize fails*/
        !          4742:     else /* --- check if we have special handler to process this token --- */
        !          4743:      if ( symdef->handler != NULL )	/* have a handler for this token */
        !          4744:       { int arg1=symdef->charnum, arg2=symdef->family, arg3=symdef->class;
        !          4745: 	if ( (sp = (subraster *)	/* returned void* is subraster* */
        !          4746: 	(*(symdef->handler))(&expression,size,prevsp,arg1,arg2,arg3))== NULL )
        !          4747: 	  continue; }			/* flush token if handler failed */
        !          4748:      else /* --- no handler, so just get subraster for this character --- */
        !          4749:       if ( !isstring )			/* rasterizing */
        !          4750: 	{ if ( (sp=get_charsubraster(symdef,size)) /* get subraster */
        !          4751: 	  ==  NULL )  continue; }	/* flush token if failed */
        !          4752:       else				/* constructing ascii string */
        !          4753: 	{ char *symbol = symdef->symbol; /* symbol for ascii string */
        !          4754: 	  int symlen = (symbol!=NULL?strlen(symbol):0); /*#chars in symbol*/
        !          4755: 	  if ( symlen < 1 ) continue;	/* no symbol for ascii string */
        !          4756: 	  if ( (sp=new_subraster(symlen+1,1,8)) /* subraster for symbol */
        !          4757: 	  ==  NULL )  continue;		/* flush token if malloc failed */
        !          4758: 	  sp->type = ASCIISTRING;	/* set subraster type */
        !          4759: 	  sp->symdef = symdef;		/* and set symbol definition */
        !          4760: 	  sp->baseline = 1;		/* default (should be unused) */
        !          4761: 	  strcpy((char *)((sp->image)->pixmap),symbol); /* copy symbol */
        !          4762: 	  /*((char *)((sp->image)->pixmap))[symlen] = '\000';*/ } /*null*/
        !          4763:     } /* --- end-of-if/else ... if/else --- */
        !          4764:   /* --- handle any super/subscripts following symbol or subexpression --- */
        !          4765:   sp = rastlimits(&expression,size,sp);
        !          4766:   /* --- debugging output --- */
        !          4767:   if ( msgfp!=NULL && msglevel >= 999 )	/* display raster for debugging */
        !          4768:     { fprintf(msgfp,"rasterize> recursion level=%d, atom#%d%s\n",
        !          4769:       recurlevel,natoms+1,(sp==NULL?" = null":"..."));
        !          4770:       if(sp!=NULL) type_raster(sp->image,msgfp); /* display raster */
        !          4771:       fflush(msgfp); }			/* flush msgfp buffer */
        !          4772:   /* --- accumulate atom or parenthesized subexpression --- */
        !          4773:   if ( natoms < 1			/* nothing previous to concat */
        !          4774:   ||   expraster == NULL		/* or previous was complete error */
        !          4775:   ||   isreplaceleft )			/* or we're replacing previous */
        !          4776:     { expraster = subrastcpy(sp);	/* so just copy static CHARASTER */
        !          4777:       isreplaceleft = 0; }		/* reset replacement flag */
        !          4778:   else					/*we've already built up atoms so...*/
        !          4779:    if ( sp != NULL )			/* ...if we have a new component */
        !          4780:     expraster = rastcat(expraster,sp,1); /* concat new one, free previous */
        !          4781:   delete_subraster(prevsp);		/* free prev (if not a CHARASTER) */
        !          4782:   prevsp = sp;				/* current becomes previous */
        !          4783:   leftexpression = expraster;		/* left half rasterized so far */
        !          4784:   /* --- bump count --- */
        !          4785:   natoms++;				/* bump #atoms count */
        !          4786:   } /* --- end-of-while(expression!=NULL) --- */
        !          4787: /* -------------------------------------------------------------------------
        !          4788: back to caller with rasterized expression
        !          4789: -------------------------------------------------------------------------- */
        !          4790: end_of_job:
        !          4791:   delete_subraster(prevsp);		/* free last (if not a CHARASTER) */
        !          4792:   /* --- debugging output --- */
        !          4793:   if ( msgfp!=NULL && msglevel >= 999 )	/* display raster for debugging */
        !          4794:     { fprintf(msgfp,"rasterize> Final recursion level=%d, atom#%d...\n",
        !          4795:       recurlevel,natoms);
        !          4796:       if ( expraster != (subraster *)NULL ) /* i.e., if natoms>0 */
        !          4797: 	type_raster(expraster->image,msgfp); /* display completed raster */
        !          4798:       fflush(msgfp); }			/* flush msgfp buffer */
        !          4799:   /* --- restore flags/values to original saved values --- */
        !          4800:   istext = wastext;			/* text mode reset */
        !          4801:   isstring = wasstring;			/* string mode reset */
        !          4802:   isdisplaystyle = wasdisplaystyle;	/* displaystyle mode reset */
        !          4803:   fontsize = oldfontsize;		/* fontsize reset */
        !          4804:   displaysize = olddisplaysize;		/* \displaystyle size reset */
        !          4805:   shrinkfactor = oldshrinkfactor;	/* shrinkfactor reset */
        !          4806:   squashmargin = oldsquashmargin;	/* squashmargin reset */
        !          4807:   issquashdelta = oldissquashdelta;	/* issquashdelta reset */
        !          4808:   workingparam = oldworkingparam;	/* working parameter reset */
        !          4809:   workingbox = oldworkingbox;		/* working box reset */
        !          4810:   leftexpression = oldleftexpression;	/* leftexpression reset */
        !          4811:   leftsymdef = oldleftsymdef;		/* leftsymdef reset */
        !          4812:   unitlength = oldunitlength;		/* unitlength reset */
        !          4813:   recurlevel--;				/* unwind one recursion level */
        !          4814:   /* --- return final subraster to caller --- */
        !          4815:   if ( 1 && expraster != (subraster *)NULL ) /* have an expression */
        !          4816:     { expraster->type = IMAGERASTER;	/* set type to constructed image */
        !          4817:       expraster->size = fontsize; }	/* set original input font size */
        !          4818:   return ( expraster );
        !          4819: } /* --- end-of-function rasterize() --- */
        !          4820: 
        !          4821: 
        !          4822: /* ==========================================================================
        !          4823:  * Function:	rastparen ( subexpr, size, basesp )
        !          4824:  * Purpose:	parentheses handler, returns a subraster corresponding to
        !          4825:  *		parenthesized subexpression at font size
        !          4826:  * --------------------------------------------------------------------------
        !          4827:  * Arguments:	subexpr (I)	char **  to first char of null-terminated
        !          4828:  *				string beginning with a LEFTBRACES
        !          4829:  *				to be rasterized
        !          4830:  *		size (I)	int containing 0-5 default font size
        !          4831:  *		basesp (I)	subraster *  to character (or subexpression)
        !          4832:  *				immediately preceding leading left{
        !          4833:  *				(unused, but passed for consistency)
        !          4834:  * --------------------------------------------------------------------------
        !          4835:  * Returns:	( subraster * )	ptr to subraster corresponding to subexpr,
        !          4836:  *				or NULL for any parsing error
        !          4837:  * --------------------------------------------------------------------------
        !          4838:  * Notes:     o	This "handler" isn't in the mathchardef symbol table,
        !          4839:  *		but is called directly from rasterize(), as necessary.
        !          4840:  *	      o	Though subexpr is returned unchanged, it's passed as char **
        !          4841:  *		for consistency with other handlers.  Ditto, basesp is unused
        !          4842:  *		but passed for consistency
        !          4843:  * ======================================================================= */
        !          4844: /* --- entry point --- */
        !          4845: subraster *rastparen ( char **subexpr, int size, subraster *basesp )
        !          4846: {
        !          4847: /* -------------------------------------------------------------------------
        !          4848: Allocations and Declarations
        !          4849: -------------------------------------------------------------------------- */
        !          4850: char	*expression = *subexpr;		/* dereference subexpr to get char* */
        !          4851: int	explen = strlen(expression);	/* total #chars, including parens */
        !          4852: int	isescape = 0,			/* true if parens \escaped */
        !          4853: 	isrightdot = 0,			/* true if right paren is \right. */
        !          4854: 	isleftdot = 0;			/* true if left paren is \left. */
        !          4855: char	left[16], right[16];		/* parens enclosing expresion */
        !          4856: char	noparens[8192];			/* get subexpr without parens */
        !          4857: subraster *rasterize(), *sp=NULL;	/* rasterize what's between ()'s */
        !          4858: int	isheight = 1;			/*true=full height, false=baseline*/
        !          4859: int	height,				/* height of rasterized noparens[] */
        !          4860: 	baseline;			/* and its baseline */
        !          4861: int	family = CMEX10;		/* family for paren chars */
        !          4862: subraster *get_delim(), *lp=NULL, *rp=NULL; /* left and right paren chars */
        !          4863: subraster *rastcat();			/* concatanate subrasters */
        !          4864: int	delete_subraster();		/*in case of error after allocation*/
        !          4865: /* -------------------------------------------------------------------------
        !          4866: rasterize "interior" of expression, i.e., without enclosing parens
        !          4867: -------------------------------------------------------------------------- */
        !          4868: /* --- first see if enclosing parens are \escaped --- */
        !          4869: if ( isthischar(*expression,ESCAPE) )	/* expression begins with \escape */
        !          4870:   isescape = 1;				/* so set flag accordingly */
        !          4871: /* --- get expression *without* enclosing parens --- */
        !          4872: strcpy(noparens,expression);		/* get local copy of expression */
        !          4873: noparens[explen-(1+isescape)] = '\000';	/* null-terminate before right} */
        !          4874: strcpy(noparens,noparens+(1+isescape));	/* and then squeeze out left{ */
        !          4875: /* --- rasterize it --- */
        !          4876: if ( (sp = rasterize(noparens,size))	/*rasterize "interior" of expression*/
        !          4877: ==   NULL ) goto end_of_job;		/* quit if failed */
        !          4878: /* --- no need to add parentheses for unescaped { --- */
        !          4879: if ( !isescape && isthischar(*expression,"{") ) /* don't add parentheses */
        !          4880:   goto end_of_job;			/* just return sp to caller */
        !          4881: /* -------------------------------------------------------------------------
        !          4882: obtain paren characters to enclose noparens[] raster with
        !          4883: -------------------------------------------------------------------------- */
        !          4884: /* --- first get left and right parens from expression --- */
        !          4885: memset(left,0,16);  memset(right,0,16);	/* init parens with nulls */
        !          4886: left[0] = *(expression+isescape);	/* left{ is 1st or 2nd char */
        !          4887: right[0] = *(expression+explen-1);	/* right} is always last char */
        !          4888: isleftdot  = (isescape && isthischar(*left,".")); /* true if \left. */
        !          4889: isrightdot = (isescape && isthischar(*right,".")); /* true if \right. */
        !          4890: /* --- need height of noparens[] raster as minimum parens height --- */
        !          4891: height = (sp->image)->height;		/* height of noparens[] raster */
        !          4892: baseline = sp->baseline;		/* baseline of noparens[] raster */
        !          4893: if ( !isheight ) height = baseline+1;	/* parens only enclose baseline up */
        !          4894: /* --- get best-fit parentheses characters --- */
        !          4895: if ( !isleftdot )			/* if not \left. */
        !          4896:   lp = get_delim(left,height+1,family);	/* get left paren char */
        !          4897: if ( !isrightdot )			/* and if not \right. */
        !          4898:   rp = get_delim(right,height+1,family); /* get right paren char */
        !          4899: if ( (lp==NULL && !isleftdot)		/* check that we got left( */
        !          4900: ||   (rp==NULL && !isrightdot) )	/* and right) if needed */
        !          4901:   { delete_subraster(sp);		/* if failed, free subraster */
        !          4902:     if ( lp != NULL ) free ((void *)lp);/*free left-paren subraster envelope*/
        !          4903:     if ( rp != NULL ) free ((void *)rp);/*and right-paren subraster envelope*/
        !          4904:     sp = (subraster *)NULL;		/* signal error to caller */
        !          4905:     goto end_of_job; }			/* and quit */
        !          4906: /* -------------------------------------------------------------------------
        !          4907: set paren baselines to center on noparens[] raster, and concat components
        !          4908: -------------------------------------------------------------------------- */
        !          4909: /* --- set baselines to center paren chars on raster --- */
        !          4910: if ( lp != NULL )			/* ignore for \left. */
        !          4911:   lp->baseline = baseline + ((lp->image)->height - height)/2;
        !          4912: if ( rp != NULL )			/* ignore for \right. */
        !          4913:   rp->baseline = baseline + ((rp->image)->height - height)/2;
        !          4914: /* --- concat lp||sp||rp to obtain final result --- */
        !          4915: if ( lp != NULL )			/* ignore \left. */
        !          4916:   sp = rastcat(lp,sp,3);		/* concat lp||sp and free sp,lp */
        !          4917: if ( sp != NULL )			/* succeeded or ignored \left. */
        !          4918:   if ( rp != NULL )			/* ignore \right. */
        !          4919:     sp = rastcat(sp,rp,3);		/* concat sp||rp and free sp,rp */
        !          4920: /* --- back to caller --- */
        !          4921: end_of_job:
        !          4922:   return ( sp );
        !          4923: } /* --- end-of-function rastparen() --- */
        !          4924: 
        !          4925: 
        !          4926: /* ==========================================================================
        !          4927:  * Function:	rastlimits ( expression, size, basesp )
        !          4928:  * Purpose:	\limits, \nolimts, _ and ^ handler,
        !          4929:  *		dispatches call to rastscripts() or to rastdispmath()
        !          4930:  *		as necessary, to handle sub/superscripts following symbol
        !          4931:  * --------------------------------------------------------------------------
        !          4932:  * Arguments:	expression (I)	char **  to first char of null-terminated
        !          4933:  *				LaTeX expression (unused/unchanged)
        !          4934:  *		size (I)	int containing base font size (not used,
        !          4935:  *				just stored in subraster)
        !          4936:  *		basesp (I)	subraster *  to current character (or
        !          4937:  *				subexpression) immediately preceding script
        !          4938:  *				indicator
        !          4939:  * --------------------------------------------------------------------------
        !          4940:  * Returns:	( subraster * )	ptr to subraster returned by rastscripts()
        !          4941:  *				or rastdispmath(), or NULL for any error
        !          4942:  * --------------------------------------------------------------------------
        !          4943:  * Notes:     o
        !          4944:  * ======================================================================= */
        !          4945: /* --- entry point --- */
        !          4946: subraster *rastlimits ( char **expression, int size, subraster *basesp )
        !          4947: {
        !          4948: /* -------------------------------------------------------------------------
        !          4949: Allocations and Declarations
        !          4950: -------------------------------------------------------------------------- */
        !          4951: subraster *rastscripts(), *rastdispmath(), /*one of these will do the work*/
        !          4952: 	*rastcat(),			/* may need to concat scripts */
        !          4953: 	*scriptsp = basesp;		/* and this will become the result */
        !          4954: int	isdisplay = (-1);		/* set 1 for displaystyle, else 0 */
        !          4955: int	oldsquashmargin = squashmargin;	/* save original squashmargin */
        !          4956: int	type_raster();			/* display debugging output */
        !          4957: /* --- to check for \limits or \nolimits preceding scripts --- */
        !          4958: char	*texchar(), *exprptr=*expression, limtoken[255]; /*check for \limits*/
        !          4959: int	toklen=0;			/* strlen(limtoken) */
        !          4960: mathchardef *tokdef, *get_symdef();	/* mathchardef struct for limtoken */
        !          4961: int	class=(leftsymdef==NULL?NOVALUE:leftsymdef->class); /*base sym class*/
        !          4962: /* -------------------------------------------------------------------------
        !          4963: determine whether or not to use displaymath
        !          4964: -------------------------------------------------------------------------- */
        !          4965: scriptlevel++;				/* first, increment subscript level*/
        !          4966: *limtoken = '\000';			/* no token yet */
        !          4967: if ( msgfp!=NULL && msglevel>=999 )
        !          4968:  { fprintf(msgfp,"rastlimits> scriptlevel#%d exprptr=%.48s\n",
        !          4969:    scriptlevel,(exprptr==NULL?"null":exprptr));  fflush(msgfp); }
        !          4970: if ( isstring ) goto end_of_job;	/* no scripts for ascii string */
        !          4971: /* --- check for \limits or \nolimits --- */
        !          4972: skipwhite(exprptr);			/* skip white space before \limits */
        !          4973: if ( exprptr != NULL )			/* expression ptr supplied */
        !          4974:  if ( *exprptr != '\000' )		/* something in expression */
        !          4975:   exprptr = texchar(exprptr,limtoken);	/* retrieve next token */
        !          4976: if ( *limtoken != '\000' )		/* have token */
        !          4977:  if ( (toklen=strlen(limtoken)) >= 3 )	/* which may be \[no]limits */
        !          4978:   if ( memcmp("\\limits",limtoken,toklen) == 0     /* may be \limits */
        !          4979:   ||   memcmp("\\nolimits",limtoken,toklen) == 0 ) /* or may be \nolimits */
        !          4980:    if ( (tokdef= get_symdef(limtoken))	/* look up token to be sure */
        !          4981:    !=   NULL )				/* found token in table */
        !          4982:     if ( strcmp("\\limits",tokdef->symbol) == 0 )  /* found \limits */
        !          4983:       isdisplay = 1;			/* so explicitly set displaymath */
        !          4984:     else				/* wasn't \limits */
        !          4985:       if ( strcmp("\\nolimits",tokdef->symbol) == 0 ) /* found \nolimits */
        !          4986: 	isdisplay = 0;			/* so explicitly reset displaymath */
        !          4987: /* --- see if we found \[no]limits --- */
        !          4988: if ( isdisplay != (-1) )		/* explicit directive found */
        !          4989:   *expression = exprptr;		/* so bump expression past it */
        !          4990: else					/* noexplicit directive */
        !          4991:   { isdisplay = 0;			/* init displaymath flag off */
        !          4992:     if ( isdisplaystyle )		/* we're in displaystyle math mode */
        !          4993:       if ( isdisplaystyle >= 5 )	/* and mode irrevocably forced true */
        !          4994: 	{ if ( class!=OPENING && class!=CLOSING ) /*don't force ('s and )'s*/
        !          4995: 	    isdisplay = 1; }		/* set flag if mode forced true */
        !          4996:       else
        !          4997:        if ( isdisplaystyle >= 2 )	/*or mode forced conditionally true*/
        !          4998: 	{ if ( class!=VARIABLE && class!=ORDINARY /*don't force characters*/
        !          4999: 	  &&   class!=OPENING  && class!=CLOSING  /*don't force ('s and )'s*/
        !          5000: 	  &&   class!=BINARYOP		/* don't force binary operators */
        !          5001: 	  &&   class!=NOVALUE )		/* finally, don't force "images" */
        !          5002: 	    isdisplay = 1; }		/* set flag if mode forced true */
        !          5003:        else				/* determine mode from base symbol */
        !          5004: 	if ( class == DISPOPER )	/* it's a displaystyle operator */
        !          5005: 	  isdisplay = 1; }		/* so set flag */
        !          5006: /* -------------------------------------------------------------------------
        !          5007: dispatch call to create sub/superscripts
        !          5008: -------------------------------------------------------------------------- */
        !          5009: if ( isdisplay )			/* scripts above/below base symbol */
        !          5010:   scriptsp = rastdispmath(expression,size,basesp); /* everything all done */
        !          5011: else					/* scripts alongside base symbol */
        !          5012:   if ( (scriptsp=rastscripts(expression,size,basesp)) == NULL ) /*no scripts*/
        !          5013:     scriptsp = basesp;			/* so just return unscripted symbol*/
        !          5014:   else					/* symbols followed by scripts */
        !          5015:     if ( basesp != NULL )		/* have base symbol */
        !          5016:      { squashmargin = 0;		/* don't squash script */
        !          5017:        scriptsp = rastcat(basesp,scriptsp,2); /*concat scripts to base sym*/
        !          5018:        scriptsp->type = IMAGERASTER;	/* flip type of composite object */
        !          5019:        scriptsp->size = size; }		/* and set font size */
        !          5020: end_of_job:
        !          5021:   squashmargin = oldsquashmargin;	/* reset original squashmargin */
        !          5022:   if ( msgfp!=NULL && msglevel>=99 )
        !          5023:     { fprintf(msgfp,"rastlimits> scriptlevel#%d returning %s\n",
        !          5024: 	scriptlevel,(scriptsp==NULL?"null":"..."));
        !          5025:       if ( scriptsp != NULL )		/* have a constructed raster */
        !          5026: 	type_raster(scriptsp->image,msgfp); /*display constructed raster*/
        !          5027:       fflush(msgfp); }
        !          5028:   scriptlevel--;			/*lastly, decrement subscript level*/
        !          5029:   return ( scriptsp );
        !          5030: } /* --- end-of-function rastlimits() --- */
        !          5031: 
        !          5032: 
        !          5033: /* ==========================================================================
        !          5034:  * Function:	rastscripts ( expression, size, basesp )
        !          5035:  * Purpose:	super/subscript handler, returns subraster for the leading
        !          5036:  *		scripts in expression, whose base symbol is at font size
        !          5037:  * --------------------------------------------------------------------------
        !          5038:  * Arguments:	expression (I/O) char **  to first char of null-terminated
        !          5039:  *				string beginning with a super/subscript,
        !          5040:  *				and returning ptr immediately following
        !          5041:  *				last script character processed.
        !          5042:  *		size (I)	int containing 0-4 default font size
        !          5043:  *		basesp (I)	subraster *  to character (or subexpression)
        !          5044:  *				immediately preceding leading script
        !          5045:  *				(scripts will be placed relative to base)
        !          5046:  * --------------------------------------------------------------------------
        !          5047:  * Returns:	( subraster * )	ptr to subraster corresponding to scripts,
        !          5048:  *				or NULL for any parsing error
        !          5049:  * --------------------------------------------------------------------------
        !          5050:  * Notes:     o	This "handler" isn't in the mathchardef symbol table,
        !          5051:  *		but is called directly from rasterize(), as necessary.
        !          5052:  * ======================================================================= */
        !          5053: /* --- entry point --- */
        !          5054: subraster *rastscripts ( char **expression, int size, subraster *basesp )
        !          5055: {
        !          5056: /* -------------------------------------------------------------------------
        !          5057: Allocations and Declarations
        !          5058: -------------------------------------------------------------------------- */
        !          5059: char	*texscripts(),			/* parse expression for scripts */
        !          5060: 	subscript[512], supscript[512];	/* scripts parsed from expression */
        !          5061: subraster *rasterize(), *subsp=NULL, *supsp=NULL; /* rasterize scripts */
        !          5062: subraster *new_subraster(), *sp=NULL,	/* super- over subscript subraster */
        !          5063: 	*rastack();			/*sets scripts in displaymath mode*/
        !          5064: raster	*rp=NULL;			/* image raster embedded in sp */
        !          5065: int	height=0, width=0,  baseline=0,	/* height,width,baseline of sp */
        !          5066: 	subht=0,  subwidth=0,  subln=0,	/* height,width,baseline of sub */
        !          5067: 	supht=0,  supwidth=0,  supln=0,	/* height,width,baseline of sup */
        !          5068: 	baseht=0, baseln=0;		/* height,baseline of base */
        !          5069: int	bdescend=0, sdescend=0;		/* descender of base, subscript */
        !          5070: int	issub=0, issup=0, isboth=0,	/* true if we have sub,sup,both */
        !          5071: 	isbase=0;			/* true if we have base symbol */
        !          5072: int	szval = min2(max2(size,0),LARGESTSIZE), /* 0...LARGESTSIZE */
        !          5073: 	vbetween = 2,			/* vertical space between scripts */
        !          5074: 	vabove   = szval+1,		/*sup's top/bot above base's top/bot*/
        !          5075: 	vbelow   = szval+1,		/*sub's top/bot below base's top/bot*/
        !          5076: 	vbottom  = szval+1;		/*sup's bot above (sub's below) bsln*/
        !          5077: /*int	istweak = 1;*/			/* true to tweak script positioning */
        !          5078: int	rastput();			/*put scripts in constructed raster*/
        !          5079: int	delete_subraster();		/* free work areas */
        !          5080: int	pixsz = 1;			/*default #bits per pixel, 1=bitmap*/
        !          5081: /* -------------------------------------------------------------------------
        !          5082: Obtain subscript and/or superscript expressions, and rasterize them/it
        !          5083: -------------------------------------------------------------------------- */
        !          5084: /* --- parse for sub,superscript(s), and bump expression past it(them) --- */
        !          5085: if ( expression == NULL ) goto end_of_job; /* no *ptr given */
        !          5086: if ( *expression == NULL ) goto end_of_job; /* no expression given */
        !          5087: if ( *(*expression) == '\000' ) goto end_of_job; /* nothing in expression */
        !          5088: *expression = texscripts(*expression,subscript,supscript,3);
        !          5089: /* --- rasterize scripts --- */
        !          5090: if ( *subscript != '\000' )		/* have a subscript */
        !          5091:   subsp = rasterize(subscript,size-1);	/* so rasterize it at size-1 */
        !          5092: if ( *supscript != '\000' )		/* have a superscript */
        !          5093:   supsp = rasterize(supscript,size-1);	/* so rasterize it at size-1 */
        !          5094: /* --- set flags for convenience --- */
        !          5095: issub  = (subsp != (subraster *)NULL);	/* true if we have subscript */
        !          5096: issup  = (supsp != (subraster *)NULL);	/* true if we have superscript */
        !          5097: isboth = (issub && issup);		/* true if we have both */
        !          5098: if (!issub && !issup) goto end_of_job;	/* quit if we have neither */
        !          5099: /* -------------------------------------------------------------------------
        !          5100: get height, width, baseline of scripts,  and height, baseline of base symbol
        !          5101: -------------------------------------------------------------------------- */
        !          5102: /* --- get height and width of components --- */
        !          5103: if ( issub )				/* we have a subscript */
        !          5104:   { subht    = (subsp->image)->height;	/* so get its height */
        !          5105:     subwidth = (subsp->image)->width;	/* and width */
        !          5106:     subln    =  subsp->baseline; }	/* and baseline */
        !          5107: if ( issup )				/* we have a superscript */
        !          5108:   { supht    = (supsp->image)->height;	/* so get its height */
        !          5109:     supwidth = (supsp->image)->width;	/* and width */
        !          5110:     supln    =  supsp->baseline; }	/* and baseline */
        !          5111: /* --- get height and baseline of base, and descender of base and sub --- */
        !          5112: if ( basesp == (subraster *)NULL )	/* no base symbol for scripts */
        !          5113:   basesp = leftexpression;		/* try using left side thus far */
        !          5114: if ( basesp != (subraster *)NULL )	/* we have base symbol for scripts */
        !          5115:   { baseht   = (basesp->image)->height;	/* height of base symbol */
        !          5116:     baseln   =  basesp->baseline;	/* and its baseline */
        !          5117:     bdescend =  baseht-(baseln+1);	/* and base symbol descender */
        !          5118:     sdescend =  bdescend + vbelow;	/*sub must descend by at least this*/
        !          5119:     if ( baseht > 0 ) isbase = 1; }	/* set flag */
        !          5120: /* -------------------------------------------------------------------------
        !          5121: determine width of constructed raster
        !          5122: -------------------------------------------------------------------------- */
        !          5123: width = max2(subwidth,supwidth);	/*widest component is overall width*/
        !          5124: /* -------------------------------------------------------------------------
        !          5125: determine height and baseline of constructed raster
        !          5126: -------------------------------------------------------------------------- */
        !          5127: /* --- both super/subscript --- */
        !          5128: if ( isboth )				/*we have subscript and superscript*/
        !          5129:   { height = max2(subht+vbetween+supht,	/* script heights + space bewteen */
        !          5130: 		vbelow+baseht+vabove);	/*sub below base bot, sup above top*/
        !          5131:     baseline = baseln + (height-baseht)/2; } /*center scripts on base symbol*/
        !          5132: /* --- superscript only --- */
        !          5133: if ( !issub )				/* we only have a superscript */
        !          5134:   { height = max3(baseln+1+vabove,	/* sup's top above base symbol top */
        !          5135: 		supht+vbottom,		/* sup's bot above baseln */
        !          5136: 		supht+vabove-bdescend);	/* sup's bot above base symbol bot */
        !          5137:     baseline = height-1; }		/*sup's baseline at bottom of raster*/
        !          5138: /* --- subscript only --- */
        !          5139: if ( !issup )				/* we only have a subscript */
        !          5140:   if ( subht > sdescend )		/*sub can descend below base bot...*/
        !          5141:     { height = subht;			/* ...without extra space on top */
        !          5142:       baseline = height-(sdescend+1);	/* sub's bot below base symbol bot */
        !          5143:       baseline = min2(baseline,max2(baseln-vbelow,0)); }/*top below base top*/
        !          5144:   else					/* sub's top will be below baseln */
        !          5145:     { height = sdescend+1;		/* sub's bot below base symbol bot */
        !          5146:       baseline = 0; }			/* sub's baseline at top of raster */
        !          5147: /* -------------------------------------------------------------------------
        !          5148: construct raster with superscript over subscript
        !          5149: -------------------------------------------------------------------------- */
        !          5150: /* --- allocate subraster containing constructed raster --- */
        !          5151: if ( (sp=new_subraster(width,height,pixsz)) /*allocate subraster and raster*/
        !          5152: ==   NULL )				/* and if we fail to allocate */
        !          5153:   goto end_of_job;			/* quit */
        !          5154: /* --- initialize subraster parameters --- */
        !          5155: sp->type  = IMAGERASTER;		/* set type as constructed image */
        !          5156: sp->size  = size;			/* set given size */
        !          5157: sp->baseline = baseline;		/* composite scripts baseline */
        !          5158: rp = sp->image;				/* raster embedded in subraster */
        !          5159: /* --- place super/subscripts in new raster --- */
        !          5160: if ( issup )				/* we have a superscript */
        !          5161:  rastput(rp,supsp->image,0,0,1);	/* it goes in upper-left corner */
        !          5162: if ( issub )				/* we have a subscript */
        !          5163:  rastput(rp,subsp->image,height-subht,0,1); /*in lower-left corner*/
        !          5164: /* -------------------------------------------------------------------------
        !          5165: free unneeded component subrasters and return final result to caller
        !          5166: -------------------------------------------------------------------------- */
        !          5167: end_of_job:
        !          5168:   if ( issub ) delete_subraster(subsp);	/* free unneeded subscript */
        !          5169:   if ( issup ) delete_subraster(supsp);	/* and superscript */
        !          5170:   return ( sp );
        !          5171: } /* --- end-of-function rastscripts() --- */
        !          5172: 
        !          5173: 
        !          5174: /* ==========================================================================
        !          5175:  * Function:	rastdispmath ( expression, size, sp )
        !          5176:  * Purpose:	displaymath handler, returns sp along with
        !          5177:  *		its immediately following super/subscripts
        !          5178:  * --------------------------------------------------------------------------
        !          5179:  * Arguments:	expression (I/O) char **  to first char of null-terminated
        !          5180:  *				string immediately following sp to be
        !          5181:  *				rasterized along with its super/subscripts,
        !          5182:  *				and returning ptr immediately following last
        !          5183:  *				character processed.
        !          5184:  *		size (I)	int containing 0-4 default font size
        !          5185:  *		sp (I)		subraster *  to display math operator
        !          5186:  *				to which super/subscripts will be added
        !          5187:  * --------------------------------------------------------------------------
        !          5188:  * Returns:	( subraster * )	ptr to subraster corresponding to sp
        !          5189:  *				plus its scripts, or NULL for any error
        !          5190:  * --------------------------------------------------------------------------
        !          5191:  * Notes:     o	sp returned unchanged if no super/subscript(s) follow it.
        !          5192:  * ======================================================================= */
        !          5193: /* --- entry point --- */
        !          5194: subraster *rastdispmath ( char **expression, int size, subraster *sp )
        !          5195: {
        !          5196: /* -------------------------------------------------------------------------
        !          5197: Allocations and Declarations
        !          5198: -------------------------------------------------------------------------- */
        !          5199: char	*texscripts(),			/* parse expression for scripts */
        !          5200: 	subscript[512], supscript[512];	/* scripts parsed from expression */
        !          5201: int	issub=0, issup=0;		/* true if we have sub,sup */
        !          5202: subraster *rasterize(), *subsp=NULL, *supsp=NULL, /* rasterize scripts */
        !          5203: 	*rastack(),			/* stack operator with scripts */
        !          5204: 	*new_subraster();		/* for dummy base sp, if needed */
        !          5205: int	vspace = 1;			/* vertical space between scripts */
        !          5206: /* -------------------------------------------------------------------------
        !          5207: Obtain subscript and/or superscript expressions, and rasterize them/it
        !          5208: -------------------------------------------------------------------------- */
        !          5209: /* --- parse for sub,superscript(s), and bump expression past it(them) --- */
        !          5210: if ( expression == NULL ) goto end_of_job; /* no *ptr given */
        !          5211: if ( *expression == NULL ) goto end_of_job; /* no expression given */
        !          5212: if ( *(*expression) == '\000' ) goto end_of_job; /* nothing in expression */
        !          5213: *expression = texscripts(*expression,subscript,supscript,3);
        !          5214: /* --- rasterize scripts --- */
        !          5215: if ( *subscript != '\000' )		/* have a subscript */
        !          5216:   subsp = rasterize(subscript,size-1);	/* so rasterize it at size-1 */
        !          5217: if ( *supscript != '\000' )		/* have a superscript */
        !          5218:   supsp = rasterize(supscript,size-1);	/* so rasterize it at size-1 */
        !          5219: /* --- set flags for convenience --- */
        !          5220: issub  = (subsp != (subraster *)NULL);	/* true if we have subscript */
        !          5221: issup  = (supsp != (subraster *)NULL);	/* true if we have superscript */
        !          5222: if (!issub && !issup) goto end_of_job;	/*return operator alone if neither*/
        !          5223: /* -------------------------------------------------------------------------
        !          5224: stack operator and its script(s)
        !          5225: -------------------------------------------------------------------------- */
        !          5226: /* --- stack superscript atop operator --- */
        !          5227: if ( issup )				/* we have a superscript */
        !          5228:  if ( sp == NULL )			/* but no base expression */
        !          5229:   sp = supsp;				/* so just use superscript */
        !          5230:  else					/* have base and superscript */
        !          5231:   if ( (sp=rastack(sp,supsp,1,vspace,1,3)) /* stack supsp atop base sp */
        !          5232:   ==   NULL ) goto end_of_job;		/* and quit if failed */
        !          5233: /* --- stack operator+superscript atop subscript --- */
        !          5234: if ( issub )				/* we have a subscript */
        !          5235:  if ( sp == NULL )			/* but no base expression */
        !          5236:   sp = subsp;				/* so just use subscript */
        !          5237:  else					/* have base and subscript */
        !          5238:   if ( (sp=rastack(subsp,sp,2,vspace,1,3)) /* stack sp atop base subsp */
        !          5239:   ==   NULL ) goto end_of_job;		/* and quit if failed */
        !          5240: sp->type = IMAGERASTER;			/* flip type of composite object */
        !          5241: sp->size = size;			/* and set font size */
        !          5242: /* -------------------------------------------------------------------------
        !          5243: free unneeded component subrasters and return final result to caller
        !          5244: -------------------------------------------------------------------------- */
        !          5245: end_of_job:
        !          5246:   return ( sp );
        !          5247: } /* --- end-of-function rastdispmath() --- */
        !          5248: 
        !          5249: 
        !          5250: /* ==========================================================================
        !          5251:  * Function:	rastleft ( expression, size, basesp, ildelim, arg2, arg3 )
        !          5252:  * Purpose:	\left...\right handler, returns a subraster corresponding to
        !          5253:  *		delimited subexpression at font size
        !          5254:  * --------------------------------------------------------------------------
        !          5255:  * Arguments:	expression (I)	char **  to first char of null-terminated
        !          5256:  *				string beginning with a \left
        !          5257:  *				to be rasterized
        !          5258:  *		size (I)	int containing 0-5 default font size
        !          5259:  *		basesp (I)	subraster *  to character (or subexpression)
        !          5260:  *				immediately preceding leading left{
        !          5261:  *				(unused, but passed for consistency)
        !          5262:  *		ildelim (I)	int containing ldelims index of
        !          5263:  *				left delimiter
        !          5264:  *		arg2 (I)	int unused
        !          5265:  *		arg3 (I)	int unused
        !          5266:  * --------------------------------------------------------------------------
        !          5267:  * Returns:	( subraster * )	ptr to subraster corresponding to subexpr,
        !          5268:  *				or NULL for any parsing error
        !          5269:  * --------------------------------------------------------------------------
        !          5270:  * Notes:     o
        !          5271:  * ======================================================================= */
        !          5272: /* --- entry point --- */
        !          5273: subraster *rastleft ( char **expression, int size, subraster *basesp,
        !          5274: 			int ildelim, int arg2, int arg3 )
        !          5275: {
        !          5276: /* -------------------------------------------------------------------------
        !          5277: Allocations and Declarations
        !          5278: -------------------------------------------------------------------------- */
        !          5279: subraster *rasterize(), *sp=NULL;	/*rasterize between \left...\right*/
        !          5280: subraster *get_delim(), *lp=NULL, *rp=NULL; /* left and right delim chars */
        !          5281: subraster *rastlimits();		/*handle sub/super scripts on lp,rp*/
        !          5282: subraster *rastcat();			/* concat lp||sp||rp subrasters */
        !          5283: int	family=CMSYEX,			/* get_delim() family */
        !          5284: 	height=0, rheight=0,		/* subexpr, right delim height */
        !          5285: 	margin=(size+1),		/* delim height margin over subexpr*/
        !          5286: 	opmargin=(5);			/* extra margin for \int,\sum,\etc */
        !          5287: char	subexpr[8192];			/* chars between \left...\right */
        !          5288: char	*texchar(),			/* get delims after \left,\right */
        !          5289: 	ldelim[256]=".", rdelim[256]="."; /* delims following \left,\right */
        !          5290: char	*strtexchr(), *pleft, *pright;	/*locate \right matching our \left*/
        !          5291: int	isleftdot=0, isrightdot=0;	/* true if \left. or \right. */
        !          5292: int	sublen=0;			/* strlen(subexpr) */
        !          5293: int	idelim=0;			/* 1=left,2=right */
        !          5294: int	delete_subraster();		/* free subraster if rastleft fails*/
        !          5295: int	wasdisplaystyle = isdisplaystyle; /* save current displaystyle */
        !          5296: /* --- recognized delimiters --- */
        !          5297: static	char left[16]="\\left", right[16]="\\right"; /* tex delimiters */
        !          5298: static	char *ldelims[] = {
        !          5299:    "unused", ".",			/* 1   for \left., \right. */
        !          5300: 	"(", ")",			/* 2,3 for \left(, \right) */
        !          5301: 	"\\{","\\}",			/* 4,5 for \left\{, \right\} */
        !          5302: 	"[", "]",			/* 6,7 for \left[, \right] */
        !          5303: 	"<", ">",			/* 8,9 for \left<, \right> */
        !          5304: 	"|", "\\|",			/* 10,11 for \left,\right |,\|*/
        !          5305: 	NULL };
        !          5306: /* --- recognized operator delimiters --- */
        !          5307: static	char *opdelims[] = {		/* operator delims from cmex10 */
        !          5308:      "int",	  "sum",	"prod",
        !          5309:      "cup",	  "cap",	"dot",
        !          5310:      "plus",	  "times",	"wedge",
        !          5311:      "vee",
        !          5312:      NULL }; /* --- end-of-opdelims[] --- */
        !          5313: /* --- delimiter xlation --- */
        !          5314: static	char *xfrom[] =			/* xlate any delim suffix... */
        !          5315:    { "\\|",				/* \| */
        !          5316:      "\\{",				/* \{ */
        !          5317:      "\\}",				/* \} */
        !          5318:      "\\lbrace",			/* \lbrace */
        !          5319:      "\\rbrace",			/* \rbrace */
        !          5320:      "\\langle",			/* \langle */
        !          5321:      "\\rangle",			/* \rangle */
        !          5322:      NULL } ; /* --- end-of-xfrom[] --- */
        !          5323: static	char *xto[] =			/* ...to this instead */
        !          5324:    { "=",				/* \| to = */
        !          5325:      "{",				/* \{ to { */
        !          5326:      "}",				/* \} to } */
        !          5327:      "{",				/* \lbrace to { */
        !          5328:      "}",				/* \rbrace to } */
        !          5329:      "<",				/* \langle to < */
        !          5330:      ">",				/* \rangle to > */
        !          5331:      NULL } ; /* --- end-of-xto[] --- */
        !          5332: /* -------------------------------------------------------------------------
        !          5333: initialization
        !          5334: -------------------------------------------------------------------------- */
        !          5335: /* --- check args --- */
        !          5336: if ( *(*expression) == '\000' ) goto end_of_job; /* nothing after \left */
        !          5337: /* --- determine left delimiter, and set default \right. delimiter --- */
        !          5338: if ( ildelim!=NOVALUE && ildelim>=1 )	/* called with explicit left delim */
        !          5339:   strcpy(ldelim,ldelims[ildelim]);	/* so just get a local copy */
        !          5340: else					/* trapped \left without delim */
        !          5341:  { skipwhite(*expression);		/* interpret \left ( as \left( */
        !          5342:    *expression = texchar(*expression,ldelim); } /*pull delim from expression*/
        !          5343: strcpy(rdelim,".");			/* init default \right. delim */
        !          5344: /* -------------------------------------------------------------------------
        !          5345: locate \right balancing our opening \left
        !          5346: -------------------------------------------------------------------------- */
        !          5347: /* --- first \right following \left --- */
        !          5348: if ( (pright=strtexchr(*expression,right)) /* look for \right after \left */
        !          5349: !=   NULL ) {				/* found it */
        !          5350:  /* --- find matching \right by pushing past any nested \left's --- */
        !          5351:  pleft = *expression;			/* start after first \left( */
        !          5352:  while ( 1 ) {				/*break when matching \right found*/
        !          5353:   /* -- locate next nested \left if there is one --- */
        !          5354:   if ( (pleft=strtexchr(pleft,left))	/* find next \left */
        !          5355:   ==   NULL ) break;			/*no more, so matching \right found*/
        !          5356:   pleft += strlen(left);		/* push ptr past \left token */
        !          5357:   if ( pleft >= pright ) break;		/* not nested if \left after \right*/
        !          5358:   /* --- have nested \left, so push forward to next \right --- */
        !          5359:   if ( (pright=strtexchr(pright+strlen(right),right)) /* find next \right */
        !          5360:   ==   NULL ) break;			/* ran out of \right's */
        !          5361:   } /* --- end-of-while(1) --- */
        !          5362:  } /* --- end-of-if(pright!=NULL) --- */
        !          5363: /* -------------------------------------------------------------------------
        !          5364: push past \left(_a^b sub/superscripts, if present
        !          5365: -------------------------------------------------------------------------- */
        !          5366: pleft = *expression;			/*reset pleft after opening \left( */
        !          5367: /*lp=*/ rastlimits(expression,size,lp);	/*dummy call push expression past b*/
        !          5368: /* -------------------------------------------------------------------------
        !          5369: get \right delimiter and subexpression between \left...\right, xlate delims
        !          5370: -------------------------------------------------------------------------- */
        !          5371: /* --- get delimiter following \right --- */
        !          5372: if ( pright == (char *)NULL ) {		/* assume \right. at end of exprssn*/
        !          5373:   strcpy(rdelim,".");			/* set default \right. */
        !          5374:   sublen = strlen(*expression);		/* use entire remaining expression */
        !          5375:   memcpy(subexpr,*expression,sublen);	/* copy all remaining chars */
        !          5376:   *expression += sublen; }		/* and push expression to its null */
        !          5377: else {					/* have explicit matching \right */
        !          5378:   sublen = (int)(pright-(*expression));	/* #chars between \left...\right */
        !          5379:   memcpy(subexpr,*expression,sublen);	/* copy chars preceding \right */
        !          5380:   *expression = pright+strlen(right);	/* push expression past \right */
        !          5381:   skipwhite(*expression);		/* interpret \right ) as \right) */
        !          5382:   *expression = texchar(*expression,rdelim); /*pull delim from expression*/
        !          5383:   if ( *rdelim == '\000' ) strcpy(rdelim,"."); } /* \right. if no rdelim */
        !          5384: /* --- get subexpression between \left...\right --- */
        !          5385: if ( sublen < 1 ) goto end_of_job;	/* nothing between delimiters */
        !          5386: subexpr[sublen] = '\000';		/* and null-terminate it */
        !          5387: /* --- check for operator delimiter --- */
        !          5388: for ( idelim=0; opdelims[idelim]!=NULL; idelim++ )
        !          5389:   if ( strstr(ldelim,opdelims[idelim]) != NULL ) /* found operator */
        !          5390:     { margin += opmargin;		/* extra height for operator */
        !          5391:       if ( *ldelim == '\\' )		/* have leading escape */
        !          5392: 	strcpy(ldelim,ldelim+1);	/* squeeze it out */
        !          5393:       break; }				/* no need to check rest of table */
        !          5394: /* --- xlate delimiters --- */
        !          5395: for ( idelim=1; idelim<=2; idelim++ ) {	/* 1=left, 2=right */
        !          5396:   char	*lrdelim  = (idelim==1? ldelim:rdelim); /* ldelim or rdelim */
        !          5397:   int	ix;  char *xdelim;		/* xfrom[] and xto[] index, delim */
        !          5398:   for( ix=0; (xdelim=xfrom[ix]) != NULL; ix++ )
        !          5399:     if ( strcmp(lrdelim,xdelim) == 0 )	/* found delim to xlate */
        !          5400:       {	strcpy(lrdelim,xto[ix]);	/* replace with corresponding xto[]*/
        !          5401: 	break; }			/* no need to check further */
        !          5402:   } /* --- end-of-for(idelim) --- */
        !          5403: /* --- debugging --- */
        !          5404: if ( msgfp!=NULL && msglevel>=99 )
        !          5405:   fprintf(msgfp,"rastleft> left=\"%s\" right=\"%s\" subexpr=\"%s\"\n",
        !          5406:   ldelim,rdelim,subexpr);
        !          5407: /* -------------------------------------------------------------------------
        !          5408: rasterize subexpression
        !          5409: -------------------------------------------------------------------------- */
        !          5410: /* --- rasterize subexpression --- */
        !          5411: if ( (sp = rasterize(subexpr,size))	/* rasterize chars between delims */
        !          5412: ==   NULL ) goto end_of_job;		/* quit if failed */
        !          5413: height = (sp->image)->height;		/* height of subexpr raster */
        !          5414: rheight = height+margin;		/*default rheight as subexpr height*/
        !          5415: /* -------------------------------------------------------------------------
        !          5416: rasterize delimiters, reset baselines, and add  sub/superscripts if present
        !          5417: -------------------------------------------------------------------------- */
        !          5418: /* --- check for dot delimiter --- */
        !          5419: isleftdot  = (strchr(ldelim,'.')!=NULL); /* true if \left. */
        !          5420: isrightdot = (strchr(rdelim,'.')!=NULL); /* true if \right. */
        !          5421: /* --- get rasters for best-fit delim characters, add sub/superscripts --- */
        !          5422: isdisplaystyle = 9;			/* force \displaystyle */
        !          5423: if ( !isleftdot )			/* if not \left. */
        !          5424:  { /* --- first get requested \left delimiter --- */
        !          5425:    lp = get_delim(ldelim,rheight,family); /* get \left delim char */
        !          5426:    /* --- reset lp delim baseline to center delim on subexpr raster --- */
        !          5427:    if ( lp != NULL )			/* if get_delim() succeeded */
        !          5428:     { int lheight = (lp->image)->height; /* actual height of left delim */
        !          5429:       lp->baseline = sp->baseline + (lheight - height)/2;
        !          5430:       if ( lheight > rheight )		/* got bigger delim than requested */
        !          5431: 	rheight = lheight-1; }		/* make sure right delim matches */
        !          5432:    /* --- then add on any sub/superscripts attached to \left( --- */
        !          5433:    lp = rastlimits(&pleft,size,lp); }	/*\left(_a^b and push pleft past b*/
        !          5434: isdisplaystyle = 9;			/* force \displaystyle */
        !          5435: if ( !isrightdot )			/* and if not \right. */
        !          5436:  { /* --- first get requested \right delimiter --- */
        !          5437:    rp = get_delim(rdelim,rheight,family); /* get \right delim char */
        !          5438:    /* --- reset rp delim baseline to center delim on subexpr raster --- */
        !          5439:    if ( rp != NULL )			/* if get_delim() succeeded */
        !          5440:      rp->baseline = sp->baseline + ((rp->image)->height - height)/2;
        !          5441:    /* --- then add on any sub/superscripts attached to \right) --- */
        !          5442:    rp = rastlimits(expression,size,rp); } /*\right)_c^d, expression past d*/
        !          5443: isdisplaystyle = wasdisplaystyle;	/* original \displystyle default */
        !          5444: /* --- check that we got delimiters --- */
        !          5445: if ( 0 )
        !          5446:  if ( (lp==NULL && !isleftdot)		/* check that we got left( */
        !          5447:  ||   (rp==NULL && !isrightdot) )	/* and right) if needed */
        !          5448:   { if ( lp != NULL ) free ((void *)lp); /* free \left-delim subraster */
        !          5449:     if ( rp != NULL ) free ((void *)rp); /* and \right-delim subraster */
        !          5450:     if (0) { delete_subraster(sp);	/* if failed, free subraster */
        !          5451:              sp = (subraster *)NULL; }	/* signal error to caller */
        !          5452:     goto end_of_job; }			/* and quit */
        !          5453: /* -------------------------------------------------------------------------
        !          5454: concat  lp || sp || rp  components
        !          5455: -------------------------------------------------------------------------- */
        !          5456: /* --- concat lp||sp||rp to obtain final result --- */
        !          5457: if ( lp != NULL )			/* ignore \left. */
        !          5458:   sp = rastcat(lp,sp,3);		/* concat lp||sp and free sp,lp */
        !          5459: if ( sp != NULL )			/* succeeded or ignored \left. */
        !          5460:   if ( rp != NULL )			/* ignore \right. */
        !          5461:     sp = rastcat(sp,rp,3);		/* concat sp||rp and free sp,rp */
        !          5462: /* --- back to caller --- */
        !          5463: end_of_job:
        !          5464:   return ( sp );
        !          5465: } /* --- end-of-function rastleft() --- */
        !          5466: 
        !          5467: 
        !          5468: /* ==========================================================================
        !          5469:  * Function:	rastflags ( expression, size, basesp,  flag, value, arg3 )
        !          5470:  * Purpose:	sets an internal flag, e.g., for \rm, or sets an internal
        !          5471:  *		value, e.g., for \unitlength=<value>, and returns NULL
        !          5472:  *		so nothing is displayed
        !          5473:  * --------------------------------------------------------------------------
        !          5474:  * Arguments:	expression (I)	char **  to first char of null-terminated
        !          5475:  *				LaTeX expression (unused/unchanged)
        !          5476:  *		size (I)	int containing base font size (not used,
        !          5477:  *				just stored in subraster)
        !          5478:  *		basesp (I)	subraster *  to character (or subexpression)
        !          5479:  *				immediately preceding space, whose baseline
        !          5480:  *				and height params are transferred to space
        !          5481:  *		flag (I)	int containing #define'd symbol specifying
        !          5482:  *				internal flag to be set
        !          5483:  *		value (I)	int containing new value of flag
        !          5484:  *		arg3 (I)	int unused
        !          5485:  * --------------------------------------------------------------------------
        !          5486:  * Returns:	( subraster * )	NULL so nothing is displayed
        !          5487:  * --------------------------------------------------------------------------
        !          5488:  * Notes:     o
        !          5489:  * ======================================================================= */
        !          5490: /* --- entry point --- */
        !          5491: subraster *rastflags ( char **expression, int size, subraster *basesp,
        !          5492: 			int flag, int value, int arg3 )
        !          5493: {
        !          5494: /* -------------------------------------------------------------------------
        !          5495: Allocations and Declarations
        !          5496: -------------------------------------------------------------------------- */
        !          5497: char	*texsubexpr(),			/* parse expression for... */
        !          5498: 	valuearg[1024]="NOVALUE";	/* value from expression, if needed */
        !          5499: int	argvalue=NOVALUE,		/* atoi(valuearg) */
        !          5500: 	isdelta=0,			/* true if + or - precedes valuearg */
        !          5501: 	valuelen=0;			/* strlen(valuearg) */
        !          5502: double	strtod();			/*convert ascii {valuearg} to double*/
        !          5503: static	int displaystylelevel = (-99);	/* \displaystyle set at recurlevel */
        !          5504: /* -------------------------------------------------------------------------
        !          5505: set flag or value
        !          5506: -------------------------------------------------------------------------- */
        !          5507: switch ( flag )
        !          5508:   {
        !          5509:   default: break;			/* unrecognized flag */
        !          5510:   case ISTEXT:
        !          5511:     if ( isthischar((*(*expression)),WHITEMATH) ) /* \rm followed by white */
        !          5512:       (*expression)++;			/* skip leading ~ after \rm */
        !          5513:     istext=value;			/* set text mode */
        !          5514:     break;
        !          5515:   case ISSTRING: isstring=value; break;	/* set string/image mode */
        !          5516:   case ISDISPLAYSTYLE:			/* set \displaystyle mode */
        !          5517:     displaystylelevel = recurlevel;	/* \displaystyle set at recurlevel */
        !          5518:     isdisplaystyle=value; break;
        !          5519:   case ISOPAQUE:  istransparent=value; break; /* set transparent/opaque */
        !          5520:   case ISREVERSE:			/* reverse video */
        !          5521:     if ( value==1 || value==NOVALUE )
        !          5522:       {	fgred=255-fgred; fggreen=255-fggreen; fgblue=255-fgblue; }
        !          5523:     if ( value==2 || value==NOVALUE )
        !          5524:       {	bgred=255-bgred; bggreen=255-bggreen; bgblue=255-bgblue; }
        !          5525:     if ( value==2 || value==NOVALUE )
        !          5526:       isblackonwhite = !isblackonwhite;
        !          5527:     break;
        !          5528:   case ISSUPER:				/* set supersampling/lowpass flag */
        !          5529:     #ifndef SSFONTS			/* don't have ss fonts loaded */
        !          5530:       value = 0;			/* so force lowpass */
        !          5531:     #endif
        !          5532:     isss = issupersampling = value;
        !          5533:     fonttable = (issupersampling?ssfonttable:aafonttable); /* set fonts */
        !          5534:     break;
        !          5535:   case ISFONTSIZE:			/* set fontsize */
        !          5536:   case ISDISPLAYSIZE:			/* set displaysize */
        !          5537:   case ISSHRINK:			/* set shrinkfactor */
        !          5538:   case ISAAALGORITHM:			/* set anti-aliasing algorithm */
        !          5539:   case ISWEIGHT:			/* set font weight */
        !          5540:   case ISCENTERWT:			/* set lowpass center pixel weight */
        !          5541:   case ISADJACENTWT:			/* set lowpass adjacent weight */
        !          5542:   case ISCORNERWT:			/* set lowpass corner weight */
        !          5543:   case ISCOLOR:				/* set red(1),green(2),blue(3) */
        !          5544:   case ISSQUASH:			/* set (minimum) "squash" margin */
        !          5545:     if ( value != NOVALUE )		/* passed a fixed value to be set */
        !          5546:       argvalue = value;			/* set given fixed value */
        !          5547:     else				/* get value from expression */
        !          5548:       {	*expression = texsubexpr(*expression,valuearg,1023,"{","}",0,0);
        !          5549: 	if ( *valuearg != '\000' )	/* guard against empty string */
        !          5550: 	 if ( !isalpha(*valuearg) )	/* and against alpha string args */
        !          5551: 	  if ( !isthischar(*valuearg,"?") ) /*leading ? is query for value*/
        !          5552: 	   { isdelta = isthischar(*valuearg,"+-"); /* leading + or - */
        !          5553: 	     if ( memcmp(valuearg,"--",2) == 0 ) /* leading -- signals...*/
        !          5554: 	       { isdelta=0; strcpy(valuearg,valuearg+1); } /* ...not delta */
        !          5555: 	     argvalue = atoi(valuearg); } } /* convert to int */
        !          5556:     switch ( flag )
        !          5557:       {
        !          5558:       default: break;
        !          5559:       case ISCOLOR:			/* set color */
        !          5560: 	slower(valuearg);		/* convert arg to lower case */
        !          5561: 	if ( argvalue==1 || strstr(valuearg,"red") )
        !          5562: 	  { fggreen = fgblue = (isblackonwhite?0:255);
        !          5563: 	    fgred = (isblackonwhite?255:0); }
        !          5564: 	if ( argvalue==2 || strstr(valuearg,"green") )
        !          5565: 	  { fgred = fgblue = (isblackonwhite?0:255);
        !          5566: 	    fggreen = (isblackonwhite?255:0); }
        !          5567: 	if ( argvalue==3 || strstr(valuearg,"blue") )
        !          5568: 	  { fgred = fggreen = (isblackonwhite?0:255);
        !          5569: 	    fgblue = (isblackonwhite?255:0); }
        !          5570: 	if ( argvalue==0 || strstr(valuearg,"black") )
        !          5571: 	    fgred = fggreen = fgblue = (isblackonwhite?0:255);
        !          5572: 	if ( argvalue==7 || strstr(valuearg,"white") )
        !          5573: 	    fgred = fggreen = fgblue = (isblackonwhite?255:0);
        !          5574: 	break;
        !          5575:       case ISFONTSIZE:			/* set fontsize */
        !          5576: 	if ( argvalue != NOVALUE )	/* got a value */
        !          5577: 	  { int largestsize = (issupersampling?16:LARGESTSIZE);
        !          5578: 	    fontsize = (isdelta? fontsize+argvalue : argvalue);
        !          5579: 	    fontsize = max2(0,min2(fontsize,largestsize));
        !          5580: 	    shrinkfactor = shrinkfactors[fontsize];
        !          5581: 	    if ( isdisplaystyle == 1 )	/* displaystyle enabled but not set*/
        !          5582: 	     if ( displaystylelevel != recurlevel ) /*respect \displaystyle*/
        !          5583: 	      if ( !ispreambledollars )	/* respect $$...$$'s */
        !          5584: 	       isdisplaystyle = (fontsize>=displaysize? 2:1); /* forced */
        !          5585: 	    /*displaystylelevel = (-99);*/ } /* reset \displaystyle level */
        !          5586: 	else				/* embed font size in expression */
        !          5587: 	  { sprintf(valuearg,"%d",fontsize); /* convert size */
        !          5588: 	    valuelen = strlen(valuearg); /* ought to be 1 */
        !          5589: 	    if ( *expression != '\000' ) /* ill-formed expression */
        !          5590: 	     { *expression = (char *)(*expression-valuelen); /*back up buff*/
        !          5591: 	       memcpy(*expression,valuearg,valuelen); } } /*and put in size*/
        !          5592: 	break;
        !          5593:       case ISDISPLAYSIZE:		/* set displaysize */
        !          5594: 	if ( argvalue != NOVALUE )	/* got a value */
        !          5595: 	    displaysize = (isdelta? displaysize+argvalue : argvalue);
        !          5596: 	break;
        !          5597:       case ISSQUASH:			/* set (minimum) "squash" margin */
        !          5598: 	if ( argvalue != NOVALUE )	/* got a value */
        !          5599: 	  { squashmargin = argvalue;	/* set value */
        !          5600: 	    if ( arg3 != NOVALUE ) isdelta=arg3; /* hard-coded isdelta */
        !          5601: 	    issquashdelta = (isdelta?1:0); } /* and set delta flag */
        !          5602: 	squashmargin = max2((isdelta?-5:0),min2(squashmargin,32)); /*sanity*/
        !          5603: 	break;
        !          5604:       case ISSHRINK:			/* set shrinkfactor */
        !          5605: 	if ( argvalue != NOVALUE )	/* got a value */
        !          5606: 	  shrinkfactor = (isdelta? shrinkfactor+argvalue : argvalue);
        !          5607: 	shrinkfactor = max2(1,min2(shrinkfactor,27)); /* sanity check */
        !          5608: 	break;
        !          5609:       case ISAAALGORITHM:		/* set anti-aliasing algorithm */
        !          5610: 	if ( argvalue != NOVALUE )	/* got a value */
        !          5611: 	  aaalgorithm = argvalue;	/* set algorithm number */
        !          5612: 	aaalgorithm = max2(0,min2(aaalgorithm,3)); /* bounds check */
        !          5613: 	break;
        !          5614:       case ISWEIGHT:			/* set font weight number */
        !          5615: 	value =	(argvalue==NOVALUE? NOVALUE : /* don't have a value */
        !          5616: 		(isdelta? weightnum+argvalue : argvalue));
        !          5617: 	if ( value>=0 && value<maxaaparams ) /* in range */
        !          5618: 	  { weightnum   = value;	/* reset weightnum index */
        !          5619: 	    minadjacent = aaparams[weightnum].minadjacent;
        !          5620: 	    maxadjacent = aaparams[weightnum].maxadjacent;
        !          5621: 	    cornerwt    = aaparams[weightnum].cornerwt;
        !          5622: 	    adjacentwt  = aaparams[weightnum].adjacentwt;
        !          5623: 	    centerwt    = aaparams[weightnum].centerwt;
        !          5624: 	    fgalias     = aaparams[weightnum].fgalias;
        !          5625: 	    fgonly      = aaparams[weightnum].fgonly;
        !          5626: 	    bgalias     = aaparams[weightnum].bgalias;
        !          5627: 	    bgonly      = aaparams[weightnum].bgonly; }
        !          5628: 	break;
        !          5629:       case ISCENTERWT:			/* set lowpass center pixel weight */
        !          5630: 	if ( argvalue != NOVALUE )	/* got a value */
        !          5631: 	  centerwt = argvalue;		/* set lowpass center weight */
        !          5632: 	break;
        !          5633:       case ISADJACENTWT:		/* set lowpass adjacent weight */
        !          5634: 	if ( argvalue != NOVALUE )	/* got a value */
        !          5635: 	  adjacentwt = argvalue;	/* set lowpass adjacent weight */
        !          5636: 	break;
        !          5637:       case ISCORNERWT:			/* set lowpass corner weight */
        !          5638: 	if ( argvalue != NOVALUE )	/* got a value */
        !          5639: 	  cornerwt = argvalue;		/* set lowpass corner weight */
        !          5640: 	break;
        !          5641:       } /* --- end-of-switch() --- */
        !          5642:     break;
        !          5643:   case PNMPARAMS:			/*set fgalias,fgonly,bgalias,bgonly*/
        !          5644:     *expression = texsubexpr(*expression,valuearg,1023,"{","}",0,0);
        !          5645:     valuelen = strlen(valuearg);	/* ought to be 1-4 */
        !          5646:     if ( valuelen>0 && isthischar(toupper(valuearg[0]),"TY1") ) fgalias=1;
        !          5647:     if ( valuelen>0 && isthischar(toupper(valuearg[0]),"FN0") ) fgalias=0;
        !          5648:     if ( valuelen>1 && isthischar(toupper(valuearg[1]),"TY1") ) fgonly =1;
        !          5649:     if ( valuelen>1 && isthischar(toupper(valuearg[1]),"FN0") ) fgonly =0;
        !          5650:     if ( valuelen>2 && isthischar(toupper(valuearg[2]),"TY1") ) bgalias=1;
        !          5651:     if ( valuelen>2 && isthischar(toupper(valuearg[2]),"FN0") ) bgalias=0;
        !          5652:     if ( valuelen>3 && isthischar(toupper(valuearg[3]),"TY1") ) bgonly =1;
        !          5653:     if ( valuelen>3 && isthischar(toupper(valuearg[3]),"FN0") ) bgonly =0;
        !          5654:     break;
        !          5655:   case UNITLENGTH:
        !          5656:     if ( value != NOVALUE )		/* passed a fixed value to be set */
        !          5657: 	unitlength = (double)(value);	/* set given fixed value */
        !          5658:     else				/* get value from expression */
        !          5659:       {	*expression = texsubexpr(*expression,valuearg,1023,"{","}",0,0);
        !          5660: 	if ( *valuearg != '\000' )	/* guard against empty string */
        !          5661: 	  unitlength = strtod(valuearg,NULL); } /* convert to double */
        !          5662:     break;
        !          5663:   } /* --- end-of-switch(flag) --- */
        !          5664: return ( NULL );			/*just set value, nothing to display*/
        !          5665: } /* --- end-of-function rastflags() --- */
        !          5666: 
        !          5667: 
        !          5668: /* ==========================================================================
        !          5669:  * Function:	rastspace(expression, size, basesp,  width, isfill, isheight)
        !          5670:  * Purpose:	returns a blank/space subraster width wide,
        !          5671:  *		with baseline and height corresponding to basep
        !          5672:  * --------------------------------------------------------------------------
        !          5673:  * Arguments:	expression (I)	char **  to first char of null-terminated
        !          5674:  *				LaTeX expression (unused/unchanged)
        !          5675:  *		size (I)	int containing base font size (not used,
        !          5676:  *				just stored in subraster)
        !          5677:  *		basesp (I)	subraster *  to character (or subexpression)
        !          5678:  *				immediately preceding space, whose baseline
        !          5679:  *				and height params are transferred to space
        !          5680:  *		width (I)	int containing #bits/pixels for space width
        !          5681:  *		isfill (I)	int containing true to \hfill complete
        !          5682:  *				expression out to width
        !          5683:  *		isheight (I)	int containing true (but not NOVALUE)
        !          5684:  *				to treat width arg as height
        !          5685:  * --------------------------------------------------------------------------
        !          5686:  * Returns:	( subraster * )	ptr to empty/blank subraster
        !          5687:  *				or NULL for any error
        !          5688:  * --------------------------------------------------------------------------
        !          5689:  * Notes:     o
        !          5690:  * ======================================================================= */
        !          5691: /* --- entry point --- */
        !          5692: subraster *rastspace ( char **expression, int size, subraster *basesp,
        !          5693: 			int width, int isfill, int isheight )
        !          5694: {
        !          5695: /* -------------------------------------------------------------------------
        !          5696: Allocations and Declarations
        !          5697: -------------------------------------------------------------------------- */
        !          5698: subraster *new_subraster(), *spacesp=NULL; /* subraster for space */
        !          5699: int	baseht=1, baseln=0;		/* height,baseline of base symbol */
        !          5700: int	pixsz = 1;			/*default #bits per pixel, 1=bitmap*/
        !          5701: char	*texsubexpr(), widtharg[256];	/* parse for optional {width} */
        !          5702: subraster *rasterize(), *rightsp=NULL;	/*rasterize right half of expression*/
        !          5703: subraster *rastcat();			/* cat rightsp after \hfill */
        !          5704: int	blanksignal = (-991234);	/*rastsquash signal right-hand blank*/
        !          5705: /* -------------------------------------------------------------------------
        !          5706: initialization
        !          5707: -------------------------------------------------------------------------- */
        !          5708: if ( isfill == NOVALUE ) isfill=0;	/* novalue means false */
        !          5709: if ( isheight == NOVALUE ) isheight=0;	/* novalue means false */
        !          5710: /* -------------------------------------------------------------------------
        !          5711: determine width if not given (e.g., \hspace{width}, \hfill{width})
        !          5712: -------------------------------------------------------------------------- */
        !          5713: if ( width <= 0 )			/* width specified in expression */
        !          5714:   { int widthval;			/* test {width} before using it */
        !          5715:     width = 1;				/* set default width */
        !          5716:     *expression = texsubexpr(*expression,widtharg,255,"{","}",0,0);
        !          5717:     widthval =				/* convert {width} to integer */
        !          5718: 		(int)((unitlength*strtod(widtharg,NULL))+0.5);
        !          5719:     if ( widthval>=2 && widthval<=600 )	/* sanity check */
        !          5720:       width = widthval; }		/* replace deafault width */
        !          5721: /* -------------------------------------------------------------------------
        !          5722: see if width is "absolute" or fill width
        !          5723: -------------------------------------------------------------------------- */
        !          5724: if ( isfill				/* called as \hfill{} */
        !          5725: &&   !isheight )			/* parameter conflict */
        !          5726:  { if ( leftexpression != NULL )	/* if we have left half */
        !          5727:     width -= (leftexpression->image)->width; /*reduce left width from total*/
        !          5728:    if ( (rightsp=rasterize(*expression,size)) /* rasterize right half */
        !          5729:    != NULL )				/* succeeded */
        !          5730:     width -= (rightsp->image)->width; } /* reduce right width from total */
        !          5731: /* -------------------------------------------------------------------------
        !          5732: construct blank subraster, and return it to caller
        !          5733: -------------------------------------------------------------------------- */
        !          5734: /* --- get parameters from base symbol --- */
        !          5735: if ( basesp != (subraster *)NULL )	/* we have base symbol for space */
        !          5736:   { baseht = (basesp->image)->height; 	/* height of base symbol */
        !          5737:     baseln =  basesp->baseline; }	/* and its baseline */
        !          5738: /* --- flip params for height --- */
        !          5739: if ( isheight )				/* width is actually height */
        !          5740:   { baseht = width;			/* use given width as height */
        !          5741:     width = 1; }			/* and set default width */
        !          5742: /* --- generate and init space subraster --- */
        !          5743: if ( width > 0 )			/*make sure we have positive width*/
        !          5744:  if ( (spacesp=new_subraster(width,baseht,pixsz)) /*generate space subraster*/
        !          5745:  !=   NULL )				/* and if we succeed... */
        !          5746:   { /* --- ...re-init subraster parameters --- */
        !          5747:     spacesp->size = size;		/*propagate base font size forward*/
        !          5748:     spacesp->baseline = baseln; }	/* ditto baseline */
        !          5749: /* -------------------------------------------------------------------------
        !          5750: concat right half if \hfill-ing
        !          5751: -------------------------------------------------------------------------- */
        !          5752: if ( rightsp != NULL )			/* we have a right half after fill */
        !          5753:   { spacesp = (spacesp==NULL? rightsp:	/* no space, so just use right half*/
        !          5754: 	rastcat(spacesp,rightsp,3));	/* or cat right half after space */
        !          5755:     spacesp->type = blanksignal;	/* need to propagate blanks */
        !          5756:     *expression += strlen((*expression)); } /* push expression to its null */
        !          5757: return ( spacesp );
        !          5758: } /* --- end-of-function rastspace() --- */
        !          5759: 
        !          5760: 
        !          5761: /* ==========================================================================
        !          5762:  * Function:	rastnewline ( expression, size, basesp,  arg1, arg2, arg3 )
        !          5763:  * Purpose:	\\ handler, returns subraster corresponding to
        !          5764:  *		left-hand expression preceding \\ above right-hand expression
        !          5765:  * --------------------------------------------------------------------------
        !          5766:  * Arguments:	expression (I/O) char **  to first char of null-terminated
        !          5767:  *				string immediately following \\ to be
        !          5768:  *				rasterized, and returning ptr immediately
        !          5769:  *				to terminating null.
        !          5770:  *		size (I)	int containing 0-4 default font size
        !          5771:  *		basesp (I)	subraster *  to character (or subexpression)
        !          5772:  *				immediately preceding \accent
        !          5773:  *				(unused, but passed for consistency)
        !          5774:  *		arg1 (I)	int unused
        !          5775:  *		arg2 (I)	int unused
        !          5776:  *		arg3 (I)	int unused
        !          5777:  * --------------------------------------------------------------------------
        !          5778:  * Returns:	( subraster * )	ptr to subraster corresponding to expression,
        !          5779:  *				or NULL for any parsing error
        !          5780:  *				(expression ptr unchanged if error occurs)
        !          5781:  * --------------------------------------------------------------------------
        !          5782:  * Notes:     o
        !          5783:  * ======================================================================= */
        !          5784: /* --- entry point --- */
        !          5785: subraster *rastnewline ( char **expression, int size, subraster *basesp,
        !          5786: 			int arg1, int arg2, int arg3 )
        !          5787: {
        !          5788: /* -------------------------------------------------------------------------
        !          5789: Allocations and Declarations
        !          5790: -------------------------------------------------------------------------- */
        !          5791: subraster *rastack(), *newlsp=NULL;	/* subraster for both lines */
        !          5792: subraster *rasterize(), *rightsp=NULL;	/*rasterize right half of expression*/
        !          5793: char	*texsubexpr(), spacexpr[129]/*, *xptr=spacexpr*/; /*for \\[vspace]*/
        !          5794: double	strtod();			/* convert ascii param to double */
        !          5795: int	vspace = size+2;		/* #pixels between lines */
        !          5796: /* -------------------------------------------------------------------------
        !          5797: obtain optional [vspace] argument immediately following \\ command
        !          5798: -------------------------------------------------------------------------- */
        !          5799: /* --- check if [vspace] given --- */
        !          5800: if ( *(*expression) == '[' )		/*have [vspace] if leading char is [*/
        !          5801:   {
        !          5802:   /* ---parse [vspace] and bump expression past it, interpret as double--- */
        !          5803:   *expression = texsubexpr(*expression,spacexpr,127,"[","]",0,0);
        !          5804:   if ( *spacexpr == '\000' ) goto end_of_job; /* couldn't get [vspace] */
        !          5805:   vspace = iround(unitlength*strtod(spacexpr,NULL)); /* vspace in pixels */
        !          5806:   } /* --- end-of-if(*(*expression)=='[') --- */
        !          5807: if ( leftexpression == NULL ) goto end_of_job; /* nothing preceding \\ */
        !          5808: /* -------------------------------------------------------------------------
        !          5809: rasterize right half of expression and stack left half above it
        !          5810: -------------------------------------------------------------------------- */
        !          5811: /* --- rasterize right half --- */
        !          5812: if ( (rightsp=rasterize(*expression,size)) /* rasterize right half */
        !          5813: == NULL ) goto end_of_job;		/* quit if failed */
        !          5814: /* --- stack left half above it --- */
        !          5815: newlsp = rastack(rightsp,leftexpression,1,vspace,0,3); /*right under left*/
        !          5816: /* --- back to caller --- */
        !          5817: end_of_job:
        !          5818:   if ( newlsp != NULL )			/* returning entire expression */
        !          5819:     { int newht = (newlsp->image)->height; /* height of returned subraster */
        !          5820:       newlsp->baseline = min2(newht-1,newht/2+5); /* guess new baseline */
        !          5821:       isreplaceleft = 1;		/* so set flag to replace left half*/
        !          5822:       *expression += strlen(*expression); } /* and push to terminating null*/
        !          5823:   return ( newlsp );			/* 1st line over 2nd, or null=error*/
        !          5824: } /* --- end-of-function rastnewline() --- */
        !          5825: 
        !          5826: 
        !          5827: /* ==========================================================================
        !          5828:  * Function:	rastarrow ( expression, size, basesp,  drctn, isBig, arg3 )
        !          5829:  * Purpose:	returns left/right arrow subraster (e.g., for \longrightarrow)
        !          5830:  * --------------------------------------------------------------------------
        !          5831:  * Arguments:	expression (I)	char **  to first char of null-terminated
        !          5832:  *				LaTeX expression (unused/unchanged)
        !          5833:  *		size (I)	int containing base font size (not used,
        !          5834:  *				just stored in subraster)
        !          5835:  *		basesp (I)	subraster *  to character (or subexpression)
        !          5836:  *				immediately preceding space, whose baseline
        !          5837:  *				and height params are transferred to space
        !          5838:  *		drctn (I)	int containing +1 for right, -1 for left,
        !          5839:  *				or 0 for leftright
        !          5840:  *		isBig (I)	int containing 0 for ---> or 1 for ===>
        !          5841:  *		arg3 (I)	int unused
        !          5842:  * --------------------------------------------------------------------------
        !          5843:  * Returns:	( subraster * )	ptr to left/right arrow subraster
        !          5844:  *				or NULL for any error
        !          5845:  * --------------------------------------------------------------------------
        !          5846:  * Notes:     o	An optional argument [width] may *immediately* follow
        !          5847:  *		the \longxxx to explicitly set the arrow's width in pixels.
        !          5848:  *		For example, \longrightarrow calculates a default width
        !          5849:  *		(as usual in LaTeX), whereas \longrightarrow[50] explicitly
        !          5850:  *		draws a 50-pixel long arrow.  This can be used, e.g.,
        !          5851:  *		to draw commutative diagrams in conjunction with
        !          5852:  *		\array (and maybe with \stackrel and/or \relstack, too).
        !          5853:  *	      o	In case you really want to render, say, [f]---->[g], just
        !          5854:  *		use an intervening space, i.e., [f]\longrightarrow~[g].
        !          5855:  *		In text mode use two spaces {\rm~[f]\longrightarrow~~[g]}.
        !          5856:  * ======================================================================= */
        !          5857: /* --- entry point --- */
        !          5858: subraster *rastarrow ( char **expression, int size, subraster *basesp,
        !          5859: 			int drctn, int isBig, int arg3 )
        !          5860: {
        !          5861: /* -------------------------------------------------------------------------
        !          5862: Allocations and Declarations
        !          5863: -------------------------------------------------------------------------- */
        !          5864: subraster *arrow_subraster(), *arrowsp=NULL; /* subraster for arrow */
        !          5865: char	*texsubexpr(), widtharg[256];	/* parse for optional [width] */
        !          5866: char	*texscripts(), sub[1024],super[1024]; /* and _^limits after [width]*/
        !          5867: subraster *rasterize(), *subsp=NULL,*supsp=NULL; /*rasterize limits*/
        !          5868: subraster *new_subraster(), *rastack(), *spacesp=NULL; /*space below arrow*/
        !          5869: int	delete_subraster();		/*free work areas in case of error*/
        !          5870: double	strtod();			/* convert ascii [width] to value */
        !          5871: int	width = 10 + 8*size,  height;	/* width, height for \longxxxarrow */
        !          5872: int	islimits = 1;			/*true to handle limits internally*/
        !          5873: int	limsize = size-1;		/* font size for limits */
        !          5874: int	vspace = 1;			/* #empty rows below arrow */
        !          5875: int	pixsz = 1;			/*default #bits per pixel, 1=bitmap*/
        !          5876: /* -------------------------------------------------------------------------
        !          5877: construct longleft/rightarrow subraster, with limits, and return it to caller
        !          5878: -------------------------------------------------------------------------- */
        !          5879: /* --- check for optional width arg and replace default width --- */
        !          5880: if ( *(*expression) == '[' )		/*check for []-enclosed optional arg*/
        !          5881:   { int widthval;			/* test [width] before using it */
        !          5882:     *expression = texsubexpr(*expression,widtharg,255,"[","]",0,0);
        !          5883:     widthval =				/* convert [width] to integer */
        !          5884: 		(int)((unitlength*strtod(widtharg,NULL))+0.5);
        !          5885:     if ( widthval>=2 && widthval<=600 )	/* sanity check */
        !          5886:       width = widthval; }		/* replace deafault width */
        !          5887: /* --- now parse for limits, and bump expression past it(them) --- */
        !          5888: if ( islimits )				/* handling limits internally */
        !          5889:   { *expression = texscripts(*expression,sub,super,3); /* parse for limits */
        !          5890:     if ( *sub != '\000' )		/*have a subscript following arrow*/
        !          5891:       subsp = rasterize(sub,limsize);	/* so try to rasterize subscript */
        !          5892:     if ( *super != '\000' )		/*have superscript following arrow*/
        !          5893:       supsp = rasterize(super,limsize); } /*so try to rasterize superscript*/
        !          5894: /* --- set height based on width --- */
        !          5895: height = min2(17,max2(9,(width+2)/6));	/* height based on width */
        !          5896: height = 1 + (height/2)*2;		/* always force odd height */
        !          5897: /* --- generate arrow subraster --- */
        !          5898: if ( (arrowsp=arrow_subraster(width,height,pixsz,drctn,isBig)) /*build arrow*/
        !          5899: ==   NULL ) goto end_of_job;		/* and quit if we failed */
        !          5900: /* --- add space below arrow --- */
        !          5901: if ( vspace > 0 )			/* if we have space below arrow */
        !          5902:   if ( (spacesp=new_subraster(width,vspace,pixsz)) /*allocate required space*/
        !          5903:   !=   NULL )				/* and if we succeeded */
        !          5904:     if ( (arrowsp = rastack(spacesp,arrowsp,2,0,1,3)) /* space below arrow */
        !          5905:     ==   NULL ) goto end_of_job;	/* and quit if we failed */
        !          5906: /* --- init arrow subraster parameters --- */
        !          5907: arrowsp->size = size;			/*propagate base font size forward*/
        !          5908: arrowsp->baseline = height+vspace-1;	/* set baseline at bottom of arrow */
        !          5909: /* --- add limits above/below arrow, as necessary --- */
        !          5910: if ( subsp != NULL )			/* stack subscript below arrow */
        !          5911:   if ( (arrowsp = rastack(subsp,arrowsp,2,0,1,3)) /* subscript below arrow */
        !          5912:   ==   NULL ) goto end_of_job;		/* quit if failed */
        !          5913: if ( supsp != NULL )			/* stack superscript above arrow */
        !          5914:   if ( (arrowsp = rastack(arrowsp,supsp,1,vspace,1,3)) /*supsc above arrow*/
        !          5915:   ==   NULL ) goto end_of_job;		/* quit if failed */
        !          5916: /* --- return arrow (or NULL) to caller --- */
        !          5917: end_of_job:
        !          5918:   return ( arrowsp );
        !          5919: } /* --- end-of-function rastarrow() --- */
        !          5920: 
        !          5921: 
        !          5922: /* ==========================================================================
        !          5923:  * Function:	rastuparrow ( expression, size, basesp,  drctn, isBig, arg3 )
        !          5924:  * Purpose:	returns an up/down arrow subraster (e.g., for \longuparrow)
        !          5925:  * --------------------------------------------------------------------------
        !          5926:  * Arguments:	expression (I)	char **  to first char of null-terminated
        !          5927:  *				LaTeX expression (unused/unchanged)
        !          5928:  *		size (I)	int containing base font size (not used,
        !          5929:  *				just stored in subraster)
        !          5930:  *		basesp (I)	subraster *  to character (or subexpression)
        !          5931:  *				immediately preceding space, whose baseline
        !          5932:  *				and height params are transferred to space
        !          5933:  *		drctn (I)	int containing +1 for up, -1 for down,
        !          5934:  *				or 0 for updown
        !          5935:  *		isBig (I)	int containing 0 for ---> or 1 for ===>
        !          5936:  *		arg3 (I)	int unused
        !          5937:  * --------------------------------------------------------------------------
        !          5938:  * Returns:	( subraster * )	ptr to up/down arrow subraster
        !          5939:  *				or NULL for any error
        !          5940:  * --------------------------------------------------------------------------
        !          5941:  * Notes:     o	An optional argument [height] may *immediately* follow
        !          5942:  *		the \longxxx to explicitly set the arrow's height in pixels.
        !          5943:  *		For example, \longuparrow calculates a default height
        !          5944:  *		(as usual in LaTeX), whereas \longuparrow[25] explicitly
        !          5945:  *		draws a 25-pixel high arrow.  This can be used, e.g.,
        !          5946:  *		to draw commutative diagrams in conjunction with
        !          5947:  *		\array (and maybe with \stackrel and/or \relstack, too).
        !          5948:  *	      o	In case you really want to render, say, [f]---->[g], just
        !          5949:  *		use an intervening space, i.e., [f]\longuparrow~[g].
        !          5950:  *		In text use two spaces {\rm~[f]\longuparrow~~[g]}.
        !          5951:  * ======================================================================= */
        !          5952: /* --- entry point --- */
        !          5953: subraster *rastuparrow ( char **expression, int size, subraster *basesp,
        !          5954: 			int drctn, int isBig, int arg3 )
        !          5955: {
        !          5956: /* -------------------------------------------------------------------------
        !          5957: Allocations and Declarations
        !          5958: -------------------------------------------------------------------------- */
        !          5959: subraster *uparrow_subraster(), *arrowsp=NULL; /* subraster for arrow */
        !          5960: char	*texsubexpr(), heightarg[256];	/* parse for optional [height] */
        !          5961: char	*texscripts(), sub[1024],super[1024]; /* and _^limits after [width]*/
        !          5962: subraster *rasterize(), *subsp=NULL,*supsp=NULL; /*rasterize limits*/
        !          5963: subraster *rastcat();			/* cat superscript left, sub right */
        !          5964: double	strtod();			/* convert ascii [height] to value */
        !          5965: int	height = 8 + 2*size,  width;	/* height, width for \longxxxarrow */
        !          5966: int	islimits = 1;			/*true to handle limits internally*/
        !          5967: int	limsize = size-1;		/* font size for limits */
        !          5968: int	pixsz = 1;			/*default #bits per pixel, 1=bitmap*/
        !          5969: /* -------------------------------------------------------------------------
        !          5970: construct blank subraster, and return it to caller
        !          5971: -------------------------------------------------------------------------- */
        !          5972: /* --- check for optional height arg and replace default height --- */
        !          5973: if ( *(*expression) == '[' )		/*check for []-enclosed optional arg*/
        !          5974:   { int heightval;			/* test height before using it */
        !          5975:     *expression = texsubexpr(*expression,heightarg,255,"[","]",0,0);
        !          5976:     heightval =				/* convert [height] to integer */
        !          5977: 		(int)((unitlength*strtod(heightarg,NULL))+0.5);
        !          5978:     if ( heightval>=2 && heightval<=600 ) /* sanity check */
        !          5979:       height = heightval; }		/* replace deafault height */
        !          5980: /* --- now parse for limits, and bump expression past it(them) --- */
        !          5981: if ( islimits )				/* handling limits internally */
        !          5982:   { *expression = texscripts(*expression,sub,super,3); /* parse for limits */
        !          5983:     if ( *sub != '\000' )		/*have a subscript following arrow*/
        !          5984:       subsp = rasterize(sub,limsize);	/* so try to rasterize subscript */
        !          5985:     if ( *super != '\000' )		/*have superscript following arrow*/
        !          5986:       supsp = rasterize(super,limsize); } /*so try to rasterize superscript*/
        !          5987: /* --- set width based on height --- */
        !          5988: width = min2(17,max2(9,(height+2)/4));	/* width based on height */
        !          5989: width = 1 + (width/2)*2;		/* always force odd width */
        !          5990: /* --- generate arrow subraster --- */
        !          5991: if ( (arrowsp=uparrow_subraster(width,height,pixsz,drctn,isBig)) /*build arr*/
        !          5992: ==   NULL ) goto end_of_job;		/* and quit if we failed */
        !          5993: /* --- init arrow subraster parameters --- */
        !          5994: arrowsp->size = size;			/*propagate base font size forward*/
        !          5995: arrowsp->baseline = height-1;		/* set baseline at bottom of arrow */
        !          5996: /* --- add limits above/below arrow, as necessary --- */
        !          5997: if ( supsp != NULL )			/* cat superscript to left of arrow*/
        !          5998:   { int	supht = (supsp->image)->height,	/* superscript height */
        !          5999: 	deltab = (1+abs(height-supht))/2; /* baseline difference to center */
        !          6000:   supsp->baseline = supht-1;		/* force script baseline to bottom */
        !          6001:   if ( supht <= height )		/* arrow usually taller than script*/
        !          6002: 	arrowsp->baseline -= deltab;	/* so bottom of script goes here */
        !          6003:   else	supsp->baseline -= deltab;	/* else bottom of arrow goes here */
        !          6004:   if ( (arrowsp = rastcat(supsp,arrowsp,3)) /* superscript left of arrow */
        !          6005:     ==   NULL ) goto end_of_job; }	/* quit if failed */
        !          6006: if ( subsp != NULL )			/* cat subscript to right of arrow */
        !          6007:   { int	subht = (subsp->image)->height,	/* subscript height */
        !          6008: 	deltab = (1+abs(height-subht))/2; /* baseline difference to center */
        !          6009:   arrowsp->baseline = height-1;		/* reset arrow baseline to bottom */
        !          6010:   subsp->baseline = subht-1;		/* force script baseline to bottom */
        !          6011:   if ( subht <= height )		/* arrow usually taller than script*/
        !          6012: 	arrowsp->baseline -= deltab;	/* so bottom of script goes here */
        !          6013:   else	subsp->baseline -= deltab;	/* else bottom of arrow goes here */
        !          6014:   if ( (arrowsp = rastcat(arrowsp,subsp,3)) /* subscript right of arrow */
        !          6015:     ==   NULL ) goto end_of_job; }	/* quit if failed */
        !          6016: /* --- return arrow (or NULL) to caller --- */
        !          6017: end_of_job:
        !          6018:   arrowsp->baseline = height-1;		/* reset arrow baseline to bottom */
        !          6019:   return ( arrowsp );
        !          6020: } /* --- end-of-function rastuparrow() --- */
        !          6021: 
        !          6022: 
        !          6023: /* ==========================================================================
        !          6024:  * Function:	rastoverlay (expression, size, basesp, overlay, offset2, arg3)
        !          6025:  * Purpose:	overlays one raster on another
        !          6026:  * --------------------------------------------------------------------------
        !          6027:  * Arguments:	expression (I/O) char **  to first char of null-terminated
        !          6028:  *				string immediately following overlay \cmd to
        !          6029:  *				be rasterized, and returning ptr immediately
        !          6030:  *				following last character processed.
        !          6031:  *		size (I)	int containing 0-5 default font size
        !          6032:  *		basesp (I)	subraster *  to character (or subexpression)
        !          6033:  *				immediately preceding overlay \cmd
        !          6034:  *				(unused, but passed for consistency)
        !          6035:  *		overlay (I)	int containing 1 to overlay / (e.g., \not)
        !          6036:  *				or NOVALUE to pick up 2nd arg from expression
        !          6037:  *		offset2 (I)	int containing #pixels to horizontally offset
        !          6038:  *				overlay relative to underlying symbol,
        !          6039:  *				positive(right) or negative or 0,
        !          6040:  *				or NOVALUE to pick up optional [offset] arg
        !          6041:  *		arg3 (I)	int unused
        !          6042:  * --------------------------------------------------------------------------
        !          6043:  * Returns:	( subraster * )	ptr to subraster corresponding to composite,
        !          6044:  *				or NULL for any parsing error
        !          6045:  * --------------------------------------------------------------------------
        !          6046:  * Notes:     o
        !          6047:  * ======================================================================= */
        !          6048: /* --- entry point --- */
        !          6049: subraster *rastoverlay ( char **expression, int size, subraster *basesp,
        !          6050: 			int overlay, int offset2, int arg3 )
        !          6051: {
        !          6052: /* -------------------------------------------------------------------------
        !          6053: Allocations and Declarations
        !          6054: -------------------------------------------------------------------------- */
        !          6055: char	*texsubexpr(),			/*parse expression for base,overlay*/
        !          6056: 	expr1[512], expr2[512];		/* base, overlay */
        !          6057: subraster *rasterize(), *sp1=NULL, *sp2=NULL, /*rasterize 1=base, 2=overlay*/
        !          6058: 	*new_subraster();		/*explicitly alloc sp2 if necessary*/
        !          6059: subraster *rastcompose(), *overlaysp=NULL; /*subraster for composite overlay*/
        !          6060: int	line_raster();			/* draw diagonal for \Not */
        !          6061: /* -------------------------------------------------------------------------
        !          6062: Obtain base, and maybe overlay, and rasterize them
        !          6063: -------------------------------------------------------------------------- */
        !          6064: /* --- check for optional offset2 arg  --- */
        !          6065: if ( offset2 == NOVALUE )		/* only if not explicitly specified*/
        !          6066:  if ( *(*expression) == '[' )		/*check for []-enclosed optional arg*/
        !          6067:   { int offsetval;			/* test before using it */
        !          6068:     *expression = texsubexpr(*expression,expr2,511,"[","]",0,0);
        !          6069:     offsetval = (int)(strtod(expr2,NULL)+0.5); /* convert [offset2] to int */
        !          6070:     if ( abs(offsetval) <= 25 )		/* sanity check */
        !          6071:       offset2 = offsetval; }		/* replace deafault */
        !          6072: if ( offset2 == NOVALUE ) offset2 = 0;	/* novalue means no offset */
        !          6073: /* --- parse for base, bump expression past it, and rasterize it --- */
        !          6074: *expression = texsubexpr(*expression,expr1,511,"{","}",0,0);
        !          6075: if ( *expr1 == '\000' ) goto end_of_job; /* nothing to overlay, so quit */
        !          6076: if ( (sp1=rasterize(expr1,size))	/* rasterize base expression */
        !          6077: ==   NULL ) goto end_of_job;		/* quit if failed to rasterize */
        !          6078: overlaysp = sp1;			/*in case we return with no overlay*/
        !          6079: /* --- get overlay expression, and rasterize it --- */
        !          6080: if ( overlay == NOVALUE )		/* get overlay from input stream */
        !          6081:   { *expression = texsubexpr(*expression,expr2,511,"{","}",0,0);
        !          6082:     if ( *expr2 != '\000' )		/* have an overlay */
        !          6083:       sp2 = rasterize(expr2,size); }	/* so rasterize overlay expression */
        !          6084: else					/* specific overlay */
        !          6085:   switch ( overlay )
        !          6086:     {
        !          6087:     default: break;
        !          6088:     case 1:				/* e.g., \not overlays slash */
        !          6089:       sp2 = rasterize("/",size+1);	/* rasterize overlay expression */
        !          6090:       offset2 = max2(1,size-3);		/* push / right a bit */
        !          6091:       offset2 = 0;
        !          6092:       break;
        !          6093:     case 2:				/* e.g., \Not draws diagonal */
        !          6094:       sp2 = NULL;			/* no overlay required */
        !          6095:       if ( overlaysp != NULL )		/* check that we have raster */
        !          6096: 	{ raster *rp = overlaysp->image; /* raster to be \Not-ed */
        !          6097: 	  int width=rp->width, height=rp->height; /* raster dimensions */
        !          6098: 	  if ( 0 )			/* diagonal within bounding box */
        !          6099: 	   line_raster(rp,0,width-1,height-1,0,1); /* just draw diagonal */
        !          6100: 	  else				/* construct "wide" diagonal */
        !          6101: 	   { int margin=3;		/* desired extra margin width */
        !          6102: 	     sp2 = new_subraster(width+margin,height+margin,1); /*alloc it*/
        !          6103: 	     if ( sp2 != NULL )		/* allocated successfully */
        !          6104: 	      line_raster(sp2->image,0,width+margin-1,height+margin-1,0,1);}}
        !          6105:       break;
        !          6106:     case 3:				/* e.g., \sout for strikeout */
        !          6107:       sp2 = NULL;			/* no overlay required */
        !          6108:       if ( overlaysp != NULL )		/* check that we have raster */
        !          6109: 	{ raster *rp = overlaysp->image; /* raster to be \Not-ed */
        !          6110: 	  int width=rp->width, height=rp->height; /* raster dimensions */
        !          6111: 	  int baseline = overlaysp->baseline; /* we'll ignore descenders */
        !          6112: 	  int midrow = max2(0,min2(height-1,offset2+((baseline+1)/2)));
        !          6113: 	  if ( 1 )			/* strikeout within bounding box */
        !          6114: 	    line_raster(rp,midrow,0,midrow,width-1,1); } /*draw strikeout*/
        !          6115:       break;
        !          6116:     } /* --- end-of-switch(overlay) --- */
        !          6117: if ( sp2 == NULL ) goto end_of_job;	/*return sp1 if failed to rasterize*/
        !          6118: /* -------------------------------------------------------------------------
        !          6119: construct composite overlay
        !          6120: -------------------------------------------------------------------------- */
        !          6121: overlaysp = rastcompose(sp1,sp2,offset2,0,3);
        !          6122: end_of_job:
        !          6123:   return ( overlaysp );
        !          6124: } /* --- end-of-function rastoverlay() --- */
        !          6125: 
        !          6126: 
        !          6127: /* ==========================================================================
        !          6128:  * Function:	rastfrac ( expression, size, basesp,  isfrac, arg2, arg3 )
        !          6129:  * Purpose:	\frac,\atop handler, returns a subraster corresponding to
        !          6130:  *		expression (immediately following \frac,\atop) at font size
        !          6131:  * --------------------------------------------------------------------------
        !          6132:  * Arguments:	expression (I/O) char **  to first char of null-terminated
        !          6133:  *				string immediately following \frac to be
        !          6134:  *				rasterized, and returning ptr immediately
        !          6135:  *				following last character processed.
        !          6136:  *		size (I)	int containing 0-5 default font size
        !          6137:  *		basesp (I)	subraster *  to character (or subexpression)
        !          6138:  *				immediately preceding \frac
        !          6139:  *				(unused, but passed for consistency)
        !          6140:  *		isfrac (I)	int containing true to draw horizontal line
        !          6141:  *				between numerator and denominator,
        !          6142:  *				or false not to draw it (for \atop).
        !          6143:  *		arg2 (I)	int unused
        !          6144:  *		arg3 (I)	int unused
        !          6145:  * --------------------------------------------------------------------------
        !          6146:  * Returns:	( subraster * )	ptr to subraster corresponding to fraction,
        !          6147:  *				or NULL for any parsing error
        !          6148:  * --------------------------------------------------------------------------
        !          6149:  * Notes:     o
        !          6150:  * ======================================================================= */
        !          6151: /* --- entry point --- */
        !          6152: subraster *rastfrac ( char **expression, int size, subraster *basesp,
        !          6153: 			int isfrac, int arg2, int arg3 )
        !          6154: {
        !          6155: /* -------------------------------------------------------------------------
        !          6156: Allocations and Declarations
        !          6157: -------------------------------------------------------------------------- */
        !          6158: char	*texsubexpr(),			/*parse expression for numer,denom*/
        !          6159: 	numer[8192], denom[8192];	/*numer,denom parsed from expression*/
        !          6160: subraster *rasterize(), *numsp=NULL, *densp=NULL; /*rasterize numer, denom*/
        !          6161: subraster *rastack(), *fracsp=NULL;	/* subraster for numer/denom */
        !          6162: subraster *new_subraster()/*, *spacesp=NULL*/; /* space for num or den */
        !          6163: int	width=0,			/* width of constructed raster */
        !          6164: 	numheight=0;			/* height of numerator */
        !          6165: int	baseht=0, baseln=0;		/* height,baseline of base symbol */
        !          6166: /*int	istweak = 1;*/			/*true to tweak baseline alignment*/
        !          6167: int	rule_raster(),			/* draw horizontal line for frac */
        !          6168: 	lineheight = 1;			/* thickness of fraction line */
        !          6169: int	vspace = 1;			/*vertical space between components*/
        !          6170: int	delete_subraster();		/*free work areas in case of error*/
        !          6171: int	type_raster();			/* display debugging output */
        !          6172: /* -------------------------------------------------------------------------
        !          6173: Obtain numerator and denominator, and rasterize them
        !          6174: -------------------------------------------------------------------------- */
        !          6175: /* --- parse for numerator,denominator and bump expression past them --- */
        !          6176: *expression = texsubexpr(*expression,numer,0,"{","}",0,0);
        !          6177: *expression = texsubexpr(*expression,denom,0,"{","}",0,0);
        !          6178: if ( *numer=='\000' && *denom=='\000' )	/* missing both components of frac */
        !          6179:   goto end_of_job;			/* nothing to do, so quit */
        !          6180: /* --- rasterize numerator, denominator --- */
        !          6181: if ( *numer != '\000' )			/* have a numerator */
        !          6182:  if ( (numsp = rasterize(numer,size-1))	/* so rasterize numer at size-1 */
        !          6183:  ==   NULL ) goto end_of_job;		/* and quit if failed */
        !          6184: if ( *denom != '\000' )			/* have a denominator */
        !          6185:  if ( (densp = rasterize(denom,size-1))	/* so rasterize denom at size-1 */
        !          6186:  ==   NULL )				/* failed */
        !          6187:   { if ( numsp != NULL )		/* already rasterized numerator */
        !          6188:       delete_subraster(numsp);		/* so free now-unneeded numerator */
        !          6189:     goto end_of_job; }			/* and quit */
        !          6190: /* --- if one componenet missing, use a blank space for it --- */
        !          6191: if ( numsp == NULL )			/* no numerator given */
        !          6192:   numsp = rasterize("[?]",size-1);	/* missing numerator */
        !          6193: if ( densp == NULL )			/* no denominator given */
        !          6194:   densp = rasterize("[?]",size-1);	/* missing denominator */
        !          6195: /* --- check that we got both components --- */
        !          6196: if ( numsp==NULL || densp==NULL )	/* some problem */
        !          6197:   { delete_subraster(numsp);		/*delete numerator (if it existed)*/
        !          6198:     delete_subraster(densp);		/*delete denominator (if it existed)*/
        !          6199:     goto end_of_job; }			/* and quit */
        !          6200: /* --- get height of numerator (to determine where line belongs) --- */
        !          6201: numheight = (numsp->image)->height;	/* get numerator's height */
        !          6202: /* -------------------------------------------------------------------------
        !          6203: construct raster with numerator stacked over denominator
        !          6204: -------------------------------------------------------------------------- */
        !          6205: /* --- construct raster with numer/denom --- */
        !          6206: if ( (fracsp = rastack(densp,numsp,0,2*vspace+lineheight,1,3))/*numer/denom*/
        !          6207: ==  NULL )				/* failed to construct numer/denom */
        !          6208:   { delete_subraster(numsp);		/* so free now-unneeded numerator */
        !          6209:     delete_subraster(densp);		/* and now-unneeded denominator */
        !          6210:     goto end_of_job; }			/* and then quit */
        !          6211: /* --- determine width of constructed raster --- */
        !          6212: width = (fracsp->image)->width;		/*just get width of embedded image*/
        !          6213: /* --- initialize subraster parameters --- */
        !          6214: fracsp->size = size;			/* propagate font size forward */
        !          6215: fracsp->baseline = (numheight+vspace+lineheight)+(size+2);/*default baseline*/
        !          6216: if ( basesp != (subraster *)NULL )	/* we have base symbol for frac */
        !          6217:   { baseht = (basesp->image)->height; 	/* height of base symbol */
        !          6218:     baseln =  basesp->baseline;		/* and its baseline */
        !          6219:   } /* --- end-of-if(basesp!=NULL) --- */
        !          6220: /* -------------------------------------------------------------------------
        !          6221: draw horizontal line between numerator and denominator
        !          6222: -------------------------------------------------------------------------- */
        !          6223: if ( isfrac )				/*line for \frac, but not for \atop*/
        !          6224:   rule_raster(fracsp->image,numheight+vspace,0,width,lineheight,0);
        !          6225: /* -------------------------------------------------------------------------
        !          6226: return final result to caller
        !          6227: -------------------------------------------------------------------------- */
        !          6228: end_of_job:
        !          6229:   if ( msgfp!=NULL && msglevel>=99 )
        !          6230:     { fprintf(msgfp,"rastfrac> returning %s\n",(fracsp==NULL?"null":"..."));
        !          6231:       if ( fracsp != NULL )		/* have a constructed raster */
        !          6232: 	type_raster(fracsp->image,msgfp); } /* display constructed raster */
        !          6233:   return ( fracsp );
        !          6234: } /* --- end-of-function rastfrac() --- */
        !          6235: 
        !          6236: 
        !          6237: /* ==========================================================================
        !          6238:  * Function:	rastackrel ( expression, size, basesp,  base, arg2, arg3 )
        !          6239:  * Purpose:	\stackrel handler, returns a subraster corresponding to
        !          6240:  *		stacked relation
        !          6241:  * --------------------------------------------------------------------------
        !          6242:  * Arguments:	expression (I/O) char **  to first char of null-terminated
        !          6243:  *				string immediately following \stackrel to be
        !          6244:  *				rasterized, and returning ptr immediately
        !          6245:  *				following last character processed.
        !          6246:  *		size (I)	int containing 0-4 default font size
        !          6247:  *		basesp (I)	subraster *  to character (or subexpression)
        !          6248:  *				immediately preceding \stackrel
        !          6249:  *				(unused, but passed for consistency)
        !          6250:  *		base (I)	int containing 1 if upper/first subexpression
        !          6251:  *				is base relation, or 2 if lower/second is
        !          6252:  *		arg2 (I)	int unused
        !          6253:  *		arg3 (I)	int unused
        !          6254:  * --------------------------------------------------------------------------
        !          6255:  * Returns:	( subraster * )	ptr to subraster corresponding to stacked
        !          6256:  *				relation, or NULL for any parsing error
        !          6257:  * --------------------------------------------------------------------------
        !          6258:  * Notes:     o
        !          6259:  * ======================================================================= */
        !          6260: /* --- entry point --- */
        !          6261: subraster *rastackrel ( char **expression, int size, subraster *basesp,
        !          6262: 			int base, int arg2, int arg3 )
        !          6263: {
        !          6264: /* -------------------------------------------------------------------------
        !          6265: Allocations and Declarations
        !          6266: -------------------------------------------------------------------------- */
        !          6267: char	*texsubexpr(),			/*parse expression for numer,denom*/
        !          6268: 	upper[8192], lower[8192];	/*upper,lower parsed from expression*/
        !          6269: subraster *rasterize(), *upsp=NULL, *lowsp=NULL; /* rasterize upper, lower */
        !          6270: subraster *rastack(), *relsp=NULL;	/* subraster for upper/lower */
        !          6271: int	upsize  = (base==1? size:size-1), /* font size for upper component */
        !          6272: 	lowsize = (base==2? size:size-1); /* font size for lower component */
        !          6273: int	vspace = 1;			/*vertical space between components*/
        !          6274: int	delete_subraster();		/*free work areas in case of error*/
        !          6275: /* -------------------------------------------------------------------------
        !          6276: Obtain numerator and denominator, and rasterize them
        !          6277: -------------------------------------------------------------------------- */
        !          6278: /* --- parse for numerator,denominator and bump expression past them --- */
        !          6279: *expression = texsubexpr(*expression,upper,0,"{","}",0,0);
        !          6280: *expression = texsubexpr(*expression,lower,0,"{","}",0,0);
        !          6281: if ( *upper=='\000' || *lower=='\000' )	/* missing either component */
        !          6282:   goto end_of_job;			/* nothing to do, so quit */
        !          6283: /* --- rasterize upper, lower --- */
        !          6284: if ( *upper != '\000' )			/* have upper component */
        !          6285:  if ( (upsp = rasterize(upper,upsize))	/* so rasterize upper component */
        !          6286:  ==   NULL ) goto end_of_job;		/* and quit if failed */
        !          6287: if ( *lower != '\000' )			/* have lower component */
        !          6288:  if ( (lowsp = rasterize(lower,lowsize)) /* so rasterize lower component */
        !          6289:  ==   NULL )				/* failed */
        !          6290:   { if ( upsp != NULL )			/* already rasterized upper */
        !          6291:       delete_subraster(upsp);		/* so free now-unneeded upper */
        !          6292:     goto end_of_job; }			/* and quit */
        !          6293: /* -------------------------------------------------------------------------
        !          6294: construct stacked relation raster
        !          6295: -------------------------------------------------------------------------- */
        !          6296: /* --- construct stacked relation --- */
        !          6297: if ( (relsp = rastack(lowsp,upsp,3-base,vspace,1,3)) /* stacked relation */
        !          6298: ==   NULL ) goto end_of_job;		/* quit if failed */
        !          6299: /* --- initialize subraster parameters --- */
        !          6300: relsp->size = size;			/* propagate font size forward */
        !          6301: /* -------------------------------------------------------------------------
        !          6302: return final result to caller
        !          6303: -------------------------------------------------------------------------- */
        !          6304: end_of_job:
        !          6305:   return ( relsp );
        !          6306: } /* --- end-of-function rastackrel() --- */
        !          6307: 
        !          6308: 
        !          6309: /* ==========================================================================
        !          6310:  * Function:	rastmathfunc ( expression, size, basesp,  base, arg2, arg3 )
        !          6311:  * Purpose:	\log, \lim, etc handler, returns a subraster corresponding
        !          6312:  *		to math functions
        !          6313:  * --------------------------------------------------------------------------
        !          6314:  * Arguments:	expression (I/O) char **  to first char of null-terminated
        !          6315:  *				string immediately following \mathfunc to be
        !          6316:  *				rasterized, and returning ptr immediately
        !          6317:  *				following last character processed.
        !          6318:  *		size (I)	int containing 0-4 default font size
        !          6319:  *		basesp (I)	subraster *  to character (or subexpression)
        !          6320:  *				immediately preceding \mathfunc
        !          6321:  *				(unused, but passed for consistency)
        !          6322:  *		mathfunc (I)	int containing 1=arccos, 2=arcsin, etc.
        !          6323:  *		islimits (I)	int containing 1 if function may have
        !          6324:  *				limits underneath, e.g., \lim_{n\to\infty}
        !          6325:  *		arg3 (I)	int unused
        !          6326:  * --------------------------------------------------------------------------
        !          6327:  * Returns:	( subraster * )	ptr to subraster corresponding to mathfunc,
        !          6328:  *				or NULL for any parsing error
        !          6329:  * --------------------------------------------------------------------------
        !          6330:  * Notes:     o
        !          6331:  * ======================================================================= */
        !          6332: /* --- entry point --- */
        !          6333: subraster *rastmathfunc ( char **expression, int size, subraster *basesp,
        !          6334: 			int mathfunc, int islimits, int arg3 )
        !          6335: {
        !          6336: /* -------------------------------------------------------------------------
        !          6337: Allocations and Declarations
        !          6338: -------------------------------------------------------------------------- */
        !          6339: char	*texscripts(),			/* parse expression for _limits */
        !          6340: 	func[4096], limits[8192];	/* func as {\rm func}, limits */
        !          6341: char	*texsubexpr(),			/* parse expression for arg */
        !          6342: 	funcarg[2048];			/* optional func arg */
        !          6343: subraster *rasterize(), *funcsp=NULL, *limsp=NULL; /*rasterize func,limits*/
        !          6344: subraster *rastack(), *mathfuncsp=NULL;	/* subraster for mathfunc/limits */
        !          6345: int	limsize = size-1;		/* font size for limits */
        !          6346: int	vspace = 1;			/*vertical space between components*/
        !          6347: int	delete_subraster();		/*free work areas in case of error*/
        !          6348: /* --- table of function names by mathfunc number --- */
        !          6349: static	int  numnames = 34;		/* number of names in table */
        !          6350: static	char *funcnames[] = {
        !          6351: 	"error",			/*  0 index is illegal/error bucket*/
        !          6352: 	"arccos",  "arcsin",  "arctan",	/*  1 -  3 */
        !          6353: 	"arg",     "cos",     "cosh",	/*  4 -  6 */
        !          6354: 	"cot",     "coth",    "csc",	/*  7 -  9 */
        !          6355: 	"deg",     "det",     "dim",	/* 10 - 12 */
        !          6356: 	"exp",     "gcd",     "hom",	/* 13 - 15 */
        !          6357: 	"inf",     "ker",     "lg",	/* 16 - 18 */
        !          6358: 	"lim",     "liminf",  "limsup",	/* 19 - 21 */
        !          6359: 	"ln",      "log",     "max",	/* 22 - 24 */
        !          6360: 	"min",     "Pr",      "sec",	/* 25 - 27 */
        !          6361: 	"sin",     "sinh",    "sup",	/* 28 - 30 */
        !          6362: 	"tan",     "tanh",		/* 31 - 32 */
        !          6363: 	/* --- extra mimetex funcnames --- */
        !          6364: 	"tr",				/* 33 */
        !          6365: 	"pmod"				/* 34 */
        !          6366: 	} ;
        !          6367: /* -------------------------------------------------------------------------
        !          6368: set up and rasterize function name in \rm
        !          6369: -------------------------------------------------------------------------- */
        !          6370: if ( mathfunc<0 || mathfunc>numnames ) mathfunc=0; /* check index bounds */
        !          6371: switch ( mathfunc )			/* check for special processing */
        !          6372:   {
        !          6373:   default:				/* no special processing */
        !          6374:     strcpy(func,"{\\rm~");		/* init string with {\rm~ */
        !          6375:     strcat(func,funcnames[mathfunc]);	/* concat function name */
        !          6376:     strcat(func,"}");			/* and add terminating } */
        !          6377:     break;
        !          6378:   case 34:				/* \pmod{x} --> (mod x) */
        !          6379:     /* --- parse for \pmod{arg} argument --- */
        !          6380:     *expression = texsubexpr(*expression,funcarg,2047,"{","}",0,0);
        !          6381:     strcpy(func,"{\\({\\rm~mod}");	/* init with {\left({\rm~mod} */
        !          6382:     strcat(func,"\\hspace2");		/* concat space */
        !          6383:     strcat(func,funcarg);		/* and \pmodargument */
        !          6384:     strcat(func,"\\)}");		/* and add terminating \right)} */
        !          6385:     break;
        !          6386:   } /* --- end-of-switch(mathfunc) --- */
        !          6387: if ( (funcsp = rasterize(func,size))	/* rasterize function name */
        !          6388: ==   NULL ) goto end_of_job;		/* and quit if failed */
        !          6389: mathfuncsp = funcsp;			/* just return funcsp if no limits */
        !          6390: if ( !islimits ) goto end_of_job;	/* treat any subscript normally */
        !          6391: /* -------------------------------------------------------------------------
        !          6392: Obtain limits, if permitted and if provided, and rasterize them
        !          6393: -------------------------------------------------------------------------- */
        !          6394: /* --- parse for subscript limits, and bump expression past it(them) --- */
        !          6395: *expression = texscripts(*expression,limits,limits,1);
        !          6396: if ( *limits=='\000') goto end_of_job;	/* no limits, nothing to do, quit */
        !          6397: /* --- rasterize limits --- */
        !          6398: if ( (limsp = rasterize(limits,limsize)) /* rasterize limits */
        !          6399: ==   NULL ) goto end_of_job;		/* and quit if failed */
        !          6400: /* -------------------------------------------------------------------------
        !          6401: construct func atop limits
        !          6402: -------------------------------------------------------------------------- */
        !          6403: /* --- construct func atop limits --- */
        !          6404: if ( (mathfuncsp = rastack(limsp,funcsp,2,vspace,1,3)) /* func atop limits */
        !          6405: ==   NULL ) goto end_of_job;		/* quit if failed */
        !          6406: /* --- initialize subraster parameters --- */
        !          6407: mathfuncsp->size = size;		/* propagate font size forward */
        !          6408: /* -------------------------------------------------------------------------
        !          6409: return final result to caller
        !          6410: -------------------------------------------------------------------------- */
        !          6411: end_of_job:
        !          6412:   return ( mathfuncsp );
        !          6413: } /* --- end-of-function rastmathfunc() --- */
        !          6414: 
        !          6415: 
        !          6416: /* ==========================================================================
        !          6417:  * Function:	rastsqrt ( expression, size, basesp,  arg1, arg2, arg3 )
        !          6418:  * Purpose:	\sqrt handler, returns a subraster corresponding to
        !          6419:  *		expression (immediately following \sqrt) at font size
        !          6420:  * --------------------------------------------------------------------------
        !          6421:  * Arguments:	expression (I/O) char **  to first char of null-terminated
        !          6422:  *				string immediately following \sqrt to be
        !          6423:  *				rasterized, and returning ptr immediately
        !          6424:  *				following last character processed.
        !          6425:  *		size (I)	int containing 0-4 default font size
        !          6426:  *		basesp (I)	subraster *  to character (or subexpression)
        !          6427:  *				immediately preceding \accent
        !          6428:  *				(unused, but passed for consistency)
        !          6429:  *		arg1 (I)	int unused
        !          6430:  *		arg2 (I)	int unused
        !          6431:  *		arg3 (I)	int unused
        !          6432:  * --------------------------------------------------------------------------
        !          6433:  * Returns:	( subraster * )	ptr to subraster corresponding to expression,
        !          6434:  *				or NULL for any parsing error
        !          6435:  *				(expression ptr unchanged if error occurs)
        !          6436:  * --------------------------------------------------------------------------
        !          6437:  * Notes:     o
        !          6438:  * ======================================================================= */
        !          6439: /* --- entry point --- */
        !          6440: subraster *rastsqrt ( char **expression, int size, subraster *basesp,
        !          6441: 			int arg1, int arg2, int arg3 )
        !          6442: {
        !          6443: /* -------------------------------------------------------------------------
        !          6444: Allocations and Declarations
        !          6445: -------------------------------------------------------------------------- */
        !          6446: char	*texsubexpr(), subexpr[8192],	/* parse subexpr to be sqrt-ed */
        !          6447: 	rootarg[8192];			/* optional \sqrt[rootarg]{...} */
        !          6448: subraster *rasterize(), *subsp=NULL;	/* rasterize subexpr */
        !          6449: subraster *accent_subraster(), *sqrtsp=NULL, /* subraster with the sqrt */
        !          6450: 	*new_subraster(), *rootsp=NULL;	/* optionally preceded by [rootarg]*/
        !          6451: int	sqrtheight=0, sqrtwidth=0, surdwidth=0,	/* height,width of sqrt */
        !          6452: 	rootheight=0, rootwidth=0,	/* height,width of rootarg raster */
        !          6453: 	subheight=0, subwidth=0, pixsz=0; /* height,width,pixsz of subexpr */
        !          6454: int	rastput();			/* put subexpr in constructed sqrt */
        !          6455: int	overspace = 2;			/*space between subexpr and overbar*/
        !          6456: int	delete_subraster();		/* free work areas */
        !          6457: /* -------------------------------------------------------------------------
        !          6458: Obtain subexpression to be sqrt-ed, and rasterize it
        !          6459: -------------------------------------------------------------------------- */
        !          6460: /* --- first check for optional \sqrt[rootarg]{...} --- */
        !          6461: if ( *(*expression) == '[' )		/*check for []-enclosed optional arg*/
        !          6462:   { *expression = texsubexpr(*expression,rootarg,0,"[","]",0,0);
        !          6463:     if ( *rootarg != '\000' )		/* got rootarg */
        !          6464:      if ( (rootsp=rasterize(rootarg,size-1)) /*rasterize it at smaller size*/
        !          6465:      != NULL )				/* rasterized successfully */
        !          6466:       {	rootheight = (rootsp->image)->height;  /* get height of rootarg */
        !          6467: 	rootwidth  = (rootsp->image)->width; } /* and its width */
        !          6468:   } /* --- end-of-if(**expression=='[') --- */
        !          6469: /* --- parse for subexpr to be sqrt-ed, and bump expression past it --- */
        !          6470: *expression = texsubexpr(*expression,subexpr,0,"{","}",0,0);
        !          6471: if ( *subexpr == '\000' )		/* couldn't get subexpression */
        !          6472:   goto end_of_job;			/* nothing to do, so quit */
        !          6473: /* --- rasterize subexpression to be accented --- */
        !          6474: if ( (subsp = rasterize(subexpr,size))	/*rasterize subexpr at original size*/
        !          6475: ==   NULL ) goto end_of_job;		/* quit if failed */
        !          6476: /* -------------------------------------------------------------------------
        !          6477: determine height and width of sqrt raster to be constructed
        !          6478: -------------------------------------------------------------------------- */
        !          6479: /* --- first get height and width of subexpr --- */
        !          6480: subheight = (subsp->image)->height;	/* height of subexpr */
        !          6481: subwidth  = (subsp->image)->width;	/* and its width */
        !          6482: pixsz     = (subsp->image)->pixsz;	/* pixsz remains constant */
        !          6483: /* --- determine height and width of sqrt to contain subexpr --- */
        !          6484: sqrtheight = subheight + overspace;	/* subexpr + blank line + overbar */
        !          6485: surdwidth  = SQRTWIDTH(sqrtheight);	/* width of surd */
        !          6486: sqrtwidth  = subwidth + surdwidth + 1;	/* total width */
        !          6487: /* -------------------------------------------------------------------------
        !          6488: construct sqrt (with room to move in subexpr) and embed subexpr in it
        !          6489: -------------------------------------------------------------------------- */
        !          6490: /* --- construct sqrt --- */
        !          6491: if ( (sqrtsp=accent_subraster(SQRTACCENT,sqrtwidth,sqrtheight,pixsz))
        !          6492: ==   NULL ) goto end_of_job;		/* quit if failed to build sqrt */
        !          6493: /* --- embed subexpr in sqrt at lower-right corner--- */
        !          6494: rastput(sqrtsp->image,subsp->image,overspace,sqrtwidth-subwidth,1);
        !          6495: sqrtsp->baseline = subsp->baseline + overspace; /* adjust baseline */
        !          6496: /* --- "embed" rootarg at upper-left --- */
        !          6497: if ( rootsp != NULL )			/*have optional \sqrt[rootarg]{...}*/
        !          6498:   {
        !          6499:   /* --- allocate full raster to contain sqrtsp and rootsp --- */
        !          6500:   int fullwidth = sqrtwidth +rootwidth - min2(rootwidth,max2(0,surdwidth-4)),
        !          6501:       fullheight= sqrtheight+rootheight- min2(rootheight,3+size);
        !          6502:   subraster *fullsp = new_subraster(fullwidth,fullheight,pixsz);
        !          6503:   if ( fullsp != NULL )			/* allocated successfully */
        !          6504:     { /* --- embed sqrtsp exactly at lower-right corner --- */
        !          6505:       rastput(fullsp->image,sqrtsp->image, /* exactly at lower-right corner*/
        !          6506: 	fullheight-sqrtheight,fullwidth-sqrtwidth,1);
        !          6507:       /* --- embed rootsp near upper-left, nestled above leading surd --- */
        !          6508:       rastput(fullsp->image,rootsp->image,
        !          6509: 	0,max2(0,surdwidth-rootwidth-2-size),0);
        !          6510:       /* --- replace sqrtsp with fullsp --- */
        !          6511:       delete_subraster(sqrtsp);		/* free original sqrtsp */
        !          6512:       sqrtsp = fullsp;			/* and repoint it to fullsp instead*/
        !          6513:       sqrtsp->baseline = fullheight - (subheight - subsp->baseline); }
        !          6514:   } /* --- end-of-if(rootsp!=NULL) --- */
        !          6515: /* --- initialize subraster parameters --- */
        !          6516: sqrtsp->size = size;			/* propagate font size forward */
        !          6517: /* -------------------------------------------------------------------------
        !          6518: free unneeded component subrasters and return final result to caller
        !          6519: -------------------------------------------------------------------------- */
        !          6520: end_of_job:
        !          6521:   if ( subsp != NULL ) delete_subraster(subsp); /* free unneeded subexpr */
        !          6522:   return ( sqrtsp );
        !          6523: } /* --- end-of-function rastsqrt() --- */
        !          6524: 
        !          6525: 
        !          6526: /* ==========================================================================
        !          6527:  * Function:	rastaccent (expression,size,basesp,accent,isabove,isscript)
        !          6528:  * Purpose:	\hat, \vec, \etc handler, returns a subraster corresponding
        !          6529:  *		to expression (immediately following \accent) at font size
        !          6530:  * --------------------------------------------------------------------------
        !          6531:  * Arguments:	expression (I/O) char **  to first char of null-terminated
        !          6532:  *				string immediately following \accent to be
        !          6533:  *				rasterized, and returning ptr immediately
        !          6534:  *				following last character processed.
        !          6535:  *		size (I)	int containing 0-4 default font size
        !          6536:  *		basesp (I)	subraster *  to character (or subexpression)
        !          6537:  *				immediately preceding \accent
        !          6538:  *				(unused, but passed for consistency)
        !          6539:  *		accent (I)	int containing HATACCENT or VECACCENT, etc,
        !          6540:  *				between numerator and denominator,
        !          6541:  *				or false not to draw it (for \over).
        !          6542:  *		isabove (I)	int containing true if accent is above
        !          6543:  *				expression to be accented, or false
        !          6544:  *				if accent is below (e.g., underbrace)
        !          6545:  *		isscript (I)	int containing true if sub/superscripts
        !          6546:  *				allowed (for under/overbrace), or 0 if not.
        !          6547:  * --------------------------------------------------------------------------
        !          6548:  * Returns:	( subraster * )	ptr to subraster corresponding to expression,
        !          6549:  *				or NULL for any parsing error
        !          6550:  *				(expression ptr unchanged if error occurs)
        !          6551:  * --------------------------------------------------------------------------
        !          6552:  * Notes:     o	Also handles \overbrace{}^{} and \underbrace{}_{} by way
        !          6553:  *		of isabove and isscript args.
        !          6554:  * ======================================================================= */
        !          6555: /* --- entry point --- */
        !          6556: subraster *rastaccent ( char **expression, int size, subraster *basesp,
        !          6557: 			int accent, int isabove, int isscript )
        !          6558: {
        !          6559: /* -------------------------------------------------------------------------
        !          6560: Allocations and Declarations
        !          6561: -------------------------------------------------------------------------- */
        !          6562: char	*texsubexpr(), subexpr[8192];	/* parse subexpr to be accented */
        !          6563: char	*texscripts(), *script=NULL,	/* \under,overbrace allow scripts */
        !          6564: 	subscript[512], supscript[512];	/* scripts parsed from expression */
        !          6565: subraster *rasterize(), *subsp=NULL, *scrsp=NULL; /*rasterize subexpr,script*/
        !          6566: subraster *rastack(), *accsubsp=NULL;	/* stack accent, subexpr, script */
        !          6567: subraster *accent_subraster(), *accsp=NULL; /*raster for the accent itself*/
        !          6568: int	accheight=0, accwidth=0,	/* height, width of accent */
        !          6569: 	subheight=0, subwidth=0, pixsz=0; /* height,width,pixsz of subexpr */
        !          6570: int	delete_subraster();		/*free work areas in case of error*/
        !          6571: int	vspace = 0;			/*vertical space between accent,sub*/
        !          6572: /* -------------------------------------------------------------------------
        !          6573: Obtain subexpression to be accented, and rasterize it
        !          6574: -------------------------------------------------------------------------- */
        !          6575: /* --- parse for subexpr to be accented, and bump expression past it --- */
        !          6576: *expression = texsubexpr(*expression,subexpr,0,"{","}",0,0);
        !          6577: if ( *subexpr=='\000' )			/* couldn't get subexpression */
        !          6578:   goto end_of_job;			/* nothing to do, so quit */
        !          6579: /* --- rasterize subexpression to be accented --- */
        !          6580: if ( (subsp = rasterize(subexpr,size))	/*rasterize subexpr at original size*/
        !          6581: ==   NULL ) goto end_of_job;		/* quit if failed */
        !          6582: /* -------------------------------------------------------------------------
        !          6583: determine desired accent width and height
        !          6584: -------------------------------------------------------------------------- */
        !          6585: /* --- first get height and width of subexpr --- */
        !          6586: subheight = (subsp->image)->height;	/* height of subexpr */
        !          6587: subwidth  = (subsp->image)->width;	/* and its width is overall width */
        !          6588: pixsz     = (subsp->image)->pixsz;	/* original pixsz remains constant */
        !          6589: /* --- determine desired width, height of accent --- */
        !          6590: accwidth = subwidth;			/* same width as subexpr */
        !          6591: accheight = 3;				/* default for bars */
        !          6592: switch ( accent )
        !          6593:   { default: break;			/* default okay */
        !          6594:   case DOTACCENT: case DDOTACCENT:
        !          6595:     accheight = (size<3? 3:4);		/* default for dots */
        !          6596:     break;
        !          6597:   case HATACCENT: case VECACCENT:
        !          6598:     accheight = 7;			/* default */
        !          6599:     if ( subwidth < 10 ) accheight = 5;	/* unless small width */
        !          6600:       else if ( subwidth > 25 ) accheight = 9; /* or large */
        !          6601:     break;
        !          6602:   } /* --- end-of-switch(accent) --- */
        !          6603: accheight = min2(accheight,subheight);	/*never higher than accented subexpr*/
        !          6604: /* -------------------------------------------------------------------------
        !          6605: construct accent, and construct subraster with accent over (or under) subexpr
        !          6606: -------------------------------------------------------------------------- */
        !          6607: /* --- first construct accent --- */
        !          6608: if ( (accsp = accent_subraster(accent,accwidth,accheight,pixsz)) /* accent */
        !          6609: ==   NULL ) goto end_of_job;		/* quit if failed to build accent */
        !          6610: /* --- now stack accent above (or below) subexpr, and free both args --- */
        !          6611: accsubsp = (isabove? rastack(subsp,accsp,1,vspace,1,3)/*accent above subexpr*/
        !          6612:            : rastack(accsp,subsp,2,vspace,1,3));      /*accent below subexpr*/
        !          6613: if ( accsubsp == NULL )			/* failed to stack accent */
        !          6614:   { delete_subraster(subsp);		/* free unneeded subsp */
        !          6615:     delete_subraster(accsp);		/* and unneeded accsp */
        !          6616:     goto end_of_job; }			/* and quit */
        !          6617: /* -------------------------------------------------------------------------
        !          6618: look for super/subscript (annotation for over/underbrace)
        !          6619: -------------------------------------------------------------------------- */
        !          6620: /* --- first check whether accent permits accompanying annotations --- */
        !          6621: if ( !isscript ) goto end_of_job;	/* no annotations for this accent */
        !          6622: /* --- now get scripts if there actually are any --- */
        !          6623: *expression = texscripts(*expression,subscript,supscript,(isabove?2:1));
        !          6624: script = (isabove? supscript : subscript); /*select above^ or below_ script*/
        !          6625: if ( *script == '\000' ) goto end_of_job; /* no accompanying script */
        !          6626: /* --- rasterize script annotation at size-2 --- */
        !          6627: if ( (scrsp = rasterize(script,size-2)) /* rasterize script at size-2 */
        !          6628: ==   NULL ) goto end_of_job;		/* quit if failed */
        !          6629: /* --- stack annotation above (or below) accent, and free both args --- */
        !          6630: accsubsp = (isabove? rastack(accsubsp,scrsp,1,0,1,3) /* accent above base */
        !          6631:            : rastack(scrsp,accsubsp,2,0,1,3));       /* accent below base */
        !          6632: /* -------------------------------------------------------------------------
        !          6633: return final result to caller
        !          6634: -------------------------------------------------------------------------- */
        !          6635: end_of_job:
        !          6636:   if ( accsubsp != NULL )		/* initialize subraster parameters */
        !          6637:     accsubsp->size = size;		/* propagate font size forward */
        !          6638:   return ( accsubsp );
        !          6639: } /* --- end-of-function rastaccent() --- */
        !          6640: 
        !          6641: 
        !          6642: /* ==========================================================================
        !          6643:  * Function:	rastfont (expression,size,basesp,font,arg2,arg3)
        !          6644:  * Purpose:	\cal{}, \scr{}, \etc handler, returns subraster corresponding
        !          6645:  *		to char(s) within {}'s rendered at size
        !          6646:  * --------------------------------------------------------------------------
        !          6647:  * Arguments:	expression (I/O) char **  to first char of null-terminated
        !          6648:  *				string immediately following \font to be
        !          6649:  *				rasterized, and returning ptr immediately
        !          6650:  *				following last character processed.
        !          6651:  *		size (I)	int containing 0-5 default font size
        !          6652:  *		basesp (I)	subraster *  to character (or subexpression)
        !          6653:  *				immediately preceding \accent
        !          6654:  *				(unused, but passed for consistency)
        !          6655:  *		font (I)	int containing 1 for \cal{}, 2 for \scr{}
        !          6656:  *		arg2 (I)	int unused
        !          6657:  *		arg3 (I)	int unused
        !          6658:  * --------------------------------------------------------------------------
        !          6659:  * Returns:	( subraster * )	ptr to subraster corresponding to chars
        !          6660:  *				between {}'s, or NULL for any parsing error
        !          6661:  * --------------------------------------------------------------------------
        !          6662:  * Notes:     o
        !          6663:  * ======================================================================= */
        !          6664: /* --- entry point --- */
        !          6665: subraster *rastfont ( char **expression, int size, subraster *basesp,
        !          6666: 			int font, int arg2, int arg3 )
        !          6667: {
        !          6668: /* -------------------------------------------------------------------------
        !          6669: Allocations and Declarations
        !          6670: -------------------------------------------------------------------------- */
        !          6671: char	*texsubexpr(), fontchars[8192],	/*parse chars to be rendered in font*/
        !          6672: 	subexpr[8192];			/* turn \cal{AB} into \calA\calB */
        !          6673: char	*pfchars=fontchars, fchar='\0';	/* run thru fontchars one at a time*/
        !          6674: char	*name = NULL;			/* fonts[font].name */
        !          6675: int	class = 0,			/* fonts[font].class */
        !          6676: 	istext = 0;			/* set true for text type */
        !          6677: subraster *rasterize(), *fontsp=NULL,	/* rasterize chars in font */
        !          6678: 	*rastflags();			/* or just set flag to switch font */
        !          6679: int	oldsquashmargin = squashmargin;	/* turn off squash in text mode */
        !          6680: int	blanksignal = (-991234);	/*rastsquash signal right-hand blank*/
        !          6681: /* --- fonts recognized by rastfont --- */
        !          6682: static	int  nfonts = 5;		/* legal font #'s are 1...nfonts */
        !          6683: static	struct {char *name; int class;}
        !          6684:   fonts[] =
        !          6685:     { /* --- name  class 1=upper,2=alpha,3=alnum,4=lower,5=digit,9=all --- */
        !          6686: 	{ "\\badfont",	0 },
        !          6687: 	{ "\\cal",	1 },		/*(1) calligraphic, uppercase */
        !          6688: 	{ "\\scr",	1 },		/*(2) rsfs/script, uppercase */
        !          6689: 	{ "\\rm",	-1 },		/*(3) \rm,\text{abc} --> {\rm~abc} */
        !          6690: 	{ "\\it",	-1 },		/*(4) \it,\textit{abc}-->{\it~abc} */
        !          6691: 	{ "\\bb",	-1 },		/*(5) \bb,\mathbb{abc}-->{\bb~abc} */
        !          6692: 	{ NULL,		0 }
        !          6693:     } ; /* --- end-of-fonts[] --- */
        !          6694: /* -------------------------------------------------------------------------
        !          6695: first get font name and class to determine type of conversion desired
        !          6696: -------------------------------------------------------------------------- */
        !          6697: if ( font<=0 || font>nfonts ) font=0;	/* set error if out-of-bounds */
        !          6698: name  = fonts[font].name;		/* font name */
        !          6699: class = fonts[font].class;		/* font class */
        !          6700: if ( font==3 || font==4 )		/* text (respect blanks) */
        !          6701:  { istext = 1;				/* signal text mode */
        !          6702:    squashmargin = 0; }			/* don't squash internal blanks */
        !          6703: /* -------------------------------------------------------------------------
        !          6704: now convert \font{abc} --> {\font~abc}, or convert ABC to \calA\calB\calC
        !          6705: -------------------------------------------------------------------------- */
        !          6706: if ( class < 0 )			/* not character-by-character */
        !          6707:  { 
        !          6708:  /* ---
        !          6709:  if \font not immediately followed by { then it has no arg, so just set flag
        !          6710:  ------------------------------------------------------------------------ */
        !          6711:  if ( *(*expression) != '{' )		/* no \font arg, so just set flag */
        !          6712:     {
        !          6713:     if ( msgfp!=NULL && msglevel>=99 )
        !          6714:       fprintf(msgfp,"rastfont> \\%s rastflags() for font#%d\n",name,font);
        !          6715:     switch ( font )			/* set flag by our internal font# */
        !          6716:       {	case 3:				/* \rm, \text sets flag istext=1 */
        !          6717: 	  fontsp = rastflags(expression,size,basesp,ISTEXT,1,arg3); break;
        !          6718: 	case 4:				/* \it, \text sets flag istext=2 */
        !          6719: 	  fontsp = rastflags(expression,size,basesp,ISTEXT,2,arg3); break;
        !          6720: 	case 5:				/* \bb, \mathbb sets flag istext=3 */
        !          6721: 	  fontsp = rastflags(expression,size,basesp,ISTEXT,3,arg3); break;
        !          6722: 	default: break; }		/* unrecognized, set no flags */
        !          6723:     goto end_of_job;
        !          6724:     } /* --- end-of-if(*(*expression)!='{') --- */
        !          6725:  /* ---
        !          6726:  convert \font{abc} --> {\font~abc}
        !          6727:  ---------------------------------- */
        !          6728:  /* --- parse for {fontchars} arg, and bump expression past it --- */
        !          6729:  *expression = texsubexpr(*expression,fontchars,0,"{","}",0,0);
        !          6730:  if ( msgfp!=NULL && msglevel>=99 )
        !          6731:   fprintf(msgfp,"rastfont> \\%s fontchars=\"%s\"\n",name,fontchars);
        !          6732:  /* --- convert all fontchars at the same time --- */
        !          6733:  strcpy(subexpr,"{");			/* start off with opening { */
        !          6734:  strcat(subexpr,name);			/* followed by font name */
        !          6735:  strcat(subexpr,"~");			/* followed by whitespace */
        !          6736:  strcat(subexpr,fontchars);		/* followed by all the chars */
        !          6737:  strcat(subexpr,"}");			/* terminate with closing } */
        !          6738:  } /* --- end-of-if(class<0) --- */
        !          6739: else					/* character-by-character */
        !          6740:  {
        !          6741:  /* ---
        !          6742:  convert ABC to \calA\calB\calC
        !          6743:  ------------------------------ */
        !          6744:  int	isprevchar=0;			/* true if prev char converted */
        !          6745:  /* --- parse for {fontchars} arg, and bump expression past it --- */
        !          6746:  *expression = texsubexpr(*expression,fontchars,0,"{","}",0,0);
        !          6747:  if ( msgfp!=NULL && msglevel>=99 )
        !          6748:   fprintf(msgfp,"rastfont> \\%s fontchars=\"%s\"\n",name,fontchars);
        !          6749:  /* --- convert fontchars one at a time --- */
        !          6750:  strcpy(subexpr,"{\\rm~");		/* start off with opening {\rm */
        !          6751:  strcpy(subexpr,"{");			/* nope, just start off with { */
        !          6752:  for ( pfchars=fontchars; (fchar= *pfchars)!='\000'; pfchars++ )
        !          6753:   {
        !          6754:   if ( isthischar(fchar,WHITEMATH) )	/* some whitespace */
        !          6755:     { if ( 0 || istext )		/* and we're in a text mode */
        !          6756: 	strcat(subexpr,"\\;"); }	/* so respect whitespace */
        !          6757:   else					/* char to be displayed in font */
        !          6758:     { int exprlen = 0;			/* #chars in subexpr before fchar */
        !          6759:       int isinclass = 0;		/* set true if fchar in font class */
        !          6760:       switch ( class )			/* check if fchar is in font class */
        !          6761: 	{ default: break;		/* no chars in unrecognized class */
        !          6762: 	  case 1: if ( isupper((int)fchar) ) isinclass=1; break;
        !          6763: 	  case 2: if ( isalpha((int)fchar) ) isinclass=1; break;
        !          6764: 	  case 3: if ( isalnum((int)fchar) ) isinclass=1; break;
        !          6765: 	  case 4: if ( islower((int)fchar) ) isinclass=1; break;
        !          6766: 	  case 5: if ( isdigit((int)fchar) ) isinclass=1; break;
        !          6767: 	  case 9: isinclass=1; break; }
        !          6768:       if ( isinclass )			/* convert current char to \font */
        !          6769: 	{ strcat(subexpr,name);		/* by prefixing it with font name */
        !          6770: 	  isprevchar = 1; }		/* and set flag to signal separator*/
        !          6771:       else				/* current char not in \font */
        !          6772: 	{ if ( isprevchar )		/* extra separator only after \font*/
        !          6773: 	   if ( isalpha(fchar) )	/* separator only before alpha */
        !          6774: 	    strcat(subexpr,"~");	/* need separator after \font */
        !          6775: 	  isprevchar = 0; }		/* reset flag for next char */
        !          6776:       exprlen = strlen(subexpr);	/* #chars so far */
        !          6777:       subexpr[exprlen] = fchar;		/*fchar immediately after \fontname*/
        !          6778:       subexpr[exprlen+1] = '\000'; }	/* replace terminating '\0' */
        !          6779:   } /* --- end-of-for(pfchars) --- */
        !          6780:  strcat(subexpr,"}");			/* add closing } */
        !          6781:  } /* --- end-of-if/else(class<0) --- */
        !          6782: /* -------------------------------------------------------------------------
        !          6783: rasterize subexpression containing chars to be rendered at font
        !          6784: -------------------------------------------------------------------------- */
        !          6785: if ( msgfp!=NULL && msglevel>=99 )
        !          6786:   fprintf(msgfp,"rastfont> subexpr=\"%s\"\n",subexpr);
        !          6787: if ( (fontsp = rasterize(subexpr,size))	/* rasterize chars in font */
        !          6788: ==   NULL ) goto end_of_job;		/* and quit if failed */
        !          6789: /* -------------------------------------------------------------------------
        !          6790: back to caller with chars rendered in font
        !          6791: -------------------------------------------------------------------------- */
        !          6792: end_of_job:
        !          6793:   squashmargin = oldsquashmargin;	/* restore squash */
        !          6794:   if ( istext && fontsp!=NULL )		/* raster contains text */
        !          6795:     fontsp->type = blanksignal;		/* signal nosquash */
        !          6796:   return ( fontsp );			/* chars rendered in font */
        !          6797: } /* --- end-of-function rastfont() --- */
        !          6798: 
        !          6799: 
        !          6800: /* ==========================================================================
        !          6801:  * Function:	rastbegin ( expression, size, basesp, arg1, arg2, arg3 )
        !          6802:  * Purpose:	\begin{}...\end{}  handler, returns a subraster corresponding
        !          6803:  *		to array expression within environment, i.e., rewrites
        !          6804:  *		\begin{}...\end{} as mimeTeX equivalent, and rasterizes that.
        !          6805:  * --------------------------------------------------------------------------
        !          6806:  * Arguments:	expression (I/O) char **  to first char of null-terminated
        !          6807:  *				string immediately following \begin to be
        !          6808:  *				rasterized, and returning ptr immediately
        !          6809:  *				following last character processed.
        !          6810:  *		size (I)	int containing 0-4 default font size
        !          6811:  *		basesp (I)	subraster *  to character (or subexpression)
        !          6812:  *				immediately preceding \begin
        !          6813:  *				(unused, but passed for consistency)
        !          6814:  *		arg1 (I)	int unused
        !          6815:  *		arg2 (I)	int unused
        !          6816:  *		arg3 (I)	int unused
        !          6817:  * --------------------------------------------------------------------------
        !          6818:  * Returns:	( subraster * )	ptr to subraster corresponding to array
        !          6819:  *				expression, or NULL for any parsing error
        !          6820:  * --------------------------------------------------------------------------
        !          6821:  * Notes:     o
        !          6822:  * ======================================================================= */
        !          6823: /* --- entry point --- */
        !          6824: subraster *rastbegin ( char **expression, int size, subraster *basesp,
        !          6825: 			int arg1, int arg2, int arg3 )
        !          6826: {
        !          6827: /* -------------------------------------------------------------------------
        !          6828: Allocations and Declarations
        !          6829: -------------------------------------------------------------------------- */
        !          6830: char	*texsubexpr(), subexpr[8210],	/* \begin{} environment paramaters */
        !          6831: 	*exprptr=NULL,*begptr=NULL,*endptr=NULL,*braceptr=NULL; /* ptrs */
        !          6832: char	*begtoken="\\begin{", *endtoken="\\end{"; /*tokens we're looking for*/
        !          6833: int	strreplace();			/* replace substring in string */
        !          6834: char	*strchange();			/*\begin...\end --> {\begin...\end}*/
        !          6835: char	*delims = (char *)NULL;		/* mdelims[ienviron] */
        !          6836: subraster *rasterize(), *sp=NULL;	/* rasterize environment */
        !          6837: int	ienviron = 0;			/* environs[] index */
        !          6838: int	nbegins = 0;			/* #\begins nested beneath this one*/
        !          6839: int	envlen=0, sublen=0;		/* #chars in environ, subexpr */
        !          6840: static	int blevel = 0;			/* \begin...\end nesting level */
        !          6841: static	char *mdelims[] = { NULL, NULL, NULL, NULL,
        !          6842: 	"()","[]","{}","||","==",	/* for pbBvVmatrix */
        !          6843: 	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
        !          6844: 	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
        !          6845: static	char *environs[] = {		/* types of environments we process*/
        !          6846: 	"eqnarray",			/* 0 eqnarray environment */
        !          6847: 	"array",			/* 1 array environment */
        !          6848: 	"matrix",			/* 2 array environment */
        !          6849: 	"tabular",			/* 3 array environment */
        !          6850: 	"pmatrix",			/* 4 ( ) */
        !          6851: 	"bmatrix",			/* 5 [ ] */
        !          6852: 	"Bmatrix",			/* 6 { } */
        !          6853: 	"vmatrix",			/* 7 | | */
        !          6854: 	"Vmatrix",			/* 8 || || */
        !          6855: 	"gather",			/* 9 gather environment */
        !          6856: 	"align",			/* 10 align environment */
        !          6857: 	"verbatim",			/* 11 verbatim environment */
        !          6858: 	"picture",			/* 12 picture environment */
        !          6859: 	NULL };				/* trailer */
        !          6860: /* -------------------------------------------------------------------------
        !          6861: determine type of environment we're beginning
        !          6862: -------------------------------------------------------------------------- */
        !          6863: /* --- first bump nesting level --- */
        !          6864: blevel++;				/* count \begin...\begin...'s */
        !          6865: /* --- \begin must be followed by {type_of_environment} --- */
        !          6866: exprptr = texsubexpr(*expression,subexpr,0,"{","}",0,0);
        !          6867: if ( *subexpr == '\000' ) goto end_of_job; /* no environment given */
        !          6868: while ( (delims=strchr(subexpr,'*')) != NULL ) /* have environment* */
        !          6869:   strcpy(delims,delims+1);		/* treat it as environment */
        !          6870: /* --- look up environment in our table --- */
        !          6871: for ( ienviron=0; ;ienviron++ )		/* search table till NULL */
        !          6872:   if ( environs[ienviron] == NULL )	/* found NULL before match */
        !          6873:     goto end_of_job;			/* so quit */
        !          6874:   else					/* see if we have an exact match */
        !          6875:     if ( memcmp(environs[ienviron],subexpr,strlen(subexpr)) == 0 ) /*match*/
        !          6876:       break;				/* leave loop with ienviron index */
        !          6877: /* --- accumulate any additional params for this environment --- */
        !          6878: *subexpr = '\000';			/* reset subexpr to empty string */
        !          6879: delims = mdelims[ienviron];		/* mdelims[] string for ienviron */
        !          6880: if ( delims != NULL )			/* add appropriate opening delim */
        !          6881:   { strcpy(subexpr,"\\");		/* start with \ for (,[,{,|,= */
        !          6882:     strcat(subexpr,delims);		/* then add opening delim */
        !          6883:     subexpr[2] = '\000'; }		/* remove extraneous closing delim */
        !          6884: switch ( ienviron )
        !          6885:   {
        !          6886:   default: goto end_of_job;		/* environ not implemented yet */
        !          6887:   case 0:				/* \begin{eqnarray} */
        !          6888:     strcpy(subexpr,"\\array{rcl$");	/* set default rcl for eqnarray */
        !          6889:     break;
        !          6890:   case 1:  case 2:  case 3:		/* \begin{array} followed by {lcr} */
        !          6891:     strcpy(subexpr,"\\array{");		/*start with mimeTeX \array{ command*/
        !          6892:     skipwhite(exprptr);			/* bump to next non-white char */
        !          6893:     if ( *exprptr == '{' )		/* assume we have {lcr} argument */
        !          6894:       {	exprptr = texsubexpr(exprptr,subexpr+7,0,"{","}",0,0); /*add on lcr*/
        !          6895: 	if ( *(subexpr+7) == '\000' ) goto end_of_job; /* quit if no lcr */
        !          6896: 	strcat(subexpr,"$"); }		/* add terminating $ to lcr */
        !          6897:     break;
        !          6898:   case 4:  case 5:  case 6:		/* \begin{pmatrix} or b,B,v,Vmatrix */
        !          6899:   case 7:  case 8:
        !          6900:     strcat(subexpr,"\\array{");		/*start with mimeTeX \array{ command*/
        !          6901:     break;
        !          6902:   case 9:				/* gather */
        !          6903:     strcat(subexpr,"\\array{c$");	/* center equations */
        !          6904:     break;
        !          6905:   case 10:				/* align */
        !          6906:     strcat(subexpr,"\\array{rclrclrclrclrclrcl$"); /* a&=b & c&=d & etc */
        !          6907:     break;
        !          6908:   case 11:				/* verbatim */
        !          6909:     strcat(subexpr,"{\\rm ");		/* {\rm ...} */
        !          6910:     /*strcat(subexpr,"\\\\{\\rm ");*/	/* \\{\rm } doesn't work in context */
        !          6911:     break;
        !          6912:   case 12:				/* picture */
        !          6913:     strcat(subexpr,"\\picture");	/* picture environment */
        !          6914:     skipwhite(exprptr);			/* bump to next non-white char */
        !          6915:     if ( *exprptr == '(' )		/*assume we have (width,height) arg*/
        !          6916:       {	exprptr = texsubexpr(exprptr,subexpr+8,0,"(",")",0,1); /*add on arg*/
        !          6917: 	if ( *(subexpr+8) == '\000' ) goto end_of_job; } /* quit if no arg */
        !          6918:     strcat(subexpr,"{");		/* opening {  after (width,height) */
        !          6919:     break;
        !          6920:   } /* --- end-of-switch(ienviron) --- */
        !          6921: /* -------------------------------------------------------------------------
        !          6922: locate matching \end{...}
        !          6923: -------------------------------------------------------------------------- */
        !          6924: /* --- first \end following \begin --- */
        !          6925: if ( (endptr=strstr(exprptr,endtoken))	/* find 1st \end following \begin */
        !          6926: ==   NULL ) goto end_of_job;		/* and quit if no \end found */
        !          6927: /* --- find matching endptr by pushing past any nested \begin's --- */
        !          6928: begptr = exprptr;			/* start after first \begin{...} */
        !          6929: while ( 1 )				/*break when we find matching \end*/
        !          6930:   {
        !          6931:   /* --- first, set ptr to closing } terminating current \end{...} --- */
        !          6932:   if ( (braceptr=strchr(endptr+1,'}'))	/* find 1st } following \end{ */
        !          6933:   ==   NULL ) goto end_of_job;		/* and quit if no } found */
        !          6934:   /* -- locate next nested \begin --- */
        !          6935:   if ( (begptr=strstr(begptr,begtoken))	/* find next \begin{...} */
        !          6936:   ==   NULL ) break;			/*no more, so we have matching \end*/
        !          6937:   begptr += strlen(begtoken);		/* push ptr past token */
        !          6938:   if ( begptr >= endptr ) break;	/* past endptr, so not nested */
        !          6939:   /* --- have nested \begin, so push forward to next \end --- */
        !          6940:   nbegins++;				/* count another nested \begin */
        !          6941:   if ( (endptr=strstr(endptr+strlen(endtoken),endtoken)) /* find next \end */
        !          6942:   ==   NULL ) goto end_of_job;		/* and quit if none found */
        !          6943:   } /* --- end-of-while(1) --- */
        !          6944: /* --- push expression past closing } of \end{} --- */
        !          6945: *expression = braceptr+1;		/* resume processing after } */
        !          6946: /* -------------------------------------------------------------------------
        !          6947: add on everything (i.e., the ...'s) between \begin{}[{}] ... \end{}
        !          6948: -------------------------------------------------------------------------- */
        !          6949: /* --- add on everything, completing subexpr for \begin{}...\end{} --- */
        !          6950: sublen = strlen(subexpr);		/* #chars in "preamble" */
        !          6951: envlen = (int)(endptr-exprptr);		/* #chars between \begin{}{}...\end */
        !          6952: memcpy(subexpr+sublen,exprptr,envlen);	/*concatanate environ after subexpr*/
        !          6953: subexpr[sublen+envlen] = '\000';	/* and null-terminate */
        !          6954: if ( 2 > 1 )				/* always... */
        !          6955:   strcat(subexpr,"}");			/* ...followed by terminating } */
        !          6956: /* --- add terminating \right), etc, if necessary --- */
        !          6957: if ( delims != (char *)NULL )		/* need closing delim */
        !          6958:  { strcat(subexpr,"\\");		/* start with \ for ),],},|,= */
        !          6959:    strcat(subexpr,delims+1); }		/* add appropriate closing delim */
        !          6960: /* -------------------------------------------------------------------------
        !          6961: change nested \begin...\end to {\begin...\end} so \array{} can handle them
        !          6962: -------------------------------------------------------------------------- */
        !          6963: if ( nbegins > 0 )			/* have nested begins */
        !          6964:  if ( blevel < 2 )			/* only need to do this once */
        !          6965:   {
        !          6966:   begptr = subexpr;			/* start at beginning of subexpr */
        !          6967:   while( (begptr=strstr(begptr,begtoken)) != NULL ) /* have \begin{...} */
        !          6968:     { strchange(0,begptr,"{");		/* \begin --> {\begin */
        !          6969:       begptr += strlen(begtoken); }	/* continue past {\begin */
        !          6970:   endptr = subexpr;			/* start at beginning of subexpr */
        !          6971:   while( (endptr=strstr(endptr,endtoken)) != NULL ) /* have \end{...} */
        !          6972:     if ( (braceptr=strchr(endptr+1,'}')) /* find 1st } following \end{ */
        !          6973:     ==   NULL ) goto end_of_job;	/* and quit if no } found */
        !          6974:     else				/* found terminating } */
        !          6975:      { strchange(0,braceptr,"}");	/* \end{...} --> \end{...}} */
        !          6976:        endptr = braceptr+1; }		/* continue past \end{...} */
        !          6977:   } /* --- end-of-if(nbegins>0) --- */
        !          6978: /* -------------------------------------------------------------------------
        !          6979: post process as necessary
        !          6980: -------------------------------------------------------------------------- */
        !          6981: switch ( ienviron )
        !          6982:   {
        !          6983:   default: break;			/* no post-processing required */
        !          6984:   case 10:				/* align */
        !          6985:     strreplace(subexpr,"&=","#*@*#=",0); /* tag all &='s */
        !          6986:     strreplace(subexpr,"&<","#*@*#<",0); /* tag all &<'s */
        !          6987:     strreplace(subexpr,"&\\lt","#*@*#<",0); /* tag all &\lt's */
        !          6988:     strreplace(subexpr,"&\\leq","#*@*#\\leq",0); /* tag all &\leq's */
        !          6989:     strreplace(subexpr,"&>","#*@*#>",0); /* tag all &>'s */
        !          6990:     strreplace(subexpr,"&\\gt","#*@*#>",0); /* tag all &\gt's */
        !          6991:     strreplace(subexpr,"&\\geq","#*@*#\\geq",0); /* tag all &\geq's */
        !          6992:     if ( nbegins < 1 )			/* don't modify nested arrays */
        !          6993:       strreplace(subexpr,"&","\\hspace{10}&\\hspace{10}",0); /* add space */
        !          6994:     strreplace(subexpr,"#*@*#=","& = &",0); /*restore and xlate tagged &='s*/
        !          6995:     strreplace(subexpr,"#*@*#<","& \\lt &",0); /*restore, xlate tagged &<'s*/
        !          6996:     strreplace(subexpr,"#*@*#\\leq","& \\leq &",0); /*xlate tagged &\leq's*/
        !          6997:     strreplace(subexpr,"#*@*#>","& \\gt &",0); /*restore, xlate tagged &>'s*/
        !          6998:     strreplace(subexpr,"#*@*#\\geq","& \\geq &",0); /*xlate tagged &\geq's*/
        !          6999:     break;
        !          7000:   case 11:				/* verbatim */
        !          7001:     strreplace(subexpr,"\n","\\\\",0);	/* xlate \n newline to latex \\ */
        !          7002:     /*strcat(subexpr,"\\\\");*/		/* add final latex \\ newline */
        !          7003:     break;
        !          7004:   case 12:				/* picture */
        !          7005:     strreplace(subexpr,"\\put "," ",0);	/*remove \put's (not really needed)*/
        !          7006:     strreplace(subexpr,"\\put(","(",0);	/*remove \put's (not really needed)*/
        !          7007:     strreplace(subexpr,"\\oval","\\circle",0); /* actually an ellipse */
        !          7008:     break;
        !          7009:   } /* --- end-of-switch(ienviron) --- */
        !          7010: /* -------------------------------------------------------------------------
        !          7011: return rasterized mimeTeX equivalent of \begin{}...\end{} environment
        !          7012: -------------------------------------------------------------------------- */
        !          7013: /* --- debugging output --- */
        !          7014: if ( msgfp!=NULL && msglevel>=99 )
        !          7015:   fprintf(msgfp,"rastbegin> subexpr=%s\n",subexpr);
        !          7016: /* --- rasterize mimeTeX equivalent of \begin{}...\end{} environment --- */
        !          7017: sp = rasterize(subexpr,size);		/* rasterize subexpr */
        !          7018: end_of_job:
        !          7019:   blevel--;				/* decrement \begin nesting level */
        !          7020:   return ( sp );			/* back to caller with sp or NULL */
        !          7021: } /* --- end-of-function rastbegin() --- */
        !          7022: 
        !          7023: 
        !          7024: /* ==========================================================================
        !          7025:  * Function:	rastarray ( expression, size, basesp, arg1, arg2, arg3 )
        !          7026:  * Purpose:	\array handler, returns a subraster corresponding to array
        !          7027:  *		expression (immediately following \array) at font size
        !          7028:  * --------------------------------------------------------------------------
        !          7029:  * Arguments:	expression (I/O) char **  to first char of null-terminated
        !          7030:  *				string immediately following \array to be
        !          7031:  *				rasterized, and returning ptr immediately
        !          7032:  *				following last character processed.
        !          7033:  *		size (I)	int containing 0-4 default font size
        !          7034:  *		basesp (I)	subraster *  to character (or subexpression)
        !          7035:  *				immediately preceding \array
        !          7036:  *				(unused, but passed for consistency)
        !          7037:  *		arg1 (I)	int unused
        !          7038:  *		arg2 (I)	int unused
        !          7039:  *		arg3 (I)	int unused
        !          7040:  * --------------------------------------------------------------------------
        !          7041:  * Returns:	( subraster * )	ptr to subraster corresponding to array
        !          7042:  *				expression, or NULL for any parsing error
        !          7043:  * --------------------------------------------------------------------------
        !          7044:  * Notes:     o	Summary of syntax...
        !          7045:  *			\array{3,lcrBC$a&b&c\\d&e&f\\etc}
        !          7046:  *	      o	The 3,lcrBC$ part is an optional "preamble".  The lcr means
        !          7047:  *		what you think, i.e., "horizontal" left,center,right
        !          7048:  *		justification down corresponding column.  The new BC means
        !          7049:  *		"vertical" baseline,center justification across corresponding
        !          7050:  *		row.  The leading 3 specifies the font size 0-4 to be used.
        !          7051:  *		You may also specify +1,-1,+2,-2, etc, which is used as an
        !          7052:  *		increment to the current font size, e.g., -1,lcr$ uses
        !          7053:  *		one font size smaller than current.  Without a leading
        !          7054:  *		+ or -, the font size is "absolute".
        !          7055:  *	      o	The preamble can also be just lcrBC$ without a leading
        !          7056:  *		size-part, or just 3$ without a trailing lcrBC-part.
        !          7057:  *		The default size is whatever is current, and the
        !          7058:  *		default justification is c(entered) and B(aseline).
        !          7059:  * ======================================================================= */
        !          7060: /* --- entry point --- */
        !          7061: subraster *rastarray ( char **expression, int size, subraster *basesp,
        !          7062: 			int arg1, int arg2, int arg3 )
        !          7063: {
        !          7064: /* -------------------------------------------------------------------------
        !          7065: Allocations and Declarations
        !          7066: -------------------------------------------------------------------------- */
        !          7067: char	*texsubexpr(),  subexpr[8210], *exprptr, /* parse array subexpr */
        !          7068: 	 subtok[4096], *subptr=subtok,	/* & or \\ inside { }'s not a delim*/
        !          7069: 	 token[4096],  *tokptr=token,	/* token from subexpr to rasterize */
        !          7070: 	*preamble(),   *preptr=token;	/*process optional size,lcr preamble*/
        !          7071: char	*coldelim="&", *rowdelim="\\";	/* need escaped rowdelim */
        !          7072: int	maxarraysz = 64;		/* max #rows, cols */
        !          7073: 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 */
        !          7074: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        !          7075: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        !          7076: 	  hline[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* hline above row? */
        !          7077: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        !          7078: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        !          7079: 	  vline[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*vline left of col?*/
        !          7080: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        !          7081: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        !          7082:        colwidth[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*widest tokn in col*/
        !          7083: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        !          7084: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        !          7085:       rowheight[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* "highest" in row */
        !          7086: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        !          7087: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        !          7088:      fixcolsize[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*1=fixed col width*/
        !          7089: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        !          7090: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        !          7091:      fixrowsize[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*1=fixed row height*/
        !          7092: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        !          7093: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        !          7094:       rowbaseln[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* baseline for row */
        !          7095: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        !          7096: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        !          7097:       rowcenter[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*true = vcenter row*/
        !          7098: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        !          7099: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
        !          7100: static int /* --- propagate global values across arrays --- */
        !          7101:        gjustify[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* -1,0,+1 = l,c,r */
        !          7102: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        !          7103: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        !          7104:       gcolwidth[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*widest tokn in col*/
        !          7105: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        !          7106: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        !          7107:      growheight[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* "highest" in row */
        !          7108: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        !          7109: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        !          7110:     gfixcolsize[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*1=fixed col width*/
        !          7111: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        !          7112: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        !          7113:     gfixrowsize[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*1=fixed row height*/
        !          7114: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        !          7115: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        !          7116:      growcenter[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*true = vcenter row*/
        !          7117: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        !          7118: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
        !          7119: int	rowglobal=0, colglobal=0,	/* true to set global values */
        !          7120: 	rowpropagate=0, colpropagate=0;	/* true if propagating values */
        !          7121: int	irow,nrows=0, icol,ncols[65],	/*#rows in array, #cols in each row*/
        !          7122: 	maxcols=0;			/* max# cols in any single row */
        !          7123: int	itoken, ntokens=0,		/* index, total #tokens in array */
        !          7124: 	subtoklen=0,			/* strlen of {...} subtoken */
        !          7125: 	istokwhite=1,			/* true if token all whitespace */
        !          7126: 	nnonwhite=0;			/* #non-white tokens */
        !          7127: int	isescape=0,wasescape=0,		/* current,prev chars escape? */
        !          7128: 	ischarescaped=0,		/* is current char escaped? */
        !          7129: 	nescapes=0;			/* #consecutive escapes */
        !          7130: subraster *rasterize(), *toksp[1025],	/* rasterize tokens */
        !          7131: 	*new_subraster(), *arraysp=NULL; /* subraster for entire array */
        !          7132: raster	*arrayrp=NULL;			/* raster for entire array */
        !          7133: int	delete_subraster();		/* free toksp[] workspace at eoj */
        !          7134: int	rowspace=2, colspace=4,		/* blank space between rows, cols */
        !          7135: 	hspace=1, vspace=1;		/*space to accommodate hline,vline*/
        !          7136: int	width=0, height=0,		/* width,height of array */
        !          7137: 	leftcol=0, toprow=0;		/*upper-left corner for cell in it*/
        !          7138: int	rastput();			/* embed tokens/cells in array */
        !          7139: int	rule_raster();			/* draw hlines and vlines in array */
        !          7140: char	*hlchar="\\hline", *hdchar="\\hdash"; /* token signals hline */
        !          7141: char	*texchar(), hltoken[1025];	/* extract \hline from token */
        !          7142: int	ishonly=0, hltoklen, minhltoklen=3; /*flag, token must be \hl or \hd*/
        !          7143: int	isnewrow=1;			/* true for new row */
        !          7144: int	pixsz = 1;			/*default #bits per pixel, 1=bitmap*/
        !          7145: /* -------------------------------------------------------------------------
        !          7146: Macros to determine extra raster space required for vline/hline
        !          7147: -------------------------------------------------------------------------- */
        !          7148: #define	vlinespace(icol) \
        !          7149: 	( vline[icol] == 0?  0 :	/* no vline so no space needed */   \
        !          7150: 	  ( icol<1 || icol>=maxcols? vspace+(colspace+1)/2 : vspace ) )
        !          7151: #define	hlinespace(irow) \
        !          7152: 	( hline[irow] == 0?  0 :	/* no hline so no space needed */   \
        !          7153: 	  ( irow<1 || irow>=nrows? hspace+(rowspace+1)/2 : hspace ) )
        !          7154: /* -------------------------------------------------------------------------
        !          7155: Obtain array subexpression
        !          7156: -------------------------------------------------------------------------- */
        !          7157: /* --- parse for array subexpression, and bump expression past it --- */
        !          7158: subexpr[1] = *subexpr = ' ';		/* set two leading blanks */
        !          7159: *expression = texsubexpr(*expression,subexpr+2,0,"{","}",0,0);
        !          7160: if ( msglevel>=29 && msgfp!=NULL )	/* debugging, display array */
        !          7161:   fprintf(msgfp,"rastarray> %.256s\n",subexpr+2);
        !          7162: if ( *(subexpr+2)=='\000' )		/* couldn't get subexpression */
        !          7163:   goto end_of_job;			/* nothing to do, so quit */
        !          7164: /* -------------------------------------------------------------------------
        !          7165: process optional size,lcr preamble if present
        !          7166: -------------------------------------------------------------------------- */
        !          7167: /* --- reset size, get lcr's, and push exprptr past preamble --- */
        !          7168: exprptr = preamble(subexpr+2,&size,preptr); /* reset size and get lcr's */
        !          7169: /* --- init with global values --- */
        !          7170: for(icol=0; icol<=maxarraysz; icol++) {	/* propagate global values... */
        !          7171:   justify[icol] = gjustify[icol];	/* -1,0,+1 = l,c,r */
        !          7172:   colwidth[icol] = gcolwidth[icol];	/* column width */
        !          7173:   rowheight[icol] = growheight[icol];	/* row height */
        !          7174:   fixcolsize[icol] = gfixcolsize[icol];	/* 1=fixed col width */
        !          7175:   fixrowsize[icol] = gfixrowsize[icol];	/* 1=fixed row height */
        !          7176:   rowcenter[icol] = growcenter[icol]; }	/* true = vcenter row */
        !          7177: /* --- process lcr's, etc in preamble --- */
        !          7178: itoken = 0;				/* debugging flag */
        !          7179: if ( msglevel>=29 && msgfp!=NULL )	/* debugging, display preamble */
        !          7180:  if ( *preptr != '\000' )		/* if we have one */
        !          7181:   fprintf(msgfp,"rastarray> preamble= \"%.256s\"\nrastarray> preamble: ",
        !          7182:   preptr);
        !          7183: irow = icol = 0;			/* init lcr counts */
        !          7184: while (  *preptr != '\000' )		/* check preamble text for lcr */
        !          7185:   {
        !          7186:   char	prepchar = *preptr;		/* current preamble character */
        !          7187:   int	prepcase = (islower(prepchar)?1:(isupper(prepchar)?2:0)); /*1,2,or 0*/
        !          7188:   if ( irow<maxarraysz && icol<maxarraysz )
        !          7189:    switch ( /*tolower*/(prepchar) )
        !          7190:     {  default: break;			/* just flush unrecognized chars */
        !          7191:       case 'l': justify[icol] = (-1);		/*left-justify this column*/
        !          7192: 		if (colglobal) gjustify[irow] = justify[irow]; break;
        !          7193:       case 'c': justify[icol] = (0);		/* center this column */
        !          7194: 		if (colglobal) gjustify[irow] = justify[irow]; break;
        !          7195:       case 'r': justify[icol] = (+1);		/* right-justify this col */
        !          7196: 		if (colglobal) gjustify[irow] = justify[irow]; break;
        !          7197:       case '|': vline[icol] += 1;   break;	/* solid vline left of col */
        !          7198:       case '.': vline[icol] = (-1); break;	/*dashed vline left of col */
        !          7199:       case 'b': prepchar='B'; prepcase=2;	/* alias for B */
        !          7200:       case 'B': break;				/* baseline-justify row */
        !          7201:       case 'v': prepchar='C'; prepcase=2;	/* alias for C */
        !          7202:       case 'C': rowcenter[irow] = 1;		/* vertically center row */
        !          7203: 		if (rowglobal) growcenter[irow] = rowcenter[irow]; break;
        !          7204:       case 'g': colglobal=1; prepcase=0; break;	/* set global col values */
        !          7205:       case 'G': rowglobal=1; prepcase=0; break;	/* set global row values */
        !          7206:       case '#': colglobal=rowglobal=1; break; }	/* set global col,row vals */
        !          7207:   if ( msglevel>=29 && msgfp!=NULL )	/* debugging */
        !          7208:     fprintf(msgfp," %c[%d]",prepchar,
        !          7209:     (prepcase==1?icol+1:(prepcase==2?irow+1:0)));
        !          7210:   preptr++;				/* check next char for lcr */
        !          7211:   itoken++;				/* #lcr's processed (debugging only)*/
        !          7212:   /* --- check for number or +number specifying colwidth or rowheight --- */
        !          7213:   if ( prepcase != 0 )			/* only check upper,lowercase */
        !          7214:    {
        !          7215:    int	ispropagate = (*preptr=='+'?1:0); /* leading + propagates width/ht */
        !          7216:    if ( ispropagate )			/* set row or col propagation */
        !          7217:      if ( prepcase == 1 ) colpropagate = 1; /* propagating col values */
        !          7218:      else if ( prepcase == 2 ) rowpropagate = 1; /* propagating row values */
        !          7219:    if ( !colpropagate && prepcase == 1 )
        !          7220:       {	colwidth[icol] = 0;		/* reset colwidth */
        !          7221: 	fixcolsize[icol] = 0; }		/* reset width flag */
        !          7222:    if ( !rowpropagate && prepcase == 2 )
        !          7223:       {	rowheight[irow] = 0;		/* reset row height */
        !          7224: 	fixrowsize[irow] = 0; }		/* reset height flag */
        !          7225:    if ( ispropagate ) preptr++;		/* bump past leading + */
        !          7226:    if ( isdigit(*preptr) )		/* digit follows character */
        !          7227:      { char *endptr = NULL;		/* preptr set to 1st char after num*/
        !          7228:        int size = (int)(strtol(preptr,&endptr,10)); /* interpret number */
        !          7229:        char *whchars="?wh";		/* debugging width/height labels */
        !          7230:        preptr = endptr;			/* skip over all digits */
        !          7231:        if ( size==0 || (size>=3&&size<=500) ) { /* sanity check */
        !          7232: 	int index;			/* icol,irow...maxarraysz index */
        !          7233: 	if ( prepcase == 1 )		/* lowercase signifies colwidth */
        !          7234: 	 for(index=icol; index<=maxarraysz; index++) { /*propagate col size*/
        !          7235: 	  colwidth[index] = size;	/* set colwidth to fixed size */
        !          7236: 	  fixcolsize[index] = (size>0?1:0); /* set fixed width flag */
        !          7237: 	  justify[index] = justify[icol]; /* and propagate justification */
        !          7238: 	  if ( colglobal ) {		/* set global values */
        !          7239: 	    gcolwidth[index] = colwidth[index]; /* set global col width */
        !          7240: 	    gfixcolsize[index] = fixcolsize[index]; /*set global width flag*/
        !          7241: 	    gjustify[index] = justify[icol]; } /* set global col justify */
        !          7242: 	  if ( !ispropagate ) break; }	/* don't propagate */
        !          7243: 	else				/* uppercase signifies rowheight */
        !          7244: 	 for(index=irow; index<=maxarraysz; index++) { /*propagate row size*/
        !          7245: 	  rowheight[index] = size;	/* set rowheight to size */
        !          7246: 	  fixrowsize[index] = (size>0?1:0); /* set fixed height flag */
        !          7247: 	  rowcenter[index] = rowcenter[irow]; /* and propagate row center */
        !          7248: 	  if ( rowglobal ) {		/* set global values */
        !          7249: 	    growheight[index] = rowheight[index]; /* set global row height */
        !          7250: 	    gfixrowsize[index] = fixrowsize[index]; /*set global height flag*/
        !          7251: 	    growcenter[index] = rowcenter[irow]; } /*set global row center*/
        !          7252: 	  if ( !ispropagate ) break; }	/* don't propagate */
        !          7253:         } /* --- end-of-if(size>=3&&size<=500) --- */
        !          7254:        if ( msglevel>=29 && msgfp!=NULL ) /* debugging */
        !          7255: 	 fprintf(msgfp,":%c=%d/fix#%d",whchars[prepcase],
        !          7256: 	 (prepcase==1?colwidth[icol]:rowheight[irow]),
        !          7257: 	 (prepcase==1?fixcolsize[icol]:fixrowsize[irow]));
        !          7258:      } /* --- end-of-if(isdigit()) --- */
        !          7259:    } /* --- end-of-if(prepcase!=0) --- */
        !          7260:   if ( prepcase == 1 ) icol++;		/* bump col if lowercase lcr */
        !          7261:     else if ( prepcase == 2 ) irow++;	/* bump row if uppercase BC */
        !          7262:   } /* --- end-of-while(*preptr!='\000') --- */
        !          7263: if ( msglevel>=29 && msgfp!=NULL )	/* debugging, emit final newline */
        !          7264:  if ( itoken > 0 )			/* if we have preamble */
        !          7265:   fprintf(msgfp,"\n");
        !          7266: /* -------------------------------------------------------------------------
        !          7267: tokenize and rasterize components  a & b \\ c & d \\ etc  of subexpr
        !          7268: -------------------------------------------------------------------------- */
        !          7269: /* --- rasterize tokens one at a time, and maintain row,col counts --- */
        !          7270: ncols[nrows] = 0;			/* no tokens/cols in top row yet */
        !          7271: while ( 1 )				/* scan chars till end */
        !          7272:   {
        !          7273:   /* --- local control flags --- */
        !          7274:   int	iseox = (*exprptr == '\000'),	/* null signals end-of-expression */
        !          7275: 	iseor = iseox,			/* \\ or eox signals end-of-row */
        !          7276: 	iseoc = iseor;			/* & or eor signals end-of-col */
        !          7277:   /* --- check for escapes --- */
        !          7278:   isescape = isthischar(*exprptr,ESCAPE); /* is current char escape? */
        !          7279:   wasescape= (!isnewrow&&isthischar(*(exprptr-1),ESCAPE)); /*prev char esc?*/
        !          7280:   nescapes = (wasescape?nescapes+1:0);	/* # preceding consecutive escapes */
        !          7281:   ischarescaped = (nescapes%2==0?0:1);	/* is current char escaped? */
        !          7282:   /* -----------------------------------------------------------------------
        !          7283:   check for {...} subexpression starting from where we are now
        !          7284:   ------------------------------------------------------------------------ */
        !          7285:   if ( *exprptr == '{'			/* start of {...} subexpression */
        !          7286:   &&   !ischarescaped )			/* if not escaped \{ */
        !          7287:     {
        !          7288:     subptr = texsubexpr(exprptr,subtok,4095,"{","}",1,1); /*entire subexpr*/
        !          7289:     subtoklen = strlen(subtok);		/* #chars in {...} */
        !          7290:     memcpy(tokptr,exprptr,subtoklen);	/* copy {...} to accumulated token */
        !          7291:     tokptr  += subtoklen;		/* bump tokptr to end of token */
        !          7292:     exprptr += subtoklen;		/* and bump exprptr past {...} */
        !          7293:     istokwhite = 0;			/* signal non-empty token */
        !          7294:     continue;				/* continue with char after {...} */
        !          7295:     } /* --- end-of-if(*exprptr=='{') --- */
        !          7296:   /* -----------------------------------------------------------------------
        !          7297:   check for end-of-row(\\) and/or end-of-col(&)
        !          7298:   ------------------------------------------------------------------------ */
        !          7299:   /* --- check for (escaped) end-of-row delimiter --- */
        !          7300:   if ( isescape && !ischarescaped )	/* current char is escaped */
        !          7301:     if ( isthischar(*(exprptr+1),rowdelim) /* next char is rowdelim */
        !          7302:     ||   *(exprptr+1) == '\000' )	/* or a pathological null */
        !          7303:       {	iseor = 1;			/* so set end-of-row flag */
        !          7304: 	wasescape=isescape=nescapes = 0; } /* reset flags for new row */
        !          7305:   /* --- check for end-of-col delimiter --- */
        !          7306:   if (iseor				/* end-of-row signals end-of-col */
        !          7307:   ||  (!ischarescaped&&isthischar(*exprptr,coldelim))) /*or unescaped coldel*/
        !          7308:       iseoc = 1;			/* so set end-of-col flag */
        !          7309:   /* -----------------------------------------------------------------------
        !          7310:   rasterize completed token
        !          7311:   ------------------------------------------------------------------------ */
        !          7312:   if ( iseoc )				/* we have a completed token */
        !          7313:     {
        !          7314:     *tokptr = '\000';			/* first, null-terminate token */
        !          7315:     /* --- check first token in row for \hline or \hdash --- */
        !          7316:     ishonly = 0;			/*init for token not only an \hline*/
        !          7317:     if ( ncols[nrows] == 0 )		/*\hline must be first token in row*/
        !          7318:       {
        !          7319:       tokptr=token; skipwhite(tokptr);	/* skip whitespace after // */
        !          7320:       tokptr = texchar(tokptr,hltoken);	/* extract first char from token */
        !          7321:       hltoklen = strlen(hltoken);	/* length of first char */
        !          7322:       if ( hltoklen >= minhltoklen )	/*token must be at least \hl or \hd*/
        !          7323: 	if ( memcmp(hlchar,hltoken,hltoklen) == 0 ) /* we have an \hline */
        !          7324: 	   hline[nrows] += 1;		/* bump \hline count for row */
        !          7325: 	else if ( memcmp(hdchar,hltoken,hltoklen) == 0 ) /*we have an \hdash*/
        !          7326: 	   hline[nrows] = (-1);		/* set \hdash flag for row */
        !          7327:       if ( hline[nrows] != 0 )		/* \hline or \hdash prefixes token */
        !          7328: 	{ skipwhite(tokptr);		/* flush whitespace after \hline */
        !          7329: 	  if ( *tokptr == '\000'	/* end-of-expression after \hline */
        !          7330: 	  ||   isthischar(*tokptr,coldelim) ) /* or unescaped coldelim */
        !          7331: 	    istokwhite = ishonly = 1;	/* so token contains \hline only */
        !          7332: 	  else				/* token contains more than \hline */
        !          7333: 	    strcpy(token,tokptr); }	/* so flush \hline from token */
        !          7334:       } /* --- end-of-if(ncols[nrows]==0) --- */
        !          7335:     /* --- rasterize completed token --- */
        !          7336:     toksp[ntokens] = (istokwhite? NULL : /* don't rasterize empty token */
        !          7337:       rasterize(token,size));		/* rasterize non-empty token */
        !          7338:     if ( toksp[ntokens] != NULL )	/* have a rasterized token */
        !          7339:       nnonwhite++;			/* bump rasterized token count */
        !          7340:     /* --- maintain colwidth[], rowheight[] max, and rowbaseln[] --- */
        !          7341:     if ( toksp[ntokens] != NULL )	/* we have a rasterized token */
        !          7342:       {
        !          7343:       /* --- update max token "height" in current row, and baseline --- */
        !          7344:       int twidth = ((toksp[ntokens])->image)->width,  /* width of token */
        !          7345: 	theight = ((toksp[ntokens])->image)->height, /* height of token */
        !          7346: 	tbaseln =  (toksp[ntokens])->baseline, /* baseline of token */
        !          7347: 	rheight = rowheight[nrows],	/* current max height for row */
        !          7348: 	rbaseln = rowbaseln[nrows];	/* current baseline for max height */
        !          7349:       if ( 0 || fixrowsize[nrows]==0 )	/* rowheight not fixed */
        !          7350:        rowheight[nrows] = /*max2( rheight,*/( /* current (max) rowheight */
        !          7351: 	max2(rbaseln+1,tbaseln+1)	/* max height above baseline */
        !          7352: 	+ max2(rheight-rbaseln-1,theight-tbaseln-1) ); /* plus max below */
        !          7353:       rowbaseln[nrows] = max2(rbaseln,tbaseln); /*max space above baseline*/
        !          7354:       /* --- update max token width in current column --- */
        !          7355:       icol = ncols[nrows];		/* current column index */
        !          7356:       if ( 0 || fixcolsize[icol]==0 )	/* colwidth not fixed */
        !          7357:        colwidth[icol] = max2(colwidth[icol],twidth); /*widest token in col*/
        !          7358:       } /* --- end-of-if(toksp[]!=NULL) --- */
        !          7359:     /* --- bump counters --- */
        !          7360:     if ( !ishonly )			/* don't count only an \hline */
        !          7361:       {	ntokens++;			/* bump total token count */
        !          7362: 	ncols[nrows] += 1; }		/* and bump #cols in current row */
        !          7363:     /* --- get ready for next token --- */
        !          7364:     tokptr = token;			/* reset ptr for next token */
        !          7365:     istokwhite = 1;			/* next token starts all white */
        !          7366:     } /* --- end-of-if(iseoc) --- */
        !          7367:   /* -----------------------------------------------------------------------
        !          7368:   bump row as necessary
        !          7369:   ------------------------------------------------------------------------ */
        !          7370:   if ( iseor )				/* we have a completed row */
        !          7371:     {
        !          7372:     maxcols = max2(maxcols,ncols[nrows]); /* max# cols in array */
        !          7373:     if ( ncols[nrows]>0 || hline[nrows]==0 ) /*ignore row with only \hline*/
        !          7374:       nrows++;				/* bump row count */
        !          7375:     ncols[nrows] = 0;			/* no cols in this row yet */
        !          7376:     if ( !iseox )			/* don't have a null yet */
        !          7377:       {	exprptr++;			/* bump past extra \ in \\ delim */
        !          7378: 	iseox = (*exprptr == '\000'); }	/* recheck for pathological \null */
        !          7379:     isnewrow = 1;			/* signal start of new row */
        !          7380:     } /* --- end-of-if(iseor) --- */
        !          7381:   else
        !          7382:     isnewrow = 0;			/* no longer first col of new row */
        !          7383:   /* -----------------------------------------------------------------------
        !          7384:   quit when done, or accumulate char in token and proceed to next char
        !          7385:   ------------------------------------------------------------------------ */
        !          7386:   /* --- quit when done --- */
        !          7387:   if ( iseox ) break;			/* null terminator signalled done */
        !          7388:   /* --- accumulate chars in token --- */
        !          7389:   if ( !iseoc )				/* don't accumulate delimiters */
        !          7390:     { *tokptr++ = *exprptr;		/* accumulate non-delim char */
        !          7391:       if ( !isthischar(*exprptr,WHITESPACE) ) /* this token isn't empty */
        !          7392: 	istokwhite = 0; }		/* so reset flag to rasterize it */
        !          7393:   /* --- ready for next char --- */
        !          7394:   exprptr++;				/* bump ptr */
        !          7395:   } /* --- end-of-while(*exprptr!='\000') --- */
        !          7396: /* --- make sure we got something to do --- */
        !          7397: if ( nnonwhite < 1 )			/* completely empty array */
        !          7398:   goto end_of_job;			/* NULL back to caller */
        !          7399: /* -------------------------------------------------------------------------
        !          7400: determine dimensions of array raster and allocate it
        !          7401: -------------------------------------------------------------------------- */
        !          7402: /* --- adjust colspace --- */
        !          7403: colspace = 2 + 2*size;			/* temp kludge */
        !          7404: /* --- reset propagated sizes at boundaries of array --- */
        !          7405: colwidth[maxcols] = rowheight[nrows] = 0; /* reset explicit 0's at edges */
        !          7406: /* --- determine width of array raster --- */
        !          7407: width = colspace*(maxcols-1);		/* empty space between cols */
        !          7408: if ( msglevel>=29 && msgfp!=NULL )	/* debugging */
        !          7409:   fprintf(msgfp,"rastarray> %d cols,  widths: ",maxcols);
        !          7410: for ( icol=0; icol<=maxcols; icol++ )	/* and for each col */
        !          7411:   { width += colwidth[icol];		/*width of this col (0 for maxcols)*/
        !          7412:     width += vlinespace(icol);		/*plus space for vline, if present*/
        !          7413:     if ( msglevel>=29 && msgfp!=NULL )	/* debugging */
        !          7414:      fprintf(msgfp," %d=%2d+%d",icol+1,colwidth[icol],(vlinespace(icol))); }
        !          7415: /* --- determine height of array raster --- */
        !          7416: height = rowspace*(nrows-1);		/* empty space between rows */
        !          7417: if ( msglevel>=29 && msgfp!=NULL )	/* debugging */
        !          7418:   fprintf(msgfp,"\nrastarray> %d rows, heights: ",nrows);
        !          7419: for ( irow=0; irow<=nrows; irow++ )	/* and for each row */
        !          7420:   { height += rowheight[irow];		/*height of this row (0 for nrows)*/
        !          7421:     height += hlinespace(irow);		/*plus space for hline, if present*/
        !          7422:     if ( msglevel>=29 && msgfp!=NULL )	/* debugging */
        !          7423:      fprintf(msgfp," %d=%2d+%d",irow+1,rowheight[irow],(hlinespace(irow))); }
        !          7424: /* --- allocate subraster and raster for array --- */
        !          7425: if ( msglevel>=29 && msgfp!=NULL )	/* debugging */
        !          7426:   fprintf(msgfp,"\nrastarray> tot width=%d(colspc=%d) height=%d(rowspc=%d)\n",
        !          7427:   width,colspace, height,rowspace);
        !          7428: if ( (arraysp=new_subraster(width,height,pixsz)) /* allocate new subraster */
        !          7429: ==   NULL )  goto end_of_job;		/* quit if failed */
        !          7430: /* --- initialize subraster parameters --- */
        !          7431: arraysp->type = IMAGERASTER;		/* image */
        !          7432: arraysp->symdef = NULL;			/* not applicable for image */
        !          7433: arraysp->baseline=min2(height/2+5,height-1); /*is a little above center good?*/
        !          7434: arraysp->size = size;			/* size (probably unneeded) */
        !          7435: arrayrp = arraysp->image;		/* raster embedded in subraster */
        !          7436: /* -------------------------------------------------------------------------
        !          7437: embed tokens/cells in array
        !          7438: -------------------------------------------------------------------------- */
        !          7439: itoken = 0;				/* start with first token */
        !          7440: toprow = 0;				/* start at top row of array */
        !          7441: for ( irow=0; irow<=nrows; irow++ )	/*tokens were accumulated row-wise*/
        !          7442:   {
        !          7443:   /* --- initialization for row --- */
        !          7444:   int	baseline = rowbaseln[irow];	/* baseline for this row */
        !          7445:   if ( hline[irow] != 0 )		/* need hline above this row */
        !          7446:     { int hrow = (irow<1? 0 : toprow - rowspace/2); /* row for hline */
        !          7447:       if ( irow >= nrows ) hrow = height-1; /* row for bottom hline */
        !          7448:       rule_raster(arrayrp,hrow,0,width,1,(hline[irow]<0?1:0)); } /* hline */
        !          7449:   if ( irow >= nrows ) break;		/*just needed \hline for irow=nrows*/
        !          7450:   toprow += hlinespace(irow);		/* space for hline above irow */
        !          7451:   leftcol = 0;				/* start at leftmost column */
        !          7452:   for ( icol=0; icol<ncols[irow]; icol++ ) /* go through cells in this row */
        !          7453:     {
        !          7454:     subraster *tsp = toksp[itoken];	/* token that belongs in this cell */
        !          7455:     if ( tsp != NULL )			/* have a rasterized cell token */
        !          7456:       {
        !          7457:       /* --- local parameters --- */
        !          7458:       int cwidth = colwidth[icol],	/* total column width */
        !          7459: 	  twidth = (tsp->image)->width,	/* token width */
        !          7460: 	  theight= (tsp->image)->height, /* token height */
        !          7461: 	  tokencol = 0,			/*H offset (init for left justify)*/
        !          7462: 	  tokenrow = baseline - tsp->baseline;/*V offset (init for baseline)*/
        !          7463:       /* --- adjust leftcol for vline to left of icol, if present ---- */
        !          7464:       leftcol += vlinespace(icol);	/* space for vline to left of col */
        !          7465:       /* --- reset justification (if not left-justified) --- */
        !          7466:       if ( justify[icol] == 0 )		/* but user wants it centered */
        !          7467: 	  tokencol = (cwidth-twidth+1)/2; /* so split margin left/right */
        !          7468:       else if ( justify[icol] == 1 )	/* or user wants right-justify */
        !          7469: 	  tokencol = cwidth-twidth;	/* so put entire margin at left */
        !          7470:       /* --- reset vertical centering (if not baseline-aligned) --- */
        !          7471:       if ( rowcenter[irow] )		/* center cells in row vertically */
        !          7472: 	  tokenrow = (rowheight[irow]-theight)/2; /* center row */
        !          7473:       /* --- embed token raster at appropriate place in array raster --- */
        !          7474:       rastput(arrayrp,tsp->image,	/* overlay cell token in array */
        !          7475: 	  toprow+ tokenrow,		/*with aligned baseline or centered*/
        !          7476: 	  leftcol+tokencol, 1);		/* and justified as requested */
        !          7477:       } /* --- end-of-if(tsp!=NULL) --- */
        !          7478:     itoken++;				/* bump index for next cell */
        !          7479:     leftcol += colwidth[icol] + colspace /*move leftcol right for next col*/
        !          7480:       /* + vlinespace(icol) */ ; /*don't add space for vline to left of col*/
        !          7481:     } /* --- end-of-for(icol) --- */
        !          7482:   toprow += rowheight[irow] + rowspace;	/* move toprow down for next row */
        !          7483:   } /* --- end-of-for(irow) --- */
        !          7484: /* -------------------------------------------------------------------------
        !          7485: draw vlines as necessary
        !          7486: -------------------------------------------------------------------------- */
        !          7487: leftcol = 0;				/* start at leftmost column */
        !          7488: for ( icol=0; icol<=maxcols; icol++ )	/* check each col for a vline */
        !          7489:   {
        !          7490:   if ( vline[icol] != 0 )		/* need vline to left of this col */
        !          7491:     { int vcol = (icol<1? 0 : leftcol - colspace/2); /* column for vline */
        !          7492:       if ( icol >= maxcols ) vcol = width-1; /*column for right edge vline*/
        !          7493:       rule_raster(arrayrp,0,vcol,1,height,(vline[icol]<0?2:0)); } /* vline */
        !          7494:   leftcol += vlinespace(icol);		/* space for vline to left of col */
        !          7495:   if ( icol < maxcols )			/* don't address past end of array */
        !          7496:     leftcol += colwidth[icol] + colspace; /*move leftcol right for next col*/
        !          7497:   } /* --- end-of-for(icol) --- */
        !          7498: /* -------------------------------------------------------------------------
        !          7499: free workspace and return final result to caller
        !          7500: -------------------------------------------------------------------------- */
        !          7501: end_of_job:
        !          7502:   /* --- free workspace --- */
        !          7503:   if ( ntokens > 0 )			/* if we have workspace to free */
        !          7504:     while ( --ntokens >= 0 )		/* free each token subraster */
        !          7505:       if ( toksp[ntokens] != NULL )	/* if we rasterized this cell */
        !          7506: 	delete_subraster(toksp[ntokens]); /* then free it */
        !          7507:   /* --- return final result to caller --- */
        !          7508:   return ( arraysp );
        !          7509: } /* --- end-of-function rastarray() --- */
        !          7510: 
        !          7511: 
        !          7512: /* ==========================================================================
        !          7513:  * Function:	rastpicture ( expression, size, basesp, arg1, arg2, arg3 )
        !          7514:  * Purpose:	\picture handler, returns subraster corresponding to picture
        !          7515:  *		expression (immediately following \picture) at font size
        !          7516:  * --------------------------------------------------------------------------
        !          7517:  * Arguments:	expression (I/O) char **  to first char of null-terminated
        !          7518:  *				string immediately following \picture to be
        !          7519:  *				rasterized, and returning ptr immediately
        !          7520:  *				following last character processed.
        !          7521:  *		size (I)	int containing 0-4 default font size
        !          7522:  *		basesp (I)	subraster *  to character (or subexpression)
        !          7523:  *				immediately preceding \picture
        !          7524:  *				(unused, but passed for consistency)
        !          7525:  *		arg1 (I)	int unused
        !          7526:  *		arg2 (I)	int unused
        !          7527:  *		arg3 (I)	int unused
        !          7528:  * --------------------------------------------------------------------------
        !          7529:  * Returns:	( subraster * )	ptr to subraster corresponding to picture
        !          7530:  *				expression, or NULL for any parsing error
        !          7531:  * --------------------------------------------------------------------------
        !          7532:  * Notes:     o	Summary of syntax...
        !          7533:  *		  \picture(width,height){(x,y){pic_elem}~(x,y){pic_elem}~etc}
        !          7534:  *	      o	
        !          7535:  * ======================================================================= */
        !          7536: /* --- entry point --- */
        !          7537: subraster *rastpicture ( char **expression, int size, subraster *basesp,
        !          7538: 			int arg1, int arg2, int arg3 )
        !          7539: {
        !          7540: /* -------------------------------------------------------------------------
        !          7541: Allocations and Declarations
        !          7542: -------------------------------------------------------------------------- */
        !          7543: char	*texsubexpr(), picexpr[2049], *picptr=picexpr, /* picture {expre} */
        !          7544: 	putexpr[256], *putptr,*multptr,	/*[multi]put (x,y[;xinc,yinc;num])*/
        !          7545: 	pream[64], *preptr,		/* optional put preamble */
        !          7546: 	picelem[1025];			/* picture element following put */
        !          7547: subraster   *rasterize(), *picelemsp=NULL, /* rasterize picture elements */
        !          7548: 	*new_subraster(), *picturesp=NULL, /* subraster for entire picture */
        !          7549: 	*oldworkingbox = workingbox;	/* save working box on entry */
        !          7550: raster	*picturerp=NULL;		/* raster for entire picture */
        !          7551: int	delete_subraster();		/* free picelemsp[] workspace */
        !          7552: int	pixsz = 1;			/* pixels are one bit each */
        !          7553: double	strtod(),			/* convert ascii params to doubles */
        !          7554: 	x=0.0,y=0.0,			/* x,y-coords for put,multiput*/
        !          7555: 	xinc=0.0,yinc=0.0;		/* x,y-incrementss for multiput*/
        !          7556: int	width=0,  height=0,		/* #pixels width,height of picture */
        !          7557: 	ewidth=0, eheight=0,		/* pic element width,height */
        !          7558: 	ix=0,xpos=0, iy=0,ypos=0,	/* mimeTeX x,y pixel coords */
        !          7559: 	num=1, inum;			/* number reps, index of element */
        !          7560: int	iscenter=0;			/* center or lowerleft put position*/
        !          7561: int	*oldworkingparam = workingparam, /* save working param on entry */
        !          7562: 	origin = 0;			/* x,yinc ++=00 +-=01 -+=10 --=11 */
        !          7563: int	rastput();			/* embed elements in picture */
        !          7564: int	type_raster();			/* display debugging output */
        !          7565: /* -------------------------------------------------------------------------
        !          7566: First obtain (width,height) arguments immediately following \picture command
        !          7567: -------------------------------------------------------------------------- */
        !          7568: /* --- parse for (width,height) arguments, and bump expression past it --- */
        !          7569: *expression = texsubexpr(*expression,putexpr,254,"(",")",0,0);
        !          7570: if ( *putexpr == '\000' ) goto end_of_job; /* couldn't get (width,height) */
        !          7571: /* --- now interpret width,height returned in putexpr --- */
        !          7572: if ( (putptr=strchr(putexpr,',')) != NULL ) /* look for ',' in width,height*/
        !          7573:   *putptr = '\000';			/* found it, so replace ',' by '\0'*/
        !          7574: width=height = iround(unitlength*strtod(putexpr,NULL)); /*width pixels*/
        !          7575: if ( putptr != NULL )			/* 2nd arg, if present, is height */
        !          7576:   height = iround(unitlength*strtod(putptr+1,NULL)); /*in pixels*/
        !          7577: /* -------------------------------------------------------------------------
        !          7578: Then obtain entire picture {...} subexpression following (width,height)
        !          7579: -------------------------------------------------------------------------- */
        !          7580: /* --- parse for picture subexpression, and bump expression past it --- */
        !          7581: *expression = texsubexpr(*expression,picexpr,2047,"{","}",0,0);
        !          7582: if ( *picexpr == '\000' ) goto end_of_job; /* couldn't get {pic_elements} */
        !          7583: /* -------------------------------------------------------------------------
        !          7584: allocate subraster and raster for complete picture
        !          7585: -------------------------------------------------------------------------- */
        !          7586: /* --- sanity check on width,height args --- */
        !          7587: if ( width < 2 ||  width > 600
        !          7588: ||  height < 2 || height > 600 ) goto end_of_job;
        !          7589: /* --- allocate and initialize subraster for constructed picture --- */
        !          7590: if ( (picturesp=new_subraster(width,height,pixsz)) /*allocate new subraster*/
        !          7591: ==   NULL )  goto end_of_job;		/* quit if failed */
        !          7592: workingbox = picturesp;			/* set workingbox to our picture */
        !          7593: /* --- initialize picture subraster parameters --- */
        !          7594: picturesp->type = IMAGERASTER;		/* image */
        !          7595: picturesp->symdef = NULL;		/* not applicable for image */
        !          7596: picturesp->baseline = height/2 + 2;	/* is a little above center good? */
        !          7597: picturesp->size = size;			/* size (probably unneeded) */
        !          7598: picturerp = picturesp->image;		/* raster embedded in subraster */
        !          7599: if ( msgfp!=NULL && msglevel>=29 )	/* debugging */
        !          7600:   fprintf(msgfp,"picture> width,height=%d,%d\n",width,height);
        !          7601: /* -------------------------------------------------------------------------
        !          7602: parse out each picture element, rasterize it, and place it in picture
        !          7603: -------------------------------------------------------------------------- */
        !          7604: while ( *picptr != '\000' )		/* until we run out of pic_elems */
        !          7605:   {
        !          7606:   /* -----------------------------------------------------------------------
        !          7607:   first obtain leading \[multi]put(x,y[;xinc,yinc;num]) args for pic_elem
        !          7608:   ------------------------------------------------------------------------ */
        !          7609:   /* --- init default values in case not explicitly supplied in args --- */
        !          7610:   x=y=0.0;  xinc=yinc=0.0;  num=1;	/* init default values */
        !          7611:   iscenter = origin = 0;		/* center, origin */
        !          7612:   /* --- get (pream$x,y;xinc,yinc;num ) args and bump picptr past it --- */
        !          7613:   while ( *picptr != '\000' )		/* skip invalid chars preceding ( */
        !          7614:     if ( *picptr == '(' ) break;	/* found opening ( */
        !          7615:     else picptr++;			/* else skip invalid char */
        !          7616:   picptr = texsubexpr(picptr,putexpr,254,"(",")",0,0);
        !          7617:   if ( *putexpr == '\000' ) goto end_of_job; /* couldn't get (x,y) */
        !          7618:   /* --- first look for $-terminated or for any non-digit preamble --- */
        !          7619:   *pream = '\000';			/* init preamble as empty string */
        !          7620:   if ( (putptr=strchr(putexpr,'$')) != NULL ) /*check for $ pream terminator*/
        !          7621:     { *putptr++ = '\000';		/* replace $ by '\0', bump past $ */
        !          7622:       strcpy(pream,putexpr); }		/* copy leading preamble from put */
        !          7623:   else					/* look for any non-digit preamble */
        !          7624:     { for ( preptr=pream,putptr=putexpr; ; putptr++ )
        !          7625: 	if ( *putptr == '\000'		/* end-of-putdata signalled */
        !          7626: 	||   !isalpha((int)(*putptr)) ) break; /* or found non-alpha char */
        !          7627: 	else *preptr++ = *putptr;	/* copy alpha char to preamble */
        !          7628:       *preptr = '\000'; }		/* null-terminate preamble */
        !          7629:   /* --- interpret preamble --- */
        !          7630:   for ( preptr=pream; ; preptr++ )	/* examine each preamble char */
        !          7631:     if ( *preptr == '\000' ) break;	/* end-of-preamble signalled */
        !          7632:     else switch ( tolower(*preptr) )	/* check lowercase preamble char */
        !          7633:       {
        !          7634:       default: break;			/* unrecognized flag */
        !          7635:       case 'c': iscenter=1; break;	/* center pic_elem at x,y coords */
        !          7636:       } /* --- end-of-switch --- */
        !          7637:   /* --- interpret x,y;xinc,yinc;num following preamble --- */      
        !          7638:   if ( *putptr != '\000' )		/*check for put data after preamble*/
        !          7639:    {
        !          7640:    /* --- first squeeze preamble out of put expression --- */
        !          7641:    if ( *pream != '\000' ) strcpy(putexpr,putptr); /* squeeze out preamble */
        !          7642:    /* --- interpret x,y --- */
        !          7643:    if ( (multptr=strchr(putexpr,';')) != NULL ) /*semicolon signals multiput*/
        !          7644:      *multptr = '\000';			/* replace semicolon by '\0' */
        !          7645:    if ( (putptr=strchr(putexpr,',')) != NULL ) /* comma separates x,y */
        !          7646:      *putptr = '\000';			/* replace comma by '\0'  */
        !          7647:    if ( *putexpr != '\000' )		/* leading , may be placeholder */
        !          7648:      x = unitlength*strtod(putexpr,NULL); /* x coord in pixels*/
        !          7649:    if ( putptr != NULL )		/* 2nd arg, if present, is y coord */
        !          7650:      y = unitlength*strtod(putptr+1,NULL); /* in pixels */
        !          7651:    /* --- interpret xinc,yinc,num if we have a multiput --- */
        !          7652:    if ( multptr != NULL )		/* found ';' signalling multiput */
        !          7653:      {
        !          7654:      if ( (preptr=strchr(multptr+1,';')) != NULL ) /* ';' preceding num arg*/
        !          7655:        *preptr = '\000';		/* replace ';' by '\0' */
        !          7656:      if ( (putptr=strchr(multptr+1,',')) != NULL ) /* ',' between xinc,yinc*/
        !          7657:        *putptr = '\000';		/* replace ',' by '\0' */
        !          7658:      if ( *(multptr+1) != '\000' )	/* leading , may be placeholder */
        !          7659:        xinc = unitlength*strtod(multptr+1,NULL); /* xinc in pixels */
        !          7660:      if ( putptr != NULL )		/* 2nd arg, if present, is yinc */
        !          7661:        yinc = unitlength*strtod(putptr+1,NULL); /* in user pixels */
        !          7662:      num = (preptr==NULL? 999 : atoi(preptr+1)); /*explicit num val or 999*/
        !          7663:      } /* --- end-of-if(multptr!=NULL) --- */
        !          7664:    } /* --- end-of-if(*preptr!='\000') --- */
        !          7665:   if ( msgfp!=NULL && msglevel>=29 )	/* debugging */
        !          7666:     fprintf(msgfp,
        !          7667:     "picture> pream;x,y;xinc,yinc;num=\"%s\";%.2f,%.2f;%.2f,%.2f;%d\n",
        !          7668:     pream,x,y,xinc,yinc,num);
        !          7669:   /* -----------------------------------------------------------------------
        !          7670:   now obtain {...} picture element following [multi]put, and rasterize it
        !          7671:   ------------------------------------------------------------------------ */
        !          7672:   /* --- parse for {...} picture element and bump picptr past it --- */
        !          7673:   picptr = texsubexpr(picptr,picelem,1023,"{","}",0,0);
        !          7674:   if ( *picelem == '\000' ) goto end_of_job; /* couldn't get {pic_elem} */
        !          7675:   if ( msgfp!=NULL && msglevel>=29 )	/* debugging */
        !          7676:     fprintf(msgfp, "picture> picelem=\"%.50s\"\n",picelem);
        !          7677:   /* --- rasterize picture element --- */
        !          7678:   origin = 0;				/* init origin as working param */
        !          7679:   workingparam = &origin;		/* and point working param to it */
        !          7680:   picelemsp = rasterize(picelem,size);	/* rasterize picture element */
        !          7681:   if ( picelemsp == NULL ) continue;	/* failed to rasterize, skip elem */
        !          7682:   ewidth  = (picelemsp->image)->width;	/* width of element, in pixels */
        !          7683:   eheight = (picelemsp->image)->height;	/* height of element, in pixels */
        !          7684:   if ( origin == 55 ) iscenter = 1;	/* origin set to (.5,.5) for center*/
        !          7685:   if ( msgfp!=NULL && msglevel>=29 )	/* debugging */
        !          7686:     { fprintf(msgfp, "picture> ewidth,eheight,origin,num=%d,%d,%d,%d\n",
        !          7687:       ewidth,eheight,origin,num);
        !          7688:       if ( msglevel >= 999 ) type_raster(picelemsp->image,msgfp); }
        !          7689:   /* -----------------------------------------------------------------------
        !          7690:   embed element in picture (once, or multiple times if requested)
        !          7691:   ------------------------------------------------------------------------ */
        !          7692:   for ( inum=0; inum<num; inum++ )	/* once, or for num repetitions */
        !          7693:     {
        !          7694:     /* --- set x,y-coords for this iteration --- */
        !          7695:     ix = iround(x);  iy = iround(y);	/* round x,y to nearest integer */
        !          7696:     if ( iscenter )			/* place center of element at x,y */
        !          7697:       {	xpos = ix - ewidth/2;		/* x picture coord to center elem */
        !          7698: 	ypos = height - iy - eheight/2; } /* y pixel coord to center elem */
        !          7699:     else				/* default places lower-left at x,y*/
        !          7700:       {	xpos = ix;			/* set x pixel coord for left */
        !          7701: 	if ( origin==10 || origin==11 )	/* x,yinc's are -+ or -- */
        !          7702: 	  xpos = ix - ewidth;		/* so set for right instead */
        !          7703: 	ypos = height - iy - eheight;	/* set y pixel coord for lower */
        !          7704: 	if ( origin==1 || origin==11 )	/* x,yinc's are +- or -- */
        !          7705: 	  ypos = height - iy; }		/* so set for upper instead */
        !          7706:     if ( msgfp!=NULL && msglevel>=29 )	/* debugging */
        !          7707:       fprintf(msgfp,
        !          7708:       "picture> inum,x,y,ix,iy,xpos,ypos=%d,%.2f,%.2f,%d,%d,%d,%d\n",
        !          7709:       inum,x,y,ix,iy,xpos,ypos);
        !          7710:     /* --- embed token raster at xpos,ypos, and quit if out-of-bounds --- */
        !          7711:     if ( !rastput(picturerp,picelemsp->image,ypos,xpos,0) ) break;
        !          7712:     /* --- apply increment --- */
        !          7713:     if ( xinc==0. && yinc==0. ) break;	/* quit if both increments zero */
        !          7714:     x += xinc;  y += yinc;		/* increment coords for next iter */
        !          7715:     } /* --- end-of-for(inum) --- */
        !          7716:   /* --- free picture element subraster after embedding it in picture --- */
        !          7717:   delete_subraster(picelemsp);		/* done with subraster, so free it */
        !          7718:   } /* --- end-of-while(*picptr!=0) --- */
        !          7719: /* -------------------------------------------------------------------------
        !          7720: return picture constructed from pic_elements to caller
        !          7721: -------------------------------------------------------------------------- */
        !          7722: end_of_job:
        !          7723:   workingbox = oldworkingbox;		/* restore original working box */
        !          7724:   workingparam = oldworkingparam;	/* restore original working param */
        !          7725:   return ( picturesp );			/* return our picture to caller */
        !          7726: } /* --- end-of-function rastpicture() --- */
        !          7727: 
        !          7728: 
        !          7729: /* ==========================================================================
        !          7730:  * Function:	rastline ( expression, size, basesp, arg1, arg2, arg3 )
        !          7731:  * Purpose:	\line handler, returns subraster corresponding to line
        !          7732:  *		parameters (xinc,yinc){xlen}
        !          7733:  * --------------------------------------------------------------------------
        !          7734:  * Arguments:	expression (I/O) char **  to first char of null-terminated
        !          7735:  *				string immediately following \line to be
        !          7736:  *				rasterized, and returning ptr immediately
        !          7737:  *				following last character processed.
        !          7738:  *		size (I)	int containing 0-4 default font size
        !          7739:  *		basesp (I)	subraster *  to character (or subexpression)
        !          7740:  *				immediately preceding \line
        !          7741:  *				(unused, but passed for consistency)
        !          7742:  *		arg1 (I)	int unused
        !          7743:  *		arg2 (I)	int unused
        !          7744:  *		arg3 (I)	int unused
        !          7745:  * --------------------------------------------------------------------------
        !          7746:  * Returns:	( subraster * )	ptr to subraster corresponding to line
        !          7747:  *				requested, or NULL for any parsing error
        !          7748:  * --------------------------------------------------------------------------
        !          7749:  * Notes:     o	Summary of syntax...
        !          7750:  *		  \line(xinc,yinc){xlen}
        !          7751:  *	      o	if {xlen} not given, then it's assumed xlen = |xinc|
        !          7752:  * ======================================================================= */
        !          7753: /* --- entry point --- */
        !          7754: subraster *rastline ( char **expression, int size, subraster *basesp,
        !          7755: 			int arg1, int arg2, int arg3 )
        !          7756: {
        !          7757: /* -------------------------------------------------------------------------
        !          7758: Allocations and Declarations
        !          7759: -------------------------------------------------------------------------- */
        !          7760: char	*texsubexpr(),linexpr[257], *xptr=linexpr; /*line(xinc,yinc){xlen}*/
        !          7761: subraster *new_subraster(), *linesp=NULL; /* subraster for line */
        !          7762: /*char	*origexpression = *expression;*/ /*original expression after \line*/
        !          7763: int	pixsz = 1;			/* pixels are one bit each */
        !          7764: double	strtod(),			/* convert ascii params to doubles */
        !          7765: 	xinc=0.0, yinc=0.0,		/* x,y-increments for line, */
        !          7766: 	xlen=0.0, ylen=0.0;		/* x,y lengths for line */
        !          7767: int	width=0,  height=0;		/* #pixels width,height of line */
        !          7768: int	istop=0,  isright=0,		/* origin at bot-left if x,yinc>=0 */
        !          7769: 	origin = 0;			/* x,yinc: ++=00 +-=01 -+=10 --=11 */
        !          7770: int	line_raster();			/* draw line in linesp->image */
        !          7771: /* -------------------------------------------------------------------------
        !          7772: obtain (xinc,yinc) arguments immediately following \line command
        !          7773: -------------------------------------------------------------------------- */
        !          7774: /* --- parse for (xinc,yinc) arguments, and bump expression past it --- */
        !          7775: *expression = texsubexpr(*expression,linexpr,253,"(",")",0,0);
        !          7776: if ( *linexpr == '\000' ) goto end_of_job; /* couldn't get (xinc,yinc) */
        !          7777: /* --- now interpret xinc,yinc returned in linexpr --- */
        !          7778: if ( (xptr=strchr(linexpr,',')) != NULL ) /* look for ',' in xinc,yinc */
        !          7779:   *xptr = '\000';			/* found it, so replace ',' by '\0'*/
        !          7780: if ( *linexpr != '\000' )		/* check against missing 1st arg */
        !          7781:   xinc = xlen = strtod(linexpr,NULL);	/* xinc in user units */
        !          7782: if ( xptr != NULL )			/* 2nd arg, if present, is yinc */
        !          7783:   yinc = ylen = strtod(xptr+1,NULL);	/* in user units */
        !          7784: /* -------------------------------------------------------------------------
        !          7785: obtain optional {xlen} following (xinc,yinc), and calculate ylen
        !          7786: -------------------------------------------------------------------------- */
        !          7787: /* --- check if {xlen} given --- */
        !          7788: if ( *(*expression) == '{' )		/*have {xlen} if leading char is { */
        !          7789:   {
        !          7790:   /* --- parse {xlen} and bump expression past it, interpret as double --- */
        !          7791:   *expression = texsubexpr(*expression,linexpr,253,"{","}",0,0);
        !          7792:   if ( *linexpr == '\000' ) goto end_of_job; /* couldn't get {xlen} */
        !          7793:   xlen = strtod(linexpr,NULL);		/* xlen in user units */
        !          7794:   /* --- set other values accordingly --- */
        !          7795:   if ( xlen  < 0.0 ) xinc = -xinc;	/* if xlen negative, flip xinc sign*/
        !          7796:   if ( xinc != 0.0 ) ylen = xlen*yinc/xinc; /* set ylen from xlen and slope*/
        !          7797:   else xlen  = 0.0;			/* can't have xlen if xinc=0 */
        !          7798:   } /* --- end-of-if(*(*expression)=='{') --- */
        !          7799: /* -------------------------------------------------------------------------
        !          7800: calculate width,height, etc, based on xlen,ylen, etc
        !          7801: -------------------------------------------------------------------------- */
        !          7802: /* --- force lengths positive --- */
        !          7803: xlen = absval(xlen);			/* force xlen positive */
        !          7804: ylen = absval(ylen);			/* force ylen positive */
        !          7805: /* --- calculate corresponding lengths in pixels --- */
        !          7806: width  = max2(1,iround(unitlength*xlen)); /*scale by unitlength and round,*/
        !          7807: height = max2(1,iround(unitlength*ylen)); /* and must be at least 1 pixel */
        !          7808: /* --- set origin corner, x,yinc's: ++=0=(0,0) +-=1=(0,1) -+=10=(1,0) --- */
        !          7809: if ( xinc < 0.0 ) isright = 1;		/*negative xinc, so corner is (1,?)*/
        !          7810: if ( yinc < 0.0 ) istop = 1;		/*negative yinc, so corner is (?,1)*/
        !          7811: origin = isright*10 + istop;		/* interpret 0=(0,0), 11=(1,1), etc*/
        !          7812: if ( msgfp!=NULL && msglevel>=29 )	/* debugging */
        !          7813:   fprintf(msgfp,"rastline> width,height,origin;x,yinc=%d,%d,%d;%g,%g\n",
        !          7814:   width,height,origin,xinc,yinc);
        !          7815: /* -------------------------------------------------------------------------
        !          7816: allocate subraster and raster for complete picture
        !          7817: -------------------------------------------------------------------------- */
        !          7818: /* --- sanity check on width,height args --- */
        !          7819: if ( width < 1 ||  width > 600
        !          7820: ||  height < 1 || height > 600 ) goto end_of_job;
        !          7821: /* --- allocate and initialize subraster for constructed line --- */
        !          7822: if ( (linesp=new_subraster(width,height,pixsz)) /* allocate new subraster */
        !          7823: ==   NULL )  goto end_of_job;		/* quit if failed */
        !          7824: /* --- initialize line subraster parameters --- */
        !          7825: linesp->type = IMAGERASTER;		/* image */
        !          7826: linesp->symdef = NULL;			/* not applicable for image */
        !          7827: linesp->baseline = height/2 + 2;	/* is a little above center good? */
        !          7828: linesp->size = size;			/* size (probably unneeded) */
        !          7829: /* -------------------------------------------------------------------------
        !          7830: draw the line
        !          7831: -------------------------------------------------------------------------- */
        !          7832: line_raster ( linesp->image,		/* embedded raster image */
        !          7833: 	(istop?   0 : height-1),	/* row0, from bottom or top */
        !          7834: 	(isright?  width-1 : 0),	/* col0, from left or right */
        !          7835: 	(istop?   height-1 : 0),	/* row1, to top or bottom */
        !          7836: 	(isright? 0 :  width-1),	/* col1, to right or left */
        !          7837: 	1 );				/* line thickness is 1 pixel */
        !          7838: /* -------------------------------------------------------------------------
        !          7839: return constructed line to caller
        !          7840: -------------------------------------------------------------------------- */
        !          7841: end_of_job:
        !          7842:   if ( workingparam != NULL )		/* caller wants origin */
        !          7843:     *workingparam = origin;		/* return origin corner to caller */
        !          7844:   return ( linesp );			/* return line to caller */
        !          7845: } /* --- end-of-function rastline() --- */
        !          7846: 
        !          7847: 
        !          7848: /* ==========================================================================
        !          7849:  * Function:	rastcircle ( expression, size, basesp, arg1, arg2, arg3 )
        !          7850:  * Purpose:	\circle handler, returns subraster corresponding to ellipse
        !          7851:  *		parameters (xdiam[,ydiam])
        !          7852:  * --------------------------------------------------------------------------
        !          7853:  * Arguments:	expression (I/O) char **  to first char of null-terminated
        !          7854:  *				string immediately following \circle to be
        !          7855:  *				rasterized, and returning ptr immediately
        !          7856:  *				following last character processed.
        !          7857:  *		size (I)	int containing 0-4 default font size
        !          7858:  *		basesp (I)	subraster *  to character (or subexpression)
        !          7859:  *				immediately preceding \circle
        !          7860:  *				(unused, but passed for consistency)
        !          7861:  *		arg1 (I)	int unused
        !          7862:  *		arg2 (I)	int unused
        !          7863:  *		arg3 (I)	int unused
        !          7864:  * --------------------------------------------------------------------------
        !          7865:  * Returns:	( subraster * )	ptr to subraster corresponding to ellipse
        !          7866:  *				requested, or NULL for any parsing error
        !          7867:  * --------------------------------------------------------------------------
        !          7868:  * Notes:     o	Summary of syntax...
        !          7869:  *		  \circle(xdiam[,ydiam])
        !          7870:  *	      o
        !          7871:  * ======================================================================= */
        !          7872: /* --- entry point --- */
        !          7873: subraster *rastcircle ( char **expression, int size, subraster *basesp,
        !          7874: 			int arg1, int arg2, int arg3 )
        !          7875: {
        !          7876: /* -------------------------------------------------------------------------
        !          7877: Allocations and Declarations
        !          7878: -------------------------------------------------------------------------- */
        !          7879: char	*texsubexpr(), circexpr[512],*xptr=circexpr; /*circle(xdiam[,ydiam])*/
        !          7880: char	*qptr=NULL, quads[256]="1234";	/* default to draw all quadrants */
        !          7881: double	theta0=0.0, theta1=0.0;		/* ;theta0,theta1 instead of ;quads*/
        !          7882: subraster *new_subraster(), *circsp=NULL; /* subraster for ellipse */
        !          7883: int	pixsz = 1;			/* pixels are one bit each */
        !          7884: double	strtod(),			/* convert ascii params to doubles */
        !          7885: 	xdiam=0.0, ydiam=0.0;		/* x,y major/minor axes/diameters */
        !          7886: int	width=0,  height=0;		/* #pixels width,height of ellipse */
        !          7887: int	thickness = 1;			/* drawn lines are one pixel thick */
        !          7888: int	origin = 55;			/* force origin centered */
        !          7889: int	circle_raster(),		/* draw ellipse in circsp->image */
        !          7890: 	circle_recurse();		/* for theta0,theta1 args */
        !          7891: /* -------------------------------------------------------------------------
        !          7892: obtain (xdiam[,ydiam]) arguments immediately following \circle command
        !          7893: -------------------------------------------------------------------------- */
        !          7894: /* --- parse for (xdiam[,ydiam]) args, and bump expression past it --- */
        !          7895: *expression = texsubexpr(*expression,circexpr,511,"(",")",0,0);
        !          7896: if ( *circexpr == '\000' ) goto end_of_job; /* couldn't get (xdiam[,ydiam])*/
        !          7897: /* --- now interpret xdiam[,ydiam] returned in circexpr --- */
        !          7898: if ( (qptr=strchr(circexpr,';')) != NULL ) /* semicolon signals quads data */
        !          7899:   { *qptr = '\000';			/* replace semicolon by '\0' */
        !          7900:     strcpy(quads,qptr+1);		/* save user-requested quads */
        !          7901:     if ( (qptr=strchr(quads,',')) != NULL ) /* have theta0,theta1 instead */
        !          7902:       {	*qptr = '\000';			/* replace , with null */
        !          7903: 	theta0 = strtod(quads,NULL);	/* theta0 precedes , */
        !          7904: 	theta1 = strtod(qptr+1,NULL);	/* theta1 follows , */
        !          7905: 	qptr = NULL; }			/* signal thetas instead of quads */
        !          7906:     else
        !          7907: 	qptr = quads; }			/* set qptr arg for circle_raster()*/
        !          7908: else					/* no ;quads at all */
        !          7909:   qptr = quads;				/* default to all 4 quadrants */
        !          7910: if ( (xptr=strchr(circexpr,',')) != NULL ) /* look for ',' in xdiam[,ydiam]*/
        !          7911:   *xptr = '\000';			/* found it, so replace ',' by '\0'*/
        !          7912: xdiam = ydiam = strtod(circexpr,NULL);	/* xdiam=ydiam in user units */
        !          7913: if ( xptr != NULL )			/* 2nd arg, if present, is ydiam */
        !          7914:   ydiam = strtod(xptr+1,NULL);		/* in user units */
        !          7915: /* -------------------------------------------------------------------------
        !          7916: calculate width,height, etc
        !          7917: -------------------------------------------------------------------------- */
        !          7918: /* --- calculate width,height in pixels --- */
        !          7919: width  = max2(1,iround(unitlength*xdiam)); /*scale by unitlength and round,*/
        !          7920: height = max2(1,iround(unitlength*ydiam)); /* and must be at least 1 pixel */
        !          7921: if ( msgfp!=NULL && msglevel>=29 )	/* debugging */
        !          7922:   fprintf(msgfp,"rastcircle> width,height;quads=%d,%d,%s\n",
        !          7923:   width,height,(qptr==NULL?"default":qptr));
        !          7924: /* -------------------------------------------------------------------------
        !          7925: allocate subraster and raster for complete picture
        !          7926: -------------------------------------------------------------------------- */
        !          7927: /* --- sanity check on width,height args --- */
        !          7928: if ( width < 1 ||  width > 600
        !          7929: ||  height < 1 || height > 600 ) goto end_of_job;
        !          7930: /* --- allocate and initialize subraster for constructed ellipse --- */
        !          7931: if ( (circsp=new_subraster(width,height,pixsz)) /* allocate new subraster */
        !          7932: ==   NULL )  goto end_of_job;		/* quit if failed */
        !          7933: /* --- initialize ellipse subraster parameters --- */
        !          7934: circsp->type = IMAGERASTER;		/* image */
        !          7935: circsp->symdef = NULL;			/* not applicable for image */
        !          7936: circsp->baseline = height/2 + 2;	/* is a little above center good? */
        !          7937: circsp->size = size;			/* size (probably unneeded) */
        !          7938: /* -------------------------------------------------------------------------
        !          7939: draw the ellipse
        !          7940: -------------------------------------------------------------------------- */
        !          7941: if ( qptr != NULL )			/* have quads */
        !          7942:   circle_raster ( circsp->image,	/* embedded raster image */
        !          7943: 	0, 0,				/* row0,col0 are upper-left corner */
        !          7944: 	height-1, width-1,		/* row1,col1 are lower-right */
        !          7945: 	thickness,			/* line thickness is 1 pixel */
        !          7946: 	qptr );				/* "1234" quadrants to be drawn */
        !          7947: else					/* have theta0,theta1 */
        !          7948:   circle_recurse ( circsp->image,	/* embedded raster image */
        !          7949: 	0, 0,				/* row0,col0 are upper-left corner */
        !          7950: 	height-1, width-1,		/* row1,col1 are lower-right */
        !          7951: 	thickness,			/* line thickness is 1 pixel */
        !          7952: 	theta0,theta1 );		/* theta0,theta1 arc to be drawn */
        !          7953: /* -------------------------------------------------------------------------
        !          7954: return constructed ellipse to caller
        !          7955: -------------------------------------------------------------------------- */
        !          7956: end_of_job:
        !          7957:   if ( workingparam != NULL )		/* caller wants origin */
        !          7958:     *workingparam = origin;		/* return center origin to caller */
        !          7959:   return ( circsp );			/* return ellipse to caller */
        !          7960: } /* --- end-of-function rastcircle() --- */
        !          7961: 
        !          7962: 
        !          7963: /* ==========================================================================
        !          7964:  * Function:	rastbezier ( expression, size, basesp, arg1, arg2, arg3 )
        !          7965:  * Purpose:	\bezier handler, returns subraster corresponding to bezier
        !          7966:  *		parameters (col0,row0)(col1,row1)(colt,rowt)
        !          7967:  * --------------------------------------------------------------------------
        !          7968:  * Arguments:	expression (I/O) char **  to first char of null-terminated
        !          7969:  *				string immediately following \bezier to be
        !          7970:  *				rasterized, and returning ptr immediately
        !          7971:  *				following last character processed.
        !          7972:  *		size (I)	int containing 0-5 default font size
        !          7973:  *		basesp (I)	subraster *  to character (or subexpression)
        !          7974:  *				immediately preceding \bezier
        !          7975:  *				(unused, but passed for consistency)
        !          7976:  *		arg1 (I)	int unused
        !          7977:  *		arg2 (I)	int unused
        !          7978:  *		arg3 (I)	int unused
        !          7979:  * --------------------------------------------------------------------------
        !          7980:  * Returns:	( subraster * )	ptr to subraster corresponding to bezier
        !          7981:  *				requested, or NULL for any parsing error
        !          7982:  * --------------------------------------------------------------------------
        !          7983:  * Notes:     o	Summary of syntax...
        !          7984:  *		  \bezier(col1,row1)(colt,rowt)
        !          7985:  *	      o	col0=0,row0=0 assumed, i.e., given by
        !          7986:  *		\picture(){~(col0,row0){\bezier(col1,row1)(colt,rowt)}~}
        !          7987:  * ======================================================================= */
        !          7988: /* --- entry point --- */
        !          7989: subraster *rastbezier ( char **expression, int size, subraster *basesp,
        !          7990: 			int arg1, int arg2, int arg3 )
        !          7991: {
        !          7992: /* -------------------------------------------------------------------------
        !          7993: Allocations and Declarations
        !          7994: -------------------------------------------------------------------------- */
        !          7995: subraster *new_subraster(), *bezsp=NULL; /* subraster for bezier */
        !          7996: char	*texsubexpr(), bezexpr[129],*xptr=bezexpr; /*\bezier(r,c)(r,c)(r,c)*/
        !          7997: double	strtod();			/* convert ascii params to doubles */
        !          7998: double	r0=0.0,c0=0.0, r1=0.0,c1=0.0, rt=0.0,ct=0.0, /* bezier points */
        !          7999: 	rmid=0.0, cmid=0.0,		/* coords at parameterized midpoint*/
        !          8000: 	rmin=0.0, cmin=0.0,		/* minimum r,c */
        !          8001: 	rmax=0.0, cmax=0.0,		/* maximum r,c */
        !          8002: 	rdelta=0.0, cdelta=0.0,		/* rmax-rmin, cmax-cmin */
        !          8003: 	r=0.0, c=0.0;			/* some point */
        !          8004: int	iarg=0;				/* 0=r0,c0 1=r1,c1 2=rt,ct */
        !          8005: int	width=0, height=0;		/* dimensions of bezier raster */
        !          8006: int	pixsz = 1;			/* pixels are one bit each */
        !          8007: /*int	thickness = 1;*/		/* drawn lines are one pixel thick */
        !          8008: int	origin = 0;			/*c's,r's reset to lower-left origin*/
        !          8009: int	bezier_raster();		/* draw bezier in bezsp->image */
        !          8010: /* -------------------------------------------------------------------------
        !          8011: obtain (c1,r1)(ct,rt) args immediately following \bezier command
        !          8012: -------------------------------------------------------------------------- */
        !          8013: for ( iarg=1; iarg<=2; iarg++ )		/* 0=c0,r0 1=c1,r1 2=ct,rt */
        !          8014:   {
        !          8015:   /* --- parse for (r,c) args, and bump expression past them all --- */
        !          8016:   *expression = texsubexpr(*expression,bezexpr,127,"(",")",0,0);
        !          8017:   if ( *bezexpr == '\000' ) goto end_of_job; /* couldn't get (r,c)*/
        !          8018:   /* --- now interpret (r,c) returned in bezexpr --- */
        !          8019:   c = r = 0.0;				/* init x-coord=col, y-coord=row */
        !          8020:   if ( (xptr=strchr(bezexpr,',')) != NULL ) /* comma separates row,col */
        !          8021:     { *xptr = '\000';			/* found it, so replace ',' by '\0'*/
        !          8022:       r = unitlength*strtod(xptr+1,NULL); } /* row=y-coord in pixels */
        !          8023:   c = unitlength*strtod(bezexpr,NULL);	/* col=x-coord in pixels */
        !          8024:   /* --- store r,c --- */
        !          8025:   switch ( iarg )
        !          8026:     { case 0: r0=r; c0=c; break;
        !          8027:       case 1: r1=r; c1=c; break;
        !          8028:       case 2: rt=r; ct=c; break; }
        !          8029:   } /* --- end-of-for(iarg) --- */
        !          8030: /* --- determine midpoint and maximum,minimum points --- */
        !          8031: rmid = 0.5*(rt + 0.5*(r0+r1));		/* y-coord at middle of bezier */
        !          8032: cmid = 0.5*(ct + 0.5*(c0+c1));		/* x-coord at middle of bezier */
        !          8033: rmin = min3(r0,r1,rmid);		/* lowest row */
        !          8034: cmin = min3(c0,c1,cmid);		/* leftmost col */
        !          8035: rmax = max3(r0,r1,rmid);		/* highest row */
        !          8036: cmax = max3(c0,c1,cmid);		/* rightmost col */
        !          8037: rdelta = rmax-rmin;			/* height */
        !          8038: cdelta = cmax-cmin;			/* width */
        !          8039: /* --- rescale coords so we start at 0,0 --- */
        !          8040: r0 -= rmin;  c0 -= cmin;		/* rescale r0,c0 */
        !          8041: r1 -= rmin;  c1 -= cmin;		/* rescale r1,c1 */
        !          8042: rt -= rmin;  ct -= cmin;		/* rescale rt,ct */
        !          8043: /* --- flip rows so 0,0 becomes lower-left corner instead of upper-left--- */
        !          8044: r0 = rdelta - r0 + 1;			/* map 0-->height-1, height-1-->0 */
        !          8045: r1 = rdelta - r1 + 1;
        !          8046: rt = rdelta - rt + 1;
        !          8047: /* --- determine width,height of raster needed for bezier --- */
        !          8048: width  = (int)(cdelta + 0.9999) + 1;	/* round width up */
        !          8049: height = (int)(rdelta + 0.9999) + 1;	/* round height up */
        !          8050: if ( msgfp!=NULL && msglevel>=29 )	/* debugging */
        !          8051:   fprintf(msgfp,"rastbezier> width,height,origin=%d,%d,%d; c0,r0=%g,%g; "
        !          8052:   "c1,r1=%g,%g\n rmin,mid,max=%g,%g,%g; cmin,mid,max=%g,%g,%g\n",
        !          8053:   width,height,origin, c0,r0, c1,r1, rmin,rmid,rmax, cmin,cmid,cmax);
        !          8054: /* -------------------------------------------------------------------------
        !          8055: allocate raster
        !          8056: -------------------------------------------------------------------------- */
        !          8057: /* --- sanity check on width,height args --- */
        !          8058: if ( width < 1 ||  width > 600
        !          8059: ||  height < 1 || height > 600 ) goto end_of_job;
        !          8060: /* --- allocate and initialize subraster for constructed bezier --- */
        !          8061: if ( (bezsp=new_subraster(width,height,pixsz)) /* allocate new subraster */
        !          8062: ==   NULL )  goto end_of_job;		/* quit if failed */
        !          8063: /* --- initialize bezier subraster parameters --- */
        !          8064: bezsp->type = IMAGERASTER;		/* image */
        !          8065: bezsp->symdef = NULL;			/* not applicable for image */
        !          8066: bezsp->baseline = height/2 + 2;		/* is a little above center good? */
        !          8067: bezsp->size = size;			/* size (probably unneeded) */
        !          8068: /* -------------------------------------------------------------------------
        !          8069: draw the bezier
        !          8070: -------------------------------------------------------------------------- */
        !          8071: bezier_raster ( bezsp->image,		/* embedded raster image */
        !          8072: 	r0, c0,				/* row0,col0 are lower-left corner */
        !          8073: 	r1, c1,				/* row1,col1 are upper-right */
        !          8074: 	rt, ct );			/* bezier tangent point */
        !          8075: /* -------------------------------------------------------------------------
        !          8076: return constructed bezier to caller
        !          8077: -------------------------------------------------------------------------- */
        !          8078: end_of_job:
        !          8079:   if ( workingparam != NULL )		/* caller wants origin */
        !          8080:     *workingparam = origin;		/* return center origin to caller */
        !          8081:   return ( bezsp );			/* return bezier to caller */
        !          8082: } /* --- end-of-function rastbezier() --- */
        !          8083: 
        !          8084: 
        !          8085: /* ==========================================================================
        !          8086:  * Function:	rastraise ( expression, size, basesp, arg1, arg2, arg3 )
        !          8087:  * Purpose:	\raisebox{lift}{subexpression} handler, returns subraster
        !          8088:  *		containing subexpression with its baseline "lifted" by lift
        !          8089:  *		pixels, scaled by \unitlength, or "lowered" if lift arg
        !          8090:  *		negative
        !          8091:  * --------------------------------------------------------------------------
        !          8092:  * Arguments:	expression (I/O) char **  to first char of null-terminated
        !          8093:  *				string immediately following \raisebox to be
        !          8094:  *				rasterized, and returning ptr immediately
        !          8095:  *				following last character processed.
        !          8096:  *		size (I)	int containing 0-5 default font size
        !          8097:  *		basesp (I)	subraster *  to character (or subexpression)
        !          8098:  *				immediately preceding \rotatebox
        !          8099:  *				(unused, but passed for consistency)
        !          8100:  *		arg1 (I)	int unused
        !          8101:  *		arg2 (I)	int unused
        !          8102:  *		arg3 (I)	int unused
        !          8103:  * --------------------------------------------------------------------------
        !          8104:  * Returns:	( subraster * )	ptr to subraster corresponding to \raisebox
        !          8105:  *				requested, or NULL for any parsing error
        !          8106:  * --------------------------------------------------------------------------
        !          8107:  * Notes:     o	Summary of syntax...
        !          8108:  *		  \raisebox{lift}{subexpression}
        !          8109:  *	      o
        !          8110:  * ======================================================================= */
        !          8111: /* --- entry point --- */
        !          8112: subraster *rastraise ( char **expression, int size, subraster *basesp,
        !          8113: 			int arg1, int arg2, int arg3 )
        !          8114: {
        !          8115: /* -------------------------------------------------------------------------
        !          8116: Allocations and Declarations
        !          8117: -------------------------------------------------------------------------- */
        !          8118: char	*texsubexpr(), subexpr[8192], *liftexpr=subexpr; /* args */
        !          8119: subraster *rasterize(), *raisesp=NULL;	/* rasterize subexpr to be raised */
        !          8120: int	lift=0;				/* amount to raise/lower baseline */
        !          8121: /* -------------------------------------------------------------------------
        !          8122: obtain {lift} argument immediately following \raisebox command
        !          8123: -------------------------------------------------------------------------- */
        !          8124: /* --- parse for {lift} arg, and bump expression past it --- */
        !          8125: *expression = texsubexpr(*expression,liftexpr,0,"{","}",0,0);
        !          8126: if ( *liftexpr == '\000' ) goto end_of_job; /* couldn't get {lift} */
        !          8127: lift = (int)((unitlength*strtod(liftexpr,NULL))+0.0);	/*{lift} to integer*/
        !          8128: if ( abs(lift) > 200 ) lift=0;		/* sanity check */
        !          8129: /* -------------------------------------------------------------------------
        !          8130: obtain {subexpr} argument after {lift}, and rasterize it
        !          8131: -------------------------------------------------------------------------- */
        !          8132: /* --- parse for {subexpr} arg, and bump expression past it --- */
        !          8133: *expression = texsubexpr(*expression,subexpr,0,"{","}",0,0);
        !          8134: /* --- rasterize subexpression to be raised/lowered --- */
        !          8135: if ( (raisesp = rasterize(subexpr,size)) /* rasterize subexpression */
        !          8136: ==   NULL ) goto end_of_job;		/* and quit if failed */
        !          8137: /* -------------------------------------------------------------------------
        !          8138: raise/lower baseline and return it to caller
        !          8139: -------------------------------------------------------------------------- */
        !          8140: /* --- raise/lower baseline --- */
        !          8141: raisesp->baseline += lift;		/* new baseline (no height checks) */
        !          8142: /* --- return raised subexpr to caller --- */
        !          8143: end_of_job:
        !          8144:   return ( raisesp );			/* return raised subexpr to caller */
        !          8145: } /* --- end-of-function rastraise() --- */
        !          8146: 
        !          8147: 
        !          8148: /* ==========================================================================
        !          8149:  * Function:	rastrotate ( expression, size, basesp, arg1, arg2, arg3 )
        !          8150:  * Purpose:	\rotatebox{degrees}{subexpression} handler, returns subraster
        !          8151:  *		containing subexpression rotated by degrees (counterclockwise
        !          8152:  *		if degrees positive)
        !          8153:  * --------------------------------------------------------------------------
        !          8154:  * Arguments:	expression (I/O) char **  to first char of null-terminated
        !          8155:  *				string immediately following \rotatebox to be
        !          8156:  *				rasterized, and returning ptr immediately
        !          8157:  *				following last character processed.
        !          8158:  *		size (I)	int containing 0-5 default font size
        !          8159:  *		basesp (I)	subraster *  to character (or subexpression)
        !          8160:  *				immediately preceding \rotatebox
        !          8161:  *				(unused, but passed for consistency)
        !          8162:  *		arg1 (I)	int unused
        !          8163:  *		arg2 (I)	int unused
        !          8164:  *		arg3 (I)	int unused
        !          8165:  * --------------------------------------------------------------------------
        !          8166:  * Returns:	( subraster * )	ptr to subraster corresponding to \rotatebox
        !          8167:  *				requested, or NULL for any parsing error
        !          8168:  * --------------------------------------------------------------------------
        !          8169:  * Notes:     o	Summary of syntax...
        !          8170:  *		  \rotatebox{degrees}{subexpression}
        !          8171:  *	      o
        !          8172:  * ======================================================================= */
        !          8173: /* --- entry point --- */
        !          8174: subraster *rastrotate ( char **expression, int size, subraster *basesp,
        !          8175: 			int arg1, int arg2, int arg3 )
        !          8176: {
        !          8177: /* -------------------------------------------------------------------------
        !          8178: Allocations and Declarations
        !          8179: -------------------------------------------------------------------------- */
        !          8180: char	*texsubexpr(), subexpr[8192], *degexpr=subexpr; /* args */
        !          8181: subraster *rasterize(), *rotsp=NULL;	/* subraster for rotated subexpr */
        !          8182: raster	*rastrot(), *rotrp=NULL;	/* rotate subraster->image 90 degs */
        !          8183: int	delete_raster();		/* delete intermediate rasters */
        !          8184: int	baseline=0;			/* baseline of rasterized image */
        !          8185: double	strtod(),			/* convert ascii params to doubles */
        !          8186: 	degrees=0.0, ipart,fpart;	/* degrees to be rotated */
        !          8187: int	idegrees=0, isneg=0;		/* positive ipart, isneg=1 if neg */
        !          8188: int	n90=0, isn90=1;			/* degrees is n90 multiples of 90 */
        !          8189: /* -------------------------------------------------------------------------
        !          8190: obtain {degrees} argument immediately following \rotatebox command
        !          8191: -------------------------------------------------------------------------- */
        !          8192: /* --- parse for {degrees} arg, and bump expression past it --- */
        !          8193: *expression = texsubexpr(*expression,degexpr,0,"{","}",0,0);
        !          8194: if ( *degexpr == '\000' ) goto end_of_job; /* couldn't get {degrees} */
        !          8195: degrees = strtod(degexpr,NULL);		/* degrees to be rotated */
        !          8196: if ( degrees < 0.0 )			/* clockwise rotation desired */
        !          8197:   { degrees = -degrees;			/* flip sign so degrees positive */
        !          8198:     isneg = 1; }			/* and set flag to indicate flip */
        !          8199: fpart = modf(degrees,&ipart);		/* integer and fractional parts */
        !          8200: ipart = (double)(((int)degrees)%360);	/* degrees mod 360 */
        !          8201: degrees = ipart + fpart;		/* restore fractional part */
        !          8202: if ( isneg )				/* if clockwise rotation requested */
        !          8203:   degrees = 360.0 - degrees;		/* do equivalent counterclockwise */
        !          8204: idegrees = (int)(degrees+0.5);		/* integer degrees */
        !          8205: n90 = idegrees/90;			/* degrees is n90 multiples of 90 */
        !          8206: isn90 = (90*n90==idegrees);		/*true if degrees is multiple of 90*/
        !          8207: isn90 = 1;				/* forced true for time being */
        !          8208: /* -------------------------------------------------------------------------
        !          8209: obtain {subexpr} argument after {degrees}, and rasterize it
        !          8210: -------------------------------------------------------------------------- */
        !          8211: /* --- parse for {subexpr} arg, and bump expression past it --- */
        !          8212: *expression = texsubexpr(*expression,subexpr,0,"{","}",0,0);
        !          8213: /* --- rasterize subexpression to be rotated --- */
        !          8214: if ( (rotsp = rasterize(subexpr,size))	/* rasterize subexpression */
        !          8215: ==   NULL ) goto end_of_job;		/* and quit if failed */
        !          8216: /* --- return unmodified image if no rotation requested --- */
        !          8217: if ( abs(idegrees) < 2 ) goto end_of_job; /* don't bother rotating image */
        !          8218: /* --- extract params for image to be rotated --- */
        !          8219: rotrp = rotsp->image;			/* unrotated rasterized image */
        !          8220: baseline = rotsp->baseline;		/* and baseline of that image */
        !          8221: /* -------------------------------------------------------------------------
        !          8222: rotate by multiples of 90 degrees
        !          8223: -------------------------------------------------------------------------- */
        !          8224: if ( isn90 )				/* rotation by multiples of 90 */
        !          8225:  if ( n90 > 0 )				/* do nothing for 0 degrees */
        !          8226:   {
        !          8227:   n90 = 4-n90;				/* rasrot() rotates clockwise */
        !          8228:   while ( n90 > 0 )			/* still have remaining rotations */
        !          8229:     { raster *nextrp = rastrot(rotrp);	/* rotate raster image */
        !          8230:       if ( nextrp == NULL ) break;	/* something's terribly wrong */
        !          8231:       delete_raster(rotrp);		/* free previous raster image */
        !          8232:       rotrp = nextrp;			/* and replace it with rotated one */
        !          8233:       n90--; }				/* decrement remaining count */
        !          8234:   } /* --- end-of-if(isn90) --- */
        !          8235: /* -------------------------------------------------------------------------
        !          8236: requested rotation not multiple of 90 degrees
        !          8237: -------------------------------------------------------------------------- */
        !          8238: if ( !isn90 )				/* explicitly construct rotation */
        !          8239:   { ; }					/* not yet implemented */
        !          8240: /* -------------------------------------------------------------------------
        !          8241: re-populate subraster envelope with rotated image
        !          8242: -------------------------------------------------------------------------- */
        !          8243: /* --- re-init various subraster parameters, embedding raster in it --- */
        !          8244: if ( rotrp != NULL )			/* rotated raster constructed okay */
        !          8245:  { rotsp->type = IMAGERASTER;		/* signal constructed image */
        !          8246:    rotsp->image = rotrp;		/* raster we just constructed */
        !          8247:    /* --- now try to guess pleasing baseline --- */
        !          8248:    if ( idegrees > 2 )			/* leave unchanged if unrotated */
        !          8249:     if ( strlen(subexpr) < 3		/* we rotated a short expression */
        !          8250:     ||   abs(idegrees-180) < 3 )	/* or just turned it upside-down */
        !          8251:       baseline = rotrp->height - 1;	/* so set with nothing descending */
        !          8252:     else				/* rotated a long expression */
        !          8253:       baseline = (65*(rotrp->height-1))/100; /* roughly center long expr */
        !          8254:    rotsp->baseline = baseline; }	/* set baseline as calculated above*/
        !          8255: /* --- return rotated subexpr to caller --- */
        !          8256: end_of_job:
        !          8257:   return ( rotsp );			/*return rotated subexpr to caller*/
        !          8258: } /* --- end-of-function rastrotate() --- */
        !          8259: 
        !          8260: 
        !          8261: /* ==========================================================================
        !          8262:  * Function:	rastfbox ( expression, size, basesp, arg1, arg2, arg3 )
        !          8263:  * Purpose:	\fbox{subexpression} handler, returns subraster
        !          8264:  *		containing subexpression with frame box drawn around it
        !          8265:  * --------------------------------------------------------------------------
        !          8266:  * Arguments:	expression (I/O) char **  to first char of null-terminated
        !          8267:  *				string immediately following \fbox to be
        !          8268:  *				rasterized, and returning ptr immediately
        !          8269:  *				following last character processed.
        !          8270:  *		size (I)	int containing 0-5 default font size
        !          8271:  *		basesp (I)	subraster *  to character (or subexpression)
        !          8272:  *				immediately preceding \fbox
        !          8273:  *				(unused, but passed for consistency)
        !          8274:  *		arg1 (I)	int unused
        !          8275:  *		arg2 (I)	int unused
        !          8276:  *		arg3 (I)	int unused
        !          8277:  * --------------------------------------------------------------------------
        !          8278:  * Returns:	( subraster * )	ptr to subraster corresponding to \fbox
        !          8279:  *				requested, or NULL for any parsing error
        !          8280:  * --------------------------------------------------------------------------
        !          8281:  * Notes:     o	Summary of syntax...
        !          8282:  *		  \fbox[width][height]{subexpression}
        !          8283:  *	      o
        !          8284:  * ======================================================================= */
        !          8285: /* --- entry point --- */
        !          8286: subraster *rastfbox ( char **expression, int size, subraster *basesp,
        !          8287: 			int arg1, int arg2, int arg3 )
        !          8288: {
        !          8289: /* -------------------------------------------------------------------------
        !          8290: Allocations and Declarations
        !          8291: -------------------------------------------------------------------------- */
        !          8292: char	*texsubexpr(), subexpr[8192], widtharg[512]; /* args */
        !          8293: subraster *rasterize(), *framesp=NULL;	/* rasterize subexpr to be framed */
        !          8294: raster	*border_raster(), *bp=NULL;	/* framed image raster */
        !          8295: double	strtod();			/* interpret [width][height] */
        !          8296: int	fwidth=6, fthick=1;		/*extra frame width, line thickness*/
        !          8297: int	width=(-1), height=(-1),	/* optional [width][height] args */
        !          8298: 	iscompose = 0;			/* set true if optional args given */
        !          8299: /* -------------------------------------------------------------------------
        !          8300: obtain optional [width][height] arguments immediately following \fbox
        !          8301: -------------------------------------------------------------------------- */
        !          8302: /* --- first check for optional \fbox[width] --- */
        !          8303: if ( *(*expression) == '[' )		/* check for []-enclosed width arg */
        !          8304:   { *expression = texsubexpr(*expression,widtharg,511,"[","]",0,0);
        !          8305:     if ( *widtharg != '\000' )		/* got widtharg */
        !          8306:      { width = max2(1,iround(unitlength*strtod(widtharg,NULL)));
        !          8307:        height = 1;  fwidth = 2; iscompose = 1; }
        !          8308:   } /* --- end-of-if(**expression=='[') --- */
        !          8309: if ( width > 0 )			/* found leading [width], so... */
        !          8310:  if ( *(*expression) == '[' )		/* check for []-enclosed height arg */
        !          8311:   { *expression = texsubexpr(*expression,widtharg,511,"[","]",0,0);
        !          8312:     if ( *widtharg != '\000' )		/* got widtharg */
        !          8313:      { height = max2(1,iround(unitlength*strtod(widtharg,NULL)));
        !          8314:        fwidth = 0; }			/* no extra border */
        !          8315:   } /* --- end-of-if(**expression=='[') --- */
        !          8316: /* -------------------------------------------------------------------------
        !          8317: obtain {subexpr} argument
        !          8318: -------------------------------------------------------------------------- */
        !          8319: /* --- parse for {subexpr} arg, and bump expression past it --- */
        !          8320: *expression = texsubexpr(*expression,subexpr,0,"{","}",0,0);
        !          8321: /* --- rasterize subexpression to be framed --- */
        !          8322: if ( width<0 || height<0 )		/* no explicit dimensions given */
        !          8323:   { if ( (framesp = rasterize(subexpr,size)) /* rasterize subexpression */
        !          8324:     ==   NULL ) goto end_of_job; }	/* and quit if failed */
        !          8325: else
        !          8326:   { char composexpr[8192];		/* compose subexpr with empty box */
        !          8327:     sprintf(composexpr,"\\compose{\\hspace{%d}\\vspace{%d}}{%s}",
        !          8328:     width,height,subexpr);
        !          8329:     if ( (framesp = rasterize(composexpr,size)) /* rasterize subexpression */
        !          8330:     ==   NULL ) goto end_of_job; }	/* and quit if failed */
        !          8331: /* -------------------------------------------------------------------------
        !          8332: draw frame, reset params, and return it to caller
        !          8333: -------------------------------------------------------------------------- */
        !          8334: /* --- draw border --- */
        !          8335: if ( (bp = border_raster(framesp->image,-fwidth,-fwidth,fthick,1))
        !          8336: ==   NULL ) goto end_of_job;		/* draw border and quit if failed */
        !          8337: /* --- replace original image and raise baseline to accommodate frame --- */
        !          8338: framesp->image = bp;			/* replace image with framed one */
        !          8339: if ( !iscompose )			/* simple border around subexpr */
        !          8340:   framesp->baseline += fwidth;		/* so just raise baseline */
        !          8341: else
        !          8342:   framesp->baseline = (framesp->image)->height - 1; /* set at bottom */
        !          8343: /* --- return framed subexpr to caller --- */
        !          8344: end_of_job:
        !          8345:   return ( framesp );			/* return framed subexpr to caller */
        !          8346: } /* --- end-of-function rastfbox() --- */
        !          8347: 
        !          8348: 
        !          8349: /* ==========================================================================
        !          8350:  * Function:	rastinput ( expression, size, basesp, arg1, arg2, arg3 )
        !          8351:  * Purpose:	\input{filename} handler, reads filename and returns
        !          8352:  *		subraster containing image of expression read from filename
        !          8353:  * --------------------------------------------------------------------------
        !          8354:  * Arguments:	expression (I/O) char **  to first char of null-terminated
        !          8355:  *				string immediately following \input to be
        !          8356:  *				rasterized, and returning ptr immediately
        !          8357:  *				following last character processed.
        !          8358:  *		size (I)	int containing 0-5 default font size
        !          8359:  *		basesp (I)	subraster *  to character (or subexpression)
        !          8360:  *				immediately preceding \input
        !          8361:  *				(unused, but passed for consistency)
        !          8362:  *		arg1 (I)	int unused
        !          8363:  *		arg2 (I)	int unused
        !          8364:  *		arg3 (I)	int unused
        !          8365:  * --------------------------------------------------------------------------
        !          8366:  * Returns:	( subraster * )	ptr to subraster corresponding to expression
        !          8367:  *				in filename, or NULL for any parsing error
        !          8368:  * --------------------------------------------------------------------------
        !          8369:  * Notes:     o	Summary of syntax...
        !          8370:  *		  \input{filename}
        !          8371:  *	      o
        !          8372:  * ======================================================================= */
        !          8373: /* --- entry point --- */
        !          8374: subraster *rastinput ( char **expression, int size, subraster *basesp,
        !          8375: 			int arg1, int arg2, int arg3 )
        !          8376: {
        !          8377: /* -------------------------------------------------------------------------
        !          8378: Allocations and Declarations
        !          8379: -------------------------------------------------------------------------- */
        !          8380: char	*texsubexpr(), tag[512]="\000", filename[1024]="\000"; /* args */
        !          8381: subraster *rasterize(), *inputsp=NULL; /* rasterized input image */
        !          8382: int	status, rastreadfile();	/* read input file */
        !          8383: int	format=0, npts=0;	/* don't reformat (numerical) input */
        !          8384: char	subexpr[8192],		/* concatanated lines from input file */
        !          8385: 	*mimeprep(),		/* preprocess inputted data */
        !          8386: 	*dtoa(), *reformat=NULL; /* reformat numerical input */
        !          8387: /* -------------------------------------------------------------------------
        !          8388: obtain [tag]{filename} argument
        !          8389: -------------------------------------------------------------------------- */
        !          8390: /* --- parse for optional [tag] or [fmt] arg, bump expression past it --- */
        !          8391: if ( *(*expression) == '[' )		/* check for []-enclosed value */
        !          8392:   { char argfld[2048];			/* optional argument field */
        !          8393:     *expression = texsubexpr(*expression,argfld,2047,"[","]",0,0);
        !          8394:     if ( (reformat=strstr(argfld,"dtoa")) != NULL ) /* dtoa requested */
        !          8395:       {	format = 1;			/* signal dtoa() format */
        !          8396: 	if ( (reformat=strchr(reformat,'=')) != NULL ) /* have dtoa= */
        !          8397: 	  npts = (int)strtol(reformat+1,NULL,0); } /* so set npts */
        !          8398:     if ( format == 0 )			/* reformat not requested */
        !          8399:       strcpy(tag,argfld); }		/* so interpret arg as tag */
        !          8400: /* --- parse for {filename} arg, and bump expression past it --- */
        !          8401: *expression = texsubexpr(*expression,filename,1023,"{","}",0,0);
        !          8402: /* --- check for alternate filename:tag --- */
        !          8403: if ( *filename != '\000'		/* got filename */
        !          8404: /*&& *tag == '\000'*/ )			/* but no [tag] */
        !          8405:  { char	*delim = strchr(filename,':');	/* look for : in filename:tag */
        !          8406:    if ( delim != (char *)NULL )		/* found it */
        !          8407:     { *delim = '\000';			/* null-terminate filename at : */
        !          8408:       strcpy(tag,delim+1); } }		/* and stuff after : is tag */
        !          8409: /* --------------------------------------------------------------------------
        !          8410: Read file and rasterize constructed subexpression
        !          8411: -------------------------------------------------------------------------- */
        !          8412: status = rastreadfile(filename,tag,subexpr); /* read file */
        !          8413: if ( *subexpr == '\000' ) goto end_of_job;   /* quit if problem */
        !          8414: /* --- rasterize input subexpression  --- */
        !          8415: mimeprep(subexpr);			/* preprocess subexpression */
        !          8416: if ( format == 1 )			/* dtoa() */
        !          8417:  { double d = strtod(subexpr,NULL);	/* interpret subexpr as double */
        !          8418:    if ( d != 0.0 )			/* conversion to double successful */
        !          8419:     if ( (reformat=dtoa(d,npts)) != NULL ) /* reformat successful */
        !          8420:      strcpy(subexpr,reformat); }	/*replace subexpr with reformatted*/
        !          8421: inputsp = rasterize(subexpr,size);	/* rasterize subexpression */
        !          8422: /* --- return input image to caller --- */
        !          8423: end_of_job:
        !          8424:   return ( inputsp );			/* return input image to caller */
        !          8425: } /* --- end-of-function rastinput() --- */
        !          8426: 
        !          8427: 
        !          8428: /* ==========================================================================
        !          8429:  * Function:	rastcounter ( expression, size, basesp, arg1, arg2, arg3 )
        !          8430:  * Purpose:	\counter[value]{filename} handler, returns subraster
        !          8431:  *		containing image of counter value read from filename
        !          8432:  *		(or optional [value]), and increments counter
        !          8433:  * --------------------------------------------------------------------------
        !          8434:  * Arguments:	expression (I/O) char **  to first char of null-terminated
        !          8435:  *				string immediately following \counter to be
        !          8436:  *				rasterized, and returning ptr immediately
        !          8437:  *				following last character processed.
        !          8438:  *		size (I)	int containing 0-5 default font size
        !          8439:  *		basesp (I)	subraster *  to character (or subexpression)
        !          8440:  *				immediately preceding \counter
        !          8441:  *				(unused, but passed for consistency)
        !          8442:  *		arg1 (I)	int unused
        !          8443:  *		arg2 (I)	int unused
        !          8444:  *		arg3 (I)	int unused
        !          8445:  * --------------------------------------------------------------------------
        !          8446:  * Returns:	( subraster * )	ptr to subraster corresponding to \counter
        !          8447:  *				requested, or NULL for any parsing error
        !          8448:  * --------------------------------------------------------------------------
        !          8449:  * Notes:     o	Summary of syntax...
        !          8450:  *		  \counter[value][logfile]{filename}
        !          8451:  *	      o
        !          8452:  * ======================================================================= */
        !          8453: /* --- entry point --- */
        !          8454: subraster *rastcounter ( char **expression, int size, subraster *basesp,
        !          8455: 			int arg1, int arg2, int arg3 )
        !          8456: {
        !          8457: /* -------------------------------------------------------------------------
        !          8458: Allocations and Declarations
        !          8459: -------------------------------------------------------------------------- */
        !          8460: char	*texsubexpr(), filename[1024]="\000", /* counter file */
        !          8461: 	logfile[1024]="\000", tag[512]="\000"; /* log file and tag */
        !          8462: subraster *rasterize(), *countersp=NULL; /* rasterized counter image */
        !          8463: FILE	/* *fp=NULL,*/ *logfp=NULL; /* counter and log file pointers */
        !          8464: int	rastreadfile(), rastwritefile(); /* to read and write counter file */
        !          8465: char	text[2048] = "1_",	/* first (and only) line in counter file */
        !          8466: 	*delim = NULL,		/* delimiter in text */
        !          8467: 	utext[32] = "1_",	/* default delimiter */
        !          8468: 	*udelim = utext+1;	/* underscore delimiter */
        !          8469: char	*timestamp(),		/* timestamp for logging */
        !          8470: 	*dtoa();		/* double to comma-separated */
        !          8471: int	counter = 1,		/* atoi(text) (after _ removed, if present) */
        !          8472: 	gotcount = 0,		/* set true once counter value determined */
        !          8473: 	ordindex = (-1);	/* ordinal[] index to append ordinal suffix */
        !          8474: /*--- ordinal suffixes based on units digit of counter ---*/
        !          8475: static	char *ordinal[]={"th","st","nd","rd","th","th","th","th","th","th"};
        !          8476: static	char *logvars[]={"REMOTE_ADDR","HTTP_REFERER",NULL}; /* log vars*/
        !          8477: static	int  commentvar = 1;	/* logvars[commentvar] replaced by comment */
        !          8478: /* -------------------------------------------------------------------------
        !          8479: first obtain optional [value][logfile] args immediately following \counter
        !          8480: -------------------------------------------------------------------------- */
        !          8481: /* --- first check for optional \counter[value] --- */
        !          8482: if ( *(*expression) == '[' )		/* check for []-enclosed value */
        !          8483:   { *expression = texsubexpr(*expression,text,2047,"[","]",0,0);
        !          8484:     if ( *text != '\000' )		/* got counter value */
        !          8485:      if ( strlen(text) >= 1 )		/* and it's not an empty string */
        !          8486:       if ( isdigit((int)(*text)) )	/* leading 0-9 digit signals value */
        !          8487:        { counter = (int)(strtod(text,&udelim)+0.1); /* value and delim */
        !          8488: 	 gotcount = 1; }		/* signal we got counter value */
        !          8489:       else				/* not a digit, so must be logfile */
        !          8490: 	 strcpy(logfile,text);		/* so just copy it */
        !          8491:   } /* --- end-of-if(**expression=='[') --- */
        !          8492: /* --- next check for optional \counter[][logfile] --- */
        !          8493: if ( *(*expression) == '[' )		/* check for []-enclosed logfile */
        !          8494:   { *expression = texsubexpr(*expression,filename,1023,"[","]",0,0);
        !          8495:     if ( *text != '\000' )		/* got logfile value */
        !          8496:      if ( strlen(filename) >= 1 )	/* and it's not an empty string */
        !          8497:       if ( !(isdigit((int)(*filename)))	/*leading non-digit signals logfile*/
        !          8498:       ||   gotcount )			/* or we already got counter value */
        !          8499: 	 strcpy(logfile,filename);	/* so just copy it */
        !          8500:       else				/* 0-9 digit, so must be value */
        !          8501:        { strcpy(text,filename);		/* copy value to text line */
        !          8502: 	 counter = (int)(strtod(text,&udelim)+0.1); /* value and delim */
        !          8503: 	 gotcount = 1; }		/* signal we got counter value */
        !          8504:   } /* --- end-of-if(**expression=='[') --- */
        !          8505: /* -------------------------------------------------------------------------
        !          8506: obtain counter {filename} argument
        !          8507: -------------------------------------------------------------------------- */
        !          8508: /* --- parse for {filename} arg, and bump expression past it --- */
        !          8509: *expression = texsubexpr(*expression,filename,1023,"{","}",0,0);
        !          8510: /* --- check for counter filename:tag --- */
        !          8511: if ( *filename != '\000' )		/* got filename */
        !          8512:  if ( (delim=strchr(filename,':'))	/* look for : in filename:tag */
        !          8513:  !=   (char *)NULL )			/* found it */
        !          8514:   { *delim = '\000';			/* null-terminate filename at : */
        !          8515:     strcpy(tag,delim+1); }		/* and stuff after : is tag */
        !          8516: /* --------------------------------------------------------------------------
        !          8517: Read and parse file, increment and rewrite counter (with optional underscore)
        !          8518: -------------------------------------------------------------------------- */
        !          8519: if ( strlen(filename) > 1 )		/* make sure we got {filename} arg */
        !          8520:   {
        !          8521:   /* --- read and interpret first (and only) line from counter file --- */
        !          8522:   if ( !gotcount )			/* if no [count] argument supplied */
        !          8523:    if ( rastreadfile(filename,tag,text) != 0 ) /* try reading it from file */
        !          8524:     { counter= 1 + (int)(strtod(text,&udelim)+0.1); /*counter val and delim*/
        !          8525:       gotcount = 1; }			/* signal we got counter value */
        !          8526:   /* --- check for ordinal suffix --- */
        !          8527:   if ( udelim != (char *)NULL )		/* have some delim after value */
        !          8528:    if ( *udelim == '_' )		/* underscore signals ordinal */
        !          8529:     { ordindex = counter%10;		/* least significant digit */
        !          8530:       if ( counter >= 10 )		/* counter is 10 or greater */
        !          8531:        if ( (counter/10)%10 == 1 )	/* and the last two are 10-19 */
        !          8532: 	ordindex = 0; }		/* use th for 11,12,13 rather than st,nd,rd */
        !          8533:   /* --- rewrite counter file --- */
        !          8534:   sprintf(text,"%d",counter);		/*build image of incremented counter*/
        !          8535:   if ( ordindex >= 0 ) strcat(text,"_"); /* tack on _ */
        !          8536:   if ( *tag == '\000' ) strcat(text,"\n"); /* and newline */
        !          8537:   rastwritefile(filename,tag,text,1);	/* rewrite incremented counter */
        !          8538:   } /* --- end-of-if(strlen(filename)>1) --- */
        !          8539: /* --------------------------------------------------------------------------
        !          8540: log counter request
        !          8541: -------------------------------------------------------------------------- */
        !          8542: if ( strlen(logfile) > 1 )		/* optional [logfile] given */
        !          8543:  {
        !          8544:  char	comment[1024] = "\000",		/* embedded comment, logfile:comment*/
        !          8545: 	*commptr = strchr(logfile,':');	/* check for : signalling comment */
        !          8546:  if ( commptr != NULL )			/* have embedded comment */
        !          8547:   { strcpy(comment,commptr+1);		/* comment follows : */
        !          8548:     *commptr = '\000'; }		/* null-terminate actual logfile */
        !          8549:  if ( (logfp = fopen(logfile,"a"))	/* open logfile */
        !          8550:  != (FILE *)NULL )			/* opened successfully */
        !          8551:   {
        !          8552:   int	ilog=0;				/* logvars[] index */
        !          8553:   fprintf(logfp,"%s  ",timestamp());	/* first emit timestamp */
        !          8554:   if (*tag=='\000') fprintf(logfp,"%s",filename); /* emit counter filename */
        !          8555:   else fprintf(logfp,"<%s>",tag);	/* or tag if we have one */
        !          8556:   fprintf(logfp,"=%d",counter);		/* emit counter value */
        !          8557:   for ( ilog=0; logvars[ilog] != NULL; ilog++ ) /* log till end-of-table */
        !          8558:    if ( ilog == commentvar		/* replace with comment... */
        !          8559:    &&   commptr != NULL )		/* ...if available */  
        !          8560:     fprintf(logfp,"  %.256s",comment); /* log embedded comment */
        !          8561:    else
        !          8562:     { char *logval = getenv(logvars[ilog]); /* getenv(variable) to be logged*/
        !          8563:       fprintf(logfp,"  %.64s",	/* log variable */
        !          8564: 	(logval!=NULL?logval:"<unknown>")); } /* emit value or <unknown> */
        !          8565:   fprintf(logfp,"\n");			/* terminating newline */
        !          8566:   fclose(logfp);			/* close logfile */
        !          8567:   } /* --- end-of-if(logfp!=NULL) --- */
        !          8568:  } /* --- end-of-if(strlen(logfile)>1) --- */
        !          8569: /* --------------------------------------------------------------------------
        !          8570: construct counter expression and rasterize it
        !          8571: -------------------------------------------------------------------------- */
        !          8572: /* --- construct expression --- */
        !          8573: /*sprintf(text,"%d",counter);*/		/* start with counter */
        !          8574: strcpy(text,dtoa(((double)counter),0));	/* comma-separated counter value */
        !          8575: if ( ordindex >= 0 )			/* need to tack on ordinal suffix */
        !          8576:   { strcat(text,"^{\\underline{\\rm~");	/* start with ^ and {\underline{\rm */
        !          8577:     strcat(text,ordinal[ordindex]);	/* then st,nd,rd, or th */
        !          8578:     strcat(text,"}}"); }		/* finish with }} */
        !          8579: /* --- rasterize it --- */
        !          8580: countersp = rasterize(text,size);	/* rasterize counter subexpression */
        !          8581: /* --- return counter image to caller --- */
        !          8582: /*end_of_job:*/
        !          8583:   return ( countersp );			/* return counter image to caller */
        !          8584: } /* --- end-of-function rastcounter() --- */
        !          8585: 
        !          8586: 
        !          8587: /* ==========================================================================
        !          8588:  * Function:	rastnoop ( expression, size, basesp, nargs, arg2, arg3 )
        !          8589:  * Purpose:	no op -- flush \escape without error
        !          8590:  * --------------------------------------------------------------------------
        !          8591:  * Arguments:	expression (I/O) char **  to first char of null-terminated
        !          8592:  *				string immediately following \escape to be
        !          8593:  *				flushed, and returning ptr immediately
        !          8594:  *				following last character processed.
        !          8595:  *		size (I)	int containing 0-5 default font size
        !          8596:  *		basesp (I)	subraster *  to character (or subexpression)
        !          8597:  *				immediately preceding \fbox
        !          8598:  *				(unused, but passed for consistency)
        !          8599:  *		nargs (I)	int containing number of {}-args after
        !          8600:  *				\escape to be flushed along with it
        !          8601:  *		arg2 (I)	int unused
        !          8602:  *		arg3 (I)	int unused
        !          8603:  * --------------------------------------------------------------------------
        !          8604:  * Returns:	( subraster * )	NULL subraster ptr
        !          8605:  * --------------------------------------------------------------------------
        !          8606:  * Notes:     o
        !          8607:  * ======================================================================= */
        !          8608: /* --- entry point --- */
        !          8609: subraster *rastnoop ( char **expression, int size, subraster *basesp,
        !          8610: 			int nargs, int arg2, int arg3 )
        !          8611: {
        !          8612: /* -------------------------------------------------------------------------
        !          8613: Allocations and Declarations
        !          8614: -------------------------------------------------------------------------- */
        !          8615: char	*texsubexpr(), subexpr[8192];	/* flush dummy args eaten by \escape*/
        !          8616: subraster *rasterize(), *noopsp=NULL;	/* rasterize subexpr */
        !          8617: /* --- flush accompanying args if necessary --- */
        !          8618: if ( nargs != NOVALUE			/* not unspecified */
        !          8619: &&   nargs > 0 )			/* and args to be flushed */
        !          8620:   while ( --nargs >= 0 )		/* count down */
        !          8621:     *expression = texsubexpr(*expression,subexpr,0,"{","}",0,0); /*flush arg*/
        !          8622: /* --- return null ptr to caller --- */
        !          8623: /*end_of_job:*/
        !          8624:   return ( noopsp );			/* return NULL ptr to caller */
        !          8625: } /* --- end-of-function rastnoop() --- */
        !          8626: 
        !          8627: 
        !          8628: /* ==========================================================================
        !          8629:  * Function:	rastopenfile ( filename, mode )
        !          8630:  * Purpose:	Opens filename[.tex] in mode, returning FILE *
        !          8631:  * --------------------------------------------------------------------------
        !          8632:  * Arguments:	filename (I/O)	char * to null-terminated string containing
        !          8633:  *				name of file to open (preceded by path
        !          8634:  *				relative to mimetex executable)
        !          8635:  *				If fopen() fails, .tex appeneded,
        !          8636:  *				and returned if that fopen() succeeds
        !          8637:  *		mode (I)	char * to null-terminated string containing
        !          8638:  *				fopen() mode
        !          8639:  * --------------------------------------------------------------------------
        !          8640:  * Returns:	( FILE * )	pointer to opened file, or NULL if error
        !          8641:  * --------------------------------------------------------------------------
        !          8642:  * Notes:     o
        !          8643:  * ======================================================================= */
        !          8644: /* --- entry point --- */
        !          8645: FILE	*rastopenfile ( char *filename, char *mode )
        !          8646: {
        !          8647: /* -------------------------------------------------------------------------
        !          8648: Allocations and Declarations
        !          8649: -------------------------------------------------------------------------- */
        !          8650: FILE	*fp = (FILE *)NULL /*,*fopen()*/; /*file pointer to opened filename*/
        !          8651: char	texfile[2048] = "\000",		/* local copy of input filename */
        !          8652: 	amode[128] = "r";		/* test open mode if arg mode=NULL */
        !          8653: int	ismode = 0,			/* true of mode!=NULL */
        !          8654: 	isprefix = (*pathprefix=='\000'?0:1); /* true if paths have prefix */
        !          8655: /* --------------------------------------------------------------------------
        !          8656: Check mode and open file
        !          8657: -------------------------------------------------------------------------- */
        !          8658: /* --- check filename --- */
        !          8659: if ( filename != (char *)NULL )		/* caller passed filename arg */
        !          8660:  if ( strlen(filename) >= 1 )		/* make sure we got actual filename*/
        !          8661:   { char *pfilename = filename;		/* ptr to 1st char of filename */
        !          8662:     *texfile = '\000';			/* init filename as null string */
        !          8663:     while ( isthischar(*pfilename," /\\") ) /* absolute paths invalid */
        !          8664:       pfilename++;			/* so flush leading / or \ (or ' ')*/
        !          8665:     if ( isprefix && *pfilename!='\000' ) /* paths preceded by prefix */
        !          8666:       {	strcat(texfile,pathprefix);	/* init filename with path */
        !          8667: 	while (	memcmp(pfilename,"../",3)==0 /* have leading ../ */
        !          8668: 	  || memcmp(pfilename,"..\\",3)==0 ) /* or ..\ with prefix */
        !          8669: 	    pfilename += 3; }		/* flush leading ../ or ..\ */
        !          8670:     strcat(texfile,pfilename);		/* local copy of given filename */
        !          8671:     compress(texfile,' '); }		/* remove embedded blanks */
        !          8672: /* --- check mode --- */
        !          8673: if ( mode != (char *)NULL )		/* caller passed mode arg */
        !          8674:  if ( *mode != '\000' )			/* and it's not an empty string */
        !          8675:   { ismode = 1;				/* so flip mode flag true */
        !          8676:     strcpy(amode,mode);			/* and replace "r" with caller's */
        !          8677:     compress(amode,' '); }		/* remove embedded blanks */
        !          8678: /* --- open filename or filename.tex --- */
        !          8679: if ( strlen(texfile) > 1 )		/* make sure we got actual filename*/
        !          8680:   if ( (fp = fopen(texfile,amode))	/* try opening given filename */
        !          8681:   ==   NULL )				/* failed to open given filename */
        !          8682:   { strcpy(filename,texfile);		/* signal possible filename error */
        !          8683:     strcat(texfile,".tex");		/* but first try adding .tex */
        !          8684:     if ( (fp = fopen(texfile,amode))	/* now try opening filename.tex */
        !          8685:     !=   NULL )				/* filename.tex succeeded */
        !          8686:       strcpy(filename,texfile); }	/* replace caller's filename */
        !          8687: /* --- close file if only opened to check name --- */
        !          8688: if ( !ismode && fp!=NULL )		/* no mode, so just checking */
        !          8689:   fclose(fp);				/* close file, fp signals success */
        !          8690: /* --- return fp or NULL to caller --- */
        !          8691: /*end_of_job:*/
        !          8692:   if ( msglevel>=9 && msgfp!=NULL )	/* debuging */
        !          8693:     { fprintf(msgfp,"rastopenfile> returning fopen(%s,%s) = %s\n",
        !          8694:       filename,amode,(fp==NULL?"NULL":"Okay")); fflush(msgfp); }
        !          8695:   return ( fp );			/* return fp or NULL to caller */
        !          8696: } /* --- end-of-function rastopenfile() --- */
        !          8697: 
        !          8698: 
        !          8699: /* ==========================================================================
        !          8700:  * Function:	rastreadfile ( filename, tag, value )
        !          8701:  * Purpose:	Read filename, returning value as string
        !          8702:  *		between <tag>...</tag> or entire file if tag=NULL passed.
        !          8703:  * --------------------------------------------------------------------------
        !          8704:  * Arguments:	filename (I)	char * to null-terminated string containing
        !          8705:  *				name of file to read (preceded by path
        !          8706:  *				relative to mimetex executable)
        !          8707:  *		tag (I)		char * to null-terminated string containing
        !          8708:  *				html-like tagname.  File contents between
        !          8709:  *				<tag> and </tag> will be returned, or
        !          8710:  *				entire file if tag=NULL passed.
        !          8711:  *		value (O)	char * returning value between <tag>...</tag>
        !          8712:  *				or entire file if tag=NULL.
        !          8713:  * --------------------------------------------------------------------------
        !          8714:  * Returns:	( int )		1=okay, 0=some error
        !          8715:  * --------------------------------------------------------------------------
        !          8716:  * Notes:     o
        !          8717:  * ======================================================================= */
        !          8718: /* --- entry point --- */
        !          8719: int	rastreadfile ( char *filename, char *tag, char *value )
        !          8720: {
        !          8721: /* -------------------------------------------------------------------------
        !          8722: Allocations and Declarations
        !          8723: -------------------------------------------------------------------------- */
        !          8724: FILE	*fp = (FILE *)NULL, *rastopenfile(); /* pointer to opened filename */
        !          8725: char	texfile[2048] = "\000",		/* local copy of input filename */
        !          8726: 	text[4096];			/* line from input file */
        !          8727: char	*tagp, tag1[512], tag2[512];	/* left <tag> and right <tag/> */
        !          8728: int	vallen=0, maxvallen=8000;	/* #chars in value, max allowed */
        !          8729: int	status = 0;			/* status returned, 1=okay */
        !          8730: int	tagnum = 0;			/* tag we're looking for */
        !          8731: /* --------------------------------------------------------------------------
        !          8732: Open file
        !          8733: -------------------------------------------------------------------------- */
        !          8734: /* --- first check output arg --- */
        !          8735: if ( value == (char *)NULL ) goto end_of_job; /* no output buffer supplied */
        !          8736: *value = '\000';			/* init buffer with empty string */
        !          8737: /* --- open filename or filename.tex --- */
        !          8738: if ( filename != (char *)NULL )		/* make sure we got filename arg */
        !          8739:   { strcpy(texfile,filename);		/* local copy of filename */
        !          8740:     fp = rastopenfile(texfile,"r"); }	/* try opening it */
        !          8741: /* --- check that file opened --- */
        !          8742: if ( fp == (FILE *)NULL )		/* failed to open file */
        !          8743:   { sprintf(value,"{\\normalsize\\rm[file %s?]}",texfile);
        !          8744:     goto end_of_job; }			/* return error message to caller */
        !          8745: /* --------------------------------------------------------------------------
        !          8746: construct <tag>'s
        !          8747: -------------------------------------------------------------------------- */
        !          8748: if ( tag != (char *)NULL )		/* caller passed tag arg */
        !          8749:  if ( *tag != '\000' )			/* and it's not an empty string */
        !          8750:   { strcpy(tag1,"<"); strcpy(tag2,"</"); /* begin with < and </ */
        !          8751:     strcat(tag1,tag); strcat(tag2,tag);	/* followed by caller's tag */
        !          8752:     strcat(tag1,">"); strcat(tag2,">");	/* ending both tags with > */
        !          8753:     compress(tag1,' '); compress(tag2,' '); /* remove embedded blanks */
        !          8754:     tagnum = 1; }			/* signal that we have tag */
        !          8755: /* --------------------------------------------------------------------------
        !          8756: Read file, concatnate lines
        !          8757: -------------------------------------------------------------------------- */
        !          8758: while ( fgets(text,4090,fp) != (char *)NULL ) { /* read input till eof */
        !          8759:   switch ( tagnum ) {			/* look for left- or right-tag */
        !          8760:     case 0: break;			/* no tag to look for */
        !          8761:     case 1:				/* looking for opening left <tag> */
        !          8762:       if ( (tagp=strstr(text,tag1)) == NULL ) break; /*haven't found it yet*/
        !          8763:       strcpy(text,tagp+strlen(tag1));	/* shift out preceding text */
        !          8764:       tagnum = 2;			/*now looking for closing right tag*/
        !          8765:     case 2:				/* looking for closing right </tag> */
        !          8766:       if ( (tagp=strstr(text,tag2)) == NULL ) break; /*haven't found it yet*/
        !          8767:       *tagp = '\000';			/* terminate line at tag */
        !          8768:       tagnum = 3;			/* done after this line */
        !          8769:       break;
        !          8770:     } /* ---end-of-switch(tagnum) --- */
        !          8771:   if ( tagnum != 1 ) {			/* no tag or left tag already found*/
        !          8772:     int	textlen = strlen(text);		/* #chars in current line */
        !          8773:     if ( vallen+textlen > maxvallen ) break; /* quit before overflow */
        !          8774:     strcat(value,text);			/* concat line to end of value */
        !          8775:     vallen += textlen;			/* bump length */
        !          8776:     if ( tagnum > 2 ) break; }		/* found right tag, so we're done */
        !          8777:   } /* --- end-of-while(fgets()!=NULL) --- */
        !          8778: if ( tagnum<1 || tagnum>2 ) status=1;	/* okay if no tag or we found tag */
        !          8779: fclose ( fp );				/* close input file after reading */
        !          8780: /* --- return value and status to caller --- */
        !          8781: end_of_job:
        !          8782:   return ( status );			/* return status to caller */
        !          8783: } /* --- end-of-function rastreadfile() --- */
        !          8784: 
        !          8785: 
        !          8786: /* ==========================================================================
        !          8787:  * Function:	rastwritefile ( filename, tag, value, isstrict )
        !          8788:  * Purpose:	Re/writes filename, replacing string between <tag>...</tag>
        !          8789:  *		with value, or writing entire file as value if tag=NULL.
        !          8790:  * --------------------------------------------------------------------------
        !          8791:  * Arguments:	filename (I)	char * to null-terminated string containing
        !          8792:  *				name of file to write (preceded by path
        !          8793:  *				relative to mimetex executable)
        !          8794:  *		tag (I)		char * to null-terminated string containing
        !          8795:  *				html-like tagname.  File contents between
        !          8796:  *				<tag> and </tag> will be replaced, or
        !          8797:  *				entire file written if tag=NULL passed.
        !          8798:  *		value (I)	char * containing string replacing value
        !          8799:  *				between <tag>...</tag> or replacing entire
        !          8800:  *				file if tag=NULL.
        !          8801:  *		isstrict (I)	int containing 1 to only rewrite existing
        !          8802:  *				files, or 0 to create new file if necessary.
        !          8803:  * --------------------------------------------------------------------------
        !          8804:  * Returns:	( int )		1=okay, 0=some error
        !          8805:  * --------------------------------------------------------------------------
        !          8806:  * Notes:     o
        !          8807:  * ======================================================================= */
        !          8808: /* --- entry point --- */
        !          8809: int	rastwritefile( char *filename, char *tag, char *value, int isstrict )
        !          8810: {
        !          8811: /* -------------------------------------------------------------------------
        !          8812: Allocations and Declarations
        !          8813: -------------------------------------------------------------------------- */
        !          8814: FILE	*fp = (FILE *)NULL, *rastopenfile(); /* pointer to opened filename */
        !          8815: char	texfile[2048] = "\000",		/* local copy of input filename */
        !          8816: 	filebuff[16384] = "\000",	/* entire contents of file */
        !          8817: 	tag1[512], tag2[512],		/* left <tag> and right <tag/> */
        !          8818: 	*strchange(),			/* put value between <tag>...</tag>*/
        !          8819: 	*timestamp();			/* log modification time */
        !          8820: int	istag=0, rastreadfile(),	/* read file if tag!=NULL */
        !          8821: 	/*isstrict = (seclevel>5? 1:0),*/ /*true only writes existing files*/
        !          8822: 	isnewfile = 0,			/* true if writing new file */
        !          8823: 	status = 0;			/* status returned, 1=okay */
        !          8824: /* --------------------------------------------------------------------------
        !          8825: check args
        !          8826: -------------------------------------------------------------------------- */
        !          8827: /* --- check filename and value --- */
        !          8828: if ( filename == (char *)NULL		/* quit if no filename arg supplied*/
        !          8829: ||   value == (char *)NULL ) goto end_of_job; /* or no value arg supplied */
        !          8830: if ( strlen(filename) < 2		/* quit if unreasonable filename */
        !          8831: ||   *value == '\000' ) goto end_of_job; /* or empty value string supplied */
        !          8832: /* --- establish filename[.tex] --- */
        !          8833: strcpy(texfile,filename);		/* local copy of input filename */
        !          8834: if ( rastopenfile(texfile,NULL)		/* unchanged or .tex appended */
        !          8835: ==   (FILE *)NULL )			/* can't open, so write new file */
        !          8836:   { if ( isstrict ) goto end_of_job;	/* fail if new files not permitted */
        !          8837:     isnewfile = 1; }			/* signal we're writing new file */
        !          8838: /* --- check whether tag supplied by caller --- */
        !          8839: if ( tag != (char *)NULL )		/* caller passed tag argument */
        !          8840:  if ( *tag != '\000' )			/* and it's not an empty string */
        !          8841:   { istag = 1;				/* so flip tag flag true */
        !          8842:     strcpy(tag1,"<"); strcpy(tag2,"</");  /* begin tags with < and </ */
        !          8843:     strcat(tag1,tag); strcat(tag2,tag);   /* followed by caller's tag */
        !          8844:     strcat(tag1,">"); strcat(tag2,">");	/* ending both tags with > */
        !          8845:     compress(tag1,' '); compress(tag2,' '); } /* remove embedded blanks */
        !          8846: /* --------------------------------------------------------------------------
        !          8847: read existing file if just rewriting a single tag
        !          8848: -------------------------------------------------------------------------- */
        !          8849: /* --- read original file if only replacing a tag within it --- */
        !          8850: *filebuff = '\000';			/* init as empty file */
        !          8851: if ( !isnewfile )			/* if file already exists */
        !          8852:  if ( istag )				/* and just rewriting one tag */
        !          8853:   if ( rastreadfile(texfile,NULL,filebuff) /* read entire existing file */
        !          8854:   ==   0 ) goto end_of_job;		/* signal error if failed to read */
        !          8855: /* --------------------------------------------------------------------------
        !          8856: construct new file data if needed (entire file replaced by value if no tag)
        !          8857: -------------------------------------------------------------------------- */
        !          8858: if ( istag )				/* only replacing tag in file */
        !          8859:  {
        !          8860:  /* --- find <tag> and </tag> in file --- */
        !          8861:  int	tlen1=strlen(tag1),  tlen2=strlen(tag2), flen;  /*tag,buff lengths*/
        !          8862:  char	*tagp1 = (isnewfile? NULL:strstr(filebuff,tag1)), /* <tag> in file*/
        !          8863: 	*tagp2 = (isnewfile? NULL:strstr(filebuff,tag2)); /*</tag> in file*/
        !          8864:  /* --- if adding new <tag> just concatanate at end of file --- */
        !          8865:  if ( tagp1 == (char *)NULL )		/* add new tag to file */
        !          8866:   {
        !          8867:   /* --- preprocess filebuff --- */
        !          8868:   if ( tagp2 != (char *)NULL )		/* apparently have ...</tag> */
        !          8869:     strcpy(filebuff,tagp2+tlen2);	/* so get rid of leading ...</tag> */
        !          8870:   if ( (flen = strlen(filebuff))	/* #chars currently in buffer */
        !          8871:   > 0 )					/* we have non-empty buffer */
        !          8872:    if (!isthischar(*(filebuff+flen-1),"\n\r")) /*no newline at end of file*/
        !          8873:      if(0)strcat(filebuff,"\n");	/* so add one before new tag */
        !          8874:   /* --- add new tag --- */
        !          8875:   strcat(filebuff,tag1);		/* add opening <tag> */
        !          8876:   strcat(filebuff,value);		/* then value */
        !          8877:   strcat(filebuff,tag2);		/* finally closing </tag> */
        !          8878:   strcat(filebuff,"\n");		/* newline at end of file */
        !          8879:   } /* --- end-of-if(tagp1==NULL) --- */
        !          8880:  else					/* found existing opening <tag> */
        !          8881:   {
        !          8882:   if ( tagp2 == NULL )			/* apparently have <tag>... */
        !          8883:     { *(tagp1+tlen1) = '\000';		/* so get rid of trailing ... */
        !          8884:       strcat(filebuff,value);		/* then concatanate value */
        !          8885:       strcat(filebuff,tag2); }		/* and finally closing </tag> */
        !          8886:   else					/* else have <tag>...<tag/> */
        !          8887:    if ( (flen=((int)(tagp2-tagp1))-tlen1) /* len of .'s in <tag>...</tag> */
        !          8888:    >=   0 )				/* usually <tag> precedes </tag> */
        !          8889:     strchange(flen,tagp1+tlen1,value);	/* change ...'s to value */
        !          8890:    else					/* weirdly, </tag> precedes <tag> */
        !          8891:     { char fbuff[2048];			/* field buff for <tag>value</tag> */
        !          8892:       if ( (flen = ((int)(tagp1-tagp2))+tlen1) /* strlen(</tag>...<tag>) */
        !          8893:       <=   0 ) goto end_of_job;		/* must be internal error */
        !          8894:       strcpy(fbuff,tag1);		/* set opening <tag> */
        !          8895:       strcat(fbuff,value);		/* then value */
        !          8896:       strcat(fbuff,tag2);		/* finally closing </tag> */
        !          8897:       strchange(flen,tagp2,fbuff); }	/* replace original </tag>...<tag> */
        !          8898:   } /* --- end-of-if/else(tagp1==NULL) --- */
        !          8899:  } /* --- end-of-if(istag) --- */
        !          8900: /* --------------------------------------------------------------------------
        !          8901: rewrite file and return to caller
        !          8902: -------------------------------------------------------------------------- */
        !          8903: /* --- first open file for write --- */
        !          8904: if ( (fp=rastopenfile(texfile,"w"))	/* open for write */
        !          8905: ==   (FILE *)NULL ) goto end_of_job;	/* signal error if can't open */
        !          8906: /* --- rewrite and close file --- */
        !          8907: if ( fputs((istag?filebuff:value),fp)	/* write filebuff or value */
        !          8908: !=  EOF ) status = 1;			/* signal success if succeeded */
        !          8909: fclose ( fp );				/* close output file after writing */
        !          8910: /* --- modify timestamp --- */
        !          8911: if ( istag )				/* log mod time in tagged file */
        !          8912:  if ( strstr(tag,"timestamp") == (char *)NULL ) /* but avoid recursion */
        !          8913:   { char fbuff[2048];			/* field buff <timestamp> value */
        !          8914:     strcpy(fbuff,tag);			/* tag modified */
        !          8915:     strcat(fbuff," modified at ");	/* spacer */
        !          8916:     strcat(fbuff,timestamp());		/* start with timestamp */
        !          8917:     rastwritefile(filename,"timestamp",fbuff,1); }
        !          8918: /* --- return status to caller --- */
        !          8919: end_of_job:
        !          8920:   return ( status );			/* return status to caller */
        !          8921: } /* --- end-of-function rastwritefile() --- */
        !          8922: 
        !          8923: 
        !          8924: /* ==========================================================================
        !          8925:  * Function:	timestamp ( )
        !          8926:  * Purpose:	returns null-terminated character string containing
        !          8927:  *		current date:time stamp as ccyy-mm-dd:hh:mm:ss{am,pm}
        !          8928:  * --------------------------------------------------------------------------
        !          8929:  * Arguments:	( none )
        !          8930:  * --------------------------------------------------------------------------
        !          8931:  * Returns:	( char * )	ptr to null-terminated buffer
        !          8932:  *				containing current date:time stamp
        !          8933:  * --------------------------------------------------------------------------
        !          8934:  * Notes:     o
        !          8935:  * ======================================================================= */
        !          8936: /* --- entry point --- */
        !          8937: char	*timestamp( )
        !          8938: {
        !          8939: /* -------------------------------------------------------------------------
        !          8940: Allocations and Declarations
        !          8941: -------------------------------------------------------------------------- */
        !          8942: static	char timebuff[64];		/* date:time buffer back to caller */
        !          8943: /*long	time_val = 0L;*/		/* binary value returned by time() */
        !          8944: time_t	time_val = (time_t)(0);		/* binary value returned by time() */
        !          8945: struct tm *tmstruct=(struct tm *)NULL, *localtime(); /* interpret time_val */
        !          8946: int	year=0, hour=0,ispm=1;		/* adjust year, and set am/pm hour */
        !          8947: /* -------------------------------------------------------------------------
        !          8948: get current date:time, adjust values, and and format stamp
        !          8949: -------------------------------------------------------------------------- */
        !          8950: /* --- get current date:time --- */
        !          8951: time((time_t *)(&time_val));		/* get date and time */
        !          8952: tmstruct = localtime((time_t *)(&time_val)); /* interpret time_val */
        !          8953: /* --- adjust year and hour as necessary --- */
        !          8954: year = (int)(tmstruct->tm_year);	/* local copy of year */
        !          8955: hour = (int)(tmstruct->tm_hour);	/* local copy of hour */
        !          8956: year += 1900;				/* set century in year */
        !          8957: if ( hour < 12 )			/* am check */
        !          8958:   { ispm=0;				/* reset pm flag */
        !          8959:     if ( hour == 0 ) hour = 12; }	/* set 00hrs = 12am */
        !          8960: if ( hour > 12 ) hour -= 12;		/* pm check sets 13hrs to 1pm, etc */
        !          8961: /* --- format date:time stamp --- */
        !          8962: sprintf(timebuff,"%04d-%02d-%02d:%02d:%02d:%02d%s",
        !          8963:   year,(int)((tmstruct->tm_mon)+1),(int)(tmstruct->tm_mday),
        !          8964:   hour,(int)(tmstruct->tm_min),(int)(tmstruct->tm_sec),((ispm)?"pm":"am"));
        !          8965: return ( timebuff );			/* return stamp to caller */
        !          8966: } /* --- end-of-function timestamp() --- */
        !          8967: 
        !          8968: 
        !          8969: /* ==========================================================================
        !          8970:  * Function:	dtoa ( dblval, npts )
        !          8971:  * Purpose:	Converts double to ascii, in financial format
        !          8972:  *		(e.g., comma-separated and negatives enclosed in ()'s).
        !          8973:  * -------------------------------------------------------------------------
        !          8974:  * Arguments:	dblval (I)	double containing value to be converted.
        !          8975:  *		npts (I)	int containing #places after decimal point
        !          8976:  *				to be displayed in returned string.
        !          8977:  * Returns:	( char * )	null-terminated string containing
        !          8978:  *				double converted to financial format.
        !          8979:  * -------------------------------------------------------------------------
        !          8980:  * Notes:     o
        !          8981:  * ======================================================================= */
        !          8982: /* --- entry point --- */
        !          8983: char	*dtoa ( dblval, npts )
        !          8984: double	dblval;
        !          8985: int	npts;
        !          8986: {
        !          8987: /* -------------------------------------------------------------------------
        !          8988: Allocations and Declarations
        !          8989: ------------------------------------------------------------------------- */
        !          8990: static	char finval[128];		/* buffer returned to caller */
        !          8991: static	char digittbl[32]="0123456789*"; /* table of ascii decimal digits */
        !          8992: char	*finptr = finval;		/* ptr to next char being converted*/
        !          8993: double	floor();			/* integer which is glb(double) */
        !          8994: double	dbldigit;			/* to shift out digits from dblval */
        !          8995: int	digit;				/* one digit from dblval */
        !          8996: int	isneg = 0;			/* reset true if dblval negative */
        !          8997: int	ifrac = 0;			/* npts fractional digits of dblval*/
        !          8998: char	digits[64]; int ndigits=0;	/* all the digits [0]=least signif */
        !          8999: /* -------------------------------------------------------------------------
        !          9000: Check sign
        !          9001: ------------------------------------------------------------------------- */
        !          9002: if ( dblval < 0.0 )			/* got a negative value to convert */
        !          9003:     { isneg=1; dblval=(-dblval); }	/* set flag and make it positive */
        !          9004: /* -------------------------------------------------------------------------
        !          9005: Get fractional part of dblval if required
        !          9006: ------------------------------------------------------------------------- */
        !          9007: if ( npts > 0 )
        !          9008:     { int ipts = npts;			/* loop index */
        !          9009:       dbldigit = dblval-floor(dblval);	/* fractional part as double */
        !          9010:       digit = 1;			/* check if rounded frac > 1 */
        !          9011:       while ( --ipts >= 0 )		/* count down */
        !          9012: 	{ dbldigit *= 10.0;		/* shift left one digit at a time */
        !          9013: 	  digit *= 10; }		/* and keep max up-to-date */
        !          9014:       ifrac = (int)(dbldigit + 0.5);	/* store fractional part as integer*/
        !          9015:       if ( ifrac >= digit )		/* round to next whole number */
        !          9016: 	{ dblval++; ifrac=0; }		/* bump val, reset frac to zero */
        !          9017:     } /* --- end-of-if(npts>0) --- */
        !          9018: else dblval += 0.5;			/* no frac, round to nearest whole */
        !          9019: /* -------------------------------------------------------------------------
        !          9020: Get whole digits
        !          9021: ------------------------------------------------------------------------- */
        !          9022: dblval = floor(dblval);			/* get rid of fractional part */
        !          9023: while ( dblval > 0.0 )			/* still have data digits remaining*/
        !          9024:     { dbldigit = floor(dblval/10.0);	/* shift out next digit */
        !          9025:       digit = (int)(dblval - 10.0*dbldigit + 0.01); /* least signif digit */
        !          9026:       if ( digit<0 || digit>9 ) digit=10; /* index check */
        !          9027:       digits[ndigits++] = digittbl[digit]; /* store ascii digit */
        !          9028:       dblval = dbldigit; }		/* ready for next digit */
        !          9029: if ( ndigits < 1 ) digits[ndigits++] = '0'; /* store a single '0' for 0.0 */
        !          9030: /* -------------------------------------------------------------------------
        !          9031: Format whole part from digits[] array
        !          9032: ------------------------------------------------------------------------- */
        !          9033: if ( isneg ) *finptr++ = '(';		/* leading paren for negative value*/
        !          9034: for ( digit=ndigits-1; digit>=0; digit-- ) /* start with most significant */
        !          9035:     { *finptr++ = digits[digit];	/* store digit */
        !          9036:       if ( digit>0 && digit%3==0 )	/* need a comma */
        !          9037: 	*finptr++ = ','; }		/* put in separating comma */
        !          9038: /* -------------------------------------------------------------------------
        !          9039: Format fractional part using ifrac
        !          9040: ------------------------------------------------------------------------- */
        !          9041: if ( npts > 0 )
        !          9042:     { *finptr++ = '.';			/* start with decimal point */
        !          9043:       sprintf(finptr,"%0*d",npts,ifrac); /* convert to string */
        !          9044:       finptr += npts; }			/* bump ptr past fractional digits */
        !          9045: /* -------------------------------------------------------------------------
        !          9046: End-of-Job
        !          9047: ------------------------------------------------------------------------- */
        !          9048: if ( isneg ) *finptr++ = ')';		/*trailing paren for negative value*/
        !          9049: *finptr = '\000';			/* null-terminate converted double */
        !          9050: return ( finval );			/* converted double back to caller */
        !          9051: } /* --- end-of-function dtoa() --- */
        !          9052: 
        !          9053: 
        !          9054: /* ==========================================================================
        !          9055:  * Function:	aalowpass ( rp, bytemap, grayscale )
        !          9056:  * Purpose:	calculates a lowpass anti-aliased bytemap
        !          9057:  *		for rp->bitmap, with each byte 0...grayscale-1
        !          9058:  * --------------------------------------------------------------------------
        !          9059:  * Arguments:	rp (I)		raster *  to raster whose bitmap
        !          9060:  *				is to be anti-aliased
        !          9061:  *		bytemap (O)	intbyte * to bytemap, calculated
        !          9062:  *				by applying lowpass filter to rp->bitmap,
        !          9063:  *				and returned (as you'd expect) in 1-to-1
        !          9064:  *				addressing correspondence with rp->bitmap
        !          9065:  *		grayscale (I)	int containing number of grayscales
        !          9066:  *				to be calculated, 0...grayscale-1
        !          9067:  *				(should typically be given as 256)
        !          9068:  * --------------------------------------------------------------------------
        !          9069:  * Returns:	( int )		1=success, 0=any error
        !          9070:  * --------------------------------------------------------------------------
        !          9071:  * Notes:     o	If the center point of the box being averaged is black,
        !          9072:  *		then the entire "average" is forced black (grayscale-1)
        !          9073:  *		regardless of the surrounding points.  This is my attempt
        !          9074:  *		to avoid a "washed-out" appearance of thin (one-pixel-wide)
        !          9075:  *		lines, which would otherwise turn from black to a gray shade.
        !          9076:  *	     o	Also, while the weights for neighbor points are fixed,
        !          9077:  *		you may adjust the center point weight on the compile line.
        !          9078:  *		A higher weight sharpens the resulting anti-aliased image;
        !          9079:  *		lower weights blur it out more (but keep the "center" black
        !          9080:  *		as per the preceding note).
        !          9081:  * ======================================================================= */
        !          9082: /* --- entry point --- */
        !          9083: int	aalowpass (raster *rp, intbyte *bytemap, int grayscale)
        !          9084: {
        !          9085: /* -------------------------------------------------------------------------
        !          9086: Allocations and Declarations
        !          9087: -------------------------------------------------------------------------- */
        !          9088: int	status = 1;			/* 1=success, 0=failure to caller */
        !          9089: pixbyte	*bitmap= (rp==NULL?NULL:rp->pixmap); /*local rp->pixmap ptr*/
        !          9090: int	irow=0, icol=0;			/* rp->height, rp->width indexes */
        !          9091: int	weights[9] = { 1,3,1, 3,0,3, 1,3,1 }; /* matrix of weights */
        !          9092: int	adjindex[9]= { 0,1,2, 7,-1,3, 6,5,4 }; /*clockwise from upper-left*/
        !          9093: int	totwts = 0;			/* sum of all weights in matrix */
        !          9094: int	isforceavg = 1,			/*force avg black if center black?*/
        !          9095: 	isminmaxwts = 1,		/*use wts or #pts for min/max test */
        !          9096: 	blackscale = 0; /*(grayscale+1)/4;*/ /*force black if wgted avg>bs */
        !          9097: /* -------------------------------------------------------------------------
        !          9098: Initialization
        !          9099: -------------------------------------------------------------------------- */
        !          9100: /* --- calculate total weights --- */
        !          9101: weights[4]= centerwt;			/* weight for center point */
        !          9102: weights[1]= weights[3]= weights[5]= weights[7]= adjacentwt; /*adjacent pts*/
        !          9103: totwts = centerwt + 4*(1+adjacentwt);	/* tot is center plus neighbors */
        !          9104: /* -------------------------------------------------------------------------
        !          9105: Calculate bytemap as 9-point weighted average over bitmap
        !          9106: -------------------------------------------------------------------------- */
        !          9107: for ( irow=0; irow<rp->height; irow++ )
        !          9108:  for ( icol=0; icol<rp->width; icol++ )
        !          9109:   {
        !          9110:   int	ipixel = icol + irow*(rp->width); /* center pixel index */
        !          9111:   int	jrow=0, jcol=0,			/* box around ipixel */
        !          9112: 	bitval = 0,			/* value of bit/pixel at jrow,jcol */
        !          9113: 	iscenter = 0,			/* set true if center pixel black */
        !          9114: 	nadjacent=0, wadjacent=0,	/* #adjacent black pixels, their wts*/
        !          9115: 	ngaps = 0,			/* #gaps in 8 pixels around center */
        !          9116: 	iwt=(-1), sumwts=0;		/* weights index, sum in-bound wts */
        !          9117:   char	adjmatrix[8];			/* adjacency "matrix" */
        !          9118:   memset(adjmatrix,0,8);		/* zero out adjacency matrix */
        !          9119:   bytemap[ipixel] = 0;			/* init pixel white */
        !          9120:   /*--- for ipixel at irow,icol, get weighted average of adjacent pixels ---*/
        !          9121:   for ( jrow=irow-1; jrow<=irow+1; jrow++ )  /* jrow = irow-1...irow+1 */
        !          9122:    for ( jcol=icol-1; jcol<=icol+1; jcol++ ) /* jcol = icol-1...icol+1 */
        !          9123:     {
        !          9124:     int	jpixel = jcol + jrow*(rp->width); /* averaging index */
        !          9125:     iwt++;				/*always bump weight index*/
        !          9126:     if ( jrow<0 || jrow>=rp->height	/* if row out pf bounds */
        !          9127:     ||   jcol<0 || jcol>=rp->width )	/* or col out of bounds */
        !          9128: 	continue;			/* ignore this point */
        !          9129:     bitval = (int)getlongbit(bitmap,jpixel); /* value of bit at jrow,jcol */
        !          9130:     if ( bitval )			/* this is a black pixel */
        !          9131:       {	if ( jrow==irow && jcol==icol )	/* and this is center point */
        !          9132: 	  iscenter = 1;			/* set flag for center point black */
        !          9133: 	else				/* adjacent point black */
        !          9134: 	  { nadjacent++;		/* bump adjacent black count */
        !          9135: 	    adjmatrix[adjindex[iwt]] = 1; } /*set "bit" in adjacency matrix*/
        !          9136: 	wadjacent += weights[iwt]; }	/* sum weights for black pixels */
        !          9137:     sumwts += weights[iwt];		/* and sum weights for all pixels */
        !          9138:     } /* --- end-of-for(jrow,jcol) --- */
        !          9139:   /* --- count gaps --- */
        !          9140:   ngaps = (adjmatrix[7]!=adjmatrix[0]?1:0); /* init count */
        !          9141:   for ( iwt=0; iwt<7; iwt++ )		/* clockwise around adjacency */
        !          9142:     if ( adjmatrix[iwt] != adjmatrix[iwt+1] ) ngaps++; /* black/white flip */
        !          9143:   ngaps /= 2;				/*each gap has 2 black/white flips*/
        !          9144:   /* --- anti-alias pixel, but leave it black if it was already black --- */
        !          9145:   if ( isforceavg && iscenter )		/* force avg if center point black */
        !          9146:       bytemap[ipixel] = grayscale-1;	/* so force grayscale-1=black */
        !          9147:   else					/* center point not black */
        !          9148:    if ( ngaps <= 2 )			/*don't darken checkerboarded pixel*/
        !          9149:     { bytemap[ipixel] =			/* 0=white ... grayscale-1=black */
        !          9150: 	((totwts/2 - 1) + (grayscale-1)*wadjacent)/totwts; /* not /sumwts; */
        !          9151:       if ( blackscale > 0		/* blackscale kludge turned on */
        !          9152:       &&   bytemap[ipixel] > blackscale ) /* weighted avg > blackscale */
        !          9153: 	bytemap[ipixel] = grayscale-1; } /* so force it entirely black */
        !          9154:   /*--- only anti-alias pixels whose adjacent pixels fall within bounds ---*/
        !          9155:   if ( !iscenter )			/* apply min/maxadjacent test */
        !          9156:    if ( isminmaxwts )			/* min/max refer to adjacent weights*/
        !          9157:     { if ( wadjacent < minadjacent	/* wts of adjacent points too low */
        !          9158:       ||   wadjacent > maxadjacent )	/* or too high */
        !          9159: 	bytemap[ipixel] = 0; }		/* so leave point white */
        !          9160:    else					/* min/max refer to #adjacent points*/
        !          9161:     { if ( nadjacent < minadjacent	/* too few adjacent points black */
        !          9162:       ||   nadjacent > maxadjacent )	/* or too many */
        !          9163: 	bytemap[ipixel] = 0; }		/* so leave point white */
        !          9164:   } /* --- end-of-for(irow,icol) --- */
        !          9165: /* -------------------------------------------------------------------------
        !          9166: Back to caller with gray-scale anti-aliased bytemap
        !          9167: -------------------------------------------------------------------------- */
        !          9168: /*end_of_job:*/
        !          9169:   return ( status );
        !          9170: } /* --- end-of-function aalowpass() --- */
        !          9171: 
        !          9172: 
        !          9173: /* ==========================================================================
        !          9174:  * Function:	aapnm ( rp, bytemap, grayscale )
        !          9175:  * Purpose:	calculates a lowpass anti-aliased bytemap
        !          9176:  *		for rp->bitmap, with each byte 0...grayscale-1,
        !          9177:  *		based on the pnmalias.c algorithm
        !          9178:  * --------------------------------------------------------------------------
        !          9179:  * Arguments:	rp (I)		raster *  to raster whose bitmap
        !          9180:  *				is to be anti-aliased
        !          9181:  *		bytemap (O)	intbyte * to bytemap, calculated
        !          9182:  *				by applying pnm-based filter to rp->bitmap,
        !          9183:  *				and returned (as you'd expect) in 1-to-1
        !          9184:  *				addressing correspondence with rp->bitmap
        !          9185:  *		grayscale (I)	int containing number of grayscales
        !          9186:  *				to be calculated, 0...grayscale-1
        !          9187:  *				(should typically be given as 256)
        !          9188:  * --------------------------------------------------------------------------
        !          9189:  * Returns:	( int )		1=success, 0=any error
        !          9190:  * --------------------------------------------------------------------------
        !          9191:  * Notes:    o	Based on the pnmalias.c algorithm in the netpbm package
        !          9192:  *		on sourceforge.
        !          9193:  * ======================================================================= */
        !          9194: /* --- entry point --- */
        !          9195: int	aapnm (raster *rp, intbyte *bytemap, int grayscale)
        !          9196: {
        !          9197: /* -------------------------------------------------------------------------
        !          9198: Allocations and Declarations
        !          9199: -------------------------------------------------------------------------- */
        !          9200: pixbyte	*bitmap = rp->pixmap;		/* local rp->pixmap ptr */
        !          9201: int	width=rp->width, height=rp->height, /* width, height of raster */
        !          9202: 	icol = 0,        irow = 0,	/* width, height indexes */
        !          9203: 	imap = (-1);			/* pixel index = icol + irow*width */
        !          9204: int	bgbitval=0, fgbitval=1;		/* background, foreground bitval */
        !          9205: #if 0
        !          9206: int	totwts=12, wts[9]={1,1,1, 1,4,1, 1,1,1}; /* pnmalias default wts */
        !          9207: int	totwts=16, wts[9]={1,2,1, 2,4,2, 1,2,1}; /* weights */
        !          9208: #endif
        !          9209: int	totwts=18, wts[9]={1,2,1, 2,6,2, 1,2,1}; /* pnmalias default wts */
        !          9210: int	isresetparams = 1,		/* true to set antialiasing params */
        !          9211: 	isfgalias  = 1,			/* true to antialias fg bits */
        !          9212: 	isfgonly   = 0,			/* true to only antialias fg bits */
        !          9213: 	isbgalias  = 0,			/* true to antialias bg bits */
        !          9214: 	isbgonly   = 0;			/* true to only antialias bg bits */
        !          9215: /* -------------------------------------------------------------------------
        !          9216: Initialization
        !          9217: -------------------------------------------------------------------------- */
        !          9218: /* --- check for bold light --- */
        !          9219: if ( 0 )
        !          9220:  { if ( weightnum > 2 ) { isbgalias=1; }	/* simulate bold */
        !          9221:    if ( weightnum < 1 ) { isbgonly=1; isfgalias=0; } } /* simulate light */
        !          9222: /* --- reset wts[], etc, and calculate total weights --- */
        !          9223: if ( isresetparams )			/* wts[], etc taken from params */
        !          9224:   { int	iwt=0;				/* wts[iwt] index */
        !          9225:     wts[4]= centerwt;			/* weight for center point */
        !          9226:     wts[1]=wts[3]=wts[5]=wts[7] = adjacentwt; /* and adjacent points */
        !          9227:     wts[0]=wts[2]=wts[6]=wts[8] = cornerwt;   /* and corner points */
        !          9228:     for ( totwts=0,iwt=0; iwt<9; iwt++ ) totwts += wts[iwt]; /* sum wts */
        !          9229:     isfgalias = fgalias;		/* set isfgalias */
        !          9230:     isfgonly = fgonly;			/* set isfgonly */
        !          9231:     isbgalias = bgalias;		/* set isbgalias */
        !          9232:     isbgonly = bgonly; }		/* set isbgonly */
        !          9233: /* -------------------------------------------------------------------------
        !          9234: Calculate bytemap as 9-point weighted average over bitmap
        !          9235: -------------------------------------------------------------------------- */
        !          9236: for ( irow=0; irow<height; irow++ )
        !          9237:  for ( icol=0; icol<width; icol++ )
        !          9238:   {
        !          9239:   /* --- local allocations and declarations --- */
        !          9240:   int	bitval=0,			/* value of rp bit at irow,icol */
        !          9241: 	nnbitval=0, nebitval=0, eebitval=0, sebitval=0,	/*adjacent vals*/
        !          9242: 	ssbitval=0, swbitval=0, wwbitval=0, nwbitval=0;	/*compass pt names*/
        !          9243:   int	isbgedge=0, isfgedge=0;		/*does pixel border a bg or fg edge*/
        !          9244:   int	aabyteval=0;			/* antialiased (or unchanged) value*/
        !          9245:   /* --- bump imap index and get center bit value --- */
        !          9246:   imap++;				/* imap = icol + irow*width */
        !          9247:   bitval = getlongbit(bitmap,imap);	/* value of rp input bit at imap */
        !          9248:   aabyteval = (intbyte)(bitval==bgbitval?0:grayscale-1); /* default aa val */
        !          9249:   bytemap[imap] = (intbyte)(aabyteval);	/* init antialiased pixel */
        !          9250:   /* --- check if we're antialiasing this pixel --- */
        !          9251:   if ( (isbgonly && bitval==fgbitval)	/* only antialias background bit */
        !          9252:   ||   (isfgonly && bitval==bgbitval) )	/* only antialias foreground bit */
        !          9253:     continue;				/* leave default and do next bit */
        !          9254:   /* --- get surrounding bits --- */
        !          9255:   if ( irow > 0 )			/* nn (north) bit available */
        !          9256:      nnbitval = getlongbit(bitmap,imap-width); /* nn bit value */
        !          9257:   if ( irow < height-1 )		/* ss (south) bit available */
        !          9258:      ssbitval = getlongbit(bitmap,imap+width); /* ss bit value */
        !          9259:   if ( icol > 0 )			/* ww (west) bit available */
        !          9260:    { wwbitval = getlongbit(bitmap,imap-1); /* ww bit value */
        !          9261:      if ( irow > 0 )			/* nw bit available */
        !          9262:        nwbitval = getlongbit(bitmap,imap-width-1); /* nw bit value */
        !          9263:      if ( irow < height-1 )		/* sw bit available */
        !          9264:        swbitval = getlongbit(bitmap,imap+width-1); } /* sw bit value */
        !          9265:   if ( icol < width-1 )			/* ee (east) bit available */
        !          9266:    { eebitval = getlongbit(bitmap,imap+1); /* ee bit value */
        !          9267:      if ( irow > 0 )			/* ne bit available */
        !          9268:        nebitval = getlongbit(bitmap,imap-width+1); /* ne bit value */
        !          9269:      if ( irow < height-1 )		/* se bit available */
        !          9270:        sebitval = getlongbit(bitmap,imap+width+1); } /* se bit value */
        !          9271:   /* --- check for edges --- */
        !          9272:   isbgedge =				/* current pixel borders a bg edge */
        !          9273: 	(nnbitval==bgbitval && eebitval==bgbitval) ||	/*upper-right edge*/
        !          9274: 	(eebitval==bgbitval && ssbitval==bgbitval) ||	/*lower-right edge*/
        !          9275: 	(ssbitval==bgbitval && wwbitval==bgbitval) ||	/*lower-left  edge*/
        !          9276: 	(wwbitval==bgbitval && nnbitval==bgbitval) ;	/*upper-left  edge*/
        !          9277:   isfgedge =				/* current pixel borders an fg edge*/
        !          9278: 	(nnbitval==fgbitval && eebitval==fgbitval) ||	/*upper-right edge*/
        !          9279: 	(eebitval==fgbitval && ssbitval==fgbitval) ||	/*lower-right edge*/
        !          9280: 	(ssbitval==fgbitval && wwbitval==fgbitval) ||	/*lower-left  edge*/
        !          9281: 	(wwbitval==fgbitval && nnbitval==fgbitval) ;	/*upper-left  edge*/
        !          9282:   /* --- antialias if necessary --- */
        !          9283:   if ( (isbgalias && isbgedge)		/* alias pixel surrounding bg */
        !          9284:   ||   (isfgalias && isfgedge)		/* alias pixel surrounding fg */
        !          9285:   ||   (isbgedge  && isfgedge) )	/* neighboring fg and bg pixel */
        !          9286:     {
        !          9287:     int	aasumval =			/* sum wts[]*bitmap[] */
        !          9288: 	wts[0]*nwbitval + wts[1]*nnbitval + wts[2]*nebitval +
        !          9289: 	wts[3]*wwbitval +  wts[4]*bitval  + wts[5]*eebitval +
        !          9290: 	wts[6]*swbitval + wts[7]*ssbitval + wts[8]*sebitval ;
        !          9291:     double aawtval = ((double)aasumval)/((double)totwts); /* weighted val */
        !          9292:     aabyteval= (int)(((double)(grayscale-1))*aawtval+0.5); /*0...grayscale-1*/
        !          9293:     bytemap[imap] = (intbyte)(aabyteval); /* set antialiased pixel */
        !          9294:     if ( msglevel>=99 && msgfp!=NULL ) fprintf(msgfp,	/* debugging */
        !          9295:       "aapnm> irow,icol,imap=%d,%d,%d aawtval=%.4f aabyteval=%d\n",
        !          9296:       irow,icol,imap, aawtval,aabyteval);
        !          9297:     } /* --- end-of-if(isedge) --- */
        !          9298:   } /* --- end-of-for(irow,icol) --- */
        !          9299: /* -------------------------------------------------------------------------
        !          9300: Back to caller with gray-scale anti-aliased bytemap
        !          9301: -------------------------------------------------------------------------- */
        !          9302: /*end_of_job:*/
        !          9303:   return ( 1 );
        !          9304: } /* --- end-of-function aapnm() --- */
        !          9305: 
        !          9306: 
        !          9307: /* ==========================================================================
        !          9308:  * Function:	aasupsamp ( rp, aa, sf, grayscale )
        !          9309:  * Purpose:	calculates a supersampled anti-aliased bytemap
        !          9310:  *		for rp->bitmap, with each byte 0...grayscale-1
        !          9311:  * --------------------------------------------------------------------------
        !          9312:  * Arguments:	rp (I)		raster *  to raster whose bitmap
        !          9313:  *				is to be anti-aliased
        !          9314:  *		aa (O)		address of raster * to supersampled bytemap,
        !          9315:  *				calculated by supersampling rp->bitmap
        !          9316:  *		sf (I)		int containing supersampling shrinkfactor
        !          9317:  *		grayscale (I)	int containing number of grayscales
        !          9318:  *				to be calculated, 0...grayscale-1
        !          9319:  *				(should typically be given as 256)
        !          9320:  * --------------------------------------------------------------------------
        !          9321:  * Returns:	( int )		1=success, 0=any error
        !          9322:  * --------------------------------------------------------------------------
        !          9323:  * Notes:     o	If the center point of the box being averaged is black,
        !          9324:  *		then the entire "average" is forced black (grayscale-1)
        !          9325:  *		regardless of the surrounding points.  This is my attempt
        !          9326:  *		to avoid a "washed-out" appearance of thin (one-pixel-wide)
        !          9327:  *		lines, which would otherwise turn from black to a gray shade.
        !          9328:  * ======================================================================= */
        !          9329: /* --- entry point --- */
        !          9330: int	aasupsamp (raster *rp, raster **aa, int sf, int grayscale)
        !          9331: {
        !          9332: /* -------------------------------------------------------------------------
        !          9333: Allocations and Declarations
        !          9334: -------------------------------------------------------------------------- */
        !          9335: int	status = 0;			/* 1=success, 0=failure to caller */
        !          9336: int	rpheight=rp->height, rpwidth=rp->width, /*bitmap raster dimensions*/
        !          9337: 	heightrem=0, widthrem=0,	/* rp+rem is a multiple of shrinkf */
        !          9338: 	aaheight=0,  aawidth=0,		/* supersampled dimensions */
        !          9339: 	aapixsz=8;			/* output pixels are 8-bit bytes */
        !          9340: int	maxaaval=(-9999),		/* max grayscale val set in matrix */
        !          9341: 	isrescalemax=1;			/* 1=rescale maxaaval to grayscale */
        !          9342: int	irp=0,jrp=0, iaa=0,jaa=0, iwt=0,jwt=0; /*indexes, i=width j=height*/
        !          9343: raster	*aap=NULL, *new_raster();	/* raster for supersampled image */
        !          9344: raster	*aaweights();			/* get weight matrix applied to rp */
        !          9345: static	raster *aawts = NULL;		/* aaweights() resultant matrix */
        !          9346: static	int prevshrink = NOVALUE,	/* shrinkfactor from previous call */
        !          9347: 	sumwts = 0;			/* sum of weights */
        !          9348: static	int blackfrac = 40,		/* force black if this many pts are */
        !          9349: 	/*grayfrac = 20,*/
        !          9350: 	maxwt = 10,			/* max weight in weight matrix */
        !          9351: 	minwtfrac=10, maxwtfrac=70;	/* force light pts white, dark black*/
        !          9352: int	type_raster(), type_bytemap();	/* debugging display routines */
        !          9353: int	delete_raster();		/* delete old rasters */
        !          9354: /* -------------------------------------------------------------------------
        !          9355: Initialization
        !          9356: -------------------------------------------------------------------------- */
        !          9357: /* --- check args --- */
        !          9358: if ( aa == NULL ) goto end_of_job;	/* no ptr for return output arg */
        !          9359: *aa = NULL;				/* init null ptr for error return */
        !          9360: if ( rp == NULL				/* no ptr to input arg */
        !          9361: ||   sf < 1				/* invalid shrink factor */
        !          9362: ||   grayscale < 2 ) goto end_of_job;	/* invalid grayscale */
        !          9363: /* --- get weight matrix (or use current one) --- */
        !          9364: if ( sf != prevshrink )			/* have new shrink factor */
        !          9365:   { if ( aawts != NULL )		/* have unneeded weight matrix */
        !          9366:       delete_raster(aawts);		/* so free it */
        !          9367:     sumwts = 0;				/* reinitialize sum of weights */
        !          9368:     aawts = aaweights(sf,sf);		/* get new weight matrix */
        !          9369:     if ( aawts != NULL )		/* got weight matrix okay*/
        !          9370:       for ( jwt=0; jwt<sf; jwt++ )	/* for each row */
        !          9371:        for ( iwt=0; iwt<sf; iwt++ )	/* and each column */
        !          9372: 	{ int wt = (int)(getpixel(aawts,jwt,iwt)); /* weight */
        !          9373: 	  if ( wt > maxwt )		/* don't overweight center pts */
        !          9374: 	    { wt = maxwt;		/* scale it back */
        !          9375: 	      setpixel(aawts,jwt,iwt,wt); } /* and replace it in matrix */
        !          9376: 	  sumwts += wt; }		/* add weight to sum */
        !          9377:     prevshrink = sf; }			/* save new shrink factor */
        !          9378: if ( msgfp!=NULL && msglevel>=999 )
        !          9379:   { fprintf(msgfp,"aasupsamp> sf=%d, sumwts=%d weights=...\n", sf,sumwts);
        !          9380:     type_bytemap((intbyte *)aawts->pixmap,grayscale,
        !          9381:     aawts->width,aawts->height,msgfp); }
        !          9382: /* --- calculate supersampled height,width and allocate output raster */
        !          9383: heightrem = rpheight%sf;		/* remainder after division... */
        !          9384: widthrem  = rpwidth%sf;			/* ...by shrinkfactor */
        !          9385: aaheight  = 1+(rpheight+sf-(heightrem+1))/sf; /* ss height */
        !          9386: aawidth   = 1+(rpwidth+sf-(widthrem+1))/sf; /* ss width */
        !          9387: if ( msgfp!=NULL && msglevel>=999 )
        !          9388:  { fprintf(msgfp,"aasupsamp> rpwid,ht=%d,%d wd,htrem=%d,%d aawid,ht=%d,%d\n",
        !          9389:    rpwidth,rpheight, widthrem,heightrem, aawidth,aaheight);
        !          9390:    fprintf(msgfp,"aasupsamp> dump of original bitmap image...\n");
        !          9391:    type_raster(rp,msgfp); }		/* ascii image of rp raster */
        !          9392: if ( (aap = new_raster(aawidth,aaheight,aapixsz)) /* alloc output raster*/
        !          9393: ==   NULL ) goto end_of_job;		/* quit if alloc fails */
        !          9394: /* -------------------------------------------------------------------------
        !          9395: Step through rp->bitmap, applying aawts to each "submatrix" of bitmap
        !          9396: -------------------------------------------------------------------------- */
        !          9397: for ( jaa=0,jrp=(-(heightrem+1)/2); jrp<rpheight; jrp+=sf ) /* height */
        !          9398:  {
        !          9399:  for ( iaa=0,irp=(-(widthrem+1)/2); irp<rpwidth; irp+=sf ) /* width */
        !          9400:   {
        !          9401:   int aaval=0;				/* weighted rpvals */
        !          9402:   int nrp=0, mrp=0;			/* #rp bits set, #within matrix */
        !          9403:   for ( jwt=0; jwt<sf; jwt++ )
        !          9404:    for ( iwt=0; iwt<sf; iwt++ )
        !          9405:     {
        !          9406:     int i=irp+iwt, j=jrp+jwt;		/* rp->pixmap point */
        !          9407:     int rpval = 0;			/* rp->pixmap value at i,j */
        !          9408:     if ( i>=0 && i<rpwidth		/* i within actual pixmap */
        !          9409:     &&   j>=0 && j<rpheight )		/* ditto j */
        !          9410:       {	mrp++;				/* count another bit within matrix */
        !          9411: 	rpval = (int)(getpixel(rp,j,i)); } /* get actual pixel value */
        !          9412:     if ( rpval != 0 )
        !          9413:       {	nrp++;				/* count another bit set */
        !          9414: 	aaval += (int)(getpixel(aawts,jwt,iwt)); } /* sum weighted vals */
        !          9415:     } /* --- end-of-for(iwt,jwt) --- */
        !          9416:   if ( aaval > 0 )			/*normalize and rescale non-zero val*/
        !          9417:     { int aafrac = (100*aaval)/sumwts;	/* weighted percent of black points */
        !          9418:       /*if((100*nrp)/mrp >=blackfrac)*/	/* many black interior pts */
        !          9419:       if( aafrac >= maxwtfrac )		/* high weight of sampledblack pts */
        !          9420: 	aaval = grayscale-1;		/* so set supersampled pt black */
        !          9421:       else if( aafrac <= minwtfrac )	/* low weight of sampledblack pts */
        !          9422: 	aaval = 0;			/* so set supersampled pt white */
        !          9423:       else				/* rescale calculated weight */
        !          9424: 	aaval = ((sumwts/2 - 1) + (grayscale-1)*aaval)/sumwts; }
        !          9425:   maxaaval = max2(maxaaval,aaval);	/* largest aaval so far */
        !          9426:   if ( msgfp!=NULL && msglevel>=999 )
        !          9427:     fprintf(msgfp,"aasupsamp> jrp,irp=%d,%d jaa,iaa=%d,%d"
        !          9428:     " mrp,nrp=%d,%d aaval=%d\n",
        !          9429:     jrp,irp, jaa,iaa, mrp,nrp, aaval);
        !          9430:   if ( jaa<aaheight && iaa<aawidth )	/* bounds check */
        !          9431:     setpixel(aap,jaa,iaa,aaval);	/*weighted val in supersamp raster*/
        !          9432:   else if( msgfp!=NULL && msglevel>=9 )	/* emit error if out-of-bounds */
        !          9433:     fprintf(msgfp,"aasupsamp> Error: aaheight,aawidth=%d,%d jaa,iaa=%d,%d\n",
        !          9434:     aaheight,aawidth, jaa,iaa);
        !          9435:   iaa++;				/* bump aa col index */
        !          9436:   } /* --- end-of-for(irp) --- */
        !          9437:  jaa++;					/* bump aa row index */
        !          9438:  } /* --- end-of-for(jrp) --- */
        !          9439: /* --- rescale supersampled image so darkest points become black --- */
        !          9440: if ( isrescalemax )			/* flag set to rescale maxaaval */
        !          9441:   {
        !          9442:   double scalef = ((double)(grayscale-1))/((double)maxaaval);
        !          9443:   for ( jaa=0; jaa<aaheight; jaa++ )	/* height */
        !          9444:    for ( iaa=0; iaa<aawidth; iaa++ )	/* width */
        !          9445:     { int aafrac, aaval = getpixel(aap,jaa,iaa); /* un-rescaled value */
        !          9446:       aaval = (int)(0.5+((double)aaval)*scalef); /*multiply by scale factor*/
        !          9447:       aafrac = (100*aaval)/(grayscale-1); /* fraction of blackness */
        !          9448:       if( aafrac >= blackfrac )		/* high weight of sampledblack pts */
        !          9449: 	aaval = grayscale-1;		/* so set supersampled pt black */
        !          9450:       else if( 0&&aafrac <= minwtfrac )	/* low weight of sampledblack pts */
        !          9451: 	aaval = 0;			/* so set supersampled pt white */
        !          9452:       setpixel(aap,jaa,iaa,aaval); }	/* replace rescaled val in raster */
        !          9453:   } /* --- end-of-if(isrescalemax) --- */
        !          9454: *aa = aap;				/* return supersampled image*/
        !          9455: status = 1;				/* set successful status */
        !          9456: if ( msgfp!=NULL && msglevel>=999 )
        !          9457:   { fprintf(msgfp,"aasupsamp> anti-aliased image...\n");
        !          9458:     type_bytemap((intbyte *)aap->pixmap,grayscale,
        !          9459:     aap->width,aap->height,msgfp);  fflush(msgfp); }
        !          9460: /* -------------------------------------------------------------------------
        !          9461: Back to caller with gray-scale anti-aliased bytemap
        !          9462: -------------------------------------------------------------------------- */
        !          9463: end_of_job:
        !          9464:   return ( status );
        !          9465: } /* --- end-of-function aasupsamp() --- */
        !          9466: 
        !          9467: 
        !          9468: /* ==========================================================================
        !          9469:  * Function:	aacolormap ( bytemap, nbytes, colors, colormap )
        !          9470:  * Purpose:	searches bytemap, returning a list of its discrete values
        !          9471:  *		in ascending order in colors[], and returning an "image"
        !          9472:  *		of bytemap (where vales are replaced by colors[]
        !          9473:  *		indexes) in colormap[].
        !          9474:  * --------------------------------------------------------------------------
        !          9475:  * Arguments:	bytemap (I)	intbyte *  to bytemap containing
        !          9476:  *				grayscale values (usually 0=white
        !          9477:  *				through 255=black) for which colors[]
        !          9478:  *				and colormap[] will be constructed.
        !          9479:  *		nbytes (I)	int containing #bytes in bytemap
        !          9480:  *				(usually just #rows * #cols)
        !          9481:  *		colors (O)	intbyte *  (to be interpreted as ints)
        !          9482:  *				returning a list of the discrete/different
        !          9483:  *				values in bytemap, in ascending value order
        !          9484:  *		colormap (O)	intbyte *  returning a bytemap "image",
        !          9485:  *				i.e., in one-to-one pixel correspondence
        !          9486:  *				with bytemap, but where the values have been
        !          9487:  *				replaced with corresponding colors[] indexes.
        !          9488:  * --------------------------------------------------------------------------
        !          9489:  * Returns:	( int )		#colors in colors[], or 0 for any error
        !          9490:  * --------------------------------------------------------------------------
        !          9491:  * Notes:     o
        !          9492:  * ======================================================================= */
        !          9493: /* --- entry point --- */
        !          9494: int	aacolormap ( intbyte *bytemap, int nbytes,
        !          9495: 			intbyte *colors, intbyte *colormap )
        !          9496: {
        !          9497: /* -------------------------------------------------------------------------
        !          9498: Allocations and Declarations
        !          9499: -------------------------------------------------------------------------- */
        !          9500: int	ncolors = 0,			/* #different values in bytemap */
        !          9501: 	igray, grayscale = 256;		/* bytemap contains intbyte's */
        !          9502: intbyte	*bytevalues = NULL;		/* 1's where bytemap contains value*/
        !          9503: int	ibyte;				/* bytemap/colormap index */
        !          9504: int	isscale = 0;			/* true to scale largest val to 255*/
        !          9505: int	maxcolors = 0;			/* maximum ncolors */
        !          9506: /* -------------------------------------------------------------------------
        !          9507: Accumulate colors[] from values occurring in bytemap
        !          9508: -------------------------------------------------------------------------- */
        !          9509: /* --- initialization --- */
        !          9510: if ( (bytevalues = (intbyte *)malloc(grayscale)) /*alloc bytevalues*/
        !          9511: ==   NULL ) goto end_of_job;		/* signal error if malloc() failed */
        !          9512: memset(bytevalues,0,grayscale);		/* zero out bytevalues */
        !          9513: /* --- now set 1's at offsets corresponding to values found in bytemap --- */
        !          9514: for ( ibyte=0; ibyte<nbytes; ibyte++ )	/* for each byte in bytemap */
        !          9515:   bytevalues[(int)bytemap[ibyte]] = 1;	/*use its value to index bytevalues*/
        !          9516: /* --- collect the 1's indexes in colors[] --- */
        !          9517: for ( igray=0; igray<grayscale; igray++ ) /* check all possible values */
        !          9518:   if ( (int)bytevalues[igray] )		/*bytemap contains igray somewheres*/
        !          9519:     { colors[ncolors] = (intbyte)igray;	/* so store igray in colors */
        !          9520:       bytevalues[igray] = (intbyte)ncolors; /* save colors[] index */
        !          9521:       if ( maxcolors>0 && ncolors>=maxcolors ) /* too many color indexes */
        !          9522:         bytevalues[igray] = (intbyte)(maxcolors-1); /*so scale back to max*/
        !          9523:       ncolors++; }			/* and bump #colors */
        !          9524: /* --- rescale colors so largest, colors[ncolors-1], is black --- */
        !          9525: if ( isscale )				/* only rescale if requested */
        !          9526:  if ( ncolors > 1 )			/* and if not a "blank" raster */
        !          9527:   if ( colors[ncolors-1] > 0 )		/*and at least one pixel non-white*/
        !          9528:    {
        !          9529:    /* --- multiply each colors[] by factor that scales largest to 255 --- */
        !          9530:    double scalefactor = ((double)(grayscale-1))/((double)colors[ncolors-1]);
        !          9531:    for ( igray=1; igray<ncolors; igray++ ) /* re-scale each colors[] */
        !          9532:     { colors[igray] = min2(grayscale-1,(int)(scalefactor*colors[igray]+0.5));
        !          9533:       if (igray>5) colors[igray] = min2(grayscale-1,colors[igray]+2*igray); }
        !          9534:    } /* --- end-of-if(isscale) --- */
        !          9535: /* -------------------------------------------------------------------------
        !          9536: Construct colormap
        !          9537: -------------------------------------------------------------------------- */
        !          9538: for ( ibyte=0; ibyte<nbytes; ibyte++ )	/* for each byte in bytemap */
        !          9539:   colormap[ibyte] = bytevalues[(int)bytemap[ibyte]]; /*index for this value*/
        !          9540: /* -------------------------------------------------------------------------
        !          9541: back to caller with #colors, or 0 for any error
        !          9542: -------------------------------------------------------------------------- */
        !          9543: end_of_job:
        !          9544:   if ( bytevalues != NULL ) free(bytevalues); /* free working memory */
        !          9545:   if ( maxcolors>0 && ncolors>maxcolors ) /* too many color indexes */
        !          9546:     ncolors = maxcolors;		/* return maximum to caller */
        !          9547:   return ( ncolors );			/* back with #colors, or 0=error */
        !          9548: } /* --- end-of-function aacolormap() --- */
        !          9549: 
        !          9550: 
        !          9551: /* ==========================================================================
        !          9552:  * Function:	aaweights ( width, height )
        !          9553:  *		Builds "canonical" weight matrix, width x height, in a raster
        !          9554:  *		(see Notes below for discussion).
        !          9555:  * --------------------------------------------------------------------------
        !          9556:  * Arguments:	width (I)	int containing width (#cols) of returned
        !          9557:  *				raster/matrix of weights
        !          9558:  *		height (I)	int containing height (#rows) of returned
        !          9559:  *				raster/matrix of weights
        !          9560:  * --------------------------------------------------------------------------
        !          9561:  * Returns:	( raster * )	ptr to raster containing width x height
        !          9562:  *				weight matrix, or NULL for any error
        !          9563:  * --------------------------------------------------------------------------
        !          9564:  * Notes:     o For example, given width=7, height=5, builds the matrix
        !          9565:  *			1 2 3  4 3 2 1
        !          9566:  *			2 4 6  8 6 4 2
        !          9567:  *			3 6 9 12 9 6 3
        !          9568:  *			2 4 6  8 6 4 2
        !          9569:  *			1 2 3  4 3 2 1
        !          9570:  *		If an even dimension given, the two center numbers stay
        !          9571:  *		the same, e.g., 123321 for the top row if width=6.
        !          9572:  *	      o	For an odd square n x n matrix, the sum of all n^2
        !          9573:  *		weights will be ((n+1)/2)^4.
        !          9574:  *	      o	The largest weight (in the allocated pixsz=8 raster) is 255,
        !          9575:  *		so the largest square matrix is 31 x 31.  Any weight that
        !          9576:  *		tries to grow beyond 255 is held constant at 255.
        !          9577:  *	      o	A new_raster(), pixsz=8, is allocated for the caller.
        !          9578:  *		To avoid memory leaks, be sure to delete_raster() when done.
        !          9579:  * ======================================================================= */
        !          9580: /* --- entry point --- */
        !          9581: raster	*aaweights ( int width, int height )
        !          9582: {
        !          9583: /* -------------------------------------------------------------------------
        !          9584: Allocations and Declarations
        !          9585: -------------------------------------------------------------------------- */
        !          9586: raster	*new_raster(), *weights=NULL;	/* raster of weights returned */
        !          9587: int	irow=0, icol=0,			/* height, width indexes */
        !          9588: 	weight = 0;			/*running weight, as per Notes above*/
        !          9589: /* -------------------------------------------------------------------------
        !          9590: Initialization
        !          9591: -------------------------------------------------------------------------- */
        !          9592: /* --- allocate raster for weights --- */
        !          9593: if ( (weights = new_raster(width,height,8)) /* allocate 8-bit byte raster */
        !          9594: ==  NULL ) goto end_of_job;		/* return NULL error if failed */
        !          9595: /* -------------------------------------------------------------------------
        !          9596: Fill weight matrix, as per Notes above
        !          9597: -------------------------------------------------------------------------- */
        !          9598: for ( irow=0; irow<height; irow++ )	/* outer loop over rows */
        !          9599:   for ( icol=0; icol<width; icol++ )	/* inner loop over cols */
        !          9600:     {
        !          9601:     int	jrow = height-irow-1,		/* backwards irow, height-1,...,0 */
        !          9602: 	jcol =  width-icol-1;		/* backwards icol,  width-1,...,0 */
        !          9603:     weight = min2(irow+1,jrow+1) * min2(icol+1,jcol+1); /* weight */
        !          9604:     if ( aaalgorithm == 1 ) weight=1;	/* force equal weights */
        !          9605:     setpixel(weights,irow,icol,min2(255,weight)); /*store weight in matrix*/
        !          9606:     } /* --- end-of-for(irow,icol) --- */
        !          9607: end_of_job:
        !          9608:   return ( weights );			/* back with weights or NULL=error */
        !          9609: } /* --- end-of-function aaweights() --- */
        !          9610: 
        !          9611: 
        !          9612: /* ==========================================================================
        !          9613:  * Function:	aawtpixel ( image, ipixel, weights, rotate )
        !          9614:  * Purpose:	Applies matrix of weights to the pixels
        !          9615:  *		surrounding ipixel in image, rotated clockwise
        !          9616:  *		by rotate degrees (typically 0 or 30).
        !          9617:  * --------------------------------------------------------------------------
        !          9618:  * Arguments:	image (I)	raster * to bitmap (though it can be bytemap)
        !          9619:  *				containing image with pixels to be averaged.
        !          9620:  *		ipixel (I)	int containing index (irow*width+icol) of
        !          9621:  *				center pixel of image for weighted average.
        !          9622:  *		weights (I)	raster * to bytemap of relative weights
        !          9623:  *				(0-255), whose dimensions (usually odd width
        !          9624:  *				and odd height) determine the "subgrid" of
        !          9625:  *				image surrounding ipixel to be averaged.
        !          9626:  *		rotate (I)	int containing degrees clockwise rotation
        !          9627:  *				(typically 0 or 30), i.e., imagine weights
        !          9628:  *				rotated clockwise and then averaging the
        !          9629:  *				image pixels "underneath" it now.
        !          9630:  * --------------------------------------------------------------------------
        !          9631:  * Returns:	( int )		0-255 weighted average, or -1 for any error
        !          9632:  * --------------------------------------------------------------------------
        !          9633:  * Notes:     o	The rotation matrix used (when requested) is
        !          9634:  *		    / x' \     / cos(theta)  sin(theta)/a \  / x \
        !          9635:  *		    |    |  =  |                          |  |   |
        !          9636:  *                  \ y' /     \ -a*sin(theta) cos(theta) /  \ y /
        !          9637:  *		where a=1 (current default) is the pixel (not screen)
        !          9638:  *		aspect ratio width:height, and theta is rotate (converted
        !          9639:  *		from degrees to radians).  Then x=col,y=row are the integer
        !          9640:  *		pixel coords relative to the input center ipixel, and
        !          9641:  *		x',y' are rotated coords which aren't necessarily integer.
        !          9642:  *		The actual pixel used is the one nearest x',y'.
        !          9643:  * ======================================================================= */
        !          9644: /* --- entry point --- */
        !          9645: int	aawtpixel ( raster *image, int ipixel, raster *weights, int rotate )
        !          9646: {
        !          9647: /* -------------------------------------------------------------------------
        !          9648: Allocations and Declarations
        !          9649: -------------------------------------------------------------------------- */
        !          9650: int	aaimgval = 0,			/* weighted avg returned to caller */
        !          9651: 	totwts=0, sumwts=0;		/* total of all wts, sum wts used */
        !          9652: int	pixsz = image->pixsz,		/* #bits per image pixel */
        !          9653: 	black1=1, black8=255,		/* black for 1-bit, 8-bit pixels */
        !          9654: 	black = (pixsz==1? black1:black8), /* black value for our image */
        !          9655: 	scalefactor = (black1+black8-black), /* only scale 1-bit images */
        !          9656: 	iscenter = 0;			/* set true if center pixel black */
        !          9657: /* --- grid dimensions and indexes --- */
        !          9658: int	wtheight  = weights->height,	/* #rows in weight matrix */
        !          9659: 	wtwidth   = weights->width,	/* #cols in weight matrix */
        !          9660: 	imgheight =   image->height,	/* #rows in image */
        !          9661: 	imgwidth  =   image->width;	/* #cols in image */
        !          9662: int	wtrow,  wtrow0 = wtheight/2,	/* center row index for weights */
        !          9663: 	wtcol,  wtcol0 = wtwidth/2,	/* center col index for weights */
        !          9664: 	imgrow, imgrow0= ipixel/imgwidth, /* center row index for ipixel */
        !          9665: 	imgcol, imgcol0= ipixel-(imgrow0*imgwidth); /*center col for ipixel*/
        !          9666: /* --- rotated grid variables --- */
        !          9667: static	int prevrotate = 0;		/* rotate from previous call */
        !          9668: static	double costheta = 1.0,		/* cosine for previous rotate */
        !          9669: 	sintheta = 0.0;			/* and sine for previous rotate */
        !          9670: double	a = 1.0;			/* default aspect ratio */
        !          9671: /* -------------------------------------------------------------------------
        !          9672: Initialization
        !          9673: -------------------------------------------------------------------------- */
        !          9674: /* --- refresh trig functions for rotate when it changes --- */
        !          9675: if ( rotate != prevrotate )		/* need new sine/cosine */
        !          9676:   { costheta = cos(((double)rotate)/57.29578);	/*cos of rotate in radians*/
        !          9677:     sintheta = sin(((double)rotate)/57.29578);	/*sin of rotate in radians*/
        !          9678:     prevrotate = rotate; }		/* save current rotate as prev */
        !          9679: /* -------------------------------------------------------------------------
        !          9680: Calculate aapixel as weighted average over image points around ipixel
        !          9681: -------------------------------------------------------------------------- */
        !          9682: for ( wtrow=0; wtrow<wtheight; wtrow++ )
        !          9683:  for ( wtcol=0; wtcol<wtwidth; wtcol++ )
        !          9684:   {
        !          9685:   /* --- allocations and declarations --- */
        !          9686:   int	wt = (int)getpixel(weights,wtrow,wtcol); /* weight for irow,icol */
        !          9687:   int	drow = wtrow - wtrow0,		/* delta row offset from center */
        !          9688: 	dcol = wtcol - wtcol0;		/* delta col offset from center */
        !          9689:   int	iscenter = 0;			/* set true if center point black */
        !          9690:   /* --- initialization --- */
        !          9691:   totwts += wt;				/* sum all weights */
        !          9692:   /* --- rotate (if requested) --- */
        !          9693:   if ( rotate != 0 )			/* non-zero rotation */
        !          9694:     {
        !          9695:     /* --- apply rotation matrix to (x=dcol,y=drow) --- */
        !          9696:     double dx=(double)dcol, dy=(double)drow, dtemp; /* need floats */
        !          9697:     dtemp = dx*costheta + dy*sintheta/a; /* save new dx' */
        !          9698:     dy = -a*dx*sintheta + dy*costheta;	/* dy becomes new dy' */
        !          9699:     dx = dtemp;				/* just for notational convenience */
        !          9700:     /* --- replace original (drow,dcol) with nearest rotated point --- */
        !          9701:     drow = (int)(dy+0.5);		/* round dy for nearest row */
        !          9702:     dcol = (int)(dx+0.5);		/* round dx for nearest col */
        !          9703:     } /* --- end-of-if(rotate!=0) --- */
        !          9704:   /* --- select image pixel to be weighted --- */
        !          9705:   imgrow = imgrow0 + drow;		/*apply displacement to center row*/
        !          9706:   imgcol = imgcol0 + dcol;		/*apply displacement to center col*/
        !          9707:   /* --- if pixel in bounds, accumulate weighted average --- */
        !          9708:   if ( imgrow>=0 && imgrow<imgheight )	/* row is in bounds */
        !          9709:    if ( imgcol>=0 && imgcol<imgwidth )	/* and col is in bounds */
        !          9710:     {
        !          9711:     /* --- accumulate weighted average --- */
        !          9712:     int imgval = (int)getpixel(image,imgrow,imgcol); /* image value */
        !          9713:     aaimgval += wt*imgval;		/* weighted sum of image values */
        !          9714:     sumwts += wt;			/* and also sum weights used */
        !          9715:     /* --- check if center image pixel black --- */
        !          9716:     if ( drow==0 && dcol==0 )		/* this is center ipixel */
        !          9717:       if ( imgval==black )		/* and it's black */
        !          9718: 	iscenter = 1;			/* so set black center flag true */
        !          9719:     } /* --- end-of-if(bounds checks ok) --- */
        !          9720:   } /* --- end-of-for(irow,icol) --- */
        !          9721: if ( 0 && iscenter )			/* center point is black */
        !          9722:   aaimgval = black8;			/* so force average black */
        !          9723: else					/* center point not black */
        !          9724:   aaimgval =				/* 0=white ... black */
        !          9725:       ((totwts/2 - 1) + scalefactor*aaimgval)/totwts; /* not /sumwts; */
        !          9726: /*end_of_job:*/
        !          9727:   return ( aaimgval );
        !          9728: } /* --- end-of-function aawtpixel() --- */
        !          9729: #endif /* PART3 */
        !          9730: 
        !          9731: /* ---
        !          9732:  * PART1
        !          9733:  * ------ */
        !          9734: #if !defined(PARTS) || defined(PART1)
        !          9735: #ifdef DRIVER
        !          9736: /* ==========================================================================
        !          9737:  * Function:	main() driver for mimetex.c
        !          9738:  * Purpose:	emits a mime xbitmap or gif image of a LaTeX math expression
        !          9739:  *		entered either as
        !          9740:  *		    (1)	html query string from a browser (most typical), or
        !          9741:  *		    (2)	a query string from an html <form method="get">
        !          9742:  *			whose <input name="formdata"> (mostly for demo), or
        !          9743:  *		    (3)	command-line arguments (mostly to test).
        !          9744:  *		If no input supplied, expression defaults to "f(x)=x^2",
        !          9745:  *		treated as test (input method 3).
        !          9746:  *		   If args entered on command-line (or if no input supplied),
        !          9747:  *		output is (usually) human-viewable ascii raster images on
        !          9748:  *		stdout rather than the usual mime xbitmaps or gif images.
        !          9749:  * --------------------------------------------------------------------------
        !          9750:  * Command-Line Arguments:
        !          9751:  *		When running mimeTeX from the command-line, rather than
        !          9752:  *		from a browser, syntax is
        !          9753:  *		     ./mimetex	[-d ]		dump gif to stdout
        !          9754:  *				[expression	expression, e.g., x^2+y^2,
        !          9755:  *				|-f input_file]	or read expression from file
        !          9756:  *				[-m msglevel]	verbosity of debugging output
        !          9757:  *				[-s fontsize]	default fontsize, 0-5
        !          9758:  *		-d   Rather than ascii debugging output, mimeTeX dumps the
        !          9759:  *		     actual gif (or xbitmap) to stdout, e.g.,
        !          9760:  *			./mimetex  -d  x^2+y^2  > expression.gif
        !          9761:  *		     creates a gif file containing an image of x^2+y^2
        !          9762:  *		-f   Reads expression from input_file, and automatically
        !          9763:  *		     assumes -d switch.  The input_file may contain the
        !          9764:  *		     expression on one line or spread out over many lines.
        !          9765:  *		     MimeTeX will concatanate all lines from input_file
        !          9766:  *		     to construct one long expression.  Blanks, tabs, and
        !          9767:  *		     newlines will just be ignored.
        !          9768:  *		-m   0-99, controls verbosity level for debugging output
        !          9769:  *		     (usually used only while testing code).
        !          9770:  *		-s   Font size, 0-5.  As usual, the font size can
        !          9771:  *		     also be specified in the expression by a leading
        !          9772:  *		     preamble terminated by $, e.g., 3$f(x)=x^2 displays
        !          9773:  *		     f(x)=x^2 at font size 3.  Default font size is 2.
        !          9774:  * --------------------------------------------------------------------------
        !          9775:  * Exits:	0=success, 1=some error
        !          9776:  * --------------------------------------------------------------------------
        !          9777:  * Notes:     o For an executable that emits mime xbitmaps, compile as
        !          9778:  *		     cc -DXBITMAP mimetex.c -lm -o mimetex.cgi
        !          9779:  *		or, alternatively, for an executable that emits gif images
        !          9780:  *		     cc -DGIF mimetex.c gifsave.c -lm -o mimetex.cgi
        !          9781:  *		or for gif images with anti-aliasing
        !          9782:  *		     cc -DGIF -DAA mimetex.c gifsave.c -lm -o mimetex.cgi
        !          9783:  *		See Notes at top of file for other compile-line -D options.
        !          9784:  *	      o	Move executable to your cgi-bin directory and either
        !          9785:  *		point your browser to it directly in the form
        !          9786:  *		     http://www.yourdomain.com/cgi-bin/mimetex.cgi?3$f(x)=x^2
        !          9787:  *		or put a tag in your html document of the form
        !          9788:  *		     <img src="../cgi-bin/mimetex.cgi?3$f(x)=x^2"
        !          9789:  *		       border=0 align=absmiddle>
        !          9790:  *		where f(x)=x^2 (or any other expression) will be displayed
        !          9791:  *		either as a mime xbitmap or gif image (as per -D flag).
        !          9792:  * ======================================================================= */
        !          9793: 
        !          9794: /* -------------------------------------------------------------------------
        !          9795: header files and other data
        !          9796: -------------------------------------------------------------------------- */
        !          9797: /* --- (additional) standard headers --- */
        !          9798: /* --- other data --- */
        !          9799: #ifdef DUMPENVIRON
        !          9800:  extern	char **environ;			/* environment information */
        !          9801: #endif
        !          9802: 
        !          9803: /* -------------------------------------------------------------------------
        !          9804: globals for gif and png callback functions
        !          9805: -------------------------------------------------------------------------- */
        !          9806: GLOBAL(raster,*bitmap_raster,NULL);	/* use 0/1 bitmap image or */
        !          9807: GLOBAL(intbyte,*colormap_raster,NULL);	/* anti-aliased color indexes */
        !          9808: /* --- anti-aliasing flags (needed by GetPixel() as well as main()) --- */
        !          9809: #ifdef AA				/* if anti-aliasing requested */
        !          9810:   #define ISAAVALUE 1			/* turn flag on */
        !          9811: #else
        !          9812:   #define ISAAVALUE 0			/* else turn flag off */
        !          9813: #endif
        !          9814: GLOBAL(int,isaa,ISAAVALUE);		/* set anti-aliasing flag */
        !          9815: 
        !          9816: /* -------------------------------------------------------------------------
        !          9817: logging data structure, and default data to be logged
        !          9818: -------------------------------------------------------------------------- */
        !          9819: /* --- logging data structure --- */
        !          9820: #define	logdata	struct logdata_struct	/* "typedef" for logdata_struct*/
        !          9821: logdata
        !          9822:   {
        !          9823:   /* -----------------------------------------------------------------------
        !          9824:   environment variable name, max #chars to display, min msglevel to display
        !          9825:   ------------------------------------------------------------------------ */
        !          9826:   char	*name;				/* environment variable name */
        !          9827:   int	maxlen;				/* max #chars to display */
        !          9828:   int	msglevel;			/* min msglevel to display data */
        !          9829:   } ; /* --- end-of-logdata_struct --- */
        !          9830: /* --- data logged by mimeTeX --- */
        !          9831: STATIC logdata mimelog[]
        !          9832: #ifdef INITVALS
        !          9833:   =
        !          9834:   {
        !          9835:   /* ------ variable ------ maxlen msglevel ----- */
        !          9836:     { "QUERY_STRING",         999,    4 },
        !          9837:     { "REMOTE_ADDR",          999,    3 },
        !          9838:     { "HTTP_REFERER",         999,    3 },
        !          9839:     { "REQUEST_URI",          999,    5 },
        !          9840:     { "HTTP_USER_AGENT",      999,    3 },
        !          9841:     { "HTTP_X_FORWARDED_FOR", 999,    3 },
        !          9842:     { NULL, -1, -1 }			/* trailer record */
        !          9843:   } /* --- end-of-mimelog[] --- */
        !          9844: #endif
        !          9845:   ;
        !          9846: 
        !          9847: /* -------------------------------------------------------------------------
        !          9848: messages
        !          9849: -------------------------------------------------------------------------- */
        !          9850: static	char *copyright =		/* copyright, gnu/gpl notice */
        !          9851:  "+-----------------------------------------------------------------------+\n"
        !          9852:  "|mimeTeX vers 1.61, Copyright(c) 2002-2005, John Forkosh Associates, Inc|\n"
        !          9853:  "+-----------------------------------------------------------------------+\n"
        !          9854:  "| mimeTeX is free software, licensed to you under terms of the GNU/GPL, |\n"
        !          9855:  "|           and comes with absolutely no warranty whatsoever.           |\n"
        !          9856:  "+-----------------------------------------------------------------------+";
        !          9857: static	int maxmsgnum = 2;		/* maximum msgtable[] index */
        !          9858: static	char *msgtable[] = {		/* messages referenced by [index] */
        !          9859:  "\\red\\small\\rm\\fbox{\\array{"	/* [0] is invalid_referer_msg */
        !          9860:    "Please~read~www.forkosh.com/mimetex.html\\\\and~install~mimetex.cgi~"
        !          9861:    "on~your~own~server.\\\\Thank~you,~John~Forkosh}}",
        !          9862:  "\\red\\small\\rm\\fbox{\\array{"	/* [1] */
        !          9863:    "Please~provide~your~{\\tiny~HTTP-REFERER}~to~access~the~public\\\\"
        !          9864:    "mimetex~server.~~Or~please~read~~www.forkosh.com/mimetex.html\\\\"
        !          9865:    "and~install~mimetex.cgi~on~your~own~server.~~Thank~you,~John~Forkosh}}",
        !          9866:  "\\red\\small\\rm\\fbox{\\array{"	/* [2] */
        !          9867:    "The~public~mimetex~server~is~for~testing.~~For~production,\\\\"
        !          9868:    "please~read~~www.forkosh.com/mimetex.html~~and~install\\\\"
        !          9869:    "mimetex.cgi~on~your~own~server.~~Thank~you,~John~Forkosh}}",
        !          9870:  NULL } ;				/* trailer */
        !          9871: 
        !          9872: 
        !          9873: /* --- entry point --- */
        !          9874: int	main ( int argc, char *argv[]
        !          9875: 	  #ifdef DUMPENVP
        !          9876: 	    , char *envp[]
        !          9877: 	  #endif
        !          9878: 	)
        !          9879: {
        !          9880: /* -------------------------------------------------------------------------
        !          9881: Allocations and Declarations
        !          9882: -------------------------------------------------------------------------- */
        !          9883: /* --- expression to be emitted --- */
        !          9884: static	char exprbuffer[16385] = "f(x)=x^2"; /* expression to be processed */
        !          9885: char	*expression = exprbuffer;	/* ptr to expression */
        !          9886: int	size = NORMALSIZE;		/* default font size */
        !          9887: char	*query = getenv("QUERY_STRING"); /* getenv("QUERY_STRING") result */
        !          9888: char	*mimeprep();			/* preprocess expression */
        !          9889: int	unescape_url();			/* convert %xx's to ascii chars */
        !          9890: int	emitcache();			/* emit cached image if it exists */
        !          9891: int	isquery = 0,			/* true if input from QUERY_STRING */
        !          9892: 	isqempty = 0,			/* true if query string empty */
        !          9893: 	isqlogging = 0,			/* true if logging in query mode */
        !          9894: 	isformdata = 0,			/* true if input from html form */
        !          9895: 	isdumpimage = 0;		/* true to dump image on stdout */
        !          9896: /* --- rasterization --- */
        !          9897: subraster *rasterize(), *sp;		/* rasterize expression */
        !          9898: raster	*border_raster(), *bp;		/* put a border around raster */
        !          9899: int	type_raster(), type_bytemap(),	/* screen dump function prototypes */
        !          9900: 	xbitmap_raster();		/* mime xbitmap output function */
        !          9901: /* --- http_referer --- */
        !          9902: char	*referer = REFERER;		/* http_referer must contain this */
        !          9903: struct	{ char *referer; int msgnum; }	/* http_referer can't contain this */
        !          9904: 	denyreferer[] = {		/* referer table to deny access to */
        !          9905: 	#ifdef DENYREFERER
        !          9906: 	  #include DENYREFERER		/* e.g.,  {"",1},  for no referer */
        !          9907: 	#endif
        !          9908: 	{ NULL, -999 } };		/* trailer */
        !          9909: char	*http_referer = getenv("HTTP_REFERER"); /* referer using mimeTeX */
        !          9910: int	ishttpreferer = (http_referer==NULL?0:(*http_referer=='\000'?0:1));
        !          9911: int	isstrstr();			/* search http_referer for referer */
        !          9912: int	isinvalidreferer = 0;		/* true for inavlid referer */
        !          9913: int	norefmaxlen = NOREFMAXLEN;	/*max query_string len if no referer*/
        !          9914: /* --- gif --- */
        !          9915: #if defined(GIF)
        !          9916:   int	GetPixel();			/* feed pixels to gifsave library */
        !          9917:   int	GIF_Create(),GIF_CompressImage(),GIF_Close(); /* prototypes for... */
        !          9918:   void	GIF_SetColor(),GIF_SetTransparent(); /* ...gifsave enntry points */
        !          9919: #endif
        !          9920: char	*gif_outfile = (char *)NULL,	/* gif output defaults to stdout */
        !          9921: 	cachefile[256] = "\000",	/* full path and name to cache file*/
        !          9922: 	*md5str();			/* md5 has of expression */
        !          9923: int	maxage = 7200;			/* max-age is two hours */
        !          9924: /* --- anti-aliasing --- */
        !          9925: intbyte	*bytemap_raster = NULL,		/* anti-aliased bitmap */
        !          9926: 	colors[256];			/* grayscale vals in bytemap */
        !          9927: int	aalowpass(), aapnm(),		/*lowpass filters for anti-aliasing*/
        !          9928: 	grayscale = 256;		/* 0-255 grayscales in 8-bit bytes */
        !          9929: int	ncolors=2,			/* #colors (2=b&w) */
        !          9930: 	aacolormap();			/* build colormap from bytemap */
        !          9931: /* --- messages --- */
        !          9932: char	logfile[256] = LOGFILE,		/*log queries if msglevel>=LOGLEVEL*/
        !          9933: 	cachelog[256] = CACHELOG;	/* cached image log in cachepath/ */
        !          9934: char	*timestamp();			/* time stamp for logged messages */
        !          9935: int	logger();			/* logs environ variables */
        !          9936: int	ismonth();			/* check argv[0] for current month */
        !          9937: char	*progname = (argc>0?argv[0]:"noname"); /* name program executed as */
        !          9938: char	*dashes =			/* separates logfile entries */
        !          9939:  "--------------------------------------------------------------------------";
        !          9940: char	*invalid_referer_msg = msgtable[0]; /* msg to invalid http_referer */
        !          9941: /* -------------------------------------------------------------------------
        !          9942: initialization
        !          9943: -------------------------------------------------------------------------- */
        !          9944: /* --- run optional system command string --- */
        !          9945: #ifdef SYSTEM
        !          9946:   system(SYSTEM);
        !          9947: #endif
        !          9948: /* --- set global variables --- */
        !          9949: msgfp = stdout;				/* for comamnd-line mode output */
        !          9950: isss = issupersampling;			/* set supersampling flag */
        !          9951: shrinkfactor = shrinkfactors[NORMALSIZE]; /* set shrinkfactor */
        !          9952: /* ---
        !          9953:  * check QUERY_STRING query for expression overriding command-line arg
        !          9954:  * ------------------------------------------------------------------- */
        !          9955: if ( query != NULL )			/* check query string from environ */
        !          9956:   if ( strlen(query) >= 1 )		/* caller gave us a query string */
        !          9957:     { strncpy(expression,query,16384);	/* so use it as expression */
        !          9958:       expression[16384] = '\000';	/* make sure it's null terminated */
        !          9959:       isquery = 1; }			/* and set isquery flag */
        !          9960: if ( !isquery )				/* empty query string */
        !          9961:   { char *host = getenv("HTTP_HOST"),	/* additional getenv("") results */
        !          9962:     *name = getenv("SERVER_NAME"), *addr = getenv("SERVER_ADDR");
        !          9963:     if ( host!=NULL || name!=NULL || addr!=NULL ) /* assume http query */
        !          9964:       {	isquery = 1;			/* set flag to signal query */
        !          9965: 	strcpy(expression,"\\red\\small\\rm~missing~query~string"); }
        !          9966:     isqempty = 1;			/* signal empty query string */
        !          9967:   } /* --- end-of-if(!isquery) --- */
        !          9968: /* ---
        !          9969:  * process command-line input args (if not a query)
        !          9970:  * ------------------------------------------------ */
        !          9971: if ( !isquery				/* don't have an html query string */
        !          9972: ||   ( /*isqempty &&*/ argc>1) )	/* or have command-line args */
        !          9973:  {
        !          9974:  char	*argsignal = ARGSIGNAL,		/* signals start of mimeTeX args */
        !          9975: 	stopsignal[32] = "--";		/* default Unix end-of-args signal */
        !          9976:  int	iarg=0, argnum=0,		/*argv[] index for command-line args*/
        !          9977: 	exprarg = 0,			/* argv[] index for expression */
        !          9978: 	infilearg = 0,			/* argv[] index for infile */
        !          9979: 	nswitches = 0,			/* number of -switches */
        !          9980: 	isstopsignal = 0,		/* true after stopsignal found */
        !          9981: 	isstrict = 1/*iswindows*/,	/* true for strict arg checking */
        !          9982: 					/*nb, windows has apache "x -3" bug*/
        !          9983: 	nargs=0, nbadargs=0,		/* number of arguments, bad ones */
        !          9984: 	maxbadargs = (isstrict?0:1),	/*assume query if too many bad args*/
        !          9985: 	isgoodargs = 0;			/* true to accept command-line args*/
        !          9986:  if ( argsignal != NULL )		/* if compiled with -DARGSIGNAL */
        !          9987:   while ( argc > ++iarg )		/* check each argv[] for argsignal */
        !          9988:     if ( !strcmp(argv[iarg],argsignal) ) /* check for exact match */
        !          9989:      { argnum = iarg;			/* got it, start parsing next arg */
        !          9990:        break; }				/* stop looking for argsignal */
        !          9991:  while ( argc > ++argnum )		/* check for switches and values, */
        !          9992:     {
        !          9993:     nargs++;				/* count another command-line arg */
        !          9994:     if ( strcmp(argv[argnum],stopsignal) == 0 ) /* found stopsignal */
        !          9995:       {	isstopsignal = 1;		/* so set stopsignal flag */
        !          9996: 	continue; }			/* and get expression after it */
        !          9997:     if ( !isstopsignal			/* haven't seen stopsignal switch */
        !          9998:     &&   *argv[argnum] == '-' )		/* and have some '-' switch */
        !          9999:       {
        !          10000:       char flag = tolower(*(argv[argnum]+1)); /* single char following '-' */
        !          10001:       int  arglen = strlen(argv[argnum]) - 1; /* #chars following - */
        !          10002:       argnum++;		/* arg following flag/switch is usually its value */
        !          10003:       nswitches++;			/* another switch on command line */
        !          10004:       if ( isstrict && arglen!=1 )	/* only single-char switch allowed */
        !          10005: 	{ nbadargs++; argnum--; }	/* so ignore longer -xxx switch */
        !          10006:       else				/* process single-char -x switch */
        !          10007:        switch ( flag ) {		/* see what user wants to tell us */
        !          10008: 	/* --- ignore uninterpreted flag --- */
        !          10009: 	default:  nbadargs++;                              argnum--;  break;
        !          10010: 	/* --- adjustable program parameters (not checking input) --- */
        !          10011: 	case 'd': isdumpimage++;                           argnum--;  break;
        !          10012: 	case 'e': isdumpimage++;           gif_outfile=argv[argnum];  break;
        !          10013: 	case 'f': isdumpimage++;                   infilearg=argnum;  break;
        !          10014: 	case 'm': msglevel = atoi(argv[argnum]);                      break;
        !          10015: 	case 'o': istransparent = 0;                       argnum--;  break;
        !          10016: 	case 's': size = atoi(argv[argnum]);                          break;
        !          10017: 	} /* --- end-of-switch(flag) --- */
        !          10018:       } /* --- end-of-if(*argv[argnum]=='-') --- */
        !          10019:     else				/* expression if arg not a -flag */
        !          10020:       if ( infilearg == 0 )		/* no infile arg yet */
        !          10021: 	{ if ( exprarg != 0 ) nbadargs++; /* 2nd expression invalid */
        !          10022: 	  exprarg = argnum;		/* but take last expression */
        !          10023: 	  /*infilearg = (-1);*/ }	/* and set infilearg */
        !          10024:       else nbadargs++;			/* infile and expression invalid */
        !          10025:     } /* --- end-of-while(argc>++argnum) --- */
        !          10026:  if ( msglevel>=999 && msgfp!=NULL )	/* display command-line info */
        !          10027:   fprintf(msgfp,"argc=%d, progname=%s, #args=%d, #badargs=%d\n",
        !          10028:   argc,progname,nargs,nbadargs);
        !          10029:  /* ---
        !          10030:   * decide whether command-line input overrides query_string
        !          10031:   * -------------------------------------------------------- */
        !          10032:  if ( isdumpimage > 2 ) nbadargs++;	/* duplicate/conflicting -switch */
        !          10033:  isgoodargs =  ( !isstrict		/*good if not doing strict checking*/
        !          10034:   || !isquery				/* or if no query, must use args */
        !          10035:   || (nbadargs<nargs && nbadargs<=maxbadargs) ); /* bad args imply query */
        !          10036:  /* ---
        !          10037:   * take expression from command-line args
        !          10038:   * -------------------------------------- */
        !          10039:  if ( isgoodargs && exprarg > 0		/* good expression on command line */
        !          10040:  &&   infilearg <= 0 )			/* and not given in input file */
        !          10041:   if ( !isquery				/* no conflict if no query_string */
        !          10042:   ||   nswitches > 0 )			/* explicit -switch(es) also given */
        !          10043:    { strncpy(expression,argv[exprarg],16384); /*expression from command-line*/
        !          10044:      expression[16384] = '\000';	/* make sure it's null terminated */
        !          10045:      isquery = 0; }			/* and not from a query_string */
        !          10046:  /* ---
        !          10047:   * or read expression from input file
        !          10048:   * ---------------------------------- */
        !          10049:  if ( isgoodargs && infilearg > 0 )	/* have a good -f arg */
        !          10050:   {
        !          10051:   FILE *infile = fopen(argv[infilearg],"r"); /* open input file for read */
        !          10052:   if ( infile != (FILE *)NULL )		/* opened input file successfully */
        !          10053:    { char instring[2049];		/* line from file */
        !          10054:      isquery = 0;			/* file input, not a query_string */
        !          10055:      *expression = '\000';		/* start expresion as empty string */
        !          10056:      while ( fgets(instring,2048,infile) != (char *)NULL ) /* read till eof*/
        !          10057:       strcat(expression,instring);	/* concat line to end of expression*/
        !          10058:      fclose ( infile ); }	/*close input file after reading expression*/
        !          10059:   } /* --- end-of-if(infilearg>0) --- */
        !          10060:  } /* --- end-of-if(!isquery) --- */
        !          10061: /* ---
        !          10062:  * check for <form> input
        !          10063:  * ---------------------- */
        !          10064: if ( isquery )				/* must be <form method="get"> */
        !          10065:  if ( !memcmp(expression,"formdata",8) ) /*must be <input name="formdata"> */
        !          10066:   { char *delim=strchr(expression,'=');	/* find equal following formdata */
        !          10067:     if ( delim != (char *)NULL )	/* found unescaped equal sign */
        !          10068:       strcpy(expression,delim+1);	/* so shift name= out of expression*/
        !          10069:     while ( (delim=strchr(expression,'+')) != NULL ) /*unescaped plus sign*/
        !          10070:       *delim = ' ';			/* is "shorthand" for blank space */
        !          10071:     /*unescape_url(expression,1);*/	/* convert unescaped %xx's to chars */
        !          10072:     unescape_url(expression,0);		/* convert all %xx's to chars */
        !          10073:     unescape_url(expression,0);		/* repeat */
        !          10074:     msglevel = FORMLEVEL;		/* msglevel for forms */
        !          10075:     isformdata = 1; }			/* set flag to signal form data */
        !          10076:  else /* --- query, but not <form> input --- */
        !          10077:     unescape_url(expression,0);		/* convert _all_ %xx's to chars */
        !          10078: /* ---
        !          10079:  * check queries for embedded prefixes signalling special processing
        !          10080:  * ----------------------------------------------------------------- */
        !          10081: if ( isquery )				/* only check queries */
        !          10082:  {
        !          10083:  /* --- check for msglevel=###$ prefix --- */
        !          10084:  if ( !memcmp(expression,"msglevel=",9) ) /* query has msglevel prefix */
        !          10085:    { char *delim=strchr(expression,'$'); /* find $ delim following msglevel*/
        !          10086:      if ( delim != (char *)NULL )	/* check that we found delim */
        !          10087:       {	*delim = '\000';		/* replace delim with null */
        !          10088: 	if ( seclevel <= 9 )		/* permit msglevel specification */
        !          10089: 	  msglevel = atoi(expression+9); /* interpret ### in msglevel###$ */
        !          10090: 	strcpy(expression,delim+1); } }	/* shift out prefix and delim */
        !          10091:  /* --- next check for logfile=xxx$ prefix (must follow msglevel) --- */
        !          10092:  if ( !memcmp(expression,"logfile=",8) ) /* query has logfile= prefix */
        !          10093:    { char *delim=strchr(expression,'$'); /* find $ delim following logfile=*/
        !          10094:      if ( delim != (char *)NULL )	/* check that we found delim */
        !          10095:       {	*delim = '\000';		/* replace delim with null */
        !          10096: 	if ( seclevel <= 3 )		/* permit logfile specification */
        !          10097: 	  strcpy(logfile,expression+8);	/* interpret xxx in logfile=xxx$ */
        !          10098: 	strcpy(expression,delim+1); } }	/* shift out prefix and delim */
        !          10099:  } /* --- end-of-if(isquery) --- */
        !          10100: /* ---
        !          10101:  * log query (e.g., for debugging)
        !          10102:  * ------------------------------- */
        !          10103: if ( isquery )				/* only log query_string's */
        !          10104:  if ( msglevel >= LOGLEVEL		/* check if logging */
        !          10105:  &&   seclevel <= 5 )			/* and if logging permitted */
        !          10106:   if ( logfile != NULL )		/* if a logfile is given */
        !          10107:    if ( *logfile != '\000' )		/*and if it's not an empty string*/
        !          10108:     if ( (msgfp=fopen(logfile,"a"))	/* open logfile for append */
        !          10109:     !=   NULL )				/* ignore logging if can't open */
        !          10110:      {
        !          10111:      /* --- default logging --- */
        !          10112:      logger(msgfp,msglevel,expression,mimelog); /* log query */
        !          10113:      /* --- additional debug logging (argv and environment) --- */
        !          10114:      if ( msglevel >= 9 )		/* log environment */
        !          10115:       { int i;  /*char name[999],*value;*/
        !          10116: 	fprintf(msgfp,"Command-line arguments...\n");
        !          10117: 	if ( argc < 1 )			/* no command-line args */
        !          10118: 	 fprintf(msgfp,"  ...argc=%d, no argv[] variables\n",argc);
        !          10119: 	else
        !          10120: 	 for ( i=0; i<argc; i++ )	/* display all argv[]'s */
        !          10121: 	  fprintf(msgfp,"  argv[%d] = \"%s\"\n",i,argv[i]);
        !          10122: 	#ifdef DUMPENVP			/* char *envp[] available for dump */
        !          10123: 	fprintf(msgfp,"Environment variables (using envp[])...\n");
        !          10124: 	if ( envp == (char **)NULL )	/* envp not provided */
        !          10125: 	 fprintf(msgfp,"  ...envp[] environment variables not available\n");
        !          10126: 	else
        !          10127: 	 for ( i=0; ; i++ )		/* display all envp[]'s */
        !          10128: 	  if ( envp[i] == (char *)NULL ) break;
        !          10129: 	  else fprintf(msgfp,"  envp[%d] = \"%s\"\n",i,envp[i]);
        !          10130: 	#endif /* --- DUMPENVP ---*/
        !          10131: 	#ifdef DUMPENVIRON	/* skip what should be redundant output */
        !          10132: 	fprintf(msgfp,"Environment variables (using environ)...\n");
        !          10133: 	if ( environ == (char **)NULL )	/* environ not provided */
        !          10134: 	 fprintf(msgfp,"  ...extern environ variables not available\n");
        !          10135: 	else
        !          10136: 	 for ( i=0; ; i++ )		/*display environ[] and getenv()'s*/
        !          10137: 	  if ( environ[i] == (char *)NULL ) break;
        !          10138: 	  else {
        !          10139: 	    strcpy(name,environ[i]);	/* set up name for getenv() arg */
        !          10140: 	    if ( (value=strchr(name,'=')) != NULL ) /* = delimits name */
        !          10141: 	      {	*value = '\000';	/* got it, so null-terminate name */
        !          10142: 		value = getenv(name); }	/* and look up name using getenv() */
        !          10143: 	    else strcpy(name,"NULL");	/* missing = delim in environ[i] */
        !          10144: 	    fprintf(msgfp,"environ[%d]: \"%s\"\n\tgetenv(%s) = \"%s\"\n",
        !          10145: 	    i,environ[i],name,(value==NULL?"NULL":value));
        !          10146: 	    } /* --- end-of-if/else --- */
        !          10147: 	#endif /* --- DUMPENVIRON ---*/
        !          10148:       } /* --- end-of-if(msglevel>=9) --- */
        !          10149:      /* --- close log file if no longer needed --- */
        !          10150:      if ( msglevel < DBGLEVEL )		/* logging, but not debugging */
        !          10151:       {	fprintf(msgfp,"%s\n",dashes);	/* so log separator line, */
        !          10152: 	fclose(msgfp);			/* close logfile immediately, */
        !          10153: 	msgfp = NULL; }			/* and reset msgfp pointer */
        !          10154:      else
        !          10155: 	isqlogging = 1;			/* set query logging flag */
        !          10156:      } /* --- end-of-if(msglevel>=LOGLEVEL) --- */
        !          10157:     else				/* couldn't open logfile */
        !          10158:      msglevel = 0;			/* can't emit messages */
        !          10159: /* ---
        !          10160:  * prepend prefix to submitted expression
        !          10161:  * -------------------------------------- */
        !          10162: if ( 1 || isquery )			/* queries or command-line */
        !          10163:  if ( *exprprefix != '\000' )		/* we have a prefix string */
        !          10164:   { int npref = strlen(exprprefix);	/* #chars in prefix */
        !          10165:     memmove(expression+npref+1,expression,strlen(expression)+1); /*make room*/
        !          10166:     memcpy(expression,exprprefix,npref); /* copy prefix into expression */
        !          10167:     expression[npref] = '{';		/* followed by { */
        !          10168:     strcat(expression,"}"); }		/* and terminating } to balance { */
        !          10169: /* ---
        !          10170:  * check if http_referer is allowed to use this image
        !          10171:  * -------------------------------------------------- */
        !          10172: if ( isquery )				/* not relevant if "interactive" */
        !          10173:  if ( referer != NULL )			/* nor if compiled w/o -DREFERER= */
        !          10174:   if ( strcmp(referer,"month") != 0 )	/* nor if it's *only* "month" */
        !          10175:    if ( http_referer != NULL )		/* nor if called "standalone" */
        !          10176:     if ( !isstrstr(http_referer,referer,0) ) /* invalid http_referer */
        !          10177:      { expression = invalid_referer_msg; /* so give user error message */
        !          10178:        isinvalidreferer = 1; }		/* and signal invalid referer */
        !          10179: /* ---
        !          10180:  * check if referer contains "month" signal
        !          10181:  * ---------------------------------------- */
        !          10182: if ( isquery )				/* not relevant if "interactive" */
        !          10183:  if ( referer != NULL )			/* nor if compiled w/o -DREFERER= */
        !          10184:   if ( !isinvalidreferer )		/* nor if already invalid referer */
        !          10185:    if ( strstr(referer,"month") != NULL ) /* month check requested */
        !          10186:     if ( !ismonth(progname) )		/* not executed as mimetexJan-Dec */
        !          10187:      { expression = invalid_referer_msg; /* so give user error message */
        !          10188:        isinvalidreferer = 1; }		/* and signal invalid referer */
        !          10189: /* ---
        !          10190:  * check if http_referer is to be denied access
        !          10191:  * -------------------------------------------- */
        !          10192: if ( isquery )				/* not relevant if "interactive" */
        !          10193:  if ( !isinvalidreferer )		/* nor if already invalid referer */
        !          10194:   { int	iref=0, msgnum=(-999);		/* denyreferer index, message# */
        !          10195:     for ( iref=0; msgnum<0; iref++ ) {	/* run through denyreferer[] table */
        !          10196:       char *deny = denyreferer[iref].referer; /* referer to be denied */
        !          10197:       if ( deny == NULL ) break;	/* null signals end-of-table */
        !          10198:       if ( msglevel>=999 && msgfp!=NULL ) /* debugging */
        !          10199: 	{fprintf(msgfp,"main> invalid iref=%d: deny=%s http_referer=%s\n",
        !          10200: 	 iref,deny,(http_referer==NULL?"null":http_referer)); fflush(msgfp);}
        !          10201:       if ( *deny == '\000' )		/* signal to check for no referer */
        !          10202: 	{ if ( http_referer == NULL )	/* http_referer not supplied */
        !          10203: 	   msgnum = denyreferer[iref].msgnum; } /* so set message# */
        !          10204:       else				/* have referer to check for */
        !          10205:        if ( http_referer != NULL )	/* and have referer to be checked */
        !          10206: 	if ( isstrstr(http_referer,deny,0) ) /* invalid http_referer */
        !          10207: 	 msgnum = denyreferer[iref].msgnum; /* so set message# */
        !          10208:       } /* --- end-of-for(iref) --- */
        !          10209:     if ( msgnum >= 0 )			/* deny access to this referer */
        !          10210:      { if ( msgnum > maxmsgnum ) msgnum = 0; /* keep index within bounds */
        !          10211:        expression = msgtable[msgnum];	/* set user error message */
        !          10212:        isinvalidreferer = 1; }		/* and signal invalid referer */
        !          10213:   } /* --- end-of-if(!isinvalidreferer) --- */
        !          10214: /* --- also check maximum query_string length if no http_referer given --- */
        !          10215: if ( isquery )				/* not relevant if "interactive" */
        !          10216:  if ( !isinvalidreferer )		/* nor if already invalid referer */
        !          10217:   if ( !ishttpreferer )			/* no http_referer supplied */
        !          10218:    if ( strlen(expression) > norefmaxlen ) /* query_string too long */
        !          10219:     { expression = invalid_referer_msg;	/* set invalid http_referer message*/
        !          10220:       isinvalidreferer = 1; }		/* and signal invalid referer */
        !          10221: /* ---
        !          10222:  * check for image caching
        !          10223:  * ----------------------- */
        !          10224: if ( strstr(expression,"\\counter") != NULL /* can't cache \counter{} */
        !          10225: ||   strstr(expression,"\\input")   != NULL /* can't cache \input{} */
        !          10226: ||   strstr(expression,"\\nocach")  != NULL /* no caching requested */
        !          10227:  ) { iscaching = 0;			/* so turn caching off */
        !          10228:      maxage = 2; }			/* and set max-age to two seconds */
        !          10229: if ( isquery )				/* don't cache command-line images */
        !          10230:  if ( iscaching )			/* image caching enabled */
        !          10231:   {
        !          10232:   /* --- set up path to cached image file --- */
        !          10233:   char *md5hash = md5str(expression);	/* md5 hash of expression */
        !          10234:   if ( md5hash == NULL )		/* failed for some reason */
        !          10235:     iscaching = 0;			/* so turn off caching */
        !          10236:   else
        !          10237:    {
        !          10238:    strcpy(cachefile,cachepath);		/* start with (relative) path */
        !          10239:    strcat(cachefile,md5hash);		/* add md5 hash of expression */
        !          10240:    strcat(cachefile,".gif");		/* finish with .gif extension */
        !          10241:    gif_outfile = cachefile;		/* signal GIF_Create() to cache */
        !          10242:    /* --- (always) emit mime content-type line --- */
        !          10243:    fprintf( stdout, "Cache-Control: max-age=%d\n",maxage );
        !          10244:    fprintf( stdout, "Content-type: image/gif\n\n" );
        !          10245:    /* --- emit cached image if it already exists --- */
        !          10246:    if ( emitcache(cachefile) > 0 )	/* cached image emitted */
        !          10247:     goto end_of_job;			/* so nothing else to do */
        !          10248:    /* --- log caching request --- */
        !          10249:    if ( msglevel >= 1			/* check if logging */
        !          10250:    /*&&   seclevel <= 5*/ )		/* and if logging permitted */
        !          10251:     if ( cachelog != NULL )		/* if a logfile is given */
        !          10252:      if ( *cachelog != '\000' )		/*and if it's not an empty string*/
        !          10253:       { char filename[256];		/* construct cachepath/cachelog */
        !          10254:         FILE *filefp=NULL;		/* fopen(filename) */
        !          10255:         strcpy(filename,cachepath);	/* start with (relative) path */
        !          10256:         strcat(filename,cachelog);	/* add cache log filename */
        !          10257:         if ( (filefp=fopen(filename,"a")) /* open cache logfile for append */
        !          10258:         !=   NULL )			/* ignore logging if can't open */
        !          10259: 	 { int isreflogged = 0;		/* set true if http_referer logged */
        !          10260: 	   fprintf(filefp,"%s                 %s\n", /* timestamp, md5 file */
        !          10261: 	    timestamp(),cachefile+strlen(cachepath)); /* (path not shown) */
        !          10262: 	   fprintf(filefp,"%s\n",expression); /* expression in filename */
        !          10263: 	   if ( http_referer != NULL )	/* show referer if we have one */
        !          10264: 	    if ( *http_referer != '\000' )    /* and if not an empty string*/
        !          10265: 	      {	int loglen = strlen(dashes);  /* #chars on line in log file*/
        !          10266: 		char *refp = http_referer;    /* line to be printed */
        !          10267: 		isreflogged = 1;	      /* signal http_referer logged*/
        !          10268: 		while ( 1 ) {		      /* printed in parts if needed*/
        !          10269: 		  fprintf(filefp,"%.*s\n",loglen,refp); /* print a part */
        !          10270: 		  if ( strlen(refp) <= loglen ) break;  /* no more parts */
        !          10271: 		  refp += loglen; } }	      /* bump ptr to next part */
        !          10272: 	   if ( !isreflogged )		      /* http_referer not logged */
        !          10273: 	     fprintf(filefp,"http://none\n"); /* so log dummy referer line */
        !          10274: 	   fprintf(filefp,"%s\n",dashes);     /* separator line */
        !          10275: 	   fclose(filefp); }		     /* close logfile immediately */
        !          10276:       } /* --- end-of-if(cachelog!=NULL) --- */
        !          10277:    } /* --- end-of-if/else(md5hash==NULL) --- */
        !          10278:   } /* --- end-of-if(iscaching) --- */
        !          10279: /* ---
        !          10280:  * emit copyright, gnu/gpl notice (if "interactive")
        !          10281:  * ------------------------------------------------- */
        !          10282: if ( !isdumpimage )			/* don't mix ascii with image dump */
        !          10283:  if ( (!isquery||isqlogging) && msgfp!=NULL ) /* called from command line */
        !          10284:    fprintf(msgfp,"%s\n",copyright);	/* display copyright, gnu/gpl info */
        !          10285: /* -------------------------------------------------------------------------
        !          10286: rasterize expression and put a border around it
        !          10287: -------------------------------------------------------------------------- */
        !          10288: /* --- preprocess expression, converting LaTeX constructs for mimeTeX  --- */
        !          10289: expression = mimeprep(expression);	/* preprocess expression */
        !          10290: /* --- double-check that we actually have an expression to rasterize --- */
        !          10291: if ( expression == NULL )		/* nothing to rasterize */
        !          10292:  { if ( (!isquery||isqlogging) && msgfp!=NULL ) /*emit error if not a query*/
        !          10293:      fprintf(msgfp,"No expression to rasterize\n");
        !          10294:    goto end_of_job; }			/* and then quit */
        !          10295: /* --- rasterize expression --- */
        !          10296: if ( (sp = rasterize(expression,size)) == NULL ) /* failed to rasterize */
        !          10297:  { if ( (!isquery||isqlogging) && msgfp!=NULL ) /*emit error if not a query*/
        !          10298:      fprintf(msgfp,"Failed to rasterize %s\n",expression);
        !          10299:    if ( isquery ) sp = rasterize(	/* or emit error raster if query */
        !          10300:      "\\red\\rm~\\fbox{mimeTeX~failed~to~render\\\\your~expression}",1);
        !          10301:    if ( sp ==  NULL ) goto end_of_job; } /* re-check for failure */
        !          10302: /* ---no border requested, but this adjusts width to multiple of 8 bits--- */
        !          10303: if ( issupersampling )			/* no border needed for gifs */
        !          10304:   bp = sp->image;			/* so just extract pixel map */
        !          10305: else					/* for mime xbitmaps must have... */
        !          10306:   bp = border_raster(sp->image,0,0,0,1); /* image width multiple of 8 bits */
        !          10307: sp->image = bitmap_raster = bp;		/* global copy for gif,png output */
        !          10308: /* -------------------------------------------------------------------------
        !          10309: generate anti-aliased bytemap from (bordered) bitmap
        !          10310: -------------------------------------------------------------------------- */
        !          10311: if ( isaa )				/* we want anti-aliased bitmap */
        !          10312:   {
        !          10313:   /* ---
        !          10314:    * allocate bytemap and colormap as per width*height of bitmap
        !          10315:    * ----------------------------------------------------------- */
        !          10316:   int	nbytes = (bp->width)*(bp->height); /*#bytes needed in byte,colormap*/
        !          10317:   if ( isss )				/* anti-aliasing by supersampling */
        !          10318:     bytemap_raster = (intbyte *)(bitmap_raster->pixmap); /*bytemap in raster*/
        !          10319:   else					/* need to allocate bytemap */
        !          10320:     if ( aaalgorithm == 0 )		/* anti-aliasing not wanted */
        !          10321:       isaa = 0;				/* so signal no anti-aliasing */
        !          10322:     else				/* anti-aliasing wanted */
        !          10323:       if ( (bytemap_raster = (intbyte *)malloc(nbytes)) /* malloc bytemap */
        !          10324:       ==   NULL ) isaa = 0;		/* reset flag if malloc failed */
        !          10325:   if ( isaa )				/* have bytemap, so... */
        !          10326:     if ( (colormap_raster = (intbyte *)malloc(nbytes)) /* malloc colormap */
        !          10327:     ==   NULL ) isaa = 0;		/* reset flag if malloc failed */
        !          10328:   /* ---
        !          10329:    * now generate anti-aliased bytemap and colormap from bitmap
        !          10330:    * ---------------------------------------------------------- */
        !          10331:   if ( isaa )				/*re-check that we're anti-aliasing*/
        !          10332:     {
        !          10333:     /* ---
        !          10334:      * select anti-aliasing algorithm
        !          10335:      * ------------------------------ */
        !          10336:     if ( !isss )			/* generate bytemap for lowpass */
        !          10337:      if ( aaalgorithm == 1 )		/* 1 for aalowpass() */
        !          10338: 	{ if ( aalowpass(bp,bytemap_raster,grayscale) /* my lowpass filter */
        !          10339: 	  ==   0 )  isaa = 0; }		/*failed, so turn off anti-aliasing*/
        !          10340:      else				/* or 2 for aapnm() */
        !          10341:       if ( aaalgorithm == 2 )		/*2 for netpbm pnmalias.c algorithm*/
        !          10342: 	{ if ( aapnm(bp,bytemap_raster,grayscale) /* pnmalias.c filter */
        !          10343: 	  ==   0 )  isaa = 0; }		/*failed, so turn off anti-aliasing*/
        !          10344:       else isaa = 0;			/* unrecognized algorithm */
        !          10345:     /* ---
        !          10346:      * finally, generate colors and colormap
        !          10347:      * ------------------------------------- */
        !          10348:     if ( isaa ) {			/* we have bytemap_raster */
        !          10349:       ncolors = aacolormap(bytemap_raster,nbytes,colors,colormap_raster);
        !          10350:       if ( ncolors < 2 )		/* failed */
        !          10351: 	{ isaa = 0;			/* so turn off anti-aliasing */
        !          10352: 	  ncolors = 2; }		/* and reset for black&white */
        !          10353:       } /* --- end-of-if(isaa) --- */
        !          10354:     } /* --- end-of-if(isaa) --- */
        !          10355:   } /* --- end-of-if(isaa) --- */
        !          10356: /* -------------------------------------------------------------------------
        !          10357: display results on msgfp if called from command line (usually for testing)
        !          10358: -------------------------------------------------------------------------- */
        !          10359: if ( (!isquery||isqlogging) || msglevel >= 99 )	/*command line or debuging*/
        !          10360:  if ( !isdumpimage )			/* don't mix ascii with image dump */
        !          10361:   {
        !          10362:   /* ---
        !          10363:    * display ascii image of rasterize()'s rasterized bitmap
        !          10364:    * ------------------------------------------------------ */
        !          10365:   if ( !isss )				/* no bitmap for supersampling */
        !          10366:     { fprintf(msgfp,"\nAscii dump of bitmap image...\n");
        !          10367:       type_raster(bp,msgfp); }		/* emit ascii image of raster */
        !          10368:   /* ---
        !          10369:    * display anti-aliasing results applied to rasterized bitmap
        !          10370:    * ---------------------------------------------------------- */
        !          10371:   if ( isaa )				/* if anti-aliasing applied */
        !          10372:     {
        !          10373:     int	igray;				/* colors[] index */
        !          10374:     /* --- anti-aliased bytemap image --- */
        !          10375:     if ( msgfp!=NULL && msglevel>=9 )	/* don't usually emit raw bytemap */
        !          10376:       {	fprintf(msgfp,"\nHex dump of anti-aliased bytemap, " /*emit bytemap*/
        !          10377: 	"asterisks denote \"black\" bytes (value=%d)...\n",grayscale-1);
        !          10378: 	type_bytemap(bytemap_raster,grayscale,bp->width,bp->height,msgfp); }
        !          10379:     /* --- colormap image --- */
        !          10380:     fprintf(msgfp,"\nHex dump of colormap indexes, "  /* emit colormap */
        !          10381:       "asterisks denote \"black\" bytes (index=%d)...\n",ncolors-1);
        !          10382:     type_bytemap(colormap_raster,ncolors,bp->width,bp->height,msgfp);
        !          10383:     /* --- rgb values corresponding to colormap indexes */
        !          10384:     fprintf(msgfp,"\nThe %d colormap indexes denote rgb values...",ncolors);
        !          10385:     for ( igray=0; igray<ncolors; igray++ ) /* show colors[] values */
        !          10386:       fprintf(msgfp,"%s%2x-->%3d", (igray%5?"   ":"\n"),
        !          10387: 	igray,(int)(colors[ncolors-1]-colors[igray]));
        !          10388:     fprintf(msgfp,"\n");		/* always needs a final newline */
        !          10389:     } /* --- end-of-if(isaa) --- */
        !          10390:   } /* --- end-of-if(!isquery||msglevel>=9) --- */
        !          10391: /* -------------------------------------------------------------------------
        !          10392: emit xbitmap or gif image, and exit
        !          10393: -------------------------------------------------------------------------- */
        !          10394: if (  isquery				/* called from browser (usual) */
        !          10395: ||    isdumpimage			/* or to emit dump of image */
        !          10396: ||    msglevel >= 99 )			/* or for debugging */
        !          10397:  {
        !          10398:  int  igray = 0;			/* grayscale index */
        !          10399:  #if defined(GIF)			/* compiled to emit gif */
        !          10400:  /* ------------------------------------------------------------------------
        !          10401:  emit GIF image
        !          10402:  ------------------------------------------------------------------------- */
        !          10403:   /* --- emit mime content-type line --- */
        !          10404:   if ( !isdumpimage			/* don't mix ascii with image dump */
        !          10405:   &&   !iscaching )			/* already emitted if caching */
        !          10406:     { fprintf( stdout, "Cache-Control: max-age=%d\n",maxage );
        !          10407:       /*fprintf( stdout, "Expires: Fri, 31 Oct 2003 23:59:59 GMT\n" );*/
        !          10408:       /*fprintf( stdout, "Last-Modified: Wed, 15 Oct 2003 01:01:01 GMT\n" );*/
        !          10409:       fprintf( stdout, "Content-type: image/gif\n\n" ); }
        !          10410:   /* --- initialize gifsave library and colors --- */
        !          10411:   if ( msgfp!=NULL && msglevel>=999 )
        !          10412:     fprintf(msgfp,"main> calling GIF_Create(*,%d,%d,%d,8)\n",
        !          10413:     bp->width,bp->height,ncolors);
        !          10414:   while ( 1 )		/* init gifsave lib, and retry if caching fails */
        !          10415:     { int status = GIF_Create(gif_outfile, bp->width,bp->height, ncolors, 8);
        !          10416:       if ( status == 0 ) break;		/* continue if succeeded */
        !          10417:       if ( iscaching == 0 ) goto end_of_job; /* quit if failed */
        !          10418:       iscaching = 0;			/* retry without cache file */
        !          10419:       gif_outfile = (char *)NULL; }	/* emit images to stdout */
        !          10420:   GIF_SetColor(0,bgred,bggreen,bgblue);	/* background white if all 255 */
        !          10421:   if ( !isaa )				/* just b&w if not anti-aliased */
        !          10422:     { GIF_SetColor(1,fgred,fggreen,fgblue); /* foreground black if all 0 */
        !          10423:       colors[0]='\000'; colors[1]='\001'; } /* and set 2 b&w color indexes */
        !          10424:   else					/* set grayscales for anti-aliasing */
        !          10425:     /* --- anti-aliased, so call GIF_SetColor() for each colors[] --- */
        !          10426:     for ( igray=1; igray<ncolors; igray++ ) /* for colors[] values */
        !          10427:       {
        !          10428:       /*--- gfrac goes from 0 to 1.0, as igray goes from 0 to ncolors-1 ---*/
        !          10429:       double gfrac = ((double)colors[igray])/((double)colors[ncolors-1]);
        !          10430:       /* --- r,g,b components go from background to foreground color --- */
        !          10431:       int red  = iround(((double)bgred)  +gfrac*((double)(fgred-bgred))),
        !          10432: 	  green= iround(((double)bggreen)+gfrac*((double)(fggreen-bggreen))),
        !          10433: 	  blue = iround(((double)bgblue) +gfrac*((double)(fgblue-bgblue)));
        !          10434:       /* --- set color index number igray to rgb values gray,gray,gray --- */
        !          10435:       GIF_SetColor(igray, red,green,blue); /*set gray,grayer,...,0=black*/
        !          10436:       } /* --- end-of-for(igray) --- */
        !          10437:   /* --- set gif color#0 (background) transparent --- */
        !          10438:   if ( istransparent )			/* transparent background wanted */
        !          10439:     GIF_SetTransparent(0);		/* set transparent background */
        !          10440:   if (msgfp!=NULL && msglevel>=9) fflush(msgfp); /*flush debugging output*/
        !          10441:   /* --- emit compressed gif image (to stdout or cache file) --- */
        !          10442:   GIF_CompressImage(0, 0, -1, -1, GetPixel); /* emit gif */
        !          10443:   GIF_Close();				/* close file */
        !          10444:   /* --- may need to emit image from cached file --- */
        !          10445:   if ( isquery && iscaching )		/* caching enabled */
        !          10446:     emitcache(cachefile);		/* cached image (hopefully) emitted*/
        !          10447:  #else
        !          10448:  /* ------------------------------------------------------------------------
        !          10449:  emit mime XBITMAP image
        !          10450:  ------------------------------------------------------------------------- */
        !          10451:   xbitmap_raster(bp,stdout);		/* default emits mime xbitmap */
        !          10452:  #endif
        !          10453:  } /* --- end-of-if(isquery) --- */
        !          10454: /* --- exit --- */
        !          10455: end_of_job:
        !          10456:   if ( bytemap_raster != NULL ) free(bytemap_raster); /*free bytemap_raster*/
        !          10457:   if (colormap_raster != NULL )free(colormap_raster); /*and colormap_raster*/
        !          10458:   if ( msgfp != NULL			/* have message/log file open */
        !          10459:   &&   msgfp != stdout )		/* and it's not stdout */
        !          10460:    { fprintf(msgfp,"mimeTeX> successful end-of-job at %s\n",timestamp());
        !          10461:      fprintf(msgfp,"%s\n",dashes);	/* so log separator line */
        !          10462:      fclose(msgfp); }			/* and close logfile */
        !          10463:   exit ( 0 );
        !          10464: } /* --- end-of-function main() --- */
        !          10465: 
        !          10466: /* ==========================================================================
        !          10467:  * Function:	isstrstr ( char *string, char *snippets, int iscase )
        !          10468:  * Purpose:	determine whether any substring of 'string'
        !          10469:  *		matches any of the comma-separated list of 'snippets',
        !          10470:  *		ignoring case if iscase=0.
        !          10471:  * --------------------------------------------------------------------------
        !          10472:  * Arguments:	string (I)	char * containing null-terminated
        !          10473:  *				string that will be searched for
        !          10474:  *				any one of the specified snippets
        !          10475:  *		snippets (I)	char * containing null-terminated,
        !          10476:  *				comma-separated list of snippets
        !          10477:  *				to be searched for in string
        !          10478:  *		iscase (I)	int containing 0 for case-insensitive
        !          10479:  *				comparisons, or 1 for case-sensitive
        !          10480:  * --------------------------------------------------------------------------
        !          10481:  * Returns:	( int )		1 if any snippet is a substring of
        !          10482:  *				string, 0 if not
        !          10483:  * --------------------------------------------------------------------------
        !          10484:  * Notes:     o
        !          10485:  * ======================================================================= */
        !          10486: /* --- entry point --- */
        !          10487: int	isstrstr ( char *string, char *snippets, int iscase )
        !          10488: {
        !          10489: /* -------------------------------------------------------------------------
        !          10490: Allocations and Declarations
        !          10491: -------------------------------------------------------------------------- */
        !          10492: int	status = 0;			/*1 if any snippet found in string*/
        !          10493: char	snip[99], *snipptr = snippets,	/* munge through each snippet */
        !          10494: 	delim = ',', *delimptr = NULL;	/* separated by delim's */
        !          10495: char	stringcp[999], *cp = stringcp;	/*maybe lowercased copy of string*/
        !          10496: /* -------------------------------------------------------------------------
        !          10497: initialization
        !          10498: -------------------------------------------------------------------------- */
        !          10499: /* --- arg check --- */
        !          10500: if ( string==NULL || snippets==NULL ) goto end_of_job; /* missing arg */
        !          10501: if ( *string=='\000' || *snippets=='\000' ) goto end_of_job; /* empty arg */
        !          10502: /* --- copy string and lowercase it if case-insensitive --- */
        !          10503: strcpy(stringcp,string);		/* local copy of string */
        !          10504: if ( !iscase )				/* want case-insensitive compares */
        !          10505:   for ( cp=stringcp; *cp != '\000'; cp++ ) /* so for each string char */
        !          10506:     if ( isupper(*cp) ) *cp = tolower(*cp); /*lowercase any uppercase chars*/
        !          10507: /* -------------------------------------------------------------------------
        !          10508: extract each snippet and see if it's a substring of string
        !          10509: -------------------------------------------------------------------------- */
        !          10510: while ( snipptr != NULL )		/* while we still have snippets */
        !          10511:   {
        !          10512:   /* --- extract next snippet --- */
        !          10513:   if ( (delimptr = strchr(snipptr,delim)) /* locate next comma delim */
        !          10514:   ==   NULL )				/*not found following last snippet*/
        !          10515:     { strcpy(snip,snipptr);		/* local copy of last snippet */
        !          10516:       snipptr = NULL; }			/* signal end-of-string */
        !          10517:   else					/* snippet ends just before delim */
        !          10518:     { int sniplen = (int)(delimptr-snipptr) - 1;  /* #chars in snippet */
        !          10519:       memcpy(snip,snipptr,sniplen);	/* local copy of snippet chars */
        !          10520:       snip[sniplen] = '\000';		/* null-terminated snippet */
        !          10521:       snipptr = delimptr + 1; }		/* next snippet starts after delim */
        !          10522:   /* --- lowercase snippet if case-insensitive --- */
        !          10523:   if ( !iscase )			/* want case-insensitive compares */
        !          10524:     for ( cp=snip; *cp != '\000'; cp++ ) /* so for each snippet char */
        !          10525:       if ( isupper(*cp) ) *cp=tolower(*cp); /*lowercase any uppercase chars*/
        !          10526:   /* --- check if snippet in string --- */
        !          10527:   if ( strstr(stringcp,snip) != NULL )	/* found snippet in string */
        !          10528:     { status = 1;			/* so reset return status */
        !          10529:       break; }				/* no need to check any further */
        !          10530:   } /* --- end-of-while(*snipptr!=0) --- */
        !          10531: end_of_job: return ( status );		/*1 if snippet found in list, else 0*/
        !          10532: } /* --- end-of-function isstrstr() --- */
        !          10533: 
        !          10534: /* ==========================================================================
        !          10535:  * Function:	ismonth ( char *month )
        !          10536:  * Purpose:	returns 1 if month contains current month "jan"..."dec".
        !          10537:  * --------------------------------------------------------------------------
        !          10538:  * Arguments:	month (I)	char * containing null-terminated string
        !          10539:  *				in which "jan"..."dec" is (putatively)
        !          10540:  *				contained as a substring.
        !          10541:  * --------------------------------------------------------------------------
        !          10542:  * Returns:	( int )		1 if month contains current month,
        !          10543:  *				0 otherwise
        !          10544:  * --------------------------------------------------------------------------
        !          10545:  * Notes:     o	There's a three day "grace period", e.g., Dec 3 mtaches Nov.
        !          10546:  * ======================================================================= */
        !          10547: /* --- entry point --- */
        !          10548: int	ismonth ( char *month )
        !          10549: {
        !          10550: /* -------------------------------------------------------------------------
        !          10551: Allocations and Declarations
        !          10552: -------------------------------------------------------------------------- */
        !          10553: int	isokay = 0;			/*1 if month contains current month*/
        !          10554: /*long	time_val = 0L;*/		/* binary value returned by time() */
        !          10555: time_t	time_val = (time_t)(0);		/* binary value returned by time() */
        !          10556: struct tm *tmstruct=(struct tm *)NULL, *localtime(); /* interpret time_val */
        !          10557: int	imonth, mday;			/* current month 1-12 and day 1-31 */
        !          10558: int	ngrace = 3;			/* grace period */
        !          10559: char	lcmonth[128]="\000"; int i=0;	/* lowercase month */
        !          10560: static	char *months[] =		/* month must contain current one */
        !          10561:    {"dec","jan","feb","mar","apr","may","jun",
        !          10562:     "jul","aug","sep","oct","nov","dec","jan"};
        !          10563: /* -------------------------------------------------------------------------
        !          10564: get current date:time info, and check month
        !          10565: -------------------------------------------------------------------------- */
        !          10566: /* --- lowercase input month --- */
        !          10567: if ( month != NULL )			/* check that we got input */
        !          10568:   for ( i=0; i<120 && *month!='\000'; i++,month++ ) /* go thru month chars */
        !          10569:     lcmonth[i] = tolower(*month);	/* lowerase each char in month */
        !          10570: if ( i < 2 ) goto end_of_job;		/* must be invalid input */
        !          10571: lcmonth[i] = '\000';			/* null-terminate lcmonth[] */
        !          10572: /* --- get current date:time --- */
        !          10573: time((time_t *)(&time_val));		/* get date and time */
        !          10574: tmstruct = localtime((time_t *)(&time_val)); /* interpret time_val */
        !          10575: /* --- month and day  --- */
        !          10576: imonth = 1 + (int)(tmstruct->tm_mon);	/* 1=jan ... 12=dec */
        !          10577: mday = (int)(tmstruct->tm_mday);	/* 1-31 */
        !          10578: if ( imonth<1 || imonth>12		/* quit if month out-of-range */
        !          10579: ||   mday<0 || mday>31 ) goto end_of_job; /* or date out of range */
        !          10580: /* --- check input month against current date --- */
        !          10581: if ( strstr(lcmonth,months[imonth]) != NULL ) isokay = 1; /* current month */
        !          10582: if ( mday <= ngrace )			/* 1-3 within grace period */
        !          10583:  if ( strstr(lcmonth,months[imonth-1]) != NULL ) isokay = 1; /* last month */
        !          10584: if ( mday >= 31-ngrace )		/* 28-31 within grace period */
        !          10585:  if ( strstr(lcmonth,months[imonth+1]) != NULL ) isokay = 1; /* next month */
        !          10586: end_of_job:
        !          10587:   return ( isokay );			/*1 if month contains current month*/
        !          10588: } /* --- end-of-function ismonth() --- */
        !          10589: 
        !          10590: /* ==========================================================================
        !          10591:  * Functions:	int  unescape_url ( char *url, int isescape )
        !          10592:  *		char x2c ( char *what )
        !          10593:  * Purpose:	unescape_url replaces 3-character sequences %xx in url
        !          10594:  *		    with the single character represented by hex xx.
        !          10595:  *		x2c returns the single character represented by hex xx
        !          10596:  *		    passed as a 2-character sequence in what.
        !          10597:  * --------------------------------------------------------------------------
        !          10598:  * Arguments:	url (I)		char * containing null-terminated
        !          10599:  *				string with embedded %xx sequences
        !          10600:  *				to be converted.
        !          10601:  *		isescape (I)	int containing 1 to _not_ unescape
        !          10602:  *				\% sequences (0 would be NCSA default)
        !          10603:  *		what (I)	char * whose first 2 characters are
        !          10604:  *				interpreted as ascii representations
        !          10605:  *				of hex digits.
        !          10606:  * --------------------------------------------------------------------------
        !          10607:  * Returns:	( int )		unescape_url always returns 0.
        !          10608:  *		( char )	x2c returns the single char
        !          10609:  *				corresponding to hex xx passed in what.
        !          10610:  * --------------------------------------------------------------------------
        !          10611:  * Notes:     o	These two functions were taken verbatim from util.c in
        !          10612:  *   ftp://ftp.ncsa.uiuc.edu/Web/httpd/Unix/ncsa_httpd/cgi/ncsa-default.tar.Z
        !          10613:  *	      o	Not quite "verbatim" -- I added the "isescape logic" 4-Dec-03
        !          10614:  *		so unescape_url() can be safely applied to input which may or
        !          10615:  *		may not have been url-encoded.
        !          10616:  * ======================================================================= */
        !          10617: /* --- entry point --- */
        !          10618: int unescape_url(char *url, int isescape) {
        !          10619:     int x=0,y=0,prevescape=0,gotescape=0;
        !          10620:     char x2c();
        !          10621:     static char *hex="0123456789ABCDEFabcdef";
        !          10622:     for(;url[y];++x,++y) {
        !          10623: 	gotescape = prevescape;
        !          10624: 	prevescape = (url[x]=='\\');
        !          10625: 	if((url[x] = url[y]) == '%')
        !          10626: 	 if(!isescape || !gotescape)
        !          10627: 	  if(isthischar(url[y+1],hex)
        !          10628: 	  && isthischar(url[y+2],hex))
        !          10629: 	    { url[x] = x2c(&url[y+1]);
        !          10630: 	      y+=2; }
        !          10631:     }
        !          10632:     url[x] = '\0';
        !          10633:     return 0;
        !          10634: } /* --- end-of-function unescape_url() --- */
        !          10635: /* --- entry point --- */
        !          10636: char x2c(char *what) {
        !          10637:     char digit;
        !          10638:     digit = (what[0] >= 'A' ? ((what[0] & 0xdf) - 'A')+10 : (what[0] - '0'));
        !          10639:     digit *= 16;
        !          10640:     digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A')+10 : (what[1] - '0'));
        !          10641:     return(digit);
        !          10642: } /* --- end-of-function x2c() --- */
        !          10643: 
        !          10644: /* ==========================================================================
        !          10645:  * Function:	logger ( fp, msglevel, message, logvars )
        !          10646:  * Purpose:	Logs the environment variables specified in logvars
        !          10647:  *		to fp if their msglevel is >= the passed msglevel.
        !          10648:  * --------------------------------------------------------------------------
        !          10649:  * Arguments:	fp (I)		FILE * to file containing log
        !          10650:  *		msglevel (I)	int containing logging message level
        !          10651:  *		message (I)	char * to optional message, or NULL
        !          10652:  *		logvars (I)	logdata * to array of environment variables
        !          10653:  *				to be logged
        !          10654:  * --------------------------------------------------------------------------
        !          10655:  * Returns:	( int )		number of variables from logvars
        !          10656:  *				that were actually logged
        !          10657:  * --------------------------------------------------------------------------
        !          10658:  * Notes:     o
        !          10659:  * ======================================================================= */
        !          10660: /* --- entry point --- */
        !          10661: int	logger ( FILE *fp, int msglevel, char *message, logdata *logvars )
        !          10662: {
        !          10663: /* -------------------------------------------------------------------------
        !          10664: Allocations and Declarations
        !          10665: -------------------------------------------------------------------------- */
        !          10666: int	ilog=0, nlogged=0;		/* logvars[] index, #vars logged */
        !          10667: char	*timestamp();			/* timestamp logged */
        !          10668: char	*value = NULL;			/* getenv(name) to be logged */
        !          10669: /* -------------------------------------------------------------------------
        !          10670: Log each variable
        !          10671: -------------------------------------------------------------------------- */
        !          10672: fprintf(fp,"%s\n",timestamp());		/* emit timestamp before first var */
        !          10673: if ( message != NULL )			/* optional message supplied */
        !          10674:  fprintf(fp,"  MESSAGE = %s\n",message); /* emit caller-supplied message */
        !          10675: if ( logvars != (logdata *)NULL )	/* have logvars */
        !          10676:  for ( ilog=0; logvars[ilog].name != NULL; ilog++ )  /* till end-of-table */
        !          10677:   if ( msglevel >= logvars[ilog].msglevel ) /* check msglevel for this var */
        !          10678:    if ( (value=getenv(logvars[ilog].name))  /* getenv(name) to be logged */
        !          10679:    != NULL )				/* check that name exists */
        !          10680:     {
        !          10681:     fprintf(fp,"  %s = %.*s\n",		/* emit variable name = value */
        !          10682:      logvars[ilog].name,logvars[ilog].maxlen,value);
        !          10683:     nlogged++;				/* bump #vars logged */
        !          10684:     } /* --- end-of-for(ilog) --- */
        !          10685: return ( nlogged );			/* back to caller */
        !          10686: } /* --- end-of-function logger() --- */
        !          10687: 
        !          10688: /* ==========================================================================
        !          10689:  * Function:	emitcache ( cachefile )
        !          10690:  * Purpose:	dumps bytes from cachefile to stdout
        !          10691:  * --------------------------------------------------------------------------
        !          10692:  * Arguments:	cachefile (I)	pointer to null-terminated char string
        !          10693:  *				containing full path to file to be dumped
        !          10694:  * --------------------------------------------------------------------------
        !          10695:  * Returns:	( int )		#bytes dumped (0 signals error)
        !          10696:  * --------------------------------------------------------------------------
        !          10697:  * Notes:     o
        !          10698:  * ======================================================================= */
        !          10699: /* --- entry point --- */
        !          10700: int	emitcache ( char *cachefile )
        !          10701: {
        !          10702: /* -------------------------------------------------------------------------
        !          10703: Allocations and Declarations
        !          10704: -------------------------------------------------------------------------- */
        !          10705: FILE	*cacheptr = fopen(cachefile,"rb"), /*open cachefile for binary read*/
        !          10706: 	*emitptr = stdout;		/* emit cachefile to stdout */
        !          10707: unsigned char buffer[64];		/* characters from cachefile */
        !          10708: int	buflen = 32,			/* #bytes we try to read from cache*/
        !          10709: 	nread = 0,			/* #bytes actually read */
        !          10710: 	nbytes = 0;			/* total #bytes emitted */
        !          10711: /* -------------------------------------------------------------------------
        !          10712: initialization
        !          10713: -------------------------------------------------------------------------- */
        !          10714: /* --- check that files opened okay --- */
        !          10715: if ( cacheptr == (FILE *)NULL		/* failed to open cachefile */
        !          10716: ||   emitptr == (FILE *)NULL )		/* or failed to open emit file */
        !          10717:   goto end_of_job;			/* so return 0 bytes to caller */
        !          10718: /* --- set stdout to binary mode (for Windows) --- */
        !          10719: /* emitptr = fdopen(STDOUT_FILENO,"wb"); */  /* doesn't work portably, */
        !          10720: #ifdef WINDOWS				/* so instead... */
        !          10721:   #ifdef HAVE_SETMODE			/* prefer (non-portable) setmode() */
        !          10722:     if ( setmode ( fileno (stdout), O_BINARY) /* windows specific call */
        !          10723:     == -1 ) ; /* handle error */	/* sets stdout to binary mode */
        !          10724:   #else					/* setmode() not available */
        !          10725:     #if 1
        !          10726:       freopen ("CON", "wb", stdout);	/* freopen() stdout binary */
        !          10727:     #else
        !          10728:       stdout = fdopen (STDOUT_FILENO, "wb"); /* fdopen() stdout binary */
        !          10729:     #endif
        !          10730:   #endif
        !          10731: #endif
        !          10732: /* -------------------------------------------------------------------------
        !          10733: emit bytes from cachefile
        !          10734: -------------------------------------------------------------------------- */
        !          10735: while ( 1 )
        !          10736:   {
        !          10737:   /* --- read bytes from cachefile --- */
        !          10738:   nread = fread(buffer,sizeof(unsigned char),buflen,cacheptr); /* read */
        !          10739:   if ( nread < 1 ) break;		/* no bytes left in cachefile */
        !          10740:   /* --- write bytes to stdout --- */
        !          10741:   if ( fwrite(buffer,sizeof(unsigned char),nread,emitptr) /* write buffer */
        !          10742:   <    nread)				/* failed to write all bytes */
        !          10743:     { nbytes = 0;			/* reset total count to 0 */
        !          10744:       goto end_of_job; }		/* and signal error to caller */
        !          10745:   nbytes += nread;			/* bump total #bytes emitted */
        !          10746:   if ( nread < buflen ) break;		/* no bytes left in cachefile */
        !          10747:   } /* --- end-of-while(1) --- */
        !          10748: end_of_job:
        !          10749:   if ( cacheptr != NULL ) fclose(cacheptr); /* close file if opened */
        !          10750:   return ( nbytes );			/* back with #bytes emitted */
        !          10751: } /* --- end-of-function emitcache() --- */
        !          10752: 
        !          10753: /* ==========================================================================
        !          10754:  * Function:	md5str ( instr )
        !          10755:  * Purpose:	returns null-terminated character string containing
        !          10756:  *		md5 hash of instr (input string)
        !          10757:  * --------------------------------------------------------------------------
        !          10758:  * Arguments:	instr (I)	pointer to null-terminated char string
        !          10759:  *				containing input string whose md5 hash
        !          10760:  *				is desired
        !          10761:  * --------------------------------------------------------------------------
        !          10762:  * Returns:	( char * )	ptr to null-terminated 32-character
        !          10763:  *				md5 hash of instr
        !          10764:  * --------------------------------------------------------------------------
        !          10765:  * Notes:     o	Other md5 library functions are included below.
        !          10766:  *		They're all taken from Christophe Devine's code,
        !          10767:  *		which (as of 04-Aug-2004) is available from
        !          10768:  *		     http://www.cr0.net:8040/code/crypto/md5/
        !          10769:  *	      o	The P,F,S macros in the original code are replaced
        !          10770:  *		by four functions P1()...P4() to accommodate a problem
        !          10771:  *		with Compaq's vax/vms C compiler.
        !          10772:  * ======================================================================= */
        !          10773: /* --- #include "md5.h" --- */
        !          10774: #ifndef uint8
        !          10775:   #define uint8  unsigned char
        !          10776: #endif
        !          10777: #ifndef uint32
        !          10778:   #define uint32 unsigned long int
        !          10779: #endif
        !          10780: typedef struct
        !          10781:   { uint32 total[2];
        !          10782:     uint32 state[4];
        !          10783:     uint8 buffer[64];
        !          10784:   } md5_context;
        !          10785: void md5_starts( md5_context *ctx );
        !          10786: void md5_update( md5_context *ctx, uint8 *input, uint32 length );
        !          10787: void md5_finish( md5_context *ctx, uint8 digest[16] );
        !          10788: /* --- md5.h --- */
        !          10789: #define GET_UINT32(n,b,i)                       \
        !          10790:   { (n) = ( (uint32) (b)[(i)    ]       )       \
        !          10791:         | ( (uint32) (b)[(i) + 1] <<  8 )       \
        !          10792:         | ( (uint32) (b)[(i) + 2] << 16 )       \
        !          10793:         | ( (uint32) (b)[(i) + 3] << 24 ); }
        !          10794: #define PUT_UINT32(n,b,i)                       \
        !          10795:   { (b)[(i)    ] = (uint8) ( (n)       );       \
        !          10796:     (b)[(i) + 1] = (uint8) ( (n) >>  8 );       \
        !          10797:     (b)[(i) + 2] = (uint8) ( (n) >> 16 );       \
        !          10798:     (b)[(i) + 3] = (uint8) ( (n) >> 24 ); }
        !          10799: /* --- P,S,F macros defined as functions --- */
        !          10800: void P1(uint32 *X,uint32 *a,uint32 b,uint32 c,uint32 d,int k,int s,uint32 t)
        !          10801:   { *a += (uint32)(d ^ (b & (c ^ d))) + X[k] + t;
        !          10802:     *a  = ((*a<<s) | ((*a & 0xFFFFFFFF) >> (32-s))) + b;
        !          10803:     return; }
        !          10804: void P2(uint32 *X,uint32 *a,uint32 b,uint32 c,uint32 d,int k,int s,uint32 t)
        !          10805:   { *a += (uint32)(c ^ (d & (b ^ c))) + X[k] + t;
        !          10806:     *a  = ((*a<<s) | ((*a & 0xFFFFFFFF) >> (32-s))) + b;
        !          10807:     return; }
        !          10808: void P3(uint32 *X,uint32 *a,uint32 b,uint32 c,uint32 d,int k,int s,uint32 t)
        !          10809:   { *a += (uint32)(b ^ c ^ d) + X[k] + t;
        !          10810:     *a  = ((*a<<s) | ((*a & 0xFFFFFFFF) >> (32-s))) + b;
        !          10811:     return; }
        !          10812: void P4(uint32 *X,uint32 *a,uint32 b,uint32 c,uint32 d,int k,int s,uint32 t)
        !          10813:   { *a += (uint32)(c ^ (b | ~d)) + X[k] + t;
        !          10814:     *a  = ((*a<<s) | ((*a & 0xFFFFFFFF) >> (32-s))) + b;
        !          10815:     return; }
        !          10816: 
        !          10817: /* --- entry point (this one little stub written by me)--- */
        !          10818: char *md5str( char *instr )
        !          10819:   { static char outstr[64];
        !          10820:     unsigned char md5sum[16];
        !          10821:     md5_context ctx;
        !          10822:     int j;
        !          10823:     md5_starts( &ctx );
        !          10824:     md5_update( &ctx, (uint8 *)instr, strlen(instr) );
        !          10825:     md5_finish( &ctx, md5sum );
        !          10826:     for( j=0; j<16; j++ )
        !          10827:       sprintf( outstr + j*2, "%02x", md5sum[j] );
        !          10828:     outstr[32] = '\000';
        !          10829:     return ( outstr ); }
        !          10830: 
        !          10831: /* --- entry point (all md5 functions below by Christophe Devine) --- */
        !          10832: void md5_starts( md5_context *ctx )
        !          10833:   { ctx->total[0] = 0;
        !          10834:     ctx->total[1] = 0;
        !          10835:     ctx->state[0] = 0x67452301;
        !          10836:     ctx->state[1] = 0xEFCDAB89;
        !          10837:     ctx->state[2] = 0x98BADCFE;
        !          10838:     ctx->state[3] = 0x10325476; }
        !          10839: 
        !          10840: void md5_process( md5_context *ctx, uint8 data[64] )
        !          10841:   { uint32 X[16], A, B, C, D;
        !          10842:     GET_UINT32( X[0],  data,  0 );
        !          10843:     GET_UINT32( X[1],  data,  4 );
        !          10844:     GET_UINT32( X[2],  data,  8 );
        !          10845:     GET_UINT32( X[3],  data, 12 );
        !          10846:     GET_UINT32( X[4],  data, 16 );
        !          10847:     GET_UINT32( X[5],  data, 20 );
        !          10848:     GET_UINT32( X[6],  data, 24 );
        !          10849:     GET_UINT32( X[7],  data, 28 );
        !          10850:     GET_UINT32( X[8],  data, 32 );
        !          10851:     GET_UINT32( X[9],  data, 36 );
        !          10852:     GET_UINT32( X[10], data, 40 );
        !          10853:     GET_UINT32( X[11], data, 44 );
        !          10854:     GET_UINT32( X[12], data, 48 );
        !          10855:     GET_UINT32( X[13], data, 52 );
        !          10856:     GET_UINT32( X[14], data, 56 );
        !          10857:     GET_UINT32( X[15], data, 60 );
        !          10858:     A = ctx->state[0];
        !          10859:     B = ctx->state[1];
        !          10860:     C = ctx->state[2];
        !          10861:     D = ctx->state[3];
        !          10862:     P1( X, &A, B, C, D,  0,  7, 0xD76AA478 );
        !          10863:     P1( X, &D, A, B, C,  1, 12, 0xE8C7B756 );
        !          10864:     P1( X, &C, D, A, B,  2, 17, 0x242070DB );
        !          10865:     P1( X, &B, C, D, A,  3, 22, 0xC1BDCEEE );
        !          10866:     P1( X, &A, B, C, D,  4,  7, 0xF57C0FAF );
        !          10867:     P1( X, &D, A, B, C,  5, 12, 0x4787C62A );
        !          10868:     P1( X, &C, D, A, B,  6, 17, 0xA8304613 );
        !          10869:     P1( X, &B, C, D, A,  7, 22, 0xFD469501 );
        !          10870:     P1( X, &A, B, C, D,  8,  7, 0x698098D8 );
        !          10871:     P1( X, &D, A, B, C,  9, 12, 0x8B44F7AF );
        !          10872:     P1( X, &C, D, A, B, 10, 17, 0xFFFF5BB1 );
        !          10873:     P1( X, &B, C, D, A, 11, 22, 0x895CD7BE );
        !          10874:     P1( X, &A, B, C, D, 12,  7, 0x6B901122 );
        !          10875:     P1( X, &D, A, B, C, 13, 12, 0xFD987193 );
        !          10876:     P1( X, &C, D, A, B, 14, 17, 0xA679438E );
        !          10877:     P1( X, &B, C, D, A, 15, 22, 0x49B40821 );
        !          10878:     P2( X, &A, B, C, D,  1,  5, 0xF61E2562 );
        !          10879:     P2( X, &D, A, B, C,  6,  9, 0xC040B340 );
        !          10880:     P2( X, &C, D, A, B, 11, 14, 0x265E5A51 );
        !          10881:     P2( X, &B, C, D, A,  0, 20, 0xE9B6C7AA );
        !          10882:     P2( X, &A, B, C, D,  5,  5, 0xD62F105D );
        !          10883:     P2( X, &D, A, B, C, 10,  9, 0x02441453 );
        !          10884:     P2( X, &C, D, A, B, 15, 14, 0xD8A1E681 );
        !          10885:     P2( X, &B, C, D, A,  4, 20, 0xE7D3FBC8 );
        !          10886:     P2( X, &A, B, C, D,  9,  5, 0x21E1CDE6 );
        !          10887:     P2( X, &D, A, B, C, 14,  9, 0xC33707D6 );
        !          10888:     P2( X, &C, D, A, B,  3, 14, 0xF4D50D87 );
        !          10889:     P2( X, &B, C, D, A,  8, 20, 0x455A14ED );
        !          10890:     P2( X, &A, B, C, D, 13,  5, 0xA9E3E905 );
        !          10891:     P2( X, &D, A, B, C,  2,  9, 0xFCEFA3F8 );
        !          10892:     P2( X, &C, D, A, B,  7, 14, 0x676F02D9 );
        !          10893:     P2( X, &B, C, D, A, 12, 20, 0x8D2A4C8A );
        !          10894:     P3( X, &A, B, C, D,  5,  4, 0xFFFA3942 );
        !          10895:     P3( X, &D, A, B, C,  8, 11, 0x8771F681 );
        !          10896:     P3( X, &C, D, A, B, 11, 16, 0x6D9D6122 );
        !          10897:     P3( X, &B, C, D, A, 14, 23, 0xFDE5380C );
        !          10898:     P3( X, &A, B, C, D,  1,  4, 0xA4BEEA44 );
        !          10899:     P3( X, &D, A, B, C,  4, 11, 0x4BDECFA9 );
        !          10900:     P3( X, &C, D, A, B,  7, 16, 0xF6BB4B60 );
        !          10901:     P3( X, &B, C, D, A, 10, 23, 0xBEBFBC70 );
        !          10902:     P3( X, &A, B, C, D, 13,  4, 0x289B7EC6 );
        !          10903:     P3( X, &D, A, B, C,  0, 11, 0xEAA127FA );
        !          10904:     P3( X, &C, D, A, B,  3, 16, 0xD4EF3085 );
        !          10905:     P3( X, &B, C, D, A,  6, 23, 0x04881D05 );
        !          10906:     P3( X, &A, B, C, D,  9,  4, 0xD9D4D039 );
        !          10907:     P3( X, &D, A, B, C, 12, 11, 0xE6DB99E5 );
        !          10908:     P3( X, &C, D, A, B, 15, 16, 0x1FA27CF8 );
        !          10909:     P3( X, &B, C, D, A,  2, 23, 0xC4AC5665 );
        !          10910:     P4( X, &A, B, C, D,  0,  6, 0xF4292244 );
        !          10911:     P4( X, &D, A, B, C,  7, 10, 0x432AFF97 );
        !          10912:     P4( X, &C, D, A, B, 14, 15, 0xAB9423A7 );
        !          10913:     P4( X, &B, C, D, A,  5, 21, 0xFC93A039 );
        !          10914:     P4( X, &A, B, C, D, 12,  6, 0x655B59C3 );
        !          10915:     P4( X, &D, A, B, C,  3, 10, 0x8F0CCC92 );
        !          10916:     P4( X, &C, D, A, B, 10, 15, 0xFFEFF47D );
        !          10917:     P4( X, &B, C, D, A,  1, 21, 0x85845DD1 );
        !          10918:     P4( X, &A, B, C, D,  8,  6, 0x6FA87E4F );
        !          10919:     P4( X, &D, A, B, C, 15, 10, 0xFE2CE6E0 );
        !          10920:     P4( X, &C, D, A, B,  6, 15, 0xA3014314 );
        !          10921:     P4( X, &B, C, D, A, 13, 21, 0x4E0811A1 );
        !          10922:     P4( X, &A, B, C, D,  4,  6, 0xF7537E82 );
        !          10923:     P4( X, &D, A, B, C, 11, 10, 0xBD3AF235 );
        !          10924:     P4( X, &C, D, A, B,  2, 15, 0x2AD7D2BB );
        !          10925:     P4( X, &B, C, D, A,  9, 21, 0xEB86D391 );
        !          10926:     ctx->state[0] += A;
        !          10927:     ctx->state[1] += B;
        !          10928:     ctx->state[2] += C;
        !          10929:     ctx->state[3] += D; }
        !          10930: 
        !          10931: void md5_update( md5_context *ctx, uint8 *input, uint32 length )
        !          10932:   { uint32 left, fill;
        !          10933:     if( length < 1 ) return;
        !          10934:     left = ctx->total[0] & 0x3F;
        !          10935:     fill = 64 - left;
        !          10936:     ctx->total[0] += length;
        !          10937:     ctx->total[0] &= 0xFFFFFFFF;
        !          10938:     if( ctx->total[0] < length )
        !          10939:         ctx->total[1]++;
        !          10940:     if( left && length >= fill )
        !          10941:       { memcpy( (void *) (ctx->buffer + left),
        !          10942:                 (void *) input, fill );
        !          10943:         md5_process( ctx, ctx->buffer );
        !          10944:         length -= fill;
        !          10945:         input  += fill;
        !          10946:         left = 0; }
        !          10947:     while( length >= 64 )
        !          10948:       { md5_process( ctx, input );
        !          10949:         length -= 64;
        !          10950:         input  += 64; }
        !          10951:     if( length >= 1 )
        !          10952:       memcpy( (void *) (ctx->buffer + left),
        !          10953:               (void *) input, length ); }
        !          10954: 
        !          10955: void md5_finish( md5_context *ctx, uint8 digest[16] )
        !          10956:   { static uint8 md5_padding[64] =
        !          10957:      { 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        !          10958:           0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        !          10959:           0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        !          10960:           0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
        !          10961:     uint32 last, padn;
        !          10962:     uint32 high, low;
        !          10963:     uint8 msglen[8];
        !          10964:     high = ( ctx->total[0] >> 29 )
        !          10965:          | ( ctx->total[1] <<  3 );
        !          10966:     low  = ( ctx->total[0] <<  3 );
        !          10967:     PUT_UINT32( low,  msglen, 0 );
        !          10968:     PUT_UINT32( high, msglen, 4 );
        !          10969:     last = ctx->total[0] & 0x3F;
        !          10970:     padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last );
        !          10971:     md5_update( ctx, md5_padding, padn );
        !          10972:     md5_update( ctx, msglen, 8 );
        !          10973:     PUT_UINT32( ctx->state[0], digest,  0 );
        !          10974:     PUT_UINT32( ctx->state[1], digest,  4 );
        !          10975:     PUT_UINT32( ctx->state[2], digest,  8 );
        !          10976:     PUT_UINT32( ctx->state[3], digest, 12 ); }
        !          10977: /* --- end-of-function md5str() and "friends" --- */
        !          10978: 
        !          10979: #if defined(GIF)
        !          10980: /* ==========================================================================
        !          10981:  * Function:	GetPixel ( int x, int y )
        !          10982:  * Purpose:	callback for GIF_CompressImage() returning the
        !          10983:  *		pixel at column x, row y
        !          10984:  * --------------------------------------------------------------------------
        !          10985:  * Arguments:	x (I)		int containing column=0...width-1
        !          10986:  *				of desired pixel
        !          10987:  *		y (I)		int containing row=0...height-1
        !          10988:  *				of desired pixel
        !          10989:  * --------------------------------------------------------------------------
        !          10990:  * Returns:	( int )		0 or 1, if pixel at x,y is off or on
        !          10991:  * --------------------------------------------------------------------------
        !          10992:  * Notes:     o
        !          10993:  * ======================================================================= */
        !          10994: /* --- entry point --- */
        !          10995: int	GetPixel ( int x, int y )
        !          10996: {
        !          10997: int	ipixel = y*bitmap_raster->width + x; /* pixel index for x,y-coords*/
        !          10998: int	pixval =0;			/* value of pixel */
        !          10999: if ( !isaa )				/* use bitmap if not anti-aliased */
        !          11000:   pixval = (int)getlongbit(bitmap_raster->pixmap,ipixel); /*pixel = 0 or 1*/
        !          11001: else					/* else use anti-aliased grayscale*/
        !          11002:   pixval = (int)(colormap_raster[ipixel]); /* colors[] index number */
        !          11003: if ( msgfp!=NULL && msglevel>=9999 )	/* dump pixel */
        !          11004:   { fprintf(msgfp,"GetPixel> x=%d, y=%d  pixel=%d\n",x,y,pixval);
        !          11005:     fflush(msgfp); }
        !          11006: return pixval;
        !          11007: } /* --- end-of-function GetPixel() --- */
        !          11008: #endif /* gif */
        !          11009: #endif /* driver */
        !          11010: #endif /* PART1 */
        !          11011: /* ======================= END-OF-FILE MIMETEX.C ========================= */
        !          11012: 

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