tab completion now allows directories (PLEASE TEST ON WIN32)
authordivverent <divverent@d7cf8633-e32d-0410-b094-e92efae38249>
Fri, 25 Jan 2008 09:08:53 +0000 (09:08 +0000)
committerdivverent <divverent@d7cf8633-e32d-0410-b094-e92efae38249>
Fri, 25 Jan 2008 09:08:53 +0000 (09:08 +0000)
git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@8008 d7cf8633-e32d-0410-b094-e92efae38249

console.c
fs.c
fs.h

index d7084e3..a70c05e 100644 (file)
--- a/console.c
+++ b/console.c
@@ -2245,95 +2245,146 @@ void Con_CompleteCommandLine (void)
                else
                {
                        const char *patterns = Cvar_VariableString(va("con_completion_%s", command)); // TODO maybe use a better place for this?
-                       char t[MAX_QPATH];
-                       stringlist_t resultbuf;
-
-                       // Usage:
-                       //   // store completion patterns (space separated) for command foo in con_completion_foo
-                       //   set con_completion_foo "foodata/*.foodefault *.foo"
-                       //   foo <TAB>
-                       //
-                       // Note: patterns with slash are always treated as absolute
-                       // patterns; patterns without slash search in the innermost
-                       // directory the user specified. There is no way to "complete into"
-                       // a directory as of now, as directories seem to be unknown to the
-                       // FS subsystem.
-                       //
-                       // Examples:
-                       //   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);
-                       while(COM_ParseToken_Simple(&patterns, false, false))
+
+                       if(patterns && *patterns)
                        {
-                               fssearch_t *search;
-                               if(strchr(com_token, '/'))
+                               char t[MAX_QPATH];
+                               stringlist_t resultbuf, dirbuf;
+
+                               // Usage:
+                               //   // store completion patterns (space separated) for command foo in con_completion_foo
+                               //   set con_completion_foo "foodata/*.foodefault *.foo"
+                               //   foo <TAB>
+                               //
+                               // Note: patterns with slash are always treated as absolute
+                               // patterns; patterns without slash search in the innermost
+                               // directory the user specified. There is no way to "complete into"
+                               // a directory as of now, as directories seem to be unknown to the
+                               // FS subsystem.
+                               //
+                               // Examples:
+                               //   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);
+                               while(COM_ParseToken_Simple(&patterns, false, false))
                                {
-                                       search = FS_Search(com_token, true, true);
+                                       fssearch_t *search;
+                                       if(strchr(com_token, '/'))
+                                       {
+                                               search = FS_Search(com_token, true, true);
+                                       }
+                                       else
+                                       {
+                                               const char *slash = strrchr(s, '/');
+                                               if(slash)
+                                               {
+                                                       strlcpy(t, s, min(sizeof(t), (unsigned int)(slash - s + 2))); // + 2, because I want to include the slash
+                                                       strlcat(t, com_token, sizeof(t));
+                                                       search = FS_Search(t, true, true);
+                                               }
+                                               else
+                                                       search = FS_Search(com_token, true, true);
+                                       }
+                                       if(search)
+                                       {
+                                               for(i = 0; i < search->numfilenames; ++i)
+                                                       if(!strncmp(search->filenames[i], s, strlen(s)))
+                                                               if(FS_FileType(search->filenames[i]) == FS_FILETYPE_FILE)
+                                                                       stringlistappend(&resultbuf, search->filenames[i]);
+                                               FS_FreeSearch(search);
+                                       }
                                }
-                               else
+
+                               // In any case, add directory names
                                {
+                                       fssearch_t *search;
                                        const char *slash = strrchr(s, '/');
                                        if(slash)
                                        {
                                                strlcpy(t, s, min(sizeof(t), (unsigned int)(slash - s + 2))); // + 2, because I want to include the slash
-                                               strlcat(t, com_token, sizeof(t));
+                                               strlcat(t, "/*", sizeof(t));
                                                search = FS_Search(t, true, true);
                                        }
                                        else
-                                               search = FS_Search(com_token, true, true);
-                               }
-                               if(search)
-                               {
-                                       for(i = 0; i < search->numfilenames; ++i)
-                                               if(!strncmp(search->filenames[i], s, strlen(s)))
-                                                       stringlistappend(&resultbuf, search->filenames[i]);
-                                       FS_FreeSearch(search);
-                               }
-                       }
-                       
-                       if(resultbuf.numstrings > 0)
-                       {
-                               const char *p, *q;
-                               if(resultbuf.numstrings == 1)
-                               {
-                                       dpsnprintf(t, sizeof(t), "%s ", resultbuf.strings[0]);
+                                               search = FS_Search("*", true, true);
+                                       if(search)
+                                       {
+                                               for(i = 0; i < search->numfilenames; ++i)
+                                                       if(!strncmp(search->filenames[i], s, strlen(s)))
+                                                               if(FS_FileType(search->filenames[i]) == FS_FILETYPE_DIRECTORY)
+                                                                       stringlistappend(&dirbuf, search->filenames[i]);
+                                               FS_FreeSearch(search);
+                                       }
                                }
-                               else
+
+                               if(resultbuf.numstrings > 0 || dirbuf.numstrings > 0)
                                {
-                                       stringlistsort(&resultbuf);
-                                       Con_Printf("\n%i possible filenames\n", resultbuf.numstrings);
-                                       for(i = 0; i < resultbuf.numstrings; ++i)
+                                       const char *p, *q;
+                                       unsigned int matchchars;
+                                       if(resultbuf.numstrings == 0 && dirbuf.numstrings == 1)
                                        {
-                                               Con_Printf("%s\n", resultbuf.strings[i]);
+                                               dpsnprintf(t, sizeof(t), "%s/", dirbuf.strings[0]);
+                                       }
+                                       else
+                                       if(resultbuf.numstrings == 1 && dirbuf.numstrings == 0)
+                                       {
+                                               dpsnprintf(t, sizeof(t), "%s ", resultbuf.strings[0]);
+                                       }
+                                       else
+                                       {
+                                               stringlistsort(&resultbuf); // dirbuf is already sorted
+                                               Con_Printf("\n%i possible filenames\n", resultbuf.numstrings + dirbuf.numstrings);
+                                               for(i = 0; i < dirbuf.numstrings; ++i)
+                                               {
+                                                       Con_Printf("%s/\n", dirbuf.strings[i]);
+                                               }
+                                               for(i = 0; i < resultbuf.numstrings; ++i)
+                                               {
+                                                       Con_Printf("%s\n", resultbuf.strings[i]);
+                                               }
+                                               matchchars = sizeof(t) - 1;
+                                               if(resultbuf.numstrings > 0)
+                                               {
+                                                       p = resultbuf.strings[0];
+                                                       q = resultbuf.strings[resultbuf.numstrings - 1];
+                                                       for(; *p && *p == *q; ++p, ++q);
+                                                       matchchars = p - resultbuf.strings[0];
+                                               }
+                                               if(dirbuf.numstrings > 0)
+                                               {
+                                                       p = dirbuf.strings[0];
+                                                       q = dirbuf.strings[dirbuf.numstrings - 1];
+                                                       for(; *p && *p == *q; ++p, ++q);
+                                                       matchchars = min(matchchars, p - dirbuf.strings[0]);
+                                               }
+                                               // 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
+                                               strlcpy(t, (resultbuf.numstrings > 0 ? resultbuf : dirbuf).strings[0], min(matchchars + 1, sizeof(t)));
                                        }
-                                       p = resultbuf.strings[0];
-                                       q = resultbuf.strings[resultbuf.numstrings - 1];
-                                       for(; *p && *p == *q; ++p, ++q);
-                                       // 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
-                                       strlcpy(t, resultbuf.strings[0], min((unsigned int)(p - resultbuf.strings[0] + 1), sizeof(t)));
-                               }
 
-                               // first move the cursor
-                               key_linepos += (int)strlen(t) - (int)strlen(s);
+                                       // first move the cursor
+                                       key_linepos += (int)strlen(t) - (int)strlen(s);
 
-                               // and now do the actual work
-                               *s = 0;
-                               strlcat(key_lines[edit_line], t, MAX_INPUTLINE);
-                               strlcat(key_lines[edit_line], s2, MAX_INPUTLINE); //add back chars after cursor
+                                       // and now do the actual work
+                                       *s = 0;
+                                       strlcat(key_lines[edit_line], t, MAX_INPUTLINE);
+                                       strlcat(key_lines[edit_line], s2, MAX_INPUTLINE); //add back chars after cursor
 
-                               // and fix the cursor
-                               if(key_linepos > (int) strlen(key_lines[edit_line]))
-                                       key_linepos = (int) strlen(key_lines[edit_line]);
+                                       // and fix the cursor
+                                       if(key_linepos > (int) strlen(key_lines[edit_line]))
+                                               key_linepos = (int) strlen(key_lines[edit_line]);
+                               }
+                               stringlistfreecontents(&resultbuf);
+                               stringlistfreecontents(&dirbuf);
                        }
