[openwrt/openwrt] ucode-mod-uline: add support for querying window size from terminal if ioctl fails

LEDE Commits lede-commits at lists.infradead.org
Fri Feb 28 08:36:17 PST 2025


nbd pushed a commit to openwrt/openwrt.git, branch main:
https://git.openwrt.org/8835ecf29b0610dc00bdd6e15578ec52699184c2

commit 8835ecf29b0610dc00bdd6e15578ec52699184c2
Author: Felix Fietkau <nbd at nbd.name>
AuthorDate: Thu Feb 27 11:21:22 2025 +0100

    ucode-mod-uline: add support for querying window size from terminal if ioctl fails
    
    This is useful for running the cli on a serial console
    
    Signed-off-by: Felix Fietkau <nbd at nbd.name>
---
 package/utils/ucode-mod-uline/src/private.h | 14 +++++++-
 package/utils/ucode-mod-uline/src/uline.c   | 43 +++++++++++++++++-------
 package/utils/ucode-mod-uline/src/uline.h   |  3 +-
 package/utils/ucode-mod-uline/src/vt100.c   | 51 ++++++++++++++++++-----------
 4 files changed, 78 insertions(+), 33 deletions(-)

diff --git a/package/utils/ucode-mod-uline/src/private.h b/package/utils/ucode-mod-uline/src/private.h
index fa38d06737..e53ec22e03 100644
--- a/package/utils/ucode-mod-uline/src/private.h
+++ b/package/utils/ucode-mod-uline/src/private.h
@@ -52,6 +52,7 @@ enum vt100_escape {
 	VT100_CURSOR_WORD_LEFT,
 	VT100_CURSOR_RIGHT,
 	VT100_CURSOR_WORD_RIGHT,
+	VT100_CURSOR_POS,
 	VT100_HOME,
 	VT100_END,
 	VT100_INSERT,
@@ -63,7 +64,7 @@ enum vt100_escape {
 };
 
 ssize_t utf8_nsyms(const char *str, size_t len);
-enum vt100_escape vt100_esc_decode(const char *str);
+enum vt100_escape vt100_esc_decode(const char *str, uint32_t *data);
 
 // helpers:
 void __vt100_csi_num(FILE *out, int num, char code);
@@ -191,4 +192,15 @@ static inline void vt100_ding(FILE *out)
 	fflush(out);
 }
 
+static inline void vt100_request_window_size(FILE *out)
+{
+	fputs(
+	      "\e7"             /* save cursor position */
+	      "\e[r"            /* reset margins */
+	      "\e[999;999H"     /* move cursor to bottom right */
+	      "\e[6n"           /* report cursor position */
+	      "\e8",            /* restore cursor position */
+	      out);
+}
+
 #endif
diff --git a/package/utils/ucode-mod-uline/src/uline.c b/package/utils/ucode-mod-uline/src/uline.c
index 26cea6fa24..17d1d69bc2 100644
--- a/package/utils/ucode-mod-uline/src/uline.c
+++ b/package/utils/ucode-mod-uline/src/uline.c
@@ -101,13 +101,16 @@ update_window_size(struct uline_state *s, bool init)
 #ifdef TIOCGWINSZ
 	struct winsize ws = {};
 
-	if (!ioctl(fileno(s->output), TIOCGWINSZ, &ws)) {
-		if (ws.ws_col)
-			cols = ws.ws_col;
-		if (ws.ws_row)
-			rows = ws.ws_row;
-	}
+	if (s->ioctl_winsize &&
+	    !ioctl(fileno(s->output), TIOCGWINSZ, &ws) &&
+	    ws.ws_col && ws.ws_row) {
+		cols = ws.ws_col;
+		rows = ws.ws_row;
+	} else
 #endif
+	{
+		s->ioctl_winsize = false;
+	}
 
 	s->sigwinch_count = sigwinch_count;
 	if (s->cols == cols && s->rows == rows)
@@ -534,7 +537,7 @@ move_word_right(struct uline_state *s, struct linebuf *line)
 }
 
 static bool
