1 /* -*- Mode: C; c-basic-offset:4 ; -*- */
2 #include <stdio.h>
3 #include <ctype.h>
4 #include <unistd.h> /* for unlink */
5 #include "sowing.h"
6 #include "search.h"
7 #include "tex.h"
8 
9 #define OUTFILE_SIZE 512
10 /* #define DBGMSG(a) if (DebugCommands) { fprintf( stdout, ... what goes here .. ); } */
11 
12 extern LINK *GetNextLink ( LINK * );
13 /*
14    This is a simple program to translate mostly ASCII text code from
15    a latexinfo file into another format.  Different formats can be
16    generated by providing different versions of the file tex2....c
17    For example,
18        tex2win generates Windows help output.
19        tex2html generates HTML (WWW) output
20 
21  */
22 
23 /*
24   Outstanding problems:
25 
26   InOutputBody isn't set except within "sections" (after WriteBeginPage).
27   Some TeX code doesn't use section/chapter/part.
28 
29   InOutputBody is used to suppress random TeX code from generating output.
30   We need another way to indicate InOutputBody.
31    -> many commands could set when they are called, indicating that we are
32    doing output.  ???
33 
34   In general, this code needs to be entirely re-written.  The model should be
35   a string of filters that recognize various constructions and replace them.
36   replaced code is contained in (nested?) begin-tok/end-tok pairs.
37  */
38 
39 
40 /*
41    I'd like to push a token in every routine, but the PC version seems to
42    run out of stack space (very, very tiny stacks!)
43 
44    What I should do is just allocate and deallocate.  This is more expensive,
45    but TeX is recursive and I've had problems with things like TeX commands
46    in \subsection....
47  */
48 
49 /*
50    Still need code to handle
51    \font\manual=manfnt at 12pt
52    (also, how to handle in generating output?  Should it produce a
53    bitmap?)
54  */
55 /* static char gtoken[MAX_TOKEN]; */
56 
57 /* These are used to hold a stack of tokens so that (a) we don't have to
58    allocated them all the time and (b) we don't require the ability to
59    allocate them off of the stack (a problem for PC's) */
60 char *tokbuf, *curtok;
61 int  toknum;
62 
63 /*
64 void TeXProcessCommand();
65 extern void TXem(), TXtt(), TXbf(), TXsf(), TXrm(),
66             TXmath(), TXmathend(), TXinlinemath(),
67             TXinlinemathend(), TXbitmap(), TXbw2(), TXbw(), TXbbrace(),
68             TXebrace(), TXoutbullet(), TXbgroup(), TXegroup();
69 extern int  TXWriteStartNewline(), TeXoutNewline();
70 
71 extern void TXbitemize(), TXeitemize(),
72             TXbenumerate(), TXeenumberate(),
73             TXbdescription(), TXedescription(),
74             TXbmenu(), TXemenu(), TXdimen(), TXnumber(), TXDef(), TeXtabular(),
75             TXbibitem(), TXDoNewenvironment(), TXDoNewtheorem(),
76             TXcounter(), TXadvance(),
77             TXDoBibliography(), TXcaption();
78 extern int LookupEnv();
79 
80 extern void SCSetAtLetter();
81 extern char SCGetCommentChar();
82 
83 */
84 /* Forward refs */
85 /*
86 void TeXskipEnv(), TXoutactiveToken();
87 */
88 void PrintLastSectionName( FILE * );
89 
90 FILE *ferr = 0;  /* We'd like this to be stderr, but ANSI/ISO C broke this */
91 SRList *topicctx = 0;
92 
93 int InDocument = 0;
94 int InOutputBody = 0;
95 
96 static int AmSkipping = 0;    /* Set to one when skipping a tex environment */
97 int InArg             = 0;    /* Set to one when in an argument */
98 char *ArgBuffer       = 0;    /* Used to hold temporary text */
99 static int InVerbatim = 0;    /* Verbatim has special processing */
100 static int DebugArgs  = 0;    /* Set to one to dump arg processing */
101 int DebugCommands = 0;        /* Set to one to dump command processing */
102 int DebugOutput   = 0;        /* Set to one to dump output processing */
103 int DebugFile     = 0;        /* Debug file related commands */
104 int DebugFont     = 0;        /* Debug font related commands */
105 int warnRedefinition = 0;     /* Warn on redefinition of commands */
106 static int UseIfTex   = 0;    /* Set to use begin{iftex} ... instead of
107 				 begin{ifinfo} code */
108 
109 static int ProcessManPageTokens = 0;
110                               /* Check EVERY token for a reference to a
111 				 known man page.  WARNING: this may make
112 				 the file rather link-heavy */
113 
114 /* These control whether we generate GIF files for what we don't understand */
115 int LatexUnknownEnvs = 0;
116 /* Latex Tables? */
117 int LatexTables	    = 0;
118 /* Latex Math? */
119 int LatexMath	    = 0;
120 /* Simple Math (math mode with no TeX commands done in italics */
121 int SimpleMath	    = 0;
122 /* Number of the generated image file */
123 int imageno	            = 0;
124 /* Set LatexAgain to 0 if you want to use the old img files */
125 int LatexAgain       = 1;
126 
127 /* Set to 1 to generate HTML for tabular environments */
128 int HandleAlign      = 0;
129 
130 int TableNumber = 0, FigureNumber = 0, EquationNumber = 0;
131 int NumberedEnvironmentType = ENV_NONE;
132 char *envjumpname = 0;
133 int  envJumpNum   = 0;
134 
135 int IncludeSectionNumbers = 0;
136 /* MinSectionKind allows us to handle documents with Chapters and Sections
137    better (a bibliography is the same kind of item as the largest
138    section (Chapter == 0, Section == 1)
139  */
140 int MinSectionKind = 1;
141 
142 int IgnoreCatcode = 1;
143 
144 /* This is for debugging the LaTeX file; it is important to ensure that all
145    braces are closed */
146 int LineNo[10] = { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
147 char *(InFName[10]) = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
148 int BraceCount = 0;
149 char CmdName[65];
150 
151 /* These hold the current section being processed, so that LABELS to this
152    section may be entered into the label table (and hence to the aux file for
153    the section pass). */
154 static int CurSeqnum     = 0;
155 static char CurNodename[256];
156 
157 /* Citation characters.  The default is [ and ] */
158 char *CitePrefix = 0;
159 char *CiteSuffix = 0;
160 
161 
162 int UsingLatexinfo = 0;
163 int DestIsHtml     = 1;
164 int HTMLv3         = 1;
165 
166 #if defined(WIN32) || defined(__MSDOS__)
167 char HTML_Suffix[5] = "htm";
168 char DirSep        = '\\';
169 char DirSepString[2] = "\\";
170 #else
171 char HTML_Suffix[5] = "html";
172 char DirSep        = '/';
173 char DirSepString[2] = "/";
174 #endif
175 /*
176     TeX processing:
177     We need to manage things like \section{name}, \begin{tex}, \c (comment)
178 
179     Multiple blank lines must generate a \par in the rtf file.
180     More specifically, a completely blank line must be changed into a par.
181  */
182 /* The tex stack */
183 typedef struct {
184     int fonttype;         /* Current font */
185     /* Other parameters as required */
186     int fontsize;         /* Not used yet; should be delta? */
187     } TeXStack;
188 
189 /* The section stack */
190 typedef struct {
191     int level;     /* Section level */
192     int count;     /* Current count of sections at this level */
193     /* entries for parent, children, siblings?? */
194     } SectionStack;
195 
196 /* The list of known functions */
197 SRList *TeXlist = 0;
198 
199 /* Stack of input files */
200 int    curfile = 0;
201 FILE   *(fpin[10]) = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
202 FILE   *fpout = 0;
203 char   *outfile = 0;
204 
205 TeXStack stack[MAX_TEX_STACK];
206 static int texSp = -1;
207 LaTeXStack lstack[MAX_TEX_STACK];
208 int lSp = -1;
209 SectionStack sstack[MAX_TEX_STACK];
210 static int SSp = -1;
211 
212 /* The "brace" stack */
213 #define MAX_BLOC_FNAME 50
214 typedef struct {
215     int lineno;
216     char filename[255];
217     } BraceElment;
218 #define MAX_BLOC 50
219 static BraceElment bloc[MAX_BLOC];
220 
221 /* TeX special characters on startup */
222 char CommandChar     = '\\';
223 char SubscriptChar   = '_';
224 char SuperscriptChar = '^';
225 char MathmodeChar    = '$';
226 char CommentChar     = '%';
227 char LbraceChar      = '{';
228 char RbraceChar      = '}';
229 char ArgChar         = '#';
230 char AlignChar       = '&';
231 char ActiveChar      = '\0';   /* Normally not set.  We allow a single char
232 				  to be active; (will be) used for obeylines */
233 void (*activeCharAction)(char *) = 0;
234 
235 /* Special user commands */
236 char *UserIndexName = 0;
237 
238 /* File for latex or graphics output errors */
239 char latex_errname[300];
240 
241 /* File name base for generated image files (default img) */
242 char imgfilebase[MAX_IMAGE_FILE_BASE];
243 
244 /*
245     This file contains the actions for processing a subset of LaTeXinfo
246     files
247  */
248 
TXSetDebug(int flag)249 void TXSetDebug( int flag )
250 {
251     DebugArgs     = flag;
252     DebugCommands = flag;
253 }
254 
TXSetDebugFile(int flag)255 void TXSetDebugFile( int flag )
256 {
257     DebugFile = flag;
258 }
259 
TXSetDebugFont(int flag)260 void TXSetDebugFont( int flag )
261 {
262     DebugFont = flag;
263 }
264 
TXSetUseIfTex(int flag)265 void TXSetUseIfTex( int flag )
266 {
267     UseIfTex = flag;
268 }
269 
TXSetLatexUnknown(int flag)270 void TXSetLatexUnknown( int flag )
271 {
272     LatexUnknownEnvs = flag;
273 }
274 
TXSetProcessManPageTokens(flag)275 void TXSetProcessManPageTokens( flag )
276 int flag;
277 {
278     ProcessManPageTokens = flag;
279 }
280 
TXSetLatexTables(int flag)281 void TXSetLatexTables( int flag )
282 {
283     LatexTables = flag;
284 }
TXSetLatexMath(int flag)285 void TXSetLatexMath( int flag )
286 {
287     LatexMath = flag;
288 }
289 
TXSetSimpleMath(int flag)290 void TXSetSimpleMath( int flag )
291 {
292     SimpleMath = flag;
293 }
294 
TXSetLatexAgain(int flag)295 void TXSetLatexAgain( int flag )
296 {
297     LatexAgain       = flag;
298 }
299 
TXSetFiles(char * fin,char * fout)300 void TXSetFiles( char *fin, char *fout )
301 {
302     outfile = (char *)MALLOC( OUTFILE_SIZE );
303     CHKPTR(outfile);
304     strcpy( outfile, fout );
305     InFName[0] = (char *)MALLOC( strlen(fin) + 1 );
306     CHKPTR(InFName[0]);
307     strcpy( InFName[0], fin );
308 }
309 
TeXAbort(char * routine,char * msg)310 void TeXAbort( char *routine, char *msg )
311 {
312     fprintf( stdout, "%s:%s\n", routine, msg ? msg : "No message" );
313     fprintf( stdout, "File %s line %d\n",
314 	     InFName[curfile] ? InFName[curfile]: "", LineNo[curfile] );
315     exit(1);
316 }
317 
TXPrintLocation(FILE * fp)318 void TXPrintLocation( FILE *fp )
319 {
320     fprintf( fp, "File %s line %d\n",
321 	     InFName[curfile] ? InFName[curfile]: "", LineNo[curfile] );
322 }
323 
324 /* Output a command */
TeXoutcmd(FILE * fout,char * str)325 void TeXoutcmd( FILE *fout, char *str )
326 {
327     char buf[102];
328     if (strlen(str) >= 100) {
329 	fprintf( stderr,
330 		 "Error in TeXoutcmd (internal error, command too long)!\n" );
331 	return;
332     }
333     buf[0] = TOK_START;
334     strcpy( buf+1, str );
335     buf[strlen(buf)+1] = 0;
336     buf[strlen(buf)]   = TOK_END;
337     if (InArg) {
338 	if (!ArgBuffer) {
339 	    ArgBuffer = MALLOC( MAX_TOKEN );
340 	    CHKPTR(ArgBuffer);
341 	}
342 
343 	strcat( ArgBuffer, buf );
344     } /* SCAppendToken( buf ); */
345     else {
346 	if (ProcessManPageTokens)
347 	    TXoutactiveToken( buf );
348 	else
349 	    WriteString( fout, buf );
350     }
351 }
352 
353 /* Output text (may be pushed back for rescanning) */
TeXoutstr(FILE * fout,char * str)354 void TeXoutstr( FILE *fout, char *str )
355 {
356     if (InArg) {
357 	if (!ArgBuffer) {
358 	    ArgBuffer = MALLOC( MAX_TOKEN );
359 	    CHKPTR(ArgBuffer);
360 	}
361 	strcat( ArgBuffer, str );
362     } /* SCAppendToken( str ); */
363     else {
364 	if (ProcessManPageTokens)
365 	    TXoutactiveToken( str );
366 	else
367 	    WriteString( fout, str );
368     }
369 }
370 
TeXoutsp(FILE * fout,int nsp)371 void TeXoutsp( FILE *fout, int nsp )
372 {
373     int i;
374     if (!InDocument) return;
375     for (i=0; i<nsp; i++) TeXoutstr( fout, " " );
376 }
377 
378 /* Read a TeX token; control sequences (\name) are read as a single token.
379    nsp is the number of preceding blanks.  Returns the first character in
380    the token (or EOF)
381  */
TeXReadToken(char * token,int * nsp)382 int TeXReadToken( char *token, int *nsp )
383 {
384     int ch, ch2, d;
385 
386     ch = SCTxtFindNextANToken( fpin[curfile], token, MAX_TOKEN, nsp );
387     if (ch == '\\') {
388 	ch2 = SCTxtFindNextANToken( fpin[curfile], token+1, MAX_TOKEN-1, &d );
389 	if (d > 0) {
390 	    /* If there were leading blanks, convert them into a single
391 	       blank and push the token back */
392 	    SCPushToken( token+1 );
393 	    token[1] = ' ';
394 	    token[2] = 0;
395 	}
396     }
397     return ch;
398 }
399 
400 /* Read a macro name, having read the first \ */
TeXReadMacroName(char * token)401 void TeXReadMacroName( char *token )
402 {
403     int ch, nsp;
404 
405     while ( (ch = SCTxtFindNextANToken( fpin[curfile], token, MAX_TOKEN, &nsp ))
406 	    == EOF) {
407 	if (DebugCommands)
408 	    fprintf( stdout, "EOF in TeXskipEnv while reading macro\n" );
409 	TXPopFile();
410     }
411     if (nsp != 0) {
412 	SCPushToken( token );
413 	strcpy( token, " " );
414     }
415 }
416 
417 /*
418    Get a TeX argument.  Return 0 if no matching argument found, 1 otherwise.
419    if doeval is false, don't process any command that is found; instead,
420    just copy it.
421 
422    A complication here is that we may need to recursively evaluate the tokens
423    that are being read.  We have to be careful to avoid rescanning the
424    same text over and over.  We do this by having EACH invocation of
425    TeXGetGenArg create a private output buffer that is used by the output
426    routines to append the output into each time "TXProcessCommand" is
427    invoked.  At the end of the call to TXProcessCommand, this output is
428    pushed back onto the input buffer.
429 
430    2/11/97
431    If a comment character is defined, we skip to the end of the line
432  */
433 #define MAX_MEM_STACK 128
434 /* Just to avoid constant mallocs, we maintain a stack of memory */
435 /* Points to last used element */
436 /* When new space is allocated, it MUST have size MAX_TOKEN */
437 static int mem_stack_sp = -1;
438 static int mem_stack_wm = -1;
439 static char *(mem_stack[MAX_MEM_STACK]);
440 #define ALLOC_TOKEN(t) {\
441     if (mem_stack_sp < mem_stack_wm) {\
442       t = mem_stack[++mem_stack_sp];\
443     }else {\
444        if (mem_stack_wm >= MAX_MEM_STACK) {\
445         fprintf( stderr, "Overflow in TeX argument processing!\n" );\
446         exit(1);\
447         }\
448        mem_stack[++mem_stack_wm] = MALLOC( MAX_TOKEN );\
449        t = mem_stack[++mem_stack_sp];}}
450 
451 #define FREE_TOKEN(t) mem_stack_sp--;
452 
453 /*
454    Return 1 for found argument, 0 for no arg, and -1 for error.
455  */
456 static int GetArgDepth=0;
TeXGetGenArg(FILE * fin,char * token,int maxtoken,char sc,char ec,int doeval)457 int TeXGetGenArg( FILE *fin, char *token, int maxtoken, char sc, char ec,
458 		  int doeval )
459 {
460     int  ch, nsp, i, oldinarg = InArg;
461     char *local_buf, *save_buf, *save_buf2;
462     char mtoken[MAX_TOKEN];
463 /* tf and mf save the token state */
464     char *tf = token;
465     int  mf  = maxtoken;
466     int  depth = 0, found;
467     TeXEntry E;
468 
469     GetArgDepth++;
470     found = 0;
471     save_buf  = ArgBuffer;
472     ALLOC_TOKEN(local_buf);
473     ArgBuffer = local_buf;
474 /* ArgBuffer = local_buf = MALLOC( MAX_TOKEN );
475    if (!local_buf) {
476        fprintf( stderr, "Out of memory!\n" );
477        exit(1);
478        }
479    */
480     local_buf[0] = 0;  /* In case we don't push anything back */
481     while ((ch = SCTxtFindNextANToken( fin, token, maxtoken, &nsp )) == EOF) {
482 	if (DebugCommands)
483 	    fprintf( stdout, "EOF in TeXGetGenArg\n" );
484 	TXPopFile();
485     }
486     if (DebugArgs) fprintf( stdout, "ARG-start: ch = %c\n", ch );
487     if (ch == sc) {
488 	found = 1;
489 	depth = 1;
490 	InArg = 1;
491 	token[0] = 0;
492 	while (maxtoken > 0) {
493 	    while ((ch = SCTxtFindNextANToken( fin, mtoken, MAX_TOKEN, &nsp )) ==
494 		   EOF) {
495 		if (DebugCommands)
496 		    fprintf( stdout, "EOF in TeXGetGenArg while reading arg.\n" );
497 		/* This is almost certainly an error */
498 		fprintf( stdout, "EOF while reading an argument in file %s[%d]\n",
499 			 InFName[curfile], LineNo[curfile] );
500 		if (CmdName[0])
501 		    fprintf( stdout, "In command %s\n", CmdName );
502 		TXPopFile();
503 	    }
504 	    if (DebugArgs) {
505 		fprintf( stdout, "ARG(%d):%s\n", GetArgDepth, mtoken );
506 	    }
507 	    for (i=0; i<nsp; i++) token[i] = ' ';
508 	    token    += nsp;
509 	    maxtoken -= nsp;
510 	    /* We must correctly process enter and leave groups.  Note that since
511 	       we may be looking for [] instead of {}, we defer the testing for
512 	       {} to the end */
513 	    if (ch == ec) {
514 		depth--;
515 		if (depth == 0) break;
516 		if (!doeval) {
517 		    *token++ = ec;
518 		    maxtoken--;
519 		}
520 	    }
521 	    else if (ch == sc) {
522 		depth++;
523 		if (!doeval) {
524 		    *token++ = sc;
525 		    maxtoken--;
526 		}
527 	    }
528 	    /* Skip comments 2/11/97 */
529 	    else if (ch == CommentChar) {
530 		SCTxtDiscardToEndOfLine( fin );
531 		LineNo[curfile]++;
532 		continue;
533 	    }
534 	    else if (mtoken[0] == '\\') {
535 		TeXReadMacroName( mtoken );
536 		if (DebugArgs)
537 		    fprintf( stdout, "ARGcmd(%d):\\%s\n", GetArgDepth, mtoken );
538 		/* DONT REPROCESS RTF commands */
539 		/* NEEDS TO BE CHANGED FOR HTML */
540 		if (0) {
541 		}
542 		else {
543 		    if (doeval) {
544 #ifndef FOO
545 			ArgBuffer = local_buf;
546 			TeXProcessCommand( mtoken, fin, (FILE *)0 );
547 			ArgBuffer = local_buf;
548 			SCPushToken( local_buf );
549 			local_buf[0] = 0;
550 			InArg = 1;
551 #else
552 			/* I think that this should allocate a new buffer
553 			   for args, then push the evaluation of that
554 			   back onto the stack ... */
555 			/* ArgBuffer = local_buf; */
556 			char *tmpbuf;
557 			ALLOC_TOKEN(tmpbuf)
558 			    tmpbuf[0] = 0;
559 			ArgBuffer = tmpbuf;
560 			TeXProcessCommand( mtoken, fin, (FILE *)0 );
561 			ArgBuffer = local_buf;
562 			/* I'm not sure about this either.  It is really
563 			   time to redesign the code.... */
564 			SCPushToken( tmpbuf );
565 			/* SCPushToken( local_buf ); */
566 			local_buf[0] = 0;
567 			/* FREE(tmpbuf); */
568 			FREE_TOKEN;
569 			InArg = 1;
570 #endif
571 		    }
572 		    else {
573 			*token++ = '\\';
574 			maxtoken--;
575 			strcpy( token, mtoken );
576 			token    += strlen( mtoken );
577 			maxtoken -= strlen( mtoken );
578 		    }
579 		}
580             }
581 	    else {
582 		if (DebugArgs)
583 		    fprintf( stdout, "->ARG(%d):%s\n", GetArgDepth, mtoken );
584 		strcpy( token, mtoken );
585 		token    += strlen( mtoken );
586 		maxtoken -= strlen( mtoken );
587             }
588 
589 	    if (ch == RbraceChar) {
590 		/* In case any output is generated... */
591 		save_buf2 = ArgBuffer;
592 		ArgBuffer = local_buf;
593 		local_buf[0] = 0;
594 		TXegroup( &E );
595 		SCPushToken( local_buf );
596 		local_buf[0] = 0;
597 		ArgBuffer = save_buf2;
598 	    }
599 	    else if (ch == LbraceChar)
600 		TXbgroup( &E );
601         }
602 	token[0] = 0;
603 	InArg    = oldinarg;
604     }
605     else {
606 	SCPushToken( token );
607 	token[0] = 0;
608     }
609     FREE_TOKEN(local_buf);
610 /* mem_stack_sp--; */
611 /*FREE( local_buf );*/
612     ArgBuffer = save_buf;
613     if (maxtoken <= 0) {
614 	if (maxtoken < 0)
615 	    fprintf( stderr, "token size exceeded by %d\n", -maxtoken );
616 	fprintf( stderr, "Argument too long in file %s line %d; aborting\n",
617 		 InFName[curfile] ? InFName[curfile]: "<input>", LineNo[curfile] );
618 	if (strlen(tf) < 80) {
619 	    fprintf( stderr, "Token(%d) was %s\n", mf, tf );
620 	}
621 	else {
622 	    fprintf( stderr, "Token(%d) was %.40s...%40s\n", mf, tf, tf );
623 	}
624 	found = -1;
625 	/* exit(1); */
626     }
627     if (DebugArgs && found) {
628 	fprintf( stdout, "ARG-end:arg is |" );
629 	TXPrintToken( stdout, tf );
630 	fprintf( stdout, "|\n" );
631     }
632 /* else just use the single token */
633     GetArgDepth--;
634     return found;
635 }
636 
637 /* Get a TeX argument */
TeXGetArg(FILE * fin,char * token,int maxtoken)638 int TeXGetArg( FILE *fin, char *token, int maxtoken )
639 {
640     int rc = TeXGetGenArg( fin, token, maxtoken, LbraceChar, RbraceChar, 1 );
641     if (rc < 0) {
642 	fprintf( stdout, "Error getting argument\n" );
643     }
644     return rc;
645 }
646 
647 /* This is a version of TeXGetArg that aborts on failure */
TeXMustGetArg(FILE * fin,char * token,int maxtoken,char * caller,char * texcmd)648 void TeXMustGetArg( FILE *fin, char *token, int maxtoken,
649 		    char *caller, char *texcmd )
650 {
651     if (TeXGetGenArg( fin, token, maxtoken, LbraceChar, RbraceChar, 1 ) < 0) {
652 	TeXAbort( caller, texcmd );
653     }
654 }
655 
TXnop(TeXEntry * e)656 void TXnop( TeXEntry *e )
657 {
658   int i;
659 
660   /* Tex command that has no effect in RTF file */
661   if (DebugCommands)
662     fprintf( stdout, "Skipping %d arguments for %s\n", e->nargs, e->name );
663   /* Don't bother to evaluate arguments that we are skipping */
664   PUSHCURTOK;
665   for (i=0; i<e->nargs; i++) {
666     if (TeXGetGenArg( fpin[curfile], curtok, MAX_TOKEN,
667 		      LbraceChar, RbraceChar, 0 ) == -1) {
668       TeXAbort( "TXnop", e->name );
669     }
670   }
671   POPCURTOK;
672 }
673 
674 /* Copy a string and return it in newly allocated memory */
TXCopy(char * s)675 char *TXCopy( char *s )
676 {
677     char *n;
678     n = MALLOC( strlen( s ) + 1 );
679     strcpy( n, s );
680     return n;
681 }
682 
683 /* Place the argument in the location stored in the ctx */
TXsavearg(TeXEntry * e)684 void TXsavearg( TeXEntry *e )
685 {
686     PUSHCURTOK;
687     strncpy( CmdName, e->name, sizeof(CmdName)-1 );
688     TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN, "TXsavearg", e->name );
689     strcpy( (char *)(e->ctx), curtok );
690     CmdName[0] = 0;
691     POPCURTOK;
692 }
693 
694 /* Replace \name with a string */
TXname(TeXEntry * e)695 void TXname( TeXEntry *e )
696 {
697     if (!InDocument) return;
698     TeXoutstr( fpout, (char *)(e->ctx) );
699 }
700 
701 /* Output raw HTML (or RTF) */
TXraw(TeXEntry * e)702 void TXraw( TeXEntry *e )
703 {
704     if (!InDocument) return;
705     TeXoutcmd( fpout, (char *)(e->ctx) );
706 }
707 
TXcomment(TeXEntry * e)708 void TXcomment( TeXEntry *e )
709 {
710     SCTxtDiscardToEndOfLine( fpin[curfile] );
711     LineNo[curfile]++;
712 }
713 
714 /* Do \char`\\ */
TXchar(TeXEntry * e)715 void TXchar( TeXEntry *e )
716 {
717     int ch, nsp;
718 
719     PUSHCURTOK;
720     ch = SCTxtFindNextANToken( fpin[curfile], curtok, MAX_TOKEN, &nsp );
721     if (ch == EOF) return;
722     if (ch == '`') {
723       ch = TeXReadToken( curtok, &nsp );
724       TeXoutstr( fpout, curtok );
725     }
726     else
727       TeXoutstr( fpout, curtok );
728     POPCURTOK;
729 }
730 
731 /* Remove a LaTeX [...].  Leave the token in "token"; if there is
732  no [], set token[0] to 0 */
TXRemoveOptionalArg(char * token)733 void TXRemoveOptionalArg( char *token )
734 {
735     int ch;
736     int nsp;
737 
738     token[0] = 0;
739     ch = SCTxtFindNextANToken( fpin[curfile], token, MAX_TOKEN, &nsp );
740     SCPushToken( token );
741     if (ch == '[') {
742 	/* Strip the token */
743 	if (TeXGetGenArg( fpin[curfile], token, MAX_TOKEN, '[', ']', 0 ) == -1)
744 	    TeXAbort( "TXRemoveOptionalArg", (char *)0 );
745     }
746     else
747       token[0] = 0;
748 }
749 
750 /* Remove a LaTeX * (as in \vspace*{3in}) */
TXRemoveOptionalStar(char * token)751 void TXRemoveOptionalStar( char *token )
752 {
753     int ch;
754     int nsp;
755 
756     token[0] = 0;
757     ch = SCTxtFindNextANToken( fpin[curfile], token, MAX_TOKEN, &nsp );
758     if (ch != '*')
759 	SCPushToken( token );
760 }
761 
762 /* This is nop, but after removing any optional * */
TXnopStar(TeXEntry * e)763 void TXnopStar( TeXEntry *e )
764 {
765     PUSHCURTOK;
766     TXRemoveOptionalStar( curtok );
767     POPCURTOK;
768     TXnop( e );
769 }
770 
771 /* the \\ can have the form \\[dimen] for adding space; this
772    routine eats the [...] */
TXdoublebw(TeXEntry * e)773 void TXdoublebw( TeXEntry *e )
774 {
775     PUSHCURTOK;
776     TXRemoveOptionalArg( curtok );
777     POPCURTOK;
778     if (HandleAlign && lstack[lSp].env == TXTABULAR)
779 	TeXEndHalignRow();
780     else
781 	TXbw2( e );
782 }
783 
TXnewline(TeXEntry * e)784 void TXnewline( TeXEntry *e )
785 {
786     PUSHCURTOK;
787     TXRemoveOptionalArg( curtok );
788     POPCURTOK;
789     TXbw2( e );
790 }
791 /* generate the date.  Probably not in the expected format */
TXtoday(TeXEntry * e)792 void TXtoday( TeXEntry *e )
793 {
794     char date[100];
795     SYGetDate( date );
796     SCPushToken( date );
797 }
798 
799 /* For ref to work, we need to install the \label defines in a label table */
TXref(TeXEntry * e)800 void TXref( TeXEntry *e )
801 {
802     int  refnumber = 0;
803     char *refname;
804     char fname[256];
805     char lfname[256];
806 
807     if (DebugCommands)
808 	fprintf( stdout, "Getting argument for %s\n", e->name );
809     PUSHCURTOK;
810     strcmp( CmdName, "ref" );
811     TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN, "TXref", e->name );
812     if (!InDocument) {
813       POPCURTOK;
814       return;
815     }
816     ReplaceWhite( curtok );
817 /* Match name to section and generate node reference */
818     refname = LabelLookup( curtok, &refnumber, fname );
819 
820     if (refname) {
821 	if (refnumber >= 0) {
822 	    if (fname)
823 		snprintf( lfname, sizeof(lfname), "%s#Node", fname );
824 	    else
825 		strncpy( lfname, "Node", sizeof(lfname) );
826 	    WritePointerText( fpout, refname, lfname, refnumber );
827 	}
828 	else {
829 	    WritePointerText( fpout, refname, fname, refnumber );
830 	}
831     }
832     else
833 	fprintf( stderr, "\\ref{%s} unknown (%s line %d)\n",
834 		 curtok,
835 		 InFName[curfile] ? InFName[curfile]: "",
836 		 LineNo[curfile] );
837     POPCURTOK;
838     CmdName[0] = 0;
839 }
840 
841 /*
842    A label within an equation, table, or figure should be treated
843    differently.  In particular, for cases where we use an image file for
844    the equation etc, we really need to place a new anchor and node number
845    around the equation etc.
846 
847    Note that the anchor need only be around a small part;
848    also, we could ALWAYS place the anchor and only use it as required.
849  */
TXlabel(TeXEntry * e)850 void TXlabel( TeXEntry *e )
851 {
852     if (DebugCommands)
853 	fprintf( stdout, "Getting argument for %s\n", e->name );
854     PUSHCURTOK;
855     strcpy( CmdName, "label" );
856     TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN, "TXlabel", e->name );
857     /* goken in ref refers to the value of LabelName */
858     ReplaceWhite( curtok );
859 
860     /* Originally, the value of the label was just the section that contained
861        the label.  This gave both poor label names and rather imprecise
862        location to a reference */
863     if (NumberedEnvironmentType == ENV_NONE)
864       WriteLabeltoauxfile( CurSeqnum-1, outfile, curtok, CurNodename );
865     else {
866       char buf[10];
867       if (!envjumpname || !envjumpname[0]) {
868 	fprintf( ferr,
869 		 "Label %s occurs in an unexpected environment at %s, line %d\n",
870 		 curtok, InFName[curfile], LineNo[curfile] );
871 	POPCURTOK;
872 	return;
873       }
874       snprintf( buf, sizeof(buf), "%d", envJumpNum );
875       WriteLabeltoauxfile( -1, envjumpname, curtok, buf );
876     }
877     POPCURTOK;
878     CmdName[0] = 0;
879 }
880 
881 /* Just use the section names as the reference */
TXhref(TeXEntry * e)882 void TXhref( TeXEntry *e )
883 {
884     LINK *RefedSection;
885     int  dummy;
886     char lfname[256];
887     char *topicfile;
888 
889     if (!InDocument) return;
890     strcpy( CmdName, "href" );
891     if (DebugCommands)
892 	fprintf( stdout, "Getting argument for %s\n", e->name );
893     PUSHCURTOK;
894     TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN, "THhref", e->name );
895     ReplaceWhite( curtok );
896     RefedSection = SRLookup( topicctx, curtok, (char *)0, &dummy );
897     if (!RefedSection) {
898 	fprintf( stderr, "Could not find %s in topicctx\n", curtok );
899 	POPCURTOK;
900 	return;
901     }
902     topicfile = TopicFilename( RefedSection );
903     if (topicfile)
904 	snprintf( lfname, sizeof(lfname), "%s#Node", topicfile );
905     else
906 	strncpy( lfname, "Node", sizeof(lfname) );
907     WritePointerText( fpout, curtok, lfname, RefedSection->number );
908 /* \hrefa has a second argument that is used in the LaTeX version as the
909    replacement text */
910     if (e->nargs > 1) {
911 	TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN, "TXhref", e->name );
912     }
913     POPCURTOK;
914     CmdName[0] = 0;
915 }
916 
TXmore(TeXEntry * e)917 void TXmore( TeXEntry *e )
918 {
919     TeXoutstr( fpout, " (Press " );
920     TXref( e );
921     TeXoutstr( fpout, " for more information)\n" );
922 }
923 
924 /*
925  * This is ugly, but there are several places where comment characters are used
926  * We should really combine them
927  */
928 #define MAX_COMMENT_DEPTH 25
929 static char SavedCommentChar[MAX_COMMENT_DEPTH];
930 static char SavedCommentChar2[MAX_COMMENT_DEPTH];
931 static int comment_depth=0;
PushCommentChar(char new_c)932 void PushCommentChar( char new_c )
933 {
934     SavedCommentChar[comment_depth] = CommentChar;
935     SavedCommentChar2[comment_depth++] = SCGetCommentChar();
936     CommentChar = new_c;
937     SCSetCommentChar( new_c );
938 }
PopCommentChar(void)939 void PopCommentChar( void )
940 {
941     CommentChar = SavedCommentChar[--comment_depth];
942     SCSetCommentChar( SavedCommentChar2[comment_depth] );
943 }
944 
TXcode(TeXEntry * e)945 void TXcode( TeXEntry *e )
946 {
947 /* output in "code" font */
948     if (!InDocument) return;
949     strcpy( CmdName, "code" );
950     if (DebugCommands)
951 	fprintf( stdout, "Getting argument for %s\n", e->name );
952     PUSHCURTOK;
953     /* We must allow comments (any arbitrary character) */
954     PushCommentChar( '\0' );
955     TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN, "TXcode", e->name );
956     PopCommentChar();
957     TXbgroup( e );
958     TXfont_tt( e );
959     TeXoutstr( fpout, curtok );
960     TXegroup( e );
961     POPCURTOK;
962     CmdName[0] = 0;
963 }
964 
TXroutine(TeXEntry * e)965 void TXroutine( TeXEntry *e )
966 {
967 /* output in "code" font */
968     if (!InDocument) return;
969     strcpy( CmdName, "routine" );
970     if (DebugCommands)
971 	fprintf( stdout, "Getting argument for %s\n", e->name );
972     PUSHCURTOK;
973     TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN, "TXroutine", e->name );
974     AddToIndex( curtok, CurNodename, CurSeqnum - 1, 0 );
975     TXbgroup( e );
976     TXfont_tt( e );
977     TeXoutstr( fpout, curtok );
978     TXegroup( e );
979     POPCURTOK;
980     CmdName[0] = 0;
981 }
982 
TXdfn(TeXEntry * e)983 void TXdfn( TeXEntry *e )
984 {
985 /* output in "definition" font */
986     if (!InDocument) return;
987     if (DebugCommands)
988 	fprintf( stdout, "Getting argument for %s\n", e->name );
989     PUSHCURTOK;
990     TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN, "TXdfn", e->name );
991     TXbgroup( e );
992     TXem( e );
993     TeXoutstr( fpout, curtok );
994     TXegroup( e );
995     POPCURTOK;
996 }
997 
TXvar(TeXEntry * e)998 void TXvar( TeXEntry *e )
999 {
1000 /* output in "var" font */
1001     if (!InDocument) return;
1002     if (DebugCommands)
1003 	fprintf( stdout, "Getting argument for %s\n", e->name );
1004     PUSHCURTOK;
1005     TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN, "TXvar", e->name );
1006     TXbgroup( e );
1007     TXem( e );
1008     TeXoutstr( fpout, curtok );
1009     TXegroup( e );
1010     POPCURTOK;
1011 }
1012 
TXfile(TeXEntry * e)1013 void TXfile( TeXEntry *e )
1014 {
1015 /* output in "file" font */
1016     if (!InDocument) return;
1017     if (DebugCommands)
1018 	fprintf( stdout, "Getting argument for %s\n", e->name );
1019     PUSHCURTOK;
1020     TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN, "TXfile", e->name );
1021     TXbgroup( e );
1022     TXfont_ss( e );
1023     TeXoutstr( fpout, curtok );
1024     TXegroup( e );
1025     POPCURTOK;
1026 }
1027 
TXatletter(TeXEntry * e)1028 void TXatletter( TeXEntry *e )
1029 {
1030     SCSetAtLetter( 1 );
1031 }
1032 
TXatother(TeXEntry * e)1033 void TXatother( TeXEntry *e )
1034 {
1035     SCSetAtLetter( 0 );
1036 }
1037 
1038 static char  asistoken[MAX_TOKEN];
TXasis(e)1039 void TXasis( e )
1040 TeXEntry *e;
1041 {
1042     int  i;
1043 
1044     if (DebugCommands)
1045 	fprintf( stdout, "Getting %d arguments for %s\n", e->nargs, e->name );
1046     for (i=0; i<e->nargs; i++) {
1047 	if (TeXGetArg( fpin[curfile], asistoken, MAX_TOKEN ) == -1) {
1048 	    fprintf( stdout, "Failed to get argument for %s\n", e->name );
1049 	    TeXAbort( "TXasis", e->name );
1050 	}
1051 	if (InDocument)
1052 	    TeXoutstr( fpout, asistoken );
1053     }
1054 }
1055 
1056 /* This is "as-is", but with a surrounding group
1057  */
TXasisGrouped(TeXEntry * e)1058 void TXasisGrouped( TeXEntry *e )
1059 {
1060     int  i;
1061 
1062     TXbgroup( e );
1063     if (DebugCommands)
1064 	fprintf( stdout, "Getting %d arguments for %s\n", e->nargs, e->name );
1065     strncpy( CmdName, e->name, sizeof(CmdName)-1 );
1066     for (i=0; i<e->nargs; i++) {
1067 	if (TeXGetArg( fpin[curfile], asistoken, MAX_TOKEN ) == -1) {
1068 	    fprintf( stdout, "Failed to get argument for %s\n", e->name );
1069 	    TeXAbort( "TXasisGrouped", e->name );
1070 	}
1071 	if (InDocument) {
1072 	    TeXoutstr( fpout, asistoken );
1073 	}
1074     }
1075     TXegroup( e );
1076     CmdName[0] = 0;
1077 }
1078 
1079 /* This is like asis, but handles the "to <dimension>", which may be \hsize */
TXbox(TeXEntry * e)1080 void TXbox( TeXEntry *e )
1081 {
1082     int  i, nsp, ch;
1083 
1084     ch = SCTxtFindNextANToken( fpin[curfile], asistoken, MAX_TOKEN, &nsp );
1085     if (ch == EOF) return;
1086     if (strcmp( asistoken, "to" ) == 0) {
1087 	/* Very simple-minded: look for \name or <value><scale> */
1088 	ch = TeXReadToken( asistoken, &nsp );
1089 	if (ch != '\\') {
1090 	    SCPushToken( asistoken );
1091 	    TXReadDimen( fpin[curfile] );
1092 	}
1093     }
1094     else
1095 	SCPushToken( asistoken );
1096 
1097     TXbgroup( e );
1098     if (DebugCommands)
1099 	fprintf( stdout, "Getting %d arguments for %s\n", e->nargs, e->name );
1100     strncpy( CmdName, e->name, sizeof(CmdName)-1 );
1101     for (i=0; i<e->nargs; i++) {
1102 	if (TeXGetArg( fpin[curfile], asistoken, MAX_TOKEN ) == -1) {
1103 	    fprintf( stdout, "Failed to get argument for %s\n", e->name );
1104 	    TeXAbort( "TXbox", e->name );
1105 	}
1106 	if (InDocument)
1107 	    TeXoutstr( fpout, asistoken );
1108     }
1109     TXegroup( e );
1110     CmdName[0] = 0;
1111 }
1112 
1113 /* Handle \input and \include commands */
1114 /*
1115    Problem.  The TeX \input does not require an argument; you
1116    can use \input filename.  Need to check for that case and add it
1117    to the list of 'predoc' commands
1118    */
TXinclude(TeXEntry * e)1119 void TXinclude( TeXEntry *e )
1120 {
1121     char *p;
1122     int  ch;
1123     if (DebugCommands)
1124 	fprintf( stdout, "Getting argument for %s\n", e->name );
1125     PUSHCURTOK;
1126     curtok[0] = 0;
1127     TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN, "TXinclude", e->name );
1128     if (strlen(curtok) == 0) {
1129 	/* Get next space-delimited token that is not a right brace*/
1130 	/* (What are the rules on input?) */
1131 	while ((ch = SCTxtGetChar( fpin[curfile] )) != -1 &&
1132 	       isspace(ch) && ch != '\n' && ch != RbraceChar);
1133 	p    = curtok;
1134 	*p++ = ch;
1135 	while ((ch = SCTxtGetChar( fpin[curfile] )) != -1 && !isspace(ch)
1136 	       && ch != RbraceChar)
1137 	    *p++ = ch;
1138 	*p = 0;
1139 	/* We still need to EVALUATE any TeX commands in this string !!! */
1140 	/* Need a command to evaluate a buffer */
1141 	/* We cheat by pushing back { curtok } and making getarg to all
1142 	   of the work */
1143 	SCPushChar( RbraceChar );
1144 	SCPushToken( curtok );
1145 	SCPushChar( LbraceChar );
1146 	TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN,
1147 		       "TXinclude", e->name );
1148     }
1149     if (DebugCommands || DebugFile)
1150 	fprintf( stdout, "About to open |%s|\n", curtok );
1151 
1152     if (!InDocument) {
1153 	/* fprintf( stderr, "\\include{%s}\n", curtok ); */
1154 	strcat( predoc, "\\include{" );
1155 	strcat( predoc, curtok );
1156 	strcat( predoc, "}\n" );
1157     }
1158     /* Catch the case where a file hasn't been closed */
1159     if (fpin[curfile+1]) fclose( fpin[curfile+1] );
1160 
1161     /* In some cases, particularly complex macro files used in Books,
1162        we may want to skip reading an include file, instead relying on
1163        customized defintions provide through a definitions file.  Without
1164        this, we'd have to implement the most complex parts of TeX, including
1165        catcode changes, expandafter, etc. This include file name is
1166        specified in a defs file using the skipinclude command */
1167     if (TXIsSkipFile( curtok )) {
1168 	POPCURTOK;
1169 	return;
1170     }
1171     /* Check for a replacemtn file name */
1172     {
1173 	char newtok[MAX_TOKEN];
1174 	if (TXIsReplaceFile( curtok, newtok )) {
1175 	    strcpy( curtok, newtok );
1176 	}
1177     }
1178 
1179     /* Push this file and process it */
1180     fpin[++curfile] = fopen( curtok, "r" );
1181     if (!fpin[curfile]) {
1182 	strcat( curtok, ".tex" );
1183 	fpin[curfile] = fopen( curtok, "r" );
1184 	if (!fpin[curfile]) {
1185 	    curfile--;
1186 	    fprintf( stderr, "(TXinclude) Could not open file %s\n", curtok );
1187 	    POPCURTOK;;
1188 	    return;
1189 	}
1190     }
1191     InFName[curfile] = STRDUP( curtok );
1192     CHKPTR(InFName[curfile]);
1193     LineNo[curfile] = 1;      /* Line numbers are 1-origin */
1194     POPCURTOK;
1195 }
1196 
TXPopFile(void)1197 void TXPopFile( void )
1198 {
1199     if (DebugCommands) {
1200 	fprintf( stdout, "Popping file from stack!\n" );
1201     }
1202     if (InFName[curfile]) {
1203 	FREE( InFName[curfile] );
1204     }
1205     curfile--;
1206     if (curfile < 0) {
1207 	fprintf( stderr, "EOF in input; aborting\n" );
1208 	fprintf( stderr,
1209 "(Reached an end-of-file in the top-level file without an \\end{document})\n" );
1210 	exit(1);
1211     }
1212 }
1213 
1214 /* ----------------------------------------------------------------------- */
1215 /*
1216    This implements my \fileinclude{filename} macro which includes the
1217    entire contents of filename in verbatim form
1218    */
TXfileinclude(TeXEntry * e)1219 void TXfileinclude( TeXEntry *e )
1220 {
1221     char line[257];
1222     FILE *fp;
1223 
1224     if (DebugCommands)
1225 	fprintf( stdout, "Getting argument for %s\n", e->name );
1226     PUSHCURTOK;
1227     curtok[0] = 0;
1228     TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN,
1229 		   "TXfileinclude", e->name );
1230     if (DebugCommands || DebugFile)
1231 	fprintf( stdout, "About to open |%s|\n", curtok );
1232 
1233     fp = fopen( curtok, "r" );
1234     if (!fp) {
1235 	fprintf( stderr, "(TXfileinclude) Could not open file %s\n", curtok );
1236 	POPCURTOK;
1237 	return;
1238     }
1239 /* Start of verbatim output */
1240     TXWriteStartNewLine( fpout );
1241     TXpreformated( fpout, 1 );
1242     TXbgroup( e );
1243     TXfont_tt( e );
1244 
1245 /* We'd like to map tokens here as well */
1246     while (fgets( line, sizeof(line)-1, fp )) {
1247 	if (ProcessManPageTokens)
1248 	    /* Eventually used WriteString(fpout,...) */
1249 	    TXoutactiveToken( line );
1250 	else
1251 	    WriteString( fpout, line );
1252 
1253 	/* WriteStringRaw( fpout, line ); */
1254     }
1255 /* End of verbatim output */
1256     TXegroup( e );
1257     TXpreformated( fpout, 0 );
1258     fclose( fp );
1259     POPCURTOK;
1260 }
1261 /*
1262    TXIfFileExists - Implement the LaTeX command:
1263    if file #1 exists, do #2 else do #3
1264 
1265    This routine may not be nested.
1266  */
TXIfFileExists(TeXEntry * e)1267 void TXIfFileExists( TeXEntry *e )
1268 {
1269     FILE *fp;
1270 
1271     if (DebugCommands)
1272 	fprintf( stdout, "Getting argument for %s\n", e->name );
1273     PUSHCURTOK;
1274     curtok[0] = 0;
1275     TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN,
1276 		   "TXIfFileExists", e->name );
1277     if (DebugCommands || DebugFile)
1278 	fprintf( stdout, "Attempting to open file %s\n", curtok );
1279     fp = fopen( curtok, "r" );
1280     /* We need to read these args *but not evaluate them* until after
1281        they are pushed back */
1282     if (fp) {
1283 	char savetok[MAX_TOKEN];
1284 	fclose( fp );
1285 	/* Use genarg because we want to suppress evaluation */
1286 	if (TeXGetGenArg( fpin[curfile], savetok, MAX_TOKEN,
1287 			   LbraceChar, RbraceChar, 0 ) < 0)
1288 	    TeXAbort( "TXIfFileExists", e->name );
1289 	if (TeXGetGenArg( fpin[curfile], curtok, MAX_TOKEN,
1290 			   LbraceChar, RbraceChar, 0 ) < 0)
1291 	    TeXAbort( "TXIfFileExists", e->name );
1292 	/* Push back the second token */
1293 	SCPushToken( savetok );
1294     }
1295     else {
1296 	if (TeXGetGenArg( fpin[curfile], curtok, MAX_TOKEN,
1297 			   LbraceChar, RbraceChar, 0 ) < 0)
1298 	    TeXAbort( "TXIfFileExists", e->name );
1299 	if (TeXGetGenArg( fpin[curfile], curtok, MAX_TOKEN,
1300 			   LbraceChar, RbraceChar, 0 ) < 0)
1301 	    TeXAbort( "TXIfFileExists", e->name );
1302 	/* Push back the third token */
1303 	SCPushToken( curtok );
1304     }
1305     POPCURTOK;
1306 }
1307 
1308 static LINK *LastSection = 0;
1309 int splitlevel = -1;
1310 char splitdir[256];
1311 
TXSetSplitLevel(int sl,char * dir)1312 void TXSetSplitLevel( int sl, char *dir )
1313 {
1314     splitlevel = sl;
1315     if (strlen(dir) > sizeof(splitdir) - 1) {
1316 	TeXAbort( "TXSetSplitLevel", "directory name too long" );
1317     }
1318     strncpy( splitdir, dir, sizeof(splitdir) );
1319 }
1320 
1321 /*
1322    Generate the output for a new section; may start by generating the contents
1323    table for the previous section.
1324 
1325    In order to generate the "correct" navigation links, we use the "next"
1326    links created when the contents aux (.hux file) were read in.
1327    To do this, we keep track of where we were in that list with a local
1328    variable LastSection.
1329 
1330  */
TXsection(TeXEntry * e)1331 void TXsection( TeXEntry *e )
1332 {
1333     int   level = (int)(PTRINT)(e->ctx), ch;
1334     int   dummy;
1335     char *p;
1336     int   isUnnumbered = 0;
1337 
1338     TeXWriteContents( fpout );
1339 
1340     if (level < MinSectionKind) MinSectionKind = level;
1341 
1342 /* Must put a marker at the END of each section */
1343     if (CurSeqnum > 0) {
1344 	/* Before we do this, we'd like to add the jump lines for the subsections.
1345 	   We can get this information from the aux file (but not now, since
1346 	   we've started writing the new one). */
1347 	if (LastSection) {
1348 	    if (NumChildren( LastSection ) > 0) {
1349 		WriteChildren( fpout, LastSection, -1 );
1350 	    }
1351         }
1352 	WriteEndofTopic( fpout );
1353 	/* Add the buttons at the bottom of the section if requested
1354 	   (tohtml only) */
1355 	if (DestIsHtml) {
1356 	    /* fputs( "\n<P><HR>\n", fpout ); */
1357 	    WriteSectionButtonsBottom( fpout, CurNodename, LastSection );
1358 	}
1359     }
1360 
1361 /* First, check for a *.  discard if seen */
1362     ch = SCTxtGetChar( fpin[curfile] );
1363     /* FIXME: If an asterisk, the section is neither numbered nor
1364        updates the section number. */
1365     if (ch == '*')
1366 	isUnnumbered = 1;
1367     else
1368 	SCPushChar( ch );
1369 
1370 /* Start a new section (at several levels) */
1371 /* Pop section stack if this item is at same level or lower */
1372     if (SSp >= 0 && sstack[SSp].level == level) {
1373 	if (!isUnnumbered)
1374 	    sstack[SSp].count ++;
1375     }
1376     else {
1377 	if (SSp >= 0 && sstack[SSp].level >= level) {
1378 	    /* Move up in heirarchy */
1379 	    while (SSp >= 0 && sstack[SSp].level >= level ) SSp--;
1380 	    SSp++;
1381 	    if (!isUnnumbered)
1382 		sstack[SSp].count++;
1383 	}
1384 	else {
1385 	    /* Move down */
1386 	    SSp++;
1387 	    sstack[SSp].level = level;
1388 	    sstack[SSp].count = 1;
1389 	}
1390     }
1391 
1392 /* First, check for a *.  discard if seen */
1393     ch = SCTxtGetChar( fpin[curfile] );
1394     /* FIXME: If an asterisk, the section is neither numbered nor
1395        updates the section number. */
1396     if (ch == '*')
1397 	isUnnumbered = 1;
1398     else
1399 	SCPushChar( ch );
1400 
1401     if (DebugCommands)
1402 	fprintf( stdout, "Getting argument for %s\n", e->name );
1403     PUSHCURTOK;
1404     TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN,
1405 		   "TXsection(arg)", e->name );
1406     /* Remove any leading blanks */
1407     if (curtok[0] == ' ') {
1408 	char *p = curtok;
1409 	char *p1 = curtok;
1410 	/* Find the first nonblank */
1411 	while (*p == ' ') p++;
1412 	/* Move the first nonblank over, and then copy the rest of the string */
1413 	while (*p != 0) *p1++ = *p++;
1414 	*p1 = 0;
1415     }
1416     /* Remove any trailing blanks */
1417     p = curtok + strlen(curtok) - 1;
1418     if (*p == ' ') {
1419 	while (p > curtok && *p == ' ') p--;
1420         *++p = 0;
1421     }
1422     strncpy( CurNodename, curtok, sizeof(CurNodename) - 1 );
1423 
1424     /* Write out section numbers in the "natural way" */
1425     if (IncludeSectionNumbers && !isUnnumbered) {
1426 	int i;
1427 	for (i=0; i<=SSp; i++) fprintf( stdout, "%d.", sstack[i].count );
1428 	fprintf( stdout, " %s\n", CurNodename );
1429     }
1430 
1431     if (level <= splitlevel) {
1432 	/* Need to close current file and open new file. */
1433 	/* New file name is directory/node#.html */
1434 	if (fpout) {
1435 	    WriteEndPage( fpout );
1436 	}
1437 	if (!outfile) {
1438 	    outfile = (char *)MALLOC( OUTFILE_SIZE );
1439 	    CHKPTR(outfile);
1440 	}
1441 	snprintf( outfile, OUTFILE_SIZE, "%s%cnode%d.%s",
1442                  splitdir, DirSep, CurSeqnum, HTML_Suffix );
1443 	fclose( fpout );
1444 	fpout = fopen( outfile, "w" );
1445 	if (!fpout) {
1446 	    fprintf( ferr, "(TXsection) Could not open file %s\n", outfile );
1447 	    TeXAbort( "TXsection(file)", (char *)0 );
1448         }
1449 	/* We use the local name (in the directory) for all references */
1450 	snprintf( outfile, OUTFILE_SIZE, "node%d.%s", CurSeqnum, HTML_Suffix );
1451 	WriteHeadPage( fpout );
1452 	WriteFileTitle( fpout, curtok );
1453 	WriteBeginPage( fpout );
1454     }
1455     ReplaceWhite( curtok );
1456     WRtoauxfile( CurSeqnum++, outfile, level, curtok );
1457 
1458     /* FIXME: Why the if (1)? */
1459     if (1) {
1460 	LastSection = GetNextLink( LastSection );
1461 	/* Check that this matches the name */
1462 	if (LastSection && strcmp(LastSection->topicname,curtok) != 0) {
1463 	    fprintf( stderr, "**Mismatch in section; expected \"%s\" found \"%s\"\n",
1464 		     curtok, LastSection->topicname );
1465 	}
1466     }
1467     else {
1468 /* Lookup the section in the aux file list */
1469 	LastSection = SRLookup( topicctx, curtok, (char *)0, &dummy );
1470 	if (!LastSection) {
1471 	    fprintf( stderr, "**Could not find %s in topicctx\n", curtok );
1472 	}
1473     }
1474 
1475 /* Output section headers */
1476     if (IncludeSectionNumbers && !isUnnumbered) {
1477 	char tmptok[MAX_TOKEN], *p;
1478 	int i;
1479 	p = tmptok;
1480 	for (i=0; i<=SSp; i++) {
1481 	    sprintf( p, "%d.", sstack[i].count );
1482 	    p += strlen(p);
1483 	}
1484 	strcat( p, " " );
1485 	strcat( p, curtok );
1486 	WriteSectionAnchor( fpout, tmptok, "Node", CurSeqnum-1,
1487 			    (int)(PTRINT)(e->ctx) );
1488     }
1489     else {
1490 	WriteSectionAnchor( fpout, curtok, "Node", CurSeqnum-1,
1491 			    (int)(PTRINT)(e->ctx) );
1492     }
1493     WriteSectionButtons( fpout, curtok, LastSection );
1494 
1495 /* These values (entrylevel, number) aren't correct */
1496 /* Write the section numbers by prefixing the section entry values */
1497     WriteSectionHeader( fpout, curtok, "Node", CurSeqnum-1, (char *)0,
1498 			(int)(PTRINT)(e->ctx) );
1499 /* Insert tree links to parent, child, and sibling.  Sets name for subsequent
1500    label statements */
1501     WriteTextHeader( fpout );
1502 
1503 /* Skip any newlines until we find the first non-blank */
1504     SCSkipNewlines( fpin[curfile] );
1505     POPCURTOK;
1506 }
1507 
1508 /*
1509     This is a special version of section for the first page (vtitle) of
1510     a set of slides.
1511  */
TXtitlesection(TeXEntry * e)1512 void TXtitlesection( TeXEntry *e )
1513 {
1514     int level = (int)(PTRINT)(e->ctx), ch;
1515     int dummy;
1516 
1517 /* Start a new section (at several levels) */
1518 /* Pop section stack if this item is at same level or lower */
1519     while (SSp >= 0 && sstack[SSp].level >= level ) SSp--;
1520     SSp++;
1521     sstack[SSp].count = 1;
1522 
1523 /* First, check for a *.  discard if seen */
1524     ch = SCTxtGetChar( fpin[curfile] );
1525     if (ch != '*') SCPushChar( ch );
1526 
1527     if (DebugCommands)
1528 	fprintf( stdout, "Getting argument for %s\n", e->name );
1529     PUSHCURTOK;
1530     TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN, "TXsection", (char *)0 );
1531     strncpy( CurNodename, curtok, sizeof(CurNodename) - 1 );
1532 
1533     WriteHeadPage( fpout );
1534     WriteFileTitle( fpout, curtok );
1535     WriteBeginPage( fpout );
1536     ReplaceWhite( curtok );
1537     WRtoauxfile( CurSeqnum++, outfile, level, curtok );
1538 
1539 /* Lookup the section in the aux file list */
1540     LastSection = SRLookup( topicctx, curtok, (char *)0, &dummy );
1541     if (!LastSection) {
1542 	fprintf( stderr, "Could not find %s in topicctx\n", curtok );
1543     }
1544 
1545 /* Output section headers */
1546     WriteSectionAnchor( fpout, curtok, "Node", CurSeqnum-1,
1547 			(int)(PTRINT)(e->ctx) );
1548     WriteSectionButtons( fpout, curtok, LastSection );
1549 
1550 /* These values (entrylevel, number) aren't correct */
1551     WriteSectionHeader( fpout, curtok, "Node", CurSeqnum-1, (char *)0,
1552 			(int)(PTRINT)(e->ctx) );
1553 
1554 /* Insert tree links to parent, child, and sibling.  Sets name for subsequent
1555    label statements */
1556     WriteTextHeader( fpout );
1557 
1558 /* Skip any newlines until we find the first non-blank */
1559     SCSkipNewlines( fpin[curfile] );
1560     TXWriteStartNewLine( fpout );
1561     POPCURTOK;
1562 }
1563 
PrintLastSectionName(FILE * fp)1564 void PrintLastSectionName( FILE *fp )
1565 {
1566     if (LastSection)
1567 	fprintf( fp, "%s\n", LastSection->topicname );
1568     else
1569 	fprintf( fp, "<before first section>\n" );
1570 }
1571 
1572 /* Paragraphs are a very restricted form of section */
TXparagraph(TeXEntry * e)1573 void TXparagraph( TeXEntry *e )
1574 {
1575     int ch;
1576 
1577     /* First, check for a  *.  discard if seen */
1578     ch = SCTxtGetChar( fpin[curfile] );
1579     if (ch != '*') SCPushChar( ch );
1580 
1581     if (DebugCommands)
1582 	fprintf( stdout, "Getting argument for %s\n", e->name );
1583     PUSHCURTOK;
1584     TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN,
1585 		   "TXparagraph(arg)", e->name );
1586 
1587     /* Output a new paragraph */
1588     TXWritePar( fpout );
1589     /* Output the paragraph title.  Font choice? */
1590     TeXoutstr( fpout, curtok );
1591     /* Output some space */
1592     TeXoutstr( fpout, " " );
1593     /* Skip any newlines until we find the first non-blank */
1594     SCSkipNewlines( fpin[curfile] );
1595     POPCURTOK;
1596 }
1597 
1598 
1599 /*
1600    \vtt is rather special.  We want to start processing, but most of the
1601    code expects to see a \begin{document} ... \end{document}.
1602  */
TXvtt(TeXEntry * e)1603 void TXvtt( TeXEntry *e )
1604 {
1605     if (!InDocument) {
1606 	TXStyleEPSF( TeXlist, fpin[curfile], fpout );
1607 	TXStyleAnlhtext( TeXlist, fpin[curfile], fpout );
1608 	InDocument = 1;
1609 	TXStartDoc(1);
1610 	TXtitlesection( e );
1611 	TeXskipEnv( e, "_vtt", 1 );
1612     }
1613     else {
1614 	TXsection( e );
1615     }
1616 }
TXvt(TeXEntry * e)1617 void TXvt( TeXEntry *e )
1618 {
1619     if (!InDocument) {
1620 	TXStyleEPSF( TeXlist, fpin[curfile], fpout );
1621 	TXStyleAnlhtext( TeXlist, fpin[curfile], fpout );
1622 	InDocument = 1;
1623 	TXStartDoc(1);
1624 	TeXskipEnv( e, "_vtt", 1 );
1625     }
1626 }
TXdetails(TeXEntry * e)1627 void TXdetails( TeXEntry *e )
1628 {
1629     TeXoutstr( fpout, "(Press " );
1630     TXhref( e );
1631     TeXoutstr( fpout, " for details)" );
1632 }
1633 
1634 /* ----------------------------------------------------------------------- */
1635 /* Code for long environments:
1636    iftex
1637    tex
1638    example
1639    verbatim
1640    */
1641 
1642 /* Skip over an environment.  If flag, write out text and process TeX command,
1643    otherwise, just skip */
TeXskipEnv(TeXEntry * e,char * name,int flag)1644 void TeXskipEnv( TeXEntry *e, char *name, int flag )
1645 {
1646     int  nsp, ch;
1647     FILE *fout = fpout;
1648     int  last_was_nl  = 0;
1649     int  last_skip    = AmSkipping;
1650     char *btext, *etext;
1651     int  nargs;
1652     int line_num = LineNo[curfile];  /* Line number where we start */
1653 
1654     /* If we are here, we assume that we are in valid TeX.  This is a
1655        temporary hack.  What we'd like to do is wait until we see either
1656        a command that generates non-blank output or a section/chapter/part
1657        command */
1658     if (!InOutputBody) {
1659 	if (DebugOutput) printf( "Starting a page in skipenv\n" );
1660 	/* Start the page if we haven't already */
1661 	WriteHeadPage( fpout );
1662 	/* We don't have a good title.  Use the input file name */
1663 	WriteFileTitle( fpout, InFName[0] );
1664 	/* WriteBeginPage sets InOutputBody to 1 */
1665 	WriteBeginPage( fpout );
1666 	/* In case we created a new file */
1667 	fout = fpout;
1668     }
1669 
1670 /* First, remove any newlines */
1671     if (strcmp( name, "verbatim" ) != 0 &&
1672 	(!UsingLatexinfo || strcmp( name, "example" ) != 0)) {
1673 	/* Preserve blanks before first non-blank character */
1674 	SCSkipNewlines2( fpin[curfile] );
1675     }
1676 
1677 /* Flush output at the beginning of each environment (makes debugging a
1678    little easier) */
1679     fflush( fpout );
1680     if (DebugCommands)
1681 	fprintf( stdout, "Processing environment %s (skipenv)\n", name );
1682     AmSkipping = !flag;
1683     PUSHCURTOK;
1684     while (1) {
1685 	while ( (ch =
1686 		 SCTxtFindNextANToken(fpin[curfile], curtok, MAX_TOKEN, &nsp )) == EOF) {
1687 	    if (DebugCommands)
1688 		fprintf( stdout, "EOF in TeXskipEnv\n" );
1689 	    /* Special case for vtt */
1690 	    if (curfile == 0 && strcmp( name, "_vtt" ) == 0) {
1691 		break;
1692 	    }
1693 	    TXPopFile();
1694 	}
1695 	if (ch == EOF) break;
1696 	if (InVerbatim && ch == CommandChar) {
1697 	    last_was_nl  = 0;
1698 	    TeXoutsp( fout, nsp );
1699 	    TeXReadMacroName( curtok );
1700 	    if (strcmp( curtok, "end" ) == 0) {
1701 		if (DebugCommands)
1702 		    fprintf( stdout, "Getting argument for end{}\n" );
1703 		TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN,
1704 			       "TXSkipEnv", e->name );
1705 		if (strcmp( name, curtok ) == 0) {
1706 		    /* We've found the end of the verbatim */
1707 		    break;
1708 		}
1709 		else {
1710 		    /* Ignore it */
1711 		    TeXoutstr( fout, "\\end{" );
1712 		    TeXoutstr( fout, curtok );
1713 		    TeXoutstr( fout, "}" );
1714 		}
1715 	    }
1716 	    else {
1717 		TeXoutstr( fout, "\\" );
1718 		TeXoutstr( fout, curtok );
1719 	    }
1720 	    continue;
1721 	}
1722 
1723 	if (InVerbatim) {
1724 	    TeXoutsp( fout, nsp );
1725 	    if (ch == '\n') {
1726 		/* In preformatted output, we don't need any pars */
1727 		TeXoutstr( fout, NewLineString );
1728 		last_was_nl = 1;
1729 	    }
1730 	    else
1731 		TeXoutstr( fout, curtok );
1732 	}
1733 	else {
1734 	    if (ch == CommentChar) {
1735 		SCTxtDiscardToEndOfLine( fpin[curfile] );
1736 		LineNo[curfile]++;
1737 		continue;
1738 	    }
1739 	    if (ch == MathmodeChar && !UsingLatexinfo) {
1740 		TeXoutsp( fout, nsp );
1741 		TXProcessDollar( e, LatexMath, 1 );
1742 	    }
1743 	    else if (ch == AlignChar && HandleAlign &&
1744 		     lstack[lSp].env == TXTABULAR) {
1745 		/* We want to be prepared to put an alignment command,
1746 		   but to support the \multicolumn command, we
1747 		   need to wait until we are sure that the next
1748 		   command is not \multicolumn.  Not handled yet */
1749 		TeXPutAlign();
1750 	    }
1751 	    else if (ch == CommandChar) {
1752 		last_was_nl  = 0;
1753 		TeXoutsp( fout, nsp );
1754 		TeXReadMacroName( curtok );
1755 
1756 		if (strcmp( curtok, "end" ) == 0) {
1757 		    if (DebugCommands)
1758 			fprintf( stdout, "Getting argument for end{}\n" );
1759 		    TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN,
1760 				   "TeXSkipEnv", e->name );
1761 		    if (LookupEnv( curtok, &btext, &etext, &nargs)) {
1762 			if (etext) {
1763 			    if (DebugCommands)
1764 				fprintf( stdout, "Pushing back |%s|\n", etext );
1765 			    SCPushToken( etext );
1766 			}
1767 		    }
1768 		    else if (strcmp( curtok, name ) != 0) {
1769 			if (flag)
1770 			    fprintf( ferr,
1771 		  "<skipEnv>%s does not match %s (started at line %d), at %s line %d\n",
1772 		  curtok, name, line_num,
1773  	          InFName[curfile] ? InFName[curfile]: "", LineNo[curfile] );
1774 		    }
1775 		    else
1776 			break;
1777 		}
1778 		else {
1779 		    /* Need to process TeX command */
1780 		    if (flag) {
1781 			TeXProcessCommand( curtok, fpin[curfile], fout );
1782 			/* Incase we change fpout */
1783 			fout = fpout;
1784 		    }
1785 		}
1786 	    }
1787 	    else if (ch == ActiveChar) {
1788 		TXActiveCharDo(curtok);
1789 	    }
1790 	    else {
1791 		if (flag) {
1792 		    TeXoutsp( fout, nsp );
1793 		    if (ch == '\n') {
1794 			if (last_was_nl) {
1795 			    TXWritePar( fout );
1796 			    SCSkipNewlines( fpin[curfile] );
1797 			}
1798 			else if (lSp >= 0)
1799 			    (*lstack[lSp].newline)( fout );
1800 			else
1801 			    TeXoutNewline( fout );
1802 			last_was_nl = 1;
1803 		    }
1804 		    else {
1805 			if (ch == LbraceChar) {
1806 			    if (BraceCount >= MAX_BLOC) {
1807 				TeXAbort( "", "Braces too deep" );
1808 			    }
1809 			    strncpy( bloc[BraceCount].filename, InFName[curfile],
1810 				     MAX_BLOC_FNAME );
1811 			    bloc[BraceCount++].lineno = LineNo[curfile];
1812 			    /* fprintf( fout, "**+%d[%d]**",
1813 			       LineNo[curfile], BraceCount ); */
1814 			    TXbgroup( e );
1815 			}
1816 			else if (ch == RbraceChar) {
1817 			    BraceCount--;
1818 			    /* fprintf( fout, "**-%d[%d]**",
1819 			       LineNo[curfile], BraceCount ); */
1820 			    if (BraceCount < 0) {
1821 				fprintf( ferr,
1822 				 "Improperly nested brace at %s line %d in ",
1823 				 InFName[curfile] ? InFName[curfile]: "",
1824 				 LineNo[curfile] );
1825 				PrintLastSectionName( ferr );
1826 				/* Reset brace count */
1827 				BraceCount = 0;
1828 			    }
1829 			    TXegroup( e );
1830 			} else {
1831 			    TeXoutstr( fout, curtok );
1832 			}
1833 			last_was_nl  = 0;
1834 		    }
1835 		}
1836 	    }
1837 	}
1838     }
1839     POPCURTOK;
1840     AmSkipping = last_skip;
1841 /* Skip the newlines and the end of the environment */
1842     SCSkipNewlines( fpin[curfile] );
1843     if (DebugCommands)
1844 	fprintf( stdout, "Done with environment %s\n", name );
1845 }
1846 
1847 /* This is a version of skipEnv that just copies to output; it DOES handle
1848    nested versions of itself.
1849 
1850    if doout is false, generate no output
1851 
1852    As a special case, we need to watch for \label{...}, since we may need that
1853    name to resolve references.
1854 
1855    We must process comments, since we must properly detect the following
1856    \begin{foo}
1857    ...
1858    %\end{foo}
1859    %\begin{foo}
1860    ...
1861    \end{foo}
1862 
1863    That is, if % is a comment character, we must stop looking for \end
1864    to match.  If we are doing something like verbatim, we have to
1865    turn off the comment character (set to null)
1866 */
TeXskipRaw(TeXEntry * e,char * name,int doout)1867 void TeXskipRaw( TeXEntry *e, char *name, int doout )
1868 {
1869     int  nsp, ch;
1870     FILE *fout = fpout;
1871 /*     int  (*oldtrans)( char *, int ); */
1872     /* char commentchar; */
1873 
1874 /* First, remove any newlines */
1875     SCSkipNewlines( fpin[curfile] );
1876 
1877 /*
1878   commentchar = SCGetCommentChar();
1879   SCSetCommentChar( 0 );
1880   */
1881     /*
1882     oldtrans = SCSetTranslate( (int (*)( char *, int ))0 );
1883     */
1884     if (DebugCommands)
1885 	fprintf( stdout, "Processing environment %s (skipRaw)\n", name );
1886     PUSHCURTOK;
1887     while (1) {
1888 	while ( (ch = TeXReadToken( curtok, &nsp )) == EOF) {
1889 	    if (DebugCommands)
1890 		fprintf( stdout, "EOF in TeXskipRaw\n" );
1891 	    /* Special case for vtt */
1892 	    if (curfile == 0 && strcmp( name, "_vtt" ) == 0) {
1893 		break;
1894 	    }
1895 	    TXPopFile();
1896 	}
1897 	if (ch == EOF) break;
1898 	if (DebugCommands) {
1899 	    fprintf( stdout, "Token is %s\n", curtok );
1900 	}
1901 	if (ch == CommentChar) {
1902 	    /* Handle comments */
1903 	    if (doout) {
1904 		TeXoutsp( fout, nsp );
1905 		TeXoutstr( fout, curtok );
1906 		TeXoutstr( fout, NewLineString );
1907 	    }
1908 	    SCTxtDiscardToEndOfLine( fpin[curfile] );
1909 	}
1910 	else if (ch == CommandChar) {
1911 	    if (doout)
1912 		TeXoutsp( fout, nsp );
1913 
1914 	    if (strcmp( curtok, "\\end" ) == 0) {
1915 		if (DebugCommands)
1916 		    fprintf( stdout, "Getting argument for end{}\n" );
1917 		TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN,
1918 			       "TXSkipRaw", e->name );
1919 		/* Check for a user-defined environment type */
1920 		if (strcmp( curtok, name ) != 0) {
1921 		    if (doout)
1922 			fprintf( fout, "\\end{%s}\n", curtok );
1923 		}
1924 		else {
1925 		    break;
1926 		}
1927 	    }
1928 	    else if (strcmp( curtok, "\\label" ) == 0) {
1929 		/* Need to process a label command */
1930 		TXlabel( e );
1931 		/* Add a dummy incase this is the only thing on a line */
1932 		if (doout)
1933 		    TeXoutstr( fout, "\\label{eq-foo}" );
1934 	    }
1935 	    else if (strcmp( curtok, "\\caption" ) == 0) {
1936 		/* Need to increment the appropriate counter/name */
1937 		TXcaptionHandling( e );
1938 	    }
1939 	    else {
1940 		if (doout) {
1941 		    TeXoutstr( fout, curtok );
1942 		}
1943 	    }
1944 	}
1945 	else {
1946 	    if (doout) {
1947 		TeXoutsp( fout, nsp );
1948 		TeXoutstr( fout, curtok );
1949 	    }
1950 	}
1951     }
1952 /* Skip the newlines and the end of the environment */
1953 /*     SCSetTranslate( oldtrans ); */
1954 /*
1955   SCSetCommentChar( commentchar );
1956   */
1957     SCSkipNewlines( fpin[curfile] );
1958     if (DebugCommands)
1959 	fprintf( stdout, "Done with environment %s\n", name );
1960     POPCURTOK;
1961 }
1962 
1963 
1964 /*
1965    See if the named file exists in the current or split directory
1966  */
FileExists(char * fname)1967 int FileExists( char *fname )
1968 {
1969     FILE *fp;
1970     int  exists = 0;
1971     if (splitlevel >= 0) {
1972 	char *fname2;
1973 	fname2 = (char *)MALLOC( 1024 );
1974 	if (!fname2) {
1975 	    fprintf( stderr, "Out of memory!\n" );
1976 	    exit(1);
1977 	}
1978 	sprintf( fname2, "%s/%s", splitdir, fname );
1979 	fp = fopen( fname2, "r" );
1980 	FREE( fname2 );
1981     }
1982     else {
1983 	fp = fopen( fname, "r" );
1984     }
1985     if (fp) {
1986 	exists = 1;
1987 	fclose( fp );
1988     }
1989     return exists;
1990 }
1991 
1992 /*
1993    Manage math mode (\[ .. \] or \( ... \) )
1994  */
TXmathmode(TeXEntry * e)1995 void TXmathmode( TeXEntry *e )
1996 {
1997 #ifndef FOO
1998     /* If display math, make it look like $$ starts it */
1999     if (e->name[0] == '[') SCPushToken( "$" );
2000     TXProcessDollar( e, LatexMath, 1 );
2001     return;
2002 #else
2003     if (DebugCommands) fprintf( stdout, "Starting math mode processing\n" );
2004     if (LatexUnknownEnvs || LatexMath) {
2005 	char bname[100];
2006 	char fname[100];
2007 	char name[4];
2008 	snprintf( bname, sizeof(bname), "%s%d", imgfilebase, imageno++ );
2009 	strcpy( fname, bname );
2010 	strcat( fname, "." );
2011 	strcat( fname, ImageExt );
2012 
2013 	name[0] = '\\';
2014 	name[1] = e->name[0];
2015 	name[2] = 0;
2016 	if (LatexAgain || !FileExists( fname )) {
2017 	    /* We always process, because either LatexAgain is 1,
2018 	       or the file does not exists */
2019 	    RunLatex( (char *)0, (char *)0, bname, name, ImageExt, 1 );
2020 	}
2021 	else if (FileExists( fname ) && !LatexAgain) {
2022 	    /* Skip over the section */
2023 	    RunLatex( (char *)0, (char *)0, bname, name, ImageExt, 0 );
2024 	}
2025 
2026 	if (name[1] == '[') {
2027 	    /*
2028 	      if (envjumpname && envjumpname[0])
2029 	      TXAnchoredImage( e, envjumpname, fname );
2030 	      else
2031 	      */
2032 	    TXimage( e, fname );
2033 	}
2034 	else  /* \( ... \)  */
2035 	    TXInlineImage( e, fname );
2036     }
2037 #endif
2038 }
2039 
2040 int itemizelevel = -1;
2041 
2042 /* Copy this envrionment without doing anything */
TeXBenign(TeXEntry * e,char * name)2043 void TeXBenign( TeXEntry *e, char *name )
2044 {
2045     TXbgroup( e );
2046     TeXskipEnv( e, name, 1 );
2047     TXegroup( e );
2048 }
2049 
TeXitemize(TeXEntry * e)2050 void TeXitemize( TeXEntry *e )
2051 {
2052     itemizelevel++;
2053     lstack[++lSp].env	    = TXITEMIZE;
2054     lstack[lSp].newline	    = TeXoutNewline;
2055     lstack[lSp].label_node_name = 0;
2056     lstack[lSp].label_text	    = 0;
2057     lstack[lSp].line_num = LineNo[curfile];
2058     lstack[lSp].extra_data = 0;
2059     TXbitemize( e );
2060     TeXskipEnv( e, "itemize", 1 );
2061     TXeitemize( e );
2062     itemizelevel--;
2063     lSp--;
2064     TXWriteStartNewLine( fpout );
2065 }
2066 
TeXenumerate(TeXEntry * e)2067 void TeXenumerate( TeXEntry *e )
2068 {
2069     itemizelevel++;
2070     lstack[++lSp].env	    = TXENUMERATE;
2071     lstack[lSp].num		    = 1;
2072     lstack[lSp].newline	    = TeXoutNewline;
2073     lstack[lSp].label_node_name = 0;
2074     lstack[lSp].label_text	    = 0;
2075     lstack[lSp].line_num = LineNo[curfile];
2076     lstack[lSp].extra_data = 0;
2077     TXbenumerate( e );
2078     TeXskipEnv( e, "enumerate", 1 );
2079     TXeenumerate( e );
2080     itemizelevel--;
2081     lSp--;
2082 /* Note that the begin/end enumerate generates a newline already */
2083 /* TXWriteStartNewLine( fpout ); */
2084 }
2085 
TeXdescription(TeXEntry * e)2086 void TeXdescription( TeXEntry *e )
2087 {
2088     itemizelevel++;
2089     lstack[++lSp].env	    = TXDESCRIPTION;
2090     lstack[lSp].newline	    = TeXoutNewline;
2091     lstack[lSp].label_node_name = 0;
2092     lstack[lSp].label_text	    = 0;
2093     lstack[lSp].line_num = LineNo[curfile];
2094     lstack[lSp].extra_data = 0;
2095     TXbdescription( e );
2096     TeXskipEnv( e, "description", 1 );
2097     TXedescription( e );
2098     itemizelevel--;
2099     lSp--;
2100     TXWriteStartNewLine( fpout );
2101 }
2102 
2103 /*
2104    This handles \begin{list}{itemtext} ... \end{list}
2105    itemtext is stored in p1 and MUST BE PROCESSED AGAIN
2106  */
TeXDoList(TeXEntry * e,char * itemtext,char * itemcommands)2107 void TeXDoList( TeXEntry *e, char *itemtext, char *itemcommands )
2108 {
2109     itemizelevel++;
2110     lstack[++lSp].env		= TXLIST;
2111     lstack[lSp].num		= 1;
2112     lstack[lSp].newline		= TeXoutNewline;
2113     lstack[lSp].p1		= itemtext;
2114     lstack[lSp].p2		= itemcommands;
2115     lstack[lSp].label_node_name	= 0;
2116     lstack[lSp].label_text	= 0;
2117     lstack[lSp].line_num        = LineNo[curfile];
2118     lstack[lSp].extra_data = 0;
2119 /* May want description instead of itemize */
2120 /* TXbitemize( e ); */
2121     TeXskipEnv( e, "list", 1 );
2122 /* TXeitemize( e ); */
2123     itemizelevel--;
2124     lSp--;
2125     TXWriteStartNewLine( fpout );
2126 }
2127 
TeXsmall(TeXEntry * e)2128 void TeXsmall( TeXEntry *e )
2129 {
2130     TeXskipEnv( e, "small", 1 );
2131 }
2132 
2133 
TXverb(TeXEntry * e)2134 void TXverb( TeXEntry *e )
2135 {
2136     int  ch;
2137     int  match;
2138     char *p;
2139 
2140     if (DebugCommands) fprintf( stdout, "LaTeX verb command\n" );
2141     PUSHCURTOK;
2142     p = curtok;
2143     match = SCTxtGetChar( fpin[curfile] );
2144     if (match == EOF) return;
2145     while ( (ch = SCTxtGetChar( fpin[curfile] )) != match ) {
2146 	*p++ = ch;
2147     }
2148     *p++ = 0;
2149     TeXoutstr( fpout, curtok );
2150     POPCURTOK;
2151 }
2152 
TeXverbatim(TeXEntry * e)2153 void TeXverbatim( TeXEntry *e )
2154 {
2155     /* char comment_char; */
2156 /* Skip code until find an \end{verbatim}.
2157    In verbatim mode, ignore any \ EXCEPT \end{verbatim} (How?) */
2158     InVerbatim = 1;
2159     TXWriteStartNewLine( fpout );
2160     TXpreformated( fpout, 1 );
2161     TXbgroup( e );
2162     TXfont_tt( e );
2163     lstack[++lSp].env	    = TXVERBATIM;
2164     lstack[lSp].newline	    = TXWriteStartNewLine;
2165     lstack[lSp].label_node_name = 0;
2166     lstack[lSp].label_text	    = 0;
2167     lstack[lSp].line_num    = LineNo[curfile];
2168     lstack[lSp].extra_data = 0;
2169 
2170 /* Verbatim has no comments! */
2171 /*
2172   comment_char = SCGetCommentChar();
2173   SCSetCommentChar( (char)0 );
2174   */
2175     /* Skip newlines at the head of the verbatim.  This isn't precisely
2176        correct; I think that it should skip at most one.
2177        SCSkipNewlines2 is better about this. */
2178     SCSkipNewlines2( fpin[curfile] );
2179 
2180     TeXskipEnv( e, "verbatim", 1 );
2181 /*
2182    SCSetCommentChar( comment_char );
2183    */
2184     TXegroup( e );
2185     TXpreformated( fpout, 0 );
2186     lSp--;
2187     InVerbatim = 0;
2188 }
2189 
2190 /*
2191 void TeXCenter( TeXEntry *e )
2192 {
2193     TXbcenter( fpout );
2194     TeXskipEnv( e, "center", 0 );
2195     TXecenter( fpout );
2196 }
2197 
2198 void TeXCenterline( TeXEntry *e )
2199 {
2200     TXbcenter( fpout );
2201     TXasisGrouped( e );
2202     TXecenter( fpout );
2203 }
2204  */
2205 /* ----------------------------------------------------------------------- */
2206 /*
2207    There should really be a generic
2208    TeXSimpleEnv(
2209      startfcn( FILE *, TeXEntry * ), "name", endfcn( FILE *, TeXEntry * ) )
2210    for things like center and quote, as an improvement over TeXBenign
2211 
2212    Then these can be put into the basedefs by having the commands for start
2213    and end in the basedefs file and stored in the TeXEntry (which could
2214    store the functions themselves)
2215  */
TXbegin(TeXEntry * e)2216 void TXbegin( TeXEntry *e )
2217 {
2218 /* Process the beginning of an environment */
2219     char *p;
2220     char *btext, *etext;
2221     int  nargs;
2222 
2223 /*    (look for tex, enumerate, description, example, iftex, etc ) */
2224     if (DebugCommands)
2225 	fprintf( stdout, "Getting argument for begin{}\n" );
2226     PUSHCURTOK;
2227     TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN,
2228 		   "TXbegin argument", e->name );
2229     if (strcmp( curtok, "iftex" ) == 0) {
2230 	if (UseIfTex) TeXBenign( e, "iftex" );
2231 	else          TeXiftex( e );
2232     }
2233     else if (strcmp( curtok, "tex" ) == 0) TeXtex( e );
2234     /* MPI reports use example as a variation of theorem, not verbatim,
2235        as LatexInfo does.  We need to configure this somehow...
2236        In fact, we need to label it as an Example, with example numbers...
2237    */
2238     else if (UsingLatexinfo && strcmp( curtok, "example" ) == 0) TeXexample( e );
2239     else if (strcmp( curtok, "verbatim" ) == 0) TeXverbatim( e );
2240     else if (strcmp( curtok, "menu" ) == 0) TeXmenu( e );
2241     else if (strcmp( curtok, "ifinfo" ) == 0) {
2242 	if (UseIfTex) TeXskipEnv( e, "ifinfo", 0 );
2243 	else          TeXBenign( e, "ifinfo" );
2244     }
2245 /* There should be a way to specify these in the basedef file */
2246     /*    else if (strcmp( curtok, "center" ) == 0) TeXCenter( e ); */
2247     else if (strcmp( curtok, "benign" ) == 0) TeXBenign( e, "benign" );
2248     else if (strcmp( curtok, "displaymath") == 0 && !UsingLatexinfo) {
2249 	if (LatexMath) {
2250 	    TXProcessDollar( e, LatexMath, 0 );
2251 	}
2252 	else
2253 	    TeXBenign( e, "displaymath" );
2254     }
2255 #ifdef FOO
2256     else if (strcmp( curtok, "centering" ) == 0) TeXBenign( e, "centering" );
2257     else if (strcmp( curtok, "normalsize" ) == 0) TeXBenign( e, "normalsize" );
2258     else if (strcmp( curtok, "abstract" ) == 0) TeXBenign( e, "abstract" );
2259     else if (strcmp( curtok, "raggedright" ) == 0) TeXBenign( e, "raggedright" );
2260     else if (strcmp( curtok, "flushleft" ) == 0) TeXBenign( e, "flushleft" );
2261     else if (strcmp( curtok, "sloppypar" ) == 0) TeXBenign( e, "sloppypar" );
2262     else if (strcmp( curtok, "quote" ) == 0) TeXBenign( e, "quote" );
2263 #endif
2264     else if (strcmp( curtok, "slide" ) == 0) {
2265 	/* Strip the color arg */
2266 	TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN,
2267 		       "TXbegin slide", e->name );
2268 	/* Pick the equivalent slide format */
2269 	TXvt( e );
2270 	/* Need a new page ... */
2271 	TeXBenign( e, "slide" );
2272     }
2273 #ifdef FOO
2274     else if (strcmp( curtok, "quotation" ) == 0) {
2275 	TXWritePar( fpout );
2276 	TeXBenign( e, "quotation" );
2277 	TXWritePar( fpout );
2278     }
2279 #endif
2280     else if (strcmp( curtok, "figure" ) == 0) {
2281 	/* Remove optional positioning */
2282 	TXRemoveOptionalArg( curtok );
2283 	NumberedEnvironmentType = ENV_FIGURE;
2284 	TeXBenign( e, "figure" );
2285 	NumberedEnvironmentType = ENV_NONE;
2286     }
2287     else if (strcmp( curtok, "thebibliography" ) == 0) {
2288 	/* Eat the next argument */
2289 	TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN,
2290 		       "TXbegin thebibliography", e->name );
2291 	/* Need to act as if "Section{Bibliography}" seen */
2292 	SCPushToken( "{Bibliography}" );
2293 	e->ctx = (void*)(PTRINT)MinSectionKind;
2294 	TXsection( e );
2295 	TeXBenign( e, "thebibliography" );
2296     }
2297     /* displaymath should be like \[ ... \] */
2298     /* math should be like \( ... \) */
2299     /* Note that in LaTeX, \begin{foo}...\end{foo} is just like (almost)
2300        \foo ... \endfoo, and \displaymath=\[, \enddisplaymath=\] */
2301     else if (strcmp( curtok, "theindex" ) == 0) {
2302 	/* An index done with the regular system is useless, since the names
2303 	   have pages.  Instead, we skip the index entirely, then dump
2304 	   the results of our own use of \index */
2305 	SCPushToken( "{Index}" );
2306 	e->ctx = (void*)(PTRINT)MinSectionKind;
2307 	TXsection( e );
2308 	TeXskipEnv( e, "theindex", 0 );
2309 	WriteIndex( fpout, 2 );
2310     }
2311     else if (UserIndexName && strcmp( curtok, UserIndexName ) == 0) {
2312 	/* An index done with the regular system is useless, since the names
2313 	   have pages.  Instead, we skip the index entirely, then dump
2314 	   the results of our own use of \index */
2315 	SCPushToken( "{Index}" );
2316 	e->ctx = (void*)(PTRINT)MinSectionKind;
2317 	TXsection( e );
2318 	TeXskipEnv( e, UserIndexName, 0 );
2319 	WriteIndex( fpout, 2 );
2320     }
2321     else if (!LatexTables && (strcmp( curtok, "table" ) == 0)) {
2322 	/* Remove optional positioning */
2323 	TXRemoveOptionalArg( curtok );
2324 	NumberedEnvironmentType = ENV_TABLE;
2325 	TeXBenign( e, "table" );
2326 	NumberedEnvironmentType = ENV_NONE;
2327     }
2328     else if (strcmp( curtok, "small" ) == 0) TeXsmall( e );
2329 #ifdef FOO
2330     else if (strcmp( curtok, "tiny" ) == 0) TeXBenign( e, "tiny" );
2331 #endif
2332     else if (!LatexTables && (strcmp( curtok, "tabular" ) == 0))
2333 	if (HandleAlign) {
2334 	    lstack[++lSp].env = TXTABULAR;
2335 	    lstack[lSp].newline = TeXoutNewline;
2336 	    lstack[lSp].line_num = LineNo[curfile];
2337 	    lstack[lSp].extra_data = 0;
2338 	    TeXGetTabularDefn();
2339 	    TeXBeginHalignTable();
2340 	    TeXBenign( e, "tabular" );
2341 	    TeXEndHalignTable();
2342 	    --lSp;
2343 	}
2344 	else {
2345 	    TeXtabular( e );
2346 	}
2347     else if (strcmp( curtok, "document" ) == 0) {
2348 	InDocument = 1;
2349 	TXStartDoc(1);
2350 	TXInitialCommands(); /* Push back any initial commands */
2351 	TeXBenign( e, "document" );
2352 	InDocument = 0;
2353 	TXStartDoc(0);
2354     }
2355     else if (strcmp( curtok, "description" ) == 0) TeXdescription( e );
2356     else if (strcmp( curtok, "itemize" ) == 0) TeXitemize( e );
2357     else if (strcmp( curtok, "enumerate" ) == 0) TeXenumerate( e );
2358     else if (strcmp( curtok, "list" ) == 0) {
2359 	char *itemtext, *itemcommands;
2360 	/* 2 arguments; first is text for \item, second is code executed for
2361 	   each item */
2362 	/* fprintf( stdout,"Ignoring details list environment defn for now\n" ); */
2363 	/* Note that we need to allocate the itemtext and itemcommands,
2364 	   since list environments may be nested */
2365 	ALLOC_TOKEN(itemtext);
2366 	ALLOC_TOKEN(itemcommands);
2367 	/*
2368 	  itemtext     = (char *)MALLOC( MAX_TOKEN );    CHKPTR(itemtext);
2369 	  itemcommands = (char *)MALLOC( MAX_TOKEN );    CHKPTR(itemcommands);
2370 	  */
2371 	TeXMustGetArg( fpin[curfile], itemtext, MAX_TOKEN,
2372 		       "TXbegin list itemtext", e->name );
2373 	TeXMustGetArg( fpin[curfile], itemcommands, MAX_TOKEN,
2374 		       "TXbegin list itemcommands", e->name );
2375 	TeXDoList( e, itemtext, itemcommands );
2376 	/*
2377 	  FREE( itemtext );
2378 	  FREE( itemcommands );
2379 	  */
2380 	FREE_TOKEN(itemcommands);
2381 	FREE_TOKEN(itemtext);
2382     }
2383     else if (LookupEnv( curtok, &btext, &etext, &nargs)) {
2384 	/* Really needs to look for arguments; use def's pushback with eval */
2385 	if (DebugCommands) {
2386 	    fprintf( stdout, "Processing user-defined environment %s\n", curtok );
2387 	    fprintf( stdout, "defined as begin:|%s|\nend:|%s|\n",
2388 		     btext ? btext : "<NULL>", etext ? etext : "<NULL>" );
2389 	}
2390 	PushBeginEnv( btext, nargs );
2391 	/* TeXBenign( e, curtok ); */
2392     }
2393 /*
2394   else if () {
2395   This code should look up the name \"curtok", if defined, then do that.
2396   The corresponding end code should look up \end"curtok".  This can
2397   be used to handle things like \begin{em} ... \end{em}, which has
2398   the effect of {\em ...}  .  We might be able to integrate this
2399   with lookupenv...
2400   }
2401   */
2402     else {
2403 	p = STRDUP( curtok );  CHKPTR(p);
2404 	/* Identify environment as table, figure, equation, or none */
2405 	if (strcmp( p, "table" ) == 0) {
2406 	    NumberedEnvironmentType = ENV_TABLE;
2407 	}
2408 	else if (strcmp( p, "figure" ) == 0) {
2409 	    NumberedEnvironmentType = ENV_FIGURE;
2410 	}
2411 	else if (strcmp( p, "equation" ) == 0 ||
2412 		 strcmp( p, "eqnarray" ) == 0) {
2413 	    NumberedEnvironmentType = ENV_EQUATION;
2414 	    TeXSetEnvJump( "Equation" );
2415 	    TXcaptionHandling( e );
2416 	}
2417 	if (LatexUnknownEnvs ||
2418 	    (LatexTables &&
2419 	     (strcmp( curtok, "tabular" ) == 0 ||
2420 	      strcmp( curtok, "table" ) == 0))) {
2421 	    char bname[100];
2422 	    char fname[100];
2423 
2424 	    if (DebugCommands) {
2425 		fprintf( stdout, "Using Latex to process an environment\n" );
2426 	    }
2427 	    snprintf( bname, sizeof(bname), "%s%d", imgfilebase, imageno++ );
2428 	    strcpy( fname, bname );
2429 	    strcat( fname, "." );
2430 	    strcat( fname, ImageExt );
2431 	    if (LatexAgain || !FileExists( fname )) {
2432 		RunLatex( curtok, (char *)0, bname, (char *)0, ImageExt, 1 );
2433 	    }
2434 	    else if (FileExists( fname ) && !LatexAgain) {
2435 		RunLatex( curtok, (char *)0, bname, (char *)0, ImageExt, 0 );
2436 	    }
2437 	    /* If this is a figure or table environment, generate a target
2438 	       location for the image */
2439 	    if (NumberedEnvironmentType != ENV_NONE && envjumpname &&
2440 		envjumpname[0])
2441 		TXAnchoredImage( e, envjumpname, fname );
2442 	    else
2443 		TXimage( e, fname );
2444 	}
2445 	else {
2446 	    if (!AmSkipping) /* &&  InDocument) */ {
2447 		fprintf( ferr, "Unknown environment %s, %s line %d\n",
2448 			 curtok,
2449 			 InFName[curfile] ? InFName[curfile]: "",
2450 			 LineNo[curfile] );
2451 	    }
2452 	    TeXBenign( e, p );
2453 	    if (!AmSkipping) /* &&  InDocument) */ {
2454 		fprintf( ferr, "End of unknown environment %s, %s line %d\n",
2455 			 curtok,
2456 			 InFName[curfile] ? InFName[curfile]: "",
2457 			 LineNo[curfile] );
2458 	    }
2459 	}
2460 	NumberedEnvironmentType = ENV_NONE;
2461 	FREE( p );
2462     }
2463     POPCURTOK;
2464 }
2465 
TXend(TeXEntry * e)2466 void TXend( TeXEntry *e )
2467 {
2468     PUSHCURTOK;
2469     TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN, "TXend", e->name );
2470     /* Finish processing of an environment */
2471     if (lstack[lSp].env == TXTABULAR) {
2472 	if (strcmp( "tabular", curtok ) != 0) {
2473 	    fprintf( stderr, "Saw end{%s} but expected tabular\n", curtok );
2474 	}
2475 	else {
2476 	    TeXEndHalignTable();
2477 	    FREE( lstack[lSp].extra_data );
2478 	    lSp--;
2479 	}
2480     }
2481     POPCURTOK;
2482 }
2483 
TXitem(TeXEntry * e)2484 void TXitem( TeXEntry *e )
2485 {
2486     char buf[12];
2487 /* Process an item based on the current environment */
2488     TXWriteStartItem( fpout );
2489     if (lstack[lSp].env == TXDESCRIPTION) {
2490 	TXbdesItem( e );
2491 	TXbgroup( e );
2492 	TXbf( e );
2493 	/* Defer reading option until here in case it causes direct output (for
2494 	   example, a pointer reference) */
2495 	if (DebugCommands)
2496 	    fprintf( stdout, "Getting argument for item in description\n" );
2497 	PUSHCURTOK;
2498 	strncpy( CmdName, e->name, sizeof(CmdName)-1 );
2499 	if (TeXGetGenArg( fpin[curfile], curtok, MAX_TOKEN, '[', ']', 1 ) != 1) {
2500 	  /* It is legal to forget the [], but probably unintended */
2501 	  fprintf( stdout,
2502 		   "Missing [] in description item in File %s line %d\n",
2503 		   InFName[curfile] ? InFName[curfile]: "", LineNo[curfile] );
2504 	  strcpy( curtok, " " );
2505 	  /* TeXAbort( "TXbegin description", e->name ); */
2506 	}
2507 	CmdName[0] = 0;
2508 	/* FIXME: Need to process the curtok for commands */
2509 #if 0
2510 	TeXoutstr( fpout, curtok );
2511 	TXegroup( e );
2512 	TXedesItem( e );
2513 #else
2514 	SCPushCommand("<dd>\n");
2515 	/* Not quite right - need an endgroup, or need to change */
2516 	BraceCount++;
2517 	SCPushToken("}");
2518 	SCPushToken(curtok);
2519 #endif
2520 	POPCURTOK;
2521     }
2522     else if (lstack[lSp].env == TXENUMERATE) {
2523       /* \item[] is legal in \enumerate */
2524       PUSHCURTOK;
2525       TXRemoveOptionalArg( curtok );
2526       POPCURTOK;
2527       /* We don't want to do this on the FIRST item in an enumerate */
2528       if (lstack[lSp].num > 1)
2529 	TXWriteStartNewLine( fpout );
2530       sprintf( buf, "%d. ", lstack[lSp].num++ );
2531       TeXoutstr( fpout, buf );
2532     }
2533     else if (lstack[lSp].env == TXLIST) {
2534       /* We might want to allow bullet to be identified... */
2535       /* Remove any [] in the list argument.  Save the value */
2536       /* eventually, if the is a labellength, use an html table to
2537          force the correct width */
2538       PUSHCURTOK;
2539       curtok[0] = 0;
2540       TXRemoveOptionalArg( curtok );
2541       TXWriteStartNewLine( fpout );
2542       /* This really needs to process the string FIRST before outputting it... */
2543       if (curtok[0]) {
2544 	SCPushToken( curtok );
2545       }
2546       else {
2547 	/* p1 is the itemtext, which is the text used if there is no optional
2548 	   argument */
2549 	if (lstack[lSp].p1)
2550 	  SCPushToken( lstack[lSp].p1 );
2551       }
2552       /* TeXoutstr( fpout, lstack[lSp].p1 ); */
2553       if (lstack[lSp].p2)
2554 	SCPushToken( lstack[lSp].p2 );
2555       POPCURTOK;
2556     }
2557     else {
2558       /* Assume itemize for now.  This is incorrect for an index */
2559       TXbgroup( e );
2560       TXoutbullet( e );
2561       TXegroup( e );
2562     }
2563     SCSkipNewlines( fpin[curfile] );
2564 }
2565 
2566 /* Process index entries */
TXcindex(TeXEntry * e)2567 void TXcindex( TeXEntry *e )
2568 {
2569     if (DebugCommands)
2570 	fprintf( stdout, "Getting argument for %s\n", e->name );
2571     PUSHCURTOK;
2572     TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN, "TXcindex", e->name );
2573     AddToIndex( curtok, CurNodename, CurSeqnum - 1, 1 );
2574     POPCURTOK;
2575 }
2576 
TXfindex(TeXEntry * e)2577 void TXfindex( TeXEntry *e )
2578 {
2579     if (DebugCommands)
2580 	fprintf( stdout, "Getting argument for %s\n", e->name );
2581     PUSHCURTOK;
2582     TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN, "TXfindex", e->name );
2583     AddToIndex( curtok, CurNodename, CurSeqnum - 1, 0 );
2584     POPCURTOK;
2585 }
2586 
TXindex(TeXEntry * e)2587 void TXindex( TeXEntry *e )
2588 {
2589     if (DebugCommands)
2590 	fprintf( stdout, "Getting argument for %s\n", e->name );
2591     PUSHCURTOK;
2592     TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN, "TXindex", e->name );
2593     AddToIndex( curtok, CurNodename, CurSeqnum - 1, 2 );
2594     POPCURTOK;
2595 }
2596 
TXprintindex(TeXEntry * e)2597 void TXprintindex( TeXEntry *e )
2598 {
2599     int which;
2600 
2601     if (DebugCommands)
2602 	fprintf( stdout, "Getting argument for %s\n", e->name );
2603     PUSHCURTOK;
2604     TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN, "TXprintindex", e->name );
2605     which = 0;
2606     if (strcmp( curtok, "cp" ) == 0) which = 1;
2607     WriteIndex( fpout, which );
2608     POPCURTOK;
2609 }
2610 
TXmakeindex(TeXEntry * e)2611 void TXmakeindex( TeXEntry *e )
2612 {
2613 /* Does makeindex do some sort of heading? */
2614 /* WriteIndex( fpout, 2 ); */
2615 }
2616 
2617 /* Process \documentstyle[]{}.  Note that we need to save the document
2618    style inorder to process code in latex mode */
2619 char *preamble = 0;
2620 char *predoc   = 0;
2621 char *documentcmd = 0;
2622 
2623 #ifndef MAX_PATH_LEN
2624 #define MAX_PATH_LEN 1024
2625 #endif
TXLoadPackage(const char * p)2626 void TXLoadPackage( const char *p )
2627 {
2628     /* First, try to find name.def in the definition list.  If
2629        found, load that */
2630     if (userpath[0]) {
2631 	char filename[MAX_PATH_LEN];
2632 	char fullfilename[MAX_PATH_LEN];
2633 
2634 	strcpy( filename, p );
2635 	strcat( filename, ".def" );
2636 	if (SYGetFileFromPath( userpath, filename, "DOCTEXT_USERPATH",
2637 			       fullfilename, 'r' )) {
2638 	    RdBaseDef( fullfilename );
2639 	    return;
2640 	}
2641     }
2642 
2643     /* Otherwise, check against predefined commands */
2644     if (strcmp( p, "latexinfo" ) == 0 ||
2645 	strcmp( p, "latexinfo-1.2") == 0)
2646 	TXStyleLatexInfo( TeXlist, fpin[curfile], fpout );
2647     else if (strcmp( p, "epsf" ) == 0)
2648 	TXStyleEPSF( TeXlist, fpin[curfile], fpout );
2649     else if (strcmp( p, "psfig" ) == 0) {
2650 	TXStylePsfig( TeXlist, fpin[curfile], fpout );
2651 	/* Must defined the ESPF style, since we currently use
2652 	   them to implement psfig */
2653 	TXStyleEPSF( TeXlist, fpin[curfile], fpout );
2654     }
2655     else if (strcmp( p, "graphics" ) == 0 ||
2656 	     strcmp( p, "graphicx" ) == 0 ||
2657 	     strcmp( p, "graphicsx" ) == 0) {
2658 	TXInsertName( TeXlist, "includegraphics", TXincludegraphics,
2659 		      1, (void *)0 );
2660     }
2661     else if (strcmp( p, "11pt" ) == 0 ||
2662 	     strcmp( p, "12pt" ) == 0 ||
2663 	     strcmp( p, "twoside" ) == 0 ||
2664 	     strcmp( p, "fleqn" ) == 0)
2665 	/* Standard packages that do not affect HTML formatting */
2666 	    ;
2667     /* My private styles ... */
2668     else if (strcmp( p, "fileinclude" ) == 0)
2669 	;
2670     else if (strcmp( p, "funclist" ) == 0)
2671 	TXStyleFunclist( TeXlist, fpin[curfile], fpout );
2672     else if (strcmp( p, "tpage" ) == 0)
2673 	TXStyleTpage( TeXlist, fpin[curfile], fpout );
2674     else if (strcmp( p, "tools" ) == 0)
2675 	TXStyleTools( TeXlist, fpin[curfile], fpout );
2676     else if (strcmp( p, "anlhtext" ) == 0)
2677 	TXStyleAnlhtext( TeXlist, fpin[curfile], fpout );
2678     else if (strcmp( p, "handpage" ) == 0)
2679 	TXStyleANLHandpage( TeXlist, fpin[curfile], fpout );
2680     else if (strcmp( p, "urlsimple" ) == 0 ||
2681 	     strcmp( p, "code" ) == 0) {
2682 	/* Simple defines URL and AURL */
2683 	;
2684     }
2685     else {
2686 	fprintf( ferr,
2687 "Unknown documentstyle or package %s; provide\n\
2688 %s.def to define the commands\n", p, p );
2689 	/* Question here is whether we should try to read the document
2690 	   style file */
2691     }
2692 }
2693 
2694 /* Latex 2e specifies additional stuff using \usepackage, which may have
2695    a comma separate list of packages.  Format is
2696    \usepackage[optional fields]{package-name}
2697    Note that it is common for the optional fields to be across multiple
2698    lines.
2699  */
TXusepackage(TeXEntry * e)2700 void TXusepackage( TeXEntry *e )
2701 {
2702     char *p, *ptr, *p1;
2703     int hadOptional=0;
2704 
2705     PUSHCURTOK;
2706     strncpy( CmdName, e->name, sizeof(CmdName)-1 );
2707     TXRemoveOptionalArg(curtok);
2708     if (curtok[0]) {
2709 	strcat(preamble, "\\usepackage[");
2710 	strcat(preamble, curtok);
2711 	strcat(preamble, "]");
2712 	hadOptional = 1;
2713     }
2714     TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN, "TXusepackage", e->name );
2715     if (curtok[0]) {
2716 	if (!hadOptional) {
2717 	    strcat( preamble, "\\usepackage" );
2718 	}
2719 	strcat(preamble, "{");
2720 	strcat( preamble, curtok );
2721 	strcat( preamble, "}\n" );
2722     }
2723     if (1) {
2724 	fprintf( stdout, "Procssing usepackage[...]{%s} in %s line %d\n",
2725 		 curtok, InFName[curfile] ? InFName[curfile]: "",
2726 		 LineNo[curfile] );
2727     }
2728     /* Now, get comma-separate list of packages and load each one */
2729     p = curtok;
2730     while (*p) {
2731 	ptr = p;
2732 	while (*ptr && *ptr != ',') ptr++;
2733 	if (!*ptr) ptr[1] = 0; /* so the p = ptr + 1 below will exit */
2734 	*ptr = 0; /* Change , to null */
2735 	/* Skip over any path part of the specification */
2736 	p1 = strrchr( p, '/' );
2737 	if (p1) p = p1 + 1;
2738 	TXLoadPackage( p );
2739 	p = ptr + 1;
2740     }
2741 
2742     CmdName[0] = 0;
2743     POPCURTOK;
2744 }
2745 
TXdocumentstyle(TeXEntry * e)2746 void TXdocumentstyle( TeXEntry *e )
2747 {
2748     static char name[] = "documentstyle";
2749     char *p, *ptr, *p1;
2750 
2751     preamble    = (char *)MALLOC( 20000 ); CHKPTR(preamble);
2752     predoc	= (char *)MALLOC( 20000 ); CHKPTR(predoc);
2753     predoc[0]   = 0;
2754     preamble[0] = 0;
2755     if (!documentcmd) documentcmd = name;
2756 
2757     PUSHCURTOK;
2758     if (TeXGetGenArg( fpin[curfile], curtok, MAX_TOKEN, '[', ']', 1 ) == 1) {
2759       strcat( preamble, "[" );
2760       strcat( preamble, curtok );
2761       strcat( preamble, "]" );
2762       p = curtok;
2763       while (*p) {
2764 	/* Look for known styles */
2765 	ptr = p;
2766 	while (*ptr && *ptr != ',') ptr++;
2767 	if (!*ptr) ptr[1] = 0;   /* so the p = ptr + 1 below will exit */
2768 	*ptr = 0;
2769 	/* Skip over any path part of the specification */
2770 	p1 = p;
2771 	while (*p1) {
2772 	  if (*p1 == '/') p = p1 + 1;
2773 	  p1++;
2774 	}
2775 	TXLoadPackage( p );
2776 	p = ptr + 1;
2777       }
2778     }
2779     TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN,
2780 		   "TXdocumentstyle", (char *)0 );
2781     strcat( preamble, "{" );
2782     strcat( preamble, curtok );
2783     strcat( preamble, "}\n" );
2784     p = strrchr( curtok, '/' );
2785     if (!p) p = curtok;
2786     else p++;
2787     if (strcmp( p, "linfoem" ) == 0) {
2788 	TXStyleLatexInfo( TeXlist, fpin[curfile], fpout );
2789     }
2790     POPCURTOK;
2791 }
2792 
TXdocumentclass(TeXEntry * e)2793 void TXdocumentclass( TeXEntry *e )
2794 {
2795     static char name[] = "documentclass";
2796     TXdocumentstyle( e );
2797     documentcmd = name;
2798 }
2799 
2800 /* Code for the title etc */
2801 #define MAX_TITLE 1000
2802 #define MAX_AUTHOR 1000
2803 static char TitleString[MAX_TITLE], AuthorString[MAX_AUTHOR];
TXtitle(TeXEntry * e)2804 void TXtitle( TeXEntry *e )
2805 {
2806     if (DebugCommands)
2807 	fprintf( stdout, "Getting argument for %s\n", e->name );
2808     TeXMustGetArg( fpin[curfile], TitleString, MAX_TITLE, "TXtitle", e->name );
2809 }
2810 
TXauthor(TeXEntry * e)2811 void TXauthor( TeXEntry *e )
2812 {
2813     if (DebugCommands)
2814 	fprintf( stdout, "Getting argument for %s\n", e->name );
2815     TeXMustGetArg( fpin[curfile], AuthorString, MAX_AUTHOR,
2816 		   "TXauthor", e->name );
2817 }
TXdate(TeXEntry * e)2818 void TXdate( TeXEntry *e )
2819 {
2820     if (DebugCommands)
2821 	fprintf( stdout, "Getting argument for %s\n", e->name );
2822     PUSHCURTOK;
2823     TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN, "TXdata", e->name );
2824     POPCURTOK;
2825 }
TeXmaketitle(e)2826 void TeXmaketitle( e )
2827 TeXEntry *e;
2828 {
2829     TXmaketitle( e, TitleString, AuthorString );
2830 }
2831 
TXDef(TeXEntry * e)2832 void TXDef( TeXEntry *e )
2833 {
2834     TXAddUserDef( TeXlist, e );
2835 }
TXNewCommand(TeXEntry * e)2836 void TXNewCommand( TeXEntry *e )
2837 {
2838     TXDoNewCommand( TeXlist, e );
2839 }
2840 
TXNewLength(TeXEntry * e)2841 void TXNewLength( TeXEntry *e )
2842 {
2843     TXDoNewLength( TeXlist, e );
2844 }
2845 
TXURL(TeXEntry * e)2846 void TXURL( TeXEntry *e )
2847 {
2848     PUSHCURTOK;
2849     TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN, "TXURL", e->name );
2850     TXWriteHyperLink( fpout, curtok, curtok, 0 );
2851     POPCURTOK;
2852 }
2853 
TXAURL(TeXEntry * e)2854 void TXAURL( TeXEntry *e )
2855 {
2856     char nametok[512];
2857 
2858     PUSHCURTOK;
2859     TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN, "TXAURL", e->name );
2860     TeXMustGetArg( fpin[curfile], nametok, MAX_TOKEN, "TXAURL", e->name );
2861     TXWriteHyperLink( fpout, nametok, curtok, 0 );
2862     POPCURTOK;
2863 }
2864 
TXcite(TeXEntry * e)2865 void TXcite( TeXEntry *e )
2866 {
2867     int  urltype;
2868     char *url, *text;
2869     char *p, *pn;
2870 
2871     if (DebugCommands)
2872 	fprintf( stdout, "Getting argument for %s\n", e->name );
2873     PUSHCURTOK;
2874     TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN, "TXcite", e->name );
2875 /* Must handle the case of citations separated by commas */
2876     p = curtok;
2877     pn = 0;
2878     TeXoutstr( fpout, CitePrefix );
2879     while (p) {
2880 	pn = strchr( p, ',' );
2881 	if (pn) {
2882 	    *pn = 0;
2883 	    pn++;
2884 	}
2885 	if (RefMapLookup( "cite", p, &url, &urltype, &text )) {
2886 	    /* Found a hypertext reference */
2887 	    TXWriteHyperLink( fpout, text, url, urltype );
2888 	}
2889 	else if (BibLookup( p, &url, &text )) {
2890 	    /* code to handle a jump to the destination name returned */
2891 	    TXWriteHyperLink( fpout, text, url, urltype );
2892 	}
2893 	else {
2894 	    fprintf( fpout, "(ref %s)", p );
2895 	    fprintf( ferr, "citation to %s has no hyperlink\n", p );
2896 	}
2897 	p = pn;
2898 	if (p)
2899 	    TeXoutstr( fpout, "," );
2900     }
2901     TeXoutstr( fpout, CiteSuffix );
2902 
2903 /* \hcitea has a second argument that is used in the LaTeX version as the
2904    replacement text */
2905     if (e->nargs > 1) {
2906 	TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN, "TXhcite", e->name );
2907     }
2908     POPCURTOK;
2909 }
2910 
2911 /*
2912  * Process catcode commands
2913  * \catcode<char>=code or \catcode`\<char>=code or \catcode`<char>=code
2914  *
2915  * Note that since a typical use is
2916  *  \bgroup\catcode....   \egroup
2917  * we need to implement the full stack semantics of TeX scoping when
2918  * processing groups
2919  */
TXcatcode(TeXEntry * e)2920 void TXcatcode( TeXEntry *e )
2921 {
2922     char c, codetoken[21];
2923     int  code, nsp;
2924 
2925     if (IgnoreCatcode) return;
2926 
2927     /* Read char */
2928     c = SCTxtGetChar( fpin[curfile] );
2929     if (c == '`') {
2930 	c = SCTxtGetChar( fpin[curfile] );
2931 	if (c == '\\') {
2932 	    c = SCTxtGetChar( fpin[curfile] );
2933 	}
2934     }
2935     /* Read code */
2936     c = SCTxtGetChar( fpin[curfile] );
2937     if (c != '=') {
2938 	fprintf( stderr, "Missing = in \\catcode command, %s line %d\n",
2939 		 InFName[curfile] ? InFName[curfile]: "",
2940 		 LineNo[curfile] );
2941 	return;
2942     }
2943     SCTxtFindNextANToken( fpin[curfile], codetoken, 20, &nsp );
2944     code = atoi(codetoken);
2945 
2946     /* Now, change the relevant character to the given class */
2947     switch (code) {
2948     case 1: /* begin group */
2949 	break;
2950     case 2: /* end group */
2951 	break;
2952     case 3: /* math shift */
2953 	break;
2954     case 4: /* alignment tab */
2955 	break;
2956     case 5: /* end of line */
2957 	break;
2958     case 6: /* macro parameter character */
2959 	break;
2960     case 7: /* superscript */
2961 	break;
2962     case 8: /* subscript */
2963 	break;
2964     case 9: /* Null */
2965 	break;
2966     case 10: /* Blank space */
2967 	break;
2968     case 11: /* Letters */
2969 	break;
2970     case 12: /* Other */
2971 	break;
2972     case 13: /* active */
2973 	break;
2974     case 14: /* comment */
2975 	break;
2976     case 15: /* invalid */
2977 	break;
2978     default:
2979 	fprintf( stderr,
2980 		 "Unrecognized code %d for %c in \\catcode command, %s line %d\n",
2981 		 code, c,
2982 		 InFName[curfile] ? InFName[curfile]: "",
2983 		 LineNo[curfile] );
2984     }
2985     return;
2986 }
2987 
2988 /*
2989    This is a version of "output token" that makes a final check for a known
2990    name.  If found, the hyperlink in inserted in its place
2991  */
2992 static char activetok[MAX_TOKEN];
2993 static int lasttok = 0;
2994 static int breakchar[256];
TXinitbreaktable(void)2995 void TXinitbreaktable( void )
2996 {
2997     int i;
2998     for (i=0; i<256; i++) {
2999 	breakchar[i] = 0;
3000 	if (isalnum((char)(i))) breakchar[i] = 1;
3001     }
3002 /* Add underscore */
3003     breakchar['_'] = 1;
3004 }
3005 
TXoutactiveToken(char * token)3006 void TXoutactiveToken( char *token )
3007 {
3008     int    urltype;
3009     char   *url, *text;
3010 
3011 /*
3012  * First, tokenize the output and check each token.  Special case:
3013  * if we reach the end of the string without a "break" character, we must
3014  * delay processing until we get the break character, since the token may
3015  * be incomplete (for example, MPI_Send may be delivered to this
3016  * routine as MPI, then _, then Send.
3017  *
3018  * We must also be careful to skip over commands (TOK_START to TOK_END).
3019  */
3020     while (*token) {
3021 	/* This really needs to tokenize the string */
3022 	if (breakchar[(int)*token]) {
3023 	    /* The character is a "alphanum" */
3024 	    while (*token && breakchar[(int)*token]) {
3025 		activetok[lasttok++] = *token++;
3026 		if (lasttok >= MAX_TOKEN) {
3027 		    activetok[MAX_TOKEN-1] = 0;
3028 		    fprintf( stderr, "Token too long (%s)=n", activetok );
3029 		    return;
3030 		}
3031 	    }
3032 	    activetok[lasttok] = 0;
3033 	    /* If we ended at a alnum-like token, don't output yet. */
3034 	    if (*token == 0) break;
3035 	}
3036 	else {
3037 	    /* If there is already something in the token buffer,
3038 	       drain it first */
3039 	    if (lasttok == 0) {
3040 		/* Skip over the non-alphanum characters */
3041 		if ((unsigned char)(*token) == TOK_START &&
3042 		    lasttok < MAX_TOKEN) {
3043 		    while (*token && (unsigned char)(*token) != TOK_END) {
3044 			activetok[lasttok++] = *token++;
3045 		    }
3046 		}
3047 		else {
3048 		    while (*token && !breakchar[(int)*token] &&
3049 			   (unsigned char )(*token) != TOK_START &&
3050 			   lasttok < MAX_TOKEN) {
3051 			activetok[lasttok++] = *token++;
3052 		    }
3053 		}
3054 		if (lasttok >= MAX_TOKEN) {
3055 		    activetok[MAX_TOKEN-1] = 0;
3056 		    fprintf( stderr, "Token too long (%s)=n", activetok );
3057 		    return;
3058 		}
3059 		activetok[lasttok] = 0;
3060 	    }
3061 	}
3062 	if (DoActiveTokens &&
3063 	    RefMapLookup( "man", activetok, &url, &urltype, &text )) {
3064 	    TXWriteHyperLink( fpout, text, url, urltype );
3065 	}
3066 	else {
3067 	    WriteString( fpout, activetok );
3068 	}
3069 	/* We've flushed the token */
3070 	lasttok = 0;
3071     }
3072 }
3073 
3074 /*
3075  * Process a Postscript or Encapsulated Postscript image.
3076  */
3077 static char imagefile[256];
3078 static char imgoutfile[256];
3079 static int AlwaysMakeNewGif = 0;
3080 static int DebugImages = 1;
TXepsfbox(TeXEntry * e)3081 void TXepsfbox( TeXEntry *e )
3082 {
3083     char *p;
3084     FILE *fp;
3085     char fname[256];
3086     int  found_gif;
3087 
3088     if (DebugCommands)
3089 	fprintf( stdout, "Getting argument for %s\n", e->name );
3090     PUSHCURTOK;
3091     TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN, "TXepsfbox", e->name );
3092 
3093 /* Save the filename */
3094     strncpy( imagefile, curtok, sizeof(imagefile) );
3095 
3096 /* Modify curtok by changing .ps or .eps to .gif */
3097     p = curtok + strlen(curtok) - 1;
3098     while (p > curtok && *p != '.') p--;
3099     strcpy( p+1, "gif" );
3100 
3101 /* out file is the file without the directory */
3102     p = curtok;
3103     strncpy( imgoutfile, curtok, sizeof(imgoutfile) );
3104     while (*p) {
3105 	if (p[0] == '/') strncpy( imgoutfile, p + 1, sizeof(imgoutfile) );
3106 	p++;
3107     }
3108 /* Here are the file names:
3109    imagefile - original source file name
3110    curtok    - the same as imagefile, but with .gif extension
3111    imgoutfile - the same as curtok, but without the directories
3112    fname      - outputdirectory/imgoutfile
3113 
3114    i.e., if imagefile = /home/foo/bar.eps, then
3115             curtok    = /home/foo/bar.gif
3116             imgoutfile = bar.gif
3117 
3118    Here are the rules for deciding whether to use an existing filename
3119    if (!AlwaysMakeNewGif)
3120        if (destdir/imgoutfile exists and is newer than imagefile)
3121             use destdir/imgoutfile (nothing to do)
3122        else if (curtok exists and is newer than imagefile)
3123             copy curtok to destdir
3124    if (no file yet, then try to generate one)
3125  */
3126 
3127     /* Create the final output file name */
3128     if (splitlevel >=0)
3129 	sprintf( fname, "%s/%s", splitdir, imgoutfile );
3130     else
3131 	sprintf( fname, "./%s", imgoutfile );
3132     found_gif = 0;
3133     if (!AlwaysMakeNewGif) {
3134 	if (DebugImages) {
3135 	    printf( "Checking for file %s (%s)\n",
3136    	      fname, SYiFileExists( fname, 'r' ) ? "Exists" : "Missing" );
3137 	    printf( "%s is %s than %s\n", fname,
3138 		    SYFileNewer( fname, imagefile ) ? "newer" : "older",
3139 		    imagefile );
3140 	}
3141 	if (SYiFileExists( fname, 'r' ) && SYFileNewer( fname, imagefile )) {
3142 	    /* The file we need is already where we need it */
3143 	    if (DebugImages) printf( "Found gif file %s\n", imagefile );
3144 	    found_gif = 1;
3145 	}
3146 	else {
3147 	    if (DebugImages) {
3148 		printf( "Check for file %s (%s)\n",
3149    	      curtok, SYiFileExists( curtok, 'r' ) ? "Exists" : "Missing" );
3150 		printf( "%s is %s than %s\n", curtok,
3151 		    SYFileNewer( curtok, imagefile ) ? "newer" : "older",
3152 			imagefile );
3153 	    }
3154 	    if (SYiFileExists( curtok, 'r' ) &&
3155 		 SYFileNewer( curtok, imagefile )) {
3156 		char tmpbuf[1024];
3157 		if (DebugImages) printf( "Using %s for image\n", curtok );
3158 		/* GIF version of file is in same directory as source file */
3159 		sprintf( tmpbuf, "/bin/cp -f %s %s", curtok, fname );
3160 		system( tmpbuf );
3161 		found_gif = 1;
3162 	    }
3163 	}
3164     }
3165     if (found_gif) {
3166 	if (DebugImages) printf( "Using %s\n", imgoutfile );
3167 	TXimage( e, imgoutfile );
3168     }
3169     else {
3170 #ifdef OLDEPSF
3171 /* See if the file exists */
3172     if (splitlevel >= 0) {
3173 	/* first, is the file in the destination directory ? */
3174 	sprintf( fname, "%s/%s", splitdir, imgoutfile );
3175 	fp = fopen( fname, "r" );
3176 	if (!fp) {
3177 	    /* Is it in the CURRENT directory */
3178 	    fp = fopen( imgoutfile, "r" );
3179 	    if (fp) {
3180 		sprintf( fname, "/bin/cp -f %s %s", imgoutfile, splitdir );
3181 		system( fname );
3182 	    }
3183 	}
3184     }
3185     else {
3186 	fp = fopen( imgoutfile, "r" );
3187     }
3188     if (fp) {
3189 	fclose(fp);
3190 	TXimage( e, imgoutfile );
3191     }
3192     else {
3193 #endif
3194 	/* Try to generate it on the fly */
3195 #ifndef __MSDOS__
3196 	char pgm[1024];
3197 
3198 	if (DebugImages) printf( "Attempting to create gif image file\n" );
3199 	fp = fopen( imagefile, "r" );
3200 	if (fp) {
3201 	    fclose( fp );
3202 	    sprintf( pgm, "%spstogif %s %s figure > /dev/null",
3203 		     PSPATH, imagefile, imgoutfile );
3204 	    system( pgm );
3205 	    /* The output of this is imgoutfile, not curtok */
3206 	    /* (was fopen( curtok, "r" )) */
3207 	    fp = fopen( imgoutfile, "r" );
3208 	    if (fp) {
3209 		fclose( fp );
3210 		if (splitlevel >= 0) {
3211 		    sprintf( pgm, "/bin/mv %s %s", imgoutfile, splitdir );
3212 		    system( pgm );
3213 		}
3214 		TXimage( e, imgoutfile );
3215 		POPCURTOK;
3216 		return;
3217 	    }
3218 	}
3219 #endif
3220 	fprintf( stderr, "No file %s available for image\n", curtok );
3221 	fprintf( stderr, "(Could not open %s)\n", imagefile );
3222 	TXbgroup( e );
3223 	TXbf( e );
3224 	TeXoutstr( fpout, "Image file " );
3225 	TeXoutstr( fpout, imgoutfile );
3226 	TeXoutstr( fpout, " to be provided..." );
3227 	TXegroup( e );
3228 	TXWriteStartNewLine( fpout );
3229     }
3230     POPCURTOK;
3231 }
3232 
3233 /* psfig is like epsfbox, but with a different syntax */
3234 /* It also supports things that epsf doesn't, like angle commands */
3235 /* What we need to do is this:
3236    Find file= for figure= for the filename
3237    Find any angle= , bbllx=, bblly=, bburx=, bbury= commands
3238    (there are still others but these will do)
3239    If angle is not 0, then create a new file with
3240    angle rotate
3241    dx dy translate
3242    as the first commands, where
3243    dx and dy are choosen to make the picture fit in positive coords.
3244    Note that the new bounding box, after rotation, has
3245    corners with coords (x cos angle - y sin angle, y sin angle + y cos angle)
3246    (corner at (x,y) before rotation.
3247    Take the min x of these, and set dx = -(min x)
3248    Set dx as -(min y)
3249    (adjust if the bounding box doesn't start at (0,0).
3250  */
3251 
3252 void TXpsfig( TeXEntry *e )
3253 {
3254     char *p, *fname;
3255 
3256     if (DebugCommands)
3257 	fprintf( stdout, "Getting argument for %s\n", e->name );
3258     PUSHCURTOK;
3259     TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN, "TXpsfig", e->name );
3260 
3261     p = curtok;
3262     /* Look for file= and figure= */
3263     /* Should look for xxxx= and then parse the xxxx */
3264     while (p && strncmp( p, "figure=", 7 ) != 0 &&
3265 	   strncmp( p, "file=", 5 ) != 0) {
3266 	p = strchr( p, ',' );
3267 	if (p) p++;
3268     }
3269     if (p) {
3270 	/* get to the equal */
3271 	p = strchr( p, '=' ) + 1;
3272 	fname = p;
3273 	p = strchr( p, ',' );
3274 	if (p) *p = 0;
3275 	SCPushToken( "}" );
3276 	SCPushToken( fname );
3277 	SCPushToken( "\\epsfbox{" );
3278     }
3279     else {
3280 	fprintf( ferr, "Could not find file name for \\psfig command\n" );
3281     }
3282     POPCURTOK;
3283 }
3284 
3285 /* FIXME: Add
3286 
3287    includegraphics[width=dim]{filename}
3288 
3289    File name may be missing the extension, in which case assume pdf
3290  */
3291 
3292 void TXincludegraphics( TeXEntry *e )
3293 {
3294     int  rc;
3295 
3296     /* See if there is an option argument (which we'll ignore for now) */
3297     PUSHCURTOK;
3298     if (TeXGetGenArg( fpin[curfile], curtok, MAX_TOKEN, '[', ']', 1 ) == 1) {
3299 	/* Use this to process any arguments that we might need with
3300 	   the picture */
3301 #if 0
3302 	static char name[] = "includegraphics";
3303 	char *p, *ptr, *p1;
3304 	p = curtok;
3305 	while (*p) {
3306 	    /* Look for known commands */
3307 	    ptr = p;
3308 	    while (*ptr && *ptr != ',') ptr++;
3309 	    if (!*ptr) ptr[1] = 0;   /* so the p = ptr + 1 below will exit */
3310 	    *ptr = 0;
3311 	    /* Skip over any path part of the specification */
3312 	    p1 = p;
3313 	    while (*p1) {
3314 		if (*p1 == '/') p = p1 + 1;
3315 		p1++;
3316 	    }
3317 	    p = ptr + 1;
3318 	}
3319 #endif
3320     }
3321     /* Get the file name */
3322     TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN,
3323 		   "TXincludegraphics", (char *)0 );
3324     /* Try to find the file */
3325     rc = SYFindFileWithExtension( curtok, "gif:eps:pnm:ps:pdf" );
3326     if (rc == 1) {
3327 	/* We may need to convert the file to one that can be displayed */
3328 	rc = TXConvertFigureToGIF( curtok );
3329 	if (rc == 1) {
3330 	    TXimage( 0, curtok );
3331 	}
3332 	else {
3333 	    fprintf( stderr, "Unable to convert %s to .gif\n", curtok );
3334 	}
3335     }
3336     else {
3337 	fprintf( stderr, "Cannot find graphics file %s\n", curtok );
3338     }
3339 
3340     POPCURTOK;
3341 }
3342 
3343 /* Process an animation entry.  args are {mpg}{gif}{text} */
3344 
3345 static char mpgtoken[256], giftoken[256];
3346 
3347 void TXanimation( TeXEntry *e )
3348 {
3349     TeXMustGetArg( fpin[curfile], mpgtoken, 256, "TXanimation", e->name );
3350     TeXMustGetArg( fpin[curfile], giftoken, 256, "TXanimation", e->name );
3351     PUSHCURTOK;
3352     TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN, "TXanimation", e->name );
3353     TXmovie( e, mpgtoken, giftoken, curtok );
3354     POPCURTOK;
3355     TXWriteStartNewLine( fpout );
3356 }
3357 
3358 /* Initialization code */
3359 void TXInsertName( SRList *list, const char *name, void (*action)( TeXEntry *),
3360 		   int nargs, void *ctx )
3361 {
3362     LINK *l;
3363     int  d;
3364     TeXEntry *new;
3365 
3366     l           = SRInsert( list, name, (char *)0, &d );
3367     new         = NEW(TeXEntry);    CHKPTR(new);
3368     l->priv     = (void *)new;
3369     new->action = action;
3370     new->ctx    = ctx;
3371     new->nargs  = nargs;
3372     new->name   = l->topicname;
3373 }
3374 
3375 /*
3376    Initialize the TeX commands. This adds ONLY the ones that are
3377    not set by the basedefs.txt file.
3378  */
3379 void TXInit( FILE *fin, FILE *fout )
3380 {
3381     if (!TeXlist)
3382 	TeXlist = SRCreate();
3383 
3384 /* Insert the known LaTeXinfo commands into the table for processing */
3385 
3386     TXInsertName( TeXlist, "def", TXDef, 0, (void *)0 );
3387     /* gdef should eventually be different */
3388     TXInsertName( TeXlist, "gdef", TXDef, 0, (void *)0 );
3389     TXInsertName( TeXlist, "let", TXlet, 0, (void *)0 );
3390     TXInsertName( TeXlist, "newcommand", TXNewCommand, 0, (void *)0 );
3391     TXInsertName( TeXlist, "newlength", TXNewLength, 0, (void *)0 );
3392     TXInsertName( TeXlist, "renewcommand", TXNewCommand, 0, (void *)0 );
3393     TXInsertName( TeXlist, "newenvironment", TXDoNewenvironment, 0, (void *)0 );
3394     TXInsertName( TeXlist, "renewenvironment", TXDoNewenvironment, 0, (void *)0 );
3395     TXInsertName( TeXlist, "newtheorem", TXDoNewtheorem, 0, (void *)0 );
3396     TXInsertName( TeXlist, "renewtheorem", TXDoNewtheorem, 0, (void *)0 );
3397 
3398     TXInsertName( TeXlist, "obeylines", TXDoObeylines, 0, (void *)0 );
3399 
3400     /* LaTeX conditionals */
3401     TXInsertName( TeXlist, "newif", TXNewif, 0, (void *)0 );
3402     TXInsertName( TeXlist, "else", TXElse, 0, (void *)0 );
3403     TXInsertName( TeXlist, "fi", TXFi, 0, (void *)0 );
3404 
3405     /* Font controls.  Old forms first */
3406     TXInsertName( TeXlist, "rm", TXrm, 0, (void *)0 );
3407     TXInsertName( TeXlist, "bf", TXbf, 0, (void *)0 );
3408     TXInsertName( TeXlist, "em", TXem, 0, (void *)0 );
3409     TXInsertName( TeXlist, "it", TXem, 0, (void *)0 );
3410     TXInsertName( TeXlist, "tt", TXtt, 0, (void *)0 );
3411     TXInsertName( TeXlist, "sf", TXsf, 0, (void *)0 );
3412 
3413     TXInsertName( TeXlist, "label",  TXlabel, 1, (void *)0 );
3414     TXInsertName( TeXlist, "ref",  TXref, 1, (void *)0 );
3415 
3416     TXInsertName( TeXlist, "pageref",  TXref, 1, (void *)0 );
3417     TXInsertName( TeXlist, "documentstyle", TXdocumentstyle, 1, (void *)0 );
3418     TXInsertName( TeXlist, "documentclass", TXdocumentclass, 1, (void *)0 );
3419     TXInsertName( TeXlist, "usepackage", TXusepackage, 1, (void *)0 );
3420     TXInsertName( TeXlist, "title",  TXtitle, 1, (void *)0 );
3421     TXInsertName( TeXlist, "author",  TXauthor, 1, (void *)0 );
3422     TXInsertName( TeXlist, "date",  TXdate, 1, (void *)0 );
3423     TXInsertName( TeXlist, "bullet", TXoutbullet, 0, (void *)0 );
3424 
3425     TXInsertName( TeXlist, "index", TXindex, 1, (void *)0 );
3426     TXInsertName( TeXlist, "makeindex", TXmakeindex, 0, (void *)0 );
3427 
3428     /* These should eventually process the TeX stack */
3429     TXInsertName( TeXlist, "bgroup",     TXbgroup, 0, (void *)0 );
3430     TXInsertName( TeXlist, "egroup",     TXegroup, 0, (void *)0 );
3431 
3432     TXInsertName( TeXlist, "[",     TXmath, 0, (void *)0 );
3433     TXInsertName( TeXlist, "]",     TXmathend, 0, (void *)0 );
3434     if (LatexUnknownEnvs || LatexMath) {
3435 	/* Need to change this... */
3436 	TXInsertName( TeXlist, "(",     TXmathmode, 0, (void *)0 );
3437 	TXInsertName( TeXlist, ")",     TXnop, 0, (void *)0 );
3438 	TXInsertName( TeXlist, "[",     TXmathmode, 0, (void *)0 );
3439 	/* "]" is handled by TXmathmode... */
3440 	TXInsertName( TeXlist, "]",     TXnop, 0, (void *)0 );
3441     }
3442     else {
3443 	TXInsertName( TeXlist, "(",     TXinlinemath, 0, (void *)0 );
3444 	TXInsertName( TeXlist, ")",     TXinlinemathend, 0, (void *)0 );
3445 	TXInsertName( TeXlist, "[",     TXmath, 0, (void *)0 );
3446 	TXInsertName( TeXlist, "]",     TXmathend, 0, (void *)0 );
3447     }
3448     TXInsertName( TeXlist, "cite",  TXcite, 0, (void *)0 );
3449 /* hcite is like cite, but it doesn't show up in LaTeX slides done with
3450    "header" */
3451     TXInsertName( TeXlist, "hcite",  TXcite, 1, (void *)0 );
3452     TXInsertName( TeXlist, "hcitea",  TXcite, 2, (void *)0 );
3453     TXInsertName( TeXlist, "maketitle", TeXmaketitle, 0, (void *)0 );
3454     TXInsertName( TeXlist, "include", TXinclude, 1, (void *)0 );
3455     TXInsertName( TeXlist, "input", TXinclude, 1, (void *)0 );
3456     TXInsertName( TeXlist, "IfFileExists", TXIfFileExists, 3, (void *)0 );
3457     TXInsertName( TeXlist, "char", TXchar, 0, (void *)0 );
3458     TXInsertName( TeXlist, "bibitem", TXbibitem, 1, (void *)0 );
3459 
3460 /* These two are specific to latexinfo, but I'm using these in other
3461    situations */
3462     TXInsertName( TeXlist, "code", TXcode, 1, (void *)0 );
3463     TXInsertName( TeXlist, "verb", TXverb, 0, (void *)0 );
3464     TXInsertName( TeXlist, "file", TXfile, 1, (void *)0 );
3465 
3466 /* Question: should part be 0, chapter 1, etc? */
3467     TXInsertName( TeXlist, "part", TXsection, 1, (void *)0 );
3468     TXInsertName( TeXlist, "chapter", TXsection, 1, (void *)0 );
3469     TXInsertName( TeXlist, "section", TXsection, 1, (void *)1 );
3470     TXInsertName( TeXlist, "subsection", TXsection, 1, (void *)2 );
3471     TXInsertName( TeXlist, "subsubsection", TXsection, 1, (void *)3 );
3472     TXInsertName( TeXlist, "subsubsubsection", TXsection, 1, (void *)4 );
3473 
3474 /* Paragraph and subparagraph are sort of like sections, but they
3475    should not generate contents or links */
3476     TXInsertName( TeXlist, "paragraph", TXparagraph, 1, (void *)0 );
3477     TXInsertName( TeXlist, "subparagraph", TXparagraph, 1, (void *)1 );
3478 
3479 /*
3480    TXInsertName( TeXlist, "paragraph", TXsection, 1, (void *)4 );
3481    TXInsertName( TeXlist, "subparagraph", TXsection, 1, (void *)5 );
3482    */
3483 
3484 /* TeX commands to change what is allowed in a command name */
3485     TXInsertName( TeXlist, "makeatletter", TXatletter, 0, (void *)0 );
3486     TXInsertName( TeXlist, "makeatother",  TXatother,  0, (void *)0 );
3487 
3488     TXInsertName( TeXlist, "advance", TXadvance, 0, (void *)0 );
3489 
3490 /* TeX commands that take numbers that should be ignored */
3491     TXInsertName( TeXlist, "penalty", TXnumber, 0, (void *)0 );
3492     TXInsertName( TeXlist, "hangafter", TXcounter, 0, (void *)0 );
3493 
3494     TXInsertName( TeXlist, "begin", TXbegin, 1, (void *)0 );
3495     TXInsertName( TeXlist, "end", TXend, 1, (void *)0 );
3496     TXInsertName( TeXlist, "item", TXitem, 1, (void *)0 );
3497     /* TXInsertName( TeXlist, "bitmap", TXbitmap, 1, (void *)0 ); */
3498     TXInsertName( TeXlist, "\\", TXdoublebw, 0, (void *)0 );
3499     TXInsertName( TeXlist, "newline", TXnewline, 0, (void *)0 );
3500 /* Note that a \linebreak can have an optional argument (the desirability
3501    of a line break) */
3502     TXInsertName( TeXlist, "linebreak", TXnewline, 0, (void *)0 );
3503     TXInsertName( TeXlist, "bw", TXbw, 0, (void *)0 );
3504     TXInsertName( TeXlist, "back", TXbw, 0, (void *)0 );
3505 
3506 /* \- is usually a discretionary hyphen */
3507 /* setlength needs to eat the first argument without evaluation;
3508    we don't do that yet. */
3509     TXInsertName( TeXlist, "setlength", TXnop, 2, (void *)0 );
3510     TXInsertName( TeXlist, "bibliography", TXDoBibliography, 1, (void *)0 );
3511     TXInsertName( TeXlist, "cleardoublepage", TXnop, 0, (void *)0 );
3512     TXInsertName( TeXlist, "today", TXtoday, 0, (void *)0 );
3513     TXInsertName( TeXlist, "vspace", TXnopStar, 1, (void *)0 );
3514     TXInsertName( TeXlist, "hspace", TXnopStar, 1, (void *)0 );
3515 
3516 /* Be prepared for center environment */
3517     /*     TXInsertName( TeXlist, "centerline", TeXCenterline, 1, (void *)0 );*/
3518 /* Just copy the arguments */
3519     TXInsertName( TeXlist, "caption", TXcaption, 1, (void *)0 );
3520 
3521 /* Boxes need to look for "to size" arguments ????????? */
3522     TXInsertName( TeXlist, "hbox", TXbox, 0, (void *)0 );
3523     TXInsertName( TeXlist, "vbox", TXbox, 0, (void *)0 );
3524 
3525     TXInsertName( TeXlist, "{", TXbbrace, 0, (void *)0 );
3526     TXInsertName( TeXlist, "}", TXebrace, 0, (void *)0 );
3527     if (!DestIsHtml)
3528 	TXInsertName( TeXlist, "par", TXname, 0, TXCopy("par") );
3529     else
3530 	TXInsertName( TeXlist, "par", TXnewline, 0, (void *)0 );
3531 
3532     if (DestIsHtml) {
3533 	char buf[10];
3534 	buf[0] = TOK_START;
3535 	strcpy( buf+1, "&amp;" );
3536 	buf[6] = TOK_END;
3537 	buf[7] = 0;
3538 	TXInsertName( TeXlist, "&", TXname, 0, TXCopy(buf) );
3539     }
3540     else
3541 	TXInsertName( TeXlist, "&", TXname, 0, TXCopy("&") );
3542 
3543     {
3544 	char buf[4];
3545 	buf[0] = TOK_START;
3546 	buf[1] = '%';
3547 	buf[2] = TOK_END;
3548 	buf[3] = 0;
3549 	TXInsertName( TeXlist, "%", TXname, 0, TXCopy(buf) );
3550     }
3551 /* Process \<space> and \<newline> as a single space */
3552     TXInsertName( TeXlist, " ", TXname, 0, TXCopy(" ") );
3553     TXInsertName( TeXlist, "\n", TXname, 0, TXCopy(" ") );
3554 
3555     TXInsertName( TeXlist, "times", TXname, 0, TXCopy(" x ") );
3556 
3557     TXInsertName( TeXlist, "fileinclude", TXfileinclude, 1, (void *)0 );
3558 
3559 /* Slide commands */
3560     TXInsertName( TeXlist, "vt",  TXvt,  0, (void *)1 );
3561     TXInsertName( TeXlist, "vtitle",  TXvtt,  1, (void *)0 );
3562     TXInsertName( TeXlist, "vtt", TXvtt, 1, (void *)1 );
3563     TXInsertName( TeXlist, "vtts", TXvtt, 1, (void *)2 );
3564     TXInsertName( TeXlist, "vttss", TXvtt, 1, (void *)3 );
3565     TXInsertName( TeXlist, "vttsss", TXvtt, 1, (void *)4 );
3566     TXInsertName( TeXlist, "vttssss", TXvtt, 1, (void *)5 );
3567     TXInsertName( TeXlist, "vttsssss", TXvtt, 1, (void *)6 );
3568     TXInsertName( TeXlist, "href", TXhref, 1, (void *)0 );
3569     TXInsertName( TeXlist, "hrefa", TXhref, 2, (void *)0 );
3570     TXInsertName( TeXlist, "details", TXdetails, 1, (void *)0 );
3571 
3572 /* Stuff generated by bibligraphy files */
3573     TXInsertName( TeXlist, "bibitem",  TXbibitem, 1, (void *)0 );
3574 
3575 /* Hypertext */
3576     TXInsertName( TeXlist, "URL",  TXURL,  1, (void *)0 );
3577     TXInsertName( TeXlist, "AURL", TXAURL, 2, (void *)0 );
3578 
3579 /* TeX accents */
3580     InitAccents();
3581 
3582 /* HTML Tables */
3583     if (HandleAlign)
3584 	InitTabular();
3585 
3586     /* Initialize the name of the error output file */
3587     GetBaseName( latex_errname );
3588     strcat( latex_errname, ".ler" );
3589 }
3590 
3591 void TeXProcessCommand( char *token, FILE *fin, FILE *fout )
3592 {
3593     LINK     *l;
3594     TeXEntry *e;
3595     int      i;
3596 
3597     if (DebugCommands)
3598 	fprintf( stdout, "Handling Command %s\n", token );
3599     l  = SRLookup( TeXlist, token, token, &i );
3600     if (!l) {
3601 	if (!AmSkipping) /*  && InDocument)  */
3602 	    fprintf( ferr, "Unknown LaTeX command %s, %s line %d\n",
3603 		     token,
3604 		     InFName[curfile] ? InFName[curfile]: "",
3605 		     LineNo[curfile] );
3606 	/* If next token is {, eat {} until no more {} pairs */
3607     }
3608     else {
3609 	e = (TeXEntry *)( l->priv );
3610 	(*e->action)( e );
3611     }
3612 }
3613 
3614 /* Write out the contents page
3615  * If the header hasn't been written, we need to add it.
3616  */
3617 static int ContentsDepth = 100;
3618 static int WrittenContents = 0;
3619 static char ContentsLocation[256];
3620 
3621 void TeXNoContents( void )
3622 {
3623     WrittenContents = 1;
3624 }
3625 char *ContentsLoc( void )
3626 {
3627     if (!WrittenContents) return 0;
3628     return ContentsLocation;
3629 }
3630 void TeXWriteContents( FILE *fout )
3631 {
3632     if (WrittenContents) return;
3633 /* WRtoauxfile( 0, outfile, 0, "Contents" ); */
3634 
3635     WriteHeadPage( fpout );
3636     WriteFileTitle( fpout, "Contents" );
3637     WriteBeginPage( fpout );
3638 
3639     WriteSectionAnchor( fpout, "Contents", "Node", 0, 0 );
3640     if (outfile)
3641 	sprintf( ContentsLocation, "%s#Node0", outfile );
3642     else
3643 	strcpy(  ContentsLocation, "Node0" );
3644     WriteSectionHeader( fpout, "Contents", "Node", 0, (char *)0, 0 );
3645     if (NumChildren( (void *)0 ) > 0 ) {
3646 	WriteChildren( fpout, (void *)0, ContentsDepth );
3647     }
3648     CurSeqnum++;
3649 /* At this point, we'd like to generate the title and authors. */
3650     WriteTextHeader( fpout );
3651 /*WRfromauxfile( fout, 0 );*/
3652 
3653 /* Switch to writing a new aux file */
3654     OpenWRAuxFile();
3655     WrittenContents = 1;
3656 }
3657 
3658 /*
3659    Here is the beginning of a sketch of a routine to process a LaTeXInfo file
3660  */
3661 void ProcessLatexFile( int argc, char **argv, FILE *fin, FILE *fout )
3662 {
3663     int  nsp, ch;
3664 
3665     fpin[0] = fin;
3666     fpout   = fout;
3667 
3668     curtok = tokbuf = (char *)MALLOC( MAX_CURTOKS * MAX_TOKEN );
3669     toknum = 0;
3670     CHKPTR(curtok);
3671 
3672     TXInit( fin, fout );
3673     ferr = fopen( "latex.err", "w" );
3674 
3675 /* Define the symbols to process */
3676     TXinitbreaktable();
3677 
3678 /* Try to read info file */
3679     topicctx = SRCreate();
3680     RdAuxFile( topicctx );
3681 /*
3682    SCSetCommentChar( '%' );
3683    */
3684 /* We shouldn't do this until we've seen the maketitle or the first section */
3685 /* Note that this is executed outside of the "begin{document}", so
3686    we have turned off all output here */
3687     PUSHCURTOK;
3688     while (1) {
3689 	ch = TeXReadToken( curtok, &nsp );
3690 	if (ch == EOF) {
3691 	    if (curfile > 0) {
3692 		if (DebugCommands)
3693 		    fprintf( stdout, "EOF in ProcessLatexFile\n" );
3694 		TXPopFile();
3695 	    }
3696 	    else break;
3697 	}
3698 	if (ch == CommentChar) {
3699 	    SCTxtDiscardToEndOfLine( fpin[curfile] );
3700 	    LineNo[curfile]++;
3701 	}
3702 	else if (ch == CommandChar) {
3703 	    /* TeXoutsp( fout, nsp ); */
3704 	    TeXProcessCommand( curtok+1, fpin[curfile], fout );
3705 	}
3706 	else if (ch == LbraceChar) {
3707 	    if (BraceCount >= MAX_BLOC) {
3708 		TeXAbort( "", "Braces too deep" );
3709 	    }
3710 	    strncpy( bloc[BraceCount].filename, InFName[curfile],
3711 		     MAX_BLOC_FNAME );
3712 	    bloc[BraceCount++].lineno = LineNo[curfile];
3713 	    /* fprintf( fout, "**+%d[%d]**",
3714 	       LineNo[curfile], BraceCount ); */
3715 #ifdef FOO
3716 	    push stack (font, spacing parameters);
3717 #endif
3718 	}
3719 	else if (ch == RbraceChar) {
3720 	    BraceCount--;
3721 	    /* fprintf( fout, "**-%d[%d]**",
3722 	       LineNo[curfile], BraceCount ); */
3723 #ifdef FOO
3724 	    pop stack;
3725 #endif
3726 	}
3727 	else if (ch == AlignChar && HandleAlign) {
3728 	    TeXPutAlign();
3729 	}
3730 	else if (ch == ActiveChar) {
3731 	    TXActiveCharDo(curtok);
3732 	}
3733 	else {
3734 	    /*
3735 	      for (i=0; i<nsp; i++) fputs( " ", fout );
3736 	      if (ch == '\n' && lSp >= 0)
3737 	      (*lstack[lSp].newline)( fout );
3738 	      else
3739 	      fputs( curtok, fout );
3740 	      */
3741 	}
3742     }
3743     POPCURTOK;
3744     WriteEndofTopic( fout );
3745     fclose( ferr );
3746     if (0)
3747 	PrintAllContents(stdout);
3748     if (BraceCount != 0) {
3749 	fprintf( stderr, "Brace count = %d\n", BraceCount );
3750 	if (BraceCount > 0) {
3751 	    int ii;
3752 	    for (ii=0; ii<BraceCount; ii++)
3753 		fprintf( stderr, "Unmatched brace near line %d in file %s\n",
3754 			 bloc[ii].lineno, bloc[ii].filename );
3755 	}
3756     }
3757 
3758     FREE( tokbuf );
3759 }
3760 
3761 /* Manage the citation characters */
3762 void TXSetCitePrefix( char *s )
3763 {
3764     if (CitePrefix) FREE( CitePrefix );
3765     CitePrefix = STRDUP( s );
3766     CHKPTR(s);
3767 }
3768 
3769 void TXSetCiteSuffix( char *s )
3770 {
3771     if (CiteSuffix) FREE( CiteSuffix );
3772     CiteSuffix = STRDUP( s );
3773     CHKPTR(s);
3774 }
3775 
3776 /*
3777  * Print strings that may contain TOK_START/TOK_END sequences
3778  */
3779 
3780 void TXPrintToken( FILE *fp, const char *str )
3781 {
3782     char c;
3783     while (*str) {
3784 	c = *str++;
3785 	if ((unsigned char)c == TOK_START) {
3786 	    fputs( "TOK_START", fp );
3787 	}
3788 	else if ((unsigned char)c == TOK_END) {
3789 	    fputs( "TOK_END", fp );
3790 	}
3791 	else if (isgraph(c) || c == ' ') {
3792 	    fputc( c, fp );
3793 	}
3794 	else {
3795 	    fputc( '^', fp );
3796 	    /* Make 7 bit */
3797 	    c = c & 0x7f;
3798 	    /* make at least space */
3799 	    if (c < 0x20) c |= 0x40;
3800 	    /* Delete is at the end of the range */
3801 	    if (c == 127) c = '?';
3802 	    fputc( c, fp );
3803 	}
3804     }
3805 }
3806 
3807 /*
3808    Notes on handling \" etc.
3809    These are tex commands that modify the next character.  They should
3810    be thought of a commands that look at the next argument, which, since
3811    it does not start with a {, is a single letter.  The mapping to HTML is
3812    (see http://www.hut.fi/~jkorpela/HTML3.2/latin1.html)
3813    \"A &Auml;    &#196;
3814    \`A &Agrave;  &#192;
3815    \`E &Egrave;  &#200;
3816    \`I &Igrave;  &#204;
3817    \"O &Ouml;    &#214;
3818    \`O &Ograve;  &#210;
3819    \"U &Uuml;    &#220;
3820    \"a &auml;    &#228;
3821    \`a &agrave;  &#224;
3822    \`e &egrave;  &#232;
3823    \`i &igrave;  &#236;
3824    \"o &ouml;    &#246;
3825    \`o &ograve;  &#242;
3826    \"u &uuml;    &#246;
3827    \`u &ugrave;  &#249;
3828 */
3829 #ifdef FOO
3830 // From TeXSkipEnv.  I believe that this code is dead and can
3831 // never be executed
3832 
3833 	    if (InVerbatim) {
3834 		if (strcmp( curtok, "end" ) == 0) {
3835 		    if (DebugCommands)
3836 			fprintf( stdout, "Getting argument for end{}\n" );
3837 		    TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN,
3838 				   "TXSkipEnv", e->name );
3839 		    /* Check for a user-defined environment type */
3840 		    if (LookupEnv( curtok, &btext, &etext, &nargs)) {
3841 			if (etext) {
3842 			    if (DebugCommands)
3843 				fprintf( stdout, "Pushing back |%s|\n", etext );
3844 			    SCPushToken( etext );
3845 			}
3846 		    }
3847 		    else if (strcmp( curtok, name ) != 0) {
3848 			if (flag)
3849 			    fprintf( ferr,
3850 		  "%s does not match %s (started at line %d), at %s line %d\n",
3851 		  curtok, name, line_num,
3852  	          InFName[curfile] ? InFName[curfile]: "", LineNo[curfile] );
3853 		    }
3854 		    else {
3855 			break;
3856 		    }
3857 		}
3858 		else {
3859 		    if (DestIsHtml)
3860 			TeXoutstr( fout, "\\" );
3861 		    else
3862 			TeXoutstr( fout, "\\\\" );
3863 		    TeXoutstr( fout, curtok );
3864 		}
3865 	    }
3866 	    else {
3867 	    }
3868 #endif
3869 
3870 /* Eventually, we may prefer to use PNM of PNG files */
3871 static int debugF2GIF = 0;
3872 
3873 /* Convert fname to GIF.  Replace fname with the filename to be used to
3874  include the file in the generated output */
3875 int TXConvertFigureToGIF( char *fname )
3876 {
3877     char *p;
3878     char pgm[2048], fname2[2048];
3879     int  rc = 0;
3880 
3881     /* Determine the type of the file from the known types (if this
3882        ever gets long, we could use an array of types and methods to convert
3883        to GIF */
3884 
3885     if (debugF2GIF)
3886 	fprintf( stderr, "Converting file %s\n", fname );
3887 
3888     /* Create the result file name.  The result name is created from the
3889        basename of the result, with the target directory */
3890     p = fname + strlen(fname) - 1;
3891     /* find the dirctory separator, if any */
3892     while (p != fname && *p != DirSep) p--;
3893     if (*p == DirSep) p++;
3894     if (!splitdir || *splitdir == 0) {
3895 	snprintf( fname2, sizeof(fname2), "%s", p );
3896     }
3897     else {
3898 	snprintf( fname2, sizeof(fname2), "%s%c%s", splitdir, DirSep, p );
3899     }
3900     p = fname2 + strlen(fname2) - 1;
3901     while (p != fname2 && *p != '.') p--;
3902     *p = 0;
3903     strncpy( p, ".gif", 10 );
3904 
3905     if (strstr( fname, ".gif" )) {
3906 	if (debugF2GIF)
3907 	    fprintf( stderr, "Do we need to move %s to %s\n", fname, fname2 );
3908 	/* Copy the file into the proper directory if it isn't there */
3909 	if (strcmp( fname, fname2 ) != 0) {
3910 	    snprintf( pgm, sizeof(pgm), "/bin/cp -f %s %s", fname, fname2 );
3911 	    rc = system( pgm );
3912 	    if (rc) {
3913 		fprintf( stderr, "Error code %d from %s\n", rc, pgm );
3914 		return 0;
3915 	    }
3916 	}
3917 	p = fname2 + strlen(fname2) - 1;
3918 	while (p != fname2 && *p != DirSep) p--;
3919 	if (*p == DirSep) p++;
3920 	strcpy( fname, p );
3921 	return 1;
3922     }
3923 
3924 
3925     if ( strstr( fname, ".ps" ) != 0) {
3926 	if (debugF2GIF) fprintf( stderr, "Converting ps to gif for %s\n",
3927 				 fname );
3928 	/* The figure argument preserves color */
3929 	snprintf( pgm, sizeof(pgm), "%spstogif %s %s figure >>%s 2>&1",
3930 		  PSPATH, fname, fname2, latex_errname );
3931 	if (debugF2GIF)
3932 	    fprintf( stderr, "Running %s\n", pgm );
3933 	rc = system( pgm );
3934 	if (rc != 0) {
3935 	    fprintf( stderr, "Error code %d from %s\n", rc, pgm );
3936 	    return 0;
3937 	}
3938 	p = fname2 + strlen(fname2) - 1;
3939 	while (p != fname2 && *p != DirSep) p--;
3940 	if (*p == DirSep) p++;
3941 	strcpy( fname, p );
3942 	return 1;
3943     }
3944     else if ( strstr( fname, ".eps" ) != 0) {
3945 	if (debugF2GIF) fprintf( stderr, "Converting eps to gif for %s\n",
3946 				 fname );
3947 	/* The figure argument preserves color */
3948 	snprintf( pgm, sizeof(pgm), "%spstogif %s %s figure >>%s 2>&1",
3949 		  PSPATH, fname, fname2, latex_errname );
3950 	if (debugF2GIF)
3951 	    fprintf( stderr, "Running %s\n", pgm );
3952 	rc = system( pgm );
3953 	if (rc != 0) {
3954 	    fprintf( stderr, "Error code %d from %s\n", rc, pgm );
3955 	    return 0;
3956 	}
3957 	p = fname2 + strlen(fname2) - 1;
3958 	while (p != fname2 && *p != DirSep) p--;
3959 	if (*p == DirSep) p++;
3960 	strcpy( fname, p );
3961 	return 1;
3962     }
3963     else if ( strstr( fname, ".pnm" ) != 0) {
3964 	return 1; /* Will this work? */
3965     }
3966     else if ( strstr( fname, ".pdf" ) != 0) {
3967 	if (debugF2GIF) fprintf( stderr, "Converting pdf to gif for %s\n",
3968 				 fname );
3969 	/* Change the output name to a .ps file */
3970 	p = fname2 + strlen(fname2) - 1;
3971 	while (p != fname2 && *p != '.') p--;
3972 	strncpy( p, ".ps", 10 );
3973 
3974 	/* Remove the file that we're about to create.  Ignore errors */
3975 	unlink( fname2 );
3976 	snprintf( pgm, sizeof(pgm), "pdf2ps %s %s >>%s 2>&1", fname, fname2,
3977 		  latex_errname );
3978 	if (debugF2GIF)
3979 	    fprintf( stderr, "Running %s\n", pgm );
3980 	rc = system( pgm );
3981 	if (rc != 0) {
3982 	    fprintf( stderr, "Error code %d from %s\n", rc, pgm );
3983 	    return 0;
3984 	}
3985 	if (debugF2GIF)
3986 	    fprintf( stderr, "Now, convert %s to gif\n", fname2 );
3987 	/* Now convert the postscript file */
3988 	rc = TXConvertFigureToGIF( fname2 );
3989 	strcpy( fname, fname2 );
3990 	return rc;
3991     }
3992     return rc;
3993 }
3994 
3995 /* Active character handling */
3996 void TXActiveCharSet( char ch, void (*fcn)(char *) )
3997 {
3998     ActiveChar       = ch;
3999     activeCharAction = fcn;
4000 }
4001 void TXActiveCharClear(void)
4002 {
4003     ActiveChar       = '\0';
4004     activeCharAction = 0;
4005 }
4006 void TXActiveCharDo(char *str)
4007 {
4008     if (activeCharAction) {
4009 	(*activeCharAction)(str);
4010     }
4011     else {
4012 	fprintf(stderr, "Attempt to invoke active char action with null action\n");
4013     }
4014 }
4015 
4016 void TXObeylinesFlushLine(char *token)
4017 {
4018     if (!InDocument) return;
4019     TeXoutcmd( fpout, "<br>\n" );
4020 }
4021 void TXObeylinesClearActive(void)
4022 {
4023     TXActiveCharClear();
4024 }
4025 /* Obeylines changes the handling of \n until the current group ends */
4026 void TXDoObeylines(TeXEntry *e)
4027 {
4028     TXActiveCharSet('\n', TXObeylinesFlushLine);
4029     TXgroupPushAction(TXObeylinesClearActive);
4030 }
4031