mathtex.c 363 KB
Newer Older
Fjen Undso's avatar
Fjen Undso committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/****************************************************************************
 *
 * Copyright(c) 2007-2012, John Forkosh Associates, Inc. All rights reserved.
 *           http://www.forkosh.com   mailto: john@forkosh.com
 * --------------------------------------------------------------------------
 * This file is part of mathTeX, which is free software. You may redistribute
 * and/or modify it under the terms of the GNU General Public License,
 * version 3 or later, as published by the Free Software Foundation.
 *      MathTeX 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.
 *      By using mathTeX, you warrant that you have read, understood and
 * agreed to these terms and conditions, and that you possess the legal
 * right and ability to enter into this agreement and to use mathTeX
 * in accordance with it.
 *      Your mathtex.zip distribution 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	MathTeX, licensed under the gpl, lets you easily embed
 *		LaTeX math in your html pages or blogs, wikis, bb's, etc.
 *		It submits a LaTeX math expression to latex, and
 *		immediately emits the corresponding gif image, rather than
 *		the usual TeX dvi.
 *		For example,
 *		 <img src="/cgi-bin/mathtex.cgi?\int_{-\infty}^xe^{-t^2}dt"
 *		  alt="" border=0 align=middle>
 *		immediately generates the corresponding gif image,
 *		displaying the rendered expression wherever you put
 *		that <img> tag.
 *		But there's no inherent need to repeatedly write
 *		cumbersome <img> tags as illustrated above.  You can write
 *		your own custom tags, or write a wrapper script around
 *		mathTeX to simplify the notation.
 *		See http://www.forkosh.com/mathtex.html for more information.
 *
 * Functions:	=============================================================
 *		main(argc,argv)     latex's math expression and emits gif/png
 *		mathtex(expression,filename)  create image of math expression
 *		setpaths(method)     set paths for latex,dvipng,dvips,convert
 *		isnotfound(filename)  check "... 2>.err" file for "not found"
 *		validate(expression) remove illegal \commands from expression
 *		advertisement(expression,mode)  wrap expression in ad message
 *		mathlog(expression,filename)          write entry in log file
 *		makepath(oath,name,extension)   construct path/name.extension
 *		isfexists(filename)      check whether or not filename exists
 *		isdexists(dirname)      check whether or not directory exists
 *		whichpath(program,nlocate)         determines path to command
 *		locatepath(program,nlocate) tries locate if whichpath() fails
 *		presentwd(nsub)                  determines pwd, nsub dirs up
 *		rrmdir(path)                                       rm -r path
 *		rewritecache(cachefile,maxage) write cachefile with imageinfo
 *		emitcache(cachefile,maxage,isbuffer) dump cachefile to stdout
 *		readcachefile(cachefile,buffer)    read cachefile into buffer
 *		crc16(s)                               16-bit crc of string s
 *		md5str(instr)                      md5 hash library functions
 *		unescape_url(url)             xlate all %xx's in url to ascii
 *		x2c(what)    xlate a single hex "xx" to equivalent ascii char
 *		timelimit(command,killtime)   throttle command after killtime
 *		getdirective(string,directive,iscase,isvalid,nargs,args) \dir
 *		mathprep(expression)           preprocessor for mathTeX input
 *		strwstr(string,substr,white,sublen)     find substr in string
 *		strreplace(string,from,to,iscase,nreplace)  change from to to
 *		strchange(nfirst,from,to)   change nfirst chars of from to to
 *		isstrstr(string,snippets,iscase)    is any snippet in string?
 *		nomath(s)          removes/replaces any LaTeX math chars in s
 *		strwrap(s,linelen,tablen)insert \n's and spaces to wrap lines
 *		strpspn(s,reject,segment) non-{[()]} chars of s not in reject
 *		strqspn(s,q,isunescape) find matching " or ' in quoted 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
 *		timewaitfor(whundredths)delay execution for hundredths of sec
 *		timestamp(tzdelta,ifmt)       returns current date:time stamp
 *		tzadjust(tzdelta,year,month,day,hour)   adjust time for tzone
 *		daynumber(year,month,day)     #calendar days from Jan 1, 1973
 *		calendar(year,month,day)    formats one-month calendar string
 *		emitembedded(imagenum,isquery)  emit embedded image to stdout
 *		embeddedimages(imagenum,nbytes,imgtype)   embedded gif or png
 *
 * Source:	mathtex.c
 *
 * --------------------------------------------------------------------------
 * Notes      o	See the comment block above each function
 *		for more information about it.
 *	      o	MathTeX runs only under Unix-like operating systems.
 *		To compile mathTeX
 *		   cc mathtex.c -DLATEX=\"$(which latex)\" \
 *		   -DDVIPNG=\"$(which dvipng)\" \
 *		   -DDVIPS=\"$(which dvips)\" \
 *		   -DCONVERT=\"$(which convert)\" \
 *		   -o mathtex.cgi
 *		And see discussion of optional -D switches below.
 *		To install mathTeX
 *		   (a) mv mathtex.cgi to your cgi-bin/ directory
 *		       and chmod its permissions as necessary
 *		   (b) mkdir cgi-bin/mathtex  which is the cache directory
 *		       and chmod its permissions so mathtex.cgi can rw it.
 *		See http://www.forkosh.com/mathtex.html for more information.
 *	      o	The timelimit() code is adapted from
 *		   http://devel.ringlet.net/sysutils/timelimit/
 *		Compile with -DTIMELIMIT=\"$(which timelimit)\" to use an
 *		installed copy of that program rather than the built-in code.
 *	      o	Some program parameters adjustable by optional -D switches on
 *		mathTeX's compile line are illustrated with default values...
 *		-DLATEX=\"/usr/share/texmf/bin/latex\"  path to LaTeX program
 *		-DPDFLATEX=\"/usr/share/texmf/bin/pdflatex\" path to pdflatex
 *		-DDVIPNG=\"/usr/share/texmf/bin/dvipng\"       path to dvipng
 *		-DDVIPS=\"/usr/share/texmf/bin/dvips\"          path to dvips
 *		-DPS2EPSI=\"/usr/bin/ps2epsi\"                path to ps2epsi
 *		-DCONVERT=\"/usr/bin/convert\"                path to convert
 *		-DCACHE=\"mathtex/\"     relative path to mathTeX's cache dir
 *		-DTIMELIMIT=\"/usr/local/bin/timelimit\"    path to timelimit
 *		-WARNTIME=10   #secs latex can run using standalone timelimit
 *		-KILLTIME=10   #secs latex can run using built-in timelimit()
 *		-DGIF                                         emit gif images
 *		-DPNG                                         emit png images
 *		-DDISPLAYSTYLE                            \[ \displaystyle \]
 *		-DTEXTSTYLE                                    $ \textstyle $
 *		-DPARSTYLE       paragraph mode, supply your own $ $ or \[ \]
 *		-DFONTSIZE=5           1=\tiny,...,5=\normalsize,...,10=\Huge
 *		-DUSEPACKAGE=\"filename\"       file containing \usepackage's
 *		-DNEWCOMMAND=\"filename\"       file containing \newcommand's
 *		-DDPI=\"120\"        dvipng -D DPI  parameter (as \"string\")
 *		-DGAMMA=\"2.5\"   dvipng --gamma GAMMA  param (as \"string\")
 *		-DNOQUIET     -halt-on-error (default reply q(uiet) to error)
 *		-DTEXTAREANAME=\"formdata\"   <textarea name=...> in a <form>
 *		-DREFERER=\"\"         comma-separated list of valid referers
 *		-DMAXINVALID=0     max length expression from invalid referer
 *		-DADFREQUENCY=0  one request out of n displayed along with ad
 *		-DADVERTISEMENT=\"filename\"      file containing ad template
 *		See http://www.forkosh.com/mathtex.html for more information.
 *
 * --------------------------------------------------------------------------
 * Revision History:
 * See http://www.forkosh.com/mathtexchangelog.html for more information.
 * 10/11/07	J.Forkosh	Installation Version 1.00.
 * 02/17/08	J.Forkosh	Version 1.01
 * 03/06/09	J.Forkosh	Version 1.02
 * 08/14/09	J.Forkosh	Version 1.03
 * 04/06/11	J.Forkosh	Version 1.04
 * 11/15/11	J.Forkosh	Version 1.05
 * 04/07/14	J.Forkosh	Most recent change (see REVISIONDATE below)
 *
 ****************************************************************************/

