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