[mod.sources] texchk

sources-request@panda.UUCP (12/17/85)

Mod.sources:  Volume 3, Issue 63
Submitted by: talcott!think!massar


This is 'texchk', a syntax checker for the LaTeX TeX macro package.

JP Massar
massar@think.com
ihnp4!think!massar

------------------------------------------------------------------------------
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	README
#	Makefile
#	texchk.1
#	cmds.h
#	ctools.h
#	texchars.h
#	texchk.h
#	texchk.c
# This archive created: Tue Dec 17 09:59:40 1985
export PATH; PATH=/bin:$PATH
echo shar: extracting "'README'" '(1687 characters)'
if test -f 'README'
then
	echo shar: will not over-write existing file "'README'"
else
cat << \SHAR_EOF > 'README'
Instructions for installing texchk:

  Assuming you are running a BSD 4.2 or compatible system:

    Edit the file Makefile.
         Change the BIN variable to the directory where you want the
           executable to go.
         Change the MS variable to be the 'x' which makes /usr/man/manx
           acceptable to you.
         Change the CC variable if you wish to compile with other than 'cc'.
    Type 'make install -n' to insure that you understand what is
      happening.
    Type 'make install'.
    Type 'make man'.
    Type 'texchk -c -v <your-own-latex-file>' and verify that it is
      producing reasonable output.
    Type 'make clean'.

  Assuming you are on a non BSD 4.2 compatible system:

    You will probably have to change the call to 'rindex' in ctools.c.
    (Compiling with "-Drindex=strrchr" is sufficient - see the Makefile).

    If your compiler is brain damaged you may have to extensively    
      shorten and revise the names of various variables and preprocessor
      constants.

    Take out the -DBSD42 flag from the Makefile.  If your system does
    not have <string.h> (Berkeley called it strings.h), you may have
    to edit ctools.c and texchk.c to declare the strxxx functions.

    Modulo these changes, I got texchk to run under BRL's SYSTEM V
      emulation package so hopefully it will run on most systems.


The files ctools.c and ctools.h are potentially useful in their own 
right.  They contain a library of routines I find or found useful.  You
might look through it for functions you will find useful or you could
install the whole thing as a library as has been done here at TMC.

JP Massar
massar@think.com
ihnp4!think!massar
SHAR_EOF
if test 1687 -ne "`wc -c < 'README'`"
then
	echo shar: error transmitting "'README'" '(should have been 1687 characters)'
fi
fi
echo shar: extracting "'Makefile'" '(592 characters)'
if test -f 'Makefile'
then
	echo shar: will not over-write existing file "'Makefile'"
else
cat << \SHAR_EOF > 'Makefile'
CC = cc

#use the following DEFINES line for non-BSD systems
#DEFINES = -Drindex=strchr
DEFINES = -DBSD42

#use the following CFLAGS line for debugging
#CFLAGS = -g $(DEFINES)
CFLAGS = -O $(DEFINES)

SOURCES = texchk.c cmds.c texchars.c errors.c verbatim.c ctools.c
OBJECTS = texchk.o cmds.o texchars.o errors.o verbatim.o ctools.o
LIBS =
BIN = /usr/local
MS=1
MAN = /usr/man/man$(MS)

texchk: $(OBJECTS)
	$(CC) -o texchk $(CFLAGS) $(OBJECTS) $(LIBS)
install: texchk
	rm -f $(BIN)/texchk
	mv texchk $(BIN)/texchk
man: 
	cp texchk.1 $(MAN)/texchk.$(MS)
	man texchk
clean:
	rm -f texchk *.o *~
SHAR_EOF
if test 592 -ne "`wc -c < 'Makefile'`"
then
	echo shar: error transmitting "'Makefile'" '(should have been 592 characters)'
fi
fi
echo shar: extracting "'texchk.1'" '(2494 characters)'
if test -f 'texchk.1'
then
	echo shar: will not over-write existing file "'texchk.1'"
else
cat << \SHAR_EOF > 'texchk.1'
.TH TEXCHK 1 3/13/85
.SH NAME
TEXCHK - syntax checker for LaTeX.
.SH SYNOPSIS
.B texchk
[ -v -c ] [ file1 file2 ... ]
.SH DESCRIPTION
By default
.I texchk 
reads from standard input and outputs error messages and whatnot to
standard error.  If filenames are given as arguments, each file is read
and parsed in turn.
.PP
.I texchk
checks for the proper nesting of matching '{' '}' and '[' ']' pairs,
matching '\\begin{<environment>}' '\\end{<environment>} pairs, and Math Mode
and Display Math Mode constructs.  If improper nesting is detected, an error
message consisting of a reason, a line number, and the actual line of text
on which the error occurred is displayed.
.PP
In general, the program will halt after finding and printing the first
error, because attempts at further diagnosis would likely result in a
stream of spurious error messages.  
.PP
Unlike LaTeX
.I texchk
is fast.  It can grovel over a large file in a matter of a few seconds,
and can be run repeatedly without causing the CPU to beg for mercy.
.PP
The command line options are:
.TP
.B \-v
Verbose option.  Produces on the standard output a trace of each environment
.I texchk 
enters and leaves, along with the number of the line it is currently 
processing.  This is used basically to assure yourself that
.I texchk
is actually doing something, if you give it a huge file to process.
(And is mildly interesting).
.TP
.B \-c
Check mode.  Tells
.I texchk
to check each keyword (e.g., \\hspace, \\mbox) in the file against a list of 
known keywords, and issue a warning if the keyword is not in the list.
Also, each keyword is checked as to whether it is only legal if given 
inside of math mode, and if so and if math mode is not enabled, an error
message is given (but processing continues).
.SH FILES
/usr/src/local/cmd/texchk		source directory.
.br
/usr/local/texchk			executable image.
.SH AUTHOR
JP Massar, Thinking Machines Corporation
.SH BUGS
.I texchk
makes no claims to being perfect.  It is quite possible that an 'error'
flagged by
.I texchk
is not really an error at all if run through LaTeX.
.PP
It does not check for number of arguments, presence of optional arguments,
etc.
.PP
It does not know that certain keywords can only be used in certain modes.
.PP
It does not understand that '[' and ']' can be used in regular text
without necessarily being paired. Also, \\left], for example,
will confuse it totally.
.PP
Although not officially part of LaTeX, 
.I texchk
understands the '$$' construct of TeX.
SHAR_EOF
if test 2494 -ne "`wc -c < 'texchk.1'`"
then
	echo shar: error transmitting "'texchk.1'" '(should have been 2494 characters)'
fi
fi
echo shar: extracting "'cmds.h'" '(803 characters)'
if test -f 'cmds.h'
then
	echo shar: will not over-write existing file "'cmds.h'"
else
cat << \SHAR_EOF > 'cmds.h'
typedef struct {

   int cmdlength;
   char *cmd;
   char is_math_mode_only;
   unsigned flags;

 } Latex_Command, *Ptr_Latex_Command;


extern Latex_Command Command_Table[]; 
 
extern char *Math_Environments[];

#define COMMAND_TABLE_SIZE (sizeof(Command_Table)/sizeof(Latex_Command))

#define IS_MATH_MODE(i) (Command_Table[(i)].is_math_mode_only)

#define NOT_FOUND -1 

extern int command_lookup ();

  /* ARGUMENTS:
     char *command;
  */

  /* 'command' is the name of a presumed latex command, including the */
  /* initial backslash.   Returns NOT_FOUND if command is not in the command */
  /* table, otherwise the index into the command table of the entry for */
  /* the input command. */

extern int is_math_environment();

  /* ARGUMENTS:
     char *keyword;
  */

  /* Returns 1 or 0 */
SHAR_EOF
if test 803 -ne "`wc -c < 'cmds.h'`"
then
	echo shar: error transmitting "'cmds.h'" '(should have been 803 characters)'
fi
fi
echo shar: extracting "'ctools.h'" '(10814 characters)'
if test -f 'ctools.h'
then
	echo shar: will not over-write existing file "'ctools.h'"
else
cat << \SHAR_EOF > 'ctools.h'
/* -*- Mode: C; Package: (CTOOLS C) -*- */

#ifndef Bool
#define Bool int
#endif

#ifndef T
#define T 1
#endif

#ifndef F
#define F 0
#endif

#ifndef MAXINT
#define MAXINT 2147483647
#define MAXINTSTR "2147483647"
#endif

#ifndef MAXPATHLEN
#define MAXPATHLEN 80
#endif

extern char *emalloc();

  /* int space; */
  /* space must be greater than 0 */
  /* Causes executution to halt with a 'Fatal error' message if memory */
  /* cannot be allocated, otherwise returns pointer to malloc-ed space */

extern char *anewstr();

  /* char *astring; */
  /* emalloc's space and copies astring into that space.  Returns pointer */
  /* to new string. */


extern int copy();

  /* char *dest, *src; int n; */
  /* copies exactly n bytes. */
  /* return value undefined.  Use only as procedure. */

extern int fill();

  /* char *addr, ch; int n; */
  /* copies ch into n consecutive bytes. */
  /* return value undefined.  Use only as procedure. */

extern int to_upper_if_lower();

  /* char ch;  Returns possibly upper-cased value. */

extern int to_lower_if_upper();

  /* char ch;  Returns possibly lower-cased value. */

extern int buffconcat();

  /* char *buffer, *s1, *s2; */
  /* s1 and s2 must be null terminated.  Buffer must be at least */
  /* strlen(s1) + strlen(s2) + 1 characters long.  Buffer is null */
  /* terminated upon completion. */

  /* return value undefined.  Use only as procedure. */

extern int nbuffconcat();

  /* char *buffer; int n; char *s1,*s2,*s3,*s4,*s5,*s6; */
  /* all the strings must be null terminated.  Buffer must be big enough */
  /* to hold the null terminated result.  0 < n < 7 . 
  /* returns -1 if n is out of range, otherwise 0 */

extern int slcompare();

  /* char *s1; int l1; char *s2; int l2 */
  /* does not stop if it encounters a null character. */
  /* returns 0 if equal, -1 if not equal */

extern int slge_compare();

  /* char *s1; int l1; char *s2; int l2 */
  /* does not stop if it encounters a null character. */
  /* returns 0 if equal, -1 if s1 < s2, 1 if s1 > s2 */

extern int nocase_compare();

  /* char *s1; int l1; char *s2; int l2 */
  /* does not stop if it encounters a null character. */
  /* returns 0 if equal, -1 if s1 < s2, 1 if s1 > s2  case independently. */

extern char * strfind();

  /* char *s1; char *s2; int fast; */
  /* finds s2 as a substring of s1.  s1 and s2 are null terminated. */
  /* returns 0 if not found, otherwise pointer into s1 to first matching */
  /* character. */

  /* WARNING:  will access off the end of s1 in the interest of efficiency */
  /* if 'fast' is non-zero. */

extern char * strncfind();

  /* char *s1; char *s2; int fast; */
  /* finds s2 as a substring of s1 case independently.  s1 and s2 are */
  /* null terminated. */
  /* returns 0 if not found, otherwise pointer into s1 to first matching */
  /* character. */

  /* WARNING:  will access off the end of s1 in the interest of efficiency */
  /* if 'fast' is non-zero. */

extern char * strsearch();

  /* char *s1; int l1; char *s2; int l2 */
  /* finds s2 as a substring of s1.  Does not stop if it encounters a null. */
  /* returns pointer into s1, otherwise (char *) 0 if search fails */
  /* case dependent */

extern char * strncsearch();

  /* char *s1; int l1; char *s2; int l2 */
  /* finds s2 as a substring of s1. */
  /* returns pointer into s1, otherwise (char *) 0 if search fails */
  /* case independent */

extern int remove_excess_blanks();

  /* char *newstring, *oldstring; */
  /* newstring must be long enough to hold the result, which may be as */
  /* long as oldstring.  oldstring must be null terminated. */
  /* an excess blank is any blank before the first non-blank character, */
  /* any blank after the last non-blank character, and any blank immediately */
  /* following a blank. */
  /* returns length of newstring */

extern int yes_or_no_check();

  /* char *astring; */
  /* returns 1 for yes, 0 for no, -1 for neither. */
  /* astring must be one of "YES", "Y", "NO", "N" in any capitalization. */


/* These routines return T if every char satisfies a certain condition. */
/* These returns all returns T if given a null string. */

extern Bool all_digits();
extern Bool all_whitespace();
extern Bool all_uppercase();
extern Bool all_lowercase();
extern Bool all_alphabetic();
extern Bool all_alphanumeric();
extern Bool all_ascii();