/* -------------------------------------------------------------------------
Program ID
-------------------------------------------------------------------------- */
#define	VERSION "1.05"			/* mathTeX version number */
#define	REVISIONDATE "07 April 2014"	/* date of most recent revision */
#define	COPYRIGHTDATE "2007-2014"	/* copyright date */

/* -------------------------------------------------------------------------
Standard header files
-------------------------------------------------------------------------- */
/* ---
 * standard headers
 * ---------------- */
#include <stdio.h>
#include <stdlib.h>
#define _GNU_SOURCE
#include <string.h>
char	*strcasestr();			/* non-standard extension */
#include <ctype.h>
#include <time.h>
extern	char **environ;			/* for \environment directive */

/* -------------------------------------------------------------------------
Information adjustable by -D switches on compile line
-------------------------------------------------------------------------- */
/* ---
 * executable paths (e.g., path/latex including filename of executable image)
 * ----------------------------------------------------------------------- */
/* --- determine which switches have been explicitly specified --- */
#if defined(LATEX)
  #define ISLATEXSWITCH 1		/* have -DLATEX=\"path/latex\" */
#else
  #define ISLATEXSWITCH 0		/* no -DLATEX switch */
  #define LATEX "/usr/share/texmf/bin/latex" /* default path to latex */
#endif
#if defined(PDFLATEX)
  #define ISPDFLATEXSWITCH 1		/* have -PDFDLATEX=\"path/latex\" */
#else
  #define ISPDFLATEXSWITCH 0		/* no -PDFDLATEX switch */
  #define PDFLATEX "/usr/share/texmf/bin/pdflatex" /* default pdflatex path*/
#endif
#if defined(DVIPNG)
  #define ISDVIPNGSWITCH 1		/* have -DDVIPNG=\"path/dvipng\" */
#else
  #define ISDVIPNGSWITCH 0		/* no -DDVIPNG switch */
  #define DVIPNG "/usr/share/texmf/bin/dvipng"/* default path to dvipng */
#endif
#if defined(DVIPS)
  #define ISDVIPSSWITCH 1		/* have -DDVIPS=\"path/dvips\" */
#else
  #define ISDVIPSSWITCH 0		/* no -DDVIPS switch */
  #define DVIPS "/usr/share/texmf/bin/dvips" /* default path to dvips */
#endif
#if defined(PS2EPSI)
  #define ISPS2EPSISWITCH 1		/* have -DPS2EPSI=\"path/ps2epsi\" */
#else
  #define ISPS2EPSISWITCH 0		/* no -DPS2EPSI switch */
  #define PS2EPSI "/usr/bin/ps2epsi"	/* default path to ps2epsi */
#endif
#if defined(CONVERT)
  #define ISCONVERTSWITCH 1		/* have -DCONVERT=\"path/convert\" */
#else
  #define ISCONVERTSWITCH 0		/* no -DCONVERT switch */
  #define CONVERT "/usr/bin/convert"	/* default path to convert */
#endif
#if defined(TIMELIMIT)
  #define ISTIMELIMITSWITCH 1	    /* have -DTIMELIMIT=\"path/timelimit\" */
#else
  #define ISTIMELIMITSWITCH 0		/* no -DTIMELIMIT switch */
  #define TIMELIMIT "/usr/local/bin/timelimit" /* default path to timelimit*/
#endif
#if !defined(WAIT)			/* -DWAIT=\"50\" for half-second */
  #define WAIT "\000"			/* default to no wait */
#endif
#if !defined(NOWAIT)			/* comma-separated list of referer's*/
  #define NOWAIT "\000"			/* default to everyone waits */
#endif
/* --- paths, as specified by -D switches, else from whichpath() --- */
static	char latexpath[256] = LATEX,    pdflatexpath[256] = PDFLATEX,
	dvipngpath[256] = DVIPNG,       dvipspath[256] = DVIPS,
	ps2epsipath[256] = PS2EPSI,     convertpath[256] = CONVERT,
	timelimitpath[256] = TIMELIMIT;
/* --- source of path info: 0=default, 1=switch, 2=which, 3=locate --- */
static	int  islatexpath=ISLATEXSWITCH, ispdflatexpath=ISPDFLATEXSWITCH,
	isdvipngpath=ISDVIPNGSWITCH,    isdvipspath=ISDVIPSSWITCH,
	isps2epsipath=ISPS2EPSISWITCH,  isconvertpath=ISCONVERTSWITCH,
	istimelimitpath=ISTIMELIMITSWITCH;
