[Toybox] [PATCH] Implement man.

makepost at firemail.cc makepost at firemail.cc
Fri Apr 19 12:49:14 PDT 2019


To look up docs on my netbook and server. Practically deroff.1, with
heuristic for where to put spaces and newlines. How would you simplify
file resolution and bzcat? What have I got wrong when escaping slashes,
because while \-\^\- is -- ok, \-\- becomes -\-, e.g. in git-pull.1?
---
  tests/man.test     | 196 +++++++++++++++++++++++++++++++++++++++++++++
  toys/pending/man.c | 112 ++++++++++++++++++++++++++
  2 files changed, 308 insertions(+)
  create mode 100644 tests/man.test
  create mode 100644 toys/pending/man.c

diff --git a/toys/pending/man.c b/toys/pending/man.c
new file mode 100644
index 00000000..57f77c61
--- /dev/null
+++ b/toys/pending/man.c
@@ -0,0 +1,112 @@
+/* man.c - Read system documentation
+ *
+ * Copyright 2019 makepost <makepost at firemail.cc>
+ *
+ * See 
http://pubs.opengroup.org/onlinepubs/9699919799/utilities/man.html
+
+USE_MAN(NEWTOY(man, "<1>1", TOYFLAG_USR|TOYFLAG_BIN))
+
+config MAN
+  bool "man"
+  default n
+  help
+    usage: man COMMAND
+
+    Read manual for system command.
+*/
+
+#define FOR_man
+#include <toys.h>
+#include <glob.h>
+
+GLOBALS(
+  char any, cell, *f, *line;
+)
+
+static void newln()
+{
+  if (TT.any) putchar('\n');
+  if (TT.any && TT.cell != 2) putchar('\n'); // gawk alias
+  TT.any = TT.cell = 0;
+}
+static void put(char *x) { while (*x && *x != '\n') TT.any = 
putchar(*x++); }
+
+static void s(char *x, char *y) { // Substitute with same length or 
shorter.
+  int i = strlen(x), j = strlen(y), k, l;
+  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];
+  }
+}
+
+static char start(char *x) { return !strncmp(x, TT.line, strlen(x)); }
+static void trim(char *x) { if (start(x)) while (*x++) TT.line++; }
+
+static void do_man(FILE *fp)
+{
+  size_t len = 0;
+  char *line = 0;
+  while (getline(&line, &len, fp) > 0) {
+    TT.line = line;
+    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
+    s("\\(bu", "*"), s("\\(bv", "|"); // bash symbol
+    s("\\&", ""), s("\\f(CW", ""); // gawk,rsync fancy
+    s("\\-", "-"), s("\\(", ""), s("\\^", ""), s("\\e", "\\"); // bash 
escape
+    s("\\*(", "#"); // gawk var
+    if (start(".BR")) trim(".BR "), s(" ", ""); // bash boldpunct
+    if (start(".IP")) newln(), trim(".IP "); // bash list
+    if (start(".IR")) trim(".IR "), s(" ", ""); // bash itapunct
+    trim(".B "); // bash bold
+    trim(".BI "); // gawk boldita
+    trim(".FN "); // bash filename
+    trim(".I "); // bash ita
+    trim(".if n "); // bash nroff
+    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
+    else if (start(".TH")) s("\"", " "), put(TT.line + 4); // gawk,git 
head
+    else if (start(".TP")) newln(), TT.cell = 1; // bash table
+    else if (start(".") || start("\'")); // bash,git garbage
+    else if (!*TT.line); // emerge
+    else ((TT.cell != 0) && TT.cell++), put(" "), put(TT.line);
+  }
+  newln();
+  free(line);
+  fclose(fp);
+}
+
+static FILE *bzcat()
+{
+  char cmd[FILENAME_MAX];
+  snprintf(cmd, sizeof(cmd), "bzcat %s", TT.f);
+  return popen(cmd, "r");
+}
+
+static char *find(char *path, int suf)
+{
+  glob_t g;
+  int i;
+  size_t len = strlen(*toys.optargs);
+  char *name;
+  glob(path, 0, 0, &g);
+  for (i = 0; !TT.f && i < g.gl_pathc; i++) {
+    name = basename(g.gl_pathv[i]);
+    if (strlen(name) == len + suf && !strncmp(name, *toys.optargs, 
len))
+      TT.f = strdup(g.gl_pathv[i]);
+  }
+  globfree(&g);
+  return TT.f;
+}
+
+void man_main(void)
+{
+  chdir("/usr/share/man");
+  if (find("man?/*.?.bz2", 6)) do_man(bzcat()); // curl_strequal
+  else if (find("man?/*.bz2", 4)) do_man(bzcat()); // curl_strequal.3
+  else if (find("man?/*.?", 2)) do_man(fopen(TT.f, "r")); // 
curl_strnequal
+  else if (find("man?/*", 0)) do_man(fopen(TT.f, "r")); // 
curl_strnequal.3
+  if (TT.f) free(TT.f);
+}
diff --git a/tests/man.test b/tests/man.test
new file mode 100644
index 00000000..dfad35b7
--- /dev/null
+++ b/tests/man.test
@@ -0,0 +1,196 @@
+#!/bin/bash
+# Copyright 2019 makepost <makepost at firemail.cc>
+
+[ -f testing.sh ] && . testing.sh
+
+#testing "name" "command" "result" "infile" "stdin"
+
+x=$((RANDOM))
+echo $x | bzip2 >/usr/share/man/man1/toybox.1.bz2
+testing "curl_strequal" "man toybox" " $x\n\n" "" ""
+
+x=$((RANDOM))
+echo $x | bzip2 >/usr/share/man/man1/toybox.1.bz2
+testing "curl_strequal.3" "man toybox.1" " $x\n\n" "" ""
+
+rm /usr/share/man/man1/toybox.1.bz2
+
+x=$((RANDOM))
+echo $x >/usr/share/man/man1/toybox.1
+testing "curl_strnequal" "man toybox" " $x\n\n" "" ""
+
+x=$((RANDOM))
+echo $x >/usr/share/man/man1/toybox.1
+testing "curl_strnequal.3" "man toybox.1" " $x\n\n" "" ""
+
+cat >/usr/share/man/man1/toybox.1 <<EOF
+.TP
+.PD 0
+.B \\-F
+.TP
+.PD
+.B \\-\\^\\-foo\\-bar
+Does something.
+.TP
+.PD 0
+.B \\-\\^\\-no\\-alias
+Has no alias.
+EOF
+testing "gawk alias" "man toybox" " -F\n --foo-bar Does something.\n\n 
--no-alias Has no alias.\n\n" "" ""
+
+cat >/usr/share/man/man1/toybox.1 <<EOF
+\\fBsh\\fR-compatible
+\\fIKorn\\fP
+EOF
+testing "bash bold,ita" "man toybox" " sh-compatible Korn\n\n" "" ""
+
+cat >/usr/share/man/man1/toybox.1 <<EOF
+quoted \\(aqstring\\(aq
+\\(dqserver\\(dq
+Don\\(cq\\&t
+EOF
+testing "bash,rsync quote" "man toybox" " quoted 'string' \"server\" 
Don't\n\n" "" ""
+
+echo "\\*(lq\\-\\^\\-\\*(rq" >/usr/share/man/man1/toybox.1
+testing "gawk quote" "man toybox" " \"--\"\n\n" "" ""
+
+cat >/usr/share/man/man1/toybox.1 <<EOF
+.IP \\(bu
+[\\fB|\\fP\\(bv\\fB|&\\fP]
+EOF
+testing "bash symbol" "man toybox" " * [|||&]\n\n" "" ""
+
+cat >/usr/share/man/man1/toybox.1 <<EOF
+\\&\\fBfflush(...)\\fR
+\\f(CW$ ssh ...\\fP
+EOF
+testing "gawk,rsync fancy" "man toybox" " fflush(...) $ ssh ...\n\n" "" 
""
+
+cat >/usr/share/man/man1/toybox.1 <<EOF
+"\\eC\\-x\\eC\\-r": re\\-read
+must be \\(>= 1
+EOF
+testing "bash escape" "man toybox" " \"\C-x\C-r\": re-read must be >= 
1\n\n" "" ""
+
+echo "\\*(AK language.  The \\*(PX standard" 
 >/usr/share/man/man1/toybox.1
+testing "gawk var" "man toybox" " #AK language.  The #PX standard\n\n" 
"" ""
+
+cat >/usr/share/man/man1/toybox.1 <<EOF
+If set to
+.BR On ,
+(...)
+EOF
+testing "bash boldpunct" "man toybox" " If set to On, (...)\n\n" "" ""
+
+cat >/usr/share/man/man1/toybox.1 <<EOF
+.IP \\fB\$endif\\fP
+(...)
+.IP \\fB\$else\\fP
+(...)
+.IP \\fB\$include\\fP
+EOF
+testing "bash list" "man toybox" " \$endif (...)\n\n \$else (...)\n\n 
\$include\n\n" "" ""
+
+cat >/usr/share/man/man1/toybox.1 <<EOF
+asynchronously (in the
+.IR background ),
+it prints
+EOF
+testing "bash itapunct" "man toybox" " asynchronously (in the 
background), it prints\n\n" "" ""
+
+cat >/usr/share/man/man1/toybox.1 <<EOF
+(...)
+.B Bash
+is
+EOF
+testing "bash bold" "man toybox" " (...) Bash is\n\n" "" ""
+
+# TODO: Unquote.
+cat >/usr/share/man/man1/toybox.1 <<EOF
+.PD 0
+.BI \\-f " program-file"
+.TP
+EOF
+testing "gawk boldita" "man toybox" " -f \" program-file\"\n\n" "" ""
+
+cat >/usr/share/man/man1/toybox.1 <<EOF
+same format as
+.FN /etc/hosts
+(...)
+EOF
+testing "bash filename" "man toybox" " same format as /etc/hosts 
(...)\n\n" "" ""
+
+cat >/usr/share/man/man1/toybox.1 <<EOF
+The
+.I Internal Field Separator
+that is used
+EOF
+testing "bash ita" "man toybox" " The Internal Field Separator that is 
used\n\n" "" ""
+
+cat >/usr/share/man/man1/toybox.1 <<EOF
+.if n Copyright (C) 2019
+.if t Copyright \\(co 2019
+EOF
+testing "bash nroff" "man toybox" " Copyright (C) 2019\n\n" "" ""
+
+cat >/usr/share/man/man1/toybox.1 <<EOF
+(...)
+.PP
+(...)
+EOF
+testing "bash paragraph" "man toybox" " (...)\n\n (...)\n\n" "" ""
+
+# TODO: Find some command I saw that has a different see below.
+cat >/usr/share/man/man1/toybox.1 <<EOF
+(see
+.SM
+.B INVOCATION
+below)
+EOF
+testing "bash small" "man toybox" " (see INVOCATION below)\n\n" "" ""
+
+cat >/usr/share/man/man1/toybox.1 <<EOF
+(...)
+.SH "SHELL GRAMMAR"
+.SS Simple Commands
+.PP
+(...)
+EOF
+testing "bash section" "man toybox" " (...)\n\n\"SHELL 
GRAMMAR\"\n\nSimple Commands\n\n (...)\n\n" "" ""
+
+echo ".so man1/last.1" >/usr/share/man/man1/toybox.1
+testing "lastb" "man toybox" "See last.1\n\n" "" ""
+
+cat >/usr/share/man/man1/toybox.1 <<EOF
+.TH TOYBOX 1 "Apr 13 2019" "Project Organization" "Document Name"
+.PP
+.TH "TOYBOX" "1" "04/13/2019" "Toybox 0\\&.8\\&.0" "Toybox Manual"
+EOF
+testing "gawk,git head" "man toybox" "TOYBOX 1  Apr 13 2019   Project 
Organization   Document Name \n\n TOYBOX   1   04/13/2019   Toybox 0.8.0 
   Toybox Manual \n\n" "" ""
+
+cat >/usr/share/man/man1/toybox.1 <<EOF
+(...)
+.PP
+.PD 0
+.TP 10
+.B \\-c
+If the
+.B \\-c
+option is present
+EOF
+testing "bash table" "man toybox" " (...)\n\n -c If the -c option is 
present\n\n" "" ""
+
+cat >/usr/share/man/man1/toybox.1 <<EOF
+.de FN
+.sp
+.RE
+'\" t
+EOF
+testing "bash,git garbage" "man toybox" "" "" ""
+
+# TODO: -k
+# TODO: emerge section header newline
+# TODO: fdm not roff
+# TODO: git-pull consecutive escaped slashes
+
+rm /usr/share/man/man1/toybox.1


More information about the Toybox mailing list