extern int str_to_pos_int();

  /* char *astring; int low,high; */
  /* low must be >= 0. */
  /* returns -1 if *astring is not composed of digits. */
  /* returns -2 if the integer is out of range. */
  /* despite its name, 0 can be returned as a legitimate value. */
  /* treats all digit strings as decimal. */


extern int sreverse();

  /* char *buffer; char *astring; */
  /* puts the characters of astring in reverse order into buffer. */
  /* buffer must be at least as long as astring + 1. */
  /* buffer is null terminated when done. */
  /* No return value.  Use only as procedure. */

extern char *ip_sreverse();

  /* char *astring; */
  /* Returns astring with its characters reversed. */
  /* reversal is done in place. */



#define PATH_MAXPATHLEN 256

char *temp_path();

/*
  char *dir; char *filename;

  Returns a pointer to a character string containing the string
  <dir>/<filename>.  The pointer points to a buffer which will may get
  overwritten if any functions in this package are subsequently called.
  0 is returned if the pathname would exceed PATH_MAXPATHLEN-1 chars.
*/


char *perm_path();

/*
  char *dir; char *filename;

  Same as temp_path, except the pathname string is malloc'ed and is thus
  permanent unless specifically freed by the user.  Further, no limit
  on the size of the path is made.
*/


char *make_path();

/*
  char *dir; char *filename; char *extension; Bool perm;

  Creates <dir>/<filename><extension> .  The string returned is permanent
  or not depending on 'perm'.  If perm is not true, 0 will be returned if
  the resulting path exceeds PATH_MAXPATHLEN-1 chars.
*/


char *make_path_numeric_extension();

/*
  char *dir; char *filename; int extension; Bool perm;

  Same as make_path except that extension is first converted into a
  string using sprintf.
*/


char *just_filename();

/*  
  char *path; Bool new; Bool perm;

  Given a path of the form /<x>/<y>/<z> returns <z>.  If new is not set
  then a pointer into the original input string is returned.  If new is
  set a copy is returned, either permanent or not depending on perm.
*/


#define ANSWER_NO 0
#define ANSWER_YES 1
#define ANSWER_HELP 2
#define ANSWER_QUIT 3
#define ANSWER_EOF 4

#define AT_EOF -1
#define TOO_MANY_CHARS -2
#define IOERROR -3
#define TOO_MANY_LINES -4
#define LINE_TOO_LONG -5

extern read_yes_or_no ();

  /* FILE *iport, *oport; char *prompt; char *helpstring; char *quitstring; */

  /* prints prompt, then reads from iport until is gets 'Y', 'N', 'YES' or */
  /* 'NO' (case independently).  If helpstring and/or quitstring are not */
  /* "" or (char *) 0 then if the user types in one of those ANSWER_HELP */
  /* or ANSWER_QUIT are returned, otherwise ANSWER_NO or ANSWER_YES are */
  /* eventually returned. */


extern int getline ();

  /* FILE *iport; char *buffer; int buflen; */

  /* reads a line into buffer.  Does not put the '\n' into buffer. */
  /* Returns AT_EOF if at end of file when called.  If it encounters */
  /* end of file after reading at least one character, the eof is treated */
  /* as if it were a newline.   Returns TOO_MANY_CHARS if more than */
  /* buflen - 1 characters are read before encountering a newline. */        
  /* In this case exactly buflen - 1 characters are read. */
  /* The last character read is always follwed by a '\0'. */
  /* if successful getline returns the number of characters read exclusive */
  /* of a terminating newline or eof. */


extern int getlines();

  /* FILE *fp; int n; char ***ptr_lines; char *linebuf; int maxlinelen; */
  /* See documentation for getfile below */
  /* If called, 'n' must have a value 0. */

extern int getfile();

  /* char *filename; char ***ptr_lines; char *linebuf; int maxlinelen; */

  /* read in a file as an array of character strings */
  /* 'maxlinelen+1' is the maximum length a line of the file is allowed */
  /* to be.  'linebuf' must be at least 'maxlinelen+1' characters long. */
  /* Returns the number of lines in the file (and therefore the number */
  /* of entries in *ptr_lines) if successful.  Returns IOERROR if it */
  /* could not open the file to read from. Returns TOO_MANY_CHARS if */
  /* it encounters a line longer than 'maxlinelen' characters.  

  /* Space for each line is malloc'ed as it is read in and the text for */
  /* the jth line is stored in (*ptr_lines)[j] */

  /* Only works for fairly small files as it recurses its way through the */
  /* file and does a lot of malloc-ing.  Use read_file_into_buffer or */
  /* ngetfile for large files. */

extern int ngetlines();

 /* FILE *fp; int n; char ***ptr_lines; char *linebuf; int maxlinelen; */
 /* Same as getlines, except at most 'n' lines will be read.  Returns */
 /* TOO_MANY_LINES if more than 'n' lines are present. */

extern int ngetfile();

 /* int n; char *filename; char ***ptr_lines; char *linebuf; int maxlinelen; */
 /* See ngetlines above. */

extern int read_file_into_buffer();

  /* char *filename; 
     char ***ptr_lines;
     int maxlines;
     char *buffer;
     int buflen;
     char *linebuffer;
     int linebuflen;
  */

  /* *ptr_lines should be an array of character string pointers maxlines */
  /* big.  buffer should be an array of characters buflen long.  The routine */
  /* reads lines using getline and stores them into buffer, terminating each */
  /* with a null.  A pointer to the nth line read is stored in *ptr_lines[n] */
  /* Returns IOERROR if it cannot open the file for reading, TOO_MANY_LINES */
  /* if more than maxlines were read in, TOO_MANY_CHARS if buffer is */
  /* filled before end of file is reached, and LINE_TOO_LONG is any line is */
  /* longer than linebuflen.  Returns number of lines read in if successful. */

extern char *efopen();  

  /* char *filename; char *mode */

  /* Actually returns a (FILE *), so one must cast the return value to this */
  /* type.  It doesn't return a (FILE *) explicitly because then to include */
  /* this file one would have to include <stdio.h> explicitly before it. */
  /* The routine simply calls fopen with the same arguments, but prints a */
  /* reasonable error message and calls exit if the call to fopen fails. */


Bool check_string();

  /* char *str; long minlen; long maxlen; */

  /* Returns T if str is not 0 and has a length between minlen and maxlen */
  /* inclusived, otherwise returns F. */


#ifndef check_int
#define check_int(i,minval,maxval) ((i) >= (minval) && (i) <= (maxval))
#endif
SHAR_EOF
if test 10814 -ne "`wc -c < 'ctools.h'`"
then
	echo shar: error transmitting "'ctools.h'" '(should have been 10814 characters)'
fi
fi
echo shar: extracting "'texchars.h'" '(198 characters)'
if test -f 'texchars.h'
then
	echo shar: will not over-write existing file "'texchars.h'"
else
cat << \SHAR_EOF > 'texchars.h'
extern init_legal_chars();

extern char Lgl_Chars[];
extern char Lgl_Single_Char_Commands[];

#define LGL_CHAR(x) (Lgl_Chars[(x)])
#define LGL_SINGLE_COMMAND_CHAR(x) (Lgl_Single_Char_Commands[(x)])
SHAR_EOF
if test 198 -ne "`wc -c < 'texchars.h'`"
then
	echo shar: error transmitting "'texchars.h'" '(should have been 198 characters)'
fi
fi
echo shar: extracting "'texchk.h'" '(1489 characters)'
if test -f 'texchk.h'
then
	echo shar: will not over-write existing file "'texchk.h'"
else
cat << \SHAR_EOF > 'texchk.h'
/* these are the types of tokens we look for in the input text */

typedef enum { 
        
        ESCAPE_BEGIN, ESCAPE_END, ESCAPE_ANY,
        LEFT_SQUARE_BRACKET, RIGHT_SQUARE_BRACKET,
        LEFT_CURLY_BRACKET, RIGHT_CURLY_BRACKET,
        MATH, DOUBLE_MATH, ESCAPE_SINGLE_CHAR

    } envtype;

/* some of the input tokens cause the LaTeX environment to be 'pushed' */
/* following is the definition of the stack on which these tokens are stored */
    
typedef struct {
   envtype etype;
   char *keyword;
   int linenum;
 } Stack_Entry, *Ptr_Stack_Entry;

#define MAX_ENTRIES 10000
#define MAX_KEYWORD_LENGTH 100
 
extern Stack_Entry Lex_Stack[];
extern int Lex_TOS;

#define Stack_Empty (Lex_TOS < 0)

#define LCB '{'
#define RCB '}'
#define LSB '['
#define RSB ']'
#define MATH_CHAR '$'
#define ESCAPE '\\'
#define COMMENT '%'

#define ENDSTRING "end"
#define BEGINSTRING "begin"

/* these are the possible actions we can take when we see a token. */
/* the 'CHECK' action has not been implemented. */

typedef enum {POP,PUSH,CHECK_SINGLE,DOLLAR,DOLLAR_DOLLAR,CHECK} Actions;

/* we keep track of where we are in the text so we can print out nice */
/* error messages */

extern long Current_Line, Current_Char;
extern char Line_Buffer[];
#define MAXLL 1024

extern Indent_Level;                    /* for verbose pretty print */

extern FILE *fp;

/* this is LaTeX's definition of whitespace */

#define ISWHITE(x) ((x) == ' ' || (x) == '\t')

#define SPACES_PER_INDENT_LEVEL 2
SHAR_EOF
if test 1489 -ne "`wc -c < 'texchk.h'`"
then
	echo shar: error transmitting "'texchk.h'" '(should have been 1489 characters)'
fi
fi
echo shar: extracting "'texchk.c'" '(16712 characters)'
if test -f 'texchk.c'
then
	echo shar: will not over-write existing file "'texchk.c'"
else
cat << \SHAR_EOF > 'texchk.c'
/* Texchk -- a LaTeX syntax and spelling checker.
   Written by JP Massar, Thinking Machines Corporation, Cambridge, MA
   This code is hereby released into the public domain, for better or worse.
*/

#include <stdio.h>
#include <ctype.h>

/* if your system doesn't have either string.h or strings.h you */
/* may have to declare the string functions yourself */
#ifdef BSD42
#include <strings.h>
#else
#include <string.h>
#endif

#ifdef TMC
#include <ctools.h>
#else
#include "ctools.h"
#endif

#include "texchk.h"
#include "cmds.h"
#include "texchars.h"

Bool Verbose_Mode = F;                  /* -v option */
Bool Check_Mode = F;                    /* -c option */

int Indent_Level = 0;                   /* for verbose output mode */

Stack_Entry Lex_Stack[MAX_ENTRIES];     /* environment stack */
int Lex_TOS = -1;

FILE *fp;                               /* file being processed */
Bool Already_At_Eof = F;

long Current_Line = 0;                  /* where we are in input text */
long Current_Char = 0;                  /* where we are in input text */
long Line_Length = 0;                   /* current line length */
char Line_Buffer[MAXLL];                /* buffer for input text */

Bool In_Math_Mode = F;
int Math_Mode_Depth = 0;

char Keyword_Buffer[MAX_KEYWORD_LENGTH];


new_file ()
{
  Current_Line = 0;
  Current_Char = 0;
  Line_Length = 0;
  In_Math_Mode = F;
  Math_Mode_Depth = 0;
  Indent_Level = 0;
}  
        
do_indent (level) int level;
{
  int j,i;
  for (j = 0; j < level; j++)
      for (i = 0; i < SPACES_PER_INDENT_LEVEL; i++) putc(' ',stderr);
}


lex_push (etype,keyword,linenum) envtype etype; char *keyword; long linenum;

/* push an environment onto the stack */

{
  if (++Lex_TOS >= MAX_ENTRIES) {
     fprintf(stderr,"Stack overflow...Process terminating.\n");
     texit();
  }
  Lex_Stack[Lex_TOS].etype = etype;
  Lex_Stack[Lex_TOS].keyword = keyword;
  Lex_Stack[Lex_TOS].linenum = linenum;
}


lex_pop (ptr_etype,ptr_keyword,ptr_linenum)

/* pop an environment and return its components */

  envtype *ptr_etype;
  char **ptr_keyword;
  long *ptr_linenum;

{
  if (Lex_TOS < 0) {
     fprintf(stderr,"Stack underflow...Process terminating\n");
     texit();
  }
  *ptr_etype = Lex_Stack[Lex_TOS].etype;
  *ptr_keyword = Lex_Stack[Lex_TOS].keyword;
  *ptr_linenum = Lex_Stack[Lex_TOS].linenum;
  Lex_TOS--;
}