/* ---
 * home path pwd of running executable image
 * ------------------------------------------ */
static	char homepath[256] = "\000";	/* path to executable image */
/* ---
 * cache path -DCACHE=\"path/\" specifies directory
 * ------------------------------------------------ */
#if !defined(CACHE)
  #define CACHE "mathtex/"		/* relative to mathtex.cgi */
#endif
#if !defined(CACHELOG)
  #define CACHELOG "mathtex.log"	/* default cache log file */
#endif
#define	MAXAGE 7200			/* maxage in cache is 7200 secs */
static	int  iscaching = 1;		/* true if caching images */
static	char cachepath[256] = CACHE;	/* path to cached image files */
/* ---
 * working directory for temp files -DWORK=\"path/\"
 * ------------------------------------------------- */
#if !defined(WORK)
  #define WORK "\000"			/* relative to mathtex.cgi */
#endif
static	char workpath[256] = WORK;	/* path to temp file working dir */
/* ---
 * latex method info specifying latex,pdflatex
 * ------------------------------------------- */
#if    ISLATEXSWITCH==0 && ISPDFLATEXSWITCH==1
  #define LATEXMETHOD 2
#elif !defined(LATEXMETHOD)
  #define LATEXMETHOD 1
#endif
static	int  latexmethod = LATEXMETHOD;	/* 1=latex, 2=pdflatex */
static	int  ispicture = 0;		/* true for picture environment */
/* ---
 * image method info specifying dvipng or dvips/convert
 * use dvipng if -DDVIPNG supplied (or -DDVIPNGMETHOD specified),
 * else use dvips/convert if -DDVIPS supplied (or -DDVIPSMETHOD specified)
 * ----------------------------------------------------------------------- */
#if    defined(DVIPNGMETHOD) || ISDVIPNGSWITCH==1
  #define IMAGEMETHOD 1
#elif  defined(DVIPSMETHOD)  || (ISDVIPSSWITCH==1 && ISDVIPNGSWITCH==0)
  #define IMAGEMETHOD 2
#elif !defined(IMAGEMETHOD)
  #define IMAGEMETHOD 1
#endif
static	int  imagemethod = IMAGEMETHOD;	/* 1=dvipng, 2=dvips/convert */
/* ---
 * image type info specifying gif, png
 * ----------------------------------- */
#if defined(GIF)
  #define IMAGETYPE 1
#endif
#if defined(PNG)
  #define IMAGETYPE 2
#endif
#if !defined(IMAGETYPE)
  #define IMAGETYPE 1
#endif
static	int  imagetype = IMAGETYPE;	/* 1=gif, 2=png */
static	char *extensions[] = { NULL,	/* image type file .extensions */
  "gif", "png", NULL };
/* ---
 * \[ \displaystyle \]  or  $ \textstyle $  or  \parstyle
 * ------------------------------------------------------ */
#if defined(DISPLAYSTYLE)
  #define MATHMODE 0
#endif
#if defined(TEXTSTYLE)
  #define MATHMODE 1
#endif
#if defined(PARSTYLE)
  #define MATHMODE 2
#endif
#if !defined(MATHMODE)
  #define MATHMODE 0
#endif
static	int  mathmode = MATHMODE; /* 0=display 1=text 2=paragraph */
/* ---
 * font size info 1=\tiny ... 10=\Huge
 * ----------------------------------- */
#if !defined(FONTSIZE)
  #define FONTSIZE 5
#endif
static	int  fontsize = FONTSIZE;	/* 1=tiny ... 10=Huge */
static	char *sizedirectives[] = { NULL, /* fontsize directives */
  "\\tiny", "\\scriptsize", "\\footnotesize", "\\small", "\\normalsize",
  "\\large", "\\Large", "\\LARGE", "\\huge", "\\Huge", NULL };
/* ---
 * dpi/density info for dvipng/convert
 * ----------------------------------- */
#if !defined(DPI)
  #define DPI "120"
#endif
static	char density[256] = DPI;	/*-D/-density arg for dvipng/convert*/
/* ---
 * default -gamma for convert is 0.5, or --gamma for dvipng is 2.5
 * --------------------------------------------------------------- */
#define	DVIPNGGAMMA "2.5"		/* default gamma for dvipng */
#define	CONVERTGAMMA "0.5"		/* default gamma for convert */
#if !defined(GAMMA)
  #define ISGAMMA 0			/* no -DGAMMA=\"gamma\" switch */
  #if IMAGEMETHOD == 1			/* for dvipng... */
    #define GAMMA DVIPNGGAMMA		/* ...default gamma is 2.5 */
  #elif IMAGEMETHOD == 2		/* for convert... */
    #define GAMMA CONVERTGAMMA		/* ...default gamma is 0.5 */
  #else					/* otherwise... */
    #define GAMMA "1.0"			/* ...default gamma is 1.0 */
  #endif
#else
  #define ISGAMMA 1			/* -DGAMMA=\"gamma\" supplied */
#endif
static	char gamma[256] = GAMMA;	/* -gamma arg for convert() */
/* ---
 * latex -halt-on-error or quiet
 * ----------------------------- */
#if defined(QUIET)
  #define ISQUIET 99999			/* reply q */
#elif  defined(NOQUIET)
  #define ISQUIET 0			/* reply x (-halt-on-error) */
#elif  defined(NQUIET)
  #define ISQUIET NQUIET		/* reply NQUIET <Enter>'s then x */
#else
  #define ISQUIET 3			/* default reply 3 <Enter>'s then x*/
#endif
static	int  isquiet = ISQUIET;		/* >99=quiet, 0=-halt-on-error */
/* ---
 * emit depth below baseline (for vertical centering)
 * -------------------------------------------------- */
#if defined(DEPTH)
  #define ISDEPTH 1
#else
  #define ISDEPTH 0
#endif
static	int  isdepth = ISDEPTH;		/* true to emit depth */
/* ---
 * timelimit -tWARNTIME -TKILLTIME
 * ------------------------------- */
#if !defined(KILLTIME)			/* no -DKILLTIME given... */
  #define NOKILLTIMESWITCH		/* ...remember that fact below */
  #if ISTIMELIMITSWITCH == 0		/* standalone timelimit disabled */
    #if defined(WARNTIME)		/* have WARNTIME but not KILLTIME */
      #define KILLTIME (WARNTIME)	/* so use WARNTIME for KILLTIME */
    #else				/* neither WARNTIME nor KILLTIME */
      #define KILLTIME (10)		/* default for built-in timelimit()*/
      /*#define KILLTIME (0)*/		/* disable until debugged */
    #endif
  #else					/* using standalone timelimit */
    #define KILLTIME (1)		/* always default -T1 killtime */
  #endif
