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

1.1       albertel    1: /****************************************************************************
                      2:  *
1.2       albertel    3:  * Copyright(c) 2002-2006, John Forkosh Associates, Inc. All rights reserved.
1.1       albertel    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.
1.3     ! albertel   39:  *		But image caching is available by using mimeTeX's
        !            40:  *		-DCACHEPATH=\"path/\" compile option (see below).
1.1       albertel   41:  *		There's also no inherent need to repeatedly write the
                     42:  *		cumbersome <img> tag illustrated above.  You can write
                     43:  *		your own custom tags, or write a wrapper script around
1.3     ! albertel   44:  *		mimeTeX to simplify the notation.
1.1       albertel   45:  *
                     46:  * Functions:	===================== Raster Functions ======================
                     47:  *	PART2	--- raster constructor functions ---
                     48:  *		new_raster(width,height,pixsz)   allocation (and constructor)
                     49:  *		new_subraster(width,height,pixsz)allocation (and constructor)
                     50:  *		new_chardef()                         allocate chardef struct
                     51:  *		delete_raster(rp)        deallocate raster (rp =  raster ptr)
                     52:  *		delete_subraster(sp)  deallocate subraster (sp=subraster ptr)
                     53:  *		delete_chardef(cp)      deallocate chardef (cp = chardef ptr)
                     54:  *		--- primitive (sub)raster functions ---
                     55:  *		rastcpy(rp)                           allocate new copy of rp
                     56:  *		subrastcpy(sp)                        allocate new copy of sp
                     57:  *		rastrot(rp)         new raster rotated right 90 degrees to rp
1.3     ! albertel   58:  *		rastref(rp,axis)    new raster reflected (axis 1=horz,2=vert)
1.1       albertel   59:  *		rastput(target,source,top,left,isopaque)  overlay src on trgt
                     60:  *		rastcompose(sp1,sp2,offset2,isalign,isfree) sp2 on top of sp1
                     61:  *		rastcat(sp1,sp2,isfree)                  concatanate sp1||sp2
                     62:  *		rastack(sp1,sp2,base,space,iscenter,isfree)stack sp2 atop sp1
                     63:  *		rastile(tiles,ntiles)      create composite raster from tiles
1.2       albertel   64:  *		rastsmash(sp1,sp2,xmin,ymin)      calc #smash pixels sp1||sp2
1.3     ! albertel   65:  *		rastsmashcheck(term)         check if term is "safe" to smash
1.1       albertel   66:  *		--- raster "drawing" functions ---
                     67:  *		accent_subraster(accent,width,height)       draw \hat\vec\etc
                     68:  *		arrow_subraster(width,height,drctn,isBig)    left/right arrow
                     69:  *		uparrow_subraster(width,height,drctn,isBig)     up/down arrow
                     70:  *		rule_raster(rp,top,left,width,height,type)    draw rule in rp
                     71:  *		line_raster(rp,row0,col0,row1,col1,thickness) draw line in rp
                     72:  *		line_recurse(rp,row0,col0,row1,col1,thickness)   recurse line
                     73:  *		circle_raster(rp,row0,col0,row1,col1,thickness,quads) ellipse
                     74:  *		circle_recurse(rp,row0,col0,row1,col1,thickness,theta0,theta1)
                     75:  *		bezier_raster(rp,r0,c0,r1,c1,rt,ct)   draw bezier recursively
                     76:  *		border_raster(rp,ntop,nbot,isline,isfree)put border around rp
1.3     ! albertel   77:  *		backspace_raster(rp,nback,pback,minspace,isfree)    neg space
1.1       albertel   78:  *		--- raster (and chardef) output functions ---
                     79:  *		type_raster(rp,fp)       emit ascii dump of rp on file ptr fp
                     80:  *		type_bytemap(bp,grayscale,width,height,fp) dump bytemap on fp
                     81:  *		xbitmap_raster(rp,fp)           emit mime xbitmap of rp on fp
1.2       albertel   82:  *		type_pbmpgm(rp,ptype,file)     pbm or pgm image of rp to file
1.1       albertel   83:  *		cstruct_chardef(cp,fp,col1)         emit C struct of cp on fp
                     84:  *		cstruct_raster(rp,fp,col1)          emit C struct of rp on fp
                     85:  *		hex_bitmap(rp,fp,col1,isstr)emit hex dump of rp->pixmap on fp
                     86:  *		--- ancillary output functions ---
                     87:  *		emit_string(fp,col1,string,comment) emit string and C comment
1.2       albertel   88:  *		gftobitmap(rp)        convert .gf-like pixmap to bitmap image
1.1       albertel   89:  *		====================== Font Functions =======================
                     90:  *		--- font lookup functions ---
1.3     ! albertel   91:  *		get_symdef(symbol)              return mathchardef for symbol
        !            92:  *		get_ligature(expr,family)  return symtable index for ligature
        !            93:  *		get_chardef(symdef,size)       return chardef for symdef,size
1.1       albertel   94:  *		get_charsubraster(symdef,size)  wrap subraster around chardef
1.2       albertel   95:  *		get_symsubraster(symbol,size)    returns subraster for symbol
1.1       albertel   96:  *		--- ancillary font functions ---
                     97:  *		get_baseline(gfdata)       determine baseline (in our coords)
                     98:  *		get_delim(symbol,height,family) delim just larger than height
                     99:  *		make_delim(symbol,height) construct delim exactly height size
                    100:  *		================= Tokenize/Parse Functions ==================
                    101:  *		texchar(expression,chartoken)  retruns next char or \sequence
                    102:  *		texsubexpr(expr,subexpr,maxsubsz,left,right,isescape,isdelim)
1.2       albertel  103:  *		texleft(expr,subexpr,maxsubsz,ldelim,rdelim)   \left...\right
1.1       albertel  104:  *		texscripts(expression,subscript,superscript,which)get scripts
                    105:  *		--- ancillary parse functions ---
                    106:  *		isbrace(expression,braces,isescape)   check for leading brace
                    107:  *		preamble(expression,size,subexpr)              parse preamble
                    108:  *		mimeprep(expression) preprocessor converts \left( to \(, etc.
                    109:  *		strchange(nfirst,from,to)   change nfirst chars of from to to
                    110:  *		strreplace(string,from,to,nreplace)  change from to to in str
1.3     ! albertel  111:  *		strwstr(string,substr,white,sublen)     find substr in string
1.1       albertel  112:  *		strtexchr(string,texchr)                find texchr in string
                    113:  *		findbraces(expression,command)    find opening { or closing }
                    114:  *	PART3	=========== Rasterize an Expression (recursively) ===========
                    115:  *		--- here's the primary entry point for all of mimeTeX ---
                    116:  *		rasterize(expression,size)     parse and rasterize expression
                    117:  *		--- explicitly called handlers that rasterize... ---
                    118:  *		rastparen(subexpr,size,basesp)          parenthesized subexpr
                    119:  *		rastlimits(expression,size,basesp)    dispatch super/sub call
                    120:  *		rastscripts(expression,size,basesp) super/subscripted exprssn
                    121:  *		rastdispmath(expression,size,sp)      scripts for displaymath
                    122:  *		--- table-driven handlers that rasterize... ---
                    123:  *		rastleft(expression,size,basesp,ildelim,arg2,arg3)\left\right
1.2       albertel  124:  *		rastright(expression,size,basesp,ildelim,arg2,arg3) ...\right
                    125:  *		rastmiddle(expression,size,basesp,arg1,arg2,arg3)     \middle
1.1       albertel  126:  *		rastflags(expression,size,basesp,flag,value,arg3)    set flag
                    127:  *		rastspace(expression,size,basesp,width,isfill,isheight)\,\:\;
                    128:  *		rastnewline(expression,size,basesp,arg1,arg2,arg3)         \\
                    129:  *		rastarrow(expression,size,basesp,width,height,drctn) \longarr
                    130:  *		rastuparrow(expression,size,basesp,width,height,drctn)up/down
                    131:  *		rastoverlay(expression,size,basesp,overlay,arg2,arg3)    \not
                    132:  *		rastfrac(expression,size,basesp,isfrac,arg2,arg3) \frac \atop
                    133:  *		rastackrel(expression,size,basesp,base,arg2,arg3)   \stackrel
                    134:  *		rastmathfunc(expression,size,basesp,base,arg2,arg3) \lim,\etc
                    135:  *		rastsqrt(expression,size,basesp,arg1,arg2,arg3)         \sqrt
                    136:  *		rastaccent(expression,size,basesp,accent,isabove,isscript)
                    137:  *		rastfont(expression,size,basesp,font,arg2,arg3) \cal{},\scr{}
                    138:  *		rastbegin(expression,size,basesp,arg1,arg2,arg3)     \begin{}
                    139:  *		rastarray(expression,size,basesp,arg1,arg2,arg3)       \array
                    140:  *		rastpicture(expression,size,basesp,arg1,arg2,arg3)   \picture
                    141:  *		rastline(expression,size,basesp,arg1,arg2,arg3)         \line
1.3     ! albertel  142:  *		rastrule(expression,size,basesp,arg1,arg2,arg3)         \rule
1.1       albertel  143:  *		rastcircle(expression,size,basesp,arg1,arg2,arg3)     \circle
                    144:  *		rastbezier(expression,size,basesp,arg1,arg2,arg3)     \bezier
                    145:  *		rastraise(expression,size,basesp,arg1,arg2,arg3)    \raisebox
                    146:  *		rastrotate(expression,size,basesp,arg1,arg2,arg3)  \rotatebox
1.3     ! albertel  147:  *		rastreflect(expression,size,basesp,arg1,arg2,arg3)\reflectbox
1.1       albertel  148:  *		rastfbox(expression,size,basesp,arg1,arg2,arg3)         \fbox
                    149:  *		rastinput(expression,size,basesp,arg1,arg2,arg3)       \input
                    150:  *		rastcounter(expression,size,basesp,arg1,arg2,arg3)   \counter
1.2       albertel  151:  *		rasttoday(expression,size,basesp,arg1,arg2,arg3)       \today
                    152:  *		rastcalendar(expression,size,basesp,arg1,arg2,arg3) \calendar
1.1       albertel  153:  *		rastnoop(expression,size,basesp,arg1,arg2,arg3) flush \escape
                    154:  *		--- helper functions for handlers ---
                    155:  *		rastopenfile(filename,mode)      opens filename[.tex] in mode
1.2       albertel  156:  *		rasteditfilename(filename)       edit filename (for security)
                    157:  *		rastreadfile(filename,islock,tag,value)   read <tag>...</tag>
1.1       albertel  158:  *		rastwritefile(filename,tag,value,isstrict)write<tag>...</tag>
1.2       albertel  159:  *		calendar(year,month,day)    formats one-month calendar string
                    160:  *		timestamp(tzdelta,ifmt)              formats timestamp string
                    161:  *		tzadjust(tzdelta,year,month,day,hour)        adjust date/time
                    162:  *		daynumber(year,month,day)     #days since Monday, Jan 1, 1973
                    163:  *		dbltoa(d,npts)                double to comma-separated ascii
1.1       albertel  164:  *		=== Anti-alias completed raster (lowpass) or symbols (ss) ===
                    165:  *		aalowpass(rp,bytemap,grayscale)     lowpass grayscale bytemap
                    166:  *		aapnm(rp,bytemap,grayscale)       lowpass based on pnmalias.c
1.3     ! albertel  167:  *		aapnmlookup(rp,bytemap,grayscale)  aapnm based on aagridnum()
        !           168:  *		aapatterns(rp,irow,icol,gridnum,patternum,grayscale) call 19,
        !           169:  *		aapattern1124(rp,irow,icol,gridnum,grayscale)antialias pattrn
        !           170:  *		aapattern19(rp,irow,icol,gridnum,grayscale) antialias pattern
        !           171:  *		aapattern20(rp,irow,icol,gridnum,grayscale) antialias pattern
        !           172:  *		aapattern39(rp,irow,icol,gridnum,grayscale) antialias pattern
        !           173:  *		aafollowline(rp,irow,icol,direction)       looks for a "turn"
        !           174:  *		aagridnum(rp,irow,icol)             calculates gridnum, 0-511
        !           175:  *		aapatternnum(gridnum)    looks up pattern#, 1-51, for gridnum
        !           176:  *		aalookup(gridnum)     table lookup for all possible 3x3 grids
        !           177:  *		aalowpasslookup(rp,bytemap,grayscale)   driver for aalookup()
1.1       albertel  178:  *		aasupsamp(rp,aa,sf,grayscale)             or by supersampling
                    179:  *		aacolormap(bytemap,nbytes,colors,colormap)make colors,colormap
                    180:  *		aaweights(width,height)      builds "canonical" weight matrix
                    181:  *		aawtpixel(image,ipixel,weights,rotate) weight image at ipixel
1.3     ! albertel  182:  *		=== miscellaneous ===
        !           183:  *		mimetexsetmsg(newmsglevel,newmsgfp)    set msglevel and msgfp
1.1       albertel  184:  *	PART1	========================== Driver ===========================
                    185:  *		main(argc,argv) parses math expression and emits mime xbitmap
1.2       albertel  186:  *		CreateGifFromEq(expression,gifFileName)  entry pt for win dll
1.1       albertel  187:  *		isstrstr(string,snippets,iscase)  are any snippets in string?
                    188:  *		ismonth(month)          is month current month ("jan"-"dec")?
                    189:  *		unescape_url(url,isescape), x2c(what)   xlate %xx url-encoded
                    190:  *		logger(fp,msglevel,logvars)        logs environment variables
1.2       albertel  191:  *		emitcache(cachefile,maxage,isbuffer) emit cachefile to stdout
                    192:  *		readcachefile(cachefile,buffer)    read cachefile into buffer
1.1       albertel  193:  *		md5str(instr)                      md5 hash library functions
                    194:  *		GetPixel(x,y)           callback function for gifsave library
                    195:  *
                    196:  * Source:	mimetex.c  (needs mimetex.h and texfonts.h to compile,
                    197:  *		and also needs gifsave.c if compiled with -DAA or -DGIF)
                    198:  *
                    199:  * --------------------------------------------------------------------------
                    200:  * Notes      o	See bottom of file for main() driver (and "friends"),
                    201:  *		and compile as
                    202:  *		   cc -DAA mimetex.c gifsave.c -lm -o mimetex.cgi
                    203:  *		to produce an executable that emits gif images with
                    204:  *		anti-aliasing (see Notes below).  You may also compile
                    205:  *		   cc -DGIF mimetex.c gifsave.c -lm -o mimetex.cgi
                    206:  *		to produce an executable that emits gif images without
                    207:  *		anti-aliasing.  Alternatively, compile mimeTeX as
                    208:  *		   cc -DXBITMAP mimetex.c -lm -o mimetex.cgi
                    209:  *		to produce an executable that just emits mime xbitmaps.
                    210:  *		In either case you'll need mimetex.h and texfonts.h,
                    211:  *		and with -DAA or -DGIF you'll also need gifsave.c
                    212:  *	      o	For gif images, the gifsave.c library by Sverre H. Huseby
                    213:  *		<http://shh.thathost.com> slightly modified by me to allow
                    214:  *		(a)sending output to stdout and (b)specifying a transparent
                    215:  *		background color index, is included with mimeTeX,
                    216:  *		and it's documented in mimetex.html#gifsave .
                    217:  *	      o	Optional compile-line -D defined symbols are documented
1.3     ! albertel  218:  *		in mimetex.html#options .  They include (additional -D
        !           219:  *		switches are discussed in mimetex.html#options)...
1.1       albertel  220:  *		-DAA
                    221:  *		    Turns on gif anti-aliasing with default values
                    222:  *		    (CENTERWT=32, ADJACENTWT=3, CORNERWT=1)
                    223:  *		    for the following anti-aliasing parameters...
                    224:  *		-DCENTERWT=n
                    225:  *		-DADJACENTWT=j
                    226:  *		-DCORNERWT=k
                    227:  *		    MimeTeX currently provides a lowpass filtering
                    228:  *		    algorithm for anti-aliasing, which is applied to the
                    229:  *		    existing set of bitmap fonts.  This lowpass filter
                    230:  *		    applies default weights
1.3     ! albertel  231:  *				1   2   1
        !           232:  *				2   8   2
        !           233:  *				1   2   1
1.1       albertel  234:  *		    to neighboring pixels. The defaults weights are
1.3     ! albertel  235:  *		    CENTERWT=8, ADJACENTWT=2 and CORNERWT=1,
1.1       albertel  236:  *		    which you can adjust to control anti-aliasing.
                    237:  *		    Lower CENTERWT values will blur/spread out lines
                    238:  *		    while higher values will tend to sharpen lines.
                    239:  *		    Experimentation is recommended to determine
                    240:  *		    what value works best for you.
                    241:  *		-DCACHEPATH=\"path/\"
                    242:  *		    This option saves each rendered image to a file
                    243:  *		    in directory  path/  which mimeTeX reads rather than
                    244:  *		    re-rendering the same image every time it's given
                    245:  *		    the same LaTeX expression.  Sometimes mimeTeX disables
                    246:  *		    caching, e.g., expressions containing \input{ } are
                    247:  *		    re-rendered since the contents of the inputted file
                    248:  *		    may have changed.  If compiled without -DCACHEPATH
                    249:  *		    mimeTeX always re-renders expressions.  This usually
                    250:  *		    isn't too cpu intensive, but if you have unusually
                    251:  *		    high hit rates then image caching may be helpful.
                    252:  *			The  path/  is relative to mimetex.cgi, and must
                    253:  *		    be writable by it.  Files created under  path/  are
                    254:  *		    named filename.gif, where filename is the 32-character
                    255:  *		    MD5 hash of the LaTeX expression.
                    256:  *		-DDISPLAYSIZE=n
                    257:  *		    By default, operator limits like \int_a^b are rendered
                    258:  *		    \textstyle at font sizes \normalsize and smaller,
                    259:  *		    and rendered \displaystyle at font sizes \large and
                    260:  *		    larger.  This default corresponds to -DDISPLAYSIZE=3,
                    261:  *		    which you can adjust; e.g., -DDISPLAYSIZE=0 always
                    262:  *		    defaults to \displaystyle, and 99 (or any large number)
                    263:  *		    always defaults to \textstyle.  Note that explicit
                    264:  *		    \textstyle, \displaystyle, \limits or \nolimits
                    265:  *		    directives in an expression always override
                    266:  *		    the DISPLAYSIZE default.
                    267:  *		-NORMALSIZE=n
                    268:  *		    MimeTeX currently has six font sizes numbered 0-5,
                    269:  *		    and always starts in NORMALSIZE whose default value
                    270:  *		    is 2.  Specify -DNORMALSIZE=3 on the compile line if
                    271:  *		    you prefer mimeTeX to start in default size 3, etc.
                    272:  *		-DREFERER=\"domain\"   -or-
                    273:  *		-DREFERER=\"domain1,domain2,etc\"
                    274:  *		    Blocks mimeTeX requests from unauthorized domains that
                    275:  *		    may be using your server's mimetex.cgi without permission.
                    276:  *		    If REFERER is defined, mimeTeX checks for the environment
                    277:  *		    variable HTTP_REFERER and, if it exists, performs a
                    278:  *		    case-insensitive test to make sure it contains 'domain'
                    279:  *		    as a substring.  If given several 'domain's (second form)
                    280:  *		    then HTTP_REFERER must contain either 'domain1' or
                    281:  *		    'domain2', etc, as a (case-insensitive) substring.
                    282:  *		    If HTTP_REFERER fails to contain a substring matching
                    283:  *		    any of these domain(s), mimeTeX emits an error message
                    284:  *		    image corresponding to the expression specified by
                    285:  *		    the  invalid_referer_msg  string defined in main().
                    286:  *		    Note: if HTTP_REFERER is not an environment variable,
                    287:  *		    mimeTeX correctly generates the requested expression
                    288:  *		    (i.e., no referer error).
                    289:  *		-DWARNINGS=n  -or-
                    290:  *		-DNOWARNINGS
                    291:  *		    If an expression submitted to mimeTeX contains an
                    292:  *		    unrecognzied escape sequence, e.g., "y=x+\abc+1", then
                    293:  *		    mimeTeX generates a gif image containing an embedded
                    294:  *		    warning in the form "y=x+[\abc?]+1".  If you want these
                    295:  *		    warnings suppressed, -DWARNINGS=0 or -DNOWARNINGS tells
                    296:  *		    mimeTeX to ignore unrecognized symbols, and the rendered
                    297:  *		    image is "y=x++1" instead.
                    298:  *		-DWHITE
                    299:  *		    MimeTeX usually renders black symbols on a white
                    300:  *		    background.  This option renders white symbols on
                    301:  *		    a black background instead.
                    302:  *	      o	See individual function entry points for further comments.
                    303:  *	      o	The font information in texfonts.h was produced by multiple
                    304:  *		runs of gfuntype, one run per struct (i.e., one run per font
                    305:  *		family at a particular size).  See gfuntype.c, and also
                    306:  *		mimetex.html#fonts, for details.
                    307:  *	      o	mimetex.c contains library functions implementing a raster
                    308:  *		datatype, functions to manipulate rasterized .mf fonts
                    309:  *		(see gfuntype.c which rasterizes .mf fonts), functions
                    310:  *		to parse LaTeX expressions, etc.  A complete list of
                    311:  *		mimetex.c functions is above.  See their individual entry
                    312:  *		points below for further comments.
                    313:  *		   All these functions eventually belong in several
                    314:  *		different modules, possibly along the lines suggested
                    315:  *		by the divisions above.  But until the best decomposition
                    316:  *		becomes clear, it seems better to keep mimetex.c
                    317:  *		neatly together, avoiding a bad decomposition that
                    318:  *		becomes permanent by default.
                    319:  *	      o	The "main" reusable function is rasterize(),
                    320:  *		which takes a string like "f(x)=\int_{-\infty}^xe^{-t^2}dt"
                    321:  *		and returns a (sub)raster representing it as a bit or bytemap.
                    322:  *		Your application can do anything it likes with this pixel map.
                    323:  *		MimeTeX just outputs it, either as a mime xbitmap or as a gif.
                    324:  * --------------------------------------------------------------------------
                    325:  * Revision History:
                    326:  * 09/18/02	J.Forkosh	Installation.
                    327:  * 12/11/02	J.Forkosh	Version 1.00 released.
                    328:  * 07/04/03	J.Forkosh	Version 1.01 released.
                    329:  * 10/17/03	J.Forkosh	Version 1.20 released.
                    330:  * 12/21/03	J.Forkosh	Version 1.30 released.
                    331:  * 02/01/04	J.Forkosh	Version 1.40 released.
                    332:  * 10/02/04	J.Forkosh	Version 1.50 released.
                    333:  * 11/30/04	J.Forkosh	Version 1.60 released.
1.3     ! albertel  334:  * 10/11/05	J.Forkosh	Version 1.64 released.
        !           335:  * 11/30/06	J.Forkosh	most recent changes
1.1       albertel  336:  *
                    337:  ****************************************************************************/
                    338: 
                    339: /* -------------------------------------------------------------------------
                    340: header files and macros
                    341: -------------------------------------------------------------------------- */
                    342: /* --- standard headers --- */
                    343: #include <stdio.h>
                    344: #include <stdlib.h>
                    345: /*#include <unistd.h>*/
                    346: #include <string.h>
                    347: #include <ctype.h>
                    348: #include <math.h>
                    349: #include <time.h>
                    350: 
                    351: /* --- windows-specific header info --- */
                    352: #ifndef WINDOWS			/* -DWINDOWS not supplied by user */
1.2       albertel  353:   #if defined(_WINDOWS) || defined(_WIN32) || defined(WIN32) \
                    354:   ||  defined(DJGPP)		/* try to recognize windows compilers */ \
                    355:   ||  defined(_USRDLL)		/* must be WINDOWS if compiling for DLL */
1.1       albertel  356:     #define WINDOWS		/* signal windows */
                    357:   #endif
                    358: #endif
                    359: #ifdef WINDOWS			/* Windows opens stdout in char mode, and */
                    360:   #include <fcntl.h>		/* precedes every 0x0A with spurious 0x0D.*/
                    361:   #include <io.h>		/* So emitcache() issues a Win _setmode() */
                    362: 				/* call to put stdout in binary mode. */
                    363:   #if defined(_O_BINARY) && !defined(O_BINARY)  /* only have _O_BINARY */
                    364:     #define O_BINARY _O_BINARY	/* make O_BINARY available, etc... */
                    365:     #define setmode  _setmode
                    366:     #define fileno   _fileno
                    367:   #endif
                    368:   #if defined(_O_BINARY) || defined(O_BINARY)  /* setmode() now available */
                    369:     #define HAVE_SETMODE	/* so we'll use setmode() */
                    370:   #endif
1.2       albertel  371:   #if defined(_MSC_VER) && defined(_DEBUG) /* MS VC++ in debug mode */
                    372:     /* to show source file and line numbers where memory leaks occur... */
                    373:     #define _CRTDBG_MAP_ALLOC	/* ...include this debug macro */
                    374:     #include <crtdbg.h>		/* and this debug library */
                    375:   #endif
1.1       albertel  376:   #define ISWINDOWS 1
                    377: #else
                    378:   #define ISWINDOWS 0
                    379: #endif
                    380: 
                    381: /* --- check for supersampling or low-pass anti-aliasing --- */
                    382: #ifdef SS
                    383:   #define ISSUPERSAMPLING 1
                    384:   #ifndef AAALGORITHM
                    385:     #define AAALGORITHM 1		/* default supersampling algorithm */
                    386:   #endif
                    387:   #ifndef AA				/* anti-aliasing not explicitly set */
                    388:     #define AA				/* so define it ourselves */
                    389:   #endif
                    390:   #ifndef SSFONTS			/* need supersampling fonts */
                    391:     #define SSFONTS
                    392:   #endif
                    393: #else
                    394:   #define ISSUPERSAMPLING 0
                    395:   #ifndef AAALGORITHM
1.3     ! albertel  396:     #define AAALGORITHM 3 /*2*/		/* default lowpass algorithm */
1.1       albertel  397:   #endif
                    398: #endif
1.3     ! albertel  399: #ifndef MAXFOLLOW
        !           400:   #define MAXFOLLOW 8			/* aafollowline() maxturn default */
        !           401: #endif
1.1       albertel  402: 
                    403: /* --- set aa (and default gif) if any anti-aliasing options specified --- */
                    404: #if defined(AA) || defined(GIF) || defined(PNG) \
                    405: ||  defined(CENTERWT) || defined(ADJACENTWT) || defined(CORNERWT) \
                    406: ||  defined(MINADJACENT) || defined(MAXADJACENT)
                    407:   #if !defined(GIF) && !defined(AA)	/* aa not explicitly specified */
                    408:     #define AA				/* so define it ourselves */
                    409:   #endif
                    410:   #if !defined(GIF) && !defined(PNG)	/* neither gif nor png specified */
                    411:     #define GIF				/* so default to gif */
                    412:   #endif
                    413: #endif
                    414: /* --- resolve output option inconsistencies --- */
                    415: #if defined(XBITMAP)			/* xbitmap supercedes gif and png */
                    416:   #ifdef AA
                    417:     #undef AA
                    418:   #endif
                    419:   #ifdef GIF
                    420:     #undef GIF
                    421:   #endif
                    422:   #ifdef PNG
                    423:     #undef PNG
                    424:   #endif
                    425: #endif
                    426: 
                    427: /* --- decide whether to compile main() --- */
                    428: #if defined(XBITMAP) || defined(GIF) || defined(PNG)
                    429:   #define DRIVER			/* driver will be compiled */
                    430:   /* --- check whether or not to perform http_referer check --- */
                    431:   #ifndef REFERER			/* all http_referer's allowed */
                    432:     #define REFERER NULL
                    433:   #endif
                    434:   /* --- max query_string length if no http_referer supplied --- */
                    435:   #ifndef NOREFMAXLEN
                    436:     #define NOREFMAXLEN 9999		/* default to any length query */
                    437:   #endif
                    438: #else
1.3     ! albertel  439:   #ifndef TEXFONTS
        !           440:     #define NOTEXFONTS			/* texfonts not required */
        !           441:   #endif
1.1       albertel  442: #endif
                    443: 
                    444: /* --- application headers --- */
                    445: #if !defined(NOTEXFONTS) && !defined(TEXFONTS)
                    446:   #define TEXFONTS			/* to include texfonts.h */
                    447: #endif
                    448: #include "mimetex.h"
1.2       albertel  449: /* --- info needed when gif image returned in memory buffer --- */
                    450: #ifdef GIF				/* compiling along with gifsave.c */
                    451:   extern int gifSize;
                    452:   extern int maxgifSize;
                    453: #else					/* or just set dummy values */
                    454:   static int gifSize=0, maxgifSize=0;
                    455: #endif
1.3     ! albertel  456: /* --- gamma correction --- */
        !           457: #ifndef GAMMA
        !           458:   #define GAMMA 1.25 /*1.75*/ /*2.2*/
        !           459: #endif
        !           460: #ifndef REVERSEGAMMA
        !           461:   #define REVERSEGAMMA 0.5		/* for \reverse white-on-black */
        !           462: #endif
        !           463: /* --- opaque background (default to transparent) --- */
        !           464: #ifndef OPAQUE
        !           465:   #define ISTRANSPARENT 1
        !           466: #else
        !           467:   #define ISTRANSPARENT 0
        !           468: #endif
        !           469: /* --- internal buffer sizes --- */
        !           470: #if !defined(MAXEXPRSZ)
        !           471:   #define MAXEXPRSZ (32768-1)		/*max #bytes in input tex expression*/
        !           472: #endif
        !           473: #if !defined(MAXSUBXSZ)
        !           474:   #define MAXSUBXSZ (((MAXEXPRSZ+1)/2)-1)/*max #bytes in input subexpression*/
        !           475: #endif
        !           476: #if !defined(MAXTOKNSZ)
        !           477:   #define MAXTOKNSZ (((MAXSUBXSZ+1)/4)-1) /* max #bytes in input token */
        !           478: #endif
        !           479: #if !defined(MAXFILESZ)
        !           480:   #define MAXFILESZ (65536-1)		/*max #bytes in input (output) file*/
        !           481: #endif
        !           482: #if !defined(MAXLINESZ)
        !           483:   #define MAXLINESZ (4096-1)		/* max #chars in line from file */
        !           484: #endif
        !           485: #if !defined(MAXGIFSZ)
        !           486:   #define MAXGIFSZ 131072		/* max #bytes in output GIF image */
        !           487: #endif
1.1       albertel  488: 
                    489: /* -------------------------------------------------------------------------
                    490: adjustable default values
                    491: -------------------------------------------------------------------------- */
                    492: /* --- anti-aliasing parameters --- */
                    493: #ifndef	CENTERWT
1.2       albertel  494:   /*#define CENTERWT 32*/		/* anti-aliasing centerwt default */
1.1       albertel  495:   /*#define CENTERWT 10*/		/* anti-aliasing centerwt default */
1.2       albertel  496:   #define CENTERWT 8			/* anti-aliasing centerwt default */
1.1       albertel  497: #endif
                    498: #ifndef	ADJACENTWT
                    499:   /*#define ADJACENTWT 3*/		/* anti-aliasing adjacentwt default*/
1.2       albertel  500:   #define ADJACENTWT 2			/* anti-aliasing adjacentwt default*/
1.1       albertel  501: #endif
                    502: #ifndef	CORNERWT
                    503:   #define CORNERWT 1			/* anti-aliasing cornerwt default*/
                    504: #endif
                    505: #ifndef	MINADJACENT
                    506:   #define MINADJACENT 6			/*anti-aliasing minadjacent default*/
                    507: #endif
                    508: #ifndef	MAXADJACENT
                    509:   #define MAXADJACENT 8			/*anti-aliasing maxadjacent default*/
                    510: #endif
                    511: /* --- variables for anti-aliasing parameters --- */
                    512: GLOBAL(int,centerwt,CENTERWT);		/*lowpass matrix center pixel wt */
                    513: GLOBAL(int,adjacentwt,ADJACENTWT);	/*lowpass matrix adjacent pixel wt*/
                    514: GLOBAL(int,cornerwt,CORNERWT);		/*lowpass matrix corner pixel wt */
                    515: GLOBAL(int,minadjacent,MINADJACENT);	/* darken if>=adjacent pts black*/
                    516: GLOBAL(int,maxadjacent,MAXADJACENT);	/* darken if<=adjacent pts black */
                    517: GLOBAL(int,weightnum,1);		/* font wt, */
                    518: GLOBAL(int,maxaaparams,4);		/* #entries in table */
1.3     ! albertel  519: /* --- anti-aliasing parameter values by font weight --- */
1.1       albertel  520: #define	aaparameters struct aaparameters_struct /* typedef */
                    521: aaparameters
                    522:   { int	centerwt;			/* lowpass matrix center   pixel wt*/
                    523:     int	adjacentwt;			/* lowpass matrix adjacent pixel wt*/
                    524:     int cornerwt;			/* lowpass matrix corner   pixel wt*/
                    525:     int	minadjacent;			/* darken if >= adjacent pts black */
                    526:     int	maxadjacent;			/* darken if <= adjacent pts black */
                    527:     int fgalias,fgonly,bgalias,bgonly; } ; /* aapnm() params */
                    528: STATIC aaparameters aaparams[]		/* set params by weight */
                    529:   #ifdef INITVALS
                    530:   =
                    531:   { /* ----------------------------------------------------
                    532:     centerwt adj corner minadj max  fgalias,only,bgalias,only
                    533:     ------------------------------------------------------- */
                    534: 	{ 64,  1,  1,    6,  8,     1,0,0,0 },	/* 0 = light */
                    535: 	{ CENTERWT,ADJACENTWT,CORNERWT,MINADJACENT,MAXADJACENT,1,0,0,0 },
                    536: 	{ 8,   1,  1,    5,  8,     1,0,0,0 },	/* 2 = semibold */
                    537: 	{ 8,   2,  1,    4,  9,     1,0,0,0 }	/* 3 = bold */
                    538:   } /* --- end-of-aaparams[] --- */
                    539:   #endif
                    540:   ;
1.3     ! albertel  541: /* --- anti-aliasing diagnostics (to help improve algorithm) --- */
        !           542: STATIC int patternnumcount0[99], patternnumcount1[99], /*aalookup() counts*/
        !           543: 	ispatternnumcount = 1;		/* true to accumulate counts */
1.1       albertel  544: 
                    545: /* -------------------------------------------------------------------------
                    546: other variables
                    547: -------------------------------------------------------------------------- */
                    548: /* --- black on white background (default), or white on black --- */
                    549: #ifdef WHITE
                    550:   #define ISBLACKONWHITE 0		/* white on black background */
                    551: #else
                    552:   #define ISBLACKONWHITE 1		/* black on white background */
                    553: #endif
                    554: /* --- colors --- */
                    555: #define	BGRED   (ISBLACKONWHITE?255:0)
                    556: #define	BGGREEN (ISBLACKONWHITE?255:0)
                    557: #define	BGBLUE  (ISBLACKONWHITE?255:0)
                    558: #ifndef	FGRED
                    559:   #define FGRED   (ISBLACKONWHITE?0:255)
                    560: #endif
                    561: #ifndef	FGGREEN
                    562:   #define FGGREEN (ISBLACKONWHITE?0:255)
                    563: #endif
                    564: #ifndef	FGBLUE
                    565:   #define FGBLUE  (ISBLACKONWHITE?0:255)
                    566: #endif
1.2       albertel  567: /* --- "smash" margin (0 means no smashing) --- */
                    568: #ifndef SMASHMARGIN
                    569:   #ifdef NOSMASH
                    570:     #define SMASHMARGIN 0
1.1       albertel  571:   #else
1.2       albertel  572:     #define SMASHMARGIN 3
1.1       albertel  573:   #endif
                    574: #endif
1.3     ! albertel  575: #ifndef SMASHCHECK
        !           576:   #define SMASHCHECK 0
        !           577: #endif
1.1       albertel  578: /* --- textwidth --- */
                    579: #ifndef TEXTWIDTH
                    580:   #define TEXTWIDTH (400)
                    581: #endif
                    582: /* --- font "combinations" --- */
1.2       albertel  583: #define	CMSYEX (109)			/*select CMSY10, CMEX10 or STMARY10*/
1.1       albertel  584: /* --- prefix prepended to all expressions --- */
                    585: #ifndef	PREFIX
                    586:   #define PREFIX "\000"			/* default no prepended prefix */
                    587: #endif
                    588: /* --- skip argv[]'s preceding ARGSIGNAL when parsing command-line args --- */
                    589: #ifdef NOARGSIGNAL
                    590:   #define ARGSIGNAL NULL
                    591: #endif
                    592: #ifndef	ARGSIGNAL
                    593:   #define ARGSIGNAL "++"
                    594: #endif
                    595: /* --- security and logging (inhibit message logging, etc) --- */
                    596: #ifndef	SECURITY
                    597:   #define SECURITY 999			/* default highest security level */
                    598: #endif
                    599: #ifndef	LOGFILE
                    600:   #define LOGFILE "mimetex.log"		/* default log file */
                    601: #endif
                    602: #ifndef	CACHELOG
                    603:   #define CACHELOG "mimetex.log"	/* default caching log file */
                    604: #endif
                    605: #if !defined(NODUMPENVP) && !defined(DUMPENVP)
                    606:   #define DUMPENVP			/* assume char *envp[] available */
                    607: #endif
                    608: /* --- image caching (cache images if given -DCACHEPATH=\"path\") --- */
                    609: #ifndef CACHEPATH
                    610:   #define ISCACHING 0			/* no caching */
                    611:   #define CACHEPATH "\000"		/* same directory as mimetex.cgi */
                    612: #else
                    613:   #define ISCACHING 1			/* caching if -DCACHEPATH="path" */
                    614: #endif
                    615: /* --- \input paths (prepend prefix if given -DPATHPREFIX=\"prefix\") --- */
                    616: #ifndef PATHPREFIX
                    617:   #define PATHPREFIX "\000"		/* paths relative mimetex.cgi */
                    618: #endif
1.2       albertel  619: /* --- time zone delta t (in hours) --- */
                    620: #ifndef TZDELTA
                    621:   #define TZDELTA 0
                    622: #endif
1.3     ! albertel  623: /* --- treat +'s in query string as blanks? --- */
        !           624: #ifdef PLUSBLANK			/* + always interpreted as blank */
        !           625:   #define ISPLUSBLANK 1
        !           626: #else
        !           627:   #ifdef PLUSNOTBLANK			/* + never interpreted as blank */
        !           628:     #define ISPLUSBLANK 0
        !           629:   #else					/* program tries to determine */
        !           630:     #define ISPLUSBLANK (-1)
        !           631:   #endif
        !           632: #endif
1.1       albertel  633: 
                    634: /* -------------------------------------------------------------------------
                    635: debugging and logging / error reporting
                    636: -------------------------------------------------------------------------- */
                    637: /* --- debugging and error reporting --- */
                    638: #ifndef	MSGLEVEL
                    639:   #define MSGLEVEL 1
                    640: #endif
                    641: #define	DBGLEVEL 9			/* debugging if msglevel>=DBGLEVEL */
                    642: #define	LOGLEVEL 3			/* logging if msglevel>=LOGLEVEL */
                    643: #ifndef FORMLEVEL
                    644:   #define FORMLEVEL LOGLEVEL		/*msglevel if called from html form*/
                    645: #endif
                    646: GLOBAL(int,seclevel,SECURITY);		/* security level */
                    647: GLOBAL(int,msglevel,MSGLEVEL);		/* message level for verbose/debug */
                    648: STATIC	FILE *msgfp;			/* output in command-line mode */
                    649: /* --- embed warnings in rendered expressions, [\xxx?] if \xxx unknown --- */
                    650: #ifdef WARNINGS
                    651:   #define WARNINGLEVEL WARNINGS
                    652: #else
                    653:   #ifdef NOWARNINGS
                    654:     #define WARNINGLEVEL 0
                    655:   #else
                    656:     #define WARNINGLEVEL 1
                    657:   #endif
                    658: #endif
                    659: GLOBAL(int,warninglevel,WARNINGLEVEL);	/* warning level */
                    660: 
                    661: /* -------------------------------------------------------------------------
                    662: control flags and values
                    663: -------------------------------------------------------------------------- */
                    664: GLOBAL(int,recurlevel,0);		/* inc/decremented in rasterize() */
                    665: GLOBAL(int,scriptlevel,0);		/* inc/decremented in rastlimits() */
1.2       albertel  666: GLOBAL(int,isstring,0);			/*pixmap is ascii string, not raster*/
1.3     ! albertel  667: GLOBAL(int,isligature,0);		/* true if ligature found */
        !           668: GLOBAL(char,*subexprptr,(char *)NULL);	/* ptr within expression to subexpr*/
1.2       albertel  669: /*SHARED(int,imageformat,1);*/		/* image is 1=bitmap, 2=.gf-like */
1.1       albertel  670: GLOBAL(int,isdisplaystyle,1);		/* displaystyle mode (forced if 2) */
                    671: GLOBAL(int,ispreambledollars,0);	/* displaystyle mode set by $$...$$ */
1.3     ! albertel  672: GLOBAL(int,isemitcontenttype,1);	/* true to emit mime content-type */
1.2       albertel  673: GLOBAL(int,fontnum,0);			/* cal=1,scr=2,rm=3,it=4,bb=5,bf=6 */
1.1       albertel  674: GLOBAL(int,fontsize,NORMALSIZE);	/* current size */
                    675: GLOBAL(int,displaysize,DISPLAYSIZE);	/* use \displaystyle when fontsize>=*/
                    676: GLOBAL(int,shrinkfactor,3);		/* shrinkfactors[fontsize] */
                    677: GLOBAL(double,unitlength,1.0);		/* #pixels per unit (may be <1.0) */
                    678: /*GLOBAL(int,textwidth,TEXTWIDTH);*/	/* #pixels across line */
1.3     ! albertel  679: GLOBAL(int,isnocatspace,0);		/* >0 to not add space in rastcat()*/
1.2       albertel  680: GLOBAL(int,smashmargin,SMASHMARGIN);	/* minimum "smash" margin */
1.3     ! albertel  681: GLOBAL(int,mathsmashmargin,SMASHMARGIN); /* needed for \text{if $n-m$ even}*/
1.2       albertel  682: GLOBAL(int,issmashdelta,1);		/* true if smashmargin is a delta */
1.3     ! albertel  683: GLOBAL(int,isexplicitsmash,0);		/* true if \smash explicitly given */
        !           684: GLOBAL(int,smashcheck,SMASHCHECK);	/* check if terms safe to smash */
        !           685: GLOBAL(int,isscripted,0);		/* is (lefthand) term text-scripted*/
        !           686: GLOBAL(int,isdelimscript,0);		/* is \right delim text-scripted */
        !           687: GLOBAL(int,issmashokay,0);		/*is leading char okay for smashing*/
        !           688: #define	BLANKSIGNAL (-991234)		/*rastsmash signal right-hand blank*/
        !           689: GLOBAL(int,blanksignal,BLANKSIGNAL);	/*rastsmash signal right-hand blank*/
        !           690: GLOBAL(int,blanksymspace,0);		/* extra (or too much) space wanted*/
        !           691: GLOBAL(int,istransparent,ISTRANSPARENT);/* true sets background transparent*/
1.1       albertel  692: GLOBAL(int,fgred,FGRED);
                    693:   GLOBAL(int,fggreen,FGGREEN);
                    694:   GLOBAL(int,fgblue,FGBLUE);		/* fg r,g,b */
                    695: GLOBAL(int,bgred,BGRED);
                    696:   GLOBAL(int,bggreen,BGGREEN);
                    697:   GLOBAL(int,bgblue,BGBLUE);		/* bg r,g,b */
1.3     ! albertel  698: GLOBAL(double,gammacorrection,GAMMA);	/* gamma correction */
        !           699: GLOBAL(int,isplusblank,ISPLUSBLANK);	/*interpret +'s in query as blanks?*/
1.1       albertel  700: GLOBAL(int,isblackonwhite,ISBLACKONWHITE); /*1=black on white,0=reverse*/
                    701: GLOBAL(char,exprprefix[256],PREFIX);	/* prefix prepended to expressions */
                    702: GLOBAL(int,aaalgorithm,AAALGORITHM);	/* for lp, 1=aalowpass, 2 =aapnm */
1.3     ! albertel  703: GLOBAL(int,maxfollow,MAXFOLLOW);	/* aafollowline() maxturn parameter*/
1.1       albertel  704: GLOBAL(int,fgalias,1);
                    705:   GLOBAL(int,fgonly,0);
                    706:   GLOBAL(int,bgalias,0);
                    707:   GLOBAL(int,bgonly,0);			/* aapnm() params */
                    708: GLOBAL(int,issupersampling,ISSUPERSAMPLING); /*1=supersampling 0=lowpass*/
                    709: GLOBAL(int,isss,ISSUPERSAMPLING);	/* supersampling flag for main() */
                    710: GLOBAL(int,*workingparam,(int *)NULL);	/* working parameter */
                    711: GLOBAL(subraster,*workingbox,(subraster *)NULL); /*working subraster box*/
                    712: GLOBAL(int,isreplaceleft,0);		/* true to replace leftexpression */
                    713: GLOBAL(subraster,*leftexpression,(subraster *)NULL); /*rasterized so far*/
                    714: GLOBAL(mathchardef,*leftsymdef,NULL);	/* mathchardef for preceding symbol*/
1.3     ! albertel  715: GLOBAL(int,fraccenterline,NOVALUE);	/* baseline for punct. after \frac */
        !           716: /*GLOBAL(int,currentcharclass,NOVALUE);*/ /*primarily to check for PUNCTION*/
1.1       albertel  717: GLOBAL(int,iscaching,ISCACHING);	/* true if caching images */
                    718: GLOBAL(char,cachepath[256],CACHEPATH);	/* relative path to cached files */
                    719: GLOBAL(char,pathprefix[256],PATHPREFIX); /*prefix for \input,\counter paths*/
                    720: /*GLOBAL(int,iswindows,ISWINDOWS);*/	/* true if compiled for ms windows */
                    721: 
                    722: /* -------------------------------------------------------------------------
                    723: miscellaneous macros
                    724: -------------------------------------------------------------------------- */
                    725: #define	max2(x,y)  ((x)>(y)? (x):(y))	/* larger of 2 arguments */
                    726: #define	min2(x,y)  ((x)<(y)? (x):(y))	/* smaller of 2 arguments */
                    727: #define	max3(x,y,z) max2(max2(x,y),(z))	/* largest of 3 arguments */
                    728: #define	min3(x,y,z) min2(min2(x,y),(z))	/* smallest of 3 arguments */
                    729: #define absval(x)  ((x)>=0?(x):(-(x)))	/* absolute value */
                    730: #define	iround(x)  ((int)((x)>=0?(x)+0.5:(x)-0.5)) /* round double to int */
                    731: #define	dmod(x,y)  ((x)-((y)*((double)((int)((x)/(y)))))) /*x%y for doubles*/
                    732: #define compress(s,c) if((s)!=NULL)	/* remove embedded c's from s */ \
                    733: 	{ char *p; while((p=strchr((s),(c)))!=NULL) strcpy(p,p+1); } else
                    734: #define	slower(s)  if ((s)!=NULL)	/* lowercase all chars in s */ \
                    735: 	{ char *p=(s); while(*p!='\000'){*p=tolower(*p); p++;} } else
1.3     ! albertel  736: /*subraster *subrastcpy();*/		/* need global module declaration */
        !           737: /*#define spnosmash(sp) if (sp->type==CHARASTER) sp=subrastcpy(sp); \*/
        !           738: /*	sp->type=blanksignal*/
1.1       albertel  739: 
                    740: /* ---
                    741:  * PART2
                    742:  * ------ */
                    743: #if !defined(PARTS) || defined(PART2)
                    744: /* ==========================================================================
                    745:  * Function:	new_raster ( width, height, pixsz )
                    746:  * Purpose:	Allocation and constructor for raster.
                    747:  *		mallocs and initializes memory for width*height pixels,
                    748:  *		and returns raster struct ptr to caller.
                    749:  * --------------------------------------------------------------------------
                    750:  * Arguments:	width (I)	int containing width, in bits,
                    751:  *				of raster pixmap to be allocated
                    752:  *		height (I)	int containing height, in bits/scans,
                    753:  *				of raster pixmap to be allocated
                    754:  *		pixsz (I)	int containing #bits per pixel, 1 or 8
                    755:  * --------------------------------------------------------------------------
                    756:  * Returns:	( raster * )	ptr to allocated and initialized
                    757:  *				raster struct, or NULL for any error.
                    758:  * --------------------------------------------------------------------------
                    759:  * Notes:
                    760:  * ======================================================================= */
                    761: /* --- entry point --- */
                    762: raster	*new_raster ( int width, int height, int pixsz )
                    763: {
                    764: /* -------------------------------------------------------------------------
                    765: Allocations and Declarations
                    766: -------------------------------------------------------------------------- */
                    767: raster	*rp = (raster *)NULL;		/* raster ptr returned to caller */
                    768: pixbyte	*pixmap = NULL;			/* raster pixel map to be malloced */
                    769: int	nbytes = pixsz*bitmapsz(width,height); /* #bytes needed for pixmap */
                    770: int	filler = (isstring?' ':0);	/* pixmap filler */
                    771: int	delete_raster();		/* in case pixmap malloc() fails */
                    772: int	npadding = (0&&issupersampling?8+256:0); /* padding bytes */
                    773: /* -------------------------------------------------------------------------
                    774: allocate and initialize raster struct and embedded bitmap
                    775: -------------------------------------------------------------------------- */
                    776: if ( msgfp!=NULL && msglevel>=9999 )
                    777:   { fprintf(msgfp,"new_raster(%d,%d,%d)> entry point\n",
                    778:     width,height,pixsz); fflush(msgfp); }
                    779: /* --- allocate and initialize raster struct --- */
                    780: rp = (raster *)malloc(sizeof(raster));	/* malloc raster struct */
                    781: if ( msgfp!=NULL && msglevel>=9999 )
                    782:   { fprintf(msgfp,"new_raster> rp=malloc(%d) returned (%s)\n",
                    783:     sizeof(raster),(rp==NULL?"null ptr":"success")); fflush(msgfp); }
                    784: if ( rp == (raster *)NULL )		/* malloc failed */
                    785:   goto end_of_job;			/* return error to caller */
                    786: rp->width = width;			/* store width in raster struct */
                    787: rp->height = height;			/* and store height */
1.2       albertel  788: rp->format = 1;				/* initialize as bitmap format */
1.1       albertel  789: rp->pixsz = pixsz;			/* store #bits per pixel */
                    790: rp->pixmap = (pixbyte *)NULL;		/* init bitmap as null ptr */
                    791: /* --- allocate and initialize bitmap array --- */
                    792: if ( msgfp!=NULL && msglevel>=9999 )
                    793:   { fprintf(msgfp,"new_raster> calling pixmap=malloc(%d)\n",
                    794:     nbytes); fflush(msgfp); }
                    795: if ( nbytes>0 && nbytes<=pixsz*maxraster )  /* fail if width*height too big*/
                    796:   pixmap = (pixbyte *)malloc(nbytes+npadding); /*bytes for width*height bits*/
                    797: if ( msgfp!=NULL && msglevel>=9999 )
                    798:   { fprintf(msgfp,"new_raster> pixmap=malloc(%d) returned (%s)\n",
                    799:     nbytes,(pixmap==NULL?"null ptr":"success")); fflush(msgfp); }
                    800: if ( pixmap == (pixbyte *)NULL )	/* malloc failed */
                    801:   { delete_raster(rp);			/* so free everything */
                    802:     rp = (raster *)NULL;		/* reset pointer */
                    803:     goto end_of_job; }			/* and return error to caller */
                    804: memset((void *)pixmap,filler,nbytes);	/* init bytes to binary 0's or ' 's*/
                    805: *pixmap = (pixbyte)0;			/* and first byte alwasy 0 */
                    806: rp->pixmap = pixmap;			/* store ptr to malloced memory */
                    807: /* -------------------------------------------------------------------------
                    808: Back to caller with address of raster struct, or NULL ptr for any error.
                    809: -------------------------------------------------------------------------- */
                    810: end_of_job:
                    811:   if ( msgfp!=NULL && msglevel>=9999 )
                    812:     { fprintf(msgfp,"new_raster(%d,%d,%d)> returning (%s)\n",
                    813:       width,height,pixsz,(rp==NULL?"null ptr":"success")); fflush(msgfp); }
                    814:   return ( rp );			/* back to caller with raster */
                    815: } /* --- end-of-function new_raster() --- */
                    816: 
                    817: 
                    818: /* ==========================================================================
                    819:  * Function:	new_subraster ( width, height, pixsz )
                    820:  * Purpose:	Allocate a new subraster along with
                    821:  *		an embedded raster of width x height.
                    822:  * --------------------------------------------------------------------------
                    823:  * Arguments:	width (I)	int containing width of embedded raster
                    824:  *		height (I)	int containing height of embedded raster
                    825:  *		pixsz (I)	int containing #bits per pixel, 1 or 8
                    826:  * --------------------------------------------------------------------------
                    827:  * Returns:	( subraster * )	ptr to newly-allocated subraster,
                    828:  *				or NULL for any error.
                    829:  * --------------------------------------------------------------------------
                    830:  * Notes:     o	if width or height <=0, embedded raster not allocated
                    831:  * ======================================================================= */
                    832: /* --- entry point --- */
                    833: subraster *new_subraster ( int width, int height, int pixsz )
                    834: {
                    835: /* -------------------------------------------------------------------------
                    836: Allocations and Declarations
                    837: -------------------------------------------------------------------------- */
                    838: subraster *sp=NULL;			/* subraster returned to caller */
                    839: raster	*new_raster(), *rp=NULL;	/* image raster embedded in sp */
                    840: int	delete_subraster();		/* in case new_raster() fails */
                    841: int	size = NORMALSIZE,		/* default size */
                    842: 	baseline = height-1;		/* and baseline */
                    843: /* -------------------------------------------------------------------------
                    844: allocate and initialize subraster struct
                    845: -------------------------------------------------------------------------- */
                    846: if ( msgfp!=NULL && msglevel>=9999 )
                    847:   { fprintf(msgfp,"new_subraster(%d,%d,%d)> entry point\n",
                    848:     width,height,pixsz); fflush(msgfp); }
                    849: /* --- allocate subraster struct --- */
                    850: sp = (subraster *)malloc(sizeof(subraster));  /* malloc subraster struct */
                    851: if ( sp == (subraster *)NULL )		/* malloc failed */
                    852:   goto end_of_job;			/* return error to caller */
                    853: /* --- initialize subraster struct --- */
                    854: sp->type = NOVALUE;			/* character or image raster */
                    855: sp->symdef =  (mathchardef *)NULL;	/* mathchardef identifying image */
                    856: sp->baseline = baseline;		/*0 if image is entirely descending*/
                    857: sp->size = size;			/* font size 0-4 */
                    858: sp->toprow = sp->leftcol = (-1);	/* upper-left corner of subraster */
                    859: sp->image = (raster *)NULL;		/*ptr to bitmap image of subraster*/
                    860: /* -------------------------------------------------------------------------
                    861: allocate raster and embed it in subraster, and return to caller
                    862: -------------------------------------------------------------------------- */
                    863: /* --- allocate raster struct if desired --- */
                    864: if ( width>0 && height>0 && pixsz>0 )	/* caller wants raster */
                    865:   { if ( (rp=new_raster(width,height,pixsz)) /* allocate embedded raster */
                    866:     !=   NULL )				/* if allocate succeeded */
                    867:         sp->image = rp;			/* embed raster in subraster */
                    868:     else				/* or if allocate failed */
                    869:       { delete_subraster(sp);		/* free non-unneeded subraster */
                    870: 	sp = NULL; } }			/* signal error */
                    871: /* --- back to caller with new subraster or NULL --- */
                    872: end_of_job:
                    873:   if ( msgfp!=NULL && msglevel>=9999 )
                    874:     { fprintf(msgfp,"new_subraster(%d,%d,%d)> returning (%s)\n",
                    875:       width,height,pixsz,(sp==NULL?"null ptr":"success")); fflush(msgfp); }
                    876:   return ( sp );
                    877: } /* --- end-of-function new_subraster() --- */
                    878: 
                    879: 
                    880: /* ==========================================================================
                    881:  * Function:	new_chardef (  )
                    882:  * Purpose:	Allocates and initializes a chardef struct,
                    883:  *		but _not_ the embedded raster struct.
                    884:  * --------------------------------------------------------------------------
                    885:  * Arguments:	none
                    886:  * --------------------------------------------------------------------------
                    887:  * Returns:	( chardef * )	ptr to allocated and initialized
                    888:  *				chardef struct, or NULL for any error.
                    889:  * --------------------------------------------------------------------------
                    890:  * Notes:
                    891:  * ======================================================================= */
                    892: /* --- entry point --- */
                    893: chardef	*new_chardef (  )
                    894: {
                    895: /* -------------------------------------------------------------------------
                    896: Allocations and Declarations
                    897: -------------------------------------------------------------------------- */
                    898: chardef	*cp = (chardef *)NULL;		/* chardef ptr returned to caller */
                    899: /* -------------------------------------------------------------------------
                    900: allocate and initialize chardef struct
                    901: -------------------------------------------------------------------------- */
                    902: cp = (chardef *)malloc(sizeof(chardef)); /* malloc chardef struct */
                    903: if ( cp == (chardef *)NULL )		/* malloc failed */
                    904:   goto end_of_job;			/* return error to caller */
                    905: cp->charnum = cp->location = 0;		/* init character description */
                    906: cp->toprow = cp->topleftcol = 0;	/* init upper-left corner */
                    907: cp->botrow = cp->botleftcol = 0;	/* init lower-left corner */
                    908: cp->image.width = cp->image.height = 0;	/* init raster dimensions */
1.2       albertel  909: cp->image.format = 0;			/* init raster format */
1.1       albertel  910: cp->image.pixsz = 0;			/* and #bits per pixel */
                    911: cp->image.pixmap = NULL;		/* init raster pixmap as null */
                    912: /* -------------------------------------------------------------------------
                    913: Back to caller with address of chardef struct, or NULL ptr for any error.
                    914: -------------------------------------------------------------------------- */
                    915: end_of_job:
                    916:   return ( cp );
                    917: } /* --- end-of-function new_chardef() --- */
                    918: 
                    919: 
                    920: /* ==========================================================================
                    921:  * Function:	delete_raster ( rp )
                    922:  * Purpose:	Destructor for raster.
                    923:  *		Frees memory for raster bitmap and struct.
                    924:  * --------------------------------------------------------------------------
                    925:  * Arguments:	rp (I)		ptr to raster struct to be deleted.
                    926:  * --------------------------------------------------------------------------
                    927:  * Returns:	( int )		1 if completed successfully,
                    928:  *				or 0 otherwise (for any error).
                    929:  * --------------------------------------------------------------------------
                    930:  * Notes:
                    931:  * ======================================================================= */
                    932: /* --- entry point --- */
                    933: int	delete_raster ( raster *rp )
                    934: {
                    935: /* -------------------------------------------------------------------------
                    936: free raster bitmap and struct
                    937: -------------------------------------------------------------------------- */
                    938: if ( rp != (raster *)NULL )		/* can't free null ptr */
                    939:   {
                    940:   if ( rp->pixmap != (pixbyte *)NULL )	/* can't free null ptr */
                    941:     free((void *)rp->pixmap);		/* free pixmap within raster */
                    942:   free((void *)rp);			/* lastly, free raster struct */
                    943:   } /* --- end-of-if(rp!=NULL) --- */
                    944: return ( 1 );				/* back to caller, 1=okay 0=failed */
                    945: } /* --- end-of-function delete_raster() --- */
                    946: 
                    947: 
                    948: /* ==========================================================================
                    949:  * Function:	delete_subraster ( sp )
                    950:  * Purpose:	Deallocates a subraster (and embedded raster)
                    951:  * --------------------------------------------------------------------------
                    952:  * Arguments:	sp (I)		ptr to subraster struct to be deleted.
                    953:  * --------------------------------------------------------------------------
                    954:  * Returns:	( int )		1 if completed successfully,
                    955:  *				or 0 otherwise (for any error).
                    956:  * --------------------------------------------------------------------------
                    957:  * Notes:
                    958:  * ======================================================================= */
                    959: /* --- entry point --- */
                    960: int	delete_subraster ( subraster *sp )
                    961: {
                    962: /* -------------------------------------------------------------------------
                    963: free subraster struct
                    964: -------------------------------------------------------------------------- */
                    965: int	delete_raster();		/* to delete embedded raster */
                    966: if ( sp != (subraster *)NULL )		/* can't free null ptr */
                    967:   {
                    968:   if ( sp->type != CHARASTER )		/* not static character data */
                    969:     if ( sp->image != NULL )		/*raster allocated within subraster*/
                    970:       delete_raster(sp->image);		/* so free embedded raster */
                    971:   free((void *)sp);			/* and free subraster struct itself*/
                    972:   } /* --- end-of-if(sp!=NULL) --- */
                    973: return ( 1 );				/* back to caller, 1=okay 0=failed */
                    974: } /* --- end-of-function delete_subraster() --- */
                    975: 
                    976: 
                    977: /* ==========================================================================
                    978:  * Function:	delete_chardef ( cp )
                    979:  * Purpose:	Deallocates a chardef (and bitmap of embedded raster)
                    980:  * --------------------------------------------------------------------------
                    981:  * Arguments:	cp (I)		ptr to chardef struct to be deleted.
                    982:  * --------------------------------------------------------------------------
                    983:  * Returns:	( int )		1 if completed successfully,
                    984:  *				or 0 otherwise (for any error).
                    985:  * --------------------------------------------------------------------------
                    986:  * Notes:
                    987:  * ======================================================================= */
                    988: /* --- entry point --- */
                    989: int	delete_chardef ( chardef *cp )
                    990: {
                    991: /* -------------------------------------------------------------------------
                    992: free chardef struct
                    993: -------------------------------------------------------------------------- */
                    994: if ( cp != (chardef *)NULL )		/* can't free null ptr */
                    995:   {
                    996:   if ( cp->image.pixmap != NULL )	/* pixmap allocated within raster */
                    997:     free((void *)cp->image.pixmap);	/* so free embedded pixmap */
                    998:   free((void *)cp);			/* and free chardef struct itself */
                    999:   } /* --- end-of-if(cp!=NULL) --- */
                   1000: /* -------------------------------------------------------------------------
                   1001: Back to caller with 1=okay, 0=failed.
                   1002: -------------------------------------------------------------------------- */
                   1003: return ( 1 );
                   1004: } /* --- end-of-function delete_chardef() --- */
                   1005: 
                   1006: 
                   1007: /* ==========================================================================
                   1008:  * Function:	rastcpy ( rp )
                   1009:  * Purpose:	makes duplicate copy of rp
                   1010:  * --------------------------------------------------------------------------
                   1011:  * Arguments:	rp (I)		ptr to raster struct to be copied
                   1012:  * --------------------------------------------------------------------------
                   1013:  * Returns:	( raster * )	ptr to new copy rp,
                   1014:  *				or NULL for any error.
                   1015:  * --------------------------------------------------------------------------
                   1016:  * Notes:     o
                   1017:  * ======================================================================= */
                   1018: /* --- entry point --- */
                   1019: raster	*rastcpy ( raster *rp )
                   1020: {
                   1021: /* -------------------------------------------------------------------------
                   1022: Allocations and Declarations
                   1023: -------------------------------------------------------------------------- */
                   1024: raster	*new_raster(), *newrp=NULL;	/*copied raster returned to caller*/
                   1025: int	height= (rp==NULL?0:rp->height), /* original and copied height */
                   1026: 	width = (rp==NULL?0:rp->width),	/* original and copied width */
                   1027: 	pixsz = (rp==NULL?0:rp->pixsz),	/* #bits per pixel */
                   1028: 	nbytes= (rp==NULL?0:(pixmapsz(rp))); /* #bytes in rp's pixmap */
                   1029: /* -------------------------------------------------------------------------
                   1030: allocate rotated raster and fill it
                   1031: -------------------------------------------------------------------------- */
                   1032: /* --- allocate copied raster with same width,height, and copy bitmap --- */
                   1033: if ( rp != NULL )			/* nothing to copy if ptr null */
                   1034:   if ( (newrp = new_raster(width,height,pixsz)) /*same width,height in copy*/
                   1035:   !=   NULL )				/* check that allocate succeeded */
                   1036:     memcpy(newrp->pixmap,rp->pixmap,nbytes); /* fill copied raster pixmap */
                   1037: return ( newrp );			/* return copied raster to caller */
                   1038: } /* --- end-of-function rastcpy() --- */
                   1039: 
                   1040: 
                   1041: /* ==========================================================================
                   1042:  * Function:	subrastcpy ( sp )
                   1043:  * Purpose:	makes duplicate copy of sp
                   1044:  * --------------------------------------------------------------------------
                   1045:  * Arguments:	sp (I)		ptr to subraster struct to be copied
                   1046:  * --------------------------------------------------------------------------
                   1047:  * Returns:	( subraster * )	ptr to new copy sp,
                   1048:  *				or NULL for any error.
                   1049:  * --------------------------------------------------------------------------
                   1050:  * Notes:     o
                   1051:  * ======================================================================= */
                   1052: /* --- entry point --- */
                   1053: subraster *subrastcpy ( subraster *sp )
                   1054: {
                   1055: /* -------------------------------------------------------------------------
                   1056: Allocations and Declarations
                   1057: -------------------------------------------------------------------------- */
                   1058: subraster *new_subraster(), *newsp=NULL; /* allocate new subraster */
                   1059: raster	*rastcpy(), *newrp=NULL;	/* and new raster image within it */
                   1060: int	delete_subraster();		/* dealloc newsp if rastcpy() fails*/
                   1061: /* -------------------------------------------------------------------------
                   1062: make copy, and return it to caller
                   1063: -------------------------------------------------------------------------- */
                   1064: if ( sp == NULL ) goto end_of_job;	/* nothing to copy */
                   1065: /* --- allocate new subraster "envelope" for copy --- */
                   1066: if ( (newsp=new_subraster(0,0,0))	/* allocate subraster "envelope" */
                   1067: ==   NULL ) goto end_of_job;		/* and quit if we fail to allocate */
                   1068: /* --- transparently copy original envelope to new one --- */
                   1069: memcpy((void *)newsp,(void *)sp,sizeof(subraster)); /* copy envelope */
                   1070: /* --- make a copy of the rasterized image itself, if there is one --- */
                   1071: if ( sp->image != NULL )		/* there's an image embedded in sp */
                   1072:   if ( (newrp = rastcpy(sp->image))	/* so copy rasterized image in sp */
                   1073:   ==   NULL )				/* failed to copy successfully */
                   1074:     { delete_subraster(newsp);		/* won't need newsp any more */
                   1075:       newsp = NULL;			/* because we're returning error */
                   1076:       goto end_of_job; }		/* back to caller with error signal*/
                   1077: /* --- set new params in new envelope --- */
                   1078: newsp->image = newrp;			/* new raster image we just copied */
                   1079: switch ( sp->type )			/* set new raster image type */
                   1080:   { case STRINGRASTER: case CHARASTER: newsp->type = STRINGRASTER; break;
                   1081:     case ASCIISTRING:                  newsp->type = ASCIISTRING;  break;
1.3     ! albertel 1082:     case FRACRASTER:                   newsp->type = FRACRASTER;   break;
        !          1083:     case BLANKSIGNAL:                  newsp->type = blanksignal;  break;
1.1       albertel 1084:     case IMAGERASTER:  default:        newsp->type = IMAGERASTER;  break; }
                   1085: /* --- return copy of sp to caller --- */
                   1086: end_of_job:
                   1087:   return ( newsp );			/* copy back to caller */
                   1088: } /* --- end-of-function subrastcpy() --- */
                   1089: 
                   1090: 
                   1091: /* ==========================================================================
                   1092:  * Function:	rastrot ( rp )
                   1093:  * Purpose:	rotates rp image 90 degrees right/clockwise
                   1094:  * --------------------------------------------------------------------------
                   1095:  * Arguments:	rp (I)		ptr to raster struct to be rotated
                   1096:  * --------------------------------------------------------------------------
1.3     ! albertel 1097:  * Returns:	( raster * )	ptr to new raster rotated relative to rp,
1.1       albertel 1098:  *				or NULL for any error.
                   1099:  * --------------------------------------------------------------------------
                   1100:  * Notes:     o	An underbrace is } rotated 90 degrees clockwise,
                   1101:  *		a hat is <, etc.
                   1102:  * ======================================================================= */
                   1103: /* --- entry point --- */
                   1104: raster	*rastrot ( raster *rp )
                   1105: {
                   1106: /* -------------------------------------------------------------------------
                   1107: Allocations and Declarations
                   1108: -------------------------------------------------------------------------- */
                   1109: raster	*new_raster(), *rotated=NULL;	/*rotated raster returned to caller*/
                   1110: int	height = rp->height, irow,	/* original height, row index */
                   1111: 	width = rp->width, icol,	/* original width, column index */
                   1112: 	pixsz = rp->pixsz;		/* #bits per pixel */
                   1113: /* -------------------------------------------------------------------------
                   1114: allocate rotated raster and fill it
                   1115: -------------------------------------------------------------------------- */
                   1116: /* --- allocate rotated raster with flipped width<-->height --- */
                   1117: if ( (rotated = new_raster(height,width,pixsz)) /* flip width,height */
                   1118: !=   NULL )				/* check that allocation succeeded */
                   1119:   /* --- fill rotated raster --- */
                   1120:   for ( irow=0; irow<height; irow++ )	/* for each row of rp */
                   1121:     for ( icol=0; icol<width; icol++ )	/* and each column of rp */
                   1122:       {	int value = getpixel(rp,irow,icol);
                   1123: 	/* setpixel(rotated,icol,irow,value); } */
                   1124: 	setpixel(rotated,icol,(height-1-irow),value); }
                   1125: return ( rotated );			/* return rotated raster to caller */
                   1126: } /* --- end-of-function rastrot() --- */
                   1127: 
                   1128: 
                   1129: /* ==========================================================================
1.3     ! albertel 1130:  * Function:	rastref ( rp, axis )
        !          1131:  * Purpose:	reflects rp, horizontally about y-axis |_ becomes _| if axis=1
        !          1132:  *		or vertically about x-axis M becomes W if axis=2.
        !          1133:  * --------------------------------------------------------------------------
        !          1134:  * Arguments:	rp (I)		ptr to raster struct to be reflected
        !          1135:  *		axis (I)	int containing 1 for horizontal reflection,
        !          1136:  *				or 2 for vertical
        !          1137:  * --------------------------------------------------------------------------
        !          1138:  * Returns:	( raster * )	ptr to new raster reflected relative to rp,
        !          1139:  *				or NULL for any error.
        !          1140:  * --------------------------------------------------------------------------
        !          1141:  * Notes:     o
        !          1142:  * ======================================================================= */
        !          1143: /* --- entry point --- */
        !          1144: raster	*rastref ( raster *rp, int axis )
        !          1145: {
        !          1146: /* -------------------------------------------------------------------------
        !          1147: Allocations and Declarations
        !          1148: -------------------------------------------------------------------------- */
        !          1149: raster	*new_raster(), *reflected=NULL;	/* reflected raster back to caller */
        !          1150: int	height = rp->height, irow,	/* height, row index */
        !          1151: 	width = rp->width, icol,	/* width, column index */
        !          1152: 	pixsz = rp->pixsz;		/* #bits per pixel */
        !          1153: /* -------------------------------------------------------------------------
        !          1154: allocate reflected raster and fill it
        !          1155: -------------------------------------------------------------------------- */
        !          1156: /* --- allocate reflected raster with same width, height --- */
        !          1157: if ( axis==1 || axis==2 )		/* first validate axis arg */
        !          1158:  if ( (reflected = new_raster(width,height,pixsz)) /* same width, height */
        !          1159:  !=   NULL )				/* check that allocation succeeded */
        !          1160:   /* --- fill reflected raster --- */
        !          1161:   for ( irow=0; irow<height; irow++ )	/* for each row of rp */
        !          1162:     for ( icol=0; icol<width; icol++ ) { /* and each column of rp */
        !          1163:       int value = getpixel(rp,irow,icol);
        !          1164:       if ( axis == 1 ) { setpixel(reflected,irow,width-1-icol,value); }
        !          1165:       if ( axis == 2 ) { setpixel(reflected,height-1-irow,icol,value); } }
        !          1166: return ( reflected );			/*return reflected raster to caller*/
        !          1167: } /* --- end-of-function rastref() --- */
        !          1168: 
        !          1169: 
        !          1170: /* ==========================================================================
1.1       albertel 1171:  * Function:	rastput ( target, source, top, left, isopaque )
                   1172:  * Purpose:	Overlays source onto target,
                   1173:  *		with the 0,0-bit of source onto the top,left-bit of target.
                   1174:  * --------------------------------------------------------------------------
                   1175:  * Arguments:	target (I)	ptr to target raster struct
                   1176:  *		source (I)	ptr to source raster struct
                   1177:  *		top (I)		int containing 0 ... target->height - 1
                   1178:  *		left (I)	int containing 0 ... target->width - 1
                   1179:  *		isopaque (I)	int containing false (zero) to allow
                   1180:  *				original 1-bits of target to "show through"
                   1181:  *				0-bits of source.
                   1182:  * --------------------------------------------------------------------------
                   1183:  * Returns:	( int )		1 if completed successfully,
                   1184:  *				or 0 otherwise (for any error).
                   1185:  * --------------------------------------------------------------------------
                   1186:  * Notes:
                   1187:  * ======================================================================= */
                   1188: /* --- entry point --- */
                   1189: int	rastput ( raster *target, raster *source,
                   1190: 		int top, int left, int isopaque )
                   1191: {
                   1192: /* -------------------------------------------------------------------------
                   1193: Allocations and Declarations
                   1194: -------------------------------------------------------------------------- */
                   1195: int	irow, icol,		/* indexes over source raster */
                   1196: 	twidth=target->width, theight=target->height, /*target width,height*/
                   1197: 	tpix, ntpix = twidth*theight; /* #pixels in target */
                   1198: int	isfatal = 0,		/* true to abend on out-of-bounds error */
                   1199: 	isstrict = 0/*1*/,	/* true for strict bounds check - no "wrap"*/
                   1200: 	isokay = 1;		/* true if no pixels out-of-bounds */
                   1201: /* -------------------------------------------------------------------------
                   1202: superimpose source onto target, one bit at a time
                   1203: -------------------------------------------------------------------------- */
                   1204: if ( isstrict && (top<0 || left<0) )		/* args fail strict test */
                   1205:  isokay = 0;					/* so just return error */
                   1206: else
                   1207:  for ( irow=0; irow<source->height; irow++ )	/* for each scan line */
                   1208:   {
                   1209:   tpix = (top+irow)*target->width + left - 1;	/*first target pixel (-1)*/
                   1210:   for ( icol=0; icol<source->width; icol++ )	/* each pixel in scan line */
                   1211:     {
                   1212:     int svalue = getpixel(source,irow,icol);	/* source pixel value */
                   1213:     ++tpix;					/* bump target pixel */
                   1214:     if ( msgfp!=NULL && msglevel>=9999 )	/* debugging output */
                   1215:       {	fprintf(msgfp,"rastput> tpix,ntpix=%d,%d top,irow,theight=%d,%d,%d "
                   1216: 	"left,icol,twidth=%d,%d,%d\n", tpix,ntpix, top,irow,theight,
                   1217: 	left,icol,twidth);  fflush(msgfp); }
                   1218:     if ( tpix >= ntpix				/* bounds check failed */
                   1219:     ||   (isstrict && (irow+top>=theight || icol+left>=twidth)) )
                   1220:       {	isokay = 0;				/* reset okay flag */
                   1221: 	if ( isfatal ) goto end_of_job;		/* abort if error is fatal */
                   1222: 	else break; }				/*or just go on to next row*/
                   1223:     if ( tpix >= 0 )				/* bounds check okay */
1.3     ! albertel 1224:      if ( svalue!=0 || isopaque ) {		/*got dark or opaque source*/
        !          1225:       setpixel(target,irow+top,icol+left,svalue); }/*overlay source on targ*/
1.1       albertel 1226:     } /* --- end-of-for(icol) --- */
                   1227:   } /* --- end-of-for(irow) --- */
                   1228: /* -------------------------------------------------------------------------
                   1229: Back to caller with 1=okay, 0=failed.
                   1230: -------------------------------------------------------------------------- */
                   1231: end_of_job:
                   1232:   return ( isokay /*isfatal? (tpix<ntpix? 1:0) : 1*/ );
                   1233: } /* --- end-of-function rastput() --- */
                   1234: 
                   1235: 
                   1236: /* ==========================================================================
                   1237:  * Function:	rastcompose ( sp1, sp2, offset2, isalign, isfree )
                   1238:  * Purpose:	Overlays sp2 on top of sp1, leaving both unchanged
                   1239:  *		and returning a newly-allocated composite subraster.
                   1240:  *		Frees/deletes input sp1 and/or sp2 depending on value
                   1241:  *		of isfree (0=none, 1=sp1, 2=sp2, 3=both).
                   1242:  * --------------------------------------------------------------------------
                   1243:  * Arguments:	sp1 (I)		subraster *  to "underneath" subraster,
                   1244:  *				whose baseline is preserved
                   1245:  *		sp2 (I)		subraster *  to "overlaid" subraster
                   1246:  *		offset2 (I)	int containing 0 or number of pixels
                   1247:  *				to horizontally shift sp2 relative to sp1,
                   1248:  *				either positive (right) or negative
                   1249:  *		isalign (I)	int containing 1 to align baselines,
                   1250:  *				or 0 to vertically center sp2 over sp1
                   1251:  *		isfree (I)	int containing 1=free sp1 before return,
                   1252:  *				2=free sp2, 3=free both, 0=free none.
                   1253:  * --------------------------------------------------------------------------
                   1254:  * Returns:	( subraster * )	pointer to constructed subraster
                   1255:  *				or  NULL for any error
                   1256:  * --------------------------------------------------------------------------
                   1257:  * Notes:
                   1258:  * ======================================================================= */
                   1259: /* --- entry point --- */
                   1260: subraster *rastcompose ( subraster *sp1, subraster *sp2, int offset2,
                   1261: 			int isalign, int isfree )
                   1262: {
                   1263: /* -------------------------------------------------------------------------
                   1264: Allocations and Declarations
                   1265: -------------------------------------------------------------------------- */
                   1266: subraster *new_subraster(), *sp=(subraster *)NULL; /* returned subraster */
                   1267: raster	*rp=(raster *)NULL;		/* new composite raster in sp */
                   1268: int	delete_subraster();		/* in case isfree non-zero */
                   1269: int	rastput();			/*place sp1,sp2 in composite raster*/
                   1270: int	base1   = sp1->baseline,	/*baseline for underlying subraster*/
                   1271: 	height1 = (sp1->image)->height,	/* height for underlying subraster */
                   1272: 	width1  = (sp1->image)->width,	/* width for underlying subraster */
                   1273: 	pixsz1  = (sp1->image)->pixsz,	/* pixsz for underlying subraster */
                   1274: 	base2   = sp2->baseline,	/*baseline for overlaid subraster */
                   1275: 	height2 = (sp2->image)->height,	/* height for overlaid subraster */
                   1276: 	width2  = (sp2->image)->width,	/* width for overlaid subraster */
                   1277: 	pixsz2  = (sp2->image)->pixsz;	/* pixsz for overlaid subraster */
                   1278: int	height=0, width=0, pixsz=0, base=0; /* overlaid composite */
                   1279: /* -------------------------------------------------------------------------
                   1280: Initialization
                   1281: -------------------------------------------------------------------------- */
                   1282: /* --- determine height, width and baseline of composite raster --- */
                   1283: if ( isalign )				/* baselines of sp1,sp2 aligned */
                   1284:   { height = max2(base1+1,base2+1)	/* max height above baseline */
                   1285:            + max2(height1-base1-1,height2-base2-1); /*+ max descending below*/
                   1286:     base   = max2(base1,base2); }	/* max space above baseline */
                   1287: else					/* baselines not aligned */
                   1288:   { height = max2(height1,height2);	/* max height */
                   1289:     base   = base1 + (height-height1)/2; } /* baseline for sp1 */
                   1290: width      = max2(width1,width2+abs(offset2)); /* max width */
                   1291: pixsz      = max2(pixsz1,pixsz2);	/* bitmap,bytemap becomes bytemap */
                   1292: /* -------------------------------------------------------------------------
                   1293: allocate concatted composite subraster
                   1294: -------------------------------------------------------------------------- */
                   1295: /* --- allocate returned subraster (and then initialize it) --- */
                   1296: if ( (sp=new_subraster(width,height,pixsz)) /* allocate new subraster */
                   1297: ==   (subraster *)NULL ) goto end_of_job; /* failed, so quit */
                   1298: /* --- initialize subraster parameters --- */
                   1299: sp->type = IMAGERASTER;			/* image */
                   1300: sp->baseline = base;			/* composite baseline */
                   1301: sp->size = sp1->size;			/* underlying char is sp1 */
                   1302: /* --- extract raster from subraster --- */
                   1303: rp = sp->image;				/* raster allocated in subraster */
                   1304: /* -------------------------------------------------------------------------
                   1305: overlay sp1 and sp2 in new composite raster
                   1306: -------------------------------------------------------------------------- */
                   1307: if ( isalign )
                   1308:   { rastput (rp, sp1->image, base-base1, (width-width1)/2, 1);	/*underlying*/
                   1309:     rastput (rp, sp2->image, base-base2,			/*overlaid*/
                   1310: 		(width-width2)/2+offset2, 0); }
                   1311: else
                   1312:   { rastput (rp, sp1->image, base-base1, (width-width1)/2, 1);	/*underlying*/
                   1313:     rastput (rp, sp2->image, (height-height2)/2,		/*overlaid*/
                   1314: 		(width-width2)/2+offset2, 0); }
                   1315: /* -------------------------------------------------------------------------
                   1316: free input if requested
                   1317: -------------------------------------------------------------------------- */
                   1318: if ( isfree > 0 )			/* caller wants input freed */
                   1319:   { if ( isfree==1 || isfree>2 ) delete_subraster(sp1);	/* free sp1 */
                   1320:     if ( isfree >= 2 ) delete_subraster(sp2); }		/* and/or sp2 */
                   1321: /* -------------------------------------------------------------------------
                   1322: Back to caller with pointer to concatted subraster or with null for error
                   1323: -------------------------------------------------------------------------- */
                   1324: end_of_job:
                   1325:   return ( sp );			/* back with subraster or null ptr */
                   1326: } /* --- end-of-function rastcompose() --- */
                   1327: 
                   1328: 
                   1329: /* ==========================================================================
                   1330:  * Function:	rastcat ( sp1, sp2, isfree )
                   1331:  * Purpose:	"Concatanates" subrasters sp1||sp2, leaving both unchanged
                   1332:  *		and returning a newly-allocated subraster.
                   1333:  *		Frees/deletes input sp1 and/or sp2 depending on value
                   1334:  *		of isfree (0=none, 1=sp1, 2=sp2, 3=both).
                   1335:  * --------------------------------------------------------------------------
                   1336:  * Arguments:	sp1 (I)		subraster *  to left-hand subraster
                   1337:  *		sp2 (I)		subraster *  to right-hand subraster
                   1338:  *		isfree (I)	int containing 1=free sp1 before return,
                   1339:  *				2=free sp2, 3=free both, 0=free none.
                   1340:  * --------------------------------------------------------------------------
                   1341:  * Returns:	( subraster * )	pointer to constructed subraster sp1||sp2
                   1342:  *				or  NULL for any error
                   1343:  * --------------------------------------------------------------------------
                   1344:  * Notes:
                   1345:  * ======================================================================= */
                   1346: /* --- entry point --- */
                   1347: subraster *rastcat ( subraster *sp1, subraster *sp2, int isfree )
                   1348: {
                   1349: /* -------------------------------------------------------------------------
                   1350: Allocations and Declarations
                   1351: -------------------------------------------------------------------------- */
                   1352: subraster *new_subraster(), *sp=(subraster *)NULL; /* returned subraster */
                   1353: raster	*rp=(raster *)NULL;		/* new concatted raster */
                   1354: int	delete_subraster();		/* in case isfree non-zero */
                   1355: int	rastput();			/*place sp1,sp2 in concatted raster*/
                   1356: int	type_raster();			/* debugging display */
                   1357: int	base1   = sp1->baseline,	/*baseline for left-hand subraster*/
                   1358: 	height1 = (sp1->image)->height,	/* height for left-hand subraster */
                   1359: 	width1  = (sp1->image)->width,	/* width for left-hand subraster */
                   1360: 	pixsz1  = (sp1->image)->pixsz,	/* pixsz for left-hand subraster */
                   1361: 	type1   = sp1->type,		/* image type for left-hand */
                   1362: 	base2   = sp2->baseline,	/*baseline for right-hand subraster*/
                   1363: 	height2 = (sp2->image)->height,	/* height for right-hand subraster */
                   1364: 	width2  = (sp2->image)->width,	/* width for right-hand subraster */
                   1365: 	pixsz2  = (sp2->image)->pixsz,	/* pixsz for right-hand subraster */
                   1366: 	type2   = sp2->type;		/* image type for right-hand */
                   1367: int	height=0, width=0, pixsz=0, base=0; /*concatted sp1||sp2 composite*/
1.2       albertel 1368: int	issmash = (smashmargin!=0?1:0),	/* true to "squash" sp1||sp2 */
                   1369: 	isopaque = (issmash?0:1),	/* not oppaque if smashing */
                   1370: 	rastsmash(), isblank=0, nsmash=0, /* #cols to smash */
1.3     ! albertel 1371: 	oldsmashmargin = smashmargin,	/* save original smashmargin */
        !          1372: 	oldblanksymspace = blanksymspace, /* save original blanksymspace */
        !          1373: 	oldnocatspace = isnocatspace;	/* save original isnocatspace */
1.1       albertel 1374: mathchardef *symdef1 = sp1->symdef,	/*mathchardef of last left-hand char*/
                   1375: 	*symdef2 = sp2->symdef;		/* mathchardef of right-hand char */
                   1376: int	class1 = (symdef1==NULL?ORDINARY:symdef1->class), /* symdef->class */
                   1377: 	class2 = (symdef2==NULL?ORDINARY:symdef2->class), /* or default */
                   1378: 	smash1 = (symdef1!=NULL)&&(class1==ORDINARY||class1==VARIABLE||
                   1379: 		  class1==OPENING||class1==CLOSING||class1==PUNCTION),
                   1380: 	smash2 = (symdef2!=NULL)&&(class2==ORDINARY||class2==VARIABLE||
                   1381: 		  class2==OPENING||class2==CLOSING||class2==PUNCTION),
                   1382: 	space = fontsize/2+1;		/* #cols between sp1 and sp2 */
1.3     ! albertel 1383: int	isfrac = (type1 == FRACRASTER	/* sp1 is a \frac */
        !          1384: 		  && class2 == PUNCTION); /* and sp2 is punctuation */
1.1       albertel 1385: /* -------------------------------------------------------------------------
                   1386: Initialization
                   1387: -------------------------------------------------------------------------- */
                   1388: /* --- determine inter-character space from character class --- */
                   1389: if ( !isstring )
                   1390:   space = max2(2,(symspace[class1][class2] + fontsize-3)); /* space */
                   1391: else space = 1;				/* space for ascii string */
1.3     ! albertel 1392: if ( isnocatspace > 0 ) {		/* spacing explicitly turned off */
        !          1393:   space = 0;				/* reset space */
        !          1394:   isnocatspace--; }			/* and decrement isnocatspace flag */
        !          1395: if ( 0 && sp1->type == BLANKSIGNAL ) space=0; /*implicitly turn off spacing*/
        !          1396: if ( sp1->type==BLANKSIGNAL && sp2->type==BLANKSIGNAL ) /* both blank */
        !          1397:   space = 0;				/* no extra space between spaces */
        !          1398: if ( sp2->type != BLANKSIGNAL )		/* not a blank space signal */
        !          1399:   if ( blanksymspace != 0 ) {		/* and we have a space adjustment */
        !          1400:     space = max2(0,space+blanksymspace); /* adjust as much as possible */
        !          1401:     blanksymspace = 0; }		/* and reset adjustment */
        !          1402: if ( msgfp!=NULL && msglevel>=999 )	/* display space results */
        !          1403:   { fprintf(msgfp,"rastcat> space=%d, blanksymspace=%d, isnocatspace=%d\n",
        !          1404:     space,oldblanksymspace,oldnocatspace);  fflush(msgfp); }
1.2       albertel 1405: /* --- determine smash --- */
1.3     ! albertel 1406: if ( !isstring && !isfrac )		/* don't smash strings or \frac's */
1.2       albertel 1407:  if ( issmash ) {			/* raster smash wanted */
                   1408:    int	maxsmash = rastsmash(sp1,sp2),	/* calculate max smash space */
                   1409: 	margin = smashmargin;		/* init margin without delta */
1.1       albertel 1410:    if ( (1 && smash1 && smash2)		/* concatanating two chars */
1.3     ! albertel 1411:    ||   (1 && type1!=IMAGERASTER && type2!=IMAGERASTER
        !          1412: 	   && type1!=FRACRASTER  && type2!=FRACRASTER ) )
1.2       albertel 1413:      /*maxsmash = 0;*/			/* turn off smash */
                   1414:      margin = max2(space-1,0);		/* force small smashmargin */
1.1       albertel 1415:    else					/* adjust for delta if images */
1.2       albertel 1416:      if ( issmashdelta )		/* smashmargin is a delta value */
1.1       albertel 1417:        margin += fontsize;		/* add displaystyle base to margin */
1.2       albertel 1418:    if ( maxsmash == blanksignal )	/* sp2 is intentional blank */
1.1       albertel 1419:      isblank = 1;			/* set blank flag signal */
                   1420:    else					/* see how much extra space we have*/
1.2       albertel 1421:      if ( maxsmash > margin )		/* enough space for adjustment */
                   1422:        nsmash = maxsmash-margin;	/* make adjustment */
                   1423:    if ( msgfp!=NULL && msglevel>=99 )	/* display smash results */
                   1424:      { fprintf(msgfp,"rastcat> maxsmash=%d, margin=%d, nsmash=%d\n",
                   1425:        maxsmash,margin,nsmash);
1.1       albertel 1426:        fprintf(msgfp,"rastcat> type1=%d,2=%d, class1=%d,2=%d\n", type1,type2,
                   1427:        (symdef1==NULL?-999:class1),(symdef2==NULL?-999:class2));
                   1428:        fflush(msgfp); }
1.2       albertel 1429:    } /* --- end-of-if(issmash) --- */
1.1       albertel 1430: /* --- determine height, width and baseline of composite raster --- */
                   1431: if ( !isstring )
                   1432:  { height = max2(base1+1,base2+1)	/* max height above baseline */
                   1433:           + max2(height1-base1-1,height2-base2-1); /*+ max descending below*/
1.2       albertel 1434:    width  = width1+width2 + space-nsmash; /*add widths and space-smash*/
                   1435:    width  = max3(width,width1,width2); } /* don't "over-smash" composite */
1.1       albertel 1436: else					/* ascii string */
                   1437:  { height = 1;				/* default */
                   1438:    width  = width1 + width2 + space - 1; } /* no need for two nulls */
                   1439: pixsz  = max2(pixsz1,pixsz2);		/* bitmap||bytemap becomes bytemap */
                   1440: base   = max2(base1,base2);		/* max space above baseline */
                   1441: if ( msgfp!=NULL && msglevel>=9999 )	/* display components */
                   1442:   { fprintf(msgfp,"rastcat> Left-hand ht,width,pixsz,base = %d,%d,%d,%d\n",
                   1443:     height1,width1,pixsz1,base1);
                   1444:     type_raster(sp1->image,msgfp);	/* display left-hand raster */
                   1445:     fprintf(msgfp,"rastcat> Right-hand ht,width,pixsz,base = %d,%d,%d,%d\n",
                   1446:     height2,width2,pixsz2,base2);
                   1447:     type_raster(sp2->image,msgfp);	/* display right-hand raster */
                   1448:     fprintf(msgfp,
1.2       albertel 1449:     "rastcat> Composite ht,width,smash,pixsz,base = %d,%d,%d,%d,%d\n",
                   1450:     height,width,nsmash,pixsz,base);
1.1       albertel 1451:     fflush(msgfp); }			/* flush msgfp buffer */
                   1452: /* -------------------------------------------------------------------------
                   1453: allocate concatted composite subraster
                   1454: -------------------------------------------------------------------------- */
                   1455: /* --- allocate returned subraster (and then initialize it) --- */
                   1456: if ( msgfp!=NULL && msglevel>=9999 )
                   1457:   { fprintf(msgfp,"rastcat> calling new_subraster(%d,%d,%d)\n",
                   1458:     width,height,pixsz); fflush(msgfp); }
                   1459: if ( (sp=new_subraster(width,height,pixsz)) /* allocate new subraster */
                   1460: ==   (subraster *)NULL )		/* failed */
                   1461:   { if ( msgfp!=NULL && msglevel>=1 )	/* report failure */
                   1462:       {	fprintf(msgfp,"rastcat> new_subraster(%d,%d,%d) failed\n",
                   1463: 	width,height,pixsz); fflush(msgfp); }
                   1464:     goto end_of_job; }			/* failed, so quit */
                   1465: /* --- initialize subraster parameters --- */
                   1466: /* sp->type = (!isstring?STRINGRASTER:ASCIISTRING); */  /*concatted string*/
                   1467: if ( !isstring )
1.2       albertel 1468:   sp->type = /*type2;*//*(type1==type2?type2:IMAGERASTER);*/
1.3     ! albertel 1469: 	(type2!=CHARASTER? type2 :
        !          1470: 	(type1!=CHARASTER&&type1!=BLANKSIGNAL
        !          1471: 	 &&type1!=FRACRASTER?type1:IMAGERASTER));
1.1       albertel 1472: else
                   1473:   sp->type = ASCIISTRING;		/* concatted ascii string */
                   1474: sp->symdef = symdef2;			/* rightmost char is sp2 */
                   1475: sp->baseline = base;			/* composite baseline */
                   1476: sp->size = sp2->size;			/* rightmost char is sp2 */
                   1477: if ( isblank )				/* need to propagate blanksignal */
                   1478:   sp->type = blanksignal;		/* may not be completely safe??? */
                   1479: /* --- extract raster from subraster --- */
                   1480: rp = sp->image;				/* raster allocated in subraster */
                   1481: /* -------------------------------------------------------------------------
                   1482: overlay sp1 and sp2 in new composite raster
                   1483: -------------------------------------------------------------------------- */
                   1484: if ( msgfp!=NULL && msglevel>=9999 )
                   1485:   { fprintf(msgfp,"rastcat> calling rastput() to concatanate left||right\n");
                   1486:     fflush(msgfp); }			/* flush msgfp buffer */
                   1487: if ( !isstring )
                   1488:  rastput (rp, sp1->image, base-base1,	/* overlay left-hand */
1.2       albertel 1489:  max2(0,nsmash-width1), 1);		/* plus any residual smash space */
1.1       albertel 1490: else
                   1491:  memcpy(rp->pixmap,(sp1->image)->pixmap,width1-1);  /*init left string*/
                   1492: if ( msgfp!=NULL && msglevel>=9999 )
                   1493:   { type_raster(sp->image,msgfp);	/* display composite raster */
                   1494:     fflush(msgfp); }			/* flush msgfp buffer */
                   1495: if ( !isstring )
1.3     ! albertel 1496:  { int	fracbase = ( isfrac?		/* baseline for punc after \frac */
        !          1497: 	max2(fraccenterline,base2):base ); /*adjust baseline or use original*/
        !          1498:    rastput (rp, sp2->image, fracbase-base2, /* overlay right-hand */
        !          1499:    max2(0,width1+space-nsmash), isopaque); /* minus any smashed space */
        !          1500:    if ( 1 && type1 == FRACRASTER	/* we're done with \frac image */
        !          1501:    &&   type2 != FRACRASTER )		/* unless we have \frac\frac */
        !          1502:      fraccenterline = NOVALUE;		/* so reset centerline signal */
        !          1503:    if ( fraccenterline != NOVALUE )	/* sp2 is a fraction */
        !          1504:      fraccenterline += (base-base2); }	/* so adjust its centerline */
1.1       albertel 1505: else
                   1506:  { strcpy((char *)(rp->pixmap)+width1-1+space,(char *)((sp2->image)->pixmap));
                   1507:    ((char *)(rp->pixmap))[width1+width2+space-2] = '\000'; } /*null-term*/
                   1508: if ( msgfp!=NULL && msglevel>=9999 )
                   1509:   { type_raster(sp->image,msgfp);	/* display composite raster */
                   1510:     fflush(msgfp); }			/* flush msgfp buffer */
                   1511: /* -------------------------------------------------------------------------
                   1512: free input if requested
                   1513: -------------------------------------------------------------------------- */
                   1514: if ( isfree > 0 )			/* caller wants input freed */
                   1515:   { if ( isfree==1 || isfree>2 ) delete_subraster(sp1);	/* free sp1 */
                   1516:     if ( isfree >= 2 ) delete_subraster(sp2); }		/* and/or sp2 */
                   1517: /* -------------------------------------------------------------------------
                   1518: Back to caller with pointer to concatted subraster or with null for error
                   1519: -------------------------------------------------------------------------- */
                   1520: end_of_job:
1.2       albertel 1521:   smashmargin = oldsmashmargin;		/* reset original smashmargin */
1.1       albertel 1522:   return ( sp );			/* back with subraster or null ptr */
                   1523: } /* --- end-of-function rastcat() --- */
                   1524: 
                   1525: 
                   1526: /* ==========================================================================
                   1527:  * Function:	rastack ( sp1, sp2, base, space, iscenter, isfree )
                   1528:  * Purpose:	Stack subrasters sp2 atop sp1, leaving both unchanged
                   1529:  *		and returning a newly-allocated subraster,
                   1530:  *		whose baseline is sp1's if base=1, or sp2's if base=2.
                   1531:  *		Frees/deletes input sp1 and/or sp2 depending on value
                   1532:  *		of isfree (0=none, 1=sp1, 2=sp2, 3=both).
                   1533:  * --------------------------------------------------------------------------
                   1534:  * Arguments:	sp1 (I)		subraster *  to lower subraster
                   1535:  *		sp2 (I)		subraster *  to upper subraster
                   1536:  *		base (I)	int containing 1 if sp1 is baseline,
                   1537:  *				or 2 if sp2 is baseline.
                   1538:  *		space (I)	int containing #rows blank space inserted
                   1539:  *				between sp1's image and sp2's image.
                   1540:  *		iscenter (I)	int containing 1 to center both sp1 and sp2
                   1541:  *				in stacked array, 0 to left-justify both
                   1542:  *		isfree (I)	int containing 1=free sp1 before return,
                   1543:  *				2=free sp2, 3=free both, 0=free none.
                   1544:  * --------------------------------------------------------------------------
                   1545:  * Returns:	( subraster * )	pointer to constructed subraster sp2 atop sp1
                   1546:  *				or  NULL for any error
                   1547:  * --------------------------------------------------------------------------
                   1548:  * Notes:
                   1549:  * ======================================================================= */
                   1550: /* --- entry point --- */
                   1551: subraster *rastack ( subraster *sp1, subraster *sp2,
                   1552: 			int base, int space, int iscenter, int isfree )
                   1553: {
                   1554: /* -------------------------------------------------------------------------
                   1555: Allocations and Declarations
                   1556: -------------------------------------------------------------------------- */
                   1557: subraster *new_subraster(), *sp=(subraster *)NULL; /* returned subraster */
                   1558: raster	*rp=(raster *)NULL;		/* new stacked raster in sp */
                   1559: int	delete_subraster();		/* in case isfree non-zero */
                   1560: int	rastput();			/* place sp1,sp2 in stacked raster */
                   1561: int	base1   = sp1->baseline,	/* baseline for lower subraster */
                   1562: 	height1 = (sp1->image)->height,	/* height for lower subraster */
                   1563: 	width1  = (sp1->image)->width,	/* width for lower subraster */
                   1564: 	pixsz1  = (sp1->image)->pixsz,	/* pixsz for lower subraster */
                   1565: 	base2   = sp2->baseline,	/* baseline for upper subraster */
                   1566: 	height2 = (sp2->image)->height,	/* height for upper subraster */
                   1567: 	width2  = (sp2->image)->width,	/* width for upper subraster */
                   1568: 	pixsz2  = (sp2->image)->pixsz;	/* pixsz for upper subraster */
                   1569: int	height=0, width=0, pixsz=0, baseline=0;	/*for stacked sp2 atop sp1*/
                   1570: mathchardef *symdef1 = sp1->symdef,	/* mathchardef of right lower char */
                   1571: 	*symdef2 = sp2->symdef;		/* mathchardef of right upper char */
                   1572: /* -------------------------------------------------------------------------
                   1573: Initialization
                   1574: -------------------------------------------------------------------------- */
                   1575: /* --- determine height, width and baseline of composite raster --- */
                   1576: height   = height1 + space + height2;	/* sum of heights plus space */
                   1577: width    = max2(width1,width2);		/* max width is overall width */
                   1578: pixsz    = max2(pixsz1,pixsz2);		/* bitmap||bytemap becomes bytemap */
                   1579: baseline = (base==1? height2+space+base1 : (base==2? base2 : 0));
                   1580: /* -------------------------------------------------------------------------
                   1581: allocate stacked composite subraster (with embedded raster)
                   1582: -------------------------------------------------------------------------- */
                   1583: /* --- allocate returned subraster (and then initialize it) --- */
                   1584: if ( (sp=new_subraster(width,height,pixsz)) /* allocate new subraster */
                   1585: ==   (subraster *)NULL ) goto end_of_job; /* failed, so quit */
                   1586: /* --- initialize subraster parameters --- */
                   1587: sp->type = IMAGERASTER;			/* stacked rasters */
                   1588: sp->symdef = (base==1? symdef1 : (base==2? symdef2 : NULL)); /* symdef */
                   1589: sp->baseline = baseline;		/* composite baseline */
                   1590: sp->size = (base==1? sp1->size : (base==2? sp2->size : NORMALSIZE)); /*size*/
                   1591: /* --- extract raster from subraster --- */
                   1592: rp = sp->image;				/* raster embedded in subraster */
                   1593: /* -------------------------------------------------------------------------
                   1594: overlay sp1 and sp2 in new composite raster
                   1595: -------------------------------------------------------------------------- */
                   1596: if ( iscenter == 1 )			/* center both sp1 and sp2 */
                   1597:   { rastput (rp, sp2->image, 0, (width-width2)/2, 1);  /* overlay upper */
                   1598:     rastput (rp, sp1->image, height2+space, (width-width1)/2, 1); } /*lower*/
                   1599: else					/* left-justify both sp1 and sp2 */
                   1600:   { rastput (rp, sp2->image, 0, 0, 1);  /* overlay upper */
                   1601:     rastput (rp, sp1->image, height2+space, 0, 1); } /*lower*/
                   1602: /* -------------------------------------------------------------------------
                   1603: free input if requested
                   1604: -------------------------------------------------------------------------- */
                   1605: if ( isfree > 0 )			/* caller wants input freed */
                   1606:   { if ( isfree==1 || isfree>2 ) delete_subraster(sp1);	/* free sp1 */
                   1607:     if ( isfree>=2 ) delete_subraster(sp2); } /* and/or sp2 */
                   1608: /* -------------------------------------------------------------------------
                   1609: Back to caller with pointer to stacked subraster or with null for error
                   1610: -------------------------------------------------------------------------- */
                   1611: end_of_job:
                   1612:   return ( sp );			/* back with subraster or null ptr */
                   1613: } /* --- end-of-function rastack() --- */
                   1614: 
                   1615: 
                   1616: /* ==========================================================================
                   1617:  * Function:	rastile ( tiles, ntiles )
                   1618:  * Purpose:	Allocate and build up a composite raster
                   1619:  *		from the ntiles components/characters supplied in tiles.
                   1620:  * --------------------------------------------------------------------------
                   1621:  * Arguments:	tiles (I)	subraster *  to array of subraster structs
                   1622:  *				describing the components and their locations
                   1623:  *		ntiles (I)	int containing number of subrasters in tiles[]
                   1624:  * --------------------------------------------------------------------------
                   1625:  * Returns:	( raster * )	ptr to composite raster,
                   1626:  *				or NULL for any error.
                   1627:  * --------------------------------------------------------------------------
                   1628:  * Notes:     o	The top,left corner of a raster is row=0,col=0
                   1629:  *		with row# increasing as you move down,
                   1630:  *		and col# increasing as you move right.
                   1631:  *		Metafont numbers rows with the baseline=0,
                   1632:  *		so the top row is a positive number that
                   1633:  *		decreases as you move down.
                   1634:  *	      o	rastile() is no longer used.
                   1635:  *		It was used by an earlier rasterize() algorithm,
                   1636:  *		and I've left it in place should it be needed again.
                   1637:  *		But recent changes haven't been tested/exercised.
                   1638:  * ======================================================================= */
                   1639: /* --- entry point --- */
                   1640: raster	*rastile ( subraster *tiles, int ntiles )
                   1641: {
                   1642: /* -------------------------------------------------------------------------
                   1643: Allocations and Declarations
                   1644: -------------------------------------------------------------------------- */
                   1645: raster	*new_raster(), *composite=(raster *)NULL;  /*raster back to caller*/
                   1646: int	width=0, height=0, pixsz=0, /*width,height,pixsz of composite raster*/
                   1647: 	toprow=9999, rightcol=-999, /* extreme upper-right corner of tiles */
                   1648: 	botrow=-999, leftcol=9999;  /* extreme lower-left corner of tiles */
                   1649: int	itile;			/* tiles[] index */
                   1650: int	rastput();		/* overlay each tile in composite raster */
                   1651: /* -------------------------------------------------------------------------
                   1652: run through tiles[] to determine dimensions for composite raster
                   1653: -------------------------------------------------------------------------- */
                   1654: /* --- determine row and column bounds of composite raster --- */
                   1655: for ( itile=0; itile<ntiles; itile++ )
                   1656:   {
                   1657:   subraster *tile = &(tiles[itile]);		/* ptr to current tile */
                   1658:   /* --- upper-left corner of composite --- */
                   1659:   toprow = min2(toprow, tile->toprow);
                   1660:   leftcol = min2(leftcol, tile->leftcol);
                   1661:   /* --- lower-right corner of composite --- */
                   1662:   botrow = max2(botrow, tile->toprow + (tile->image)->height - 1);
                   1663:   rightcol = max2(rightcol, tile->leftcol + (tile->image)->width  - 1);
                   1664:   /* --- pixsz of composite --- */
                   1665:   pixsz = max2(pixsz,(tile->image)->pixsz);
                   1666:   } /* --- end-of-for(itile) --- */
                   1667: /* --- calculate width and height from bounds --- */
                   1668: width  = rightcol - leftcol + 1;
                   1669: height = botrow - toprow + 1;
                   1670: /* --- sanity check (quit if bad dimensions) --- */
                   1671: if ( width<1 || height<1 ) goto end_of_job;
                   1672: /* -------------------------------------------------------------------------
                   1673: allocate composite raster, and embed tiles[] within it
                   1674: -------------------------------------------------------------------------- */
                   1675: /* --- allocate composite raster --- */
                   1676: if ( (composite=new_raster(width,height,pixsz))	/*allocate composite raster*/
                   1677: ==   (raster *)NULL ) goto end_of_job;		/* and quit if failed */
                   1678: /* --- embed tiles[] in composite --- */
                   1679: for ( itile=0; itile<ntiles; itile++ )
                   1680:   { subraster *tile = &(tiles[itile]);		/* ptr to current tile */
                   1681:     rastput (composite, tile->image,		/* overlay tile image at...*/
                   1682:       tile->toprow-toprow, tile->leftcol-leftcol, 1); } /*upper-left corner*/
                   1683: /* -------------------------------------------------------------------------
                   1684: Back to caller with composite raster (or null for any error)
                   1685: -------------------------------------------------------------------------- */
                   1686: end_of_job:
                   1687:   return ( composite );			/* back with composite or null ptr */
                   1688: } /* --- end-of-function rastile() --- */
                   1689: 
                   1690: 
                   1691: /* ==========================================================================
1.2       albertel 1692:  * Function:	rastsmash ( sp1, sp2 )
1.1       albertel 1693:  * Purpose:	When concatanating sp1||sp2, calculate #pixels
1.2       albertel 1694:  *		we can "smash sp2 left"
1.1       albertel 1695:  * --------------------------------------------------------------------------
                   1696:  * Arguments:	sp1 (I)		subraster *  to left-hand raster
                   1697:  *		sp2 (I)		subraster *  to right-hand raster
                   1698:  * --------------------------------------------------------------------------
1.2       albertel 1699:  * Returns:	( int )		max #pixels we can smash sp1||sp2,
1.1       albertel 1700:  *				or "blanksignal" if sp2 intentionally blank,
                   1701:  *				or 0 for any error.
                   1702:  * --------------------------------------------------------------------------
                   1703:  * Notes:     o
                   1704:  * ======================================================================= */
                   1705: /* --- entry point --- */
1.2       albertel 1706: int	rastsmash ( subraster *sp1, subraster *sp2 )
1.1       albertel 1707: {
                   1708: /* -------------------------------------------------------------------------
                   1709: Allocations and Declarations
                   1710: -------------------------------------------------------------------------- */
1.2       albertel 1711: int	nsmash = 0;			/* #pixels to smash sp1||sp2 */
1.1       albertel 1712: int	base1   = sp1->baseline,	/*baseline for left-hand subraster*/
                   1713: 	height1 = (sp1->image)->height,	/* height for left-hand subraster */
                   1714: 	width1  = (sp1->image)->width,	/* width for left-hand subraster */
                   1715: 	base2   = sp2->baseline,	/*baseline for right-hand subraster*/
                   1716: 	height2 = (sp2->image)->height,	/* height for right-hand subraster */
                   1717: 	width2  = (sp2->image)->width;	/* width for right-hand subraster */
                   1718: int	base = max2(base1,base2),	/* max ascenders - 1 above baseline*/
                   1719: 	top1=base-base1, top2=base-base2, /* top irow indexes for sp1, sp2 */
                   1720: 	bot1=top1+height1-1, bot2=top2+height2-1, /* bot irow indexes */
                   1721: 	height = max2(bot1,bot2)+1;	/* total height */
                   1722: int	irow1=0,irow2=0, icol=0;	/* row,col indexes */
                   1723: int	firstcol1[1025], nfirst1=0,	/* 1st sp1 col containing set pixel*/
                   1724: 	firstcol2[1025], nfirst2=0;	/* 1st sp2 col containing set pixel*/
                   1725: int	smin=9999, xmin=9999,ymin=9999;	/* min separation (s=x+y) */
                   1726: int	type_raster();			/* display debugging output */
                   1727: /* -------------------------------------------------------------------------
                   1728: find right edge of sp1 and left edge of sp2 (these will be abutting edges)
                   1729: -------------------------------------------------------------------------- */
                   1730: /* --- check args --- */
                   1731: if ( isstring ) goto end_of_job;	/* ignore string rasters */
1.2       albertel 1732: if ( 0 && istextmode ) goto end_of_job;	/* don't smash in text mode */
                   1733: if ( height > 1023 ) goto end_of_job;	/* don't try to smash huge image */
1.1       albertel 1734: if ( sp2->type == blanksignal )		/*blanksignal was propagated to us*/
1.2       albertel 1735:   goto end_of_job;			/* don't smash intentional blank */
1.1       albertel 1736: /* --- init firstcol1[], firstcol2[] --- */
                   1737: for ( irow1=0; irow1<height; irow1++ )	/* for each row */
                   1738:   firstcol1[irow1] = firstcol2[irow1] = blanksignal; /* signal empty rows */
                   1739: /* --- set firstcol2[] indicating left edge of sp2 --- */
                   1740: for ( irow2=top2; irow2<=bot2; irow2++ ) /* for each row inside sp2 */
                   1741:   for ( icol=0; icol<width2; icol++ )	/* find first non-empty col in row */
                   1742:     if ( getpixel(sp2->image,irow2-top2,icol) != 0 ) /* found a set pixel */
                   1743:       {	firstcol2[irow2] = icol;	/* icol is #cols from left edge */
                   1744: 	nfirst2++;			/* bump #rows containing set pixels*/
                   1745: 	break; }			/* and go on to next row */
                   1746: if ( nfirst2 < 1 )			/*right-hand sp2 is completely blank*/
1.2       albertel 1747:   { nsmash = blanksignal;		/* signal intentional blanks */
                   1748:     goto end_of_job; }			/* don't smash intentional blanks */
1.1       albertel 1749: /* --- now check if preceding image in sp1 was an intentional blank --- */
                   1750: if ( sp1->type == blanksignal )		/*blanksignal was propagated to us*/
1.2       albertel 1751:   goto end_of_job;			/* don't smash intentional blank */
1.1       albertel 1752: /* --- set firstcol1[] indicating right edge of sp1 --- */
                   1753: for ( irow1=top1; irow1<=bot1; irow1++ ) /* for each row inside sp1 */
                   1754:   for ( icol=width1-1; icol>=0; icol-- ) /* find last non-empty col in row */
                   1755:     if ( getpixel(sp1->image,irow1-top1,icol) != 0 ) /* found a set pixel */
                   1756:       {	firstcol1[irow1] = (width1-1)-icol; /* save #cols from right edge */
                   1757: 	nfirst1++;			/* bump #rows containing set pixels*/
                   1758: 	break; }			/* and go on to next row */
                   1759: if ( nfirst1 < 1 )			/*left-hand sp1 is completely blank*/
1.2       albertel 1760:   goto end_of_job;			/* don't smash intentional blanks */
1.1       albertel 1761: /* -------------------------------------------------------------------------
                   1762: find minimum separation
                   1763: -------------------------------------------------------------------------- */
                   1764: for ( irow2=top2; irow2<=bot2; irow2++ ) { /* check each row inside sp2 */
                   1765:  int margin1, margin2=firstcol2[irow2];	/* #cols to first set pixel */
1.3     ! albertel 1766:  if ( margin2 != blanksignal ) {	/* irow2 not an empty/blank row */
1.1       albertel 1767:   for ( irow1=max2(irow2-smin,top1); ; irow1++ )
                   1768:    if ( irow1 > min2(irow2+smin,bot1) ) break; /* upper bound check */
                   1769:    else
                   1770:     if ( (margin1=firstcol1[irow1]) != blanksignal ) { /*have non-blank row*/
                   1771:      int dx=(margin1+margin2), dy=absval(irow2-irow1), ds=dx+dy; /* deltas */
                   1772:      if ( ds >= smin ) continue;	/* min unchanged */
1.2       albertel 1773:      if ( dy>smashmargin && dx<xmin && smin<9999 ) continue; /* dy alone */
1.1       albertel 1774:      smin=ds; xmin=dx; ymin=dy;		/* set new min */
                   1775:      } /* --- end-of-if(margin1!=blanksignal) --- */
1.3     ! albertel 1776:   } /* --- end-of-if(margin2!=blanksignal) --- */
1.2       albertel 1777:  if ( smin<2 ) goto end_of_job;		/* can't smash */
1.1       albertel 1778:  } /* --- end-of-for(irow2) --- */
1.2       albertel 1779: /*nsmash = min2(xmin,width2);*/		/* permissible smash */
                   1780: nsmash = xmin;				/* permissible smash */
1.1       albertel 1781: /* -------------------------------------------------------------------------
1.2       albertel 1782: Back to caller with #pixels to smash sp1||sp2
1.1       albertel 1783: -------------------------------------------------------------------------- */
                   1784: end_of_job:
                   1785:   /* --- debugging output --- */
                   1786:   if ( msgfp!=NULL && msglevel >= 99 )	/* display for debugging */
1.2       albertel 1787:     { fprintf(msgfp,"rastsmash> nsmash=%d, smashmargin=%d\n",
                   1788:       nsmash,smashmargin);
1.1       albertel 1789:       if ( msglevel >= 999 )		/* also display rasters */
1.2       albertel 1790: 	{ fprintf(msgfp,"rastsmash>left-hand image...\n");
1.1       albertel 1791: 	  if(sp1!=NULL) type_raster(sp1->image,msgfp); /* left image */
1.2       albertel 1792: 	  fprintf(msgfp,"rastsmash>right-hand image...\n");
1.1       albertel 1793: 	  if(sp2!=NULL) type_raster(sp2->image,msgfp); } /* right image */
                   1794:       fflush(msgfp); }
1.2       albertel 1795:   return ( nsmash );			/* back with #smash pixels */
                   1796: } /* --- end-of-function rastsmash() --- */
1.1       albertel 1797: 
                   1798: 
                   1799: /* ==========================================================================
1.3     ! albertel 1800:  * Function:	rastsmashcheck ( term )
        !          1801:  * Purpose:	Check an exponent term to see if its leading symbol
        !          1802:  *		would make smashing dangerous
        !          1803:  * --------------------------------------------------------------------------
        !          1804:  * Arguments:	term (I)	char *  to null-terminated string
        !          1805:  *				containing right-hand exponent term about to
        !          1806:  *				be smashed against existing left-hand.
        !          1807:  * --------------------------------------------------------------------------
        !          1808:  * Returns:	( int )		1 if it's okay to smash term, or
        !          1809:  *				0 if smash is dangerous.
        !          1810:  * --------------------------------------------------------------------------
        !          1811:  * Notes:     o
        !          1812:  * ======================================================================= */
        !          1813: /* --- entry point --- */
        !          1814: int	rastsmashcheck ( char *term )
        !          1815: {
        !          1816: /* -------------------------------------------------------------------------
        !          1817: Allocations and Declarations
        !          1818: -------------------------------------------------------------------------- */
        !          1819: int	isokay = 0;		/* 1 to signal okay to caller */
        !          1820: static	char nosmashchars[64] = "-.,="; /* don't smash these leading chars */
        !          1821: static	char *nosmashstrs[64] = { "\\frac", NULL }; /* or leading strings */
        !          1822: static	char *grayspace[64] = { "\\tiny", "\\small", "\\normalsize",
        !          1823: 	"\\large", "\\Large", "\\LARGE", "\\huge", "\\Huge", NULL };
        !          1824: char	*expression = term;	/* local ptr to beginning of expression */
        !          1825: char	*token = NULL;  int i;	/* token = nosmashstrs[i] or grayspace[i] */
        !          1826: /* -------------------------------------------------------------------------
        !          1827: see if smash check enabled
        !          1828: -------------------------------------------------------------------------- */
        !          1829: if ( smashcheck < 1 ) {		/* no smash checking wanted */
        !          1830:   if ( smashcheck >= 0 )	/* -1 means check should always fail */
        !          1831:     isokay = 1;			/* otherwise (if 0), signal okay to smash */
        !          1832:   goto end_of_job; }		/* return to caller */
        !          1833: /* -------------------------------------------------------------------------
        !          1834: skip leading white and gray space
        !          1835: -------------------------------------------------------------------------- */
        !          1836: /* --- first check input --- */
        !          1837: if ( term == NULL )    goto end_of_job; /* no input so return 0 to caller */
        !          1838: if ( *term == '\000' ) goto end_of_job; /* ditto for empty string */
        !          1839: /* --- skip leading white space --- */
        !          1840: skipwhite(term);		/* skip leading white sapce */
        !          1841: if ( *term == '\000' ) goto end_of_job; /* nothing but white space */
        !          1842: /* --- skip leading gray space --- */
        !          1843: skipgray:
        !          1844:  for ( i=0; (token=grayspace[i]) != NULL; i++ ) /* check each grayspace */
        !          1845:   if ( strncmp(term,token,strlen(token)) == 0 ) { /* found grayspace */
        !          1846:    term += strlen(token);	/* skip past this grayspace token */
        !          1847:    if ( *term == '\000' ) {	/* nothing left so quit */
        !          1848:      if ( msgfp!=NULL && msglevel >= 99 ) /* display for debugging */
        !          1849:        fprintf(msgfp,"rastsmashcheck> only grayspace in %.32s\n",expression);
        !          1850:      goto end_of_job; }
        !          1851:    goto skipgray; }		/* restart grayspace check from beginning */
        !          1852: /* -------------------------------------------------------------------------
        !          1853: check for leading no-smash single char
        !          1854: -------------------------------------------------------------------------- */
        !          1855: /* --- don't smash if term begins with a "nosmash" char --- */
        !          1856: if ( (token=strchr(nosmashchars,*term)) != NULL ) {
        !          1857:   if ( msgfp!=NULL && msglevel >= 99 )	/* display for debugging */
        !          1858:     fprintf(msgfp,"rastsmashcheck> char %.1s found in %.32s\n",token,term);
        !          1859:   goto end_of_job; }
        !          1860: /* -------------------------------------------------------------------------
        !          1861: check for leading no-smash token
        !          1862: -------------------------------------------------------------------------- */
        !          1863: for ( i=0; (token=nosmashstrs[i]) != NULL; i++ ) /* check each nosmashstr */
        !          1864:  if ( strncmp(term,token,strlen(token)) == 0 ) { /* found a nosmashstr */
        !          1865:   if ( msgfp!=NULL && msglevel >= 99 )	/* display for debugging */
        !          1866:     fprintf(msgfp,"rastsmashcheck> token %s found in %.32s\n",token,term);
        !          1867:   goto end_of_job; }		/* so don't smash term */
        !          1868: /* -------------------------------------------------------------------------
        !          1869: back to caller
        !          1870: -------------------------------------------------------------------------- */
        !          1871: isokay = 1;			/* no problem, so signal okay to smash */
        !          1872: end_of_job:
        !          1873:   if ( msgfp!=NULL && msglevel >= 999 )	/* display for debugging */
        !          1874:     fprintf(msgfp,"rastsmashcheck> returning isokay=%d for \"%.32s\"\n",
        !          1875:     isokay,(expression==NULL?"<no input>":expression));
        !          1876:   return ( isokay );		/* back to caller with 1 if okay to smash */
        !          1877: } /* --- end-of-function rastsmashcheck() --- */
        !          1878: 
        !          1879: 
        !          1880: /* ==========================================================================
1.1       albertel 1881:  * Function:	accent_subraster ( accent, width, height, pixsz )
                   1882:  * Purpose:	Allocate a new subraster of width x height
                   1883:  *		(or maybe different dimensions, depending on accent),
                   1884:  *		and draw an accent (\hat or \vec or \etc) that fills it
                   1885:  * --------------------------------------------------------------------------
                   1886:  * Arguments:	accent (I)	int containing either HATACCENT or VECACCENT,
                   1887:  *				etc, indicating the type of accent desired
                   1888:  *		width (I)	int containing desired width of accent (#cols)
                   1889:  *		height (I)	int containing desired height of accent(#rows)
                   1890:  *		pixsz (I)	int containing 1 for bitmap, 8 for bytemap
                   1891:  * --------------------------------------------------------------------------
                   1892:  * Returns:	( subraster * )	ptr to newly-allocated subraster with accent,
                   1893:  *				or NULL for any error.
                   1894:  * --------------------------------------------------------------------------
                   1895:  * Notes:     o	Some accents have internally-determined dimensions,
                   1896:  *		and caller should check dimensions in returned subraster
                   1897:  * ======================================================================= */
                   1898: /* --- entry point --- */
                   1899: subraster *accent_subraster (  int accent, int width, int height, int pixsz )
                   1900: {
                   1901: /* -------------------------------------------------------------------------
                   1902: Allocations and Declarations
                   1903: -------------------------------------------------------------------------- */
                   1904: /* --- general info --- */
                   1905: raster	*new_raster(), *rp=NULL;	/*raster containing desired accent*/
                   1906: subraster *new_subraster(), *sp=NULL;	/* subraster returning accent */
                   1907: int	delete_raster(), delete_subraster(); /*free allocated raster on err*/
                   1908: int	line_raster(),			/* draws lines */
1.2       albertel 1909: 	rule_raster(),			/* draw solid boxes */
1.1       albertel 1910: 	thickness = 1;			/* line thickness */
                   1911: /*int	pixval = (pixsz==1? 1 : (pixsz==8?255:(-1)));*/ /*black pixel value*/
                   1912: /* --- other working info --- */
                   1913: int	col0, col1,			/* cols for line */
                   1914: 	row0, row1;			/* rows for line */
                   1915: subraster *get_delim(), *accsp=NULL;	/*find suitable cmex10 symbol/accent*/
                   1916: /* --- info for under/overbraces, tildes, etc --- */
                   1917: char	brace[16];			/*"{" for over, "}" for under, etc*/
                   1918: raster	*rastrot(),			/* rotate { for overbrace, etc */
                   1919: 	*rastcpy();			/* may need copy of original */
                   1920: subraster *arrow_subraster();		/* rightarrow for vec */
                   1921: subraster *rastack();			/* stack accent atop extra space */
                   1922: /* -------------------------------------------------------------------------
                   1923: outer switch() traps accents that may change caller's height,width
                   1924: -------------------------------------------------------------------------- */
                   1925: switch ( accent )
                   1926:  {
                   1927:  default:
                   1928:   /* -----------------------------------------------------------------------
                   1929:   inner switch() first allocates fixed-size raster for accents that don't
                   1930:   ------------------------------------------------------------------------ */
                   1931:   if ( (rp = new_raster(width,height,pixsz)) /* allocate fixed-size raster */
                   1932:   !=   NULL )				/* and if we succeeded... */
                   1933:    switch ( accent )			/* ...draw requested accent in it */
                   1934:     {
                   1935:     /* --- unrecognized request --- */
                   1936:     default: delete_raster(rp);		/* unrecognized accent requested */
                   1937: 	rp = NULL;  break;		/* so free raster and signal error */
                   1938:     /* --- bar request --- */
                   1939:     case UNDERBARACCENT:
                   1940:     case BARACCENT:
1.2       albertel 1941: 	thickness = 1; /*height-1;*/	/* adjust thickness */
1.1       albertel 1942: 	if ( accent == BARACCENT )	/* bar is above expression */
1.2       albertel 1943: 	 { row0 = row1 = max2(height-3,0); /* row numbers for overbar */
                   1944: 	   line_raster(rp,row0,0,row1,width-1,thickness); } /*blanks at bot*/
1.1       albertel 1945: 	else				/* underbar is below expression */
1.2       albertel 1946: 	 { row0 = row1 = min2(2,height-1); /* row numbers for underbar */
                   1947: 	   line_raster(rp,row0,0,row1,width-1,thickness); } /*blanks at top*/
1.1       albertel 1948: 	break;
                   1949:     /* --- dot request --- */
                   1950:     case DOTACCENT:
                   1951: 	thickness = height-1;		/* adjust thickness */
1.2       albertel 1952: 	/*line_raster(rp,0,width/2,1,(width/2)+1,thickness);*//*centered dot*/
                   1953: 	rule_raster(rp,0,(width+1-thickness)/2,thickness,thickness,3); /*box*/
1.1       albertel 1954: 	break;
                   1955:     /* --- ddot request --- */
                   1956:     case DDOTACCENT:
                   1957: 	thickness = height-1;		/* adjust thickness */
1.2       albertel 1958: 	col0 = max2((width+1)/3-(thickness/2)-1,0); /* one-third of width */
                   1959: 	col1 = min2((2*width+1)/3-(thickness/2)+1,width-thickness); /*2/3rds*/
                   1960: 	if ( col0+thickness >= col1 )	/* dots overlap */
                   1961: 	  { col0 = max2(col0-1,0);	/* try moving left dot more left */
                   1962: 	    col1 = min2(col1+1,width-thickness); } /* and right dot right */
                   1963: 	if ( col0+thickness >= col1 )	/* dots _still_ overlap */
                   1964: 	  thickness = max2(thickness-1,1); /* so try reducing thickness */
                   1965: 	/*line_raster(rp,0,col0,1,col0+1,thickness);*//*set dot at 1st third*/
                   1966: 	/*line_raster(rp,0,col1,1,col1+1,thickness);*//*and another at 2nd*/
                   1967: 	rule_raster(rp,0,col0,thickness,thickness,3); /*box at 1st third*/
                   1968: 	rule_raster(rp,0,col1,thickness,thickness,3); /*box at 2nd third*/
1.1       albertel 1969: 	break;
                   1970:     /* --- hat request --- */
                   1971:     case HATACCENT:
1.2       albertel 1972: 	thickness = 1; /*(width<=12? 2 : 3);*/	/* adjust thickness */
1.1       albertel 1973: 	line_raster(rp,height-1,0,0,width/2,thickness);    /* / part of hat*/
                   1974: 	line_raster(rp,0,(width-1)/2,height-1,width-1,thickness); /* \ part*/
                   1975: 	break;
                   1976:     /* --- sqrt request --- */
                   1977:     case SQRTACCENT:
                   1978: 	col1 = SQRTWIDTH(height) - 1;	/* right col of sqrt symbol */
                   1979: 	col0 = (col1+2)/3;		/* midpoint col of sqrt */
                   1980: 	row0 = (height+1)/2;		/* midpoint row of sqrt */
                   1981: 	row1 = height-1;		/* bottom row of sqrt */
                   1982: 	line_raster(rp,row0,0,row1,col0,thickness); /* descending portion */
                   1983: 	line_raster(rp,row1,col0,0,col1,thickness); /* ascending portion */
                   1984: 	line_raster(rp,0,col1,0,width-1,thickness); /*overbar of thickness 1*/
                   1985: 	break;
                   1986:     } /* --- end-of-inner-switch(accent) --- */
                   1987:     break;				/* break from outer accent switch */
                   1988:  /* --- underbrace, overbrace request --- */
                   1989:  case UNDERBRACE:
                   1990:  case OVERBRACE:
                   1991:     if ( accent == UNDERBRACE ) strcpy(brace,"}"); /* start with } brace */
                   1992:     if ( accent ==  OVERBRACE ) strcpy(brace,"{"); /* start with { brace */
                   1993:     if ( (accsp=get_delim(brace,width,CMEX10)) /* use width for height */
                   1994:     !=  NULL )				/* found desired brace */
                   1995:       { rp = rastrot(accsp->image);	/* rotate 90 degrees clockwise */
                   1996: 	delete_subraster(accsp); }	/* and free subraster "envelope" */
                   1997:     break;
                   1998:  /* --- hat request --- */
                   1999:  case HATACCENT:
                   2000:     if ( accent == HATACCENT ) strcpy(brace,"<"); /* start with < */
                   2001:     if ( (accsp=get_delim(brace,width,CMEX10)) /* use width for height */
                   2002:     !=  NULL )				/* found desired brace */
                   2003:       { rp = rastrot(accsp->image);	/* rotate 90 degrees clockwise */
                   2004: 	delete_subraster(accsp); }	/* and free subraster "envelope" */
                   2005:     break;
                   2006:  /* --- vec request --- */
                   2007:  case VECACCENT:
                   2008:     height = 2*(height/2) + 1;		/* force height odd */
                   2009:     if ( (accsp=arrow_subraster(width,height,pixsz,1,0)) /*build rightarrow*/
                   2010:     !=  NULL )				/* succeeded */
                   2011: 	{ rp = accsp->image;		/* "extract" raster with bitmap */
                   2012: 	  free((void *)accsp); }	/* and free subraster "envelope" */
                   2013:     break;
                   2014:  /* --- tilde request --- */
                   2015:  case TILDEACCENT:
                   2016:     accsp=(width<25? get_delim("\\sim",-width,CMSY10) :
                   2017: 		     get_delim("~",-width,CMEX10)); /*width search for tilde*/
                   2018:     if ( accsp !=  NULL )		/* found desired tilde */
                   2019:       if ( (sp=rastack(new_subraster(1,1,pixsz),accsp,1,0,1,3))/*space below*/
                   2020:       !=  NULL )			/* have tilde with space below it */
                   2021: 	{ rp = sp->image;		/* "extract" raster with bitmap */
1.2       albertel 2022: 	  free((void *)sp);		/* and free subraster "envelope" */
                   2023: 	  leftsymdef = NULL; }		/* so \tilde{x}^2 works properly */
1.1       albertel 2024:     break;
                   2025:  } /* --- end-of-outer-switch(accent) --- */
                   2026: /* -------------------------------------------------------------------------
                   2027: if we constructed accent raster okay, embed it in a subraster and return it
                   2028: -------------------------------------------------------------------------- */
                   2029: /* --- if all okay, allocate subraster to contain constructed raster --- */
1.3     ! albertel 2030: if ( rp != NULL ) {			/* accent raster constructed okay */
1.1       albertel 2031:   if ( (sp=new_subraster(0,0,0))	/* allocate subraster "envelope" */
                   2032:   ==   NULL )				/* and if we fail to allocate */
                   2033:     delete_raster(rp);			/* free now-unneeded raster */
                   2034:   else					/* subraster allocated okay */
                   2035:     { /* --- init subraster parameters, embedding raster in it --- */
                   2036:       sp->type = IMAGERASTER;		/* constructed image */
                   2037:       sp->image = rp;			/* raster we just constructed */
                   2038:       sp->size = (-1);			/* can't set font size here */
                   2039:       sp->baseline = 0; }		/* can't set baseline here */
1.3     ! albertel 2040:   } /* --- end-of-if(rp!=NULL) --- */
1.1       albertel 2041: /* --- return subraster containing desired accent to caller --- */
                   2042: return ( sp );				/* return accent or NULL to caller */
                   2043: } /* --- end-of-function accent_subraster() --- */
                   2044: 
                   2045: 
                   2046: /* ==========================================================================
                   2047:  * Function:	arrow_subraster ( width, height, pixsz, drctn, isBig )
                   2048:  * Purpose:	Allocate a raster/subraster and draw left/right arrow in it
                   2049:  * --------------------------------------------------------------------------
                   2050:  * Arguments:	width (I)	int containing number of cols for arrow
                   2051:  *		height (I)	int containing number of rows for arrow
                   2052:  *		pixsz (I)	int containing 1 for bitmap, 8 for bytemap
                   2053:  *		drctn (I)	int containing +1 for right arrow,
                   2054:  *				or -1 for left, 0 for leftright
                   2055:  *		isBig (I)	int containing 1/true for \Long arrows,
                   2056:  *				or false for \long arrows, i.e.,
                   2057:  *				true for ===> or false for --->.
                   2058:  * --------------------------------------------------------------------------
                   2059:  * Returns:	( subraster * )	ptr to constructed left/right arrow
                   2060:  *				or NULL for any error.
                   2061:  * --------------------------------------------------------------------------
                   2062:  * Notes:     o
                   2063:  * ======================================================================= */
                   2064: /* --- entry point --- */
                   2065: subraster *arrow_subraster ( int width, int height, int pixsz,
                   2066: 				int drctn, int isBig )
                   2067: {
                   2068: /* -------------------------------------------------------------------------
                   2069: Allocations and Declarations
                   2070: -------------------------------------------------------------------------- */
                   2071: subraster *new_subraster(), *arrowsp=NULL; /* allocate arrow subraster */
                   2072: int	rule_raster();			/* draw arrow line */
                   2073: int	irow, midrow=height/2;		/* index, midrow is arrowhead apex */
1.2       albertel 2074: int	icol, thickness=(height>15?2:2); /* arrowhead thickness and index */
1.1       albertel 2075: int	pixval = (pixsz==1? 1 : (pixsz==8?255:(-1))); /* black pixel value */
                   2076: int	ipix,				/* raster pixmap[] index */
                   2077: 	npix = width*height;		/* #pixels malloced in pixmap[] */
                   2078: /* -------------------------------------------------------------------------
                   2079: allocate raster/subraster and draw arrow line
                   2080: -------------------------------------------------------------------------- */
                   2081: if ( height < 3 ) { height=3; midrow=1; }	/* set minimum height */
                   2082: if ( (arrowsp=new_subraster(width,height,pixsz)) /* allocate empty raster */
                   2083: ==   NULL ) goto end_of_job;			/* and quit if failed */
                   2084: if ( !isBig )					/* single line */
                   2085:   rule_raster(arrowsp->image,midrow,0,width,1,0); /*draw line across midrow*/
                   2086: else
                   2087:   { int	delta = (width>6? (height>15? 3: (height>7? 2 : 1)) : 1);
                   2088:     rule_raster(arrowsp->image,midrow-delta,delta,width-2*delta,1,0);
                   2089:     rule_raster(arrowsp->image,midrow+delta,delta,width-2*delta,1,0); }
                   2090: /* -------------------------------------------------------------------------
                   2091: construct arrowhead(s)
                   2092: -------------------------------------------------------------------------- */
                   2093: for ( irow=0; irow<height; irow++ )		/* for each row of arrow */
                   2094:   {
                   2095:   int	delta = abs(irow-midrow);		/*arrowhead offset for irow*/
                   2096:   /* --- right arrowhead --- */
                   2097:   if ( drctn >= 0 )				/* right arrowhead wanted */
                   2098:     for ( icol=0; icol<thickness; icol++ )	/* for arrowhead thickness */
                   2099:      { ipix = ((irow+1)*width - 1) - delta - icol; /* rightmost-delta-icol */
1.3     ! albertel 2100:        if ( ipix >= 0 ) {				/* bounds check */
1.1       albertel 2101: 	if ( pixsz == 1 )			/* have a bitmap */
                   2102: 	  setlongbit((arrowsp->image)->pixmap,ipix);/*turn on arrowhead bit*/
                   2103: 	else					/* should have a bytemap */
                   2104: 	 if ( pixsz == 8 )			/* check pixsz for bytemap */
1.3     ! albertel 2105: 	  ((arrowsp->image)->pixmap)[ipix] = pixval; } }/*set arrowhead byte*/
1.1       albertel 2106:   /* --- left arrowhead (same as right except for ipix calculation) --- */
                   2107:   if ( drctn <= 0 )				/* left arrowhead wanted */
                   2108:     for ( icol=0; icol<thickness; icol++ )	/* for arrowhead thickness */
                   2109:      { ipix = irow*width + delta + icol;	/* leftmost bit+delta+icol */
1.3     ! albertel 2110:        if ( ipix < npix ) {			/* bounds check */
1.1       albertel 2111: 	if ( pixsz == 1 )			/* have a bitmap */
                   2112: 	  setlongbit((arrowsp->image)->pixmap,ipix);/*turn on arrowhead bit*/
                   2113: 	else					/* should have a bytemap */
                   2114: 	 if ( pixsz == 8 )			/* check pixsz for bytemap */
1.3     ! albertel 2115: 	  ((arrowsp->image)->pixmap)[ipix] = pixval; } }/*set arrowhead byte*/
1.1       albertel 2116:   } /* --- end-of-for(irow) --- */
                   2117: end_of_job:
                   2118:   return ( arrowsp );			/*back to caller with arrow or NULL*/
                   2119: } /* --- end-of-function arrow_subraster() --- */
                   2120: 
                   2121: 
                   2122: /* ==========================================================================
                   2123:  * Function:	uparrow_subraster ( width, height, pixsz, drctn, isBig )
                   2124:  * Purpose:	Allocate a raster/subraster and draw up/down arrow in it
                   2125:  * --------------------------------------------------------------------------
                   2126:  * Arguments:	width (I)	int containing number of cols for arrow
                   2127:  *		height (I)	int containing number of rows for arrow
                   2128:  *		pixsz (I)	int containing 1 for bitmap, 8 for bytemap
                   2129:  *		drctn (I)	int containing +1 for up arrow,
                   2130:  *				or -1 for down, or 0 for updown
                   2131:  *		isBig (I)	int containing 1/true for \Long arrows,
                   2132:  *				or false for \long arrows, i.e.,
                   2133:  *				true for ===> or false for --->.
                   2134:  * --------------------------------------------------------------------------
                   2135:  * Returns:	( subraster * )	ptr to constructed up/down arrow
                   2136:  *				or NULL for any error.
                   2137:  * --------------------------------------------------------------------------
                   2138:  * Notes:     o
                   2139:  * ======================================================================= */
                   2140: /* --- entry point --- */
                   2141: subraster *uparrow_subraster ( int width, int height, int pixsz,
                   2142: 					int drctn, int isBig )
                   2143: {
                   2144: /* -------------------------------------------------------------------------
                   2145: Allocations and Declarations
                   2146: -------------------------------------------------------------------------- */
                   2147: subraster *new_subraster(), *arrowsp=NULL; /* allocate arrow subraster */
                   2148: int	rule_raster();			/* draw arrow line */
                   2149: int	icol, midcol=width/2;		/* index, midcol is arrowhead apex */
1.2       albertel 2150: int	irow, thickness=(width>15?2:2);	/* arrowhead thickness and index */
1.1       albertel 2151: int	pixval = (pixsz==1? 1 : (pixsz==8?255:(-1))); /* black pixel value */
                   2152: int	ipix,				/* raster pixmap[] index */
                   2153: 	npix = width*height;		/* #pixels malloced in pixmap[] */
                   2154: /* -------------------------------------------------------------------------
                   2155: allocate raster/subraster and draw arrow line
                   2156: -------------------------------------------------------------------------- */
                   2157: if ( width < 3 ) { width=3; midcol=1; }		/* set minimum width */
                   2158: if ( (arrowsp=new_subraster(width,height,pixsz)) /* allocate empty raster */
                   2159: ==   NULL ) goto end_of_job;			/* and quit if failed */
                   2160: if ( !isBig )					/* single line */
                   2161:   rule_raster(arrowsp->image,0,midcol,1,height,0); /*draw line down midcol*/
                   2162: else
                   2163:   { int	delta = (height>6? (width>15? 3: (width>7? 2 : 1)) : 1);
                   2164:     rule_raster(arrowsp->image,delta,midcol-delta,1,height-2*delta,0);
                   2165:     rule_raster(arrowsp->image,delta,midcol+delta,1,height-2*delta,0); }
                   2166: /* -------------------------------------------------------------------------
                   2167: construct arrowhead(s)
                   2168: -------------------------------------------------------------------------- */
                   2169: for ( icol=0; icol<width; icol++ )		/* for each col of arrow */
                   2170:   {
                   2171:   int	delta = abs(icol-midcol);		/*arrowhead offset for icol*/
                   2172:   /* --- up arrowhead --- */
                   2173:   if ( drctn >= 0 )				/* up arrowhead wanted */
                   2174:     for ( irow=0; irow<thickness; irow++ )	/* for arrowhead thickness */
                   2175:      { ipix = (irow+delta)*width + icol;	/* leftmost+icol */
1.3     ! albertel 2176:        if ( ipix < npix ) {			/* bounds check */
1.1       albertel 2177: 	if ( pixsz == 1 )			/* have a bitmap */
                   2178: 	  setlongbit((arrowsp->image)->pixmap,ipix);/*turn on arrowhead bit*/
                   2179: 	else					/* should have a bytemap */
                   2180: 	 if ( pixsz == 8 )			/* check pixsz for bytemap */
1.3     ! albertel 2181: 	  ((arrowsp->image)->pixmap)[ipix] = pixval; } }/*set arrowhead byte*/
1.1       albertel 2182:   /* --- down arrowhead (same as up except for ipix calculation) --- */
                   2183:   if ( drctn <= 0 )				/* down arrowhead wanted */
                   2184:     for ( irow=0; irow<thickness; irow++ )	/* for arrowhead thickness */
                   2185:      { ipix = (height-1-delta-irow)*width + icol; /* leftmost + icol */
1.3     ! albertel 2186:        if ( ipix > 0 ) {			/* bounds check */
1.1       albertel 2187: 	if ( pixsz == 1 )			/* have a bitmap */
                   2188: 	  setlongbit((arrowsp->image)->pixmap,ipix);/*turn on arrowhead bit*/
                   2189: 	else					/* should have a bytemap */
                   2190: 	 if ( pixsz == 8 )			/* check pixsz for bytemap */
1.3     ! albertel 2191: 	  ((arrowsp->image)->pixmap)[ipix] = pixval; } }/*set arrowhead byte*/
1.2       albertel 2192:   } /* --- end-of-for(icol) --- */
1.1       albertel 2193: end_of_job:
                   2194:   return ( arrowsp );			/*back to caller with arrow or NULL*/
                   2195: } /* --- end-of-function uparrow_subraster() --- */
                   2196: 
                   2197: 
                   2198: /* ==========================================================================
                   2199:  * Function:	rule_raster ( rp, top, left, width, height, type )
                   2200:  * Purpose:	Draw a solid or dashed line (or box) in existing raster rp,
                   2201:  *		starting at top,left with dimensions width,height.
                   2202:  * --------------------------------------------------------------------------
                   2203:  * Arguments:	rp (I)		raster *  to raster in which rule
                   2204:  *				will be drawn
                   2205:  *		top (I)		int containing row at which top-left corner
                   2206:  *				of rule starts (0 is topmost)
                   2207:  *		left (I)	int containing col at which top-left corner
                   2208:  *				of rule starts (0 is leftmost)
                   2209:  *		width (I)	int containing number of cols for rule
                   2210:  *		height (I)	int containing number of rows for rule
                   2211:  *		type (I)	int containing 0 for solid rule,
                   2212:  *				1 for horizontal dashes, 2 for vertical
1.3     ! albertel 2213:  *				3 for solid rule with corners removed (bevel)
        !          2214:  *				4 for strut (nothing drawn)
1.1       albertel 2215:  * --------------------------------------------------------------------------
                   2216:  * Returns:	( int )		1 if rule drawn okay,
                   2217:  *				or 0 for any error.
                   2218:  * --------------------------------------------------------------------------
                   2219:  * Notes:     o	Rule line is implicitly "horizontal" or "vertical" depending
                   2220:  *		on relative width,height dimensions.  It's a box if they're
                   2221:  *		more or less comparable.
                   2222:  * ======================================================================= */
                   2223: /* --- entry point --- */
                   2224: int	rule_raster ( raster *rp, int top, int left,
                   2225: 		int width, int height, int type )
                   2226: {
                   2227: /* -------------------------------------------------------------------------
                   2228: Allocations and Declarations
                   2229: -------------------------------------------------------------------------- */
1.2       albertel 2230: int	irow=0, icol=0;		/* indexes over rp raster */
                   2231: int	ipix = 0,		/* raster pixmap[] index */
1.1       albertel 2232: 	npix = rp->width * rp->height; /* #pixels malloced in rp->pixmap[] */
                   2233: int	isfatal = 0;		/* true to abend on out-of-bounds error */
1.3     ! albertel 2234: int	hdash=1, vdash=2,	/* type for horizontal, vertical dashes */
        !          2235: 	bevel=99/*3*/, strut=4;	/* type for bevel (turned off), strut */
1.1       albertel 2236: int	dashlen=3, spacelen=2,	/* #pixels for dash followed by space */
                   2237: 	isdraw=1;		/* true when drawing dash (init for solid) */
                   2238: /* -------------------------------------------------------------------------
                   2239: Check args
                   2240: -------------------------------------------------------------------------- */
1.3     ! albertel 2241: if ( rp == (raster *)NULL ) {	/* no raster arg supplied */
1.1       albertel 2242:   if ( workingbox != (subraster *)NULL )  /* see if we have a workingbox */
                   2243:     rp = workingbox->image;	/* use workingbox if possible */
1.3     ! albertel 2244:   else return ( 0 ); }		/* otherwise signal error to caller */
        !          2245: if ( type == bevel )		/* remove corners of solid box */
1.2       albertel 2246:   if ( width<3 || height<3 ) type=0; /* too small to remove corners */
1.1       albertel 2247: /* -------------------------------------------------------------------------
                   2248: Fill line/box
                   2249: -------------------------------------------------------------------------- */
1.3     ! albertel 2250: if ( width > 0 )				/* zero width implies strut*/
        !          2251:  for ( irow=top; irow<top+height; irow++ )	/* for each scan line */
1.1       albertel 2252:   {
1.3     ! albertel 2253:   if ( type == strut ) isdraw = 0;		/* draw nothing for strut */
1.1       albertel 2254:   if ( type == vdash )				/*set isdraw for vert dash*/
                   2255:     isdraw = (((irow-top)%(dashlen+spacelen)) < dashlen);
                   2256:   ipix = irow*rp->width + left - 1;		/*first pixel preceding icol*/
                   2257:   for ( icol=left; icol<left+width; icol++ )	/* each pixel in scan line */
                   2258:     {
1.3     ! albertel 2259:     if ( type == bevel ) {			/* remove corners of box */
1.2       albertel 2260:       if ( (irow==top && icol==left)		/* top-left corner */
                   2261:       ||   (irow==top && icol>=left+width-1)	/* top-right corner */
                   2262:       ||   (irow>=top+height-1 && icol==left)	/* bottom-left corner */
                   2263:       ||   (irow>=top+height-1 && icol>=left+width-1) ) /* bottom-right */
1.3     ! albertel 2264: 	isdraw = 0;  else isdraw = 1; }		/*set isdraw to skip corner*/
1.1       albertel 2265:     if ( type == hdash )			/*set isdraw for horiz dash*/
                   2266:       isdraw = (((icol-left)%(dashlen+spacelen)) < dashlen);
                   2267:     if ( ++ipix >= npix )			/* bounds check failed */
                   2268:          if ( isfatal ) goto end_of_job;	/* abort if error is fatal */
                   2269:          else break;				/*or just go on to next row*/
                   2270:     else					/*ibit is within rp bounds*/
1.3     ! albertel 2271:       if ( isdraw ) {				/*and we're drawing this bit*/
1.1       albertel 2272: 	if ( rp->pixsz == 1 )			/* have a bitmap */
                   2273: 	  setlongbit(rp->pixmap,ipix);		/* so turn on bit in line */
                   2274: 	else					/* should have a bytemap */
                   2275: 	 if ( rp->pixsz == 8 )			/* check pixsz for bytemap */
1.3     ! albertel 2276: 	  ((unsigned char *)(rp->pixmap))[ipix] = 255; } /* set black byte */
1.1       albertel 2277:     } /* --- end-of-for(icol) --- */
                   2278:   } /* --- end-of-for(irow) --- */
                   2279: end_of_job:
                   2280:   return ( isfatal? (ipix<npix? 1:0) : 1 );
                   2281: } /* --- end-of-function rule_raster() --- */
                   2282: 
                   2283: 
                   2284: /* ==========================================================================
                   2285:  * Function:	line_raster ( rp,  row0, col0,  row1, col1,  thickness )
                   2286:  * Purpose:	Draw a line from row0,col0 to row1,col1 of thickness
                   2287:  *		in existing raster rp.
                   2288:  * --------------------------------------------------------------------------
                   2289:  * Arguments:	rp (I)		raster *  to raster in which a line
                   2290:  *				will be drawn
                   2291:  *		row0 (I)	int containing row at which
                   2292:  *				line will start (0 is topmost)
                   2293:  *		col0 (I)	int containing col at which
                   2294:  *				line will start (0 is leftmost)
                   2295:  *		row1 (I)	int containing row at which
                   2296:  *				line will end (rp->height-1 is bottom-most)
                   2297:  *		col1 (I)	int containing col at which
                   2298:  *				line will end (rp->width-1 is rightmost)
                   2299:  *		thickness (I)	int containing number of pixels/bits
                   2300:  *				thick the line will be
                   2301:  * --------------------------------------------------------------------------
                   2302:  * Returns:	( int )		1 if line drawn okay,
                   2303:  *				or 0 for any error.
                   2304:  * --------------------------------------------------------------------------
                   2305:  * Notes:     o	if row0==row1, a horizontal line is drawn
                   2306:  *		between col0 and col1, with row0(==row1) the top row
                   2307:  *		and row0+(thickness-1) the bottom row
                   2308:  *	      o	if col0==col1, a vertical bar is drawn
                   2309:  *		between row0 and row1, with col0(==col1) the left col
                   2310:  *		and col0+(thickness-1) the right col
                   2311:  *	      o	if both the above, you get a square thickness x thickness
                   2312:  *		whose top-left corner is row0,col0.
                   2313:  * ======================================================================= */
                   2314: /* --- entry point --- */
                   2315: int	line_raster ( raster *rp, int row0, int col0,
                   2316: 	int row1, int col1, int thickness )
                   2317: {
                   2318: /* -------------------------------------------------------------------------
                   2319: Allocations and Declarations
                   2320: -------------------------------------------------------------------------- */
1.2       albertel 2321: int	irow=0, icol=0,		/* indexes over rp raster */
1.1       albertel 2322: 	locol=col0, hicol=col1,	/* col limits at irow */
                   2323: 	lorow=row0, hirow=row1;	/* row limits at icol */
1.2       albertel 2324: int	width=rp->width, height=rp->height; /* dimensions of input raster */
                   2325: int	ipix = 0,		/* raster pixmap[] index */
                   2326: 	npix = width*height;	/* #pixels malloced in rp->pixmap[] */
1.1       albertel 2327: int	isfatal = 0;		/* true to abend on out-of-bounds error */
                   2328: int	isline=(row1==row0), isbar=(col1==col0); /*true if slope a=0,\infty*/
                   2329: double	dy = row1-row0 /* + (row1>=row0? +1.0 : -1.0) */, /* delta-x */
                   2330: 	dx = col1-col0 /* + (col1>=col0? +1.0 : -1.0) */, /* delta-y */
                   2331: 	a= (isbar||isline? 0.0 : dy/dx), /* slope = tan(theta) = dy/dx */
1.2       albertel 2332: 	xcol=0, xrow=0;		/* calculated col at irow, or row at icol */
1.1       albertel 2333: double	ar = ASPECTRATIO,	/* aspect ratio width/height of one pixel */
                   2334: 	xwidth= (isline? 0.0 :	/*#pixels per row to get sloped line thcknss*/
                   2335: 		((double)thickness)*sqrt((dx*dx)+(dy*dy*ar*ar))/fabs(dy*ar)),
                   2336: 	xheight = 1.0;
                   2337: int	line_recurse(), isrecurse=1; /* true to draw line recursively */
                   2338: /* -------------------------------------------------------------------------
                   2339: Check args
                   2340: -------------------------------------------------------------------------- */
1.3     ! albertel 2341: if ( rp == (raster *)NULL ) {	/* no raster arg supplied */
1.1       albertel 2342:   if ( workingbox != (subraster *)NULL )  /* see if we have a workingbox */
                   2343:     rp = workingbox->image;	/* use workingbox if possible */
1.3     ! albertel 2344:   else return ( 0 ); }		/* otherwise signal error to caller */
1.1       albertel 2345: /* -------------------------------------------------------------------------
                   2346: Initialization
                   2347: -------------------------------------------------------------------------- */
1.3     ! albertel 2348: if ( msgfp!=NULL && msglevel>=29 ) {		/* debugging */
1.1       albertel 2349:    fprintf(msgfp,"line_raster> row,col0=%d,%d row,col1=%d,%d, thickness=%d\n"
                   2350:    "\t dy,dx=%3.1f,%3.1f, a=%4.3f, xwidth=%4.3f\n",
1.3     ! albertel 2351:    row0,col0, row1,col1, thickness,  dy,dx, a, xwidth); fflush(msgfp); }
1.1       albertel 2352: /* --- check for recursive line drawing --- */
1.2       albertel 2353: if ( isrecurse ) {		/* drawing lines recursively */
                   2354:  for ( irow=0; irow<thickness; irow++ )		/* each line 1 pixel thick */
                   2355:   { double xrow0=(double)row0, xcol0=(double)col0,
                   2356: 	xrow1=(double)row1, xcol1=(double)col1;
                   2357:     if ( isline ) xrow0 = xrow1 = (double)(row0+irow);
                   2358:     else if ( isbar ) xcol0 = xcol1 = (double)(col0+irow);
                   2359:     if( xrow0>(-0.001) && xcol0>(-0.001)	/*check line inside raster*/
                   2360:     &&  xrow1<((double)(height-1)+0.001) && xcol1<((double)(width-1)+0.001) )
                   2361:       line_recurse(rp,xrow0,xcol0,xrow1,xcol1,thickness); }
                   2362:  return ( 1 ); }
1.1       albertel 2363: /* --- set params for horizontal line or vertical bar --- */
                   2364: if ( isline )					/*interpret row as top row*/
                   2365:   row1 = row0 + (thickness-1);			/* set bottom row for line */
                   2366: if ( 0&&isbar )					/*interpret col as left col*/
                   2367:   hicol = col0 + (thickness-1);			/* set right col for bar */
                   2368: /* -------------------------------------------------------------------------
                   2369: draw line one row at a time
                   2370: -------------------------------------------------------------------------- */
                   2371: for ( irow=min2(row0,row1); irow<=max2(row0,row1); irow++ ) /*each scan line*/
                   2372:   {
                   2373:   if ( !isbar && !isline )			/* neither vert nor horiz */
                   2374:     { xcol  = col0 + ((double)(irow-row0))/a;	/* "middle" col in irow */
                   2375:       locol = max2((int)(xcol-0.5*(xwidth-1.0)),0); /* leftmost col */
                   2376:       hicol = min2((int)(xcol+0.5*(xwidth-0.0)),max2(col0,col1)); } /*right*/
                   2377:   if ( msgfp!=NULL && msglevel>=29 )		/* debugging */
                   2378:     fprintf(msgfp,"\t irow=%d, xcol=%4.2f, lo,hicol=%d,%d\n",
                   2379:     irow,xcol,locol,hicol);
                   2380:   ipix = irow*rp->width + min2(locol,hicol) - 1; /*first pix preceding icol*/
                   2381:   for ( icol=min2(locol,hicol); icol<=max2(locol,hicol); icol++ ) /*each pix*/
                   2382:     if ( ++ipix >= npix )			/* bounds check failed */
                   2383: 	if ( isfatal ) goto end_of_job;	/* abort if error is fatal */
                   2384: 	else break;				/*or just go on to next row*/
                   2385:     else					/* turn on pixel in line */
                   2386: 	if ( rp->pixsz == 1 )			/* have a pixel bitmap */
                   2387: 	  setlongbit(rp->pixmap,ipix);		/* so turn on bit in line */
                   2388: 	else					/* should have a bytemap */
                   2389: 	 if ( rp->pixsz == 8 )			/* check pixsz for bytemap */
                   2390: 	  ((unsigned char *)(rp->pixmap))[ipix] = 255; /* set black byte */
                   2391:   } /* --- end-of-for(irow) --- */
                   2392: /* -------------------------------------------------------------------------
                   2393: now _redraw_ line one col at a time to avoid "gaps"
                   2394: -------------------------------------------------------------------------- */
                   2395: if ( 1 )
                   2396:  for ( icol=min2(col0,col1); icol<=max2(col0,col1); icol++ )/*each scan line*/
                   2397:   {
                   2398:   if ( !isbar && !isline )			/* neither vert nor horiz */
                   2399:     { xrow  = row0 + ((double)(icol-col0))*a;	/* "middle" row in icol */
                   2400:       lorow = max2((int)(xrow-0.5*(xheight-1.0)),0); /* topmost row */
                   2401:       hirow = min2((int)(xrow+0.5*(xheight-0.0)),max2(row0,row1)); } /*bot*/
                   2402:   if ( msgfp!=NULL && msglevel>=29 )		/* debugging */
                   2403:     fprintf(msgfp,"\t icol=%d, xrow=%4.2f, lo,hirow=%d,%d\n",
                   2404:     icol,xrow,lorow,hirow);
                   2405:   ipix = irow*rp->width + min2(locol,hicol) - 1; /*first pix preceding icol*/
                   2406:   for ( irow=min2(lorow,hirow); irow<=max2(lorow,hirow); irow++ ) /*each pix*/
                   2407:     if ( irow<0 || irow>=rp->height
                   2408:     ||   icol<0 || icol>=rp->width )		/* bounds check */
                   2409:       if ( isfatal ) goto end_of_job;		/* abort if error is fatal */
                   2410:       else continue;				/*or just go on to next row*/
                   2411:     else
                   2412:       setpixel(rp,irow,icol,255);		/* set pixel at irow,icol */
                   2413:   } /* --- end-of-for(irow) --- */
                   2414: /* -------------------------------------------------------------------------
                   2415: Back to caller with 1=okay, 0=failed.
                   2416: -------------------------------------------------------------------------- */
                   2417: end_of_job:
                   2418:   return ( isfatal? (ipix<npix? 1:0) : 1 );
                   2419: } /* --- end-of-function line_raster() --- */
                   2420: 
                   2421: 
                   2422: /* ==========================================================================
                   2423:  * Function:	line_recurse ( rp,  row0, col0,  row1, col1,  thickness )
                   2424:  * Purpose:	Draw a line from row0,col0 to row1,col1 of thickness
                   2425:  *		in existing raster rp.
                   2426:  * --------------------------------------------------------------------------
                   2427:  * Arguments:	rp (I)		raster *  to raster in which a line
                   2428:  *				will be drawn
                   2429:  *		row0 (I)	double containing row at which
                   2430:  *				line will start (0 is topmost)
                   2431:  *		col0 (I)	double containing col at which
                   2432:  *				line will start (0 is leftmost)
                   2433:  *		row1 (I)	double containing row at which
                   2434:  *				line will end (rp->height-1 is bottom-most)
                   2435:  *		col1 (I)	double containing col at which
                   2436:  *				line will end (rp->width-1 is rightmost)
                   2437:  *		thickness (I)	int containing number of pixels/bits
                   2438:  *				thick the line will be
                   2439:  * --------------------------------------------------------------------------
                   2440:  * Returns:	( int )		1 if line drawn okay,
                   2441:  *				or 0 for any error.
                   2442:  * --------------------------------------------------------------------------
                   2443:  * Notes:     o	Recurses, drawing left- and right-halves of line
                   2444:  *		until a horizontal or vertical segment is found
                   2445:  * ======================================================================= */
                   2446: /* --- entry point --- */
                   2447: int	line_recurse ( raster *rp, double row0, double col0,
                   2448: 	double row1, double col1, int thickness )
                   2449: {
                   2450: /* -------------------------------------------------------------------------
                   2451: Allocations and Declarations
                   2452: -------------------------------------------------------------------------- */
                   2453: double	delrow = fabs(row1-row0),	/* 0 if line horizontal */
                   2454: 	delcol = fabs(col1-col0),	/* 0 if line vertical */
                   2455: 	tolerance = 0.5;		/* draw line when it goes to point */
                   2456: double	midrow = 0.5*(row0+row1),	/* midpoint row */
                   2457: 	midcol = 0.5*(col0+col1);	/* midpoint col */
                   2458: /* -------------------------------------------------------------------------
                   2459: recurse if either delta > tolerance
                   2460: -------------------------------------------------------------------------- */
                   2461: if ( delrow > tolerance			/* row hasn't converged */
                   2462: ||   delcol > tolerance )		/* col hasn't converged */
                   2463:   { line_recurse(rp,row0,col0,midrow,midcol,thickness); /* left half */
                   2464:     line_recurse(rp,midrow,midcol,row1,col1,thickness); /* right half */
                   2465:     return ( 1 ); }
                   2466: /* -------------------------------------------------------------------------
                   2467: draw converged point
                   2468: -------------------------------------------------------------------------- */
                   2469: setpixel(rp,iround(midrow),iround(midcol),255); /*set pixel at midrow,midcol*/
                   2470: return ( 1 );
                   2471: } /* --- end-of-function line_recurse() --- */
                   2472: 
                   2473: 
                   2474: /* ==========================================================================
                   2475:  * Function:	circle_raster ( rp,  row0, col0,  row1, col1,
                   2476:  *		thickness, quads )
                   2477:  * Purpose:	Draw quad(rant)s of an ellipse in box determined by
                   2478:  *		diagonally opposite corner points (row0,col0) and
                   2479:  *		(row1,col1), of thickness pixels in existing raster rp.
                   2480:  * --------------------------------------------------------------------------
                   2481:  * Arguments:	rp (I)		raster *  to raster in which an ellipse
                   2482:  *				will be drawn
                   2483:  *		row0 (I)	int containing 1st corner row bounding ellipse
                   2484:  *				(0 is topmost)
                   2485:  *		col0 (I)	int containing 1st corner col bounding ellipse
                   2486:  *				(0 is leftmost)
                   2487:  *		row1 (I)	int containing 2nd corner row bounding ellipse
                   2488:  *				(rp->height-1 is bottom-most)
                   2489:  *		col1 (I)	int containing 2nd corner col bounding ellipse
                   2490:  *				(rp->width-1 is rightmost)
                   2491:  *		thickness (I)	int containing number of pixels/bits
                   2492:  *				thick the ellipse arc line will be
                   2493:  *		quads (I)	char * to null-terminated string containing
                   2494:  *				any subset/combination of "1234" specifying
                   2495:  *				which quadrant(s) of ellipse to draw.
                   2496:  *				NULL ptr draws all four quadrants;
                   2497:  *				otherwise 1=upper-right quadrant,
                   2498:  *				2=uper-left, 3=lower-left, 4=lower-right,
                   2499:  *				i.e., counterclockwise from 1=positive quad.
                   2500:  * --------------------------------------------------------------------------
                   2501:  * Returns:	( int )		1 if ellipse drawn okay,
                   2502:  *				or 0 for any error.
                   2503:  * --------------------------------------------------------------------------
                   2504:  * Notes:     o	row0==row1 or col0==col1 are errors
                   2505:  *	      o	using ellipse equation x^2/a^2 + y^2/b^2 = 1
                   2506:  * ======================================================================= */
                   2507: /* --- entry point --- */
                   2508: int	circle_raster ( raster *rp, int row0, int col0,
                   2509: 	int row1, int col1, int thickness, char *quads )
                   2510: {
                   2511: /* -------------------------------------------------------------------------
                   2512: Allocations and Declarations
                   2513: -------------------------------------------------------------------------- */
                   2514: /* --- lower-left and upper-right bounding points (in our coords) --- */
                   2515: int	lorow = min2(row0,row1),	/* lower bounding row (top of box) */
                   2516: 	locol = min2(col0,col1),	/* lower bounding col (left of box)*/
                   2517: 	hirow = max2(row0,row1),	/* upper bounding row (bot of box) */
                   2518: 	hicol = max2(col0,col1);	/* upper bounding col (right of box)*/
                   2519: /* --- a and b ellipse params --- */
                   2520: int	width = hicol-locol+1,		/* width of bounding box */
                   2521: 	height= hirow-lorow+1,		/* height of bounding box */
                   2522: 	islandscape = (width>=height? 1:0); /*true if ellipse lying on side*/
                   2523: double	a = ((double)width)/2.0,	/* x=a when y=0 */
                   2524: 	b = ((double)height)/2.0,	/* y=b when x=0 */
                   2525: 	abmajor = (islandscape? a : b),	/* max2(a,b) */
                   2526: 	abminor = (islandscape? b : a),	/* min2(a,b) */
                   2527: 	abmajor2 = abmajor*abmajor,	/* abmajor^2 */
                   2528: 	abminor2 = abminor*abminor;	/* abminor^2 */
                   2529: /* --- other stuff --- */
                   2530: int	imajor=0, nmajor=max2(width,height), /*index, #pixels on major axis*/
                   2531: 	iminor=0, nminor=min2(width,height); /* solved index on minor axis */
                   2532: int	irow, icol,			/* raster indexes at circumference */
                   2533: 	rsign=1, csign=1;		/* row,col signs, both +1 in quad 1*/
                   2534: double	midrow= ((double)(row0+row1))/2.0, /* center row */
                   2535: 	midcol= ((double)(col0+col1))/2.0; /* center col */
                   2536: double	xy, xy2,			/* major axis ellipse coord */
                   2537: 	yx2, yx;			/* solved minor ellipse coord */
                   2538: int	isokay = 1;			/* true if no pixels out-of-bounds */
                   2539: char	*qptr=NULL, *allquads="1234";	/* quadrants if input quads==NULL */
                   2540: int	circle_recurse(), isrecurse=1;	/* true to draw ellipse recursively*/
                   2541: /* -------------------------------------------------------------------------
                   2542: pixel-by-pixel along positive major axis, quit when it goes negative
                   2543: -------------------------------------------------------------------------- */
                   2544: if ( quads == NULL ) quads = allquads;	/* draw all quads, or only user's */
                   2545: if ( msgfp!=NULL && msglevel>=39 )	/* debugging */
                   2546:   fprintf(msgfp,"circle_raster> width,height;quads=%d,%d,%s\n",
                   2547:   width,height,quads);
                   2548: if ( nmajor < 1 ) isokay = 0;		/* problem with input args */
                   2549: else
                   2550:  {
                   2551:  if ( isrecurse )			/* use recursive algorithm */
                   2552:   {
                   2553:   for ( qptr=quads; *qptr!='\000'; qptr++ ) /* for each character in quads */
                   2554:    {
                   2555:    double theta0=0.0, theta1=0.0;	/* set thetas based on quadrant */
                   2556:    switch ( *qptr )			/* check for quadrant 1,2,3,4 */
                   2557:     { default:				/* unrecognized, assume quadrant 1 */
                   2558:       case '1': theta0=  0.0; theta1= 90.0; break;   /* first quadrant */
                   2559:       case '2': theta0= 90.0; theta1=180.0; break;   /* second quadrant */
                   2560:       case '3': theta0=180.0; theta1=270.0; break;   /* third quadrant */
                   2561:       case '4': theta0=270.0; theta1=360.0; break; } /* fourth quadrant */
                   2562:    circle_recurse(rp,row0,col0,row1,col1,thickness,theta0,theta1);
                   2563:    } /* --- end-of-for(qptr) --- */
                   2564:   return ( 1 );
                   2565:   } /* --- end-of-if(isrecurse) --- */
                   2566:  for ( imajor=(nmajor+1)/2; ; imajor-- )
                   2567:   {
                   2568:   /* --- xy is coord along major axis, yx is "solved" along minor axis --- */
                   2569:   xy  = ((double)imajor);		/* xy = abmajor ... 0 */
                   2570:   if ( xy < 0.0 ) break;		/* negative side symmetrical */
                   2571:   yx2 = abminor2*(1.0 - xy*xy/abmajor2); /* "solve" ellipse equation */
                   2572:   yx  = (yx2>0.0? sqrt(yx2) : 0.0);	/* take sqrt if possible */
                   2573:   iminor = iround(yx);			/* nearest integer */
                   2574:   /* --- set pixels for each requested quadrant --- */
                   2575:   for ( qptr=quads; *qptr!='\000'; qptr++ ) /* for each character in quads */
                   2576:    {
                   2577:    rsign = (-1);  csign = 1;		/* init row,col in user quadrant 1 */
                   2578:    switch ( *qptr )			/* check for quadrant 1,2,3,4 */
                   2579:     { default: break;			/* unrecognized, assume quadrant 1 */
                   2580:       case '4': rsign = 1; break;	/* row,col both pos in quadrant 4 */
                   2581:       case '3': rsign = 1;		/* row pos, col neg in quadrant 3 */
                   2582:       case '2': csign = (-1); break; }	/* row,col both neg in quadrant 2 */
                   2583:    irow = iround(midrow + (double)rsign*(islandscape?yx:xy));
                   2584:    irow = min2(hirow,max2(lorow,irow));	/* keep irow in bounds */
                   2585:    icol = iround(midcol + (double)csign*(islandscape?xy:yx));
                   2586:    icol = min2(hicol,max2(locol,icol));	/* keep icol in bounds */
                   2587:    if ( msgfp!=NULL && msglevel>=49 )	/* debugging */
                   2588:      fprintf(msgfp,"\t...imajor=%d; iminor,quad,irow,icol=%d,%c,%d,%d\n",
                   2589:      imajor,iminor,*qptr,irow,icol);
                   2590:    if ( irow<0 || irow>=rp->height	/* row outside raster */
                   2591:    ||   icol<0 || icol>=rp->width )	/* col outside raster */
                   2592:       {	isokay = 0;			/* signal out-of-bounds pixel */
                   2593: 	continue; }			/* but still try remaining points */
                   2594:    setpixel(rp,irow,icol,255);		/* set pixel at irow,icol */
                   2595:    } /* --- end-of-for(qptr) --- */
                   2596:   } /* --- end-of-for(imajor) --- */
                   2597:  /* ------------------------------------------------------------------------
                   2598:  now do it _again_ along minor axis to avoid "gaps"
                   2599:  ------------------------------------------------------------------------- */
                   2600:  if ( 1 && iminor>0 )
                   2601:   for ( iminor=(nminor+1)/2; ; iminor-- )
                   2602:    {
                   2603:    /* --- yx is coord along minor axis, xy is "solved" along major axis --- */
                   2604:    yx  = ((double)iminor);		/* yx = abminor ... 0 */
                   2605:    if ( yx < 0.0 ) break;		/* negative side symmetrical */
                   2606:    xy2 = abmajor2*(1.0 - yx*yx/abminor2); /* "solve" ellipse equation */
                   2607:    xy  = (xy2>0.0? sqrt(xy2) : 0.0);	/* take sqrt if possible */
                   2608:    imajor = iround(xy);			/* nearest integer */
                   2609:    /* --- set pixels for each requested quadrant --- */
                   2610:    for ( qptr=quads; *qptr!='\000'; qptr++ ) /* for each character in quads */
                   2611:     {
                   2612:     rsign = (-1);  csign = 1;		/* init row,col in user quadrant 1 */
                   2613:     switch ( *qptr )			/* check for quadrant 1,2,3,4 */
                   2614:      { default: break;			/* unrecognized, assume quadrant 1 */
                   2615:        case '4': rsign = 1; break;	/* row,col both pos in quadrant 4 */
                   2616:        case '3': rsign = 1;		/* row pos, col neg in quadrant 3 */
                   2617:        case '2': csign = (-1); break; }	/* row,col both neg in quadrant 2 */
                   2618:     irow = iround(midrow + (double)rsign*(islandscape?yx:xy));
                   2619:     irow = min2(hirow,max2(lorow,irow)); /* keep irow in bounds */
                   2620:     icol = iround(midcol + (double)csign*(islandscape?xy:yx));
                   2621:     icol = min2(hicol,max2(locol,icol)); /* keep icol in bounds */
                   2622:     if ( msgfp!=NULL && msglevel>=49 )	/* debugging */
                   2623:      fprintf(msgfp,"\t...iminor=%d; imajor,quad,irow,icol=%d,%c,%d,%d\n",
                   2624:      iminor,imajor,*qptr,irow,icol);
                   2625:     if ( irow<0 || irow>=rp->height	/* row outside raster */
                   2626:     ||   icol<0 || icol>=rp->width )	/* col outside raster */
                   2627:       {	isokay = 0;			/* signal out-of-bounds pixel */
                   2628: 	continue; }			/* but still try remaining points */
                   2629:     setpixel(rp,irow,icol,255);		/* set pixel at irow,icol */
                   2630:     } /* --- end-of-for(qptr) --- */
                   2631:    } /* --- end-of-for(iminor) --- */
                   2632:  } /* --- end-of-if/else(nmajor<1) --- */
                   2633: return ( isokay );
                   2634: } /* --- end-of-function circle_raster() --- */
                   2635: 
                   2636: 
                   2637: /* ==========================================================================
                   2638:  * Function:	circle_recurse ( rp,  row0, col0,  row1, col1,
                   2639:  *		thickness, theta0, theta1 )
                   2640:  * Purpose:	Recursively draws arc theta0<=theta<=theta1 of the ellipse
                   2641:  *		in box determined by diagonally opposite corner points
                   2642:  *		(row0,col0) and (row1,col1), of thickness pixels in raster rp.
                   2643:  * --------------------------------------------------------------------------
                   2644:  * Arguments:	rp (I)		raster *  to raster in which an ellipse
                   2645:  *				will be drawn
                   2646:  *		row0 (I)	int containing 1st corner row bounding ellipse
                   2647:  *				(0 is topmost)
                   2648:  *		col0 (I)	int containing 1st corner col bounding ellipse
                   2649:  *				(0 is leftmost)
                   2650:  *		row1 (I)	int containing 2nd corner row bounding ellipse
                   2651:  *				(rp->height-1 is bottom-most)
                   2652:  *		col1 (I)	int containing 2nd corner col bounding ellipse
                   2653:  *				(rp->width-1 is rightmost)
                   2654:  *		thickness (I)	int containing number of pixels/bits
                   2655:  *				thick the ellipse arc line will be
                   2656:  *		theta0 (I)	double containing first angle -360 -> +360
                   2657:  *		theta1 (I)	double containing second angle -360 -> +360
                   2658:  *				0=x-axis, positive moving counterclockwise
                   2659:  * --------------------------------------------------------------------------
                   2660:  * Returns:	( int )		1 if ellipse drawn okay,
                   2661:  *				or 0 for any error.
                   2662:  * --------------------------------------------------------------------------
                   2663:  * Notes:     o	row0==row1 or col0==col1 are errors
                   2664:  *	      o	using ellipse equation x^2/a^2 + y^2/b^2 = 1
                   2665:  *		Then, with x=r*cos(theta), y=r*sin(theta), ellipse
                   2666:  *		equation is r = ab/sqrt(a^2*sin^2(theta)+b^2*cos^2(theta))
                   2667:  * ======================================================================= */
                   2668: /* --- entry point --- */
                   2669: int	circle_recurse ( raster *rp, int row0, int col0,
                   2670: 	int row1, int col1, int thickness, double theta0, double theta1 )
                   2671: {
                   2672: /* -------------------------------------------------------------------------
                   2673: Allocations and Declarations
                   2674: -------------------------------------------------------------------------- */
                   2675: /* --- lower-left and upper-right bounding points (in our coords) --- */
                   2676: int	lorow = min2(row0,row1),	/* lower bounding row (top of box) */
                   2677: 	locol = min2(col0,col1),	/* lower bounding col (left of box)*/
                   2678: 	hirow = max2(row0,row1),	/* upper bounding row (bot of box) */
                   2679: 	hicol = max2(col0,col1);	/* upper bounding col (right of box)*/
                   2680: /* --- a and b ellipse params --- */
                   2681: int	width = hicol-locol+1,		/* width of bounding box */
                   2682: 	height= hirow-lorow+1;		/* height of bounding box */
                   2683: double	a = ((double)width)/2.0,	/* col x=a when row y=0 */
                   2684: 	b = ((double)height)/2.0,	/* row y=b when col x=0 */
                   2685: 	ab=a*b, a2=a*a, b2=b*b;		/* product and squares */
                   2686: /* --- arc parameters --- */
                   2687: double	rads = 0.017453292,		/* radians per degree = 1/57.29578 */
                   2688: 	lotheta = rads*dmod(min2(theta0,theta1),360), /* smaller angle */
                   2689: 	hitheta = rads*dmod(max2(theta0,theta1),360), /* larger angle */
                   2690: 	locos=cos(lotheta), losin=sin(lotheta), /* trigs for lotheta */
                   2691: 	hicos=cos(hitheta), hisin=sin(hitheta), /* trigs for hitheta */
                   2692: 	rlo = ab/sqrt(b2*locos*locos+a2*losin*losin), /* r for lotheta */
                   2693: 	rhi = ab/sqrt(b2*hicos*hicos+a2*hisin*hisin), /* r for hitheta */
                   2694: 	xlo=rlo*locos, ylo=rlo*losin,	/*col,row pixel coords for lotheta*/
                   2695: 	xhi=rhi*hicos, yhi=rhi*hisin,	/*col,row pixel coords for hitheta*/
                   2696: 	xdelta=fabs(xhi-xlo), ydelta=fabs(yhi-ylo), /* col,row deltas */
                   2697: 	tolerance = 0.5;		/* convergence tolerance */
                   2698: /* -------------------------------------------------------------------------
                   2699: recurse if either delta > tolerance
                   2700: -------------------------------------------------------------------------- */
                   2701: if ( ydelta > tolerance			/* row hasn't converged */
                   2702: ||   xdelta > tolerance )		/* col hasn't converged */
                   2703:   { double midtheta = 0.5*(theta0+theta1); /* mid angle for arc */
                   2704:     circle_recurse(rp,row0,col0,row1,col1,thickness,theta0,midtheta);  /*lo*/
                   2705:     circle_recurse(rp,row0,col0,row1,col1,thickness,midtheta,theta1); }/*hi*/
                   2706: /* -------------------------------------------------------------------------
                   2707: draw converged point
                   2708: -------------------------------------------------------------------------- */
                   2709: else
                   2710:   { double xcol=0.5*(xlo+xhi), yrow=0.5*(ylo+yhi),    /* relative to center*/
                   2711: 	centerrow = 0.5*((double)(lorow+hirow)),      /* ellipse y-center */
                   2712: 	centercol = 0.5*((double)(locol+hicol)),      /* ellipse x-center */
                   2713: 	midrow=centerrow-yrow, midcol=centercol+xcol; /* pixel coords */
                   2714:     setpixel(rp,iround(midrow),iround(midcol),255); } /* set midrow,midcol */
                   2715: return ( 1 );
                   2716: } /* --- end-of-function circle_recurse() --- */
                   2717: 
                   2718: 
                   2719: /* ==========================================================================
                   2720:  * Function:	bezier_raster ( rp, r0,c0, r1,c1, rt,ct )
                   2721:  * Purpose:	Recursively draw bezier from r0,c0 to r1,c1
                   2722:  *		(with tangent point rt,ct) in existing raster rp.
                   2723:  * --------------------------------------------------------------------------
                   2724:  * Arguments:	rp (I)		raster *  to raster in which a line
                   2725:  *				will be drawn
                   2726:  *		r0 (I)		double containing row at which
                   2727:  *				bezier will start (0 is topmost)
                   2728:  *		c0 (I)		double containing col at which
                   2729:  *				bezier will start (0 is leftmost)
                   2730:  *		r1 (I)		double containing row at which
                   2731:  *				bezier will end (rp->height-1 is bottom-most)
                   2732:  *		c1 (I)		double containing col at which
                   2733:  *				bezier will end (rp->width-1 is rightmost)
                   2734:  *		rt (I)		double containing row for tangent point
                   2735:  *		ct (I)		double containing col for tangent point
                   2736:  * --------------------------------------------------------------------------
                   2737:  * Returns:	( int )		1 if line drawn okay,
                   2738:  *				or 0 for any error.
                   2739:  * --------------------------------------------------------------------------
                   2740:  * Notes:     o	Recurses, drawing left- and right-halves of bezier curve
                   2741:  *		until a point is found
                   2742:  * ======================================================================= */
                   2743: /* --- entry point --- */
                   2744: int	bezier_raster ( raster *rp, double r0, double c0,
                   2745: 	double r1, double c1, double rt, double ct )
                   2746: {
                   2747: /* -------------------------------------------------------------------------
                   2748: Allocations and Declarations
                   2749: -------------------------------------------------------------------------- */
                   2750: double	delrow = fabs(r1-r0),		/* 0 if same row */
                   2751: 	delcol = fabs(c1-c0),		/* 0 if same col */
                   2752: 	tolerance = 0.5;		/* draw curve when it goes to point*/
                   2753: double	midrow = 0.5*(r0+r1),		/* midpoint row */
                   2754: 	midcol = 0.5*(c0+c1);		/* midpoint col */
                   2755: int	irow=0, icol=0;			/* point to be drawn */
                   2756: int	status = 1;			/* return status */
                   2757: /* -------------------------------------------------------------------------
                   2758: recurse if either delta > tolerance
                   2759: -------------------------------------------------------------------------- */
                   2760: if ( delrow > tolerance			/* row hasn't converged */
                   2761: ||   delcol > tolerance )		/* col hasn't converged */
                   2762:   { bezier_raster(rp, r0,c0,		/* left half */
                   2763: 	0.5*(rt+midrow), 0.5*(ct+midcol),
                   2764: 	0.5*(r0+rt), 0.5*(c0+ct) );
                   2765:     bezier_raster(rp, 0.5*(rt+midrow), 0.5*(ct+midcol), /* right half */
                   2766: 	r1,c1,
                   2767: 	0.5*(r1+rt), 0.5*(c1+ct) );
                   2768:     return ( 1 ); }
                   2769: /* -------------------------------------------------------------------------
                   2770: draw converged point
                   2771: -------------------------------------------------------------------------- */
                   2772: /* --- get integer point --- */
                   2773: irow = iround(midrow);			/* row pixel coord */
                   2774: icol = iround(midcol);			/* col pixel coord */
                   2775: /* --- bounds check --- */
                   2776: if ( irow>=0 && irow<rp->height		/* row in bounds */
                   2777: &&   icol>=0 && icol<rp->width )	/* col in bounds */
                   2778: 	setpixel(rp,irow,icol,255);	/* so set pixel at irow,icol*/
                   2779: else	status = 0;			/* bad status if out-of-bounds */
                   2780: return ( status );
                   2781: } /* --- end-of-function bezier_raster() --- */
                   2782: 
                   2783: 
                   2784: /* ==========================================================================
                   2785:  * Function:	border_raster ( rp, ntop, nbot, isline, isfree )
                   2786:  * Purpose:	Allocate a new raster containing a copy of input rp,
                   2787:  *		along with ntop extra rows at top and nbot at bottom,
                   2788:  *		and whose width is either adjusted correspondingly,
                   2789:  *		or is automatically enlarged to a multiple of 8
                   2790:  *		with original bitmap centered
                   2791:  * --------------------------------------------------------------------------
                   2792:  * Arguments:	rp (I)		raster *  to raster on which a border
                   2793:  *				is to be placed
                   2794:  *		ntop (I)	int containing number extra rows at top.
                   2795:  *				if negative, abs(ntop) used, and same
                   2796:  *				number of extra cols added at left.
                   2797:  *		nbot (I)	int containing number extra rows at bottom.
                   2798:  *				if negative, abs(nbot) used, and same
                   2799:  *				number of extra cols added at right.
                   2800:  *		isline (I)	int containing 0 to leave border pixels clear
                   2801:  *				or >0 to draw a line around border of width
                   2802:  *				isline.
                   2803:  *		isfree (I)	int containing true to free rp before return
                   2804:  * --------------------------------------------------------------------------
                   2805:  * Returns:	( raster * )	ptr to bordered raster,
                   2806:  *				or NULL for any error.
                   2807:  * --------------------------------------------------------------------------
                   2808:  * Notes:     o
                   2809:  * ======================================================================= */
                   2810: /* --- entry point --- */
                   2811: raster	*border_raster ( raster *rp, int ntop, int nbot,
                   2812: 			int isline, int isfree )
                   2813: {
                   2814: /* -------------------------------------------------------------------------
                   2815: Allocations and Declarations
                   2816: -------------------------------------------------------------------------- */
                   2817: raster	*new_raster(), *bp=(raster *)NULL;  /*raster back to caller*/
                   2818: int	rastput();		/* overlay rp in new bordered raster */
1.3     ! albertel 2819: int	width  = (rp==NULL?0:rp->width),  /* width of raster */
        !          2820: 	height = (rp==NULL?0:rp->height), /* height of raster */
        !          2821: 	istopneg=0, isbotneg=0,		/* true if ntop or nbot negative */
1.1       albertel 2822: 	leftmargin = 0;		/* adjust width to whole number of bytes */
1.3     ! albertel 2823: int	delete_raster();		/* free input rp if isfree is true */
1.1       albertel 2824: /* -------------------------------------------------------------------------
                   2825: Initialization
                   2826: -------------------------------------------------------------------------- */
                   2827: if ( rp == NULL ) goto end_of_job;	/* no input raster provided */
                   2828: if ( isstring || (1 && rp->height==1) )	/* explicit string signal or infer */
                   2829:   { bp=rp; goto end_of_job; }		/* return ascii string unchanged */
                   2830: /* --- check for negative args --- */
                   2831: if ( ntop < 0 ) { ntop = -ntop; istopneg=1; } /*flip positive and set flag*/
                   2832: if ( nbot < 0 ) { nbot = -nbot; isbotneg=1; } /*flip positive and set flag*/
                   2833: /* --- adjust height for ntop and nbot margins --- */
                   2834: height += (ntop+nbot);			/* adjust height for margins */
                   2835: /* --- adjust width for left and right margins --- */
                   2836: if ( istopneg || isbotneg )	/*caller wants nleft=ntop and/or nright=nbot*/
                   2837:   { /* --- adjust width (and leftmargin) as requested by caller -- */
                   2838:     if ( istopneg ) { width += ntop; leftmargin = ntop; }
                   2839:     if ( isbotneg )   width += nbot;  }
                   2840: else
                   2841:   { /* --- or adjust width (and leftmargin) to whole number of bytes --- */
                   2842:     leftmargin = (width%8==0? 0 : 8-(width%8)); /*makes width multiple of 8*/
                   2843:     width += leftmargin;		/* width now multiple of 8 */
                   2844:     leftmargin /= 2; }			/* center original raster */
                   2845: /* -------------------------------------------------------------------------
                   2846: allocate bordered raster, and embed rp within it
                   2847: -------------------------------------------------------------------------- */
                   2848: /* --- allocate bordered raster --- */
                   2849: if ( (bp=new_raster(width,height,rp->pixsz))  /*allocate bordered raster*/
                   2850: ==   (raster *)NULL ) goto end_of_job;	/* and quit if failed */
                   2851: /* --- embed rp in it --- */
                   2852: rastput(bp,rp,ntop,leftmargin,1);	/* rp embedded in bp */
                   2853: /* -------------------------------------------------------------------------
                   2854: draw border if requested
                   2855: -------------------------------------------------------------------------- */
                   2856: if ( isline )
                   2857:  { int	irow, icol, nthick=isline;	/*height,width index, line thickness*/
                   2858:   /* --- draw left- and right-borders --- */
                   2859:   for ( irow=0; irow<height; irow++ )	/* for each row of bp */
                   2860:     for ( icol=0; icol<nthick; icol++ )	/* and each pixel of thickness */
                   2861:       {	setpixel(bp,irow,icol,255);	/* left border */
                   2862: 	setpixel(bp,irow,width-1-icol,255); } /* right border */
                   2863:   /* --- draw top- and bottom-borders --- */
                   2864:   for ( icol=0; icol<width; icol++ )	/* for each col of bp */
                   2865:     for ( irow=0; irow<nthick; irow++ )	/* and each pixel of thickness */
                   2866:       {	setpixel(bp,irow,icol,255);	/* top border */
                   2867: 	setpixel(bp,height-1-irow,icol,255); } /* bottom border */
                   2868:  } /* --- end-of-if(isline) --- */
                   2869: /* -------------------------------------------------------------------------
                   2870: free rp if no longer needed
                   2871: -------------------------------------------------------------------------- */
                   2872: if ( isfree )					/*caller no longer needs rp*/
                   2873:   delete_raster(rp);				/* so free it for him */
                   2874: /* -------------------------------------------------------------------------
                   2875: Back to caller with bordered raster (or null for any error)
                   2876: -------------------------------------------------------------------------- */
                   2877: end_of_job:
                   2878:   return ( bp );			/* back with bordered or null ptr */
                   2879: } /* --- end-of-function border_raster() --- */
                   2880: 
                   2881: 
                   2882: /* ==========================================================================
1.3     ! albertel 2883:  * Function:	backspace_raster ( rp, nback, pback, minspace, isfree )
        !          2884:  * Purpose:	Allocate a new raster containing a copy of input rp,
        !          2885:  *		but with trailing nback columns removed.
        !          2886:  *		If minspace>=0 then (at least) that many columns
        !          2887:  *		of whitespace will be left in place, regardless of nback.
        !          2888:  *		If minspace<0 then existing black pixels will be deleted
        !          2889:  *		as required.
        !          2890:  * --------------------------------------------------------------------------
        !          2891:  * Arguments:	rp (I)		raster *  to raster on which a border
        !          2892:  *				is to be placed
        !          2893:  *		nback (I)	int containing number of columns to
        !          2894:  *				backspace (>=0)
        !          2895:  *		pback (O)	ptr to int returning #pixels actually
        !          2896:  *				backspaced (or NULL to not use)
        !          2897:  *		minspace (I)	int containing number of columns
        !          2898:  *				of whitespace to be left in place
        !          2899:  *		isfree (I)	int containing true to free rp before return
        !          2900:  * --------------------------------------------------------------------------
        !          2901:  * Returns:	( raster * )	ptr to backspaced raster,
        !          2902:  *				or NULL for any error.
        !          2903:  * --------------------------------------------------------------------------
        !          2904:  * Notes:     o	For \! negative space, for \hspace{-10}, etc.
        !          2905:  * ======================================================================= */
        !          2906: /* --- entry point --- */
        !          2907: raster	*backspace_raster ( raster *rp, int nback, int *pback, int minspace,
        !          2908: 	int isfree )
        !          2909: {
        !          2910: /* -------------------------------------------------------------------------
        !          2911: Allocations and Declarations
        !          2912: -------------------------------------------------------------------------- */
        !          2913: raster	*new_raster(), *bp=(raster *)NULL;  /* raster returned to caller */
        !          2914: int	delete_raster();		/* free input rp if isfree is true */
        !          2915: int	width  = (rp==NULL?0:rp->width),  /* original width of raster */
        !          2916: 	height = (rp==NULL?0:rp->height), /* height of raster */
        !          2917: 	mback = nback,			/* nback adjusted for minspace */
        !          2918: 	newwidth = width,		/* adjusted width after backspace */
        !          2919: 	icol=0, irow=0;			/* col,row index */
        !          2920: if ( rp == NULL ) goto end_of_job;	/* no input given */
        !          2921: /* -------------------------------------------------------------------------
        !          2922: locate rightmost column of rp containing ink, and determine backspaced width
        !          2923: -------------------------------------------------------------------------- */
        !          2924: /* --- locate rightmost column of rp containing ink --- */
        !          2925: if ( minspace >= 0 )			/* only needed if given minspace */
        !          2926:  for ( icol=width-1; icol>=0; icol-- )	/* find first non-empty col in row */
        !          2927:   for ( irow=0; irow<height; irow++ )	/* for each row inside rp */
        !          2928:    if ( getpixel(rp,irow,icol) != 0 ) {	/* found a set pixel */
        !          2929:      int whitecols = (width-1) - icol;	/* #cols containing white space */
        !          2930:      mback = min2(nback,max2(0,whitecols-minspace)); /*leave minspace cols*/
        !          2931:      goto gotright; }			/* no need to look further */
        !          2932: /* --- determine width of new backspaced raster --- */
        !          2933: gotright:				/* found col with ink (or rp empty)*/
        !          2934:   if ( mback > width ) mback = width;	/* can't backspace before beginning*/
        !          2935:   newwidth = max2(1,width-mback);	/* #cols in backspaced raster */
        !          2936:   if ( pback != NULL ) *pback = width-newwidth; /* caller wants #pixels */
        !          2937: /* -------------------------------------------------------------------------
        !          2938: allocate new raster and fill it with leftmost cols of rp
        !          2939: -------------------------------------------------------------------------- */
        !          2940: /* --- allocate backspaced raster --- */
        !          2941: if ( (bp=new_raster(newwidth,height,rp->pixsz)) /*allocate backspaced raster*/
        !          2942: ==   (raster *)NULL ) goto end_of_job;	/* and quit if failed */
        !          2943: /* --- fill new raster --- */
        !          2944: if ( width-nback > 0 )			/* don't fill 1-pixel wide empty bp*/
        !          2945:  for ( icol=0; icol<newwidth; icol++ )	/* find first non-empty col in row */
        !          2946:   for ( irow=0; irow<height; irow++ )	/* for each row inside rp */
        !          2947:     { int value = getpixel(rp,irow,icol); /* original pixel at irow,icol */
        !          2948:       setpixel(bp,irow,icol,value); }	/* saved in backspaced raster */
        !          2949: /* -------------------------------------------------------------------------
        !          2950: Back to caller with backspaced raster (or null for any error)
        !          2951: -------------------------------------------------------------------------- */
        !          2952: end_of_job:
        !          2953:   if ( msgfp!=NULL && msglevel>=999 ) { fprintf(msgfp, /* diagnostics */
        !          2954:    "backspace_raster> nback=%d,minspace=%d,mback=%d, width:old=%d,new=%d\n",
        !          2955:    nback,minspace,mback,width,newwidth); fflush(msgfp); }
        !          2956:   if ( isfree && bp!=NULL ) delete_raster(rp); /* free original raster */
        !          2957:   return ( bp );			/* back with backspaced or null ptr*/
        !          2958: } /* --- end-of-function backspace_raster() --- */
        !          2959: 
        !          2960: 
        !          2961: /* ==========================================================================
1.1       albertel 2962:  * Function:	type_raster ( rp, fp )
                   2963:  * Purpose:	Emit an ascii dump representing rp, on fp.
                   2964:  * --------------------------------------------------------------------------
                   2965:  * Arguments:	rp (I)		ptr to raster struct for which an
                   2966:  *				ascii dump is to be constructed.
                   2967:  *		fp (I)		File ptr to output device (defaults to
                   2968:  *				stdout if passed as NULL).
                   2969:  * --------------------------------------------------------------------------
                   2970:  * Returns:	( int )		1 if completed successfully,
                   2971:  *				or 0 otherwise (for any error).
                   2972:  * --------------------------------------------------------------------------
                   2973:  * Notes:
                   2974:  * ======================================================================= */
                   2975: /* --- entry point --- */
                   2976: int	type_raster ( raster *rp, FILE *fp )
                   2977: {
                   2978: /* -------------------------------------------------------------------------
                   2979: Allocations and Declarations
                   2980: -------------------------------------------------------------------------- */
                   2981: static	int display_width = 72;		/* max columns for display */
                   2982: static	char display_chars[16] =	/* display chars for bytemap */
                   2983: 	{ ' ','1','2','3','4','5','6','7','8','9','A','B','C','D','E','*' };
                   2984: char	scanline[133];			/* ascii image for one scan line */
                   2985: int	scan_width;			/* #chars in scan (<=display_width)*/
                   2986: int	irow, locol,hicol=(-1);		/* height index, width indexes */
1.2       albertel 2987: raster	*gftobitmap(), *bitmaprp=rp;	/* convert .gf to bitmap if needed */
                   2988: int	delete_raster();		/*free bitmap converted for display*/
1.1       albertel 2989: /* --------------------------------------------------------------------------
                   2990: initialization
                   2991: -------------------------------------------------------------------------- */
                   2992: /* --- redirect null fp --- */
                   2993: if ( fp == (FILE *)NULL ) fp = stdout;	/* default fp to stdout if null */
1.3     ! albertel 2994: if ( msglevel >= 999 ) { fprintf(fp,	/* debugging diagnostics */
        !          2995:   "type_raster> width=%d height=%d ...\n",
        !          2996:   rp->width,rp->height); fflush(fp); }
1.1       albertel 2997: /* --- check for ascii string --- */
                   2998: if ( isstring				/* pixmap has string, not raster */
1.2       albertel 2999: ||   (0 && rp->height==1) )		/* infer input rp is a string */
1.1       albertel 3000:  {
                   3001:  char *string = (char *)(rp->pixmap);	/*interpret pixmap as ascii string*/
                   3002:  int width = strlen(string);		/* #chars in ascii string */
                   3003:  while ( width > display_width-2 )	/* too big for one line */
                   3004:   { fprintf(fp,"\"%.*s\"\n",display_width-2,string); /*display leading chars*/
                   3005:     string += (display_width-2);	/* bump string past displayed chars*/
                   3006:     width -= (display_width-2); }	/* decrement remaining width */
                   3007:  fprintf(fp,"\"%.*s\"\n",width,string);	/* display trailing chars */
                   3008:  return ( 1 );
                   3009:  } /* --- end-of-if(isstring) --- */
                   3010: /* --------------------------------------------------------------------------
                   3011: display ascii dump of bitmap image (in segments if display_width < rp->width)
                   3012: -------------------------------------------------------------------------- */
1.2       albertel 3013: if ( rp->format == 2			/* input is .gf-formatted */
                   3014: ||   rp->format == 3 )
                   3015:   bitmaprp = gftobitmap(rp);		/* so convert it for display */
                   3016: if ( bitmaprp != NULL )			/* if we have image for display */
                   3017:  while ( (locol=hicol+1) < rp->width )	/*start where prev segment left off*/
1.1       albertel 3018:   {
                   3019:   /* --- set hicol for this pass (locol set above) --- */
                   3020:   hicol += display_width;		/* show as much as display allows */
                   3021:   if (hicol >= rp->width) hicol = rp->width - 1; /*but not more than raster*/
                   3022:   scan_width = hicol-locol+1;		/* #chars in this scan */
                   3023:   if ( locol > 0 ) fprintf(fp,"----------\n"); /*separator between segments*/
                   3024:   /* ------------------------------------------------------------------------
                   3025:   display all scan lines for this local...hicol segment range
                   3026:   ------------------------------------------------------------------------ */
                   3027:   for ( irow=0; irow<rp->height; irow++ )  /* all scan lines for col range */
                   3028:     {
                   3029:     /* --- allocations and declarations --- */
                   3030:     int	ipix,				/* pixmap[] index for this scan */
                   3031: 	lopix = irow*rp->width + locol;	/*first pixmap[] pixel in this scan*/
                   3032:     /* --- set chars in scanline[] based on pixels in rp->pixmap[] --- */
                   3033:     for ( ipix=0; ipix<scan_width; ipix++ ) /* set each char */
1.2       albertel 3034:       if ( bitmaprp->pixsz == 1 )	/*' '=0 or '*'=1 to display bitmap*/
                   3035: 	scanline[ipix]=(getlongbit(bitmaprp->pixmap,lopix+ipix)==1? '*':'.');
1.1       albertel 3036:       else				/* should have a bytemap */
1.2       albertel 3037:        if ( bitmaprp->pixsz == 8 )	/* double-check pixsz for bytemap */
                   3038: 	{ int pixval = (int)((bitmaprp->pixmap)[lopix+ipix]), /*byte value*/
1.1       albertel 3039: 	  ichar = min2(15,pixval/16);	/* index for ' ', '1'...'e', '*' */
                   3040: 	  scanline[ipix] = display_chars[ichar]; } /*set ' ' for 0-15, etc*/
                   3041:     /* --- display completed scan line --- */
                   3042:     fprintf(fp,"%.*s\n",scan_width,scanline);	
                   3043:     } /* --- end-of-for(irow) --- */
                   3044:   } /* --- end-of-while(hicol<rp->width) --- */
                   3045: /* -------------------------------------------------------------------------
                   3046: Back to caller with 1=okay, 0=failed.
                   3047: -------------------------------------------------------------------------- */
1.2       albertel 3048: if ( rp->format == 2			/* input was .gf-format */
                   3049: ||   rp->format == 3 )
                   3050:   if ( bitmaprp != NULL )		/* and we converted it for display */
                   3051:     delete_raster(bitmaprp);		/* no longer needed, so free it */
1.1       albertel 3052: return ( 1 );
                   3053: } /* --- end-of-function type_raster() --- */
                   3054: 
                   3055: 
                   3056: /* ==========================================================================
                   3057:  * Function:	type_bytemap ( bp, grayscale, width, height, fp )
                   3058:  * Purpose:	Emit an ascii dump representing bp, on fp.
                   3059:  * --------------------------------------------------------------------------
                   3060:  * Arguments:	bp (I)		intbyte * to bytemap for which an
                   3061:  *				ascii dump is to be constructed.
                   3062:  *		grayscale (I)	int containing #gray shades, 256 for 8-bit
                   3063:  *		width (I)	int containing #cols in bytemap
                   3064:  *		height (I)	int containing #rows in bytemap
                   3065:  *		fp (I)		File ptr to output device (defaults to
                   3066:  *				stdout if passed as NULL).
                   3067:  * --------------------------------------------------------------------------
                   3068:  * Returns:	( int )		1 if completed successfully,
                   3069:  *				or 0 otherwise (for any error).
                   3070:  * --------------------------------------------------------------------------
                   3071:  * Notes:
                   3072:  * ======================================================================= */
                   3073: /* --- entry point --- */
                   3074: int	type_bytemap ( intbyte *bp, int grayscale,
                   3075: 			int width, int height, FILE *fp )
                   3076: {
                   3077: /* -------------------------------------------------------------------------
                   3078: Allocations and Declarations
                   3079: -------------------------------------------------------------------------- */
                   3080: static	int display_width = 72;		/* max columns for display */
                   3081: int	byte_width = 3,			/* cols to display byte (ff+space) */
                   3082: 	maxbyte = 0;			/* if maxbyte<16, set byte_width=2 */
                   3083: int	white_byte = 0,			/* show dots for white_byte's */
                   3084: 	black_byte = grayscale-1;	/* show stars for black_byte's */
                   3085: char	scanline[133];			/* ascii image for one scan line */
                   3086: int	scan_width,			/* #chars in scan (<=display_width)*/
                   3087: 	scan_cols;			/* #cols in scan (hicol-locol+1) */
                   3088: int	ibyte,				/* bp[] index */
                   3089: 	irow, locol,hicol=(-1);		/* height index, width indexes */
                   3090: /* --------------------------------------------------------------------------
                   3091: initialization
                   3092: -------------------------------------------------------------------------- */
                   3093: /* --- redirect null fp --- */
                   3094: if ( fp == (FILE *)NULL ) fp = stdout;	/* default fp to stdout if null */
                   3095: /* --- check for ascii string --- */
                   3096: if ( isstring )				/* bp has ascii string, not raster */
                   3097:  { width = strlen((char *)bp);		/* #chars in ascii string */
                   3098:    height = 1; }			/* default */
                   3099: /* --- see if we can get away with byte_width=1 --- */
                   3100: for ( ibyte=0; ibyte<width*height; ibyte++ )  /* check all bytes */
                   3101:   { int	byteval = (int)bp[ibyte];	/* current byte value */
                   3102:     if ( byteval < black_byte )		/* if it's less than black_byte */
                   3103:       maxbyte = max2(maxbyte,byteval); } /* then find max non-black value */
                   3104: if ( maxbyte < 16 )			/* bytevals will fit in one column */
                   3105:   byte_width = 1;			/* so reset display byte_width */
                   3106: /* --------------------------------------------------------------------------
                   3107: display ascii dump of bitmap image (in segments if display_width < rp->width)
                   3108: -------------------------------------------------------------------------- */
                   3109: while ( (locol=hicol+1) < width )	/*start where prev segment left off*/
                   3110:   {
                   3111:   /* --- set hicol for this pass (locol set above) --- */
                   3112:   hicol += display_width/byte_width;	/* show as much as display allows */
                   3113:   if (hicol >= width) hicol = width - 1; /* but not more than bytemap */
                   3114:   scan_cols = hicol-locol+1;		/* #cols in this scan */
                   3115:   scan_width = byte_width*scan_cols;	/* #chars in this scan */
                   3116:   if ( locol>0 && !isstring ) fprintf(fp,"----------\n"); /* separator */
                   3117:   /* ------------------------------------------------------------------------
                   3118:   display all scan lines for this local...hicol segment range
                   3119:   ------------------------------------------------------------------------ */
                   3120:   for ( irow=0; irow<height; irow++ )	/* all scan lines for col range */
                   3121:     {
                   3122:     /* --- allocations and declarations --- */
                   3123:     int  lobyte = irow*width + locol;	/* first bp[] byte in this scan */
                   3124:     char scanbyte[32];			/* sprintf() buffer for byte */
                   3125:     /* --- set chars in scanline[] based on bytes in bytemap bp[] --- */
                   3126:     memset(scanline,' ',scan_width);	/* blank out scanline */
                   3127:     for ( ibyte=0; ibyte<scan_cols; ibyte++ ) /* set chars for each col */
                   3128:       {	int byteval = (int)bp[lobyte+ibyte];  /* value of current byte */
                   3129: 	memset(scanbyte,'.',byte_width); /* dot-fill scanbyte */
                   3130: 	if ( byteval == black_byte )	/* but if we have a black byte */
                   3131: 	  memset(scanbyte,'*',byte_width); /* star-fill scanbyte instead */
                   3132: 	if ( byte_width > 1 )		/* don't blank out single char */
                   3133: 	  scanbyte[byte_width-1] = ' ';	/* blank-fill rightmost character */
                   3134: 	if ( byteval != white_byte	/* format bytes that are non-white */
                   3135: 	&&   byteval != black_byte )	/* and that are non-black */
                   3136: 	  sprintf(scanbyte,"%*x ",max2(1,byte_width-1),byteval); /*hex-format*/
                   3137: 	memcpy(scanline+ibyte*byte_width,scanbyte,byte_width); } /*in line*/
                   3138:     /* --- display completed scan line --- */
                   3139:     fprintf(fp,"%.*s\n",scan_width,scanline);	
                   3140:     } /* --- end-of-for(irow) --- */
                   3141:   } /* --- end-of-while(hicol<width) --- */
                   3142: /* -------------------------------------------------------------------------
                   3143: Back to caller with 1=okay, 0=failed.
                   3144: -------------------------------------------------------------------------- */
                   3145: return ( 1 );
                   3146: } /* --- end-of-function type_bytemap() --- */
                   3147: 
                   3148: 
                   3149: /* ==========================================================================
                   3150:  * Function:	xbitmap_raster ( rp, fp )
                   3151:  * Purpose:	Emit a mime xbitmap representing rp, on fp.
                   3152:  * --------------------------------------------------------------------------
                   3153:  * Arguments:	rp (I)		ptr to raster struct for which a mime
                   3154:  *				xbitmap is to be constructed.
                   3155:  *		fp (I)		File ptr to output device (defaults to
                   3156:  *				stdout if passed as NULL).
                   3157:  * --------------------------------------------------------------------------
                   3158:  * Returns:	( int )		1 if completed successfully,
                   3159:  *				or 0 otherwise (for any error).
                   3160:  * --------------------------------------------------------------------------
                   3161:  * Notes:
                   3162:  * ======================================================================= */
                   3163: /* --- entry point --- */
                   3164: int	xbitmap_raster ( raster *rp, FILE *fp )
                   3165: {
                   3166: /* -------------------------------------------------------------------------
                   3167: Allocations and Declarations
                   3168: -------------------------------------------------------------------------- */
                   3169: char	*title = "image";		/* dummy title */
                   3170: int	hex_bitmap();			/* dump bitmap as hex bytes */
                   3171: /* --------------------------------------------------------------------------
                   3172: emit text to display mime xbitmap representation of rp->bitmap image
                   3173: -------------------------------------------------------------------------- */
                   3174: /* --- first redirect null fp --- */
                   3175: if ( fp == (FILE *)NULL ) fp = stdout;	/* default fp to stdout if null */
                   3176: /* --- check for ascii string --- */
                   3177: if ( isstring )				/* pixmap has string, not raster */
                   3178:  return ( 0 );				/* can't handle ascii string */
                   3179: /* --- emit prologue strings and hex dump of bitmap for mime xbitmap --- */
                   3180: fprintf( fp, "Content-type: image/x-xbitmap\n\n" );
                   3181: fprintf( fp, "#define %s_width %d\n#define %s_height %d\n",
                   3182: 	title,rp->width, title,rp->height );
                   3183: fprintf( fp, "static char %s_bits[] = {\n", title );
                   3184: hex_bitmap(rp,fp,0,0);			/* emit hex dump of bitmap bytes */
                   3185: fprintf (fp,"};\n");			/* ending with "};" for C array */
                   3186: /* -------------------------------------------------------------------------
                   3187: Back to caller with 1=okay, 0=failed.
                   3188: -------------------------------------------------------------------------- */
                   3189: return ( 1 );
                   3190: } /* --- end-of-function xbitmap_raster() --- */
                   3191: 
                   3192: 
                   3193: /* ==========================================================================
1.2       albertel 3194:  * Function:	type_pbmpgm ( rp, ptype, file )
                   3195:  * Purpose:	Write pbm or pgm image of rp to file
                   3196:  * --------------------------------------------------------------------------
                   3197:  * Arguments:	rp (I)		ptr to raster struct for which
                   3198:  *				a pbm/pgm file is to be written.
                   3199:  *		ptype (I)	int containing 1 for pbm, 2 for pgm, or
                   3200:  *				0 to determine ptype from values in rp
                   3201:  *		file (I)	ptr to null-terminated char string
                   3202:  *				containing name of fuke to be written
                   3203:  *				(see notes below).
                   3204:  * --------------------------------------------------------------------------
                   3205:  * Returns:	( int )		total #bytes written,
                   3206:  *				or 0 for any error.
                   3207:  * --------------------------------------------------------------------------
                   3208:  * Notes:     o	(a) If file==NULL, output is written to stdout;
                   3209:  *		(b) if *file=='\000' then file is taken as the
                   3210:  *		    address of an output buffer to which output
                   3211:  *		    is written (and is followed by a terminating
                   3212:  *		    '\0' which is not counted in #bytes returned);
                   3213:  *		(c) otherwise file is the filename (opened and
                   3214:  *		    closed internally) to which output is written,
                   3215:  *		    except that any final .ext extension is replaced
                   3216:  *		    by .pbm or .pgm depending on ptype.
                   3217:  * ======================================================================= */
                   3218: /* --- entry point --- */
                   3219: int	type_pbmpgm ( raster *rp, int ptype, char *file )
                   3220: {
                   3221: /* -------------------------------------------------------------------------
                   3222: Allocations and Declarations
                   3223: -------------------------------------------------------------------------- */
                   3224: int	isokay=0, nbytes=0;	/* completion flag, total #bytes written */
                   3225: int	irow=0, jcol=0;		/*height(row), width(col) indexes in raster*/
                   3226: int	pixmin=9999, pixmax=(-9999), /* min, max pixel value in raster */
                   3227: 	ngray = 0;		/* #gray scale values */
                   3228: FILE	/* *fopen(), */ *fp=NULL; /* pointer to output file (or NULL) */
                   3229: char	outline[1024], outfield[256], /* output line, field */
                   3230: 	cr[16] = "\n\000";	/* cr at end-of-line */
                   3231: int	maxlinelen = 70;	/* maximum allowed line length */
                   3232: int	pixfrac=6;		/* use (pixmax-pixmin)/pixfrac as step */
                   3233: static	char
                   3234: 	*suffix[] = { NULL, ".pbm", ".pgm" },	/* file.suffix[ptype] */
                   3235: 	*magic[] = { NULL, "P1", "P2" },	/*identifying "magic number"*/
                   3236: 	*mode[] = { NULL, "w", "w" };		/* fopen() mode[ptype] */
                   3237: /* -------------------------------------------------------------------------
                   3238: check input, determine grayscale,  and set up output file if necessary
                   3239: -------------------------------------------------------------------------- */
                   3240: /* --- check input args --- */
                   3241: if ( rp == NULL ) goto end_of_job;	/* no input raster provided */
                   3242: if ( ptype != 0 )			/* we'll determine ptype below */
                   3243:  if ( ptype<1 || ptype>2 ) goto end_of_job; /*invalid output graphic format*/
                   3244: /* --- determine largest (and smallest) value in pixmap --- */
                   3245: for ( irow=0; irow<rp->height; irow++ )	/* for each row, top-to-bottom */
                   3246:  for ( jcol=0; jcol<rp->width; jcol++ )	/* for each col, left-to-right */
                   3247:   { int	pixval = getpixel(rp,irow,jcol);  /* value of pixel at irow,jcol */
                   3248:     pixmin = min2(pixmin,pixval);	/* new minimum */
                   3249:     pixmax = max2(pixmax,pixval); }	/* new maximum */
                   3250: ngray = 1 + (pixmax-pixmin);		/* should be 2 for b/w bitmap */
                   3251: if ( ptype == 0 )			/* caller wants us to set ptype */
                   3252:   ptype = (ngray>=3?2:1);		/* use grayscale if >2 shades */
                   3253: /* --- open output file if necessary --- */
                   3254: if ( file == NULL ) fp = stdout;	/*null ptr signals output to stdout*/
                   3255: else if ( *file != '\000' ) {		/* explicit filename provided, so...*/
                   3256:   char	fname[512], *pdot=NULL;		/* file.ext, ptr to last . in fname*/
                   3257:   strncpy(fname,file,255);		/* local copy of file name */
                   3258:   fname[255] = '\000';			/* make sure it's null terminated */
                   3259:   if ( (pdot=strrchr(fname,'.')) == NULL ) /*no extension on original name*/
                   3260:     strcat(fname,suffix[ptype]);	/* so add extension */
                   3261:   else					/* we already have an extension */
                   3262:     strcpy(pdot,suffix[ptype]);		/* so replace original extension */
                   3263:   if ( (fp = fopen(fname,mode[ptype]))	/* open output file */
                   3264:   ==   (FILE *)NULL ) goto end_of_job;	/* quit if failed to open */
                   3265:   } /* --- ens-of-if(*file!='\0') --- */
                   3266: /* -------------------------------------------------------------------------
                   3267: format and write header
                   3268: -------------------------------------------------------------------------- */
                   3269: /* --- format header info --- */
                   3270: *outline = '\000';			/* initialize line buffer */
                   3271: strcat(outline,magic[ptype]);		/* begin file with "magic number" */
                   3272: strcat(outline,cr);			/* followed by cr to end line */
                   3273: sprintf(outfield,"%d %d",rp->width,rp->height); /* format width and height */
                   3274: strcat(outline,outfield);		/* add width and height to header */
                   3275: strcat(outline,cr);			/* followed by cr to end line */
                   3276: if ( ptype == 2 )			/* need max grayscale value */
                   3277:   { sprintf(outfield,"%d",pixmax);	/* format maximum pixel value */
                   3278:     strcat(outline,outfield);		/* add max value to header */
                   3279:     strcat(outline,cr); }		/* followed by cr to end line */
                   3280: /* --- write header to file or memory buffer --- */
                   3281: if ( fp == NULL )			/* if we have no open file... */
                   3282:   strcat(file,outline);			/* add header to caller's buffer */
                   3283: else					/* or if we have an open file... */
                   3284:   if ( fputs(outline,fp)		/* try writing header to open file */
                   3285:   ==   EOF ) goto end_of_job;		/* return with error if failed */
                   3286: nbytes += strlen(outline);		/* bump output byte count */
                   3287: /* -------------------------------------------------------------------------
                   3288: format and write pixels
                   3289: -------------------------------------------------------------------------- */
                   3290: *outline = '\000';			/* initialize line buffer */
                   3291: for ( irow=0; irow<=rp->height; irow++ ) /* for each row, top-to-bottom */
                   3292:  for ( jcol=0; jcol<rp->width; jcol++ )	{ /* for each col, left-to-right */
                   3293:   /* --- format value at irow,jcol--- */
                   3294:   *outfield = '\000';			/* init empty field */
                   3295:   if ( irow < rp->height ) {		/* check row index */
                   3296:     int	pixval = getpixel(rp,irow,jcol);  /* value of pixel at irow,jcol */
                   3297:     if ( ptype == 1 )			/* pixval must be 1 or 0 */
                   3298:       pixval = (pixval>pixmin+((pixmax-pixmin)/pixfrac)?1:0);
                   3299:     sprintf(outfield,"%d ",pixval); }	/* format pixel value */
                   3300:   /* --- write line if this value won't fit on it (or last line) --- */
                   3301:   if ( strlen(outline)+strlen(outfield)+strlen(cr) >= maxlinelen /*won't fit*/
                   3302:   ||   irow >= rp->height ) {		/* force writing last line */
                   3303:     strcat(outline,cr);			/* add cr to end current line */
                   3304:     if ( fp == NULL )			/* if we have no open file... */
                   3305:       strcat(file,outline);		/* add header to caller's buffer */
                   3306:     else				/* or if we have an open file... */
                   3307:       if ( fputs(outline,fp)		/* try writing header to open file */
                   3308:       ==   EOF ) goto end_of_job;	/* return with error if failed */
                   3309:     nbytes += strlen(outline);		/* bump output byte count */
                   3310:     *outline = '\000';			/* re-initialize line buffer */
                   3311:     } /* --- end-of-if(strlen>=maxlinelen) --- */
                   3312:   if ( irow >= rp->height ) break;	/* done after writing last line */
                   3313:   /* --- concatanate value to line -- */
                   3314:   strcat(outline,outfield);		/* concatanate value to line */
                   3315:   } /* --- end-of-for(jcol,irow) --- */
                   3316: isokay = 1;				/* signal successful completion */
                   3317: /* -------------------------------------------------------------------------
                   3318: Back to caller with total #bytes written, or 0=failed.
                   3319: -------------------------------------------------------------------------- */
                   3320: end_of_job:
                   3321:   if ( fp != NULL			/* output written to an open file */
                   3322:   &&   fp != stdout )			/* and it's not just stdout */
                   3323:     fclose(fp);				/* so close file before returning */
                   3324:   return ( (isokay?nbytes:0) );		/*back to caller with #bytes written*/
                   3325: } /* --- end-of-function type_pbmpgm() --- */
                   3326: 
                   3327: 
                   3328: /* ==========================================================================
1.1       albertel 3329:  * Function:	cstruct_chardef ( cp, fp, col1 )
                   3330:  * Purpose:	Emit a C struct of cp on fp, starting in col1.
                   3331:  * --------------------------------------------------------------------------
                   3332:  * Arguments:	cp (I)		ptr to chardef struct for which
                   3333:  *				a C struct is to be generated.
                   3334:  *		fp (I)		File ptr to output device (defaults to
                   3335:  *				stdout if passed as NULL).
                   3336:  *		col1 (I)	int containing 0...65; output lines
                   3337:  *				are preceded by col1 blanks.
                   3338:  * --------------------------------------------------------------------------
                   3339:  * Returns:	( int )		1 if completed successfully,
                   3340:  *				or 0 otherwise (for any error).
                   3341:  * --------------------------------------------------------------------------
                   3342:  * Notes:
                   3343:  * ======================================================================= */
                   3344: /* --- entry point --- */
                   3345: int	cstruct_chardef ( chardef *cp, FILE *fp, int col1 )
                   3346: {
                   3347: /* -------------------------------------------------------------------------
                   3348: Allocations and Declarations
                   3349: -------------------------------------------------------------------------- */
                   3350: char	field[64];		/* field within output line */
                   3351: int	cstruct_raster(),	/* emit a raster */
                   3352: 	emit_string();		/* emit a string and comment */
                   3353: /* -------------------------------------------------------------------------
                   3354: emit   charnum, location, name  /  hirow, hicol,  lorow, locol
                   3355: -------------------------------------------------------------------------- */
                   3356: /* --- charnum, location, name --- */
                   3357: sprintf(field,"{ %3d,%5d,\n", cp->charnum,cp->location);  /*char#,location*/
                   3358: emit_string ( fp, col1, field, "character number, location");
                   3359: /* --- toprow, topleftcol,   botrow, botleftcol  --- */
1.2       albertel 3360: sprintf(field,"  %3d,%2d,  %3d,%2d,\n",		/* format... */
1.1       albertel 3361:   cp->toprow,cp->topleftcol,			/* toprow, topleftcol, */
                   3362:   cp->botrow,cp->botleftcol);			/* and botrow, botleftcol */
                   3363: emit_string ( fp, col1, field, "topleft row,col, and botleft row,col");
                   3364: /* -------------------------------------------------------------------------
                   3365: emit raster and chardef's closing brace, and then return to caller
                   3366: -------------------------------------------------------------------------- */
                   3367: cstruct_raster(&cp->image,fp,col1+4);		/* emit raster */
                   3368: emit_string ( fp, 0, "  }", NULL);		/* emit closing brace */
                   3369: return ( 1 );			/* back to caller with 1=okay, 0=failed */
                   3370: } /* --- end-of-function cstruct_chardef() --- */
                   3371: 
                   3372: 
                   3373: /* ==========================================================================
                   3374:  * Function:	cstruct_raster ( rp, fp, col1 )
                   3375:  * Purpose:	Emit a C struct of rp on fp, starting in col1.
                   3376:  * --------------------------------------------------------------------------
                   3377:  * Arguments:	rp (I)		ptr to raster struct for which
                   3378:  *				a C struct is to be generated.
                   3379:  *		fp (I)		File ptr to output device (defaults to
                   3380:  *				stdout if passed as NULL).
                   3381:  *		col1 (I)	int containing 0...65; output lines
                   3382:  *				are preceded by col1 blanks.
                   3383:  * --------------------------------------------------------------------------
                   3384:  * Returns:	( int )		1 if completed successfully,
                   3385:  *				or 0 otherwise (for any error).
                   3386:  * --------------------------------------------------------------------------
                   3387:  * Notes:
                   3388:  * ======================================================================= */
                   3389: /* --- entry point --- */
                   3390: int	cstruct_raster ( raster *rp, FILE *fp, int col1 )
                   3391: {
                   3392: /* -------------------------------------------------------------------------
                   3393: Allocations and Declarations
                   3394: -------------------------------------------------------------------------- */
                   3395: char	field[64];		/* field within output line */
                   3396: char	typecast[64] = "(pixbyte *)"; /* type cast for pixmap string */
                   3397: int	hex_bitmap();		/* to emit raster bitmap */
                   3398: int	emit_string();		/* emit a string and comment */
                   3399: /* -------------------------------------------------------------------------
                   3400: emit width and height
                   3401: -------------------------------------------------------------------------- */
1.2       albertel 3402: sprintf(field,"{ %2d,  %3d,%2d,%2d, %s\n", /* format width,height,pixsz */
                   3403: 	rp->width,rp->height,rp->format,rp->pixsz,typecast);
                   3404: emit_string ( fp, col1, field, "width,ht, fmt,pixsz,map...");
1.1       albertel 3405: /* -------------------------------------------------------------------------
                   3406: emit bitmap and closing brace, and return to caller
                   3407: -------------------------------------------------------------------------- */
                   3408: hex_bitmap(rp,fp,col1+2,1);	/* emit bitmap */
                   3409: emit_string ( fp, 0, " }", NULL); /* emit closing brace */
                   3410: return ( 1 );			/* back to caller with 1=okay, 0=failed */
                   3411: } /* --- end-of-function cstruct_raster() --- */
                   3412: 
                   3413: 
                   3414: /* ==========================================================================
                   3415:  * Function:	hex_bitmap ( rp, fp, col1, isstr )
                   3416:  * Purpose:	Emit a hex dump of the bitmap of rp on fp, starting in col1.
                   3417:  *		If isstr (is string) is true, the dump is of the form
                   3418:  *			"\x01\x02\x03\x04\x05..."
                   3419:  *		Otherwise, if isstr is false, the dump is of the form
                   3420:  *			0x01,0x02,0x03,0x04,0x05...
                   3421:  * --------------------------------------------------------------------------
                   3422:  * Arguments:	rp (I)		ptr to raster struct for which
                   3423:  *				a hex dump is to be constructed.
                   3424:  *		fp (I)		File ptr to output device (defaults to
                   3425:  *				stdout if passed as NULL).
                   3426:  *		col1 (I)	int containing 0...65; output lines
                   3427:  *				are preceded by col1 blanks.
                   3428:  *		isstr (I)	int specifying dump format as described above
                   3429:  * --------------------------------------------------------------------------
                   3430:  * Returns:	( int )		1 if completed successfully,
                   3431:  *				or 0 otherwise (for any error).
                   3432:  * --------------------------------------------------------------------------
                   3433:  * Notes:
                   3434:  * ======================================================================= */
                   3435: /* --- entry point --- */
                   3436: int	hex_bitmap ( raster *rp, FILE *fp, int col1, int isstr )
                   3437: {
                   3438: /* -------------------------------------------------------------------------
                   3439: Allocations and Declarations
                   3440: -------------------------------------------------------------------------- */
1.2       albertel 3441: int	ibyte,				/* pixmap[ibyte] index */
                   3442: 	nbytes = pixbytes(rp);		/*#bytes in bitmap or .gf-formatted*/
1.1       albertel 3443: char	stub[64]="                                ";/* col1 leading blanks */
                   3444: int	linewidth = 64,			/* (roughly) rightmost column */
                   3445: 	colwidth = (isstr? 4:5);	/* #cols required for each byte */
                   3446: int	ncols = (linewidth-col1)/colwidth; /* new line after ncols bytes */
                   3447: /* --------------------------------------------------------------------------
                   3448: initialization
                   3449: -------------------------------------------------------------------------- */
                   3450: /* --- redirect null fp --- */
                   3451: if ( fp == (FILE *)NULL ) fp = stdout;	/* default fp to stdout if null */
                   3452: /* --- emit initial stub if wanted --- */
                   3453: if ( col1 > 0 ) fprintf(fp,"%.*s",col1,stub); /* stub preceding 1st line */
                   3454: /* --------------------------------------------------------------------------
                   3455: emit hex dump of rp->bitmap image
                   3456: -------------------------------------------------------------------------- */
                   3457: if ( isstr ) fprintf(fp,"\"");		/* opening " before first line */
                   3458: for ( ibyte=0; ibyte<nbytes; ibyte++ )	/* one byte at a time */
                   3459:   {
                   3460:   /* --- display a byte as hex char or number, depending on isstr --- */
                   3461:   if ( isstr )				/* string format wanted */
                   3462:     fprintf(fp,"\\x%02x",(rp->pixmap)[ibyte]);	/*print byte as hex char*/
                   3463:   else					/* comma-separated format wanted */
                   3464:     fprintf(fp,"0x%02x",(rp->pixmap)[ibyte]);	/*print byte as hex number*/
                   3465:   /* --- add a separator and newline, etc, as necessary --- */
                   3466:   if ( ibyte < nbytes-1)		/* not the last byte yet */
                   3467:     {
                   3468:     if ( !isstr ) fprintf(fp,",");	/* follow hex number with comma */
1.3     ! albertel 3469:     if ( (ibyte+1)%ncols==0 ) {		/* need new line after every ncols */
1.1       albertel 3470:       if ( !isstr )			/* for hex numbers format ... */
                   3471: 	fprintf(fp,"\n%.*s",col1,stub);	/* ...just need newline and stub */
                   3472:       else				/* for string format... */
1.3     ! albertel 3473: 	fprintf(fp,"\"\n%.*s\"",col1,stub); } /*...need closing, opening "s*/
1.1       albertel 3474:     } /* --- end-of-if(ibyte<nbytes-1) --- */
                   3475:   } /* --- end-of-for(ibyte) --- */
                   3476: if ( isstr ) fprintf(fp,"\"");		/* closing " after last line */
                   3477: return ( 1 );				/* back with 1=okay, 0=failed */
                   3478: } /* --- end-of-function hex_bitmap() --- */
                   3479: 
                   3480: 
                   3481: /* ==========================================================================
                   3482:  * Function:	emit_string ( fp, col1, string, comment )
                   3483:  * Purpose:	Emit string on fp, starting in col1,
                   3484:  *		and followed by right-justified comment.
                   3485:  * --------------------------------------------------------------------------
                   3486:  * Arguments:	fp (I)		File ptr to output device (defaults to
                   3487:  *				stdout if passed as NULL).
                   3488:  *		col1 (I)	int containing 0 or #blanks preceding string
                   3489:  *		string (I)	char *  containing string to be emitted.
                   3490:  *				If last char of string is '\n',
                   3491:  *				the emitted line ends with a newline,
                   3492:  *				otherwise not.
                   3493:  *		comment (I)	NULL or char * containing right-justified
                   3494:  *				comment (we enclose between /star and star/)
                   3495:  * --------------------------------------------------------------------------
                   3496:  * Returns:	( int )		1 if completed successfully,
                   3497:  *				or 0 otherwise (for any error).
                   3498:  * --------------------------------------------------------------------------
                   3499:  * Notes:     o
                   3500:  * ======================================================================= */
                   3501: /* --- entry point --- */
                   3502: int	emit_string ( FILE *fp, int col1, char *string, char *comment )
                   3503: {
                   3504: /* -------------------------------------------------------------------------
                   3505: Allocations and Declarations
                   3506: -------------------------------------------------------------------------- */
                   3507: char	line[256];		/* construct line with caller's fields */
                   3508: int	fieldlen;		/* #chars in one of caller's fields */
                   3509: int	linelen = 72;		/*line length (for right-justified comment)*/
                   3510: int	isnewline = 0;		/* true to emit \n at end of line */
                   3511: /* --------------------------------------------------------------------------
                   3512: construct line containing prolog, string, epilog, and finally comment
                   3513: -------------------------------------------------------------------------- */
                   3514: /* --- init line --- */
                   3515: memset(line,' ',255);			/* start line with blanks */
                   3516: /* --- embed string into line --- */
                   3517: if ( string != NULL )			/* if caller gave us a string... */
                   3518:   { fieldlen = strlen(string);		/* #cols required for string */
                   3519:     if ( string[fieldlen-1] == '\n' )	/* check last char for newline */
                   3520:       {	isnewline = 1;			/* got it, so set flag */
                   3521: 	fieldlen--; }			/* but don't print it yet */
                   3522:     memcpy(line+col1,string,fieldlen);	/* embid string starting at col1 */
                   3523:     col1 += fieldlen; }			/* bump col past epilog */
                   3524: /* --- embed comment into line --- */
                   3525: if ( comment != NULL )			/* if caller gave us a comment... */
                   3526:   { fieldlen = 6 + strlen(comment);	/* plus  /star, star/, 2 spaces */
                   3527:     if ( linelen-fieldlen < col1 )	/* comment won't fit */
                   3528:       fieldlen -= (col1 - (linelen-fieldlen)); /* truncate comment to fit */
                   3529:     if ( fieldlen > 6 )			/* can fit all or part of comment */
                   3530:       sprintf(line+linelen-fieldlen,"/%c %.*s %c/", /* so embed it in line */
                   3531: 	'*', fieldlen-6,comment, '*');
                   3532:     col1 = linelen; }			/* indicate line filled */
                   3533: /* --- line completed --- */
                   3534: line[col1] = '\000';			/* null-terminate completed line */
                   3535: /* -------------------------------------------------------------------------
                   3536: emit line, then back to caller with 1=okay, 0=failed.
                   3537: -------------------------------------------------------------------------- */
                   3538: /* --- first redirect null fp --- */
                   3539: if ( fp == (FILE *)NULL ) fp = stdout;	/* default fp to stdout if null */
                   3540: /* --- emit line (and optional newline) --- */
                   3541: fprintf(fp,"%.*s",linelen,line);	/* no more than linelen chars */
                   3542: if ( isnewline ) fprintf(fp,"\n");	/*caller wants terminating newline*/
                   3543: return ( 1 );
                   3544: } /* --- end-of-function emit_string() --- */
                   3545: 
                   3546: 
                   3547: /* ==========================================================================
1.2       albertel 3548:  * Function:	gftobitmap ( gf )
                   3549:  * Purpose:	convert .gf-like pixmap to bitmap image
                   3550:  * --------------------------------------------------------------------------
                   3551:  * Arguments:	gf (I)		raster * to struct in .gf-format
                   3552:  * --------------------------------------------------------------------------
                   3553:  * Returns:	( raster * )	image-format raster * if successful,
                   3554:  *				or NULL for any error.
                   3555:  * --------------------------------------------------------------------------
                   3556:  * Notes:     o
                   3557:  * ======================================================================= */
                   3558: /* --- entry point --- */
                   3559: raster	*gftobitmap ( raster *gf )
                   3560: {
                   3561: /* -------------------------------------------------------------------------
                   3562: Allocations and Declarations
                   3563: -------------------------------------------------------------------------- */
                   3564: raster	*new_raster(), *rp=NULL;	/* image raster retuned to caller */
                   3565: int	width=0, height=0, totbits=0;	/* gf->width, gf->height, #bits */
                   3566: int	format=0, icount=0, ncounts=0,	/*.gf format, count index, #counts*/
                   3567: 	ibit=0, bitval=0;		/* bitmap index, bit value */
                   3568: int	isrepeat = 1,			/* true to process repeat counts */
                   3569: 	repeatcmds[2] = {255,15},	/*opcode for repeat/duplicate count*/
                   3570: 	nrepeats=0, irepeat=0,		/* scan line repeat count,index */
                   3571: 	wbits = 0;			/* count bits to width of scan line*/
                   3572: /* -------------------------------------------------------------------------
                   3573: initialization
                   3574: -------------------------------------------------------------------------- */
                   3575: /* --- check args --- */
                   3576: if ( gf == NULL ) goto end_of_job;	/* input raster not provided */
                   3577: format = gf->format;			/* 2 or 3 */
                   3578: if ( format!=2 && format!=3 ) goto end_of_job; /* invalid raster format */
                   3579: ncounts = gf->pixsz;			/*pixsz is really #counts in pixmap*/
                   3580: /* --- allocate output raster with proper dimensions for bitmap --- */
                   3581: width=gf->width;  height=gf->height;	/* dimensions of raster */
                   3582: if ( (rp = new_raster(width,height,1))	/* allocate new raster and bitmap */
                   3583: ==   NULL ) goto end_of_job;		/* quit if failed to allocate */
                   3584: totbits = width*height;			/* total #bits in image */
                   3585: /* -------------------------------------------------------------------------
                   3586: fill bitmap
                   3587: -------------------------------------------------------------------------- */
                   3588: for ( icount=0,bitval=0; icount<ncounts; icount++ )
                   3589:   {
                   3590:   int	nbits = (int)(getbyfmt(format,gf->pixmap,icount)); /*#bits to set*/
                   3591:   if ( isrepeat				/* we're proxessing repeat counts */
1.3     ! albertel 3592:   &&   nbits == repeatcmds[format-2] ) { /* and repeat opcode found */
1.2       albertel 3593:    if ( nrepeats == 0 )			/* recursive repeat is error */
                   3594:     { nrepeats = (int)(getbyfmt(format,gf->pixmap,icount+1));/*repeat count*/
                   3595:       nbits = (int)(getbyfmt(format,gf->pixmap,icount+2)); /*#bits to set*/
                   3596:       icount += 2; }			/* bump byte/nibble count */
                   3597:    else					/* some internal error occurred */
                   3598:     if ( msgfp!=NULL && msglevel>=1 )	/* report error */
1.3     ! albertel 3599:      fprintf(msgfp,"gftobitmap> found embedded repeat command\n"); }
1.2       albertel 3600:   if ( 0 )
                   3601:     fprintf(stdout,
                   3602:     "gftobitmap> icount=%d bitval=%d nbits=%d ibit=%d totbits=%d\n",
                   3603:     icount,bitval,nbits,ibit,totbits);
                   3604:   for ( ; nbits>0; nbits-- )		/* count down */
                   3605:     { if ( ibit >= totbits ) goto end_of_job; /* overflow check */
                   3606:       for ( irepeat=0; irepeat<=nrepeats; irepeat++ )
                   3607:        if ( bitval == 1 )		/* set pixel */
                   3608: 	{ setlongbit(rp->pixmap,(ibit+irepeat*width)); }
                   3609:        else				/* clear pixel */
                   3610: 	{ unsetlongbit(rp->pixmap,(ibit+irepeat*width)); }
                   3611:       if ( nrepeats > 0 ) wbits++;	/* count another repeated bit */
                   3612:       ibit++; }				/* bump bit index */
                   3613:   bitval = 1-bitval;			/* flip bit value */
                   3614:   if ( wbits >= width ) {		/* completed repeats */
                   3615:    ibit += nrepeats*width;		/*bump bit count past repeated scans*/
                   3616:    if ( wbits > width )			/* out-of alignment error */
                   3617:     if ( msgfp!=NULL && msglevel>=1 )	/* report error */
                   3618:      fprintf(msgfp,"gftobitmap> width=%d wbits=%d\n",width,wbits);
                   3619:    wbits = nrepeats = 0; }		/* reset repeat counts */
                   3620:   } /* --- end-of-for(icount) --- */
                   3621: end_of_job:
                   3622:   return ( rp );			/* back to caller with image */
                   3623: } /* --- end-of-function gftobitmap() --- */
                   3624: 
                   3625: 
                   3626: /* ==========================================================================
1.1       albertel 3627:  * Function:	get_symdef ( symbol )
                   3628:  * Purpose:	returns mathchardef struct for symbol
                   3629:  * --------------------------------------------------------------------------
                   3630:  * Arguments:	symbol (I)	char *  containing symbol
                   3631:  *				whose corresponding mathchardef is wanted
                   3632:  * --------------------------------------------------------------------------
                   3633:  * Returns:	( mathchardef * )  pointer to struct defining symbol,
                   3634:  *				or NULL for any error
                   3635:  * --------------------------------------------------------------------------
                   3636:  * Notes:     o	Input symbol need only contain a leading substring to match,
                   3637:  *		e.g., \gam passed in symbol will match \gamma in the table.
                   3638:  *		If the table contains two or more possible matches,
                   3639:  *		the shortest is returned, e.g., input \e will return with
                   3640:  *		data for \eta rather than \epsilon.  To get \epsilon,
                   3641:  *		you must pass a leading substring long enough to eliminate
                   3642:  *		shorter table matches, i.e., in this case \ep
                   3643:  * ======================================================================= */
                   3644: /* --- entry point --- */
                   3645: mathchardef *get_symdef ( char *symbol )
                   3646: {
                   3647: /* -------------------------------------------------------------------------
                   3648: Allocations and Declarations
                   3649: -------------------------------------------------------------------------- */
                   3650: mathchardef *symdefs = symtable;	/* table of mathchardefs */
1.3     ! albertel 3651: int	ligdef=0, get_ligature();	/* or we may have a ligature */
1.1       albertel 3652: int	idef = 0,			/* symdefs[] index */
                   3653: 	bestdef = (-9999);		/*index of shortest matching symdef*/
                   3654: int	symlen = strlen(symbol),	/* length of input symbol */
                   3655: 	deflen, minlen=9999;		/*length of shortest matching symdef*/
                   3656: int	/*alnumsym = (symlen==1 && isalnum(*symbol)),*/ /*alphanumeric sym*/
                   3657: 	alphasym = (symlen==1 && isalpha(*symbol)); /* or alpha symbol */
1.2       albertel 3658: int	family = fontinfo[fontnum].family; /* current font family */
1.1       albertel 3659: static	char *displaysyms[][2] = {	/*xlate to Big sym for \displaystyle*/
1.2       albertel 3660: 	/* --- see table on page 536 in TLC2 --- */
1.1       albertel 3661: 	{"\\int",	"\\Bigint"},
                   3662: 	{"\\oint",	"\\Bigoint"},
                   3663: 	{"\\sum",	"\\Bigsum"},
                   3664: 	{"\\prod",	"\\Bigprod"},
                   3665: 	{"\\coprod",	"\\Bigcoprod"},
1.2       albertel 3666: 	/* --- must be 'big' when related to similar binary operators --- */
                   3667: 	{"\\bigcup",	"\\Bigcup"},
                   3668: 	{"\\bigsqcup",	"\\Bigsqcup"},
                   3669: 	{"\\bigcap",	"\\Bigcap"},
                   3670: 	/*{"\\bigsqcap", "\\sqcap"},*/	/* don't have \Bigsqcap */
                   3671: 	{"\\bigodot",	"\\Bigodot"},
                   3672: 	{"\\bigoplus",	"\\Bigoplus"},
                   3673: 	{"\\bigominus",	"\\ominus"},
                   3674: 	{"\\bigotimes",	"\\Bigotimes"},
                   3675: 	{"\\bigoslash",	"\\oslash"},
                   3676: 	{"\\biguplus",	"\\Biguplus"},
                   3677: 	{"\\bigwedge",	"\\Bigwedge"},
                   3678: 	{"\\bigvee",	"\\Bigvee"},
1.1       albertel 3679: 	{NULL, NULL} };
                   3680: /* -------------------------------------------------------------------------
1.3     ! albertel 3681: First check for ligature
        !          3682: -------------------------------------------------------------------------- */
        !          3683: isligature = 0;				/* init signal for no ligature */
        !          3684: if ( family == CYR10 )			/*only check for cyrillic ligatures*/
        !          3685:  if ( (ligdef=get_ligature(subexprptr,family)) /* check for ligature */
        !          3686:  >=    0  )				/* found a ligature */
        !          3687:   { bestdef = ligdef;			/* set bestdef for ligature */
        !          3688:     isligature = 1;			/* signal we found a ligature */
        !          3689:     goto end_of_job; }			/* so just give it to caller */
        !          3690: /* -------------------------------------------------------------------------
1.1       albertel 3691: If in \displaystyle mode, first xlate int to Bigint, etc.
                   3692: -------------------------------------------------------------------------- */
                   3693: if ( isdisplaystyle > 1 )		/* we're in \displaystyle mode */
                   3694:   for ( idef=0; ; idef++ ) {		/* lookup symbol in displaysyms */
                   3695:     char *fromsym = displaysyms[idef][0], /* look for this symbol */
                   3696: 	 *tosym = displaysyms[idef][1];	  /* and xlate it to this symbol */
                   3697:     if ( fromsym == NULL ) break;	/* end-of-table */
                   3698:     if ( !strcmp(symbol,fromsym) )	/* found a match */
                   3699:       {	if ( msglevel>=99 && msgfp!=NULL ) /* debugging output */
                   3700: 	 { fprintf(msgfp,"get_symdef> isdisplaystyle=%d, xlated %s to %s\n",
                   3701: 	   isdisplaystyle,symbol,tosym); fflush(msgfp); }
                   3702: 	symbol = tosym;			/* so look up tosym instead */
                   3703: 	symlen = strlen(symbol);	/* reset symbol length */
                   3704: 	break; }			/* no need to search further */
                   3705:     } /* --- end-of-for(idef) --- */
                   3706: /* -------------------------------------------------------------------------
                   3707: search symdefs[] in order for first occurrence of symbol
                   3708: -------------------------------------------------------------------------- */
                   3709: for ( idef=0; ;idef++ )			/* until trailer record found */
                   3710:   if ( symdefs[idef].symbol == NULL ) break; /* reached end-of-table */
                   3711:   else					/* check against caller's symbol */
                   3712:     if ( strncmp(symbol,symdefs[idef].symbol,symlen) == 0 ) /* found match */
1.3     ! albertel 3713:      if ( (fontnum==0||family==CYR10)	/* mathmode, so check every match */
1.2       albertel 3714:      || (0 && istextmode && (!alphasym	/* text mode and not alpha symbol */
                   3715: 	|| symdefs[idef].handler!=NULL))   /* or text mode and directive */
                   3716:      || (symdefs[idef].family==family	/* have correct family */
                   3717: 	&& symdefs[idef].handler==NULL) )  /* and not a handler collision */
                   3718: #if 0
                   3719:      || (fontnum==1 && symdefs[idef].family==CMR10)   /*textmode && rm text*/
                   3720:      || (fontnum==2 && symdefs[idef].family==CMMI10)  /*textmode && it text*/
                   3721:      || (fontnum==3 && symdefs[idef].family==BBOLD10  /*textmode && bb text*/
                   3722: 	&& symdefs[idef].handler==NULL)
                   3723:      || (fontnum==4 && symdefs[idef].family==CMMIB10  /*textmode && bf text*/
                   3724: 	&& symdefs[idef].handler==NULL) )
                   3725: #endif
1.1       albertel 3726:       if ( (deflen=strlen(symdefs[idef].symbol)) < minlen ) /*new best match*/
                   3727: 	{ bestdef = idef;		/* save index of new best match */
                   3728: 	  if ( (minlen = deflen)	/* and save its len for next test */
                   3729: 	  ==  symlen ) break; }		/*perfect match, so return with it*/
                   3730: if ( bestdef < 0 )			/* failed to look up symbol */
1.2       albertel 3731:   if ( fontnum != 0 )			/* we're in a restricted font mode */
                   3732:     { int oldfontnum = fontnum;		/* save current font family */
                   3733:       mathchardef *symdef = NULL;	/* lookup result with fontnum=0 */
                   3734:       fontnum = 0;			/*try to look up symbol in any font*/
                   3735:       symdef = get_symdef(symbol);	/* repeat lookup with fontnum=0 */
                   3736:       fontnum = oldfontnum;		/* reset font family */
                   3737:       return symdef; }			/* caller gets fontnum=0 lookup */
1.3     ! albertel 3738: end_of_job:
        !          3739:  if ( msgfp!=NULL && msglevel>=999 )	/* debugging output */
        !          3740:   { fprintf(msgfp,
        !          3741:     "get_symdef> symbol=%s matches symtable[%d]=%s (isligature=%d)\n",
        !          3742:     symbol,bestdef,(bestdef<0?"NotFound":symdefs[bestdef].symbol),isligature);
1.1       albertel 3743:     fflush(msgfp); }
1.3     ! albertel 3744:  return ( (bestdef<0? NULL : &(symdefs[bestdef])) );/*NULL or best symdef[]*/
1.1       albertel 3745: } /* --- end-of-function get_symdef() --- */
                   3746: 
                   3747: 
                   3748: /* ==========================================================================
1.3     ! albertel 3749:  * Function:	get_ligature ( expression, family )
        !          3750:  * Purpose:	returns symtable[] index for ligature
        !          3751:  * --------------------------------------------------------------------------
        !          3752:  * Arguments:	expression (I)	char *  containing ligature
        !          3753:  *				whose corresponding mathchardef is wanted
        !          3754:  *		family (I)	int containing NOVALUE for any family,
        !          3755:  *				or, e.g., CYR10 for cyrillic, etc.
        !          3756:  * --------------------------------------------------------------------------
        !          3757:  * Returns:	( int ) 	symtable[] index defining ligature,
        !          3758:  *				or -9999 if no ligature found or for any error
        !          3759:  * --------------------------------------------------------------------------
        !          3760:  * Notes:     o
        !          3761:  * ======================================================================= */
        !          3762: /* --- entry point --- */
        !          3763: int	get_ligature ( char *expression, int family )
        !          3764: {
        !          3765: /* -------------------------------------------------------------------------
        !          3766: Allocations and Declarations
        !          3767: -------------------------------------------------------------------------- */
        !          3768: mathchardef *symdefs = symtable;	/* table of mathchardefs */
        !          3769: char	*ligature = expression /*- 1*/,	/* expression ptr */
        !          3770: 	*symbol = NULL;			/* symdefs[idef].symbol */
        !          3771: int	liglen = strlen(ligature);	/* #chars remaining in expression */
        !          3772: int	iscyrfam = (family==CYR10);	/* true for cyrillic families */
        !          3773: int	idef = 0,			/* symdefs[] index */
        !          3774: 	bestdef = (-9999),		/*index of longest matching symdef*/
        !          3775: 	maxlen=(-9999);			/*length of longest matching symdef*/
        !          3776: /* -------------------------------------------------------------------------
        !          3777: search symdefs[] in order for first occurrence of symbol
        !          3778: -------------------------------------------------------------------------- */
        !          3779: if ( !isstring ) {			/* no ligatures in "string" mode */
        !          3780:  for ( idef=0; ;idef++ )		/* until trailer record found */
        !          3781:   if ( (symbol=symdefs[idef].symbol) == NULL ) break; /* end-of-table */
        !          3782:   else {				/* check against caller's ligature */
        !          3783:     int symlen = strlen(symbol);	/* #chars in symbol */
        !          3784:     if ( ( symlen>1 || iscyrfam )	/*ligature >1 char long or cyrillic*/
        !          3785:     &&   symlen <= liglen		/* and enough remaining chars */
        !          3786:     &&   ( *symbol!='\\' || iscyrfam )	/* not escaped or cyrillic */
        !          3787:     &&   symdefs[idef].handler == NULL ) /* and not a handler */
        !          3788:      if ( strncmp(ligature,symbol,symlen) == 0 ) /* found match */
        !          3789:       if ( family < 0			/* no family specifies */
        !          3790:       ||   symdefs[idef].family == family ) /* or have correct family */
        !          3791:        if ( symlen > maxlen )		/* new longest ligature */
        !          3792: 	{ bestdef = idef;		/* save index of new best match */
        !          3793: 	  maxlen = symlen; }		/* and save its len for next test */
        !          3794:     } /* --- end-of-if/else(symbol==NULL) --- */
        !          3795:  if ( msgfp!=NULL && msglevel>=999 )	/* debugging output */
        !          3796:   { fprintf(msgfp,"get_ligature> ligature=%.4s matches symtable[%d]=%s\n",
        !          3797:     ligature,bestdef,(bestdef<0?"NotFound":symdefs[bestdef].symbol));
        !          3798:     fflush(msgfp); }
        !          3799:  } /* --- end-of-if(!isstring) --- */
        !          3800: return ( bestdef );			/* -9999 or index of best symdef[] */
        !          3801: } /* --- end-of-function get_ligature --- */
        !          3802: 
        !          3803: 
        !          3804: /* ==========================================================================
1.1       albertel 3805:  * Function:	get_chardef ( symdef, size )
                   3806:  * Purpose:	returns chardef ptr containing data for symdef at given size
                   3807:  * --------------------------------------------------------------------------
                   3808:  * Arguments:	symdef (I)	mathchardef *  corresponding to symbol
                   3809:  *				whose corresponding chardef is wanted
1.2       albertel 3810:  *		size (I)	int containing 0-5 for desired size
1.1       albertel 3811:  * --------------------------------------------------------------------------
                   3812:  * Returns:	( chardef * )	pointer to struct defining symbol at size,
                   3813:  *				or NULL for any error
                   3814:  * --------------------------------------------------------------------------
                   3815:  * Notes:     o	if size unavailable, the next-closer-to-normalsize
                   3816:  *		is returned instead.
                   3817:  * ======================================================================= */
                   3818: /* --- entry point --- */
                   3819: chardef	*get_chardef ( mathchardef *symdef, int size )
                   3820: {
                   3821: /* -------------------------------------------------------------------------
                   3822: Allocations and Declarations
                   3823: -------------------------------------------------------------------------- */
                   3824: fontfamily  *fonts = fonttable;		/* table of font families */
                   3825: chardef	**fontdef,			/*tables for desired font, by size*/
                   3826: 	*gfdata = (chardef *)NULL;	/* chardef for symdef,size */
                   3827: int	ifont;				/* fonts[] index */
                   3828: int	family, charnum;		/* indexes retrieved from symdef */
                   3829: int	sizeinc = 0,			/*+1 or -1 to get closer to normal*/
                   3830: 	normalsize = 2;			/* this size always present */
                   3831: int	isBig = 0;			/*true if symbol's 1st char is upper*/
                   3832: char	*symptr = NULL;			/* look for 1st alpha of symbol */
                   3833: /* -------------------------------------------------------------------------
                   3834: initialization
                   3835: -------------------------------------------------------------------------- */
                   3836: /* --- check symdef --- */
1.3     ! albertel 3837: if ( symdef == NULL ) goto end_of_job;	/* get_symdef() probably failed */
1.1       albertel 3838: /* --- get local copy of indexes from symdef --- */
                   3839: family = symdef->family;		/* font family containing symbol */
                   3840: charnum = symdef->charnum;		/* char# of symbol within font */
                   3841: /* --- check for supersampling --- */
                   3842: if ( issupersampling )			/* check for supersampling fonts */
                   3843:  if ( fonts != ssfonttable )		/* uh oh--probably internal error */
                   3844:   { fonts = ssfonttable; }		/* force it */
                   3845: /* --- check requested size, and set size increment --- */
                   3846: if ( 0 && issupersampling )		/* set size index for supersampling */
                   3847:   size = LARGESTSIZE+1;			/* index 1 past largest size */
                   3848: else					/* low pass indexes 0...LARGESTSIZE */
                   3849:   {
                   3850:   if( size<0 ) size = 0;		/* size was definitely too small */
                   3851:   if( size>LARGESTSIZE ) size = LARGESTSIZE;  /* or definitely too large */
                   3852:   if( size<normalsize ) sizeinc = (+1);	/*use next larger if size too small*/
                   3853:   if( size>normalsize ) sizeinc = (-1);	/*or next smaller if size too large*/
                   3854:   }
                   3855: /* --- check for really big symbol (1st char of symbol name uppercase) --- */
                   3856: for ( symptr=symdef->symbol; *symptr!='\000'; symptr++ ) /*skip leading \'s*/
                   3857:   if ( isalpha(*symptr) )		/* found leading alpha char */
                   3858:     { isBig = isupper(*symptr);		/* is 1st char of name uppercase? */
                   3859:       if ( !isBig			/* 1st char lowercase */
                   3860:       &&   strlen(symptr) >= 4 )	/* but followed by at least 3 chars */
                   3861:        isBig = !memcmp(symptr,"big\\",4) /* isBig if name starts with big\ */
                   3862: 	|| !memcmp(symptr,"bigg",4);	/* or with bigg */
                   3863:       break; }				/* don't check beyond 1st char */
                   3864: /* -------------------------------------------------------------------------
                   3865: find font family in table of fonts[]
                   3866: -------------------------------------------------------------------------- */
                   3867: /* --- look up font family --- */
                   3868: for ( ifont=0; ;ifont++ )		/* until trailer record found */
1.3     ! albertel 3869:   if ( fonts[ifont].family < 0 ) {	/* error, no such family */
        !          3870:     if ( msgfp!=NULL && msglevel>=99 ) { /* emit error */
        !          3871:      fprintf(msgfp,"get_chardef> failed to find font family %d\n",
        !          3872:      family); fflush(msgfp); }
        !          3873:     goto end_of_job; }			/* quit if can't find font family*/
1.1       albertel 3874:   else if ( fonts[ifont].family == family ) break; /* found font family */
                   3875: /* --- get local copy of table for this family by size --- */
                   3876: fontdef = fonts[ifont].fontdef;		/* font by size */
                   3877: /* -------------------------------------------------------------------------
                   3878: get font in desired size, or closest available size, and return symbol
                   3879: -------------------------------------------------------------------------- */
                   3880: /* --- get font in desired size --- */
                   3881: while ( 1 )				/* find size or closest available */
                   3882:   if ( fontdef[size] != NULL ) break;	/* found available size */
                   3883:   else					/* adjust size closer to normal */
                   3884:     if ( size == NORMALSIZE		/* already normal so no more sizes,*/
1.3     ! albertel 3885:     || sizeinc == 0 ) {			/* or must be supersampling */
        !          3886:       if ( msgfp!=NULL && msglevel>=99 ) { /* emit error */
        !          3887: 	fprintf(msgfp,"get_chardef> failed to find font size %d\n",
        !          3888: 	size); fflush(msgfp); }
        !          3889:       goto end_of_job; }		/* quit if can't find desired size */
1.1       albertel 3890:     else				/*bump size 1 closer to NORMALSIZE*/
                   3891:       size += sizeinc;			/* see if adjusted size available */
                   3892: /* --- ptr to chardef struct --- */
                   3893: gfdata = &((fontdef[size])[charnum]);	/*ptr to chardef for symbol in size*/
                   3894: /* -------------------------------------------------------------------------
                   3895: kludge to tweak CMEX10 (which appears to have incorrect descenders)
                   3896: -------------------------------------------------------------------------- */
                   3897: if ( family == CMEX10 )			/* cmex10 needs tweak */
                   3898:   { int height = gfdata->toprow - gfdata->botrow + 1; /*total height of char*/
                   3899:     gfdata->botrow = (isBig? (-height/3) : (-height/4));
                   3900:     gfdata->toprow = gfdata->botrow + gfdata->image.height; }
                   3901: /* -------------------------------------------------------------------------
                   3902: return subraster containing chardef data for symbol in requested size
                   3903: -------------------------------------------------------------------------- */
1.3     ! albertel 3904: end_of_job:
        !          3905:  if ( msgfp!=NULL && msglevel>=999 )
        !          3906:   { if (symdef == NULL) fprintf(msgfp,"get_chardef> input symdef==NULL\n");
        !          3907:     else
        !          3908:      fprintf(msgfp,"get_chardef> requested symbol=\"%s\" size=%d  %s\n",
        !          3909:      symdef->symbol,size,(gfdata==NULL?"FAILED":"Succeeded"));
        !          3910:     fflush(msgfp); }
        !          3911:  return ( gfdata );			/*ptr to chardef for symbol in size*/
1.1       albertel 3912: } /* --- end-of-function get_chardef() --- */
                   3913: 
                   3914: 
                   3915: /* ==========================================================================
                   3916:  * Function:	get_charsubraster ( symdef, size )
                   3917:  * Purpose:	returns new subraster ptr containing
                   3918:  *		data for symdef at given size
                   3919:  * --------------------------------------------------------------------------
1.2       albertel 3920:  * Arguments:	symdef (I)	mathchardef *  corresponding to symbol whose
                   3921:  *				corresponding chardef subraster is wanted
                   3922:  *		size (I)	int containing 0-5 for desired size
1.1       albertel 3923:  * --------------------------------------------------------------------------
                   3924:  * Returns:	( subraster * )	pointer to struct defining symbol at size,
                   3925:  *				or NULL for any error
                   3926:  * --------------------------------------------------------------------------
                   3927:  * Notes:     o	just wraps a subraster envelope around get_chardef()
                   3928:  * ======================================================================= */
                   3929: /* --- entry point --- */
                   3930: subraster *get_charsubraster ( mathchardef *symdef, int size )
                   3931: {
                   3932: /* -------------------------------------------------------------------------
                   3933: Allocations and Declarations
                   3934: -------------------------------------------------------------------------- */
                   3935: chardef	*get_chardef(), *gfdata=NULL;	/* chardef struct for symdef,size */
                   3936: int	get_baseline();			/* baseline of gfdata */
                   3937: subraster *new_subraster(), *sp=NULL;	/* subraster containing gfdata */
1.2       albertel 3938: raster	*bitmaprp=NULL, *gftobitmap();	/* convert .gf-format to bitmap */
                   3939: int	delete_subraster();		/* in case gftobitmap() fails */
1.1       albertel 3940: int	aasupsamp(),			/*antialias char with supersampling*/
                   3941: 	grayscale=256;			/* aasupersamp() parameters */
                   3942: /* -------------------------------------------------------------------------
                   3943: look up chardef for symdef at size, and embed data (gfdata) in subraster
                   3944: -------------------------------------------------------------------------- */
                   3945: if ( (gfdata=get_chardef(symdef,size))	/* look up chardef for symdef,size */
                   3946: !=   NULL )				/* and check that we found it */
                   3947:  if ( (sp=new_subraster(0,0,0))		/* allocate subraster "envelope" */
                   3948:  !=   NULL )				/* and check that we succeeded */
                   3949:   {
1.2       albertel 3950:   raster *image = &(gfdata->image);	/* ptr to chardef's bitmap or .gf */
                   3951:   int format = image->format;		/* 1=bitmap, else .gf */
1.1       albertel 3952:   sp->symdef = symdef;			/* replace NULL with caller's arg */
                   3953:   sp->size = size;			/*replace default with caller's size*/
                   3954:   sp->baseline = get_baseline(gfdata);	/* get baseline of character */
1.2       albertel 3955:   if ( format == 1 )			/* already a bitmap */
                   3956:    { sp->type = CHARASTER;		/* static char raster */
                   3957:      sp->image = image; }		/* store ptr to its bitmap */
                   3958:   else					/* need to convert .gf-to-bitmap */
                   3959:    if ( (bitmaprp = gftobitmap(image))	/* convert */
                   3960:    !=   (raster *)NULL )		/* successful */
                   3961:     { sp->type = IMAGERASTER;		/* allocated raster will be freed */
                   3962:       sp->image = bitmaprp; }		/* store ptr to converted bitmap */
                   3963:    else					/* conversion failed */
                   3964:     { delete_subraster(sp);		/* free unneeded subraster */
                   3965:       sp = (subraster *)NULL;		/* signal error to caller */
                   3966:       goto end_of_job; }		/* quit */
1.1       albertel 3967:   if ( issupersampling )		/* antialias character right here */
                   3968:     {
                   3969:     raster *aa = NULL;			/* antialiased char raster */
                   3970:     int status = aasupsamp(sp->image,&aa,shrinkfactor,grayscale);
                   3971:     if ( status )			/* supersampled successfully */
                   3972:       {	int baseline = sp->baseline;	/* baseline before supersampling */
                   3973: 	int height = gfdata->image.height; /* #rows before supersampling */
                   3974: 	sp->image = aa;			/* replace chardef with ss image */
                   3975: 	if ( baseline >= height-1 )	/* baseline at bottom of char */
                   3976: 	  sp->baseline = aa->height -1;	/* so keep it at bottom */
                   3977: 	else				/* char has descenders */
                   3978: 	  sp->baseline /= shrinkfactor;	/* rescale baseline */
                   3979: 	sp->type = IMAGERASTER; }	/* character is an image raster */
                   3980:     } /* --- end-of-if(issupersampling) --- */
                   3981:   } /* --- end-of-if(sp!=NULL) --- */
1.2       albertel 3982: end_of_job:
                   3983:  if ( msgfp!=NULL && msglevel>=999 )
1.3     ! albertel 3984:   { fprintf(msgfp,"get_charsubraster> requested symbol=\"%s\" baseline=%d"
        !          3985:     " %s %s\n", symdef->symbol, (sp==NULL?0:sp->baseline),
        !          3986:     (sp==NULL?"FAILED":"Succeeded"), (gfdata==NULL?"(gfdata=NULL)":" "));
        !          3987:     fflush(msgfp); }
1.1       albertel 3988: return ( sp );				/* back to caller */
                   3989: } /* --- end-of-function get_charsubraster() --- */
                   3990: 
                   3991: 
                   3992: /* ==========================================================================
1.2       albertel 3993:  * Function:	get_symsubraster ( symbol, size )
                   3994:  * Purpose:	returns new subraster ptr containing
                   3995:  *		data for symbol at given size
                   3996:  * --------------------------------------------------------------------------
                   3997:  * Arguments:	symbol (I)	char *  corresponding to symbol
                   3998:  *				whose corresponding subraster is wanted
                   3999:  *		size (I)	int containing 0-5 for desired size
                   4000:  * --------------------------------------------------------------------------
                   4001:  * Returns:	( subraster * )	pointer to struct defining symbol at size,
                   4002:  *				or NULL for any error
                   4003:  * --------------------------------------------------------------------------
                   4004:  * Notes:     o	just combines get_symdef() and get_charsubraster()
                   4005:  * ======================================================================= */
                   4006: /* --- entry point --- */
                   4007: subraster *get_symsubraster ( char *symbol, int size )
                   4008: {
                   4009: /* -------------------------------------------------------------------------
                   4010: Allocations and Declarations
                   4011: -------------------------------------------------------------------------- */
                   4012: subraster *sp=NULL, *get_charsubraster(); /* subraster containing gfdata */
                   4013: mathchardef *symdef=NULL, *get_symdef(); /* mathchardef lookup for symbol */
                   4014: /* -------------------------------------------------------------------------
                   4015: look up mathchardef for symbol
                   4016: -------------------------------------------------------------------------- */
                   4017: if ( symbol != NULL )			/* user supplied input symbol */
                   4018:   symdef = get_symdef(symbol);		/*look up corresponding mathchardef*/
                   4019: /* -------------------------------------------------------------------------
                   4020: look up chardef for mathchardef and wrap a subraster structure around data
                   4021: -------------------------------------------------------------------------- */
                   4022: if ( symdef != NULL )			/* lookup succeeded */
                   4023:   sp = get_charsubraster(symdef,size);	/* so get symbol data in subraster */
                   4024: return ( sp );				/* back to caller with sp or NULL */
                   4025: } /* --- end-of-function get_symsubraster() --- */
                   4026: 
                   4027: 
                   4028: /* ==========================================================================
1.1       albertel 4029:  * Function:	get_baseline ( gfdata )
                   4030:  * Purpose:	returns baseline for a chardef struct
                   4031:  * --------------------------------------------------------------------------
                   4032:  * Arguments:	gfdata (I)	chardef *  containing chardef for symbol
                   4033:  *				whose baseline is wanted
                   4034:  * --------------------------------------------------------------------------
                   4035:  * Returns:	( int )		baseline for symdef,
                   4036:  *				or -1 for any error
                   4037:  * --------------------------------------------------------------------------
                   4038:  * Notes:     o	Unlike TeX, the top-left corners of our rasters are (0,0),
                   4039:  *		with (row,col) increasing as you move down and right.
                   4040:  *		Baselines are calculated with respect to this scheme,
                   4041:  *		so 0 would mean the very top row is on the baseline
                   4042:  *		and everything else descends below the baseline.
                   4043:  * ======================================================================= */
                   4044: /* --- entry point --- */
                   4045: int	get_baseline ( chardef *gfdata )
                   4046: {
                   4047: /* -------------------------------------------------------------------------
                   4048: Allocations and Declarations
                   4049: -------------------------------------------------------------------------- */
                   4050: int	/*toprow = gfdata->toprow,*/	/*TeX top row from .gf file info*/
                   4051: 	botrow = gfdata->botrow,	/*TeX bottom row from .gf file info*/
                   4052: 	height = gfdata->image.height;	/* #rows comprising symbol */
                   4053: /* -------------------------------------------------------------------------
                   4054: give caller baseline
                   4055: -------------------------------------------------------------------------- */
                   4056: return ( (height-1) + botrow );		/* note: descenders have botrow<0 */
                   4057: } /* --- end-of-function get_baseline() --- */
                   4058: 
                   4059: 
                   4060: /* ==========================================================================
                   4061:  * Function:	get_delim ( char *symbol, int height, int family )
                   4062:  * Purpose:	returns subraster corresponding to the samllest
                   4063:  *		character containing symbol, but at least as large as height,
                   4064:  *		and in caller's family (if specified).
                   4065:  *		If no symbol character as large as height is available,
                   4066:  *		then the largest availabale character is returned instead.
                   4067:  * --------------------------------------------------------------------------
                   4068:  * Arguments:	symbol (I)	char *  containing (substring of) desired
                   4069:  *				symbol, e.g., if symbol="(", then any
                   4070:  *				mathchardef like "(" or "\\(", etc, match.
                   4071:  *		height (I)	int containing minimum acceptable height
                   4072:  *				for returned character
                   4073:  *		family (I)	int containing -1 to consider all families,
                   4074:  *				or, e.g., CMEX10 for only that family
                   4075:  * --------------------------------------------------------------------------
                   4076:  * Returns:	( subraster * )	best matching character available,
                   4077:  *				or NULL for any error
                   4078:  * --------------------------------------------------------------------------
                   4079:  * Notes:     o	If height is passed as negative, its absolute value is used
                   4080:  *		but the best-fit width is searched for (rather than height)
                   4081:  * ======================================================================= */
                   4082: /* --- entry point --- */
                   4083: subraster *get_delim ( char *symbol, int height, int family )
                   4084: {
                   4085: /* -------------------------------------------------------------------------
                   4086: Allocations and Declarations
                   4087: -------------------------------------------------------------------------- */
                   4088: mathchardef *symdefs = symtable;	/* table of mathchardefs */
                   4089: subraster *get_charsubraster(), *sp=(subraster *)NULL; /* best match char */
                   4090: subraster *make_delim();		/* construct delim if can't find it*/
                   4091: chardef	*get_chardef(), *gfdata=NULL;	/* get chardef struct for a symdef */
                   4092: char	lcsymbol[256], *symptr,		/* lowercase symbol for comparison */
                   4093: 	*unescsymbol = symbol;		/* unescaped symbol */
                   4094: int	symlen = (symbol==NULL?0:strlen(symbol)), /* #chars in caller's sym*/
                   4095: 	deflen = 0;			/* length of symdef (aka lcsymbol) */
                   4096: int	idef = 0,			/* symdefs[] index */
                   4097: 	bestdef = (-9999),		/* index of best fit symdef */
                   4098: 	bigdef = (-9999);		/*index of biggest (in case no best)*/
                   4099: int	size = 0,			/* size index 0...LARGESTSIZE */
                   4100: 	bestsize = (-9999),		/* index of best fit size */
                   4101: 	bigsize = (-9999);		/*index of biggest (in case no best)*/
                   4102: int	defheight, bestheight=9999,	/* height of best fit symdef */
                   4103: 	bigheight = (-9999);		/*height of biggest(in case no best)*/
                   4104: int	iswidth = 0;			/* true if best-fit width desired */
                   4105: int	isunesc = 0,			/* true if leading escape removed */
                   4106: 	issq=0, isoint=0;		/* true for \sqcup,etc, \oint,etc */
1.3     ! albertel 4107: int	iscurly = 0;			/* true for StMary's curly symbols */
1.1       albertel 4108: char	*bigint="bigint", *bigoint="bigoint"; /* substitutes for int, oint */
                   4109: /* -------------------------------------------------------------------------
                   4110: determine if searching height or width, and search symdefs[] for best-fit
                   4111: -------------------------------------------------------------------------- */
                   4112: /* --- arg checks --- */
                   4113: if ( symlen < 1 ) return (sp);		/* no input symbol suplied */
                   4114: if ( strcmp(symbol,"e") == 0 ) return(sp); /* e causes segfault??? */
1.3     ! albertel 4115: if ( strstr(symbol,"curly") != NULL ) iscurly=1; /* user wants curly delim */
1.1       albertel 4116: /* --- ignore leading escapes for CMEX10 --- */
                   4117: if ( 1 )				/* ignore leading escape */
                   4118:  if ( (family==CMEX10 || family==CMSYEX) ) { /* for CMEX10 or CMSYEX */
                   4119:   if ( strstr(symbol,"sq") != NULL )	/* \sq symbol requested */
                   4120:      issq = 1;				/* seq \sq signal */
                   4121:   if ( strstr(symbol,"oint") != NULL )	/* \oint symbol requested */
                   4122:      isoint = 1;			/* seq \oint signal */
                   4123:   if ( *symbol=='\\' )			/* have leading \ */
                   4124:    { unescsymbol = symbol+1;		/* push past leading \ */
                   4125:      if ( --symlen < 1 ) return(sp);	/* one less char */
                   4126:      if ( strcmp(unescsymbol,"int") == 0 ) /* \int requested by caller */
                   4127:        unescsymbol = bigint;		/* but big version looks better */
                   4128:      if ( strcmp(unescsymbol,"oint") == 0 ) /* \oint requested by caller */
                   4129:        unescsymbol = bigoint;		/* but big version looks better */
                   4130:      symlen = strlen(unescsymbol);	/* explicitly recalculate length */
                   4131:      isunesc = 1; }			/* signal leading escape removed */
                   4132:   } /* --- end-of-if(family) --- */
                   4133: /* --- determine whether searching for best-fit height or width --- */
                   4134: if ( height < 0 )			/* negative signals width search */
                   4135:   { height = (-height);			/* flip "height" positive */
                   4136:     iswidth = 1; }			/* set flag for width search */
                   4137: /* --- search symdefs[] for best-fit height (or width) --- */
                   4138: for ( idef=0; ;idef++ )			/* until trailer record found */
                   4139:  {
                   4140:  char *defsym = symdefs[idef].symbol;	/* local copies */
                   4141:  int  deffam  = symdefs[idef].family;
                   4142:  if ( defsym == NULL ) break;		/* reached end-of-table */
                   4143:  else					/* check against caller's symbol */
                   4144:   if ( family<0 || deffam == family	/* if explicitly in caller's family*/
1.2       albertel 4145:   ||  (family==CMSYEX && (deffam==CMSY10||deffam==CMEX10||deffam==STMARY10)) )
1.1       albertel 4146:     {
                   4147:     strcpy(lcsymbol,defsym);		/* local copy of symdefs[] symbol */
                   4148:     if ( isunesc && *lcsymbol=='\\' )	/* ignored leading \ in symbol */
                   4149:      strcpy(lcsymbol,lcsymbol+1);	/* so squeeze it out of lcsymbol too*/
                   4150:     if ( 0 )				/* don't ignore case */
                   4151:      for ( symptr=lcsymbol; *symptr!='\000'; symptr++ ) /*for each symbol ch*/
                   4152:       if ( isalpha(*symptr) ) *symptr=tolower(*symptr); /*lowercase the char*/
                   4153:     deflen = strlen(lcsymbol);		/* #chars in symbol we're checking */
                   4154:     if ((symptr=strstr(lcsymbol,unescsymbol)) != NULL) /*found caller's sym*/
                   4155:      if ( (isoint || strstr(lcsymbol,"oint")==NULL) /* skip unwanted "oint"*/
                   4156:      &&   (issq || strstr(lcsymbol,"sq")==NULL) ) /* skip unwanted "sq" */
1.3     ! albertel 4157:       if ( ( deffam == CMSY10 ?		/* CMSY10 or not CMSY10 */
1.1       albertel 4158: 	  symptr == lcsymbol		/* caller's sym is a prefix */
                   4159:           && deflen == symlen:		/* and same length */
1.3     ! albertel 4160: 	  (iscurly || strstr(lcsymbol,"curly")==NULL) &&/*not unwanted curly*/
        !          4161: 	  (symptr == lcsymbol		/* caller's sym is a prefix */
        !          4162:           || symptr == lcsymbol+deflen-symlen) ) ) /* or a suffix */
1.1       albertel 4163:        for ( size=0; size<=LARGESTSIZE; size++ ) /* check all font sizes */
                   4164: 	if ( (gfdata=get_chardef(&(symdefs[idef]),size)) != NULL ) /*got one*/
                   4165: 	  { defheight = gfdata->image.height;	/* height of this character */
                   4166: 	    if ( iswidth )		/* width search wanted instead... */
                   4167: 	      defheight = gfdata->image.width;	/* ...so substitute width */
                   4168: 	    leftsymdef = &(symdefs[idef]);	/* set symbol class, etc */
                   4169: 	    if ( defheight>=height && defheight<bestheight ) /*new best fit*/
                   4170: 	      { bestdef=idef; bestsize=size;	/* save indexes of best fit */
                   4171: 		bestheight = defheight; }	/* and save new best height */
                   4172: 	    if ( defheight >= bigheight )	/* new biggest character */
                   4173: 	      { bigdef=idef; bigsize=size;	/* save indexes of biggest */
                   4174: 		bigheight = defheight; }	/* and save new big height */
                   4175:           } /* --- end-of-if(gfdata!=NULL) --- */
                   4176:     } /* --- end-of-if(family) --- */
                   4177:  } /* --- end-of-for(idef) --- */
                   4178: /* -------------------------------------------------------------------------
                   4179: construct subraster for best fit character, and return it to caller
                   4180: -------------------------------------------------------------------------- */
                   4181: if ( bestdef >= 0 )			/* found a best fit for caller */
                   4182:   sp = get_charsubraster(&(symdefs[bestdef]),bestsize); /* best subraster */
1.2       albertel 4183: if ( (sp==NULL && height-bigheight>5)	/* try to construct delim */
                   4184: ||   bigdef < 0 )			/* delim not in font tables */
1.1       albertel 4185:   sp = make_delim(symbol,(iswidth?-height:height)); /* try to build delim */
                   4186: if ( sp==NULL && bigdef>=0 )		/* just give biggest to caller */
                   4187:   sp = get_charsubraster(&(symdefs[bigdef]),bigsize); /* biggest subraster */
1.2       albertel 4188: if ( msgfp!=NULL && msglevel>=99 )
                   4189:     fprintf(msgfp,"get_delim> symbol=%.50s, height=%d family=%d isokay=%s\n",
                   4190:     (symbol==NULL?"null":symbol),height,family,(sp==NULL?"fail":"success"));
1.1       albertel 4191: return ( sp );
                   4192: } /* --- end-of-function get_delim() --- */
                   4193: 
                   4194: 
                   4195: /* ==========================================================================
                   4196:  * Function:	make_delim ( char *symbol, int height )
                   4197:  * Purpose:	constructs subraster corresponding to symbol
                   4198:  *		exactly as large as height,
                   4199:  * --------------------------------------------------------------------------
                   4200:  * Arguments:	symbol (I)	char *  containing, e.g., if symbol="("
                   4201:  *				for desired delimiter
                   4202:  *		height (I)	int containing height
                   4203:  *				for returned character
                   4204:  * --------------------------------------------------------------------------
                   4205:  * Returns:	( subraster * )	constructed delimiter
                   4206:  *				or NULL for any error
                   4207:  * --------------------------------------------------------------------------
                   4208:  * Notes:     o	If height is passed as negative, its absolute value is used
                   4209:  *		and interpreted as width (rather than height)
                   4210:  * ======================================================================= */
                   4211: /* --- entry point --- */
                   4212: subraster *make_delim ( char *symbol, int height )
                   4213: {
                   4214: /* -------------------------------------------------------------------------
                   4215: Allocations and Declarations
                   4216: -------------------------------------------------------------------------- */
                   4217: subraster *sp = (subraster *)NULL,	/* subraster returned to caller */
                   4218: 	*new_subraster();		/* allocate subraster */
1.2       albertel 4219: subraster *get_symsubraster(),		/* look up delim pieces in cmex10 */
                   4220: 	*symtop=NULL, *symbot=NULL, *symmid=NULL, *symbar=NULL,	/* pieces */
                   4221: 	*topsym=NULL, *botsym=NULL, *midsym=NULL, *barsym=NULL,	/* +filler */
                   4222: 	*rastack(), *rastcat();		/* stack pieces, concat filler */
                   4223: int	isdrawparen = 0;		/*1=draw paren, 0=build from pieces*/
1.1       albertel 4224: raster	*rasp = (raster *)NULL;		/* sp->image */
                   4225: int	isokay=0, delete_subraster();	/* set true if delimiter drawn ok */
1.2       albertel 4226: int	pixsz = 1,			/* pixels are one bit each */
                   4227: 	symsize = 0;			/* size arg for get_symsubraster() */
1.1       albertel 4228: int	thickness = 1;			/* drawn lines are one pixel thick */
                   4229: int	aspectratio = 8;		/* default height/width for parens */
                   4230: int	iswidth = 0,			/*true if width specified by height*/
                   4231: 	width = height;			/* #pixels width (e.g., of ellipse)*/
1.2       albertel 4232: char	*lp=NULL,  *rp=NULL,		/* check symbol for left or right */
                   4233: 	*lp2=NULL, *rp2=NULL,		/* synonym for lp,rp */
                   4234: 	*lp3=NULL, *rp3=NULL,		/* synonym for lp,rp */
                   4235: 	*lp4=NULL, *rp4=NULL;		/* synonym for lp,rp */
1.1       albertel 4236: int	circle_raster(),		/* ellipse for ()'s in sp->image */
                   4237: 	rule_rsater(),			/* horizontal or vertical lines */
                   4238: 	line_raster();			/* line between two points */
1.2       albertel 4239: subraster *uparrow_subraster();		/* up/down arrows */
                   4240: int	isprealloc = 1;			/*pre-alloc subraster, except arrow*/
                   4241: int	oldsmashmargin = smashmargin,	/* save original smashmargin */
1.3     ! albertel 4242: 	wasnocatspace = isnocatspace;	/* save original isnocatspace */
1.1       albertel 4243: /* -------------------------------------------------------------------------
                   4244: initialization
                   4245: -------------------------------------------------------------------------- */
                   4246: /* --- determine whether constructing height or width --- */
                   4247: if ( height < 0 )			/* negative "height" signals width */
                   4248:   { width = height = (-height);		/* flip height positive */
                   4249:     iswidth = 1; }			/* set flag for width */
                   4250: if ( height < 3 ) goto end_of_job;	/* too small, must be error */
                   4251: /* --- set default width (or height) accordingly --- */
                   4252: if ( iswidth ) height =  (width+(aspectratio+1)/2)/aspectratio;
                   4253: else            width = (height+(aspectratio+1)/2)/aspectratio;
                   4254: if ( strchr(symbol,'=') != NULL		/* left or right || bracket wanted */
1.2       albertel 4255: ||   strstr(symbol,"\\|") != NULL	/* same || in standard tex notation*/
                   4256: ||   strstr(symbol,"dbl") != NULL )	/* semantic bracket with ||'s */
                   4257:   width = max2(width,6);		/* need space between two |'s */
1.1       albertel 4258: if ( width < 2 ) width=2;		/* set min width */
                   4259: if ( strchr(symbol,'(') != NULL		/* if left ( */
                   4260: ||   strchr(symbol,')') != NULL )	/* or right ) paren wanted */
1.2       albertel 4261:   { width = (3*width)/2;		/* adjust width */
                   4262:     if ( !isdrawparen ) isprealloc=0; }	/* don't prealloc if building */
                   4263: if ( strchr(symbol,'/') != NULL		/* left / */
                   4264: ||   strstr(symbol,"\\\\") != NULL	/* or \\ for right \ */
                   4265: ||   strstr(symbol,"backsl") != NULL )	/* or \backslash for \ */
                   4266:   width = max2(height/3,5);
                   4267: if ( strstr(symbol,"arrow") != NULL )	/* arrow wanted */
                   4268:   { width = min2(height/3,20);		/* adjust width */
                   4269:     isprealloc = 0; }			/* don't preallocate subraster */
                   4270: if ( strchr(symbol,'{') != NULL		/* if left { */
                   4271: ||   strchr(symbol,'}') != NULL )	/* or right } brace wanted */
                   4272:   { isprealloc = 0; }			/* don't preallocate */
1.1       albertel 4273: /* --- allocate and initialize subraster for constructed delimiter --- */
1.2       albertel 4274: if ( isprealloc )			/* pre-allocation wanted */
                   4275:  { if ( (sp=new_subraster(width,height,pixsz)) /* allocate new subraster */
                   4276:    ==   NULL )  goto end_of_job;	/* quit if failed */
                   4277:    /* --- initialize delimiter subraster parameters --- */
                   4278:    sp->type = IMAGERASTER;		/* image */
                   4279:    sp->symdef = NULL;			/* not applicable for image */
                   4280:    sp->baseline = height/2 + 2;		/* is a little above center good? */
                   4281:    sp->size = NORMALSIZE;		/* size (probably unneeded) */
                   4282:    rasp = sp->image; }			/* pointer to image in subraster */
1.1       albertel 4283: /* -------------------------------------------------------------------------
                   4284: ( ) parens
                   4285: -------------------------------------------------------------------------- */
                   4286: if ( (lp=strchr(symbol,'(')) != NULL	/* left ( paren wanted */
                   4287: ||   (rp=strchr(symbol,')')) != NULL )	/* right ) paren wanted */
                   4288:   {
1.2       albertel 4289:   if ( isdrawparen ) {			/* draw the paren */
                   4290:    int	mywidth = min2(width,20);	/* max width for ()'s */
                   4291:    circle_raster ( rasp,		/* embedded raster image */
1.1       albertel 4292: 	0, 0,				/* row0,col0 are upper-left corner */
                   4293: 	height-1, mywidth-1,		/* row1,col1 are lower-right */
                   4294: 	thickness,			/* line thickness is 1 pixel */
                   4295: 	(rp==NULL?"23":"41") );		/* "1234" quadrants to be drawn */
1.2       albertel 4296:    isokay = 1; }			/* set flag */
                   4297:   else {
                   4298:    int	isleft = (lp!=NULL?1:0);	/* true for left, false for right */
                   4299:    char	*parentop = (isleft?"\\leftparentop":"\\rightparentop"),
                   4300: 	*parenbot = (isleft?"\\leftparenbot":"\\rightparenbot"),
                   4301: 	*parenbar = (isleft?"\\leftparenbar":"\\rightparenbar");
                   4302:    int	baseht=0, barht=0,		/* height of base=top+bot, bar */
                   4303: 	ibar=0, nbars=0;		/* bar index, #bars between top&bot*/
                   4304:    int	largestsize = min2(2,LARGESTSIZE), /* largest size for parens */
                   4305: 	topfill=(isleft?0:0), botfill=(isleft?0:0),
                   4306: 	barfill=(isleft?0:7);		/* alignment fillers */
                   4307:    /* --- get pieces at largest size smaller than total height --- */
                   4308:    for ( symsize=largestsize; symsize>=0; symsize-- ) /*largest to smallest*/
                   4309:     {
                   4310:     /* --- get pieces at current test size --- */
                   4311:     isokay = 1;				/* check for all pieces */
                   4312:     if ( (symtop=get_symsubraster(parentop,symsize)) == NULL ) isokay=0;
                   4313:     if ( (symbot=get_symsubraster(parenbot,symsize)) == NULL ) isokay=0;
                   4314:     if ( (symbar=get_symsubraster(parenbar,symsize)) == NULL ) isokay=0;
                   4315:     /* --- check sum of pieces against total desired height --- */
                   4316:     if ( isokay ) {			/* all pieces retrieved */
                   4317:       baseht = (symtop->image)->height + (symbot->image)->height; /*top+bot*/
                   4318:       barht  = (symbar->image)->height;	/* bar height */
                   4319:       if ( baseht < height+5 ) break;	/* largest base that's not too big */
                   4320:       if ( symsize < 1 ) break;		/* or smallest available base */
                   4321:       } /* --- end-of-if(isokay) --- */
                   4322:     /* --- free test pieces that were too big --- */
                   4323:     if ( symtop != NULL ) delete_subraster(symtop); /* free top */
                   4324:     if ( symbot != NULL ) delete_subraster(symbot); /* free bot */
                   4325:     if ( symbar != NULL ) delete_subraster(symbar); /* free bar */
                   4326:     isokay = 0;				/* nothing available */
                   4327:     if ( symsize < 1 ) break;		/* leave isokay=0 after smallest */
                   4328:     } /* --- end-of-for(symsize) --- */
                   4329:    /* --- construct brace from pieces --- */
                   4330:    if ( isokay ) {			/* we have the pieces */
                   4331:     /* --- add alignment fillers --- */
1.3     ! albertel 4332:     smashmargin=0;  isnocatspace=99;	/*turn off rastcat smashing,space*/
1.2       albertel 4333:     topsym = (topfill>0?rastcat(new_subraster(topfill,1,1),symtop,3):symtop);
                   4334:     botsym = (botfill>0?rastcat(new_subraster(botfill,1,1),symbot,3):symbot);
                   4335:     barsym = (barfill>0?rastcat(new_subraster(barfill,1,1),symbar,3):symbar);
                   4336:     smashmargin = oldsmashmargin;	/* reset smashmargin */
1.3     ! albertel 4337:     isnocatspace = wasnocatspace;	/* reset isnocatspace */
1.2       albertel 4338:     /* --- #bars needed between top and bot --- */
                   4339:     nbars = (barht<1?0:max2(0,1+(height-baseht)/barht)); /* #bars needed */
                   4340:     /* --- stack pieces --- */
                   4341:     sp = topsym;			/* start with top piece */
                   4342:     if ( nbars > 0 )			/* need nbars between top and bot */
                   4343:       for ( ibar=1; ibar<=nbars; ibar++ ) sp = rastack(barsym,sp,1,0,0,2);
                   4344:     sp = rastack(botsym,sp,1,0,0,3);	/* bottom below bars or middle */
                   4345:     delete_subraster(barsym);		/* barsym no longer needed */
                   4346:     } /* --- end-of-if(isokay) --- */
                   4347:    } /* --- end-of-if/else(isdrawparen) --- */
1.1       albertel 4348:   } /* --- end-of-if(left- or right-() paren wanted) --- */
                   4349: /* -------------------------------------------------------------------------
1.2       albertel 4350: { } braces
                   4351: -------------------------------------------------------------------------- */
                   4352: else
                   4353:  if ( (lp=strchr(symbol,'{')) != NULL	/* left { brace wanted */
                   4354:  ||   (rp=strchr(symbol,'}')) != NULL )	/* right } brace wanted */
                   4355:   {
                   4356:   int	isleft = (lp!=NULL?1:0);	/* true for left, false for right */
                   4357:   char	*bracetop = (isleft?"\\leftbracetop":"\\rightbracetop"),
                   4358: 	*bracebot = (isleft?"\\leftbracebot":"\\rightbracebot"),
                   4359: 	*bracemid = (isleft?"\\leftbracemid":"\\rightbracemid"),
                   4360: 	*bracebar = (isleft?"\\leftbracebar":"\\rightbracebar");
                   4361:   int	baseht=0, barht=0,		/* height of base=top+bot+mid, bar */
                   4362: 	ibar=0, nbars=0;		/* bar index, #bars above,below mid*/
                   4363:   int	largestsize = min2(2,LARGESTSIZE), /* largest size for braces */
                   4364: 	topfill=(isleft?4:0), botfill=(isleft?4:0),
                   4365: 	midfill=(isleft?0:4), barfill=(isleft?4:4); /* alignment fillers */
                   4366:   /* --- get pieces at largest size smaller than total height --- */
                   4367:   for ( symsize=largestsize; symsize>=0; symsize-- ) /*largest to smallest*/
                   4368:     {
                   4369:     /* --- get pieces at current test size --- */
                   4370:     isokay = 1;				/* check for all pieces */
                   4371:     if ( (symtop=get_symsubraster(bracetop,symsize)) == NULL ) isokay=0;
                   4372:     if ( (symbot=get_symsubraster(bracebot,symsize)) == NULL ) isokay=0;
                   4373:     if ( (symmid=get_symsubraster(bracemid,symsize)) == NULL ) isokay=0;
                   4374:     if ( (symbar=get_symsubraster(bracebar,symsize)) == NULL ) isokay=0;
                   4375:     /* --- check sum of pieces against total desired height --- */
                   4376:     if ( isokay ) {			/* all pieces retrieved */
                   4377:       baseht = (symtop->image)->height + (symbot->image)->height
                   4378: 	+ (symmid->image)->height;	/* top+bot+mid height */
                   4379:       barht = (symbar->image)->height;	/* bar height */
                   4380:       if ( baseht < height+5 ) break;	/* largest base that's not too big */
                   4381:       if ( symsize < 1 ) break;		/* or smallest available base */
                   4382:       } /* --- end-of-if(isokay) --- */
                   4383:     /* --- free test pieces that were too big --- */
                   4384:     if ( symtop != NULL ) delete_subraster(symtop); /* free top */
                   4385:     if ( symbot != NULL ) delete_subraster(symbot); /* free bot */
                   4386:     if ( symmid != NULL ) delete_subraster(symmid); /* free mid */
                   4387:     if ( symbar != NULL ) delete_subraster(symbar); /* free bar */
                   4388:     isokay = 0;				/* nothing available */
                   4389:     if ( symsize < 1 ) break;		/* leave isokay=0 after smallest */
                   4390:     } /* --- end-of-for(symsize) --- */
                   4391:   /* --- construct brace from pieces --- */
                   4392:   if ( isokay ) {			/* we have the pieces */
                   4393:     /* --- add alignment fillers --- */
1.3     ! albertel 4394:     smashmargin=0;  isnocatspace=99;	/*turn off rastcat smashing,space*/
1.2       albertel 4395:     topsym = (topfill>0?rastcat(new_subraster(topfill,1,1),symtop,3):symtop);
                   4396:     botsym = (botfill>0?rastcat(new_subraster(botfill,1,1),symbot,3):symbot);
                   4397:     midsym = (midfill>0?rastcat(new_subraster(midfill,1,1),symmid,3):symmid);
                   4398:     barsym = (barfill>0?rastcat(new_subraster(barfill,1,1),symbar,3):symbar);
                   4399:     smashmargin = oldsmashmargin;	/* reset smashmargin */
1.3     ! albertel 4400:     isnocatspace = wasnocatspace;	/* reset isnocatspace */
1.2       albertel 4401:     /* --- #bars needed on each side of mid piece --- */
                   4402:     nbars = (barht<1?0:max2(0,1+(height-baseht)/barht/2)); /*#bars per side*/
                   4403:     /* --- stack pieces --- */
                   4404:     sp = topsym;			/* start with top piece */
                   4405:     if ( nbars > 0 )			/* need nbars above middle */
                   4406:       for ( ibar=1; ibar<=nbars; ibar++ ) sp = rastack(barsym,sp,1,0,0,2);
                   4407:     sp = rastack(midsym,sp,1,0,0,3);	/*mid after top or bars*/
                   4408:     if ( nbars > 0 )			/* need nbars below middle */
                   4409:       for ( ibar=1; ibar<=nbars; ibar++ ) sp = rastack(barsym,sp,1,0,0,2);
                   4410:     sp = rastack(botsym,sp,1,0,0,3);	/* bottom below bars or middle */
                   4411:     delete_subraster(barsym);		/* barsym no longer needed */
                   4412:     } /* --- end-of-if(isokay) --- */
                   4413:   } /* --- end-of-if(left- or right-{} brace wanted) --- */
                   4414: /* -------------------------------------------------------------------------
1.1       albertel 4415: [ ] brackets
                   4416: -------------------------------------------------------------------------- */
                   4417: else
                   4418:  if ( (lp=strchr(symbol,'[')) != NULL	/* left [ bracket wanted */
1.2       albertel 4419:  ||   (rp=strchr(symbol,']')) != NULL	/* right ] bracket wanted */
                   4420:  ||   (lp2=strstr(symbol,"lceil")) != NULL /* left ceiling wanted */
                   4421:  ||   (rp2=strstr(symbol,"rceil")) != NULL /* right ceiling wanted */
                   4422:  ||   (lp3=strstr(symbol,"lfloor")) != NULL /* left floor wanted */
                   4423:  ||   (rp3=strstr(symbol,"rfloor")) != NULL /* right floor wanted */
                   4424:  ||   (lp4=strstr(symbol,"llbrack")) != NULL /* left semantic bracket */
                   4425:  ||   (rp4=strstr(symbol,"rrbrack")) != NULL ) /* right semantic bracket */
                   4426:   {
                   4427:   /* --- use rule_raster ( rasp, top, left, width, height, type=0 ) --- */
                   4428:   int	mywidth = min2(width,12),	/* max width for horizontal bars */
                   4429: 	wthick = 1;			/* thickness of top.bottom bars */
                   4430:   thickness = (height<25?1:2);		/* set lines 1 or 2 pixels thick */
                   4431:   if ( lp2!=NULL || rp2!=NULL || lp3!=NULL || rp3 !=NULL ) /*ceil or floor*/
                   4432:     wthick = thickness;			/* same thickness for top/bot bar */
                   4433:   if ( lp3==NULL && rp3==NULL )		/* set top bar if floor not wanted */
                   4434:     rule_raster(rasp, 0,0, mywidth,wthick, 0); /* top horizontal bar */
                   4435:   if ( lp2==NULL && rp2==NULL )		/* set bot bar if ceil not wanted */
                   4436:     rule_raster(rasp, height-wthick,0, mywidth,thickness, 0); /* bottom */
                   4437:   if ( lp!=NULL || lp2!=NULL || lp3!=NULL || lp4!=NULL ) /* left bracket */
1.1       albertel 4438:    rule_raster(rasp, 0,0, thickness,height, 0); /* left vertical bar */
1.2       albertel 4439:   if ( lp4 != NULL )			/* 2nd left vertical bar needed */
                   4440:    rule_raster(rasp, 0,thickness+1, 1,height, 0); /* 2nd left vertical bar */
                   4441:   if ( rp!=NULL || rp2!=NULL || rp3!=NULL || rp4!=NULL ) /* right bracket */
1.1       albertel 4442:    rule_raster(rasp, 0,mywidth-thickness, thickness,height, 0); /* right */
1.2       albertel 4443:   if ( rp4 != NULL )			/* 2nd right vertical bar needed */
                   4444:    rule_raster(rasp, 0,mywidth-thickness-2, 1,height, 0); /*2nd right vert*/
1.1       albertel 4445:   isokay = 1;				/* set flag */
                   4446:   } /* --- end-of-if(left- or right-[] bracket wanted) --- */
                   4447: /* -------------------------------------------------------------------------
                   4448: < > brackets
                   4449: -------------------------------------------------------------------------- */
                   4450: else
                   4451:  if ( (lp=strchr(symbol,'<')) != NULL	/* left < bracket wanted */
                   4452:  ||   (rp=strchr(symbol,'>')) != NULL )	/* right > bracket wanted */
                   4453:   {
1.2       albertel 4454:   /* --- use line_raster( rasp,  row0, col0,  row1, col1,  thickness ) --- */
                   4455:   int	mywidth = min2(width,12),	/* max width for brackets */
                   4456: 	mythick = 1;			/* all lines one pixel thick */
                   4457:   thickness = (height<25?1:2);		/* set line pixel thickness */
1.1       albertel 4458:   if ( lp != NULL )			/* left < bracket wanted */
1.2       albertel 4459:     { line_raster(rasp,height/2,0,0,mywidth-1,mythick);
                   4460:       if ( thickness>1 )
                   4461: 	line_raster(rasp,height/2,1,0,mywidth-1,mythick);
                   4462:       line_raster(rasp,height/2,0,height-1,mywidth-1,mythick);
                   4463:       if ( thickness>1 )
                   4464: 	line_raster(rasp,height/2,1,height-1,mywidth-1,mythick); }
1.1       albertel 4465:   if ( rp != NULL )			/* right > bracket wanted */
1.2       albertel 4466:     { line_raster(rasp,height/2,mywidth-1,0,0,mythick);
                   4467:       if ( thickness>1 )
                   4468: 	line_raster(rasp,height/2,mywidth-2,0,0,mythick);
                   4469:       line_raster(rasp,height/2,mywidth-1,height-1,0,mythick);
                   4470:       if ( thickness>1 )
                   4471: 	line_raster(rasp,height/2,mywidth-2,height-1,0,mythick); }
1.1       albertel 4472:   isokay = 1;				/* set flag */
                   4473:   } /* --- end-of-if(left- or right-<> bracket wanted) --- */
                   4474: /* -------------------------------------------------------------------------
1.2       albertel 4475: / \ delimiters
                   4476: -------------------------------------------------------------------------- */
                   4477: else
                   4478:  if ( (lp=strchr(symbol,'/')) != NULL	/* left /  wanted */
                   4479:  ||   (rp=strstr(symbol,"\\\\")) != NULL /* right \ wanted */
                   4480:  ||   (rp2=strstr(symbol,"backsl")) != NULL ) /* right \ wanted */
                   4481:   {
                   4482:   /* --- use line_raster( rasp,  row0, col0,  row1, col1,  thickness ) --- */
                   4483:   int	mywidth = width;		/* max width for / \ */
                   4484:   thickness = 1;			/* set line pixel thickness */
                   4485:   if ( lp != NULL )			/* left / wanted */
                   4486:     line_raster(rasp,0,mywidth-1,height-1,0,thickness);
                   4487:   if ( rp!=NULL || rp2!=NULL )		/* right \ wanted */
                   4488:     line_raster(rasp,0,0,height-1,mywidth-1,thickness);
                   4489:   isokay = 1;				/* set flag */
                   4490:   } /* --- end-of-if(left- or right-/\ delimiter wanted) --- */
                   4491: /* -------------------------------------------------------------------------
                   4492: arrow delimiters
                   4493: -------------------------------------------------------------------------- */
                   4494: else
                   4495:  if ( strstr(symbol,"arrow") != NULL )	/* arrow delimiter wanted */
                   4496:   {
                   4497:   /* --- use uparrow_subraster(width,height,pixsz,drctn,isBig) --- */
                   4498:   int	mywidth = width;		/* max width for / \ */
                   4499:   int	isBig = (strstr(symbol,"Up")!=NULL /* isBig if we have an Up */
                   4500: 		|| strstr(symbol,"Down")!=NULL); /* or a Down */
                   4501:   int	drctn = +1;			/* init for uparrow */
                   4502:   if ( strstr(symbol,"down")!=NULL	/* down if we have down */
                   4503:   ||   strstr(symbol,"Down")!=NULL )	/* or Down */
                   4504:    { drctn = (-1);			/* reset direction to down */
                   4505:      if ( strstr(symbol,"up")!=NULL	/* updown if we have up or Up */
                   4506:      ||   strstr(symbol,"Up")!=NULL )	/* and down or Down */
                   4507:       drctn = 0; }			/* reset direction to updown */
                   4508:   sp = uparrow_subraster(mywidth,height,pixsz,drctn,isBig);
                   4509:   if ( sp != NULL )
                   4510:    { sp->type = IMAGERASTER;		/* image */
                   4511:      sp->symdef = NULL;			/* not applicable for image */
                   4512:      sp->baseline = height/2 + 2;	/* is a little above center good? */
                   4513:      sp->size = NORMALSIZE;		/* size (probably unneeded) */
                   4514:      isokay = 1; }			/* set flag */
                   4515:   } /* --- end-of-if(arrow delimiter wanted) --- */
                   4516: /* -------------------------------------------------------------------------
1.1       albertel 4517: \- for | | brackets or \= for || || brackets
                   4518: -------------------------------------------------------------------------- */
                   4519: else
                   4520:  if ( (lp=strchr(symbol,'-')) != NULL	/* left or right | bracket wanted */
                   4521:  ||  (lp2=strchr(symbol,'|')) != NULL	/* synonym for | bracket */
                   4522:  ||   (rp=strchr(symbol,'=')) != NULL	/* left or right || bracket wanted */
                   4523:  || (rp2=strstr(symbol,"\\|"))!= NULL )	/* || in standard tex notation */
                   4524:   {
                   4525:   /* --- rule_raster ( rasp, top, left, width, height, type=0 ) --- */
                   4526:   int	midcol = width/2;		/* middle col, left of mid if even */
                   4527:   if ( rp  != NULL			/* left or right || bracket wanted */
                   4528:   ||   rp2 != NULL )			/* or || in standard tex notation */
1.2       albertel 4529:    { thickness = (height<75?1:2);	/* each | of || 1 or 2 pixels thick*/
1.1       albertel 4530:      rule_raster(rasp, 0,max2(0,midcol-2), thickness,height, 0); /* left */
                   4531:      rule_raster(rasp, 0,min2(width,midcol+2), thickness,height, 0); }
                   4532:   else					/*nb, lp2 spuriously set if rp2 set*/
                   4533:    if ( lp  != NULL			/* left or right | bracket wanted */
                   4534:    ||   lp2 != NULL )			/* ditto for synomym */
1.2       albertel 4535:     { thickness = (height<75?1:2);	/* set | 1 or 2 pixels thick */
1.1       albertel 4536:       rule_raster(rasp, 0,midcol, thickness,height, 0); } /*mid vertical bar*/
                   4537:   isokay = 1;				/* set flag */
                   4538:   } /* --- end-of-if(left- or right-[] bracket wanted) --- */
                   4539: /* -------------------------------------------------------------------------
                   4540: back to caller
                   4541: -------------------------------------------------------------------------- */
                   4542: end_of_job:
1.2       albertel 4543:   if ( msgfp!=NULL && msglevel>=99 )
                   4544:     fprintf(msgfp,"make_delim> symbol=%.50s, isokay=%d\n",
                   4545:     (symbol==NULL?"null":symbol),isokay);
1.1       albertel 4546:   if ( !isokay )			/* don't have requested delimiter */
1.2       albertel 4547:     { if (sp!=NULL) delete_subraster(sp); /* so free unneeded structure */
1.1       albertel 4548:       sp = NULL; }			/* and signal error to caller */
                   4549:   return ( sp );			/*back to caller with delim or NULL*/
                   4550: } /* --- end-of-function make_delim() --- */
                   4551: 
                   4552: 
                   4553: /* ==========================================================================
                   4554:  * Function:	texchar ( expression, chartoken )
                   4555:  * Purpose:	scans expression, returning either its first character,
                   4556:  *		or the next \sequence if that first char is \,
                   4557:  *		and a pointer to the first expression char past that.
                   4558:  * --------------------------------------------------------------------------
                   4559:  * Arguments:	expression (I)	char * to first char of null-terminated
                   4560:  *				string containing valid LaTeX expression
                   4561:  *				to be scanned
                   4562:  *		chartoken (O)	char * to null-terminated string returning
                   4563:  *				either the first (non-whitespace) character
                   4564:  *				of expression if that char isn't \, or else
                   4565:  *				the \ and everything following it up to
                   4566:  *				the next non-alphabetic character (but at
                   4567:  *				least one char following the \ even if
                   4568:  *				it's non-alpha)
                   4569:  * --------------------------------------------------------------------------
                   4570:  * Returns:	( char * )	ptr to the first char of expression
                   4571:  *				past returned chartoken,
                   4572:  *				or NULL for any parsing error.
                   4573:  * --------------------------------------------------------------------------
                   4574:  * Notes:     o	Does *not* skip leading whitespace, but simply
                   4575:  *		returns any whitespace character as the next character.
                   4576:  * ======================================================================= */
                   4577: /* --- entry point --- */
                   4578: char	*texchar ( char *expression, char *chartoken )
                   4579: {
                   4580: /* -------------------------------------------------------------------------
                   4581: Allocations and Declarations
                   4582: -------------------------------------------------------------------------- */
                   4583: int	esclen = 0,				/*length of escape sequence*/
                   4584: 	maxesclen = 128;			/* max len of esc sequence */
                   4585: char	*ptoken = chartoken;			/* ptr into chartoken */
                   4586: int	iprefix = 0;				/* prefix index */
                   4587: static	char *prefixes[] =			/*e.g., \big followed by ( */
                   4588: 	{ /* "\\left", "\\right", */
                   4589: 	  "\\big",  "\\Big",  "\\bigg",  "\\Bigg",
                   4590: 	  "\\bigl", "\\Bigl", "\\biggl", "\\Biggl",
                   4591: 	  "\\bigr", "\\Bigr", "\\biggr", "\\Biggr", NULL };
1.3     ! albertel 4592: static	char *starred[] =			/* may be followed by * */
        !          4593: 	{ "\\hspace",  "\\!",  NULL };
1.1       albertel 4594: /* -------------------------------------------------------------------------
                   4595: just return the next char if it's not \
                   4596: -------------------------------------------------------------------------- */
                   4597: /* --- error check for end-of-string --- */
                   4598: *ptoken = '\000';				/* init in case of error */
                   4599: if ( expression == NULL ) return(NULL);		/* nothing to scan */
                   4600: if ( *expression == '\000' ) return(NULL);	/* nothing to scan */
                   4601: /* --- always returning first character (either \ or some other char) --- */
                   4602: *ptoken++ = *expression++;			/* here's first character */
                   4603: /* --- if first char isn't \, then just return it to caller --- */
                   4604: if ( !isthischar(*(expression-1),ESCAPE) )	/* not a \, so return char */
                   4605:   { *ptoken = '\000';				/* add a null terminator */
                   4606:     goto end_of_job; }				/* ptr past returned char */
                   4607: if ( *expression == '\000' )			/* \ is very last char */
                   4608:   { *chartoken = '\000';			/* flush bad trailing \ */
                   4609:     return(NULL); }				/* and signal end-of-job */
                   4610: /* -------------------------------------------------------------------------
                   4611: we have an escape sequence, so return all alpha chars following \
                   4612: -------------------------------------------------------------------------- */
                   4613: /* --- accumulate chars until first non-alpha char found --- */
                   4614: for ( ; isalpha(*expression); esclen++ )	/* till first non-alpha... */
                   4615:   { if ( esclen < maxesclen-3 )			/* more room in chartoken */
                   4616:       *ptoken++ = *expression;			/*copy alpha char, bump ptr*/
                   4617:     expression++; }				/* bump expression ptr */
                   4618: /* --- if we have a prefix, append next texchar, e.g., \big( --- */
                   4619: *ptoken = '\000';				/* set null for compare */
                   4620: for ( iprefix=0; prefixes[iprefix] != NULL; iprefix++ ) /* run thru list */
                   4621:  if ( strcmp(chartoken,prefixes[iprefix]) == 0 ) /* have an exact match */
                   4622:   { char nextchar[256];  int nextlen=0;		/* texchar after prefix */
                   4623:     skipwhite(expression);			/* skip space after prefix*/
                   4624:     expression = texchar(expression,nextchar);	/* get nextchar */
                   4625:     if ( (nextlen = strlen(nextchar)) > 0 )	/* #chars in nextchar */
                   4626:       { strcpy(ptoken,nextchar);		/* append nextchar */
                   4627:         ptoken += strlen(nextchar);		/* point to null terminator*/
                   4628:         esclen += strlen(nextchar); }		/* and bump escape length */
                   4629:     break; }					/* stop checking prefixes */
                   4630: /* --- every \ must be followed by at least one char, e.g., \[ --- */
                   4631: if ( esclen < 1 )				/* \ followed by non-alpha */
                   4632:   *ptoken++ = *expression++;			/*copy non-alpha, bump ptrs*/
1.3     ! albertel 4633: *ptoken = '\000';				/* null-terminate token */
        !          4634: /* --- check for \hspace* or other starred commands --- */
        !          4635: for ( iprefix=0; starred[iprefix] != NULL; iprefix++ ) /* run thru list */
        !          4636:  if ( strcmp(chartoken,starred[iprefix]) == 0 )	/* have an exact match */
        !          4637:   if ( *expression == '*' )			/* follows by a * */
        !          4638:    { *ptoken++ = *expression++;			/* copy * and bump ptr */
        !          4639:      *ptoken = '\000';				/* null-terminate token */
        !          4640:      break; }					/* stop checking */
        !          4641: /* --- respect spaces in text mode, except first space after \escape --- */
        !          4642: if ( esclen >= 1 ) {				/*only for alpha \sequences*/
1.2       albertel 4643:   if ( istextmode )				/* in \rm or \it text mode */
                   4644:    if ( isthischar(*expression,WHITEDELIM) )	/* delim follows \sequence */
                   4645:     expression++; }				/* so flush delim */
1.1       albertel 4646: /* --- back to caller --- */
                   4647: end_of_job:
                   4648:   if ( msgfp!=NULL && msglevel>=999 )
                   4649:     { fprintf(msgfp,"texchar> returning token = \"%s\"\n",chartoken);
                   4650:       fflush(msgfp); }
                   4651:   return ( expression );			/*ptr to 1st non-alpha char*/
                   4652: } /* --- end-of-function texchar() --- */
                   4653: 
                   4654: 
                   4655: /* ==========================================================================
                   4656:  * Function:	texsubexpr (expression,subexpr,maxsubsz,
                   4657:  *		left,right,isescape,isdelim)
                   4658:  * Purpose:	scans expression, returning everything between a balanced
                   4659:  *		left{...right} subexpression if the first non-whitespace
                   4660:  *		char of expression is an (escaped or unescaped) left{,
                   4661:  *		or just the next texchar() otherwise,
                   4662:  *		and a pointer to the first expression char past that.
                   4663:  * --------------------------------------------------------------------------
                   4664:  * Arguments:	expression (I)	char * to first char of null-terminated
                   4665:  *				string containing valid LaTeX expression
                   4666:  *				to be scanned
                   4667:  *		subexpr (O)	char * to null-terminated string returning
                   4668:  *				either everything between a balanced {...}
                   4669:  *				subexpression if the first char is {,
                   4670:  *				or the next texchar() otherwise.
                   4671:  *		maxsubsz (I)	int containing max #bytes returned
                   4672:  *				in subexpr buffer (0 means unlimited)
                   4673:  *		left (I)	char * specifying allowable left delimiters
                   4674:  *				that begin subexpression, e.g., "{[(<"
                   4675:  *		right (I)	char * specifying matching right delimiters
                   4676:  *				in the same order as left, e.g., "}])>"
                   4677:  *		isescape (I)	int controlling whether escaped and/or
                   4678:  *				unescaped left,right are matched;
                   4679:  *				see isbrace() comments below for details.
                   4680:  *		isdelim (I)	int containing true (non-zero) to return
                   4681:  *				the leading left and trailing right delims
                   4682:  *				(if any were found) along with subexpr,
                   4683:  *				or containing false=0 to return subexpr
                   4684:  *				without its delimiters
                   4685:  * --------------------------------------------------------------------------
                   4686:  * Returns:	( char * )	ptr to the first char of expression
                   4687:  *				past returned subexpr (see Notes),
                   4688:  *				or NULL for any parsing error.
                   4689:  * --------------------------------------------------------------------------
                   4690:  * Notes:     o	If subexpr is of the form left{...right},
                   4691:  *		the outer {}'s are returned as part of subexpr
                   4692:  *		if isdelim is true; if isdelim is false the {}'s aren't
                   4693:  *		returned.  In either case the returned pointer is
                   4694:  *		*always* bumped past the closing right}, even if
                   4695:  *		that closing right} isn't returned in subexpr.
                   4696:  *	      o	If subexpr is not of the form left{...right},
                   4697:  *		the returned pointer is on the character immediately
                   4698:  *		following the last character returned in subexpr
                   4699:  *	      o	\. acts as LaTeX \right. and matches any \left(
                   4700:  *		And it also acts as a LaTeX \left. and matches any \right)
                   4701:  * ======================================================================= */
                   4702: /* --- entry point --- */
                   4703: char	*texsubexpr ( char *expression, char *subexpr, int maxsubsz,
                   4704: 	char *left, char *right, int isescape, int isdelim )
                   4705: {
                   4706: /* -------------------------------------------------------------------------
                   4707: Allocations and Declarations
                   4708: -------------------------------------------------------------------------- */
                   4709: char	*texchar();		/*next char (or \sequence) from expression*/
1.2       albertel 4710: char	*leftptr, leftdelim[256] = "(\000", /* left( found in expression */
                   4711: 	rightdelim[256] = ")\000"; /* and matching right) */
1.1       albertel 4712: char	*origexpression=expression, *origsubexpr=subexpr; /*original inputs*/
1.2       albertel 4713: char	*strtexchr(), *texleft(); /* check for \left, and get it */
1.1       albertel 4714: int	gotescape = 0,		/* true if leading char of expression is \ */
                   4715: 	prevescape = 0;		/* while parsing, true if preceding char \ */
                   4716: int	isbrace();		/* check for left,right braces */
                   4717: int	isanyright = 1;		/* true matches any right with left, (...] */
                   4718: int	isleftdot = 0;		/* true if left brace is a \. */
                   4719: int	nestlevel = 1;		/* current # of nested braces */
1.3     ! albertel 4720: int	subsz=0 /*,maxsubsz=MAXSUBXSZ*/; /*#chars in returned subexpr buffer*/
1.1       albertel 4721: /* -------------------------------------------------------------------------
                   4722: skip leading whitespace and just return the next char if it's not {
                   4723: -------------------------------------------------------------------------- */
                   4724: /* --- skip leading whitespace and error check for end-of-string --- */
                   4725: *subexpr = '\000';				/* init in case of error */
                   4726: if ( expression == NULL ) return(NULL);		/*can't dereference null ptr*/
                   4727: skipwhite(expression);				/* leading whitespace gone */
                   4728: if ( *expression == '\000' ) return(NULL);	/* nothing left to scan */
                   4729: /* --- set maxsubsz --- */
1.3     ! albertel 4730: if ( maxsubsz < 1 ) maxsubsz = MAXSUBXSZ-2;	/* input 0 means unlimited */
1.1       albertel 4731: /* --- check for escape --- */
                   4732: if ( isthischar(*expression,ESCAPE) )		/* expression is escaped */
                   4733:   gotescape = 1;				/* so set flag accordingly */
1.2       albertel 4734: /* --- check for \left...\right --- */
                   4735: if ( gotescape )				/* begins with \ */
                   4736:  if ( memcmp(expression+1,"left",4) )		/* and followed by left */
                   4737:   if ( strchr(left,'l') != NULL )		/* caller wants \left's */
                   4738:    if ( strtexchr(expression,"\\left") == expression ) /*expression=\left...*/
                   4739:     { char *pright = texleft(expression,subexpr,maxsubsz, /* find ...\right*/
                   4740: 	(isdelim?NULL:leftdelim),rightdelim);
                   4741:       if ( isdelim ) strcat(subexpr,rightdelim); /* caller wants delims */
                   4742:       return ( pright );			/*back to caller past \right*/
                   4743:     } /* --- end-of-if(expression=="\\left") --- */
1.1       albertel 4744: /* --- if first char isn't left{ or script, just return it to caller --- */
1.3     ! albertel 4745: if ( !isbrace(expression,left,isescape) ) {	/* not a left{ */
1.1       albertel 4746:   if ( !isthischar(*expression,SCRIPTS) )	/* and not a script */
                   4747:     return ( texchar(expression,subexpr) );	/* next char to caller */
                   4748:   else /* --- kludge for super/subscripts to accommodate texscripts() --- */
                   4749:     { *subexpr++ = *expression;			/* signal script */
                   4750:       *subexpr = '\000';			/* null-terminate subexpr */
1.3     ! albertel 4751:       return ( expression ); } }		/* leave script in stream */
1.1       albertel 4752: /* --- extract left and find matching right delimiter --- */
                   4753: *leftdelim  = *(expression+gotescape);		/* the left( in expression */
                   4754: if ( (gotescape && *leftdelim == '.')		/* we have a left \. */
                   4755: ||   (gotescape && isanyright) )		/*or are matching any right*/
                   4756:   { isleftdot = 1;				/* so just set flag */
                   4757:     *leftdelim = '\000'; }			/* and reset leftdelim */
                   4758: else						/* find matching \right */
                   4759:   if ( (leftptr=strchr(left,*leftdelim)) != NULL ) /* ptr to that left( */
                   4760:     *rightdelim = right[(int)(leftptr-left)];	/* get the matching right) */
                   4761:   else						/* can't happen -- pgm bug */
                   4762:     return ( NULL );				/*just signal eoj to caller*/
                   4763: /* -------------------------------------------------------------------------
                   4764: accumulate chars between balanced {}'s, i.e., till nestlevel returns to 0
                   4765: -------------------------------------------------------------------------- */
                   4766: /* --- first initialize by bumping past left{ or \{ --- */
                   4767: if ( isdelim )   *subexpr++ = *expression++;	/*caller wants { in subexpr*/
                   4768:   else expression++;				/* always bump past left{ */
1.3     ! albertel 4769: if ( gotescape ) {				/*need to bump another char*/
1.1       albertel 4770:   if ( isdelim ) *subexpr++ = *expression++;	/* caller wants char, too */
1.3     ! albertel 4771:   else expression++; }				/* else just bump past it */
1.1       albertel 4772: /* --- set maximum size for numerical arguments --- */
                   4773: if ( 0 )					/* check turned on or off? */
                   4774:  if ( !isescape && !isdelim )			/*looking for numerical arg*/
                   4775:   maxsubsz = 96;				/* set max arg size */
                   4776: /* --- search for matching right} --- */
                   4777: while ( 1 )					/*until balanced right} */
                   4778:   {
                   4779:   /* --- error check for end-of-string --- */
                   4780:   if ( *expression == '\000' )			/* premature end-of-string */
                   4781:     { if ( 0 && (!isescape && !isdelim) )	/*looking for numerical arg,*/
                   4782: 	{ expression = origexpression;		/* so end-of-string is error*/
                   4783: 	  subexpr = origsubexpr; }		/* so reset all ptrs */
1.3     ! albertel 4784:       if ( isdelim ) {				/* generate fake right */
1.1       albertel 4785: 	if ( gotescape )			/* need escaped right */
                   4786: 	  { *subexpr++ = '\\';			/* set escape char */
                   4787: 	    *subexpr++ = '.'; }			/* and fake \right. */
                   4788: 	else					/* escape not wanted */
1.3     ! albertel 4789: 	    *subexpr++ = *rightdelim; }		/* so fake actual right */
1.1       albertel 4790:       *subexpr = '\000';			/* null-terminate subexpr */
                   4791:       return ( expression ); }			/* back with final token */
                   4792:   /* --- check preceding char for escape --- */
                   4793:   if ( isthischar(*(expression-1),ESCAPE) )	/* previous char was \ */
                   4794: 	prevescape = 1-prevescape;		/* so flip escape flag */
                   4795:   else	prevescape = 0;				/* or turn flag off */
                   4796:   /* --- check for { and } (un/escaped as per leading left) --- */
                   4797:   if ( gotescape == prevescape )		/* escaped iff leading is */
                   4798:     { /* --- check for (closing) right delim and see if we're done --- */
                   4799:       if ( isthischar(*expression,rightdelim)	/* found a right} */
                   4800:       ||   (isleftdot && isthischar(*expression,right)) /*\left. matches all*/
                   4801:       ||   (prevescape && isthischar(*expression,".")) ) /*or found \right. */
                   4802:         if ( --nestlevel < 1 )			/*\right balances 1st \left*/
                   4803: 	  { if ( isdelim ) 			/*caller wants } in subexpr*/
                   4804: 	      *subexpr++ = *expression;		/* so end subexpr with } */
                   4805: 	    else				/*check for \ before right}*/
                   4806: 	      if ( prevescape )			/* have unwanted \ */
                   4807: 		*(subexpr-1) = '\000';		/* so replace it with null */
                   4808: 	    *subexpr = '\000';			/* null-terminate subexpr */
                   4809: 	    return ( expression+1 ); }		/* back with char after } */
                   4810:       /* --- check for (another) left{ --- */
                   4811:       if ( isthischar(*expression,leftdelim)	/* found another left{ */
                   4812:       ||   (isleftdot && isthischar(*expression,left)) ) /* any left{ */
                   4813: 	nestlevel++;
                   4814:     } /* --- end-of-if(gotescape==prevescape) --- */
                   4815:   /* --- not done, so copy char to subexpr and continue with next char --- */
                   4816:   if ( ++subsz < maxsubsz-5 )			/* more room in subexpr */
                   4817:     *subexpr++ = *expression;			/* so copy char and bump ptr*/
                   4818:   expression++;					/* bump expression ptr */
                   4819:   } /* --- end-of-while(1) --- */
                   4820: } /* --- end-of-function texsubexpr() --- */
                   4821: 
                   4822: 
                   4823: /* ==========================================================================
1.2       albertel 4824:  * Function:	texleft (expression,subexpr,maxsubsz,ldelim,rdelim)
                   4825:  * Purpose:	scans expression, starting after opening \left,
                   4826:  *		and returning ptr after matching closing \right.
                   4827:  *		Everything between is returned in subexpr, if given.
                   4828:  *		Likewise, if given, ldelim returns delimiter after \left
                   4829:  *		and rdelim returns delimiter after \right.
                   4830:  *		If ldelim is given, the returned subexpr doesn't include it.
                   4831:  *		If rdelim is given, the returned pointer is after that delim.
                   4832:  * --------------------------------------------------------------------------
                   4833:  * Arguments:	expression (I)	char * to first char of null-terminated
                   4834:  *				string immediately following opening \left
                   4835:  *		subexpr (O)	char * to null-terminated string returning
                   4836:  *				either everything between balanced
                   4837:  *				\left ... \right.  If leftdelim given,
                   4838:  *				subexpr does _not_ contain that delimiter.
                   4839:  *		maxsubsz (I)	int containing max #bytes returned
                   4840:  *				in subexpr buffer (0 means unlimited)
                   4841:  *		ldelim (O)	char * returning delimiter following
                   4842:  *				opening \left
                   4843:  *		rdelim (O)	char * returning delimiter following
                   4844:  *				closing \right
                   4845:  * --------------------------------------------------------------------------
                   4846:  * Returns:	( char * )	ptr to the first char of expression
                   4847:  *				past closing \right, or past closing
                   4848:  *				right delimiter if rdelim!=NULL,
                   4849:  *				or NULL for any error.
                   4850:  * --------------------------------------------------------------------------
                   4851:  * Notes:     o
                   4852:  * ======================================================================= */
                   4853: /* --- entry point --- */
                   4854: char	*texleft ( char *expression, char *subexpr, int maxsubsz,
                   4855: 	char *ldelim, char *rdelim )
                   4856: {
                   4857: /* -------------------------------------------------------------------------
                   4858: Allocations and Declarations
                   4859: -------------------------------------------------------------------------- */
                   4860: char	*texchar(),			/* get delims after \left,\right */
                   4861: 	*strtexchr(), *pright=expression; /* locate matching \right */
                   4862: static	char left[16]="\\left", right[16]="\\right"; /* tex delimiters */
                   4863: int	sublen = 0;			/* #chars between \left...\right */
                   4864: /* -------------------------------------------------------------------------
                   4865: initialization
                   4866: -------------------------------------------------------------------------- */
                   4867: /* --- init output --- */
                   4868: if ( subexpr != NULL ) *subexpr = '\000'; /* init subexpr, if given */
                   4869: if ( ldelim  != NULL ) *ldelim  = '\000'; /* init ldelim,  if given */
                   4870: if ( rdelim  != NULL ) *rdelim  = '\000'; /* init rdelim,  if given */
                   4871: /* --- check args --- */
                   4872: if ( expression == NULL ) goto end_of_job; /* no input supplied */
                   4873: if ( *expression == '\000' ) goto end_of_job; /* nothing after \left */
                   4874: /* --- determine left delimiter  --- */
                   4875: if ( ldelim != NULL )			/* caller wants left delim */
                   4876:  { skipwhite(expression);		/* interpret \left ( as \left( */
                   4877:    expression = texchar(expression,ldelim); } /*delim from expression*/
                   4878: /* -------------------------------------------------------------------------
                   4879: locate \right balancing opening \left
                   4880: -------------------------------------------------------------------------- */
                   4881: /* --- first \right following \left --- */
                   4882: if ( (pright=strtexchr(expression,right)) /* look for \right after \left */
                   4883: !=   NULL ) {				/* found it */
                   4884:  /* --- find matching \right by pushing past any nested \left's --- */
                   4885:  char *pleft = expression;		/* start after first \left( */
                   4886:  while ( 1 ) {				/*break when matching \right found*/
                   4887:   /* -- locate next nested \left if there is one --- */
                   4888:   if ( (pleft=strtexchr(pleft,left))	/* find next \left */
                   4889:   ==   NULL ) break;			/*no more, so matching \right found*/
                   4890:   pleft += strlen(left);		/* push ptr past \left token */
                   4891:   if ( pleft >= pright ) break;		/* not nested if \left after \right*/
                   4892:   /* --- have nested \left, so push forward to next \right --- */
                   4893:   if ( (pright=strtexchr(pright+strlen(right),right)) /* find next \right */
                   4894:   ==   NULL ) break;			/* ran out of \right's */
                   4895:   } /* --- end-of-while(1) --- */
                   4896:  } /* --- end-of-if(pright!=NULL) --- */
                   4897: /* --- set subexpression length, push pright past \right --- */
                   4898: if ( pright != (char *)NULL )		/* found matching \right */
                   4899:  { sublen = (int)(pright-expression);	/* #chars between \left...\right */
                   4900:    pright += strlen(right); }		/* so push pright past \right */
                   4901: /* -------------------------------------------------------------------------
                   4902: get rightdelim and subexpr between \left...\right
                   4903: -------------------------------------------------------------------------- */
                   4904: /* --- get delimiter following \right --- */
1.3     ! albertel 4905: if ( rdelim != NULL ) {			/* caller wants right delim */
1.2       albertel 4906:  if ( pright == (char *)NULL )		/* assume \right. at end of exprssn*/
                   4907:   { strcpy(rdelim,".");			/* set default \right. */
                   4908:     sublen = strlen(expression);	/* use entire remaining expression */
                   4909:     pright = expression + sublen; }	/* and push pright to end-of-string*/
                   4910:  else					/* have explicit matching \right */
                   4911:   { skipwhite(pright);			/* interpret \right ) as \right) */
                   4912:     pright = texchar(pright,rdelim);	/* pull delim from expression */
1.3     ! albertel 4913:     if ( *rdelim == '\000' ) strcpy(rdelim,"."); } } /* or set \right. */
1.2       albertel 4914: /* --- get subexpression between \left...\right --- */
                   4915: if ( sublen > 0 )			/* have subexpr */
                   4916:  if ( subexpr != NULL ) {		/* and caller wants it */
                   4917:   if ( maxsubsz > 0 ) sublen = min2(sublen,maxsubsz-1); /* max buffer size */
                   4918:   memcpy(subexpr,expression,sublen);	/* stuff between \left...\right */
                   4919:   subexpr[sublen] = '\000'; }		/* null-terminate subexpr */
                   4920: end_of_job:
                   4921:   if ( msglevel>=99 && msgfp!=NULL )
                   4922:     { fprintf(msgfp,"texleft> ldelim=%s, rdelim=%s, subexpr=%.128s\n",
                   4923:       (ldelim==NULL?"none":ldelim),(rdelim==NULL?"none":rdelim),
                   4924:       (subexpr==NULL?"none":subexpr)); fflush(msgfp); }
                   4925:   return ( pright );
                   4926: } /* --- end-of-function texleft --- */
                   4927: 
                   4928: 
                   4929: /* ==========================================================================
1.1       albertel 4930:  * Function:	texscripts ( expression, subscript, superscript, which )
                   4931:  * Purpose:	scans expression, returning subscript and/or superscript
                   4932:  *		if expression is of the form _x^y or ^{x}_{y},
                   4933:  *		or any (valid LaTeX) permutation of the above,
                   4934:  *		and a pointer to the first expression char past "scripts"
                   4935:  * --------------------------------------------------------------------------
                   4936:  * Arguments:	expression (I)	char * to first char of null-terminated
                   4937:  *				string containing valid LaTeX expression
                   4938:  *				to be scanned
                   4939:  *		subscript (O)	char * to null-terminated string returning
                   4940:  *				subscript (without _), if found, or "\000"
                   4941:  *		superscript (O)	char * to null-terminated string returning
                   4942:  *				superscript (without ^), if found, or "\000"
                   4943:  *		which (I)	int containing 1 for subscript only,
                   4944:  *				2 for superscript only, >=3 for either/both
                   4945:  * --------------------------------------------------------------------------
                   4946:  * Returns:	( char * )	ptr to the first char of expression
                   4947:  *				past returned "scripts" (unchanged
                   4948:  *				except for skipped whitespace if
                   4949:  *				neither subscript nor superscript found),
                   4950:  *				or NULL for any parsing error.
                   4951:  * --------------------------------------------------------------------------
                   4952:  * Notes:     o	an input expression like ^a^b_c will return superscript="b",
                   4953:  *		i.e., totally ignoring all but the last "script" encountered
                   4954:  * ======================================================================= */
                   4955: /* --- entry point --- */
                   4956: char	*texscripts ( char *expression, char *subscript,
                   4957: 			char *superscript, int which )
                   4958: {
                   4959: /* -------------------------------------------------------------------------
                   4960: Allocations and Declarations
                   4961: -------------------------------------------------------------------------- */
                   4962: char	*texsubexpr();		/* next subexpression from expression */
                   4963: int	gotsub=0, gotsup=0;	/* check that we don't eat, e.g., x_1_2 */
                   4964: /* -------------------------------------------------------------------------
1.2       albertel 4965: init "scripts"
1.1       albertel 4966: -------------------------------------------------------------------------- */
1.2       albertel 4967: if ( subscript != NULL ) *subscript = '\000';	/*init in case no subscript*/
                   4968: if ( superscript!=NULL ) *superscript = '\000';	/*init in case no super*/
1.1       albertel 4969: /* -------------------------------------------------------------------------
                   4970: get subscript and/or superscript from expression
                   4971: -------------------------------------------------------------------------- */
1.2       albertel 4972: while ( expression != NULL ) {
                   4973:   skipwhite(expression);			/* leading whitespace gone */
                   4974:   if ( *expression == '\000' ) return(expression); /* nothing left to scan */
1.1       albertel 4975:   if ( isthischar(*expression,SUBSCRIPT)	/* found _ */
                   4976:   &&   (which==1 || which>2 ) )			/* and caller wants it */
                   4977:     { if ( gotsub				/* found 2nd subscript */
                   4978:       ||   subscript == NULL ) break;		/* or no subscript buffer */
                   4979:       gotsub = 1;				/* set subscript flag */
                   4980:       expression = texsubexpr(expression+1,subscript,0,"{","}",0,0); }
                   4981:   else						/* no _, check for ^ */
                   4982:     if ( isthischar(*expression,SUPERSCRIPT)	/* found ^ */
                   4983:     &&   which>=2  )				/* and caller wants it */
                   4984:       {	if ( gotsup				/* found 2nd superscript */
                   4985: 	||   superscript == NULL ) break;	/* or no superscript buffer*/
                   4986: 	gotsup = 1;				/* set superscript flag */
                   4987: 	expression = texsubexpr(expression+1,superscript,0,"{","}",0,0); }
                   4988:     else					/* neither _ nor ^ */
                   4989:       return ( expression );			/*return ptr past "scripts"*/
1.2       albertel 4990:   } /* --- end-of-while(expression!=NULL) --- */
1.1       albertel 4991: return ( expression );
                   4992: } /* --- end-of-function texscripts() --- */
                   4993: 
                   4994: 
                   4995: /* ==========================================================================
                   4996:  * Function:	isbrace ( expression, braces, isescape )
                   4997:  * Purpose:	checks leading char(s) of expression for a brace,
                   4998:  *		either escaped or unescaped depending on isescape,
                   4999:  *		except that { and } are always matched, if they're
                   5000:  *		in braces, regardless of isescape.
                   5001:  * --------------------------------------------------------------------------
                   5002:  * Arguments:	expression (I)	char * to first char of null-terminated
                   5003:  *				string containing a valid LaTeX expression
                   5004:  *				whose leading char(s) are checked for braces
                   5005:  *				that begin subexpression, e.g., "{[(<"
                   5006:  *		braces (I)	char * specifying matching brace delimiters
                   5007:  *				to be checked for, e.g., "{[(<" or "}])>"
                   5008:  *		isescape (I)	int containing 0 to match only unescaped
                   5009:  *				braces, e.g., (...) or {...}, etc,
                   5010:  *				or containing 1 to match only escaped
                   5011:  *				braces, e.g., \(...\) or \[...\], etc,
                   5012:  *				or containing 2 to match either.
                   5013:  *				But note: if {,} are in braces
                   5014:  *				then they're *always* matched whether
                   5015:  *				escaped or not, regardless of isescape.
                   5016:  * --------------------------------------------------------------------------
                   5017:  * Returns:	( int )		1 if the leading char(s) of expression
                   5018:  *				is a brace, or 0 if not.
                   5019:  * --------------------------------------------------------------------------
                   5020:  * Notes:     o
                   5021:  * ======================================================================= */
                   5022: /* --- entry point --- */
                   5023: int	isbrace ( char *expression, char *braces, int isescape )
                   5024: {
                   5025: /* -------------------------------------------------------------------------
                   5026: Allocations and Declarations
                   5027: -------------------------------------------------------------------------- */
                   5028: int	gotescape = 0,		/* true if leading char is an escape */
                   5029: 	gotbrace = 0;		/*true if first non-escape char is a brace*/
                   5030: /* -------------------------------------------------------------------------
                   5031: check for brace
                   5032: -------------------------------------------------------------------------- */
1.3     ! albertel 5033: /* --- first check for end-of-string or \= ligature --- */
        !          5034: if ( *expression == '\000'			/* nothing to check */
        !          5035: ||   isligature ) goto end_of_job;		/* have a \= ligature */
1.1       albertel 5036: /* --- check leading char for escape --- */
                   5037: if ( isthischar(*expression,ESCAPE) )		/* expression is escaped */
                   5038:   { gotescape = 1;				/* so set flag accordingly */
                   5039:     expression++; }				/* and bump past escape */
                   5040: /* --- check (maybe next char) for brace --- */
                   5041: if ( isthischar(*expression,braces) )		/* expression is braced */
                   5042:   gotbrace = 1;					/* so set flag accordingly */
                   5043: if ( gotescape && *expression == '.' )		/* \. matches any brace */
                   5044:   gotbrace = 1;					/* set flag */
                   5045: /* --- check for TeX brace { or } --- */
                   5046: if ( gotbrace && isthischar(*expression,"{}") )	/*expression has TeX brace*/
                   5047:   if ( isescape ) isescape = 2;			/* reset escape flag */
                   5048: /* -------------------------------------------------------------------------
                   5049: back to caller
                   5050: -------------------------------------------------------------------------- */
1.3     ! albertel 5051: end_of_job:
        !          5052:  if ( msglevel>=999 && msgfp!=NULL )
        !          5053:   { fprintf(msgfp,"isbrace> expression=%.8s, gotbrace=%d (isligature=%d)\n",
        !          5054:     expression,gotbrace,isligature); fflush(msgfp); }
        !          5055:  if ( gotbrace &&				/* found a brace */
1.1       albertel 5056:      ( isescape==2 ||				/* escape irrelevant */
                   5057:        gotescape==isescape )			/* un/escaped as requested */
                   5058:    ) return ( 1 );  return ( 0 );		/* return 1,0 accordingly */
                   5059: } /* --- end-of-function isbrace() --- */
                   5060: 
                   5061: 
                   5062: /* ==========================================================================
                   5063:  * Function:	preamble ( expression, size, subexpr )
                   5064:  * Purpose:	parses $-terminated preamble, if present, at beginning
                   5065:  *		of expression, re-setting size if necessary, and
                   5066:  *		returning any other parameters besides size in subexpr.
                   5067:  * --------------------------------------------------------------------------
                   5068:  * Arguments:	expression (I)	char * to first char of null-terminated
                   5069:  *				string containing LaTeX expression possibly
                   5070:  *				preceded by $-terminated preamble
                   5071:  *		size (I/O)	int *  containing 0-4 default font size,
                   5072:  *				and returning size modified by first
                   5073:  *				preamble parameter (or unchanged)
                   5074:  *		subexpr(O)	char *  returning any remaining preamble
                   5075:  *				parameters past size
                   5076:  * --------------------------------------------------------------------------
                   5077:  * Returns:	( char * )	ptr to first char past preamble in expression
                   5078:  *				or NULL for any parsing error.
                   5079:  * --------------------------------------------------------------------------
                   5080:  * Notes:     o	size can be any number >=0. If preceded by + or -, it's
                   5081:  *		interpreted as an increment to input size; otherwise
                   5082:  *		it's interpreted as the size.
                   5083:  *	      o	if subexpr is passed as NULL ptr, then returned expression
                   5084:  *		ptr will have "flushed" and preamble parameters after size
                   5085:  * ======================================================================= */
                   5086: /* --- entry point --- */
                   5087: char	*preamble ( char *expression, int *size, char *subexpr )
                   5088: {
                   5089: /* -------------------------------------------------------------------------
                   5090: Allocations and Declarations
                   5091: -------------------------------------------------------------------------- */
                   5092: char	pretext[512], *prep=expression,	/*pream from expression, ptr into it*/
                   5093: 	*dollar, *comma;		/* preamble delimiters */
                   5094: int	prelen = 0,			/* preamble length */
                   5095: 	sizevalue = 0,			/* value of size parameter */
                   5096: 	isfontsize = 0,			/*true if leading fontsize present*/
                   5097: 	isdelta = 0;			/*true to increment passed size arg*/
                   5098: /* -------------------------------------------------------------------------
                   5099: initialization
                   5100: -------------------------------------------------------------------------- */
                   5101: if ( subexpr != NULL )			/* caller passed us an address */
                   5102:   *subexpr = '\000';			/* so init assuming no preamble */
                   5103: if ( expression == NULL ) goto end_of_job; /* no input */
                   5104: if ( *expression == '\000' ) goto end_of_job; /* input is an empty string */
                   5105: /* -------------------------------------------------------------------------
                   5106: process preamble if present
                   5107: -------------------------------------------------------------------------- */
                   5108: /*process_preamble:*/
                   5109: if ( (dollar=strchr(expression,'$'))	/* $ signals preceding preamble */
1.3     ! albertel 5110: !=   NULL ) {				/* found embedded $ */
1.1       albertel 5111:  if ( (prelen = (int)(dollar-expression)) /*#chars in expression preceding $*/
                   5112:  > 0 ) {				/* must have preamble preceding $ */
                   5113:   if ( prelen < 65 ) {			/* too long for a prefix */
                   5114:    memcpy(pretext,expression,prelen);	/* local copy of preamble */
                   5115:    pretext[prelen] = '\000';		/* null-terminated */
                   5116:    if ( strchr(pretext,*(ESCAPE))==NULL	/*shouldn't be an escape in preamble*/
                   5117:    &&   strchr(pretext,'{') == NULL ) {	/*shouldn't be a left{ in preamble*/
                   5118:     /* --- skip any leading whitespace  --- */
                   5119:     prep = pretext;			/* start at beginning of preamble */
                   5120:     skipwhite(prep);			/* skip any leading white space */
                   5121:     /* --- check for embedded , or leading +/- (either signalling size) --- */
                   5122:     if ( isthischar(*prep,"+-") )	/* have leading + or - */
                   5123:      isdelta = 1;			/* so use size value as increment */
                   5124:     comma = strchr(pretext,',');	/* , signals leading size param */
                   5125:     /* --- process leading size parameter if present --- */
                   5126:     if ( comma != NULL			/* size param explicitly signalled */
                   5127:     ||   isdelta || isdigit(*prep) ) {	/* or inferred implicitly */
                   5128:       /* --- parse size parameter and reset size accordingly --- */
                   5129:       if( comma != NULL ) *comma = '\000';/*, becomes null, terminating size*/
                   5130:       sizevalue = atoi(prep);		/* convert size string to integer */
                   5131:       if ( size != NULL )		/* caller passed address for size */
                   5132: 	*size = (isdelta? *size+sizevalue : sizevalue); /* so reset size */
                   5133:       /* --- finally, set flag and shift size parameter out of preamble --- */
                   5134:       isfontsize = 1;			/*set flag showing font size present*/
                   5135:       if ( comma != NULL ) strcpy(pretext,comma+1);/*leading size param gone*/
                   5136:      } /* --- end-of-if(comma!=NULL||etc) --- */
                   5137:     /* --- copy any preamble params following size to caller's subexpr --- */
                   5138:     if ( comma != NULL || !isfontsize )	/*preamb contains params past size*/
                   5139:      if ( subexpr != NULL )		/* caller passed us an address */
                   5140:       strcpy(subexpr,pretext);		/*so return extra params to caller*/
                   5141:     /* --- finally, set prep to shift preamble out of expression --- */
                   5142:     prep = expression + prelen+1;	/* set prep past $ in expression */
                   5143:     } /* --- end-of-if(strchr(pretext,*ESCAPE)==NULL) --- */
                   5144:    } /* --- end-of-if(prelen<65) --- */
                   5145:   } /* --- end-of-if(prelen>0) --- */
                   5146:  else {					/* $ is first char of expression */
                   5147:   int ndollars = 0;			/* number of $...$ pairs removed */
                   5148:   prep = expression;			/* start at beginning of expression*/
                   5149:   while ( *prep == '$' ) {		/* remove all matching $...$'s */
                   5150:    int	explen = strlen(prep)-1;	/* index of last char in expression*/
                   5151:    if ( explen < 2 ) break;		/* no $...$'s left to remove */
                   5152:    if ( prep[explen] != '$' ) break;	/* unmatched $ */
                   5153:    prep[explen] = '\000';		/* remove trailing $ */
                   5154:    prep++;				/* and remove matching leading $ */
                   5155:    ndollars++;				/* count another pair removed */
                   5156:    } /* --- end-of-while(*prep=='$') --- */
                   5157:   ispreambledollars = ndollars;		/* set flag to fix \displaystyle */
                   5158:   if ( ndollars == 1 )			/* user submitted $...$ expression */
                   5159:     isdisplaystyle = 0;			/* so set \textstyle */
                   5160:   if ( ndollars > 1 )			/* user submitted $$...$$ */
                   5161:     isdisplaystyle = 2;			/* so set \displaystyle */
                   5162:   /*goto process_preamble;*/		/*check for preamble after leading $*/
                   5163:   } /* --- end-of-if/else(prelen>0) --- */
1.3     ! albertel 5164:  } /* --- end-of-if(dollar!=NULL) --- */
1.1       albertel 5165: /* -------------------------------------------------------------------------
                   5166: back to caller
                   5167: -------------------------------------------------------------------------- */
                   5168: end_of_job:
                   5169:   return ( prep );			/*expression, or ptr past preamble*/
                   5170: } /* --- end-of-function preamble() --- */
                   5171: 
                   5172: 
                   5173: /* ==========================================================================
                   5174:  * Function:	mimeprep ( expression )
                   5175:  * Purpose:	preprocessor for mimeTeX input, e.g.,
                   5176:  *		(a) removes comments,
                   5177:  *		(b) converts \left( to \( and \right) to \),
                   5178:  *		(c) xlates &html; special chars to equivalent latex
                   5179:  *		Should only be called once (after unescape_url())
                   5180:  * --------------------------------------------------------------------------
                   5181:  * Arguments:	expression (I/O) char * to first char of null-terminated
                   5182:  *				string containing mimeTeX/LaTeX expression,
                   5183:  *				and returning preprocessed string
                   5184:  * --------------------------------------------------------------------------
                   5185:  * Returns:	( char * )	ptr to input expression,
                   5186:  *				or NULL for any parsing error.
                   5187:  * --------------------------------------------------------------------------
                   5188:  * Notes:     o
                   5189:  * ======================================================================= */
                   5190: /* --- entry point --- */
                   5191: char	*mimeprep ( char *expression )
                   5192: {
                   5193: /* -------------------------------------------------------------------------
                   5194: Allocations and Declarations
                   5195: -------------------------------------------------------------------------- */
                   5196: char	*expptr=expression,		/* ptr within expression */
                   5197: 	*tokptr=NULL,			/*ptr to token found in expression*/
                   5198: 	*texsubexpr(), argval[8192];	/*parse for macro args after token*/
                   5199: char	*strchange();			/* change leading chars of string */
1.3     ! albertel 5200: char	*strwstr();			/*use strwstr() instead of strstr()*/
1.1       albertel 5201: char	*findbraces();			/*find left { and right } for \atop*/
                   5202: int	idelim=0,			/* left- or right-index */
                   5203: 	isymbol=0;			/*symbols[],rightcomment[],etc index*/
                   5204: int	xlateleft = 0;			/* true to xlate \left and \right */
                   5205: /* ---
                   5206:  * comments
                   5207:  * -------- */
                   5208: char	*leftptr=NULL;			/* find leftcomment in expression */
                   5209: static	char *leftcomment = "%%",	/* open comment */
                   5210: 	*rightcomment[] = {"\n", "%%", NULL}; /* close comments */
                   5211: /* ---
                   5212:  * special long (more than 1-char) \left and \right delimiters
                   5213:  * ----------------------------------------------------------- */
                   5214: static	char *leftfrom[] =		/* xlate any \left suffix... */
                   5215:    { "\\|",				/* \left\| */
                   5216:      "\\{",				/* \left\{ */
                   5217:      "\\langle",			/* \left\langle */
                   5218:      NULL } ; /* --- end-of-leftfrom[] --- */
                   5219: static	char *leftto[] =		/* ...to this instead */
                   5220:    { "=",				/* = */
                   5221:      "{",				/* { */
                   5222:      "<",				/* < */
                   5223:      NULL } ; /* --- end-of-leftto[] --- */
                   5224: static	char *rightfrom[] =		/* xlate any \right suffix... */
                   5225:    { "\\|",				/* \right\| */
                   5226:      "\\}",				/* \right\} */
                   5227:      "\\rangle",			/* \right\rangle */
                   5228:      NULL } ; /* --- end-of-rightfrom[] --- */
                   5229: static	char *rightto[] =		/* ...to this instead */
                   5230:    { "=",				/* = */
                   5231:      "}",				/* } */
                   5232:      ">",				/* > */
                   5233:      NULL } ; /* --- end-of-rightto[] --- */
                   5234: /* ---
                   5235:  * { \atop }-like commands
                   5236:  * ----------------------- */
                   5237: char	*atopsym=NULL;			/* atopcommands[isymbol] */
                   5238: static	char *atopcommands[] =		/* list of {a+b\command c+d}'s */
                   5239:    { "\\over",				/* plain tex for \frac */
                   5240:      "\\choose",			/* binomial coefficient */
                   5241:    #ifndef NOATOP			/*noatop preserves old mimeTeX rule*/
                   5242:      "\\atop",
                   5243:    #endif
                   5244:      NULL } ; /* --- end-of-atopcommands[] --- */
                   5245: static	char *atopdelims[] =		/* delims for atopcommands[] */
                   5246:    { NULL, NULL,			/* \\over has no delims */
                   5247:      "\\left(", "\\right)",		/* \\choose has ( ) delims*/
                   5248:    #ifndef NOATOP			/*noatop preserves old mimeTeX rule*/
                   5249:      NULL, NULL,			/* \\atop has no delims */
                   5250:    #endif
                   5251:      NULL, NULL } ; /* --- end-of-atopdelims[] --- */
                   5252: /* ---
                   5253:  * html special/escape chars converted to latex equivalents
                   5254:  * -------------------------------------------------------- */
                   5255: char	*htmlsym=NULL;			/* symbols[isymbol].html */
                   5256: static	struct { char *html; char *args; char *latex; } symbols[] =
                   5257:  { /* ---------------------------------------
                   5258:      user-supplied newcommands
                   5259:    --------------------------------------- */
                   5260:  #ifdef NEWCOMMANDS			/* -DNEWCOMMANDS=\"filename.h\" */
                   5261:    #include NEWCOMMANDS
                   5262:  #endif
1.3     ! albertel 5263:    /* ---------------------------------------
        !          5264:      Cyrillic termchar  mimeTeX equivalent...
        !          5265:    --------------------------------------- */
        !          5266:    { "\\\'G",	"embed\\","{\\acute{G}}" },
        !          5267:    { "\\\'g",	"embed\\","{\\acute{g}}" },
        !          5268:    { "\\\'K",	"embed\\","{\\acute{K}}" },
        !          5269:    { "\\\'k",	"embed\\","{\\acute{k}}" },
        !          5270:    { "\\u U",	"embed\\","{\\breve{U}}" },
        !          5271:    { "\\u u",	"embed\\","{\\breve{u}}" },
        !          5272:    /*{ "\\\"E",	"embed\\","{\\ddot{E}}" },*/
        !          5273:    /*{ "\\\"e",	"embed\\","{\\ddot{e}}" },*/
        !          5274:    { "\\\"I",	"embed\\","{\\ddot{\\=I}}" },
        !          5275:    { "\\\"\\i",	"embed\\","{\\ddot{\\=\\i}}" },
1.1       albertel 5276:    /* ------------------------------------------
                   5277:    LaTeX Macro  #args,default   template...
                   5278:    ------------------------------------------ */
                   5279:    { "\\lvec",	"2n",	"{#2_1,\\cdots,#2_{#1}}" },
1.2       albertel 5280:    { "\\grave", "1",	"{\\stackrel{\\Huge\\gravesym}{#1}}" }, /* \grave */
                   5281:    { "\\acute", "1",	"{\\stackrel{\\Huge\\acutesym}{#1}}" }, /* \acute */
                   5282:    { "\\check", "1",	"{\\stackrel{\\Huge\\checksym}{#1}}" }, /* \check */
                   5283:    { "\\breve", "1",	"{\\stackrel{\\Huge\\brevesym}{#1}}" }, /* \breve */
1.3     ! albertel 5284:    { "\\buildrel","3",	"{\\stackrel{#1}{#3}}" }, /* ignore #2 = \over */
1.1       albertel 5285:    { "\\overset", NULL,	"\\stackrel" },		/* just an alias */
                   5286:    { "\\underset", "2",	"\\relstack{#2}{#1}" },	/* reverse args */
                   5287:    /* ---------------------------------------
1.2       albertel 5288:     html char termchar  LaTeX equivalent...
1.1       albertel 5289:    --------------------------------------- */
                   5290:    { "&quot",	";",	"\"" },		/* &quot; is first, &#034; */
                   5291:    { "&amp",	";",	"&" },
                   5292:    { "&lt",	";",	"<" },
                   5293:    { "&gt",	";",	">" },
1.3     ! albertel 5294:    { "&#092",	";",	"\\" },		/* backslash */
        !          5295:    { "&backslash",";",	"\\" },
1.1       albertel 5296:    { "&nbsp",	";",	"~" },
                   5297:    { "&iexcl",	";",	"{\\raisebox{-2}{\\rotatebox{180}{!}}}" },
                   5298:    { "&brvbar",	";",	"|" },
                   5299:    { "&plusmn",	";",	"\\pm" },
                   5300:    { "&sup2",	";",	"{{}^2}" },
                   5301:    { "&sup3",	";",	"{{}^3}" },
                   5302:    { "&micro",	";",	"\\mu" },
                   5303:    { "&sup1",	";",	"{{}^1}" },
                   5304:    { "&frac14",	";",	"{\\frac14}" },
                   5305:    { "&frac12",	";",	"{\\frac12}" },
                   5306:    { "&frac34",	";",	"{\\frac34}" },
                   5307:    { "&iquest",	";",	"{\\raisebox{-2}{\\rotatebox{180}{?}}}" },
                   5308:    { "&Acirc",	";",	"{\\rm~\\hat~A}" },
                   5309:    { "&Atilde",	";",	"{\\rm~\\tilde~A}" },
                   5310:    { "&Auml",	";",	"{\\rm~\\ddot~A}" },
                   5311:    { "&Aring",	";",	"{\\rm~A\\limits^{-1$o}}" },
                   5312:    { "&atilde",	";",	"{\\rm~\\tilde~a}" },
                   5313:    { "&yuml",	";",	"{\\rm~\\ddot~y}" },  /* &yuml; is last, &#255; */
                   5314:    /* ---------------------------------------
1.2       albertel 5315:     html tag  termchar  LaTeX equivalent...
                   5316:    --------------------------------------- */
1.3     ! albertel 5317:    { "<br>",	"embed\\i","\\\\" },
        !          5318:    { "<br/>",	"embed\\i","\\\\" },
        !          5319:    /* ---------------------------------------
        !          5320:     garbage  termchar  LaTeX equivalent...
        !          5321:    --------------------------------------- */
        !          5322:    { "< TEX >",	"embed\\i","\000" },
        !          5323:    { "< / TEX >","embed\\i","\000" },
        !          5324:    { "<br / >",	"embed\\i","\000" },
1.2       albertel 5325:    /* ---------------------------------------
1.1       albertel 5326:      LaTeX   termchar   mimeTeX equivalent...
                   5327:    --------------------------------------- */
                   5328:    { "\\AA",	NULL,	"{\\rm~A\\limits^{-1$o}}" },
                   5329:    { "\\aa",	NULL,	"{\\rm~a\\limits^{-1$o}}" },
                   5330:    { "\\bmod",	NULL,	"{\\hspace2{\\rm~mod}\\hspace2}" },
                   5331:    { "\\vdots",	NULL,	"{\\raisebox3{\\rotatebox{90}{\\ldots}}}" },
1.2       albertel 5332:    { "\\dots",	NULL,	"{\\cdots}" },
1.1       albertel 5333:    { "\\cdots",	NULL,	"{\\raisebox3{\\ldots}}" },
                   5334:    { "\\ldots",	NULL,	"{\\fs4.\\hspace1.\\hspace1.}" },
                   5335:    { "\\ddots",	NULL,	"{\\fs4\\raisebox8.\\hspace1\\raisebox4.\\hspace1.}"},
                   5336:    { "\\notin",	NULL,	"{\\not\\in}" },
                   5337:    { "\\neq",	NULL,	"{\\not=}" },
1.2       albertel 5338:    { "\\ne",	NULL,	"{\\not=}" },
1.1       albertel 5339:    { "\\hbar",	NULL,	"{\\compose~h{{\\fs{-1}-\\atop\\vspace3}}}" },
                   5340:    { "\\angle",	NULL, "{\\compose{\\hspace{3}\\lt}{\\circle(10,15;-80,80)}}"},
1.2       albertel 5341:    { "\\textcelsius", NULL, "{\\textdegree C}"},
                   5342:    { "\\textdegree", NULL, "{\\Large^{^{\\tiny\\mathbf o}}}"},
1.1       albertel 5343:    { "\\cr",	NULL,	"\\\\" },
1.2       albertel 5344:    { "\\iiint",	NULL,	"{\\int\\int\\int}\\limits" },
1.1       albertel 5345:    { "\\iint",	NULL,	"{\\int\\int}\\limits" },
                   5346:    { "\\Bigiint", NULL,	"{\\Bigint\\Bigint}\\limits" },
1.2       albertel 5347:    { "\\bigsqcap",NULL,	"{\\fs{+4}\\sqcap}" },
1.1       albertel 5348:    { "!`",	NULL,	"{\\raisebox{-2}{\\rotatebox{180}{!}}}" },
                   5349:    { "?`",	NULL,	"{\\raisebox{-2}{\\rotatebox{180}{?}}}" },
1.2       albertel 5350:    { "^\'",	"embed","\'" }, /* avoid ^^ when re-xlating \' below */
                   5351:    { "\'\'\'\'","embed","^{\\fs{-1}\\prime\\prime\\prime\\prime}" },
                   5352:    { "\'\'\'",	"embed","^{\\fs{-1}\\prime\\prime\\prime}" },
                   5353:    { "\'\'",	"embed","^{\\fs{-1}\\prime\\prime}" },
                   5354:    { "\'",	"embed","^{\\fs{-1}\\prime}" },
1.1       albertel 5355:    { "\\rightleftharpoons",NULL,"{\\rightharpoonup\\atop\\leftharpoondown}" },
1.2       albertel 5356:    { "\\therefore",NULL,"{\\Huge\\raisebox{-4}{.\\atop.\\,.}}" },
1.1       albertel 5357:    { "\\LaTeX",	NULL,	"{\\rm~L\\raisebox{3}{\\fs{-1}A}\\TeX}" },
                   5358:    { "\\TeX",	NULL,	"{\\rm~T\\raisebox{-3}{E}X}" },
                   5359:    { "\\cyan",	NULL,	"{\\reverse\\red\\reversebg}" },
                   5360:    { "\\magenta",NULL,	"{\\reverse\\green\\reversebg}" },
                   5361:    { "\\yellow",NULL,	"{\\reverse\\blue\\reversebg}" },
1.2       albertel 5362:    { "\\cancel",NULL,	"\\Not" },
1.1       albertel 5363:    { "\\hhline",NULL,	"\\Hline" },
                   5364:    { "\\Hline", NULL,	"\\hline\\,\\\\\\hline" },
                   5365:    /* ---------------------------------------------------------
                   5366:      "Algebra Syntax"  termchar   mimeTeX/LaTeX equivalent...
                   5367:    ------------------------------------------------------------ */
                   5368:    { "sqrt",	"1",	"{\\sqrt{#1}}" },
                   5369:    { "sin",	"1",	"{\\sin{#1}}" },
                   5370:    { "cos",	"1",	"{\\cos{#1}}" },
                   5371:    { "asin",	"1",	"{\\sin^{-1}{#1}}" },
                   5372:    { "acos",	"1",	"{\\cos^{-1}{#1}}" },
1.2       albertel 5373:    { "exp",	"1",	"{{\\rm~e}^{#1}}" },
1.1       albertel 5374:    { "det",	"1",	"{\\left|{#1}\\right|}" },
                   5375:    /* ---------------------------------------
                   5376:    LaTeX Constant    termchar   value...
                   5377:    --------------------------------------- */
                   5378:    { "\\thinspace",	NULL,	"2" },
                   5379:    { "\\thinmathspace",	NULL,	"2" },
                   5380:    { "\\textwidth",	NULL,	"400" },
                   5381:    { NULL,	NULL,	NULL }
                   5382:  } ; /* --- end-of-symbols[] --- */
                   5383: /* -------------------------------------------------------------------------
                   5384: first remove comments
                   5385: -------------------------------------------------------------------------- */
                   5386: expptr = expression;			/* start search at beginning */
                   5387: while ( (leftptr=strstr(expptr,leftcomment)) != NULL ) /*found leftcomment*/
                   5388:   {
                   5389:   char	*rightsym=NULL;			/* rightcomment[isymbol] */
                   5390:   expptr = leftptr+strlen(leftcomment);	/* start rightcomment search here */
                   5391:   /* --- check for any closing rightcomment, in given precedent order --- */
                   5392:   if ( *expptr != '\000' )		/*have chars after this leftcomment*/
                   5393:    for(isymbol=0; (rightsym=rightcomment[isymbol]) != NULL; isymbol++)
                   5394:     if ( (tokptr=strstr(expptr,rightsym)) != NULL ) /*found rightcomment*/
                   5395:      { tokptr += strlen(rightsym);	/* first char after rightcomment */
                   5396:        if ( *tokptr == '\000' )		/*nothing after this rightcomment*/
                   5397: 	{ *leftptr = '\000';		/*so terminate expr at leftcomment*/
                   5398: 	  break; }			/* and stop looking for comments */
                   5399:        *leftptr = '~';			/* replace entire comment by ~ */
                   5400:        strcpy(leftptr+1,tokptr);	/* and squeeze out comment */
                   5401:        goto next_comment; }		/* stop looking for rightcomment */
                   5402:   /* --- no rightcomment after opening leftcomment --- */
                   5403:   *leftptr = '\000';			/* so terminate expression */
                   5404:   /* --- resume search past squeezed-out comment --- */
                   5405:   next_comment:
                   5406:     if ( *leftptr == '\000' ) break;	/* reached end of expression */
                   5407:     expptr = leftptr+1;			/*resume search after this comment*/
                   5408:   } /* --- end-of-while(leftptr!=NULL) --- */
                   5409: /* -------------------------------------------------------------------------
                   5410: run thru table, converting all occurrences of each macro to its expansion
                   5411: -------------------------------------------------------------------------- */
                   5412: for(isymbol=0; (htmlsym=symbols[isymbol].html) != NULL; isymbol++)
                   5413:   {
                   5414:   int	htmllen = strlen(htmlsym);	/* length of escape, _without_ ; */
                   5415:   int	isalgebra = isalpha((int)(*htmlsym)); /* leading char alphabetic */
1.3     ! albertel 5416:   int	isembedded = 0,			/* true to xlate even if embedded */
        !          5417: 	isstrwstr = 0,			/* true to use strwstr() */
        !          5418: 	wstrlen = 0;			/* length of strwstr() match */
1.1       albertel 5419:   char	*aleft="{([<|", *aright="})]>|"; /*left,right delims for alg syntax*/
1.3     ! albertel 5420:   char	embedkeywd[99] = "embed",	/* keyword to signal embedded token*/
        !          5421: 	embedterm = '\000';		/* char immediately after embed */
        !          5422:   int	embedlen = strlen(embedkeywd);	/* #chars in embedkeywd */
1.1       albertel 5423:   char	*args = symbols[isymbol].args,	/* number {}-args, optional []-arg */
                   5424: 	*htmlterm = args,		/*if *args nonumeric, then html term*/
                   5425: 	*latexsym = symbols[isymbol].latex; /*latex replacement for htmlsym*/
                   5426:   char	abuff[8192];  int iarg,nargs=0;	/* macro expansion params */
1.3     ! albertel 5427:   char	wstrwhite[99];			/* whitespace chars for strwstr() */
1.1       albertel 5428:   if ( args != NULL )			/*we have args (or htmlterm) param*/
1.3     ! albertel 5429:    if ( *args != '\000' ) {		/* and it's not an empty string */
1.1       albertel 5430:     if ( strchr("0123456789",*args) != NULL ) /* is 1st char #args=0-9 ? */
                   5431:      { htmlterm = NULL;			/* if so, then we have no htmlterm */
                   5432:        *abuff = *args;  abuff[1] = '\000'; /* #args char in ascii buffer */
                   5433:        nargs = atoi(abuff); }		/* interpret #args to numeric */
1.3     ! albertel 5434:     else if ( strncmp(args,embedkeywd,embedlen) == 0 )/*xlate embedded token*/
1.2       albertel 5435:      { htmlterm = NULL;			/* if so, then we have no htmlterm */
1.3     ! albertel 5436:        isembedded = 1 ;			/* turn on embedded flag */
        !          5437:        embedterm = args[embedlen];	/* char immediately after embed */
        !          5438:        if (strlen(args) > embedlen+1) {	/* have embed,white for strwstr() */
        !          5439: 	isstrwstr = 1;			/* turn on strwtsr flag */
        !          5440: 	strcpy(wstrwhite,args+6); } }	/* and set its whitespace arg */
        !          5441:     } /* --- end-of-if(*args!='\000') --- */
1.1       albertel 5442:   expptr = expression;			/* re-start search at beginning */
1.3     ! albertel 5443:   while ( ( tokptr=(!isstrwstr?strstr(expptr,htmlsym): /* just use strtsr */
        !          5444:   strwstr(expptr,htmlsym,wstrwhite,&wstrlen)) ) /* or use our strwstr */
        !          5445:   != NULL )				/* found another sym */
        !          5446:     { int  toklen = (!isstrwstr?htmllen:wstrlen); /* length of matched sym */
        !          5447:       char termchar = *(tokptr+toklen),	/* char terminating html sequence */
1.2       albertel 5448:            prevchar = (tokptr==expptr?' ':*(tokptr-1)); /*char preceding html*/
1.3     ! albertel 5449:       int  escapelen = toklen;		/* total length of escape sequence */
1.1       albertel 5450:       *abuff = '\000';			/* default to empty string */
                   5451:       if ( latexsym != NULL )		/* table has .latex xlation */
                   5452:        if ( *latexsym != '\000' )	/* and it's not an empty string */
                   5453: 	strcpy(abuff,latexsym);		/* so get local copy */
                   5454:       if ( htmlterm != NULL )		/* sequence may have terminator */
                   5455: 	escapelen += (isthischar(termchar,htmlterm)?1:0); /*add terminator*/
1.2       albertel 5456:       if ( !isembedded )		/* don't xlate embedded sequence */
                   5457:        if ( isalpha((int)termchar) )	/*we just have prefix of longer sym*/
1.3     ! albertel 5458: 	{ expptr = tokptr+toklen;	/* just resume search after prefix */
1.1       albertel 5459: 	  continue; }			/* but don't replace it */
1.2       albertel 5460:       if ( isembedded )			/* for embedded sequence */
1.3     ! albertel 5461: 	if ( !isthischar(embedterm,ESCAPE)  /* don't xlate escaped \token */
        !          5462: 	&&    isthischar(prevchar,ESCAPE) ) /* and we have escaped \token */
        !          5463: 	  { expptr = tokptr+toklen;	/*just resume search after literal*/
1.2       albertel 5464: 	    continue; }			/* but don't replace it */
1.1       albertel 5465:       if ( !isthischar(*htmlsym,ESCAPE)	/* our symbol isn't escaped */
1.2       albertel 5466:       &&   isalpha(*htmlsym)		/* and our symbol starts with alpha*/
1.1       albertel 5467:       &&   !isthischar(*htmlsym,"&") )	/* and not an &html; special char */
                   5468:        if ( tokptr != expression )	/* then if we're past beginning */
                   5469: 	if ( isthischar(*(tokptr-1),ESCAPE) /*and if inline symbol escaped*/
                   5470: 	||   (isalpha(*(tokptr-1))) )	/* or if suffix of longer string */
                   5471: 	  { expptr = tokptr+escapelen;	/*just resume search after literal*/
                   5472: 	    continue; }			/* but don't replace it */
                   5473:       if ( nargs > 0 )			/*substitute #1,#2,... in latexsym*/
                   5474:        {
                   5475:        char *arg1ptr = tokptr+escapelen;/* nargs begin after macro literal */
                   5476:        char *optarg = args+1;		/* ptr 1 char past #args digit 0-9 */
                   5477:        expptr = arg1ptr;		/* ptr to beginning of next arg */
                   5478:        for ( iarg=1; iarg<=nargs; iarg++ ) /* one #`iarg` arg at a time */
                   5479: 	{
                   5480: 	char argsignal[32] = "#1",	/* #1...#9 signals arg replacement */
                   5481: 	*argsigptr = NULL;		/* ptr to argsignal in abuff[] */
                   5482: 	/* --- get argument value --- */
                   5483: 	*argval = '\000';		/* init arg as empty string */
                   5484: 	skipwhite(expptr);		/* and skip leading white space */
                   5485: 	if ( iarg==1 && *optarg!='\000'	/* check for optional [arg] */
                   5486: 	&&   !isalgebra )		/* but not in "algebra syntax" */
                   5487: 	 { strcpy(argval,optarg);	/* init with default value */
                   5488: 	   if ( *expptr == '[' )	/* but user gave us [argval] */
                   5489: 	    expptr = texsubexpr(expptr,argval,0,"[","]",0,0); } /*so get it*/
                   5490: 	else				/* not optional, so get {argval} */
1.3     ! albertel 5491: 	 if ( *expptr != '\000' ) {	/* check that some argval provided */
1.1       albertel 5492: 	  if ( !isalgebra )		/* only { } delims for latex macro */
                   5493: 	    expptr = texsubexpr(expptr,argval,0,"{","}",0,0); /*get {argval}*/
                   5494: 	  else				/*any delim for algebra syntax macro*/
                   5495: 	   { expptr = texsubexpr(expptr,argval,0,aleft,aright,0,1);
                   5496: 	     if ( isthischar(*argval,aleft) ) /* have delim-enclosed arg */
                   5497: 	      if ( *argval != '{' )	/* and it's not { }-enclosed */
                   5498: 	       { strchange(0,argval,"\\left"); /* insert opening \left, */
1.3     ! albertel 5499: 		 strchange(0,argval+strlen(argval)-1,"\\right"); } }/*\right*/
        !          5500: 	   } /* --- end-of-if(*expptr!='\000') --- */
1.1       albertel 5501: 	/* --- replace #`iarg` in macro with argval --- */
                   5502: 	sprintf(argsignal,"#%d",iarg);	/* #1...#9 signals argument */
                   5503: 	while ( (argsigptr=strstr(argval,argsignal)) != NULL ) /* #1...#9 */
                   5504: 	 strcpy(argsigptr,argsigptr+strlen(argsignal)); /*can't be in argval*/
                   5505: 	while ( (argsigptr=strstr(abuff,argsignal)) != NULL ) /* #1...#9 */
                   5506: 	 strchange(strlen(argsignal),argsigptr,argval); /*replaced by argval*/
                   5507: 	} /* --- end-of-for(iarg) --- */
                   5508:        escapelen += ((int)(expptr-arg1ptr)); /* add in length of all args */
                   5509:        } /* --- end-of-if(nargs>0) --- */
                   5510:       strchange(escapelen,tokptr,abuff); /*replace macro or html symbol*/
                   5511:       expptr = tokptr + strlen(abuff); /*resume search after macro / html*/
                   5512:     } /* --- end-of-while(tokptr!=NULL) --- */
                   5513:   } /* --- end-of-for(isymbol) --- */
                   5514: /* -------------------------------------------------------------------------
1.3     ! albertel 5515: convert \left( to \(  and  \right) to \),  etc.
        !          5516: -------------------------------------------------------------------------- */
        !          5517: if ( xlateleft )			/* \left...\right xlation wanted */
        !          5518:  for ( idelim=0; idelim<2; idelim++ )	/* 0 for \left  and  1 for \right */
        !          5519:   {
        !          5520:   char	*lrstr  = (idelim==0?"\\left":"\\right"); /* \left on 1st pass */
        !          5521:   int	lrlen   = (idelim==0?5:6);	/* strlen() of \left or \right */
        !          5522:   char	*braces = (idelim==0?LEFTBRACES ".":RIGHTBRACES "."), /*([{<or)]}>*/
        !          5523: 	**lrfrom= (idelim==0?leftfrom:rightfrom), /* long braces like \| */
        !          5524: 	**lrto  = (idelim==0?leftto:rightto), /* xlated to 1-char like = */
        !          5525: 	*lrsym  = NULL;			/* lrfrom[isymbol] */
        !          5526:   expptr = expression;			/* start search at beginning */
        !          5527:   while ( (tokptr=strstr(expptr,lrstr)) != NULL ) /* found \left or \right */
        !          5528:     {
        !          5529:     if ( isthischar(*(tokptr+lrlen),braces) ) /* followed by a 1-char brace*/
        !          5530:       {	strcpy(tokptr+1,tokptr+lrlen);	/* so squeeze out "left" or "right"*/
        !          5531: 	expptr = tokptr+2; }		/* and resume search past brace */
        !          5532:     else				/* may be a "long" brace like \| */
        !          5533:       {
        !          5534:       expptr = tokptr+lrlen;		/*init to resume search past\left\rt*/
        !          5535:       for(isymbol=0; (lrsym=lrfrom[isymbol]) != NULL; isymbol++)
        !          5536: 	{ int symlen = strlen(lrsym);	/* #chars in delim, e.g., 2 for \| */
        !          5537: 	  if ( memcmp(tokptr+lrlen,lrsym,symlen) == 0 ) /* found long delim*/
        !          5538: 	    { strcpy(tokptr+1,tokptr+lrlen+symlen-1); /* squeeze out delim */
        !          5539: 	      *(tokptr+1) = *(lrto[isymbol]); /* last char now 1-char delim*/
        !          5540: 	      expptr = tokptr+2 - lrlen; /* resume search past 1-char delim*/
        !          5541: 	      break; }			/* no need to check more lrsym's */
        !          5542: 	} /* --- end-of-for(isymbol) --- */
        !          5543:       } /* --- end-of-if/else(isthischar()) --- */
        !          5544:     } /* --- end-of-while(tokptr!=NULL) --- */
        !          5545:   } /* --- end-of-for(idelim) --- */
        !          5546: /* -------------------------------------------------------------------------
1.1       albertel 5547: run thru table, converting all {a+b\atop c+d} to \atop{a+b}{c+d}
                   5548: -------------------------------------------------------------------------- */
                   5549: for(isymbol=0; (atopsym=atopcommands[isymbol]) != NULL; isymbol++)
                   5550:   {
                   5551:   int	atoplen = strlen(atopsym);	/* #chars in \atop */
                   5552:   expptr = expression;			/* re-start search at beginning */
                   5553:   while ( (tokptr=strstr(expptr,atopsym)) != NULL ) /* found another atop */
                   5554:     { char *leftbrace=NULL, *rightbrace=NULL; /*ptr to opening {, closing }*/
                   5555:       char termchar = *(tokptr+atoplen); /* \atop followed by terminator */
                   5556:       if ( msgfp!=NULL && msglevel>=999 )
                   5557: 	{ fprintf(msgfp,"mimeprep> offset=%d rhs=\"%s\"\n",
                   5558: 	  (int)(tokptr-expression),tokptr);
                   5559: 	  fflush(msgfp); }
                   5560:       if ( isalpha((int)termchar) )	/*we just have prefix of longer sym*/
                   5561: 	{ expptr = tokptr+atoplen;	/* just resume search after prefix */
                   5562: 	  continue; }			/* but don't process it */
                   5563:       leftbrace  = findbraces(expression,tokptr);     /* find left { */
                   5564:       rightbrace = findbraces(NULL,tokptr+atoplen-1); /* find right } */
                   5565:       if ( leftbrace==NULL || rightbrace==NULL )
                   5566: 	{ expptr += atoplen;  continue; } /* skip command if didn't find */
                   5567:       else				/* we have bracketed { \atop } */
                   5568: 	{
                   5569: 	int  leftlen  = (int)(tokptr-leftbrace) - 1, /* #chars in left arg */
                   5570: 	     rightlen = (int)(rightbrace-tokptr) - atoplen, /* and in right*/
                   5571: 	     totlen   = (int)(rightbrace-leftbrace) + 1; /*tot in { \atop }*/
                   5572: 	char *open=atopdelims[2*isymbol], *close=atopdelims[2*isymbol+1];
                   5573: 	char arg[8192], command[8192];	/* left/right args, new \atop{}{} */
                   5574: 	*command = '\000';		/* start with null string */
                   5575: 	if (open!=NULL) strcat(command,open); /* add open delim if needed */
                   5576: 	strcat(command,atopsym);	/* add command with \atop */
                   5577: 	arg[0] = '{';			/* arg starts with { */
                   5578: 	memcpy(arg+1,leftbrace+1,leftlen); /* extract left-hand arg */
                   5579: 	arg[leftlen+1] = '\000';	/* and null terminate it */
                   5580: 	strcat(command,arg);		/* concatanate {left-arg to \atop */
                   5581: 	strcat(command,"}{");		/* close left-arg, open right-arg */
                   5582: 	memcpy(arg,tokptr+atoplen,rightlen); /* right-hand arg */
                   5583: 	arg[rightlen] = '}';		/* add closing } */
                   5584: 	arg[rightlen+1] = '\000';	/* and null terminate it */
                   5585: 	if ( isthischar(*arg,WHITEMATH) ) /* 1st char was mandatory space */
                   5586: 	  strcpy(arg,arg+1);		/* so squeeze it out */
                   5587: 	strcat(command,arg);		/* concatanate right-arg} */
                   5588: 	if (close!=NULL) strcat(command,close); /* add close delim if needed*/
                   5589: 	strchange(totlen-2,leftbrace+1,command); /* {\atop} --> {\atop{}{}} */
                   5590: 	expptr = leftbrace+strlen(command); /*resume search past \atop{}{}*/
                   5591: 	}
                   5592:     } /* --- end-of-while(tokptr!=NULL) --- */
                   5593:   } /* --- end-of-for(isymbol) --- */
                   5594: /* -------------------------------------------------------------------------
                   5595: back to caller with preprocessed expression
                   5596: -------------------------------------------------------------------------- */
                   5597: if ( msgfp!=NULL && msglevel>=99 )	/* display preprocessed expression */
                   5598:   { fprintf(msgfp,"mimeprep> expression=\"\"%s\"\"\n",expression);
                   5599:     fflush(msgfp); }
                   5600: return ( expression );
                   5601: } /* --- end-of-function mimeprep() --- */
                   5602: 
                   5603: 
                   5604: /* ==========================================================================
                   5605:  * Function:	strchange ( int nfirst, char *from, char *to )
                   5606:  * Purpose:	Changes the nfirst leading chars of `from` to `to`.
                   5607:  *		For example, to change char x[99]="12345678" to "123ABC5678"
                   5608:  *		call strchange(1,x+3,"ABC")
                   5609:  * --------------------------------------------------------------------------
                   5610:  * Arguments:	nfirst (I)	int containing #leading chars of `from`
                   5611:  *				that will be replace by `to`
                   5612:  *		from (I/O)	char * to null-terminated string whose nfirst
                   5613:  *				leading chars will be replaced by `to`
                   5614:  *		to (I)		char * to null-terminated string that will
                   5615:  *				replace the nfirst leading chars of `from`
                   5616:  * --------------------------------------------------------------------------
                   5617:  * Returns:	( char * )	ptr to first char of input `from`
                   5618:  *				or NULL for any error.
                   5619:  * --------------------------------------------------------------------------
                   5620:  * Notes:     o	If strlen(to)>nfirst, from must have memory past its null
                   5621:  *		(i.e., we don't do a realloc)
                   5622:  * ======================================================================= */
                   5623: /* --- entry point --- */
                   5624: char	*strchange ( int nfirst, char *from, char *to )
                   5625: {
                   5626: /* -------------------------------------------------------------------------
                   5627: Allocations and Declarations
                   5628: -------------------------------------------------------------------------- */
                   5629: int	tolen = (to==NULL?0:strlen(to)), /* #chars in replacement string */
                   5630: 	nshift = abs(tolen-nfirst);	/*need to shift from left or right*/
                   5631: /* -------------------------------------------------------------------------
                   5632: shift from left or right to accommodate replacement of its nfirst chars by to
                   5633: -------------------------------------------------------------------------- */
                   5634: if ( tolen < nfirst )			/* shift left is easy */
                   5635:   strcpy(from,from+nshift);		/* because memory doesn't overlap */
                   5636: if ( tolen > nfirst )			/* need more room at start of from */
                   5637:   { char *pfrom = from+strlen(from);	/* ptr to null terminating from */
                   5638:     for ( ; pfrom>=from; pfrom-- )	/* shift all chars including null */
                   5639:       *(pfrom+nshift) = *pfrom; }	/* shift chars nshift places right */
                   5640: /* -------------------------------------------------------------------------
                   5641: from has exactly the right number of free leading chars, so just put to there
                   5642: -------------------------------------------------------------------------- */
                   5643: if ( tolen != 0 )			/* make sure to not empty or null */
                   5644:   memcpy(from,to,tolen);		/* chars moved into place */
                   5645: return ( from );			/* changed string back to caller */
                   5646: } /* --- end-of-function strchange() --- */
                   5647: 
                   5648: 
                   5649: /* ==========================================================================
                   5650:  * Function:	strreplace (char *string, char *from, char *to, int nreplace)
                   5651:  * Purpose:	Changes the first nreplace occurrences of 'from' to 'to'
                   5652:  *		in string, or all occurrences if nreplace=0.
                   5653:  * --------------------------------------------------------------------------
                   5654:  * Arguments:	string (I/0)	char * to null-terminated string in which
                   5655:  *				occurrence of 'from' will be replaced by 'to'
                   5656:  *		from (I)	char * to null-terminated string
                   5657:  *				to be replaced by 'to'
                   5658:  *		to (I)		char * to null-terminated string that will
                   5659:  *				replace 'from'
                   5660:  *		nreplace (I)	int containing (maximum) number of
                   5661:  *				replacements, or 0 to replace all.
                   5662:  * --------------------------------------------------------------------------
                   5663:  * Returns:	( int )		number of replacements performed,
                   5664:  *				or 0 for no replacements or -1 for any error.
                   5665:  * --------------------------------------------------------------------------
                   5666:  * Notes:     o
                   5667:  * ======================================================================= */
                   5668: /* --- entry point --- */
                   5669: int	strreplace ( char *string, char *from, char *to, int nreplace )
                   5670: {
                   5671: /* -------------------------------------------------------------------------
                   5672: Allocations and Declarations
                   5673: -------------------------------------------------------------------------- */
                   5674: int	fromlen = (from==NULL?0:strlen(from)), /* #chars to be replaced */
                   5675: 	tolen = (to==NULL?0:strlen(to)); /* #chars in replacement string */
                   5676: char	*pfrom = (char *)NULL,		/*ptr to 1st char of from in string*/
                   5677: 	*pstring = string,		/*ptr past previously replaced from*/
                   5678: 	*strchange();			/* change 'from' to 'to' */
                   5679: int	nreps = 0;			/* #replacements returned to caller*/
                   5680: /* -------------------------------------------------------------------------
                   5681: repace occurrences of 'from' in string to 'to'
                   5682: -------------------------------------------------------------------------- */
                   5683: if ( string == (char *)NULL		/* no input string */
                   5684: ||   (fromlen<1 && nreplace<=0) )	/* replacing empty string forever */
                   5685:   nreps = (-1);				/* so signal error */
                   5686: else					/* args okay */
                   5687:   while (nreplace<1 || nreps<nreplace)	/* up to #replacements requested */
                   5688:     {
                   5689:     if ( fromlen > 0 )			/* have 'from' string */
                   5690:       pfrom = strstr(pstring,from);	/*ptr to 1st char of from in string*/
                   5691:     else  pfrom = pstring;		/*or empty from at start of string*/
                   5692:     if ( pfrom == (char *)NULL ) break;	/*no more from's, so back to caller*/
                   5693:     if ( strchange(fromlen,pfrom,to)	/* leading 'from' changed to 'to' */
                   5694:     ==   (char *)NULL ) { nreps=(-1); break; } /* signal error to caller */
                   5695:     nreps++;				/* count another replacement */
                   5696:     pstring = pfrom+tolen;		/* pick up search after 'to' */
                   5697:     if ( *pstring == '\000' ) break;	/* but quit at end of string */
                   5698:     } /* --- end-of-while() --- */
                   5699: return ( nreps );			/* #replacements back to caller */
                   5700: } /* --- end-of-function strreplace() --- */
                   5701: 
                   5702: 
                   5703: /* ==========================================================================
1.3     ! albertel 5704:  * Function:	strwstr (char *string, char *substr, char *white, int *sublen)
        !          5705:  * Purpose:	Find first substr in string, but wherever substr contains
        !          5706:  *		a whitespace char (in white), string may contain any number
        !          5707:  *		(including 0) of whitespace chars. If white contains I or i,
        !          5708:  *		then match is case-insensitive (and I,i _not_ whitespace).
        !          5709:  * --------------------------------------------------------------------------
        !          5710:  * Arguments:	string (I)	char * to null-terminated string in which
        !          5711:  *				first occurrence of substr will be found
        !          5712:  *		substr (I)	char * to null-terminated string containing
        !          5713:  *				"template" that will be searched for
        !          5714:  *		white (I)	char * to null-terminated string containing
        !          5715:  *				whitespace chars.  If NULL or empty, then
        !          5716:  *				"~ \t\n\r\f\v" (WHITEMATH in mimetex.h) used.
        !          5717:  *				If white contains I or i, then match is
        !          5718:  *				case-insensitive (and I,i _not_ considered
        !          5719:  *				whitespace).
        !          5720:  *		sublen (O)	address of int returning "length" of substr
        !          5721:  *				found in string (which may be longer or
        !          5722:  *				shorter than substr itself).
        !          5723:  * --------------------------------------------------------------------------
        !          5724:  * Returns:	( char * )	ptr to first char of substr in string
        !          5725:  *				or NULL if not found or for any error.
        !          5726:  * --------------------------------------------------------------------------
        !          5727:  * Notes:     o	Wherever a single whitespace char appears in substr,
        !          5728:  *		the corresponding position in string may contain any
        !          5729:  *		number (including 0) of whitespace chars, e.g.,
        !          5730:  *		string="abc   def" and string="abcdef" both match
        !          5731:  *		substr="c d" at offset 2 of string.
        !          5732:  *	      o	If substr="c  d" (two spaces between c and d),
        !          5733:  *		then string must have at least one space, so now "abcdef"
        !          5734:  *		doesn't match.  In general, the minimum number of spaces
        !          5735:  *		in string is the number of spaces in substr minus 1
        !          5736:  *		(so 1 space in substr permits 0 spaces in string).
        !          5737:  *	      o	Embedded spaces are counted in sublen, e.g.,
        !          5738:  *		string="c   d" (three spaces) matches substr="c d"
        !          5739:  *		with sublen=5 returned.  But string="ab   c   d" will
        !          5740:  *		also match substr="  c d" returning sublen=5 and
        !          5741:  *		a ptr to the "c".  That is, the mandatory preceding
        !          5742:  *		space is _not_ counted as part of the match.
        !          5743:  *		But all the embedded space is counted.
        !          5744:  *		(An inconsistent bug/feature is that mandatory
        !          5745:  *		terminating space is counted.)
        !          5746:  *	      o	Moreover, string="c   d" matches substr="  c d", i.e.,
        !          5747:  *		the very beginning of a string is assumed to be preceded
        !          5748:  *		by "virtual blanks".
        !          5749:  * ======================================================================= */
        !          5750: /* --- entry point --- */
        !          5751: char	*strwstr ( char *string, char *substr, char *white, int *sublen )
        !          5752: {
        !          5753: /* -------------------------------------------------------------------------
        !          5754: Allocations and Declarations
        !          5755: -------------------------------------------------------------------------- */
        !          5756: char	*psubstr=substr, *pstring=string,/*ptr to current char in substr,str*/
        !          5757: 	*pfound = (char *)NULL;		/*ptr to found substr back to caller*/
        !          5758: char	*pwhite=NULL, whitespace[256];	/* callers white whithout i,I */
        !          5759: int	iscase = (white==NULL?1:	/* case-sensitive if i,I in white */
        !          5760: 	strchr(white,'i')==NULL && strchr(white,'I')==NULL);
        !          5761: int	foundlen = 0;			/* length of substr found in string*/
        !          5762: int	nstrwhite=0, nsubwhite=0,	/* #leading white chars in str,sub */
        !          5763: 	nminwhite=0;			/* #mandatory leading white in str */
        !          5764: int	nstrchars=0, nsubchars=0,	/* #non-white chars to be matched */
        !          5765: 	isncmp=0;			/*strncmp() or strncasecmp() result*/
        !          5766: /* -------------------------------------------------------------------------
        !          5767: Initialization
        !          5768: -------------------------------------------------------------------------- */
        !          5769: /* --- set up whitespace --- */
        !          5770: strcpy(whitespace,WHITEMATH);		/*default if no user input for white*/
        !          5771: if ( white != NULL )			/*user provided ptr to white string*/
        !          5772:  if ( *white != '\000' ) {		/*and it's not just an empty string*/
        !          5773:    strcpy(whitespace,white);		/* so use caller's white spaces */
        !          5774:    while ( (pwhite=strchr(whitespace,'i')) != NULL ) /* have an embedded i */
        !          5775:      strcpy(pwhite,pwhite+1);		/* so squeeze it out */
        !          5776:    while ( (pwhite=strchr(whitespace,'I')) != NULL ) /* have an embedded I */
        !          5777:      strcpy(pwhite,pwhite+1);		/* so squeeze it out */
        !          5778:    if ( *whitespace == '\000' )		/* caller's white just had i,I */
        !          5779:      strcpy(whitespace,WHITEMATH); }	/* so revert back to default */
        !          5780: /* -------------------------------------------------------------------------
        !          5781: Find first occurrence of substr in string
        !          5782: -------------------------------------------------------------------------- */
        !          5783: if ( string != NULL )			/* caller passed us a string ptr */
        !          5784:  while ( *pstring != '\000' ) {		/* break when string exhausted */
        !          5785:   char	*pstrptr = pstring;		/* (re)start at next char in string*/
        !          5786:   int	leadingwhite = 0;		/* leading whitespace */
        !          5787:   psubstr = substr;			/* start at beginning of substr */
        !          5788:   foundlen = 0;				/* reset length of found substr */
        !          5789:   if ( substr != NULL )			/* caller passed us a substr ptr */
        !          5790:    while ( *psubstr != '\000' ) {	/*see if pstring begins with substr*/
        !          5791:     /* --- check for end-of-string before finding match --- */
        !          5792:     if ( *pstrptr == '\000' )		/* end-of-string without a match */
        !          5793:       goto nextstrchar;			/* keep trying with next char */
        !          5794:     /* --- actual amount of whitespace in string and substr --- */
        !          5795:     nsubwhite = strspn(psubstr,whitespace); /* #leading white chars in sub */
        !          5796:     nstrwhite = strspn(pstrptr,whitespace); /* #leading white chars in str */
        !          5797:     nminwhite = max2(0,nsubwhite-1);	/* #mandatory leading white in str */
        !          5798:     /* --- check for mandatory leading whitespace in string --- */
        !          5799:     if ( pstrptr != string )		/*not mandatory at start of string*/
        !          5800:       if ( nstrwhite < nminwhite )	/* too little leading white space */
        !          5801: 	goto nextstrchar;		/* keep trying with next char */
        !          5802:     /* ---hold on to #whitespace chars in string preceding substr match--- */
        !          5803:     if ( pstrptr == pstring )		/* whitespace at start of substr */
        !          5804:       leadingwhite = nstrwhite;		/* save it as leadingwhite */
        !          5805:     /* --- check for optional whitespace --- */
        !          5806:     if ( psubstr != substr )		/* always okay at start of substr */
        !          5807:       if ( nstrwhite>0 && nsubwhite<1 )	/* too much leading white space */
        !          5808: 	goto nextstrchar;		/* keep trying with next char */
        !          5809:     /* --- skip any leading whitespace in substr and string --- */
        !          5810:     psubstr += nsubwhite;		/* push past leading sub whitespace*/
        !          5811:     pstrptr += nstrwhite;		/* push past leading str whitespace*/
        !          5812:     /* --- now get non-whitespace chars that we have to match --- */
        !          5813:     nsubchars = strcspn(psubstr,whitespace); /* #non-white chars in sub */
        !          5814:     nstrchars = strcspn(pstrptr,whitespace); /* #non-white chars in str */
        !          5815:     if ( nstrchars < nsubchars )	/* too few chars for match */
        !          5816:       goto nextstrchar;			/* keep trying with next char */
        !          5817:     /* --- see if next nsubchars are a match --- */
        !          5818:     isncmp = (iscase? strncmp(pstrptr,psubstr,nsubchars): /*case sensitive*/
        !          5819: 		strncasecmp(pstrptr,psubstr,nsubchars)); /*case insensitive*/
        !          5820:     if ( isncmp != 0 )			/* no match */
        !          5821:       goto nextstrchar;			/* keep trying with next char */
        !          5822:     /* --- push past matched chars --- */
        !          5823:     psubstr += nsubchars;  pstrptr += nsubchars;  /*nsubchars were matched*/
        !          5824:     } /* --- end-of-while(*psubstr!='\000') --- */
        !          5825:   pfound = pstring + leadingwhite;	/* found match starting at pstring */
        !          5826:   foundlen = (int)(pstrptr-pfound);	/* consisting of this many chars */
        !          5827:   goto end_of_job;			/* back to caller */
        !          5828:   /* ---failed to find substr, continue trying with next char in string--- */
        !          5829:   nextstrchar:				/* continue outer loop */
        !          5830:     pstring++;				/* bump to next char in string */
        !          5831:   } /* --- end-of-while(*pstring!='\000') --- */
        !          5832: /* -------------------------------------------------------------------------
        !          5833: Back to caller with ptr to first occurrence of substr in string
        !          5834: -------------------------------------------------------------------------- */
        !          5835: end_of_job:
        !          5836:   if ( msglevel>=999 && msgfp!=NULL) {	/* debugging/diagnostic output */
        !          5837:     fprintf(msgfp,"strwstr> str=\"%.72s\" sub=\"%s\" found at offset %d\n",
        !          5838:     string,substr,(pfound==NULL?(-1):(int)(pfound-string))); fflush(msgfp); }
        !          5839:   if ( sublen != NULL )			/*caller wants length of found substr*/
        !          5840:     *sublen = foundlen;			/* give it to him along with ptr */
        !          5841:   return ( pfound );			/*ptr to first found substr, or NULL*/
        !          5842: } /* --- end-of-function strwstr() --- */
        !          5843: 
        !          5844: 
        !          5845: /* ==========================================================================
        !          5846:  * Function:	strtexchr ( char *string, char *texchr )
1.1       albertel 5847:  * Purpose:	Find first texchr in string, but texchr must be followed
                   5848:  *		by non-alpha
                   5849:  * --------------------------------------------------------------------------
                   5850:  * Arguments:	string (I)	char * to null-terminated string in which
1.3     ! albertel 5851:  *				first occurrence of delim will be found
1.1       albertel 5852:  *		texchr (I)	char * to null-terminated string that
                   5853:  *				will be searched for
                   5854:  * --------------------------------------------------------------------------
                   5855:  * Returns:	( char * )	ptr to first char of texchr in string
                   5856:  *				or NULL if not found or for any error.
                   5857:  * --------------------------------------------------------------------------
                   5858:  * Notes:     o	texchr should contain its leading \, e.g., "\\left"
                   5859:  * ======================================================================= */
                   5860: /* --- entry point --- */
                   5861: char	*strtexchr ( char *string, char *texchr )
                   5862: {
                   5863: /* -------------------------------------------------------------------------
                   5864: Allocations and Declarations
                   5865: -------------------------------------------------------------------------- */
1.2       albertel 5866: char	delim, *ptexchr=(char *)NULL;	/* ptr returned to caller*/
1.1       albertel 5867: char	*pstring = string;		/* start or continue up search here*/
                   5868: int	texchrlen = (texchr==NULL?0:strlen(texchr)); /* #chars in texchr */
                   5869: /* -------------------------------------------------------------------------
                   5870: locate texchr in string
                   5871: -------------------------------------------------------------------------- */
                   5872: if ( string != (char *)NULL		/* check that we got input string */
1.3     ! albertel 5873: &&   texchrlen > 0 ) {			/* and a texchr to search for */
1.1       albertel 5874:  while ( (ptexchr=strstr(pstring,texchr)) /* look for texchr in string */
                   5875:  != (char *)NULL )			/* found it */
                   5876:   if ( (delim = ptexchr[texchrlen])	/* char immediately after texchr */
                   5877:   ==   '\000' ) break;			/* texchr at very end of string */
                   5878:   else					/* if there are chars after texchr */
                   5879:    if ( isalpha(delim)			/*texchr is prefix of longer symbol*/
                   5880:    ||   0 )				/* other tests to be determined */
                   5881:     pstring = ptexchr + texchrlen;	/* continue search after texchr */
                   5882:    else					/* passed all tests */
1.3     ! albertel 5883:     break; }				/*so return ptr to texchr to caller*/
1.1       albertel 5884: return ( ptexchr );			/* ptr to texchar back to caller */
                   5885: } /* --- end-of-function strtexchr() --- */
                   5886: 
                   5887: 
                   5888: /* ==========================================================================
                   5889:  * Function:	findbraces ( char *expression, char *command )
                   5890:  * Purpose:	If expression!=NULL, finds opening left { preceding command;
                   5891:  *		if expression==NULL, finds closing right } after command.
                   5892:  *		For example, to parse out {a+b\over c+d} call findbraces()
                   5893:  *		twice.
                   5894:  * --------------------------------------------------------------------------
                   5895:  * Arguments:	expression (I)	NULL to find closing right } after command,
                   5896:  *				or char * to null-terminated string to find
                   5897:  *				left opening { preceding command.
                   5898:  *		command (I)	char * to null-terminated string whose
                   5899:  *				first character is usually the \ of \command
                   5900:  * --------------------------------------------------------------------------
                   5901:  * Returns:	( char * )	ptr to either opening { or closing },
                   5902:  *				or NULL for any error.
                   5903:  * --------------------------------------------------------------------------
                   5904:  * Notes:     o
                   5905:  * ======================================================================= */
                   5906: /* --- entry point --- */
                   5907: char	*findbraces ( char *expression, char *command )
                   5908: {
                   5909: /* -------------------------------------------------------------------------
                   5910: Allocations and Declarations
                   5911: -------------------------------------------------------------------------- */
                   5912: int	isopen = (expression==NULL?0:1); /* true to find left opening { */
                   5913: char	*left="{", *right="}",		/* delims bracketing {x\command y} */
                   5914: 	*delim = (isopen?left:right),	/* delim we want,  { if isopen */
                   5915: 	*match = (isopen?right:left),	/* matching delim, } if isopen */
                   5916: 	*brace = NULL;			/* ptr to delim returned to caller */
                   5917: int	inc = (isopen?-1:+1);		/* pointer increment */
                   5918: int	level = 1;			/* nesting level, for {{}\command} */
                   5919: char	*ptr = command;			/* start search here */
                   5920: int	setbrace = 1;			/* true to set {}'s if none found */
                   5921: /* -------------------------------------------------------------------------
                   5922: search for left opening { before command, or right closing } after command
                   5923: -------------------------------------------------------------------------- */
                   5924: while ( 1 )				/* search for brace, or until end */
                   5925:   {
                   5926:   /* --- next char to check for delim --- */
                   5927:   ptr += inc;				/* bump ptr left or right */
                   5928:   /* --- check for beginning or end of expression --- */
                   5929:   if ( isopen )				/* going left, check for beginning */
                   5930:        { if ( ptr < expression ) break;	} /* went before start of string */
                   5931:   else { if ( *ptr == '\000' ) break; }	/* went past end of string */
                   5932:   /* --- don't check this char if it's escaped --- */
                   5933:   if ( !isopen || ptr>expression )	/* very first char can't be escaped*/
                   5934:     if ( isthischar(*(ptr-1),ESCAPE) )	/* escape char precedes current */
                   5935:       continue;				/* so don't check this char */
                   5936:   /* --- check for delim --- */
                   5937:   if ( isthischar(*ptr,delim) )		/* found delim */
                   5938:     if ( --level == 0 )			/* and it's not "internally" nested*/
                   5939:       {	brace = ptr;			/* set ptr to brace */
                   5940: 	goto end_of_job; }		/* and return it to caller */
                   5941:   /* --- check for matching delim --- */
                   5942:   if ( isthischar(*ptr,match) )		/* found matching delim */
                   5943:     level++;				/* so bump nesting level */
                   5944:   } /* --- end-of-while(1) --- */
                   5945: end_of_job:
                   5946:   if ( brace == (char *)NULL )		/* open{ or close} not found */
                   5947:     if ( setbrace )			/* want to force one at start/end? */
                   5948:       brace = ptr;			/* { before expressn, } after cmmnd*/
                   5949:   return ( brace );			/*back to caller with delim or NULL*/
                   5950: } /* --- end-of-function findbraces() --- */
                   5951: #endif /* PART2 */
                   5952: 
                   5953: /* ---
                   5954:  * PART3
                   5955:  * ------ */
                   5956: #if !defined(PARTS) || defined(PART3)
                   5957: /* ==========================================================================
                   5958:  * Function:	rasterize ( expression, size )
                   5959:  * Purpose:	returns subraster corresponding to (a valid LaTeX) expression
                   5960:  *		at font size
                   5961:  * --------------------------------------------------------------------------
                   5962:  * Arguments:	expression (I)	char * to first char of null-terminated
                   5963:  *				string containing valid LaTeX expression
                   5964:  *				to be rasterized
                   5965:  *		size (I)	int containing 0-4 default font size
                   5966:  * --------------------------------------------------------------------------
                   5967:  * Returns:	( subraster * )	ptr to subraster corresponding to expression,
                   5968:  *				or NULL for any parsing error.
                   5969:  * --------------------------------------------------------------------------
                   5970:  * Notes:     o	This is mimeTeX's "main" reusable entry point.  Easy to use:
                   5971:  *		just call it with a LaTeX expression, and get back a bitmap
                   5972:  *		of that expression.  Then do what you want with the bitmap.
                   5973:  * ======================================================================= */
                   5974: /* --- entry point --- */
                   5975: subraster *rasterize ( char *expression, int size )
                   5976: {
                   5977: /* -------------------------------------------------------------------------
                   5978: Allocations and Declarations
                   5979: -------------------------------------------------------------------------- */
1.3     ! albertel 5980: char	*preamble(), pretext[512];	/* process preamble, if present */
        !          5981: char	chartoken[MAXSUBXSZ+1], *texsubexpr(), /*get subexpression from expr*/
1.1       albertel 5982: 	*subexpr = chartoken;		/* token may be parenthesized expr */
                   5983: int	isbrace();			/* check subexpr for braces */
                   5984: mathchardef *symdef, *get_symdef();	/*get mathchardef struct for symbol*/
1.3     ! albertel 5985: int	ligdef, get_ligature();		/*get symtable[] index for ligature*/
1.1       albertel 5986: int	natoms=0;			/* #atoms/tokens processed so far */
                   5987: int	type_raster();			/* display debugging output */
                   5988: subraster *rasterize(),			/* recurse */
                   5989: 	*rastparen(),			/* handle parenthesized subexpr's */
                   5990: 	*rastlimits();			/* handle sub/superscripted expr's */
                   5991: subraster *rastcat(),			/* concatanate atom subrasters */
                   5992: 	*subrastcpy(),			/* copy final result if a charaster*/
                   5993: 	*new_subraster();		/* new subraster for isstring mode */
                   5994: subraster *get_charsubraster(),		/* character subraster */
                   5995: 	*sp=NULL, *prevsp=NULL,		/* raster for current, prev char */
                   5996: 	*expraster = (subraster *)NULL;	/* raster returned to caller */
                   5997: int	delete_subraster();		/* free everything before returning*/
1.3     ! albertel 5998: int	family = fontinfo[fontnum].family; /* current font family */
        !          5999: int	isleftscript = 0,		/* true if left-hand term scripted */
        !          6000: 	wasscripted = 0,		/* true if preceding token scripted*/
        !          6001: 	wasdelimscript = 0;		/* true if preceding delim scripted*/
1.1       albertel 6002: /*int	pixsz = 1;*/			/*default #bits per pixel, 1=bitmap*/
                   6003: /* --- global values saved/restored at each recursive iteration --- */
1.2       albertel 6004: int	wasstring = isstring,		/* initial isstring mode flag */
1.1       albertel 6005: 	wasdisplaystyle = isdisplaystyle, /*initial displaystyle mode flag*/
1.2       albertel 6006: 	oldfontnum = fontnum,		/* initial font family */
1.1       albertel 6007: 	oldfontsize = fontsize,		/* initial fontsize */
                   6008: 	olddisplaysize = displaysize,	/* initial \displaystyle size */
                   6009: 	oldshrinkfactor = shrinkfactor,	/* initial shrinkfactor */
1.2       albertel 6010: 	oldsmashmargin = smashmargin,	/* initial smashmargin */
                   6011: 	oldissmashdelta = issmashdelta, /* initial issmashdelta */
1.3     ! albertel 6012: 	oldisexplicitsmash = isexplicitsmash, /* initial isexplicitsmash */
        !          6013: 	oldisscripted = isscripted,	/* initial isscripted */
1.1       albertel 6014: 	*oldworkingparam = workingparam; /* initial working parameter */
                   6015: subraster *oldworkingbox = workingbox,	/* initial working box */
                   6016: 	*oldleftexpression = leftexpression; /*left half rasterized so far*/
                   6017: double	oldunitlength = unitlength;	/* initial unitlength */
                   6018: mathchardef *oldleftsymdef = leftsymdef; /* init oldleftsymdef */
                   6019: /* -------------------------------------------------------------------------
                   6020: initialization
                   6021: -------------------------------------------------------------------------- */
                   6022: recurlevel++;				/* wind up one more recursion level*/
                   6023: leftexpression = NULL;			/* no leading left half yet */
                   6024: isreplaceleft = 0;			/* reset replaceleft flag */
1.3     ! albertel 6025: if(1)fraccenterline = NOVALUE;		/* reset \frac baseline signal */
1.1       albertel 6026: /* shrinkfactor = shrinkfactors[max2(0,min2(size,LARGESTSIZE))];*/ /*set sf*/
                   6027: shrinkfactor = shrinkfactors[max2(0,min2(size,16))]; /* have 17 sf's */
1.3     ! albertel 6028: if ( msgfp!=NULL && msglevel >= 9 ) {	/*display expression for debugging*/
        !          6029:  fprintf(msgfp,
        !          6030:  "rasterize> recursion#%d, size=%d,\n\texpression=\"%s\"\n",
        !          6031:  recurlevel,size,(expression==NULL?"<null>":expression)); fflush(msgfp); }
1.1       albertel 6032: if ( expression == NULL ) goto end_of_job; /* nothing given to do */
                   6033: /* -------------------------------------------------------------------------
                   6034: preocess optional $-terminated preamble preceding expression
                   6035: -------------------------------------------------------------------------- */
                   6036: expression = preamble(expression,&size,pretext); /* size may be modified */
                   6037: if ( *expression == '\000' ) goto end_of_job; /* nothing left to do */
                   6038: fontsize = size;			/* start at requested size */
                   6039: if ( isdisplaystyle == 1 )		/* displaystyle enabled but not set*/
                   6040:  if ( !ispreambledollars )		/* style fixed by $$...$$'s */
                   6041:   isdisplaystyle = (fontsize>=displaysize? 2:1); /*force at large fontsize*/
                   6042: /* -------------------------------------------------------------------------
                   6043: build up raster one character (or subexpression) at a time
                   6044: -------------------------------------------------------------------------- */
                   6045: while ( 1 )
                   6046:   {
1.3     ! albertel 6047:   /* --- kludge for \= cyrillic ligature --- */
        !          6048:   isligature = 0;			/* no ligature found yet */
        !          6049:   family = fontinfo[fontnum].family;	/* current font family */
        !          6050:   if ( family == CYR10 )		/* may have cyrillic \= ligature */
        !          6051:    if ( (ligdef = get_ligature(expression,family)) /*check for any ligature*/
        !          6052:    >=    0 )				/* got some ligature */
        !          6053:     if ( memcmp(symtable[ligdef].symbol,"\\=",2) == 0 ) /* starts with \= */
        !          6054:      isligature = 1;			/* signal \= ligature */
1.1       albertel 6055:   /* --- get next character/token or subexpression --- */
1.3     ! albertel 6056:   subexprptr = expression;		/* ptr within expression to subexpr*/
1.1       albertel 6057:   expression = texsubexpr(expression,chartoken,0,LEFTBRACES,RIGHTBRACES,1,1);
                   6058:   subexpr = chartoken;			/* "local" copy of chartoken ptr */
                   6059:   leftsymdef = NULL;			/* no character identified yet */
                   6060:   sp = NULL;				/* no subraster yet */
                   6061:   size = fontsize;			/* in case reset by \tiny, etc */
1.3     ! albertel 6062:   /*isleftscript = isdelimscript;*/	/*was preceding term scripted delim*/
        !          6063:   wasscripted = isscripted;		/* true if preceding token scripted*/
        !          6064:   wasdelimscript = isdelimscript;	/* preceding \right delim scripted */
        !          6065:   if(1)isscripted = 0;			/* no subscripted expression yet */
        !          6066:   isdelimscript = 0;			/* reset \right delim scripted flag*/
1.1       albertel 6067:   /* --- debugging output --- */
1.3     ! albertel 6068:   if ( msgfp!=NULL && msglevel >= 9 ) {	/* display chartoken for debugging */
        !          6069:    fprintf(msgfp,
        !          6070:    "rasterize> recursion#%d,atom#%d=\"%s\" (isligature=%d,isleftscript=%d)\n",
        !          6071:    recurlevel,natoms+1,chartoken,isligature,isleftscript); fflush(msgfp); }
1.1       albertel 6072:   if ( expression == NULL		/* no more tokens */
                   6073:   &&   *subexpr == '\000' ) break;	/* and this token empty */
                   6074:   if ( *subexpr == '\000' ) break;	/* enough if just this token empty */
                   6075:   /* --- check for parenthesized subexpression --- */
                   6076:   if ( isbrace(subexpr,LEFTBRACES,1) )	/* got parenthesized subexpression */
                   6077:     { if ( (sp=rastparen(&subexpr,size,prevsp)) /* rasterize subexpression */
                   6078:       ==   NULL )  continue; }		/* flush it if failed to rasterize */
                   6079:   else /* --- single-character atomic token --- */
                   6080:    if ( !isthischar(*subexpr,SCRIPTS) )	/* scripts handled below */
                   6081:     {
1.3     ! albertel 6082:     /* --- first check for opening $ in \text{ if $n-m$ even} --- */
        !          6083:     if ( istextmode			/* we're in \text mode */
        !          6084:     &&   *subexpr=='$' && subexpr[1]=='\000' ) { /* and have an opening $ */
        !          6085:      char *endptr=NULL, mathexpr[MAXSUBXSZ+1]; /* $expression$ in \text{ }*/
        !          6086:      int  exprlen = 0;			/* length of $expression$ */
        !          6087:      int  textfontnum = fontnum;	/* current text font number */
        !          6088:      /*if ( (endptr=strrchr(expression,'$')) != NULL )*/ /*ptr to closing $*/
        !          6089:      if ( (endptr=strchr(expression,'$')) != NULL ) /* ptr to closing $ */
        !          6090:        exprlen = (int)(endptr-expression); /* #chars preceding closing $ */
        !          6091:      else {				/* no closing $ found */
        !          6092:        exprlen = strlen(expression);	/* just assume entire expression */
        !          6093:        endptr = expression + (exprlen-1); } /*and push expression to '\000'*/
        !          6094:      exprlen = min2(exprlen,MAXSUBXSZ);	/* don't overflow mathexpr[] */
        !          6095:      if ( exprlen > 0 ) {		/* have something between $$ */
        !          6096:        memcpy(mathexpr,expression,exprlen); /*local copy of math expression*/
        !          6097:        mathexpr[exprlen] = '\000';	/* null-terminate it */
        !          6098:        fontnum = 0;			/* set math mode */
        !          6099:        sp = rasterize(mathexpr,size);	/* and rasterize $expression$ */
        !          6100:        fontnum = textfontnum; }		/* set back to text mode */
        !          6101:      expression = endptr+1;		/* push expression past closing $ */
        !          6102:      } /* --- end-of-if(istextmode&&*subexpr=='$') --- */
        !          6103:     else
        !          6104:      /* --- otherwise, look up mathchardef for atomic token in table --- */
        !          6105:      if ( (leftsymdef=symdef=get_symdef(chartoken)) /*mathchardef for token*/
        !          6106:      ==  NULL )				/* lookup failed */
        !          6107:       { char literal[512] = "[?]";	/*display for unrecognized literal*/
        !          6108:         int  oldfontnum = fontnum;	/* error display in default mode */
        !          6109:         if ( msgfp!=NULL && msglevel >= 29 ) /* display unrecognized symbol*/
1.1       albertel 6110: 	 { fprintf(msgfp,"rasterize> get_symdef() failed for \"%s\"\n",
                   6111: 	   chartoken); fflush(msgfp); }
1.3     ! albertel 6112:         sp = (subraster *)NULL;		/* init to signal failure */
        !          6113:         if ( warninglevel < 1 ) continue; /* warnings not wanted */
        !          6114:         fontnum = 0;			/* reset from \mathbb, etc */
        !          6115:         if ( isthischar(*chartoken,ESCAPE) ) /* we got unrecognized \escape*/
        !          6116: 	 { /* --- so display literal {\rm~[\backslash~chartoken?]} ---  */
        !          6117: 	   strcpy(literal,"{\\rm~[\\backslash~"); /* init token */
        !          6118: 	   strcat(literal,chartoken+1);	/* add chars following leading \ */
        !          6119: 	   strcat(literal,"?]}"); }	/* add closing brace */
        !          6120:         sp = rasterize(literal,size-1);	/* rasterize literal token */
        !          6121:         fontnum = oldfontnum;		/* reset font family */
        !          6122:         if ( sp == (subraster *)NULL ) continue; }/*flush if rasterize fails*/
        !          6123:      else /* --- check if we have special handler to process this token --- */
        !          6124:       if ( symdef->handler != NULL )	/* have a handler for this token */
        !          6125:        { int arg1=symdef->charnum, arg2=symdef->family, arg3=symdef->class;
        !          6126:          if ( (sp = (subraster *)	/* returned void* is subraster* */
        !          6127: 	 (*(symdef->handler))(&expression,size,prevsp,arg1,arg2,arg3))==NULL)
        !          6128: 	   continue; }			/* flush token if handler failed */
        !          6129:       else /* --- no handler, so just get subraster for this character --- */
        !          6130:        if ( !isstring )			/* rasterizing */
        !          6131: 	{ if ( isligature )		/* found a ligature */
        !          6132: 	   expression = subexprptr + strlen(symdef->symbol); /*push past it*/
        !          6133: 	  if ( (sp=get_charsubraster(symdef,size)) /* get subraster */
1.1       albertel 6134: 	  ==  NULL )  continue; }	/* flush token if failed */
1.3     ! albertel 6135:        else				/* constructing ascii string */
1.1       albertel 6136: 	{ char *symbol = symdef->symbol; /* symbol for ascii string */
                   6137: 	  int symlen = (symbol!=NULL?strlen(symbol):0); /*#chars in symbol*/
                   6138: 	  if ( symlen < 1 ) continue;	/* no symbol for ascii string */
                   6139: 	  if ( (sp=new_subraster(symlen+1,1,8)) /* subraster for symbol */
                   6140: 	  ==  NULL )  continue;		/* flush token if malloc failed */
                   6141: 	  sp->type = ASCIISTRING;	/* set subraster type */
                   6142: 	  sp->symdef = symdef;		/* and set symbol definition */
                   6143: 	  sp->baseline = 1;		/* default (should be unused) */
                   6144: 	  strcpy((char *)((sp->image)->pixmap),symbol); /* copy symbol */
                   6145: 	  /*((char *)((sp->image)->pixmap))[symlen] = '\000';*/ } /*null*/
1.3     ! albertel 6146:     } /* --- end-of-if(!isthischar(*subexpr,SCRIPTS)) --- */
1.1       albertel 6147:   /* --- handle any super/subscripts following symbol or subexpression --- */
                   6148:   sp = rastlimits(&expression,size,sp);
1.3     ! albertel 6149:   isleftscript = (wasscripted||wasdelimscript?1:0);/*preceding term scripted*/
1.1       albertel 6150:   /* --- debugging output --- */
1.3     ! albertel 6151:   if ( msgfp!=NULL && msglevel >= 9 ) {	/* display raster for debugging */
        !          6152:    fprintf(msgfp,"rasterize> recursion#%d,atom#%d%s\n",
        !          6153:    recurlevel,natoms+1,(sp==NULL?" = <null>":"..."));
        !          6154:    if ( msglevel >= 9 ) fprintf(msgfp,
        !          6155:     "  isleftscript=%d is/wasscripted=%d,%d is/wasdelimscript=%d,%d\n",
        !          6156:     isleftscript,isscripted,wasscripted,isdelimscript,wasdelimscript);
        !          6157:    if ( msglevel >= 99 )
        !          6158:     if(sp!=NULL) type_raster(sp->image,msgfp); /* display raster */
        !          6159:    fflush(msgfp); }			/* flush msgfp buffer */
1.1       albertel 6160:   /* --- accumulate atom or parenthesized subexpression --- */
                   6161:   if ( natoms < 1			/* nothing previous to concat */
                   6162:   ||   expraster == NULL		/* or previous was complete error */
                   6163:   ||   isreplaceleft )			/* or we're replacing previous */
1.2       albertel 6164:     { if ( 1 && expraster!=NULL )	/* probably replacing left */
                   6165: 	delete_subraster(expraster);	/* so first free original left */
                   6166:       expraster = subrastcpy(sp);	/* copy static CHARASTER or left */
1.1       albertel 6167:       isreplaceleft = 0; }		/* reset replacement flag */
                   6168:   else					/*we've already built up atoms so...*/
1.3     ! albertel 6169:    if ( sp != NULL ) {			/* ...if we have a new term */
        !          6170:     int prevsmashmargin = smashmargin;	/* save current smash margin */
        !          6171:     if ( isleftscript ) {		/* don't smash against scripts */
        !          6172:      isdelimscript = 0;			/* reset \right delim scripted flag*/
        !          6173:      if ( !isexplicitsmash ) smashmargin = 0; } /* signal no smash wanted */
        !          6174:     expraster = rastcat(expraster,sp,1); /* concat new term, free previous */
        !          6175:     smashmargin = prevsmashmargin; }	/* restore current smash margin */
1.1       albertel 6176:   delete_subraster(prevsp);		/* free prev (if not a CHARASTER) */
                   6177:   prevsp = sp;				/* current becomes previous */
                   6178:   leftexpression = expraster;		/* left half rasterized so far */
                   6179:   /* --- bump count --- */
                   6180:   natoms++;				/* bump #atoms count */
                   6181:   } /* --- end-of-while(expression!=NULL) --- */
                   6182: /* -------------------------------------------------------------------------
                   6183: back to caller with rasterized expression
                   6184: -------------------------------------------------------------------------- */
                   6185: end_of_job:
                   6186:   delete_subraster(prevsp);		/* free last (if not a CHARASTER) */
                   6187:   /* --- debugging output --- */
                   6188:   if ( msgfp!=NULL && msglevel >= 999 )	/* display raster for debugging */
                   6189:     { fprintf(msgfp,"rasterize> Final recursion level=%d, atom#%d...\n",
                   6190:       recurlevel,natoms);
                   6191:       if ( expraster != (subraster *)NULL ) /* i.e., if natoms>0 */
                   6192: 	type_raster(expraster->image,msgfp); /* display completed raster */
                   6193:       fflush(msgfp); }			/* flush msgfp buffer */
1.2       albertel 6194:   /* --- set final raster buffer --- */
                   6195:   if ( 1 && expraster != (subraster *)NULL ) /* have an expression */
1.3     ! albertel 6196:     { int type = expraster->type;	/* type of constructed image */
        !          6197:       if ( type != FRACRASTER )		/* leave \frac alone */
        !          6198: 	expraster->type = IMAGERASTER;	/* set type to constructed image */
1.2       albertel 6199:       if ( istextmode )			/* but in text mode */
                   6200:         expraster->type = blanksignal;	/* set type to avoid smash */
                   6201:       expraster->size = fontsize; }	/* set original input font size */
1.1       albertel 6202:   /* --- restore flags/values to original saved values --- */
                   6203:   isstring = wasstring;			/* string mode reset */
                   6204:   isdisplaystyle = wasdisplaystyle;	/* displaystyle mode reset */
1.2       albertel 6205:   fontnum = oldfontnum;			/* font family reset */
1.1       albertel 6206:   fontsize = oldfontsize;		/* fontsize reset */
                   6207:   displaysize = olddisplaysize;		/* \displaystyle size reset */
                   6208:   shrinkfactor = oldshrinkfactor;	/* shrinkfactor reset */
1.2       albertel 6209:   smashmargin = oldsmashmargin;		/* smashmargin reset */
                   6210:   issmashdelta = oldissmashdelta;	/* issmashdelta reset */
1.3     ! albertel 6211:   isexplicitsmash = oldisexplicitsmash;	/* isexplicitsmash reset */
        !          6212:   isscripted = oldisscripted;		/* isscripted reset */
1.1       albertel 6213:   workingparam = oldworkingparam;	/* working parameter reset */
                   6214:   workingbox = oldworkingbox;		/* working box reset */
                   6215:   leftexpression = oldleftexpression;	/* leftexpression reset */
                   6216:   leftsymdef = oldleftsymdef;		/* leftsymdef reset */
                   6217:   unitlength = oldunitlength;		/* unitlength reset */
                   6218:   recurlevel--;				/* unwind one recursion level */
                   6219:   /* --- return final subraster to caller --- */
                   6220:   return ( expraster );
                   6221: } /* --- end-of-function rasterize() --- */
                   6222: 
                   6223: 
                   6224: /* ==========================================================================
                   6225:  * Function:	rastparen ( subexpr, size, basesp )
                   6226:  * Purpose:	parentheses handler, returns a subraster corresponding to
                   6227:  *		parenthesized subexpression at font size
                   6228:  * --------------------------------------------------------------------------
                   6229:  * Arguments:	subexpr (I)	char **  to first char of null-terminated
                   6230:  *				string beginning with a LEFTBRACES
                   6231:  *				to be rasterized
                   6232:  *		size (I)	int containing 0-5 default font size
                   6233:  *		basesp (I)	subraster *  to character (or subexpression)
                   6234:  *				immediately preceding leading left{
                   6235:  *				(unused, but passed for consistency)
                   6236:  * --------------------------------------------------------------------------
                   6237:  * Returns:	( subraster * )	ptr to subraster corresponding to subexpr,
                   6238:  *				or NULL for any parsing error
                   6239:  * --------------------------------------------------------------------------
                   6240:  * Notes:     o	This "handler" isn't in the mathchardef symbol table,
                   6241:  *		but is called directly from rasterize(), as necessary.
                   6242:  *	      o	Though subexpr is returned unchanged, it's passed as char **
                   6243:  *		for consistency with other handlers.  Ditto, basesp is unused
                   6244:  *		but passed for consistency
                   6245:  * ======================================================================= */
                   6246: /* --- entry point --- */
                   6247: subraster *rastparen ( char **subexpr, int size, subraster *basesp )
                   6248: {
                   6249: /* -------------------------------------------------------------------------
                   6250: Allocations and Declarations
                   6251: -------------------------------------------------------------------------- */
                   6252: char	*expression = *subexpr;		/* dereference subexpr to get char* */
                   6253: int	explen = strlen(expression);	/* total #chars, including parens */
                   6254: int	isescape = 0,			/* true if parens \escaped */
                   6255: 	isrightdot = 0,			/* true if right paren is \right. */
                   6256: 	isleftdot = 0;			/* true if left paren is \left. */
1.3     ! albertel 6257: char	left[32], right[32];		/* parens enclosing expresion */
        !          6258: char	noparens[MAXSUBXSZ+1];		/* get subexpr without parens */
1.1       albertel 6259: subraster *rasterize(), *sp=NULL;	/* rasterize what's between ()'s */
                   6260: int	isheight = 1;			/*true=full height, false=baseline*/
                   6261: int	height,				/* height of rasterized noparens[] */
                   6262: 	baseline;			/* and its baseline */
1.2       albertel 6263: int	family = /*CMSYEX*/ CMEX10;	/* family for paren chars */
1.1       albertel 6264: subraster *get_delim(), *lp=NULL, *rp=NULL; /* left and right paren chars */
                   6265: subraster *rastcat();			/* concatanate subrasters */
                   6266: int	delete_subraster();		/*in case of error after allocation*/
                   6267: /* -------------------------------------------------------------------------
                   6268: rasterize "interior" of expression, i.e., without enclosing parens
                   6269: -------------------------------------------------------------------------- */
                   6270: /* --- first see if enclosing parens are \escaped --- */
                   6271: if ( isthischar(*expression,ESCAPE) )	/* expression begins with \escape */
                   6272:   isescape = 1;				/* so set flag accordingly */
                   6273: /* --- get expression *without* enclosing parens --- */
                   6274: strcpy(noparens,expression);		/* get local copy of expression */
                   6275: noparens[explen-(1+isescape)] = '\000';	/* null-terminate before right} */
                   6276: strcpy(noparens,noparens+(1+isescape));	/* and then squeeze out left{ */
                   6277: /* --- rasterize it --- */
                   6278: if ( (sp = rasterize(noparens,size))	/*rasterize "interior" of expression*/
                   6279: ==   NULL ) goto end_of_job;		/* quit if failed */
                   6280: /* --- no need to add parentheses for unescaped { --- */
                   6281: if ( !isescape && isthischar(*expression,"{") ) /* don't add parentheses */
                   6282:   goto end_of_job;			/* just return sp to caller */
                   6283: /* -------------------------------------------------------------------------
                   6284: obtain paren characters to enclose noparens[] raster with
                   6285: -------------------------------------------------------------------------- */
                   6286: /* --- first get left and right parens from expression --- */
                   6287: memset(left,0,16);  memset(right,0,16);	/* init parens with nulls */
                   6288: left[0] = *(expression+isescape);	/* left{ is 1st or 2nd char */
                   6289: right[0] = *(expression+explen-1);	/* right} is always last char */
                   6290: isleftdot  = (isescape && isthischar(*left,".")); /* true if \left. */
                   6291: isrightdot = (isescape && isthischar(*right,".")); /* true if \right. */
                   6292: /* --- need height of noparens[] raster as minimum parens height --- */
                   6293: height = (sp->image)->height;		/* height of noparens[] raster */
                   6294: baseline = sp->baseline;		/* baseline of noparens[] raster */
                   6295: if ( !isheight ) height = baseline+1;	/* parens only enclose baseline up */
                   6296: /* --- get best-fit parentheses characters --- */
                   6297: if ( !isleftdot )			/* if not \left. */
                   6298:   lp = get_delim(left,height+1,family);	/* get left paren char */
                   6299: if ( !isrightdot )			/* and if not \right. */
                   6300:   rp = get_delim(right,height+1,family); /* get right paren char */
                   6301: if ( (lp==NULL && !isleftdot)		/* check that we got left( */
                   6302: ||   (rp==NULL && !isrightdot) )	/* and right) if needed */
                   6303:   { delete_subraster(sp);		/* if failed, free subraster */
                   6304:     if ( lp != NULL ) free ((void *)lp);/*free left-paren subraster envelope*/
                   6305:     if ( rp != NULL ) free ((void *)rp);/*and right-paren subraster envelope*/
                   6306:     sp = (subraster *)NULL;		/* signal error to caller */
                   6307:     goto end_of_job; }			/* and quit */
                   6308: /* -------------------------------------------------------------------------
                   6309: set paren baselines to center on noparens[] raster, and concat components
                   6310: -------------------------------------------------------------------------- */
                   6311: /* --- set baselines to center paren chars on raster --- */
                   6312: if ( lp != NULL )			/* ignore for \left. */
                   6313:   lp->baseline = baseline + ((lp->image)->height - height)/2;
                   6314: if ( rp != NULL )			/* ignore for \right. */
                   6315:   rp->baseline = baseline + ((rp->image)->height - height)/2;
                   6316: /* --- concat lp||sp||rp to obtain final result --- */
                   6317: if ( lp != NULL )			/* ignore \left. */
                   6318:   sp = rastcat(lp,sp,3);		/* concat lp||sp and free sp,lp */
                   6319: if ( sp != NULL )			/* succeeded or ignored \left. */
                   6320:   if ( rp != NULL )			/* ignore \right. */
                   6321:     sp = rastcat(sp,rp,3);		/* concat sp||rp and free sp,rp */
                   6322: /* --- back to caller --- */
                   6323: end_of_job:
                   6324:   return ( sp );
                   6325: } /* --- end-of-function rastparen() --- */
                   6326: 
                   6327: 
                   6328: /* ==========================================================================
                   6329:  * Function:	rastlimits ( expression, size, basesp )
                   6330:  * Purpose:	\limits, \nolimts, _ and ^ handler,
                   6331:  *		dispatches call to rastscripts() or to rastdispmath()
                   6332:  *		as necessary, to handle sub/superscripts following symbol
                   6333:  * --------------------------------------------------------------------------
                   6334:  * Arguments:	expression (I)	char **  to first char of null-terminated
                   6335:  *				LaTeX expression (unused/unchanged)
                   6336:  *		size (I)	int containing base font size (not used,
                   6337:  *				just stored in subraster)
                   6338:  *		basesp (I)	subraster *  to current character (or
                   6339:  *				subexpression) immediately preceding script
                   6340:  *				indicator
                   6341:  * --------------------------------------------------------------------------
                   6342:  * Returns:	( subraster * )	ptr to subraster returned by rastscripts()
                   6343:  *				or rastdispmath(), or NULL for any error
                   6344:  * --------------------------------------------------------------------------
                   6345:  * Notes:     o
                   6346:  * ======================================================================= */
                   6347: /* --- entry point --- */
                   6348: subraster *rastlimits ( char **expression, int size, subraster *basesp )
                   6349: {
                   6350: /* -------------------------------------------------------------------------
                   6351: Allocations and Declarations
                   6352: -------------------------------------------------------------------------- */
                   6353: subraster *rastscripts(), *rastdispmath(), /*one of these will do the work*/
                   6354: 	*rastcat(),			/* may need to concat scripts */
1.3     ! albertel 6355: 	*rasterize(),			/* may need to construct dummy base*/
        !          6356: 	*scriptsp = basesp,		/* and this will become the result */
        !          6357: 	*dummybase = basesp;		/* for {}_i construct a dummy base */
1.1       albertel 6358: int	isdisplay = (-1);		/* set 1 for displaystyle, else 0 */
1.2       albertel 6359: int	oldsmashmargin = smashmargin;	/* save original smashmargin */
1.1       albertel 6360: int	type_raster();			/* display debugging output */
1.3     ! albertel 6361: int	delete_subraster();		/* free dummybase, if necessary */
        !          6362: int	rastsmashcheck();		/* check if okay to smash scripts */
1.1       albertel 6363: /* --- to check for \limits or \nolimits preceding scripts --- */
                   6364: char	*texchar(), *exprptr=*expression, limtoken[255]; /*check for \limits*/
                   6365: int	toklen=0;			/* strlen(limtoken) */
                   6366: mathchardef *tokdef, *get_symdef();	/* mathchardef struct for limtoken */
                   6367: int	class=(leftsymdef==NULL?NOVALUE:leftsymdef->class); /*base sym class*/
                   6368: /* -------------------------------------------------------------------------
                   6369: determine whether or not to use displaymath
                   6370: -------------------------------------------------------------------------- */
                   6371: scriptlevel++;				/* first, increment subscript level*/
                   6372: *limtoken = '\000';			/* no token yet */
1.3     ! albertel 6373: isscripted = 0;				/* signal term not (text) scripted */
1.1       albertel 6374: if ( msgfp!=NULL && msglevel>=999 )
                   6375:  { fprintf(msgfp,"rastlimits> scriptlevel#%d exprptr=%.48s\n",
                   6376:    scriptlevel,(exprptr==NULL?"null":exprptr));  fflush(msgfp); }
                   6377: if ( isstring ) goto end_of_job;	/* no scripts for ascii string */
                   6378: /* --- check for \limits or \nolimits --- */
                   6379: skipwhite(exprptr);			/* skip white space before \limits */
                   6380: if ( exprptr != NULL )			/* expression ptr supplied */
                   6381:  if ( *exprptr != '\000' )		/* something in expression */
                   6382:   exprptr = texchar(exprptr,limtoken);	/* retrieve next token */
                   6383: if ( *limtoken != '\000' )		/* have token */
                   6384:  if ( (toklen=strlen(limtoken)) >= 3 )	/* which may be \[no]limits */
                   6385:   if ( memcmp("\\limits",limtoken,toklen) == 0     /* may be \limits */
                   6386:   ||   memcmp("\\nolimits",limtoken,toklen) == 0 ) /* or may be \nolimits */
                   6387:    if ( (tokdef= get_symdef(limtoken))	/* look up token to be sure */
1.3     ! albertel 6388:    !=   NULL ) {			/* found token in table */
1.1       albertel 6389:     if ( strcmp("\\limits",tokdef->symbol) == 0 )  /* found \limits */
                   6390:       isdisplay = 1;			/* so explicitly set displaymath */
                   6391:     else				/* wasn't \limits */
                   6392:       if ( strcmp("\\nolimits",tokdef->symbol) == 0 ) /* found \nolimits */
1.3     ! albertel 6393: 	isdisplay = 0; }		/* so explicitly reset displaymath */
1.1       albertel 6394: /* --- see if we found \[no]limits --- */
                   6395: if ( isdisplay != (-1) )		/* explicit directive found */
                   6396:   *expression = exprptr;		/* so bump expression past it */
                   6397: else					/* noexplicit directive */
                   6398:   { isdisplay = 0;			/* init displaymath flag off */
1.3     ! albertel 6399:     if ( isdisplaystyle ) {		/* we're in displaystyle math mode */
1.1       albertel 6400:       if ( isdisplaystyle >= 5 )	/* and mode irrevocably forced true */
                   6401: 	{ if ( class!=OPENING && class!=CLOSING ) /*don't force ('s and )'s*/
                   6402: 	    isdisplay = 1; }		/* set flag if mode forced true */
                   6403:       else
                   6404:        if ( isdisplaystyle >= 2 )	/*or mode forced conditionally true*/
                   6405: 	{ if ( class!=VARIABLE && class!=ORDINARY /*don't force characters*/
                   6406: 	  &&   class!=OPENING  && class!=CLOSING  /*don't force ('s and )'s*/
                   6407: 	  &&   class!=BINARYOP		/* don't force binary operators */
                   6408: 	  &&   class!=NOVALUE )		/* finally, don't force "images" */
                   6409: 	    isdisplay = 1; }		/* set flag if mode forced true */
                   6410:        else				/* determine mode from base symbol */
                   6411: 	if ( class == DISPOPER )	/* it's a displaystyle operator */
1.3     ! albertel 6412: 	  isdisplay = 1; } }		/* so set flag */
1.1       albertel 6413: /* -------------------------------------------------------------------------
                   6414: dispatch call to create sub/superscripts
                   6415: -------------------------------------------------------------------------- */
                   6416: if ( isdisplay )			/* scripts above/below base symbol */
                   6417:   scriptsp = rastdispmath(expression,size,basesp); /* everything all done */
1.3     ! albertel 6418: else {					/* scripts alongside base symbol */
        !          6419:   if ( dummybase == NULL )		/* no base symbol preceding scripts*/
        !          6420:     dummybase = rasterize("\\rule0{10}",size); /*guess a typical base symbol*/
        !          6421:   issmashokay = 1;			/*haven't found a no-smash char yet*/
        !          6422:   if((scriptsp=rastscripts(expression,size,dummybase)) == NULL) /*no scripts*/
1.1       albertel 6423:     scriptsp = basesp;			/* so just return unscripted symbol*/
1.3     ! albertel 6424:   else {				/* symbols followed by scripts */
        !          6425:     isscripted = 1;			/*signal current term text-scripted*/
1.1       albertel 6426:     if ( basesp != NULL )		/* have base symbol */
1.3     ! albertel 6427:      { /*if(0)smashmargin = 0;*/	/*don't smash script (doesn't work)*/
1.2       albertel 6428:        /*scriptsp = rastcat(basesp,scriptsp,2);*//*concat scripts to base sym*/
1.3     ! albertel 6429:        /* --- smash (or just concat) script raster against base symbol --- */
        !          6430:        if ( !issmashokay )		/* don't smash leading - */
        !          6431:          if ( !isexplicitsmash ) scriptsp->type = blanksignal; /*don't smash*/
1.2       albertel 6432:        scriptsp = rastcat(basesp,scriptsp,3); /*concat scripts to base sym*/
1.3     ! albertel 6433:        if(1) scriptsp->type = IMAGERASTER; /* flip type of composite object */
        !          6434:        /* --- smash (or just concat) scripted term to stuff to its left --- */
        !          6435:        issmashokay = 1;			/* okay to smash base expression */
        !          6436:        if ( 0 && smashcheck > 1 )	/* smashcheck=2 to check base */
        !          6437:          /* note -- we _don't_ have base expression available to check */
        !          6438:          issmashokay = rastsmashcheck(*expression); /*check if okay to smash*/
        !          6439:        if ( !issmashokay )		/* don't smash leading - */
        !          6440:          if ( !isexplicitsmash ) scriptsp->type = blanksignal; /*don't smash*/
        !          6441:        scriptsp->size = size; } } }	/* and set font size */
1.1       albertel 6442: end_of_job:
1.2       albertel 6443:   smashmargin = oldsmashmargin;		/* reset original smashmargin */
1.3     ! albertel 6444:   if ( dummybase != basesp ) delete_subraster(dummybase); /*free work area*/
1.1       albertel 6445:   if ( msgfp!=NULL && msglevel>=99 )
                   6446:     { fprintf(msgfp,"rastlimits> scriptlevel#%d returning %s\n",
                   6447: 	scriptlevel,(scriptsp==NULL?"null":"..."));
                   6448:       if ( scriptsp != NULL )		/* have a constructed raster */
                   6449: 	type_raster(scriptsp->image,msgfp); /*display constructed raster*/
                   6450:       fflush(msgfp); }
                   6451:   scriptlevel--;			/*lastly, decrement subscript level*/
                   6452:   return ( scriptsp );
                   6453: } /* --- end-of-function rastlimits() --- */
                   6454: 
                   6455: 
                   6456: /* ==========================================================================
                   6457:  * Function:	rastscripts ( expression, size, basesp )
                   6458:  * Purpose:	super/subscript handler, returns subraster for the leading
                   6459:  *		scripts in expression, whose base symbol is at font size
                   6460:  * --------------------------------------------------------------------------
                   6461:  * Arguments:	expression (I/O) char **  to first char of null-terminated
                   6462:  *				string beginning with a super/subscript,
                   6463:  *				and returning ptr immediately following
                   6464:  *				last script character processed.
                   6465:  *		size (I)	int containing 0-4 default font size
                   6466:  *		basesp (I)	subraster *  to character (or subexpression)
                   6467:  *				immediately preceding leading script
                   6468:  *				(scripts will be placed relative to base)
                   6469:  * --------------------------------------------------------------------------
                   6470:  * Returns:	( subraster * )	ptr to subraster corresponding to scripts,
                   6471:  *				or NULL for any parsing error
                   6472:  * --------------------------------------------------------------------------
                   6473:  * Notes:     o	This "handler" isn't in the mathchardef symbol table,
                   6474:  *		but is called directly from rasterize(), as necessary.
                   6475:  * ======================================================================= */
                   6476: /* --- entry point --- */
                   6477: subraster *rastscripts ( char **expression, int size, subraster *basesp )
                   6478: {
                   6479: /* -------------------------------------------------------------------------
                   6480: Allocations and Declarations
                   6481: -------------------------------------------------------------------------- */
                   6482: char	*texscripts(),			/* parse expression for scripts */
                   6483: 	subscript[512], supscript[512];	/* scripts parsed from expression */
                   6484: subraster *rasterize(), *subsp=NULL, *supsp=NULL; /* rasterize scripts */
                   6485: subraster *new_subraster(), *sp=NULL,	/* super- over subscript subraster */
                   6486: 	*rastack();			/*sets scripts in displaymath mode*/
                   6487: raster	*rp=NULL;			/* image raster embedded in sp */
                   6488: int	height=0, width=0,  baseline=0,	/* height,width,baseline of sp */
                   6489: 	subht=0,  subwidth=0,  subln=0,	/* height,width,baseline of sub */
                   6490: 	supht=0,  supwidth=0,  supln=0,	/* height,width,baseline of sup */
                   6491: 	baseht=0, baseln=0;		/* height,baseline of base */
                   6492: int	bdescend=0, sdescend=0;		/* descender of base, subscript */
                   6493: int	issub=0, issup=0, isboth=0,	/* true if we have sub,sup,both */
                   6494: 	isbase=0;			/* true if we have base symbol */
                   6495: int	szval = min2(max2(size,0),LARGESTSIZE), /* 0...LARGESTSIZE */
                   6496: 	vbetween = 2,			/* vertical space between scripts */
                   6497: 	vabove   = szval+1,		/*sup's top/bot above base's top/bot*/
                   6498: 	vbelow   = szval+1,		/*sub's top/bot below base's top/bot*/
                   6499: 	vbottom  = szval+1;		/*sup's bot above (sub's below) bsln*/
                   6500: /*int	istweak = 1;*/			/* true to tweak script positioning */
                   6501: int	rastput();			/*put scripts in constructed raster*/
                   6502: int	delete_subraster();		/* free work areas */
1.3     ! albertel 6503: int	rastsmashcheck();		/* check if okay to smash scripts */
1.1       albertel 6504: int	pixsz = 1;			/*default #bits per pixel, 1=bitmap*/
                   6505: /* -------------------------------------------------------------------------
                   6506: Obtain subscript and/or superscript expressions, and rasterize them/it
                   6507: -------------------------------------------------------------------------- */
                   6508: /* --- parse for sub,superscript(s), and bump expression past it(them) --- */
                   6509: if ( expression == NULL ) goto end_of_job; /* no *ptr given */
                   6510: if ( *expression == NULL ) goto end_of_job; /* no expression given */
                   6511: if ( *(*expression) == '\000' ) goto end_of_job; /* nothing in expression */
                   6512: *expression = texscripts(*expression,subscript,supscript,3);
                   6513: /* --- rasterize scripts --- */
                   6514: if ( *subscript != '\000' )		/* have a subscript */
                   6515:   subsp = rasterize(subscript,size-1);	/* so rasterize it at size-1 */
                   6516: if ( *supscript != '\000' )		/* have a superscript */
                   6517:   supsp = rasterize(supscript,size-1);	/* so rasterize it at size-1 */
                   6518: /* --- set flags for convenience --- */
                   6519: issub  = (subsp != (subraster *)NULL);	/* true if we have subscript */
                   6520: issup  = (supsp != (subraster *)NULL);	/* true if we have superscript */
                   6521: isboth = (issub && issup);		/* true if we have both */
                   6522: if (!issub && !issup) goto end_of_job;	/* quit if we have neither */
1.3     ! albertel 6523: /* --- check for leading no-smash chars (if enabled) --- */
        !          6524: issmashokay = 0;			/* default, don't smash scripts */
        !          6525: if ( smashcheck > 0 ) {			/* smash checking wanted */
        !          6526:  issmashokay = 1;			/*haven't found a no-smash char yet*/
        !          6527:  if ( issub )				/* got a subscript */
        !          6528:   issmashokay = rastsmashcheck(subscript); /* check if okay to smash */
        !          6529:  if ( issmashokay )			/* clean sub, so check sup */
        !          6530:   if ( issup )				/* got a superscript */
        !          6531:    issmashokay = rastsmashcheck(supscript); /* check if okay to smash */
        !          6532:  } /* --- end-of-if(smashcheck>0) --- */
1.1       albertel 6533: /* -------------------------------------------------------------------------
                   6534: get height, width, baseline of scripts,  and height, baseline of base symbol
                   6535: -------------------------------------------------------------------------- */
                   6536: /* --- get height and width of components --- */
                   6537: if ( issub )				/* we have a subscript */
                   6538:   { subht    = (subsp->image)->height;	/* so get its height */
                   6539:     subwidth = (subsp->image)->width;	/* and width */
                   6540:     subln    =  subsp->baseline; }	/* and baseline */
                   6541: if ( issup )				/* we have a superscript */
                   6542:   { supht    = (supsp->image)->height;	/* so get its height */
                   6543:     supwidth = (supsp->image)->width;	/* and width */
                   6544:     supln    =  supsp->baseline; }	/* and baseline */
                   6545: /* --- get height and baseline of base, and descender of base and sub --- */
                   6546: if ( basesp == (subraster *)NULL )	/* no base symbol for scripts */
                   6547:   basesp = leftexpression;		/* try using left side thus far */
                   6548: if ( basesp != (subraster *)NULL )	/* we have base symbol for scripts */
                   6549:   { baseht   = (basesp->image)->height;	/* height of base symbol */
                   6550:     baseln   =  basesp->baseline;	/* and its baseline */
                   6551:     bdescend =  baseht-(baseln+1);	/* and base symbol descender */
                   6552:     sdescend =  bdescend + vbelow;	/*sub must descend by at least this*/
                   6553:     if ( baseht > 0 ) isbase = 1; }	/* set flag */
                   6554: /* -------------------------------------------------------------------------
                   6555: determine width of constructed raster
                   6556: -------------------------------------------------------------------------- */
                   6557: width = max2(subwidth,supwidth);	/*widest component is overall width*/
                   6558: /* -------------------------------------------------------------------------
                   6559: determine height and baseline of constructed raster
                   6560: -------------------------------------------------------------------------- */
                   6561: /* --- both super/subscript --- */
                   6562: if ( isboth )				/*we have subscript and superscript*/
                   6563:   { height = max2(subht+vbetween+supht,	/* script heights + space bewteen */
                   6564: 		vbelow+baseht+vabove);	/*sub below base bot, sup above top*/
                   6565:     baseline = baseln + (height-baseht)/2; } /*center scripts on base symbol*/
                   6566: /* --- superscript only --- */
                   6567: if ( !issub )				/* we only have a superscript */
                   6568:   { height = max3(baseln+1+vabove,	/* sup's top above base symbol top */
                   6569: 		supht+vbottom,		/* sup's bot above baseln */
                   6570: 		supht+vabove-bdescend);	/* sup's bot above base symbol bot */
                   6571:     baseline = height-1; }		/*sup's baseline at bottom of raster*/
                   6572: /* --- subscript only --- */
1.3     ! albertel 6573: if ( !issup ) {				/* we only have a subscript */
1.1       albertel 6574:   if ( subht > sdescend )		/*sub can descend below base bot...*/
                   6575:     { height = subht;			/* ...without extra space on top */
                   6576:       baseline = height-(sdescend+1);	/* sub's bot below base symbol bot */
                   6577:       baseline = min2(baseline,max2(baseln-vbelow,0)); }/*top below base top*/
                   6578:   else					/* sub's top will be below baseln */
                   6579:     { height = sdescend+1;		/* sub's bot below base symbol bot */
1.3     ! albertel 6580:       baseline = 0; } }			/* sub's baseline at top of raster */
1.1       albertel 6581: /* -------------------------------------------------------------------------
                   6582: construct raster with superscript over subscript
                   6583: -------------------------------------------------------------------------- */
                   6584: /* --- allocate subraster containing constructed raster --- */
                   6585: if ( (sp=new_subraster(width,height,pixsz)) /*allocate subraster and raster*/
                   6586: ==   NULL )				/* and if we fail to allocate */
                   6587:   goto end_of_job;			/* quit */
                   6588: /* --- initialize subraster parameters --- */
                   6589: sp->type  = IMAGERASTER;		/* set type as constructed image */
                   6590: sp->size  = size;			/* set given size */
                   6591: sp->baseline = baseline;		/* composite scripts baseline */
                   6592: rp = sp->image;				/* raster embedded in subraster */
                   6593: /* --- place super/subscripts in new raster --- */
                   6594: if ( issup )				/* we have a superscript */
                   6595:  rastput(rp,supsp->image,0,0,1);	/* it goes in upper-left corner */
                   6596: if ( issub )				/* we have a subscript */
                   6597:  rastput(rp,subsp->image,height-subht,0,1); /*in lower-left corner*/
                   6598: /* -------------------------------------------------------------------------
                   6599: free unneeded component subrasters and return final result to caller
                   6600: -------------------------------------------------------------------------- */
                   6601: end_of_job:
                   6602:   if ( issub ) delete_subraster(subsp);	/* free unneeded subscript */
                   6603:   if ( issup ) delete_subraster(supsp);	/* and superscript */
                   6604:   return ( sp );
                   6605: } /* --- end-of-function rastscripts() --- */
                   6606: 
                   6607: 
                   6608: /* ==========================================================================
                   6609:  * Function:	rastdispmath ( expression, size, sp )
                   6610:  * Purpose:	displaymath handler, returns sp along with
                   6611:  *		its immediately following super/subscripts
                   6612:  * --------------------------------------------------------------------------
                   6613:  * Arguments:	expression (I/O) char **  to first char of null-terminated
                   6614:  *				string immediately following sp to be
                   6615:  *				rasterized along with its super/subscripts,
                   6616:  *				and returning ptr immediately following last
                   6617:  *				character processed.
                   6618:  *		size (I)	int containing 0-4 default font size
                   6619:  *		sp (I)		subraster *  to display math operator
                   6620:  *				to which super/subscripts will be added
                   6621:  * --------------------------------------------------------------------------
                   6622:  * Returns:	( subraster * )	ptr to subraster corresponding to sp
                   6623:  *				plus its scripts, or NULL for any error
                   6624:  * --------------------------------------------------------------------------
                   6625:  * Notes:     o	sp returned unchanged if no super/subscript(s) follow it.
                   6626:  * ======================================================================= */
                   6627: /* --- entry point --- */
                   6628: subraster *rastdispmath ( char **expression, int size, subraster *sp )
                   6629: {
                   6630: /* -------------------------------------------------------------------------
                   6631: Allocations and Declarations
                   6632: -------------------------------------------------------------------------- */
                   6633: char	*texscripts(),			/* parse expression for scripts */
                   6634: 	subscript[512], supscript[512];	/* scripts parsed from expression */
                   6635: int	issub=0, issup=0;		/* true if we have sub,sup */
                   6636: subraster *rasterize(), *subsp=NULL, *supsp=NULL, /* rasterize scripts */
                   6637: 	*rastack(),			/* stack operator with scripts */
                   6638: 	*new_subraster();		/* for dummy base sp, if needed */
                   6639: int	vspace = 1;			/* vertical space between scripts */
                   6640: /* -------------------------------------------------------------------------
                   6641: Obtain subscript and/or superscript expressions, and rasterize them/it
                   6642: -------------------------------------------------------------------------- */
                   6643: /* --- parse for sub,superscript(s), and bump expression past it(them) --- */
                   6644: if ( expression == NULL ) goto end_of_job; /* no *ptr given */
                   6645: if ( *expression == NULL ) goto end_of_job; /* no expression given */
                   6646: if ( *(*expression) == '\000' ) goto end_of_job; /* nothing in expression */
                   6647: *expression = texscripts(*expression,subscript,supscript,3);
                   6648: /* --- rasterize scripts --- */
                   6649: if ( *subscript != '\000' )		/* have a subscript */
                   6650:   subsp = rasterize(subscript,size-1);	/* so rasterize it at size-1 */
                   6651: if ( *supscript != '\000' )		/* have a superscript */
                   6652:   supsp = rasterize(supscript,size-1);	/* so rasterize it at size-1 */
                   6653: /* --- set flags for convenience --- */
                   6654: issub  = (subsp != (subraster *)NULL);	/* true if we have subscript */
                   6655: issup  = (supsp != (subraster *)NULL);	/* true if we have superscript */
                   6656: if (!issub && !issup) goto end_of_job;	/*return operator alone if neither*/
                   6657: /* -------------------------------------------------------------------------
                   6658: stack operator and its script(s)
                   6659: -------------------------------------------------------------------------- */
                   6660: /* --- stack superscript atop operator --- */
1.3     ! albertel 6661: if ( issup ) {				/* we have a superscript */
1.1       albertel 6662:  if ( sp == NULL )			/* but no base expression */
                   6663:   sp = supsp;				/* so just use superscript */
                   6664:  else					/* have base and superscript */
                   6665:   if ( (sp=rastack(sp,supsp,1,vspace,1,3)) /* stack supsp atop base sp */
1.3     ! albertel 6666:   ==   NULL ) goto end_of_job; }	/* and quit if failed */
1.1       albertel 6667: /* --- stack operator+superscript atop subscript --- */
1.3     ! albertel 6668: if ( issub ) {				/* we have a subscript */
1.1       albertel 6669:  if ( sp == NULL )			/* but no base expression */
                   6670:   sp = subsp;				/* so just use subscript */
                   6671:  else					/* have base and subscript */
                   6672:   if ( (sp=rastack(subsp,sp,2,vspace,1,3)) /* stack sp atop base subsp */
1.3     ! albertel 6673:   ==   NULL ) goto end_of_job; }	/* and quit if failed */
1.1       albertel 6674: sp->type = IMAGERASTER;			/* flip type of composite object */
                   6675: sp->size = size;			/* and set font size */
                   6676: /* -------------------------------------------------------------------------
                   6677: free unneeded component subrasters and return final result to caller
                   6678: -------------------------------------------------------------------------- */
                   6679: end_of_job:
                   6680:   return ( sp );
                   6681: } /* --- end-of-function rastdispmath() --- */
                   6682: 
                   6683: 
                   6684: /* ==========================================================================
                   6685:  * Function:	rastleft ( expression, size, basesp, ildelim, arg2, arg3 )
                   6686:  * Purpose:	\left...\right handler, returns a subraster corresponding to
                   6687:  *		delimited subexpression at font size
                   6688:  * --------------------------------------------------------------------------
                   6689:  * Arguments:	expression (I)	char **  to first char of null-terminated
                   6690:  *				string beginning with a \left
                   6691:  *				to be rasterized
                   6692:  *		size (I)	int containing 0-5 default font size
                   6693:  *		basesp (I)	subraster *  to character (or subexpression)
                   6694:  *				immediately preceding leading left{
                   6695:  *				(unused, but passed for consistency)
1.2       albertel 6696:  *		ildelim (I)	int containing ldelims[] index of
1.1       albertel 6697:  *				left delimiter
                   6698:  *		arg2 (I)	int unused
                   6699:  *		arg3 (I)	int unused
                   6700:  * --------------------------------------------------------------------------
                   6701:  * Returns:	( subraster * )	ptr to subraster corresponding to subexpr,
                   6702:  *				or NULL for any parsing error
                   6703:  * --------------------------------------------------------------------------
                   6704:  * Notes:     o
                   6705:  * ======================================================================= */
                   6706: /* --- entry point --- */
                   6707: subraster *rastleft ( char **expression, int size, subraster *basesp,
                   6708: 			int ildelim, int arg2, int arg3 )
                   6709: {
                   6710: /* -------------------------------------------------------------------------
                   6711: Allocations and Declarations
                   6712: -------------------------------------------------------------------------- */
                   6713: subraster *rasterize(), *sp=NULL;	/*rasterize between \left...\right*/
                   6714: subraster *get_delim(), *lp=NULL, *rp=NULL; /* left and right delim chars */
                   6715: subraster *rastlimits();		/*handle sub/super scripts on lp,rp*/
                   6716: subraster *rastcat();			/* concat lp||sp||rp subrasters */
                   6717: int	family=CMSYEX,			/* get_delim() family */
                   6718: 	height=0, rheight=0,		/* subexpr, right delim height */
                   6719: 	margin=(size+1),		/* delim height margin over subexpr*/
                   6720: 	opmargin=(5);			/* extra margin for \int,\sum,\etc */
1.3     ! albertel 6721: char	/* *texleft(),*/ subexpr[MAXSUBXSZ+1];/*chars between \left...\right*/
1.1       albertel 6722: char	*texchar(),			/* get delims after \left,\right */
                   6723: 	ldelim[256]=".", rdelim[256]="."; /* delims following \left,\right */
                   6724: char	*strtexchr(), *pleft, *pright;	/*locate \right matching our \left*/
                   6725: int	isleftdot=0, isrightdot=0;	/* true if \left. or \right. */
1.3     ! albertel 6726: int	isleftscript=0, isrightscript=0; /* true if delims are scripted */
1.1       albertel 6727: int	sublen=0;			/* strlen(subexpr) */
                   6728: int	idelim=0;			/* 1=left,2=right */
1.2       albertel 6729: /* int	gotldelim = 0; */		/* true if ildelim given by caller */
1.1       albertel 6730: int	delete_subraster();		/* free subraster if rastleft fails*/
                   6731: int	wasdisplaystyle = isdisplaystyle; /* save current displaystyle */
1.2       albertel 6732: int	istextleft=0, istextright=0;	/* true for non-displaystyle delims*/
1.1       albertel 6733: /* --- recognized delimiters --- */
                   6734: static	char left[16]="\\left", right[16]="\\right"; /* tex delimiters */
                   6735: static	char *ldelims[] = {
                   6736:    "unused", ".",			/* 1   for \left., \right. */
                   6737: 	"(", ")",			/* 2,3 for \left(, \right) */
                   6738: 	"\\{","\\}",			/* 4,5 for \left\{, \right\} */
                   6739: 	"[", "]",			/* 6,7 for \left[, \right] */
                   6740: 	"<", ">",			/* 8,9 for \left<, \right> */
                   6741: 	"|", "\\|",			/* 10,11 for \left,\right |,\|*/
                   6742: 	NULL };
                   6743: /* --- recognized operator delimiters --- */
                   6744: static	char *opdelims[] = {		/* operator delims from cmex10 */
                   6745:      "int",	  "sum",	"prod",
                   6746:      "cup",	  "cap",	"dot",
                   6747:      "plus",	  "times",	"wedge",
                   6748:      "vee",
                   6749:      NULL }; /* --- end-of-opdelims[] --- */
                   6750: /* --- delimiter xlation --- */
                   6751: static	char *xfrom[] =			/* xlate any delim suffix... */
                   6752:    { "\\|",				/* \| */
                   6753:      "\\{",				/* \{ */
                   6754:      "\\}",				/* \} */
                   6755:      "\\lbrace",			/* \lbrace */
                   6756:      "\\rbrace",			/* \rbrace */
                   6757:      "\\langle",			/* \langle */
                   6758:      "\\rangle",			/* \rangle */
                   6759:      NULL } ; /* --- end-of-xfrom[] --- */
                   6760: static	char *xto[] =			/* ...to this instead */
                   6761:    { "=",				/* \| to = */
                   6762:      "{",				/* \{ to { */
                   6763:      "}",				/* \} to } */
                   6764:      "{",				/* \lbrace to { */
                   6765:      "}",				/* \rbrace to } */
                   6766:      "<",				/* \langle to < */
                   6767:      ">",				/* \rangle to > */
                   6768:      NULL } ; /* --- end-of-xto[] --- */
1.2       albertel 6769: /* --- non-displaystyle delimiters --- */
                   6770: static	char *textdelims[] =		/* these delims _aren't_ display */
                   6771:    { "|", "=",
                   6772:      "(", ")",
                   6773:      "[", "]",
                   6774:      "<", ">",
                   6775:      "{", "}",
                   6776:      "dbl",				/* \lbrackdbl and \rbrackdbl */
                   6777:      NULL } ; /* --- end-of-textdelims[] --- */
1.1       albertel 6778: /* -------------------------------------------------------------------------
                   6779: initialization
                   6780: -------------------------------------------------------------------------- */
                   6781: /* --- check args --- */
                   6782: if ( *(*expression) == '\000' ) goto end_of_job; /* nothing after \left */
                   6783: /* --- determine left delimiter, and set default \right. delimiter --- */
                   6784: if ( ildelim!=NOVALUE && ildelim>=1 )	/* called with explicit left delim */
1.2       albertel 6785:  { strcpy(ldelim,ldelims[ildelim]);	/* so just get a local copy */
                   6786:    /* gotldelim = 1; */ }		/* and set flag that we got it */
1.1       albertel 6787: else					/* trapped \left without delim */
                   6788:  { skipwhite(*expression);		/* interpret \left ( as \left( */
1.2       albertel 6789:    if ( *(*expression) == '\000' )	/* end-of-string after \left */
                   6790:       goto end_of_job;			/* so return NULL */
                   6791:    *expression = texchar(*expression,ldelim); /*pull delim from expression*/
                   6792:    if ( *expression == NULL		/* probably invalid end-of-string */
                   6793:    ||   *ldelim == '\000' ) goto end_of_job; } /* no delimiter */
1.1       albertel 6794: strcpy(rdelim,".");			/* init default \right. delim */
                   6795: /* -------------------------------------------------------------------------
                   6796: locate \right balancing our opening \left
                   6797: -------------------------------------------------------------------------- */
                   6798: /* --- first \right following \left --- */
                   6799: if ( (pright=strtexchr(*expression,right)) /* look for \right after \left */
                   6800: !=   NULL ) {				/* found it */
                   6801:  /* --- find matching \right by pushing past any nested \left's --- */
                   6802:  pleft = *expression;			/* start after first \left( */
                   6803:  while ( 1 ) {				/*break when matching \right found*/
                   6804:   /* -- locate next nested \left if there is one --- */
                   6805:   if ( (pleft=strtexchr(pleft,left))	/* find next \left */
                   6806:   ==   NULL ) break;			/*no more, so matching \right found*/
                   6807:   pleft += strlen(left);		/* push ptr past \left token */
                   6808:   if ( pleft >= pright ) break;		/* not nested if \left after \right*/
                   6809:   /* --- have nested \left, so push forward to next \right --- */
                   6810:   if ( (pright=strtexchr(pright+strlen(right),right)) /* find next \right */
                   6811:   ==   NULL ) break;			/* ran out of \right's */
                   6812:   } /* --- end-of-while(1) --- */
                   6813:  } /* --- end-of-if(pright!=NULL) --- */
                   6814: /* -------------------------------------------------------------------------
                   6815: push past \left(_a^b sub/superscripts, if present
                   6816: -------------------------------------------------------------------------- */
                   6817: pleft = *expression;			/*reset pleft after opening \left( */
1.2       albertel 6818: if ( (lp=rastlimits(expression,size,lp)) /*dummy call push expression past b*/
                   6819: !=   NULL )				/* found actual _a^b scripts, too */
                   6820:   { delete_subraster(lp);		/* but we don't need them */
                   6821:     lp = NULL; }			/* reset pointer, too */
1.1       albertel 6822: /* -------------------------------------------------------------------------
                   6823: get \right delimiter and subexpression between \left...\right, xlate delims
                   6824: -------------------------------------------------------------------------- */
                   6825: /* --- get delimiter following \right --- */
                   6826: if ( pright == (char *)NULL ) {		/* assume \right. at end of exprssn*/
                   6827:   strcpy(rdelim,".");			/* set default \right. */
                   6828:   sublen = strlen(*expression);		/* use entire remaining expression */
                   6829:   memcpy(subexpr,*expression,sublen);	/* copy all remaining chars */
                   6830:   *expression += sublen; }		/* and push expression to its null */
                   6831: else {					/* have explicit matching \right */
                   6832:   sublen = (int)(pright-(*expression));	/* #chars between \left...\right */
                   6833:   memcpy(subexpr,*expression,sublen);	/* copy chars preceding \right */
                   6834:   *expression = pright+strlen(right);	/* push expression past \right */
                   6835:   skipwhite(*expression);		/* interpret \right ) as \right) */
                   6836:   *expression = texchar(*expression,rdelim); /*pull delim from expression*/
                   6837:   if ( *rdelim == '\000' ) strcpy(rdelim,"."); } /* \right. if no rdelim */
                   6838: /* --- get subexpression between \left...\right --- */
                   6839: if ( sublen < 1 ) goto end_of_job;	/* nothing between delimiters */
                   6840: subexpr[sublen] = '\000';		/* and null-terminate it */
1.2       albertel 6841: /* --- adjust margin for expressions containing \middle's --- */
                   6842: if ( strtexchr(subexpr,"\\middle") != NULL ) /* have enclosed \middle's */
                   6843:   margin = 1;				/* so don't "overwhelm" them */
1.1       albertel 6844: /* --- check for operator delimiter --- */
                   6845: for ( idelim=0; opdelims[idelim]!=NULL; idelim++ )
                   6846:   if ( strstr(ldelim,opdelims[idelim]) != NULL ) /* found operator */
                   6847:     { margin += opmargin;		/* extra height for operator */
                   6848:       if ( *ldelim == '\\' )		/* have leading escape */
                   6849: 	strcpy(ldelim,ldelim+1);	/* squeeze it out */
                   6850:       break; }				/* no need to check rest of table */
1.2       albertel 6851: /* --- xlate delimiters and check for textstyle --- */
1.1       albertel 6852: for ( idelim=1; idelim<=2; idelim++ ) {	/* 1=left, 2=right */
                   6853:   char	*lrdelim  = (idelim==1? ldelim:rdelim); /* ldelim or rdelim */
                   6854:   int	ix;  char *xdelim;		/* xfrom[] and xto[] index, delim */
                   6855:   for( ix=0; (xdelim=xfrom[ix]) != NULL; ix++ )
                   6856:     if ( strcmp(lrdelim,xdelim) == 0 )	/* found delim to xlate */
                   6857:       {	strcpy(lrdelim,xto[ix]);	/* replace with corresponding xto[]*/
                   6858: 	break; }			/* no need to check further */
1.2       albertel 6859:   for( ix=0; (xdelim=textdelims[ix]) != NULL; ix++ )
                   6860:     if ( strstr(lrdelim,xdelim) != 0 )	/* found textstyle delim */
                   6861:       {	if ( idelim == 1 )		/* if it's the \left one */
                   6862: 	  istextleft = 1;		/* set left textstyle flag */
                   6863: 	else istextright = 1;		/* else set right textstyle flag */
                   6864: 	break; }			/* no need to check further */
1.1       albertel 6865:   } /* --- end-of-for(idelim) --- */
                   6866: /* --- debugging --- */
                   6867: if ( msgfp!=NULL && msglevel>=99 )
                   6868:   fprintf(msgfp,"rastleft> left=\"%s\" right=\"%s\" subexpr=\"%s\"\n",
                   6869:   ldelim,rdelim,subexpr);
                   6870: /* -------------------------------------------------------------------------
                   6871: rasterize subexpression
                   6872: -------------------------------------------------------------------------- */
                   6873: /* --- rasterize subexpression --- */
                   6874: if ( (sp = rasterize(subexpr,size))	/* rasterize chars between delims */
                   6875: ==   NULL ) goto end_of_job;		/* quit if failed */
                   6876: height = (sp->image)->height;		/* height of subexpr raster */
                   6877: rheight = height+margin;		/*default rheight as subexpr height*/
                   6878: /* -------------------------------------------------------------------------
                   6879: rasterize delimiters, reset baselines, and add  sub/superscripts if present
                   6880: -------------------------------------------------------------------------- */
                   6881: /* --- check for dot delimiter --- */
                   6882: isleftdot  = (strchr(ldelim,'.')!=NULL); /* true if \left. */
                   6883: isrightdot = (strchr(rdelim,'.')!=NULL); /* true if \right. */
                   6884: /* --- get rasters for best-fit delim characters, add sub/superscripts --- */
1.2       albertel 6885: isdisplaystyle = (istextleft?0:9);	/* force \displaystyle */
1.1       albertel 6886: if ( !isleftdot )			/* if not \left. */
                   6887:  { /* --- first get requested \left delimiter --- */
                   6888:    lp = get_delim(ldelim,rheight,family); /* get \left delim char */
                   6889:    /* --- reset lp delim baseline to center delim on subexpr raster --- */
                   6890:    if ( lp != NULL )			/* if get_delim() succeeded */
                   6891:     { int lheight = (lp->image)->height; /* actual height of left delim */
                   6892:       lp->baseline = sp->baseline + (lheight - height)/2;
                   6893:       if ( lheight > rheight )		/* got bigger delim than requested */
                   6894: 	rheight = lheight-1; }		/* make sure right delim matches */
                   6895:    /* --- then add on any sub/superscripts attached to \left( --- */
1.3     ! albertel 6896:    lp = rastlimits(&pleft,size,lp);	/*\left(_a^b and push pleft past b*/
        !          6897:    isleftscript = isscripted; }		/* check if left delim scripted */
1.2       albertel 6898: isdisplaystyle = (istextright?0:9);	/* force \displaystyle */
1.1       albertel 6899: if ( !isrightdot )			/* and if not \right. */
                   6900:  { /* --- first get requested \right delimiter --- */
                   6901:    rp = get_delim(rdelim,rheight,family); /* get \right delim char */
                   6902:    /* --- reset rp delim baseline to center delim on subexpr raster --- */
                   6903:    if ( rp != NULL )			/* if get_delim() succeeded */
                   6904:      rp->baseline = sp->baseline + ((rp->image)->height - height)/2;
                   6905:    /* --- then add on any sub/superscripts attached to \right) --- */
1.3     ! albertel 6906:    rp = rastlimits(expression,size,rp);	/*\right)_c^d, expression past d*/
        !          6907:    isrightscript = isscripted; }	/* check if right delim scripted */
1.1       albertel 6908: isdisplaystyle = wasdisplaystyle;	/* original \displystyle default */
                   6909: /* --- check that we got delimiters --- */
                   6910: if ( 0 )
                   6911:  if ( (lp==NULL && !isleftdot)		/* check that we got left( */
                   6912:  ||   (rp==NULL && !isrightdot) )	/* and right) if needed */
                   6913:   { if ( lp != NULL ) free ((void *)lp); /* free \left-delim subraster */
                   6914:     if ( rp != NULL ) free ((void *)rp); /* and \right-delim subraster */
                   6915:     if (0) { delete_subraster(sp);	/* if failed, free subraster */
                   6916:              sp = (subraster *)NULL; }	/* signal error to caller */
                   6917:     goto end_of_job; }			/* and quit */
                   6918: /* -------------------------------------------------------------------------
                   6919: concat  lp || sp || rp  components
                   6920: -------------------------------------------------------------------------- */
                   6921: /* --- concat lp||sp||rp to obtain final result --- */
                   6922: if ( lp != NULL )			/* ignore \left. */
                   6923:   sp = rastcat(lp,sp,3);		/* concat lp||sp and free sp,lp */
                   6924: if ( sp != NULL )			/* succeeded or ignored \left. */
                   6925:   if ( rp != NULL )			/* ignore \right. */
                   6926:     sp = rastcat(sp,rp,3);		/* concat sp||rp and free sp,rp */
                   6927: /* --- back to caller --- */
                   6928: end_of_job:
1.3     ! albertel 6929:   isdelimscript = isrightscript;	/* signal if right delim scripted */
1.1       albertel 6930:   return ( sp );
                   6931: } /* --- end-of-function rastleft() --- */
                   6932: 
                   6933: 
                   6934: /* ==========================================================================
1.2       albertel 6935:  * Function:	rastright ( expression, size, basesp, ildelim, arg2, arg3 )
                   6936:  * Purpose:	...\right handler, intercepts an unexpected/unbalanced \right
                   6937:  * --------------------------------------------------------------------------
                   6938:  * Arguments:	expression (I)	char **  to first char of null-terminated
                   6939:  *				string beginning with a \right
                   6940:  *				to be rasterized
                   6941:  *		size (I)	int containing 0-5 default font size
                   6942:  *		basesp (I)	subraster *  to character (or subexpression)
                   6943:  *				immediately preceding leading left{
                   6944:  *				(unused, but passed for consistency)
                   6945:  *		ildelim (I)	int containing rdelims[] index of
                   6946:  *				right delimiter
                   6947:  *		arg2 (I)	int unused
                   6948:  *		arg3 (I)	int unused
                   6949:  * --------------------------------------------------------------------------
                   6950:  * Returns:	( subraster * )	ptr to subraster corresponding to subexpr,
                   6951:  *				or NULL for any parsing error
                   6952:  * --------------------------------------------------------------------------
                   6953:  * Notes:     o
                   6954:  * ======================================================================= */
                   6955: /* --- entry point --- */
                   6956: subraster *rastright ( char **expression, int size, subraster *basesp,
                   6957: 			int ildelim, int arg2, int arg3 )
                   6958: {
                   6959: /* -------------------------------------------------------------------------
                   6960: Allocations and Declarations
                   6961: -------------------------------------------------------------------------- */
                   6962: subraster /* *rasterize(),*/ *sp=NULL;	/*rasterize \right subexpr's*/
                   6963:   if ( sp != NULL )			/* returning entire expression */
                   6964:     {
                   6965:       isreplaceleft = 1;		/* set flag to replace left half*/
                   6966:     }
                   6967: return ( sp );
                   6968: } /* --- end-of-function rastright() --- */
                   6969: 
                   6970: 
                   6971: /* ==========================================================================
                   6972:  * Function:	rastmiddle ( expression, size, basesp,  arg1, arg2, arg3 )
                   6973:  * Purpose:	\middle handler, returns subraster corresponding to
                   6974:  *		entire expression with \middle delimiter(s) sized to fit.
                   6975:  * --------------------------------------------------------------------------
                   6976:  * Arguments:	expression (I/O) char **  to first char of null-terminated
                   6977:  *				string immediately following \middle to be
                   6978:  *				rasterized, and returning ptr immediately
                   6979:  *				to terminating null.
                   6980:  *		size (I)	int containing 0-5 default font size
                   6981:  *		basesp (I)	subraster *  to character (or subexpression)
                   6982:  *				immediately preceding \middle
                   6983:  *				(unused, but passed for consistency)
                   6984:  *		arg1 (I)	int unused
                   6985:  *		arg2 (I)	int unused
                   6986:  *		arg3 (I)	int unused
                   6987:  * --------------------------------------------------------------------------
                   6988:  * Returns:	( subraster * )	ptr to subraster corresponding to expression,
                   6989:  *				or NULL for any parsing error
                   6990:  *				(expression ptr unchanged if error occurs)
                   6991:  * --------------------------------------------------------------------------
                   6992:  * Notes:     o
                   6993:  * ======================================================================= */
                   6994: /* --- entry point --- */
                   6995: subraster *rastmiddle ( char **expression, int size, subraster *basesp,
                   6996: 			int arg1, int arg2, int arg3 )
                   6997: {
                   6998: /* -------------------------------------------------------------------------
                   6999: Allocations and Declarations
                   7000: -------------------------------------------------------------------------- */
                   7001: subraster *rasterize(), *sp=NULL, *subsp[32]; /*rasterize \middle subexpr's*/
                   7002: char	*exprptr = *expression,		/* local copy of ptr to expression */
                   7003: 	*texchar(), delim[32][132],	/* delimiters following \middle's */
                   7004: 	*strtexchr(),			/* locate \middle's */
1.3     ! albertel 7005: 	subexpr[MAXSUBXSZ+1], *subptr=NULL;/*subexpression between \middle's*/
1.2       albertel 7006: int	height=0, habove=0, hbelow=0;	/* height, above & below baseline */
                   7007: int	idelim, ndelims=0,		/* \middle count (max 32) */
                   7008: 	family = CMSYEX;		/* delims from CMSY10 or CMEX10 */
                   7009: subraster *subrastcpy(),		/* copy subraster */
                   7010: 	*rastcat(),			/* concatanate subraster */
                   7011: 	*get_delim();			/* get rasterized delimiter */
                   7012: int	delete_subraster();		/* free work area subsp[]'s at eoj */
                   7013: /* -------------------------------------------------------------------------
                   7014: initialization
                   7015: -------------------------------------------------------------------------- */
                   7016: subsp[0] = leftexpression;		/* expressn preceding 1st \middle */
                   7017: subsp[1] = NULL;			/* set first null */
                   7018: /* -------------------------------------------------------------------------
                   7019: accumulate subrasters between consecutive \middle\delim...\middle\delim...'s
                   7020: -------------------------------------------------------------------------- */
                   7021: while ( ndelims < 30 )			/* max of 31 \middle's */
                   7022:   {
                   7023:   /* --- maintain max height above,below baseline --- */
                   7024:   if ( subsp[ndelims] != NULL )		/*exprssn preceding current \middle*/
                   7025:    { int baseline = (subsp[ndelims])->baseline;  /* #rows above baseline */
                   7026:      height = ((subsp[ndelims])->image)->height; /* tot #rows (height) */
                   7027:      habove = max2(habove,baseline);	/* max #rows above baseline */
                   7028:      hbelow = max2(hbelow,height-baseline); } /* max #rows below baseline */
                   7029:   /* --- get delimter after \middle --- */
                   7030:   skipwhite(exprptr);			/*skip space betwn \middle & \delim*/
                   7031:   exprptr = texchar(exprptr,delim[ndelims]); /* \delim after \middle */
                   7032:   if ( *(delim[ndelims]) == '\000' )	/* \middle at end-of-expression */
                   7033:     break;				/* ignore it and consider job done */
                   7034:   ndelims++;				/* count another \middle\delim */
                   7035:   /* --- get subexpression between \delim and next \middle --- */
                   7036:   subsp[ndelims] = NULL;		/* no subexpresion yet */
                   7037:   if ( *exprptr == '\000' )		/* end-of-expression after \delim */
                   7038:     break;				/* so we have all subexpressions */
                   7039:   if ( (subptr = strtexchr(exprptr,"\\middle")) /* find next \middle */
                   7040:   ==   NULL )				/* no more \middle's */
1.3     ! albertel 7041:    { strncpy(subexpr,exprptr,MAXSUBXSZ); /*get entire remaining expression*/
        !          7042:      subexpr[MAXSUBXSZ] = '\000';	/* make sure it's null-terminated */
1.2       albertel 7043:      exprptr += strlen(exprptr); }	/* push exprptr to terminating '\0'*/
                   7044:   else					/* have another \middle */
                   7045:    { int sublen = (int)(subptr-exprptr); /* #chars between \delim...\middle*/
1.3     ! albertel 7046:      memcpy(subexpr,exprptr,min2(sublen,MAXSUBXSZ)); /* get subexpression */
        !          7047:      subexpr[min2(sublen,MAXSUBXSZ)] = '\000'; /* and null-terminate it */
1.2       albertel 7048:      exprptr += (sublen+strlen("\\middle")); } /* push exprptr past \middle*/
                   7049:   /* --- rasterize subexpression --- */
                   7050:   subsp[ndelims] = rasterize(subexpr,size); /* rasterize subexpresion */
                   7051:   } /* --- end-of-while(1) --- */
                   7052: /* -------------------------------------------------------------------------
                   7053: construct \middle\delim's and concatanate them between subexpressions
                   7054: -------------------------------------------------------------------------- */
                   7055: if ( ndelims < 1			/* no delims */
                   7056: ||   (height=habove+hbelow) < 1 )	/* or no subexpressions? */
                   7057:   goto end_of_job;			/* just flush \middle directive */
                   7058: for ( idelim=0; idelim<=ndelims; idelim++ )
                   7059:   {
                   7060:   /* --- first add on subexpression preceding delim --- */
1.3     ! albertel 7061:   if ( subsp[idelim] != NULL ) {	/* have subexpr preceding delim */
1.2       albertel 7062:     if ( sp == NULL )			/* this is first piece */
                   7063:      { sp = subsp[idelim];		/* so just use it */
                   7064:        if ( idelim == 0 ) sp = subrastcpy(sp); } /* or copy leftexpression */
1.3     ! albertel 7065:     else sp = rastcat(sp,subsp[idelim],(idelim>0?3:1)); } /* or concat it */
1.2       albertel 7066:   /* --- now construct delimiter --- */
                   7067:   if ( *(delim[idelim]) != '\000' )	/* have delimter */
                   7068:    { subraster *delimsp = get_delim(delim[idelim],height,family);
                   7069:      if ( delimsp != NULL )		/* rasterized delim */
                   7070:       {	delimsp->baseline = habove;	/* set baseline */
                   7071: 	if ( sp == NULL )		/* this is first piece */
                   7072: 	  sp = delimsp;			/* so just use it */
                   7073: 	else sp = rastcat(sp,delimsp,3); } } /*or concat to existing pieces*/
                   7074:   } /* --- end-of-for(idelim) --- */
                   7075: /* --- back to caller --- */
                   7076: end_of_job:
                   7077:   if ( 0 ) /* now handled above */
                   7078:     for ( idelim=1; idelim<=ndelims; idelim++ ) /* free subsp[]'s (not 0) */
                   7079:      if ( subsp[idelim] != NULL )	/* have allocated subraster */
                   7080:       delete_subraster(subsp[idelim]);	/* so free it */
                   7081:   if ( sp != NULL )			/* returning entire expression */
                   7082:     { int newht = (sp->image)->height;	/* height of returned subraster */
                   7083:       sp->baseline = min2(newht-1,newht/2+5); /* guess new baseline */
                   7084:       isreplaceleft = 1;		/* set flag to replace left half*/
                   7085:       *expression += strlen(*expression); } /* and push to terminating null*/
                   7086:   return ( sp );
                   7087: } /* --- end-of-function rastmiddle() --- */
                   7088: 
                   7089: 
                   7090: /* ==========================================================================
1.1       albertel 7091:  * Function:	rastflags ( expression, size, basesp,  flag, value, arg3 )
                   7092:  * Purpose:	sets an internal flag, e.g., for \rm, or sets an internal
                   7093:  *		value, e.g., for \unitlength=<value>, and returns NULL
                   7094:  *		so nothing is displayed
                   7095:  * --------------------------------------------------------------------------
                   7096:  * Arguments:	expression (I)	char **  to first char of null-terminated
                   7097:  *				LaTeX expression (unused/unchanged)
                   7098:  *		size (I)	int containing base font size (not used,
                   7099:  *				just stored in subraster)
                   7100:  *		basesp (I)	subraster *  to character (or subexpression)
1.2       albertel 7101:  *				immediately preceding "flags" directive
                   7102:  *				(unused but passed for consistency)
1.1       albertel 7103:  *		flag (I)	int containing #define'd symbol specifying
                   7104:  *				internal flag to be set
                   7105:  *		value (I)	int containing new value of flag
                   7106:  *		arg3 (I)	int unused
                   7107:  * --------------------------------------------------------------------------
                   7108:  * Returns:	( subraster * )	NULL so nothing is displayed
                   7109:  * --------------------------------------------------------------------------
                   7110:  * Notes:     o
                   7111:  * ======================================================================= */
                   7112: /* --- entry point --- */
                   7113: subraster *rastflags ( char **expression, int size, subraster *basesp,
                   7114: 			int flag, int value, int arg3 )
                   7115: {
                   7116: /* -------------------------------------------------------------------------
                   7117: Allocations and Declarations
                   7118: -------------------------------------------------------------------------- */
                   7119: char	*texsubexpr(),			/* parse expression for... */
                   7120: 	valuearg[1024]="NOVALUE";	/* value from expression, if needed */
                   7121: int	argvalue=NOVALUE,		/* atoi(valuearg) */
                   7122: 	isdelta=0,			/* true if + or - precedes valuearg */
                   7123: 	valuelen=0;			/* strlen(valuearg) */
1.3     ! albertel 7124: double	dblvalue=(-99.), strtod();	/*convert ascii {valuearg} to double*/
1.1       albertel 7125: static	int displaystylelevel = (-99);	/* \displaystyle set at recurlevel */
                   7126: /* -------------------------------------------------------------------------
                   7127: set flag or value
                   7128: -------------------------------------------------------------------------- */
                   7129: switch ( flag )
                   7130:   {
                   7131:   default: break;			/* unrecognized flag */
1.2       albertel 7132:   case ISFONTFAM:
1.1       albertel 7133:     if ( isthischar((*(*expression)),WHITEMATH) ) /* \rm followed by white */
                   7134:       (*expression)++;			/* skip leading ~ after \rm */
1.2       albertel 7135:     fontnum = value;			/* set font family */
1.1       albertel 7136:     break;
                   7137:   case ISSTRING: isstring=value; break;	/* set string/image mode */
                   7138:   case ISDISPLAYSTYLE:			/* set \displaystyle mode */
                   7139:     displaystylelevel = recurlevel;	/* \displaystyle set at recurlevel */
                   7140:     isdisplaystyle=value; break;
                   7141:   case ISOPAQUE:  istransparent=value; break; /* set transparent/opaque */
                   7142:   case ISREVERSE:			/* reverse video */
                   7143:     if ( value==1 || value==NOVALUE )
                   7144:       {	fgred=255-fgred; fggreen=255-fggreen; fgblue=255-fgblue; }
                   7145:     if ( value==2 || value==NOVALUE )
                   7146:       {	bgred=255-bgred; bggreen=255-bggreen; bgblue=255-bgblue; }
                   7147:     if ( value==2 || value==NOVALUE )
                   7148:       isblackonwhite = !isblackonwhite;
1.3     ! albertel 7149:     if ( gammacorrection > 0.0001 )	/* have gamma correction */
        !          7150:       gammacorrection = REVERSEGAMMA;	/* use reverse video gamma instead */
1.1       albertel 7151:     break;
                   7152:   case ISSUPER:				/* set supersampling/lowpass flag */
                   7153:     #ifndef SSFONTS			/* don't have ss fonts loaded */
                   7154:       value = 0;			/* so force lowpass */
                   7155:     #endif
                   7156:     isss = issupersampling = value;
                   7157:     fonttable = (issupersampling?ssfonttable:aafonttable); /* set fonts */
                   7158:     break;
                   7159:   case ISFONTSIZE:			/* set fontsize */
                   7160:   case ISDISPLAYSIZE:			/* set displaysize */
1.3     ! albertel 7161:   case ISCONTENTTYPE:			/*enable/disable content-type lines*/
1.1       albertel 7162:   case ISSHRINK:			/* set shrinkfactor */
                   7163:   case ISAAALGORITHM:			/* set anti-aliasing algorithm */
                   7164:   case ISWEIGHT:			/* set font weight */
                   7165:   case ISCENTERWT:			/* set lowpass center pixel weight */
                   7166:   case ISADJACENTWT:			/* set lowpass adjacent weight */
                   7167:   case ISCORNERWT:			/* set lowpass corner weight */
                   7168:   case ISCOLOR:				/* set red(1),green(2),blue(3) */
1.2       albertel 7169:   case ISSMASH:				/* set (minimum) "smash" margin */
1.3     ! albertel 7170:   case ISGAMMA:				/* set gamma correction */
1.1       albertel 7171:     if ( value != NOVALUE )		/* passed a fixed value to be set */
1.3     ! albertel 7172:       {	argvalue = value;		/* set given fixed int value */
        !          7173: 	dblvalue = (double)value; }	/* or maybe interpreted as double */
1.1       albertel 7174:     else				/* get value from expression */
                   7175:       {	*expression = texsubexpr(*expression,valuearg,1023,"{","}",0,0);
                   7176: 	if ( *valuearg != '\000' )	/* guard against empty string */
                   7177: 	 if ( !isalpha(*valuearg) )	/* and against alpha string args */
                   7178: 	  if ( !isthischar(*valuearg,"?") ) /*leading ? is query for value*/
                   7179: 	   { isdelta = isthischar(*valuearg,"+-"); /* leading + or - */
                   7180: 	     if ( memcmp(valuearg,"--",2) == 0 ) /* leading -- signals...*/
                   7181: 	       { isdelta=0; strcpy(valuearg,valuearg+1); } /* ...not delta */
1.3     ! albertel 7182: 	     switch ( flag ) {		/* convert to double or int */
        !          7183: 	      default: argvalue = atoi(valuearg); break; /* convert to int */
        !          7184: 	      case ISGAMMA:
        !          7185: 		dblvalue = strtod(valuearg,NULL); break; } /* or to double */
        !          7186: 	   } /* --- end-of-if(*valuearg!='?') --- */
        !          7187:       } /* --- end-of-if(value==NOVALUE) --- */
1.1       albertel 7188:     switch ( flag )
                   7189:       {
                   7190:       default: break;
                   7191:       case ISCOLOR:			/* set color */
                   7192: 	slower(valuearg);		/* convert arg to lower case */
                   7193: 	if ( argvalue==1 || strstr(valuearg,"red") )
                   7194: 	  { fggreen = fgblue = (isblackonwhite?0:255);
                   7195: 	    fgred = (isblackonwhite?255:0); }
                   7196: 	if ( argvalue==2 || strstr(valuearg,"green") )
                   7197: 	  { fgred = fgblue = (isblackonwhite?0:255);
                   7198: 	    fggreen = (isblackonwhite?255:0); }
                   7199: 	if ( argvalue==3 || strstr(valuearg,"blue") )
                   7200: 	  { fgred = fggreen = (isblackonwhite?0:255);
                   7201: 	    fgblue = (isblackonwhite?255:0); }
                   7202: 	if ( argvalue==0 || strstr(valuearg,"black") )
                   7203: 	    fgred = fggreen = fgblue = (isblackonwhite?0:255);
                   7204: 	if ( argvalue==7 || strstr(valuearg,"white") )
                   7205: 	    fgred = fggreen = fgblue = (isblackonwhite?255:0);
                   7206: 	break;
                   7207:       case ISFONTSIZE:			/* set fontsize */
                   7208: 	if ( argvalue != NOVALUE )	/* got a value */
                   7209: 	  { int largestsize = (issupersampling?16:LARGESTSIZE);
                   7210: 	    fontsize = (isdelta? fontsize+argvalue : argvalue);
                   7211: 	    fontsize = max2(0,min2(fontsize,largestsize));
                   7212: 	    shrinkfactor = shrinkfactors[fontsize];
1.2       albertel 7213: 	    if ( isdisplaystyle == 1	/* displaystyle enabled but not set*/
                   7214: 	    ||  (1 && isdisplaystyle==2) /* displaystyle enabled and set */
                   7215: 	    ||  (0 && isdisplaystyle==0) )/*\textstyle disabled displaystyle*/
1.1       albertel 7216: 	     if ( displaystylelevel != recurlevel ) /*respect \displaystyle*/
1.3     ! albertel 7217: 	      if ( !ispreambledollars )	{ /* respect $$...$$'s */
1.2       albertel 7218: 	       if ( fontsize >= displaysize )
                   7219: 		isdisplaystyle = 2;	/* forced */
1.3     ! albertel 7220: 	       else isdisplaystyle = 1; }
1.1       albertel 7221: 	    /*displaystylelevel = (-99);*/ } /* reset \displaystyle level */
                   7222: 	else				/* embed font size in expression */
                   7223: 	  { sprintf(valuearg,"%d",fontsize); /* convert size */
                   7224: 	    valuelen = strlen(valuearg); /* ought to be 1 */
                   7225: 	    if ( *expression != '\000' ) /* ill-formed expression */
                   7226: 	     { *expression = (char *)(*expression-valuelen); /*back up buff*/
                   7227: 	       memcpy(*expression,valuearg,valuelen); } } /*and put in size*/
                   7228: 	break;
                   7229:       case ISDISPLAYSIZE:		/* set displaysize */
                   7230: 	if ( argvalue != NOVALUE )	/* got a value */
                   7231: 	    displaysize = (isdelta? displaysize+argvalue : argvalue);
                   7232: 	break;
1.3     ! albertel 7233:       case ISCONTENTTYPE:		/*enable/disable content-type lines*/
        !          7234: 	if ( argvalue != NOVALUE )	/* got a value */
        !          7235: 	    isemitcontenttype = (argvalue>0?1:0);
        !          7236: 	break;
1.2       albertel 7237:       case ISSMASH:			/* set (minimum) "smash" margin */
1.1       albertel 7238: 	if ( argvalue != NOVALUE )	/* got a value */
1.2       albertel 7239: 	  { smashmargin = argvalue;	/* set value */
1.1       albertel 7240: 	    if ( arg3 != NOVALUE ) isdelta=arg3; /* hard-coded isdelta */
1.2       albertel 7241: 	    issmashdelta = (isdelta?1:0); } /* and set delta flag */
                   7242: 	smashmargin = max2((isdelta?-5:0),min2(smashmargin,32)); /*sanity*/
1.3     ! albertel 7243: 	isexplicitsmash = 1;		/* signal explicit \smash directive*/
1.1       albertel 7244: 	break;
                   7245:       case ISSHRINK:			/* set shrinkfactor */
                   7246: 	if ( argvalue != NOVALUE )	/* got a value */
                   7247: 	  shrinkfactor = (isdelta? shrinkfactor+argvalue : argvalue);
                   7248: 	shrinkfactor = max2(1,min2(shrinkfactor,27)); /* sanity check */
                   7249: 	break;
                   7250:       case ISAAALGORITHM:		/* set anti-aliasing algorithm */
1.3     ! albertel 7251: 	if ( argvalue != NOVALUE ) {	/* got a value */
        !          7252: 	  if ( argvalue >= 0 ) {	/* non-negative to set algorithm */
        !          7253: 	      aaalgorithm = argvalue;	/* set algorithm number */
        !          7254: 	    aaalgorithm = max2(0,min2(aaalgorithm,4)); } /* bounds check */
        !          7255: 	  else maxfollow = abs(argvalue); } /* or maxfollow=abs(negative#) */
1.1       albertel 7256: 	break;
                   7257:       case ISWEIGHT:			/* set font weight number */
                   7258: 	value =	(argvalue==NOVALUE? NOVALUE : /* don't have a value */
                   7259: 		(isdelta? weightnum+argvalue : argvalue));
                   7260: 	if ( value>=0 && value<maxaaparams ) /* in range */
                   7261: 	  { weightnum   = value;	/* reset weightnum index */
                   7262: 	    minadjacent = aaparams[weightnum].minadjacent;
                   7263: 	    maxadjacent = aaparams[weightnum].maxadjacent;
                   7264: 	    cornerwt    = aaparams[weightnum].cornerwt;
                   7265: 	    adjacentwt  = aaparams[weightnum].adjacentwt;
                   7266: 	    centerwt    = aaparams[weightnum].centerwt;
                   7267: 	    fgalias     = aaparams[weightnum].fgalias;
                   7268: 	    fgonly      = aaparams[weightnum].fgonly;
                   7269: 	    bgalias     = aaparams[weightnum].bgalias;
                   7270: 	    bgonly      = aaparams[weightnum].bgonly; }
                   7271: 	break;
                   7272:       case ISCENTERWT:			/* set lowpass center pixel weight */
                   7273: 	if ( argvalue != NOVALUE )	/* got a value */
                   7274: 	  centerwt = argvalue;		/* set lowpass center weight */
                   7275: 	break;
                   7276:       case ISADJACENTWT:		/* set lowpass adjacent weight */
                   7277: 	if ( argvalue != NOVALUE )	/* got a value */
                   7278: 	  adjacentwt = argvalue;	/* set lowpass adjacent weight */
                   7279: 	break;
                   7280:       case ISCORNERWT:			/* set lowpass corner weight */
                   7281: 	if ( argvalue != NOVALUE )	/* got a value */
                   7282: 	  cornerwt = argvalue;		/* set lowpass corner weight */
                   7283: 	break;
1.3     ! albertel 7284:       case ISGAMMA:			/* set gamma correction */
        !          7285: 	if ( dblvalue >= 0.0 )		/* got a value */
        !          7286: 	  gammacorrection = dblvalue;	/* set gamma correction */
        !          7287: 	break;
1.1       albertel 7288:       } /* --- end-of-switch() --- */
                   7289:     break;
                   7290:   case PNMPARAMS:			/*set fgalias,fgonly,bgalias,bgonly*/
                   7291:     *expression = texsubexpr(*expression,valuearg,1023,"{","}",0,0);
                   7292:     valuelen = strlen(valuearg);	/* ought to be 1-4 */
                   7293:     if ( valuelen>0 && isthischar(toupper(valuearg[0]),"TY1") ) fgalias=1;
                   7294:     if ( valuelen>0 && isthischar(toupper(valuearg[0]),"FN0") ) fgalias=0;
                   7295:     if ( valuelen>1 && isthischar(toupper(valuearg[1]),"TY1") ) fgonly =1;
                   7296:     if ( valuelen>1 && isthischar(toupper(valuearg[1]),"FN0") ) fgonly =0;
                   7297:     if ( valuelen>2 && isthischar(toupper(valuearg[2]),"TY1") ) bgalias=1;
                   7298:     if ( valuelen>2 && isthischar(toupper(valuearg[2]),"FN0") ) bgalias=0;
                   7299:     if ( valuelen>3 && isthischar(toupper(valuearg[3]),"TY1") ) bgonly =1;
                   7300:     if ( valuelen>3 && isthischar(toupper(valuearg[3]),"FN0") ) bgonly =0;
                   7301:     break;
                   7302:   case UNITLENGTH:
                   7303:     if ( value != NOVALUE )		/* passed a fixed value to be set */
                   7304: 	unitlength = (double)(value);	/* set given fixed value */
                   7305:     else				/* get value from expression */
                   7306:       {	*expression = texsubexpr(*expression,valuearg,1023,"{","}",0,0);
                   7307: 	if ( *valuearg != '\000' )	/* guard against empty string */
                   7308: 	  unitlength = strtod(valuearg,NULL); } /* convert to double */
                   7309:     break;
                   7310:   } /* --- end-of-switch(flag) --- */
                   7311: return ( NULL );			/*just set value, nothing to display*/
                   7312: } /* --- end-of-function rastflags() --- */
                   7313: 
                   7314: 
                   7315: /* ==========================================================================
                   7316:  * Function:	rastspace(expression, size, basesp,  width, isfill, isheight)
                   7317:  * Purpose:	returns a blank/space subraster width wide,
                   7318:  *		with baseline and height corresponding to basep
                   7319:  * --------------------------------------------------------------------------
                   7320:  * Arguments:	expression (I)	char **  to first char of null-terminated
                   7321:  *				LaTeX expression (unused/unchanged)
                   7322:  *		size (I)	int containing base font size (not used,
                   7323:  *				just stored in subraster)
                   7324:  *		basesp (I)	subraster *  to character (or subexpression)
                   7325:  *				immediately preceding space, whose baseline
                   7326:  *				and height params are transferred to space
                   7327:  *		width (I)	int containing #bits/pixels for space width
                   7328:  *		isfill (I)	int containing true to \hfill complete
                   7329:  *				expression out to width
1.3     ! albertel 7330:  *				(Kludge: isfill=99 signals \hspace*
        !          7331:  *				for negative space)
1.1       albertel 7332:  *		isheight (I)	int containing true (but not NOVALUE)
                   7333:  *				to treat width arg as height
                   7334:  * --------------------------------------------------------------------------
                   7335:  * Returns:	( subraster * )	ptr to empty/blank subraster
                   7336:  *				or NULL for any error
                   7337:  * --------------------------------------------------------------------------
                   7338:  * Notes:     o
                   7339:  * ======================================================================= */
                   7340: /* --- entry point --- */
                   7341: subraster *rastspace ( char **expression, int size, subraster *basesp,
                   7342: 			int width, int isfill, int isheight )
                   7343: {
                   7344: /* -------------------------------------------------------------------------
                   7345: Allocations and Declarations
                   7346: -------------------------------------------------------------------------- */
                   7347: subraster *new_subraster(), *spacesp=NULL; /* subraster for space */
1.3     ! albertel 7348: raster	*bp=NULL, *backspace_raster();	/* for negative space */
        !          7349: int	delete_subraster();		/* if fail, free unneeded subraster*/
1.1       albertel 7350: int	baseht=1, baseln=0;		/* height,baseline of base symbol */
                   7351: int	pixsz = 1;			/*default #bits per pixel, 1=bitmap*/
1.3     ! albertel 7352: int	isstar=0, minspace=0;		/* defaults for negative hspace */
1.1       albertel 7353: char	*texsubexpr(), widtharg[256];	/* parse for optional {width} */
                   7354: subraster *rasterize(), *rightsp=NULL;	/*rasterize right half of expression*/
                   7355: subraster *rastcat();			/* cat rightsp after \hfill */
                   7356: /* -------------------------------------------------------------------------
                   7357: initialization
                   7358: -------------------------------------------------------------------------- */
1.3     ! albertel 7359: if ( isfill > 1 ) { isstar=1; isfill=0; } /* large fill signals \hspace* */
1.1       albertel 7360: if ( isfill == NOVALUE ) isfill=0;	/* novalue means false */
                   7361: if ( isheight == NOVALUE ) isheight=0;	/* novalue means false */
1.3     ! albertel 7362: minspace = (isstar?(-1):0);		/* reset default minspace */
1.1       albertel 7363: /* -------------------------------------------------------------------------
                   7364: determine width if not given (e.g., \hspace{width}, \hfill{width})
                   7365: -------------------------------------------------------------------------- */
1.3     ! albertel 7366: if ( width == 0 ) {			/* width specified in expression */
        !          7367:   double dwidth;  int widthval;		/* test {width} before using it */
        !          7368:   int minwidth = (isfill||isheight?1:-600); /* \hspace allows negative */
        !          7369:   /* --- check if optional [minspace] given for negative \hspace --- */
        !          7370:   if ( *(*expression) == '[' ) {	/* [minspace] if leading char is [ */
        !          7371:     /* ---parse [minspace], bump expression past it, interpret as double--- */
        !          7372:     *expression = texsubexpr(*expression,widtharg,127,"[","]",0,0);
        !          7373:     if ( *widtharg != '\000' )		/* got [minspace] */
        !          7374:       minspace = iround(unitlength*strtod(widtharg,NULL)); /* in pixels */
        !          7375:     } /* --- end-of-if(*(*expression)=='[') --- */
        !          7376:   width = 1;				/* set default width */
        !          7377:   *expression = texsubexpr(*expression,widtharg,255,"{","}",0,0);
        !          7378:   dwidth = unitlength*strtod(widtharg,NULL); /* scaled width value */
        !          7379:   widthval =				/* convert {width} to integer */
        !          7380: 		(int)( dwidth + (dwidth>=0.0?0.5:(-0.5)) );
        !          7381:   if ( widthval>=minwidth && widthval<=600 ) /* sanity check */
        !          7382:     width = widthval;			/* replace deafault width */
        !          7383:   } /* --- end-of-if(width==0) --- */
        !          7384: /* -------------------------------------------------------------------------
        !          7385: first check for negative space
        !          7386: -------------------------------------------------------------------------- */
        !          7387: if ( width < 0 ) {			/* have negative hspace */
        !          7388:  if ( leftexpression != (subraster *)NULL ) /* can't backspace */
        !          7389:   if ( (spacesp=new_subraster(0,0,0))	/* get new subraster for backspace */
        !          7390:   !=   NULL ) {				/* and if we succeed... */
        !          7391:    int nback=(-width), pback;		/*#pixels wanted,actually backspaced*/
        !          7392:    if ( (bp=backspace_raster(leftexpression->image,nback,&pback,minspace,0))
        !          7393:    !=    NULL ) {			/* and if backspace succeeds... */
        !          7394:      spacesp->image = bp;		/* save backspaced image */
        !          7395:      /*spacesp->type = leftexpression->type;*/ /* copy original type */
        !          7396:      spacesp->type = blanksignal;	/* need to propagate blanks */
        !          7397:      spacesp->size = leftexpression->size; /* copy original font size */
        !          7398:      spacesp->baseline = leftexpression->baseline; /* and baseline */
        !          7399:      blanksymspace += -(nback-pback);	/* wanted more than we got */
        !          7400:      isreplaceleft = 1; }		/*signal to replace entire expressn*/
        !          7401:    else {				/* backspace failed */
        !          7402:      delete_subraster(spacesp);		/* free unneeded envelope */
        !          7403:      spacesp = (subraster *)NULL; } }	/* and signal failure */
        !          7404:  goto end_of_job;
        !          7405:  } /* --- end-of-if(width<0) --- */
1.1       albertel 7406: /* -------------------------------------------------------------------------
                   7407: see if width is "absolute" or fill width
                   7408: -------------------------------------------------------------------------- */
                   7409: if ( isfill				/* called as \hfill{} */
                   7410: &&   !isheight )			/* parameter conflict */
                   7411:  { if ( leftexpression != NULL )	/* if we have left half */
                   7412:     width -= (leftexpression->image)->width; /*reduce left width from total*/
                   7413:    if ( (rightsp=rasterize(*expression,size)) /* rasterize right half */
                   7414:    != NULL )				/* succeeded */
                   7415:     width -= (rightsp->image)->width; } /* reduce right width from total */
                   7416: /* -------------------------------------------------------------------------
                   7417: construct blank subraster, and return it to caller
                   7418: -------------------------------------------------------------------------- */
                   7419: /* --- get parameters from base symbol --- */
                   7420: if ( basesp != (subraster *)NULL )	/* we have base symbol for space */
                   7421:   { baseht = (basesp->image)->height; 	/* height of base symbol */
                   7422:     baseln =  basesp->baseline; }	/* and its baseline */
                   7423: /* --- flip params for height --- */
                   7424: if ( isheight )				/* width is actually height */
                   7425:   { baseht = width;			/* use given width as height */
                   7426:     width = 1; }			/* and set default width */
                   7427: /* --- generate and init space subraster --- */
                   7428: if ( width > 0 )			/*make sure we have positive width*/
                   7429:  if ( (spacesp=new_subraster(width,baseht,pixsz)) /*generate space subraster*/
                   7430:  !=   NULL )				/* and if we succeed... */
                   7431:   { /* --- ...re-init subraster parameters --- */
                   7432:     spacesp->size = size;		/*propagate base font size forward*/
1.3     ! albertel 7433:     if(1)spacesp->type = blanksignal;	/* need to propagate blanks (???) */
1.1       albertel 7434:     spacesp->baseline = baseln; }	/* ditto baseline */
                   7435: /* -------------------------------------------------------------------------
                   7436: concat right half if \hfill-ing
                   7437: -------------------------------------------------------------------------- */
                   7438: if ( rightsp != NULL )			/* we have a right half after fill */
                   7439:   { spacesp = (spacesp==NULL? rightsp:	/* no space, so just use right half*/
                   7440: 	rastcat(spacesp,rightsp,3));	/* or cat right half after space */
                   7441:     spacesp->type = blanksignal;	/* need to propagate blanks */
                   7442:     *expression += strlen((*expression)); } /* push expression to its null */
1.3     ! albertel 7443: end_of_job:
        !          7444:   return ( spacesp );
1.1       albertel 7445: } /* --- end-of-function rastspace() --- */
                   7446: 
                   7447: 
                   7448: /* ==========================================================================
                   7449:  * Function:	rastnewline ( expression, size, basesp,  arg1, arg2, arg3 )
                   7450:  * Purpose:	\\ handler, returns subraster corresponding to
                   7451:  *		left-hand expression preceding \\ above right-hand expression
                   7452:  * --------------------------------------------------------------------------
                   7453:  * Arguments:	expression (I/O) char **  to first char of null-terminated
                   7454:  *				string immediately following \\ to be
                   7455:  *				rasterized, and returning ptr immediately
                   7456:  *				to terminating null.
1.2       albertel 7457:  *		size (I)	int containing 0-5 default font size
1.1       albertel 7458:  *		basesp (I)	subraster *  to character (or subexpression)
1.2       albertel 7459:  *				immediately preceding \\
1.1       albertel 7460:  *				(unused, but passed for consistency)
                   7461:  *		arg1 (I)	int unused
                   7462:  *		arg2 (I)	int unused
                   7463:  *		arg3 (I)	int unused
                   7464:  * --------------------------------------------------------------------------
                   7465:  * Returns:	( subraster * )	ptr to subraster corresponding to expression,
                   7466:  *				or NULL for any parsing error
                   7467:  *				(expression ptr unchanged if error occurs)
                   7468:  * --------------------------------------------------------------------------
                   7469:  * Notes:     o
                   7470:  * ======================================================================= */
                   7471: /* --- entry point --- */
                   7472: subraster *rastnewline ( char **expression, int size, subraster *basesp,
                   7473: 			int arg1, int arg2, int arg3 )
                   7474: {
                   7475: /* -------------------------------------------------------------------------
                   7476: Allocations and Declarations
                   7477: -------------------------------------------------------------------------- */
                   7478: subraster *rastack(), *newlsp=NULL;	/* subraster for both lines */
                   7479: subraster *rasterize(), *rightsp=NULL;	/*rasterize right half of expression*/
                   7480: char	*texsubexpr(), spacexpr[129]/*, *xptr=spacexpr*/; /*for \\[vspace]*/
                   7481: double	strtod();			/* convert ascii param to double */
                   7482: int	vspace = size+2;		/* #pixels between lines */
                   7483: /* -------------------------------------------------------------------------
                   7484: obtain optional [vspace] argument immediately following \\ command
                   7485: -------------------------------------------------------------------------- */
                   7486: /* --- check if [vspace] given --- */
                   7487: if ( *(*expression) == '[' )		/*have [vspace] if leading char is [*/
                   7488:   {
                   7489:   /* ---parse [vspace] and bump expression past it, interpret as double--- */
                   7490:   *expression = texsubexpr(*expression,spacexpr,127,"[","]",0,0);
                   7491:   if ( *spacexpr == '\000' ) goto end_of_job; /* couldn't get [vspace] */
                   7492:   vspace = iround(unitlength*strtod(spacexpr,NULL)); /* vspace in pixels */
                   7493:   } /* --- end-of-if(*(*expression)=='[') --- */
                   7494: if ( leftexpression == NULL ) goto end_of_job; /* nothing preceding \\ */
                   7495: /* -------------------------------------------------------------------------
                   7496: rasterize right half of expression and stack left half above it
                   7497: -------------------------------------------------------------------------- */
                   7498: /* --- rasterize right half --- */
                   7499: if ( (rightsp=rasterize(*expression,size)) /* rasterize right half */
                   7500: == NULL ) goto end_of_job;		/* quit if failed */
                   7501: /* --- stack left half above it --- */
1.2       albertel 7502: /*newlsp = rastack(rightsp,leftexpression,1,vspace,0,3);*//*right under left*/
                   7503: newlsp = rastack(rightsp,leftexpression,1,vspace,0,1); /*right under left*/
1.1       albertel 7504: /* --- back to caller --- */
                   7505: end_of_job:
                   7506:   if ( newlsp != NULL )			/* returning entire expression */
                   7507:     { int newht = (newlsp->image)->height; /* height of returned subraster */
                   7508:       newlsp->baseline = min2(newht-1,newht/2+5); /* guess new baseline */
                   7509:       isreplaceleft = 1;		/* so set flag to replace left half*/
                   7510:       *expression += strlen(*expression); } /* and push to terminating null*/
                   7511:   return ( newlsp );			/* 1st line over 2nd, or null=error*/
                   7512: } /* --- end-of-function rastnewline() --- */
                   7513: 
                   7514: 
                   7515: /* ==========================================================================
                   7516:  * Function:	rastarrow ( expression, size, basesp,  drctn, isBig, arg3 )
                   7517:  * Purpose:	returns left/right arrow subraster (e.g., for \longrightarrow)
                   7518:  * --------------------------------------------------------------------------
                   7519:  * Arguments:	expression (I)	char **  to first char of null-terminated
                   7520:  *				LaTeX expression (unused/unchanged)
                   7521:  *		size (I)	int containing base font size (not used,
                   7522:  *				just stored in subraster)
                   7523:  *		basesp (I)	subraster *  to character (or subexpression)
                   7524:  *				immediately preceding space, whose baseline
                   7525:  *				and height params are transferred to space
                   7526:  *		drctn (I)	int containing +1 for right, -1 for left,
                   7527:  *				or 0 for leftright
                   7528:  *		isBig (I)	int containing 0 for ---> or 1 for ===>
                   7529:  *		arg3 (I)	int unused
                   7530:  * --------------------------------------------------------------------------
                   7531:  * Returns:	( subraster * )	ptr to left/right arrow subraster
                   7532:  *				or NULL for any error
                   7533:  * --------------------------------------------------------------------------
                   7534:  * Notes:     o	An optional argument [width] may *immediately* follow
                   7535:  *		the \longxxx to explicitly set the arrow's width in pixels.
                   7536:  *		For example, \longrightarrow calculates a default width
                   7537:  *		(as usual in LaTeX), whereas \longrightarrow[50] explicitly
                   7538:  *		draws a 50-pixel long arrow.  This can be used, e.g.,
                   7539:  *		to draw commutative diagrams in conjunction with
                   7540:  *		\array (and maybe with \stackrel and/or \relstack, too).
                   7541:  *	      o	In case you really want to render, say, [f]---->[g], just
                   7542:  *		use an intervening space, i.e., [f]\longrightarrow~[g].
                   7543:  *		In text mode use two spaces {\rm~[f]\longrightarrow~~[g]}.
                   7544:  * ======================================================================= */
                   7545: /* --- entry point --- */
                   7546: subraster *rastarrow ( char **expression, int size, subraster *basesp,
                   7547: 			int drctn, int isBig, int arg3 )
                   7548: {
                   7549: /* -------------------------------------------------------------------------
                   7550: Allocations and Declarations
                   7551: -------------------------------------------------------------------------- */
                   7552: subraster *arrow_subraster(), *arrowsp=NULL; /* subraster for arrow */
                   7553: char	*texsubexpr(), widtharg[256];	/* parse for optional [width] */
                   7554: char	*texscripts(), sub[1024],super[1024]; /* and _^limits after [width]*/
                   7555: subraster *rasterize(), *subsp=NULL,*supsp=NULL; /*rasterize limits*/
                   7556: subraster *new_subraster(), *rastack(), *spacesp=NULL; /*space below arrow*/
                   7557: int	delete_subraster();		/*free work areas in case of error*/
                   7558: double	strtod();			/* convert ascii [width] to value */
                   7559: int	width = 10 + 8*size,  height;	/* width, height for \longxxxarrow */
                   7560: int	islimits = 1;			/*true to handle limits internally*/
                   7561: int	limsize = size-1;		/* font size for limits */
                   7562: int	vspace = 1;			/* #empty rows below arrow */
                   7563: int	pixsz = 1;			/*default #bits per pixel, 1=bitmap*/
                   7564: /* -------------------------------------------------------------------------
                   7565: construct longleft/rightarrow subraster, with limits, and return it to caller
                   7566: -------------------------------------------------------------------------- */
                   7567: /* --- check for optional width arg and replace default width --- */
                   7568: if ( *(*expression) == '[' )		/*check for []-enclosed optional arg*/
                   7569:   { int widthval;			/* test [width] before using it */
                   7570:     *expression = texsubexpr(*expression,widtharg,255,"[","]",0,0);
                   7571:     widthval =				/* convert [width] to integer */
                   7572: 		(int)((unitlength*strtod(widtharg,NULL))+0.5);
                   7573:     if ( widthval>=2 && widthval<=600 )	/* sanity check */
                   7574:       width = widthval; }		/* replace deafault width */
                   7575: /* --- now parse for limits, and bump expression past it(them) --- */
                   7576: if ( islimits )				/* handling limits internally */
                   7577:   { *expression = texscripts(*expression,sub,super,3); /* parse for limits */
                   7578:     if ( *sub != '\000' )		/*have a subscript following arrow*/
                   7579:       subsp = rasterize(sub,limsize);	/* so try to rasterize subscript */
                   7580:     if ( *super != '\000' )		/*have superscript following arrow*/
                   7581:       supsp = rasterize(super,limsize); } /*so try to rasterize superscript*/
                   7582: /* --- set height based on width --- */
                   7583: height = min2(17,max2(9,(width+2)/6));	/* height based on width */
                   7584: height = 1 + (height/2)*2;		/* always force odd height */
                   7585: /* --- generate arrow subraster --- */
                   7586: if ( (arrowsp=arrow_subraster(width,height,pixsz,drctn,isBig)) /*build arrow*/
                   7587: ==   NULL ) goto end_of_job;		/* and quit if we failed */
                   7588: /* --- add space below arrow --- */
                   7589: if ( vspace > 0 )			/* if we have space below arrow */
                   7590:   if ( (spacesp=new_subraster(width,vspace,pixsz)) /*allocate required space*/
                   7591:   !=   NULL )				/* and if we succeeded */
                   7592:     if ( (arrowsp = rastack(spacesp,arrowsp,2,0,1,3)) /* space below arrow */
                   7593:     ==   NULL ) goto end_of_job;	/* and quit if we failed */
                   7594: /* --- init arrow subraster parameters --- */
                   7595: arrowsp->size = size;			/*propagate base font size forward*/
                   7596: arrowsp->baseline = height+vspace-1;	/* set baseline at bottom of arrow */
                   7597: /* --- add limits above/below arrow, as necessary --- */
                   7598: if ( subsp != NULL )			/* stack subscript below arrow */
                   7599:   if ( (arrowsp = rastack(subsp,arrowsp,2,0,1,3)) /* subscript below arrow */
                   7600:   ==   NULL ) goto end_of_job;		/* quit if failed */
                   7601: if ( supsp != NULL )			/* stack superscript above arrow */
                   7602:   if ( (arrowsp = rastack(arrowsp,supsp,1,vspace,1,3)) /*supsc above arrow*/
                   7603:   ==   NULL ) goto end_of_job;		/* quit if failed */
                   7604: /* --- return arrow (or NULL) to caller --- */
                   7605: end_of_job:
                   7606:   return ( arrowsp );
                   7607: } /* --- end-of-function rastarrow() --- */
                   7608: 
                   7609: 
                   7610: /* ==========================================================================
                   7611:  * Function:	rastuparrow ( expression, size, basesp,  drctn, isBig, arg3 )
                   7612:  * Purpose:	returns an up/down arrow subraster (e.g., for \longuparrow)
                   7613:  * --------------------------------------------------------------------------
                   7614:  * Arguments:	expression (I)	char **  to first char of null-terminated
                   7615:  *				LaTeX expression (unused/unchanged)
                   7616:  *		size (I)	int containing base font size (not used,
                   7617:  *				just stored in subraster)
                   7618:  *		basesp (I)	subraster *  to character (or subexpression)
                   7619:  *				immediately preceding space, whose baseline
                   7620:  *				and height params are transferred to space
                   7621:  *		drctn (I)	int containing +1 for up, -1 for down,
                   7622:  *				or 0 for updown
                   7623:  *		isBig (I)	int containing 0 for ---> or 1 for ===>
                   7624:  *		arg3 (I)	int unused
                   7625:  * --------------------------------------------------------------------------
                   7626:  * Returns:	( subraster * )	ptr to up/down arrow subraster
                   7627:  *				or NULL for any error
                   7628:  * --------------------------------------------------------------------------
                   7629:  * Notes:     o	An optional argument [height] may *immediately* follow
                   7630:  *		the \longxxx to explicitly set the arrow's height in pixels.
                   7631:  *		For example, \longuparrow calculates a default height
                   7632:  *		(as usual in LaTeX), whereas \longuparrow[25] explicitly
                   7633:  *		draws a 25-pixel high arrow.  This can be used, e.g.,
                   7634:  *		to draw commutative diagrams in conjunction with
                   7635:  *		\array (and maybe with \stackrel and/or \relstack, too).
                   7636:  *	      o	In case you really want to render, say, [f]---->[g], just
                   7637:  *		use an intervening space, i.e., [f]\longuparrow~[g].
                   7638:  *		In text use two spaces {\rm~[f]\longuparrow~~[g]}.
                   7639:  * ======================================================================= */
                   7640: /* --- entry point --- */
                   7641: subraster *rastuparrow ( char **expression, int size, subraster *basesp,
                   7642: 			int drctn, int isBig, int arg3 )
                   7643: {
                   7644: /* -------------------------------------------------------------------------
                   7645: Allocations and Declarations
                   7646: -------------------------------------------------------------------------- */
                   7647: subraster *uparrow_subraster(), *arrowsp=NULL; /* subraster for arrow */
                   7648: char	*texsubexpr(), heightarg[256];	/* parse for optional [height] */
                   7649: char	*texscripts(), sub[1024],super[1024]; /* and _^limits after [width]*/
                   7650: subraster *rasterize(), *subsp=NULL,*supsp=NULL; /*rasterize limits*/
                   7651: subraster *rastcat();			/* cat superscript left, sub right */
                   7652: double	strtod();			/* convert ascii [height] to value */
                   7653: int	height = 8 + 2*size,  width;	/* height, width for \longxxxarrow */
                   7654: int	islimits = 1;			/*true to handle limits internally*/
                   7655: int	limsize = size-1;		/* font size for limits */
                   7656: int	pixsz = 1;			/*default #bits per pixel, 1=bitmap*/
                   7657: /* -------------------------------------------------------------------------
                   7658: construct blank subraster, and return it to caller
                   7659: -------------------------------------------------------------------------- */
                   7660: /* --- check for optional height arg and replace default height --- */
                   7661: if ( *(*expression) == '[' )		/*check for []-enclosed optional arg*/
                   7662:   { int heightval;			/* test height before using it */
                   7663:     *expression = texsubexpr(*expression,heightarg,255,"[","]",0,0);
                   7664:     heightval =				/* convert [height] to integer */
                   7665: 		(int)((unitlength*strtod(heightarg,NULL))+0.5);
                   7666:     if ( heightval>=2 && heightval<=600 ) /* sanity check */
                   7667:       height = heightval; }		/* replace deafault height */
                   7668: /* --- now parse for limits, and bump expression past it(them) --- */
                   7669: if ( islimits )				/* handling limits internally */
                   7670:   { *expression = texscripts(*expression,sub,super,3); /* parse for limits */
                   7671:     if ( *sub != '\000' )		/*have a subscript following arrow*/
                   7672:       subsp = rasterize(sub,limsize);	/* so try to rasterize subscript */
                   7673:     if ( *super != '\000' )		/*have superscript following arrow*/
                   7674:       supsp = rasterize(super,limsize); } /*so try to rasterize superscript*/
                   7675: /* --- set width based on height --- */
                   7676: width = min2(17,max2(9,(height+2)/4));	/* width based on height */
                   7677: width = 1 + (width/2)*2;		/* always force odd width */
                   7678: /* --- generate arrow subraster --- */
                   7679: if ( (arrowsp=uparrow_subraster(width,height,pixsz,drctn,isBig)) /*build arr*/
                   7680: ==   NULL ) goto end_of_job;		/* and quit if we failed */
                   7681: /* --- init arrow subraster parameters --- */
                   7682: arrowsp->size = size;			/*propagate base font size forward*/
                   7683: arrowsp->baseline = height-1;		/* set baseline at bottom of arrow */
                   7684: /* --- add limits above/below arrow, as necessary --- */
                   7685: if ( supsp != NULL )			/* cat superscript to left of arrow*/
                   7686:   { int	supht = (supsp->image)->height,	/* superscript height */
                   7687: 	deltab = (1+abs(height-supht))/2; /* baseline difference to center */
                   7688:   supsp->baseline = supht-1;		/* force script baseline to bottom */
                   7689:   if ( supht <= height )		/* arrow usually taller than script*/
                   7690: 	arrowsp->baseline -= deltab;	/* so bottom of script goes here */
                   7691:   else	supsp->baseline -= deltab;	/* else bottom of arrow goes here */
                   7692:   if ( (arrowsp = rastcat(supsp,arrowsp,3)) /* superscript left of arrow */
                   7693:     ==   NULL ) goto end_of_job; }	/* quit if failed */
                   7694: if ( subsp != NULL )			/* cat subscript to right of arrow */
                   7695:   { int	subht = (subsp->image)->height,	/* subscript height */
                   7696: 	deltab = (1+abs(height-subht))/2; /* baseline difference to center */
                   7697:   arrowsp->baseline = height-1;		/* reset arrow baseline to bottom */
                   7698:   subsp->baseline = subht-1;		/* force script baseline to bottom */
                   7699:   if ( subht <= height )		/* arrow usually taller than script*/
                   7700: 	arrowsp->baseline -= deltab;	/* so bottom of script goes here */
                   7701:   else	subsp->baseline -= deltab;	/* else bottom of arrow goes here */
                   7702:   if ( (arrowsp = rastcat(arrowsp,subsp,3)) /* subscript right of arrow */
                   7703:     ==   NULL ) goto end_of_job; }	/* quit if failed */
                   7704: /* --- return arrow (or NULL) to caller --- */
                   7705: end_of_job:
                   7706:   arrowsp->baseline = height-1;		/* reset arrow baseline to bottom */
                   7707:   return ( arrowsp );
                   7708: } /* --- end-of-function rastuparrow() --- */
                   7709: 
                   7710: 
                   7711: /* ==========================================================================
                   7712:  * Function:	rastoverlay (expression, size, basesp, overlay, offset2, arg3)
                   7713:  * Purpose:	overlays one raster on another
                   7714:  * --------------------------------------------------------------------------
                   7715:  * Arguments:	expression (I/O) char **  to first char of null-terminated
                   7716:  *				string immediately following overlay \cmd to
                   7717:  *				be rasterized, and returning ptr immediately
                   7718:  *				following last character processed.
                   7719:  *		size (I)	int containing 0-5 default font size
                   7720:  *		basesp (I)	subraster *  to character (or subexpression)
                   7721:  *				immediately preceding overlay \cmd
                   7722:  *				(unused, but passed for consistency)
                   7723:  *		overlay (I)	int containing 1 to overlay / (e.g., \not)
                   7724:  *				or NOVALUE to pick up 2nd arg from expression
                   7725:  *		offset2 (I)	int containing #pixels to horizontally offset
                   7726:  *				overlay relative to underlying symbol,
                   7727:  *				positive(right) or negative or 0,
                   7728:  *				or NOVALUE to pick up optional [offset] arg
                   7729:  *		arg3 (I)	int unused
                   7730:  * --------------------------------------------------------------------------
                   7731:  * Returns:	( subraster * )	ptr to subraster corresponding to composite,
                   7732:  *				or NULL for any parsing error
                   7733:  * --------------------------------------------------------------------------
                   7734:  * Notes:     o
                   7735:  * ======================================================================= */
                   7736: /* --- entry point --- */
                   7737: subraster *rastoverlay ( char **expression, int size, subraster *basesp,
                   7738: 			int overlay, int offset2, int arg3 )
                   7739: {
                   7740: /* -------------------------------------------------------------------------
                   7741: Allocations and Declarations
                   7742: -------------------------------------------------------------------------- */
                   7743: char	*texsubexpr(),			/*parse expression for base,overlay*/
                   7744: 	expr1[512], expr2[512];		/* base, overlay */
                   7745: subraster *rasterize(), *sp1=NULL, *sp2=NULL, /*rasterize 1=base, 2=overlay*/
                   7746: 	*new_subraster();		/*explicitly alloc sp2 if necessary*/
                   7747: subraster *rastcompose(), *overlaysp=NULL; /*subraster for composite overlay*/
                   7748: int	line_raster();			/* draw diagonal for \Not */
                   7749: /* -------------------------------------------------------------------------
                   7750: Obtain base, and maybe overlay, and rasterize them
                   7751: -------------------------------------------------------------------------- */
                   7752: /* --- check for optional offset2 arg  --- */
                   7753: if ( offset2 == NOVALUE )		/* only if not explicitly specified*/
                   7754:  if ( *(*expression) == '[' )		/*check for []-enclosed optional arg*/
                   7755:   { int offsetval;			/* test before using it */
                   7756:     *expression = texsubexpr(*expression,expr2,511,"[","]",0,0);
                   7757:     offsetval = (int)(strtod(expr2,NULL)+0.5); /* convert [offset2] to int */
                   7758:     if ( abs(offsetval) <= 25 )		/* sanity check */
                   7759:       offset2 = offsetval; }		/* replace deafault */
                   7760: if ( offset2 == NOVALUE ) offset2 = 0;	/* novalue means no offset */
                   7761: /* --- parse for base, bump expression past it, and rasterize it --- */
                   7762: *expression = texsubexpr(*expression,expr1,511,"{","}",0,0);
                   7763: if ( *expr1 == '\000' ) goto end_of_job; /* nothing to overlay, so quit */
                   7764: if ( (sp1=rasterize(expr1,size))	/* rasterize base expression */
                   7765: ==   NULL ) goto end_of_job;		/* quit if failed to rasterize */
                   7766: overlaysp = sp1;			/*in case we return with no overlay*/
                   7767: /* --- get overlay expression, and rasterize it --- */
                   7768: if ( overlay == NOVALUE )		/* get overlay from input stream */
                   7769:   { *expression = texsubexpr(*expression,expr2,511,"{","}",0,0);
                   7770:     if ( *expr2 != '\000' )		/* have an overlay */
                   7771:       sp2 = rasterize(expr2,size); }	/* so rasterize overlay expression */
                   7772: else					/* specific overlay */
                   7773:   switch ( overlay )
                   7774:     {
                   7775:     default: break;
                   7776:     case 1:				/* e.g., \not overlays slash */
                   7777:       sp2 = rasterize("/",size+1);	/* rasterize overlay expression */
                   7778:       offset2 = max2(1,size-3);		/* push / right a bit */
                   7779:       offset2 = 0;
                   7780:       break;
                   7781:     case 2:				/* e.g., \Not draws diagonal */
                   7782:       sp2 = NULL;			/* no overlay required */
                   7783:       if ( overlaysp != NULL )		/* check that we have raster */
                   7784: 	{ raster *rp = overlaysp->image; /* raster to be \Not-ed */
                   7785: 	  int width=rp->width, height=rp->height; /* raster dimensions */
                   7786: 	  if ( 0 )			/* diagonal within bounding box */
                   7787: 	   line_raster(rp,0,width-1,height-1,0,1); /* just draw diagonal */
                   7788: 	  else				/* construct "wide" diagonal */
                   7789: 	   { int margin=3;		/* desired extra margin width */
                   7790: 	     sp2 = new_subraster(width+margin,height+margin,1); /*alloc it*/
                   7791: 	     if ( sp2 != NULL )		/* allocated successfully */
                   7792: 	      line_raster(sp2->image,0,width+margin-1,height+margin-1,0,1);}}
                   7793:       break;
                   7794:     case 3:				/* e.g., \sout for strikeout */
                   7795:       sp2 = NULL;			/* no overlay required */
                   7796:       if ( overlaysp != NULL )		/* check that we have raster */
                   7797: 	{ raster *rp = overlaysp->image; /* raster to be \Not-ed */
                   7798: 	  int width=rp->width, height=rp->height; /* raster dimensions */
                   7799: 	  int baseline = overlaysp->baseline; /* we'll ignore descenders */
                   7800: 	  int midrow = max2(0,min2(height-1,offset2+((baseline+1)/2)));
                   7801: 	  if ( 1 )			/* strikeout within bounding box */
                   7802: 	    line_raster(rp,midrow,0,midrow,width-1,1); } /*draw strikeout*/
                   7803:       break;
                   7804:     } /* --- end-of-switch(overlay) --- */
                   7805: if ( sp2 == NULL ) goto end_of_job;	/*return sp1 if failed to rasterize*/
                   7806: /* -------------------------------------------------------------------------
                   7807: construct composite overlay
                   7808: -------------------------------------------------------------------------- */
                   7809: overlaysp = rastcompose(sp1,sp2,offset2,0,3);
                   7810: end_of_job:
                   7811:   return ( overlaysp );
                   7812: } /* --- end-of-function rastoverlay() --- */
                   7813: 
                   7814: 
                   7815: /* ==========================================================================
                   7816:  * Function:	rastfrac ( expression, size, basesp,  isfrac, arg2, arg3 )
                   7817:  * Purpose:	\frac,\atop handler, returns a subraster corresponding to
                   7818:  *		expression (immediately following \frac,\atop) at font size
                   7819:  * --------------------------------------------------------------------------
                   7820:  * Arguments:	expression (I/O) char **  to first char of null-terminated
                   7821:  *				string immediately following \frac to be
                   7822:  *				rasterized, and returning ptr immediately
                   7823:  *				following last character processed.
                   7824:  *		size (I)	int containing 0-5 default font size
                   7825:  *		basesp (I)	subraster *  to character (or subexpression)
                   7826:  *				immediately preceding \frac
                   7827:  *				(unused, but passed for consistency)
                   7828:  *		isfrac (I)	int containing true to draw horizontal line
                   7829:  *				between numerator and denominator,
                   7830:  *				or false not to draw it (for \atop).
                   7831:  *		arg2 (I)	int unused
                   7832:  *		arg3 (I)	int unused
                   7833:  * --------------------------------------------------------------------------
                   7834:  * Returns:	( subraster * )	ptr to subraster corresponding to fraction,
                   7835:  *				or NULL for any parsing error
                   7836:  * --------------------------------------------------------------------------
                   7837:  * Notes:     o
                   7838:  * ======================================================================= */
                   7839: /* --- entry point --- */
                   7840: subraster *rastfrac ( char **expression, int size, subraster *basesp,
                   7841: 			int isfrac, int arg2, int arg3 )
                   7842: {
                   7843: /* -------------------------------------------------------------------------
                   7844: Allocations and Declarations
                   7845: -------------------------------------------------------------------------- */
                   7846: char	*texsubexpr(),			/*parse expression for numer,denom*/
1.3     ! albertel 7847: 	numer[MAXSUBXSZ+1], denom[MAXSUBXSZ+1]; /* parsed numer, denom */
1.1       albertel 7848: subraster *rasterize(), *numsp=NULL, *densp=NULL; /*rasterize numer, denom*/
                   7849: subraster *rastack(), *fracsp=NULL;	/* subraster for numer/denom */
                   7850: subraster *new_subraster()/*, *spacesp=NULL*/; /* space for num or den */
                   7851: int	width=0,			/* width of constructed raster */
                   7852: 	numheight=0;			/* height of numerator */
                   7853: int	baseht=0, baseln=0;		/* height,baseline of base symbol */
                   7854: /*int	istweak = 1;*/			/*true to tweak baseline alignment*/
                   7855: int	rule_raster(),			/* draw horizontal line for frac */
                   7856: 	lineheight = 1;			/* thickness of fraction line */
1.2       albertel 7857: int	vspace = (size>2?2:1);		/*vertical space between components*/
1.1       albertel 7858: int	delete_subraster();		/*free work areas in case of error*/
                   7859: int	type_raster();			/* display debugging output */
                   7860: /* -------------------------------------------------------------------------
                   7861: Obtain numerator and denominator, and rasterize them
                   7862: -------------------------------------------------------------------------- */
                   7863: /* --- parse for numerator,denominator and bump expression past them --- */
                   7864: *expression = texsubexpr(*expression,numer,0,"{","}",0,0);
                   7865: *expression = texsubexpr(*expression,denom,0,"{","}",0,0);
                   7866: if ( *numer=='\000' && *denom=='\000' )	/* missing both components of frac */
                   7867:   goto end_of_job;			/* nothing to do, so quit */
                   7868: /* --- rasterize numerator, denominator --- */
                   7869: if ( *numer != '\000' )			/* have a numerator */
                   7870:  if ( (numsp = rasterize(numer,size-1))	/* so rasterize numer at size-1 */
                   7871:  ==   NULL ) goto end_of_job;		/* and quit if failed */
                   7872: if ( *denom != '\000' )			/* have a denominator */
                   7873:  if ( (densp = rasterize(denom,size-1))	/* so rasterize denom at size-1 */
                   7874:  ==   NULL )				/* failed */
                   7875:   { if ( numsp != NULL )		/* already rasterized numerator */
                   7876:       delete_subraster(numsp);		/* so free now-unneeded numerator */
                   7877:     goto end_of_job; }			/* and quit */
                   7878: /* --- if one componenet missing, use a blank space for it --- */
                   7879: if ( numsp == NULL )			/* no numerator given */
                   7880:   numsp = rasterize("[?]",size-1);	/* missing numerator */
                   7881: if ( densp == NULL )			/* no denominator given */
                   7882:   densp = rasterize("[?]",size-1);	/* missing denominator */
                   7883: /* --- check that we got both components --- */
                   7884: if ( numsp==NULL || densp==NULL )	/* some problem */
                   7885:   { delete_subraster(numsp);		/*delete numerator (if it existed)*/
                   7886:     delete_subraster(densp);		/*delete denominator (if it existed)*/
                   7887:     goto end_of_job; }			/* and quit */
                   7888: /* --- get height of numerator (to determine where line belongs) --- */
                   7889: numheight = (numsp->image)->height;	/* get numerator's height */
                   7890: /* -------------------------------------------------------------------------
                   7891: construct raster with numerator stacked over denominator
                   7892: -------------------------------------------------------------------------- */
                   7893: /* --- construct raster with numer/denom --- */
                   7894: if ( (fracsp = rastack(densp,numsp,0,2*vspace+lineheight,1,3))/*numer/denom*/
                   7895: ==  NULL )				/* failed to construct numer/denom */
                   7896:   { delete_subraster(numsp);		/* so free now-unneeded numerator */
                   7897:     delete_subraster(densp);		/* and now-unneeded denominator */
                   7898:     goto end_of_job; }			/* and then quit */
                   7899: /* --- determine width of constructed raster --- */
                   7900: width = (fracsp->image)->width;		/*just get width of embedded image*/
                   7901: /* --- initialize subraster parameters --- */
                   7902: fracsp->size = size;			/* propagate font size forward */
                   7903: fracsp->baseline = (numheight+vspace+lineheight)+(size+2);/*default baseline*/
1.3     ! albertel 7904: fracsp->type = FRACRASTER;		/* signal \frac image */
1.1       albertel 7905: if ( basesp != (subraster *)NULL )	/* we have base symbol for frac */
                   7906:   { baseht = (basesp->image)->height; 	/* height of base symbol */
                   7907:     baseln =  basesp->baseline;		/* and its baseline */
                   7908:   } /* --- end-of-if(basesp!=NULL) --- */
                   7909: /* -------------------------------------------------------------------------
                   7910: draw horizontal line between numerator and denominator
                   7911: -------------------------------------------------------------------------- */
1.3     ! albertel 7912: fraccenterline = numheight+vspace;	/* signal that we have a \frac */
1.1       albertel 7913: if ( isfrac )				/*line for \frac, but not for \atop*/
1.3     ! albertel 7914:   rule_raster(fracsp->image,fraccenterline,0,width,lineheight,0);
1.1       albertel 7915: /* -------------------------------------------------------------------------
                   7916: return final result to caller
                   7917: -------------------------------------------------------------------------- */
                   7918: end_of_job:
                   7919:   if ( msgfp!=NULL && msglevel>=99 )
                   7920:     { fprintf(msgfp,"rastfrac> returning %s\n",(fracsp==NULL?"null":"..."));
                   7921:       if ( fracsp != NULL )		/* have a constructed raster */
                   7922: 	type_raster(fracsp->image,msgfp); } /* display constructed raster */
                   7923:   return ( fracsp );
                   7924: } /* --- end-of-function rastfrac() --- */
                   7925: 
                   7926: 
                   7927: /* ==========================================================================
                   7928:  * Function:	rastackrel ( expression, size, basesp,  base, arg2, arg3 )
                   7929:  * Purpose:	\stackrel handler, returns a subraster corresponding to
                   7930:  *		stacked relation
                   7931:  * --------------------------------------------------------------------------
                   7932:  * Arguments:	expression (I/O) char **  to first char of null-terminated
                   7933:  *				string immediately following \stackrel to be
                   7934:  *				rasterized, and returning ptr immediately
                   7935:  *				following last character processed.
                   7936:  *		size (I)	int containing 0-4 default font size
                   7937:  *		basesp (I)	subraster *  to character (or subexpression)
                   7938:  *				immediately preceding \stackrel
                   7939:  *				(unused, but passed for consistency)
                   7940:  *		base (I)	int containing 1 if upper/first subexpression
                   7941:  *				is base relation, or 2 if lower/second is
                   7942:  *		arg2 (I)	int unused
                   7943:  *		arg3 (I)	int unused
                   7944:  * --------------------------------------------------------------------------
                   7945:  * Returns:	( subraster * )	ptr to subraster corresponding to stacked
                   7946:  *				relation, or NULL for any parsing error
                   7947:  * --------------------------------------------------------------------------
                   7948:  * Notes:     o
                   7949:  * ======================================================================= */
                   7950: /* --- entry point --- */
                   7951: subraster *rastackrel ( char **expression, int size, subraster *basesp,
                   7952: 			int base, int arg2, int arg3 )
                   7953: {
                   7954: /* -------------------------------------------------------------------------
                   7955: Allocations and Declarations
                   7956: -------------------------------------------------------------------------- */
1.3     ! albertel 7957: char	*texsubexpr(),			/*parse expression for upper,lower*/
        !          7958: 	upper[MAXSUBXSZ+1], lower[MAXSUBXSZ+1];	/* parsed upper, lower */
1.1       albertel 7959: subraster *rasterize(), *upsp=NULL, *lowsp=NULL; /* rasterize upper, lower */
                   7960: subraster *rastack(), *relsp=NULL;	/* subraster for upper/lower */
                   7961: int	upsize  = (base==1? size:size-1), /* font size for upper component */
                   7962: 	lowsize = (base==2? size:size-1); /* font size for lower component */
                   7963: int	vspace = 1;			/*vertical space between components*/
                   7964: int	delete_subraster();		/*free work areas in case of error*/
                   7965: /* -------------------------------------------------------------------------
                   7966: Obtain numerator and denominator, and rasterize them
                   7967: -------------------------------------------------------------------------- */
                   7968: /* --- parse for numerator,denominator and bump expression past them --- */
                   7969: *expression = texsubexpr(*expression,upper,0,"{","}",0,0);
                   7970: *expression = texsubexpr(*expression,lower,0,"{","}",0,0);
                   7971: if ( *upper=='\000' || *lower=='\000' )	/* missing either component */
                   7972:   goto end_of_job;			/* nothing to do, so quit */
                   7973: /* --- rasterize upper, lower --- */
                   7974: if ( *upper != '\000' )			/* have upper component */
                   7975:  if ( (upsp = rasterize(upper,upsize))	/* so rasterize upper component */
                   7976:  ==   NULL ) goto end_of_job;		/* and quit if failed */
                   7977: if ( *lower != '\000' )			/* have lower component */
                   7978:  if ( (lowsp = rasterize(lower,lowsize)) /* so rasterize lower component */
                   7979:  ==   NULL )				/* failed */
                   7980:   { if ( upsp != NULL )			/* already rasterized upper */
                   7981:       delete_subraster(upsp);		/* so free now-unneeded upper */
                   7982:     goto end_of_job; }			/* and quit */
                   7983: /* -------------------------------------------------------------------------
                   7984: construct stacked relation raster
                   7985: -------------------------------------------------------------------------- */
                   7986: /* --- construct stacked relation --- */
                   7987: if ( (relsp = rastack(lowsp,upsp,3-base,vspace,1,3)) /* stacked relation */
                   7988: ==   NULL ) goto end_of_job;		/* quit if failed */
                   7989: /* --- initialize subraster parameters --- */
                   7990: relsp->size = size;			/* propagate font size forward */
                   7991: /* -------------------------------------------------------------------------
                   7992: return final result to caller
                   7993: -------------------------------------------------------------------------- */
                   7994: end_of_job:
                   7995:   return ( relsp );
                   7996: } /* --- end-of-function rastackrel() --- */
                   7997: 
                   7998: 
                   7999: /* ==========================================================================
                   8000:  * Function:	rastmathfunc ( expression, size, basesp,  base, arg2, arg3 )
                   8001:  * Purpose:	\log, \lim, etc handler, returns a subraster corresponding
                   8002:  *		to math functions
                   8003:  * --------------------------------------------------------------------------
                   8004:  * Arguments:	expression (I/O) char **  to first char of null-terminated
                   8005:  *				string immediately following \mathfunc to be
                   8006:  *				rasterized, and returning ptr immediately
                   8007:  *				following last character processed.
                   8008:  *		size (I)	int containing 0-4 default font size
                   8009:  *		basesp (I)	subraster *  to character (or subexpression)
                   8010:  *				immediately preceding \mathfunc
                   8011:  *				(unused, but passed for consistency)
                   8012:  *		mathfunc (I)	int containing 1=arccos, 2=arcsin, etc.
                   8013:  *		islimits (I)	int containing 1 if function may have
                   8014:  *				limits underneath, e.g., \lim_{n\to\infty}
                   8015:  *		arg3 (I)	int unused
                   8016:  * --------------------------------------------------------------------------
                   8017:  * Returns:	( subraster * )	ptr to subraster corresponding to mathfunc,
                   8018:  *				or NULL for any parsing error
                   8019:  * --------------------------------------------------------------------------
                   8020:  * Notes:     o
                   8021:  * ======================================================================= */
                   8022: /* --- entry point --- */
                   8023: subraster *rastmathfunc ( char **expression, int size, subraster *basesp,
                   8024: 			int mathfunc, int islimits, int arg3 )
                   8025: {
                   8026: /* -------------------------------------------------------------------------
                   8027: Allocations and Declarations
                   8028: -------------------------------------------------------------------------- */
                   8029: char	*texscripts(),			/* parse expression for _limits */
1.3     ! albertel 8030: 	func[MAXTOKNSZ+1], limits[MAXSUBXSZ+1]; /*func as {\rm func}, limits*/
1.1       albertel 8031: char	*texsubexpr(),			/* parse expression for arg */
1.3     ! albertel 8032: 	funcarg[MAXTOKNSZ+1];		/* optional func arg */
1.1       albertel 8033: subraster *rasterize(), *funcsp=NULL, *limsp=NULL; /*rasterize func,limits*/
                   8034: subraster *rastack(), *mathfuncsp=NULL;	/* subraster for mathfunc/limits */
                   8035: int	limsize = size-1;		/* font size for limits */
                   8036: int	vspace = 1;			/*vertical space between components*/
                   8037: int	delete_subraster();		/*free work areas in case of error*/
                   8038: /* --- table of function names by mathfunc number --- */
                   8039: static	int  numnames = 34;		/* number of names in table */
                   8040: static	char *funcnames[] = {
                   8041: 	"error",			/*  0 index is illegal/error bucket*/
                   8042: 	"arccos",  "arcsin",  "arctan",	/*  1 -  3 */
                   8043: 	"arg",     "cos",     "cosh",	/*  4 -  6 */
                   8044: 	"cot",     "coth",    "csc",	/*  7 -  9 */
                   8045: 	"deg",     "det",     "dim",	/* 10 - 12 */
                   8046: 	"exp",     "gcd",     "hom",	/* 13 - 15 */
                   8047: 	"inf",     "ker",     "lg",	/* 16 - 18 */
                   8048: 	"lim",     "liminf",  "limsup",	/* 19 - 21 */
                   8049: 	"ln",      "log",     "max",	/* 22 - 24 */
                   8050: 	"min",     "Pr",      "sec",	/* 25 - 27 */
                   8051: 	"sin",     "sinh",    "sup",	/* 28 - 30 */
                   8052: 	"tan",     "tanh",		/* 31 - 32 */
                   8053: 	/* --- extra mimetex funcnames --- */
                   8054: 	"tr",				/* 33 */
                   8055: 	"pmod"				/* 34 */
                   8056: 	} ;
                   8057: /* -------------------------------------------------------------------------
                   8058: set up and rasterize function name in \rm
                   8059: -------------------------------------------------------------------------- */
                   8060: if ( mathfunc<0 || mathfunc>numnames ) mathfunc=0; /* check index bounds */
                   8061: switch ( mathfunc )			/* check for special processing */
                   8062:   {
                   8063:   default:				/* no special processing */
                   8064:     strcpy(func,"{\\rm~");		/* init string with {\rm~ */
                   8065:     strcat(func,funcnames[mathfunc]);	/* concat function name */
                   8066:     strcat(func,"}");			/* and add terminating } */
                   8067:     break;
                   8068:   case 34:				/* \pmod{x} --> (mod x) */
                   8069:     /* --- parse for \pmod{arg} argument --- */
                   8070:     *expression = texsubexpr(*expression,funcarg,2047,"{","}",0,0);
                   8071:     strcpy(func,"{\\({\\rm~mod}");	/* init with {\left({\rm~mod} */
                   8072:     strcat(func,"\\hspace2");		/* concat space */
                   8073:     strcat(func,funcarg);		/* and \pmodargument */
                   8074:     strcat(func,"\\)}");		/* and add terminating \right)} */
                   8075:     break;
                   8076:   } /* --- end-of-switch(mathfunc) --- */
                   8077: if ( (funcsp = rasterize(func,size))	/* rasterize function name */
                   8078: ==   NULL ) goto end_of_job;		/* and quit if failed */
                   8079: mathfuncsp = funcsp;			/* just return funcsp if no limits */
                   8080: if ( !islimits ) goto end_of_job;	/* treat any subscript normally */
                   8081: /* -------------------------------------------------------------------------
                   8082: Obtain limits, if permitted and if provided, and rasterize them
                   8083: -------------------------------------------------------------------------- */
                   8084: /* --- parse for subscript limits, and bump expression past it(them) --- */
                   8085: *expression = texscripts(*expression,limits,limits,1);
                   8086: if ( *limits=='\000') goto end_of_job;	/* no limits, nothing to do, quit */
                   8087: /* --- rasterize limits --- */
                   8088: if ( (limsp = rasterize(limits,limsize)) /* rasterize limits */
                   8089: ==   NULL ) goto end_of_job;		/* and quit if failed */
                   8090: /* -------------------------------------------------------------------------
                   8091: construct func atop limits
                   8092: -------------------------------------------------------------------------- */
                   8093: /* --- construct func atop limits --- */
                   8094: if ( (mathfuncsp = rastack(limsp,funcsp,2,vspace,1,3)) /* func atop limits */
                   8095: ==   NULL ) goto end_of_job;		/* quit if failed */
                   8096: /* --- initialize subraster parameters --- */
                   8097: mathfuncsp->size = size;		/* propagate font size forward */
                   8098: /* -------------------------------------------------------------------------
                   8099: return final result to caller
                   8100: -------------------------------------------------------------------------- */
                   8101: end_of_job:
                   8102:   return ( mathfuncsp );
                   8103: } /* --- end-of-function rastmathfunc() --- */
                   8104: 
                   8105: 
                   8106: /* ==========================================================================
                   8107:  * Function:	rastsqrt ( expression, size, basesp,  arg1, arg2, arg3 )
                   8108:  * Purpose:	\sqrt handler, returns a subraster corresponding to
                   8109:  *		expression (immediately following \sqrt) at font size
                   8110:  * --------------------------------------------------------------------------
                   8111:  * Arguments:	expression (I/O) char **  to first char of null-terminated
                   8112:  *				string immediately following \sqrt to be
                   8113:  *				rasterized, and returning ptr immediately
                   8114:  *				following last character processed.
                   8115:  *		size (I)	int containing 0-4 default font size
                   8116:  *		basesp (I)	subraster *  to character (or subexpression)
                   8117:  *				immediately preceding \accent
                   8118:  *				(unused, but passed for consistency)
                   8119:  *		arg1 (I)	int unused
                   8120:  *		arg2 (I)	int unused
                   8121:  *		arg3 (I)	int unused
                   8122:  * --------------------------------------------------------------------------
                   8123:  * Returns:	( subraster * )	ptr to subraster corresponding to expression,
                   8124:  *				or NULL for any parsing error
                   8125:  *				(expression ptr unchanged if error occurs)
                   8126:  * --------------------------------------------------------------------------
                   8127:  * Notes:     o
                   8128:  * ======================================================================= */
                   8129: /* --- entry point --- */
                   8130: subraster *rastsqrt ( char **expression, int size, subraster *basesp,
                   8131: 			int arg1, int arg2, int arg3 )
                   8132: {
                   8133: /* -------------------------------------------------------------------------
                   8134: Allocations and Declarations
                   8135: -------------------------------------------------------------------------- */
1.3     ! albertel 8136: char	*texsubexpr(), subexpr[MAXSUBXSZ+1], /*parse subexpr to be sqrt-ed*/
        !          8137: 	rootarg[MAXSUBXSZ+1];		/* optional \sqrt[rootarg]{...} */
1.1       albertel 8138: subraster *rasterize(), *subsp=NULL;	/* rasterize subexpr */
                   8139: subraster *accent_subraster(), *sqrtsp=NULL, /* subraster with the sqrt */
                   8140: 	*new_subraster(), *rootsp=NULL;	/* optionally preceded by [rootarg]*/
                   8141: int	sqrtheight=0, sqrtwidth=0, surdwidth=0,	/* height,width of sqrt */
                   8142: 	rootheight=0, rootwidth=0,	/* height,width of rootarg raster */
                   8143: 	subheight=0, subwidth=0, pixsz=0; /* height,width,pixsz of subexpr */
                   8144: int	rastput();			/* put subexpr in constructed sqrt */
                   8145: int	overspace = 2;			/*space between subexpr and overbar*/
                   8146: int	delete_subraster();		/* free work areas */
                   8147: /* -------------------------------------------------------------------------
                   8148: Obtain subexpression to be sqrt-ed, and rasterize it
                   8149: -------------------------------------------------------------------------- */
                   8150: /* --- first check for optional \sqrt[rootarg]{...} --- */
                   8151: if ( *(*expression) == '[' )		/*check for []-enclosed optional arg*/
                   8152:   { *expression = texsubexpr(*expression,rootarg,0,"[","]",0,0);
                   8153:     if ( *rootarg != '\000' )		/* got rootarg */
                   8154:      if ( (rootsp=rasterize(rootarg,size-1)) /*rasterize it at smaller size*/
                   8155:      != NULL )				/* rasterized successfully */
                   8156:       {	rootheight = (rootsp->image)->height;  /* get height of rootarg */
                   8157: 	rootwidth  = (rootsp->image)->width; } /* and its width */
                   8158:   } /* --- end-of-if(**expression=='[') --- */
                   8159: /* --- parse for subexpr to be sqrt-ed, and bump expression past it --- */
                   8160: *expression = texsubexpr(*expression,subexpr,0,"{","}",0,0);
                   8161: if ( *subexpr == '\000' )		/* couldn't get subexpression */
                   8162:   goto end_of_job;			/* nothing to do, so quit */
                   8163: /* --- rasterize subexpression to be accented --- */
                   8164: if ( (subsp = rasterize(subexpr,size))	/*rasterize subexpr at original size*/
                   8165: ==   NULL ) goto end_of_job;		/* quit if failed */
                   8166: /* -------------------------------------------------------------------------
                   8167: determine height and width of sqrt raster to be constructed
                   8168: -------------------------------------------------------------------------- */
                   8169: /* --- first get height and width of subexpr --- */
                   8170: subheight = (subsp->image)->height;	/* height of subexpr */
                   8171: subwidth  = (subsp->image)->width;	/* and its width */
                   8172: pixsz     = (subsp->image)->pixsz;	/* pixsz remains constant */
                   8173: /* --- determine height and width of sqrt to contain subexpr --- */
                   8174: sqrtheight = subheight + overspace;	/* subexpr + blank line + overbar */
                   8175: surdwidth  = SQRTWIDTH(sqrtheight);	/* width of surd */
                   8176: sqrtwidth  = subwidth + surdwidth + 1;	/* total width */
                   8177: /* -------------------------------------------------------------------------
                   8178: construct sqrt (with room to move in subexpr) and embed subexpr in it
                   8179: -------------------------------------------------------------------------- */
                   8180: /* --- construct sqrt --- */
                   8181: if ( (sqrtsp=accent_subraster(SQRTACCENT,sqrtwidth,sqrtheight,pixsz))
                   8182: ==   NULL ) goto end_of_job;		/* quit if failed to build sqrt */
                   8183: /* --- embed subexpr in sqrt at lower-right corner--- */
                   8184: rastput(sqrtsp->image,subsp->image,overspace,sqrtwidth-subwidth,1);
                   8185: sqrtsp->baseline = subsp->baseline + overspace; /* adjust baseline */
                   8186: /* --- "embed" rootarg at upper-left --- */
                   8187: if ( rootsp != NULL )			/*have optional \sqrt[rootarg]{...}*/
                   8188:   {
                   8189:   /* --- allocate full raster to contain sqrtsp and rootsp --- */
                   8190:   int fullwidth = sqrtwidth +rootwidth - min2(rootwidth,max2(0,surdwidth-4)),
                   8191:       fullheight= sqrtheight+rootheight- min2(rootheight,3+size);
                   8192:   subraster *fullsp = new_subraster(fullwidth,fullheight,pixsz);
                   8193:   if ( fullsp != NULL )			/* allocated successfully */
                   8194:     { /* --- embed sqrtsp exactly at lower-right corner --- */
                   8195:       rastput(fullsp->image,sqrtsp->image, /* exactly at lower-right corner*/
                   8196: 	fullheight-sqrtheight,fullwidth-sqrtwidth,1);
                   8197:       /* --- embed rootsp near upper-left, nestled above leading surd --- */
                   8198:       rastput(fullsp->image,rootsp->image,
                   8199: 	0,max2(0,surdwidth-rootwidth-2-size),0);
                   8200:       /* --- replace sqrtsp with fullsp --- */
                   8201:       delete_subraster(sqrtsp);		/* free original sqrtsp */
                   8202:       sqrtsp = fullsp;			/* and repoint it to fullsp instead*/
                   8203:       sqrtsp->baseline = fullheight - (subheight - subsp->baseline); }
                   8204:   } /* --- end-of-if(rootsp!=NULL) --- */
                   8205: /* --- initialize subraster parameters --- */
                   8206: sqrtsp->size = size;			/* propagate font size forward */
                   8207: /* -------------------------------------------------------------------------
                   8208: free unneeded component subrasters and return final result to caller
                   8209: -------------------------------------------------------------------------- */
                   8210: end_of_job:
                   8211:   if ( subsp != NULL ) delete_subraster(subsp); /* free unneeded subexpr */
                   8212:   return ( sqrtsp );
                   8213: } /* --- end-of-function rastsqrt() --- */
                   8214: 
                   8215: 
                   8216: /* ==========================================================================
                   8217:  * Function:	rastaccent (expression,size,basesp,accent,isabove,isscript)
                   8218:  * Purpose:	\hat, \vec, \etc handler, returns a subraster corresponding
                   8219:  *		to expression (immediately following \accent) at font size
                   8220:  * --------------------------------------------------------------------------
                   8221:  * Arguments:	expression (I/O) char **  to first char of null-terminated
                   8222:  *				string immediately following \accent to be
                   8223:  *				rasterized, and returning ptr immediately
                   8224:  *				following last character processed.
                   8225:  *		size (I)	int containing 0-4 default font size
                   8226:  *		basesp (I)	subraster *  to character (or subexpression)
                   8227:  *				immediately preceding \accent
                   8228:  *				(unused, but passed for consistency)
                   8229:  *		accent (I)	int containing HATACCENT or VECACCENT, etc,
                   8230:  *				between numerator and denominator,
                   8231:  *				or false not to draw it (for \over).
                   8232:  *		isabove (I)	int containing true if accent is above
                   8233:  *				expression to be accented, or false
                   8234:  *				if accent is below (e.g., underbrace)
                   8235:  *		isscript (I)	int containing true if sub/superscripts
                   8236:  *				allowed (for under/overbrace), or 0 if not.
                   8237:  * --------------------------------------------------------------------------
                   8238:  * Returns:	( subraster * )	ptr to subraster corresponding to expression,
                   8239:  *				or NULL for any parsing error
                   8240:  *				(expression ptr unchanged if error occurs)
                   8241:  * --------------------------------------------------------------------------
                   8242:  * Notes:     o	Also handles \overbrace{}^{} and \underbrace{}_{} by way
                   8243:  *		of isabove and isscript args.
                   8244:  * ======================================================================= */
                   8245: /* --- entry point --- */
                   8246: subraster *rastaccent ( char **expression, int size, subraster *basesp,
                   8247: 			int accent, int isabove, int isscript )
                   8248: {
                   8249: /* -------------------------------------------------------------------------
                   8250: Allocations and Declarations
                   8251: -------------------------------------------------------------------------- */
1.3     ! albertel 8252: char	*texsubexpr(), subexpr[MAXSUBXSZ+1]; /*parse subexpr to be accented*/
1.1       albertel 8253: char	*texscripts(), *script=NULL,	/* \under,overbrace allow scripts */
1.3     ! albertel 8254: 	subscript[MAXTOKNSZ+1], supscript[MAXTOKNSZ+1];	/* parsed scripts */
1.1       albertel 8255: subraster *rasterize(), *subsp=NULL, *scrsp=NULL; /*rasterize subexpr,script*/
                   8256: subraster *rastack(), *accsubsp=NULL;	/* stack accent, subexpr, script */
                   8257: subraster *accent_subraster(), *accsp=NULL; /*raster for the accent itself*/
                   8258: int	accheight=0, accwidth=0,	/* height, width of accent */
                   8259: 	subheight=0, subwidth=0, pixsz=0; /* height,width,pixsz of subexpr */
                   8260: int	delete_subraster();		/*free work areas in case of error*/
                   8261: int	vspace = 0;			/*vertical space between accent,sub*/
                   8262: /* -------------------------------------------------------------------------
                   8263: Obtain subexpression to be accented, and rasterize it
                   8264: -------------------------------------------------------------------------- */
                   8265: /* --- parse for subexpr to be accented, and bump expression past it --- */
                   8266: *expression = texsubexpr(*expression,subexpr,0,"{","}",0,0);
                   8267: if ( *subexpr=='\000' )			/* couldn't get subexpression */
                   8268:   goto end_of_job;			/* nothing to do, so quit */
                   8269: /* --- rasterize subexpression to be accented --- */
                   8270: if ( (subsp = rasterize(subexpr,size))	/*rasterize subexpr at original size*/
                   8271: ==   NULL ) goto end_of_job;		/* quit if failed */
                   8272: /* -------------------------------------------------------------------------
                   8273: determine desired accent width and height
                   8274: -------------------------------------------------------------------------- */
                   8275: /* --- first get height and width of subexpr --- */
                   8276: subheight = (subsp->image)->height;	/* height of subexpr */
                   8277: subwidth  = (subsp->image)->width;	/* and its width is overall width */
                   8278: pixsz     = (subsp->image)->pixsz;	/* original pixsz remains constant */
                   8279: /* --- determine desired width, height of accent --- */
                   8280: accwidth = subwidth;			/* same width as subexpr */
1.2       albertel 8281: accheight = 4;				/* default for bars */
1.1       albertel 8282: switch ( accent )
                   8283:   { default: break;			/* default okay */
                   8284:   case DOTACCENT: case DDOTACCENT:
1.2       albertel 8285:     accheight = (size<4? 3:4);		/* default for dots */
1.1       albertel 8286:     break;
1.2       albertel 8287:   case VECACCENT:
                   8288:     vspace = 1;				/* set 1-pixel vertical space */
                   8289:   case HATACCENT:
1.1       albertel 8290:     accheight = 7;			/* default */
                   8291:     if ( subwidth < 10 ) accheight = 5;	/* unless small width */
                   8292:       else if ( subwidth > 25 ) accheight = 9; /* or large */
                   8293:     break;
                   8294:   } /* --- end-of-switch(accent) --- */
                   8295: accheight = min2(accheight,subheight);	/*never higher than accented subexpr*/
                   8296: /* -------------------------------------------------------------------------
                   8297: construct accent, and construct subraster with accent over (or under) subexpr
                   8298: -------------------------------------------------------------------------- */
                   8299: /* --- first construct accent --- */
                   8300: if ( (accsp = accent_subraster(accent,accwidth,accheight,pixsz)) /* accent */
                   8301: ==   NULL ) goto end_of_job;		/* quit if failed to build accent */
                   8302: /* --- now stack accent above (or below) subexpr, and free both args --- */
                   8303: accsubsp = (isabove? rastack(subsp,accsp,1,vspace,1,3)/*accent above subexpr*/
                   8304:            : rastack(accsp,subsp,2,vspace,1,3));      /*accent below subexpr*/
                   8305: if ( accsubsp == NULL )			/* failed to stack accent */
                   8306:   { delete_subraster(subsp);		/* free unneeded subsp */
                   8307:     delete_subraster(accsp);		/* and unneeded accsp */
                   8308:     goto end_of_job; }			/* and quit */
                   8309: /* -------------------------------------------------------------------------
                   8310: look for super/subscript (annotation for over/underbrace)
                   8311: -------------------------------------------------------------------------- */
                   8312: /* --- first check whether accent permits accompanying annotations --- */
                   8313: if ( !isscript ) goto end_of_job;	/* no annotations for this accent */
                   8314: /* --- now get scripts if there actually are any --- */
                   8315: *expression = texscripts(*expression,subscript,supscript,(isabove?2:1));
                   8316: script = (isabove? supscript : subscript); /*select above^ or below_ script*/
                   8317: if ( *script == '\000' ) goto end_of_job; /* no accompanying script */
                   8318: /* --- rasterize script annotation at size-2 --- */
                   8319: if ( (scrsp = rasterize(script,size-2)) /* rasterize script at size-2 */
                   8320: ==   NULL ) goto end_of_job;		/* quit if failed */
                   8321: /* --- stack annotation above (or below) accent, and free both args --- */
                   8322: accsubsp = (isabove? rastack(accsubsp,scrsp,1,0,1,3) /* accent above base */
                   8323:            : rastack(scrsp,accsubsp,2,0,1,3));       /* accent below base */
                   8324: /* -------------------------------------------------------------------------
                   8325: return final result to caller
                   8326: -------------------------------------------------------------------------- */
                   8327: end_of_job:
                   8328:   if ( accsubsp != NULL )		/* initialize subraster parameters */
                   8329:     accsubsp->size = size;		/* propagate font size forward */
                   8330:   return ( accsubsp );
                   8331: } /* --- end-of-function rastaccent() --- */
                   8332: 
                   8333: 
                   8334: /* ==========================================================================
1.2       albertel 8335:  * Function:	rastfont (expression,size,basesp,ifontnum,arg2,arg3)
1.1       albertel 8336:  * Purpose:	\cal{}, \scr{}, \etc handler, returns subraster corresponding
                   8337:  *		to char(s) within {}'s rendered at size
                   8338:  * --------------------------------------------------------------------------
                   8339:  * Arguments:	expression (I/O) char **  to first char of null-terminated
                   8340:  *				string immediately following \font to be
                   8341:  *				rasterized, and returning ptr immediately
                   8342:  *				following last character processed.
                   8343:  *		size (I)	int containing 0-5 default font size
                   8344:  *		basesp (I)	subraster *  to character (or subexpression)
                   8345:  *				immediately preceding \accent
                   8346:  *				(unused, but passed for consistency)
1.2       albertel 8347:  *		ifontnum (I)	int containing 1 for \cal{}, 2 for \scr{}
1.1       albertel 8348:  *		arg2 (I)	int unused
                   8349:  *		arg3 (I)	int unused
                   8350:  * --------------------------------------------------------------------------
                   8351:  * Returns:	( subraster * )	ptr to subraster corresponding to chars
                   8352:  *				between {}'s, or NULL for any parsing error
                   8353:  * --------------------------------------------------------------------------
                   8354:  * Notes:     o
                   8355:  * ======================================================================= */
                   8356: /* --- entry point --- */
                   8357: subraster *rastfont ( char **expression, int size, subraster *basesp,
1.2       albertel 8358: 			int ifontnum, int arg2, int arg3 )
1.1       albertel 8359: {
                   8360: /* -------------------------------------------------------------------------
                   8361: Allocations and Declarations
                   8362: -------------------------------------------------------------------------- */
1.3     ! albertel 8363: char	*texsubexpr(), fontchars[MAXSUBXSZ+1], /* chars to render in font */
        !          8364: 	subexpr[MAXSUBXSZ+1];		/* turn \cal{AB} into \calA\calB */
1.1       albertel 8365: char	*pfchars=fontchars, fchar='\0';	/* run thru fontchars one at a time*/
1.2       albertel 8366: char	*name = NULL;			/* fontinfo[ifontnum].name */
                   8367: int	family = 0,			/* fontinfo[ifontnum].family */
                   8368: 	istext = 0,			/* fontinfo[ifontnum].istext */
                   8369: 	class = 0;			/* fontinfo[ifontnum].class */
1.1       albertel 8370: subraster *rasterize(), *fontsp=NULL,	/* rasterize chars in font */
                   8371: 	*rastflags();			/* or just set flag to switch font */
1.2       albertel 8372: int	oldsmashmargin = smashmargin;	/* turn off smash in text mode */
                   8373: #if 0
1.1       albertel 8374: /* --- fonts recognized by rastfont --- */
1.2       albertel 8375: static	int  nfonts = 6;		/* legal font #'s are 1...nfonts */
1.1       albertel 8376: static	struct {char *name; int class;}
                   8377:   fonts[] =
                   8378:     { /* --- name  class 1=upper,2=alpha,3=alnum,4=lower,5=digit,9=all --- */
1.2       albertel 8379: 	{ "\\math",	0 },
                   8380: 	{ "\\mathcal",	1 },		/*(1) calligraphic, uppercase */
                   8381: 	{ "\\mathscr",	1 },		/*(2) rsfs/script, uppercase */
                   8382: 	{ "\\textrm",	-1 },		/*(3) \rm,\text{abc} --> {\rm~abc} */
                   8383: 	{ "\\textit",	-1 },		/*(4) \it,\textit{abc}-->{\it~abc} */
                   8384: 	{ "\\mathbb",	-1 },		/*(5) \bb,\mathbb{abc}-->{\bb~abc} */
                   8385: 	{ "\\mathbf",	-1 },		/*(6) \bf,\mathbf{abc}-->{\bf~abc} */
1.3     ! albertel 8386: 	{ "\\mathrm",   -1 },		/*(7) \mathrm */
        !          8387: 	{ "\\cyr",      -1 },		/*(8) \cyr */
1.1       albertel 8388: 	{ NULL,		0 }
                   8389:     } ; /* --- end-of-fonts[] --- */
1.2       albertel 8390: #endif
1.1       albertel 8391: /* -------------------------------------------------------------------------
                   8392: first get font name and class to determine type of conversion desired
                   8393: -------------------------------------------------------------------------- */
1.2       albertel 8394: if (ifontnum<=0 || ifontnum>nfontinfo) ifontnum=0; /*math if out-of-bounds*/
                   8395: name   = fontinfo[ifontnum].name;	/* font name */
                   8396: family = fontinfo[ifontnum].family;	/* font family */
                   8397: istext = fontinfo[ifontnum].istext;	/*true in text mode (respect space)*/
                   8398: class  = fontinfo[ifontnum].class;	/* font class */
                   8399: if ( istext )				/* text (respect blanks) */
1.3     ! albertel 8400:  { mathsmashmargin = smashmargin;	/* needed for \text{if $n-m$ even} */
        !          8401:    smashmargin = 0; }			/* don't smash internal blanks */
1.1       albertel 8402: /* -------------------------------------------------------------------------
                   8403: now convert \font{abc} --> {\font~abc}, or convert ABC to \calA\calB\calC
                   8404: -------------------------------------------------------------------------- */
1.2       albertel 8405: if ( 1 || class<0 )			/* not character-by-character */
1.1       albertel 8406:  { 
                   8407:  /* ---
                   8408:  if \font not immediately followed by { then it has no arg, so just set flag
                   8409:  ------------------------------------------------------------------------ */
                   8410:  if ( *(*expression) != '{' )		/* no \font arg, so just set flag */
                   8411:     {
                   8412:     if ( msgfp!=NULL && msglevel>=99 )
1.2       albertel 8413:      fprintf(msgfp,"rastfont> \\%s rastflags() for font#%d\n",name,ifontnum);
                   8414:     fontsp = rastflags(expression,size,basesp,ISFONTFAM,ifontnum,arg3);
1.1       albertel 8415:     goto end_of_job;
                   8416:     } /* --- end-of-if(*(*expression)!='{') --- */
                   8417:  /* ---
                   8418:  convert \font{abc} --> {\font~abc}
                   8419:  ---------------------------------- */
                   8420:  /* --- parse for {fontchars} arg, and bump expression past it --- */
                   8421:  *expression = texsubexpr(*expression,fontchars,0,"{","}",0,0);
                   8422:  if ( msgfp!=NULL && msglevel>=99 )
                   8423:   fprintf(msgfp,"rastfont> \\%s fontchars=\"%s\"\n",name,fontchars);
                   8424:  /* --- convert all fontchars at the same time --- */
                   8425:  strcpy(subexpr,"{");			/* start off with opening { */
                   8426:  strcat(subexpr,name);			/* followed by font name */
                   8427:  strcat(subexpr,"~");			/* followed by whitespace */
                   8428:  strcat(subexpr,fontchars);		/* followed by all the chars */
                   8429:  strcat(subexpr,"}");			/* terminate with closing } */
                   8430:  } /* --- end-of-if(class<0) --- */
                   8431: else					/* character-by-character */
                   8432:  {
                   8433:  /* ---
                   8434:  convert ABC to \calA\calB\calC
                   8435:  ------------------------------ */
                   8436:  int	isprevchar=0;			/* true if prev char converted */
                   8437:  /* --- parse for {fontchars} arg, and bump expression past it --- */
                   8438:  *expression = texsubexpr(*expression,fontchars,0,"{","}",0,0);
                   8439:  if ( msgfp!=NULL && msglevel>=99 )
                   8440:   fprintf(msgfp,"rastfont> \\%s fontchars=\"%s\"\n",name,fontchars);
                   8441:  /* --- convert fontchars one at a time --- */
                   8442:  strcpy(subexpr,"{\\rm~");		/* start off with opening {\rm */
                   8443:  strcpy(subexpr,"{");			/* nope, just start off with { */
                   8444:  for ( pfchars=fontchars; (fchar= *pfchars)!='\000'; pfchars++ )
                   8445:   {
                   8446:   if ( isthischar(fchar,WHITEMATH) )	/* some whitespace */
1.2       albertel 8447:     { if ( 0 || istext )		/* and we're in a text mode font */
1.1       albertel 8448: 	strcat(subexpr,"\\;"); }	/* so respect whitespace */
                   8449:   else					/* char to be displayed in font */
                   8450:     { int exprlen = 0;			/* #chars in subexpr before fchar */
                   8451:       int isinclass = 0;		/* set true if fchar in font class */
1.2       albertel 8452:       /* --- class: 1=upper, 2=alpha, 3=alnum, 4=lower, 5=digit, 9=all --- */
1.1       albertel 8453:       switch ( class )			/* check if fchar is in font class */
                   8454: 	{ default: break;		/* no chars in unrecognized class */
                   8455: 	  case 1: if ( isupper((int)fchar) ) isinclass=1; break;
                   8456: 	  case 2: if ( isalpha((int)fchar) ) isinclass=1; break;
                   8457: 	  case 3: if ( isalnum((int)fchar) ) isinclass=1; break;
                   8458: 	  case 4: if ( islower((int)fchar) ) isinclass=1; break;
                   8459: 	  case 5: if ( isdigit((int)fchar) ) isinclass=1; break;
                   8460: 	  case 9: isinclass=1; break; }
                   8461:       if ( isinclass )			/* convert current char to \font */
                   8462: 	{ strcat(subexpr,name);		/* by prefixing it with font name */
                   8463: 	  isprevchar = 1; }		/* and set flag to signal separator*/
                   8464:       else				/* current char not in \font */
                   8465: 	{ if ( isprevchar )		/* extra separator only after \font*/
                   8466: 	   if ( isalpha(fchar) )	/* separator only before alpha */
                   8467: 	    strcat(subexpr,"~");	/* need separator after \font */
                   8468: 	  isprevchar = 0; }		/* reset flag for next char */
                   8469:       exprlen = strlen(subexpr);	/* #chars so far */
                   8470:       subexpr[exprlen] = fchar;		/*fchar immediately after \fontname*/
                   8471:       subexpr[exprlen+1] = '\000'; }	/* replace terminating '\0' */
                   8472:   } /* --- end-of-for(pfchars) --- */
                   8473:  strcat(subexpr,"}");			/* add closing } */
                   8474:  } /* --- end-of-if/else(class<0) --- */
                   8475: /* -------------------------------------------------------------------------
                   8476: rasterize subexpression containing chars to be rendered at font
                   8477: -------------------------------------------------------------------------- */
                   8478: if ( msgfp!=NULL && msglevel>=99 )
                   8479:   fprintf(msgfp,"rastfont> subexpr=\"%s\"\n",subexpr);
                   8480: if ( (fontsp = rasterize(subexpr,size))	/* rasterize chars in font */
                   8481: ==   NULL ) goto end_of_job;		/* and quit if failed */
                   8482: /* -------------------------------------------------------------------------
                   8483: back to caller with chars rendered in font
                   8484: -------------------------------------------------------------------------- */
                   8485: end_of_job:
1.2       albertel 8486:   smashmargin = oldsmashmargin;		/* restore smash */
1.3     ! albertel 8487:   mathsmashmargin = SMASHMARGIN;	/* this one probably not necessary */
1.2       albertel 8488:   if ( istext && fontsp!=NULL )		/* raster contains text mode font */
                   8489:     fontsp->type = blanksignal;		/* signal nosmash */
1.1       albertel 8490:   return ( fontsp );			/* chars rendered in font */
                   8491: } /* --- end-of-function rastfont() --- */
                   8492: 
                   8493: 
                   8494: /* ==========================================================================
                   8495:  * Function:	rastbegin ( expression, size, basesp, arg1, arg2, arg3 )
                   8496:  * Purpose:	\begin{}...\end{}  handler, returns a subraster corresponding
                   8497:  *		to array expression within environment, i.e., rewrites
                   8498:  *		\begin{}...\end{} as mimeTeX equivalent, and rasterizes that.
                   8499:  * --------------------------------------------------------------------------
                   8500:  * Arguments:	expression (I/O) char **  to first char of null-terminated
                   8501:  *				string immediately following \begin to be
                   8502:  *				rasterized, and returning ptr immediately
                   8503:  *				following last character processed.
                   8504:  *		size (I)	int containing 0-4 default font size
                   8505:  *		basesp (I)	subraster *  to character (or subexpression)
                   8506:  *				immediately preceding \begin
                   8507:  *				(unused, but passed for consistency)
                   8508:  *		arg1 (I)	int unused
                   8509:  *		arg2 (I)	int unused
                   8510:  *		arg3 (I)	int unused
                   8511:  * --------------------------------------------------------------------------
                   8512:  * Returns:	( subraster * )	ptr to subraster corresponding to array
                   8513:  *				expression, or NULL for any parsing error
                   8514:  * --------------------------------------------------------------------------
                   8515:  * Notes:     o
                   8516:  * ======================================================================= */
                   8517: /* --- entry point --- */
                   8518: subraster *rastbegin ( char **expression, int size, subraster *basesp,
                   8519: 			int arg1, int arg2, int arg3 )
                   8520: {
                   8521: /* -------------------------------------------------------------------------
                   8522: Allocations and Declarations
                   8523: -------------------------------------------------------------------------- */
1.3     ! albertel 8524: char	*texsubexpr(), subexpr[MAXSUBXSZ+1], /* \begin{} environment params*/
1.1       albertel 8525: 	*exprptr=NULL,*begptr=NULL,*endptr=NULL,*braceptr=NULL; /* ptrs */
                   8526: char	*begtoken="\\begin{", *endtoken="\\end{"; /*tokens we're looking for*/
                   8527: int	strreplace();			/* replace substring in string */
                   8528: char	*strchange();			/*\begin...\end --> {\begin...\end}*/
                   8529: char	*delims = (char *)NULL;		/* mdelims[ienviron] */
                   8530: subraster *rasterize(), *sp=NULL;	/* rasterize environment */
                   8531: int	ienviron = 0;			/* environs[] index */
                   8532: int	nbegins = 0;			/* #\begins nested beneath this one*/
                   8533: int	envlen=0, sublen=0;		/* #chars in environ, subexpr */
                   8534: static	int blevel = 0;			/* \begin...\end nesting level */
                   8535: static	char *mdelims[] = { NULL, NULL, NULL, NULL,
                   8536: 	"()","[]","{}","||","==",	/* for pbBvVmatrix */
1.3     ! albertel 8537: 	NULL, NULL, NULL, NULL, "{.", NULL, NULL, NULL, NULL, NULL, NULL,
1.1       albertel 8538: 	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
                   8539: static	char *environs[] = {		/* types of environments we process*/
                   8540: 	"eqnarray",			/* 0 eqnarray environment */
                   8541: 	"array",			/* 1 array environment */
                   8542: 	"matrix",			/* 2 array environment */
                   8543: 	"tabular",			/* 3 array environment */
                   8544: 	"pmatrix",			/* 4 ( ) */
                   8545: 	"bmatrix",			/* 5 [ ] */
                   8546: 	"Bmatrix",			/* 6 { } */
                   8547: 	"vmatrix",			/* 7 | | */
                   8548: 	"Vmatrix",			/* 8 || || */
                   8549: 	"gather",			/* 9 gather environment */
                   8550: 	"align",			/* 10 align environment */
                   8551: 	"verbatim",			/* 11 verbatim environment */
                   8552: 	"picture",			/* 12 picture environment */
1.3     ! albertel 8553: 	"cases",			/* 13 cases environment */
        !          8554: 	"equation",			/* 14 for \begin{equation} */
1.1       albertel 8555: 	NULL };				/* trailer */
                   8556: /* -------------------------------------------------------------------------
                   8557: determine type of environment we're beginning
                   8558: -------------------------------------------------------------------------- */
                   8559: /* --- first bump nesting level --- */
                   8560: blevel++;				/* count \begin...\begin...'s */
                   8561: /* --- \begin must be followed by {type_of_environment} --- */
                   8562: exprptr = texsubexpr(*expression,subexpr,0,"{","}",0,0);
                   8563: if ( *subexpr == '\000' ) goto end_of_job; /* no environment given */
                   8564: while ( (delims=strchr(subexpr,'*')) != NULL ) /* have environment* */
                   8565:   strcpy(delims,delims+1);		/* treat it as environment */
                   8566: /* --- look up environment in our table --- */
                   8567: for ( ienviron=0; ;ienviron++ )		/* search table till NULL */
                   8568:   if ( environs[ienviron] == NULL )	/* found NULL before match */
                   8569:     goto end_of_job;			/* so quit */
                   8570:   else					/* see if we have an exact match */
                   8571:     if ( memcmp(environs[ienviron],subexpr,strlen(subexpr)) == 0 ) /*match*/
                   8572:       break;				/* leave loop with ienviron index */
                   8573: /* --- accumulate any additional params for this environment --- */
                   8574: *subexpr = '\000';			/* reset subexpr to empty string */
                   8575: delims = mdelims[ienviron];		/* mdelims[] string for ienviron */
                   8576: if ( delims != NULL )			/* add appropriate opening delim */
                   8577:   { strcpy(subexpr,"\\");		/* start with \ for (,[,{,|,= */
                   8578:     strcat(subexpr,delims);		/* then add opening delim */
                   8579:     subexpr[2] = '\000'; }		/* remove extraneous closing delim */
                   8580: switch ( ienviron )
                   8581:   {
                   8582:   default: goto end_of_job;		/* environ not implemented yet */
                   8583:   case 0:				/* \begin{eqnarray} */
                   8584:     strcpy(subexpr,"\\array{rcl$");	/* set default rcl for eqnarray */
                   8585:     break;
                   8586:   case 1:  case 2:  case 3:		/* \begin{array} followed by {lcr} */
                   8587:     strcpy(subexpr,"\\array{");		/*start with mimeTeX \array{ command*/
                   8588:     skipwhite(exprptr);			/* bump to next non-white char */
                   8589:     if ( *exprptr == '{' )		/* assume we have {lcr} argument */
                   8590:       {	exprptr = texsubexpr(exprptr,subexpr+7,0,"{","}",0,0); /*add on lcr*/
                   8591: 	if ( *(subexpr+7) == '\000' ) goto end_of_job; /* quit if no lcr */
                   8592: 	strcat(subexpr,"$"); }		/* add terminating $ to lcr */
                   8593:     break;
                   8594:   case 4:  case 5:  case 6:		/* \begin{pmatrix} or b,B,v,Vmatrix */
                   8595:   case 7:  case 8:
                   8596:     strcat(subexpr,"\\array{");		/*start with mimeTeX \array{ command*/
                   8597:     break;
                   8598:   case 9:				/* gather */
                   8599:     strcat(subexpr,"\\array{c$");	/* center equations */
                   8600:     break;
                   8601:   case 10:				/* align */
                   8602:     strcat(subexpr,"\\array{rclrclrclrclrclrcl$"); /* a&=b & c&=d & etc */
                   8603:     break;
                   8604:   case 11:				/* verbatim */
                   8605:     strcat(subexpr,"{\\rm ");		/* {\rm ...} */
                   8606:     /*strcat(subexpr,"\\\\{\\rm ");*/	/* \\{\rm } doesn't work in context */
                   8607:     break;
                   8608:   case 12:				/* picture */
                   8609:     strcat(subexpr,"\\picture");	/* picture environment */
                   8610:     skipwhite(exprptr);			/* bump to next non-white char */
                   8611:     if ( *exprptr == '(' )		/*assume we have (width,height) arg*/
                   8612:       {	exprptr = texsubexpr(exprptr,subexpr+8,0,"(",")",0,1); /*add on arg*/
                   8613: 	if ( *(subexpr+8) == '\000' ) goto end_of_job; } /* quit if no arg */
                   8614:     strcat(subexpr,"{");		/* opening {  after (width,height) */
                   8615:     break;
1.3     ! albertel 8616:   case 13:				/* cases */
        !          8617:     strcat(subexpr,"\\array{ll$");	/* a&b \\ c&d etc */
        !          8618:     break;
        !          8619:   case 14:				/* \begin{equation} */
        !          8620:     strcat(subexpr,"{");		/* just enclose expression in {}'s */
        !          8621:     break;
1.1       albertel 8622:   } /* --- end-of-switch(ienviron) --- */
                   8623: /* -------------------------------------------------------------------------
                   8624: locate matching \end{...}
                   8625: -------------------------------------------------------------------------- */
                   8626: /* --- first \end following \begin --- */
                   8627: if ( (endptr=strstr(exprptr,endtoken))	/* find 1st \end following \begin */
                   8628: ==   NULL ) goto end_of_job;		/* and quit if no \end found */
                   8629: /* --- find matching endptr by pushing past any nested \begin's --- */
                   8630: begptr = exprptr;			/* start after first \begin{...} */
                   8631: while ( 1 )				/*break when we find matching \end*/
                   8632:   {
                   8633:   /* --- first, set ptr to closing } terminating current \end{...} --- */
                   8634:   if ( (braceptr=strchr(endptr+1,'}'))	/* find 1st } following \end{ */
                   8635:   ==   NULL ) goto end_of_job;		/* and quit if no } found */
                   8636:   /* -- locate next nested \begin --- */
                   8637:   if ( (begptr=strstr(begptr,begtoken))	/* find next \begin{...} */
                   8638:   ==   NULL ) break;			/*no more, so we have matching \end*/
                   8639:   begptr += strlen(begtoken);		/* push ptr past token */
                   8640:   if ( begptr >= endptr ) break;	/* past endptr, so not nested */
                   8641:   /* --- have nested \begin, so push forward to next \end --- */
                   8642:   nbegins++;				/* count another nested \begin */
                   8643:   if ( (endptr=strstr(endptr+strlen(endtoken),endtoken)) /* find next \end */
                   8644:   ==   NULL ) goto end_of_job;		/* and quit if none found */
                   8645:   } /* --- end-of-while(1) --- */
                   8646: /* --- push expression past closing } of \end{} --- */
                   8647: *expression = braceptr+1;		/* resume processing after } */
                   8648: /* -------------------------------------------------------------------------
                   8649: add on everything (i.e., the ...'s) between \begin{}[{}] ... \end{}
                   8650: -------------------------------------------------------------------------- */
                   8651: /* --- add on everything, completing subexpr for \begin{}...\end{} --- */
                   8652: sublen = strlen(subexpr);		/* #chars in "preamble" */
                   8653: envlen = (int)(endptr-exprptr);		/* #chars between \begin{}{}...\end */
                   8654: memcpy(subexpr+sublen,exprptr,envlen);	/*concatanate environ after subexpr*/
                   8655: subexpr[sublen+envlen] = '\000';	/* and null-terminate */
                   8656: if ( 2 > 1 )				/* always... */
                   8657:   strcat(subexpr,"}");			/* ...followed by terminating } */
                   8658: /* --- add terminating \right), etc, if necessary --- */
                   8659: if ( delims != (char *)NULL )		/* need closing delim */
                   8660:  { strcat(subexpr,"\\");		/* start with \ for ),],},|,= */
                   8661:    strcat(subexpr,delims+1); }		/* add appropriate closing delim */
                   8662: /* -------------------------------------------------------------------------
                   8663: change nested \begin...\end to {\begin...\end} so \array{} can handle them
                   8664: -------------------------------------------------------------------------- */
                   8665: if ( nbegins > 0 )			/* have nested begins */
                   8666:  if ( blevel < 2 )			/* only need to do this once */
                   8667:   {
                   8668:   begptr = subexpr;			/* start at beginning of subexpr */
                   8669:   while( (begptr=strstr(begptr,begtoken)) != NULL ) /* have \begin{...} */
                   8670:     { strchange(0,begptr,"{");		/* \begin --> {\begin */
                   8671:       begptr += strlen(begtoken); }	/* continue past {\begin */
                   8672:   endptr = subexpr;			/* start at beginning of subexpr */
                   8673:   while( (endptr=strstr(endptr,endtoken)) != NULL ) /* have \end{...} */
                   8674:     if ( (braceptr=strchr(endptr+1,'}')) /* find 1st } following \end{ */
                   8675:     ==   NULL ) goto end_of_job;	/* and quit if no } found */
                   8676:     else				/* found terminating } */
                   8677:      { strchange(0,braceptr,"}");	/* \end{...} --> \end{...}} */
                   8678:        endptr = braceptr+1; }		/* continue past \end{...} */
                   8679:   } /* --- end-of-if(nbegins>0) --- */
                   8680: /* -------------------------------------------------------------------------
                   8681: post process as necessary
                   8682: -------------------------------------------------------------------------- */
                   8683: switch ( ienviron )
                   8684:   {
                   8685:   default: break;			/* no post-processing required */
                   8686:   case 10:				/* align */
                   8687:     strreplace(subexpr,"&=","#*@*#=",0); /* tag all &='s */
                   8688:     strreplace(subexpr,"&<","#*@*#<",0); /* tag all &<'s */
                   8689:     strreplace(subexpr,"&\\lt","#*@*#<",0); /* tag all &\lt's */
                   8690:     strreplace(subexpr,"&\\leq","#*@*#\\leq",0); /* tag all &\leq's */
                   8691:     strreplace(subexpr,"&>","#*@*#>",0); /* tag all &>'s */
                   8692:     strreplace(subexpr,"&\\gt","#*@*#>",0); /* tag all &\gt's */
                   8693:     strreplace(subexpr,"&\\geq","#*@*#\\geq",0); /* tag all &\geq's */
                   8694:     if ( nbegins < 1 )			/* don't modify nested arrays */
                   8695:       strreplace(subexpr,"&","\\hspace{10}&\\hspace{10}",0); /* add space */
                   8696:     strreplace(subexpr,"#*@*#=","& = &",0); /*restore and xlate tagged &='s*/
                   8697:     strreplace(subexpr,"#*@*#<","& \\lt &",0); /*restore, xlate tagged &<'s*/
                   8698:     strreplace(subexpr,"#*@*#\\leq","& \\leq &",0); /*xlate tagged &\leq's*/
                   8699:     strreplace(subexpr,"#*@*#>","& \\gt &",0); /*restore, xlate tagged &>'s*/
                   8700:     strreplace(subexpr,"#*@*#\\geq","& \\geq &",0); /*xlate tagged &\geq's*/
                   8701:     break;
                   8702:   case 11:				/* verbatim */
                   8703:     strreplace(subexpr,"\n","\\\\",0);	/* xlate \n newline to latex \\ */
                   8704:     /*strcat(subexpr,"\\\\");*/		/* add final latex \\ newline */
                   8705:     break;
                   8706:   case 12:				/* picture */
                   8707:     strreplace(subexpr,"\\put "," ",0);	/*remove \put's (not really needed)*/
                   8708:     strreplace(subexpr,"\\put(","(",0);	/*remove \put's (not really needed)*/
                   8709:     strreplace(subexpr,"\\oval","\\circle",0); /* actually an ellipse */
                   8710:     break;
                   8711:   } /* --- end-of-switch(ienviron) --- */
                   8712: /* -------------------------------------------------------------------------
                   8713: return rasterized mimeTeX equivalent of \begin{}...\end{} environment
                   8714: -------------------------------------------------------------------------- */
                   8715: /* --- debugging output --- */
                   8716: if ( msgfp!=NULL && msglevel>=99 )
                   8717:   fprintf(msgfp,"rastbegin> subexpr=%s\n",subexpr);
                   8718: /* --- rasterize mimeTeX equivalent of \begin{}...\end{} environment --- */
                   8719: sp = rasterize(subexpr,size);		/* rasterize subexpr */
                   8720: end_of_job:
                   8721:   blevel--;				/* decrement \begin nesting level */
                   8722:   return ( sp );			/* back to caller with sp or NULL */
                   8723: } /* --- end-of-function rastbegin() --- */
                   8724: 
                   8725: 
                   8726: /* ==========================================================================
                   8727:  * Function:	rastarray ( expression, size, basesp, arg1, arg2, arg3 )
                   8728:  * Purpose:	\array handler, returns a subraster corresponding to array
                   8729:  *		expression (immediately following \array) at font size
                   8730:  * --------------------------------------------------------------------------
                   8731:  * Arguments:	expression (I/O) char **  to first char of null-terminated
                   8732:  *				string immediately following \array to be
                   8733:  *				rasterized, and returning ptr immediately
                   8734:  *				following last character processed.
                   8735:  *		size (I)	int containing 0-4 default font size
                   8736:  *		basesp (I)	subraster *  to character (or subexpression)
                   8737:  *				immediately preceding \array
                   8738:  *				(unused, but passed for consistency)
                   8739:  *		arg1 (I)	int unused
                   8740:  *		arg2 (I)	int unused
                   8741:  *		arg3 (I)	int unused
                   8742:  * --------------------------------------------------------------------------
                   8743:  * Returns:	( subraster * )	ptr to subraster corresponding to array
                   8744:  *				expression, or NULL for any parsing error
                   8745:  * --------------------------------------------------------------------------
                   8746:  * Notes:     o	Summary of syntax...
                   8747:  *			\array{3,lcrBC$a&b&c\\d&e&f\\etc}
                   8748:  *	      o	The 3,lcrBC$ part is an optional "preamble".  The lcr means
                   8749:  *		what you think, i.e., "horizontal" left,center,right
                   8750:  *		justification down corresponding column.  The new BC means
                   8751:  *		"vertical" baseline,center justification across corresponding
                   8752:  *		row.  The leading 3 specifies the font size 0-4 to be used.
                   8753:  *		You may also specify +1,-1,+2,-2, etc, which is used as an
                   8754:  *		increment to the current font size, e.g., -1,lcr$ uses
                   8755:  *		one font size smaller than current.  Without a leading
                   8756:  *		+ or -, the font size is "absolute".
                   8757:  *	      o	The preamble can also be just lcrBC$ without a leading
                   8758:  *		size-part, or just 3$ without a trailing lcrBC-part.
                   8759:  *		The default size is whatever is current, and the
                   8760:  *		default justification is c(entered) and B(aseline).
                   8761:  * ======================================================================= */
                   8762: /* --- entry point --- */
                   8763: subraster *rastarray ( char **expression, int size, subraster *basesp,
                   8764: 			int arg1, int arg2, int arg3 )
                   8765: {
                   8766: /* -------------------------------------------------------------------------
                   8767: Allocations and Declarations
                   8768: -------------------------------------------------------------------------- */
1.3     ! albertel 8769: char	*texsubexpr(), subexpr[MAXSUBXSZ+1], *exprptr, /*parse array subexpr*/
        !          8770: 	 subtok[MAXTOKNSZ+1], *subptr=subtok, /* &,\\ inside { } not a delim*/
        !          8771: 	 token[MAXTOKNSZ+1],  *tokptr=token, /* subexpr token to rasterize */
1.1       albertel 8772: 	*preamble(),   *preptr=token;	/*process optional size,lcr preamble*/
                   8773: char	*coldelim="&", *rowdelim="\\";	/* need escaped rowdelim */
                   8774: int	maxarraysz = 64;		/* max #rows, cols */
                   8775: 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 */
                   8776: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   8777: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
                   8778: 	  hline[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* hline above row? */
                   8779: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   8780: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
                   8781: 	  vline[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*vline left of col?*/
                   8782: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   8783: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
                   8784:        colwidth[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*widest tokn in col*/
                   8785: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   8786: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
                   8787:       rowheight[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* "highest" in row */
                   8788: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   8789: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
                   8790:      fixcolsize[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*1=fixed col width*/
                   8791: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   8792: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
                   8793:      fixrowsize[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*1=fixed row height*/
                   8794: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   8795: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
                   8796:       rowbaseln[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* baseline for row */
                   8797: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   8798: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
                   8799:       rowcenter[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*true = vcenter row*/
                   8800: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   8801: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
                   8802: static int /* --- propagate global values across arrays --- */
                   8803:        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 */
                   8804: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   8805: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
                   8806:       gcolwidth[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*widest tokn in col*/
                   8807: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   8808: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
                   8809:      growheight[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* "highest" in row */
                   8810: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   8811: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
                   8812:     gfixcolsize[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*1=fixed col width*/
                   8813: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   8814: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
                   8815:     gfixrowsize[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*1=fixed row height*/
                   8816: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   8817: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
                   8818:      growcenter[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*true = vcenter row*/
                   8819: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   8820: 	               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
                   8821: int	rowglobal=0, colglobal=0,	/* true to set global values */
                   8822: 	rowpropagate=0, colpropagate=0;	/* true if propagating values */
                   8823: int	irow,nrows=0, icol,ncols[65],	/*#rows in array, #cols in each row*/
                   8824: 	maxcols=0;			/* max# cols in any single row */
                   8825: int	itoken, ntokens=0,		/* index, total #tokens in array */
                   8826: 	subtoklen=0,			/* strlen of {...} subtoken */
                   8827: 	istokwhite=1,			/* true if token all whitespace */
                   8828: 	nnonwhite=0;			/* #non-white tokens */
                   8829: int	isescape=0,wasescape=0,		/* current,prev chars escape? */
                   8830: 	ischarescaped=0,		/* is current char escaped? */
                   8831: 	nescapes=0;			/* #consecutive escapes */
                   8832: subraster *rasterize(), *toksp[1025],	/* rasterize tokens */
                   8833: 	*new_subraster(), *arraysp=NULL; /* subraster for entire array */
                   8834: raster	*arrayrp=NULL;			/* raster for entire array */
                   8835: int	delete_subraster();		/* free toksp[] workspace at eoj */
                   8836: int	rowspace=2, colspace=4,		/* blank space between rows, cols */
                   8837: 	hspace=1, vspace=1;		/*space to accommodate hline,vline*/
                   8838: int	width=0, height=0,		/* width,height of array */
                   8839: 	leftcol=0, toprow=0;		/*upper-left corner for cell in it*/
                   8840: int	rastput();			/* embed tokens/cells in array */
                   8841: int	rule_raster();			/* draw hlines and vlines in array */
                   8842: char	*hlchar="\\hline", *hdchar="\\hdash"; /* token signals hline */
                   8843: char	*texchar(), hltoken[1025];	/* extract \hline from token */
                   8844: int	ishonly=0, hltoklen, minhltoklen=3; /*flag, token must be \hl or \hd*/
                   8845: int	isnewrow=1;			/* true for new row */
                   8846: int	pixsz = 1;			/*default #bits per pixel, 1=bitmap*/
                   8847: /* -------------------------------------------------------------------------
                   8848: Macros to determine extra raster space required for vline/hline
                   8849: -------------------------------------------------------------------------- */
                   8850: #define	vlinespace(icol) \
                   8851: 	( vline[icol] == 0?  0 :	/* no vline so no space needed */   \
                   8852: 	  ( icol<1 || icol>=maxcols? vspace+(colspace+1)/2 : vspace ) )
                   8853: #define	hlinespace(irow) \
                   8854: 	( hline[irow] == 0?  0 :	/* no hline so no space needed */   \
                   8855: 	  ( irow<1 || irow>=nrows? hspace+(rowspace+1)/2 : hspace ) )
                   8856: /* -------------------------------------------------------------------------
                   8857: Obtain array subexpression
                   8858: -------------------------------------------------------------------------- */
                   8859: /* --- parse for array subexpression, and bump expression past it --- */
                   8860: subexpr[1] = *subexpr = ' ';		/* set two leading blanks */
                   8861: *expression = texsubexpr(*expression,subexpr+2,0,"{","}",0,0);
                   8862: if ( msglevel>=29 && msgfp!=NULL )	/* debugging, display array */
                   8863:   fprintf(msgfp,"rastarray> %.256s\n",subexpr+2);
                   8864: if ( *(subexpr+2)=='\000' )		/* couldn't get subexpression */
                   8865:   goto end_of_job;			/* nothing to do, so quit */
                   8866: /* -------------------------------------------------------------------------
                   8867: process optional size,lcr preamble if present
                   8868: -------------------------------------------------------------------------- */
                   8869: /* --- reset size, get lcr's, and push exprptr past preamble --- */
                   8870: exprptr = preamble(subexpr+2,&size,preptr); /* reset size and get lcr's */
                   8871: /* --- init with global values --- */
                   8872: for(icol=0; icol<=maxarraysz; icol++) {	/* propagate global values... */
                   8873:   justify[icol] = gjustify[icol];	/* -1,0,+1 = l,c,r */
                   8874:   colwidth[icol] = gcolwidth[icol];	/* column width */
                   8875:   rowheight[icol] = growheight[icol];	/* row height */
                   8876:   fixcolsize[icol] = gfixcolsize[icol];	/* 1=fixed col width */
                   8877:   fixrowsize[icol] = gfixrowsize[icol];	/* 1=fixed row height */
                   8878:   rowcenter[icol] = growcenter[icol]; }	/* true = vcenter row */
                   8879: /* --- process lcr's, etc in preamble --- */
                   8880: itoken = 0;				/* debugging flag */
                   8881: if ( msglevel>=29 && msgfp!=NULL )	/* debugging, display preamble */
                   8882:  if ( *preptr != '\000' )		/* if we have one */
                   8883:   fprintf(msgfp,"rastarray> preamble= \"%.256s\"\nrastarray> preamble: ",
                   8884:   preptr);
                   8885: irow = icol = 0;			/* init lcr counts */
                   8886: while (  *preptr != '\000' )		/* check preamble text for lcr */
                   8887:   {
                   8888:   char	prepchar = *preptr;		/* current preamble character */
                   8889:   int	prepcase = (islower(prepchar)?1:(isupper(prepchar)?2:0)); /*1,2,or 0*/
                   8890:   if ( irow<maxarraysz && icol<maxarraysz )
                   8891:    switch ( /*tolower*/(prepchar) )
                   8892:     {  default: break;			/* just flush unrecognized chars */
                   8893:       case 'l': justify[icol] = (-1);		/*left-justify this column*/
                   8894: 		if (colglobal) gjustify[irow] = justify[irow]; break;
                   8895:       case 'c': justify[icol] = (0);		/* center this column */
                   8896: 		if (colglobal) gjustify[irow] = justify[irow]; break;
                   8897:       case 'r': justify[icol] = (+1);		/* right-justify this col */
                   8898: 		if (colglobal) gjustify[irow] = justify[irow]; break;
                   8899:       case '|': vline[icol] += 1;   break;	/* solid vline left of col */
                   8900:       case '.': vline[icol] = (-1); break;	/*dashed vline left of col */
                   8901:       case 'b': prepchar='B'; prepcase=2;	/* alias for B */
                   8902:       case 'B': break;				/* baseline-justify row */
                   8903:       case 'v': prepchar='C'; prepcase=2;	/* alias for C */
                   8904:       case 'C': rowcenter[irow] = 1;		/* vertically center row */
                   8905: 		if (rowglobal) growcenter[irow] = rowcenter[irow]; break;
                   8906:       case 'g': colglobal=1; prepcase=0; break;	/* set global col values */
                   8907:       case 'G': rowglobal=1; prepcase=0; break;	/* set global row values */
                   8908:       case '#': colglobal=rowglobal=1; break; }	/* set global col,row vals */
                   8909:   if ( msglevel>=29 && msgfp!=NULL )	/* debugging */
                   8910:     fprintf(msgfp," %c[%d]",prepchar,
                   8911:     (prepcase==1?icol+1:(prepcase==2?irow+1:0)));
                   8912:   preptr++;				/* check next char for lcr */
                   8913:   itoken++;				/* #lcr's processed (debugging only)*/
                   8914:   /* --- check for number or +number specifying colwidth or rowheight --- */
                   8915:   if ( prepcase != 0 )			/* only check upper,lowercase */
                   8916:    {
                   8917:    int	ispropagate = (*preptr=='+'?1:0); /* leading + propagates width/ht */
1.3     ! albertel 8918:    if ( ispropagate ) {			/* set row or col propagation */
1.1       albertel 8919:      if ( prepcase == 1 ) colpropagate = 1; /* propagating col values */
1.3     ! albertel 8920:      else if ( prepcase == 2 ) rowpropagate = 1; } /*propagating row values*/
1.1       albertel 8921:    if ( !colpropagate && prepcase == 1 )
                   8922:       {	colwidth[icol] = 0;		/* reset colwidth */
                   8923: 	fixcolsize[icol] = 0; }		/* reset width flag */
                   8924:    if ( !rowpropagate && prepcase == 2 )
                   8925:       {	rowheight[irow] = 0;		/* reset row height */
                   8926: 	fixrowsize[irow] = 0; }		/* reset height flag */
                   8927:    if ( ispropagate ) preptr++;		/* bump past leading + */
                   8928:    if ( isdigit(*preptr) )		/* digit follows character */
                   8929:      { char *endptr = NULL;		/* preptr set to 1st char after num*/
                   8930:        int size = (int)(strtol(preptr,&endptr,10)); /* interpret number */
                   8931:        char *whchars="?wh";		/* debugging width/height labels */
                   8932:        preptr = endptr;			/* skip over all digits */
                   8933:        if ( size==0 || (size>=3&&size<=500) ) { /* sanity check */
                   8934: 	int index;			/* icol,irow...maxarraysz index */
                   8935: 	if ( prepcase == 1 )		/* lowercase signifies colwidth */
                   8936: 	 for(index=icol; index<=maxarraysz; index++) { /*propagate col size*/
                   8937: 	  colwidth[index] = size;	/* set colwidth to fixed size */
                   8938: 	  fixcolsize[index] = (size>0?1:0); /* set fixed width flag */
                   8939: 	  justify[index] = justify[icol]; /* and propagate justification */
                   8940: 	  if ( colglobal ) {		/* set global values */
                   8941: 	    gcolwidth[index] = colwidth[index]; /* set global col width */
                   8942: 	    gfixcolsize[index] = fixcolsize[index]; /*set global width flag*/
                   8943: 	    gjustify[index] = justify[icol]; } /* set global col justify */
                   8944: 	  if ( !ispropagate ) break; }	/* don't propagate */
                   8945: 	else				/* uppercase signifies rowheight */
                   8946: 	 for(index=irow; index<=maxarraysz; index++) { /*propagate row size*/
                   8947: 	  rowheight[index] = size;	/* set rowheight to size */
                   8948: 	  fixrowsize[index] = (size>0?1:0); /* set fixed height flag */
                   8949: 	  rowcenter[index] = rowcenter[irow]; /* and propagate row center */
                   8950: 	  if ( rowglobal ) {		/* set global values */
                   8951: 	    growheight[index] = rowheight[index]; /* set global row height */
                   8952: 	    gfixrowsize[index] = fixrowsize[index]; /*set global height flag*/
                   8953: 	    growcenter[index] = rowcenter[irow]; } /*set global row center*/
                   8954: 	  if ( !ispropagate ) break; }	/* don't propagate */
                   8955:         } /* --- end-of-if(size>=3&&size<=500) --- */
                   8956:        if ( msglevel>=29 && msgfp!=NULL ) /* debugging */
                   8957: 	 fprintf(msgfp,":%c=%d/fix#%d",whchars[prepcase],
                   8958: 	 (prepcase==1?colwidth[icol]:rowheight[irow]),
                   8959: 	 (prepcase==1?fixcolsize[icol]:fixrowsize[irow]));
                   8960:      } /* --- end-of-if(isdigit()) --- */
                   8961:    } /* --- end-of-if(prepcase!=0) --- */
                   8962:   if ( prepcase == 1 ) icol++;		/* bump col if lowercase lcr */
                   8963:     else if ( prepcase == 2 ) irow++;	/* bump row if uppercase BC */
                   8964:   } /* --- end-of-while(*preptr!='\000') --- */
                   8965: if ( msglevel>=29 && msgfp!=NULL )	/* debugging, emit final newline */
                   8966:  if ( itoken > 0 )			/* if we have preamble */
                   8967:   fprintf(msgfp,"\n");
                   8968: /* -------------------------------------------------------------------------
                   8969: tokenize and rasterize components  a & b \\ c & d \\ etc  of subexpr
                   8970: -------------------------------------------------------------------------- */
                   8971: /* --- rasterize tokens one at a time, and maintain row,col counts --- */
                   8972: ncols[nrows] = 0;			/* no tokens/cols in top row yet */
                   8973: while ( 1 )				/* scan chars till end */
                   8974:   {
                   8975:   /* --- local control flags --- */
                   8976:   int	iseox = (*exprptr == '\000'),	/* null signals end-of-expression */
                   8977: 	iseor = iseox,			/* \\ or eox signals end-of-row */
                   8978: 	iseoc = iseor;			/* & or eor signals end-of-col */
                   8979:   /* --- check for escapes --- */
                   8980:   isescape = isthischar(*exprptr,ESCAPE); /* is current char escape? */
                   8981:   wasescape= (!isnewrow&&isthischar(*(exprptr-1),ESCAPE)); /*prev char esc?*/
                   8982:   nescapes = (wasescape?nescapes+1:0);	/* # preceding consecutive escapes */
                   8983:   ischarescaped = (nescapes%2==0?0:1);	/* is current char escaped? */
                   8984:   /* -----------------------------------------------------------------------
                   8985:   check for {...} subexpression starting from where we are now
                   8986:   ------------------------------------------------------------------------ */
                   8987:   if ( *exprptr == '{'			/* start of {...} subexpression */
                   8988:   &&   !ischarescaped )			/* if not escaped \{ */
                   8989:     {
                   8990:     subptr = texsubexpr(exprptr,subtok,4095,"{","}",1,1); /*entire subexpr*/
                   8991:     subtoklen = strlen(subtok);		/* #chars in {...} */
                   8992:     memcpy(tokptr,exprptr,subtoklen);	/* copy {...} to accumulated token */
                   8993:     tokptr  += subtoklen;		/* bump tokptr to end of token */
                   8994:     exprptr += subtoklen;		/* and bump exprptr past {...} */
                   8995:     istokwhite = 0;			/* signal non-empty token */
                   8996:     continue;				/* continue with char after {...} */
                   8997:     } /* --- end-of-if(*exprptr=='{') --- */
                   8998:   /* -----------------------------------------------------------------------
                   8999:   check for end-of-row(\\) and/or end-of-col(&)
                   9000:   ------------------------------------------------------------------------ */
                   9001:   /* --- check for (escaped) end-of-row delimiter --- */
                   9002:   if ( isescape && !ischarescaped )	/* current char is escaped */
                   9003:     if ( isthischar(*(exprptr+1),rowdelim) /* next char is rowdelim */
                   9004:     ||   *(exprptr+1) == '\000' )	/* or a pathological null */
                   9005:       {	iseor = 1;			/* so set end-of-row flag */
                   9006: 	wasescape=isescape=nescapes = 0; } /* reset flags for new row */
                   9007:   /* --- check for end-of-col delimiter --- */
                   9008:   if (iseor				/* end-of-row signals end-of-col */
                   9009:   ||  (!ischarescaped&&isthischar(*exprptr,coldelim))) /*or unescaped coldel*/
                   9010:       iseoc = 1;			/* so set end-of-col flag */
                   9011:   /* -----------------------------------------------------------------------
                   9012:   rasterize completed token
                   9013:   ------------------------------------------------------------------------ */
                   9014:   if ( iseoc )				/* we have a completed token */
                   9015:     {
                   9016:     *tokptr = '\000';			/* first, null-terminate token */
                   9017:     /* --- check first token in row for \hline or \hdash --- */
                   9018:     ishonly = 0;			/*init for token not only an \hline*/
                   9019:     if ( ncols[nrows] == 0 )		/*\hline must be first token in row*/
                   9020:       {
                   9021:       tokptr=token; skipwhite(tokptr);	/* skip whitespace after // */
                   9022:       tokptr = texchar(tokptr,hltoken);	/* extract first char from token */
                   9023:       hltoklen = strlen(hltoken);	/* length of first char */
1.3     ! albertel 9024:       if ( hltoklen >= minhltoklen ) {	/*token must be at least \hl or \hd*/
1.1       albertel 9025: 	if ( memcmp(hlchar,hltoken,hltoklen) == 0 ) /* we have an \hline */
                   9026: 	   hline[nrows] += 1;		/* bump \hline count for row */
                   9027: 	else if ( memcmp(hdchar,hltoken,hltoklen) == 0 ) /*we have an \hdash*/
1.3     ! albertel 9028: 	   hline[nrows] = (-1); }	/* set \hdash flag for row */
1.1       albertel 9029:       if ( hline[nrows] != 0 )		/* \hline or \hdash prefixes token */
                   9030: 	{ skipwhite(tokptr);		/* flush whitespace after \hline */
                   9031: 	  if ( *tokptr == '\000'	/* end-of-expression after \hline */
                   9032: 	  ||   isthischar(*tokptr,coldelim) ) /* or unescaped coldelim */
1.2       albertel 9033: 	    { istokwhite = 1;		/* so token contains \hline only */
                   9034: 	      if ( iseox ) ishonly = 1; } /* ignore entire row at eox */
1.1       albertel 9035: 	  else				/* token contains more than \hline */
                   9036: 	    strcpy(token,tokptr); }	/* so flush \hline from token */
                   9037:       } /* --- end-of-if(ncols[nrows]==0) --- */
                   9038:     /* --- rasterize completed token --- */
                   9039:     toksp[ntokens] = (istokwhite? NULL : /* don't rasterize empty token */
                   9040:       rasterize(token,size));		/* rasterize non-empty token */
                   9041:     if ( toksp[ntokens] != NULL )	/* have a rasterized token */
                   9042:       nnonwhite++;			/* bump rasterized token count */
                   9043:     /* --- maintain colwidth[], rowheight[] max, and rowbaseln[] --- */
                   9044:     if ( toksp[ntokens] != NULL )	/* we have a rasterized token */
                   9045:       {
                   9046:       /* --- update max token "height" in current row, and baseline --- */
                   9047:       int twidth = ((toksp[ntokens])->image)->width,  /* width of token */
                   9048: 	theight = ((toksp[ntokens])->image)->height, /* height of token */
                   9049: 	tbaseln =  (toksp[ntokens])->baseline, /* baseline of token */
                   9050: 	rheight = rowheight[nrows],	/* current max height for row */
                   9051: 	rbaseln = rowbaseln[nrows];	/* current baseline for max height */
                   9052:       if ( 0 || fixrowsize[nrows]==0 )	/* rowheight not fixed */
                   9053:        rowheight[nrows] = /*max2( rheight,*/( /* current (max) rowheight */
                   9054: 	max2(rbaseln+1,tbaseln+1)	/* max height above baseline */
                   9055: 	+ max2(rheight-rbaseln-1,theight-tbaseln-1) ); /* plus max below */
                   9056:       rowbaseln[nrows] = max2(rbaseln,tbaseln); /*max space above baseline*/
                   9057:       /* --- update max token width in current column --- */
                   9058:       icol = ncols[nrows];		/* current column index */
                   9059:       if ( 0 || fixcolsize[icol]==0 )	/* colwidth not fixed */
                   9060:        colwidth[icol] = max2(colwidth[icol],twidth); /*widest token in col*/
                   9061:       } /* --- end-of-if(toksp[]!=NULL) --- */
                   9062:     /* --- bump counters --- */
                   9063:     if ( !ishonly )			/* don't count only an \hline */
                   9064:       {	ntokens++;			/* bump total token count */
                   9065: 	ncols[nrows] += 1; }		/* and bump #cols in current row */
                   9066:     /* --- get ready for next token --- */
                   9067:     tokptr = token;			/* reset ptr for next token */
                   9068:     istokwhite = 1;			/* next token starts all white */
                   9069:     } /* --- end-of-if(iseoc) --- */
                   9070:   /* -----------------------------------------------------------------------
                   9071:   bump row as necessary
                   9072:   ------------------------------------------------------------------------ */
                   9073:   if ( iseor )				/* we have a completed row */
                   9074:     {
                   9075:     maxcols = max2(maxcols,ncols[nrows]); /* max# cols in array */
                   9076:     if ( ncols[nrows]>0 || hline[nrows]==0 ) /*ignore row with only \hline*/
                   9077:       nrows++;				/* bump row count */
                   9078:     ncols[nrows] = 0;			/* no cols in this row yet */
                   9079:     if ( !iseox )			/* don't have a null yet */
                   9080:       {	exprptr++;			/* bump past extra \ in \\ delim */
                   9081: 	iseox = (*exprptr == '\000'); }	/* recheck for pathological \null */
                   9082:     isnewrow = 1;			/* signal start of new row */
                   9083:     } /* --- end-of-if(iseor) --- */
                   9084:   else
                   9085:     isnewrow = 0;			/* no longer first col of new row */
                   9086:   /* -----------------------------------------------------------------------
                   9087:   quit when done, or accumulate char in token and proceed to next char
                   9088:   ------------------------------------------------------------------------ */
                   9089:   /* --- quit when done --- */
                   9090:   if ( iseox ) break;			/* null terminator signalled done */
                   9091:   /* --- accumulate chars in token --- */
                   9092:   if ( !iseoc )				/* don't accumulate delimiters */
                   9093:     { *tokptr++ = *exprptr;		/* accumulate non-delim char */
                   9094:       if ( !isthischar(*exprptr,WHITESPACE) ) /* this token isn't empty */
                   9095: 	istokwhite = 0; }		/* so reset flag to rasterize it */
                   9096:   /* --- ready for next char --- */
                   9097:   exprptr++;				/* bump ptr */
                   9098:   } /* --- end-of-while(*exprptr!='\000') --- */
                   9099: /* --- make sure we got something to do --- */
                   9100: if ( nnonwhite < 1 )			/* completely empty array */
                   9101:   goto end_of_job;			/* NULL back to caller */
                   9102: /* -------------------------------------------------------------------------
                   9103: determine dimensions of array raster and allocate it
                   9104: -------------------------------------------------------------------------- */
                   9105: /* --- adjust colspace --- */
                   9106: colspace = 2 + 2*size;			/* temp kludge */
                   9107: /* --- reset propagated sizes at boundaries of array --- */
                   9108: colwidth[maxcols] = rowheight[nrows] = 0; /* reset explicit 0's at edges */
                   9109: /* --- determine width of array raster --- */
                   9110: width = colspace*(maxcols-1);		/* empty space between cols */
                   9111: if ( msglevel>=29 && msgfp!=NULL )	/* debugging */
                   9112:   fprintf(msgfp,"rastarray> %d cols,  widths: ",maxcols);
                   9113: for ( icol=0; icol<=maxcols; icol++ )	/* and for each col */
                   9114:   { width += colwidth[icol];		/*width of this col (0 for maxcols)*/
                   9115:     width += vlinespace(icol);		/*plus space for vline, if present*/
                   9116:     if ( msglevel>=29 && msgfp!=NULL )	/* debugging */
                   9117:      fprintf(msgfp," %d=%2d+%d",icol+1,colwidth[icol],(vlinespace(icol))); }
                   9118: /* --- determine height of array raster --- */
                   9119: height = rowspace*(nrows-1);		/* empty space between rows */
                   9120: if ( msglevel>=29 && msgfp!=NULL )	/* debugging */
                   9121:   fprintf(msgfp,"\nrastarray> %d rows, heights: ",nrows);
                   9122: for ( irow=0; irow<=nrows; irow++ )	/* and for each row */
                   9123:   { height += rowheight[irow];		/*height of this row (0 for nrows)*/
                   9124:     height += hlinespace(irow);		/*plus space for hline, if present*/
                   9125:     if ( msglevel>=29 && msgfp!=NULL )	/* debugging */
                   9126:      fprintf(msgfp," %d=%2d+%d",irow+1,rowheight[irow],(hlinespace(irow))); }
                   9127: /* --- allocate subraster and raster for array --- */
                   9128: if ( msglevel>=29 && msgfp!=NULL )	/* debugging */
                   9129:   fprintf(msgfp,"\nrastarray> tot width=%d(colspc=%d) height=%d(rowspc=%d)\n",
                   9130:   width,colspace, height,rowspace);
                   9131: if ( (arraysp=new_subraster(width,height,pixsz)) /* allocate new subraster */
                   9132: ==   NULL )  goto end_of_job;		/* quit if failed */
                   9133: /* --- initialize subraster parameters --- */
                   9134: arraysp->type = IMAGERASTER;		/* image */
                   9135: arraysp->symdef = NULL;			/* not applicable for image */
                   9136: arraysp->baseline=min2(height/2+5,height-1); /*is a little above center good?*/
                   9137: arraysp->size = size;			/* size (probably unneeded) */
                   9138: arrayrp = arraysp->image;		/* raster embedded in subraster */
                   9139: /* -------------------------------------------------------------------------
                   9140: embed tokens/cells in array
                   9141: -------------------------------------------------------------------------- */
                   9142: itoken = 0;				/* start with first token */
                   9143: toprow = 0;				/* start at top row of array */
                   9144: for ( irow=0; irow<=nrows; irow++ )	/*tokens were accumulated row-wise*/
                   9145:   {
                   9146:   /* --- initialization for row --- */
                   9147:   int	baseline = rowbaseln[irow];	/* baseline for this row */
                   9148:   if ( hline[irow] != 0 )		/* need hline above this row */
                   9149:     { int hrow = (irow<1? 0 : toprow - rowspace/2); /* row for hline */
                   9150:       if ( irow >= nrows ) hrow = height-1; /* row for bottom hline */
                   9151:       rule_raster(arrayrp,hrow,0,width,1,(hline[irow]<0?1:0)); } /* hline */
                   9152:   if ( irow >= nrows ) break;		/*just needed \hline for irow=nrows*/
                   9153:   toprow += hlinespace(irow);		/* space for hline above irow */
                   9154:   leftcol = 0;				/* start at leftmost column */
                   9155:   for ( icol=0; icol<ncols[irow]; icol++ ) /* go through cells in this row */
                   9156:     {
                   9157:     subraster *tsp = toksp[itoken];	/* token that belongs in this cell */
1.2       albertel 9158:     /* --- first adjust leftcol for vline to left of icol, if present ---- */
                   9159:     leftcol += vlinespace(icol);	/* space for vline to left of col */
                   9160:     /* --- now rasterize cell ---- */
1.1       albertel 9161:     if ( tsp != NULL )			/* have a rasterized cell token */
                   9162:       {
                   9163:       /* --- local parameters --- */
                   9164:       int cwidth = colwidth[icol],	/* total column width */
                   9165: 	  twidth = (tsp->image)->width,	/* token width */
                   9166: 	  theight= (tsp->image)->height, /* token height */
                   9167: 	  tokencol = 0,			/*H offset (init for left justify)*/
                   9168: 	  tokenrow = baseline - tsp->baseline;/*V offset (init for baseline)*/
                   9169:       /* --- adjust leftcol for vline to left of icol, if present ---- */
1.2       albertel 9170:       /*leftcol += vlinespace(icol);*/	/* space for vline to left of col */
1.1       albertel 9171:       /* --- reset justification (if not left-justified) --- */
                   9172:       if ( justify[icol] == 0 )		/* but user wants it centered */
                   9173: 	  tokencol = (cwidth-twidth+1)/2; /* so split margin left/right */
                   9174:       else if ( justify[icol] == 1 )	/* or user wants right-justify */
                   9175: 	  tokencol = cwidth-twidth;	/* so put entire margin at left */
                   9176:       /* --- reset vertical centering (if not baseline-aligned) --- */
                   9177:       if ( rowcenter[irow] )		/* center cells in row vertically */
                   9178: 	  tokenrow = (rowheight[irow]-theight)/2; /* center row */
                   9179:       /* --- embed token raster at appropriate place in array raster --- */
                   9180:       rastput(arrayrp,tsp->image,	/* overlay cell token in array */
                   9181: 	  toprow+ tokenrow,		/*with aligned baseline or centered*/
                   9182: 	  leftcol+tokencol, 1);		/* and justified as requested */
                   9183:       } /* --- end-of-if(tsp!=NULL) --- */
                   9184:     itoken++;				/* bump index for next cell */
                   9185:     leftcol += colwidth[icol] + colspace /*move leftcol right for next col*/
                   9186:       /* + vlinespace(icol) */ ; /*don't add space for vline to left of col*/
                   9187:     } /* --- end-of-for(icol) --- */
                   9188:   toprow += rowheight[irow] + rowspace;	/* move toprow down for next row */
                   9189:   } /* --- end-of-for(irow) --- */
                   9190: /* -------------------------------------------------------------------------
                   9191: draw vlines as necessary
                   9192: -------------------------------------------------------------------------- */
                   9193: leftcol = 0;				/* start at leftmost column */
                   9194: for ( icol=0; icol<=maxcols; icol++ )	/* check each col for a vline */
                   9195:   {
                   9196:   if ( vline[icol] != 0 )		/* need vline to left of this col */
                   9197:     { int vcol = (icol<1? 0 : leftcol - colspace/2); /* column for vline */
                   9198:       if ( icol >= maxcols ) vcol = width-1; /*column for right edge vline*/
                   9199:       rule_raster(arrayrp,0,vcol,1,height,(vline[icol]<0?2:0)); } /* vline */
                   9200:   leftcol += vlinespace(icol);		/* space for vline to left of col */
                   9201:   if ( icol < maxcols )			/* don't address past end of array */
                   9202:     leftcol += colwidth[icol] + colspace; /*move leftcol right for next col*/
                   9203:   } /* --- end-of-for(icol) --- */
                   9204: /* -------------------------------------------------------------------------
                   9205: free workspace and return final result to caller
                   9206: -------------------------------------------------------------------------- */
                   9207: end_of_job:
                   9208:   /* --- free workspace --- */
                   9209:   if ( ntokens > 0 )			/* if we have workspace to free */
                   9210:     while ( --ntokens >= 0 )		/* free each token subraster */
                   9211:       if ( toksp[ntokens] != NULL )	/* if we rasterized this cell */
                   9212: 	delete_subraster(toksp[ntokens]); /* then free it */
                   9213:   /* --- return final result to caller --- */
                   9214:   return ( arraysp );
                   9215: } /* --- end-of-function rastarray() --- */
                   9216: 
                   9217: 
                   9218: /* ==========================================================================
                   9219:  * Function:	rastpicture ( expression, size, basesp, arg1, arg2, arg3 )
                   9220:  * Purpose:	\picture handler, returns subraster corresponding to picture
                   9221:  *		expression (immediately following \picture) at font size
                   9222:  * --------------------------------------------------------------------------
                   9223:  * Arguments:	expression (I/O) char **  to first char of null-terminated
                   9224:  *				string immediately following \picture to be
                   9225:  *				rasterized, and returning ptr immediately
                   9226:  *				following last character processed.
                   9227:  *		size (I)	int containing 0-4 default font size
                   9228:  *		basesp (I)	subraster *  to character (or subexpression)
                   9229:  *				immediately preceding \picture
                   9230:  *				(unused, but passed for consistency)
                   9231:  *		arg1 (I)	int unused
                   9232:  *		arg2 (I)	int unused
                   9233:  *		arg3 (I)	int unused
                   9234:  * --------------------------------------------------------------------------
                   9235:  * Returns:	( subraster * )	ptr to subraster corresponding to picture
                   9236:  *				expression, or NULL for any parsing error
                   9237:  * --------------------------------------------------------------------------
                   9238:  * Notes:     o	Summary of syntax...
                   9239:  *		  \picture(width,height){(x,y){pic_elem}~(x,y){pic_elem}~etc}
                   9240:  *	      o	
                   9241:  * ======================================================================= */
                   9242: /* --- entry point --- */
                   9243: subraster *rastpicture ( char **expression, int size, subraster *basesp,
                   9244: 			int arg1, int arg2, int arg3 )
                   9245: {
                   9246: /* -------------------------------------------------------------------------
                   9247: Allocations and Declarations
                   9248: -------------------------------------------------------------------------- */
                   9249: char	*texsubexpr(), picexpr[2049], *picptr=picexpr, /* picture {expre} */
                   9250: 	putexpr[256], *putptr,*multptr,	/*[multi]put (x,y[;xinc,yinc;num])*/
                   9251: 	pream[64], *preptr,		/* optional put preamble */
                   9252: 	picelem[1025];			/* picture element following put */
                   9253: subraster   *rasterize(), *picelemsp=NULL, /* rasterize picture elements */
                   9254: 	*new_subraster(), *picturesp=NULL, /* subraster for entire picture */
                   9255: 	*oldworkingbox = workingbox;	/* save working box on entry */
                   9256: raster	*picturerp=NULL;		/* raster for entire picture */
                   9257: int	delete_subraster();		/* free picelemsp[] workspace */
                   9258: int	pixsz = 1;			/* pixels are one bit each */
                   9259: double	strtod(),			/* convert ascii params to doubles */
                   9260: 	x=0.0,y=0.0,			/* x,y-coords for put,multiput*/
                   9261: 	xinc=0.0,yinc=0.0;		/* x,y-incrementss for multiput*/
                   9262: int	width=0,  height=0,		/* #pixels width,height of picture */
                   9263: 	ewidth=0, eheight=0,		/* pic element width,height */
                   9264: 	ix=0,xpos=0, iy=0,ypos=0,	/* mimeTeX x,y pixel coords */
                   9265: 	num=1, inum;			/* number reps, index of element */
                   9266: int	iscenter=0;			/* center or lowerleft put position*/
                   9267: int	*oldworkingparam = workingparam, /* save working param on entry */
                   9268: 	origin = 0;			/* x,yinc ++=00 +-=01 -+=10 --=11 */
                   9269: int	rastput();			/* embed elements in picture */
                   9270: int	type_raster();			/* display debugging output */
                   9271: /* -------------------------------------------------------------------------
                   9272: First obtain (width,height) arguments immediately following \picture command
                   9273: -------------------------------------------------------------------------- */
                   9274: /* --- parse for (width,height) arguments, and bump expression past it --- */
                   9275: *expression = texsubexpr(*expression,putexpr,254,"(",")",0,0);
                   9276: if ( *putexpr == '\000' ) goto end_of_job; /* couldn't get (width,height) */
                   9277: /* --- now interpret width,height returned in putexpr --- */
                   9278: if ( (putptr=strchr(putexpr,',')) != NULL ) /* look for ',' in width,height*/
                   9279:   *putptr = '\000';			/* found it, so replace ',' by '\0'*/
                   9280: width=height = iround(unitlength*strtod(putexpr,NULL)); /*width pixels*/
                   9281: if ( putptr != NULL )			/* 2nd arg, if present, is height */
                   9282:   height = iround(unitlength*strtod(putptr+1,NULL)); /*in pixels*/
                   9283: /* -------------------------------------------------------------------------
                   9284: Then obtain entire picture {...} subexpression following (width,height)
                   9285: -------------------------------------------------------------------------- */
                   9286: /* --- parse for picture subexpression, and bump expression past it --- */
                   9287: *expression = texsubexpr(*expression,picexpr,2047,"{","}",0,0);
                   9288: if ( *picexpr == '\000' ) goto end_of_job; /* couldn't get {pic_elements} */
                   9289: /* -------------------------------------------------------------------------
                   9290: allocate subraster and raster for complete picture
                   9291: -------------------------------------------------------------------------- */
                   9292: /* --- sanity check on width,height args --- */
                   9293: if ( width < 2 ||  width > 600
                   9294: ||  height < 2 || height > 600 ) goto end_of_job;
                   9295: /* --- allocate and initialize subraster for constructed picture --- */
                   9296: if ( (picturesp=new_subraster(width,height,pixsz)) /*allocate new subraster*/
                   9297: ==   NULL )  goto end_of_job;		/* quit if failed */
                   9298: workingbox = picturesp;			/* set workingbox to our picture */
                   9299: /* --- initialize picture subraster parameters --- */
                   9300: picturesp->type = IMAGERASTER;		/* image */
                   9301: picturesp->symdef = NULL;		/* not applicable for image */
                   9302: picturesp->baseline = height/2 + 2;	/* is a little above center good? */
                   9303: picturesp->size = size;			/* size (probably unneeded) */
                   9304: picturerp = picturesp->image;		/* raster embedded in subraster */
                   9305: if ( msgfp!=NULL && msglevel>=29 )	/* debugging */
                   9306:   fprintf(msgfp,"picture> width,height=%d,%d\n",width,height);
                   9307: /* -------------------------------------------------------------------------
                   9308: parse out each picture element, rasterize it, and place it in picture
                   9309: -------------------------------------------------------------------------- */
                   9310: while ( *picptr != '\000' )		/* until we run out of pic_elems */
                   9311:   {
                   9312:   /* -----------------------------------------------------------------------
                   9313:   first obtain leading \[multi]put(x,y[;xinc,yinc;num]) args for pic_elem
                   9314:   ------------------------------------------------------------------------ */
                   9315:   /* --- init default values in case not explicitly supplied in args --- */
                   9316:   x=y=0.0;  xinc=yinc=0.0;  num=1;	/* init default values */
                   9317:   iscenter = origin = 0;		/* center, origin */
                   9318:   /* --- get (pream$x,y;xinc,yinc;num ) args and bump picptr past it --- */
                   9319:   while ( *picptr != '\000' )		/* skip invalid chars preceding ( */
                   9320:     if ( *picptr == '(' ) break;	/* found opening ( */
                   9321:     else picptr++;			/* else skip invalid char */
                   9322:   picptr = texsubexpr(picptr,putexpr,254,"(",")",0,0);
                   9323:   if ( *putexpr == '\000' ) goto end_of_job; /* couldn't get (x,y) */
                   9324:   /* --- first look for $-terminated or for any non-digit preamble --- */
                   9325:   *pream = '\000';			/* init preamble as empty string */
                   9326:   if ( (putptr=strchr(putexpr,'$')) != NULL ) /*check for $ pream terminator*/
                   9327:     { *putptr++ = '\000';		/* replace $ by '\0', bump past $ */
                   9328:       strcpy(pream,putexpr); }		/* copy leading preamble from put */
                   9329:   else					/* look for any non-digit preamble */
                   9330:     { for ( preptr=pream,putptr=putexpr; ; putptr++ )
                   9331: 	if ( *putptr == '\000'		/* end-of-putdata signalled */
                   9332: 	||   !isalpha((int)(*putptr)) ) break; /* or found non-alpha char */
                   9333: 	else *preptr++ = *putptr;	/* copy alpha char to preamble */
                   9334:       *preptr = '\000'; }		/* null-terminate preamble */
                   9335:   /* --- interpret preamble --- */
                   9336:   for ( preptr=pream; ; preptr++ )	/* examine each preamble char */
                   9337:     if ( *preptr == '\000' ) break;	/* end-of-preamble signalled */
                   9338:     else switch ( tolower(*preptr) )	/* check lowercase preamble char */
                   9339:       {
                   9340:       default: break;			/* unrecognized flag */
                   9341:       case 'c': iscenter=1; break;	/* center pic_elem at x,y coords */
                   9342:       } /* --- end-of-switch --- */
                   9343:   /* --- interpret x,y;xinc,yinc;num following preamble --- */      
                   9344:   if ( *putptr != '\000' )		/*check for put data after preamble*/
                   9345:    {
                   9346:    /* --- first squeeze preamble out of put expression --- */
                   9347:    if ( *pream != '\000' ) strcpy(putexpr,putptr); /* squeeze out preamble */
                   9348:    /* --- interpret x,y --- */
                   9349:    if ( (multptr=strchr(putexpr,';')) != NULL ) /*semicolon signals multiput*/
                   9350:      *multptr = '\000';			/* replace semicolon by '\0' */
                   9351:    if ( (putptr=strchr(putexpr,',')) != NULL ) /* comma separates x,y */
                   9352:      *putptr = '\000';			/* replace comma by '\0'  */
                   9353:    if ( *putexpr != '\000' )		/* leading , may be placeholder */
                   9354:      x = unitlength*strtod(putexpr,NULL); /* x coord in pixels*/
                   9355:    if ( putptr != NULL )		/* 2nd arg, if present, is y coord */
                   9356:      y = unitlength*strtod(putptr+1,NULL); /* in pixels */
                   9357:    /* --- interpret xinc,yinc,num if we have a multiput --- */
                   9358:    if ( multptr != NULL )		/* found ';' signalling multiput */
                   9359:      {
                   9360:      if ( (preptr=strchr(multptr+1,';')) != NULL ) /* ';' preceding num arg*/
                   9361:        *preptr = '\000';		/* replace ';' by '\0' */
                   9362:      if ( (putptr=strchr(multptr+1,',')) != NULL ) /* ',' between xinc,yinc*/
                   9363:        *putptr = '\000';		/* replace ',' by '\0' */
                   9364:      if ( *(multptr+1) != '\000' )	/* leading , may be placeholder */
                   9365:        xinc = unitlength*strtod(multptr+1,NULL); /* xinc in pixels */
                   9366:      if ( putptr != NULL )		/* 2nd arg, if present, is yinc */
                   9367:        yinc = unitlength*strtod(putptr+1,NULL); /* in user pixels */
                   9368:      num = (preptr==NULL? 999 : atoi(preptr+1)); /*explicit num val or 999*/
                   9369:      } /* --- end-of-if(multptr!=NULL) --- */
                   9370:    } /* --- end-of-if(*preptr!='\000') --- */
                   9371:   if ( msgfp!=NULL && msglevel>=29 )	/* debugging */
                   9372:     fprintf(msgfp,
                   9373:     "picture> pream;x,y;xinc,yinc;num=\"%s\";%.2f,%.2f;%.2f,%.2f;%d\n",
                   9374:     pream,x,y,xinc,yinc,num);
                   9375:   /* -----------------------------------------------------------------------
                   9376:   now obtain {...} picture element following [multi]put, and rasterize it
                   9377:   ------------------------------------------------------------------------ */
                   9378:   /* --- parse for {...} picture element and bump picptr past it --- */
                   9379:   picptr = texsubexpr(picptr,picelem,1023,"{","}",0,0);
                   9380:   if ( *picelem == '\000' ) goto end_of_job; /* couldn't get {pic_elem} */
                   9381:   if ( msgfp!=NULL && msglevel>=29 )	/* debugging */
                   9382:     fprintf(msgfp, "picture> picelem=\"%.50s\"\n",picelem);
                   9383:   /* --- rasterize picture element --- */
                   9384:   origin = 0;				/* init origin as working param */
                   9385:   workingparam = &origin;		/* and point working param to it */
                   9386:   picelemsp = rasterize(picelem,size);	/* rasterize picture element */
                   9387:   if ( picelemsp == NULL ) continue;	/* failed to rasterize, skip elem */
                   9388:   ewidth  = (picelemsp->image)->width;	/* width of element, in pixels */
                   9389:   eheight = (picelemsp->image)->height;	/* height of element, in pixels */
                   9390:   if ( origin == 55 ) iscenter = 1;	/* origin set to (.5,.5) for center*/
                   9391:   if ( msgfp!=NULL && msglevel>=29 )	/* debugging */
                   9392:     { fprintf(msgfp, "picture> ewidth,eheight,origin,num=%d,%d,%d,%d\n",
                   9393:       ewidth,eheight,origin,num);
                   9394:       if ( msglevel >= 999 ) type_raster(picelemsp->image,msgfp); }
                   9395:   /* -----------------------------------------------------------------------
                   9396:   embed element in picture (once, or multiple times if requested)
                   9397:   ------------------------------------------------------------------------ */
                   9398:   for ( inum=0; inum<num; inum++ )	/* once, or for num repetitions */
                   9399:     {
                   9400:     /* --- set x,y-coords for this iteration --- */
                   9401:     ix = iround(x);  iy = iround(y);	/* round x,y to nearest integer */
                   9402:     if ( iscenter )			/* place center of element at x,y */
                   9403:       {	xpos = ix - ewidth/2;		/* x picture coord to center elem */
                   9404: 	ypos = height - iy - eheight/2; } /* y pixel coord to center elem */
                   9405:     else				/* default places lower-left at x,y*/
                   9406:       {	xpos = ix;			/* set x pixel coord for left */
                   9407: 	if ( origin==10 || origin==11 )	/* x,yinc's are -+ or -- */
                   9408: 	  xpos = ix - ewidth;		/* so set for right instead */
                   9409: 	ypos = height - iy - eheight;	/* set y pixel coord for lower */
                   9410: 	if ( origin==1 || origin==11 )	/* x,yinc's are +- or -- */
                   9411: 	  ypos = height - iy; }		/* so set for upper instead */
                   9412:     if ( msgfp!=NULL && msglevel>=29 )	/* debugging */
                   9413:       fprintf(msgfp,
                   9414:       "picture> inum,x,y,ix,iy,xpos,ypos=%d,%.2f,%.2f,%d,%d,%d,%d\n",
                   9415:       inum,x,y,ix,iy,xpos,ypos);
                   9416:     /* --- embed token raster at xpos,ypos, and quit if out-of-bounds --- */
                   9417:     if ( !rastput(picturerp,picelemsp->image,ypos,xpos,0) ) break;
                   9418:     /* --- apply increment --- */
                   9419:     if ( xinc==0. && yinc==0. ) break;	/* quit if both increments zero */
                   9420:     x += xinc;  y += yinc;		/* increment coords for next iter */
                   9421:     } /* --- end-of-for(inum) --- */
                   9422:   /* --- free picture element subraster after embedding it in picture --- */
                   9423:   delete_subraster(picelemsp);		/* done with subraster, so free it */
                   9424:   } /* --- end-of-while(*picptr!=0) --- */
                   9425: /* -------------------------------------------------------------------------
                   9426: return picture constructed from pic_elements to caller
                   9427: -------------------------------------------------------------------------- */
                   9428: end_of_job:
                   9429:   workingbox = oldworkingbox;		/* restore original working box */
                   9430:   workingparam = oldworkingparam;	/* restore original working param */
                   9431:   return ( picturesp );			/* return our picture to caller */
                   9432: } /* --- end-of-function rastpicture() --- */
                   9433: 
                   9434: 
                   9435: /* ==========================================================================
                   9436:  * Function:	rastline ( expression, size, basesp, arg1, arg2, arg3 )
                   9437:  * Purpose:	\line handler, returns subraster corresponding to line
                   9438:  *		parameters (xinc,yinc){xlen}
                   9439:  * --------------------------------------------------------------------------
                   9440:  * Arguments:	expression (I/O) char **  to first char of null-terminated
                   9441:  *				string immediately following \line to be
                   9442:  *				rasterized, and returning ptr immediately
                   9443:  *				following last character processed.
1.3     ! albertel 9444:  *		size (I)	int containing 0-7 default font size
1.1       albertel 9445:  *		basesp (I)	subraster *  to character (or subexpression)
                   9446:  *				immediately preceding \line
                   9447:  *				(unused, but passed for consistency)
                   9448:  *		arg1 (I)	int unused
                   9449:  *		arg2 (I)	int unused
                   9450:  *		arg3 (I)	int unused
                   9451:  * --------------------------------------------------------------------------
                   9452:  * Returns:	( subraster * )	ptr to subraster corresponding to line
                   9453:  *				requested, or NULL for any parsing error
                   9454:  * --------------------------------------------------------------------------
                   9455:  * Notes:     o	Summary of syntax...
                   9456:  *		  \line(xinc,yinc){xlen}
                   9457:  *	      o	if {xlen} not given, then it's assumed xlen = |xinc|
                   9458:  * ======================================================================= */
                   9459: /* --- entry point --- */
                   9460: subraster *rastline ( char **expression, int size, subraster *basesp,
                   9461: 			int arg1, int arg2, int arg3 )
                   9462: {
                   9463: /* -------------------------------------------------------------------------
                   9464: Allocations and Declarations
                   9465: -------------------------------------------------------------------------- */
                   9466: char	*texsubexpr(),linexpr[257], *xptr=linexpr; /*line(xinc,yinc){xlen}*/
                   9467: subraster *new_subraster(), *linesp=NULL; /* subraster for line */
                   9468: /*char	*origexpression = *expression;*/ /*original expression after \line*/
                   9469: int	pixsz = 1;			/* pixels are one bit each */
1.2       albertel 9470: int	thickness = 1;			/* line thickness */
1.1       albertel 9471: double	strtod(),			/* convert ascii params to doubles */
                   9472: 	xinc=0.0, yinc=0.0,		/* x,y-increments for line, */
                   9473: 	xlen=0.0, ylen=0.0;		/* x,y lengths for line */
1.2       albertel 9474: int	width=0,  height=0,		/* #pixels width,height of line */
                   9475: 	rwidth=0, rheight=0;		/*alloc width,height plus thickness*/
1.1       albertel 9476: int	istop=0,  isright=0,		/* origin at bot-left if x,yinc>=0 */
                   9477: 	origin = 0;			/* x,yinc: ++=00 +-=01 -+=10 --=11 */
                   9478: int	line_raster();			/* draw line in linesp->image */
                   9479: /* -------------------------------------------------------------------------
                   9480: obtain (xinc,yinc) arguments immediately following \line command
                   9481: -------------------------------------------------------------------------- */
                   9482: /* --- parse for (xinc,yinc) arguments, and bump expression past it --- */
                   9483: *expression = texsubexpr(*expression,linexpr,253,"(",")",0,0);
                   9484: if ( *linexpr == '\000' ) goto end_of_job; /* couldn't get (xinc,yinc) */
1.2       albertel 9485: /* --- now interpret xinc,yinc;thickness returned in linexpr --- */
                   9486: if ( (xptr=strchr(linexpr,';')) != NULL ) /* look for ';' after xinc,yinc */
                   9487:   { *xptr = '\000';			/* terminate linexpr at ; */
                   9488:     thickness = (int)strtol(xptr+1,NULL,10); } /* get int thickness */
1.1       albertel 9489: if ( (xptr=strchr(linexpr,',')) != NULL ) /* look for ',' in xinc,yinc */
                   9490:   *xptr = '\000';			/* found it, so replace ',' by '\0'*/
                   9491: if ( *linexpr != '\000' )		/* check against missing 1st arg */
                   9492:   xinc = xlen = strtod(linexpr,NULL);	/* xinc in user units */
                   9493: if ( xptr != NULL )			/* 2nd arg, if present, is yinc */
                   9494:   yinc = ylen = strtod(xptr+1,NULL);	/* in user units */
                   9495: /* -------------------------------------------------------------------------
                   9496: obtain optional {xlen} following (xinc,yinc), and calculate ylen
                   9497: -------------------------------------------------------------------------- */
                   9498: /* --- check if {xlen} given --- */
                   9499: if ( *(*expression) == '{' )		/*have {xlen} if leading char is { */
                   9500:   {
                   9501:   /* --- parse {xlen} and bump expression past it, interpret as double --- */
                   9502:   *expression = texsubexpr(*expression,linexpr,253,"{","}",0,0);
                   9503:   if ( *linexpr == '\000' ) goto end_of_job; /* couldn't get {xlen} */
                   9504:   xlen = strtod(linexpr,NULL);		/* xlen in user units */
                   9505:   /* --- set other values accordingly --- */
                   9506:   if ( xlen  < 0.0 ) xinc = -xinc;	/* if xlen negative, flip xinc sign*/
                   9507:   if ( xinc != 0.0 ) ylen = xlen*yinc/xinc; /* set ylen from xlen and slope*/
                   9508:   else xlen  = 0.0;			/* can't have xlen if xinc=0 */
                   9509:   } /* --- end-of-if(*(*expression)=='{') --- */
                   9510: /* -------------------------------------------------------------------------
                   9511: calculate width,height, etc, based on xlen,ylen, etc
                   9512: -------------------------------------------------------------------------- */
                   9513: /* --- force lengths positive --- */
                   9514: xlen = absval(xlen);			/* force xlen positive */
                   9515: ylen = absval(ylen);			/* force ylen positive */
                   9516: /* --- calculate corresponding lengths in pixels --- */
1.2       albertel 9517: width   = max2(1,iround(unitlength*xlen)); /*scale by unitlength and round,*/
                   9518: height  = max2(1,iround(unitlength*ylen)); /* and must be at least 1 pixel */
                   9519: rwidth  = width  + (ylen<0.001?0:max2(0,thickness-1));
                   9520: rheight = height + (xlen<0.001?0:max2(0,thickness-1));
1.1       albertel 9521: /* --- set origin corner, x,yinc's: ++=0=(0,0) +-=1=(0,1) -+=10=(1,0) --- */
                   9522: if ( xinc < 0.0 ) isright = 1;		/*negative xinc, so corner is (1,?)*/
                   9523: if ( yinc < 0.0 ) istop = 1;		/*negative yinc, so corner is (?,1)*/
                   9524: origin = isright*10 + istop;		/* interpret 0=(0,0), 11=(1,1), etc*/
                   9525: if ( msgfp!=NULL && msglevel>=29 )	/* debugging */
                   9526:   fprintf(msgfp,"rastline> width,height,origin;x,yinc=%d,%d,%d;%g,%g\n",
                   9527:   width,height,origin,xinc,yinc);
                   9528: /* -------------------------------------------------------------------------
1.3     ! albertel 9529: allocate subraster and raster for line
1.1       albertel 9530: -------------------------------------------------------------------------- */
1.2       albertel 9531: /* --- sanity check on width,height,thickness args --- */
1.1       albertel 9532: if ( width < 1 ||  width > 600
1.2       albertel 9533: ||  height < 1 || height > 600
                   9534: ||  thickness<1||thickness>25 ) goto end_of_job;
1.1       albertel 9535: /* --- allocate and initialize subraster for constructed line --- */
1.2       albertel 9536: if ( (linesp=new_subraster(rwidth,rheight,pixsz)) /* alloc new subraster */
1.1       albertel 9537: ==   NULL )  goto end_of_job;		/* quit if failed */
                   9538: /* --- initialize line subraster parameters --- */
                   9539: linesp->type = IMAGERASTER;		/* image */
                   9540: linesp->symdef = NULL;			/* not applicable for image */
1.2       albertel 9541: linesp->baseline = height/2 + 2		/* is a little above center good? */
                   9542: 	+ (rheight-height)/2;		/* account for line thickness too */
1.1       albertel 9543: linesp->size = size;			/* size (probably unneeded) */
                   9544: /* -------------------------------------------------------------------------
                   9545: draw the line
                   9546: -------------------------------------------------------------------------- */
                   9547: line_raster ( linesp->image,		/* embedded raster image */
                   9548: 	(istop?   0 : height-1),	/* row0, from bottom or top */
                   9549: 	(isright?  width-1 : 0),	/* col0, from left or right */
                   9550: 	(istop?   height-1 : 0),	/* row1, to top or bottom */
                   9551: 	(isright? 0 :  width-1),	/* col1, to right or left */
1.2       albertel 9552: 	thickness );			/* line thickness (usually 1 pixel)*/
1.1       albertel 9553: /* -------------------------------------------------------------------------
                   9554: return constructed line to caller
                   9555: -------------------------------------------------------------------------- */
                   9556: end_of_job:
                   9557:   if ( workingparam != NULL )		/* caller wants origin */
                   9558:     *workingparam = origin;		/* return origin corner to caller */
                   9559:   return ( linesp );			/* return line to caller */
                   9560: } /* --- end-of-function rastline() --- */
                   9561: 
                   9562: 
                   9563: /* ==========================================================================
1.3     ! albertel 9564:  * Function:	rastrule ( expression, size, basesp, arg1, arg2, arg3 )
        !          9565:  * Purpose:	\rule handler, returns subraster corresponding to rule
        !          9566:  *		parameters [lift]{width}{height}
        !          9567:  * --------------------------------------------------------------------------
        !          9568:  * Arguments:	expression (I/O) char **  to first char of null-terminated
        !          9569:  *				string immediately following \rule to be
        !          9570:  *				rasterized, and returning ptr immediately
        !          9571:  *				following last character processed.
        !          9572:  *		size (I)	int containing 0-7 default font size
        !          9573:  *		basesp (I)	subraster *  to character (or subexpression)
        !          9574:  *				immediately preceding \rule
        !          9575:  *				(unused, but passed for consistency)
        !          9576:  *		arg1 (I)	int unused
        !          9577:  *		arg2 (I)	int unused
        !          9578:  *		arg3 (I)	int unused
        !          9579:  * --------------------------------------------------------------------------
        !          9580:  * Returns:	( subraster * )	ptr to subraster corresponding to rule
        !          9581:  *				requested, or NULL for any parsing error
        !          9582:  * --------------------------------------------------------------------------
        !          9583:  * Notes:     o	Summary of syntax...
        !          9584:  *		  \rule[lift]{width}{height}
        !          9585:  *	      o	if [lift] not given, then bottom of rule on baseline
        !          9586:  *	      o	if width=0 then you get an invisible strut 1 (one) pixel wide
        !          9587:  * ======================================================================= */
        !          9588: /* --- entry point --- */
        !          9589: subraster *rastrule ( char **expression, int size, subraster *basesp,
        !          9590: 			int arg1, int arg2, int arg3 )
        !          9591: {
        !          9592: /* -------------------------------------------------------------------------
        !          9593: Allocations and Declarations
        !          9594: -------------------------------------------------------------------------- */
        !          9595: char	*texsubexpr(), rulexpr[257];	/* rule[lift]{wdth}{hgt} */
        !          9596: subraster *new_subraster(), *rulesp=NULL; /* subraster for rule */
        !          9597: int	pixsz = 1;			/* pixels are one bit each */
        !          9598: int	lift=0, width=0, height=0;	/* default rule parameters */
        !          9599: double	strtod(), dval;			/* convert ascii params to doubles */
        !          9600: int	rwidth=0, rheight=0;		/* alloc width, height plus lift */
        !          9601: int	rule_raster();			/* draw rule in rulesp->image */
        !          9602: /* -------------------------------------------------------------------------
        !          9603: Obtain lift,width,height
        !          9604: -------------------------------------------------------------------------- */
        !          9605: /* --- check for optional lift arg  --- */
        !          9606: if ( *(*expression) == '[' )		/*check for []-enclosed optional arg*/
        !          9607:   { *expression = texsubexpr(*expression,rulexpr,255,"[","]",0,0);
        !          9608:     dval = (int)(strtod(rulexpr,NULL)+0.5); /* convert [lift] to int */
        !          9609:     if ( dval <= 99 && dval >= (-99) )	/* sanity check */
        !          9610:       lift = iround(unitlength*dval); }	/* scale by unitlength and round */
        !          9611: /* --- parse for width --- */
        !          9612: *expression = texsubexpr(*expression,rulexpr,255,"{","}",0,0);
        !          9613: if ( *rulexpr == '\000' ) goto end_of_job; /* quit if args missing */
        !          9614: dval = (int)(strtod(rulexpr,NULL)+0.5);	/* convert {width} to int */
        !          9615: if ( dval <= 500 && dval >= 0 )		/* sanity check */
        !          9616:   width = max2(0,iround(unitlength*dval)); /* scale by unitlength and round*/
        !          9617: /* --- parse for height --- */
        !          9618: *expression = texsubexpr(*expression,rulexpr,255,"{","}",0,0);
        !          9619: if ( *rulexpr == '\000' ) goto end_of_job; /* quit if args missing */
        !          9620: dval = (int)(strtod(rulexpr,NULL)+0.5);	/* convert {height} to int */
        !          9621: if ( dval <= 500 && dval > 0 )		/* sanity check */
        !          9622:   height= max2(1,iround(unitlength*dval)); /* scale by unitlength and round*/
        !          9623: /* --- raster width,height in pixels --- */
        !          9624: rwidth  = max2(1,width);		/* raster must be at least 1 pixel*/
        !          9625: rheight = height + (lift>=0?lift:	/* raster height plus lift */
        !          9626:   (-lift<height?0:-lift-height+1));	/* may need empty space above rule */
        !          9627: /* -------------------------------------------------------------------------
        !          9628: allocate subraster and raster for rule
        !          9629: -------------------------------------------------------------------------- */
        !          9630: /* --- sanity check on width,height,thickness args --- */
        !          9631: if ( rwidth < 1 ||  rwidth > 600
        !          9632: ||  rheight < 1 || rheight > 600 ) goto end_of_job;
        !          9633: /* --- allocate and initialize subraster for constructed rule --- */
        !          9634: if ( (rulesp=new_subraster(rwidth,rheight,pixsz)) /* alloc new subraster */
        !          9635: ==   NULL )  goto end_of_job;		/* quit if failed */
        !          9636: /* --- initialize line subraster parameters --- */
        !          9637: rulesp->type = IMAGERASTER;		/* image */
        !          9638: rulesp->symdef = NULL;			/* not applicable for image */
        !          9639: rulesp->baseline = rheight-1 + (lift>=0?0:lift); /*adjust baseline for lift*/
        !          9640: rulesp->size = size;			/* size (probably unneeded) */
        !          9641: /* -------------------------------------------------------------------------
        !          9642: draw the rule
        !          9643: -------------------------------------------------------------------------- */
        !          9644: rule_raster ( rulesp->image,		/* embedded raster image */
        !          9645: 	(-lift<height?0:rheight-height), /* topmost row for top-left corner*/
        !          9646: 	0,				/* leftmost col for top-left corner*/
        !          9647: 	width,				/* rule width */
        !          9648: 	height,				/* rule height */
        !          9649: 	( width>0? 0:4 ) );		/* rule type */
        !          9650: /* -------------------------------------------------------------------------
        !          9651: return constructed rule to caller
        !          9652: -------------------------------------------------------------------------- */
        !          9653: end_of_job:
        !          9654:   return ( rulesp );			/* return rule to caller */
        !          9655: } /* --- end-of-function rastrule() --- */
        !          9656: 
        !          9657: 
        !          9658: /* ==========================================================================
1.1       albertel 9659:  * Function:	rastcircle ( expression, size, basesp, arg1, arg2, arg3 )
                   9660:  * Purpose:	\circle handler, returns subraster corresponding to ellipse
                   9661:  *		parameters (xdiam[,ydiam])
                   9662:  * --------------------------------------------------------------------------
                   9663:  * Arguments:	expression (I/O) char **  to first char of null-terminated
                   9664:  *				string immediately following \circle to be
                   9665:  *				rasterized, and returning ptr immediately
                   9666:  *				following last character processed.
                   9667:  *		size (I)	int containing 0-4 default font size
                   9668:  *		basesp (I)	subraster *  to character (or subexpression)
                   9669:  *				immediately preceding \circle
                   9670:  *				(unused, but passed for consistency)
                   9671:  *		arg1 (I)	int unused
                   9672:  *		arg2 (I)	int unused
                   9673:  *		arg3 (I)	int unused
                   9674:  * --------------------------------------------------------------------------
                   9675:  * Returns:	( subraster * )	ptr to subraster corresponding to ellipse
                   9676:  *				requested, or NULL for any parsing error
                   9677:  * --------------------------------------------------------------------------
                   9678:  * Notes:     o	Summary of syntax...
                   9679:  *		  \circle(xdiam[,ydiam])
                   9680:  *	      o
                   9681:  * ======================================================================= */
                   9682: /* --- entry point --- */
                   9683: subraster *rastcircle ( char **expression, int size, subraster *basesp,
                   9684: 			int arg1, int arg2, int arg3 )
                   9685: {
                   9686: /* -------------------------------------------------------------------------
                   9687: Allocations and Declarations
                   9688: -------------------------------------------------------------------------- */
                   9689: char	*texsubexpr(), circexpr[512],*xptr=circexpr; /*circle(xdiam[,ydiam])*/
                   9690: char	*qptr=NULL, quads[256]="1234";	/* default to draw all quadrants */
                   9691: double	theta0=0.0, theta1=0.0;		/* ;theta0,theta1 instead of ;quads*/
                   9692: subraster *new_subraster(), *circsp=NULL; /* subraster for ellipse */
                   9693: int	pixsz = 1;			/* pixels are one bit each */
                   9694: double	strtod(),			/* convert ascii params to doubles */
                   9695: 	xdiam=0.0, ydiam=0.0;		/* x,y major/minor axes/diameters */
                   9696: int	width=0,  height=0;		/* #pixels width,height of ellipse */
                   9697: int	thickness = 1;			/* drawn lines are one pixel thick */
                   9698: int	origin = 55;			/* force origin centered */
                   9699: int	circle_raster(),		/* draw ellipse in circsp->image */
                   9700: 	circle_recurse();		/* for theta0,theta1 args */
                   9701: /* -------------------------------------------------------------------------
                   9702: obtain (xdiam[,ydiam]) arguments immediately following \circle command
                   9703: -------------------------------------------------------------------------- */
                   9704: /* --- parse for (xdiam[,ydiam]) args, and bump expression past it --- */
                   9705: *expression = texsubexpr(*expression,circexpr,511,"(",")",0,0);
                   9706: if ( *circexpr == '\000' ) goto end_of_job; /* couldn't get (xdiam[,ydiam])*/
                   9707: /* --- now interpret xdiam[,ydiam] returned in circexpr --- */
                   9708: if ( (qptr=strchr(circexpr,';')) != NULL ) /* semicolon signals quads data */
                   9709:   { *qptr = '\000';			/* replace semicolon by '\0' */
                   9710:     strcpy(quads,qptr+1);		/* save user-requested quads */
                   9711:     if ( (qptr=strchr(quads,',')) != NULL ) /* have theta0,theta1 instead */
                   9712:       {	*qptr = '\000';			/* replace , with null */
                   9713: 	theta0 = strtod(quads,NULL);	/* theta0 precedes , */
                   9714: 	theta1 = strtod(qptr+1,NULL);	/* theta1 follows , */
                   9715: 	qptr = NULL; }			/* signal thetas instead of quads */
                   9716:     else
                   9717: 	qptr = quads; }			/* set qptr arg for circle_raster()*/
                   9718: else					/* no ;quads at all */
                   9719:   qptr = quads;				/* default to all 4 quadrants */
                   9720: if ( (xptr=strchr(circexpr,',')) != NULL ) /* look for ',' in xdiam[,ydiam]*/
                   9721:   *xptr = '\000';			/* found it, so replace ',' by '\0'*/
                   9722: xdiam = ydiam = strtod(circexpr,NULL);	/* xdiam=ydiam in user units */
                   9723: if ( xptr != NULL )			/* 2nd arg, if present, is ydiam */
                   9724:   ydiam = strtod(xptr+1,NULL);		/* in user units */
                   9725: /* -------------------------------------------------------------------------
                   9726: calculate width,height, etc
                   9727: -------------------------------------------------------------------------- */
                   9728: /* --- calculate width,height in pixels --- */
                   9729: width  = max2(1,iround(unitlength*xdiam)); /*scale by unitlength and round,*/
                   9730: height = max2(1,iround(unitlength*ydiam)); /* and must be at least 1 pixel */
                   9731: if ( msgfp!=NULL && msglevel>=29 )	/* debugging */
                   9732:   fprintf(msgfp,"rastcircle> width,height;quads=%d,%d,%s\n",
                   9733:   width,height,(qptr==NULL?"default":qptr));
                   9734: /* -------------------------------------------------------------------------
                   9735: allocate subraster and raster for complete picture
                   9736: -------------------------------------------------------------------------- */
                   9737: /* --- sanity check on width,height args --- */
                   9738: if ( width < 1 ||  width > 600
                   9739: ||  height < 1 || height > 600 ) goto end_of_job;
                   9740: /* --- allocate and initialize subraster for constructed ellipse --- */
                   9741: if ( (circsp=new_subraster(width,height,pixsz)) /* allocate new subraster */
                   9742: ==   NULL )  goto end_of_job;		/* quit if failed */
                   9743: /* --- initialize ellipse subraster parameters --- */
                   9744: circsp->type = IMAGERASTER;		/* image */
                   9745: circsp->symdef = NULL;			/* not applicable for image */
                   9746: circsp->baseline = height/2 + 2;	/* is a little above center good? */
                   9747: circsp->size = size;			/* size (probably unneeded) */
                   9748: /* -------------------------------------------------------------------------
                   9749: draw the ellipse
                   9750: -------------------------------------------------------------------------- */
                   9751: if ( qptr != NULL )			/* have quads */
                   9752:   circle_raster ( circsp->image,	/* embedded raster image */
                   9753: 	0, 0,				/* row0,col0 are upper-left corner */
                   9754: 	height-1, width-1,		/* row1,col1 are lower-right */
                   9755: 	thickness,			/* line thickness is 1 pixel */
                   9756: 	qptr );				/* "1234" quadrants to be drawn */
                   9757: else					/* have theta0,theta1 */
                   9758:   circle_recurse ( circsp->image,	/* embedded raster image */
                   9759: 	0, 0,				/* row0,col0 are upper-left corner */
                   9760: 	height-1, width-1,		/* row1,col1 are lower-right */
                   9761: 	thickness,			/* line thickness is 1 pixel */
                   9762: 	theta0,theta1 );		/* theta0,theta1 arc to be drawn */
                   9763: /* -------------------------------------------------------------------------
                   9764: return constructed ellipse to caller
                   9765: -------------------------------------------------------------------------- */
                   9766: end_of_job:
                   9767:   if ( workingparam != NULL )		/* caller wants origin */
                   9768:     *workingparam = origin;		/* return center origin to caller */
                   9769:   return ( circsp );			/* return ellipse to caller */
                   9770: } /* --- end-of-function rastcircle() --- */
                   9771: 
                   9772: 
                   9773: /* ==========================================================================
                   9774:  * Function:	rastbezier ( expression, size, basesp, arg1, arg2, arg3 )
                   9775:  * Purpose:	\bezier handler, returns subraster corresponding to bezier
                   9776:  *		parameters (col0,row0)(col1,row1)(colt,rowt)
                   9777:  * --------------------------------------------------------------------------
                   9778:  * Arguments:	expression (I/O) char **  to first char of null-terminated
                   9779:  *				string immediately following \bezier to be
                   9780:  *				rasterized, and returning ptr immediately
                   9781:  *				following last character processed.
                   9782:  *		size (I)	int containing 0-5 default font size
                   9783:  *		basesp (I)	subraster *  to character (or subexpression)
                   9784:  *				immediately preceding \bezier
                   9785:  *				(unused, but passed for consistency)
                   9786:  *		arg1 (I)	int unused
                   9787:  *		arg2 (I)	int unused
                   9788:  *		arg3 (I)	int unused
                   9789:  * --------------------------------------------------------------------------
                   9790:  * Returns:	( subraster * )	ptr to subraster corresponding to bezier
                   9791:  *				requested, or NULL for any parsing error
                   9792:  * --------------------------------------------------------------------------
                   9793:  * Notes:     o	Summary of syntax...
                   9794:  *		  \bezier(col1,row1)(colt,rowt)
                   9795:  *	      o	col0=0,row0=0 assumed, i.e., given by
                   9796:  *		\picture(){~(col0,row0){\bezier(col1,row1)(colt,rowt)}~}
                   9797:  * ======================================================================= */
                   9798: /* --- entry point --- */
                   9799: subraster *rastbezier ( char **expression, int size, subraster *basesp,
                   9800: 			int arg1, int arg2, int arg3 )
                   9801: {
                   9802: /* -------------------------------------------------------------------------
                   9803: Allocations and Declarations
                   9804: -------------------------------------------------------------------------- */
                   9805: subraster *new_subraster(), *bezsp=NULL; /* subraster for bezier */
                   9806: char	*texsubexpr(), bezexpr[129],*xptr=bezexpr; /*\bezier(r,c)(r,c)(r,c)*/
                   9807: double	strtod();			/* convert ascii params to doubles */
                   9808: double	r0=0.0,c0=0.0, r1=0.0,c1=0.0, rt=0.0,ct=0.0, /* bezier points */
                   9809: 	rmid=0.0, cmid=0.0,		/* coords at parameterized midpoint*/
                   9810: 	rmin=0.0, cmin=0.0,		/* minimum r,c */
                   9811: 	rmax=0.0, cmax=0.0,		/* maximum r,c */
                   9812: 	rdelta=0.0, cdelta=0.0,		/* rmax-rmin, cmax-cmin */
                   9813: 	r=0.0, c=0.0;			/* some point */
                   9814: int	iarg=0;				/* 0=r0,c0 1=r1,c1 2=rt,ct */
                   9815: int	width=0, height=0;		/* dimensions of bezier raster */
                   9816: int	pixsz = 1;			/* pixels are one bit each */
                   9817: /*int	thickness = 1;*/		/* drawn lines are one pixel thick */
                   9818: int	origin = 0;			/*c's,r's reset to lower-left origin*/
                   9819: int	bezier_raster();		/* draw bezier in bezsp->image */
                   9820: /* -------------------------------------------------------------------------
                   9821: obtain (c1,r1)(ct,rt) args immediately following \bezier command
                   9822: -------------------------------------------------------------------------- */
                   9823: for ( iarg=1; iarg<=2; iarg++ )		/* 0=c0,r0 1=c1,r1 2=ct,rt */
                   9824:   {
                   9825:   /* --- parse for (r,c) args, and bump expression past them all --- */
                   9826:   *expression = texsubexpr(*expression,bezexpr,127,"(",")",0,0);
                   9827:   if ( *bezexpr == '\000' ) goto end_of_job; /* couldn't get (r,c)*/
                   9828:   /* --- now interpret (r,c) returned in bezexpr --- */
                   9829:   c = r = 0.0;				/* init x-coord=col, y-coord=row */
                   9830:   if ( (xptr=strchr(bezexpr,',')) != NULL ) /* comma separates row,col */
                   9831:     { *xptr = '\000';			/* found it, so replace ',' by '\0'*/
                   9832:       r = unitlength*strtod(xptr+1,NULL); } /* row=y-coord in pixels */
                   9833:   c = unitlength*strtod(bezexpr,NULL);	/* col=x-coord in pixels */
                   9834:   /* --- store r,c --- */
                   9835:   switch ( iarg )
                   9836:     { case 0: r0=r; c0=c; break;
                   9837:       case 1: r1=r; c1=c; break;
                   9838:       case 2: rt=r; ct=c; break; }
                   9839:   } /* --- end-of-for(iarg) --- */
                   9840: /* --- determine midpoint and maximum,minimum points --- */
                   9841: rmid = 0.5*(rt + 0.5*(r0+r1));		/* y-coord at middle of bezier */
                   9842: cmid = 0.5*(ct + 0.5*(c0+c1));		/* x-coord at middle of bezier */
                   9843: rmin = min3(r0,r1,rmid);		/* lowest row */
                   9844: cmin = min3(c0,c1,cmid);		/* leftmost col */
                   9845: rmax = max3(r0,r1,rmid);		/* highest row */
                   9846: cmax = max3(c0,c1,cmid);		/* rightmost col */
                   9847: rdelta = rmax-rmin;			/* height */
                   9848: cdelta = cmax-cmin;			/* width */
                   9849: /* --- rescale coords so we start at 0,0 --- */
                   9850: r0 -= rmin;  c0 -= cmin;		/* rescale r0,c0 */
                   9851: r1 -= rmin;  c1 -= cmin;		/* rescale r1,c1 */
                   9852: rt -= rmin;  ct -= cmin;		/* rescale rt,ct */
                   9853: /* --- flip rows so 0,0 becomes lower-left corner instead of upper-left--- */
                   9854: r0 = rdelta - r0 + 1;			/* map 0-->height-1, height-1-->0 */
                   9855: r1 = rdelta - r1 + 1;
                   9856: rt = rdelta - rt + 1;
                   9857: /* --- determine width,height of raster needed for bezier --- */
                   9858: width  = (int)(cdelta + 0.9999) + 1;	/* round width up */
                   9859: height = (int)(rdelta + 0.9999) + 1;	/* round height up */
                   9860: if ( msgfp!=NULL && msglevel>=29 )	/* debugging */
                   9861:   fprintf(msgfp,"rastbezier> width,height,origin=%d,%d,%d; c0,r0=%g,%g; "
                   9862:   "c1,r1=%g,%g\n rmin,mid,max=%g,%g,%g; cmin,mid,max=%g,%g,%g\n",
                   9863:   width,height,origin, c0,r0, c1,r1, rmin,rmid,rmax, cmin,cmid,cmax);
                   9864: /* -------------------------------------------------------------------------
                   9865: allocate raster
                   9866: -------------------------------------------------------------------------- */
                   9867: /* --- sanity check on width,height args --- */
                   9868: if ( width < 1 ||  width > 600
                   9869: ||  height < 1 || height > 600 ) goto end_of_job;
                   9870: /* --- allocate and initialize subraster for constructed bezier --- */
                   9871: if ( (bezsp=new_subraster(width,height,pixsz)) /* allocate new subraster */
                   9872: ==   NULL )  goto end_of_job;		/* quit if failed */
                   9873: /* --- initialize bezier subraster parameters --- */
                   9874: bezsp->type = IMAGERASTER;		/* image */
                   9875: bezsp->symdef = NULL;			/* not applicable for image */
                   9876: bezsp->baseline = height/2 + 2;		/* is a little above center good? */
                   9877: bezsp->size = size;			/* size (probably unneeded) */
                   9878: /* -------------------------------------------------------------------------
                   9879: draw the bezier
                   9880: -------------------------------------------------------------------------- */
                   9881: bezier_raster ( bezsp->image,		/* embedded raster image */
                   9882: 	r0, c0,				/* row0,col0 are lower-left corner */
                   9883: 	r1, c1,				/* row1,col1 are upper-right */
                   9884: 	rt, ct );			/* bezier tangent point */
                   9885: /* -------------------------------------------------------------------------
                   9886: return constructed bezier to caller
                   9887: -------------------------------------------------------------------------- */
                   9888: end_of_job:
                   9889:   if ( workingparam != NULL )		/* caller wants origin */
                   9890:     *workingparam = origin;		/* return center origin to caller */
                   9891:   return ( bezsp );			/* return bezier to caller */
                   9892: } /* --- end-of-function rastbezier() --- */
                   9893: 
                   9894: 
                   9895: /* ==========================================================================
                   9896:  * Function:	rastraise ( expression, size, basesp, arg1, arg2, arg3 )
                   9897:  * Purpose:	\raisebox{lift}{subexpression} handler, returns subraster
                   9898:  *		containing subexpression with its baseline "lifted" by lift
                   9899:  *		pixels, scaled by \unitlength, or "lowered" if lift arg
                   9900:  *		negative
                   9901:  * --------------------------------------------------------------------------
                   9902:  * Arguments:	expression (I/O) char **  to first char of null-terminated
                   9903:  *				string immediately following \raisebox to be
                   9904:  *				rasterized, and returning ptr immediately
                   9905:  *				following last character processed.
1.3     ! albertel 9906:  *		size (I)	int containing 0-7 default font size
1.1       albertel 9907:  *		basesp (I)	subraster *  to character (or subexpression)
1.3     ! albertel 9908:  *				immediately preceding \raisebox
1.1       albertel 9909:  *				(unused, but passed for consistency)
                   9910:  *		arg1 (I)	int unused
                   9911:  *		arg2 (I)	int unused
                   9912:  *		arg3 (I)	int unused
                   9913:  * --------------------------------------------------------------------------
                   9914:  * Returns:	( subraster * )	ptr to subraster corresponding to \raisebox
                   9915:  *				requested, or NULL for any parsing error
                   9916:  * --------------------------------------------------------------------------
                   9917:  * Notes:     o	Summary of syntax...
                   9918:  *		  \raisebox{lift}{subexpression}
                   9919:  *	      o
                   9920:  * ======================================================================= */
                   9921: /* --- entry point --- */
                   9922: subraster *rastraise ( char **expression, int size, subraster *basesp,
                   9923: 			int arg1, int arg2, int arg3 )
                   9924: {
                   9925: /* -------------------------------------------------------------------------
                   9926: Allocations and Declarations
                   9927: -------------------------------------------------------------------------- */
1.3     ! albertel 9928: char	*texsubexpr(), subexpr[MAXSUBXSZ+1], *liftexpr=subexpr; /* args */
1.1       albertel 9929: subraster *rasterize(), *raisesp=NULL;	/* rasterize subexpr to be raised */
                   9930: int	lift=0;				/* amount to raise/lower baseline */
                   9931: /* -------------------------------------------------------------------------
                   9932: obtain {lift} argument immediately following \raisebox command
                   9933: -------------------------------------------------------------------------- */
                   9934: /* --- parse for {lift} arg, and bump expression past it --- */
                   9935: *expression = texsubexpr(*expression,liftexpr,0,"{","}",0,0);
                   9936: if ( *liftexpr == '\000' ) goto end_of_job; /* couldn't get {lift} */
                   9937: lift = (int)((unitlength*strtod(liftexpr,NULL))+0.0);	/*{lift} to integer*/
                   9938: if ( abs(lift) > 200 ) lift=0;		/* sanity check */
                   9939: /* -------------------------------------------------------------------------
                   9940: obtain {subexpr} argument after {lift}, and rasterize it
                   9941: -------------------------------------------------------------------------- */
                   9942: /* --- parse for {subexpr} arg, and bump expression past it --- */
                   9943: *expression = texsubexpr(*expression,subexpr,0,"{","}",0,0);
                   9944: /* --- rasterize subexpression to be raised/lowered --- */
                   9945: if ( (raisesp = rasterize(subexpr,size)) /* rasterize subexpression */
                   9946: ==   NULL ) goto end_of_job;		/* and quit if failed */
                   9947: /* -------------------------------------------------------------------------
                   9948: raise/lower baseline and return it to caller
                   9949: -------------------------------------------------------------------------- */
                   9950: /* --- raise/lower baseline --- */
                   9951: raisesp->baseline += lift;		/* new baseline (no height checks) */
                   9952: /* --- return raised subexpr to caller --- */
                   9953: end_of_job:
                   9954:   return ( raisesp );			/* return raised subexpr to caller */
                   9955: } /* --- end-of-function rastraise() --- */
                   9956: 
                   9957: 
                   9958: /* ==========================================================================
                   9959:  * Function:	rastrotate ( expression, size, basesp, arg1, arg2, arg3 )
                   9960:  * Purpose:	\rotatebox{degrees}{subexpression} handler, returns subraster
                   9961:  *		containing subexpression rotated by degrees (counterclockwise
                   9962:  *		if degrees positive)
                   9963:  * --------------------------------------------------------------------------
                   9964:  * Arguments:	expression (I/O) char **  to first char of null-terminated
                   9965:  *				string immediately following \rotatebox to be
                   9966:  *				rasterized, and returning ptr immediately
                   9967:  *				following last character processed.
1.3     ! albertel 9968:  *		size (I)	int containing 0-7 default font size
1.1       albertel 9969:  *		basesp (I)	subraster *  to character (or subexpression)
                   9970:  *				immediately preceding \rotatebox
                   9971:  *				(unused, but passed for consistency)
                   9972:  *		arg1 (I)	int unused
                   9973:  *		arg2 (I)	int unused
                   9974:  *		arg3 (I)	int unused
                   9975:  * --------------------------------------------------------------------------
                   9976:  * Returns:	( subraster * )	ptr to subraster corresponding to \rotatebox
                   9977:  *				requested, or NULL for any parsing error
                   9978:  * --------------------------------------------------------------------------
                   9979:  * Notes:     o	Summary of syntax...
                   9980:  *		  \rotatebox{degrees}{subexpression}
                   9981:  *	      o
                   9982:  * ======================================================================= */
                   9983: /* --- entry point --- */
                   9984: subraster *rastrotate ( char **expression, int size, subraster *basesp,
                   9985: 			int arg1, int arg2, int arg3 )
                   9986: {
                   9987: /* -------------------------------------------------------------------------
                   9988: Allocations and Declarations
                   9989: -------------------------------------------------------------------------- */
1.3     ! albertel 9990: char	*texsubexpr(), subexpr[MAXSUBXSZ+1], *degexpr=subexpr; /* args */
1.1       albertel 9991: subraster *rasterize(), *rotsp=NULL;	/* subraster for rotated subexpr */
                   9992: raster	*rastrot(), *rotrp=NULL;	/* rotate subraster->image 90 degs */
                   9993: int	delete_raster();		/* delete intermediate rasters */
                   9994: int	baseline=0;			/* baseline of rasterized image */
                   9995: double	strtod(),			/* convert ascii params to doubles */
                   9996: 	degrees=0.0, ipart,fpart;	/* degrees to be rotated */
                   9997: int	idegrees=0, isneg=0;		/* positive ipart, isneg=1 if neg */
                   9998: int	n90=0, isn90=1;			/* degrees is n90 multiples of 90 */
                   9999: /* -------------------------------------------------------------------------
                   10000: obtain {degrees} argument immediately following \rotatebox command
                   10001: -------------------------------------------------------------------------- */
                   10002: /* --- parse for {degrees} arg, and bump expression past it --- */
                   10003: *expression = texsubexpr(*expression,degexpr,0,"{","}",0,0);
                   10004: if ( *degexpr == '\000' ) goto end_of_job; /* couldn't get {degrees} */
                   10005: degrees = strtod(degexpr,NULL);		/* degrees to be rotated */
                   10006: if ( degrees < 0.0 )			/* clockwise rotation desired */
                   10007:   { degrees = -degrees;			/* flip sign so degrees positive */
                   10008:     isneg = 1; }			/* and set flag to indicate flip */
                   10009: fpart = modf(degrees,&ipart);		/* integer and fractional parts */
                   10010: ipart = (double)(((int)degrees)%360);	/* degrees mod 360 */
                   10011: degrees = ipart + fpart;		/* restore fractional part */
                   10012: if ( isneg )				/* if clockwise rotation requested */
                   10013:   degrees = 360.0 - degrees;		/* do equivalent counterclockwise */
                   10014: idegrees = (int)(degrees+0.5);		/* integer degrees */
                   10015: n90 = idegrees/90;			/* degrees is n90 multiples of 90 */
                   10016: isn90 = (90*n90==idegrees);		/*true if degrees is multiple of 90*/
                   10017: isn90 = 1;				/* forced true for time being */
                   10018: /* -------------------------------------------------------------------------
                   10019: obtain {subexpr} argument after {degrees}, and rasterize it
                   10020: -------------------------------------------------------------------------- */
                   10021: /* --- parse for {subexpr} arg, and bump expression past it --- */
                   10022: *expression = texsubexpr(*expression,subexpr,0,"{","}",0,0);
                   10023: /* --- rasterize subexpression to be rotated --- */
                   10024: if ( (rotsp = rasterize(subexpr,size))	/* rasterize subexpression */
                   10025: ==   NULL ) goto end_of_job;		/* and quit if failed */
                   10026: /* --- return unmodified image if no rotation requested --- */
                   10027: if ( abs(idegrees) < 2 ) goto end_of_job; /* don't bother rotating image */
                   10028: /* --- extract params for image to be rotated --- */
                   10029: rotrp = rotsp->image;			/* unrotated rasterized image */
                   10030: baseline = rotsp->baseline;		/* and baseline of that image */
                   10031: /* -------------------------------------------------------------------------
                   10032: rotate by multiples of 90 degrees
                   10033: -------------------------------------------------------------------------- */
                   10034: if ( isn90 )				/* rotation by multiples of 90 */
                   10035:  if ( n90 > 0 )				/* do nothing for 0 degrees */
                   10036:   {
                   10037:   n90 = 4-n90;				/* rasrot() rotates clockwise */
                   10038:   while ( n90 > 0 )			/* still have remaining rotations */
                   10039:     { raster *nextrp = rastrot(rotrp);	/* rotate raster image */
                   10040:       if ( nextrp == NULL ) break;	/* something's terribly wrong */
                   10041:       delete_raster(rotrp);		/* free previous raster image */
                   10042:       rotrp = nextrp;			/* and replace it with rotated one */
                   10043:       n90--; }				/* decrement remaining count */
                   10044:   } /* --- end-of-if(isn90) --- */
                   10045: /* -------------------------------------------------------------------------
                   10046: requested rotation not multiple of 90 degrees
                   10047: -------------------------------------------------------------------------- */
                   10048: if ( !isn90 )				/* explicitly construct rotation */
                   10049:   { ; }					/* not yet implemented */
                   10050: /* -------------------------------------------------------------------------
                   10051: re-populate subraster envelope with rotated image
                   10052: -------------------------------------------------------------------------- */
                   10053: /* --- re-init various subraster parameters, embedding raster in it --- */
                   10054: if ( rotrp != NULL )			/* rotated raster constructed okay */
                   10055:  { rotsp->type = IMAGERASTER;		/* signal constructed image */
                   10056:    rotsp->image = rotrp;		/* raster we just constructed */
                   10057:    /* --- now try to guess pleasing baseline --- */
1.3     ! albertel 10058:    if ( idegrees > 2 ) {		/* leave unchanged if unrotated */
1.1       albertel 10059:     if ( strlen(subexpr) < 3		/* we rotated a short expression */
                   10060:     ||   abs(idegrees-180) < 3 )	/* or just turned it upside-down */
                   10061:       baseline = rotrp->height - 1;	/* so set with nothing descending */
                   10062:     else				/* rotated a long expression */
1.3     ! albertel 10063:       baseline = (65*(rotrp->height-1))/100; } /* roughly center long expr */
1.1       albertel 10064:    rotsp->baseline = baseline; }	/* set baseline as calculated above*/
                   10065: /* --- return rotated subexpr to caller --- */
                   10066: end_of_job:
                   10067:   return ( rotsp );			/*return rotated subexpr to caller*/
                   10068: } /* --- end-of-function rastrotate() --- */
                   10069: 
                   10070: 
                   10071: /* ==========================================================================
1.3     ! albertel 10072:  * Function:	rastreflect ( expression, size, basesp, arg1, arg2, arg3 )
        !          10073:  * Purpose:	\reflectbox[axis]{subexpression} handler, returns subraster
        !          10074:  *		containing subexpression reflected horizontally (i.e., around
        !          10075:  *		vertical axis, |_ becomes _|) if [axis] not given or axis=1,
        !          10076:  *		or reflected vertically if axis=2 given.
        !          10077:  * --------------------------------------------------------------------------
        !          10078:  * Arguments:	expression (I/O) char **  to first char of null-terminated
        !          10079:  *				string immediately following \reflectbox to
        !          10080:  *				be rasterized, and returning ptr immediately
        !          10081:  *				following last character processed.
        !          10082:  *		size (I)	int containing 0-7 default font size
        !          10083:  *		basesp (I)	subraster *  to character (or subexpression)
        !          10084:  *				immediately preceding \reflectbox
        !          10085:  *				(unused, but passed for consistency)
        !          10086:  *		arg1 (I)	int unused
        !          10087:  *		arg2 (I)	int unused
        !          10088:  *		arg3 (I)	int unused
        !          10089:  * --------------------------------------------------------------------------
        !          10090:  * Returns:	( subraster * )	ptr to subraster corresponding to \reflectbox
        !          10091:  *				requested, or NULL for any parsing error
        !          10092:  * --------------------------------------------------------------------------
        !          10093:  * Notes:     o	Summary of syntax...
        !          10094:  *		  \reflectbox[axis]{subexpression}
        !          10095:  *	      o
        !          10096:  * ======================================================================= */
        !          10097: /* --- entry point --- */
        !          10098: subraster *rastreflect ( char **expression, int size, subraster *basesp,
        !          10099: 			int arg1, int arg2, int arg3 )
        !          10100: {
        !          10101: /* -------------------------------------------------------------------------
        !          10102: Allocations and Declarations
        !          10103: -------------------------------------------------------------------------- */
        !          10104: char	*texsubexpr(), subexpr[MAXSUBXSZ+1], *axisexpr=subexpr; /* args */
        !          10105: subraster *rasterize(), *refsp=NULL;	/* subraster for reflected subexpr */
        !          10106: raster	*rastref(), *refrp=NULL;	/* reflect subraster->image */
        !          10107: int	axis = 1;			/* default horizontal reflection */
        !          10108: int	delete_raster();		/* delete intermediate raster */
        !          10109: int	baseline=0;			/* baseline of rasterized image */
        !          10110: /* -------------------------------------------------------------------------
        !          10111: obtain [axis] argument immediately following \reflectbox command, if given
        !          10112: -------------------------------------------------------------------------- */
        !          10113: /* --- check for optional [axis] arg  --- */
        !          10114: if ( *(*expression) == '[' )		/*check for []-enclosed optional arg*/
        !          10115:   { *expression = texsubexpr(*expression,axisexpr,255,"[","]",0,0);
        !          10116:     axis = atoi(axisexpr);		/* convert [axis] to int */
        !          10117:     if ( axis<1 || axis>2 )		/* check axis input */
        !          10118:       axis = 1; }			/* back to default if illegal */
        !          10119: /* -------------------------------------------------------------------------
        !          10120: obtain {subexpr} argument after optional [axis], and rasterize it
        !          10121: -------------------------------------------------------------------------- */
        !          10122: /* --- parse for {subexpr} arg, and bump expression past it --- */
        !          10123: *expression = texsubexpr(*expression,subexpr,0,"{","}",0,0);
        !          10124: /* --- rasterize subexpression to be reflected --- */
        !          10125: if ( (refsp = rasterize(subexpr,size))	/* rasterize subexpression */
        !          10126: ==   NULL ) goto end_of_job;		/* and quit if failed */
        !          10127: /* --- return unmodified image if no reflection requested --- */
        !          10128: if ( axis<1 || axis>2 ) goto end_of_job; /* don't bother reflecting image */
        !          10129: /* --- extract params for image to be reflected --- */
        !          10130: refrp = refsp->image;			/* unreflected rasterized image */
        !          10131: baseline = refsp->baseline;		/* and baseline of that image */
        !          10132: /* -------------------------------------------------------------------------
        !          10133: reflect image and adjust its parameters
        !          10134: -------------------------------------------------------------------------- */
        !          10135: /* --- reflect image --- */
        !          10136: refrp = rastref(refsp->image,axis);	/* reflect raster image */
        !          10137: if ( refrp == NULL ) goto end_of_job;	/* failed to reflect image */
        !          10138: delete_raster(refsp->image);		/* free original raster image */
        !          10139: refsp->image = refrp;			/*and replace it with reflected one*/
        !          10140: /* --- adjust parameters --- */
        !          10141: if ( axis == 2 )			/* for vertical reflection */
        !          10142:   baseline = refrp->height - 1;		/* set with nothing descending */
        !          10143: refsp->baseline = baseline;		/* reset baseline of reflected image*/
        !          10144: /* --- return reflected subexpr to caller --- */
        !          10145: end_of_job:
        !          10146:   return ( refsp );			/*back to caller with reflected expr*/
        !          10147: } /* --- end-of-function rastreflect() --- */
        !          10148: 
        !          10149: 
        !          10150: /* ==========================================================================
1.1       albertel 10151:  * Function:	rastfbox ( expression, size, basesp, arg1, arg2, arg3 )
                   10152:  * Purpose:	\fbox{subexpression} handler, returns subraster
                   10153:  *		containing subexpression with frame box drawn around it
                   10154:  * --------------------------------------------------------------------------
                   10155:  * Arguments:	expression (I/O) char **  to first char of null-terminated
                   10156:  *				string immediately following \fbox to be
                   10157:  *				rasterized, and returning ptr immediately
                   10158:  *				following last character processed.
                   10159:  *		size (I)	int containing 0-5 default font size
                   10160:  *		basesp (I)	subraster *  to character (or subexpression)
                   10161:  *				immediately preceding \fbox
                   10162:  *				(unused, but passed for consistency)
                   10163:  *		arg1 (I)	int unused
                   10164:  *		arg2 (I)	int unused
                   10165:  *		arg3 (I)	int unused
                   10166:  * --------------------------------------------------------------------------
                   10167:  * Returns:	( subraster * )	ptr to subraster corresponding to \fbox
                   10168:  *				requested, or NULL for any parsing error
                   10169:  * --------------------------------------------------------------------------
                   10170:  * Notes:     o	Summary of syntax...
                   10171:  *		  \fbox[width][height]{subexpression}
                   10172:  *	      o
                   10173:  * ======================================================================= */
                   10174: /* --- entry point --- */
                   10175: subraster *rastfbox ( char **expression, int size, subraster *basesp,
                   10176: 			int arg1, int arg2, int arg3 )
                   10177: {
                   10178: /* -------------------------------------------------------------------------
                   10179: Allocations and Declarations
                   10180: -------------------------------------------------------------------------- */
1.3     ! albertel 10181: char	*texsubexpr(), subexpr[MAXSUBXSZ+1], widtharg[512]; /* args */
1.1       albertel 10182: subraster *rasterize(), *framesp=NULL;	/* rasterize subexpr to be framed */
                   10183: raster	*border_raster(), *bp=NULL;	/* framed image raster */
                   10184: double	strtod();			/* interpret [width][height] */
                   10185: int	fwidth=6, fthick=1;		/*extra frame width, line thickness*/
                   10186: int	width=(-1), height=(-1),	/* optional [width][height] args */
                   10187: 	iscompose = 0;			/* set true if optional args given */
                   10188: /* -------------------------------------------------------------------------
                   10189: obtain optional [width][height] arguments immediately following \fbox
                   10190: -------------------------------------------------------------------------- */
                   10191: /* --- first check for optional \fbox[width] --- */
                   10192: if ( *(*expression) == '[' )		/* check for []-enclosed width arg */
                   10193:   { *expression = texsubexpr(*expression,widtharg,511,"[","]",0,0);
                   10194:     if ( *widtharg != '\000' )		/* got widtharg */
                   10195:      { width = max2(1,iround(unitlength*strtod(widtharg,NULL)));
                   10196:        height = 1;  fwidth = 2; iscompose = 1; }
                   10197:   } /* --- end-of-if(**expression=='[') --- */
                   10198: if ( width > 0 )			/* found leading [width], so... */
                   10199:  if ( *(*expression) == '[' )		/* check for []-enclosed height arg */
                   10200:   { *expression = texsubexpr(*expression,widtharg,511,"[","]",0,0);
                   10201:     if ( *widtharg != '\000' )		/* got widtharg */
                   10202:      { height = max2(1,iround(unitlength*strtod(widtharg,NULL)));
                   10203:        fwidth = 0; }			/* no extra border */
                   10204:   } /* --- end-of-if(**expression=='[') --- */
                   10205: /* -------------------------------------------------------------------------
                   10206: obtain {subexpr} argument
                   10207: -------------------------------------------------------------------------- */
                   10208: /* --- parse for {subexpr} arg, and bump expression past it --- */
                   10209: *expression = texsubexpr(*expression,subexpr,0,"{","}",0,0);
                   10210: /* --- rasterize subexpression to be framed --- */
                   10211: if ( width<0 || height<0 )		/* no explicit dimensions given */
                   10212:   { if ( (framesp = rasterize(subexpr,size)) /* rasterize subexpression */
                   10213:     ==   NULL ) goto end_of_job; }	/* and quit if failed */
                   10214: else
                   10215:   { char composexpr[8192];		/* compose subexpr with empty box */
                   10216:     sprintf(composexpr,"\\compose{\\hspace{%d}\\vspace{%d}}{%s}",
                   10217:     width,height,subexpr);
                   10218:     if ( (framesp = rasterize(composexpr,size)) /* rasterize subexpression */
                   10219:     ==   NULL ) goto end_of_job; }	/* and quit if failed */
                   10220: /* -------------------------------------------------------------------------
                   10221: draw frame, reset params, and return it to caller
                   10222: -------------------------------------------------------------------------- */
                   10223: /* --- draw border --- */
                   10224: if ( (bp = border_raster(framesp->image,-fwidth,-fwidth,fthick,1))
                   10225: ==   NULL ) goto end_of_job;		/* draw border and quit if failed */
                   10226: /* --- replace original image and raise baseline to accommodate frame --- */
                   10227: framesp->image = bp;			/* replace image with framed one */
                   10228: if ( !iscompose )			/* simple border around subexpr */
                   10229:   framesp->baseline += fwidth;		/* so just raise baseline */
                   10230: else
                   10231:   framesp->baseline = (framesp->image)->height - 1; /* set at bottom */
                   10232: /* --- return framed subexpr to caller --- */
                   10233: end_of_job:
                   10234:   return ( framesp );			/* return framed subexpr to caller */
                   10235: } /* --- end-of-function rastfbox() --- */
                   10236: 
                   10237: 
                   10238: /* ==========================================================================
                   10239:  * Function:	rastinput ( expression, size, basesp, arg1, arg2, arg3 )
                   10240:  * Purpose:	\input{filename} handler, reads filename and returns
                   10241:  *		subraster containing image of expression read from filename
                   10242:  * --------------------------------------------------------------------------
                   10243:  * Arguments:	expression (I/O) char **  to first char of null-terminated
                   10244:  *				string immediately following \input to be
                   10245:  *				rasterized, and returning ptr immediately
                   10246:  *				following last character processed.
                   10247:  *		size (I)	int containing 0-5 default font size
                   10248:  *		basesp (I)	subraster *  to character (or subexpression)
                   10249:  *				immediately preceding \input
                   10250:  *				(unused, but passed for consistency)
                   10251:  *		arg1 (I)	int unused
                   10252:  *		arg2 (I)	int unused
                   10253:  *		arg3 (I)	int unused
                   10254:  * --------------------------------------------------------------------------
                   10255:  * Returns:	( subraster * )	ptr to subraster corresponding to expression
                   10256:  *				in filename, or NULL for any parsing error
                   10257:  * --------------------------------------------------------------------------
                   10258:  * Notes:     o	Summary of syntax...
                   10259:  *		  \input{filename}
                   10260:  *	      o
                   10261:  * ======================================================================= */
                   10262: /* --- entry point --- */
                   10263: subraster *rastinput ( char **expression, int size, subraster *basesp,
                   10264: 			int arg1, int arg2, int arg3 )
                   10265: {
                   10266: /* -------------------------------------------------------------------------
                   10267: Allocations and Declarations
                   10268: -------------------------------------------------------------------------- */
1.3     ! albertel 10269: char	*texsubexpr(), tag[1024]="\000", filename[1024]="\000"; /* args */
1.1       albertel 10270: subraster *rasterize(), *inputsp=NULL; /* rasterized input image */
                   10271: int	status, rastreadfile();	/* read input file */
                   10272: int	format=0, npts=0;	/* don't reformat (numerical) input */
1.3     ! albertel 10273: char	subexpr[MAXFILESZ+1] = "\000", /*concatanated lines from input file*/
1.1       albertel 10274: 	*mimeprep(),		/* preprocess inputted data */
1.2       albertel 10275: 	*dbltoa(), *reformat=NULL; /* reformat numerical input */
1.1       albertel 10276: /* -------------------------------------------------------------------------
                   10277: obtain [tag]{filename} argument
                   10278: -------------------------------------------------------------------------- */
                   10279: /* --- parse for optional [tag] or [fmt] arg, bump expression past it --- */
                   10280: if ( *(*expression) == '[' )		/* check for []-enclosed value */
1.3     ! albertel 10281:   { char argfld[MAXTOKNSZ+1];		/* optional argument field */
        !          10282:     *expression = texsubexpr(*expression,argfld,MAXTOKNSZ,"[","]",0,0);
1.2       albertel 10283:     if ( (reformat=strstr(argfld,"dtoa")) != NULL ) /*dtoa/dbltoa requested*/
                   10284:       {	format = 1;			/* signal dtoa()/dbltoa() format */
1.1       albertel 10285: 	if ( (reformat=strchr(reformat,'=')) != NULL ) /* have dtoa= */
                   10286: 	  npts = (int)strtol(reformat+1,NULL,0); } /* so set npts */
                   10287:     if ( format == 0 )			/* reformat not requested */
                   10288:       strcpy(tag,argfld); }		/* so interpret arg as tag */
                   10289: /* --- parse for {filename} arg, and bump expression past it --- */
                   10290: *expression = texsubexpr(*expression,filename,1023,"{","}",0,0);
                   10291: /* --- check for alternate filename:tag --- */
                   10292: if ( *filename != '\000'		/* got filename */
                   10293: /*&& *tag == '\000'*/ )			/* but no [tag] */
                   10294:  { char	*delim = strchr(filename,':');	/* look for : in filename:tag */
                   10295:    if ( delim != (char *)NULL )		/* found it */
                   10296:     { *delim = '\000';			/* null-terminate filename at : */
                   10297:       strcpy(tag,delim+1); } }		/* and stuff after : is tag */
                   10298: /* --------------------------------------------------------------------------
                   10299: Read file and rasterize constructed subexpression
                   10300: -------------------------------------------------------------------------- */
1.2       albertel 10301: status = rastreadfile(filename,0,tag,subexpr); /* read file */
1.1       albertel 10302: if ( *subexpr == '\000' ) goto end_of_job;   /* quit if problem */
                   10303: /* --- rasterize input subexpression  --- */
                   10304: mimeprep(subexpr);			/* preprocess subexpression */
1.2       albertel 10305: if ( format == 1 )			/* dtoa()/dbltoa() */
1.1       albertel 10306:  { double d = strtod(subexpr,NULL);	/* interpret subexpr as double */
                   10307:    if ( d != 0.0 )			/* conversion to double successful */
1.2       albertel 10308:     if ( (reformat=dbltoa(d,npts)) != NULL ) /* reformat successful */
1.1       albertel 10309:      strcpy(subexpr,reformat); }	/*replace subexpr with reformatted*/
                   10310: inputsp = rasterize(subexpr,size);	/* rasterize subexpression */
                   10311: /* --- return input image to caller --- */
                   10312: end_of_job:
                   10313:   return ( inputsp );			/* return input image to caller */
                   10314: } /* --- end-of-function rastinput() --- */
                   10315: 
                   10316: 
                   10317: /* ==========================================================================
                   10318:  * Function:	rastcounter ( expression, size, basesp, arg1, arg2, arg3 )
                   10319:  * Purpose:	\counter[value]{filename} handler, returns subraster
                   10320:  *		containing image of counter value read from filename
                   10321:  *		(or optional [value]), and increments counter
                   10322:  * --------------------------------------------------------------------------
                   10323:  * Arguments:	expression (I/O) char **  to first char of null-terminated
                   10324:  *				string immediately following \counter to be
                   10325:  *				rasterized, and returning ptr immediately
                   10326:  *				following last character processed.
                   10327:  *		size (I)	int containing 0-5 default font size
                   10328:  *		basesp (I)	subraster *  to character (or subexpression)
                   10329:  *				immediately preceding \counter
                   10330:  *				(unused, but passed for consistency)
                   10331:  *		arg1 (I)	int unused
                   10332:  *		arg2 (I)	int unused
                   10333:  *		arg3 (I)	int unused
                   10334:  * --------------------------------------------------------------------------
                   10335:  * Returns:	( subraster * )	ptr to subraster corresponding to \counter
                   10336:  *				requested, or NULL for any parsing error
                   10337:  * --------------------------------------------------------------------------
                   10338:  * Notes:     o	Summary of syntax...
1.2       albertel 10339:  *		  \counter[value][logfile]{filename:tag}
                   10340:  *	      o	:tag is optional
1.1       albertel 10341:  * ======================================================================= */
                   10342: /* --- entry point --- */
                   10343: subraster *rastcounter ( char **expression, int size, subraster *basesp,
                   10344: 			int arg1, int arg2, int arg3 )
                   10345: {
                   10346: /* -------------------------------------------------------------------------
                   10347: Allocations and Declarations
                   10348: -------------------------------------------------------------------------- */
                   10349: char	*texsubexpr(), filename[1024]="\000", /* counter file */
1.3     ! albertel 10350: 	logfile[1024]="\000", tag[1024]="\000"; /*optional log file and tag*/
1.1       albertel 10351: subraster *rasterize(), *countersp=NULL; /* rasterized counter image */
                   10352: FILE	/* *fp=NULL,*/ *logfp=NULL; /* counter and log file pointers */
1.2       albertel 10353: int	status=0,rastreadfile(),rastwritefile(), /*read,write counter file*/
                   10354: 	isstrict = 1;		/* true to only write to existing files */
1.3     ! albertel 10355: char	text[MAXFILESZ] = "1_",	/* only line in counter file without tags */
1.1       albertel 10356: 	*delim = NULL,		/* delimiter in text */
1.2       albertel 10357: 	utext[128] = "1_",	/* default delimiter */
1.1       albertel 10358: 	*udelim = utext+1;	/* underscore delimiter */
1.2       albertel 10359: char	*rasteditfilename(),	/* edit log file name */
                   10360: 	*timestamp(),		/* timestamp for logging */
                   10361: 	*dbltoa();		/* double to comma-separated ascii */
1.1       albertel 10362: int	counter = 1,		/* atoi(text) (after _ removed, if present) */
1.2       albertel 10363: 	value = 1,		/* optional [value] argument */
                   10364: 	gotvalue = 0,		/* set true if [value] supplied */
                   10365: 	isdelta = 0,		/* set true if [+value] or [-value] is delta*/
1.1       albertel 10366: 	ordindex = (-1);	/* ordinal[] index to append ordinal suffix */
                   10367: /*--- ordinal suffixes based on units digit of counter ---*/
                   10368: static	char *ordinal[]={"th","st","nd","rd","th","th","th","th","th","th"};
                   10369: static	char *logvars[]={"REMOTE_ADDR","HTTP_REFERER",NULL}; /* log vars*/
                   10370: static	int  commentvar = 1;	/* logvars[commentvar] replaced by comment */
                   10371: /* -------------------------------------------------------------------------
                   10372: first obtain optional [value][logfile] args immediately following \counter
                   10373: -------------------------------------------------------------------------- */
                   10374: /* --- first check for optional \counter[value] --- */
                   10375: if ( *(*expression) == '[' )		/* check for []-enclosed value */
1.2       albertel 10376:   { *expression = texsubexpr(*expression,text,1023,"[","]",0,0);
                   10377:     if ( *text != '\000' )		/* got counter value (or logfile) */
1.3     ! albertel 10378:      if ( strlen(text) >= 1 ) {		/* and it's not an empty string */
1.2       albertel 10379:       if ( isthischar(*text,"+-0123456789") ) /* check for leading +-digit */
                   10380: 	gotvalue = 1;			/* signal we got optional value */
                   10381:       else				/* not +-digit, so must be logfile */
1.3     ! albertel 10382: 	strcpy(logfile,text); }		/* so just copy it */
1.1       albertel 10383:   } /* --- end-of-if(**expression=='[') --- */
                   10384: /* --- next check for optional \counter[][logfile] --- */
                   10385: if ( *(*expression) == '[' )		/* check for []-enclosed logfile */
                   10386:   { *expression = texsubexpr(*expression,filename,1023,"[","]",0,0);
1.2       albertel 10387:     if ( *filename != '\000' )		/* got logfile (or counter value) */
1.3     ! albertel 10388:      if ( strlen(filename) >= 1 ) {	/* and it's not an empty string */
1.2       albertel 10389:       if ( !(isthischar(*text,"+-0123456789")) /* not a leading +-digit */
                   10390:       ||   gotvalue )			/* or we already got counter value */
                   10391: 	strcpy(logfile,filename);	/* so just copy it */
                   10392:       else				/* leading +-digit must be value */
                   10393: 	{ strcpy(text,filename);	/* copy value to text line */
1.3     ! albertel 10394: 	  gotvalue = 1; } }		/* and signal we got optional value*/
1.1       albertel 10395:   } /* --- end-of-if(**expression=='[') --- */
1.2       albertel 10396: /* --- evaluate [value] if present --- */
                   10397: if ( gotvalue ) {			/*leading +-digit should be in text*/
                   10398:  if ( *text == '+' ) isdelta = (+1);	/* signal adding */
                   10399:  if ( *text == '-' ) isdelta = (-1);	/* signal subtracting */
                   10400:  value = (int)(strtod((isdelta==0?text:text+1),&udelim)+0.1); /*abs(value)*/
                   10401:  if ( isdelta == (-1) ) value = (-value); /* set negative value if needed */
                   10402:  counter = value;			/* re-init counter */
                   10403:  } /* --- end-of-if(gotvalue) --- */
1.1       albertel 10404: /* -------------------------------------------------------------------------
                   10405: obtain counter {filename} argument
                   10406: -------------------------------------------------------------------------- */
                   10407: /* --- parse for {filename} arg, and bump expression past it --- */
                   10408: *expression = texsubexpr(*expression,filename,1023,"{","}",0,0);
                   10409: /* --- check for counter filename:tag --- */
                   10410: if ( *filename != '\000' )		/* got filename */
                   10411:  if ( (delim=strchr(filename,':'))	/* look for : in filename:tag */
                   10412:  !=   (char *)NULL )			/* found it */
                   10413:   { *delim = '\000';			/* null-terminate filename at : */
                   10414:     strcpy(tag,delim+1); }		/* and stuff after : is tag */
                   10415: /* --------------------------------------------------------------------------
                   10416: Read and parse file, increment and rewrite counter (with optional underscore)
                   10417: -------------------------------------------------------------------------- */
                   10418: if ( strlen(filename) > 1 )		/* make sure we got {filename} arg */
                   10419:   {
                   10420:   /* --- read and interpret first (and only) line from counter file --- */
1.2       albertel 10421:   if ( !gotvalue || (isdelta!=0) )	/*if no [count] arg or if delta arg*/
                   10422:    if ( (status=rastreadfile(filename,1,tag,text)) > 0 ) /*try reading file*/
                   10423:     { char *vdelim = NULL;		/* underscore delim from file */
                   10424:       double fileval  = strtod(text,&vdelim); /* value and delim from file */
                   10425:       counter = (int)(fileval<0.0?fileval-0.1:fileval+0.1); /* integerized */
                   10426:       counter += value;			/* bump count by 1 or add/sub delta*/
                   10427:       if ( !gotvalue ) udelim=vdelim; }	/* default to file's current delim */
1.1       albertel 10428:   /* --- check for ordinal suffix --- */
                   10429:   if ( udelim != (char *)NULL )		/* have some delim after value */
                   10430:    if ( *udelim == '_' )		/* underscore signals ordinal */
1.2       albertel 10431:     { int abscount = (counter>=0?counter:(-counter)); /* abs(counter) */
                   10432:       ordindex = abscount%10;		/* least significant digit */
                   10433:       if ( abscount >= 10 )		/* counter is 10 or greater */
                   10434:        if ( (abscount/10)%10 == 1 )	/* and the last two are 10-19 */
1.1       albertel 10435: 	ordindex = 0; }		/* use th for 11,12,13 rather than st,nd,rd */
                   10436:   /* --- rewrite counter file --- */
1.2       albertel 10437:   if ( status >= 0 )			/* file was read okay */
                   10438:    { sprintf(text,"%d",counter);	/*build image of incremented counter*/
                   10439:      if ( ordindex >= 0 ) strcat(text,"_"); /* tack on _ */
                   10440:      if ( *tag == '\000' ) strcat(text,"\n"); /* and newline */
                   10441:      status = rastwritefile(filename,tag,text,isstrict); } /*rewrite counter*/
1.1       albertel 10442:   } /* --- end-of-if(strlen(filename)>1) --- */
                   10443: /* --------------------------------------------------------------------------
                   10444: log counter request
                   10445: -------------------------------------------------------------------------- */
                   10446: if ( strlen(logfile) > 1 )		/* optional [logfile] given */
                   10447:  {
                   10448:  char	comment[1024] = "\000",		/* embedded comment, logfile:comment*/
                   10449: 	*commptr = strchr(logfile,':');	/* check for : signalling comment */
1.2       albertel 10450:  int	islogokay = 1;			/* logfile must exist if isstrict */
1.1       albertel 10451:  if ( commptr != NULL )			/* have embedded comment */
                   10452:   { strcpy(comment,commptr+1);		/* comment follows : */
                   10453:     *commptr = '\000'; }		/* null-terminate actual logfile */
1.2       albertel 10454:  strcpy(logfile,rasteditfilename(logfile)); /* edit log file name */
                   10455:  if ( *logfile == '\000' ) islogokay = 0; /* given an invalid file name */
                   10456:  else if ( isstrict ) {			/*okay, but only write if it exists*/
                   10457:   if ( (logfp=fopen(logfile,"r")) == (FILE *)NULL ) /*doesn't already exist*/
                   10458:     islogokay = 0;			/* so don't write log file */
                   10459:   else fclose(logfp); }			/* close file opened for test read */
                   10460:  if ( islogokay )			/* okay to write logfile */
                   10461:   if ( (logfp = fopen(logfile,"a"))	/* open logfile */
                   10462:   != (FILE *)NULL ) {			/* opened successfully for append */
                   10463:    int	ilog=0;				/* logvars[] index */
                   10464:    fprintf(logfp,"%s  ",timestamp(TZDELTA,0)); /* first emit timestamp */
                   10465:    if (*tag=='\000') fprintf(logfp,"%s",filename); /* emit counter filename */
                   10466:    else fprintf(logfp,"<%s>",tag);	/* or tag if we have one */
                   10467:    fprintf(logfp,"=%d",counter);	/* emit counter value */
                   10468:    if ( status < 1 )			/* read or re-write failed */
                   10469:     fprintf(logfp,"(%s %d)","error status",status); /* emit error */
                   10470:    for ( ilog=0; logvars[ilog] != NULL; ilog++ ) /* log till end-of-table */
                   10471:     if ( ilog == commentvar		/* replace with comment... */
                   10472:     &&   commptr != NULL )		/* ...if available */  
                   10473:      fprintf(logfp,"  %.256s",comment); /* log embedded comment */
                   10474:     else
                   10475:      { char *logval = getenv(logvars[ilog]); /*getenv(variable) to be logged*/
                   10476:        fprintf(logfp,"  %.64s",		/* log variable */
1.1       albertel 10477: 	(logval!=NULL?logval:"<unknown>")); } /* emit value or <unknown> */
1.2       albertel 10478:    fprintf(logfp,"\n");			/* terminating newline */
                   10479:    fclose(logfp);			/* close logfile */
                   10480:    } /* --- end-of-if(islogokay&&logfp!=NULL) --- */
1.1       albertel 10481:  } /* --- end-of-if(strlen(logfile)>1) --- */
                   10482: /* --------------------------------------------------------------------------
                   10483: construct counter expression and rasterize it
                   10484: -------------------------------------------------------------------------- */
                   10485: /* --- construct expression --- */
                   10486: /*sprintf(text,"%d",counter);*/		/* start with counter */
1.2       albertel 10487: strcpy(text,dbltoa(((double)counter),0)); /* comma-separated counter value */
1.1       albertel 10488: if ( ordindex >= 0 )			/* need to tack on ordinal suffix */
                   10489:   { strcat(text,"^{\\underline{\\rm~");	/* start with ^ and {\underline{\rm */
                   10490:     strcat(text,ordinal[ordindex]);	/* then st,nd,rd, or th */
                   10491:     strcat(text,"}}"); }		/* finish with }} */
                   10492: /* --- rasterize it --- */
                   10493: countersp = rasterize(text,size);	/* rasterize counter subexpression */
                   10494: /* --- return counter image to caller --- */
                   10495: /*end_of_job:*/
                   10496:   return ( countersp );			/* return counter image to caller */
                   10497: } /* --- end-of-function rastcounter() --- */
                   10498: 
                   10499: 
                   10500: /* ==========================================================================
1.2       albertel 10501:  * Function:	rasttoday ( expression, size, basesp, arg1, arg2, arg3 )
                   10502:  * Purpose:	handle \today
                   10503:  * --------------------------------------------------------------------------
                   10504:  * Arguments:	expression (I/O) char **  to first char of null-terminated
                   10505:  *				string immediately following \today,
                   10506:  *				and returning ptr immediately
                   10507:  *				following last character processed.
                   10508:  *		size (I)	int containing 0-5 default font size
                   10509:  *		basesp (I)	subraster *  to character (or subexpression)
                   10510:  *				immediately preceding \today
                   10511:  *				(unused, but passed for consistency)
                   10512:  *		arg1 (I)	int unused
                   10513:  *		arg2 (I)	int unused
                   10514:  *		arg3 (I)	int unused
                   10515:  * --------------------------------------------------------------------------
                   10516:  * Returns:	( subraster * )	subraster ptr to date stamp
                   10517:  * --------------------------------------------------------------------------
                   10518:  * Notes:     o
                   10519:  * ======================================================================= */
                   10520: /* --- entry point --- */
                   10521: subraster *rasttoday ( char **expression, int size, subraster *basesp,
                   10522: 			int arg1, int arg2, int arg3 )
                   10523: {
                   10524: /* -------------------------------------------------------------------------
                   10525: Allocations and Declarations
                   10526: -------------------------------------------------------------------------- */
                   10527: char	*texsubexpr(), optarg[2050];	/* optional [+/-tzdelta,ifmt] args */
                   10528: char	*timestamp(), *today=optarg;	/* timestamp to be rasterized */
                   10529: subraster *rasterize(), *todaysp=NULL;	/* rasterize timestamp */
                   10530: int	ifmt=1, tzdelta=0;		/* default timestamp() args */
                   10531: /* -------------------------------------------------------------------------
                   10532: Get optional args \today[+/-tzdelta,ifmt]
                   10533: -------------------------------------------------------------------------- */
                   10534: /* --- check for optional \today[+/-tzdelta,ifmt] --- */
                   10535: if ( *(*expression) == '[' )		/* check for []-enclosed value */
                   10536:   { *expression = texsubexpr(*expression,optarg,2047,"[","]",0,0);
                   10537:     if ( *optarg != '\000' )		/* got optional arg */
                   10538:      { char *comma = strchr(optarg,','); /* comma between +/-tzdelta,ifmt */
                   10539:        int iarg, nargs=(comma==NULL?1:2); /* #optional args between []'s */
                   10540:        if ( comma != NULL ) *comma = '\000'; /* null-terminate first arg */
                   10541:        for ( iarg=1; iarg<=nargs; iarg++ ) /* process one or both args */
                   10542: 	{ char *arg = (iarg==1?optarg:comma+1); /* choose 1st or 2nd arg */
                   10543: 	  if ( isthischar(*arg,"+-") )	/* leading +/- signals tzdelta */
                   10544: 	    tzdelta = atoi(arg);	/* so interpret arg as tzdelta */
                   10545: 	  else ifmt = atoi(arg); }	/* else interpret args as ifmt */
                   10546:      } /* --- end-of-if(*optarg!='\0') --- */
                   10547:   } /* --- end-of-if(**expression=='[') --- */
                   10548: /* -------------------------------------------------------------------------
                   10549: Get timestamp and rasterize it
                   10550: -------------------------------------------------------------------------- */
                   10551: strcpy(today,"\\text{");		/* rasterize timestamp as text */
                   10552: strcat(today,timestamp(tzdelta,ifmt));	/* get timestamp */
                   10553: strcat(today,"}");			/* terminate \text{} braces */
                   10554: todaysp = rasterize(today,size);	/* rasterize timestamp */
                   10555: /* --- return timestamp raster to caller --- */
                   10556: /*end_of_job:*/
                   10557:   return ( todaysp );			/* return timestamp to caller */
                   10558: } /* --- end-of-function rasttoday() --- */
                   10559: 
                   10560: 
                   10561: /* ==========================================================================
                   10562:  * Function:	rastcalendar ( expression, size, basesp, arg1, arg2, arg3 )
                   10563:  * Purpose:	handle \calendar
                   10564:  * --------------------------------------------------------------------------
                   10565:  * Arguments:	expression (I/O) char **  to first char of null-terminated
                   10566:  *				string immediately following \calendar
                   10567:  *				and returning ptr immediately
                   10568:  *				following last character processed.
                   10569:  *		size (I)	int containing 0-5 default font size
                   10570:  *		basesp (I)	subraster *  to character (or subexpression)
                   10571:  *				immediately preceding \calendar
                   10572:  *				(unused, but passed for consistency)
                   10573:  *		arg1 (I)	int unused
                   10574:  *		arg2 (I)	int unused
                   10575:  *		arg3 (I)	int unused
                   10576:  * --------------------------------------------------------------------------
                   10577:  * Returns:	( subraster * )	subraster ptr to rendered one-month calendar
                   10578:  * --------------------------------------------------------------------------
                   10579:  * Notes:     o
                   10580:  * ======================================================================= */
                   10581: /* --- entry point --- */
                   10582: subraster *rastcalendar ( char **expression, int size, subraster *basesp,
                   10583: 			int arg1, int arg2, int arg3 )
                   10584: {
                   10585: /* -------------------------------------------------------------------------
                   10586: Allocations and Declarations
                   10587: -------------------------------------------------------------------------- */
                   10588: char	*texsubexpr(), optarg[2050];	/* optional [year,month] args */
                   10589: char	*calendar(), *calstr=NULL;	/* calendar to be rasterized */
                   10590: subraster *rasterize(), *calendarsp=NULL; /* rasterize calendar string */
                   10591: int	year=0,month=0,day=0, argval=0;	/* default calendar() args */
                   10592: /* -------------------------------------------------------------------------
                   10593: Get optional args \today[+/-tzdelta,ifmt]
                   10594: -------------------------------------------------------------------------- */
                   10595: /* --- check for optional \calendar[year,month] --- */
                   10596: if ( *(*expression) == '[' )		/* check for []-enclosed value */
                   10597:   { *expression = texsubexpr(*expression,optarg,2047,"[","]",0,0);
                   10598:     if ( *optarg != '\000' )		/* got optional arg */
                   10599:      { char *comma = strchr(optarg,','), /* comma between year,month */
                   10600:        *comma2 = NULL;			/* second comma before day */
                   10601:        int iarg, nargs=(comma==NULL?1:2); /* #optional args between []'s */
                   10602:        if ( comma != NULL ) { *comma = '\000'; /*null-terminate first arg*/
                   10603: 	if ( (comma2=strchr(comma+1,',')) != NULL ) /* have third arg */
                   10604: 	 { *comma2 = '\000'; nargs++; } } /* null-term 2nd arg, bump count */
                   10605:        for ( iarg=1; iarg<=nargs; iarg++ ) /* process one or both args */
                   10606: 	{ char *arg= (iarg==1?optarg:(iarg==2?comma+1:comma2+1)); /*get arg*/
                   10607: 	  argval = atoi(arg);		/* interpret arg as integer */
                   10608: 	  if ( iarg < 3 )		/* first two args are month,year */
                   10609: 	   {if ( argval>1972 && argval<2100 ) year = argval; /* year value */
                   10610: 	    else if ( argval>=1 && argval<=12 ) month = argval;} /*or month*/
                   10611: 	  else				/* only 3rd arg can be day */
                   10612: 	   if ( argval>=1 && argval<=31 ) day = argval; } /* day value */
                   10613:      } /* --- end-of-if(*optarg!='\0') --- */
                   10614:   } /* --- end-of-if(**expression=='[') --- */
                   10615: /* -------------------------------------------------------------------------
                   10616: Get calendar string and rasterize it
                   10617: -------------------------------------------------------------------------- */
                   10618: if ( msgfp!= NULL && msglevel>=9 )
                   10619:   fprintf(msgfp,"rastcalendar> year=%d, month=%d, day=%d\n",
                   10620:   year,month,day);
                   10621: calstr = calendar(year,month,day);		/* get calendar string */
                   10622: calendarsp = rasterize(calstr,size);	/* rasterize calendar string */
                   10623: /* --- return calendar raster to caller --- */
                   10624: /*end_of_job:*/
                   10625:   return ( calendarsp );		/* return calendar to caller */
                   10626: } /* --- end-of-function rastcalendar() --- */
                   10627: 
                   10628: 
                   10629: /* ==========================================================================
1.1       albertel 10630:  * Function:	rastnoop ( expression, size, basesp, nargs, arg2, arg3 )
                   10631:  * Purpose:	no op -- flush \escape without error
                   10632:  * --------------------------------------------------------------------------
                   10633:  * Arguments:	expression (I/O) char **  to first char of null-terminated
                   10634:  *				string immediately following \escape to be
                   10635:  *				flushed, and returning ptr immediately
                   10636:  *				following last character processed.
                   10637:  *		size (I)	int containing 0-5 default font size
                   10638:  *		basesp (I)	subraster *  to character (or subexpression)
1.2       albertel 10639:  *				immediately preceding \escape
1.1       albertel 10640:  *				(unused, but passed for consistency)
                   10641:  *		nargs (I)	int containing number of {}-args after
                   10642:  *				\escape to be flushed along with it
                   10643:  *		arg2 (I)	int unused
                   10644:  *		arg3 (I)	int unused
                   10645:  * --------------------------------------------------------------------------
                   10646:  * Returns:	( subraster * )	NULL subraster ptr
                   10647:  * --------------------------------------------------------------------------
                   10648:  * Notes:     o
                   10649:  * ======================================================================= */
                   10650: /* --- entry point --- */
                   10651: subraster *rastnoop ( char **expression, int size, subraster *basesp,
                   10652: 			int nargs, int arg2, int arg3 )
                   10653: {
                   10654: /* -------------------------------------------------------------------------
                   10655: Allocations and Declarations
                   10656: -------------------------------------------------------------------------- */
1.3     ! albertel 10657: char	*texsubexpr(), subexpr[MAXSUBXSZ+1]; /*dummy args eaten by \escape*/
1.1       albertel 10658: subraster *rasterize(), *noopsp=NULL;	/* rasterize subexpr */
                   10659: /* --- flush accompanying args if necessary --- */
                   10660: if ( nargs != NOVALUE			/* not unspecified */
                   10661: &&   nargs > 0 )			/* and args to be flushed */
                   10662:   while ( --nargs >= 0 )		/* count down */
                   10663:     *expression = texsubexpr(*expression,subexpr,0,"{","}",0,0); /*flush arg*/
                   10664: /* --- return null ptr to caller --- */
                   10665: /*end_of_job:*/
                   10666:   return ( noopsp );			/* return NULL ptr to caller */
                   10667: } /* --- end-of-function rastnoop() --- */
                   10668: 
                   10669: 
                   10670: /* ==========================================================================
                   10671:  * Function:	rastopenfile ( filename, mode )
                   10672:  * Purpose:	Opens filename[.tex] in mode, returning FILE *
                   10673:  * --------------------------------------------------------------------------
                   10674:  * Arguments:	filename (I/O)	char * to null-terminated string containing
                   10675:  *				name of file to open (preceded by path
                   10676:  *				relative to mimetex executable)
                   10677:  *				If fopen() fails, .tex appeneded,
                   10678:  *				and returned if that fopen() succeeds
                   10679:  *		mode (I)	char * to null-terminated string containing
                   10680:  *				fopen() mode
                   10681:  * --------------------------------------------------------------------------
                   10682:  * Returns:	( FILE * )	pointer to opened file, or NULL if error
                   10683:  * --------------------------------------------------------------------------
                   10684:  * Notes:     o
                   10685:  * ======================================================================= */
                   10686: /* --- entry point --- */
                   10687: FILE	*rastopenfile ( char *filename, char *mode )
                   10688: {
                   10689: /* -------------------------------------------------------------------------
                   10690: Allocations and Declarations
                   10691: -------------------------------------------------------------------------- */
                   10692: FILE	*fp = (FILE *)NULL /*,*fopen()*/; /*file pointer to opened filename*/
1.3     ! albertel 10693: char	texfile[2050] = "\000",		/* local, edited copy of filename */
1.2       albertel 10694: 	*rasteditfilename(),		/* prepend pathprefix if necessary */
1.3     ! albertel 10695: 	amode[512] = "r";		/* test open mode if arg mode=NULL */
1.2       albertel 10696: int	ismode = 0;			/* true of mode!=NULL */
1.1       albertel 10697: /* --------------------------------------------------------------------------
                   10698: Check mode and open file
                   10699: -------------------------------------------------------------------------- */
1.2       albertel 10700: /* --- edit filename --- */
1.3     ! albertel 10701: strncpy(texfile,rasteditfilename(filename),2047); /*edited copy of filename*/
        !          10702: texfile[2047] = '\000';			/* make sure it's null terminated */
1.1       albertel 10703: /* --- check mode --- */
                   10704: if ( mode != (char *)NULL )		/* caller passed mode arg */
                   10705:  if ( *mode != '\000' )			/* and it's not an empty string */
                   10706:   { ismode = 1;				/* so flip mode flag true */
1.3     ! albertel 10707:     strncpy(amode,mode,254);		/* and replace "r" with caller's */
        !          10708:     amode[254] = '\000';		/* make sure it's null terminated */
1.1       albertel 10709:     compress(amode,' '); }		/* remove embedded blanks */
                   10710: /* --- open filename or filename.tex --- */
                   10711: if ( strlen(texfile) > 1 )		/* make sure we got actual filename*/
                   10712:   if ( (fp = fopen(texfile,amode))	/* try opening given filename */
                   10713:   ==   NULL )				/* failed to open given filename */
                   10714:   { strcpy(filename,texfile);		/* signal possible filename error */
                   10715:     strcat(texfile,".tex");		/* but first try adding .tex */
                   10716:     if ( (fp = fopen(texfile,amode))	/* now try opening filename.tex */
                   10717:     !=   NULL )				/* filename.tex succeeded */
                   10718:       strcpy(filename,texfile); }	/* replace caller's filename */
                   10719: /* --- close file if only opened to check name --- */
                   10720: if ( !ismode && fp!=NULL )		/* no mode, so just checking */
                   10721:   fclose(fp);				/* close file, fp signals success */
                   10722: /* --- return fp or NULL to caller --- */
                   10723: /*end_of_job:*/
                   10724:   if ( msglevel>=9 && msgfp!=NULL )	/* debuging */
                   10725:     { fprintf(msgfp,"rastopenfile> returning fopen(%s,%s) = %s\n",
                   10726:       filename,amode,(fp==NULL?"NULL":"Okay")); fflush(msgfp); }
                   10727:   return ( fp );			/* return fp or NULL to caller */
                   10728: } /* --- end-of-function rastopenfile() --- */
                   10729: 
                   10730: 
                   10731: /* ==========================================================================
1.2       albertel 10732:  * Function:	rasteditfilename ( filename )
                   10733:  * Purpose:	edits filename to remove security problems,
                   10734:  *		e.g., removes all ../'s and ..\'s.
                   10735:  * --------------------------------------------------------------------------
                   10736:  * Arguments:	filename (I)	char * to null-terminated string containing
                   10737:  *				name of file to be edited
                   10738:  * --------------------------------------------------------------------------
                   10739:  * Returns:	( char * )	pointer to edited filename,
                   10740:  *				or empty string "\000" if any problem
                   10741:  * --------------------------------------------------------------------------
                   10742:  * Notes:     o
                   10743:  * ======================================================================= */
                   10744: /* --- entry point --- */
                   10745: char	*rasteditfilename ( char *filename )
                   10746: {
                   10747: /* -------------------------------------------------------------------------
                   10748: Allocations and Declarations
                   10749: -------------------------------------------------------------------------- */
1.3     ! albertel 10750: static	char editname[2050];		/*edited filename returned to caller*/
1.2       albertel 10751: char	*strchange();			/* prepend pathprefix if necessary */
                   10752: int	strreplace(),			/* remove ../'s and ..\'s */
                   10753: 	isprefix = (*pathprefix=='\000'?0:1); /* true if paths have prefix */
                   10754: /* --------------------------------------------------------------------------
                   10755: edit filename
                   10756: -------------------------------------------------------------------------- */
                   10757: /* --- first check filename arg --- */
                   10758: *editname = '\000';			/* init edited name as empty string*/
                   10759: if ( filename == (char *)NULL ) goto end_of_job; /* no filename arg */
                   10760: if ( *filename == '\000' ) goto end_of_job; /* filename is an empty string */
                   10761: /* --- init edited filename --- */
                   10762: strcpy(editname,filename);		/* init edited name as input name */
                   10763: compress(editname,' ');			/* remove embedded blanks */
                   10764: /* --- remove leading or embedded ....'s --- */
                   10765: while ( strreplace(editname,"....",NULL,0) > 0 ) ;  /* squeeze out ....'s */
                   10766: /* --- remove leading / and \ and dots (and blanks) --- */
                   10767: if ( *editname != '\000' )		/* still have chars in filename */
                   10768:  while ( isthischar(*editname," ./\\") ) /* absolute paths invalid */
                   10769:    strcpy(editname,editname+1);		/* so flush leading / or \ (or ' ')*/
                   10770: if ( *editname == '\000' ) goto end_of_job; /* no chars left in filename */
                   10771: /* --- remove leading or embedded ../'s and ..\'s --- */
                   10772: while ( strreplace(editname,"../",NULL,0) > 0 ) ;  /* squeeze out ../'s */
                   10773: while ( strreplace(editname,"..\\",NULL,0) > 0 ) ; /* and ..\'s */
                   10774: while ( strreplace(editname,"../",NULL,0) > 0 ) ;  /* and ../'s again */
                   10775: /* --- prepend path prefix (if compiled with -DPATHPREFIX) --- */
                   10776: if ( isprefix && *editname!='\000' )	/* filename is preceded by prefix */
                   10777:   strchange(0,editname,pathprefix);	/* so prepend prefix */
                   10778: end_of_job:
                   10779:   return ( editname );			/* back with edited filename */
                   10780: } /* --- end-of-function rasteditfilename() --- */
                   10781: 
                   10782: 
                   10783: /* ==========================================================================
                   10784:  * Function:	rastreadfile ( filename, islock, tag, value )
1.1       albertel 10785:  * Purpose:	Read filename, returning value as string
                   10786:  *		between <tag>...</tag> or entire file if tag=NULL passed.
                   10787:  * --------------------------------------------------------------------------
                   10788:  * Arguments:	filename (I)	char * to null-terminated string containing
                   10789:  *				name of file to read (preceded by path
                   10790:  *				relative to mimetex executable)
1.2       albertel 10791:  *		islock (I)	int containing 1 to lock file while reading
                   10792:  *				(hopefully done by opening in "r+" mode)
1.1       albertel 10793:  *		tag (I)		char * to null-terminated string containing
                   10794:  *				html-like tagname.  File contents between
                   10795:  *				<tag> and </tag> will be returned, or
                   10796:  *				entire file if tag=NULL passed.
                   10797:  *		value (O)	char * returning value between <tag>...</tag>
                   10798:  *				or entire file if tag=NULL.
                   10799:  * --------------------------------------------------------------------------
                   10800:  * Returns:	( int )		1=okay, 0=some error
                   10801:  * --------------------------------------------------------------------------
                   10802:  * Notes:     o
                   10803:  * ======================================================================= */
                   10804: /* --- entry point --- */
1.2       albertel 10805: int	rastreadfile ( char *filename, int islock, char *tag, char *value )
1.1       albertel 10806: {
                   10807: /* -------------------------------------------------------------------------
                   10808: Allocations and Declarations
                   10809: -------------------------------------------------------------------------- */
                   10810: FILE	*fp = (FILE *)NULL, *rastopenfile(); /* pointer to opened filename */
1.3     ! albertel 10811: char	texfile[1024] = "\000",		/* local copy of input filename */
        !          10812: 	text[MAXLINESZ+1];		/* line from input file */
        !          10813: char	*tagp, tag1[1024], tag2[1024];	/* left <tag> and right <tag/> */
        !          10814: int	vallen=0, maxvallen=MAXFILESZ;	/* #chars in value, max allowed */
1.2       albertel 10815: int	status = (-1);			/* status returned, 1=okay */
1.1       albertel 10816: int	tagnum = 0;			/* tag we're looking for */
1.2       albertel 10817: /*int	islock = 1;*/			/* true to lock file */
1.1       albertel 10818: /* --------------------------------------------------------------------------
                   10819: Open file
                   10820: -------------------------------------------------------------------------- */
                   10821: /* --- first check output arg --- */
                   10822: if ( value == (char *)NULL ) goto end_of_job; /* no output buffer supplied */
                   10823: *value = '\000';			/* init buffer with empty string */
                   10824: /* --- open filename or filename.tex --- */
                   10825: if ( filename != (char *)NULL )		/* make sure we got filename arg */
1.3     ! albertel 10826:   { strncpy(texfile,filename,1023);	/* local copy of filename */
        !          10827:     texfile[1023] = '\000';		/* make sure it's null terminated */
1.2       albertel 10828:     fp = rastopenfile(texfile,(islock?"r+":"r")); } /* try opening it */
1.1       albertel 10829: /* --- check that file opened --- */
                   10830: if ( fp == (FILE *)NULL )		/* failed to open file */
                   10831:   { sprintf(value,"{\\normalsize\\rm[file %s?]}",texfile);
                   10832:     goto end_of_job; }			/* return error message to caller */
1.2       albertel 10833: status = 0;				/* file opened successfully */
                   10834: if ( islock ) rewind(fp);		/* start at beginning of file */
1.1       albertel 10835: /* --------------------------------------------------------------------------
                   10836: construct <tag>'s
                   10837: -------------------------------------------------------------------------- */
                   10838: if ( tag != (char *)NULL )		/* caller passed tag arg */
                   10839:  if ( *tag != '\000' )			/* and it's not an empty string */
                   10840:   { strcpy(tag1,"<"); strcpy(tag2,"</"); /* begin with < and </ */
                   10841:     strcat(tag1,tag); strcat(tag2,tag);	/* followed by caller's tag */
                   10842:     strcat(tag1,">"); strcat(tag2,">");	/* ending both tags with > */
                   10843:     compress(tag1,' '); compress(tag2,' '); /* remove embedded blanks */
                   10844:     tagnum = 1; }			/* signal that we have tag */
                   10845: /* --------------------------------------------------------------------------
                   10846: Read file, concatnate lines
                   10847: -------------------------------------------------------------------------- */
1.3     ! albertel 10848: while ( fgets(text,MAXLINESZ-1,fp) != (char *)NULL ) { /*read input till eof*/
1.1       albertel 10849:   switch ( tagnum ) {			/* look for left- or right-tag */
1.2       albertel 10850:     case 0: status = 1; break;		/* no tag to look for */
1.1       albertel 10851:     case 1:				/* looking for opening left <tag> */
                   10852:       if ( (tagp=strstr(text,tag1)) == NULL ) break; /*haven't found it yet*/
                   10853:       strcpy(text,tagp+strlen(tag1));	/* shift out preceding text */
                   10854:       tagnum = 2;			/*now looking for closing right tag*/
                   10855:     case 2:				/* looking for closing right </tag> */
                   10856:       if ( (tagp=strstr(text,tag2)) == NULL ) break; /*haven't found it yet*/
                   10857:       *tagp = '\000';			/* terminate line at tag */
                   10858:       tagnum = 3;			/* done after this line */
1.2       albertel 10859:       status = 1;			/* successfully read tag */
1.1       albertel 10860:       break;
                   10861:     } /* ---end-of-switch(tagnum) --- */
                   10862:   if ( tagnum != 1 ) {			/* no tag or left tag already found*/
                   10863:     int	textlen = strlen(text);		/* #chars in current line */
                   10864:     if ( vallen+textlen > maxvallen ) break; /* quit before overflow */
                   10865:     strcat(value,text);			/* concat line to end of value */
                   10866:     vallen += textlen;			/* bump length */
                   10867:     if ( tagnum > 2 ) break; }		/* found right tag, so we're done */
                   10868:   } /* --- end-of-while(fgets()!=NULL) --- */
                   10869: if ( tagnum<1 || tagnum>2 ) status=1;	/* okay if no tag or we found tag */
                   10870: fclose ( fp );				/* close input file after reading */
                   10871: /* --- return value and status to caller --- */
                   10872: end_of_job:
                   10873:   return ( status );			/* return status to caller */
                   10874: } /* --- end-of-function rastreadfile() --- */
                   10875: 
                   10876: 
                   10877: /* ==========================================================================
                   10878:  * Function:	rastwritefile ( filename, tag, value, isstrict )
                   10879:  * Purpose:	Re/writes filename, replacing string between <tag>...</tag>
                   10880:  *		with value, or writing entire file as value if tag=NULL.
                   10881:  * --------------------------------------------------------------------------
                   10882:  * Arguments:	filename (I)	char * to null-terminated string containing
                   10883:  *				name of file to write (preceded by path
                   10884:  *				relative to mimetex executable)
                   10885:  *		tag (I)		char * to null-terminated string containing
                   10886:  *				html-like tagname.  File contents between
                   10887:  *				<tag> and </tag> will be replaced, or
                   10888:  *				entire file written if tag=NULL passed.
                   10889:  *		value (I)	char * containing string replacing value
                   10890:  *				between <tag>...</tag> or replacing entire
                   10891:  *				file if tag=NULL.
                   10892:  *		isstrict (I)	int containing 1 to only rewrite existing
                   10893:  *				files, or 0 to create new file if necessary.
                   10894:  * --------------------------------------------------------------------------
                   10895:  * Returns:	( int )		1=okay, 0=some error
                   10896:  * --------------------------------------------------------------------------
                   10897:  * Notes:     o
                   10898:  * ======================================================================= */
                   10899: /* --- entry point --- */
                   10900: int	rastwritefile( char *filename, char *tag, char *value, int isstrict )
                   10901: {
                   10902: /* -------------------------------------------------------------------------
                   10903: Allocations and Declarations
                   10904: -------------------------------------------------------------------------- */
                   10905: FILE	*fp = (FILE *)NULL, *rastopenfile(); /* pointer to opened filename */
1.3     ! albertel 10906: char	texfile[1024] = "\000",		/* local copy of input filename */
        !          10907: 	filebuff[MAXFILESZ+1] = "\000",	/* entire contents of file */
        !          10908: 	tag1[1024], tag2[1024],		/* left <tag> and right <tag/> */
1.1       albertel 10909: 	*strchange(),			/* put value between <tag>...</tag>*/
                   10910: 	*timestamp();			/* log modification time */
                   10911: int	istag=0, rastreadfile(),	/* read file if tag!=NULL */
                   10912: 	/*isstrict = (seclevel>5? 1:0),*/ /*true only writes existing files*/
                   10913: 	isnewfile = 0,			/* true if writing new file */
                   10914: 	status = 0;			/* status returned, 1=okay */
1.2       albertel 10915: int	istimestamp = 0;		/* true to update <timestamp> tag */
1.1       albertel 10916: /* --------------------------------------------------------------------------
                   10917: check args
                   10918: -------------------------------------------------------------------------- */
                   10919: /* --- check filename and value --- */
                   10920: if ( filename == (char *)NULL		/* quit if no filename arg supplied*/
                   10921: ||   value == (char *)NULL ) goto end_of_job; /* or no value arg supplied */
                   10922: if ( strlen(filename) < 2		/* quit if unreasonable filename */
                   10923: ||   *value == '\000' ) goto end_of_job; /* or empty value string supplied */
                   10924: /* --- establish filename[.tex] --- */
1.3     ! albertel 10925: strncpy(texfile,filename,1023);		/* local copy of input filename */
        !          10926: texfile[1023] = '\000';			/* make sure it's null terminated */
1.1       albertel 10927: if ( rastopenfile(texfile,NULL)		/* unchanged or .tex appended */
                   10928: ==   (FILE *)NULL )			/* can't open, so write new file */
                   10929:   { if ( isstrict ) goto end_of_job;	/* fail if new files not permitted */
                   10930:     isnewfile = 1; }			/* signal we're writing new file */
                   10931: /* --- check whether tag supplied by caller --- */
                   10932: if ( tag != (char *)NULL )		/* caller passed tag argument */
                   10933:  if ( *tag != '\000' )			/* and it's not an empty string */
                   10934:   { istag = 1;				/* so flip tag flag true */
                   10935:     strcpy(tag1,"<"); strcpy(tag2,"</");  /* begin tags with < and </ */
                   10936:     strcat(tag1,tag); strcat(tag2,tag);   /* followed by caller's tag */
                   10937:     strcat(tag1,">"); strcat(tag2,">");	/* ending both tags with > */
                   10938:     compress(tag1,' '); compress(tag2,' '); } /* remove embedded blanks */
                   10939: /* --------------------------------------------------------------------------
                   10940: read existing file if just rewriting a single tag
                   10941: -------------------------------------------------------------------------- */
                   10942: /* --- read original file if only replacing a tag within it --- */
                   10943: *filebuff = '\000';			/* init as empty file */
                   10944: if ( !isnewfile )			/* if file already exists */
                   10945:  if ( istag )				/* and just rewriting one tag */
1.2       albertel 10946:   if ( rastreadfile(texfile,1,NULL,filebuff) /* read entire existing file */
                   10947:   <=   0 ) goto end_of_job;		/* signal error if failed to read */
1.1       albertel 10948: /* --------------------------------------------------------------------------
                   10949: construct new file data if needed (entire file replaced by value if no tag)
                   10950: -------------------------------------------------------------------------- */
                   10951: if ( istag )				/* only replacing tag in file */
                   10952:  {
                   10953:  /* --- find <tag> and </tag> in file --- */
                   10954:  int	tlen1=strlen(tag1),  tlen2=strlen(tag2), flen;  /*tag,buff lengths*/
                   10955:  char	*tagp1 = (isnewfile? NULL:strstr(filebuff,tag1)), /* <tag> in file*/
                   10956: 	*tagp2 = (isnewfile? NULL:strstr(filebuff,tag2)); /*</tag> in file*/
                   10957:  /* --- if adding new <tag> just concatanate at end of file --- */
                   10958:  if ( tagp1 == (char *)NULL )		/* add new tag to file */
                   10959:   {
                   10960:   /* --- preprocess filebuff --- */
                   10961:   if ( tagp2 != (char *)NULL )		/* apparently have ...</tag> */
                   10962:     strcpy(filebuff,tagp2+tlen2);	/* so get rid of leading ...</tag> */
                   10963:   if ( (flen = strlen(filebuff))	/* #chars currently in buffer */
                   10964:   > 0 )					/* we have non-empty buffer */
                   10965:    if (!isthischar(*(filebuff+flen-1),"\n\r")) /*no newline at end of file*/
                   10966:      if(0)strcat(filebuff,"\n");	/* so add one before new tag */
                   10967:   /* --- add new tag --- */
                   10968:   strcat(filebuff,tag1);		/* add opening <tag> */
                   10969:   strcat(filebuff,value);		/* then value */
                   10970:   strcat(filebuff,tag2);		/* finally closing </tag> */
                   10971:   strcat(filebuff,"\n");		/* newline at end of file */
                   10972:   } /* --- end-of-if(tagp1==NULL) --- */
                   10973:  else					/* found existing opening <tag> */
                   10974:   {
                   10975:   if ( tagp2 == NULL )			/* apparently have <tag>... */
                   10976:     { *(tagp1+tlen1) = '\000';		/* so get rid of trailing ... */
                   10977:       strcat(filebuff,value);		/* then concatanate value */
                   10978:       strcat(filebuff,tag2); }		/* and finally closing </tag> */
                   10979:   else					/* else have <tag>...<tag/> */
                   10980:    if ( (flen=((int)(tagp2-tagp1))-tlen1) /* len of .'s in <tag>...</tag> */
                   10981:    >=   0 )				/* usually <tag> precedes </tag> */
                   10982:     strchange(flen,tagp1+tlen1,value);	/* change ...'s to value */
                   10983:    else					/* weirdly, </tag> precedes <tag> */
1.3     ! albertel 10984:     { char fbuff[4096];			/* field buff for <tag>value</tag> */
1.1       albertel 10985:       if ( (flen = ((int)(tagp1-tagp2))+tlen1) /* strlen(</tag>...<tag>) */
                   10986:       <=   0 ) goto end_of_job;		/* must be internal error */
                   10987:       strcpy(fbuff,tag1);		/* set opening <tag> */
                   10988:       strcat(fbuff,value);		/* then value */
                   10989:       strcat(fbuff,tag2);		/* finally closing </tag> */
                   10990:       strchange(flen,tagp2,fbuff); }	/* replace original </tag>...<tag> */
                   10991:   } /* --- end-of-if/else(tagp1==NULL) --- */
                   10992:  } /* --- end-of-if(istag) --- */
                   10993: /* --------------------------------------------------------------------------
                   10994: rewrite file and return to caller
                   10995: -------------------------------------------------------------------------- */
                   10996: /* --- first open file for write --- */
                   10997: if ( (fp=rastopenfile(texfile,"w"))	/* open for write */
                   10998: ==   (FILE *)NULL ) goto end_of_job;	/* signal error if can't open */
                   10999: /* --- rewrite and close file --- */
                   11000: if ( fputs((istag?filebuff:value),fp)	/* write filebuff or value */
                   11001: !=  EOF ) status = 1;			/* signal success if succeeded */
                   11002: fclose ( fp );				/* close output file after writing */
                   11003: /* --- modify timestamp --- */
1.2       albertel 11004: if ( status > 0 )			/*forget timestamp if write failed*/
                   11005:  if ( istimestamp )			/* if we're updating timestamp */
                   11006:   if ( istag )				/* only log time in tagged file */
                   11007:    if ( strstr(tag,"timestamp") == (char *)NULL ) /* but avoid recursion */
                   11008:     { char fbuff[2048];			/* field buff <timestamp> value */
                   11009:       strcpy(fbuff,tag);		/* tag modified */
                   11010:       strcat(fbuff," modified at ");	/* spacer */
                   11011:       strcat(fbuff,timestamp(TZDELTA,0)); /* start with timestamp */
                   11012:       status = rastwritefile(filename,"timestamp",fbuff,1); }
1.1       albertel 11013: /* --- return status to caller --- */
                   11014: end_of_job:
                   11015:   return ( status );			/* return status to caller */
                   11016: } /* --- end-of-function rastwritefile() --- */
                   11017: 
                   11018: 
                   11019: /* ==========================================================================
1.2       albertel 11020:  * Function:	calendar ( year, month, day )
                   11021:  * Purpose:	returns null-terminated character string containing
                   11022:  *		\begin{array}...\end{array} for the one-month calendar
                   11023:  *		specified by year=1973...2099 and month=1...12.
                   11024:  *		If either arg out-of-range, today's value is used.
                   11025:  * --------------------------------------------------------------------------
                   11026:  * Arguments:	year (I)	int containing 1973...2099 or 0 for current
                   11027:  *				year
                   11028:  *		month (I)	int containing 1...12 or 0 for current month
                   11029:  *		day (I)		int containing day to emphasize or 0
                   11030:  * --------------------------------------------------------------------------
                   11031:  * Returns:	( char * )	char ptr to null-terminated buffer
                   11032:  *				containing \begin{array}...\end{array}
                   11033:  *				string that will render calendar for
                   11034:  *				requested month, or NULL for any error.
                   11035:  * --------------------------------------------------------------------------
                   11036:  * Notes:     o
                   11037:  * ======================================================================= */
                   11038: /* --- entry point --- */
                   11039: char	*calendar( int year, int month, int day )
                   11040: {
                   11041: /* -------------------------------------------------------------------------
                   11042: Allocations and Declarations
                   11043: -------------------------------------------------------------------------- */
                   11044: static char calbuff[4096];		/* calendar returned to caller */
                   11045: time_t	time_val = (time_t)(0);		/* binary value returned by time() */
                   11046: struct tm *tmstruct=(struct tm *)NULL, *localtime(); /* interpret time_val */
                   11047: int	yy=0, mm=0, dd=0;		/* today (emphasize today's dd) */
                   11048: int	idd=1, iday=0, daynumber();	/* day-of-week for idd=1...31 */
                   11049: char	aval[64];			/* ascii day or 4-digit year */
                   11050: /* --- calendar data --- */
                   11051: static	char *monthnames[] = { "?", "January", "February", "March", "April",
                   11052: 	 "May", "June", "July", "August", "September", "October",
                   11053: 	"November", "December", "?" } ;
                   11054: static	int modays[] =
                   11055: 	{ 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0 };
                   11056: /* -------------------------------------------------------------------------
                   11057: initialization
                   11058: -------------------------------------------------------------------------- */
                   11059: /* --- get current date/time --- */
                   11060: time((time_t *)(&time_val));		/* get date and time */
                   11061: tmstruct = localtime((time_t *)(&time_val)); /* interpret time_val */
                   11062: yy  =  1900 + (int)(tmstruct->tm_year);	/* current four-digit year */
                   11063: mm  =  1 + (int)(tmstruct->tm_mon);	/* current month, 1-12 */
                   11064: dd  =  (int)(tmstruct->tm_mday);	/* current day, 1-31 */
                   11065: /* --- check args --- */
                   11066: if ( year<1973 || year>2099 ) year  = yy; /* current year if out-of-bounds */
                   11067: if ( month<1 || month>12 ) month = mm;	/* current month if out-of-bounds */
                   11068: if ( month==mm && year==yy && day==0 )	/* current month and default day */
                   11069:   day = dd;				/* emphasize current day */
                   11070: modays[2] = (year%4==0?29:28);		/* Feb has 29 days in leap years */
                   11071: /* --- initialize calendar string --- */
                   11072: strcpy(calbuff,"{\\begin{gather}");	/* center `month year` above cal */
                   11073: strcat(calbuff,"\\small\\text{");	/* month set in roman */
                   11074: strcat(calbuff,monthnames[month]);	/* insert month name */
                   11075: strcat(calbuff," }");			/* add a space */
                   11076: sprintf(aval,"%d",year);		/* convert year to ascii */
                   11077: strcat(calbuff,aval);			/* add year */
                   11078: strcat(calbuff,"\\\\");			/* end top row */
                   11079: strcat(calbuff,				/* now begin calendar arrayr */
                   11080: 	"\\begin{array}{|c|c|c|c|c|c|c|CCCCCC} \\hline"
                   11081: 	"\\tiny\\text{Sun} & \\tiny\\text{Mon} & \\tiny\\text{Tue} &"
                   11082: 	"\\tiny\\text{Wed} & \\tiny\\text{Thu} & \\tiny\\text{Fri} &"
                   11083: 	"\\tiny\\text{Sat} \\\\ \\hline " );
                   11084: /* -------------------------------------------------------------------------
                   11085: generate calendar
                   11086: -------------------------------------------------------------------------- */
                   11087: for ( idd=1; idd<=modays[month]; idd++ ) /* run through days of month */
                   11088:   {
                   11089:   /* --- get day-of-week for this day --- */
                   11090:   iday = 1 + (daynumber(year,month,idd)%7); /* 1=Monday...7=Sunday */
                   11091:   if ( iday == 7 ) iday = 0;		/* now 0=Sunday...6=Saturday */
                   11092:   /* --- may need empty cells at beginning of month --- */
                   11093:   if ( idd == 1 )			/* first day of month */
                   11094:    if ( iday > 0 )			/* need to skip cells */
                   11095:     { strcpy(aval,"\\ &\\ &\\ &\\ &\\ &\\ &\\ &\\ &\\ &\\"); /*cells to skip*/
                   11096:       aval[3*iday] = '\000';		/*skip cells preceding 1st of month*/
                   11097:       strcat(calbuff,aval); }		/* add skip string to buffer */
                   11098:   /* --- add idd to current cell --- */
                   11099:   sprintf(aval,"%d",idd);		/* convert idd to ascii */
                   11100:   if ( idd == day			/* emphasize today's date */
                   11101:   /*&&   month==mm && year==yy*/ )	/* only if this month's calendar */
                   11102:    { strcat(calbuff,"{\\fs{-1}\\left\\langle "); /*emphasize, 1 size smaller*/
                   11103:      strcat(calbuff,aval);		/* put in idd */
                   11104:      strcat(calbuff,"\\right\\rangle}"); } /* finish emphasis */
                   11105:   else					/* not today's date */
                   11106:     strcat(calbuff,aval);		/* so just put in idd */
                   11107:   /* --- terminate cell --- */
1.3     ! albertel 11108:   if ( idd < modays[month] ) {		/* not yet end-of-month */
1.2       albertel 11109:    if ( iday < 6 )			/* still have days left in week */
                   11110:     strcat(calbuff,"&");		/* new cell in same week */
                   11111:    else					/* reached end-of-week */
1.3     ! albertel 11112:     strcat(calbuff,"\\\\ \\hline"); }	/* so start new week */
1.2       albertel 11113:   } /* --- end-of-for(idd) --- */
                   11114: strcat(calbuff,"\\\\ \\hline");		/* final underline at end-of-month */
                   11115: /* --- return calendar to caller --- */
                   11116: strcat(calbuff,"\\end{array}\\end{gather}}"); /* terminate array */
                   11117: return ( calbuff );			/* back to caller with calendar */
                   11118: } /* --- end-of-function calendar() --- */
                   11119: 
                   11120: 
                   11121: /* ==========================================================================
                   11122:  * Function:	timestamp ( tzdelta, ifmt )
1.1       albertel 11123:  * Purpose:	returns null-terminated character string containing
                   11124:  *		current date:time stamp as ccyy-mm-dd:hh:mm:ss{am,pm}
                   11125:  * --------------------------------------------------------------------------
1.2       albertel 11126:  * Arguments:	tzdelta (I)	integer, positive or negative, containing
                   11127:  *				containing number of hours to be added or
                   11128:  *				subtracted from system time (to accommodate
                   11129:  *				your desired time zone).
                   11130:  *		ifmt (I)	integer containing 0 for default format
1.1       albertel 11131:  * --------------------------------------------------------------------------
                   11132:  * Returns:	( char * )	ptr to null-terminated buffer
                   11133:  *				containing current date:time stamp
                   11134:  * --------------------------------------------------------------------------
                   11135:  * Notes:     o
                   11136:  * ======================================================================= */
                   11137: /* --- entry point --- */
1.2       albertel 11138: char	*timestamp( int tzdelta, int ifmt )
1.1       albertel 11139: {
                   11140: /* -------------------------------------------------------------------------
                   11141: Allocations and Declarations
                   11142: -------------------------------------------------------------------------- */
1.2       albertel 11143: static	char timebuff[256];		/* date:time buffer back to caller */
1.1       albertel 11144: /*long	time_val = 0L;*/		/* binary value returned by time() */
                   11145: time_t	time_val = (time_t)(0);		/* binary value returned by time() */
                   11146: struct tm *tmstruct=(struct tm *)NULL, *localtime(); /* interpret time_val */
1.2       albertel 11147: int	year=0, hour=0,ispm=1,		/* adjust year, and set am/pm hour */
                   11148: 	month=0, day=0;			/* adjust day and month for delta  */
                   11149: int	tzadjust();			/* time zone adjustment function */
                   11150: int	daynumber();			/* #days since Jan 1, 1973 */
                   11151: static	char *daynames[] = { "Monday", "Tuesday", "Wednesday",
                   11152: 	 "Thursday", "Friday", "Saturday", "Sunday" } ;
                   11153: static	char *monthnames[] = { "?", "January", "February", "March", "April",
                   11154: 	 "May", "June", "July", "August", "September", "October",
                   11155: 	"November", "December", "?" } ;
1.1       albertel 11156: /* -------------------------------------------------------------------------
                   11157: get current date:time, adjust values, and and format stamp
                   11158: -------------------------------------------------------------------------- */
1.2       albertel 11159: /* --- first init returned timebuff in case of any error --- */
                   11160: *timebuff = '\000';
1.1       albertel 11161: /* --- get current date:time --- */
                   11162: time((time_t *)(&time_val));		/* get date and time */
                   11163: tmstruct = localtime((time_t *)(&time_val)); /* interpret time_val */
1.2       albertel 11164: /* --- extract fields --- */
                   11165: year  = (int)(tmstruct->tm_year);	/* local copy of year,  0=1900 */
                   11166: month = (int)(tmstruct->tm_mon) + 1;	/* local copy of month, 1-12 */
                   11167: day   = (int)(tmstruct->tm_mday);	/* local copy of day,   1-31 */
                   11168: hour  = (int)(tmstruct->tm_hour);	/* local copy of hour,  0-23 */
                   11169: /* --- adjust year --- */
1.1       albertel 11170: year += 1900;				/* set century in year */
1.2       albertel 11171: /* --- adjust for timezone --- */
                   11172: tzadjust(tzdelta,&year,&month,&day,&hour);
                   11173: /* --- check params --- */
                   11174: if ( hour<0  || hour>23
                   11175: ||   day<1   || day>31
                   11176: ||   month<1 || month>12
                   11177: ||   year<1973 ) goto end_of_job;
                   11178: /* --- adjust hour for am/pm --- */
                   11179: switch ( ifmt )
                   11180:   {
                   11181:   default:
                   11182:   case 0:
                   11183:     if ( hour < 12 )			/* am check */
                   11184:      { ispm=0;				/* reset pm flag */
                   11185:        if ( hour == 0 ) hour = 12; }	/* set 00hrs = 12am */
                   11186:     if ( hour > 12 ) hour -= 12;	/* pm check sets 13hrs to 1pm, etc */
                   11187:     break;
                   11188:   } /* --- end-of-switch(ifmt) --- */
1.1       albertel 11189: /* --- format date:time stamp --- */
1.2       albertel 11190: switch ( ifmt )
                   11191:   {
                   11192:   default:
                   11193:   case 0:  /* --- 2005-03-05:11:49:59am --- */
                   11194:     sprintf(timebuff,"%04d-%02d-%02d:%02d:%02d:%02d%s", year,month,day,
                   11195:     hour,(int)(tmstruct->tm_min),(int)(tmstruct->tm_sec),((ispm)?"pm":"am"));
                   11196:     break;
                   11197:   case 1:  /* --- Saturday, March 5, 2005 --- */
                   11198:     sprintf(timebuff,"%s, %s %d, %d",
                   11199:     daynames[daynumber(year,month,day)%7],monthnames[month],day,year);
                   11200:     break;
                   11201:   case 2: /* --- Saturday, March 5, 2005, 11:49:59am --- */
                   11202:     sprintf(timebuff,"%s, %s %d, %d, %d:%02d:%02d%s",
                   11203:     daynames[daynumber(year,month,day)%7],monthnames[month],day,year,
                   11204:     hour,(int)(tmstruct->tm_min),(int)(tmstruct->tm_sec),((ispm)?"pm":"am"));
                   11205:     break;
                   11206:   case 3: /* --- 11:49:59am --- */
                   11207:     sprintf(timebuff,"%d:%02d:%02d%s",
                   11208:     hour,(int)(tmstruct->tm_min),(int)(tmstruct->tm_sec),((ispm)?"pm":"am"));
                   11209:     break;
                   11210:   } /* --- end-of-switch(ifmt) --- */
                   11211: end_of_job:
                   11212:   return ( timebuff );			/* return stamp to caller */
1.1       albertel 11213: } /* --- end-of-function timestamp() --- */
                   11214: 
                   11215: 
                   11216: /* ==========================================================================
1.2       albertel 11217:  * Function:	tzadjust ( tzdelta, year, month, day, hour )
                   11218:  * Purpose:	Adjusts hour, and day,month,year if necessary,
                   11219:  *		by delta increment to accommodate your time zone.
                   11220:  * --------------------------------------------------------------------------
                   11221:  * Arguments:	tzdelta (I)	integer, positive or negative, containing
                   11222:  *				containing number of hours to be added or
                   11223:  *				subtracted from given time (to accommodate
                   11224:  *				your desired time zone).
                   11225:  *		year (I)	addr of int containing        4-digit year
                   11226:  *		month (I)	addr of int containing month  1=Jan - 12=Dec.
                   11227:  *		day (I)		addr of int containing day    1-31 for Jan.
                   11228:  *		hour (I)	addr of int containing hour   0-23
                   11229:  * Returns:	( int )		1 for success, or 0 for error
                   11230:  * --------------------------------------------------------------------------
                   11231:  * Notes:     o
                   11232:  * ======================================================================= */
                   11233: /* --- entry point --- */
                   11234: int	tzadjust ( int tzdelta, int *year, int *month, int *day, int *hour )
                   11235: {
                   11236: /* --------------------------------------------------------------------------
                   11237: Allocations and Declarations
                   11238: -------------------------------------------------------------------------- */
                   11239: int	yy = *year, mm = *month, dd = *day, hh = *hour; /*dereference args*/
                   11240: /* --- calendar data --- */
                   11241: static	int modays[] =
                   11242: 	{ 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0 };
                   11243: /* --------------------------------------------------------------------------
                   11244: check args
                   11245: -------------------------------------------------------------------------- */
                   11246: if ( mm<1 || mm>12 ) return(-1);	/* bad month */
                   11247: if ( dd<1 || dd>modays[mm] ) return(-1); /* bad day */
                   11248: if ( hh<0 || hh>23 ) return(-1);	/* bad hour */
                   11249: if ( tzdelta>23 || tzdelta<(-23) ) return(-1); /* bad tzdelta */
                   11250: /* --------------------------------------------------------------------------
                   11251: make adjustments
                   11252: -------------------------------------------------------------------------- */
                   11253: /* --- adjust hour --- */
                   11254: hh += tzdelta;				/* apply caller's delta */
                   11255: /* --- adjust for feb 29 --- */
                   11256: modays[2] = (yy%4==0?29:28);		/* Feb has 29 days in leap years */
                   11257: /* --- adjust day --- */
                   11258: if ( hh < 0 )				/* went to preceding day */
                   11259:   { dd--;  hh += 24; }
                   11260: if ( hh > 23 )				/* went to next day */
                   11261:   { dd++;  hh -= 24; }
                   11262: /* --- adjust month --- */
                   11263: if ( dd < 1 )				/* went to preceding month */
                   11264:   { mm--;  dd = modays[mm]; }
                   11265: if ( dd > modays[mm] )			/* went to next month */
                   11266:   { mm++;  dd = 1; }
                   11267: /* --- adjust year --- */
                   11268: if ( mm < 1 )				/* went to preceding year */
                   11269:   { yy--;  mm = 12;  dd = modays[mm]; }
                   11270: if ( mm > 12 )				/* went to next year */
                   11271:   { yy++;  mm = 1;   dd = 1; }
                   11272: /* --- back to caller --- */
                   11273: *year=yy; *month=mm; *day=dd; *hour=hh;	/* reset adjusted args */
                   11274: return ( 1 );
                   11275: } /* --- end-of-function tzadjust() --- */
                   11276: 
                   11277: 
                   11278: /* ==========================================================================
                   11279:  * Function:	daynumber ( year, month, day )
                   11280:  * Purpose:	Returns number of actual calendar days from Jan 1, 1973
                   11281:  *		to the given date (e.g., bvdaynumber(1974,1,1)=365).
                   11282:  * --------------------------------------------------------------------------
                   11283:  * Arguments:	year (I)	int containing year -- may be either 1995 or
                   11284:  *				95, or may be either 2010 or 110 for those
                   11285:  *				years.
                   11286:  *		month (I)	int containing month, 1=Jan thru 12=Dec.
                   11287:  *		day (I)		int containing day of month, 1-31 for Jan, etc.
                   11288:  * Returns:	( int )		Number of days from Jan 1, 1973 to given date,
                   11289:  *				or -1 for error (e.g., year<1973).
                   11290:  * --------------------------------------------------------------------------
                   11291:  * Notes:     o
                   11292:  * ======================================================================= */
                   11293: /* --- entry point --- */
                   11294: int	daynumber ( int year, int month, int day )
                   11295: {
                   11296: /* --------------------------------------------------------------------------
                   11297: Allocations and Declarations
                   11298: -------------------------------------------------------------------------- */
                   11299: /* --- returned value (note: returned as a default "int") --- */
                   11300: int	ndays;				/* #days since jan 1, year0 */
                   11301: /* --- initial conditions --- */
                   11302: static	int year0 = 73, 		/* jan 1 was a monday, 72 was a leap */
                   11303: 	days4yrs = 1461,		/* #days in 4 yrs = 365*4 + 1 */
                   11304: 	days1yr  = 365;
                   11305: /* --- table of accumulated days per month (last index not used) --- */
                   11306: static	int modays[] =
                   11307: 	{ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };
                   11308: /* --- variables for #days since day0 --- */
                   11309: int	nyears, nfouryrs;		/*#years, #4-yr periods since year0*/
                   11310: /* --------------------------------------------------------------------------
                   11311: Check input
                   11312: -------------------------------------------------------------------------- */
                   11313: if ( month < 1 || month > 12 )		/*month used as index, so must be ok*/
                   11314: 	return ( -1 );			/* otherwise, forget it */
                   11315: if ( year >= 1900 ) year -= 1900;	/*use two-digit years (3 after 2000)*/
                   11316: /* --------------------------------------------------------------------------
                   11317: Find #days since jan 1, 1973
                   11318: -------------------------------------------------------------------------- */
                   11319: /* --- figure #complete 4-year periods and #remaining yrs till current --- */
                   11320: nyears = year - year0;			/* #years since year0 */
                   11321: if ( nyears < 0 ) return ( -1 );	/* we're not working backwards */
                   11322: nfouryrs = nyears/4;			/* #complete four-year periods */
                   11323: nyears -= (4*nfouryrs); 		/* remainder excluding current year*/
                   11324: /* --- #days from jan 1, year0 till jan 1, this year --- */
                   11325: ndays = (days4yrs*nfouryrs)		/* #days in 4-yr periods */
                   11326:       +  (days1yr*nyears);		/* +remaining days */
                   11327: /*if ( year > 100 ) ndays--;*/		/* subtract leap year for 2000AD */
                   11328: /* --- add #days within current year --- */
                   11329: ndays += (modays[month-1] + (day-1));
                   11330: /* --- may need an extra day if current year is a leap year --- */
                   11331: if ( nyears == 3 )			/*three preceding yrs so this is 4th*/
                   11332:     { if ( month > 2 )			/* past feb so need an extra day */
                   11333: 	/*if ( year != 100 )*/		/* unless it's 2000AD */
                   11334: 	  ndays++; }			/* so add it in */
                   11335: return ( (int)(ndays) );		/* #days back to caller */
                   11336: } /* --- end-of-function daynumber() --- */
                   11337: 
                   11338: 
                   11339: /* ==========================================================================
                   11340:  * Function:	dbltoa ( dblval, npts )
1.1       albertel 11341:  * Purpose:	Converts double to ascii, in financial format
                   11342:  *		(e.g., comma-separated and negatives enclosed in ()'s).
                   11343:  * -------------------------------------------------------------------------
                   11344:  * Arguments:	dblval (I)	double containing value to be converted.
                   11345:  *		npts (I)	int containing #places after decimal point
                   11346:  *				to be displayed in returned string.
                   11347:  * Returns:	( char * )	null-terminated string containing
                   11348:  *				double converted to financial format.
                   11349:  * -------------------------------------------------------------------------
                   11350:  * Notes:     o
                   11351:  * ======================================================================= */
                   11352: /* --- entry point --- */
1.2       albertel 11353: char	*dbltoa ( double dblval, int npts )
                   11354: /* double dblval;
                   11355:    int	npts; */
1.1       albertel 11356: {
                   11357: /* -------------------------------------------------------------------------
                   11358: Allocations and Declarations
                   11359: ------------------------------------------------------------------------- */
1.3     ! albertel 11360: static	char finval[256];		/* buffer returned to caller */
1.1       albertel 11361: static	char digittbl[32]="0123456789*"; /* table of ascii decimal digits */
                   11362: char	*finptr = finval;		/* ptr to next char being converted*/
                   11363: double	floor();			/* integer which is glb(double) */
                   11364: double	dbldigit;			/* to shift out digits from dblval */
                   11365: int	digit;				/* one digit from dblval */
                   11366: int	isneg = 0;			/* reset true if dblval negative */
                   11367: int	ifrac = 0;			/* npts fractional digits of dblval*/
                   11368: char	digits[64]; int ndigits=0;	/* all the digits [0]=least signif */
                   11369: /* -------------------------------------------------------------------------
                   11370: Check sign
                   11371: ------------------------------------------------------------------------- */
                   11372: if ( dblval < 0.0 )			/* got a negative value to convert */
                   11373:     { isneg=1; dblval=(-dblval); }	/* set flag and make it positive */
                   11374: /* -------------------------------------------------------------------------
                   11375: Get fractional part of dblval if required
                   11376: ------------------------------------------------------------------------- */
                   11377: if ( npts > 0 )
                   11378:     { int ipts = npts;			/* loop index */
                   11379:       dbldigit = dblval-floor(dblval);	/* fractional part as double */
                   11380:       digit = 1;			/* check if rounded frac > 1 */
                   11381:       while ( --ipts >= 0 )		/* count down */
                   11382: 	{ dbldigit *= 10.0;		/* shift left one digit at a time */
                   11383: 	  digit *= 10; }		/* and keep max up-to-date */
                   11384:       ifrac = (int)(dbldigit + 0.5);	/* store fractional part as integer*/
                   11385:       if ( ifrac >= digit )		/* round to next whole number */
                   11386: 	{ dblval++; ifrac=0; }		/* bump val, reset frac to zero */
                   11387:     } /* --- end-of-if(npts>0) --- */
                   11388: else dblval += 0.5;			/* no frac, round to nearest whole */
                   11389: /* -------------------------------------------------------------------------
                   11390: Get whole digits
                   11391: ------------------------------------------------------------------------- */
                   11392: dblval = floor(dblval);			/* get rid of fractional part */
                   11393: while ( dblval > 0.0 )			/* still have data digits remaining*/
                   11394:     { dbldigit = floor(dblval/10.0);	/* shift out next digit */
                   11395:       digit = (int)(dblval - 10.0*dbldigit + 0.01); /* least signif digit */
                   11396:       if ( digit<0 || digit>9 ) digit=10; /* index check */
                   11397:       digits[ndigits++] = digittbl[digit]; /* store ascii digit */
                   11398:       dblval = dbldigit; }		/* ready for next digit */
                   11399: if ( ndigits < 1 ) digits[ndigits++] = '0'; /* store a single '0' for 0.0 */
                   11400: /* -------------------------------------------------------------------------
                   11401: Format whole part from digits[] array
                   11402: ------------------------------------------------------------------------- */
                   11403: if ( isneg ) *finptr++ = '(';		/* leading paren for negative value*/
                   11404: for ( digit=ndigits-1; digit>=0; digit-- ) /* start with most significant */
                   11405:     { *finptr++ = digits[digit];	/* store digit */
                   11406:       if ( digit>0 && digit%3==0 )	/* need a comma */
                   11407: 	*finptr++ = ','; }		/* put in separating comma */
                   11408: /* -------------------------------------------------------------------------
                   11409: Format fractional part using ifrac
                   11410: ------------------------------------------------------------------------- */
                   11411: if ( npts > 0 )
                   11412:     { *finptr++ = '.';			/* start with decimal point */
                   11413:       sprintf(finptr,"%0*d",npts,ifrac); /* convert to string */
                   11414:       finptr += npts; }			/* bump ptr past fractional digits */
                   11415: /* -------------------------------------------------------------------------
                   11416: End-of-Job
                   11417: ------------------------------------------------------------------------- */
                   11418: if ( isneg ) *finptr++ = ')';		/*trailing paren for negative value*/
                   11419: *finptr = '\000';			/* null-terminate converted double */
                   11420: return ( finval );			/* converted double back to caller */
1.2       albertel 11421: } /* --- end-of-function dbltoa() --- */
1.1       albertel 11422: 
                   11423: 
                   11424: /* ==========================================================================
                   11425:  * Function:	aalowpass ( rp, bytemap, grayscale )
                   11426:  * Purpose:	calculates a lowpass anti-aliased bytemap
                   11427:  *		for rp->bitmap, with each byte 0...grayscale-1
                   11428:  * --------------------------------------------------------------------------
                   11429:  * Arguments:	rp (I)		raster *  to raster whose bitmap
                   11430:  *				is to be anti-aliased
                   11431:  *		bytemap (O)	intbyte * to bytemap, calculated
                   11432:  *				by applying lowpass filter to rp->bitmap,
                   11433:  *				and returned (as you'd expect) in 1-to-1
                   11434:  *				addressing correspondence with rp->bitmap
                   11435:  *		grayscale (I)	int containing number of grayscales
                   11436:  *				to be calculated, 0...grayscale-1
                   11437:  *				(should typically be given as 256)
                   11438:  * --------------------------------------------------------------------------
                   11439:  * Returns:	( int )		1=success, 0=any error
                   11440:  * --------------------------------------------------------------------------
                   11441:  * Notes:     o	If the center point of the box being averaged is black,
                   11442:  *		then the entire "average" is forced black (grayscale-1)
                   11443:  *		regardless of the surrounding points.  This is my attempt
                   11444:  *		to avoid a "washed-out" appearance of thin (one-pixel-wide)
                   11445:  *		lines, which would otherwise turn from black to a gray shade.
                   11446:  *	     o	Also, while the weights for neighbor points are fixed,
                   11447:  *		you may adjust the center point weight on the compile line.
                   11448:  *		A higher weight sharpens the resulting anti-aliased image;
                   11449:  *		lower weights blur it out more (but keep the "center" black
                   11450:  *		as per the preceding note).
                   11451:  * ======================================================================= */
                   11452: /* --- entry point --- */
                   11453: int	aalowpass (raster *rp, intbyte *bytemap, int grayscale)
                   11454: {
                   11455: /* -------------------------------------------------------------------------
                   11456: Allocations and Declarations
                   11457: -------------------------------------------------------------------------- */
                   11458: int	status = 1;			/* 1=success, 0=failure to caller */
                   11459: pixbyte	*bitmap= (rp==NULL?NULL:rp->pixmap); /*local rp->pixmap ptr*/
                   11460: int	irow=0, icol=0;			/* rp->height, rp->width indexes */
                   11461: int	weights[9] = { 1,3,1, 3,0,3, 1,3,1 }; /* matrix of weights */
                   11462: int	adjindex[9]= { 0,1,2, 7,-1,3, 6,5,4 }; /*clockwise from upper-left*/
                   11463: int	totwts = 0;			/* sum of all weights in matrix */
                   11464: int	isforceavg = 1,			/*force avg black if center black?*/
                   11465: 	isminmaxwts = 1,		/*use wts or #pts for min/max test */
                   11466: 	blackscale = 0; /*(grayscale+1)/4;*/ /*force black if wgted avg>bs */
                   11467: /* -------------------------------------------------------------------------
                   11468: Initialization
                   11469: -------------------------------------------------------------------------- */
                   11470: /* --- calculate total weights --- */
                   11471: weights[4]= centerwt;			/* weight for center point */
                   11472: weights[1]= weights[3]= weights[5]= weights[7]= adjacentwt; /*adjacent pts*/
                   11473: totwts = centerwt + 4*(1+adjacentwt);	/* tot is center plus neighbors */
                   11474: /* -------------------------------------------------------------------------
                   11475: Calculate bytemap as 9-point weighted average over bitmap
                   11476: -------------------------------------------------------------------------- */
                   11477: for ( irow=0; irow<rp->height; irow++ )
                   11478:  for ( icol=0; icol<rp->width; icol++ )
                   11479:   {
                   11480:   int	ipixel = icol + irow*(rp->width); /* center pixel index */
                   11481:   int	jrow=0, jcol=0,			/* box around ipixel */
                   11482: 	bitval = 0,			/* value of bit/pixel at jrow,jcol */
                   11483: 	iscenter = 0,			/* set true if center pixel black */
                   11484: 	nadjacent=0, wadjacent=0,	/* #adjacent black pixels, their wts*/
                   11485: 	ngaps = 0,			/* #gaps in 8 pixels around center */
                   11486: 	iwt=(-1), sumwts=0;		/* weights index, sum in-bound wts */
                   11487:   char	adjmatrix[8];			/* adjacency "matrix" */
                   11488:   memset(adjmatrix,0,8);		/* zero out adjacency matrix */
                   11489:   bytemap[ipixel] = 0;			/* init pixel white */
                   11490:   /*--- for ipixel at irow,icol, get weighted average of adjacent pixels ---*/
                   11491:   for ( jrow=irow-1; jrow<=irow+1; jrow++ )  /* jrow = irow-1...irow+1 */
                   11492:    for ( jcol=icol-1; jcol<=icol+1; jcol++ ) /* jcol = icol-1...icol+1 */
                   11493:     {
                   11494:     int	jpixel = jcol + jrow*(rp->width); /* averaging index */
                   11495:     iwt++;				/*always bump weight index*/
                   11496:     if ( jrow<0 || jrow>=rp->height	/* if row out pf bounds */
                   11497:     ||   jcol<0 || jcol>=rp->width )	/* or col out of bounds */
                   11498: 	continue;			/* ignore this point */
                   11499:     bitval = (int)getlongbit(bitmap,jpixel); /* value of bit at jrow,jcol */
                   11500:     if ( bitval )			/* this is a black pixel */
                   11501:       {	if ( jrow==irow && jcol==icol )	/* and this is center point */
                   11502: 	  iscenter = 1;			/* set flag for center point black */
                   11503: 	else				/* adjacent point black */
                   11504: 	  { nadjacent++;		/* bump adjacent black count */
                   11505: 	    adjmatrix[adjindex[iwt]] = 1; } /*set "bit" in adjacency matrix*/
                   11506: 	wadjacent += weights[iwt]; }	/* sum weights for black pixels */
                   11507:     sumwts += weights[iwt];		/* and sum weights for all pixels */
                   11508:     } /* --- end-of-for(jrow,jcol) --- */
                   11509:   /* --- count gaps --- */
                   11510:   ngaps = (adjmatrix[7]!=adjmatrix[0]?1:0); /* init count */
                   11511:   for ( iwt=0; iwt<7; iwt++ )		/* clockwise around adjacency */
                   11512:     if ( adjmatrix[iwt] != adjmatrix[iwt+1] ) ngaps++; /* black/white flip */
                   11513:   ngaps /= 2;				/*each gap has 2 black/white flips*/
                   11514:   /* --- anti-alias pixel, but leave it black if it was already black --- */
                   11515:   if ( isforceavg && iscenter )		/* force avg if center point black */
                   11516:       bytemap[ipixel] = grayscale-1;	/* so force grayscale-1=black */
                   11517:   else					/* center point not black */
                   11518:    if ( ngaps <= 2 )			/*don't darken checkerboarded pixel*/
                   11519:     { bytemap[ipixel] =			/* 0=white ... grayscale-1=black */
                   11520: 	((totwts/2 - 1) + (grayscale-1)*wadjacent)/totwts; /* not /sumwts; */
                   11521:       if ( blackscale > 0		/* blackscale kludge turned on */
                   11522:       &&   bytemap[ipixel] > blackscale ) /* weighted avg > blackscale */
                   11523: 	bytemap[ipixel] = grayscale-1; } /* so force it entirely black */
                   11524:   /*--- only anti-alias pixels whose adjacent pixels fall within bounds ---*/
1.3     ! albertel 11525:   if ( !iscenter ) {			/* apply min/maxadjacent test */
1.1       albertel 11526:    if ( isminmaxwts )			/* min/max refer to adjacent weights*/
                   11527:     { if ( wadjacent < minadjacent	/* wts of adjacent points too low */
                   11528:       ||   wadjacent > maxadjacent )	/* or too high */
                   11529: 	bytemap[ipixel] = 0; }		/* so leave point white */
                   11530:    else					/* min/max refer to #adjacent points*/
                   11531:     { if ( nadjacent < minadjacent	/* too few adjacent points black */
                   11532:       ||   nadjacent > maxadjacent )	/* or too many */
1.3     ! albertel 11533: 	bytemap[ipixel] = 0; } }	/* so leave point white */
1.1       albertel 11534:   } /* --- end-of-for(irow,icol) --- */
                   11535: /* -------------------------------------------------------------------------
                   11536: Back to caller with gray-scale anti-aliased bytemap
                   11537: -------------------------------------------------------------------------- */
                   11538: /*end_of_job:*/
                   11539:   return ( status );
                   11540: } /* --- end-of-function aalowpass() --- */
                   11541: 
                   11542: 
                   11543: /* ==========================================================================
                   11544:  * Function:	aapnm ( rp, bytemap, grayscale )
                   11545:  * Purpose:	calculates a lowpass anti-aliased bytemap
                   11546:  *		for rp->bitmap, with each byte 0...grayscale-1,
                   11547:  *		based on the pnmalias.c algorithm
                   11548:  * --------------------------------------------------------------------------
                   11549:  * Arguments:	rp (I)		raster *  to raster whose bitmap
                   11550:  *				is to be anti-aliased
                   11551:  *		bytemap (O)	intbyte * to bytemap, calculated
                   11552:  *				by applying pnm-based filter to rp->bitmap,
                   11553:  *				and returned (as you'd expect) in 1-to-1
                   11554:  *				addressing correspondence with rp->bitmap
                   11555:  *		grayscale (I)	int containing number of grayscales
                   11556:  *				to be calculated, 0...grayscale-1
                   11557:  *				(should typically be given as 256)
                   11558:  * --------------------------------------------------------------------------
                   11559:  * Returns:	( int )		1=success, 0=any error
                   11560:  * --------------------------------------------------------------------------
                   11561:  * Notes:    o	Based on the pnmalias.c algorithm in the netpbm package
                   11562:  *		on sourceforge.
                   11563:  * ======================================================================= */
                   11564: /* --- entry point --- */
                   11565: int	aapnm (raster *rp, intbyte *bytemap, int grayscale)
                   11566: {
                   11567: /* -------------------------------------------------------------------------
                   11568: Allocations and Declarations
                   11569: -------------------------------------------------------------------------- */
                   11570: pixbyte	*bitmap = rp->pixmap;		/* local rp->pixmap ptr */
                   11571: int	width=rp->width, height=rp->height, /* width, height of raster */
                   11572: 	icol = 0,        irow = 0,	/* width, height indexes */
                   11573: 	imap = (-1);			/* pixel index = icol + irow*width */
                   11574: int	bgbitval=0, fgbitval=1;		/* background, foreground bitval */
1.3     ! albertel 11575: int	isfirstaa = 1;			/*debugging switch signals 1st pixel*/
1.1       albertel 11576: #if 0
                   11577: int	totwts=12, wts[9]={1,1,1, 1,4,1, 1,1,1}; /* pnmalias default wts */
                   11578: int	totwts=16, wts[9]={1,2,1, 2,4,2, 1,2,1}; /* weights */
                   11579: #endif
                   11580: int	totwts=18, wts[9]={1,2,1, 2,6,2, 1,2,1}; /* pnmalias default wts */
                   11581: int	isresetparams = 1,		/* true to set antialiasing params */
                   11582: 	isfgalias  = 1,			/* true to antialias fg bits */
                   11583: 	isfgonly   = 0,			/* true to only antialias fg bits */
                   11584: 	isbgalias  = 0,			/* true to antialias bg bits */
                   11585: 	isbgonly   = 0;			/* true to only antialias bg bits */
                   11586: /* -------------------------------------------------------------------------
                   11587: Initialization
                   11588: -------------------------------------------------------------------------- */
                   11589: /* --- check for bold light --- */
                   11590: if ( 0 )
                   11591:  { if ( weightnum > 2 ) { isbgalias=1; }	/* simulate bold */
                   11592:    if ( weightnum < 1 ) { isbgonly=1; isfgalias=0; } } /* simulate light */
                   11593: /* --- reset wts[], etc, and calculate total weights --- */
                   11594: if ( isresetparams )			/* wts[], etc taken from params */
                   11595:   { int	iwt=0;				/* wts[iwt] index */
                   11596:     wts[4]= centerwt;			/* weight for center point */
                   11597:     wts[1]=wts[3]=wts[5]=wts[7] = adjacentwt; /* and adjacent points */
                   11598:     wts[0]=wts[2]=wts[6]=wts[8] = cornerwt;   /* and corner points */
                   11599:     for ( totwts=0,iwt=0; iwt<9; iwt++ ) totwts += wts[iwt]; /* sum wts */
                   11600:     isfgalias = fgalias;		/* set isfgalias */
                   11601:     isfgonly = fgonly;			/* set isfgonly */
                   11602:     isbgalias = bgalias;		/* set isbgalias */
                   11603:     isbgonly = bgonly; }		/* set isbgonly */
                   11604: /* -------------------------------------------------------------------------
                   11605: Calculate bytemap as 9-point weighted average over bitmap
                   11606: -------------------------------------------------------------------------- */
                   11607: for ( irow=0; irow<height; irow++ )
                   11608:  for ( icol=0; icol<width; icol++ )
                   11609:   {
                   11610:   /* --- local allocations and declarations --- */
                   11611:   int	bitval=0,			/* value of rp bit at irow,icol */
                   11612: 	nnbitval=0, nebitval=0, eebitval=0, sebitval=0,	/*adjacent vals*/
                   11613: 	ssbitval=0, swbitval=0, wwbitval=0, nwbitval=0;	/*compass pt names*/
                   11614:   int	isbgedge=0, isfgedge=0;		/*does pixel border a bg or fg edge*/
                   11615:   int	aabyteval=0;			/* antialiased (or unchanged) value*/
                   11616:   /* --- bump imap index and get center bit value --- */
                   11617:   imap++;				/* imap = icol + irow*width */
                   11618:   bitval = getlongbit(bitmap,imap);	/* value of rp input bit at imap */
                   11619:   aabyteval = (intbyte)(bitval==bgbitval?0:grayscale-1); /* default aa val */
                   11620:   bytemap[imap] = (intbyte)(aabyteval);	/* init antialiased pixel */
                   11621:   /* --- check if we're antialiasing this pixel --- */
                   11622:   if ( (isbgonly && bitval==fgbitval)	/* only antialias background bit */
                   11623:   ||   (isfgonly && bitval==bgbitval) )	/* only antialias foreground bit */
                   11624:     continue;				/* leave default and do next bit */
                   11625:   /* --- get surrounding bits --- */
                   11626:   if ( irow > 0 )			/* nn (north) bit available */
                   11627:      nnbitval = getlongbit(bitmap,imap-width); /* nn bit value */
                   11628:   if ( irow < height-1 )		/* ss (south) bit available */
                   11629:      ssbitval = getlongbit(bitmap,imap+width); /* ss bit value */
                   11630:   if ( icol > 0 )			/* ww (west) bit available */
                   11631:    { wwbitval = getlongbit(bitmap,imap-1); /* ww bit value */
                   11632:      if ( irow > 0 )			/* nw bit available */
                   11633:        nwbitval = getlongbit(bitmap,imap-width-1); /* nw bit value */
                   11634:      if ( irow < height-1 )		/* sw bit available */
                   11635:        swbitval = getlongbit(bitmap,imap+width-1); } /* sw bit value */
                   11636:   if ( icol < width-1 )			/* ee (east) bit available */
                   11637:    { eebitval = getlongbit(bitmap,imap+1); /* ee bit value */
                   11638:      if ( irow > 0 )			/* ne bit available */
                   11639:        nebitval = getlongbit(bitmap,imap-width+1); /* ne bit value */
                   11640:      if ( irow < height-1 )		/* se bit available */
                   11641:        sebitval = getlongbit(bitmap,imap+width+1); } /* se bit value */
                   11642:   /* --- check for edges --- */
                   11643:   isbgedge =				/* current pixel borders a bg edge */
                   11644: 	(nnbitval==bgbitval && eebitval==bgbitval) ||	/*upper-right edge*/
                   11645: 	(eebitval==bgbitval && ssbitval==bgbitval) ||	/*lower-right edge*/
                   11646: 	(ssbitval==bgbitval && wwbitval==bgbitval) ||	/*lower-left  edge*/
                   11647: 	(wwbitval==bgbitval && nnbitval==bgbitval) ;	/*upper-left  edge*/
                   11648:   isfgedge =				/* current pixel borders an fg edge*/
                   11649: 	(nnbitval==fgbitval && eebitval==fgbitval) ||	/*upper-right edge*/
                   11650: 	(eebitval==fgbitval && ssbitval==fgbitval) ||	/*lower-right edge*/
                   11651: 	(ssbitval==fgbitval && wwbitval==fgbitval) ||	/*lower-left  edge*/
                   11652: 	(wwbitval==fgbitval && nnbitval==fgbitval) ;	/*upper-left  edge*/
1.2       albertel 11653:   /* ---check top/bot left/right edges for corners (added by j.forkosh)--- */
                   11654:   if ( 1 ) {				/* true to perform test */
                   11655:     int	isbghorz=0, isfghorz=0, isbgvert=0, isfgvert=0; /* horz/vert edges */
                   11656:     isbghorz =				/* top or bottom edge is all bg */
                   11657: 	(nwbitval+nnbitval+nebitval == 3*bgbitval) ||	/* top edge bg */
                   11658: 	(swbitval+ssbitval+sebitval == 3*bgbitval) ;	/* bottom edge bg */
                   11659:     isfghorz =				/* top or bottom edge is all fg */
                   11660: 	(nwbitval+nnbitval+nebitval == 3*fgbitval) ||	/* top edge fg */
                   11661: 	(swbitval+ssbitval+sebitval == 3*fgbitval) ;	/* bottom edge fg */
                   11662:     isbgvert =				/* left or right edge is all bg */
                   11663: 	(nwbitval+wwbitval+swbitval == 3*bgbitval) ||	/* left edge bg */
                   11664: 	(nebitval+eebitval+sebitval == 3*bgbitval) ;	/* right edge bg */
                   11665:     isfgvert =				/* left or right edge is all bg */
                   11666: 	(nwbitval+wwbitval+swbitval == 3*fgbitval) ||	/* left edge fg */
                   11667: 	(nebitval+eebitval+sebitval == 3*fgbitval) ;	/* right edge fg */
                   11668:     if ( (isbghorz && isbgvert && (bitval==fgbitval))	/* we're at an...*/
                   11669:     ||   (isfghorz && isfgvert && (bitval==bgbitval)) )	/*...inside corner */
                   11670: 	continue;					/* don't antialias */
                   11671:     } /* --- end-of-if(1) --- */
                   11672:   /* --- check #gaps for checkerboard (added by j.forkosh) --- */
                   11673:   if ( 0 ) {				/* true to perform test */
                   11674:     int	ngaps=0, mingaps=1,maxgaps=2;	/* count #fg/bg flips (max=4 noop) */
                   11675:     if ( nwbitval!=nnbitval ) ngaps++;	/* upper-left =? upper */
                   11676:     if ( nnbitval!=nebitval ) ngaps++;	/* upper =? upper-right */
                   11677:     if ( nebitval!=eebitval ) ngaps++;	/* upper-right =? right */
                   11678:     if ( eebitval!=sebitval ) ngaps++;	/* right =? lower-right */
                   11679:     if ( sebitval!=ssbitval ) ngaps++;	/* lower-right =? lower */
                   11680:     if ( ssbitval!=swbitval ) ngaps++;	/* lower =? lower-left */
                   11681:     if ( swbitval!=wwbitval ) ngaps++;	/* lower-left =? left */
                   11682:     if ( wwbitval!=nwbitval ) ngaps++;	/* left =? upper-left */
                   11683:     if ( ngaps > 0 ) ngaps /= 2;	/* each gap has 2 bg/fg flips */
                   11684:     if ( ngaps<mingaps || ngaps>maxgaps ) continue;
                   11685:     } /* --- end-of-if(1) --- */
1.1       albertel 11686:   /* --- antialias if necessary --- */
                   11687:   if ( (isbgalias && isbgedge)		/* alias pixel surrounding bg */
                   11688:   ||   (isfgalias && isfgedge)		/* alias pixel surrounding fg */
                   11689:   ||   (isbgedge  && isfgedge) )	/* neighboring fg and bg pixel */
                   11690:     {
                   11691:     int	aasumval =			/* sum wts[]*bitmap[] */
                   11692: 	wts[0]*nwbitval + wts[1]*nnbitval + wts[2]*nebitval +
                   11693: 	wts[3]*wwbitval +  wts[4]*bitval  + wts[5]*eebitval +
                   11694: 	wts[6]*swbitval + wts[7]*ssbitval + wts[8]*sebitval ;
                   11695:     double aawtval = ((double)aasumval)/((double)totwts); /* weighted val */
                   11696:     aabyteval= (int)(((double)(grayscale-1))*aawtval+0.5); /*0...grayscale-1*/
                   11697:     bytemap[imap] = (intbyte)(aabyteval); /* set antialiased pixel */
1.3     ! albertel 11698:     if ( msglevel>=99 && msgfp!=NULL ) { fprintf(msgfp, /*diagnostic output*/
        !          11699:       "%s> irow,icol,imap=%d,%d,%d aawtval=%.4f aabyteval=%d\n",
        !          11700:       (isfirstaa?"aapnm algorithm":"aapnm"),
1.1       albertel 11701:       irow,icol,imap, aawtval,aabyteval);
1.3     ! albertel 11702:       isfirstaa = 0; }
1.1       albertel 11703:     } /* --- end-of-if(isedge) --- */
                   11704:   } /* --- end-of-for(irow,icol) --- */
                   11705: /* -------------------------------------------------------------------------
                   11706: Back to caller with gray-scale anti-aliased bytemap
                   11707: -------------------------------------------------------------------------- */
                   11708: /*end_of_job:*/
                   11709:   return ( 1 );
                   11710: } /* --- end-of-function aapnm() --- */
                   11711: 
                   11712: 
                   11713: /* ==========================================================================
1.3     ! albertel 11714:  * Function:	aapnmlookup ( rp, bytemap, grayscale )
        !          11715:  * Purpose:	calculates a lowpass anti-aliased bytemap
        !          11716:  *		for rp->bitmap, with each byte 0...grayscale-1,
        !          11717:  *		based on the pnmalias.c algorithm.
        !          11718:  *		This version uses aagridnum() and aapatternnum() lookups
        !          11719:  *		to interpret 3x3 lowpass pixel grids.
        !          11720:  * --------------------------------------------------------------------------
        !          11721:  * Arguments:	rp (I)		raster *  to raster whose bitmap
        !          11722:  *				is to be anti-aliased
        !          11723:  *		bytemap (O)	intbyte * to bytemap, calculated
        !          11724:  *				by applying pnm-based filter to rp->bitmap,
        !          11725:  *				and returned (as you'd expect) in 1-to-1
        !          11726:  *				addressing correspondence with rp->bitmap
        !          11727:  *		grayscale (I)	int containing number of grayscales
        !          11728:  *				to be calculated, 0...grayscale-1
        !          11729:  *				(should typically be given as 256)
        !          11730:  * --------------------------------------------------------------------------
        !          11731:  * Returns:	( int )		1=success, 0=any error
        !          11732:  * --------------------------------------------------------------------------
        !          11733:  * Notes:    o	Based on the pnmalias.c algorithm in the netpbm package
        !          11734:  *		on sourceforge.
        !          11735:  *	     o	This version uses aagridnum() and aapatternnum() lookups
        !          11736:  *		to interpret 3x3 lowpass pixel grids.
        !          11737:  * ======================================================================= */
        !          11738: /* --- entry point --- */
        !          11739: int	aapnmlookup (raster *rp, intbyte *bytemap, int grayscale)
        !          11740: {
        !          11741: /* -------------------------------------------------------------------------
        !          11742: Allocations and Declarations
        !          11743: -------------------------------------------------------------------------- */
        !          11744: int	width=rp->width, height=rp->height, /* width, height of raster */
        !          11745: 	icol = 0,        irow = 0,	/* width, height indexes */
        !          11746: 	imap = (-1);			/* pixel index = icol + irow*width */
        !          11747: int	bgbitval=0, fgbitval=1;		/* background, foreground bitval */
        !          11748: int	isfirstaa = 1;			/*debugging switch signals 1st pixel*/
        !          11749: int	aacenterwt=centerwt, aaadjacentwt=adjacentwt, aacornerwt=cornerwt,
        !          11750: 	totwts = centerwt + 4*(adjacentwt+cornerwt); /*pnmalias default wts*/
        !          11751: int	isfgalias  = fgalias,		/*(1) true to antialias fg bits */
        !          11752: 	isfgonly   = fgonly,		/*(0) true to only antialias fg bits*/
        !          11753: 	isbgalias  = bgalias,		/*(0) true to antialias bg bits */
        !          11754: 	isbgonly   = bgonly;		/*(0) true to only antialias bg bits*/
        !          11755: int	gridnum=(-1), aagridnum(),	/* grid# for 3x3 grid at irow,icol */
        !          11756: 	patternum=(-1), aapatternnum();	/*pattern#, 1-51, for input gridnum*/
        !          11757: int	aapatterns();			/* to antialias special patterns */
        !          11758: /* ---
        !          11759:  * pattern number data
        !          11760:  * ------------------- */
        !          11761: /* --- number of adjacent fg pixels set in pattern --- */
        !          11762: static	int nadjacents[] = { -1,	/* #adjacent fg pixels for pattern */
        !          11763:    0,  4,  0,  1,  4,  3,  1,  0,  1,  0,	/*  1-10 */
        !          11764:    2,  2,  3,  4,  3,  4,  2,  2,  1,  2,	/* 11-20 */
        !          11765:    1,  2,  1,  2,  0,  1,  3,  2,  3,  2,	/* 21-30 */
        !          11766:    3,  2,  3,  2,  4,  3,  1,  2,  2,  2,	/* 31-40 */
        !          11767:    2,  1,  2,  2,  3,  0,  3,  2,  2,  1,  4,	/* 41-51 */
        !          11768:    -1 } ; /* --- end-of-nadjacents[] --- */
        !          11769: /* --- number of corner fg pixels set in pattern --- */
        !          11770: static	int ncorners[] = { -1,		/* #corner fg pixels for pattern */
        !          11771:    0,  4,  1,  0,  3,  4,  1,  2,  1,  2,	/*  1-10 */
        !          11772:    0,  0,  3,  2,  3,  2,  4,  4,  2,  1,	/* 11-20 */
        !          11773:    2,  1,  2,  1,  3,  2,  0,  1,  2,  3,	/* 21-30 */
        !          11774:    2,  3,  2,  3,  1,  2,  4,  3,  2,  2,	/* 31-40 */
        !          11775:    2,  3,  2,  2,  1,  4,  1,  2,  2,  3,  0,	/* 41-51 */
        !          11776:    -1 } ; /* --- end-of-ncorners[] --- */
        !          11777: /* --- 0,1,2=pattern contains horizontal bg,fg,both edge; -1=no edge --- */
        !          11778: static	int horzedges[] = { -1,		/* 0,1,2 = horz bg,fg,both edge */
        !          11779:    0,  1,  0,  0,  1,  1,  0,  0,  0, -1,	/*  1-10 */
        !          11780:    0, -1,  1,  1,  1, -1,  1, -1,  2,  0,	/* 11-20 */
        !          11781:   -1, -1, -1,  0, -1, -1, -1, -1,  2,  1,	/* 21-30 */
        !          11782:   -1, -1, -1,  1, -1, -1, -1, -1,  2, -1,	/* 31-40 */
        !          11783:   -1,  1,  1, -1, -1, -1,  0,  0, -1, -1, -1,	/* 41-51 */
        !          11784:    -1 } ; /* --- end-of-horzedges[] --- */
        !          11785: /* --- 0,1,2=pattern contains vertical bg,fg,both edge; -1=no edge --- */
        !          11786: static	int vertedges[] = { -1,		/* 0,1,2 = vert bg,fg,both edge */
        !          11787:    0,  1,  0,  0,  1,  1,  0, -1, -1, -1,	/*  1-10 */
        !          11788:    0,  0,  1, -1, -1, -1,  1,  1, -1, -1,	/* 11-20 */
        !          11789:   -1,  0,  0,  0, -1, -1,  0, -1, -1, -1,	/* 21-30 */
        !          11790:   -1,  1,  1,  1, -1, -1,  1, -1, -1, -1,	/* 31-40 */
        !          11791:   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,	/* 41-51 */
        !          11792:    -1 } ; /* --- end-of-vertedges[] --- */
        !          11793: /* --- 0,1,2=pattern contains diagonal bg,fg,both edge; -1=no edge --- */
        !          11794: static	int diagedges[] = { -1,		/* 0,1,2 = diag bg,fg,both edge */
        !          11795:    0,  1,  0,  0,  1,  1,  0,  0,  0,  0,	/*  1-10 */
        !          11796:    2, -1,  1,  1,  1,  1,  1, -1,  0,  2,	/* 11-20 */
        !          11797:    0, -1,  0,  2,  0, -1,  1,  1,  1,  1,	/* 21-30 */
        !          11798:    1, -1,  1,  2,  1,  1, -1,  1,  2, -1,	/* 31-40 */
        !          11799:    1,  0, -1,  2,  1,  0,  1, -1,  1, -1,  1,	/* 41-51 */
        !          11800:    -1 } ; /* --- end-of-diagedges[] --- */
        !          11801: /* -------------------------------------------------------------------------
        !          11802: Calculate bytemap as 9-point weighted average over bitmap
        !          11803: -------------------------------------------------------------------------- */
        !          11804: for ( irow=0; irow<height; irow++ )
        !          11805:  for ( icol=0; icol<width; icol++ )
        !          11806:   {
        !          11807:   /* --- local allocations and declarations --- */
        !          11808:   int	bitval=0,			/* value of rp bit at irow,icol */
        !          11809: 	isbgdiag=0, isfgdiag=0,		/*does pixel border a bg or fg edge*/
        !          11810: 	aabyteval=0;			/* antialiased (or unchanged) value*/
        !          11811:   /* --- get gridnum and center bit value, init aabyteval --- */
        !          11812:   imap++;				/* first set imap=icol + irow*width*/
        !          11813:   gridnum = aagridnum(rp,irow,icol);	/*grid# coding 3x3 grid at irow,icol*/
        !          11814:   bitval = (gridnum&1);			/* center bit set if gridnum odd */
        !          11815:   aabyteval = (intbyte)(bitval==bgbitval?0:grayscale-1); /* default aa val */
        !          11816:   bytemap[imap] = (intbyte)(aabyteval);	/* init antialiased pixel */
        !          11817:   if ( gridnum<0 || gridnum>511 ) continue; /* gridnum out of bounds*/
        !          11818:   /* --- check if we're antialiasing this pixel --- */
        !          11819:   if ( (isbgonly && bitval==fgbitval)	/* only antialias background bit */
        !          11820:   ||   (isfgonly && bitval==bgbitval) )	/* only antialias foreground bit */
        !          11821:     continue;				/* leave default and do next bit */
        !          11822:   /* --- look up pattern number, 1-51, corresponding to input gridnum --- */
        !          11823:   patternum = aapatternnum(gridnum);	/* look up pattern number */
        !          11824:   if ( patternum<1 || patternum>51 ) continue; /* some internal error */
        !          11825:   /* --- special pattern number processing --- */
        !          11826:   if ( (aabyteval = aapatterns(rp,irow,icol,gridnum,patternum,grayscale))
        !          11827:   >=   0 ) {				/* special processing for pattern */
        !          11828:     bytemap[imap] = (intbyte)(aabyteval); /* set antialiased pixel */
        !          11829:     continue; }				/* and continue with next pixel */
        !          11830:   /* --- check for diagonal edges --- */
        !          11831:   isbgdiag = ( diagedges[patternum]==2 || /*current pixel borders a bg edge*/
        !          11832:                diagedges[patternum]==0 );
        !          11833:   isfgdiag = ( diagedges[patternum]==2 || /*current pixel borders a fg edge*/
        !          11834:                diagedges[patternum]==1 );
        !          11835:   /* ---check top/bot left/right edges for corners (added by j.forkosh)--- */
        !          11836:   if ( 1 ) {				/* true to perform test */
        !          11837:     int	isbghorz=0, isfghorz=0, isbgvert=0, isfgvert=0, /* horz/vert edges */
        !          11838: 	horzedge=horzedges[patternum], vertedge=vertedges[patternum];
        !          11839:     isbghorz = (horzedge==2||horzedge==0); /* top or bottom edge is all bg */
        !          11840:     isfghorz = (horzedge==2||horzedge==1); /* top or bottom edge is all fg */
        !          11841:     isbgvert = (vertedge==2||vertedge==0); /* left or right edge is all bg */
        !          11842:     isfgvert = (vertedge==2||vertedge==1); /* left or right edge is all fg */
        !          11843:     if ( (isbghorz && isbgvert && (bitval==fgbitval))	/* we're at an...*/
        !          11844:     ||   (isfghorz && isfgvert && (bitval==bgbitval)) )	/*...inside corner */
        !          11845: 	continue;					/* don't antialias */
        !          11846:     } /* --- end-of-if(1) --- */
        !          11847: #if 0
        !          11848:   /* --- check #gaps for checkerboard (added by j.forkosh) --- */
        !          11849:   if ( 0 ) {				/* true to perform test */
        !          11850:     int	ngaps=0, mingaps=1,maxgaps=2;	/* count #fg/bg flips (max=4 noop) */
        !          11851:     if ( nwbitval!=nnbitval ) ngaps++;	/* upper-left =? upper */
        !          11852:     if ( nnbitval!=nebitval ) ngaps++;	/* upper =? upper-right */
        !          11853:     if ( nebitval!=eebitval ) ngaps++;	/* upper-right =? right */
        !          11854:     if ( eebitval!=sebitval ) ngaps++;	/* right =? lower-right */
        !          11855:     if ( sebitval!=ssbitval ) ngaps++;	/* lower-right =? lower */
        !          11856:     if ( ssbitval!=swbitval ) ngaps++;	/* lower =? lower-left */
        !          11857:     if ( swbitval!=wwbitval ) ngaps++;	/* lower-left =? left */
        !          11858:     if ( wwbitval!=nwbitval ) ngaps++;	/* left =? upper-left */
        !          11859:     if ( ngaps > 0 ) ngaps /= 2;	/* each gap has 2 bg/fg flips */
        !          11860:     if ( ngaps<mingaps || ngaps>maxgaps ) continue;
        !          11861:     } /* --- end-of-if(1) --- */
        !          11862: #endif
        !          11863:   /* --- antialias if necessary --- */
        !          11864:   if ( (isbgalias && isbgdiag)		/* alias pixel surrounding bg */
        !          11865:   ||   (isfgalias && isfgdiag)		/* alias pixel surrounding fg */
        !          11866:   ||   (isbgdiag  && isfgdiag) )	/* neighboring fg and bg pixel */
        !          11867:     {
        !          11868:     int	aasumval =			/* sum wts[]*bitmap[] */
        !          11869: 	aacenterwt*bitval +		/* apply centerwt to center pixel */
        !          11870: 	aaadjacentwt*nadjacents[patternum] + /* similarly for adjacents */
        !          11871: 	aacornerwt*ncorners[patternum];	/* and corners */
        !          11872:     double aawtval = ((double)aasumval)/((double)totwts); /* weighted val */
        !          11873:     aabyteval= (int)(((double)(grayscale-1))*aawtval+0.5); /*0...grayscale-1*/
        !          11874:     bytemap[imap] = (intbyte)(aabyteval); /* set antialiased pixel */
        !          11875:     if ( msglevel>=99 && msgfp!=NULL ) { fprintf(msgfp, /*diagnostic output*/
        !          11876:       "%s> irow,icol,imap=%d,%d,%d aawtval=%.4f aabyteval=%d",
        !          11877:       (isfirstaa?"aapnmlookup algorithm":"aapnm"),
        !          11878:       irow,icol,imap, aawtval,aabyteval);
        !          11879:       if ( msglevel < 100 ) fprintf(msgfp,"\n"); /* no more output */
        !          11880:       else fprintf(msgfp,", grid#,pattern#=%d,%d\n",gridnum,patternum);
        !          11881:       isfirstaa = 0; }
        !          11882:     } /* --- end-of-if(isedge) --- */
        !          11883:   } /* --- end-of-for(irow,icol) --- */
        !          11884: /* -------------------------------------------------------------------------
        !          11885: Back to caller with gray-scale anti-aliased bytemap
        !          11886: -------------------------------------------------------------------------- */
        !          11887: /*end_of_job:*/
        !          11888:   return ( 1 );
        !          11889: } /* --- end-of-function aapnmlookup() --- */
        !          11890: 
        !          11891: 
        !          11892: /* ==========================================================================
        !          11893:  * Function:	aapatterns ( rp, irow, icol, gridnum, patternum, grayscale )
        !          11894:  * Purpose:	For patterns requireing special processing,
        !          11895:  *		calculates anti-aliased value for pixel at irow,icol,
        !          11896:  *		whose surrounding 3x3 pixel grid is coded by gridnum
        !          11897:  *		(which must correspond to a pattern requiring special
        !          11898:  *		processing).
        !          11899:  * --------------------------------------------------------------------------
        !          11900:  * Arguments:	rp (I)		raster *  to raster whose bitmap
        !          11901:  *				is to be anti-aliased
        !          11902:  *		irow (I)	int containing row, 0...height-1,
        !          11903:  *				of pixel to be antialiased
        !          11904:  *		icol (I)	int containing col, 0...width-1,
        !          11905:  *				of pixel to be antialiased
        !          11906:  *		gridnum (I)	int containing 0...511 corresponding to
        !          11907:  *				3x3 pixel grid surrounding irow,icol
        !          11908:  *		patternum (I)	int containing 1...51 pattern# of
        !          11909:  *				the 3x3 grid surrounding irow,icol
        !          11910:  *		grayscale (I)	int containing number of grayscales
        !          11911:  *				to be calculated, 0...grayscale-1
        !          11912:  *				(should typically be given as 256)
        !          11913:  * --------------------------------------------------------------------------
        !          11914:  * Returns:	( int )		0...grayscale-1 for success,
        !          11915:  *				-1 = error, or no special processing required
        !          11916:  * --------------------------------------------------------------------------
        !          11917:  * Notes:    o
        !          11918:  * ======================================================================= */
        !          11919: /* --- entry point --- */
        !          11920: int	aapatterns (raster *rp, int irow, int icol,
        !          11921: 	int gridnum, int patternum, int grayscale)
        !          11922: {
        !          11923: /* -------------------------------------------------------------------------
        !          11924: Allocations and Declarations
        !          11925: -------------------------------------------------------------------------- */
        !          11926: int	aaval = (-1);			/* antialiased value returned */
        !          11927: int	iscenter = (gridnum&1);		/* true if center pixel set/black */
        !          11928: int	aapatternnum(),			/* if patternum not supplied */
        !          11929: 	aapattern1124(),		/* routine for patterns #11,24 */
        !          11930: 	aapattern19(),			/* special routine for pattern #19 */
        !          11931: 	aapattern20(),			/* special routine for pattern #20 */
        !          11932: 	aapattern39();			/* special routine for pattern #39 */
        !          11933: /* -------------------------------------------------------------------------
        !          11934: special pattern number processing
        !          11935: -------------------------------------------------------------------------- */
        !          11936: if ( 1 ) {
        !          11937:   if ( patternum < 1 )			/* pattern# not supplied by caller */
        !          11938:     patternum = aapatternnum(gridnum);	/* so look it up ourselves */
        !          11939:   switch ( patternum ) {
        !          11940:     default: break;			/* no special processing */
        !          11941:     case 11:
        !          11942:     case 24: aaval = aapattern1124(rp,irow,icol,gridnum,grayscale); break;
        !          11943:     case 19: aaval = aapattern19(rp,irow,icol,gridnum,grayscale); break;
        !          11944:     case 20: aaval = aapattern20(rp,irow,icol,gridnum,grayscale); break;
        !          11945:     case 39: aaval = aapattern39(rp,irow,icol,gridnum,grayscale); break;
        !          11946:     /* case 24: if ( (gridnum&1) == 0 ) aaval=0; break; */
        !          11947:     case 29: aaval = (iscenter?grayscale-1:0); break; /* no antialiasing */
        !          11948:     } /* --- end-of-switch(patternum) --- */
        !          11949:   } /* --- end-of-if() --- */
        !          11950: return ( aaval );			/* return antialiased val to caller*/
        !          11951: } /* --- end-of-function aapatterns() --- */
        !          11952: 
        !          11953: 
        !          11954: /* ==========================================================================
        !          11955:  * Function:	aapattern1124 ( rp, irow, icol, gridnum, grayscale )
        !          11956:  * Purpose:	calculates anti-aliased value for pixel at irow,icol,
        !          11957:  *		whose surrounding 3x3 pixel grid is coded by gridnum
        !          11958:  *		(which must correspond to pattern #11 or #24).
        !          11959:  * --------------------------------------------------------------------------
        !          11960:  * Arguments:	rp (I)		raster *  to raster whose bitmap
        !          11961:  *				is to be anti-aliased
        !          11962:  *		irow (I)	int containing row, 0...height-1,
        !          11963:  *				of pixel to be antialiased
        !          11964:  *		icol (I)	int containing col, 0...width-1,
        !          11965:  *				of pixel to be antialiased
        !          11966:  *		gridnum (I)	int containing 0...511 corresponding to
        !          11967:  *				3x3 pixel grid surrounding irow,icol
        !          11968:  *		grayscale (I)	int containing number of grayscales
        !          11969:  *				to be calculated, 0...grayscale-1
        !          11970:  *				(should typically be given as 256)
        !          11971:  * --------------------------------------------------------------------------
        !          11972:  * Returns:	( int )		0...grayscale-1 for success, -1=any error
        !          11973:  * --------------------------------------------------------------------------
        !          11974:  * Notes:    o	Handles the eight gridnum's
        !          11975:  *		(gridnum/2 shown to eliminate irrelevant low-order bit)
        !          11976:  *		  ---        ---         -*-          -*-
        !          11977:  *		  --* = 10   *-- = 18    --* = 72     *-- = 80  (pattern$11)
        !          11978:  *		  -*-        -*-         ---          ---
        !          11979:  *
        !          11980:  *		  ---        ---         -**          **-
        !          11981:  *		  --* = 11   *-- = 22    --* = 104    *-- = 208 (pattern$24)
        !          11982:  *		  -**        **-         ---          ---
        !          11983:  *	     o	For black * center pixel, using grid#10 as an example,
        !          11984:  *		pixel stays ---      antialiased  ---*
        !          11985:  *		black if    -***     if part of	  -**
        !          11986:  *		part of a   -*-      a diagonal	  -*- 
        !          11987:  *		corner, eg,  *       line, eg,	  *
        !          11988:  * ======================================================================= */
        !          11989: /* --- entry point --- */
        !          11990: int	aapattern1124 (raster *rp, int irow, int icol,
        !          11991: 	int gridnum, int grayscale)
        !          11992: {
        !          11993: /* -------------------------------------------------------------------------
        !          11994: Allocations and Declarations
        !          11995: -------------------------------------------------------------------------- */
        !          11996: int	aaval = (-1);			/* antialiased value returned */
        !          11997: int	iscenter = gridnum&1;		/* true if pixel at irow,icol black*/
        !          11998: int	patternum = 24;			/* init for pattern#24 default */
        !          11999: pixbyte	*bitmap = rp->pixmap;		/* local rp->pixmap ptr */
        !          12000: int	width=rp->width, height=rp->height; /* width, height of raster */
        !          12001: int	jrow=irow, jcol=icol;		/* corner or diagonal row,col */
        !          12002: int	vertcornval=0, horzcornval=0,	/* vertical, horizontal corner bits*/
        !          12003: 	topdiagval=0,  botdiagval=0,	/* upper,lower diagonal pixel bits */
        !          12004: 	cornval=0, diagval=0;		/* vert+horzcorn, top+botdiag */
        !          12005: int	hdirection=99, vdirection=99,	/* horz,vert corner direction */
        !          12006: 	hturn=99,vturn=99, aafollowline(); /* follow corner till turns */
        !          12007: /* -------------------------------------------------------------------------
        !          12008: Check corner and diagonal pixels
        !          12009: -------------------------------------------------------------------------- */
        !          12010: if ( 0 ) goto end_of_job;		/* true to turn off pattern1124 */
        !          12011: switch ( gridnum/2 ) {			/* check pattern#11,24 corner, diag*/
        !          12012:   default: goto end_of_job;		/* not a pattern#11,24 gridnum */
        !          12013:   case 10: patternum=11; case 11:
        !          12014:     hdirection = 2;  vdirection = -1;	/* directions to follow corner */
        !          12015:     if ( (jrow=irow+2) < height ) {	/* vert corner below center pixel */
        !          12016:       vertcornval = getlongbit(bitmap,(icol+jrow*width));
        !          12017:       if ( (icol-1) >= 0 )		/* lower diag left of center */
        !          12018:         botdiagval = getlongbit(bitmap,((icol-1)+jrow*width)); }
        !          12019:     if ( (jcol=icol+2) < width ) {	/* horz corner right of center */
        !          12020:       horzcornval = getlongbit(bitmap,(jcol+irow*width));
        !          12021:       if ( (irow-1) >= 0 )		/* upper diag above center */
        !          12022:         topdiagval = getlongbit(bitmap,(jcol+(irow-1)*width)); }
        !          12023:     break;
        !          12024:   case 18: patternum=11; case 22:
        !          12025:     hdirection = -2;  vdirection = -1;	/* directions to follow corner */
        !          12026:     if ( (jrow=irow+2) < height ) {	/* vert corner below center pixel */
        !          12027:       vertcornval = getlongbit(bitmap,(icol+jrow*width));
        !          12028:       if ( (icol+1) < width )		/* lower diag right of center */
        !          12029:         botdiagval = getlongbit(bitmap,((icol+1)+jrow*width)); }
        !          12030:     if ( (jcol=icol-2) >= 0 ) {		/* horz corner left of center */
        !          12031:       horzcornval = getlongbit(bitmap,(jcol+irow*width));
        !          12032:       if ( (irow-1) >= 0 )		/* upper diag above center */
        !          12033:         topdiagval = getlongbit(bitmap,(jcol+(irow-1)*width)); }
        !          12034:     break;
        !          12035:   case 72: patternum=11; case 104:
        !          12036:     hdirection = 2;  vdirection = 1;	/* directions to follow corner */
        !          12037:     if ( (jrow=irow-2) >= 0 ) {		/* vert corner above center pixel */
        !          12038:       vertcornval = getlongbit(bitmap,(icol+jrow*width));
        !          12039:       if ( (icol-1) >= 0 )		/* upper diag left of center */
        !          12040:         topdiagval = getlongbit(bitmap,((icol-1)+jrow*width)); }
        !          12041:     if ( (jcol=icol+2) < width ) {	/* horz corner right of center */
        !          12042:       horzcornval = getlongbit(bitmap,(jcol+irow*width));
        !          12043:       if ( (irow+1) < height )		/* lower diag below center */
        !          12044:         botdiagval = getlongbit(bitmap,(jcol+(irow+1)*width)); }
        !          12045:     break;
        !          12046:   case 80: patternum=11; case 208:
        !          12047:     hdirection = -2;  vdirection = 1;	/* directions to follow corner */
        !          12048:     if ( (jrow=irow-2) >= 0 ) {		/* vert corner above center pixel */
        !          12049:       vertcornval = getlongbit(bitmap,(icol+jrow*width));
        !          12050:       if ( (icol+1) < width )		/* upper diag right of center */
        !          12051:         topdiagval = getlongbit(bitmap,((icol+1)+jrow*width)); }
        !          12052:     if ( (jcol=icol-2) >= 0 ) {		/* horz corner left of center */
        !          12053:       horzcornval = getlongbit(bitmap,(jcol+irow*width));
        !          12054:       if ( (irow+1) < height )		/* lower diag below center */
        !          12055:         botdiagval = getlongbit(bitmap,(jcol+(irow+1)*width)); }
        !          12056:     break;
        !          12057:   } /* --- end-of-switch(gridnum/2) --- */
        !          12058: cornval = vertcornval+horzcornval;	/* 0=no corner bits, 1, 2=both */
        !          12059: diagval = topdiagval+botdiagval;	/* 0=no diag bits, 1, 2=both */
        !          12060: /* -------------------------------------------------------------------------
        !          12061: Handle white center
        !          12062: -------------------------------------------------------------------------- */
        !          12063: if ( 1 && !iscenter ) { aaval = (patternum==11?51:64);  goto end_of_job; }
        !          12064: /* -------------------------------------------------------------------------
        !          12065: Handle black center
        !          12066: -------------------------------------------------------------------------- */
        !          12067: if ( diagval > 1 ) aaval = ( patternum==24? 255:191 );
        !          12068: else {
        !          12069:   hturn = aafollowline(rp,irow,icol,hdirection);
        !          12070:   vturn = aafollowline(rp,irow,icol,vdirection);
        !          12071:   if ( vturn*hdirection < 0  && hturn*vdirection < 0 )
        !          12072:        aaval = ( patternum==24? 255:191 );
        !          12073:   else aaval = grayscale-1; }		/* actual corner */
        !          12074: /* -------------------------------------------------------------------------
        !          12075: Back to caller with grayscale antialiased value for pixel at irow,icol
        !          12076: -------------------------------------------------------------------------- */
        !          12077: end_of_job:
        !          12078:   if ( aaval >= 0 )			/* have antialiasing result */
        !          12079:    if ( msglevel>=99 && msgfp!=NULL ) fprintf(msgfp, /* diagnostic output */
        !          12080:     "aapattern1124> irow,icol,grid#/2=%d,%d,%d, top,botdiag=%d,%d, "
        !          12081:     "vert,horzcorn=%d,%d, v,hdir=%d,%d, v,hturn=%d,%d, aaval=%d\n",
        !          12082:     irow,icol,gridnum/2, topdiagval,botdiagval, vertcornval,horzcornval,
        !          12083:     vdirection,hdirection, vturn,hturn, aaval);
        !          12084:   return ( aaval );			/* back with antialiased value */
        !          12085: } /* --- end-of-function aapattern1124() --- */
        !          12086: 
        !          12087: 
        !          12088: /* ==========================================================================
        !          12089:  * Function:	aapattern19 ( rp, irow, icol, gridnum, grayscale )
        !          12090:  * Purpose:	calculates anti-aliased value for pixel at irow,icol,
        !          12091:  *		whose surrounding 3x3 pixel grid is coded by gridnum
        !          12092:  *		(which must correspond to pattern #19).
        !          12093:  * --------------------------------------------------------------------------
        !          12094:  * Arguments:	rp (I)		raster *  to raster whose bitmap
        !          12095:  *				is to be anti-aliased
        !          12096:  *		irow (I)	int containing row, 0...height-1,
        !          12097:  *				of pixel to be antialiased
        !          12098:  *		icol (I)	int containing col, 0...width-1,
        !          12099:  *				of pixel to be antialiased
        !          12100:  *		gridnum (I)	int containing 0...511 corresponding to
        !          12101:  *				3x3 pixel grid surrounding irow,icol
        !          12102:  *		grayscale (I)	int containing number of grayscales
        !          12103:  *				to be calculated, 0...grayscale-1
        !          12104:  *				(should typically be given as 256)
        !          12105:  * --------------------------------------------------------------------------
        !          12106:  * Returns:	( int )		0...grayscale-1 for success, -1=any error
        !          12107:  * --------------------------------------------------------------------------
        !          12108:  * Notes:    o	Handles the four gridnum's
        !          12109:  *		(gridnum/2 shown to eliminate irrelevant low-order bit)
        !          12110:  *		  ---        --*         *--          ***
        !          12111:  *		  --- = 7    --* = 41    *-- = 148    --- = 224
        !          12112:  *		  ***        --*         *--          ---
        !          12113:  * ======================================================================= */
        !          12114: /* --- entry point --- */
        !          12115: int	aapattern19 (raster *rp, int irow, int icol,
        !          12116: 	int gridnum, int grayscale)
        !          12117: {
        !          12118: /* -------------------------------------------------------------------------
        !          12119: Allocations and Declarations
        !          12120: -------------------------------------------------------------------------- */
        !          12121: int	aaval = (-1);			/* antialiased value returned */
        !          12122: int	iscenter = gridnum&1;		/* true if pixel at irow,icol black*/
        !          12123: int	orientation = 1,		/* 1=vertical, 2=horizontal */
        !          12124: 	jrow=irow, jcol=icol;		/* middle pixel of *** line */
        !          12125: int	turn1=0,turn2=0, aafollowline(); /* follow *** line till it turns */
        !          12126: /* -------------------------------------------------------------------------
        !          12127: Initialization and calculation of antialiased value
        !          12128: -------------------------------------------------------------------------- */
        !          12129: /* --- check input -- */
        !          12130: if ( iscenter ) goto end_of_job;	/* we only antialias white pixels */
        !          12131: /* --- set params --- */
        !          12132: switch ( gridnum/2 ) {			/* check pattern#19 orientation */
        !          12133:   default: goto end_of_job;		/* not a pattern#19 gridnum */
        !          12134:   case 7:   orientation=2; jrow++; break;
        !          12135:   case 41:  orientation=1; jcol++; break;
        !          12136:   case 148: orientation=1; jcol--; break;
        !          12137:   case 224: orientation=2; jrow--; break;
        !          12138:   } /* --- end-of-switch(gridnum/2) --- */
        !          12139: /* --- get turns in both directions --- */
        !          12140: if ( (turn1 = aafollowline(rp,jrow,jcol,orientation)) == 0 ) goto end_of_job;
        !          12141: if ( (turn2 = aafollowline(rp,jrow,jcol,-orientation)) == 0) goto end_of_job;
        !          12142: if ( turn1*turn2 >= 0 ) goto end_of_job; /* both turns in same direction */
        !          12143: /* --- weight pixel --- */
        !          12144: aaval = grayscale / ( 3 + min2(abs(turn1),abs(turn2)) );
        !          12145: /* -------------------------------------------------------------------------
        !          12146: Back to caller with grayscale antialiased value for pixel at irow,icol
        !          12147: -------------------------------------------------------------------------- */
        !          12148: end_of_job:
        !          12149:   if ( aaval >= 0 )			/* have antialiasing result */
        !          12150:    if ( msglevel>=99 && msgfp!=NULL ) fprintf(msgfp, /* diagnostic output */
        !          12151:     "aapattern19> irow,icol,grid#/2=%d,%d,%d, turn+%d,%d=%d,%d, aaval=%d\n",
        !          12152:     irow,icol,gridnum/2, orientation,-orientation,turn1,turn2, aaval);
        !          12153:   return ( aaval );			/* back with antialiased value */
        !          12154: } /* --- end-of-function aapattern19() --- */
        !          12155: 
        !          12156: 
        !          12157: /* ==========================================================================
        !          12158:  * Function:	aapattern20 ( rp, irow, icol, gridnum, grayscale )
        !          12159:  * Purpose:	calculates anti-aliased value for pixel at irow,icol,
        !          12160:  *		whose surrounding 3x3 pixel grid is coded by gridnum
        !          12161:  *		(which must correspond to pattern #20).
        !          12162:  * --------------------------------------------------------------------------
        !          12163:  * Arguments:	rp (I)		raster *  to raster whose bitmap
        !          12164:  *				is to be anti-aliased
        !          12165:  *		irow (I)	int containing row, 0...height-1,
        !          12166:  *				of pixel to be antialiased
        !          12167:  *		icol (I)	int containing col, 0...width-1,
        !          12168:  *				of pixel to be antialiased
        !          12169:  *		gridnum (I)	int containing 0...511 corresponding to
        !          12170:  *				3x3 pixel grid surrounding irow,icol
        !          12171:  *		grayscale (I)	int containing number of grayscales
        !          12172:  *				to be calculated, 0...grayscale-1
        !          12173:  *				(should typically be given as 256)
        !          12174:  * --------------------------------------------------------------------------
        !          12175:  * Returns:	( int )		0...grayscale-1 for success, -1=any error
        !          12176:  * --------------------------------------------------------------------------
        !          12177:  * Notes:    o	Handles the eight gridnum's
        !          12178:  *		(gridnum/2 shown to eliminate irrelevant low-order bit)
        !          12179:  *		  ---        ---         --*          -*-      
        !          12180:  *		  --* = 14   *-- = 19    --* = 42     --* = 73
        !          12181:  *		  **-        -**         -*-          --*     
        !          12182:  *
        !          12183:  *		  -*-        -**         *--          **-      
        !          12184:  *		  *-- = 84   *-- = 112   *-- = 146    --* = 200
        !          12185:  *		  *--        ---         -*-          ---     
        !          12186:  * ======================================================================= */
        !          12187: /* --- entry point --- */
        !          12188: int	aapattern20 (raster *rp, int irow, int icol,
        !          12189: 	int gridnum, int grayscale)
        !          12190: {
        !          12191: /* -------------------------------------------------------------------------
        !          12192: Allocations and Declarations
        !          12193: -------------------------------------------------------------------------- */
        !          12194: int	aaval = (-1);			/* antialiased value returned */
        !          12195: int	iscenter = gridnum&1;		/* true if pixel at irow,icol black*/
        !          12196: int	direction = 1,			/* direction to follow ** line */
        !          12197: 	jrow1=irow, jcol1=icol,		/* coords of * */
        !          12198: 	jrow2=irow, jcol2=icol;		/* coords of adjacent ** pixel */
        !          12199: int	turn1=0,turn2=0, aafollowline(); /* follow *,** lines till turns */
        !          12200: /* -------------------------------------------------------------------------
        !          12201: Initialization and calculation of antialiased value
        !          12202: -------------------------------------------------------------------------- */
        !          12203: /* --- check input -- */
        !          12204: if ( 1 ) goto end_of_job;		/* don't want this one */
        !          12205: if ( iscenter ) goto end_of_job;	/* we only antialias white pixels */
        !          12206: /* --- set params --- */
        !          12207: switch ( gridnum/2 ) {			/* check pattern#20 orientation */
        !          12208:   default: goto end_of_job;		/* not a pattern#20 gridnum */
        !          12209:   case 14:  direction=-2; jcol1++; jrow2++; break;
        !          12210:   case 19:  direction=2;  jcol1--; jrow2++; break;
        !          12211:   case 42:  direction=1;  jrow1++; jcol2++; break;
        !          12212:   case 73:  direction=-1; jrow1--; jcol2++; break;
        !          12213:   case 84:  direction=-1; jrow1--; jcol2--; break;
        !          12214:   case 112: direction=2;  jcol1--; jrow2--; break;
        !          12215:   case 146: direction=1;  jrow1++; jcol2--; break;
        !          12216:   case 200: direction=-2; jcol1++; jrow2--; break;
        !          12217:   } /* --- end-of-switch(gridnum/2) --- */
        !          12218: /* --- get turns in both directions --- */
        !          12219: if ( (turn1=aafollowline(rp,jrow1,jcol1,-direction)) == 0 ) goto end_of_job;
        !          12220: if ( (turn2=aafollowline(rp,jrow2,jcol2,direction))  == 0 ) goto end_of_job;
        !          12221: if ( turn1*turn2 >= 0 ) goto end_of_job; /* both turns in same direction */
        !          12222: /* --- weight pixel --- */
        !          12223: aaval = grayscale / ( 3 + min2(abs(turn1),abs(turn2)) );
        !          12224: /* -------------------------------------------------------------------------
        !          12225: Back to caller with grayscale antialiased value for pixel at irow,icol
        !          12226: -------------------------------------------------------------------------- */
        !          12227: end_of_job:
        !          12228:   if ( aaval >= 0 )			/* have antialiasing result */
        !          12229:    if ( msglevel>=99 && msgfp!=NULL ) fprintf(msgfp, /* diagnostic output */
        !          12230:     "aapattern20> irow,icol,grid#/2=%d,%d,%d, turn%d,%d=%d,%d, aaval=%d\n",
        !          12231:     irow,icol,gridnum/2, -direction,direction,turn1,turn2, aaval);
        !          12232:   return ( aaval );			/* back with antialiased value */
        !          12233: } /* --- end-of-function aapattern20() --- */
        !          12234: 
        !          12235: 
        !          12236: /* ==========================================================================
        !          12237:  * Function:	aapattern39 ( rp, irow, icol, gridnum, grayscale )
        !          12238:  * Purpose:	calculates anti-aliased value for pixel at irow,icol,
        !          12239:  *		whose surrounding 3x3 pixel grid is coded by gridnum
        !          12240:  *		(which must correspond to pattern #39).
        !          12241:  * --------------------------------------------------------------------------
        !          12242:  * Arguments:	rp (I)		raster *  to raster whose bitmap
        !          12243:  *				is to be anti-aliased
        !          12244:  *		irow (I)	int containing row, 0...height-1,
        !          12245:  *				of pixel to be antialiased
        !          12246:  *		icol (I)	int containing col, 0...width-1,
        !          12247:  *				of pixel to be antialiased
        !          12248:  *		gridnum (I)	int containing 0...511 corresponding to
        !          12249:  *				3x3 pixel grid surrounding irow,icol
        !          12250:  *		grayscale (I)	int containing number of grayscales
        !          12251:  *				to be calculated, 0...grayscale-1
        !          12252:  *				(should typically be given as 256)
        !          12253:  * --------------------------------------------------------------------------
        !          12254:  * Returns:	( int )		0...grayscale-1 for success, -1=any error
        !          12255:  * --------------------------------------------------------------------------
        !          12256:  * Notes:    o	Handles the eight gridnum's
        !          12257:  *		(gridnum/2 shown to eliminate irrelevant low-order bit)
        !          12258:  *		  ---        ---         --*          -**      
        !          12259:  *		  --* = 15   *-- = 23    --* = 43     --* = 105
        !          12260:  *		  ***        ***         -**          --*     
        !          12261:  *
        !          12262:  *		  **-        ***         *--          ***      
        !          12263:  *		  *-- = 212  *-- = 240   *-- = 150    --* = 232
        !          12264:  *		  *--        ---         **-          ---     
        !          12265:  * ======================================================================= */
        !          12266: /* --- entry point --- */
        !          12267: int	aapattern39 (raster *rp, int irow, int icol,
        !          12268: 	int gridnum, int grayscale)
        !          12269: {
        !          12270: /* -------------------------------------------------------------------------
        !          12271: Allocations and Declarations
        !          12272: -------------------------------------------------------------------------- */
        !          12273: int	aaval = (-1);			/* antialiased value returned */
        !          12274: int	iscenter = gridnum&1;		/* true if pixel at irow,icol black*/
        !          12275: int	direction = 1,			/* direction to follow ** line */
        !          12276: 	jrow1=irow, jcol1=icol,		/* coords of * */
        !          12277: 	jrow2=irow, jcol2=icol;		/* coords of adjacent ** pixel */
        !          12278: int	turn1=0,turn2=0, aafollowline(); /* follow *,** lines till turns */
        !          12279: /* -------------------------------------------------------------------------
        !          12280: Initialization and calculation of antialiased value
        !          12281: -------------------------------------------------------------------------- */
        !          12282: /* --- check input -- */
        !          12283: if ( iscenter ) goto end_of_job;	/* we only antialias white pixels */
        !          12284: /* --- set params --- */
        !          12285: switch ( gridnum/2 ) {			/* check pattern#39 orientation */
        !          12286:   default: goto end_of_job;		/* not a pattern#39 gridnum */
        !          12287:   case 15:  direction=-2; jcol1++; jrow2++; break;
        !          12288:   case 23:  direction=2;  jcol1--; jrow2++; break;
        !          12289:   case 43:  direction=1;  jrow1++; jcol2++; break;
        !          12290:   case 105: direction=-1; jrow1--; jcol2++; break;
        !          12291:   case 212: direction=-1; jrow1--; jcol2--; break;
        !          12292:   case 240: direction=2;  jcol1--; jrow2--; break;
        !          12293:   case 150: direction=1;  jrow1++; jcol2--; break;
        !          12294:   case 232: direction=-2; jcol1++; jrow2--; break;
        !          12295:   } /* --- end-of-switch(gridnum/2) --- */
        !          12296: /* --- get turns directions (tunr1==1 signals inside corner) --- */
        !          12297: if ( (turn1=aafollowline(rp,jrow1,jcol1,-direction)) == 1 )
        !          12298:   { aaval=0; goto end_of_job; }
        !          12299: if ( 1 ) goto end_of_job;  /* stop here for now */
        !          12300: if ( (turn2=aafollowline(rp,jrow2,jcol2,direction))  == 0 ) goto end_of_job;
        !          12301: if ( turn1*turn2 >= 0 ) goto end_of_job; /* both turns in same direction */
        !          12302: /* --- weight pixel --- */
        !          12303: aaval = grayscale / ( 3 + min2(abs(turn1),abs(turn2)) );
        !          12304: /* -------------------------------------------------------------------------
        !          12305: Back to caller with grayscale antialiased value for pixel at irow,icol
        !          12306: -------------------------------------------------------------------------- */
        !          12307: end_of_job:
        !          12308:   if ( aaval >= 0 )			/* have antialiasing result */
        !          12309:    if ( msglevel>=99 && msgfp!=NULL ) fprintf(msgfp, /* diagnostic output */
        !          12310:     "aapattern39> irow,icol,grid#/2=%d,%d,%d, turn%d,%d=%d,%d, aaval=%d\n",
        !          12311:     irow,icol,gridnum/2, -direction,direction,turn1,turn2, aaval);
        !          12312:   return ( aaval );			/* back with antialiased value */
        !          12313: } /* --- end-of-function aapattern39() --- */
        !          12314: 
        !          12315: 
        !          12316: /* ==========================================================================
        !          12317:  * Function:	aafollowline ( rp, irow, icol, direction )
        !          12318:  * Purpose:	starting with pixel at irow,icol, moves in
        !          12319:  *		specified direction looking for a "turn"
        !          12320:  * --------------------------------------------------------------------------
        !          12321:  * Arguments:	rp (I)		raster *  to raster containing pixel image
        !          12322:  *		irow (I)	int containing row, 0...height-1,
        !          12323:  *				of first pixel
        !          12324:  *		icol (I)	int containing col, 0...width-1,
        !          12325:  *				of first pixel
        !          12326:  *		direction (I)	int containing +1 to follow line up/north
        !          12327:  *				(decreasing irow), -1 to follow line
        !          12328:  *				down/south (increasing irow), +2 to follow
        !          12329:  *				line right/east (increasing icol),
        !          12330:  *				-2 to follow line left/west (decreasing icol)
        !          12331:  * --------------------------------------------------------------------------
        !          12332:  * Returns:	( int )		#rows or #cols traversed prior to turn,
        !          12333:  *				or 0 if no turn detected (or for any error).
        !          12334:  *				Sign is + if turn direction is right/east or
        !          12335:  *				up/north, or is - if left/west or down/south.
        !          12336:  * --------------------------------------------------------------------------
        !          12337:  * Notes:     o	Here are some examples illustrating turn detection in
        !          12338:  *		+2 (right/east) direction.  Turns in other directions
        !          12339:  *		are detected similarly/symmetrically.  * denotes black
        !          12340:  *		bits (usually fg), - denotes white bits (usually bg),
        !          12341:  *		and ? denotes "don't care" bit (won't affect outcome).
        !          12342:  *		Arrow --> points to start pixel denoted by irow,icol.
        !          12343:  *
        !          12344:  *		   *???         -???	turn=0 (no turn) is returned
        !          12345:  *		-->*???   or -->-???	because the start pixel isn't
        !          12346:  *		   *???         -???	on an edge to begin with
        !          12347:  *
        !          12348:  *		   ----         **--	turn=0 returned because the
        !          12349:  *		-->***-   or -->***-	line ends abruptly without
        !          12350:  *		   ----	        ----	turning (even the second case)
        !          12351:  *
        !          12352:  *		   ---*         ---*	turn=0 returned because the
        !          12353:  *		-->***-   or -->****	line forms a Y or T rather
        !          12354:  *		   ---*	        ---*	than turning
        !          12355:  *
        !          12356:  *		   ***-	        ****	turn=+3 returned
        !          12357:  *		-->***-   or -->***-	(outside corner)
        !          12358:  *		   ----	        ----
        !          12359:  *
        !          12360:  *		   *****        ****-	turn=-4 returned
        !          12361:  *		-->*****  or -->****-	(inside corner)
        !          12362:  *		   ----*        ----*
        !          12363:  *
        !          12364:  *		   ----*        ----*	turn=+4 returned
        !          12365:  *		-->****-  or -->*****	(outside or inside corner)
        !          12366:  *		   -----        -----
        !          12367:  * ======================================================================= */
        !          12368: /* --- entry point --- */
        !          12369: int	aafollowline (raster *rp, int irow, int icol, int direction)
        !          12370: {
        !          12371: /* -------------------------------------------------------------------------
        !          12372: Allocations and Declarations
        !          12373: -------------------------------------------------------------------------- */
        !          12374: pixbyte	*bitmap = rp->pixmap;		/* local rp->pixmap ptr */
        !          12375: int	width=rp->width, height=rp->height; /* width, height of raster */
        !          12376: int	drow=0, dcol=0,			/* delta row,col to follow line */
        !          12377: 	jrow=irow, jcol=icol;		/* current row,col following line */
        !          12378: int	bitval=1,			/* value of rp bit at irow,icol */
        !          12379: 	fgval=1, bgval=0,		/* "fg" is whatever bitval is */
        !          12380: 	bitminus=0, bitplus=0;		/* value of left/down, right/up bit*/
        !          12381: int	isline=1, isedge=0;		/*isline signals one-pixel wide line*/
        !          12382: int	turn = 0,			/* detected turn back to caller */
        !          12383: 	maxturn = maxfollow;		/* don't follow more than max pixels*/
        !          12384: /* -------------------------------------------------------------------------
        !          12385: Initialization
        !          12386: -------------------------------------------------------------------------- */
        !          12387: /* --- check input --- */
        !          12388: if ( irow<0 || irow>=height		/* irow out-of-bounds */
        !          12389: ||   icol<0 || icol>=width ) goto end_of_job; /* icol out-of-bounds */
        !          12390: /* --- starting bit -- see if we're following a fg (usual), or bg line --- */
        !          12391: bitval = getlongbit(bitmap,(icol+irow*width)); /* starting pixel (bg or fg)*/
        !          12392: fgval = bitval;  bgval = (1-bitval);	/* define "fg" as whatever bitval is*/
        !          12393: /* --- set drow,dcol corresponding to desired direction --- */
        !          12394: switch ( direction ) {			/* determine drow,dcol for direction*/
        !          12395:   default: goto end_of_job;		/* unrecognized direction arg */
        !          12396:   case  1: drow = (-1); break;		/* follow line up/north */
        !          12397:   case -1: drow =   1;  break;		/* down/south */
        !          12398:   case  2: dcol =   1;  break;		/* right/east */
        !          12399:   case -2: dcol = (-1); break; }	/* left/west */
        !          12400: /* --- set bitminus and bitplus --- */
        !          12401: if ( drow == 0 ) {			/* we're following line right/left */
        !          12402:   if ( irow < height )			/* there's a pixel below current */
        !          12403:     bitminus = getlongbit(bitmap,(icol+(irow+1)*width)); /* get it */
        !          12404:   if ( irow > 0 )			/* there's a pixel above current */
        !          12405:     bitplus = getlongbit(bitmap,(icol+(irow-1)*width)); } /* get it */
        !          12406: if ( dcol == 0 ) {			/* we're following line up/down */
        !          12407:   if ( icol < width )			/* there's a pixel to the right */
        !          12408:     bitplus = getlongbit(bitmap,(icol+1+irow*width)); /* get it */
        !          12409:   if ( icol > 0 )			/* there's a pixel to the left */
        !          12410:     bitminus = getlongbit(bitmap,(icol-1+irow*width)); } /* get it */
        !          12411: /* --- check for lack of line to follow --- */
        !          12412: if ( bitval == bitplus			/* starting pixel same as above */
        !          12413: &&   bitval == bitminus )		/* and below (or right and left) */
        !          12414:   goto end_of_job;			/* so there's no line to follow */
        !          12415: /* --- set isline and isedge (already initted for isline) --- */
        !          12416: if ( bitval == bitplus )		/* starting pixel same as above */
        !          12417:   { isedge = (-1);  isline = 0; }	/* so we're at an edge below */
        !          12418: if (  bitval == bitminus )		/* starting pixel same as below */
        !          12419:   { isedge = 1;  isline = 0; }		/* so we're at an edge above */
        !          12420: /* -------------------------------------------------------------------------
        !          12421: follow line
        !          12422: -------------------------------------------------------------------------- */
        !          12423: while ( 1 ) {				/* until turn found (or max) */
        !          12424:   /* --- local allocations and declarations --- */
        !          12425:   int	dbitval=0,			/* value of bit at jrow,jcol */
        !          12426: 	dbitminus=0, dbitplus=0;	/* value of left/down, right/up bit*/
        !          12427:   /* --- bump pixel count and indexes; check for max or end-of-raster --- */
        !          12428:   turn++;				/* bump #pixels followed */
        !          12429:   jrow += drow;  jcol += dcol;		/* indexes of next pixel to check */
        !          12430:   if ( turn > maxturn			/* already followed max #pixels */
        !          12431:   ||   jrow<0 || jrow>=height		/* or jrow past end-of-raster */
        !          12432:   ||   jcol<0 || jcol>=width )		/* or jcol past end-of-raster */
        !          12433:     { turn = 0;  goto end_of_job; }	/* so quit without finding a turn */
        !          12434:   /* --- set current bit (dbitval) --- */
        !          12435:   dbitval = getlongbit(bitmap,(jcol+jrow*width)); /*value of jrow,jcol bit*/
        !          12436:   /* --- set dbitminus and dbitplus --- */
        !          12437:   if ( drow == 0 ) {			/* we're following line right/left */
        !          12438:     if ( irow < height )		/* there's a pixel below current */
        !          12439:       dbitminus = getlongbit(bitmap,(jcol+(irow+1)*width)); /* get it */
        !          12440:     if ( irow > 0 )			/* there's a pixel above current */
        !          12441:       dbitplus = getlongbit(bitmap,(jcol+(irow-1)*width)); } /* get it */
        !          12442:   if ( dcol == 0 ) {			/* we're following line up/down */
        !          12443:     if ( icol < width )			/* there's a pixel to the right */
        !          12444:       dbitplus = getlongbit(bitmap,(icol+1+jrow*width)); /* get it */
        !          12445:     if ( icol > 0 )			/* there's a pixel to the left */
        !          12446:       dbitminus = getlongbit(bitmap,(icol-1+jrow*width)); } /* get it */
        !          12447:   /* --- first check for abrupt end-of-line, or for T or Y --- */
        !          12448:   if ( isline != 0 )			/* abrupt end or T,Y must be a line*/
        !          12449:     if ( ( bgval == dbitval		/* end-of-line if pixel flips to bg*/
        !          12450:            && bgval == dbitplus		/* and bg same as above pixel */
        !          12451:            && bgval == dbitminus )	/* and below (or right and left) */
        !          12452:     ||   ( fgval == dbitplus		/* T or Y if fg same as above pixel*/
        !          12453:            && fgval == dbitminus ) )	/* and below (or right and left) */
        !          12454:       { turn = 0;  goto end_of_job; }	/* so we're at a T or Y */
        !          12455:   /* --- check for turning line --- */
        !          12456:   if ( isline != 0 ) {			/* turning line must be a line */
        !          12457:     if ( fgval == dbitminus )		/* turning down */
        !          12458:       { turn = -turn;  goto end_of_job; } /* so return negative turn */
        !          12459:     else if ( fgval == dbitplus )	/* turning up */
        !          12460:       goto end_of_job; }		/* so return positive turn */
        !          12461:   /* --- check for inside corner at edge --- */
        !          12462:   if ( isedge != 0 ) {			/* inside corner must be a edge */
        !          12463:     if ( isedge < 0 && fgval == bitminus ) /* corner below */
        !          12464:       { turn = -turn;  goto end_of_job; } /* so return negative turn */
        !          12465:     if ( isedge > 0 && fgval == bitplus ) /* corner above */
        !          12466:       goto end_of_job; }		/* so return positive turn */
        !          12467:   /* --- check for abrupt end at edge --- */
        !          12468:   if ( isedge != 0			/* abrupt edge end must be an edge */
        !          12469:   &&   fgval == dbitval )		/* and line must not end */
        !          12470:     if ( (isedge < 0 && bgval == bitplus) /* abrupt end above */
        !          12471:     ||   (isedge > 0 && bgval == bitminus) ) /* or abrupt end below */
        !          12472:       { turn = 0;  goto end_of_job; }	/* so edge ended abruptly */
        !          12473:   /* --- check for outside corner at edge --- */
        !          12474:   if ( isedge != 0			/* outside corner must be a edge */
        !          12475:   &&   bgval == dbitval ) {		/* and line must end */
        !          12476:     if ( isedge > 0 ) turn = -turn;	/* outside turn down from edge above*/
        !          12477:     goto end_of_job; }
        !          12478:   } /* --- end-of-while(1) --- */
        !          12479: /* -------------------------------------------------------------------------
        !          12480: Back to caller with #rows or #cols traversed, and direction of detected turn
        !          12481: -------------------------------------------------------------------------- */
        !          12482: end_of_job:
        !          12483:   if ( msglevel>=99 && msgfp!=NULL )	/* debugging/diagnostic output */
        !          12484:     fprintf(msgfp,"aafollowline> irow,icol,direction=%d,%d,%d, turn=%d\n",
        !          12485:     irow,icol,direction,turn);
        !          12486:   return ( turn );
        !          12487: } /* --- end-of-function aafollowline() --- */
        !          12488: 
        !          12489: 
        !          12490: /* ==========================================================================
        !          12491:  * Function:	aagridnum ( rp, irow, icol )
        !          12492:  * Purpose:	calculates gridnum, 0-511 (see Notes below),
        !          12493:  *		for 3x3 grid centered at irow,icol
        !          12494:  * --------------------------------------------------------------------------
        !          12495:  * Arguments:	rp (I)		raster *  to raster containing
        !          12496:  *				bitmap image (to be anti-aliased)
        !          12497:  *		irow (I)	int containing row, 0...height-1,
        !          12498:  *				at center of 3x3 grid
        !          12499:  *		icol (I)	int containing col, 0...width-1,
        !          12500:  *				at center of 3x3 grid
        !          12501:  * --------------------------------------------------------------------------
        !          12502:  * Returns:	( int )		0-511 grid number, or -1=any error
        !          12503:  * --------------------------------------------------------------------------
        !          12504:  * Notes:     o	Input gridnum is a 9-bit int, 0-511, coding a 3x3 pixel grid
        !          12505:  *		whose bit positions (and corresponding values) in gridnum are
        !          12506:  *		  876     256 128  64
        !          12507:  *		  504  =   32   1  16
        !          12508:  *		  321       8   4   2
        !          12509:  *		Thus, for example (*=pixel set/black, -=pixel not set/white),
        !          12510:  *		  *--         *--	  -**         (note that 209 is the
        !          12511:  *		  -*- = 259   *-- = 302   -** = 209    inverse, set<-->unset,
        !          12512:  *		  --*         ***         ---          of 302)
        !          12513:  *	      o	A set pixel is considered black, an unset pixel considered
        !          12514:  *		white.
        !          12515:  * ======================================================================= */
        !          12516: /* --- entry point --- */
        !          12517: int	aagridnum (raster *rp, int irow, int icol)
        !          12518: {
        !          12519: /* -------------------------------------------------------------------------
        !          12520: Allocations and Declarations
        !          12521: -------------------------------------------------------------------------- */
        !          12522: pixbyte	*bitmap = rp->pixmap;		/* local rp->pixmap ptr */
        !          12523: int	width=rp->width, height=rp->height, /* width, height of raster */
        !          12524: 	imap = icol + irow*width;	/* pixel index = icol + irow*width */
        !          12525: int	bitval=0,			/* value of rp bit at irow,icol */
        !          12526: 	nnbitval=0, nebitval=0, eebitval=0, sebitval=0,	/*adjacent vals*/
        !          12527: 	ssbitval=0, swbitval=0, wwbitval=0, nwbitval=0,	/*compass pt names*/
        !          12528: 	gridnum = (-1);			/* grid# 0-511 for above 9 bits */
        !          12529: /* -------------------------------------------------------------------------
        !          12530: check input
        !          12531: -------------------------------------------------------------------------- */
        !          12532: if ( irow<0 || irow>=height		/* irow out-of-bounds */
        !          12533: ||   icol<0 || icol>=width ) goto end_of_job; /* icol out-of-bounds */
        !          12534: /* -------------------------------------------------------------------------
        !          12535: get the 9 bits comprising the 3x3 grid centered at irow,icol
        !          12536: -------------------------------------------------------------------------- */
        !          12537: /* --- get center bit --- */
        !          12538: bitval = getlongbit(bitmap,imap);	/* value of rp input bit at imap */
        !          12539: /* --- get 8 surrounding bits --- */
        !          12540: if ( irow > 0 )				/* nn (north) bit available */
        !          12541:    nnbitval = getlongbit(bitmap,imap-width); /* nn bit value */
        !          12542: if ( irow < height-1 )			/* ss (south) bit available */
        !          12543:    ssbitval = getlongbit(bitmap,imap+width); /* ss bit value */
        !          12544: if ( icol > 0 )				/* ww (west) bit available */
        !          12545:  { wwbitval = getlongbit(bitmap,imap-1); /* ww bit value */
        !          12546:    if ( irow > 0 )			/* nw bit available */
        !          12547:      nwbitval = getlongbit(bitmap,imap-width-1); /* nw bit value */
        !          12548:    if ( irow < height-1 )		/* sw bit available */
        !          12549:      swbitval = getlongbit(bitmap,imap+width-1); } /* sw bit value */
        !          12550: if ( icol < width-1 )			/* ee (east) bit available */
        !          12551:  { eebitval = getlongbit(bitmap,imap+1); /* ee bit value */
        !          12552:    if ( irow > 0 )			/* ne bit available */
        !          12553:      nebitval = getlongbit(bitmap,imap-width+1); /* ne bit value */
        !          12554:    if ( irow < height-1 )		/* se bit available */
        !          12555:      sebitval = getlongbit(bitmap,imap+width+1); } /* se bit value */
        !          12556: /* --- set gridnum --- */
        !          12557: gridnum = 0;				/* clear all bits */
        !          12558: if (   bitval ) gridnum = 1;		/* set1bit(gridnum,0); */
        !          12559: if ( nwbitval ) gridnum += 256;		/* set1bit(gridnum,8); */
        !          12560: if ( nnbitval ) gridnum += 128;		/* set1bit(gridnum,7); */
        !          12561: if ( nebitval ) gridnum += 64;		/* set1bit(gridnum,6); */
        !          12562: if ( wwbitval ) gridnum += 32;		/* set1bit(gridnum,5); */
        !          12563: if ( eebitval ) gridnum += 16;		/* set1bit(gridnum,4); */
        !          12564: if ( swbitval ) gridnum += 8;		/* set1bit(gridnum,3); */
        !          12565: if ( ssbitval ) gridnum += 4;		/* set1bit(gridnum,2); */
        !          12566: if ( sebitval ) gridnum += 2;		/* set1bit(gridnum,1); */
        !          12567: /* -------------------------------------------------------------------------
        !          12568: Back to caller with gridnum coding 3x3 grid centered at irow,icol
        !          12569: -------------------------------------------------------------------------- */
        !          12570: end_of_job:
        !          12571:   return ( gridnum );
        !          12572: } /* --- end-of-function aagridnum() --- */
        !          12573: 
        !          12574: 
        !          12575: /* ==========================================================================
        !          12576:  * Function:	aapatternnum ( gridnum )
        !          12577:  * Purpose:	Looks up the pattern number 1...51
        !          12578:  *		corresponding to the 3x3 pixel grid coded by gridnum 0=no
        !          12579:  *		pixels set (white) to 511=all pixels set (black).
        !          12580:  * --------------------------------------------------------------------------
        !          12581:  * Arguments:	gridnum (I)	int containing 0-511 coding a 3x3 pixel grid
        !          12582:  *				(see Notes below)
        !          12583:  * --------------------------------------------------------------------------
        !          12584:  * Returns:	( int )		1 to 51, or -1=error
        !          12585:  * --------------------------------------------------------------------------
        !          12586:  * Notes:     o	Input gridnum is a 9-bit int, 0-511, coding a 3x3 pixel grid
        !          12587:  *		whose bit positions (and corresponding values) in gridnum are
        !          12588:  *		  876     256 128  64
        !          12589:  *		  504  =   32   1  16
        !          12590:  *		  321       8   4   2
        !          12591:  *		Thus, for example (*=pixel set/black, -=pixel not set/white),
        !          12592:  *		  *--         *--	  -**         (note that 209 is the
        !          12593:  *		  -*- = 259   *-- = 302   -** = 209    inverse, set<-->unset,
        !          12594:  *		  --*         ***         ---          of 302)
        !          12595:  *	      o	A set pixel is considered black, an unset pixel considered
        !          12596:  *		white.
        !          12597:  *	      o	Ignoring whether the center pixel is set or unset, and
        !          12598:  *		taking rotation, reflection and inversion (set<-->unset)
        !          12599:  *		symmetries into account, there are 32 unique pixel patterns.
        !          12600:  *		If inversions are listed separately, there are 51 patterns.
        !          12601:  *	      o	Here are the 51 unique patterns, with ? always denoting the
        !          12602:  *		undetermined center pixel.  At the upper-left corner of each
        !          12603:  *		pattern is the "pattern index number" assigned to it in this
        !          12604:  *		function. At the upper-right is the pattern's multiplicity,
        !          12605:  *		i.e., the number of different patterns obtained by rotations
        !          12606:  *		and reflection of the illustrated one.  Inverse patters are
        !          12607:  *		illustrated immediately beneath the original (the first three
        !          12608:  *		four-pixel patterns have identical inverses).
        !          12609:  *		-------------------------------------------------------------
        !          12610:  *		No pixels set:
        !          12611:  *		 #1 1 (in this case, 1 signifies that rotation
        !          12612:  *		  ---  and reflection give no different grids)
        !          12613:  *		  -?-
        !          12614:  *		  ---
        !          12615:  *		Inverse, all eight pixels set
        !          12616:  *		 #2 1 (the inverse multiplicity is always the same)
        !          12617:  *		  ***
        !          12618:  *		  *?*
        !          12619:  *		  ***
        !          12620:  *		-------------------------------------------------------------
        !          12621:  *		One pixel set:
        !          12622:  *		 #3 4  #4 4
        !          12623:  *		  *--   -*-
        !          12624:  *		  -?-   -?-
        !          12625:  *		  ---   ---
        !          12626:  *		Inverse, seven pixels set:
        !          12627:  *		 #5 4  #6 4
        !          12628:  *		  -**   *-*
        !          12629:  *		  *?*   *?*
        !          12630:  *		  ***   ***
        !          12631:  *		-------------------------------------------------------------
        !          12632:  *		Two pixels set:
        !          12633:  *		 #7 8  #8 4  #9 8  10 2  11 4  12 2
        !          12634:  *		  **-   *-*   *--   *--   -*-   -*-
        !          12635:  *		  -?-   -?-   -?*   -?-   -?*   -?-
        !          12636:  *		  ---   ---   ---   --*   ---   -*-
        !          12637:  *		Inverse, six pixels set:
        !          12638:  *		#13 8  14 4  15 8  16 2  17 4  18 2
        !          12639:  *		  --*   -*-   -**   -**   *-*   *-*
        !          12640:  *		  *?*   *?*   *?-   *?*   *?-   *?*
        !          12641:  *		  ***   ***   ***   **-   ***   *-*
        !          12642:  *		-------------------------------------------------------------
        !          12643:  *		Three pixels set:
        !          12644:  *		#19 4  20 8  21 8  22 8  23 8  24 4  25 4  26 4  27 4  28 4
        !          12645:  *		  ***   **-   **-   **-   **-   **-   *-*   *-*   -*-   -*-
        !          12646:  *		  -?-   -?*   -?-   -?-   -?-   *?-   -?-   -?-   -?*   -?*
        !          12647:  *		  ---   ---   --*   -*-   *--   ---   --*   -*-   -*-   *--
        !          12648:  *		Inverse, five pixels set:
        !          12649:  *		#29 4  30 8  31 8  32 8  33 8  34 4  35 4  36 4  37 4  38 4
        !          12650:  *		  ---   --*   --*   --*   --*   --*   -*-   -*-   *-*   *-*
        !          12651:  *		  *?*   *?-   *?*   *?*   *?*   -?*   *?*   *?*   *?-   *?-
        !          12652:  *		  ***   ***   **-   *-*   -**   ***   **-   *-*   *-*   -**
        !          12653:  *		-------------------------------------------------------------
        !          12654:  *		Four pixels set (including inverses):
        !          12655:  *		#39 8  40 4  41 8  42 8  43 4  44 4  45 8  46 1
        !          12656:  *		  ***   **-   **-   ***   ***   **-   **-   *-*
        !          12657:  *		  -?*   -?-   -?*   -?-   -?-   -?*   -?*   -?-
        !          12658:  *		  ---   -**   *--   --*   -*-   --*   -*-   *-*
        !          12659:  *
        !          12660:  *		                  #47 8  48 4  49 4  50 8  51 1
        !          12661:  *		                    ---   ---   --*   --*   -*-
        !          12662:  *		                    *?*   *?*   *?-   *?-   *?*
        !          12663:  *		                    **-   *-*   **-   *-*   -*-
        !          12664:  * ======================================================================= */
        !          12665: /* --- entry point --- */
        !          12666: int	aapatternnum ( int gridnum )
        !          12667: {
        !          12668: /* -------------------------------------------------------------------------
        !          12669: Allocations and Declarations
        !          12670: -------------------------------------------------------------------------- */
        !          12671: int	pattern = (-1);			/*pattern#, 1-51, for input gridnum*/
        !          12672: /* ---
        !          12673:  * pattern number corresponding to input gridnum/2 code
        !          12674:  * ( gridnum/2 strips off gridnum's low bit because it's
        !          12675:  * the same pattern whether or not center pixel is set )
        !          12676:  * --- */
        !          12677: static int patternnum[] = {
        !          12678:     1, 3, 4, 7, 3, 8, 7,19, 4, 7,11,24, 9,23,20,39,  /*   0- 15 */
        !          12679:     4, 9,11,20, 7,23,24,39,12,22,27,47,22,48,47,29,  /*  16- 31 */
        !          12680:     3, 8, 9,23,10,25,21,42, 7,19,20,39,21,42,44,34,  /*  32- 47 */
        !          12681:     9,26,28,41,21,50,49,30,22,43,45,33,40,32,31,13,  /*  48- 63 */
        !          12682:     4, 9,12,22, 9,26,22,43,11,20,27,47,28,41,45,33,  /*  64- 79 */
        !          12683:    11,28,27,45,20,41,47,33,27,45,51,35,45,36,35,14,  /*  80- 95 */
        !          12684:     7,23,22,48,21,50,40,32,24,39,47,29,49,30,31,13,  /*  96-111 */
        !          12685:    20,41,45,36,44,38,31,15,47,33,35,14,31,15,16, 5,  /* 112-127 */
        !          12686:     3,10, 9,21, 8,25,23,42, 9,21,28,49,26,50,41,30,  /* 128-143 */
        !          12687:     7,21,20,44,19,42,39,34,22,40,45,31,43,32,33,13,  /* 144-159 */
        !          12688:     8,25,26,50,25,46,50,37,23,42,41,30,50,37,38,17,  /* 160-175 */
        !          12689:    23,50,41,38,42,37,30,17,48,32,36,15,32,18,15, 6,  /* 176-191 */
        !          12690:     7,21,22,40,23,50,48,32,20,44,45,31,41,38,36,15,  /* 192-207 */
        !          12691:    24,49,47,31,39,30,29,13,47,31,35,16,33,15,14, 5,  /* 208-223 */
        !          12692:    19,42,43,32,42,37,32,18,39,34,33,13,30,17,15, 6,  /* 224-239 */
        !          12693:    39,30,33,15,34,17,13, 6,29,13,14, 5,13, 6, 5, 2,  /* 240-255 */
        !          12694:    -1 } ; /* --- end-of-patternnum[] --- */
        !          12695: /* -------------------------------------------------------------------------
        !          12696: look up pattern number for gridnum
        !          12697: -------------------------------------------------------------------------- */
        !          12698: /* --- first check input --- */
        !          12699: if ( gridnum<0 || gridnum>511 ) goto end_of_job; /* gridnum out of bounds */
        !          12700: /* --- look up pattern number, 1-51, corresponding to input gridnum --- */
        !          12701: pattern = patternnum[gridnum/2];	/* /2 strips off gridnum's low bit */
        !          12702: if ( pattern<1 || pattern>51 ) pattern = (-1); /* some internal error */
        !          12703: end_of_job:
        !          12704:   return ( pattern );			/* back to caller with pattern# */
        !          12705: } /* --- end-of-function aapatternnum() --- */
        !          12706: 
        !          12707: 
        !          12708: /* ==========================================================================
        !          12709:  * Function:	aalookup ( gridnum )
        !          12710:  * Purpose:	Looks up the grayscale value 0=white to 255=black
        !          12711:  *		corresponding to the 3x3 pixel grid coded by gridnum 0=no
        !          12712:  *		pixels set (white) to 511=all pixels set (black).
        !          12713:  * --------------------------------------------------------------------------
        !          12714:  * Arguments:	gridnum (I)	int containing 0-511 coding a 3x3 pixel grid
        !          12715:  * --------------------------------------------------------------------------
        !          12716:  * Returns:	( int )		0=white to 255=black, or -1=error
        !          12717:  * --------------------------------------------------------------------------
        !          12718:  * Notes:     o	Input gridnum is a 9-bit int, 0-511, coding a 3x3 pixel grid
        !          12719:  *	      o	A set pixel is considered black, an unset pixel considered
        !          12720:  *		white.  Likewise, the returned grayscale is 255 for black,
        !          12721:  *		0 for white.  You'd more typically want to use 255-grayscale
        !          12722:  *		so that 255 is white and 0 is black.
        !          12723:  *	      o	The returned number is the (lowpass) antialiased grayscale
        !          12724:  *		for the center pixel (gridnum bit 0) of the grid.
        !          12725:  * ======================================================================= */
        !          12726: /* --- entry point --- */
        !          12727: int	aalookup ( int gridnum )
        !          12728: {
        !          12729: /* -------------------------------------------------------------------------
        !          12730: Allocations and Declarations
        !          12731: -------------------------------------------------------------------------- */
        !          12732: int	grayscale = (-1);		/*returned grayscale, init for error*/
        !          12733: int	pattern = (-1), aapatternnum();	/*pattern#, 1-51, for input gridnum*/
        !          12734: int	iscenter = gridnum&1;		/*low-order bit set for center pixel*/
        !          12735: /* --- gray scales --- */
        !          12736: #define	WHT 0
        !          12737: #define	LGT 64
        !          12738: #define	GRY 128
        !          12739: #define	DRK 192
        !          12740: #define	BLK 255
        !          12741: #if 1
        !          12742: /* ---
        !          12743:  * modified aapnm() grayscales (second try)
        !          12744:  * --- */
        !          12745: /* --- grayscale for each pattern when center pixel set/black --- */
        !          12746: static int grayscale1[] = { -1,		/* [0] index not used */
        !          12747:    BLK,BLK,BLK,BLK,242,230,BLK,BLK,BLK,160,	/*  1-10 */
        !          12748: /* BLK,BLK,BLK,BLK,242,230,BLK,BLK,BLK,BLK, */	/*  1-10 */
        !          12749:    BLK,BLK,217,230,217,230,204,BLK,BLK,166,	/* 11-20 */
        !          12750:    BLK,BLK,BLK,BLK,BLK,BLK,178,166,204,191,	/* 21-30 */
        !          12751:    204,BLK,204,191,217,204,BLK,191,178,BLK,	/* 31-40 */
        !          12752:    178,BLK,BLK,178,191,BLK,191,BLK,178,BLK,204,	/* 41-51 */
        !          12753:    -1 } ; /* --- end-of-grayscale1[] --- */
        !          12754: /* --- grayscale for each pattern when center pixel not set/white --- */
        !          12755: static int grayscale0[] = { -1,		/* [0] index not used */
        !          12756:    WHT,WHT,WHT,WHT,WHT,WHT,WHT,WHT,WHT,WHT,	/*  1-10 */
        !          12757:     64,WHT,WHT,128,115,128,WHT,WHT,WHT, 64,	/* 11-20 */
        !          12758:  /* 51,WHT,WHT,128,115,128,WHT,WHT,WHT, 64, */	/* 11-20 */
        !          12759:    WHT,WHT,WHT, 64,WHT,WHT, 76, 64,102, 89,	/* 21-30 */
        !          12760:    102,WHT,102,WHT,115,102,WHT, 89, 76,WHT,	/* 31-40 */
        !          12761:     76,WHT,WHT, 76, 89,WHT, 89,WHT, 76,WHT,102,	/* 41-51 */
        !          12762:    -1 } ; /* --- end-of-grayscale0[] --- */
        !          12763: #endif
        !          12764: #if 0
        !          12765: /* ---
        !          12766:  * modified aapnm() grayscales (first try)
        !          12767:  * --- */
        !          12768: /* --- grayscale for each pattern when center pixel set/black --- */
        !          12769: static int grayscale1[] = { -1,		/* [0] index not used */
        !          12770:    BLK,BLK,BLK,BLK,242,230,GRY,BLK,BLK,BLK,	/*  1-10 */
        !          12771: /* BLK,BLK,BLK,BLK,242,230,BLK,BLK,BLK,BLK, */	/*  1-10 */
        !          12772:    BLK,BLK,217,230,217,230,204,BLK,BLK,166,	/* 11-20 */
        !          12773:    BLK,BLK,BLK,BLK,BLK,BLK,BLK,166,204,191,	/* 21-30 */
        !          12774: /* BLK,BLK,BLK,BLK,BLK,BLK,178,166,204,191, */	/* 21-30 */
        !          12775:    204,BLK,204,BLK,217,204,BLK,191,GRY,BLK,	/* 31-40 */
        !          12776: /* 204,BLK,204,191,217,204,BLK,191,178,BLK, */	/* 31-40 */
        !          12777:    178,BLK,BLK,178,191,BLK,BLK,BLK,178,BLK,204,	/* 41-51 */
        !          12778: /* 178,BLK,BLK,178,191,BLK,191,BLK,178,BLK,204, */ /* 41-51 */
        !          12779:    -1 } ; /* --- end-of-grayscale1[] --- */
        !          12780: /* --- grayscale for each pattern when center pixel not set/white --- */
        !          12781: static int grayscale0[] = { -1,		/* [0] index not used */
        !          12782:    WHT,WHT,WHT,WHT,WHT,WHT,WHT,WHT,WHT,WHT,	/*  1-10 */
        !          12783:    GRY,WHT,WHT,128,115,128,WHT,WHT,WHT,GRY,	/* 11-20 */
        !          12784: /*  51,WHT,WHT,128,115,128,WHT,WHT,WHT, 64, */	/* 11-20 */
        !          12785:    WHT,WHT,WHT,GRY,WHT,WHT, 76, 64,102, 89,	/* 21-30 */
        !          12786: /* WHT,WHT,WHT, 64,WHT,WHT, 76, 64,102, 89, */	/* 21-30 */
        !          12787:    102,WHT,102,WHT,115,102,WHT, 89,GRY,WHT,	/* 31-40 */
        !          12788: /* 102,WHT,102,WHT,115,102,WHT, 89, 76,WHT, */	/* 31-40 */
        !          12789:     76,WHT,WHT,GRY, 89,WHT, 89,WHT, 76,WHT,102,	/* 41-51 */
        !          12790: /*  76,WHT,WHT, 76, 89,WHT, 89,WHT, 76,WHT,102, */ /* 41-51 */
        !          12791:    -1 } ; /* --- end-of-grayscale0[] --- */
        !          12792: #endif
        !          12793: #if 0
        !          12794: /* ---
        !          12795:  * these grayscales _exactly_ correspond to the aapnm() algorithm
        !          12796:  * --- */
        !          12797: /* --- grayscale for each pattern when center pixel set/black --- */
        !          12798: static int grayscale1[] = { -1,		/* [0] index not used */
        !          12799:    BLK,BLK,BLK,BLK,242,230,BLK,BLK,BLK,BLK,	/*  1-10 */
        !          12800:    BLK,BLK,217,230,217,230,204,BLK,BLK,166,	/* 11-20 */
        !          12801:    BLK,BLK,BLK,BLK,BLK,BLK,178,166,204,191,	/* 21-30 */
        !          12802:    204,BLK,204,191,217,204,BLK,191,178,BLK,	/* 31-40 */
        !          12803:    178,BLK,BLK,178,191,BLK,191,BLK,178,BLK,204,	/* 41-51 */
        !          12804:    -1 } ; /* --- end-of-grayscale1[] --- */
        !          12805: /* --- grayscale for each pattern when center pixel not set/white --- */
        !          12806: static int grayscale0[] = { -1,		/* [0] index not used */
        !          12807:    WHT,WHT,WHT,WHT,WHT,WHT,WHT,WHT,WHT,WHT,	/*  1-10 */
        !          12808:     51,WHT,WHT,128,115,128,WHT,WHT,WHT, 64,	/* 11-20 */
        !          12809:    WHT,WHT,WHT, 64,WHT,WHT, 76, 64,102, 89,	/* 21-30 */
        !          12810:    102,WHT,102,WHT,115,102,WHT, 89, 76,WHT,	/* 31-40 */
        !          12811:     76,WHT,WHT, 76, 89,WHT, 89,WHT, 76,WHT,102,	/* 41-51 */
        !          12812:    -1 } ; /* --- end-of-grayscale0[] --- */
        !          12813: #endif
        !          12814: /* -------------------------------------------------------------------------
        !          12815: look up grayscale for gridnum
        !          12816: -------------------------------------------------------------------------- */
        !          12817: /* --- first check input --- */
        !          12818: if ( gridnum<0 || gridnum>511 ) goto end_of_job; /* gridnum out of bounds */
        !          12819: /* --- look up pattern number, 1-51, corresponding to input gridnum --- */
        !          12820: pattern = aapatternnum(gridnum);	/* look up pattern number */
        !          12821: if ( pattern<1 || pattern>51 ) goto end_of_job; /* some internal error */
        !          12822: if ( ispatternnumcount ) {		/* counts being accumulated */
        !          12823:   if (iscenter)	patternnumcount1[pattern] += 1;	/* bump diagnostic count */
        !          12824:   else		patternnumcount0[pattern] += 1; }
        !          12825: /* --- look up grayscale for this pattern --- */
        !          12826: grayscale = ( iscenter? grayscale1[pattern] : grayscale0[pattern] );
        !          12827: end_of_job:
        !          12828:   return ( grayscale );			/* back to caller with grayscale */
        !          12829: } /* --- end-of-function aalookup() --- */
        !          12830: 
        !          12831: 
        !          12832: /* ==========================================================================
        !          12833:  * Function:	aalowpasslookup ( rp, bytemap, grayscale )
        !          12834:  * Purpose:	calls aalookup() for each pixel in rp->bitmap
        !          12835:  *		to create anti-aliased bytemap
        !          12836:  * --------------------------------------------------------------------------
        !          12837:  * Arguments:	rp (I)		raster *  to raster whose bitmap
        !          12838:  *				is to be anti-aliased
        !          12839:  *		bytemap (O)	intbyte * to bytemap, calculated
        !          12840:  *				by calling aalookup() for each pixel
        !          12841:  *				in rp->bitmap
        !          12842:  *		grayscale (I)	int containing number of grayscales
        !          12843:  *				to be calculated, 0...grayscale-1
        !          12844:  *				(should typically be given as 256)
        !          12845:  * --------------------------------------------------------------------------
        !          12846:  * Returns:	( int )		1=success, 0=any error
        !          12847:  * --------------------------------------------------------------------------
        !          12848:  * Notes:    o
        !          12849:  * ======================================================================= */
        !          12850: /* --- entry point --- */
        !          12851: int	aalowpasslookup (raster *rp, intbyte *bytemap, int grayscale)
        !          12852: {
        !          12853: /* -------------------------------------------------------------------------
        !          12854: Allocations and Declarations
        !          12855: -------------------------------------------------------------------------- */
        !          12856: int	width=rp->width, height=rp->height, /* width, height of raster */
        !          12857: 	icol = 0, irow = 0, imap = (-1); /* width, height, bitmap indexes */
        !          12858: int	bgbitval=0 /*, fgbitval=1*/;	/* background, foreground bitval */
        !          12859: int	bitval=0,			/* value of rp bit at irow,icol */
        !          12860: 	aabyteval=0;			/* antialiased (or unchanged) value*/
        !          12861: int	gridnum=0, aagridnum(),		/* grid# for 3x3 grid at irow,icol */
        !          12862: 	aalookup();			/* table look up  antialiased value*/
        !          12863: /* -------------------------------------------------------------------------
        !          12864: generate bytemap by table lookup for each pixel of bitmap
        !          12865: -------------------------------------------------------------------------- */
        !          12866: for ( irow=0; irow<height; irow++ )
        !          12867:  for ( icol=0; icol<width; icol++ )
        !          12868:   {
        !          12869:   /* --- get gridnum and center bit value, init aabyteval --- */
        !          12870:   gridnum = aagridnum(rp,irow,icol);	/*grid# coding 3x3 grid at irow,icol*/
        !          12871:   bitval = (gridnum&1);			/* center bit set if gridnum odd */
        !          12872:   aabyteval = (intbyte)(bitval==bgbitval?0:grayscale-1); /* default aa val */
        !          12873:   imap++;				/* first bump bitmap[] index */  
        !          12874:   bytemap[imap] = (intbyte)(aabyteval);	/* init antialiased pixel */
        !          12875:   /* --- look up antialiased value for this grid --- */
        !          12876:   aabyteval = aalookup(gridnum);	/* look up on grid# */
        !          12877:   if ( aabyteval>=0 && aabyteval<=255 )	/* check for success */
        !          12878:     bytemap[imap] = (intbyte)(aabyteval); /* init antialiased pixel */
        !          12879:   } /* --- end-of-for(irow,icol) --- */
        !          12880: ispatternnumcount = 0;			/* accumulate counts only once */
        !          12881: /* -------------------------------------------------------------------------
        !          12882: Back to caller with gray-scale anti-aliased bytemap
        !          12883: -------------------------------------------------------------------------- */
        !          12884: /*end_of_job:*/
        !          12885:   return ( 1 );
        !          12886: } /* --- end-of-function aalowpasslookup() --- */
        !          12887: 
        !          12888: 
        !          12889: /* ==========================================================================
1.1       albertel 12890:  * Function:	aasupsamp ( rp, aa, sf, grayscale )
                   12891:  * Purpose:	calculates a supersampled anti-aliased bytemap
                   12892:  *		for rp->bitmap, with each byte 0...grayscale-1
                   12893:  * --------------------------------------------------------------------------
                   12894:  * Arguments:	rp (I)		raster *  to raster whose bitmap
                   12895:  *				is to be anti-aliased
                   12896:  *		aa (O)		address of raster * to supersampled bytemap,
                   12897:  *				calculated by supersampling rp->bitmap
                   12898:  *		sf (I)		int containing supersampling shrinkfactor
                   12899:  *		grayscale (I)	int containing number of grayscales
                   12900:  *				to be calculated, 0...grayscale-1
                   12901:  *				(should typically be given as 256)
                   12902:  * --------------------------------------------------------------------------
                   12903:  * Returns:	( int )		1=success, 0=any error
                   12904:  * --------------------------------------------------------------------------
                   12905:  * Notes:     o	If the center point of the box being averaged is black,
                   12906:  *		then the entire "average" is forced black (grayscale-1)
                   12907:  *		regardless of the surrounding points.  This is my attempt
                   12908:  *		to avoid a "washed-out" appearance of thin (one-pixel-wide)
                   12909:  *		lines, which would otherwise turn from black to a gray shade.
                   12910:  * ======================================================================= */
                   12911: /* --- entry point --- */
                   12912: int	aasupsamp (raster *rp, raster **aa, int sf, int grayscale)
                   12913: {
                   12914: /* -------------------------------------------------------------------------
                   12915: Allocations and Declarations
                   12916: -------------------------------------------------------------------------- */
                   12917: int	status = 0;			/* 1=success, 0=failure to caller */
                   12918: int	rpheight=rp->height, rpwidth=rp->width, /*bitmap raster dimensions*/
                   12919: 	heightrem=0, widthrem=0,	/* rp+rem is a multiple of shrinkf */
                   12920: 	aaheight=0,  aawidth=0,		/* supersampled dimensions */
                   12921: 	aapixsz=8;			/* output pixels are 8-bit bytes */
                   12922: int	maxaaval=(-9999),		/* max grayscale val set in matrix */
                   12923: 	isrescalemax=1;			/* 1=rescale maxaaval to grayscale */
                   12924: int	irp=0,jrp=0, iaa=0,jaa=0, iwt=0,jwt=0; /*indexes, i=width j=height*/
                   12925: raster	*aap=NULL, *new_raster();	/* raster for supersampled image */
                   12926: raster	*aaweights();			/* get weight matrix applied to rp */
                   12927: static	raster *aawts = NULL;		/* aaweights() resultant matrix */
                   12928: static	int prevshrink = NOVALUE,	/* shrinkfactor from previous call */
                   12929: 	sumwts = 0;			/* sum of weights */
                   12930: static	int blackfrac = 40,		/* force black if this many pts are */
                   12931: 	/*grayfrac = 20,*/
                   12932: 	maxwt = 10,			/* max weight in weight matrix */
                   12933: 	minwtfrac=10, maxwtfrac=70;	/* force light pts white, dark black*/
                   12934: int	type_raster(), type_bytemap();	/* debugging display routines */
                   12935: int	delete_raster();		/* delete old rasters */
                   12936: /* -------------------------------------------------------------------------
                   12937: Initialization
                   12938: -------------------------------------------------------------------------- */
                   12939: /* --- check args --- */
                   12940: if ( aa == NULL ) goto end_of_job;	/* no ptr for return output arg */
                   12941: *aa = NULL;				/* init null ptr for error return */
                   12942: if ( rp == NULL				/* no ptr to input arg */
                   12943: ||   sf < 1				/* invalid shrink factor */
                   12944: ||   grayscale < 2 ) goto end_of_job;	/* invalid grayscale */
                   12945: /* --- get weight matrix (or use current one) --- */
                   12946: if ( sf != prevshrink )			/* have new shrink factor */
                   12947:   { if ( aawts != NULL )		/* have unneeded weight matrix */
                   12948:       delete_raster(aawts);		/* so free it */
                   12949:     sumwts = 0;				/* reinitialize sum of weights */
                   12950:     aawts = aaweights(sf,sf);		/* get new weight matrix */
                   12951:     if ( aawts != NULL )		/* got weight matrix okay*/
                   12952:       for ( jwt=0; jwt<sf; jwt++ )	/* for each row */
                   12953:        for ( iwt=0; iwt<sf; iwt++ )	/* and each column */
                   12954: 	{ int wt = (int)(getpixel(aawts,jwt,iwt)); /* weight */
                   12955: 	  if ( wt > maxwt )		/* don't overweight center pts */
                   12956: 	    { wt = maxwt;		/* scale it back */
                   12957: 	      setpixel(aawts,jwt,iwt,wt); } /* and replace it in matrix */
                   12958: 	  sumwts += wt; }		/* add weight to sum */
                   12959:     prevshrink = sf; }			/* save new shrink factor */
                   12960: if ( msgfp!=NULL && msglevel>=999 )
                   12961:   { fprintf(msgfp,"aasupsamp> sf=%d, sumwts=%d weights=...\n", sf,sumwts);
                   12962:     type_bytemap((intbyte *)aawts->pixmap,grayscale,
                   12963:     aawts->width,aawts->height,msgfp); }
                   12964: /* --- calculate supersampled height,width and allocate output raster */
                   12965: heightrem = rpheight%sf;		/* remainder after division... */
                   12966: widthrem  = rpwidth%sf;			/* ...by shrinkfactor */
                   12967: aaheight  = 1+(rpheight+sf-(heightrem+1))/sf; /* ss height */
                   12968: aawidth   = 1+(rpwidth+sf-(widthrem+1))/sf; /* ss width */
                   12969: if ( msgfp!=NULL && msglevel>=999 )
                   12970:  { fprintf(msgfp,"aasupsamp> rpwid,ht=%d,%d wd,htrem=%d,%d aawid,ht=%d,%d\n",
                   12971:    rpwidth,rpheight, widthrem,heightrem, aawidth,aaheight);
                   12972:    fprintf(msgfp,"aasupsamp> dump of original bitmap image...\n");
                   12973:    type_raster(rp,msgfp); }		/* ascii image of rp raster */
                   12974: if ( (aap = new_raster(aawidth,aaheight,aapixsz)) /* alloc output raster*/
                   12975: ==   NULL ) goto end_of_job;		/* quit if alloc fails */
                   12976: /* -------------------------------------------------------------------------
                   12977: Step through rp->bitmap, applying aawts to each "submatrix" of bitmap
                   12978: -------------------------------------------------------------------------- */
                   12979: for ( jaa=0,jrp=(-(heightrem+1)/2); jrp<rpheight; jrp+=sf ) /* height */
                   12980:  {
                   12981:  for ( iaa=0,irp=(-(widthrem+1)/2); irp<rpwidth; irp+=sf ) /* width */
                   12982:   {
                   12983:   int aaval=0;				/* weighted rpvals */
                   12984:   int nrp=0, mrp=0;			/* #rp bits set, #within matrix */
                   12985:   for ( jwt=0; jwt<sf; jwt++ )
                   12986:    for ( iwt=0; iwt<sf; iwt++ )
                   12987:     {
                   12988:     int i=irp+iwt, j=jrp+jwt;		/* rp->pixmap point */
                   12989:     int rpval = 0;			/* rp->pixmap value at i,j */
                   12990:     if ( i>=0 && i<rpwidth		/* i within actual pixmap */
                   12991:     &&   j>=0 && j<rpheight )		/* ditto j */
                   12992:       {	mrp++;				/* count another bit within matrix */
                   12993: 	rpval = (int)(getpixel(rp,j,i)); } /* get actual pixel value */
                   12994:     if ( rpval != 0 )
                   12995:       {	nrp++;				/* count another bit set */
                   12996: 	aaval += (int)(getpixel(aawts,jwt,iwt)); } /* sum weighted vals */
                   12997:     } /* --- end-of-for(iwt,jwt) --- */
                   12998:   if ( aaval > 0 )			/*normalize and rescale non-zero val*/
                   12999:     { int aafrac = (100*aaval)/sumwts;	/* weighted percent of black points */
                   13000:       /*if((100*nrp)/mrp >=blackfrac)*/	/* many black interior pts */
                   13001:       if( aafrac >= maxwtfrac )		/* high weight of sampledblack pts */
                   13002: 	aaval = grayscale-1;		/* so set supersampled pt black */
                   13003:       else if( aafrac <= minwtfrac )	/* low weight of sampledblack pts */
                   13004: 	aaval = 0;			/* so set supersampled pt white */
                   13005:       else				/* rescale calculated weight */
                   13006: 	aaval = ((sumwts/2 - 1) + (grayscale-1)*aaval)/sumwts; }
                   13007:   maxaaval = max2(maxaaval,aaval);	/* largest aaval so far */
                   13008:   if ( msgfp!=NULL && msglevel>=999 )
                   13009:     fprintf(msgfp,"aasupsamp> jrp,irp=%d,%d jaa,iaa=%d,%d"
                   13010:     " mrp,nrp=%d,%d aaval=%d\n",
                   13011:     jrp,irp, jaa,iaa, mrp,nrp, aaval);
                   13012:   if ( jaa<aaheight && iaa<aawidth )	/* bounds check */
                   13013:     setpixel(aap,jaa,iaa,aaval);	/*weighted val in supersamp raster*/
                   13014:   else if( msgfp!=NULL && msglevel>=9 )	/* emit error if out-of-bounds */
                   13015:     fprintf(msgfp,"aasupsamp> Error: aaheight,aawidth=%d,%d jaa,iaa=%d,%d\n",
                   13016:     aaheight,aawidth, jaa,iaa);
                   13017:   iaa++;				/* bump aa col index */
                   13018:   } /* --- end-of-for(irp) --- */
                   13019:  jaa++;					/* bump aa row index */
                   13020:  } /* --- end-of-for(jrp) --- */
                   13021: /* --- rescale supersampled image so darkest points become black --- */
                   13022: if ( isrescalemax )			/* flag set to rescale maxaaval */
                   13023:   {
                   13024:   double scalef = ((double)(grayscale-1))/((double)maxaaval);
                   13025:   for ( jaa=0; jaa<aaheight; jaa++ )	/* height */
                   13026:    for ( iaa=0; iaa<aawidth; iaa++ )	/* width */
                   13027:     { int aafrac, aaval = getpixel(aap,jaa,iaa); /* un-rescaled value */
                   13028:       aaval = (int)(0.5+((double)aaval)*scalef); /*multiply by scale factor*/
                   13029:       aafrac = (100*aaval)/(grayscale-1); /* fraction of blackness */
                   13030:       if( aafrac >= blackfrac )		/* high weight of sampledblack pts */
                   13031: 	aaval = grayscale-1;		/* so set supersampled pt black */
                   13032:       else if( 0&&aafrac <= minwtfrac )	/* low weight of sampledblack pts */
                   13033: 	aaval = 0;			/* so set supersampled pt white */
                   13034:       setpixel(aap,jaa,iaa,aaval); }	/* replace rescaled val in raster */
                   13035:   } /* --- end-of-if(isrescalemax) --- */
                   13036: *aa = aap;				/* return supersampled image*/
                   13037: status = 1;				/* set successful status */
                   13038: if ( msgfp!=NULL && msglevel>=999 )
                   13039:   { fprintf(msgfp,"aasupsamp> anti-aliased image...\n");
                   13040:     type_bytemap((intbyte *)aap->pixmap,grayscale,
                   13041:     aap->width,aap->height,msgfp);  fflush(msgfp); }
                   13042: /* -------------------------------------------------------------------------
                   13043: Back to caller with gray-scale anti-aliased bytemap
                   13044: -------------------------------------------------------------------------- */
                   13045: end_of_job:
                   13046:   return ( status );
                   13047: } /* --- end-of-function aasupsamp() --- */
                   13048: 
                   13049: 
                   13050: /* ==========================================================================
                   13051:  * Function:	aacolormap ( bytemap, nbytes, colors, colormap )
                   13052:  * Purpose:	searches bytemap, returning a list of its discrete values
                   13053:  *		in ascending order in colors[], and returning an "image"
                   13054:  *		of bytemap (where vales are replaced by colors[]
                   13055:  *		indexes) in colormap[].
                   13056:  * --------------------------------------------------------------------------
                   13057:  * Arguments:	bytemap (I)	intbyte *  to bytemap containing
                   13058:  *				grayscale values (usually 0=white
                   13059:  *				through 255=black) for which colors[]
                   13060:  *				and colormap[] will be constructed.
                   13061:  *		nbytes (I)	int containing #bytes in bytemap
                   13062:  *				(usually just #rows * #cols)
                   13063:  *		colors (O)	intbyte *  (to be interpreted as ints)
                   13064:  *				returning a list of the discrete/different
1.3     ! albertel 13065:  *				values in bytemap, in ascending value order,
        !          13066:  *				and with gamma correction applied
1.1       albertel 13067:  *		colormap (O)	intbyte *  returning a bytemap "image",
                   13068:  *				i.e., in one-to-one pixel correspondence
                   13069:  *				with bytemap, but where the values have been
                   13070:  *				replaced with corresponding colors[] indexes.
                   13071:  * --------------------------------------------------------------------------
                   13072:  * Returns:	( int )		#colors in colors[], or 0 for any error
                   13073:  * --------------------------------------------------------------------------
                   13074:  * Notes:     o
                   13075:  * ======================================================================= */
                   13076: /* --- entry point --- */
                   13077: int	aacolormap ( intbyte *bytemap, int nbytes,
                   13078: 			intbyte *colors, intbyte *colormap )
                   13079: {
                   13080: /* -------------------------------------------------------------------------
                   13081: Allocations and Declarations
                   13082: -------------------------------------------------------------------------- */
                   13083: int	ncolors = 0,			/* #different values in bytemap */
                   13084: 	igray, grayscale = 256;		/* bytemap contains intbyte's */
                   13085: intbyte	*bytevalues = NULL;		/* 1's where bytemap contains value*/
                   13086: int	ibyte;				/* bytemap/colormap index */
1.3     ! albertel 13087: int	isscale = 0,			/* true to scale largest val to 255*/
        !          13088: 	isgamma = 1;			/* true to apply gamma correction */
1.1       albertel 13089: int	maxcolors = 0;			/* maximum ncolors */
                   13090: /* -------------------------------------------------------------------------
                   13091: Accumulate colors[] from values occurring in bytemap
                   13092: -------------------------------------------------------------------------- */
                   13093: /* --- initialization --- */
                   13094: if ( (bytevalues = (intbyte *)malloc(grayscale)) /*alloc bytevalues*/
                   13095: ==   NULL ) goto end_of_job;		/* signal error if malloc() failed */
                   13096: memset(bytevalues,0,grayscale);		/* zero out bytevalues */
                   13097: /* --- now set 1's at offsets corresponding to values found in bytemap --- */
                   13098: for ( ibyte=0; ibyte<nbytes; ibyte++ )	/* for each byte in bytemap */
                   13099:   bytevalues[(int)bytemap[ibyte]] = 1;	/*use its value to index bytevalues*/
                   13100: /* --- collect the 1's indexes in colors[] --- */
                   13101: for ( igray=0; igray<grayscale; igray++ ) /* check all possible values */
                   13102:   if ( (int)bytevalues[igray] )		/*bytemap contains igray somewheres*/
                   13103:     { colors[ncolors] = (intbyte)igray;	/* so store igray in colors */
                   13104:       bytevalues[igray] = (intbyte)ncolors; /* save colors[] index */
                   13105:       if ( maxcolors>0 && ncolors>=maxcolors ) /* too many color indexes */
                   13106:         bytevalues[igray] = (intbyte)(maxcolors-1); /*so scale back to max*/
                   13107:       ncolors++; }			/* and bump #colors */
                   13108: /* --- rescale colors so largest, colors[ncolors-1], is black --- */
                   13109: if ( isscale )				/* only rescale if requested */
                   13110:  if ( ncolors > 1 )			/* and if not a "blank" raster */
                   13111:   if ( colors[ncolors-1] > 0 )		/*and at least one pixel non-white*/
                   13112:    {
                   13113:    /* --- multiply each colors[] by factor that scales largest to 255 --- */
                   13114:    double scalefactor = ((double)(grayscale-1))/((double)colors[ncolors-1]);
                   13115:    for ( igray=1; igray<ncolors; igray++ ) /* re-scale each colors[] */
                   13116:     { colors[igray] = min2(grayscale-1,(int)(scalefactor*colors[igray]+0.5));
                   13117:       if (igray>5) colors[igray] = min2(grayscale-1,colors[igray]+2*igray); }
                   13118:    } /* --- end-of-if(isscale) --- */
1.3     ! albertel 13119: /* --- apply gamma correction --- */
        !          13120: if ( isgamma				/* only gamma correct if requested */
        !          13121: &&   gammacorrection > 0.0001 )		/* and if we have gamma correction */
        !          13122:  if ( ncolors > 1 )			/* and if not a "blank" raster */
        !          13123:   if ( colors[ncolors-1] > 0 )		/*and at least one pixel non-white*/
        !          13124:    {
        !          13125:    for ( igray=1; igray<ncolors; igray++ ) { /*gamma correct each colors[]*/
        !          13126:     int	grayval = colors[igray],	/* original 0=white to 255=black */
        !          13127: 	gmax = grayscale-1;		/* should be 255 */
        !          13128:     double dgray=((double)(gmax-grayval))/((double)gmax); /*0=black 1=white*/
        !          13129:     dgray = pow(dgray,(1.0/gammacorrection)); /* apply gamma correction */
        !          13130:     grayval = (int)( gmax*(1.0-dgray) + 0.5 ); /* convert back to grayval */
        !          13131:     colors[igray] = grayval; }		/* store back in colors[] */
        !          13132:    } /* --- end-of-if(isgamma) --- */
1.1       albertel 13133: /* -------------------------------------------------------------------------
                   13134: Construct colormap
                   13135: -------------------------------------------------------------------------- */
                   13136: for ( ibyte=0; ibyte<nbytes; ibyte++ )	/* for each byte in bytemap */
                   13137:   colormap[ibyte] = bytevalues[(int)bytemap[ibyte]]; /*index for this value*/
                   13138: /* -------------------------------------------------------------------------
                   13139: back to caller with #colors, or 0 for any error
                   13140: -------------------------------------------------------------------------- */
                   13141: end_of_job:
                   13142:   if ( bytevalues != NULL ) free(bytevalues); /* free working memory */
                   13143:   if ( maxcolors>0 && ncolors>maxcolors ) /* too many color indexes */
                   13144:     ncolors = maxcolors;		/* return maximum to caller */
                   13145:   return ( ncolors );			/* back with #colors, or 0=error */
                   13146: } /* --- end-of-function aacolormap() --- */
                   13147: 
                   13148: 
                   13149: /* ==========================================================================
                   13150:  * Function:	aaweights ( width, height )
                   13151:  *		Builds "canonical" weight matrix, width x height, in a raster
                   13152:  *		(see Notes below for discussion).
                   13153:  * --------------------------------------------------------------------------
                   13154:  * Arguments:	width (I)	int containing width (#cols) of returned
                   13155:  *				raster/matrix of weights
                   13156:  *		height (I)	int containing height (#rows) of returned
                   13157:  *				raster/matrix of weights
                   13158:  * --------------------------------------------------------------------------
                   13159:  * Returns:	( raster * )	ptr to raster containing width x height
                   13160:  *				weight matrix, or NULL for any error
                   13161:  * --------------------------------------------------------------------------
                   13162:  * Notes:     o For example, given width=7, height=5, builds the matrix
                   13163:  *			1 2 3  4 3 2 1
                   13164:  *			2 4 6  8 6 4 2
                   13165:  *			3 6 9 12 9 6 3
                   13166:  *			2 4 6  8 6 4 2
                   13167:  *			1 2 3  4 3 2 1
                   13168:  *		If an even dimension given, the two center numbers stay
                   13169:  *		the same, e.g., 123321 for the top row if width=6.
                   13170:  *	      o	For an odd square n x n matrix, the sum of all n^2
                   13171:  *		weights will be ((n+1)/2)^4.
                   13172:  *	      o	The largest weight (in the allocated pixsz=8 raster) is 255,
                   13173:  *		so the largest square matrix is 31 x 31.  Any weight that
                   13174:  *		tries to grow beyond 255 is held constant at 255.
                   13175:  *	      o	A new_raster(), pixsz=8, is allocated for the caller.
                   13176:  *		To avoid memory leaks, be sure to delete_raster() when done.
                   13177:  * ======================================================================= */
                   13178: /* --- entry point --- */
                   13179: raster	*aaweights ( int width, int height )
                   13180: {
                   13181: /* -------------------------------------------------------------------------
                   13182: Allocations and Declarations
                   13183: -------------------------------------------------------------------------- */
                   13184: raster	*new_raster(), *weights=NULL;	/* raster of weights returned */
                   13185: int	irow=0, icol=0,			/* height, width indexes */
                   13186: 	weight = 0;			/*running weight, as per Notes above*/
                   13187: /* -------------------------------------------------------------------------
                   13188: Initialization
                   13189: -------------------------------------------------------------------------- */
                   13190: /* --- allocate raster for weights --- */
                   13191: if ( (weights = new_raster(width,height,8)) /* allocate 8-bit byte raster */
                   13192: ==  NULL ) goto end_of_job;		/* return NULL error if failed */
                   13193: /* -------------------------------------------------------------------------
                   13194: Fill weight matrix, as per Notes above
                   13195: -------------------------------------------------------------------------- */
                   13196: for ( irow=0; irow<height; irow++ )	/* outer loop over rows */
                   13197:   for ( icol=0; icol<width; icol++ )	/* inner loop over cols */
                   13198:     {
                   13199:     int	jrow = height-irow-1,		/* backwards irow, height-1,...,0 */
                   13200: 	jcol =  width-icol-1;		/* backwards icol,  width-1,...,0 */
                   13201:     weight = min2(irow+1,jrow+1) * min2(icol+1,jcol+1); /* weight */
                   13202:     if ( aaalgorithm == 1 ) weight=1;	/* force equal weights */
                   13203:     setpixel(weights,irow,icol,min2(255,weight)); /*store weight in matrix*/
                   13204:     } /* --- end-of-for(irow,icol) --- */
                   13205: end_of_job:
                   13206:   return ( weights );			/* back with weights or NULL=error */
                   13207: } /* --- end-of-function aaweights() --- */
                   13208: 
                   13209: 
                   13210: /* ==========================================================================
                   13211:  * Function:	aawtpixel ( image, ipixel, weights, rotate )
                   13212:  * Purpose:	Applies matrix of weights to the pixels
                   13213:  *		surrounding ipixel in image, rotated clockwise
                   13214:  *		by rotate degrees (typically 0 or 30).
                   13215:  * --------------------------------------------------------------------------
                   13216:  * Arguments:	image (I)	raster * to bitmap (though it can be bytemap)
                   13217:  *				containing image with pixels to be averaged.
                   13218:  *		ipixel (I)	int containing index (irow*width+icol) of
                   13219:  *				center pixel of image for weighted average.
                   13220:  *		weights (I)	raster * to bytemap of relative weights
                   13221:  *				(0-255), whose dimensions (usually odd width
                   13222:  *				and odd height) determine the "subgrid" of
                   13223:  *				image surrounding ipixel to be averaged.
                   13224:  *		rotate (I)	int containing degrees clockwise rotation
                   13225:  *				(typically 0 or 30), i.e., imagine weights
                   13226:  *				rotated clockwise and then averaging the
                   13227:  *				image pixels "underneath" it now.
                   13228:  * --------------------------------------------------------------------------
                   13229:  * Returns:	( int )		0-255 weighted average, or -1 for any error
                   13230:  * --------------------------------------------------------------------------
                   13231:  * Notes:     o	The rotation matrix used (when requested) is
                   13232:  *		    / x' \     / cos(theta)  sin(theta)/a \  / x \
                   13233:  *		    |    |  =  |                          |  |   |
                   13234:  *                  \ y' /     \ -a*sin(theta) cos(theta) /  \ y /
                   13235:  *		where a=1 (current default) is the pixel (not screen)
                   13236:  *		aspect ratio width:height, and theta is rotate (converted
                   13237:  *		from degrees to radians).  Then x=col,y=row are the integer
                   13238:  *		pixel coords relative to the input center ipixel, and
                   13239:  *		x',y' are rotated coords which aren't necessarily integer.
                   13240:  *		The actual pixel used is the one nearest x',y'.
                   13241:  * ======================================================================= */
                   13242: /* --- entry point --- */
                   13243: int	aawtpixel ( raster *image, int ipixel, raster *weights, int rotate )
                   13244: {
                   13245: /* -------------------------------------------------------------------------
                   13246: Allocations and Declarations
                   13247: -------------------------------------------------------------------------- */
                   13248: int	aaimgval = 0,			/* weighted avg returned to caller */
                   13249: 	totwts=0, sumwts=0;		/* total of all wts, sum wts used */
                   13250: int	pixsz = image->pixsz,		/* #bits per image pixel */
                   13251: 	black1=1, black8=255,		/* black for 1-bit, 8-bit pixels */
                   13252: 	black = (pixsz==1? black1:black8), /* black value for our image */
                   13253: 	scalefactor = (black1+black8-black), /* only scale 1-bit images */
                   13254: 	iscenter = 0;			/* set true if center pixel black */
                   13255: /* --- grid dimensions and indexes --- */
                   13256: int	wtheight  = weights->height,	/* #rows in weight matrix */
                   13257: 	wtwidth   = weights->width,	/* #cols in weight matrix */
                   13258: 	imgheight =   image->height,	/* #rows in image */
                   13259: 	imgwidth  =   image->width;	/* #cols in image */
                   13260: int	wtrow,  wtrow0 = wtheight/2,	/* center row index for weights */
                   13261: 	wtcol,  wtcol0 = wtwidth/2,	/* center col index for weights */
                   13262: 	imgrow, imgrow0= ipixel/imgwidth, /* center row index for ipixel */
                   13263: 	imgcol, imgcol0= ipixel-(imgrow0*imgwidth); /*center col for ipixel*/
                   13264: /* --- rotated grid variables --- */
                   13265: static	int prevrotate = 0;		/* rotate from previous call */
                   13266: static	double costheta = 1.0,		/* cosine for previous rotate */
                   13267: 	sintheta = 0.0;			/* and sine for previous rotate */
                   13268: double	a = 1.0;			/* default aspect ratio */
                   13269: /* -------------------------------------------------------------------------
                   13270: Initialization
                   13271: -------------------------------------------------------------------------- */
                   13272: /* --- refresh trig functions for rotate when it changes --- */
                   13273: if ( rotate != prevrotate )		/* need new sine/cosine */
                   13274:   { costheta = cos(((double)rotate)/57.29578);	/*cos of rotate in radians*/
                   13275:     sintheta = sin(((double)rotate)/57.29578);	/*sin of rotate in radians*/
                   13276:     prevrotate = rotate; }		/* save current rotate as prev */
                   13277: /* -------------------------------------------------------------------------
                   13278: Calculate aapixel as weighted average over image points around ipixel
                   13279: -------------------------------------------------------------------------- */
                   13280: for ( wtrow=0; wtrow<wtheight; wtrow++ )
                   13281:  for ( wtcol=0; wtcol<wtwidth; wtcol++ )
                   13282:   {
                   13283:   /* --- allocations and declarations --- */
                   13284:   int	wt = (int)getpixel(weights,wtrow,wtcol); /* weight for irow,icol */
                   13285:   int	drow = wtrow - wtrow0,		/* delta row offset from center */
                   13286: 	dcol = wtcol - wtcol0;		/* delta col offset from center */
                   13287:   int	iscenter = 0;			/* set true if center point black */
                   13288:   /* --- initialization --- */
                   13289:   totwts += wt;				/* sum all weights */
                   13290:   /* --- rotate (if requested) --- */
                   13291:   if ( rotate != 0 )			/* non-zero rotation */
                   13292:     {
                   13293:     /* --- apply rotation matrix to (x=dcol,y=drow) --- */
                   13294:     double dx=(double)dcol, dy=(double)drow, dtemp; /* need floats */
                   13295:     dtemp = dx*costheta + dy*sintheta/a; /* save new dx' */
                   13296:     dy = -a*dx*sintheta + dy*costheta;	/* dy becomes new dy' */
                   13297:     dx = dtemp;				/* just for notational convenience */
                   13298:     /* --- replace original (drow,dcol) with nearest rotated point --- */
                   13299:     drow = (int)(dy+0.5);		/* round dy for nearest row */
                   13300:     dcol = (int)(dx+0.5);		/* round dx for nearest col */
                   13301:     } /* --- end-of-if(rotate!=0) --- */
                   13302:   /* --- select image pixel to be weighted --- */
                   13303:   imgrow = imgrow0 + drow;		/*apply displacement to center row*/
                   13304:   imgcol = imgcol0 + dcol;		/*apply displacement to center col*/
                   13305:   /* --- if pixel in bounds, accumulate weighted average --- */
                   13306:   if ( imgrow>=0 && imgrow<imgheight )	/* row is in bounds */
                   13307:    if ( imgcol>=0 && imgcol<imgwidth )	/* and col is in bounds */
                   13308:     {
                   13309:     /* --- accumulate weighted average --- */
                   13310:     int imgval = (int)getpixel(image,imgrow,imgcol); /* image value */
                   13311:     aaimgval += wt*imgval;		/* weighted sum of image values */
                   13312:     sumwts += wt;			/* and also sum weights used */
                   13313:     /* --- check if center image pixel black --- */
                   13314:     if ( drow==0 && dcol==0 )		/* this is center ipixel */
                   13315:       if ( imgval==black )		/* and it's black */
                   13316: 	iscenter = 1;			/* so set black center flag true */
                   13317:     } /* --- end-of-if(bounds checks ok) --- */
                   13318:   } /* --- end-of-for(irow,icol) --- */
                   13319: if ( 0 && iscenter )			/* center point is black */
                   13320:   aaimgval = black8;			/* so force average black */
                   13321: else					/* center point not black */
                   13322:   aaimgval =				/* 0=white ... black */
                   13323:       ((totwts/2 - 1) + scalefactor*aaimgval)/totwts; /* not /sumwts; */
                   13324: /*end_of_job:*/
                   13325:   return ( aaimgval );
                   13326: } /* --- end-of-function aawtpixel() --- */
1.3     ! albertel 13327: 
        !          13328: 
        !          13329: /* ==========================================================================
        !          13330:  * Function:	mimetexsetmsg ( newmsglevel, newmsgfp )
        !          13331:  * Purpose:	Sets msglevel and msgfp, usually called from
        !          13332:  *		an external driver (i.e., DRIVER not defined
        !          13333:  *		in this module).
        !          13334:  * --------------------------------------------------------------------------
        !          13335:  * Arguments:	newmsglevel (I)	int containing new msglevel
        !          13336:  *				(unchanged if newmsglevel<0)
        !          13337:  *		newmsgfp (I)	FILE * containing new msgfp
        !          13338:  *				(unchanged if newmsgfp=NULL)
        !          13339:  * --------------------------------------------------------------------------
        !          13340:  * Returns:	( int )		always 1
        !          13341:  * --------------------------------------------------------------------------
        !          13342:  * Notes:     o
        !          13343:  * ======================================================================= */
        !          13344: /* --- entry point --- */
        !          13345: int	mimetexsetmsg ( int newmsglevel, FILE *newmsgfp )
        !          13346: {
        !          13347: /* -------------------------------------------------------------------------
        !          13348: set msglevel and msgfp
        !          13349: -------------------------------------------------------------------------- */
        !          13350: if ( newmsglevel >= 0 ) msglevel = newmsglevel;
        !          13351: if ( newmsgfp != NULL ) msgfp = newmsgfp;
        !          13352: return ( 1 );
        !          13353: } /* --- end-of-function mimetexsetmsg() --- */
1.1       albertel 13354: #endif /* PART3 */
                   13355: 
                   13356: /* ---
                   13357:  * PART1
                   13358:  * ------ */
                   13359: #if !defined(PARTS) || defined(PART1)
                   13360: #ifdef DRIVER
                   13361: /* ==========================================================================
                   13362:  * Function:	main() driver for mimetex.c
                   13363:  * Purpose:	emits a mime xbitmap or gif image of a LaTeX math expression
                   13364:  *		entered either as
                   13365:  *		    (1)	html query string from a browser (most typical), or
                   13366:  *		    (2)	a query string from an html <form method="get">
                   13367:  *			whose <input name="formdata"> (mostly for demo), or
                   13368:  *		    (3)	command-line arguments (mostly to test).
                   13369:  *		If no input supplied, expression defaults to "f(x)=x^2",
                   13370:  *		treated as test (input method 3).
                   13371:  *		   If args entered on command-line (or if no input supplied),
                   13372:  *		output is (usually) human-viewable ascii raster images on
                   13373:  *		stdout rather than the usual mime xbitmaps or gif images.
                   13374:  * --------------------------------------------------------------------------
                   13375:  * Command-Line Arguments:
                   13376:  *		When running mimeTeX from the command-line, rather than
                   13377:  *		from a browser, syntax is
                   13378:  *		     ./mimetex	[-d ]		dump gif to stdout
                   13379:  *				[expression	expression, e.g., x^2+y^2,
                   13380:  *				|-f input_file]	or read expression from file
                   13381:  *				[-m msglevel]	verbosity of debugging output
                   13382:  *				[-s fontsize]	default fontsize, 0-5
                   13383:  *		-d   Rather than ascii debugging output, mimeTeX dumps the
                   13384:  *		     actual gif (or xbitmap) to stdout, e.g.,
                   13385:  *			./mimetex  -d  x^2+y^2  > expression.gif
                   13386:  *		     creates a gif file containing an image of x^2+y^2
                   13387:  *		-f   Reads expression from input_file, and automatically
                   13388:  *		     assumes -d switch.  The input_file may contain the
                   13389:  *		     expression on one line or spread out over many lines.
                   13390:  *		     MimeTeX will concatanate all lines from input_file
                   13391:  *		     to construct one long expression.  Blanks, tabs, and
                   13392:  *		     newlines will just be ignored.
                   13393:  *		-m   0-99, controls verbosity level for debugging output
                   13394:  *		     (usually used only while testing code).
                   13395:  *		-s   Font size, 0-5.  As usual, the font size can
                   13396:  *		     also be specified in the expression by a leading
                   13397:  *		     preamble terminated by $, e.g., 3$f(x)=x^2 displays
                   13398:  *		     f(x)=x^2 at font size 3.  Default font size is 2.
                   13399:  * --------------------------------------------------------------------------
                   13400:  * Exits:	0=success, 1=some error
                   13401:  * --------------------------------------------------------------------------
                   13402:  * Notes:     o For an executable that emits mime xbitmaps, compile as
                   13403:  *		     cc -DXBITMAP mimetex.c -lm -o mimetex.cgi
                   13404:  *		or, alternatively, for an executable that emits gif images
                   13405:  *		     cc -DGIF mimetex.c gifsave.c -lm -o mimetex.cgi
                   13406:  *		or for gif images with anti-aliasing
                   13407:  *		     cc -DGIF -DAA mimetex.c gifsave.c -lm -o mimetex.cgi
                   13408:  *		See Notes at top of file for other compile-line -D options.
                   13409:  *	      o	Move executable to your cgi-bin directory and either
                   13410:  *		point your browser to it directly in the form
                   13411:  *		     http://www.yourdomain.com/cgi-bin/mimetex.cgi?3$f(x)=x^2
                   13412:  *		or put a tag in your html document of the form
                   13413:  *		     <img src="../cgi-bin/mimetex.cgi?3$f(x)=x^2"
                   13414:  *		       border=0 align=absmiddle>
                   13415:  *		where f(x)=x^2 (or any other expression) will be displayed
                   13416:  *		either as a mime xbitmap or gif image (as per -D flag).
                   13417:  * ======================================================================= */
                   13418: 
                   13419: /* -------------------------------------------------------------------------
                   13420: header files and other data
                   13421: -------------------------------------------------------------------------- */
                   13422: /* --- (additional) standard headers --- */
                   13423: /* --- other data --- */
                   13424: #ifdef DUMPENVIRON
                   13425:  extern	char **environ;			/* environment information */
                   13426: #endif
                   13427: 
                   13428: /* -------------------------------------------------------------------------
                   13429: globals for gif and png callback functions
                   13430: -------------------------------------------------------------------------- */
                   13431: GLOBAL(raster,*bitmap_raster,NULL);	/* use 0/1 bitmap image or */
                   13432: GLOBAL(intbyte,*colormap_raster,NULL);	/* anti-aliased color indexes */
                   13433: /* --- anti-aliasing flags (needed by GetPixel() as well as main()) --- */
                   13434: #ifdef AA				/* if anti-aliasing requested */
                   13435:   #define ISAAVALUE 1			/* turn flag on */
                   13436: #else
                   13437:   #define ISAAVALUE 0			/* else turn flag off */
                   13438: #endif
                   13439: GLOBAL(int,isaa,ISAAVALUE);		/* set anti-aliasing flag */
                   13440: 
                   13441: /* -------------------------------------------------------------------------
                   13442: logging data structure, and default data to be logged
                   13443: -------------------------------------------------------------------------- */
                   13444: /* --- logging data structure --- */
                   13445: #define	logdata	struct logdata_struct	/* "typedef" for logdata_struct*/
                   13446: logdata
                   13447:   {
                   13448:   /* -----------------------------------------------------------------------
                   13449:   environment variable name, max #chars to display, min msglevel to display
                   13450:   ------------------------------------------------------------------------ */
                   13451:   char	*name;				/* environment variable name */
                   13452:   int	maxlen;				/* max #chars to display */
                   13453:   int	msglevel;			/* min msglevel to display data */
                   13454:   } ; /* --- end-of-logdata_struct --- */
                   13455: /* --- data logged by mimeTeX --- */
                   13456: STATIC logdata mimelog[]
                   13457: #ifdef INITVALS
                   13458:   =
                   13459:   {
                   13460:   /* ------ variable ------ maxlen msglevel ----- */
                   13461:     { "QUERY_STRING",         999,    4 },
                   13462:     { "REMOTE_ADDR",          999,    3 },
                   13463:     { "HTTP_REFERER",         999,    3 },
                   13464:     { "REQUEST_URI",          999,    5 },
                   13465:     { "HTTP_USER_AGENT",      999,    3 },
                   13466:     { "HTTP_X_FORWARDED_FOR", 999,    3 },
                   13467:     { NULL, -1, -1 }			/* trailer record */
                   13468:   } /* --- end-of-mimelog[] --- */
                   13469: #endif
                   13470:   ;
                   13471: 
                   13472: /* -------------------------------------------------------------------------
                   13473: messages
                   13474: -------------------------------------------------------------------------- */
                   13475: static	char *copyright =		/* copyright, gnu/gpl notice */
                   13476:  "+-----------------------------------------------------------------------+\n"
1.3     ! albertel 13477:  "|mimeTeX vers 1.64, Copyright(c) 2002-2006, John Forkosh Associates, Inc|\n"
1.1       albertel 13478:  "+-----------------------------------------------------------------------+\n"
                   13479:  "| mimeTeX is free software, licensed to you under terms of the GNU/GPL, |\n"
                   13480:  "|           and comes with absolutely no warranty whatsoever.           |\n"
                   13481:  "+-----------------------------------------------------------------------+";
                   13482: static	int maxmsgnum = 2;		/* maximum msgtable[] index */
                   13483: static	char *msgtable[] = {		/* messages referenced by [index] */
                   13484:  "\\red\\small\\rm\\fbox{\\array{"	/* [0] is invalid_referer_msg */
                   13485:    "Please~read~www.forkosh.com/mimetex.html\\\\and~install~mimetex.cgi~"
                   13486:    "on~your~own~server.\\\\Thank~you,~John~Forkosh}}",
                   13487:  "\\red\\small\\rm\\fbox{\\array{"	/* [1] */
                   13488:    "Please~provide~your~{\\tiny~HTTP-REFERER}~to~access~the~public\\\\"
                   13489:    "mimetex~server.~~Or~please~read~~www.forkosh.com/mimetex.html\\\\"
                   13490:    "and~install~mimetex.cgi~on~your~own~server.~~Thank~you,~John~Forkosh}}",
                   13491:  "\\red\\small\\rm\\fbox{\\array{"	/* [2] */
                   13492:    "The~public~mimetex~server~is~for~testing.~~For~production,\\\\"
                   13493:    "please~read~~www.forkosh.com/mimetex.html~~and~install\\\\"
                   13494:    "mimetex.cgi~on~your~own~server.~~Thank~you,~John~Forkosh}}",
                   13495:  NULL } ;				/* trailer */
                   13496: 
                   13497: 
                   13498: /* --- entry point --- */
                   13499: int	main ( int argc, char *argv[]
                   13500: 	  #ifdef DUMPENVP
                   13501: 	    , char *envp[]
                   13502: 	  #endif
                   13503: 	)
                   13504: {
                   13505: /* -------------------------------------------------------------------------
                   13506: Allocations and Declarations
                   13507: -------------------------------------------------------------------------- */
                   13508: /* --- expression to be emitted --- */
1.3     ! albertel 13509: static	char exprbuffer[MAXEXPRSZ+1] = "f(x)=x^2"; /* input TeX expression */
1.1       albertel 13510: char	*expression = exprbuffer;	/* ptr to expression */
                   13511: int	size = NORMALSIZE;		/* default font size */
                   13512: char	*query = getenv("QUERY_STRING"); /* getenv("QUERY_STRING") result */
                   13513: char	*mimeprep();			/* preprocess expression */
                   13514: int	unescape_url();			/* convert %xx's to ascii chars */
                   13515: int	emitcache();			/* emit cached image if it exists */
                   13516: int	isquery = 0,			/* true if input from QUERY_STRING */
                   13517: 	isqempty = 0,			/* true if query string empty */
1.2       albertel 13518: 	isqforce = 0,			/* true to force query emulation */
1.1       albertel 13519: 	isqlogging = 0,			/* true if logging in query mode */
                   13520: 	isformdata = 0,			/* true if input from html form */
1.2       albertel 13521: 	isinmemory = 1,			/* true to generate image in memory*/
                   13522: 	isdumpimage = 0,		/* true to dump image on stdout */
                   13523: 	isdumpbuffer = 0;		/* true to dump to memory buffer */
1.1       albertel 13524: /* --- rasterization --- */
1.2       albertel 13525: subraster *rasterize(), *sp=NULL;	/* rasterize expression */
                   13526: raster	*border_raster(), *bp=NULL;	/* put a border around raster */
                   13527: int	delete_subraster();		/* for clean-up at end-of-job */
1.1       albertel 13528: int	type_raster(), type_bytemap(),	/* screen dump function prototypes */
                   13529: 	xbitmap_raster();		/* mime xbitmap output function */
                   13530: /* --- http_referer --- */
                   13531: char	*referer = REFERER;		/* http_referer must contain this */
                   13532: struct	{ char *referer; int msgnum; }	/* http_referer can't contain this */
                   13533: 	denyreferer[] = {		/* referer table to deny access to */
                   13534: 	#ifdef DENYREFERER
                   13535: 	  #include DENYREFERER		/* e.g.,  {"",1},  for no referer */
                   13536: 	#endif
                   13537: 	{ NULL, -999 } };		/* trailer */
                   13538: char	*http_referer = getenv("HTTP_REFERER"); /* referer using mimeTeX */
                   13539: int	ishttpreferer = (http_referer==NULL?0:(*http_referer=='\000'?0:1));
                   13540: int	isstrstr();			/* search http_referer for referer */
                   13541: int	isinvalidreferer = 0;		/* true for inavlid referer */
                   13542: int	norefmaxlen = NOREFMAXLEN;	/*max query_string len if no referer*/
                   13543: /* --- gif --- */
                   13544: #if defined(GIF)
                   13545:   int	GetPixel();			/* feed pixels to gifsave library */
                   13546:   int	GIF_Create(),GIF_CompressImage(),GIF_Close(); /* prototypes for... */
                   13547:   void	GIF_SetColor(),GIF_SetTransparent(); /* ...gifsave enntry points */
                   13548: #endif
                   13549: char	*gif_outfile = (char *)NULL,	/* gif output defaults to stdout */
1.3     ! albertel 13550: 	gif_buffer[MAXGIFSZ] = "\000",	/* or gif written in memory buffer */
1.1       albertel 13551: 	cachefile[256] = "\000",	/* full path and name to cache file*/
                   13552: 	*md5str();			/* md5 has of expression */
                   13553: int	maxage = 7200;			/* max-age is two hours */
1.2       albertel 13554: /* --- pbm/pgm (-g switch) --- */
                   13555: int	ispbmpgm = 0;			/* true to write pbm/pgm file */
                   13556: int	type_pbmpgm(), ptype=0;		/* entry point, graphic format */
                   13557: char	*pbm_outfile = (char *)NULL;	/* output file defaults to stdout */
1.1       albertel 13558: /* --- anti-aliasing --- */
                   13559: intbyte	*bytemap_raster = NULL,		/* anti-aliased bitmap */
                   13560: 	colors[256];			/* grayscale vals in bytemap */
                   13561: int	aalowpass(), aapnm(),		/*lowpass filters for anti-aliasing*/
                   13562: 	grayscale = 256;		/* 0-255 grayscales in 8-bit bytes */
                   13563: int	ncolors=2,			/* #colors (2=b&w) */
                   13564: 	aacolormap();			/* build colormap from bytemap */
1.3     ! albertel 13565: int	ipattern;			/*patternnumcount[] index diagnostic*/
1.1       albertel 13566: /* --- messages --- */
                   13567: char	logfile[256] = LOGFILE,		/*log queries if msglevel>=LOGLEVEL*/
                   13568: 	cachelog[256] = CACHELOG;	/* cached image log in cachepath/ */
                   13569: char	*timestamp();			/* time stamp for logged messages */
                   13570: int	logger();			/* logs environ variables */
                   13571: int	ismonth();			/* check argv[0] for current month */
                   13572: char	*progname = (argc>0?argv[0]:"noname"); /* name program executed as */
                   13573: char	*dashes =			/* separates logfile entries */
                   13574:  "--------------------------------------------------------------------------";
                   13575: char	*invalid_referer_msg = msgtable[0]; /* msg to invalid http_referer */
                   13576: /* -------------------------------------------------------------------------
                   13577: initialization
                   13578: -------------------------------------------------------------------------- */
                   13579: /* --- run optional system command string --- */
                   13580: #ifdef SYSTEM
                   13581:   system(SYSTEM);
                   13582: #endif
                   13583: /* --- set global variables --- */
                   13584: msgfp = stdout;				/* for comamnd-line mode output */
                   13585: isss = issupersampling;			/* set supersampling flag */
1.2       albertel 13586: gifSize = 0;				/* signal that image not in memory */
1.1       albertel 13587: shrinkfactor = shrinkfactors[NORMALSIZE]; /* set shrinkfactor */
1.3     ! albertel 13588: for ( ipattern=1; ipattern<=51; ipattern++ )
        !          13589:  patternnumcount0[ipattern] = patternnumcount1[ipattern] = 0;
1.1       albertel 13590: /* ---
                   13591:  * check QUERY_STRING query for expression overriding command-line arg
                   13592:  * ------------------------------------------------------------------- */
                   13593: if ( query != NULL )			/* check query string from environ */
                   13594:   if ( strlen(query) >= 1 )		/* caller gave us a query string */
1.3     ! albertel 13595:     { strncpy(expression,query,MAXEXPRSZ); /* so use it as expression */
        !          13596:       expression[MAXEXPRSZ] = '\000';	/* make sure it's null terminated */
1.1       albertel 13597:       isquery = 1; }			/* and set isquery flag */
                   13598: if ( !isquery )				/* empty query string */
                   13599:   { char *host = getenv("HTTP_HOST"),	/* additional getenv("") results */
                   13600:     *name = getenv("SERVER_NAME"), *addr = getenv("SERVER_ADDR");
                   13601:     if ( host!=NULL || name!=NULL || addr!=NULL ) /* assume http query */
                   13602:       {	isquery = 1;			/* set flag to signal query */
1.2       albertel 13603: 	strcpy(expression,"\\red\\small\\fbox{\\rm~no~query~string}"); }
1.1       albertel 13604:     isqempty = 1;			/* signal empty query string */
                   13605:   } /* --- end-of-if(!isquery) --- */
                   13606: /* ---
                   13607:  * process command-line input args (if not a query)
                   13608:  * ------------------------------------------------ */
                   13609: if ( !isquery				/* don't have an html query string */
                   13610: ||   ( /*isqempty &&*/ argc>1) )	/* or have command-line args */
                   13611:  {
                   13612:  char	*argsignal = ARGSIGNAL,		/* signals start of mimeTeX args */
                   13613: 	stopsignal[32] = "--";		/* default Unix end-of-args signal */
                   13614:  int	iarg=0, argnum=0,		/*argv[] index for command-line args*/
                   13615: 	exprarg = 0,			/* argv[] index for expression */
                   13616: 	infilearg = 0,			/* argv[] index for infile */
                   13617: 	nswitches = 0,			/* number of -switches */
                   13618: 	isstopsignal = 0,		/* true after stopsignal found */
                   13619: 	isstrict = 1/*iswindows*/,	/* true for strict arg checking */
                   13620: 					/*nb, windows has apache "x -3" bug*/
                   13621: 	nargs=0, nbadargs=0,		/* number of arguments, bad ones */
                   13622: 	maxbadargs = (isstrict?0:1),	/*assume query if too many bad args*/
                   13623: 	isgoodargs = 0;			/* true to accept command-line args*/
                   13624:  if ( argsignal != NULL )		/* if compiled with -DARGSIGNAL */
                   13625:   while ( argc > ++iarg )		/* check each argv[] for argsignal */
                   13626:     if ( !strcmp(argv[iarg],argsignal) ) /* check for exact match */
                   13627:      { argnum = iarg;			/* got it, start parsing next arg */
                   13628:        break; }				/* stop looking for argsignal */
                   13629:  while ( argc > ++argnum )		/* check for switches and values, */
                   13630:     {
                   13631:     nargs++;				/* count another command-line arg */
                   13632:     if ( strcmp(argv[argnum],stopsignal) == 0 ) /* found stopsignal */
                   13633:       {	isstopsignal = 1;		/* so set stopsignal flag */
                   13634: 	continue; }			/* and get expression after it */
                   13635:     if ( !isstopsignal			/* haven't seen stopsignal switch */
                   13636:     &&   *argv[argnum] == '-' )		/* and have some '-' switch */
                   13637:       {
1.2       albertel 13638:       char *field = argv[argnum] + 1;	/* ptr to char(s) following - */
                   13639:       char flag = tolower(*field);	/* single char following '-' */
                   13640:       int  arglen = strlen(field);	/* #chars following - */
1.1       albertel 13641:       argnum++;		/* arg following flag/switch is usually its value */
                   13642:       nswitches++;			/* another switch on command line */
1.2       albertel 13643:       if ( isstrict &&			/* if strict checking then... */
                   13644:       !isthischar(flag,"g") && arglen!=1 ) /*must be single-char switch*/
1.1       albertel 13645: 	{ nbadargs++; argnum--; }	/* so ignore longer -xxx switch */
                   13646:       else				/* process single-char -x switch */
                   13647:        switch ( flag ) {		/* see what user wants to tell us */
                   13648: 	/* --- ignore uninterpreted flag --- */
                   13649: 	default:  nbadargs++;                              argnum--;  break;
                   13650: 	/* --- adjustable program parameters (not checking input) --- */
1.2       albertel 13651: 	case 'b': isdumpimage++; isdumpbuffer++;           argnum--;  break;
1.1       albertel 13652: 	case 'd': isdumpimage++;                           argnum--;  break;
                   13653: 	case 'e': isdumpimage++;           gif_outfile=argv[argnum];  break;
                   13654: 	case 'f': isdumpimage++;                   infilearg=argnum;  break;
1.2       albertel 13655: 	case 'g': ispbmpgm++;
                   13656: 	     if ( arglen > 1 ) ptype = atoi(field+1);	/* -g2 ==> ptype=2 */
                   13657: 	     if ( 1 || *argv[argnum]=='-' ) argnum--; /*next arg is -switch*/
                   13658: 	     else pbm_outfile = argv[argnum]; break; /*next arg is filename*/
1.1       albertel 13659: 	case 'm': msglevel = atoi(argv[argnum]);                      break;
1.3     ! albertel 13660: 	case 'o': istransparent = (istransparent?0:1);     argnum--;  break;
1.2       albertel 13661: 	case 'q': isqforce = 1;                            argnum--;  break;
1.1       albertel 13662: 	case 's': size = atoi(argv[argnum]);                          break;
                   13663: 	} /* --- end-of-switch(flag) --- */
                   13664:       } /* --- end-of-if(*argv[argnum]=='-') --- */
                   13665:     else				/* expression if arg not a -flag */
                   13666:       if ( infilearg == 0 )		/* no infile arg yet */
                   13667: 	{ if ( exprarg != 0 ) nbadargs++; /* 2nd expression invalid */
                   13668: 	  exprarg = argnum;		/* but take last expression */
                   13669: 	  /*infilearg = (-1);*/ }	/* and set infilearg */
                   13670:       else nbadargs++;			/* infile and expression invalid */
                   13671:     } /* --- end-of-while(argc>++argnum) --- */
                   13672:  if ( msglevel>=999 && msgfp!=NULL )	/* display command-line info */
1.2       albertel 13673:   { fprintf(msgfp,"argc=%d, progname=%s, #args=%d, #badargs=%d\n",
                   13674:     argc,progname,nargs,nbadargs);
                   13675:     fprintf(msgfp,"cachepath=\"%.50s\" pathprefix=\"%.50s\"\n",
                   13676:     cachepath,pathprefix); }
1.1       albertel 13677:  /* ---
                   13678:   * decide whether command-line input overrides query_string
                   13679:   * -------------------------------------------------------- */
                   13680:  if ( isdumpimage > 2 ) nbadargs++;	/* duplicate/conflicting -switch */
                   13681:  isgoodargs =  ( !isstrict		/*good if not doing strict checking*/
                   13682:   || !isquery				/* or if no query, must use args */
                   13683:   || (nbadargs<nargs && nbadargs<=maxbadargs) ); /* bad args imply query */
                   13684:  /* ---
                   13685:   * take expression from command-line args
                   13686:   * -------------------------------------- */
                   13687:  if ( isgoodargs && exprarg > 0		/* good expression on command line */
                   13688:  &&   infilearg <= 0 )			/* and not given in input file */
                   13689:   if ( !isquery				/* no conflict if no query_string */
                   13690:   ||   nswitches > 0 )			/* explicit -switch(es) also given */
1.3     ! albertel 13691:    { strncpy(expression,argv[exprarg],MAXEXPRSZ); /*expr from command-line*/
        !          13692:      expression[MAXEXPRSZ] = '\000';	/* make sure it's null terminated */
1.1       albertel 13693:      isquery = 0; }			/* and not from a query_string */
                   13694:  /* ---
                   13695:   * or read expression from input file
                   13696:   * ---------------------------------- */
                   13697:  if ( isgoodargs && infilearg > 0 )	/* have a good -f arg */
                   13698:   {
                   13699:   FILE *infile = fopen(argv[infilearg],"r"); /* open input file for read */
                   13700:   if ( infile != (FILE *)NULL )		/* opened input file successfully */
1.3     ! albertel 13701:    { char instring[MAXLINESZ+1];	/* line from file */
        !          13702:      int  exprsz = 0;			/* total #bytes read from file */
1.1       albertel 13703:      isquery = 0;			/* file input, not a query_string */
                   13704:      *expression = '\000';		/* start expresion as empty string */
1.3     ! albertel 13705:      while ( fgets(instring,MAXLINESZ,infile) != (char *)NULL ) /*till eof*/
        !          13706:       if ( exprsz + strlen(instring) < MAXEXPRSZ ) { /* have room for line */
        !          13707: 	strcat(expression,instring);	/* concat line to end of expression*/
        !          13708: 	exprsz += strlen(instring); }	/* update expression buffer length */
1.1       albertel 13709:      fclose ( infile ); }	/*close input file after reading expression*/
                   13710:   } /* --- end-of-if(infilearg>0) --- */
1.2       albertel 13711:  /* ---
1.3     ! albertel 13712:   * xlate +++'s to blanks only if query
        !          13713:   * ----------------------------------- */
        !          13714:  if ( !isquery ) isplusblank = 0;	/* don't xlate +++'s to blanks */
        !          13715:  /* ---
1.2       albertel 13716:   * check if emulating query (for testing)
                   13717:   * -------------------------------------- */
                   13718:  if ( isqforce ) isquery = 1;		/* emulate query string processing */
                   13719:  /* ---
                   13720:   * check if emitting pbm/pgm graphic
                   13721:   * --------------------------------- */
                   13722:  if ( isgoodargs && ispbmpgm > 0 )	/* have a good -g arg */
                   13723:   if ( 1 && gif_outfile != NULL )	/* had an -e switch with file */
                   13724:    if ( *gif_outfile != '\000' )	/* make sure string isn't empty */
                   13725:      { pbm_outfile = gif_outfile;	/* use -e switch file for pbm/pgm */
                   13726:        gif_outfile = (char *)NULL;	/* reset gif output file */
                   13727:        /*isdumpimage--;*/ }		/* and decrement -e count */
1.1       albertel 13728:  } /* --- end-of-if(!isquery) --- */
                   13729: /* ---
                   13730:  * check for <form> input
                   13731:  * ---------------------- */
1.3     ! albertel 13732: if ( isquery ) {				/* must be <form method="get"> */
1.1       albertel 13733:  if ( !memcmp(expression,"formdata",8) ) /*must be <input name="formdata"> */
                   13734:   { char *delim=strchr(expression,'=');	/* find equal following formdata */
                   13735:     if ( delim != (char *)NULL )	/* found unescaped equal sign */
                   13736:       strcpy(expression,delim+1);	/* so shift name= out of expression*/
                   13737:     while ( (delim=strchr(expression,'+')) != NULL ) /*unescaped plus sign*/
                   13738:       *delim = ' ';			/* is "shorthand" for blank space */
                   13739:     /*unescape_url(expression,1);*/	/* convert unescaped %xx's to chars */
                   13740:     unescape_url(expression,0);		/* convert all %xx's to chars */
                   13741:     unescape_url(expression,0);		/* repeat */
1.3     ! albertel 13742:     if(0) msglevel = FORMLEVEL;		/* msglevel for forms */
1.1       albertel 13743:     isformdata = 1; }			/* set flag to signal form data */
                   13744:  else /* --- query, but not <form> input --- */
1.3     ! albertel 13745:     unescape_url(expression,0); }	/* convert _all_ %xx's to chars */
1.1       albertel 13746: /* ---
                   13747:  * check queries for embedded prefixes signalling special processing
                   13748:  * ----------------------------------------------------------------- */
                   13749: if ( isquery )				/* only check queries */
                   13750:  {
                   13751:  /* --- check for msglevel=###$ prefix --- */
                   13752:  if ( !memcmp(expression,"msglevel=",9) ) /* query has msglevel prefix */
                   13753:    { char *delim=strchr(expression,'$'); /* find $ delim following msglevel*/
                   13754:      if ( delim != (char *)NULL )	/* check that we found delim */
                   13755:       {	*delim = '\000';		/* replace delim with null */
                   13756: 	if ( seclevel <= 9 )		/* permit msglevel specification */
                   13757: 	  msglevel = atoi(expression+9); /* interpret ### in msglevel###$ */
                   13758: 	strcpy(expression,delim+1); } }	/* shift out prefix and delim */
                   13759:  /* --- next check for logfile=xxx$ prefix (must follow msglevel) --- */
                   13760:  if ( !memcmp(expression,"logfile=",8) ) /* query has logfile= prefix */
                   13761:    { char *delim=strchr(expression,'$'); /* find $ delim following logfile=*/
                   13762:      if ( delim != (char *)NULL )	/* check that we found delim */
                   13763:       {	*delim = '\000';		/* replace delim with null */
                   13764: 	if ( seclevel <= 3 )		/* permit logfile specification */
                   13765: 	  strcpy(logfile,expression+8);	/* interpret xxx in logfile=xxx$ */
                   13766: 	strcpy(expression,delim+1); } }	/* shift out prefix and delim */
                   13767:  } /* --- end-of-if(isquery) --- */
                   13768: /* ---
                   13769:  * log query (e.g., for debugging)
                   13770:  * ------------------------------- */
                   13771: if ( isquery )				/* only log query_string's */
                   13772:  if ( msglevel >= LOGLEVEL		/* check if logging */
                   13773:  &&   seclevel <= 5 )			/* and if logging permitted */
                   13774:   if ( logfile != NULL )		/* if a logfile is given */
1.3     ! albertel 13775:    if ( *logfile != '\000' ) {		/*and if it's not an empty string*/
1.1       albertel 13776:     if ( (msgfp=fopen(logfile,"a"))	/* open logfile for append */
                   13777:     !=   NULL )				/* ignore logging if can't open */
                   13778:      {
                   13779:      /* --- default logging --- */
                   13780:      logger(msgfp,msglevel,expression,mimelog); /* log query */
                   13781:      /* --- additional debug logging (argv and environment) --- */
                   13782:      if ( msglevel >= 9 )		/* log environment */
                   13783:       { int i;  /*char name[999],*value;*/
                   13784: 	fprintf(msgfp,"Command-line arguments...\n");
                   13785: 	if ( argc < 1 )			/* no command-line args */
                   13786: 	 fprintf(msgfp,"  ...argc=%d, no argv[] variables\n",argc);
                   13787: 	else
                   13788: 	 for ( i=0; i<argc; i++ )	/* display all argv[]'s */
                   13789: 	  fprintf(msgfp,"  argv[%d] = \"%s\"\n",i,argv[i]);
                   13790: 	#ifdef DUMPENVP			/* char *envp[] available for dump */
                   13791: 	fprintf(msgfp,"Environment variables (using envp[])...\n");
                   13792: 	if ( envp == (char **)NULL )	/* envp not provided */
                   13793: 	 fprintf(msgfp,"  ...envp[] environment variables not available\n");
                   13794: 	else
                   13795: 	 for ( i=0; ; i++ )		/* display all envp[]'s */
                   13796: 	  if ( envp[i] == (char *)NULL ) break;
                   13797: 	  else fprintf(msgfp,"  envp[%d] = \"%s\"\n",i,envp[i]);
                   13798: 	#endif /* --- DUMPENVP ---*/
                   13799: 	#ifdef DUMPENVIRON	/* skip what should be redundant output */
                   13800: 	fprintf(msgfp,"Environment variables (using environ)...\n");
                   13801: 	if ( environ == (char **)NULL )	/* environ not provided */
                   13802: 	 fprintf(msgfp,"  ...extern environ variables not available\n");
                   13803: 	else
                   13804: 	 for ( i=0; ; i++ )		/*display environ[] and getenv()'s*/
                   13805: 	  if ( environ[i] == (char *)NULL ) break;
                   13806: 	  else {
                   13807: 	    strcpy(name,environ[i]);	/* set up name for getenv() arg */
                   13808: 	    if ( (value=strchr(name,'=')) != NULL ) /* = delimits name */
                   13809: 	      {	*value = '\000';	/* got it, so null-terminate name */
                   13810: 		value = getenv(name); }	/* and look up name using getenv() */
                   13811: 	    else strcpy(name,"NULL");	/* missing = delim in environ[i] */
                   13812: 	    fprintf(msgfp,"environ[%d]: \"%s\"\n\tgetenv(%s) = \"%s\"\n",
                   13813: 	    i,environ[i],name,(value==NULL?"NULL":value));
                   13814: 	    } /* --- end-of-if/else --- */
                   13815: 	#endif /* --- DUMPENVIRON ---*/
                   13816:       } /* --- end-of-if(msglevel>=9) --- */
                   13817:      /* --- close log file if no longer needed --- */
                   13818:      if ( msglevel < DBGLEVEL )		/* logging, but not debugging */
                   13819:       {	fprintf(msgfp,"%s\n",dashes);	/* so log separator line, */
                   13820: 	fclose(msgfp);			/* close logfile immediately, */
                   13821: 	msgfp = NULL; }			/* and reset msgfp pointer */
                   13822:      else
                   13823: 	isqlogging = 1;			/* set query logging flag */
                   13824:      } /* --- end-of-if(msglevel>=LOGLEVEL) --- */
                   13825:     else				/* couldn't open logfile */
1.3     ! albertel 13826:      msglevel = 0; }			/* can't emit messages */
1.1       albertel 13827: /* ---
                   13828:  * prepend prefix to submitted expression
                   13829:  * -------------------------------------- */
                   13830: if ( 1 || isquery )			/* queries or command-line */
                   13831:  if ( *exprprefix != '\000' )		/* we have a prefix string */
                   13832:   { int npref = strlen(exprprefix);	/* #chars in prefix */
                   13833:     memmove(expression+npref+1,expression,strlen(expression)+1); /*make room*/
                   13834:     memcpy(expression,exprprefix,npref); /* copy prefix into expression */
                   13835:     expression[npref] = '{';		/* followed by { */
                   13836:     strcat(expression,"}"); }		/* and terminating } to balance { */
                   13837: /* ---
                   13838:  * check if http_referer is allowed to use this image
                   13839:  * -------------------------------------------------- */
                   13840: if ( isquery )				/* not relevant if "interactive" */
                   13841:  if ( referer != NULL )			/* nor if compiled w/o -DREFERER= */
                   13842:   if ( strcmp(referer,"month") != 0 )	/* nor if it's *only* "month" */
                   13843:    if ( http_referer != NULL )		/* nor if called "standalone" */
                   13844:     if ( !isstrstr(http_referer,referer,0) ) /* invalid http_referer */
                   13845:      { expression = invalid_referer_msg; /* so give user error message */
                   13846:        isinvalidreferer = 1; }		/* and signal invalid referer */
                   13847: /* ---
                   13848:  * check if referer contains "month" signal
                   13849:  * ---------------------------------------- */
                   13850: if ( isquery )				/* not relevant if "interactive" */
                   13851:  if ( referer != NULL )			/* nor if compiled w/o -DREFERER= */
                   13852:   if ( !isinvalidreferer )		/* nor if already invalid referer */
                   13853:    if ( strstr(referer,"month") != NULL ) /* month check requested */
                   13854:     if ( !ismonth(progname) )		/* not executed as mimetexJan-Dec */
                   13855:      { expression = invalid_referer_msg; /* so give user error message */
                   13856:        isinvalidreferer = 1; }		/* and signal invalid referer */
                   13857: /* ---
                   13858:  * check if http_referer is to be denied access
                   13859:  * -------------------------------------------- */
                   13860: if ( isquery )				/* not relevant if "interactive" */
                   13861:  if ( !isinvalidreferer )		/* nor if already invalid referer */
                   13862:   { int	iref=0, msgnum=(-999);		/* denyreferer index, message# */
                   13863:     for ( iref=0; msgnum<0; iref++ ) {	/* run through denyreferer[] table */
                   13864:       char *deny = denyreferer[iref].referer; /* referer to be denied */
                   13865:       if ( deny == NULL ) break;	/* null signals end-of-table */
                   13866:       if ( msglevel>=999 && msgfp!=NULL ) /* debugging */
                   13867: 	{fprintf(msgfp,"main> invalid iref=%d: deny=%s http_referer=%s\n",
                   13868: 	 iref,deny,(http_referer==NULL?"null":http_referer)); fflush(msgfp);}
                   13869:       if ( *deny == '\000' )		/* signal to check for no referer */
                   13870: 	{ if ( http_referer == NULL )	/* http_referer not supplied */
                   13871: 	   msgnum = denyreferer[iref].msgnum; } /* so set message# */
                   13872:       else				/* have referer to check for */
                   13873:        if ( http_referer != NULL )	/* and have referer to be checked */
                   13874: 	if ( isstrstr(http_referer,deny,0) ) /* invalid http_referer */
                   13875: 	 msgnum = denyreferer[iref].msgnum; /* so set message# */
                   13876:       } /* --- end-of-for(iref) --- */
                   13877:     if ( msgnum >= 0 )			/* deny access to this referer */
                   13878:      { if ( msgnum > maxmsgnum ) msgnum = 0; /* keep index within bounds */
                   13879:        expression = msgtable[msgnum];	/* set user error message */
                   13880:        isinvalidreferer = 1; }		/* and signal invalid referer */
                   13881:   } /* --- end-of-if(!isinvalidreferer) --- */
                   13882: /* --- also check maximum query_string length if no http_referer given --- */
                   13883: if ( isquery )				/* not relevant if "interactive" */
                   13884:  if ( !isinvalidreferer )		/* nor if already invalid referer */
                   13885:   if ( !ishttpreferer )			/* no http_referer supplied */
                   13886:    if ( strlen(expression) > norefmaxlen ) /* query_string too long */
                   13887:     { expression = invalid_referer_msg;	/* set invalid http_referer message*/
                   13888:       isinvalidreferer = 1; }		/* and signal invalid referer */
                   13889: /* ---
                   13890:  * check for image caching
                   13891:  * ----------------------- */
1.2       albertel 13892: if ( strstr(expression,"\\counter")  != NULL /* can't cache \counter{} */
                   13893: ||   strstr(expression,"\\input")    != NULL /* can't cache \input{} */
                   13894: ||   strstr(expression,"\\today")    != NULL /* can't cache \today */
                   13895: ||   strstr(expression,"\\calendar") != NULL /* can't cache \calendar */
                   13896: ||   strstr(expression,"\\nocach")   != NULL /* no caching requested */
                   13897: ||   isformdata				/* don't cache user form input */
1.1       albertel 13898:  ) { iscaching = 0;			/* so turn caching off */
1.2       albertel 13899:      maxage = 5; }			/* and set max-age to 5 seconds */
1.1       albertel 13900: if ( isquery )				/* don't cache command-line images */
                   13901:  if ( iscaching )			/* image caching enabled */
                   13902:   {
                   13903:   /* --- set up path to cached image file --- */
                   13904:   char *md5hash = md5str(expression);	/* md5 hash of expression */
                   13905:   if ( md5hash == NULL )		/* failed for some reason */
                   13906:     iscaching = 0;			/* so turn off caching */
                   13907:   else
                   13908:    {
                   13909:    strcpy(cachefile,cachepath);		/* start with (relative) path */
                   13910:    strcat(cachefile,md5hash);		/* add md5 hash of expression */
                   13911:    strcat(cachefile,".gif");		/* finish with .gif extension */
                   13912:    gif_outfile = cachefile;		/* signal GIF_Create() to cache */
1.2       albertel 13913:    /* --- emit mime content-type line --- */
1.3     ! albertel 13914:    if ( 0 && isemitcontenttype )	/* now done in emitcache() */
1.2       albertel 13915:     { fprintf( stdout, "Cache-Control: max-age=%d\n",maxage );
                   13916:       fprintf( stdout, "Content-type: image/gif\n\n" ); }
1.1       albertel 13917:    /* --- emit cached image if it already exists --- */
1.2       albertel 13918:    if ( emitcache(cachefile,maxage,0) > 0 ) /* cached image emitted */
1.1       albertel 13919:     goto end_of_job;			/* so nothing else to do */
                   13920:    /* --- log caching request --- */
                   13921:    if ( msglevel >= 1			/* check if logging */
                   13922:    /*&&   seclevel <= 5*/ )		/* and if logging permitted */
                   13923:     if ( cachelog != NULL )		/* if a logfile is given */
                   13924:      if ( *cachelog != '\000' )		/*and if it's not an empty string*/
                   13925:       { char filename[256];		/* construct cachepath/cachelog */
                   13926:         FILE *filefp=NULL;		/* fopen(filename) */
                   13927:         strcpy(filename,cachepath);	/* start with (relative) path */
                   13928:         strcat(filename,cachelog);	/* add cache log filename */
                   13929:         if ( (filefp=fopen(filename,"a")) /* open cache logfile for append */
                   13930:         !=   NULL )			/* ignore logging if can't open */
                   13931: 	 { int isreflogged = 0;		/* set true if http_referer logged */
                   13932: 	   fprintf(filefp,"%s                 %s\n", /* timestamp, md5 file */
1.2       albertel 13933: 	    timestamp(TZDELTA,0),cachefile+strlen(cachepath)); /*skip path*/
1.1       albertel 13934: 	   fprintf(filefp,"%s\n",expression); /* expression in filename */
                   13935: 	   if ( http_referer != NULL )	/* show referer if we have one */
                   13936: 	    if ( *http_referer != '\000' )    /* and if not an empty string*/
                   13937: 	      {	int loglen = strlen(dashes);  /* #chars on line in log file*/
                   13938: 		char *refp = http_referer;    /* line to be printed */
                   13939: 		isreflogged = 1;	      /* signal http_referer logged*/
                   13940: 		while ( 1 ) {		      /* printed in parts if needed*/
                   13941: 		  fprintf(filefp,"%.*s\n",loglen,refp); /* print a part */
                   13942: 		  if ( strlen(refp) <= loglen ) break;  /* no more parts */
                   13943: 		  refp += loglen; } }	      /* bump ptr to next part */
                   13944: 	   if ( !isreflogged )		      /* http_referer not logged */
                   13945: 	     fprintf(filefp,"http://none\n"); /* so log dummy referer line */
                   13946: 	   fprintf(filefp,"%s\n",dashes);     /* separator line */
                   13947: 	   fclose(filefp); }		     /* close logfile immediately */
                   13948:       } /* --- end-of-if(cachelog!=NULL) --- */
                   13949:    } /* --- end-of-if/else(md5hash==NULL) --- */
                   13950:   } /* --- end-of-if(iscaching) --- */
                   13951: /* ---
                   13952:  * emit copyright, gnu/gpl notice (if "interactive")
                   13953:  * ------------------------------------------------- */
                   13954: if ( !isdumpimage )			/* don't mix ascii with image dump */
                   13955:  if ( (!isquery||isqlogging) && msgfp!=NULL ) /* called from command line */
                   13956:    fprintf(msgfp,"%s\n",copyright);	/* display copyright, gnu/gpl info */
                   13957: /* -------------------------------------------------------------------------
                   13958: rasterize expression and put a border around it
                   13959: -------------------------------------------------------------------------- */
                   13960: /* --- preprocess expression, converting LaTeX constructs for mimeTeX  --- */
                   13961: expression = mimeprep(expression);	/* preprocess expression */
                   13962: /* --- double-check that we actually have an expression to rasterize --- */
                   13963: if ( expression == NULL )		/* nothing to rasterize */
                   13964:  { if ( (!isquery||isqlogging) && msgfp!=NULL ) /*emit error if not a query*/
                   13965:      fprintf(msgfp,"No expression to rasterize\n");
                   13966:    goto end_of_job; }			/* and then quit */
                   13967: /* --- rasterize expression --- */
                   13968: if ( (sp = rasterize(expression,size)) == NULL ) /* failed to rasterize */
                   13969:  { if ( (!isquery||isqlogging) && msgfp!=NULL ) /*emit error if not a query*/
                   13970:      fprintf(msgfp,"Failed to rasterize %s\n",expression);
                   13971:    if ( isquery ) sp = rasterize(	/* or emit error raster if query */
                   13972:      "\\red\\rm~\\fbox{mimeTeX~failed~to~render\\\\your~expression}",1);
                   13973:    if ( sp ==  NULL ) goto end_of_job; } /* re-check for failure */
                   13974: /* ---no border requested, but this adjusts width to multiple of 8 bits--- */
                   13975: if ( issupersampling )			/* no border needed for gifs */
                   13976:   bp = sp->image;			/* so just extract pixel map */
                   13977: else					/* for mime xbitmaps must have... */
                   13978:   bp = border_raster(sp->image,0,0,0,1); /* image width multiple of 8 bits */
                   13979: sp->image = bitmap_raster = bp;		/* global copy for gif,png output */
1.2       albertel 13980: if ( ispbmpgm && ptype<2 )		/* -g switch or -g1 switch */
                   13981:   type_pbmpgm(bp,ptype,pbm_outfile);	/* emit b/w pbm file */
1.1       albertel 13982: /* -------------------------------------------------------------------------
                   13983: generate anti-aliased bytemap from (bordered) bitmap
                   13984: -------------------------------------------------------------------------- */
                   13985: if ( isaa )				/* we want anti-aliased bitmap */
                   13986:   {
                   13987:   /* ---
                   13988:    * allocate bytemap and colormap as per width*height of bitmap
                   13989:    * ----------------------------------------------------------- */
                   13990:   int	nbytes = (bp->width)*(bp->height); /*#bytes needed in byte,colormap*/
                   13991:   if ( isss )				/* anti-aliasing by supersampling */
                   13992:     bytemap_raster = (intbyte *)(bitmap_raster->pixmap); /*bytemap in raster*/
                   13993:   else					/* need to allocate bytemap */
                   13994:     if ( aaalgorithm == 0 )		/* anti-aliasing not wanted */
                   13995:       isaa = 0;				/* so signal no anti-aliasing */
                   13996:     else				/* anti-aliasing wanted */
                   13997:       if ( (bytemap_raster = (intbyte *)malloc(nbytes)) /* malloc bytemap */
                   13998:       ==   NULL ) isaa = 0;		/* reset flag if malloc failed */
                   13999:   if ( isaa )				/* have bytemap, so... */
                   14000:     if ( (colormap_raster = (intbyte *)malloc(nbytes)) /* malloc colormap */
                   14001:     ==   NULL ) isaa = 0;		/* reset flag if malloc failed */
                   14002:   /* ---
                   14003:    * now generate anti-aliased bytemap and colormap from bitmap
                   14004:    * ---------------------------------------------------------- */
                   14005:   if ( isaa )				/*re-check that we're anti-aliasing*/
                   14006:     {
                   14007:     /* ---
                   14008:      * select anti-aliasing algorithm
                   14009:      * ------------------------------ */
                   14010:     if ( !isss )			/* generate bytemap for lowpass */
1.3     ! albertel 14011:      switch ( aaalgorithm ) {		/* choose antialiasing algorithm */
        !          14012:        default: isaa = 0; break;	/* unrecognized algorithm */
        !          14013:        case 1:				/* 1 for aalowpass() */
        !          14014: 	if ( aalowpass(bp,bytemap_raster,grayscale) /*my own lowpass filter*/
        !          14015: 	==   0 )  isaa = 0;		/*failed, so turn off anti-aliasing*/
        !          14016: 	break;
        !          14017:        case 2:				/*2 for netpbm pnmalias.c algorithm*/
        !          14018: 	if ( aapnm(bp,bytemap_raster,grayscale) /* pnmalias.c filter */
        !          14019: 	==   0 )  isaa = 0;		/*failed, so turn off anti-aliasing*/
        !          14020: 	break;
        !          14021:        case 3:				/*3 for aapnm() based on aagridnum()*/
        !          14022: 	if ( aapnmlookup(bp,bytemap_raster,grayscale) /* pnmalias.c filter */
        !          14023: 	==   0 )  isaa = 0;		/*failed, so turn off anti-aliasing*/
        !          14024: 	break;
        !          14025:        case 4:				/* 4 for aalookup() table lookup */
        !          14026: 	if ( aalowpasslookup(bp,bytemap_raster,grayscale) /* aalookup() */
        !          14027: 	==   0 )  isaa = 0;		/*failed, so turn off anti-aliasing*/
        !          14028: 	break;
        !          14029:        } /* --- end-of-switch(aaalgorithm) --- */
        !          14030:     /* ---
        !          14031:      * emit aalookup() pattern# counts/percents diagnostics
        !          14032:      * ---------------------------------------------------- */
        !          14033:     if ( !isquery && msgfp!=NULL && msglevel>=99 ) { /*emit patternnumcounts*/
        !          14034:      int pcount0=0, pcount1=0;		/* init total w,b center counts */
        !          14035:      for ( ipattern=1; ipattern<=51; ipattern++ ) { /*each possible pattern*/
        !          14036:       if ( ipattern > 1 )		/* ignore all-white squares */
        !          14037:        pcount0 += patternnumcount0[ipattern];  /* bump total white centers */
        !          14038:       pcount1 += patternnumcount1[ipattern]; } /* bump total black centers */
        !          14039:      if ( pcount0+pcount1 > 0 )		/* have pcounts (using aalookup) */
        !          14040:       fprintf(msgfp, "  aalookup() patterns excluding#1 white"
        !          14041:       " (%%'s are in tenths of a percent)...\n");
        !          14042:      for ( ipattern=1; ipattern<=51; ipattern++ ) { /*each possible pattern*/
        !          14043:       int tot = patternnumcount0[ipattern] + patternnumcount1[ipattern];
        !          14044:       if ( tot > 0 )			/* this pattern occurs in image */
        !          14045:        fprintf(msgfp,
        !          14046:        "  pattern#%2d: %7d(%6.2f%%) +%7d(%6.2f%%) =%7d(%6.2f%%)\n",
        !          14047:        ipattern, patternnumcount0[ipattern],  (ipattern<=1? 999.99:
        !          14048:        1000.*((double)patternnumcount0[ipattern])/((double)pcount0)),
        !          14049:        patternnumcount1[ipattern],
        !          14050:        1000.*((double)patternnumcount1[ipattern])/((double)pcount1),
        !          14051:        tot,  (ipattern<=1? 999.99:
        !          14052:        1000.*((double)tot)/((double)(pcount0+pcount1))) ); }
        !          14053:       if ( pcount0+pcount1 > 0 )	/* true when using aalookup() */
        !          14054:        fprintf(msgfp,
        !          14055:        "all patterns: %7d          +%7d          =%7d  total pixels\n",
        !          14056:        pcount0,pcount1,pcount0+pcount1); }
1.1       albertel 14057:     /* ---
                   14058:      * finally, generate colors and colormap
                   14059:      * ------------------------------------- */
                   14060:     if ( isaa ) {			/* we have bytemap_raster */
                   14061:       ncolors = aacolormap(bytemap_raster,nbytes,colors,colormap_raster);
                   14062:       if ( ncolors < 2 )		/* failed */
                   14063: 	{ isaa = 0;			/* so turn off anti-aliasing */
                   14064: 	  ncolors = 2; }		/* and reset for black&white */
                   14065:       } /* --- end-of-if(isaa) --- */
1.2       albertel 14066:      if ( isaa && ispbmpgm && ptype>1 ) { /* -g2 switch  */
                   14067:       raster pbm_raster;		/*construct arg for write_pbmpgm()*/
                   14068:       pbm_raster.width  = bp->width;  pbm_raster.height = bp->height;
                   14069:       pbm_raster.pixsz  = 8;  pbm_raster.pixmap = (pixbyte *)bytemap_raster;
                   14070:       type_pbmpgm(&pbm_raster,ptype,pbm_outfile); } /*write grayscale file*/
1.1       albertel 14071:     } /* --- end-of-if(isaa) --- */
                   14072:   } /* --- end-of-if(isaa) --- */
                   14073: /* -------------------------------------------------------------------------
                   14074: display results on msgfp if called from command line (usually for testing)
                   14075: -------------------------------------------------------------------------- */
                   14076: if ( (!isquery||isqlogging) || msglevel >= 99 )	/*command line or debuging*/
                   14077:  if ( !isdumpimage )			/* don't mix ascii with image dump */
                   14078:   {
                   14079:   /* ---
                   14080:    * display ascii image of rasterize()'s rasterized bitmap
                   14081:    * ------------------------------------------------------ */
                   14082:   if ( !isss )				/* no bitmap for supersampling */
                   14083:     { fprintf(msgfp,"\nAscii dump of bitmap image...\n");
                   14084:       type_raster(bp,msgfp); }		/* emit ascii image of raster */
                   14085:   /* ---
                   14086:    * display anti-aliasing results applied to rasterized bitmap
                   14087:    * ---------------------------------------------------------- */
                   14088:   if ( isaa )				/* if anti-aliasing applied */
                   14089:     {
                   14090:     int	igray;				/* colors[] index */
                   14091:     /* --- anti-aliased bytemap image --- */
                   14092:     if ( msgfp!=NULL && msglevel>=9 )	/* don't usually emit raw bytemap */
                   14093:       {	fprintf(msgfp,"\nHex dump of anti-aliased bytemap, " /*emit bytemap*/
                   14094: 	"asterisks denote \"black\" bytes (value=%d)...\n",grayscale-1);
                   14095: 	type_bytemap(bytemap_raster,grayscale,bp->width,bp->height,msgfp); }
                   14096:     /* --- colormap image --- */
                   14097:     fprintf(msgfp,"\nHex dump of colormap indexes, "  /* emit colormap */
                   14098:       "asterisks denote \"black\" bytes (index=%d)...\n",ncolors-1);
                   14099:     type_bytemap(colormap_raster,ncolors,bp->width,bp->height,msgfp);
                   14100:     /* --- rgb values corresponding to colormap indexes */
                   14101:     fprintf(msgfp,"\nThe %d colormap indexes denote rgb values...",ncolors);
                   14102:     for ( igray=0; igray<ncolors; igray++ ) /* show colors[] values */
                   14103:       fprintf(msgfp,"%s%2x-->%3d", (igray%5?"   ":"\n"),
                   14104: 	igray,(int)(colors[ncolors-1]-colors[igray]));
                   14105:     fprintf(msgfp,"\n");		/* always needs a final newline */
                   14106:     } /* --- end-of-if(isaa) --- */
                   14107:   } /* --- end-of-if(!isquery||msglevel>=9) --- */
                   14108: /* -------------------------------------------------------------------------
                   14109: emit xbitmap or gif image, and exit
                   14110: -------------------------------------------------------------------------- */
                   14111: if (  isquery				/* called from browser (usual) */
1.2       albertel 14112: ||    (isdumpimage && !ispbmpgm)	/* or to emit gif dump of image */
1.1       albertel 14113: ||    msglevel >= 99 )			/* or for debugging */
                   14114:  {
                   14115:  int  igray = 0;			/* grayscale index */
                   14116:  #if defined(GIF)			/* compiled to emit gif */
                   14117:  /* ------------------------------------------------------------------------
                   14118:  emit GIF image
                   14119:  ------------------------------------------------------------------------- */
1.2       albertel 14120:   /* --- don't use memory buffer if outout file given --- */
                   14121:   if ( gif_outfile != NULL ) isinmemory = 0; /* reset memory buffer flag */
1.1       albertel 14122:   /* --- emit mime content-type line --- */
1.3     ! albertel 14123:   if ( isemitcontenttype		/* content-type lines wanted */
        !          14124:   &&   !isdumpimage			/* don't mix ascii with image dump */
1.2       albertel 14125:   &&   !isinmemory			/* done below if in memory */
                   14126:   &&   !iscaching )			/* done by emitcache() if caching */
1.1       albertel 14127:     { fprintf( stdout, "Cache-Control: max-age=%d\n",maxage );
                   14128:       /*fprintf( stdout, "Expires: Fri, 31 Oct 2003 23:59:59 GMT\n" );*/
                   14129:       /*fprintf( stdout, "Last-Modified: Wed, 15 Oct 2003 01:01:01 GMT\n" );*/
                   14130:       fprintf( stdout, "Content-type: image/gif\n\n" ); }
1.2       albertel 14131:   /* --- write output to memory buffer, possibly for testing --- */
                   14132:   if ( isinmemory			/* want gif written to memory */
                   14133:   ||   isdumpbuffer )			/*or dump memory buffer for testing*/
                   14134:    if ( gif_outfile == NULL )		/* and don't already have a file */
                   14135:     { *gif_buffer = '\000';		/* init buffer as empty string */
1.3     ! albertel 14136:       memset(gif_buffer,0,MAXGIFSZ);	/* zero out buffer */
1.2       albertel 14137:       gif_outfile = gif_buffer;		/* and point outfile to buffer */
                   14138:       if ( isdumpbuffer )		/* buffer dump test requested */
                   14139: 	isdumpbuffer = 999; }		/* so signal dumping to buffer */
1.1       albertel 14140:   /* --- initialize gifsave library and colors --- */
                   14141:   if ( msgfp!=NULL && msglevel>=999 )
1.2       albertel 14142:     { fprintf(msgfp,"main> calling GIF_Create(*,%d,%d,%d,8)\n",
                   14143:       bp->width,bp->height,ncolors); fflush(msgfp); }
1.1       albertel 14144:   while ( 1 )		/* init gifsave lib, and retry if caching fails */
                   14145:     { int status = GIF_Create(gif_outfile, bp->width,bp->height, ncolors, 8);
                   14146:       if ( status == 0 ) break;		/* continue if succeeded */
                   14147:       if ( iscaching == 0 ) goto end_of_job; /* quit if failed */
                   14148:       iscaching = 0;			/* retry without cache file */
1.2       albertel 14149:       isdumpbuffer = 0;			/* reset isdumpbuffer signal */
                   14150:       if ( isquery ) isinmemory = 1;	/* force in-memory image generation*/
                   14151:       if ( isinmemory ) {		/* using memory buffer */
                   14152: 	gif_outfile = gif_buffer;	/* emit images to memory buffer */
                   14153: 	*gif_outfile = '\000'; }	/* empty string signals buffer */
                   14154:       else {				/* or */
                   14155: 	gif_outfile = (char *)NULL;	/* emit images to stdout */
1.3     ! albertel 14156: 	if ( isemitcontenttype ) {	/* content-type lines wanted */
        !          14157: 	  fprintf( stdout, "Cache-Control: max-age=%d\n",maxage );
        !          14158: 	  fprintf( stdout, "Content-type: image/gif\n\n" ); } }
1.2       albertel 14159:     } /* --- end-of-while(1) --- */
1.1       albertel 14160:   GIF_SetColor(0,bgred,bggreen,bgblue);	/* background white if all 255 */
                   14161:   if ( !isaa )				/* just b&w if not anti-aliased */
                   14162:     { GIF_SetColor(1,fgred,fggreen,fgblue); /* foreground black if all 0 */
                   14163:       colors[0]='\000'; colors[1]='\001'; } /* and set 2 b&w color indexes */
                   14164:   else					/* set grayscales for anti-aliasing */
                   14165:     /* --- anti-aliased, so call GIF_SetColor() for each colors[] --- */
                   14166:     for ( igray=1; igray<ncolors; igray++ ) /* for colors[] values */
                   14167:       {
                   14168:       /*--- gfrac goes from 0 to 1.0, as igray goes from 0 to ncolors-1 ---*/
                   14169:       double gfrac = ((double)colors[igray])/((double)colors[ncolors-1]);
                   14170:       /* --- r,g,b components go from background to foreground color --- */
                   14171:       int red  = iround(((double)bgred)  +gfrac*((double)(fgred-bgred))),
                   14172: 	  green= iround(((double)bggreen)+gfrac*((double)(fggreen-bggreen))),
                   14173: 	  blue = iround(((double)bgblue) +gfrac*((double)(fgblue-bgblue)));
                   14174:       /* --- set color index number igray to rgb values gray,gray,gray --- */
                   14175:       GIF_SetColor(igray, red,green,blue); /*set gray,grayer,...,0=black*/
                   14176:       } /* --- end-of-for(igray) --- */
                   14177:   /* --- set gif color#0 (background) transparent --- */
                   14178:   if ( istransparent )			/* transparent background wanted */
                   14179:     GIF_SetTransparent(0);		/* set transparent background */
                   14180:   if (msgfp!=NULL && msglevel>=9) fflush(msgfp); /*flush debugging output*/
                   14181:   /* --- emit compressed gif image (to stdout or cache file) --- */
                   14182:   GIF_CompressImage(0, 0, -1, -1, GetPixel); /* emit gif */
                   14183:   GIF_Close();				/* close file */
1.2       albertel 14184:   if ( msgfp!=NULL && msglevel>=9 )
                   14185:     { fprintf(msgfp,"main> created gifSize=%d\n", gifSize);
                   14186:       fflush(msgfp); }
                   14187:   /* --- may need to emit image from cached file or from memory --- */
                   14188:   if ( isquery				/* have an actual query string */
                   14189:   ||   isdumpimage			/* or dumping image */
                   14190:   ||   msglevel >= 99 ) {		/* or debugging */
                   14191:   int maxage2 = (isdumpimage?(-1):maxage); /* no headers if dumping image */
                   14192:    if ( iscaching )			/* caching enabled */
                   14193:      emitcache(cachefile,maxage2,0);	/* cached image (hopefully) emitted*/
                   14194:    else if ( isinmemory )		/* or emit image from memory buffer*/
                   14195:      emitcache(gif_buffer,maxage2,1); }	/* emitted from memory buffer */
                   14196:   /* --- for testing, may need to write image buffer to file --- */
                   14197:   if ( isdumpbuffer > 99 )		/* gif image in memory buffer */
                   14198:    if ( gifSize > 0 )			/* and it's not an empty buffer */
                   14199:     { FILE *dumpfp = fopen("mimetex.gif","wb"); /* dump to mimetex.gif */
                   14200:       if ( dumpfp != NULL )		/* file opened successfully */
                   14201: 	{ fwrite(gif_buffer,sizeof(unsigned char),gifSize,dumpfp); /*write*/
                   14202: 	  fclose(dumpfp); }		/* and close file */
                   14203:     } /* --- end-of-if(isdumpbuffer>99) --- */
1.1       albertel 14204:  #else
                   14205:  /* ------------------------------------------------------------------------
                   14206:  emit mime XBITMAP image
                   14207:  ------------------------------------------------------------------------- */
                   14208:   xbitmap_raster(bp,stdout);		/* default emits mime xbitmap */
                   14209:  #endif
                   14210:  } /* --- end-of-if(isquery) --- */
                   14211: /* --- exit --- */
                   14212: end_of_job:
1.2       albertel 14213:   if ( !isss )				/*bytemap raster in sp for supersamp*/
                   14214:    if ( bytemap_raster != NULL ) free(bytemap_raster);/*free bytemap_raster*/
1.1       albertel 14215:   if (colormap_raster != NULL )free(colormap_raster); /*and colormap_raster*/
1.2       albertel 14216:   if ( 0 && gif_buffer != NULL ) free(gif_buffer); /* free malloced buffer */
                   14217:   if ( 1 && sp != NULL ) delete_subraster(sp);	/* and free expression */
1.1       albertel 14218:   if ( msgfp != NULL			/* have message/log file open */
                   14219:   &&   msgfp != stdout )		/* and it's not stdout */
1.2       albertel 14220:    { fprintf(msgfp,"mimeTeX> successful end-of-job at %s\n",
                   14221:        timestamp(TZDELTA,0));
1.1       albertel 14222:      fprintf(msgfp,"%s\n",dashes);	/* so log separator line */
                   14223:      fclose(msgfp); }			/* and close logfile */
1.2       albertel 14224:   /* --- dump memory leaks in debug window if in MS VC++ debug mode --- */
                   14225:   #if defined(_CRTDBG_MAP_ALLOC)
                   14226:     _CrtDumpMemoryLeaks();
                   14227:   #endif
                   14228:   /* --- exit() if not running as Windows DLL (see CreateGifFromEq()) --- */
                   14229:   #if !defined(_USRDLL)
                   14230:     exit ( 0 );
                   14231:   #endif
1.1       albertel 14232: } /* --- end-of-function main() --- */
                   14233: 
                   14234: /* ==========================================================================
1.2       albertel 14235:  * Function:	CreateGifFromEq ( expression, gifFileName )
                   14236:  * Purpose:	shortcut method to create GIF file for expression,
                   14237:  *		with antialising and all other capabilities
                   14238:  * --------------------------------------------------------------------------
                   14239:  * Arguments:	expression (I)	char *ptr to null-terminated string
                   14240:  *				containing LaTeX expression to be rendred
                   14241:  *		gifFileName (I)	char *ptr to null-terminated string
                   14242:  *				containing name of output gif file
                   14243:  * --------------------------------------------------------------------------
                   14244:  * Returns:	( int )		exit value from main (0 if successful)
                   14245:  * --------------------------------------------------------------------------
                   14246:  * Notes:     o	This function is the entry point when mimeTeX is built
                   14247:  *		as a Win32 DLL rather then a standalone app or CGI
                   14248:  *	      o	Contributed to mimeTeX by Shital Shah.  See his homepage
                   14249:  *		  http://www.shitalshah.com
                   14250:  *	      o	Shital discusses the mimeTeX Win32 DLL project at
                   14251:  *		  http://www.codeproject.com/dotnet/Eq2Img.asp
                   14252:  *		and you can download his latest code from
                   14253:  *		  http://www.shitalshah.com/dev/eq2img_all.zip
                   14254:  * ======================================================================= */
                   14255: /* --- include function to expose Win32 DLL to outside world --- */
                   14256: #if defined(_USRDLL)
                   14257:   extern _declspec(dllexport)int _cdecl
                   14258: 	CreateGifFromEq ( char *expression, char *gifFileName );
                   14259: #endif
                   14260: /* --- entry point --- */
                   14261: int	CreateGifFromEq ( char *expression, char *gifFileName )
                   14262: {
                   14263: /* -------------------------------------------------------------------------
                   14264: Allocations and Declarations
                   14265: -------------------------------------------------------------------------- */
                   14266: int	main();			/* main() akways returns an int */
                   14267: /* --- set constants --- */
                   14268: int	argc = 4;		/* count of args supplied to main() */
                   14269: char	*argv[5] =		/* command line args to run with -e option */
                   14270: 	  { "MimeTeXWin32DLL", "-e", /* constant args */
                   14271: 	    /*gifFileName, expression,*/ NULL, NULL, NULL };
                   14272: /* --- set argv[]'s not computable at load time --- */
                   14273: argv[2] = gifFileName;		/* args are -e gifFileName */
                   14274: argv[3] = expression;		/* and now  -e gifFileName expression */
                   14275: /* -------------------------------------------------------------------------
                   14276: Run mimeTeX in command-line mode with -e (export) option, and then return
                   14277: -------------------------------------------------------------------------- */
                   14278: return	main ( argc, argv
                   14279: 	  #ifdef DUMPENVP
                   14280: 	    , NULL
                   14281: 	  #endif
                   14282: 	) ;
                   14283: } /* --- end-of-function CreateGifFromEq() --- */
                   14284: 
                   14285: /* ==========================================================================
1.1       albertel 14286:  * Function:	isstrstr ( char *string, char *snippets, int iscase )
                   14287:  * Purpose:	determine whether any substring of 'string'
                   14288:  *		matches any of the comma-separated list of 'snippets',
                   14289:  *		ignoring case if iscase=0.
                   14290:  * --------------------------------------------------------------------------
                   14291:  * Arguments:	string (I)	char * containing null-terminated
                   14292:  *				string that will be searched for
                   14293:  *				any one of the specified snippets
                   14294:  *		snippets (I)	char * containing null-terminated,
                   14295:  *				comma-separated list of snippets
                   14296:  *				to be searched for in string
                   14297:  *		iscase (I)	int containing 0 for case-insensitive
                   14298:  *				comparisons, or 1 for case-sensitive
                   14299:  * --------------------------------------------------------------------------
                   14300:  * Returns:	( int )		1 if any snippet is a substring of
                   14301:  *				string, 0 if not
                   14302:  * --------------------------------------------------------------------------
                   14303:  * Notes:     o
                   14304:  * ======================================================================= */
                   14305: /* --- entry point --- */
                   14306: int	isstrstr ( char *string, char *snippets, int iscase )
                   14307: {
                   14308: /* -------------------------------------------------------------------------
                   14309: Allocations and Declarations
                   14310: -------------------------------------------------------------------------- */
                   14311: int	status = 0;			/*1 if any snippet found in string*/
                   14312: char	snip[99], *snipptr = snippets,	/* munge through each snippet */
                   14313: 	delim = ',', *delimptr = NULL;	/* separated by delim's */
                   14314: char	stringcp[999], *cp = stringcp;	/*maybe lowercased copy of string*/
                   14315: /* -------------------------------------------------------------------------
                   14316: initialization
                   14317: -------------------------------------------------------------------------- */
                   14318: /* --- arg check --- */
                   14319: if ( string==NULL || snippets==NULL ) goto end_of_job; /* missing arg */
                   14320: if ( *string=='\000' || *snippets=='\000' ) goto end_of_job; /* empty arg */
                   14321: /* --- copy string and lowercase it if case-insensitive --- */
                   14322: strcpy(stringcp,string);		/* local copy of string */
                   14323: if ( !iscase )				/* want case-insensitive compares */
                   14324:   for ( cp=stringcp; *cp != '\000'; cp++ ) /* so for each string char */
                   14325:     if ( isupper(*cp) ) *cp = tolower(*cp); /*lowercase any uppercase chars*/
                   14326: /* -------------------------------------------------------------------------
                   14327: extract each snippet and see if it's a substring of string
                   14328: -------------------------------------------------------------------------- */
                   14329: while ( snipptr != NULL )		/* while we still have snippets */
                   14330:   {
                   14331:   /* --- extract next snippet --- */
                   14332:   if ( (delimptr = strchr(snipptr,delim)) /* locate next comma delim */
                   14333:   ==   NULL )				/*not found following last snippet*/
                   14334:     { strcpy(snip,snipptr);		/* local copy of last snippet */
                   14335:       snipptr = NULL; }			/* signal end-of-string */
                   14336:   else					/* snippet ends just before delim */
                   14337:     { int sniplen = (int)(delimptr-snipptr) - 1;  /* #chars in snippet */
                   14338:       memcpy(snip,snipptr,sniplen);	/* local copy of snippet chars */
                   14339:       snip[sniplen] = '\000';		/* null-terminated snippet */
                   14340:       snipptr = delimptr + 1; }		/* next snippet starts after delim */
                   14341:   /* --- lowercase snippet if case-insensitive --- */
                   14342:   if ( !iscase )			/* want case-insensitive compares */
                   14343:     for ( cp=snip; *cp != '\000'; cp++ ) /* so for each snippet char */
                   14344:       if ( isupper(*cp) ) *cp=tolower(*cp); /*lowercase any uppercase chars*/
                   14345:   /* --- check if snippet in string --- */
                   14346:   if ( strstr(stringcp,snip) != NULL )	/* found snippet in string */
                   14347:     { status = 1;			/* so reset return status */
                   14348:       break; }				/* no need to check any further */
                   14349:   } /* --- end-of-while(*snipptr!=0) --- */
                   14350: end_of_job: return ( status );		/*1 if snippet found in list, else 0*/
                   14351: } /* --- end-of-function isstrstr() --- */
                   14352: 
                   14353: /* ==========================================================================
                   14354:  * Function:	ismonth ( char *month )
                   14355:  * Purpose:	returns 1 if month contains current month "jan"..."dec".
                   14356:  * --------------------------------------------------------------------------
                   14357:  * Arguments:	month (I)	char * containing null-terminated string
                   14358:  *				in which "jan"..."dec" is (putatively)
                   14359:  *				contained as a substring.
                   14360:  * --------------------------------------------------------------------------
                   14361:  * Returns:	( int )		1 if month contains current month,
                   14362:  *				0 otherwise
                   14363:  * --------------------------------------------------------------------------
                   14364:  * Notes:     o	There's a three day "grace period", e.g., Dec 3 mtaches Nov.
                   14365:  * ======================================================================= */
                   14366: /* --- entry point --- */
                   14367: int	ismonth ( char *month )
                   14368: {
                   14369: /* -------------------------------------------------------------------------
                   14370: Allocations and Declarations
                   14371: -------------------------------------------------------------------------- */
                   14372: int	isokay = 0;			/*1 if month contains current month*/
                   14373: /*long	time_val = 0L;*/		/* binary value returned by time() */
                   14374: time_t	time_val = (time_t)(0);		/* binary value returned by time() */
                   14375: struct tm *tmstruct=(struct tm *)NULL, *localtime(); /* interpret time_val */
                   14376: int	imonth, mday;			/* current month 1-12 and day 1-31 */
                   14377: int	ngrace = 3;			/* grace period */
                   14378: char	lcmonth[128]="\000"; int i=0;	/* lowercase month */
                   14379: static	char *months[] =		/* month must contain current one */
                   14380:    {"dec","jan","feb","mar","apr","may","jun",
                   14381:     "jul","aug","sep","oct","nov","dec","jan"};
                   14382: /* -------------------------------------------------------------------------
                   14383: get current date:time info, and check month
                   14384: -------------------------------------------------------------------------- */
                   14385: /* --- lowercase input month --- */
                   14386: if ( month != NULL )			/* check that we got input */
                   14387:   for ( i=0; i<120 && *month!='\000'; i++,month++ ) /* go thru month chars */
                   14388:     lcmonth[i] = tolower(*month);	/* lowerase each char in month */
                   14389: if ( i < 2 ) goto end_of_job;		/* must be invalid input */
                   14390: lcmonth[i] = '\000';			/* null-terminate lcmonth[] */
                   14391: /* --- get current date:time --- */
                   14392: time((time_t *)(&time_val));		/* get date and time */
                   14393: tmstruct = localtime((time_t *)(&time_val)); /* interpret time_val */
                   14394: /* --- month and day  --- */
                   14395: imonth = 1 + (int)(tmstruct->tm_mon);	/* 1=jan ... 12=dec */
                   14396: mday = (int)(tmstruct->tm_mday);	/* 1-31 */
                   14397: if ( imonth<1 || imonth>12		/* quit if month out-of-range */
                   14398: ||   mday<0 || mday>31 ) goto end_of_job; /* or date out of range */
                   14399: /* --- check input month against current date --- */
                   14400: if ( strstr(lcmonth,months[imonth]) != NULL ) isokay = 1; /* current month */
                   14401: if ( mday <= ngrace )			/* 1-3 within grace period */
                   14402:  if ( strstr(lcmonth,months[imonth-1]) != NULL ) isokay = 1; /* last month */
                   14403: if ( mday >= 31-ngrace )		/* 28-31 within grace period */
                   14404:  if ( strstr(lcmonth,months[imonth+1]) != NULL ) isokay = 1; /* next month */
                   14405: end_of_job:
                   14406:   return ( isokay );			/*1 if month contains current month*/
                   14407: } /* --- end-of-function ismonth() --- */
                   14408: 
                   14409: /* ==========================================================================
                   14410:  * Functions:	int  unescape_url ( char *url, int isescape )
                   14411:  *		char x2c ( char *what )
                   14412:  * Purpose:	unescape_url replaces 3-character sequences %xx in url
                   14413:  *		    with the single character represented by hex xx.
                   14414:  *		x2c returns the single character represented by hex xx
                   14415:  *		    passed as a 2-character sequence in what.
                   14416:  * --------------------------------------------------------------------------
                   14417:  * Arguments:	url (I)		char * containing null-terminated
                   14418:  *				string with embedded %xx sequences
                   14419:  *				to be converted.
                   14420:  *		isescape (I)	int containing 1 to _not_ unescape
                   14421:  *				\% sequences (0 would be NCSA default)
                   14422:  *		what (I)	char * whose first 2 characters are
                   14423:  *				interpreted as ascii representations
                   14424:  *				of hex digits.
                   14425:  * --------------------------------------------------------------------------
                   14426:  * Returns:	( int )		unescape_url always returns 0.
                   14427:  *		( char )	x2c returns the single char
                   14428:  *				corresponding to hex xx passed in what.
                   14429:  * --------------------------------------------------------------------------
                   14430:  * Notes:     o	These two functions were taken verbatim from util.c in
                   14431:  *   ftp://ftp.ncsa.uiuc.edu/Web/httpd/Unix/ncsa_httpd/cgi/ncsa-default.tar.Z
                   14432:  *	      o	Not quite "verbatim" -- I added the "isescape logic" 4-Dec-03
                   14433:  *		so unescape_url() can be safely applied to input which may or
1.3     ! albertel 14434:  *		may not have been url-encoded.  (Note: currently, all calls
        !          14435:  *		to unescape_url() pass iescape=0, so it's not used.)
        !          14436:  *	      o	Added +++'s to blank xlation on 24-Sep-06
        !          14437:  *	      o	Added ^M,^F,etc to blank xlation 0n 01-Oct-06
1.1       albertel 14438:  * ======================================================================= */
                   14439: /* --- entry point --- */
                   14440: int unescape_url(char *url, int isescape) {
                   14441:     int x=0,y=0,prevescape=0,gotescape=0;
1.3     ! albertel 14442:     int xlateplus = (isplusblank==1?1:0); /* true to xlate plus to blank */
        !          14443:     int strreplace();			/* replace + with blank, if needed */
1.1       albertel 14444:     char x2c();
                   14445:     static char *hex="0123456789ABCDEFabcdef";
1.3     ! albertel 14446:     /* ---
        !          14447:      * xlate ctrl chars to blanks
        !          14448:      * -------------------------- */
        !          14449:     if ( 1 ) {				/* xlate ctrl chars to blanks */
        !          14450:       char *ctrlchars = "\n\t\v\b\r\f\a\015";
        !          14451:       int  seglen = strspn(url,ctrlchars); /*initial segment with ctrlchars*/
        !          14452:       int  urllen = strlen(url);	/* total length of url string */
        !          14453:       /* --- first, entirely remove ctrlchars from beginning and end --- */
        !          14454:       if ( seglen > 0 ) {		/*have ctrlchars at start of string*/
        !          14455: 	strcpy(url,url+seglen);		/* squeeze out initial ctrlchars */
        !          14456: 	urllen -= seglen; }		/* string is now shorter */
        !          14457:       while ( --urllen >= 0 )		/* now remove ctrlchars from end */
        !          14458: 	if ( isthischar(url[urllen],ctrlchars) ) /* ctrlchar at end */
        !          14459: 	  url[urllen] = '\000';		/* re-terminate string before it */
        !          14460: 	else break;			/* or we're done */
        !          14461:       urllen++;				/* length of url string */
        !          14462:       /* --- now, replace interior ctrlchars with ~ blanks --- */
        !          14463:       while ( (seglen=strcspn(url,ctrlchars)) < urllen ) /*found a ctrlchar*/
        !          14464: 	url[seglen] = '~';		/* replace ctrlchar with ~ */
        !          14465:       } /* --- end-of-if(1) --- */
        !          14466:     /* ---
        !          14467:      * xlate +'s to blanks if requested or if deemed necessary
        !          14468:      * ------------------------------------------------------- */
        !          14469:     if ( isplusblank == (-1) ) {	/*determine whether or not to xlate*/
        !          14470:       char *searchfor[] = { " ","%20", "%2B","%2b", "+++","++",
        !          14471: 	"+=+","+-+", NULL };
        !          14472:       int  isearch = 0,			/* searchfor[] index */
        !          14473: 	   nfound[11] = {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}; /*#occurrences*/
        !          14474:       /* --- locate occurrences of searchfor[] strings in url --- */
        !          14475:       for ( isearch=0; searchfor[isearch] != NULL; isearch++ ) {
        !          14476: 	char *psearch = url;		/* start search at beginning */
        !          14477: 	nfound[isearch] = 0;		/* init #occurrences count */
        !          14478: 	while ( (psearch=strstr(psearch,searchfor[isearch])) != NULL ) {
        !          14479: 	  nfound[isearch] += 1;		/* count another occurrence */
        !          14480: 	  psearch += strlen(searchfor[isearch]); } /*resume search after it*/
        !          14481: 	} /* --- end-of-for(isearch) --- */
        !          14482:       /* --- apply some common-sense logic --- */
        !          14483:       if ( nfound[0] + nfound[1] > 0 )	/* we have actual " "s or "%20"s */
        !          14484: 	isplusblank = xlateplus = 0;	/* so +++'s aren't blanks */
        !          14485:       if ( nfound[2] + nfound[3] > 0 ) { /* we have "%2B" for +++'s */
        !          14486:         if ( isplusblank != 0 )		/* and haven't disabled xlation */
        !          14487: 	  isplusblank = xlateplus = 1;	/* so +++'s are blanks */
        !          14488: 	else				/* we have _both_ "%20" and "%2b" */
        !          14489: 	  xlateplus = 0; }		/* tough call */
        !          14490:       if ( nfound[4] + nfound[5] > 0	/* we have multiple ++'s */
        !          14491:       ||   nfound[6] + nfound[7] > 0 )	/* or we have a +=+ or +-+ */
        !          14492: 	if ( isplusblank != 0 )		/* and haven't disabled xlation */
        !          14493: 	  xlateplus = 1;		/* so xlate +++'s to blanks */
        !          14494:       } /* --- end-of-if(isplusblank==-1) --- */
        !          14495:     if ( xlateplus > 0 ) {		/* want +'s xlated to blanks */
        !          14496:       char *xlateto[] = { ""," "," "," + "," "," "," "," "," " };
        !          14497:       while ( xlateplus > 0 ) {		/* still have +++'s to xlate */
        !          14498: 	char plusses[99] = "++++++++++++++++++++"; /* longest +++ string */
        !          14499: 	plusses[xlateplus] = '\000';	/* null-terminate +++'s */
        !          14500: 	strreplace(url,plusses,xlateto[xlateplus],0); /* xlate +++'s */
        !          14501: 	xlateplus--;			/* next shorter +++ string */
        !          14502: 	} /* --- end-of-while(xlateplus>0) --- */
        !          14503:       } /* --- end-of-if(xlateplus) --- */
        !          14504:     isplusblank = 0;			/* don't iterate this xlation */
        !          14505:     /* ---
        !          14506:      * xlate %nn to corresponding char
        !          14507:      * ------------------------------- */
1.1       albertel 14508:     for(;url[y];++x,++y) {
                   14509: 	gotescape = prevescape;
                   14510: 	prevescape = (url[x]=='\\');
                   14511: 	if((url[x] = url[y]) == '%')
                   14512: 	 if(!isescape || !gotescape)
                   14513: 	  if(isthischar(url[y+1],hex)
                   14514: 	  && isthischar(url[y+2],hex))
                   14515: 	    { url[x] = x2c(&url[y+1]);
                   14516: 	      y+=2; }
                   14517:     }
                   14518:     url[x] = '\0';
                   14519:     return 0;
                   14520: } /* --- end-of-function unescape_url() --- */
                   14521: /* --- entry point --- */
                   14522: char x2c(char *what) {
                   14523:     char digit;
                   14524:     digit = (what[0] >= 'A' ? ((what[0] & 0xdf) - 'A')+10 : (what[0] - '0'));
                   14525:     digit *= 16;
                   14526:     digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A')+10 : (what[1] - '0'));
                   14527:     return(digit);
                   14528: } /* --- end-of-function x2c() --- */
                   14529: 
                   14530: /* ==========================================================================
                   14531:  * Function:	logger ( fp, msglevel, message, logvars )
                   14532:  * Purpose:	Logs the environment variables specified in logvars
                   14533:  *		to fp if their msglevel is >= the passed msglevel.
                   14534:  * --------------------------------------------------------------------------
                   14535:  * Arguments:	fp (I)		FILE * to file containing log
                   14536:  *		msglevel (I)	int containing logging message level
                   14537:  *		message (I)	char * to optional message, or NULL
                   14538:  *		logvars (I)	logdata * to array of environment variables
                   14539:  *				to be logged
                   14540:  * --------------------------------------------------------------------------
                   14541:  * Returns:	( int )		number of variables from logvars
                   14542:  *				that were actually logged
                   14543:  * --------------------------------------------------------------------------
                   14544:  * Notes:     o
                   14545:  * ======================================================================= */
                   14546: /* --- entry point --- */
                   14547: int	logger ( FILE *fp, int msglevel, char *message, logdata *logvars )
                   14548: {
                   14549: /* -------------------------------------------------------------------------
                   14550: Allocations and Declarations
                   14551: -------------------------------------------------------------------------- */
                   14552: int	ilog=0, nlogged=0;		/* logvars[] index, #vars logged */
                   14553: char	*timestamp();			/* timestamp logged */
                   14554: char	*value = NULL;			/* getenv(name) to be logged */
                   14555: /* -------------------------------------------------------------------------
                   14556: Log each variable
                   14557: -------------------------------------------------------------------------- */
1.2       albertel 14558: fprintf(fp,"%s\n",timestamp(TZDELTA,0)); /*emit timestamp before first var*/
1.1       albertel 14559: if ( message != NULL )			/* optional message supplied */
                   14560:  fprintf(fp,"  MESSAGE = %s\n",message); /* emit caller-supplied message */
                   14561: if ( logvars != (logdata *)NULL )	/* have logvars */
                   14562:  for ( ilog=0; logvars[ilog].name != NULL; ilog++ )  /* till end-of-table */
                   14563:   if ( msglevel >= logvars[ilog].msglevel ) /* check msglevel for this var */
                   14564:    if ( (value=getenv(logvars[ilog].name))  /* getenv(name) to be logged */
                   14565:    != NULL )				/* check that name exists */
                   14566:     {
                   14567:     fprintf(fp,"  %s = %.*s\n",		/* emit variable name = value */
                   14568:      logvars[ilog].name,logvars[ilog].maxlen,value);
                   14569:     nlogged++;				/* bump #vars logged */
                   14570:     } /* --- end-of-for(ilog) --- */
                   14571: return ( nlogged );			/* back to caller */
                   14572: } /* --- end-of-function logger() --- */
                   14573: 
                   14574: /* ==========================================================================
1.2       albertel 14575:  * Function:	emitcache ( cachefile, maxage, isbuffer )
1.1       albertel 14576:  * Purpose:	dumps bytes from cachefile to stdout
                   14577:  * --------------------------------------------------------------------------
                   14578:  * Arguments:	cachefile (I)	pointer to null-terminated char string
1.2       albertel 14579:  *				containing full path to file to be dumped,
                   14580:  *				or contains buffer of bytes to be dumped
                   14581:  *		maxage (I)	int containing maxage. in seconds, for
                   14582:  *				http header, or -1 to not emit headers
                   14583:  *		isbuffer (I)	1 if cachefile is buffer of bytes to be
                   14584:  *				dumped
1.1       albertel 14585:  * --------------------------------------------------------------------------
                   14586:  * Returns:	( int )		#bytes dumped (0 signals error)
                   14587:  * --------------------------------------------------------------------------
                   14588:  * Notes:     o
                   14589:  * ======================================================================= */
                   14590: /* --- entry point --- */
1.2       albertel 14591: int	emitcache ( char *cachefile, int maxage, int isbuffer )
1.1       albertel 14592: {
                   14593: /* -------------------------------------------------------------------------
                   14594: Allocations and Declarations
                   14595: -------------------------------------------------------------------------- */
1.2       albertel 14596: int	nbytes=gifSize, readcachefile(); /* read cache file */
                   14597: FILE	*emitptr = stdout;		/* emit cachefile to stdout */
1.3     ! albertel 14598: unsigned char buffer[MAXGIFSZ+1];	/* bytes from cachefile */
1.2       albertel 14599: unsigned char *buffptr = buffer;	/* ptr to buffer */
1.1       albertel 14600: /* -------------------------------------------------------------------------
                   14601: initialization
                   14602: -------------------------------------------------------------------------- */
                   14603: /* --- check that files opened okay --- */
1.2       albertel 14604: if ( emitptr == (FILE *)NULL )		/* failed to open emit file */
1.1       albertel 14605:   goto end_of_job;			/* so return 0 bytes to caller */
1.2       albertel 14606: /* --- read the file if necessary --- */
                   14607: if ( isbuffer )				/* cachefile is buffer */
                   14608:  buffptr = (unsigned char *)cachefile;	/* so reset buffer pointer */
                   14609: else					/* cachefile is file name */
                   14610:  if ( (nbytes = readcachefile(cachefile,buffer)) /* read the file */
                   14611:  < 1 ) goto end_of_job;			/* quit if file not read */
                   14612: /* --- first emit http headers if requested --- */
1.3     ! albertel 14613: if ( isemitcontenttype			/* content-type lines enabled */
        !          14614: &&   maxage >= 0 )			/* caller wants http headers */
1.2       albertel 14615:  { /* --- emit mime content-type line --- */
                   14616:    fprintf( emitptr, "Cache-Control: max-age=%d\n",maxage );
                   14617:    fprintf( emitptr, "Content-Length: %d\n",nbytes );
                   14618:    fprintf( emitptr, "Content-type: image/gif\n\n" ); }
                   14619: /* -------------------------------------------------------------------------
                   14620: set stdout to binary mode (for Windows)
                   14621: -------------------------------------------------------------------------- */
1.1       albertel 14622: /* emitptr = fdopen(STDOUT_FILENO,"wb"); */  /* doesn't work portably, */
                   14623: #ifdef WINDOWS				/* so instead... */
                   14624:   #ifdef HAVE_SETMODE			/* prefer (non-portable) setmode() */
                   14625:     if ( setmode ( fileno (stdout), O_BINARY) /* windows specific call */
                   14626:     == -1 ) ; /* handle error */	/* sets stdout to binary mode */
                   14627:   #else					/* setmode() not available */
                   14628:     #if 1
                   14629:       freopen ("CON", "wb", stdout);	/* freopen() stdout binary */
                   14630:     #else
                   14631:       stdout = fdopen (STDOUT_FILENO, "wb"); /* fdopen() stdout binary */
                   14632:     #endif
                   14633:   #endif
                   14634: #endif
                   14635: /* -------------------------------------------------------------------------
                   14636: emit bytes from cachefile
                   14637: -------------------------------------------------------------------------- */
1.2       albertel 14638: /* --- write bytes to stdout --- */
                   14639: if ( fwrite(buffptr,sizeof(unsigned char),nbytes,emitptr) /* write buffer */
                   14640: <    nbytes )				/* failed to write all bytes */
                   14641:   nbytes = 0;				/* reset total count to 0 */
                   14642: end_of_job:
                   14643:   return ( nbytes );			/* back with #bytes emitted */
                   14644: } /* --- end-of-function emitcache() --- */
                   14645: 
                   14646: /* ==========================================================================
                   14647:  * Function:	readcachefile ( cachefile, buffer )
                   14648:  * Purpose:	read cachefile into buffer
                   14649:  * --------------------------------------------------------------------------
                   14650:  * Arguments:	cachefile (I)	pointer to null-terminated char string
                   14651:  *				containing full path to file to be read
                   14652:  *		buffer (O)	pointer to unsigned char string
                   14653:  *				returning contents of cachefile
                   14654:  *				(max 64000 bytes)
                   14655:  * --------------------------------------------------------------------------
                   14656:  * Returns:	( int )		#bytes read (0 signals error)
                   14657:  * --------------------------------------------------------------------------
                   14658:  * Notes:     o
                   14659:  * ======================================================================= */
                   14660: /* --- entry point --- */
                   14661: int	readcachefile ( char *cachefile, unsigned char *buffer )
                   14662: {
                   14663: /* -------------------------------------------------------------------------
                   14664: Allocations and Declarations
                   14665: -------------------------------------------------------------------------- */
                   14666: FILE	*cacheptr = fopen(cachefile,"rb"); /*open cachefile for binary read*/
                   14667: unsigned char cachebuff[64];		/* bytes from cachefile */
                   14668: int	buflen = 32,			/* #bytes we try to read from file */
                   14669: 	nread = 0,			/* #bytes actually read from file */
1.3     ! albertel 14670: 	maxbytes = MAXGIFSZ,		/* max #bytes returned in buffer */
1.2       albertel 14671: 	nbytes = 0;			/* total #bytes read */
                   14672: /* -------------------------------------------------------------------------
                   14673: initialization
                   14674: -------------------------------------------------------------------------- */
                   14675: /* --- check that files opened okay --- */
                   14676: if ( cacheptr == (FILE *)NULL ) goto end_of_job; /*failed to open cachefile*/
                   14677: /* --- check that output buffer provided --- */
                   14678: if ( buffer == (unsigned char *)NULL ) goto end_of_job; /* no buffer */
                   14679: /* -------------------------------------------------------------------------
                   14680: read bytes from cachefile
                   14681: -------------------------------------------------------------------------- */
1.1       albertel 14682: while ( 1 )
                   14683:   {
                   14684:   /* --- read bytes from cachefile --- */
1.2       albertel 14685:   nread = fread(cachebuff,sizeof(unsigned char),buflen,cacheptr); /* read */
                   14686:   if ( nbytes + nread > maxbytes )	/* block too big for buffer */
                   14687:     nread = maxbytes - nbytes;		/* so truncate it */
1.1       albertel 14688:   if ( nread < 1 ) break;		/* no bytes left in cachefile */
1.2       albertel 14689:   /* --- store bytes in buffer --- */
                   14690:   memcpy(buffer+nbytes,cachebuff,nread); /* copy current block to buffer */
                   14691:   /* --- ready to read next block --- */
1.1       albertel 14692:   nbytes += nread;			/* bump total #bytes emitted */
                   14693:   if ( nread < buflen ) break;		/* no bytes left in cachefile */
1.2       albertel 14694:   if ( nbytes >= maxbytes ) break;	/* avoid buffer overflow */
1.1       albertel 14695:   } /* --- end-of-while(1) --- */
                   14696: end_of_job:
                   14697:   if ( cacheptr != NULL ) fclose(cacheptr); /* close file if opened */
                   14698:   return ( nbytes );			/* back with #bytes emitted */
1.2       albertel 14699: } /* --- end-of-function readcachefile() --- */
1.1       albertel 14700: 
                   14701: /* ==========================================================================
                   14702:  * Function:	md5str ( instr )
                   14703:  * Purpose:	returns null-terminated character string containing
                   14704:  *		md5 hash of instr (input string)
                   14705:  * --------------------------------------------------------------------------
                   14706:  * Arguments:	instr (I)	pointer to null-terminated char string
                   14707:  *				containing input string whose md5 hash
                   14708:  *				is desired
                   14709:  * --------------------------------------------------------------------------
                   14710:  * Returns:	( char * )	ptr to null-terminated 32-character
                   14711:  *				md5 hash of instr
                   14712:  * --------------------------------------------------------------------------
                   14713:  * Notes:     o	Other md5 library functions are included below.
                   14714:  *		They're all taken from Christophe Devine's code,
                   14715:  *		which (as of 04-Aug-2004) is available from
                   14716:  *		     http://www.cr0.net:8040/code/crypto/md5/
                   14717:  *	      o	The P,F,S macros in the original code are replaced
                   14718:  *		by four functions P1()...P4() to accommodate a problem
                   14719:  *		with Compaq's vax/vms C compiler.
                   14720:  * ======================================================================= */
                   14721: /* --- #include "md5.h" --- */
                   14722: #ifndef uint8
                   14723:   #define uint8  unsigned char
                   14724: #endif
                   14725: #ifndef uint32
                   14726:   #define uint32 unsigned long int
                   14727: #endif
                   14728: typedef struct
                   14729:   { uint32 total[2];
                   14730:     uint32 state[4];
                   14731:     uint8 buffer[64];
                   14732:   } md5_context;
                   14733: void md5_starts( md5_context *ctx );
                   14734: void md5_update( md5_context *ctx, uint8 *input, uint32 length );
                   14735: void md5_finish( md5_context *ctx, uint8 digest[16] );
                   14736: /* --- md5.h --- */
                   14737: #define GET_UINT32(n,b,i)                       \
                   14738:   { (n) = ( (uint32) (b)[(i)    ]       )       \
                   14739:         | ( (uint32) (b)[(i) + 1] <<  8 )       \
                   14740:         | ( (uint32) (b)[(i) + 2] << 16 )       \
                   14741:         | ( (uint32) (b)[(i) + 3] << 24 ); }
                   14742: #define PUT_UINT32(n,b,i)                       \
                   14743:   { (b)[(i)    ] = (uint8) ( (n)       );       \
                   14744:     (b)[(i) + 1] = (uint8) ( (n) >>  8 );       \
                   14745:     (b)[(i) + 2] = (uint8) ( (n) >> 16 );       \
                   14746:     (b)[(i) + 3] = (uint8) ( (n) >> 24 ); }
                   14747: /* --- P,S,F macros defined as functions --- */
                   14748: void P1(uint32 *X,uint32 *a,uint32 b,uint32 c,uint32 d,int k,int s,uint32 t)
                   14749:   { *a += (uint32)(d ^ (b & (c ^ d))) + X[k] + t;
                   14750:     *a  = ((*a<<s) | ((*a & 0xFFFFFFFF) >> (32-s))) + b;
                   14751:     return; }
                   14752: void P2(uint32 *X,uint32 *a,uint32 b,uint32 c,uint32 d,int k,int s,uint32 t)
                   14753:   { *a += (uint32)(c ^ (d & (b ^ c))) + X[k] + t;
                   14754:     *a  = ((*a<<s) | ((*a & 0xFFFFFFFF) >> (32-s))) + b;
                   14755:     return; }
                   14756: void P3(uint32 *X,uint32 *a,uint32 b,uint32 c,uint32 d,int k,int s,uint32 t)
                   14757:   { *a += (uint32)(b ^ c ^ d) + X[k] + t;
                   14758:     *a  = ((*a<<s) | ((*a & 0xFFFFFFFF) >> (32-s))) + b;
                   14759:     return; }
                   14760: void P4(uint32 *X,uint32 *a,uint32 b,uint32 c,uint32 d,int k,int s,uint32 t)
                   14761:   { *a += (uint32)(c ^ (b | ~d)) + X[k] + t;
                   14762:     *a  = ((*a<<s) | ((*a & 0xFFFFFFFF) >> (32-s))) + b;
                   14763:     return; }
                   14764: 
                   14765: /* --- entry point (this one little stub written by me)--- */
                   14766: char *md5str( char *instr )
                   14767:   { static char outstr[64];
                   14768:     unsigned char md5sum[16];
                   14769:     md5_context ctx;
                   14770:     int j;
                   14771:     md5_starts( &ctx );
                   14772:     md5_update( &ctx, (uint8 *)instr, strlen(instr) );
                   14773:     md5_finish( &ctx, md5sum );
                   14774:     for( j=0; j<16; j++ )
                   14775:       sprintf( outstr + j*2, "%02x", md5sum[j] );
                   14776:     outstr[32] = '\000';
                   14777:     return ( outstr ); }
                   14778: 
                   14779: /* --- entry point (all md5 functions below by Christophe Devine) --- */
                   14780: void md5_starts( md5_context *ctx )
                   14781:   { ctx->total[0] = 0;
                   14782:     ctx->total[1] = 0;
                   14783:     ctx->state[0] = 0x67452301;
                   14784:     ctx->state[1] = 0xEFCDAB89;
                   14785:     ctx->state[2] = 0x98BADCFE;
                   14786:     ctx->state[3] = 0x10325476; }
                   14787: 
                   14788: void md5_process( md5_context *ctx, uint8 data[64] )
                   14789:   { uint32 X[16], A, B, C, D;
                   14790:     GET_UINT32( X[0],  data,  0 );
                   14791:     GET_UINT32( X[1],  data,  4 );
                   14792:     GET_UINT32( X[2],  data,  8 );
                   14793:     GET_UINT32( X[3],  data, 12 );
                   14794:     GET_UINT32( X[4],  data, 16 );
                   14795:     GET_UINT32( X[5],  data, 20 );
                   14796:     GET_UINT32( X[6],  data, 24 );
                   14797:     GET_UINT32( X[7],  data, 28 );
                   14798:     GET_UINT32( X[8],  data, 32 );
                   14799:     GET_UINT32( X[9],  data, 36 );
                   14800:     GET_UINT32( X[10], data, 40 );
                   14801:     GET_UINT32( X[11], data, 44 );
                   14802:     GET_UINT32( X[12], data, 48 );
                   14803:     GET_UINT32( X[13], data, 52 );
                   14804:     GET_UINT32( X[14], data, 56 );
                   14805:     GET_UINT32( X[15], data, 60 );
                   14806:     A = ctx->state[0];
                   14807:     B = ctx->state[1];
                   14808:     C = ctx->state[2];
                   14809:     D = ctx->state[3];
                   14810:     P1( X, &A, B, C, D,  0,  7, 0xD76AA478 );
                   14811:     P1( X, &D, A, B, C,  1, 12, 0xE8C7B756 );
                   14812:     P1( X, &C, D, A, B,  2, 17, 0x242070DB );
                   14813:     P1( X, &B, C, D, A,  3, 22, 0xC1BDCEEE );
                   14814:     P1( X, &A, B, C, D,  4,  7, 0xF57C0FAF );
                   14815:     P1( X, &D, A, B, C,  5, 12, 0x4787C62A );
                   14816:     P1( X, &C, D, A, B,  6, 17, 0xA8304613 );
                   14817:     P1( X, &B, C, D, A,  7, 22, 0xFD469501 );
                   14818:     P1( X, &A, B, C, D,  8,  7, 0x698098D8 );
                   14819:     P1( X, &D, A, B, C,  9, 12, 0x8B44F7AF );
                   14820:     P1( X, &C, D, A, B, 10, 17, 0xFFFF5BB1 );
                   14821:     P1( X, &B, C, D, A, 11, 22, 0x895CD7BE );
                   14822:     P1( X, &A, B, C, D, 12,  7, 0x6B901122 );
                   14823:     P1( X, &D, A, B, C, 13, 12, 0xFD987193 );
                   14824:     P1( X, &C, D, A, B, 14, 17, 0xA679438E );
                   14825:     P1( X, &B, C, D, A, 15, 22, 0x49B40821 );
                   14826:     P2( X, &A, B, C, D,  1,  5, 0xF61E2562 );
                   14827:     P2( X, &D, A, B, C,  6,  9, 0xC040B340 );
                   14828:     P2( X, &C, D, A, B, 11, 14, 0x265E5A51 );
                   14829:     P2( X, &B, C, D, A,  0, 20, 0xE9B6C7AA );
                   14830:     P2( X, &A, B, C, D,  5,  5, 0xD62F105D );
                   14831:     P2( X, &D, A, B, C, 10,  9, 0x02441453 );
                   14832:     P2( X, &C, D, A, B, 15, 14, 0xD8A1E681 );
                   14833:     P2( X, &B, C, D, A,  4, 20, 0xE7D3FBC8 );
                   14834:     P2( X, &A, B, C, D,  9,  5, 0x21E1CDE6 );
                   14835:     P2( X, &D, A, B, C, 14,  9, 0xC33707D6 );
                   14836:     P2( X, &C, D, A, B,  3, 14, 0xF4D50D87 );
                   14837:     P2( X, &B, C, D, A,  8, 20, 0x455A14ED );
                   14838:     P2( X, &A, B, C, D, 13,  5, 0xA9E3E905 );
                   14839:     P2( X, &D, A, B, C,  2,  9, 0xFCEFA3F8 );
                   14840:     P2( X, &C, D, A, B,  7, 14, 0x676F02D9 );
                   14841:     P2( X, &B, C, D, A, 12, 20, 0x8D2A4C8A );
                   14842:     P3( X, &A, B, C, D,  5,  4, 0xFFFA3942 );
                   14843:     P3( X, &D, A, B, C,  8, 11, 0x8771F681 );
                   14844:     P3( X, &C, D, A, B, 11, 16, 0x6D9D6122 );
                   14845:     P3( X, &B, C, D, A, 14, 23, 0xFDE5380C );
                   14846:     P3( X, &A, B, C, D,  1,  4, 0xA4BEEA44 );
                   14847:     P3( X, &D, A, B, C,  4, 11, 0x4BDECFA9 );
                   14848:     P3( X, &C, D, A, B,  7, 16, 0xF6BB4B60 );
                   14849:     P3( X, &B, C, D, A, 10, 23, 0xBEBFBC70 );
                   14850:     P3( X, &A, B, C, D, 13,  4, 0x289B7EC6 );
                   14851:     P3( X, &D, A, B, C,  0, 11, 0xEAA127FA );
                   14852:     P3( X, &C, D, A, B,  3, 16, 0xD4EF3085 );
                   14853:     P3( X, &B, C, D, A,  6, 23, 0x04881D05 );
                   14854:     P3( X, &A, B, C, D,  9,  4, 0xD9D4D039 );
                   14855:     P3( X, &D, A, B, C, 12, 11, 0xE6DB99E5 );
                   14856:     P3( X, &C, D, A, B, 15, 16, 0x1FA27CF8 );
                   14857:     P3( X, &B, C, D, A,  2, 23, 0xC4AC5665 );
                   14858:     P4( X, &A, B, C, D,  0,  6, 0xF4292244 );
                   14859:     P4( X, &D, A, B, C,  7, 10, 0x432AFF97 );
                   14860:     P4( X, &C, D, A, B, 14, 15, 0xAB9423A7 );
                   14861:     P4( X, &B, C, D, A,  5, 21, 0xFC93A039 );
                   14862:     P4( X, &A, B, C, D, 12,  6, 0x655B59C3 );
                   14863:     P4( X, &D, A, B, C,  3, 10, 0x8F0CCC92 );
                   14864:     P4( X, &C, D, A, B, 10, 15, 0xFFEFF47D );
                   14865:     P4( X, &B, C, D, A,  1, 21, 0x85845DD1 );
                   14866:     P4( X, &A, B, C, D,  8,  6, 0x6FA87E4F );
                   14867:     P4( X, &D, A, B, C, 15, 10, 0xFE2CE6E0 );
                   14868:     P4( X, &C, D, A, B,  6, 15, 0xA3014314 );
                   14869:     P4( X, &B, C, D, A, 13, 21, 0x4E0811A1 );
                   14870:     P4( X, &A, B, C, D,  4,  6, 0xF7537E82 );
                   14871:     P4( X, &D, A, B, C, 11, 10, 0xBD3AF235 );
                   14872:     P4( X, &C, D, A, B,  2, 15, 0x2AD7D2BB );
                   14873:     P4( X, &B, C, D, A,  9, 21, 0xEB86D391 );
                   14874:     ctx->state[0] += A;
                   14875:     ctx->state[1] += B;
                   14876:     ctx->state[2] += C;
                   14877:     ctx->state[3] += D; }
                   14878: 
                   14879: void md5_update( md5_context *ctx, uint8 *input, uint32 length )
                   14880:   { uint32 left, fill;
                   14881:     if( length < 1 ) return;
                   14882:     left = ctx->total[0] & 0x3F;
                   14883:     fill = 64 - left;
                   14884:     ctx->total[0] += length;
                   14885:     ctx->total[0] &= 0xFFFFFFFF;
                   14886:     if( ctx->total[0] < length )
                   14887:         ctx->total[1]++;
                   14888:     if( left && length >= fill )
                   14889:       { memcpy( (void *) (ctx->buffer + left),
                   14890:                 (void *) input, fill );
                   14891:         md5_process( ctx, ctx->buffer );
                   14892:         length -= fill;
                   14893:         input  += fill;
                   14894:         left = 0; }
                   14895:     while( length >= 64 )
                   14896:       { md5_process( ctx, input );
                   14897:         length -= 64;
                   14898:         input  += 64; }
                   14899:     if( length >= 1 )
                   14900:       memcpy( (void *) (ctx->buffer + left),
                   14901:               (void *) input, length ); }
                   14902: 
                   14903: void md5_finish( md5_context *ctx, uint8 digest[16] )
                   14904:   { static uint8 md5_padding[64] =
                   14905:      { 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                   14906:           0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                   14907:           0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                   14908:           0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
                   14909:     uint32 last, padn;
                   14910:     uint32 high, low;
                   14911:     uint8 msglen[8];
                   14912:     high = ( ctx->total[0] >> 29 )
                   14913:          | ( ctx->total[1] <<  3 );
                   14914:     low  = ( ctx->total[0] <<  3 );
                   14915:     PUT_UINT32( low,  msglen, 0 );
                   14916:     PUT_UINT32( high, msglen, 4 );
                   14917:     last = ctx->total[0] & 0x3F;
                   14918:     padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last );
                   14919:     md5_update( ctx, md5_padding, padn );
                   14920:     md5_update( ctx, msglen, 8 );
                   14921:     PUT_UINT32( ctx->state[0], digest,  0 );
                   14922:     PUT_UINT32( ctx->state[1], digest,  4 );
                   14923:     PUT_UINT32( ctx->state[2], digest,  8 );
                   14924:     PUT_UINT32( ctx->state[3], digest, 12 ); }
                   14925: /* --- end-of-function md5str() and "friends" --- */
                   14926: 
                   14927: #if defined(GIF)
                   14928: /* ==========================================================================
                   14929:  * Function:	GetPixel ( int x, int y )
                   14930:  * Purpose:	callback for GIF_CompressImage() returning the
                   14931:  *		pixel at column x, row y
                   14932:  * --------------------------------------------------------------------------
                   14933:  * Arguments:	x (I)		int containing column=0...width-1
                   14934:  *				of desired pixel
                   14935:  *		y (I)		int containing row=0...height-1
                   14936:  *				of desired pixel
                   14937:  * --------------------------------------------------------------------------
                   14938:  * Returns:	( int )		0 or 1, if pixel at x,y is off or on
                   14939:  * --------------------------------------------------------------------------
                   14940:  * Notes:     o
                   14941:  * ======================================================================= */
                   14942: /* --- entry point --- */
                   14943: int	GetPixel ( int x, int y )
                   14944: {
                   14945: int	ipixel = y*bitmap_raster->width + x; /* pixel index for x,y-coords*/
                   14946: int	pixval =0;			/* value of pixel */
                   14947: if ( !isaa )				/* use bitmap if not anti-aliased */
                   14948:   pixval = (int)getlongbit(bitmap_raster->pixmap,ipixel); /*pixel = 0 or 1*/
                   14949: else					/* else use anti-aliased grayscale*/
                   14950:   pixval = (int)(colormap_raster[ipixel]); /* colors[] index number */
                   14951: if ( msgfp!=NULL && msglevel>=9999 )	/* dump pixel */
                   14952:   { fprintf(msgfp,"GetPixel> x=%d, y=%d  pixel=%d\n",x,y,pixval);
                   14953:     fflush(msgfp); }
                   14954: return pixval;
                   14955: } /* --- end-of-function GetPixel() --- */
                   14956: #endif /* gif */
                   14957: #endif /* driver */
                   14958: #endif /* PART1 */
                   14959: /* ======================= END-OF-FILE MIMETEX.C ========================= */
                   14960: 

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