From 9545e120545f07de4315fb6282d7bd14d666240b Mon Sep 17 00:00:00 2001 From: raiden00pl Date: Sat, 22 Aug 2026 07:42:22 +0200 Subject: [PATCH] system/readline: add caller-selectable control handling Let CPython handle Ctrl-D and signals without changing existing NSH behavior. Signed-off-by: raiden00pl Assisted-by: Claude Code --- include/system/readline.h | 18 +++++++++++++++++ system/readline/readline.h | 2 +- system/readline/readline_common.c | 15 +++++++++++--- system/readline/readline_fd.c | 33 +++++++++++++++++++++++++++---- 4 files changed, 60 insertions(+), 8 deletions(-) diff --git a/include/system/readline.h b/include/system/readline.h index 8f40c92c1..2deb2a976 100644 --- a/include/system/readline.h +++ b/include/system/readline.h @@ -42,6 +42,11 @@ # undef CONFIG_READLINE_TABCOMPLETION #endif +/* readline_fd_ex() options */ + +#define READLINE_CTRL_D_EOF (1 << 0) +#define READLINE_RETURN_ON_EINTR (1 << 1) + /* Make sure that the are valid values for all tab-completion settings */ #ifdef CONFIG_READLINE_TABCOMPLETION @@ -176,6 +181,19 @@ FAR const struct extmatch_vtable_s * ssize_t readline_fd(FAR char *buf, int buflen, int infd, int outfd); +/**************************************************************************** + * Name: readline_fd_ex + * + * readline_fd_ex() extends readline_fd() with caller-selected options. + * READLINE_CTRL_D_EOF makes Ctrl-D return EOF when the line is empty. + * READLINE_RETURN_ON_EINTR makes an interrupted input read return -EINTR + * instead of being retried. + * + ****************************************************************************/ + +ssize_t readline_fd_ex(FAR char *buf, int buflen, int infd, int outfd, + unsigned int options); + /**************************************************************************** * Name: readline_stream * diff --git a/system/readline/readline.h b/system/readline/readline.h index b4b5927f8..c255a6580 100644 --- a/system/readline/readline.h +++ b/system/readline/readline.h @@ -84,6 +84,6 @@ struct rl_common_s ****************************************************************************/ ssize_t readline_common(FAR struct rl_common_s *vtbl, - FAR char *buf, int buflen); + FAR char *buf, int buflen, unsigned int options); #endif /* __APPS_SYSTEM_READLINE_READLINE_H */ diff --git a/system/readline/readline_common.c b/system/readline/readline_common.c index 872fcf768..742759ebb 100644 --- a/system/readline/readline_common.c +++ b/system/readline/readline_common.c @@ -49,12 +49,13 @@ # define RL_CMDHIST_LINELEN CONFIG_READLINE_CMD_HISTORY_LINELEN #endif +#define CTRL_D 4 /* ^D - EOF or delete at cursor */ + #ifdef CONFIG_READLINE_EDIT_EMACS -/* Emacs-style control key codes */ +/* Additional Emacs-style control key codes */ # define CTRL_A 1 /* ^A - Home */ # define CTRL_B 2 /* ^B - Left */ -# define CTRL_D 4 /* ^D - Delete at cursor */ # define CTRL_E 5 /* ^E - End */ # define CTRL_F 6 /* ^F - Right */ # define CTRL_K 11 /* ^K - Kill to end of line */ @@ -849,7 +850,7 @@ FAR const struct extmatch_vtable_s * ****************************************************************************/ ssize_t readline_common(FAR struct rl_common_s *vtbl, FAR char *buf, - int buflen) + int buflen, unsigned int options) { int escape; int nch; @@ -1440,6 +1441,14 @@ ssize_t readline_common(FAR struct rl_common_s *vtbl, FAR char *buf, return submit_line(buf, nch); } + /* Some callers use the conventional empty-line Ctrl-D as EOF. */ + + else if (ch == CTRL_D && (options & READLINE_CTRL_D_EOF) != 0 && + nch == 0) + { + return EOF; + } + /* Emacs-style control keys */ #ifdef CONFIG_READLINE_EDIT_EMACS diff --git a/system/readline/readline_fd.c b/system/readline/readline_fd.c index 0490af266..cc9d5cb18 100644 --- a/system/readline/readline_fd.c +++ b/system/readline/readline_fd.c @@ -43,9 +43,11 @@ struct readline_s { struct rl_common_s vtbl; - int infd; + unsigned int options; + int infd; + int errcode; #ifdef CONFIG_READLINE_ECHO - int outfd; + int outfd; #endif }; @@ -93,7 +95,13 @@ static int readline_getc(FAR struct rl_common_s *vtbl) */ int errcode = errno; - if (errcode != EINTR) + if (errcode == EINTR && + (priv->options & READLINE_RETURN_ON_EINTR) != 0) + { + priv->errcode = errcode; + return EOF; + } + else if (errcode != EINTR) { /* Return EOF on any errors that we cannot handle */ @@ -219,6 +227,16 @@ static void readline_write(FAR struct rl_common_s *vtbl, ****************************************************************************/ ssize_t readline_fd(FAR char *buf, int buflen, int infd, int outfd) +{ + return readline_fd_ex(buf, buflen, infd, outfd, 0); +} + +/**************************************************************************** + * Name: readline_fd_ex + ****************************************************************************/ + +ssize_t readline_fd_ex(FAR char *buf, int buflen, int infd, int outfd, + unsigned int options) { UNUSED(outfd); @@ -244,6 +262,8 @@ ssize_t readline_fd(FAR char *buf, int buflen, int infd, int outfd) vtbl.vtbl.rl_getc = readline_getc; vtbl.infd = infd; + vtbl.options = options; + vtbl.errcode = 0; #ifdef CONFIG_READLINE_ECHO vtbl.vtbl.rl_putc = readline_putc; @@ -253,12 +273,17 @@ ssize_t readline_fd(FAR char *buf, int buflen, int infd, int outfd) /* The let the common readline logic do the work */ - ret = readline_common(&vtbl.vtbl, buf, buflen); + ret = readline_common(&vtbl.vtbl, buf, buflen, options); if (restore_termios) { tcsetattr(infd, TCSANOW, &cfg); } + if (vtbl.errcode != 0) + { + ret = -vtbl.errcode; + } + return ret; }