#endif
#if !defined(WARNTIME)			/*no -DWARNTIME given for timelimit*/
  #if ISTIMELIMITSWITCH == 0		/* and no -DTIMELIMIT path either */
    #define WARNTIME (-1)		/* so standalone timelimit disabled*/
  #else					/*have path to standalone timelimit*/
    #if !defined(NOKILLTIMESWITCH)	/* have KILLTIME but not WARNTIME */
      #define WARNTIME (KILLTIME)	/* so use KILLTIME for WARNTIME */
      #undef  KILLTIME			/* but not for standalone killtime */
      #define KILLTIME (1)		/* default -T1 killtime instead */
    #else				/* neither KILLTIME nor WARNTIME */
      #define WARNTIME (10)		/* so default -t10 warntime */
    #endif
  #endif
#endif
static	int  warntime=WARNTIME, killtime=KILLTIME; /* -twarn -Tkill values */
/* ---
 * compile (or not) built-in timelimit() code
 * ------------------------------------------ */
#if ISTIMELIMITSWITCH==0 && KILLTIME>0	/*no -DTIMELIMIT, but have KILLTIME*/
  #define ISCOMPILETIMELIMIT 1		/* so we need built-in code */
#else
  #define ISCOMPILETIMELIMIT 0		/* else we don't need built-in code*/
#endif
static	int  iscompiletimelimit=ISCOMPILETIMELIMIT; /* 1=use timelimit() */
#if ISCOMPILETIMELIMIT			/*header files, etc for timelimit()*/
  /* --- header files for timelimit() --- */
  #include <sys/signal.h>
  #include <sys/wait.h>
  #include <unistd.h>
  #include <signal.h>
  #include <sysexits.h>
  /*#define EX_OSERR	71*/		/* system error (e.g., can't fork) */
  #define HAVE_SIGACTION
  /* --- global variables for timelimit() --- */
  volatile int	fdone, falarm, fsig, sigcaught;
#endif
/* ---
 * additional latex \usepackage{}'s
 * -------------------------------- */
static	int  npackages = 0;		/* number of additional packages */
static	char packages[9][128];		/* additional package names */
static	char packargs[9][128];		/* optional arg for package */
/* ---
 * <textarea name=TEXTAREANAME ...> in a <form>
 * -------------------------------------------- */
#if !defined(TEXTAREANAME)
  #define TEXTAREANAME "formdata"
#endif
/* ---
 * comma-separated list of HTTP_REFERER's allowed/denied to use mathTeX
 * -------------------------------------------------------------------- */
#if !defined(REFERER)
  #define REFERER "\000"
#endif
#if !defined(DENYREFERER)
  #define DENYREFERER "\000"
#endif
#if !defined(MAXINVALID)		/* longest length expression */
  #define MAXINVALID 0			/* from an invalid referer */
#endif					/* that will be rendered w/o error */
/* ---
 * time zone delta t (in hours)
 * ---------------------------- */
#if !defined(TZDELTA)
  #define TZDELTA 0
#endif
/* ---
 * default uses locatepath() if whichpath() fails
 * ---------------------------------------------- */
#if !defined(NOWHICH)
  #define ISWHICH 1
#else
  #define ISWHICH 0
#endif
#if !defined(NOLOCATE)
  #define ISLOCATE 1
#else
  #define ISLOCATE 0
#endif
/* ---
 * one image in every ADFREQUENCY will be wrapped inside "advertisement"
 * --------------------------------------------------------------------- */
#if !defined(ADFREQUENCY)
  #define ADFREQUENCY 0			/* never show advertisement if 0 */
#endif
static	int  adfrequency = ADFREQUENCY;	/* advertisement frequency */
#if !defined(HOST_SHOWAD)
  #define HOST_SHOWAD "\000"		/* show ads on all hosts */
#endif
/* ---
 * debugging and error reporting
 * ----------------------------- */
#if !defined(MSGLEVEL)
  #define MSGLEVEL 1
#endif
#if !defined(MAXMSGLEVEL)
  #define MAXMSGLEVEL 999999
#endif
static	int  msglevel = MSGLEVEL;	/* message level for verbose/debug */
static	FILE *msgfp = NULL;		/* output in command-line mode */
char	*strwrap();			/* help format debugging messages */
#define	showmsg(showlevel,label,data)	/* default message format */ \
	if ( msgfp!=NULL && msglevel>=(showlevel) ) { \
	  fprintf(msgfp,(strlen(label)+strlen(data)<64? \
	  "\nmathTeX> %s: %s\n" : "\nmathTeX> %s:\n         %s\n"), \
	  (label),strwrap((data),64,-9)); fflush(msgfp); } else
