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

version 1.1, 2005/02/28 19:08:11 version 1.2, 2006/03/24 23:08:33
Line 1 Line 1
 /****************************************************************************  /****************************************************************************
  *   *
  * Copyright(c) 2002-2005, John Forkosh Associates, Inc. All rights reserved.   * Copyright(c) 2002-2006, John Forkosh Associates, Inc. All rights reserved.
  * --------------------------------------------------------------------------   * --------------------------------------------------------------------------
  * This file is part of mimeTeX, which is free software. You may redistribute   * This file is part of mimeTeX, which is free software. You may redistribute
  * and/or modify it under the terms of the GNU General Public License,   * and/or modify it under the terms of the GNU General Public License,
Line 58 Line 58
  * rastcat(sp1,sp2,isfree)                  concatanate sp1||sp2   * rastcat(sp1,sp2,isfree)                  concatanate sp1||sp2
  * rastack(sp1,sp2,base,space,iscenter,isfree)stack sp2 atop sp1   * rastack(sp1,sp2,base,space,iscenter,isfree)stack sp2 atop sp1
  * rastile(tiles,ntiles)      create composite raster from tiles   * rastile(tiles,ntiles)      create composite raster from tiles
  * rastsquash(sp1,sp2,xmin,ymin)    calc #squash pixels sp1||sp2   * rastsmash(sp1,sp2,xmin,ymin)      calc #smash pixels sp1||sp2
  * --- raster "drawing" functions ---   * --- raster "drawing" functions ---
  * accent_subraster(accent,width,height)       draw \hat\vec\etc   * accent_subraster(accent,width,height)       draw \hat\vec\etc
  * arrow_subraster(width,height,drctn,isBig)    left/right arrow   * arrow_subraster(width,height,drctn,isBig)    left/right arrow
Line 74 Line 74
  * type_raster(rp,fp)       emit ascii dump of rp on file ptr fp   * type_raster(rp,fp)       emit ascii dump of rp on file ptr fp
  * type_bytemap(bp,grayscale,width,height,fp) dump bytemap on fp   * type_bytemap(bp,grayscale,width,height,fp) dump bytemap on fp
  * xbitmap_raster(rp,fp)           emit mime xbitmap of rp on fp   * xbitmap_raster(rp,fp)           emit mime xbitmap of rp on fp
    * type_pbmpgm(rp,ptype,file)     pbm or pgm image of rp to file
  * cstruct_chardef(cp,fp,col1)         emit C struct of cp on fp   * cstruct_chardef(cp,fp,col1)         emit C struct of cp on fp
  * cstruct_raster(rp,fp,col1)          emit C struct of rp on fp   * cstruct_raster(rp,fp,col1)          emit C struct of rp on fp
  * hex_bitmap(rp,fp,col1,isstr)emit hex dump of rp->pixmap on fp   * hex_bitmap(rp,fp,col1,isstr)emit hex dump of rp->pixmap on fp
  * --- ancillary output functions ---   * --- ancillary output functions ---
  * emit_string(fp,col1,string,comment) emit string and C comment   * emit_string(fp,col1,string,comment) emit string and C comment
    * gftobitmap(rp)        convert .gf-like pixmap to bitmap image
  * ====================== Font Functions =======================   * ====================== Font Functions =======================
  * --- font lookup functions ---   * --- font lookup functions ---
  * get_symdef(symbol)             returns mathchardef for symbol   * get_symdef(symbol)             returns mathchardef for symbol
  * get_chardef(symdef,size)      returns chardef for symdef,size   * get_chardef(symdef,size)      returns chardef for symdef,size
  * get_charsubraster(symdef,size)  wrap subraster around chardef   * get_charsubraster(symdef,size)  wrap subraster around chardef
    * get_symsubraster(symbol,size)    returns subraster for symbol
  * --- ancillary font functions ---   * --- ancillary font functions ---
  * get_baseline(gfdata)       determine baseline (in our coords)   * get_baseline(gfdata)       determine baseline (in our coords)
  * get_delim(symbol,height,family) delim just larger than height   * get_delim(symbol,height,family) delim just larger than height
Line 91 Line 94
  * ================= Tokenize/Parse Functions ==================   * ================= Tokenize/Parse Functions ==================
  * texchar(expression,chartoken)  retruns next char or \sequence   * texchar(expression,chartoken)  retruns next char or \sequence
  * texsubexpr(expr,subexpr,maxsubsz,left,right,isescape,isdelim)   * texsubexpr(expr,subexpr,maxsubsz,left,right,isescape,isdelim)
    * texleft(expr,subexpr,maxsubsz,ldelim,rdelim)   \left...\right
  * texscripts(expression,subscript,superscript,which)get scripts   * texscripts(expression,subscript,superscript,which)get scripts
  * --- ancillary parse functions ---   * --- ancillary parse functions ---
  * isbrace(expression,braces,isescape)   check for leading brace   * isbrace(expression,braces,isescape)   check for leading brace
Line 110 Line 114
  * rastdispmath(expression,size,sp)      scripts for displaymath   * rastdispmath(expression,size,sp)      scripts for displaymath
  * --- table-driven handlers that rasterize... ---   * --- table-driven handlers that rasterize... ---
  * rastleft(expression,size,basesp,ildelim,arg2,arg3)\left\right   * rastleft(expression,size,basesp,ildelim,arg2,arg3)\left\right
    * rastright(expression,size,basesp,ildelim,arg2,arg3) ...\right
    * rastmiddle(expression,size,basesp,arg1,arg2,arg3)     \middle
  * rastflags(expression,size,basesp,flag,value,arg3)    set flag   * rastflags(expression,size,basesp,flag,value,arg3)    set flag
  * rastspace(expression,size,basesp,width,isfill,isheight)\,\:\;   * rastspace(expression,size,basesp,width,isfill,isheight)\,\:\;
  * rastnewline(expression,size,basesp,arg1,arg2,arg3)         \\   * rastnewline(expression,size,basesp,arg1,arg2,arg3)         \\
Line 133 Line 139
  * rastfbox(expression,size,basesp,arg1,arg2,arg3)         \fbox   * rastfbox(expression,size,basesp,arg1,arg2,arg3)         \fbox
  * rastinput(expression,size,basesp,arg1,arg2,arg3)       \input   * rastinput(expression,size,basesp,arg1,arg2,arg3)       \input
  * rastcounter(expression,size,basesp,arg1,arg2,arg3)   \counter   * rastcounter(expression,size,basesp,arg1,arg2,arg3)   \counter
    * rasttoday(expression,size,basesp,arg1,arg2,arg3)       \today
    * rastcalendar(expression,size,basesp,arg1,arg2,arg3) \calendar
  * rastnoop(expression,size,basesp,arg1,arg2,arg3) flush \escape   * rastnoop(expression,size,basesp,arg1,arg2,arg3) flush \escape
  * --- helper functions for handlers ---   * --- helper functions for handlers ---
  * rastopenfile(filename,mode)      opens filename[.tex] in mode   * rastopenfile(filename,mode)      opens filename[.tex] in mode
  * rastreadfile(filename,tag,value)  read between <tag>...</tag>   * rasteditfilename(filename)       edit filename (for security)
    * rastreadfile(filename,islock,tag,value)   read <tag>...</tag>
  * rastwritefile(filename,tag,value,isstrict)write<tag>...</tag>   * rastwritefile(filename,tag,value,isstrict)write<tag>...</tag>
  * timestamp( )   formats timestamp string (used by rastcounter)   * calendar(year,month,day)    formats one-month calendar string
  * dtoa(d,npts)                  double to comma-separated ascii   * timestamp(tzdelta,ifmt)              formats timestamp string
    * tzadjust(tzdelta,year,month,day,hour)        adjust date/time
    * daynumber(year,month,day)     #days since Monday, Jan 1, 1973
    * dbltoa(d,npts)                double to comma-separated ascii
  * === Anti-alias completed raster (lowpass) or symbols (ss) ===   * === Anti-alias completed raster (lowpass) or symbols (ss) ===
  * aalowpass(rp,bytemap,grayscale)     lowpass grayscale bytemap   * aalowpass(rp,bytemap,grayscale)     lowpass grayscale bytemap
  * aapnm(rp,bytemap,grayscale)       lowpass based on pnmalias.c   * aapnm(rp,bytemap,grayscale)       lowpass based on pnmalias.c
Line 149 Line 161
  * aawtpixel(image,ipixel,weights,rotate) weight image at ipixel   * aawtpixel(image,ipixel,weights,rotate) weight image at ipixel
  * PART1 ========================== Driver ===========================   * PART1 ========================== Driver ===========================
  * main(argc,argv) parses math expression and emits mime xbitmap   * main(argc,argv) parses math expression and emits mime xbitmap
    * CreateGifFromEq(expression,gifFileName)  entry pt for win dll
  * isstrstr(string,snippets,iscase)  are any snippets in string?   * isstrstr(string,snippets,iscase)  are any snippets in string?
  * ismonth(month)          is month current month ("jan"-"dec")?   * ismonth(month)          is month current month ("jan"-"dec")?
  * unescape_url(url,isescape), x2c(what)   xlate %xx url-encoded   * unescape_url(url,isescape), x2c(what)   xlate %xx url-encoded
  * logger(fp,msglevel,logvars)        logs environment variables   * logger(fp,msglevel,logvars)        logs environment variables
  * emitcache(cachefile)  read cachefile and emit bytes to stdout   * emitcache(cachefile,maxage,isbuffer) emit cachefile to stdout
    * readcachefile(cachefile,buffer)    read cachefile into buffer
  * md5str(instr)                      md5 hash library functions   * md5str(instr)                      md5 hash library functions
  * GetPixel(x,y)           callback function for gifsave library   * GetPixel(x,y)           callback function for gifsave library
  *   *
Line 311  header files and macros Line 325  header files and macros
   
 /* --- windows-specific header info --- */  /* --- windows-specific header info --- */
 #ifndef WINDOWS /* -DWINDOWS not supplied by user */  #ifndef WINDOWS /* -DWINDOWS not supplied by user */
   #if defined(_WIN32) || defined(WIN32) \    #if defined(_WINDOWS) || defined(_WIN32) || defined(WIN32) \
   ||  defined(DJGPP) /* try to recognize windows */    ||  defined(DJGPP) /* try to recognize windows compilers */ \
     ||  defined(_USRDLL) /* must be WINDOWS if compiling for DLL */
     #define WINDOWS /* signal windows */      #define WINDOWS /* signal windows */
   #endif    #endif
 #endif  #endif
Line 328  header files and macros Line 343  header files and macros
   #if defined(_O_BINARY) || defined(O_BINARY)  /* setmode() now available */    #if defined(_O_BINARY) || defined(O_BINARY)  /* setmode() now available */
     #define HAVE_SETMODE /* so we'll use setmode() */      #define HAVE_SETMODE /* so we'll use setmode() */
   #endif    #endif
     #if defined(_MSC_VER) && defined(_DEBUG) /* MS VC++ in debug mode */
       /* to show source file and line numbers where memory leaks occur... */
       #define _CRTDBG_MAP_ALLOC /* ...include this debug macro */
       #include <crtdbg.h> /* and this debug library */
     #endif
   #define ISWINDOWS 1    #define ISWINDOWS 1
 #else  #else
   #define ISWINDOWS 0    #define ISWINDOWS 0
Line 396  header files and macros Line 416  header files and macros
   #define TEXFONTS /* to include texfonts.h */    #define TEXFONTS /* to include texfonts.h */
 #endif  #endif
 #include "mimetex.h"  #include "mimetex.h"
   /* --- info needed when gif image returned in memory buffer --- */
   #ifdef GIF /* compiling along with gifsave.c */
     extern int gifSize;
     extern int maxgifSize;
   #else /* or just set dummy values */
     static int gifSize=0, maxgifSize=0;
   #endif
   
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 adjustable default values  adjustable default values
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 /* --- anti-aliasing parameters --- */  /* --- anti-aliasing parameters --- */
 #ifndef CENTERWT  #ifndef CENTERWT
     /*#define CENTERWT 32*/ /* anti-aliasing centerwt default */
   /*#define CENTERWT 10*/ /* anti-aliasing centerwt default */    /*#define CENTERWT 10*/ /* anti-aliasing centerwt default */
   /*#define CENTERWT 6*/ /* anti-aliasing centerwt default */    #define CENTERWT 8 /* anti-aliasing centerwt default */
   #define CENTERWT 32 /* anti-aliasing centerwt default */  
 #endif  #endif
 #ifndef ADJACENTWT  #ifndef ADJACENTWT
   /*#define ADJACENTWT 3*/ /* anti-aliasing adjacentwt default*/    /*#define ADJACENTWT 3*/ /* anti-aliasing adjacentwt default*/
   /*#define ADJACENTWT 2*/ /* anti-aliasing adjacentwt default*/    #define ADJACENTWT 2 /* anti-aliasing adjacentwt default*/
   #define ADJACENTWT 3 /* anti-aliasing adjacentwt default*/  
 #endif  #endif
 #ifndef CORNERWT  #ifndef CORNERWT
   #define CORNERWT 1 /* anti-aliasing cornerwt default*/    #define CORNERWT 1 /* anti-aliasing cornerwt default*/
Line 473  other variables Line 499  other variables
 #ifndef FGBLUE  #ifndef FGBLUE
   #define FGBLUE  (ISBLACKONWHITE?0:255)    #define FGBLUE  (ISBLACKONWHITE?0:255)
 #endif  #endif
 /* --- "squash" margin (0 means no squashing) --- */  /* --- "smash" margin (0 means no smashing) --- */
 #ifndef SQUASHMARGIN  #ifndef SMASHMARGIN
   #ifdef NOSQUASH    #ifdef NOSMASH
     #define SQUASHMARGIN 0      #define SMASHMARGIN 0
   #else    #else
     #define SQUASHMARGIN 3      #define SMASHMARGIN 3
   #endif    #endif
 #endif  #endif
 /* --- textwidth --- */  /* --- textwidth --- */
Line 486  other variables Line 512  other variables
   #define TEXTWIDTH (400)    #define TEXTWIDTH (400)
 #endif  #endif
 /* --- font "combinations" --- */  /* --- font "combinations" --- */
 #define CMSYEX (107) /* select CMSY10 _or_ CMEX10 */  #define CMSYEX (109) /*select CMSY10, CMEX10 or STMARY10*/
 /* --- prefix prepended to all expressions --- */  /* --- prefix prepended to all expressions --- */
 #ifndef PREFIX  #ifndef PREFIX
   #define PREFIX "\000" /* default no prepended prefix */    #define PREFIX "\000" /* default no prepended prefix */
Line 522  other variables Line 548  other variables
 #ifndef PATHPREFIX  #ifndef PATHPREFIX
   #define PATHPREFIX "\000" /* paths relative mimetex.cgi */    #define PATHPREFIX "\000" /* paths relative mimetex.cgi */
 #endif  #endif
   /* --- time zone delta t (in hours) --- */
   #ifndef TZDELTA
     #define TZDELTA 0
   #endif
   
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 debugging and logging / error reporting  debugging and logging / error reporting
Line 555  control flags and values Line 585  control flags and values
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 GLOBAL(int,recurlevel,0); /* inc/decremented in rasterize() */  GLOBAL(int,recurlevel,0); /* inc/decremented in rasterize() */
 GLOBAL(int,scriptlevel,0); /* inc/decremented in rastlimits() */  GLOBAL(int,scriptlevel,0); /* inc/decremented in rastlimits() */
 GLOBAL(int,istext,0); /* textmode if true,italics=2,bb=3 */  GLOBAL(int,isstring,0); /*pixmap is ascii string, not raster*/
 GLOBAL(int,isstring ,0); /*pixmap is ascii string, not raster*/  /*SHARED(int,imageformat,1);*/ /* image is 1=bitmap, 2=.gf-like */
 GLOBAL(int,isdisplaystyle,1); /* displaystyle mode (forced if 2) */  GLOBAL(int,isdisplaystyle,1); /* displaystyle mode (forced if 2) */
 GLOBAL(int,ispreambledollars,0); /* displaystyle mode set by $$...$$ */  GLOBAL(int,ispreambledollars,0); /* displaystyle mode set by $$...$$ */
   GLOBAL(int,fontnum,0); /* cal=1,scr=2,rm=3,it=4,bb=5,bf=6 */
 GLOBAL(int,fontsize,NORMALSIZE); /* current size */  GLOBAL(int,fontsize,NORMALSIZE); /* current size */
 GLOBAL(int,displaysize,DISPLAYSIZE); /* use \displaystyle when fontsize>=*/  GLOBAL(int,displaysize,DISPLAYSIZE); /* use \displaystyle when fontsize>=*/
 GLOBAL(int,shrinkfactor,3); /* shrinkfactors[fontsize] */  GLOBAL(int,shrinkfactor,3); /* shrinkfactors[fontsize] */
 GLOBAL(double,unitlength,1.0); /* #pixels per unit (may be <1.0) */  GLOBAL(double,unitlength,1.0); /* #pixels per unit (may be <1.0) */
 /*GLOBAL(int,textwidth,TEXTWIDTH);*/ /* #pixels across line */  /*GLOBAL(int,textwidth,TEXTWIDTH);*/ /* #pixels across line */
 GLOBAL(int,squashmargin,SQUASHMARGIN); /* minimum "squash" margin */  GLOBAL(int,iscatspace,1); /* true to add space in rastcat() */
 GLOBAL(int,issquashdelta,1); /* true if squashmargin is a delta */  GLOBAL(int,smashmargin,SMASHMARGIN); /* minimum "smash" margin */
   GLOBAL(int,issmashdelta,1); /* true if smashmargin is a delta */
   GLOBAL(int,blanksignal,(-991234)); /*rastsmash signal right-hand blank*/
 GLOBAL(int,istransparent,1); /*true to set background transparent*/  GLOBAL(int,istransparent,1); /*true to set background transparent*/
 GLOBAL(int,fgred,FGRED);  GLOBAL(int,fgred,FGRED);
   GLOBAL(int,fggreen,FGGREEN);    GLOBAL(int,fggreen,FGGREEN);
Line 655  if ( rp == (raster *)NULL )  /* malloc f Line 688  if ( rp == (raster *)NULL )  /* malloc f
   goto end_of_job; /* return error to caller */    goto end_of_job; /* return error to caller */
 rp->width = width; /* store width in raster struct */  rp->width = width; /* store width in raster struct */
 rp->height = height; /* and store height */  rp->height = height; /* and store height */
   rp->format = 1; /* initialize as bitmap format */
 rp->pixsz = pixsz; /* store #bits per pixel */  rp->pixsz = pixsz; /* store #bits per pixel */
 rp->pixmap = (pixbyte *)NULL; /* init bitmap as null ptr */  rp->pixmap = (pixbyte *)NULL; /* init bitmap as null ptr */
 /* --- allocate and initialize bitmap array --- */  /* --- allocate and initialize bitmap array --- */
Line 775  cp->charnum = cp->location = 0;  /* init Line 809  cp->charnum = cp->location = 0;  /* init
 cp->toprow = cp->topleftcol = 0; /* init upper-left corner */  cp->toprow = cp->topleftcol = 0; /* init upper-left corner */
 cp->botrow = cp->botleftcol = 0; /* init lower-left corner */  cp->botrow = cp->botleftcol = 0; /* init lower-left corner */
 cp->image.width = cp->image.height = 0; /* init raster dimensions */  cp->image.width = cp->image.height = 0; /* init raster dimensions */
   cp->image.format = 0; /* init raster format */
 cp->image.pixsz = 0; /* and #bits per pixel */  cp->image.pixsz = 0; /* and #bits per pixel */
 cp->image.pixmap = NULL; /* init raster pixmap as null */  cp->image.pixmap = NULL; /* init raster pixmap as null */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
Line 1190  int base1   = sp1->baseline, /*baseline Line 1225  int base1   = sp1->baseline, /*baseline
  pixsz2  = (sp2->image)->pixsz, /* pixsz for right-hand subraster */   pixsz2  = (sp2->image)->pixsz, /* pixsz for right-hand subraster */
  type2   = sp2->type; /* image type for right-hand */   type2   = sp2->type; /* image type for right-hand */
 int height=0, width=0, pixsz=0, base=0; /*concatted sp1||sp2 composite*/  int height=0, width=0, pixsz=0, base=0; /*concatted sp1||sp2 composite*/
 int issquash = (squashmargin!=0?1:0), /* true to "squash" sp1||sp2 */  int issmash = (smashmargin!=0?1:0), /* true to "squash" sp1||sp2 */
  isopaque = (issquash?0:1), /* not oppaque if squashing */   isopaque = (issmash?0:1), /* not oppaque if smashing */
  rastsquash(), isblank=0, nsquash=0, /* #cols to squash */   rastsmash(), isblank=0, nsmash=0, /* #cols to smash */
  oldsquashmargin = squashmargin; /* save original squashmargin */   oldsmashmargin = smashmargin; /* save original smashmargin */
 int blanksignal = (-991234); /*rastsquash signal right-hand blank*/  
 mathchardef *symdef1 = sp1->symdef, /*mathchardef of last left-hand char*/  mathchardef *symdef1 = sp1->symdef, /*mathchardef of last left-hand char*/
  *symdef2 = sp2->symdef; /* mathchardef of right-hand char */   *symdef2 = sp2->symdef; /* mathchardef of right-hand char */
 int class1 = (symdef1==NULL?ORDINARY:symdef1->class), /* symdef->class */  int class1 = (symdef1==NULL?ORDINARY:symdef1->class), /* symdef->class */
Line 1211  Initialization Line 1245  Initialization
 if ( !isstring )  if ( !isstring )
   space = max2(2,(symspace[class1][class2] + fontsize-3)); /* space */    space = max2(2,(symspace[class1][class2] + fontsize-3)); /* space */
 else space = 1; /* space for ascii string */  else space = 1; /* space for ascii string */
 /* --- determine squash --- */  if ( !iscatspace ) space=0; /* spacing explicitly turned off */
 if ( !isstring ) /* don't squash strings */  /* --- determine smash --- */
  if ( issquash ) { /* raster squash wanted */  if ( !isstring ) /* don't smash strings */
    int maxsquash = rastsquash(sp1,sp2), /* calculate max squash space */   if ( issmash ) { /* raster smash wanted */
  margin = squashmargin; /* init margin without delta */     int maxsmash = rastsmash(sp1,sp2), /* calculate max smash space */
    margin = smashmargin; /* init margin without delta */
    if ( (1 && smash1 && smash2) /* concatanating two chars */     if ( (1 && smash1 && smash2) /* concatanating two chars */
    ||   (1 && type1!=IMAGERASTER && type2!=IMAGERASTER) )     ||   (1 && type1!=IMAGERASTER && type2!=IMAGERASTER) )
      /*maxsquash = 0;*/ /* turn off squash */       /*maxsmash = 0;*/ /* turn off smash */
      margin = max2(space-1,0); /* force small squashmargin */       margin = max2(space-1,0); /* force small smashmargin */
    else /* adjust for delta if images */     else /* adjust for delta if images */
      if ( issquashdelta ) /* squashmargin is a delta value */       if ( issmashdelta ) /* smashmargin is a delta value */
        margin += fontsize; /* add displaystyle base to margin */         margin += fontsize; /* add displaystyle base to margin */
    if ( maxsquash == blanksignal ) /* sp2 is intentional blank */     if ( maxsmash == blanksignal ) /* sp2 is intentional blank */
      isblank = 1; /* set blank flag signal */       isblank = 1; /* set blank flag signal */
    else /* see how much extra space we have*/     else /* see how much extra space we have*/
      if ( maxsquash > margin ) /* enough space for adjustment */       if ( maxsmash > margin ) /* enough space for adjustment */
        nsquash = maxsquash-margin; /* make adjustment */         nsmash = maxsmash-margin; /* make adjustment */
    if ( msgfp!=NULL && msglevel>=99 ) /* display squash results */     if ( msgfp!=NULL && msglevel>=99 ) /* display smash results */
      { fprintf(msgfp,"rastcat> maxsquash=%d, margin=%d, nsquash=%d\n",       { fprintf(msgfp,"rastcat> maxsmash=%d, margin=%d, nsmash=%d\n",
        maxsquash,margin,nsquash);         maxsmash,margin,nsmash);
        fprintf(msgfp,"rastcat> type1=%d,2=%d, class1=%d,2=%d\n", type1,type2,         fprintf(msgfp,"rastcat> type1=%d,2=%d, class1=%d,2=%d\n", type1,type2,
        (symdef1==NULL?-999:class1),(symdef2==NULL?-999:class2));         (symdef1==NULL?-999:class1),(symdef2==NULL?-999:class2));
        fflush(msgfp); }         fflush(msgfp); }
    } /* --- end-of-if(issquash) --- */     } /* --- end-of-if(issmash) --- */
 /* --- determine height, width and baseline of composite raster --- */  /* --- determine height, width and baseline of composite raster --- */
 if ( !isstring )  if ( !isstring )
  { height = max2(base1+1,base2+1) /* max height above baseline */   { height = max2(base1+1,base2+1) /* max height above baseline */
           + max2(height1-base1-1,height2-base2-1); /*+ max descending below*/            + max2(height1-base1-1,height2-base2-1); /*+ max descending below*/
    width  = width1+width2 + space-nsquash; /*add widths and space-squash*/     width  = width1+width2 + space-nsmash; /*add widths and space-smash*/
    width  = max3(width,width1,width2); } /* don't "over-squash" composite */     width  = max3(width,width1,width2); } /* don't "over-smash" composite */
 else /* ascii string */  else /* ascii string */
  { height = 1; /* default */   { height = 1; /* default */
    width  = width1 + width2 + space - 1; } /* no need for two nulls */     width  = width1 + width2 + space - 1; } /* no need for two nulls */
Line 1254  if ( msgfp!=NULL && msglevel>=9999 ) /* Line 1289  if ( msgfp!=NULL && msglevel>=9999 ) /*
     height2,width2,pixsz2,base2);      height2,width2,pixsz2,base2);
     type_raster(sp2->image,msgfp); /* display right-hand raster */      type_raster(sp2->image,msgfp); /* display right-hand raster */
     fprintf(msgfp,      fprintf(msgfp,
     "rastcat> Composite ht,width,squash,pixsz,base = %d,%d,%d,%d,%d\n",      "rastcat> Composite ht,width,smash,pixsz,base = %d,%d,%d,%d,%d\n",
     height,width,nsquash,pixsz,base);      height,width,nsmash,pixsz,base);
     fflush(msgfp); } /* flush msgfp buffer */      fflush(msgfp); } /* flush msgfp buffer */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 allocate concatted composite subraster  allocate concatted composite subraster
Line 1273  if ( (sp=new_subraster(width,height,pixs Line 1308  if ( (sp=new_subraster(width,height,pixs
 /* --- initialize subraster parameters --- */  /* --- initialize subraster parameters --- */
 /* sp->type = (!isstring?STRINGRASTER:ASCIISTRING); */  /*concatted string*/  /* sp->type = (!isstring?STRINGRASTER:ASCIISTRING); */  /*concatted string*/
 if ( !isstring )  if ( !isstring )
   sp->type = type2;/*(type1==type2?type2:IMAGERASTER);*/ /*string or image*/    sp->type = /*type2;*//*(type1==type2?type2:IMAGERASTER);*/
    (type2!=CHARASTER? type2 : (type1!=CHARASTER?type1:STRINGRASTER));
 else  else
   sp->type = ASCIISTRING; /* concatted ascii string */    sp->type = ASCIISTRING; /* concatted ascii string */
 sp->symdef = symdef2; /* rightmost char is sp2 */  sp->symdef = symdef2; /* rightmost char is sp2 */
Line 1291  if ( msgfp!=NULL && msglevel>=9999 ) Line 1327  if ( msgfp!=NULL && msglevel>=9999 )
     fflush(msgfp); } /* flush msgfp buffer */      fflush(msgfp); } /* flush msgfp buffer */
 if ( !isstring )  if ( !isstring )
  rastput (rp, sp1->image, base-base1, /* overlay left-hand */   rastput (rp, sp1->image, base-base1, /* overlay left-hand */
  max2(0,nsquash-width1), 1); /* plus any residual squash space */   max2(0,nsmash-width1), 1); /* plus any residual smash space */
 else  else
  memcpy(rp->pixmap,(sp1->image)->pixmap,width1-1);  /*init left string*/   memcpy(rp->pixmap,(sp1->image)->pixmap,width1-1);  /*init left string*/
 if ( msgfp!=NULL && msglevel>=9999 )  if ( msgfp!=NULL && msglevel>=9999 )
Line 1299  if ( msgfp!=NULL && msglevel>=9999 ) Line 1335  if ( msgfp!=NULL && msglevel>=9999 )
     fflush(msgfp); } /* flush msgfp buffer */      fflush(msgfp); } /* flush msgfp buffer */
 if ( !isstring )  if ( !isstring )
  rastput (rp, sp2->image, base-base2, /* overlay right-hand */   rastput (rp, sp2->image, base-base2, /* overlay right-hand */
  max2(0,width1+space-nsquash), isopaque); /* minus any squashed space */   max2(0,width1+space-nsmash), isopaque); /* minus any smashed space */
 else  else
  { strcpy((char *)(rp->pixmap)+width1-1+space,(char *)((sp2->image)->pixmap));   { strcpy((char *)(rp->pixmap)+width1-1+space,(char *)((sp2->image)->pixmap));
    ((char *)(rp->pixmap))[width1+width2+space-2] = '\000'; } /*null-term*/     ((char *)(rp->pixmap))[width1+width2+space-2] = '\000'; } /*null-term*/
Line 1316  if ( isfree > 0 )   /* caller wants inpu Line 1352  if ( isfree > 0 )   /* caller wants inpu
 Back to caller with pointer to concatted subraster or with null for error  Back to caller with pointer to concatted subraster or with null for error
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 end_of_job:  end_of_job:
   squashmargin = oldsquashmargin; /* reset original squashmargin */    smashmargin = oldsmashmargin; /* reset original smashmargin */
   return ( sp ); /* back with subraster or null ptr */    return ( sp ); /* back with subraster or null ptr */
 } /* --- end-of-function rastcat() --- */  } /* --- end-of-function rastcat() --- */
   
Line 1487  end_of_job: Line 1523  end_of_job:
   
   
 /* ==========================================================================  /* ==========================================================================
  * Function: rastsquash ( sp1, sp2 )   * Function: rastsmash ( sp1, sp2 )
  * Purpose: When concatanating sp1||sp2, calculate #pixels   * Purpose: When concatanating sp1||sp2, calculate #pixels
  * we can "squash sp2 left"   * we can "smash sp2 left"
  * --------------------------------------------------------------------------   * --------------------------------------------------------------------------
  * Arguments: sp1 (I) subraster *  to left-hand raster   * Arguments: sp1 (I) subraster *  to left-hand raster
  * sp2 (I) subraster *  to right-hand raster   * sp2 (I) subraster *  to right-hand raster
  * --------------------------------------------------------------------------   * --------------------------------------------------------------------------
  * Returns: ( int ) max #pixels we can squash sp1||sp2,   * Returns: ( int ) max #pixels we can smash sp1||sp2,
  * or "blanksignal" if sp2 intentionally blank,   * or "blanksignal" if sp2 intentionally blank,
  * or 0 for any error.   * or 0 for any error.
  * --------------------------------------------------------------------------   * --------------------------------------------------------------------------
  * Notes:     o   * Notes:     o
  * ======================================================================= */   * ======================================================================= */
 /* --- entry point --- */  /* --- entry point --- */
 int rastsquash ( subraster *sp1, subraster *sp2 )  int rastsmash ( subraster *sp1, subraster *sp2 )
 {  {
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 Allocations and Declarations  Allocations and Declarations
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 int nsquash = 0; /* #pixels to squash sp1||sp2 */  int nsmash = 0; /* #pixels to smash sp1||sp2 */
 int base1   = sp1->baseline, /*baseline for left-hand subraster*/  int base1   = sp1->baseline, /*baseline for left-hand subraster*/
  height1 = (sp1->image)->height, /* height for left-hand subraster */   height1 = (sp1->image)->height, /* height for left-hand subraster */
  width1  = (sp1->image)->width, /* width for left-hand subraster */   width1  = (sp1->image)->width, /* width for left-hand subraster */
Line 1520  int base = max2(base1,base2), /* max asc Line 1556  int base = max2(base1,base2), /* max asc
 int irow1=0,irow2=0, icol=0; /* row,col indexes */  int irow1=0,irow2=0, icol=0; /* row,col indexes */
 int firstcol1[1025], nfirst1=0, /* 1st sp1 col containing set pixel*/  int firstcol1[1025], nfirst1=0, /* 1st sp1 col containing set pixel*/
  firstcol2[1025], nfirst2=0; /* 1st sp2 col containing set pixel*/   firstcol2[1025], nfirst2=0; /* 1st sp2 col containing set pixel*/
 int blanksignal = (-991234); /*rastsquash signal right-hand blank*/  
 int smin=9999, xmin=9999,ymin=9999; /* min separation (s=x+y) */  int smin=9999, xmin=9999,ymin=9999; /* min separation (s=x+y) */
 int type_raster(); /* display debugging output */  int type_raster(); /* display debugging output */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
Line 1528  find right edge of sp1 and left edge of Line 1563  find right edge of sp1 and left edge of
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 /* --- check args --- */  /* --- check args --- */
 if ( isstring ) goto end_of_job; /* ignore string rasters */  if ( isstring ) goto end_of_job; /* ignore string rasters */
 if ( height > 1023 ) goto end_of_job; /* don't try to squash huge image */  if ( 0 && istextmode ) goto end_of_job; /* don't smash in text mode */
   if ( height > 1023 ) goto end_of_job; /* don't try to smash huge image */
 if ( sp2->type == blanksignal ) /*blanksignal was propagated to us*/  if ( sp2->type == blanksignal ) /*blanksignal was propagated to us*/
   goto end_of_job; /* don't squash intentional blank */    goto end_of_job; /* don't smash intentional blank */
 /* --- init firstcol1[], firstcol2[] --- */  /* --- init firstcol1[], firstcol2[] --- */
 for ( irow1=0; irow1<height; irow1++ ) /* for each row */  for ( irow1=0; irow1<height; irow1++ ) /* for each row */
   firstcol1[irow1] = firstcol2[irow1] = blanksignal; /* signal empty rows */    firstcol1[irow1] = firstcol2[irow1] = blanksignal; /* signal empty rows */
Line 1542  for ( irow2=top2; irow2<=bot2; irow2++ ) Line 1578  for ( irow2=top2; irow2<=bot2; irow2++ )
  nfirst2++; /* bump #rows containing set pixels*/   nfirst2++; /* bump #rows containing set pixels*/
  break; } /* and go on to next row */   break; } /* and go on to next row */
 if ( nfirst2 < 1 ) /*right-hand sp2 is completely blank*/  if ( nfirst2 < 1 ) /*right-hand sp2 is completely blank*/
   { nsquash = blanksignal; /* signal intentional blanks */    { nsmash = blanksignal; /* signal intentional blanks */
     goto end_of_job; } /* don't squash intentional blanks */      goto end_of_job; } /* don't smash intentional blanks */
 /* --- now check if preceding image in sp1 was an intentional blank --- */  /* --- now check if preceding image in sp1 was an intentional blank --- */
 if ( sp1->type == blanksignal ) /*blanksignal was propagated to us*/  if ( sp1->type == blanksignal ) /*blanksignal was propagated to us*/
   goto end_of_job; /* don't squash intentional blank */    goto end_of_job; /* don't smash intentional blank */
 /* --- set firstcol1[] indicating right edge of sp1 --- */  /* --- set firstcol1[] indicating right edge of sp1 --- */
 for ( irow1=top1; irow1<=bot1; irow1++ ) /* for each row inside sp1 */  for ( irow1=top1; irow1<=bot1; irow1++ ) /* for each row inside sp1 */
   for ( icol=width1-1; icol>=0; icol-- ) /* find last non-empty col in row */    for ( icol=width1-1; icol>=0; icol-- ) /* find last non-empty col in row */
Line 1555  for ( irow1=top1; irow1<=bot1; irow1++ ) Line 1591  for ( irow1=top1; irow1<=bot1; irow1++ )
  nfirst1++; /* bump #rows containing set pixels*/   nfirst1++; /* bump #rows containing set pixels*/
  break; } /* and go on to next row */   break; } /* and go on to next row */
 if ( nfirst1 < 1 ) /*left-hand sp1 is completely blank*/  if ( nfirst1 < 1 ) /*left-hand sp1 is completely blank*/
   goto end_of_job; /* don't squash intentional blanks */    goto end_of_job; /* don't smash intentional blanks */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 find minimum separation  find minimum separation
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
Line 1568  for ( irow2=top2; irow2<=bot2; irow2++ ) Line 1604  for ( irow2=top2; irow2<=bot2; irow2++ )
     if ( (margin1=firstcol1[irow1]) != blanksignal ) { /*have non-blank row*/      if ( (margin1=firstcol1[irow1]) != blanksignal ) { /*have non-blank row*/
      int dx=(margin1+margin2), dy=absval(irow2-irow1), ds=dx+dy; /* deltas */       int dx=(margin1+margin2), dy=absval(irow2-irow1), ds=dx+dy; /* deltas */
      if ( ds >= smin ) continue; /* min unchanged */       if ( ds >= smin ) continue; /* min unchanged */
      if ( dy>squashmargin && dx<xmin && smin<9999 ) continue; /* dy alone */       if ( dy>smashmargin && dx<xmin && smin<9999 ) continue; /* dy alone */
      smin=ds; xmin=dx; ymin=dy; /* set new min */       smin=ds; xmin=dx; ymin=dy; /* set new min */
      } /* --- end-of-if(margin1!=blanksignal) --- */       } /* --- end-of-if(margin1!=blanksignal) --- */
  if ( smin<2 ) goto end_of_job; /* can't squash */   if ( smin<2 ) goto end_of_job; /* can't smash */
  } /* --- end-of-for(irow2) --- */   } /* --- end-of-for(irow2) --- */
 /*nsquash = min2(xmin,width2);*/ /* permissible squash */  /*nsmash = min2(xmin,width2);*/ /* permissible smash */
 nsquash = xmin; /* permissible squash */  nsmash = xmin; /* permissible smash */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 Back to caller with #pixels to squash sp1||sp2  Back to caller with #pixels to smash sp1||sp2
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 end_of_job:  end_of_job:
   /* --- debugging output --- */    /* --- debugging output --- */
   if ( msgfp!=NULL && msglevel >= 99 ) /* display for debugging */    if ( msgfp!=NULL && msglevel >= 99 ) /* display for debugging */
     { fprintf(msgfp,"rastsquash> nsquash=%d, squashmargin=%d\n",      { fprintf(msgfp,"rastsmash> nsmash=%d, smashmargin=%d\n",
       nsquash,squashmargin);        nsmash,smashmargin);
       if ( msglevel >= 999 ) /* also display rasters */        if ( msglevel >= 999 ) /* also display rasters */
  { fprintf(msgfp,"rastsquash>left-hand image...\n");   { fprintf(msgfp,"rastsmash>left-hand image...\n");
   if(sp1!=NULL) type_raster(sp1->image,msgfp); /* left image */    if(sp1!=NULL) type_raster(sp1->image,msgfp); /* left image */
   fprintf(msgfp,"rastsquash>right-hand image...\n");    fprintf(msgfp,"rastsmash>right-hand image...\n");
   if(sp2!=NULL) type_raster(sp2->image,msgfp); } /* right image */    if(sp2!=NULL) type_raster(sp2->image,msgfp); } /* right image */
       fflush(msgfp); }        fflush(msgfp); }
   return ( nsquash ); /* back with #squash pixels */    return ( nsmash ); /* back with #smash pixels */
 } /* --- end-of-function rastsquash() --- */  } /* --- end-of-function rastsmash() --- */
   
   
 /* ==========================================================================  /* ==========================================================================
Line 1622  raster *new_raster(), *rp=NULL; /*raster Line 1658  raster *new_raster(), *rp=NULL; /*raster
 subraster *new_subraster(), *sp=NULL; /* subraster returning accent */  subraster *new_subraster(), *sp=NULL; /* subraster returning accent */
 int delete_raster(), delete_subraster(); /*free allocated raster on err*/  int delete_raster(), delete_subraster(); /*free allocated raster on err*/
 int line_raster(), /* draws lines */  int line_raster(), /* draws lines */
    rule_raster(), /* draw solid boxes */
  thickness = 1; /* line thickness */   thickness = 1; /* line thickness */
 /*int pixval = (pixsz==1? 1 : (pixsz==8?255:(-1)));*/ /*black pixel value*/  /*int pixval = (pixsz==1? 1 : (pixsz==8?255:(-1)));*/ /*black pixel value*/
 /* --- other working info --- */  /* --- other working info --- */