curstack (ptr_etype,ptr_keyword,ptr_linenum)

  /* get the components of the current stack entry, but leave the entry */
  /* on the stack. */

  envtype *ptr_etype;
  char **ptr_keyword;
  long *ptr_linenum;

{
  if (Stack_Empty) {
     fprintf(stderr,"Fatal error, bad call to curstack\n");
     texit();
  }
  lex_pop(ptr_etype,ptr_keyword,ptr_linenum);
  lex_push(*ptr_etype,*ptr_keyword,*ptr_linenum);
}


char *copy_keyword (starttoken,endtoken) int starttoken,endtoken;

/* grab a keyword from the Line_Buffer and copy it into a static buffer */

{
  int len;
  if (MAX_KEYWORD_LENGTH <= (len = (endtoken - starttoken) + 1)) {
     keyword_length_error();
     texit();
  }
  strncpy(Keyword_Buffer,Line_Buffer + starttoken,len);
  Keyword_Buffer[len] = '\0';
  return(Keyword_Buffer);
}


do_pop (etype,keyword) envtype etype; char *keyword;

/* make sure that the current environment is the matching begin-environment */
/* for the end-environment that we have just discovered.  If so, pop the */
/* environment off the stack.   If not its an error. */

{
        
  envtype oldetype;
  char *oldkeyword;
  long oldlinenum;
  char *s, *e;
        
  lex_pop(&oldetype,&oldkeyword,&oldlinenum);
        
  switch (etype) {
     
    case ESCAPE_END :
      s = "\\begin";
      e = "\\end";
      if (oldetype != ESCAPE_BEGIN) goto nesterror;
      if (0 != strcmp(oldkeyword,keyword)) goto nesterror;
      break;

    case RIGHT_SQUARE_BRACKET :
      s = "[";
      e = "]";
      if (oldetype != LEFT_SQUARE_BRACKET) goto nesterror;
      break;

    case RIGHT_CURLY_BRACKET :
      s = "{";
      e = "}";
      if (oldetype != LEFT_CURLY_BRACKET) goto nesterror;
      break;

    case MATH :
      s = "Begin Math Mode";
      e = "End Math Mode";
      if (oldetype != etype) goto nesterror;
      break;
      
    case DOUBLE_MATH :
      s = "Begin Display Math Mode";
      e = "End Display Math Mode";
      if (oldetype != etype) goto nesterror;
      break;

  }

  return(0);

  nesterror:

  nest_error(s,e,oldlinenum,oldkeyword);
  texit();

}


int get_a_char ()

/* buffered input routine, to keep track of line number */

{
  int ch,rval;
  if (Current_Char >= Line_Length) {
     switch (rval = getline(fp,Line_Buffer,MAXLL-2)) {
       case AT_EOF:
         return(EOF);
         break;
       case TOO_MANY_CHARS :
         line_too_long_error();
         texit();
         break;
       default :
         Line_Buffer[rval] = '\n';
         Line_Buffer[++rval] = '\0';
         Line_Length = rval;
         Current_Char = 0;
         Current_Line++;
         break;
     }
  }
  ch = (int) (255 & Line_Buffer[Current_Char++]);
  if (!LGL_CHAR(ch)) bad_char_error(ch,T);
  return(ch);
}  
  

unget_a_char ()

{
  if (Current_Char == 0) {        
     fprintf(stderr,"Invalid unget...process terminating\n");
     texit();
  }
  Current_Char--;
}  


char *get_keyword ()

/* read a keyword.  Keywords consist of contiguous alphabetic characters */
/* keyword returned is in a static buffer. */

{
  int starttoken,endtoken,ch;
  starttoken = Current_Char - 1;
  endtoken = Current_Char - 1;
  while (isalpha(ch = get_a_char())) {
    endtoken++;
  }
  if (ch == EOF) {
     Already_At_Eof = 1;
  }
  else unget_a_char();
  return(copy_keyword(starttoken,endtoken));
} 


char *get_begin_end_keyword ()

/* called after a \begin or \end construct is found. */
/* begin and end keywords are enclosed in {}. */
/* a warning is issued if there is any whitespace within the {}s */
/* returns a string constituting what is in between the {}s save for */
/* whitespace immediately after the { and immediately before the } */

/* keyword returned is in a static buffer. */

{
  int ch;        
  int starttoken,endtoken;
  
  ch = get_a_char();
  if (ch != LCB) {
     no_brace_after_begin_end_error();
     texit();
  }
  
  starttoken = Current_Char;
  endtoken = starttoken - 1;
  while (RCB != (ch = get_a_char())) {
    if (ch == '\n')
       warning_close_brace();
    else if (ch == EOF) {
       eof_error();
       texit();
    }
    else 
       endtoken++;
  }
  
  /* ignore whitespace after '{' and before '}' */
  
  if (ISWHITE(Line_Buffer[starttoken]) || ISWHITE(Line_Buffer[endtoken])) {
     warning_blanks_in_cb();
  }
  
  while (starttoken < endtoken && ISWHITE(Line_Buffer[starttoken]))
    starttoken++;
  if (starttoken >= endtoken) {
     blank_begin_end_error();
     texit();
  }
  while (endtoken > starttoken && ISWHITE(Line_Buffer[endtoken]))
    endtoken--;
  return(copy_keyword(starttoken,endtoken));
  
}  


get_token (action,etype,keyword) 

  /* get the next significant token from the input stream. Based on its type */
  /* an action to perform is computed.  The significant part of the token is */
  /* returns in *keyword, which points to a static buffer. */

  /* returns 0 on encountering EOF, otherwise returns 1. */

  Actions *action;
  envtype *etype;
  char **keyword;

{  
  int ch,isbegin,isend;
  
  *keyword = 0;
  if (Already_At_Eof) return(0);
  
  readloop:
  
  if (EOF == (ch = get_a_char())) return(0);

  switch (ch) {

    case LSB :
      *etype = LEFT_SQUARE_BRACKET;
      *action = PUSH;
      *keyword = "[";
      return(1);

    case RSB :
      *etype = RIGHT_SQUARE_BRACKET;
      *action = POP;
      *keyword = "]";
      return(1);

    case LCB :
      *etype = LEFT_CURLY_BRACKET;
      *action = PUSH;
      *keyword = "{";
      return(1);

    case RCB :
      *etype = RIGHT_CURLY_BRACKET;
      *action = POP;
      *keyword = "}";
      return(1);

    case MATH_CHAR :
    
      /* Is the next character also a '$'? If so this is 'Display Math Mode' */
    
      if (EOF == (ch = get_a_char())) {
         *action = DOLLAR;
         *etype = MATH;
         *keyword = "$";
         Already_At_Eof = 1;
      }
      else if (ch == MATH_CHAR) {
         *action = DOLLAR_DOLLAR;
         *etype = DOUBLE_MATH;
         *keyword = "$$";
      }
      else {
         unget_a_char();
         *action = DOLLAR;
         *etype = MATH;
         *keyword = "$";
      }
      return(1);

    case ESCAPE :
    
      /* treat specially \begin and \end */
    
      if (EOF == (ch = get_a_char())) {
         eof_error();
         texit();
      }
      
      /* first check for single character non-alphabetic commands */
      
      if (!isalpha(ch)) {
         *action = CHECK_SINGLE;
         *etype = ESCAPE_SINGLE_CHAR;
         Keyword_Buffer[0] = ch;
         Keyword_Buffer[1] = '\0';
         *keyword = Keyword_Buffer;
         return(1);
      }
        
        
      *keyword = get_keyword();
      isbegin = (0 == strcmp(*keyword,BEGINSTRING));
      isend = (0 == strcmp(*keyword,ENDSTRING));
      if (!isbegin && !isend) {
         *action = CHECK;
         *etype = ESCAPE_ANY;
         return(1);
      }
      
      *etype = isbegin ? ESCAPE_BEGIN : ESCAPE_END;
      *action = isbegin ? PUSH : POP;
      *keyword = get_begin_end_keyword();
      return(1);

    case COMMENT :
    
      /* just read in the rest of the line and ignore what's on it */
    
      while ('\n' != (ch = get_a_char())) {
        if (EOF == ch) return(0);
      }
      goto readloop;
    
    default :
      goto readloop;
      
  }

}

push_math_mode (key) char *key;
{
  if (Verbose_Mode) {
     do_indent(Indent_Level++);
     fprintf (
         stderr,"Line %d: Entering math mode using <%s>\n",Current_Line,key
       );
  }
  Math_Mode_Depth++;
  In_Math_Mode = T;
  lex_push(MATH,key,Current_Line);
}

pop_math_mode (key) char *key;
{
  envtype etype;
  char *keyword;
  long linenum;
  if (Verbose_Mode) {
     do_indent(--Indent_Level);
     fprintf (
         stderr,"Line %d: Leaving math mode using <%s>\n",Current_Line,key
       );
  }
  Math_Mode_Depth--;
  In_Math_Mode = (Math_Mode_Depth > 0);
  lex_pop(&etype,&keyword,&linenum);
}


math_mode_action (action,keyword) Actions action; char *keyword;

/* check for math mode tokens, and enter or leave math mode as appropriate */

{
  char *stack_keyword;
  long linenum;
  envtype etype;
  char *key, *matching_keyword;
  
  switch (action) {

    /* If there is a matching '$' or '$$' as the latest entry on the stack */
    /* we pop it because it is a matching token.  Otherwise, we push it, */
    /* even if we are already in math mode. */
        
    case (DOLLAR) :
    case (DOLLAR_DOLLAR) :
      key = (action == DOLLAR) ? "$" : "$$";
      if (!In_Math_Mode) {
         push_math_mode(key);
         break;
      }
      curstack(&etype,&stack_keyword,&linenum);
      if (0 != strcmp(key,stack_keyword)) {
         push_math_mode(key);
      }
      else {
         pop_math_mode(key);
      }
      break;
     
    /* just adjust Math Mode for PUSH and POP, because in process_file */
    /* we will do the actual pushing and popping of these environments. */
      
    case (PUSH) :
      if (is_math_environment(keyword)) {
         Math_Mode_Depth++;
         In_Math_Mode = T;
      }
      break;

    case (POP) :
      if (is_math_environment(keyword)) {
         Math_Mode_Depth--;
         In_Math_Mode = (Math_Mode_Depth == 0);
      }
      break;
      
    /* look for \( and \[ commands which put us into math mode, and \) and */
    /* \] commands which pop us out of math mode.   Make sure if we are */
    /* popping that the proper pushed math mode command is the current */
    /* stack entry. */
      
    case (CHECK_SINGLE) :
      if (*keyword == '(' || *keyword == '[') {
         push_math_mode(anewstr(keyword));
      }
      else if (*keyword == ')' || *keyword == ']') {
         if (Stack_Empty) {
            stack_empty_error(MATH,keyword);
            texit();
         }
         curstack(&etype,&stack_keyword,&linenum);
         matching_keyword = (*keyword == ')') ? "(" : "[";
         if (0 != strcmp(matching_keyword,stack_keyword)) {
            nest_error(matching_keyword,keyword,linenum,stack_keyword);
            texit();
         }
         pop_math_mode(keyword);
      }
      break;

  }

}


process_file () 

/* Get significant LaTeX forms from the input file.  For each one, depending */
/* on its nature perform a verification or manipulate the environment stack. */
/* When we are done the stack should be empty. */

/* The file has already been opened using the global file descriptor 'fp' */

