LCOV - code coverage report
Current view: top level - src - nl.c (source / functions) Hit Total Coverage
Test: coreutils.info Lines: 222 238 93.3 %
Date: 2018-01-30 Functions: 11 11 100.0 %

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

Generated by: LCOV version 1.10