From 7386c291be8e2de115f2e161886e872429edadd7 Mon Sep 17 00:00:00 2001
From: Paul Eggert <eggert@cs.ucla.edu>
Date: Fri, 14 Feb 2025 13:10:02 -0800
Subject: =?UTF-8?q?cat:=20fix=20plain=20=E2=80=98cat=E2=80=99=20bug?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* src/cat.c (main): Do not fail with plain ‘cat’ where input and
output are both /dev/tty, if the output happens to have O_APPEND set.
Problem reported by lilydjwg <https://bugs.gnu.org/76255>.
Also, don’t report an error if the seek position is at or after EOF,
even if O_APPEND is set.
---
 src/cat.c | 26 +++++++++++++-------------
 1 file changed, 13 insertions(+), 13 deletions(-)

(limited to 'src/cat.c')

diff --git a/src/cat.c b/src/cat.c
index 274f844..c022103 100644
--- a/src/cat.c
+++ b/src/cat.c
@@ -646,9 +646,15 @@
   idx_t outsize = io_blksize (stat_buf);
 
   /* Device, I-node number and lazily-acquired flags of the output.  */
-  dev_t out_dev = stat_buf.st_dev;
-  ino_t out_ino = stat_buf.st_ino;
+  dev_t out_dev;
+  ino_t out_ino;
   int out_flags = -2;
+  bool have_out_dev = ! (S_TYPEISSHM (&stat_buf) || S_TYPEISTMO (&stat_buf));
+  if (have_out_dev)
+    {
+      out_dev = stat_buf.st_dev;
+      out_ino = stat_buf.st_ino;
+   }
 
   /* True if the output is a regular file.  */
   bool out_isreg = S_ISREG (stat_buf.st_mode) != 0;
@@ -706,22 +712,25 @@
          output device.  It's better to catch this error earlier
          rather than later.  */
 
-      if (stat_buf.st_dev == out_dev && stat_buf.st_ino == out_ino)
+      if (! (S_ISFIFO (stat_buf.st_mode) || S_ISSOCK (stat_buf.st_mode)
+             || S_TYPEISSHM (&stat_buf) || S_TYPEISTMO (&stat_buf))
+          && have_out_dev
+          && stat_buf.st_dev == out_dev && stat_buf.st_ino == out_ino)
         {
-          if (out_flags < -1)
-            out_flags = fcntl (STDOUT_FILENO, F_GETFL);
-          bool exhausting = 0 <= out_flags && out_flags & O_APPEND;
-          if (!exhausting)
-            {
-              off_t in_pos = lseek (input_desc, 0, SEEK_CUR);
-              if (0 <= in_pos)
-                exhausting = in_pos < lseek (STDOUT_FILENO, 0, SEEK_CUR);
-            }
-          if (exhausting)
+          off_t in_pos = lseek (input_desc, 0, SEEK_CUR);
+          if (0 <= in_pos)
             {
-              error (0, 0, _("%s: input file is output file"), quotef (infile));
-              ok = false;
-              goto contin;
+              if (out_flags < -1)
+                out_flags = fcntl (STDOUT_FILENO, F_GETFL);
+              int whence = (0 <= out_flags && out_flags & O_APPEND
+                            ? SEEK_END : SEEK_CUR);
+              if (in_pos < lseek (STDOUT_FILENO, 0, whence))
+                {
+                  error (0, 0, _("%s: input file is output file"),
+                         quotef (infile));
+                  ok = false;
+                  goto contin;
+                }
             }
         }
 
