/* l2xlib.c    Main and support routines for ltx2x LaTeX to X translator */
/* Written by Peter Wilson (ex Catholic University and NIST)             */
/*            ex pwilson@cme.nist.gov                                    */
/*            Now at: peter.r.wilson@boeing.com                          */
/*            Version 0.5, October 1995                                  */
/*            Extended July 1996                                         */
/*            Extended November 1996                                     */
/*            Extended January 1997                                      */
/*            Extended March 1999                                        */
/*            Fixed CHAR_COMMAND bug November 1999                       */
/*            Eliminated some gcc whines November 1999                   */
/*-----------------------------------------------------------------------*/

char FILE_VERSION[] = "Version 0.92";
char FILE_DATE[] = "November 1999";

/* VERSION HISTORY:
 * Version 0.2 (January 1995): First alpha release
 *         Patch 1 (April 1995): Fixed problem with the hex escape sequence
 *                               in function "delete_quotes"
 *                     NB. gcc compiler gives warnings, but cc does not.
 * Version 0.5 (October 1995): Beta release
 *         Added capability for TEX_CHAR commands to take parameters
 *     -- this was requested for processing super- and sub-scripts.
 *         Changed calling parameters of functions get_opttag_t and
 *     get_opttag_et from (pos,num) to (pos).
 *         Major restructuring and renaming of source files.
 *         Added capability for a second method of specifying SPECIALs
 *     -- now either in the grammer (as before), or via code additions
 *     to the provided grammer actions. This later avoids re-yaccing
 *     the grammer. Both require recompilation.
 * Version 0.6 (July 1996)
 *        Added capability for read/write to external files and user buffers
 * Version 0.7 (November 1996)
 *        Added the SWITCH_XXX capability
 * Version 0.8 (January 1997)
 *        Added code capability
 *        Replaced the most of the tedious enumeration/string mappings
 *     by cunning use of the l2xlibtc.h include file and defines.
 * Version 0.9 (March 1999)
 *        Changed the close_down procedure 
 * Version 0.91 (November 1999)
 *        Fixed bug in CHAR_COMMAND processing (ignored parameters)
 * Version 0.92 (November 1999) 
 *        (at request of Thomas Ruedas ruedas@geophysik.uni-frankfurt.de)
 *        Added -h option (some getopts don't return ? if unknown option)
 *        Deleted unused init_page_header and print_page_header from l2xiscan.c
 *        Changed SR order in l2xirexp to stop gcc whine
 */

/* Development of this software upto and including version 0.7
 * (November 1996) was funded by the United States Government,
 * and is not subject to copyright. This version is released
 * under the LaTeX Project Public License.
 */

/* National Institute of Standards and Technology (NIST)
 * Manufacturing Engineering Laboratory (MEL)
 * Manufacturing Systems Integration Division (MSID)
 * ********************************************************************
 *                            D I S C L A I M E R
 *  
 * There is no warranty for the LTX2X software.
 * If the LTX2X software is modified by someone else and passed on,
 * NIST wants the software's recipients to know that what they 
 * have is not what NIST distributed.
 * 
 * Policies
 * 
 * 1. Anyone may copy and distribute verbatim copies of the 
 *    source code as received in any medium.
 * 
 * 2. Anyone may modify your copy or copies of the LTX2X source
 *    code or any portion of it, and copy and distribute such modifications
 *    provided that all modifications are clearly associated with the entity
 *    that performs the modifications.
 * 
 * NO WARRANTY
 * ===========
 * 
 * NIST PROVIDES ABSOLUTELY NO WARRANTY.  THE LTX2X SOFTWARE
 * IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS
 * WITH YOU.  SHOULD ANY PORTION OF THE LTX2X SOFTWARE PROVE DEFECTIVE,
 * YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
 * 
 * IN NO EVENT WILL NIST BE LIABLE FOR DAMAGES,
 * INCLUDING ANY LOST PROFITS, LOST MONIES, OR OTHER SPECIAL,
 * INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR
 * INABILITY TO USE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA
 * BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY THIRD PARTIES OR A
 * FAILURE OF THE PROGRAM TO OPERATE WITH PROGRAMS NOT DISTRIBUTED BY
 * NIST) THE PROGRAMS, EVEN IF YOU HAVE BEEN ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGES, OR FOR ANY CLAIM BY ANY OTHER PARTY.
 */

#include <string.h>
#include "getopt.h"
#include <stdio.h>
#include <ctype.h>
#ifndef STRTYPES_H
#include "strtypes.h"
#endif
/* typedef char *STRING;     A pointer-to-a-char */
/* typedef STRING *PTRADR;   A pointer-to-a-pointer-to-a-char */
/* define SNUL '\0'          end of a string */
#ifndef L2XCOM_H
#include "l2xcom.h"
#endif
#include "l2xytab.h"

  /* the symbol table typedefs */
#ifndef licomsym_h
#include "licomsym.h"
#endif

# define MAX_ERRORS 10

FILE *yyin;                   /* Lexer input file */
FILE *filerr;                        /* Error file */
FILE *yyout;                  /* Lexer/Parser output file */
FILE *filtabin;                      /* Table input file */
FILE *filtabout;                     /* Table output file */

int MIN_GRAMM_SPECIAL = 10000;     /* min code number for grammer special */
int MIN_CODE_SPECIAL = 50000;      /* min code number for code special */

int LDEBUG = 0;                    /* >0 for debugging lexer */
int TDEBUG = FALSE;                /* for debugging command table */
int YDEBUG = 0;                    /* >0 for debugging parser */
int DEBUG = 0;                     /* >0 for debugging interpreter */
int cdebug = TRUE;                 /* set FALSE to disable inteprreter Code debug */
int edebug = TRUE;                 /* set FALSE to disable interpreter Execution debug */
int cl_debug = 0;                  /* command line value for DEBUG */
int SLD_OFF = TRUE;                /* set FALSE to enable interpreter SLD */


int num_errors = 0;                /* number of errors */
int lineno = 1;                    /* input file line number */
# define MAX_LINE 2000
# define MAX_BUFFER 2000
char linebuf[MAX_LINE];            /* buffer for input line  */
int linlen = 0;                    /* current no of chars in linebuf */
int line_count = 0;                /* count of line printing */
# define EVERY_N_LINES 100
int darray[10];                     /* dummy array for something */

int leave_comments = FALSE;        /* control for leaving in TeX comments */
int collapse_ws = FALSE;           /* control for collapsing whitespace */
int pretty_print = FALSE;          /* control for pretty-printing */
int max_pretty_line = MAX_BUFFER;  /* max length of pretty-print line */
char pretty_line[MAX_BUFFER];      /* char buffer for pretty-printing */

int no_print = FALSE;              /* a control for myprint */
int print_to_buffer = FALSE;       /* a control for myprint */
char buffer[MAX_BUFFER];           /* character buffer for myprint */


/*---------------------6/96 extensions-----------------------------*/

  /* number of user's string  buffers */
# define MAX_USER_BUFFS 64
  /* max number of user's files */
# define MAX_USER_FILES 16
  /* length of a user buffer */
/* # define MAX_UBUFF_LEN 514 */
# define MAX_UBUFF_LEN 1026

typedef char UBUFF[MAX_UBUFF_LEN];
UBUFF user_buffs[MAX_USER_BUFFS]; /* array of user buffers */
char errstr[MAX_UBUFF_LEN];      /* buffer for assembling error/warning text */

int cur_user_buf = 0;  /* number of current user buffer for writing to */
int num_ubufwrite = 0;    /* number of open write to user buffer calls */
int num_ubuff[MAX_USER_BUFFS];   /* no of times user buffer opened */
int used_rubuff[MAX_USER_BUFFS];  /* >0 if used_ubuff[N] has been read */
int used_wubuff[MAX_USER_BUFFS];  /* >0 if used_ubuff[N] has been written */
int written_from_buff[MAX_USER_BUFFS];  /* TRUE if read from user buffer */

STRING cur_user_fn = NULL; /* name of current user file for writing to */
int num_fileprint = 0;    /* number of open write to file calls */
int num_filep[MAX_USER_FILES];  /* number of opened to write for file */
FILE *ufp[MAX_USER_FILES];      /* user file pointer */
STRING ufn[MAX_USER_FILES];     /* user file name */
int cur_user_fpos = 0;          /* current pos in file array */
FILE *cur_user_fileid = NULL;   /* current user file id */

int num_otherprint = 0;   /* total number of non-default open write calls */
# define SET_NUM_OTHERPRINT (num_noprint + num_bufprint + num_ubufwrite + num_fileprint)
PSTRWC cur_printc = NULL; /* current print control */

int bverb = FALSE;             /* TRUE if starting verbatim print */

int in_noop = FALSE;           /* TRUE if doing a no op */
int start_noop = FALSE;        /* TRUE if start of a no op */

#define MAX_MODES 32
STRING mode_names[MAX_MODES];               /* unordered list of mode names */
int num_modes = 0;                          /* number of mode names */
#define MAX_MODE_STACK 20
int mode_stack[MAX_MODE_STACK];             /* stack for mode ids */
int top_mode = 0;                           /* empty top of mode stack */

/* keywords */

  /* command table commands */
enum key_word{
#define kwtc(a, b) a,
#define lctc(a, b)
#define pctc(a, b)
#include "l2xlibtc.h"
#undef kwtc
#undef lctc
#undef pctc
};

int max_keystr = MAX_KEYSTR;

  /* map from keyword enum to string representation */
STRING key_array[] = {
#define kwtc(a, b) b,
#define lctc(a, b)
#define pctc(a, b)
#include "l2xlibtc.h"
#undef kwtc
#undef lctc
#undef pctc
};

  /* map from print control enum to string representation */
int max_pcstr = MAX_PCSTR;

STRING pc_array[] = {
#define kwtc(a, b) 
#define lctc(a, b)
#define pctc(a, b) b,
#include "l2xlibtc.h"
#undef kwtc
#undef lctc
#undef pctc
};


/* the types of LaTeX commands */

#define UNKNOWN_CTYPE -10
/* number of names/enums in command types, update when ctnames changes!! */
#define NUM_CTIDS 50
  /* ctnames[] is an alphabetical array of command type strings */
STRING ctnames[] = {
#define kwtc(a, b) 
#define lctc(a, b) b,
#define pctc(a, b)
#include "l2xlibtc.h"
#undef kwtc
#undef lctc
#undef pctc
};
  /* ctenums[] is an array of command type enums to match ctnames[] */
int ctenums[] = {
#define kwtc(a, b) 
#define lctc(a, b) a,
#define pctc(a, b)
#include "l2xlibtc.h"
#undef kwtc
#undef lctc
#undef pctc
};




  /* pointers to fixed, predefined print control structs */
PSTRWC p_default_print;           /* default printing i.e., DEFAULT_PRINT */
PSTRWC p_no_print;                /* no printing i.e., NO_PRINT */
PSTRWC p_print_to_sysbuf;         /* print to system buffer i.e., TO_SYSBUF */
PSTRWC p_print_underflow;         /* underflow i.e., PRINT_UNDERFLOW */
PSTRWC p_unknown_print;           /* unknown print i.e., UNKNOWN_PRINT */
PSTRWC p_print_from_sysbuf;       /* print from system buffer i.e., SYSBUF */
PSTRWC p_print_null;              /* print null string i.e.,  */
PSTRWC p_reset_print;             /* reset printing i.e., RESET */
PSTRWC p_noop_print;              /* no-op i.e., NO_OP */

/* new functions */
PSTRWC create_st_rwc();
void init_common_print();
PSTRWC create_print_tag();
PSTRWC create_print_source();
PSTRWC parse_pc();
void tprint_st_rwc();
void tprint_tag();
void set_pc_enum();
int get_pc_enum();
void set_pcdata_num();
int get_pcdata_num();
void set_pcdata_name();
STRING get_pcdata_name();
PSTRWC get_next_write();
PSTRWC append_to_write();
PSTRWC initialise_pc();
FILE *ufopenr();
FILE *ufopenw();
int ufclose();
PSTRWC get_start_pc();
PSTRWC get_end_pc();
void flush_pretty();
int pretty_empty();
void strtouc();                /* convert string to upper case */
void tprint_str();
int lookup_string();  /* finds posn. of string in an ordered array of strings */
void init_print_control();
int key_to_int();
STRING key_to_string();
void set_pc_cmd();
int get_pc_cmd();
int chk_bufnum();
STRING chk_ufname();
void init_ubuff();            /* initialises a user buffer */
void init_ufile();            /* initialise a user file */
STRING get_mode_str();        /* returns string name of a mode */
void add_to_modelist();       /* adds a mode to the mode list */
void set_mode_name();         /* puts mode id into sym entry */
int get_mode();               /* gets mode id from sym entry */
PSENTRY get_next_mode();      /* gets next mode of a sym entry */
int isempty();                /* TRUE if an empty string */
void push_mode();              /* pushes mode onto stack */
int pop_mode();               /* pops mode from stack */
int reset_mode();             /* resets the mode */
int get_current_mode();        /* gets the current mode */
PSENTRY get_mode_sym();        /* gets sym entry matching current mode */
void tag_print();              /* print a tag */


    /*-------- 7/96 statistics stuff ---------------------------*/

int used_ubuff_chars = 0;  /* max # of chars attempted for buffer storage */
int used_sbuff_chars = 0;  /* max # of chars attempted for system buffer */
int used_files = 0;                /* # of user files used */

int used_clause_stack = 0;  /* max clause stack depth usage */
int used_list_stack = 0;    /* list stack */

int used_table_line = 0;    /* max length of a table line */
int used_table_entries = 0; /* # of entries in command table */

int used_print_stack = 0;   /* max depth of print stack */

int used_ct_file_stack = 0; /* max depth of command table stack */

void write_stats();         /* write statistics */
int maxof();                /* max of two integers */


/*--------------------------------------------------------------*/

# define CLAUSE_STACK_SIZE 10
int clause_depth = 0;
/* 6/96 changed STRING to PSTRWC */
PSTRWC clause_str_stack[CLAUSE_STACK_SIZE];
int base_level = CLAUSE_STACK_SIZE - 1; /* Highest doc level not closed off */
                                    /* Section is highest and s4clause lowest */
int current_level = 0;                  /* level of current doc division */
int new_level = 0;

# define DEF_LIST_STACK_SIZE 10
int LIST_STACK_SIZE = DEF_LIST_STACK_SIZE;
int list_level = 0;
int num_items[DEF_LIST_STACK_SIZE];      /* holds number of current items in a list */
/* 6/96 in following changed STRING to PSTRWC */
PSTRWC list_str_stack[DEF_LIST_STACK_SIZE];  /* holds closing string for an item */
PSTRWC list_item_start[DEF_LIST_STACK_SIZE]; /* holds start string for an item */
PSTRWC list_descitemp_start[DEF_LIST_STACK_SIZE]; /* holds start string for the */
                                   /* parameter of a descriptive list item */
PSTRWC list_descitemp_end[DEF_LIST_STACK_SIZE];  /* holds corresponding end string */
          


char *strsave();
void myprint();
void pprint();
void verbatim_print();
void initialise_clause_stack();
void set_clause_stack();
void close_all_divs();
void close_doc_divs();
void close_down();
void set_list_stack();
void print_end_item();
void yyerror();
void warning();
void catl();
void do_newline();
void initialise_sysbuf();
void print_sysbuf();
void copy_sysbuf();
void initialise_string();


            /* new stuff for l2x */


enum opt_pos_enum {NO_OPT_PARAM = -1, FIRST = 0, LAST};
enum sect_level_enum {UNKNOWN_LEVEL, PARTM2, PARTM1, PART,
                      CHAPTER, SECT, SUBSECT, SUBSUBSECT, PARA, SUBPARA,
                      SUBPARAP1, SUBPARAP2};
enum cont_enum {CONT_UNKNOWN_ENUM,
                CONT_TAG,
                CONT_START_TAG, CONT_END_TAG,
                CONT_START_OPT, CONT_END_OPT,
                CONT_START_TAG_1, CONT_END_TAG_1,
                CONT_START_TAG_2, CONT_END_TAG_2,
                CONT_START_TAG_3, CONT_END_TAG_3,
                CONT_START_TAG_4, CONT_END_TAG_4,
                CONT_START_TAG_5, CONT_END_TAG_5,
                CONT_START_TAG_6, CONT_END_TAG_6,
                CONT_START_TAG_7, CONT_END_TAG_7,
                CONT_START_TAG_8, CONT_END_TAG_8,
                CONT_START_TAG_9, CONT_END_TAG_9 };

#define MAX_TABLE_LINE 1000
char table_line[MAX_TABLE_LINE];
#define MAX_TABLE_ENTRIES 1000
PSENTRY symbol_table[MAX_TABLE_ENTRIES];
int num_table_entries = 0;
int num_table_errors = 0;
char env_name[MAX_TABLE_LINE];

int DONT_CARE = -100;
STRING dont_care_str = "!@#$%^&*";

int command_types[] = {COMMAND, COMMAND_1, COMMAND_2, COMMAND_3, COMMAND_4,
                     COMMAND_5, COMMAND_6, COMMAND_7, COMMAND_8, COMMAND_9};
int command_opt_types[] = {COMMAND_OPT, COMMAND_1_OPT, COMMAND_2_OPT, COMMAND_3_OPT,
                         COMMAND_4_OPT, COMMAND_5_OPT, COMMAND_6_OPT, COMMAND_7_OPT,
                         COMMAND_8_OPT, COMMAND_9};

char escape_char = '\\';
char newline_char = 'n';
char horizontal_tab_char = 't';
char vertical_tab_char = 'v';
char backspace_char = 'b';
char carriage_return_char = 'r';
char formfeed_char = 'f';
char audible_alert_char = 'a';
char hex_char = 'x';


/* Extended 6/96 */
STRING unk = "ERROR";


int num_print = 0;
int num_noprint = 0;
int num_bufprint = 0;
#define MAX_PRINT_STACK 100
PSTRWC print_control_stack[MAX_PRINT_STACK];    /* (6/96 changed from int) */


                                         /* positions in command table */
int pos_bdoc;                 /* BEGIN_DOCUMENT */
int pos_edoc;                 /* END_DOCUMENT */
int pos_bvbm;                 /* BEGIN_VERBATIM */
int pos_evbm;                 /* END_VERBATIM */
int pos_bv;                   /* BEGIN_VERB */
int pos_ev;                   /* END_VERB */
int pos_oc;                   /* OTHER_COMMAND */
int pos_ob;                   /* OTHER_BEGIN */
int pos_oe;                   /* OTHER_END */
int pos_lbrace;               /* LEFT_BRACE */
int pos_rbrace;               /* RIGHT_BRACE */
int pos_para;                 /* PARAGRAPH */
int pos_bdol;                 /* 'begin' $ */
int pos_edol;                 /* 'end' $ */
int pos_bss;                  /* SLASH_SPACE */

PSENTRY new_table_entry();
void create_def_reqentry();
void read_table();
void print_table();
int check_table();
void delete_to_chars();
int delete_quotes();
void twarn_noline();
void table_error();
void terror_noline();
void tdebug_tline();
void tdebug_str_int();
void tdebug_str();
void tdebug_char();
int ctype_to_int();
char *ctype_to_string();
int pos_to_int();
char *pos_to_string();
int level_to_int();
char *level_to_string();
int printc_to_int();
char *printc_to_string();
void sort_table();
int compare_st_entry();
int lookup_entry();
void get_env_name();
void print_to_err();
void print_debug_1s();
void print_debug_2s();
void print_debug_undef();
void complete_table_entry();
void complete_entry();
int command_type();
int get_user_type();
int get_special_token();
/* 6/96 changed following from STRING to PSTRWC */
PSTRWC get_t();
PSTRWC get_et();
PSTRWC get_tag_t();
PSTRWC get_tag_et();
PSTRWC get_opttag_t();
PSTRWC get_opttag_et();
int get_level();
PSTRWC get_item_t();
PSTRWC get_item_et();
PSTRWC get_itemopt_t();
PSTRWC get_itemopt_et();
void warning_3s();
void set_print();
void reset_print();
void push_print_control();
PSTRWC pop_print_control();
PSTRWC peek_print_control();    /* added 6/96 */
/* 6/96 changed following from int to PSTRWC */
PSTRWC get_com_print();
PSTRWC get_param_print();
PSTRWC get_opt_print();

/*         Command table file stack stuff */
# define MAX_CT_STACK 20
FILE *ct_fp_stack[MAX_CT_STACK];                 /* ct file pointers */
STRING ct_fn_stack[MAX_CT_STACK];                /* ct file names */
int ct_ln_stack[MAX_CT_STACK];                   /* ct line numbers */
int ct_file_num;                                 /* current ct file */

void initialise_ct_files();
int set_ct_file();
int reset_ct_file();
FILE *get_ct_filep();
STRING get_ct_filen();
int get_ct_linenum();
void incr_ct_linenum();

/*         Environment variable defined search path stuff */
void initialise_senv();
char path_name[257];                         /* name of a path */
char sys_envname[20];                        /* name of environment variable */
char path_sep[10];                           /* path name seperators */
char dir_cat;                                /* directory catenation char */
int senv_debug;                              /* =1 for debug searchenv() */


/*------------------- Additions for interpreting procedural code ------*/

SYMTAB_NODE_PTR code_root; /* where the initial compiled code is stored */

     /* EXTERNALS */
extern SYMTAB_NODE_PTR code_setup();
extern int isynt_error_count;
extern int isynt_warn_count;
extern int irun_error_count;
extern int irun_warn_count;

     /* FORWARDS */
void print_usage();         /* how to call the program */
void set_pc_code();         /* store a code segment */

