//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];
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);
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;
}
/*
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
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)
{
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++;
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));
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
{
if(patterns)
{
- char t[MAX_QPATH];
stringlist_t resultbuf, dirbuf;
// Usage:
stringlistinit(&resultbuf);
stringlistinit(&dirbuf);
+ searchstrlen = Con_UnescapeSpaces(searchstr, s, sizeof(searchstr));
+
while(COM_ParseToken_Simple(&patterns, false, false, true))
{
fssearch_t *search;
}
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);
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);
// 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);
{
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
{
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
{
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
// 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