Add support for pressing 'q' to throw away any further output until the prompt is reached. Signed-off-by: Simon Glass <sjg@chromium.org>
235 lines
7.1 KiB
C
235 lines
7.1 KiB
C
/* 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
|