/*******************************************************************/
/*                                                                 */
/* MAIN the main program ----------------------------------------- */
/*                                                                 */
/*******************************************************************/
main(argc, argv)
int argc;
char **argv;
{
    int optchar;
    int n;
    FILE *file;
    int result;
    char tabnam[100];

                 /* print banner */
   fprintf(stdout, "\n          ltx2x: A LaTeX to X autotagger\n");
   fprintf(stdout, "\n          (%s, %s)\n", FILE_VERSION, FILE_DATE);

                 /* open error log file */
   file = fopen("ltx2x.err", "w");
   if (!file) {
     fprintf(stderr, "Fatal Error: Could not open file ltx2x.err\n");
     exit(1);
   }
   filerr = file;
   fprintf(stdout, "Error file is ltx2x.err\n");
   fprintf(filerr, "Error file for program ltx2x (%s, %s)\n", FILE_VERSION, FILE_DATE);
   fprintf(filerr, "Author: Peter Wilson (ex Catholic University and NIST)\n");
   fprintf(filerr, "Email any comments or suggestions to peter.r.wilson@boeing.com\n\n");
                /* open Table output file */
   file = fopen("ltx2xct.lis", "w");
   if (!file) {
     fprintf(stderr, "Fatal Error: Could not open file ltx2xct.lis\n");
     exit(1);
   }
   filtabout = file;
   fprintf(stdout, "Table output file is ltx2xct.lis\n");
   fprintf(filerr, "Table output file is ltx2xct.lis\n");

                  /* set up for Table input file */
   strcpy(tabnam, "ltx2x.ct");
   initialise_senv();                     /* initialise directory searching */
    
    /* get command line optional parameters */
    opterr = 1;                    /* getopt prints errors if opterr is 1 */
    while (EOF != (optchar =
          getopt(argc,argv,"htcwCESl:y:f:p:P:D:i:"))) {
    /*
     * Uwe Sassenberg <sassen@hal1.physik.uni-dortmund.de> found
     * that he had to add this next line of code to stop an infinite
     * loop of this while (It did not seem to recognise EOF!!)
     * If it compiles but just sits there chewing CPU cycles, try
     * uncommenting the next line of code
     */
    /* if (optchar == 255) break; */
          switch(optchar) {
          case '?': {             /* command line error */
            print_usage();
            break;
          }
          case 'h': {             /* command line help */
            print_usage();
            break;
          }
          case 'l': {             /* lexer debugging >0 */
            LDEBUG = atoi(optarg);
            if (LDEBUG <= 0) {
              LDEBUG = 0;
            }
            fprintf(stdout, "Lexer debugging set to %d\n", LDEBUG);
            fprintf(filerr, "Lexer debugging set to %d\n", LDEBUG);
            break;
          }
          case 't': {       /* switch on command table & SEARCHENV debugging */
            TDEBUG = TRUE;
            senv_debug = 1;
            fprintf(stdout, "Command table debugging set ON\n");
            fprintf(filerr, "Command table debugging set ON\n");
            break;
          }
          case 'y': {             /* yacc parser debugging >0 */
            YDEBUG = atoi(optarg);
            if (YDEBUG <= 0) {
              YDEBUG = 0;
            }
            fprintf(stdout, "Parser debugging set to %d\n", YDEBUG);
            fprintf(filerr, "Parser debugging set to %d\n", YDEBUG);
            break;
          }
          case 'f': {            /* special Table input file */
            strcpy(tabnam, optarg);
            break;
          }
          case 'c': {            /* leave in TeX comments */
            leave_comments = TRUE;
            fprintf(stdout, "Print TeX comments set ON\n");
            fprintf(filerr, "Print TeX comments set ON\n");
            break;
          }
          case 'w': {            /* collapse whitespace */
            collapse_ws = TRUE;
            fprintf(stdout, "Collapse whitespace set ON\n");
            fprintf(filerr, "Collapse whitespace set ON\n");
            break;
          }
          case 'P': {             /* pathname seperators */
            strcpy(path_sep, optarg);
            strcat(path_sep, " ");
            fprintf(stdout, "Pathname seperators set to (%s)\n", path_sep);
            fprintf(filerr, "Pathname seperators set to (%s)\n", path_sep);
            break;
          }
          case 'D': {             /* directory catenation char */
            dir_cat = optarg[0];
            fprintf(stdout, "Directory catenation character set to %c\n", dir_cat);
            fprintf(filerr, "Directory catenation character set to %c\n", dir_cat);
            break;
          }
          case 'p': {            /* pretty-print line length */
            pretty_print = TRUE;
            collapse_ws = TRUE;
            max_pretty_line = atoi(optarg);
            if (max_pretty_line > MAX_BUFFER) {
              max_pretty_line = MAX_BUFFER;
              fprintf(stdout, "Line length too long. Set to %d\n", max_pretty_line);
              fprintf(filerr, "Line length too long. Set to %d\n", max_pretty_line);
            }
            else if (max_pretty_line <= 10) {
              max_pretty_line = 70;
              fprintf(stdout, "Line length too short. Set to %d\n", max_pretty_line);
              fprintf(filerr, "Line length too short. Set to %d\n", max_pretty_line);
            }
            else {
              fprintf(stdout, "Line length set to %d\n", max_pretty_line);
              fprintf(filerr, "Line length set to %d\n", max_pretty_line);
              fflush(stdout);
              fflush(filerr);
            }
            break;
          }
          case 'i': {         /* interpreter code debugging */
            cl_debug = atoi(optarg);
            if (cl_debug <= 0) {
              cl_debug = 0;
            }
            fprintf(stdout, "Interpreter debugging set to %d\n", cl_debug);
            fprintf(filerr, "Interpreter debugging set to %d\n", cl_debug);
            break;
          }
          case 'C': {         /* disable any interpreter source Code debug */
            cdebug = FALSE;
            fprintf(stdout, "Interpreter source Code debugging disabled\n");
            fprintf(filerr, "Interpreter source Code debugging disabled\n");
            break;
          }
          case 'E': {         /* disable any interpreter Execution debug */
            edebug = FALSE;
            fprintf(stdout, "Interpreter Execution debugging disabled\n");
            fprintf(filerr, "Interpreter Execution debugging disabled\n");
            break;
          }
          case 'S': {         /* enable interpreter Source Level Debugger */
            SLD_OFF = FALSE;
            fprintf(stdout, "Interpreter Source Level Debugger set ON\n");
            fprintf(filerr, "Interpreter Source Level Debugger set ON\n");
            break;
          }
        } /* end switch */
    } /* end of optional parameter processing */

                         /* open Table file */
   if (!searchenv(tabnam, sys_envname, path_name, path_sep, dir_cat, senv_debug)) {
     fprintf(stderr, "Fatal Error: Could not find file %s\n", tabnam);
     exit(1);
   }
   file = fopen(path_name, "r");
   if (!file) {
     fprintf(stderr, "Fatal Error: Could not open file %s\n", path_name);
     exit(1);
   }
   filtabin = file;
   fprintf(stdout, "Table input file is %s\n", path_name);
   fprintf(filerr, "Table input file is %s\n", path_name);

    /* rest of parameters are file names (input and output) */
    n = 0;
    for (;optind < argc; optind++) {
        n++;
        if (n == 1) {
           file = fopen(argv[optind], "r");
           if (!file) {
              fprintf(stderr,"Fatal Error: Could not open input file %s\n",argv[optind]);
              fprintf(filerr,"Fatal Error: Could not open input file %s\n",argv[optind]);
              exit(1);
           }
           fprintf(stdout,"Reading file %s", argv[optind]);
           fprintf(filerr,"Reading file %s", argv[optind]);
           yyin = file;
        }
        else { if (n == 2) {
                  file = fopen(argv[optind], "w");
                  if (!file) {
              fprintf(stderr,"Fatal Error: Could not open output file %s\n",argv[optind]);
              fprintf(filerr,"Fatal Error: Could not open output file %s\n",argv[optind]);
                     exit(1);
                  }
                  fprintf(stdout," and writing to file %s\n",argv[optind]);
                  fprintf(filerr," and writing to file %s\n",argv[optind]);
                  yyout = file;
               }
               else {
  fprintf(stderr,"Warning: Only two files permitted. File %s ignored\n",argv[optind]);
  fprintf(filerr,"Warning: Only two files permitted. File %s ignored\n",argv[optind]);
               }
        }
    }
    if (n == 0) { 
       fprintf(stderr,"Fatal Error: An input and an output file are required.\n");
       fprintf(filerr,"Fatal Error: An input and an output file are required.\n");
       exit(1);
    }

                     /* do some initialisations */
    print_to_err("\n > Starting initialisations\n");
    initialise_clause_stack();
    darray[0] = 0;
    initialise_sysbuf();
    init_common_print();                 /* 6/96 common print structs */
    init_print_control();                /* 6/96  initialise print control */

    code_root = NULL;     /* code interp, initialise to no-code */

    print_to_err(" > Reading command table(s)\n");
    initialise_ct_files(path_name);
    read_table();
    if (TDEBUG) {
      print_table(filerr);
    }
    print_to_err(" > Sorting table\n");
    sort_table();
    print_to_err(" > Checking table\n");
    if (check_table()) {
      print_to_err("    > Re-sorting table\n");
      sort_table();
      print_to_err("    > Re-checking table\n");
      check_table();
    }
    print_to_err(" > Printing table\n");
    print_table(filtabout);
    fclose(filtabout);

    /* added for code interp */
    /* execute initial code */
    if (code_root != NULL) {      /* only process initial code if there is any */
      exec_startup(code_root);
    }

                 /* process input */
   print_to_err(" > Processing source file\n");
   while(!feof(yyin)) {
      result = yyparse();
      if (result == 0) {
        write_stats(" (end of parsing)");
        exit(0);
      }
   }
    write_stats(" (end of program)");
    exit(0);
}                                                             /* end MAIN */
/*------------------------------------------------------------------------*/

/*------------------------------------------------------------------------*/
/* print_usage   Prints the command line parameters                       */

void print_usage()
{
  fprintf(stderr, "\nUsage: [options] infile outfile\n");
  fprintf(stderr, "  Options for the general user\n");
  fprintf(stderr, "       [-c] include LaTeX comments in the output\n");
  fprintf(stderr, "       [-f tablename] use tablename as the command file instead of ltx2x.ct\n");
  fprintf(stderr, "       [-h] print usage (this message)\n");
  fprintf(stderr, "       [-p num] set pretty printing with num characters per line\n");
  fprintf(stderr, "       [-w] collapse whitespace in the output\n");
  fprintf(stderr, "       [-D char] directory catenation character\n");
  fprintf(stderr, "       [-P string] pathname seperator characters\n");
  fprintf(stderr, "       [-S] enable the interpreter's source level debugger\n");
  fprintf(stderr, "  Options for the system developer\n");
  fprintf(stderr, "       [-i num] set level of interpreter debug print output\n");
  fprintf(stderr, "       [-l num] set level of lexer debug print output\n");
  fprintf(stderr, "       [-t] enable command table debug print output\n");
  fprintf(stderr, "       [-y num] unused at present\n");
  fprintf(stderr, "       [-C] disable any interpreter source code debug print\n");
  fprintf(stderr, "       [-E] disable any interpreter execution code debug print\n");

  fprintf(filerr, "\nUsage: [options] infile outfile\n");
  fprintf(filerr, "  Options for the general user\n");
  fprintf(filerr, "       [-c] include LaTeX comments in the output\n");
  fprintf(filerr, "       [-f tablename] use tablename as the command file instead of ltx2x.ct\n");
  fprintf(filerr, "       [-h] print usage (this message)\n");
  fprintf(filerr, "       [-p num] set pretty printing with num characters per line\n");
  fprintf(filerr, "       [-w] collapse whitespace in the output\n");
  fprintf(filerr, "       [-D char] directory catenation character\n");
  fprintf(filerr, "       [-P string] pathname seperator characters\n");
  fprintf(filerr, "       [-S] enable the interpreter's source level debugger\n");
  fprintf(filerr, "  Options for the system developer\n");
  fprintf(filerr, "       [-i num] set level of interpreter debug print output\n");
  fprintf(filerr, "       [-l num] set level of lexer debug print output\n");
  fprintf(filerr, "       [-t] enable command table debug print output\n");
  fprintf(filerr, "       [-y num] unused at present\n");
  fprintf(filerr, "       [-C] disable any interpreter source code debug print\n");
  fprintf(filerr, "       [-E] disable any interpreter execution code debug print\n");

exit(0);
}                                                      /* end PRINT_USAGE */
/*------------------------------------------------------------------------*/




                 /*-----------STRING FUNCTIONS-------------------------*/

/* STRSAVE saves a string somewhere */
char *strsave(s)                 
char *s;
{
/*  char *p, *malloc(); */
  char *p;

/*  if ((p = malloc(strlen(s)+1)) != NULL) { */
  if ((p = (char *) malloc(strlen(s)+1)) != NULL) {
     strcpy(p, s);
   }
   else {
     yyerror("FATAL ERROR: No memory left for STRSAVE");
     exit(1);
   }
   return(p);
}                                      /* end STRSAVE */


/* GET_ENV_NAME puts ...{ name } name into env_name */
void get_env_name(str)      
char str[];
{
  int first, last, i, n;
  int c;
  int top;

  top = MAX_TABLE_LINE + 10;
  first = top;
  n = 0;
  env_name[0] = '\0';

  for (i = 0; (c = str[i]) != '\0'; i++) {  /* look for opening brace */
    if (c == '{') {
      first = i+1;
      break;
    }
  }
  if (first == top) {  /* opening brace not found */
    yyerror("get_env_name: opening brace not found");
    return;
  }
  last = first;
  first = top;
  for (i = last; (c = str[i]) != '\0'; i++) {  /* look for start of name */
    if (!isspace(c)) {   /* found it */
      first = i;
      break;
    }
  }
  if (first == top) { /* name not found */
    yyerror("get_env_name: name not found");
    return;
  }
  for (i = first; (c = str[i]) != '\0'; i++) {
    if (!isspace(c)) {  /* a part of the name */
      if (c == '}') { /* name ended */
        break;
      }
      env_name[n] = c;
      n++;
    }
  }
  env_name[n] = '\0';
}                                         /* end GET_ENV_NAME */


/* DELETE_TO_CHARS deletes all chars through first occurrence of "c" */
void delete_to_chars(c,s)       
char c[];
char s[];
{
  int len;
  int pos;
  int i, n;

  len = strlen(s);
  pos = strcspn(s, c);
  if (pos > 0 && pos < len) {             /* = found */
    n = 0;
    for (i = (pos+1); i <= len; i++ ) {
      s[n] = s[i];
      n++;
    }
  }
}                                         /* end DELETE_TO_CHARS */


/* DELETE_QUOTES deletes leading and trailing " and replaces 
                 escaped character pairs. Returns # of chars  */
int delete_quotes(s,sout)    
char s[];                     
char sout[];
{
  int first, last, len;
  int i, n, j, k;
  char last_char, this_char;
  char hex_str[20];

  len = strlen(s);
  for (i=0; i < len; i++) {
    first = i;
    if (s[i] == '"') {
      break;
    }
  }
  last = first;
  for (i = (first + 1); i < len; i++) {
    if (s[i] == '"') {
      last = i;
    }
  }
  if (first == last) {   /* zero or one quote */
    sout[0] = SNUL;
    if (first != (len -1) ) {  /* one quote */
      table_error("ERROR: string not enclosed in quotes");
    }
    return(0);
  }
  n = 0;
  last_char = '"';
  for (i= (first+1); i < last; i++) {
    this_char = s[i];
    sout[n] = this_char;
    if (last_char == escape_char)  {  /* possibly an escaped pair */
      if (this_char == newline_char) {
        n--;
        sout[n] = '\n';
      }
      else if (this_char == horizontal_tab_char) {
        n--;
        sout[n] = '\t';
      }
      else if (this_char == vertical_tab_char) {
        n--;
        sout[n] = '\v';
      }
      else if (this_char == backspace_char) {
        n--;
        sout[n] = '\b';
      }
      else if (this_char == carriage_return_char) {
        n--;
        sout[n] = '\r';
      }
      else if (this_char == formfeed_char) {
        n--;
        sout[n] = '\f';
      }
      else if (this_char == audible_alert_char) {
        n--;
        sout[n] = '\a';
      }
      else if (this_char == hex_char) {     /* do hex stuff */
        k = 0;
        hex_str[k] = '\0';
        for (j = i+1; j < last; j++) {        /* get hex chars */   
          if (isxdigit(s[j])) {               /* got hex digit */
            hex_str[k] = s[j];
            k++;
          }
          else {                              /* end of hex digits */
            hex_str[k] = '\0';
            break;
          }
        }                                     /* got all hex digits */
        n--;
        sout[n] = strtoul(hex_str, NULL, 16);
        i = i + k;                            /* shift i to end of hex digits */
      }
      else if (this_char == escape_char) {
        if (escape_char == '\\' ) {         /* default */
          n--;
          sout[n] = '\\';
          this_char = '"';
        }
        else {
          n--;
          this_char = '"';
        }
      }
    }
    n++;
    last_char = this_char;
  }
  sout[n] = SNUL;
  return(n);
}                             /* end DELETE_QUOTES */


/* STRTOUC converts a string in place to upper case */
void strtouc(str)
char str[];
{
  int i;
  for (i = 0; str[i] != SNUL; i++) {
    str[i] = toupper(str[i]);
  }
  return;
}                                   /* end STRTOUC */



      /*------------PRINT FUNCTIONS-----------------------*/

/* MYPRINT output to file, buffer or black hole */
void myprint(s)                 
char s[];
{
  PSTRWC ptr;
  int num;
  int n;
  int k;

  ptr = cur_printc;
  if (ptr == NULL) {                   /* should not be null */
    yyerror("internal error: cur_printc is NULL in MYPRINT");
    return;
  }

  if (s == NULL) {                   /* nothing to print */
    return;
  }
  if (s[0] == SNUL) {               /* nothing to print */
    return;
  }

  k = get_pc_enum(ptr);
  switch (k) {
    case NO_PRINT: {                   /* do nothing */
      return;
      break;
    }
    case NO_OP: {                     /* do nothing */
      return;
      break;
    }
    case TO_SYSBUF: {           /* store string in system buffer */
             /* check buffer overflow */
      n = strlen(buffer) + strlen(s);
      used_sbuff_chars = maxof(n, used_sbuff_chars);
      if (n >= MAX_BUFFER - 2) { 
        warning("Unpredictable results due to system buffer overflow");
      }
      else {                               /* add to buffer */
        strcat(buffer, s);
      }
      break;
    }
    case TO_BUFFER: {            /* store string in user buffer */
      num = get_pcdata_num(ptr);
      if ( num < 0 || num > MAX_USER_BUFFS ) {  /* buffer out of range */
        sprintf(errstr, "in MYPRINT user buffer %d is out of range", num);
        warning(errstr);
        break;
      }
      n = strlen(user_buffs[num]) + strlen(s);
      used_ubuff_chars = maxof(n, used_ubuff_chars);
      if (n >= MAX_UBUFF_LEN - 2) { 
        sprintf(errstr, "Unpredictable results; User buffer %d overflowed", num);
        warning(errstr);
      }
      else {                               /* add to buffer */
        strcat(user_buffs[num], s);
      }
      break;
    }
    case TO_FILE: {                   /* write to file */
/*      name = get_pcdata_name(ptr); */
      fprintf(cur_user_fileid, "%s", s);
      fflush(cur_user_fileid);
      break;              
    }
    case DEFAULT_PRINT: {
      if (pretty_print) {                  /* pretty printing */
        pprint(s);
      }
      else {
        fprintf(yyout, "%s", s);
        fflush(yyout);
      }
      break;
    }
    default: {                   /* should not be here */
      sprintf(errstr, "Illegal print option (%d %s) in MYPRINT",
                       k, printc_to_string(k));
      warning(errstr);
      return;
      break;
    }
  } /* end switch */
  return;
}                                      /* end MYPRINT */


int pprint_top = 0;        /* pointer into pretty_line */
int last_space = 0;        /* posn of last space in pretty_line */
int ppoverflow = FALSE;    /* pretty_line buffer overflow */

/* PRETTY_EMPTY checks if the pretty line is empty */
int pretty_empty()
{
  return((pretty_line[0] == SNUL));
}                                         /* end PRETTY_EMPTY */


/* FLUSH_PRETTY flushes pretty line buffer and resets it */
void flush_pretty()
{

  pretty_line[pprint_top] = SNUL;
  fprintf(yyout, "%s\n", pretty_line);
  pprint_top = 0;
  pretty_line[pprint_top] = SNUL;
  last_space = 0;
  ppoverflow = FALSE;
  return;
}                                       /* end FLUSH_PRETTY */


/* PPRINT pretty printing */
void pprint(s)                         
char s[];
{

  char c;
  int i,n,j;
  int start;

  start = 0;
      /* add characters from s into pretty_line until overflow. */
      /* back off to last space. Flush at newline. */

  for (i = start; (c = s[i]) != SNUL; i++) {
    if (c == '\n') {                         /* newline, flush */
      flush_pretty();
      ppoverflow = FALSE;
    }      
    else {
      if (c == ' ' || c == '\t') {             /* whitespace */
        last_space = pprint_top;
        if (ppoverflow) {                     /* overflow, flush on whitespace */
          flush_pretty();
          ppoverflow = FALSE;
        }
      }
      pretty_line[pprint_top] = c;
      pprint_top++;
      if (pprint_top >= (max_pretty_line - 1)) {     /* buffer is full, back off */
        if (last_space > 0) {                  /* but only if there is a prior space */
          pretty_line[last_space] = SNUL;
          fprintf(yyout, "%s\n", pretty_line);
          j = 0;
          for (n = last_space + 1; n < pprint_top; n++) { /* move final chars down */
            pretty_line[j] = pretty_line[n];
            j++;
          }
          pprint_top = j;
          last_space = 0;
          ppoverflow = FALSE;
	}
        else {                                /* overflow, print the buffer */
          flush_pretty();
          ppoverflow = TRUE;
        }
      }
    }
  } /* end for loop */
}                                     /* end PPRINT */


