/* See LICENSE file for copyright and license details. */ #include #include #include #include #include #include "text.h" #include "utf.h" #include "util.h" static size_t startnum = 1; static size_t incr = 1; static size_t blines = 1; static size_t delimlen = 2; static size_t seplen = 1; static int width = 6; static int pflag = 0; static char type[] = { 'n', 't', 'n' }; /* footer, body, header */ static char *delim = "\\:"; static char format[6] = "%*ld"; static char *sep = "\t"; static regex_t preg[3]; static int getsection(struct line *l, int *section) { size_t i; int sectionchanged = 0, newsection = *section; for (i = 0; (l->len - i) >= delimlen && !memcmp(l->data + i, delim, delimlen); i += delimlen) { if (!sectionchanged) { sectionchanged = 1; newsection = 0; } else { newsection = (newsection + 1) % 3; } } if (!(l->len - i) || l->data[i] == '\n') *section = newsection; else sectionchanged = 0; return sectionchanged; } static void nl(const char *fname, FILE *fp) { static struct line line; static size_t size; size_t number = startnum, bl = 1; ssize_t len; int donumber, oldsection, section = 1; while ((len = getline(&line.data, &size, fp)) > 0) { line.len = len; donumber = 0; oldsection = section; if (getsection(&line, §ion)) { if ((section >= oldsection) && !pflag) number = startnum; continue; } switch (type[section]) { case 't': if (line.data[0] != '\n') donumber = 1; break; case 'p': if (!regexec(preg + section, line.data, 0, NULL, 0)) donumber = 1; break; case 'a': if (line.data[0] == '\n' && bl < blines) { ++bl; } else { donumber = 1; bl = 1; } } if (donumber) { printf(format, width, number); fwrite(sep, 1, seplen, stdout); number += incr; } fwrite(line.data, 1, line.len, stdout); } free(line.data); if (ferror(fp)) eprintf("getline %s:", fname); } static void usage(void) { eprintf("usage: %s [-p] [-b type] [-d delim] [-f type]\n" " [-h type] [-i num] [-l num] [-n format]\n" " [-s sep] [-v num] [-w num] [file]\n", argv0); } static char getlinetype(char *type, regex_t *preg) { if (type[0] == 'p') eregcomp(preg, type + 1, REG_NOSUB); else if (!type[0] || !strchr("ant", type[0])) usage(); return type[0]; } int main(int argc, char *argv[]) { FILE *fp = NULL; size_t s; int ret = 0; char *d, *formattype, *formatblit; ARGBEGIN { case 'd': switch (utflen((d = EARGF(usage())))) { case 0: eprintf("empty logical page delimiter\n"); case 1: s = strlen(d); delim = emalloc(s + 1 + 1); estrlcpy(delim, d, s + 1 + 1); estrlcat(delim, ":", s + 1 + 1); delimlen = s + 1; break; default: delim = d; delimlen = strlen(delim); break; } break; case 'f': type[0] = getlinetype(EARGF(usage()), preg); break; case 'b': type[1] = getlinetype(EARGF(usage()), preg + 1); break; case 'h': type[2] = getlinetype(EARGF(usage()), preg + 2); break; case 'i': incr = estrtonum(EARGF(usage()), 0, MIN(LLONG_MAX, SIZE_MAX)); break; case 'l': blines = estrtonum(EARGF(usage()), 0, MIN(LLONG_MAX, SIZE_MAX)); break; case 'n': formattype = EARGF(usage()); estrlcpy(format, "%", sizeof(format)); if (!strcmp(formattype, "ln")) { formatblit = "-"; } else if (!strcmp(formattype, "rn")) { formatblit = ""; } else if (!strcmp(formattype, "rz")) { formatblit = "0"; } else { eprintf("%s: bad format\n", formattype); } estrlcat(format, formatblit, sizeof(format)); estrlcat(format, "*ld", sizeof(format)); break; case 'p': pflag = 1; break; case 's': sep = EARGF(usage()); seplen = unescape(sep); break; case 'v': startnum = estrtonum(EARGF(usage()), 0, MIN(LLONG_MAX, SIZE_MAX)); break; case 'w': width = estrtonum(EARGF(usage()), 1, INT_MAX); break; default: usage(); } ARGEND if (argc > 1) usage(); if (!argc) { nl("", stdin); } else { if (!strcmp(argv[0], "-")) { argv[0] = ""; fp = stdin; } else if (!(fp = fopen(argv[0], "r"))) { eprintf("fopen %s:", argv[0]); } nl(argv[0], fp); } ret |= fp && fp != stdin && fshut(fp, argv[0]); ret |= fshut(stdin, "") | fshut(stdout, ""); return ret; }