From a92a74961d838209f3468d10426bc945ba26070c Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 30 Oct 2016 22:31:30 +0100 Subject: man: allow nroff and tbl commands be overridden; unmangle writing to files Parse this in config files: DEFINE col ... DEFINE tbl ... DEFINE nroff ... Add width options to nroff command line. Use "tbl", not "gtbl", as default tbl command. Export GROFF_NO_SGR=1 and use "col -b -p -x" instead of pager when writing to file. function old new delta man_main 735 863 +128 if_redefined - 64 +64 show_manpage 199 169 -30 ------------------------------------------------------------------------------ (add/remove: 1/0 grow/shrink: 1/1 up/down: 192/-30) Total: 162 bytes Signed-off-by: Denys Vlasenko --- miscutils/man.c | 104 ++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 79 insertions(+), 25 deletions(-) (limited to 'miscutils') diff --git a/miscutils/man.c b/miscutils/man.c index f705dd31e..01382c4d7 100644 --- a/miscutils/man.c +++ b/miscutils/man.c @@ -11,6 +11,7 @@ //usage: "\n -w Show page locations" #include "libbb.h" +#include "common_bufsiz.h" enum { OPT_a = 1, /* all */ @@ -18,7 +19,6 @@ enum { }; /* This is what I see on my desktop system being executed: - ( echo ".ll 12.4i" echo ".nr LL 12.4i" @@ -28,11 +28,39 @@ echo ".\\\"" echo ".pl \n(nlu+10" ) | gtbl | nroff -Tlatin1 -mandoc | less -*/ +On another system I see this: -static int show_manpage(const char *pager, char *man_filename, int man, int level); +... | tbl | nroff -mandoc -rLL=n -rLT=n -Tutf8 | less -static int run_pipe(const char *pager, char *man_filename, int man, int level) +where is screen width minus 5. +Replacing "DEFINE nroff nroff -mandoc" in /etc/man_db.conf +changes "nroff -mandoc" part; -rLL=n, -rLT=n and -Tutf8 parts are +appended to the user-specified command. + +Redirecting to a pipe or file sets GROFF_NO_SGR=1 to prevent color escapes, +and uses "col -b -p -x" instead of pager, this filters out backspace +and underscore tricks. +*/ + +struct globals { + const char *col; + const char *tbl; + const char *nroff; + const char *pager; +} FIX_ALIASING; +#define G (*(struct globals*)bb_common_bufsiz1) +#define INIT_G() do { \ + setup_common_bufsiz(); \ + G.col = "col"; \ + G.tbl = "tbl"; \ + /* replaced -Tlatin1 with -Tascii for non-UTF8 displays */; \ + G.nroff = "nroff -mandoc -Tascii"; \ + G.pager = ENABLE_LESS ? "less" : "more"; \ +} while (0) + +static int show_manpage(char *man_filename, int man, int level); + +static int run_pipe(char *man_filename, int man, int level) { char *cmd; @@ -95,7 +123,7 @@ static int run_pipe(const char *pager, char *man_filename, int man, int level) man_filename = xasprintf("%s/%s", man_filename, linkname); free(line); /* Note: we leak "new" man_filename string as well... */ - if (show_manpage(pager, man_filename, man, level + 1)) + if (show_manpage(man_filename, man, level + 1)) return 1; /* else: show the link, it's better than nothing */ } @@ -103,20 +131,29 @@ static int run_pipe(const char *pager, char *man_filename, int man, int level) ordinary_manpage: close(STDIN_FILENO); open_zipped(man_filename, /*fail_if_not_compressed:*/ 0); /* guaranteed to use fd 0 (STDIN_FILENO) */ - /* "2>&1" is added so that nroff errors are shown in pager too. - * Otherwise it may show just empty screen */ - cmd = xasprintf( - /* replaced -Tlatin1 with -Tascii for non-UTF8 displays */ - man ? "gtbl | nroff -Tascii -mandoc 2>&1 | %s" - : "%s", - pager); + if (man) { + /* "man man" formats to screen width. + * "man man >file" formats to default 80 columns. + * "man man | cat" formats to default 80 columns. + */ + int w = get_terminal_width(STDOUT_FILENO); + if (w > 10) + w -= 2; + /* "2>&1" is added so that nroff errors are shown in pager too. + * Otherwise it may show just empty screen */ + cmd = xasprintf("%s | %s -rLL=%un -rLT=%un 2>&1 | %s", + G.tbl, G.nroff, w, w, + G.pager); + } else { + cmd = xstrdup(G.pager); + } system(cmd); free(cmd); return 1; } /* man_filename is of the form "/dir/dir/dir/name.s" */ -static int show_manpage(const char *pager, char *man_filename, int man, int level) +static int show_manpage(char *man_filename, int man, int level) { #if SEAMLESS_COMPRESSION /* We leak this allocation... */ @@ -125,26 +162,26 @@ static int show_manpage(const char *pager, char *man_filename, int man, int leve #endif #if ENABLE_FEATURE_SEAMLESS_LZMA - if (run_pipe(pager, filename_with_zext, man, level)) + if (run_pipe(filename_with_zext, man, level)) return 1; #endif #if ENABLE_FEATURE_SEAMLESS_XZ strcpy(ext, "xz"); - if (run_pipe(pager, filename_with_zext, man, level)) + if (run_pipe(filename_with_zext, man, level)) return 1; #endif #if ENABLE_FEATURE_SEAMLESS_BZ2 strcpy(ext, "bz2"); - if (run_pipe(pager, filename_with_zext, man, level)) + if (run_pipe(filename_with_zext, man, level)) return 1; #endif #if ENABLE_FEATURE_SEAMLESS_GZ strcpy(ext, "gz"); - if (run_pipe(pager, filename_with_zext, man, level)) + if (run_pipe(filename_with_zext, man, level)) return 1; #endif - return run_pipe(pager, man_filename, man, level); + return run_pipe(man_filename, man, level); } static char **add_MANPATH(char **man_path_list, int *count_mp, char *path) @@ -182,11 +219,20 @@ static char **add_MANPATH(char **man_path_list, int *count_mp, char *path) return man_path_list; } +static const char *if_redefined(const char *var, const char *key, const char *line) +{ + if (!is_prefixed_with(line, key)) + return var; + line += strlen(key); + if (!isspace(line[0])) + return var; + return xstrdup(skip_whitespace(line)); +} + int man_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int man_main(int argc UNUSED_PARAM, char **argv) { parser_t *parser; - const char *pager = ENABLE_LESS ? "less" : "more"; char *sec_list; char *cur_path, *cur_sect; char **man_path_list; @@ -195,6 +241,8 @@ int man_main(int argc UNUSED_PARAM, char **argv) int opt, not_found; char *token[2]; + INIT_G(); + opt_complementary = "-1"; /* at least one argument */ opt = getopt32(argv, "+aw"); argv += optind; @@ -228,9 +276,10 @@ int man_main(int argc UNUSED_PARAM, char **argv) if (!token[1]) continue; if (strcmp("DEFINE", token[0]) == 0) { - if (is_prefixed_with(token[1], "pager")) { - pager = xstrdup(skip_whitespace(token[1] + 5)); - } + G.col = if_redefined(G.tbl , "col", token[1]); + G.tbl = if_redefined(G.tbl , "tbl", token[1]); + G.nroff = if_redefined(G.nroff, "nroff", token[1]); + G.pager = if_redefined(G.pager, "pager", token[1]); } else if (strcmp("MANDATORY_MANPATH"+10, token[0]) == 0 /* "MANPATH"? */ || strcmp("MANDATORY_MANPATH", token[0]) == 0 @@ -250,7 +299,12 @@ int man_main(int argc UNUSED_PARAM, char **argv) if (!env_pager) env_pager = getenv("PAGER"); if (env_pager) - pager = env_pager; + G.pager = env_pager; + } + + if (!isatty(STDOUT_FILENO)) { + putenv((char*)"GROFF_NO_SGR=1"); + G.pager = xasprintf("%s -b -p -x", G.col); } not_found = 0; @@ -259,7 +313,7 @@ int man_main(int argc UNUSED_PARAM, char **argv) cur_mp = 0; if (strchr(*argv, '/')) { - found = show_manpage(pager, *argv, /*man:*/ 1, 0); + found = show_manpage(*argv, /*man:*/ 1, 0); goto check_found; } while ((cur_path = man_path_list[cur_mp++]) != NULL) { @@ -280,7 +334,7 @@ int man_main(int argc UNUSED_PARAM, char **argv) sect_len, cur_sect, *argv, sect_len, cur_sect); - found_here = show_manpage(pager, man_filename, cat0man1, 0); + found_here = show_manpage(man_filename, cat0man1, 0); found |= found_here; cat0man1 += found_here + 1; free(man_filename); -- cgit v1.2.3-55-g6feb From 7c3c92c533b65d4c29f2990915c9c424c3f6629d Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Mon, 31 Oct 2016 01:52:18 +0100 Subject: man: make width selection more thorough; explain how to override it Fedora's "man CMD >file" still uses terminal width, not 80 (but disables formatting), this change mimics that. Signed-off-by: Denys Vlasenko --- libbb/xfuncs.c | 47 ++++++++++++++++++++++++++++++++++++++--------- miscutils/man.c | 13 ++++++------- 2 files changed, 44 insertions(+), 16 deletions(-) (limited to 'miscutils') diff --git a/libbb/xfuncs.c b/libbb/xfuncs.c index 3f9a84ad4..45650edba 100644 --- a/libbb/xfuncs.c +++ b/libbb/xfuncs.c @@ -237,16 +237,27 @@ ssize_t FAST_FUNC full_write2_str(const char *str) static int wh_helper(int value, int def_val, const char *env_name, int *err) { - if (value == 0) { - char *s = getenv(env_name); - if (s) { - value = atoi(s); - /* If LINES/COLUMNS are set, pretend that there is - * no error getting w/h, this prevents some ugly - * cursor tricks by our callers */ - *err = 0; - } + /* Envvars override even if "value" from ioctl is valid (>0). + * Rationale: it's impossible to guess what user wants. + * For example: "man CMD | ...": should "man" format output + * to stdout's width? stdin's width? /dev/tty's width? 80 chars? + * We _cant_ know it. If "..." saves text for e.g. email, + * then it's probably 80 chars. + * If "..." is, say, "grep -v DISCARD | $PAGER", then user + * would prefer his tty's width to be used! + * + * Since we don't know, at least allow user to do this: + * "COLUMNS=80 man CMD | ..." + */ + char *s = getenv(env_name); + if (s) { + value = atoi(s); + /* If LINES/COLUMNS are set, pretend that there is + * no error getting w/h, this prevents some ugly + * cursor tricks by our callers */ + *err = 0; } + if (value <= 1 || value >= 30000) value = def_val; return value; @@ -258,6 +269,20 @@ int FAST_FUNC get_terminal_width_height(int fd, unsigned *width, unsigned *heigh { struct winsize win; int err; + int close_me = -1; + + if (fd == -1) { + if (isatty(STDOUT_FILENO)) + fd = STDOUT_FILENO; + else + if (isatty(STDERR_FILENO)) + fd = STDERR_FILENO; + else + if (isatty(STDIN_FILENO)) + fd = STDIN_FILENO; + else + close_me = fd = open("/dev/tty", O_RDONLY); + } win.ws_row = 0; win.ws_col = 0; @@ -268,6 +293,10 @@ int FAST_FUNC get_terminal_width_height(int fd, unsigned *width, unsigned *heigh *height = wh_helper(win.ws_row, 24, "LINES", &err); if (width) *width = wh_helper(win.ws_col, 80, "COLUMNS", &err); + + if (close_me >= 0) + close(close_me); + return err; } int FAST_FUNC get_terminal_width(int fd) diff --git a/miscutils/man.c b/miscutils/man.c index 01382c4d7..adb7770b4 100644 --- a/miscutils/man.c +++ b/miscutils/man.c @@ -9,6 +9,8 @@ //usage: "Format and display manual page\n" //usage: "\n -a Display all pages" //usage: "\n -w Show page locations" +//usage: "\n" +//usage: "\n$COLUMNS overrides output width" #include "libbb.h" #include "common_bufsiz.h" @@ -53,7 +55,7 @@ struct globals { setup_common_bufsiz(); \ G.col = "col"; \ G.tbl = "tbl"; \ - /* replaced -Tlatin1 with -Tascii for non-UTF8 displays */; \ + /* replaced -Tlatin1 with -Tascii for non-UTF8 displays */ \ G.nroff = "nroff -mandoc -Tascii"; \ G.pager = ENABLE_LESS ? "less" : "more"; \ } while (0) @@ -132,15 +134,12 @@ static int run_pipe(char *man_filename, int man, int level) close(STDIN_FILENO); open_zipped(man_filename, /*fail_if_not_compressed:*/ 0); /* guaranteed to use fd 0 (STDIN_FILENO) */ if (man) { - /* "man man" formats to screen width. - * "man man >file" formats to default 80 columns. - * "man man | cat" formats to default 80 columns. - */ - int w = get_terminal_width(STDOUT_FILENO); + int w = get_terminal_width(-1); if (w > 10) w -= 2; /* "2>&1" is added so that nroff errors are shown in pager too. - * Otherwise it may show just empty screen */ + * Otherwise it may show just empty screen. + */ cmd = xasprintf("%s | %s -rLL=%un -rLT=%un 2>&1 | %s", G.tbl, G.nroff, w, w, G.pager); -- cgit v1.2.3-55-g6feb From 2e6af549715f5d7b4c2ab204e46c8b8f6f057045 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Mon, 31 Oct 2016 14:05:34 +0100 Subject: man: remove -Tascii from nroff invocation Signed-off-by: Denys Vlasenko --- miscutils/man.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'miscutils') diff --git a/miscutils/man.c b/miscutils/man.c index adb7770b4..932f0b794 100644 --- a/miscutils/man.c +++ b/miscutils/man.c @@ -30,6 +30,8 @@ echo ".\\\"" echo ".pl \n(nlu+10" ) | gtbl | nroff -Tlatin1 -mandoc | less +Some systems use -Tascii. + On another system I see this: ... | tbl | nroff -mandoc -rLL=n -rLT=n -Tutf8 | less @@ -55,8 +57,8 @@ struct globals { setup_common_bufsiz(); \ G.col = "col"; \ G.tbl = "tbl"; \ - /* replaced -Tlatin1 with -Tascii for non-UTF8 displays */ \ - G.nroff = "nroff -mandoc -Tascii"; \ + /* Removed -Tlatin1. Assuming system nroff has suitable default */ \ + G.nroff = "nroff -mandoc"; \ G.pager = ENABLE_LESS ? "less" : "more"; \ } while (0) -- cgit v1.2.3-55-g6feb