home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 11 Util / 11-Util.zip / txtut122.zip / textutil / src / nl.c < prev    next >
C/C++ Source or Header  |  1998-04-11  |  16KB  |  642 lines

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