/* printct.c    Main and support routines for printing ltx2x ct file   */
/* Written by Peter Wilson (Catholic University and NIST)              */
/*            [email protected]                                     */
/*            Version 0.1, July 1996                                   */
/*                    0.2, November 1996                               */
/*---------------------------------------------------------------------*/

char FILE_VERSION[] = "Version 0.2";
char FILE_DATE[] = "November 1996";

/* VERSION HISTORY:
* Version 0.1 (July 1996): First release
* Version 0.2 (November 1996): Added SWITCH_TO_XXX and SWITCH_BACK
*                              Added CODE and friends
*
*/

/* Development of this software was funded by the United States Government,
* and is not subject to copyright.
*/

/* 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 PRINTCT software.
* If the PRINTCT 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 PRINTCT 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 PRINTCT 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 PRINTCT 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 */

# define MAX_ERRORS 10

FILE *filerr;                        /* Error file */
FILE *filtabin;                      /* Table input file */
FILE *filtabout;                     /* Table output file */


int TDEBUG = FALSE;                /* for debugging command table */

 /* length of a user buffer */
# define MAX_UBUFF_LEN 514

char errstr[MAX_UBUFF_LEN];      /* buffer for assembling error/warning text */

/* keywords */
enum key_word{AUDIBLE_ALERT_CHAR,
             BACKSPACE_CHAR,
             CA,
             CARRIAGE_RETURN_CHAR,
             CODE,
             CODE_SETUP,
             COMMENT,
             CONTINUE,
             END_CODE,
             END_CTFILE,
             END_ITEM,
             END_ITEM_PARAM,
             END_MODE,
             END_OPT,
             END_TAG,
             END_TAG_1,
             END_TAG_2,
             END_TAG_3,
             END_TAG_4,
             END_TAG_5,
             END_TAG_6,
             END_TAG_7,
             END_TAG_8,
             END_TAG_9,
             END_TYPE,
             ESCAPE_CHAR,
             FORMFEED_CHAR,
             HEX_CHAR,
             HORIZONTAL_TAB_CHAR,
             INCLUDE,
             IN_MODE,
             NAMEK,
             NEWLINE_CHAR,
             OPT_PARAM,
             PC_AT_END,
             PC_AT_START,
             PRINT_CONTROL,
             PRINT_OPT,
             PRINT_P1,
             PRINT_P2,
             PRINT_P3,
             PRINT_P4,
             PRINT_P5,
             PRINT_P6,
             PRINT_P7,
             PRINT_P8,
             PRINT_P9,
             REQPARAMS,
             RESET_BUFFER,
             RESET_FILE,
             RESET_MODE,
             RESET_SYSBUF,
             SECTIONING_LEVEL,
             SET_MODE,
             SOURCE,
             SPECIAL_TOKEN,
             START_ITEM,
             START_ITEM_PARAM,
             START_OPT,
             START_TAG,
             START_TAG_1,
             START_TAG_2,
             START_TAG_3,
             START_TAG_4,
             START_TAG_5,
             START_TAG_6,
             START_TAG_7,
             START_TAG_8,
             START_TAG_9,
             STRINGG,
             SWITCH_BACK,
             SWITCH_TO_BUFFER,
             SWITCH_TO_FILE,
             SWITCH_TO_SYSBUF,
             TYPE,
             VERTICAL_TAB_CHAR,
             MAX_KEYSTR};
int max_keystr = MAX_KEYSTR;
STRING key_array[MAX_KEYSTR];


void strtouc();                /* convert string to upper case */
int lookup_string();  /* finds posn. of string in an ordered array of strings */
void init_print_control();
void init_keys();
int key_to_int();
STRING key_to_string();

enum cont_enum {CONT_UNKNOWN_ENUM,
               CONT_TAG};

#define MAX_TABLE_LINE 1000
char table_line[MAX_TABLE_LINE];
#define MAX_TABLE_ENTRIES 1000
int num_table_errors = 0;


STRING unk = "ERROR";

enum print_control{BUFFER,
                  DEFAULT_PRINT,
                  FFILE,
                  FIRST_STRING,
                  MODE,
                  NO_OP,
                  NO_PRINT,
                  PRINT_UNDERFLOW,
                  RESET,
                  SOURCE_STRING,
                  SYSBUF,
                  TO_BUFFER,
                  TO_FILE,
                  TO_SYSBUF,
                  UNKNOWN_PRINT,
                  MAX_PCSTR};
