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