-                       stringlistfreecontents(&resultbuf);
                }
        }
 
diff --git a/fs.c b/fs.c
index 64b2754..8fd5d61 100644 (file)
--- a/fs.c
+++ b/fs.c
@@ -2424,6 +2424,30 @@ void FS_DefaultExtension (char *path, const char *extension, size_t size_path)
 }
 
 
+/*
+==================
+FS_FileType
+
+Look for a file in the packages and in the filesystem
+==================
+*/
+int FS_FileType (const char *filename)
+{
+       searchpath_t *search;
+       char fullpath[MAX_QPATH];
+
+       search = FS_FindFile (filename, NULL, true);
+       if(!search)
+               return FS_FILETYPE_NONE;
+
+       if(search->pack)
+               return FS_FILETYPE_FILE; // TODO can't check directories in paks yet, maybe later
+
+       dpsnprintf(fullpath, sizeof(fullpath), "%s%s", search->filename, filename);
+       return FS_SysFileType(fullpath);
+}
+
+
 /*
 ==================
 FS_FileExists
@@ -2444,28 +2468,36 @@ FS_SysFileExists
 Look for a file in the filesystem only
 ==================
 */
-qboolean FS_SysFileExists (const char *path)
+int FS_SysFileType (const char *path)
 {
 #if WIN32
-       int desc;
+       DWORD result = GetFileAttributes(path);
 
-       // TODO: use another function instead, to avoid opening the file
-       desc = open (path, O_RDONLY | O_BINARY);
-       if (desc < 0)
-               return false;
+       if(result == INVALID_FILE_ATTRIBUTES)
+               return FS_FILETYPE_NONE;
 
-       close (desc);
-       return true;
+       if(result & FILE_ATTRIBUTE_DIRECTORY)
+               return FS_FILETYPE_DIRECTORY;
+
+       return FS_FILETYPE_FILE;
 #else
        struct stat buf;
 
        if (stat (path,&buf) == -1)
-               return false;
+               return FS_FILETYPE_NONE;
 
-       return true;
+       if(S_ISDIR(buf.st_mode))
+               return FS_FILETYPE_DIRECTORY;
+
+       return FS_FILETYPE_FILE;
 #endif
 }
 
+qboolean FS_SysFileExists (const char *path)
+{
+       return FS_SysFileType (path) != FS_FILETYPE_NONE;
+}
+
 void FS_mkdir (const char *path)
 {
 #if WIN32
diff --git a/fs.h b/fs.h
index 141886a..8f31b7c 100644 (file)
--- a/fs.h
+++ b/fs.h
@@ -100,6 +100,12 @@ qboolean FS_WriteFile (const char *filename, void *data, fs_offset_t len);
 void FS_StripExtension (const char *in, char *out, size_t size_out);
 void FS_DefaultExtension (char *path, const char *extension, size_t size_path);
 
+#define FS_FILETYPE_NONE 0
+#define FS_FILETYPE_FILE 1
+#define FS_FILETYPE_DIRECTORY 2
+int FS_FileType (const char *filename);                // the file can be into a package
+int FS_SysFileType (const char *filename);             // only look for files outside of packages
+
 qboolean FS_FileExists (const char *filename);         // the file can be into a package
 qboolean FS_SysFileExists (const char *filename);      // only look for files outside of packages