Paul Eggert <egg...@cs.ucla.edu> writes: >> +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 sentence should start "Before" not "After", with the rest of the > sentence changed accordingly. There is no bell printed after the last > page. > > Looking at the code, it seems that this mistake has leaked into the > code as well, in that sometimes 'pr' will print an alert and wait for > '\n' at end of file, when there is no page to print. If I'm right, > that needs to be fixed; pr should wait for '\n' only if it will > actually print something afterward.
Fixing this required a bit more understanding of the code on my part, but I think the attached patch should address it. The existing code uses a 'pad_vertically' variable to keep track of whether the current page has text or a header that needs to be printed. Therefore, any time we set it to true, we must also pause. But only once, at the start of the page. Collin
>From 8afb79bbf294687fd4bdd3c92d1599f5021f703a Mon Sep 17 00:00:00 2001 Message-ID: <8afb79bbf294687fd4bdd3c92d1599f5021f703a.1753850358.git.collin.fu...@gmail.com> From: Collin Funk <collin.fu...@gmail.com> Date: Sun, 27 Jul 2025 15:00:15 -0700 Subject: [PATCH v5] pr: implement '-p' and modify '-f' conforming to POSIX * src/pr.c (last_paused_page_number, pause_option, pause_on_first_page) (tty_fd): New variables. (short_options): Add '-p'. (long_options): Add '--pause'. (main): Add the option. Open a file descriptor to /dev/tty. Disable pausing if POSIXLY_CORRECT is set. (pause_maybe): New function. (read_line, print_stored): Call it whenever pad_vertically is set to true. (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 | 80 ++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 101 insertions(+), 3 deletions(-) diff --git a/NEWS b/NEWS index 0b2be7116..3034ce718 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 before 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 40ecf3126..5df11f910 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 +This option will also pause until a newline is read from @file{/dev/tty} +before printing the first page if the @env{POSIXLY_CORRECT} environment +variable is set and standard output is associated with a terminal. + @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 +Before 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 +This option is ignored if the @env{POSIXLY_CORRECT} environment variable +is set and standard output is not associated with a terminal. + @item -r @itemx --no-file-warnings @opindex -r diff --git a/src/pr.c b/src/pr.c index e7081a059..5d945910c 100644 --- a/src/pr.c +++ b/src/pr.c @@ -419,6 +419,7 @@ typedef struct COLUMN COLUMN; static int char_to_clump (char c); static bool read_line (COLUMN *p); +static void pause_maybe (void); static bool print_page (void); static bool print_stored (COLUMN *p); static bool open_file (char *name, COLUMN *p); @@ -618,6 +619,9 @@ static int files_ready_to_read = 0; /* Current page number. Displayed in header. */ static uintmax_t page_number; +/* The last page number that we paused on. */ +static uintmax_t last_paused_page_number; + /* Current line number. Displayed when -n flag is specified. When printing files in parallel (-m flag), line numbering is as follows: @@ -711,6 +715,15 @@ static char *custom_header; /* (-D) Date format for the header. */ static char const *date_format; +/* If true, pause before each page until a newline is read from /dev/tty. */ +static bool pause_option; + +/* If true, pause before 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 +751,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 +771,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 +882,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 +974,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 +1017,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 +1082,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 +1100,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 occur only if + standard output is associated with a terminal. 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. */ @@ -1640,6 +1677,36 @@ print_files (int number_of_files, char **av) ; } +/* Pause until reading a newline from /dev/tty if needed. This function should + not be called until we know that we have text or a header to print for a + page. This means it must be called when pad_vertically is set to true. */ + +static void +pause_maybe (void) +{ + if ((pause_option || (pause_on_first_page && page_number == 1)) + && last_paused_page_number < page_number) + { + last_paused_page_number = page_number; + if (pause_option + && (fputc ('\a', stderr) == EOF || fflush (stderr) != 0)) + write_error (); + while (true) + { + 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; + } + } +} + /* Initialize header information. If DESC is non-negative, it is a file descriptor open to FILENAME for reading. */ @@ -2452,6 +2519,7 @@ read_line (COLUMN *p) if (print_a_header && !storing_columns) { pad_vertically = true; + pause_maybe (); print_header (); } else if (keep_FF) @@ -2476,6 +2544,7 @@ read_line (COLUMN *p) if (p->char_func != store_char) { pad_vertically = true; + pause_maybe (); if (print_a_header && !storing_columns) print_header (); @@ -2586,7 +2655,10 @@ print_stored (COLUMN *p) pad_vertically = true; if (print_a_header) - print_header (); + { + pause_maybe (); + print_header (); + } if (p->status == FF_FOUND) { @@ -2824,6 +2896,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