home *** CD-ROM | disk | FTP | other *** search
/ PC-Online 1996 May / PCOnline_05_1996.bin / linux / source / a / txtutils / textutil.9 / textutil / textutils-1.9 / src / nl.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-10-24  |  14.2 KB  |  614 lines

  1. /* nl -- number lines of files
  2.    Copyright (C) 1989, 1992 Free Software Foundation, Inc.
  3.  
  4.    This program is free software; you can redistribute it and/or modify
  5.    it under the terms of the GNU General Public License as published by
  6.    the Free Software Foundation; either version 2, or (at your option)
  7.    any later version.
  8.  
  9.    This program is distributed in the hope that it will be useful,
  10.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.    GNU General Public License for more details.
  13.  
  14.    You should have received a copy of the GNU General Public License
  15.    along with this program; if not, write to the Free Software
  16.    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
  17.  
  18. /* Written by Scott Bartram (nancy!scott@uunet.uu.net)
  19.    Revised by David MacKenzie (djm@gnu.ai.mit.edu) */
  20.  
  21. #ifdef HAVE_CONFIG_H
  22. #if defined (CONFIG_BROKETS)
  23. /* We use <config.h> instead of "config.h" so that a compilation
  24.    using -I. -I$srcdir will use ./config.h rather than $srcdir/config.h
  25.    (which it would do because it found this file in $srcdir).  */
  26. #include <config.h>
  27. #else
  28. #include "config.h"
  29. #endif
  30. #endif
  31.  
  32. #include <stdio.h>
  33. #include <sys/types.h>
  34. #include <getopt.h>
  35. #include <regex.h>
  36. #include "linebuffer.h"
  37. #include "system.h"
  38. #include "version.h"
  39.  
  40. #ifndef TRUE
  41. #define TRUE   1
  42. #define FALSE  0
  43. #endif
  44.  
  45. /* Line-number formats. */
  46. enum number_format
  47. {
  48.   FORMAT_RIGHT_NOLZ,        /* Right justified, no leading zeroes.  */
  49.   FORMAT_RIGHT_LZ,        /* Right justified, leading zeroes.  */
  50.   FORMAT_LEFT            /* Left justified, no leading zeroes.  */
  51. };
  52.  
  53. /* Default section delimiter characters.  */
  54. #define DEFAULT_SECTION_DELIMITERS  "\\:"
  55.  
  56. /* Types of input lines: either one of the section delimiters,
  57.    or text to output. */
  58. enum section
  59. {
  60.   Header, Body, Footer, Text
  61. };
  62.  
  63. char *xmalloc ();
  64. char *xrealloc ();
  65. void error ();
  66.  
  67. static enum section check_section ();
  68. static int build_type_arg ();
  69. static int nl_file ();
  70. static void usage ();
  71. static void process_file ();
  72. static void proc_header ();
  73. static void proc_body ();
  74. static void proc_footer ();
  75. static void proc_text ();
  76. static void print_lineno ();
  77. static void build_print_fmt ();
  78.  
  79. /* The name this program was run with. */
  80. char *program_name;
  81.  
  82. /* Format of body lines (-b).  */
  83. static char *body_type = "t";
  84.  
  85. /* Format of header lines (-h).  */
  86. static char *header_type = "n";
  87.  
  88. /* Format of footer lines (-f).  */
  89. static char *footer_type = "n";
  90.  
  91. /* Format currently being used (body, header, or footer).  */
  92. static char *current_type;
  93.  
  94. /* Regex for body lines to number (-bp).  */
  95. static struct re_pattern_buffer body_regex;
  96.  
  97. /* Regex for header lines to number (-hp).  */
  98. static struct re_pattern_buffer header_regex;
  99.  
  100. /* Regex for footer lines to number (-fp).  */
  101. static struct re_pattern_buffer footer_regex;
  102.  
  103. /* Pointer to current regex, if any.  */
  104. static struct re_pattern_buffer *current_regex = NULL;
  105.  
  106. /* Separator string to print after line number (-s).  */
  107. static char *separator_str = "\t";
  108.  
  109. /* Input section delimiter string (-d).  */
  110. static char *section_del = DEFAULT_SECTION_DELIMITERS;
  111.  
  112. /* Header delimiter string.  */
  113. static char *header_del = NULL;
  114.  
  115. /* Header section delimiter length.  */
  116. static int header_del_len;
  117.  
  118. /* Body delimiter string.  */
  119. static char *body_del = NULL;
  120.  
  121. /* Body section delimiter length.  */
  122. static int body_del_len;
  123.  
  124. /* Footer delimiter string.  */
  125. static char *footer_del = NULL;
  126.  
  127. /* Footer section delimiter length.  */
  128. static int footer_del_len;
  129.  
  130. /* Input buffer.  */
  131. static struct linebuffer line_buf;
  132.  
  133. /* printf format string for line number.  */
  134. static char *print_fmt;
  135.  
  136. /* printf format string for unnumbered lines.  */
  137. static char *print_no_line_fmt = NULL;
  138.  
  139. /* Starting line number on each page (-v).  */
  140. static int page_start = 1;
  141.  
  142. /* Line number increment (-i).  */
  143. static int page_incr = 1;
  144.  
  145. /* If TRUE, reset line number at start of each page (-p).  */
  146. static int reset_numbers = TRUE;
  147.  
  148. /* Number of blank lines to consider to be one line for numbering (-l).  */
  149. static int blank_join = 1;
  150.  
  151. /* Width of line numbers (-w).  */
  152. static int lineno_width = 6;
  153.  
  154. /* Line number format (-n).  */
  155. static enum number_format lineno_format = FORMAT_RIGHT_NOLZ;
  156.  
  157. /* Current print line number.  */
  158. static int line_no;
  159.  
  160. /* Nonzero if we have ever read standard input. */
  161. static int have_read_stdin;
  162.  
  163. /* If non-zero, display usage information and exit.  */
  164. static int show_help;
  165.  
  166. /* If non-zero, print the version on standard output then exit.  */
  167. static int show_version;
  168.  
  169. static struct option const longopts[] =
  170. {
  171.   {"header-numbering", required_argument, NULL, 'h'},
  172.   {"body-numbering", required_argument, NULL, 'b'},
  173.   {"footer-numbering", required_argument, NULL, 'f'},
  174.   {"first-page", required_argument, NULL, 'v'},
  175.   {"page-increment", required_argument, NULL, 'i'},
  176.   {"no-renumber", no_argument, NULL, 'p'},
  177.   {"join-blank-lines", required_argument, NULL, 'l'},
  178.   {"number-separator", required_argument, NULL, 's'},
  179.   {"number-width", required_argument, NULL, 'w'},
  180.   {"number-format", required_argument, NULL, 'n'},
  181.   {"section-delimiter", required_argument, NULL, 'd'},
  182.   {"help", no_argument, &show_help, 1},
  183.   {"version", no_argument, &show_version, 1},
  184.   {NULL, 0, NULL, 0}
  185. };
  186.  
  187. void
  188. main (argc, argv)
  189.      int argc;
  190.      char **argv;
  191. {
  192.   int c, exit_status = 0;
  193.  
  194.   program_name = argv[0];
  195.   have_read_stdin = 0;
  196.  
  197.   while ((c = getopt_long (argc, argv, "h:b:f:v:i:pl:s:w:n:d:", longopts,
  198.                (int *) 0)) != EOF)
  199.     {
  200.       switch (c)
  201.     {
  202.     case 0:
  203.       break;
  204.  
  205.     case 'h':
  206.       if (build_type_arg (&header_type, &header_regex) != TRUE)
  207.         usage (2);
  208.       break;
  209.     case 'b':
  210.       if (build_type_arg (&body_type, &body_regex) != TRUE)
  211.         usage (2);
  212.       break;
  213.     case 'f':
  214.       if (build_type_arg (&footer_type, &footer_regex) != TRUE)
  215.         usage (2);
  216.       break;
  217.     case 'v':
  218.       page_start = atoi (optarg);
  219.       break;
  220.     case 'i':
  221.       page_incr = atoi (optarg);
  222.       if (page_incr < 1)
  223.         page_incr = 1;
  224.       break;
  225.     case 'p':
  226.       reset_numbers = FALSE;
  227.       break;
  228.     case 'l':
  229.       blank_join = atoi (optarg);
  230.       break;
  231.     case 's':
  232.       separator_str = optarg;
  233.       break;
  234.     case 'w':
  235.       lineno_width = atoi (optarg);
  236.       if (lineno_width < 1)
  237.         lineno_width = 1;
  238.       break;
  239.     case 'n':
  240.       switch (*optarg)
  241.         {
  242.         case 'l':
  243.           if (optarg[1] == 'n')
  244.         lineno_format = FORMAT_LEFT;
  245.           else
  246.         usage (2);
  247.           break;
  248.         case 'r':
  249.           switch (optarg[1])
  250.         {
  251.         case 'n':
  252.           lineno_format = FORMAT_RIGHT_NOLZ;
  253.           break;
  254.         case 'z':
  255.           lineno_format = FORMAT_RIGHT_LZ;
  256.           break;
  257.         default:
  258.           usage (2);
  259.           break;
  260.         }
  261.           break;
  262.         default:
  263.           usage (2);
  264.           break;
  265.         }
  266.       break;
  267.     case 'd':
  268.       section_del = optarg;
  269.       break;
  270.     default:
  271.       usage (2);
  272.       break;
  273.     }
  274.     }
  275.  
  276.   if (show_version)
  277.     {
  278.       printf ("%s\n", version_string);
  279.       exit (0);
  280.     }
  281.  
  282.   if (show_help)
  283.     usage (0);
  284.  
  285.   /* Initialize the section delimiters.  */
  286.   c = strlen (section_del);
  287.  
  288.   header_del_len = c * 3;
  289.   header_del = xmalloc (header_del_len + 1);
  290.   strcat (strcat (strcpy (header_del, section_del), section_del), section_del);
  291.  
  292.   body_del_len = c * 2;
  293.   body_del = xmalloc (body_del_len + 1);
  294.   strcat (strcpy (body_del, section_del), section_del);
  295.  
  296.   footer_del_len = c;
  297.   footer_del = xmalloc (footer_del_len + 1);
  298.   strcpy (footer_del, section_del);
  299.  
  300.   /* Initialize the input buffer.  */
  301.   initbuffer (&line_buf);
  302.  
  303.   /* Initialize the printf format for unnumbered lines. */
  304.   c = strlen (separator_str);
  305.   print_no_line_fmt = xmalloc (lineno_width + c + 1);
  306.   memset (print_no_line_fmt, ' ', lineno_width + c);
  307.   print_no_line_fmt[lineno_width + c] = '\0';
  308.  
  309.   line_no = page_start;
  310.   current_type = body_type;
  311.   current_regex = &body_regex;
  312.   build_print_fmt ();
  313.  
  314.   /* Main processing. */
  315.  
  316.   if (optind == argc)
  317.     exit_status |= nl_file ("-");
  318.   else
  319.     for (; optind < argc; optind++)
  320.       exit_status |= nl_file (argv[optind]);
  321.  
  322.   if (have_read_stdin && fclose (stdin) == EOF)
  323.     {
  324.       error (0, errno, "-");
  325.       exit_status = 1;
  326.     }
  327.   if (ferror (stdout) || fclose (stdout) == EOF)
  328.     error (1, errno, "write error");
  329.  
  330.   exit (exit_status);
  331. }
  332.  
  333. /* Process file FILE to standard output.
  334.    Return 0 if successful, 1 if not. */
  335.  
  336. static int
  337. nl_file (file)
  338.      char *file;
  339. {
  340.   FILE *stream;
  341.  
  342.   if (!strcmp (file, "-"))
  343.     {
  344.       have_read_stdin = 1;
  345.       stream = stdin;
  346.     }
  347.   else
  348.     {
  349.       stream = fopen (file, "r");
  350.       if (stream == NULL)
  351.     {
  352.       error (0, errno, "%s", file);
  353.       return 1;
  354.     }
  355.     }
  356.  
  357.   process_file (stream);
  358.  
  359.   if (ferror (stream))
  360.     {
  361.       error (0, errno, "%s", file);
  362.       return 1;
  363.     }
  364.   if (!strcmp (file, "-"))
  365.     clearerr (stream);        /* Also clear EOF. */
  366.   else if (fclose (stream) == EOF)
  367.     {
  368.       error (0, errno, "%s", file);
  369.       return 1;
  370.     }
  371.   return 0;
  372. }
  373.  
  374. /* Read and process the file pointed to by FP. */
  375.  
  376. static void
  377. process_file (fp)
  378.      FILE *fp;
  379. {
  380.   while (readline (&line_buf, fp))
  381.     {
  382.       switch ((int) check_section ())
  383.     {
  384.     case Header:
  385.       proc_header ();
  386.       break;
  387.     case Body:
  388.       proc_body ();
  389.       break;
  390.     case Footer:
  391.       proc_footer ();
  392.       break;
  393.     case Text:
  394.       proc_text ();
  395.       break;
  396.     }
  397.     }
  398. }
  399.  
  400. /* Return the type of line in `line_buf'. */
  401.  
  402. static enum section
  403. check_section ()
  404. {
  405.   if (line_buf.length < 2 || memcmp (line_buf.buffer, section_del, 2))
  406.     return Text;
  407.   if (line_buf.length == header_del_len
  408.       && !memcmp (line_buf.buffer, header_del, header_del_len))
  409.     return Header;
  410.   if (line_buf.length == body_del_len
  411.       && !memcmp (line_buf.buffer, body_del, body_del_len))
  412.     return Body;
  413.   if (line_buf.length == footer_del_len
  414.       && !memcmp (line_buf.buffer, footer_del, footer_del_len))
  415.     return Footer;
  416.   return Text;
  417. }
  418.  
  419. /* Switch to a header section. */
  420.  
  421. static void
  422. proc_header ()
  423. {
  424.   current_type = header_type;
  425.   current_regex = &header_regex;
  426.   if (reset_numbers)
  427.     line_no = page_start;
  428.   putchar ('\n');
  429. }
  430.  
  431. /* Switch to a body section. */
  432.  
  433. static void
  434. proc_body ()
  435. {
  436.   current_type = body_type;
  437.   current_regex = &body_regex;
  438.   putchar ('\n');
  439. }
  440.  
  441. /* Switch to a footer section. */
  442.  
  443. static void
  444. proc_footer ()
  445. {
  446.   current_type = footer_type;
  447.   current_regex = &footer_regex;
  448.   putchar ('\n');
  449. }
  450.  
  451. /* Process a regular text line in `line_buf'. */
  452.  
  453. static void
  454. proc_text ()
  455. {
  456.   static int blank_lines = 0;    /* Consecutive blank lines so far. */
  457.  
  458.   switch (*current_type)
  459.     {
  460.     case 'a':
  461.       if (blank_join > 1)
  462.     {
  463.       if (line_buf.length || ++blank_lines == blank_join)
  464.         {
  465.           print_lineno ();
  466.           blank_lines = 0;
  467.         }
  468.       else
  469.         printf (print_no_line_fmt);
  470.     }
  471.       else
  472.     print_lineno ();
  473.       break;
  474.     case 't':
  475.       if (line_buf.length)
  476.     print_lineno ();
  477.       else
  478.     printf (print_no_line_fmt);
  479.       break;
  480.     case 'n':
  481.       printf (print_no_line_fmt);
  482.       break;
  483.     case 'p':
  484.       if (re_search (current_regex, line_buf.buffer, line_buf.length,
  485.              0, line_buf.length, (struct re_registers *) 0) < 0)
  486.     printf (print_no_line_fmt);
  487.       else
  488.     print_lineno ();
  489.       break;
  490.     }
  491.   fwrite (line_buf.buffer, sizeof (char), line_buf.length, stdout);
  492.   putchar ('\n');
  493. }
  494.  
  495. /* Print and increment the line number. */
  496.  
  497. static void
  498. print_lineno ()
  499. {
  500.   printf (print_fmt, line_no);
  501.   line_no += page_incr;
  502. }
  503.  
  504. /* Build the printf format string, based on `lineno_format'. */
  505.  
  506. static void
  507. build_print_fmt ()
  508. {
  509.   /* 12 = 10 chars for lineno_width, 1 for %, 1 for \0.  */
  510.   print_fmt = xmalloc (strlen (separator_str) + 12);
  511.   switch (lineno_format)
  512.     {
  513.     case FORMAT_RIGHT_NOLZ:
  514.       sprintf (print_fmt, "%%%dd%s", lineno_width, separator_str);
  515.       break;
  516.     case FORMAT_RIGHT_LZ:
  517.       sprintf (print_fmt, "%%0%dd%s", lineno_width, separator_str);
  518.       break;
  519.     case FORMAT_LEFT:
  520.       sprintf (print_fmt, "%%-%dd%s", lineno_width, separator_str);
  521.       break;
  522.     }
  523. }
  524.  
  525. /* Set the command line flag TYPEP and possibly the regex pointer REGEXP,
  526.    according to `optarg'.  */
  527.  
  528. static int
  529. build_type_arg (typep, regexp)
  530.      char **typep;
  531.      struct re_pattern_buffer *regexp;
  532. {
  533.   const char *errmsg;
  534.   int rval = TRUE;
  535.   int optlen;
  536.  
  537.   switch (*optarg)
  538.     {
  539.     case 'a':
  540.     case 't':
  541.     case 'n':
  542.       *typep = optarg;
  543.       break;
  544.     case 'p':
  545.       *typep = optarg++;
  546.       optlen = strlen (optarg);
  547.       regexp->allocated = optlen * 2;
  548.       regexp->buffer = (unsigned char *) xmalloc (regexp->allocated);
  549.       regexp->translate = NULL;
  550.       regexp->fastmap = xmalloc (256);
  551.       regexp->fastmap_accurate = 0;
  552.       errmsg = re_compile_pattern (optarg, optlen, regexp);
  553.       if (errmsg)
  554.     error (1, 0, "%s", errmsg);
  555.       break;
  556.     default:
  557.       rval = FALSE;
  558.       break;
  559.     }
  560.   return rval;
  561. }
  562.  
  563. /* Print a usage message and quit. */
  564.  
  565. static void
  566. usage (status)
  567.      int status;
  568. {
  569.   if (status != 0)
  570.     fprintf (stderr, "Try `%s --help' for more information.\n",
  571.          program_name);
  572.   else
  573.     {
  574.       printf ("\
  575. Usage: %s [OPTION]... [FILE]...\n\
  576. ",
  577.           program_name);
  578.       printf ("\
  579. \n\
  580.   -b, --body-numbering=STYLE      use STYLE for numbering body lines\n\
  581.   -d, --section-delimiter=CC      use CC for separating logical pages\n\
  582.   -f, --footer-numbering=STYLE    use STYLE for numbering footer lines\n\
  583.   -h, --header-numbering=STYLE    use STYLE for numbering header lines\n\
  584.   -i, --page-increment=NUMBER     line number increment at each line\n\
  585.   -l, --join-blank-lines=NUMBER   group of NUMBER empty lines counted as one\n\
  586.   -n, --number-format=FORMAT      insert line numbers according to FORMAT\n\
  587.   -p, --no-renumber               do not reset line numbers at logical pages\n\
  588.   -s, --number-separator=STRING   add STRING after (possible) line number\n\
  589.   -v, --first-page=NUMBER         first line number on each logical page\n\
  590.   -w, --number-width=NUMBER       use NUMBER columns for line numbers\n\
  591.       --help                      display this help and exit\n\
  592.       --version                   output version information and exit\n\
  593. \n\
  594. By default, selects -v1 -i1 -l1 -sTAB -w6 -nrn -hn -bt -fn.  CC are\n\
  595. two delimiter characters for separating logical pages, a missing\n\
  596. second character implies :.  Type \\\\ for \\.  STYLE is one of:\n\
  597. \n\
  598.   a         number all lines\n\
  599.   t         number only nonempty lines\n\
  600.   n         number no lines\n\
  601.   pREGEXP   number only lines that contain a match for REGEXP\n\
  602. \n\
  603. FORMAT is one of:\n\
  604. \n\
  605.   ln   left justified, no leading zeros\n\
  606.   rn   right justified, no leading zeros\n\
  607.   rz   right justified, leading zeros\n\
  608. \n\
  609. With no FILE, or when FILE is -, read standard input.\n\
  610. ");
  611.     }
  612.   exit (status);
  613. }
  614.