/* SPDX-License-Identifier: GPL-2.0+ */ /* * Deals with splitting up text output into separate screenfuls * * Copyright 2025 Simon Glass */ #ifndef __PAGER_H #define __PAGER_H #include #include #include #include #define PAGER_BUF_SIZE SZ_4K /* Special return value from pager_next() indicating waiting for user input */ #define PAGER_WAITING ((const char *)1) /* Prompt shown to user when pager reaches page limit */ #define PAGER_PROMPT "\n: Press SPACE to continue" /* String used to blank/clear the pager prompt */ #define PAGER_BLANK "\r \r" /** * enum pager_state: Tracks the state of the pager * * @PAGERST_OK: Normal output is happening * @PAGERST_AT_LIMIT: No more output can be provided; the next call to * pager_next() will return a user prompt * @PAGERST_WAIT_USER: Waiting for the user to press a key * @PAGERST_CLEAR_PROMPT: Clearing the prompt ready for more output * @PAGERST_BYPASS: Pager is being bypassed * @PAGERST_QUIT_SUPPRESS: Output is being suppressed after 'q' keypress */ enum pager_state { PAGERST_OK, PAGERST_AT_LIMIT, PAGERST_WAIT_USER, PAGERST_CLEAR_PROMPT, PAGERST_BYPASS, PAGERST_QUIT_SUPPRESS, }; /** * struct pager - pager state * * The pager uses a buffer @buf to hold text that it is in the process of * sending out. This helps deal with the stdio puts() interface, which does not * permit passing a string length, only a string, which means that strings must * be nul-terminated. The termination is handled automatically by the pager. * * If the text passed to pager_post() is too large for @buf then all the next * will be written at once, without any paging, in the next call to * pager_next(). * * The membuf @mb is only used to feed out text in chunks, with a pager message * (and a keypress wait) inserted between each chunk. * * @line_count: Number of lines output since last pause * @page_len: Sets the height of the page in lines. The maximum lines to display * before pausing is one less than this. Set from 'pager' env variable * @buf: Buffer containing text to eventually be returned * @mb: Circular buffer to manage @buf * @overflow: pointer to overflow text to send nexts * @nulch: pointer to where a nul character was written, NULL if none * @oldch: old character that was at @nulch * @state: current state of the pager state-machine * @test_bypass: true if pager should behave as if in test mode (bypass all) */ struct pager { int line_count; int page_len; struct abuf buf; struct membuf mb; const char *overflow; char *nulch; int oldch; enum pager_state state; bool test_bypass; }; #if CONFIG_IS_ENABLED(CONSOLE_PAGER) /** * pager_post() - Add text to the input buffer for later handling * * If @use_pager the text is added to the pager buffer and fed out a screenful * at a time. This function calls pager_post() after storing the text. * * After calling pager_post(), if it returns anything other than NULL, you must * repeatedly call pager_next() until it returns NULL, otherwise text may be * lost * * If @pag is NULL, this does nothing but return @s * * @pag: Pager to use, may be NULL * @use_pager: Whether or not to use the pager functionality * @s: Text to add * Return: text which should be sent to output, or NULL if there is no more. * If !@use_pager this just returns @s and does not affect the pager state */ const char *pager_post(struct pager *pag, bool use_pager, const char *s); /** * pager_next() - Returns the next screenful of text to show * * If this function returns PAGER_WAITING then the caller must check for user * input and pass in the keypress in the next call to pager_next(). It can * busy-wait for a keypress, if desired, since pager_next() will only ever * return PAGER_WAITING until @ch is non-zero. * * When the pager prompts for user input, pressing SPACE continues to the next * page, while pressing capital 'Q' puts the pager into bypass mode and * disables further paging. Pressing 'q' quits and suppresses all output until * the next command prompt. * * @pag: Pager to use * @use_pager: Whether or not to use the pager functionality * @ch: Key that the user has pressed, or 0 if none * * Return: text which should be sent to output, or PAGER_WAITING if waiting for * the user to press a key, or NULL if there is no more text. * If !@use_pager this just returns NULL and does not affect the pager state */ const char *pager_next(struct pager *pag, bool use_pager, int ch); /** * pager_set_bypass() - put the pager into bypass mode * * This is used when output is not connected to a terminal. Bypass mode stops * the pager from doing anything to interrupt output. * * @pag: Pager to use, may be NULL in which case this function does nothing * @bypass: true to put the pager in bypass mode, false for normal mode * Return: old value of the bypass flag */ bool pager_set_bypass(struct pager *pag, bool bypass); /** * pager_set_test_bypass() - put the pager into test bypass mode * * This is used for tests. Test bypass mode stops the pager from doing * anything to interrupt output, regardless of the current pager state. * * @pag: Pager to use, may be NULL in which case this function does nothing * @bypass: true to put the pager in test bypass mode, false for normal * Return: old value of the test bypass flag */ bool pager_set_test_bypass(struct pager *pag, bool bypass); /** * pager_reset() - reset the line count in the pager * * Sets line_count to zero so that the pager starts afresh with its counting. * * @pag: Pager to update */ void pager_reset(struct pager *pag); /** * pager_clear_quit() - Clear quit suppression mode * * If the pager is in PAGERST_QUIT_SUPPRESS state, this resets it to normal * operation (PAGERST_OK). This is typically called at the start of * cli_readline_into_buffer() to allow new commands to display output normally. * * @pag: Pager to update, may be NULL in which case this function does nothing */ void pager_clear_quit(struct pager *pag); /** * pager_uninit() - Uninit the pager * * Frees all memory and also @pag * * @pag: Pager to uninit */ void pager_uninit(struct pager *pag); #else static inline const char *pager_post(struct pager *pag, bool use_pager, const char *s) { return s; } static inline const char *pager_next(struct pager *pag, bool use_pager, int ch) { return NULL; } static inline bool pager_set_bypass(struct pager *pag, bool bypass) { return true; } static inline bool pager_set_test_bypass(struct pager *pag, bool bypass) { return true; } static inline void pager_clear_quit(struct pager *pag) { } static inline void pager_reset(struct pager *pag) { } #endif /** * pager_set_page_len() - Set the page length of a pager * * @pag: Pager to use * @page_len: Page length to set */ void pager_set_page_len(struct pager *pag, int page_len); /** * pager_init() - Set up a new pager * * @pagp: Returns allocaed pager, on success * @page_len: Number of lines per page * @buf_size: Buffer size to use in bytes, this is the maximum amount of output * that can be paged * Return: 0 if OK, -ENOMEM if out of memory */ int pager_init(struct pager **pagp, int page_len, int buf_size); #endif