static	int  msgnumber = 0;		/* embeddedimages() in query mode */
#define	MAXEMBEDDED 15			/* 1...#embedded images available */
#define	TESTMESSAGE 1			/* msg# for mathTeX test message */
#define	UNKNOWNERROR 2			/* msg# for non-specific error */
#define	CACHEFAILED 3			/* msg# if mkdir cache failed */
#define	MKDIRFAILED 4			/* msg# if mkdir tempdir failed */
#define	CHDIRFAILED 5			/* msg# if chdir tempdir failed */
#define	FOPENFAILED 6			/* msg# if fopen(latex.tex) failed */
#define	SYLTXFAILED 7			/* msg# if system(latex) failed */
#define	LATEXFAILED 8			/* msg# if latex failed */
#define	SYPNGFAILED 9			/* msg# if system(dvipng) failed */
#define	DVIPNGFAILED 10			/* msg# if dvipng failed */
#define	SYPSFAILED 11			/* msg# if system(dvips) failed */
#define	DVIPSFAILED 12			/* msg# if dvips failed */
#define	SYCVTFAILED 13			/* msg# if system(convert) failed */
#define	CONVERTFAILED 14		/* msg# if convert failed */
#define	EMITFAILED 15			/* msg# if emitcache() failed */
static	char *embeddedtext[] = { NULL,	/* text of embedded image messages */
  "(1) mathTeX test message:\\"		/* msg#1 mathTeX "okay" test */
  "cgi program running okay.",
  "(2) mathTeX failed: probably due to\\" /* msg#2 general error message */
  "bad paths, permissions, or installation.",
  "(3) Can't mkdir cgi-bin/mathtex/\\"	/* msg#3 can't create cache dir */
  "cache directory: check permissions.",
  "(4) Can't mkdir cgi-bin/tempnam/\\"	/* msg#4 */
  "work directory: check permissions.",
  "(5) Can't cd cgi-bin/tempnam/\\"	/* msg#5 */
  "work directory: check permissions.",
  "(6) Can't fopen(\"latex.tex\") file:\\" /* msg#6 */
  "check permissions.",
  "(7) Can't run latex program:\\"	/* msg#7 */
  "check -DLATEX=\"path\", etc.",
  "(8) latex ran but failed:\\"		/* msg#8 */
  "check your input expression.",
  "(9) Can't run dvipng program:\\"	/* msg#9 */
  "check -DDVIPNG=\"path\", etc.",
  "(10) dvipng ran but failed:",	/* msg#10 */
  "(11) Can't run dvips program:\\"	/* msg#11 */
  "check -DDVIPS=\"path\", etc.",
  "(12) dvips ran but failed:",		/* msg#12 */
  "(13) Can't run convert program:\\"	/* msg#13 */
  "check -DCONVERT=\"path\", etc.",
  "(14) convert ran but failed:",	/* msg#14 */
  "(15) Can't emit cached image:\\"	/* msg#15 */
  "check permissions.",
  NULL } ; /* --- end-of-*embeddedtext[] --- */
/* ---
 * output file (from shell mode)
 * ----------------------------- */
static	char outfile[256] = "\000";	/* output file, or empty for default*/
/* ---
 * temporary work directory
 * ------------------------ */
static	char tempdir[256] = "\000";	/* temporary work directory */
/* ---
 * internal buffer sizes
 * --------------------- */
#if !defined(MAXEXPRSZ)
  #define MAXEXPRSZ (32767)		/*max #bytes in input tex expression*/
#endif
#if !defined(MAXGIFSZ)
  #define MAXGIFSZ (131072)		/* max #bytes in output GIF image */
#endif

/* ---
 * latex wrapper document template (default, isdepth=0, without depth)
 * ------------------------------------------------------------------- */
static	char latexdefaultwrapper[MAXEXPRSZ+16384] =
	"\\documentclass[10pt]{article}\n" /*[fleqn] omitted*/
	"\\usepackage[latin1]{inputenc}\n"
	"\\usepackage{amsmath}\n"
	"\\usepackage{amsfonts}\n"
	"\\usepackage{amssymb}\n"
	/*"\\usepackage{bm}\n"*/	/* bold math */
	#if defined(USEPACKAGE)		/* cc -DUSEPACKAGE=\"filename\" */
	  #include USEPACKAGE		/* filename with \usepackage{}'s */
	#endif				/* or anything for the preamble */
	"%%usepackage%%\n"
      #if 0
        "\\def\\stackboxes#1{\\vbox{\\def\\\\{\\egroup\\hbox\\bgroup}"
        "\\hbox\\bgroup#1\\egroup}}\n"
        "\\def\\fparbox#1{\\fbox{\\stackboxes{#1}}}\n"
      #endif
	"%%%\\pagestyle{empty}\n"
	"%%pagestyle%%\n"
	"%%previewenviron%%\n"
	"\\begin{document}\n"
	/*"\\setlength{\\mathindent}{0pt}\n"*/ /* only with [fleqn] option */
	"\\setlength{\\parindent}{0pt}\n"
      #if 0
	"%%%\\renewcommand{\\input}[1]"	/* don't let users \input{} */
	"{\\mbox{[[$\\backslash$input\\{#1\\} illegal]]}}\n"
      #endif
	#if defined(NEWCOMMAND)		/* cc -DNEWCOMMAND=\"filename\" */
	  #include NEWCOMMAND		/* filename with \newcommand{}'s */
	#endif				/* or anything for the document */
	"\\newcommand{\\amsatop}[2]{{\\genfrac{}{}{0pt}{1}{#1}{#2}}}\n"
	"\\newcommand{\\twolines}[2]{{\\amsatop{\\mbox{#1}}{\\mbox{#2}}}}\n"
	"\\newcommand{\\fs}{{\\eval{fs}}}\n" /* \eval{} test */
	"%%fontsize%%\n"
	"%%setlength%%\n"
	"%%beginmath%% "
	"%%expression%% \n"		/* \n in case expression contains %*/
	" %%endmath%%\n"
	"\\end{document}\n";

/* ---
 * latex wrapper document template (optional, isdepth=1, with depth)
 * see http://www.mactextoolbox.sourceforge.net/articles/baseline.html
 * for discussion of this procedure
 * ------------------------------------------------------------------- */
static	char latexdepthwrapper[MAXEXPRSZ+16384] =
	"\\documentclass[10pt]{article}\n" /*[fleqn] omitted*/
	"\\usepackage[latin1]{inputenc}\n"
	"\\usepackage{amsmath}\n"
	"\\usepackage{amsfonts}\n"
	"\\usepackage{amssymb}\n"
	"%%%\\usepackage{calc}\n"
	#if defined(USEPACKAGE)		/* cc -DUSEPACKAGE=\"filename\" */
	  #include USEPACKAGE		/* filename with \usepackage{}'s */
	#endif				/* or anything for the preamble */
	"%%usepackage%%\n"
	#if defined(NEWCOMMAND)		/* cc -DNEWCOMMAND=\"filename\" */
	  #include NEWCOMMAND		/* filename with \newcommand{}'s */
	#endif				/* or anything for the document */
	"\\newcommand{\\amsatop}[2]{{\\genfrac{}{}{0pt}{1}{#1}{#2}}}\n"
	"\\newcommand{\\twolines}[2]{{\\amsatop{\\mbox{#1}}{\\mbox{#2}}}}\n"
	"\\newcommand{\\fs}{{\\eval{fs}}}\n" /* \eval{} test */
	"%%pagestyle%%\n"
	"%%previewenviron%%\n"
	"\\newsavebox{\\mybox}\n"
	"\n"
	"\\newlength{\\mywidth}\n"
	"\\newlength{\\myheight}\n"
	"\\newlength{\\mydepth}\n"
	"\n"
	/*"\\setlength{\\mathindent}{0pt}\n"*/ /* only with [fleqn] option */
	"\\setlength{\\parindent}{0pt}\n"
	"%%fontsize%%\n"
	"%%setlength%%\n"
	"\n"
	"\\begin{lrbox}{\\mybox}\n"
	"%%beginmath%% "
	"%%expression%% \n"		/* \n in case expression contains %*/
	" %%endmath%%\n"
	"\\end{lrbox}\n"
	"\n"
	"\\settowidth {\\mywidth}  {\\usebox{\\mybox}}\n"
	"\\settoheight{\\myheight} {\\usebox{\\mybox}}\n"
	"\\settodepth {\\mydepth}  {\\usebox{\\mybox}}\n"
	"\n"
	"\\newwrite\\foo\n"
	"\\immediate\\openout\\foo=\\jobname.info\n"
	"    \\immediate\\write\\foo{depth = \\the\\mydepth}\n"
	"    \\immediate\\write\\foo{height = \\the\\myheight}\n"
	"    \\addtolength{\\myheight} {\\mydepth}\n"
	"    \\immediate\\write\\foo{totalheight = \\the\\myheight}\n"
	"    \\immediate\\write\\foo{width = \\the\\mywidth}\n"
	"\\closeout\\foo\n"
	"\n"
	"\\begin{document}\n"
	"\\usebox{\\mybox}\n"
	"\\end{document}\n";
