1 /* nl.c - print line numbers
2  *
3  * Copyright 2013 CE Strake <strake888@gmail.com>
4  *
5  * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/nl.html
6  *
7  * This implements a subset: only one logical page (-ip), no sections (-dfh).
8  * todo: -lv
9 
10 USE_NL(NEWTOY(nl, "v#<1=1l#w#<0=6Eb:n:s:", TOYFLAG_USR|TOYFLAG_BIN))
11 
12 config NL
13   bool "nl"
14   default y
15   help
16     usage: nl [-E] [-l #] [-b MODE] [-n STYLE] [-s SEPARATOR] [-w WIDTH] [FILE...]
17 
18     Number lines of input.
19 
20     -E	Use extended regex syntax (when doing -b pREGEX)
21     -b	Which lines to number: a (all) t (non-empty, default) pREGEX (pattern)
22     -l	Only count last of this many consecutive blank lines
23     -n	Number STYLE: ln (left justified) rn (right justified) rz (zero pad)
24     -s	Separator to use between number and line (instead of TAB)
25     -w	Width of line numbers (default 6)
26 */
27 
28 #define FOR_nl
29 #include "toys.h"
30 
31 GLOBALS(
32   char *s, *n, *b;
33   long w, l, v;
34 
35   // Count of consecutive blank lines for -l has to persist between files
36   long lcount;
37 )
38 
do_nl(int fd,char * name)39 static void do_nl(int fd, char *name)
40 {
41   FILE *f = xfdopen(fd, "r");
42   int w = TT.w, slen = strlen(TT.s);
43 
44   for (;;) {
45     char *line = 0;
46     size_t temp;
47     int match = *TT.b != 'n';
48 
49     if (getline(&line, &temp, f) < 1) {
50       if (ferror(f)) perror_msg_raw(name);
51       break;
52     }
53 
54     if (*TT.b == 'p') match = !regexec((void *)(toybuf+16), line, 0, 0, 0);
55     if (TT.l || *TT.b == 't')
56       if (*line == '\n') match = TT.l && ++TT.lcount >= TT.l;
57     if (match) {
58       TT.lcount = 0;
59       printf(toybuf, w, TT.v++, TT.s);
60     } else printf("%*c", (int)w+slen, ' ');
61     xprintf("%s", line);
62 
63     free(line);
64   }
65 
66   fclose(f);
67 }
68 
nl_main(void)69 void nl_main(void)
70 {
71   char *clip = "";
72 
73   if (!TT.s) TT.s = "\t";
74 
75   if (!TT.n || !strcmp(TT.n, "rn")); // default
76   else if (!strcmp(TT.n, "ln")) clip = "-";
77   else if (!strcmp(TT.n, "rz")) clip = "0";
78   else error_exit("bad -n '%s'", TT.n);
79 
80   sprintf(toybuf, "%%%s%s", clip, "*ld%s");
81 
82   if (!TT.b) TT.b = "t";
83   if (*TT.b == 'p' && TT.b[1])
84     xregcomp((void *)(toybuf+16), TT.b+1,
85       REG_NOSUB | (toys.optflags&FLAG_E)*REG_EXTENDED);
86   else if (!strchr("atn", *TT.b)) error_exit("bad -b '%s'", TT.b);
87 
88   loopfiles (toys.optargs, do_nl);
89 }
90