int max_pcstr = MAX_PCSTR;
STRING pc_array[MAX_PCSTR];


void delete_to_chars();
void table_error();
void tdebug_tline();
void tdebug_str_str_int();
void tdebug_str_int();
void tdebug_str();
void process_table();
void process_pc();
int ct_lineno = 0;                               /* table line number */


/*         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() */





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

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

                /* print banner */
  fprintf(stdout, "\n          printct: An ltx2x command table printer\n");
  fprintf(stdout, "\n          (%s, %s)\n", FILE_VERSION, FILE_DATE);

                /* open error log file */
  file = fopen("printct.err", "w");
  if (!file) {
    fprintf(stderr, "Fatal Error: Could not open file printct.err\n");
    exit(1);
  }
  filerr = file;
  fprintf(stdout, "Error file is printct.err\n");
  fprintf(filerr, "Error file for program printct (%s, %s)\n", FILE_VERSION, FILE_DATE);
  fprintf(filerr, "Author: Peter Wilson (Catholic University and NIST)\n");
  fprintf(filerr, "Email any comments or suggestions to [email protected]\n\n");
               /* open Table output file */
  file = fopen("printct.lis", "w");
  if (!file) {
    fprintf(stderr, "Fatal Error: Could not open file printct.lis\n");
    exit(1);
  }
  filtabout = file;
  fprintf(stdout, "Table output file is printct.lis\n");
  fprintf(filerr, "Table output file is printct.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,"tf:P:D:"))) {
   /* Uwe Sassenberg [email protected] 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 */
           fprintf(stderr, "\nUsage: [-t] [-f tablename] [-P chars] [-D char]\n");
           fprintf(filerr, "\nUsage: [-t] [-f tablename] [-P chars] [-D char]\n");
           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 'f':             /* special Table input file */
           strcpy(tabnam, optarg);
           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;
         }  /* end of 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);

   /* No other parameters */

                    /* do some initialisations */
   init_keys();                         /* 6/96 initialise command keywords */
   init_print_control();    /* initialise pc keywords */

   process_table(filtabin, filtabout);
   fclose(filtabin);
   fclose(filtabout);
   exit(0);

}                                                             /* end MAIN */
/*------------------------------------------------------------------------*/


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

/* 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 */


/* 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 */



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


