-/* $OpenBSD: sftp.c,v 1.245 2025/10/02 04:23:11 djm Exp $ */
+/* $OpenBSD: sftp.c,v 1.246 2025/10/08 21:48:40 djm Exp $ */
/*
* Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
*
static char *
complete_ambiguous(const char *word, char **list, size_t count)
{
+ size_t i, j, matchlen;
+ char *tmp;
+ int len;
+
if (word == NULL)
return NULL;
- if (count > 0) {
- u_int y, matchlen = strlen(list[0]);
-
- /* Find length of common stem */
- for (y = 1; list[y]; y++) {
- u_int x;
-
- for (x = 0; x < matchlen; x++)
- if (list[0][x] != list[y][x])
- break;
-
- matchlen = x;
- }
+ if (count == 0)
+ return xstrdup(word); /* no options to complete */
- if (matchlen > strlen(word)) {
- char *tmp = xstrdup(list[0]);
+ /* Find length of common stem across list */
+ matchlen = strlen(list[0]);
+ for (i = 1; i < count && list[i] != NULL; i++) {
+ for (j = 0; j < matchlen; j++)
+ if (list[0][j] != list[i][j])
+ break;
+ matchlen = j;
+ }
- tmp[matchlen] = '\0';
- return tmp;
- }
+ /*
+ * Now check that the common stem doesn't finish in the middle of
+ * a multibyte character.
+ */
+ mblen(NULL, 0);
+ for (i = 0; i < matchlen;) {
+ len = mblen(list[0] + i, matchlen - i);
+ if (len <= 0 || i + (size_t)len > matchlen)
+ break;
+ i += (size_t)len;
+ }
+ /* If so, truncate */
+ if (i < matchlen)
+ matchlen = i;
+
+ if (matchlen > strlen(word)) {
+ tmp = xstrdup(list[0]);
+ tmp[matchlen] = '\0';
+ return tmp;
}
return xstrdup(word);
tmp2 = tmp + filelen - cesc;
len = strlen(tmp2);
/* quote argument on way out */
+ mblen(NULL, 0);
for (i = 0; i < len; i += clen) {
if ((clen = mblen(tmp2 + i, len - i)) < 0 ||
(size_t)clen > sizeof(ins) - 2)