nl.c revision 92921
1169691Skan/*- 2169691Skan * Copyright (c) 1999 The NetBSD Foundation, Inc. 3169691Skan * All rights reserved. 4169691Skan * 5169691Skan * This code is derived from software contributed to The NetBSD Foundation 6169691Skan * by Klaus Klein. 7169691Skan * 8169691Skan * Redistribution and use in source and binary forms, with or without 9169691Skan * modification, are permitted provided that the following conditions 10169691Skan * are met: 11169691Skan * 1. Redistributions of source code must retain the above copyright 12169691Skan * notice, this list of conditions and the following disclaimer. 13169691Skan * 2. Redistributions in binary form must reproduce the above copyright 14169691Skan * notice, this list of conditions and the following disclaimer in the 15169691Skan * documentation and/or other materials provided with the distribution. 16169691Skan * 3. All advertising materials mentioning features or use of this software 17169691Skan * must display the following acknowledgement: 18169691Skan * This product includes software developed by the NetBSD 19169691Skan * Foundation, Inc. and its contributors. 20169691Skan * 4. Neither the name of The NetBSD Foundation nor the names of its 21169691Skan * contributors may be used to endorse or promote products derived 22169691Skan * from this software without specific prior written permission. 23169691Skan * 24169691Skan * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 25169691Skan * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 26169691Skan * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 27169691Skan * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 28169691Skan * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 29169691Skan * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 30169691Skan * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 31169691Skan * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 32169691Skan * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 33169691Skan * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 34169691Skan * POSSIBILITY OF SUCH DAMAGE. 35169691Skan */ 36169691Skan 37169691Skan#include <sys/cdefs.h> 38169691Skan#ifndef lint 39169691Skan__COPYRIGHT( 40169691Skan"@(#) Copyright (c) 1999\ 41169691Skan The NetBSD Foundation, Inc. All rights reserved."); 42169691Skan__RCSID("$FreeBSD: head/usr.bin/nl/nl.c 92921 2002-03-22 01:33:25Z imp $"); 43169691Skan#endif 44169691Skan 45169691Skan#include <sys/types.h> 46169691Skan 47169691Skan#include <errno.h> 48169691Skan#include <limits.h> 49169691Skan#include <locale.h> 50169691Skan#include <regex.h> 51169691Skan#include <stdio.h> 52169691Skan#include <stdlib.h> 53169691Skan#include <string.h> 54169691Skan#include <unistd.h> 55169691Skan 56169691Skantypedef enum { 57169691Skan number_all, /* number all lines */ 58169691Skan number_nonempty, /* number non-empty lines */ 59169691Skan number_none, /* no line numbering */ 60169691Skan number_regex /* number lines matching regular expression */ 61169691Skan} numbering_type; 62169691Skan 63169691Skanstruct numbering_property { 64169691Skan const char * const name; /* for diagnostics */ 65169691Skan numbering_type type; /* numbering type */ 66169691Skan regex_t expr; /* for type == number_regex */ 67169691Skan}; 68169691Skan 69169691Skan/* line numbering formats */ 70169691Skan#define FORMAT_LN "%-*d" /* left justified, leading zeros suppressed */ 71169691Skan#define FORMAT_RN "%*d" /* right justified, leading zeros suppressed */ 72169691Skan#define FORMAT_RZ "%0*d" /* right justified, leading zeros kept */ 73169691Skan 74169691Skan#define FOOTER 0 75169691Skan#define BODY 1 76169691Skan#define HEADER 2 77169691Skan#define NP_LAST HEADER 78169691Skan 79169691Skanstatic struct numbering_property numbering_properties[NP_LAST + 1] = { 80169691Skan { "footer", number_none }, 81169691Skan { "body", number_nonempty }, 82169691Skan { "header", number_none } 83169691Skan}; 84169691Skan 85169691Skan#define max(a, b) ((a) > (b) ? (a) : (b)) 86169691Skan 87169691Skan/* 88169691Skan * Maximum number of characters required for a decimal representation of a 89169691Skan * (signed) int; courtesy of tzcode. 90169691Skan */ 91169691Skan#define INT_STRLEN_MAXIMUM \ 92169691Skan ((sizeof (int) * CHAR_BIT - 1) * 302 / 1000 + 2) 93169691Skan 94169691Skanstatic void filter(void); 95169691Skanint main(int, char *[]); 96169691Skanstatic void parse_numbering(const char *, int); 97169691Skanstatic void usage(void); 98169691Skan 99169691Skan/* 100169691Skan * Pointer to dynamically allocated input line buffer, and its size. 101169691Skan */ 102169691Skanstatic char *buffer; 103169691Skanstatic size_t buffersize; 104169691Skan 105169691Skan/* 106169691Skan * Dynamically allocated buffer suitable for string representation of ints. 107169691Skan */ 108169691Skanstatic char *intbuffer; 109169691Skan 110169691Skan/* 111169691Skan * Configurable parameters. 112169691Skan */ 113169691Skan/* delimiter characters that indicate the start of a logical page section */ 114169691Skanstatic char delim[2] = { '\\', ':' }; 115169691Skan 116169691Skan/* line numbering format */ 117169691Skanstatic const char *format = FORMAT_RN; 118169691Skan 119169691Skan/* increment value used to number logical page lines */ 120169691Skanstatic int incr = 1; 121169691Skan 122169691Skan/* number of adjacent blank lines to be considered (and numbered) as one */ 123169691Skanstatic unsigned int nblank = 1; 124169691Skan 125169691Skan/* whether to restart numbering at logical page delimiters */ 126169691Skanstatic int restart = 1; 127169691Skan 128169691Skan/* characters used in separating the line number and the corrsp. text line */ 129169691Skanstatic const char *sep = "\t"; 130169691Skan 131169691Skan/* initial value used to number logical page lines */ 132169691Skanstatic int startnum = 1; 133169691Skan 134169691Skan/* number of characters to be used for the line number */ 135169691Skan/* should be unsigned but required signed by `*' precision conversion */ 136169691Skanstatic int width = 6; 137169691Skan 138169691Skan 139169691Skanint 140169691Skanmain(argc, argv) 141169691Skan int argc; 142169691Skan char *argv[]; 143169691Skan{ 144169691Skan int c; 145169691Skan long val; 146169691Skan unsigned long uval; 147169691Skan char *ep; 148169691Skan size_t intbuffersize; 149169691Skan 150169691Skan (void)setlocale(LC_ALL, ""); 151169691Skan 152169691Skan /* 153169691Skan * Note: this implementation strictly conforms to the XBD Utility 154169691Skan * Syntax Guidelines and does not permit the optional `file' operand 155169691Skan * to be intermingled with the options, which is defined in the 156169691Skan * XCU specification (Issue 5) but declared an obsolescent feature that 157169691Skan * will be removed from a future issue. It shouldn't matter, though. 158169691Skan */ 159169691Skan while ((c = getopt(argc, argv, "pb:d:f:h:i:l:n:s:v:w:")) != -1) { 160169691Skan switch (c) { 161169691Skan case 'p': 162169691Skan restart = 0; 163169691Skan break; 164169691Skan case 'b': 165169691Skan parse_numbering(optarg, BODY); 166169691Skan break; 167169691Skan case 'd': 168169691Skan if (optarg[0] != '\0') 169169691Skan delim[0] = optarg[0]; 170169691Skan if (optarg[1] != '\0') 171169691Skan delim[1] = optarg[1]; 172169691Skan /* at most two delimiter characters */ 173169691Skan if (optarg[2] != '\0') { 174169691Skan (void)fprintf(stderr, 175169691Skan "nl: invalid delim argument -- %s\n", 176169691Skan optarg); 177169691Skan exit(EXIT_FAILURE); 178169691Skan /* NOTREACHED */ 179169691Skan } 180169691Skan break; 181169691Skan case 'f': 182169691Skan parse_numbering(optarg, FOOTER); 183169691Skan break; 184169691Skan case 'h': 185169691Skan parse_numbering(optarg, HEADER); 186169691Skan break; 187169691Skan case 'i': 188169691Skan errno = 0; 189169691Skan val = strtol(optarg, &ep, 10); 190169691Skan if ((ep != NULL && *ep != '\0') || 191169691Skan ((val == LONG_MIN || val == LONG_MAX) && errno != 0)) { 192169691Skan (void)fprintf(stderr, 193169691Skan "invalid incr argument -- %s\n", optarg); 194169691Skan exit(EXIT_FAILURE); 195169691Skan } 196169691Skan incr = (int)val; 197169691Skan break; 198169691Skan case 'l': 199169691Skan errno = 0; 200169691Skan uval = strtoul(optarg, &ep, 10); 201169691Skan if ((ep != NULL && *ep != '\0') || 202169691Skan (uval == ULONG_MAX && errno != 0)) { 203169691Skan (void)fprintf(stderr, 204169691Skan "invalid num argument -- %s\n", optarg); 205169691Skan exit(EXIT_FAILURE); 206169691Skan } 207169691Skan nblank = (unsigned int)uval; 208169691Skan break; 209169691Skan case 'n': 210169691Skan if (strcmp(optarg, "ln") == 0) { 211169691Skan format = FORMAT_LN; 212169691Skan } else if (strcmp(optarg, "rn") == 0) { 213169691Skan format = FORMAT_RN; 214169691Skan } else if (strcmp(optarg, "rz") == 0) { 215169691Skan format = FORMAT_RZ; 216169691Skan } else { 217169691Skan (void)fprintf(stderr, 218169691Skan "nl: illegal format -- %s\n", optarg); 219169691Skan exit(EXIT_FAILURE); 220169691Skan } 221169691Skan break; 222169691Skan case 's': 223169691Skan sep = optarg; 224169691Skan break; 225169691Skan case 'v': 226169691Skan errno = 0; 227169691Skan val = strtol(optarg, &ep, 10); 228169691Skan if ((ep != NULL && *ep != '\0') || 229169691Skan ((val == LONG_MIN || val == LONG_MAX) && errno != 0)) { 230169691Skan (void)fprintf(stderr, 231169691Skan "invalid startnum value -- %s\n", optarg); 232169691Skan exit(EXIT_FAILURE); 233169691Skan } 234169691Skan startnum = (int)val; 235169691Skan break; 236169691Skan case 'w': 237169691Skan errno = 0; 238169691Skan val = strtol(optarg, &ep, 10); 239169691Skan if ((ep != NULL && *ep != '\0') || 240169691Skan ((val == LONG_MIN || val == LONG_MAX) && errno != 0)) { 241169691Skan (void)fprintf(stderr, 242169691Skan "invalid width value -- %s\n", optarg); 243169691Skan exit(EXIT_FAILURE); 244169691Skan } 245169691Skan width = (int)val; 246169691Skan if (!(width > 0)) { 247169691Skan (void)fprintf(stderr, 248169691Skan "nl: width argument must be > 0 -- %d\n", 249169691Skan width); 250169691Skan exit(EXIT_FAILURE); 251169691Skan } 252169691Skan break; 253169691Skan case '?': 254169691Skan default: 255169691Skan usage(); 256169691Skan /* NOTREACHED */ 257169691Skan } 258169691Skan } 259169691Skan argc -= optind; 260169691Skan argv += optind; 261169691Skan 262169691Skan switch (argc) { 263169691Skan case 0: 264169691Skan break; 265169691Skan case 1: 266169691Skan if (freopen(argv[0], "r", stdin) == NULL) { 267169691Skan perror(argv[0]); 268169691Skan exit(EXIT_FAILURE); 269169691Skan } 270169691Skan break; 271169691Skan default: 272169691Skan usage(); 273169691Skan /* NOTREACHED */ 274169691Skan } 275169691Skan 276169691Skan /* Determine the maximum input line length to operate on. */ 277169691Skan if ((val = sysconf(_SC_LINE_MAX)) == -1) /* ignore errno */ 278169691Skan val = LINE_MAX; 279169691Skan /* Allocate sufficient buffer space (including the terminating NUL). */ 280169691Skan buffersize = (size_t)val + 1; 281169691Skan if ((buffer = malloc(buffersize)) == NULL) { 282169691Skan perror("cannot allocate input line buffer"); 283169691Skan exit(EXIT_FAILURE); 284169691Skan } 285169691Skan 286169691Skan /* Allocate a buffer suitable for preformatting line number. */ 287169691Skan intbuffersize = max(INT_STRLEN_MAXIMUM, width) + 1; /* NUL */ 288169691Skan if ((intbuffer = malloc(intbuffersize)) == NULL) { 289169691Skan perror("cannot allocate preformatting buffer"); 290169691Skan exit(EXIT_FAILURE); 291169691Skan } 292169691Skan 293169691Skan /* Do the work. */ 294 filter(); 295 296 exit(EXIT_SUCCESS); 297 /* NOTREACHED */ 298} 299 300static void 301filter() 302{ 303 int line; /* logical line number */ 304 int section; /* logical page section */ 305 unsigned int adjblank; /* adjacent blank lines */ 306 int consumed; /* intbuffer measurement */ 307 int donumber, idx; 308 309 adjblank = 0; 310 line = startnum; 311 section = BODY; 312#ifdef __GNUC__ 313 (void)&donumber; /* avoid bogus `uninitialized' warning */ 314#endif 315 316 while (fgets(buffer, (int)buffersize, stdin) != NULL) { 317 for (idx = FOOTER; idx <= NP_LAST; idx++) { 318 /* Does it look like a delimiter? */ 319 if (buffer[2 * idx + 0] == delim[0] && 320 buffer[2 * idx + 1] == delim[1]) { 321 /* Was this the whole line? */ 322 if (buffer[2 * idx + 2] == '\n') { 323 section = idx; 324 adjblank = 0; 325 if (restart) 326 line = startnum; 327 goto nextline; 328 } 329 } else { 330 break; 331 } 332 } 333 334 switch (numbering_properties[section].type) { 335 case number_all: 336 /* 337 * Doing this for number_all only is disputable, but 338 * the standard expresses an explicit dependency on 339 * `-b a' etc. 340 */ 341 if (buffer[0] == '\n' && ++adjblank < nblank) 342 donumber = 0; 343 else 344 donumber = 1, adjblank = 0; 345 break; 346 case number_nonempty: 347 donumber = (buffer[0] != '\n'); 348 break; 349 case number_none: 350 donumber = 0; 351 break; 352 case number_regex: 353 donumber = 354 (regexec(&numbering_properties[section].expr, 355 buffer, 0, NULL, 0) == 0); 356 break; 357 } 358 359 if (donumber) { 360 /* Note: sprintf() is safe here. */ 361 consumed = sprintf(intbuffer, format, width, line); 362 (void)printf("%s", 363 intbuffer + max(0, consumed - width)); 364 line += incr; 365 } else { 366 (void)printf("%*s", width, ""); 367 } 368 (void)printf("%s%s", sep, buffer); 369 370 if (ferror(stdout)) { 371 perror("output error"); 372 exit(EXIT_FAILURE); 373 } 374nextline: 375 ; 376 } 377 378 if (ferror(stdin)) { 379 perror("input error"); 380 exit(EXIT_FAILURE); 381 } 382} 383 384/* 385 * Various support functions. 386 */ 387 388static void 389parse_numbering(argstr, section) 390 const char *argstr; 391 int section; 392{ 393 int error; 394 char errorbuf[NL_TEXTMAX]; 395 396 switch (argstr[0]) { 397 case 'a': 398 numbering_properties[section].type = number_all; 399 break; 400 case 'n': 401 numbering_properties[section].type = number_none; 402 break; 403 case 't': 404 numbering_properties[section].type = number_nonempty; 405 break; 406 case 'p': 407 /* If there was a previous expression, throw it away. */ 408 if (numbering_properties[section].type == number_regex) 409 regfree(&numbering_properties[section].expr); 410 else 411 numbering_properties[section].type = number_regex; 412 413 /* Compile/validate the supplied regular expression. */ 414 if ((error = regcomp(&numbering_properties[section].expr, 415 &argstr[1], REG_NEWLINE|REG_NOSUB)) != 0) { 416 (void)regerror(error, 417 &numbering_properties[section].expr, 418 errorbuf, sizeof (errorbuf)); 419 (void)fprintf(stderr, 420 "nl: %s expr: %s -- %s\n", 421 numbering_properties[section].name, errorbuf, 422 &argstr[1]); 423 exit(EXIT_FAILURE); 424 } 425 break; 426 default: 427 (void)fprintf(stderr, 428 "nl: illegal %s line numbering type -- %s\n", 429 numbering_properties[section].name, argstr); 430 exit(EXIT_FAILURE); 431 } 432} 433 434static void 435usage() 436{ 437 438 (void)fprintf(stderr, "usage: nl [-p] [-b type] [-d delim] [-f type] \ 439[-h type] [-i incr] [-l num]\n\t[-n format] [-s sep] [-v startnum] [-w width] \ 440[file]\n"); 441 exit(EXIT_FAILURE); 442} 443