{
  Actions action;          
  envtype etype;
  char *keyword;
  int cmd_index,ch;
        
  while (0 != get_token(&action,&etype,&keyword)) {

    switch (action) {

      case (POP) :

        /* \end{keyword},, '}', ']' */
      
        if (Stack_Empty) {
           stack_empty_error(etype,keyword);
           texit();
        }
        
        math_mode_action(POP,keyword);
      
        if (Verbose_Mode && *keyword != '}' && *keyword != ']') {
           do_indent(--Indent_Level);
           printf("line %d: \\end{%s}\n",Current_Line,keyword);
        }
        do_pop(etype,keyword);
        break;

      case (PUSH) :
      
        /* \begin{keyword}, '{', '[' */
      
        math_mode_action(PUSH,keyword);
      
        if (Verbose_Mode && *keyword != '{' && *keyword != '[') {
           do_indent(Indent_Level++);
           printf("line %d: \\begin{%s}\n",Current_Line,keyword);
        }
        
        if (0==strcmp("verbatim",keyword) || 0==strcmp("verbatim*",keyword)) {
           do_verbatim(keyword);
           break;
        }
        else {
           lex_push(etype,anewstr(keyword),Current_Line);
           break;
        }

      case (DOLLAR) :
        math_mode_action(DOLLAR,keyword);
        break;
        
      case (DOLLAR_DOLLAR) :
        math_mode_action(DOLLAR_DOLLAR,keyword);
        break;
      
      case (CHECK_SINGLE) :

        /* check for \(, \[, \), \] for math mode */
      
        math_mode_action(CHECK_SINGLE,keyword);
        
        if (Check_Mode) {
           if (!LGL_SINGLE_COMMAND_CHAR(*keyword)) {
              single_char_command_error(*keyword);
           }
           if (NOT_FOUND == (cmd_index = command_lookup(keyword))) {
              fprintf(stderr,"Fatal error:\n");
              fprintf(stderr,"Command Table and Legal Chars out of sync\n");
              exit(1);
           }
           if (!In_Math_Mode && IS_MATH_MODE(cmd_index)) {
              math_keyword_error(keyword);
           }
              
        }
        break;
        
      case (CHECK) :
      
        /* \command token */
      
        if (0 == strcmp("verb",keyword)) {
           if ('*' != (ch = get_a_char())) unget_a_char();
           do_verb();
           break;
        }
      
        if (Check_Mode) {
           if (NOT_FOUND == (cmd_index = command_lookup(keyword))) {
              keyword_error(keyword);
           }
           else if (!In_Math_Mode && IS_MATH_MODE(cmd_index)) {
              math_keyword_error(keyword);
           }
        }
        
        break;
        
      default :
        fprintf(stderr,"Invalid return from get_token...\n");
        texit();
        
    }

  }
    
  if (!Stack_Empty) {
     eof_error();
     texit();
  }

  return(0);

}


texit ()
{
  fclose(fp);
  exit(1);
}


usage () 
{
  fprintf(stderr,"\nUnrecognized argument to texchk\n");
  fprintf(stderr,"Usage: texchk [ -v -c ] [ file1 file2 ... ]\n");
  exit(1);
}
  
  
main (argc,argv) int argc; char **argv;

{
  char **argptr;        
  int j,input_files = 0;
        
  init_legal_chars();
  
  /* process command line arguments */
  
  argptr = argv;        
  while (*++argptr != 0) {
    if (**argptr == '-') {
       if (strlen(*argptr) != 2) { 
          usage();
       }
       switch ((*argptr)[1]) {
         case 'v' :
           Verbose_Mode = T;
           break;
         case 'c' :
           Check_Mode = T;
           break;
         default :
           usage();
           break;
       }
       *argptr = '\0';
    }
    else input_files = 1;
  }
        
  /* read and process each file */
  
  if (!input_files) {
     printf("\n");
     fp = stdin;
     process_file();
     printf("\nOK!\n\n");
  }  
  else {
    for (j = 1; j < argc; j++) {
        if (argv[j] != '\0') {
           printf("\nChecking file %s.\n\n",argv[j]);
           new_file();
           fp = (FILE *) efopen(argv[j],"r");
           process_file();
           fclose(fp);
        }
    }
 }
  
 exit(0);
 
}


SHAR_EOF
if test 16712 -ne "`wc -c < 'texchk.c'`"
then
	echo shar: error transmitting "'texchk.c'" '(should have been 16712 characters)'
fi
fi
exit 0
#	End of shell archive

sources-request@panda.UUCP (12/17/85)

Mod.sources:  Volume 3, Issue 64
Submitted by: talcott!think!massar


This is 'texchk', a syntax checker for the LaTeX TeX macro package.

JP Massar
massar@think.com
ihnp4!think!massar

------------------------------------------------------------------------------
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	cmds.c
#	ctools.c
#	errors.c
#	texchars.c
#	verbatim.c
# This archive created: Tue Dec 17 10:00:15 1985
export PATH; PATH=/bin:$PATH
echo shar: extracting "'cmds.c'" '(21192 characters)'
if test -f 'cmds.c'
then
	echo shar: will not over-write existing file "'cmds.c'"
else
cat << \SHAR_EOF > 'cmds.c'
#include "cmds.h"

