diff options
author | makepost <makepost@firemail.cc> | 2019-04-28 17:00:31 +0300 |
---|---|---|
committer | Rob Landley <rob@landley.net> | 2019-04-29 17:15:11 -0500 |
commit | 07a716b167ef6b21f82c86b026dc0f8fdd385c6f (patch) | |
tree | 3f78dcf9ff392838a0acfc409d593716f5b13e41 | |
parent | 2794b23fb331a1f69807d04defa2c2c21d02c588 (diff) | |
download | toybox-07a716b167ef6b21f82c86b026dc0f8fdd385c6f.tar.gz |
Search name and first line with man -k regex.
Exec -k value as regex on basename, and on the first content line
outside a tag or on a referenced see-other, whichever appears earlier.
Reuse zcat choice as a function when looping over files. Fix \-\- and
glob.h include leftover. Handle man-pages example newlines. Clarify the
todos, naming package and issue. Remaining items are more of a wishlist
than a plan. Remove `<1>2` because it doesn't let `-k .` work, please
look into that.
-rw-r--r-- | tests/man.test | 31 | ||||
-rw-r--r-- | toys/pending/man.c | 125 |
2 files changed, 106 insertions, 50 deletions
diff --git a/tests/man.test b/tests/man.test index f261437c..65b8a06f 100644 --- a/tests/man.test +++ b/tests/man.test @@ -16,6 +16,7 @@ echo five > banana/man5/numbers.5 testing "man" "$MAN numbers" " one\n\n" "" "" testing "man.section" "$MAN numbers.3" " three\n\n" "" "" testing "section man" "$MAN 5 numbers" " five\n\n" "" "" +testing "/" "$MAN /" "" "" "" # Regression guard for !suf in zopen cat >banana/man1/toybox.1 <<EOF .TP @@ -27,7 +28,7 @@ cat >banana/man1/toybox.1 <<EOF Does something. .TP .PD 0 -.B \\-\\^\\-no\\-alias +.B \\-\\-no\\-alias Has no alias. EOF @@ -129,6 +130,14 @@ EOF testing "bash nroff" "$MAN toybox" " Copyright (C) 2019\n\n" "" "" cat >banana/man1/toybox.1 <<EOF +.EX +#include <stdio.h> +#include <stdlib.h> +.EE +EOF +testing "stat example" "$MAN toybox" "#include <stdio.h>\n#include <stdlib.h>\n\n\n" "" "" + +cat >banana/man1/toybox.1 <<EOF (...) .PP (...) @@ -183,9 +192,23 @@ cat >banana/man1/toybox.1 <<EOF EOF testing "bash,git garbage" "$MAN toybox" "" "" "" -# TODO: -k +# "bash,git garbage" from above has no content, -k skips it. +bzip2 >banana/man1/numbers.1.bz2 <<EOF +.TH NUMBERS 1 "2019 Apr 28" "Toybox Manual" +.SH NAME +man \- test -k +.SH ANOTHER SECTION +.so noop.1 +Skip this text. +EOF +echo "No dash." | gzip >banana/man3/numbers.3.gz +echo .so man1/numbers.1 >banana/man5/numbers.5 +testing "-k ." "$MAN -k ." "numbers.1.bz2 - test -k\nnumbers.3.gz - No dash.\nnumbers.5 - See numbers.1\n" "" "" +testing "-k -k" "$MAN -k -k" "numbers.1.bz2 - test -k\n" "" "" +testing "-k d.*h" "$MAN -k 'd.*h'" "numbers.3.gz - No dash.\n" "" "" +testing "-k ers.1" "$MAN -k ers.1" "numbers.1.bz2 - test -k\nnumbers.5 - See numbers.1\n" "" "" + # TODO: emerge section header newline -# TODO: fdm not roff -# TODO: git-pull consecutive escaped slashes +# TODO: fdm,man-pages man1p/, .nf, rare tags rm -rf banana diff --git a/toys/pending/man.c b/toys/pending/man.c index 9abf92bc..0e54e93c 100644 --- a/toys/pending/man.c +++ b/toys/pending/man.c @@ -4,7 +4,7 @@ * * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/man.html -USE_MAN(NEWTOY(man, "<1>2k:M:", TOYFLAG_USR|TOYFLAG_BIN)) +USE_MAN(NEWTOY(man, "k:M:", TOYFLAG_USR|TOYFLAG_BIN)) config MAN bool "man" @@ -28,16 +28,17 @@ config MAN #define FOR_man #include <toys.h> -#include <glob.h> GLOBALS( char *M, *k; - char any, cell, *f, *line; + char any, cell, ex, *f, k_done, *line, **sufs; + regex_t reg; ) static void newln() { + if (FLAG(k)) return; if (TT.any) putchar('\n'); if (TT.any && TT.cell != 2) putchar('\n'); // gawk alias TT.any = TT.cell = 0; @@ -45,7 +46,7 @@ static void newln() static void put(char *x) { - while (*x && *x != '\n') TT.any = putchar(*x++); + while (*x && (TT.ex || *x != '\n')) TT.any = putchar(*x++); } // Substitute with same length or shorter. @@ -56,6 +57,7 @@ static void s(char *x, char *y) for (k = 0; TT.line[k]; k++) if (!strncmp(x, &TT.line[k], i)) { memmove(&TT.line[k], y, j); for (l = k += j; TT.line[l]; l++) TT.line[l] = TT.line[l + i - j]; + k--; } } @@ -69,17 +71,23 @@ static void trim(char *x) if (start(x)) while (*x++) TT.line++; } +static char k(char *s) { + TT.k_done = 2; + if (s) TT.line = s; + return !regexec(&TT.reg, TT.k, 0, 0, 0)||!regexec(&TT.reg, TT.line, 0, 0, 0); +} + static void do_man(char **pline, long len) { - char *line; - - if (!pline) { - newln(); - return; - } - line = *pline; - - TT.line = line; + if (!pline) return newln(); + TT.line = *pline; + + if (FLAG(k)) { + if (!TT.k_done && !start(".") && !start("'") && k(strstr(*pline, "- "))) + printf("%s %s%s", TT.k, "- "+2*(TT.line!=*pline), TT.line); + else if (!TT.k_done && start(".so") && k(basename(*pline + 4))) + printf("%s - See %s", TT.k, TT.line); + } else { s("\\fB", ""), s("\\fI", ""), s("\\fP", ""), s("\\fR", ""); // bash bold,ita s("\\(aq", "'"), s("\\(cq", "'"), s("\\(dq", "\""); // bash,rsync quote s("\\*(lq", "\""), s("\\*(rq", "\""); // gawk quote @@ -97,7 +105,8 @@ static void do_man(char **pline, long len) trim(".FN "); // bash filename trim(".I "); // bash ita trim(".if n "); // bash nroff - if (start(".PP")) newln(); // bash paragraph + if (start(".E")) TT.ex = TT.line[2] == 'X'; // stat example + else if (start(".PP")) newln(); // bash paragraph else if (start(".SM")); // bash small else if (start(".S")) newln(), put(TT.line + 4), newln(); // bash section else if (start(".so")) put("See "), put(basename(TT.line + 4)); // lastb @@ -107,56 +116,80 @@ static void do_man(char **pline, long len) else if (!*TT.line); // emerge else { if (TT.cell) TT.cell++; - put(" "); + if (!TT.ex) put(" "); put(TT.line); } + } +} + +// Open file, decompressing if suffix known. +static int zopen(char *s) +{ + int fds[] = {-1, -1}; + char **known = TT.sufs, *suf = strrchr(s, '.'); + + if ((*fds = open(s, O_RDONLY)) == -1) return -1; + while (suf && *known && strcmp(suf, *known++)); + if (!suf || !*known) return *fds; + sprintf(toybuf, "%czcat"+2*(suf[1]=='g'), suf[1]); + xpopen_both((char *[]){toybuf, s, 0}, fds); + close(fds[0]); + return fds[1]; } // Try opening all the possible file extensions. int tryfile(char *section, char *name) { - char *suf[] = {".gz", ".bz2", ".xz", ""}, *end, - *s = xmprintf("%s/man%s/%s.%s.bz2", TT.M, section, name, section); - int fd, i, and = 1; - - end = s+strlen(s); - do { - for (i = 0; i<ARRAY_LEN(suf); i++) { - strcpy(end-4, suf[i]); - if ((fd = open(s, O_RDONLY))!= -1) { - if (*suf[i]) { - int fds[] = {fd, -1}; - - sprintf(toybuf, "%czcat"+(2*!i), suf[i][1]); - xpopen_both((char *[]){toybuf, s, 0}, fds); - fd = fds[1]; - } - goto done; - } - } - end -= strlen(section)+1; - } while (and--); - -done: + char *s = xmprintf("%s/man%s/%s.%s.bz2", TT.M, section, name, section), **suf; + int dotnum, fd = -1; + size_t len = strlen(s) - 4; + + for (dotnum = 0; dotnum <= 2; dotnum += 2) { + suf = TT.sufs; + while ((fd == -1) && *suf) strcpy(s + len - dotnum, *suf++), fd = zopen(s); + // Recheck suf in zopen, because for x.1.gz name here it is "". + } free(s); return fd; } void man_main(void) { - char *order = "18325467"; - int fd; + int fd = -1; + char **order = (char *[]) {"1", "8", "3", "2", "5", "4", "6", "7", 0}; + TT.sufs = (char *[]) {".bz2", ".gz", ".xz", "", 0}; if (!TT.M) TT.M = "/usr/share/man"; - if (!toys.optc || FLAG(k)) error_exit("not yet"); + if (FLAG(k)) { + char *d, *f; + DIR *dp; + struct dirent *entry; + if (regcomp(&TT.reg, TT.k, REG_ICASE|REG_NOSUB)) error_exit("bad regex"); + while (*order) { + d = xmprintf("%s/man%s", TT.M, *order++); + if (!(dp = opendir(d))) continue; + while ((entry = readdir(dp))) { + if (entry->d_name[0] == '.') continue; + f = xmprintf("%s/%s", d, TT.k = entry->d_name); + if (-1 != (fd = zopen(f))) { + TT.k_done = 0; + do_lines(fd, '\n', do_man); + close(fd); + } + free(f); + } + closedir(dp); + free(d); + } + return regfree(&TT.reg); + } + + if (!toys.optc) error_exit("not yet"); if (toys.optc == 1) { - if (strchr(*toys.optargs, '/')) fd = xopen(*toys.optargs, O_RDONLY); - else for (order = "18325467"; *order; order++) { - *toybuf = *order; - if (-1 != (fd = tryfile(toybuf, *toys.optargs))) break; - } + if (strchr(*toys.optargs, '/')) fd = zopen(*toys.optargs); + else while ((fd == -1) && *order) fd = tryfile(*order++, *toys.optargs); if (!*order) error_exit("no %s", *toys.optargs); // If they specified a section, look for file in that section |