Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 | /* SPDX-License-Identifier: GPL-2.0+ */ /* * Deals with splitting up text output into separate screenfuls * * Copyright 2025 Simon Glass <sjg@chromium.org> */ #ifndef __PAGER_H #define __PAGER_H #include <stdbool.h> #include <abuf.h> #include <membuf.h> #include <linux/sizes.h> #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 |