/* TABLE_ERROR prints Command table error message */
void table_error(s)
char *s;
{
 fprintf(stderr, "\nCommand table: %s line\n%d: %s\n",
                                    s, ct_lineno, table_line);
 fprintf(filerr, "\nCommand table: %s line\n%d: %s\n",
                                    s, ct_lineno, 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 */

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


/* 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 */

/* PROCESS_TABLE reads and writes a command table */
/* Just checks keyword at start of line then prints the line out         */
/* Keywords are specified in key_word enumeration                        */
/*-----------------------------------------------------------------------*/

int in_code;                     /* global flag TRUE while processing CODE */
void process_table(fin, fout)
 FILE *fin;                      /* input file */
 FILE *fout;                     /* output file */
{

 char line[MAX_TABLE_LINE];
 char code_name[MAX_TABLE_LINE];
 int num_names;
 int num;
 char str[MAX_TABLE_LINE];
 int last_string = CONT_UNKNOWN_ENUM;
 char *cin;
 int key;                                /* command keyword */

 STRING tab0 = "";
 STRING tab1 = "  ";
 STRING tab2 = "    ";
 STRING tab3 = "      ";
 char code_tab[20];

 in_code = FALSE;                    /* flag for CODE processing */


 for (;;) {                              /* loop over all files */
   line[0] = SNUL;
   cin = fgets(line, MAX_TABLE_LINE, fin);   /* read a line */
   ct_lineno++;
   if (TDEBUG) {
     tdebug_tline(line);
   }
   if (feof(fin) != 0) {                             /* end of this file */
     return;
   }
   strcpy(table_line, line);
           /* get first name on line */
   num_names = sscanf(line, "%s", code_name);
   if (num_names <= 0) {                    /* blank line */
     fprintf(fout, "\n");
     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");
       }
       delete_to_chars("=:", line);
       fprintf(fout, "%s%s%s", tab0, key_to_string(key), line);
       break;
     }

     case CA :
     case COMMENT : {                            /* comment line */
       if (TDEBUG) {
         tdebug_str("C=\n");
       }
       delete_to_chars("=:", line);
       fprintf(fout, "%sc=%s", tab3, line);  /* print lower case c= */
       break;
     }

     case INCLUDE : {        /* file inclusion */
       if (TDEBUG) {
         tdebug_str(code_name);
       }
       last_string = CONT_UNKNOWN_ENUM;
       delete_to_chars("=:", line);
       fprintf(fout, "%s%s%s", tab0, key_to_string(key), line);
       break;
     }

     case TYPE : {     /* start of a record */
       last_string = CONT_UNKNOWN_ENUM;
       delete_to_chars("=:", line);
       fprintf(fout, "%s%s%s", tab0, key_to_string(key), line);
       break;
     }

     case END_TYPE : {     /* end of a record */
       last_string = CONT_UNKNOWN_ENUM;
/*        delete_to_chars("=:", line); */
       fprintf(fout, "%s%s", tab0, line);
       break;
     }

     case NAMEK : {                        /* process NAME= */
       last_string = CONT_UNKNOWN_ENUM;
       delete_to_chars("=:", line);
       fprintf(fout, "%s%s%s", tab0, key_to_string(key), line);
       break;
     }

     case PRINT_CONTROL : {                /* process  PRINT_CONTROL= */
       last_string = CONT_UNKNOWN_ENUM;
       delete_to_chars("=:", line);
       if (TDEBUG) {
         sprintf(errstr, "%s %s\n", code_name, line);
         tdebug_str(errstr);
       }
       fprintf(fout, "%s%s", tab1, key_to_string(key));
       process_pc(fout, line);
       break;
     }

     case START_TAG :
     case END_TAG : {                     /* process START/END__TAG= */
       last_string = CONT_TAG;
       delete_to_chars("=:", line);
       fprintf(fout, "%s%s%s", tab1, key_to_string(key), line);
       break;
     }

     case OPT_PARAM : {                    /* process OPT_PARAM= */
       last_string = CONT_UNKNOWN_ENUM;
       delete_to_chars("=:", line);
       fprintf(fout, "%s%s%s", tab1, key_to_string(key), line);
       break;
     }

     case PRINT_OPT : {                   /* process PRINT_OPT= */
       last_string = CONT_UNKNOWN_ENUM;
       delete_to_chars("=:", line);
       fprintf(fout, "%s%s%s", tab1, key_to_string(key), line);
       break;
     }

     case START_OPT :
     case END_OPT : {                    /* process START/END_OPT= */
       last_string = CONT_TAG;
       delete_to_chars("=:", line);
       fprintf(fout, "%s%s%s", tab1, key_to_string(key), line);
       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 ) {
         ;
       }
       else {
         table_error("REQPARAMS out of range (0-9)");
       }
       fprintf(fout, "%s%s%s", tab1, key_to_string(key), line);
       break;
     }

     case START_TAG_1 :
     case END_TAG_1 :
     case START_TAG_2 :
     case END_TAG_2 :
     case START_TAG_3 :
     case END_TAG_3 :
     case START_TAG_4 :
     case END_TAG_4 :
     case START_TAG_5 :
     case END_TAG_5 :
     case START_TAG_6 :
     case END_TAG_6 :
     case START_TAG_7 :
     case END_TAG_7 :
     case START_TAG_8 :
     case END_TAG_8 :
     case START_TAG_9 :
     case END_TAG_9 : {                 /* process START/END_TAG_N= */
       last_string = CONT_TAG;
       delete_to_chars("=:", line);
       fprintf(fout, "%s%s%s", tab1, key_to_string(key), line);
       break;
     }

     case PRINT_P1 :
     case PRINT_P2 :
     case PRINT_P3 :
     case PRINT_P4 :
     case PRINT_P5 :
     case PRINT_P6 :
     case PRINT_P7 :
     case PRINT_P8 :
     case PRINT_P9 : {                    /* process PRINT_PN= */
       last_string = CONT_UNKNOWN_ENUM;
       delete_to_chars("=:", line);
       fprintf(fout, "%s%s", tab1, key_to_string(key));
       process_pc(fout, line);
       break;
     }

     case START_ITEM :
     case END_ITEM : {              /* process START/END_ITEM= */
       last_string = CONT_TAG;
     delete_to_chars("=:", line);
     fprintf(fout, "%s%s%s", tab1, key_to_string(key), line);
     break;
   }

     case START_ITEM_PARAM :
     case END_ITEM_PARAM : {          /* process START/END_ITEM_PARAM= */
       last_string = CONT_TAG;
     delete_to_chars("=:", line);
     fprintf(fout, "%s%s%s", tab1, key_to_string(key), line);
     break;
   }

     case CONTINUE :                         /* process CONTINUE= */
     case STRINGG : {                        /* process STRING= */
       delete_to_chars("=:", line);
       fprintf(fout, "%s%s%s", tab2, key_to_string(STRINGG), line);
       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);
       }
       delete_to_chars("=:", line);
       fprintf(fout, "%s%s%s", tab2, key_to_string(key), line);
       break;
     }

     case PC_AT_START :
     case PC_AT_END : {                    /* 6/96 process PC_AT_START/END= */
       last_string = CONT_UNKNOWN_ENUM;
       delete_to_chars("=:", line);
       fprintf(fout, "%s%s", tab1, key_to_string(key));
       process_pc(fout, 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);
       fprintf(fout, "%s%s%s", tab1, key_to_string(key), line);
       if (TDEBUG) {
         sprintf(errstr, "%s %s\n", code_name, str);
         tdebug_str(errstr);
       }
       break;
     }

     case RESET_SYSBUF :
     case RESET_BUFFER :
     case RESET_FILE :
     case RESET_MODE :
     case SET_MODE :  {                 /* process (RE)SET_X= */
       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);
       }
       delete_to_chars("=:", line);
       fprintf(fout, "%s%s%s", tab2, key_to_string(key), line);
       break;
     }

     case SWITCH_BACK :
     case SWITCH_TO_BUFFER :
     case SWITCH_TO_FILE :
     case SWITCH_TO_SYSBUF : {           /* 11/96 process SWITCH-XXX */
       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);
       }
       delete_to_chars("=:", line);
       fprintf(fout, "%s%s%s", tab2, key_to_string(key), line);
       break;
     }

     case IN_MODE : {                    /* 6/96 process IN_MODE */
       last_string = CONT_UNKNOWN_ENUM;
       delete_to_chars("=:", line);
       fprintf(fout, "%s%s%s", tab0, key_to_string(key), line);
       break;
     }

     case END_MODE : {                    /* 6/96 process END_MODE */
       last_string = CONT_UNKNOWN_ENUM;
/*        delete_to_chars("=:", line); */
       fprintf(fout, "%s%s", tab0, line);
       break;
     }

     case SPECIAL_TOKEN : {                /* process SPECIAL_TOKEN= */
       last_string = CONT_UNKNOWN_ENUM;
       delete_to_chars("=:", line);
       fprintf(fout, "%s%s%s", tab0, key_to_string(key), line);
       break;
     }

     case ESCAPE_CHAR :
     case NEWLINE_CHAR :
     case HORIZONTAL_TAB_CHAR :
     case VERTICAL_TAB_CHAR :
     case BACKSPACE_CHAR :
     case CARRIAGE_RETURN_CHAR :
     case FORMFEED_CHAR :
     case AUDIBLE_ALERT_CHAR :
     case HEX_CHAR : {                      /* process characters */
       if (TDEBUG) {
         tdebug_str(code_name);
       }
       last_string = CONT_UNKNOWN_ENUM;
       delete_to_chars("=:", line);
       fprintf(fout, "%s%s%s", tab0, key_to_string(key), line);
       break;
     }

     case CODE_SETUP : {          /* CODE_SETUP added for code interp */
       if (TDEBUG) {
         sprintf(errstr, "%s\n", code_name);
         tdebug_str(errstr);
       }
       last_string = CONT_UNKNOWN_ENUM;
       fprintf(fout, "%s%s\n", tab0, key_to_string(key));
       strcpy(code_tab, tab0);
       in_code = TRUE;
       break;
     }

     case CODE : {               /* CODE added for code interp */
       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);
       }
       fprintf(fout, "%s%s\n", tab2, key_to_string(key));
       strcpy(code_tab, tab2);
       in_code = TRUE;
       break;
     }


     default : {                              /* possibly unrecognised */
       if (!in_code) {                        /* unrecognised! */
         if (TDEBUG) {
           tdebug_str("UNRECOGNISED CODE NAME\n");
         }
         last_string = CONT_UNKNOWN_ENUM;
         table_error("Unrecognised code");
         break;
       }
       /* are in CODE processing */
       if (key == END_CODE) {                    /* end of CODE */
         if (TDEBUG) {
           sprintf(errstr, "%s\n", code_name);
           tdebug_str(errstr);
         }
         fprintf(fout, "%s%s\n", code_tab, key_to_string(key));
         in_code = FALSE;
         break;
       }
       else {                                   /* still in CODE, just print */
         fprintf(fout, "%s%s", code_tab, table_line);
         break;
       }
       break;
     } /* end of default case */
   }  /* end of switch */
   fflush(fout);

 } /* end of loop over all files */
}                                      /* end PROCESS_TABLE */


      /*---------------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 */





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



