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 : }
|