/* -*- Mode: C; c-basic-offset:4 ; -*- */
#include <stdio.h>
#include <ctype.h>
#include <unistd.h> /* for unlink */
#include "sowing.h"
#include "search.h"
#include "tex.h"

#define OUTFILE_SIZE 512
/* #define DBGMSG(a) if (DebugCommands) { fprintf( stdout, ... what goes here .. ); } */

extern LINK *GetNextLink ( LINK * );
/*
   This is a simple program to translate mostly ASCII text code from
   a latexinfo file into another format.  Different formats can be
   generated by providing different versions of the file tex2....c
   For example,
       tex2win generates Windows help output.
       tex2html generates HTML (WWW) output

 */

/*
  Outstanding problems:

  InOutputBody isn't set except within "sections" (after WriteBeginPage).
  Some TeX code doesn't use section/chapter/part.

  InOutputBody is used to suppress random TeX code from generating output.
  We need another way to indicate InOutputBody.
   -> many commands could set when they are called, indicating that we are
   doing output.  ???

  In general, this code needs to be entirely re-written.  The model should be
  a string of filters that recognize various constructions and replace them.
  replaced code is contained in (nested?) begin-tok/end-tok pairs.
 */


/*
   I'd like to push a token in every routine, but the PC version seems to
   run out of stack space (very, very tiny stacks!)

   What I should do is just allocate and deallocate.  This is more expensive,
   but TeX is recursive and I've had problems with things like TeX commands
   in \subsection....
 */

/*
   Still need code to handle
   \font\manual=manfnt at 12pt
   (also, how to handle in generating output?  Should it produce a
   bitmap?)
 */
/* static char gtoken[MAX_TOKEN]; */

/* These are used to hold a stack of tokens so that (a) we don't have to
   allocated them all the time and (b) we don't require the ability to
   allocate them off of the stack (a problem for PC's) */
char *tokbuf, *curtok;
int  toknum;

/*
void TeXProcessCommand();
extern void TXem(), TXtt(), TXbf(), TXsf(), TXrm(),
            TXmath(), TXmathend(), TXinlinemath(),
            TXinlinemathend(), TXbitmap(), TXbw2(), TXbw(), TXbbrace(),
            TXebrace(), TXoutbullet(), TXbgroup(), TXegroup();
extern int  TXWriteStartNewline(), TeXoutNewline();

extern void TXbitemize(), TXeitemize(),
            TXbenumerate(), TXeenumberate(),
            TXbdescription(), TXedescription(),
            TXbmenu(), TXemenu(), TXdimen(), TXnumber(), TXDef(), TeXtabular(),
            TXbibitem(), TXDoNewenvironment(), TXDoNewtheorem(),
            TXcounter(), TXadvance(),
            TXDoBibliography(), TXcaption();
extern int LookupEnv();

extern void SCSetAtLetter();
extern char SCGetCommentChar();

*/
/* Forward refs */
/*
void TeXskipEnv(), TXoutactiveToken();
*/
void PrintLastSectionName( FILE * );

FILE *ferr = 0;  /* We'd like this to be stderr, but ANSI/ISO C broke this */
SRList *topicctx = 0;

int InDocument = 0;
int InOutputBody = 0;

static int AmSkipping = 0;    /* Set to one when skipping a tex environment */
int InArg             = 0;    /* Set to one when in an argument */
char *ArgBuffer       = 0;    /* Used to hold temporary text */
static int InVerbatim = 0;    /* Verbatim has special processing */
static int DebugArgs  = 0;    /* Set to one to dump arg processing */
int DebugCommands = 0;        /* Set to one to dump command processing */
int DebugOutput   = 0;        /* Set to one to dump output processing */
int DebugFile     = 0;        /* Debug file related commands */
int DebugFont     = 0;        /* Debug font related commands */
int warnRedefinition = 0;     /* Warn on redefinition of commands */
static int UseIfTex   = 0;    /* Set to use begin{iftex} ... instead of
				 begin{ifinfo} code */

static int ProcessManPageTokens = 0;
                              /* Check EVERY token for a reference to a
				 known man page.  WARNING: this may make
				 the file rather link-heavy */

/* These control whether we generate GIF files for what we don't understand */
int LatexUnknownEnvs = 0;
/* Latex Tables? */
int LatexTables	    = 0;
/* Latex Math? */
int LatexMath	    = 0;
/* Simple Math (math mode with no TeX commands done in italics */
int SimpleMath	    = 0;
/* Number of the generated image file */
int imageno	            = 0;
/* Set LatexAgain to 0 if you want to use the old img files */
int LatexAgain       = 1;

/* Set to 1 to generate HTML for tabular environments */
int HandleAlign      = 0;

int TableNumber = 0, FigureNumber = 0, EquationNumber = 0;
int NumberedEnvironmentType = ENV_NONE;
char *envjumpname = 0;
int  envJumpNum   = 0;

int IncludeSectionNumbers = 0;
/* MinSectionKind allows us to handle documents with Chapters and Sections
   better (a bibliography is the same kind of item as the largest
   section (Chapter == 0, Section == 1)
 */
int MinSectionKind = 1;

int IgnoreCatcode = 1;

/* This is for debugging the LaTeX file; it is important to ensure that all
   braces are closed */
int LineNo[10] = { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
char *(InFName[10]) = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
int BraceCount = 0;
char CmdName[65];

/* These hold the current section being processed, so that LABELS to this
   section may be entered into the label table (and hence to the aux file for
   the section pass). */
static int CurSeqnum     = 0;
static char CurNodename[256];

/* Citation characters.  The default is [ and ] */
char *CitePrefix = 0;
char *CiteSuffix = 0;


int UsingLatexinfo = 0;
int DestIsHtml     = 1;
int HTMLv3         = 1;

#if defined(WIN32) || defined(__MSDOS__)
char HTML_Suffix[5] = "htm";
char DirSep        = '\\';
char DirSepString[2] = "\\";
#else
char HTML_Suffix[5] = "html";
char DirSep        = '/';
char DirSepString[2] = "/";
#endif
/*
    TeX processing:
    We need to manage things like \section{name}, \begin{tex}, \c (comment)

    Multiple blank lines must generate a \par in the rtf file.
    More specifically, a completely blank line must be changed into a par.
 */
/* The tex stack */
typedef struct {
    int fonttype;         /* Current font */
    /* Other parameters as required */
    int fontsize;         /* Not used yet; should be delta? */
    } TeXStack;

/* The section stack */
typedef struct {
    int level;     /* Section level */
    int count;     /* Current count of sections at this level */
    /* entries for parent, children, siblings?? */
    } SectionStack;

/* The list of known functions */
SRList *TeXlist = 0;

/* Stack of input files */
int    curfile = 0;
FILE   *(fpin[10]) = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
FILE   *fpout = 0;
char   *outfile = 0;

TeXStack stack[MAX_TEX_STACK];
/* static int texSp = -1; */
LaTeXStack lstack[MAX_TEX_STACK];
int lSp = -1;
SectionStack sstack[MAX_TEX_STACK];
static int SSp = -1;

/* The "brace" stack */
#define MAX_BLOC_FNAME 50
typedef struct {
    int lineno;
    char filename[255];
    } BraceElment;
#define MAX_BLOC 50
static BraceElment bloc[MAX_BLOC];

/* TeX special characters on startup */
char CommandChar     = '\\';
char SubscriptChar   = '_';
char SuperscriptChar = '^';
char MathmodeChar    = '$';
char CommentChar     = '%';
char LbraceChar      = '{';
char RbraceChar      = '}';
char ArgChar         = '#';
char AlignChar       = '&';
char ActiveChar      = '\0';   /* Normally not set.  We allow a single char
				  to be active; (will be) used for obeylines */
void (*activeCharAction)(char *) = 0;

/* Special user commands */
char *UserIndexName = 0;

/* File for latex or graphics output errors */
char latex_errname[300];

/* File name base for generated image files (default img) */
char imgfilebase[MAX_IMAGE_FILE_BASE];

/*
    This file contains the actions for processing a subset of LaTeXinfo
    files
 */

void TXSetDebug( int flag )
{
    DebugArgs     = flag;
    DebugCommands = flag;
}

void TXSetDebugFile( int flag )
{
    DebugFile = flag;
}

void TXSetDebugFont( int flag )
{
    DebugFont = flag;
}

void TXSetUseIfTex( int flag )
{
    UseIfTex = flag;
}

void TXSetLatexUnknown( int flag )
{
    LatexUnknownEnvs = flag;
}

void TXSetProcessManPageTokens( flag )
int flag;
{
    ProcessManPageTokens = flag;
}

void TXSetLatexTables( int flag )
{
    LatexTables = flag;
}
void TXSetLatexMath( int flag )
{
    LatexMath = flag;
}

void TXSetSimpleMath( int flag )
{
    SimpleMath = flag;
}

void TXSetLatexAgain( int flag )
{
    LatexAgain       = flag;
}

void TXSetFiles( char *fin, char *fout )
{
    outfile = (char *)MALLOC( OUTFILE_SIZE );
    CHKPTR(outfile);
    strcpy( outfile, fout );
    InFName[0] = (char *)MALLOC( strlen(fin) + 1 );
    CHKPTR(InFName[0]);
    strcpy( InFName[0], fin );
}

void TeXAbort( const char *routine, const char *msg )
{
    fprintf( stdout, "%s:%s\n", routine, msg ? msg : "No message" );
    fprintf( stdout, "File %s line %d\n",
	     InFName[curfile] ? InFName[curfile]: "", LineNo[curfile] );
    exit(1);
}

void TXPrintLocation( FILE *fp )
{
    fprintf( fp, "File %s line %d\n",
	     InFName[curfile] ? InFName[curfile]: "", LineNo[curfile] );
}

/* Output a command */
void TeXoutcmd( FILE *fout, const char *str )
{
    char buf[102];
    if (strlen(str) >= 100) {
	fprintf( stderr,
		 "Error in TeXoutcmd (internal error, command too long)!\n" );
	return;
    }
    buf[0] = TOK_START;
    strcpy( buf+1, str );
    buf[strlen(buf)+1] = 0;
    buf[strlen(buf)]   = TOK_END;
    if (InArg) {
	if (!ArgBuffer) {
	    ArgBuffer = MALLOC( MAX_TOKEN );
	    CHKPTR(ArgBuffer);
	}

	strcat( ArgBuffer, buf );
    } /* SCAppendToken( buf ); */
    else {
	if (ProcessManPageTokens)
	    TXoutactiveToken( buf );
	else
	    WriteString( fout, buf );
    }
}

/* Output text (may be pushed back for rescanning) */
void TeXoutstr( FILE *fout, const char *str )
{
    if (InArg) {
	if (!ArgBuffer) {
	    ArgBuffer = MALLOC( MAX_TOKEN );
	    CHKPTR(ArgBuffer);
	}
	strcat( ArgBuffer, str );
    } /* SCAppendToken( str ); */
    else {
	if (ProcessManPageTokens)
	    TXoutactiveToken( str );
	else
	    WriteString( fout, str );
    }
}

void TeXoutsp( FILE *fout, int nsp )
{
    int i;
    if (!InDocument) return;
    for (i=0; i<nsp; i++) TeXoutstr( fout, " " );
}

/* Read a TeX token; control sequences (\name) are read as a single token.
   nsp is the number of preceding blanks.  Returns the first character in
   the token (or EOF)
 */
int TeXReadToken( char *token, int *nsp )
{
    int ch, ch2, d;

    ch = SCTxtFindNextANToken( fpin[curfile], token, MAX_TOKEN, nsp );
    if (ch == '\\') {
	ch2 = SCTxtFindNextANToken( fpin[curfile], token+1, MAX_TOKEN-1, &d );
	if (d > 0) {
	    /* If there were leading blanks, convert them into a single
	       blank and push the token back */
	    SCPushToken( token+1 );
	    token[1] = ' ';
	    token[2] = 0;
	}
    }
    return ch;
}

/* Read a macro name, having read the first \ */
void TeXReadMacroName( char *token )
{
    int ch, nsp;

    while ( (ch = SCTxtFindNextANToken( fpin[curfile], token, MAX_TOKEN, &nsp ))
	    == EOF) {
	if (DebugCommands)
	    fprintf( stdout, "EOF in TeXskipEnv while reading macro\n" );
	TXPopFile();
    }
    if (nsp != 0) {
	SCPushToken( token );
	strcpy( token, " " );
    }
}

/*
   Get a TeX argument.  Return 0 if no matching argument found, 1 otherwise.
   if doeval is false, don't process any command that is found; instead,
   just copy it.

   A complication here is that we may need to recursively evaluate the tokens
   that are being read.  We have to be careful to avoid rescanning the
   same text over and over.  We do this by having EACH invocation of
   TeXGetGenArg create a private output buffer that is used by the output
   routines to append the output into each time "TXProcessCommand" is
   invoked.  At the end of the call to TXProcessCommand, this output is
   pushed back onto the input buffer.

   2/11/97
   If a comment character is defined, we skip to the end of the line
 */
#define MAX_MEM_STACK 128
/* Just to avoid constant mallocs, we maintain a stack of memory */
/* Points to last used element */
/* When new space is allocated, it MUST have size MAX_TOKEN */
static int mem_stack_sp = -1;
static int mem_stack_wm = -1;
static char *(mem_stack[MAX_MEM_STACK]);
#define ALLOC_TOKEN(t) {\
    if (mem_stack_sp < mem_stack_wm) {\
      t = mem_stack[++mem_stack_sp];\
    }else {\
       if (mem_stack_wm >= MAX_MEM_STACK) {\
        fprintf( stderr, "Overflow in TeX argument processing!\n" );\
        exit(1);\
        }\
       mem_stack[++mem_stack_wm] = MALLOC( MAX_TOKEN );\
       t = mem_stack[++mem_stack_sp];}}

#define FREE_TOKEN(t) mem_stack_sp--;

/*
   Return 1 for found argument, 0 for no arg, and -1 for error.
 */
static int GetArgDepth=0;
int TeXGetGenArg( FILE *fin, char *token, int maxtoken, char sc, char ec,
		  int doeval )
{
    int  ch, nsp, i, oldinarg = InArg;
    char *local_buf, *save_buf, *save_buf2;
    char mtoken[MAX_TOKEN];
/* tf and mf save the token state */
    char *tf = token;
    int  mf  = maxtoken;
    int  depth = 0, found;
    TeXEntry E;

    GetArgDepth++;
    found = 0;
    save_buf  = ArgBuffer;
    ALLOC_TOKEN(local_buf);
    ArgBuffer = local_buf;
/* ArgBuffer = local_buf = MALLOC( MAX_TOKEN );
   if (!local_buf) {
       fprintf( stderr, "Out of memory!\n" );
       exit(1);
       }
   */
    local_buf[0] = 0;  /* In case we don't push anything back */
    while ((ch = SCTxtFindNextANToken( fin, token, maxtoken, &nsp )) == EOF) {
	if (DebugCommands)
	    fprintf( stdout, "EOF in TeXGetGenArg\n" );
	TXPopFile();
    }
    if (DebugArgs) fprintf( stdout, "ARG-start: ch = %c\n", ch );
    if (ch == sc) {
	found = 1;
	depth = 1;
	InArg = 1;
	token[0] = 0;
	while (maxtoken > 0) {
	    while ((ch = SCTxtFindNextANToken( fin, mtoken, MAX_TOKEN, &nsp )) ==
		   EOF) {
		if (DebugCommands)
		    fprintf( stdout, "EOF in TeXGetGenArg while reading arg.\n" );
		/* This is almost certainly an error */
		fprintf( stdout, "EOF while reading an argument in file %s[%d]\n",
			 InFName[curfile], LineNo[curfile] );
		if (CmdName[0])
		    fprintf( stdout, "In command %s\n", CmdName );
		TXPopFile();
	    }
	    if (DebugArgs) {
		fprintf( stdout, "ARG(%d):%s\n", GetArgDepth, mtoken );
	    }
	    for (i=0; i<nsp; i++) token[i] = ' ';
	    token    += nsp;
	    maxtoken -= nsp;
	    /* We must correctly process enter and leave groups.  Note that since
	       we may be looking for [] instead of {}, we defer the testing for
	       {} to the end */
	    if (ch == ec) {
		depth--;
		if (depth == 0) break;
		if (!doeval) {
		    *token++ = ec;
		    maxtoken--;
		}
	    }
	    else if (ch == sc) {
		depth++;
		if (!doeval) {
		    *token++ = sc;
		    maxtoken--;
		}
	    }
	    /* Skip comments 2/11/97 */
	    else if (ch == CommentChar) {
		SCTxtDiscardToEndOfLine( fin );
		LineNo[curfile]++;
		continue;
	    }
	    else if (mtoken[0] == '\\') {
		TeXReadMacroName( mtoken );
		if (DebugArgs)
		    fprintf( stdout, "ARGcmd(%d):\\%s\n", GetArgDepth, mtoken );
		/* DONT REPROCESS RTF commands */
		/* NEEDS TO BE CHANGED FOR HTML */
		if (0) {
		}
		else {
		    if (doeval) {
#ifndef FOO
			ArgBuffer = local_buf;
			TeXProcessCommand( mtoken, fin, (FILE *)0 );
			ArgBuffer = local_buf;
			SCPushToken( local_buf );
			local_buf[0] = 0;
			InArg = 1;
#else
			/* I think that this should allocate a new buffer
			   for args, then push the evaluation of that
			   back onto the stack ... */
			/* ArgBuffer = local_buf; */
			char *tmpbuf;
			ALLOC_TOKEN(tmpbuf)
			    tmpbuf[0] = 0;
			ArgBuffer = tmpbuf;
			TeXProcessCommand( mtoken, fin, (FILE *)0 );
			ArgBuffer = local_buf;
			/* I'm not sure about this either.  It is really
			   time to redesign the code.... */
			SCPushToken( tmpbuf );
			/* SCPushToken( local_buf ); */
			local_buf[0] = 0;
			/* FREE(tmpbuf); */
			FREE_TOKEN;
			InArg = 1;
#endif
		    }
		    else {
			*token++ = '\\';
			maxtoken--;
			strcpy( token, mtoken );
			token    += strlen( mtoken );
			maxtoken -= strlen( mtoken );
		    }
		}
            }
	    else {
		if (DebugArgs)
		    fprintf( stdout, "->ARG(%d):%s\n", GetArgDepth, mtoken );
		strcpy( token, mtoken );
		token    += strlen( mtoken );
		maxtoken -= strlen( mtoken );
            }

	    if (ch == RbraceChar) {
		/* In case any output is generated... */
		save_buf2 = ArgBuffer;
		ArgBuffer = local_buf;
		local_buf[0] = 0;
		TXegroup( &E );
		SCPushToken( local_buf );
		local_buf[0] = 0;
		ArgBuffer = save_buf2;
	    }
	    else if (ch == LbraceChar)
		TXbgroup( &E );
        }
	token[0] = 0;
	InArg    = oldinarg;
    }
    else {
	SCPushToken( token );
	token[0] = 0;
    }
    FREE_TOKEN(local_buf);
/* mem_stack_sp--; */
/*FREE( local_buf );*/
    ArgBuffer = save_buf;
    if (maxtoken <= 0) {
	if (maxtoken < 0)
	    fprintf( stderr, "token size exceeded by %d\n", -maxtoken );
	fprintf( stderr, "Argument too long in file %s line %d; aborting\n",
		 InFName[curfile] ? InFName[curfile]: "<input>", LineNo[curfile] );
	if (strlen(tf) < 80) {
	    fprintf( stderr, "Token(%d) was %s\n", mf, tf );
	}
	else {
	    fprintf( stderr, "Token(%d) was %.40s...%40s\n", mf, tf, tf );
	}
	found = -1;
	/* exit(1); */
    }
    if (DebugArgs && found) {
	fprintf( stdout, "ARG-end:arg is |" );
	TXPrintToken( stdout, tf );
	fprintf( stdout, "|\n" );
    }
/* else just use the single token */
    GetArgDepth--;
    return found;
}

