1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 1995 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 28 /* All Rights Reserved */ 29 30 31 #include <locale.h> 32 #include <regexpr.h> 33 #include <stdio.h> 34 #include <stdlib.h> 35 #include <string.h> 36 #include <errno.h> 37 #include <wchar.h> 38 #include <wctype.h> 39 #include <limits.h> 40 41 #define EXPSIZ 512 42 43 #ifdef XPG4 44 #define USAGE "usage: nl [-p] [-b type] [-d delim] [ -f type] " \ 45 "[-h type] [-i incr] [-l num] [-n format]\n" \ 46 "[-s sep] [-v startnum] [-w width] [file]\n" 47 #else 48 #define USAGE "usage: nl [-p] [-btype] [-ddelim] [ -ftype] " \ 49 "[-htype] [-iincr] [-lnum] [-nformat] [-ssep] " \ 50 "[-vstartnum] [-wwidth] [file]\n" 51 #endif 52 53 #ifdef u370 54 int nbra, sed; /* u370 - not used in nl.c, but extern in regexp.h */ 55 #endif 56 static int width = 6; /* Declare default width of number */ 57 static char nbuf[100]; /* Declare bufsize used in convert/pad/cnt routines */ 58 static char *bexpbuf; /* Declare the regexp buf */ 59 static char *hexpbuf; /* Declare the regexp buf */ 60 static char *fexpbuf; /* Declare the regexp buf */ 61 static char delim1 = '\\'; 62 static char delim2 = ':'; /* Default delimiters. */ 63 static char pad = ' '; /* Declare the default pad for numbers */ 64 static char *s; /* Declare the temp array for args */ 65 static char s1[EXPSIZ]; /* Declare the conversion array */ 66 static char format = 'n'; /* Declare the format of numbers to be rt just */ 67 static int q = 2; /* Initialize arg pointer to drop 1st 2 chars */ 68 static int k; /* Declare var for return of convert */ 69 static int r; /* Declare the arg array ptr for string args */ 70 71 #ifdef XPG4 72 static int convert(int, char *); 73 #else 74 static int convert(char *); 75 #endif 76 static void num(int, int); 77 static void npad(int, char *); 78 #ifdef XPG4 79 static void optmsg(int, char *); 80 #else 81 static void optmsg(char *); 82 #endif 83 static void pnum(int, char *); 84 static void regerr(int); 85 static void usage(); 86 87 extern char *optarg; /* getopt support */ 88 extern int optind; 89 90 int 91 main(int argc, char *argv[]) 92 { 93 register int j; 94 register int i = 0; 95 register char *p; 96 register char header = 'n'; 97 register char body = 't'; 98 register char footer = 'n'; 99 char line[LINE_MAX]; 100 char tempchr; /* Temporary holding variable. */ 101 char swtch = 'n'; 102 char cntck = 'n'; 103 char type; 104 int cnt; /* line counter */ 105 int pass1 = 1; /* First pass flag. 1=pass1, 0=additional passes. */ 106 char sep[EXPSIZ]; 107 char pat[EXPSIZ]; 108 int startcnt = 1; 109 int increment = 1; 110 int blank = 1; 111 int blankctr = 0; 112 int c; 113 int lnt; 114 char last; 115 FILE *iptr = stdin; 116 FILE *optr = stdout; 117 #ifndef XPG4 118 int option_end = 0; 119 #endif 120 121 sep[0] = '\t'; 122 sep[1] = '\0'; 123 124 (void) setlocale(LC_ALL, ""); 125 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ 126 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */ 127 #endif 128 (void) textdomain(TEXT_DOMAIN); 129 130 #ifdef XPG4 131 /* 132 * XPG4: Allow either a space or no space between the 133 * options and their required arguments. 134 */ 135 136 while (argc > 0) { 137 while ((c = getopt(argc, argv, 138 "pb:d:f:h:i:l:n:s:v:w:")) != EOF) { 139 140 switch (c) { 141 case 'h': 142 switch (*optarg) { 143 case 'n': 144 header = 'n'; 145 break; 146 case 't': 147 header = 't'; 148 break; 149 case 'a': 150 header = 'a'; 151 break; 152 case 'p': 153 (void) strcpy(pat, optarg+1); 154 header = 'h'; 155 hexpbuf = 156 compile(pat, NULL, NULL); 157 if (regerrno) 158 regerr(regerrno); 159 break; 160 case '\0': 161 header = 'n'; 162 break; 163 default: 164 optmsg(c, optarg); 165 } 166 break; 167 case 'b': 168 switch (*optarg) { 169 case 't': 170 body = 't'; 171 break; 172 case 'a': 173 body = 'a'; 174 break; 175 case 'n': 176 body = 'n'; 177 break; 178 case 'p': 179 (void) strcpy(pat, optarg+1); 180 body = 'b'; 181 bexpbuf = 182 compile(pat, NULL, NULL); 183 if (regerrno) 184 regerr(regerrno); 185 break; 186 case '\0': 187 body = 't'; 188 break; 189 default: 190 optmsg(c, optarg); 191 } 192 break; 193 case 'f': 194 switch (*optarg) { 195 case 'n': 196 footer = 'n'; 197 break; 198 case 't': 199 footer = 't'; 200 break; 201 case 'a': 202 footer = 'a'; 203 break; 204 case 'p': 205 (void) strcpy(pat, optarg+1); 206 footer = 'f'; 207 fexpbuf = 208 compile(pat, NULL, NULL); 209 if (regerrno) 210 regerr(regerrno); 211 break; 212 case '\0': 213 footer = 'n'; 214 break; 215 default: 216 optmsg(c, optarg); 217 } 218 break; 219 case 'p': 220 if (optarg == (char *)NULL) 221 cntck = 'y'; 222 else 223 optmsg(c, optarg); 224 break; 225 case 'v': 226 if (*optarg == '\0') 227 startcnt = 1; 228 else 229 startcnt = convert(c, optarg); 230 break; 231 case 'i': 232 if (*optarg == '\0') 233 increment = 1; 234 else 235 increment = convert(c, optarg); 236 break; 237 case 'w': 238 if (*optarg == '\0') 239 width = 6; 240 else 241 width = convert(c, optarg); 242 break; 243 case 'l': 244 if (*optarg == '\0') 245 blank = 1; 246 else 247 blank = convert(c, optarg); 248 break; 249 case 'n': 250 switch (*optarg) { 251 case 'l': 252 if (*(optarg+1) == 'n') 253 format = 'l'; 254 else 255 optmsg(c, optarg); 256 break; 257 case 'r': 258 if ((*(optarg+1) == 'n') || 259 (*(optarg+1) == 'z')) 260 format = *(optarg+1); 261 else 262 optmsg(c, optarg); 263 break; 264 case '\0': 265 format = 'n'; 266 break; 267 default: 268 optmsg(c, optarg); 269 break; 270 } 271 break; 272 case 's': 273 (void) strcpy(sep, optarg); 274 break; 275 case 'd': 276 delim1 = *optarg; 277 278 if (*(optarg+1) == '\0') 279 break; 280 delim2 = *(optarg+1); 281 if (*(optarg+2) != '\0') 282 optmsg(c, optarg); 283 break; 284 default: 285 optmsg(c, optarg); 286 } /* end switch char returned from getopt() */ 287 } /* end while getopt */ 288 289 argv += optind; 290 argc -= optind; 291 optind = 0; 292 293 if (argc > 0) { 294 if ((iptr = fopen(argv[0], "r")) == NULL) { 295 (void) fprintf(stderr, "nl: %s: ", argv[0]); 296 perror(""); 297 return (1); 298 } 299 ++argv; 300 --argc; 301 } 302 } /* end while argc > 0 */ 303 /* end XPG4 version of argument parsing */ 304 #else 305 /* 306 * Solaris: For backward compatibility, do not allow a space between the 307 * options and their arguments. Option arguments are optional, 308 * not required as in the XPG4 version of nl. 309 */ 310 for (j = 1; j < argc; j++) { 311 if (argv[j][i] == '-' && (c = argv[j][i + 1])) { 312 if (!option_end) { 313 switch (c) { 314 case 'h': 315 switch (argv[j][i + 2]) { 316 case 'n': 317 header = 'n'; 318 break; 319 case 't': 320 header = 't'; 321 break; 322 case 'a': 323 header = 'a'; 324 break; 325 case 'p': 326 s = argv[j]; 327 q = 3; 328 r = 0; 329 while (s[q] != '\0') { 330 pat[r] = s[q]; 331 r++; 332 q++; 333 } 334 pat[r] = '\0'; 335 header = 'h'; 336 hexpbuf = 337 compile(pat, NULL, NULL); 338 if (regerrno) 339 regerr(regerrno); 340 break; 341 case '\0': 342 header = 'n'; 343 break; 344 default: 345 optmsg(argv[j]); 346 } 347 break; 348 case 'b': 349 switch (argv[j][i + 2]) { 350 case 't': 351 body = 't'; 352 break; 353 case 'a': 354 body = 'a'; 355 break; 356 case 'n': 357 body = 'n'; 358 break; 359 case 'p': 360 s = argv[j]; 361 q = 3; 362 r = 0; 363 while (s[q] != '\0') { 364 pat[r] = s[q]; 365 r++; 366 q++; 367 } 368 pat[r] = '\0'; 369 body = 'b'; 370 bexpbuf = 371 compile(pat, NULL, NULL); 372 if (regerrno) 373 regerr(regerrno); 374 break; 375 case '\0': 376 body = 't'; 377 break; 378 default: 379 optmsg(argv[j]); 380 } 381 break; 382 case 'f': 383 switch (argv[j][i + 2]) { 384 case 'n': 385 footer = 'n'; 386 break; 387 case 't': 388 footer = 't'; 389 break; 390 case 'a': 391 footer = 'a'; 392 break; 393 case 'p': 394 s = argv[j]; 395 q = 3; 396 r = 0; 397 while (s[q] != '\0') { 398 pat[r] = s[q]; 399 r++; 400 q++; 401 } 402 pat[r] = '\0'; 403 footer = 'f'; 404 fexpbuf = 405 compile(pat, NULL, NULL); 406 if (regerrno) 407 regerr(regerrno); 408 break; 409 case '\0': 410 footer = 'n'; 411 break; 412 default: 413 optmsg(argv[j]); 414 } 415 break; 416 case 'p': 417 if (argv[j][i+2] == '\0') 418 cntck = 'y'; 419 else 420 { 421 optmsg(argv[j]); 422 } 423 break; 424 case 'v': 425 if (argv[j][i+2] == '\0') 426 startcnt = 1; 427 else 428 startcnt = convert(argv[j]); 429 break; 430 case 'i': 431 if (argv[j][i+2] == '\0') 432 increment = 1; 433 else 434 increment = convert(argv[j]); 435 break; 436 case 'w': 437 if (argv[j][i+2] == '\0') 438 width = 6; 439 else 440 width = convert(argv[j]); 441 break; 442 case 'l': 443 if (argv[j][i+2] == '\0') 444 blank = 1; 445 else 446 blank = convert(argv[j]); 447 break; 448 case 'n': 449 switch (argv[j][i+2]) { 450 case 'l': 451 if (argv[j][i+3] == 'n') 452 format = 'l'; 453 else 454 { 455 optmsg(argv[j]); 456 } 457 break; 458 case 'r': 459 if ((argv[j][i+3] == 'n') || 460 (argv[j][i+3] == 'z')) 461 format = argv[j][i+3]; 462 else 463 { 464 optmsg(argv[j]); 465 } 466 break; 467 case '\0': 468 format = 'n'; 469 break; 470 default: 471 optmsg(argv[j]); 472 break; 473 } 474 break; 475 case 's': 476 if (argv[j][i + 2] != '\0') { 477 s = argv[j]; 478 q = 2; 479 r = 0; 480 while (s[q] != '\0') { 481 sep[r] = s[q]; 482 r++; 483 q++; 484 } 485 sep[r] = '\0'; 486 } 487 /* else default sep is tab (set above) */ 488 break; 489 case 'd': 490 tempchr = argv[j][i+2]; 491 if (tempchr == '\0')break; 492 delim1 = tempchr; 493 494 tempchr = argv[j][i+3]; 495 if (tempchr == '\0')break; 496 delim2 = tempchr; 497 if (argv[j][i+4] != '\0')optmsg(argv[j]); 498 break; 499 case '-': 500 if (argv[j][i + 2] == '\0') { 501 option_end = 1; 502 break; 503 } 504 /* FALLTHROUGH */ 505 default: 506 optmsg(argv[j]); 507 } 508 } else if ((iptr = fopen(argv[j], "r")) == NULL) { 509 /* end of options, filename starting with '-' */ 510 (void) fprintf(stderr, "nl: %s: ", argv[j]); 511 perror(""); 512 return (1); 513 } 514 } else if ((iptr = fopen(argv[j], "r")) == NULL) { 515 /* filename starting with char other than '-' */ 516 (void) fprintf(stderr, "nl: %s: ", argv[j]); 517 perror(""); 518 return (1); 519 } 520 } /* closing brace of for loop */ 521 /* end Solaris version of argument parsing */ 522 #endif 523 524 /* ON FIRST PASS ONLY, SET LINE COUNTER (cnt) = startcnt & */ 525 /* SET DEFAULT BODY TYPE TO NUMBER ALL LINES. */ 526 if (pass1) { 527 cnt = startcnt; 528 type = body; 529 last = 'b'; 530 pass1 = 0; 531 } 532 533 /* 534 * DO WHILE THERE IS INPUT 535 * CHECK TO SEE IF LINE IS NUMBERED, 536 * IF SO, CALCULATE NUM, PRINT NUM, 537 * THEN OUTPUT SEPERATOR CHAR AND LINE 538 */ 539 540 while ((p = fgets(line, sizeof (line), iptr)) != NULL) { 541 if (p[0] == delim1 && p[1] == delim2) { 542 if (p[2] == delim1 && 543 p[3] == delim2 && 544 p[4] == delim1 && 545 p[5] == delim2 && 546 p[6] == '\n') { 547 if (cntck != 'y') 548 cnt = startcnt; 549 type = header; 550 last = 'h'; 551 swtch = 'y'; 552 } else { 553 if (p[2] == delim1 && p[3] == delim2 && p[4] == '\n') { 554 if (cntck != 'y' && last != 'h') 555 cnt = startcnt; 556 type = body; 557 last = 'b'; 558 swtch = 'y'; 559 } else { 560 if (p[0] == delim1 && p[1] == delim2 && 561 p[2] == '\n') { 562 if (cntck != 'y' && last == 'f') 563 cnt = startcnt; 564 type = footer; 565 last = 'f'; 566 swtch = 'y'; 567 } 568 } 569 } 570 } 571 if (p[0] != '\n') { 572 lnt = strlen(p); 573 if (p[lnt-1] == '\n') 574 p[lnt-1] = '\0'; 575 } 576 577 if (swtch == 'y') { 578 swtch = 'n'; 579 (void) fprintf(optr, "\n"); 580 } else { 581 switch (type) { 582 case 'n': 583 npad(width, sep); 584 break; 585 case 't': 586 /* 587 * XPG4: The wording of Spec 1170 is misleading; 588 * the official interpretation is to number all 589 * non-empty lines, ie: the Solaris code has not 590 * been changed. 591 */ 592 if (p[0] != '\n') { 593 pnum(cnt, sep); 594 cnt += increment; 595 } else { 596 npad(width, sep); 597 } 598 break; 599 case 'a': 600 if (p[0] == '\n') { 601 blankctr++; 602 if (blank == blankctr) { 603 blankctr = 0; 604 pnum(cnt, sep); 605 cnt += increment; 606 } else 607 npad(width, sep); 608 } else { 609 blankctr = 0; 610 pnum(cnt, sep); 611 cnt += increment; 612 } 613 break; 614 case 'b': 615 if (step(p, bexpbuf)) { 616 pnum(cnt, sep); 617 cnt += increment; 618 } else { 619 npad(width, sep); 620 } 621 break; 622 case 'h': 623 if (step(p, hexpbuf)) { 624 pnum(cnt, sep); 625 cnt += increment; 626 } else { 627 npad(width, sep); 628 } 629 break; 630 case 'f': 631 if (step(p, fexpbuf)) { 632 pnum(cnt, sep); 633 cnt += increment; 634 } else { 635 npad(width, sep); 636 } 637 break; 638 } 639 if (p[0] != '\n') 640 p[lnt-1] = '\n'; 641 (void) fprintf(optr, "%s", line); 642 643 } /* Closing brace of "else" */ 644 } /* Closing brace of "while". */ 645 (void) fclose(iptr); 646 647 return (0); 648 } 649 650 /* REGEXP ERR ROUTINE */ 651 652 static void 653 regerr(int c) 654 { 655 (void) fprintf(stderr, gettext( 656 "nl: invalid regular expression: error code %d\n"), c); 657 exit(1); 658 } 659 660 /* CALCULATE NUMBER ROUTINE */ 661 662 static void 663 pnum(int n, char *sep) 664 { 665 register int i; 666 667 if (format == 'z') { 668 pad = '0'; 669 } 670 for (i = 0; i < width; i++) 671 nbuf[i] = pad; 672 num(n, width - 1); 673 if (format == 'l') { 674 while (nbuf[0] == ' ') { 675 for (i = 0; i < width; i++) 676 nbuf[i] = nbuf[i+1]; 677 nbuf[width-1] = ' '; 678 } 679 } 680 (void) printf("%s%s", nbuf, sep); 681 } 682 683 /* IF NUM > 10, THEN USE THIS CALCULATE ROUTINE */ 684 685 static void 686 num(int v, int p) 687 { 688 if (v < 10) 689 nbuf[p] = v + '0'; 690 else { 691 nbuf[p] = (v % 10) + '0'; 692 if (p > 0) 693 num(v / 10, p - 1); 694 } 695 } 696 697 /* CONVERT ARG STRINGS TO STRING ARRAYS */ 698 699 #ifdef XPG4 700 static int 701 convert(int c, char *option_arg) 702 { 703 s = option_arg; 704 q = r = 0; 705 while (s[q] != '\0') { 706 if (s[q] >= '0' && s[q] <= '9') { 707 s1[r] = s[q]; 708 r++; 709 q++; 710 } else 711 optmsg(c, option_arg); 712 } 713 s1[r] = '\0'; 714 k = atoi(s1); 715 return (k); 716 } 717 #else 718 /* Solaris version */ 719 static int 720 convert(char *argv) 721 { 722 s = (char *)argv; 723 q = 2; 724 r = 0; 725 while (s[q] != '\0') { 726 if (s[q] >= '0' && s[q] <= '9') { 727 s1[r] = s[q]; 728 r++; 729 q++; 730 } else { 731 optmsg(argv); 732 } 733 } 734 s1[r] = '\0'; 735 k = atoi(s1); 736 return (k); 737 } 738 #endif 739 740 /* CALCULATE NUM/TEXT SEPRATOR */ 741 742 static void 743 npad(int width, char *sep) 744 { 745 register int i; 746 747 pad = ' '; 748 for (i = 0; i < width; i++) 749 nbuf[i] = pad; 750 (void) printf("%s", nbuf); 751 752 for (i = 0; i < (int)strlen(sep); i++) 753 (void) printf(" "); 754 } 755 756 #ifdef XPG4 757 static void 758 optmsg(int option, char *option_arg) 759 { 760 if (option_arg != (char *)NULL) { 761 (void) fprintf(stderr, gettext( 762 "nl: invalid option (-%c %s)\n"), option, option_arg); 763 } 764 /* else getopt() will print illegal option message */ 765 usage(); 766 } 767 #else 768 /* Solaris version */ 769 static void 770 optmsg(char *option) 771 { 772 (void) fprintf(stderr, gettext("nl: invalid option (%s)\n"), option); 773 usage(); 774 } 775 #endif 776 777 void 778 usage() 779 { 780 (void) fprintf(stderr, gettext(USAGE)); 781 exit(1); 782 } 783