/* ---
 * latex wrapper used
 * ------------------ */
static	char *latexwrapper =		/* with or without depth */
	( ISDEPTH? latexdepthwrapper : latexdefaultwrapper );
/* ---
 * elements from \jobname.info to be prepended to graphics file
 * ------------------------------------------------------------ */
#define	IMAGEINFO struct imageinfo_struct /*"typedef" for imageinfo struct*/
#define	MAXIMAGEINFO 32			/* max 32 image info elements */
IMAGEINFO {
  char	*identifier;			/* identifier in \jobname.info */
  char	*format;			/* format to write in graphics file*/
  double value;				/* value of identifier */
  char	units[32];			/* units of value, e.g., "pt" */
  int	algorithm;			/* value conversion before writing */
  } ; /* --- end-of-store_struct --- */
static IMAGEINFO imageinfo[MAXIMAGEINFO] = {
    { "depth", "Vertical-Align:%dpx\n", -9999., "", 1 }, /* below baseline */
    { NULL, NULL, -9999., "", -9999 }	/* end-of-imageinfo */
  } ; /* --- end-of-imageinfo[] --- */

/* -------------------------------------------------------------------------
Unix or Windows header files
-------------------------------------------------------------------------- */
/* ---
 * compiling under Windows if -DWINDOWS explicitly supplied or...
 * -------------------------------------------------------------- */
#if !defined(WINDOWS)		/* -DWINDOWS not explicitly given by user */
  #if defined(_WINDOWS) || defined(_WIN32) || defined(WIN32) \
  ||  defined(DJGPP)		/* try to recognize windows compilers */ \
  ||  defined(_USRDLL)		/* must be WINDOWS if compiling for DLL */
    #define WINDOWS		/* signal windows */
  #endif
#endif
/* ---
 * unix headers
 * ------------ */
#if !defined(WINDOWS)		/* if not compiling under Windows... */
  #include <unistd.h>
  #include <sys/types.h>
  #include <sys/stat.h>
  #include <dirent.h>
#endif
/* ---
 * windows-specific header info
 * ---------------------------- */
#if defined(WINDOWS)		/* Windows opens stdout in char mode, and */
  #include <fcntl.h>		/* precedes every 0x0A with spurious 0x0D.*/
  #include <io.h>		/* So emitcache() issues a Win _setmode() */
				/* call to put stdout in binary mode. */
  #if defined(_O_BINARY) && !defined(O_BINARY)  /* only have _O_BINARY */
    #define O_BINARY _O_BINARY	/* make O_BINARY available, etc... */
    #define setmode  _setmode
    #define fileno   _fileno
  #endif
  #if defined(_O_BINARY) || defined(O_BINARY)  /* setmode() now available */
    #define HAVE_SETMODE	/* so we'll use setmode() */
  #endif
#endif
static	int iswindows =		/* 1 if running under Windows, or 0 if not */
  #ifdef WINDOWS
    1;				/* 1 when program running under Windows */
  #else
    0;				/* 0 when not running under Windows */
  #endif

/* -------------------------------------------------------------------------
application macros
-------------------------------------------------------------------------- */
/* --- check if a string is empty --- */
#define	isempty(s)  ((s)==NULL?1:(*(s)=='\000'?1:0))
/* --- last char of a string --- */
#define	plastchar(s) (isempty(s)?NULL:((s)+(strlen(s)-1)))
#define	lastchar(s) (isempty(s)?'\000':*(plastchar(s)))
/* --- check for thischar in accept --- */
#define	isthischar(thischar,accept) \
	( (thischar)!='\000' && !isempty(accept) \
	&& strchr((accept),(thischar))!=(char *)NULL )
/* --- skip/find whitespace --- */
#define	WHITESPACE  " \t\n\r\f\v"	/* skipped whitespace chars */
#define	skipwhite(thisstr)  if ( (thisstr) != NULL ) \
	thisstr += strspn(thisstr,WHITESPACE)
#define	findwhite(thisstr)  while ( !isempty(thisstr) ) \
	if ( isthischar(*(thisstr),WHITESPACE) ) break; else (thisstr)++
	/* thisstr += strcspn(thisstr,WHITESPACE) */
/* --- skip \command (i.e., find char past last char of \command) --- */
#define	skipcommand(thisstr)  while ( !isempty(thisstr) ) \
	if ( !isalpha(*(thisstr)) ) break; else (thisstr)++
/* --- 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'; } }
/* --- strip leading and trailing whitespace --- */
#define	trimwhite(thisstr) if ( (thisstr) != NULL ) { \
	int thislen = strlen(thisstr); \
	while ( --thislen >= 0 ) \
	  if ( isthischar((thisstr)[thislen],WHITESPACE) ) \
	    (thisstr)[thislen] = '\000'; \
	  else break; \
	if ( (thislen = strspn((thisstr),WHITESPACE)) > 0 ) \
	  {strsqueeze((thisstr),thislen);} } else
/* --- 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 ;*/
/* --- 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
/* --- min and max of args --- */
#define	max2(x,y)  ((x)>(y)? (x):(y))	/* larger of 2 arguments */
#define	min2(x,y)  ((x)<(y)? (x):(y))	/* smaller of 2 arguments */