Latex_Command Command_Table[] = {

        { 1, " ", '\0' },
        { 1, "!", '1' },
        { 1, "\"", '\0' },
        { 1, "#", '\0' },
        { 1, "$", '\0' },
        { 1, "%", '\0' },
        { 1, "&", '\0' },
        { 1, "'", '\0' },
        { 1, "(", '\0' },
        { 1, ")", '\0' },
        { 1, "+", '\0' },
        { 1, ",", '\0' },
        { 1, "-", '\0' },
        { 1, ".", '\0' },
        { 1, "/", '\0' },
        { 1, ":", '1' },
        { 1, ";", '1' },
        { 1, "<", '\0' },
        { 1, "=", '\0' },
        { 1, ">", '\0' },
        { 1, "@", '\0' },
        { 2, "AA", '\0' },
        { 2, "AE", '\0' },
        { 4, "Alph", '\0' },
        { 3, "Box", '1' },
        { 5, "Delta", '1' },
        { 7, "Diamond", '1' },
        { 9, "Downarrow", '1' },
        { 5, "Gamma", '1' },
        { 1, "H", '\0' },
        { 4, "Huge", '\0' },
        { 2, "Im", '1' },
        { 4, "Join", '1' },
        { 5, "LARGE", '\0' },
        { 5, "LaTeX", '\0' },
        { 6, "Lambda", '1' },
        { 5, "Large", '\0' },
        { 9, "Leftarrow", '1' },
        { 14, "Leftrightarrow", '1' },
        { 13, "Longleftarrow", '1' },
        { 18, "Longleftrightarrow", '1' },
        { 14, "Longrightarrow", '1' },
        { 1, "O", '\0' },
        { 2, "OE", '\0' },
        { 5, "Omega", '1' },
        { 1, "P", '\0' },
        { 3, "Phi", '1' },
        { 2, "Pi", '1' },
        { 2, "Pr", '1' },
        { 3, "Psi", '1' },
        { 2, "Re", '1' },
        { 10, "Rightarrow", '1' },
        { 5, "Roman", '\0' },
        { 1, "S", '\0' },
        { 5, "Sigma", '1' },
        { 3, "TeX", '\0' },
        { 5, "Theta", '1' },
        { 7, "Uparrow", '1' },
        { 11, "Updownarrow", '1' },
        { 7, "Upsilon", '1' },
        { 2, "Xi", '1' },
        { 1, "[", '\0' },
        { 1, "\\", '\0' },
        { 2, "\\*", '\0' },
        { 1, "]", '\0' },
        { 1, "^", '\0' },
        { 1, "_", '1' },
        { 1, "`", '\0' },
        { 1, "a", '\0' },
        { 2, "aa", '\0' },
        { 21, "abovedisplayshortskip", '\0' },
        { 16, "abovedisplayskip", '\0' },
        { 5, "acute", '1' },
        { 15, "addcontentsline", '\0' },
        { 7, "address", '\0' },
        { 7, "address", '\0' },
        { 13, "addtocontents", '\0' },
        { 12, "addtocounter", '\0' },
        { 11, "addtolength", '\0' },
        { 9, "addvspace", '\0' },
        { 13, "advancepageno", '\0' },
        { 2, "ae", '\0' },
        { 5, "aleph", '1' },
        { 4, "alph", '\0' },
        { 5, "alpha", '1' },
        { 5, "amalg", '1' },
        { 3, "and", '\0' },
        { 5, "angle", '1' },
        { 8, "appendix", '\0' },
        { 6, "approx", '1' },
        { 6, "arabic", '\0' },
        { 6, "arccos", '1' },
        { 6, "arcsin", '1' },
        { 6, "arctan", '1' },
        { 3, "arg", '1' },
        { 11, "arraycolsep", '\0' },
        { 14, "arrayrulewidth", '\0' },
        { 12, "arraystretch", '\0' },
        { 3, "ast", '1' },
        { 5, "asymp", '1' },
        { 6, "author", '\0' },
        { 1, "b", '\0' },
        { 9, "backslash", '1' },
        { 3, "bar", '1' },
        { 12, "baselineskip", '\0' },
        { 15, "baselinestretch", '\0' },
        { 9, "batchmode", '\0' },
        { 5, "begin", '\0' },
        { 12, "beginsection", '\0' },
        { 21, "belowdisplayshortskip", '\0' },
        { 16, "belowdisplayskip", '\0' },
        { 4, "beta", '1' },
        { 2, "bf", '\0' },
        { 9, "bibindent", '\0' },
        { 7, "bibitem", '\0' },
        { 12, "bibliography", '\0' },
        { 17, "bibliographystyle", '\0' },
        { 6, "bigcap", '1' },
        { 7, "bigcirc", '1' },
        { 6, "bigcup", '1' },
        { 7, "bigodot", '1' },
        { 8, "bigoplus", '1' },
        { 9, "bigotimes", '1' },
        { 7, "bigskip", '\0' },
        { 13, "bigskipamount", '\0' },
        { 8, "bigsqcup", '1' },
        { 15, "bigtriangledown", '1' },
        { 13, "bigtriangleup", '1' },
        { 8, "biguplus", '1' },
        { 6, "bigvee", '1' },
        { 8, "bigwedge", '1' },
        { 13, "blackandwhite", '\0' },
        { 4, "bmod", '1' },
        { 8, "boldmath", '\0' },
        { 3, "bot", '1' },
        { 14, "bottomfraction", '\0' },
        { 6, "bowtie", '1' },
        { 5, "breve", '1' },
        { 6, "bullet", '1' },
        { 3, "bye", '\0' },
        { 1, "c", '\0' },
        { 3, "cal", '1' },
        { 3, "cap", '1' },
        { 7, "caption", '\0' },
        { 2, "cc", '\0' },
        { 4, "cdot", '1' },
        { 5, "cdots", '1' },
        { 9, "centering", '\0' },
        { 7, "chapter", '\0' },
        { 7, "chapter", '\0' },
        { 5, "check", '1' },
        { 3, "chi", '1' },
        { 4, "circ", '1' },
        { 6, "circle", '\0' },
        { 7, "circle*", '\0' },
        { 4, "cite", '\0' },
        { 15, "cleardoublepage", '\0' },
        { 9, "clearpage", '\0' },
        { 9, "cleartabs", '\0' },
        { 5, "cline", '\0' },
        { 7, "closing", '\0' },
        { 8, "clubsuit", '1' },
        { 6, "colors", '\0' },
        { 11, "colorslides", '\0' },
        { 9, "columnsep", '\0' },
        { 13, "columnseprule", '\0' },
        { 4, "cong", '1' },
        { 6, "coprod", '1' },
        { 9, "copyright", '\0' },
        { 3, "cos", '1' },
        { 4, "cosh", '1' },
        { 3, "cot", '1' },
        { 4, "coth", '1' },
        { 3, "csc", '1' },
        { 3, "cup", '1' },
        { 1, "d", '\0' },
        { 3, "dag", '\0' },
        { 6, "dagger", '1' },
        { 7, "dashbox", '\0' },
        { 5, "dashv", '1' },
        { 4, "date", '\0' },
        { 20, "dblfloatpagefraction", '\0' },
        { 11, "dblfloatsep", '\0' },
        { 15, "dbltextfloatsep", '\0' },
        { 14, "dbltopfraction", '\0' },
        { 4, "ddag", '\0' },
        { 7, "ddagger", '1' },
        { 4, "ddot", '1' },
        { 5, "ddots", '1' },
        { 3, "def", '\0' },
        { 3, "deg", '1' },
        { 5, "delta", '1' },
        { 3, "det", '1' },
        { 7, "diamond", '1' },
        { 11, "diamondsuit", '1' },
        { 3, "dim", '1' },
        { 12, "displaystyle", '1' },
        { 3, "div", '1' },
        { 13, "documentstyle", '\0' },
        { 12, "dosupereject", '\0' },
        { 3, "dot", '1' },
        { 5, "doteq", '1' },
        { 7, "dotfill", '\0' },
        { 9, "downarrow", '1' },
        { 3, "ell", '1' },
        { 2, "em", '\0' },
        { 8, "emptyset", '1' },
        { 4, "encl", '\0' },
        { 3, "end", '\0' },
        { 9, "endinsert", '\0' },
        { 7, "epsilon", '1' },
        { 7, "eqalign", '\0' },
        { 9, "eqalignno", '\0' },
        { 5, "equiv", '1' },
        { 3, "eta", '1' },
        { 14, "evensidemargin", '\0' },
        { 6, "exists", '1' },
        { 3, "exp", '1' },
        { 11, "extracolsep", '\0' },
        { 4, "fbox", '\0' },
        { 8, "fboxrule", '\0' },
        { 7, "fboxsep", '\0' },
        { 4, "fill", '\0' },
        { 6, "fivebf", '\0' },
        { 5, "fivei", '\0' },
        { 6, "fiverm", '\0' },
        { 6, "fivesy", '\0' },
        { 4, "flat", '1' },
        { 17, "floatpagefraction", '\0' },
        { 8, "floatsep", '\0' },
        { 11, "flushbottom", '\0' },
        { 8, "fnsymbol", '1' },
        { 5, "folio", '\0' },
        { 10, "footheight", '\0' },
        { 8, "footline", '\0' },
        { 8, "footnote", '\0' },
        { 12, "footnotemark", '\0' },
        { 12, "footnoterule", '\0' },
        { 11, "footnotesep", '\0' },
        { 12, "footnotesize", '\0' },
        { 12, "footnotetext", '\0' },
        { 8, "footskip", '\0' },
        { 9, "footstrut", '\0' },
        { 6, "forall", '1' },
        { 4, "frac", '1' },
        { 5, "frame", '\0' },
        { 8, "framebox", '\0' },
        { 13, "frenchspacing", '\0' },
        { 5, "frown", '1' },
        { 5, "fussy", '\0' },
        { 5, "gamma", '1' },
        { 3, "gcd", '1' },
        { 3, "geq", '1' },
        { 2, "gg", '1' },
        { 8, "glossary", '\0' },
        { 13, "glossaryentry", '\0' },
        { 5, "grave", '1' },
        { 3, "hat", '1' },
        { 4, "hbar", '1' },
        { 4, "hbox", '\0' },
        { 10, "headheight", '\0' },
        { 8, "headline", '\0' },
        { 7, "headsep", '\0' },
        { 9, "heartsuit", '1' },
        { 5, "hfill", '\0' },
        { 5, "hline", '\0' },
        { 3, "hom", '1' },
        { 13, "hookleftarrow", '1' },
        { 14, "hookrightarrow", '1' },
        { 9, "hrulefill", '\0' },
        { 6, "hspace", '\0' },
        { 7, "hspace*", '\0' },
        { 4, "huge", '\0' },
        { 11, "hyphenation", '\0' },
        { 1, "i", '\0' },
        { 5, "imath", '1' },
        { 2, "in", '1' },
        { 7, "include", '\0' },
        { 11, "includeonly", '\0' },
        { 6, "indent", '\0' },
        { 5, "index", '\0' },
        { 10, "indexentry", '\0' },
        { 10, "indexspace", '\0' },
        { 3, "inf", '1' },
        { 5, "infty", '1' },
        { 5, "input", '\0' },
        { 3, "int", '1' },
        { 9, "intextsep", '\0' },
        { 9, "invisible", '\0' },
        { 4, "iota", '1' },
        { 2, "it", '\0' },
        { 4, "item", '\0' },
        { 10, "itemindent", '\0' },
        { 7, "itemsep", '\0' },
        { 1, "j", '\0' },
        { 5, "jmath", '1' },
        { 3, "jot", '\0' },
        { 5, "kappa", '1' },
        { 3, "ker", '1' },
        { 4, "kill", '\0' },
        { 1, "l", '\0' },
        { 5, "label", '\0' },
        { 10, "labelitemi", '\0' },
        { 8, "labelsep", '\0' },
        { 10, "labelwidth", '\0' },
        { 6, "lambda", '1' },
        { 6, "langle", '1' },
        { 5, "large", '\0' },
        { 5, "lceil", '1' },
        { 5, "ldots", '1' },
        { 7, "leadsto", '1' },
        { 4, "left", '1' },
        { 9, "leftarrow", '1' },
        { 7, "lefteqn", '1' },
        { 15, "leftharpoondown", '1' },
        { 13, "leftharpoonup", '1' },
        { 10, "leftmargin", '\0' },
        { 11, "leftmargini", '\0' },
        { 12, "leftmarginii", '\0' },
        { 13, "leftmarginiii", '\0' },
        { 12, "leftmarginiv", '\0' },
        { 11, "leftmarginv", '\0' },
        { 12, "leftmarginvi", '\0' },
        { 14, "leftrightarrow", '1' },
        { 3, "leq", '1' },
        { 10, "leqalignno", '\0' },
        { 6, "lfloor", '1' },
        { 2, "lg", '1' },
        { 3, "lhd", '1' },
        { 3, "lim", '1' },
        { 6, "liminf", '1' },
        { 6, "limsup", '1' },
        { 4, "line", '\0' },
        { 9, "linebreak", '\0' },
        { 13, "linethickness", '\0' },
        { 9, "linewidth", '\0' },
        { 13, "listoffigures", '\0' },
        { 12, "listoftables", '\0' },
        { 13, "listparindent", '\0' },
        { 2, "ll", '1' },
        { 2, "ln", '1' },
        { 4, "load", '\0' },
        { 3, "log", '1' },
        { 13, "longleftarrow", '1' },
        { 18, "longleftrightarrow", '1' },
        { 10, "longmapsto", '1' },
        { 14, "longrightarrow", '1' },
        { 13, "magnification", '\0' },
        { 7, "makebox", '\0' },
        { 12, "makefootline", '\0' },
        { 12, "makeglossary", '\0' },
        { 12, "makeheadline", '\0' },
        { 9, "makeindex", '\0' },
        { 9, "makelabel", '\0' },
        { 10, "makelabels", '\0' },
        { 9, "maketitle", '\0' },
        { 6, "mapsto", '1' },
        { 9, "marginpar", '\0' },
        { 13, "marginparpush", '\0' },
        { 12, "marginparsep", '\0' },
        { 14, "marginparwidth", '\0' },
        { 8, "markboth", '\0' },
        { 9, "markright", '\0' },
        { 10, "mathindent", '\0' },
        { 3, "max", '1' },
        { 4, "mbox", '\0' },
        { 7, "medskip", '\0' },
        { 13, "medskipamount", '\0' },
        { 3, "mho", '1' },
        { 3, "mid", '1' },
        { 9, "midinsert", '\0' },
        { 3, "min", '1' },
        { 3, "mit", '1' },
        { 6, "models", '1' },
        { 2, "mp", '1' },
        { 2, "mu", '1' },
        { 11, "multicolumn", '\0' },
        { 8, "multiput", '\0' },
        { 5, "nabla", '1' },
        { 7, "natural", '1' },
        { 7, "nearrow", '1' },
        { 3, "neg", '1' },
        { 3, "neq", '1' },
        { 10, "newcommand", '\0' },
        { 10, "newcounter", '\0' },
        { 14, "newenvironment", '\0' },
        { 7, "newfont", '\0' },
        { 9, "newlength", '\0' },
        { 7, "newline", '\0' },
        { 7, "newpage", '\0' },
        { 10, "newsavebox", '\0' },
        { 10, "newtheorem", '\0' },
        { 2, "ni", '1' },
        { 6, "nocite", '\0' },
        { 7, "nofiles", '\0' },
        { 8, "noindent", '\0' },
        { 11, "nolinebreak", '\0' },
        { 16, "nonfrenchspacing", '\0' },
        { 8, "nonumber", '\0' },
        { 11, "nopagebreak", '\0' },
        { 13, "nopagenumbers", '\0' },
        { 12, "normalbottom", '\0' },
        { 15, "normalmarginpar", '\0' },
        { 10, "normalsize", '\0' },
        { 3, "not", '1' },
        { 2, "nu", '1' },
        { 10, "numberline", '\0' },
        { 7, "nwarrow", '1' },
        { 1, "o", '1' },
        { 13, "oddsidemargin", '\0' },
        { 4, "odot", '1' },
        { 2, "oe", '\0' },
        { 4, "oint", '1' },
        { 8, "oldstyle", '\0' },
        { 5, "omega", '1' },
        { 6, "ominus", '1' },
        { 9, "onecolumn", '\0' },
        { 9, "onlynotes", '\0' },
        { 10, "onlyslides", '\0' },
        { 7, "opening", '\0' },
        { 5, "oplus", '\0' },
        { 6, "oslash", '1' },
        { 6, "otimes", '1' },
        { 6, "output", '\0' },
        { 4, "oval", '\0' },
        { 9, "overbrace", '\0' },
        { 8, "overline", '\0' },
        { 8, "pagebody", '\0' },
        { 9, "pagebreak", '\0' },
        { 12, "pagecontents", '\0' },
        { 10, "pageinsert", '\0' },
        { 6, "pageno", '\0' },
        { 13, "pagenumbering", '\0' },
        { 7, "pageref", '\0' },
        { 9, "pagestyle", '\0' },
        { 3, "par", '\0' },
        { 9, "paragraph", '\0' },
        { 8, "parallel", '1' },
        { 6, "parbox", '\0' },
        { 9, "parindent", '\0' },
        { 6, "parsep", '\0' },
        { 7, "parskip", '\0' },
        { 4, "part", '\0' },
        { 7, "partial", '1' },
        { 9, "partopsep", '\0' },
        { 4, "perp", '1' },
        { 3, "phi", '1' },
        { 2, "pi", '1' },
        { 11, "plainoutput", '\0' },
        { 2, "pm", '1' },
        { 4, "pmod", '1' },
        { 7, "poptabs", '\0' },
        { 6, "pounds", '\0' },
        { 4, "prec", '1' },
        { 6, "preceq", '1' },
        { 5, "prime", '1' },
        { 4, "prod", '1' },
        { 6, "propto", '1' },
        { 7, "protect", '\0' },
        { 2, "ps", '\0' },
        { 3, "psi", '1' },
        { 8, "pushtabs", '\0' },
        { 3, "put", '\0' },
        { 12, "raggedbottom", '\0' },
        { 12, "raggedbottom", '\0' },
        { 10, "raggedleft", '\0' },
        { 11, "raggedright", '\0' },
        { 8, "raisebox", '\0' },
        { 6, "rangle", '1' },
        { 5, "rceil", '1' },
        { 3, "ref", '\0' },
        { 14, "refstepcounter", '\0' },
        { 12, "renewcommand", '\0' },
        { 16, "renewenvironment", '\0' },
        { 16, "reversemarginpar", '\0' },
        { 6, "rfloor", '1' },
        { 3, "rhd", '1' },
        { 3, "rho", '1' },
        { 5, "right", '1' },
        { 10, "rightarrow", '1' },
        { 16, "rightharpoondown", '1' },
        { 14, "rightharpoonup", '1' },
        { 17, "rightleftharpoons", '1' },
        { 11, "rightmargin", '\0' },
        { 2, "rm", '\0' },
        { 5, "roman", '\0' },
        { 4, "rule", '\0' },
        { 8, "samepage", '\0' },
        { 7, "savebox", '\0' },
        { 4, "sbox", '\0' },
        { 2, "sc", '\0' },
        { 10, "scriptfont", '\0' },
        { 16, "scriptscriptfont", '\0' },
        { 17, "scriptscriptstyle", '1' },
        { 10, "scriptsize", '\0' },
        { 11, "scriptstyle", '1' },
        { 7, "searrow", '1' },
        { 3, "sec", '1' },
        { 7, "section", '\0' },
        { 10, "setcounter", '\0' },
        { 9, "setlength", '\0' },
        { 8, "setminus", '1' },
        { 7, "settabs", '\0' },
        { 10, "settowidth", '\0' },
        { 7, "sevenbf", '\0' },
        { 6, "seveni", '\0' },
        { 7, "sevensy", '\0' },
        { 2, "sf", '\0' },
        { 5, "sharp", '1' },
        { 10, "shortstack", '\0' },
        { 5, "sigma", '1' },
        { 9, "signature", '\0' },
        { 9, "signature", '\0' },
        { 3, "sim", '1' },
        { 5, "simeq", '1' },
        { 3, "sin", '1' },
        { 4, "sinh", '1' },
        { 2, "sl", '\0' },
        { 6, "sloppy", '\0' },
        { 5, "small", '\0' },
        { 9, "smallskip", '\0' },
        { 15, "smallskipamount", '\0' },
        { 5, "smile", '\0' },
        { 5, "space", '\0' },
        { 9, "spadesuit", '1' },
        { 5, "sqcap", '1' },
        { 5, "sqcup", '1' },
        { 4, "sqrt", '1' },
        { 8, "sqsubset", '1' },
        { 10, "sqsubseteq", '1' },
        { 8, "sqsupset", '1' },
        { 10, "sqsupseteq", '1' },
        { 2, "ss", '\0' },
        { 8, "stackrel", '1' },
        { 4, "star", '1' },
        { 11, "stepcounter", '\0' },
        { 4, "stop", '\0' },
        { 7, "stretch", '\0' },
        { 7, "subitem", '\0' },
        { 12, "subparagraph", '\0' },
        { 10, "subsection", '\0' },
        { 6, "subset", '1' },
        { 8, "subseteq", '1' },
        { 10, "subsubitem", '\0' },
        { 13, "subsubsection", '\0' },
        { 4, "succ", '1' },
        { 6, "succeq", '1' },
        { 3, "sum", '1' },
        { 3, "sup", '1' },
        { 6, "supset", '1' },
        { 8, "supseteq", '1' },
        { 4, "surd", '1' },
        { 7, "swarrow", '1' },
        { 6, "symbol", '\0' },
        { 1, "t", '\0' },
        { 8, "tabalign", '\0' },
        { 10, "tabbingsep", '\0' },
        { 9, "tabcolsep", '\0' },
        { 15, "tableofcontents", '\0' },
        { 4, "tabs", '\0' },
        { 8, "tabsdone", '\0' },
        { 6, "tabset", '\0' },
        { 3, "tan", '1' },
        { 4, "tanh", '1' },
        { 3, "tau", '1' },
        { 4, "teni", '\0' },
        { 12, "textfloatsep", '\0' },
        { 8, "textfont", '\0' },
        { 12, "textfraction", '\0' },
        { 10, "textheight", '\0' },
        { 9, "textstyle", '1' },
        { 9, "textwidth", '\0' },
        { 6, "thanks", '\0' },
        { 3, "the", '\0' },
        { 7, "thepage", '\0' },
        { 5, "theta", '1' },
        { 10, "thicklines", '\0' },
        { 9, "thinlines", '\0' },
        { 13, "thispagestyle", '\0' },
        { 5, "tilde", '1' },
        { 5, "times", '1' },
        { 4, "tiny", '\0' },
        { 5, "title", '\0' },
        { 5, "title", '\0' },
        { 5, "today", '\0' },
        { 3, "top", '1' },
        { 11, "topfraction", '\0' },
        { 6, "topins", '\0' },
        { 9, "topinsert", '\0' },
        { 9, "topmargin", '\0' },
        { 6, "topsep", '\0' },
        { 7, "topskip", '\0' },
        { 8, "triangle", '1' },
        { 12, "triangleleft", '1' },
        { 13, "triangleright", '1' },
        { 2, "tt", '\0' },
        { 9, "twocolumn", '\0' },
        { 6, "typein", '\0' },
        { 7, "typeout", '\0' },
        { 1, "u", '\0' },
        { 10, "unboldmath", '\0' },
        { 10, "underbrace", '\0' },
        { 9, "underline", '\0' },
        { 10, "unitlength", '\0' },
        { 5, "unlhd", '1' },
        { 5, "unrhd", '1' },
        { 7, "uparrow", '1' },
        { 11, "updownarrow", '1' },
        { 5, "uplus", '1' },
        { 7, "upsilon", '1' },
        { 6, "usebox", '\0' },
        { 10, "usecounter", '\0' },
        { 1, "v", '\0' },
        { 5, "value", '\0' },
        { 10, "varepsilon", '1' },
        { 6, "varphi", '1' },
        { 5, "varpi", '1' },
        { 6, "varrho", '1' },
        { 8, "varsigma", '1' },
        { 8, "vartheta", '1' },
        { 4, "vbox", '\0' },
        { 5, "vdash", '1' },
        { 5, "vdots", '1' },
        { 3, "vec", '1' },
        { 6, "vector", '\0' },
        { 3, "vee", '1' },
        { 4, "verb", '\0' },
        { 5, "verb*", '\0' },
        { 5, "vfill", '\0' },
        { 9, "vfootnote", '\0' },
        { 5, "vline", '\0' },
        { 6, "vspace", '\0' },
        { 7, "vspace*", '\0' },
        { 5, "wedge", '1' },
        { 7, "widehat", '1' },
        { 9, "widetilde", '1' },
        { 2, "wp", '1' },
        { 2, "wr", '1' },
        { 2, "xi", '1' },
        { 4, "zeta", '1' },
        { 1, "{", '\0' },
        { 1, "|", '1' },
        { 1, "}", '\0' },
        { 1, "~", '\0' }

   };