/* Get a TeX argument */
int TeXGetArg( FILE *fin, char *token, int maxtoken )
{
    int rc = TeXGetGenArg( fin, token, maxtoken, LbraceChar, RbraceChar, 1 );
    if (rc < 0) {
	fprintf( stdout, "Error getting argument\n" );
    }
    return rc;
}

/* This is a version of TeXGetArg that aborts on failure */
void TeXMustGetArg( FILE *fin, char *token, int maxtoken,
		    const char *caller, char *texcmd )
{
    if (TeXGetGenArg( fin, token, maxtoken, LbraceChar, RbraceChar, 1 ) < 0) {
	TeXAbort( caller, texcmd );
    }
}

void TXnop( TeXEntry *e )
{
  int i;

  /* Tex command that has no effect in RTF file */
  if (DebugCommands)
    fprintf( stdout, "Skipping %d arguments for %s\n", e->nargs, e->name );
  /* Don't bother to evaluate arguments that we are skipping */
  PUSHCURTOK;
  for (i=0; i<e->nargs; i++) {
    if (TeXGetGenArg( fpin[curfile], curtok, MAX_TOKEN,
		      LbraceChar, RbraceChar, 0 ) == -1) {
      TeXAbort( "TXnop", e->name );
    }
  }
  POPCURTOK;
}

/* Copy a string and return it in newly allocated memory */
char *TXCopy( const char *s )
{
    char *n;
    n = MALLOC( strlen( s ) + 1 );
    strcpy( n, s );
    return n;
}

/* Place the argument in the location stored in the ctx */
void TXsavearg( TeXEntry *e )
{
    PUSHCURTOK;
    strncpy( CmdName, e->name, sizeof(CmdName)-1 );
    TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN, "TXsavearg", e->name );
    strcpy( (char *)(e->ctx), curtok );
    CmdName[0] = 0;
    POPCURTOK;
}

/* Replace \name with a string */
void TXname( TeXEntry *e )
{
    if (!InDocument) return;
    TeXoutstr( fpout, (char *)(e->ctx) );
}

/* Output raw HTML (or RTF) */
void TXraw( TeXEntry *e )
{
    if (!InDocument) return;
    TeXoutcmd( fpout, (char *)(e->ctx) );
}

void TXcomment( TeXEntry *e )
{
    SCTxtDiscardToEndOfLine( fpin[curfile] );
    LineNo[curfile]++;
}

/* Do \char`\\ */
void TXchar( TeXEntry *e )
{
    int ch, nsp;

    PUSHCURTOK;
    ch = SCTxtFindNextANToken( fpin[curfile], curtok, MAX_TOKEN, &nsp );
    if (ch == EOF) return;
    if (ch == '`') {
      ch = TeXReadToken( curtok, &nsp );
      TeXoutstr( fpout, curtok );
    }
    else
      TeXoutstr( fpout, curtok );
    POPCURTOK;
}

/* Remove a LaTeX [...].  Leave the token in "token"; if there is
 no [], set token[0] to 0 */
void TXRemoveOptionalArg( char *token )
{
    int ch;
    int nsp;

    token[0] = 0;
    ch = SCTxtFindNextANToken( fpin[curfile], token, MAX_TOKEN, &nsp );
    SCPushToken( token );
    if (ch == '[') {
	/* Strip the token */
	if (TeXGetGenArg( fpin[curfile], token, MAX_TOKEN, '[', ']', 0 ) == -1)
	    TeXAbort( "TXRemoveOptionalArg", (char *)0 );
    }
    else
      token[0] = 0;
}

/* Remove a LaTeX * (as in \vspace*{3in}) */
void TXRemoveOptionalStar( char *token )
{
    int ch;
    int nsp;

    token[0] = 0;
    ch = SCTxtFindNextANToken( fpin[curfile], token, MAX_TOKEN, &nsp );
    if (ch != '*')
	SCPushToken( token );
}

/* This is nop, but after removing any optional * */
void TXnopStar( TeXEntry *e )
{
    PUSHCURTOK;
    TXRemoveOptionalStar( curtok );
    POPCURTOK;
    TXnop( e );
}

/* the \\ can have the form \\[dimen] for adding space; this
   routine eats the [...] */
void TXdoublebw( TeXEntry *e )
{
    PUSHCURTOK;
    TXRemoveOptionalArg( curtok );
    POPCURTOK;
    if (HandleAlign && lstack[lSp].env == TXTABULAR)
	TeXEndHalignRow();
    else
	TXbw2( e );
}

void TXnewline( TeXEntry *e )
{
    PUSHCURTOK;
    TXRemoveOptionalArg( curtok );
    POPCURTOK;
    TXbw2( e );
}
/* generate the date.  Probably not in the expected format */
void TXtoday( TeXEntry *e )
{
    char date[100];
    SYGetDate( date );
    SCPushToken( date );
}

/* For ref to work, we need to install the \label defines in a label table */
void TXref( TeXEntry *e )
{
    int  refnumber = 0;
    char *refname;
    char fname[256];
    char lfname[256];

    if (DebugCommands)
	fprintf( stdout, "Getting argument for %s\n", e->name );
    PUSHCURTOK;
    strcmp( CmdName, "ref" );
    TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN, "TXref", e->name );
    if (!InDocument) {
      POPCURTOK;
      return;
    }
    ReplaceWhite( curtok );
/* Match name to section and generate node reference */
    refname = LabelLookup( curtok, &refnumber, fname );

    if (refname) {
	if (refnumber >= 0) {
	    if (fname[0])
		snprintf( lfname, sizeof(lfname), "%s#Node", fname );
	    else
		strncpy( lfname, "Node", sizeof(lfname) );
	    WritePointerText( fpout, refname, lfname, refnumber );
	}
	else {
	    WritePointerText( fpout, refname, fname, refnumber );
	}
    }
    else
	fprintf( stderr, "\\ref{%s} unknown (%s line %d)\n",
		 curtok,
		 InFName[curfile] ? InFName[curfile]: "",
		 LineNo[curfile] );
    POPCURTOK;
    CmdName[0] = 0;
}

/*
   A label within an equation, table, or figure should be treated
   differently.  In particular, for cases where we use an image file for
   the equation etc, we really need to place a new anchor and node number
   around the equation etc.

   Note that the anchor need only be around a small part;
   also, we could ALWAYS place the anchor and only use it as required.
 */
void TXlabel( TeXEntry *e )
{
    if (DebugCommands)
	fprintf( stdout, "Getting argument for %s\n", e->name );
    PUSHCURTOK;
    strcpy( CmdName, "label" );
    TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN, "TXlabel", e->name );
    /* goken in ref refers to the value of LabelName */
    ReplaceWhite( curtok );

    /* Originally, the value of the label was just the section that contained
       the label.  This gave both poor label names and rather imprecise
       location to a reference */
    if (NumberedEnvironmentType == ENV_NONE)
      WriteLabeltoauxfile( CurSeqnum-1, outfile, curtok, CurNodename );
    else {
      char buf[10];
      if (!envjumpname || !envjumpname[0]) {
	fprintf( ferr,
		 "Label %s occurs in an unexpected environment at %s, line %d\n",
		 curtok, InFName[curfile], LineNo[curfile] );
	POPCURTOK;
	return;
      }
      snprintf( buf, sizeof(buf), "%d", envJumpNum );
      WriteLabeltoauxfile( -1, envjumpname, curtok, buf );
    }
    POPCURTOK;
    CmdName[0] = 0;
}

/* Just use the section names as the reference */
void TXhref( TeXEntry *e )
{
    LINK *RefedSection;
    int  dummy;
    char lfname[256];
    char *topicfile;

    if (!InDocument) return;
    strcpy( CmdName, "href" );
    if (DebugCommands)
	fprintf( stdout, "Getting argument for %s\n", e->name );
    PUSHCURTOK;
    TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN, "THhref", e->name );
    ReplaceWhite( curtok );
    RefedSection = SRLookup( topicctx, curtok, (char *)0, &dummy );
    if (!RefedSection) {
	fprintf( stderr, "Could not find %s in topicctx\n", curtok );
	POPCURTOK;
	return;
    }
    topicfile = TopicFilename( RefedSection );
    if (topicfile)
	snprintf( lfname, sizeof(lfname), "%s#Node", topicfile );
    else
	strncpy( lfname, "Node", sizeof(lfname) );
    WritePointerText( fpout, curtok, lfname, RefedSection->number );
/* \hrefa has a second argument that is used in the LaTeX version as the
   replacement text */
    if (e->nargs > 1) {
	TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN, "TXhref", e->name );
    }
    POPCURTOK;
    CmdName[0] = 0;
}

void TXmore( TeXEntry *e )
{
    TeXoutstr( fpout, " (Press " );
    TXref( e );
    TeXoutstr( fpout, " for more information)\n" );
}

/*
 * This is ugly, but there are several places where comment characters are used
 * We should really combine them
 */
#define MAX_COMMENT_DEPTH 25
static char SavedCommentChar[MAX_COMMENT_DEPTH];
static char SavedCommentChar2[MAX_COMMENT_DEPTH];
static int comment_depth=0;
void PushCommentChar( char new_c )
{
    SavedCommentChar[comment_depth] = CommentChar;
    SavedCommentChar2[comment_depth++] = SCGetCommentChar();
    CommentChar = new_c;
    SCSetCommentChar( new_c );
}
void PopCommentChar( void )
{
    CommentChar = SavedCommentChar[--comment_depth];
    SCSetCommentChar( SavedCommentChar2[comment_depth] );
}

void TXcode( TeXEntry *e )
{
/* output in "code" font */
    if (!InDocument) return;
    strcpy( CmdName, "code" );
    if (DebugCommands)
	fprintf( stdout, "Getting argument for %s\n", e->name );
    PUSHCURTOK;
    /* We must allow comments (any arbitrary character) */
    PushCommentChar( '\0' );
    TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN, "TXcode", e->name );
    PopCommentChar();
    TXbgroup( e );
    TXfont_tt( e );
    TeXoutstr( fpout, curtok );
    TXegroup( e );
    POPCURTOK;
    CmdName[0] = 0;
}

void TXroutine( TeXEntry *e )
{
/* output in "code" font */
    if (!InDocument) return;
    strcpy( CmdName, "routine" );
    if (DebugCommands)
	fprintf( stdout, "Getting argument for %s\n", e->name );
    PUSHCURTOK;
    TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN, "TXroutine", e->name );
    AddToIndex( curtok, CurNodename, CurSeqnum - 1, 0 );
    TXbgroup( e );
    TXfont_tt( e );
    TeXoutstr( fpout, curtok );
    TXegroup( e );
    POPCURTOK;
    CmdName[0] = 0;
}

void TXdfn( TeXEntry *e )
{
/* output in "definition" font */
    if (!InDocument) return;
    if (DebugCommands)
	fprintf( stdout, "Getting argument for %s\n", e->name );
    PUSHCURTOK;
    TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN, "TXdfn", e->name );
    TXbgroup( e );
    TXem( e );
    TeXoutstr( fpout, curtok );
    TXegroup( e );
    POPCURTOK;
}

void TXvar( TeXEntry *e )
{
/* output in "var" font */
    if (!InDocument) return;
    if (DebugCommands)
	fprintf( stdout, "Getting argument for %s\n", e->name );
    PUSHCURTOK;
    TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN, "TXvar", e->name );
    TXbgroup( e );
    TXem( e );
    TeXoutstr( fpout, curtok );
    TXegroup( e );
    POPCURTOK;
}

void TXfile( TeXEntry *e )
{
/* output in "file" font */
    if (!InDocument) return;
    if (DebugCommands)
	fprintf( stdout, "Getting argument for %s\n", e->name );
    PUSHCURTOK;
    TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN, "TXfile", e->name );
    TXbgroup( e );
    TXfont_ss( e );
    TeXoutstr( fpout, curtok );
    TXegroup( e );
    POPCURTOK;
}

void TXatletter( TeXEntry *e )
{
    SCSetAtLetter( 1 );
}

void TXatother( TeXEntry *e )
{
    SCSetAtLetter( 0 );
}

static char  asistoken[MAX_TOKEN];
void TXasis( e )
TeXEntry *e;
{
    int  i;

    if (DebugCommands)
	fprintf( stdout, "Getting %d arguments for %s\n", e->nargs, e->name );
    for (i=0; i<e->nargs; i++) {
	if (TeXGetArg( fpin[curfile], asistoken, MAX_TOKEN ) == -1) {
	    fprintf( stdout, "Failed to get argument for %s\n", e->name );
	    TeXAbort( "TXasis", e->name );
	}
	if (InDocument)
	    TeXoutstr( fpout, asistoken );
    }
}

/* This is "as-is", but with a surrounding group
 */
void TXasisGrouped( TeXEntry *e )
{
    int  i;

    TXbgroup( e );
    if (DebugCommands)
	fprintf( stdout, "Getting %d arguments for %s\n", e->nargs, e->name );
    strncpy( CmdName, e->name, sizeof(CmdName)-1 );
    for (i=0; i<e->nargs; i++) {
	if (TeXGetArg( fpin[curfile], asistoken, MAX_TOKEN ) == -1) {
	    fprintf( stdout, "Failed to get argument for %s\n", e->name );
	    TeXAbort( "TXasisGrouped", e->name );
	}
	if (InDocument) {
	    TeXoutstr( fpout, asistoken );
	}
    }
    TXegroup( e );
    CmdName[0] = 0;
}

/* This is like asis, but handles the "to <dimension>", which may be \hsize */
void TXbox( TeXEntry *e )
{
    int  i, nsp, ch;

    ch = SCTxtFindNextANToken( fpin[curfile], asistoken, MAX_TOKEN, &nsp );
    if (ch == EOF) return;
    if (strcmp( asistoken, "to" ) == 0) {
	/* Very simple-minded: look for \name or <value><scale> */
	ch = TeXReadToken( asistoken, &nsp );
	if (ch != '\\') {
	    SCPushToken( asistoken );
	    TXReadDimen( fpin[curfile] );
	}
    }
    else
	SCPushToken( asistoken );

    TXbgroup( e );
    if (DebugCommands)
	fprintf( stdout, "Getting %d arguments for %s\n", e->nargs, e->name );
    strncpy( CmdName, e->name, sizeof(CmdName)-1 );
    for (i=0; i<e->nargs; i++) {
	if (TeXGetArg( fpin[curfile], asistoken, MAX_TOKEN ) == -1) {
	    fprintf( stdout, "Failed to get argument for %s\n", e->name );
	    TeXAbort( "TXbox", e->name );
	}
	if (InDocument)
	    TeXoutstr( fpout, asistoken );
    }
    TXegroup( e );
    CmdName[0] = 0;
}