/* -------------------------------------------------------------------------
other application global data
-------------------------------------------------------------------------- */
/* --- getdirective() global data --- */
static	int  argformat   = 0;		/* 111... if arg not {}-enclosed */
static	int  optionalpos = 0;		/* # {args} before optional [args] */
static	int  noptional   = 0;		/* # optional [args] found */
static	char optionalargs[8][512] =	/* buffer for optional args */
  { "\000", "\000", "\000", "\000", "\000", "\000", "\000", "\000" };

/* -------------------------------------------------------------------------
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 mathtexstore[MAXSTORE] = {
    { "fontsize", &fontsize },	{ "fs", &fontsize },	/* font size */
    /*{ "mytestvar", &mytestvar },*/
    { NULL, NULL }					/* end-of-store */
  } ; /* --- end-of-mimestore[] --- */


/* ==========================================================================
 * Function:	main ( argc, argv )
 * Purpose:	driver for mathtex.c
 *		emits, usually to stdout, a gif or png image
 *		of a LaTeX math expression entered either as
 *		    (1)	html query string from a browser (most typical), or
 *		    (2)	a query string from an html <form method="get">
 *			whose <textarea name=TEXTAREANAME>
 *			(usually for demo), or
 *		    (3)	command-line arguments (usually just to test).
 *		If no input supplied, expression defaults to "f(x)=x^2",
 *		treated as test (input method 3).
 * --------------------------------------------------------------------------
 * Command-Line Arguments:
 *		When running mathTeX from the command-line, rather than
 *		from a browser (usually just for testing), syntax is
 *		     ./mathtex	[-m msglevel]	verbosity of debugging output
 *				[-c cachepath ]	name of cache directory
 *				[expression	expression, e.g., x^2+y^2,
 *		-m   0-99, controls verbosity level for debugging output
 *		     >=9 retains all directories and files created
 *		-c   cachepath specifies relative path to cache directory
 * --------------------------------------------------------------------------
 * Exits:	0=success (always exits 0, regardless of success/failure)
 * --------------------------------------------------------------------------
 * Notes:     o For an executable that emits gif images
 *		     cc mathtex.c -o mathtex.cgi
 *		or, alternatively, for an executable that emits png images
 *		     cc -DPNG mathtex.c -o mathtex.cgi
 *		See Notes at top of file for other compile-line -D options.
 *	      o	Move executable to your cgi-bin directory and either
 *		point your browser to it directly in the form
 *		     http://www.yourdomain.com/cgi-bin/mathtex.cgi?f(x)=x^2
 *		or put a tag in your html document of the form
 *		     <img src="/cgi-bin/mathtex.cgi?f(x)=x^2"
 *		       border=0 align=middle>
 *		where f(x)=x^2 (or any other expression) will be displayed
 *		either as a gif or png image (as per -DIMAGETYPE flag).
 * ======================================================================= */