/* PARSE_PC parses print control and returns new structure */
void process_pc(fout, a_line)
 FILE *fout;
 char a_line[];
{
 char key_name[MAX_TABLE_LINE];
 int key_enum;


 /* 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 */
     fprintf(fout, "%s", a_line);
     return;
   }
   case NO_PRINT: { /* got it all */
     fprintf(fout, "%s", a_line);
     return;
   }
   case TO_SYSBUF: { /* got it all */
     fprintf(fout, "%s", a_line);
     return;
   }
   case PRINT_UNDERFLOW: { /* got it all */
     fprintf(fout, "%s", a_line);
     return;
   }
   case UNKNOWN_PRINT: { /* got it all */
     fprintf(fout, "%s", a_line);
     return;
   }

   case TO_BUFFER: { /* get buffer number */
     fprintf(fout, "%s", a_line);
     return;
   }

   case TO_FILE: { /* get file */
     fprintf(fout, "%s", a_line);
     return;
   }

   case SYSBUF: { /* got it all */
     fprintf(fout, "%s", a_line);
     return;
   }

   case BUFFER: { /* get buffer number */
     fprintf(fout, "%s", a_line);
     return;
   }

   case FFILE: { /* get file */
     fprintf(fout, "%s", a_line);
     return;
   }

   case FIRST_STRING: { /* get string */
     fprintf(fout, "%s", a_line);
     return;
   }

   case RESET: { /* got it all */
     fprintf(fout, "%s", a_line);
     return;
   }

   case NO_OP: { /* got it all */
     fprintf(fout, "%s", a_line);
     return;
   }

   default: {
     fprintf(fout, "**** %s", a_line);
     return;
   }
 }
}                                       /* end PROCESS_PC */

