aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormakepost <makepost@firemail.cc>2019-04-28 17:00:31 +0300
committerRob Landley <rob@landley.net>2019-04-29 17:15:11 -0500
commit07a716b167ef6b21f82c86b026dc0f8fdd385c6f (patch)
tree3f78dcf9ff392838a0acfc409d593716f5b13e41
parent2794b23fb331a1f69807d04defa2c2c21d02c588 (diff)
downloadtoybox-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.test31
-rw-r--r--toys/pending/man.c125
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