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 <vda.linux@googlemail.com>
diff --git a/libbb/xfuncs.c b/libbb/xfuncs.c
index 3f9a84a..45650ed 100644
--- a/libbb/xfuncs.c
+++ b/libbb/xfuncs.c
@@ -237,16 +237,27 @@
 
 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 @@
 {
 	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 @@
 		*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 01382c4..adb7770 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 @@
 	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 @@
 	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);