Hi Paul,

Paul Eggert <egg...@cs.ucla.edu> writes:

> Thanks for looking into that. Unfortunately POSIX says -p should be
> ignored only if standard output is a terminal, and that newline should
> be read from /dev/tty, not from standard input. This is so that users
> can pipe into 'pr -p'. So the proposed patch needs some changes. Here
> are the issues I found:

Thanks for the thorough review.

I think that v3 attached should cover everything.

Collin

>From 79dc6d5cfbc45966cd039a599c7db372452b1a9a Mon Sep 17 00:00:00 2001
Message-ID: <79dc6d5cfbc45966cd039a599c7db372452b1a9a.1753669052.git.collin.fu...@gmail.com>
From: Collin Funk <collin.fu...@gmail.com>
Date: Sun, 27 Jul 2025 15:00:15 -0700
Subject: [PATCH v3] pr: implement '-p' as required by POSIX Issue 6

* src/pr.c (pause_option): New variable.
(tty_fp): Likewise.
(short_options): Add '-p'.
(long_options): Add '--pause'.
(main): Add the option. Ignore it if standard output is not a tty. Open
a 'FILE *' for /dev/tty and fail if any operations on it are
unsuccessful.
(print_files): If the option is use emit '\a' to stderr and wait until a
newline is read to print the next page.
(usage): Mention the new option.
* doc/coreutils.texi (pr invocation): Document the new option.
* NEWS: Mention the new option.
---
 NEWS               |  4 ++++
 doc/coreutils.texi |  8 +++++++
 src/pr.c           | 55 +++++++++++++++++++++++++++++++++++++++++++---
 3 files changed, 64 insertions(+), 3 deletions(-)

diff --git a/NEWS b/NEWS
index 0b2be7116..776932c69 100644
--- a/NEWS
+++ b/NEWS
@@ -64,6 +64,10 @@ GNU coreutils NEWS                                    -*- outline -*-
   Iranian locale (fa_IR) and for the Ethiopian locale (am_ET), and also
   does so more consistently for the Thailand locale (th_TH.UTF-8).
 
+  pr now supports the -p option, to pause upon printing each page until
+  a newline character is read from /dev/tty, as required by POSIX Issue
+  6.  The corresponding long option is --pause.
+
 
 * Noteworthy changes in release 9.7 (2025-04-09) [stable]
 
diff --git a/doc/coreutils.texi b/doc/coreutils.texi
index 7ca5b222c..a8193f5ff 100644
--- a/doc/coreutils.texi
+++ b/doc/coreutils.texi
@@ -2825,6 +2825,14 @@ @node pr invocation
 set with the @option{-W/-w} option.  A limited overflow may occur with
 numbered single column output (compare @option{-n} option).
 
+@item -p
+@itemx --pause
+@opindex -p
+@opindex --pause
+After printing each page, print an alert (bell) to standard error and
+wait for a newline to be read from @file{/dev/tty} before printing the
+next page.  This option is ignored unless standard output is a tty.
+
 @item -r
 @itemx --no-file-warnings
 @opindex -r
diff --git a/src/pr.c b/src/pr.c
index e7081a059..22e752675 100644
--- a/src/pr.c
+++ b/src/pr.c
@@ -711,6 +711,12 @@ static char *custom_header;
 /* (-D) Date format for the header.  */
 static char const *date_format;
 
+/* If true, pause after each page until a newline is read from /dev/tty.  */
+static bool pause_option;
+
+/* Used to read from /dev/tty if --p/--pause is in use.  */
+static FILE *tty_fp;
+
 /* The local time zone rules, as per the TZ environment variable.  */
 static timezone_t localtz;
 
@@ -738,7 +744,7 @@ enum
 };
 
 static char const short_options[] =
-  "-0123456789D:FJN:S::TW:abcde::fh:i::l:mn::o:rs::tvw:";
+  "-0123456789D:FJN:S::TW:abcde::fh:i::l:mn::po:rs::tvw:";
 
 static struct option const long_options[] =
 {
@@ -758,6 +764,7 @@ static struct option const long_options[] =
   {"number-lines", optional_argument, nullptr, 'n'},
   {"first-line-number", required_argument, nullptr, 'N'},
   {"indent", required_argument, nullptr, 'o'},
+  {"pause", no_argument, nullptr, 'p'},
   {"no-file-warnings", no_argument, nullptr, 'r'},
   {"separator", optional_argument, nullptr, 's'},
   {"sep-string", optional_argument, nullptr, 'S'},
@@ -999,6 +1006,9 @@ main (int argc, char **argv)
           chars_per_margin = getoptnum (optarg, 0,
                                         _("'-o MARGIN' invalid line offset"));
           break;
+        case 'p':
+          pause_option = true;
+          break;
         case 'r':
           ignore_failed_opens = true;
           break;
@@ -1079,6 +1089,18 @@ main (int argc, char **argv)
     error (EXIT_FAILURE, 0,
            _("cannot specify both printing across and printing in parallel"));
 
+  if (pause_option)
+    {
+      pause_option = isatty (STDOUT_FILENO);
+      if (pause_option)
+        {
+          tty_fp = fopen ("/dev/tty", "r");
+          if (tty_fp == nullptr)
+            error (EXIT_FAILURE, errno, _("cannot open %s for reading"),
+                   quoteaf ("/dev/tty"));
+        }
+    }
+
 /* Translate some old short options to new/long options.
    To meet downward compatibility with other UNIX pr utilities
    and some POSIX specifications. */
@@ -1149,6 +1171,8 @@ main (int argc, char **argv)
 
   if (have_read_stdin && fclose (stdin) == EOF)
     error (EXIT_FAILURE, errno, _("standard input"));
+  if (pause_option && fclose (tty_fp) == EOF)
+    error (EXIT_FAILURE, errno, "%s", quotef ("/dev/tty"));
   main_exit (failed_opens ? EXIT_FAILURE : EXIT_SUCCESS);
 }
 
@@ -1636,8 +1660,31 @@ print_files (int number_of_files, char **av)
   init_funcs ();
 
   line_number = line_count;
-  while (print_page ())
-    ;
+  for (;;)
+    {
+      if (pause_option)
+        {
+          if (putc ('\a', stderr) == EOF || fflush (stderr) != 0)
+            write_error ();
+          for (;;)
+            {
+              int ch = getc (tty_fp);
+              if (ch == EOF)
+                {
+                  /* Just exit if the user presses Ctrl-D.  */
+                  if (feof (tty_fp))
+                    goto finish;
+                  error (EXIT_FAILURE, errno, _("error reading %s"),
+                         quoteaf ("/dev/tty"));
+                }
+              if (ch == '\n')
+                break;
+            }
+        }
+      if (! print_page ())
+        break;
+    }
+ finish:
 }
 
 /* Initialize header information.
@@ -2824,6 +2871,8 @@ Paginate or columnate FILE(s) for printing.\n\
   -o, --indent=MARGIN\n\
                     offset each line with MARGIN (zero) spaces, do not\n\
                     affect -w or -W, MARGIN will be added to PAGE_WIDTH\n\
+  -p, --pause       pause at the beginning of each page until a newline\n\
+                    is read from /dev/tty.\n\
   -r, --no-file-warnings\n\
                     omit warning when a file cannot be opened\n\
 "), stdout);
-- 
2.50.1

Reply via email to