/* VERBATIM_PRINT output to file, buffer or black hole (almost identical to myprint ) */
void verbatim_print(s)                 
char s[];
{
  PSTRWC ptr;
  int num;
  int n;
  int k;

  ptr = cur_printc;
  if (ptr == NULL) {                   /* should not be null */
    yyerror("internal error: cur_printc is NULL in VERBATIM_PRINT");
    return;
  }

  if (s == NULL) {                   /* nothing to print */
    return;
  }
  if (s[0] == SNUL) {               /* nothing to print */
    return;
  }

  k = get_pc_enum(ptr);
  switch (k) {
    case NO_PRINT: {                   /* do nothing */
      return;
      break;
    }
    case NO_OP: {                      /* do nothing */
      return;
      break;
    }
    case TO_SYSBUF: {           /* store string in system buffer */
             /* check buffer overflow */
      n = strlen(buffer) + strlen(s);
      used_sbuff_chars = maxof(n, used_sbuff_chars);
      if (n >= MAX_BUFFER - 2) { 
        warning("Unpredictable results due to system buffer overflow");
      }
      else {                               /* add to buffer */
        strcat(buffer, s);
      }
      break;
    }
    case TO_BUFFER: {            /* store string in user buffer */
      num = get_pcdata_num(ptr);
      if ( num < 0 || num > MAX_USER_BUFFS ) {  /* buffer out of range */
        sprintf(errstr, "in VERBATIM_PRINT user buffer %d is out of range", num);
        warning(errstr);
        break;
      }
      n = strlen(user_buffs[num]) + strlen(s);
      used_ubuff_chars = maxof(n, used_ubuff_chars);
      if (n >= MAX_UBUFF_LEN - 2) { 
        sprintf(errstr, "Unpredictable results; User buffer %d overflowed", num);
        warning(errstr);
      }
      else {                               /* add to buffer */
        strcat(user_buffs[num], s);
      }
      break;
    }
    case TO_FILE: {                   /* write to file */
      fprintf(cur_user_fileid, "%s", s);
      fflush(cur_user_fileid);
      break;              
    }
    case DEFAULT_PRINT: {
      if (bverb && pretty_print) {        /* at start of verbatim */
        if (!pretty_empty()) {
          flush_pretty();                 /* flush the current pretty line */
        }
        bverb = FALSE;
      }
      fprintf(yyout, "%s", s);
      fflush(yyout);
      break;
    }
    default: {                   /* should not be here */
      sprintf(errstr, "Illegal print option (%d %s) in VERBATIM_PRINT",
                       k, printc_to_string(k));
      warning(errstr);
      return;
      break;
    }
  } /* end switch */
  return;
}                                      /* end VERBATIM_PRINT */


/* PRINT_SYSBUF prints system buffer */
void print_sysbuf()                     
{
  fprintf(yyout, "%s", buffer);
  fflush(yyout);
}                                       /* end PRINT_SYSBUF */


/* INITIALISE_SYSBUF clears system buffer */
void initialise_sysbuf()               
{
  buffer[0] = SNUL;
}                                      /* end INITIALISE_SYSBUF */


/* COPY_SYSBUF copies system buffer to S */
void copy_sysbuf(s)                    
char s[];
{
  strcat(s, buffer);
}                                      /* end COPY_SYSBUF */


/* INITIALISE_STRING clears a string S */
void initialise_string(s)              
char s[];
{
  s[0] = SNUL;
}                                      /* end INITIALISE_STRING */


/* INITIALISE_PC initialises print control */
PSTRWC initialise_pc()
{

  /* set counters to zero */
  num_print = 0;                        /* (empty) top of stack */
  num_noprint = 0;                      /* # of open no prints */
  num_bufprint = 0;                     /* # of open sys buff prints */
  num_ubufwrite = 0;                    /* # of open user buff writes */
  num_fileprint = 0;                    /* # of open file writes */
  num_otherprint = 0;                   /* # of open non-default prints */
  /* set flags */
  no_print = FALSE;                     /* we are printing */
  print_to_buffer = FALSE;              /* not printing to sys buffer */
  /* set stack for default printing */
  push_print_control(p_default_print);
  cur_printc = p_default_print;
/*  set_print(p_default_print); */
  return(p_default_print);
}                                     /* end INITIALISE_PC */

/* SET_PRINT sets myprint and other print behaviours */
/* 6/96 changed pswitch from int to PSTRWC */
void set_print(pswitch)         
PSTRWC pswitch;
{
  int num;
  PSTRWC ptr;
  int k;
  STRING str;
  int curpc;

  if (pswitch == NULL) {            /* got a problem! */
      yyerror("internal error: SET_PRINT called with NULL value (setting it to the default)");
      initialise_pc();
      return;
  }

  k = get_pc_enum(pswitch);

  switch (k) {                        /* check on pswitch */
    case DEFAULT_PRINT :
    case NO_PRINT :
    case TO_SYSBUF :
    case TO_BUFFER :
    case NO_OP :
    case TO_FILE : {        /* pswitch is OK */
      ptr = pswitch;
      break;
    }
      default : {                 /* should not be here */
      sprintf(errstr, 
"internal error: SET_PRINT called with illegal value (%d %s), setting it to the default",
              k, printc_to_string(k));
      yyerror(errstr);
      ptr = p_default_print;
      k = DEFAULT_PRINT;
      break;
    }
  }  /* end of switch on pswitch */

   /* possibly start a no op */
  if (k == NO_OP && !in_noop) {
    start_noop = TRUE;
    push_print_control(ptr);
    cur_printc = ptr;
    return;
  }

  curpc = get_pc_enum(cur_printc);
  switch(curpc) {                      /* action depends on current state */
    case DEFAULT_PRINT: {              /* default is almost a non-event */
      push_print_control(ptr);
      cur_printc = ptr;
      break;
    }
    case NO_PRINT: {                  /* carry on with no printing */
      push_print_control(cur_printc);
      break;
    }
    case TO_SYSBUF :
    case TO_BUFFER :
    case TO_FILE : {          
      if (k == DEFAULT_PRINT) {       /* default doesn't change anything */
        push_print_control(cur_printc);
      }
      else {                          /* a change */
        push_print_control(ptr);
        cur_printc = ptr;
      }
      break;
    }
    default : {                       /* should not be here */
      sprintf(errstr, 
"internal error: SET_PRINT has illegal value (%d %s) for cur_printc, setting it to the default",
              curpc, printc_to_string(curpc));
      warning(errstr);
      cur_printc = p_default_print;
      break;
    }
  }  /* end switch on cur_printc */

  switch (get_pc_enum(cur_printc)) {    /* finally update counters, etc */
    case DEFAULT_PRINT : {
      break;
    }
    case NO_PRINT : {
      num_noprint++;
      num_otherprint = SET_NUM_OTHERPRINT;
      no_print = TRUE;
      break;
    }
    case TO_SYSBUF : {
      num_bufprint++;
      num_otherprint = SET_NUM_OTHERPRINT;
      print_to_buffer = TRUE;
      break;
    }
    case TO_BUFFER : {
      num_ubufwrite++;
      num_otherprint = SET_NUM_OTHERPRINT;
      num = get_pcdata_num(cur_printc);
      num_ubuff[num]++;
      cur_user_buf = num;
      if (written_from_buff[num]) {       /* have written from this buffer */
        initialise_string(user_buffs[num]);
        written_from_buff[num] = FALSE;
      }
      break;
    }
    case TO_FILE : {
      num_fileprint++;
      num_otherprint = SET_NUM_OTHERPRINT;
      str = get_pcdata_name(cur_printc);
      num = get_fpos(str);
      num_filep[num]++;
      cur_user_fn = str;
      cur_user_fpos = num;
      if (num_filep[num] == 1) {       /* a first write call to this file */
        ufp[num] = ufopenw(str);
      }
      cur_user_fileid = ufp[num];
      break;
    }
    default : {                       /* should not be here */
      sprintf(errstr, 
"internal error: SET_PRINT has set illegal value (%d %s) for cur_printc",
              curpc, printc_to_string(curpc));
      yyerror(errstr);
      break;
    }
  } /* end of switch over cur_printc */
  return;
}                                       /* end SET_PRINT */


/* RESET_PRINT resets myprint, etc behaviour */
void reset_print()             
{

  PSTRWC ptr;
  int num;
  STRING fn;
  int file_pos;               /* pos of file in file list */
  int k;

  ptr = pop_print_control();
  k = get_pc_enum(ptr);
  switch (k) {                /* close off the popped print control */
    case DEFAULT_PRINT : {       /* nothing special to do */
      break;
    }
    case NO_PRINT: {
      num_noprint--;
      if (num_noprint < 0) {
        yyerror("at least one too many calls to reset_print after NO_PRINT");
        num_noprint = 0;
      }
      if (num_noprint <= 0) {  /* back to some output */
        no_print = FALSE;
      }
      num_otherprint = SET_NUM_OTHERPRINT;
      break;
    }
    case TO_SYSBUF: {
      num_bufprint--;
      if (num_bufprint < 0) {
        yyerror("at least one too many calls to reset_print after PRINT_TO_BUFFER");
        num_bufprint = 0;
      }
      if (num_bufprint <= 0) {
        print_to_buffer = FALSE;
      }
      num_otherprint = SET_NUM_OTHERPRINT;
      break;
    }
    case TO_BUFFER: {   
      num = get_pcdata_num(ptr);
      num_ubufwrite--;
      num_ubuff[num]--;
      if (num_ubufwrite < 0) {
        yyerror("at least one too many calls to reset_print after WRITE_TO_BUFFER");
        num_ubufwrite = 0;
        num_ubuff[num] = 0;
      }
      num_otherprint = SET_NUM_OTHERPRINT;
      break;
    }
    case TO_FILE: {     /* close file */
      fn = get_pcdata_name(ptr);
      num_fileprint--;
      file_pos = get_fpos(fn);
/*      num_filep[file_pos]--;            10/96 */
      if (num_fileprint < 0) {
        yyerror("at least one too many calls to reset_print after WRITE_TO_FILE");
        num_fileprint = 0;
        num_filep[file_pos] = 0;
      }
      if (num_filep[file_pos] == 0) {        /* close the file */
/*        ufclose(fn);                   10/96 */
      }
      num_otherprint = SET_NUM_OTHERPRINT;
      break;
    }
    case  NO_OP: {    /* reset flags */
      start_noop = FALSE;
      in_noop = FALSE;
      break;
    }
    default: {                   /* stack underflow */
      sprintf(errstr, 
"internal error: RESET_PRINT called with illegal value (%d %s), setting it to the default",
              k, printc_to_string(k));
      warning(errstr);
      initialise_pc();
      break;
    }
  }  /* end of switch on popped */
  cur_printc = peek_print_control();

  switch (get_pc_enum(cur_printc)) {
    case DEFAULT_PRINT : {               /* nothing to do */
      break;
    }
    case NO_PRINT : {                    /* done already */
      break;
    }
    case TO_SYSBUF : {             /* done already */
      break;
    }
    case TO_BUFFER : {             /* set current buffer */
      cur_user_buf = get_pcdata_num(cur_printc);
      break;
    }
    case TO_FILE : {               /* set current file name */
      cur_user_fn = get_pcdata_name(cur_printc);
      break;
    }
    default : {                          /* should not be here, but do nothing */
      sprintf(errstr, 
"internal error: RESET_PRINT peeked at illegal value (%d %s)",
              k, printc_to_string(k));
      yyerror(errstr);
      break;
    }
  }  /* end of switch on peeked */
  return;
}                               /* end RESET_PRINT */


/* PUSH_PRINT_CONTROL */
void push_print_control(kind)                     
PSTRWC kind;
{

  used_print_stack = maxof(num_print, used_print_stack);
  if (num_print >= MAX_PRINT_STACK) {    /* stack overflow */
    yyerror("Somethings wrong: print stack overflow");
    return;
  }
  print_control_stack[num_print] = kind;
  num_print++;
}                                         /* end PUSH_PRINT_CONTROL */


/* POP_PRINT_CONTROL returns top of print control stack and pops it */
PSTRWC pop_print_control()           
{
  PSTRWC item;

  if (num_print < 0) {               /* stack underflow */
    yyerror("Somethings wrong: print stack underflow");
    return(p_print_underflow);
  }
  num_print--;
  item = print_control_stack[num_print];
  return(item);
}                                        /* end POP_PRINT_CONTROL */


/* PEEK_PRINT_CONTROL returns top of print control stack */
PSTRWC peek_print_control()
{
  if (num_print < 0) {                /* stack underflow */
    yyerror("Somethings wrong: print stack underflow");
    return(p_print_underflow);
  }
  return(print_control_stack[num_print - 1]);
}                                        /* end PEEK_PRINT_CONTROL */



              /*---------------ERROR AND DEBUG PRINTING-------------*/

/* YYERROR prints error message */
void yyerror(s)                  
char *s;
{
  fprintf(stderr, "\n%s in line\n%d: %s\n", s, lineno, linebuf);
  fprintf(filerr, "\n%s in line\n%d: %s\n", s, lineno, linebuf);
  fflush(stderr);
  fflush(filerr);
  num_errors++;
  if (num_errors >= MAX_ERRORS) {
    fprintf(stderr, "\n** Translation ended with at least %d errors **\n", num_errors);
    fprintf(filerr, "\n** Translation ended with at least %d errors **\n", num_errors);
    fprintf(yyout, "\n** Translation ended with at least %d errors **\n", num_errors);
    write_stats(" (at error failure)");
    exit(1);
  }
}                                      /* end YYERROR */


/* WARNING prints warning message */
void warning(s)                    
char *s;
{
  fprintf(stderr, "\nWARNING: %s in line\n%d: %s\n", s, lineno, linebuf);
  fprintf(filerr, "\nWARNING: %s in line\n%d: %s\n", s, lineno, linebuf);
  fflush(stderr);
  fflush(filerr);
}                                         /* end WARNING */


/* WARNING_3S prints 3 warning strings */
void warning_3s(s1, s2, s3)               
char *s1;
char *s2;
char *s3;
{

  fprintf(stderr, "\nWarning: %s %s %s in line\n%d: %s\n",
                    s1, s2, s3, lineno, linebuf);
  fprintf(filerr, "\nWarning: %s %s %s in line\n%d: %s\n",
                    s1, s2, s3, lineno, linebuf);
  fflush(stderr);
  fflush(filerr);
}                                         /* end WARNING_3S */


/* PRINT_TO_ERR prints one string to error file */
void print_to_err(s1)                   
char s1[];
{
  fprintf(filerr, "%s", s1);
  fflush(filerr);
}                                        /* end PRINT_TO_ERR */


/* PRINT_DEBUG_1S prints LD string to error file */
void print_debug_1s(s1)                   
char s1[];
{
  fprintf(filerr, " LD%s", s1);
  fflush(filerr);
}                                        /* end PRINT_DEBUG_1S */


/* PRINT_DEBUG_2S prints LD two strings to error file */
void print_debug_2s(s1, s2)              
char s1[];
char s2[];
{
  fprintf(filerr, " LD%s %s", s1, s2);
  fflush(filerr);
}                                        /* end PRINT_DEBUG_2S */

/* PRINT_DEBUG_UNDEF prints "undefined table entry" to error file */
void print_debug_undef(s1, s2)
char s1[];
char s2[];
{
  fprintf(filerr, "\n  %s on line %d not in command table. %s", 
                   s1, lineno, s2);
  fflush(filerr);
}                                        /* end PRINT_DEBUG_UNDEF */



/* TABLE_ERROR prints Command table error message */
void table_error(s)                  
char *s;
{
  fprintf(stderr, "\nCommand table: %s in file %s line\n%d: %s\n", 
                                     s, get_ct_filen(), get_ct_linenum(), table_line);
  fprintf(filerr, "\nCommand table: %s in file %s line\n%d: %s\n", 
                                     s, get_ct_filen(), get_ct_linenum(), table_line);
  fflush(stderr);
  fflush(filerr);
  num_table_errors++;
  if (num_table_errors >= MAX_ERRORS) {
    fprintf(stderr, "\n** Table processing ended with at least %d errors **\n",
                                num_table_errors);
    fprintf(filerr, "\n** Table processing ended with at least %d errors **\n",
                                num_table_errors);
    exit(1);
  }
}                                      /* end TABLE_ERROR */


/* TERROR_NOLINE prints error message without source line id */
void terror_noline(s)                  
char *s;
{
  fprintf(stderr, "\nCommand table error: %s\n", s);
  fprintf(filerr, "\nCommand table error: %s\n", s);
  fflush(stderr);
  fflush(filerr);
  num_table_errors++;
  if (num_table_errors >= MAX_ERRORS) {
    fprintf(stderr, "\n** Table processing ended with at least %d errors **\n",
                                num_table_errors);
    fprintf(filerr, "\n** Table processing ended with at least %d errors **\n",
                                num_table_errors);
    exit(1);
  }
}                                      /* end TERROR_NOLINE */


/* TWARN_NOLINE prints table warning */
void twarn_noline(str)                    
char str[];
{
/*  fprintf(stderr, "\nTable Warning: %s\n", str); */
  fprintf(filerr, "\nTable Warning: %s\n", str);
/*  fflush(stderr); */
  fflush(filerr);
}                                         /* end TWARN_NOLINE */


/* TDEBUG_TLINE prints table line */
void tdebug_tline(str)                    
char str[];
{
  fprintf(stderr, "\n%s line %d: %s", get_ct_filen(), get_ct_linenum(), str);
  fprintf(filerr, "\n%s line %d: %s", get_ct_filen(), get_ct_linenum(), str);
  fflush(stderr);
  fflush(filerr);
}                                         /* end TDEBUG_TLINE */


/* TDEBUG_STR_INT prints diagnostics */
void tdebug_str_int(str, num)  
char str[];
int num;
{
  fprintf(stderr, "LD: %s %d\n", str, num);
  fprintf(filerr, "LD: %s %d\n", str, num);
  fflush(stderr);
  fflush(filerr);
  return;
}                                           /* end TDEBUG_STR_INT */


/* TDEBUG_STR prints diagnostics */
void tdebug_str(str)          
char str[];
{
  fprintf(stderr, "LD: (Read_table) %s", str);
  fprintf(filerr, "LD: (Read_table) %s", str);
  fflush(stderr);
  fflush(filerr);
}                                           /* end TDEBUG_STR */


/* TDEBUG_CHAR prints diagnostics */
void tdebug_char(c)           
char c;
{
  fprintf(stderr, "%c\n", c);
  fprintf(filerr, "%c\n", c);
  fflush(stderr);
  fflush(filerr);
}                                           /* end TDEBUG_CHAR */





             /*----------MISCELLANEOUS (PARSER) FUNCTIONS-----------------*/


/* INITIALISE_CLAUSE_STACK */
void initialise_clause_stack()          
{
  int i;
  for (i = 0; i < CLAUSE_STACK_SIZE; i++ ) {
    clause_str_stack[i] = p_print_null;
  }
  clause_depth = 0;
  base_level = CLAUSE_STACK_SIZE - 1;
  current_level = 0;
  new_level = 0;
}                                      /* end INITIALISE_CLAUSE_STACK */


/* SET_CLAUSE_STACK */
void set_clause_stack(lev, str)    
int lev;
PSTRWC str;
{
  int limit;

  limit = lev;
  used_clause_stack = maxof(limit, used_clause_stack);

  if (lev < 0) {                   /* underflow */
    yyerror("Somethings wrong (set_clause_stack); underflow");
    limit = 0;
  }
  else if (lev >= CLAUSE_STACK_SIZE) {    /* overflow */
    yyerror("Somethings wrong (set_clause_stack): overflow");
    limit = CLAUSE_STACK_SIZE - 1;
  }
  clause_str_stack[limit] = str;
}                                  /* end SET_CLAUSE_STACK */


/* CLOSE_DOC_DIVS Close off prior divisions */
void close_doc_divs(level)         
int level;
{
  int i;
  int temp_level;
  int limit;

  myprint("\n");
  limit = level;
  if (level < 0) {                /* underflow */
    yyerror("Somethings wrong (close_docs_div); underflow");
    limit = 0;
  }
  else if (level >= CLAUSE_STACK_SIZE) {    /* overflow */
    yyerror("Somethings wrong (close_docs_div): overflow");
    limit = CLAUSE_STACK_SIZE - 1;
  }
  temp_level = limit;
  if (base_level > limit) {
    temp_level = base_level;
  }
  if (temp_level <= current_level) {
    for (i = current_level; i >= temp_level; i--) {
      tag_print(clause_str_stack[i]);
    }
  }
  if (base_level > limit) {
    base_level = limit;
  }
}                                      /* end CLOSE_DOC_DIVS */


/* CLOSE_ALL_DIVS closes all open divisions */
void close_all_divs()              
{
  close_doc_divs(base_level);
  initialise_clause_stack();
}                                      /* end CLOSE_ALL_DIVS */


/* CLOSE_DOWN finish off the document */
void close_down(s)                  
STRING s;
{
  PSENTRY sym = get_mode_sym(pos_edoc);

  /*    this is the original code
  close_all_divs();
  myprint("\n");
  tag_print(get_t(sym));
  tag_print(get_et(sym));
  printf("\n");
  write_stats(s); 
  */

  myprint("\n");
  tag_print(get_t(sym));
  close_all_divs();
  tag_print(get_et(sym));
  printf("\n");
  write_stats(s); 

}                                      /* end CLOSE_DOWN */


/* SET_LIST_STACK sets up list stack */
/* 6/96 changed from STRING to PSTRWC */
void set_list_stack(istart, iend, pstart, pend) 
PSTRWC istart;          /* start tag for item */
PSTRWC iend;            /* end tag for item */
PSTRWC pstart;          /* start tag for parameter */
PSTRWC pend;            /* end tag for parameter */
{
  if (list_level < -1) {    /* underflow */
    yyerror("Somethings wrong (set_list_stack): underflow");
    list_level = -1;
  }
  else if (list_level >= (LIST_STACK_SIZE - 1)) {  /* overflow */
    yyerror("Somethings wrong (set_list_stack): overflow");
    list_level = LIST_STACK_SIZE - 2;
  }
  list_level++;
  used_list_stack = maxof(list_level, used_list_stack);
  num_items[list_level] = 0;
  list_item_start[list_level] = istart;
  list_str_stack[list_level] = iend;
  list_descitemp_start[list_level] = pstart;
  list_descitemp_end[list_level] = pend;
}                                  /* end SET_LIST_STACK */


/* PRINT_END_ITEM closes off an item */
void print_end_item()              
{
  if (list_level < 0) {          /* underflow */
    yyerror("Somethings wrong (print_end_item): underflow");
  }
  else if (list_level >= LIST_STACK_SIZE) {    /* overflow */
    yyerror("Somethings wrong (print_end_item): overflow");
  }
  else if (num_items[list_level] > 0) {      /* close off last item */
    tag_print(list_str_stack[list_level]);
  }
}                                  /* end PRINT_END_ITEM */


