2 * Copyright (c) 1999 The NetBSD Foundation, Inc.
5 * This code is derived from software contributed to The NetBSD Foundation
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
21 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
29 * @(#) Copyright (c) 1999 The NetBSD Foundation, Inc. All rights reserved.
30 * $FreeBSD: head/usr.bin/nl/nl.c 303595 2016-07-31 19:02:19Z bapt $
33 #include <sys/types.h>
47 number_all
, /* number all lines */
48 number_nonempty
, /* number non-empty lines */
49 number_none
, /* no line numbering */
50 number_regex
/* number lines matching regular expression */
53 struct numbering_property
{
54 const char * const name
; /* for diagnostics */
55 numbering_type type
; /* numbering type */
56 regex_t expr
; /* for type == number_regex */
59 /* line numbering formats */
60 #define FORMAT_LN "%-*d" /* left justified, leading zeros suppressed */
61 #define FORMAT_RN "%*d" /* right justified, leading zeros suppressed */
62 #define FORMAT_RZ "%0*d" /* right justified, leading zeros kept */
67 #define NP_LAST HEADER
69 static struct numbering_property numbering_properties
[NP_LAST
+ 1] = {
70 { .name
= "footer", .type
= number_none
},
71 { .name
= "body", .type
= number_nonempty
},
72 { .name
= "header", .type
= number_none
}
75 #define max(a, b) ((a) > (b) ? (a) : (b))
78 * Maximum number of characters required for a decimal representation of a
79 * (signed) int; courtesy of tzcode.
81 #define INT_STRLEN_MAXIMUM \
82 ((int)(sizeof (int) * CHAR_BIT - 1) * 302 / 1000 + 2)
84 static void filter(void);
85 static void parse_numbering(const char *, int);
86 static void usage(void);
89 * Dynamically allocated buffer suitable for string representation of ints.
91 static char *intbuffer
;
93 /* delimiter characters that indicate the start of a logical page section */
94 static char delim
[2 * MB_LEN_MAX
];
98 * Configurable parameters.
101 /* line numbering format */
102 static const char *format
= FORMAT_RN
;
104 /* increment value used to number logical page lines */
107 /* number of adjacent blank lines to be considered (and numbered) as one */
108 static unsigned int nblank
= 1;
110 /* whether to restart numbering at logical page delimiters */
111 static int restart
= 1;
113 /* characters used in separating the line number and the corrsp. text line */
114 static const char *sep
= "\t";
116 /* initial value used to number logical page lines */
117 static int startnum
= 1;
119 /* number of characters to be used for the line number */
120 /* should be unsigned but required signed by `*' precision conversion */
121 static int width
= 6;
125 main(int argc
, char **argv
)
131 size_t intbuffersize
, clen
;
132 char delim1
[MB_LEN_MAX
] = { '\\' }, delim2
[MB_LEN_MAX
] = { ':' };
133 size_t delim1len
= 1, delim2len
= 1;
135 (void)setlocale(LC_ALL
, "");
137 while ((c
= getopt(argc
, argv
, "pb:d:f:h:i:l:n:s:v:w:")) != -1) {
143 parse_numbering(optarg
, BODY
);
146 clen
= mbrlen(optarg
, MB_CUR_MAX
, NULL
);
147 if (clen
== (size_t)-1 || clen
== (size_t)-2)
148 errc(EXIT_FAILURE
, EILSEQ
, NULL
);
150 memcpy(delim1
, optarg
, delim1len
= clen
);
151 clen
= mbrlen(optarg
+ delim1len
,
153 if (clen
== (size_t)-1 ||
155 errc(EXIT_FAILURE
, EILSEQ
, NULL
);
157 memcpy(delim2
, optarg
+ delim1len
,
159 if (optarg
[delim1len
+ clen
] != '\0')
161 "invalid delim argument -- %s",
167 parse_numbering(optarg
, FOOTER
);
170 parse_numbering(optarg
, HEADER
);
174 val
= strtol(optarg
, &ep
, 10);
175 if ((ep
!= NULL
&& *ep
!= '\0') ||
176 ((val
== LONG_MIN
|| val
== LONG_MAX
) && errno
!= 0))
178 "invalid incr argument -- %s", optarg
);
183 uval
= strtoul(optarg
, &ep
, 10);
184 if ((ep
!= NULL
&& *ep
!= '\0') ||
185 (uval
== ULONG_MAX
&& errno
!= 0))
187 "invalid num argument -- %s", optarg
);
188 nblank
= (unsigned int)uval
;
191 if (strcmp(optarg
, "ln") == 0) {
193 } else if (strcmp(optarg
, "rn") == 0) {
195 } else if (strcmp(optarg
, "rz") == 0) {
199 "illegal format -- %s", optarg
);
206 val
= strtol(optarg
, &ep
, 10);
207 if ((ep
!= NULL
&& *ep
!= '\0') ||
208 ((val
== LONG_MIN
|| val
== LONG_MAX
) && errno
!= 0))
210 "invalid startnum value -- %s", optarg
);
215 val
= strtol(optarg
, &ep
, 10);
216 if ((ep
!= NULL
&& *ep
!= '\0') ||
217 ((val
== LONG_MIN
|| val
== LONG_MAX
) && errno
!= 0))
219 "invalid width value -- %s", optarg
);
223 "width argument must be > 0 -- %d",
239 if (strcmp(argv
[0], "-") != 0 &&
240 freopen(argv
[0], "r", stdin
) == NULL
)
241 err(EXIT_FAILURE
, "%s", argv
[0]);
248 /* Generate the delimiter sequence */
249 memcpy(delim
, delim1
, delim1len
);
250 memcpy(delim
+ delim1len
, delim2
, delim2len
);
251 delimlen
= delim1len
+ delim2len
;
253 /* Allocate a buffer suitable for preformatting line number. */
254 intbuffersize
= max(INT_STRLEN_MAXIMUM
, width
) + 1; /* NUL */
255 if ((intbuffer
= malloc(intbuffersize
)) == NULL
)
256 err(EXIT_FAILURE
, "cannot allocate preformatting buffer");
271 int line
; /* logical line number */
272 int section
; /* logical page section */
273 unsigned int adjblank
; /* adjacent blank lines */
274 int consumed
; /* intbuffer measurement */
277 adjblank
= donumber
= 0;
283 while ((linelen
= getline(&buffer
, &buffersize
, stdin
)) > 0) {
284 for (idx
= FOOTER
; idx
<= NP_LAST
; idx
++) {
285 /* Does it look like a delimiter? */
286 if (delimlen
* (idx
+ 1) > linelen
)
288 if (memcmp(buffer
+ delimlen
* idx
, delim
,
291 /* Was this the whole line? */
292 if (buffer
[delimlen
* (idx
+ 1)] == '\n') {
301 switch (numbering_properties
[section
].type
) {
304 * Doing this for number_all only is disputable, but
305 * the standard expresses an explicit dependency on
308 if (buffer
[0] == '\n' && ++adjblank
< nblank
)
311 donumber
= 1, adjblank
= 0;
313 case number_nonempty
:
314 donumber
= (buffer
[0] != '\n');
321 (regexec(&numbering_properties
[section
].expr
,
322 buffer
, 0, NULL
, 0) == 0);
327 /* Note: sprintf() is safe here. */
328 consumed
= sprintf(intbuffer
, format
, width
, line
);
330 intbuffer
+ max(0, consumed
- width
));
333 (void)printf("%*s", width
, "");
335 (void)fputs(sep
, stdout
);
336 (void)fwrite(buffer
, linelen
, 1, stdout
);
339 err(EXIT_FAILURE
, "output error");
345 err(EXIT_FAILURE
, "input error");
351 * Various support functions.
355 parse_numbering(const char *argstr
, int section
)
358 char errorbuf
[NL_TEXTMAX
];
362 numbering_properties
[section
].type
= number_all
;
365 numbering_properties
[section
].type
= number_none
;
368 numbering_properties
[section
].type
= number_nonempty
;
371 /* If there was a previous expression, throw it away. */
372 if (numbering_properties
[section
].type
== number_regex
)
373 regfree(&numbering_properties
[section
].expr
);
375 numbering_properties
[section
].type
= number_regex
;
377 /* Compile/validate the supplied regular expression. */
378 if ((error
= regcomp(&numbering_properties
[section
].expr
,
379 &argstr
[1], REG_NEWLINE
|REG_NOSUB
)) != 0) {
380 (void)regerror(error
,
381 &numbering_properties
[section
].expr
,
382 errorbuf
, sizeof (errorbuf
));
385 numbering_properties
[section
].name
, errorbuf
,
391 "illegal %s line numbering type -- %s",
392 numbering_properties
[section
].name
, argstr
);
400 (void)fprintf(stderr
,
401 "usage: nl [-p] [-b type] [-d delim] [-f type] [-h type] [-i incr] [-l num]\n"
402 " [-n format] [-s sep] [-v startnum] [-w width] [file]\n");