/* Handle \input and \include commands */
/*
   Problem.  The TeX \input does not require an argument; you
   can use \input filename.  Need to check for that case and add it
   to the list of 'predoc' commands
   */
void TXinclude( TeXEntry *e )
{
    char *p;
    int  ch;
    if (DebugCommands)
	fprintf( stdout, "Getting argument for %s\n", e->name );
    PUSHCURTOK;
    curtok[0] = 0;
    TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN, "TXinclude", e->name );
    if (strlen(curtok) == 0) {
	/* Get next space-delimited token that is not a right brace*/
	/* (What are the rules on input?) */
	while ((ch = SCTxtGetChar( fpin[curfile] )) != -1 &&
	       isspace(ch) && ch != '\n' && ch != RbraceChar);
	p    = curtok;
	*p++ = ch;
	while ((ch = SCTxtGetChar( fpin[curfile] )) != -1 && !isspace(ch)
	       && ch != RbraceChar)
	    *p++ = ch;
	*p = 0;
	/* We still need to EVALUATE any TeX commands in this string !!! */
	/* Need a command to evaluate a buffer */
	/* We cheat by pushing back { curtok } and making getarg to all
	   of the work */
	SCPushChar( RbraceChar );
	SCPushToken( curtok );
	SCPushChar( LbraceChar );
	TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN,
		       "TXinclude", e->name );
    }
    if (DebugCommands || DebugFile)
	fprintf( stdout, "About to open |%s|\n", curtok );

    if (!InDocument) {
	/* fprintf( stderr, "\\include{%s}\n", curtok ); */
	strcat( predoc, "\\include{" );
	strcat( predoc, curtok );
	strcat( predoc, "}\n" );
    }
    /* Catch the case where a file hasn't been closed */
    if (fpin[curfile+1]) fclose( fpin[curfile+1] );

    /* In some cases, particularly complex macro files used in Books,
       we may want to skip reading an include file, instead relying on
       customized defintions provide through a definitions file.  Without
       this, we'd have to implement the most complex parts of TeX, including
       catcode changes, expandafter, etc. This include file name is
       specified in a defs file using the skipinclude command */
    if (TXIsSkipFile( curtok )) {
	POPCURTOK;
	return;
    }
    /* Check for a replacemtn file name */
    {
	char newtok[MAX_TOKEN];
	if (TXIsReplaceFile( curtok, newtok )) {
	    strcpy( curtok, newtok );
	}
    }

    /* Push this file and process it */
    fpin[++curfile] = fopen( curtok, "r" );
    if (!fpin[curfile]) {
	strcat( curtok, ".tex" );
	fpin[curfile] = fopen( curtok, "r" );
	if (!fpin[curfile]) {
	    curfile--;
	    fprintf( stderr, "(TXinclude) Could not open file %s\n", curtok );
	    POPCURTOK;;
	    return;
	}
    }
    InFName[curfile] = STRDUP( curtok );
    CHKPTR(InFName[curfile]);
    LineNo[curfile] = 1;      /* Line numbers are 1-origin */
    POPCURTOK;
}

void TXPopFile( void )
{
    if (DebugCommands) {
	fprintf( stdout, "Popping file from stack!\n" );
    }
    if (InFName[curfile]) {
	FREE( InFName[curfile] );
    }
    curfile--;
    if (curfile < 0) {
	fprintf( stderr, "EOF in input; aborting\n" );
	fprintf( stderr,
"(Reached an end-of-file in the top-level file without an \\end{document})\n" );
	exit(1);
    }
}

/* ----------------------------------------------------------------------- */
/*
   This implements my \fileinclude{filename} macro which includes the
   entire contents of filename in verbatim form
   */
void TXfileinclude( TeXEntry *e )
{
    char line[257];
    FILE *fp;

    if (DebugCommands)
	fprintf( stdout, "Getting argument for %s\n", e->name );
    PUSHCURTOK;
    curtok[0] = 0;
    TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN,
		   "TXfileinclude", e->name );
    if (DebugCommands || DebugFile)
	fprintf( stdout, "About to open |%s|\n", curtok );

    fp = fopen( curtok, "r" );
    if (!fp) {
	fprintf( stderr, "(TXfileinclude) Could not open file %s\n", curtok );
	POPCURTOK;
	return;
    }
/* Start of verbatim output */
    TXWriteStartNewLine( fpout );
    TXpreformated( fpout, 1 );
    TXbgroup( e );
    TXfont_tt( e );

/* We'd like to map tokens here as well */
    while (fgets( line, sizeof(line)-1, fp )) {
	if (ProcessManPageTokens)
	    /* Eventually used WriteString(fpout,...) */
	    TXoutactiveToken( line );
	else
	    WriteString( fpout, line );

	/* WriteStringRaw( fpout, line ); */
    }
/* End of verbatim output */
    TXegroup( e );
    TXpreformated( fpout, 0 );
    fclose( fp );
    POPCURTOK;
}
/*
   TXIfFileExists - Implement the LaTeX command:
   if file #1 exists, do #2 else do #3

   This routine may not be nested.
 */
void TXIfFileExists( TeXEntry *e )
{
    FILE *fp;

    if (DebugCommands)
	fprintf( stdout, "Getting argument for %s\n", e->name );
    PUSHCURTOK;
    curtok[0] = 0;
    TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN,
		   "TXIfFileExists", e->name );
    if (DebugCommands || DebugFile)
	fprintf( stdout, "Attempting to open file %s\n", curtok );
    fp = fopen( curtok, "r" );
    /* We need to read these args *but not evaluate them* until after
       they are pushed back */
    if (fp) {
	char savetok[MAX_TOKEN];
	fclose( fp );
	/* Use genarg because we want to suppress evaluation */
	if (TeXGetGenArg( fpin[curfile], savetok, MAX_TOKEN,
			   LbraceChar, RbraceChar, 0 ) < 0)
	    TeXAbort( "TXIfFileExists", e->name );
	if (TeXGetGenArg( fpin[curfile], curtok, MAX_TOKEN,
			   LbraceChar, RbraceChar, 0 ) < 0)
	    TeXAbort( "TXIfFileExists", e->name );
	/* Push back the second token */
	SCPushToken( savetok );
    }
    else {
	if (TeXGetGenArg( fpin[curfile], curtok, MAX_TOKEN,
			   LbraceChar, RbraceChar, 0 ) < 0)
	    TeXAbort( "TXIfFileExists", e->name );
	if (TeXGetGenArg( fpin[curfile], curtok, MAX_TOKEN,
			   LbraceChar, RbraceChar, 0 ) < 0)
	    TeXAbort( "TXIfFileExists", e->name );
	/* Push back the third token */
	SCPushToken( curtok );
    }
    POPCURTOK;
}

static LINK *LastSection = 0;
int splitlevel = -1;
char splitdir[256];

void TXSetSplitLevel( int sl, char *dir )
{
    splitlevel = sl;
    if (strlen(dir) > sizeof(splitdir) - 1) {
	TeXAbort( "TXSetSplitLevel", "directory name too long" );
    }
    strncpy( splitdir, dir, sizeof(splitdir) );
}

/*
   Generate the output for a new section; may start by generating the contents
   table for the previous section.

   In order to generate the "correct" navigation links, we use the "next"
   links created when the contents aux (.hux file) were read in.
   To do this, we keep track of where we were in that list with a local
   variable LastSection.

 */
void TXsection( TeXEntry *e )
{
    int   level = (int)(PTRINT)(e->ctx), ch;
    int   dummy;
    char *p;
    int   isUnnumbered = 0;

    TeXWriteContents( fpout );

    if (level < MinSectionKind) MinSectionKind = level;

/* Must put a marker at the END of each section */
    if (CurSeqnum > 0) {
	/* Before we do this, we'd like to add the jump lines for the subsections.
	   We can get this information from the aux file (but not now, since
	   we've started writing the new one). */
	if (LastSection) {
	    if (NumChildren( LastSection ) > 0) {
		WriteChildren( fpout, LastSection, -1 );
	    }
        }
	WriteEndofTopic( fpout );
	/* Add the buttons at the bottom of the section if requested
	   (tohtml only) */
	if (DestIsHtml) {
	    /* fputs( "\n<P><HR>\n", fpout ); */
	    WriteSectionButtonsBottom( fpout, CurNodename, LastSection );
	}
    }

/* First, check for a *.  discard if seen */
    ch = SCTxtGetChar( fpin[curfile] );
    /* FIXME: If an asterisk, the section is neither numbered nor
       updates the section number. */
    if (ch == '*')
	isUnnumbered = 1;
    else
	SCPushChar( ch );

/* Start a new section (at several levels) */
/* Pop section stack if this item is at same level or lower */
    if (SSp >= 0 && sstack[SSp].level == level) {
	if (!isUnnumbered)
	    sstack[SSp].count ++;
    }
    else {
	if (SSp >= 0 && sstack[SSp].level >= level) {
	    /* Move up in heirarchy */
	    while (SSp >= 0 && sstack[SSp].level >= level ) SSp--;
	    SSp++;
	    if (!isUnnumbered)
		sstack[SSp].count++;
	}
	else {
	    /* Move down */
	    SSp++;
	    sstack[SSp].level = level;
	    sstack[SSp].count = 1;
	}
    }

/* First, check for a *.  discard if seen */
    ch = SCTxtGetChar( fpin[curfile] );
    /* FIXME: If an asterisk, the section is neither numbered nor
       updates the section number. */
    if (ch == '*')
	isUnnumbered = 1;
    else
	SCPushChar( ch );

    if (DebugCommands)
	fprintf( stdout, "Getting argument for %s\n", e->name );
    PUSHCURTOK;
    TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN,
		   "TXsection(arg)", e->name );
    /* Remove any leading blanks */
    if (curtok[0] == ' ') {
	char *p0 = curtok;
	char *p1 = curtok;
	/* Find the first nonblank */
	while (*p0 == ' ') p0++;
	/* Move the first nonblank over, and then copy the rest of the string */
	while (*p0 != 0) *p1++ = *p0++;
	*p1 = 0;
    }
    /* Remove any trailing blanks */
    p = curtok + strlen(curtok) - 1;
    if (*p == ' ') {
	while (p > curtok && *p == ' ') p--;
        *++p = 0;
    }
    strncpy( CurNodename, curtok, sizeof(CurNodename) - 1 );

    /* Write out section numbers in the "natural way" */
    if (IncludeSectionNumbers && !isUnnumbered) {
	int i;
	for (i=0; i<=SSp; i++) fprintf( stdout, "%d.", sstack[i].count );
	fprintf( stdout, " %s\n", CurNodename );
    }

    if (level <= splitlevel) {
	/* Need to close current file and open new file. */
	/* New file name is directory/node#.html */
	if (fpout) {
	    WriteEndPage( fpout );
	}
	if (!outfile) {
	    outfile = (char *)MALLOC( OUTFILE_SIZE );
	    CHKPTR(outfile);
	}
	snprintf( outfile, OUTFILE_SIZE, "%s%cnode%d.%s",
                 splitdir, DirSep, CurSeqnum, HTML_Suffix );
	fclose( fpout );
	fpout = fopen( outfile, "w" );
	if (!fpout) {
	    fprintf( ferr, "(TXsection) Could not open file %s\n", outfile );
	    TeXAbort( "TXsection(file)", (char *)0 );
        }
	/* We use the local name (in the directory) for all references */
	snprintf( outfile, OUTFILE_SIZE, "node%d.%s", CurSeqnum, HTML_Suffix );
	WriteHeadPage( fpout );
	WriteFileTitle( fpout, curtok );
	WriteBeginPage( fpout );
    }
    ReplaceWhite( curtok );
    WRtoauxfile( CurSeqnum++, outfile, level, curtok );

    /* FIXME: Why the if (1)? */
    if (1) {
	LastSection = GetNextLink( LastSection );
	/* Check that this matches the name */
	if (LastSection && strcmp(LastSection->topicname,curtok) != 0) {
	    fprintf( stderr, "**Mismatch in section; expected \"%s\" found \"%s\"\n",
		     curtok, LastSection->topicname );
	}
    }
    else {
/* Lookup the section in the aux file list */
	LastSection = SRLookup( topicctx, curtok, (char *)0, &dummy );
	if (!LastSection) {
	    fprintf( stderr, "**Could not find %s in topicctx\n", curtok );
	}
    }

/* Output section headers */
    if (IncludeSectionNumbers && !isUnnumbered) {
	char tmptok[MAX_TOKEN], *p0;
	int i;
	p0 = tmptok;
	for (i=0; i<=SSp; i++) {
	    sprintf( p0, "%d.", sstack[i].count );
	    p0 += strlen(p0);
	}
	strcat( p0, " " );
	strcat( p0, curtok );
	WriteSectionAnchor( fpout, tmptok, "Node", CurSeqnum-1,
			    (int)(PTRINT)(e->ctx) );
    }
    else {
	WriteSectionAnchor( fpout, curtok, "Node", CurSeqnum-1,
			    (int)(PTRINT)(e->ctx) );
    }
    WriteSectionButtons( fpout, curtok, LastSection );

/* These values (entrylevel, number) aren't correct */
/* Write the section numbers by prefixing the section entry values */
    WriteSectionHeader( fpout, curtok, "Node", CurSeqnum-1, (char *)0,
			(int)(PTRINT)(e->ctx) );
/* Insert tree links to parent, child, and sibling.  Sets name for subsequent
   label statements */
    WriteTextHeader( fpout );

/* Skip any newlines until we find the first non-blank */
    SCSkipNewlines( fpin[curfile] );
    POPCURTOK;
}

/*
    This is a special version of section for the first page (vtitle) of
    a set of slides.
 */
void TXtitlesection( TeXEntry *e )
{
    int level = (int)(PTRINT)(e->ctx), ch;
    int dummy;

/* Start a new section (at several levels) */
/* Pop section stack if this item is at same level or lower */
    while (SSp >= 0 && sstack[SSp].level >= level ) SSp--;
    SSp++;
    sstack[SSp].count = 1;

/* First, check for a *.  discard if seen */
    ch = SCTxtGetChar( fpin[curfile] );
    if (ch != '*') SCPushChar( ch );

    if (DebugCommands)
	fprintf( stdout, "Getting argument for %s\n", e->name );
    PUSHCURTOK;
    TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN, "TXsection", (char *)0 );
    strncpy( CurNodename, curtok, sizeof(CurNodename) - 1 );

    WriteHeadPage( fpout );
    WriteFileTitle( fpout, curtok );
    WriteBeginPage( fpout );
    ReplaceWhite( curtok );
    WRtoauxfile( CurSeqnum++, outfile, level, curtok );

/* Lookup the section in the aux file list */
    LastSection = SRLookup( topicctx, curtok, (char *)0, &dummy );
    if (!LastSection) {
	fprintf( stderr, "Could not find %s in topicctx\n", curtok );
    }

/* Output section headers */
    WriteSectionAnchor( fpout, curtok, "Node", CurSeqnum-1,
			(int)(PTRINT)(e->ctx) );
    WriteSectionButtons( fpout, curtok, LastSection );

/* These values (entrylevel, number) aren't correct */
    WriteSectionHeader( fpout, curtok, "Node", CurSeqnum-1, (char *)0,
			(int)(PTRINT)(e->ctx) );

/* Insert tree links to parent, child, and sibling.  Sets name for subsequent
   label statements */
    WriteTextHeader( fpout );

/* Skip any newlines until we find the first non-blank */
    SCSkipNewlines( fpin[curfile] );
    TXWriteStartNewLine( fpout );
    POPCURTOK;
}

void PrintLastSectionName( FILE *fp )
{
    if (LastSection)
	fprintf( fp, "%s\n", LastSection->topicname );
    else
	fprintf( fp, "<before first section>\n" );
}

/* Paragraphs are a very restricted form of section */
void TXparagraph( TeXEntry *e )
{
    int ch;

    /* First, check for a  *.  discard if seen */
    ch = SCTxtGetChar( fpin[curfile] );
    if (ch != '*') SCPushChar( ch );

    if (DebugCommands)
	fprintf( stdout, "Getting argument for %s\n", e->name );
    PUSHCURTOK;
    TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN,
		   "TXparagraph(arg)", e->name );

    /* Output a new paragraph */
    TXWritePar( fpout );
    /* Output the paragraph title.  Font choice? */
    TeXoutstr( fpout, curtok );
    /* Output some space */
    TeXoutstr( fpout, " " );
    /* Skip any newlines until we find the first non-blank */
    SCSkipNewlines( fpin[curfile] );
    POPCURTOK;
}


/*
   \vtt is rather special.  We want to start processing, but most of the
   code expects to see a \begin{document} ... \end{document}.
 */
