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
main(int argc,char * argv[])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
regerr(int c)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
pnum(int n,char * sep)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
num(int v,int p)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
convert(int c,char * option_arg)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
convert(char * argv)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
npad(int width,char * sep)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
optmsg(int option,char * option_arg)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
optmsg(char * option)770 optmsg(char *option)
771 {
772 (void) fprintf(stderr, gettext("nl: invalid option (%s)\n"), option);
773 usage();
774 }
775 #endif
776
777 void
usage()778 usage()
779 {
780 (void) fprintf(stderr, gettext(USAGE));
781 exit(1);
782 }
783