Paul Eggert <egg...@cs.ucla.edu> 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
>From bb063435c2019af6a6b9e26c67c4b378b9243919 Mon Sep 17 00:00:00 2001 Message-ID: <bb063435c2019af6a6b9e26c67c4b378b9243919.1753762640.git.collin.fu...@gmail.com> From: Collin Funk <collin.fu...@gmail.com> 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