void TXvtt( TeXEntry *e )
{
    if (!InDocument) {
	TXStyleEPSF( TeXlist, fpin[curfile], fpout );
	TXStyleAnlhtext( TeXlist, fpin[curfile], fpout );
	InDocument = 1;
	TXStartDoc(1);
	TXtitlesection( e );
	TeXskipEnv( e, "_vtt", 1 );
    }
    else {
	TXsection( e );
    }
}
void TXvt( TeXEntry *e )
{
    if (!InDocument) {
	TXStyleEPSF( TeXlist, fpin[curfile], fpout );
	TXStyleAnlhtext( TeXlist, fpin[curfile], fpout );
	InDocument = 1;
	TXStartDoc(1);
	TeXskipEnv( e, "_vtt", 1 );
    }
}
void TXdetails( TeXEntry *e )
{
    TeXoutstr( fpout, "(Press " );
    TXhref( e );
    TeXoutstr( fpout, " for details)" );
}

/* ----------------------------------------------------------------------- */
/* Code for long environments:
   iftex
   tex
   example
   verbatim
   */

/* Skip over an environment.  If flag, write out text and process TeX command,
   otherwise, just skip */
void TeXskipEnv( TeXEntry *e, const char *name, int flag )
{
    int  nsp, ch;
    FILE *fout = fpout;
    int  last_was_nl  = 0;
    int  last_skip    = AmSkipping;
    char *btext, *etext;
    int  nargs;
    int line_num = LineNo[curfile];  /* Line number where we start */

    /* If we are here, we assume that we are in valid TeX.  This is a
       temporary hack.  What we'd like to do is wait until we see either
       a command that generates non-blank output or a section/chapter/part
       command */
    if (!InOutputBody) {
	if (DebugOutput) printf( "Starting a page in skipenv\n" );
	/* Start the page if we haven't already */
	WriteHeadPage( fpout );
	/* We don't have a good title.  Use the input file name */
	WriteFileTitle( fpout, InFName[0] );
	/* WriteBeginPage sets InOutputBody to 1 */
	WriteBeginPage( fpout );
	/* In case we created a new file */
	fout = fpout;
    }

/* First, remove any newlines */
    if (strcmp( name, "verbatim" ) != 0 &&
	(!UsingLatexinfo || strcmp( name, "example" ) != 0)) {
	/* Preserve blanks before first non-blank character */
	SCSkipNewlines2( fpin[curfile] );
    }

/* Flush output at the beginning of each environment (makes debugging a
   little easier) */
    fflush( fpout );
    if (DebugCommands)
	fprintf( stdout, "Processing environment %s (skipenv)\n", name );
    AmSkipping = !flag;
    PUSHCURTOK;
    while (1) {
	while ( (ch =
		 SCTxtFindNextANToken(fpin[curfile], curtok, MAX_TOKEN, &nsp )) == EOF) {
	    if (DebugCommands)
		fprintf( stdout, "EOF in TeXskipEnv\n" );
	    /* Special case for vtt */
	    if (curfile == 0 && strcmp( name, "_vtt" ) == 0) {
		break;
	    }
	    TXPopFile();
	}
	if (ch == EOF) break;
	if (InVerbatim && ch == CommandChar) {
	    last_was_nl  = 0;
	    TeXoutsp( fout, nsp );
	    TeXReadMacroName( curtok );
	    if (strcmp( curtok, "end" ) == 0) {
		if (DebugCommands)
		    fprintf( stdout, "Getting argument for end{}\n" );
		TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN,
			       "TXSkipEnv", e->name );
		if (strcmp( name, curtok ) == 0) {
		    /* We've found the end of the verbatim */
		    break;
		}
		else {
		    /* Ignore it */
		    TeXoutstr( fout, "\\end{" );
		    TeXoutstr( fout, curtok );
		    TeXoutstr( fout, "}" );
		}
	    }
	    else {
		TeXoutstr( fout, "\\" );
		TeXoutstr( fout, curtok );
	    }
	    continue;
	}

	if (InVerbatim) {
	    TeXoutsp( fout, nsp );
	    if (ch == '\n') {
		/* In preformatted output, we don't need any pars */
		TeXoutstr( fout, NewLineString );
		last_was_nl = 1;
	    }
	    else
		TeXoutstr( fout, curtok );
	}
	else {
	    if (ch == CommentChar) {
		SCTxtDiscardToEndOfLine( fpin[curfile] );
		LineNo[curfile]++;
		continue;
	    }
	    if (ch == MathmodeChar && !UsingLatexinfo) {
		TeXoutsp( fout, nsp );
		TXProcessDollar( e, LatexMath, 1 );
	    }
	    else if (ch == AlignChar && HandleAlign &&
		     lstack[lSp].env == TXTABULAR) {
		/* We want to be prepared to put an alignment command,
		   but to support the \multicolumn command, we
		   need to wait until we are sure that the next
		   command is not \multicolumn.  Not handled yet */
		TeXPutAlign();
	    }
	    else if (ch == CommandChar) {
		last_was_nl  = 0;
		TeXoutsp( fout, nsp );
		TeXReadMacroName( curtok );

		if (strcmp( curtok, "end" ) == 0) {
		    if (DebugCommands)
			fprintf( stdout, "Getting argument for end{}\n" );
		    TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN,
				   "TeXSkipEnv", e->name );
		    if (LookupEnv( curtok, &btext, &etext, &nargs)) {
			if (etext) {
			    if (DebugCommands)
				fprintf( stdout, "Pushing back |%s|\n", etext );
			    SCPushToken( etext );
			}
		    }
		    else if (strcmp( curtok, name ) != 0) {
			if (flag)
			    fprintf( ferr,
		  "<skipEnv>%s does not match %s (started at line %d), at %s line %d\n",
		  curtok, name, line_num,
 	          InFName[curfile] ? InFName[curfile]: "", LineNo[curfile] );
		    }
		    else
			break;
		}
		else {
		    /* Need to process TeX command */
		    if (flag) {
			TeXProcessCommand( curtok, fpin[curfile], fout );
			/* Incase we change fpout */
			fout = fpout;
		    }
		}
	    }
	    else if (ch == ActiveChar) {
		TXActiveCharDo(curtok);
	    }
	    else {
		if (flag) {
		    TeXoutsp( fout, nsp );
		    if (ch == '\n') {
			if (last_was_nl) {
			    TXWritePar( fout );
			    SCSkipNewlines( fpin[curfile] );
			}
			else if (lSp >= 0)
			    (*lstack[lSp].newline)( fout );
			else
			    TeXoutNewline( fout );
			last_was_nl = 1;
		    }
		    else {
			if (ch == LbraceChar) {
			    if (BraceCount >= MAX_BLOC) {
				TeXAbort( "", "Braces too deep" );
			    }
			    strncpy( bloc[BraceCount].filename, InFName[curfile],
				     MAX_BLOC_FNAME );
			    bloc[BraceCount++].lineno = LineNo[curfile];
			    /* fprintf( fout, "**+%d[%d]**",
			       LineNo[curfile], BraceCount ); */
			    TXbgroup( e );
			}
			else if (ch == RbraceChar) {
			    BraceCount--;
			    /* fprintf( fout, "**-%d[%d]**",
			       LineNo[curfile], BraceCount ); */
			    if (BraceCount < 0) {
				fprintf( ferr,
				 "Improperly nested brace at %s line %d in ",
				 InFName[curfile] ? InFName[curfile]: "",
				 LineNo[curfile] );
				PrintLastSectionName( ferr );
				/* Reset brace count */
				BraceCount = 0;
			    }
			    TXegroup( e );
			} else {
			    TeXoutstr( fout, curtok );
			}
			last_was_nl  = 0;
		    }
		}
	    }
	}
    }
    POPCURTOK;
    AmSkipping = last_skip;
/* Skip the newlines and the end of the environment */
    SCSkipNewlines( fpin[curfile] );
    if (DebugCommands)
	fprintf( stdout, "Done with environment %s\n", name );
}

/* This is a version of skipEnv that just copies to output; it DOES handle
   nested versions of itself.

   if doout is false, generate no output

   As a special case, we need to watch for \label{...}, since we may need that
   name to resolve references.

   We must process comments, since we must properly detect the following
   \begin{foo}
   ...
   %\end{foo}
   %\begin{foo}
   ...
   \end{foo}

   That is, if % is a comment character, we must stop looking for \end
   to match.  If we are doing something like verbatim, we have to
   turn off the comment character (set to null)
*/
void TeXskipRaw( TeXEntry *e, char *name, int doout )
{
    int  nsp, ch;
    FILE *fout = fpout;
/*     int  (*oldtrans)( char *, int ); */
    /* char commentchar; */

/* First, remove any newlines */
    SCSkipNewlines( fpin[curfile] );

/*
  commentchar = SCGetCommentChar();
  SCSetCommentChar( 0 );
  */
    /*
    oldtrans = SCSetTranslate( (int (*)( char *, int ))0 );
    */
    if (DebugCommands)
	fprintf( stdout, "Processing environment %s (skipRaw)\n", name );
    PUSHCURTOK;
    while (1) {
	while ( (ch = TeXReadToken( curtok, &nsp )) == EOF) {
	    if (DebugCommands)
		fprintf( stdout, "EOF in TeXskipRaw\n" );
	    /* Special case for vtt */
	    if (curfile == 0 && strcmp( name, "_vtt" ) == 0) {
		break;
	    }
	    TXPopFile();
	}
	if (ch == EOF) break;
	if (DebugCommands) {
	    fprintf( stdout, "Token is %s\n", curtok );
	}
	if (ch == CommentChar) {
	    /* Handle comments */
	    if (doout) {
		TeXoutsp( fout, nsp );
		TeXoutstr( fout, curtok );
		TeXoutstr( fout, NewLineString );
	    }
	    SCTxtDiscardToEndOfLine( fpin[curfile] );
	}
	else if (ch == CommandChar) {
	    if (doout)
		TeXoutsp( fout, nsp );

	    if (strcmp( curtok, "\\end" ) == 0) {
		if (DebugCommands)
		    fprintf( stdout, "Getting argument for end{}\n" );
		TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN,
			       "TXSkipRaw", e->name );
		/* Check for a user-defined environment type */
		if (strcmp( curtok, name ) != 0) {
		    if (doout)
			fprintf( fout, "\\end{%s}\n", curtok );
		}
		else {
		    break;
		}
	    }
	    else if (strcmp( curtok, "\\label" ) == 0) {
		/* Need to process a label command */
		TXlabel( e );
		/* Add a dummy incase this is the only thing on a line */
		if (doout)
		    TeXoutstr( fout, "\\label{eq-foo}" );
	    }
	    else if (strcmp( curtok, "\\caption" ) == 0) {
		/* Need to increment the appropriate counter/name */
		TXcaptionHandling( e );
	    }
	    else {
		if (doout) {
		    TeXoutstr( fout, curtok );
		}
	    }
	}
	else {
	    if (doout) {
		TeXoutsp( fout, nsp );
		TeXoutstr( fout, curtok );
	    }
	}
    }
/* Skip the newlines and the end of the environment */
/*     SCSetTranslate( oldtrans ); */
/*
  SCSetCommentChar( commentchar );
  */
    SCSkipNewlines( fpin[curfile] );
    if (DebugCommands)
	fprintf( stdout, "Done with environment %s\n", name );
    POPCURTOK;
}


/*
   See if the named file exists in the current or split directory
 */
int FileExists( char *fname )
{
    FILE *fp;
    int  exists = 0;
    if (splitlevel >= 0) {
	char *fname2;
	fname2 = (char *)MALLOC( 1024 );
	if (!fname2) {
	    fprintf( stderr, "Out of memory!\n" );
	    exit(1);
	}
	sprintf( fname2, "%s/%s", splitdir, fname );
	fp = fopen( fname2, "r" );
	FREE( fname2 );
    }
    else {
	fp = fopen( fname, "r" );
    }
    if (fp) {
	exists = 1;
	fclose( fp );
    }
    return exists;
}

/*
   Manage math mode (\[ .. \] or \( ... \) )
 */
void TXmathmode( TeXEntry *e )
{
#ifndef FOO
    /* If display math, make it look like $$ starts it */
    if (e->name[0] == '[') SCPushToken( "$" );
    TXProcessDollar( e, LatexMath, 1 );
    return;
#else
    if (DebugCommands) fprintf( stdout, "Starting math mode processing\n" );
    if (LatexUnknownEnvs || LatexMath) {
	char bname[100];
	char fname[100];
	char name[4];
	snprintf( bname, sizeof(bname), "%s%d", imgfilebase, imageno++ );
	strcpy( fname, bname );
	strcat( fname, "." );
	strcat( fname, ImageExt );

	name[0] = '\\';
	name[1] = e->name[0];
	name[2] = 0;
	if (LatexAgain || !FileExists( fname )) {
	    /* We always process, because either LatexAgain is 1,
	       or the file does not exists */
	    RunLatex( (char *)0, (char *)0, bname, name, ImageExt, 1 );
	}
	else if (FileExists( fname ) && !LatexAgain) {
	    /* Skip over the section */
	    RunLatex( (char *)0, (char *)0, bname, name, ImageExt, 0 );
	}

	if (name[1] == '[') {
	    /*
	      if (envjumpname && envjumpname[0])
	      TXAnchoredImage( e, envjumpname, fname );
	      else
	      */
	    TXimage( e, fname );
	}
	else  /* \( ... \)  */
	    TXInlineImage( e, fname );
    }
#endif
}

int itemizelevel = -1;

/* Copy this envrionment without doing anything */
void TeXBenign( TeXEntry *e, const char *name )
{
    TXbgroup( e );
    TeXskipEnv( e, name, 1 );
    TXegroup( e );
}

void TeXitemize( TeXEntry *e )
{
    itemizelevel++;
    lstack[++lSp].env	    = TXITEMIZE;
    lstack[lSp].newline	    = TeXoutNewline;
    lstack[lSp].label_node_name = 0;
    lstack[lSp].label_text	    = 0;
    lstack[lSp].line_num = LineNo[curfile];
    lstack[lSp].extra_data = 0;
    TXbitemize( e );
    TeXskipEnv( e, "itemize", 1 );
    TXeitemize( e );
    itemizelevel--;
    lSp--;
    TXWriteStartNewLine( fpout );
}

void TeXenumerate( TeXEntry *e )
{
    itemizelevel++;
    lstack[++lSp].env	    = TXENUMERATE;
    lstack[lSp].num		    = 1;
    lstack[lSp].newline	    = TeXoutNewline;
    lstack[lSp].label_node_name = 0;
    lstack[lSp].label_text	    = 0;
    lstack[lSp].line_num = LineNo[curfile];
    lstack[lSp].extra_data = 0;
    TXbenumerate( e );
    TeXskipEnv( e, "enumerate", 1 );
    TXeenumerate( e );
    itemizelevel--;
    lSp--;
/* Note that the begin/end enumerate generates a newline already */
/* TXWriteStartNewLine( fpout ); */
}

void TeXdescription( TeXEntry *e )
{
    itemizelevel++;
    lstack[++lSp].env	    = TXDESCRIPTION;
    lstack[lSp].newline	    = TeXoutNewline;
    lstack[lSp].label_node_name = 0;
    lstack[lSp].label_text	    = 0;
    lstack[lSp].line_num = LineNo[curfile];
    lstack[lSp].extra_data = 0;
    TXbdescription( e );
    TeXskipEnv( e, "description", 1 );
    TXedescription( e );
    itemizelevel--;
    lSp--;
    TXWriteStartNewLine( fpout );
}

/*
   This handles \begin{list}{itemtext} ... \end{list}
   itemtext is stored in p1 and MUST BE PROCESSED AGAIN
 */
void TeXDoList( TeXEntry *e, char *itemtext, char *itemcommands )
{
    itemizelevel++;
    lstack[++lSp].env		= TXLIST;
    lstack[lSp].num		= 1;
    lstack[lSp].newline		= TeXoutNewline;
    lstack[lSp].p1		= itemtext;
    lstack[lSp].p2		= itemcommands;
    lstack[lSp].label_node_name	= 0;
    lstack[lSp].label_text	= 0;
    lstack[lSp].line_num        = LineNo[curfile];
    lstack[lSp].extra_data = 0;
/* May want description instead of itemize */
/* TXbitemize( e ); */
    TeXskipEnv( e, "list", 1 );
/* TXeitemize( e ); */
    itemizelevel--;
    lSp--;
    TXWriteStartNewLine( fpout );
}

void TeXsmall( TeXEntry *e )
{
    TeXskipEnv( e, "small", 1 );
}


void TXverb( TeXEntry *e )
{
    int  ch;
    int  match;
    char *p;

    if (DebugCommands) fprintf( stdout, "LaTeX verb command\n" );
    PUSHCURTOK;
    p = curtok;
    match = SCTxtGetChar( fpin[curfile] );
    if (match == EOF) return;
    while ( (ch = SCTxtGetChar( fpin[curfile] )) != match ) {
	*p++ = ch;
    }
    *p++ = 0;
    TeXoutstr( fpout, curtok );
    POPCURTOK;
}

