bug#47243: pr lacks -p

Collin Funk <[email protected]>
Newsgroups gmane.comp.gnu.core-utils.bugs
Message-ID <[email protected]>
Paul Eggert <[email protected]> writes:

> On 2025-07-28 10:36, Collin Funk wrote:
>> I don't really like the idea of changing '-f' depending on whether
>> POSIXLY_CORRECT is defined. So I would prefer this as well.
>
> On second thought (sorry...) I now think I understand why GNU pr
> behaves the way it does. The GNU coding standards[1] say "...please
> don’t make the behavior of a command-line program depend on the type
> of output device it gets as standard output or standard input. Device
> independence is an important principle of the system’s design; do not
> compromise it merely to save someone from typing an option now and
> then."
>
> The standards go on to list some exceptions, such as 'ls'. 'pr' is not
> one of the exceptions. Currently there are a few other exceptions in
> Coreutils ('df', 'mv', 'nohup', 'rm', 'tail') but I guess 'pr' didn't
> make the cut when it was written.
>
> That is, my theory is that when GNU 'pr' was written, its original
> author Pete TerMaat followed the GNU coding standards, and
> deliberately made '-f' and '-F' aliases, because that way the behavior
> of pr would not depend on isatty (STDOUT_FILENO). And Pete did not
> implement -p because it didn't sound useful if we follow the coding
> standards.
>
> If this theory is correct, then a fix that would be more in the spirit
> of GNU would be something like this:

Interesting, I did not know about this part of the GNU Coding Standards.

> (a) Implement -p as if isatty (STDOUT_FILENO) is true. This form of -p
> is useful only when stdout is a terminal, but that's good enough since
> nobody uses (or will use :-) -p.
>
> (b) If POSIXLY_CORRECT is set, change -f and -p so that they both
> worry about isatty (STDOUT_FILENO), as POSIX requires.
>
> This would conform to POSIX, would address Pádraig's concern about
> compatibility, and would follow the GNU coding standards.

That sounds good to me. I attached a v4 patch that should behave this
way and address the other issues that you mentioned.

> It'd be less useful in practice, but that doesn't matter since the
> practical need for this behavior vanished decades ago.

Thanks for again for the thorough review and explanations. I find it
funny that I assumed this change was simple and learned it's purpose was
for logging in on a teletype. The Model 37 predates me ~30 years, so it
never occured to me that the purpose was to not print logins.

Collin
v4-0001-pr-implement-p-and-modify-f-conforming-to-POSIX.patch (text/x-patch, 8.3 KB)
From bb063435c2019af6a6b9e26c67c4b378b9243919 Mon Sep 17 00:00:00 2001
Message-ID: <bb063435c2019af6a6b9e26c67c4b378b9243919.1753762640.git.collin.funk1@gmail.com>
From: Collin Funk <[email protected]>
Date: Sun, 27 Jul 2025 15:00:15 -0700
Subject: [PATCH v4] pr: implement '-p' and modify '-f' conforming to POSIX

* src/pr.c (pause_option, pause_on_first_page, tty_fd): New variables.
(short_options): Add '-p'.
(long_options): Add '--pause'.
(main): Add the option. Open and close a file descriptor to
/dev/tty. Disable pausing if POSIXLY_CORRECT is set.
(print_files): If the pausing is enabled, emit '\a' to standard error
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. Document
the behaviors of POSIXLY_CORRECT on -p and -f.
* NEWS: Mention the new option. Mention the new behavior of 'pr -f' with
the POSIXLY_CORRECT environment variable set.
---
 NEWS               |  7 +++++
 doc/coreutils.texi | 17 ++++++++++++
 src/pr.c           | 66 +++++++++++++++++++++++++++++++++++++++++++---
 3 files changed, 86 insertions(+), 4 deletions(-)

diff --git a/NEWS b/NEWS
index 0b2be7116..6e78c81d3 100644
--- a/NEWS
+++ b/NEWS
@@ -7,6 +7,9 @@ GNU coreutils NEWS                                    -*- outline -*-
   'factor' is now much faster at identifying large prime numbers,
   and significantly faster on composite numbers greater than 2^128.
 
+  'pr -f' with the POSIXLY_CORRECT environment variable set will pause
+  until a newline is read from /dev/tty before printing the first page.
+
 ** Bug fixes
 
   cksum was not compilable by Apple LLVM 10.0.0 x86-64, which
@@ -64,6 +67,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.  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..25e4e4e51 100644
--- a/doc/coreutils.texi
+++ b/doc/coreutils.texi
@@ -2726,6 +2726,11 @@ @node pr invocation
 Use a form feed instead of newlines to separate output pages.  This does
 not alter the default page length of 66 lines.
 
