home *** CD-ROM | disk | FTP | other *** search
/ The C Users' Group Library 1994 August / wc-cdrom-cusersgrouplibrary-1994-08.iso / vol_300 / 329_02 / xcxref.c < prev    next >
C/C++ Source or Header  |  1990-06-11  |  51KB  |  1,761 lines

  1.       /***********************************************************
  2.  
  3.       XCXREF  -  A 'C' Concordance Utility
  4.  
  5.       Version 1.0   xc     - January, 1982
  6.       Version 1.0   xcxref - May,     1990
  7.  
  8.       Copyright (c) 1982 by Philip N. Hisley
  9.  
  10.               Philip N. Hisley
  11.               548H Jamestown Court
  12.               Edgewood, Maryland 21040
  13.               (301) 679-4606
  14.  
  15.       Released for non-commercial distribution only
  16.  
  17.       Converted to IBM/PC CI/C86 by David N. Smith, May/June 1983
  18.       with enhancements and Lattice compiler support in December 1983.
  19.  
  20.               David N. Smith
  21.               44 Ole Musket Lane
  22.               Danbury, CT 06810
  23.               (203) 748-5934
  24.               CompuServe: 73145,153
  25.  
  26.       Changes Copyright (c) 1983 by David N. Smith
  27.       Permission granted to copy for non-commercial purposes.
  28.  
  29.       Version v1.0   XCXREF - May, 1990.
  30.       Program functions extended, hence the name change.
  31.  
  32.               Martin D. Winnick
  33.               5301 El Arbol Drive
  34.               Carlsbad, CA 92008
  35.               (619) 431-0485
  36.               CompuServe: 71665,456
  37.               May 1990
  38.  
  39.       Changes Copyright (c) 1990 by Martin D. Winnick
  40.       Permission granted to copy for non-commercial use only.
  41.       See the accompanying xcxref.doc file for program history and
  42.       all change details.
  43.  
  44.  
  45.       Abstract:
  46.  
  47.       'XCXREF' is a cross-reference utility for 'C' programs.
  48.       Its will handle nested #include files and properly process
  49.       nested comments.
  50.  
  51.       Option flags control the following features:
  52.  
  53.       Usage: xcxref <filename> <flag(s)>
  54.  
  55.       Flags: -e            = Write program data to log file
  56.              -g            = Ignore missing files
  57.              -i            = Enable #include processing
  58.              -l            = Generate listing only - no xref
  59.              -o <outfile>  = Write output to named file
  60.              -p            = Write output to line printer LPT1
  61.              -r            = Cross-reference 'C' reserved words
  62.              -s            = Write output to video screen
  63.              -w width      = Width of output page; default = 78
  64.                                                    max     = 150
  65.       Flags MUST FOLLOW all input file names
  66.  
  67.       ***********************************************************/
  68.  
  69. #include "bios.h"
  70. #include "ctype.h"
  71. #include "direct.h"
  72. #include "dos.h"
  73. #include "stdio.h"
  74. #include "stdlib.h"
  75. #include "string.h"
  76. #include "time.h"
  77.  
  78. #ifndef  TRUE
  79. #define  TRUE        1
  80. #define  FALSE       0
  81. #endif
  82.  
  83. #define  ERROR      -1
  84. #define  FF         0x0C        /* formfeed                            */
  85. #define  FOREVER    for(;;)
  86. #define  LINES_PER_PAGE 60
  87. #define  LPT1       0           /* defines LPT1                        */
  88. #define  MAX_ALPHA  53          /* maximum alpha chain heads           */
  89. #define  MAX_LEN    31          /* maximum identifier length           */
  90. #define  MAX_REF     5          /* maximum refs per ref-block          */
  91. #define  MAX_REFS_LINE  10      /* maximum refs per line               */
  92. #define  MAX_WRD  5000          /* maximum number of identifiers (749) */
  93. #define  MAXCOL     78          /* right margin for listing line       */
  94. #define  MAXLINE   150          /* maximum print line length.          */
  95. #define  MINCOL     30          /* minimum value for -w option         */
  96.  
  97. struct alpha_hdr
  98.     {
  99.     struct id_blk *alpha_top;
  100.     struct id_blk *alpha_lst;
  101.     };
  102.  
  103. struct  id_blk
  104.     {
  105.     char  id_name[MAX_LEN];
  106.     struct id_blk *alpha_lnk;
  107.     struct rf_blk *top_lnk;
  108.     struct rf_blk *lst_lnk;
  109.     };
  110.  
  111. struct  rf_blk
  112.     {
  113.     int  ref_item[MAX_REF];
  114.     int  ref_cnt;
  115.     struct rf_blk *next_rfb;
  116.     };
  117.  
  118. struct alpha_hdr alpha_vector[MAX_ALPHA];
  119. struct dosdate_t dt;
  120. struct id_blk *id_vector[MAX_WRD];
  121.  
  122. int     al_count   = 0;       /* number of alpha links        */
  123. int     edtnum     = 0;       /* edit line number             */
  124. int     file_level = 0;       /* file level                   */
  125. int     hash_hits  = 0;       /* number of conflict hits      */
  126. int     id_cnt     = 0;       /* number of unique identifiers */
  127. int     id_count   = 0;       /* number of id structs alloc.  */
  128. int     linum      = 0;       /* line number                  */
  129. int     paglin     = 0;       /* page line counter            */
  130. int     pagno      = 0;       /* page number                  */
  131. int     rf_count   = 0;       /* number of rf structs alloc.  */
  132.  
  133. short   base_row    = 0;             /* basic data display video row   */
  134. short   maxcol      = MAXCOL;        /* right column for listing       */
  135. short   maxrefs     = MAX_REFS_LINE; /* max references per line        */
  136. short   vline       = 10;            /* work data display video column */
  137.  
  138. short   do_echo     = TRUE;
  139. short   do_includes = FALSE;
  140. short   do_lprint   = FALSE;
  141. short   do_numbrs   = TRUE;
  142. short   do_outfile  = FALSE;
  143. short   do_res_wds  = FALSE;
  144. short   do_screen   = FALSE;
  145. short   file_queue  = FALSE;
  146. short   ignore      = FALSE;
  147. short   infl_open   = FALSE;
  148. short   lf_open     = FALSE;
  149. short   list_only   = FALSE;
  150. short   log_on      = FALSE;
  151. short   prt_ref     = FALSE;
  152. short   qfl_open    = FALSE;
  153. short   qfirst      = TRUE;
  154.  
  155. char    current_file[_MAX_PATH] = { '\0' };
  156. char    glbl_file[_MAX_PATH]    = { '\0' };
  157. char    list_file[_MAX_PATH]    = { '\0' };
  158. char    log_file[]              = { "XCXREF.LOG" };
  159. char    work_name[_MAX_PATH]    = { '\0' };
  160.  
  161. /* Working file name components */
  162. char    wdrive[_MAX_DRIVE]      = { '\0' };
  163. char    wdir[_MAX_DIR]          = { '\0' };
  164. char    wfname[_MAX_FNAME]      = { '\0' };
  165. char    wext[_MAX_EXT]          = { '\0' };
  166.  
  167. /* Current default directory components */
  168. char    ddrive[_MAX_DRIVE]      = { '\0' };
  169. char    ddir[_MAX_DIR]          = { '\0' };
  170.  
  171. /* Alternate path name components */
  172. char    adrive1[_MAX_DRIVE]     = { '\0' };
  173. char    adir1[_MAX_DIR]         = { '\0' };
  174. char    adrive2[_MAX_DRIVE]     = { '\0' };
  175. char    adir2[_MAX_DIR]         = { '\0' };
  176. char    adrive3[_MAX_DRIVE]     = { '\0' };
  177. char    adir3[_MAX_DIR]         = { '\0' };
  178. char    adrive4[_MAX_DRIVE]     = { '\0' };
  179. char    adir4[_MAX_DIR]         = { '\0' };
  180. char    xfname[_MAX_FNAME]      = { '\0' };
  181. char    xext[_MAX_EXT]          = { '\0' };
  182.  
  183. char    b22[]         = { "                      " };
  184. char    *days[]       = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
  185. char    emsgs[71]     = { '\0' };
  186. char    pdate[]       = { "Thu 05/10/90" };
  187. char    prt_line[256] = { '\0' };
  188. char    version[]     = { "v1.0" };
  189. char    xcx_name[]    = { "XCXREF ... " };
  190. char    xname[]       = { "XCXREF.LST" };
  191.  
  192. FILE    *f_list_file;         /* list  file            */
  193. FILE    *lgfile;              /* program data log file */
  194. FILE    *ofile;               /* general open file     */
  195.  
  196. /* function prototypes */
  197. struct rf_blk *add_rf(struct rf_blk *adr_ptr, int adr_ref);
  198. struct id_blk *alloc_id(char *aid_token);
  199. struct rf_blk *alloc_rf(int arf_ref);
  200. void chain_alpha(struct id_blk *ca_ptr, char *ca_token);
  201. int  check_for_resword(char *c_token);
  202. void clr_scrn(short crow, short nrows);
  203. void do_prints(int pparm);
  204. void do_print_xrefs(void);
  205. void echo(char c);
  206. void echochar(char c);
  207. int  get_file_char(FILE *cfile, int *f_eof);
  208. int  get_fqueue(char *qfn);
  209. void get_include_fileid(char *token, FILE *ifile, short parm);
  210. int  get_token(FILE *gfile, char *g_token, int *g_toklen, int *g_eoflg, char x_chr);
  211. void list_err(int parm);
  212. void logit(short parm);
  213. void lprintr(char *buffer);
  214. void new_line(void);
  215. int  openlist(char *lname);
  216. FILE *open_rfile(void);
  217. void pause(clock_t mcount);
  218. void pexit(int xparm);
  219. void print_header(void);
  220. int  proc_file(int incnum);
  221. void put_token(char *p_token, int p_ref);
  222. char read_file_char(FILE *rfile, int *r_eoflg, char rd_flg);
  223. void set_csr(short vrow, short vcol);
  224. void use_err(void);
  225.  
  226. /*************************************************************************/
  227.  
  228. int main(int p_argc, char **p_argv)
  229. {
  230.     char *arg, **argv;
  231.     int  argc, i, ret;
  232.  
  233.     argc = p_argc;
  234.     argv = p_argv;
  235.     if (argc < 2)
  236.       {
  237.       printf("\nThere are no option arguments.\n");
  238.       use_err();
  239.       }
  240.  
  241.     while (--argc != 0)
  242.       {
  243.       if (*(arg = *++argv) == '-')
  244.         {
  245.         switch(*++arg)
  246.           {
  247.           /* log program data to a disk log file. */
  248.           case 'e':
  249.           case 'E': if ((lgfile = fopen(log_file, "w")) != NULL)
  250.                       {
  251.                       log_on = TRUE;
  252.                       break;
  253.                       }
  254.                     else
  255.                       {
  256.                       sprintf(emsgs, "\nCannot open XCXREF log file...%s\n", log_file);
  257.                       pexit(0);
  258.                       }
  259.  
  260.           /* Ignore missing files. */
  261.           case 'g':
  262.           case 'G': ignore = TRUE;
  263.                     break;
  264.  
  265.           /* enable processing of #include */
  266.           case 'i':
  267.           case 'I': do_includes = TRUE;
  268.                     break;
  269.  
  270.           /* xref reserved words also. */
  271.           case 'r':
  272.           case 'R': do_res_wds = TRUE;
  273.                     break;
  274.  
  275.           /* Program listing only - no xref. */
  276.           case 'l':
  277.           case 'L': list_only = TRUE;
  278.                     break;
  279.  
  280.           /* write the xref to the disk file named. */
  281.           case 'o':
  282.           case 'O': do_outfile = TRUE;
  283.                     if ((--argc == 0) || (*++argv[0] == '-'))
  284.                       {
  285.                       printf("\nInvalid -o option argument.\n");
  286.                       use_err();
  287.                       }
  288.  
  289.                     if ((openlist(*++argv)) == 0)
  290.                       {
  291.                       printf("\n%s  %s\n", xcx_name, version);
  292.                       vline += 2;
  293.                       }
  294.                     else
  295.               { pexit(1); }
  296.  
  297.                     break;
  298.  
  299.           /* write xref to the line printer. */
  300.           case 'p':
  301.           case 'P': do_lprint = TRUE;
  302.                     break;
  303.  
  304.           /* write xref to the video screen.     */
  305.           case 's':
  306.           case 'S': do_screen = TRUE;
  307.                     break;
  308.  
  309.           /* set page width. */
  310.           case 'w':
  311.           case 'W': if (--argc == 0)
  312.                       {
  313.                       ("\nInvalid -w option argument.\n");
  314.                       use_err();
  315.                       }
  316.  
  317.             i = atoi(*++argv);
  318.                     if (i <= MINCOL || i >= MAXLINE)
  319.                       {
  320.                       printf("\nInvalid -w option argument.\n");
  321.                       use_err();
  322.                       }
  323.  
  324.                     maxcol = i;
  325.                     break;
  326.  
  327.           default:  printf("\nUnrecognizable option argument.\n");
  328.                     use_err();
  329.           }
  330.         }
  331.       }
  332.  
  333.     if (!do_outfile && !do_lprint && !do_screen)
  334.       {
  335.       printf("\nAt least one output destination must be selected.\n");
  336.       use_err();
  337.       }
  338.  
  339.     /* Get current system date from dos. */
  340.     _dos_getdate(&dt);
  341.     sprintf(pdate, "%s %u/%02u/%02u", days[dt.dayofweek],
  342.                                  dt.month, dt.day, dt.year - 1900);
  343.  
  344.     /* Get currently logged directory. */
  345.     getcwd(current_file, _MAX_DIR);
  346.     strcat(current_file, "\\");
  347.     _splitpath(current_file, ddrive, ddir, xfname, xext);
  348.  
  349.     /* display program header */
  350.     clr_scrn(0, 25);
  351.     set_csr(vline++, 0);
  352.     printf("%s   %s\n", xcx_name, pdate);
  353.     vline++;
  354.  
  355.     prt_ref = FALSE;
  356.     for (linum = 0; linum < MAX_WRD; linum++)
  357.       { id_vector[linum] = NULL; }
  358.  
  359.     for (linum = 0; linum < MAX_ALPHA; linum++)
  360.       { alpha_vector[linum].alpha_top = alpha_vector[linum].alpha_lst = NULL; }
  361.  
  362.     linum = 0;
  363.  
  364.     argc = p_argc;
  365.     argc--;
  366.     argv = p_argv;
  367.     while (argc--)
  368.       {
  369.       if (!file_queue)
  370.         {
  371.         /* Get program names from the command line. */
  372.     strcpy(glbl_file, *++argv);
  373.  
  374.     /* Separate file name into components. */
  375.     _splitpath(glbl_file, wdrive, wdir, wfname, wext);
  376.         }
  377.       else
  378.         {
  379.         /* Get the program names from the list file. */
  380.     /* maintain arg count to preserve while loop */
  381.     argc++;
  382.     if ((ret = get_fqueue(glbl_file)) == -1)
  383.       { pexit(2); }
  384.  
  385.     /* If this is false then at eof of list file, */
  386.     /* go back to command line.                   */
  387.     if (!file_queue)
  388.       {
  389.       argc--;
  390.       continue;
  391.       }
  392.         }
  393.  
  394.       /* If the first character of a file name is an @  */
  395.       /* it is a reference to a list of file names in a */
  396.       /* list file.  Get the program names, and paths   */
  397.       /* from that file.                                */
  398.       /* NOTE: The first character of any file name in  */
  399.       /*       a list file CANNOT be an @.              */
  400.       /* This @filename may be mixed with program names */
  401.       /* on the command line.  When it is encountered   */
  402.       /* XCXREF will get all program names from the     */
  403.       /* file.  It will return to the command line on   */
  404.       /* its eof; however, any alternate paths set will */
  405.       /* be retained but may be overridden.             */
  406.       if (glbl_file[0] == '@')
  407.         {
  408.         /* maintain arg count to preserve while loop */
  409.     argc++;
  410.         file_queue = TRUE;
  411.  
  412.         /* delete the leading '@' */
  413.     strcpy(glbl_file, &glbl_file[1]);
  414.         strcpy(wfname, &wfname[1]);
  415.         continue;
  416.         }
  417.  
  418.       /* No more program names; this is an option; */
  419.       /* we are all done.                          */
  420.       if (glbl_file[0] == '-')
  421.         { break; }
  422.  
  423.       /* This is where all the work will be done. */
  424.       if ((ret = proc_file(0)) == -1)
  425.         {
  426.         /* If this flag is TRUE then ignore a not-found */
  427.         /* source file and just go to the next one.     */
  428.         if (!ignore)
  429.           { pexit(3); }
  430.         }
  431.  
  432.       }  /* end while(argc...) */
  433.  
  434.     /* Process the cross reference.  */
  435.     if (!list_only)
  436.       {
  437.       glbl_file[0] = '\0';
  438.       do_print_xrefs();
  439.       printf("\nAllowable Symbols: %d\n", MAX_WRD);
  440.       printf("Unique    Symbols: %d\n", id_cnt);
  441.       }
  442.  
  443.     if (do_outfile)
  444.       {
  445.       new_line();
  446.       fprintf(f_list_file, "\nAllowable Symbols: %d\n", MAX_WRD);
  447.       fprintf(f_list_file, "Unique    Symbols: %d\n", id_cnt);
  448.       fclose(f_list_file);
  449.       lf_open = FALSE;
  450.       }
  451.  
  452.     if (log_on)
  453.       {
  454.       logit(999);
  455.       fclose(lgfile);
  456.       }
  457.  
  458. }  /* exit xcxref program. */
  459.  
  460. /*************************************************************************/
  461.  
  462. FILE *open_rfile()
  463. /* The file_name is in the q????? component variables and  */
  464. /* must be put together into the work_name variable.       */
  465. /* IF                                                      */
  466. /* wdir is already set then only that pathname will be     */
  467. /* tried.  If not found then will exit without trying any  */
  468. /* alternate pathnames.                                    */
  469. /* ELSE ...                                                */
  470. /* The drive and path in ddir, etc., will be tried.        */
  471. /* If the file is not found there alternate path 1-4 will  */
  472. /* be tried next. If not in any of those a not-found will  */
  473. /* be returned to the caller.  Further disposition will be */
  474. /* up to the caller.                                       */
  475.  
  476. {
  477.   short i   = 0;               /* retry control         */
  478.   int   ret = TRUE;            /* set return as 'found' */
  479.  
  480.   FOREVER
  481.     {
  482.     /* if wdir is already set then do not          */
  483.     /* do any of the alternate directory searches. */
  484.     if (wdir[0] != '\0')
  485.       { i = 99; }
  486.  
  487.     switch (i++)
  488.       {
  489.       /* Default directory */
  490.       case 0: strcpy(wdrive, ddrive);
  491.               strcpy(wdir, ddir);
  492.               break;
  493.  
  494.       /* Alternate 1 directory */
  495.       case 1: if (adrive1[0] != '\0')
  496.                 {
  497.                 strcpy(wdrive, adrive1);
  498.                 strcpy(wdir, adir1);
  499.                 break;
  500.                 }
  501.               else
  502.                 { continue; }
  503.  
  504.       /* Alternate 2 directory */
  505.       case 2: if (adrive2[0] != '\0')
  506.                 {
  507.                 strcpy(wdrive, adrive2);
  508.                 strcpy(wdir, adir2);
  509.                 break;
  510.                 }
  511.               else
  512.                 { continue; }
  513.  
  514.       /* Alternate 3 directory */
  515.       case 3: if (adrive3[0] != '\0')
  516.                 {
  517.                 strcpy(wdrive, adrive3);
  518.                 strcpy(wdir, adir3);
  519.                 break;
  520.                 }
  521.               else
  522.                 { continue; }
  523.  
  524.       /* Alternate 4 directory */
  525.       case 4: if (adrive4[0] != '\0')
  526.                 {
  527.                 strcpy(wdrive, adrive4);
  528.                 strcpy(wdir, adir4);
  529.                 break;
  530.                 }
  531.               else
  532.                 { continue; }
  533.  
  534.       /* Path already set */
  535.       case 99: break;
  536.  
  537.       /* not found in any path */
  538.       /* ignore error return.  */
  539.       default: ret = FALSE;
  540.                break;
  541.  
  542.       }  /* end switch (++path...)  */
  543.  
  544.     /* File not found exit.  */
  545.     if (!ret)
  546.       { break; }
  547.  
  548.     /* Put all file name components together. */
  549.     _makepath(work_name, wdrive, wdir, wfname, wext);
  550.  
  551.     /* Try to open file.  If not able then try next path. */
  552.     if ((ofile = fopen(work_name, "r")) != NULL)
  553.       { break; }
  554.  
  555.     /* Clear so switch will work. */
  556.     wdir[0] = '\0';
  557.  
  558.    }  /* end FOREVER... */
  559.  
  560.   /* Save only .c file names for page header. */
  561.   if (strncmp(wext, ".h", 2) != 0)
  562.     { strcpy(current_file, work_name); }
  563.  
  564.   return(ofile);
  565. }
  566.  
  567. /*************************************************************************/
  568.  
  569. int proc_file(int incnum)
  570. /* The file_name is in the q????? component variables and  */
  571. /* must be put together into the work_name variable.       */
  572. /* -  incnum;        prev. included line number (returned to caller) */
  573. {
  574.   FILE  *infile;        /* input file            */
  575.   char  token[MAX_LEN]; /* token buffer          */
  576.   int   eof_flg;        /* end-of-file indicator */
  577.   int   tok_len;        /* token length          */
  578.   int   i, ret, iret;
  579.  
  580.   /* Reset edit line counter. */
  581.   edtnum = 0;
  582.  
  583.   if ((infile = open_rfile()) == NULL)
  584.     {
  585.     sprintf(emsgs, "\nUnable to open input file: %s\n", work_name);
  586.     printf(emsgs);
  587.     vline += 2;
  588.     logit(101);
  589.     return(-1);             /* caller may ignore error */
  590.     }
  591.  
  592.   infl_open = TRUE;
  593.   if (file_level++ == 0)
  594.     { print_header(); }
  595.  
  596.   eof_flg = FALSE;
  597.   do
  598.     {
  599.     if (get_token(infile, token, &tok_len, &eof_flg, 0x254))
  600.       {
  601.       if (check_for_resword(token))
  602.         {
  603.         if (strcmp(token, "#include") == 0)
  604.           {
  605.       do_echo = FALSE;
  606.       get_include_fileid(token, infile, FALSE);
  607.       do_echo = TRUE;
  608.           if (!do_includes)
  609.             { continue; }
  610.           else
  611.             {
  612.             /* Process #include file. */
  613.         new_line();
  614.  
  615.         /* Separate #include name into components. */
  616.             _splitpath(token, wdrive, wdir, wfname, wext);
  617.  
  618.             /* if file not found then ignore the #include file. */
  619.         if ((iret = proc_file(edtnum)) == -1)
  620.           { ; }
  621.             }  /* end else ... */
  622.           }  /* end if (strcmp...)  */
  623.  
  624.         put_token (token, linum);
  625.         }  /* end if (check_for...)  */
  626.       }  /* end if (get_token...)  */
  627.     } while (!eof_flg);
  628.  
  629.   file_level -= 1;
  630.   fclose(infile);
  631.   infl_open = FALSE;
  632.   return(incnum);
  633. }
  634.  
  635. /*************************************************************************/
  636. /*  Get the program names from the external list file. */
  637. int  get_fqueue(char *qfn)
  638. {
  639.   char  token[MAX_LEN]; /* token buffer            */
  640.   char  q_p;            /* work q_path number      */
  641.  
  642.   int   qeof_flg;       /* end-of-file indicator   */
  643.   int   tok_len;        /* token length            */
  644.  
  645.   FILE  *qfile;         /* program name input file */
  646.  
  647.   /* On the first entry open the external list file. */
  648.   /* Then on this and subsequent calls return a name */
  649.   /* from the file or set file_queue to FALSE to     */
  650.   /* indicate end of list and continue from command  */
  651.   /* line.                                           */
  652.   if (qfirst)
  653.     {
  654.     if ((qfile = fopen(qfn, "r")) == NULL)
  655.       {
  656.       sprintf(emsgs, "\nCannot open external program list file: %s\n", qfn);
  657.       vline += 2;
  658.       printf(emsgs);
  659.       logit(102);
  660.       return(-1);
  661.       }
  662.  
  663.     qfl_open = TRUE;
  664.     qeof_flg = qfirst = FALSE;
  665.     } /* end if (qfirst.. ) */
  666.  
  667.   /* Now get names from the list file.                         */
  668.   /* #qpath are optional paths, if any.  Save in path 1-4.     */
  669.   /* Save only the drive and dir/path, throw away any fn.ext,  */
  670.   /* each is expected as '#qpathn <dr:\path\path\>'; the 'n'   */
  671.   /* may be 1 - 4.                                             */
  672.   /* There must be a space before the '<' and the actual path  */
  673.   /* must be enclosed in the "<>" brackets.                    */
  674.   /* These may be anywhere in the file.  They will become      */
  675.   /* effective when encountered.                               */
  676.   /* To clear an alternate path enter as #qpathn <>            */
  677.   do
  678.     {
  679.     do_echo = FALSE;
  680.     if (get_token(qfile, token, &tok_len, &qeof_flg, '.'))
  681.       {
  682.       if (strncmp(token, "#qpath", 6) == 0)
  683.         {
  684.         /* Save the path number. */
  685.         q_p = token[6];
  686.  
  687.         /* Now go get the path. */
  688.     get_include_fileid(token, qfile, TRUE);
  689.         switch(q_p)
  690.           {
  691.           /* qpath1 */
  692.       case 49: _splitpath(token, adrive1, adir1, xfname, xext);
  693.                   break;
  694.  
  695.           /* qpath2 */
  696.           case 50: _splitpath(token, adrive2, adir2, xfname, xext);
  697.                   break;
  698.  
  699.           /* qpath3 */
  700.           case 51: _splitpath(token, adrive3, adir3, xfname, xext);
  701.                   break;
  702.  
  703.           /* qpath4 */
  704.           case 52: _splitpath(token, adrive4, adir4, xfname, xext);
  705.                   break;
  706.  
  707.           default: break;                 /* max of 4 alternate paths */
  708.           }  /* end switch (path... ) */
  709.         continue;
  710.  
  711.         }  /* end if(strncmp...)  */
  712.  
  713.       /* if the token is not a qpath and not eof then assume */
  714.       /* it is a filename in the form of nnnnnnnn.ext        */
  715.       /* If it has a full path it will override any now set. */
  716.       /* - decompose into separate variables and exit.       */
  717.       _splitpath(token, wdrive, wdir, wfname, wext);
  718.       break;
  719.       }  /* end if(get_token...)  */
  720.     } while (!qeof_flg);
  721.  
  722.   if (qeof_flg)
  723.     {
  724.     fclose(qfile);
  725.     qfl_open = file_queue = FALSE;
  726.     }
  727.  
  728.   do_echo = TRUE;
  729.   return(0);
  730. }
  731.  
  732. /*************************************************************************/
  733.  
  734. void get_include_fileid(char *token, FILE *ifile, short parm)
  735. {
  736.    char c, term;
  737.  
  738.    while ((term = getc(ifile)) == ' ')
  739.      { echo(term); }
  740.  
  741.    echo(term);
  742.    if (term == '<' )
  743.      { term = '>'; }
  744.  
  745.    /* Terminator is > or " */
  746.    if ((term != '>') && (term != '"'))
  747.       {
  748.       sprintf(emsgs, "\nError scanning #INCLUDE fileid: %c\n", term);
  749.       pexit(4);
  750.       }
  751.  
  752.    do
  753.      {
  754.      if ((c = getc(ifile)) != ' ')
  755.        {
  756.        *token++ = c;
  757.        echo(c);
  758.        }
  759.       else
  760.     { echo(c); }
  761.  
  762.      } while (c != term);
  763.  
  764.    *--token = '\0';
  765.  
  766.    /* necessary only for list file paths. */
  767.    if (parm)
  768.      {
  769.      if (token[strlen(token)-1] != '\\')
  770.        { strcat(token, "\\"); }
  771.      }
  772. }
  773.  
  774. /*************************************************************************/
  775.  
  776. /*
  777.   'getoken' returns the next valid identifier or
  778.   reserved word from a given file along with the
  779.   character length of the token and an end-of-file
  780.   indicator.
  781. */
  782. int get_token(FILE *gfile, char *g_token, int *g_toklen, int *g_eoflg, char x_chr)
  783. {
  784.   int     c,
  785.           ret = TRUE;
  786.   char    *h_token;
  787.   char    tmpchr;
  788.  
  789.   h_token = g_token;
  790.  
  791.   FOREVER
  792.     {
  793.     *g_toklen = 0;
  794.     g_token   = h_token;
  795.  
  796.     /*
  797.       Scan and discard characters until an alphabetic or
  798.       '_' (underscore) character is encountered or an end-of-file
  799.       condition occurs
  800.     */
  801.     while ((!isalpha(*g_token = read_file_char(gfile, g_eoflg, x_chr))) &&
  802.         !*g_eoflg                                                   &&
  803.          *g_token != '_'                                            &&
  804.          *g_token != '0'                                            &&
  805.              *g_token != '#');
  806.  
  807.     if (*g_eoflg)
  808.       {
  809.       ret = FALSE;
  810.       break;
  811.       }
  812.  
  813.     *g_toklen += 1;
  814.  
  815.     /*
  816.       Scan and collect identified alphanumeric token until
  817.       a non-alphanumeric character is encountered or an
  818.       end-of-file condition occurs
  819.     */
  820.     tmpchr = x_chr;
  821.  
  822.     while ((isalpha(c = read_file_char (gfile, g_eoflg, x_chr)) ||
  823.         isdigit(c)                                          ||
  824.         c == '_'                                            ||
  825.         c == tmpchr)                                        &&
  826.             !*g_eoflg)
  827.       {
  828.       if (*g_toklen < MAX_LEN)
  829.         {
  830.         *++g_token = c;
  831.         *g_toklen += 1;
  832.         }
  833.       }  /* end while ((isalpha... ) */
  834.  
  835.     /*
  836.       Check to see if a numeric hex or octal constant has
  837.       been encountered ... if so dump it and try again
  838.     */
  839.     if (*h_token == '0')
  840.       { continue; }
  841.  
  842.     /* NULL terminate the token */
  843.     *++g_token = NULL;
  844.  
  845.     /*  Screen out all #token strings except  #include */
  846.     /*  and special #qpathn from qpath list.           */
  847.     if (*h_token == '#')
  848.       {
  849.       if ((strcmp(h_token, "#include") == 0)    ||
  850.       (strncmp(h_token, "#qpath", 6)  == 0))
  851.         { break; }
  852.       else
  853.         { continue; }
  854.       }
  855.  
  856.     break;
  857.     }
  858.  
  859.   return (TRUE);
  860. }
  861.  
  862. /*************************************************************************/
  863.  
  864. int get_file_char(FILE *cfile, int *f_eof)
  865.  {
  866.   int fc;
  867.  
  868.   if ((fc = fgetc(cfile)) == EOF)
  869.     {
  870.     *f_eof = TRUE;
  871.     fc = NULL;
  872.     }
  873.   else
  874.     { *f_eof = FALSE; }
  875.  
  876.   return(fc);
  877. }
  878.  
  879. /*************************************************************************/
  880.  
  881. char read_file_char(FILE *rfile, int *r_eoflg, char rd_flg)
  882.  
  883.   /*
  884.     'read_file_char' returns the next valid character in a file
  885.     and an end-of-file indicator. A valid character is defined
  886.     as any which does not appear in either a commented or a
  887.     quoted string ... 'read_file_char' will correctly handle
  888.     comment tokens which appear within a quoted string.
  889.   */
  890.  
  891. {
  892.   int  c;
  893.   int  double_quotes;      /* double quoted string flag */
  894.   int  single_quotes;      /* single quoted string flag */
  895.   int  comment_start;      /* comment start flag        */
  896.   int  comment_end;        /* comment end flag          */
  897.   int  nesting_level;      /* comment nesting level     */
  898.   int  transparent;        /* transparency flag         */
  899.  
  900.   double_quotes = single_quotes = comment_start = FALSE;
  901.   comment_end   = transparent   = FALSE;
  902.   nesting_level = 0;
  903.  
  904.   FOREVER
  905.     {
  906.     /* Fetch character from file  */
  907.     c = get_file_char(rfile, r_eoflg);
  908.  
  909.     if (*r_eoflg)
  910.       { break; }              /* EOF encountered */
  911.  
  912.     if (c == '\n')
  913.       { new_line(); }
  914.     else
  915.       { echo(c); }
  916.  
  917.     if (rd_flg == 0x254)
  918.       { break; }
  919.  
  920.     if (transparent)
  921.       {
  922.       transparent = !transparent;
  923.       continue;
  924.       }
  925.  
  926.     if (c == '\\')
  927.       {
  928.       transparent = TRUE;
  929.       continue;
  930.       }
  931.  
  932.     /*
  933.       If the character is not part of a quoted string
  934.       check for and process commented strings...
  935.       nested comments are handled correctly but unbalanced
  936.       comments are not ... it is assumed that the syntax of
  937.       the program being xref'd is correct.
  938.     */
  939.     if (!double_quotes && !single_quotes)
  940.       {
  941.       /* test for end of a comment block. */
  942.       if (c == '*' && nesting_level && !comment_start)
  943.         {
  944.         comment_end = TRUE;
  945.         continue;
  946.         }
  947.  
  948.       /* end of a comment */
  949.       if (c == '/' && comment_end)
  950.         {
  951.         nesting_level -= 1;
  952.         comment_end = FALSE;
  953.         continue;
  954.         }
  955.  
  956.       comment_end = FALSE;
  957.       if (c == '/')
  958.         {
  959.         comment_start = TRUE;
  960.         continue;
  961.         }
  962.  
  963.        if (c == '*' && comment_start)
  964.          {
  965.          nesting_level += 1;
  966.          comment_start = FALSE;
  967.          continue;
  968.          }
  969.  
  970.        /* if nesting_level > 0 then we are in a comment block; */
  971.        /* throw away characters until nesting_level is zero.   */
  972.        comment_start = FALSE;
  973.        if (nesting_level)
  974.      { continue; }
  975.       }
  976.  
  977.     /* Check for and process quoted strings */
  978.     if ( c == '"' && !single_quotes)
  979.       {
  980.       /* toggle quote flag */
  981.       double_quotes =  !double_quotes;
  982.       continue;
  983.       }
  984.  
  985.     if (double_quotes)
  986.       { continue; }
  987.  
  988.     if (c == '\'')
  989.       {
  990.       /* toggle quote flag */
  991.       single_quotes = !single_quotes;
  992.       continue;
  993.       }
  994.  
  995.     if (single_quotes)
  996.       { continue; }
  997.  
  998.     /* Valid character ... exit function. */
  999.     break;
  1000.     }  /* end FOREVER */
  1001.  
  1002.   return (c);
  1003. }
  1004.  
  1005. /*************************************************************************/
  1006.  
  1007. int check_for_resword(char *c_token)
  1008. {
  1009.   char  u_token[MAX_LEN];
  1010.   int   ret,
  1011.         i = 0;
  1012.  
  1013.       if (do_res_wds)
  1014.     { return(TRUE); }
  1015.  
  1016.       do
  1017.         {
  1018.         u_token[i] = toupper(c_token[i]);
  1019.         } while (c_token[i++] != NULL);
  1020.  
  1021.       ret = TRUE;
  1022.       switch(u_token[0])
  1023.         {
  1024.         case 'A': if (strcmp(u_token,"AUTO")      == 0)
  1025.             { ret = FALSE; }
  1026.                   break;
  1027.  
  1028.         case 'B': if (strcmp(u_token,"BREAK")     == 0)
  1029.             { ret = FALSE; }
  1030.                   break;
  1031.  
  1032.         case 'C': if ((strcmp(u_token,"CASE")     == 0)  ||
  1033.                       (strcmp(u_token,"CHAR")     == 0)  ||
  1034.                       (strcmp(u_token,"CONST")    == 0)  ||
  1035.                       (strcmp(u_token,"CONTINUE") == 0))
  1036.             { ret = FALSE; }
  1037.                   break;
  1038.  
  1039.         case 'D': if ((strcmp(u_token,"DEFAULT")  == 0)  ||
  1040.                       (strcmp(u_token,"DO")       == 0)  ||
  1041.                       (strcmp(u_token,"DOUBLE")   == 0))
  1042.             { ret = FALSE; }
  1043.                   break;
  1044.  
  1045.         case 'E': if ((strcmp(u_token,"ELSE")     == 0)  ||
  1046.                       (strcmp(u_token,"ENTRY")    == 0)  ||
  1047.                       (strcmp(u_token,"ENUM")     == 0)  ||
  1048.                       (strcmp(u_token,"EXTERN")   == 0))
  1049.             { ret = FALSE; }
  1050.                   break;
  1051.  
  1052.         case 'F': if ((strcmp(u_token,"FLOAT")   == 0)   ||
  1053.                       (strcmp(u_token,"FOR")     == 0))
  1054.             { ret = FALSE; }
  1055.                   break;
  1056.  
  1057.         case 'G': if (strcmp(u_token,"GOTO")     == 0)
  1058.             { ret = FALSE; }
  1059.                   break;
  1060.  
  1061.         case 'I': if ((strcmp(u_token,"IF")      == 0)   ||
  1062.                       (strcmp(u_token,"INT")     == 0))
  1063.             { ret = FALSE; }
  1064.                   break;
  1065.  
  1066.         case 'L': if (strcmp(u_token,"LONG")     == 0)
  1067.             { ret = FALSE; }
  1068.                   break;
  1069.  
  1070.         case 'R': if ((strcmp(u_token,"REGISTER")== 0)   ||
  1071.                       (strcmp(u_token,"RETURN")  == 0))
  1072.             { ret = FALSE; }
  1073.                   break;
  1074.  
  1075.         case 'S': if ((strcmp(u_token,"SHORT")   == 0)   ||
  1076.                       (strcmp(u_token,"SIGNED")  == 0)   ||
  1077.                       (strcmp(u_token,"SIZEOF")  == 0)   ||
  1078.                       (strcmp(u_token,"STATIC")  == 0)   ||
  1079.                       (strcmp(u_token,"STRUCT")  == 0)   ||
  1080.                       (strcmp(u_token,"SWITCH")  == 0))
  1081.             { ret = FALSE; }
  1082.                   break;
  1083.  
  1084.         case 'T': if (strcmp(u_token,"TYPEDEF")  == 0)
  1085.             { ret = FALSE; }
  1086.                   break;
  1087.  
  1088.         case 'U': if ((strcmp(u_token,"UNION")   == 0)   ||
  1089.                       (strcmp(u_token,"UNSIGNED")== 0))
  1090.             { ret = FALSE; }
  1091.                   break;
  1092.  
  1093.         case 'V': if ((strcmp(u_token,"VOID")    == 0)   ||
  1094.                       (strcmp(u_token,"VOLATILE")== 0))
  1095.             { ret = FALSE; }
  1096.                   break;
  1097.  
  1098.         case 'W': if (strcmp(u_token,"WHILE")    == 0)
  1099.             { ret = FALSE; }
  1100.                   break;
  1101.         }
  1102.  
  1103.   return(ret);
  1104. }
  1105.  
  1106. /*************************************************************************/
  1107.  
  1108. /* Install parsed token and line reference in linked structure */
  1109.  
  1110. void put_token(char *p_token, int p_ref)
  1111. {
  1112.   int  hash_index, i,
  1113.        found = FALSE,
  1114.        j     = 0,
  1115.        d     = 1;
  1116.  
  1117.   struct id_blk *idptr;
  1118.   struct rf_blk *rfptr;
  1119.  
  1120.   if (list_only)
  1121.     { return; }
  1122.  
  1123.   /* Hashing algorithm is far from */
  1124.   /* optimal but is adequate for a */
  1125.   /* memory-bound index vector!    */
  1126.   for (i = 0; p_token[i] != NULL; i++)
  1127.     { j = j * 10 + p_token[i]; }
  1128.  
  1129.   hash_index = abs(j) % MAX_WRD;
  1130.   do
  1131.     {
  1132.     if ((idptr = id_vector[hash_index]) == NULL)
  1133.       {
  1134.       id_cnt++;
  1135.       idptr = id_vector[hash_index] = alloc_id(p_token);
  1136.       chain_alpha(idptr, p_token);
  1137.       idptr->top_lnk = idptr->lst_lnk = alloc_rf(p_ref);
  1138.       found = TRUE;
  1139.       }
  1140.     else
  1141.       if(strncmp(p_token, idptr->id_name, MAX_LEN) == 0)
  1142.         {
  1143.         idptr->lst_lnk = add_rf(idptr->lst_lnk, p_ref);
  1144.         found = TRUE;
  1145.         }
  1146.       else
  1147.         {
  1148.         hash_index += d;
  1149.         d += 2;
  1150.         hash_hits++;
  1151.         if (hash_index >= MAX_WRD)
  1152.           hash_index -= MAX_WRD;
  1153.  
  1154.     if (d >= MAX_WRD)
  1155.           {
  1156.           sprintf(emsgs, "\nSymbol table overflow\n");
  1157.           pexit(5);
  1158.           }
  1159.         }
  1160.     } while (!found);
  1161. }
  1162.  
  1163. /*************************************************************************/
  1164.  
  1165. void chain_alpha(struct id_blk *ca_ptr, char *ca_token)
  1166. {
  1167.   char  c;
  1168.  
  1169.   struct id_blk *cur_ptr;
  1170.   struct id_blk *lst_ptr;
  1171.  
  1172.   if ((c = ca_token[0]) == '_')
  1173.     { c = 0; }
  1174.   else
  1175.     if (isupper(c))
  1176.       { c = 1 + ((c-'A')*2); }
  1177.     else
  1178.       { c = 2 + ((c-'a')*2); }
  1179.  
  1180.   FOREVER
  1181.     {
  1182.     if (alpha_vector[c].alpha_top == NULL)
  1183.       {
  1184.       alpha_vector[c].alpha_top = alpha_vector[c].alpha_lst = ca_ptr;
  1185.       ca_ptr->alpha_lnk = NULL;
  1186.       break;
  1187.       }
  1188.  
  1189.     /*
  1190.     check to see if new id_blk should be inserted between
  1191.     the alpha_vector header block and the first id_blk in
  1192.     the current alpha chain
  1193.     */
  1194.     if (strncmp(alpha_vector[c].alpha_top->id_name, ca_token, MAX_LEN) > 0)
  1195.       {
  1196.       ca_ptr->alpha_lnk = alpha_vector[c].alpha_top;
  1197.       alpha_vector[c].alpha_top = ca_ptr;
  1198.       break;
  1199.       }
  1200.  
  1201.     if (strncmp(alpha_vector[c].alpha_lst->id_name, ca_token, MAX_LEN) < 0)
  1202.       {
  1203.       alpha_vector[c].alpha_lst->alpha_lnk = ca_ptr;
  1204.       ca_ptr->alpha_lnk = NULL;
  1205.       alpha_vector[c].alpha_lst = ca_ptr;
  1206.       break;
  1207.       }
  1208.  
  1209.     cur_ptr = alpha_vector[c].alpha_top;
  1210.     while (strncmp(cur_ptr->id_name, ca_token, MAX_LEN) < 0)
  1211.      {
  1212.      lst_ptr = cur_ptr;
  1213.      cur_ptr = lst_ptr->alpha_lnk;
  1214.      }
  1215.  
  1216.     lst_ptr->alpha_lnk = ca_ptr;
  1217.     ca_ptr->alpha_lnk  = cur_ptr;
  1218.     break;
  1219.     }
  1220.  
  1221.   al_count++;
  1222. }
  1223.  
  1224. /*************************************************************************/
  1225.  
  1226. struct id_blk *alloc_id(char *aid_token)
  1227. {
  1228.     int  i = 0;
  1229.     struct id_blk *aid_ptr;
  1230.  
  1231.     if ((aid_ptr = malloc(sizeof(struct id_blk))) == NULL)
  1232.       {
  1233.       sprintf(emsgs, "\nUnable to allocate identifier block\n");
  1234.       pexit(6);
  1235.       }
  1236.  
  1237.     do
  1238.       {
  1239.       aid_ptr->id_name[i] = aid_token[i];
  1240.       } while ((aid_token[i++] != NULL) && (i < MAX_LEN));
  1241.  
  1242.     id_count++;
  1243.     return(aid_ptr);
  1244. }
  1245.  
  1246. /*************************************************************************/
  1247.  
  1248. struct rf_blk *alloc_rf(int arf_ref)
  1249. {
  1250.     short  i;
  1251.     struct rf_blk *arf_ptr;
  1252.  
  1253.     if ((arf_ptr = malloc(sizeof(struct rf_blk))) == NULL)
  1254.       {
  1255.       sprintf(emsgs, "\nUnable to allocate reference block\n");
  1256.       pexit(7);
  1257.       }
  1258.  
  1259.     arf_ptr->ref_item[0] = arf_ref;
  1260.     arf_ptr->ref_cnt     = 1;
  1261.     arf_ptr->next_rfb    = NULL;
  1262.  
  1263.     for (i = 1; i < MAX_REF; i++)
  1264.       { arf_ptr->ref_item[i] = NULL; }
  1265.  
  1266.     rf_count++;
  1267.     return(arf_ptr);
  1268.   }
  1269.  
  1270. /*************************************************************************/
  1271.  
  1272. struct rf_blk *add_rf(struct rf_blk *adr_ptr, int adr_ref)
  1273. {
  1274.     struct rf_blk *tmp_ptr;
  1275.  
  1276.     tmp_ptr = adr_ptr;
  1277.     if (adr_ptr->ref_cnt == MAX_REF)
  1278.       { tmp_ptr = adr_ptr->next_rfb = alloc_rf(adr_ref); }
  1279.     else
  1280.       { adr_ptr->ref_item[adr_ptr->ref_cnt++] = adr_ref; }
  1281.  
  1282.     return (tmp_ptr);
  1283. }
  1284.  
  1285. /*************************************************************************/
  1286.  
  1287. /*
  1288.    build cross reference print lines.
  1289.    when a line is full then write to:
  1290.      the screen,
  1291.      a file,
  1292.      the line printer;
  1293.    depending on program options selected.
  1294. */
  1295. void do_print_xrefs()
  1296. {
  1297.   int  prf_cnt, ptb_cnt, i, line_cnt;
  1298.   int  pref = TRUE;
  1299.  
  1300.   char wk_line[21] = { '\0' };
  1301.   char work[21]    = { '\0' };
  1302.  
  1303.   struct id_blk *pid_ptr;
  1304.   struct rf_blk *ptb_ptr;
  1305.  
  1306.   printf ("\nWriting Cross Reference Table... Please Wait...\n");
  1307.   do_numbrs = FALSE;
  1308.   print_header();
  1309.  
  1310.   for (i = 0; i < MAX_ALPHA; i++)
  1311.     {
  1312.     if ((pid_ptr = alpha_vector[i].alpha_top) != NULL)
  1313.       {
  1314.       do
  1315.         {
  1316.         /* Place reference variable in print line */
  1317.         prt_line[0] = '\0';
  1318.         sprintf(prt_line, "%-20.19s: ", pid_ptr->id_name);
  1319.         ptb_ptr = pid_ptr->top_lnk;
  1320.         line_cnt = prf_cnt = 0;
  1321.  
  1322.         do
  1323.           {
  1324.           if (prf_cnt == MAX_REF)
  1325.             {
  1326.             prf_cnt = 0;
  1327.             ptb_cnt = ptb_ptr->ref_cnt;
  1328.             ptb_ptr = ptb_ptr->next_rfb;
  1329.             }
  1330.  
  1331.           /* if (ptb_cnt > MAX_REF) */
  1332.           if (ptb_ptr != NULL)
  1333.             {
  1334.             if ((pref = ptb_ptr->ref_item[prf_cnt++]) != 0)
  1335.               {
  1336.               sprintf(wk_line, "%4d ", pref);
  1337.               strcat(prt_line, wk_line);
  1338.  
  1339.               /* if the number of references or the next reference */
  1340.               /* line number may exceed the print line capacity    */
  1341.               /* then print the line and start another.            */
  1342.               if ((strlen(prt_line) + (strlen(itoa(linum, work, 10))) > maxcol) ||
  1343.                   (++line_cnt == maxrefs))
  1344.                 {
  1345.                 /* print file line, screen line, etc. */
  1346.                 do_prints(FALSE);
  1347.                 new_line();
  1348.                 sprintf(prt_line, b22);
  1349.                 line_cnt = 0;
  1350.                 }
  1351.               }  /* end if ((pref... ) */
  1352.             }  /* end if (ptb->ptr...) */
  1353.           else
  1354.             {
  1355.             pref = 0;
  1356.             /* print file line, screen line, etc. */
  1357.             do_prints(FALSE);
  1358.             }
  1359.  
  1360.           } while (pref);
  1361.  
  1362.         do_prints(FALSE);
  1363.         new_line();
  1364.         } while ((pid_ptr = pid_ptr->alpha_lnk) != NULL);
  1365.       }
  1366.    }  /* end for(i... */
  1367.  
  1368.    echo('\n');
  1369.    echo(FF);
  1370. }
  1371.  
  1372. /*************************************************************************/
  1373. void do_prints(int pparm)
  1374. {
  1375.   if (prt_line[0] != '\0')
  1376.     {
  1377.     /* print file line, screen line, etc. */
  1378.     if (do_outfile)
  1379.       {
  1380.       if ((fprintf(f_list_file, prt_line)) == ERROR)
  1381.         { list_err(3); }
  1382.       }
  1383.  
  1384.     if (do_screen)
  1385.       { printf(prt_line); }
  1386.     else
  1387.       {
  1388.       if (pparm)
  1389.         {
  1390.     if (linum % 60 == 1)            /* sixty dots per line. */
  1391.           {
  1392.       if (vline >= 24)              /* if at bottom line of screen; */
  1393.             {
  1394.         clr_scrn(base_row+1, 24);   /* clear screen and position at line 2. */
  1395.             vline++;
  1396.             }
  1397.           set_csr(vline++, 0);
  1398.       printf("\n<%d> ", linum);
  1399.           }
  1400.     printf(".");
  1401.         fflush(stdout);
  1402.         }
  1403.       }
  1404.  
  1405.     if (do_lprint)
  1406.       { lprintr(prt_line); }
  1407.  
  1408.     /* Clear the print line. */
  1409.     prt_line[0] = '\0';
  1410.     }
  1411. }
  1412.  
  1413. /*************************************************************************/
  1414.  
  1415. void print_header()
  1416. {
  1417.   if (pagno++ != 0)
  1418.     {
  1419.     echo('\n');
  1420.     echo(FF);
  1421.     }
  1422.  
  1423.   /* _makepath(current_file, wdrive, wdir, wfname, wext); */
  1424.   sprintf(prt_line, "%s  %s  %s    %s  Page %d", xcx_name, glbl_file, current_file, pdate, pagno);
  1425.  
  1426.   /* print file line, screen line, etc. */
  1427.   if (do_screen)
  1428.     {
  1429.     clr_scrn(base_row, 25);
  1430.     set_csr(vline++, 0);
  1431.     }
  1432.  
  1433.   do_prints(FALSE);
  1434.   echo('\n');
  1435.   paglin = 3;
  1436.   new_line();            /* number first line on next page. */
  1437. }
  1438.  
  1439. /*************************************************************************/
  1440.  
  1441. void new_line()
  1442. {
  1443.   echo('\n');
  1444.   if (++paglin >= LINES_PER_PAGE)
  1445.     { print_header(); }
  1446.   else
  1447.     {
  1448.     /* no line numbers when doing xref table. */
  1449.     if (do_numbrs)
  1450.       {
  1451.       if (!prt_ref)
  1452.         {
  1453.         sprintf(prt_line, "%-4d %4d: ", ++linum, ++edtnum);
  1454.         if (do_screen)
  1455.           {
  1456.           if (vline >= 24)
  1457.             {
  1458.         clr_scrn(base_row+1, 24);
  1459.             vline++;
  1460.             }
  1461.           set_csr(vline++, 0);
  1462.           }
  1463.  
  1464.     do_prints(TRUE);
  1465.         }  /* end if (!prt_ref...) */
  1466.       }  /*  end if (nparm...) */
  1467.     }  /* end else ... */
  1468. }
  1469.  
  1470. /*************************************************************************/
  1471.  
  1472. void echo(char c)
  1473. {
  1474.    static int col = 11;
  1475.    short i;
  1476.  
  1477.    if(do_echo)
  1478.      {
  1479.      echochar(c);
  1480.  
  1481.      if (c == '\n' )
  1482.        { col = 11; }
  1483.      else if (++col > maxcol )
  1484.        {
  1485.        col = 11;
  1486.        paglin++;
  1487.        echochar('\n');
  1488.        for (i = 1; i <= 11; i++)
  1489.           { echochar(' '); }
  1490.        }
  1491.      }  /* end if(do_echo ... */
  1492. }
  1493.  
  1494. void echochar(char c)
  1495. {
  1496.    if (do_outfile)
  1497.      {
  1498.      if ((fprintf(f_list_file, "%c", c)) == ERROR)
  1499.        { list_err(1); }
  1500.      }
  1501.  
  1502.    if (do_screen)
  1503.      { printf("%c", c); }
  1504.  
  1505.    if (do_lprint)
  1506.      {
  1507.      prt_line[0] = c;
  1508.      prt_line[1] = '\0';
  1509.      lprintr(prt_line);
  1510.      }
  1511. }
  1512.  
  1513. /*************************************************************************/
  1514.  
  1515. /* Print using direct call to the system BIOS, */
  1516. void lprintr(char *buffer)
  1517. {
  1518.     char *p;
  1519.     int  key;
  1520.     union REGS inregs, outregs;
  1521.  
  1522.     /*
  1523.     Check for printer available and ready.
  1524.     Fail if any error bit is on or if a selected operation bit is off.
  1525.     */
  1526.     FOREVER
  1527.       {
  1528.       inregs.h.ah = 0x2;              /* get printer status. */
  1529.       inregs.x.dx = LPT1;
  1530.       int86( 0x17, &inregs, &outregs );
  1531.       if ((outregs.h.ah & 0x29)   ||  /* 1 = out-of-paper, i/o-error, time-out */
  1532.           !(outregs.h.ah & 0x80)  ||  /* 1 = not-busy                          */
  1533.           !(outregs.h.ah & 0x10) )    /* 1 = selected                          */
  1534.         {
  1535.         /* Compensate for slow printer response...     */
  1536.         /* Wait one second and then try printer again. */
  1537.         pause((clock_t) 1000);
  1538.         inregs.h.ah = 0x2;
  1539.         inregs.x.dx = LPT1;
  1540.         int86( 0x17, &inregs, &outregs );
  1541.         if ((outregs.h.ah & 0x29)    ||
  1542.              !(outregs.h.ah & 0x80)  ||
  1543.              !(outregs.h.ah & 0x10))
  1544.           {
  1545.           fflush(stdin);                    /* Clear keyboard buffer. */
  1546.           printf("\nPrinter not ready...\n");
  1547.           printf("\nPress 'Q' to quit - XREF will not be printed\n");
  1548.  
  1549.           /* Cannot redirect if a list file already assigned. */
  1550.           if (list_file[0] == '\0')
  1551.             { printf("              'D' to redirect to 'XCXREF.LST' disk file\n"); }
  1552.  
  1553.           printf("               'R' to retry the printer.\n");
  1554.           key = getche();
  1555.           switch(toupper(key))
  1556.             {
  1557.             /* Redirect printer output to XCXREF.LST file */
  1558.             /* and close printer.                       */
  1559.             case 'D': if (list_file[0] == '\0')
  1560.                         {
  1561.                         if ((openlist(xname)) == 0)
  1562.                           {
  1563.                           do_lprint = FALSE;
  1564.                           return;
  1565.                           }
  1566.                         else
  1567.                           { logit(103); }
  1568.                         }
  1569.                       break;
  1570.  
  1571.             /* Quit - close printer. */
  1572.             case 'Q': do_lprint = FALSE;
  1573.                       return;
  1574.  
  1575.             /* Retry printer. */
  1576.             case 'R': break;
  1577.  
  1578.             /* Retry printer. */
  1579.             default:  break;
  1580.             }  /* end switch ... */
  1581.           }  /* end if ((outregs... ) */
  1582.         }
  1583.  
  1584.       /* Printer is ready. */
  1585.       break;
  1586.  
  1587.       }  /* end FOREVER... */
  1588.  
  1589.     /* Output a string to the printer using DOS function 0x5. */
  1590.     for (p = buffer; *p != 0; p++)
  1591.       { bdos(0x05, *p, 0); }
  1592.  
  1593.     /* Clear print line buffer. */
  1594.     buffer[0] = '\0';
  1595. }
  1596.  
  1597. /*************************************************************************/
  1598.  
  1599. /* Position the cursor using direct call to the system BIOS, */
  1600. /* vrow 0, vcol 0 == upper left.                             */
  1601. void set_csr(short vrow, short vcol)
  1602. {
  1603.     union REGS inregs, outregs;
  1604.  
  1605.     inregs.h.dh = vrow;               /* set row register. */
  1606.     inregs.h.dl = vcol;               /* set col register. */
  1607.     inregs.x.bx = 0;                  /* page number       */
  1608.     inregs.h.ah = 2;                  /* bios position cursor */
  1609.     int86(0x10, &inregs, &outregs);
  1610. }
  1611.  
  1612. /*************************************************************************/
  1613.  
  1614. /* Clear the screen using direct call to the system BIOS, */
  1615. /* Be CAREFUL, no validation of row/col is done.          */
  1616. /* Use crow  = 0          and                             */
  1617. /*     nrows = 25 to clear the entire screen.             */
  1618. void clr_scrn(short crow, short nrows)
  1619. {
  1620.     union REGS inregs, outregs;
  1621.  
  1622.     inregs.h.ch = crow;               /* upper left  corner row  */
  1623.     inregs.h.cl = 0;                  /*                    col  */
  1624.     inregs.h.dh = crow + nrows;       /* lower right corner row  */
  1625.     inregs.h.dl = 79;                 /*                    col  */
  1626.     if (crow == 0 && nrows == 25)
  1627.       { inregs.h.al = 0; }            /* indicates entire screen */
  1628.     else
  1629.       { inregs.h.al = nrows; }
  1630.  
  1631.     inregs.h.ah = 6;                  /* scroll page up          */
  1632.     inregs.x.bx = 0;                  /* page number             */
  1633.     int86(0x10, &inregs, &outregs);
  1634.     vline = base_row;
  1635. }
  1636.  
  1637. /*************************************************************************/
  1638.  
  1639. /* Pause for a specified number of microseconds. */
  1640. void pause(clock_t mcount)
  1641. {
  1642.     clock_t mtime;
  1643.  
  1644.     mtime = mcount + clock();
  1645.     while (mtime > clock());
  1646. }
  1647.  
  1648. /*************************************************************************/
  1649.  
  1650. int openlist(char *lname)
  1651. {
  1652.   if ((f_list_file = fopen(lname, "w")) == NULL)
  1653.     {
  1654.     sprintf(emsgs, "\nUnable to create XCXREF list file - %s\n", lname);
  1655.     printf(emsgs);
  1656.     vline += 2;
  1657.     return(-1);
  1658.     }
  1659.  
  1660.   lf_open = TRUE;
  1661.   strcpy(list_file, lname);
  1662.   return(0);
  1663. }
  1664.  
  1665. /*************************************************************************/
  1666.  
  1667. void list_err(int parm)
  1668. {
  1669.   sprintf(emsgs, "\nWrite error <%d> on XCXREF list output file - %s\n", parm, list_file);
  1670.   pexit(8);
  1671. }
  1672.  
  1673. /*************************************************************************/
  1674.  
  1675. void use_err()
  1676. {
  1677.   printf("%s  %s\n\n", xcx_name, version);
  1678.   printf("\nXCXREF: Invalid parameter specification\n\n");
  1679.   printf("Usage: xcxref <filename>... <flag(s)>\n\n");
  1680.   printf("Flags: -e            = Write program data to log file\n");
  1681.   printf("       -g            = Ignore missing files\n");
  1682.   printf("       -i            = Enable #include processing\n");
  1683.   printf("       -l            = Generate listing only - no xref\n");
  1684.   printf("       -o <outfile>  = Write output to named file\n");
  1685.   printf("       -p            = Write output to line printer LPT1\n");
  1686.   printf("       -r            = Cross-reference reserved words\n");
  1687.   printf("       -s            = Write output to video screen\n");
  1688.   printf("       -w width      = Width of output page; default = 78\n");
  1689.   printf("                                             maximum = 150\n");
  1690.   printf("Flags MUST FOLLOW all input file names");
  1691.   pexit(99);
  1692. }
  1693.  
  1694. /*************************************************************************/
  1695.  
  1696. /* Display program data only if flag is on. */
  1697.  
  1698. void logit(short parm)
  1699. {
  1700.   if (log_on)
  1701.     {
  1702.     fprintf(lgfile, emsgs);
  1703.     emsgs[0] = '\0';
  1704.     fprintf(lgfile, "XCXREF program data...ID# %d\n", parm);
  1705.     fprintf(lgfile, "sizesof  id_blk = %d; rf_blk = %d; alpha_hdr = %d\n",
  1706.              (sizeof(struct id_blk)), (sizeof(struct rf_blk)), (sizeof(struct alpha_hdr)));
  1707.     fprintf(lgfile, "id_blk count - %d; rf_blk count %d\n", id_count, rf_count);
  1708.     fprintf(lgfile, "Line nbr - %d; Edit line nbr - %d\n", linum, edtnum);
  1709.     fprintf(lgfile, "Page nbr - %d; Unique id cnt - %d; Alpha cnt - %d\n", pagno, id_cnt, al_count);
  1710.     fprintf(lgfile, "Nbr of conflict hits - %d; File level - %d\n", hash_hits, file_level);
  1711.     fprintf(lgfile, "Page line counter - %d; Right col margin - %d\n", paglin, maxcol);
  1712.     fprintf(lgfile, "Active file - %s\n", current_file);
  1713.     fprintf(lgfile, "List file - %s\n", list_file);
  1714.     fprintf(lgfile, "Gbl file - %s\n", glbl_file);
  1715.  
  1716.     fprintf(lgfile, "wdrive:dir:fname.ext %s%s%s%s\n", wdrive, wdir, wfname, wext);
  1717.     fprintf(lgfile, "adrive1:dir1 %s%s\n", adrive1, adir1);
  1718.     fprintf(lgfile, "adrive2:dir2 %s%s\n", adrive2, adir2);
  1719.     fprintf(lgfile, "adrive3:dir3 %s%s\n", adrive3, adir3);
  1720.     fprintf(lgfile, "adrive4:dir4 %s%s\n", adrive4, adir4);
  1721.  
  1722.     fprintf(lgfile,"qfl_open %d; lf_open %d; infl_open %d; do_res_wds %d; ignore %d\n",
  1723.             qfl_open, lf_open, infl_open, do_res_wds, ignore);
  1724.     fprintf(lgfile,"do_outfile %d; do_screen %d; list_only %d; do_includes %d; log_on %d\n",
  1725.                         do_outfile, do_screen, list_only, do_includes, log_on);
  1726.     fprintf(lgfile,"do_lprint %d; do_numbrs %d; qfirst %d; file_queue %d\n",
  1727.             do_lprint, do_numbrs, qfirst, file_queue);
  1728.     }
  1729. }
  1730.  
  1731. /*************************************************************************/
  1732.  
  1733. /* Error exit from program */
  1734.  
  1735. void pexit(int xparm)
  1736. {
  1737.   printf(emsgs);
  1738.  
  1739.   /* Write console message and log data. */
  1740.   if (xparm < 100)
  1741.     { logit(xparm); }
  1742.  
  1743.   /* if log on print any partial xref table. */
  1744.   if (log_on)
  1745.     {
  1746.     do_print_xrefs();
  1747.     logit(100);
  1748.     fclose(lgfile);
  1749.     }
  1750.  
  1751. /*
  1752.   if (infl_open)
  1753.     { fclose(infile); }
  1754. */
  1755.  
  1756.   if (lf_open)
  1757.     { fclose(f_list_file); }
  1758.  
  1759.   exit(xparm);
  1760. }
  1761.