Subject: rfc/pre-patch: automatic diff width from the terminal

From: "Dr. David Alan Gilbert" <[email-masked]>
Date: Fri, 1 Nov 2013 00:47:00 +0000

Hi,
  The patch below is a rough 1st cut of an attempt to make diff use the
terminal width as the default width.

It pinches the code from coreutils ls.c that does the same thing, and so
uses the terminal width, failing that reads the COLUMNS env variable,
and failing that falls back to the default (all of which can still be
over ridden with -w/-W).

  Other than glue, all the actual code comes straight out of ls.c

Now given that this is a first cut, is this basically the right approach,
and are people happy to change the default behaviour?

I know that there are some things I need to do:
  - figure out the autoconf magic so it works when termios isn't available
    and the 'WINSIZE_IN_PTEM' def that coreutils picks up
  - Probably more docs fixing
  - some tests
  - (Copyright assignment? Or is the code so close to coreutils it isn't
an issue?)

It's the side by side diff that I'm most interested in this for,
having nice wide cheap 1920 monitors is great for side by side diffs
and I normally want to fill whatever terminal I have.

Thanks in advance for all comments,

Dave

-----------------------
diff --git a/bootstrap.conf b/bootstrap.conf
index ac85adf..10be643 100644
--- a/bootstrap.conf
+++ b/bootstrap.conf
@@ -56,6 +56,7 @@ mkstemp
 mktime
 progname
 propername
+quotearg
 rawmemchr
 readme-release
 regex
@@ -82,6 +83,7 @@ wcwidth
 xalloc
 xfreopen
 xreadlink
+xstrtol
 xstrtoumax
 xvasprintf
 '
diff --git a/doc/diffutils.texi b/doc/diffutils.texi
index 2d238dc..09dae68 100644
--- a/doc/diffutils.texi
+++ b/doc/diffutils.texi
@@ -3967,8 +3967,10 @@ Ignore white space when comparing lines.  @xref{White Space}.
 
 @item -W @var{columns}
 @itemx --width=@var{columns}
-Output at most @var{columns} (default 130) print columns per line in
-side by side format.  @xref{Side by Side Format}.
+Output at most @var{columns} print columns per line in side by side format.
+The default is taken from the terminal settings if possible; otherwise the
+environment variable @env{COLUMNS} is used if it is set; otherwise the default
+is 130 columns.  @xref{Side by Side Format}.
 
 @item -x @var{pattern}
 @itemx --exclude=@var{pattern}
diff --git a/src/diff.c b/src/diff.c
index 50d0365..8d283d3 100644
--- a/src/diff.c
+++ b/src/diff.c
@@ -34,13 +34,23 @@
 #include <hard-locale.h>
 #include <prepargs.h>
 #include <progname.h>
+#include <quotearg.h>
 #include <sh-quote.h>
 #include <stat-time.h>
+#include <xstrtol.h>
 #include <timespec.h>
 #include <version-etc.h>
 #include <xalloc.h>
 #include <xreadlink.h>
 #include <binary-io.h>
+#include <termios.h>
+
+#include <sys/ioctl.h>
+#ifdef WINSIZE_IN_PTEM
+# include <sys/stream.h>
+# include <sys/ptem.h>
+#endif
+
 
 /* The official name of this program (e.g., no 'g' prefix).  */
 #define PROGRAM_NAME "diff"
@@ -255,6 +265,51 @@ exclude_options (void)
 {
   return EXCLUDE_WILDCARDS | (ignore_file_name_case ? FNM_CASEFOLD : 0);
 }
+
+/* Return the number of columns to use, based on:
+ *   a default (130)
+ *   the COLUMNS env variable
+ *   the terminal width
+ * The code is based on coreutils/ls.c - except diff has always defaulted
+ * to 130 rather than ls's 80.   I'd probably say it's better to allow
+ * overriding terminal width by COLUMNS but decided to keep it consistent
+ * with coreutils.
+ */
+static size_t
+get_default_width (void)
+{
+  size_t line_length = 130;
+
+  char const *p = getenv ("COLUMNS");
+  if (p && *p)
+    {
+      unsigned long int tmp_ulong;
+      if (xstrtoul (p, NULL, 0, &tmp_ulong, NULL) == LONGINT_OK
+          && 0 < tmp_ulong && tmp_ulong <= SIZE_MAX)
+        {
+          line_length = tmp_ulong;
+        }
+      else
+        {
+          error (0, 0,
+             _("ignoring invalid width in environment variable COLUMNS: %s"),
+                 quotearg (p));
+        }
+   }
+
+#ifdef TIOCGWINSZ
+  {
+    struct winsize ws;
+
+    if (ioctl (STDOUT_FILENO, TIOCGWINSZ, &ws) != -1
+        && 0 < ws.ws_col && ws.ws_col == (size_t) ws.ws_col)
+      line_length = ws.ws_col;
+  }
+#endif
+
+  return line_length;
+}
+
 
 int
 main (int argc, char **argv)
@@ -668,7 +723,7 @@ main (int argc, char **argv)
   if (! tabsize)
     tabsize = 8;
   if (! width)
-    width = 130;
+    width = get_default_width ();
 
   {
     /* Maximize first the half line width, and then the gutter width,

-----------------------
-- 
 -----Open up your eyes, open up your mind, open up your code -------   
/ Dr. David Alan Gilbert    |       Running GNU/Linux       | Happy  \ 
\ gro.gilbert @ treblig.org |                               | In Hex /
 \ _________________________|_____ http://www.treblig.org   |_______/