xref: /minix3/usr.bin/nl/nl.c (revision 1e69cb63f46730e77da9d78903bc04fd914ae8a8)
1*1e69cb63SThomas Cort /*	$NetBSD: nl.c,v 1.12 2013/09/17 20:00:50 wiz Exp $	*/
2*1e69cb63SThomas Cort 
3*1e69cb63SThomas Cort /*-
4*1e69cb63SThomas Cort  * Copyright (c) 1999 The NetBSD Foundation, Inc.
5*1e69cb63SThomas Cort  * All rights reserved.
6*1e69cb63SThomas Cort  *
7*1e69cb63SThomas Cort  * This code is derived from software contributed to The NetBSD Foundation
8*1e69cb63SThomas Cort  * by Klaus Klein.
9*1e69cb63SThomas Cort  *
10*1e69cb63SThomas Cort  * Redistribution and use in source and binary forms, with or without
11*1e69cb63SThomas Cort  * modification, are permitted provided that the following conditions
12*1e69cb63SThomas Cort  * are met:
13*1e69cb63SThomas Cort  * 1. Redistributions of source code must retain the above copyright
14*1e69cb63SThomas Cort  *    notice, this list of conditions and the following disclaimer.
15*1e69cb63SThomas Cort  * 2. Redistributions in binary form must reproduce the above copyright
16*1e69cb63SThomas Cort  *    notice, this list of conditions and the following disclaimer in the
17*1e69cb63SThomas Cort  *    documentation and/or other materials provided with the distribution.
18*1e69cb63SThomas Cort  *
19*1e69cb63SThomas Cort  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20*1e69cb63SThomas Cort  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21*1e69cb63SThomas Cort  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22*1e69cb63SThomas Cort  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23*1e69cb63SThomas Cort  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24*1e69cb63SThomas Cort  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25*1e69cb63SThomas Cort  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26*1e69cb63SThomas Cort  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27*1e69cb63SThomas Cort  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28*1e69cb63SThomas Cort  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29*1e69cb63SThomas Cort  * POSSIBILITY OF SUCH DAMAGE.
30*1e69cb63SThomas Cort  */
31*1e69cb63SThomas Cort 
32*1e69cb63SThomas Cort #include <sys/cdefs.h>
33*1e69cb63SThomas Cort #ifndef lint
34*1e69cb63SThomas Cort __COPYRIGHT("@(#) Copyright (c) 1999\
35*1e69cb63SThomas Cort  The NetBSD Foundation, Inc.  All rights reserved.");
36*1e69cb63SThomas Cort __RCSID("$NetBSD: nl.c,v 1.12 2013/09/17 20:00:50 wiz Exp $");
37*1e69cb63SThomas Cort #endif
38*1e69cb63SThomas Cort 
39*1e69cb63SThomas Cort #include <errno.h>
40*1e69cb63SThomas Cort #include <limits.h>
41*1e69cb63SThomas Cort #include <locale.h>
42*1e69cb63SThomas Cort #include <regex.h>
43*1e69cb63SThomas Cort #include <stdio.h>
44*1e69cb63SThomas Cort #include <stdlib.h>
45*1e69cb63SThomas Cort #include <string.h>
46*1e69cb63SThomas Cort #include <unistd.h>
47*1e69cb63SThomas Cort #include <err.h>
48*1e69cb63SThomas Cort 
49*1e69cb63SThomas Cort typedef enum {
50*1e69cb63SThomas Cort 	number_all,		/* number all lines */
51*1e69cb63SThomas Cort 	number_nonempty,	/* number non-empty lines */
52*1e69cb63SThomas Cort 	number_none,		/* no line numbering */
53*1e69cb63SThomas Cort 	number_regex		/* number lines matching regular expression */
54*1e69cb63SThomas Cort } numbering_type;
55*1e69cb63SThomas Cort 
56*1e69cb63SThomas Cort struct numbering_property {
57*1e69cb63SThomas Cort 	const char * const	name;		/* for diagnostics */
58*1e69cb63SThomas Cort 	numbering_type		type;		/* numbering type */
59*1e69cb63SThomas Cort 	regex_t			expr;		/* for type == number_regex */
60*1e69cb63SThomas Cort };
61*1e69cb63SThomas Cort 
62*1e69cb63SThomas Cort /* line numbering formats */
63*1e69cb63SThomas Cort #define FORMAT_LN	"%-*d"	/* left justified, leading zeros suppressed */
64*1e69cb63SThomas Cort #define FORMAT_RN	"%*d"	/* right justified, leading zeros suppressed */
65*1e69cb63SThomas Cort #define FORMAT_RZ	"%0*d"	/* right justified, leading zeros kept */
66*1e69cb63SThomas Cort 
67*1e69cb63SThomas Cort #define FOOTER		0
68*1e69cb63SThomas Cort #define BODY		1
69*1e69cb63SThomas Cort #define HEADER		2
70*1e69cb63SThomas Cort #define NP_LAST		HEADER
71*1e69cb63SThomas Cort 
72*1e69cb63SThomas Cort static struct numbering_property numbering_properties[NP_LAST + 1] = {
73*1e69cb63SThomas Cort 	{ "footer",	number_none,	{ 0, 0, 0, 0 } },
74*1e69cb63SThomas Cort 	{ "body",	number_nonempty, { 0, 0, 0, 0 } },
75*1e69cb63SThomas Cort 	{ "header",	number_none,	{ 0, 0, 0, 0 } },
76*1e69cb63SThomas Cort };
77*1e69cb63SThomas Cort 
78*1e69cb63SThomas Cort #define max(a, b)	((a) > (b) ? (a) : (b))
79*1e69cb63SThomas Cort 
80*1e69cb63SThomas Cort /*
81*1e69cb63SThomas Cort  * Maximum number of characters required for a decimal representation of a
82*1e69cb63SThomas Cort  * (signed) int; courtesy of tzcode.
83*1e69cb63SThomas Cort  */
84*1e69cb63SThomas Cort #define INT_STRLEN_MAXIMUM \
85*1e69cb63SThomas Cort 	((sizeof (int) * CHAR_BIT - 1) * 302 / 1000 + 2)
86*1e69cb63SThomas Cort 
87*1e69cb63SThomas Cort static void	filter(void);
88*1e69cb63SThomas Cort static void	parse_numbering(const char *, int);
89*1e69cb63SThomas Cort static void	usage(void) __attribute__((__noreturn__));
90*1e69cb63SThomas Cort 
91*1e69cb63SThomas Cort /*
92*1e69cb63SThomas Cort  * Pointer to dynamically allocated input line buffer, and its size.
93*1e69cb63SThomas Cort  */
94*1e69cb63SThomas Cort static char *buffer;
95*1e69cb63SThomas Cort static size_t buffersize;
96*1e69cb63SThomas Cort 
97*1e69cb63SThomas Cort /*
98*1e69cb63SThomas Cort  * Dynamically allocated buffer suitable for string representation of ints.
99*1e69cb63SThomas Cort  */
100*1e69cb63SThomas Cort static char *intbuffer;
101*1e69cb63SThomas Cort static size_t intbuffersize;
102*1e69cb63SThomas Cort 
103*1e69cb63SThomas Cort /*
104*1e69cb63SThomas Cort  * Configurable parameters.
105*1e69cb63SThomas Cort  */
106*1e69cb63SThomas Cort /* delimiter characters that indicate the start of a logical page section */
107*1e69cb63SThomas Cort static char delim[2] = { '\\', ':' };
108*1e69cb63SThomas Cort 
109*1e69cb63SThomas Cort /* line numbering format */
110*1e69cb63SThomas Cort static const char *format = FORMAT_RN;
111*1e69cb63SThomas Cort 
112*1e69cb63SThomas Cort /* increment value used to number logical page lines */
113*1e69cb63SThomas Cort static int incr = 1;
114*1e69cb63SThomas Cort 
115*1e69cb63SThomas Cort /* number of adjacent blank lines to be considered (and numbered) as one */
116*1e69cb63SThomas Cort static unsigned int nblank = 1;
117*1e69cb63SThomas Cort 
118*1e69cb63SThomas Cort /* whether to restart numbering at logical page delimiters */
119*1e69cb63SThomas Cort static int restart = 1;
120*1e69cb63SThomas Cort 
121*1e69cb63SThomas Cort /* characters used in separating the line number and the corrsp. text line */
122*1e69cb63SThomas Cort static const char *sep = "\t";
123*1e69cb63SThomas Cort 
124*1e69cb63SThomas Cort /* initial value used to number logical page lines */
125*1e69cb63SThomas Cort static int startnum = 1;
126*1e69cb63SThomas Cort 
127*1e69cb63SThomas Cort /* number of characters to be used for the line number */
128*1e69cb63SThomas Cort /* should be unsigned but required signed by `*' precision conversion */
129*1e69cb63SThomas Cort static int width = 6;
130*1e69cb63SThomas Cort 
131*1e69cb63SThomas Cort 
132*1e69cb63SThomas Cort int
main(int argc,char * argv[])133*1e69cb63SThomas Cort main(int argc, char *argv[])
134*1e69cb63SThomas Cort {
135*1e69cb63SThomas Cort 	int c;
136*1e69cb63SThomas Cort 	long val;
137*1e69cb63SThomas Cort 	unsigned long uval;
138*1e69cb63SThomas Cort 	char *ep;
139*1e69cb63SThomas Cort 
140*1e69cb63SThomas Cort 	(void)setlocale(LC_ALL, "");
141*1e69cb63SThomas Cort 
142*1e69cb63SThomas Cort 	/*
143*1e69cb63SThomas Cort 	 * Note: this implementation strictly conforms to the XBD Utility
144*1e69cb63SThomas Cort 	 * Syntax Guidelines and does not permit the optional `file' operand
145*1e69cb63SThomas Cort 	 * to be intermingled with the options, which is defined in the
146*1e69cb63SThomas Cort 	 * XCU specification (Issue 5) but declared an obsolescent feature that
147*1e69cb63SThomas Cort 	 * will be removed from a future issue.  It shouldn't matter, though.
148*1e69cb63SThomas Cort 	 */
149*1e69cb63SThomas Cort 	while ((c = getopt(argc, argv, "pb:d:f:h:i:l:n:s:v:w:")) != -1) {
150*1e69cb63SThomas Cort 		switch (c) {
151*1e69cb63SThomas Cort 		case 'p':
152*1e69cb63SThomas Cort 			restart = 0;
153*1e69cb63SThomas Cort 			break;
154*1e69cb63SThomas Cort 		case 'b':
155*1e69cb63SThomas Cort 			parse_numbering(optarg, BODY);
156*1e69cb63SThomas Cort 			break;
157*1e69cb63SThomas Cort 		case 'd':
158*1e69cb63SThomas Cort 			if (optarg[0] != '\0')
159*1e69cb63SThomas Cort 				delim[0] = optarg[0];
160*1e69cb63SThomas Cort 			if (optarg[1] != '\0')
161*1e69cb63SThomas Cort 				delim[1] = optarg[1];
162*1e69cb63SThomas Cort 			/* at most two delimiter characters */
163*1e69cb63SThomas Cort 			if (optarg[2] != '\0') {
164*1e69cb63SThomas Cort 				errx(EXIT_FAILURE,
165*1e69cb63SThomas Cort 				    "invalid delim argument -- %s",
166*1e69cb63SThomas Cort 				    optarg);
167*1e69cb63SThomas Cort 				/* NOTREACHED */
168*1e69cb63SThomas Cort 			}
169*1e69cb63SThomas Cort 			break;
170*1e69cb63SThomas Cort 		case 'f':
171*1e69cb63SThomas Cort 			parse_numbering(optarg, FOOTER);
172*1e69cb63SThomas Cort 			break;
173*1e69cb63SThomas Cort 		case 'h':
174*1e69cb63SThomas Cort 			parse_numbering(optarg, HEADER);
175*1e69cb63SThomas Cort 			break;
176*1e69cb63SThomas Cort 		case 'i':
177*1e69cb63SThomas Cort 			errno = 0;
178*1e69cb63SThomas Cort 			val = strtol(optarg, &ep, 10);
179*1e69cb63SThomas Cort 			if ((ep != NULL && *ep != '\0') ||
180*1e69cb63SThomas Cort 			 ((val == LONG_MIN || val == LONG_MAX) && errno != 0))
181*1e69cb63SThomas Cort 				errx(EXIT_FAILURE,
182*1e69cb63SThomas Cort 				    "invalid incr argument -- %s", optarg);
183*1e69cb63SThomas Cort 			incr = (int)val;
184*1e69cb63SThomas Cort 			break;
185*1e69cb63SThomas Cort 		case 'l':
186*1e69cb63SThomas Cort 			errno = 0;
187*1e69cb63SThomas Cort 			uval = strtoul(optarg, &ep, 10);
188*1e69cb63SThomas Cort 			if ((ep != NULL && *ep != '\0') ||
189*1e69cb63SThomas Cort 			    (uval == ULONG_MAX && errno != 0))
190*1e69cb63SThomas Cort 				errx(EXIT_FAILURE,
191*1e69cb63SThomas Cort 				    "invalid num argument -- %s", optarg);
192*1e69cb63SThomas Cort 			nblank = (unsigned int)uval;
193*1e69cb63SThomas Cort 			break;
194*1e69cb63SThomas Cort 		case 'n':
195*1e69cb63SThomas Cort 			if (strcmp(optarg, "ln") == 0) {
196*1e69cb63SThomas Cort 				format = FORMAT_LN;
197*1e69cb63SThomas Cort 			} else if (strcmp(optarg, "rn") == 0) {
198*1e69cb63SThomas Cort 				format = FORMAT_RN;
199*1e69cb63SThomas Cort 			} else if (strcmp(optarg, "rz") == 0) {
200*1e69cb63SThomas Cort 				format = FORMAT_RZ;
201*1e69cb63SThomas Cort 			} else
202*1e69cb63SThomas Cort 				errx(EXIT_FAILURE,
203*1e69cb63SThomas Cort 				    "illegal format -- %s", optarg);
204*1e69cb63SThomas Cort 			break;
205*1e69cb63SThomas Cort 		case 's':
206*1e69cb63SThomas Cort 			sep = optarg;
207*1e69cb63SThomas Cort 			break;
208*1e69cb63SThomas Cort 		case 'v':
209*1e69cb63SThomas Cort 			errno = 0;
210*1e69cb63SThomas Cort 			val = strtol(optarg, &ep, 10);
211*1e69cb63SThomas Cort 			if ((ep != NULL && *ep != '\0') ||
212*1e69cb63SThomas Cort 			 ((val == LONG_MIN || val == LONG_MAX) && errno != 0))
213*1e69cb63SThomas Cort 				errx(EXIT_FAILURE,
214*1e69cb63SThomas Cort 				    "invalid startnum value -- %s", optarg);
215*1e69cb63SThomas Cort 			startnum = (int)val;
216*1e69cb63SThomas Cort 			break;
217*1e69cb63SThomas Cort 		case 'w':
218*1e69cb63SThomas Cort 			errno = 0;
219*1e69cb63SThomas Cort 			val = strtol(optarg, &ep, 10);
220*1e69cb63SThomas Cort 			if ((ep != NULL && *ep != '\0') ||
221*1e69cb63SThomas Cort 			 ((val == LONG_MIN || val == LONG_MAX) && errno != 0))
222*1e69cb63SThomas Cort 				errx(EXIT_FAILURE,
223*1e69cb63SThomas Cort 				    "invalid width value -- %s", optarg);
224*1e69cb63SThomas Cort 			width = (int)val;
225*1e69cb63SThomas Cort 			if (!(width > 0))
226*1e69cb63SThomas Cort 				errx(EXIT_FAILURE,
227*1e69cb63SThomas Cort 				    "width argument must be > 0 -- %d",
228*1e69cb63SThomas Cort 				    width);
229*1e69cb63SThomas Cort 			break;
230*1e69cb63SThomas Cort 		case '?':
231*1e69cb63SThomas Cort 		default:
232*1e69cb63SThomas Cort 			usage();
233*1e69cb63SThomas Cort 			/* NOTREACHED */
234*1e69cb63SThomas Cort 		}
235*1e69cb63SThomas Cort 	}
236*1e69cb63SThomas Cort 	argc -= optind;
237*1e69cb63SThomas Cort 	argv += optind;
238*1e69cb63SThomas Cort 
239*1e69cb63SThomas Cort 	switch (argc) {
240*1e69cb63SThomas Cort 	case 0:
241*1e69cb63SThomas Cort 		break;
242*1e69cb63SThomas Cort 	case 1:
243*1e69cb63SThomas Cort 		if (strcmp(argv[0], "-") != 0 &&
244*1e69cb63SThomas Cort 		    freopen(argv[0], "r", stdin) == NULL)
245*1e69cb63SThomas Cort 			err(EXIT_FAILURE, "Cannot open `%s'", argv[0]);
246*1e69cb63SThomas Cort 		break;
247*1e69cb63SThomas Cort 	default:
248*1e69cb63SThomas Cort 		usage();
249*1e69cb63SThomas Cort 		/* NOTREACHED */
250*1e69cb63SThomas Cort 	}
251*1e69cb63SThomas Cort 
252*1e69cb63SThomas Cort 	/* Determine the maximum input line length to operate on. */
253*1e69cb63SThomas Cort 	if ((val = sysconf(_SC_LINE_MAX)) == -1) /* ignore errno */
254*1e69cb63SThomas Cort 		val = LINE_MAX;
255*1e69cb63SThomas Cort 	/* Allocate sufficient buffer space (including the terminating NUL). */
256*1e69cb63SThomas Cort 	buffersize = (size_t)val + 1;
257*1e69cb63SThomas Cort 	if ((buffer = malloc(buffersize)) == NULL)
258*1e69cb63SThomas Cort 		err(EXIT_FAILURE, "Cannot allocate input line buffer");
259*1e69cb63SThomas Cort 
260*1e69cb63SThomas Cort 	/* Allocate a buffer suitable for preformatting line number. */
261*1e69cb63SThomas Cort 	intbuffersize = max((int)INT_STRLEN_MAXIMUM, width) + 1; /* NUL */
262*1e69cb63SThomas Cort 	if ((intbuffer = malloc(intbuffersize)) == NULL)
263*1e69cb63SThomas Cort 		err(EXIT_FAILURE, "cannot allocate preformatting buffer");
264*1e69cb63SThomas Cort 
265*1e69cb63SThomas Cort 	/* Do the work. */
266*1e69cb63SThomas Cort 	filter();
267*1e69cb63SThomas Cort 
268*1e69cb63SThomas Cort 	return EXIT_SUCCESS;
269*1e69cb63SThomas Cort 	/* NOTREACHED */
270*1e69cb63SThomas Cort }
271*1e69cb63SThomas Cort 
272*1e69cb63SThomas Cort static void
filter(void)273*1e69cb63SThomas Cort filter(void)
274*1e69cb63SThomas Cort {
275*1e69cb63SThomas Cort 	int line;		/* logical line number */
276*1e69cb63SThomas Cort 	int section;		/* logical page section */
277*1e69cb63SThomas Cort 	unsigned int adjblank;	/* adjacent blank lines */
278*1e69cb63SThomas Cort 	int consumed;		/* intbuffer measurement */
279*1e69cb63SThomas Cort 	int donumber, idx;
280*1e69cb63SThomas Cort 
281*1e69cb63SThomas Cort 	adjblank = 0;
282*1e69cb63SThomas Cort 	line = startnum;
283*1e69cb63SThomas Cort 	section = BODY;
284*1e69cb63SThomas Cort #ifdef __GNUC__
285*1e69cb63SThomas Cort 	donumber = 0;	/* avoid bogus `uninitialized' warning */
286*1e69cb63SThomas Cort #endif
287*1e69cb63SThomas Cort 
288*1e69cb63SThomas Cort 	while (fgets(buffer, (int)buffersize, stdin) != NULL) {
289*1e69cb63SThomas Cort 		for (idx = FOOTER; idx <= NP_LAST; idx++) {
290*1e69cb63SThomas Cort 			/* Does it look like a delimiter? */
291*1e69cb63SThomas Cort 			if (buffer[2 * idx + 0] == delim[0] &&
292*1e69cb63SThomas Cort 			    buffer[2 * idx + 1] == delim[1]) {
293*1e69cb63SThomas Cort 				/* Was this the whole line? */
294*1e69cb63SThomas Cort 				if (buffer[2 * idx + 2] == '\n') {
295*1e69cb63SThomas Cort 					section = idx;
296*1e69cb63SThomas Cort 					adjblank = 0;
297*1e69cb63SThomas Cort 					if (restart)
298*1e69cb63SThomas Cort 						line = startnum;
299*1e69cb63SThomas Cort 					goto nextline;
300*1e69cb63SThomas Cort 				}
301*1e69cb63SThomas Cort 			} else {
302*1e69cb63SThomas Cort 				break;
303*1e69cb63SThomas Cort 			}
304*1e69cb63SThomas Cort 		}
305*1e69cb63SThomas Cort 
306*1e69cb63SThomas Cort 		switch (numbering_properties[section].type) {
307*1e69cb63SThomas Cort 		case number_all:
308*1e69cb63SThomas Cort 			/*
309*1e69cb63SThomas Cort 			 * Doing this for number_all only is disputable, but
310*1e69cb63SThomas Cort 			 * the standard expresses an explicit dependency on
311*1e69cb63SThomas Cort 			 * `-b a' etc.
312*1e69cb63SThomas Cort 			 */
313*1e69cb63SThomas Cort 			if (buffer[0] == '\n' && ++adjblank < nblank)
314*1e69cb63SThomas Cort 				donumber = 0;
315*1e69cb63SThomas Cort 			else
316*1e69cb63SThomas Cort 				donumber = 1, adjblank = 0;
317*1e69cb63SThomas Cort 			break;
318*1e69cb63SThomas Cort 		case number_nonempty:
319*1e69cb63SThomas Cort 			donumber = (buffer[0] != '\n');
320*1e69cb63SThomas Cort 			break;
321*1e69cb63SThomas Cort 		case number_none:
322*1e69cb63SThomas Cort 			donumber = 0;
323*1e69cb63SThomas Cort 			break;
324*1e69cb63SThomas Cort 		case number_regex:
325*1e69cb63SThomas Cort 			donumber =
326*1e69cb63SThomas Cort 			    (regexec(&numbering_properties[section].expr,
327*1e69cb63SThomas Cort 			    buffer, 0, NULL, 0) == 0);
328*1e69cb63SThomas Cort 			break;
329*1e69cb63SThomas Cort 		}
330*1e69cb63SThomas Cort 
331*1e69cb63SThomas Cort 		if (donumber) {
332*1e69cb63SThomas Cort 			consumed = snprintf(intbuffer, intbuffersize, format,
333*1e69cb63SThomas Cort 			    width, line);
334*1e69cb63SThomas Cort 			(void)printf("%s",
335*1e69cb63SThomas Cort 			    intbuffer + max(0, consumed - width));
336*1e69cb63SThomas Cort 			line += incr;
337*1e69cb63SThomas Cort 		} else {
338*1e69cb63SThomas Cort 			(void)printf("%*s", width, "");
339*1e69cb63SThomas Cort 		}
340*1e69cb63SThomas Cort 		(void)printf("%s%s", sep, buffer);
341*1e69cb63SThomas Cort 
342*1e69cb63SThomas Cort 		if (ferror(stdout))
343*1e69cb63SThomas Cort 			err(EXIT_FAILURE, "output error");
344*1e69cb63SThomas Cort nextline:
345*1e69cb63SThomas Cort 		;
346*1e69cb63SThomas Cort 	}
347*1e69cb63SThomas Cort 
348*1e69cb63SThomas Cort 	if (ferror(stdin))
349*1e69cb63SThomas Cort 		err(EXIT_FAILURE, "input error");
350*1e69cb63SThomas Cort }
351*1e69cb63SThomas Cort 
352*1e69cb63SThomas Cort /*
353*1e69cb63SThomas Cort  * Various support functions.
354*1e69cb63SThomas Cort  */
355*1e69cb63SThomas Cort 
356*1e69cb63SThomas Cort static void
parse_numbering(const char * argstr,int section)357*1e69cb63SThomas Cort parse_numbering(const char *argstr, int section)
358*1e69cb63SThomas Cort {
359*1e69cb63SThomas Cort 	int error;
360*1e69cb63SThomas Cort 	char errorbuf[NL_TEXTMAX];
361*1e69cb63SThomas Cort 
362*1e69cb63SThomas Cort 	switch (argstr[0]) {
363*1e69cb63SThomas Cort 	case 'a':
364*1e69cb63SThomas Cort 		numbering_properties[section].type = number_all;
365*1e69cb63SThomas Cort 		break;
366*1e69cb63SThomas Cort 	case 'n':
367*1e69cb63SThomas Cort 		numbering_properties[section].type = number_none;
368*1e69cb63SThomas Cort 		break;
369*1e69cb63SThomas Cort 	case 't':
370*1e69cb63SThomas Cort 		numbering_properties[section].type = number_nonempty;
371*1e69cb63SThomas Cort 		break;
372*1e69cb63SThomas Cort 	case 'p':
373*1e69cb63SThomas Cort 		/* If there was a previous expression, throw it away. */
374*1e69cb63SThomas Cort 		if (numbering_properties[section].type == number_regex)
375*1e69cb63SThomas Cort 			regfree(&numbering_properties[section].expr);
376*1e69cb63SThomas Cort 		else
377*1e69cb63SThomas Cort 			numbering_properties[section].type = number_regex;
378*1e69cb63SThomas Cort 
379*1e69cb63SThomas Cort 		/* Compile/validate the supplied regular expression. */
380*1e69cb63SThomas Cort 		if ((error = regcomp(&numbering_properties[section].expr,
381*1e69cb63SThomas Cort 		    &argstr[1], REG_NEWLINE|REG_NOSUB)) != 0) {
382*1e69cb63SThomas Cort 			(void)regerror(error,
383*1e69cb63SThomas Cort 			    &numbering_properties[section].expr,
384*1e69cb63SThomas Cort 			    errorbuf, sizeof (errorbuf));
385*1e69cb63SThomas Cort 			errx(EXIT_FAILURE,
386*1e69cb63SThomas Cort 			    "%s expr: %s -- %s",
387*1e69cb63SThomas Cort 			    numbering_properties[section].name, errorbuf,
388*1e69cb63SThomas Cort 			    &argstr[1]);
389*1e69cb63SThomas Cort 		}
390*1e69cb63SThomas Cort 		break;
391*1e69cb63SThomas Cort 	default:
392*1e69cb63SThomas Cort 		errx(EXIT_FAILURE,
393*1e69cb63SThomas Cort 		    "illegal %s line numbering type -- %s",
394*1e69cb63SThomas Cort 		    numbering_properties[section].name, argstr);
395*1e69cb63SThomas Cort 		exit(EXIT_FAILURE);
396*1e69cb63SThomas Cort 	}
397*1e69cb63SThomas Cort }
398*1e69cb63SThomas Cort 
399*1e69cb63SThomas Cort static void
usage(void)400*1e69cb63SThomas Cort usage(void)
401*1e69cb63SThomas Cort {
402*1e69cb63SThomas Cort 	(void)fprintf(stderr, "Usage: %s [-p] [-b type] [-d delim] [-f type] "
403*1e69cb63SThomas Cort 	    "[-h type] [-i incr] [-l num]\n\t[-n format] [-s sep] "
404*1e69cb63SThomas Cort 	    "[-v startnum] [-w width] [file]\n", getprogname());
405*1e69cb63SThomas Cort 	exit(EXIT_FAILURE);
406*1e69cb63SThomas Cort }
407