void TeXverbatim( TeXEntry *e )
{
    /* char comment_char; */
/* Skip code until find an \end{verbatim}.
   In verbatim mode, ignore any \ EXCEPT \end{verbatim} (How?) */
    InVerbatim = 1;
    TXWriteStartNewLine( fpout );
    TXpreformated( fpout, 1 );
    TXbgroup( e );
    TXfont_tt( e );
    lstack[++lSp].env	    = TXVERBATIM;
    lstack[lSp].newline	    = TXWriteStartNewLine;
    lstack[lSp].label_node_name = 0;
    lstack[lSp].label_text	    = 0;
    lstack[lSp].line_num    = LineNo[curfile];
    lstack[lSp].extra_data = 0;

/* Verbatim has no comments! */
/*
  comment_char = SCGetCommentChar();
  SCSetCommentChar( (char)0 );
  */
    /* Skip newlines at the head of the verbatim.  This isn't precisely
       correct; I think that it should skip at most one.
       SCSkipNewlines2 is better about this. */
    SCSkipNewlines2( fpin[curfile] );

    TeXskipEnv( e, "verbatim", 1 );
/*
   SCSetCommentChar( comment_char );
   */
    TXegroup( e );
    TXpreformated( fpout, 0 );
    lSp--;
    InVerbatim = 0;
}

/*
void TeXCenter( TeXEntry *e )
{
    TXbcenter( fpout );
    TeXskipEnv( e, "center", 0 );
    TXecenter( fpout );
}

void TeXCenterline( TeXEntry *e )
{
    TXbcenter( fpout );
    TXasisGrouped( e );
    TXecenter( fpout );
}
 */
/* ----------------------------------------------------------------------- */
/*
   There should really be a generic
   TeXSimpleEnv(
     startfcn( FILE *, TeXEntry * ), "name", endfcn( FILE *, TeXEntry * ) )
   for things like center and quote, as an improvement over TeXBenign

   Then these can be put into the basedefs by having the commands for start
   and end in the basedefs file and stored in the TeXEntry (which could
   store the functions themselves)
 */
void TXbegin( TeXEntry *e )
{
/* Process the beginning of an environment */
    char *p;
    char *btext, *etext;
    int  nargs;

/*    (look for tex, enumerate, description, example, iftex, etc ) */
    if (DebugCommands)
	fprintf( stdout, "Getting argument for begin{}\n" );
    PUSHCURTOK;
    TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN,
		   "TXbegin argument", e->name );
    if (strcmp( curtok, "iftex" ) == 0) {
	if (UseIfTex) TeXBenign( e, "iftex" );
	else          TeXiftex( e );
    }
    else if (strcmp( curtok, "tex" ) == 0) TeXtex( e );
    /* MPI reports use example as a variation of theorem, not verbatim,
       as LatexInfo does.  We need to configure this somehow...
       In fact, we need to label it as an Example, with example numbers...
   */
    else if (UsingLatexinfo && strcmp( curtok, "example" ) == 0) TeXexample( e );
    else if (strcmp( curtok, "verbatim" ) == 0) TeXverbatim( e );
    else if (strcmp( curtok, "menu" ) == 0) TeXmenu( e );
    else if (strcmp( curtok, "ifinfo" ) == 0) {
	if (UseIfTex) TeXskipEnv( e, "ifinfo", 0 );
	else          TeXBenign( e, "ifinfo" );
    }
/* There should be a way to specify these in the basedef file */
    /*    else if (strcmp( curtok, "center" ) == 0) TeXCenter( e ); */
    else if (strcmp( curtok, "benign" ) == 0) TeXBenign( e, "benign" );
    else if (strcmp( curtok, "displaymath") == 0 && !UsingLatexinfo) {
	if (LatexMath) {
	    TXProcessDollar( e, LatexMath, 0 );
	}
	else
	    TeXBenign( e, "displaymath" );
    }
#ifdef FOO
    else if (strcmp( curtok, "centering" ) == 0) TeXBenign( e, "centering" );
    else if (strcmp( curtok, "normalsize" ) == 0) TeXBenign( e, "normalsize" );
    else if (strcmp( curtok, "abstract" ) == 0) TeXBenign( e, "abstract" );
    else if (strcmp( curtok, "raggedright" ) == 0) TeXBenign( e, "raggedright" );
    else if (strcmp( curtok, "flushleft" ) == 0) TeXBenign( e, "flushleft" );
    else if (strcmp( curtok, "sloppypar" ) == 0) TeXBenign( e, "sloppypar" );
    else if (strcmp( curtok, "quote" ) == 0) TeXBenign( e, "quote" );
#endif
    else if (strcmp( curtok, "slide" ) == 0) {
	/* Strip the color arg */
	TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN,
		       "TXbegin slide", e->name );
	/* Pick the equivalent slide format */
	TXvt( e );
	/* Need a new page ... */
	TeXBenign( e, "slide" );
    }
#ifdef FOO
    else if (strcmp( curtok, "quotation" ) == 0) {
	TXWritePar( fpout );
	TeXBenign( e, "quotation" );
	TXWritePar( fpout );
    }
#endif
    else if (strcmp( curtok, "figure" ) == 0) {
	/* Remove optional positioning */
	TXRemoveOptionalArg( curtok );
	NumberedEnvironmentType = ENV_FIGURE;
	TeXBenign( e, "figure" );
	NumberedEnvironmentType = ENV_NONE;
    }
    else if (strcmp( curtok, "thebibliography" ) == 0) {
	/* Eat the next argument */
	TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN,
		       "TXbegin thebibliography", e->name );
	/* Need to act as if "Section{Bibliography}" seen */
	SCPushToken( "{Bibliography}" );
	e->ctx = (void*)(PTRINT)MinSectionKind;
	TXsection( e );
	TeXBenign( e, "thebibliography" );
    }
    /* displaymath should be like \[ ... \] */
    /* math should be like \( ... \) */
    /* Note that in LaTeX, \begin{foo}...\end{foo} is just like (almost)
       \foo ... \endfoo, and \displaymath=\[, \enddisplaymath=\] */
    else if (strcmp( curtok, "theindex" ) == 0) {
	/* An index done with the regular system is useless, since the names
	   have pages.  Instead, we skip the index entirely, then dump
	   the results of our own use of \index */
	SCPushToken( "{Index}" );
	e->ctx = (void*)(PTRINT)MinSectionKind;
	TXsection( e );
	TeXskipEnv( e, "theindex", 0 );
	WriteIndex( fpout, 2 );
    }
    else if (UserIndexName && strcmp( curtok, UserIndexName ) == 0) {
	/* An index done with the regular system is useless, since the names
	   have pages.  Instead, we skip the index entirely, then dump
	   the results of our own use of \index */
	SCPushToken( "{Index}" );
	e->ctx = (void*)(PTRINT)MinSectionKind;
	TXsection( e );
	TeXskipEnv( e, UserIndexName, 0 );
	WriteIndex( fpout, 2 );
    }
    else if (!LatexTables && (strcmp( curtok, "table" ) == 0)) {
	/* Remove optional positioning */
	TXRemoveOptionalArg( curtok );
	NumberedEnvironmentType = ENV_TABLE;
	TeXBenign( e, "table" );
	NumberedEnvironmentType = ENV_NONE;
    }
    else if (strcmp( curtok, "small" ) == 0) TeXsmall( e );
#ifdef FOO
    else if (strcmp( curtok, "tiny" ) == 0) TeXBenign( e, "tiny" );
#endif
    else if (!LatexTables && (strcmp( curtok, "tabular" ) == 0))
	if (HandleAlign) {
	    lstack[++lSp].env = TXTABULAR;
	    lstack[lSp].newline = TeXoutNewline;
	    lstack[lSp].line_num = LineNo[curfile];
	    lstack[lSp].extra_data = 0;
	    TeXGetTabularDefn();
	    TeXBeginHalignTable();
	    TeXBenign( e, "tabular" );
	    TeXEndHalignTable();
	    --lSp;
	}
	else {
	    TeXtabular( e );
	}
    else if (strcmp( curtok, "document" ) == 0) {
	InDocument = 1;
	TXStartDoc(1);
	TXInitialCommands(); /* Push back any initial commands */
	TeXBenign( e, "document" );
	InDocument = 0;
	TXStartDoc(0);
    }
    else if (strcmp( curtok, "description" ) == 0) TeXdescription( e );
    else if (strcmp( curtok, "itemize" ) == 0) TeXitemize( e );
    else if (strcmp( curtok, "enumerate" ) == 0) TeXenumerate( e );
    else if (strcmp( curtok, "list" ) == 0) {
	char *itemtext, *itemcommands;
	/* 2 arguments; first is text for \item, second is code executed for
	   each item */
	/* fprintf( stdout,"Ignoring details list environment defn for now\n" ); */
	/* Note that we need to allocate the itemtext and itemcommands,
	   since list environments may be nested */
	ALLOC_TOKEN(itemtext);
	ALLOC_TOKEN(itemcommands);
	/*
	  itemtext     = (char *)MALLOC( MAX_TOKEN );    CHKPTR(itemtext);
	  itemcommands = (char *)MALLOC( MAX_TOKEN );    CHKPTR(itemcommands);
	  */
	TeXMustGetArg( fpin[curfile], itemtext, MAX_TOKEN,
		       "TXbegin list itemtext", e->name );
	TeXMustGetArg( fpin[curfile], itemcommands, MAX_TOKEN,
		       "TXbegin list itemcommands", e->name );
	TeXDoList( e, itemtext, itemcommands );
	/*
	  FREE( itemtext );
	  FREE( itemcommands );
	  */
	FREE_TOKEN(itemcommands);
	FREE_TOKEN(itemtext);
    }
    else if (LookupEnv( curtok, &btext, &etext, &nargs)) {
	/* Really needs to look for arguments; use def's pushback with eval */
	if (DebugCommands) {
	    fprintf( stdout, "Processing user-defined environment %s\n", curtok );
	    fprintf( stdout, "defined as begin:|%s|\nend:|%s|\n",
		     btext ? btext : "<NULL>", etext ? etext : "<NULL>" );
	}
	PushBeginEnv( btext, nargs );
	/* TeXBenign( e, curtok ); */
    }
/*
  else if () {
  This code should look up the name \"curtok", if defined, then do that.
  The corresponding end code should look up \end"curtok".  This can
  be used to handle things like \begin{em} ... \end{em}, which has
  the effect of {\em ...}  .  We might be able to integrate this
  with lookupenv...
  }
  */
    else {
	p = STRDUP( curtok );  CHKPTR(p);
	/* Identify environment as table, figure, equation, or none */
	if (strcmp( p, "table" ) == 0) {
	    NumberedEnvironmentType = ENV_TABLE;
	}
	else if (strcmp( p, "figure" ) == 0) {
	    NumberedEnvironmentType = ENV_FIGURE;
	}
	else if (strcmp( p, "equation" ) == 0 ||
		 strcmp( p, "eqnarray" ) == 0) {
	    NumberedEnvironmentType = ENV_EQUATION;
	    TeXSetEnvJump( "Equation" );
	    TXcaptionHandling( e );
	}
	if (LatexUnknownEnvs ||
	    (LatexTables &&
	     (strcmp( curtok, "tabular" ) == 0 ||
	      strcmp( curtok, "table" ) == 0))) {
	    char bname[100];
	    char fname[100];

	    if (DebugCommands) {
		fprintf( stdout, "Using Latex to process an environment\n" );
	    }
	    snprintf( bname, sizeof(bname), "%s%d", imgfilebase, imageno++ );
	    strcpy( fname, bname );
	    strcat( fname, "." );
	    strcat( fname, ImageExt );
	    if (LatexAgain || !FileExists( fname )) {
		RunLatex( curtok, (char *)0, bname, (char *)0, ImageExt, 1 );
	    }
	    else if (FileExists( fname ) && !LatexAgain) {
		RunLatex( curtok, (char *)0, bname, (char *)0, ImageExt, 0 );
	    }
	    /* If this is a figure or table environment, generate a target
	       location for the image */
	    if (NumberedEnvironmentType != ENV_NONE && envjumpname &&
		envjumpname[0])
		TXAnchoredImage( e, envjumpname, fname );
	    else
		TXimage( e, fname );
	}
	else {
	    if (!AmSkipping) /* &&  InDocument) */ {
		fprintf( ferr, "Unknown environment %s, %s line %d\n",
			 curtok,
			 InFName[curfile] ? InFName[curfile]: "",
			 LineNo[curfile] );
	    }
	    TeXBenign( e, p );
	    if (!AmSkipping) /* &&  InDocument) */ {
		fprintf( ferr, "End of unknown environment %s, %s line %d\n",
			 curtok,
			 InFName[curfile] ? InFName[curfile]: "",
			 LineNo[curfile] );
	    }
	}
	NumberedEnvironmentType = ENV_NONE;
	FREE( p );
    }
    POPCURTOK;
}

void TXend( TeXEntry *e )
{
    PUSHCURTOK;
    TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN, "TXend", e->name );
    /* Finish processing of an environment */
    if (lstack[lSp].env == TXTABULAR) {
	if (strcmp( "tabular", curtok ) != 0) {
	    fprintf( stderr, "Saw end{%s} but expected tabular\n", curtok );
	}
	else {
	    TeXEndHalignTable();
	    FREE( lstack[lSp].extra_data );
	    lSp--;
	}
    }
    POPCURTOK;
}

void TXitem( TeXEntry *e )
{
    char buf[12];
/* Process an item based on the current environment */
    TXWriteStartItem( fpout );
    if (lstack[lSp].env == TXDESCRIPTION) {
	TXbdesItem( e );
	TXbgroup( e );
	TXbf( e );
	/* Defer reading option until here in case it causes direct output (for
	   example, a pointer reference) */
	if (DebugCommands)
	    fprintf( stdout, "Getting argument for item in description\n" );
	PUSHCURTOK;
	strncpy( CmdName, e->name, sizeof(CmdName)-1 );
	if (TeXGetGenArg( fpin[curfile], curtok, MAX_TOKEN, '[', ']', 1 ) != 1) {
	  /* It is legal to forget the [], but probably unintended */
	  fprintf( stdout,
		   "Missing [] in description item in File %s line %d\n",
		   InFName[curfile] ? InFName[curfile]: "", LineNo[curfile] );
	  strcpy( curtok, " " );
	  /* TeXAbort( "TXbegin description", e->name ); */
	}
	CmdName[0] = 0;
	/* FIXME: Need to process the curtok for commands */
#if 0
	TeXoutstr( fpout, curtok );
	TXegroup( e );
	TXedesItem( e );
#else
	SCPushCommand("<dd>\n");
	/* Not quite right - need an endgroup, or need to change */
	BraceCount++;
	SCPushToken("}");
	SCPushToken(curtok);
#endif
	POPCURTOK;
    }
    else if (lstack[lSp].env == TXENUMERATE) {
      /* \item[] is legal in \enumerate */
      PUSHCURTOK;
      TXRemoveOptionalArg( curtok );
      POPCURTOK;
      /* We don't want to do this on the FIRST item in an enumerate */
      if (lstack[lSp].num > 1)
	TXWriteStartNewLine( fpout );
      sprintf( buf, "%d. ", lstack[lSp].num++ );
      TeXoutstr( fpout, buf );
    }
    else if (lstack[lSp].env == TXLIST) {
      /* We might want to allow bullet to be identified... */
      /* Remove any [] in the list argument.  Save the value */
      /* eventually, if the is a labellength, use an html table to
         force the correct width */
      PUSHCURTOK;
      curtok[0] = 0;
      TXRemoveOptionalArg( curtok );
      TXWriteStartNewLine( fpout );
      /* This really needs to process the string FIRST before outputting it... */
      if (curtok[0]) {
	SCPushToken( curtok );
      }
      else {
	/* p1 is the itemtext, which is the text used if there is no optional
	   argument */
	if (lstack[lSp].p1)
	  SCPushToken( lstack[lSp].p1 );
      }
      /* TeXoutstr( fpout, lstack[lSp].p1 ); */
      if (lstack[lSp].p2)
	SCPushToken( lstack[lSp].p2 );
      POPCURTOK;
    }
    else {
      /* Assume itemize for now.  This is incorrect for an index */
      TXbgroup( e );
      TXoutbullet( e );
      TXegroup( e );
    }
    SCSkipNewlines( fpin[curfile] );
}

/* Process index entries */
void TXcindex( TeXEntry *e )
{
    if (DebugCommands)
	fprintf( stdout, "Getting argument for %s\n", e->name );
    PUSHCURTOK;
    TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN, "TXcindex", e->name );
    AddToIndex( curtok, CurNodename, CurSeqnum - 1, 1 );
    POPCURTOK;
}

void TXfindex( TeXEntry *e )
{
    if (DebugCommands)
	fprintf( stdout, "Getting argument for %s\n", e->name );
    PUSHCURTOK;
    TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN, "TXfindex", e->name );
    AddToIndex( curtok, CurNodename, CurSeqnum - 1, 0 );
    POPCURTOK;
}

void TXindex( TeXEntry *e )
{
    if (DebugCommands)
	fprintf( stdout, "Getting argument for %s\n", e->name );
    PUSHCURTOK;
    TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN, "TXindex", e->name );
    AddToIndex( curtok, CurNodename, CurSeqnum - 1, 2 );
    POPCURTOK;
}

