home *** CD-ROM | disk | FTP | other *** search
/ Geek Gadgets 1 / ADE-1.bin / ade-dist / textutils-1.19-src.tgz / tar.out / fsf / textutils / src / nl.c < prev    next >
C/C++ Source or Header  |  1996-09-28  |  15KB  |  635 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
  16.    Foundation, 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. #include "system.h"    /* Before regex to avoid clashes with system defines */
  32. #if WITH_REGEX
  33. # include <regex.h>
  34. #else
  35. # include <rx.h>
  36. #endif
  37.  
  38. #ifndef UINT_MAX
  39. # define UINT_MAX ((unsigned int) ~(unsigned int) 0)
  40. #endif
  41.  
  42. #ifndef INT_MAX
  43. # define INT_MAX ((int) (UINT_MAX >> 1))
  44. #endif
  45.  
  46. #include "linebuffer.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.     }
  233.   exit (status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
  234. }
  235.  
  236. /* Build the printf format string, based on `lineno_format'. */
  237.  
  238. static void
  239. build_print_fmt (void)
  240. {
  241.   /* 12 = 10 chars for lineno_width, 1 for %, 1 for \0.  */
  242.   print_fmt = xmalloc (strlen (separator_str) + 12);
  243.   switch (lineno_format)
  244.     {
  245.     case FORMAT_RIGHT_NOLZ:
  246.       sprintf (print_fmt, "%%%dd%s", lineno_width, separator_str);
  247.       break;
  248.     case FORMAT_RIGHT_LZ:
  249.       sprintf (print_fmt, "%%0%dd%s", lineno_width, separator_str);
  250.       break;
  251.     case FORMAT_LEFT:
  252.       sprintf (print_fmt, "%%-%dd%s", lineno_width, separator_str);
  253.       break;
  254.     }
  255. }
  256.  
  257. /* Set the command line flag TYPEP and possibly the regex pointer REGEXP,
  258.    according to `optarg'.  */
  259.  
  260. static int
  261. build_type_arg (char **typep, struct re_pattern_buffer *regexp)
  262. {
  263.   const char *errmsg;
  264.   int rval = TRUE;
  265.   int optlen;
  266.  
  267.   switch (*optarg)
  268.     {
  269.     case 'a':
  270.     case 't':
  271.     case 'n':
  272.       *typep = optarg;
  273.       break;
  274.     case 'p':
  275.       *typep = optarg++;
  276.       optlen = strlen (optarg);
  277.       regexp->allocated = optlen * 2;
  278.       regexp->buffer = (unsigned char *) xmalloc (regexp->allocated);
  279.       regexp->translate = NULL;
  280.       regexp->fastmap = xmalloc (256);
  281.       regexp->fastmap_accurate = 0;
  282.       errmsg = re_compile_pattern (optarg, optlen, regexp);
  283.       if (errmsg)
  284.     error (EXIT_FAILURE, 0, "%s", errmsg);
  285.       break;
  286.     default:
  287.       rval = FALSE;
  288.       break;
  289.     }
  290.   return rval;
  291. }
  292.  
  293. /* Print and increment the line number. */
  294.  
  295. static void
  296. print_lineno (void)
  297. {
  298.   printf (print_fmt, line_no);
  299.   line_no += page_incr;
  300. }
  301.  
  302. /* Switch to a header section. */
  303.  
  304. static void
  305. proc_header (void)
  306. {
  307.   current_type = header_type;
  308.   current_regex = &header_regex;
  309.   if (reset_numbers)
  310.     line_no = starting_line_number;
  311.   putchar ('\n');
  312. }
  313.  
  314. /* Switch to a body section. */
  315.  
  316. static void
  317. proc_body (void)
  318. {
  319.   current_type = body_type;
  320.   current_regex = &body_regex;
  321.   putchar ('\n');
  322. }
  323.  
  324. /* Switch to a footer section. */
  325.  
  326. static void
  327. proc_footer (void)
  328. {
  329.   current_type = footer_type;
  330.   current_regex = &footer_regex;
  331.   putchar ('\n');
  332. }
  333.  
  334. /* Process a regular text line in `line_buf'. */
  335.  
  336. static void
  337. proc_text (void)
  338. {
  339.   static int blank_lines = 0;    /* Consecutive blank lines so far. */
  340.  
  341.   switch (*current_type)
  342.     {
  343.     case 'a':
  344.       if (blank_join > 1)
  345.     {
  346.       if (line_buf.length || ++blank_lines == blank_join)
  347.         {
  348.           print_lineno ();
  349.           blank_lines = 0;
  350.         }
  351.       else
  352.         printf (print_no_line_fmt);
  353.     }
  354.       else
  355.     print_lineno ();
  356.       break;
  357.     case 't':
  358.       if (line_buf.length)
  359.     print_lineno ();
  360.       else
  361.     printf (print_no_line_fmt);
  362.       break;
  363.     case 'n':
  364.       printf (print_no_line_fmt);
  365.       break;
  366.     case 'p':
  367.       if (re_search (current_regex, line_buf.buffer, line_buf.length,
  368.              0, line_buf.length, (struct re_registers *) 0) < 0)
  369.     printf (print_no_line_fmt);
  370.       else
  371.     print_lineno ();
  372.       break;
  373.     }
  374.   fwrite (line_buf.buffer, sizeof (char), line_buf.length, stdout);
  375.   putchar ('\n');
  376. }
  377.  
  378. /* Return the type of line in `line_buf'. */
  379.  
  380. static enum section
  381. check_section (void)
  382. {
  383.   if (line_buf.length < 2 || memcmp (line_buf.buffer, section_del, 2))
  384.     return Text;
  385.   if (line_buf.length == header_del_len
  386.       && !memcmp (line_buf.buffer, header_del, header_del_len))
  387.     return Header;
  388.   if (line_buf.length == body_del_len
  389.       && !memcmp (line_buf.buffer, body_del, body_del_len))
  390.     return Body;
  391.   if (line_buf.length == footer_del_len
  392.       && !memcmp (line_buf.buffer, footer_del, footer_del_len))
  393.     return Footer;
  394.   return Text;
  395. }
  396.  
  397. /* Read and process the file pointed to by FP. */
  398.  
  399. static void
  400. process_file (FILE *fp)
  401. {
  402.   while (readline (&line_buf, fp))
  403.     {
  404.       switch ((int) check_section ())
  405.     {
  406.     case Header:
  407.       proc_header ();
  408.       break;
  409.     case Body:
  410.       proc_body ();
  411.       break;
  412.     case Footer:
  413.       proc_footer ();
  414.       break;
  415.     case Text:
  416.       proc_text ();
  417.       break;
  418.     }
  419.     }
  420. }
  421.  
  422. /* Process file FILE to standard output.
  423.    Return 0 if successful, 1 if not. */
  424.  
  425. static int
  426. nl_file (const char *file)
  427. {
  428.   FILE *stream;
  429.  
  430.   if (!strcmp (file, "-"))
  431.     {
  432.       have_read_stdin = 1;
  433.       stream = stdin;
  434.     }
  435.   else
  436.     {
  437.       stream = fopen (file, "r");
  438.       if (stream == NULL)
  439.     {
  440.       error (0, errno, "%s", file);
  441.       return 1;
  442.     }
  443.     }
  444.  
  445.   process_file (stream);
  446.  
  447.   if (ferror (stream))
  448.     {
  449.       error (0, errno, "%s", file);
  450.       return 1;
  451.     }
  452.   if (!strcmp (file, "-"))
  453.     clearerr (stream);        /* Also clear EOF. */
  454.   else if (fclose (stream) == EOF)
  455.     {
  456.       error (0, errno, "%s", file);
  457.       return 1;
  458.     }
  459.   return 0;
  460. }
  461.  
  462. int
  463. main (int argc, char **argv)
  464. {
  465.   int c, exit_status = 0;
  466.  
  467.   program_name = argv[0];
  468.   setlocale (LC_ALL, "");
  469.   bindtextdomain (PACKAGE, LOCALEDIR);
  470.   textdomain (PACKAGE);
  471.  
  472.   have_read_stdin = 0;
  473.  
  474.   while ((c = getopt_long (argc, argv, "h:b:f:v:i:pl:s:w:n:d:", longopts,
  475.                (int *) 0)) != EOF)
  476.     {
  477.       switch (c)
  478.     {
  479.     case 0:
  480.       break;
  481.  
  482.     case 'h':
  483.       if (build_type_arg (&header_type, &header_regex) != TRUE)
  484.         usage (2);
  485.       break;
  486.     case 'b':
  487.       if (build_type_arg (&body_type, &body_regex) != TRUE)
  488.         usage (2);
  489.       break;
  490.     case 'f':
  491.       if (build_type_arg (&footer_type, &footer_regex) != TRUE)
  492.         usage (2);
  493.       break;
  494.     case 'v':
  495.       {
  496.         long int tmp_long;
  497.         if (xstrtol (optarg, NULL, 10, &tmp_long, NULL) != LONGINT_OK
  498.         /* Allow it to be negative.  */
  499.         || tmp_long > INT_MAX)
  500.           error (EXIT_FAILURE, 0, _("invalid starting line number: `%s'"),
  501.              optarg);
  502.         starting_line_number = (int) tmp_long;
  503.       }
  504.       break;
  505.     case 'i':
  506.       {
  507.         long int tmp_long;
  508.         if (xstrtol (optarg, NULL, 10, &tmp_long, NULL) != LONGINT_OK
  509.         || tmp_long <= 0 || tmp_long > INT_MAX)
  510.           error (EXIT_FAILURE, 0, _("invalid line number increment: `%s'"),
  511.              optarg);
  512.         page_incr = (int) tmp_long;
  513.       }
  514.       break;
  515.     case 'p':
  516.       reset_numbers = FALSE;
  517.       break;
  518.     case 'l':
  519.       {
  520.         long int tmp_long;
  521.         if (xstrtol (optarg, NULL, 10, &tmp_long, NULL) != LONGINT_OK
  522.         || tmp_long <= 0 || tmp_long > INT_MAX)
  523.           error (EXIT_FAILURE, 0, _("invalid number of blank lines: `%s'"),
  524.              optarg);
  525.         blank_join = (int) tmp_long;
  526.       }
  527.       break;
  528.     case 's':
  529.       separator_str = optarg;
  530.       break;
  531.     case 'w':
  532.       {
  533.         long int tmp_long;
  534.         if (xstrtol (optarg, NULL, 10, &tmp_long, NULL) != LONGINT_OK
  535.         || tmp_long <= 0 || tmp_long > INT_MAX)
  536.           error (EXIT_FAILURE, 0,
  537.              _("invalid line number field width: `%s'"),
  538.              optarg);
  539.         lineno_width = (int) tmp_long;
  540.       }
  541.       break;
  542.     case 'n':
  543.       switch (*optarg)
  544.         {
  545.         case 'l':
  546.           if (optarg[1] == 'n')
  547.         lineno_format = FORMAT_LEFT;
  548.           else
  549.         usage (2);
  550.           break;
  551.         case 'r':
  552.           switch (optarg[1])
  553.         {
  554.         case 'n':
  555.           lineno_format = FORMAT_RIGHT_NOLZ;
  556.           break;
  557.         case 'z':
  558.           lineno_format = FORMAT_RIGHT_LZ;
  559.           break;
  560.         default:
  561.           usage (2);
  562.           break;
  563.         }
  564.           break;
  565.         default:
  566.           usage (2);
  567.           break;
  568.         }
  569.       break;
  570.     case 'd':
  571.       section_del = optarg;
  572.       break;
  573.     default:
  574.       usage (2);
  575.       break;
  576.     }
  577.     }
  578.  
  579.   if (show_version)
  580.     {
  581.       printf ("nl - %s\n", PACKAGE_VERSION);
  582.       exit (EXIT_SUCCESS);
  583.     }
  584.  
  585.   if (show_help)
  586.     usage (0);
  587.  
  588.   /* Initialize the section delimiters.  */
  589.   c = strlen (section_del);
  590.  
  591.   header_del_len = c * 3;
  592.   header_del = xmalloc (header_del_len + 1);
  593.   strcat (strcat (strcpy (header_del, section_del), section_del), section_del);
  594.  
  595.   body_del_len = c * 2;
  596.   body_del = xmalloc (body_del_len + 1);
  597.   strcat (strcpy (body_del, section_del), section_del);
  598.  
  599.   footer_del_len = c;
  600.   footer_del = xmalloc (footer_del_len + 1);
  601.   strcpy (footer_del, section_del);
  602.  
  603.   /* Initialize the input buffer.  */
  604.   initbuffer (&line_buf);
  605.  
  606.   /* Initialize the printf format for unnumbered lines. */
  607.   c = strlen (separator_str);
  608.   print_no_line_fmt = xmalloc (lineno_width + c + 1);
  609.   memset (print_no_line_fmt, ' ', lineno_width + c);
  610.   print_no_line_fmt[lineno_width + c] = '\0';
  611.  
  612.   line_no = starting_line_number;
  613.   current_type = body_type;
  614.   current_regex = &body_regex;
  615.   build_print_fmt ();
  616.  
  617.   /* Main processing. */
  618.  
  619.   if (optind == argc)
  620.     exit_status |= nl_file ("-");
  621.   else
  622.     for (; optind < argc; optind++)
  623.       exit_status |= nl_file (argv[optind]);
  624.  
  625.   if (have_read_stdin && fclose (stdin) == EOF)
  626.     {
  627.       error (0, errno, "-");
  628.       exit_status = 1;
  629.     }
  630.   if (ferror (stdout) || fclose (stdout) == EOF)
  631.     error (EXIT_FAILURE, errno, _("write error"));
  632.  
  633.   exit (exit_status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
  634. }
  635.