]> git.rm.cloudns.org Git - xonotic/darkplaces.git/commitdiff
con: support spaces in arguments and VFS tab completion via escapes
authorbones_was_here <bones_was_here@xonotic.au>
Tue, 13 Aug 2024 04:28:34 +0000 (14:28 +1000)
committerbones_was_here <bones_was_here@xonotic.au>
Thu, 12 Sep 2024 15:44:43 +0000 (01:44 +1000)
This is consistent with how it's done in bash and other shells.

Renames `completednamebufferlength` and a `t` for clarity.

Changes GetMapList() to return the number of bytes written (and be
slightly faster), though it wasn't needed in the final version of this
patch.

Signed-off-by: bones_was_here <bones_was_here@xonotic.au>
common.c
console.c
console.h
qdefs.h

index 0870a4547d26035cf25edf054282728c28cd8b1c..67c2a4fbfdd9a91aaac0b15256eb7077c3ac6a99 100644 (file)
--- a/common.c
+++ b/common.c
@@ -831,8 +831,9 @@ qbool COM_ParseToken_Console(const char **datapointer)
        }
 
 // skip whitespace
+// unless it's an escaped space
 skipwhite:
-       for (;ISWHITESPACE(*data);data++)
+       for (;ISWHITESPACE_ANDNOTESCAPED(*data, data > *datapointer ? data[-1] : *data);data++)
        {
                if (*data == 0)
                {
@@ -869,8 +870,9 @@ skipwhite:
        else
        {
                // regular word
-               for (;!ISWHITESPACE(*data);data++)
-                       if (len < (int)sizeof(com_token) - 1)
+               for (;!ISWHITESPACE_ANDNOTESCAPED(*data, data > *datapointer ? data[-1] : *data); data++)
+                       if (len < (int)sizeof(com_token) - 1
+                       && !(*data == '\\' && data[1] == ' '))
                                com_token[len++] = *data;
                com_token[len] = '\0';
                com_token_len = len;
index 6e6d1b2ef7a58d71d5d11f85fe15c24b28eb2e35..70f8fa201123d6e2f8400cf38be7624bcbe4e2dc 100644 (file)
--- a/console.c
+++ b/console.c
@@ -2220,7 +2220,7 @@ its format (q1/q2/q3/hl) and even its message
 //LadyHavoc: rewrote bsp type detection, rewrote message extraction to do proper worldspawn parsing
 //LadyHavoc: added .ent file loading, and redesigned error handling to still try the .ent file even if the map format is not recognized, this also eliminated one goto
 //LadyHavoc: FIXME: man this GetMapList is STILL ugly code even after my cleanups...
-qbool GetMapList (const char *s, char *completedname, int completednamebufferlength)
+size_t GetMapList (const char *s, char *completedname, int completednamebuffersize)
 {
        fssearch_t      *t;
        char            message[1024];
@@ -2228,11 +2228,12 @@ qbool GetMapList (const char *s, char *completedname, int completednamebufferlen
        unsigned char *len;
        qfile_t         *f;
        unsigned char buf[1024];
+       size_t completednamelen = 0;
 
        dpsnprintf(message, sizeof(message), "maps/%s*.bsp", s);
        t = FS_Search(message, 1, true, NULL);
        if(!t)
-               return false;
+               return 0;
        if (t->numfilenames > 1)
                Con_Printf("^1 %i maps found :\n", t->numfilenames);
        len = (unsigned char *)Z_Malloc(t->numfilenames);
@@ -2379,14 +2380,15 @@ qbool GetMapList (const char *s, char *completedname, int completednamebufferlen
                                goto endcomplete;
        }
 endcomplete:
-       if(p > o && completedname && completednamebufferlength > 0)
+       if(p > o && completedname && completednamebuffersize > 0)
        {
-               memset(completedname, 0, completednamebufferlength);
-               memcpy(completedname, (t->filenames[0]+5), min(p, completednamebufferlength - 1));
+               completednamelen = min(p, completednamebuffersize - 1);
+               memcpy(completedname, (t->filenames[0]+5), completednamelen);
+               completedname[completednamelen] = '\0';
        }
        Z_Free(len);
        FS_FreeSearch(t);
-       return p > o;
+       return completednamelen;
 }
 
 /*
@@ -2833,6 +2835,30 @@ static int Nicks_AddLastColor(char *buffer, int pos)
        return pos;
 }
 
+
+static size_t Con_UnescapeSpaces(char *dst, const char *src, size_t dsize)
+{
+       char *dstptr = dst;
+       char *end = dst + dsize - 1;
+
+       for (; *src && dstptr < end; *dstptr++ = *src++)
+               if (*src == '\\' && src[1] == ' ')
+                       ++src;
+       *dstptr = '\0';
+       return dstptr - dst;
+}
+static size_t Con_EscapeSpaces(char *dst, const char *src, size_t dsize)
+{
+       char *dstptr = dst;
+       char *end = dst + dsize - 2; // allow for writing 2 chars per iteration
+
+       for (; *src && dstptr < end; *dstptr++ = *src++)
+               if (*src == ' ')
+                       *dstptr++ = '\\';
+       *dstptr = '\0';
+       return dstptr - dst;
+}
+
 /*
        Con_CompleteCommandLine
 
@@ -2841,7 +2867,10 @@ static int Nicks_AddLastColor(char *buffer, int pos)
        Thanks to Fett erich@heintz.com
        Thanks to taniwha
        Enhanced to tab-complete map names by [515]
-
+       Nick support by Blub
+       Pattern-matching file argument support by divVerent
+       Directory support by divVerent
+       Escaping support by bones_was_here
 */
 int Con_CompleteCommandLine(cmd_state_t *cmd, qbool is_console)
 {
@@ -2878,7 +2907,8 @@ int Con_CompleteCommandLine(cmd_state_t *cmd, qbool is_console)
        while(--pos >= linestart)
        {
                k = line[pos];
-               if(k == '\"' || k == ';' || k == ' ' || k == '\'')
+               if (k == '\"' || k == ';' || k == '\''
+               || (k == ' ' && (!is_console || pos == linestart || line[pos-1] != '\\')))
                        break;
        }
        pos++;
@@ -2894,6 +2924,10 @@ int Con_CompleteCommandLine(cmd_state_t *cmd, qbool is_console)
        space = strchr(line + 1, ' ');
        if(space && pos == (space - line) + 1)
        {
+               char temp[MAX_QPATH];
+               char searchstr[MAX_QPATH], result[MAX_QPATH * 2]; // enough to escape all possible spaces
+               size_t searchstrlen, resultlen;
+
                // adding 1 to line drops the leading ]
                dp_ustr2stp(command, sizeof(command), line + 1, space - (line + 1));
 
@@ -2904,15 +2938,17 @@ int Con_CompleteCommandLine(cmd_state_t *cmd, qbool is_console)
                if(!strcmp(command, "map") || !strcmp(command, "changelevel") || (patterns && !strcmp(patterns, "map")))
                {
                        //maps search
-                       char t[MAX_QPATH];
-                       if (GetMapList(s, t, sizeof(t)))
+                       Con_UnescapeSpaces(searchstr, s, sizeof(searchstr));
+                       if (GetMapList(searchstr, temp, sizeof(temp)))
                        {
+                               resultlen = Con_EscapeSpaces(result, temp, sizeof(result));
+
                                // first move the cursor
-                               linepos += (int)strlen(t) - (int)strlen(s);
+                               linepos += (int)resultlen - (int)strlen(s);
 
                                // and now do the actual work
                                *s = 0;
-                               dp_strlcat(line, t, MAX_INPUTLINE);
+                               dp_strlcat(line, result, MAX_INPUTLINE);
                                dp_strlcat(line, s2, MAX_INPUTLINE); //add back chars after cursor
 
                                // and fix the cursor
@@ -2925,7 +2961,6 @@ int Con_CompleteCommandLine(cmd_state_t *cmd, qbool is_console)
                {
                        if(patterns)
                        {
-                               char t[MAX_QPATH];
                                stringlist_t resultbuf, dirbuf;
 
                                // Usage:
@@ -2946,6 +2981,8 @@ int Con_CompleteCommandLine(cmd_state_t *cmd, qbool is_console)
 
                                stringlistinit(&resultbuf);
                                stringlistinit(&dirbuf);
+                               searchstrlen = Con_UnescapeSpaces(searchstr, s, sizeof(searchstr));
+
                                while(COM_ParseToken_Simple(&patterns, false, false, true))
                                {
                                        fssearch_t *search;
@@ -2955,12 +2992,12 @@ int Con_CompleteCommandLine(cmd_state_t *cmd, qbool is_console)
                                        }
                                        else
                                        {
-                                               const char *slash = strrchr(s, '/');
+                                               const char *slash = strrchr(searchstr, '/');
                                                if(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);
+                                                       dp_ustr2stp(temp, sizeof(temp), searchstr, slash - searchstr + 1); // + 1, because I want to include the slash
+                                                       dp_strlcat(temp, com_token, sizeof(temp));
+                                                       search = FS_Search(temp, true, true, NULL);
                                                }
                                                else
                                                        search = FS_Search(com_token, true, true, NULL);
@@ -2968,7 +3005,7 @@ int Con_CompleteCommandLine(cmd_state_t *cmd, qbool is_console)
                                        if(search)
                                        {
                                                for(i = 0; i < search->numfilenames; ++i)
-                                                       if(!strncmp(search->filenames[i], s, strlen(s)))
+                                                       if(!strncmp(search->filenames[i], searchstr, searchstrlen))
                                                                if(FS_FileType(search->filenames[i]) == FS_FILETYPE_FILE)
                                                                        stringlistappend(&resultbuf, search->filenames[i]);
                                                FS_FreeSearch(search);
@@ -2978,19 +3015,19 @@ int Con_CompleteCommandLine(cmd_state_t *cmd, qbool is_console)
                                // In any case, add directory names
                                {
                                        fssearch_t *search;
-                                       const char *slash = strrchr(s, '/');
+                                       const char *slash = strrchr(searchstr, '/');
                                        if(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);
+                                               dp_ustr2stp(temp, sizeof(temp), searchstr, slash - searchstr + 1); // + 1, because I want to include the slash
+                                               dp_strlcat(temp, "*", sizeof(temp));
+                                               search = FS_Search(temp, true, true, NULL);
                                        }
                                        else
                                                search = FS_Search("*", true, true, NULL);
                                        if(search)
                                        {
                                                for(i = 0; i < search->numfilenames; ++i)
-                                                       if(!strncmp(search->filenames[i], s, strlen(s)))
+                                                       if(!strncmp(search->filenames[i], searchstr, searchstrlen))
                                                                if(FS_FileType(search->filenames[i]) == FS_FILETYPE_DIRECTORY)
                                                                        stringlistappend(&dirbuf, search->filenames[i]);
                                                FS_FreeSearch(search);
@@ -3001,15 +3038,18 @@ int Con_CompleteCommandLine(cmd_state_t *cmd, qbool is_console)
                                {
                                        const char *p, *q;
                                        unsigned int matchchars;
+                                       bool appendspace = false;
 
                                        if(resultbuf.numstrings == 0 && dirbuf.numstrings == 1)
                                        {
-                                               dpsnprintf(t, sizeof(t), "%s/", dirbuf.strings[0]);
+                                               dpsnprintf(temp, sizeof(temp), "%s/", dirbuf.strings[0]);
                                        }
                                        else
                                        if(resultbuf.numstrings == 1 && dirbuf.numstrings == 0)
                                        {
-                                               dpsnprintf(t, sizeof(t), "%s ", resultbuf.strings[0]);
+                                               dp_strlcpy(temp, resultbuf.strings[0], sizeof(temp));
+                                               // trailing space can't be appended yet as it would get escaped
+                                               appendspace = true;
                                        }
                                        else
                                        {
@@ -3036,7 +3076,7 @@ int Con_CompleteCommandLine(cmd_state_t *cmd, qbool is_console)
                                                        else
                                                        for(; *p && *p == *q; ++p, ++q);
                                                        matchchars = (unsigned int)(p - resultbuf.strings[0]);
-                                                       dp_ustr2stp(t, sizeof(t), resultbuf.strings[0], matchchars);
+                                                       dp_ustr2stp(temp, sizeof(temp), resultbuf.strings[0], matchchars);
                                                }
                                                else // matching directory only
                                                {
@@ -3044,7 +3084,7 @@ int Con_CompleteCommandLine(cmd_state_t *cmd, qbool is_console)
                                                        q = dirbuf.strings[dirbuf.numstrings - 1];
                                                        for(; *p && *p == *q; ++p, ++q);
                                                        matchchars = (unsigned int)(p - dirbuf.strings[0]);
-                                                       dp_ustr2stp(t, sizeof(t), dirbuf.strings[0], matchchars);
+                                                       dp_ustr2stp(temp, sizeof(temp), 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
@@ -3052,12 +3092,19 @@ int Con_CompleteCommandLine(cmd_state_t *cmd, qbool is_console)
                                                // the unique prefix
                                        }
 
+                                       resultlen = Con_EscapeSpaces(result, temp, sizeof(result));
+                                       if (appendspace && resultlen < sizeof(result) - 1)
+                                       {
+                                               result[resultlen++] = ' ';
+                                               result[resultlen] = '\0';
+                                       }
+
                                        // first move the cursor
-                                       linepos += (int)strlen(t) - (int)strlen(s);
+                                       linepos += (int)resultlen - (int)strlen(s);
 
                                        // and now do the actual work
                                        *s = 0;
-                                       dp_strlcat(line, t, MAX_INPUTLINE);
+                                       dp_strlcat(line, result, MAX_INPUTLINE);
                                        dp_strlcat(line, s2, MAX_INPUTLINE); //add back chars after cursor
 
                                        // and fix the cursor
index 0f350376edc84273a745ea91d0e59f46ced911b9..7ea1ecd06823c3025d118eca1602351eee539e60 100644 (file)
--- a/console.h
+++ b/console.h
@@ -74,7 +74,7 @@ void Con_DrawNotify (void);
 void Con_ClearNotify (void);
 void Con_ToggleConsole_f(cmd_state_t *cmd);
 
-qbool GetMapList (const char *s, char *completedname, int completednamebufferlength);
+size_t GetMapList (const char *s, char *completedname, int completednamebuffersize);
 
 /// wrapper function to attempt to either complete the command line
 /// or to list possible matches grouped by type
diff --git a/qdefs.h b/qdefs.h
index 78f668d899fc895823eaf91b2cfb8fec2094fc9e..ad302bca269a7ffedc6fadcd1450435f6692b03d 100644 (file)
--- a/qdefs.h
+++ b/qdefs.h
 #define ISCOMMENT(ch, pos) ch[pos] == '/' && ch[pos + 1] == '/' && (pos == 0 || ISWHITESPACE(ch[pos - 1]))
 // This also includes extended characters, and ALL control chars
 #define ISWHITESPACEORCONTROL(ch) ((signed char) (ch) <= (signed char) ' ')
+// Returns false for spaces if they're escaped
+#define ISWHITESPACE_ANDNOTESCAPED(ch, prevch) (!(ch) || ((ch) == ' ' && (prevch) != '\\') || (ch) == '\t' || (ch) == '\r' || (ch) == '\n')
 
 #define DOUBLE_IS_TRUE_FOR_INT(x) ((x) & 0x7FFFFFFFFFFFFFFF) // also match "negative zero" doubles of value 0x8000000000000000
 #define DOUBLE_LOSSLESS_FORMAT "%.17g"