man-db-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Man-db-devel] [PATCH v3] man(1): Do not roff non-mandoc pages


From: Mihail Konev
Subject: [Man-db-devel] [PATCH v3] man(1): Do not roff non-mandoc pages
Date: Sun, 30 Oct 2016 11:40:31 +0500

This fixes the display of plain-text manpages,
such as the bmake(1) one.
---

v2: Adjust description.
v3: Adjust comments not to mention nroff.
    Fix shift_chunk_back argument signedness.

v1/2 were not sent to the list.

The NetBSD manpages do not seem to use any macro package.
(These are mentioned in groff(7)).
nroff, when ran on them, inserts newlines, so text ends up like

    Line line line line line line line line line line
line end_of_line.
    Line2 line2 line2 line2 line2 line2 line2 line2 l
ine2 end_of_line.

The nroff is called by man(1) in a pipeline, like

  sed ... | ... | nroff ... | ...

The solution is to run a wrapper for nroff, which would start
an nroff only if appropriate, i.e. if it detects a macro call in its
stdin contents, and otherwise passes stdin as is.

  sed ... | ... | /usr/libexec/roff_maybe nroff ... | ...

 .gitignore         |   1 +
 configure.ac       |   1 +
 man/replace.sin.in |   1 +
 src/Makefile.am    |   5 +-
 src/man.c          |  22 ++++++-
 src/roff_maybe.c   | 190 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 217 insertions(+), 3 deletions(-)
 create mode 100644 src/roff_maybe.c

diff --git a/.gitignore b/.gitignore
index 4d491f8b2d10..713033918803 100644
--- a/.gitignore
+++ b/.gitignore
@@ -132,6 +132,7 @@ src/man_db.conf
 src/manconv
 src/mandb
 src/manpath
+src/roff_maybe
 src/whatis
 src/wrapper
 src/zsoelim
diff --git a/configure.ac b/configure.ac
index c8339f378a4a..1e30c4777be7 100644
--- a/configure.ac
+++ b/configure.ac
@@ -440,6 +440,7 @@ MAN_TRANS_SUBST([man])
 MAN_TRANS_SUBST([manconv])
 MAN_TRANS_SUBST([mandb])
 MAN_TRANS_SUBST([manpath])
+MAN_TRANS_SUBST([roff_maybe])
 MAN_TRANS_SUBST([whatis])
 MAN_TRANS_SUBST([zsoelim])
 
diff --git a/man/replace.sin.in b/man/replace.sin.in
index 72a7e09835f9..5da9f275bd29 100644
--- a/man/replace.sin.in
+++ b/man/replace.sin.in
@@ -11,6 +11,7 @@ s,%catman%,@TRANS_CATMAN@,g
 s,%apropos%,@TRANS_APROPOS@,g
 s,%whatis%,@TRANS_WHATIS@,g
 s,%manconv%,@TRANS_MANCONV@,g
+s,%roff_maybe%,@TRANS_ROFF_MAYBE@,g
 s,%thzsoelim%,@TRANS_ZSOELIM_UPPER@,g
 s,%thman%,@TRANS_MAN_UPPER@,g
 s,%thmandb%,@TRANS_MANDB_UPPER@,g
diff --git a/src/Makefile.am b/src/Makefile.am
index d485ef42a72f..ab0c7270e2ff 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -32,7 +32,7 @@ bin_PROGRAMS = \
        manpath \
        whatis
 sbin_PROGRAMS = accessdb
-pkglibexec_PROGRAMS = globbing manconv zsoelim
+pkglibexec_PROGRAMS = globbing manconv roff_maybe zsoelim
 noinst_DATA = man_db.conf
 
 EXTRA_DIST = lexgrog.c zsoelim.c
@@ -49,6 +49,7 @@ AM_CPPFLAGS = \
        -DMAN=\"$(bindir)/$(TRANS_MAN)\" \
        -DMANCONV=\"$(pkglibexecdir)/$(TRANS_MANCONV)\" \
        -DMANDB=\"$(bindir)/$(TRANS_MANDB)\" \
+       -DROFF_MAYBE=\"$(pkglibexecdir)/$(TRANS_ROFF_MAYBE)\" \
        -DWHATIS=\"$(bindir)/$(TRANS_WHATIS)\" \
        -DZSOELIM=\"$(pkglibexecdir)/$(TRANS_ZSOELIM)\"
 AM_CFLAGS = \
@@ -147,6 +148,8 @@ manpath_SOURCES = \
        manp.c \
        manp.h \
        manpath.c
+roff_maybe_SOURCES = \
+       roff_maybe.c
 whatis_SOURCES = \
        globbing.c \
        globbing.h \
diff --git a/src/man.c b/src/man.c
index aa1d0c8a0ffc..2638ba3397fd 100644
--- a/src/man.c
+++ b/src/man.c
@@ -1065,6 +1065,24 @@ static void add_col (pipeline *p, const char 
*locale_charset, ...)
        pipeline_command (p, cmd);
 }
 
