GNU 'cat' calls fstat() on the input file and standard output. When we
use splice(), it would be nice to check if they are pipes without having
isapipe() repeat the fstat() call.

I don't think this is possible on Windows, but that shouldn't matter for
coreutils. I've hidden the declaration there anyways, so no one
mistakenly uses it thinking that is portable.

The attached patch feels obvious, but I will leave it for review
anyways. Maybe someone more creative than I will prefer to name the
function something else.

Collin

>From 6f6ab94ba06fa9a0cf323856fd205e98f3867af9 Mon Sep 17 00:00:00 2001
Message-ID: <6f6ab94ba06fa9a0cf323856fd205e98f3867af9.1775617228.git.collin.fu...@gmail.com>
From: Collin Funk <[email protected]>
Date: Tue, 7 Apr 2026 19:54:25 -0700
Subject: [PATCH] isapipe: Add the st_isapipe function.

* lib/isapipe.c: Include sys/stat.h.
(st_isapipe) [!(_WIN32 && !__CYGWIN__)]: New declaration.
* lib/isapipe.h (st_isapipe) [!(_WIN32 && !__CYGWIN__)]: New function.
(isapipe) [!(_WIN32 && !__CYGWIN__)]: Use it.
---
 ChangeLog     |  8 ++++++++
 lib/isapipe.c | 34 +++++++++++++++++++---------------
 lib/isapipe.h |  7 ++++++-
 3 files changed, 33 insertions(+), 16 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index d526ad8b0a..4883e91066 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2026-04-07  Collin Funk  <[email protected]>
+
+	isapipe: Add the st_isapipe function.
+	* lib/isapipe.c: Include sys/stat.h.
+	(st_isapipe) [!(_WIN32 && !__CYGWIN__)]: New declaration.
+	* lib/isapipe.h (st_isapipe) [!(_WIN32 && !__CYGWIN__)]: New function.
+	(isapipe) [!(_WIN32 && !__CYGWIN__)]: Use it.
+
 2026-04-06  Bruno Haible  <[email protected]>
 
 	stat: Update doc regarding mingw bug.
diff --git a/lib/isapipe.c b/lib/isapipe.c
index 758d05d092..b456579f2d 100644
--- a/lib/isapipe.c
+++ b/lib/isapipe.c
@@ -62,23 +62,13 @@ isapipe (int fd)
 #  define PIPE_LINK_COUNT_MAX ((nlink_t) (-1))
 # endif
 
-/* Return 1 if FD is a pipe, 0 if not, -1 (setting errno) on error.
-
-   Test fairly strictly whether FD is a pipe.  lseek and checking for
-   ESPIPE does not suffice, since many non-pipe files cause lseek to
-   fail with errno == ESPIPE.  */
-
+/* Return 1 if ST is a pipe, 0 if not, -1 (setting errno) on error.  */
 int
-isapipe (int fd)
+st_isapipe (struct stat const *const st)
 {
   nlink_t pipe_link_count_max = PIPE_LINK_COUNT_MAX;
   bool check_for_fifo = (HAVE_FIFO_PIPES == 1);
 
-  struct stat st;
-  int fstat_result = fstat (fd, &st);
-  if (fstat_result != 0)
-    return fstat_result;
-
   /* We want something that succeeds only for pipes, but on
      POSIX-conforming hosts S_ISFIFO succeeds for both FIFOs and pipes
      and we know of no portable, reliable way to distinguish them in
@@ -91,7 +81,7 @@ isapipe (int fd)
 
   if (! ((HAVE_FIFO_PIPES == 0 || HAVE_FIFO_PIPES == 1)
          && PIPE_LINK_COUNT_MAX != (nlink_t) -1)
-      && (S_ISFIFO (st.st_mode) | S_ISSOCK (st.st_mode)))
+      && (S_ISFIFO (st->st_mode) | S_ISSOCK (st->st_mode)))
     {
       int fd_pair[2];
       int pipe_result = pipe (fd_pair);
@@ -115,8 +105,22 @@ isapipe (int fd)
     }
 
   return
-    (st.st_nlink <= pipe_link_count_max
-     && (check_for_fifo ? S_ISFIFO (st.st_mode) : S_ISSOCK (st.st_mode)));
+    (st->st_nlink <= pipe_link_count_max
+     && (check_for_fifo ? S_ISFIFO (st->st_mode) : S_ISSOCK (st->st_mode)));
+}
+
+/* Return 1 if FD is a pipe, 0 if not, -1 (setting errno) on error.
+
+   Test fairly strictly whether FD is a pipe.  lseek and checking for
+   ESPIPE does not suffice, since many non-pipe files cause lseek to
+   fail with errno == ESPIPE.  */
+
+int
+isapipe (int fd)
+{
+  struct stat st;
+  int fstat_result = fstat (fd, &st);
+  return fstat_result < 0 ? fstat_result : st_isapipe (&st);
 }
 
 #endif
diff --git a/lib/isapipe.h b/lib/isapipe.h
index 841b200628..5a638fc3c8 100644
--- a/lib/isapipe.h
+++ b/lib/isapipe.h
@@ -15,11 +15,13 @@
    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
 
-/* This file uses HAVE_FIFO_PIPES.  */
+/* This file uses _GL_ATTRIBUTE_PURE and HAVE_FIFO_PIPES.  */
 #if !_GL_CONFIG_H_INCLUDED
  #error "Please include config.h first."
 #endif
 
+#include <sys/stat.h>
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -30,6 +32,9 @@ extern "C" {
 # define HAVE_FIFO_PIPES (-1)
 #endif
 
+#if ! (defined _WIN32 && ! defined __CYGWIN__)
+int st_isapipe (struct stat const *const st) _GL_ATTRIBUTE_PURE;
+#endif
 int isapipe (int fd);
 
 
-- 
2.53.0

Reply via email to