void TXprintindex( TeXEntry *e )
{
    int which;

    if (DebugCommands)
	fprintf( stdout, "Getting argument for %s\n", e->name );
    PUSHCURTOK;
    TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN, "TXprintindex", e->name );
    which = 0;
    if (strcmp( curtok, "cp" ) == 0) which = 1;
    WriteIndex( fpout, which );
    POPCURTOK;
}

void TXmakeindex( TeXEntry *e )
{
/* Does makeindex do some sort of heading? */
/* WriteIndex( fpout, 2 ); */
}

/* Process \documentstyle[]{}.  Note that we need to save the document
   style inorder to process code in latex mode */
char *preamble = 0;
char *predoc   = 0;
char *documentcmd = 0;

#ifndef MAX_PATH_LEN
#define MAX_PATH_LEN 1024
#endif
void TXLoadPackage( const char *p )
{
    /* First, try to find name.def in the definition list.  If
       found, load that */
    if (userpath[0]) {
	char filename[MAX_PATH_LEN];
	char fullfilename[MAX_PATH_LEN];

	strcpy( filename, p );
	strcat( filename, ".def" );
	if (SYGetFileFromPath( userpath, filename, "DOCTEXT_USERPATH",
			       fullfilename, 'r' )) {
	    RdBaseDef( fullfilename );
	    return;
	}
    }

    /* Otherwise, check against predefined commands */
    if (strcmp( p, "latexinfo" ) == 0 ||
	strcmp( p, "latexinfo-1.2") == 0)
	TXStyleLatexInfo( TeXlist, fpin[curfile], fpout );
    else if (strcmp( p, "epsf" ) == 0)
	TXStyleEPSF( TeXlist, fpin[curfile], fpout );
    else if (strcmp( p, "psfig" ) == 0) {
	TXStylePsfig( TeXlist, fpin[curfile], fpout );
	/* Must defined the ESPF style, since we currently use
	   them to implement psfig */
	TXStyleEPSF( TeXlist, fpin[curfile], fpout );
    }
    else if (strcmp( p, "graphics" ) == 0 ||
	     strcmp( p, "graphicx" ) == 0 ||
	     strcmp( p, "graphicsx" ) == 0) {
	TXInsertName( TeXlist, "includegraphics", TXincludegraphics,
		      1, (void *)0 );
    }
    else if (strcmp( p, "11pt" ) == 0 ||
	     strcmp( p, "12pt" ) == 0 ||
	     strcmp( p, "twoside" ) == 0 ||
	     strcmp( p, "fleqn" ) == 0)
	/* Standard packages that do not affect HTML formatting */
	    ;
    /* My private styles ... */
    else if (strcmp( p, "fileinclude" ) == 0)
	;
    else if (strcmp( p, "funclist" ) == 0)
	TXStyleFunclist( TeXlist, fpin[curfile], fpout );
    else if (strcmp( p, "tpage" ) == 0)
	TXStyleTpage( TeXlist, fpin[curfile], fpout );
    else if (strcmp( p, "tools" ) == 0)
	TXStyleTools( TeXlist, fpin[curfile], fpout );
    else if (strcmp( p, "anlhtext" ) == 0)
	TXStyleAnlhtext( TeXlist, fpin[curfile], fpout );
    else if (strcmp( p, "handpage" ) == 0)
	TXStyleANLHandpage( TeXlist, fpin[curfile], fpout );
    else if (strcmp( p, "urlsimple" ) == 0 ||
	     strcmp( p, "code" ) == 0) {
	/* Simple defines URL and AURL */
	;
    }
    else {
	fprintf( ferr,
"Unknown documentstyle or package %s; provide\n\
%s.def to define the commands\n", p, p );
	/* Question here is whether we should try to read the document
	   style file */
    }
}

/* Latex 2e specifies additional stuff using \usepackage, which may have
   a comma separate list of packages.  Format is
   \usepackage[optional fields]{package-name}
   Note that it is common for the optional fields to be across multiple
   lines.
 */
void TXusepackage( TeXEntry *e )
{
    char *p, *ptr, *p1;
    int hadOptional=0;

    PUSHCURTOK;
    strncpy( CmdName, e->name, sizeof(CmdName)-1 );
    TXRemoveOptionalArg(curtok);
    if (curtok[0]) {
	strcat(preamble, "\\usepackage[");
	strcat(preamble, curtok);
	strcat(preamble, "]");
	hadOptional = 1;
    }
    TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN, "TXusepackage", e->name );
    if (curtok[0]) {
	if (!hadOptional) {
	    strcat( preamble, "\\usepackage" );
	}
	strcat(preamble, "{");
	strcat( preamble, curtok );
	strcat( preamble, "}\n" );
    }
    if (1) {
	fprintf( stdout, "Procssing usepackage[...]{%s} in %s line %d\n",
		 curtok, InFName[curfile] ? InFName[curfile]: "",
		 LineNo[curfile] );
    }
    /* Now, get comma-separate list of packages and load each one */
    p = curtok;
    while (*p) {
	ptr = p;
	while (*ptr && *ptr != ',') ptr++;
	if (!*ptr) ptr[1] = 0; /* so the p = ptr + 1 below will exit */
	*ptr = 0; /* Change , to null */
	/* Skip over any path part of the specification */
	p1 = strrchr( p, '/' );
	if (p1) p = p1 + 1;
	TXLoadPackage( p );
	p = ptr + 1;
    }

    CmdName[0] = 0;
    POPCURTOK;
}

void TXdocumentstyle( TeXEntry *e )
{
    static char name[] = "documentstyle";
    char *p, *ptr, *p1;

    preamble    = (char *)MALLOC( 20000 ); CHKPTR(preamble);
    predoc	= (char *)MALLOC( 20000 ); CHKPTR(predoc);
    predoc[0]   = 0;
    preamble[0] = 0;
    if (!documentcmd) documentcmd = name;

    PUSHCURTOK;
    if (TeXGetGenArg( fpin[curfile], curtok, MAX_TOKEN, '[', ']', 1 ) == 1) {
      strcat( preamble, "[" );
      strcat( preamble, curtok );
      strcat( preamble, "]" );
      p = curtok;
      while (*p) {
	/* Look for known styles */
	ptr = p;
	while (*ptr && *ptr != ',') ptr++;
	if (!*ptr) ptr[1] = 0;   /* so the p = ptr + 1 below will exit */
	*ptr = 0;
	/* Skip over any path part of the specification */
	p1 = p;
	while (*p1) {
	  if (*p1 == '/') p = p1 + 1;
	  p1++;
	}
	TXLoadPackage( p );
	p = ptr + 1;
      }
    }
    TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN,
		   "TXdocumentstyle", (char *)0 );
    strcat( preamble, "{" );
    strcat( preamble, curtok );
    strcat( preamble, "}\n" );
    p = strrchr( curtok, '/' );
    if (!p) p = curtok;
    else p++;
    if (strcmp( p, "linfoem" ) == 0) {
	TXStyleLatexInfo( TeXlist, fpin[curfile], fpout );
    }
    POPCURTOK;
}

void TXdocumentclass( TeXEntry *e )
{
    static char name[] = "documentclass";
    TXdocumentstyle( e );
    documentcmd = name;
}

/* Code for the title etc */
#define MAX_TITLE 1000
#define MAX_AUTHOR 1000
static char TitleString[MAX_TITLE], AuthorString[MAX_AUTHOR];
void TXtitle( TeXEntry *e )
{
    if (DebugCommands)
	fprintf( stdout, "Getting argument for %s\n", e->name );
    TeXMustGetArg( fpin[curfile], TitleString, MAX_TITLE, "TXtitle", e->name );
}

void TXauthor( TeXEntry *e )
{
    if (DebugCommands)
	fprintf( stdout, "Getting argument for %s\n", e->name );
    TeXMustGetArg( fpin[curfile], AuthorString, MAX_AUTHOR,
		   "TXauthor", e->name );
}
void TXdate( TeXEntry *e )
{
    if (DebugCommands)
	fprintf( stdout, "Getting argument for %s\n", e->name );
    PUSHCURTOK;
    TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN, "TXdata", e->name );
    POPCURTOK;
}
void TeXmaketitle( e )
TeXEntry *e;
{
    TXmaketitle( e, TitleString, AuthorString );
}

void TXDef( TeXEntry *e )
{
    TXAddUserDef( TeXlist, e );
}
void TXNewCommand( TeXEntry *e )
{
    TXDoNewCommand( TeXlist, e );
}

void TXNewLength( TeXEntry *e )
{
    TXDoNewLength( TeXlist, e );
}

void TXURL( TeXEntry *e )
{
    PUSHCURTOK;
    TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN, "TXURL", e->name );
    TXWriteHyperLink( fpout, curtok, curtok, 0 );
    POPCURTOK;
}

void TXAURL( TeXEntry *e )
{
    char nametok[512];

    PUSHCURTOK;
    TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN, "TXAURL", e->name );
    TeXMustGetArg( fpin[curfile], nametok, MAX_TOKEN, "TXAURL", e->name );
    TXWriteHyperLink( fpout, nametok, curtok, 0 );
    POPCURTOK;
}

void TXcite( TeXEntry *e )
{
    int  urltype;
    char *url, *text;
    char *p, *pn;

    if (DebugCommands)
	fprintf( stdout, "Getting argument for %s\n", e->name );
    PUSHCURTOK;
    TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN, "TXcite", e->name );
/* Must handle the case of citations separated by commas */
    p = curtok;
    pn = 0;
    TeXoutstr( fpout, CitePrefix );
    while (p) {
	pn = strchr( p, ',' );
	if (pn) {
	    *pn = 0;
	    pn++;
	}
	if (RefMapLookup( "cite", p, &url, &urltype, &text )) {
	    /* Found a hypertext reference */
	    TXWriteHyperLink( fpout, text, url, urltype );
	}
	else if (BibLookup( p, &url, &text )) {
	    /* code to handle a jump to the destination name returned */
	    TXWriteHyperLink( fpout, text, url, urltype );
	}
	else {
	    fprintf( fpout, "(ref %s)", p );
	    fprintf( ferr, "citation to %s has no hyperlink\n", p );
	}
	p = pn;
	if (p)
	    TeXoutstr( fpout, "," );
    }
    TeXoutstr( fpout, CiteSuffix );

/* \hcitea has a second argument that is used in the LaTeX version as the
   replacement text */
    if (e->nargs > 1) {
	TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN, "TXhcite", e->name );
    }
    POPCURTOK;
}

/*
 * Process catcode commands
 * \catcode<char>=code or \catcode`\<char>=code or \catcode`<char>=code
 *
 * Note that since a typical use is
 *  \bgroup\catcode....   \egroup
 * we need to implement the full stack semantics of TeX scoping when
 * processing groups
 */
void TXcatcode( TeXEntry *e )
{
    char c, codetoken[21];
    int  code, nsp;

    if (IgnoreCatcode) return;

    /* Read char */
    c = SCTxtGetChar( fpin[curfile] );
    if (c == '`') {
	c = SCTxtGetChar( fpin[curfile] );
	if (c == '\\') {
	    c = SCTxtGetChar( fpin[curfile] );
	}
    }
    /* Read code */
    c = SCTxtGetChar( fpin[curfile] );
    if (c != '=') {
	fprintf( stderr, "Missing = in \\catcode command, %s line %d\n",
		 InFName[curfile] ? InFName[curfile]: "",
		 LineNo[curfile] );
	return;
    }
    SCTxtFindNextANToken( fpin[curfile], codetoken, 20, &nsp );
    code = atoi(codetoken);

    /* Now, change the relevant character to the given class */
    switch (code) {
    case 1: /* begin group */
	break;
    case 2: /* end group */
	break;
    case 3: /* math shift */
	break;
    case 4: /* alignment tab */
	break;
    case 5: /* end of line */
	break;
    case 6: /* macro parameter character */
	break;
    case 7: /* superscript */
	break;
    case 8: /* subscript */
	break;
    case 9: /* Null */
	break;
    case 10: /* Blank space */
	break;
    case 11: /* Letters */
	break;
    case 12: /* Other */
	break;
    case 13: /* active */
	break;
    case 14: /* comment */
	break;
    case 15: /* invalid */
	break;
    default:
	fprintf( stderr,
		 "Unrecognized code %d for %c in \\catcode command, %s line %d\n",
		 code, c,
		 InFName[curfile] ? InFName[curfile]: "",
		 LineNo[curfile] );
    }
    return;
}

/*
   This is a version of "output token" that makes a final check for a known
   name.  If found, the hyperlink in inserted in its place
 */
static char activetok[MAX_TOKEN];
static int lasttok = 0;
static int breakchar[256];
void TXinitbreaktable( void )
{
    int i;
    for (i=0; i<256; i++) {
	breakchar[i] = 0;
	if (isalnum((char)(i))) breakchar[i] = 1;
    }
/* Add underscore */
    breakchar['_'] = 1;
}

void TXoutactiveToken( const char *token )
{
    int    urltype;
    char   *url, *text;

/*
 * First, tokenize the output and check each token.  Special case:
 * if we reach the end of the string without a "break" character, we must
 * delay processing until we get the break character, since the token may
 * be incomplete (for example, MPI_Send may be delivered to this
 * routine as MPI, then _, then Send.
 *
 * We must also be careful to skip over commands (TOK_START to TOK_END).
 */
    while (*token) {
	/* This really needs to tokenize the string */
	if (breakchar[(int)*token]) {
	    /* The character is a "alphanum" */
	    while (*token && breakchar[(int)*token]) {
		activetok[lasttok++] = *token++;
		if (lasttok >= MAX_TOKEN) {
		    activetok[MAX_TOKEN-1] = 0;
		    fprintf( stderr, "Token too long (%s)=n", activetok );
		    return;
		}
	    }
	    activetok[lasttok] = 0;
	    /* If we ended at a alnum-like token, don't output yet. */
	    if (*token == 0) break;
	}
	else {
	    /* If there is already something in the token buffer,
	       drain it first */
	    if (lasttok == 0) {
		/* Skip over the non-alphanum characters */
		if ((unsigned char)(*token) == TOK_START &&
		    lasttok < MAX_TOKEN) {
		    while (*token && (unsigned char)(*token) != TOK_END) {
			activetok[lasttok++] = *token++;
		    }
		}
		else {
		    while (*token && !breakchar[(int)*token] &&
			   (unsigned char )(*token) != TOK_START &&
			   lasttok < MAX_TOKEN) {
			activetok[lasttok++] = *token++;
		    }
		}
		if (lasttok >= MAX_TOKEN) {
		    activetok[MAX_TOKEN-1] = 0;
		    fprintf( stderr, "Token too long (%s)=n", activetok );
		    return;
		}
		activetok[lasttok] = 0;
	    }
	}
	if (DoActiveTokens &&
	    RefMapLookup( "man", activetok, &url, &urltype, &text )) {
	    TXWriteHyperLink( fpout, text, url, urltype );
	}
	else {
	    WriteString( fpout, activetok );
	}
	/* We've flushed the token */
	lasttok = 0;
    }
}

/*
 * Process a Postscript or Encapsulated Postscript image.
 */
static char imagefile[256];
static char imgoutfile[256];
static int AlwaysMakeNewGif = 0;
static int DebugImages = 1;
void TXepsfbox( TeXEntry *e )
{
    char *p;
    FILE *fp;
    char fname[256];
    int  found_gif;

    if (DebugCommands)
	fprintf( stdout, "Getting argument for %s\n", e->name );
    PUSHCURTOK;
    TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN, "TXepsfbox", e->name );

/* Save the filename */
    strncpy( imagefile, curtok, sizeof(imagefile) );

/* Modify curtok by changing .ps or .eps to .gif */
    p = curtok + strlen(curtok) - 1;
    while (p > curtok && *p != '.') p--;
    strcpy( p+1, "gif" );

/* out file is the file without the directory */
    p = curtok;
    strncpy( imgoutfile, curtok, sizeof(imgoutfile) );
    while (*p) {
	if (p[0] == '/') strncpy( imgoutfile, p + 1, sizeof(imgoutfile) );
	p++;
    }