/* CATL concatenates S to line buffer */
void catl(n,s)                            
int n;                 /* no of chars in s */
char *s;
{
  linlen = linlen + n;                  /* current chars to be in linebuf */
  if (linlen >= MAX_LINE) {             /* linebuf will overflow */
    warning("A very long line. Internal buffer size exceeded");
    linebuf[0] = SNUL;
    linlen = n;
  }
  strcat(linebuf, s);
}                                       /* end CATL */


/* DO_NEWLINE does stuff at start of each line */
void do_newline()                
{
  lineno++;
  linlen = 0;
  linebuf[0] = SNUL;
  if ((lineno % EVERY_N_LINES) == 0) {
    line_count++;
    fprintf(stdout, "[%d] ", line_count);
    fflush(stdout);
    if ((line_count % 10) == 0) {
      fprintf(stdout, "\n");
    }
  }
}                                       /* end DO_NEWLINE */


/*-----------------------------------------------------------------------*/
/* void read_table()                                                     */
/*           keywords are:                                               */
/*  COMMENT=               anything                                      */
/*  C=                     comment (added 6/96)                          */
/*  ESCAPE_CHAR=           single char (default \ )                      */
/*  NEWLINE_CHAR=          single char (default n )                      */
/*  HORIZONTAL_TAB_CHAR=   single char (default t )                      */
/*  VERTICAL_TAB_CHAR=     single char (default v )                      */
/*  BACKSPACE_CHAR=        single char (default b )                      */
/*  CARRIAGE_RETURN_CHAR=  single char (default r )                      */
/*  FORMFEED_CHAR=         single char (default f )                      */
/*  AUDIBLE_ALERT_CHAR=    single char (default a )                      */
/*  HEX_CHAR=              single char (default x )                      */
/*  INCLUDE=               file name for inclusion                       */
/*  TYPE=                  command type keyword                          */
/*  NAME=                  LaTeX command or environment name             */
/*  START_TAG=             " print this before NAME "                    */
/*  END_TAG=               " print this after all processing "           */
/*  PRINT_CONTROL=         NO_PRINT (or maybe extended later)            */
/*  OPT_PARAM=             FIRST or LAST (position of optional param)    */
/*  START_OPT=             " print this before optional param "          */
/*  END_OPT=               " print this after optional param             */
/*  PRINT_OPT=             NO_PRINT ing of the parameter text            */
/*  REQPARAMS=             0 through 9 (number of required params)       */
/*  START_TAG_1=           " print before req. param 1 "                 */
/*  END_TAG_1=             " print after req. param 1 "                  */
/*  PRINT_P1=              NO_PRINT ing of the parameter text            */
/*    ...                                                                */
/*  START_TAG_9=           " print before req. param 9 "                 */
/*  END_TAG_9=             " print after req. param 9 "                  */
/*  PRINT_P9=              NO_PRINT ing of the parameter text            */
/*  SECTIONING_LEVEL       keyword describing the level                  */
/*  START_ITEM=            " print before \item "                        */
/*  END_ITEM=              " print after processing \item text "         */
/*  START_ITEM_PARAM=      " print before \item opt. param "             */
/*  END_ITEM_PARAM=        " print after \item opt. param "              */
/*  CONTINUE=              " continuation of previous string "           */
/*  SPECIAL_TOKEN=         integer (pre-defined as a yac token)          */
/*  END_TYPE               must be last entry to close off TYPE=         */
/*  END_CTFILE=            anything, ends file                           */
/*  OTHER_SOURCE=          o/p printing (added 6/96)                     */
/*  PC_AT_START=                        (added 6/96)                     */
/*  PC_AT_END=                        (added 6/96)                       */
/*-----------------------------------------------------------------------*/
/* READ_TABLE reads command table */
void read_table()                      
{

  char line[MAX_TABLE_LINE];
  char code_name[MAX_TABLE_LINE];
  char noquotes[MAX_TABLE_LINE];
  int num_names;
  int num;
  PSENTRY entry;
  PSENTRY last_entry = NULL;              /* last command entry */
  char str[MAX_TABLE_LINE];
  int last_string = CONT_UNKNOWN_ENUM;
  FILE *filtabin;                         /* input file */
  char *cin;
  int endit;
  PSTRWC lastp = NULL;                    /* last print control */
  int key;                                /* command keyword */
  int numbuf;

  filtabin = get_ct_filep();              /* get root file */

  for (;;) {                              /* loop over all files */
    line[0] = SNUL;
    cin = fgets(line, MAX_TABLE_LINE, filtabin);   /* read a line */
    used_table_line = maxof(strlen(line), used_table_line);
    incr_ct_linenum();
    if (TDEBUG) {
      tdebug_tline(line);
    }
    if (feof(filtabin) != 0) {                             /* end of this file */
      if (TDEBUG) {
        tdebug_str_int("Calling RESET_CT_FILE with ct_file_num=", ct_file_num);
      }
      endit = reset_ct_file();
      if (TDEBUG) {
        tdebug_str_int("Called RESET_CT_FILE and ct_file_num now=", ct_file_num);
        tdebug_str_int("    END OF FILE, endit = (reset_ct_file) = ", endit);
      }
      filtabin = get_ct_filep();
      if (endit == EOF) {                          /* end of all files */
        break;
      }
      continue;                          /* skip rest of this line processing */
    }
    strcpy(table_line, line);
            /* get first name on line */
    num_names = sscanf(line, "%s", code_name);
    if (num_names <= 0) {                    /* blank line */
      continue;                              /* ignore */
    }
    strtouc(code_name);        /* convert to upper case for comparisons */
    key = key_to_int(code_name);
    switch (key) {
      case END_CTFILE : { /* end of file */
        if (TDEBUG) {
          tdebug_str("END_CTFILE=\n");
        }
        if (TDEBUG) {
          tdebug_str_int("Calling RESET_CT_FILE with ct_file_num=", ct_file_num);
        }
        endit = reset_ct_file();
        if (TDEBUG) {
          tdebug_str_int("Called RESET_CT_FILE and ct_file_num now=", ct_file_num);
          tdebug_str_int("    END OF FILE, endit = (reset_ct_file) = ", endit);
        }
        filtabin = get_ct_filep();
        if (endit == EOF) {                          /* end of all files */
          return;
        }
        continue;                          /* skip rest of this line processing */
      }
      case CA :
      case COMMENT : {  /* comment line */
        if (TDEBUG) {
          tdebug_str("C=\n");
        }
        continue;                                            /* ignore */
      }
      case INCLUDE : {        /* file inclusion */
        if (TDEBUG) {
          tdebug_str("INCLUDE= ");
        }
        last_string = CONT_UNKNOWN_ENUM;
        delete_to_chars("=:", line);
        sscanf(line, "%s", str );
        if (TDEBUG) {
          tdebug_str(str);
          tdebug_str("\n");
          tdebug_str_int("Calling SET_CT_FILE with ct_file_num=", ct_file_num);
        }
        set_ct_file(str);
        if (TDEBUG) {
          tdebug_str_int("  SET_CT_FILE changed ct_file_num to ", ct_file_num);
        }
        filtabin = get_ct_filep();
        break;
      }
      case TYPE :  {    /* start of a record */
        last_string = CONT_UNKNOWN_ENUM;
        entry = new_table_entry();
        last_entry = entry;
        if (num_table_entries >= MAX_TABLE_ENTRIES) {
          table_error("FATAL ERROR: Too many table entries");
          exit(0);
        }
        symbol_table[num_table_entries] = entry;
        num_table_entries++;
        delete_to_chars("=:", line);
        sscanf(line, "%s", str);
        strtouc(str);
        if (TDEBUG) {
          sprintf(errstr, "%s %s\n", code_name, str);
          tdebug_str(errstr);
        }
        entry->kind = ctype_to_int(str);
        break;
      }
      case END_TYPE :  { /* end of a record */
        if (TDEBUG) {
          sprintf(errstr, "%s\n", code_name );
          tdebug_str(errstr);
        }
        lastp = NULL;
        last_string = CONT_UNKNOWN_ENUM;
        complete_entry(last_entry);
        break;
      }
      case NAMEK : {                        /* process NAME= */
        last_string = CONT_UNKNOWN_ENUM;
        delete_to_chars("=:", line);
        sscanf(line, "%s", str);
        entry->command = strsave(str);
        if (TDEBUG) {
          sprintf(errstr, "%s %s\n", code_name, entry->command);
          tdebug_str(errstr);
        }
        break;
        }
      case PRINT_CONTROL : {                /* process  PRINT_CONTROL= */
        last_string = CONT_UNKNOWN_ENUM;
        delete_to_chars("=:", line);
            /* changed 6/96 */
        entry->printing = parse_pc(line);
        if (TDEBUG) {
          sprintf(errstr, "%s %s\n", code_name, line);
          tdebug_str(errstr);
        }
        break;
      }
      case START_TAG : {                     /* process START_TAG= */
        last_string = CONT_TAG;
        delete_quotes(line,noquotes);
        if (TDEBUG) {
          sprintf(errstr, "%s %s\n", code_name, noquotes);
          tdebug_str(errstr);
        }
        /* 6/96 this, and all like it changed */
        lastp = create_print_tag(strsave(noquotes));
        entry->command_t = lastp;
        break;
      }
      case END_TAG : {                       /* process END_TAG= */
        last_string = CONT_TAG;
        delete_quotes(line,noquotes);
        if (TDEBUG) {
          sprintf(errstr, "%s %s\n", code_name, noquotes);
          tdebug_str(errstr);
        }
        lastp = create_print_tag(strsave(noquotes));
        entry->command_et = lastp;
        break;
      }
      case OPT_PARAM : {                    /* process OPT_PARAM= */
        last_string = CONT_UNKNOWN_ENUM;
        delete_to_chars("=:", line);
        sscanf(line, "%s", str);
        strtouc(str);
        if (TDEBUG) {
          sprintf(errstr, "%s %s\n", code_name, str);
          tdebug_str(errstr);
        }
        entry->opt_param_pos = pos_to_int(str);
        break;
      }
      case PRINT_OPT : {                   /* process PRINT_OPT= */
        last_string = CONT_UNKNOWN_ENUM;
        delete_to_chars("=:", line);
        /* changed 6/96 */
        entry->print_opt[1] = parse_pc(line);
        if (TDEBUG) {
          sprintf(errstr, "%s\n", code_name);
          tdebug_str(errstr);
        }
        break;
      }
      case START_OPT : {                    /* process START_OPT= */
        last_string = CONT_TAG;
        delete_quotes(line,noquotes);
        if (TDEBUG) {
          sprintf(errstr, "%s %s\n", code_name, noquotes);
          tdebug_str(errstr);
        }
        lastp = create_print_tag(strsave(noquotes));
        entry->opt_param_t = lastp;
        break;
      }
      case END_OPT : {                      /* process END_OPT= */
        last_string = CONT_TAG;
        delete_quotes(line,noquotes);
        if (TDEBUG) {
          sprintf(errstr, "%s %s\n", code_name, noquotes);
          tdebug_str(errstr);
        }
        lastp = create_print_tag(strsave(noquotes));
        entry->opt_param_et = lastp;
        break;
      }
      case REQPARAMS : {                     /* process REQPARAMS= */
        last_string = CONT_UNKNOWN_ENUM;
        delete_to_chars("=:", line);
        sscanf(line, "%d", &num);
        if (TDEBUG) {
          sprintf(errstr, "%s %d\n", code_name, num);
          tdebug_str(errstr);
        }
        if (num >= 0 && num <= 9 ) {
          entry->numparams = num;
        }
        else {
          table_error("REQPARAMS out of range (0-9)");
        }
        break;
      }
      case START_TAG_1 : {                 /* process START_TAG_1= */
        last_string = CONT_TAG;
        delete_quotes(line,noquotes);
        if (TDEBUG) {
          sprintf(errstr, "%s %s\n", code_name, noquotes);
          tdebug_str(errstr);
        }
        lastp = create_print_tag(strsave(noquotes));
        entry->param_t[0] = lastp;
        break;
      }
      case END_TAG_1 : {                  /* process END_TAG_1= */
        last_string = CONT_TAG;
        delete_quotes(line,noquotes);
        if (TDEBUG) {
          sprintf(errstr, "%s %s\n", code_name, noquotes);
          tdebug_str(errstr);
        }
        lastp = create_print_tag(strsave(noquotes));
        entry->param_et[0] = lastp;
        break;
      }
      case START_TAG_2 : {               /* process START_TAG_2= */
        last_string = CONT_TAG;
        delete_quotes(line,noquotes);
        if (TDEBUG) {
          sprintf(errstr, "%s %s\n", code_name, noquotes);
          tdebug_str(errstr);
        }
        lastp = create_print_tag(strsave(noquotes));
        entry->param_t[1] =lastp;
        break;
      }
      case END_TAG_2 : {                  /* process END_TAG_2= */
        last_string = CONT_TAG;
        delete_quotes(line,noquotes);
        if (TDEBUG) {
          sprintf(errstr, "%s %s\n", code_name, noquotes);
          tdebug_str(errstr);
        }
        lastp = create_print_tag(strsave(noquotes));
        entry->param_et[1] = lastp;
        break;
      }
      case START_TAG_3 : {                /* process START_TAG_3= */
        last_string = CONT_TAG;
        delete_quotes(line,noquotes);
        if (TDEBUG) {
          sprintf(errstr, "%s %s\n", code_name, noquotes);
          tdebug_str(errstr);
        }
        lastp = create_print_tag(strsave(noquotes));
        entry->param_t[2] = lastp;
        break;
      }
    case END_TAG_3 : { /* process END_TAG_3= */
      last_string = CONT_TAG;
      delete_quotes(line,noquotes);
        if (TDEBUG) {
          sprintf(errstr, "%s %s\n", code_name, noquotes);
          tdebug_str(errstr);
        }
      lastp = create_print_tag(strsave(noquotes));
      entry->param_et[2] = lastp;
      break;
    }
    case START_TAG_4 : { /* process START_TAG_4= */
      last_string = CONT_TAG;
      delete_quotes(line,noquotes);
        if (TDEBUG) {
          sprintf(errstr, "%s %s\n", code_name, noquotes);
          tdebug_str(errstr);
        }
      lastp = create_print_tag(strsave(noquotes));
      entry->param_t[3] = lastp;
      break;
    }
    case END_TAG_4 : { /* process END_TAG_4= */
      last_string = CONT_TAG;
      delete_quotes(line,noquotes);
        if (TDEBUG) {
          sprintf(errstr, "%s %s\n", code_name, noquotes);
          tdebug_str(errstr);
        }
      lastp = create_print_tag(strsave(noquotes));
      entry->param_et[3] = lastp;
      break;
    }
      case START_TAG_5 : { /* process START_TAG_5= */
      last_string = CONT_TAG;
      delete_quotes(line,noquotes);
        if (TDEBUG) {
          sprintf(errstr, "%s %s\n", code_name, noquotes);
          tdebug_str(errstr);
        }
      lastp = create_print_tag(strsave(noquotes));
      entry->param_t[4] = lastp;
      break;
    }
      case END_TAG_5 : { /* process END_TAG_5= */
      last_string = CONT_TAG;
      delete_quotes(line,noquotes);
        if (TDEBUG) {
          sprintf(errstr, "%s %s\n", code_name, noquotes);
          tdebug_str(errstr);
        }
      lastp = create_print_tag(strsave(noquotes));
      entry->param_et[4] = lastp;
      break;
    }
      case START_TAG_6 : { /* process START_TAG_6= */
      last_string = CONT_TAG;
      delete_quotes(line,noquotes);
        if (TDEBUG) {
          sprintf(errstr, "%s %s\n", code_name, noquotes);
          tdebug_str(errstr);
        }
      lastp = create_print_tag(strsave(noquotes));
      entry->param_t[5] = lastp;
      break;
    }
      case END_TAG_6 : { /* process END_TAG_6= */
      last_string = CONT_TAG;
      delete_quotes(line,noquotes);
        if (TDEBUG) {
          sprintf(errstr, "%s %s\n", code_name, noquotes);
          tdebug_str(errstr);
        }
      lastp = create_print_tag(strsave(noquotes));
      entry->param_et[5] = lastp;
      break;
    }
      case START_TAG_7 : { /* process START_TAG_7= */
      last_string = CONT_TAG;
      delete_quotes(line,noquotes);
        if (TDEBUG) {
          sprintf(errstr, "%s %s\n", code_name, noquotes);
          tdebug_str(errstr);
        }
      lastp = create_print_tag(strsave(noquotes));
      entry->param_t[6] = lastp;
      break;
    }
      case END_TAG_7 : { /* process END_TAG_7= */
      last_string = CONT_TAG;
      delete_quotes(line,noquotes);
        if (TDEBUG) {
          sprintf(errstr, "%s %s\n", code_name, noquotes);
          tdebug_str(errstr);
        }
      lastp = create_print_tag(strsave(noquotes));
      entry->param_et[6] = lastp;
      break;
    }
      case START_TAG_8 : { /* process START_TAG_8= */
      last_string = CONT_TAG;
      delete_quotes(line,noquotes);
        if (TDEBUG) {
          sprintf(errstr, "%s %s\n", code_name, noquotes);
          tdebug_str(errstr);
        }
      lastp = create_print_tag(strsave(noquotes));
      entry->param_t[7] = lastp;
      break;
    }
      case END_TAG_8 : { /* process END_TAG_8= */
      last_string = CONT_TAG;
      delete_quotes(line,noquotes);
        if (TDEBUG) {
          sprintf(errstr, "%s %s\n", code_name, noquotes);
          tdebug_str(errstr);
        }
      lastp = create_print_tag(strsave(noquotes));
      entry->param_et[7] = lastp;
      break;
    }
      case START_TAG_9 : { /* process START_TAG_9= */
      last_string = CONT_TAG;
      delete_quotes(line,noquotes);
        if (TDEBUG) {
          sprintf(errstr, "%s %s\n", code_name, noquotes);
          tdebug_str(errstr);
        }
      lastp = create_print_tag(strsave(noquotes));
      entry->param_t[8] = lastp;
      break;
    }
      case END_TAG_9 : { /* process END_TAG_9= */
      last_string = CONT_TAG;
      delete_quotes(line,noquotes);
        if (TDEBUG) {
          sprintf(errstr, "%s %s\n", code_name, noquotes);
          tdebug_str(errstr);
        }
      lastp = create_print_tag(strsave(noquotes));
      entry->param_et[8] = lastp;
      break;
    }
      case PRINT_P1 : { /* process PRINT_P1= */
      last_string = CONT_UNKNOWN_ENUM;
      delete_to_chars("=:", line);
      /* changed 6/96 */
      entry->print_param[0] = parse_pc(line);
      if (TDEBUG) {
        sprintf(errstr, "%s %s\n", code_name, line);
        tdebug_str(errstr);
      }
      break;
    }
      case PRINT_P2 : { /* process PRINT_P2= */
      last_string = CONT_UNKNOWN_ENUM;
      delete_to_chars("=:", line);
      /* changed 6/96 */
      entry->print_param[1] = parse_pc(line);
      if (TDEBUG) {
        sprintf(errstr, "%s %s\n", code_name, line);
        tdebug_str(errstr);
      }
      break;
    }
      case PRINT_P3 : { /* process PRINT_P3= */
      last_string = CONT_UNKNOWN_ENUM;
      delete_to_chars("=:", line);
      /* changed 6/96 */
      entry->print_param[2] = parse_pc(line);
      if (TDEBUG) {
        sprintf(errstr, "%s %s\n", code_name, line);
        tdebug_str(errstr);
      }
      break;
    }
      case PRINT_P4 : { /* process PRINT_P4= */
      last_string = CONT_UNKNOWN_ENUM;
      delete_to_chars("=:", line);
      /* changed 6/96 */
      entry->print_param[3] = parse_pc(line);
      if (TDEBUG) {
        sprintf(errstr, "%s %s\n", code_name, line);
        tdebug_str(errstr);
      }
      break;
    }
      case PRINT_P5 : { /* process PRINT_P5= */
      last_string = CONT_UNKNOWN_ENUM;
      delete_to_chars("=:", line);
      /* changed 6/96 */
      entry->print_param[4] = parse_pc(line);
      if (TDEBUG) {
        sprintf(errstr, "%s %s\n", code_name, line);
        tdebug_str(errstr);
      }
      break;
    }
      case PRINT_P6 : { /* process PRINT_P6= */
      last_string = CONT_UNKNOWN_ENUM;
      delete_to_chars("=:", line);
      /* changed 6/96 */
      entry->print_param[5] = parse_pc(line);
      if (TDEBUG) {
        sprintf(errstr, "%s %s\n", code_name, line);
        tdebug_str(errstr);
      }
      break;
    }
      case PRINT_P7 : { /* process PRINT_P7= */
      last_string = CONT_UNKNOWN_ENUM;
      delete_to_chars("=:", line);
      /* changed 6/96 */
      entry->print_param[6] = parse_pc(line);
      if (TDEBUG) {
        sprintf(errstr, "%s %s\n", code_name, line);
        tdebug_str(errstr);
      }
      break;
    }
      case PRINT_P8 : { /* process PRINT_P8= */
      last_string = CONT_UNKNOWN_ENUM;
      delete_to_chars("=:", line);
      /* changed 6/96 */
      entry->print_param[7] = parse_pc(line);
      if (TDEBUG) {
        sprintf(errstr, "%s %s\n", code_name, line);
        tdebug_str(errstr);
      }
      break;
    }
      case PRINT_P9 : { /* process PRINT_P9= */
      last_string = CONT_UNKNOWN_ENUM;
      delete_to_chars("=:", line);
      /* changed 6/96 */
      entry->print_param[8] = parse_pc(line);
      if (TDEBUG) {
        sprintf(errstr, "%s %s\n", code_name, line);
        tdebug_str(errstr);
      }
      break;
    }
      case START_ITEM : { /* process START_ITEM= */
      last_string = CONT_TAG;
      delete_quotes(line,noquotes);
      if (TDEBUG) {
        sprintf(errstr, "%s %s\n", code_name, noquotes);
        tdebug_str(errstr);
      }
      lastp = create_print_tag(strsave(noquotes));
      entry->param_t[7] = lastp;
      break;
    }
      case END_ITEM : { /* process END_ITEM= */
      last_string = CONT_TAG;
      delete_quotes(line,noquotes);
      if (TDEBUG) {
        sprintf(errstr, "%s %s\n", code_name, noquotes);
        tdebug_str(errstr);
      }
      lastp = create_print_tag(strsave(noquotes));
      entry->param_et[7] = lastp;
      break;
    }
      case START_ITEM_PARAM : { /* process START_ITEM_PARAM= */
      last_string = CONT_TAG;
      delete_quotes(line,noquotes);
      if (TDEBUG) {
        sprintf(errstr, "%s %s\n", code_name, noquotes);
        tdebug_str(errstr);
      }
      lastp = create_print_tag(strsave(noquotes));
      entry->param_t[8] = lastp;
      break;
    }
      case END_ITEM_PARAM : { /* process END_ITEM_PARAM= */
      last_string = CONT_TAG;
      delete_quotes(line,noquotes);
      if (TDEBUG) {
        sprintf(errstr, "%s %s\n", code_name, noquotes);
        tdebug_str(errstr);
      }
      lastp = create_print_tag(strsave(noquotes));
      entry->param_et[8] = lastp;
      break;
    }

      case CONTINUE :                         /* process CONTINUE= */
      case STRINGG : {                        /* process STRING= */
        if (last_string == CONT_UNKNOWN_ENUM) {
          sprintf(errstr, "Can not use %s at this point", code_name);
          table_error(errstr);
        }
        else {
          delete_quotes(line,noquotes);
        if (TDEBUG) {
          sprintf(errstr, "%s %s\n", code_name, noquotes);
          tdebug_str(errstr);
        }
          lastp = append_to_write(lastp, create_print_source(strsave(noquotes)));
          set_pc_cmd(lastp, STRINGG);
        }
        break;
      }

      case SOURCE : {                          /* 6/96 process SOURCE= */
        if (last_string == CONT_UNKNOWN_ENUM) {
          sprintf(errstr, "Can not use %s at this point", code_name);
          table_error(errstr);
        }
        else {
          delete_to_chars("=:", line);
        if (TDEBUG) {
          sprintf(errstr, "%s %s\n", code_name, line);
          tdebug_str(errstr);
        }
          lastp = append_to_write(lastp, parse_pc(line));
          set_pc_cmd(lastp, SOURCE);
        }
        break;
      }

      case PC_AT_START : {                    /* 6/96 process PC_AT_START= */
        last_string = CONT_UNKNOWN_ENUM;
        delete_to_chars("=:", line);
        entry->start_pc = parse_pc(line);
        if (TDEBUG) {
          sprintf(errstr, "%s %s\n", code_name, line);
          tdebug_str(errstr);
        }
        break;
      }

      case PC_AT_END : {                      /* 6/96 process PC_AT_END= */
        if (TDEBUG) {
          tdebug_str("PC_AT_END=\n");
        }
        last_string = CONT_UNKNOWN_ENUM;
        delete_to_chars("=:", line);
        entry->end_pc = parse_pc(line);
        if (TDEBUG) {
          sprintf(errstr, "%s %s\n", code_name, line);
          tdebug_str(errstr);
        }
        break;
      }

      case SECTIONING_LEVEL : {              /* process SECTIONING_LEVEL= */
        last_string = CONT_UNKNOWN_ENUM;
        delete_to_chars("=:", line);
        sscanf(line, "%s", str);
        strtouc(str);
        if (TDEBUG) {
          sprintf(errstr, "%s %s\n", code_name, str);
          tdebug_str(errstr);
        }
        entry->sectioning_level = level_to_int(str);
        break;
      }

      case RESET_SYSBUF : {                 /* 6/96 process RESET_SYSBUF= */
        if (TDEBUG) {
          sprintf(errstr, "%s\n", code_name);
          tdebug_str(errstr);
        }
        if (last_string == CONT_UNKNOWN_ENUM) {
          sprintf(errstr, "Can not use %s at this point", code_name);
          table_error(errstr);
        }
        else {                             /* takes no value */
          lastp = append_to_write(lastp, create_st_rwc());
          set_pc_cmd(lastp, RESET_SYSBUF);
        }
        break;
      }

      case RESET_BUFFER : {                 /* 6/96 process RESET_BUFFER */
        if (last_string == CONT_UNKNOWN_ENUM) {
          sprintf(errstr, "Can not use %s at this point", code_name);
          table_error(errstr);
        }
        else {                             /* get buffer number */
          numbuf = 0;
          sscanf(line, "%s %d", str, &numbuf);
        if (TDEBUG) {
          sprintf(errstr, "%s %d\n", code_name, numbuf);
          tdebug_str(errstr);
        }
          lastp = append_to_write(lastp, create_st_rwc());
          set_pc_cmd(lastp, RESET_BUFFER);
          set_pc_enum(lastp, BUFFER);
          set_pcdata_num(lastp, chk_bufnum(numbuf));
        }
        break;
      }

      case RESET_FILE : {                   /* 6/96 process RESET_FILE  */
        if (last_string == CONT_UNKNOWN_ENUM) {
          sprintf(errstr, "Can not use %s at this point", code_name);
          table_error(errstr);
        }
        else {                             /* get file name */
          delete_to_chars("=:", line);
          sscanf(line, "%s", str);
        if (TDEBUG) {
          sprintf(errstr, "%s %s\n", code_name, str);
          tdebug_str(errstr);
        }
          lastp = append_to_write(lastp, create_st_rwc());
          set_pc_cmd(lastp, RESET_FILE);
          set_pc_enum(lastp, FFILE);
          set_pcdata_name(lastp, chk_ufname(str));
        }
        break;
      }

      case SWITCH_BACK : {                 /* 11/96 process SWITCH_BACK */
        if (TDEBUG) {
          sprintf(errstr, "%s\n", code_name);
          tdebug_str(errstr);
        }
        if (last_string == CONT_UNKNOWN_ENUM) {
          sprintf(errstr, "Can not use %s at this point", code_name);
          table_error(errstr);
        }
        else {                             /* takes no value */
          lastp = append_to_write(lastp, create_st_rwc());
          set_pc_cmd(lastp, SWITCH_BACK);
          set_pc_enum(lastp, RESET);
        }
        break;
      }

      case SWITCH_TO_BUFFER : {            /* 11/96 process SWITCH_TO_BUFFER */
        if (last_string == CONT_UNKNOWN_ENUM) {
          sprintf(errstr, "Can not use %s at this point", code_name);
          table_error(errstr);
        }
        else {                             /* get buffer number */
          numbuf = 0;
          sscanf(line, "%s %d", str, &numbuf);
        if (TDEBUG) {
          sprintf(errstr, "%s %d\n", code_name, numbuf);
          tdebug_str(errstr);
        }
          lastp = append_to_write(lastp, create_st_rwc());
          set_pc_cmd(lastp, SWITCH_TO_BUFFER);
          set_pc_enum(lastp, TO_BUFFER);
          set_pcdata_num(lastp, chk_bufnum(numbuf));
        }
        break;
      }

      case SWITCH_TO_FILE : {              /* 11/96 process SWITCH_TO_FILE */
        if (last_string == CONT_UNKNOWN_ENUM) {
          sprintf(errstr, "Can not use %s at this point", code_name);
          table_error(errstr);
        }
        else {                             /* get file name */
          delete_to_chars("=:", line);
          sscanf(line, "%s", str);
        if (TDEBUG) {
          sprintf(errstr, "%s %s\n", code_name, str);
          tdebug_str(errstr);
        }
          lastp = append_to_write(lastp, create_st_rwc());
          set_pc_cmd(lastp, SWITCH_TO_FILE);
          set_pc_enum(lastp, TO_FILE);
          set_pcdata_name(lastp, chk_ufname(str));
        }
        break;
      }

      case SWITCH_TO_SYSBUF : {            /* 11/96 process SWITCH_TO_SYSBUF */
        if (TDEBUG) {
          sprintf(errstr, "%s\n", code_name);
          tdebug_str(errstr);
        }
        if (last_string == CONT_UNKNOWN_ENUM) {
          sprintf(errstr, "Can not use %s at this point", code_name);
          table_error(errstr);
        }
        else {                             /* takes no value */
          lastp = append_to_write(lastp, create_st_rwc());
          set_pc_cmd(lastp, SWITCH_TO_SYSBUF);
          set_pc_enum(lastp, TO_SYSBUF);
        }
        break;
      }

      case SET_MODE : {                     /* 6/96 process SET_MODE */
        if (last_string == CONT_UNKNOWN_ENUM) {
          sprintf(errstr, "Can not use %s at this point", code_name);
          table_error(errstr);
        }
        else {                               /* get mode name */
          delete_to_chars("=:", line);
          sscanf(line, "%s", str);
        if (TDEBUG) {
          sprintf(errstr, "%s %s\n", code_name, str);
          tdebug_str(errstr);
        }
          numbuf = add_mode(str);
          lastp = append_to_write(lastp, create_st_rwc());
          set_pc_cmd(lastp, SET_MODE);
          set_pc_enum(lastp, MODE);
          set_pcdata_num(lastp, numbuf);
        }
        break;
      }

      case RESET_MODE : {                     /* 6/96 process RESET_MODE */
        if (TDEBUG) {
          sprintf(errstr, "%s\n", code_name);
          tdebug_str(errstr);
        }
        if (last_string == CONT_UNKNOWN_ENUM) {
          sprintf(errstr, "Can not use %s at this point", code_name);
          table_error(errstr);
        }
        else {                            /* takes no value */
          lastp = append_to_write(lastp, create_st_rwc());
          set_pc_cmd(lastp, RESET_MODE);
        }
        break;
      }

      case IN_MODE : {                     /* 6/96 process IN_MODE */
        complete_entry(last_entry);         /* complete prior defn */
        delete_to_chars("=:", line);
        sscanf(line, "%s", str);
        if (TDEBUG) {
          sprintf(errstr, "%s %s\n", code_name, str);
          tdebug_str(errstr);
        }
        entry = new_table_entry();          /* create new symbol entry */
        add_to_modelist(last_entry, entry); /* add it to the list of modes */
        set_mode_name(str, entry);          /* set the mode name in the new entry */
        last_entry = entry;                 /* update last entry pointer */
        break;
      }

      case END_MODE : {                     /* 6/96 process END_MODE */
        if (TDEBUG) {
          sprintf(errstr, "%s\n", code_name);
          tdebug_str(errstr);
        }
        /* PERHAPS DO STUFF HERE LATER */
        break;
      }


      case SPECIAL_TOKEN : {                /* process SPECIAL_TOKEN= */
        last_string = CONT_UNKNOWN_ENUM;
        delete_to_chars("=:", line);
        sscanf(line, "%d", &num);
        if (TDEBUG) {
          sprintf(errstr, "%s %d\n", code_name, num);
          tdebug_str(errstr);
        }
        if (num < MIN_GRAMM_SPECIAL) {
          table_error("SPECIAL_TOKEN value too small");
          tdebug_str_int("Value sould be at least",MIN_GRAMM_SPECIAL);
        }
        entry->special_token = num;
        break;
      }


      case ESCAPE_CHAR : {   /* escape char */
      if (TDEBUG) {
        tdebug_str("ESCAPE_CHAR= ");
      }
      last_string = CONT_UNKNOWN_ENUM;
      delete_to_chars("=:", line);
      sscanf(line, "%s", str );
      escape_char = str[0];
      if (TDEBUG) {
        tdebug_char(escape_char);
      }
      break;
    }
      case NEWLINE_CHAR : {   /*  newline char */
      if (TDEBUG) {
        tdebug_str("NEWLINE_CHAR= ");
      }
      last_string = CONT_UNKNOWN_ENUM;
      delete_to_chars("=:", line);
      sscanf(line, "%s", str );
      newline_char = str[0];
      if (TDEBUG) {
        tdebug_char(newline_char);
      }
      break;
    }
      case HORIZONTAL_TAB_CHAR : {   /*  char */
      if (TDEBUG) {
        tdebug_str("HORIZONTAL_TAB_CHAR= ");
      }
      last_string = CONT_UNKNOWN_ENUM;
      delete_to_chars("=:", line);
      sscanf(line, "%s", str);
      horizontal_tab_char = str[0];
      if (TDEBUG) {
        tdebug_char(horizontal_tab_char);
      }
      break;
    }
      case VERTICAL_TAB_CHAR : {   /*  char */
      if (TDEBUG) {
        tdebug_str("VERTICAL_TAB_CHAR= ");
      }
      last_string = CONT_UNKNOWN_ENUM;
      delete_to_chars("=:", line);
      sscanf(line, "%s", str);
      vertical_tab_char = str[0];
      if (TDEBUG) {
        tdebug_char(vertical_tab_char);
      }
      break;
    }
      case BACKSPACE_CHAR : {   /*  char */
      if (TDEBUG) {
        tdebug_str("BACKSPACE_CHAR= ");
      }
      last_string = CONT_UNKNOWN_ENUM;
      delete_to_chars("=:", line);
      sscanf(line, "%s", str);
      backspace_char = str[0];
      if (TDEBUG) {
        tdebug_char(backspace_char);
      }
      break;
    }
      case CARRIAGE_RETURN_CHAR : {   /*  char */
      if (TDEBUG) {
        tdebug_str("CARRIAGE_RETURN_CHAR= ");
      }
      last_string = CONT_UNKNOWN_ENUM;
      delete_to_chars("=:", line);
      sscanf(line, "%s", str);
      carriage_return_char = str[0];
      if (TDEBUG) {
        tdebug_char(carriage_return_char);
      }
      break;
    }
      case FORMFEED_CHAR : {   /*  char */
      if (TDEBUG) {
        tdebug_str("FORMFEED_CHAR= ");
      }
      last_string = CONT_UNKNOWN_ENUM;
      delete_to_chars("=:", line);
      sscanf(line, "%s", str);
      formfeed_char = str[0];
      if (TDEBUG) {
        tdebug_char(formfeed_char);
      }
      break;
    }
      case AUDIBLE_ALERT_CHAR : {   /*  char */
      if (TDEBUG) {
        tdebug_str("AUDIBLE_ALERT_CHAR= ");
      }
      last_string = CONT_UNKNOWN_ENUM;
      delete_to_chars("=:", line);
      sscanf(line, "%s", str);
      audible_alert_char = str[0];
      if (TDEBUG) {
        tdebug_char(audible_alert_char);
      }
      break;
    }
      case HEX_CHAR : {   /*  char */
      if (TDEBUG) {
        tdebug_str("HEX_CHAR= ");
      }
      last_string = CONT_UNKNOWN_ENUM;
      delete_to_chars("=:", line);
      sscanf(line, "%s", str);
      hex_char = str[0];
      if (TDEBUG) {
        tdebug_char(hex_char);
      }
      break;
    }

    case CODE_SETUP : {               /* CODE_SETUP added for code interp */
      last_string = CONT_UNKNOWN_ENUM;
      if (TDEBUG) {
        sprintf(errstr, "%s\n", code_name);
        tdebug_str(errstr);
      }
      code_root = code_setup(filtabin);      /* parse code and store symtab */
      break;
    }

    case CODE : {                   /* CODE added for code interp */
      if (last_string == CONT_UNKNOWN_ENUM) {
        sprintf(errstr, "Can not use %s at this point", code_name);
        table_error(errstr);
      }
      else {
        if (TDEBUG) {
          sprintf(errstr, "%s\n", code_name);
          tdebug_str(errstr);
        }
        /* set up new action, parse code and store code segment */
        lastp = append_to_write(lastp, create_st_rwc());
        set_pc_code(lastp, code_action(filtabin));
      }
      break;
    }


    default : {                              /* unrecognised */
      if (TDEBUG) {
        tdebug_str("UNRECOGNISED CODE NAME\n");
      }
      last_string = CONT_UNKNOWN_ENUM;
      table_error("Unrecognised code");
      break;
    }
      }  /* end of switch */
  } /* end of loop over all files */
}                                      /* end READ_TABLE */


