---
Makefile | 5 +
README | 1 +
config.def.h | 19 ++
man.1 | 154 ++++++++++++++
man.c | 677 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 856 insertions(+)
create mode 100644 config.def.h
create mode 100644 man.1
create mode 100644 man.c
diff --git a/Makefile b/Makefile
index 9ec9990..1e507f9 100644
--- a/Makefile
+++ b/Makefile
_AT_@ -6,6 +6,7 @@ include config.mk
HDR =\
arg.h\
compat.h\
+ config.h\
crypt.h\
fs.h\
md5.h\
_AT_@ -121,6 +122,7 @@ BIN =\
logger\
logname\
ls\
+ man\
md5sum\
mkdir\
mkfifo\
_AT_@ -192,6 +194,9 @@ $(BIN): $(LIB) $(@:=.o)
$(OBJ): $(HDR) config.mk
+config.h:
+ cp config.def.h $_AT_
+
.o:
$(CC) $(LDFLAGS) -o $_AT_ $< $(LIB)
diff --git a/README b/README
index da2e500..e518796 100644
--- a/README
+++ b/README
_AT_@ -50,6 +50,7 @@ The following tools are implemented:
0=*|o logger .
0=*|o logname .
0#* o ls (-C, -k, -m, -p, -s, -x)
+0#* o man (-k)
0=*|x md5sum .
0=*|o mkdir .
0=*|o mkfifo .
diff --git a/config.def.h b/config.def.h
new file mode 100644
index 0000000..14e1440
--- /dev/null
+++ b/config.def.h
_AT_@ -0,0 +1,19 @@
+/* See LICENSE file for copyright and license details. */
+
+#define GROFF "/usr/bin/groff"
+
+#define GUNZIP "exec /usr/bin/gunzip"
+#define UNCOMPRESS "exec /usr/bin/uncompress"
+#define BUNZIP2 "exec /usr/bin/bunzip2"
+#define UNXZ "exec /usr/bin/unxz"
+#define UNLZMA "exec /usr/bin/unlzma"
+#define LUNZIP "exec /usr/bin/lzip -d"
+#define UNLZ4 "exec /usr/bin/unlz4"
+
+#define MANPATHTRANS "/=/usr"
+#define MANPATH "/usr/local/share/man:/usr/share/man"
+#define MANSECT "3p:3:0:2:1:8:n:l:5:4:6:7:x:9:a:b:c:d:e:f:g:h:i:j:k:l:m:o:p:q:r:s:t:u:v:w:u:y:z"
+#define MANBINSECT "1:8"
+#define MANETCSECT "5"
+#define MANCOMP "gz="GUNZIP":z="GUNZIP":Z="UNCOMPRESS":bz2="BUNZIP2":xz="UNXZ":lzma="UNLZMA":lz="LUNZIP":lz4="UNLZ4
+#define MANPAGER "exec /usr/bin/less"
diff --git a/man.1 b/man.1
new file mode 100644
index 0000000..9985b2b
--- /dev/null
+++ b/man.1
_AT_@ -0,0 +1,154 @@
+.Dd 2017-02-01
+.Dt MAN 1
+.Os sbase
+.Sh NAME
+.Nm man
+.Nd display online reference manuals
+.Sh SYNOPSIS
+.Nm
+.Op Fl 7 | Fl 8
+.Op Ar section
+.Ar name
+.Op Ar subcommand ...
+.Sh DESCRIPTION
+.Nm
+displays the manual with the selected
+.Ar name .
+.Ar name
+is usually a command, C function or system call.
+All
+.Ar subcommand
+are appended to
+.Ar name
+with \fB-\fP joining the arguments.
+Multiple manuals have the same name, in this case
+.Ar section
+can be used to specify which of them to display.
+.P
+The table below shows common
+.Ar section
+numbers.
+.Bd -literal -offset left
+1 Executable commands, as implemented
+1p Executable commands, as specified by POSIX
+2 System calls
+3 Library calls, as implemented
+3p Library calls, as specified by POSIX
+4 Special files
+5 Configuration files, file formats and conventions
+6 Entertainment
+7 Miscellanea
+8 System administration commands
+9 Kernel routines
+0 Header files, as implemented
+0p Header files, as specified by POSIX
+l Tcl/Tk commands
+n SQL commands
+.Ed
+.Sh OPTIONS
+.Bl -tag -width Ds
+.It Fl 7
+Convert output to ASCII.
+.It Fl 8
+Allow to UTF-8.
+.El
+.Sh ENVIRONMENT VARIABLES
+.Bl -tag -width Ds
+.It Ev MANPATH
+Colon-separated list of where manual are stored.
+Each listed directory shall directions for each
+section rather than the manuals themself.
+.It Ev MANPATHTRANS
+Colon-separated list of directory remappings used
+to find the manual when command is given by its
+path. Each entry is of the format
+.Va bindir Ns Cm = Ns Va mandir .
+If the parent directory of the directory
+.Ar name
+matches
+.Va bindir ,
+.Va mandir
+is used as
+.Ar MANPATH .
+.It Ev MANSECT
+Colon-separated list of
+.Ar section
+numbers. If a manual is found under two or more
+sections, the first named section in
+.Ev MANSECT
+is selected.
+.It Ev MANBINSECT
+Similar to
+.Ev MANSECT ,
+but is used when
+.Ar name
+is the path to an executable file.
+.It Ev MANETCSECT ,
+but is used when
+.Ar name
+is the path to file in
+.Pa /etc .
+.It Ev MANCOMP
+Colon-separated list of compressions. Each entry
+is formated as
+.Va extension Ns Cm = Ns Va command ,
+where
+.Va extension
+is the typical file name extension used on files
+compressed such that
+.Nm sh Fl c Li ' Ns Va command Ns Li '
+decompresses the files.
+This variable is ignored when running as root.
+.It Ev MANPAGER
+The pager to used. By default
+.Xr less 1
+is used.
+.It Ev PAGER
+Used instead of
+.Ev MANPAGER
+if
+.Ev MANPAGER
+is not set.
+.It Ev MANWIDTH
+The display-width of the manual, in columns.
+.It Ev COLUMNS
+Used instead of
+.Ev MANWIDTH
+if
+.Ev MANWIDTH
+is not set.
+.It Ev MANMAXWIDTH
+The maximum display-width of the manual, in columns.
+.It Ev MAN_KEEP_STDERR
+If set and not empty,
+.Nm
+will not redirect stderr to
+.Pa /dev/null
+before parsing the file and before starting the pager.
+.It Ev MAN_KEEP_FORMATTING
+If set and not empty,
+.Nm
+will not remove formatting information from the output.
+Formatting is always keeped when stdout is a terminal
+(which causes
+.Nm
+to start a pager).
+.Ed
+.Sh SEE ALSO
+.Xr less 1
+.Sh STANDARDS
+The
+.Nm
+utility is compliant with the
+.St -p1003.1-2013
+specification except from the
+.Op Fl k
+flag and support for looking up multiple manuals.
+.Pp
+The
+.Op Fl 78
+flags,
+.Ar section
+and all environment variables except
+.Ev PAGER
+are an extension to that specification.
diff --git a/man.c b/man.c
new file mode 100644
index 0000000..0e02aea
--- /dev/null
+++ b/man.c
_AT_@ -0,0 +1,677 @@
+/* See LICENSE file for copyright and license details. */
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "config.h"
+#include "util.h"
+
+static void
+usage(void)
+{
+ eprintf("usage: %s [-7 | -8] [section] name [subcommand ...]\n", argv0);
+}
+
+static void
+backslash(char *out, const char *str, const char *esc)
+{
+ while (*str) {
+ if (strchr(esc, *str))
+ *out++ = '\\';
+ *out++ = *str++;
+ }
+ *out = '\0';
+}
+
+static void
+setprompt(const char *name, const char *sect)
+{
+ char *buf1, *buf2;
+ const char *p;
+ size_t n;
+
+#define PATTERN "-ix8RmPm Manual page %s ?ltline %%lt?L/%%L.:byte %%bB?s/%%s..?e (END):?pB %%pB\\%%.. (press h for help or q to quit)$PM Manual page %s ?ltline %%lt?L/%%L.:byte %%bB?s/%%s..?e (END):?pB %%pB\\%%.. (press h for help or q to quit)$"
+
+ if ((p = strchr(name, '/')))
+ name = p + 1;
+
+ n = strlen(name) + (sect ? strlen(sect) : 0) + 3;
+ buf1 = emalloc(n + sizeof(PATTERN));
+ buf2 = emalloc(n);
+
+ if (sect)
+ sprintf(buf1, "%s(%s)", name, sect);
+ else
+ sprintf(buf1, "%s", name);
+ if (setenv("MAN_PN", buf1, 1))
+ weprintf("setenv MAN_PN:");
+
+ backslash(buf2, buf1, "$?:.%\\");
+ sprintf(buf1, PATTERN, buf2, buf2);
+ if (setenv("LESS", buf1, 1))
+ weprintf("setenv LESS:");
+
+ free(buf1);
+ free(buf2);
+
+#undef PATTERN
+}
+
+static void
+checkmap(const char *s, const char *name)
+{
+ char last = ':';
+ int need_eq = 0;
+ for (; *s; last = *s++) {
+ if (*s == ':') {
+ if (need_eq || last == '=')
+ goto bad;
+ need_eq = 1;
+ } else if (*s == '=') {
+ need_eq = 0;
+ if (last == ':')
+ goto bad;
+ }
+ }
+ return;
+bad:
+ eprintf("%s is malformatted\n", name);
+}
+
+static void
+xdup2(int old, int new)
+{
+ if (old != new) {
+ if (dup2(old, new) == -1)
+ eprintf("dup2:");
+ close(old);
+ }
+}
+
+static void
+xwaitpid(pid_t pid)
+{
+ int status;
+ if (pid < 0)
+ return;
+ if (waitpid(pid, &status, 0) != pid)
+ eprintf("waitpid:");
+ if (status)
+ exit(1);
+}
+
+static char *
+xgetenv(const char *var, const char *def, int secure)
+{
+ const char *env = NULL;
+ if (secure || getuid())
+ env = getenv(var);
+ return estrdup(env ? env : def);
+}
+
+static char *
+xstrchr(char *s, int c)
+{
+ char *r = strchr(s, c);
+ return r ? r : strchr(s, '\0');
+}
+
+static int
+hasextension(const char *path, const char *ext)
+{
+ char *p;
+ if (strlen(path) <= strlen(ext))
+ return 0;
+ p = strchr(path, '\0');
+ p -= strlen(ext);
+ return p[-1] == '.' && !strcmp(p, ext);
+}
+
+static int
+find_man_in_dir(const char *dir, const char *name, const char *sectprefix, const char *ext)
+{
+ DIR *d;
+ struct dirent *f;
+ char *p, *sect;
+ int dirfd, fd;
+ size_t n;
+
+ dirfd = open(dir, O_DIRECTORY);
+ if (dirfd < 0)
+ return -1;
+ d = fdopendir(dirfd);
+ if (!d)
+ return -1;
+
+ while ((f = readdir(d))) {
+ p = f->d_name;
+ n = strlen(name);
+ if (strncmp(p, name, n))
+ continue;
+ p += n;
+ if (*p++ != '.')
+ continue;
+ n = strlen(sectprefix);
+ if (strncmp(p, sectprefix, n))
+ continue;
+ sect = p;
+ p = strchr(p + n, '.');
+ if (p)
+ *p = '\0';
+ if (ext ? (p && !strcmp(p + 1, ext)) : !p) {
+ setprompt(name, sect);
+ if (p)
+ *p = '.';
+ fd = openat(dirfd, f->d_name, O_RDONLY);
+ if (fd >= 0) {
+ closedir(d);
+ close(dirfd);
+ return fd;
+ }
+ }
+ }
+
+ closedir(d);
+ close(dirfd);
+ return -1;
+}
+
+static int
+cleanabspath(char *path, size_t size, const char *cwd)
+{
+ char *p, *q;
+ size_t i, n, len;
+ char *parts[PATH_MAX];
+
+ if (*path != '/') {
+ if (strlen(path) + strlen(cwd) + 2 > size)
+ return -1;
+ memmove(path + strlen(cwd) + 1, path, strlen(path) + 1);
+ *stpcpy(path, cwd) = '/';
+ }
+
+ for (n = 0, p = q = path; *p; p++) {
+ if (*p == '/') {
+ *p = '\0';
+ parts[n++] = q;
+ q = p + 1;
+ }
+ }
+
+ for (i = 0; i < n;) {
+ if (!parts[i][parts[i][0] == '.']) {
+ memmove(parts + i, parts + i + 1, --n - i);
+ } else if (parts[i][0] == '.' && parts[i][1] == '.' && !parts[i][2]) {
+ memmove(parts + i, parts + i + 1, --n - i);
+ if (i) {
+ i--;
+ memmove(parts + i, parts + i + 1, --n - i);
+ }
+ } else {
+ i++;
+ }
+ }
+
+ for (p = path, i = 0; i < n; i++) {
+ *p++ = '/';
+ len = strlen(parts[i]);
+ memmove(p, parts[i], len);
+ p += len;
+ }
+ *p = '\0';
+ if (!*path)
+ path[0] = '/', path[1] = '\0';
+
+ return 0;
+}
+
+static char *
+getmanpath(const char *name, char *manpathtrans)
+{
+ char *p, *q, *t, *ret, cwd[PATH_MAX], path[PATH_MAX], clean[PATH_MAX];
+ int done = 0;
+
+ if (strlen(name) >= sizeof(path))
+ return NULL;
+
+ if (!getcwd(cwd, sizeof(cwd)))
+ eprintf("getcwd:");
+
+ strcpy(path, name);
+ p = strrchr(path, '/');
+ *p = '\0';
+ p = strrchr(path, '/');
+ if (p && p != path)
+ *p = '\0';
+ if (cleanabspath(path, sizeof(path), cwd))
+ return NULL;
+
+ for (p = manpathtrans; !done; p = q + 1) {
+ q = xstrchr(p, ':');
+ done = !*q;
+ *q = '\0';
+ t = strchr(p, '=');
+ if (!t)
+ break;
+ *t++ = '\0';
+
+ if (strlen(p) >= sizeof(clean))
+ continue;
+ strcpy(clean, p);
+ if (cleanabspath(clean, sizeof(clean), cwd))
+ continue;
+
+ if (strcmp(path, clean))
+ continue;
+
+ ret = emalloc(strlen(t) + sizeof("/share/man"));
+ stpcpy(stpcpy(ret, t), "/share/man");
+ return ret;
+ }
+
+ ret = emalloc(strlen(path) + sizeof("/share/man"));
+ stpcpy(stpcpy(ret, path), "/share/man");
+ return ret;
+}
+
+static int
+openman(const char *name, char *manpath, char *manpathtrans, char *mansect,
+ char *manbinsect, char *manetcsect, char *mancomp, char *pathbuf, pid_t *pid)
+{
+ char *pp = NULL, *ps = NULL, *pc, *qp, *qs, *qc, cp, cs, cc, *p;
+ char *temp_manpath = NULL;
+ const char *orig_name = NULL;
+ int fd, rw[2];
+ struct stat st;
+
+ *pid = -1;
+
+#define FOR(s, p, q, c)\
+ for (p = s, c = 1;\
+ c ? (q = xstrchr(p, ':'), c = *q, *q = '\0', 1) : 0;\
+ *q = c, p = q + 1)
+
+ if (strchr(name, '/')) {
+ if (!stat(name, &st) && S_ISREG(st.st_mode) && (st.st_mode & 0111)) {
+ manpath = temp_manpath = getmanpath(name, manpathtrans);
+ if (!manpath)
+ return -1;
+ mansect = manbinsect;
+ name = strrchr(name, '/') + 1;
+ goto search;
+ }
+
+ /* This part is a just a convenience, nothing in the POSIX
+ * standard suggests that this feature many be available. */
+ if (strstr(name, "/etc/") == name) {
+ orig_name = name;
+ name = strrchr(name, '/') + 1;
+ mansect = manetcsect;
+ goto search;
+ }
+
+ try_file:
+ strcpy(pathbuf, name);
+ fd = open(pathbuf, O_RDONLY);
+ if (fd < 0)
+ eprintf("open %s:", pathbuf);
+ setprompt(pathbuf, NULL);
+ FOR(mancomp, pc, qc, cc) {
+ *(p = strchr(pc, '=')) = '\0';
+ if (hasextension(name, pc)) {
+ *p = '=';
+ goto match;
+ }
+ *p = '=';
+ }
+ pc = NULL;
+ goto match;
+ }
+
+#define SEARCH\
+ FOR(mansect, ps, qs, cs) {\
+ if (!*ps)\
+ continue;\
+ FOR(manpath, pp, qp, cp) {\
+ sprintf(pathbuf, "%s/man%c/%s.%s", pp, *ps, name, ps);\
+ if (pc) {\
+ *(p = strchr(pc, '=')) = '\0';\
+ stpcpy(stpcpy(strchr(pathbuf, '\0'), "."), pc);\
+ *p = '=';\
+ }\
+ fd = open(pathbuf, O_RDONLY);\
+ if (fd >= 0) {\
+ setprompt(name, ps);\
+ goto match;\
+ }\
+ if (!ps[1]) {\
+ sprintf(pathbuf, "%s/man%c", pp, *ps);\
+ if (pc)\
+ *(p = strchr(pc, '=')) = '\0';\
+ fd = find_man_in_dir(pathbuf, name, ps, pc);\
+ if (pc)\
+ *p = '=';\
+ if (fd >= 0)\
+ goto match;\
+ }\
+ }\
+ }
+
+search:
+ FOR(mancomp, pc, qc, cc)
+ SEARCH
+ pc = NULL;
+ SEARCH
+
+#undef SEARCH
+#undef FOR
+
+ free(temp_manpath);
+ if (orig_name) {
+ name = orig_name;
+ goto try_file;
+ }
+ return -1;
+match:
+
+ free(temp_manpath);
+ if (!pc)
+ return fd;
+ pc = strchr(pc, '=') + 1;
+
+ if (pipe(rw))
+ eprintf("pipe:");
+
+ switch ((*pid = fork())) {
+ case -1:
+ eprintf("fork:");
+ case 0:
+ close(rw[0]);
+ xdup2(fd, STDIN_FILENO);
+ xdup2(rw[1], STDOUT_FILENO);
+ execl("/bin/sh", "sh", "-c", pc, NULL);
+ eprintf("execl /bin/sh:");
+ default:
+ close(rw[1]);
+ close(fd);
+ break;
+ }
+
+ return rw[0];
+}
+
+static int
+convman(int fd, size_t columns, int ascii, int null_stderr, pid_t *pid)
+{
+ char arg1[3 * sizeof(size_t) + sizeof("-rLL=n")];
+ char arg2[3 * sizeof(size_t) + sizeof("-rLT=n")];
+ int rw[2];
+
+ if (pipe(rw))
+ eprintf("pipe:");
+
+ switch ((*pid = fork())) {
+ case -1:
+ eprintf("fork:");
+ case 0:
+ close(rw[0]);
+ xdup2(fd, STDIN_FILENO);
+ xdup2(rw[1], STDOUT_FILENO);
+ if (null_stderr) {
+ fd = open("/dev/null", O_WRONLY);
+ if (fd != STDERR_FILENO) {
+ dup2(fd, STDERR_FILENO);
+ close(fd);
+ }
+ }
+ sprintf(arg1, "-rLL=%zun", columns);
+ sprintf(arg2, "-rLT=%zun", columns);
+ execv(GROFF, (char *[]){GROFF, arg1, arg2, "-mtty-char", "-mandoc",
+ ascii ? "-Tascii" : "-Tutf8", NULL});
+ eprintf("exec %s:", GROFF);
+ default:
+ close(rw[1]);
+ break;
+ }
+
+ return rw[0];
+}
+
+static size_t
+remove_lfs_head(char *str, size_t n)
+{
+ size_t off;
+ char *p;
+ for (p = str; n && *p == '\n'; p++, n--);
+ if (p != str)
+ memmove(str, p, n);
+ p = memchr(str, '\n', n);
+ if (!p)
+ return n;
+ if (*p == '\n')
+ p++;
+ n -= off = (size_t)(++p - str);
+ while (n && *p == '\n')
+ n--, p++;
+ memmove(str + off, p, n);
+ return off + n;
+}
+
+static size_t
+remove_lfs_foot(char *str, size_t n)
+{
+ char *p, *q;
+ p = str + n;
+ if (n && p[-1] == '\n') {
+ while (p != str && p[-1] == '\n')
+ p--, n--;
+ n++;
+ }
+ while (p != str && p[-1] != '\n')
+ p--;
+ if (p-- == str)
+ return n;
+ if (p == str || p[-1] != '\n')
+ return n;
+ q = --p;
+ while (p != str && p[-1] == '\n')
+ p--;
+ memmove(p, q, n - (size_t)(q - str));
+ return n - (size_t)(q - p);
+}
+
+static size_t
+rmfmt(char *str, size_t n)
+{
+ char *r, *w;
+ for (r = w = str; n--; r++) {
+ if (*r == '\b') {
+ while (w != str && (w[-1] & 0xC0) == 0x80)
+ w--;
+ w -= w != str;
+ } else {
+ *w++ = *r;
+ }
+ }
+ return (size_t)(w - str);
+}
+
+int
+main(int argc, char *argv[])
+{
+ char *manpathtrans = xgetenv("MANPATHTRANS", MANPATHTRANS, 1);
+ char *manpath = xgetenv("MANPATH", MANPATH, 1);
+ char *mansect = xgetenv("MANSECT", MANSECT, 1);
+ char *manbinsect = xgetenv("MANBINSECT", MANBINSECT, 1);
+ char *manetcsect = xgetenv("MANETCSECT", MANETCSECT, 1);
+ char *mancomp = xgetenv("MANCOMP", MANCOMP, 0);
+ const char *pager = NULL, *env;
+ char *name, *p, *buf;
+ size_t len, ptr, siz, max, columns = 0;
+ int i, rfd = -1, cfd, fd, rw[2];
+ int ascii = 0;
+ int null_stderr;
+ pid_t zpid, ppid;
+ struct winsize winsize;
+ ssize_t r;
+
+ if ((env = getenv("TERM")))
+ ascii = !strcmp(env, "linux");
+
+ ARGBEGIN {
+ case '7':
+ ascii = 1;
+ break;
+ case '8':
+ ascii = 0;
+ break;
+ default:
+ usage();
+ } ARGEND
+ if (!argc)
+ usage();
+
+ checkmap(manpathtrans, "MANPATHTRANS");
+ checkmap(mancomp, "MANCOMP");
+
+ if (isatty(STDOUT_FILENO)) {
+ if (getuid()) {
+ pager = getenv("MANPAGER");
+ pager = pager ? pager : getenv("PAGER");
+ }
+ pager = pager ? pager : MANPAGER;
+ }
+
+ len = 0;
+ for (i = 0; i < argc; i++)
+ len += strlen(argv[i]) + 1;
+ p = name = emalloc(len);
+ p = stpcpy(p, argv[0]);
+ for (i = 1; i < argc; i++)
+ *p++ = '-', p = stpcpy(p, argv[i]);
+ *p = '\0';
+
+ buf = emalloc(strlen(name) + strlen(manpath) +
+ strlen(mansect) + strlen(mancomp) + 9);
+ if (argc > 1 && !strchr(argv[0], '/')) {
+ p = name + strlen(argv[0]);
+ *p = '\0';
+ rfd = openman(p + 1, manpath, manpathtrans, name,
+ name, name, mancomp, buf, &zpid);
+ *p = '-';
+ if (rfd < 0 && strchr(p + 1, '/'))
+ eprintf("could not find a manual\n");
+ }
+ if (rfd < 0)
+ rfd = openman(name, manpath, manpathtrans, mansect,
+ manbinsect, manetcsect, mancomp, buf, &zpid);
+ if (rfd < 0)
+ eprintf("could not find a manual\n");
+
+ free(name);
+ free(manpathtrans);
+ free(manpath);
+ free(mansect);
+ free(manbinsect);
+ free(manetcsect);
+ free(mancomp);
+ free(buf);
+
+ if ((env = getenv("MANWIDTH")))
+ columns = strtonum(env, 0, SIZE_MAX, NULL);
+ if (!columns && (env = getenv("COLUMNS")))
+ columns = strtonum(env, 0, SIZE_MAX, NULL);
+ if (!columns) {
+ if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &winsize) == -1)
+ columns = 80;
+ else
+ columns = winsize.ws_col;
+ }
+ if ((env = getenv("MANMAXWIDTH"))) {
+ max = strtonum(env, 0, SIZE_MAX, NULL);
+ columns = columns < max ? columns : max;
+ }
+
+ null_stderr = (env = getenv("MAN_KEEP_STDERR"), !env || !*env);
+
+ cfd = convman(rfd, columns, ascii, null_stderr, &ppid);
+ close(rfd);
+
+ buf = emalloc(siz = BUFSIZ);
+ for (len = 0;;) {
+ if (len == siz)
+ buf = erealloc(buf, siz <<= 1);
+ r = read(cfd, buf + len, siz - len);
+ if (r < 0)
+ eprintf("read <subprocess>:");
+ if (r == 0)
+ break;
+ len += (size_t)r;
+ }
+
+ len = remove_lfs_head(buf, len);
+ len = remove_lfs_foot(buf, len);
+ if (!pager && (env = getenv("MAN_KEEP_FORMATTING"), !env || !*env))
+ len = rmfmt(buf, len);
+
+ close(cfd);
+ xwaitpid(zpid);
+ xwaitpid(ppid);
+
+ if (null_stderr) {
+ fd = open("/dev/null", O_WRONLY);
+ if (fd != STDERR_FILENO) {
+ dup2(fd, STDERR_FILENO);
+ close(fd);
+ }
+ }
+
+ ppid = -1;
+ if (pager) {
+ if (pipe(rw))
+ eprintf("pipe:");
+ switch ((ppid = fork())) {
+ case -1:
+ eprintf("fork:");
+ case 0:
+ close(rw[1]);
+ xdup2(rw[0], STDIN_FILENO);
+ execl("/bin/sh", "sh", "-c", pager, NULL);
+ eprintf("execl /bin/sh:");
+ default:
+ close(rw[0]);
+ xdup2(rw[1], STDOUT_FILENO);
+ close(STDIN_FILENO);
+ break;
+ }
+ }
+
+ if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
+ weprintf("signal SIGPIPE:");
+
+ for (ptr = 0; ptr < len;) {
+ r = write(STDOUT_FILENO, buf + ptr, len - ptr);
+ if (r < 0) {
+ if (errno == EPIPE)
+ break;
+ eprintf("write %s:", pager ? "<pager>" : "<stdout>");
+ }
+ ptr += (size_t)r;
+ }
+ free(buf);
+ close(STDOUT_FILENO);
+
+ xwaitpid(ppid);
+
+ return 0;
+}
--
2.11.0
Received on Wed Feb 01 2017 - 10:28:06 CET
This archive was generated by hypermail 2.3.0 : Wed Feb 01 2017 - 10:36:21 CET