/* Here are the file names:
   imagefile - original source file name
   curtok    - the same as imagefile, but with .gif extension
   imgoutfile - the same as curtok, but without the directories
   fname      - outputdirectory/imgoutfile

   i.e., if imagefile = /home/foo/bar.eps, then
            curtok    = /home/foo/bar.gif
            imgoutfile = bar.gif

   Here are the rules for deciding whether to use an existing filename
   if (!AlwaysMakeNewGif)
       if (destdir/imgoutfile exists and is newer than imagefile)
            use destdir/imgoutfile (nothing to do)
       else if (curtok exists and is newer than imagefile)
            copy curtok to destdir
   if (no file yet, then try to generate one)
 */

    /* Create the final output file name */
    if (splitlevel >=0)
	sprintf( fname, "%s/%s", splitdir, imgoutfile );
    else
	sprintf( fname, "./%s", imgoutfile );
    found_gif = 0;
    if (!AlwaysMakeNewGif) {
	if (DebugImages) {
	    printf( "Checking for file %s (%s)\n",
   	      fname, SYiFileExists( fname, 'r' ) ? "Exists" : "Missing" );
	    printf( "%s is %s than %s\n", fname,
		    SYFileNewer( fname, imagefile ) ? "newer" : "older",
		    imagefile );
	}
	if (SYiFileExists( fname, 'r' ) && SYFileNewer( fname, imagefile )) {
	    /* The file we need is already where we need it */
	    if (DebugImages) printf( "Found gif file %s\n", imagefile );
	    found_gif = 1;
	}
	else {
	    if (DebugImages) {
		printf( "Check for file %s (%s)\n",
   	      curtok, SYiFileExists( curtok, 'r' ) ? "Exists" : "Missing" );
		printf( "%s is %s than %s\n", curtok,
		    SYFileNewer( curtok, imagefile ) ? "newer" : "older",
			imagefile );
	    }
	    if (SYiFileExists( curtok, 'r' ) &&
		 SYFileNewer( curtok, imagefile )) {
		char tmpbuf[1024];
		if (DebugImages) printf( "Using %s for image\n", curtok );
		/* GIF version of file is in same directory as source file */
		sprintf( tmpbuf, "/bin/cp -f %s %s", curtok, fname );
		system( tmpbuf );
		found_gif = 1;
	    }
	}
    }
    if (found_gif) {
	if (DebugImages) printf( "Using %s\n", imgoutfile );
	TXimage( e, imgoutfile );
    }
    else {
#ifdef OLDEPSF
/* See if the file exists */
    if (splitlevel >= 0) {
	/* first, is the file in the destination directory ? */
	sprintf( fname, "%s/%s", splitdir, imgoutfile );
	fp = fopen( fname, "r" );
	if (!fp) {
	    /* Is it in the CURRENT directory */
	    fp = fopen( imgoutfile, "r" );
	    if (fp) {
		sprintf( fname, "/bin/cp -f %s %s", imgoutfile, splitdir );
		system( fname );
	    }
	}
    }
    else {
	fp = fopen( imgoutfile, "r" );
    }
    if (fp) {
	fclose(fp);
	TXimage( e, imgoutfile );
    }
    else {
#endif
	/* Try to generate it on the fly */
#ifndef __MSDOS__
	char pgm[1024];

	if (DebugImages) printf( "Attempting to create gif image file\n" );
	fp = fopen( imagefile, "r" );
	if (fp) {
	    fclose( fp );
	    sprintf( pgm, "%spstogif %s %s figure > /dev/null",
		     PSPATH, imagefile, imgoutfile );
	    system( pgm );
	    /* The output of this is imgoutfile, not curtok */
	    /* (was fopen( curtok, "r" )) */
	    fp = fopen( imgoutfile, "r" );
	    if (fp) {
		fclose( fp );
		if (splitlevel >= 0) {
		    sprintf( pgm, "/bin/mv %s %s", imgoutfile, splitdir );
		    system( pgm );
		}
		TXimage( e, imgoutfile );
		POPCURTOK;
		return;
	    }
	}
#endif
	fprintf( stderr, "No file %s available for image\n", curtok );
	fprintf( stderr, "(Could not open %s)\n", imagefile );
	TXbgroup( e );
	TXbf( e );
	TeXoutstr( fpout, "Image file " );
	TeXoutstr( fpout, imgoutfile );
	TeXoutstr( fpout, " to be provided..." );
	TXegroup( e );
	TXWriteStartNewLine( fpout );
    }
    POPCURTOK;
}

/* psfig is like epsfbox, but with a different syntax */
/* It also supports things that epsf doesn't, like angle commands */
/* What we need to do is this:
   Find file= for figure= for the filename
   Find any angle= , bbllx=, bblly=, bburx=, bbury= commands
   (there are still others but these will do)
   If angle is not 0, then create a new file with
   angle rotate
   dx dy translate
   as the first commands, where
   dx and dy are choosen to make the picture fit in positive coords.
   Note that the new bounding box, after rotation, has
   corners with coords (x cos angle - y sin angle, y sin angle + y cos angle)
   (corner at (x,y) before rotation.
   Take the min x of these, and set dx = -(min x)
   Set dx as -(min y)
   (adjust if the bounding box doesn't start at (0,0).
 */

void TXpsfig( TeXEntry *e )
{
    char *p, *fname;

    if (DebugCommands)
	fprintf( stdout, "Getting argument for %s\n", e->name );
    PUSHCURTOK;
    TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN, "TXpsfig", e->name );

    p = curtok;
    /* Look for file= and figure= */
    /* Should look for xxxx= and then parse the xxxx */
    while (p && strncmp( p, "figure=", 7 ) != 0 &&
	   strncmp( p, "file=", 5 ) != 0) {
	p = strchr( p, ',' );
	if (p) p++;
    }
    if (p) {
	/* get to the equal */
	p = strchr( p, '=' ) + 1;
	fname = p;
	p = strchr( p, ',' );
	if (p) *p = 0;
	SCPushToken( "}" );
	SCPushToken( fname );
	SCPushToken( "\\epsfbox{" );
    }
    else {
	fprintf( ferr, "Could not find file name for \\psfig command\n" );
    }
    POPCURTOK;
}

/* FIXME: Add

   includegraphics[width=dim]{filename}

   File name may be missing the extension, in which case assume pdf
 */

void TXincludegraphics( TeXEntry *e )
{
    int  rc;

    /* See if there is an option argument (which we'll ignore for now) */
    PUSHCURTOK;
    if (TeXGetGenArg( fpin[curfile], curtok, MAX_TOKEN, '[', ']', 1 ) == 1) {
	/* Use this to process any arguments that we might need with
	   the picture */
#if 0
	static char name[] = "includegraphics";
	char *p, *ptr, *p1;
	p = curtok;
	while (*p) {
	    /* Look for known commands */
	    ptr = p;
	    while (*ptr && *ptr != ',') ptr++;
	    if (!*ptr) ptr[1] = 0;   /* so the p = ptr + 1 below will exit */
	    *ptr = 0;
	    /* Skip over any path part of the specification */
	    p1 = p;
	    while (*p1) {
		if (*p1 == '/') p = p1 + 1;
		p1++;
	    }
	    p = ptr + 1;
	}
#endif
    }
    /* Get the file name */
    TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN,
		   "TXincludegraphics", (char *)0 );
    /* Try to find the file */
    rc = SYFindFileWithExtension( curtok, "gif:eps:pnm:ps:pdf" );
    if (rc == 1) {
	/* We may need to convert the file to one that can be displayed */
	rc = TXConvertFigureToGIF( curtok );
	if (rc == 1) {
	    TXimage( 0, curtok );
	}
	else {
	    fprintf( stderr, "Unable to convert %s to .gif\n", curtok );
	}
    }
    else {
	fprintf( stderr, "Cannot find graphics file %s\n", curtok );
    }

    POPCURTOK;
}

/* Process an animation entry.  args are {mpg}{gif}{text} */

static char mpgtoken[256], giftoken[256];

void TXanimation( TeXEntry *e )
{
    TeXMustGetArg( fpin[curfile], mpgtoken, 256, "TXanimation", e->name );
    TeXMustGetArg( fpin[curfile], giftoken, 256, "TXanimation", e->name );
    PUSHCURTOK;
    TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN, "TXanimation", e->name );
    TXmovie( e, mpgtoken, giftoken, curtok );
    POPCURTOK;
    TXWriteStartNewLine( fpout );
}

/* Initialization code */
void TXInsertName( SRList *list, const char *name, void (*action)( TeXEntry *),
		   int nargs, void *ctx )
{
    LINK *l;
    int  d;
    TeXEntry *new;

    l           = SRInsert( list, name, (char *)0, &d );
    new         = NEW(TeXEntry);    CHKPTR(new);
    l->priv     = (void *)new;
    new->action = action;
    new->ctx    = ctx;
    new->nargs  = nargs;
    new->name   = l->topicname;
}

/*
   Initialize the TeX commands. This adds ONLY the ones that are
   not set by the basedefs.txt file.
 */
void TXInit( FILE *fin, FILE *fout )
{
    if (!TeXlist)
	TeXlist = SRCreate();

/* Insert the known LaTeXinfo commands into the table for processing */

    TXInsertName( TeXlist, "def", TXDef, 0, (void *)0 );
    /* gdef should eventually be different */
    TXInsertName( TeXlist, "gdef", TXDef, 0, (void *)0 );
    TXInsertName( TeXlist, "let", TXlet, 0, (void *)0 );
    TXInsertName( TeXlist, "newcommand", TXNewCommand, 0, (void *)0 );
    TXInsertName( TeXlist, "newlength", TXNewLength, 0, (void *)0 );
    TXInsertName( TeXlist, "renewcommand", TXNewCommand, 0, (void *)0 );
    TXInsertName( TeXlist, "newenvironment", TXDoNewenvironment, 0, (void *)0 );
    TXInsertName( TeXlist, "renewenvironment", TXDoNewenvironment, 0, (void *)0 );
    TXInsertName( TeXlist, "newtheorem", TXDoNewtheorem, 0, (void *)0 );
    TXInsertName( TeXlist, "renewtheorem", TXDoNewtheorem, 0, (void *)0 );

    TXInsertName( TeXlist, "obeylines", TXDoObeylines, 0, (void *)0 );

    /* LaTeX conditionals */
    TXInsertName( TeXlist, "newif", TXNewif, 0, (void *)0 );
    TXInsertName( TeXlist, "else", TXElse, 0, (void *)0 );
    TXInsertName( TeXlist, "fi", TXFi, 0, (void *)0 );

    /* Font controls.  Old forms first */
    TXInsertName( TeXlist, "rm", TXrm, 0, (void *)0 );
    TXInsertName( TeXlist, "bf", TXbf, 0, (void *)0 );
    TXInsertName( TeXlist, "em", TXem, 0, (void *)0 );
    TXInsertName( TeXlist, "it", TXem, 0, (void *)0 );
    TXInsertName( TeXlist, "tt", TXtt, 0, (void *)0 );
    TXInsertName( TeXlist, "sf", TXsf, 0, (void *)0 );

    TXInsertName( TeXlist, "label",  TXlabel, 1, (void *)0 );
    TXInsertName( TeXlist, "ref",  TXref, 1, (void *)0 );

    TXInsertName( TeXlist, "pageref",  TXref, 1, (void *)0 );
    TXInsertName( TeXlist, "documentstyle", TXdocumentstyle, 1, (void *)0 );
    TXInsertName( TeXlist, "documentclass", TXdocumentclass, 1, (void *)0 );
    TXInsertName( TeXlist, "usepackage", TXusepackage, 1, (void *)0 );
    TXInsertName( TeXlist, "title",  TXtitle, 1, (void *)0 );
    TXInsertName( TeXlist, "author",  TXauthor, 1, (void *)0 );
    TXInsertName( TeXlist, "date",  TXdate, 1, (void *)0 );
    TXInsertName( TeXlist, "bullet", TXoutbullet, 0, (void *)0 );

    TXInsertName( TeXlist, "index", TXindex, 1, (void *)0 );
    TXInsertName( TeXlist, "makeindex", TXmakeindex, 0, (void *)0 );

    /* These should eventually process the TeX stack */
    TXInsertName( TeXlist, "bgroup",     TXbgroup, 0, (void *)0 );
    TXInsertName( TeXlist, "egroup",     TXegroup, 0, (void *)0 );

    TXInsertName( TeXlist, "[",     TXmath, 0, (void *)0 );
    TXInsertName( TeXlist, "]",     TXmathend, 0, (void *)0 );
    if (LatexUnknownEnvs || LatexMath) {
	/* Need to change this... */
	TXInsertName( TeXlist, "(",     TXmathmode, 0, (void *)0 );
	TXInsertName( TeXlist, ")",     TXnop, 0, (void *)0 );
	TXInsertName( TeXlist, "[",     TXmathmode, 0, (void *)0 );
	/* "]" is handled by TXmathmode... */
	TXInsertName( TeXlist, "]",     TXnop, 0, (void *)0 );
    }
    else {
	TXInsertName( TeXlist, "(",     TXinlinemath, 0, (void *)0 );
	TXInsertName( TeXlist, ")",     TXinlinemathend, 0, (void *)0 );
	TXInsertName( TeXlist, "[",     TXmath, 0, (void *)0 );
	TXInsertName( TeXlist, "]",     TXmathend, 0, (void *)0 );
    }
    TXInsertName( TeXlist, "cite",  TXcite, 0, (void *)0 );
/* hcite is like cite, but it doesn't show up in LaTeX slides done with
   "header" */
    TXInsertName( TeXlist, "hcite",  TXcite, 1, (void *)0 );
    TXInsertName( TeXlist, "hcitea",  TXcite, 2, (void *)0 );
    TXInsertName( TeXlist, "maketitle", TeXmaketitle, 0, (void *)0 );
    TXInsertName( TeXlist, "include", TXinclude, 1, (void *)0 );
    TXInsertName( TeXlist, "input", TXinclude, 1, (void *)0 );
    TXInsertName( TeXlist, "IfFileExists", TXIfFileExists, 3, (void *)0 );
    TXInsertName( TeXlist, "char", TXchar, 0, (void *)0 );
    TXInsertName( TeXlist, "bibitem", TXbibitem, 1, (void *)0 );

/* These two are specific to latexinfo, but I'm using these in other
   situations */
    TXInsertName( TeXlist, "code", TXcode, 1, (void *)0 );
    TXInsertName( TeXlist, "verb", TXverb, 0, (void *)0 );
    TXInsertName( TeXlist, "file", TXfile, 1, (void *)0 );

/* Question: should part be 0, chapter 1, etc? */
    TXInsertName( TeXlist, "part", TXsection, 1, (void *)0 );
    TXInsertName( TeXlist, "chapter", TXsection, 1, (void *)0 );
    TXInsertName( TeXlist, "section", TXsection, 1, (void *)1 );
    TXInsertName( TeXlist, "subsection", TXsection, 1, (void *)2 );
    TXInsertName( TeXlist, "subsubsection", TXsection, 1, (void *)3 );
    TXInsertName( TeXlist, "subsubsubsection", TXsection, 1, (void *)4 );

/* Paragraph and subparagraph are sort of like sections, but they
   should not generate contents or links */
    TXInsertName( TeXlist, "paragraph", TXparagraph, 1, (void *)0 );
    TXInsertName( TeXlist, "subparagraph", TXparagraph, 1, (void *)1 );

/*
   TXInsertName( TeXlist, "paragraph", TXsection, 1, (void *)4 );
   TXInsertName( TeXlist, "subparagraph", TXsection, 1, (void *)5 );
   */

/* TeX commands to change what is allowed in a command name */
    TXInsertName( TeXlist, "makeatletter", TXatletter, 0, (void *)0 );
    TXInsertName( TeXlist, "makeatother",  TXatother,  0, (void *)0 );

    TXInsertName( TeXlist, "advance", TXadvance, 0, (void *)0 );

/* TeX commands that take numbers that should be ignored */
    TXInsertName( TeXlist, "penalty", TXnumber, 0, (void *)0 );
    TXInsertName( TeXlist, "hangafter", TXcounter, 0, (void *)0 );

    TXInsertName( TeXlist, "begin", TXbegin, 1, (void *)0 );
    TXInsertName( TeXlist, "end", TXend, 1, (void *)0 );
    TXInsertName( TeXlist, "item", TXitem, 1, (void *)0 );
    /* TXInsertName( TeXlist, "bitmap", TXbitmap, 1, (void *)0 ); */
    TXInsertName( TeXlist, "\\", TXdoublebw, 0, (void *)0 );
    TXInsertName( TeXlist, "newline", TXnewline, 0, (void *)0 );
/* Note that a \linebreak can have an optional argument (the desirability
   of a line break) */
    TXInsertName( TeXlist, "linebreak", TXnewline, 0, (void *)0 );
    TXInsertName( TeXlist, "bw", TXbw, 0, (void *)0 );
    TXInsertName( TeXlist, "back", TXbw, 0, (void *)0 );

/* \- is usually a discretionary hyphen */
/* setlength needs to eat the first argument without evaluation;
   we don't do that yet. */
    TXInsertName( TeXlist, "setlength", TXnop, 2, (void *)0 );
    TXInsertName( TeXlist, "bibliography", TXDoBibliography, 1, (void *)0 );
    TXInsertName( TeXlist, "cleardoublepage", TXnop, 0, (void *)0 );
    TXInsertName( TeXlist, "today", TXtoday, 0, (void *)0 );
    TXInsertName( TeXlist, "vspace", TXnopStar, 1, (void *)0 );
    TXInsertName( TeXlist, "hspace", TXnopStar, 1, (void *)0 );

/* Be prepared for center environment */
    /*     TXInsertName( TeXlist, "centerline", TeXCenterline, 1, (void *)0 );*/
/* Just copy the arguments */
    TXInsertName( TeXlist, "caption", TXcaption, 1, (void *)0 );

/* Boxes need to look for "to size" arguments ????????? */
    TXInsertName( TeXlist, "hbox", TXbox, 0, (void *)0 );
    TXInsertName( TeXlist, "vbox", TXbox, 0, (void *)0 );

    TXInsertName( TeXlist, "{", TXbbrace, 0, (void *)0 );
    TXInsertName( TeXlist, "}", TXebrace, 0, (void *)0 );
    if (!DestIsHtml)
	TXInsertName( TeXlist, "par", TXname, 0, TXCopy("par") );
    else
	TXInsertName( TeXlist, "par", TXnewline, 0, (void *)0 );

    if (DestIsHtml) {
	char buf[10];
	buf[0] = TOK_START;
	strcpy( buf+1, "&amp;" );
	buf[6] = TOK_END;
	buf[7] = 0;
	TXInsertName( TeXlist, "&", TXname, 0, TXCopy(buf) );
    }
    else
	TXInsertName( TeXlist, "&", TXname, 0, TXCopy("&") );

    {
	char buf[4];
	buf[0] = TOK_START;
	buf[1] = '%';
	buf[2] = TOK_END;
	buf[3] = 0;
	TXInsertName( TeXlist, "%", TXname, 0, TXCopy(buf) );
    }
/* Process \<space> and \<newline> as a single space */
    TXInsertName( TeXlist, " ", TXname, 0, TXCopy(" ") );
    TXInsertName( TeXlist, "\n", TXname, 0, TXCopy(" ") );

    TXInsertName( TeXlist, "times", TXname, 0, TXCopy(" x ") );

    TXInsertName( TeXlist, "fileinclude", TXfileinclude, 1, (void *)0 );

/* Slide commands */
    TXInsertName( TeXlist, "vt",  TXvt,  0, (void *)1 );
    TXInsertName( TeXlist, "vtitle",  TXvtt,  1, (void *)0 );
    TXInsertName( TeXlist, "vtt", TXvtt, 1, (void *)1 );
    TXInsertName( TeXlist, "vtts", TXvtt, 1, (void *)2 );
    TXInsertName( TeXlist, "vttss", TXvtt, 1, (void *)3 );
    TXInsertName( TeXlist, "vttsss", TXvtt, 1, (void *)4 );
    TXInsertName( TeXlist, "vttssss", TXvtt, 1, (void *)5 );
    TXInsertName( TeXlist, "vttsssss", TXvtt, 1, (void *)6 );
    TXInsertName( TeXlist, "href", TXhref, 1, (void *)0 );
    TXInsertName( TeXlist, "hrefa", TXhref, 2, (void *)0 );
    TXInsertName( TeXlist, "details", TXdetails, 1, (void *)0 );

/* Stuff generated by bibligraphy files */
    TXInsertName( TeXlist, "bibitem",  TXbibitem, 1, (void *)0 );

/* Hypertext */
    TXInsertName( TeXlist, "URL",  TXURL,  1, (void *)0 );
    TXInsertName( TeXlist, "AURL", TXAURL, 2, (void *)0 );

/* TeX accents */
    InitAccents();

/* HTML Tables */
    if (HandleAlign)
	InitTabular();

    /* Initialize the name of the error output file */
    GetBaseName( latex_errname );
    strcat( latex_errname, ".ler" );
}

