Merge commit '4ec4134be29a3b00791f6d70074168a6a3ff4fb3'
[unleashed/tickless.git] / usr / src / cmd / nl / nl.c
blob7a16ee311f2c012ec7d82c0ac3c2e90865597be7
1 /*
2 * CDDL HEADER START
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.
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.
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]
20 * CDDL HEADER END
23 * Copyright 1995 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
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>
41 #define EXPSIZ 512
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
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 */
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();
87 extern char *optarg; /* getopt support */
88 extern int optind;
90 int
91 main(int argc, char *argv[])
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
121 sep[0] = '\t';
122 sep[1] = '\0';
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);
130 #ifdef XPG4
132 * XPG4: Allow either a space or no space between the
133 * options and their required arguments.
136 while (argc > 0) {
137 while ((c = getopt(argc, argv,
138 "pb:d:f:h:i:l:n:s:v:w:")) != EOF) {
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);
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);
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);
218 break;
219 case 'p':
220 if (optarg == 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;
271 break;
272 case 's':
273 (void) strcpy(sep, optarg);
274 break;
275 case 'd':
276 delim1 = *optarg;
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 */
289 argv += optind;
290 argc -= optind;
291 optind = 0;
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);
299 ++argv;
300 --argc;
302 } /* end while argc > 0 */
303 /* end XPG4 version of argument parsing */
304 #else
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.
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++;
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]);
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++;
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]);
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++;
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]);
415 break;
416 case 'p':
417 if (argv[j][i+2] == '\0')
418 cntck = 'y';
419 else
421 optmsg(argv[j]);
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
455 optmsg(argv[j]);
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
464 optmsg(argv[j]);
466 break;
467 case '\0':
468 format = 'n';
469 break;
470 default:
471 optmsg(argv[j]);
472 break;
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++;
485 sep[r] = '\0';
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;
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;
504 default:
505 optmsg(argv[j]);
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);
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);
519 } /* closing brace of for loop */
520 /* end Solaris version of argument parsing */
521 #endif
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;
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
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';
570 if (p[0] != '\n') {
571 lnt = strlen(p);
572 if (p[lnt-1] == '\n')
573 p[lnt-1] = 0;
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':
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.
591 if (p[0] != '\n') {
592 pnum(cnt, sep);
593 cnt += increment;
594 } else {
595 npad(width, sep);
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;
612 break;
613 case 'b':
614 if (step(p, bexpbuf)) {
615 pnum(cnt, sep);
616 cnt += increment;
617 } else {
618 npad(width, sep);
620 break;
621 case 'h':
622 if (step(p, hexpbuf)) {
623 pnum(cnt, sep);
624 cnt += increment;
625 } else {
626 npad(width, sep);
628 break;
629 case 'f':
630 if (step(p, fexpbuf)) {
631 pnum(cnt, sep);
632 cnt += increment;
633 } else {
634 npad(width, sep);
636 break;
638 if (p[0] != '\n')
639 p[lnt-1] = '\n';
640 (void) fprintf(optr, "%s", line);
642 } /* Closing brace of "else" */
643 } /* Closing brace of "while". */
644 (void) fclose(iptr);
646 return (0);
649 /* REGEXP ERR ROUTINE */
651 static void
652 regerr(int c)
654 (void) fprintf(stderr, gettext(
655 "nl: invalid regular expression: error code %d\n"), c);
656 exit(1);
659 /* CALCULATE NUMBER ROUTINE */
661 static void
662 pnum(int n, char *sep)
664 register int i;
666 if (format == 'z') {
667 pad = '0';
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] = ' ';
679 (void) printf("%s%s", nbuf, sep);
682 /* IF NUM > 10, THEN USE THIS CALCULATE ROUTINE */
684 static void
685 num(int v, int p)
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);
696 /* CONVERT ARG STRINGS TO STRING ARRAYS */
698 #ifdef XPG4
699 static int
700 convert(int c, char *option_arg)
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);
712 s1[r] = '\0';
713 k = atoi(s1);
714 return (k);
716 #else
717 /* Solaris version */
718 static int
719 convert(char *argv)
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);
733 s1[r] = '\0';
734 k = atoi(s1);
735 return (k);
737 #endif
739 /* CALCULATE NUM/TEXT SEPRATOR */
741 static void
742 npad(int width, char *sep)
744 register int i;
746 pad = ' ';
747 for (i = 0; i < width; i++)
748 nbuf[i] = pad;
749 (void) printf("%s", nbuf);
751 for (i = 0; i < (int)strlen(sep); i++)
752 (void) printf(" ");
755 #ifdef XPG4
756 static void
757 optmsg(int option, char *option_arg)
759 if (option_arg != NULL) {
760 (void) fprintf(stderr, gettext(
761 "nl: invalid option (-%c %s)\n"), option, option_arg);
763 /* else getopt() will print illegal option message */
764 usage();
766 #else
767 /* Solaris version */
768 static void
769 optmsg(char *option)
771 (void) fprintf(stderr, gettext("nl: invalid option (%s)\n"), option);
772 usage();
774 #endif
776 void
777 usage()
779 (void) fprintf(stderr, gettext(USAGE));
780 exit(1);