char *Math_Environments[] = {   
   
     "math",
     "displaymath",
     "equation",
     "eqnarray",
     "eqnarray*",
     0

  };
   

int is_math_environment (keyword) char *keyword;  
{
  char **mathenvs = Math_Environments;
  while (*mathenvs != 0) {
    if (0 == strcmp(*mathenvs,keyword)) return(1);
    mathenvs++;
  }
  return(0);
}

  
int command_lookup (command) char *command;

{
  int head = 0;
  int tail = COMMAND_TABLE_SIZE - 1;
  int mid,result;
        
  while (head <= tail) {
    mid = head + (tail - head) / 2;
    result = strcmp(command,Command_Table[mid].cmd);
    if (result > 0)
       head = mid + 1;
    else if (result < 0)
       tail = mid - 1;
    else
       return(mid);
  }
  return(NOT_FOUND);
}
  
SHAR_EOF
if test 21192 -ne "`wc -c < 'cmds.c'`"
then
	echo shar: error transmitting "'cmds.c'" '(should have been 21192 characters)'
fi
fi
echo shar: extracting "'ctools.c'" '(19032 characters)'
if test -f 'ctools.c'
then
	echo shar: will not over-write existing file "'ctools.c'"
else
cat << \SHAR_EOF > 'ctools.c'
/* -*- Mode: C; Package: (CTOOLS C) -*- */

#include <ctype.h>
#include <stdio.h>

/* if your system doesn't have either string.h or strings.h you may have to */
/* declare the string functions yourself */
#ifdef BSD42
#include <strings.h>
#else
#include <string.h>
#endif

#ifdef TMC
#include "newctools.h"
#else
#include "ctools.h"
#endif

/* miscellaneous fairly primitive routines that deal with characters, */
/* strings, memory, simple input and pathnames. */
 

/* Author:  JP Massar */
/* Thinking Machines Corporation */

/* Included routines:

   emalloc
   anewstr
   
   copy
   fill
   
   to_upper_if_lower
   to_lower_if_upper
   
   buffconcat
   nbuffconcat
   
   slcompare
   slge_compare
   nocase_compare
   
   strfind
   strncfind
   strsearch
   strncsearch
   
   yes_or_no_check
   
   remove_excess_blanks
   
   all_digits
   all_whitespace
   all_uppercase
   all_lowercase
   all_alphabetic
   all_alphanumeric
   all_ascii
   
   str_to_pos_int
   
   sreverse
   ip_sreverse
        
   temp_path
   perm_path
   make_path_numeric_extension
   make_path
   just_filename
   
   read_yes_or_no
   getline
   getlines
   ngetlines
   getfile
   ngetfile
   read_file_into_buffer
   efopen

   check_int
   check_string
   
*/
   

extern char *malloc();


char *emalloc (space) int space;

/* allocate 'space' bytes, die if we have run out of memory. */

{
  char *rval;        
  if (space < 0) {
     fprintf(stderr,"Fatal error: argument to emalloc < 0\n");
     exit(-1);
  }
  if (0 == (rval = malloc((unsigned) space))) {
     fprintf(stderr,"Fatal error:  No more memory\n");
     exit(-1);
  }
  return(rval);
}  


char *anewstr (astring) char *astring;

/* allocate space for and then copy a string.  Returns pointer to */
/* new string. */

{        
  char *newstr;
  newstr = emalloc(strlen(astring)+1);
  strcpy(newstr,astring);
  return(newstr);
}


copy (dest,src,n)

  /* copy n bytes */

  register char *dest,*src;
  register int n;

  { register int j = 0;
    while (j++ < n) *dest++ = *src++; 
  }
 

fill (addr,ch,n)

  /* fill n sequential bytes with 'ch' */

  register char *addr;
  register char ch;
  register int n;
  
  { register int j = 0;
    while (j++ < n) *addr++ = ch;
  }


to_upper_if_lower (ch)

  char ch;

  { return(islower(ch) ? toupper(ch) : ch); }


to_lower_if_upper (ch)

  char ch;

  { return(isupper(ch) ? tolower(ch) : ch); }


buffconcat (buffer,s1,s2) 

  /* concatenate two null terminated strings into a buffer. */

  char *buffer, *s1, *s2;
  
  { while (*s1 != '\0') *buffer++ = *s1++;
    while (*s2 != '\0') *buffer++ = *s2++;
    *buffer = '\0';
  }


nbuffconcat (buffer,n,s1,s2,s3,s4,s5,s6)

  /* concatenates up to 6 strings into a buffer.  Returns -1 if n */
  /* is not reasonable, otherwise returns 0. */

  char *buffer;
  int n;
  char *s1,*s2,*s3,*s4,*s5,*s6;

{
  register char *b;
  register char *s;
  int i;
  b = buffer;
  if (n < 1 || n > 6) return(-1);
  for (i = 1; i <= 6; i++) {
      if (i > n) break;
      switch (i) {
        case 1 : s = s1; break;
        case 2 : s = s2; break;
        case 3 : s = s3; break;
        case 4 : s = s4; break;
        case 5 : s = s5; break;
        case 6 : s = s6; break;
      }
      while (*s != '\0') *b++ = *s++;
  }
  *b = '\0';
  return(0);
}


slcompare (s1,l1,s2,l2)

  /* compare strings with possible nulls in them given their lengths */
  /* only returns EQUAL (0) or NOT EQUAL (-1) */

  char *s1;
  int l1;
  char *s2;
  int l2;

  { int j;
    if (l1 != l2) return(-1);
    j = 0;
    while (j++ < l1) 
      if (*s1++ != *s2++) return(-1);
    return(0);
  }

  
slge_compare (s1,l1,s2,l2)

  /* returns -1 if s1 < s2; 1 if s2 < s1; 0 if s1 = s2 */
  /* ignores nulls in the strings */

  char *s1;
  int l1;
  char *s2;
  int l2;

  { int j,len;
    j = 0;
    len = l2 > l1 ? l1 : l2;
    while (j++ < len) {
      if (*s1 != *s2) 
         return((*s1 < *s2) ? -1 : 1);
      s1++;   
      s2++;
    }  
    return((l2 == l1) ? 0 : ((l1 < l2) ? -1 : 1));
  }

nocase_compare (s1,l1,s2,l2)

  /* treats nulls as normal characters.  Returns same as slge_compare */

  char *s1;
  int l1;
  char *s2;
  int l2;

  { int j,len,ch1,ch2;
    j = 0;
    len = l2 > l1 ? l1 : l2;
    while (j++ < len) {
      ch1 = to_upper_if_lower(*s1++);
      ch2 = to_upper_if_lower(*s2++);
      if (ch1 != ch2) {
         return((ch1 < ch2) ? -1 : 1);
      }
    }  
    return((l2 == l1) ? 0 : ((l1 < l2) ? -1 : 1));
  }


char *strfind(s1,s2,fast)  
  
  register char *s1;
  char *s2;
  Bool fast;
  
{  
  register int len1,len2;
  len2 = strlen(s2);
  if (fast) {
     while (*s1 != '\0')
       if (0 == strncmp(s1++,s2,len2)) return(s1-1);
  }  
  else {
     len1 = strlen(s1);
     while (len1 >= len2) {
       if (0 == strncmp(s1++,s2,len2)) return(s1-1);
       len1--;
     }
  }
  return(0);
}     


char *strncfind(s1,s2,fast)

  register char *s1;
  char *s2;
  Bool fast;
  
{  
  register int len1,len2;
  len2 = strlen(s2);
  if (fast) {
     while (*s1 != '\0')
       if (0 == nocase_compare(s1++,len2,s2,len2)) return(s1-1);
  }
  else {
     len1 = strlen(s1);
     while (len1 >= len2) {
       if (0 == nocase_compare(s1++,len2,s2,len2)) return(s1-1);
       len1--;
     }
  }
  return(0);
}  

  
char *strsearch(s1,s1len,s2,s2len)

  /* do a substring search without noticing nulls */
  /* finds s2 in s1, returns pointer into s1 or 0 */

  register char *s1, *s2;
  register int s1len,s2len;
  
  {  register char *pc;
     register char *bound;
     register char *pctemp;
     register char *s2temp;
     register int j;

     bound = s1 + s1len - s2len;
     for (pc = s1; pc <= bound; pc++) {
         pctemp = pc;
         s2temp = s2;
         for (j = 0; j < s2len; j++)
             if (*pctemp++ != *s2temp++) goto not_here;
         return(pc);       
         not_here :
         continue;
     }    
     return(0);
}


