nl.c - sbase - suckless unix tools
(HTM) git clone git://git.suckless.org/sbase
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) README
(DIR) LICENSE
---
nl.c (4369B)
---
1 /* See LICENSE file for copyright and license details. */
2 #include <limits.h>
3 #include <stdint.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <string.h>
7
8 #include "text.h"
9 #include "utf.h"
10 #include "util.h"
11
12 static size_t startnum = 1;
13 static size_t incr = 1;
14 static size_t blines = 1;
15 static size_t delimlen = 2;
16 static size_t seplen = 1;
17 static int width = 6;
18 static int pflag = 0;
19 static char type[] = { 'n', 't', 'n' }; /* footer, body, header */
20 static char *delim = "\\:";
21 static char format[6] = "%*ld";
22 static char *sep = "\t";
23 static regex_t preg[3];
24
25 static int
26 getsection(struct line *l, int *section)
27 {
28 size_t i;
29 int sectionchanged = 0, newsection = *section;
30
31 for (i = 0; (l->len - i) >= delimlen &&
32 !memcmp(l->data + i, delim, delimlen); i += delimlen) {
33 if (!sectionchanged) {
34 sectionchanged = 1;
35 newsection = 0;
36 } else {
37 newsection = (newsection + 1) % 3;
38 }
39 }
40
41 if (!(l->len - i) || l->data[i] == '\n')
42 *section = newsection;
43 else
44 sectionchanged = 0;
45
46 return sectionchanged;
47 }
48
49 static void
50 nl(const char *fname, FILE *fp)
51 {
52 static struct line line;
53 static size_t size;
54 size_t number = startnum, bl = 1;
55 ssize_t len;
56 int donumber, oldsection, section = 1;
57
58 while ((len = getline(&line.data, &size, fp)) > 0) {
59 line.len = len;
60 donumber = 0;
61 oldsection = section;
62
63 if (getsection(&line, §ion)) {
64 if ((section >= oldsection) && !pflag)
65 number = startnum;
66 continue;
67 }
68
69 switch (type[section]) {
70 case 't':
71 if (line.data[0] != '\n')
72 donumber = 1;
73 break;
74 case 'p':
75 if (!regexec(preg + section, line.data, 0, NULL, 0))
76 donumber = 1;
77 break;
78 case 'a':
79 if (line.data[0] == '\n' && bl < blines) {
80 ++bl;
81 } else {
82 donumber = 1;
83 bl = 1;
84 }
85 }
86
87 if (donumber) {
88 printf(format, width, number);
89 fwrite(sep, 1, seplen, stdout);
90 number += incr;
91 }
92 fwrite(line.data, 1, line.len, stdout);
93 }
94 free(line.data);
95 if (ferror(fp))
96 eprintf("getline %s:", fname);
97 }
98
99 static void
100 usage(void)
101 {
102 eprintf("usage: %s [-p] [-b type] [-d delim] [-f type]\n"
103 " [-h type] [-i num] [-l num] [-n format]\n"
104 " [-s sep] [-v num] [-w num] [file]\n", argv0);
105 }
106
107 static char
108 getlinetype(char *type, regex_t *preg)
109 {
110 if (type[0] == 'p')
111 eregcomp(preg, type + 1, REG_NOSUB);
112 else if (!type[0] || !strchr("ant", type[0]))
113 usage();
114
115 return type[0];
116 }
117
118 int
119 main(int argc, char *argv[])
120 {
121 FILE *fp = NULL;
122 size_t s;
123 int ret = 0;
124 char *d, *formattype, *formatblit;
125
126 ARGBEGIN {
127 case 'd':
128 switch (utflen((d = EARGF(usage())))) {
129 case 0:
130 eprintf("empty logical page delimiter\n");
131 case 1:
132 s = strlen(d);
133 delim = emalloc(s + 1 + 1);
134 estrlcpy(delim, d, s + 1 + 1);
135 estrlcat(delim, ":", s + 1 + 1);
136 delimlen = s + 1;
137 break;
138 default:
139 delim = d;
140 delimlen = strlen(delim);
141 break;
142 }
143 break;
144 case 'f':
145 type[0] = getlinetype(EARGF(usage()), preg);
146 break;
147 case 'b':
148 type[1] = getlinetype(EARGF(usage()), preg + 1);
149 break;
150 case 'h':
151 type[2] = getlinetype(EARGF(usage()), preg + 2);
152 break;
153 case 'i':
154 incr = estrtonum(EARGF(usage()), 0, MIN(LLONG_MAX, SIZE_MAX));
155 break;
156 case 'l':
157 blines = estrtonum(EARGF(usage()), 0, MIN(LLONG_MAX, SIZE_MAX));
158 break;
159 case 'n':
160 formattype = EARGF(usage());
161 estrlcpy(format, "%", sizeof(format));
162
163 if (!strcmp(formattype, "ln")) {
164 formatblit = "-";
165 } else if (!strcmp(formattype, "rn")) {
166 formatblit = "";
167 } else if (!strcmp(formattype, "rz")) {
168 formatblit = "0";
169 } else {
170 eprintf("%s: bad format\n", formattype);
171 }
172
173 estrlcat(format, formatblit, sizeof(format));
174 estrlcat(format, "*ld", sizeof(format));
175 break;
176 case 'p':
177 pflag = 1;
178 break;
179 case 's':
180 sep = EARGF(usage());
181 seplen = unescape(sep);
182 break;
183 case 'v':
184 startnum = estrtonum(EARGF(usage()), 0, MIN(LLONG_MAX, SIZE_MAX));
185 break;
186 case 'w':
187 width = estrtonum(EARGF(usage()), 1, INT_MAX);
188 break;
189 default:
190 usage();
191 } ARGEND
192
193 if (argc > 1)
194 usage();
195
196 if (!argc) {
197 nl("<stdin>", stdin);
198 } else {
199 if (!strcmp(argv[0], "-")) {
200 argv[0] = "<stdin>";
201 fp = stdin;
202 } else if (!(fp = fopen(argv[0], "r"))) {
203 eprintf("fopen %s:", argv[0]);
204 }
205 nl(argv[0], fp);
206 }
207
208 ret |= fp && fp != stdin && fshut(fp, argv[0]);
209 ret |= fshut(stdin, "<stdin>") | fshut(stdout, "<stdout>");
210
211 return ret;
212 }