void TeXProcessCommand( char *token, FILE *fin, FILE *fout )
{
    LINK     *l;
    TeXEntry *e;
    int      i;

    if (DebugCommands)
	fprintf( stdout, "Handling Command %s\n", token );
    l  = SRLookup( TeXlist, token, token, &i );
    if (!l) {
	if (!AmSkipping) /*  && InDocument)  */
	    fprintf( ferr, "Unknown LaTeX command %s, %s line %d\n",
		     token,
		     InFName[curfile] ? InFName[curfile]: "",
		     LineNo[curfile] );
	/* If next token is {, eat {} until no more {} pairs */
    }
    else {
	e = (TeXEntry *)( l->priv );
	(*e->action)( e );
    }
}

/* Write out the contents page
 * If the header hasn't been written, we need to add it.
 */
static int ContentsDepth = 100;
static int WrittenContents = 0;
static char ContentsLocation[256];

void TeXNoContents( void )
{
    WrittenContents = 1;
}
char *ContentsLoc( void )
{
    if (!WrittenContents) return 0;
    return ContentsLocation;
}
void TeXWriteContents( FILE *fout )
{
    if (WrittenContents) return;
/* WRtoauxfile( 0, outfile, 0, "Contents" ); */

    WriteHeadPage( fpout );
    WriteFileTitle( fpout, "Contents" );
    WriteBeginPage( fpout );

    WriteSectionAnchor( fpout, "Contents", "Node", 0, 0 );
    if (outfile)
	sprintf( ContentsLocation, "%s#Node0", outfile );
    else
	strcpy(  ContentsLocation, "Node0" );
    WriteSectionHeader( fpout, "Contents", "Node", 0, (char *)0, 0 );
    if (NumChildren( (void *)0 ) > 0 ) {
	WriteChildren( fpout, (void *)0, ContentsDepth );
    }
    CurSeqnum++;
/* At this point, we'd like to generate the title and authors. */
    WriteTextHeader( fpout );
/*WRfromauxfile( fout, 0 );*/

/* Switch to writing a new aux file */
    OpenWRAuxFile();
    WrittenContents = 1;
}

/*
   Here is the beginning of a sketch of a routine to process a LaTeXInfo file
 */
void ProcessLatexFile( int argc, char **argv, FILE *fin, FILE *fout )
{
    int  nsp, ch;

    fpin[0] = fin;
    fpout   = fout;

    curtok = tokbuf = (char *)MALLOC( MAX_CURTOKS * MAX_TOKEN );
    toknum = 0;
    CHKPTR(curtok);

    TXInit( fin, fout );
    ferr = fopen( "latex.err", "w" );

/* Define the symbols to process */
    TXinitbreaktable();

/* Try to read info file */
    topicctx = SRCreate();
    RdAuxFile( topicctx );
/*
   SCSetCommentChar( '%' );
   */
/* We shouldn't do this until we've seen the maketitle or the first section */
/* Note that this is executed outside of the "begin{document}", so
   we have turned off all output here */
    PUSHCURTOK;
    while (1) {
	ch = TeXReadToken( curtok, &nsp );
	if (ch == EOF) {
	    if (curfile > 0) {
		if (DebugCommands)
		    fprintf( stdout, "EOF in ProcessLatexFile\n" );
		TXPopFile();
	    }
	    else break;
	}
	if (ch == CommentChar) {
	    SCTxtDiscardToEndOfLine( fpin[curfile] );
	    LineNo[curfile]++;
	}
	else if (ch == CommandChar) {
	    /* TeXoutsp( fout, nsp ); */
	    TeXProcessCommand( curtok+1, fpin[curfile], fout );
	}
	else if (ch == LbraceChar) {
	    if (BraceCount >= MAX_BLOC) {
		TeXAbort( "", "Braces too deep" );
	    }
	    strncpy( bloc[BraceCount].filename, InFName[curfile],
		     MAX_BLOC_FNAME );
	    bloc[BraceCount++].lineno = LineNo[curfile];
	    /* fprintf( fout, "**+%d[%d]**",
	       LineNo[curfile], BraceCount ); */
#ifdef FOO
	    push stack (font, spacing parameters);
#endif
	}
	else if (ch == RbraceChar) {
	    BraceCount--;
	    /* fprintf( fout, "**-%d[%d]**",
	       LineNo[curfile], BraceCount ); */
#ifdef FOO
	    pop stack;
#endif
	}
	else if (ch == AlignChar && HandleAlign) {
	    TeXPutAlign();
	}
	else if (ch == ActiveChar) {
	    TXActiveCharDo(curtok);
	}
	else {
	    /*
	      for (i=0; i<nsp; i++) fputs( " ", fout );
	      if (ch == '\n' && lSp >= 0)
	      (*lstack[lSp].newline)( fout );
	      else
	      fputs( curtok, fout );
	      */
	}
    }
    POPCURTOK;
    WriteEndofTopic( fout );
    fclose( ferr );
    if (0)
	PrintAllContents(stdout);
    if (BraceCount != 0) {
	fprintf( stderr, "Brace count = %d\n", BraceCount );
	if (BraceCount > 0) {
	    int ii;
	    for (ii=0; ii<BraceCount; ii++)
		fprintf( stderr, "Unmatched brace near line %d in file %s\n",
			 bloc[ii].lineno, bloc[ii].filename );
	}
    }

    FREE( tokbuf );
}

/* Manage the citation characters */
void TXSetCitePrefix( const char *s )
{
    if (CitePrefix) FREE( CitePrefix );
    CitePrefix = STRDUP( s );
    CHKPTR(s);
}

void TXSetCiteSuffix( const char *s )
{
    if (CiteSuffix) FREE( CiteSuffix );
    CiteSuffix = STRDUP( s );
    CHKPTR(s);
}

/*
 * Print strings that may contain TOK_START/TOK_END sequences
 */

void TXPrintToken( FILE *fp, const char *str )
{
    char c;
    while (*str) {
	c = *str++;
	if ((unsigned char)c == TOK_START) {
	    fputs( "TOK_START", fp );
	}
	else if ((unsigned char)c == TOK_END) {
	    fputs( "TOK_END", fp );
	}
	else if (isgraph(c) || c == ' ') {
	    fputc( c, fp );
	}
	else {
	    fputc( '^', fp );
	    /* Make 7 bit */
	    c = c & 0x7f;
	    /* make at least space */
	    if (c < 0x20) c |= 0x40;
	    /* Delete is at the end of the range */
	    if (c == 127) c = '?';
	    fputc( c, fp );
	}
    }
}

/*
   Notes on handling \" etc.
   These are tex commands that modify the next character.  They should
   be thought of a commands that look at the next argument, which, since
   it does not start with a {, is a single letter.  The mapping to HTML is
   (see http://www.hut.fi/~jkorpela/HTML3.2/latin1.html)
   \"A &Auml;    &#196;
   \`A &Agrave;  &#192;
   \`E &Egrave;  &#200;
   \`I &Igrave;  &#204;
   \"O &Ouml;    &#214;
   \`O &Ograve;  &#210;
   \"U &Uuml;    &#220;
   \"a &auml;    &#228;
   \`a &agrave;  &#224;
   \`e &egrave;  &#232;
   \`i &igrave;  &#236;
   \"o &ouml;    &#246;
   \`o &ograve;  &#242;
   \"u &uuml;    &#246;
   \`u &ugrave;  &#249;
*/
#ifdef FOO
// From TeXSkipEnv.  I believe that this code is dead and can
// never be executed

	    if (InVerbatim) {
		if (strcmp( curtok, "end" ) == 0) {
		    if (DebugCommands)
			fprintf( stdout, "Getting argument for end{}\n" );
		    TeXMustGetArg( fpin[curfile], curtok, MAX_TOKEN,
				   "TXSkipEnv", e->name );
		    /* Check for a user-defined environment type */
		    if (LookupEnv( curtok, &btext, &etext, &nargs)) {
			if (etext) {
			    if (DebugCommands)
				fprintf( stdout, "Pushing back |%s|\n", etext );
			    SCPushToken( etext );
			}
		    }
		    else if (strcmp( curtok, name ) != 0) {
			if (flag)
			    fprintf( ferr,
		  "%s does not match %s (started at line %d), at %s line %d\n",
		  curtok, name, line_num,
 	          InFName[curfile] ? InFName[curfile]: "", LineNo[curfile] );
		    }
		    else {
			break;
		    }
		}
		else {
		    if (DestIsHtml)
			TeXoutstr( fout, "\\" );
		    else
			TeXoutstr( fout, "\\\\" );
		    TeXoutstr( fout, curtok );
		}
	    }
	    else {
	    }
#endif

/* Eventually, we may prefer to use PNM of PNG files */
static int debugF2GIF = 0;

/* Convert fname to GIF.  Replace fname with the filename to be used to
 include the file in the generated output */
int TXConvertFigureToGIF( char *fname )
{
    char *p;
    char pgm[2048], fname2[2048];
    int  rc = 0;

    /* Determine the type of the file from the known types (if this
       ever gets long, we could use an array of types and methods to convert
       to GIF */

    if (debugF2GIF)
	fprintf( stderr, "Converting file %s\n", fname );

    /* Create the result file name.  The result name is created from the
       basename of the result, with the target directory */
    p = fname + strlen(fname) - 1;
    /* find the dirctory separator, if any */
    while (p != fname && *p != DirSep) p--;
    if (*p == DirSep) p++;
    /* splitdir is statically allocated, so it is always a non-null pointer */
    if (*splitdir == 0) {
	snprintf( fname2, sizeof(fname2), "%s", p );
    }
    else {
	snprintf( fname2, sizeof(fname2), "%s%c%s", splitdir, DirSep, p );
    }
    p = fname2 + strlen(fname2) - 1;
    while (p != fname2 && *p != '.') p--;
    *p = 0;
    strncpy( p, ".gif", 10 );

    if (strstr( fname, ".gif" )) {
	if (debugF2GIF)
	    fprintf( stderr, "Do we need to move %s to %s\n", fname, fname2 );
	/* Copy the file into the proper directory if it isn't there */
	if (strcmp( fname, fname2 ) != 0) {
	    snprintf( pgm, sizeof(pgm), "/bin/cp -f %s %s", fname, fname2 );
	    rc = system( pgm );
	    if (rc) {
		fprintf( stderr, "Error code %d from %s\n", rc, pgm );
		return 0;
	    }
	}
	p = fname2 + strlen(fname2) - 1;
	while (p != fname2 && *p != DirSep) p--;
	if (*p == DirSep) p++;
	strcpy( fname, p );
	return 1;
    }


    if ( strstr( fname, ".ps" ) != 0) {
	if (debugF2GIF) fprintf( stderr, "Converting ps to gif for %s\n",
				 fname );
	/* The figure argument preserves color */
	snprintf( pgm, sizeof(pgm), "%spstogif %s %s figure >>%s 2>&1",
		  PSPATH, fname, fname2, latex_errname );
	if (debugF2GIF)
	    fprintf( stderr, "Running %s\n", pgm );
	rc = system( pgm );
	if (rc != 0) {
	    fprintf( stderr, "Error code %d from %s\n", rc, pgm );
	    return 0;
	}
	p = fname2 + strlen(fname2) - 1;
	while (p != fname2 && *p != DirSep) p--;
	if (*p == DirSep) p++;
	strcpy( fname, p );
	return 1;
    }
    else if ( strstr( fname, ".eps" ) != 0) {
	if (debugF2GIF) fprintf( stderr, "Converting eps to gif for %s\n",
				 fname );
	/* The figure argument preserves color */
	snprintf( pgm, sizeof(pgm), "%spstogif %s %s figure >>%s 2>&1",
		  PSPATH, fname, fname2, latex_errname );
	if (debugF2GIF)
	    fprintf( stderr, "Running %s\n", pgm );
	rc = system( pgm );
	if (rc != 0) {
	    fprintf( stderr, "Error code %d from %s\n", rc, pgm );
	    return 0;
	}
	p = fname2 + strlen(fname2) - 1;
	while (p != fname2 && *p != DirSep) p--;
	if (*p == DirSep) p++;
	strcpy( fname, p );
	return 1;
    }
    else if ( strstr( fname, ".pnm" ) != 0) {
	return 1; /* Will this work? */
    }
    else if ( strstr( fname, ".pdf" ) != 0) {
	if (debugF2GIF) fprintf( stderr, "Converting pdf to gif for %s\n",
				 fname );
	/* Change the output name to a .ps file */
	p = fname2 + strlen(fname2) - 1;
	while (p != fname2 && *p != '.') p--;
	strncpy( p, ".ps", 10 );

	/* Remove the file that we're about to create.  Ignore errors */
	unlink( fname2 );
	snprintf( pgm, sizeof(pgm), "pdf2ps %s %s >>%s 2>&1", fname, fname2,
		  latex_errname );
	if (debugF2GIF)
	    fprintf( stderr, "Running %s\n", pgm );
	rc = system( pgm );
	if (rc != 0) {
	    fprintf( stderr, "Error code %d from %s\n", rc, pgm );
	    return 0;
	}
	if (debugF2GIF)
	    fprintf( stderr, "Now, convert %s to gif\n", fname2 );
	/* Now convert the postscript file */
	rc = TXConvertFigureToGIF( fname2 );
	strcpy( fname, fname2 );
	return rc;
    }
    return rc;
}

/* Active character handling */
void TXActiveCharSet( char ch, void (*fcn)(char *) )
{
    ActiveChar       = ch;
    activeCharAction = fcn;
}
void TXActiveCharClear(void)
{
    ActiveChar       = '\0';
    activeCharAction = 0;
}
void TXActiveCharDo(char *str)
{
    if (activeCharAction) {
	(*activeCharAction)(str);
    }
    else {
	fprintf(stderr, "Attempt to invoke active char action with null action\n");
    }
}

void TXObeylinesFlushLine(char *token)
{
    if (!InDocument) return;
    TeXoutcmd( fpout, "<br>\n" );
}
void TXObeylinesClearActive(void)
{
    TXActiveCharClear();
}
/* Obeylines changes the handling of \n until the current group ends */
void TXDoObeylines(TeXEntry *e)
{
    TXActiveCharSet('\n', TXObeylinesFlushLine);
    TXgroupPushAction(TXObeylinesClearActive);
}