/* --- entry point --- */
int	main ( int argc, char *argv[] )
{
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
/* --- expression to be emitted --- */
static	char exprbuffer[MAXEXPRSZ+1] = "\000"; /* input TeX expression */
char	hashexpr[MAXEXPRSZ+1] = "\000";	/* usually use md5 of original expr*/
char	*expression = exprbuffer;	/* ptr to expression */
char	*query = getenv("QUERY_STRING"); /* getenv("QUERY_STRING") result */
int	isquery = 0;			/* true if input from QUERY_STRING */
/* --- preprocess expression for special mathTeX directives, etc --- */
char	*mathprep();			/* preprocess expression */
int	unescape_url();			/* convert %20 to blank space, etc */
int	strreplace(), irep=0;		/* look for keywords in expression */
char	*strchange();			/* edit expression */
char	*getdirective(), argstring[256],/*look for \density,\usepackage,etc*/
	*pdirective = NULL;		/* ptr to char after \directive */
int	validate();			/* remove \input, etc */
int	advertisement(), crc16();	/*wrap expression in advertisement*/
int	evalterm();			/* preprocess \eval{expression} */
char	*adtemplate = NULL;		/* usually use default message */
char	*host_showad = HOST_SHOWAD;	/* show ads only on this host */
char	*nomath();			/* remove math chars from string */
/* --- referer initialization variables --- */
char	*http_referer = getenv("HTTP_REFERER"), /* referer using mathTeX */
	*mathtex_host = getenv("HTTP_HOST"),	/* http host for mathTeX */
	*server_name  = getenv("SERVER_NAME"),	/* server hosting mathTeX */
	*http_host = (!isempty(mathtex_host)?mathtex_host: /*match host*/
	  (!isempty(server_name)?server_name:(NULL))); /* or server_name */
int	isvalidreferer = 1;		/* can this referer use mathTeX? */
struct	{ char *deny;			/* http_referer can't contain this */
	int msgnumber; }		/* emit invalid_referer_msg[msg#] */
	htaccess[] = {			/* ".htaccess" table for deny */
	#ifdef HTACCESS			/* eg, -DHTACCESS=\"htaccess.txt\" */
	  #include HTACCESS		/* eg,  {"",1},  for no referer */
	#endif
	{ NULL, -999 } };		/* trailer */
/* --- other initialization variables --- */
char	*whichpath();			/* look for latex,dvipng,etc */
int	setpaths();			/* set paths to latex,dvipng,etc */
int	isstrstr();			/* find any snippet in string */
int	isdexists();			/* check whether cache dir exists */
int	perm_all = (S_IRWXU|S_IRWXG|S_IRWXO); /* 777 permissions */
int	readcachefile(), nbytes=0;	/*read expr for -f command-line arg*/
char	*timestamp(),			/*for \time (in addition to \today)*/
	*calendar();			/* for \calendar{yyyy,mm,dd} string*/
int	emitembedded();			/* in case latex can't run */
int	timelimit();			/* just to check stub or built-in */
int	timewaitfor();			/* impose a delay on query replies */
int	iscolorpackage = 0,		/* true if \usepackage{color} found*/
	iseepicpackage = 0,		/* true if \usepackage{eepic} found*/
	ispict2epackage = 0,		/* true if \usepackage{pict2e}found*/
	ispreviewpackage = 0;		/* true if \usepackage{preview} */
/* --- image rendering --- */
int	mathtex();			/* generate new image using LaTeX */
/* --- image caching --- */
int	rewritecache();			/* rewrite cachefile with imageinfo*/
int	emitcache();			/* emit cached image if it exists */
int	maxage = MAXAGE;		/* max-age is typically two hours */
char	*makepath();			/* construct full path/filename.ext*/
char	*md5str(), *md5hash=NULL;	/* md5 has of expression */
char	*presentwd(), *pwdpath=NULL;	/* home pwd for relative file paths */
int	mathlog();			/* log requests */
/* --- messages --- */
char	*copyright1 =			/* copyright, gnu/gpl notice */
 "+-----------------------------------------------------------------------+\n"
 "|mathTeX vers " VERSION ", Copyright(c) " COPYRIGHTDATE
 ", John Forkosh Associates, Inc|\n"
 "+-----------------------------------------------------------------------+\n"
 "| mathTeX is free software, licensed to you under terms of the GNU/GPL  |\n"
 "|           and comes with absolutely no warranty whatsoever.           |\n",
 *copyright2 =				/* second part of copyright */
 "|     See http://www.forkosh.com/mathtex.html for complete details.     |\n"
 "+-----------------------------------------------------------------------+";
char	*usage1 =			/* usage instructions */
 "Command-line Usage:\n"
 "  ./mathtex.cgi \"expression\"     expression, e.g., \"x^2+y^2\"\n"
 "            | -f input_file      or read expression from input_file\n"
 "            [ -o output_file ]   write image to ouput_file\n"
 "            [ -m msglevel ]      verbosity / message level\n"
 "            [ -c cache ]         path to cache directory\n",
 *usage2 =				/* second part of usage */
 "Example:\n"
 "  ./mathtex.cgi \"x^2+y^2\" -o equation1\n"
 "creates equation1.gif containing image of x^2+y^2\n";
char	*versiontemplate =		/* mathTeX version "adtemplate" */
 "\\begin{center}\n"
 "\\fbox{\\footnotesize $\\twolines{mathTeX version " VERSION
 ",\\ \\ revised " REVISIONDATE "}{Copyright (c) " COPYRIGHTDATE
 ", John Forkosh Associates, Inc.}$}\\\\ \\vspace*{-.2in}"
 "%%beginmath%% %%expression%% %%endmath%%\n"
 "\\end{center}\n";
char	whichtemplate[512] =		/* mathTeX which "adtemplate" */
 "\\begin{center}\n"
 "\\fbox{\\footnotesize %%whichpath%%}\\\\ \\vspace*{-.2in}"
 "%%beginmath%% %%expression%% %%endmath%%\n"
 "\\end{center}\n";
int	maxinvalidmsg = 2;		/* max invalid_referer_msg[] index */
char	*invalid_referer_msg[] = {	/* invalid referer messages */
 /* --- message#0 (default invalid_referer message) --- */
 "\\parstyle\\usepackage{color}\\small\\color{red}\\noindent "
 "\\fbox{$\\twolines{\\textbf{%%referer%%}}"
 "{\\textbf{is not authorized to use mathTeX on this server}}$}",
 /* --- message#1 ("provide http_referer" message) --- */
 "\\parstyle\\usepackage{color}\\small\\color{red}\\noindent "
 "\\fbox{$\\twolines{\\textbf{To use the public math\\TeX{} server,}}"
 "{\\textbf{please provide your HTTP\\_REFERER}}$}",
 /* --- message#2 ("production use" message) --- */
 "\\parstyle\\usepackage{color}\\small\\color{red}\\noindent "
 "\\fbox{$\\twolines{\\textbf{For production use, please install mathtex.cgi on}}"
 "{\\textbf{your own server.  See www.forkosh.com/mathtex.html}}$}",
 /* --- end-of-table trailer --- */
 NULL } ; /* --- end-of-invalid_referer_msg[] --- */
/* -------------------------------------------------------------------------
Initialization
-------------------------------------------------------------------------- */
/* --- set global variables --- */
msgfp = NULL;				/* for query mode output */
msgnumber = 0;				/* no errors to report yet */
if ( imagetype < 1 || imagetype > 2 ) imagetype = 1;   /* keep in bounds */
if ( imagemethod<1 || imagemethod>2 ) imagemethod = 1; /* keep in bounds */
if ( (pwdpath = presentwd(0))		/* pwd where exectuable resides */
!=   NULL )				/* got it */
  strcpy(homepath,pwdpath);		/* save it for mathtex() later */
/* ---
 * check QUERY_STRING query for expression
 * --------------------------------------- */
if ( query != NULL )			/* check query string from environ */
  if ( strlen(query) >= 1 )		/* caller gave us a query string */
    { strninit(expression,query,MAXEXPRSZ); /* so use it as expression */
      isquery = 1; }			/* and set isquery flag */
if ( !isquery )				/* empty query string */
  { char *addr = getenv("SERVER_ADDR");	/* additional getenv("") results */
    if ( !isempty(http_host) || addr!=NULL ) /* assume http query */
      {	isquery = 1;			/* set flag to signal query */
	strcpy(expression,"\\fbox{\\rm No expression supplied}"); }
  } /* --- end-of-if(!isquery) --- */
/* ---
 * query delay
 * -------------- */
if ( isquery )				/* have a query */
  if ( !isempty(WAIT) ) {		/* wait imposed on queries */
    int	whundredths = atoi(WAIT);	/* specified in hundredths of a sec */
    if ( !isempty(NOWAIT) )		/* have -DNOWAIT=\",,,\" referers */
      if ( isstrstr(http_referer,NOWAIT,0) ) /* no wait for this refererer */
        whundredths = 0;		/* so reset wait time */
    if ( whundredths > 0 )		/* if we have a wait */
      timewaitfor(whundredths);		/* return after waiting */
    } /* --- end-of-if(!isempty(WAIT)) --- */
/* ---
 * process command-line args (only if not a query)
 * ----------------------------------------------- */
if ( !isquery				/* don't have an html query string */
/*&&   argc>1*/ ) {			/* and have command-line args */
  int	argnum = 0;			/*argv[] index for command-line args*/
  int	isendofswitches = 0;		/* -- arg signals no more -switches */
  msglevel = 3;				/*adjust minimum shell message level*/
  msgfp = stdout;			/* for comamnd-line mode output */
  while ( argc > ++argnum ) {		/*check for switches and expression*/
    if ( *argv[argnum] == '-'		/* have some -switch */
    &&   !isendofswitches ) {		/* and haven't seen a -- arg yet*/
For faster browsing, not all history is shown. View entire blame