home *** CD-ROM | disk | FTP | other *** search
/ The Developer Connection…ice Driver Kit for OS/2 3 / DEV3-D1.ISO / source / util2src / grep.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-09-30  |  25.2 KB  |  638 lines

  1. /*============================================================================*
  2.  * main() module: grep.c - Get regular expression and print.      OS/2 2.0
  3.  *
  4.  * (C)Copyright IBM Corporation, 1991, 1992, 1993         Brian E. Yoder
  5.  *
  6.  * The grep (get regular expression and print) command is an adaptation of
  7.  * the AIX grep command.  It searches one or more files for a pattern.
  8.  *
  9.  * See grep.doc for a complete description.
  10.  *
  11.  * 10/28/91 - Created for DOS and OS/2.
  12.  * 11/04/91 - Initial version.
  13.  * 07/24/92 - Changed "rt" to "r" for fopen(). C Set/2 doesn't allow "rt".
  14.  * 09/28/93 - In scanfile(): If fopen fails, print error msg to stderr.
  15.  * 09/30/93 - Use functions in lbuf.c to allow binary files to be scanned.
  16.  *============================================================================*/
  17.  
  18. static char copr[] = "(c)Copyright IBM Corporation, 1993.  All rights reserved.";
  19.  
  20. #include <stdio.h>
  21. #include <stdlib.h>
  22. #include <fcntl.h>
  23. #include <memory.h>
  24. #include <sys/types.h>
  25.  
  26. #include "util.h"
  27.  
  28. #define MAXLINE 512                 /* Maximum line length supported */
  29.  
  30. static  char     buff[2048+1];      /* Buffer for output filenames */
  31.  
  32. #define ELEN     1024               /* Length of compiled pattern buffer */
  33. static  char     cexpr[ELEN+1];     /* Buffer for expanded/compiled pattern */
  34.  
  35. /*----------------------------------------------------------------------------*
  36.  * Structure for grep run-time control information
  37.  *----------------------------------------------------------------------------*/
  38.  
  39. typedef struct {
  40.  
  41.      int        ExactCase;          /* Exact case match? */
  42.      int        StdIn;              /* Reading from stdin? */
  43.      int        MatchType;          /* What to display: RMATCH or NO_RMATCH */
  44.  
  45.      int        ShowFilesOnly;      /* Show only filenames? */
  46.      int        ShowCountOnly;      /* Show only count of matching lines? */
  47.      int        ShowLines;          /* Show lines? */
  48.  
  49.      int        PrefixFilename;     /* Prefix output lines with filename? */
  50.      int        PrefixLineNumber;   /* Prefix output lines with line number? */
  51.  
  52.      ulong      mcount;             /* Match count across all files */
  53. //   char      *iobuff;             /* Pointer to I/O buffer for reading */
  54.      LBUF      *istream;            /* Pointer to buffered input stream */
  55.      uint       iolen;              /* Length of io buffer */
  56.  
  57. } RUNINFO;
  58.  
  59. static RUNINFO rinfo = {            /* Initial settings of run-time info */
  60.  
  61.      TRUE  , FALSE , RMATCH ,
  62.      FALSE , FALSE , TRUE  ,
  63.      TRUE  , FALSE ,
  64.  
  65.      0L, NULL, 16384                     /* 8192, 16384 : Multiples of 512 */
  66. };
  67.  
  68. /*============================================================================*
  69.  * Define internal functions to allow forward access
  70.  *============================================================================*/
  71.  
  72. static void   syntax    ( void );
  73.  
  74. static int  expandpat   ( char *expr, char *rexpr, char *erexpr, char **endp );
  75. static void scanfile    ( char *fname, LBUF *istream,
  76.                           char *cexpr, RUNINFO *rinfo );
  77. static void showline    ( char *fname, ulong linenum, char *fline,
  78.                           RUNINFO  *rinfo );
  79.  
  80. /*============================================================================*
  81.  * Main Program Entry Point
  82.  *============================================================================*/
  83.  
  84. main(argc, argv)
  85.  
  86. int argc;                           /* arg count */
  87. char **argv;                        /* arg pointers */
  88.  
  89. {
  90.   int     rc;                       /* Return code storage */
  91.   long    lrc;                      /* Long return code */
  92.   char   *flagstr;                  /* Pointer to string of flags */
  93.  
  94.   char   *pattern;                  /* Pattern, as entered by user */
  95.   char   *next_eexpr;               /* Returned by rscan(), but not used */
  96.   char   *next_cexpr;
  97.  
  98.   SLPATH *speclist;                 /* Pointer to linked list of path specs */
  99.   ushort  mflags;                   /* Match flags for slrewind() */
  100.   ushort  recurse;                  /* Recursive directory descent? */
  101.  
  102.   FINFO  *fdata;                    /* Pointers returned by slmatch() */
  103.   SLPATH *pdata;
  104.   SLNAME *ndata;
  105.  
  106.   char    eexpr[ELEN+1];            /* Buffer for expanded pattern */
  107.  
  108.  /*---------------------------------------------------------------------------*
  109.   * Process flags from the command line, if any.  Store information from the
  110.   * flags (except for -s: recurse subdirectories) in the rinfo structure.
  111.   *---------------------------------------------------------------------------*/
  112.  
  113.   recurse = FALSE;                  /* Initially: don't descend subdirectories */
  114.  
  115.   argc--;                           /* Ignore 1st argument (program name) */
  116.   argv++;
  117.  
  118.   while (argc != 0)                 /* For each argument: */
  119.   {
  120.      flagstr = *argv;                  /* Point 'flagstr' to argument */
  121.      if (*flagstr != '-')              /* If it doesn't begin with '-': */
  122.         break;                              /* Then it's the first filespec */
  123.      else                              /* Otherwise: It's a set of flags: */
  124.      {
  125.         flagstr++;                          /* Point past the dash */
  126.  
  127.         if (*flagstr == '?')                /* If help is requested: */
  128.            syntax();                             /* Show help */
  129.  
  130.         while (*flagstr != '\0')            /* For each character in flag string: */
  131.         {
  132.            switch (*flagstr)
  133.            {
  134.               case '?':                          /* If we see ? in the list: */
  135.                  syntax();                       /* then user wants help */
  136.                  return(2);
  137.                  break;
  138.  
  139.               case 's':
  140.               case 'S':
  141.                  recurse = TRUE;
  142.                  break;
  143.  
  144.               case 'i':
  145.               case 'I':
  146.               case 'y':
  147.               case 'Y':
  148.                  rinfo.ExactCase = FALSE;
  149.                  break;
  150.  
  151.               case 'c':
  152.               case 'C':
  153.                  rinfo.ShowCountOnly = TRUE;
  154.                  rinfo.ShowFilesOnly = FALSE;
  155.                  rinfo.ShowLines = FALSE;
  156.                  break;
  157.  
  158.               case 'l':
  159.               case 'L':
  160.                  rinfo.ShowFilesOnly = TRUE;
  161.                  rinfo.ShowCountOnly = FALSE;
  162.                  rinfo.ShowLines = FALSE;
  163.                  break;
  164.  
  165.               case 'n':
  166.               case 'N':
  167.                  rinfo.PrefixLineNumber = TRUE;
  168.                  break;
  169.  
  170.               case 'v':
  171.               case 'V':
  172.                  rinfo.MatchType = NO_RMATCH;
  173.                  break;
  174.  
  175.               default:
  176.                  fprintf(stderr, "grep: Invalid flag '%c'.\n",
  177.                     *flagstr);
  178.                  exit(2);
  179.                  break;
  180.            }
  181.            flagstr++;                            /* Check next character */
  182.         } /* end of while stmt to process all flag characters in current argv */
  183.  
  184.         argc--;                             /* Done with flags: Discard them */
  185.         argv++;
  186.      } /* end of if stmt to process flag string */
  187.   } /* end of while stmt to process all flag strings */
  188.  
  189.   if (argc < 1) syntax();            /* If no arguments: Display syntax */
  190.  
  191.  /*---------------------------------------------------------------------------*
  192.   * Allocate a read buffer to be used by fgets()
  193.   *---------------------------------------------------------------------------*/
  194.  
  195.   rinfo.istream = newlbuf(rinfo.iolen);
  196.   if (rinfo.istream == NULL)
  197.   {
  198.      fprintf(stderr, "grep: Cannot allocate memory for istream.\n");
  199.      exit(2);
  200.   }
  201.  
  202.  /*---------------------------------------------------------------------------*
  203.   * Compile pattern and store pointer to it.  If we are not matching exact
  204.   * case, then we need to first convert the pattern to uppercase before we
  205.   * compile it.
  206.   *---------------------------------------------------------------------------*/
  207.  
  208.   pattern = *argv;                  /* Point to user-entered pattern */
  209.   argc--;                           /* Bump args past pattern */
  210.   argv++;
  211.  
  212.   if (rinfo.ExactCase == FALSE)     /* If matches are case-insensitive: */
  213.      strupr(pattern);                    /* Convert pattern to uppercase */
  214.  
  215.   rc = expandpat(pattern, eexpr,    /* Expand the pattern */
  216.                  &eexpr[ELEN+1],
  217.                  &next_eexpr);
  218.   if (rc != 0)
  219.   {
  220.      fprintf(stderr, "grep: Cannot expand pattern into regular expression.\n");
  221.      return(2);
  222.   }
  223.  
  224.   rc = rcompile(eexpr, cexpr,       /* Compile the regular expression */
  225.                 &cexpr[ELEN+1],
  226.                 '\0', &next_cexpr);
  227.   if (rc != 0)
  228.   {
  229.      fprintf(stderr, "grep: Cannot compile pattern: %s\n",
  230.         rcmpmsg(rc));
  231.      return(2);
  232.   }
  233.  
  234. //printf("User-entered pattern: %s\n", pattern);   /* Test */
  235. //printf("Compiled expression:  %s\n", eexpr);
  236. //return(0);
  237.  
  238.  /*---------------------------------------------------------------------------*
  239.   * If there are no filespecs, then scan standard input
  240.   *---------------------------------------------------------------------------*/
  241.  
  242.   if (argc == 0)                    /* If no file specifications present: */
  243.   {
  244.      rinfo.StdIn = TRUE;                 /* Set standard input flag */
  245.      rinfo.PrefixFilename = FALSE;       /* There's no filename to prefix! */
  246.      scanfile(NULL, rinfo.istream,       /* Scan stdin for compiled expression */
  247.               cexpr, &rinfo);
  248.      goto ScanDone;                      /* Bypass filespec scanning */
  249.   }
  250.  
  251.  /*---------------------------------------------------------------------------*
  252.   * Process file specifications and build specification list
  253.   *---------------------------------------------------------------------------*/
  254.  
  255.   rc = slmake(&speclist, TRUE, TRUE, argc, argv);
  256.  
  257.   if (rc != 0)                      /* If there was an error: */
  258.   {                                 /* Analyze rc, show msg, and return */
  259.      if (rc == REGX_MEMORY)
  260.         fprintf(stderr, "Out of memory while processing '%s'.\n",
  261.            slerrspec());
  262.      else
  263.         fprintf(stderr, "Invalid filespec: '%s'.\n",
  264.            slerrspec());
  265.  
  266.      return(2);
  267.   }
  268.  
  269.  /*---------------------------------------------------------------------------*
  270.   * Attempt to match files to the specification list
  271.   *---------------------------------------------------------------------------*/
  272.  
  273.   mflags = 0;                       /* Set list of files to match to include: */
  274.   mflags = mflags | SL_NORMAL;           /* Ordinary files */
  275. // no!              SL_HIDDEN |          /* Hidden files */
  276. // no!              SL_SYSTEM;           /* System files */
  277.  
  278.   slrewind(speclist, mflags, recurse);/* Initialize slmatch() */
  279.   for (;;)                          /* Loop to find all matching files: */
  280.   {
  281.      fdata = slmatch(&pdata,             /* Get next matching file */
  282.                      &ndata);
  283.      if (fdata == NULL)                  /* If none: */
  284.         break;                                /* Done */
  285.  
  286.      strcpy(buff, pdata->path);          /* Store path portion of filename */
  287.      pathcat(buff, fdata->Fname);        /* Add name to end of path string */
  288.  
  289.      scanfile(buff, rinfo.istream,       /* Scan file for compiled expression */
  290.               cexpr, &rinfo);
  291.  
  292.   } /* end of for(;;) loop to find matching files */
  293.  
  294.  /*---------------------------------------------------------------------------*
  295.   * Display (on stderr) all filespecs that had no matching files
  296.   *
  297.   * For grep, we don't currently care about files that we couldn't match.
  298.   * If I find out that the AIX version does, then I will too.
  299.   *---------------------------------------------------------------------------*/
  300.  
  301. //lrc = slnotfound(speclist);       /* Display path\names w/no matching files */
  302. //if (lrc == 0L)                    /* If all fspecs were matched: */
  303. //   return(0);                          /* Return successfully */
  304. //else                              /* Otherwise:  One or more not found: */
  305. //   return(2);                          /* Return with error */
  306.  
  307.  
  308.  /*---------------------------------------------------------------------------*
  309.   * Done: See if we have a count to display
  310.   *---------------------------------------------------------------------------*/
  311.  
  312. ScanDone:
  313.   if (rinfo.ShowCountOnly == TRUE)
  314.      printf("%lu\n", rinfo.mcount);
  315.  
  316.   if (rinfo.mcount == 0L)           /* If no matches were found: return 1 */
  317.      return(1);
  318.  
  319.   return(0);                        /* Matches were found: return 0 */
  320.  
  321. } /* end of main() */
  322.  
  323. /*============================================================================*
  324.  * syntax() - Display command syntax and exit to operating system!
  325.  *============================================================================*/
  326. static void syntax()
  327. {
  328.   fprintf(stderr, "\n");
  329.   fprintf(stderr, "Usage: grep [-flags] \"pattern\" [fspec ...]\n");
  330.   fprintf(stderr, "\n");
  331.   fprintf(stderr, "The grep command searches the specified files for the \"pattern\"\n");
  332.   fprintf(stderr, "and writes lines that contain the pattern to standard output.\n");
  333.   fprintf(stderr, "If no files are listed, it reads lines from standard input.\n");
  334.   fprintf(stderr, "\n");
  335.   fprintf(stderr, "Flags:\n");
  336.   fprintf(stderr, "   c  Display only a count of matching lines.\n");
  337.   fprintf(stderr, "   i  Ignore case when making comparisons.\n");
  338.   fprintf(stderr, "   l  List just the names of files (once) with matching lines.\n");
  339.   fprintf(stderr, "   n  Preceed each line with the line number, along with the file name.\n");
  340.   fprintf(stderr, "   s  Descend subdirectories, also.\n");
  341.   fprintf(stderr, "   v  Display lines that don't contain the pattern.\n");
  342.   fprintf(stderr, "   y  Ignore case when making comparisons.\n");
  343.   exit(1);
  344. }
  345.  
  346. /*============================================================================*
  347.  * expandpat() - Expand pattern into a regular expression.
  348.  *
  349.  * REMARKS:
  350.  *   This subroutine creates a regular expression from the user-entered
  351.  *   pattern.  It is more convenient to enter repeat factors within
  352.  *   braces without having to escape the braces.  However, the rcompile()
  353.  *   subroutine in rxpm.c requires braces in {m.n} to be escaped and
  354.  *   otherwise treats braces as-is.
  355.  *
  356.  * RETURNS:
  357.  *   0, if successful.
  358.  *   REGX_FULL, if the expanded expression won't fit in the buffer.
  359.  *
  360.  *   If successful, 'endp' contains a pointer to the location that is one byte
  361.  *   past the null terminating byte of the expanded expression.  If error,
  362.  *   then 'endp' contains a pointer to 'rexpr'.
  363.  *============================================================================*/
  364.  
  365. #define STORE(ch) if (to >= erexpr) return(REGX_FULL); *to++ = ch;
  366.  
  367. static int expandpat(
  368.  
  369. char     *expr,                    /* Pointer to user-entered pattern */
  370. char     *rexpr,                   /* Pointer to expansion buffer */
  371. char     *erexpr,                  /* Pointer past end of expansion buffer */
  372. char    **endp )                   /* Pointer to loc. in which to store pointer */
  373.                                    /* to byte just past expanded expression */
  374.  
  375. {
  376.   char *from;                      /* Character pointers used to expand the */
  377.   char *to;                        /*    expression */
  378.  
  379.   from = expr;                     /* Point to start of each expression */
  380.   to = rexpr;
  381.  
  382.   *endp = rexpr;                   /* Initialize 'endp' to 'rexpr */
  383.  
  384.   if (rexpr >= erexpr)             /* If end pointer doesn't allow at least */
  385.      return(REGX_FULL);            /* one character: return w/error */
  386.  
  387.   while (*from != '\0')            /* For each character in 'expr': */
  388.   {
  389.      switch (*from)                    /* Handle special cases: */
  390.      {
  391.         case '{':                            /* Put '\' before */
  392.         case '}':                            /* these characters: */
  393.            STORE('\\');
  394.            STORE(*from);
  395.            break;
  396.  
  397.         case '\\':                           /* If escape character found: */
  398.            from++;                                /* Point to next character */
  399.            switch (*from)
  400.            {
  401.               case '{':                           /* Store these characters */
  402.               case '}':                           /* without the escape char: */
  403.                  STORE(*from);
  404.                  break;
  405.  
  406.               default:                            /* Otherwise: store the esc */
  407.                  STORE('\\');                     /* character, too */
  408.                  STORE(*from);
  409.                  break;
  410.            }
  411.            break;
  412.  
  413.         default:                             /* For all other characters: */
  414.            STORE(*from);                          /* Just copy character */
  415.            break;
  416.      }
  417.  
  418.      from++;                           /* Bump pointer to next source char */
  419.   }
  420.  
  421.   STORE('\0');                     /* Store null after the expanded expression */
  422.   *endp = to;                      /* Store pointer past the null byte */
  423.   return(0);
  424. }
  425.  
  426. /*============================================================================*
  427.  * scanfile() - Scan a file for a regular expression.
  428.  *
  429.  * REMARKS:
  430.  *   This subroutine scans the specified file for the compiled regular
  431.  *   expression.  If the StdIn flag is TRUE, then it scans standard input
  432.  *   and ignores the filename.
  433.  *
  434.  *   This subroutine calls showline() to actually display the results
  435.  *   of the scan.  It also updates rinfo->mcount with the number of
  436.  *   matching/non-matching lines (as appropriate) that were found.
  437.  *
  438.  * RETURNS:
  439.  *   Nothing, for now.
  440.  *
  441.  * NOTE:
  442.  *   This subroutine calls lgets() from lbuf.c to get lines from the file.
  443.  *   The setlbuf() closes any previously opened file so we don't have to
  444.  *   explicitly close the file as we did when we called fgets().
  445.  *============================================================================*/
  446.  
  447. static void scanfile(
  448.  
  449. char     *fname,                   /* Pointer to file name */
  450. LBUF     *istream,                  /* Pointer to buffered input stream */
  451. char     *cexpr,                   /* Pointer to compiled regular expression */
  452. RUNINFO  *rinfo )                  /* Pointer to run-time information */
  453.  
  454. {
  455.   int     rc;                      /* Return code storage */
  456.  
  457.   int     fd;                       /* File descriptor */
  458.  
  459.   ulong   linenum;                 /* Current line's line number */
  460.   int     newline;                 /* Did previous line end with \n ? */
  461.   char   *line;                    /* Pointer to line */
  462.   char   *end;                     /* Pointer to end of line */
  463.  
  464.   int     match;                   /* Result from rscan() */
  465.   char   *mstart;                  /* From rscan(): Pointer to start of match */
  466.   char   *mend;                    /*               Pointer past end of match */
  467.   int     found;                   /* TRUE, if file contains a match */
  468.  
  469.   char    fline[MAXLINE+1];        /* Original line from file */
  470.   char    uline[MAXLINE+1];        /* Uppercased line, if case-insensitive matches */
  471.  
  472.  /*---------------------------------------------------------------------------*
  473.   * Set up buffered stream, opened for reading
  474.   *---------------------------------------------------------------------------*/
  475.  
  476.   if (rinfo->StdIn == TRUE)        /* If reading from standard input: */
  477.   {
  478.      fd = 0;                            /* Store file handle of stdin */
  479.      fname = "";                        /* Set file name to empty string */
  480.   }
  481.   else                             /* Otherwise: */
  482.   {
  483.      fd = open(fname, O_RDONLY | O_BINARY); /* Open the file in binary: */
  484.      if (fd == -1)                      /* lgets() will handle it ok */
  485.      {
  486.         fprintf(stderr, "grep: Cannot open file %s.\n", fname);
  487.         return;
  488.      }
  489.   }
  490.  
  491.   setlbuf(istream, fd);            /* Attach opened file to buffered stream */
  492.  
  493.  /*---------------------------------------------------------------------------*
  494.   * Scan the file for the regular expression
  495.   *---------------------------------------------------------------------------*/
  496.  
  497.   newline = TRUE;                   /* Initialize for loop */
  498.   linenum = 0L;
  499.   found = FALSE;
  500.  
  501.   for (;;)                          /* Loop to scan the file */
  502.   {
  503.     /*-------------------------------------------------------*
  504.      * Get the next chunk from the file, and update the current
  505.      * line number if the previous chunk ended with \n
  506.      *-------------------------------------------------------*/
  507.  
  508.      line = lgets(fline, MAXLINE,        /* Get next chunk from file */
  509.                   rinfo->istream);
  510.      if (line == NULL) break;            /* If no more data: quit loop */
  511.  
  512.      if (newline == TRUE)                /* If previous line ended with \n: */
  513.      {
  514.         linenum++;                            /* We are on the next line */
  515.         newline = FALSE;                      /* Reset the flag */
  516.      }
  517.  
  518.      end = line + strlen(line) - 1;      /* Point to last character in line */
  519.      if (*end == '\n')                   /* If last char is a new-line: */
  520.      {
  521.         *end = '\0';                          /* Get rid of it, and */
  522.         newline = TRUE;                       /* set flag */
  523.      }
  524.  
  525.     /*-------------------------------------------------------*
  526.      * If we are performing case-insensitive matches, then we
  527.      * need to uppercase the line.  We copy it to another
  528.      * buffer because if we show the line, we want to show the
  529.      * original unaltered version.
  530.      *-------------------------------------------------------*/
  531.  
  532.      if (rinfo->ExactCase == FALSE)      /* If case-insensitive matching: */
  533.      {
  534.         strcpy(uline, fline);                 /* Make a copy of the line */
  535.         strupr(uline);                        /* Convert copy to uppercase */
  536.         line = uline;                         /* Point 'line' to the copy */
  537.      }
  538.  
  539.     /*-------------------------------------------------------*
  540.      * Set 'match' to result of scanning the line for the
  541.      * compiled regular expression.
  542.      *-------------------------------------------------------*/
  543.  
  544.      match = rscan(cexpr, line, &mstart, &mend);
  545.  
  546.     /*-------------------------------------------------------*
  547.      * Depending upon 'match' and 'rinfo' flags, show
  548.      * the result of the scan on stdout, and update the
  549.      * 'rinfo->mcount' field.
  550.      *-------------------------------------------------------*/
  551.  
  552.      if (match == rinfo->MatchType)      /* If match is what we want to show: */
  553.      {
  554.         rinfo->mcount++;                      /* Update match count */
  555.         found = TRUE;                         /* We found what we wanted */
  556.         if (rinfo->ShowLines == TRUE)         /* If we're to show the line: */
  557.            showline(fname, linenum,                /* The show it */
  558.                     fline, rinfo);
  559.      }
  560.  
  561.   } /* end of for(;;) loop to scan the file */
  562.  
  563.  /*---------------------------------------------------------------------------*
  564.   * If we're only showing filenames and we found what we were looking for,
  565.   * then write the file name to stdout
  566.   *---------------------------------------------------------------------------*/
  567.  
  568.   if ((found == TRUE) &&
  569.       (rinfo->ShowFilesOnly == TRUE))
  570.           puts(fname);
  571.  
  572.  /*---------------------------------------------------------------------------*
  573.   * Done: return
  574.   *---------------------------------------------------------------------------*/
  575.  
  576.   return;
  577. }
  578.  
  579. /*============================================================================*
  580.  * showline() - Show the line.
  581.  *
  582.  * REMARKS:
  583.  *   This subroutine shows the specified line.  Depending upon the flags
  584.  *   in rinfo, various decorations are added to the lines.
  585.  *
  586.  *   We assume that, by getting called, the line is to be shown.  It is
  587.  *   a bit faster if the caller checks the rinfo->ShowLines flag and only
  588.  *   call us if it's TRUE rather than calling us for each and every line
  589.  *   and letting us check the flag.
  590.  *
  591.  * RETURNS:
  592.  *   Nothing.
  593.  *============================================================================*/
  594.  
  595. static void showline(
  596.  
  597. char     *fname,                   /* Pointer to file name */
  598. ulong     linenum,                 /* Line number */
  599. char     *fline,                   /* Pointer to line string */
  600. RUNINFO  *rinfo )                  /* Pointer to run-time information */
  601.  
  602. {
  603.   switch (rinfo->PrefixFilename)   /* Determine the prefix information to */
  604.   {                                /* show before the line itself: */
  605.      case TRUE:
  606.         switch (rinfo->PrefixLineNumber)
  607.         {
  608.            case TRUE:
  609.               printf("%s(%lu) : %s\n",         /* Filename + line number */
  610.                  fname, linenum, fline);
  611.               break;
  612.  
  613.            default:
  614.               printf("%s : %s\n",              /* Filename only */
  615.                  fname, fline);
  616.               break;
  617.         }
  618.         break;
  619.  
  620.      default:
  621.         switch (rinfo->PrefixLineNumber)
  622.         {
  623.            case TRUE:
  624.               printf("(%lu) : %s\n",           /* Linenumber only */
  625.                  linenum, fline);
  626.               break;
  627.  
  628.            default:
  629.               printf("%s\n",                   /* No prefix information */
  630.                  fline);
  631.               break;
  632.         }
  633.         break;
  634.   }
  635.  
  636.   return;
  637. }
  638.