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 default: 505 optmsg(argv[j]); 506 } 507 } else if ((iptr = fopen(argv[j], "r")) == NULL) { 508 /* end of options, filename starting with '-' */ 509 (void) fprintf(stderr, "nl: %s: ", argv[j]); 510 perror(""); 511 return (1); 512 } 513 } else if ((iptr = fopen(argv[j], "r")) == NULL) { 514 /* filename starting with char other than '-' */ 515 (void) fprintf(stderr, "nl: %s: ", argv[j]); 516 perror(""); 517 return (1); 518 } 519 } /* closing brace of for loop */ 520 /* end Solaris version of argument parsing */ 521 #endif 522 523 /* ON FIRST PASS ONLY, SET LINE COUNTER (cnt) = startcnt & */ 524 /* SET DEFAULT BODY TYPE TO NUMBER ALL LINES. */ 525 if (pass1) { 526 cnt = startcnt; 527 type = body; 528 last = 'b'; 529 pass1 = 0; 530 } 531 532 /* 533 * DO WHILE THERE IS INPUT 534 * CHECK TO SEE IF LINE IS NUMBERED, 535 * IF SO, CALCULATE NUM, PRINT NUM, 536 * THEN OUTPUT SEPERATOR CHAR AND LINE 537 */ 538 539 while ((p = fgets(line, sizeof (line), iptr)) != NULL) { 540 if (p[0] == delim1 && p[1] == delim2) { 541 if (p[2] == delim1 && 542 p[3] == delim2 && 543 p[4] == delim1 && 544 p[5] == delim2 && 545 p[6] == '\n') { 546 if (cntck != 'y') 547 cnt = startcnt; 548 type = header; 549 last = 'h'; 550 swtch = 'y'; 551 } else { 552 if (p[2] == delim1 && p[3] == delim2 && p[4] == '\n') { 553 if (cntck != 'y' && last != 'h') 554 cnt = startcnt; 555 type = body; 556 last = 'b'; 557 swtch = 'y'; 558 } else { 559 if (p[0] == delim1 && p[1] == delim2 && 560 p[2] == '\n') { 561 if (cntck != 'y' && last == 'f') 562 cnt = startcnt; 563 type = footer; 564 last = 'f'; 565 swtch = 'y'; 566 } 567 } 568 } 569 } 570 if (p[0] != '\n') { 571 lnt = strlen(p); 572 if (p[lnt-1] == '\n') 573 p[lnt-1] = NULL; 574 } 575 576 if (swtch == 'y') { 577 swtch = 'n'; 578 (void) fprintf(optr, "\n"); 579 } else { 580 switch (type) { 581 case 'n': 582 npad(width, sep); 583 break; 584 case 't': 585 /* 586 * XPG4: The wording of Spec 1170 is misleading; 587 * the official interpretation is to number all 588 * non-empty lines, ie: the Solaris code has not 589 * been changed. 590 */ 591 if (p[0] != '\n') { 592 pnum(cnt, sep); 593 cnt += increment; 594 } else { 595 npad(width, sep); 596 } 597 break; 598 case 'a': 599 if (p[0] == '\n') { 600 blankctr++; 601 if (blank == blankctr) { 602 blankctr = 0; 603 pnum(cnt, sep); 604 cnt += increment; 605 } else 606 npad(width, sep); 607 } else { 608 blankctr = 0; 609 pnum(cnt, sep); 610 cnt += increment; 611 } 612 break; 613 case 'b': 614 if (step(p, bexpbuf)) { 615 pnum(cnt, sep); 616 cnt += increment; 617 } else { 618 npad(width, sep); 619 } 620 break; 621 case 'h': 622 if (step(p, hexpbuf)) { 623 pnum(cnt, sep); 624 cnt += increment; 625 } else { 626 npad(width, sep); 627 } 628 break; 629 case 'f': 630 if (step(p, fexpbuf)) { 631 pnum(cnt, sep); 632 cnt += increment; 633 } else { 634 npad(width, sep); 635 } 636 break; 637 } 638 if (p[0] != '\n') 639 p[lnt-1] = '\n'; 640 (void) fprintf(optr, "%s", line); 641 642 } /* Closing brace of "else" */ 643 } /* Closing brace of "while". */ 644 (void) fclose(iptr); 645 646 return (0); 647 } 648 649 /* REGEXP ERR ROUTINE */ 650 651 static void 652 regerr(int c) 653 { 654 (void) fprintf(stderr, gettext( 655 "nl: invalid regular expression: error code %d\n"), c); 656 exit(1); 657 } 658 659 /* CALCULATE NUMBER ROUTINE */ 660 661 static void 662 pnum(int n, char *sep) 663 { 664 register int i; 665 666 if (format == 'z') { 667 pad = '0'; 668 } 669 for (i = 0; i < width; i++) 670 nbuf[i] = pad; 671 num(n, width - 1); 672 if (format == 'l') { 673 while (nbuf[0] == ' ') { 674 for (i = 0; i < width; i++) 675 nbuf[i] = nbuf[i+1]; 676 nbuf[width-1] = ' '; 677 } 678 } 679 (void) printf("%s%s", nbuf, sep); 680 } 681 682 /* IF NUM > 10, THEN USE THIS CALCULATE ROUTINE */ 683 684 static void 685 num(int v, int p) 686 { 687 if (v < 10) 688 nbuf[p] = v + '0'; 689 else { 690 nbuf[p] = (v % 10) + '0'; 691 if (p > 0) 692 num(v / 10, p - 1); 693 } 694 } 695 696 /* CONVERT ARG STRINGS TO STRING ARRAYS */ 697 698 #ifdef XPG4 699 static int 700 convert(int c, char *option_arg) 701 { 702 s = option_arg; 703 q = r = 0; 704 while (s[q] != '\0') { 705 if (s[q] >= '0' && s[q] <= '9') { 706 s1[r] = s[q]; 707 r++; 708 q++; 709 } else 710 optmsg(c, option_arg); 711 } 712 s1[r] = '\0'; 713 k = atoi(s1); 714 return (k); 715 } 716 #else 717 /* Solaris version */ 718 static int 719 convert(char *argv) 720 { 721 s = (char *)argv; 722 q = 2; 723 r = 0; 724 while (s[q] != '\0') { 725 if (s[q] >= '0' && s[q] <= '9') { 726 s1[r] = s[q]; 727 r++; 728 q++; 729 } else { 730 optmsg(argv); 731 } 732 } 733 s1[r] = '\0'; 734 k = atoi(s1); 735 return (k); 736 } 737 #endif 738 739 /* CALCULATE NUM/TEXT SEPRATOR */ 740 741 static void 742 npad(int width, char *sep) 743 { 744 register int i; 745 746 pad = ' '; 747 for (i = 0; i < width; i++) 748 nbuf[i] = pad; 749 (void) printf("%s", nbuf); 750 751 for (i = 0; i < (int)strlen(sep); i++) 752 (void) printf(" "); 753 } 754 755 #ifdef XPG4 756 static void 757 optmsg(int option, char *option_arg) 758 { 759 if (option_arg != (char *)NULL) { 760 (void) fprintf(stderr, gettext( 761 "nl: invalid option (-%c %s)\n"), option, option_arg); 762 } 763 /* else getopt() will print illegal option message */ 764 usage(); 765 } 766 #else 767 /* Solaris version */ 768 static void 769 optmsg(char *option) 770 { 771 (void) fprintf(stderr, gettext("nl: invalid option (%s)\n"), option); 772 usage(); 773 } 774 #endif 775 776 void 777 usage() 778 { 779 (void) fprintf(stderr, gettext(USAGE)); 780 exit(1); 781 } 782