@@ -361,11 +361,11 @@ int err_printf(bool serial_only, const char *fmt, ...)
return ret;
}
-static void console_puts(int file, bool use_pager, const char *s)
+static void console_putsn(int file, bool use_pager, const char *s, int len)
{
int key = 0;
- for (s = pager_post(gd_pager(), use_pager, s); s;
+ for (s = pager_postn(gd_pager(), use_pager, s, len); s;
s = pager_next(gd_pager(), use_pager, key)) {
struct stdio_dev *dev;
int i;
@@ -384,13 +384,8 @@ static void console_puts(int file, bool use_pager, const char *s)
static void console_putsn_pager(int file, const char *s, int len)
{
- if (IS_ENABLED(CONFIG_CONSOLE_PAGER) && gd_pager()) {
- /*
- * Pager only works with nul-terminated strings, so output
- * character by character for length-based strings
- */
- while (len--)
- console_putc_pager(file, *s++);
+ if (IS_ENABLED(CONFIG_CONSOLE_PAGER) && pager_active(gd_pager())) {
+ console_putsn(file, true, s, len);
} else {
struct stdio_dev *dev;
int i;
@@ -409,11 +404,10 @@ static void console_putsn_pager(int file, const char *s, int len)
static void console_puts_pager(int file, const char *s)
{
- if (IS_ENABLED(CONFIG_CONSOLE_PAGER) && gd_pager()) {
- console_puts(file, true, s);
- } else {
+ if (IS_ENABLED(CONFIG_CONSOLE_PAGER) && gd_pager())
+ console_putsn(file, true, s, strlen(s));
+ else
console_putsn_pager(file, s, strlen(s));
- }
}
#ifdef CONFIG_CONSOLE_FLUSH_SUPPORT
@@ -15,10 +15,11 @@
DECLARE_GLOBAL_DATA_PTR;
-const char *pager_post(struct pager *pag, bool use_pager, const char *s)
+const char *pager_postn(struct pager *pag, bool use_pager, const char *s,
+ int len)
{
struct membuf old;
- int ret, len;
+ int ret;
if (!pag || !use_pager || pag->test_bypass ||
pag->state == PAGERST_BYPASS)
@@ -27,7 +28,6 @@ const char *pager_post(struct pager *pag, bool use_pager, const char *s)
if (pag->state == PAGERST_QUIT_SUPPRESS)
return NULL;
- len = strlen(s);
if (!len)
return NULL;
@@ -42,8 +42,13 @@ const char *pager_post(struct pager *pag, bool use_pager, const char *s)
* can eject the overflow text.
*
* The buffer is presumably empty, since callers are not allowed
- * to call pager_post() unless all the output from the previous
+ * to call pager_postn() unless all the output from the previous
* call was provided via pager_next().
+ *
+ * Note: the overflow path returns @s directly via
+ * pager_next(), so @s must be nul-terminated. In practice
+ * this only triggers when len > buf_size, and typical
+ * console strings are well within the 4K default buffer.
*/
pag->overflow = s;
pag->mb = old;
@@ -12,6 +12,7 @@
#include <abuf.h>
#include <membuf.h>
#include <linux/sizes.h>
+#include <linux/string.h>
#define PAGER_BUF_SIZE SZ_4K
@@ -52,7 +53,7 @@ enum pager_state {
* 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
+ * If the text passed to pager_postn() is too large for @buf then all the next
* will be written at once, without any paging, in the next call to
* pager_next().
*
@@ -85,12 +86,12 @@ struct pager {
#if CONFIG_IS_ENABLED(CONSOLE_PAGER)
/**
- * pager_post() - Add text to the input buffer for later handling
+ * pager_postn() - 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.
+ * at a time. This function calls pager_postn() after storing the text.
*
- * After calling pager_post(), if it returns anything other than NULL, you must
+ * After calling pager_postn(), if it returns anything other than NULL, you must
* repeatedly call pager_next() until it returns NULL, otherwise text may be
* lost
*
@@ -99,10 +100,28 @@ struct pager {
* @pag: Pager to use, may be NULL
* @use_pager: Whether or not to use the pager functionality
* @s: Text to add
+ * @len: Length of @s in bytes
* 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);
+const char *pager_postn(struct pager *pag, bool use_pager, const char *s,
+ int len);
+
+/**
+ * pager_post() - Add a nul-terminated string to the pager input buffer
+ *
+ * Convenience wrapper around pager_postn() for nul-terminated strings.
+ *
+ * @pag: Pager to use, may be NULL
+ * @use_pager: Whether or not to use the pager functionality
+ * @s: Nul-terminated text to add
+ * Return: text which should be sent to output, or NULL if there is no more
+ */
+static inline const char *pager_post(struct pager *pag, bool use_pager,
+ const char *s)
+{
+ return pager_postn(pag, use_pager, s, strlen(s));
+}
/**
* pager_next() - Returns the next screenful of text to show
@@ -127,6 +146,21 @@ const char *pager_post(struct pager *pag, bool use_pager, const char *s);
*/
const char *pager_next(struct pager *pag, bool use_pager, int ch);
+/**
+ * pager_active() - check if pager needs to process output
+ *
+ * Returns true only when the pager is genuinely active and needs to
+ * process output (not bypassed or in test bypass mode).
+ *
+ * @pag: Pager to check, may be NULL
+ * Return: true if the pager is active
+ */
+static inline bool pager_active(struct pager *pag)
+{
+ return pag && !pag->test_bypass &&
+ pag->state != PAGERST_BYPASS;
+}
+
/**
* pager_set_bypass() - put the pager into bypass mode
*
@@ -181,6 +215,12 @@ void pager_clear_quit(struct pager *pag);
void pager_uninit(struct pager *pag);
#else
+static inline const char *pager_postn(struct pager *pag, bool use_pager,
+ const char *s, int len)
+{
+ return s;
+}
+
static inline const char *pager_post(struct pager *pag, bool use_pager,
const char *s)
{
@@ -192,6 +232,11 @@ static inline const char *pager_next(struct pager *pag, bool use_pager, int ch)
return NULL;
}
+static inline bool pager_active(struct pager *pag)
+{
+ return false;
+}
+
static inline bool pager_set_bypass(struct pager *pag, bool bypass)
{
return true;
@@ -665,3 +665,22 @@ static int pager_test_quit_keypress(struct unit_test_state *uts)
return 0;
}
COMMON_TEST(pager_test_quit_keypress, 0);
+
+/* Test pager with non-nul-terminated string using explicit length */
+static int pager_test_non_nul_terminated(struct unit_test_state *uts)
+{
+ struct pager *pag;
+ /* "Hello" followed by other data - not nul-terminated at offset 5 */
+ static const char data[] = "HelloWorld";
+
+ ut_assertok(pager_init(&pag, 20, 1024));
+
+ /* Post only the first 5 bytes */
+ ut_asserteq_str("Hello", pager_postn(pag, true, data, 5));
+ ut_assertnull(pager_next(pag, true, 0));
+
+ pager_uninit(pag);
+
+ return 0;
+}
+COMMON_TEST(pager_test_non_nul_terminated, 0);