-process_esc(struct uline_state *s, enum vt100_escape esc)
+process_esc(struct uline_state *s, enum vt100_escape esc, uint32_t data)
 {
 	struct linebuf *line = &s->line;
 
@@ -552,6 +555,15 @@ process_esc(struct uline_state *s, enum vt100_escape esc)
 		return move_right(s, line);
 	case VT100_CURSOR_WORD_RIGHT:
 		return move_word_right(s, line);
+	case VT100_CURSOR_POS:
+		if (s->rows == (data & 0xffff) &&
+		    s->cols == data >> 16)
+			return false;
+		s->rows = data & 0xffff;
+		s->cols = data >> 16;
+	    s->full_update = true;
+		s->cb->event(s, EDITLINE_EV_WINDOW_CHANGED);
+		return true;
 	case VT100_HOME:
 		line->pos = 0;
 		return true;
@@ -682,9 +694,9 @@ process_ctrl(struct uline_state *s, char c)
 		linebuf_reset(line);
 		return true;
 	case KEY_SOH:
-		return process_esc(s, VT100_HOME);
+		return process_esc(s, VT100_HOME, 0);
 	case KEY_ENQ:
-		return process_esc(s, VT100_END);
+		return process_esc(s, VT100_END, 0);
 	case KEY_VT:
 		// TODO: kill
 		return false;
@@ -718,18 +730,19 @@ static void
 process_char(struct uline_state *s, char c)
 {
 	enum vt100_escape esc;
+	uint32_t data = 0;
 
 	check_key_repeat(s, c);
 	if (s->esc_idx >= 0) {
 		s->esc_seq[s->esc_idx++] = c;
 		s->esc_seq[s->esc_idx] = 0;
-		esc = vt100_esc_decode(s->esc_seq);
+		esc = vt100_esc_decode(s->esc_seq, &data);
 		if (esc == VT100_INCOMPLETE &&
 		    s->esc_idx < (int)sizeof(s->esc_seq) - 1)
 			return;
 
 		s->esc_idx = -1;
-		if (!process_esc(s, esc))
+		if (!process_esc(s, esc, data))
 			return;
 	} else if (s->cb->key_input &&
 	           !check_utf8(s, (unsigned char )c) &&
@@ -901,7 +914,7 @@ void uline_init(struct uline_state *s, const struct uline_cb *cb,
 	s->utf8 = utf8;
 	s->input = in_fd;
 	s->output = out_stream;
-	update_window_size(s, true);
+	s->ioctl_winsize = true;
 	reset_input_state(s);
 
 #ifdef USE_SYSTEM_WCHAR
@@ -916,6 +929,12 @@ void uline_init(struct uline_state *s, const struct uline_cb *cb,
 		s->has_termios = true;
 		termios_set_native_mode(s);
 	}
+
+	update_window_size(s, true);
+	if (!s->ioctl_winsize) {
+		vt100_request_window_size(s->output);
+		fflush(s->output);
+	}
 }
 
 void uline_free(struct uline_state *s)
diff --git a/package/utils/ucode-mod-uline/src/uline.h b/package/utils/ucode-mod-uline/src/uline.h
index 6f7b75542f..514675e799 100644
--- a/package/utils/ucode-mod-uline/src/uline.h
+++ b/package/utils/ucode-mod-uline/src/uline.h
@@ -82,12 +82,13 @@ struct uline_state {
 	unsigned int rows, cols;
 	struct pos cursor_pos;
 	struct pos end_pos;
+	bool ioctl_winsize;
 	bool full_update;
 	bool stop;
 
 	bool utf8;
 
-	char esc_seq[8];
+	char esc_seq[32];
 	int8_t esc_idx;
 	uint8_t utf8_cont;
 };
diff --git a/package/utils/ucode-mod-uline/src/vt100.c b/package/utils/ucode-mod-uline/src/vt100.c
index b13e6a6722..f81b11d3ad 100644
--- a/package/utils/ucode-mod-uline/src/vt100.c
+++ b/package/utils/ucode-mod-uline/src/vt100.c
@@ -7,10 +7,10 @@
 #include "uline.h"
 #include "private.h"
 
-enum vt100_escape vt100_esc_decode(const char *str)
+enum vt100_escape vt100_esc_decode(const char *str, uint32_t *data)
 {
-	unsigned long code;
-	size_t idx;
+	unsigned long code, code2;
+	char *err;
 
 	switch (*(str++)) {
 	case 0:
@@ -45,23 +45,36 @@ enum vt100_escape vt100_esc_decode(const char *str)
 		case '0' ... '4':
 		case '6' ... '9':
 			str--;
-			idx = strspn(str, "0123456789");
-			if (!str[idx])
+			code = strtoul(str, &err, 10);
+			switch (*err) {
+			case 0:
 				return VT100_INCOMPLETE;
-			if (str[idx] != '~')
-				return VT100_UNKNOWN;
-			code = strtoul(str, NULL, 10);
-			switch (code) {
-			case 1:
-				return VT100_HOME;
-			case 3:
-				return VT100_DELETE;
-			case 4:
-				return VT100_END;
-			case 200:
-			case 201:
-				// paste start/end
-				return VT100_IGNORE;
+			case '~':
+				switch (code) {
+				case 1:
+					return VT100_HOME;
+				case 3:
+					return VT100_DELETE;
+				case 4:
+					return VT100_END;
+				case 200:
+				case 201:
+					// paste start/end
+					return VT100_IGNORE;
+				default:
+					return VT100_UNKNOWN;
+				}
+			case ';':
+				code2 = strtoul(err + 1, &err, 10);
+				switch (*err) {
+				case 0:
+					return VT100_INCOMPLETE;
+				case 'R':
+					*data = (code2 << 16) | (code & 0xffff);
+					return VT100_CURSOR_POS;
+				default:
+					return VT100_UNKNOWN;
+				}
 			default:
 				return VT100_UNKNOWN;
 			}




More information about the lede-commits mailing list