Line 1653  switch ( accent ) Line 1690  switch ( accent )
     /* --- bar request --- */      /* --- bar request --- */
     case UNDERBARACCENT:      case UNDERBARACCENT:
     case BARACCENT:      case BARACCENT:
  thickness = height-1; /* adjust thickness */   thickness = 1; /*height-1;*/ /* adjust thickness */
  if ( accent == BARACCENT ) /* bar is above expression */   if ( accent == BARACCENT ) /* bar is above expression */
   line_raster(rp,0,0,0,width-1,thickness); /*leave blank line at bot*/   { row0 = row1 = max2(height-3,0); /* row numbers for overbar */
      line_raster(rp,row0,0,row1,width-1,thickness); } /*blanks at bot*/
  else /* underbar is below expression */   else /* underbar is below expression */
   line_raster(rp,1,0,1,width-1,thickness); /*leave blank line at top*/   { row0 = row1 = min2(2,height-1); /* row numbers for underbar */
      line_raster(rp,row0,0,row1,width-1,thickness); } /*blanks at top*/
  break;   break;
     /* --- dot request --- */      /* --- dot request --- */
     case DOTACCENT:      case DOTACCENT:
  thickness = height-1; /* adjust thickness */   thickness = height-1; /* adjust thickness */
  line_raster(rp,0,width/2,1,(width/2)+1,thickness); /* centered dot */   /*line_raster(rp,0,width/2,1,(width/2)+1,thickness);*//*centered dot*/
    rule_raster(rp,0,(width+1-thickness)/2,thickness,thickness,3); /*box*/
  break;   break;
     /* --- ddot request --- */      /* --- ddot request --- */
     case DDOTACCENT:      case DDOTACCENT:
  thickness = height-1; /* adjust thickness */   thickness = height-1; /* adjust thickness */
  col0 = max2(width/3-(thickness-1),0); /* one-third of width */   col0 = max2((width+1)/3-(thickness/2)-1,0); /* one-third of width */
  col1 = min2((2*width)/3+(thickness-1),width-thickness); /*two thirds*/   col1 = min2((2*width+1)/3-(thickness/2)+1,width-thickness); /*2/3rds*/
  line_raster(rp,0,col0,1,col0+1,thickness); /* set a dot at 1st third*/   if ( col0+thickness >= col1 ) /* dots overlap */
  line_raster(rp,0,col1,1,col1+1,thickness); /* and another at 2nd */    { col0 = max2(col0-1,0); /* try moving left dot more left */
       col1 = min2(col1+1,width-thickness); } /* and right dot right */
    if ( col0+thickness >= col1 ) /* dots _still_ overlap */
     thickness = max2(thickness-1,1); /* so try reducing thickness */
    /*line_raster(rp,0,col0,1,col0+1,thickness);*//*set dot at 1st third*/
    /*line_raster(rp,0,col1,1,col1+1,thickness);*//*and another at 2nd*/
    rule_raster(rp,0,col0,thickness,thickness,3); /*box at 1st third*/
    rule_raster(rp,0,col1,thickness,thickness,3); /*box at 2nd third*/
  break;   break;
     /* --- hat request --- */      /* --- hat request --- */
     case HATACCENT:      case HATACCENT:
  thickness = (width<=12? 2 : 3); /* adjust thickness */   thickness = 1; /*(width<=12? 2 : 3);*/ /* adjust thickness */
  line_raster(rp,height-1,0,0,width/2,thickness);    /* / part of hat*/   line_raster(rp,height-1,0,0,width/2,thickness);    /* / part of hat*/
  line_raster(rp,0,(width-1)/2,height-1,width-1,thickness); /* \ part*/   line_raster(rp,0,(width-1)/2,height-1,width-1,thickness); /* \ part*/
  break;   break;
Line 1724  switch ( accent ) Line 1771  switch ( accent )
       if ( (sp=rastack(new_subraster(1,1,pixsz),accsp,1,0,1,3))/*space below*/        if ( (sp=rastack(new_subraster(1,1,pixsz),accsp,1,0,1,3))/*space below*/
       !=  NULL ) /* have tilde with space below it */        !=  NULL ) /* have tilde with space below it */
  { rp = sp->image; /* "extract" raster with bitmap */   { rp = sp->image; /* "extract" raster with bitmap */
   free((void *)sp); } /* and free subraster "envelope" */    free((void *)sp); /* and free subraster "envelope" */
     leftsymdef = NULL; } /* so \tilde{x}^2 works properly */
     break;      break;
  } /* --- end-of-outer-switch(accent) --- */   } /* --- end-of-outer-switch(accent) --- */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
Line 1774  Allocations and Declarations Line 1822  Allocations and Declarations
 subraster *new_subraster(), *arrowsp=NULL; /* allocate arrow subraster */  subraster *new_subraster(), *arrowsp=NULL; /* allocate arrow subraster */
 int rule_raster(); /* draw arrow line */  int rule_raster(); /* draw arrow line */
 int irow, midrow=height/2; /* index, midrow is arrowhead apex */  int irow, midrow=height/2; /* index, midrow is arrowhead apex */
 int icol, thickness=(height>15?2:1); /* arrowhead thickness and index */  int icol, thickness=(height>15?2:2); /* arrowhead thickness and index */
 int pixval = (pixsz==1? 1 : (pixsz==8?255:(-1))); /* black pixel value */  int pixval = (pixsz==1? 1 : (pixsz==8?255:(-1))); /* black pixel value */
 int ipix, /* raster pixmap[] index */  int ipix, /* raster pixmap[] index */
  npix = width*height; /* #pixels malloced in pixmap[] */   npix = width*height; /* #pixels malloced in pixmap[] */
Line 1850  Allocations and Declarations Line 1898  Allocations and Declarations
 subraster *new_subraster(), *arrowsp=NULL; /* allocate arrow subraster */  subraster *new_subraster(), *arrowsp=NULL; /* allocate arrow subraster */
 int rule_raster(); /* draw arrow line */  int rule_raster(); /* draw arrow line */
 int icol, midcol=width/2; /* index, midcol is arrowhead apex */  int icol, midcol=width/2; /* index, midcol is arrowhead apex */
 int irow, thickness=(width>15?2:1); /* arrowhead thickness and index */  int irow, thickness=(width>15?2:2); /* arrowhead thickness and index */
 int pixval = (pixsz==1? 1 : (pixsz==8?255:(-1))); /* black pixel value */  int pixval = (pixsz==1? 1 : (pixsz==8?255:(-1))); /* black pixel value */
 int ipix, /* raster pixmap[] index */  int ipix, /* raster pixmap[] index */
  npix = width*height; /* #pixels malloced in pixmap[] */   npix = width*height; /* #pixels malloced in pixmap[] */
Line 1892  for ( icol=0; icol<width; icol++ )  /* f Line 1940  for ( icol=0; icol<width; icol++ )  /* f
  else /* should have a bytemap */   else /* should have a bytemap */
  if ( pixsz == 8 ) /* check pixsz for bytemap */   if ( pixsz == 8 ) /* check pixsz for bytemap */
   ((arrowsp->image)->pixmap)[ipix] = pixval; } /*set arrowhead byte*/    ((arrowsp->image)->pixmap)[ipix] = pixval; } /*set arrowhead byte*/
   } /* --- end-of-for(irow) --- */    } /* --- end-of-for(icol) --- */
 end_of_job:  end_of_job:
   return ( arrowsp ); /*back to caller with arrow or NULL*/    return ( arrowsp ); /*back to caller with arrow or NULL*/
 } /* --- end-of-function uparrow_subraster() --- */  } /* --- end-of-function uparrow_subraster() --- */
Line 1913  end_of_job: Line 1961  end_of_job:
  * height (I) int containing number of rows for rule   * height (I) int containing number of rows for rule
  * type (I) int containing 0 for solid rule,   * type (I) int containing 0 for solid rule,
  * 1 for horizontal dashes, 2 for vertical   * 1 for horizontal dashes, 2 for vertical
    * 3 for solid rule with corners removed
  * --------------------------------------------------------------------------   * --------------------------------------------------------------------------
  * Returns: ( int ) 1 if rule drawn okay,   * Returns: ( int ) 1 if rule drawn okay,
  * or 0 for any error.   * or 0 for any error.
