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