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 }