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