Line 1928  int rule_raster ( raster *rp, int top, i Line 1977  int rule_raster ( raster *rp, int top, i
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 Allocations and Declarations  Allocations and Declarations
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 int irow, icol; /* indexes over rp raster */  int irow=0, icol=0; /* indexes over rp raster */
 int ipix, /* raster pixmap[] index */  int ipix = 0, /* raster pixmap[] index */
  npix = rp->width * rp->height; /* #pixels malloced in rp->pixmap[] */   npix = rp->width * rp->height; /* #pixels malloced in rp->pixmap[] */
 int isfatal = 0; /* true to abend on out-of-bounds error */  int isfatal = 0; /* true to abend on out-of-bounds error */
 int hdash=1, vdash=2; /* type for horizontal, vertical dashes */  int hdash=1, vdash=2; /* type for horizontal, vertical dashes */
Line 1942  if ( rp == (raster *)NULL ) /* no raster Line 1991  if ( rp == (raster *)NULL ) /* no raster
   if ( workingbox != (subraster *)NULL )  /* see if we have a workingbox */    if ( workingbox != (subraster *)NULL )  /* see if we have a workingbox */
     rp = workingbox->image; /* use workingbox if possible */      rp = workingbox->image; /* use workingbox if possible */
   else return ( 0 ); /* otherwise signal error to caller */    else return ( 0 ); /* otherwise signal error to caller */
   if ( type == 3 ) /* remove corners of solid box */
     if ( width<3 || height<3 ) type=0; /* too small to remove corners */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 Fill line/box  Fill line/box
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
Line 1952  for ( irow=top; irow<top+height; irow++ Line 2003  for ( irow=top; irow<top+height; irow++
   ipix = irow*rp->width + left - 1; /*first pixel preceding icol*/    ipix = irow*rp->width + left - 1; /*first pixel preceding icol*/
   for ( icol=left; icol<left+width; icol++ ) /* each pixel in scan line */    for ( icol=left; icol<left+width; icol++ ) /* each pixel in scan line */
     {      {
       if ( type == 3 ) /* remove corners of box */
         if ( (irow==top && icol==left) /* top-left corner */
         ||   (irow==top && icol>=left+width-1) /* top-right corner */
         ||   (irow>=top+height-1 && icol==left) /* bottom-left corner */
         ||   (irow>=top+height-1 && icol>=left+width-1) ) /* bottom-right */
    isdraw = 0;  else isdraw = 1; /*set isdraw to skip corner*/
     if ( type == hdash ) /*set isdraw for horiz dash*/      if ( type == hdash ) /*set isdraw for horiz dash*/
       isdraw = (((icol-left)%(dashlen+spacelen)) < dashlen);        isdraw = (((icol-left)%(dashlen+spacelen)) < dashlen);
     if ( ++ipix >= npix ) /* bounds check failed */      if ( ++ipix >= npix ) /* bounds check failed */
Line 2008  int line_raster ( raster *rp, int row0, Line 2065  int line_raster ( raster *rp, int row0,
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 Allocations and Declarations  Allocations and Declarations
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 int irow, icol, /* indexes over rp raster */  int irow=0, icol=0, /* indexes over rp raster */
  locol=col0, hicol=col1, /* col limits at irow */   locol=col0, hicol=col1, /* col limits at irow */
  lorow=row0, hirow=row1; /* row limits at icol */   lorow=row0, hirow=row1; /* row limits at icol */
 int ipix, /* raster pixmap[] index */  int width=rp->width, height=rp->height; /* dimensions of input raster */
  npix = rp->width * rp->height; /* #pixels malloced in rp->pixmap[] */  int ipix = 0, /* raster pixmap[] index */
    npix = width*height; /* #pixels malloced in rp->pixmap[] */
 int isfatal = 0; /* true to abend on out-of-bounds error */  int isfatal = 0; /* true to abend on out-of-bounds error */
 int isline=(row1==row0), isbar=(col1==col0); /*true if slope a=0,\infty*/  int isline=(row1==row0), isbar=(col1==col0); /*true if slope a=0,\infty*/
 double dy = row1-row0 /* + (row1>=row0? +1.0 : -1.0) */, /* delta-x */  double dy = row1-row0 /* + (row1>=row0? +1.0 : -1.0) */, /* delta-x */
  dx = col1-col0 /* + (col1>=col0? +1.0 : -1.0) */, /* delta-y */   dx = col1-col0 /* + (col1>=col0? +1.0 : -1.0) */, /* delta-y */
  a= (isbar||isline? 0.0 : dy/dx), /* slope = tan(theta) = dy/dx */   a= (isbar||isline? 0.0 : dy/dx), /* slope = tan(theta) = dy/dx */
  xcol, xrow; /* calculated col at irow, or row at icol */   xcol=0, xrow=0; /* calculated col at irow, or row at icol */
 double ar = ASPECTRATIO, /* aspect ratio width/height of one pixel */  double ar = ASPECTRATIO, /* aspect ratio width/height of one pixel */
  xwidth= (isline? 0.0 : /*#pixels per row to get sloped line thcknss*/   xwidth= (isline? 0.0 : /*#pixels per row to get sloped line thcknss*/
  ((double)thickness)*sqrt((dx*dx)+(dy*dy*ar*ar))/fabs(dy*ar)),   ((double)thickness)*sqrt((dx*dx)+(dy*dy*ar*ar))/fabs(dy*ar)),
Line 2039  if ( msgfp!=NULL && msglevel>=29 )  /* d Line 2097  if ( msgfp!=NULL && msglevel>=29 )  /* d
    "\t dy,dx=%3.1f,%3.1f, a=%4.3f, xwidth=%4.3f\n",     "\t dy,dx=%3.1f,%3.1f, a=%4.3f, xwidth=%4.3f\n",
    row0,col0, row1,col1, thickness,  dy,dx, a, xwidth);     row0,col0, row1,col1, thickness,  dy,dx, a, xwidth);
 /* --- check for recursive line drawing --- */  /* --- check for recursive line drawing --- */
 if ( isrecurse ) /* drawing lines recursively */  if ( isrecurse ) { /* drawing lines recursively */
   { line_recurse(rp,(double)row0,(double)col0,   for ( irow=0; irow<thickness; irow++ ) /* each line 1 pixel thick */
     (double)row1,(double)col1,thickness);    { double xrow0=(double)row0, xcol0=(double)col0,
     return ( 1 ); }   xrow1=(double)row1, xcol1=(double)col1;
       if ( isline ) xrow0 = xrow1 = (double)(row0+irow);
       else if ( isbar ) xcol0 = xcol1 = (double)(col0+irow);
       if( xrow0>(-0.001) && xcol0>(-0.001) /*check line inside raster*/
       &&  xrow1<((double)(height-1)+0.001) && xcol1<((double)(width-1)+0.001) )
         line_recurse(rp,xrow0,xcol0,xrow1,xcol1,thickness); }
    return ( 1 ); }
 /* --- set params for horizontal line or vertical bar --- */  /* --- set params for horizontal line or vertical bar --- */
 if ( isline ) /*interpret row as top row*/  if ( isline ) /*interpret row as top row*/
   row1 = row0 + (thickness-1); /* set bottom row for line */    row1 = row0 + (thickness-1); /* set bottom row for line */
Line 2588  static char display_chars[16] = /* displ Line 2652  static char display_chars[16] = /* displ
 char scanline[133]; /* ascii image for one scan line */  char scanline[133]; /* ascii image for one scan line */
 int scan_width; /* #chars in scan (<=display_width)*/  int scan_width; /* #chars in scan (<=display_width)*/
 int irow, locol,hicol=(-1); /* height index, width indexes */  int irow, locol,hicol=(-1); /* height index, width indexes */
   raster *gftobitmap(), *bitmaprp=rp; /* convert .gf to bitmap if needed */
   int delete_raster(); /*free bitmap converted for display*/
 /* --------------------------------------------------------------------------  /* --------------------------------------------------------------------------
 initialization  initialization
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
Line 2595  initialization Line 2661  initialization
 if ( fp == (FILE *)NULL ) fp = stdout; /* default fp to stdout if null */  if ( fp == (FILE *)NULL ) fp = stdout; /* default fp to stdout if null */
 /* --- check for ascii string --- */  /* --- check for ascii string --- */
 if ( isstring /* pixmap has string, not raster */  if ( isstring /* pixmap has string, not raster */
 ||   (1 && rp->height==1) ) /* infer input rp is a string */  ||   (0 && rp->height==1) ) /* infer input rp is a string */
  {   {
  char *string = (char *)(rp->pixmap); /*interpret pixmap as ascii string*/   char *string = (char *)(rp->pixmap); /*interpret pixmap as ascii string*/
  int width = strlen(string); /* #chars in ascii string */   int width = strlen(string); /* #chars in ascii string */
Line 2609  if ( isstring    /* pixmap has string, n Line 2675  if ( isstring    /* pixmap has string, n
 /* --------------------------------------------------------------------------  /* --------------------------------------------------------------------------
 display ascii dump of bitmap image (in segments if display_width < rp->width)  display ascii dump of bitmap image (in segments if display_width < rp->width)
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 while ( (locol=hicol+1) < rp->width ) /*start where prev segment left off*/  if ( rp->format == 2 /* input is .gf-formatted */
   ||   rp->format == 3 )
     bitmaprp = gftobitmap(rp); /* so convert it for display */
   if ( bitmaprp != NULL ) /* if we have image for display */
    while ( (locol=hicol+1) < rp->width ) /*start where prev segment left off*/
   {    {
   /* --- set hicol for this pass (locol set above) --- */    /* --- set hicol for this pass (locol set above) --- */
   hicol += display_width; /* show as much as display allows */    hicol += display_width; /* show as much as display allows */
Line 2626  while ( (locol=hicol+1) < rp->width ) /* Line 2696  while ( (locol=hicol+1) < rp->width ) /*
  lopix = irow*rp->width + locol; /*first pixmap[] pixel in this scan*/   lopix = irow*rp->width + locol; /*first pixmap[] pixel in this scan*/
     /* --- set chars in scanline[] based on pixels in rp->pixmap[] --- */      /* --- set chars in scanline[] based on pixels in rp->pixmap[] --- */
     for ( ipix=0; ipix<scan_width; ipix++ ) /* set each char */      for ( ipix=0; ipix<scan_width; ipix++ ) /* set each char */
       if ( rp->pixsz == 1 ) /*' '=0 or '*'=1 to display bitmap*/        if ( bitmaprp->pixsz == 1 ) /*' '=0 or '*'=1 to display bitmap*/
  scanline[ipix] = (getlongbit(rp->pixmap,lopix+ipix)==1? '*':'.');   scanline[ipix]=(getlongbit(bitmaprp->pixmap,lopix+ipix)==1? '*':'.');
       else /* should have a bytemap */        else /* should have a bytemap */
        if ( rp->pixsz == 8 ) /* double-check pixsz for bytemap */         if ( bitmaprp->pixsz == 8 ) /* double-check pixsz for bytemap */
  { int pixval = (int)((rp->pixmap)[lopix+ipix]), /*pixel's byte value*/   { int pixval = (int)((bitmaprp->pixmap)[lopix+ipix]), /*byte value*/
   ichar = min2(15,pixval/16); /* index for ' ', '1'...'e', '*' */    ichar = min2(15,pixval/16); /* index for ' ', '1'...'e', '*' */
   scanline[ipix] = display_chars[ichar]; } /*set ' ' for 0-15, etc*/    scanline[ipix] = display_chars[ichar]; } /*set ' ' for 0-15, etc*/
     /* --- display completed scan line --- */      /* --- display completed scan line --- */
Line 2640  while ( (locol=hicol+1) < rp->width ) /* Line 2710  while ( (locol=hicol+1) < rp->width ) /*
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 Back to caller with 1=okay, 0=failed.  Back to caller with 1=okay, 0=failed.
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
   if ( rp->format == 2 /* input was .gf-format */
   ||   rp->format == 3 )
     if ( bitmaprp != NULL ) /* and we converted it for display */
       delete_raster(bitmaprp); /* no longer needed, so free it */
 return ( 1 );  return ( 1 );
 } /* --- end-of-function type_raster() --- */  } /* --- end-of-function type_raster() --- */
   
Line 2782  return ( 1 ); Line 2856  return ( 1 );
   
   
 /* ==========================================================================  /* ==========================================================================
    * Function: type_pbmpgm ( rp, ptype, file )
    * Purpose: Write pbm or pgm image of rp to file
    * --------------------------------------------------------------------------
    * Arguments: rp (I) ptr to raster struct for which
    * a pbm/pgm file is to be written.
    * ptype (I) int containing 1 for pbm, 2 for pgm, or
    * 0 to determine ptype from values in rp
    * file (I) ptr to null-terminated char string
    * containing name of fuke to be written
    * (see notes below).
    * --------------------------------------------------------------------------
    * Returns: ( int ) total #bytes written,
    * or 0 for any error.
    * --------------------------------------------------------------------------
    * Notes:     o (a) If file==NULL, output is written to stdout;
    * (b) if *file=='\000' then file is taken as the
    *    address of an output buffer to which output
    *    is written (and is followed by a terminating
    *    '\0' which is not counted in #bytes returned);
    * (c) otherwise file is the filename (opened and
    *    closed internally) to which output is written,
    *    except that any final .ext extension is replaced
    *    by .pbm or .pgm depending on ptype.
    * ======================================================================= */
   /* --- entry point --- */
   int type_pbmpgm ( raster *rp, int ptype, char *file )
   {
   /* -------------------------------------------------------------------------
   Allocations and Declarations
   -------------------------------------------------------------------------- */
   int isokay=0, nbytes=0; /* completion flag, total #bytes written */
   int irow=0, jcol=0; /*height(row), width(col) indexes in raster*/
   int pixmin=9999, pixmax=(-9999), /* min, max pixel value in raster */
    ngray = 0; /* #gray scale values */
   FILE /* *fopen(), */ *fp=NULL; /* pointer to output file (or NULL) */
   char outline[1024], outfield[256], /* output line, field */
    cr[16] = "\n\000"; /* cr at end-of-line */
   int maxlinelen = 70; /* maximum allowed line length */
   int pixfrac=6; /* use (pixmax-pixmin)/pixfrac as step */
   static char
    *suffix[] = { NULL, ".pbm", ".pgm" }, /* file.suffix[ptype] */
    *magic[] = { NULL, "P1", "P2" }, /*identifying "magic number"*/
    *mode[] = { NULL, "w", "w" }; /* fopen() mode[ptype] */
   /* -------------------------------------------------------------------------
   check input, determine grayscale,  and set up output file if necessary
   -------------------------------------------------------------------------- */
   /* --- check input args --- */
   if ( rp == NULL ) goto end_of_job; /* no input raster provided */
   if ( ptype != 0 ) /* we'll determine ptype below */
    if ( ptype<1 || ptype>2 ) goto end_of_job; /*invalid output graphic format*/
   /* --- determine largest (and smallest) value in pixmap --- */
   for ( irow=0; irow<rp->height; irow++ ) /* for each row, top-to-bottom */
    for ( jcol=0; jcol<rp->width; jcol++ ) /* for each col, left-to-right */
     { int pixval = getpixel(rp,irow,jcol);  /* value of pixel at irow,jcol */
       pixmin = min2(pixmin,pixval); /* new minimum */
       pixmax = max2(pixmax,pixval); } /* new maximum */
   ngray = 1 + (pixmax-pixmin); /* should be 2 for b/w bitmap */
   if ( ptype == 0 ) /* caller wants us to set ptype */
     ptype = (ngray>=3?2:1); /* use grayscale if >2 shades */
   /* --- open output file if necessary --- */
   if ( file == NULL ) fp = stdout; /*null ptr signals output to stdout*/
   else if ( *file != '\000' ) { /* explicit filename provided, so...*/
     char fname[512], *pdot=NULL; /* file.ext, ptr to last . in fname*/
     strncpy(fname,file,255); /* local copy of file name */
     fname[255] = '\000'; /* make sure it's null terminated */
     if ( (pdot=strrchr(fname,'.')) == NULL ) /*no extension on original name*/
       strcat(fname,suffix[ptype]); /* so add extension */
     else /* we already have an extension */
       strcpy(pdot,suffix[ptype]); /* so replace original extension */
     if ( (fp = fopen(fname,mode[ptype])) /* open output file */
     ==   (FILE *)NULL ) goto end_of_job; /* quit if failed to open */
     } /* --- ens-of-if(*file!='\0') --- */
   /* -------------------------------------------------------------------------
   format and write header
   -------------------------------------------------------------------------- */
   /* --- format header info --- */
   *outline = '\000'; /* initialize line buffer */
   strcat(outline,magic[ptype]); /* begin file with "magic number" */
   strcat(outline,cr); /* followed by cr to end line */
   sprintf(outfield,"%d %d",rp->width,rp->height); /* format width and height */
   strcat(outline,outfield); /* add width and height to header */
   strcat(outline,cr); /* followed by cr to end line */
   if ( ptype == 2 ) /* need max grayscale value */
     { sprintf(outfield,"%d",pixmax); /* format maximum pixel value */
       strcat(outline,outfield); /* add max value to header */
       strcat(outline,cr); } /* followed by cr to end line */
   /* --- write header to file or memory buffer --- */
   if ( fp == NULL ) /* if we have no open file... */
     strcat(file,outline); /* add header to caller's buffer */
   else /* or if we have an open file... */
     if ( fputs(outline,fp) /* try writing header to open file */
     ==   EOF ) goto end_of_job; /* return with error if failed */
   nbytes += strlen(outline); /* bump output byte count */
   /* -------------------------------------------------------------------------
   format and write pixels
   -------------------------------------------------------------------------- */
   *outline = '\000'; /* initialize line buffer */
   for ( irow=0; irow<=rp->height; irow++ ) /* for each row, top-to-bottom */
    for ( jcol=0; jcol<rp->width; jcol++ ) { /* for each col, left-to-right */
     /* --- format value at irow,jcol--- */
     *outfield = '\000'; /* init empty field */
     if ( irow < rp->height ) { /* check row index */
       int pixval = getpixel(rp,irow,jcol);  /* value of pixel at irow,jcol */
       if ( ptype == 1 ) /* pixval must be 1 or 0 */
         pixval = (pixval>pixmin+((pixmax-pixmin)/pixfrac)?1:0);
       sprintf(outfield,"%d ",pixval); } /* format pixel value */
     /* --- write line if this value won't fit on it (or last line) --- */
     if ( strlen(outline)+strlen(outfield)+strlen(cr) >= maxlinelen /*won't fit*/
     ||   irow >= rp->height ) { /* force writing last line */
       strcat(outline,cr); /* add cr to end current line */
       if ( fp == NULL ) /* if we have no open file... */
         strcat(file,outline); /* add header to caller's buffer */
       else /* or if we have an open file... */
         if ( fputs(outline,fp) /* try writing header to open file */
         ==   EOF ) goto end_of_job; /* return with error if failed */
       nbytes += strlen(outline); /* bump output byte count */
       *outline = '\000'; /* re-initialize line buffer */
       } /* --- end-of-if(strlen>=maxlinelen) --- */
     if ( irow >= rp->height ) break; /* done after writing last line */
     /* --- concatanate value to line -- */
     strcat(outline,outfield); /* concatanate value to line */
     } /* --- end-of-for(jcol,irow) --- */
   isokay = 1; /* signal successful completion */
   /* -------------------------------------------------------------------------
   Back to caller with total #bytes written, or 0=failed.
   -------------------------------------------------------------------------- */
   end_of_job:
     if ( fp != NULL /* output written to an open file */
     &&   fp != stdout ) /* and it's not just stdout */
       fclose(fp); /* so close file before returning */
     return ( (isokay?nbytes:0) ); /*back to caller with #bytes written*/
   } /* --- end-of-function type_pbmpgm() --- */
   
   
   /* ==========================================================================
  * Function: cstruct_chardef ( cp, fp, col1 )   * Function: cstruct_chardef ( cp, fp, col1 )
  * Purpose: Emit a C struct of cp on fp, starting in col1.   * Purpose: Emit a C struct of cp on fp, starting in col1.
  * --------------------------------------------------------------------------   * --------------------------------------------------------------------------
Line 2813  emit   charnum, location, name  /  hirow Line 3022  emit   charnum, location, name  /  hirow
 sprintf(field,"{ %3d,%5d,\n", cp->charnum,cp->location);  /*char#,location*/  sprintf(field,"{ %3d,%5d,\n", cp->charnum,cp->location);  /*char#,location*/
 emit_string ( fp, col1, field, "character number, location");  emit_string ( fp, col1, field, "character number, location");
 /* --- toprow, topleftcol,   botrow, botleftcol  --- */  /* --- toprow, topleftcol,   botrow, botleftcol  --- */
 sprintf(field,"  %3d,%2d,   %3d,%2d,\n", /* format... */  sprintf(field,"  %3d,%2d,  %3d,%2d,\n", /* format... */
   cp->toprow,cp->topleftcol, /* toprow, topleftcol, */    cp->toprow,cp->topleftcol, /* toprow, topleftcol, */
   cp->botrow,cp->botleftcol); /* and botrow, botleftcol */    cp->botrow,cp->botleftcol); /* and botrow, botleftcol */
 emit_string ( fp, col1, field, "topleft row,col, and botleft row,col");  emit_string ( fp, col1, field, "topleft row,col, and botleft row,col");
Line 2855  int emit_string();  /* emit a string and Line 3064  int emit_string();  /* emit a string and
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 emit width and height  emit width and height
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 sprintf(field,"{ %2d,   %3d,%2d,   %s\n", /* format width,height,pixsz */  sprintf(field,"{ %2d,  %3d,%2d,%2d, %s\n", /* format width,height,pixsz */
  rp->width,rp->height,rp->pixsz,typecast);   rp->width,rp->height,rp->format,rp->pixsz,typecast);
 emit_string ( fp, col1, field, "widthxheight, pixsz,map...");  emit_string ( fp, col1, field, "width,ht, fmt,pixsz,map...");
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 emit bitmap and closing brace, and return to caller  emit bitmap and closing brace, and return to caller
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
Line 2894  int hex_bitmap ( raster *rp, FILE *fp, i Line 3103  int hex_bitmap ( raster *rp, FILE *fp, i
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 Allocations and Declarations  Allocations and Declarations
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 int ibyte, nbytes=pixmapsz(rp); /* #bytes in raster */  int ibyte, /* pixmap[ibyte] index */
    nbytes = pixbytes(rp); /*#bytes in bitmap or .gf-formatted*/
 char stub[64]="                                ";/* col1 leading blanks */  char stub[64]="                                ";/* col1 leading blanks */
 int linewidth = 64, /* (roughly) rightmost column */  int linewidth = 64, /* (roughly) rightmost column */
  colwidth = (isstr? 4:5); /* #cols required for each byte */   colwidth = (isstr? 4:5); /* #cols required for each byte */
Line 3000  return ( 1 ); Line 3210  return ( 1 );
   
   
 /* ==========================================================================  /* ==========================================================================
    * Function: gftobitmap ( gf )
    * Purpose: convert .gf-like pixmap to bitmap image
    * --------------------------------------------------------------------------
    * Arguments: gf (I) raster * to struct in .gf-format
    * --------------------------------------------------------------------------
    * Returns: ( raster * ) image-format raster * if successful,
    * or NULL for any error.
    * --------------------------------------------------------------------------
    * Notes:     o
    * ======================================================================= */
   /* --- entry point --- */
   raster *gftobitmap ( raster *gf )
   {
   /* -------------------------------------------------------------------------
   Allocations and Declarations
   -------------------------------------------------------------------------- */
   raster *new_raster(), *rp=NULL; /* image raster retuned to caller */
   int width=0, height=0, totbits=0; /* gf->width, gf->height, #bits */
   int format=0, icount=0, ncounts=0, /*.gf format, count index, #counts*/
    ibit=0, bitval=0; /* bitmap index, bit value */
   int isrepeat = 1, /* true to process repeat counts */
    repeatcmds[2] = {255,15}, /*opcode for repeat/duplicate count*/
    nrepeats=0, irepeat=0, /* scan line repeat count,index */
    wbits = 0; /* count bits to width of scan line*/
   /* -------------------------------------------------------------------------
   initialization
   -------------------------------------------------------------------------- */
   /* --- check args --- */
   if ( gf == NULL ) goto end_of_job; /* input raster not provided */
   format = gf->format; /* 2 or 3 */
   if ( format!=2 && format!=3 ) goto end_of_job; /* invalid raster format */
   ncounts = gf->pixsz; /*pixsz is really #counts in pixmap*/
   /* --- allocate output raster with proper dimensions for bitmap --- */
   width=gf->width;  height=gf->height; /* dimensions of raster */
   if ( (rp = new_raster(width,height,1)) /* allocate new raster and bitmap */
   ==   NULL ) goto end_of_job; /* quit if failed to allocate */
   totbits = width*height; /* total #bits in image */
   /* -------------------------------------------------------------------------
   fill bitmap
   -------------------------------------------------------------------------- */
   for ( icount=0,bitval=0; icount<ncounts; icount++ )
     {
     int nbits = (int)(getbyfmt(format,gf->pixmap,icount)); /*#bits to set*/
     if ( isrepeat /* we're proxessing repeat counts */
     &&   nbits == repeatcmds[format-2] ) /* and repeat opcode found */
      if ( nrepeats == 0 ) /* recursive repeat is error */
       { nrepeats = (int)(getbyfmt(format,gf->pixmap,icount+1));/*repeat count*/
         nbits = (int)(getbyfmt(format,gf->pixmap,icount+2)); /*#bits to set*/
         icount += 2; } /* bump byte/nibble count */
      else /* some internal error occurred */
       if ( msgfp!=NULL && msglevel>=1 ) /* report error */
        fprintf(msgfp,"gftobitmap> found embedded repeat command\n");
     if ( 0 )
       fprintf(stdout,
       "gftobitmap> icount=%d bitval=%d nbits=%d ibit=%d totbits=%d\n",
       icount,bitval,nbits,ibit,totbits);
     for ( ; nbits>0; nbits-- ) /* count down */
       { if ( ibit >= totbits ) goto end_of_job; /* overflow check */
         for ( irepeat=0; irepeat<=nrepeats; irepeat++ )
          if ( bitval == 1 ) /* set pixel */
    { setlongbit(rp->pixmap,(ibit+irepeat*width)); }
          else /* clear pixel */
    { unsetlongbit(rp->pixmap,(ibit+irepeat*width)); }
         if ( nrepeats > 0 ) wbits++; /* count another repeated bit */
         ibit++; } /* bump bit index */
     bitval = 1-bitval; /* flip bit value */
     if ( wbits >= width ) { /* completed repeats */
      ibit += nrepeats*width; /*bump bit count past repeated scans*/
      if ( wbits > width ) /* out-of alignment error */
       if ( msgfp!=NULL && msglevel>=1 ) /* report error */
        fprintf(msgfp,"gftobitmap> width=%d wbits=%d\n",width,wbits);
      wbits = nrepeats = 0; } /* reset repeat counts */
     } /* --- end-of-for(icount) --- */
   end_of_job:
     return ( rp ); /* back to caller with image */
   } /* --- end-of-function gftobitmap() --- */
   
   
   /* ==========================================================================
  * Function: get_symdef ( symbol )   * Function: get_symdef ( symbol )
  * Purpose: returns mathchardef struct for symbol   * Purpose: returns mathchardef struct for symbol
  * --------------------------------------------------------------------------   * --------------------------------------------------------------------------
Line 3030  int symlen = strlen(symbol), /* length o Line 3319  int symlen = strlen(symbol), /* length o
  deflen, minlen=9999; /*length of shortest matching symdef*/   deflen, minlen=9999; /*length of shortest matching symdef*/
 int /*alnumsym = (symlen==1 && isalnum(*symbol)),*/ /*alphanumeric sym*/  int /*alnumsym = (symlen==1 && isalnum(*symbol)),*/ /*alphanumeric sym*/
  alphasym = (symlen==1 && isalpha(*symbol)); /* or alpha symbol */   alphasym = (symlen==1 && isalpha(*symbol)); /* or alpha symbol */
   int family = fontinfo[fontnum].family; /* current font family */
 static char *displaysyms[][2] = { /*xlate to Big sym for \displaystyle*/  static char *displaysyms[][2] = { /*xlate to Big sym for \displaystyle*/
    /* --- see table on page 536 in TLC2 --- */
  {"\\int", "\\Bigint"},   {"\\int", "\\Bigint"},
  {"\\oint", "\\Bigoint"},   {"\\oint", "\\Bigoint"},
  {"\\sum", "\\Bigsum"},   {"\\sum", "\\Bigsum"},
  {"\\prod", "\\Bigprod"},   {"\\prod", "\\Bigprod"},
  {"\\coprod", "\\Bigcoprod"},   {"\\coprod", "\\Bigcoprod"},
  {"\\cup", "\\Bigcup"},   /* --- must be 'big' when related to similar binary operators --- */
  {"\\sqcup", "\\Bigsqcup"},   {"\\bigcup", "\\Bigcup"},
  {"\\cap", "\\Bigcap"},   {"\\bigsqcup", "\\Bigsqcup"},
  {"\\sqcap", "\\sqcap"}, /* don't have \Bigsqcap */   {"\\bigcap", "\\Bigcap"},
  {"\\odot", "\\Bigodot"},   /*{"\\bigsqcap", "\\sqcap"},*/ /* don't have \Bigsqcap */
  {"\\oplus", "\\Bigoplus"},   {"\\bigodot", "\\Bigodot"},
  {"\\otimes", "\\Bigotimes"},   {"\\bigoplus", "\\Bigoplus"},
  {"\\uplus", "\\Biguplus"},   {"\\bigominus", "\\ominus"},
  {"\\wedge", "\\Bigwedge"},   {"\\bigotimes", "\\Bigotimes"},
  {"\\vee", "\\Bigvee"},   {"\\bigoslash", "\\oslash"},
    {"\\biguplus", "\\Biguplus"},
    {"\\bigwedge", "\\Bigwedge"},
    {"\\bigvee", "\\Bigvee"},
  {NULL, NULL} };   {NULL, NULL} };
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 If in \displaystyle mode, first xlate int to Bigint, etc.  If in \displaystyle mode, first xlate int to Bigint, etc.
Line 3070  for ( idef=0; ;idef++ )   /* until trail Line 3364  for ( idef=0; ;idef++ )   /* until trail
   if ( symdefs[idef].symbol == NULL ) break; /* reached end-of-table */    if ( symdefs[idef].symbol == NULL ) break; /* reached end-of-table */
   else /* check against caller's symbol */    else /* check against caller's symbol */
     if ( strncmp(symbol,symdefs[idef].symbol,symlen) == 0 ) /* found match */      if ( strncmp(symbol,symdefs[idef].symbol,symlen) == 0 ) /* found match */
      if ( symdefs[idef].handler != NULL /* mode irrelevant for directives */       if (fontnum==0 /* mathmode, so check every match */
      ||  istext==0 /* mathmode, so use first match */       || (0 && istextmode && (!alphasym /* text mode and not alpha symbol */
      || (istext==1 && symdefs[idef].family==CMR10)   /*textmode && rm text*/   || symdefs[idef].handler!=NULL))   /* or text mode and directive */
      || (istext==2 && symdefs[idef].family==CMMI10)  /*textmode && it text*/       || (symdefs[idef].family==family /* have correct family */
      || (istext==3 && symdefs[idef].family==BBOLD10) /*textmode && bb text*/   && symdefs[idef].handler==NULL) )  /* and not a handler collision */
      || (istext!=3 && !alphasym) ) /* not bb and not alpha */  #if 0
        || (fontnum==1 && symdefs[idef].family==CMR10)   /*textmode && rm text*/
        || (fontnum==2 && symdefs[idef].family==CMMI10)  /*textmode && it text*/
        || (fontnum==3 && symdefs[idef].family==BBOLD10  /*textmode && bb text*/
    && symdefs[idef].handler==NULL)
        || (fontnum==4 && symdefs[idef].family==CMMIB10  /*textmode && bf text*/
    && symdefs[idef].handler==NULL) )
   #endif
       if ( (deflen=strlen(symdefs[idef].symbol)) < minlen ) /*new best match*/        if ( (deflen=strlen(symdefs[idef].symbol)) < minlen ) /*new best match*/
  { bestdef = idef; /* save index of new best match */   { bestdef = idef; /* save index of new best match */
   if ( (minlen = deflen) /* and save its len for next test */    if ( (minlen = deflen) /* and save its len for next test */
   ==  symlen ) break; } /*perfect match, so return with it*/    ==  symlen ) break; } /*perfect match, so return with it*/
 if ( bestdef < 0 ) /* failed to look up symbol */  if ( bestdef < 0 ) /* failed to look up symbol */
   if ( istext != 0 ) /* we're in a restricted font mode */    if ( fontnum != 0 ) /* we're in a restricted font mode */
     { int wastext = istext; /* save current mode */      { int oldfontnum = fontnum; /* save current font family */
       mathchardef *symdef = NULL; /* lookup result with istext=0 */        mathchardef *symdef = NULL; /* lookup result with fontnum=0 */
       istext = 0; /*try to look up symbol in any font*/        fontnum = 0; /*try to look up symbol in any font*/
       symdef = get_symdef(symbol); /* repeat lookup with istext=0 */        symdef = get_symdef(symbol); /* repeat lookup with fontnum=0 */
       istext = wastext; /* reset font mode */        fontnum = oldfontnum; /* reset font family */
       return symdef; } /* caller gets istext=0 lookup */        return symdef; } /* caller gets fontnum=0 lookup */
 if ( msgfp!=NULL && msglevel>=999 ) /* debugging output */  if ( msgfp!=NULL && msglevel>=999 ) /* debugging output */
   { fprintf(msgfp,"get_symdefs> symbol=%s matches symtable[%d]=%s\n",    { fprintf(msgfp,"get_symdef> symbol=%s matches symtable[%d]=%s\n",
     symbol,bestdef,(bestdef<0?"NotFound":symdefs[bestdef].symbol));      symbol,bestdef,(bestdef<0?"NotFound":symdefs[bestdef].symbol));
     fflush(msgfp); }      fflush(msgfp); }
 return ( (bestdef<0? NULL : &(symdefs[bestdef])) ); /*NULL or best symdef[]*/  return ( (bestdef<0? NULL : &(symdefs[bestdef])) ); /*NULL or best symdef[]*/
Line 3102  return ( (bestdef<0? NULL : &(symdefs[be Line 3403  return ( (bestdef<0? NULL : &(symdefs[be
  * --------------------------------------------------------------------------   * --------------------------------------------------------------------------
  * Arguments: symdef (I) mathchardef *  corresponding to symbol   * Arguments: symdef (I) mathchardef *  corresponding to symbol
  * whose corresponding chardef is wanted   * whose corresponding chardef is wanted
  * size (I) int containing 0-4 for desired size   * size (I) int containing 0-5 for desired size
  * --------------------------------------------------------------------------   * --------------------------------------------------------------------------
  * Returns: ( chardef * ) pointer to struct defining symbol at size,   * Returns: ( chardef * ) pointer to struct defining symbol at size,
  * or NULL for any error   * or NULL for any error
Line 3197  return ( gfdata );   /*ptr to chardef fo Line 3498  return ( gfdata );   /*ptr to chardef fo
  * Purpose: returns new subraster ptr containing   * Purpose: returns new subraster ptr containing
  * data for symdef at given size   * data for symdef at given size
  * --------------------------------------------------------------------------   * --------------------------------------------------------------------------
  * Arguments: symdef (I) mathchardef *  corresponding to symbol   * Arguments: symdef (I) mathchardef *  corresponding to symbol whose
  * whose corresponding chardef is wanted   * corresponding chardef subraster is wanted
  * size (I) int containing 0-4 for desired size   * size (I) int containing 0-5 for desired size
  * --------------------------------------------------------------------------   * --------------------------------------------------------------------------
  * Returns: ( subraster * ) pointer to struct defining symbol at size,   * Returns: ( subraster * ) pointer to struct defining symbol at size,
  * or NULL for any error   * or NULL for any error
Line 3215  Allocations and Declarations Line 3516  Allocations and Declarations
 chardef *get_chardef(), *gfdata=NULL; /* chardef struct for symdef,size */  chardef *get_chardef(), *gfdata=NULL; /* chardef struct for symdef,size */
 int get_baseline(); /* baseline of gfdata */  int get_baseline(); /* baseline of gfdata */
 subraster *new_subraster(), *sp=NULL; /* subraster containing gfdata */  subraster *new_subraster(), *sp=NULL; /* subraster containing gfdata */
   raster *bitmaprp=NULL, *gftobitmap(); /* convert .gf-format to bitmap */
   int delete_subraster(); /* in case gftobitmap() fails */
 int aasupsamp(), /*antialias char with supersampling*/  int aasupsamp(), /*antialias char with supersampling*/
  grayscale=256; /* aasupersamp() parameters */   grayscale=256; /* aasupersamp() parameters */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
Line 3225  if ( (gfdata=get_chardef(symdef,size)) / Line 3528  if ( (gfdata=get_chardef(symdef,size)) /
  if ( (sp=new_subraster(0,0,0)) /* allocate subraster "envelope" */   if ( (sp=new_subraster(0,0,0)) /* allocate subraster "envelope" */
  !=   NULL ) /* and check that we succeeded */   !=   NULL ) /* and check that we succeeded */
   {    {
   sp->type = CHARASTER; /* static character raster */    raster *image = &(gfdata->image); /* ptr to chardef's bitmap or .gf */
     int format = image->format; /* 1=bitmap, else .gf */
   sp->symdef = symdef; /* replace NULL with caller's arg */    sp->symdef = symdef; /* replace NULL with caller's arg */
   sp->size = size; /*replace default with caller's size*/    sp->size = size; /*replace default with caller's size*/
   sp->baseline = get_baseline(gfdata); /* get baseline of character */    sp->baseline = get_baseline(gfdata); /* get baseline of character */
   sp->image    = &(gfdata->image); /* store ptr to its bitmap */    if ( format == 1 ) /* already a bitmap */
      { sp->type = CHARASTER; /* static char raster */
        sp->image = image; } /* store ptr to its bitmap */
     else /* need to convert .gf-to-bitmap */
      if ( (bitmaprp = gftobitmap(image)) /* convert */
      !=   (raster *)NULL ) /* successful */
       { sp->type = IMAGERASTER; /* allocated raster will be freed */
         sp->image = bitmaprp; } /* store ptr to converted bitmap */
      else /* conversion failed */
       { delete_subraster(sp); /* free unneeded subraster */
         sp = (subraster *)NULL; /* signal error to caller */
         goto end_of_job; } /* quit */
   if ( issupersampling ) /* antialias character right here */    if ( issupersampling ) /* antialias character right here */
     {      {
     raster *aa = NULL; /* antialiased char raster */      raster *aa = NULL; /* antialiased char raster */
Line 3245  if ( (gfdata=get_chardef(symdef,size)) / Line 3560  if ( (gfdata=get_chardef(symdef,size)) /
  sp->type = IMAGERASTER; } /* character is an image raster */   sp->type = IMAGERASTER; } /* character is an image raster */
     } /* --- end-of-if(issupersampling) --- */      } /* --- end-of-if(issupersampling) --- */
   } /* --- end-of-if(sp!=NULL) --- */    } /* --- end-of-if(sp!=NULL) --- */
 if ( msgfp!=NULL && msglevel>=999 )  end_of_job:
    if ( msgfp!=NULL && msglevel>=999 )
   { fprintf(msgfp,"get_charsubraster> requested symbol=\"%s\" baseline=%d\n",    { fprintf(msgfp,"get_charsubraster> requested symbol=\"%s\" baseline=%d\n",
     symdef->symbol, (sp==NULL?0:sp->baseline)); fflush(msgfp); }      symdef->symbol, (sp==NULL?0:sp->baseline)); fflush(msgfp); }
 return ( sp ); /* back to caller */  return ( sp ); /* back to caller */
Line 3253  return ( sp );    /* back to caller */ Line 3569  return ( sp );    /* back to caller */
   
   
 /* ==========================================================================  /* ==========================================================================
    * Function: get_symsubraster ( symbol, size )
    * Purpose: returns new subraster ptr containing
    * data for symbol at given size
    * --------------------------------------------------------------------------
    * Arguments: symbol (I) char *  corresponding to symbol
    * whose corresponding subraster is wanted
    * size (I) int containing 0-5 for desired size
    * --------------------------------------------------------------------------
    * Returns: ( subraster * ) pointer to struct defining symbol at size,
    * or NULL for any error
    * --------------------------------------------------------------------------
    * Notes:     o just combines get_symdef() and get_charsubraster()
    * ======================================================================= */
   /* --- entry point --- */
   subraster *get_symsubraster ( char *symbol, int size )
   {
   /* -------------------------------------------------------------------------
   Allocations and Declarations
   -------------------------------------------------------------------------- */
   subraster *sp=NULL, *get_charsubraster(); /* subraster containing gfdata */
   mathchardef *symdef=NULL, *get_symdef(); /* mathchardef lookup for symbol */
   /* -------------------------------------------------------------------------
   look up mathchardef for symbol
   -------------------------------------------------------------------------- */
   if ( symbol != NULL ) /* user supplied input symbol */
     symdef = get_symdef(symbol); /*look up corresponding mathchardef*/
   /* -------------------------------------------------------------------------
   look up chardef for mathchardef and wrap a subraster structure around data
   -------------------------------------------------------------------------- */
   if ( symdef != NULL ) /* lookup succeeded */
     sp = get_charsubraster(symdef,size); /* so get symbol data in subraster */
   return ( sp ); /* back to caller with sp or NULL */
   } /* --- end-of-function get_symsubraster() --- */
   
   
   /* ==========================================================================
  * Function: get_baseline ( gfdata )   * Function: get_baseline ( gfdata )
  * Purpose: returns baseline for a chardef struct   * Purpose: returns baseline for a chardef struct
  * --------------------------------------------------------------------------   * --------------------------------------------------------------------------
Line 3367  for ( idef=0; ;idef++ )   /* until trail Line 3719  for ( idef=0; ;idef++ )   /* until trail
  if ( defsym == NULL ) break; /* reached end-of-table */   if ( defsym == NULL ) break; /* reached end-of-table */
  else /* check against caller's symbol */   else /* check against caller's symbol */
   if ( family<0 || deffam == family /* if explicitly in caller's family*/    if ( family<0 || deffam == family /* if explicitly in caller's family*/
   ||  (family==CMSYEX && (deffam==CMSY10||deffam==CMEX10)) )    ||  (family==CMSYEX && (deffam==CMSY10||deffam==CMEX10||deffam==STMARY10)) )
     {      {
     strcpy(lcsymbol,defsym); /* local copy of symdefs[] symbol */      strcpy(lcsymbol,defsym); /* local copy of symdefs[] symbol */
     if ( isunesc && *lcsymbol=='\\' ) /* ignored leading \ in symbol */      if ( isunesc && *lcsymbol=='\\' ) /* ignored leading \ in symbol */
Line 3404  construct subraster for best fit charact Line 3756  construct subraster for best fit charact
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 if ( bestdef >= 0 ) /* found a best fit for caller */  if ( bestdef >= 0 ) /* found a best fit for caller */
   sp = get_charsubraster(&(symdefs[bestdef]),bestsize); /* best subraster */    sp = get_charsubraster(&(symdefs[bestdef]),bestsize); /* best subraster */
 if ( sp==NULL && height-bigheight>5 ) /* try to construct delim */  if ( (sp==NULL && height-bigheight>5) /* try to construct delim */
   ||   bigdef < 0 ) /* delim not in font tables */
   sp = make_delim(symbol,(iswidth?-height:height)); /* try to build delim */    sp = make_delim(symbol,(iswidth?-height:height)); /* try to build delim */
 if ( sp==NULL && bigdef>=0 ) /* just give biggest to caller */  if ( sp==NULL && bigdef>=0 ) /* just give biggest to caller */
   sp = get_charsubraster(&(symdefs[bigdef]),bigsize); /* biggest subraster */    sp = get_charsubraster(&(symdefs[bigdef]),bigsize); /* biggest subraster */
   if ( msgfp!=NULL && msglevel>=99 )
       fprintf(msgfp,"get_delim> symbol=%.50s, height=%d family=%d isokay=%s\n",
       (symbol==NULL?"null":symbol),height,family,(sp==NULL?"fail":"success"));
 return ( sp );  return ( sp );
 } /* --- end-of-function get_delim() --- */  } /* --- end-of-function get_delim() --- */
   
Line 3436  Allocations and Declarations Line 3792  Allocations and Declarations
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 subraster *sp = (subraster *)NULL, /* subraster returned to caller */  subraster *sp = (subraster *)NULL, /* subraster returned to caller */
  *new_subraster(); /* allocate subraster */   *new_subraster(); /* allocate subraster */
   subraster *get_symsubraster(), /* look up delim pieces in cmex10 */
    *symtop=NULL, *symbot=NULL, *symmid=NULL, *symbar=NULL, /* pieces */
    *topsym=NULL, *botsym=NULL, *midsym=NULL, *barsym=NULL, /* +filler */
    *rastack(), *rastcat(); /* stack pieces, concat filler */
   int isdrawparen = 0; /*1=draw paren, 0=build from pieces*/
 raster *rasp = (raster *)NULL; /* sp->image */  raster *rasp = (raster *)NULL; /* sp->image */
 int isokay=0, delete_subraster(); /* set true if delimiter drawn ok */  int isokay=0, delete_subraster(); /* set true if delimiter drawn ok */
 int pixsz = 1; /* pixels are one bit each */  int pixsz = 1, /* pixels are one bit each */
    symsize = 0; /* size arg for get_symsubraster() */
 int thickness = 1; /* drawn lines are one pixel thick */  int thickness = 1; /* drawn lines are one pixel thick */
 int aspectratio = 8; /* default height/width for parens */  int aspectratio = 8; /* default height/width for parens */
 int iswidth = 0, /*true if width specified by height*/  int iswidth = 0, /*true if width specified by height*/
  width = height; /* #pixels width (e.g., of ellipse)*/   width = height; /* #pixels width (e.g., of ellipse)*/
 char *lp=NULL,  *rp=NULL, *strchr(), /* check symbol for left or right */  char *lp=NULL,  *rp=NULL, /* check symbol for left or right */
  *lp2=NULL, *rp2=NULL; /* synonym for lp,rp */   *lp2=NULL, *rp2=NULL, /* synonym for lp,rp */
    *lp3=NULL, *rp3=NULL, /* synonym for lp,rp */
    *lp4=NULL, *rp4=NULL; /* synonym for lp,rp */
 int circle_raster(), /* ellipse for ()'s in sp->image */  int circle_raster(), /* ellipse for ()'s in sp->image */
  rule_rsater(), /* horizontal or vertical lines */   rule_rsater(), /* horizontal or vertical lines */
  line_raster(); /* line between two points */   line_raster(); /* line between two points */
   subraster *uparrow_subraster(); /* up/down arrows */
   int isprealloc = 1; /*pre-alloc subraster, except arrow*/
   int oldsmashmargin = smashmargin, /* save original smashmargin */
    wascatspace = iscatspace; /* save original iscatspace */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 initialization  initialization
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
Line 3460  if ( height < 3 ) goto end_of_job; /* to Line 3828  if ( height < 3 ) goto end_of_job; /* to
 if ( iswidth ) height =  (width+(aspectratio+1)/2)/aspectratio;  if ( iswidth ) height =  (width+(aspectratio+1)/2)/aspectratio;
 else            width = (height+(aspectratio+1)/2)/aspectratio;  else            width = (height+(aspectratio+1)/2)/aspectratio;
 if ( strchr(symbol,'=') != NULL /* left or right || bracket wanted */  if ( strchr(symbol,'=') != NULL /* left or right || bracket wanted */
 ||   strstr(symbol,"\\|") != NULL ) /* same || in standard tex notation*/  ||   strstr(symbol,"\\|") != NULL /* same || in standard tex notation*/
   width = max2(width,5); /* need space between two |'s */  ||   strstr(symbol,"dbl") != NULL ) /* semantic bracket with ||'s */
     width = max2(width,6); /* need space between two |'s */
 if ( width < 2 ) width=2; /* set min width */  if ( width < 2 ) width=2; /* set min width */
 if ( strchr(symbol,'(') != NULL /* if left ( */  if ( strchr(symbol,'(') != NULL /* if left ( */
 ||   strchr(symbol,')') != NULL ) /* or right ) paren wanted */  ||   strchr(symbol,')') != NULL ) /* or right ) paren wanted */
   width = (3*width)/2; /* adjust width */    { width = (3*width)/2; /* adjust width */
       if ( !isdrawparen ) isprealloc=0; } /* don't prealloc if building */
   if ( strchr(symbol,'/') != NULL /* left / */
   ||   strstr(symbol,"\\\\") != NULL /* or \\ for right \ */
   ||   strstr(symbol,"backsl") != NULL ) /* or \backslash for \ */
     width = max2(height/3,5);
   if ( strstr(symbol,"arrow") != NULL ) /* arrow wanted */
     { width = min2(height/3,20); /* adjust width */
       isprealloc = 0; } /* don't preallocate subraster */
   if ( strchr(symbol,'{') != NULL /* if left { */
   ||   strchr(symbol,'}') != NULL ) /* or right } brace wanted */
     { isprealloc = 0; } /* don't preallocate */
 /* --- allocate and initialize subraster for constructed delimiter --- */  /* --- allocate and initialize subraster for constructed delimiter --- */
 if ( (sp=new_subraster(width,height,pixsz)) /* allocate new subraster */  if ( isprealloc ) /* pre-allocation wanted */
 ==   NULL )  goto end_of_job; /* quit if failed */   { if ( (sp=new_subraster(width,height,pixsz)) /* allocate new subraster */
 /* --- initialize delimiter subraster parameters --- */     ==   NULL )  goto end_of_job; /* quit if failed */
 sp->type = IMAGERASTER; /* image */     /* --- initialize delimiter subraster parameters --- */
 sp->symdef = NULL; /* not applicable for image */     sp->type = IMAGERASTER; /* image */
 sp->baseline = height/2 + 2; /* is a little above center good? */     sp->symdef = NULL; /* not applicable for image */
 sp->size = NORMALSIZE; /* size (probably unneeded) */     sp->baseline = height/2 + 2; /* is a little above center good? */
 rasp = sp->image; /* pointer to image in subraster */     sp->size = NORMALSIZE; /* size (probably unneeded) */
      rasp = sp->image; } /* pointer to image in subraster */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 ( ) parens  ( ) parens
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 if ( (lp=strchr(symbol,'(')) != NULL /* left ( paren wanted */  if ( (lp=strchr(symbol,'(')) != NULL /* left ( paren wanted */
 ||   (rp=strchr(symbol,')')) != NULL ) /* right ) paren wanted */  ||   (rp=strchr(symbol,')')) != NULL ) /* right ) paren wanted */
   {    {
   int mywidth = min2(width,20); /* max width for ()'s */    if ( isdrawparen ) { /* draw the paren */
   circle_raster ( rasp, /* embedded raster image */     int mywidth = min2(width,20); /* max width for ()'s */
      circle_raster ( rasp, /* embedded raster image */
  0, 0, /* row0,col0 are upper-left corner */   0, 0, /* row0,col0 are upper-left corner */
  height-1, mywidth-1, /* row1,col1 are lower-right */   height-1, mywidth-1, /* row1,col1 are lower-right */
  thickness, /* line thickness is 1 pixel */   thickness, /* line thickness is 1 pixel */
  (rp==NULL?"23":"41") ); /* "1234" quadrants to be drawn */   (rp==NULL?"23":"41") ); /* "1234" quadrants to be drawn */
   isokay = 1; /* set flag */     isokay = 1; } /* set flag */
     else {
      int isleft = (lp!=NULL?1:0); /* true for left, false for right */
      char *parentop = (isleft?"\\leftparentop":"\\rightparentop"),
    *parenbot = (isleft?"\\leftparenbot":"\\rightparenbot"),
    *parenbar = (isleft?"\\leftparenbar":"\\rightparenbar");
      int baseht=0, barht=0, /* height of base=top+bot, bar */
    ibar=0, nbars=0; /* bar index, #bars between top&bot*/
      int largestsize = min2(2,LARGESTSIZE), /* largest size for parens */
    topfill=(isleft?0:0), botfill=(isleft?0:0),
    barfill=(isleft?0:7); /* alignment fillers */
      /* --- get pieces at largest size smaller than total height --- */
      for ( symsize=largestsize; symsize>=0; symsize-- ) /*largest to smallest*/
       {
       /* --- get pieces at current test size --- */
       isokay = 1; /* check for all pieces */
       if ( (symtop=get_symsubraster(parentop,symsize)) == NULL ) isokay=0;
       if ( (symbot=get_symsubraster(parenbot,symsize)) == NULL ) isokay=0;
       if ( (symbar=get_symsubraster(parenbar,symsize)) == NULL ) isokay=0;
       /* --- check sum of pieces against total desired height --- */
       if ( isokay ) { /* all pieces retrieved */
         baseht = (symtop->image)->height + (symbot->image)->height; /*top+bot*/
         barht  = (symbar->image)->height; /* bar height */
         if ( baseht < height+5 ) break; /* largest base that's not too big */
         if ( symsize < 1 ) break; /* or smallest available base */
         } /* --- end-of-if(isokay) --- */
       /* --- free test pieces that were too big --- */
       if ( symtop != NULL ) delete_subraster(symtop); /* free top */
       if ( symbot != NULL ) delete_subraster(symbot); /* free bot */
       if ( symbar != NULL ) delete_subraster(symbar); /* free bar */
       isokay = 0; /* nothing available */
       if ( symsize < 1 ) break; /* leave isokay=0 after smallest */
       } /* --- end-of-for(symsize) --- */
      /* --- construct brace from pieces --- */
      if ( isokay ) { /* we have the pieces */
       /* --- add alignment fillers --- */
       smashmargin = iscatspace = 0; /*turn off rastcat smashing,space*/
       topsym = (topfill>0?rastcat(new_subraster(topfill,1,1),symtop,3):symtop);
       botsym = (botfill>0?rastcat(new_subraster(botfill,1,1),symbot,3):symbot);
       barsym = (barfill>0?rastcat(new_subraster(barfill,1,1),symbar,3):symbar);
       smashmargin = oldsmashmargin; /* reset smashmargin */
       iscatspace = wascatspace; /* reset iscatspace */
       /* --- #bars needed between top and bot --- */
       nbars = (barht<1?0:max2(0,1+(height-baseht)/barht)); /* #bars needed */
       /* --- stack pieces --- */
       sp = topsym; /* start with top piece */
       if ( nbars > 0 ) /* need nbars between top and bot */
         for ( ibar=1; ibar<=nbars; ibar++ ) sp = rastack(barsym,sp,1,0,0,2);
       sp = rastack(botsym,sp,1,0,0,3); /* bottom below bars or middle */
       delete_subraster(barsym); /* barsym no longer needed */
       } /* --- end-of-if(isokay) --- */
      } /* --- end-of-if/else(isdrawparen) --- */
   } /* --- end-of-if(left- or right-() paren wanted) --- */    } /* --- end-of-if(left- or right-() paren wanted) --- */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
   { } braces
   -------------------------------------------------------------------------- */
   else
    if ( (lp=strchr(symbol,'{')) != NULL /* left { brace wanted */
    ||   (rp=strchr(symbol,'}')) != NULL ) /* right } brace wanted */
     {
     int isleft = (lp!=NULL?1:0); /* true for left, false for right */
     char *bracetop = (isleft?"\\leftbracetop":"\\rightbracetop"),
    *bracebot = (isleft?"\\leftbracebot":"\\rightbracebot"),
    *bracemid = (isleft?"\\leftbracemid":"\\rightbracemid"),
    *bracebar = (isleft?"\\leftbracebar":"\\rightbracebar");
     int baseht=0, barht=0, /* height of base=top+bot+mid, bar */
    ibar=0, nbars=0; /* bar index, #bars above,below mid*/
     int largestsize = min2(2,LARGESTSIZE), /* largest size for braces */
    topfill=(isleft?4:0), botfill=(isleft?4:0),
    midfill=(isleft?0:4), barfill=(isleft?4:4); /* alignment fillers */
     /* --- get pieces at largest size smaller than total height --- */
     for ( symsize=largestsize; symsize>=0; symsize-- ) /*largest to smallest*/
       {
       /* --- get pieces at current test size --- */
       isokay = 1; /* check for all pieces */
       if ( (symtop=get_symsubraster(bracetop,symsize)) == NULL ) isokay=0;
       if ( (symbot=get_symsubraster(bracebot,symsize)) == NULL ) isokay=0;
       if ( (symmid=get_symsubraster(bracemid,symsize)) == NULL ) isokay=0;
       if ( (symbar=get_symsubraster(bracebar,symsize)) == NULL ) isokay=0;
       /* --- check sum of pieces against total desired height --- */
       if ( isokay ) { /* all pieces retrieved */
         baseht = (symtop->image)->height + (symbot->image)->height
    + (symmid->image)->height; /* top+bot+mid height */
         barht = (symbar->image)->height; /* bar height */
         if ( baseht < height+5 ) break; /* largest base that's not too big */
         if ( symsize < 1 ) break; /* or smallest available base */
         } /* --- end-of-if(isokay) --- */
       /* --- free test pieces that were too big --- */
       if ( symtop != NULL ) delete_subraster(symtop); /* free top */
       if ( symbot != NULL ) delete_subraster(symbot); /* free bot */
       if ( symmid != NULL ) delete_subraster(symmid); /* free mid */
       if ( symbar != NULL ) delete_subraster(symbar); /* free bar */
       isokay = 0; /* nothing available */
       if ( symsize < 1 ) break; /* leave isokay=0 after smallest */
       } /* --- end-of-for(symsize) --- */
     /* --- construct brace from pieces --- */
     if ( isokay ) { /* we have the pieces */
       /* --- add alignment fillers --- */
       smashmargin = iscatspace = 0; /*turn off rastcat smashing,space*/
       topsym = (topfill>0?rastcat(new_subraster(topfill,1,1),symtop,3):symtop);
       botsym = (botfill>0?rastcat(new_subraster(botfill,1,1),symbot,3):symbot);
       midsym = (midfill>0?rastcat(new_subraster(midfill,1,1),symmid,3):symmid);
       barsym = (barfill>0?rastcat(new_subraster(barfill,1,1),symbar,3):symbar);
       smashmargin = oldsmashmargin; /* reset smashmargin */
       iscatspace = wascatspace; /* reset iscatspace */
       /* --- #bars needed on each side of mid piece --- */
       nbars = (barht<1?0:max2(0,1+(height-baseht)/barht/2)); /*#bars per side*/
       /* --- stack pieces --- */
       sp = topsym; /* start with top piece */
       if ( nbars > 0 ) /* need nbars above middle */
         for ( ibar=1; ibar<=nbars; ibar++ ) sp = rastack(barsym,sp,1,0,0,2);
       sp = rastack(midsym,sp,1,0,0,3); /*mid after top or bars*/
       if ( nbars > 0 ) /* need nbars below middle */
         for ( ibar=1; ibar<=nbars; ibar++ ) sp = rastack(barsym,sp,1,0,0,2);
       sp = rastack(botsym,sp,1,0,0,3); /* bottom below bars or middle */
       delete_subraster(barsym); /* barsym no longer needed */
       } /* --- end-of-if(isokay) --- */
     } /* --- end-of-if(left- or right-{} brace wanted) --- */
   /* -------------------------------------------------------------------------
 [ ] brackets  [ ] brackets
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 else  else
  if ( (lp=strchr(symbol,'[')) != NULL /* left [ bracket wanted */   if ( (lp=strchr(symbol,'[')) != NULL /* left [ bracket wanted */
  ||   (rp=strchr(symbol,']')) != NULL ) /* right ] bracket wanted */   ||   (rp=strchr(symbol,']')) != NULL /* right ] bracket wanted */
   {   ||   (lp2=strstr(symbol,"lceil")) != NULL /* left ceiling wanted */
   /* --- rule_raster ( rasp, top, left, width, height, type=0 ) --- */   ||   (rp2=strstr(symbol,"rceil")) != NULL /* right ceiling wanted */
   int mywidth = min2(width,12); /* max width for horizontal bars */   ||   (lp3=strstr(symbol,"lfloor")) != NULL /* left floor wanted */
   thickness = 1; /* set lines 1 or 2 pixels thick */   ||   (rp3=strstr(symbol,"rfloor")) != NULL /* right floor wanted */
   rule_raster(rasp, 0,0, mywidth,thickness, 0); /* top horizontal bar */   ||   (lp4=strstr(symbol,"llbrack")) != NULL /* left semantic bracket */
   rule_raster(rasp, height-thickness,0, mywidth,thickness, 0); /* bottom */   ||   (rp4=strstr(symbol,"rrbrack")) != NULL ) /* right semantic bracket */
   if ( lp != NULL ) /* left [ bracket wanted */    {
     /* --- use rule_raster ( rasp, top, left, width, height, type=0 ) --- */
     int mywidth = min2(width,12), /* max width for horizontal bars */
    wthick = 1; /* thickness of top.bottom bars */
     thickness = (height<25?1:2); /* set lines 1 or 2 pixels thick */
     if ( lp2!=NULL || rp2!=NULL || lp3!=NULL || rp3 !=NULL ) /*ceil or floor*/
       wthick = thickness; /* same thickness for top/bot bar */
     if ( lp3==NULL && rp3==NULL ) /* set top bar if floor not wanted */
       rule_raster(rasp, 0,0, mywidth,wthick, 0); /* top horizontal bar */
     if ( lp2==NULL && rp2==NULL ) /* set bot bar if ceil not wanted */
       rule_raster(rasp, height-wthick,0, mywidth,thickness, 0); /* bottom */
     if ( lp!=NULL || lp2!=NULL || lp3!=NULL || lp4!=NULL ) /* left bracket */
    rule_raster(rasp, 0,0, thickness,height, 0); /* left vertical bar */     rule_raster(rasp, 0,0, thickness,height, 0); /* left vertical bar */
   if ( rp != NULL ) /* right ] bracket wanted */    if ( lp4 != NULL ) /* 2nd left vertical bar needed */
      rule_raster(rasp, 0,thickness+1, 1,height, 0); /* 2nd left vertical bar */
     if ( rp!=NULL || rp2!=NULL || rp3!=NULL || rp4!=NULL ) /* right bracket */
    rule_raster(rasp, 0,mywidth-thickness, thickness,height, 0); /* right */     rule_raster(rasp, 0,mywidth-thickness, thickness,height, 0); /* right */
     if ( rp4 != NULL ) /* 2nd right vertical bar needed */
      rule_raster(rasp, 0,mywidth-thickness-2, 1,height, 0); /*2nd right vert*/
   isokay = 1; /* set flag */    isokay = 1; /* set flag */
   } /* --- end-of-if(left- or right-[] bracket wanted) --- */    } /* --- end-of-if(left- or right-[] bracket wanted) --- */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
Line 3514  else Line 4027  else
  if ( (lp=strchr(symbol,'<')) != NULL /* left < bracket wanted */   if ( (lp=strchr(symbol,'<')) != NULL /* left < bracket wanted */
  ||   (rp=strchr(symbol,'>')) != NULL ) /* right > bracket wanted */   ||   (rp=strchr(symbol,'>')) != NULL ) /* right > bracket wanted */
   {    {
   /* --- line_raster ( rasp,  row0, col0,  row1, col1,  thickness ) --- */    /* --- use line_raster( rasp,  row0, col0,  row1, col1,  thickness ) --- */
   int mywidth = min2(width,12); /* max width for brackets */    int mywidth = min2(width,12), /* max width for brackets */
   thickness = 1; /* set line pixel thickness */   mythick = 1; /* all lines one pixel thick */
     thickness = (height<25?1:2); /* set line pixel thickness */
   if ( lp != NULL ) /* left < bracket wanted */    if ( lp != NULL ) /* left < bracket wanted */
     { line_raster(rasp,height/2,0,0,mywidth-1,thickness);      { line_raster(rasp,height/2,0,0,mywidth-1,mythick);
       line_raster(rasp,height/2,0,height-1,mywidth-1,thickness); }        if ( thickness>1 )
    line_raster(rasp,height/2,1,0,mywidth-1,mythick);
         line_raster(rasp,height/2,0,height-1,mywidth-1,mythick);
         if ( thickness>1 )
    line_raster(rasp,height/2,1,height-1,mywidth-1,mythick); }
   if ( rp != NULL ) /* right > bracket wanted */    if ( rp != NULL ) /* right > bracket wanted */
     { line_raster(rasp,height/2,mywidth-1,0,0,thickness);      { line_raster(rasp,height/2,mywidth-1,0,0,mythick);
       line_raster(rasp,height/2,mywidth-1,height-1,0,thickness); }        if ( thickness>1 )
    line_raster(rasp,height/2,mywidth-2,0,0,mythick);
         line_raster(rasp,height/2,mywidth-1,height-1,0,mythick);
         if ( thickness>1 )
    line_raster(rasp,height/2,mywidth-2,height-1,0,mythick); }
   isokay = 1; /* set flag */    isokay = 1; /* set flag */
   } /* --- end-of-if(left- or right-<> bracket wanted) --- */    } /* --- end-of-if(left- or right-<> bracket wanted) --- */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
   / \ delimiters
   -------------------------------------------------------------------------- */
   else
    if ( (lp=strchr(symbol,'/')) != NULL /* left /  wanted */
    ||   (rp=strstr(symbol,"\\\\")) != NULL /* right \ wanted */
    ||   (rp2=strstr(symbol,"backsl")) != NULL ) /* right \ wanted */
     {
     /* --- use line_raster( rasp,  row0, col0,  row1, col1,  thickness ) --- */
     int mywidth = width; /* max width for / \ */
     thickness = 1; /* set line pixel thickness */
     if ( lp != NULL ) /* left / wanted */
       line_raster(rasp,0,mywidth-1,height-1,0,thickness);
     if ( rp!=NULL || rp2!=NULL ) /* right \ wanted */
       line_raster(rasp,0,0,height-1,mywidth-1,thickness);
     isokay = 1; /* set flag */
     } /* --- end-of-if(left- or right-/\ delimiter wanted) --- */
   /* -------------------------------------------------------------------------
   arrow delimiters
   -------------------------------------------------------------------------- */
   else
    if ( strstr(symbol,"arrow") != NULL ) /* arrow delimiter wanted */
     {
     /* --- use uparrow_subraster(width,height,pixsz,drctn,isBig) --- */
     int mywidth = width; /* max width for / \ */
     int isBig = (strstr(symbol,"Up")!=NULL /* isBig if we have an Up */
    || strstr(symbol,"Down")!=NULL); /* or a Down */
     int drctn = +1; /* init for uparrow */
     if ( strstr(symbol,"down")!=NULL /* down if we have down */
     ||   strstr(symbol,"Down")!=NULL ) /* or Down */
      { drctn = (-1); /* reset direction to down */
        if ( strstr(symbol,"up")!=NULL /* updown if we have up or Up */
        ||   strstr(symbol,"Up")!=NULL ) /* and down or Down */
         drctn = 0; } /* reset direction to updown */
     sp = uparrow_subraster(mywidth,height,pixsz,drctn,isBig);
     if ( sp != NULL )
      { sp->type = IMAGERASTER; /* image */
        sp->symdef = NULL; /* not applicable for image */
        sp->baseline = height/2 + 2; /* is a little above center good? */
        sp->size = NORMALSIZE; /* size (probably unneeded) */
        isokay = 1; } /* set flag */
     } /* --- end-of-if(arrow delimiter wanted) --- */
   /* -------------------------------------------------------------------------
 \- for | | brackets or \= for || || brackets  \- for | | brackets or \= for || || brackets
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 else  else
Line 3538  else Line 4102  else
   int midcol = width/2; /* middle col, left of mid if even */    int midcol = width/2; /* middle col, left of mid if even */
   if ( rp  != NULL /* left or right || bracket wanted */    if ( rp  != NULL /* left or right || bracket wanted */
   ||   rp2 != NULL ) /* or || in standard tex notation */    ||   rp2 != NULL ) /* or || in standard tex notation */
    { thickness = 1; /* each | of || one pixel thick */     { thickness = (height<75?1:2); /* each | of || 1 or 2 pixels thick*/
      rule_raster(rasp, 0,max2(0,midcol-2), thickness,height, 0); /* left */       rule_raster(rasp, 0,max2(0,midcol-2), thickness,height, 0); /* left */
      rule_raster(rasp, 0,min2(width,midcol+2), thickness,height, 0); }       rule_raster(rasp, 0,min2(width,midcol+2), thickness,height, 0); }
   else /*nb, lp2 spuriously set if rp2 set*/    else /*nb, lp2 spuriously set if rp2 set*/
    if ( lp  != NULL /* left or right | bracket wanted */     if ( lp  != NULL /* left or right | bracket wanted */
    ||   lp2 != NULL ) /* ditto for synomym */     ||   lp2 != NULL ) /* ditto for synomym */
     { thickness = 1; /* set | two pixels thick */      { thickness = (height<75?1:2); /* set | 1 or 2 pixels thick */
       rule_raster(rasp, 0,midcol, thickness,height, 0); } /*mid vertical bar*/        rule_raster(rasp, 0,midcol, thickness,height, 0); } /*mid vertical bar*/
   isokay = 1; /* set flag */    isokay = 1; /* set flag */
   } /* --- end-of-if(left- or right-[] bracket wanted) --- */    } /* --- end-of-if(left- or right-[] bracket wanted) --- */
Line 3552  else Line 4116  else
 back to caller  back to caller
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 end_of_job:  end_of_job:
     if ( msgfp!=NULL && msglevel>=99 )
       fprintf(msgfp,"make_delim> symbol=%.50s, isokay=%d\n",
       (symbol==NULL?"null":symbol),isokay);
   if ( !isokay ) /* don't have requested delimiter */    if ( !isokay ) /* don't have requested delimiter */
     { delete_subraster(sp); /* so free unneeded structure */      { if (sp!=NULL) delete_subraster(sp); /* so free unneeded structure */
       sp = NULL; } /* and signal error to caller */        sp = NULL; } /* and signal error to caller */
   return ( sp ); /*back to caller with delim or NULL*/    return ( sp ); /*back to caller with delim or NULL*/
 } /* --- end-of-function make_delim() --- */  } /* --- end-of-function make_delim() --- */
Line 3639  if ( esclen < 1 )    /* \ followed by no Line 4206  if ( esclen < 1 )    /* \ followed by no
   *ptoken++ = *expression++; /*copy non-alpha, bump ptrs*/    *ptoken++ = *expression++; /*copy non-alpha, bump ptrs*/
 else { /* normal alpha \sequence */  else { /* normal alpha \sequence */
   /* --- respect spaces in text mode, except first space after \escape --- */    /* --- respect spaces in text mode, except first space after \escape --- */
   if ( istext > 0 ) /* in \rm or \it text mode */    if ( istextmode ) /* in \rm or \it text mode */
    if ( istext != 3 ) /* but not \mathbb */     if ( isthischar(*expression,WHITEDELIM) ) /* delim follows \sequence */
     if ( isthischar(*expression,WHITEDELIM) ) /* delim follows \sequence */      expression++; } /* so flush delim */
      expression++; } /* so flush delim */  
 *ptoken = '\000'; /* null-terminate token */  *ptoken = '\000'; /* null-terminate token */
 /* --- back to caller --- */  /* --- back to caller --- */
 end_of_job:  end_of_job:
Line 3708  char *texsubexpr ( char *expression, cha Line 4274  char *texsubexpr ( char *expression, cha
 Allocations and Declarations  Allocations and Declarations
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 char *texchar(); /*next char (or \sequence) from expression*/  char *texchar(); /*next char (or \sequence) from expression*/
 char *leftptr, leftdelim[32] = "(\000", /* left( found in expression */  char *leftptr, leftdelim[256] = "(\000", /* left( found in expression */
  rightdelim[32] = ")\000"; /* and matching right) */   rightdelim[256] = ")\000"; /* and matching right) */
 char *origexpression=expression, *origsubexpr=subexpr; /*original inputs*/  char *origexpression=expression, *origsubexpr=subexpr; /*original inputs*/
   char *strtexchr(), *texleft(); /* check for \left, and get it */
 int gotescape = 0, /* true if leading char of expression is \ */  int gotescape = 0, /* true if leading char of expression is \ */
  prevescape = 0; /* while parsing, true if preceding char \ */   prevescape = 0; /* while parsing, true if preceding char \ */
 int isbrace(); /* check for left,right braces */  int isbrace(); /* check for left,right braces */
Line 3731  if ( maxsubsz < 1 ) maxsubsz = 8192;  /* Line 4298  if ( maxsubsz < 1 ) maxsubsz = 8192;  /*
 /* --- check for escape --- */  /* --- check for escape --- */
 if ( isthischar(*expression,ESCAPE) ) /* expression is escaped */  if ( isthischar(*expression,ESCAPE) ) /* expression is escaped */
   gotescape = 1; /* so set flag accordingly */    gotescape = 1; /* so set flag accordingly */
   /* --- check for \left...\right --- */
   if ( gotescape ) /* begins with \ */
    if ( memcmp(expression+1,"left",4) ) /* and followed by left */
     if ( strchr(left,'l') != NULL ) /* caller wants \left's */
      if ( strtexchr(expression,"\\left") == expression ) /*expression=\left...*/
       { char *pright = texleft(expression,subexpr,maxsubsz, /* find ...\right*/
    (isdelim?NULL:leftdelim),rightdelim);
         if ( isdelim ) strcat(subexpr,rightdelim); /* caller wants delims */
         return ( pright ); /*back to caller past \right*/
       } /* --- end-of-if(expression=="\\left") --- */
 /* --- if first char isn't left{ or script, just return it to caller --- */  /* --- if first char isn't left{ or script, just return it to caller --- */
 if ( !isbrace(expression,left,isescape) ) /* not a left{ */  if ( !isbrace(expression,left,isescape) ) /* not a left{ */
   if ( !isthischar(*expression,SCRIPTS) ) /* and not a script */    if ( !isthischar(*expression,SCRIPTS) ) /* and not a script */
Line 3811  while ( 1 )     /*until balanced right} Line 4388  while ( 1 )     /*until balanced right}
   
   
 /* ==========================================================================  /* ==========================================================================
    * Function: texleft (expression,subexpr,maxsubsz,ldelim,rdelim)
    * Purpose: scans expression, starting after opening \left,
    * and returning ptr after matching closing \right.
    * Everything between is returned in subexpr, if given.
    * Likewise, if given, ldelim returns delimiter after \left
    * and rdelim returns delimiter after \right.
    * If ldelim is given, the returned subexpr doesn't include it.
    * If rdelim is given, the returned pointer is after that delim.
    * --------------------------------------------------------------------------
    * Arguments: expression (I) char * to first char of null-terminated
    * string immediately following opening \left
    * subexpr (O) char * to null-terminated string returning
    * either everything between balanced
    * \left ... \right.  If leftdelim given,
    * subexpr does _not_ contain that delimiter.
    * maxsubsz (I) int containing max #bytes returned
    * in subexpr buffer (0 means unlimited)
    * ldelim (O) char * returning delimiter following
    * opening \left
    * rdelim (O) char * returning delimiter following
    * closing \right
    * --------------------------------------------------------------------------
    * Returns: ( char * ) ptr to the first char of expression
    * past closing \right, or past closing
    * right delimiter if rdelim!=NULL,
    * or NULL for any error.
    * --------------------------------------------------------------------------
    * Notes:     o
    * ======================================================================= */
   /* --- entry point --- */
   char *texleft ( char *expression, char *subexpr, int maxsubsz,
    char *ldelim, char *rdelim )
   {
   /* -------------------------------------------------------------------------
   Allocations and Declarations
   -------------------------------------------------------------------------- */
   char *texchar(), /* get delims after \left,\right */
    *strtexchr(), *pright=expression; /* locate matching \right */
   static char left[16]="\\left", right[16]="\\right"; /* tex delimiters */
   int sublen = 0; /* #chars between \left...\right */
   /* -------------------------------------------------------------------------
   initialization
   -------------------------------------------------------------------------- */
   /* --- init output --- */
   if ( subexpr != NULL ) *subexpr = '\000'; /* init subexpr, if given */
   if ( ldelim  != NULL ) *ldelim  = '\000'; /* init ldelim,  if given */
   if ( rdelim  != NULL ) *rdelim  = '\000'; /* init rdelim,  if given */
   /* --- check args --- */
   if ( expression == NULL ) goto end_of_job; /* no input supplied */
   if ( *expression == '\000' ) goto end_of_job; /* nothing after \left */
   /* --- determine left delimiter  --- */
   if ( ldelim != NULL ) /* caller wants left delim */
    { skipwhite(expression); /* interpret \left ( as \left( */
      expression = texchar(expression,ldelim); } /*delim from expression*/
   /* -------------------------------------------------------------------------
   locate \right balancing opening \left
   -------------------------------------------------------------------------- */
   /* --- first \right following \left --- */
   if ( (pright=strtexchr(expression,right)) /* look for \right after \left */
   !=   NULL ) { /* found it */
    /* --- find matching \right by pushing past any nested \left's --- */
    char *pleft = expression; /* start after first \left( */
    while ( 1 ) { /*break when matching \right found*/
     /* -- locate next nested \left if there is one --- */
     if ( (pleft=strtexchr(pleft,left)) /* find next \left */
     ==   NULL ) break; /*no more, so matching \right found*/
     pleft += strlen(left); /* push ptr past \left token */
     if ( pleft >= pright ) break; /* not nested if \left after \right*/
     /* --- have nested \left, so push forward to next \right --- */
     if ( (pright=strtexchr(pright+strlen(right),right)) /* find next \right */
     ==   NULL ) break; /* ran out of \right's */
     } /* --- end-of-while(1) --- */
    } /* --- end-of-if(pright!=NULL) --- */
   /* --- set subexpression length, push pright past \right --- */
   if ( pright != (char *)NULL ) /* found matching \right */
    { sublen = (int)(pright-expression); /* #chars between \left...\right */
      pright += strlen(right); } /* so push pright past \right */
   /* -------------------------------------------------------------------------
   get rightdelim and subexpr between \left...\right
   -------------------------------------------------------------------------- */
   /* --- get delimiter following \right --- */
   if ( rdelim != NULL ) /* caller wants right delim */
    if ( pright == (char *)NULL ) /* assume \right. at end of exprssn*/
     { strcpy(rdelim,"."); /* set default \right. */
       sublen = strlen(expression); /* use entire remaining expression */
       pright = expression + sublen; } /* and push pright to end-of-string*/
    else /* have explicit matching \right */
     { skipwhite(pright); /* interpret \right ) as \right) */
       pright = texchar(pright,rdelim); /* pull delim from expression */
       if ( *rdelim == '\000' ) strcpy(rdelim,"."); } /* or set \right. */
   /* --- get subexpression between \left...\right --- */
   if ( sublen > 0 ) /* have subexpr */
    if ( subexpr != NULL ) { /* and caller wants it */
     if ( maxsubsz > 0 ) sublen = min2(sublen,maxsubsz-1); /* max buffer size */
     memcpy(subexpr,expression,sublen); /* stuff between \left...\right */
     subexpr[sublen] = '\000'; } /* null-terminate subexpr */
   end_of_job:
     if ( msglevel>=99 && msgfp!=NULL )
       { fprintf(msgfp,"texleft> ldelim=%s, rdelim=%s, subexpr=%.128s\n",
         (ldelim==NULL?"none":ldelim),(rdelim==NULL?"none":rdelim),
         (subexpr==NULL?"none":subexpr)); fflush(msgfp); }
     return ( pright );
   } /* --- end-of-function texleft --- */
   
   
   /* ==========================================================================
  * Function: texscripts ( expression, subscript, superscript, which )   * Function: texscripts ( expression, subscript, superscript, which )
  * Purpose: scans expression, returning subscript and/or superscript   * Purpose: scans expression, returning subscript and/or superscript
  * if expression is of the form _x^y or ^{x}_{y},   * if expression is of the form _x^y or ^{x}_{y},
Line 3846  Allocations and Declarations Line 4529  Allocations and Declarations
 char *texsubexpr(); /* next subexpression from expression */  char *texsubexpr(); /* next subexpression from expression */
 int gotsub=0, gotsup=0; /* check that we don't eat, e.g., x_1_2 */  int gotsub=0, gotsup=0; /* check that we don't eat, e.g., x_1_2 */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 init "scripts" and skip leading whitespace  init "scripts"
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 /* --- skip leading whitespace and check for end-of-string --- */  if ( subscript != NULL ) *subscript = '\000'; /*init in case no subscript*/
 *subscript = *superscript = '\000'; /* init in case no scripts */  if ( superscript!=NULL ) *superscript = '\000'; /*init in case no super*/
 skipwhite(expression); /* leading whitespace gone */  
 if ( *expression == '\000' ) return(expression); /* nothing left to scan */  
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 get subscript and/or superscript from expression  get subscript and/or superscript from expression
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 while ( expression != NULL )  while ( expression != NULL ) {
     skipwhite(expression); /* leading whitespace gone */
     if ( *expression == '\000' ) return(expression); /* nothing left to scan */
   if ( isthischar(*expression,SUBSCRIPT) /* found _ */    if ( isthischar(*expression,SUBSCRIPT) /* found _ */
   &&   (which==1 || which>2 ) ) /* and caller wants it */    &&   (which==1 || which>2 ) ) /* and caller wants it */
     { if ( gotsub /* found 2nd subscript */      { if ( gotsub /* found 2nd subscript */
Line 3871  while ( expression != NULL ) Line 4554  while ( expression != NULL )
  expression = texsubexpr(expression+1,superscript,0,"{","}",0,0); }   expression = texsubexpr(expression+1,superscript,0,"{","}",0,0); }
     else /* neither _ nor ^ */      else /* neither _ nor ^ */
       return ( expression ); /*return ptr past "scripts"*/        return ( expression ); /*return ptr past "scripts"*/
     } /* --- end-of-while(expression!=NULL) --- */
 return ( expression );  return ( expression );
 } /* --- end-of-function texscripts() --- */  } /* --- end-of-function texscripts() --- */
   
Line 4140  static struct { char *html; char *args; Line 4824  static struct { char *html; char *args;
    LaTeX Macro  #args,default   template...     LaTeX Macro  #args,default   template...
    ------------------------------------------ */     ------------------------------------------ */
    { "\\lvec", "2n", "{#2_1,\\cdots,#2_{#1}}" },     { "\\lvec", "2n", "{#2_1,\\cdots,#2_{#1}}" },
      { "\\grave", "1", "{\\stackrel{\\Huge\\gravesym}{#1}}" }, /* \grave */
      { "\\acute", "1", "{\\stackrel{\\Huge\\acutesym}{#1}}" }, /* \acute */
      { "\\check", "1", "{\\stackrel{\\Huge\\checksym}{#1}}" }, /* \check */
      { "\\breve", "1", "{\\stackrel{\\Huge\\brevesym}{#1}}" }, /* \breve */
    { "\\overset", NULL, "\\stackrel" }, /* just an alias */     { "\\overset", NULL, "\\stackrel" }, /* just an alias */
    { "\\underset", "2", "\\relstack{#2}{#1}" }, /* reverse args */     { "\\underset", "2", "\\relstack{#2}{#1}" }, /* reverse args */
    /* ---------------------------------------     /* ---------------------------------------
      html    termchar   LaTeX equivalent...      html char termchar  LaTeX equivalent...
    --------------------------------------- */     --------------------------------------- */
    { "&quot", ";", "\"" }, /* &quot; is first, &#034; */     { "&quot", ";", "\"" }, /* &quot; is first, &#034; */
    { "&amp", ";", "&" },     { "&amp", ";", "&" },
Line 4168  static struct { char *html; char *args; Line 4856  static struct { char *html; char *args;
    { "&atilde", ";", "{\\rm~\\tilde~a}" },     { "&atilde", ";", "{\\rm~\\tilde~a}" },
    { "&yuml", ";", "{\\rm~\\ddot~y}" },  /* &yuml; is last, &#255; */     { "&yuml", ";", "{\\rm~\\ddot~y}" },  /* &yuml; is last, &#255; */
    /* ---------------------------------------     /* ---------------------------------------
       html tag  termchar  LaTeX equivalent...
      --------------------------------------- */
      { "<br>", NULL, "\\\\" },
      { "<br/>", NULL, "\\\\" },
      { "<Br>", NULL, "\\\\" },
      { "<Br/>", NULL, "\\\\" },
      { "<BR>", NULL, "\\\\" },
      { "<BR/>", NULL, "\\\\" },
      /* ---------------------------------------
      LaTeX   termchar   mimeTeX equivalent...       LaTeX   termchar   mimeTeX equivalent...
    --------------------------------------- */     --------------------------------------- */
    { "\\AA", NULL, "{\\rm~A\\limits^{-1$o}}" },     { "\\AA", NULL, "{\\rm~A\\limits^{-1$o}}" },
    { "\\aa", NULL, "{\\rm~a\\limits^{-1$o}}" },     { "\\aa", NULL, "{\\rm~a\\limits^{-1$o}}" },
    { "\\bmod", NULL, "{\\hspace2{\\rm~mod}\\hspace2}" },     { "\\bmod", NULL, "{\\hspace2{\\rm~mod}\\hspace2}" },
    { "\\vdots", NULL, "{\\raisebox3{\\rotatebox{90}{\\ldots}}}" },     { "\\vdots", NULL, "{\\raisebox3{\\rotatebox{90}{\\ldots}}}" },
      { "\\dots", NULL, "{\\cdots}" },
    { "\\cdots", NULL, "{\\raisebox3{\\ldots}}" },     { "\\cdots", NULL, "{\\raisebox3{\\ldots}}" },
    { "\\ldots", NULL, "{\\fs4.\\hspace1.\\hspace1.}" },     { "\\ldots", NULL, "{\\fs4.\\hspace1.\\hspace1.}" },
    { "\\ddots", NULL, "{\\fs4\\raisebox8.\\hspace1\\raisebox4.\\hspace1.}"},     { "\\ddots", NULL, "{\\fs4\\raisebox8.\\hspace1\\raisebox4.\\hspace1.}"},
    { "\\notin", NULL, "{\\not\\in}" },     { "\\notin", NULL, "{\\not\\in}" },
    { "\\neq", NULL, "{\\not=}" },     { "\\neq", NULL, "{\\not=}" },
      { "\\ne", NULL, "{\\not=}" },
    { "\\hbar", NULL, "{\\compose~h{{\\fs{-1}-\\atop\\vspace3}}}" },     { "\\hbar", NULL, "{\\compose~h{{\\fs{-1}-\\atop\\vspace3}}}" },
    { "\\angle", NULL, "{\\compose{\\hspace{3}\\lt}{\\circle(10,15;-80,80)}}"},     { "\\angle", NULL, "{\\compose{\\hspace{3}\\lt}{\\circle(10,15;-80,80)}}"},
      { "\\textcelsius", NULL, "{\\textdegree C}"},
      { "\\textdegree", NULL, "{\\Large^{^{\\tiny\\mathbf o}}}"},
    { "\\cr", NULL, "\\\\" },     { "\\cr", NULL, "\\\\" },
      { "\\iiint", NULL, "{\\int\\int\\int}\\limits" },
    { "\\iint", NULL, "{\\int\\int}\\limits" },     { "\\iint", NULL, "{\\int\\int}\\limits" },
    { "\\Bigiint", NULL, "{\\Bigint\\Bigint}\\limits" },     { "\\Bigiint", NULL, "{\\Bigint\\Bigint}\\limits" },
      { "\\bigsqcap",NULL, "{\\fs{+4}\\sqcap}" },
    { "!`", NULL, "{\\raisebox{-2}{\\rotatebox{180}{!}}}" },     { "!`", NULL, "{\\raisebox{-2}{\\rotatebox{180}{!}}}" },
    { "?`", NULL, "{\\raisebox{-2}{\\rotatebox{180}{?}}}" },     { "?`", NULL, "{\\raisebox{-2}{\\rotatebox{180}{?}}}" },
      { "^\'", "embed","\'" }, /* avoid ^^ when re-xlating \' below */
      { "\'\'\'\'","embed","^{\\fs{-1}\\prime\\prime\\prime\\prime}" },
      { "\'\'\'", "embed","^{\\fs{-1}\\prime\\prime\\prime}" },
      { "\'\'", "embed","^{\\fs{-1}\\prime\\prime}" },
      { "\'", "embed","^{\\fs{-1}\\prime}" },
    { "\\rightleftharpoons",NULL,"{\\rightharpoonup\\atop\\leftharpoondown}" },     { "\\rightleftharpoons",NULL,"{\\rightharpoonup\\atop\\leftharpoondown}" },
      { "\\therefore",NULL,"{\\Huge\\raisebox{-4}{.\\atop.\\,.}}" },
    { "\\LaTeX", NULL, "{\\rm~L\\raisebox{3}{\\fs{-1}A}\\TeX}" },     { "\\LaTeX", NULL, "{\\rm~L\\raisebox{3}{\\fs{-1}A}\\TeX}" },
    { "\\TeX", NULL, "{\\rm~T\\raisebox{-3}{E}X}" },     { "\\TeX", NULL, "{\\rm~T\\raisebox{-3}{E}X}" },
    { "\\cyan", NULL, "{\\reverse\\red\\reversebg}" },     { "\\cyan", NULL, "{\\reverse\\red\\reversebg}" },
    { "\\magenta",NULL, "{\\reverse\\green\\reversebg}" },     { "\\magenta",NULL, "{\\reverse\\green\\reversebg}" },
    { "\\yellow",NULL, "{\\reverse\\blue\\reversebg}" },     { "\\yellow",NULL, "{\\reverse\\blue\\reversebg}" },
      { "\\cancel",NULL, "\\Not" },
    { "\\hhline",NULL, "\\Hline" },     { "\\hhline",NULL, "\\Hline" },
    { "\\Hline", NULL, "\\hline\\,\\\\\\hline" },     { "\\Hline", NULL, "\\hline\\,\\\\\\hline" },
    /* ---------------------------------------------------------     /* ---------------------------------------------------------
Line 4202  static struct { char *html; char *args; Line 4912  static struct { char *html; char *args;
    { "cos", "1", "{\\cos{#1}}" },     { "cos", "1", "{\\cos{#1}}" },
    { "asin", "1", "{\\sin^{-1}{#1}}" },     { "asin", "1", "{\\sin^{-1}{#1}}" },
    { "acos", "1", "{\\cos^{-1}{#1}}" },     { "acos", "1", "{\\cos^{-1}{#1}}" },
    { "exp", "1", "{e^{#1}}" },     { "exp", "1", "{{\\rm~e}^{#1}}" },
    { "det", "1", "{\\left|{#1}\\right|}" },     { "det", "1", "{\\left|{#1}\\right|}" },
    /* ---------------------------------------     /* ---------------------------------------
    LaTeX Constant    termchar   value...     LaTeX Constant    termchar   value...
Line 4277  for(isymbol=0; (htmlsym=symbols[isymbol] Line 4987  for(isymbol=0; (htmlsym=symbols[isymbol]
   {    {
   int htmllen = strlen(htmlsym); /* length of escape, _without_ ; */    int htmllen = strlen(htmlsym); /* length of escape, _without_ ; */
   int isalgebra = isalpha((int)(*htmlsym)); /* leading char alphabetic */    int isalgebra = isalpha((int)(*htmlsym)); /* leading char alphabetic */
     int isembedded = 0; /* true to xlate even if embedded */
   char *aleft="{([<|", *aright="})]>|"; /*left,right delims for alg syntax*/    char *aleft="{([<|", *aright="})]>|"; /*left,right delims for alg syntax*/
   char *args = symbols[isymbol].args, /* number {}-args, optional []-arg */    char *args = symbols[isymbol].args, /* number {}-args, optional []-arg */
  *htmlterm = args, /*if *args nonumeric, then html term*/   *htmlterm = args, /*if *args nonumeric, then html term*/
Line 4288  for(isymbol=0; (htmlsym=symbols[isymbol] Line 4999  for(isymbol=0; (htmlsym=symbols[isymbol]
      { htmlterm = NULL; /* if so, then we have no htmlterm */       { htmlterm = NULL; /* if so, then we have no htmlterm */
        *abuff = *args;  abuff[1] = '\000'; /* #args char in ascii buffer */         *abuff = *args;  abuff[1] = '\000'; /* #args char in ascii buffer */
        nargs = atoi(abuff); } /* interpret #args to numeric */         nargs = atoi(abuff); } /* interpret #args to numeric */
       else if ( strncmp(args,"embed",5) == 0 ) /* xlate even if embedded */
        { htmlterm = NULL; /* if so, then we have no htmlterm */
          isembedded = 1 ; } /* turn on embedded flag */
   expptr = expression; /* re-start search at beginning */    expptr = expression; /* re-start search at beginning */
   while ( (tokptr=strstr(expptr,htmlsym)) != NULL ) /* found another sym */    while ( (tokptr=strstr(expptr,htmlsym)) != NULL ) /* found another sym */
     { char termchar = *(tokptr+htmllen); /* char terminating html sequence */      { char termchar = *(tokptr+htmllen), /* char terminating html sequence */
              prevchar = (tokptr==expptr?' ':*(tokptr-1)); /*char preceding html*/
       int escapelen = htmllen; /* total length of escape sequence */        int escapelen = htmllen; /* total length of escape sequence */
       *abuff = '\000'; /* default to empty string */        *abuff = '\000'; /* default to empty string */
       if ( latexsym != NULL ) /* table has .latex xlation */        if ( latexsym != NULL ) /* table has .latex xlation */
Line 4298  for(isymbol=0; (htmlsym=symbols[isymbol] Line 5013  for(isymbol=0; (htmlsym=symbols[isymbol]
  strcpy(abuff,latexsym); /* so get local copy */   strcpy(abuff,latexsym); /* so get local copy */
       if ( htmlterm != NULL ) /* sequence may have terminator */        if ( htmlterm != NULL ) /* sequence may have terminator */
  escapelen += (isthischar(termchar,htmlterm)?1:0); /*add terminator*/   escapelen += (isthischar(termchar,htmlterm)?1:0); /*add terminator*/
       if ( isalpha((int)termchar) ) /*we just have prefix of longer sym*/        if ( !isembedded ) /* don't xlate embedded sequence */
          if ( isalpha((int)termchar) ) /*we just have prefix of longer sym*/
  { expptr = tokptr+htmllen; /* just resume search after prefix */   { expptr = tokptr+htmllen; /* just resume search after prefix */
   continue; } /* but don't replace it */    continue; } /* but don't replace it */
         if ( isembedded ) /* for embedded sequence */
    if ( isthischar(prevchar,ESCAPE) ) /* don't xlate escaped char */
     { expptr = tokptr+htmllen; /*just resume search after literal*/
       continue; } /* but don't replace it */
       if ( !isthischar(*htmlsym,ESCAPE) /* our symbol isn't escaped */        if ( !isthischar(*htmlsym,ESCAPE) /* our symbol isn't escaped */
         &&   isalpha(*htmlsym) /* and our symbol starts with alpha*/
       &&   !isthischar(*htmlsym,"&") ) /* and not an &html; special char */        &&   !isthischar(*htmlsym,"&") ) /* and not an &html; special char */
        if ( tokptr != expression ) /* then if we're past beginning */         if ( tokptr != expression ) /* then if we're past beginning */
  if ( isthischar(*(tokptr-1),ESCAPE) /*and if inline symbol escaped*/   if ( isthischar(*(tokptr-1),ESCAPE) /*and if inline symbol escaped*/
Line 4527  char *strtexchr ( char *string, char *te Line 5248  char *strtexchr ( char *string, char *te
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 Allocations and Declarations  Allocations and Declarations
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 char *strstr(), delim, *ptexchr=(char *)NULL;  /* ptr returned to caller*/  char delim, *ptexchr=(char *)NULL; /* ptr returned to caller*/
 char *pstring = string; /* start or continue up search here*/  char *pstring = string; /* start or continue up search here*/
 int texchrlen = (texchr==NULL?0:strlen(texchr)); /* #chars in texchr */  int texchrlen = (texchr==NULL?0:strlen(texchr)); /* #chars in texchr */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
Line 4660  subraster *get_charsubraster(),  /* char Line 5381  subraster *get_charsubraster(),  /* char
 int delete_subraster(); /* free everything before returning*/  int delete_subraster(); /* free everything before returning*/
 /*int pixsz = 1;*/ /*default #bits per pixel, 1=bitmap*/  /*int pixsz = 1;*/ /*default #bits per pixel, 1=bitmap*/
 /* --- global values saved/restored at each recursive iteration --- */  /* --- global values saved/restored at each recursive iteration --- */
 int wastext = istext, /* initial istext mode flag */  int wasstring = isstring, /* initial isstring mode flag */
  wasstring = isstring, /* initial isstring mode flag */  
  wasdisplaystyle = isdisplaystyle, /*initial displaystyle mode flag*/   wasdisplaystyle = isdisplaystyle, /*initial displaystyle mode flag*/
    oldfontnum = fontnum, /* initial font family */
  oldfontsize = fontsize, /* initial fontsize */   oldfontsize = fontsize, /* initial fontsize */
  olddisplaysize = displaysize, /* initial \displaystyle size */   olddisplaysize = displaysize, /* initial \displaystyle size */
  oldshrinkfactor = shrinkfactor, /* initial shrinkfactor */   oldshrinkfactor = shrinkfactor, /* initial shrinkfactor */
  oldsquashmargin = squashmargin, /* initial squashmargin */   oldsmashmargin = smashmargin, /* initial smashmargin */
  oldissquashdelta = issquashdelta, /* initial issquashdelta */   oldissmashdelta = issmashdelta, /* initial issmashdelta */
  *oldworkingparam = workingparam; /* initial working parameter */   *oldworkingparam = workingparam; /* initial working parameter */
 subraster *oldworkingbox = workingbox, /* initial working box */  subraster *oldworkingbox = workingbox, /* initial working box */
  *oldleftexpression = leftexpression; /*left half rasterized so far*/   *oldleftexpression = leftexpression; /*left half rasterized so far*/
Line 4724  while ( 1 ) Line 5445  while ( 1 )
     if ( (leftsymdef=symdef=get_symdef(chartoken)) /*mathchardef for token*/      if ( (leftsymdef=symdef=get_symdef(chartoken)) /*mathchardef for token*/
     ==  NULL ) /* lookup failed */      ==  NULL ) /* lookup failed */
      { char literal[512] = "[?]"; /*display for unrecognized literal*/       { char literal[512] = "[?]"; /*display for unrecognized literal*/
        int  wastext = istext; /* error display in default mode */         int  oldfontnum = fontnum; /* error display in default mode */
        if ( msgfp!=NULL && msglevel >= 29 ) /* display unrecognized symbol */         if ( msgfp!=NULL && msglevel >= 29 ) /* display unrecognized symbol */
  { fprintf(msgfp,"rasterize> get_symdef() failed for \"%s\"\n",   { fprintf(msgfp,"rasterize> get_symdef() failed for \"%s\"\n",
    chartoken); fflush(msgfp); }     chartoken); fflush(msgfp); }
        sp = (subraster *)NULL; /* init to signal failure */         sp = (subraster *)NULL; /* init to signal failure */
        if ( warninglevel < 1 ) continue; /* warnings not wanted */         if ( warninglevel < 1 ) continue; /* warnings not wanted */
        istext = 0; /* reset from \mathbb, etc */         fontnum = 0; /* reset from \mathbb, etc */
        if ( isthischar(*chartoken,ESCAPE) ) /* we got unrecognized \escape */         if ( isthischar(*chartoken,ESCAPE) ) /* we got unrecognized \escape */
  { /* --- so display literal {\rm~[\backslash~chartoken?]} ---  */   { /* --- so display literal {\rm~[\backslash~chartoken?]} ---  */
   strcpy(literal,"{\\rm~[\\backslash~"); /* init token */    strcpy(literal,"{\\rm~[\\backslash~"); /* init token */
   strcat(literal,chartoken+1); /* add chars following leading \ */    strcat(literal,chartoken+1); /* add chars following leading \ */
   strcat(literal,"?]}"); } /* add closing brace */    strcat(literal,"?]}"); } /* add closing brace */
        sp = rasterize(literal,size-1); /* rasterize literal token */         sp = rasterize(literal,size-1); /* rasterize literal token */
        istext = wastext; /* reset text mode */         fontnum = oldfontnum; /* reset font family */
        if ( sp == (subraster *)NULL ) continue; } /*flush if rasterize fails*/         if ( sp == (subraster *)NULL ) continue; } /*flush if rasterize fails*/
     else /* --- check if we have special handler to process this token --- */      else /* --- check if we have special handler to process this token --- */
      if ( symdef->handler != NULL ) /* have a handler for this token */       if ( symdef->handler != NULL ) /* have a handler for this token */
Line 4773  while ( 1 ) Line 5494  while ( 1 )
   if ( natoms < 1 /* nothing previous to concat */    if ( natoms < 1 /* nothing previous to concat */
   ||   expraster == NULL /* or previous was complete error */    ||   expraster == NULL /* or previous was complete error */
   ||   isreplaceleft ) /* or we're replacing previous */    ||   isreplaceleft ) /* or we're replacing previous */
     { expraster = subrastcpy(sp); /* so just copy static CHARASTER */      { if ( 1 && expraster!=NULL ) /* probably replacing left */
    delete_subraster(expraster); /* so first free original left */
         expraster = subrastcpy(sp); /* copy static CHARASTER or left */
       isreplaceleft = 0; } /* reset replacement flag */        isreplaceleft = 0; } /* reset replacement flag */
   else /*we've already built up atoms so...*/    else /*we've already built up atoms so...*/
    if ( sp != NULL ) /* ...if we have a new component */     if ( sp != NULL ) /* ...if we have a new component */
Line 4796  end_of_job: Line 5519  end_of_job:
       if ( expraster != (subraster *)NULL ) /* i.e., if natoms>0 */        if ( expraster != (subraster *)NULL ) /* i.e., if natoms>0 */
  type_raster(expraster->image,msgfp); /* display completed raster */   type_raster(expraster->image,msgfp); /* display completed raster */
       fflush(msgfp); } /* flush msgfp buffer */        fflush(msgfp); } /* flush msgfp buffer */
     /* --- set final raster buffer --- */
     if ( 1 && expraster != (subraster *)NULL ) /* have an expression */
       { expraster->type = IMAGERASTER; /* set type to constructed image */
         if ( istextmode ) /* but in text mode */
           expraster->type = blanksignal; /* set type to avoid smash */
         expraster->size = fontsize; } /* set original input font size */
   /* --- restore flags/values to original saved values --- */    /* --- restore flags/values to original saved values --- */
   istext = wastext; /* text mode reset */  
   isstring = wasstring; /* string mode reset */    isstring = wasstring; /* string mode reset */
   isdisplaystyle = wasdisplaystyle; /* displaystyle mode reset */    isdisplaystyle = wasdisplaystyle; /* displaystyle mode reset */
     fontnum = oldfontnum; /* font family reset */
   fontsize = oldfontsize; /* fontsize reset */    fontsize = oldfontsize; /* fontsize reset */
   displaysize = olddisplaysize; /* \displaystyle size reset */    displaysize = olddisplaysize; /* \displaystyle size reset */
   shrinkfactor = oldshrinkfactor; /* shrinkfactor reset */    shrinkfactor = oldshrinkfactor; /* shrinkfactor reset */
   squashmargin = oldsquashmargin; /* squashmargin reset */    smashmargin = oldsmashmargin; /* smashmargin reset */
   issquashdelta = oldissquashdelta; /* issquashdelta reset */    issmashdelta = oldissmashdelta; /* issmashdelta reset */
   workingparam = oldworkingparam; /* working parameter reset */    workingparam = oldworkingparam; /* working parameter reset */
   workingbox = oldworkingbox; /* working box reset */    workingbox = oldworkingbox; /* working box reset */
   leftexpression = oldleftexpression; /* leftexpression reset */    leftexpression = oldleftexpression; /* leftexpression reset */
Line 4812  end_of_job: Line 5541  end_of_job:
   unitlength = oldunitlength; /* unitlength reset */    unitlength = oldunitlength; /* unitlength reset */
   recurlevel--; /* unwind one recursion level */    recurlevel--; /* unwind one recursion level */
   /* --- return final subraster to caller --- */    /* --- return final subraster to caller --- */
   if ( 1 && expraster != (subraster *)NULL ) /* have an expression */  
     { expraster->type = IMAGERASTER; /* set type to constructed image */  
       expraster->size = fontsize; } /* set original input font size */  
   return ( expraster );    return ( expraster );
 } /* --- end-of-function rasterize() --- */  } /* --- end-of-function rasterize() --- */
   
Line 4858  subraster *rasterize(), *sp=NULL; /* ras Line 5584  subraster *rasterize(), *sp=NULL; /* ras
 int isheight = 1; /*true=full height, false=baseline*/  int isheight = 1; /*true=full height, false=baseline*/
 int height, /* height of rasterized noparens[] */  int height, /* height of rasterized noparens[] */
  baseline; /* and its baseline */   baseline; /* and its baseline */
 int family = CMEX10; /* family for paren chars */  int family = /*CMSYEX*/ CMEX10; /* family for paren chars */
 subraster *get_delim(), *lp=NULL, *rp=NULL; /* left and right paren chars */  subraster *get_delim(), *lp=NULL, *rp=NULL; /* left and right paren chars */
 subraster *rastcat(); /* concatanate subrasters */  subraster *rastcat(); /* concatanate subrasters */
 int delete_subraster(); /*in case of error after allocation*/  int delete_subraster(); /*in case of error after allocation*/
Line 4952  subraster *rastscripts(), *rastdispmath( Line 5678  subraster *rastscripts(), *rastdispmath(
  *rastcat(), /* may need to concat scripts */   *rastcat(), /* may need to concat scripts */
  *scriptsp = basesp; /* and this will become the result */   *scriptsp = basesp; /* and this will become the result */
 int isdisplay = (-1); /* set 1 for displaystyle, else 0 */  int isdisplay = (-1); /* set 1 for displaystyle, else 0 */
 int oldsquashmargin = squashmargin; /* save original squashmargin */  int oldsmashmargin = smashmargin; /* save original smashmargin */
 int type_raster(); /* display debugging output */  int type_raster(); /* display debugging output */
 /* --- to check for \limits or \nolimits preceding scripts --- */  /* --- to check for \limits or \nolimits preceding scripts --- */
 char *texchar(), *exprptr=*expression, limtoken[255]; /*check for \limits*/  char *texchar(), *exprptr=*expression, limtoken[255]; /*check for \limits*/
Line 5013  else     /* scripts alongside base symbo Line 5739  else     /* scripts alongside base symbo
     scriptsp = basesp; /* so just return unscripted symbol*/      scriptsp = basesp; /* so just return unscripted symbol*/
   else /* symbols followed by scripts */    else /* symbols followed by scripts */
     if ( basesp != NULL ) /* have base symbol */      if ( basesp != NULL ) /* have base symbol */
      { squashmargin = 0; /* don't squash script */       { smashmargin = 0; /* don't smash script */
        scriptsp = rastcat(basesp,scriptsp,2); /*concat scripts to base sym*/         /*scriptsp = rastcat(basesp,scriptsp,2);*//*concat scripts to base sym*/
          scriptsp = rastcat(basesp,scriptsp,3); /*concat scripts to base sym*/
        scriptsp->type = IMAGERASTER; /* flip type of composite object */         scriptsp->type = IMAGERASTER; /* flip type of composite object */
        scriptsp->size = size; } /* and set font size */         scriptsp->size = size; } /* and set font size */
 end_of_job:  end_of_job:
   squashmargin = oldsquashmargin; /* reset original squashmargin */    smashmargin = oldsmashmargin; /* reset original smashmargin */
   if ( msgfp!=NULL && msglevel>=99 )    if ( msgfp!=NULL && msglevel>=99 )
     { fprintf(msgfp,"rastlimits> scriptlevel#%d returning %s\n",      { fprintf(msgfp,"rastlimits> scriptlevel#%d returning %s\n",
  scriptlevel,(scriptsp==NULL?"null":"..."));   scriptlevel,(scriptsp==NULL?"null":"..."));
Line 5259  end_of_job: Line 5986  end_of_job:
  * basesp (I) subraster *  to character (or subexpression)   * basesp (I) subraster *  to character (or subexpression)
  * immediately preceding leading left{   * immediately preceding leading left{
  * (unused, but passed for consistency)   * (unused, but passed for consistency)
  * ildelim (I) int containing ldelims index of   * ildelim (I) int containing ldelims[] index of
  * left delimiter   * left delimiter
  * arg2 (I) int unused   * arg2 (I) int unused
  * arg3 (I) int unused   * arg3 (I) int unused
Line 5284  int family=CMSYEX,   /* get_delim() fami Line 6011  int family=CMSYEX,   /* get_delim() fami
  height=0, rheight=0, /* subexpr, right delim height */   height=0, rheight=0, /* subexpr, right delim height */
  margin=(size+1), /* delim height margin over subexpr*/   margin=(size+1), /* delim height margin over subexpr*/
  opmargin=(5); /* extra margin for \int,\sum,\etc */   opmargin=(5); /* extra margin for \int,\sum,\etc */
 char subexpr[8192]; /* chars between \left...\right */  char /* *texleft(),*/ subexpr[8192]; /* chars between \left...\right */
 char *texchar(), /* get delims after \left,\right */  char *texchar(), /* get delims after \left,\right */
  ldelim[256]=".", rdelim[256]="."; /* delims following \left,\right */   ldelim[256]=".", rdelim[256]="."; /* delims following \left,\right */
 char *strtexchr(), *pleft, *pright; /*locate \right matching our \left*/  char *strtexchr(), *pleft, *pright; /*locate \right matching our \left*/
 int isleftdot=0, isrightdot=0; /* true if \left. or \right. */  int isleftdot=0, isrightdot=0; /* true if \left. or \right. */
 int sublen=0; /* strlen(subexpr) */  int sublen=0; /* strlen(subexpr) */
 int idelim=0; /* 1=left,2=right */  int idelim=0; /* 1=left,2=right */
   /* int gotldelim = 0; */ /* true if ildelim given by caller */
 int delete_subraster(); /* free subraster if rastleft fails*/  int delete_subraster(); /* free subraster if rastleft fails*/
 int wasdisplaystyle = isdisplaystyle; /* save current displaystyle */  int wasdisplaystyle = isdisplaystyle; /* save current displaystyle */
   int istextleft=0, istextright=0; /* true for non-displaystyle delims*/
 /* --- recognized delimiters --- */  /* --- recognized delimiters --- */
 static char left[16]="\\left", right[16]="\\right"; /* tex delimiters */  static char left[16]="\\left", right[16]="\\right"; /* tex delimiters */
 static char *ldelims[] = {  static char *ldelims[] = {
Line 5329  static char *xto[] =   /* ...to this ins Line 6058  static char *xto[] =   /* ...to this ins
      "<", /* \langle to < */       "<", /* \langle to < */
      ">", /* \rangle to > */       ">", /* \rangle to > */
      NULL } ; /* --- end-of-xto[] --- */       NULL } ; /* --- end-of-xto[] --- */
   /* --- non-displaystyle delimiters --- */
   static char *textdelims[] = /* these delims _aren't_ display */
      { "|", "=",
        "(", ")",
        "[", "]",
        "<", ">",
        "{", "}",
        "dbl", /* \lbrackdbl and \rbrackdbl */
        NULL } ; /* --- end-of-textdelims[] --- */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 initialization  initialization
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
Line 5336  initialization Line 6074  initialization
 if ( *(*expression) == '\000' ) goto end_of_job; /* nothing after \left */  if ( *(*expression) == '\000' ) goto end_of_job; /* nothing after \left */
 /* --- determine left delimiter, and set default \right. delimiter --- */  /* --- determine left delimiter, and set default \right. delimiter --- */
 if ( ildelim!=NOVALUE && ildelim>=1 ) /* called with explicit left delim */  if ( ildelim!=NOVALUE && ildelim>=1 ) /* called with explicit left delim */
   strcpy(ldelim,ldelims[ildelim]); /* so just get a local copy */   { strcpy(ldelim,ldelims[ildelim]); /* so just get a local copy */
      /* gotldelim = 1; */ } /* and set flag that we got it */
 else /* trapped \left without delim */  else /* trapped \left without delim */
  { skipwhite(*expression); /* interpret \left ( as \left( */   { skipwhite(*expression); /* interpret \left ( as \left( */
    *expression = texchar(*expression,ldelim); } /*pull delim from expression*/     if ( *(*expression) == '\000' ) /* end-of-string after \left */
         goto end_of_job; /* so return NULL */
      *expression = texchar(*expression,ldelim); /*pull delim from expression*/
      if ( *expression == NULL /* probably invalid end-of-string */
      ||   *ldelim == '\000' ) goto end_of_job; } /* no delimiter */
 strcpy(rdelim,"."); /* init default \right. delim */  strcpy(rdelim,"."); /* init default \right. delim */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 locate \right balancing our opening \left  locate \right balancing our opening \left
Line 5364  if ( (pright=strtexchr(*expression,right Line 6107  if ( (pright=strtexchr(*expression,right
 push past \left(_a^b sub/superscripts, if present  push past \left(_a^b sub/superscripts, if present
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 pleft = *expression; /*reset pleft after opening \left( */  pleft = *expression; /*reset pleft after opening \left( */
 /*lp=*/ rastlimits(expression,size,lp); /*dummy call push expression past b*/  if ( (lp=rastlimits(expression,size,lp)) /*dummy call push expression past b*/
   !=   NULL ) /* found actual _a^b scripts, too */
     { delete_subraster(lp); /* but we don't need them */
       lp = NULL; } /* reset pointer, too */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 get \right delimiter and subexpression between \left...\right, xlate delims  get \right delimiter and subexpression between \left...\right, xlate delims
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
Line 5384  else {     /* have explicit matching \ri Line 6130  else {     /* have explicit matching \ri
 /* --- get subexpression between \left...\right --- */  /* --- get subexpression between \left...\right --- */
 if ( sublen < 1 ) goto end_of_job; /* nothing between delimiters */  if ( sublen < 1 ) goto end_of_job; /* nothing between delimiters */
 subexpr[sublen] = '\000'; /* and null-terminate it */  subexpr[sublen] = '\000'; /* and null-terminate it */
   /* --- adjust margin for expressions containing \middle's --- */
   if ( strtexchr(subexpr,"\\middle") != NULL ) /* have enclosed \middle's */
     margin = 1; /* so don't "overwhelm" them */
 /* --- check for operator delimiter --- */  /* --- check for operator delimiter --- */
 for ( idelim=0; opdelims[idelim]!=NULL; idelim++ )  for ( idelim=0; opdelims[idelim]!=NULL; idelim++ )
   if ( strstr(ldelim,opdelims[idelim]) != NULL ) /* found operator */    if ( strstr(ldelim,opdelims[idelim]) != NULL ) /* found operator */
Line 5391  for ( idelim=0; opdelims[idelim]!=NULL; Line 6140  for ( idelim=0; opdelims[idelim]!=NULL;
       if ( *ldelim == '\\' ) /* have leading escape */        if ( *ldelim == '\\' ) /* have leading escape */
  strcpy(ldelim,ldelim+1); /* squeeze it out */   strcpy(ldelim,ldelim+1); /* squeeze it out */
       break; } /* no need to check rest of table */        break; } /* no need to check rest of table */
 /* --- xlate delimiters --- */  /* --- xlate delimiters and check for textstyle --- */
 for ( idelim=1; idelim<=2; idelim++ ) { /* 1=left, 2=right */  for ( idelim=1; idelim<=2; idelim++ ) { /* 1=left, 2=right */
   char *lrdelim  = (idelim==1? ldelim:rdelim); /* ldelim or rdelim */    char *lrdelim  = (idelim==1? ldelim:rdelim); /* ldelim or rdelim */
   int ix;  char *xdelim; /* xfrom[] and xto[] index, delim */    int ix;  char *xdelim; /* xfrom[] and xto[] index, delim */
Line 5399  for ( idelim=1; idelim<=2; idelim++ ) { Line 6148  for ( idelim=1; idelim<=2; idelim++ ) {
     if ( strcmp(lrdelim,xdelim) == 0 ) /* found delim to xlate */      if ( strcmp(lrdelim,xdelim) == 0 ) /* found delim to xlate */
       { strcpy(lrdelim,xto[ix]); /* replace with corresponding xto[]*/        { strcpy(lrdelim,xto[ix]); /* replace with corresponding xto[]*/
  break; } /* no need to check further */   break; } /* no need to check further */
     for( ix=0; (xdelim=textdelims[ix]) != NULL; ix++ )
       if ( strstr(lrdelim,xdelim) != 0 ) /* found textstyle delim */
         { if ( idelim == 1 ) /* if it's the \left one */
     istextleft = 1; /* set left textstyle flag */
    else istextright = 1; /* else set right textstyle flag */
    break; } /* no need to check further */
   } /* --- end-of-for(idelim) --- */    } /* --- end-of-for(idelim) --- */
 /* --- debugging --- */  /* --- debugging --- */
 if ( msgfp!=NULL && msglevel>=99 )  if ( msgfp!=NULL && msglevel>=99 )
Line 5419  rasterize delimiters, reset baselines, a Line 6174  rasterize delimiters, reset baselines, a
 isleftdot  = (strchr(ldelim,'.')!=NULL); /* true if \left. */  isleftdot  = (strchr(ldelim,'.')!=NULL); /* true if \left. */
 isrightdot = (strchr(rdelim,'.')!=NULL); /* true if \right. */  isrightdot = (strchr(rdelim,'.')!=NULL); /* true if \right. */
 /* --- get rasters for best-fit delim characters, add sub/superscripts --- */  /* --- get rasters for best-fit delim characters, add sub/superscripts --- */
 isdisplaystyle = 9; /* force \displaystyle */  isdisplaystyle = (istextleft?0:9); /* force \displaystyle */
 if ( !isleftdot ) /* if not \left. */  if ( !isleftdot ) /* if not \left. */
  { /* --- first get requested \left delimiter --- */   { /* --- first get requested \left delimiter --- */
    lp = get_delim(ldelim,rheight,family); /* get \left delim char */     lp = get_delim(ldelim,rheight,family); /* get \left delim char */
Line 5431  if ( !isleftdot )   /* if not \left. */ Line 6186  if ( !isleftdot )   /* if not \left. */
  rheight = lheight-1; } /* make sure right delim matches */   rheight = lheight-1; } /* make sure right delim matches */
    /* --- then add on any sub/superscripts attached to \left( --- */     /* --- then add on any sub/superscripts attached to \left( --- */
    lp = rastlimits(&pleft,size,lp); } /*\left(_a^b and push pleft past b*/     lp = rastlimits(&pleft,size,lp); } /*\left(_a^b and push pleft past b*/
 isdisplaystyle = 9; /* force \displaystyle */  isdisplaystyle = (istextright?0:9); /* force \displaystyle */
 if ( !isrightdot ) /* and if not \right. */  if ( !isrightdot ) /* and if not \right. */
  { /* --- first get requested \right delimiter --- */   { /* --- first get requested \right delimiter --- */
    rp = get_delim(rdelim,rheight,family); /* get \right delim char */     rp = get_delim(rdelim,rheight,family); /* get \right delim char */
Line 5466  end_of_job: Line 6221  end_of_job:
   
   
 /* ==========================================================================  /* ==========================================================================
    * Function: rastright ( expression, size, basesp, ildelim, arg2, arg3 )
    * Purpose: ...\right handler, intercepts an unexpected/unbalanced \right
    * --------------------------------------------------------------------------
    * Arguments: expression (I) char **  to first char of null-terminated
    * string beginning with a \right
    * to be rasterized
    * size (I) int containing 0-5 default font size
    * basesp (I) subraster *  to character (or subexpression)
    * immediately preceding leading left{
    * (unused, but passed for consistency)
    * ildelim (I) int containing rdelims[] index of
    * right delimiter
    * arg2 (I) int unused
    * arg3 (I) int unused
    * --------------------------------------------------------------------------
    * Returns: ( subraster * ) ptr to subraster corresponding to subexpr,
    * or NULL for any parsing error
    * --------------------------------------------------------------------------
    * Notes:     o
    * ======================================================================= */
   /* --- entry point --- */
   subraster *rastright ( char **expression, int size, subraster *basesp,
    int ildelim, int arg2, int arg3 )
   {
   /* -------------------------------------------------------------------------
   Allocations and Declarations
   -------------------------------------------------------------------------- */
   subraster /* *rasterize(),*/ *sp=NULL; /*rasterize \right subexpr's*/
     if ( sp != NULL ) /* returning entire expression */
       {
         isreplaceleft = 1; /* set flag to replace left half*/
       }
   return ( sp );
   } /* --- end-of-function rastright() --- */
   
   
   /* ==========================================================================
    * Function: rastmiddle ( expression, size, basesp,  arg1, arg2, arg3 )
    * Purpose: \middle handler, returns subraster corresponding to
    * entire expression with \middle delimiter(s) sized to fit.
    * --------------------------------------------------------------------------
    * Arguments: expression (I/O) char **  to first char of null-terminated
    * string immediately following \middle to be
    * rasterized, and returning ptr immediately
    * to terminating null.
    * size (I) int containing 0-5 default font size
    * basesp (I) subraster *  to character (or subexpression)
    * immediately preceding \middle
    * (unused, but passed for consistency)
    * arg1 (I) int unused
    * arg2 (I) int unused
    * arg3 (I) int unused
    * --------------------------------------------------------------------------
    * Returns: ( subraster * ) ptr to subraster corresponding to expression,
    * or NULL for any parsing error
    * (expression ptr unchanged if error occurs)
    * --------------------------------------------------------------------------
    * Notes:     o
    * ======================================================================= */
   /* --- entry point --- */
   subraster *rastmiddle ( char **expression, int size, subraster *basesp,
    int arg1, int arg2, int arg3 )
   {
   /* -------------------------------------------------------------------------
   Allocations and Declarations
   -------------------------------------------------------------------------- */
   subraster *rasterize(), *sp=NULL, *subsp[32]; /*rasterize \middle subexpr's*/
   char *exprptr = *expression, /* local copy of ptr to expression */
    *texchar(), delim[32][132], /* delimiters following \middle's */
    *strtexchr(), /* locate \middle's */
    subexpr[8193], *subptr=NULL; /* subexpression between \middle's */
   int height=0, habove=0, hbelow=0; /* height, above & below baseline */
   int idelim, ndelims=0, /* \middle count (max 32) */
    family = CMSYEX; /* delims from CMSY10 or CMEX10 */
   subraster *subrastcpy(), /* copy subraster */
    *rastcat(), /* concatanate subraster */
    *get_delim(); /* get rasterized delimiter */
   int delete_subraster(); /* free work area subsp[]'s at eoj */
   /* -------------------------------------------------------------------------
   initialization
   -------------------------------------------------------------------------- */
   subsp[0] = leftexpression; /* expressn preceding 1st \middle */
   subsp[1] = NULL; /* set first null */
   /* -------------------------------------------------------------------------
   accumulate subrasters between consecutive \middle\delim...\middle\delim...'s
   -------------------------------------------------------------------------- */
   while ( ndelims < 30 ) /* max of 31 \middle's */
     {
     /* --- maintain max height above,below baseline --- */
     if ( subsp[ndelims] != NULL ) /*exprssn preceding current \middle*/
      { int baseline = (subsp[ndelims])->baseline;  /* #rows above baseline */
        height = ((subsp[ndelims])->image)->height; /* tot #rows (height) */
        habove = max2(habove,baseline); /* max #rows above baseline */
        hbelow = max2(hbelow,height-baseline); } /* max #rows below baseline */
     /* --- get delimter after \middle --- */
     skipwhite(exprptr); /*skip space betwn \middle & \delim*/
     exprptr = texchar(exprptr,delim[ndelims]); /* \delim after \middle */
     if ( *(delim[ndelims]) == '\000' ) /* \middle at end-of-expression */
       break; /* ignore it and consider job done */
     ndelims++; /* count another \middle\delim */
     /* --- get subexpression between \delim and next \middle --- */
     subsp[ndelims] = NULL; /* no subexpresion yet */
     if ( *exprptr == '\000' ) /* end-of-expression after \delim */
       break; /* so we have all subexpressions */
     if ( (subptr = strtexchr(exprptr,"\\middle")) /* find next \middle */
     ==   NULL ) /* no more \middle's */
      { strncpy(subexpr,exprptr,8192); /* get entire remaining expression */
        subexpr[8192] = '\000'; /* make sure it's null-terminated */
        exprptr += strlen(exprptr); } /* push exprptr to terminating '\0'*/
     else /* have another \middle */
      { int sublen = (int)(subptr-exprptr); /* #chars between \delim...\middle*/
        memcpy(subexpr,exprptr,min2(sublen,8192)); /* get subexpression */
        subexpr[min2(sublen,8192)] = '\000'; /* and null-terminate it */
        exprptr += (sublen+strlen("\\middle")); } /* push exprptr past \middle*/
     /* --- rasterize subexpression --- */
     subsp[ndelims] = rasterize(subexpr,size); /* rasterize subexpresion */
     } /* --- end-of-while(1) --- */
   /* -------------------------------------------------------------------------
   construct \middle\delim's and concatanate them between subexpressions
   -------------------------------------------------------------------------- */
   if ( ndelims < 1 /* no delims */
   ||   (height=habove+hbelow) < 1 ) /* or no subexpressions? */
     goto end_of_job; /* just flush \middle directive */
   for ( idelim=0; idelim<=ndelims; idelim++ )
     {
     /* --- first add on subexpression preceding delim --- */
     if ( subsp[idelim] != NULL ) /* have subexpr preceding delim */
       if ( sp == NULL ) /* this is first piece */
        { sp = subsp[idelim]; /* so just use it */
          if ( idelim == 0 ) sp = subrastcpy(sp); } /* or copy leftexpression */
       else sp = rastcat(sp,subsp[idelim],(idelim>0?3:1)); /* or concat it */
     /* --- now construct delimiter --- */
     if ( *(delim[idelim]) != '\000' ) /* have delimter */
      { subraster *delimsp = get_delim(delim[idelim],height,family);
        if ( delimsp != NULL ) /* rasterized delim */
         { delimsp->baseline = habove; /* set baseline */
    if ( sp == NULL ) /* this is first piece */
     sp = delimsp; /* so just use it */
    else sp = rastcat(sp,delimsp,3); } } /*or concat to existing pieces*/
     } /* --- end-of-for(idelim) --- */
   /* --- back to caller --- */
   end_of_job:
     if ( 0 ) /* now handled above */
       for ( idelim=1; idelim<=ndelims; idelim++ ) /* free subsp[]'s (not 0) */
        if ( subsp[idelim] != NULL ) /* have allocated subraster */
         delete_subraster(subsp[idelim]); /* so free it */
     if ( sp != NULL ) /* returning entire expression */
       { int newht = (sp->image)->height; /* height of returned subraster */
         sp->baseline = min2(newht-1,newht/2+5); /* guess new baseline */
         isreplaceleft = 1; /* set flag to replace left half*/
         *expression += strlen(*expression); } /* and push to terminating null*/
     return ( sp );
   } /* --- end-of-function rastmiddle() --- */
   
   
   /* ==========================================================================
  * Function: rastflags ( expression, size, basesp,  flag, value, arg3 )   * Function: rastflags ( expression, size, basesp,  flag, value, arg3 )
  * Purpose: sets an internal flag, e.g., for \rm, or sets an internal   * Purpose: sets an internal flag, e.g., for \rm, or sets an internal
  * value, e.g., for \unitlength=<value>, and returns NULL   * value, e.g., for \unitlength=<value>, and returns NULL
Line 5476  end_of_job: Line 6387  end_of_job:
  * size (I) int containing base font size (not used,   * size (I) int containing base font size (not used,
  * just stored in subraster)   * just stored in subraster)
  * basesp (I) subraster *  to character (or subexpression)   * basesp (I) subraster *  to character (or subexpression)
  * immediately preceding space, whose baseline   * immediately preceding "flags" directive
  * and height params are transferred to space   * (unused but passed for consistency)
  * flag (I) int containing #define'd symbol specifying   * flag (I) int containing #define'd symbol specifying
  * internal flag to be set   * internal flag to be set
  * value (I) int containing new value of flag   * value (I) int containing new value of flag
Line 5507  set flag or value Line 6418  set flag or value
 switch ( flag )  switch ( flag )
   {    {
   default: break; /* unrecognized flag */    default: break; /* unrecognized flag */
   case ISTEXT:    case ISFONTFAM:
     if ( isthischar((*(*expression)),WHITEMATH) ) /* \rm followed by white */      if ( isthischar((*(*expression)),WHITEMATH) ) /* \rm followed by white */
       (*expression)++; /* skip leading ~ after \rm */        (*expression)++; /* skip leading ~ after \rm */
     istext=value; /* set text mode */      fontnum = value; /* set font family */
     break;      break;
   case ISSTRING: isstring=value; break; /* set string/image mode */    case ISSTRING: isstring=value; break; /* set string/image mode */
   case ISDISPLAYSTYLE: /* set \displaystyle mode */    case ISDISPLAYSTYLE: /* set \displaystyle mode */
Line 5541  switch ( flag ) Line 6452  switch ( flag )
   case ISADJACENTWT: /* set lowpass adjacent weight */    case ISADJACENTWT: /* set lowpass adjacent weight */
   case ISCORNERWT: /* set lowpass corner weight */    case ISCORNERWT: /* set lowpass corner weight */
   case ISCOLOR: /* set red(1),green(2),blue(3) */    case ISCOLOR: /* set red(1),green(2),blue(3) */
   case ISSQUASH: /* set (minimum) "squash" margin */    case ISSMASH: /* set (minimum) "smash" margin */
     if ( value != NOVALUE ) /* passed a fixed value to be set */      if ( value != NOVALUE ) /* passed a fixed value to be set */
       argvalue = value; /* set given fixed value */        argvalue = value; /* set given fixed value */
     else /* get value from expression */      else /* get value from expression */
Line 5578  switch ( flag ) Line 6489  switch ( flag )
     fontsize = (isdelta? fontsize+argvalue : argvalue);      fontsize = (isdelta? fontsize+argvalue : argvalue);
     fontsize = max2(0,min2(fontsize,largestsize));      fontsize = max2(0,min2(fontsize,largestsize));
     shrinkfactor = shrinkfactors[fontsize];      shrinkfactor = shrinkfactors[fontsize];
     if ( isdisplaystyle == 1 ) /* displaystyle enabled but not set*/      if ( isdisplaystyle == 1 /* displaystyle enabled but not set*/
       ||  (1 && isdisplaystyle==2) /* displaystyle enabled and set */
       ||  (0 && isdisplaystyle==0) )/*\textstyle disabled displaystyle*/
      if ( displaystylelevel != recurlevel ) /*respect \displaystyle*/       if ( displaystylelevel != recurlevel ) /*respect \displaystyle*/
       if ( !ispreambledollars ) /* respect $$...$$'s */        if ( !ispreambledollars ) /* respect $$...$$'s */
        isdisplaystyle = (fontsize>=displaysize? 2:1); /* forced */         if ( fontsize >= displaysize )
    isdisplaystyle = 2; /* forced */
          else isdisplaystyle = 1;
     /*displaystylelevel = (-99);*/ } /* reset \displaystyle level */      /*displaystylelevel = (-99);*/ } /* reset \displaystyle level */
  else /* embed font size in expression */   else /* embed font size in expression */
   { sprintf(valuearg,"%d",fontsize); /* convert size */    { sprintf(valuearg,"%d",fontsize); /* convert size */
Line 5594  switch ( flag ) Line 6509  switch ( flag )
  if ( argvalue != NOVALUE ) /* got a value */   if ( argvalue != NOVALUE ) /* got a value */
     displaysize = (isdelta? displaysize+argvalue : argvalue);      displaysize = (isdelta? displaysize+argvalue : argvalue);
  break;   break;
       case ISSQUASH: /* set (minimum) "squash" margin */        case ISSMASH: /* set (minimum) "smash" margin */
  if ( argvalue != NOVALUE ) /* got a value */   if ( argvalue != NOVALUE ) /* got a value */
   { squashmargin = argvalue; /* set value */    { smashmargin = argvalue; /* set value */
     if ( arg3 != NOVALUE ) isdelta=arg3; /* hard-coded isdelta */      if ( arg3 != NOVALUE ) isdelta=arg3; /* hard-coded isdelta */
     issquashdelta = (isdelta?1:0); } /* and set delta flag */      issmashdelta = (isdelta?1:0); } /* and set delta flag */
  squashmargin = max2((isdelta?-5:0),min2(squashmargin,32)); /*sanity*/   smashmargin = max2((isdelta?-5:0),min2(smashmargin,32)); /*sanity*/
  break;   break;
       case ISSHRINK: /* set shrinkfactor */        case ISSHRINK: /* set shrinkfactor */
  if ( argvalue != NOVALUE ) /* got a value */   if ( argvalue != NOVALUE ) /* got a value */
Line 5701  int pixsz = 1;   /*default #bits per pix Line 6616  int pixsz = 1;   /*default #bits per pix
 char *texsubexpr(), widtharg[256]; /* parse for optional {width} */  char *texsubexpr(), widtharg[256]; /* parse for optional {width} */
 subraster *rasterize(), *rightsp=NULL; /*rasterize right half of expression*/  subraster *rasterize(), *rightsp=NULL; /*rasterize right half of expression*/
 subraster *rastcat(); /* cat rightsp after \hfill */  subraster *rastcat(); /* cat rightsp after \hfill */
 int blanksignal = (-991234); /*rastsquash signal right-hand blank*/  
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 initialization  initialization
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
Line 5767  return ( spacesp ); Line 6681  return ( spacesp );
  * string immediately following \\ to be   * string immediately following \\ to be
  * rasterized, and returning ptr immediately   * rasterized, and returning ptr immediately
  * to terminating null.   * to terminating null.
  * size (I) int containing 0-4 default font size   * size (I) int containing 0-5 default font size
  * basesp (I) subraster *  to character (or subexpression)   * basesp (I) subraster *  to character (or subexpression)
  * immediately preceding \accent   * immediately preceding \\
  * (unused, but passed for consistency)   * (unused, but passed for consistency)
  * arg1 (I) int unused   * arg1 (I) int unused
  * arg2 (I) int unused   * arg2 (I) int unused
Line 5812  rasterize right half of expression and s Line 6726  rasterize right half of expression and s
 if ( (rightsp=rasterize(*expression,size)) /* rasterize right half */  if ( (rightsp=rasterize(*expression,size)) /* rasterize right half */
 == NULL ) goto end_of_job; /* quit if failed */  == NULL ) goto end_of_job; /* quit if failed */
 /* --- stack left half above it --- */  /* --- stack left half above it --- */
 newlsp = rastack(rightsp,leftexpression,1,vspace,0,3); /*right under left*/  /*newlsp = rastack(rightsp,leftexpression,1,vspace,0,3);*//*right under left*/
   newlsp = rastack(rightsp,leftexpression,1,vspace,0,1); /*right under left*/
 /* --- back to caller --- */  /* --- back to caller --- */
 end_of_job:  end_of_job:
   if ( newlsp != NULL ) /* returning entire expression */    if ( newlsp != NULL ) /* returning entire expression */
Line 6166  int baseht=0, baseln=0;  /* height,basel Line 7081  int baseht=0, baseln=0;  /* height,basel
 /*int istweak = 1;*/ /*true to tweak baseline alignment*/  /*int istweak = 1;*/ /*true to tweak baseline alignment*/
 int rule_raster(), /* draw horizontal line for frac */  int rule_raster(), /* draw horizontal line for frac */
  lineheight = 1; /* thickness of fraction line */   lineheight = 1; /* thickness of fraction line */
 int vspace = 1; /*vertical space between components*/  int vspace = (size>2?2:1); /*vertical space between components*/
 int delete_subraster(); /*free work areas in case of error*/  int delete_subraster(); /*free work areas in case of error*/
 int type_raster(); /* display debugging output */  int type_raster(); /* display debugging output */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
Line 6588  subwidth  = (subsp->image)->width; /* an Line 7503  subwidth  = (subsp->image)->width; /* an
 pixsz     = (subsp->image)->pixsz; /* original pixsz remains constant */  pixsz     = (subsp->image)->pixsz; /* original pixsz remains constant */
 /* --- determine desired width, height of accent --- */  /* --- determine desired width, height of accent --- */
 accwidth = subwidth; /* same width as subexpr */  accwidth = subwidth; /* same width as subexpr */
 accheight = 3; /* default for bars */  accheight = 4; /* default for bars */
 switch ( accent )  switch ( accent )
   { default: break; /* default okay */    { default: break; /* default okay */
   case DOTACCENT: case DDOTACCENT:    case DOTACCENT: case DDOTACCENT:
     accheight = (size<3? 3:4); /* default for dots */      accheight = (size<4? 3:4); /* default for dots */
     break;      break;
   case HATACCENT: case VECACCENT:    case VECACCENT:
       vspace = 1; /* set 1-pixel vertical space */
     case HATACCENT:
     accheight = 7; /* default */      accheight = 7; /* default */
     if ( subwidth < 10 ) accheight = 5; /* unless small width */      if ( subwidth < 10 ) accheight = 5; /* unless small width */
       else if ( subwidth > 25 ) accheight = 9; /* or large */        else if ( subwidth > 25 ) accheight = 9; /* or large */
Line 6640  end_of_job: Line 7557  end_of_job:
   
   
 /* ==========================================================================  /* ==========================================================================
  * Function: rastfont (expression,size,basesp,font,arg2,arg3)   * Function: rastfont (expression,size,basesp,ifontnum,arg2,arg3)
  * Purpose: \cal{}, \scr{}, \etc handler, returns subraster corresponding   * Purpose: \cal{}, \scr{}, \etc handler, returns subraster corresponding
  * to char(s) within {}'s rendered at size   * to char(s) within {}'s rendered at size
  * --------------------------------------------------------------------------   * --------------------------------------------------------------------------
Line 6652  end_of_job: Line 7569  end_of_job:
  * basesp (I) subraster *  to character (or subexpression)   * basesp (I) subraster *  to character (or subexpression)
  * immediately preceding \accent   * immediately preceding \accent
  * (unused, but passed for consistency)   * (unused, but passed for consistency)
  * font (I) int containing 1 for \cal{}, 2 for \scr{}   * ifontnum (I) int containing 1 for \cal{}, 2 for \scr{}
  * arg2 (I) int unused   * arg2 (I) int unused
  * arg3 (I) int unused   * arg3 (I) int unused
  * --------------------------------------------------------------------------   * --------------------------------------------------------------------------
Line 6663  end_of_job: Line 7580  end_of_job:
  * ======================================================================= */   * ======================================================================= */
 /* --- entry point --- */  /* --- entry point --- */
 subraster *rastfont ( char **expression, int size, subraster *basesp,  subraster *rastfont ( char **expression, int size, subraster *basesp,
  int font, int arg2, int arg3 )   int ifontnum, int arg2, int arg3 )
 {  {
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 Allocations and Declarations  Allocations and Declarations
Line 6671  Allocations and Declarations Line 7588  Allocations and Declarations
 char *texsubexpr(), fontchars[8192], /*parse chars to be rendered in font*/  char *texsubexpr(), fontchars[8192], /*parse chars to be rendered in font*/
  subexpr[8192]; /* turn \cal{AB} into \calA\calB */   subexpr[8192]; /* turn \cal{AB} into \calA\calB */
 char *pfchars=fontchars, fchar='\0'; /* run thru fontchars one at a time*/  char *pfchars=fontchars, fchar='\0'; /* run thru fontchars one at a time*/
 char *name = NULL; /* fonts[font].name */  char *name = NULL; /* fontinfo[ifontnum].name */
 int class = 0, /* fonts[font].class */  int family = 0, /* fontinfo[ifontnum].family */
  istext = 0; /* set true for text type */   istext = 0, /* fontinfo[ifontnum].istext */
    class = 0; /* fontinfo[ifontnum].class */
 subraster *rasterize(), *fontsp=NULL, /* rasterize chars in font */  subraster *rasterize(), *fontsp=NULL, /* rasterize chars in font */
  *rastflags(); /* or just set flag to switch font */   *rastflags(); /* or just set flag to switch font */
 int oldsquashmargin = squashmargin; /* turn off squash in text mode */  int oldsmashmargin = smashmargin; /* turn off smash in text mode */
 int blanksignal = (-991234); /*rastsquash signal right-hand blank*/  #if 0
 /* --- fonts recognized by rastfont --- */  /* --- fonts recognized by rastfont --- */
 static int  nfonts = 5; /* legal font #'s are 1...nfonts */  static int  nfonts = 6; /* legal font #'s are 1...nfonts */
 static struct {char *name; int class;}  static struct {char *name; int class;}
   fonts[] =    fonts[] =
     { /* --- name  class 1=upper,2=alpha,3=alnum,4=lower,5=digit,9=all --- */      { /* --- name  class 1=upper,2=alpha,3=alnum,4=lower,5=digit,9=all --- */
  { "\\badfont", 0 },   { "\\math", 0 },
  { "\\cal", 1 }, /*(1) calligraphic, uppercase */   { "\\mathcal", 1 }, /*(1) calligraphic, uppercase */
  { "\\scr", 1 }, /*(2) rsfs/script, uppercase */   { "\\mathscr", 1 }, /*(2) rsfs/script, uppercase */
  { "\\rm", -1 }, /*(3) \rm,\text{abc} --> {\rm~abc} */   { "\\textrm", -1 }, /*(3) \rm,\text{abc} --> {\rm~abc} */
  { "\\it", -1 }, /*(4) \it,\textit{abc}-->{\it~abc} */   { "\\textit", -1 }, /*(4) \it,\textit{abc}-->{\it~abc} */
  { "\\bb", -1 }, /*(5) \bb,\mathbb{abc}-->{\bb~abc} */   { "\\mathbb", -1 }, /*(5) \bb,\mathbb{abc}-->{\bb~abc} */
    { "\\mathbf", -1 }, /*(6) \bf,\mathbf{abc}-->{\bf~abc} */
  { NULL, 0 }   { NULL, 0 }
     } ; /* --- end-of-fonts[] --- */      } ; /* --- end-of-fonts[] --- */
   #endif
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 first get font name and class to determine type of conversion desired  first get font name and class to determine type of conversion desired
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 if ( font<=0 || font>nfonts ) font=0; /* set error if out-of-bounds */  if (ifontnum<=0 || ifontnum>nfontinfo) ifontnum=0; /*math if out-of-bounds*/
 name  = fonts[font].name; /* font name */  name   = fontinfo[ifontnum].name; /* font name */
 class = fonts[font].class; /* font class */  family = fontinfo[ifontnum].family; /* font family */
 if ( font==3 || font==4 ) /* text (respect blanks) */  istext = fontinfo[ifontnum].istext; /*true in text mode (respect space)*/
  { istext = 1; /* signal text mode */  class  = fontinfo[ifontnum].class; /* font class */
    squashmargin = 0; } /* don't squash internal blanks */  if ( istext ) /* text (respect blanks) */
     smashmargin = 0; /* don't smash internal blanks */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 now convert \font{abc} --> {\font~abc}, or convert ABC to \calA\calB\calC  now convert \font{abc} --> {\font~abc}, or convert ABC to \calA\calB\calC
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 if ( class < 0 ) /* not character-by-character */  if ( 1 || class<0 ) /* not character-by-character */
  {    { 
  /* ---   /* ---
  if \font not immediately followed by { then it has no arg, so just set flag   if \font not immediately followed by { then it has no arg, so just set flag
Line 6711  if ( class < 0 )   /* not character-by-c Line 7632  if ( class < 0 )   /* not character-by-c
  if ( *(*expression) != '{' ) /* no \font arg, so just set flag */   if ( *(*expression) != '{' ) /* no \font arg, so just set flag */
     {      {
     if ( msgfp!=NULL && msglevel>=99 )      if ( msgfp!=NULL && msglevel>=99 )
       fprintf(msgfp,"rastfont> \\%s rastflags() for font#%d\n",name,font);       fprintf(msgfp,"rastfont> \\%s rastflags() for font#%d\n",name,ifontnum);
     switch ( font ) /* set flag by our internal font# */      fontsp = rastflags(expression,size,basesp,ISFONTFAM,ifontnum,arg3);
       { case 3: /* \rm, \text sets flag istext=1 */  
   fontsp = rastflags(expression,size,basesp,ISTEXT,1,arg3); break;  
  case 4: /* \it, \text sets flag istext=2 */  
   fontsp = rastflags(expression,size,basesp,ISTEXT,2,arg3); break;  
  case 5: /* \bb, \mathbb sets flag istext=3 */  
   fontsp = rastflags(expression,size,basesp,ISTEXT,3,arg3); break;  
  default: break; } /* unrecognized, set no flags */  
     goto end_of_job;      goto end_of_job;
     } /* --- end-of-if(*(*expression)!='{') --- */      } /* --- end-of-if(*(*expression)!='{') --- */
  /* ---   /* ---
Line 6752  else     /* character-by-character */ Line 7666  else     /* character-by-character */
  for ( pfchars=fontchars; (fchar= *pfchars)!='\000'; pfchars++ )   for ( pfchars=fontchars; (fchar= *pfchars)!='\000'; pfchars++ )
   {    {
   if ( isthischar(fchar,WHITEMATH) ) /* some whitespace */    if ( isthischar(fchar,WHITEMATH) ) /* some whitespace */
     { if ( 0 || istext ) /* and we're in a text mode */      { if ( 0 || istext ) /* and we're in a text mode font */
  strcat(subexpr,"\\;"); } /* so respect whitespace */   strcat(subexpr,"\\;"); } /* so respect whitespace */
   else /* char to be displayed in font */    else /* char to be displayed in font */
     { int exprlen = 0; /* #chars in subexpr before fchar */      { int exprlen = 0; /* #chars in subexpr before fchar */
       int isinclass = 0; /* set true if fchar in font class */        int isinclass = 0; /* set true if fchar in font class */
         /* --- class: 1=upper, 2=alpha, 3=alnum, 4=lower, 5=digit, 9=all --- */
       switch ( class ) /* check if fchar is in font class */        switch ( class ) /* check if fchar is in font class */
  { default: break; /* no chars in unrecognized class */   { default: break; /* no chars in unrecognized class */
   case 1: if ( isupper((int)fchar) ) isinclass=1; break;    case 1: if ( isupper((int)fchar) ) isinclass=1; break;
Line 6790  if ( (fontsp = rasterize(subexpr,size)) Line 7705  if ( (fontsp = rasterize(subexpr,size))
 back to caller with chars rendered in font  back to caller with chars rendered in font
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 end_of_job:  end_of_job:
   squashmargin = oldsquashmargin; /* restore squash */    smashmargin = oldsmashmargin; /* restore smash */
   if ( istext && fontsp!=NULL ) /* raster contains text */    if ( istext && fontsp!=NULL ) /* raster contains text mode font */
     fontsp->type = blanksignal; /* signal nosquash */      fontsp->type = blanksignal; /* signal nosmash */
   return ( fontsp ); /* chars rendered in font */    return ( fontsp ); /* chars rendered in font */
 } /* --- end-of-function rastfont() --- */  } /* --- end-of-function rastfont() --- */
   
Line 7328  while ( 1 )    /* scan chars till end */ Line 8243  while ( 1 )    /* scan chars till end */
  { skipwhite(tokptr); /* flush whitespace after \hline */   { skipwhite(tokptr); /* flush whitespace after \hline */
   if ( *tokptr == '\000' /* end-of-expression after \hline */    if ( *tokptr == '\000' /* end-of-expression after \hline */
   ||   isthischar(*tokptr,coldelim) ) /* or unescaped coldelim */    ||   isthischar(*tokptr,coldelim) ) /* or unescaped coldelim */
     istokwhite = ishonly = 1; /* so token contains \hline only */      { istokwhite = 1; /* so token contains \hline only */
         if ( iseox ) ishonly = 1; } /* ignore entire row at eox */
   else /* token contains more than \hline */    else /* token contains more than \hline */
     strcpy(token,tokptr); } /* so flush \hline from token */      strcpy(token,tokptr); } /* so flush \hline from token */
       } /* --- end-of-if(ncols[nrows]==0) --- */        } /* --- end-of-if(ncols[nrows]==0) --- */
Line 7452  for ( irow=0; irow<=nrows; irow++ ) /*to Line 8368  for ( irow=0; irow<=nrows; irow++ ) /*to
   for ( icol=0; icol<ncols[irow]; icol++ ) /* go through cells in this row */    for ( icol=0; icol<ncols[irow]; icol++ ) /* go through cells in this row */
     {      {
     subraster *tsp = toksp[itoken]; /* token that belongs in this cell */      subraster *tsp = toksp[itoken]; /* token that belongs in this cell */
       /* --- first adjust leftcol for vline to left of icol, if present ---- */
       leftcol += vlinespace(icol); /* space for vline to left of col */
       /* --- now rasterize cell ---- */
     if ( tsp != NULL ) /* have a rasterized cell token */      if ( tsp != NULL ) /* have a rasterized cell token */
       {        {
       /* --- local parameters --- */        /* --- local parameters --- */
Line 7461  for ( irow=0; irow<=nrows; irow++ ) /*to Line 8380  for ( irow=0; irow<=nrows; irow++ ) /*to
   tokencol = 0, /*H offset (init for left justify)*/    tokencol = 0, /*H offset (init for left justify)*/
   tokenrow = baseline - tsp->baseline;/*V offset (init for baseline)*/    tokenrow = baseline - tsp->baseline;/*V offset (init for baseline)*/
       /* --- adjust leftcol for vline to left of icol, if present ---- */        /* --- adjust leftcol for vline to left of icol, if present ---- */
       leftcol += vlinespace(icol); /* space for vline to left of col */        /*leftcol += vlinespace(icol);*/ /* space for vline to left of col */
       /* --- reset justification (if not left-justified) --- */        /* --- reset justification (if not left-justified) --- */
       if ( justify[icol] == 0 ) /* but user wants it centered */        if ( justify[icol] == 0 ) /* but user wants it centered */
   tokencol = (cwidth-twidth+1)/2; /* so split margin left/right */    tokencol = (cwidth-twidth+1)/2; /* so split margin left/right */
Line 7761  char *texsubexpr(),linexpr[257], *xptr=l Line 8680  char *texsubexpr(),linexpr[257], *xptr=l
 subraster *new_subraster(), *linesp=NULL; /* subraster for line */  subraster *new_subraster(), *linesp=NULL; /* subraster for line */
 /*char *origexpression = *expression;*/ /*original expression after \line*/  /*char *origexpression = *expression;*/ /*original expression after \line*/
 int pixsz = 1; /* pixels are one bit each */  int pixsz = 1; /* pixels are one bit each */
   int thickness = 1; /* line thickness */
 double strtod(), /* convert ascii params to doubles */  double strtod(), /* convert ascii params to doubles */
  xinc=0.0, yinc=0.0, /* x,y-increments for line, */   xinc=0.0, yinc=0.0, /* x,y-increments for line, */
  xlen=0.0, ylen=0.0; /* x,y lengths for line */   xlen=0.0, ylen=0.0; /* x,y lengths for line */
 int width=0,  height=0; /* #pixels width,height of line */  int width=0,  height=0, /* #pixels width,height of line */
    rwidth=0, rheight=0; /*alloc width,height plus thickness*/
 int istop=0,  isright=0, /* origin at bot-left if x,yinc>=0 */  int istop=0,  isright=0, /* origin at bot-left if x,yinc>=0 */
  origin = 0; /* x,yinc: ++=00 +-=01 -+=10 --=11 */   origin = 0; /* x,yinc: ++=00 +-=01 -+=10 --=11 */
 int line_raster(); /* draw line in linesp->image */  int line_raster(); /* draw line in linesp->image */
Line 7774  obtain (xinc,yinc) arguments immediately Line 8695  obtain (xinc,yinc) arguments immediately
 /* --- parse for (xinc,yinc) arguments, and bump expression past it --- */  /* --- parse for (xinc,yinc) arguments, and bump expression past it --- */
 *expression = texsubexpr(*expression,linexpr,253,"(",")",0,0);  *expression = texsubexpr(*expression,linexpr,253,"(",")",0,0);
 if ( *linexpr == '\000' ) goto end_of_job; /* couldn't get (xinc,yinc) */  if ( *linexpr == '\000' ) goto end_of_job; /* couldn't get (xinc,yinc) */
 /* --- now interpret xinc,yinc returned in linexpr --- */  /* --- now interpret xinc,yinc;thickness returned in linexpr --- */
   if ( (xptr=strchr(linexpr,';')) != NULL ) /* look for ';' after xinc,yinc */
     { *xptr = '\000'; /* terminate linexpr at ; */
       thickness = (int)strtol(xptr+1,NULL,10); } /* get int thickness */
 if ( (xptr=strchr(linexpr,',')) != NULL ) /* look for ',' in xinc,yinc */  if ( (xptr=strchr(linexpr,',')) != NULL ) /* look for ',' in xinc,yinc */
   *xptr = '\000'; /* found it, so replace ',' by '\0'*/    *xptr = '\000'; /* found it, so replace ',' by '\0'*/
 if ( *linexpr != '\000' ) /* check against missing 1st arg */  if ( *linexpr != '\000' ) /* check against missing 1st arg */
Line 7803  calculate width,height, etc, based on xl Line 8727  calculate width,height, etc, based on xl
 xlen = absval(xlen); /* force xlen positive */  xlen = absval(xlen); /* force xlen positive */
 ylen = absval(ylen); /* force ylen positive */  ylen = absval(ylen); /* force ylen positive */
 /* --- calculate corresponding lengths in pixels --- */  /* --- calculate corresponding lengths in pixels --- */
 width  = max2(1,iround(unitlength*xlen)); /*scale by unitlength and round,*/  width   = max2(1,iround(unitlength*xlen)); /*scale by unitlength and round,*/
 height = max2(1,iround(unitlength*ylen)); /* and must be at least 1 pixel */  height  = max2(1,iround(unitlength*ylen)); /* and must be at least 1 pixel */
   rwidth  = width  + (ylen<0.001?0:max2(0,thickness-1));
   rheight = height + (xlen<0.001?0:max2(0,thickness-1));
 /* --- set origin corner, x,yinc's: ++=0=(0,0) +-=1=(0,1) -+=10=(1,0) --- */  /* --- set origin corner, x,yinc's: ++=0=(0,0) +-=1=(0,1) -+=10=(1,0) --- */
 if ( xinc < 0.0 ) isright = 1; /*negative xinc, so corner is (1,?)*/  if ( xinc < 0.0 ) isright = 1; /*negative xinc, so corner is (1,?)*/
 if ( yinc < 0.0 ) istop = 1; /*negative yinc, so corner is (?,1)*/  if ( yinc < 0.0 ) istop = 1; /*negative yinc, so corner is (?,1)*/
Line 7815  if ( msgfp!=NULL && msglevel>=29 ) /* de Line 8741  if ( msgfp!=NULL && msglevel>=29 ) /* de
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 allocate subraster and raster for complete picture  allocate subraster and raster for complete picture
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 /* --- sanity check on width,height args --- */  /* --- sanity check on width,height,thickness args --- */
 if ( width < 1 ||  width > 600  if ( width < 1 ||  width > 600
 ||  height < 1 || height > 600 ) goto end_of_job;  ||  height < 1 || height > 600
   ||  thickness<1||thickness>25 ) goto end_of_job;
 /* --- allocate and initialize subraster for constructed line --- */  /* --- allocate and initialize subraster for constructed line --- */
 if ( (linesp=new_subraster(width,height,pixsz)) /* allocate new subraster */  if ( (linesp=new_subraster(rwidth,rheight,pixsz)) /* alloc new subraster */
 ==   NULL )  goto end_of_job; /* quit if failed */  ==   NULL )  goto end_of_job; /* quit if failed */
 /* --- initialize line subraster parameters --- */  /* --- initialize line subraster parameters --- */
 linesp->type = IMAGERASTER; /* image */  linesp->type = IMAGERASTER; /* image */
 linesp->symdef = NULL; /* not applicable for image */  linesp->symdef = NULL; /* not applicable for image */
 linesp->baseline = height/2 + 2; /* is a little above center good? */  linesp->baseline = height/2 + 2 /* is a little above center good? */
    + (rheight-height)/2; /* account for line thickness too */
 linesp->size = size; /* size (probably unneeded) */  linesp->size = size; /* size (probably unneeded) */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 draw the line  draw the line
Line 7834  line_raster ( linesp->image,  /* embedde Line 8762  line_raster ( linesp->image,  /* embedde
  (isright?  width-1 : 0), /* col0, from left or right */   (isright?  width-1 : 0), /* col0, from left or right */
  (istop?   height-1 : 0), /* row1, to top or bottom */   (istop?   height-1 : 0), /* row1, to top or bottom */
  (isright? 0 :  width-1), /* col1, to right or left */   (isright? 0 :  width-1), /* col1, to right or left */
  1 ); /* line thickness is 1 pixel */   thickness ); /* line thickness (usually 1 pixel)*/
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 return constructed line to caller  return constructed line to caller
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
Line 8381  char *texsubexpr(), tag[512]="\000", fil Line 9309  char *texsubexpr(), tag[512]="\000", fil
 subraster *rasterize(), *inputsp=NULL; /* rasterized input image */  subraster *rasterize(), *inputsp=NULL; /* rasterized input image */
 int status, rastreadfile(); /* read input file */  int status, rastreadfile(); /* read input file */
 int format=0, npts=0; /* don't reformat (numerical) input */  int format=0, npts=0; /* don't reformat (numerical) input */
 char subexpr[8192], /* concatanated lines from input file */  char subexpr[8192] = "\000", /* concatanated lines from input file */
  *mimeprep(), /* preprocess inputted data */   *mimeprep(), /* preprocess inputted data */
  *dtoa(), *reformat=NULL; /* reformat numerical input */   *dbltoa(), *reformat=NULL; /* reformat numerical input */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 obtain [tag]{filename} argument  obtain [tag]{filename} argument
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
Line 8391  obtain [tag]{filename} argument Line 9319  obtain [tag]{filename} argument
 if ( *(*expression) == '[' ) /* check for []-enclosed value */  if ( *(*expression) == '[' ) /* check for []-enclosed value */
   { char argfld[2048]; /* optional argument field */    { char argfld[2048]; /* optional argument field */
     *expression = texsubexpr(*expression,argfld,2047,"[","]",0,0);      *expression = texsubexpr(*expression,argfld,2047,"[","]",0,0);
     if ( (reformat=strstr(argfld,"dtoa")) != NULL ) /* dtoa requested */      if ( (reformat=strstr(argfld,"dtoa")) != NULL ) /*dtoa/dbltoa requested*/
       { format = 1; /* signal dtoa() format */        { format = 1; /* signal dtoa()/dbltoa() format */
  if ( (reformat=strchr(reformat,'=')) != NULL ) /* have dtoa= */   if ( (reformat=strchr(reformat,'=')) != NULL ) /* have dtoa= */
   npts = (int)strtol(reformat+1,NULL,0); } /* so set npts */    npts = (int)strtol(reformat+1,NULL,0); } /* so set npts */
     if ( format == 0 ) /* reformat not requested */      if ( format == 0 ) /* reformat not requested */
Line 8409  if ( *filename != '\000'  /* got filenam Line 9337  if ( *filename != '\000'  /* got filenam
 /* --------------------------------------------------------------------------  /* --------------------------------------------------------------------------
 Read file and rasterize constructed subexpression  Read file and rasterize constructed subexpression
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 status = rastreadfile(filename,tag,subexpr); /* read file */  status = rastreadfile(filename,0,tag,subexpr); /* read file */
 if ( *subexpr == '\000' ) goto end_of_job;   /* quit if problem */  if ( *subexpr == '\000' ) goto end_of_job;   /* quit if problem */
 /* --- rasterize input subexpression  --- */  /* --- rasterize input subexpression  --- */
 mimeprep(subexpr); /* preprocess subexpression */  mimeprep(subexpr); /* preprocess subexpression */
 if ( format == 1 ) /* dtoa() */  if ( format == 1 ) /* dtoa()/dbltoa() */
  { double d = strtod(subexpr,NULL); /* interpret subexpr as double */   { double d = strtod(subexpr,NULL); /* interpret subexpr as double */
    if ( d != 0.0 ) /* conversion to double successful */     if ( d != 0.0 ) /* conversion to double successful */
     if ( (reformat=dtoa(d,npts)) != NULL ) /* reformat successful */      if ( (reformat=dbltoa(d,npts)) != NULL ) /* reformat successful */
      strcpy(subexpr,reformat); } /*replace subexpr with reformatted*/       strcpy(subexpr,reformat); } /*replace subexpr with reformatted*/
 inputsp = rasterize(subexpr,size); /* rasterize subexpression */  inputsp = rasterize(subexpr,size); /* rasterize subexpression */
 /* --- return input image to caller --- */  /* --- return input image to caller --- */
Line 8447  end_of_job: Line 9375  end_of_job:
  * requested, or NULL for any parsing error   * requested, or NULL for any parsing error
  * --------------------------------------------------------------------------   * --------------------------------------------------------------------------
  * Notes:     o Summary of syntax...   * Notes:     o Summary of syntax...
  *  \counter[value][logfile]{filename}   *  \counter[value][logfile]{filename:tag}
  *      o   *      o :tag is optional
  * ======================================================================= */   * ======================================================================= */
 /* --- entry point --- */  /* --- entry point --- */
 subraster *rastcounter ( char **expression, int size, subraster *basesp,  subraster *rastcounter ( char **expression, int size, subraster *basesp,
Line 8458  subraster *rastcounter ( char **expressi Line 9386  subraster *rastcounter ( char **expressi
 Allocations and Declarations  Allocations and Declarations
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 char *texsubexpr(), filename[1024]="\000", /* counter file */  char *texsubexpr(), filename[1024]="\000", /* counter file */
  logfile[1024]="\000", tag[512]="\000"; /* log file and tag */   logfile[1024]="\000", tag[512]="\000"; /*optional log file and tag*/
 subraster *rasterize(), *countersp=NULL; /* rasterized counter image */  subraster *rasterize(), *countersp=NULL; /* rasterized counter image */
 FILE /* *fp=NULL,*/ *logfp=NULL; /* counter and log file pointers */  FILE /* *fp=NULL,*/ *logfp=NULL; /* counter and log file pointers */
 int rastreadfile(), rastwritefile(); /* to read and write counter file */  int status=0,rastreadfile(),rastwritefile(), /*read,write counter file*/
 char text[2048] = "1_", /* first (and only) line in counter file */   isstrict = 1; /* true to only write to existing files */
   char text[8192] = "1_", /* only line in counter file without tags */
  *delim = NULL, /* delimiter in text */   *delim = NULL, /* delimiter in text */
  utext[32] = "1_", /* default delimiter */   utext[128] = "1_", /* default delimiter */
  *udelim = utext+1; /* underscore delimiter */   *udelim = utext+1; /* underscore delimiter */
 char *timestamp(), /* timestamp for logging */  char *rasteditfilename(), /* edit log file name */
  *dtoa(); /* double to comma-separated */   *timestamp(), /* timestamp for logging */
    *dbltoa(); /* double to comma-separated ascii */
 int counter = 1, /* atoi(text) (after _ removed, if present) */  int counter = 1, /* atoi(text) (after _ removed, if present) */
  gotcount = 0, /* set true once counter value determined */   value = 1, /* optional [value] argument */
    gotvalue = 0, /* set true if [value] supplied */
    isdelta = 0, /* set true if [+value] or [-value] is delta*/
  ordindex = (-1); /* ordinal[] index to append ordinal suffix */   ordindex = (-1); /* ordinal[] index to append ordinal suffix */
 /*--- ordinal suffixes based on units digit of counter ---*/  /*--- ordinal suffixes based on units digit of counter ---*/
 static char *ordinal[]={"th","st","nd","rd","th","th","th","th","th","th"};  static char *ordinal[]={"th","st","nd","rd","th","th","th","th","th","th"};
Line 8480  first obtain optional [value][logfile] a Line 9412  first obtain optional [value][logfile] a
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 /* --- first check for optional \counter[value] --- */  /* --- first check for optional \counter[value] --- */
 if ( *(*expression) == '[' ) /* check for []-enclosed value */  if ( *(*expression) == '[' ) /* check for []-enclosed value */
   { *expression = texsubexpr(*expression,text,2047,"[","]",0,0);    { *expression = texsubexpr(*expression,text,1023,"[","]",0,0);
     if ( *text != '\000' ) /* got counter value */      if ( *text != '\000' ) /* got counter value (or logfile) */
      if ( strlen(text) >= 1 ) /* and it's not an empty string */       if ( strlen(text) >= 1 ) /* and it's not an empty string */
       if ( isdigit((int)(*text)) ) /* leading 0-9 digit signals value */        if ( isthischar(*text,"+-0123456789") ) /* check for leading +-digit */
        { counter = (int)(strtod(text,&udelim)+0.1); /* value and delim */   gotvalue = 1; /* signal we got optional value */
  gotcount = 1; } /* signal we got counter value */        else /* not +-digit, so must be logfile */
       else /* not a digit, so must be logfile */   strcpy(logfile,text); /* so just copy it */
  strcpy(logfile,text); /* so just copy it */  
   } /* --- end-of-if(**expression=='[') --- */    } /* --- end-of-if(**expression=='[') --- */
 /* --- next check for optional \counter[][logfile] --- */  /* --- next check for optional \counter[][logfile] --- */
 if ( *(*expression) == '[' ) /* check for []-enclosed logfile */  if ( *(*expression) == '[' ) /* check for []-enclosed logfile */
   { *expression = texsubexpr(*expression,filename,1023,"[","]",0,0);    { *expression = texsubexpr(*expression,filename,1023,"[","]",0,0);
     if ( *text != '\000' ) /* got logfile value */      if ( *filename != '\000' ) /* got logfile (or counter value) */
      if ( strlen(filename) >= 1 ) /* and it's not an empty string */       if ( strlen(filename) >= 1 ) /* and it's not an empty string */
       if ( !(isdigit((int)(*filename))) /*leading non-digit signals logfile*/        if ( !(isthischar(*text,"+-0123456789")) /* not a leading +-digit */
       ||   gotcount ) /* or we already got counter value */        ||   gotvalue ) /* or we already got counter value */
  strcpy(logfile,filename); /* so just copy it */   strcpy(logfile,filename); /* so just copy it */
       else /* 0-9 digit, so must be value */        else /* leading +-digit must be value */
        { strcpy(text,filename); /* copy value to text line */   { strcpy(text,filename); /* copy value to text line */
  counter = (int)(strtod(text,&udelim)+0.1); /* value and delim */    gotvalue = 1; } /* and signal we got optional value*/
  gotcount = 1; } /* signal we got counter value */  
   } /* --- end-of-if(**expression=='[') --- */    } /* --- end-of-if(**expression=='[') --- */
   /* --- evaluate [value] if present --- */
   if ( gotvalue ) { /*leading +-digit should be in text*/
    if ( *text == '+' ) isdelta = (+1); /* signal adding */
    if ( *text == '-' ) isdelta = (-1); /* signal subtracting */
    value = (int)(strtod((isdelta==0?text:text+1),&udelim)+0.1); /*abs(value)*/
    if ( isdelta == (-1) ) value = (-value); /* set negative value if needed */
    counter = value; /* re-init counter */
    } /* --- end-of-if(gotvalue) --- */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 obtain counter {filename} argument  obtain counter {filename} argument
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
Line 8519  Read and parse file, increment and rewri Line 9457  Read and parse file, increment and rewri
 if ( strlen(filename) > 1 ) /* make sure we got {filename} arg */  if ( strlen(filename) > 1 ) /* make sure we got {filename} arg */
   {    {
   /* --- read and interpret first (and only) line from counter file --- */    /* --- read and interpret first (and only) line from counter file --- */
   if ( !gotcount ) /* if no [count] argument supplied */    if ( !gotvalue || (isdelta!=0) ) /*if no [count] arg or if delta arg*/
    if ( rastreadfile(filename,tag,text) != 0 ) /* try reading it from file */     if ( (status=rastreadfile(filename,1,tag,text)) > 0 ) /*try reading file*/
     { counter= 1 + (int)(strtod(text,&udelim)+0.1); /*counter val and delim*/      { char *vdelim = NULL; /* underscore delim from file */
       gotcount = 1; } /* signal we got counter value */        double fileval  = strtod(text,&vdelim); /* value and delim from file */
         counter = (int)(fileval<0.0?fileval-0.1:fileval+0.1); /* integerized */
         counter += value; /* bump count by 1 or add/sub delta*/
         if ( !gotvalue ) udelim=vdelim; } /* default to file's current delim */
   /* --- check for ordinal suffix --- */    /* --- check for ordinal suffix --- */
   if ( udelim != (char *)NULL ) /* have some delim after value */    if ( udelim != (char *)NULL ) /* have some delim after value */
    if ( *udelim == '_' ) /* underscore signals ordinal */     if ( *udelim == '_' ) /* underscore signals ordinal */
     { ordindex = counter%10; /* least significant digit */      { int abscount = (counter>=0?counter:(-counter)); /* abs(counter) */
       if ( counter >= 10 ) /* counter is 10 or greater */        ordindex = abscount%10; /* least significant digit */
        if ( (counter/10)%10 == 1 ) /* and the last two are 10-19 */        if ( abscount >= 10 ) /* counter is 10 or greater */
          if ( (abscount/10)%10 == 1 ) /* and the last two are 10-19 */
  ordindex = 0; } /* use th for 11,12,13 rather than st,nd,rd */   ordindex = 0; } /* use th for 11,12,13 rather than st,nd,rd */
   /* --- rewrite counter file --- */    /* --- rewrite counter file --- */
   sprintf(text,"%d",counter); /*build image of incremented counter*/    if ( status >= 0 ) /* file was read okay */
   if ( ordindex >= 0 ) strcat(text,"_"); /* tack on _ */     { sprintf(text,"%d",counter); /*build image of incremented counter*/
   if ( *tag == '\000' ) strcat(text,"\n"); /* and newline */       if ( ordindex >= 0 ) strcat(text,"_"); /* tack on _ */
   rastwritefile(filename,tag,text,1); /* rewrite incremented counter */       if ( *tag == '\000' ) strcat(text,"\n"); /* and newline */
        status = rastwritefile(filename,tag,text,isstrict); } /*rewrite counter*/
   } /* --- end-of-if(strlen(filename)>1) --- */    } /* --- end-of-if(strlen(filename)>1) --- */
 /* --------------------------------------------------------------------------  /* --------------------------------------------------------------------------
 log counter request  log counter request
Line 8543  if ( strlen(logfile) > 1 )  /* optional Line 9486  if ( strlen(logfile) > 1 )  /* optional
  {   {
  char comment[1024] = "\000", /* embedded comment, logfile:comment*/   char comment[1024] = "\000", /* embedded comment, logfile:comment*/
  *commptr = strchr(logfile,':'); /* check for : signalling comment */   *commptr = strchr(logfile,':'); /* check for : signalling comment */
    int islogokay = 1; /* logfile must exist if isstrict */
  if ( commptr != NULL ) /* have embedded comment */   if ( commptr != NULL ) /* have embedded comment */
   { strcpy(comment,commptr+1); /* comment follows : */    { strcpy(comment,commptr+1); /* comment follows : */
     *commptr = '\000'; } /* null-terminate actual logfile */      *commptr = '\000'; } /* null-terminate actual logfile */
  if ( (logfp = fopen(logfile,"a")) /* open logfile */   strcpy(logfile,rasteditfilename(logfile)); /* edit log file name */
  != (FILE *)NULL ) /* opened successfully */   if ( *logfile == '\000' ) islogokay = 0; /* given an invalid file name */
   {   else if ( isstrict ) { /*okay, but only write if it exists*/
   int ilog=0; /* logvars[] index */    if ( (logfp=fopen(logfile,"r")) == (FILE *)NULL ) /*doesn't already exist*/
   fprintf(logfp,"%s  ",timestamp()); /* first emit timestamp */      islogokay = 0; /* so don't write log file */
   if (*tag=='\000') fprintf(logfp,"%s",filename); /* emit counter filename */    else fclose(logfp); } /* close file opened for test read */
   else fprintf(logfp,"<%s>",tag); /* or tag if we have one */   if ( islogokay ) /* okay to write logfile */
   fprintf(logfp,"=%d",counter); /* emit counter value */    if ( (logfp = fopen(logfile,"a")) /* open logfile */
   for ( ilog=0; logvars[ilog] != NULL; ilog++ ) /* log till end-of-table */    != (FILE *)NULL ) { /* opened successfully for append */
    if ( ilog == commentvar /* replace with comment... */     int ilog=0; /* logvars[] index */
    &&   commptr != NULL ) /* ...if available */       fprintf(logfp,"%s  ",timestamp(TZDELTA,0)); /* first emit timestamp */
     fprintf(logfp,"  %.256s",comment); /* log embedded comment */     if (*tag=='\000') fprintf(logfp,"%s",filename); /* emit counter filename */
    else     else fprintf(logfp,"<%s>",tag); /* or tag if we have one */
     { char *logval = getenv(logvars[ilog]); /* getenv(variable) to be logged*/     fprintf(logfp,"=%d",counter); /* emit counter value */
       fprintf(logfp,"  %.64s", /* log variable */     if ( status < 1 ) /* read or re-write failed */
       fprintf(logfp,"(%s %d)","error status",status); /* emit error */
      for ( ilog=0; logvars[ilog] != NULL; ilog++ ) /* log till end-of-table */
       if ( ilog == commentvar /* replace with comment... */
       &&   commptr != NULL ) /* ...if available */  
        fprintf(logfp,"  %.256s",comment); /* log embedded comment */
       else
        { char *logval = getenv(logvars[ilog]); /*getenv(variable) to be logged*/
          fprintf(logfp,"  %.64s", /* log variable */
  (logval!=NULL?logval:"<unknown>")); } /* emit value or <unknown> */   (logval!=NULL?logval:"<unknown>")); } /* emit value or <unknown> */
   fprintf(logfp,"\n"); /* terminating newline */     fprintf(logfp,"\n"); /* terminating newline */
   fclose(logfp); /* close logfile */     fclose(logfp); /* close logfile */
   } /* --- end-of-if(logfp!=NULL) --- */     } /* --- end-of-if(islogokay&&logfp!=NULL) --- */
  } /* --- end-of-if(strlen(logfile)>1) --- */   } /* --- end-of-if(strlen(logfile)>1) --- */
 /* --------------------------------------------------------------------------  /* --------------------------------------------------------------------------
 construct counter expression and rasterize it  construct counter expression and rasterize it
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 /* --- construct expression --- */  /* --- construct expression --- */
 /*sprintf(text,"%d",counter);*/ /* start with counter */  /*sprintf(text,"%d",counter);*/ /* start with counter */
 strcpy(text,dtoa(((double)counter),0)); /* comma-separated counter value */  strcpy(text,dbltoa(((double)counter),0)); /* comma-separated counter value */
 if ( ordindex >= 0 ) /* need to tack on ordinal suffix */  if ( ordindex >= 0 ) /* need to tack on ordinal suffix */
   { strcat(text,"^{\\underline{\\rm~"); /* start with ^ and {\underline{\rm */    { strcat(text,"^{\\underline{\\rm~"); /* start with ^ and {\underline{\rm */
     strcat(text,ordinal[ordindex]); /* then st,nd,rd, or th */      strcat(text,ordinal[ordindex]); /* then st,nd,rd, or th */
Line 8585  countersp = rasterize(text,size); /* ras Line 9537  countersp = rasterize(text,size); /* ras
   
   
 /* ==========================================================================  /* ==========================================================================
    * Function: rasttoday ( expression, size, basesp, arg1, arg2, arg3 )
    * Purpose: handle \today
    * --------------------------------------------------------------------------
    * Arguments: expression (I/O) char **  to first char of null-terminated
    * string immediately following \today,
    * and returning ptr immediately
    * following last character processed.
    * size (I) int containing 0-5 default font size
    * basesp (I) subraster *  to character (or subexpression)
    * immediately preceding \today
    * (unused, but passed for consistency)
    * arg1 (I) int unused
    * arg2 (I) int unused
    * arg3 (I) int unused
    * --------------------------------------------------------------------------
    * Returns: ( subraster * ) subraster ptr to date stamp
    * --------------------------------------------------------------------------
    * Notes:     o
    * ======================================================================= */
   /* --- entry point --- */
   subraster *rasttoday ( char **expression, int size, subraster *basesp,
    int arg1, int arg2, int arg3 )
   {
   /* -------------------------------------------------------------------------
   Allocations and Declarations
   -------------------------------------------------------------------------- */
   char *texsubexpr(), optarg[2050]; /* optional [+/-tzdelta,ifmt] args */
   char *timestamp(), *today=optarg; /* timestamp to be rasterized */
   subraster *rasterize(), *todaysp=NULL; /* rasterize timestamp */
   int ifmt=1, tzdelta=0; /* default timestamp() args */
   /* -------------------------------------------------------------------------
   Get optional args \today[+/-tzdelta,ifmt]
   -------------------------------------------------------------------------- */
   /* --- check for optional \today[+/-tzdelta,ifmt] --- */
   if ( *(*expression) == '[' ) /* check for []-enclosed value */
     { *expression = texsubexpr(*expression,optarg,2047,"[","]",0,0);
       if ( *optarg != '\000' ) /* got optional arg */
        { char *comma = strchr(optarg,','); /* comma between +/-tzdelta,ifmt */
          int iarg, nargs=(comma==NULL?1:2); /* #optional args between []'s */
          if ( comma != NULL ) *comma = '\000'; /* null-terminate first arg */
          for ( iarg=1; iarg<=nargs; iarg++ ) /* process one or both args */
    { char *arg = (iarg==1?optarg:comma+1); /* choose 1st or 2nd arg */
     if ( isthischar(*arg,"+-") ) /* leading +/- signals tzdelta */
       tzdelta = atoi(arg); /* so interpret arg as tzdelta */
     else ifmt = atoi(arg); } /* else interpret args as ifmt */
        } /* --- end-of-if(*optarg!='\0') --- */
     } /* --- end-of-if(**expression=='[') --- */
   /* -------------------------------------------------------------------------
   Get timestamp and rasterize it
   -------------------------------------------------------------------------- */
   strcpy(today,"\\text{"); /* rasterize timestamp as text */
   strcat(today,timestamp(tzdelta,ifmt)); /* get timestamp */
   strcat(today,"}"); /* terminate \text{} braces */
   todaysp = rasterize(today,size); /* rasterize timestamp */
   /* --- return timestamp raster to caller --- */
   /*end_of_job:*/
     return ( todaysp ); /* return timestamp to caller */
   } /* --- end-of-function rasttoday() --- */
   
   
   /* ==========================================================================
    * Function: rastcalendar ( expression, size, basesp, arg1, arg2, arg3 )
    * Purpose: handle \calendar
    * --------------------------------------------------------------------------
    * Arguments: expression (I/O) char **  to first char of null-terminated
    * string immediately following \calendar
    * and returning ptr immediately
    * following last character processed.
    * size (I) int containing 0-5 default font size
    * basesp (I) subraster *  to character (or subexpression)
    * immediately preceding \calendar
    * (unused, but passed for consistency)
    * arg1 (I) int unused
    * arg2 (I) int unused
    * arg3 (I) int unused
    * --------------------------------------------------------------------------
    * Returns: ( subraster * ) subraster ptr to rendered one-month calendar
    * --------------------------------------------------------------------------
    * Notes:     o
    * ======================================================================= */
   /* --- entry point --- */
   subraster *rastcalendar ( char **expression, int size, subraster *basesp,
    int arg1, int arg2, int arg3 )
   {
   /* -------------------------------------------------------------------------
   Allocations and Declarations
   -------------------------------------------------------------------------- */
   char *texsubexpr(), optarg[2050]; /* optional [year,month] args */
   char *calendar(), *calstr=NULL; /* calendar to be rasterized */
   subraster *rasterize(), *calendarsp=NULL; /* rasterize calendar string */
   int year=0,month=0,day=0, argval=0; /* default calendar() args */
   /* -------------------------------------------------------------------------
   Get optional args \today[+/-tzdelta,ifmt]
   -------------------------------------------------------------------------- */
   /* --- check for optional \calendar[year,month] --- */
   if ( *(*expression) == '[' ) /* check for []-enclosed value */
     { *expression = texsubexpr(*expression,optarg,2047,"[","]",0,0);
       if ( *optarg != '\000' ) /* got optional arg */
        { char *comma = strchr(optarg,','), /* comma between year,month */
          *comma2 = NULL; /* second comma before day */
          int iarg, nargs=(comma==NULL?1:2); /* #optional args between []'s */
          if ( comma != NULL ) { *comma = '\000'; /*null-terminate first arg*/
    if ( (comma2=strchr(comma+1,',')) != NULL ) /* have third arg */
    { *comma2 = '\000'; nargs++; } } /* null-term 2nd arg, bump count */
          for ( iarg=1; iarg<=nargs; iarg++ ) /* process one or both args */
    { char *arg= (iarg==1?optarg:(iarg==2?comma+1:comma2+1)); /*get arg*/
     argval = atoi(arg); /* interpret arg as integer */
     if ( iarg < 3 ) /* first two args are month,year */
      {if ( argval>1972 && argval<2100 ) year = argval; /* year value */
       else if ( argval>=1 && argval<=12 ) month = argval;} /*or month*/
     else /* only 3rd arg can be day */
      if ( argval>=1 && argval<=31 ) day = argval; } /* day value */
        } /* --- end-of-if(*optarg!='\0') --- */
     } /* --- end-of-if(**expression=='[') --- */
   /* -------------------------------------------------------------------------
   Get calendar string and rasterize it
   -------------------------------------------------------------------------- */
   if ( msgfp!= NULL && msglevel>=9 )
     fprintf(msgfp,"rastcalendar> year=%d, month=%d, day=%d\n",
     year,month,day);
   calstr = calendar(year,month,day); /* get calendar string */
   calendarsp = rasterize(calstr,size); /* rasterize calendar string */
   /* --- return calendar raster to caller --- */
   /*end_of_job:*/
     return ( calendarsp ); /* return calendar to caller */
   } /* --- end-of-function rastcalendar() --- */
   
   
   /* ==========================================================================
  * Function: rastnoop ( expression, size, basesp, nargs, arg2, arg3 )   * Function: rastnoop ( expression, size, basesp, nargs, arg2, arg3 )
  * Purpose: no op -- flush \escape without error   * Purpose: no op -- flush \escape without error
  * --------------------------------------------------------------------------   * --------------------------------------------------------------------------
Line 8594  countersp = rasterize(text,size); /* ras Line 9675  countersp = rasterize(text,size); /* ras
  * following last character processed.   * following last character processed.
  * size (I) int containing 0-5 default font size   * size (I) int containing 0-5 default font size
  * basesp (I) subraster *  to character (or subexpression)   * basesp (I) subraster *  to character (or subexpression)
  * immediately preceding \fbox   * immediately preceding \escape
  * (unused, but passed for consistency)   * (unused, but passed for consistency)
  * nargs (I) int containing number of {}-args after   * nargs (I) int containing number of {}-args after
  * \escape to be flushed along with it   * \escape to be flushed along with it
Line 8648  FILE *rastopenfile ( char *filename, cha Line 9729  FILE *rastopenfile ( char *filename, cha
 Allocations and Declarations  Allocations and Declarations
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 FILE *fp = (FILE *)NULL /*,*fopen()*/; /*file pointer to opened filename*/  FILE *fp = (FILE *)NULL /*,*fopen()*/; /*file pointer to opened filename*/
 char texfile[2048] = "\000", /* local copy of input filename */  char texfile[2048] = "\000", /* local, edited copy of filename */
    *rasteditfilename(), /* prepend pathprefix if necessary */
  amode[128] = "r"; /* test open mode if arg mode=NULL */   amode[128] = "r"; /* test open mode if arg mode=NULL */
 int ismode = 0, /* true of mode!=NULL */  int ismode = 0; /* true of mode!=NULL */
  isprefix = (*pathprefix=='\000'?0:1); /* true if paths have prefix */  
 /* --------------------------------------------------------------------------  /* --------------------------------------------------------------------------
 Check mode and open file  Check mode and open file
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 /* --- check filename --- */  /* --- edit filename --- */
 if ( filename != (char *)NULL ) /* caller passed filename arg */  strcpy(texfile,rasteditfilename(filename)); /*edited copy of input filename*/
  if ( strlen(filename) >= 1 ) /* make sure we got actual filename*/  
   { char *pfilename = filename; /* ptr to 1st char of filename */  
     *texfile = '\000'; /* init filename as null string */  
     while ( isthischar(*pfilename," /\\") ) /* absolute paths invalid */  
       pfilename++; /* so flush leading / or \ (or ' ')*/  
     if ( isprefix && *pfilename!='\000' ) /* paths preceded by prefix */  
       { strcat(texfile,pathprefix); /* init filename with path */  
  while ( memcmp(pfilename,"../",3)==0 /* have leading ../ */  
   || memcmp(pfilename,"..\\",3)==0 ) /* or ..\ with prefix */  
     pfilename += 3; } /* flush leading ../ or ..\ */  
     strcat(texfile,pfilename); /* local copy of given filename */  
     compress(texfile,' '); } /* remove embedded blanks */  
 /* --- check mode --- */  /* --- check mode --- */
 if ( mode != (char *)NULL ) /* caller passed mode arg */  if ( mode != (char *)NULL ) /* caller passed mode arg */
  if ( *mode != '\000' ) /* and it's not an empty string */   if ( *mode != '\000' ) /* and it's not an empty string */
Line 8697  if ( !ismode && fp!=NULL )  /* no mode, Line 9766  if ( !ismode && fp!=NULL )  /* no mode,
   
   
 /* ==========================================================================  /* ==========================================================================
  * Function: rastreadfile ( filename, tag, value )   * Function: rasteditfilename ( filename )
    * Purpose: edits filename to remove security problems,
    * e.g., removes all ../'s and ..\'s.
    * --------------------------------------------------------------------------
    * Arguments: filename (I) char * to null-terminated string containing
    * name of file to be edited
    * --------------------------------------------------------------------------
    * Returns: ( char * ) pointer to edited filename,
    * or empty string "\000" if any problem
    * --------------------------------------------------------------------------
    * Notes:     o
    * ======================================================================= */
   /* --- entry point --- */
   char *rasteditfilename ( char *filename )
   {
   /* -------------------------------------------------------------------------
   Allocations and Declarations
   -------------------------------------------------------------------------- */
   static char editname[2048]; /*edited filename returned to caller*/
   char *strchange(); /* prepend pathprefix if necessary */
   int strreplace(), /* remove ../'s and ..\'s */
    isprefix = (*pathprefix=='\000'?0:1); /* true if paths have prefix */
   /* --------------------------------------------------------------------------
   edit filename
   -------------------------------------------------------------------------- */
   /* --- first check filename arg --- */
   *editname = '\000'; /* init edited name as empty string*/
   if ( filename == (char *)NULL ) goto end_of_job; /* no filename arg */
   if ( *filename == '\000' ) goto end_of_job; /* filename is an empty string */
   /* --- init edited filename --- */
   strcpy(editname,filename); /* init edited name as input name */
   compress(editname,' '); /* remove embedded blanks */
   /* --- remove leading or embedded ....'s --- */
   while ( strreplace(editname,"....",NULL,0) > 0 ) ;  /* squeeze out ....'s */
   /* --- remove leading / and \ and dots (and blanks) --- */
   if ( *editname != '\000' ) /* still have chars in filename */
    while ( isthischar(*editname," ./\\") ) /* absolute paths invalid */
      strcpy(editname,editname+1); /* so flush leading / or \ (or ' ')*/
   if ( *editname == '\000' ) goto end_of_job; /* no chars left in filename */
   /* --- remove leading or embedded ../'s and ..\'s --- */
   while ( strreplace(editname,"../",NULL,0) > 0 ) ;  /* squeeze out ../'s */
   while ( strreplace(editname,"..\\",NULL,0) > 0 ) ; /* and ..\'s */
   while ( strreplace(editname,"../",NULL,0) > 0 ) ;  /* and ../'s again */
   /* --- prepend path prefix (if compiled with -DPATHPREFIX) --- */
   if ( isprefix && *editname!='\000' ) /* filename is preceded by prefix */
     strchange(0,editname,pathprefix); /* so prepend prefix */
   end_of_job:
     return ( editname ); /* back with edited filename */
   } /* --- end-of-function rasteditfilename() --- */
   
   
   /* ==========================================================================
    * Function: rastreadfile ( filename, islock, tag, value )
  * Purpose: Read filename, returning value as string   * Purpose: Read filename, returning value as string
  * between <tag>...</tag> or entire file if tag=NULL passed.   * between <tag>...</tag> or entire file if tag=NULL passed.
  * --------------------------------------------------------------------------   * --------------------------------------------------------------------------
  * Arguments: filename (I) char * to null-terminated string containing   * Arguments: filename (I) char * to null-terminated string containing
  * name of file to read (preceded by path   * name of file to read (preceded by path
  * relative to mimetex executable)   * relative to mimetex executable)
    * islock (I) int containing 1 to lock file while reading
    * (hopefully done by opening in "r+" mode)
  * tag (I) char * to null-terminated string containing   * tag (I) char * to null-terminated string containing
  * html-like tagname.  File contents between   * html-like tagname.  File contents between
  * <tag> and </tag> will be returned, or   * <tag> and </tag> will be returned, or
Line 8716  if ( !ismode && fp!=NULL )  /* no mode, Line 9839  if ( !ismode && fp!=NULL )  /* no mode,
  * Notes:     o   * Notes:     o
  * ======================================================================= */   * ======================================================================= */
 /* --- entry point --- */  /* --- entry point --- */
 int rastreadfile ( char *filename, char *tag, char *value )  int rastreadfile ( char *filename, int islock, char *tag, char *value )
 {  {
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 Allocations and Declarations  Allocations and Declarations
Line 8726  char texfile[2048] = "\000",  /* local c Line 9849  char texfile[2048] = "\000",  /* local c
  text[4096]; /* line from input file */   text[4096]; /* line from input file */
 char *tagp, tag1[512], tag2[512]; /* left <tag> and right <tag/> */  char *tagp, tag1[512], tag2[512]; /* left <tag> and right <tag/> */
 int vallen=0, maxvallen=8000; /* #chars in value, max allowed */  int vallen=0, maxvallen=8000; /* #chars in value, max allowed */
 int status = 0; /* status returned, 1=okay */  int status = (-1); /* status returned, 1=okay */
 int tagnum = 0; /* tag we're looking for */  int tagnum = 0; /* tag we're looking for */
   /*int islock = 1;*/ /* true to lock file */
 /* --------------------------------------------------------------------------  /* --------------------------------------------------------------------------
 Open file  Open file
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
Line 8737  if ( value == (char *)NULL ) goto end_of Line 9861  if ( value == (char *)NULL ) goto end_of
 /* --- open filename or filename.tex --- */  /* --- open filename or filename.tex --- */
 if ( filename != (char *)NULL ) /* make sure we got filename arg */  if ( filename != (char *)NULL ) /* make sure we got filename arg */
   { strcpy(texfile,filename); /* local copy of filename */    { strcpy(texfile,filename); /* local copy of filename */
     fp = rastopenfile(texfile,"r"); } /* try opening it */      fp = rastopenfile(texfile,(islock?"r+":"r")); } /* try opening it */
 /* --- check that file opened --- */  /* --- check that file opened --- */
 if ( fp == (FILE *)NULL ) /* failed to open file */  if ( fp == (FILE *)NULL ) /* failed to open file */
   { sprintf(value,"{\\normalsize\\rm[file %s?]}",texfile);    { sprintf(value,"{\\normalsize\\rm[file %s?]}",texfile);
     goto end_of_job; } /* return error message to caller */      goto end_of_job; } /* return error message to caller */
   status = 0; /* file opened successfully */
   if ( islock ) rewind(fp); /* start at beginning of file */
 /* --------------------------------------------------------------------------  /* --------------------------------------------------------------------------
 construct <tag>'s  construct <tag>'s
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
Line 8757  Read file, concatnate lines Line 9883  Read file, concatnate lines
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 while ( fgets(text,4090,fp) != (char *)NULL ) { /* read input till eof */  while ( fgets(text,4090,fp) != (char *)NULL ) { /* read input till eof */
   switch ( tagnum ) { /* look for left- or right-tag */    switch ( tagnum ) { /* look for left- or right-tag */
     case 0: break; /* no tag to look for */      case 0: status = 1; break; /* no tag to look for */
     case 1: /* looking for opening left <tag> */      case 1: /* looking for opening left <tag> */
       if ( (tagp=strstr(text,tag1)) == NULL ) break; /*haven't found it yet*/        if ( (tagp=strstr(text,tag1)) == NULL ) break; /*haven't found it yet*/
       strcpy(text,tagp+strlen(tag1)); /* shift out preceding text */        strcpy(text,tagp+strlen(tag1)); /* shift out preceding text */
Line 8766  while ( fgets(text,4090,fp) != (char *)N Line 9892  while ( fgets(text,4090,fp) != (char *)N
       if ( (tagp=strstr(text,tag2)) == NULL ) break; /*haven't found it yet*/        if ( (tagp=strstr(text,tag2)) == NULL ) break; /*haven't found it yet*/
       *tagp = '\000'; /* terminate line at tag */        *tagp = '\000'; /* terminate line at tag */
       tagnum = 3; /* done after this line */        tagnum = 3; /* done after this line */
         status = 1; /* successfully read tag */
       break;        break;
     } /* ---end-of-switch(tagnum) --- */      } /* ---end-of-switch(tagnum) --- */
   if ( tagnum != 1 ) { /* no tag or left tag already found*/    if ( tagnum != 1 ) { /* no tag or left tag already found*/
Line 8821  int istag=0, rastreadfile(), /* read fil Line 9948  int istag=0, rastreadfile(), /* read fil
  /*isstrict = (seclevel>5? 1:0),*/ /*true only writes existing files*/   /*isstrict = (seclevel>5? 1:0),*/ /*true only writes existing files*/
  isnewfile = 0, /* true if writing new file */   isnewfile = 0, /* true if writing new file */
  status = 0; /* status returned, 1=okay */   status = 0; /* status returned, 1=okay */
   int istimestamp = 0; /* true to update <timestamp> tag */
 /* --------------------------------------------------------------------------  /* --------------------------------------------------------------------------
 check args  check args
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
Line 8850  read existing file if just rewriting a s Line 9978  read existing file if just rewriting a s
 *filebuff = '\000'; /* init as empty file */  *filebuff = '\000'; /* init as empty file */
 if ( !isnewfile ) /* if file already exists */  if ( !isnewfile ) /* if file already exists */
  if ( istag ) /* and just rewriting one tag */   if ( istag ) /* and just rewriting one tag */
   if ( rastreadfile(texfile,NULL,filebuff) /* read entire existing file */    if ( rastreadfile(texfile,1,NULL,filebuff) /* read entire existing file */
   ==   0 ) goto end_of_job; /* signal error if failed to read */    <=   0 ) goto end_of_job; /* signal error if failed to read */
 /* --------------------------------------------------------------------------  /* --------------------------------------------------------------------------
 construct new file data if needed (entire file replaced by value if no tag)  construct new file data if needed (entire file replaced by value if no tag)
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
Line 8908  if ( fputs((istag?filebuff:value),fp) /* Line 10036  if ( fputs((istag?filebuff:value),fp) /*
 !=  EOF ) status = 1; /* signal success if succeeded */  !=  EOF ) status = 1; /* signal success if succeeded */
 fclose ( fp ); /* close output file after writing */  fclose ( fp ); /* close output file after writing */
 /* --- modify timestamp --- */  /* --- modify timestamp --- */
 if ( istag ) /* log mod time in tagged file */  if ( status > 0 ) /*forget timestamp if write failed*/
  if ( strstr(tag,"timestamp") == (char *)NULL ) /* but avoid recursion */   if ( istimestamp ) /* if we're updating timestamp */
   { char fbuff[2048]; /* field buff <timestamp> value */    if ( istag ) /* only log time in tagged file */
     strcpy(fbuff,tag); /* tag modified */     if ( strstr(tag,"timestamp") == (char *)NULL ) /* but avoid recursion */
     strcat(fbuff," modified at "); /* spacer */      { char fbuff[2048]; /* field buff <timestamp> value */
     strcat(fbuff,timestamp()); /* start with timestamp */        strcpy(fbuff,tag); /* tag modified */
     rastwritefile(filename,"timestamp",fbuff,1); }        strcat(fbuff," modified at "); /* spacer */
         strcat(fbuff,timestamp(TZDELTA,0)); /* start with timestamp */
         status = rastwritefile(filename,"timestamp",fbuff,1); }
 /* --- return status to caller --- */  /* --- return status to caller --- */
 end_of_job:  end_of_job:
   return ( status ); /* return status to caller */    return ( status ); /* return status to caller */
Line 8922  end_of_job: Line 10052  end_of_job:
   
   
 /* ==========================================================================  /* ==========================================================================
  * Function: timestamp ( )   * Function: calendar ( year, month, day )
    * Purpose: returns null-terminated character string containing
    * \begin{array}...\end{array} for the one-month calendar
    * specified by year=1973...2099 and month=1...12.
    * If either arg out-of-range, today's value is used.
    * --------------------------------------------------------------------------
    * Arguments: year (I) int containing 1973...2099 or 0 for current
    * year
    * month (I) int containing 1...12 or 0 for current month
    * day (I) int containing day to emphasize or 0
    * --------------------------------------------------------------------------
    * Returns: ( char * ) char ptr to null-terminated buffer
    * containing \begin{array}...\end{array}
    * string that will render calendar for
    * requested month, or NULL for any error.
    * --------------------------------------------------------------------------
    * Notes:     o
    * ======================================================================= */
   /* --- entry point --- */
   char *calendar( int year, int month, int day )
   {
   /* -------------------------------------------------------------------------
   Allocations and Declarations
   -------------------------------------------------------------------------- */
   static char calbuff[4096]; /* calendar returned to caller */
   time_t time_val = (time_t)(0); /* binary value returned by time() */
   struct tm *tmstruct=(struct tm *)NULL, *localtime(); /* interpret time_val */
   int yy=0, mm=0, dd=0; /* today (emphasize today's dd) */
   int idd=1, iday=0, daynumber(); /* day-of-week for idd=1...31 */
   char aval[64]; /* ascii day or 4-digit year */
   /* --- calendar data --- */
   static char *monthnames[] = { "?", "January", "February", "March", "April",
    "May", "June", "July", "August", "September", "October",
    "November", "December", "?" } ;
   static int modays[] =
    { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0 };
   /* -------------------------------------------------------------------------
   initialization
   -------------------------------------------------------------------------- */
   /* --- get current date/time --- */
   time((time_t *)(&time_val)); /* get date and time */
   tmstruct = localtime((time_t *)(&time_val)); /* interpret time_val */
   yy  =  1900 + (int)(tmstruct->tm_year); /* current four-digit year */
   mm  =  1 + (int)(tmstruct->tm_mon); /* current month, 1-12 */
   dd  =  (int)(tmstruct->tm_mday); /* current day, 1-31 */
   /* --- check args --- */
   if ( year<1973 || year>2099 ) year  = yy; /* current year if out-of-bounds */
   if ( month<1 || month>12 ) month = mm; /* current month if out-of-bounds */
   if ( month==mm && year==yy && day==0 ) /* current month and default day */
     day = dd; /* emphasize current day */
   modays[2] = (year%4==0?29:28); /* Feb has 29 days in leap years */
   /* --- initialize calendar string --- */
   strcpy(calbuff,"{\\begin{gather}"); /* center `month year` above cal */
   strcat(calbuff,"\\small\\text{"); /* month set in roman */
   strcat(calbuff,monthnames[month]); /* insert month name */
   strcat(calbuff," }"); /* add a space */
   sprintf(aval,"%d",year); /* convert year to ascii */
   strcat(calbuff,aval); /* add year */
   strcat(calbuff,"\\\\"); /* end top row */
   strcat(calbuff, /* now begin calendar arrayr */
    "\\begin{array}{|c|c|c|c|c|c|c|CCCCCC} \\hline"
    "\\tiny\\text{Sun} & \\tiny\\text{Mon} & \\tiny\\text{Tue} &"
    "\\tiny\\text{Wed} & \\tiny\\text{Thu} & \\tiny\\text{Fri} &"
    "\\tiny\\text{Sat} \\\\ \\hline " );
   /* -------------------------------------------------------------------------
   generate calendar
   -------------------------------------------------------------------------- */
   for ( idd=1; idd<=modays[month]; idd++ ) /* run through days of month */
     {
     /* --- get day-of-week for this day --- */
     iday = 1 + (daynumber(year,month,idd)%7); /* 1=Monday...7=Sunday */
     if ( iday == 7 ) iday = 0; /* now 0=Sunday...6=Saturday */
     /* --- may need empty cells at beginning of month --- */
     if ( idd == 1 ) /* first day of month */
      if ( iday > 0 ) /* need to skip cells */
       { strcpy(aval,"\\ &\\ &\\ &\\ &\\ &\\ &\\ &\\ &\\ &\\"); /*cells to skip*/
         aval[3*iday] = '\000'; /*skip cells preceding 1st of month*/
         strcat(calbuff,aval); } /* add skip string to buffer */
     /* --- add idd to current cell --- */
     sprintf(aval,"%d",idd); /* convert idd to ascii */
     if ( idd == day /* emphasize today's date */
     /*&&   month==mm && year==yy*/ ) /* only if this month's calendar */
      { strcat(calbuff,"{\\fs{-1}\\left\\langle "); /*emphasize, 1 size smaller*/
        strcat(calbuff,aval); /* put in idd */
        strcat(calbuff,"\\right\\rangle}"); } /* finish emphasis */
     else /* not today's date */
       strcat(calbuff,aval); /* so just put in idd */
     /* --- terminate cell --- */
     if ( idd < modays[month] ) /* not yet end-of-month */
      if ( iday < 6 ) /* still have days left in week */
       strcat(calbuff,"&"); /* new cell in same week */
      else /* reached end-of-week */
       strcat(calbuff,"\\\\ \\hline"); /* so start new week */
     } /* --- end-of-for(idd) --- */
   strcat(calbuff,"\\\\ \\hline"); /* final underline at end-of-month */
   /* --- return calendar to caller --- */
   strcat(calbuff,"\\end{array}\\end{gather}}"); /* terminate array */
   return ( calbuff ); /* back to caller with calendar */
   } /* --- end-of-function calendar() --- */
   
   
   /* ==========================================================================
    * Function: timestamp ( tzdelta, ifmt )
  * Purpose: returns null-terminated character string containing   * Purpose: returns null-terminated character string containing
  * current date:time stamp as ccyy-mm-dd:hh:mm:ss{am,pm}   * current date:time stamp as ccyy-mm-dd:hh:mm:ss{am,pm}
  * --------------------------------------------------------------------------   * --------------------------------------------------------------------------
  * Arguments: ( none )   * Arguments: tzdelta (I) integer, positive or negative, containing
    * containing number of hours to be added or
    * subtracted from system time (to accommodate
    * your desired time zone).
    * ifmt (I) integer containing 0 for default format
  * --------------------------------------------------------------------------   * --------------------------------------------------------------------------
  * Returns: ( char * ) ptr to null-terminated buffer   * Returns: ( char * ) ptr to null-terminated buffer
  * containing current date:time stamp   * containing current date:time stamp
Line 8934  end_of_job: Line 10170  end_of_job:
  * Notes:     o   * Notes:     o
  * ======================================================================= */   * ======================================================================= */
 /* --- entry point --- */  /* --- entry point --- */
 char *timestamp( )  char *timestamp( int tzdelta, int ifmt )
 {  {
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 Allocations and Declarations  Allocations and Declarations
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 static char timebuff[64]; /* date:time buffer back to caller */  static char timebuff[256]; /* date:time buffer back to caller */
 /*long time_val = 0L;*/ /* binary value returned by time() */  /*long time_val = 0L;*/ /* binary value returned by time() */
 time_t time_val = (time_t)(0); /* binary value returned by time() */  time_t time_val = (time_t)(0); /* binary value returned by time() */
 struct tm *tmstruct=(struct tm *)NULL, *localtime(); /* interpret time_val */  struct tm *tmstruct=(struct tm *)NULL, *localtime(); /* interpret time_val */
 int year=0, hour=0,ispm=1; /* adjust year, and set am/pm hour */  int year=0, hour=0,ispm=1, /* adjust year, and set am/pm hour */
    month=0, day=0; /* adjust day and month for delta  */
   int tzadjust(); /* time zone adjustment function */
   int daynumber(); /* #days since Jan 1, 1973 */
   static char *daynames[] = { "Monday", "Tuesday", "Wednesday",
    "Thursday", "Friday", "Saturday", "Sunday" } ;
   static char *monthnames[] = { "?", "January", "February", "March", "April",
    "May", "June", "July", "August", "September", "October",
    "November", "December", "?" } ;
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 get current date:time, adjust values, and and format stamp  get current date:time, adjust values, and and format stamp
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
   /* --- first init returned timebuff in case of any error --- */
   *timebuff = '\000';
 /* --- get current date:time --- */  /* --- get current date:time --- */
 time((time_t *)(&time_val)); /* get date and time */  time((time_t *)(&time_val)); /* get date and time */
 tmstruct = localtime((time_t *)(&time_val)); /* interpret time_val */  tmstruct = localtime((time_t *)(&time_val)); /* interpret time_val */
 /* --- adjust year and hour as necessary --- */  /* --- extract fields --- */
 year = (int)(tmstruct->tm_year); /* local copy of year */  year  = (int)(tmstruct->tm_year); /* local copy of year,  0=1900 */
 hour = (int)(tmstruct->tm_hour); /* local copy of hour */  month = (int)(tmstruct->tm_mon) + 1; /* local copy of month, 1-12 */
   day   = (int)(tmstruct->tm_mday); /* local copy of day,   1-31 */
   hour  = (int)(tmstruct->tm_hour); /* local copy of hour,  0-23 */
   /* --- adjust year --- */
 year += 1900; /* set century in year */  year += 1900; /* set century in year */
 if ( hour < 12 ) /* am check */  /* --- adjust for timezone --- */
   { ispm=0; /* reset pm flag */  tzadjust(tzdelta,&year,&month,&day,&hour);
     if ( hour == 0 ) hour = 12; } /* set 00hrs = 12am */  /* --- check params --- */
 if ( hour > 12 ) hour -= 12; /* pm check sets 13hrs to 1pm, etc */  if ( hour<0  || hour>23
   ||   day<1   || day>31
   ||   month<1 || month>12
   ||   year<1973 ) goto end_of_job;
   /* --- adjust hour for am/pm --- */
   switch ( ifmt )
     {
     default:
     case 0:
       if ( hour < 12 ) /* am check */
        { ispm=0; /* reset pm flag */
          if ( hour == 0 ) hour = 12; } /* set 00hrs = 12am */
       if ( hour > 12 ) hour -= 12; /* pm check sets 13hrs to 1pm, etc */
       break;
     } /* --- end-of-switch(ifmt) --- */
 /* --- format date:time stamp --- */  /* --- format date:time stamp --- */
 sprintf(timebuff,"%04d-%02d-%02d:%02d:%02d:%02d%s",  switch ( ifmt )
   year,(int)((tmstruct->tm_mon)+1),(int)(tmstruct->tm_mday),    {
   hour,(int)(tmstruct->tm_min),(int)(tmstruct->tm_sec),((ispm)?"pm":"am"));    default:
 return ( timebuff ); /* return stamp to caller */    case 0:  /* --- 2005-03-05:11:49:59am --- */
       sprintf(timebuff,"%04d-%02d-%02d:%02d:%02d:%02d%s", year,month,day,
       hour,(int)(tmstruct->tm_min),(int)(tmstruct->tm_sec),((ispm)?"pm":"am"));
       break;
     case 1:  /* --- Saturday, March 5, 2005 --- */
       sprintf(timebuff,"%s, %s %d, %d",
       daynames[daynumber(year,month,day)%7],monthnames[month],day,year);
       break;
     case 2: /* --- Saturday, March 5, 2005, 11:49:59am --- */
       sprintf(timebuff,"%s, %s %d, %d, %d:%02d:%02d%s",
       daynames[daynumber(year,month,day)%7],monthnames[month],day,year,
       hour,(int)(tmstruct->tm_min),(int)(tmstruct->tm_sec),((ispm)?"pm":"am"));
       break;
     case 3: /* --- 11:49:59am --- */
       sprintf(timebuff,"%d:%02d:%02d%s",
       hour,(int)(tmstruct->tm_min),(int)(tmstruct->tm_sec),((ispm)?"pm":"am"));
       break;
     } /* --- end-of-switch(ifmt) --- */
   end_of_job:
     return ( timebuff ); /* return stamp to caller */
 } /* --- end-of-function timestamp() --- */  } /* --- end-of-function timestamp() --- */
   
   
 /* ==========================================================================  /* ==========================================================================
  * Function: dtoa ( dblval, npts )   * Function: tzadjust ( tzdelta, year, month, day, hour )
    * Purpose: Adjusts hour, and day,month,year if necessary,
    * by delta increment to accommodate your time zone.
    * --------------------------------------------------------------------------
    * Arguments: tzdelta (I) integer, positive or negative, containing
    * containing number of hours to be added or
    * subtracted from given time (to accommodate
    * your desired time zone).
    * year (I) addr of int containing        4-digit year
    * month (I) addr of int containing month  1=Jan - 12=Dec.
    * day (I) addr of int containing day    1-31 for Jan.
    * hour (I) addr of int containing hour   0-23
    * Returns: ( int ) 1 for success, or 0 for error
    * --------------------------------------------------------------------------
    * Notes:     o
    * ======================================================================= */
   /* --- entry point --- */
   int tzadjust ( int tzdelta, int *year, int *month, int *day, int *hour )
   {
   /* --------------------------------------------------------------------------
   Allocations and Declarations
   -------------------------------------------------------------------------- */
   int yy = *year, mm = *month, dd = *day, hh = *hour; /*dereference args*/
   /* --- calendar data --- */
   static int modays[] =
    { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0 };
   /* --------------------------------------------------------------------------
   check args
   -------------------------------------------------------------------------- */
   if ( mm<1 || mm>12 ) return(-1); /* bad month */
   if ( dd<1 || dd>modays[mm] ) return(-1); /* bad day */
   if ( hh<0 || hh>23 ) return(-1); /* bad hour */
   if ( tzdelta>23 || tzdelta<(-23) ) return(-1); /* bad tzdelta */
   /* --------------------------------------------------------------------------
   make adjustments
   -------------------------------------------------------------------------- */
   /* --- adjust hour --- */
   hh += tzdelta; /* apply caller's delta */
   /* --- adjust for feb 29 --- */
   modays[2] = (yy%4==0?29:28); /* Feb has 29 days in leap years */
   /* --- adjust day --- */
   if ( hh < 0 ) /* went to preceding day */
     { dd--;  hh += 24; }
   if ( hh > 23 ) /* went to next day */
     { dd++;  hh -= 24; }
   /* --- adjust month --- */
   if ( dd < 1 ) /* went to preceding month */
     { mm--;  dd = modays[mm]; }
   if ( dd > modays[mm] ) /* went to next month */
     { mm++;  dd = 1; }
   /* --- adjust year --- */
   if ( mm < 1 ) /* went to preceding year */
     { yy--;  mm = 12;  dd = modays[mm]; }
   if ( mm > 12 ) /* went to next year */
     { yy++;  mm = 1;   dd = 1; }
   /* --- back to caller --- */
   *year=yy; *month=mm; *day=dd; *hour=hh; /* reset adjusted args */
   return ( 1 );
   } /* --- end-of-function tzadjust() --- */
   
   
   /* ==========================================================================
    * Function: daynumber ( year, month, day )
    * Purpose: Returns number of actual calendar days from Jan 1, 1973
    * to the given date (e.g., bvdaynumber(1974,1,1)=365).
    * --------------------------------------------------------------------------
    * Arguments: year (I) int containing year -- may be either 1995 or
    * 95, or may be either 2010 or 110 for those
    * years.
    * month (I) int containing month, 1=Jan thru 12=Dec.
    * day (I) int containing day of month, 1-31 for Jan, etc.
    * Returns: ( int ) Number of days from Jan 1, 1973 to given date,
    * or -1 for error (e.g., year<1973).
    * --------------------------------------------------------------------------
    * Notes:     o
    * ======================================================================= */
   /* --- entry point --- */
   int daynumber ( int year, int month, int day )
   {
   /* --------------------------------------------------------------------------
   Allocations and Declarations
   -------------------------------------------------------------------------- */
   /* --- returned value (note: returned as a default "int") --- */
   int ndays; /* #days since jan 1, year0 */
   /* --- initial conditions --- */
   static int year0 = 73, /* jan 1 was a monday, 72 was a leap */
    days4yrs = 1461, /* #days in 4 yrs = 365*4 + 1 */
    days1yr  = 365;
   /* --- table of accumulated days per month (last index not used) --- */
   static int modays[] =
    { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };
   /* --- variables for #days since day0 --- */
   int nyears, nfouryrs; /*#years, #4-yr periods since year0*/
   /* --------------------------------------------------------------------------
   Check input
   -------------------------------------------------------------------------- */
   if ( month < 1 || month > 12 ) /*month used as index, so must be ok*/
    return ( -1 ); /* otherwise, forget it */
   if ( year >= 1900 ) year -= 1900; /*use two-digit years (3 after 2000)*/
   /* --------------------------------------------------------------------------
   Find #days since jan 1, 1973
   -------------------------------------------------------------------------- */
   /* --- figure #complete 4-year periods and #remaining yrs till current --- */
   nyears = year - year0; /* #years since year0 */
   if ( nyears < 0 ) return ( -1 ); /* we're not working backwards */
   nfouryrs = nyears/4; /* #complete four-year periods */
   nyears -= (4*nfouryrs); /* remainder excluding current year*/
   /* --- #days from jan 1, year0 till jan 1, this year --- */
   ndays = (days4yrs*nfouryrs) /* #days in 4-yr periods */
         +  (days1yr*nyears); /* +remaining days */
   /*if ( year > 100 ) ndays--;*/ /* subtract leap year for 2000AD */
   /* --- add #days within current year --- */
   ndays += (modays[month-1] + (day-1));
   /* --- may need an extra day if current year is a leap year --- */
   if ( nyears == 3 ) /*three preceding yrs so this is 4th*/
       { if ( month > 2 ) /* past feb so need an extra day */
    /*if ( year != 100 )*/ /* unless it's 2000AD */
     ndays++; } /* so add it in */
   return ( (int)(ndays) ); /* #days back to caller */
   } /* --- end-of-function daynumber() --- */
   
   
   /* ==========================================================================
    * Function: dbltoa ( dblval, npts )
  * Purpose: Converts double to ascii, in financial format   * Purpose: Converts double to ascii, in financial format
  * (e.g., comma-separated and negatives enclosed in ()'s).   * (e.g., comma-separated and negatives enclosed in ()'s).
  * -------------------------------------------------------------------------   * -------------------------------------------------------------------------
Line 8980  return ( timebuff );   /* return stamp t Line 10385  return ( timebuff );   /* return stamp t
  * Notes:     o   * Notes:     o
  * ======================================================================= */   * ======================================================================= */
 /* --- entry point --- */  /* --- entry point --- */
 char *dtoa ( dblval, npts )  char *dbltoa ( double dblval, int npts )
 double dblval;  /* double dblval;
 int npts;     int npts; */
 {  {
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 Allocations and Declarations  Allocations and Declarations
Line 9048  End-of-Job Line 10453  End-of-Job
 if ( isneg ) *finptr++ = ')'; /*trailing paren for negative value*/  if ( isneg ) *finptr++ = ')'; /*trailing paren for negative value*/
 *finptr = '\000'; /* null-terminate converted double */  *finptr = '\000'; /* null-terminate converted double */
 return ( finval ); /* converted double back to caller */  return ( finval ); /* converted double back to caller */
 } /* --- end-of-function dtoa() --- */  } /* --- end-of-function dbltoa() --- */
   
   
 /* ==========================================================================  /* ==========================================================================
Line 9279  for ( irow=0; irow<height; irow++ ) Line 10684  for ( irow=0; irow<height; irow++ )
  (eebitval==fgbitval && ssbitval==fgbitval) || /*lower-right edge*/   (eebitval==fgbitval && ssbitval==fgbitval) || /*lower-right edge*/
  (ssbitval==fgbitval && wwbitval==fgbitval) || /*lower-left  edge*/   (ssbitval==fgbitval && wwbitval==fgbitval) || /*lower-left  edge*/
  (wwbitval==fgbitval && nnbitval==fgbitval) ; /*upper-left  edge*/   (wwbitval==fgbitval && nnbitval==fgbitval) ; /*upper-left  edge*/
     /* ---check top/bot left/right edges for corners (added by j.forkosh)--- */
     if ( 1 ) { /* true to perform test */
       int isbghorz=0, isfghorz=0, isbgvert=0, isfgvert=0; /* horz/vert edges */
       isbghorz = /* top or bottom edge is all bg */
    (nwbitval+nnbitval+nebitval == 3*bgbitval) || /* top edge bg */
    (swbitval+ssbitval+sebitval == 3*bgbitval) ; /* bottom edge bg */
       isfghorz = /* top or bottom edge is all fg */
    (nwbitval+nnbitval+nebitval == 3*fgbitval) || /* top edge fg */
    (swbitval+ssbitval+sebitval == 3*fgbitval) ; /* bottom edge fg */
       isbgvert = /* left or right edge is all bg */
    (nwbitval+wwbitval+swbitval == 3*bgbitval) || /* left edge bg */
    (nebitval+eebitval+sebitval == 3*bgbitval) ; /* right edge bg */
       isfgvert = /* left or right edge is all bg */
    (nwbitval+wwbitval+swbitval == 3*fgbitval) || /* left edge fg */
    (nebitval+eebitval+sebitval == 3*fgbitval) ; /* right edge fg */
       if ( (isbghorz && isbgvert && (bitval==fgbitval)) /* we're at an...*/
       ||   (isfghorz && isfgvert && (bitval==bgbitval)) ) /*...inside corner */
    continue; /* don't antialias */
       } /* --- end-of-if(1) --- */
     /* --- check #gaps for checkerboard (added by j.forkosh) --- */
     if ( 0 ) { /* true to perform test */
       int ngaps=0, mingaps=1,maxgaps=2; /* count #fg/bg flips (max=4 noop) */
       if ( nwbitval!=nnbitval ) ngaps++; /* upper-left =? upper */
       if ( nnbitval!=nebitval ) ngaps++; /* upper =? upper-right */
       if ( nebitval!=eebitval ) ngaps++; /* upper-right =? right */
       if ( eebitval!=sebitval ) ngaps++; /* right =? lower-right */
       if ( sebitval!=ssbitval ) ngaps++; /* lower-right =? lower */
       if ( ssbitval!=swbitval ) ngaps++; /* lower =? lower-left */
       if ( swbitval!=wwbitval ) ngaps++; /* lower-left =? left */
       if ( wwbitval!=nwbitval ) ngaps++; /* left =? upper-left */
       if ( ngaps > 0 ) ngaps /= 2; /* each gap has 2 bg/fg flips */
       if ( ngaps<mingaps || ngaps>maxgaps ) continue;
       } /* --- end-of-if(1) --- */
   /* --- antialias if necessary --- */    /* --- antialias if necessary --- */
   if ( (isbgalias && isbgedge) /* alias pixel surrounding bg */    if ( (isbgalias && isbgedge) /* alias pixel surrounding bg */
   ||   (isfgalias && isfgedge) /* alias pixel surrounding fg */    ||   (isfgalias && isfgedge) /* alias pixel surrounding fg */
Line 9849  messages Line 11287  messages
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 static char *copyright = /* copyright, gnu/gpl notice */  static char *copyright = /* copyright, gnu/gpl notice */
  "+-----------------------------------------------------------------------+\n"   "+-----------------------------------------------------------------------+\n"
  "|mimeTeX vers 1.61, Copyright(c) 2002-2005, John Forkosh Associates, Inc|\n"   "|mimeTeX vers 1.63, Copyright(c) 2002-2006, John Forkosh Associates, Inc|\n"
  "+-----------------------------------------------------------------------+\n"   "+-----------------------------------------------------------------------+\n"
  "| mimeTeX is free software, licensed to you under terms of the GNU/GPL, |\n"   "| mimeTeX is free software, licensed to you under terms of the GNU/GPL, |\n"
  "|           and comes with absolutely no warranty whatsoever.           |\n"   "|           and comes with absolutely no warranty whatsoever.           |\n"
Line 9890  int unescape_url();   /* convert %xx's t Line 11328  int unescape_url();   /* convert %xx's t
 int emitcache(); /* emit cached image if it exists */  int emitcache(); /* emit cached image if it exists */
 int isquery = 0, /* true if input from QUERY_STRING */  int isquery = 0, /* true if input from QUERY_STRING */
  isqempty = 0, /* true if query string empty */   isqempty = 0, /* true if query string empty */
    isqforce = 0, /* true to force query emulation */
  isqlogging = 0, /* true if logging in query mode */   isqlogging = 0, /* true if logging in query mode */
  isformdata = 0, /* true if input from html form */   isformdata = 0, /* true if input from html form */
  isdumpimage = 0; /* true to dump image on stdout */   isinmemory = 1, /* true to generate image in memory*/
    isdumpimage = 0, /* true to dump image on stdout */
    isdumpbuffer = 0; /* true to dump to memory buffer */
 /* --- rasterization --- */  /* --- rasterization --- */
 subraster *rasterize(), *sp; /* rasterize expression */  subraster *rasterize(), *sp=NULL; /* rasterize expression */
 raster *border_raster(), *bp; /* put a border around raster */  raster *border_raster(), *bp=NULL; /* put a border around raster */
   int delete_subraster(); /* for clean-up at end-of-job */
 int type_raster(), type_bytemap(), /* screen dump function prototypes */  int type_raster(), type_bytemap(), /* screen dump function prototypes */
  xbitmap_raster(); /* mime xbitmap output function */   xbitmap_raster(); /* mime xbitmap output function */
 /* --- http_referer --- */  /* --- http_referer --- */
Line 9918  int norefmaxlen = NOREFMAXLEN; /*max que Line 11360  int norefmaxlen = NOREFMAXLEN; /*max que
   void GIF_SetColor(),GIF_SetTransparent(); /* ...gifsave enntry points */    void GIF_SetColor(),GIF_SetTransparent(); /* ...gifsave enntry points */
 #endif  #endif
 char *gif_outfile = (char *)NULL, /* gif output defaults to stdout */  char *gif_outfile = (char *)NULL, /* gif output defaults to stdout */
    gif_buffer[64000] = "\000", /* or gif written in memory buffer */
  cachefile[256] = "\000", /* full path and name to cache file*/   cachefile[256] = "\000", /* full path and name to cache file*/
  *md5str(); /* md5 has of expression */   *md5str(); /* md5 has of expression */
 int maxage = 7200; /* max-age is two hours */  int maxage = 7200; /* max-age is two hours */
   /* --- pbm/pgm (-g switch) --- */
   int ispbmpgm = 0; /* true to write pbm/pgm file */
   int type_pbmpgm(), ptype=0; /* entry point, graphic format */
   char *pbm_outfile = (char *)NULL; /* output file defaults to stdout */
 /* --- anti-aliasing --- */  /* --- anti-aliasing --- */
 intbyte *bytemap_raster = NULL, /* anti-aliased bitmap */  intbyte *bytemap_raster = NULL, /* anti-aliased bitmap */
  colors[256]; /* grayscale vals in bytemap */   colors[256]; /* grayscale vals in bytemap */
Line 9948  initialization Line 11395  initialization
 /* --- set global variables --- */  /* --- set global variables --- */
 msgfp = stdout; /* for comamnd-line mode output */  msgfp = stdout; /* for comamnd-line mode output */
 isss = issupersampling; /* set supersampling flag */  isss = issupersampling; /* set supersampling flag */
   gifSize = 0; /* signal that image not in memory */
 shrinkfactor = shrinkfactors[NORMALSIZE]; /* set shrinkfactor */  shrinkfactor = shrinkfactors[NORMALSIZE]; /* set shrinkfactor */
 /* ---  /* ---
  * check QUERY_STRING query for expression overriding command-line arg   * check QUERY_STRING query for expression overriding command-line arg
Line 9962  if ( !isquery )    /* empty query string Line 11410  if ( !isquery )    /* empty query string
     *name = getenv("SERVER_NAME"), *addr = getenv("SERVER_ADDR");      *name = getenv("SERVER_NAME"), *addr = getenv("SERVER_ADDR");
     if ( host!=NULL || name!=NULL || addr!=NULL ) /* assume http query */      if ( host!=NULL || name!=NULL || addr!=NULL ) /* assume http query */
       { isquery = 1; /* set flag to signal query */        { isquery = 1; /* set flag to signal query */
  strcpy(expression,"\\red\\small\\rm~missing~query~string"); }   strcpy(expression,"\\red\\small\\fbox{\\rm~no~query~string}"); }
     isqempty = 1; /* signal empty query string */      isqempty = 1; /* signal empty query string */
   } /* --- end-of-if(!isquery) --- */    } /* --- end-of-if(!isquery) --- */
 /* ---  /* ---
Line 9997  if ( !isquery    /* don't have an html q Line 11445  if ( !isquery    /* don't have an html q
     if ( !isstopsignal /* haven't seen stopsignal switch */      if ( !isstopsignal /* haven't seen stopsignal switch */
     &&   *argv[argnum] == '-' ) /* and have some '-' switch */      &&   *argv[argnum] == '-' ) /* and have some '-' switch */
       {        {
       char flag = tolower(*(argv[argnum]+1)); /* single char following '-' */        char *field = argv[argnum] + 1; /* ptr to char(s) following - */
       int  arglen = strlen(argv[argnum]) - 1; /* #chars following - */        char flag = tolower(*field); /* single char following '-' */
         int  arglen = strlen(field); /* #chars following - */
       argnum++; /* arg following flag/switch is usually its value */        argnum++; /* arg following flag/switch is usually its value */
       nswitches++; /* another switch on command line */        nswitches++; /* another switch on command line */
       if ( isstrict && arglen!=1 ) /* only single-char switch allowed */        if ( isstrict && /* if strict checking then... */
         !isthischar(flag,"g") && arglen!=1 ) /*must be single-char switch*/
  { nbadargs++; argnum--; } /* so ignore longer -xxx switch */   { nbadargs++; argnum--; } /* so ignore longer -xxx switch */
       else /* process single-char -x switch */        else /* process single-char -x switch */
        switch ( flag ) { /* see what user wants to tell us */         switch ( flag ) { /* see what user wants to tell us */
  /* --- ignore uninterpreted flag --- */   /* --- ignore uninterpreted flag --- */
  default:  nbadargs++;                              argnum--;  break;   default:  nbadargs++;                              argnum--;  break;
  /* --- adjustable program parameters (not checking input) --- */   /* --- adjustable program parameters (not checking input) --- */
    case 'b': isdumpimage++; isdumpbuffer++;           argnum--;  break;
  case 'd': isdumpimage++;                           argnum--;  break;   case 'd': isdumpimage++;                           argnum--;  break;
  case 'e': isdumpimage++;           gif_outfile=argv[argnum];  break;   case 'e': isdumpimage++;           gif_outfile=argv[argnum];  break;
  case 'f': isdumpimage++;                   infilearg=argnum;  break;   case 'f': isdumpimage++;                   infilearg=argnum;  break;
    case 'g': ispbmpgm++;
        if ( arglen > 1 ) ptype = atoi(field+1); /* -g2 ==> ptype=2 */
        if ( 1 || *argv[argnum]=='-' ) argnum--; /*next arg is -switch*/
        else pbm_outfile = argv[argnum]; break; /*next arg is filename*/
  case 'm': msglevel = atoi(argv[argnum]);                      break;   case 'm': msglevel = atoi(argv[argnum]);                      break;
  case 'o': istransparent = 0;                       argnum--;  break;   case 'o': istransparent = 0;                       argnum--;  break;
    case 'q': isqforce = 1;                            argnum--;  break;
  case 's': size = atoi(argv[argnum]);                          break;   case 's': size = atoi(argv[argnum]);                          break;
  } /* --- end-of-switch(flag) --- */   } /* --- end-of-switch(flag) --- */
       } /* --- end-of-if(*argv[argnum]=='-') --- */        } /* --- end-of-if(*argv[argnum]=='-') --- */
Line 10024  if ( !isquery    /* don't have an html q Line 11480  if ( !isquery    /* don't have an html q
       else nbadargs++; /* infile and expression invalid */        else nbadargs++; /* infile and expression invalid */
     } /* --- end-of-while(argc>++argnum) --- */      } /* --- end-of-while(argc>++argnum) --- */
  if ( msglevel>=999 && msgfp!=NULL ) /* display command-line info */   if ( msglevel>=999 && msgfp!=NULL ) /* display command-line info */
   fprintf(msgfp,"argc=%d, progname=%s, #args=%d, #badargs=%d\n",    { fprintf(msgfp,"argc=%d, progname=%s, #args=%d, #badargs=%d\n",
   argc,progname,nargs,nbadargs);      argc,progname,nargs,nbadargs);
       fprintf(msgfp,"cachepath=\"%.50s\" pathprefix=\"%.50s\"\n",
       cachepath,pathprefix); }
  /* ---   /* ---
   * decide whether command-line input overrides query_string    * decide whether command-line input overrides query_string
   * -------------------------------------------------------- */    * -------------------------------------------------------- */
Line 10057  if ( !isquery    /* don't have an html q Line 11515  if ( !isquery    /* don't have an html q
       strcat(expression,instring); /* concat line to end of expression*/        strcat(expression,instring); /* concat line to end of expression*/
      fclose ( infile ); } /*close input file after reading expression*/       fclose ( infile ); } /*close input file after reading expression*/
   } /* --- end-of-if(infilearg>0) --- */    } /* --- end-of-if(infilearg>0) --- */
    /* ---
     * check if emulating query (for testing)
     * -------------------------------------- */
    if ( isqforce ) isquery = 1; /* emulate query string processing */
    /* ---
     * check if emitting pbm/pgm graphic
     * --------------------------------- */
    if ( isgoodargs && ispbmpgm > 0 ) /* have a good -g arg */
     if ( 1 && gif_outfile != NULL ) /* had an -e switch with file */
      if ( *gif_outfile != '\000' ) /* make sure string isn't empty */
        { pbm_outfile = gif_outfile; /* use -e switch file for pbm/pgm */
          gif_outfile = (char *)NULL; /* reset gif output file */
          /*isdumpimage--;*/ } /* and decrement -e count */
  } /* --- end-of-if(!isquery) --- */   } /* --- end-of-if(!isquery) --- */
 /* ---  /* ---
  * check for <form> input   * check for <form> input
Line 10221  if ( isquery )    /* not relevant if "in Line 11692  if ( isquery )    /* not relevant if "in
 /* ---  /* ---
  * check for image caching   * check for image caching
  * ----------------------- */   * ----------------------- */
 if ( strstr(expression,"\\counter") != NULL /* can't cache \counter{} */  if ( strstr(expression,"\\counter")  != NULL /* can't cache \counter{} */
 ||   strstr(expression,"\\input")   != NULL /* can't cache \input{} */  ||   strstr(expression,"\\input")    != NULL /* can't cache \input{} */
 ||   strstr(expression,"\\nocach")  != NULL /* no caching requested */  ||   strstr(expression,"\\today")    != NULL /* can't cache \today */
   ||   strstr(expression,"\\calendar") != NULL /* can't cache \calendar */
   ||   strstr(expression,"\\nocach")   != NULL /* no caching requested */
   ||   isformdata /* don't cache user form input */
  ) { iscaching = 0; /* so turn caching off */   ) { iscaching = 0; /* so turn caching off */
      maxage = 2; } /* and set max-age to two seconds */       maxage = 5; } /* and set max-age to 5 seconds */
 if ( isquery ) /* don't cache command-line images */  if ( isquery ) /* don't cache command-line images */
  if ( iscaching ) /* image caching enabled */   if ( iscaching ) /* image caching enabled */
   {    {
Line 10239  if ( isquery )    /* don't cache command Line 11713  if ( isquery )    /* don't cache command
    strcat(cachefile,md5hash); /* add md5 hash of expression */     strcat(cachefile,md5hash); /* add md5 hash of expression */
    strcat(cachefile,".gif"); /* finish with .gif extension */     strcat(cachefile,".gif"); /* finish with .gif extension */
    gif_outfile = cachefile; /* signal GIF_Create() to cache */     gif_outfile = cachefile; /* signal GIF_Create() to cache */
    /* --- (always) emit mime content-type line --- */     /* --- emit mime content-type line --- */
    fprintf( stdout, "Cache-Control: max-age=%d\n",maxage );     if ( 0 ) /* now done in emitcache() */
    fprintf( stdout, "Content-type: image/gif\n\n" );      { fprintf( stdout, "Cache-Control: max-age=%d\n",maxage );
         fprintf( stdout, "Content-type: image/gif\n\n" ); }
    /* --- emit cached image if it already exists --- */     /* --- emit cached image if it already exists --- */
    if ( emitcache(cachefile) > 0 ) /* cached image emitted */     if ( emitcache(cachefile,maxage,0) > 0 ) /* cached image emitted */
     goto end_of_job; /* so nothing else to do */      goto end_of_job; /* so nothing else to do */
    /* --- log caching request --- */     /* --- log caching request --- */
    if ( msglevel >= 1 /* check if logging */     if ( msglevel >= 1 /* check if logging */
Line 10258  if ( isquery )    /* don't cache command Line 11733  if ( isquery )    /* don't cache command
         !=   NULL ) /* ignore logging if can't open */          !=   NULL ) /* ignore logging if can't open */
  { int isreflogged = 0; /* set true if http_referer logged */   { int isreflogged = 0; /* set true if http_referer logged */
    fprintf(filefp,"%s                 %s\n", /* timestamp, md5 file */     fprintf(filefp,"%s                 %s\n", /* timestamp, md5 file */
     timestamp(),cachefile+strlen(cachepath)); /* (path not shown) */      timestamp(TZDELTA,0),cachefile+strlen(cachepath)); /*skip path*/
    fprintf(filefp,"%s\n",expression); /* expression in filename */     fprintf(filefp,"%s\n",expression); /* expression in filename */
    if ( http_referer != NULL ) /* show referer if we have one */     if ( http_referer != NULL ) /* show referer if we have one */
     if ( *http_referer != '\000' )    /* and if not an empty string*/      if ( *http_referer != '\000' )    /* and if not an empty string*/
Line 10305  if ( issupersampling )   /* no border ne Line 11780  if ( issupersampling )   /* no border ne
 else /* for mime xbitmaps must have... */  else /* for mime xbitmaps must have... */
   bp = border_raster(sp->image,0,0,0,1); /* image width multiple of 8 bits */    bp = border_raster(sp->image,0,0,0,1); /* image width multiple of 8 bits */
 sp->image = bitmap_raster = bp; /* global copy for gif,png output */  sp->image = bitmap_raster = bp; /* global copy for gif,png output */
   if ( ispbmpgm && ptype<2 ) /* -g switch or -g1 switch */
     type_pbmpgm(bp,ptype,pbm_outfile); /* emit b/w pbm file */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 generate anti-aliased bytemap from (bordered) bitmap  generate anti-aliased bytemap from (bordered) bitmap
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
Line 10351  if ( isaa )    /* we want anti-aliased b Line 11828  if ( isaa )    /* we want anti-aliased b
  { isaa = 0; /* so turn off anti-aliasing */   { isaa = 0; /* so turn off anti-aliasing */
   ncolors = 2; } /* and reset for black&white */    ncolors = 2; } /* and reset for black&white */
       } /* --- end-of-if(isaa) --- */        } /* --- end-of-if(isaa) --- */
        if ( isaa && ispbmpgm && ptype>1 ) { /* -g2 switch  */
         raster pbm_raster; /*construct arg for write_pbmpgm()*/
         pbm_raster.width  = bp->width;  pbm_raster.height = bp->height;
         pbm_raster.pixsz  = 8;  pbm_raster.pixmap = (pixbyte *)bytemap_raster;
         type_pbmpgm(&pbm_raster,ptype,pbm_outfile); } /*write grayscale file*/
     } /* --- end-of-if(isaa) --- */      } /* --- end-of-if(isaa) --- */
   } /* --- end-of-if(isaa) --- */    } /* --- end-of-if(isaa) --- */
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
Line 10392  if ( (!isquery||isqlogging) || msglevel Line 11874  if ( (!isquery||isqlogging) || msglevel
 emit xbitmap or gif image, and exit  emit xbitmap or gif image, and exit
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 if (  isquery /* called from browser (usual) */  if (  isquery /* called from browser (usual) */
 ||    isdumpimage /* or to emit dump of image */  ||    (isdumpimage && !ispbmpgm) /* or to emit gif dump of image */
 ||    msglevel >= 99 ) /* or for debugging */  ||    msglevel >= 99 ) /* or for debugging */
  {   {
  int  igray = 0; /* grayscale index */   int  igray = 0; /* grayscale index */
Line 10400  if (  isquery    /* called from browser Line 11882  if (  isquery    /* called from browser
  /* ------------------------------------------------------------------------   /* ------------------------------------------------------------------------
  emit GIF image   emit GIF image
  ------------------------------------------------------------------------- */   ------------------------------------------------------------------------- */
     /* --- don't use memory buffer if outout file given --- */
     if ( gif_outfile != NULL ) isinmemory = 0; /* reset memory buffer flag */
   /* --- emit mime content-type line --- */    /* --- emit mime content-type line --- */
   if ( !isdumpimage /* don't mix ascii with image dump */    if ( !isdumpimage /* don't mix ascii with image dump */
   &&   !iscaching ) /* already emitted if caching */    &&   !isinmemory /* done below if in memory */
     &&   !iscaching ) /* done by emitcache() if caching */
     { fprintf( stdout, "Cache-Control: max-age=%d\n",maxage );      { fprintf( stdout, "Cache-Control: max-age=%d\n",maxage );
       /*fprintf( stdout, "Expires: Fri, 31 Oct 2003 23:59:59 GMT\n" );*/        /*fprintf( stdout, "Expires: Fri, 31 Oct 2003 23:59:59 GMT\n" );*/
       /*fprintf( stdout, "Last-Modified: Wed, 15 Oct 2003 01:01:01 GMT\n" );*/        /*fprintf( stdout, "Last-Modified: Wed, 15 Oct 2003 01:01:01 GMT\n" );*/
       fprintf( stdout, "Content-type: image/gif\n\n" ); }        fprintf( stdout, "Content-type: image/gif\n\n" ); }
     /* --- write output to memory buffer, possibly for testing --- */
     if ( isinmemory /* want gif written to memory */
     ||   isdumpbuffer ) /*or dump memory buffer for testing*/
      if ( gif_outfile == NULL ) /* and don't already have a file */
       { *gif_buffer = '\000'; /* init buffer as empty string */
         memset(gif_buffer,0,4096); /* zero out buffer */
         gif_outfile = gif_buffer; /* and point outfile to buffer */
         if ( isdumpbuffer ) /* buffer dump test requested */
    isdumpbuffer = 999; } /* so signal dumping to buffer */
   /* --- initialize gifsave library and colors --- */    /* --- initialize gifsave library and colors --- */
   if ( msgfp!=NULL && msglevel>=999 )    if ( msgfp!=NULL && msglevel>=999 )
     fprintf(msgfp,"main> calling GIF_Create(*,%d,%d,%d,8)\n",      { fprintf(msgfp,"main> calling GIF_Create(*,%d,%d,%d,8)\n",
     bp->width,bp->height,ncolors);        bp->width,bp->height,ncolors); fflush(msgfp); }
   while ( 1 ) /* init gifsave lib, and retry if caching fails */    while ( 1 ) /* init gifsave lib, and retry if caching fails */
     { int status = GIF_Create(gif_outfile, bp->width,bp->height, ncolors, 8);      { int status = GIF_Create(gif_outfile, bp->width,bp->height, ncolors, 8);
       if ( status == 0 ) break; /* continue if succeeded */        if ( status == 0 ) break; /* continue if succeeded */
       if ( iscaching == 0 ) goto end_of_job; /* quit if failed */        if ( iscaching == 0 ) goto end_of_job; /* quit if failed */
       iscaching = 0; /* retry without cache file */        iscaching = 0; /* retry without cache file */
       gif_outfile = (char *)NULL; } /* emit images to stdout */        isdumpbuffer = 0; /* reset isdumpbuffer signal */
         if ( isquery ) isinmemory = 1; /* force in-memory image generation*/
         if ( isinmemory ) { /* using memory buffer */
    gif_outfile = gif_buffer; /* emit images to memory buffer */
    *gif_outfile = '\000'; } /* empty string signals buffer */
         else { /* or */
    gif_outfile = (char *)NULL; /* emit images to stdout */
    fprintf( stdout, "Cache-Control: max-age=%d\n",maxage );
    fprintf( stdout, "Content-type: image/gif\n\n" ); }
       } /* --- end-of-while(1) --- */
   GIF_SetColor(0,bgred,bggreen,bgblue); /* background white if all 255 */    GIF_SetColor(0,bgred,bggreen,bgblue); /* background white if all 255 */
   if ( !isaa ) /* just b&w if not anti-aliased */    if ( !isaa ) /* just b&w if not anti-aliased */
     { GIF_SetColor(1,fgred,fggreen,fgblue); /* foreground black if all 0 */      { GIF_SetColor(1,fgred,fggreen,fgblue); /* foreground black if all 0 */
Line 10441  if (  isquery    /* called from browser Line 11944  if (  isquery    /* called from browser
   /* --- emit compressed gif image (to stdout or cache file) --- */    /* --- emit compressed gif image (to stdout or cache file) --- */
   GIF_CompressImage(0, 0, -1, -1, GetPixel); /* emit gif */    GIF_CompressImage(0, 0, -1, -1, GetPixel); /* emit gif */
   GIF_Close(); /* close file */    GIF_Close(); /* close file */
   /* --- may need to emit image from cached file --- */    if ( msgfp!=NULL && msglevel>=9 )
   if ( isquery && iscaching ) /* caching enabled */      { fprintf(msgfp,"main> created gifSize=%d\n", gifSize);
     emitcache(cachefile); /* cached image (hopefully) emitted*/        fflush(msgfp); }
     /* --- may need to emit image from cached file or from memory --- */
     if ( isquery /* have an actual query string */
     ||   isdumpimage /* or dumping image */
     ||   msglevel >= 99 ) { /* or debugging */
     int maxage2 = (isdumpimage?(-1):maxage); /* no headers if dumping image */
      if ( iscaching ) /* caching enabled */
        emitcache(cachefile,maxage2,0); /* cached image (hopefully) emitted*/
      else if ( isinmemory ) /* or emit image from memory buffer*/
        emitcache(gif_buffer,maxage2,1); } /* emitted from memory buffer */
     /* --- for testing, may need to write image buffer to file --- */
     if ( isdumpbuffer > 99 ) /* gif image in memory buffer */
      if ( gifSize > 0 ) /* and it's not an empty buffer */
       { FILE *dumpfp = fopen("mimetex.gif","wb"); /* dump to mimetex.gif */
         if ( dumpfp != NULL ) /* file opened successfully */
    { fwrite(gif_buffer,sizeof(unsigned char),gifSize,dumpfp); /*write*/
     fclose(dumpfp); } /* and close file */
       } /* --- end-of-if(isdumpbuffer>99) --- */
  #else   #else
  /* ------------------------------------------------------------------------   /* ------------------------------------------------------------------------
  emit mime XBITMAP image   emit mime XBITMAP image
Line 10453  if (  isquery    /* called from browser Line 11973  if (  isquery    /* called from browser
  } /* --- end-of-if(isquery) --- */   } /* --- end-of-if(isquery) --- */
 /* --- exit --- */  /* --- exit --- */
 end_of_job:  end_of_job:
   if ( bytemap_raster != NULL ) free(bytemap_raster); /*free bytemap_raster*/    if ( !isss ) /*bytemap raster in sp for supersamp*/
      if ( bytemap_raster != NULL ) free(bytemap_raster);/*free bytemap_raster*/
   if (colormap_raster != NULL )free(colormap_raster); /*and colormap_raster*/    if (colormap_raster != NULL )free(colormap_raster); /*and colormap_raster*/
     if ( 0 && gif_buffer != NULL ) free(gif_buffer); /* free malloced buffer */
     if ( 1 && sp != NULL ) delete_subraster(sp); /* and free expression */
   if ( msgfp != NULL /* have message/log file open */    if ( msgfp != NULL /* have message/log file open */
   &&   msgfp != stdout ) /* and it's not stdout */    &&   msgfp != stdout ) /* and it's not stdout */
    { fprintf(msgfp,"mimeTeX> successful end-of-job at %s\n",timestamp());     { fprintf(msgfp,"mimeTeX> successful end-of-job at %s\n",
          timestamp(TZDELTA,0));
      fprintf(msgfp,"%s\n",dashes); /* so log separator line */       fprintf(msgfp,"%s\n",dashes); /* so log separator line */
      fclose(msgfp); } /* and close logfile */       fclose(msgfp); } /* and close logfile */
   exit ( 0 );    /* --- dump memory leaks in debug window if in MS VC++ debug mode --- */
     #if defined(_CRTDBG_MAP_ALLOC)
       _CrtDumpMemoryLeaks();
     #endif
     /* --- exit() if not running as Windows DLL (see CreateGifFromEq()) --- */
     #if !defined(_USRDLL)
       exit ( 0 );
     #endif
 } /* --- end-of-function main() --- */  } /* --- end-of-function main() --- */
   
 /* ==========================================================================  /* ==========================================================================
    * Function: CreateGifFromEq ( expression, gifFileName )
    * Purpose: shortcut method to create GIF file for expression,
    * with antialising and all other capabilities
    * --------------------------------------------------------------------------
    * Arguments: expression (I) char *ptr to null-terminated string
    * containing LaTeX expression to be rendred
    * gifFileName (I) char *ptr to null-terminated string
    * containing name of output gif file
    * --------------------------------------------------------------------------
    * Returns: ( int ) exit value from main (0 if successful)
    * --------------------------------------------------------------------------
    * Notes:     o This function is the entry point when mimeTeX is built
    * as a Win32 DLL rather then a standalone app or CGI
    *      o Contributed to mimeTeX by Shital Shah.  See his homepage
    *  http://www.shitalshah.com
    *      o Shital discusses the mimeTeX Win32 DLL project at
    *  http://www.codeproject.com/dotnet/Eq2Img.asp
    * and you can download his latest code from
    *  http://www.shitalshah.com/dev/eq2img_all.zip
    * ======================================================================= */
   /* --- include function to expose Win32 DLL to outside world --- */
   #if defined(_USRDLL)
     extern _declspec(dllexport)int _cdecl
    CreateGifFromEq ( char *expression, char *gifFileName );
   #endif
   /* --- entry point --- */
   int CreateGifFromEq ( char *expression, char *gifFileName )
   {
   /* -------------------------------------------------------------------------
   Allocations and Declarations
   -------------------------------------------------------------------------- */
   int main(); /* main() akways returns an int */
   /* --- set constants --- */
   int argc = 4; /* count of args supplied to main() */
   char *argv[5] = /* command line args to run with -e option */
     { "MimeTeXWin32DLL", "-e", /* constant args */
       /*gifFileName, expression,*/ NULL, NULL, NULL };
   /* --- set argv[]'s not computable at load time --- */
   argv[2] = gifFileName; /* args are -e gifFileName */
   argv[3] = expression; /* and now  -e gifFileName expression */
   /* -------------------------------------------------------------------------
   Run mimeTeX in command-line mode with -e (export) option, and then return
   -------------------------------------------------------------------------- */
   return main ( argc, argv
     #ifdef DUMPENVP
       , NULL
     #endif
    ) ;
   } /* --- end-of-function CreateGifFromEq() --- */
   
   /* ==========================================================================
  * Function: isstrstr ( char *string, char *snippets, int iscase )   * Function: isstrstr ( char *string, char *snippets, int iscase )
  * Purpose: determine whether any substring of 'string'   * Purpose: determine whether any substring of 'string'
  * matches any of the comma-separated list of 'snippets',   * matches any of the comma-separated list of 'snippets',
Line 10669  char *value = NULL;   /* getenv(name) to Line 12251  char *value = NULL;   /* getenv(name) to
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 Log each variable  Log each variable
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 fprintf(fp,"%s\n",timestamp()); /* emit timestamp before first var */  fprintf(fp,"%s\n",timestamp(TZDELTA,0)); /*emit timestamp before first var*/
 if ( message != NULL ) /* optional message supplied */  if ( message != NULL ) /* optional message supplied */
  fprintf(fp,"  MESSAGE = %s\n",message); /* emit caller-supplied message */   fprintf(fp,"  MESSAGE = %s\n",message); /* emit caller-supplied message */
 if ( logvars != (logdata *)NULL ) /* have logvars */  if ( logvars != (logdata *)NULL ) /* have logvars */
Line 10686  return ( nlogged );   /* back to caller Line 12268  return ( nlogged );   /* back to caller
 } /* --- end-of-function logger() --- */  } /* --- end-of-function logger() --- */
   
 /* ==========================================================================  /* ==========================================================================
  * Function: emitcache ( cachefile )   * Function: emitcache ( cachefile, maxage, isbuffer )
  * Purpose: dumps bytes from cachefile to stdout   * Purpose: dumps bytes from cachefile to stdout
  * --------------------------------------------------------------------------   * --------------------------------------------------------------------------
  * Arguments: cachefile (I) pointer to null-terminated char string   * Arguments: cachefile (I) pointer to null-terminated char string
  * containing full path to file to be dumped   * containing full path to file to be dumped,
    * or contains buffer of bytes to be dumped
    * maxage (I) int containing maxage. in seconds, for
    * http header, or -1 to not emit headers
    * isbuffer (I) 1 if cachefile is buffer of bytes to be
    * dumped
  * --------------------------------------------------------------------------   * --------------------------------------------------------------------------
  * Returns: ( int ) #bytes dumped (0 signals error)   * Returns: ( int ) #bytes dumped (0 signals error)
  * --------------------------------------------------------------------------   * --------------------------------------------------------------------------
  * Notes:     o   * Notes:     o
  * ======================================================================= */   * ======================================================================= */
 /* --- entry point --- */  /* --- entry point --- */
 int emitcache ( char *cachefile )  int emitcache ( char *cachefile, int maxage, int isbuffer )
 {  {
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 Allocations and Declarations  Allocations and Declarations
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 FILE *cacheptr = fopen(cachefile,"rb"), /*open cachefile for binary read*/  int nbytes=gifSize, readcachefile(); /* read cache file */
  *emitptr = stdout; /* emit cachefile to stdout */  FILE *emitptr = stdout; /* emit cachefile to stdout */
 unsigned char buffer[64]; /* characters from cachefile */  unsigned char buffer[64000]; /* bytes from cachefile */
 int buflen = 32, /* #bytes we try to read from cache*/  unsigned char *buffptr = buffer; /* ptr to buffer */
  nread = 0, /* #bytes actually read */  
  nbytes = 0; /* total #bytes emitted */  
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 initialization  initialization
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
 /* --- check that files opened okay --- */  /* --- check that files opened okay --- */
 if ( cacheptr == (FILE *)NULL /* failed to open cachefile */  if ( emitptr == (FILE *)NULL ) /* failed to open emit file */
 ||   emitptr == (FILE *)NULL ) /* or failed to open emit file */  
   goto end_of_job; /* so return 0 bytes to caller */    goto end_of_job; /* so return 0 bytes to caller */
 /* --- set stdout to binary mode (for Windows) --- */  /* --- read the file if necessary --- */
   if ( isbuffer ) /* cachefile is buffer */
    buffptr = (unsigned char *)cachefile; /* so reset buffer pointer */
   else /* cachefile is file name */
    if ( (nbytes = readcachefile(cachefile,buffer)) /* read the file */
    < 1 ) goto end_of_job; /* quit if file not read */
   /* --- first emit http headers if requested --- */
   if ( maxage >= 0 ) /* caller wants http headers */
    { /* --- emit mime content-type line --- */
      fprintf( emitptr, "Cache-Control: max-age=%d\n",maxage );
      fprintf( emitptr, "Content-Length: %d\n",nbytes );
      fprintf( emitptr, "Content-type: image/gif\n\n" ); }
   /* -------------------------------------------------------------------------
   set stdout to binary mode (for Windows)
   -------------------------------------------------------------------------- */
 /* emitptr = fdopen(STDOUT_FILENO,"wb"); */  /* doesn't work portably, */  /* emitptr = fdopen(STDOUT_FILENO,"wb"); */  /* doesn't work portably, */
 #ifdef WINDOWS /* so instead... */  #ifdef WINDOWS /* so instead... */
   #ifdef HAVE_SETMODE /* prefer (non-portable) setmode() */    #ifdef HAVE_SETMODE /* prefer (non-portable) setmode() */
Line 10732  if ( cacheptr == (FILE *)NULL  /* failed Line 12330  if ( cacheptr == (FILE *)NULL  /* failed
 /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
 emit bytes from cachefile  emit bytes from cachefile
 -------------------------------------------------------------------------- */  -------------------------------------------------------------------------- */
   /* --- write bytes to stdout --- */
   if ( fwrite(buffptr,sizeof(unsigned char),nbytes,emitptr) /* write buffer */
   <    nbytes ) /* failed to write all bytes */
     nbytes = 0; /* reset total count to 0 */
   end_of_job:
     return ( nbytes ); /* back with #bytes emitted */
   } /* --- end-of-function emitcache() --- */
   
   /* ==========================================================================
    * Function: readcachefile ( cachefile, buffer )
    * Purpose: read cachefile into buffer
    * --------------------------------------------------------------------------
    * Arguments: cachefile (I) pointer to null-terminated char string
    * containing full path to file to be read
    * buffer (O) pointer to unsigned char string
    * returning contents of cachefile
    * (max 64000 bytes)
    * --------------------------------------------------------------------------
    * Returns: ( int ) #bytes read (0 signals error)
    * --------------------------------------------------------------------------
    * Notes:     o
    * ======================================================================= */
   /* --- entry point --- */
   int readcachefile ( char *cachefile, unsigned char *buffer )
   {
   /* -------------------------------------------------------------------------
   Allocations and Declarations
   -------------------------------------------------------------------------- */
   FILE *cacheptr = fopen(cachefile,"rb"); /*open cachefile for binary read*/
   unsigned char cachebuff[64]; /* bytes from cachefile */
   int buflen = 32, /* #bytes we try to read from file */
    nread = 0, /* #bytes actually read from file */
    maxbytes = 64000, /* max #bytes returned in buffer */
    nbytes = 0; /* total #bytes read */
   /* -------------------------------------------------------------------------
   initialization
   -------------------------------------------------------------------------- */
   /* --- check that files opened okay --- */
   if ( cacheptr == (FILE *)NULL ) goto end_of_job; /*failed to open cachefile*/
   /* --- check that output buffer provided --- */
   if ( buffer == (unsigned char *)NULL ) goto end_of_job; /* no buffer */
   /* -------------------------------------------------------------------------
   read bytes from cachefile
   -------------------------------------------------------------------------- */
 while ( 1 )  while ( 1 )
   {    {
   /* --- read bytes from cachefile --- */    /* --- read bytes from cachefile --- */
   nread = fread(buffer,sizeof(unsigned char),buflen,cacheptr); /* read */    nread = fread(cachebuff,sizeof(unsigned char),buflen,cacheptr); /* read */
     if ( nbytes + nread > maxbytes ) /* block too big for buffer */
       nread = maxbytes - nbytes; /* so truncate it */
   if ( nread < 1 ) break; /* no bytes left in cachefile */    if ( nread < 1 ) break; /* no bytes left in cachefile */
   /* --- write bytes to stdout --- */    /* --- store bytes in buffer --- */
   if ( fwrite(buffer,sizeof(unsigned char),nread,emitptr) /* write buffer */    memcpy(buffer+nbytes,cachebuff,nread); /* copy current block to buffer */
   <    nread) /* failed to write all bytes */    /* --- ready to read next block --- */
     { nbytes = 0; /* reset total count to 0 */  
       goto end_of_job; } /* and signal error to caller */  
   nbytes += nread; /* bump total #bytes emitted */    nbytes += nread; /* bump total #bytes emitted */
   if ( nread < buflen ) break; /* no bytes left in cachefile */    if ( nread < buflen ) break; /* no bytes left in cachefile */
     if ( nbytes >= maxbytes ) break; /* avoid buffer overflow */
   } /* --- end-of-while(1) --- */    } /* --- end-of-while(1) --- */
 end_of_job:  end_of_job:
   if ( cacheptr != NULL ) fclose(cacheptr); /* close file if opened */    if ( cacheptr != NULL ) fclose(cacheptr); /* close file if opened */
   return ( nbytes ); /* back with #bytes emitted */    return ( nbytes ); /* back with #bytes emitted */
 } /* --- end-of-function emitcache() --- */  } /* --- end-of-function readcachefile() --- */
   
 /* ==========================================================================  /* ==========================================================================
  * Function: md5str ( instr )   * Function: md5str ( instr )

Removed from v.1.1  
changed lines
  Added in v.1.2


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