char *strncsearch(s1,s1len,s2,s2len)

  /* do a substring search without noticing nulls */
  /* finds s2 in s1, returns pointer into s1 or 0 */
  /* case independent */

  register char *s1, *s2;
  register int s1len,s2len;
  
  {  register char *pc;
     register char *bound;
     register char *pctemp;
     register char *s2temp;
     register int j;
     char ch1, ch2;

     bound = s1 + s1len - s2len;
     for (pc = s1; pc <= bound; pc++) {
         pctemp = pc;
         s2temp = s2;
         for (j = 0; j < s2len; j++) {
             ch1 = *pctemp++;
             ch2 = *s2temp++;
             if (to_upper_if_lower(ch1) != to_upper_if_lower(ch2))
                goto not_here;
         }
         return(pc);       
         not_here :
         continue;
     }    
     return(0);
}


int remove_excess_blanks (newstring,oldstring) 

  /* it is assumed that newstring is as long as oldstring if necessary */

  char *newstring,*oldstring;

{
  int count = 0;
  int space_found = 0;

  /* skip over all blanks at beginning */
  
  if (*oldstring == ' ') {
     while (*oldstring == ' ') oldstring++;
  }

  while (*oldstring != '\0') {
        if (space_found && *oldstring == ' ') {
           oldstring++;
           continue;
        }
        space_found = (*oldstring == ' ');
        *newstring++ = *oldstring++;
        count++;
  }

  *newstring = '\0';
  if (count > 0 && *(newstring - 1) == ' ') {
     count--;
     *(newstring - 1) = '\0';
  }

  return(count);

}


yes_or_no_check (astring) char *astring;

/* returns 1 if yes, 0 if no, -1 if neither */
/* works for 'Y' 'YES' 'NO' 'N' in any capitalization */

{  
  int len;
  len = strlen(astring);
  if (len == 0 || len > 3) return(-1);
  if (0 == nocase_compare(astring,len,"YES",3) || 
      0 == nocase_compare(astring,len,"Y",1))
     return(1);
  if (0 == nocase_compare(astring,len,"NO",2) || 
      0 == nocase_compare(astring,len,"N",1))
     return(0);
  return(-1);
}


Bool all_digits (astring) char *astring;

/* test whether every character is a digit (0-9) */

{
  while (*astring != '\0') 
    if (!isdigit(*astring++)) return(F);
  return(T);
}


Bool all_whitespace (astring) char *astring;

/* test whether every character is a blank or a tab */

{
  register char ch;
  while ((ch = *astring++) != '\0') {
    if (ch == ' ' || ch == '\t') continue;
    return(F);
  }
  return(T);
}

Bool all_uppercase(astring) char *astring;
{
  register char ch;
  while ((ch = *astring++) != '\0') {
    if (!isupper(ch)) return(F);
  }
  return(T);
}

Bool all_lowercase(astring) char *astring;
{
  register char ch;
  while ((ch = *astring++) != '\0') {
    if (!islower(ch)) return(F);
  }
  return(T);
}

Bool all_alphabetic(astring) char *astring;
{
  register char ch;
  while ((ch = *astring++) != '\0') {
    if (!isalpha(ch)) return(F);
  }
  return(T);
}

Bool all_ascii(astring) char *astring;
{
  register char ch;
  while ((ch = *astring++) != '\0') {
    if (!isascii(ch)) return(F);
  }
  return(T);
}

Bool all_alphanumeric(astring) char *astring;
{
  register char ch;
  while ((ch = *astring++) != '\0') {
    if (!isalnum(ch)) return(F);
  }
  return(T);
}

int str_to_pos_int (astring,low,high) char *astring; int low,high;

  /* returns -1 if *astring is not composed of digits. */
  /* returns -2 if the integer is out of range. */
  /* treats all digit strings as decimal. */

{
  int value,len,maxlen,j;
  maxlen = strlen(MAXINTSTR);
  len = strlen(astring);
  if (!all_digits(astring)) return(-1);
  if (len > maxlen) return(-2);
  if (len == maxlen) {
     if (1 == strcmp(astring,MAXINTSTR)) return(-2);
  }
  for (j = 0; j < len-1; j++) {
      if (*astring != '0') break;
      astring++;
  }
  sscanf(astring,"%d",&value);
  if (value < low || value > high) return(-2);
  return(value);
}


int sreverse (buffer,astring) char *buffer, *astring;
{
  register int last = strlen(astring);
  buffer[last--] = '\0';
  while (last >= 0) buffer[last--] = *astring++;
}

char * ip_sreverse (astring) char *astring;
{
  register int last = strlen(astring) - 1;
  register int first = 0;
  register char ch;
  while (first < last) {
    ch = astring[first];
    astring[first++] = astring[last];
    astring[last--] = ch;
  }
  return(astring);
}



static char pathbuffer[PATH_MAXPATHLEN];


char *temp_path (dir,filename) char *dir; char *filename;

{
  return(make_path(dir,filename,"",F));
}


char *perm_path (dir,filename) char *dir; char *filename;

{
  return(make_path(dir,filename,"",T));
}


char *make_path_numeric_extension (dir,filename,extension,perm)

  char *dir, *filename;
  int extension;
  Bool perm;

{
  char buffer[20];
  sprintf(buffer,"%d",extension);
  return(make_path(dir,filename,buffer,perm));
}


char *make_path (dir,filename,extension,perm)

  char *dir, *filename, *extension;
  Bool perm;

{
  char *rval;
  if (!perm && (strlen(dir) + 1 + strlen(filename) + strlen(extension) + 1 >=
                PATH_MAXPATHLEN)) {
     return((char *) 0);
  }
  nbuffconcat(pathbuffer,4,dir,"/",filename,extension);
  if (!perm) return(pathbuffer);
  rval = emalloc(strlen(pathbuffer) + 1);  
  strcpy(rval,pathbuffer);
  return(rval);
}


char *just_filename (path,new,perm) char *path; Bool new,perm;

{
  char *fnp,*rval;
  fnp = (0 == (fnp = rindex(path,'/'))) ? path : fnp + 1;
  if (!new) return(fnp);
  if (!perm) {
     strcpy(pathbuffer,fnp);
     return(pathbuffer);
  }
  else {
     rval = emalloc(strlen(fnp) + 1);
     strcpy(rval,fnp);
     return(rval);
  }
}



read_yes_or_no (iport,oport,prompt,helpstring,quitstring)

  /* prints prompt, then reads from port until it gets 'Y', 'N', 'YES' or */
  /* 'NO' (case independently).  If helpstring and/or quitstring are not */
  /* "" or (char *) 0 then if the user types in one of those ANSWER_HELP */
  /* or ANSWER_QUIT are returned, otherwise ANSWER_NO or ANSWER_YES are */
  /* eventually returned. */

  FILE *iport, *oport;
  char *prompt, *helpstring, *quitstring;

{
  char buffer[20],buffer2[20];
  int bl,hl,ql,len;
  
  buffer[19] = '\0';
  
  while (T) {
        
    fprintf(oport,"%s",prompt);
    switch (len = getline(iport,buffer,20)) {
      case (AT_EOF) :
        return(ANSWER_EOF);
        break;
      case (TOO_MANY_CHARS) :
        break;
      default :
        if (0 == (bl = remove_excess_blanks(buffer2,buffer))) break;
        switch (yes_or_no_check(buffer2)) {
          case (0) :
            return(ANSWER_NO);
          case (1) :
            return(ANSWER_YES);
          case (-1) :
            if (helpstring != (char *) 0 && (hl = strlen(helpstring)) > 0) {
               if (0 == nocase_compare(buffer2,bl,helpstring,hl)) {
                  return(ANSWER_HELP);
               }
            }
            if (quitstring != (char *) 0 && (ql = strlen(quitstring)) > 0) {
               if (0 == nocase_compare(buffer2,bl,quitstring,ql)) {
                  return(ANSWER_QUIT);
               }
            }
            break;
        }   
        break;
    }
   
    fprintf(oport,"Please answer 'YES' or 'NO'\n");
    continue;
   
  }
    
}


int getline (iport,buffer,buflen) FILE *iport; char *buffer; int buflen;

  /* reads a line into buffer.  Does not put the '\n' into buffer. */
  /* Returns AT_EOF if at end of file when called.  If it encounters */
  /* end of file after reading at least one character, the eof is treated */
  /* as if it were a newline.   Returns TOO_MANY_CHARS if more than */
  /* buflen - 1 characters are read before encountering a newline. */        
  /* In this case exactly buflen - 1 characters are read. */
  /* The last character read is always follwed by a '\0'. */
  /* if successful getline returns the number of characters read exclusive */
  /* of a terminating newline or eof. */

{
  int ch;
  char *bptr = buffer;
  int nchars = 0;
  
  if (buflen <= 0) return(TOO_MANY_CHARS);
  
  while (T) {
    switch (ch = getc(iport)) {
      case (EOF) :
      case ('\n') :
        if (ch == EOF && nchars == 0) return(AT_EOF);
        *bptr = '\0';
        return(nchars);
      default :
        if (++nchars == buflen) { 
           *bptr = '\0';
           ungetc(ch,iport);
           return(TOO_MANY_CHARS);
        }
        *bptr++ = ch;
    }
    
  }
    
}


int getlines (fp,n,ptr_lines,linebuf,maxlinelen)

  /* See documentation for getfile below */

  FILE *fp;
  int n;
  char ***ptr_lines;
  char *linebuf;
  int maxlinelen;

{
  int len;
  char *line;
  if (0 > (len = getline(fp,linebuf,maxlinelen))) {
     if (len == AT_EOF) {
        *ptr_lines = (char **) emalloc(n * sizeof(char **));
        return(n);
     }
     else {
        return(TOO_MANY_CHARS);
     }
  }
  else {
     line = emalloc(len+1);
     strcpy(line,linebuf);
     len = getlines(fp,n+1,ptr_lines,linebuf,maxlinelen);
     if (len == TOO_MANY_CHARS) return(TOO_MANY_CHARS);
     (*ptr_lines)[n] = line;
     return(len);
  }
}


int getfile (filename,ptr_lines,linebuf,maxlinelen)

  /* read in a file as an array of character strings */
  /* 'maxlinelen+1' is the maximum length a line of the file is allowed */
  /* to be.  'linebuf' must be at least 'maxlinelen+1' characters long. */
  /* Returns the number of lines in the file (and therefore the number */
  /* of entries in *ptr_lines) if successful.  Returns IOERROR if it */
  /* could not open the file to read from. Returns TOO_MANY_CHARS if */
  /* it encounters a line longer than 'maxlinelen' characters. */

  /* Space for each line is malloc'ed as it is read in and the text for */
  /* the jth line is stored in (*ptr_lines)[j] */

  char *filename;
  char ***ptr_lines;
  char *linebuf;
  int maxlinelen;

{
  FILE *fp;
  int nlines;
  if (NULL == (fp = fopen(filename,"r"))) return(IOERROR);
  nlines = getlines(fp,0,ptr_lines,linebuf,maxlinelen);
  fclose(fp);
  return(nlines);
}


int ngetlines (fp,n,ptr_lines,linebuf,maxlinelen)

  /* See documentation for ngetfile below */

  FILE *fp;
  int n;
  char ***ptr_lines;
  char *linebuf;
  int maxlinelen;

{
  int len;
  int nlines = 0;
  *ptr_lines = (char **) emalloc(n * sizeof(char **));
  while (T) {
    if (0 > (len = getline(fp,linebuf,maxlinelen))) {
       if (len == AT_EOF) {
          return(nlines);
       }
       else {
          return(TOO_MANY_CHARS);
       }
    }
    else {
       if (++nlines > n) {
          return(TOO_MANY_LINES);
       }
       (*ptr_lines)[nlines-1] = anewstr(linebuf);
    }
  }
}



int ngetfile (n,filename,ptr_lines,linebuf,maxlinelen)

  /* Same as getfile except that at most n lines will be read. */
  /* If it attempts to read more than n lines, TOO_MANY_LINES will */
  /* be returned. */

  int n;
  char *filename;
  char ***ptr_lines;
  char *linebuf;
  int maxlinelen;

