Ensure that cat works with any of the options, -A -v -e -E -T,
authorJim Meyering <jim@meyering.net>
Thu, 8 Jun 2006 20:13:37 +0000 (20:13 +0000)
committerJim Meyering <jim@meyering.net>
Thu, 8 Jun 2006 20:13:37 +0000 (20:13 +0000)
when applied to files in /proc and /sys, even when the FIONREAD
ioctl produces nonsensical results.  Before this change, cat would
produce no output (or truncated output), for some linux kernels.

* src/cat.c (write_pending): New function, factored out of cat.
(cat): Also interpret a negative ioctl/FIONREAD count as indicating
that there are bytes to read.  Some versions of linux-2.6.16 do that.
Write any pending output before returning.
Reported by Dan Jacobson in <http://bugs.debian.org/370583>.
* NEWS: Mention this bug fix.
* tests/misc/cat-proc: New file.  Test for the above.
* tests/misc/Makefile.am (TESTS): Add cat-proc.

ChangeLog
NEWS
src/cat.c
tests/misc/Makefile.am
tests/misc/cat-proc [new file with mode: 0755]

index 0036b94..805965d 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,7 +1,23 @@
-2006-06-07  Paul Eggert  <eggert@cs.ucla.edu>
+2006-06-08  Jim Meyering  <jim@meyering.net>
 
        * Version 6.0-cvs.
 
+       Ensure that cat works with any of the options, -A -v -e -E -T,
+       when applied to files in /proc and /sys, even when the FIONREAD
+       ioctl produces nonsensical results.  Before this change, cat would
+       produce no output (or truncated output), for some linux kernels.
+
+       * src/cat.c (write_pending): New function, factored out of cat.
+       (cat): Also interpret a negative ioctl/FIONREAD count as indicating
+       that there are bytes to read.  Some versions of linux-2.6.16 do that.
+       Write any pending output before returning.
+       Reported by Dan Jacobson in <http://bugs.debian.org/370583>.
+       * NEWS: Mention this bug fix.
+       * tests/misc/cat-proc: New file.  Test for the above.
+       * tests/misc/Makefile.am (TESTS): Add cat-proc.
+
+2006-06-07  Paul Eggert  <eggert@cs.ucla.edu>
+
        * src/expr.c (eval4): Detect overflow properly when multiplying
        INTMAX_MIN * -1.
 
diff --git a/NEWS b/NEWS
index be511eb..fdca452 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -138,6 +138,10 @@ GNU coreutils NEWS                                    -*- outline -*-
 
 ** Bug fixes
 
+  cat with any of the options, -A -v -e -E -T, when applied to a
+  file in /proc or /sys (linux-specific), would truncate its output,
+  usually printing nothing.
+
   cp -p would fail in a /proc-less chroot, on some systems
 
   When `cp -RL' encounters the same directory more than once in the
index b08e91e..ec2c528 100644 (file)
--- a/src/cat.c
+++ b/src/cat.c
@@ -1,5 +1,5 @@
 /* cat -- concatenate files and print on the standard output.
-   Copyright (C) 88, 90, 91, 1995-2005 Free Software Foundation, Inc.
+   Copyright (C) 88, 90, 91, 1995-2006 Free Software Foundation, Inc.
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -195,6 +195,22 @@ simple_cat (
     }
 }
 
+/* Write any pending output to STDOUT_FILENO.
+   Pending is defined to be the *BPOUT - OUTBUF bytes starting at OUTBUF.
+   Then set *BPOUT to OUTPUT if it's not already that value.  */
+
+static inline void
+write_pending (char *outbuf, char **bpout)
+{
+  size_t n_write = *bpout - outbuf;
+  if (0 < n_write)
+    {
+      if (full_write (STDOUT_FILENO, outbuf, n_write) != n_write)
+        error (EXIT_FAILURE, errno, _("write error"));
+      *bpout = outbuf;
+    }
+}
+
 /* Cat the file behind INPUT_DESC to the file behind OUTPUT_DESC.
    Return true if successful.
    Called if any option more than -u was specified.
@@ -291,6 +307,7 @@ cat (
 
          if (bpin > eob)
            {
+             bool input_pending = false;
 #ifdef FIONREAD
              int n_to_read = 0;
 
@@ -318,15 +335,12 @@ cat (
                      return false;
                    }
                }
-             if (n_to_read == 0)
+             if (n_to_read != 0)
+               input_pending = true;
 #endif
-               {
-                 size_t n_write = bpout - outbuf;
 
-                 if (full_write (STDOUT_FILENO, outbuf, n_write) != n_write)
-                   error (EXIT_FAILURE, errno, _("write error"));
-                 bpout = outbuf;
-               }
+             if (input_pending)
+               write_pending (outbuf, &bpout);
 
              /* Read more input into INBUF.  */
 
@@ -334,11 +348,13 @@ cat (
              if (n_read == SAFE_READ_ERROR)
                {
                  error (0, errno, "%s", infile);
+                 write_pending (outbuf, &bpout);
                  newlines2 = newlines;
                  return false;
                }
              if (n_read == 0)
                {
+                 write_pending (outbuf, &bpout);
                  newlines2 = newlines;
                  return true;
                }
index 5f6b07a..696b9fd 100644 (file)
@@ -18,6 +18,7 @@ TESTS_ENVIRONMENT = \
 # will execute the test script rather than the standard utility.
 
 TESTS = \
+  cat-proc \
   base64 \
   basename \
   close-stdout \
diff --git a/tests/misc/cat-proc b/tests/misc/cat-proc
new file mode 100755 (executable)
index 0000000..69e2065
--- /dev/null
@@ -0,0 +1,42 @@
+#!/bin/sh
+# Ensure that cat -E produces same output as cat, module `$'s,
+# even when applied to a file in /proc.
+
+if test "$VERBOSE" = yes; then
+  set -x
+  cat --version
+fi
+
+pwd=`pwd`
+t0=`echo "$0"|sed 's,.*/,,'`.tmp; tmp=$t0/$$
+trap 'status=$?; cd $pwd; chmod -R u+rwx $t0; rm -rf $t0 && exit $status' 0
+trap '(exit $?); exit $?' 1 2 13 15
+
+framework_failure=0
+mkdir -p $tmp || framework_failure=1
+cd $tmp || framework_failure=1
+
+if test $framework_failure = 1; then
+  echo "$0: failure in testing framework" 1>&2
+  (exit 1); exit 1
+fi
+
+f=/proc/cpuinfo
+test -f $f \
+  || {
+       echo "$0: no $f skipping this test" 1>&2
+       (exit 77); exit 77
+     }
+
+fail=0
+
+# Yes, parts of /proc/cpuinfo might change between cat runs.
+# If that happens, consider choosing a file that's less likely to change,
+# or just filter out the changing lines.
+cat -E $f | tr -d '$' > out || fail=1
+cat    $f | tr -d '$' > exp || fail=1
+
+cmp out exp || fail=1
+test $fail = 1 && diff out exp 2> /dev/null
+
+(exit $fail); exit $fail