From 5ba7ce6802eccabb15acf01f0f57b25d60e0f3f4 Mon Sep 17 00:00:00 2001 From: Jonas 'Sortie' Termansen Date: Sun, 27 Dec 2015 21:43:41 +0100 Subject: [PATCH] Add backwards support to pager(1). --- utils/pager.cpp | 619 +++++++++++++++++++++++++++++------------------- 1 file changed, 381 insertions(+), 238 deletions(-) diff --git a/utils/pager.cpp b/utils/pager.cpp index c8615207..a1efb52d 100644 --- a/utils/pager.cpp +++ b/utils/pager.cpp @@ -45,9 +45,11 @@ enum control_state CONTROL_STATE_COMMAND, }; -struct display_entry +struct line { - wchar_t c; + char* content; + size_t content_used; + size_t content_length; }; struct pager @@ -55,27 +57,36 @@ struct pager int tty_fd; unsigned int tty_fd_old_termmode; int out_fd; - bool out_fd_tty; + bool out_fd_is_tty; struct winsize out_fd_winsize; - struct display_entry* display; - struct wincurpos begun_wcp; - struct wincurpos current_wcp; mbstate_t in_ps; mbstate_t out_ps; const char* input_prompt_name; + size_t possible_lines; size_t allowed_lines; bool quiting; bool flag_raw_control_chars; bool flag_color_sequences; enum control_state control_state; - unsigned char control_sequence[CONTROL_SEQUENCE_MAX]; + wchar_t control_sequence[CONTROL_SEQUENCE_MAX]; size_t control_sequence_length; bool input_set_color; + struct line* lines; + size_t lines_used; + size_t lines_length; + enum control_state incoming_control_state; + struct line* incoming_line; + size_t incoming_line_width; + size_t current_line; + size_t current_line_offset; + bool allowance_ever_exhausted; + bool skipping_to_end; }; -void pager_init(struct pager* pager) +static void pager_init(struct pager* pager) { - if ( !isatty(pager->tty_fd = 0) ) + pager->tty_fd = 0; + if ( !isatty(pager->tty_fd) ) { if ( (pager->tty_fd = open("/dev/tty", O_RDONLY)) < 0 ) error(1, errno, "/dev/tty"); @@ -85,41 +96,36 @@ void pager_init(struct pager* pager) gettermmode(pager->tty_fd, &pager->tty_fd_old_termmode); settermmode(pager->tty_fd, TERMMODE_KBKEY | TERMMODE_UNICODE | TERMMODE_SIGNAL); - if ( (pager->out_fd_tty = isatty(pager->out_fd = 1)) ) + pager->out_fd = 1; + if ( (pager->out_fd_is_tty = isatty(pager->out_fd)) ) { tcgetwinsize(pager->out_fd, &pager->out_fd_winsize); - size_t display_length = (size_t) pager->out_fd_winsize.ws_col * - (size_t) pager->out_fd_winsize.ws_row; - pager->display = (struct display_entry*) - calloc(display_length, sizeof(struct display_entry)); - if ( !pager->display ) - { - error(0, errno, "malloc"); - settermmode(pager->tty_fd, pager->tty_fd_old_termmode); - exit(1); - } - tcgetwincurpos(pager->out_fd, &pager->begun_wcp); - memcpy(&pager->current_wcp, &pager->begun_wcp, sizeof(struct wincurpos)); - pager->allowed_lines = pager->out_fd_winsize.ws_row - 1; + pager->possible_lines = pager->out_fd_winsize.ws_row - 1; + pager->allowed_lines = pager->possible_lines; } memset(&pager->in_ps, 0, sizeof(pager->in_ps)); memset(&pager->out_ps, 0, sizeof(pager->out_ps)); } -void pager_end(struct pager* pager) +static void pager_end(struct pager* pager) { + for ( size_t i = 0; i < pager->lines_used; i++ ) + free(pager->lines[i].content); + free(pager->lines); settermmode(pager->tty_fd, pager->tty_fd_old_termmode); if ( pager->tty_fd != 0 ) close(pager->tty_fd); } -void pager_prompt(struct pager* pager) +static void pager_prompt(struct pager* pager, bool at_end) { - if ( pager->input_prompt_name[0] && pager->input_set_color ) - dprintf(pager->out_fd, "%s\e[J", pager->input_prompt_name); - else if ( pager->input_prompt_name[0] && !pager->input_set_color ) - dprintf(pager->out_fd, "\e[47;30m%s\e[m\e[J", pager->input_prompt_name); + const char* pre = pager->input_set_color ? "" : "\e[47;30m"; + const char* post = pager->input_set_color ? "" : "\e[m"; + if ( at_end ) + dprintf(pager->out_fd, "%s(END)%s\e[J", pre, post); + else if ( pager->input_prompt_name[0] ) + dprintf(pager->out_fd, "%s%s%s\e[J", pre, pager->input_prompt_name, post); else dprintf(pager->out_fd, ":"); uint32_t codepoint; @@ -132,20 +138,57 @@ void pager_prompt(struct pager* pager) return; } + if ( KBKEY_DECODE(codepoint) == KBKEY_UP ) + { + if ( pager->current_line <= pager->possible_lines ) + continue; + dprintf(pager->out_fd, "\e[2J\e[H"); + pager->current_line -= pager->possible_lines + 1; + pager->current_line_offset = 0; + pager->allowed_lines = pager->possible_lines; + return; + } + if ( codepoint == L' ' || KBKEY_DECODE(codepoint) == KBKEY_PGDOWN ) { dprintf(pager->out_fd, "\r\e[J"); - pager->allowed_lines = pager->out_fd_winsize.ws_row - 1; + pager->allowed_lines = pager->possible_lines; + return; + } + + if ( KBKEY_DECODE(codepoint) == KBKEY_PGUP ) + { + if ( pager->current_line <= pager->possible_lines ) + continue; + size_t distance = pager->possible_lines; + if ( pager->current_line - pager->possible_lines < distance ) + distance = pager->current_line - pager->possible_lines; + dprintf(pager->out_fd, "\e[2J\e[H"); + pager->current_line -= pager->possible_lines + distance; + pager->current_line_offset = 0; + pager->allowed_lines = pager->possible_lines; return; } if ( KBKEY_DECODE(codepoint) == KBKEY_END ) { dprintf(pager->out_fd, "\r\e[J"); + pager->skipping_to_end = true; pager->allowed_lines = SIZE_MAX; return; } + if ( KBKEY_DECODE(codepoint) == KBKEY_HOME ) + { + if ( pager->current_line <= pager->possible_lines ) + continue; + dprintf(pager->out_fd, "\e[2J\e[H"); + pager->current_line = 0; + pager->current_line_offset = 0; + pager->allowed_lines = pager->possible_lines; + return; + } + if ( codepoint == L'q' || codepoint == L'Q' ) { dprintf(pager->out_fd, "\r\e[J"); @@ -158,187 +201,208 @@ void pager_prompt(struct pager* pager) exit(1); } -bool pager_would_push_newline_scroll(struct pager* pager) +static void pager_line_push_char(struct pager* pager, struct line* line, char c) { - return pager->out_fd_winsize.ws_row <= pager->current_wcp.wcp_row + 1; -} - -bool pager_would_push_char_overflow(struct pager* pager, wchar_t wc) -{ - if ( wc == L'\r' ) - return false; - return pager->out_fd_winsize.ws_col <= pager->current_wcp.wcp_col; -} - -bool pager_would_push_char_scroll(struct pager* pager, wchar_t wc) -{ - if ( wc == L'\n' ) - return pager_would_push_newline_scroll(pager); - if ( pager_would_push_char_overflow(pager, wc) ) - return pager_would_push_newline_scroll(pager); - return false; -} - -void pager_push_newline(struct pager* pager) -{ - if ( 0 < pager->allowed_lines && pager->allowed_lines != SIZE_MAX ) - pager->allowed_lines--; - if ( pager->current_wcp.wcp_row + 1 < pager->out_fd_winsize.ws_row ) + if ( line->content_used == line->content_length ) { - pager->current_wcp.wcp_row++; - } - else - { - if ( 0 < pager->begun_wcp.wcp_row ) - pager->begun_wcp.wcp_row--; - } - pager->current_wcp.wcp_col = 0; -} - -bool pager_is_out_of_allowed_lines(struct pager* pager, wchar_t wc) -{ - if ( pager->allowed_lines == 1 && wc != L'\n' && - pager_would_push_char_overflow(pager, wc) ) - return true; - return pager->allowed_lines == 0; -} - -void pager_push_final_char(struct pager* pager, wchar_t wc) -{ - if ( pager->quiting || !wc ) - return; - - while ( pager_is_out_of_allowed_lines(pager, wc) ) - { - pager_prompt(pager); - if ( pager->quiting ) - return; - } - - mbstate_t ps; - memcpy(&ps, &pager->out_ps, sizeof(mbstate_t)); - char mb[MB_CUR_MAX]; - size_t mb_size = wcrtomb(mb, wc, &ps); - if ( mb_size == (size_t) -1 ) - { - memset(&pager->out_ps, 0, sizeof(pager->out_ps)); - return; - } - - if ( writeall(pager->out_fd, mb, mb_size) < mb_size ) - { - error(0, errno, "write: "); - pager_end(pager); - exit(1); - } - memcpy(&pager->out_ps, &ps, sizeof(mbstate_t)); - - if ( wc == L'\n' ) - { - pager_push_newline(pager); - } - else if ( wc == L'\t' ) - { - if ( pager_would_push_char_overflow(pager, ' ') ) - pager_push_newline(pager); - do + size_t new_length = 2 * line->content_length; + if ( new_length == 0 ) + new_length = 128; + char* new_content = (char*) realloc(line->content, new_length); + if ( !new_content ) { - if ( pager_would_push_char_overflow(pager, ' ') ) - break; - pager->current_wcp.wcp_col++; - } while ( pager->current_wcp.wcp_col % 8 != 0 ); - } - else if ( wc == L'\r' ) - { - pager->current_wcp.wcp_col = 0; - } - else - { - if ( pager_would_push_char_overflow(pager, wc) ) - pager_push_newline(pager); - pager->current_wcp.wcp_col++; - } -} - -void pager_push_char(struct pager* pager, wchar_t wc) -{ - if ( wc == L'\b' && - (pager->flag_raw_control_chars || pager->flag_color_sequences) ) - { - dprintf(pager->out_fd, "\b"); - tcgetwincurpos(pager->out_fd, &pager->current_wcp); - } - else if ( wc < 32 && wc != L'\t' && wc != L'\n' ) - { - if ( !pager->input_set_color ) - dprintf(pager->out_fd, "\e[97m"); - pager_push_final_char(pager, L'^'); - pager_push_final_char(pager, L'@' + wc); - if ( !pager->input_set_color ) - dprintf(pager->out_fd, "\e[m"); - } - else - { - pager_push_final_char(pager, wc); - } -} - -void pager_control_sequence_begin(struct pager* pager) -{ - pager->control_sequence_length = 0; -} - -void pager_control_sequence_accept(struct pager* pager) -{ - unsigned char* cs = pager->control_sequence; - size_t cs_length = pager->control_sequence_length; - if ( writeall(pager->out_fd, cs, cs_length) < cs_length ) - { - error(0, errno, "write: "); - pager_end(pager); - exit(1); - } - pager->control_sequence_length = 0; - pager->control_state = CONTROL_STATE_NONE; -} - -void pager_control_sequence_reject(struct pager* pager) -{ - for ( size_t i = 0; i < pager->control_sequence_length; i++ ) - { - wint_t wc = btowc(pager->control_sequence[i]); - if ( wc != WEOF ) - pager_push_char(pager, wc); - } - pager->control_sequence_length = 0; - pager->control_state = CONTROL_STATE_NONE; -} - -void pager_control_sequence_push(struct pager* pager, unsigned char byte) -{ - if ( CONTROL_SEQUENCE_MAX <= pager->control_sequence_length ) - { - if ( !pager->flag_raw_control_chars ) - return pager_control_sequence_reject(pager); - unsigned char* cs = pager->control_sequence; - size_t cs_length = pager->control_sequence_length; - if ( writeall(pager->out_fd, cs, cs_length) < cs_length ) - { - error(0, errno, "write: "); + error(0, errno, "malloc"); pager_end(pager); exit(1); } - pager->control_sequence_length = 0; + line->content = new_content; + line->content_length = new_length; } - pager->control_sequence[pager->control_sequence_length++] = byte; + line->content[line->content_used++] = c; } -void pager_control_sequence_finish(struct pager* pager, unsigned char byte) +static struct line* pager_continue_line(struct pager* pager) { - pager_control_sequence_push(pager, byte); + if ( pager->incoming_line ) + return pager->incoming_line; + if ( pager->lines_used == pager->lines_length ) + { + size_t new_length = 2 * pager->lines_length; + if ( new_length == 0 ) + new_length = 128; + struct line* new_lines = (struct line*) + reallocarray(pager->lines, new_length, sizeof(struct line)); + if ( !new_lines ) + { + error(0, errno, "malloc"); + pager_end(pager); + exit(1); + } + pager->lines = new_lines; + pager->lines_length = new_length; + } + pager->incoming_line = &pager->lines[pager->lines_used++]; + memset(pager->incoming_line, 0, sizeof(*pager->incoming_line)); + pager->incoming_line_width = 0; + return pager->incoming_line; +} + +static void pager_finish_line(struct pager* pager) +{ + struct line* line = pager->incoming_line; + assert(line); + char* final_content = (char*) realloc(line->content, line->content_used); + if ( final_content ) + line->content = final_content; + pager->incoming_line = NULL; + pager->incoming_line_width = 0; +} + +static struct line* pager_next_line(struct pager* pager) +{ + pager_finish_line(pager); + return pager_continue_line(pager); +} + +static void pager_push_wchar(struct pager* pager, wchar_t wc) +{ + bool newline = false; + int width; + struct line* line = pager_continue_line(pager); + + if ( pager->incoming_control_state == CONTROL_STATE_CSI ) + { + if ( wc == '[' ) + pager->incoming_control_state = CONTROL_STATE_COMMAND; + } + else if ( pager->incoming_control_state == CONTROL_STATE_COMMAND ) + { + pager->incoming_control_state = CONTROL_STATE_NONE; + if ( ('0' <= wc && wc <= '9') || + wc == L';' || wc == L':' || wc == L'?' ) + pager->incoming_control_state = CONTROL_STATE_COMMAND; + } + else if ( wc == L'\b' ) + { + if ( pager->incoming_line_width ) + pager->incoming_line_width--; + } + else if ( wc == L'\e' ) + { + pager->incoming_control_state = CONTROL_STATE_CSI; + } + else if ( wc == L'\n' ) + { + newline = true; + } + else if ( wc == L'\t' ) + { + if ( pager->out_fd_winsize.ws_col == pager->incoming_line_width ) + line = pager_next_line(pager); + do + { + if ( pager->out_fd_winsize.ws_col == pager->incoming_line_width ) + break; + pager->incoming_line_width++; + } while ( pager->incoming_line_width % 8 != 0 ); + } + else if ( wc == L'\r' ) + { + pager->incoming_line_width = 0; + } + else if ( wc == 127 ) + { + } + else if ( 0 <= (width = wcwidth(wc)) ) + { + size_t left = pager->out_fd_winsize.ws_col - pager->incoming_line_width; + if ( left < (size_t) width ) + line = pager_next_line(pager); + else + pager->incoming_line_width += width; + } + else + { + // TODO: What can cause this and how to handle it? + } + + char mb[MB_CUR_MAX]; + size_t amount = wcrtomb(mb, wc, &pager->out_ps); + if ( amount != (size_t) -1 ) + { + for ( size_t i = 0; i < amount; i++ ) + pager_line_push_char(pager, line, mb[i]); + } + + if ( newline ) + pager_finish_line(pager); +} + +static bool pager_push_wchar_is_escaped(struct pager* pager, wchar_t wc) +{ + if ( wc == '\b' && + (pager->flag_raw_control_chars || pager->flag_color_sequences) ) + return false; + return wc < 32 && wc != L'\t' && wc != L'\n'; +} + +static void pager_push_wchar_escape(struct pager* pager, wchar_t wc) +{ + if ( pager_push_wchar_is_escaped(pager, wc) ) + { + pager_push_wchar(pager, L'^'); + //pager_push_wchar(pager, L'\b'); + //pager_push_wchar(pager, L'^'); + pager_push_wchar(pager, L'@' + wc); + //pager_push_wchar(pager, L'\b'); + //pager_push_wchar(pager, L'@' + wc); + } + else + { + pager_push_wchar(pager, wc); + } +} + +static void pager_control_sequence_begin(struct pager* pager) +{ + pager->control_sequence_length = 0; +} + +static void pager_control_sequence_accept(struct pager* pager) +{ + for ( size_t i = 0; i < pager->control_sequence_length; i++ ) + pager_push_wchar(pager, pager->control_sequence[i]); + pager->control_sequence_length = 0; + pager->control_state = CONTROL_STATE_NONE; +} + +static void pager_control_sequence_reject(struct pager* pager) +{ + for ( size_t i = 0; i < pager->control_sequence_length; i++ ) + pager_push_wchar_escape(pager, pager->control_sequence[i]); + pager->control_sequence_length = 0; + pager->control_state = CONTROL_STATE_NONE; +} + +static void pager_control_sequence_push(struct pager* pager, wchar_t wc) +{ + if ( pager->flag_raw_control_chars ) + return pager_push_wchar(pager, wc); + if ( CONTROL_SEQUENCE_MAX <= pager->control_sequence_length ) + { + pager_control_sequence_reject(pager); + pager_push_wchar_escape(pager, wc); + return; + } + pager->control_sequence[pager->control_sequence_length++] = wc; +} + +static void pager_control_sequence_finish(struct pager* pager, wchar_t wc) +{ + pager_control_sequence_push(pager, wc); if ( pager->control_state == CONTROL_STATE_NONE ) return; - if ( byte == 'm' ) + if ( wc == 'm' ) { pager->input_set_color = true; return pager_control_sequence_accept(pager); @@ -346,28 +410,24 @@ void pager_control_sequence_finish(struct pager* pager, unsigned char byte) if ( !pager->flag_raw_control_chars ) return pager_control_sequence_reject(pager); pager_control_sequence_accept(pager); - tcgetwincurpos(pager->out_fd, &pager->current_wcp); } -void pager_push_byte(struct pager* pager, unsigned char byte) +static void pager_push_wchar_filter(struct pager* pager, wchar_t wc) { - if ( pager->quiting ) - return; - - if ( byte == '\e' && mbsinit(&pager->in_ps) && + if ( wc == L'\e' && (pager->flag_raw_control_chars || pager->flag_color_sequences) && pager->control_state == CONTROL_STATE_NONE ) { pager_control_sequence_begin(pager); - pager_control_sequence_push(pager, byte); + pager_control_sequence_push(pager, wc); pager->control_state = CONTROL_STATE_CSI; return; } else if ( pager->control_state == CONTROL_STATE_CSI ) { - if ( byte == '[' ) + if ( wc == L'[' ) { - pager_control_sequence_push(pager, byte); + pager_control_sequence_push(pager, wc); pager->control_state = CONTROL_STATE_COMMAND; return; } @@ -375,15 +435,22 @@ void pager_push_byte(struct pager* pager, unsigned char byte) } else if ( pager->control_state == CONTROL_STATE_COMMAND ) { - if ( ('0' <= byte && byte <= '9') || - byte == ';' || byte == ':' || byte == '?' ) + if ( ('0' <= wc && wc <= '9') || + wc == L';' || wc == L':' || wc == L'?' ) { - pager_control_sequence_push(pager, byte); + pager_control_sequence_push(pager, wc); return; } - pager_control_sequence_finish(pager, byte); + pager_control_sequence_finish(pager, wc); return; } + pager_push_wchar_escape(pager, wc); +} + +static void pager_push_byte(struct pager* pager, unsigned char byte) +{ + if ( pager->quiting ) + return; wchar_t wc; size_t amount = mbrtowc(&wc, (const char*) &byte, 1, &pager->in_ps); @@ -394,23 +461,38 @@ void pager_push_byte(struct pager* pager, unsigned char byte) wc = 0xFFFD /* REPLACEMENT CHARACTER */; memset(&pager->in_ps, 0, sizeof(pager->in_ps)); } - pager_push_char(pager, wc); + pager_push_wchar_filter(pager, wc); } -void pager_simple_output_fd(struct pager* pager, int fd, const char* fdpath) +static bool pager_read_fd(struct pager* pager, int fd, const char* fdpath) { unsigned char buffer[4096]; - ssize_t in_amount; - while ( 0 < (in_amount = read(fd, buffer, sizeof(buffer))) ) + ssize_t amount = read(fd, buffer, sizeof(buffer)); + if ( amount < 0 ) { - if ( writeall(pager->out_fd, buffer, in_amount) < (size_t) in_amount ) + error(0, errno, "%s", fdpath); + pager_end(pager); + exit(1); + } + for ( ssize_t i = 0; i < amount; i++ ) + pager_push_byte(pager, buffer[i]); + return amount != 0; +} + +static void pager_simple_fd(struct pager* pager, int fd, const char* fdpath) +{ + unsigned char buffer[4096]; + ssize_t amount = 0; + while ( 0 < (amount = read(fd, buffer, sizeof(buffer))) ) + { + if ( writeall(pager->out_fd, buffer, amount) < (size_t) amount ) { error(0, errno, ""); pager_end(pager); exit(1); } } - if ( in_amount < 0 ) + if ( amount < 0 ) { error(0, errno, "%s", fdpath); pager_end(pager); @@ -418,46 +500,83 @@ void pager_simple_output_fd(struct pager* pager, int fd, const char* fdpath) } } -void pager_push_fd(struct pager* pager, int fd, const char* fdpath) +static bool pager_can_page(struct pager* pager) +{ + if ( pager->lines_used <= pager->current_line ) + return false; + struct line* line = &pager->lines[pager->current_line]; + if ( line->content_used <= pager->current_line_offset ) + return false; + return true; +} + +static void pager_page(struct pager* pager) +{ + struct line* line = &pager->lines[pager->current_line]; + if ( pager->current_line_offset < line->content_used ) + { + const char* buffer = line->content + pager->current_line_offset; + size_t amount = line->content_used - pager->current_line_offset; + if ( writeall(pager->out_fd, buffer, amount) < amount ) + { + error(0, errno, "write: "); + pager_end(pager); + exit(1); + } + pager->current_line_offset = line->content_used; + } + if ( pager->current_line + 1 < pager->lines_used ) + { + if ( pager->allowed_lines != SIZE_MAX ) + pager->allowed_lines--; + pager->current_line++; + pager->current_line_offset = 0; + } +} + +static void pager_push_fd(struct pager* pager, int fd, const char* fdpath) { if ( pager->quiting ) return; - if ( !strcmp(fdpath, "") ) pager->input_prompt_name = ""; else pager->input_prompt_name = fdpath; // TODO: In this case, we should disable echoing and read from the terminal - // anyways. + // anyways. Remember to enable it again. if ( isatty(fd) ) { - error(0, errno, "/dev/tty"); + error(0, 0, "/dev/tty: Is a terminal"); pager_end(pager); exit(1); } - if ( !pager->out_fd_tty ) - return pager_simple_output_fd(pager, fd, fdpath); - unsigned char buffer[4096]; - ssize_t in_amount = 0; - while ( !pager->quiting && - 0 < (in_amount = read(fd, buffer, sizeof(buffer))) ) + if ( !pager->out_fd_is_tty ) + return pager_simple_fd(pager, fd, fdpath); + bool eof = false; + while ( !pager->quiting ) { - for ( ssize_t i = 0; i < in_amount; i++ ) - pager_push_byte(pager, buffer[i]); - } - if ( in_amount < 0 ) - { - error(0, errno, "%s", fdpath); - pager_end(pager); - exit(1); + if ( !pager->skipping_to_end && pager->allowed_lines == 0 ) + { + pager->allowance_ever_exhausted = true; + pager_prompt(pager, false); + continue; + } + if ( !pager->skipping_to_end && pager_can_page(pager) ) + { + pager_page(pager); + continue; + } + if ( eof ) + break; + if ( !pager_read_fd(pager, fd, fdpath) ) + eof = true; } } -void pager_push_path(struct pager* pager, const char* path) +static void pager_push_path(struct pager* pager, const char* path) { if ( pager->quiting ) return; - if ( !strcmp(path, "-") ) return pager_push_fd(pager, 0, ""); int fd = open(path, O_RDONLY); @@ -573,6 +692,30 @@ int main(int argc, char* argv[]) pager_push_path(&pager, argv[i]); } + while ( pager.out_fd_is_tty && + pager.allowance_ever_exhausted && + !pager.quiting ) + { + if ( pager.skipping_to_end ) + { + dprintf(pager.out_fd, "\e[2J\e[H"); + size_t line = 0; + if ( pager.possible_lines <= pager.lines_used ) + line = pager.lines_used - pager.possible_lines; + pager.current_line = line; + pager.current_line_offset = 0; + pager.allowed_lines = pager.possible_lines; + pager.skipping_to_end = false; + } + bool cant_page = !pager_can_page(&pager); + if ( cant_page || pager.allowed_lines == 0 ) + { + pager_prompt(&pager, cant_page); + continue; + } + pager_page(&pager); + } + pager_end(&pager); return 0;