diff options
Diffstat (limited to 'util/sbase/nl.c')
-rw-r--r-- | util/sbase/nl.c | 212 |
1 files changed, 212 insertions, 0 deletions
diff --git a/util/sbase/nl.c b/util/sbase/nl.c new file mode 100644 index 00000000..9a289b02 --- /dev/null +++ b/util/sbase/nl.c @@ -0,0 +1,212 @@ +/* See LICENSE file for copyright and license details. */ +#include <limits.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#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>", stdin); + } else { + if (!strcmp(argv[0], "-")) { + argv[0] = "<stdin>"; + 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, "<stdin>") | fshut(stdout, "<stdout>"); + + return ret; +} |