sbase

suckless unix tools
git clone git://git.2f30.org/sbase.git
Log | Files | Refs | README | LICENSE

commit 587575dcb40c1094bebf92dfd6e060d2717ed5cb
parent d49f6f2044c3a1a9d00d45f74e684cd1f44f91ae
Author: FRIGN <dev@frign.de>
Date:   Sun Mar 22 16:29:50 +0100

Audit nl(1)

1) Refactor the manpage to use the num-syntax and concise wording.
2) Build format instead of having a list of static strings.
3) BUGFIX: if (!buf[0] || buf[0] == '\n') Process last-read-line
           properly.
4) BUGFIX: In case we hit a formatting line, print a newline instead
           of just dropping it.
5) Use a switch instead of having spaghetti-cases.
6) Don't use printf-magic but explicitly do a putchar(' ')-loop.
7) Update usage(), indent properly.
8) BUGFIX: strchr is not NULL when type[0] is \0. Check for \0
           separately beforehand.
9) Reorder arg.h-cases for better readability.

Diffstat:
README | 2+-
nl.1 | 82+++++++++++++++++++++++++++++++++++++++----------------------------------------
nl.c | 124++++++++++++++++++++++++++++++++++++++++++-------------------------------------
3 files changed, 107 insertions(+), 101 deletions(-)
diff --git a/README b/README @@ -49,7 +49,7 @@ The following tools are implemented ('*' == finished, '#' == UTF-8 support, =*| mktemp non-posix none =*| mv yes none (-i) =*| nice yes none -#* nl yes none +#*| nl yes none =*| nohup yes none #*| paste yes none =*| printenv non-posix none diff --git a/nl.1 b/nl.1 @@ -1,4 +1,4 @@ -.Dd March 18, 2015 +.Dd March 22, 2015 .Dt NL 1 .Os sbase .Sh NAME @@ -11,18 +11,19 @@ .Op Fl d Ar delim .Op Fl f Ar type .Op Fl h Ar type -.Op Fl i Ar incr +.Op Fl i Ar num .Op Fl l Ar num .Op Fl n Ar format .Op Fl s Ar sep -.Op Fl v Ar startnum -.Op Fl w Ar width +.Op Fl v Ar num +.Op Fl w Ar num .Op Ar file .Sh DESCRIPTION .Nm -reads lines from the named +reads lines from .Ar file -and writes them to stdout with non-empty lines numbered. If no +and writes them to stdout, numbering non-empty lines. +If no .Ar file is given .Nm @@ -30,64 +31,61 @@ reads from stdin. .Sh OPTIONS .Bl -tag -width Ds .It Fl p -Do not reset number for logical pages -.It Fl b Ar type -Defines which lines will be numbered for body sections: +Do not reset line number for logical pages. +.It Fl h Ar type | Fl b Ar type | Fl f Ar type +Define which lines to number in the head | body | footer section: .Bl -tag -width pstringXX .It a All lines. .It n No lines. .It t -Only non-empty lines (default). +Only non-empty lines. This is the default. .It p Ns Ar expr -Only lines which match -.Ar expr , -a regular expression as defined in +Only lines matching +.Ar expr +according to .Xr regex 7 . .El .It Fl d Ar delim -Specify the delimiter (default is "\\:"). If only one character is specified, the second remains ':'. -.It Fl f Ar type -Same as -.Fl b -except for footer sections. -.It Fl h Ar type -Same as -.Fl b -except for header sections. -.It Fl i Ar incr -Defines the increment between numbered lines. +Set +.Ar delim +as the delimiter for logical pages. If +.Ar delim +is only on character, +.Nm +appends ":" to it. The default is "\e:". +.It Fl i Ar num +Set the increment between numbered lines to +.Ar num . .It Fl l Ar num -Specify the number of adjacent blank lines to be considered as one. Default is 1. +Set the number of adjacent blank lines to be considered as one to +.Ar num . +The default is 1. .It Fl n Ar format -Specify the line number output format. -The +Set the line number output .Ar format -can be any of the following: +to one of: .Bl -tag -width pstringXX .It ln Left justified. .It rn -Right justified. +Right justified. This is the default. .It rz Right justified with leading zeroes. .El .Pp -The default -.Ar format -is rn. .It Fl s Ar sep -Defines the string used to separate line numbers and lines. By default this is -a tab. -.It Fl v Ar startnum -Start counting from -.Ar startnum -instead of the default 1. -.It Fl w Ar width -The number of characters to be occupied by the line number -will be set to -.Ar width . +Use +.Ar sep +to separate line numbers and lines. The default is "\et". +.It Fl v Ar num +Start counting lines from +.Ar num . +The default is 1. +.It Fl w Ar num +Set the width of the line number to +.Ar num . The default is 6. .El .Sh SEE ALSO diff --git a/nl.c b/nl.c @@ -9,40 +9,33 @@ #include "utf.h" #include "util.h" -/* formats here specify line number and separator (not line content) */ -#define FORMAT_LN "%-*ld%s" -#define FORMAT_RN "%*ld%s" -#define FORMAT_RZ "%0*ld%s" - -static char type[] = { 'n', 't', 'n' }; /* footer, body, header */ -static char *delim = "\\:"; -static const char *format = FORMAT_RN; -static const char *sep = "\t"; -static int width = 6; -static int pflag = 0; -static size_t startnum = 1; -static size_t incr = 1; -static size_t blines = 1; -static size_t delimlen = 2; -static regex_t preg[3]; +static size_t startnum = 1; +static size_t incr = 1; +static size_t blines = 1; +static size_t delimlen = 2; +static int width = 6; +static int pflag = 0; +static char type[] = { 'n', 't', 'n' }; /* footer, body, header */ +static char *delim = "\\:"; +static char format[8] = "%*ld%s"; +static char *sep = "\t"; +static regex_t preg[3]; static int getsection(char *buf, int *section) { - int sectionchanged = 0; - int newsection = *section; + int sectionchanged = 0, newsection = *section; for (; !strncmp(buf, delim, delimlen); buf += delimlen) { if (!sectionchanged) { sectionchanged = 1; newsection = 0; } else { - ++newsection; - newsection %= 3; + newsection = (newsection + 1) % 3; } } - if (buf && buf[0] == '\n') + if (!buf[0] || buf[0] == '\n') *section = newsection; else sectionchanged = 0; @@ -51,27 +44,33 @@ getsection(char *buf, int *section) } static void -nl(const char *name, FILE *fp) +nl(const char *fname, FILE *fp) { - char *buf = NULL; + size_t i, number = startnum, size = 0; int donumber, oldsection, section = 1, bl = 1; - size_t number = startnum, size = 0; + char *buf = NULL; - while (getline(&buf, &size, fp) != -1) { + while (getline(&buf, &size, fp) >= 0) { donumber = 0; oldsection = section; if (getsection(buf, &section)) { if ((section >= oldsection) && !pflag) number = startnum; + putchar('\n'); continue; } - if ((type[section] == 't' && buf[0] != '\n') - || (type[section] == 'p' && - !regexec(&preg[section], buf, 0, NULL, 0))) { - donumber = 1; - } else if (type[section] == 'a') { + switch (type[section]) { + case 't': + if (buf[0] != '\n') + donumber = 1; + break; + case 'p': + if (!regexec(preg + section, buf, 0, NULL, 0)) + donumber = 1; + break; + case 'a': if (buf[0] == '\n' && bl < blines) { ++bl; } else { @@ -84,28 +83,30 @@ nl(const char *name, FILE *fp) printf(format, width, number, sep); number += incr; } else { - printf("%*s", width, ""); + for (i = 0; i < width; i++) + putchar(' '); } - printf("%s", buf); + fputs(buf, stdout); } free(buf); if (ferror(fp)) - eprintf("%s: read error:", name); + eprintf("getline %s:", fname); } static void usage(void) { - eprintf("usage: %s [-p] [-b type] [-d delim] [-f type] " - "[-h type] [-i incr] [-l num]\n[-n format] [-s sep] " - "[-v startnum] [-w width] [file]\n", argv0); + 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 (!strchr("ant", type[0])) + eregcomp(preg, type + 1, REG_NOSUB); + else if (!type[0] || !strchr("ant", type[0])) usage(); return type[0]; @@ -115,13 +116,10 @@ int main(int argc, char *argv[]) { FILE *fp; - char *d; size_t l, s; + char *d, *formattype, *formatblit; ARGBEGIN { - case 'b': - type[1] = getlinetype(EARGF(usage()), &preg[1]); - break; case 'd': d = EARGF(usage()); l = utflen(d); @@ -131,9 +129,9 @@ main(int argc, char *argv[]) break; case 1: s = strlen(d); - delim = emalloc(s + 2); - estrlcpy(delim, d, s + 2); - estrlcat(delim, ":", s + 2); + delim = emalloc(s + 1 + 1); + estrlcpy(delim, d, s + 1 + 1); + estrlcat(delim, ":", s + 1 + 1); delimlen = s + 1; break; default: @@ -143,27 +141,36 @@ main(int argc, char *argv[]) } break; case 'f': - type[0] = getlinetype(EARGF(usage()), &preg[0]); + 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]); + 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, UINT_MAX); + blines = estrtonum(EARGF(usage()), 0, MIN(LLONG_MAX, SIZE_MAX)); break; case 'n': - format = EARGF(usage()); - if (!strcmp(format, "ln")) - format = FORMAT_LN; - else if (!strcmp(format, "rn")) - format = FORMAT_RN; - else if (!strcmp(format, "rz")) - format = FORMAT_RZ; - else - eprintf("%s: bad format\n", format); + 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%s", sizeof(format)); break; case 'p': pflag = 1; @@ -184,7 +191,7 @@ main(int argc, char *argv[]) if (argc > 1) usage(); - if (argc == 0) { + if (!argc) { nl("<stdin>", stdin); } else { if (!(fp = fopen(argv[0], "r"))) @@ -192,5 +199,6 @@ main(int argc, char *argv[]) nl(argv[0], fp); fclose(fp); } + return 0; }