From 6ab5f987847c32053d9b53af95890fe44f5d1314 Mon Sep 17 00:00:00 2001 From: bones_was_here Date: Mon, 12 Aug 2024 03:32:44 +1000 Subject: [PATCH] con: support tab completion when both directories and files match Fixes truncation warnings (caused by using strlcpy to copy an unterminated substring, technically invoking UB with old strlcpy). Closes https://gitlab.com/xonotic/darkplaces/-/issues/421 Signed-off-by: bones_was_here --- console.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/console.c b/console.c index f46eddb5..2da2ddc5 100644 --- a/console.c +++ b/console.c @@ -2939,9 +2939,6 @@ int Con_CompleteCommandLine(cmd_state_t *cmd, qbool is_console) // set con_completion_playermodel "models/player/*.zym models/player/*.md3 models/player/*.psk models/player/*.dpm" // set con_completion_playdemo "*.dem" // set con_completion_play "*.wav *.ogg" - // - // TODO somehow add support for directories; these shall complete - // to their name + an appended slash. stringlistinit(&resultbuf); stringlistinit(&dirbuf); @@ -2957,7 +2954,7 @@ int Con_CompleteCommandLine(cmd_state_t *cmd, qbool is_console) const char *slash = strrchr(s, '/'); if(slash) { - dp_strlcpy(t, s, min(sizeof(t), (unsigned int)(slash - s + 2))); // + 2, because I want to include the slash + dp_ustr2stp(t, sizeof(t), s, slash - s + 1); // + 1, because I want to include the slash dp_strlcat(t, com_token, sizeof(t)); search = FS_Search(t, true, true, NULL); } @@ -2980,7 +2977,7 @@ int Con_CompleteCommandLine(cmd_state_t *cmd, qbool is_console) const char *slash = strrchr(s, '/'); if(slash) { - dp_strlcpy(t, s, min(sizeof(t), (unsigned int)(slash - s + 2))); // + 2, because I want to include the slash + dp_ustr2stp(t, sizeof(t), s, slash - s + 1); // + 1, because I want to include the slash dp_strlcat(t, "*", sizeof(t)); search = FS_Search(t, true, true, NULL); } @@ -3000,6 +2997,7 @@ int Con_CompleteCommandLine(cmd_state_t *cmd, qbool is_console) { const char *p, *q; unsigned int matchchars; + if(resultbuf.numstrings == 0 && dirbuf.numstrings == 1) { dpsnprintf(t, sizeof(t), "%s/", dirbuf.strings[0]); @@ -3021,26 +3019,33 @@ int Con_CompleteCommandLine(cmd_state_t *cmd, qbool is_console) { Con_Printf("%s\n", resultbuf.strings[i]); } - matchchars = sizeof(t) - 1; - if(resultbuf.numstrings > 0) + + if(resultbuf.numstrings > 0) // matching file { p = resultbuf.strings[0]; q = resultbuf.strings[resultbuf.numstrings - 1]; + if (dirbuf.numstrings > 0) // and matching directory + { + const char *r = dirbuf.strings[0]; + for(; *p && *p == *q && *p == *r; ++p, ++q, ++r); + } + else for(; *p && *p == *q; ++p, ++q); matchchars = (unsigned int)(p - resultbuf.strings[0]); + dp_ustr2stp(t, sizeof(t), resultbuf.strings[0], matchchars); } - if(dirbuf.numstrings > 0) + else // matching directory only { p = dirbuf.strings[0]; q = dirbuf.strings[dirbuf.numstrings - 1]; for(; *p && *p == *q; ++p, ++q); - matchchars = min(matchchars, (unsigned int)(p - dirbuf.strings[0])); + matchchars = (unsigned int)(p - dirbuf.strings[0]); + dp_ustr2stp(t, sizeof(t), dirbuf.strings[0], matchchars); } // now p points to the first non-equal character, or to the end // of resultbuf.strings[0]. We want to append the characters // from resultbuf.strings[0] to (not including) p as these are // the unique prefix - dp_strlcpy(t, (resultbuf.numstrings > 0 ? resultbuf : dirbuf).strings[0], min(matchchars + 1, sizeof(t))); } // first move the cursor -- 2.39.2