From 6deeba06e4406b618d820ad9eb3ac49273775b4a Mon Sep 17 00:00:00 2001
From: Assaf Gordon <assafgordon@gmail.com>
Date: Mon, 4 Apr 2016 00:37:38 -0400
Subject: [PATCH] seq: detect and report I/O errors

Ensure I/O errors are detected (and terminate seq), preventing seq
from infloop (or running for long time with a large
range) upon write errors or ignored SIGPIPE. Examples:

     seq 1 inf > /dev/full             (seq_fast)
     seq 1.1 0.1 inf >/dev/full        (print_numbers)

* src/seq.c: (seq_fast,print_numbers): explicitly check for write errors
   and break out of the output writing loop.
* tests/misc/seq-io-errors.sh: test new error detection.
* tests/local.mk: add new test.
* NEWS: mention change.
---
 NEWS                        |  2 ++
 src/seq.c                   |  7 +++++-
 tests/local.mk              |  1 +
 tests/misc/seq-io-errors.sh | 57 +++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 66 insertions(+), 1 deletion(-)
 create mode 100755 tests/misc/seq-io-errors.sh

diff --git a/NEWS b/NEWS
index d3597d0..8055396 100644
--- a/NEWS
+++ b/NEWS
@@ -15,6 +15,8 @@ GNU coreutils NEWS                                    -*- outline -*-
    stty --help no longer outputs extraneous gettext header lines
    for translated languages. [bug introduced in coreutils-8.24]
 
+   seq no longer infloops under certain write-error conditions.
+
 ** Changes in behavior
 
    stat now outputs nanosecond information for time stamps even if
diff --git a/src/seq.c b/src/seq.c
index fbb94a0..5fe2229 100644
--- a/src/seq.c
+++ b/src/seq.c
@@ -326,6 +326,8 @@ print_numbers (char const *fmt, struct layout layout,
             }
 
           fputs (separator, stdout);
+          if (ferror (stdout))
+            break;
         }
 
       fputs (terminator, stdout);
@@ -495,7 +497,10 @@ seq_fast (char const *a, char const *b)
              output buffer so far, and reset to start of buffer.  */
           if (buf_end - (p_len + 1) < bufp)
             {
-              fwrite (buf, bufp - buf, 1, stdout);
+              if (fwrite (buf, bufp - buf, 1, stdout) != 1) {
+                ok = 0;
+                break;
+              }
               bufp = buf;
             }
         }
diff --git a/tests/local.mk b/tests/local.mk
index a83c3d0..8b2a19a 100644
--- a/tests/local.mk
+++ b/tests/local.mk
@@ -235,6 +235,7 @@ all_tests =					\
   tests/misc/ptx.pl				\
   tests/misc/test.pl				\
   tests/misc/seq.pl				\
+  tests/misc/seq-io-errors.sh			\
   tests/misc/seq-long-double.sh			\
   tests/misc/seq-precision.sh			\
   tests/misc/head.pl				\
diff --git a/tests/misc/seq-io-errors.sh b/tests/misc/seq-io-errors.sh
new file mode 100755
index 0000000..7e06cdd
--- /dev/null
+++ b/tests/misc/seq-io-errors.sh
@@ -0,0 +1,57 @@
+#!/bin/sh
+# Test for proper detection of I/O errors in seq
+
+# Copyright (C) 2016 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
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ seq
+
+if ! test -w /dev/full || ! test -c /dev/full; then
+  skip_ '/dev/full is required'
+fi
+
+# Run 'seq' with a timeout, preventing infinite-loop run.
+# if seq fails (returns 1) - the test passed.
+# if seq times-out (timeout forces exitcode of 124) - the test failed.
+# any other code - framework failure.
+timed_seq_fail()
+{
+  timeout 5 seq "$@" > /dev/full 2>/dev/null
+  rc=$?
+  if test $rc -eq 1 ; then
+    :
+  elif test $rc -eq 124 ; then
+    fail=1
+  else
+    framework_failure_
+  fi
+}
+
+
+# Test infinite sequence, using fast-path method (seq_fast).
+timed_seq_fail 1 inf
+
+# Test infinite sequence, using slow-path method (print_numbers).
+timed_seq_fail 1.1 .1 inf
+
+# Test non-infinite sequence, using slow-path method (print_numbers).
+# (despite being non-infinite, the entire sequence should take long time to
+#  print. Thus, either an I/O error is detected immedaitely, or seq will
+#  timeout).
+timed_seq_fail 1 0.0001 99999999
+
+
+Exit $fail
-- 
1.9.1

