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
main(argc,argv)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
regerr(c)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
pnum(n,sep)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
num(v,p)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
convert(c,option_arg)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
convert(argv)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
npad(width,sep)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
optmsg(option,option_arg)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
optmsg(option)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
usage()797 usage()
798 {
799 (void) fprintf(stderr, gettext(USAGE));
800 exit(1);
801 }
802