{
  FILE *fp;
  int nlines;
  if (NULL == (fp = fopen(filename,"r"))) return(IOERROR);
  nlines = ngetlines(fp,n,ptr_lines,linebuf,maxlinelen);
  fclose(fp);
  return(nlines);
}


extern int read_file_into_buffer (

       filename,ptr_lines,maxlines,buffer,buflen,linebuffer,linebuflen

    )
       
  char *filename; 
  char ***ptr_lines;
  int maxlines;
  char *buffer;
  int buflen;
  char *linebuffer;
  int linebuflen;

  /* *ptr_lines should be an array of character string pointers maxlines */
  /* big.  buffer should be an array of characters buflen long.  The routine */
  /* reads lines using getline and stores them into buffer, terminating each */
  /* with a null.  A pointer to the nth line read is stored in *ptr_lines[n] */
  /* Returns IOERROR if it cannot open the file for reading, TOO_MANY_LINES */
  /* if more than maxlines were read in, TOO_MANY_CHARS if buffer is */
  /* filled before end of file is reached, and LINE_TOO_LONG is any line is */
  /* longer than linebuflen.  Returns number of lines read in if successful. */
  
{  
  FILE *fp;
  int linecount,charcount,len;
  char *bp;
  char **lines;
  
  if (NULL == (fp = fopen(filename,"r"))) return(IOERROR);
  linecount = 0;
  charcount = 0;
  bp = buffer;
  lines = *ptr_lines;
  
  while (T) {
        
    if (0 > (len = getline(fp,linebuffer,linebuflen))) {
       fclose(fp);
       if (len == AT_EOF) {
          return(linecount);
       }
       else {
          return(LINE_TOO_LONG);
       }
    }
    
    if (linecount >= maxlines) {
       fclose(fp);
       return(TOO_MANY_LINES);
    }
    
    charcount += len;
    if (charcount >= buflen) {
       fclose(fp);
       return(TOO_MANY_CHARS);
    }
    
    strcpy(bp,linebuffer);
    lines[linecount++] = bp;
    bp += (len + 1);
  
  }
  
}
  
extern char *efopen (filename,mode) char *filename; char *mode;

  /* The routine simply calls fopen with the same arguments, but prints a */
  /* reasonable error message and calls exit if the call to fopen fails. */

{
  FILE *fp;
  if (NULL == (fp = fopen(filename,mode))) {
     fprintf(stderr,"Could not open %s, mode: %s\n",filename,mode);
     perror("Reason: ");
     exit(1);
  }
  return((char *) fp);
}


Bool check_string (s,minlen,maxlen) char *s; long minlen,maxlen;
{
  long len;
  if (s == 0) return(F);
  len = strlen(s);
  return (len >= minlen && len <= maxlen);
}

SHAR_EOF
if test 19032 -ne "`wc -c < 'ctools.c'`"
then
	echo shar: error transmitting "'ctools.c'" '(should have been 19032 characters)'
fi
fi
echo shar: extracting "'errors.c'" '(4039 characters)'
if test -f 'errors.c'
then
	echo shar: will not over-write existing file "'errors.c'"
else
cat << \SHAR_EOF > 'errors.c'
#include <stdio.h>
#ifdef TMC
#include <ctools.h>
#else
#include "ctools.h"
#endif
#include "texchk.h"


print_error_line ()
{
  int j;        
  fprintf(stderr,"Error on line number %d\n",Current_Line);        
  fprintf(stderr,"\n%s\n",Line_Buffer);
  for (j = 0; j < Current_Char-1; j++) fprintf(stderr," ");
  fprintf(stderr,"^\n\n");
}


eof_error ()
{  
  fprintf(stderr,"\nActual end of file before logical end of input.\n");
  fprintf(stderr,"Current environment:\n");
  print_stack();
}

stack_empty_error (etype,keyword) envtype etype; char *keyword;

{
  char *s, *e;
  switch (etype) {
    case ESCAPE_END :
      s = "\\begin";
      e = "\\end";
      break;
    case RIGHT_SQUARE_BRACKET :
      s = "[";
      e = "]";
      break;
    case RIGHT_CURLY_BRACKET :
      s = "{";
      e = "}";
      break;
    default :
      if (*keyword == ')') {
         s = "\\(";
         e = "\\)";
         break;
      }
      else if (*keyword == ']') {
         s = "\\[";
         e = "\\]";
         break;
      }
      fprintf(stderr,"Impossible etype argument to stack_empty_error\n");
      exit(1);
  }
  fprintf(stderr,"\nNo matching %s to go with %s\n",s,e);
  print_error_line();
}


no_brace_after_begin_end_error ()
{
  fprintf (
     stderr,
     "\n'\\begin' or '\\end' construct has no '{' immediately following\n"
   );
  print_error_line();
}

warning_blanks_in_cb ()
{  
  fprintf(stderr,"\nWarning:  probable error.\n");        
  fprintf(stderr,"LaTeX does not like initial or terminating blanks\n");
  fprintf(stderr,"inside of \\begin{} or \\end{} constructs\n");
  print_error_line();
}

warning_close_brace () 
{
  fprintf(stderr,"\nWarning:  texchk cannot handle environment names that\n");
  fprintf(stderr,"span more than one line...\n");
  print_error_line();
}  

bad_char_error (ch,abort) int ch; Bool abort;
{
  fprintf(stderr,"\nIllegal character: integer value = %d\n",ch);
  print_error_line();
  if (abort) exit(1);
}  

line_too_long_error ()
{
  fprintf(stderr,"\nToo many characters: line = %d\n",Current_Line);
}

blank_begin_end_error ()
{
  fprintf(stderr,"\nNo environment defined in '\\begin{}' or '\\end{}'\n");
  print_error_line();
}

keyword_error (keyword) char *keyword;
{
  fprintf(stderr,"\nWarning: unrecognized command name: \\%s\n",keyword);
  print_error_line();
}

math_keyword_error (keyword) char *keyword;
{
  fprintf (
      stderr,
      "\nError: 'Math mode only' command used outside math mode: \\%s\n",
      keyword
   );
   print_error_line();
}


nest_error (s,e,oldlinenum,current_key) 
  char *s, *e;
  int oldlinenum;
  char *current_key;
{
  fprintf(stderr,"\nNo matching %s for %s at current nesting level\n",s,e);
  print_error_line();
  fprintf (
     stderr,"Current nesting is '%s' at line %d\n",current_key,oldlinenum
   );
}


keyword_length_error ()
{
  fprintf(stderr,"\nLaTeX command too long\n");
  print_error_line();
}


single_char_command_error (ch) char ch;
{
  fprintf(stderr,"\nThe command \\%c is not legal LaTeX\n",ch);
  print_error_line();
}


print_stack ()

{
  int j;        
  fprintf(stderr,"\n\nTEXCHK ENVIRONMENT STACK:\n\n");
  for (j = 0; j <= Lex_TOS; j++) {
      switch (Lex_Stack[j].etype) {
        case ESCAPE_BEGIN :
          fprintf(stderr,"\\begin{%s}\n",Lex_Stack[j].keyword);
          break;
        case LEFT_SQUARE_BRACKET :
          fprintf(stderr,"[\n");
          break;
        case LEFT_CURLY_BRACKET :
          fprintf(stderr,"{\n");
          break;
        case MATH :
          fprintf(stderr,"Math Mode: %s\n",Lex_Stack[j].keyword);
          break;
        case DOUBLE_MATH :
          fprintf(stderr,"Display Math Mode\n");
          break;
        default :
          fprintf(stderr,"Stack corrupted...\n");
          texit();
      }
  }
  fprintf(stderr,"\n");
}


eof_verbatim_error ()
{
  fprintf(stderr,"\nError: EOF in middle of verbatim/verb environment\n");
}


verb_error (ch) char ch;
{
  fprintf(stderr,"Error: Illegal character after \\verb command: %c\n",ch);
  print_error_line();
}
SHAR_EOF
if test 4039 -ne "`wc -c < 'errors.c'`"
then
	echo shar: error transmitting "'errors.c'" '(should have been 4039 characters)'
fi
fi
echo shar: extracting "'texchars.c'" '(599 characters)'
if test -f 'texchars.c'
then
	echo shar: will not over-write existing file "'texchars.c'"
else
cat << \SHAR_EOF > 'texchars.c'
#include "texchars.h"

static char *Latex_Chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.:;,?!`'()[]-/*@#$%&~_^\\{}+=|<>\"\t \n";

static char *Lgl_Command_Chars = " !\"#$%&'()+,-./:;<=>@[\\]^_`{|}~";

char Lgl_Chars[256];
char Lgl_Single_Char_Commands[256];

init_legal_chars () 
{
  int j;
  char *lc = Latex_Chars;
  char *ls = Lgl_Command_Chars;
  for (j = 0; j < 256; j++) { 
      Lgl_Chars[j] = 0;
      Lgl_Single_Char_Commands[j] = 0;
  }
  while (*lc != '\0') Lgl_Chars[*lc++] = '1';  
  while (*ls != '\0') Lgl_Single_Char_Commands[*ls++] = '1';  
  return(1);
}
SHAR_EOF
if test 599 -ne "`wc -c < 'texchars.c'`"
then
	echo shar: error transmitting "'texchars.c'" '(should have been 599 characters)'
fi
fi
echo shar: extracting "'verbatim.c'" '(2479 characters)'
if test -f 'verbatim.c'
then
	echo shar: will not over-write existing file "'verbatim.c'"
else
cat << \SHAR_EOF > 'verbatim.c'
#include <stdio.h>
#include <ctype.h>

#ifdef TMC
#include <ctools.h>
#else
#include "ctools.h"
#endif

#include "texchk.h"

extern Bool Verbose_Mode;

do_verbatim (keyword) char *keyword;
{
  if (0 == strcmp("verbatim",keyword))
     find_end_verbatim("\\end{verbatim}");
  else if (0 == strcmp("verbatim*",keyword))        
     find_end_verbatim("\\end{verbatim*}");
  else {
     fprintf(stderr,"Fatal error, bad argument to do_verbatim: %s\n",keyword);
     exit(1);
  }
  if (Verbose_Mode) {
     do_indent(--Indent_Level);
     fprintf(stderr,"line %d: \\end{%s}\n",Current_Line,keyword);
  }
}


do_verb () 

/* get the next character after the 'verb' or 'verb*' command, then read */
/* until we find that character again. */

{
  char endchar[2];
  int ch;
  ch = get_a_char();
  if (ch == EOF) {
     eof_verbatim_error();
     exit(1);
  }
  else if (isalpha(ch) || ch == '*' || ch == ' ' || ch == '\n') {
     verb_error(ch);
     exit(1);
  }
  endchar[0] = ch;
  endchar[1] = '\0';
  find_end_verbatim(endchar);
}


find_end_verbatim (matchstring) char *matchstring;

/* Read characters until the exact characters constituting matchstring show */
/* up in the input stream, then return.  Matchstring may not contain '\n' */

{
  static char Verbatim_Buffer[MAXLL];
  char *vbptr;
  int j,ch;
  int matchlen;
  
  matchlen = strlen(matchstring);
  
  /* matches cannot span lines, since matchstring cannot contain '\n' */
  
  newline:
  
  for (j = 0; j < MAXLL; j++) Verbatim_Buffer[j] = '\0';
  vbptr = Verbatim_Buffer; 
  
  /* Nothing can possibly match until we have read in as many characters as */
  /* there are in matchstring, so read that many in. */
  
  for (j = 0; j < matchlen; j++) {
      switch (ch = get_a_char()) {
        case '\n' :
          goto newline;
          break;
        case EOF :
          eof_verbatim_error();
          exit(1);
          break;
        default :
          Verbatim_Buffer[j] = ch;
          break;
      }
  }

  /* check for a match, then read in the next character.  Keep checking */
  /* for a match against the last 'matchlen' characters. */
  
  j = matchlen;
  while (1) {
    if (0 == strcmp(matchstring,vbptr)) {
       return(1);
    }
    switch (ch = get_a_char()) {
      case '\n' :
        goto newline;
        break;
      case EOF :
        eof_verbatim_error();
        exit(1);
        break;
      default :
        Verbatim_Buffer[j] = ch;
        break;
    }
    j++;
    vbptr++;
  }
}
SHAR_EOF
if test 2479 -ne "`wc -c < 'verbatim.c'`"
then
	echo shar: error transmitting "'verbatim.c'" '(should have been 2479 characters)'
fi
fi
exit 0
#	End of shell archive