+/* Return pipecmd invoking the command through roff_maybe. */
+static pipecmd *roff_maybe_argstr (const char *command)
+{
+       const char command_prefix[] = ROFF_MAYBE " ";
+       char *full_command;
+       pipecmd *ret;
+
+       full_command = malloc(strlen(command_prefix) +
+                             strlen(command) + 1);
+       sprintf(full_command, "%s%s",
+                       command_prefix, command);
+
+       ret = pipecmd_new_argstr(full_command);
+
+       free(full_command);
+       return ret;
+}
+
 /* Return pipeline to format file to stdout. */
 static pipeline *make_roff_command (const char *dir, const char *file,
                                    pipeline *decomp, const char *dbfilters,
@@ -1297,11 +1315,11 @@ static pipeline *make_roff_command (const char *dir, 
const char *file,
                        case 0:
                                /* done with preprocessors, now add roff */
                                if (troff) {
-                                       cmd = pipecmd_new_argstr
+                                       cmd = roff_maybe_argstr
                                                (get_def ("troff", TROFF));
                                        save_cat = 0;
                                } else
-                                       cmd = pipecmd_new_argstr
+                                       cmd = roff_maybe_argstr
                                                (get_def ("nroff", NROFF));
 
 #ifdef TROFF_IS_GROFF
diff --git a/src/roff_maybe.c b/src/roff_maybe.c
new file mode 100644
index 000000000000..30aa78f587dd
--- /dev/null
+++ b/src/roff_maybe.c
@@ -0,0 +1,190 @@
+/*
+ * roff_maybe.c: Run arguments, but only if stdin is a mandoc markup.
+ *
+ * Copyright (C) 2016 Mihail Konev
+ *
+ * This file is part of man-db.
+ *
+ * man-db is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * man-db is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with man-db; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif /* HAVE_CONIFG_H */
+
+#include <stdio.h>
+#include <unistd.h>
+
+/* A mandoc markup typically has one of
+ *
+ *  '\" comment...
+ *  .\" comment...
+ *  .DIRECTIVE ...
+ *
+ * lines nearly start of file.
+ *
+ * Look for them in first 512 bytes,
+ * otherwise consider nroff not to be applicable
+ * (NetBSD manpages, such as for bmake).
+ *
+ * */
+
+ssize_t chunk_len;
+char chunk[512];
+
+int getchunk (void) {
+       chunk_len = read (STDIN_FILENO, chunk, sizeof chunk);
+       if (chunk_len <= 0)
+               return -1;
+       return 0;
+}
+
+int putchunk (int fd) {
+       ssize_t bytes_written;
+
+       bytes_written = write (fd, chunk, chunk_len);
+       if (bytes_written < 0)
+               return -1;
+       return 0;
+}
+
+void shift_chunk_back (size_t distance) {
+       ssize_t i;
+
+       chunk_len -= distance;
+       if (chunk_len < 0) {
+               chunk_len = 0;
+               return;
+       }
+
+       for (i = 0; i < chunk_len; i++) {
+               chunk[i] = chunk[i + distance];
+       }
+}
+
+int create_pipe (int *pipefd) {
+       int rc;
+
+       rc = pipe (pipefd);
+       if (rc != 0)
+               return -1;
+       return 0;
+}
+
+/* Return non-zero if looking at ".if" or ".lf".
+ * (These are prepended by GNU tbl).
+ * */
+int looking_at_tbl (char *s, ssize_t pos, ssize_t len) {
+       if (pos + 2 >= len)
+               return 0;
+
+       char c0 = s[pos];
+       char c1 = s[pos+1];
+       char c2 = s[pos+2];
+
+       return (c0 == '.')
+               && (c1 == 'i' || c1 == 'l')
+               && (c2 == 'f');
+}
+
+int pipefd[2];
+
+int main (int argc, char **argv) {
+       /* silence compiler warnings */
+        (void) argc;
+
+       int is_manpage = 0;
+
+       /* read first portion of input */
+       getchunk ();
+
+       /* determine whether it is a manpage */
+       ssize_t i;
+       for (i = 0; i < chunk_len; i++) {
+               char c = chunk[i];
+               if (c == '\'' || c == '.') {
+                       if ( (i == 0 || chunk[i-1] == '\n')
+                               && !looking_at_tbl (chunk, i, chunk_len))
+                       {
+                               is_manpage = 1;
+                               break;
+                       }
+               }
+       }
+
+       /* if not a manpage, erase GNU tbl lines,
+        * pass the remaining stdin to the stdout, and exit.
+        * */
+       if (!is_manpage) {
+               int in_tbl_line = 0;
+
+               /* find a line not starting with tbl directive */
+               for (i = 0; i < chunk_len; i++) {
+                       if (looking_at_tbl (chunk, i, chunk_len)) {
+                               in_tbl_line = 1;
+                       }
+                       if (!in_tbl_line)
+                               break;
+                       if (chunk[i] == '\n') {
+                               in_tbl_line = 0;
+                       }
+               }
+
+               /* delete preceding characters */
+               shift_chunk_back (i);
+
+               for (;;) {
+                       if (putchunk (STDOUT_FILENO) != 0)
+                               break;
+                       if (getchunk () != 0)
+                               break;
+               }
+
+               return 0;
+       }
+
+       if (create_pipe (pipefd) != 0)
+               return 1;
+
+       if (fork () == 0) {
+               /* only reached in child */
+               close (pipefd[1]);
+
+               /* make the read end of pipe be stdin */
+               dup2 (pipefd[0], STDIN_FILENO);
+
+               /* perform `exec "$@"` */
+               execvp (argv[1], argv+1);
+
+               /* should never be reached in child */
+               return 1;
+       }
+
+       /* only reached in parent */
+       close (pipefd[0]);
+
+       /* pipe the remaining input to nroff stdin */
+       for (;;) {
+               if (putchunk (pipefd[1]) != 0)
+                       break;
+               if (getchunk () != 0)
+                       break;
+       }
+
+       /* cleanup */
+       close (pipefd[1]);
+
+       return 0;
+}
-- 
2.9.2




reply via email to

[Prev in Thread] Current Thread [Next in Thread]