/* NEW_TABLE_ENTRY creates new entry for symbol table */
/* changed 6/96 */
struct st_entry *new_table_entry() 
{
  struct st_entry *p;
  int i;


  if ((p = (struct st_entry *) malloc(sizeof(struct st_entry))) != NULL) {
    p->command = dont_care_str;           /* user defined command string */
    p->kind = UNKNOWN_CTYPE;              /* user defined kind of command */
    p->parse_kind = UNKNOWN_CTYPE;        /* system defined kind of command */
    p->mode = IERR;                       /* name of mode for this command */
    p->next_mode = NULL;                  /* next mode */
    p->printing = p_default_print;        /* print control (changed 6/96) */
    p->command_t = NULL;                  /* start tag:  nothing to print */
    p->command_et = NULL;                 /* end tag: nothing to print */
    p->numparams = 0;                     /* number of params */
    p->opt_param_pos = NO_OPT_PARAM;      /* position of optional param */
    p->opt_param_t = NULL;                /* opt start tag: nothing to print */
    p->opt_param_et = NULL;               /* opt end tag: nothing to print */
    for (i = 0; i <= 1; i++) {
      p->popt_param_t[i] = NULL;          /* opt param start tags: */
      p->popt_param_et[i] = NULL;         /* opt param end tags: */
    }
    p->print_opt[0] = p_no_print;         /* print control for opt param */
    p->print_opt[1] = p_default_print;    /* print control for opt param */
    p->sectioning_level = UNKNOWN_LEVEL;  /* sectioning level */
    for (i = 0; i <= 8; i++) {
      p->param_t[i] = NULL;               /* start param tags: */
      p->param_et[i] = NULL;              /* end param tags: */
      p->print_param[i] = p_default_print; /* print control for params */
    }
    p->start_pc = p_default_print;        /* start pc (added 6/96) */
    p->end_pc = p_default_print;            /* end pc (added 6/96) */
    p->special_token = COMMAND;           /* user defined special token */
    return(p);
  }
  yyerror("FATAL ERROR: No memory left for NEW_TABLE_ENTRY");
  exit(1);
}                                         /* end NEW_TABLE_ENTRY */


/* CREATE_DEF_REQENTRY new default required comm */
void create_def_reqentry(name)     
int name;
{
  struct st_entry *entry;

  entry = new_table_entry();
  if (num_table_entries >= MAX_TABLE_ENTRIES) {
    terror_noline("FATAL ERROR: Too many table entries");
    exit(0);
  }
  entry->kind = name;
  symbol_table[num_table_entries] = entry;
  complete_table_entry(num_table_entries);
  num_table_entries++;
}                                        /* end CREATE_DEF_REQENTRY */

/* COMPLETE_TABLE_ENTRY of an entry in the command table */
void complete_table_entry(ctpos)
int ctpos;      /* position in comand table */
{
  complete_entry(symbol_table[ctpos]);
  return;
}                                        /* end COMPLETE_TABLE_ENTRY */


/* COMPLETE_ENTRY of a symbol entry */
void complete_entry(pos)                 
PSENTRY pos;
{
  int num, user_kind, popt_pos;

  num = pos->numparams;
  user_kind = pos->kind;
  popt_pos = pos->opt_param_pos;

                       /* do default stuff */
  pos->parse_kind = user_kind;
  if (popt_pos != NO_OPT_PARAM) {        /* fix up optional parameter stuff */
    pos->popt_param_t[popt_pos] = pos->opt_param_t;
    pos->popt_param_et[popt_pos] = pos->opt_param_et;
    pos->print_opt[0] = pos->print_opt[1];
  }
                      /* do specific stuff */
  switch (user_kind) {
    case COMMAND:
    case CHAR_COMMAND:           /* PRW added 11/99 for version 0.91 */
    case SPECIAL_COMMAND:
    case BEGIN_ENV:
    case SPECIAL_BEGIN_ENV:
    case END_ENV:
    case SPECIAL_END_ENV:
    case BEGIN_LIST_ENV:
    case SPECIAL_BEGIN_LIST:
    case END_LIST_ENV:
    case SPECIAL_END_LIST:
    case SECTIONING:
    case SPECIAL_SECTIONING:
    case BEGIN_VENV:
    case END_VENV:
    case VCOMMAND: {
      if ((popt_pos == LAST) || (popt_pos == FIRST && num == 0)) {
        pos->parse_kind = command_opt_types[num];
      }
      else {
        pos->parse_kind = command_types[num];
      }
      break;
    }
/* added 10/95 for TEX_CHAR taking parameters */
/* If parameters are specified, treat it like a command */
    case TEX_CHAR: {
      if ((popt_pos == LAST) || (popt_pos == FIRST && num == 0)) {
        pos->parse_kind = command_opt_types[num];
      }
      else if (num > 0) {
        pos->parse_kind = command_types[num];
      }
      break;
    }
/* end of 10/95 TEX_CHAR addition */
    case BEGIN_PICTURE_CC: {
      pos->numparams = 2;
      break;
    }
    case END_PICTURE: {
      pos->numparams = 0;
      break;
    }
    case PICTURE_CCPP: {
      pos->numparams = 4;
      break;
    }
    case PICTURE_CO: {
      pos->numparams = 2;
      break;
    }
    case PICTURE_COP: {
      pos->numparams = 3;
      break;
    }
    case PICTURE_CP: {
      pos->numparams = 2;
      break;
    }
    case PICTURE_OCC: {
      pos->numparams = 3;
      break;
    }
    case PICTURE_OCCC: {
      pos->numparams = 4;
      break;
    }
    case PICTURE_OCO: {
      pos->numparams = 3;
      break;
    }
    case PICTURE_PCOP: {
      pos->numparams = 4;
      break;
    }
/* July 1996 additions */
    case COMMAND_OOP: {
      pos->numparams = 3;
      break;
    }
    case COMMAND_OOOPP: {
      pos->numparams = 5;
      break;
    }
    case COMMAND_OPO: {
      pos->numparams = 3;
      break;
    }
    case COMMAND_POOOP: {
      pos->numparams = 5;
      break;
    }
    case COMMAND_POOP: {
      pos->numparams = 4;
      break;
    }
    case COMMAND_POOPP: {
      pos->numparams = 5;
      break;
    }
  } /* end SWITCH */
}                                        /* end COMPLETE_ENTRY */


