cp: fix the buffer size used when writing zeros
[coreutils/ericb.git] / src / nl.c
blob328e14568b8e6154b46aa503f32592aeba6f6802
1 /* nl -- number lines of files
2 Copyright (C) 1989, 1992, 1995-2011 Free Software Foundation, Inc.
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.
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.
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/>. */
17 /* Written by Scott Bartram (nancy!scott@uunet.uu.net)
18 Revised by David MacKenzie (djm@gnu.ai.mit.edu) */
20 #include <config.h>
22 #include <stdio.h>
23 #include <sys/types.h>
24 #include <getopt.h>
26 #include "system.h"
28 #include <regex.h>
30 #include "error.h"
31 #include "fadvise.h"
32 #include "linebuffer.h"
33 #include "quote.h"
34 #include "xstrtol.h"
36 /* The official name of this program (e.g., no `g' prefix). */
37 #define PROGRAM_NAME "nl"
39 #define AUTHORS \
40 proper_name ("Scott Bartram"), \
41 proper_name ("David MacKenzie")
43 /* Line-number formats. They are given an int width, an intmax_t
44 value, and a string separator. */
46 /* Right justified, no leading zeroes. */
47 static char const FORMAT_RIGHT_NOLZ[] = "%*" PRIdMAX "%s";
49 /* Right justified, leading zeroes. */
50 static char const FORMAT_RIGHT_LZ[] = "%0*" PRIdMAX "%s";
52 /* Left justified, no leading zeroes. */
53 static char const FORMAT_LEFT[] = "%-*" PRIdMAX "%s";
55 /* Default section delimiter characters. */
56 static char const DEFAULT_SECTION_DELIMITERS[] = "\\:";
58 /* Types of input lines: either one of the section delimiters,
59 or text to output. */
60 enum section
62 Header, Body, Footer, Text
65 /* Format of body lines (-b). */
66 static char const *body_type = "t";
68 /* Format of header lines (-h). */
69 static char const *header_type = "n";
71 /* Format of footer lines (-f). */
72 static char const *footer_type = "n";
74 /* Format currently being used (body, header, or footer). */
75 static char const *current_type;
77 /* Regex for body lines to number (-bp). */
78 static struct re_pattern_buffer body_regex;
80 /* Regex for header lines to number (-hp). */
81 static struct re_pattern_buffer header_regex;
83 /* Regex for footer lines to number (-fp). */
84 static struct re_pattern_buffer footer_regex;
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];
91 /* Pointer to current regex, if any. */
92 static struct re_pattern_buffer *current_regex = NULL;
94 /* Separator string to print after line number (-s). */
95 static char const *separator_str = "\t";
97 /* Input section delimiter string (-d). */
98 static char const *section_del = DEFAULT_SECTION_DELIMITERS;
100 /* Header delimiter string. */
101 static char *header_del = NULL;
103 /* Header section delimiter length. */
104 static size_t header_del_len;
106 /* Body delimiter string. */
107 static char *body_del = NULL;
109 /* Body section delimiter length. */
110 static size_t body_del_len;
112 /* Footer delimiter string. */
113 static char *footer_del = NULL;
115 /* Footer section delimiter length. */
116 static size_t footer_del_len;
118 /* Input buffer. */
119 static struct linebuffer line_buf;
121 /* printf format string for unnumbered lines. */
122 static char *print_no_line_fmt = NULL;
124 /* Starting line number on each page (-v). */
125 static intmax_t starting_line_number = 1;
127 /* Line number increment (-i). */
128 static intmax_t page_incr = 1;
130 /* If true, reset line number at start of each page (-p). */
131 static bool reset_numbers = true;
133 /* Number of blank lines to consider to be one line for numbering (-l). */
134 static intmax_t blank_join = 1;
136 /* Width of line numbers (-w). */
137 static int lineno_width = 6;
139 /* Line number format (-n). */
140 static char const *lineno_format = FORMAT_RIGHT_NOLZ;
142 /* Current print line number. */
143 static intmax_t line_no;
145 /* True if we have ever read standard input. */
146 static bool have_read_stdin;
148 enum
150 PAGE_INCREMENT_OPTION_DEPRECATED = CHAR_MAX + 1
153 static struct option const longopts[] =
155 {"header-numbering", required_argument, NULL, 'h'},
156 {"body-numbering", required_argument, NULL, 'b'},
157 {"footer-numbering", required_argument, NULL, 'f'},
158 {"starting-line-number", required_argument, NULL, 'v'},
159 {"line-increment", required_argument, NULL, 'i'},
160 /* FIXME: page-increment is deprecated, remove in dec-2011. */
161 {"page-increment", required_argument, NULL, PAGE_INCREMENT_OPTION_DEPRECATED},
162 {"no-renumber", no_argument, NULL, 'p'},
163 {"join-blank-lines", required_argument, NULL, 'l'},
164 {"number-separator", required_argument, NULL, 's'},
165 {"number-width", required_argument, NULL, 'w'},
166 {"number-format", required_argument, NULL, 'n'},
167 {"section-delimiter", required_argument, NULL, 'd'},
168 {GETOPT_HELP_OPTION_DECL},
169 {GETOPT_VERSION_OPTION_DECL},
170 {NULL, 0, NULL, 0}
173 /* Print a usage message and quit. */
175 void
176 usage (int status)
178 if (status != EXIT_SUCCESS)
179 fprintf (stderr, _("Try `%s --help' for more information.\n"),
180 program_name);
181 else
183 printf (_("\
184 Usage: %s [OPTION]... [FILE]...\n\
186 program_name);
187 fputs (_("\
188 Write each FILE to standard output, with line numbers added.\n\
189 With no FILE, or when FILE is -, read standard input.\n\
191 "), stdout);
192 fputs (_("\
193 Mandatory arguments to long options are mandatory for short options too.\n\
194 "), stdout);
195 fputs (_("\
196 -b, --body-numbering=STYLE use STYLE for numbering body lines\n\
197 -d, --section-delimiter=CC use CC for separating logical pages\n\
198 -f, --footer-numbering=STYLE use STYLE for numbering footer lines\n\
199 "), stdout);
200 fputs (_("\
201 -h, --header-numbering=STYLE use STYLE for numbering header lines\n\
202 -i, --line-increment=NUMBER line number increment at each line\n\
203 -l, --join-blank-lines=NUMBER group of NUMBER empty lines counted as one\n\
204 -n, --number-format=FORMAT insert line numbers according to FORMAT\n\
205 -p, --no-renumber do not reset line numbers at logical pages\n\
206 -s, --number-separator=STRING add STRING after (possible) line number\n\
207 "), stdout);
208 fputs (_("\
209 -v, --starting-line-number=NUMBER first line number on each logical page\n\
210 -w, --number-width=NUMBER use NUMBER columns for line numbers\n\
211 "), stdout);
212 fputs (HELP_OPTION_DESCRIPTION, stdout);
213 fputs (VERSION_OPTION_DESCRIPTION, stdout);
214 fputs (_("\
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 "), stdout);
220 fputs (_("\
222 a number all lines\n\
223 t number only nonempty lines\n\
224 n number no lines\n\
225 pBRE number only lines that contain a match for the basic regular\n\
226 expression, BRE\n\
228 FORMAT is one of:\n\
230 ln left justified, no leading zeros\n\
231 rn right justified, no leading zeros\n\
232 rz right justified, leading zeros\n\
234 "), stdout);
235 emit_ancillary_info ();
237 exit (status);
240 /* Set the command line flag TYPEP and possibly the regex pointer REGEXP,
241 according to `optarg'. */
243 static bool
244 build_type_arg (char const **typep,
245 struct re_pattern_buffer *regexp, char *fastmap)
247 char const *errmsg;
248 bool rval = true;
250 switch (*optarg)
252 case 'a':
253 case 't':
254 case 'n':
255 *typep = optarg;
256 break;
257 case 'p':
258 *typep = optarg++;
259 regexp->buffer = NULL;
260 regexp->allocated = 0;
261 regexp->fastmap = fastmap;
262 regexp->translate = NULL;
263 re_syntax_options =
264 RE_SYNTAX_POSIX_BASIC & ~RE_CONTEXT_INVALID_DUP & ~RE_NO_EMPTY_RANGES;
265 errmsg = re_compile_pattern (optarg, strlen (optarg), regexp);
266 if (errmsg)
267 error (EXIT_FAILURE, 0, "%s", errmsg);
268 break;
269 default:
270 rval = false;
271 break;
273 return rval;
276 /* Print the line number and separator; increment the line number. */
278 static void
279 print_lineno (void)
281 intmax_t next_line_no;
283 printf (lineno_format, lineno_width, line_no, separator_str);
285 next_line_no = line_no + page_incr;
286 if (next_line_no < line_no)
287 error (EXIT_FAILURE, 0, _("line number overflow"));
288 line_no = next_line_no;
291 /* Switch to a header section. */
293 static void
294 proc_header (void)
296 current_type = header_type;
297 current_regex = &header_regex;
298 if (reset_numbers)
299 line_no = starting_line_number;
300 putchar ('\n');
303 /* Switch to a body section. */
305 static void
306 proc_body (void)
308 current_type = body_type;
309 current_regex = &body_regex;
310 putchar ('\n');
313 /* Switch to a footer section. */
315 static void
316 proc_footer (void)
318 current_type = footer_type;
319 current_regex = &footer_regex;
320 putchar ('\n');
323 /* Process a regular text line in `line_buf'. */
325 static void
326 proc_text (void)
328 static intmax_t blank_lines = 0; /* Consecutive blank lines so far. */
330 switch (*current_type)
332 case 'a':
333 if (blank_join > 1)
335 if (1 < line_buf.length || ++blank_lines == blank_join)
337 print_lineno ();
338 blank_lines = 0;
340 else
341 fputs (print_no_line_fmt, stdout);
343 else
344 print_lineno ();
345 break;
346 case 't':
347 if (1 < line_buf.length)
348 print_lineno ();
349 else
350 fputs (print_no_line_fmt, stdout);
351 break;
352 case 'n':
353 fputs (print_no_line_fmt, stdout);
354 break;
355 case 'p':
356 switch (re_search (current_regex, line_buf.buffer, line_buf.length - 1,
357 0, line_buf.length - 1, NULL))
359 case -2:
360 error (EXIT_FAILURE, errno, _("error in regular expression search"));
362 case -1:
363 fputs (print_no_line_fmt, stdout);
364 break;
366 default:
367 print_lineno ();
368 break;
371 fwrite (line_buf.buffer, sizeof (char), line_buf.length, stdout);
374 /* Return the type of line in `line_buf'. */
376 static enum section
377 check_section (void)
379 size_t len = line_buf.length - 1;
381 if (len < 2 || memcmp (line_buf.buffer, section_del, 2))
382 return Text;
383 if (len == header_del_len
384 && !memcmp (line_buf.buffer, header_del, header_del_len))
385 return Header;
386 if (len == body_del_len
387 && !memcmp (line_buf.buffer, body_del, body_del_len))
388 return Body;
389 if (len == footer_del_len
390 && !memcmp (line_buf.buffer, footer_del, footer_del_len))
391 return Footer;
392 return Text;
395 /* Read and process the file pointed to by FP. */
397 static void
398 process_file (FILE *fp)
400 while (readlinebuffer (&line_buf, fp))
402 switch (check_section ())
404 case Header:
405 proc_header ();
406 break;
407 case Body:
408 proc_body ();
409 break;
410 case Footer:
411 proc_footer ();
412 break;
413 case Text:
414 proc_text ();
415 break;
420 /* Process file FILE to standard output.
421 Return true if successful. */
423 static bool
424 nl_file (char const *file)
426 FILE *stream;
428 if (STREQ (file, "-"))
430 have_read_stdin = true;
431 stream = stdin;
433 else
435 stream = fopen (file, "r");
436 if (stream == NULL)
438 error (0, errno, "%s", file);
439 return false;
443 fadvise (stream, FADVISE_SEQUENTIAL);
445 process_file (stream);
447 if (ferror (stream))
449 error (0, errno, "%s", file);
450 return false;
452 if (STREQ (file, "-"))
453 clearerr (stream); /* Also clear EOF. */
454 else if (fclose (stream) == EOF)
456 error (0, errno, "%s", file);
457 return false;
459 return true;
463 main (int argc, char **argv)
465 int c;
466 size_t len;
467 bool ok = true;
469 initialize_main (&argc, &argv);
470 set_program_name (argv[0]);
471 setlocale (LC_ALL, "");
472 bindtextdomain (PACKAGE, LOCALEDIR);
473 textdomain (PACKAGE);
475 atexit (close_stdout);
477 have_read_stdin = false;
479 while ((c = getopt_long (argc, argv, "h:b:f:v:i:pl:s:w:n:d:", longopts,
480 NULL)) != -1)
482 switch (c)
484 case 'h':
485 if (! build_type_arg (&header_type, &header_regex, header_fastmap))
487 error (0, 0, _("invalid header numbering style: %s"),
488 quote (optarg));
489 ok = false;
491 break;
492 case 'b':
493 if (! build_type_arg (&body_type, &body_regex, body_fastmap))
495 error (0, 0, _("invalid body numbering style: %s"),
496 quote (optarg));
497 ok = false;
499 break;
500 case 'f':
501 if (! build_type_arg (&footer_type, &footer_regex, footer_fastmap))
503 error (0, 0, _("invalid footer numbering style: %s"),
504 quote (optarg));
505 ok = false;
507 break;
508 case 'v':
509 if (xstrtoimax (optarg, NULL, 10, &starting_line_number, "")
510 != LONGINT_OK)
512 error (0, 0, _("invalid starting line number: %s"),
513 quote (optarg));
514 ok = false;
516 break;
517 case PAGE_INCREMENT_OPTION_DEPRECATED:
518 error (0, 0, _("WARNING: --page-increment is deprecated; "
519 "use --line-increment instead"));
520 /* fall through */
521 case 'i':
522 if (! (xstrtoimax (optarg, NULL, 10, &page_incr, "") == LONGINT_OK
523 && 0 < page_incr))
525 error (0, 0, _("invalid line number increment: %s"),
526 quote (optarg));
527 ok = false;
529 break;
530 case 'p':
531 reset_numbers = false;
532 break;
533 case 'l':
534 if (! (xstrtoimax (optarg, NULL, 10, &blank_join, "") == LONGINT_OK
535 && 0 < blank_join))
537 error (0, 0, _("invalid number of blank lines: %s"),
538 quote (optarg));
539 ok = false;
541 break;
542 case 's':
543 separator_str = optarg;
544 break;
545 case 'w':
547 long int tmp_long;
548 if (xstrtol (optarg, NULL, 10, &tmp_long, "") != LONGINT_OK
549 || tmp_long <= 0 || tmp_long > INT_MAX)
551 error (0, 0, _("invalid line number field width: %s"),
552 quote (optarg));
553 ok = false;
555 else
557 lineno_width = tmp_long;
560 break;
561 case 'n':
562 if (STREQ (optarg, "ln"))
563 lineno_format = FORMAT_LEFT;
564 else if (STREQ (optarg, "rn"))
565 lineno_format = FORMAT_RIGHT_NOLZ;
566 else if (STREQ (optarg, "rz"))
567 lineno_format = FORMAT_RIGHT_LZ;
568 else
570 error (0, 0, _("invalid line numbering format: %s"),
571 quote (optarg));
572 ok = false;
574 break;
575 case 'd':
576 section_del = optarg;
577 break;
578 case_GETOPT_HELP_CHAR;
579 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
580 default:
581 ok = false;
582 break;
586 if (!ok)
587 usage (EXIT_FAILURE);
589 /* Initialize the section delimiters. */
590 len = strlen (section_del);
592 header_del_len = len * 3;
593 header_del = xmalloc (header_del_len + 1);
594 stpcpy (stpcpy (stpcpy (header_del, section_del), section_del), section_del);
596 body_del_len = len * 2;
597 body_del = xmalloc (body_del_len + 1);
598 stpcpy (stpcpy (body_del, section_del), section_del);
600 footer_del_len = len;
601 footer_del = xmalloc (footer_del_len + 1);
602 stpcpy (footer_del, section_del);
604 /* Initialize the input buffer. */
605 initbuffer (&line_buf);
607 /* Initialize the printf format for unnumbered lines. */
608 len = strlen (separator_str);
609 print_no_line_fmt = xmalloc (lineno_width + len + 1);
610 memset (print_no_line_fmt, ' ', lineno_width + len);
611 print_no_line_fmt[lineno_width + len] = '\0';
613 line_no = starting_line_number;
614 current_type = body_type;
615 current_regex = &body_regex;
617 /* Main processing. */
619 if (optind == argc)
620 ok = nl_file ("-");
621 else
622 for (; optind < argc; optind++)
623 ok &= nl_file (argv[optind]);
625 if (have_read_stdin && fclose (stdin) == EOF)
626 error (EXIT_FAILURE, errno, "-");
628 exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);