Skip to content

Commit 70c1ee4

Browse files
squellmillert
authored andcommitted
backspace correctly over multibyte character sequences in pwfeedback mode
1 parent f6ee7dd commit 70c1ee4

1 file changed

Lines changed: 29 additions & 5 deletions

File tree

src/tgetpass.c

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,27 @@ sudo_askpass(const char *askpass, const char *prompt)
370370

371371
extern int sudo_term_eof, sudo_term_erase, sudo_term_kill;
372372

373+
static ssize_t
374+
last_chunk_len(const char *buf, ssize_t len)
375+
{
376+
ssize_t pos = len;
377+
378+
/* Determine the length of what could be a (partial) UTF8 sequence */
379+
while (pos > 0 && len - pos < 4) {
380+
char last = buf[--pos];
381+
switch (last & 0xC0) {
382+
case 0xC0:
383+
return len - pos;
384+
case 0x80:
385+
continue;
386+
default:
387+
return 1;
388+
}
389+
}
390+
391+
return len != 0;
392+
}
393+
373394
static char *
374395
getln(int fd, char *buf, size_t bufsiz, bool feedback,
375396
enum tgetpass_errval *errval)
@@ -401,28 +422,31 @@ getln(int fd, char *buf, size_t bufsiz, bool feedback,
401422
while (cp > buf) {
402423
if (write(fd, "\b \b", 3) != 3)
403424
break;
404-
cp--;
425+
cp -= last_chunk_len(buf, cp - buf);
405426
}
406427
cp = buf;
407428
continue;
408429
} else if (c == sudo_term_erase) {
409430
if (cp > buf) {
410431
ignore_result(write(fd, "\b \b", 3));
411-
cp--;
432+
cp -= last_chunk_len(buf, cp - buf);
412433
}
413434
continue;
414435
}
415-
ignore_result(write(fd, "*", 1));
436+
*cp++ = c;
437+
if (last_chunk_len(buf, cp - buf) == 1)
438+
ignore_result(write(fd, "*", 1));
439+
} else {
440+
*cp++ = c;
416441
}
417-
*cp++ = c;
418442
}
419443
*cp = '\0';
420444
if (feedback) {
421445
/* erase stars */
422446
while (cp > buf) {
423447
if (write(fd, "\b \b", 3) != 3)
424448
break;
425-
cp--;
449+
cp -= last_chunk_len(buf, cp - buf);
426450
}
427451
}
428452

0 commit comments

Comments
 (0)