/* PRINT_TABLE prints symbol table */
/* many changes 6/96 */
void print_table(fout)                   
FILE *fout;
{
  int i, n, num;
  PSTRWC ptr;
  PSENTRY symbol;
  STRING com = key_to_string(CA);       /* comment string */
  STRING spaces = "    ";
  int pmode;


  fprintf(fout, "%s generated command table\n\n", com);

  fprintf(fout, "%s special printing characters\n\n", com);
  fprintf(fout, "%s %c\n", key_to_string(ESCAPE_CHAR), escape_char);
  fprintf(fout, "%s %c\n", key_to_string(AUDIBLE_ALERT_CHAR), audible_alert_char);
  fprintf(fout, "%s %c\n", key_to_string(BACKSPACE_CHAR), backspace_char);
  fprintf(fout, "%s %c\n", key_to_string(CARRIAGE_RETURN_CHAR), carriage_return_char);
  fprintf(fout, "%s %c\n", key_to_string(FORMFEED_CHAR), formfeed_char);
  fprintf(fout, "%s %c\n", key_to_string(HEX_CHAR), hex_char);
  fprintf(fout, "%s %c\n", key_to_string(HORIZONTAL_TAB_CHAR), horizontal_tab_char);
  fprintf(fout, "%s %c\n", key_to_string(NEWLINE_CHAR), newline_char);
  fprintf(fout, "%s %c\n", key_to_string(VERTICAL_TAB_CHAR), vertical_tab_char);


  fprintf(fout, "\n\n%s the commands (in the internal ordering)\n\n", com);
  for (i=0; i < num_table_entries; i++) {
    symbol = symbol_table[i];
    fprintf(fout, "\n");
    if (TDEBUG) {
      tdebug_str_int("Print_table entry", i);
    }
                       /* print TYPE= kind */
    fprintf(fout, "%s %s\n", key_to_string(TYPE), ctype_to_string(symbol->kind) );
    pmode = 0;      /* start of command, so no modes yet */
    if (TDEBUG) {
      fprintf(fout, "  %s Type stored internally as %d\n", 
                          com,  symbol->kind);
    }
                      /* print NAME= command */
    if (symbol->command != dont_care_str) {
      fprintf(fout, "%s %s\n", key_to_string(NAMEK), symbol->command );
    }
    else {
      fprintf(fout, "%s %s %s\n", com, key_to_string(NAMEK), symbol->command );
      
    }

                      /* NOTE USE OF GOTO/LABEL HERE */
    domode:

                      /* print PC_AT_START= command */
    ptr = symbol->start_pc;
    fprintf(fout, "%s%s ", spaces, key_to_string(PC_AT_START));
    tprint_st_rwc(fout, ptr);
    fprintf(fout, "\n");
    if (TDEBUG) {
      fprintf(fout, "  %s value stored internally as %d\n",
                    com, get_pc_enum(ptr) );
    }

                     /* print PRINT_CONTROL= command */
    ptr = symbol->printing;
    fprintf(fout, "%s%s ", spaces, key_to_string(PRINT_CONTROL));
    tprint_st_rwc(fout, ptr);
    fprintf(fout, "\n");
    if (TDEBUG) {
      fprintf(fout, "  %s value stored internally as %d\n",
                       com,  ptr->pcenum);
    }

                     /* print START_TAG= command_t */
    ptr = symbol->command_t;
    fprintf(fout, "%s%s ", spaces, key_to_string(START_TAG));
    tprint_tag(fout, ptr);

                     /* print END_TAG= command_et */
    ptr = symbol->command_et;
    fprintf(fout, "%s%s ", spaces, key_to_string(END_TAG));
    tprint_tag(fout, ptr);

                     /* print OPT_PARAM= opt_param_pos */
    if (symbol->opt_param_pos != NO_OPT_PARAM) { /* opt param */
      fprintf(fout, "%s%s %s\n", spaces, key_to_string(OPT_PARAM),
                        pos_to_string(symbol->opt_param_pos) );
      if (TDEBUG) {
        fprintf(fout, "  %s value stored internally as %d\n",
                         com, symbol->opt_param_pos);
      }

                     /* print PRINT_OPT= command */
      ptr = symbol->print_opt[0];
      fprintf(fout, "%s%s ", spaces, key_to_string(PRINT_OPT));
      tprint_st_rwc(fout, ptr);
      fprintf(fout, "\n");
      if (TDEBUG) {
        fprintf(fout, "  %s value stored internally as %d\n",
                         com, get_pc_enum(ptr));
      }

                     /* print START_OPT= opt_param_t */
      ptr = symbol->opt_param_t;
      fprintf(fout, "%s%s ", spaces, key_to_string(START_OPT));
      tprint_tag(fout, ptr);


                     /* print END_OPT= opt_param_et */
      ptr = symbol->opt_param_et;
      fprintf(fout, "%s%s ", spaces, key_to_string(END_OPT));
      tprint_tag(fout, ptr);
    } /* end of if over OPT_PARAM */

                     /* print parameter info, if any */
    num = symbol->numparams;
    if (num > 0) {                       /* are parameters */

                     /* print REQPARAMS= numparams */
      fprintf(fout, "%sREQPARAMS= %d\n", spaces, num );
      for (n = 0; n < num; n++) {

                     /* print START_TAG_N= param_t[n] */
        ptr = symbol->param_t[n];
        fprintf(fout, "%sSTART_TAG_%d= ", spaces, (n+1) );
        tprint_tag(fout, ptr);

                     /* print END_TAG_N= param_et[n] */
        ptr = symbol->param_et[n];
        fprintf(fout, "%sEND_TAG_%d=  ", spaces, (n+1) );
        tprint_tag(fout, ptr);

                     /* print PRINT_Pn= print_param[n] */
        ptr = symbol->print_param[n];
        fprintf(fout, "%sPRINT_P%d= ", spaces, (n+1) );
        tprint_st_rwc(fout, ptr);
        fprintf(fout, "\n");
          if (TDEBUG) {
            fprintf(fout, "   %s PRINT_P%d stored internally as %d\n",
                           com, (n+1), get_pc_enum(ptr));
          }
      }  /* end loop over nunparams */
    }    /* end if num > 0 */

                     /* print SECTIONING_LEVEL= sectioning_level */
    if (symbol_table[i]->kind == SECTIONING) { /* sectioning command */
      fprintf(fout, "%s%s %s\n", spaces, key_to_string(SECTIONING_LEVEL),
                        level_to_string(symbol->sectioning_level) );
      if (TDEBUG) {
        fprintf(fout, "  %s value stored internally as %d\n",
                           com, symbol->sectioning_level);
      }
    }

                     /* print SPECIAL_TOKEN= token number */
    if ((TDEBUG) || (symbol->special_token != COMMAND)) {
      fprintf(fout, "%s%s %d\n", spaces,
                     key_to_string(SPECIAL_TOKEN), get_special_token(symbol) );
    }

                     /* extra debug printing */
    if (TDEBUG) {
                      /* print internal kind */
      fprintf(fout, "  %s parse_kind= %d\n",
                       com, symbol->parse_kind);
    }

                      /* print PC_AT_END= command */
    ptr = symbol->end_pc;
    fprintf(fout, "%s%s ", spaces, key_to_string(PC_AT_END) );
    tprint_st_rwc(fout, ptr);
    fprintf(fout, "\n");
    if (TDEBUG) {
      fprintf(fout, "  %s value stored internally as %d\n",
                    com, get_pc_enum(ptr) );
    }

                    /* do next mode for this entry */
    if (pmode > 0) {                    /* not the first */
      fprintf(fout, "%s\n", key_to_string(END_MODE));
    }
    if ((symbol = get_next_mode(symbol)) != NULL) {  /* there is another mode */
      pmode++;
      fprintf(fout, "%s %s\n", 
                    key_to_string(IN_MODE), get_mode_str(get_mode(symbol)));
      goto domode;    /* GOTO print the mode's actions */
    }

                    /* print END_TYPE at end of entry */
    fprintf(fout, "%s\n", key_to_string(END_TYPE));
  } 
}                                         /* end PRINT_TABLE */


/* CHECK_TABLE = TRUE if missing entries and fixes them up */
int check_table()     
{
  int res = FALSE;

  pos_bdoc = lookup_entry(dont_care_str, BEGIN_DOCUMENT);
  if (pos_bdoc < 0) {
    twarn_noline("BEGIN_DOCUMENT required but not found. Defaulted to null tags.");
    res = TRUE;
    create_def_reqentry(BEGIN_DOCUMENT);
  }
  pos_edoc = lookup_entry(dont_care_str, END_DOCUMENT);
  if (pos_edoc < 0) {
    twarn_noline("END_DOCUMENT required but not found. Defaulted to null tags.");
    res = TRUE;
    create_def_reqentry(END_DOCUMENT);
  }
  pos_bvbm = lookup_entry(dont_care_str, BEGIN_VERBATIM);
  if (pos_bvbm < 0) {
    twarn_noline("BEGIN_VERBATIM required but not found. Defaulted to null tags.");
    res = TRUE;
    create_def_reqentry(BEGIN_VERBATIM);
  }
  pos_evbm = lookup_entry(dont_care_str, END_VERBATIM);
  if (pos_evbm < 0) {
    twarn_noline("END_VERBATIM required but not found. Defaulted to null tags.");
    res = TRUE;
    create_def_reqentry(END_VERBATIM);
  }
  pos_bv = lookup_entry(dont_care_str, BEGIN_VERB );
  if (pos_bv < 0) {
    twarn_noline("BEGIN_VERB required but not found. Defaulted to null tags.");
    res = TRUE;
    create_def_reqentry(BEGIN_VERB);
  }
  pos_ev = lookup_entry(dont_care_str, END_VERB);
  if (pos_ev < 0) {
    twarn_noline("END_VERB required but not found. Defaulted to null tags.");
    res = TRUE;
    create_def_reqentry(END_VERB);
  }
  pos_oc = lookup_entry(dont_care_str, OTHER_COMMAND);
  if (pos_oc < 0) {
    twarn_noline("OTHER_COMMAND required but not found. Defaulted to null tags.");
    res = TRUE;
    create_def_reqentry(OTHER_COMMAND);
  }
  pos_ob = lookup_entry(dont_care_str, OTHER_BEGIN );
  if (pos_ob < 0) {
    twarn_noline("OTHER_BEGIN required but not found. Defaulted to null tags.");
    res = TRUE;
    create_def_reqentry(OTHER_BEGIN);
  }
  pos_oe = lookup_entry(dont_care_str, OTHER_END );
  if (pos_oe < 0) {
    twarn_noline("OTHER_END required but not found. Defaulted to null tags.");
    res = TRUE;
    create_def_reqentry(OTHER_END);
  }
  pos_lbrace = lookup_entry(dont_care_str, LBRACE);
  if (pos_lbrace < 0) {
    twarn_noline("LBRACE required but not found. Defaulted to null tags.");
    res = TRUE;
    create_def_reqentry(LBRACE);
  }
  pos_rbrace = lookup_entry(dont_care_str, RBRACE);
  if (pos_rbrace < 0) {
    twarn_noline("RBRACE required but not found. Defaulted to null tags.");
    res = TRUE;
    create_def_reqentry(RBRACE);
  }
  pos_para = lookup_entry(dont_care_str, PARAGRAPH);
  if (pos_para < 0) {
    twarn_noline("PARAGRAPH required but not found. Defaulted to null tags.");
    res = TRUE;
    create_def_reqentry(PARAGRAPH);
  }
  pos_bdol = lookup_entry(dont_care_str, BEGIN_DOLLAR);
  if (pos_bdol < 0) {
    twarn_noline("BEGIN_DOLLAR required but not found. Defaulted to null tags.");
    res = TRUE;
    create_def_reqentry(BEGIN_DOLLAR);
  }
  pos_edol = lookup_entry(dont_care_str, END_DOLLAR);
  if (pos_bdol < 0) {
    twarn_noline("END_DOLLAR required but not found. Defaulted to null tags.");
    res = TRUE;
    create_def_reqentry(END_DOLLAR);
  }
  pos_bss = lookup_entry(dont_care_str, SLASH_SPACE);
  if (pos_bss < 0) {
    twarn_noline("SLASH_SPACE required but not found. Defaulted to a space tag.");
    res = TRUE;
    create_def_reqentry(SLASH_SPACE);
    symbol_table[num_table_entries - 1]->command_t = create_print_tag(" ");
  }

  return(res);
}                              /* end CHECK_TABLE */



/* CTYPE_TO_INT returns enum value corrsponding to a command type string */
int ctype_to_int(s)
STRING s;
{
  int pos;
  pos = lookup_string(s, ctnames, NUM_CTIDS);
  if (pos == -1) {
    table_error("TYPE entry is unrecognised");
    return(UNKNOWN_CTYPE);
  }
  return(ctenums[pos]);
}                                    /* end CTYPE_TO_INT */

/* CTYPE_TO_STRING returns enum string (the inverse of CTYPE_TO_INT) */
char *ctype_to_string(ienum)        
int ienum;
{

  switch (ienum) {
    case UNKNOWN_CTYPE: {
      return("UNKNOWN_CTYPE");
    }
    case TEX_CHAR: {
      return("TEX_CHAR");
    }
    case CHAR_COMMAND: {
      return("CHAR_COMMAND");
    }
    case COMMAND: {
      return("COMMAND");
    }
    case BEGIN_ENV: {
      return("BEGIN_ENV");
    }
    case END_ENV: {
      return("END_ENV");
    }
    case BEGIN_LIST_ENV: {
      return("BEGIN_LIST_ENV");
    }
    case END_LIST_ENV: {
      return("END_LIST_ENV");
    }
    case BEGIN_VERBATIM: {
      return("BEGIN_VERBATIM");
    }
    case END_VERBATIM: {
      return("END_VERBATIM");
    }
    case BEGIN_VERB: {
      return("BEGIN_VERB");
    }
    case END_VERB: {
      return("END_VERB");
    }
    case SECTIONING: {
      return("SECTIONING");
    }
    case SPECIAL: {
      return("SPECIAL");
    }
    case SPECIAL_COMMAND: {
      return("SPECIAL_COMMAND");
    }
    case SPECIAL_BEGIN_ENV: {
      return("SPECIAL_BEGIN_ENV");
    }
    case SPECIAL_END_ENV: {
      return("SPECIAL_END_ENV");
    }
    case SPECIAL_BEGIN_LIST: {
      return("SPECIAL_BEGIN_LIST");
    }
    case SPECIAL_END_LIST: {
      return("SPECIAL_END_LIST");
    }
    case SPECIAL_SECTIONING: {
      return("SPECIAL_SECTIONING");
    }
    case OTHER_COMMAND: {
      return("OTHER_COMMAND");
    }
    case OTHER_BEGIN: {
      return("OTHER_BEGIN");
    }
    case OTHER_END: {
      return("OTHER_END");
    }
    case LBRACE: {
      return("LBRACE");
    }
    case RBRACE: {
      return("RBRACE");
    }
    case BEGIN_PICTURE_CC: {
      return("BEGIN_PICTURE_CC");
    }
    case END_PICTURE: {
      return("END_PICTURE");
    }
    case PICTURE_CCPP: {
      return("PICTURE_CCPP");
    }
    case PICTURE_CO: {
      return("PICTURE_CO");
    }
    case PICTURE_COP: {
      return("PICTURE_COP");
    }
    case PICTURE_CP: {
      return("PICTURE_CP");
    }
    case PICTURE_OCC: {
      return("PICTURE_OCC");
    }
    case PICTURE_OCCC: {
      return("PICTURE_OCCC");
    }
    case PICTURE_OCO: {
      return("PICTURE_OCO");
    }
    case PICTURE_PCOP: {
      return("PICTURE_PCOP");
    }
    case BEGIN_VENV: {
      return("BEGIN_VENV");
    }
    case END_VENV: {
      return("END_VENV");
    }
    case VCOMMAND: {
      return("VCOMMAND");
    }
    case BEGIN_DOCUMENT: {
      return("BEGIN_DOCUMENT");
    }
    case END_DOCUMENT: {
      return("END_DOCUMENT");
    }
    case PARAGRAPH: {
      return("PARAGRAPH");
    }
    case BEGIN_DOLLAR: {
      return("BEGIN_DOLLAR");
    }
    case END_DOLLAR: {
      return("END_DOLLAR");
    }
    case SLASH_SPACE: {
      return("SLASH_SPACE");
    }
    case COMMAND_OOP: {
      return("COMMAND_OOP");
    }
    case COMMAND_OOOPP: {
      return("COMMAND_OOOPP");
    }
    case COMMAND_OPO: {
      return("COMMAND_OPO");
    }
    case COMMAND_POOOP: {
      return("COMMAND_POOOP");
    }
    case COMMAND_POOP: {
      return("COMMAND_POOP");
    }
    case COMMAND_POOPP: {
      return("COMMAND_POOPP");
    }
    default: {
      return("UNKNOWN_CTYPE");
    }
  }
}                                   /* end CTYPE_TO_STRING */


/* POS_TO_INT converts an opt_pos_enum enum string to an int */
int pos_to_int(s)      
char s[];
{

  if (strcmp(s, "NO_OPT_PARAM") == 0) {
      return(NO_OPT_PARAM);
  }
  else if (strcmp(s, "FIRST") == 0) {
    return(FIRST);
  }
  else if (strcmp(s, "LAST") == 0) {
    return(LAST);
  }
  else {
    table_error("OPT_PARAM entry is unrecognised");
    return(NO_OPT_PARAM);
  }
}                                    /* end POS_TO_INT */


/* POS_TO_STRING returns enum string (the inverse of POS_TO_INT) */
char *pos_to_string(ienum)        
int ienum;
{

  switch (ienum) {
    case NO_OPT_PARAM: {
      return("NO_OPT_PARAM");
    }
    case FIRST: {
      return("FIRST");
    }
    case LAST: {
      return("LAST");
    }
    default: {
      return("NO_OPT_PARAM");
    }
  }
}                                   /* end POS_TO_STRING */


/* LEVEL_TO_INT converts an sect_level_enum enum string to an int */
int level_to_int(s)      
char s[];
{

  if (strcmp(s, "UNKNOWN_LEVEL") == 0) {
      return(UNKNOWN_LEVEL);
  }
  else if (strcmp(s, "PARTM2") == 0) {
    return(PARTM2);
  }
  else if (strcmp(s, "PARTM1") == 0) {
    return(PARTM1);
  }
  else if (strcmp(s, "PART") == 0) {
    return(PART);
  }
  else if (strcmp(s, "CHAPTER") == 0) {
    return(CHAPTER);
  }
  else if (strcmp(s, "SECT") == 0) {
    return(SECT);
  }
  else if (strcmp(s, "SUBSECT") == 0) {
    return(SUBSECT);
  }
  else if (strcmp(s, "SUBSUBSECT") == 0) {
    return(SUBSUBSECT);
  }
  else if (strcmp(s, "PARA") == 0) {
    return(PARA);
  }
  else if (strcmp(s, "SUBPARA") == 0) {
    return(SUBPARA);
  }
  else if (strcmp(s, "SUBPARAP1") == 0) {
    return(SUBPARAP1);
  }
  else if (strcmp(s, "SUBPARAP2") == 0) {
    return(SUBPARAP2);
  }
  else {
    table_error("SECTIONING_LEVEL entry is unrecognised");
    return(UNKNOWN_LEVEL);
  }
}                                    /* end LEVEL_TO_INT */


/* LEVEL_TO_STRING returns enum string (the inverse of LEVEL_TO_INT) */
char *level_to_string(ienum)        
int ienum;
{

  switch (ienum) {
    case UNKNOWN_LEVEL: {
      return("UNKNOWN_LEVEL");
    }
    case PARTM2: {
      return("PARTM2");
    }
    case PARTM1: {
      return("PARTM1");
    }
    case PART: {
      return("PART");
    }
    case CHAPTER: {
      return("CHAPTER");
    }
    case SECT: {
      return("SECT");
    }
    case SUBSECT: {
      return("SUBSECT");
    }
    case SUBSUBSECT: {
      return("SUBSUBSECT");
    }
    case PARA: {
      return("PARA");
    }
    case SUBPARA: {
      return("SUBPARA");
    }
    case SUBPARAP1: {
      return("SUBPARAP1");
    }
    case SUBPARAP2: {
      return("SUBPARAP2");
    }
    default: {
      return("UNKNOWN_LEVEL");
    }
  }
}                                   /* end LEVEL_TO_STRING */




/* SORT_TABLE sorts command table. From K&R Ed 2, p 62 */
void sort_table()                   
{                                   

  int gap, i, j;
  struct st_entry  *temp;

  for (gap = num_table_entries/2; gap > 0; gap /= 2) {
    for (i = gap; i < num_table_entries; i++) {
      for (j = (i-gap); 
           j >= 0 && compare_st_entry(symbol_table[j], symbol_table[j+gap]) > 0; 
           j-=gap) {
        temp = symbol_table[j];
        symbol_table[j] = symbol_table[j+gap];
        symbol_table[j+gap] = temp;
      }
    }
  }
}                                   /* end SORT_TABLE */


/* COMPARE_ST_ENTRY compares two st_entry */
int compare_st_entry(e1, e2)       
struct st_entry *e1;
struct st_entry *e2;
{
  int result;

               /* compare on command name */
  result = strcmp(e1->command, e2->command);
  if (result != 0) {
    return(result);
  }
               /* compare on kind */
  return(e1->kind - e2->kind);
}                                  /* end COMPARE_ST_ENTRY */


/* LOOKUP_ENTRY searches for command s in table. From K&R 2 Ed, p 58 */
int lookup_entry(s,k)             
char s[];                         
int k;
{
  int low, mid, high, result;

  low = 0;
  high = num_table_entries - 1;
  while (low <= high) {
    mid = (low + high)/2;
    result = compare_id_to_entry(s,k,symbol_table[mid]);
    if (result < 0) {
      high = mid - 1;
    }
    else if (result > 0) {
      low = mid + 1;
    }
    else {                      /* found a match */
      return(mid);
    }
  }
  return(-1);                   /* not found */
}                                 /* end LOOKUP_ENTRY */