/* 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()
{

 /* set up enum/str array */
 pc_array[BUFFER] = "BUFFER";
 pc_array[DEFAULT_PRINT] = "DEFAULT_PRINT";
 pc_array[FFILE] = "FILE";
 pc_array[FIRST_STRING] = "FIRST_STRING";
 pc_array[MODE] = "MODE";
 pc_array[NO_OP] = "NO_OP";
 pc_array[NO_PRINT] = "NO_PRINT";
 pc_array[PRINT_UNDERFLOW] = "PRINT_UNDERFLOW";
 pc_array[RESET] = "RESET";
 pc_array[SOURCE_STRING] = "SOURCE_STRING";
 pc_array[SYSBUF] = "SYSBUF";
 pc_array[TO_BUFFER] = "TO_BUFFER";
 pc_array[TO_FILE] = "TO_FILE";
 pc_array[TO_SYSBUF] = "TO_SYSBUF";
 pc_array[UNKNOWN_PRINT] = "UNKNOWN_PRINT";


}                                               /* 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 */



/* INIT_KEYS initialises keyword stuff */
void init_keys()
{

 /* set up enum/string array */
 key_array[AUDIBLE_ALERT_CHAR] = "AUDIBLE_ALERT_CHAR=";
 key_array[BACKSPACE_CHAR] = "BACKSPACE_CHAR=";
 key_array[CA] = "C=";
 key_array[CARRIAGE_RETURN_CHAR] = "CARRIAGE_RETURN_CHAR=";
 key_array[CODE] = "CODE:";
 key_array[CODE_SETUP] = "CODE_SETUP=";
 key_array[COMMENT] = "COMMENT=";
 key_array[CONTINUE] = "CONTINUE=";
 key_array[END_CODE] = "END_CODE";
 key_array[END_CTFILE] = "END_CTFILE=";
 key_array[END_ITEM] = "END_ITEM=";
 key_array[END_ITEM_PARAM] = "END_ITEM_PARAM=";
 key_array[END_MODE] = "END_MODE";
 key_array[END_OPT] = "END_OPT=";
 key_array[END_TAG] = "END_TAG=";
 key_array[END_TAG_1] = "END_TAG_1=";
 key_array[END_TAG_2] = "END_TAG_2=";
 key_array[END_TAG_3] = "END_TAG_3=";
 key_array[END_TAG_4] = "END_TAG_4=";
 key_array[END_TAG_5] = "END_TAG_5=";
 key_array[END_TAG_6] = "END_TAG_6=";
 key_array[END_TAG_7] = "END_TAG_7=";
 key_array[END_TAG_8] = "END_TAG_8=";
 key_array[END_TAG_9] = "END_TAG_9=";
 key_array[END_TYPE] = "END_TYPE";
 key_array[ESCAPE_CHAR] = "ESCAPE_CHAR=";
 key_array[FORMFEED_CHAR] = "FORMFEED_CHAR=";
 key_array[HEX_CHAR] = "HEX_CHAR=";
 key_array[HORIZONTAL_TAB_CHAR] = "HORIZONTAL_TAB_CHAR=";
 key_array[INCLUDE] = "INCLUDE=";
 key_array[IN_MODE] = "IN_MODE=";
 key_array[NAMEK] = "NAME=";
 key_array[NEWLINE_CHAR] = "NEWLINE_CHAR=";
 key_array[OPT_PARAM] = "OPT_PARAM=";
 key_array[PC_AT_END] = "PC_AT_END=";
 key_array[PC_AT_START] = "PC_AT_START=";
 key_array[PRINT_CONTROL] = "PRINT_CONTROL=";
 key_array[PRINT_OPT] = "PRINT_OPT=";
 key_array[PRINT_P1] = "PRINT_P1=";
 key_array[PRINT_P2] = "PRINT_P2=";
 key_array[PRINT_P3] = "PRINT_P3=";
 key_array[PRINT_P4] = "PRINT_P4=";
 key_array[PRINT_P5] = "PRINT_P5=";
 key_array[PRINT_P6] = "PRINT_P6=";
 key_array[PRINT_P7] = "PRINT_P7=";
 key_array[PRINT_P8] = "PRINT_P8=";
 key_array[PRINT_P9] = "PRINT_P9=";
 key_array[REQPARAMS] = "REQPARAMS=";
 key_array[RESET_BUFFER] = "RESET_BUFFER:";
 key_array[RESET_FILE] = "RESET_FILE:";
 key_array[RESET_MODE] = "RESET_MODE:";
 key_array[RESET_SYSBUF] = "RESET_SYSBUF:";
 key_array[SECTIONING_LEVEL] = "SECTIONING_LEVEL=";
 key_array[SET_MODE] = "SET_MODE:";
 key_array[SOURCE] = "SOURCE:";
 key_array[SPECIAL_TOKEN] = "SPECIAL_TOKEN=";
 key_array[START_ITEM] = "START_ITEM=";
 key_array[START_ITEM_PARAM] = "START_ITEM_PARAM=";
 key_array[START_OPT] = "START_OPT=";
 key_array[START_TAG] = "START_TAG=";
 key_array[START_TAG_1] = "START_TAG_1=";
 key_array[START_TAG_2] = "START_TAG_2=";
 key_array[START_TAG_3] = "START_TAG_3=";
 key_array[START_TAG_4] = "START_TAG_4=";
 key_array[START_TAG_5] = "START_TAG_5=";
 key_array[START_TAG_6] = "START_TAG_6=";
 key_array[START_TAG_7] = "START_TAG_7=";
 key_array[START_TAG_8] = "START_TAG_8=";
 key_array[START_TAG_9] = "START_TAG_9=";
 key_array[STRINGG] = "STRING:";
 key_array[SWITCH_BACK] = "SWITCH_BACK:";
 key_array[SWITCH_TO_BUFFER] = "SWITCH_TO_BUFFER:";
 key_array[SWITCH_TO_FILE] = "SWITCH_TO_FILE:";
 key_array[SWITCH_TO_SYSBUF] = "SWITCH_TO_SYSBUF:";
 key_array[TYPE] = "TYPE=";
 key_array[VERTICAL_TAB_CHAR] = "VERTICAL_TAB_CHAR=";

}                                                   /* end INIT_KEYS */


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

 pos = lookup_string(s, key_array, max_keystr);
 if (pos == -1) {                             /* unknown string */
   if (!in_code) {
     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 */