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