140 lines
4.9 KiB
Diff
140 lines
4.9 KiB
Diff
From b462fdefe74836c22cb02039dca419a512dc6c88 Mon Sep 17 00:00:00 2001
|
|
From: Karel Zak <kzak@redhat.com>
|
|
Date: Wed, 17 Sep 2025 12:58:59 +0200
|
|
Subject: [PATCH] agetty: fix erasure of escape sequences and tab characters
|
|
|
|
When escape sequences (like arrow keys) or tab characters are entered
|
|
at the login prompt, they are properly visualized but only partially
|
|
erasable with backspace. This is because the erase logic assumes each
|
|
stored character corresponds to one visual character, but escape
|
|
sequences display as "^[" (2 chars) and tabs expand to multiple spaces.
|
|
|
|
Track visual character width for each stored byte in a parallel array.
|
|
When erasing, use the stored visual width to properly erase all
|
|
displayed characters for that input byte.
|
|
|
|
Fixes: https://github.com/util-linux/util-linux/issues/3624
|
|
Signed-off-by: Karel Zak <kzak@redhat.com>
|
|
---
|
|
term-utils/agetty.c | 51 ++++++++++++++++++++++++++++++++++++---------
|
|
1 file changed, 41 insertions(+), 10 deletions(-)
|
|
|
|
diff --git a/term-utils/agetty.c b/term-utils/agetty.c
|
|
index 5e564c4f0..c6cb48081 100644
|
|
--- a/term-utils/agetty.c
|
|
+++ b/term-utils/agetty.c
|
|
@@ -2131,20 +2131,30 @@ static void next_speed(struct options *op, struct termios *tp)
|
|
tcsetattr(STDIN_FILENO, TCSANOW, tp);
|
|
}
|
|
|
|
+/* Erase visual characters for one stored character */
|
|
+static void erase_char(int visual_count, struct chardata *cp)
|
|
+{
|
|
+ static const char *const erase[] = { /* backspace-space-backspace */
|
|
+ "\010\040\010", /* space parity */
|
|
+ "\010\040\010", /* odd parity */
|
|
+ "\210\240\210", /* even parity */
|
|
+ "\210\240\210", /* no parity */
|
|
+ };
|
|
+ int i;
|
|
+ for (i = 0; i < visual_count; i++)
|
|
+ write_all(1, erase[cp->parity], 3);
|
|
+}
|
|
+
|
|
/* Get user name, establish parity, speed, erase, kill & eol. */
|
|
static char *get_logname(struct issue *ie, struct options *op, struct termios *tp, struct chardata *cp)
|
|
{
|
|
static char logname[BUFSIZ];
|
|
+ static int visual_widths[BUFSIZ]; /* visual char count for each stored byte */
|
|
char *bp;
|
|
+ int *visual_bp;
|
|
char c; /* input character, full eight bits */
|
|
char ascval; /* low 7 bits of input character */
|
|
int eightbit;
|
|
- static const char *const erase[] = { /* backspace-space-backspace */
|
|
- "\010\040\010", /* space parity */
|
|
- "\010\040\010", /* odd parity */
|
|
- "\210\240\210", /* even parity */
|
|
- "\210\240\210", /* no parity */
|
|
- };
|
|
|
|
/* Initialize kill, erase, parity etc. (also after switching speeds). */
|
|
INIT_CHARDATA(cp);
|
|
@@ -2158,7 +2168,11 @@ static char *get_logname(struct issue *ie, struct options *op, struct termios *t
|
|
tcflush(STDIN_FILENO, TCIFLUSH);
|
|
|
|
eightbit = (op->flags & (F_EIGHTBITS|F_UTF8));
|
|
+
|
|
+ /* Initialize buffer pointers. visual_widths tracks how many visual
|
|
+ * characters each stored byte represents (e.g. ESC = 2 for "^[", tab = 1-8 spaces) */
|
|
bp = logname;
|
|
+ visual_bp = visual_widths;
|
|
*bp = '\0';
|
|
|
|
eval_issue_file(ie, op, tp);
|
|
@@ -2182,6 +2196,7 @@ static char *get_logname(struct issue *ie, struct options *op, struct termios *t
|
|
&& (op->flags & F_NOCLEAR) == 0)
|
|
termio_clear(STDOUT_FILENO);
|
|
bp = logname;
|
|
+ visual_bp = visual_widths;
|
|
*bp = '\0';
|
|
continue;
|
|
}
|
|
@@ -2259,8 +2274,9 @@ static char *get_logname(struct issue *ie, struct options *op, struct termios *t
|
|
cp->erase = ascval; /* set erase character */
|
|
if (bp > logname) {
|
|
if ((tp->c_lflag & ECHO) == 0)
|
|
- write_all(1, erase[cp->parity], 3);
|
|
+ erase_char(*(visual_bp - 1), cp);
|
|
bp--;
|
|
+ visual_bp--;
|
|
}
|
|
break;
|
|
case CTL('U'):
|
|
@@ -2272,8 +2288,9 @@ static char *get_logname(struct issue *ie, struct options *op, struct termios *t
|
|
break;
|
|
while (bp > logname) {
|
|
if ((tp->c_lflag & ECHO) == 0)
|
|
- write_all(1, erase[cp->parity], 3);
|
|
+ erase_char(*(visual_bp - 1), cp);
|
|
bp--;
|
|
+ visual_bp--;
|
|
}
|
|
break;
|
|
case CTL('D'):
|
|
@@ -2283,15 +2300,29 @@ static char *get_logname(struct issue *ie, struct options *op, struct termios *t
|
|
log_err(_("%s: input overrun"), op->tty);
|
|
if ((tp->c_lflag & ECHO) == 0) {
|
|
/* Visualize escape sequence instead of its execution */
|
|
- if (ascval == CTL('['))
|
|
+ if (ascval == CTL('[')) {
|
|
/* Ideally it should be "\xe2\x90\x9b"
|
|
* if (op->flags & (F_UTF8)),
|
|
* but only some fonts contain it */
|
|
write_all(1, "^[", 2);
|
|
- else
|
|
+ *visual_bp = 2; /* ESC shows as ^[ (2 chars) */
|
|
+ } else if (ascval == '\t') {
|
|
+ /* Tab expands to spaces */
|
|
+ int pos = bp - logname;
|
|
+ int spaces = 8 - (pos % 8);
|
|
+ int i;
|
|
+ for (i = 0; i < spaces; i++)
|
|
+ write_all(1, " ", 1);
|
|
+ *visual_bp = spaces;
|
|
+ } else {
|
|
write_all(1, &c, 1); /* echo the character */
|
|
+ *visual_bp = 1; /* normal char shows as 1 */
|
|
+ }
|
|
+ } else {
|
|
+ *visual_bp = 1; /* when echo is on, assume 1 char */
|
|
}
|
|
*bp++ = ascval; /* and store it */
|
|
+ visual_bp++;
|
|
break;
|
|
}
|
|
/* Everything was erased. */
|
|
--
|
|
2.48.1
|
|
|