--- loncom/cgi/mimeTeX/mimetex.c 2007/10/09 21:41:41 1.3
+++ loncom/cgi/mimeTeX/mimetex.c 2012/06/09 00:58:11 1.5
@@ -1,10 +1,11 @@
/****************************************************************************
*
- * Copyright(c) 2002-2006, John Forkosh Associates, Inc. All rights reserved.
+ * Copyright(c) 2002-2012, John Forkosh Associates, Inc. All rights reserved.
+ * http://www.forkosh.com mailto: john@forkosh.com
* --------------------------------------------------------------------------
* This file is part of mimeTeX, which is free software. You may redistribute
* and/or modify it under the terms of the GNU General Public License,
- * version 2 or later, as published by the Free Software Foundation.
+ * version 3 or later, as published by the Free Software Foundation.
* MimeTeX is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY, not even the implied warranty of MERCHANTABILITY.
* See the GNU General Public License for specific details.
@@ -12,10 +13,11 @@
* agreed to these terms and conditions, and that you possess the legal
* right and ability to enter into this agreement and to use mimeTeX
* in accordance with it.
- * Your mimeTeX distribution should contain a copy of the GNU General
- * Public License. If not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA,
- * or point your browser to http://www.gnu.org/licenses/gpl.html
+ * Your mimetex.zip distribution file should contain the file COPYING,
+ * an ascii text copy of the GNU General Public License, version 3.
+ * If not, point your browser to http://www.gnu.org/licenses/
+ * or write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
* --------------------------------------------------------------------------
*
* Purpose: o MimeTeX, licensed under the gpl, lets you easily embed
@@ -25,25 +27,43 @@
* entirely separate little program that doesn't use TeX or
* its fonts in any way. It's just one cgi that you put in
* your site's cgi-bin/ directory, with no other dependencies.
- * So mimeTeX is very easy to install. And it's equally easy
- * to use. Just place an html tag in your document
+ * So mimeTeX is very easy to install. And it's equally
+ * easy to use. Just place an html tag in your document
* wherever you want to see the corresponding LaTeX expression.
* For example,
*
* immediately generates the corresponding gif image on-the-fly,
* displaying the rendered expression wherever you put that
- * tag. MimeTeX doesn't need intermediate dvi-to-gif
- * conversion, and it doesn't clutter up your filesystem with
- * separate little gif files for each converted expression.
+ * tag.
+ * MimeTeX doesn't need intermediate dvi-to-gif conversion,
+ * and it doesn't clutter up your filesystem with separate
+ * little gif files for each converted expression.
* But image caching is available by using mimeTeX's
* -DCACHEPATH=\"path/\" compile option (see below).
* There's also no inherent need to repeatedly write the
* cumbersome tag illustrated above. You can write
* your own custom tags, or write a wrapper script around
* mimeTeX to simplify the notation.
+ * Further discussion about mimeTeX's features and
+ * usage is available on its homepage,
+ * http://www.forkosh.com/mimetex.html
+ * and similarly in mimetex.html included with your mimetex.zip
+ * distribution file. (Note: http://www.forkosh.com/mimetex.html
+ * is a "quickstart" version of the the full mimetex.html manual
+ * included in your mimetex.zip distribution file.)
*
- * Functions: ===================== Raster Functions ======================
+ * Functions: The following "table of contents" lists each function
+ * comprising mimeTeX in the order it appears in this file.
+ * See individual function entry points for specific comments
+ * about its purpose, calling sequence, side effects, etc.
+ * (All these functions eventually belong in several
+ * different modules, possibly along the lines suggested
+ * by the divisions below. But until the best decomposition
+ * becomes clear, it seems better to keep mimetex.c
+ * neatly together, avoiding a bad decomposition that
+ * becomes permanent by default.)
+ * ===================== Raster Functions ======================
* PART2 --- raster constructor functions ---
* new_raster(width,height,pixsz) allocation (and constructor)
* new_subraster(width,height,pixsz)allocation (and constructor)
@@ -55,6 +75,8 @@
* rastcpy(rp) allocate new copy of rp
* subrastcpy(sp) allocate new copy of sp
* rastrot(rp) new raster rotated right 90 degrees to rp
+ * rastmag(rp,magstep) new raster magnified by "magstep" to rp
+ * bytemapmag(bytemap,width,height,magstep) magnify bytemap
* rastref(rp,axis) new raster reflected (axis 1=horz,2=vert)
* rastput(target,source,top,left,isopaque) overlay src on trgt
* rastcompose(sp1,sp2,offset2,isalign,isfree) sp2 on top of sp1
@@ -64,7 +86,7 @@
* rastsmash(sp1,sp2,xmin,ymin) calc #smash pixels sp1||sp2
* rastsmashcheck(term) check if term is "safe" to smash
* --- raster "drawing" functions ---
- * accent_subraster(accent,width,height) draw \hat\vec\etc
+ * accent_subraster(accent,width,height,direction,pixsz)\hat\vec
* arrow_subraster(width,height,drctn,isBig) left/right arrow
* uparrow_subraster(width,height,drctn,isBig) up/down arrow
* rule_raster(rp,top,left,width,height,type) draw rule in rp
@@ -109,8 +131,15 @@
* strchange(nfirst,from,to) change nfirst chars of from to to
* strreplace(string,from,to,nreplace) change from to to in str
* strwstr(string,substr,white,sublen) find substr in string
+ * strdetex(s,mode) replace math chars like \^_{} for display
* strtexchr(string,texchr) find texchr in string
* findbraces(expression,command) find opening { or closing }
+ * strpspn(s,reject,segment) non-() chars of s not in reject
+ * isstrstr(string,snippets,iscase) are any snippets in string?
+ * isnumeric(s) determine if s is an integer
+ * evalterm(store,term) evaluate numeric value of expression
+ * getstore(store,identifier)return value corresponding to ident
+ * unescape_url(url,isescape), x2c(what) xlate %xx url-encoded
* PART3 =========== Rasterize an Expression (recursively) ===========
* --- here's the primary entry point for all of mimeTeX ---
* rasterize(expression,size) parse and rasterize expression
@@ -144,12 +173,16 @@
* rastbezier(expression,size,basesp,arg1,arg2,arg3) \bezier
* rastraise(expression,size,basesp,arg1,arg2,arg3) \raisebox
* rastrotate(expression,size,basesp,arg1,arg2,arg3) \rotatebox
+ * rastmagnify(expression,size,basesp,arg1,arg2,arg3) \magnify
* rastreflect(expression,size,basesp,arg1,arg2,arg3)\reflectbox
* rastfbox(expression,size,basesp,arg1,arg2,arg3) \fbox
* rastinput(expression,size,basesp,arg1,arg2,arg3) \input
* rastcounter(expression,size,basesp,arg1,arg2,arg3) \counter
+ * rasteval(expression,size,basesp,arg1,arg2,arg3) \eval
* rasttoday(expression,size,basesp,arg1,arg2,arg3) \today
* rastcalendar(expression,size,basesp,arg1,arg2,arg3) \calendar
+ * rastenviron(expression,size,basesp,arg1,arg2,arg3) \environ
+ * rastmessage(expression,size,basesp,arg1,arg2,arg3) \message
* rastnoop(expression,size,basesp,arg1,arg2,arg3) flush \escape
* --- helper functions for handlers ---
* rastopenfile(filename,mode) opens filename[.tex] in mode
@@ -160,6 +193,10 @@
* timestamp(tzdelta,ifmt) formats timestamp string
* tzadjust(tzdelta,year,month,day,hour) adjust date/time
* daynumber(year,month,day) #days since Monday, Jan 1, 1973
+ * strwrap(s,linelen,tablen)insert \n's and spaces to wrap lines
+ * strnlower(s,n) lowercase the first n chars of string s
+ * urlprune(url,n) http://abc.def.ghi.com/etc-->abc.def.ghi.com
+ * urlncmp(url1,url2,n) compares topmost n levels of two url's
* dbltoa(d,npts) double to comma-separated ascii
* === Anti-alias completed raster (lowpass) or symbols (ss) ===
* aalowpass(rp,bytemap,grayscale) lowpass grayscale bytemap
@@ -184,20 +221,23 @@
* PART1 ========================== Driver ===========================
* 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?
* ismonth(month) is month current month ("jan"-"dec")?
- * unescape_url(url,isescape), x2c(what) xlate %xx url-encoded
* logger(fp,msglevel,logvars) logs environment variables
- * emitcache(cachefile,maxage,isbuffer) emit cachefile to stdout
+ * emitcache(cachefile,maxage,valign,isbuffer) emit cachefile
* readcachefile(cachefile,buffer) read cachefile into buffer
+ * advertisement(expression,mode) wrap expression in ad message
+ * crc16(s) 16-bit crc of string s
* md5str(instr) md5 hash library functions
* GetPixel(x,y) callback function for gifsave library
*
* Source: mimetex.c (needs mimetex.h and texfonts.h to compile,
- * and also needs gifsave.c if compiled with -DAA or -DGIF)
+ * and also needs gifsave.c when compiled with -DAA or -DGIF)
*
* --------------------------------------------------------------------------
- * Notes o See bottom of file for main() driver (and "friends"),
+ * Notes o See individual function entry points for specific comments
+ * about the purpose, calling sequence, side effects, etc
+ * of each mimeTeX function listed above.
+ * o See bottom of file for main() driver (and "friends"),
* and compile as
* cc -DAA mimetex.c gifsave.c -lm -o mimetex.cgi
* to produce an executable that emits gif images with
@@ -209,14 +249,39 @@
* to produce an executable that just emits mime xbitmaps.
* In either case you'll need mimetex.h and texfonts.h,
* and with -DAA or -DGIF you'll also need gifsave.c
+ * o The font information in texfonts.h was produced by multiple
+ * runs of gfuntype, one run per struct (i.e., one run per font
+ * family at a particular size). Compile gfuntype as
+ * cc gfuntype.c mimetex.c -lm -o gfuntype
+ * See gfuntype.c, and also mimetex.html#fonts, for details.
* o For gif images, the gifsave.c library by Sverre H. Huseby
* slightly modified by me to allow
- * (a)sending output to stdout and (b)specifying a transparent
- * background color index, is included with mimeTeX,
- * and it's documented in mimetex.html#gifsave .
+ * (a)sending output to stdout or returning it in memory,
+ * and (b)specifying a transparent background color index,
+ * is included with mimeTeX, and it's documented in
+ * mimetex.html#gifsave
+ * o MimeTeX's principal reusable function is rasterize(),
+ * which takes a string like "f(x)=\int_{-\infty}^xe^{-t^2}dt"
+ * and returns a (sub)raster representing it as a bit or bytemap.
+ * Your application can do anything it likes with this pixel map.
+ * MimeTeX just outputs it, either as a mime xbitmap or as a gif.
+ * See mimetex.html#makeraster for further discussion
+ * and examples.
+ * o File mimetex.c also contains library functions implementing
+ * a raster datatype, functions to manipulate rasterized .mf
+ * fonts (see gfuntype.c which rasterizes .mf fonts), functions
+ * to parse LaTeX expressions, etc. As already mentioned,
+ * a complete list of mimetex.c functions is above. See their
+ * individual entry points below for further comments.
+ * As also mentioned, these functions eventually belong in
+ * several different modules, possibly along the lines suggested
+ * by the divisions above. But until the best decomposition
+ * becomes clear, it seems better to keep mimetex.c
+ * neatly together, avoiding a bad decomposition that
+ * becomes permanent by default.
* o Optional compile-line -D defined symbols are documented
* in mimetex.html#options . They include (additional -D
- * switches are discussed in mimetex.html#options)...
+ * switches are discussed at mimetex.html#options)...
* -DAA
* Turns on gif anti-aliasing with default values
* (CENTERWT=32, ADJACENTWT=3, CORNERWT=1)
@@ -224,6 +289,9 @@
* -DCENTERWT=n
* -DADJACENTWT=j
* -DCORNERWT=k
+ * *** Note: Ignore these three switches because
+ * *** mimeTeX's current anti-aliasing algorithm
+ * *** no longer uses them (as of version 1.60).
* MimeTeX currently provides a lowpass filtering
* algorithm for anti-aliasing, which is applied to the
* existing set of bitmap fonts. This lowpass filter
@@ -253,6 +321,12 @@
* be writable by it. Files created under path/ are
* named filename.gif, where filename is the 32-character
* MD5 hash of the LaTeX expression.
+ * -DDEFAULTSIZE=n
+ * MimeTeX currently has eight font sizes numbered 0-7,
+ * and always starts in DEFAULTSIZE whose default value
+ * is 3 (corresponding to \large). Specify -DDEFAULTSIZE=4
+ * on the compile line if you prefer mimeTeX to start in
+ * larger default size 4 (corresponding to \Large), etc.
* -DDISPLAYSIZE=n
* By default, operator limits like \int_a^b are rendered
* \textstyle at font sizes \normalsize and smaller,
@@ -264,11 +338,15 @@
* \textstyle, \displaystyle, \limits or \nolimits
* directives in an expression always override
* the DISPLAYSIZE default.
- * -NORMALSIZE=n
- * MimeTeX currently has six font sizes numbered 0-5,
- * and always starts in NORMALSIZE whose default value
- * is 2. Specify -DNORMALSIZE=3 on the compile line if
- * you prefer mimeTeX to start in default size 3, etc.
+ * -DERRORSTATUS=n
+ * The default, 0, means mimeTeX always exits with status 0,
+ * regardless of whether or not it detects error(s) while
+ * trying to render your expression. Specify any non-zero
+ * value (typically -1) if you write a script/plugin for
+ * mimeTeX that traps non-zero exit statuses. MimeTeX then
+ * exits with its own non-zero status when it detects an
+ * error it can identify, or with your ERRORSTATUS value
+ * for errors it can't specifically identify.
* -DREFERER=\"domain\" -or-
* -DREFERER=\"domain1,domain2,etc\"
* Blocks mimeTeX requests from unauthorized domains that
@@ -299,28 +377,6 @@
* MimeTeX usually renders black symbols on a white
* background. This option renders white symbols on
* a black background instead.
- * o See individual function entry points for further comments.
- * o The font information in texfonts.h was produced by multiple
- * runs of gfuntype, one run per struct (i.e., one run per font
- * family at a particular size). See gfuntype.c, and also
- * mimetex.html#fonts, for details.
- * o mimetex.c contains library functions implementing a raster
- * datatype, functions to manipulate rasterized .mf fonts
- * (see gfuntype.c which rasterizes .mf fonts), functions
- * to parse LaTeX expressions, etc. A complete list of
- * mimetex.c functions is above. See their individual entry
- * points below for further comments.
- * All these functions eventually belong in several
- * different modules, possibly along the lines suggested
- * by the divisions above. But until the best decomposition
- * becomes clear, it seems better to keep mimetex.c
- * neatly together, avoiding a bad decomposition that
- * becomes permanent by default.
- * o The "main" reusable function is rasterize(),
- * which takes a string like "f(x)=\int_{-\infty}^xe^{-t^2}dt"
- * and returns a (sub)raster representing it as a bit or bytemap.
- * Your application can do anything it likes with this pixel map.
- * MimeTeX just outputs it, either as a mime xbitmap or as a gif.
* --------------------------------------------------------------------------
* Revision History:
* 09/18/02 J.Forkosh Installation.
@@ -332,11 +388,25 @@
* 10/02/04 J.Forkosh Version 1.50 released.
* 11/30/04 J.Forkosh Version 1.60 released.
* 10/11/05 J.Forkosh Version 1.64 released.
- * 11/30/06 J.Forkosh most recent changes
+ * 11/30/06 J.Forkosh Version 1.65 released.
+ * 09/06/08 J.Forkosh Version 1.70 released.
+ * 03/23/09 J.Forkosh Version 1.71 released.
+ * 11/18/09 J.Forkosh Version 1.72 released.
+ * 11/15/11 J.Forkosh Version 1.73 released.
+ * 02/15/12 J.Forkosh Version 1.74 released.
+ * 03/31/12 J.Forkosh Most recent revision (also see REVISIONDATE)
+ * See http://www.forkosh.com/mimetexchangelog.html for further details.
*
****************************************************************************/
/* -------------------------------------------------------------------------
+Program id
+-------------------------------------------------------------------------- */
+#define VERSION "1.74" /* mimeTeX version number */
+#define REVISIONDATE "31 March 2012" /* date of most recent revision */
+#define COPYRIGHTTEXT "Copyright(c) 2002-2012, John Forkosh Associates, Inc"
+
+/* -------------------------------------------------------------------------
header files and macros
-------------------------------------------------------------------------- */
/* --- standard headers --- */
@@ -347,8 +417,48 @@ header files and macros
#include
#include
#include
+extern char **environ; /* for \environment directive */
+
+/* -------------------------------------------------------------------------
+messages (used mostly by main() and also by rastmessage())
+-------------------------------------------------------------------------- */
+static char *copyright1 = /* copyright, gnu/gpl notice */
+ "+-----------------------------------------------------------------------+\n"
+ "|mimeTeX vers " VERSION ", " COPYRIGHTTEXT "|\n"
+ "+-----------------------------------------------------------------------+\n"
+ "| mimeTeX is free software, licensed to you under terms of the GNU/GPL, |\n"
+ "| and comes with absolutely no warranty whatsoever. |",
+*copyright2 =
+ "| See http://www.forkosh.com/mimetex.html for details. |\n"
+ "+-----------------------------------------------------------------------+";
+static int maxmsgnum = 3, /* maximum msgtable[] index */
+ /* --- keep these message numbers updated if table changes --- */
+ invmsgnum = 0, /* general invalid message */
+ refmsgnum = 3; /* urlncmp() failed to validate */
+static char *msgtable[] = { /* messages referenced by [index] */
+ "\\red\\small\\rm\\fbox{\\array{" /* [0] is invalid_referer_msg */
+ "Please~read~www.forkosh.com/mimetex.html\\\\and~install~mimetex.cgi~"
+ "on~your~own~server.\\\\Thank~you,~John~Forkosh}}",
+ "\\red\\small\\rm\\fbox{\\array{" /* [1] */
+ "Please~provide~your~{\\tiny~HTTP-REFERER}~to~access~the~public\\\\"
+ "mimetex~server.~~Or~please~read~~www.forkosh.com/mimetex.html\\\\"
+ "and~install~mimetex.cgi~on~your~own~server.~~Thank~you,~John~Forkosh}}",
+ "\\red\\small\\rm\\fbox{\\array{" /* [2] */
+ "The~public~mimetex~server~is~for~testing.~~For~production,\\\\"
+ "please~read~~www.forkosh.com/mimetex.html~~and~install\\\\"
+ "mimetex.cgi~on~your~own~server.~~Thank~you,~John~Forkosh}}",
+ "\\red\\small\\rm\\fbox{\\array{" /* [3] */
+ "Only~SERVER_NAME~may~use~mimetex~on~this~server.\\\\"
+ "Please~read~~www.forkosh.com/mimetex.html~~and~install\\\\"
+ "mimetex.cgi~on~your~own~server.~~Thank~you,~John~Forkosh}}",
+ NULL } ; /* trailer */
-/* --- windows-specific header info --- */
+/* -------------------------------------------------------------------------
+additional symbols
+-------------------------------------------------------------------------- */
+/* ---
+ * windows-specific header info
+ * ---------------------------- */
#ifndef WINDOWS /* -DWINDOWS not supplied by user */
#if defined(_WINDOWS) || defined(_WIN32) || defined(WIN32) \
|| defined(DJGPP) /* try to recognize windows compilers */ \
@@ -378,7 +488,9 @@ header files and macros
#define ISWINDOWS 0
#endif
-/* --- check for supersampling or low-pass anti-aliasing --- */
+/* ---
+ * check for supersampling or low-pass anti-aliasing
+ * ------------------------------------------------- */
#ifdef SS
#define ISSUPERSAMPLING 1
#ifndef AAALGORITHM
@@ -400,7 +512,9 @@ header files and macros
#define MAXFOLLOW 8 /* aafollowline() maxturn default */
#endif
-/* --- set aa (and default gif) if any anti-aliasing options specified --- */
+/* ---
+ * set aa (and default gif) if any anti-aliasing options specified
+ * --------------------------------------------------------------- */
#if defined(AA) || defined(GIF) || defined(PNG) \
|| defined(CENTERWT) || defined(ADJACENTWT) || defined(CORNERWT) \
|| defined(MINADJACENT) || defined(MAXADJACENT)
@@ -424,29 +538,29 @@ header files and macros
#endif
#endif
-/* --- decide whether to compile main() --- */
+/* ---
+ * decide whether or not to compile main()
+ * --------------------------------------- */
#if defined(XBITMAP) || defined(GIF) || defined(PNG)
- #define DRIVER /* driver will be compiled */
- /* --- check whether or not to perform http_referer check --- */
- #ifndef REFERER /* all http_referer's allowed */
- #define REFERER NULL
- #endif
- /* --- max query_string length if no http_referer supplied --- */
- #ifndef NOREFMAXLEN
- #define NOREFMAXLEN 9999 /* default to any length query */
- #endif
-#else
+ /* --- yes, compile main() --- */
+ #define DRIVER /* main() driver will be compiled */
+#else /* --- main() won't be compiled (e.g., for gfuntype.c) --- */
#ifndef TEXFONTS
#define NOTEXFONTS /* texfonts not required */
#endif
#endif
-/* --- application headers --- */
+/* ---
+ * application headers
+ * ------------------- */
#if !defined(NOTEXFONTS) && !defined(TEXFONTS)
#define TEXFONTS /* to include texfonts.h */
#endif
#include "mimetex.h"
-/* --- info needed when gif image returned in memory buffer --- */
+
+/* ---
+ * info needed when gif image returned in memory buffer
+ * ---------------------------------------------------- */
#ifdef GIF /* compiling along with gifsave.c */
extern int gifSize;
extern int maxgifSize;
@@ -466,7 +580,10 @@ header files and macros
#else
#define ISTRANSPARENT 0
#endif
-/* --- internal buffer sizes --- */
+
+/* ---
+ * internal buffer sizes
+ * --------------------- */
#if !defined(MAXEXPRSZ)
#define MAXEXPRSZ (32768-1) /*max #bytes in input tex expression*/
#endif
@@ -489,7 +606,9 @@ header files and macros
/* -------------------------------------------------------------------------
adjustable default values
-------------------------------------------------------------------------- */
-/* --- anti-aliasing parameters --- */
+/* ---
+ * anti-aliasing parameters
+ * ------------------------ */
#ifndef CENTERWT
/*#define CENTERWT 32*/ /* anti-aliasing centerwt default */
/*#define CENTERWT 10*/ /* anti-aliasing centerwt default */
@@ -564,6 +683,14 @@ other variables
#ifndef FGBLUE
#define FGBLUE (ISBLACKONWHITE?0:255)
#endif
+/* --- advertisement
+ one image in every ADFREQUENCY is wrapped in "advertisement" --- */
+#if !defined(ADFREQUENCY)
+ #define ADFREQUENCY 0 /* never show advertisement if 0 */
+#endif
+#ifndef HOST_SHOWAD
+ #define HOST_SHOWAD "\000" /* show ads on all hosts */
+#endif
/* --- "smash" margin (0 means no smashing) --- */
#ifndef SMASHMARGIN
#ifdef NOSMASH
@@ -605,6 +732,72 @@ other variables
#if !defined(NODUMPENVP) && !defined(DUMPENVP)
#define DUMPENVP /* assume char *envp[] available */
#endif
+/* --- max query_string length if no http_referer supplied --- */
+#ifndef NOREFMAXLEN
+ #define NOREFMAXLEN 9999 /* default to any length query */
+#endif
+#ifndef NOREFSAFELEN
+ #define NOREFSAFELEN 24 /* too small for hack exploit */
+#endif
+/* --- check whether or not to perform http_referer check --- */
+#ifdef REFERER /* only specified referers allowed */
+ #undef NOREFMAXLEN
+ #define NOREFMAXLEN NOREFSAFELEN
+#else /* all http_referer's allowed */
+ #define REFERER NULL
+#endif
+/* --- check top levels of http_referer against server_name --- */
+#ifdef REFLEVELS /* #topmost levels to check */
+ #undef NOREFMAXLEN
+ #define NOREFMAXLEN NOREFSAFELEN
+#else
+ #ifdef NOREFCHECK
+ #define REFLEVELS 0 /* don't match host and referer */
+ #else
+ #define REFLEVELS 3 /* default matches abc.def.com */
+ #endif
+#endif
+/* --- check whether or not \input, \counter, \environment permitted --- */
+#ifdef DEFAULTSECURITY /* default security specified */
+ #define EXPLICITDEFSECURITY /* don't override explicit default */
+#else /* defualt security not specified */
+ #define DEFAULTSECURITY (8) /* so set default security level */
+#endif
+#ifdef INPUTREFERER /*http_referer's permitted to \input*/
+ #ifndef INPUTSECURITY /* so we need to permit \input{} */
+ #define INPUTSECURITY (99999) /* make sure SECURITY=*/
GLOBAL(int,shrinkfactor,3); /* shrinkfactors[fontsize] */
+GLOBAL(int,rastlift,0); /* rastraise() lift parameter */
+GLOBAL(int,rastlift1,0); /* rastraise() lift for base exprssn*/
GLOBAL(double,unitlength,1.0); /* #pixels per unit (may be <1.0) */
+GLOBAL(int,iunitlength,1); /* #pixels per unit as int for store*/
/*GLOBAL(int,textwidth,TEXTWIDTH);*/ /* #pixels across line */
+GLOBAL(int,adfrequency,ADFREQUENCY); /* advertisement frequency */
GLOBAL(int,isnocatspace,0); /* >0 to not add space in rastcat()*/
GLOBAL(int,smashmargin,SMASHMARGIN); /* minimum "smash" margin */
GLOBAL(int,mathsmashmargin,SMASHMARGIN); /* needed for \text{if $n-m$ even}*/
GLOBAL(int,issmashdelta,1); /* true if smashmargin is a delta */
GLOBAL(int,isexplicitsmash,0); /* true if \smash explicitly given */
GLOBAL(int,smashcheck,SMASHCHECK); /* check if terms safe to smash */
+GLOBAL(int,isnomath,0); /* true to inhibit math mode */
GLOBAL(int,isscripted,0); /* is (lefthand) term text-scripted*/
GLOBAL(int,isdelimscript,0); /* is \right delim text-scripted */
GLOBAL(int,issmashokay,0); /*is leading char okay for smashing*/
@@ -716,12 +924,33 @@ GLOBAL(int,fraccenterline,NOVALUE); /* b
/*GLOBAL(int,currentcharclass,NOVALUE);*/ /*primarily to check for PUNCTION*/
GLOBAL(int,iscaching,ISCACHING); /* true if caching images */
GLOBAL(char,cachepath[256],CACHEPATH); /* relative path to cached files */
+GLOBAL(int,isemitcontenttype,1); /* true to emit mime content-type */
+int iscachecontenttype = 0; /* true to cache mime content-type */
+char contenttype[2048] = "\000"; /* content-type:, etc buffer */
GLOBAL(char,pathprefix[256],PATHPREFIX); /*prefix for \input,\counter paths*/
/*GLOBAL(int,iswindows,ISWINDOWS);*/ /* true if compiled for ms windows */
/* -------------------------------------------------------------------------
+store for evalterm() [n.b., these are stripped-down funcs from nutshell]
+-------------------------------------------------------------------------- */
+#define STORE struct store_struct /* "typedef" for store struct */
+#define MAXSTORE 100 /* max 100 identifiers */
+STORE {
+ char *identifier; /* identifier */
+ int *value; /* address of corresponding value */
+ } ; /* --- end-of-store_struct --- */
+static STORE mimestore[MAXSTORE] = {
+ { "fontsize", &fontsize }, { "fs", &fontsize }, /* font size */
+ { "fontnum", &fontnum }, { "fn", &fontnum }, /* font number */
+ { "unitlength", &iunitlength }, /* unitlength */
+ /*{ "mytestvar", &mytestvar },*/
+ { NULL, NULL } /* end-of-store */
+ } ; /* --- end-of-mimestore[] --- */
+
+/* -------------------------------------------------------------------------
miscellaneous macros
-------------------------------------------------------------------------- */
+#if 0 /* --- these are now #define'd in mimetex.h --- */
#define max2(x,y) ((x)>(y)? (x):(y)) /* larger of 2 arguments */
#define min2(x,y) ((x)<(y)? (x):(y)) /* smaller of 2 arguments */
#define max3(x,y,z) max2(max2(x,y),(z)) /* largest of 3 arguments */
@@ -729,13 +958,47 @@ miscellaneous macros
#define absval(x) ((x)>=0?(x):(-(x))) /* absolute value */
#define iround(x) ((int)((x)>=0?(x)+0.5:(x)-0.5)) /* round double to int */
#define dmod(x,y) ((x)-((y)*((double)((int)((x)/(y)))))) /*x%y for doubles*/
+#endif
#define compress(s,c) if((s)!=NULL) /* remove embedded c's from s */ \
- { char *p; while((p=strchr((s),(c)))!=NULL) strcpy(p,p+1); } else
+ { char *p; while((p=strchr((s),(c)))!=NULL) {strsqueeze(p,1);} } else
#define slower(s) if ((s)!=NULL) /* lowercase all chars in s */ \
{ char *p=(s); while(*p!='\000'){*p=tolower(*p); p++;} } else
/*subraster *subrastcpy();*/ /* need global module declaration */
-/*#define spnosmash(sp) if (sp->type==CHARASTER) sp=subrastcpy(sp); \*/
-/* sp->type=blanksignal*/
+/*#define spnosmash(sp) if (sp->type==CHARASTER) sp=subrastcpy(sp); \ */
+/* sp->type=blanksignal */
+/* ---evaluate \directive[arg] or \directive{arg} scaled by unitlength--- */
+#define eround(arg) (iround(unitlength*((double)evalterm(mimestore,(arg)))))
+/* --- check if a string is empty --- */
+#define isempty(s) ((s)==NULL?1:(*(s)=='\000'?1:0))
+/* --- last char of a string --- */
+#define lastchar(s) (isempty(s)?'\000':*((s)+(strlen(s)-1)))
+/* --- lowercase a string --- */
+#define strlower(s) strnlower((s),0) /* lowercase an entire string */
+/* --- strip leading and trailing whitespace (including ~) --- */
+#define trimwhite(thisstr) if ( (thisstr) != NULL ) { \
+ int thislen = strlen(thisstr); \
+ while ( --thislen >= 0 ) \
+ if ( isthischar((thisstr)[thislen]," \t\n\r\f\v") ) \
+ (thisstr)[thislen] = '\000'; \
+ else break; \
+ if ( (thislen = strspn((thisstr)," \t\n\r\f\v")) > 0 ) \
+ {strsqueeze((thisstr),thislen);} } else
+/* --- strncpy() n bytes and make sure it's null-terminated --- */
+#define strninit(target,source,n) if( (target)!=NULL && (n)>=0 ) { \
+ char *thissource = (source); \
+ (target)[0] = '\000'; \
+ if ( (n)>0 && thissource!=NULL ) { \
+ strncpy((target),thissource,(n)); \
+ (target)[(n)] = '\000'; } }
+/* --- strcpy(s,s+n) using memmove() (also works for negative n) --- */
+#define strsqueeze(s,n) if((n)!=0) { if(!isempty((s))) { \
+ int thislen3=strlen(s); \
+ if ((n) >= thislen3) *(s) = '\000'; \
+ else memmove(s,s+(n),1+thislen3-(n)); }} else/*user supplies final;*/
+/* --- strsqueeze(s,t) with two pointers --- */
+#define strsqueezep(s,t) if(!isempty((s))&&!isempty((t))) { \
+ int sqlen=strlen((s))-strlen((t)); \
+ if (sqlen>0 && sqlen<=999) {strsqueeze((s),sqlen);} } else
/* ---
* PART2
@@ -1127,6 +1390,178 @@ return ( rotated ); /* return rotated
/* ==========================================================================
+ * Function: rastmag ( rp, magstep )
+ * Purpose: magnifies rp by integer magstep,
+ * e.g., double-height and double-width if magstep=2
+ * --------------------------------------------------------------------------
+ * Arguments: rp (I) ptr to raster struct to be "magnified"
+ * magstep (I) int containing magnification scale,
+ * e.g., 2 to double the width and height of rp
+ * --------------------------------------------------------------------------
+ * Returns: ( raster * ) ptr to new raster magnified relative to rp,
+ * or NULL for any error.
+ * --------------------------------------------------------------------------
+ * Notes: o
+ * ======================================================================= */
+/* --- entry point --- */
+raster *rastmag ( raster *rp, int magstep )
+{
+/* -------------------------------------------------------------------------
+Allocations and Declarations
+-------------------------------------------------------------------------- */
+raster *new_raster(), *magnified=NULL; /* magnified raster back to caller */
+int height = rp->height, irow, /* height, row index */
+ width = rp->width, icol, /* width, column index */
+ mrow = 0, mcol = 0, /* dup pixels magstep*magstep times*/
+ pixsz = rp->pixsz; /* #bits per pixel */
+/* -------------------------------------------------------------------------
+check args
+-------------------------------------------------------------------------- */
+if ( rp == NULL ) goto end_of_job; /* no input raster supplied */
+if ( magstep<1 || magstep>10 ) goto end_of_job; /* sanity check */
+/* -------------------------------------------------------------------------
+allocate magnified raster and fill it
+-------------------------------------------------------------------------- */
+/* --- allocate magnified raster with magstep*width, magstep*height --- */
+if ( (magnified = new_raster(magstep*width,magstep*height,pixsz))/*allocate*/
+!= NULL ) /* check that allocation succeeded */
+ /* --- fill reflected raster --- */
+ for ( irow=0; irow100000 ) goto end_of_job; /* sanity check */
+if ( magstep<1 || magstep>10 ) goto end_of_job; /* sanity check */
+/* -------------------------------------------------------------------------
+allocate magnified bytemap and fill it
+-------------------------------------------------------------------------- */
+/* --- allocate bytemap for magstep*width, magstep*height --- */
+if ( (magnified = (intbyte *)(malloc(magstep*width*magstep*height)))/*alloc*/
+!= NULL ) /* check that allocation succeeded */
+ /* --- fill reflected raster --- */
+ for ( irow=0; irow0?(int)(bytemap[imap-1]):byteval), /*left of center*/
+ icell[6]= (icol0?(int)(bytemap[imap-width]):byteval),/*above center*/
+ icell[8]= (irow0&&icol>0?(int)(bytemap[imap-width-1]):byteval),
+ icell[3]= (irow>0&&icol0?(int)(bytemap[imap+width-1]):byteval),
+ icell[9]=(irow=h or b<0, for
+ * an image additionally lifted (b>=h) or lowered (b<0)
+ * with respect to the surrounding expression.
+ * o Note that b=h-1 means no descenders and the bottom
+ * of the symbol rests exactly on the baseline,
+ * whereas b=0 means the top pixel of the symbol rests
+ * on the baseline, and all other pixels are descenders.
+ * o The composite raster is constructed as follows...
+ * The base image is labelled height h1 and baseline b1,
+ * the overlay h2 and b2, and the composite H and B.
+ * base overlay
+ * --- +------------------------+ --- For the overlay to be
+ * ^ | ^ +----------+| ^ vertically centered with
+ * | | | | || | respect to the base,
+ * | | |B-b1 | || | B - b1 = H-B -(h1-b1), so
+ * | | v | || | 2*B = H-h1 + 2*b1
+ * | |+----------+| || | B = b1 + (H-h1)/2
+ * B || ^ ^ || || | And when the base image is
+ * | || | | || || | bigger, H=h1 and B=b1 is
+ * | || b1 | || || | the obvious correct answer.
+ * | || | h1 || || H=h2
+ * v || v | || || |
+ * ----------||-------|--|| ||--|--------
+ * baseline || h1-b1 v || overlay || |
+ * for base |+----------+| baseline || |
+ * and com- | ^ | ignored || |
+ * posite | |H-B- |----------|| |
+ * | | (h1-b1)| || |
+ * | v +----------+| v
+ * +------------------------+ ---
* ======================================================================= */
/* --- entry point --- */
subraster *rastcompose ( subraster *sp1, subraster *sp2, int offset2,
@@ -1275,20 +1746,39 @@ int base1 = sp1->baseline, /*baseline
height2 = (sp2->image)->height, /* height for overlaid subraster */
width2 = (sp2->image)->width, /* width for overlaid subraster */
pixsz2 = (sp2->image)->pixsz; /* pixsz for overlaid subraster */
-int height=0, width=0, pixsz=0, base=0; /* overlaid composite */
+int height = max2(height1,height2), /*composite height if sp2 centered*/
+ base = base1 + (height-height1)/2, /* and composite baseline */
+ tlc2 = (height-height2)/2, /* top-left corner for overlay */
+ width=0, pixsz=0; /* other params for composite */
+int lift1 = rastlift1, /* vertical \raisebox lift for sp1 */
+ lift2 = rastlift; /* vertical \raisebox lift for sp2 */
/* -------------------------------------------------------------------------
Initialization
-------------------------------------------------------------------------- */
/* --- determine height, width and baseline of composite raster --- */
-if ( isalign ) /* baselines of sp1,sp2 aligned */
- { height = max2(base1+1,base2+1) /* max height above baseline */
- + max2(height1-base1-1,height2-base2-1); /*+ max descending below*/
- base = max2(base1,base2); } /* max space above baseline */
-else /* baselines not aligned */
- { height = max2(height1,height2); /* max height */
- base = base1 + (height-height1)/2; } /* baseline for sp1 */
-width = max2(width1,width2+abs(offset2)); /* max width */
-pixsz = max2(pixsz1,pixsz2); /* bitmap,bytemap becomes bytemap */
+switch ( isalign ) {
+ default:
+ case 0: /* centered, baselines not aligned */
+ height = max2(height1,height2); /* max height */
+ base = base1 + (height-height1)/2; /* baseline for sp1 */
+ break;
+ case 1: /* baselines of sp1,sp2 aligned */
+ height = max2(base1+1,base2+1) /* max height above baseline */
+ + max2(height1-base1-1,height2-base2-1); /*+max descending below*/
+ base = max2(base1,base2); /* max space above baseline */
+ break;
+ case 2: /* centered +/- \raisebox lifts */
+ base1 -= lift1; base2 -= lift2; /* reset to unlifted images */
+ /* --- start with default for centered, unlifted images --- */
+ height2 += 2*absval(lift2); /* "virtual" height of overlay */
+ height = max2(height1,height2); /* max height */
+ base = base1 + (height-height1)/2; /* baseline for sp1 */
+ tlc2 = (height-height2)/2 /* top-left corner for overlay */
+ + (lift2>=0?0:2*absval(lift2)); /* "reflect" overlay below base */
+ break;
+ } /* --- end-of-switch(isalign) --- */
+width = max2(width1,width2+abs(offset2)); /* max width */
+pixsz = max2(pixsz1,pixsz2); /* bitmap,bytemap becomes bytemap */
/* -------------------------------------------------------------------------
allocate concatted composite subraster
-------------------------------------------------------------------------- */
@@ -1299,19 +1789,29 @@ if ( (sp=new_subraster(width,height,pixs
sp->type = IMAGERASTER; /* image */
sp->baseline = base; /* composite baseline */
sp->size = sp1->size; /* underlying char is sp1 */
+if ( isalign == 2 ) sp->baseline += lift1; /* adjust baseline */
/* --- extract raster from subraster --- */
rp = sp->image; /* raster allocated in subraster */
/* -------------------------------------------------------------------------
overlay sp1 and sp2 in new composite raster
-------------------------------------------------------------------------- */
-if ( isalign )
- { rastput (rp, sp1->image, base-base1, (width-width1)/2, 1); /*underlying*/
- rastput (rp, sp2->image, base-base2, /*overlaid*/
- (width-width2)/2+offset2, 0); }
-else
- { rastput (rp, sp1->image, base-base1, (width-width1)/2, 1); /*underlying*/
+switch ( isalign ) {
+ default:
+ case 0: /* centered, baselines not aligned */
+ rastput (rp, sp1->image, base-base1, (width-width1)/2, 1); /*underlying*/
rastput (rp, sp2->image, (height-height2)/2, /*overlaid*/
- (width-width2)/2+offset2, 0); }
+ (width-width2)/2+offset2, 0);
+ break;
+ case 1: /* baselines of sp1,sp2 aligned */
+ rastput (rp, sp1->image, base-base1, (width-width1)/2, 1); /*underlying*/
+ rastput (rp, sp2->image, base-base2, /*overlaid*/
+ (width-width2)/2+offset2, 0);
+ break;
+ case 2: if(1){ /* centered +/- \raisebox lifts */
+ rastput (rp, sp1->image, base-base1, (width-width1)/2, 1);
+ rastput (rp, sp2->image, tlc2, (width-width2)/2+offset2, 0); }
+ break;
+ } /* --- end-of-switch(isalign) --- */
/* -------------------------------------------------------------------------
free input if requested
-------------------------------------------------------------------------- */
@@ -1834,16 +2334,16 @@ if ( smashcheck < 1 ) { /* no smash che
skip leading white and gray space
-------------------------------------------------------------------------- */
/* --- first check input --- */
-if ( term == NULL ) goto end_of_job; /* no input so return 0 to caller */
-if ( *term == '\000' ) goto end_of_job; /* ditto for empty string */
+if ( isempty(term) ) goto end_of_job; /* no input so return 0 to caller */
/* --- skip leading white space --- */
-skipwhite(term); /* skip leading white sapce */
+skipwhite(term); /* skip leading white space */
if ( *term == '\000' ) goto end_of_job; /* nothing but white space */
/* --- skip leading gray space --- */
skipgray:
for ( i=0; (token=grayspace[i]) != NULL; i++ ) /* check each grayspace */
if ( strncmp(term,token,strlen(token)) == 0 ) { /* found grayspace */
term += strlen(token); /* skip past this grayspace token */
+ skipwhite(term); /* and skip any subsequent white space */
if ( *term == '\000' ) { /* nothing left so quit */
if ( msgfp!=NULL && msglevel >= 99 ) /* display for debugging */
fprintf(msgfp,"rastsmashcheck> only grayspace in %.32s\n",expression);
@@ -1878,7 +2378,7 @@ end_of_job:
/* ==========================================================================
- * Function: accent_subraster ( accent, width, height, pixsz )
+ * Function: accent_subraster ( accent, width, height, direction, pixsz )
* Purpose: Allocate a new subraster of width x height
* (or maybe different dimensions, depending on accent),
* and draw an accent (\hat or \vec or \etc) that fills it
@@ -1887,6 +2387,8 @@ end_of_job:
* etc, indicating the type of accent desired
* width (I) int containing desired width of accent (#cols)
* height (I) int containing desired height of accent(#rows)
+ * direction (I) int containing desired direction of accent,
+ * +1=right, -1=left, 0=left/right
* pixsz (I) int containing 1 for bitmap, 8 for bytemap
* --------------------------------------------------------------------------
* Returns: ( subraster * ) ptr to newly-allocated subraster with accent,
@@ -1896,7 +2398,8 @@ end_of_job:
* and caller should check dimensions in returned subraster
* ======================================================================= */
/* --- entry point --- */
-subraster *accent_subraster ( int accent, int width, int height, int pixsz )
+subraster *accent_subraster ( int accent, int width, int height,
+int direction, int pixsz )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
@@ -1919,6 +2422,13 @@ raster *rastrot(), /* rotate { for ove
*rastcpy(); /* may need copy of original */
subraster *arrow_subraster(); /* rightarrow for vec */
subraster *rastack(); /* stack accent atop extra space */
+int iswidthneg = 0; /* set true if width<0 arg passed */
+int serifwidth=0; /* serif for surd */
+int isBig=0; /* true for ==>arrow, false for -->*/
+/* -------------------------------------------------------------------------
+initialization
+-------------------------------------------------------------------------- */
+if ( width < 0 ) { width=(-width); iswidthneg=1; } /* set neg width flag */
/* -------------------------------------------------------------------------
outer switch() traps accents that may change caller's height,width
-------------------------------------------------------------------------- */
@@ -1975,11 +2485,15 @@ switch ( accent )
break;
/* --- sqrt request --- */
case SQRTACCENT:
- col1 = SQRTWIDTH(height) - 1; /* right col of sqrt symbol */
- col0 = (col1+2)/3; /* midpoint col of sqrt */
- row0 = (height+1)/2; /* midpoint row of sqrt */
+ serifwidth = SURDSERIFWIDTH(height); /* leading serif on surd */
+ col1 = SQRTWIDTH(height,(iswidthneg?1:2)) - 1; /*right col of sqrt*/
+ /*col0 = (col1-serifwidth+2)/3;*/ /* midpoint col of sqrt */
+ col0 = (col1-serifwidth+1)/2; /* midpoint col of sqrt */
+ row0 = max2(1,((height+1)/2)-2); /* midpoint row of sqrt */
row1 = height-1; /* bottom row of sqrt */
- line_raster(rp,row0,0,row1,col0,thickness); /* descending portion */
+ /*line_raster(rp,row0,0,row1,col0,thickness);*/ /*descending portion*/
+ line_raster(rp,row0+serifwidth,0,row0,serifwidth,thickness);
+ line_raster(rp,row0,serifwidth,row1,col0,thickness); /* descending */
line_raster(rp,row1,col0,0,col1,thickness); /* ascending portion */
line_raster(rp,0,col1,0,width-1,thickness); /*overbar of thickness 1*/
break;
@@ -2006,7 +2520,10 @@ switch ( accent )
/* --- vec request --- */
case VECACCENT:
height = 2*(height/2) + 1; /* force height odd */
- if ( (accsp=arrow_subraster(width,height,pixsz,1,0)) /*build rightarrow*/
+ if ( absval(direction) >= 9 ) { /* want ==> arrow rather than --> */
+ isBig = 1; /* signal "Big" arrow */
+ direction -= 10; } /* reset direction = +1, -1, or 0 */
+ if ((accsp=arrow_subraster(width,height,pixsz,direction,isBig)) /*arrow*/
!= NULL ) /* succeeded */
{ rp = accsp->image; /* "extract" raster with bitmap */
free((void *)accsp); } /* and free subraster "envelope" */
@@ -2798,14 +3315,22 @@ return ( status );
* if negative, abs(nbot) used, and same
* number of extra cols added at right.
* isline (I) int containing 0 to leave border pixels clear
- * or >0 to draw a line around border of width
- * isline.
+ * or >0 to draw a line around border of
+ * thickness isline pixels. See Notes below.
* isfree (I) int containing true to free rp before return
* --------------------------------------------------------------------------
* Returns: ( raster * ) ptr to bordered raster,
* or NULL for any error.
* --------------------------------------------------------------------------
- * Notes: o
+ * Notes: o The isline arg also controls which sides border lines
+ * are drawn for. To do this, isline is interpreted as
+ * thickness + 100*sides so that, e.g., passing isline=601
+ * is interpreted as sides=6 and thickness=1. And
+ * sides is further interpreted as 1=left side, 2=top,
+ * 4=right and 8=bottom. For example, sides=6 where 6=2+4
+ * draws the top and right borders only. 15 draws all four
+ * sides. And 0 (no sides value embedded in isline)
+ * draws all four sides, too.
* ======================================================================= */
/* --- entry point --- */
raster *border_raster ( raster *rp, int ntop, int nbot,
@@ -2820,6 +3345,7 @@ int width = (rp==NULL?0:rp->width), /*
height = (rp==NULL?0:rp->height), /* height of raster */
istopneg=0, isbotneg=0, /* true if ntop or nbot negative */
leftmargin = 0; /* adjust width to whole number of bytes */
+int left=1, top=1, right=1, bot=1; /* frame sides to draw */
int delete_raster(); /* free input rp if isfree is true */
/* -------------------------------------------------------------------------
Initialization
@@ -2842,6 +3368,22 @@ else
leftmargin = (width%8==0? 0 : 8-(width%8)); /*makes width multiple of 8*/
width += leftmargin; /* width now multiple of 8 */
leftmargin /= 2; } /* center original raster */
+/* --- check which sides to draw --- */
+if ( isline > 100 ) { /* sides arg embedded in isline */
+ int iside=0, sides=isline/100; /* index, sides=1-15 from 101-1599 */
+ isline -= 100*sides; /* and remove sides from isline */
+ for ( iside=1; iside<=4; iside++ ) { /* check left, top, right, bot */
+ int shift = sides/2; /* shift sides left one bit */
+ if ( sides == 2*shift ) /* low-order bit is >>not<< set */
+ switch ( iside ) { /* don't draw corresponding side */
+ default: break; /* internal error */
+ case 1: left = 0; break; /* 1 = left side */
+ case 2: top = 0; break; /* 2 = top side */
+ case 3: right= 0; break; /* 4 = tight side */
+ case 4: bot = 0; break; } /* 8 = bottom side */
+ sides = shift; /* ready for next side */
+ } /* --- end-of-for(iside) --- */
+ } /* --- end-of-if(isline>100) --- */
/* -------------------------------------------------------------------------
allocate bordered raster, and embed rp within it
-------------------------------------------------------------------------- */
@@ -2858,13 +3400,13 @@ if ( isline )
/* --- draw left- and right-borders --- */
for ( irow=0; irowpixsz)) /*allocate backspaced raster*/
== (raster *)NULL ) goto end_of_job; /* and quit if failed */
/* --- fill new raster --- */
-if ( width-nback > 0 ) /* don't fill 1-pixel wide empty bp*/
+if ( 1 || width-nback > 0 ) /* don't fill 1-pixel wide empty bp*/
for ( icol=0; icol" },
- { "\", ";", "\\" }, /* backslash */
+ /*{ "\", ";", "\\" },*/ /* backslash */
{ "&backslash",";", "\\" },
{ " ", ";", "~" },
{ "¡", ";", "{\\raisebox{-2}{\\rotatebox{180}{!}}}" },
@@ -5310,21 +5878,27 @@ static struct { char *html; char *args;
{ "Ä", ";", "{\\rm~\\ddot~A}" },
{ "Å", ";", "{\\rm~A\\limits^{-1$o}}" },
{ "ã", ";", "{\\rm~\\tilde~a}" },
- { "ÿ", ";", "{\\rm~\\ddot~y}" }, /* ÿ is last, ÿ */
- /* ---------------------------------------
- html tag termchar LaTeX equivalent...
- --------------------------------------- */
- { "
", "embed\\i","\\\\" },
- { "
", "embed\\i","\\\\" },
- /* ---------------------------------------
- garbage termchar LaTeX equivalent...
- --------------------------------------- */
- { "< TEX >", "embed\\i","\000" },
- { "< / TEX >","embed\\i","\000" },
- { "
", "embed\\i","\000" },
- /* ---------------------------------------
+ { "ÿ", ";", "{\\rm~\\ddot~y}" }, /* ÿ is last, ÿ */
+ { "", ";", "{[\\&\\#nnn?]}" }, /* all other explicit nnn's */
+ /* --------------------------------------------
+ html tag termchar LaTeX equivalent...
+ -------------------------------------------- */
+ { "< br >", "embed\\i", "\\\\" },
+ { "< br / >", "embed\\i", "\\\\" },
+ { "< dd >", "embed\\i", " \000" },
+ { "< / dd >", "embed\\i", " \000" },
+ { "< dl >", "embed\\i", " \000" },
+ { "< / dl >", "embed\\i", " \000" },
+ { "< p >", "embed\\i", " \000" },
+ { "< / p >", "embed\\i", " \000" },
+ /* --------------------------------------------
+ garbage termchar LaTeX equivalent...
+ -------------------------------------------- */
+ { "< tex >", "embed\\i", " \000" },
+ { "< / tex >", "embed\\i", " \000" },
+ /* --------------------------------------------
LaTeX termchar mimeTeX equivalent...
- --------------------------------------- */
+ -------------------------------------------- */
{ "\\AA", NULL, "{\\rm~A\\limits^{-1$o}}" },
{ "\\aa", NULL, "{\\rm~a\\limits^{-1$o}}" },
{ "\\bmod", NULL, "{\\hspace2{\\rm~mod}\\hspace2}" },
@@ -5332,19 +5906,23 @@ static struct { char *html; char *args;
{ "\\dots", NULL, "{\\cdots}" },
{ "\\cdots", NULL, "{\\raisebox3{\\ldots}}" },
{ "\\ldots", NULL, "{\\fs4.\\hspace1.\\hspace1.}" },
- { "\\ddots", NULL, "{\\fs4\\raisebox8.\\hspace1\\raisebox4.\\hspace1.}"},
+ { "\\ddots", NULL, "{\\fs4\\raisebox8.\\hspace1\\raisebox4."
+ "\\hspace1\\raisebox0.}"},
{ "\\notin", NULL, "{\\not\\in}" },
{ "\\neq", NULL, "{\\not=}" },
{ "\\ne", NULL, "{\\not=}" },
+ { "\\mapsto", NULL, "{\\rule[fs/2]{1}{5+fs}\\hspace{-99}\\to}" },
{ "\\hbar", NULL, "{\\compose~h{{\\fs{-1}-\\atop\\vspace3}}}" },
{ "\\angle", NULL, "{\\compose{\\hspace{3}\\lt}{\\circle(10,15;-80,80)}}"},
{ "\\textcelsius", NULL, "{\\textdegree C}"},
{ "\\textdegree", NULL, "{\\Large^{^{\\tiny\\mathbf o}}}"},
{ "\\cr", NULL, "\\\\" },
+ /*{ "\\colon", NULL, "{:}" },*/
{ "\\iiint", NULL, "{\\int\\int\\int}\\limits" },
{ "\\iint", NULL, "{\\int\\int}\\limits" },
{ "\\Bigiint", NULL, "{\\Bigint\\Bigint}\\limits" },
{ "\\bigsqcap",NULL, "{\\fs{+4}\\sqcap}" },
+ { "\\_", "embed","{\\underline{\\ }}" }, /* displayed underscore */
{ "!`", NULL, "{\\raisebox{-2}{\\rotatebox{180}{!}}}" },
{ "?`", NULL, "{\\raisebox{-2}{\\rotatebox{180}{?}}}" },
{ "^\'", "embed","\'" }, /* avoid ^^ when re-xlating \' below */
@@ -5362,9 +5940,10 @@ static struct { char *html; char *args;
{ "\\cancel",NULL, "\\Not" },
{ "\\hhline",NULL, "\\Hline" },
{ "\\Hline", NULL, "\\hline\\,\\\\\\hline" },
- /* ---------------------------------------------------------
+ /* -----------------------------------------------------------------------
+ As per emails with Zbigniew Fiedorowicz
"Algebra Syntax" termchar mimeTeX/LaTeX equivalent...
- ------------------------------------------------------------ */
+ ----------------------------------------------------------------------- */
{ "sqrt", "1", "{\\sqrt{#1}}" },
{ "sin", "1", "{\\sin{#1}}" },
{ "cos", "1", "{\\cos{#1}}" },
@@ -5372,14 +5951,66 @@ static struct { char *html; char *args;
{ "acos", "1", "{\\cos^{-1}{#1}}" },
{ "exp", "1", "{{\\rm~e}^{#1}}" },
{ "det", "1", "{\\left|{#1}\\right|}" },
- /* ---------------------------------------
- LaTeX Constant termchar value...
- --------------------------------------- */
- { "\\thinspace", NULL, "2" },
- { "\\thinmathspace", NULL, "2" },
+ /* --------------------------------------------
+ LaTeX Constant termchar value...
+ -------------------------------------------- */
+ { "\\thinspace", NULL, "\\," },
+ { "\\thinmathspace", NULL, "\\," },
{ "\\textwidth", NULL, "400" },
+ /* --- end-of-table indicator --- */
{ NULL, NULL, NULL }
} ; /* --- end-of-symbols[] --- */
+/* ---
+ * html nn chars converted to latex equivalents
+ * ---------------------------------------------- */
+int htmlnum=0; /* numbers[inum].html */
+static struct { int html; char *latex; } numbers[] =
+ { /* ---------------------------------------
+ html num LaTeX equivalent...
+ --------------------------------------- */
+ { 9, " " }, /* horizontal tab */
+ { 10, " " }, /* line feed */
+ { 13, " " }, /* carriage return */
+ { 32, " " }, /* space */
+ { 33, "!" }, /* exclamation point */
+ { 34, "\"" }, /* " */
+ { 35, "#" }, /* hash mark */
+ { 36, "$" }, /* dollar */
+ { 37, "%" }, /* percent */
+ { 38, "&" }, /* & */
+ { 39, "\'" }, /* apostrophe (single quote) */
+ { 40, ")" }, /* left parenthesis */
+ { 41, ")" }, /* right parenthesis */
+ { 42, "*" }, /* asterisk */
+ { 43, "+" }, /* plus */
+ { 44, "," }, /* comma */
+ { 45, "-" }, /* hyphen (minus) */
+ { 46, "." }, /* period */
+ { 47, "/" }, /* slash */
+ { 58, ":" }, /* colon */
+ { 59, ";" }, /* semicolon */
+ { 60, "<" }, /* < */
+ { 61, "=" }, /* = */
+ { 62, ">" }, /* > */
+ { 63, "\?" }, /* question mark */
+ { 64, "@" }, /* commercial at sign */
+ { 91, "[" }, /* left square bracket */
+ { 92, "\\" }, /* backslash */
+ { 93, "]" }, /* right square bracket */
+ { 94, "^" }, /* caret */
+ { 95, "_" }, /* underscore */
+ { 96, "`" }, /* grave accent */
+ { 123, "{" }, /* left curly brace */
+ { 124, "|" }, /* vertical bar */
+ { 125, "}" }, /* right curly brace */
+ { 126, "~" }, /* tilde */
+ { 160, "~" }, /* (use tilde for latex) */
+ { 166, "|" }, /* ¦ (broken vertical bar) */
+ { 173, "-" }, /* (soft hyphen) */
+ { 177, "{\\pm}" }, /* ± (plus or minus) */
+ { 215, "{\\times}" }, /* × (plus or minus) */
+ { -999, NULL }
+ } ; /* --- end-of-numbers[] --- */
/* -------------------------------------------------------------------------
first remove comments
-------------------------------------------------------------------------- */
@@ -5397,7 +6028,7 @@ while ( (leftptr=strstr(expptr,leftcomme
{ *leftptr = '\000'; /*so terminate expr at leftcomment*/
break; } /* and stop looking for comments */
*leftptr = '~'; /* replace entire comment by ~ */
- strcpy(leftptr+1,tokptr); /* and squeeze out comment */
+ strsqueezep(leftptr+1,tokptr); /* squeeze out comment */
goto next_comment; } /* stop looking for rightcomment */
/* --- no rightcomment after opening leftcomment --- */
*leftptr = '\000'; /* so terminate expression */
@@ -5414,6 +6045,7 @@ for(isymbol=0; (htmlsym=symbols[isymbol]
int htmllen = strlen(htmlsym); /* length of escape, _without_ ; */
int isalgebra = isalpha((int)(*htmlsym)); /* leading char alphabetic */
int isembedded = 0, /* true to xlate even if embedded */
+ istag=0, isamp=0, /* true for , &char; symbols */
isstrwstr = 0, /* true to use strwstr() */
wstrlen = 0; /* length of strwstr() match */
char *aleft="{([<|", *aright="})]>|"; /*left,right delims for alg syntax*/
@@ -5422,9 +6054,14 @@ for(isymbol=0; (htmlsym=symbols[isymbol]
int embedlen = strlen(embedkeywd); /* #chars in embedkeywd */
char *args = symbols[isymbol].args, /* number {}-args, optional []-arg */
*htmlterm = args, /*if *args nonumeric, then html term*/
- *latexsym = symbols[isymbol].latex; /*latex replacement for htmlsym*/
+ *latexsym = symbols[isymbol].latex, /*latex replacement for htmlsym*/
+ errorsym[256]; /*or latexsym may point to error msg*/
char abuff[8192]; int iarg,nargs=0; /* macro expansion params */
char wstrwhite[99]; /* whitespace chars for strwstr() */
+ skipwhite(htmlsym); /*skip any bogus leading whitespace*/
+ htmllen = strlen(htmlsym); /* reset length of html token */
+ istag = (isthischar(*htmlsym,"<")?1:0); /* html starts with < */
+ isamp = (isthischar(*htmlsym,"&")?1:0); /* html &char; starts with & */
if ( args != NULL ) /*we have args (or htmlterm) param*/
if ( *args != '\000' ) { /* and it's not an empty string */
if ( strchr("0123456789",*args) != NULL ) /* is 1st char #args=0-9 ? */
@@ -5432,44 +6069,84 @@ for(isymbol=0; (htmlsym=symbols[isymbol]
*abuff = *args; abuff[1] = '\000'; /* #args char in ascii buffer */
nargs = atoi(abuff); } /* interpret #args to numeric */
else if ( strncmp(args,embedkeywd,embedlen) == 0 )/*xlate embedded token*/
- { htmlterm = NULL; /* if so, then we have no htmlterm */
+ { int arglen = strlen(args); /* length of "embed..." string */
+ htmlterm = NULL; /* if so, then we have no htmlterm */
isembedded = 1 ; /* turn on embedded flag */
- embedterm = args[embedlen]; /* char immediately after embed */
- if (strlen(args) > embedlen+1) { /* have embed,white for strwstr() */
- isstrwstr = 1; /* turn on strwtsr flag */
- strcpy(wstrwhite,args+6); } } /* and set its whitespace arg */
+ if ( arglen > embedlen ) /* have embed "allow escape" flag */
+ embedterm = args[embedlen]; /* char immediately after "embed" */
+ if (arglen > embedlen+1) { /* have embed,flag,white for strwstr*/
+ isstrwstr = 1; /* turn on strwtsr flag */
+ strcpy(wstrwhite,args+embedlen+1); } } /*and set its whitespace arg*/
} /* --- end-of-if(*args!='\000') --- */
expptr = expression; /* re-start search at beginning */
while ( ( tokptr=(!isstrwstr?strstr(expptr,htmlsym): /* just use strtsr */
strwstr(expptr,htmlsym,wstrwhite,&wstrlen)) ) /* or use our strwstr */
- != NULL ) /* found another sym */
- { int toklen = (!isstrwstr?htmllen:wstrlen); /* length of matched sym */
+ != NULL ) { /* found another sym */
+ int toklen = (!isstrwstr?htmllen:wstrlen); /* length of matched sym */
char termchar = *(tokptr+toklen), /* char terminating html sequence */
- prevchar = (tokptr==expptr?' ':*(tokptr-1)); /*char preceding html*/
+ prevchar = (tokptr==expptr?' ':*(tokptr-1));/*char preceding html*/
+ int isescaped = (isthischar(prevchar,ESCAPE)?1:0); /* token escaped?*/
int escapelen = toklen; /* total length of escape sequence */
+ int isflush = 0; /* true to flush (don't xlate) */
+ /* --- check odd/even backslashes preceding tokens --- */
+ if ( isescaped ) { /* have one preceding backslash */
+ char *p = tokptr-1; /* ptr to that preceding backslash */
+ while ( p != expptr ) { /* and we may have more preceding */
+ p--; if(!isthischar(*p,ESCAPE))break; /* but we don't, so quit */
+ isescaped = 1-isescaped; } } /* or flip isescaped flag if we do */
+ /* --- init with "trivial" abuff,escapelen from symbols[] table --- */
*abuff = '\000'; /* default to empty string */
if ( latexsym != NULL ) /* table has .latex xlation */
if ( *latexsym != '\000' ) /* and it's not an empty string */
strcpy(abuff,latexsym); /* so get local copy */
- if ( htmlterm != NULL ) /* sequence may have terminator */
+ if ( !isembedded ) /*embedded sequences not terminated*/
+ if ( htmlterm != NULL ) /* sequence may have terminator */
escapelen += (isthischar(termchar,htmlterm)?1:0); /*add terminator*/
- if ( !isembedded ) /* don't xlate embedded sequence */
- if ( isalpha((int)termchar) ) /*we just have prefix of longer sym*/
- { expptr = tokptr+toklen; /* just resume search after prefix */
+ /* --- don't xlate if we just found prefix of longer symbol, etc --- */
+ if ( !isembedded ) { /* not embedded */
+ if ( isescaped ) /* escaped */
+ isflush = 1; /* set flag to flush escaped token */
+ if ( !istag && isalpha((int)termchar) ) /* followed by alpha */
+ isflush = 1; /* so just a prefix of longer symbol*/
+ if ( isalpha((int)(*htmlsym)) ) /* symbol starts with alpha */
+ if ( (!isspace(prevchar)&&isalpha(prevchar)) ) /* just a suffix*/
+ isflush = 1; } /* set flag to flush token */
+ if ( isembedded ) /* for embedded token */
+ if ( isescaped ) /* and embedded \token escaped */
+ if ( !isthischar(embedterm,ESCAPE) ) /* don't xlate escaped \token */
+ isflush = 1; /* set flag to flush token */
+ if ( isflush ) /* don't xlate this token */
+ { expptr = tokptr+1;/*toklen;*/ /* just resume search after token */
continue; } /* but don't replace it */
- if ( isembedded ) /* for embedded sequence */
- if ( !isthischar(embedterm,ESCAPE) /* don't xlate escaped \token */
- && isthischar(prevchar,ESCAPE) ) /* and we have escaped \token */
- { expptr = tokptr+toklen; /*just resume search after literal*/
- continue; } /* but don't replace it */
- 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 */
- if ( tokptr != expression ) /* then if we're past beginning */
- if ( isthischar(*(tokptr-1),ESCAPE) /*and if inline symbol escaped*/
- || (isalpha(*(tokptr-1))) ) /* or if suffix of longer string */
- { expptr = tokptr+escapelen; /*just resume search after literal*/
- continue; } /* but don't replace it */
+ /* --- check for prefix signalling nnn; --- */
+ if ( strcmp(htmlsym,"") == 0 ) { /* replacing special nnn; chars */
+ /* --- accumulate chars comprising number following --- */
+ char anum[32]; /* chars comprising number after */
+ int inum = 0; /* no chars accumulated yet */
+ while ( termchar != '\000' ) { /* don't go past end-of-string */
+ if ( !isdigit((int)termchar) ) break; /* and don't go past digits */
+ if ( inum > 10 ) break; /* some syntax error in expression */
+ anum[inum] = termchar; /* accumulate this digit */
+ inum++; toklen++; /* bump field length, token length */
+ termchar = *(tokptr+toklen); } /* char terminating html sequence */
+ anum[inum] = '\000'; /* null-terminate anum */
+ escapelen = toklen; /* length of nnn; sequence */
+ if ( htmlterm != NULL ) /* sequence may have terminator */
+ escapelen += (isthischar(termchar,htmlterm)?1:0); /*add terminator*/
+ /* --- look up nnn in number[] table --- */
+ htmlnum = atoi(anum); /* convert anum[] to an integer */
+ strninit(errorsym,latexsym,128); /* init error message */
+ latexsym = errorsym; /* init latexsym as error message */
+ strreplace(latexsym,"nnn",anum,1); /*place actual num in message*/
+ for ( inum=0; numbers[inum].html>=0; inum++ ) /* run thru numbers[] */
+ if ( htmlnum == numbers[inum].html ) { /* till we find a match */
+ latexsym = numbers[inum].latex; /* latex replacement */
+ break; } /* no need to look any further */
+ if ( latexsym != NULL ) /* table has .latex xlation */
+ if ( *latexsym != '\000' ) /* and it's not an empty string */
+ strcpy(abuff,latexsym); /* so get local copy */
+ } /* --- end-of-if(strcmp(htmlsym,"")==0) --- */
+ /* --- substitute macro arguments --- */
if ( nargs > 0 ) /*substitute #1,#2,... in latexsym*/
{
char *arg1ptr = tokptr+escapelen;/* nargs begin after macro literal */
@@ -5486,22 +6163,25 @@ for(isymbol=0; (htmlsym=symbols[isymbol]
&& !isalgebra ) /* but not in "algebra syntax" */
{ strcpy(argval,optarg); /* init with default value */
if ( *expptr == '[' ) /* but user gave us [argval] */
- expptr = texsubexpr(expptr,argval,0,"[","]",0,0); } /*so get it*/
+ expptr = texsubexpr(expptr,argval,0,"[","]",0,0); } /*so get it*/
else /* not optional, so get {argval} */
if ( *expptr != '\000' ) { /* check that some argval provided */
- if ( !isalgebra ) /* only { } delims for latex macro */
- expptr = texsubexpr(expptr,argval,0,"{","}",0,0); /*get {argval}*/
- else /*any delim for algebra syntax macro*/
- { expptr = texsubexpr(expptr,argval,0,aleft,aright,0,1);
+ if ( !isalgebra ) /* only { } delims for latex macro */
+ expptr = texsubexpr(expptr,argval,0,"{","}",0,0);/*get {argval}*/
+ else { /*any delim for algebra syntax macro*/
+ expptr = texsubexpr(expptr,argval,0,aleft,aright,0,1);
if ( isthischar(*argval,aleft) ) /* have delim-enclosed arg */
- if ( *argval != '{' ) /* and it's not { }-enclosed */
- { strchange(0,argval,"\\left"); /* insert opening \left, */
- strchange(0,argval+strlen(argval)-1,"\\right"); } }/*\right*/
- } /* --- end-of-if(*expptr!='\000') --- */
+ if ( *argval != '{' ) { /* and it's not { }-enclosed */
+ strchange(0,argval,"\\left"); /* insert opening \left, */
+ strchange(0,argval+strlen(argval)-1,"\\right"); } }/*\right*/
+ } /* --- end-of-if(*expptr!='\000') --- */
+ /* --- (recursively) call mimeprep() to prep the argument --- */
+ if ( !isempty(argval) ) /* have an argument */
+ mimeprep(argval); /* so (recursively) prep it */
/* --- replace #`iarg` in macro with argval --- */
sprintf(argsignal,"#%d",iarg); /* #1...#9 signals argument */
while ( (argsigptr=strstr(argval,argsignal)) != NULL ) /* #1...#9 */
- strcpy(argsigptr,argsigptr+strlen(argsignal)); /*can't be in argval*/
+ {strsqueeze(argsigptr,strlen(argsignal));} /* can't be in argval */
while ( (argsigptr=strstr(abuff,argsignal)) != NULL ) /* #1...#9 */
strchange(strlen(argsignal),argsigptr,argval); /*replaced by argval*/
} /* --- end-of-for(iarg) --- */
@@ -5509,7 +6189,7 @@ for(isymbol=0; (htmlsym=symbols[isymbol]
} /* --- end-of-if(nargs>0) --- */
strchange(escapelen,tokptr,abuff); /*replace macro or html symbol*/
expptr = tokptr + strlen(abuff); /*resume search after macro / html*/
- } /* --- end-of-while(tokptr!=NULL) --- */
+ } /* --- end-of-while(tokptr!=NULL) --- */
} /* --- end-of-for(isymbol) --- */
/* -------------------------------------------------------------------------
convert \left( to \( and \right) to \), etc.
@@ -5527,7 +6207,7 @@ if ( xlateleft ) /* \left...\right xla
while ( (tokptr=strstr(expptr,lrstr)) != NULL ) /* found \left or \right */
{
if ( isthischar(*(tokptr+lrlen),braces) ) /* followed by a 1-char brace*/
- { strcpy(tokptr+1,tokptr+lrlen); /* so squeeze out "left" or "right"*/
+ { strsqueeze((tokptr+1),(lrlen-1));/*so squeeze out "left" or "right"*/
expptr = tokptr+2; } /* and resume search past brace */
else /* may be a "long" brace like \| */
{
@@ -5535,7 +6215,7 @@ if ( xlateleft ) /* \left...\right xla
for(isymbol=0; (lrsym=lrfrom[isymbol]) != NULL; isymbol++)
{ int symlen = strlen(lrsym); /* #chars in delim, e.g., 2 for \| */
if ( memcmp(tokptr+lrlen,lrsym,symlen) == 0 ) /* found long delim*/
- { strcpy(tokptr+1,tokptr+lrlen+symlen-1); /* squeeze out delim */
+ { strsqueeze((tokptr+1),(lrlen+symlen-2)); /*squeeze out delim*/
*(tokptr+1) = *(lrto[isymbol]); /* last char now 1-char delim*/
expptr = tokptr+2 - lrlen; /* resume search past 1-char delim*/
break; } /* no need to check more lrsym's */
@@ -5583,7 +6263,7 @@ for(isymbol=0; (atopsym=atopcommands[isy
arg[rightlen] = '}'; /* add closing } */
arg[rightlen+1] = '\000'; /* and null terminate it */
if ( isthischar(*arg,WHITEMATH) ) /* 1st char was mandatory space */
- strcpy(arg,arg+1); /* so squeeze it out */
+ {strsqueeze(arg,1);} /* so squeeze it out */
strcat(command,arg); /* concatanate right-arg} */
if (close!=NULL) strcat(command,close); /* add close delim if needed*/
strchange(totlen-2,leftbrace+1,command); /* {\atop} --> {\atop{}{}} */
@@ -5632,7 +6312,7 @@ int tolen = (to==NULL?0:strlen(to)), /*
shift from left or right to accommodate replacement of its nfirst chars by to
-------------------------------------------------------------------------- */
if ( tolen < nfirst ) /* shift left is easy */
- strcpy(from,from+nshift); /* because memory doesn't overlap */
+ {strsqueeze(from,nshift);} /* memmove avoids overlap memory */
if ( tolen > nfirst ) /* need more room at start of from */
{ char *pfrom = from+strlen(from); /* ptr to null terminating from */
for ( ; pfrom>=from; pfrom-- ) /* shift all chars including null */
@@ -5772,9 +6452,9 @@ if ( white != NULL ) /*user provided p
if ( *white != '\000' ) { /*and it's not just an empty string*/
strcpy(whitespace,white); /* so use caller's white spaces */
while ( (pwhite=strchr(whitespace,'i')) != NULL ) /* have an embedded i */
- strcpy(pwhite,pwhite+1); /* so squeeze it out */
+ {strsqueeze(pwhite,1);} /* so squeeze it out */
while ( (pwhite=strchr(whitespace,'I')) != NULL ) /* have an embedded I */
- strcpy(pwhite,pwhite+1); /* so squeeze it out */
+ {strsqueeze(pwhite,1);} /* so squeeze it out */
if ( *whitespace == '\000' ) /* caller's white just had i,I */
strcpy(whitespace,WHITEMATH); } /* so revert back to default */
/* -------------------------------------------------------------------------
@@ -5843,6 +6523,63 @@ end_of_job:
/* ==========================================================================
+ * Function: strdetex ( s, mode )
+ * Purpose: Removes/replaces any LaTeX math chars in s
+ * so that s can be displayed "verbatim",
+ * e.g., for error messages.
+ * --------------------------------------------------------------------------
+ * Arguments: s (I) char * to null-terminated string
+ * whose math chars are to be removed/replaced
+ * mode (I) int containing 0 to _not_ use macros (i.e.,
+ * mimeprep won't be called afterwards),
+ * or containing 1 to use macros that will
+ * be expanded by a subsequent call to mimeprep.
+ * --------------------------------------------------------------------------
+ * Returns: ( char * ) ptr to "cleaned" copy of s
+ * or "" (empty string) for any error.
+ * --------------------------------------------------------------------------
+ * Notes: o The returned pointer addresses a static buffer,
+ * so don't call strdetex() again until you're finished
+ * with output from the preceding call.
+ * ======================================================================= */
+/* --- entry point --- */
+char *strdetex ( char *s, int mode )
+{
+/* -------------------------------------------------------------------------
+Allocations and Declarations
+-------------------------------------------------------------------------- */
+static char sbuff[4096]; /* copy of s with no math chars */
+int strreplace(); /* replace _ with -, etc */
+/* -------------------------------------------------------------------------
+Make a clean copy of s
+-------------------------------------------------------------------------- */
+/* --- check input --- */
+*sbuff = '\000'; /* initialize in case of error */
+if ( isempty(s) ) goto end_of_job; /* no input */
+/* --- start with copy of s --- */
+strninit(sbuff,s,2048); /* leave room for replacements */
+/* --- make some replacements -- we *must* replace \ { } first --- */
+strreplace(sbuff,"\\","\\backslash~\\!\\!",0); /*change all \'s to text*/
+strreplace(sbuff,"{", "\\lbrace~\\!\\!",0); /*change all {'s to \lbrace*/
+strreplace(sbuff,"}", "\\rbrace~\\!\\!",0); /*change all }'s to \rbrace*/
+/* --- now our further replacements may contain \directives{args} --- */
+if( mode >= 1 ) strreplace(sbuff,"_","\\_",0); /* change all _'s to \_ */
+else strreplace(sbuff,"_","\\underline{\\qquad}",0); /*change them to text*/
+if(0)strreplace(sbuff,"<","\\textlangle ",0); /* change all <'s to text */
+if(0)strreplace(sbuff,">","\\textrangle ",0); /* change all >'s to text */
+if(0)strreplace(sbuff,"$","\\textdollar ",0); /* change all $'s to text */
+strreplace(sbuff,"$","\\$",0); /* change all $'s to \$ */
+strreplace(sbuff,"&","\\&",0); /* change all &'s to \& */
+strreplace(sbuff,"%","\\%",0); /* change all %'s to \% */
+strreplace(sbuff,"#","\\#",0); /* change all #'s to \# */
+/*strreplace(sbuff,"~","\\~",0);*/ /* change all ~'s to \~ */
+strreplace(sbuff,"^","{\\fs{+2}\\^}",0); /* change all ^'s to \^ */
+end_of_job:
+ return ( sbuff ); /* back with clean copy of s */
+} /* --- end-of-function strdetex() --- */
+
+
+/* ==========================================================================
* Function: strtexchr ( char *string, char *texchr )
* Purpose: Find first texchr in string, but texchr must be followed
* by non-alpha
@@ -5948,6 +6685,471 @@ end_of_job:
brace = ptr; /* { before expressn, } after cmmnd*/
return ( brace ); /*back to caller with delim or NULL*/
} /* --- end-of-function findbraces() --- */
+
+
+/* ==========================================================================
+ * Function: strpspn ( char *s, char *reject, char *segment )
+ * Purpose: finds the initial segment of s containing no chars
+ * in reject that are outside (), [] and {} parens, e.g.,
+ * strpspn("abc(---)def+++","+-",segment) returns
+ * segment="abc(---)def" and a pointer to the first '+' in s
+ * because the -'s are enclosed in () parens.
+ * --------------------------------------------------------------------------
+ * Arguments: s (I) (char *)pointer to null-terminated string
+ * whose initial segment is desired
+ * reject (I) (char *)pointer to null-terminated string
+ * containing the "reject chars"
+ * segment (O) (char *)pointer returning null-terminated
+ * string comprising the initial segment of s
+ * that contains non-rejected chars outside
+ * (),[],{} parens, i.e., all the chars up to
+ * but not including the returned pointer.
+ * (That's the entire string if no non-rejected
+ * chars are found.)
+ * --------------------------------------------------------------------------
+ * Returns: ( char * ) pointer to first reject-char found in s
+ * outside parens, or a pointer to the
+ * terminating '\000' of s if there are
+ * no reject chars in s outside all () parens.
+ * --------------------------------------------------------------------------
+ * Notes: o the return value is _not_ like strcspn()'s
+ * o improperly nested (...[...)...] are not detected,
+ * but are considered "balanced" after the ]
+ * o if reject not found, segment returns the entire string s
+ * o leading/trailing whitespace is trimmed from returned segment
+ * ======================================================================= */
+/* --- entry point --- */
+char *strpspn ( char *s, char *reject, char *segment )
+{
+/* -------------------------------------------------------------------------
+Allocations and Declarations
+-------------------------------------------------------------------------- */
+char *ps = s; /* current pointer into s */
+int depth = 0; /* () paren nesting level */
+int seglen=0, maxseg=2047; /* segment length, max allowed */
+/* -------------------------------------------------------------------------
+initialization
+-------------------------------------------------------------------------- */
+/* --- check arguments --- */
+if ( isempty(s) ) /* no input string supplied */
+ goto end_of_job; /* no reject chars supplied */
+/* -------------------------------------------------------------------------
+find first char from s outside () parens (and outside ""'s) and in reject
+-------------------------------------------------------------------------- */
+while ( *ps != '\000' ) { /* search till end of input string */
+ if ( isthischar(*ps,"([{") ) depth++; /* push another paren */
+ if ( isthischar(*ps,")]}") ) depth--; /* or pop another paren */
+ if ( depth < 1 ) { /* we're outside all parens */
+ if ( isempty(reject) ) break; /* no reject so break immediately */
+ if ( isthischar(*ps,reject) ) break; } /* only break on a reject char */
+ if ( segment != NULL ) /* caller gave us segment */
+ if ( seglen < maxseg ) /* don't overflow segment buffer */
+ memcpy(segment+seglen,ps,1); /* so copy non-reject char */
+ seglen += 1; ps += 1; /* bump to next char */
+ } /* --- end-of-while(*ps!=0) --- */
+end_of_job:
+ if ( segment != NULL ) { /* caller gave us segment */
+ if ( isempty(reject) ) { /* no reject char */
+ segment[min2(seglen,maxseg)] = *ps; seglen++; } /*closing )]} to seg*/
+ segment[min2(seglen,maxseg)] = '\000'; /* null-terminate the segment */
+ trimwhite(segment); } /* trim leading/trailing whitespace*/
+ return ( ps ); /* back to caller */
+} /* --- end-of-function strpspn() --- */
+
+
+/* ==========================================================================
+ * Function: isstrstr ( char *string, char *snippets, int iscase )
+ * Purpose: determine whether any substring of 'string'
+ * matches any of the comma-separated list of 'snippets',
+ * ignoring case if iscase=0.
+ * --------------------------------------------------------------------------
+ * Arguments: string (I) char * containing null-terminated
+ * string that will be searched for
+ * any one of the specified snippets
+ * snippets (I) char * containing null-terminated,
+ * comma-separated list of snippets
+ * to be searched for in string
+ * iscase (I) int containing 0 for case-insensitive
+ * comparisons, or 1 for case-sensitive
+ * --------------------------------------------------------------------------
+ * Returns: ( int ) 1 if any snippet is a substring of
+ * string, 0 if not
+ * --------------------------------------------------------------------------
+ * Notes: o
+ * ======================================================================= */
+/* --- entry point --- */
+int isstrstr ( char *string, char *snippets, int iscase )
+{
+/* -------------------------------------------------------------------------
+Allocations and Declarations
+-------------------------------------------------------------------------- */
+int status = 0; /*1 if any snippet found in string*/
+char snip[256], *snipptr = snippets, /* munge through each snippet */
+ delim = ',', *delimptr = NULL; /* separated by delim's */
+char stringcp[4096], *cp = stringcp; /*maybe lowercased copy of string*/
+/* -------------------------------------------------------------------------
+initialization
+-------------------------------------------------------------------------- */
+/* --- arg check --- */
+if ( string==NULL || snippets==NULL ) goto end_of_job; /* missing arg */
+if ( *string=='\000' || *snippets=='\000' ) goto end_of_job; /* empty arg */
+/* --- copy string and lowercase it if case-insensitive --- */
+strninit(stringcp,string,4064); /* local copy of string */
+if ( !iscase ) /* want case-insensitive compares */
+ for ( cp=stringcp; *cp != '\000'; cp++ ) /* so for each string char */
+ if ( isupper(*cp) ) *cp = tolower(*cp); /*lowercase any uppercase chars*/
+/* -------------------------------------------------------------------------
+extract each snippet and see if it's a substring of string
+-------------------------------------------------------------------------- */
+while ( snipptr != NULL ) /* while we still have snippets */
+ {
+ /* --- extract next snippet --- */
+ if ( (delimptr = strchr(snipptr,delim)) /* locate next comma delim */
+ == NULL ) /*not found following last snippet*/
+ { strninit(snip,snipptr,255); /* local copy of last snippet */
+ snipptr = NULL; } /* signal end-of-string */
+ else /* snippet ends just before delim */
+ { int sniplen = (int)(delimptr-snipptr) - 1; /* #chars in snippet */
+ memcpy(snip,snipptr,sniplen); /* local copy of snippet chars */
+ snip[sniplen] = '\000'; /* null-terminated snippet */
+ snipptr = delimptr + 1; } /* next snippet starts after delim */
+ /* --- lowercase snippet if case-insensitive --- */
+ if ( !iscase ) /* want case-insensitive compares */
+ for ( cp=snip; *cp != '\000'; cp++ ) /* so for each snippet char */
+ if ( isupper(*cp) ) *cp=tolower(*cp); /*lowercase any uppercase chars*/
+ /* --- check if snippet in string --- */
+ if ( strstr(stringcp,snip) != NULL ) /* found snippet in string */
+ { status = 1; /* so reset return status */
+ break; } /* no need to check any further */
+ } /* --- end-of-while(*snipptr!=0) --- */
+end_of_job: return ( status ); /*1 if snippet found in list, else 0*/
+} /* --- end-of-function isstrstr() --- */
+
+
+/* ==========================================================================
+ * Function: isnumeric ( s )
+ * Purpose: determine if s is an integer
+ * --------------------------------------------------------------------------
+ * Arguments: s (I) (char *)pointer to null-terminated string
+ * that's checked for a leading + or -
+ * followed by digits
+ * --------------------------------------------------------------------------
+ * Returns: ( int ) 1 if s is numeric, 0 if it is not
+ * --------------------------------------------------------------------------
+ * Notes: o
+ * ======================================================================= */
+/* --- entry point --- */
+int isnumeric ( char *s )
+{
+/* -------------------------------------------------------------------------
+determine whether s is an integer
+-------------------------------------------------------------------------- */
+int status = 0; /* return 0 if not numeric, 1 if is*/
+char *p = s; /* pointer into s */
+if ( isempty(s) ) goto end_of_job; /* missing arg or empty string */
+skipwhite(p); /*check for leading +or- after space*/
+if ( *p=='+' || *p=='-' ) p++; /* skip leading + or - */
+for ( ; *p != '\000'; p++ ) { /* check rest of s for digits */
+ if ( isdigit(*p) ) continue; /* still got uninterrupted digits */
+ if ( !isthischar(*p,WHITESPACE) ) goto end_of_job; /* non-numeric char */
+ skipwhite(p); /* skip all subsequent whitespace */
+ if ( *p == '\000' ) break; /* trailing whitespace okay */
+ goto end_of_job; /* embedded whitespace non-numeric */
+ } /* --- end-of-for(*p) --- */
+status = 1; /* numeric after checks succeeded */
+end_of_job:
+ return ( status ); /*back to caller with 1=string, 0=no*/
+} /* --- end-of-function isnumeric() --- */
+
+
+/* ==========================================================================
+ * Function: evalterm ( STORE *store, char *term )
+ * Purpose: evaluates a term
+ * --------------------------------------------------------------------------
+ * Arguments: store (I/O) STORE * containing environment
+ * in which term is to be evaluated
+ * term (I) char * containing null-terminated string
+ * with a term like "3" or "a" or "a+3"
+ * whose value is to be determined
+ * --------------------------------------------------------------------------
+ * Returns: ( int ) value of term,
+ * or NOVALUE for any error
+ * --------------------------------------------------------------------------
+ * Notes: o Also evaluates index?a:b:c:etc, returning a if index<=0,
+ * b if index=1, etc, and the last value if index is too large.
+ * Each a:b:c:etc can be another expression, including another
+ * (index?a:b:c:etc) which must be enclosed in parentheses.
+ * ======================================================================= */
+/* --- entry point --- */
+int evalterm ( STORE *store, char *term )
+{
+/* -------------------------------------------------------------------------
+Allocations and Declarations
+-------------------------------------------------------------------------- */
+int termval = 0; /* term value returned to caller */
+char token[2048] = "\000", /* copy term */
+ *delim = NULL; /* delim '(' or '?' in token */
+/*int evalwff(),*/ /* recurse to evaluate terms */
+/* evalfunc();*/ /* evaluate function(arg1,arg2,...)*/
+char *strpspn(); /* span delims */
+int getstore(); /* lookup variables */
+int isnumeric(); /* numeric=constant, else variable */
+static int evaltermdepth = 0; /* recursion depth */
+int novalue = (-89123456); /* dummy (for now) error signal */
+/* -------------------------------------------------------------------------
+Initialization
+-------------------------------------------------------------------------- */
+if ( ++evaltermdepth > 99 ) goto end_of_job; /*probably recursing forever*/
+if ( store==NULL || isempty(term) ) goto end_of_job; /*check for missing arg*/
+skipwhite(term); /* skip any leading whitespace */
+/* -------------------------------------------------------------------------
+First look for conditional of the form term?term:term:...
+-------------------------------------------------------------------------- */
+/* ---left-hand part of conditional is chars preceding "?" outside ()'s--- */
+delim = strpspn(term,"?",token); /* chars preceding ? outside () */
+if ( *delim != '\000' ) { /* found conditional expression */
+ int ncolons = 0; /* #colons we've found so far */
+ if ( *token != '\000' ) /* evaluate "index" value on left */
+ if ( (termval=evalterm(store,token)) /* evaluate left-hand term */
+ == novalue ) goto end_of_job; /* return error if failed */
+ while ( *delim != '\000' ) { /* still have chars in term */
+ delim++; *token='\000'; /* initialize for next "value:" */
+ if ( *delim == '\000' ) break; /* no more values */
+ delim = strpspn(delim,":",token); /* chars preceding : outside () */
+ if ( ncolons++ >= termval ) break; /* have corresponding term */
+ } /* --- end-of-while(*delim!='\000')) --- */
+ if ( *token != '\000' ) /* have x:x:value:x:x on right */
+ termval=evalterm(store,token); /* so evaluate it */
+ goto end_of_job; /* return result to caller */
+ } /* --- end-of-if(*delim!='\000')) --- */
+/* -------------------------------------------------------------------------
+evaluate a+b recursively
+-------------------------------------------------------------------------- */
+/* --- left-hand part of term is chars preceding "/+-*%" outside ()'s --- */
+term = strpspn(term,"/+-*%",token); /* chars preceding /+-*% outside ()*/
+/* --- evaluate a+b, a-b, etc --- */
+if ( *term != '\000' ) { /* found arithmetic operation */
+ int leftval=0, rightval=0; /* init leftval for unary +a or -a */
+ if ( *token != '\000' ) /* or eval for binary a+b or a-b */
+ if ( (leftval=evalterm(store,token)) /* evaluate left-hand term */
+ == novalue ) goto end_of_job; /* return error if failed */
+ if ( (rightval=evalterm(store,term+1)) /* evaluate right-hand term */
+ == novalue ) goto end_of_job; /* return error if failed */
+ switch ( *term ) { /* perform requested arithmetic */
+ default: break; /* internal error */
+ case '+': termval = leftval+rightval; break; /* addition */
+ case '-': termval = leftval-rightval; break; /* subtraction */
+ case '*': termval = leftval*rightval; break; /* multiplication */
+ case '/': if ( rightval != 0 ) /* guard against divide by zero */
+ termval = leftval/rightval; break; /* integer division */
+ case '%': if ( rightval != 0 ) /* guard against divide by zero */
+ termval = leftval%rightval; break; /*left modulo right */
+ } /* --- end-of-switch(*relation) --- */
+ goto end_of_job; /* return result to caller */
+ } /* --- end-of-if(*term!='\000')) --- */
+/* -------------------------------------------------------------------------
+check for parenthesized expression or term of the form function(arg1,arg2,...)
+-------------------------------------------------------------------------- */
+if ( (delim = strchr(token,'(')) != NULL ) { /* token contains a ( */
+ /* --- strip trailing paren (if there hopefully is one) --- */
+ int toklen = strlen(token); /* total #chars in token */
+ if ( token[toklen-1] == ')' ) /* found matching ) at end of token*/
+ token[--toklen] = '\000'; /* remove trailing ) */
+ /* --- handle parenthesized subexpression --- */
+ if ( *token == '(' ) { /* have parenthesized expression */
+ strsqueeze(token,1); /* so squeeze out leading ( */
+ /* --- evaluate edited term --- */
+ trimwhite(token); /* trim leading/trailing whitespace*/
+ termval = evalterm(store,token); } /* evaluate token recursively */
+ /* --- handle function(arg1,arg2,...) --- */
+ else { /* have function(arg1,arg2,...) */
+ *delim = '\000'; /* separate function name and args */
+ /*termval = evalfunc(store,token,delim+1);*/ } /* evaluate function */
+ goto end_of_job; } /* return result to caller */
+/* -------------------------------------------------------------------------
+evaluate constants directly, or recursively evaluate variables, etc
+-------------------------------------------------------------------------- */
+if ( *token != '\000' ) { /* empty string */
+ if ( isnumeric(token) ) /* have a constant */
+ termval = atoi(token); /* convert ascii-to-int */
+ else { /* variable or "stored proposition"*/
+ termval = getstore(store,token); } /* look up token */
+ } /* --- end-of-if(*token!=0) --- */
+/* -------------------------------------------------------------------------
+back to caller with truth value of proposition
+-------------------------------------------------------------------------- */
+end_of_job:
+ /* --- back to caller --- */
+ if ( evaltermdepth > 0 ) evaltermdepth--; /* pop recursion depth */
+ return ( termval ); /* back to caller with value */
+} /* --- end-of-function evalterm() --- */
+
+
+/* ==========================================================================
+ * Function: getstore ( store, identifier )
+ * Purpose: finds identifier in store and returns corresponding value
+ * --------------------------------------------------------------------------
+ * Arguments: store (I) (STORE *)pointer to store containing
+ * the desired identifier
+ * identifier (I) (char *)pointer to null-terminated string
+ * containing the identifier whose value
+ * is to be returned
+ * --------------------------------------------------------------------------
+ * Returns: ( int ) identifier's corresponding value,
+ * or 0 if identifier not found (or any error)
+ * --------------------------------------------------------------------------
+ * Notes: o
+ * ======================================================================= */
+/* --- entry point --- */
+int getstore ( STORE *store, char *identifier )
+{
+/* -------------------------------------------------------------------------
+Allocations and Declarations
+-------------------------------------------------------------------------- */
+int value = 0; /* store[istore].value for identifier */
+int istore=0; /* store[] index containing identifier */
+char seek[512], hide[512]; /* identifier arg, identifier in store */
+/* --- first check args --- */
+if ( store==NULL || isempty(identifier)) goto end_of_job; /* missing arg */
+strninit(seek,identifier,500); /* local copy of caller's identifier */
+trimwhite(seek); /* remove leading/trailing whitespace */
+/* --- loop over store --- */
+for ( istore=0; istorevalues[istore] or NULL */
+} /* --- end-of-function getstore() --- */
+
+
+/* ==========================================================================
+ * Functions: int unescape_url ( char *url, int isescape )
+ * char x2c ( char *what )
+ * Purpose: unescape_url replaces 3-character sequences %xx in url
+ * with the single character represented by hex xx.
+ * x2c returns the single character represented by hex xx
+ * passed as a 2-character sequence in what.
+ * --------------------------------------------------------------------------
+ * Arguments: url (I) char * containing null-terminated
+ * string with embedded %xx sequences
+ * to be converted.
+ * isescape (I) int containing 1 to _not_ unescape
+ * \% sequences (0 would be NCSA default)
+ * what (I) char * whose first 2 characters are
+ * interpreted as ascii representations
+ * of hex digits.
+ * --------------------------------------------------------------------------
+ * Returns: ( int ) unescape_url always returns 0.
+ * ( char ) x2c returns the single char
+ * corresponding to hex xx passed in what.
+ * --------------------------------------------------------------------------
+ * Notes: o These two functions were taken verbatim from util.c in
+ * ftp://ftp.ncsa.uiuc.edu/Web/httpd/Unix/ncsa_httpd/cgi/ncsa-default.tar.Z
+ * o Not quite "verbatim" -- I added the "isescape logic" 4-Dec-03
+ * so unescape_url() can be safely applied to input which may or
+ * may not have been url-encoded. (Note: currently, all calls
+ * to unescape_url() pass iescape=0, so it's not used.)
+ * o Added +++'s to blank xlation on 24-Sep-06
+ * o Added ^M,^F,etc to blank xlation 0n 01-Oct-06
+ * ======================================================================= */
+/* --- entry point --- */
+int unescape_url(char *url, int isescape) {
+ int x=0,y=0,prevescape=0,gotescape=0;
+ int xlateplus = (isplusblank==1?1:0); /* true to xlate plus to blank */
+ int strreplace(); /* replace + with blank, if needed */
+ char x2c();
+ static char *hex="0123456789ABCDEFabcdef";
+ /* ---
+ * xlate ctrl chars to blanks
+ * -------------------------- */
+ if ( 1 ) { /* xlate ctrl chars to blanks */
+ char *ctrlchars = "\n\t\v\b\r\f\a\015";
+ int seglen = strspn(url,ctrlchars); /*initial segment with ctrlchars*/
+ int urllen = strlen(url); /* total length of url string */
+ /* --- first, entirely remove ctrlchars from beginning and end --- */
+ if ( seglen > 0 ) { /*have ctrlchars at start of string*/
+ strsqueeze(url,seglen); /* squeeze out initial ctrlchars */
+ urllen -= seglen; } /* string is now shorter */
+ while ( --urllen >= 0 ) /* now remove ctrlchars from end */
+ if ( isthischar(url[urllen],ctrlchars) ) /* ctrlchar at end */
+ url[urllen] = '\000'; /* re-terminate string before it */
+ else break; /* or we're done */
+ urllen++; /* length of url string */
+ /* --- now, replace interior ctrlchars with ~ blanks --- */
+ while ( (seglen=strcspn(url,ctrlchars)) < urllen ) /*found a ctrlchar*/
+ url[seglen] = '~'; /* replace ctrlchar with ~ */
+ } /* --- end-of-if(1) --- */
+ /* ---
+ * xlate +'s to blanks if requested or if deemed necessary
+ * ------------------------------------------------------- */
+ if ( isplusblank == (-1) ) { /*determine whether or not to xlate*/
+ char *searchfor[] = { " ","%20", "%2B","%2b", "+++","++",
+ "+=+","+-+", NULL };
+ int isearch = 0, /* searchfor[] index */
+ nfound[11] = {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}; /*#occurrences*/
+ /* --- locate occurrences of searchfor[] strings in url --- */
+ for ( isearch=0; searchfor[isearch] != NULL; isearch++ ) {
+ char *psearch = url; /* start search at beginning */
+ nfound[isearch] = 0; /* init #occurrences count */
+ while ( (psearch=strstr(psearch,searchfor[isearch])) != NULL ) {
+ nfound[isearch] += 1; /* count another occurrence */
+ psearch += strlen(searchfor[isearch]); } /*resume search after it*/
+ } /* --- end-of-for(isearch) --- */
+ /* --- apply some common-sense logic --- */
+ if ( nfound[0] + nfound[1] > 0 ) /* we have actual " "s or "%20"s */
+ isplusblank = xlateplus = 0; /* so +++'s aren't blanks */
+ if ( nfound[2] + nfound[3] > 0 ) { /* we have "%2B" for +++'s */
+ if ( isplusblank != 0 ) /* and haven't disabled xlation */
+ isplusblank = xlateplus = 1; /* so +++'s are blanks */
+ else /* we have _both_ "%20" and "%2b" */
+ xlateplus = 0; } /* tough call */
+ if ( nfound[4] + nfound[5] > 0 /* we have multiple ++'s */
+ || nfound[6] + nfound[7] > 0 ) /* or we have a +=+ or +-+ */
+ if ( isplusblank != 0 ) /* and haven't disabled xlation */
+ xlateplus = 1; /* so xlate +++'s to blanks */
+ } /* --- end-of-if(isplusblank==-1) --- */
+ if ( xlateplus > 0 ) { /* want +'s xlated to blanks */
+ char *xlateto[] = { ""," "," "," + "," "," "," "," "," " };
+ while ( xlateplus > 0 ) { /* still have +++'s to xlate */
+ char plusses[99] = "++++++++++++++++++++"; /* longest +++ string */
+ plusses[xlateplus] = '\000'; /* null-terminate +++'s */
+ strreplace(url,plusses,xlateto[xlateplus],0); /* xlate +++'s */
+ xlateplus--; /* next shorter +++ string */
+ } /* --- end-of-while(xlateplus>0) --- */
+ } /* --- end-of-if(xlateplus) --- */
+ isplusblank = 0; /* don't iterate this xlation */
+ /* ---
+ * xlate %nn to corresponding char
+ * ------------------------------- */
+ for(;url[y];++x,++y) {
+ gotescape = prevescape;
+ prevescape = (url[x]=='\\');
+ if((url[x] = url[y]) == '%')
+ if(!isescape || !gotescape)
+ if(isthischar(url[y+1],hex)
+ && isthischar(url[y+2],hex))
+ { url[x] = x2c(&url[y+1]);
+ y+=2; }
+ }
+ url[x] = '\0';
+ return 0;
+} /* --- end-of-function unescape_url() --- */
+/* --- entry point --- */
+char x2c(char *what) {
+ char digit;
+ digit = (what[0] >= 'A' ? ((what[0] & 0xdf) - 'A')+10 : (what[0] - '0'));
+ digit *= 16;
+ digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A')+10 : (what[1] - '0'));
+ return(digit);
+} /* --- end-of-function x2c() --- */
#endif /* PART2 */
/* ---
@@ -6000,6 +7202,7 @@ int isleftscript = 0, /* true if left-h
wasscripted = 0, /* true if preceding token scripted*/
wasdelimscript = 0; /* true if preceding delim scripted*/
/*int pixsz = 1;*/ /*default #bits per pixel, 1=bitmap*/
+char *strdetex(); /* detex token for error message */
/* --- global values saved/restored at each recursive iteration --- */
int wasstring = isstring, /* initial isstring mode flag */
wasdisplaystyle = isdisplaystyle, /*initial displaystyle mode flag*/
@@ -6025,6 +7228,7 @@ isreplaceleft = 0; /* reset replacelef
if(1)fraccenterline = NOVALUE; /* reset \frac baseline signal */
/* shrinkfactor = shrinkfactors[max2(0,min2(size,LARGESTSIZE))];*/ /*set sf*/
shrinkfactor = shrinkfactors[max2(0,min2(size,16))]; /* have 17 sf's */
+rastlift = 0; /* reset global rastraise() lift */
if ( msgfp!=NULL && msglevel >= 9 ) { /*display expression for debugging*/
fprintf(msgfp,
"rasterize> recursion#%d, size=%d,\n\texpression=\"%s\"\n",
@@ -6114,9 +7318,9 @@ while ( 1 )
fontnum = 0; /* reset from \mathbb, etc */
if ( isthischar(*chartoken,ESCAPE) ) /* we got unrecognized \escape*/
{ /* --- so display literal {\rm~[\backslash~chartoken?]} --- */
- strcpy(literal,"{\\rm~[\\backslash~"); /* init token */
- strcat(literal,chartoken+1); /* add chars following leading \ */
- strcat(literal,"?]}"); } /* add closing brace */
+ strcpy(literal,"{\\rm~["); /* init error message token */
+ strcat(literal,strdetex(chartoken,0)); /* detex the token */
+ strcat(literal,"?]}"); } /* add closing ? and brace */
sp = rasterize(literal,size-1); /* rasterize literal token */
fontnum = oldfontnum; /* reset font family */
if ( sp == (subraster *)NULL ) continue; }/*flush if rasterize fails*/
@@ -6215,6 +7419,7 @@ end_of_job:
leftexpression = oldleftexpression; /* leftexpression reset */
leftsymdef = oldleftsymdef; /* leftsymdef reset */
unitlength = oldunitlength; /* unitlength reset */
+ iunitlength = (int)(unitlength+0.5); /* iunitlength reset */
recurlevel--; /* unwind one recursion level */
/* --- return final subraster to caller --- */
return ( expraster );
@@ -6229,7 +7434,7 @@ end_of_job:
* Arguments: subexpr (I) char ** to first char of null-terminated
* string beginning with a LEFTBRACES
* to be rasterized
- * size (I) int containing 0-5 default font size
+ * size (I) int containing 0-7 default font size
* basesp (I) subraster * to character (or subexpression)
* immediately preceding leading left{
* (unused, but passed for consistency)
@@ -6273,7 +7478,7 @@ if ( isthischar(*expression,ESCAPE) ) /*
/* --- get expression *without* enclosing parens --- */
strcpy(noparens,expression); /* get local copy of expression */
noparens[explen-(1+isescape)] = '\000'; /* null-terminate before right} */
-strcpy(noparens,noparens+(1+isescape)); /* and then squeeze out left{ */
+strsqueeze(noparens,(1+isescape)); /* and then squeeze out left{ */
/* --- rasterize it --- */
if ( (sp = rasterize(noparens,size)) /*rasterize "interior" of expression*/
== NULL ) goto end_of_job; /* quit if failed */
@@ -6377,8 +7582,7 @@ if ( msgfp!=NULL && msglevel>=999 )
if ( isstring ) goto end_of_job; /* no scripts for ascii string */
/* --- check for \limits or \nolimits --- */
skipwhite(exprptr); /* skip white space before \limits */
-if ( exprptr != NULL ) /* expression ptr supplied */
- if ( *exprptr != '\000' ) /* something in expression */
+if ( !isempty(exprptr) ) /* non-empty expression supplied */
exprptr = texchar(exprptr,limtoken); /* retrieve next token */
if ( *limtoken != '\000' ) /* have token */
if ( (toklen=strlen(limtoken)) >= 3 ) /* which may be \[no]limits */
@@ -6462,7 +7666,7 @@ end_of_job:
* string beginning with a super/subscript,
* and returning ptr immediately following
* last script character processed.
- * size (I) int containing 0-4 default font size
+ * size (I) int containing 0-7 default font size
* basesp (I) subraster * to character (or subexpression)
* immediately preceding leading script
* (scripts will be placed relative to base)
@@ -6615,7 +7819,7 @@ end_of_job:
* rasterized along with its super/subscripts,
* and returning ptr immediately following last
* character processed.
- * size (I) int containing 0-4 default font size
+ * size (I) int containing 0-7 default font size
* sp (I) subraster * to display math operator
* to which super/subscripts will be added
* --------------------------------------------------------------------------
@@ -6689,7 +7893,7 @@ end_of_job:
* Arguments: expression (I) char ** to first char of null-terminated
* string beginning with a \left
* to be rasterized
- * size (I) int containing 0-5 default font size
+ * size (I) int containing 0-7 default font size
* basesp (I) subraster * to character (or subexpression)
* immediately preceding leading left{
* (unused, but passed for consistency)
@@ -6846,7 +8050,7 @@ for ( idelim=0; opdelims[idelim]!=NULL;
if ( strstr(ldelim,opdelims[idelim]) != NULL ) /* found operator */
{ margin += opmargin; /* extra height for operator */
if ( *ldelim == '\\' ) /* have leading escape */
- strcpy(ldelim,ldelim+1); /* squeeze it out */
+ {strsqueeze(ldelim,1);} /* squeeze it out */
break; } /* no need to check rest of table */
/* --- xlate delimiters and check for textstyle --- */
for ( idelim=1; idelim<=2; idelim++ ) { /* 1=left, 2=right */
@@ -6938,7 +8142,7 @@ end_of_job:
* 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
+ * size (I) int containing 0-7 default font size
* basesp (I) subraster * to character (or subexpression)
* immediately preceding leading left{
* (unused, but passed for consistency)
@@ -6977,7 +8181,7 @@ return ( sp );
* string immediately following \middle to be
* rasterized, and returning ptr immediately
* to terminating null.
- * size (I) int containing 0-5 default font size
+ * size (I) int containing 0-7 default font size
* basesp (I) subraster * to character (or subexpression)
* immediately preceding \middle
* (unused, but passed for consistency)
@@ -7157,8 +8361,10 @@ switch ( flag )
fonttable = (issupersampling?ssfonttable:aafonttable); /* set fonts */
break;
case ISFONTSIZE: /* set fontsize */
+ case ISMAGSTEP: /* set magstep */
case ISDISPLAYSIZE: /* set displaysize */
case ISCONTENTTYPE: /*enable/disable content-type lines*/
+ case ISCONTENTCACHED: /* write content-type to cache file*/
case ISSHRINK: /* set shrinkfactor */
case ISAAALGORITHM: /* set anti-aliasing algorithm */
case ISWEIGHT: /* set font weight */
@@ -7178,7 +8384,7 @@ switch ( flag )
if ( !isthischar(*valuearg,"?") ) /*leading ? is query for value*/
{ isdelta = isthischar(*valuearg,"+-"); /* leading + or - */
if ( memcmp(valuearg,"--",2) == 0 ) /* leading -- signals...*/
- { isdelta=0; strcpy(valuearg,valuearg+1); } /* ...not delta */
+ { isdelta=0; strsqueeze(valuearg,1); } /* ...not delta */
switch ( flag ) { /* convert to double or int */
default: argvalue = atoi(valuearg); break; /* convert to int */
case ISGAMMA:
@@ -7226,6 +8432,12 @@ switch ( flag )
{ *expression = (char *)(*expression-valuelen); /*back up buff*/
memcpy(*expression,valuearg,valuelen); } } /*and put in size*/
break;
+ case ISMAGSTEP: /* set magstep */
+ if ( argvalue != NOVALUE ) { /* got a value */
+ int largestmag = 10;
+ magstep = (isdelta? magstep+argvalue : argvalue);
+ magstep = max2(1,min2(magstep,largestmag)); }
+ break;
case ISDISPLAYSIZE: /* set displaysize */
if ( argvalue != NOVALUE ) /* got a value */
displaysize = (isdelta? displaysize+argvalue : argvalue);
@@ -7234,6 +8446,10 @@ switch ( flag )
if ( argvalue != NOVALUE ) /* got a value */
isemitcontenttype = (argvalue>0?1:0);
break;
+ case ISCONTENTCACHED: /* write content-type to cache file*/
+ if ( argvalue != NOVALUE ) /* got a value */
+ iscachecontenttype = (argvalue>0?1:0);
+ break;
case ISSMASH: /* set (minimum) "smash" margin */
if ( argvalue != NOVALUE ) /* got a value */
{ smashmargin = argvalue; /* set value */
@@ -7306,6 +8522,7 @@ switch ( flag )
{ *expression = texsubexpr(*expression,valuearg,1023,"{","}",0,0);
if ( *valuearg != '\000' ) /* guard against empty string */
unitlength = strtod(valuearg,NULL); } /* convert to double */
+ iunitlength = (int)(unitlength+0.5); /* iunitlength reset */
break;
} /* --- end-of-switch(flag) --- */
return ( NULL ); /*just set value, nothing to display*/
@@ -7351,6 +8568,7 @@ int baseht=1, baseln=0; /* height,basel
int pixsz = 1; /*default #bits per pixel, 1=bitmap*/
int isstar=0, minspace=0; /* defaults for negative hspace */
char *texsubexpr(), widtharg[256]; /* parse for optional {width} */
+int evalterm(), evalue=0; /* evaluate [args], {args} */
subraster *rasterize(), *rightsp=NULL; /*rasterize right half of expression*/
subraster *rastcat(); /* cat rightsp after \hfill */
/* -------------------------------------------------------------------------
@@ -7368,14 +8586,15 @@ if ( width == 0 ) { /* width specified
int minwidth = (isfill||isheight?1:-600); /* \hspace allows negative */
/* --- check if optional [minspace] given for negative \hspace --- */
if ( *(*expression) == '[' ) { /* [minspace] if leading char is [ */
- /* ---parse [minspace], bump expression past it, interpret as double--- */
+ /* ---parse [minspace], bump expression past it, evaluate expression--- */
*expression = texsubexpr(*expression,widtharg,127,"[","]",0,0);
- if ( *widtharg != '\000' ) /* got [minspace] */
- minspace = iround(unitlength*strtod(widtharg,NULL)); /* in pixels */
+ if ( !isempty(widtharg) ) { /* got [minspace] */
+ evalue = evalterm(mimestore,widtharg); /* evaluate widtharg expr */
+ minspace = iround(unitlength*((double)evalue)); } /* in pixels */
} /* --- end-of-if(*(*expression)=='[') --- */
width = 1; /* set default width */
*expression = texsubexpr(*expression,widtharg,255,"{","}",0,0);
- dwidth = unitlength*strtod(widtharg,NULL); /* scaled width value */
+ dwidth = unitlength*((double)evalterm(mimestore,widtharg)); /* scaled */
widthval = /* convert {width} to integer */
(int)( dwidth + (dwidth>=0.0?0.5:(-0.5)) );
if ( widthval>=minwidth && widthval<=600 ) /* sanity check */
@@ -7454,7 +8673,7 @@ end_of_job:
* string immediately following \\ to be
* rasterized, and returning ptr immediately
* to terminating null.
- * size (I) int containing 0-5 default font size
+ * size (I) int containing 0-7 default font size
* basesp (I) subraster * to character (or subexpression)
* immediately preceding \\
* (unused, but passed for consistency)
@@ -7478,7 +8697,7 @@ Allocations and Declarations
subraster *rastack(), *newlsp=NULL; /* subraster for both lines */
subraster *rasterize(), *rightsp=NULL; /*rasterize right half of expression*/
char *texsubexpr(), spacexpr[129]/*, *xptr=spacexpr*/; /*for \\[vspace]*/
-double strtod(); /* convert ascii param to double */
+int evalterm(), evalue=0; /* evaluate [arg], {arg} */
int vspace = size+2; /* #pixels between lines */
/* -------------------------------------------------------------------------
obtain optional [vspace] argument immediately following \\ command
@@ -7489,7 +8708,8 @@ if ( *(*expression) == '[' ) /*have [vs
/* ---parse [vspace] and bump expression past it, interpret as double--- */
*expression = texsubexpr(*expression,spacexpr,127,"[","]",0,0);
if ( *spacexpr == '\000' ) goto end_of_job; /* couldn't get [vspace] */
- vspace = iround(unitlength*strtod(spacexpr,NULL)); /* vspace in pixels */
+ evalue = evalterm(mimestore,spacexpr); /* evaluate [space] arg */
+ vspace = iround(unitlength*((double)evalue)); /* vspace in pixels */
} /* --- end-of-if(*(*expression)=='[') --- */
if ( leftexpression == NULL ) goto end_of_job; /* nothing preceding \\ */
/* -------------------------------------------------------------------------
@@ -7555,7 +8775,7 @@ char *texscripts(), sub[1024],super[1024
subraster *rasterize(), *subsp=NULL,*supsp=NULL; /*rasterize limits*/
subraster *new_subraster(), *rastack(), *spacesp=NULL; /*space below arrow*/
int delete_subraster(); /*free work areas in case of error*/
-double strtod(); /* convert ascii [width] to value */
+int evalterm(); /* evaluate [arg], {arg} */
int width = 10 + 8*size, height; /* width, height for \longxxxarrow */
int islimits = 1; /*true to handle limits internally*/
int limsize = size-1; /* font size for limits */
@@ -7569,7 +8789,7 @@ if ( *(*expression) == '[' ) /*check fo
{ int widthval; /* test [width] before using it */
*expression = texsubexpr(*expression,widtharg,255,"[","]",0,0);
widthval = /* convert [width] to integer */
- (int)((unitlength*strtod(widtharg,NULL))+0.5);
+ (int)((unitlength*((double)evalterm(mimestore,widtharg)))+0.5);
if ( widthval>=2 && widthval<=600 ) /* sanity check */
width = widthval; } /* replace deafault width */
/* --- now parse for limits, and bump expression past it(them) --- */
@@ -7649,7 +8869,7 @@ char *texsubexpr(), heightarg[256]; /* p
char *texscripts(), sub[1024],super[1024]; /* and _^limits after [width]*/
subraster *rasterize(), *subsp=NULL,*supsp=NULL; /*rasterize limits*/
subraster *rastcat(); /* cat superscript left, sub right */
-double strtod(); /* convert ascii [height] to value */
+int evalterm(); /* evaluate [arg], {arg} */
int height = 8 + 2*size, width; /* height, width for \longxxxarrow */
int islimits = 1; /*true to handle limits internally*/
int limsize = size-1; /* font size for limits */
@@ -7662,7 +8882,7 @@ if ( *(*expression) == '[' ) /*check fo
{ int heightval; /* test height before using it */
*expression = texsubexpr(*expression,heightarg,255,"[","]",0,0);
heightval = /* convert [height] to integer */
- (int)((unitlength*strtod(heightarg,NULL))+0.5);
+ (int)((unitlength*((double)evalterm(mimestore,heightarg)))+0.5);
if ( heightval>=2 && heightval<=600 ) /* sanity check */
height = heightval; } /* replace deafault height */
/* --- now parse for limits, and bump expression past it(them) --- */
@@ -7716,7 +8936,7 @@ end_of_job:
* string immediately following overlay \cmd to
* be rasterized, and returning ptr immediately
* following last character processed.
- * size (I) int containing 0-5 default font size
+ * size (I) int containing 0-7 default font size
* basesp (I) subraster * to character (or subexpression)
* immediately preceding overlay \cmd
* (unused, but passed for consistency)
@@ -7745,7 +8965,9 @@ char *texsubexpr(), /*parse expression
subraster *rasterize(), *sp1=NULL, *sp2=NULL, /*rasterize 1=base, 2=overlay*/
*new_subraster(); /*explicitly alloc sp2 if necessary*/
subraster *rastcompose(), *overlaysp=NULL; /*subraster for composite overlay*/
+int isalign = 0; /* true to align baselines */
int line_raster(); /* draw diagonal for \Not */
+int evalterm(); /* evaluate [arg], {arg} */
/* -------------------------------------------------------------------------
Obtain base, and maybe overlay, and rasterize them
-------------------------------------------------------------------------- */
@@ -7754,32 +8976,41 @@ if ( offset2 == NOVALUE ) /* only if no
if ( *(*expression) == '[' ) /*check for []-enclosed optional arg*/
{ int offsetval; /* test before using it */
*expression = texsubexpr(*expression,expr2,511,"[","]",0,0);
- offsetval = (int)(strtod(expr2,NULL)+0.5); /* convert [offset2] to int */
+ offsetval = /* convert [offset2] to int */
+ (int)(((double)evalterm(mimestore,expr2))+0.5);
if ( abs(offsetval) <= 25 ) /* sanity check */
offset2 = offsetval; } /* replace deafault */
if ( offset2 == NOVALUE ) offset2 = 0; /* novalue means no offset */
/* --- parse for base, bump expression past it, and rasterize it --- */
*expression = texsubexpr(*expression,expr1,511,"{","}",0,0);
-if ( *expr1 == '\000' ) goto end_of_job; /* nothing to overlay, so quit */
+if ( isempty(expr1) ) goto end_of_job; /* nothing to overlay, so quit */
+rastlift1 = rastlift = 0; /* reset all raisebox() lifts */
+if ( strstr(expr1,"\\raise") != NULL ) /* have a \raisebox */
+ isalign = 2; /* so align baselines */
if ( (sp1=rasterize(expr1,size)) /* rasterize base expression */
== NULL ) goto end_of_job; /* quit if failed to rasterize */
overlaysp = sp1; /*in case we return with no overlay*/
+rastlift1 = rastlift; /* save lift for base expression */
/* --- get overlay expression, and rasterize it --- */
if ( overlay == NOVALUE ) /* get overlay from input stream */
{ *expression = texsubexpr(*expression,expr2,511,"{","}",0,0);
- if ( *expr2 != '\000' ) /* have an overlay */
- sp2 = rasterize(expr2,size); } /* so rasterize overlay expression */
-else /* specific overlay */
+ if ( !isempty(expr2) ) { /* have an overlay */
+ if ( strstr(expr2,"\\raise") != NULL ) /* have a \raisebox */
+ isalign = 2; /* so align baselines */
+ sp2 = rasterize(expr2,size); } } /* rasterize overlay expression */
+else /* use specific built-in overlay */
switch ( overlay )
{
default: break;
case 1: /* e.g., \not overlays slash */
sp2 = rasterize("/",size+1); /* rasterize overlay expression */
+ isalign = 0; /* automatically handled correctly */
offset2 = max2(1,size-3); /* push / right a bit */
offset2 = 0;
break;
case 2: /* e.g., \Not draws diagonal */
sp2 = NULL; /* no overlay required */
+ isalign = 0; /* automatically handled correctly */
if ( overlaysp != NULL ) /* check that we have raster */
{ raster *rp = overlaysp->image; /* raster to be \Not-ed */
int width=rp->width, height=rp->height; /* raster dimensions */
@@ -7794,9 +9025,9 @@ else /* specific overlay */
case 3: /* e.g., \sout for strikeout */
sp2 = NULL; /* no overlay required */
if ( overlaysp != NULL ) /* check that we have raster */
- { raster *rp = overlaysp->image; /* raster to be \Not-ed */
+ { raster *rp = overlaysp->image; /* raster to be \sout-ed */
int width=rp->width, height=rp->height; /* raster dimensions */
- int baseline = overlaysp->baseline; /* we'll ignore descenders */
+ int baseline = (overlaysp->baseline)-rastlift; /*skip descenders*/
int midrow = max2(0,min2(height-1,offset2+((baseline+1)/2)));
if ( 1 ) /* strikeout within bounding box */
line_raster(rp,midrow,0,midrow,width-1,1); } /*draw strikeout*/
@@ -7806,7 +9037,7 @@ if ( sp2 == NULL ) goto end_of_job; /*re
/* -------------------------------------------------------------------------
construct composite overlay
-------------------------------------------------------------------------- */
-overlaysp = rastcompose(sp1,sp2,offset2,0,3);
+overlaysp = rastcompose(sp1,sp2,offset2,isalign,3);
end_of_job:
return ( overlaysp );
} /* --- end-of-function rastoverlay() --- */
@@ -7821,7 +9052,7 @@ end_of_job:
* string immediately following \frac to be
* rasterized, and returning ptr immediately
* following last character processed.
- * size (I) int containing 0-5 default font size
+ * size (I) int containing 0-7 default font size
* basesp (I) subraster * to character (or subexpression)
* immediately preceding \frac
* (unused, but passed for consistency)
@@ -7933,7 +9164,7 @@ end_of_job:
* string immediately following \stackrel to be
* rasterized, and returning ptr immediately
* following last character processed.
- * size (I) int containing 0-4 default font size
+ * size (I) int containing 0-7 default font size
* basesp (I) subraster * to character (or subexpression)
* immediately preceding \stackrel
* (unused, but passed for consistency)
@@ -8005,7 +9236,7 @@ end_of_job:
* string immediately following \mathfunc to be
* rasterized, and returning ptr immediately
* following last character processed.
- * size (I) int containing 0-4 default font size
+ * size (I) int containing 0-7 default font size
* basesp (I) subraster * to character (or subexpression)
* immediately preceding \mathfunc
* (unused, but passed for consistency)
@@ -8112,7 +9343,7 @@ end_of_job:
* string immediately following \sqrt to be
* rasterized, and returning ptr immediately
* following last character processed.
- * size (I) int containing 0-4 default font size
+ * size (I) int containing 0-7 default font size
* basesp (I) subraster * to character (or subexpression)
* immediately preceding \accent
* (unused, but passed for consistency)
@@ -8172,13 +9403,14 @@ subwidth = (subsp->image)->width; /* an
pixsz = (subsp->image)->pixsz; /* pixsz remains constant */
/* --- determine height and width of sqrt to contain subexpr --- */
sqrtheight = subheight + overspace; /* subexpr + blank line + overbar */
-surdwidth = SQRTWIDTH(sqrtheight); /* width of surd */
+surdwidth = SQRTWIDTH(sqrtheight,(rootheight<1?2:1)); /* width of surd */
sqrtwidth = subwidth + surdwidth + 1; /* total width */
/* -------------------------------------------------------------------------
construct sqrt (with room to move in subexpr) and embed subexpr in it
-------------------------------------------------------------------------- */
/* --- construct sqrt --- */
-if ( (sqrtsp=accent_subraster(SQRTACCENT,sqrtwidth,sqrtheight,pixsz))
+if ( (sqrtsp=accent_subraster(SQRTACCENT,
+(rootheight<1?sqrtwidth:(-sqrtwidth)),sqrtheight,0,pixsz))
== NULL ) goto end_of_job; /* quit if failed to build sqrt */
/* --- embed subexpr in sqrt at lower-right corner--- */
rastput(sqrtsp->image,subsp->image,overspace,sqrtwidth-subwidth,1);
@@ -8222,7 +9454,7 @@ end_of_job:
* string immediately following \accent to be
* rasterized, and returning ptr immediately
* following last character processed.
- * size (I) int containing 0-4 default font size
+ * size (I) int containing 0-7 default font size
* basesp (I) subraster * to character (or subexpression)
* immediately preceding \accent
* (unused, but passed for consistency)
@@ -8255,7 +9487,7 @@ char *texscripts(), *script=NULL, /* \un
subraster *rasterize(), *subsp=NULL, *scrsp=NULL; /*rasterize subexpr,script*/
subraster *rastack(), *accsubsp=NULL; /* stack accent, subexpr, script */
subraster *accent_subraster(), *accsp=NULL; /*raster for the accent itself*/
-int accheight=0, accwidth=0, /* height, width of accent */
+int accheight=0, accwidth=0, accdir=0,/*accent height, width, direction*/
subheight=0, subwidth=0, pixsz=0; /* height,width,pixsz of subexpr */
int delete_subraster(); /*free work areas in case of error*/
int vspace = 0; /*vertical space between accent,sub*/
@@ -8286,6 +9518,8 @@ switch ( accent )
break;
case VECACCENT:
vspace = 1; /* set 1-pixel vertical space */
+ accdir = isscript; /* +1=right,-1=left,0=lr; +10for==>*/
+ isscript = 0; /* >>don't<< signal sub/supscript */
case HATACCENT:
accheight = 7; /* default */
if ( subwidth < 10 ) accheight = 5; /* unless small width */
@@ -8297,7 +9531,7 @@ accheight = min2(accheight,subheight); /
construct accent, and construct subraster with accent over (or under) subexpr
-------------------------------------------------------------------------- */
/* --- first construct accent --- */
-if ( (accsp = accent_subraster(accent,accwidth,accheight,pixsz)) /* accent */
+if ( (accsp = accent_subraster(accent,accwidth,accheight,accdir,pixsz))
== NULL ) goto end_of_job; /* quit if failed to build accent */
/* --- now stack accent above (or below) subexpr, and free both args --- */
accsubsp = (isabove? rastack(subsp,accsp,1,vspace,1,3)/*accent above subexpr*/
@@ -8340,7 +9574,7 @@ end_of_job:
* string immediately following \font to be
* rasterized, and returning ptr immediately
* following last character processed.
- * size (I) int containing 0-5 default font size
+ * size (I) int containing 0-7 default font size
* basesp (I) subraster * to character (or subexpression)
* immediately preceding \accent
* (unused, but passed for consistency)
@@ -8372,7 +9606,7 @@ subraster *rasterize(), *fontsp=NULL, /*
int oldsmashmargin = smashmargin; /* turn off smash in text mode */
#if 0
/* --- fonts recognized by rastfont --- */
-static int nfonts = 6; /* legal font #'s are 1...nfonts */
+static int nfonts = 11; /* legal font #'s are 1...nfonts */
static struct {char *name; int class;}
fonts[] =
{ /* --- name class 1=upper,2=alpha,3=alnum,4=lower,5=digit,9=all --- */
@@ -8385,6 +9619,9 @@ static struct {char *name; int class;}
{ "\\mathbf", -1 }, /*(6) \bf,\mathbf{abc}-->{\bf~abc} */
{ "\\mathrm", -1 }, /*(7) \mathrm */
{ "\\cyr", -1 }, /*(8) \cyr */
+ { "\\textgreek",-1 }, /*(9) \textgreek */
+ { "\\textbfgreek",CMMI10BGR,1,-1 },/*(10) \textbfgreek{ab} */
+ { "\\textbbgreek",BBOLD10GR,1,-1 },/*(11) \textbbgreek{ab} */
{ NULL, 0 }
} ; /* --- end-of-fonts[] --- */
#endif
@@ -8501,7 +9738,7 @@ end_of_job:
* string immediately following \begin to be
* rasterized, and returning ptr immediately
* following last character processed.
- * size (I) int containing 0-4 default font size
+ * size (I) int containing 0-7 default font size
* basesp (I) subraster * to character (or subexpression)
* immediately preceding \begin
* (unused, but passed for consistency)
@@ -8562,7 +9799,7 @@ blevel++; /* count \begin...\begin...
exprptr = texsubexpr(*expression,subexpr,0,"{","}",0,0);
if ( *subexpr == '\000' ) goto end_of_job; /* no environment given */
while ( (delims=strchr(subexpr,'*')) != NULL ) /* have environment* */
- strcpy(delims,delims+1); /* treat it as environment */
+ {strsqueeze(delims,1);} /* treat it as environment */
/* --- look up environment in our table --- */
for ( ienviron=0; ;ienviron++ ) /* search table till NULL */
if ( environs[ienviron] == NULL ) /* found NULL before match */
@@ -8732,7 +9969,7 @@ end_of_job:
* string immediately following \array to be
* rasterized, and returning ptr immediately
* following last character processed.
- * size (I) int containing 0-4 default font size
+ * size (I) int containing 0-7 default font size
* basesp (I) subraster * to character (or subexpression)
* immediately preceding \array
* (unused, but passed for consistency)
@@ -8771,7 +10008,7 @@ char *texsubexpr(), subexpr[MAXSUBXSZ+1]
token[MAXTOKNSZ+1], *tokptr=token, /* subexpr token to rasterize */
*preamble(), *preptr=token; /*process optional size,lcr preamble*/
char *coldelim="&", *rowdelim="\\"; /* need escaped rowdelim */
-int maxarraysz = 64; /* max #rows, cols */
+int maxarraysz = 63; /* max #rows, cols */
int justify[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* -1,0,+1 = l,c,r */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
@@ -8796,6 +10033,9 @@ int justify[65]={0,0,0,0,0,0,0,0,0,0,0,0
rowbaseln[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* baseline for row */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+ vrowspace[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*extra //[len]space*/
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
rowcenter[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*true = vcenter row*/
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
@@ -8844,6 +10084,8 @@ char *texchar(), hltoken[1025]; /* extra
int ishonly=0, hltoklen, minhltoklen=3; /*flag, token must be \hl or \hd*/
int isnewrow=1; /* true for new row */
int pixsz = 1; /*default #bits per pixel, 1=bitmap*/
+int evalterm(), evalue=0; /* evaluate [arg], {arg} */
+static int mydaemonlevel = 0; /* check against global daemonlevel*/
/* -------------------------------------------------------------------------
Macros to determine extra raster space required for vline/hline
-------------------------------------------------------------------------- */
@@ -8864,6 +10106,14 @@ if ( msglevel>=29 && msgfp!=NULL ) /* de
if ( *(subexpr+2)=='\000' ) /* couldn't get subexpression */
goto end_of_job; /* nothing to do, so quit */
/* -------------------------------------------------------------------------
+reset static arrays if main re-entered as daemon (or dll)
+-------------------------------------------------------------------------- */
+if ( mydaemonlevel != daemonlevel ) { /* main re-entered */
+ for ( icol=0; icol<=maxarraysz; icol++ ) /* for each array[] index */
+ gjustify[icol] = gcolwidth[icol] = growheight[icol] =
+ gfixcolsize[icol] = gfixrowsize[icol] = growcenter[icol] = 0;
+ mydaemonlevel = daemonlevel; } /* update mydaemonlevel */
+/* -------------------------------------------------------------------------
process optional size,lcr preamble if present
-------------------------------------------------------------------------- */
/* --- reset size, get lcr's, and push exprptr past preamble --- */
@@ -8969,6 +10219,7 @@ if ( msglevel>=29 && msgfp!=NULL ) /* de
tokenize and rasterize components a & b \\ c & d \\ etc of subexpr
-------------------------------------------------------------------------- */
/* --- rasterize tokens one at a time, and maintain row,col counts --- */
+nrows = 0; /* start with top row */
ncols[nrows] = 0; /* no tokens/cols in top row yet */
while ( 1 ) /* scan chars till end */
{
@@ -9014,11 +10265,25 @@ while ( 1 ) /* scan chars till end */
if ( iseoc ) /* we have a completed token */
{
*tokptr = '\000'; /* first, null-terminate token */
- /* --- check first token in row for \hline or \hdash --- */
+ /* --- check first token in row for [len] and/or \hline or \hdash --- */
ishonly = 0; /*init for token not only an \hline*/
if ( ncols[nrows] == 0 ) /*\hline must be first token in row*/
{
tokptr=token; skipwhite(tokptr); /* skip whitespace after // */
+ /* --- first check for optional [len] --- */
+ if ( *tokptr == '[' ) { /* have [len] if leading char is [ */
+ /* ---parse [len] and bump tokptr past it, interpret as double--- */
+ char lenexpr[128]; int len; /* chars between [...] as int */
+ tokptr = texsubexpr(tokptr,lenexpr,127,"[","]",0,0);
+ if ( *lenexpr != '\000' ) { /* got [len] expression */
+ evalue = evalterm(mimestore,lenexpr); /* evaluate len expression */
+ len = iround(unitlength*((double)evalue)); /* len in pixels */
+ if ( len>=(-63) && len<=255 ) { /* sanity check */
+ vrowspace[nrows] = len; /* extra vspace before this row */
+ strsqueezep(token,tokptr); /* flush [len] from token */
+ tokptr=token; skipwhite(tokptr); } } /* reset ptr, skip white */
+ } /* --- end-of-if(*tokptr=='[') --- */
+ /* --- now check for \hline or \hdash --- */
tokptr = texchar(tokptr,hltoken); /* extract first char from token */
hltoklen = strlen(hltoken); /* length of first char */
if ( hltoklen >= minhltoklen ) { /*token must be at least \hl or \hd*/
@@ -9033,7 +10298,7 @@ while ( 1 ) /* scan chars till end */
{ istokwhite = 1; /* so token contains \hline only */
if ( iseox ) ishonly = 1; } /* ignore entire row at eox */
else /* token contains more than \hline */
- strcpy(token,tokptr); } /* so flush \hline from token */
+ {strsqueezep(token,tokptr);} } /* so flush \hline */
} /* --- end-of-if(ncols[nrows]==0) --- */
/* --- rasterize completed token --- */
toksp[ntokens] = (istokwhite? NULL : /* don't rasterize empty token */
@@ -9061,8 +10326,9 @@ while ( 1 ) /* scan chars till end */
} /* --- end-of-if(toksp[]!=NULL) --- */
/* --- bump counters --- */
if ( !ishonly ) /* don't count only an \hline */
- { ntokens++; /* bump total token count */
- ncols[nrows] += 1; } /* and bump #cols in current row */
+ if ( ncols[nrows] < maxarraysz ) /* don't overflow arrays */
+ { ntokens++; /* bump total token count */
+ ncols[nrows] += 1; } /* and bump #cols in current row */
/* --- get ready for next token --- */
tokptr = token; /* reset ptr for next token */
istokwhite = 1; /* next token starts all white */
@@ -9074,7 +10340,8 @@ while ( 1 ) /* scan chars till end */
{
maxcols = max2(maxcols,ncols[nrows]); /* max# cols in array */
if ( ncols[nrows]>0 || hline[nrows]==0 ) /*ignore row with only \hline*/
- nrows++; /* bump row count */
+ if ( nrows < maxarraysz ) /* don't overflow arrays */
+ nrows++; /* bump row count */
ncols[nrows] = 0; /* no cols in this row yet */
if ( !iseox ) /* don't have a null yet */
{ exprptr++; /* bump past extra \ in \\ delim */
@@ -9121,6 +10388,7 @@ if ( msglevel>=29 && msgfp!=NULL ) /* de
fprintf(msgfp,"\nrastarray> %d rows, heights: ",nrows);
for ( irow=0; irow<=nrows; irow++ ) /* and for each row */
{ height += rowheight[irow]; /*height of this row (0 for nrows)*/
+ height += vrowspace[irow]; /*plus extra //[len], if present*/
height += hlinespace(irow); /*plus space for hline, if present*/
if ( msglevel>=29 && msgfp!=NULL ) /* debugging */
fprintf(msgfp," %d=%2d+%d",irow+1,rowheight[irow],(hlinespace(irow))); }
@@ -9150,6 +10418,8 @@ for ( irow=0; irow<=nrows; irow++ ) /*to
if ( irow >= nrows ) hrow = height-1; /* row for bottom hline */
rule_raster(arrayrp,hrow,0,width,1,(hline[irow]<0?1:0)); } /* hline */
if ( irow >= nrows ) break; /*just needed \hline for irow=nrows*/
+ toprow += vrowspace[irow]; /* extra //[len] space above irow */
+ if ( toprow < 0 ) toprow = 0; /* check for large negative [-len] */
toprow += hlinespace(irow); /* space for hline above irow */
leftcol = 0; /* start at leftmost column */
for ( icol=0; icol 92 ) break; /* or preamble too long */
else *preptr++ = *putptr; /* copy alpha char to preamble */
*preptr = '\000'; } /* null-terminate preamble */
/* --- interpret preamble --- */
@@ -9344,16 +10616,17 @@ while ( *picptr != '\000' ) /* until we
if ( *putptr != '\000' ) /*check for put data after preamble*/
{
/* --- first squeeze preamble out of put expression --- */
- if ( *pream != '\000' ) strcpy(putexpr,putptr); /* squeeze out preamble */
+ if ( *pream != '\000' ) /* have preamble */
+ {strsqueezep(putexpr,putptr);} /* squeeze it out */
/* --- interpret x,y --- */
if ( (multptr=strchr(putexpr,';')) != NULL ) /*semicolon signals multiput*/
*multptr = '\000'; /* replace semicolon by '\0' */
if ( (putptr=strchr(putexpr,',')) != NULL ) /* comma separates x,y */
*putptr = '\000'; /* replace comma by '\0' */
if ( *putexpr != '\000' ) /* leading , may be placeholder */
- x = unitlength*strtod(putexpr,NULL); /* x coord in pixels*/
+ x = (double)(eround(putexpr)); /* x coord in pixels*/
if ( putptr != NULL ) /* 2nd arg, if present, is y coord */
- y = unitlength*strtod(putptr+1,NULL); /* in pixels */
+ y = (double)(eround(putptr+1)); /* in pixels */
/* --- interpret xinc,yinc,num if we have a multiput --- */
if ( multptr != NULL ) /* found ';' signalling multiput */
{
@@ -9362,9 +10635,9 @@ while ( *picptr != '\000' ) /* until we
if ( (putptr=strchr(multptr+1,',')) != NULL ) /* ',' between xinc,yinc*/
*putptr = '\000'; /* replace ',' by '\0' */
if ( *(multptr+1) != '\000' ) /* leading , may be placeholder */
- xinc = unitlength*strtod(multptr+1,NULL); /* xinc in pixels */
+ xinc = (double)(eround(multptr+1)); /* xinc in pixels */
if ( putptr != NULL ) /* 2nd arg, if present, is yinc */
- yinc = unitlength*strtod(putptr+1,NULL); /* in user pixels */
+ yinc = (double)(eround(putptr+1)); /* in user pixels */
num = (preptr==NULL? 999 : atoi(preptr+1)); /*explicit num val or 999*/
} /* --- end-of-if(multptr!=NULL) --- */
} /* --- end-of-if(*preptr!='\000') --- */
@@ -9468,11 +10741,11 @@ subraster *new_subraster(), *linesp=NULL
/*char *origexpression = *expression;*/ /*original expression after \line*/
int pixsz = 1; /* pixels are one bit each */
int thickness = 1; /* line thickness */
-double strtod(), /* convert ascii params to doubles */
- xinc=0.0, yinc=0.0, /* x,y-increments for line, */
+double xinc=0.0, yinc=0.0, /* x,y-increments for line, */
xlen=0.0, ylen=0.0; /* x,y lengths for line */
int width=0, height=0, /* #pixels width,height of line */
rwidth=0, rheight=0; /*alloc width,height plus thickness*/
+int evalterm(); /* evaluate [arg] and {arg}'s */
int istop=0, isright=0, /* origin at bot-left if x,yinc>=0 */
origin = 0; /* x,yinc: ++=00 +-=01 -+=10 --=11 */
int line_raster(); /* draw line in linesp->image */
@@ -9485,13 +10758,13 @@ if ( *linexpr == '\000' ) goto end_of_jo
/* --- 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 */
+ thickness = evalterm(mimestore,xptr+1); } /* get int thickness */
if ( (xptr=strchr(linexpr,',')) != NULL ) /* look for ',' in xinc,yinc */
*xptr = '\000'; /* found it, so replace ',' by '\0'*/
if ( *linexpr != '\000' ) /* check against missing 1st arg */
- xinc = xlen = strtod(linexpr,NULL); /* xinc in user units */
+ xinc = xlen = (double)evalterm(mimestore,linexpr); /* xinc in user units */
if ( xptr != NULL ) /* 2nd arg, if present, is yinc */
- yinc = ylen = strtod(xptr+1,NULL); /* in user units */
+ yinc = ylen = (double)evalterm(mimestore,xptr+1); /* in user units */
/* -------------------------------------------------------------------------
obtain optional {xlen} following (xinc,yinc), and calculate ylen
-------------------------------------------------------------------------- */
@@ -9501,7 +10774,7 @@ if ( *(*expression) == '{' ) /*have {xl
/* --- parse {xlen} and bump expression past it, interpret as double --- */
*expression = texsubexpr(*expression,linexpr,253,"{","}",0,0);
if ( *linexpr == '\000' ) goto end_of_job; /* couldn't get {xlen} */
- xlen = strtod(linexpr,NULL); /* xlen in user units */
+ xlen = (double)evalterm(mimestore,linexpr); /* xlen in user units */
/* --- set other values accordingly --- */
if ( xlen < 0.0 ) xinc = -xinc; /* if xlen negative, flip xinc sign*/
if ( xinc != 0.0 ) ylen = xlen*yinc/xinc; /* set ylen from xlen and slope*/
@@ -9596,28 +10869,29 @@ char *texsubexpr(), rulexpr[257]; /* rul
subraster *new_subraster(), *rulesp=NULL; /* subraster for rule */
int pixsz = 1; /* pixels are one bit each */
int lift=0, width=0, height=0; /* default rule parameters */
-double strtod(), dval; /* convert ascii params to doubles */
+double dval; /* convert ascii params to doubles */
int rwidth=0, rheight=0; /* alloc width, height plus lift */
int rule_raster(); /* draw rule in rulesp->image */
+int evalterm(); /* evaluate args */
/* -------------------------------------------------------------------------
Obtain lift,width,height
-------------------------------------------------------------------------- */
/* --- check for optional lift arg --- */
if ( *(*expression) == '[' ) /*check for []-enclosed optional arg*/
{ *expression = texsubexpr(*expression,rulexpr,255,"[","]",0,0);
- dval = (int)(strtod(rulexpr,NULL)+0.5); /* convert [lift] to int */
+ dval = evalterm(mimestore,rulexpr); /* convert [lift] to int */
if ( dval <= 99 && dval >= (-99) ) /* sanity check */
lift = iround(unitlength*dval); } /* scale by unitlength and round */
/* --- parse for width --- */
*expression = texsubexpr(*expression,rulexpr,255,"{","}",0,0);
if ( *rulexpr == '\000' ) goto end_of_job; /* quit if args missing */
-dval = (int)(strtod(rulexpr,NULL)+0.5); /* convert {width} to int */
+dval = evalterm(mimestore,rulexpr); /* convert {width} to int */
if ( dval <= 500 && dval >= 0 ) /* sanity check */
width = max2(0,iround(unitlength*dval)); /* scale by unitlength and round*/
/* --- parse for height --- */
*expression = texsubexpr(*expression,rulexpr,255,"{","}",0,0);
if ( *rulexpr == '\000' ) goto end_of_job; /* quit if args missing */
-dval = (int)(strtod(rulexpr,NULL)+0.5); /* convert {height} to int */
+dval = evalterm(mimestore,rulexpr); /* convert {height} to int */
if ( dval <= 500 && dval > 0 ) /* sanity check */
height= max2(1,iround(unitlength*dval)); /* scale by unitlength and round*/
/* --- raster width,height in pixels --- */
@@ -9664,7 +10938,7 @@ end_of_job:
* string immediately following \circle to be
* rasterized, and returning ptr immediately
* following last character processed.
- * size (I) int containing 0-4 default font size
+ * size (I) int containing 0-7 default font size
* basesp (I) subraster * to character (or subexpression)
* immediately preceding \circle
* (unused, but passed for consistency)
@@ -9691,10 +10965,10 @@ char *qptr=NULL, quads[256]="1234"; /* d
double theta0=0.0, theta1=0.0; /* ;theta0,theta1 instead of ;quads*/
subraster *new_subraster(), *circsp=NULL; /* subraster for ellipse */
int pixsz = 1; /* pixels are one bit each */
-double strtod(), /* convert ascii params to doubles */
- xdiam=0.0, ydiam=0.0; /* x,y major/minor axes/diameters */
+double xdiam=0.0, ydiam=0.0; /* x,y major/minor axes/diameters */
int width=0, height=0; /* #pixels width,height of ellipse */
int thickness = 1; /* drawn lines are one pixel thick */
+int evalterm(); /* evaluate [arg],{arg} expressions*/
int origin = 55; /* force origin centered */
int circle_raster(), /* draw ellipse in circsp->image */
circle_recurse(); /* for theta0,theta1 args */
@@ -9702,16 +10976,16 @@ int circle_raster(), /* draw ellipse in
obtain (xdiam[,ydiam]) arguments immediately following \circle command
-------------------------------------------------------------------------- */
/* --- parse for (xdiam[,ydiam]) args, and bump expression past it --- */
-*expression = texsubexpr(*expression,circexpr,511,"(",")",0,0);
+*expression = texsubexpr(*expression,circexpr,500,"(",")",0,0);
if ( *circexpr == '\000' ) goto end_of_job; /* couldn't get (xdiam[,ydiam])*/
/* --- now interpret xdiam[,ydiam] returned in circexpr --- */
if ( (qptr=strchr(circexpr,';')) != NULL ) /* semicolon signals quads data */
{ *qptr = '\000'; /* replace semicolon by '\0' */
- strcpy(quads,qptr+1); /* save user-requested quads */
+ strninit(quads,qptr+1,128); /* save user-requested quads */
if ( (qptr=strchr(quads,',')) != NULL ) /* have theta0,theta1 instead */
{ *qptr = '\000'; /* replace , with null */
- theta0 = strtod(quads,NULL); /* theta0 precedes , */
- theta1 = strtod(qptr+1,NULL); /* theta1 follows , */
+ theta0 = (double)evalterm(mimestore,quads); /* theta0 precedes , */
+ theta1 = (double)evalterm(mimestore,qptr+1); /* theta1 follows , */
qptr = NULL; } /* signal thetas instead of quads */
else
qptr = quads; } /* set qptr arg for circle_raster()*/
@@ -9719,9 +10993,10 @@ else /* no ;quads at all */
qptr = quads; /* default to all 4 quadrants */
if ( (xptr=strchr(circexpr,',')) != NULL ) /* look for ',' in xdiam[,ydiam]*/
*xptr = '\000'; /* found it, so replace ',' by '\0'*/
-xdiam = ydiam = strtod(circexpr,NULL); /* xdiam=ydiam in user units */
+xdiam = ydiam = /* xdiam=ydiam in user units */
+ (double)evalterm(mimestore,circexpr); /* evaluate expression */
if ( xptr != NULL ) /* 2nd arg, if present, is ydiam */
- ydiam = strtod(xptr+1,NULL); /* in user units */
+ ydiam = (double)evalterm(mimestore,xptr+1); /* in user units */
/* -------------------------------------------------------------------------
calculate width,height, etc
-------------------------------------------------------------------------- */
@@ -9779,7 +11054,7 @@ end_of_job:
* string immediately following \bezier to be
* rasterized, and returning ptr immediately
* following last character processed.
- * size (I) int containing 0-5 default font size
+ * size (I) int containing 0-7 default font size
* basesp (I) subraster * to character (or subexpression)
* immediately preceding \bezier
* (unused, but passed for consistency)
@@ -9804,13 +11079,13 @@ Allocations and Declarations
-------------------------------------------------------------------------- */
subraster *new_subraster(), *bezsp=NULL; /* subraster for bezier */
char *texsubexpr(), bezexpr[129],*xptr=bezexpr; /*\bezier(r,c)(r,c)(r,c)*/
-double strtod(); /* convert ascii params to doubles */
double r0=0.0,c0=0.0, r1=0.0,c1=0.0, rt=0.0,ct=0.0, /* bezier points */
rmid=0.0, cmid=0.0, /* coords at parameterized midpoint*/
rmin=0.0, cmin=0.0, /* minimum r,c */
rmax=0.0, cmax=0.0, /* maximum r,c */
rdelta=0.0, cdelta=0.0, /* rmax-rmin, cmax-cmin */
r=0.0, c=0.0; /* some point */
+int evalterm(); /* evaluate [arg],{arg} expressions*/
int iarg=0; /* 0=r0,c0 1=r1,c1 2=rt,ct */
int width=0, height=0; /* dimensions of bezier raster */
int pixsz = 1; /* pixels are one bit each */
@@ -9829,8 +11104,10 @@ for ( iarg=1; iarg<=2; iarg++ ) /* 0=c0
c = r = 0.0; /* init x-coord=col, y-coord=row */
if ( (xptr=strchr(bezexpr,',')) != NULL ) /* comma separates row,col */
{ *xptr = '\000'; /* found it, so replace ',' by '\0'*/
- r = unitlength*strtod(xptr+1,NULL); } /* row=y-coord in pixels */
- c = unitlength*strtod(bezexpr,NULL); /* col=x-coord in pixels */
+ /* --- row=y-coord in pixels --- */
+ r = unitlength*((double)evalterm(mimestore,xptr+1)); }
+ /* --- col=x-coord in pixels --- */
+ c = unitlength*((double)evalterm(mimestore,bezexpr));
/* --- store r,c --- */
switch ( iarg )
{ case 0: r0=r; c0=c; break;
@@ -9928,13 +11205,15 @@ Allocations and Declarations
char *texsubexpr(), subexpr[MAXSUBXSZ+1], *liftexpr=subexpr; /* args */
subraster *rasterize(), *raisesp=NULL; /* rasterize subexpr to be raised */
int lift=0; /* amount to raise/lower baseline */
+int evalterm(); /* evaluate [arg],{arg} expressions*/
/* -------------------------------------------------------------------------
obtain {lift} argument immediately following \raisebox command
-------------------------------------------------------------------------- */
+rastlift = 0; /* reset global lift adjustment */
/* --- parse for {lift} arg, and bump expression past it --- */
*expression = texsubexpr(*expression,liftexpr,0,"{","}",0,0);
if ( *liftexpr == '\000' ) goto end_of_job; /* couldn't get {lift} */
-lift = (int)((unitlength*strtod(liftexpr,NULL))+0.0); /*{lift} to integer*/
+lift = eround(liftexpr); /* {lift} to integer */
if ( abs(lift) > 200 ) lift=0; /* sanity check */
/* -------------------------------------------------------------------------
obtain {subexpr} argument after {lift}, and rasterize it
@@ -9949,6 +11228,7 @@ raise/lower baseline and return it to ca
-------------------------------------------------------------------------- */
/* --- raise/lower baseline --- */
raisesp->baseline += lift; /* new baseline (no height checks) */
+rastlift = lift; /* set global to signal adjustment */
/* --- return raised subexpr to caller --- */
end_of_job:
return ( raisesp ); /* return raised subexpr to caller */
@@ -9992,17 +11272,17 @@ subraster *rasterize(), *rotsp=NULL; /*
raster *rastrot(), *rotrp=NULL; /* rotate subraster->image 90 degs */
int delete_raster(); /* delete intermediate rasters */
int baseline=0; /* baseline of rasterized image */
-double strtod(), /* convert ascii params to doubles */
- degrees=0.0, ipart,fpart; /* degrees to be rotated */
+double degrees=0.0, ipart,fpart; /* degrees to be rotated */
int idegrees=0, isneg=0; /* positive ipart, isneg=1 if neg */
int n90=0, isn90=1; /* degrees is n90 multiples of 90 */
+int evalterm(); /* evaluate [arg],{arg} expressions*/
/* -------------------------------------------------------------------------
obtain {degrees} argument immediately following \rotatebox command
-------------------------------------------------------------------------- */
/* --- parse for {degrees} arg, and bump expression past it --- */
*expression = texsubexpr(*expression,degexpr,0,"{","}",0,0);
if ( *degexpr == '\000' ) goto end_of_job; /* couldn't get {degrees} */
-degrees = strtod(degexpr,NULL); /* degrees to be rotated */
+degrees = (double)evalterm(mimestore,degexpr); /* degrees to be rotated */
if ( degrees < 0.0 ) /* clockwise rotation desired */
{ degrees = -degrees; /* flip sign so degrees positive */
isneg = 1; } /* and set flag to indicate flip */
@@ -10069,6 +11349,82 @@ end_of_job:
/* ==========================================================================
+ * Function: rastmagnify ( expression, size, basesp, arg1, arg2, arg3 )
+ * Purpose: \magnify{magstep}{subexpression} handler, returns subraster
+ * containing magnified subexpression
+ * --------------------------------------------------------------------------
+ * Arguments: expression (I/O) char ** to first char of null-terminated
+ * string immediately following \reflectbox to
+ * be rasterized, and returning ptr immediately
+ * following last character processed.
+ * size (I) int containing 0-7 default font size
+ * basesp (I) subraster * to character (or subexpression)
+ * immediately preceding \reflectbox
+ * (unused, but passed for consistency)
+ * arg1 (I) int unused
+ * arg2 (I) int unused
+ * arg3 (I) int unused
+ * --------------------------------------------------------------------------
+ * Returns: ( subraster * ) ptr to subraster corresponding to \magnify
+ * requested, or NULL for any parsing error
+ * --------------------------------------------------------------------------
+ * Notes: o Summary of syntax...
+ * \magnify{magstep}{subexpression}
+ * o
+ * ======================================================================= */
+/* --- entry point --- */
+subraster *rastmagnify ( char **expression, int size, subraster *basesp,
+ int arg1, int arg2, int arg3 )
+{
+/* -------------------------------------------------------------------------
+Allocations and Declarations
+-------------------------------------------------------------------------- */
+char *texsubexpr(), subexpr[MAXSUBXSZ+1], *magexpr=subexpr; /* args */
+subraster *rasterize(), *magsp=NULL; /* subraster for magnified subexpr */
+raster *rastmag(), *magrp=NULL; /* magnify subraster->image */
+int magstep = 1; /* default magnification */
+int delete_raster(); /* delete intermediate raster */
+int baseline=0; /* baseline of rasterized image */
+/* -------------------------------------------------------------------------
+obtain {magstep} argument immediately following \magnify command
+-------------------------------------------------------------------------- */
+/* --- parse for {magstep} arg, and bump expression past it --- */
+*expression = texsubexpr(*expression,magexpr,255,"{","}",0,0);
+magstep = atoi(magexpr); /* convert {magstep} to int */
+if ( magstep<1 || magstep>10 ) /* check magstep input */
+ magstep = 1; /* back to default if illegal */
+/* -------------------------------------------------------------------------
+obtain {subexpr} argument after {magstep}, and rasterize it
+-------------------------------------------------------------------------- */
+/* --- parse for {subexpr} arg, and bump expression past it --- */
+*expression = texsubexpr(*expression,subexpr,0,"{","}",0,0);
+/* --- rasterize subexpression to be reflected --- */
+if ( (magsp = rasterize(subexpr,size)) /* rasterize subexpression */
+== NULL ) goto end_of_job; /* and quit if failed */
+/* --- return unmodified image if no magnification requested --- */
+if ( magstep<=1 ) goto end_of_job; /* don't bother magnifying image */
+/* --- extract params for image to be magnified --- */
+magrp = magsp->image; /* unmagnified rasterized image */
+baseline = magsp->baseline; /* and baseline of that image */
+/* -------------------------------------------------------------------------
+magnify image and adjust its parameters
+-------------------------------------------------------------------------- */
+/* --- magnify image --- */
+magrp = rastmag(magsp->image,magstep); /* magnify raster image */
+if ( magrp == NULL ) goto end_of_job; /* failed to magnify image */
+delete_raster(magsp->image); /* free original raster image */
+magsp->image = magrp; /*and replace it with magnified one*/
+/* --- adjust parameters --- */
+baseline *= magstep; /* scale baseline */
+if ( baseline > 0 ) baseline += 1; /* adjust for no descenders */
+magsp->baseline = baseline; /*reset baseline of magnified image*/
+/* --- return magnified subexpr to caller --- */
+end_of_job:
+ return ( magsp ); /*back to caller with magnified expr*/
+} /* --- end-of-function rastmagnify() --- */
+
+
+/* ==========================================================================
* Function: rastreflect ( expression, size, basesp, arg1, arg2, arg3 )
* Purpose: \reflectbox[axis]{subexpression} handler, returns subraster
* containing subexpression reflected horizontally (i.e., around
@@ -10156,7 +11512,7 @@ end_of_job:
* string immediately following \fbox to be
* rasterized, and returning ptr immediately
* following last character processed.
- * size (I) int containing 0-5 default font size
+ * size (I) int containing 0-7 default font size
* basesp (I) subraster * to character (or subexpression)
* immediately preceding \fbox
* (unused, but passed for consistency)
@@ -10181,26 +11537,48 @@ Allocations and Declarations
char *texsubexpr(), subexpr[MAXSUBXSZ+1], widtharg[512]; /* args */
subraster *rasterize(), *framesp=NULL; /* rasterize subexpr to be framed */
raster *border_raster(), *bp=NULL; /* framed image raster */
-double strtod(); /* interpret [width][height] */
-int fwidth=6, fthick=1; /*extra frame width, line thickness*/
+int evalterm(), evalue=0; /* interpret [width][height] */
+int fwidth=6, fthick=1, /*extra frame width, line thickness*/
+ fsides=0; /* frame sides: 1=left,2=top,4=right,8=bot */
int width=(-1), height=(-1), /* optional [width][height] args */
iscompose = 0; /* set true if optional args given */
/* -------------------------------------------------------------------------
obtain optional [width][height] arguments immediately following \fbox
-------------------------------------------------------------------------- */
/* --- first check for optional \fbox[width] --- */
-if ( *(*expression) == '[' ) /* check for []-enclosed width arg */
- { *expression = texsubexpr(*expression,widtharg,511,"[","]",0,0);
- if ( *widtharg != '\000' ) /* got widtharg */
- { width = max2(1,iround(unitlength*strtod(widtharg,NULL)));
- height = 1; fwidth = 2; iscompose = 1; }
+if ( *(*expression) == '[' ) { /* check for []-enclosed width arg */
+ *expression = texsubexpr(*expression,widtharg,511,"[","]",0,0);
+ if ( !isempty(widtharg) ) { /* got widtharg */
+ char *comma = strchr(widtharg,','); /* look for [width,sides] */
+ if ( comma == (char *)NULL ) /* no comma */
+ comma = strchr(widtharg,';'); /* permit semicolon [width;sides] */
+ if ( comma != (char *)NULL ) { /* optional [width,fsides] found */
+ fsides = atoi(comma+1); /* interpret fsides after comma */
+ if ( size < 5 ) /* for smaller fonts */
+ { fwidth = 2; fthick = 1; } /* tighten frame, thinner accent */
+ else { fwidth = 3; fthick = 2; } /* loosen frame, thicken accent */
+ *comma = '\000'; /* null-terminate width at comma */
+ trimwhite(widtharg); } /*remove leading/trailing whitespace*/
+ if ( comma==(char *)NULL || !isempty(widtharg) ) { /* have a width */
+ height = 1; /* default explicit height, too */
+ if ( fsides == 0 ) { /* a normal framebox */
+ evalue = eround(widtharg); /* interpret and scale width */
+ width = max2(1,evalue); /* must be >0 */
+ fwidth = 2; iscompose = 1; }
+ else /* absolute pixels for "accents" */
+ width = evalterm(mimestore,widtharg); }
+ } /* --- end-of-if(!isempty(widtharg)) --- */
} /* --- end-of-if(**expression=='[') --- */
-if ( width > 0 ) /* found leading [width], so... */
+if ( width > 0 || fsides > 0) /* found leading [width], so... */
if ( *(*expression) == '[' ) /* check for []-enclosed height arg */
{ *expression = texsubexpr(*expression,widtharg,511,"[","]",0,0);
- if ( *widtharg != '\000' ) /* got widtharg */
- { height = max2(1,iround(unitlength*strtod(widtharg,NULL)));
- fwidth = 0; } /* no extra border */
+ if ( !isempty(widtharg) ) { /* got widtharg */
+ if ( fsides == 0 ) { /* a normal framebox */
+ evalue = eround(widtharg); /* interpret and scale height */
+ height = max2(1,evalue); /* must be >0 */
+ fwidth = 0; } /* no extra border */
+ else /* absolute pixels for "accents" */
+ height = evalterm(mimestore,widtharg); }
} /* --- end-of-if(**expression=='[') --- */
/* -------------------------------------------------------------------------
obtain {subexpr} argument
@@ -10213,7 +11591,7 @@ if ( width<0 || height<0 ) /* no explic
== NULL ) goto end_of_job; } /* and quit if failed */
else
{ char composexpr[8192]; /* compose subexpr with empty box */
- sprintf(composexpr,"\\compose{\\hspace{%d}\\vspace{%d}}{%s}",
+ sprintf(composexpr,"\\compose{\\hspace{%d}\\vspace{%d}}{%.8000s}",
width,height,subexpr);
if ( (framesp = rasterize(composexpr,size)) /* rasterize subexpression */
== NULL ) goto end_of_job; } /* and quit if failed */
@@ -10221,6 +11599,7 @@ else
draw frame, reset params, and return it to caller
-------------------------------------------------------------------------- */
/* --- draw border --- */
+if ( fsides > 0 ) fthick += (100*fsides); /* embed fsides in fthick arg */
if ( (bp = border_raster(framesp->image,-fwidth,-fwidth,fthick,1))
== NULL ) goto end_of_job; /* draw border and quit if failed */
/* --- replace original image and raise baseline to accommodate frame --- */
@@ -10244,7 +11623,7 @@ end_of_job:
* string immediately following \input to be
* rasterized, and returning ptr immediately
* following last character processed.
- * size (I) int containing 0-5 default font size
+ * size (I) int containing 0-7 default font size
* basesp (I) subraster * to character (or subexpression)
* immediately preceding \input
* (unused, but passed for consistency)
@@ -10256,7 +11635,9 @@ end_of_job:
* in filename, or NULL for any parsing error
* --------------------------------------------------------------------------
* Notes: o Summary of syntax...
- * \input{filename}
+ * \input{filename} reads entire file named filename
+ * \input{filename:tag} reads filename, but returns only
+ * those characters between ... in that file.
* o
* ======================================================================= */
/* --- entry point --- */
@@ -10270,6 +11651,10 @@ char *texsubexpr(), tag[1024]="\000", fi
subraster *rasterize(), *inputsp=NULL; /* rasterized input image */
int status, rastreadfile(); /* read input file */
int format=0, npts=0; /* don't reformat (numerical) input */
+int isinput = (seclevel<=inputseclevel?1:0); /*true if \input permitted*/
+/*int evalterm();*/ /* evaluate expressions */
+char *inputpath = INPUTPATH; /* permitted \input{} paths for any user */
+int isstrstr(); /* search for valid inputpath in filename */
char subexpr[MAXFILESZ+1] = "\000", /*concatanated lines from input file*/
*mimeprep(), /* preprocess inputted data */
*dbltoa(), *reformat=NULL; /* reformat numerical input */
@@ -10279,34 +11664,56 @@ obtain [tag]{filename} argument
/* --- parse for optional [tag] or [fmt] arg, bump expression past it --- */
if ( *(*expression) == '[' ) /* check for []-enclosed value */
{ char argfld[MAXTOKNSZ+1]; /* optional argument field */
- *expression = texsubexpr(*expression,argfld,MAXTOKNSZ,"[","]",0,0);
+ *expression = texsubexpr(*expression,argfld,MAXTOKNSZ-1,"[","]",0,0);
if ( (reformat=strstr(argfld,"dtoa")) != NULL ) /*dtoa/dbltoa requested*/
{ format = 1; /* signal dtoa()/dbltoa() format */
if ( (reformat=strchr(reformat,'=')) != NULL ) /* have dtoa= */
npts = (int)strtol(reformat+1,NULL,0); } /* so set npts */
- if ( format == 0 ) /* reformat not requested */
- strcpy(tag,argfld); } /* so interpret arg as tag */
+ if ( format == 0 ) { /* reformat not requested */
+ strninit(tag,argfld,1020); } } /* so interpret arg as tag */
/* --- parse for {filename} arg, and bump expression past it --- */
-*expression = texsubexpr(*expression,filename,1023,"{","}",0,0);
+*expression = texsubexpr(*expression,filename,1020,"{","}",0,0);
/* --- check for alternate filename:tag --- */
-if ( *filename != '\000' /* got filename */
-/*&& *tag == '\000'*/ ) /* but no [tag] */
+if ( !isempty(filename) /* got filename */
+/*&& isempty(tag)*/ ) /* but no [tag] */
{ char *delim = strchr(filename,':'); /* look for : in filename:tag */
if ( delim != (char *)NULL ) /* found it */
{ *delim = '\000'; /* null-terminate filename at : */
- strcpy(tag,delim+1); } } /* and stuff after : is tag */
+ strninit(tag,delim+1,1020); } } /* and stuff after : is tag */
+/* --- check filename for an inputpath valid for all users --- */
+if ( !isinput /* if this user can't \input{} */
+&& !isempty(filename) /* and we got a filename */
+&& !isempty(inputpath) ) /* and an inputpath */
+ if ( isstrstr(filename,inputpath,0) ) /* filename has allowed inputpath */
+ isinput = 1; /* okay to \input{} this filename */
+/* --- guard against recursive runaway (e.g., file \input's itself) --- */
+if ( ++ninputcmds > 8 ) /* max \input's per expression */
+ isinput = 0; /* flip flag off after the max */
+/* --------------------------------------------------------------------------
+Read file (and convert to numeric if [dtoa] option was given)
+-------------------------------------------------------------------------- */
+if ( isinput ) { /* user permitted to use \input{} */
+ status = rastreadfile(filename,0,tag,subexpr); /* read file */
+ if ( *subexpr == '\000' ) goto end_of_job; /* quit if problem */
+ /* --- rasterize input subexpression --- */
+ mimeprep(subexpr); /* preprocess subexpression */
+ if ( format == 1 ) { /* dtoa()/dbltoa() */
+ double d = strtod(subexpr,NULL); /* interpret subexpr as double */
+ if ( d != 0.0 ) /* conversion to double successful */
+ if ( (reformat=dbltoa(d,npts)) != NULL ) /* reformat successful */
+ strcpy(subexpr,reformat); } /*replace subexpr with reformatted*/
+ } /* --- end-of-if(isinput) --- */
/* --------------------------------------------------------------------------
-Read file and rasterize constructed subexpression
+emit error message for unauthorized users trying to use \input{}
+-------------------------------------------------------------------------- */
+else { /* inputseclevel > seclevel */
+ sprintf(subexpr,
+ "\\ \\text{[\\backslash input\\lbrace %.128s\\rbrace\\ not permitted]}\\ ",
+ (isempty(filename)?"???":filename));
+ } /* --- end-of-if/else(isinput) --- */
+/* --------------------------------------------------------------------------
+Rasterize constructed subexpression
-------------------------------------------------------------------------- */
-status = rastreadfile(filename,0,tag,subexpr); /* read file */
-if ( *subexpr == '\000' ) goto end_of_job; /* quit if problem */
-/* --- rasterize input subexpression --- */
-mimeprep(subexpr); /* preprocess subexpression */
-if ( format == 1 ) /* dtoa()/dbltoa() */
- { double d = strtod(subexpr,NULL); /* interpret subexpr as double */
- if ( d != 0.0 ) /* conversion to double successful */
- if ( (reformat=dbltoa(d,npts)) != NULL ) /* reformat successful */
- strcpy(subexpr,reformat); } /*replace subexpr with reformatted*/
inputsp = rasterize(subexpr,size); /* rasterize subexpression */
/* --- return input image to caller --- */
end_of_job:
@@ -10324,7 +11731,7 @@ end_of_job:
* string immediately following \counter to be
* rasterized, and returning ptr immediately
* following last character processed.
- * size (I) int containing 0-5 default font size
+ * size (I) int containing 0-7 default font size
* basesp (I) subraster * to character (or subexpression)
* immediately preceding \counter
* (unused, but passed for consistency)
@@ -10351,6 +11758,7 @@ char *texsubexpr(), filename[1024]="\000
subraster *rasterize(), *countersp=NULL; /* rasterized counter image */
FILE /* *fp=NULL,*/ *logfp=NULL; /* counter and log file pointers */
int status=0,rastreadfile(),rastwritefile(), /*read,write counter file*/
+ iscounter = (seclevel<=counterseclevel?1:0), /*is \counter permitted*/
isstrict = 1; /* true to only write to existing files */
char text[MAXFILESZ] = "1_", /* only line in counter file without tags */
*delim = NULL, /* delimiter in text */
@@ -10413,6 +11821,15 @@ if ( *filename != '\000' ) /* got filen
{ *delim = '\000'; /* null-terminate filename at : */
strcpy(tag,delim+1); } /* and stuff after : is tag */
/* --------------------------------------------------------------------------
+emit error message for unauthorized users trying to use \counter{}
+-------------------------------------------------------------------------- */
+if ( !iscounter ) { /* counterseclevel > seclevel */
+ sprintf(text,
+ "\\ \\text{[\\backslash counter\\lbrace %.128s\\rbrace\\ not permitted]}\\ ",
+ (isempty(filename)?"???":filename));
+ goto rasterize_counter; /* rasterize error message */
+ } /* --- end-of-if(!iscounter) --- */
+/* --------------------------------------------------------------------------
Read and parse file, increment and rewrite counter (with optional underscore)
-------------------------------------------------------------------------- */
if ( strlen(filename) > 1 ) /* make sure we got {filename} arg */
@@ -10490,7 +11907,8 @@ if ( ordindex >= 0 ) /* need to tack o
strcat(text,ordinal[ordindex]); /* then st,nd,rd, or th */
strcat(text,"}}"); } /* finish with }} */
/* --- rasterize it --- */
-countersp = rasterize(text,size); /* rasterize counter subexpression */
+rasterize_counter:
+ countersp = rasterize(text,size); /* rasterize counter subexpression */
/* --- return counter image to caller --- */
/*end_of_job:*/
return ( countersp ); /* return counter image to caller */
@@ -10498,6 +11916,57 @@ countersp = rasterize(text,size); /* ras
/* ==========================================================================
+ * Function: rasteval ( expression, size, basesp, arg1, arg2, arg3 )
+ * Purpose: handle \eval
+ * --------------------------------------------------------------------------
+ * Arguments: expression (I/O) char ** to first char of null-terminated
+ * string immediately following \eval,
+ * and returning ptr immediately
+ * following last character processed.
+ * size (I) int containing 0-7 default font size
+ * basesp (I) subraster * to character (or subexpression)
+ * immediately preceding \eval
+ * (unused, but passed for consistency)
+ * arg1 (I) int unused
+ * arg2 (I) int unused
+ * arg3 (I) int unused
+ * --------------------------------------------------------------------------
+ * Returns: ( subraster * ) subraster ptr to date stamp
+ * --------------------------------------------------------------------------
+ * Notes: o
+ * ======================================================================= */
+/* --- entry point --- */
+subraster *rasteval ( char **expression, int size, subraster *basesp,
+ int arg1, int arg2, int arg3 )
+{
+/* -------------------------------------------------------------------------
+Allocations and Declarations
+-------------------------------------------------------------------------- */
+char *texsubexpr(), subexpr[MAXSUBXSZ]; /* arg to be evaluated */
+subraster *rasterize(), *evalsp=NULL; /* rasterize evaluated expression */
+int evalterm(), value=0; /* evaluate expression */
+/* -------------------------------------------------------------------------
+Parse for subexpr to be \eval-uated, and bump expression past it
+-------------------------------------------------------------------------- */
+*expression = texsubexpr(*expression,subexpr,0,"{","}",0,0);
+if ( *subexpr == '\000' ) /* couldn't get subexpression */
+ goto end_of_job; /* nothing to do, so quit */
+/* -------------------------------------------------------------------------
+Evaluate expression, ascii-ize integer result, rasterize it
+-------------------------------------------------------------------------- */
+/* --- evaluate expression --- */
+value = evalterm(mimestore,subexpr); /* evaluate expression */
+/* --- ascii-ize it --- */
+sprintf(subexpr,"%d",value); /* ascii version of value */
+/* --- rasterize ascii-ized expression value --- */
+evalsp = rasterize(subexpr,size); /* rasterize evaluated expression */
+/* --- return evaluated expression raster to caller --- */
+end_of_job:
+ return ( evalsp ); /* return evaluated expr to caller */
+} /* --- end-of-function rasteval() --- */
+
+
+/* ==========================================================================
* Function: rasttoday ( expression, size, basesp, arg1, arg2, arg3 )
* Purpose: handle \today
* --------------------------------------------------------------------------
@@ -10505,7 +11974,7 @@ countersp = rasterize(text,size); /* ras
* string immediately following \today,
* and returning ptr immediately
* following last character processed.
- * size (I) int containing 0-5 default font size
+ * size (I) int containing 0-7 default font size
* basesp (I) subraster * to character (or subexpression)
* immediately preceding \today
* (unused, but passed for consistency)
@@ -10566,7 +12035,7 @@ todaysp = rasterize(today,size); /* rast
* string immediately following \calendar
* and returning ptr immediately
* following last character processed.
- * size (I) int containing 0-5 default font size
+ * size (I) int containing 0-7 default font size
* basesp (I) subraster * to character (or subexpression)
* immediately preceding \calendar
* (unused, but passed for consistency)
@@ -10627,6 +12096,169 @@ calendarsp = rasterize(calstr,size); /*
/* ==========================================================================
+ * Function: rastenviron ( expression, size, basesp, arg1, arg2, arg3 )
+ * Purpose: handle \environment
+ * --------------------------------------------------------------------------
+ * Arguments: expression (I/O) char ** to first char of null-terminated
+ * string immediately following \environment
+ * and returning ptr immediately
+ * following last character processed (in this
+ * case, \environment takes no arguments, so
+ * expression is returned unchanged).
+ * size (I) int containing 0-7 default font size
+ * basesp (I) subraster * to character (or subexpression)
+ * immediately preceding \environment
+ * (unused, but passed for consistency)
+ * arg1 (I) int unused
+ * arg2 (I) int unused
+ * arg3 (I) int unused
+ * --------------------------------------------------------------------------
+ * Returns: ( subraster * ) subraster ptr to rendered environment image
+ * --------------------------------------------------------------------------
+ * Notes: o
+ * ======================================================================= */
+/* --- entry point --- */
+subraster *rastenviron ( char **expression, int size, subraster *basesp,
+ int arg1, int arg2, int arg3 )
+{
+/* -------------------------------------------------------------------------
+Allocations and Declarations
+-------------------------------------------------------------------------- */
+char *texsubexpr(), optarg[255]; /* optional [...] args (for future)*/
+char environstr[8192] = "\000", /* string for all environment vars */
+ environvar[1024] = "\000"; /* one environment variable */
+char *strwrap(), /* wrap long lines */
+ *strdetex(), /* removes/replaces any math chars */
+ *environptr = NULL; /* ptr to preprocessed environvar */
+char *mimeprep(); /* preprocess environvar string */
+int unescape_url(); /* convert all %xx's to chars */
+int isenviron = (seclevel<=environseclevel?1:0); /*is \environ permitted*/
+int maxvarlen = 512, /* max chars in environment var */
+ maxenvlen = 6400, /* max chars in entire string */
+ wraplen = 48; /* strwrap() wrap lines at 48 chars*/
+int ienv = 0; /* environ[] index */
+subraster *rasterize(), *environsp=NULL; /* rasterize environment string */
+/* -------------------------------------------------------------------------
+Get args
+-------------------------------------------------------------------------- */
+/* --- check for optional \environment args --- */
+if ( 1 ) /* there aren't any args (yet) */
+ if ( *(*expression) == '[' ) { /* check for []-enclosed value */
+ *expression = texsubexpr(*expression,optarg,250,"[","]",0,0);
+ if ( *optarg != '\000' ) { ; /* got optional arg, so process it */
+ wraplen = atoi(optarg); /* interpret \environment[wraplen] */
+ if ( wraplen < 1 ) wraplen = 8; /* set minimum */
+ } /* --- end-of-if(*optarg!='\0') --- */
+ } /* --- end-of-if(**expression=='[') --- */
+/* --------------------------------------------------------------------------
+emit error message for unauthorized users trying to use \environ
+-------------------------------------------------------------------------- */
+if ( !isenviron ) { /* environseclevel > seclevel */
+ sprintf(environstr,
+ "\\ \\text{[\\backslash environment\\ not permitted]}\\ ");
+ goto rasterize_environ; /* rasterize error message */
+ } /* --- end-of-if(!isenviron) --- */
+/* -------------------------------------------------------------------------
+Accumulate environment variables and rasterize string containing them
+-------------------------------------------------------------------------- */
+*environstr = '\000'; /* reset environment string */
+strcat(environstr,"\\nocaching\\fbox{\\normalsize\\text{"); /*init string*/
+for ( ienv=0; ; ienv++ ) { /* loop over environ[] strings */
+ if ( environ[ienv] == (char *)NULL ) break; /* null terminates list */
+ if ( *(environ[ienv]) == '\000' ) break; /* double-check empty string */
+ strninit(environvar,environ[ienv],maxvarlen); /* max length displayed */
+ if ( strlen(environ[ienv]) > maxvarlen ) /* we truncated the variable */
+ strcat(environvar,"..."); /* so add an ellipsis */
+ unescape_url(environvar,0); /* convert all %xx's to chars */
+ environptr = strdetex(environvar,1); /* remove/replace any math chars */
+ strninit(environvar,environptr,maxvarlen); /*de-tex'ed/nomath environvar*/
+ environptr = strwrap(environvar,wraplen,-6); /* wrap long lines */
+ strninit(environvar,environptr,maxvarlen); /* line-wrapped environvar */
+ mimeprep(environvar); /* preprocess environvar string */
+ if ( strlen(environstr) + strlen(environvar) > maxenvlen ) break;
+ sprintf(environstr+strlen(environstr), /* display environment string */
+ " %2d. %s\\\\\n", ienv+1,environvar);
+ if ( msgfp!= NULL && msglevel>=9 )
+ fprintf(msgfp,"rastenviron> %2d. %.256s\n",
+ ienv+1,/*environ[ienv]*/environvar);
+ if ( strlen(environstr) >= 7200 ) break; /* don't overflow buffer */
+ } /* --- end-of-for(ienv) --- */
+strcat(environstr,"}}"); /* end {\text{...}} mode */
+rasterize_environ:
+ environsp = rasterize(environstr,size); /* rasterize environment string */
+/* --- return environment raster to caller --- */
+/*end_of_job:*/
+ return ( environsp ); /* return environment to caller */
+} /* --- end-of-function rastenviron() --- */
+
+
+/* ==========================================================================
+ * Function: rastmessage ( expression, size, basesp, arg1, arg2, arg3 )
+ * Purpose: handle \message
+ * --------------------------------------------------------------------------
+ * Arguments: expression (I/O) char ** to first char of null-terminated
+ * string immediately following \message
+ * and returning ptr immediately
+ * following last character processed.
+ * size (I) int containing 0-7 default font size
+ * basesp (I) subraster * to character (or subexpression)
+ * immediately preceding \mesasge
+ * (unused, but passed for consistency)
+ * arg1 (I) int unused
+ * arg2 (I) int unused
+ * arg3 (I) int unused
+ * --------------------------------------------------------------------------
+ * Returns: ( subraster * ) subraster ptr to rendered message image
+ * --------------------------------------------------------------------------
+ * Notes: o
+ * ======================================================================= */
+/* --- entry point --- */
+subraster *rastmessage ( char **expression, int size, subraster *basesp,
+ int arg1, int arg2, int arg3 )
+{
+/* -------------------------------------------------------------------------
+Allocations and Declarations
+-------------------------------------------------------------------------- */
+char *texsubexpr(), amsg[256]="\000"; /* message number text */
+int imsg = 0; /* default message number */
+char msg[4096];
+subraster *rasterize(), *messagesp=NULL; /* rasterize requested message */
+int strreplace(); /*replace SERVER_NAME in refmsgnum*/
+int reflevels = REFLEVELS; /* #topmost levels to match */
+char *urlprune(); /*prune referer_match in refmsgnum*/
+char *strdetex(); /* remove math chars from messages */
+char *http_host = getenv("HTTP_HOST"), /* http host for mimeTeX */
+ *server_name = getenv("SERVER_NAME"), /* server hosting mimeTeX */
+ *referer_match = (!isempty(http_host)?http_host: /*match http_host*/
+ (!isempty(server_name)?server_name:(NULL))); /* or server_name */
+/* -------------------------------------------------------------------------
+obtain message {amsg} argument
+-------------------------------------------------------------------------- */
+/* --- parse for {amsg} arg, and bump expression past it --- */
+*expression = texsubexpr(*expression,amsg,255,"{","}",0,0);
+/* --- interpret argument --- */
+if ( *amsg != '\000' ) { /* got amsg arg */
+ imsg = atoi(amsg); /* interpret as an int */
+ if ( imsg < 0 /* if too small */
+ || imsg > maxmsgnum ) /* or too big */
+ imsg = 0; } /* default to first message */
+/* --- retrieve requested message --- */
+strninit(msg,msgtable[imsg],4095); /* local copy of message */
+/* --- process as necessary --- */
+if ( imsg == refmsgnum) { /* urlncmp() failed to validate */
+ if ( reflevels > 0 ) /* have #levels to validate */
+ strreplace(msg,"SERVER_NAME", /* replace SERVER_NAME */
+ strdetex(urlprune(referer_match,reflevels),1),0); /*with referer_match*/
+ } /* --- end-of-switch(imsg) --- */
+/* --- rasterize requested message --- */
+messagesp = rasterize(msg,size); /* rasterize message string */
+/* --- return message raster to caller --- */
+/*end_of_job:*/
+ return ( messagesp ); /* return message to caller */
+} /* --- end-of-function rastmessage() --- */
+
+
+/* ==========================================================================
* Function: rastnoop ( expression, size, basesp, nargs, arg2, arg3 )
* Purpose: no op -- flush \escape without error
* --------------------------------------------------------------------------
@@ -10766,7 +12398,7 @@ while ( strreplace(editname,"....",NULL,
/* --- 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 ' ')*/
+ {strsqueeze(editname,1);} /* so flush leading / or \ (or ' ')*/
if ( *editname == '\000' ) goto end_of_job; /* no chars left in filename */
/* --- remove leading or embedded ../'s and ..\'s --- */
while ( strreplace(editname,"../",NULL,0) > 0 ) ; /* squeeze out ../'s */
@@ -10850,7 +12482,8 @@ while ( fgets(text,MAXLINESZ-1,fp) != (c
case 0: status = 1; break; /* no tag to look for */
case 1: /* looking for opening left */
if ( (tagp=strstr(text,tag1)) == NULL ) break; /*haven't found it yet*/
- strcpy(text,tagp+strlen(tag1)); /* shift out preceding text */
+ tagp += strlen(tag1); /* first char past tag */
+ strsqueezep(text,tagp); /*shift out preceding text and tag*/
tagnum = 2; /*now looking for closing right tag*/
case 2: /* looking for closing right */
if ( (tagp=strstr(text,tag2)) == NULL ) break; /*haven't found it yet*/
@@ -10959,7 +12592,7 @@ if ( istag ) /* only replacing tag in
{
/* --- preprocess filebuff --- */
if ( tagp2 != (char *)NULL ) /* apparently have ... */
- strcpy(filebuff,tagp2+tlen2); /* so get rid of leading ... */
+ {strsqueezep(filebuff,tagp2+tlen2);} /* remove ... */
if ( (flen = strlen(filebuff)) /* #chars currently in buffer */
> 0 ) /* we have non-empty buffer */
if (!isthischar(*(filebuff+flen-1),"\n\r")) /*no newline at end of file*/
@@ -11145,7 +12778,8 @@ static char timebuff[256]; /* date:time
time_t time_val = (time_t)(0); /* binary value returned by time() */
struct tm *tmstruct=(struct tm *)NULL, *localtime(); /* interpret time_val */
int year=0, hour=0,ispm=1, /* adjust year, and set am/pm hour */
- month=0, day=0; /* adjust day and month for delta */
+ month=0, day=0, /* adjust day and month for delta */
+ minute=0,second=0; /* minute and second not adjusted */
int tzadjust(); /* time zone adjustment function */
int daynumber(); /* #days since Jan 1, 1973 */
static char *daynames[] = { "Monday", "Tuesday", "Wednesday",
@@ -11166,6 +12800,8 @@ year = (int)(tmstruct->tm_year); /* loc
month = (int)(tmstruct->tm_mon) + 1; /* local copy of month, 1-12 */
day = (int)(tmstruct->tm_mday); /* local copy of day, 1-31 */
hour = (int)(tmstruct->tm_hour); /* local copy of hour, 0-23 */
+minute= (int)(tmstruct->tm_min); /* local copy of minute,0-59 */
+second= (int)(tmstruct->tm_sec); /* local copy of second,0-59 */
/* --- adjust year --- */
year += 1900; /* set century in year */
/* --- adjust for timezone --- */
@@ -11192,7 +12828,7 @@ switch ( ifmt )
default:
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"));
+ hour,minute,second,((ispm)?"pm":"am"));
break;
case 1: /* --- Saturday, March 5, 2005 --- */
sprintf(timebuff,"%s, %s %d, %d",
@@ -11201,11 +12837,15 @@ switch ( ifmt )
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"));
+ hour,minute,second,((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"));
+ hour,minute,second,((ispm)?"pm":"am"));
+ break;
+ case 4: /* --- 1231235959 (mmddhhmmss time as integer) --- */
+ sprintf(timebuff,"%d%02d%02d%02d%02d",
+ month,day,hour,minute,second);
break;
} /* --- end-of-switch(ifmt) --- */
end_of_job:
@@ -11337,6 +12977,273 @@ return ( (int)(ndays) ); /* #days back
/* ==========================================================================
+ * Function: strwrap ( s, linelen, tablen )
+ * Purpose: Inserts \n's and spaces in (a copy of) s to wrap lines
+ * at linelen and indent them by tablen.
+ * --------------------------------------------------------------------------
+ * Arguments: s (I) char * to null-terminated string
+ * to be wrapped.
+ * linelen (I) int containing maximum linelen
+ * between \\'s.
+ * tablen (I) int containing number of spaces to indent
+ * lines. 0=no indent. Positive means
+ * only indent first line and not others.
+ * Negative means indent all lines except first.
+ * --------------------------------------------------------------------------
+ * Returns: ( char * ) ptr to "line-wrapped" copy of s
+ * or "" (empty string) for any error.
+ * --------------------------------------------------------------------------
+ * Notes: o The returned copy of s has embedded \\'s as necessary
+ * to wrap lines at linelen. Any \\'s in the input copy
+ * are removed first. If (and only if) the input s contains
+ * a terminating \\ then so does the returned copy.
+ * o The returned pointer addresses a static buffer,
+ * so don't call strwrap() again until you're finished
+ * with output from the preceding call.
+ * o Modified for mimetex from original version written
+ * for mathtex (where \n in verbatim mode instead of \\
+ * produced linebreaks).
+ * ======================================================================= */
+/* --- entry point --- */
+char *strwrap ( char *s, int linelen, int tablen )
+{
+/* -------------------------------------------------------------------------
+Allocations and Declarations
+-------------------------------------------------------------------------- */
+static char sbuff[4096]; /* line-wrapped copy of s */
+char *sol = sbuff; /* ptr to start of current line*/
+char tab[32] = " "; /* tab string */
+int strreplace(); /* remove \n's */
+char *strchange(); /* add \n's and indent space */
+int finalnewline = (lastchar(s)=='\n'?1:0); /*newline at end of string?*/
+int istab = (tablen>0?1:0), /* init true to indent first line */
+ iswhite = 0; /* true if line break on whitespace*/
+int rhslen = 0, /* remaining right hand side length*/
+ thislen = 0, /* length of current line segment */
+ thistab = 0, /* length of tab on current line */
+ wordlen = 0; /* length to next whitespace char */
+/* -------------------------------------------------------------------------
+Make a clean copy of s
+-------------------------------------------------------------------------- */
+/* --- check input --- */
+*sbuff = '\000'; /* initialize in case of error */
+if ( isempty(s) ) goto end_of_job; /* no input */
+if ( tablen < 0 ) tablen = (-tablen); /* set positive tablen */
+if ( tablen >= linelen ) tablen = linelen-1; /* tab was longer than line */
+tab[min2(tablen,16)] = '\000'; /* null-terminate tab string */
+tablen = strlen(tab); /* reset to actual tab length */
+finalnewline = 0; /* turned off for mimetex version */
+/* --- start with copy of s --- */
+strninit(sbuff,s,3000); /* leave room for \n's and tabs */
+if ( linelen < 1 ) goto end_of_job; /* can't do anything */
+trimwhite(sbuff); /*remove leading/trailing whitespace*/
+strreplace(sbuff,"\n"," ",0); /* remove any original \n's */
+strreplace(sbuff,"\r"," ",0); /* remove any original \r's */
+strreplace(sbuff,"\t"," ",0); /* remove any original \t's */
+strreplace(sbuff,"\f"," ",0); /* remove any original \f's */
+strreplace(sbuff,"\v"," ",0); /* remove any original \v's */
+strreplace(sbuff,"\\\\"," ",0); /* remove any original \\'s */
+/* -------------------------------------------------------------------------
+Insert \\'s and spaces as needed
+-------------------------------------------------------------------------- */
+while ( 1 ) { /* till end-of-line */
+ /* --- init --- */
+ trimwhite(sol); /*remove leading/trailing whitespace*/
+ thislen = thistab = 0; /* no chars in current line yet */
+ if ( istab && tablen>0 ) { /* need to indent this line */
+ strchange(0,sol,tab); /* insert indent at start of line */
+ thistab = tablen; } /* line starts with whitespace tab */
+ if ( sol == sbuff ) istab = 1-istab; /* flip tab flag after first line */
+ sol += thistab; /* skip tab */
+ rhslen = strlen(sol); /* remaining right hand side chars */
+ if ( rhslen+thistab <= linelen ) break; /* no more \\'s needed */
+ if ( 0 && msgfp!=NULL && msglevel >= 99 ) {
+ fprintf(msgfp,"strwrap> rhslen=%d, sol=\"\"%s\"\"\n",rhslen,sol);
+ fflush(msgfp); }
+ /* --- look for last whitespace preceding linelen --- */
+ while ( 1 ) { /* till we exceed linelen */
+ wordlen = strcspn(sol+thislen," \t\n\r\f\v :;.,"); /*ptr to next white/break*/
+ if ( sol[thislen+wordlen] == '\000' ) /* no more whitespace in string */
+ goto end_of_job; /* so nothing more we can do */
+ if ( thislen+thistab+wordlen >= linelen ) /* next word won't fit */
+ if ( thislen > 0 ) break; /* but make sure line has one word */
+ thislen += (wordlen+1); } /* ptr past next whitespace char */
+ if ( thislen < 1 ) break; /* line will have one too-long word*/
+ /*sol[thislen-1] = '\n';*/ /* replace last space with newline */
+ /*sol += thislen;*/ /* next line starts after newline */
+ iswhite = (isthischar(sol[thislen-1],":;.,")?0:1); /*linebreak on space?*/
+ strchange(iswhite,sol+thislen-iswhite,"\\\\"); /* put \\ at end of line */
+ sol += (thislen+2-iswhite); /* next line starts after \\ */
+ } /* --- end-of-while(1) --- */
+end_of_job:
+ if ( finalnewline ) strcat(sbuff,"\\\\"); /* replace final newline */
+ return ( sbuff ); /* back with clean copy of s */
+} /* --- end-of-function strwrap() --- */
+
+
+/* ==========================================================================
+ * Function: strnlower ( s, n )
+ * Purpose: lowercase the first n chars of string s
+ * --------------------------------------------------------------------------
+ * Arguments: s (I/O) (char *)pointer to null-terminated string
+ * whose chars are to be lowercased
+ * n (I) int containing max number of chars to be
+ * lowercased (less than n will be lowercased
+ * if terminating '\000' found first)
+ * If n<=0 (or n>=strlen(s)) then the entire
+ * string s will be lowercased
+ * --------------------------------------------------------------------------
+ * Returns: ( char * ) s (always same as input)
+ * --------------------------------------------------------------------------
+ * Notes: o
+ * ======================================================================= */
+/* --- entry point --- */
+char *strnlower ( char *s, int n )
+{
+/* -------------------------------------------------------------------------
+lowercase s
+-------------------------------------------------------------------------- */
+char *p = s; /* save s for return to caller */
+if ( !isempty(s) ) /* check for valid input */
+ while ( *p != '\000' ) { /* lowercase each char till end */
+ *p = tolower(*p); /* lowercase this char */
+ if ( n > 0 ) /* only lowercase first n chars */
+ if ( --n < 1 ) break; /* quit when we're done */
+ p++; } /* proceed to next char */
+return ( s ); /* back to caller with s */
+} /* --- end-of-function strnlower() --- */
+
+
+/* ==========================================================================
+ * Function: urlprune ( url, n )
+ * Purpose: Prune http://abc.def.ghi.com/etc into abc.def.ghi.com
+ * (if n=2 only ghi.com is returned, or if n=-1 only "ghi")
+ * --------------------------------------------------------------------------
+ * Arguments: url (I) char * to null-terminated string
+ * containing url to be pruned
+ * n (i) int containing number of levels retained
+ * in pruned url. If n<0 its abs() is used,
+ * but the topmost level (usually .com, .org,
+ * etc) is omitted. That is, if n=2 would
+ * return "ghi.com" then n=-1 returns "ghi".
+ * n=0 retains all levels.
+ * --------------------------------------------------------------------------
+ * Returns: ( char * ) pointer to (static) null-terminated string
+ * containing pruned url with the first n
+ * top-level domain, e.g., for n=2,
+ * http://abc.def.ghi.com/etc returns ghi.com,
+ * or an empty string "\000" for any error
+ * --------------------------------------------------------------------------
+ * Notes: o
+ * ======================================================================= */
+/* --- entry point --- */
+char *urlprune ( char *url, int n )
+{
+/* -------------------------------------------------------------------------
+Allocations and Declarations
+-------------------------------------------------------------------------- */
+static char pruned[2048]; /* pruned url returned to caller */
+char *purl = /*NULL*/pruned; /* ptr to pruned, init for error */
+char *delim = NULL; /* delimiter separating components */
+char *strnlower(); /* lowercase a string */
+int istruncate = (n<0?1:0); /*true to truncate .com from pruned*/
+int ndots = 0; /* number of dots found in url */
+/* -------------------------------------------------------------------------
+prune the url
+-------------------------------------------------------------------------- */
+/* --- first check input --- */
+*pruned = '\000'; /* init for error */
+if ( isempty(url) ) goto end_of_job; /* missing input, so return NULL */
+if ( n < 0 ) n = (-n); /* flip n positive */
+if ( n == 0 ) n = 999; /* retain all levels of url */
+/* --- preprocess url --- */
+strninit(pruned,url,2032); /* copy url to our static buffer */
+strlower(pruned); /* lowercase it and... */
+trimwhite(pruned); /*remove leading/trailing whitespace*/
+/* --- first remove leading http:// --- */
+if ( (delim=strstr(pruned,"://")) != NULL ) /* found http:// or ftp:// etc */
+ if ( ((int)(delim-pruned)) <= 8 ) { /* make sure it's a prefix */
+ strsqueezep(pruned,delim+3); /* squeeze out leading http:// */
+ trimwhite(pruned); } /*remove leading/trailing whitespace*/
+/* --- next remove leading www. --- */
+if ( (delim=strstr(pruned,"www.")) != NULL ) /* found www. */
+ if ( ((int)(delim-pruned)) == 0 ) { /* make sure it's the leading chars*/
+ strsqueezep(pruned,delim+4); /* squeeze out leading www. */
+ trimwhite(pruned); } /*remove leading/trailing whitespace*/
+/* --- finally remove leading / and everything following it --- */
+if ( (delim=strchr(pruned,'/')) != NULL ) /* found first / */
+ *delim = '\000'; /* null-terminate url at first / */
+if ( isempty(pruned) ) goto end_of_job; /* nothing left in url */
+/* --- count dots from back of url --- */
+delim = pruned + strlen(pruned); /*ptr to '\000' terminating pruned*/
+while ( ((int)(delim-pruned)) > 0 ) { /* don't back up before first char */
+ delim--; /* ptr to preceding character */
+ if ( *delim != '.' ) continue; /* not a dot, so keep looking */
+ ndots++; /* count another dot found */
+ if ( istruncate ) { /* remove trailing .com */
+ istruncate = 0; /* don't truncate any more dots */
+ *delim = '\000'; /* truncate pruned url */
+ ndots = 0; } /* and reset dot count */
+ if ( ndots >= n ) { /* have all requested levels */
+ strsqueezep(pruned,delim+1); /* squeeze out leading levels */
+ break; } /* and we're done */
+ } /* --- end-of-while() --- */
+purl = pruned; /*completed okay, return pruned url*/
+end_of_job:
+ return ( purl ); /* back with pruned url */
+} /* --- end-of-function urlprune() --- */
+
+
+/* ==========================================================================
+ * Function: urlncmp ( url1, url2, n )
+ * Purpose: Compares the n topmost levels of two urls
+ * --------------------------------------------------------------------------
+ * Arguments: url1 (I) char * to null-terminated string
+ * containing url to be compared with url2
+ * url2 (I) char * to null-terminated string
+ * containing url to be compared with url1
+ * n (I) int containing number of top levels
+ * to compare, or 0 to compare them all.
+ * n<0 compares that many top levels excluding
+ * the last, i.e., for n=-1, xxx.com and xxx.org
+ * would be considered a match
+ * --------------------------------------------------------------------------
+ * Returns: ( int ) 1 if url's match, or
+ * 0 if not.
+ * --------------------------------------------------------------------------
+ * Notes: o
+ * ======================================================================= */
+/* --- entry point --- */
+int urlncmp ( char *url1, char *url2, int n )
+{
+/* -------------------------------------------------------------------------
+Allocations and Declarations
+-------------------------------------------------------------------------- */
+char *urlprune(), *prune=NULL, /* prune url's */
+ prune1[4096], prune2[4096]; /* pruned copies of url1,url2 */
+int ismatch = 0; /* true if url's match */
+/* -------------------------------------------------------------------------
+prune url's and compare the pruned results
+-------------------------------------------------------------------------- */
+/* --- check input --- */
+if ( isempty(url1) /*make sure both url1,url2 supplied*/
+|| isempty(url2) ) goto end_of_job; /* missing input, so return 0 */
+/* --- prune url's --- */
+prune = urlprune(url1,n); /* ptr to pruned version of url1 */
+if ( isempty(prune) ) goto end_of_job; /* some problem with url1 */
+strninit(prune1,prune,4064); /* local copy of pruned url1 */
+prune = urlprune(url2,n); /* ptr to pruned version of url2 */
+if ( isempty(prune) ) goto end_of_job; /* some problem with url2 */
+strninit(prune2,prune,4064); /* local copy of pruned url2 */
+/* --- compare pruned url's --- */
+if ( strcmp(prune1,prune2) == 0 ) /* pruned url's are identical */
+ ismatch = 1; /* signal match to caller */
+end_of_job:
+ return ( ismatch ); /*back with #matching url components*/
+} /* --- end-of-function urlncmp() --- */
+
+
+/* ==========================================================================
* Function: dbltoa ( dblval, npts )
* Purpose: Converts double to ascii, in financial format
* (e.g., comma-separated and negatives enclosed in ()'s).
@@ -12387,6 +14294,10 @@ Initialization
/* --- check input --- */
if ( irow<0 || irow>=height /* irow out-of-bounds */
|| icol<0 || icol>=width ) goto end_of_job; /* icol out-of-bounds */
+/* --- adjust maxturn for magstep --- */
+if ( 1 ) /* guard */
+ if ( magstep > 1 && magstep <= 10 ) /* sanity check */
+ maxturn *= magstep; /* factor in magnification */
/* --- starting bit -- see if we're following a fg (usual), or bg line --- */
bitval = getlongbit(bitmap,(icol+irow*width)); /* starting pixel (bg or fg)*/
fgval = bitval; bgval = (1-bitval); /* define "fg" as whatever bitval is*/
@@ -13051,7 +14962,7 @@ end_of_job:
* Function: aacolormap ( bytemap, nbytes, colors, colormap )
* Purpose: searches bytemap, returning a list of its discrete values
* in ascending order in colors[], and returning an "image"
- * of bytemap (where vales are replaced by colors[]
+ * of bytemap (where values are replaced by colors[]
* indexes) in colormap[].
* --------------------------------------------------------------------------
* Arguments: bytemap (I) intbyte * to bytemap containing
@@ -13430,6 +15341,9 @@ globals for gif and png callback functio
-------------------------------------------------------------------------- */
GLOBAL(raster,*bitmap_raster,NULL); /* use 0/1 bitmap image or */
GLOBAL(intbyte,*colormap_raster,NULL); /* anti-aliased color indexes */
+GLOBAL(int,raster_width,0); /* width of final/displayed image */
+GLOBAL(int,raster_height,0); /* height of final/displayed image */
+GLOBAL(int,raster_baseline,0); /* baseline of final/displayed image*/
/* --- anti-aliasing flags (needed by GetPixel() as well as main()) --- */
#ifdef AA /* if anti-aliasing requested */
#define ISAAVALUE 1 /* turn flag on */
@@ -13469,31 +15383,6 @@ STATIC logdata mimelog[]
#endif
;
-/* -------------------------------------------------------------------------
-messages
--------------------------------------------------------------------------- */
-static char *copyright = /* copyright, gnu/gpl notice */
- "+-----------------------------------------------------------------------+\n"
- "|mimeTeX vers 1.64, Copyright(c) 2002-2006, John Forkosh Associates, Inc|\n"
- "+-----------------------------------------------------------------------+\n"
- "| mimeTeX is free software, licensed to you under terms of the GNU/GPL, |\n"
- "| and comes with absolutely no warranty whatsoever. |\n"
- "+-----------------------------------------------------------------------+";
-static int maxmsgnum = 2; /* maximum msgtable[] index */
-static char *msgtable[] = { /* messages referenced by [index] */
- "\\red\\small\\rm\\fbox{\\array{" /* [0] is invalid_referer_msg */
- "Please~read~www.forkosh.com/mimetex.html\\\\and~install~mimetex.cgi~"
- "on~your~own~server.\\\\Thank~you,~John~Forkosh}}",
- "\\red\\small\\rm\\fbox{\\array{" /* [1] */
- "Please~provide~your~{\\tiny~HTTP-REFERER}~to~access~the~public\\\\"
- "mimetex~server.~~Or~please~read~~www.forkosh.com/mimetex.html\\\\"
- "and~install~mimetex.cgi~on~your~own~server.~~Thank~you,~John~Forkosh}}",
- "\\red\\small\\rm\\fbox{\\array{" /* [2] */
- "The~public~mimetex~server~is~for~testing.~~For~production,\\\\"
- "please~read~~www.forkosh.com/mimetex.html~~and~install\\\\"
- "mimetex.cgi~on~your~own~server.~~Thank~you,~John~Forkosh}}",
- NULL } ; /* trailer */
-
/* --- entry point --- */
int main ( int argc, char *argv[]
@@ -13529,14 +15418,22 @@ int type_raster(), type_bytemap(), /* sc
xbitmap_raster(); /* mime xbitmap output function */
/* --- http_referer --- */
char *referer = REFERER; /* http_referer must contain this */
+char *inputreferer = INPUTREFERER; /*http_referer's permitted to \input*/
+int reflevels = REFLEVELS, urlncmp(); /* cmp http_referer,server_name */
+int strreplace(); /* replace SERVER_NAME in errmsg */
+char *urlprune(); /* prune referer_match */
struct { char *referer; int msgnum; } /* http_referer can't contain this */
denyreferer[] = { /* referer table to deny access to */
#ifdef DENYREFERER
#include DENYREFERER /* e.g., {"",1}, for no referer */
#endif
{ NULL, -999 } }; /* trailer */
-char *http_referer = getenv("HTTP_REFERER"); /* referer using mimeTeX */
-int ishttpreferer = (http_referer==NULL?0:(*http_referer=='\000'?0:1));
+char *http_referer = getenv("HTTP_REFERER"), /* referer using mimeTeX */
+ *http_host = getenv("HTTP_HOST"), /* http host for mimeTeX */
+ *server_name = getenv("SERVER_NAME"), /* server hosting mimeTeX */
+ *referer_match = (!isempty(http_host)?http_host: /*match http_host*/
+ (!isempty(server_name)?server_name:(NULL))); /* or server_name */
+int ishttpreferer = (isempty(http_referer)?0:1);
int isstrstr(); /* search http_referer for referer */
int isinvalidreferer = 0; /* true for inavlid referer */
int norefmaxlen = NOREFMAXLEN; /*max query_string len if no referer*/
@@ -13551,6 +15448,7 @@ char *gif_outfile = (char *)NULL, /* gif
cachefile[256] = "\000", /* full path and name to cache file*/
*md5str(); /* md5 has of expression */
int maxage = 7200; /* max-age is two hours */
+int valign = (-9999); /*Vertical-Align:baseline-(height-1)*/
/* --- pbm/pgm (-g switch) --- */
int ispbmpgm = 0; /* true to write pbm/pgm file */
int type_pbmpgm(), ptype=0; /* entry point, graphic format */
@@ -13563,16 +15461,22 @@ int aalowpass(), aapnm(), /*lowpass fil
int ncolors=2, /* #colors (2=b&w) */
aacolormap(); /* build colormap from bytemap */
int ipattern; /*patternnumcount[] index diagnostic*/
+/* --- advertisement preprocessing --- */
+int advertisement(), crc16(); /*wrap expression in advertisement*/
+char *adtemplate = NULL; /* usually use default message */
+char *host_showad = HOST_SHOWAD; /* show ads only on this host */
/* --- messages --- */
char logfile[256] = LOGFILE, /*log queries if msglevel>=LOGLEVEL*/
cachelog[256] = CACHELOG; /* cached image log in cachepath/ */
char *timestamp(); /* time stamp for logged messages */
+char *strdetex(); /* remove math chars from messages */
int logger(); /* logs environ variables */
int ismonth(); /* check argv[0] for current month */
char *progname = (argc>0?argv[0]:"noname"); /* name program executed as */
char *dashes = /* separates logfile entries */
"--------------------------------------------------------------------------";
-char *invalid_referer_msg = msgtable[0]; /* msg to invalid http_referer */
+char *invalid_referer_msg = msgtable[invmsgnum]; /*msg to invalid referer*/
+char *invalid_referer_match = msgtable[refmsgnum]; /*referer isn't host*/
/* -------------------------------------------------------------------------
initialization
-------------------------------------------------------------------------- */
@@ -13581,9 +15485,28 @@ initialization
system(SYSTEM);
#endif
/* --- set global variables --- */
+daemonlevel++; /* signal other funcs to reset */
msgfp = stdout; /* for comamnd-line mode output */
isss = issupersampling; /* set supersampling flag */
+isemitcontenttype = 1; /* true to emit mime content-type */
+iscachecontenttype = 0; /* true to cache mime content-type */
+*contenttype = '\000'; /* reset content-type:, etc. cache */
+isnomath = 0; /* true to inhibit math mode */
+seclevel = SECURITY; /* overall security level */
+inputseclevel = INPUTSECURITY; /* security level for \input{} */
+counterseclevel = COUNTERSECURITY; /* security level for \counter{} */
+environseclevel = ENVIRONSECURITY; /* security level for \environ */
+ninputcmds = 0; /* reset count of \input commands */
+exitstatus=0; errorstatus=ERRORSTATUS; /* reset exit/error status */
+iscaching = ISCACHING; /* true if caching images */
+if ( iscaching ) { /* images are being cached */
+ strcpy(cachepath,CACHEPATH); /* relative path to cached files */
+ if ( *cachepath == '%' ) { /* leading % signals cache headers */
+ iscachecontenttype = 1; /* signal caching mime content-type*/
+ strsqueeze(cachepath,1); } } /* and squeeze out leading % char */
gifSize = 0; /* signal that image not in memory */
+fgred=FGRED; fggreen=FGGREEN; fgblue=FGBLUE; /* default foreground colors */
+bgred=BGRED; bggreen=BGGREEN; bgblue=BGBLUE; /* default background colors */
shrinkfactor = shrinkfactors[NORMALSIZE]; /* set shrinkfactor */
for ( ipattern=1; ipattern<=51; ipattern++ )
patternnumcount0[ipattern] = patternnumcount1[ipattern] = 0;
@@ -13591,17 +15514,23 @@ for ( ipattern=1; ipattern<=51; ipattern
* check QUERY_STRING query for expression overriding command-line arg
* ------------------------------------------------------------------- */
if ( query != NULL ) /* check query string from environ */
- if ( strlen(query) >= 1 ) /* caller gave us a query string */
- { strncpy(expression,query,MAXEXPRSZ); /* so use it as expression */
- expression[MAXEXPRSZ] = '\000'; /* make sure it's null terminated */
- isquery = 1; } /* and set isquery flag */
-if ( !isquery ) /* empty query string */
- { char *host = getenv("HTTP_HOST"), /* additional getenv("") results */
- *name = getenv("SERVER_NAME"), *addr = getenv("SERVER_ADDR");
- if ( host!=NULL || name!=NULL || addr!=NULL ) /* assume http query */
- { isquery = 1; /* set flag to signal query */
- strcpy(expression,"\\red\\small\\fbox{\\rm~no~query~string}"); }
- isqempty = 1; /* signal empty query string */
+ if ( strlen(query) >= 1 ) { /* caller gave us a query string */
+ strncpy(expression,query,MAXEXPRSZ); /* so use it as expression */
+ expression[MAXEXPRSZ] = '\000'; /* make sure it's null terminated */
+ if ( 0 ) /*true to remove leading whitespace*/
+ while ( isspace(*expression) && *expression!='\000' )
+ {strsqueeze(expression,1);} /* squeeze out white space */
+ isquery = 1; } /* and set isquery flag */
+if ( !isquery ) { /* empty query string */
+ char *host = getenv("HTTP_HOST"), /* additional getenv("") results */
+ *name = getenv("SERVER_NAME"), *addr = getenv("SERVER_ADDR");
+ if ( host!=NULL || name!=NULL || addr!=NULL ) { /* assume http query */
+ isquery = 1; /* set flag to signal query */
+ if ( exitstatus == 0 ) exitstatus = errorstatus; /* signal error */
+ strcpy(expression, /* and give user an error message */
+ "\\red\\small\\rm\\fbox{\\begin{gather}\\LaTeX~expression~not~supplied"
+ "\\\\i.e.,~no~?query\\_string~given~to~mimetex.cgi\\end{gather}}"); }
+ isqempty = 1; /* signal empty query string */
} /* --- end-of-if(!isquery) --- */
/* ---
* process command-line input args (if not a query)
@@ -13656,10 +15585,10 @@ if ( !isquery /* don't have an html q
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': if ( argnum < argc ) msglevel = atoi(argv[argnum]); break;
case 'o': istransparent = (istransparent?0:1); argnum--; break;
case 'q': isqforce = 1; argnum--; break;
- case 's': size = atoi(argv[argnum]); break;
+ case 's': if ( argnum < argc ) size = atoi(argv[argnum]); break;
} /* --- end-of-switch(flag) --- */
} /* --- end-of-if(*argv[argnum]=='-') --- */
else /* expression if arg not a -flag */
@@ -13733,7 +15662,7 @@ if ( isquery ) { /* must be