@@ -370,6 +370,27 @@ sudo_askpass(const char *askpass, const char *prompt)
370370
371371extern 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+
373394static char *
374395getln (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