/* printct.c    Main and support routines for printing ltx2x ct file   */
/* Written by Peter Wilson (Catholic University and NIST)              */
/*            pwilson@cme.nist.gov                                     */
/*            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 pwilson@cme.nist.gov\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 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 */
            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 */