+@vindex POSIXLY_CORRECT
+If the @env{POSIXLY_CORRECT} environment variable is set and standard
+output is a tty, then using @option{-f} will also pause until a newline
+is read from @file{/dev/tty} before printing the first page.
+
 @item -h @var{header}
 @itemx --header=@var{header}
 @opindex -h
@@ -2825,6 +2830,18 @@ @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.
+
+@vindex POSIXLY_CORRECT
+If the @env{POSIXLY_CORRECT} environment variable is set and standard
+output is not a tty, this option will be ignored.
+
 @item -r
 @itemx --no-file-warnings
 @opindex -r
diff --git a/src/pr.c b/src/pr.c
index e7081a059..298be4ebd 100644
--- a/src/pr.c
+++ b/src/pr.c
@@ -711,6 +711,15 @@ 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;
+
+/* If true, pause on only the first page.  */
+static bool pause_on_first_page;
+
+/* Used to read from /dev/tty if --p/--pause is in use.  */
+static int tty_fd;
+
 /* The local time zone rules, as per the TZ environment variable.  */
 static timezone_t localtz;
 
@@ -738,7 +747,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 +767,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'},
@@ -868,6 +878,8 @@ main (int argc, char **argv)
   idx_t n_digits = 0;
   idx_t n_alloc = 0;
 
+  bool posixly_correct = (getenv ("POSIXLY_CORRECT") != nullptr);
+
   initialize_main (&argc, &argv);
   set_program_name (argv[0]);
   setlocale (LC_ALL, "");
@@ -958,6 +970,8 @@ main (int argc, char **argv)
           untabify_input = true;
           break;
         case 'f':
+          pause_on_first_page = posixly_correct;
+          FALLTHROUGH;
         case 'F':
           use_form_feed = true;
           break;
@@ -999,6 +1013,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;
@@ -1061,7 +1078,7 @@ main (int argc, char **argv)
     }
 
   if (! date_format)
-    date_format = (getenv ("POSIXLY_CORRECT") && !hard_locale (LC_TIME)
+    date_format = (posixly_correct && !hard_locale (LC_TIME)
                    ? "%b %e %H:%M %Y"
                    : "%Y-%m-%d %H:%M");
 
@@ -1079,6 +1096,22 @@ main (int argc, char **argv)
     error (EXIT_FAILURE, 0,
            _("cannot specify both printing across and printing in parallel"));
 
+  /* POSIX states that the pausing behavior of -f and -p should only occur if
+     standard output is a tty.  Only behave this way is POSIXLY_CORRECT is set
+     since the GNU Coding Standards discourages changing program behavior based
+     on output device type.  */
+  if ((pause_option || pause_on_first_page) && posixly_correct
+      && ! isatty (STDOUT_FILENO))
+    pause_option = pause_on_first_page = false;
+
+  if (pause_option || pause_on_first_page)
+    {
+      tty_fd = open ("/dev/tty", O_RDONLY);
+      if (tty_fd < 0)
+        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 +1182,8 @@ main (int argc, char **argv)
 
   if (have_read_stdin && fclose (stdin) == EOF)
     error (EXIT_FAILURE, errno, _("standard input"));
+  if (pause_option && close (tty_fd) < 0)
+    error (EXIT_FAILURE, errno, "%s", quotef ("/dev/tty"));
   main_exit (failed_opens ? EXIT_FAILURE : EXIT_SUCCESS);
 }
 
@@ -1636,8 +1671,29 @@ print_files (int number_of_files, char **av)
   init_funcs ();
 
   line_number = line_count;
-  while (print_page ())
-    ;
+  while (true)
+    {
+      if (pause_option || (pause_on_first_page && page_number == 1))
+        {
+          if (putc ('\a', stderr) == EOF || fflush (stderr) != 0)
+            write_error ();
+          while (true)
+            {
+              unsigned char ch;
+              ssize_t bytes_read = read (tty_fd, &ch, sizeof ch);
+              if (bytes_read < 0)
+                error (EXIT_FAILURE, errno, _("error reading %s"),
+                       quoteaf ("/dev/tty"));
+              /* Just exit if the user presses Ctrl-D.  */
+              if (bytes_read == 0)
+                return;
+              if (ch == '\n')
+                break;
+            }
+        }
+      if (! print_page ())
+        break;
+    }
 }
 
 /* Initialize header information.
@@ -2824,6 +2880,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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.