/* COMPARE_ID_TO_ENTRY similar to COMPARE_ST_ENTRY */
int compare_id_to_entry(s,k,e)    
char s[];
int k;
struct st_entry *e;
{
  int result;

               /* compare on command name */
  result = strcmp(s, e->command);
  if (result != 0) {
    return(result);
  }
  if (k == DONT_CARE) {
    return(result);
  }
  else {       /* compare on kind */
    return(k - e->kind);
  }
}                                  /* end COMPARE_ID_TO_ENTRY */


           /*-----------------GET_ FUNCTIONS-----------------------------*/



/* COMMAND_TYPE returns system-assigned kind of command */
int command_type(pos)                    
PSENTRY pos;
{
  if (pos == NULL) {  /* out of range */
    return(IERR);
  }
  return(pos->parse_kind);
}                                         /* end COMMAND_TYPE */

/* 6/96  changed STRING to PSTRWC and pos to PSENTRY in following */
/* GET_T returns command start tag */
PSTRWC get_t(pos)                         
PSENTRY pos;
{
  if (pos == NULL) {  /* out of range */
    return(NULL);
  }
  return(pos->command_t);
}                                           /* end GET_T */


/* GET_ET returns command end tag */
PSTRWC get_et(pos)                         
PSENTRY pos;
{
  if (pos == NULL) {  /* out of range */
    return(NULL);
  }
  return(pos->command_et);
}                                           /* end GET_ET */


/* GET_TAG_T returns param start tag for n'th req param */
PSTRWC get_tag_t(pos,num)                 
PSENTRY pos;
int num;
{
  if (pos == NULL) {  /* out of range */
    return(NULL);
  }
  if (num < 1 || num > 9) {                 /* out of range */
    yyerror("Tag number out of range");
    return(NULL);
  }
  return(pos->param_t[num-1]);
}                                           /* end GET_TAG_T */


/* GET_TAG_ET returns param end tag for n'th req param */
PSTRWC get_tag_et(pos,num)                
PSENTRY pos;
int num;
{
  if (pos == NULL) {  /* out of range */
    return(NULL);
  }
  if (num < 1 || num > 9) {                 /* out of range */
    yyerror("Tag number out of range");
    return(NULL);
  }
  return(pos->param_et[num-1]);
}                                           /* end GET_TAG_ET */


/* GET_OPTTAG_T returns opt param start tag */
PSTRWC get_opttag_t(pos)
PSENTRY pos;
{
  if (pos == NULL) {  /* out of range */
    return(NULL);
  }
  return(pos->opt_param_t);
}                                           /* end GET_OPTTAG_T */

/* GET_OPTTAG_ET returns param end tag */
PSTRWC get_opttag_et(pos) 
PSENTRY pos;
{
  if (pos == NULL) {  /* out of range */
    return(NULL);
  }
  return(pos->opt_param_et);
}                                           /* end GET_OPTTAG_ET */


/* GET_LEVEL returns sectioning level */
int get_level(pos)                      
PSENTRY pos;
{
  if (pos == NULL) {  /* out of range */
    return(IERR);
  }
  return(pos->sectioning_level);
}                                           /* end GET_LEVEL */


/* GET_ITEM_T returns item start tag */
PSTRWC get_item_t(pos)             
PSENTRY pos;
{
  if (pos == NULL) {  /* out of range */
    return(NULL);
  }
  return(pos->param_t[7]);
}                                           /* end GET_ITEM_T */


/* GET_ITEM_ET returns item end tag */
PSTRWC get_item_et(pos)             
PSENTRY pos;
{
  if (pos == NULL) {  /* out of range */
    return(NULL);
  }
  return(pos->param_et[7]);
}                                           /* end GET_ITEM_ET */


/* GET_ITEMOPT_T returns item optional param start tag */
PSTRWC get_itemopt_t(pos)        
PSENTRY pos;
{
  if (pos == NULL) {  /* out of range */
    return(NULL);
  }
  return(pos->param_t[8]);
}                                           /* end GET_ITEMOPT_T */


/* GET_ITEMOPT_ET returns item optional param end tag */
PSTRWC get_itemopt_et(pos)        
PSENTRY pos;
{
  if (pos == NULL) {  /* out of range */
    return(NULL);
  }
  return(pos->param_et[8]);
}                                           /* end GET_ITEMOPT_ET */


/* GET_USER_TYPE returns user's command type */
int get_user_type(pos)                     
PSENTRY pos;
{
  if (pos == NULL) {  /* out of range */
    return(IERR);
  }
  return(pos->kind);
}                                           /* end GET_USER_TYPE */


/* GET_SPECIAL_TOKEN returns special token */
int get_special_token(pos)                 
PSENTRY pos;
{
  if (pos == NULL) {  /* out of range */
    return(OTHER);
  }
  return(pos->special_token);
}                                          /* end GET_SPECIAL_TOKEN */


/* GET_COM_PRINT returns command print control */
PSTRWC get_com_print(pos)        
PSENTRY pos;
{
  if (pos == NULL) {  /* out of range */
    return(NULL);
  }
  return(pos->printing);
}                                         /* end GET_COM_PRINT */


/* GET_PARAM_PRINT returns param print control */
PSTRWC get_param_print(pos,num) 
PSENTRY pos;
int num;
{
  if (pos == NULL) {  /* out of range */
    return(NULL);
  }
  if (num < 1 || num > 9) {  /* out of range */
    yyerror("Parameter number out of range");
    return(NULL);
  }
  return(pos->print_param[num-1]);
}                                         /* end GET_PARAM_PRINT */


/* GET_OPT_PRINT returns opt param print control */
PSTRWC get_opt_print(pos)    
PSENTRY pos;
{
  if (pos == NULL) {  /* out of range */
    return(NULL);
  }
  return(pos->print_opt[0]);
}                                         /* end GET_PARAM_PRINT */


       /*---------------FILE INCLUSION-------------------*/

/* INITIALISE_SENV initialises path searching */
void initialise_senv()
{
  strcpy(sys_envname,"LTX2XTABLES");                 /* environment variable name */
  strcpy(path_sep," :;");                          /* path seperators */
  dir_cat = '/';                                   /* dir catenation char */
  senv_debug = 0;                                  /* debugging off */
}                                          /* end INITIALISE_SENV */


/* INITIALISE_CT_FILES */
void initialise_ct_files(tabnam)                       
char tabnam[];
{
  ct_file_num = 0;
  ct_fp_stack[ct_file_num] = filtabin;
  ct_fn_stack[ct_file_num] = strsave(tabnam);
  ct_ln_stack[ct_file_num] = 0;
  fprintf(stdout, "\nReading command table file %s\n", tabnam);
  fprintf(filerr, "\nReading command table file %s\n", tabnam);
}                                         /* end INITIALISE_CT_FILES */


/* SET_CT_FILE opens new ct file */
int set_ct_file(str)                     
char str[];
{
  FILE *file;
  char answer[MAX_TABLE_LINE];
  char path[257];
  int i;

  if (ct_file_num >= MAX_CT_STACK - 1) {         /* overflow */
    table_error("FATAL ERROR: Attempting to open too many files");
    exit(1);
  }

  strcpy(answer, str);
  for (i = 1; i <= 4; i++) {                    /* 4 attempts to open a file */
    if (searchenv(answer, sys_envname, path, path_sep, dir_cat, senv_debug)) {
      file = fopen(path, "r");
      if (file) {                          /* file is opened */
        break;
      }
    }
    if (i == 3) {                              /* last attempt failed */
      fprintf(stdout, "\nLast attempt. Can't open file %s. I'm giving up.\n", answer);
      exit(0);
    }
    fprintf(stdout, "\nCan't open file %s\n", answer);
    fprintf(filerr, "\nCan't open file %s\n", answer);
    fprintf(stdout, "Enter new file name, or I to ignore, or Q to quit\n: ");
    fflush(stdout);
    scanf("%s", answer);
    if (strcmp("Q", answer) == 0 || strcmp("q", answer) == 0) {
      exit(0);
    }
    else if (strcmp("I", answer) == 0 || strcmp("i", answer) == 0) {
      return(FALSE);
    }
  }    
                 /* file has been opened sucessfully */
  ct_file_num++;
  used_ct_file_stack = maxof(ct_file_num, used_ct_file_stack);
  ct_fp_stack[ct_file_num] = file;
  ct_fn_stack[ct_file_num] = strsave(path);
  ct_ln_stack[ct_file_num] = 0;
  fprintf(stdout, "\nReading command table file %s\n", path);
  fprintf(filerr, "\nReading command table file %s\n", path);
  return(TRUE);
}                                        /* end SET_CT_FILE */


/* RESET_CT_FILE pops file stack */
int reset_ct_file()                      
{

  if (ct_file_num < 0) {                 /* underflow */
    table_error("Command table stack underflow");
    return(EOF);
  }
  fclose(ct_fp_stack[ct_file_num]);
  fprintf(stdout, "\n    Closing command table file %s\n", ct_fn_stack[ct_file_num]);
  fprintf(filerr, "\n    Closing command table file %s\n", ct_fn_stack[ct_file_num]);
  free(ct_fn_stack[ct_file_num]);
  ct_fp_stack[ct_file_num] = NULL;
  ct_fn_stack[ct_file_num] = NULL;
  ct_ln_stack[ct_file_num] = 0;
  ct_file_num--;
  if (ct_file_num < 0) {                 /* empty stack, end of files */
    return(EOF);
  }
  else {
    return(ct_file_num);
  }
}                                        /* end RESET_CT_FILE */


/* GET_CT_FILEP returns file pointer */
FILE *get_ct_filep()                      
{
  return(ct_fp_stack[ct_file_num]);
}                                        /* end GET_CT_FILEP */


/* GET_CT_FILEN returns file name */
STRING get_ct_filen()                    
{
  return(ct_fn_stack[ct_file_num]);
}                                        /* end GET_CT_FILEN */


/* GET_CT_LINENUM returns line number */
int get_ct_linenum()                     
{
  return(ct_ln_stack[ct_file_num]);
}                                        /* end GET_CT_LINENUM */


/* INCR_CT_LINENUM increments line number */
void incr_ct_linenum()                   
{
  ct_ln_stack[ct_file_num]++;
}                                        /* end INCR_CT_LINENUM */

/* SET_CT_LINENUM sets command table line number */
void set_ct_linenum(linenum)                   
int linenum;                             /* the line number */
{
  ct_ln_stack[ct_file_num] = linenum;
}                                        /* end SET_CT_LINENUM */


/*---------------------------------------------------------------*/
/*             6/96 extras                                       */

/* CREATE_ST_RWC creates new struct */
PSTRWC create_st_rwc()
{
  PSTRWC p;

  if ((p = (PSTRWC ) malloc(sizeof(struct st_rwc))) != NULL) {
    p->next = NULL;  
    p->cmd = -1;
    p->pcenum = UNKNOWN_PRINT;
    p->rwcode.bfnum = 0;
    return(p);
  }
  yyerror("FATAL ERROR: No memory left for CREATE_ST_RWC");
  exit(1);
}                                       /* end CREATE_ST_RWC */

/* INIT_COMMON_PRINT initialises common print structures */
void init_common_print()
{
  PSTRWC p;

  /* default print */
  p = create_st_rwc();
  set_pc_enum(p, DEFAULT_PRINT);
  p_default_print = p;

  /* no print */
  p = create_st_rwc();
  set_pc_enum(p, NO_PRINT);
  p_no_print = p;

  /* print to buffer */
  p = create_st_rwc();
  set_pc_enum(p, TO_SYSBUF);
  p_print_to_sysbuf = p;

  /* print_underflow */
  p = create_st_rwc();
  set_pc_enum(p, PRINT_UNDERFLOW);
  p_print_underflow = p;


  /* unknown print */
  p = create_st_rwc();
  set_pc_enum(p, UNKNOWN_PRINT);
  p_unknown_print = p;


  /* print from buffer */
  p = create_st_rwc();
  set_pc_enum(p, SYSBUF);
  p_print_from_sysbuf = p;

  /* print null string */
  p = create_st_rwc();
  set_pc_enum(p, FIRST_STRING);
  set_pcdata_name(p, "");
  p_print_null = p;

  /* reset print */
  p = create_st_rwc();
  set_pc_enum(p, RESET);
  p_reset_print = p;

  /* no op */
  p = create_st_rwc();
  set_pc_enum(p, NO_OP);
  p_noop_print = p;

}                                       /* end INIT_COMMON_PRINT */

/* CREATE_PRINT_TAG creates a tag print string structure */
PSTRWC create_print_tag(a_str)
  char a_str[];                       /* a string */
{
  PSTRWC p;

  p = create_st_rwc();
  set_pc_enum(p, FIRST_STRING);
  set_pcdata_name(p, a_str);
  return(p);
}                                       /* end CREATE_PRINT_TAG */

/* CREATE_PRINT_SOURCE creates a source print string structure */
PSTRWC create_print_source(a_str)
  char a_str[];                       /* a string */
{
  PSTRWC p;

  p = create_st_rwc();
  set_pc_enum(p, SOURCE_STRING);
  set_pcdata_name(p, a_str);
  set_pc_cmd(p, SOURCE);
  return(p);
}                                       /* end CREATE_PRINT_SOURCE */



/* PARSE_PC parses print control and returns new structure */
PSTRWC parse_pc(a_line)
  char a_line[];
{
  char key_name[MAX_TABLE_LINE];
  int key_enum;
  int num;
  char name[MAX_TABLE_LINE];
  PSTRWC p;
  

  /* get first keyword on line */
  sscanf(a_line, "%s", key_name);
  strtouc(key_name);
  /* convert to enumeration type */
  key_enum = printc_to_int(key_name);
  /* switch to get all the data */
  switch (key_enum) {
    case DEFAULT_PRINT: {  /* got it all */
      return(p_default_print);
    }
    case NO_PRINT: { /* got it all */
      return(p_no_print);
    }
    case TO_SYSBUF: { /* got it all */
      return(p_print_to_sysbuf);
    }
    case PRINT_UNDERFLOW: { /* got it all */
      return(p_print_underflow);
    }
    case UNKNOWN_PRINT: { /* got it all */
      return(p_unknown_print);
    }

    case TO_BUFFER: { /* get buffer number */
      sscanf(a_line, "%s %d", key_name, &num);
      p = create_st_rwc();
      set_pc_enum(p, TO_BUFFER);
      set_pcdata_num(p, chk_bufnum(num));
      used_wubuff[num]++;
      return(p);
    }

    case TO_FILE: { /* get file */
      sscanf(a_line, "%s %s", key_name, name);
      p = create_st_rwc();
      set_pc_enum(p, TO_FILE);
                        /* do file name stuff, adding it to the file array */
      set_pcdata_name(p, chk_ufname(name));
      return(p);
    }

    case SYSBUF: { /* got it all */
      return(p_print_from_sysbuf);
    }

    case BUFFER: { /* get buffer number */
      sscanf(a_line, "%s %d", key_name, &num);
      p = create_st_rwc();
      set_pc_enum(p, BUFFER);
      set_pcdata_num(p, chk_bufnum(num));
      used_rubuff[num]++;
      return(p);
    }

    case FFILE: { /* get file */
      sscanf(a_line, "%s %s", key_name, name);
      p = create_st_rwc();
      set_pc_enum(p, FFILE);
                        /* do file name stuff, adding it to the file array */
      set_pcdata_name(p, chk_ufname(name));
      return(p);
    }

    case FIRST_STRING: { /* get string */
      return(p_unknown_print);
    }

    case RESET: { /* got it all */
      return(p_reset_print);
    }

    case NO_OP: { /* got it all */
      return(p_noop_print);
    }

    default: {
      return(p_unknown_print);
    }
  }
}                                       /* end PARSE_PC */


/* TPRINT_ST_RWC print out the st_rwc structure */
void tprint_st_rwc(fout, prwc)
FILE *fout;
PSTRWC prwc;
{

  STRING str;
  int kind;
  STRING indent = "    ";

  if (prwc == NULL) {          /* nothing to print */
    return;
  }

  kind = get_pc_enum(prwc);
  str = printc_to_string(kind);
  switch (kind) {
    case DEFAULT_PRINT: { /* got it all */
      fprintf(fout, "%s", str);
      return;
    }
    case NO_PRINT: { /* got it all */
      fprintf(fout, "%s", str);
      return;
    }
    case TO_SYSBUF: { /* got it all */
      fprintf(fout, "%s", str);
      return;
    }
    case PRINT_UNDERFLOW: { /* got it all */
      fprintf(fout, "%s", str);
      return;
    }
    case UNKNOWN_PRINT: { /* got it all */
      fprintf(fout, "%s", str);
      return;
    }
    case TO_BUFFER: { /* write buffer number */
      fprintf(fout, "%s", str);
      fprintf(fout, " %d", get_pcdata_num(prwc));
      return;
    }
    case TO_FILE: { /* write file id */
      fprintf(fout, "%s", str);
      fprintf(fout, " %s", get_pcdata_name(prwc));
      return;
    }
    case SYSBUF: { /* got it all */
      fprintf(fout, "%s", str);
      return;
    }
    case BUFFER: { /* write buffer number */
      fprintf(fout, "%s", str);
      fprintf(fout, " %d", get_pcdata_num(prwc));
      return;
    }
    case FFILE: { /* write file id */
      fprintf(fout, "%s", str);
      fprintf(fout, " %s", get_pcdata_name(prwc));
      return;
    }
    case FIRST_STRING: { /* write the string only */
      tprint_str(fout, get_pcdata_name(prwc));
      return;
    }
    case SOURCE_STRING: {  /* write the string only */
      tprint_str(fout, get_pcdata_name(prwc));
      return;
    }
    case RESET: { /* got it all */
      fprintf(fout, "%s", str);
      return;
    }
    default: { /* should not be here */
    }
  }
}                                                   /* end TPRINT_ST_RWC */


/* TPRINT_TAG prints tag data from table */
void tprint_tag(fout, prwc)
FILE *fout;
PSTRWC prwc;
{

  PSTRWC ptr;
  int kmd;
  STRING indent = "    ";

  if (prwc == NULL) {    /* nothing, so just do a new line */
    fprintf(fout, "\n");
    return;
  }

  ptr = prwc;
  while (ptr != NULL) {
    kmd = get_pc_cmd(ptr);
    switch (kmd) {                  /* some are special, need to add command */
      case SOURCE : {
        fprintf(fout, "%s %s ", indent, key_to_string(SOURCE));
        tprint_st_rwc(fout, ptr);
        break;
      }
      case STRINGG : {
        fprintf(fout, "%s %s ", indent, key_to_string(STRINGG));
        tprint_st_rwc(fout, ptr);
        break;
      }
      case RESET_SYSBUF : {
        fprintf(fout, "%s %s", indent, key_to_string(RESET_SYSBUF));
        break;
      }
      case RESET_BUFFER : {
        fprintf(fout, "%s %s ", indent, key_to_string(RESET_BUFFER));
        fprintf(fout, "%d", get_pcdata_num(ptr));
        break;
      }
      case RESET_FILE : {
        fprintf(fout, "%s %s ", indent, key_to_string(RESET_FILE));
        fprintf(fout, "%s", get_pcdata_name(ptr));
        break;
      }
      case SET_MODE : {
        fprintf(fout, "%s %s ", indent, key_to_string(SET_MODE));
        fprintf(fout, "%s", get_mode_str(get_pcdata_num(ptr)));
        break;
      }
      case RESET_MODE : {
        fprintf(fout, "%s %s ", indent, key_to_string(RESET_MODE));
        break;
      }
      case SWITCH_BACK : {
        fprintf(fout, "%s%s ", indent, key_to_string(SWITCH_BACK));
        break;
      }
      case SWITCH_TO_BUFFER : {
        fprintf(fout, "%s%s ", indent, key_to_string(SWITCH_TO_BUFFER));
        fprintf(fout, "%d", get_pcdata_num(ptr));
        break;
      }
      case SWITCH_TO_FILE : {
        fprintf(fout, "%s%s ", indent, key_to_string(SWITCH_TO_FILE));
        fprintf(fout, "%s", get_pcdata_name(ptr));
        break;
      }
      case SWITCH_TO_SYSBUF : {
        fprintf(fout, "%s%s ", indent, key_to_string(SWITCH_TO_SYSBUF));
        break;
      }

      default : {
        tprint_st_rwc(fout, ptr);
        break;
      }
    }  /* end switch */
    fprintf(fout, "\n");
    ptr = ptr->next;
  }  /* end loop */
}                                                  /* end TPRINT_TAG */


/* GET_PC_ENUM returns the control code from printing data */
int get_pc_enum(ptr)
PSTRWC ptr;
{

  if (ptr == NULL) {
    return(UNKNOWN_PRINT);
  }
  return(ptr->pcenum);

}                                                 /* end GET_PC_ENUM */

/* SET_PC_ENUM sets the control code for printing data */
void set_pc_enum(ptr, cenum)
PSTRWC ptr;
int cenum;
{
  if (ptr != NULL) {
    ptr->pcenum = cenum;
  }
  return;
}                                                 /* end SET_PC_ENUM */


/* GET_PCDATA_NUM returns the number code from printing data */
int get_pcdata_num(ptr)
PSTRWC ptr;
{

  if (ptr == NULL) {
    return(0);
  }
  return(ptr->rwcode.bfnum);

}                                                 /* end GET_PCDATA_NUM */

/* SET_PCDATA_NUM sets the number code for printing data */
void set_pcdata_num(ptr, num)
PSTRWC ptr;
int num;
{
  if (ptr != NULL) {
    ptr->rwcode.bfnum = num;
  }
  return;
}                                                 /* end SET_PCDATA_NUM */

/* GET_PCDATA_NAME returns the string code from printing data */
STRING get_pcdata_name(ptr)
PSTRWC ptr;
{

  if (ptr == NULL) {
    return(NULL);
  }
  return(ptr->rwcode.defstr);

}                                                 /* end GET_PCDATA_NAME */

/* SET_PCDATA_NAME sets the number code for printing data */
void set_pcdata_name(ptr, str)
PSTRWC ptr;
STRING str;
{
  if (ptr != NULL) {
    ptr->rwcode.defstr = str;
  }
  return;
}                                                 /* end SET_PCDATA_NAME */

/* GET_PC_CMD returns command name from print control */
int get_pc_cmd(ptr)
PSTRWC ptr;
{
  if (ptr == NULL) {
    return(IERR);
  }
  return(ptr->cmd);
}                                                 /* end GET_PC_CMD */


/* SET_PC_CMD sets command name in print control */
void set_pc_cmd(ptr, kmd)
PSTRWC ptr;
int kmd;
{
  if (ptr != NULL) {
    ptr->cmd = kmd;
  }
  return;
}                                                 /* end SET_PC_CMD */

    /* next two added for code interp */
/* SET_PC_CODE sets print control for compiled code */
void set_pc_code(ptr, code_seg)
PSTRWC ptr;
ICT *code_seg;
{

  if (ptr == NULL) return;
  set_pc_cmd(ptr, CODE);
  ptr->rwcode.bytecode = code_seg;
  return;
}                                                 /* end SET_PC_CODE */

/* GET_PC_CODE gets compiled code from print control */
ICT *get_pc_code(ptr)
PSTRWC ptr;
{

  if (ptr == NULL) return(NULL);
  return(ptr->rwcode.bytecode);
}                                                 /* end GET_PC_CODE */


/* GET_NEXT_WRITE returns the next output instruction */
PSTRWC get_next_write(ptr)
PSTRWC ptr;
{

    if (ptr == NULL) {
      return(NULL);
    }
    return(ptr->next);
}                                                  /* end GET_NEXT_WRITE */


/* APPEND_TO_WRITE adds the next output instruction */
PSTRWC append_to_write(list, new)
PSTRWC list;
PSTRWC new;
{

   if (list != NULL) {
     list->next = new;
   }
   return(new);

}                                                  /* end APPEND_TO_WRITE */


/* TAG_PRINT passes off tag strings to myprint for printing */
void tag_print(ptr)
PSTRWC ptr;
{

  char temp[MAX_UBUFF_LEN];
  STRING fname;
  int num;
  FILE *fin;
  int opt;
  int kmd;

  if (ptr == NULL) {          /* nothing there */
    return;
  }
  while (ptr != NULL) {
    kmd = get_pc_cmd(ptr);               /* do special command types first */
    switch (kmd) {
      case RESET_SYSBUF : {              /* reset system buffer */
        initialise_sysbuf();
        break;
      }
      case RESET_BUFFER : {               /* reset user buffer */
        num = get_pcdata_num(ptr);
        init_ubuff(num);
        break;
      }
      case RESET_FILE : {               /* reset file */
        fname = get_pcdata_name(ptr);
        init_ufile(fname);
        break;
      }
      case SET_MODE : {                 /* set the mode */
        num = get_pcdata_num(ptr);
        push_mode(num);
        break;
      }
      case RESET_MODE : {               /* reset the mode */
        reset_mode();
        break;
      }
      /*   10/96 extensions */
      case SWITCH_BACK : {
        reset_print();                  /* reset the print mode */
        break;
      }
      case SWITCH_TO_BUFFER : {
        set_print(ptr);
        break;
      }
      case SWITCH_TO_FILE : {
        set_print(ptr);
        break;
      }
      case SWITCH_TO_SYSBUF : {
        set_print(ptr);
        break;
      }
      case CODE : {                   /* added for code interp */
        exec_statements(get_pc_code(ptr));   /* execute code */
        break;
      }
      default : {                     /* now "ordinary " commands */
        opt = get_pc_enum(ptr);
        switch (opt) {
          case FIRST_STRING: {               /* get the specified string */
            myprint(get_pcdata_name(ptr));
            break;
          }
          case STRINGG: {
            myprint(get_pcdata_name(ptr));
            break;
          }
          case SOURCE_STRING : {
            myprint(get_pcdata_name(ptr));
            break;
          }
          case SYSBUF: {              /* get the system buffer */
            myprint(buffer);
            break;
          }
          case BUFFER: {              /* get users buffer */
            num = get_pcdata_num(ptr);
            written_from_buff[num] = TRUE;
            myprint(user_buffs[num]);
            break;
          }
          case FFILE: {                /* get file */
            fname = get_pcdata_name(ptr);
            fin = ufopenr(fname);
            if (fin == NULL) {
              warning_3s("In TAG_PRINT could not open file ", fname, " for reading.");
            }
            else {
              temp[0] = SNUL;
              while (fgets(temp, MAX_UBUFF_LEN - 2, fin) != NULL) {
                myprint(temp);
                temp[0] = SNUL;
              } /* end loop */
              ufclose(fname);                     /* close file */
            }
            break;
          }
          default: {                          /* should not be here */
            sprintf(errstr, 
               "internal error: Illegal print option (%d %s) in TAG_PRINT",
                opt, printc_to_string(opt));
            yyerror(errstr);
            break;
          }
        }   /* end (opt) switch */
        break;
      } /* end kmd default */
    } /* end (kmd) switch  */
    ptr = get_next_write(ptr);
  } /* end while */

}                                                 /* end TAG_PRINT */


/* GET_FPOS given a name, returns position in file array */
int get_fpos(str)
STRING str;
{
  int i;

  for (i = 0; i < used_files; i++) {  /* search along array */
    if (ufn[i] == str) {                /* got it */
      return(i);
    }
  }
             /* should not be here, file not in array */
  sprintf(errstr, "Unknown user file %s (list follows in error file)", str);
  yyerror(errstr);
  ufn[MAX_USER_FILES - 1] = str;
  for (i = 0; i < used_files; i++) {
    sprintf(errstr, "(%d) File %s\n", i, ufn[i]);
    print_to_err(errstr);
  }
  return(MAX_USER_FILES - 1);
}                                                 /* end GET_FPOS */


/* UFOPENR  function to open user file "name" for reading */
FILE *ufopenr(name)
STRING name;
{
  FILE *fil;
  int i;

  i = get_fpos(name);
      /*  10/96  if already open then close it */
  if (num_filep[i] > 0) {
    ufclose(name);
  }
  sprintf(errstr, "Opening file %s number %d for reading\n", name, i);
  print_to_err(errstr);
  fil = fopen(name, "r");
  if (fil == NULL) {               /* can't open the file */
    sprintf(errstr, "Cannot open file %s for reading", name);
    yyerror(errstr);
    ufp[i] = NULL;
  }
  else {
    ufp[i] = fil;             /* set it in the file array */
    num_filep[i] = 0;         /* 10/96 */
  }
  return(fil);
}                                                 /* end UFOPENR */


/* UFOPENW  function to open user file "name" for writing */
FILE *ufopenw(name)
STRING name;
{
  FILE *fil;
  int i;

  i = get_fpos(name);
  sprintf(errstr, "Opening file %s number %d for writing\n", name, i);
  print_to_err(errstr);
  fil = fopen(name, "w");
  if (fil == NULL) {               /* can't open the file */
    sprintf(errstr, "Cannot open file %s for writing", name);
    yyerror(errstr);
    ufp[i] = NULL;
  }
  else {
    ufp[i] = fil;             /* set it in the file array */
  }
  return(fil);
}                                                 /* end UFOPENW */


/* UFCLOSE function to close user file "name" */
int ufclose(name)
STRING name;
{
  int i;
  FILE *fil;

  i = get_fpos(name);
  sprintf(errstr, "    Closing file %s numbered %d\n", name, i);
  print_to_err(errstr);
  fil = ufp[i];
  num_filep[i] = 0;         /* 10/96  set to closed */  
  
  i = fclose(fil);
  if (i == EOF) {                       /* error in closing file */
    sprintf(errstr, "Cannot close file %s", name);
    yyerror(errstr);
  }
  return(i);
}                                               /* end UFCLOSE */
  

/* WRITE_STATS writes statistics to error file */
void write_stats(s)
STRING s;
{

  int num;
  int i;

  print_to_err("\n         STATISTICS");
  print_to_err(s);
  print_to_err("\n\n");
  
  num = MAX_TABLE_ENTRIES;
  used_table_entries = num_table_entries;
  sprintf(errstr, "Used %d table entries out of %d (MAX_TABLE_ENTRIES)\n",
                   used_table_entries, num);
  print_to_err(errstr);

  num = MAX_TABLE_LINE;
  sprintf(errstr, "Max table line %d characters out of %d (MAX_TABLE_LINE)\n",
                   used_table_line, num);
  print_to_err(errstr);

  num = MAX_USER_BUFFS;
  sprintf(errstr, "Max number of user buffers %d (MAX_USER_BUFFS)\n",
                   num);
  print_to_err(errstr);
  print_to_err("    Written to buffers: ");
  for (i = 0; i < MAX_USER_BUFFS; i++) {
    if (used_wubuff[i] >= 0) {
      sprintf(errstr,"%d ",i);
      print_to_err(errstr);
    }
  }
  print_to_err("\n    Read from buffers:  ");
  for (i = 0; i < MAX_USER_BUFFS; i++) {
    if (used_rubuff[i] > 0) {
      sprintf(errstr,"%d ",i);
      print_to_err(errstr);
    }
  }
  print_to_err("\n");

  num = MAX_UBUFF_LEN;
  sprintf(errstr, "Max user buffer %d characters out of %d (MAX_UBUFF_LEN)\n",
                   used_ubuff_chars, num);
  print_to_err(errstr);

  num = MAX_BUFFER;
  sprintf(errstr, "Used system buffer %d characters out of %d (MAX_BUFFER)\n",
                   used_sbuff_chars, num);
  print_to_err(errstr);

  num = MAX_USER_FILES;
  sprintf(errstr, "Used %d user files out of %d (MAX_USER_FILES)\n",
                   used_files, num);
  print_to_err(errstr);

  num = EVERY_N_LINES;
  sprintf(errstr, "Progress reported every %d (EVERY_N_LINES) lines\n",
                   num);
  print_to_err(errstr);

  num = CLAUSE_STACK_SIZE;
  sprintf(errstr, "Max clause nesting depth %d out of %d (CLAUSE_STACK_SIZE)\n",
                   used_clause_stack, num);
  print_to_err(errstr);

  num = DEF_LIST_STACK_SIZE;
  sprintf(errstr, "Max list nesting depth %d out of %d (DEF_LIST_STACK_SIZE)\n",
                   used_list_stack, num);
  print_to_err(errstr);

  num = MAX_PRINT_STACK;
  sprintf(errstr, "Max print stack depth %d out of %d (MAX_PRINT_STACK)\n",
                   used_print_stack, num);
  print_to_err(errstr);

  num = MAX_CT_STACK;
  sprintf(errstr, "Max command table stack depth %d out of %d (MAX_CT_STACK)\n",
                   used_ct_file_stack, num);
  print_to_err(errstr);

                  /* interpreter statistics */
  sprintf(errstr, "\nNumber of interpreter syntax errors:    %d\n", isynt_error_count);
  print_to_err(errstr);

  sprintf(errstr, "Number of interpreter syntax warnings:  %d\n", isynt_warn_count);
  print_to_err(errstr);

  sprintf(errstr, "Number of interpreter runtime errors:   %d\n", irun_error_count);
  print_to_err(errstr);

  sprintf(errstr, "Number of interpreter runtime warnings: %d\n", irun_warn_count);
  print_to_err(errstr);

  return;
}                                               /* end WRITE_STATS */


/* MAXOF returns the maximum of two integers */
int maxof(n,m)
int n;
int m;
{
  if (n >= m) {
    return(n);
  }
  return(m);
}                                               /* end MAXOF */


/* GET_START_PC returns start pc struct */
PSTRWC get_start_pc(pos)
PSENTRY pos;
{

  if (pos == NULL) { /* out of range */
    return(NULL);
  }
  return(pos->start_pc);
}                                                  /* end GET_START_PC */


/* GET_END_PC returns end  pc  struct */
PSTRWC get_end_pc(pos)
PSENTRY pos;
{

  if (pos == NULL) { /* out of range */
    return(NULL);
  }
  return(pos->end_pc);
}                                                  /* end GET_START_PC */


/* TPRINT_STR prints string from command table */
void tprint_str(fout, str)
FILE *fout;
char str[];
{
  int i;
  char c, k;

  if (isempty(str)) {          /* empty string */
    return;
  }

  fputc('"', fout);                    /* write starting quote */
  for (i = 0; (c = str[i]) != SNUL; i++) {   /* loop over all characters */
    if (iscntrl(c)) {                        /* treat controls specially */
      fputc(escape_char, fout);              /* write table escape char */
      switch (c) {
        case '\n' : {
          k = newline_char;
          break;
        }
        case '\t' : {
          k = horizontal_tab_char;
          break;
        }
        case '\v' : {
          k = vertical_tab_char;
          break;
        }
        case '\b' : {
          k = backspace_char;
          break;
        }
        case '\r' : {
          k = carriage_return_char;
          break;
        }
        case '\f' : {
          k = formfeed_char;
          break;
        }
        case '\a' : {
          k = audible_alert_char;
          break;
        }
        default : {    /* should not be here */
          k = escape_char;
          break;
        }
      }                 /* end switch */
      fputc(k, fout);
    }
    else {                             /* normal character for printing */
      fputc(c, fout);                  /* DO HEX LATER */
    }
  }                                    /* all charcters processed */
  fputc('"', fout);                    /* add closing quote */
  return;
}                                                 /* end TPRINT_STR */



/* LOOKUP_STRING returns position of string in an array of strings */
int lookup_string(str, array, numstr)
STRING str;                        /* string to search for */
STRING array[];                    /* array of strings */
int numstr;                        /* number of strings in array */
{
  int low, mid, high, result;

  low = 0;
  high = numstr - 1;
  while (low <= high) {
    mid = (low + high)/2;
    result = strcmp(str, array[mid]);
    if (result < 0) {                   /* not in top half */
      high = mid - 1;
    }
    else if (result > 0) {              /* not in bottom half */
      low = mid + 1;
    }
    else {                               /* found it */
     return(mid);
    }
  }
  return(-1);                            /* str not in array */
}                                               /* end LOOKUP_STRING */

/* INIT_PRINT_CONTROL initialises print control stuff */
void init_print_control()
{

  int n;

  initialise_pc();                         /* set default printing  */

  /* initialise buffers */
  for (n = 0; n < MAX_USER_BUFFS; n++) {
    num_ubuff[n] = 0;
    used_wubuff[n] = -1;
    used_rubuff[n] = 0;
    written_from_buff[n] = FALSE;
    initialise_string(user_buffs[n]);
  }
  /* initialise files */
  for (n = 0; n < MAX_USER_FILES; n++) {
    num_filep[n] = 0;
    ufp[n] = NULL;
    ufn[n] = NULL;
  }
  /* initialise modes */
  for (n = 0; n < MAX_MODES; n++) {
    mode_names[n] = NULL;
  }
  num_modes = 0;    /* no modes defined */
  top_mode = 0;     /* empty mode stack */
}                                               /* end INIT_PRINT_CONTROL */

/* PRINTC_TO_INT converts print control string to enum */
int printc_to_int(s)
STRING s;
{
  int pos;

  pos = lookup_string(s, pc_array, max_pcstr);
  if (pos == -1) {                             /* unknown string */
    table_error("PRINT_CONTROL unrecognised");
    return(UNKNOWN_PRINT);
  }
  return(pos);
}                                             /* end PRINTC_TO_INT */


/* PRINTC_TO_STRING converts print control enum to a string */
STRING printc_to_string(pos)
int pos;
{
  if (pos >= 0 && pos < max_pcstr) {
    return(pc_array[pos]);
  }
  table_error("PRINT_CONTROL out of range");
  return(unk);
}                                                     /* end PRINTC_TO_STRING */



/* KEY_TO_INT converts a keyword string to an integer */
int key_to_int(s)
STRING s;
{
  int pos;

  pos = lookup_string(s, key_array, max_keystr);
  if (pos == -1) {                             /* unknown string */
    table_error("KEYWORD unrecognised");
  }
  return(pos);
}                                                 /* end KEY_TO_INT */


/* KEY_TO_STRING converts a keyword integer to a string */
STRING key_to_string(pos)
int pos;
{
  if (pos >= 0 && pos < max_keystr) {
    return(key_array[pos]);
  }
  table_error("KEYWORD out of range");
  return(unk);
}                                                /* end KEY_TO_STRING */


/* CHK_BUFNUM returns a valid user buffer number */
int chk_bufnum(num)
int num;
{

  if (num < 0 || num > MAX_USER_BUFFS) {
    table_error("User buffer number out of range (I have set it to 0)");
    num = 0;
  }
  return(num);
}                                                /* end CHK_BUFNUM */

/* CHK_UFNAME checks the name of a user's file */
STRING chk_ufname(name)
char name[];
{
  int i;
  STRING str;

  for (i = 0; i < used_files; i++) { /* check existing names */
    if (strcmp(name, ufn[i]) == 0) { /* already in array */
      return(ufn[i]);
    }
  }                                  /* not in array, so add it */
  if (used_files == MAX_USER_FILES) { /* array overflow */
    table_error("Too many file names (I have set this one to NULL)");
    return(NULL);
  }
  str = strsave(name);
  ufn[used_files] = str;
  used_files++;
  return(str);
}                                                /* end CHK_UFNAME */


/* INIT_UBUFF initialises a user buffer */
void init_ubuff(bufnum)
int bufnum;
{
  int n;
  n  = chk_bufnum(bufnum);
  initialise_string(user_buffs[n]);
  return;
}                                                /* end INIT_UBUFF */

/* INIT_UFILE initialises a user file */
void init_ufile(name)
STRING name;
{
  int i;
  FILE *fil;
  
  i = get_fpos(name);
  fil = ufp[i];

  if (fil == NULL) {                 /* unknown or closed file */
    return;
  }
  fclose(fil);                       /* close it */
  ufp[i] = NULL;                     /* and set to NULL in file array */
  num_filep[i] = 0;                  /* 10/96 flag as closed */
  return;
}                                               /* end INIT_UFILE */


/* ADD_MODE checks mode name and adds to list if necessary */
int add_mode(str)
char str[];
{

  int i;

  for (i = 0; i < num_modes; i++) {
    if (strcmp(mode_names[i], str) == 0) {    /* in the list */
      return(i);
    }
  }                                           /* not in the list */
  if (num_modes >= MAX_MODES) {               /* list is full */
    table_error("Too many mode names");
    return(-1);
  }
  mode_names[num_modes] = strsave(str);
  num_modes++;
  return(num_modes - 1);
}                                               /* end ADD_MODE */

/* CHK_MODE_NUM checks if mode number is in range */
int chk_mode_num(num)
int num;
{
  if (num < 0 || num >= num_modes) {  /* out of range */
    sprintf(errstr,
            "internal error: mode number %d out of range (0 to %d)",
             num, (num_modes - 1) );
    yyerror(errstr);
    return(-1);
  }
  return(num);
}                                               /* end CHK_MODE_NUM */


/* GET_MODE_STR returns string corresponding to a mode */
STRING get_mode_str(pos)
int pos;
{
  if (chk_mode_num(pos) < 0) {           /* out of range */
    return(NULL);
  }
  return(mode_names[pos]);
}                                               /* end GET_MODE_STR */


/* ADD_TO_MODELIST adds a new mode into a list of modes */
void add_to_modelist(last, new)
PSENTRY last;
PSENTRY new;
{

  if (last == NULL) {
    return;
  }
  last->next_mode = new;      /* attach new to last */
  new->kind = last->kind;
  new->command = last->command;
  return;
}                                               /* end ADD_TO_MODELIST */


/* SET_MODE_NAME sets the mode name in an entry */
void set_mode_name(str, entry)
char str[];
PSENTRY entry;
{
  int pos;

  pos = add_mode(str);
  entry->mode = pos;
  return;
}                                               /* end SET_MODE_NAME */

/* GET_MODE gets the mode code from an entry */
int get_mode(sym)
PSENTRY sym;
{

  if (sym == NULL) {
    return(IERR);
  }
  return(sym->mode);
}                                               /* end GET_MODE */


/* GET_NEXT_MODE gets the next mode from an entry */
PSENTRY get_next_mode(sym)
PSENTRY sym;
{

  if (sym == NULL) {
    return(NULL);
  }
  return(sym->next_mode);
}                                               /* end GET_NEXT_MODE */


/* ISEMPTY returns TRUE if string is empty */
int isempty(str)
char str[];
{
  if (str == NULL) {
    return(TRUE);
  }
  if (str[0] == SNUL) {
    return(TRUE);
  }
  return(FALSE);
}                                                /* end ISEMPTY */


/* PUSH_MODE pushes mode onto the stack */
void push_mode(id)
int id;
{

  if (top_mode >= MAX_MODE_STACK) {      /* overflow */
    yyerror("Something is wrong: mode stack overflow when pushing");
    return;
  }
  mode_stack[top_mode] = id;
  top_mode++;
  return;
}                                                /* end PUSH_MODE */


/* POP_MODE returns top of mode stack and pops it */
int pop_mode()
{

  if (top_mode <= 0) {          /* underflow */
    yyerror("Something is wrong: mode stack underflow when popping");
    top_mode = 0;
    return(IERR);
  }
  top_mode--;
  return(mode_stack[top_mode]);
}                                                 /* end POP_MODE */


/* GET_CURRENT_MODE returns the current mode */
int get_current_mode()
{

  if (top_mode <= 0) {  /* underflow, but its OK */
    return(IERR);
  }
  return(mode_stack[top_mode - 1]);
}                                                 /* end GET_CURRENT_MODE */

/* RESET_MODE resets the current mode */
int reset_mode()
{
  return(pop_mode());
}                                                 /* end RESET_MODE */


/* GET_MODE_SYM returns the sym entry for the current mode */
PSENTRY get_mode_sym(pos)
int pos;                           /* position in command table */
{

  PSENTRY start = symbol_table[pos];    /* default mode defn */
  int cur_mode = get_current_mode();
  PSENTRY next;

  if (cur_mode == IERR) {               /* unknown or default mode */
    return(start);
  }
  if (get_mode(start) == cur_mode) {    /* modes match */
    return(start);
  }
  next = get_next_mode(start);           /* look for next in the list */
  while (next != NULL) {
    if (get_mode(next) == cur_mode) {   /* modes match */
      return(next);
    }
    next = get_next_mode(next);         /* repeat */   
  }                            /* no mode match, return non-mode entry */
  return(start);
}                                                /* end GET_MODE_SYM */