]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - fs.c
rewrote FS_Search, hopefully it will work better now, and it now matches directories...
[xonotic/darkplaces.git] / fs.c
diff --git a/fs.c b/fs.c
index 90ac578014801dea68fbaad4bff311251fd09979..3143e7badb681d6c5f9be81ea5771e0df151c073 100644 (file)
--- a/fs.c
+++ b/fs.c
@@ -328,7 +328,7 @@ qboolean PK3_OpenLibrary (void)
 #ifdef WIN32
        dllname = "zlib.dll";
 #else
-       dllname = "libz.so.1";
+       dllname = "libz.so";
 #endif
 
        // Initializations
@@ -547,7 +547,7 @@ pack_t *FS_LoadPackPK3 (const char *packfile)
        // Create a package structure in memory
        pack = Mem_Alloc (pak_mempool, sizeof (pack_t));
        pack->ignorecase = true; // PK3 ignores case
-       strcpy (pack->filename, packfile);
+       strlcpy (pack->filename, packfile, sizeof (pack->filename));
        pack->handle = packhandle;
        pack->numfiles = eocd.nbentries;
        pack->mempool = Mem_AllocPool (packfile);
@@ -688,7 +688,7 @@ pack_t *FS_LoadPackPAK (const char *packfile)
 
        pack = Mem_Alloc(pak_mempool, sizeof (pack_t));
        pack->ignorecase = false; // PAK is case sensitive
-       strcpy (pack->filename, packfile);
+       strlcpy (pack->filename, packfile, sizeof (pack->filename));
        pack->handle = packhandle;
        pack->numfiles = numpackfiles;
        pack->mempool = Mem_AllocPool(packfile);
@@ -706,7 +706,7 @@ pack_t *FS_LoadPackPAK (const char *packfile)
                size_t size;
                packfile_t *file = &pack->files[i];
 
-               strcpy (file->name, info[i].name);
+               strlcpy (file->name, info[i].name, sizeof (file->name));
                file->offset = LittleLong(info[i].filepos);
                size = LittleLong (info[i].filelen);
                file->packsize = size;
@@ -736,11 +736,11 @@ void FS_AddGameDirectory (char *dir)
        pack_t *pak;
        char pakfile[MAX_OSPATH];
 
-       strcpy (fs_gamedir, dir);
+       strlcpy (fs_gamedir, dir, sizeof (fs_gamedir));
 
        // add the directory to the search path
        search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
-       strcpy (search->filename, dir);
+       strlcpy (search->filename, dir, sizeof (search->filename));
        search->next = fs_searchpaths;
        fs_searchpaths = search;
 
@@ -751,7 +751,7 @@ void FS_AddGameDirectory (char *dir)
        {
                if (matchpattern(current->text, "*.pak", true))
                {
-                       sprintf (pakfile, "%s/%s", dir, current->text);
+                       snprintf (pakfile, sizeof (pakfile), "%s/%s", dir, current->text);
                        pak = FS_LoadPackPAK (pakfile);
                        if (pak)
                        {
@@ -770,7 +770,7 @@ void FS_AddGameDirectory (char *dir)
        {
                if (matchpattern(current->text, "*.pk3", true))
                {
-                       sprintf (pakfile, "%s/%s", dir, current->text);
+                       snprintf (pakfile, sizeof (pakfile), "%s/%s", dir, current->text);
                        pak = FS_LoadPackPK3 (pakfile);
                        if (pak)
                        {
@@ -816,6 +816,8 @@ char *FS_FileExtension (const char *in)
        return exten;
 }
 
+void FS_Dir_f(void);
+void FS_Ls_f(void);
 
 /*
 ================
@@ -831,6 +833,8 @@ void FS_Init (void)
        pak_mempool = Mem_AllocPool("paks");
 
        Cmd_AddCommand ("path", FS_Path_f);
+       Cmd_AddCommand ("dir", FS_Dir_f);
+       Cmd_AddCommand ("ls", FS_Ls_f);
 
        strcpy(fs_basedir, ".");
 
@@ -840,19 +844,19 @@ void FS_Init (void)
        // Overrides the system supplied base directory (under GAMENAME)
        i = COM_CheckParm ("-basedir");
        if (i && i < com_argc-1)
-               strcpy (fs_basedir, com_argv[i+1]);
+               strlcpy (fs_basedir, com_argv[i+1], sizeof (fs_basedir));
 
        i = strlen (fs_basedir);
        if (i > 0 && (fs_basedir[i-1] == '\\' || fs_basedir[i-1] == '/'))
                fs_basedir[i-1] = 0;
 
        // start up with GAMENAME by default (id1)
-       strcpy(com_modname, GAMENAME);
+       strlcpy (com_modname, GAMENAME, sizeof (com_modname));
        FS_AddGameDirectory (va("%s/"GAMENAME, fs_basedir));
        if (gamedirname[0])
        {
                fs_modified = true;
-               strcpy(com_modname, gamedirname);
+               strlcpy (com_modname, gamedirname, sizeof (com_modname));
                FS_AddGameDirectory (va("%s/%s", fs_basedir, gamedirname));
        }
 
@@ -862,7 +866,7 @@ void FS_Init (void)
        if (i && i < com_argc-1)
        {
                fs_modified = true;
-               strcpy(com_modname, com_argv[i+1]);
+               strlcpy (com_modname, com_argv[i+1], sizeof (com_modname));
                FS_AddGameDirectory (va("%s/%s", fs_basedir, com_argv[i+1]));
        }
 
@@ -892,7 +896,7 @@ void FS_Init (void)
                                        Sys_Error ("Couldn't load packfile: %s", com_argv[i]);
                        }
                        else
-                               strcpy (search->filename, com_argv[i]);
+                               strlcpy (search->filename, com_argv[i], sizeof (search->filename));
                        search->next = fs_searchpaths;
                        fs_searchpaths = search;
                }
@@ -982,6 +986,103 @@ qfile_t *FS_FOpenFile (const char *filename, qboolean quiet)
 
        filenamelen = strlen (filename);
 
+       // LordHavoc: this is not written right!
+       // (should search have higher priority for files in each gamedir, while
+       // preserving the gamedir priorities, not searching for all paks in all
+       // gamedirs and then all files in all gamedirs)
+#ifdef AKVERSION
+       // first we search for a real file, after that we start to search through the paks
+       // search through the path, one element at a time
+       search = fs_searchpaths;
+
+       for( ; search ; search = search->next)
+               if(!search->pack)
+               {
+                       snprintf (netpath, sizeof (netpath), "%s/%s",search->filename, filename);
+
+                       if (!FS_SysFileExists (netpath))
+                               continue;
+
+                       if (!quiet)
+                               Sys_Printf ("FindFile: %s\n",netpath);
+                       return FS_OpenRead (netpath, -1, -1);
+               }
+
+       search = fs_searchpaths;
+       for ( ; search ; search = search->next)
+               // is the element a pak file?
+               if (search->pack)
+               {
+                       // look through all the pak file elements
+                       pak = search->pack;
+                       for (i=0 ; i<pak->numfiles ; i++)
+                       {
+                               if (pak->ignorecase)
+                                       matched = !strcasecmp (pak->files[i].name, filename);
+                               else
+                                       matched = !strcmp (pak->files[i].name, filename);
+                               if (matched)  // found it?
+                               {
+                                       qfile_t *file;
+
+                                       if (!quiet)
+                                               Sys_Printf ("PackFile: %s : %s\n",pak->filename, pak->files[i].name);
+
+                                       // If we don't have the true offset, get it now
+                                       if (! (pak->files[i].flags & FILE_FLAG_TRUEOFFS))
+                                               PK3_GetTrueFileOffset (&pak->files[i], pak);
+
+                                       // No Zlib DLL = no compressed files
+                                       if (!zlib_dll && (pak->files[i].flags & FILE_FLAG_DEFLATED))
+                                       {
+                                               Con_Printf ("WARNING: can't open the compressed file %s\n"
+                                                                       "You need the Zlib DLL to use compressed files\n", filename);
+                                               fs_filesize = -1;
+                                               return NULL;
+                                       }
+
+                                       // open a new file in the pakfile
+                                       file = FS_OpenRead (pak->filename, pak->files[i].offset, pak->files[i].packsize);
+                                       fs_filesize = pak->files[i].realsize;
+
+                                       if (pak->files[i].flags & FILE_FLAG_DEFLATED)
+                                       {
+                                               ztoolkit_t *ztk;
+
+                                               file->flags |= FS_FLAG_DEFLATED;
+
+                                               // We need some more variables
+                                               ztk = Mem_Alloc (fs_mempool, sizeof (*file->z));
+
+                                               ztk->real_length = pak->files[i].realsize;
+
+                                               // Initialize zlib stream
+                                               ztk->zstream.next_in = ztk->input;
+                                               ztk->zstream.avail_in = 0;
+
+                                               /* From Zlib's "unzip.c":
+                                                *
+                                                * windowBits is passed < 0 to tell that there is no zlib header.
+                                                * Note that in this case inflate *requires* an extra "dummy" byte
+                                                * after the compressed stream in order to complete decompression and
+                                                * return Z_STREAM_END.
+                                                * In unzip, i don't wait absolutely Z_STREAM_END because I known the
+                                                * size of both compressed and uncompressed data
+                                                */
+                                               if (qz_inflateInit2 (&ztk->zstream, -MAX_WBITS) != Z_OK)
+                                                       Sys_Error ("inflate init error (file: %s)", filename);
+
+                                               ztk->zstream.next_out = ztk->output;
+                                               ztk->zstream.avail_out = sizeof (ztk->output);
+
+                                               file->z = ztk;
+                                       }
+
+                                       return file;
+                               }
+                       }
+               }
+#else
        // search through the path, one element at a time
        search = fs_searchpaths;
 
@@ -1061,7 +1162,7 @@ qfile_t *FS_FOpenFile (const char *filename, qboolean quiet)
                }
                else
                {
-                       sprintf (netpath, "%s/%s",search->filename, filename);
+                       snprintf (netpath, sizeof (netpath), "%s/%s",search->filename, filename);
 
                        if (!FS_SysFileExists (netpath))
                                continue;
@@ -1071,6 +1172,7 @@ qfile_t *FS_FOpenFile (const char *filename, qboolean quiet)
                        return FS_OpenRead (netpath, -1, -1);
                }
        }
+#endif
 
        if (!quiet)
                Sys_Printf ("FindFile: can't find %s\n", filename);
@@ -1603,7 +1705,7 @@ qboolean FS_WriteFile (const char *filename, void *data, int len)
        FILE *handle;
        char name[MAX_OSPATH];
 
-       sprintf (name, "%s/%s", fs_gamedir, filename);
+       snprintf (name, sizeof (name), "%s/%s", fs_gamedir, filename);
 
        // Create directories up to the file
        FS_CreatePath (name);
@@ -1635,16 +1737,21 @@ OTHERS PUBLIC FUNCTIONS
 FS_StripExtension
 ============
 */
-void FS_StripExtension (const char *in, char *out)
+void FS_StripExtension (const char *in, char *out, size_t size_out)
 {
        char *last = NULL;
-       while (*in)
+
+       if (size_out == 0)
+               return;
+
+       while (*in && size_out > 1)
        {
                if (*in == '.')
                        last = out;
                else if (*in == '/' || *in == '\\' || *in == ':')
                        last = NULL;
                *out++ = *in++;
+               size_out--;
        }
        if (last)
                *last = 0;
@@ -1658,7 +1765,7 @@ void FS_StripExtension (const char *in, char *out)
 FS_DefaultExtension
 ==================
 */
-void FS_DefaultExtension (char *path, const char *extension)
+void FS_DefaultExtension (char *path, const char *extension, size_t size_path)
 {
        const char *src;
 
@@ -1673,7 +1780,7 @@ void FS_DefaultExtension (char *path, const char *extension)
                src--;
        }
 
-       strcat (path, extension);
+       strlcat (path, extension, size_path);
 }
 
 
@@ -1695,7 +1802,7 @@ qboolean FS_FileExists (const char *filename)
                }
                else
                {
-                       sprintf (netpath, "%s/%s",search->filename, filename);
+                       snprintf (netpath, sizeof (netpath), "%s/%s",search->filename, filename);
                        if (FS_SysFileExists (netpath))
                                return true;
                }
@@ -1736,3 +1843,255 @@ void FS_mkdir (const char *path)
        mkdir (path, 0777);
 #endif
 }
+
+/*
+===========
+FS_Search
+
+Allocate and fill a search structure with information on matching filenames.
+===========
+*/
+fssearch_t *FS_Search(const char *pattern, int caseinsensitive, int quiet)
+{
+       fssearch_t *search;
+       searchpath_t *searchpath;
+       pack_t *pak;
+       int i, basepathlength, numfiles, numchars;
+       stringlist_t *dir, *dirfile, *liststart, *listcurrent, *listtemp;
+       const char *slash, *backslash, *colon, *separator;
+       char *basepath;
+       char netpath[MAX_OSPATH];
+       char temp[MAX_OSPATH];
+
+       while(!strncmp(pattern, "./", 2))
+               pattern += 2;
+       while(!strncmp(pattern, ".\\", 2))
+               pattern += 2;
+
+       search = NULL;
+       liststart = NULL;
+       listcurrent = NULL;
+       listtemp = NULL;
+       slash = strrchr(pattern, '/');
+       backslash = strrchr(pattern, '\\');
+       colon = strrchr(pattern, ':');
+       separator = pattern;
+       if (separator < slash)
+               separator = slash;
+       if (separator < backslash)
+               separator = backslash;
+       if (separator < colon)
+               separator = colon;
+       basepathlength = separator - pattern;
+       basepath = Z_Malloc(basepathlength + 1);
+       if (basepathlength)
+               memcpy(basepath, pattern, basepathlength);
+       basepath[basepathlength] = 0;
+
+       // search through the path, one element at a time
+       for (searchpath = fs_searchpaths;searchpath;searchpath = searchpath->next)
+       {
+               // is the element a pak file?
+               if (searchpath->pack)
+               {
+                       // look through all the pak file elements
+                       pak = searchpath->pack;
+                       for (i = 0;i < pak->numfiles;i++)
+                       {
+                               strcpy(temp, pak->files[i].name);
+                               while (temp[0])
+                               {
+                                       if (matchpattern(temp, (char *)pattern, true))
+                                       {
+                                               for (listtemp = liststart;listtemp;listtemp = listtemp->next)
+                                                       if (!strcmp(listtemp->text, temp))
+                                                               break;
+                                               if (listtemp == NULL)
+                                               {
+                                                       listcurrent = stringlistappend(listcurrent, temp);
+                                                       if (liststart == NULL)
+                                                               liststart = listcurrent;
+                                                       if (!quiet)
+                                                               Sys_Printf("SearchPackFile: %s : %s\n", pak->filename, temp);
+                                               }
+                                       }
+                                       // strip off one path element at a time until empty
+                                       // this way directories are added to the listing if they match the pattern
+                                       slash = strrchr(temp, '/');
+                                       backslash = strrchr(temp, '\\');
+                                       colon = strrchr(temp, ':');
+                                       separator = temp;
+                                       if (separator < slash)
+                                               separator = slash;
+                                       if (separator < backslash)
+                                               separator = backslash;
+                                       if (separator < colon)
+                                               separator = colon;
+                                       *((char *)separator) = 0;
+                               }
+                       }
+               }
+               else
+               {
+                       // get a directory listing and look at each name
+                       snprintf(netpath, sizeof (netpath), "%s/%s", searchpath->filename, basepath);
+                       if ((dir = listdirectory(netpath)))
+                       {
+                               for (dirfile = dir;dirfile;dirfile = dirfile->next)
+                               {
+                                       memcpy(temp, basepath, basepathlength);
+                                       strcpy(temp + basepathlength, dirfile->text);
+                                       if (matchpattern(temp, (char *)pattern, true))
+                                       {
+                                               for (listtemp = liststart;listtemp;listtemp = listtemp->next)
+                                                       if (!strcmp(listtemp->text, temp))
+                                                               break;
+                                               if (listtemp == NULL)
+                                               {
+                                                       listcurrent = stringlistappend(listcurrent, temp);
+                                                       if (liststart == NULL)
+                                                               liststart = listcurrent;
+                                                       if (!quiet)
+                                                               Sys_Printf("SearchDirFile: %s\n", temp);
+                                               }
+                                       }
+                               }
+                               freedirectory(dir);
+                       }
+               }
+       }
+
+       if (liststart)
+       {
+               liststart = stringlistsort(liststart);
+               numfiles = 0;
+               numchars = 0;
+               for (listtemp = liststart;listtemp;listtemp = listtemp->next)
+               {
+                       numfiles++;
+                       numchars += strlen(listtemp->text) + 1;
+               }
+               search = Z_Malloc(sizeof(fssearch_t) + numchars + numfiles * sizeof(char *));
+               search->filenames = (char **)((char *)search + sizeof(fssearch_t));
+               search->filenamesbuffer = (char *)((char *)search + sizeof(fssearch_t) + numfiles * sizeof(char *));
+               search->numfilenames = numfiles;
+               numfiles = 0;
+               numchars = 0;
+               for (listtemp = liststart;listtemp;listtemp = listtemp->next)
+               {
+                       search->filenames[numfiles] = search->filenamesbuffer + numchars;
+                       strcpy(search->filenames[numfiles], listtemp->text);
+                       numfiles++;
+                       numchars += strlen(listtemp->text) + 1;
+               }
+               if (liststart)
+                       stringlistfree(liststart);
+       }
+
+       Z_Free(basepath);
+       return search;
+}
+
+void FS_FreeSearch(fssearch_t *search)
+{
+       Z_Free(search);
+}
+
+extern int con_linewidth;
+int FS_ListDirectory(const char *pattern, int oneperline)
+{
+       int numfiles;
+       int numcolumns;
+       int numlines;
+       int columnwidth;
+       int linebufpos;
+       int i, j, k, l;
+       const char *name;
+       char linebuf[4096];
+       fssearch_t *search;
+       search = FS_Search(pattern, true, false);
+       if (!search)
+               return 0;
+       numfiles = search->numfilenames;
+       if (!oneperline)
+       {
+               // FIXME: the names could be added to one column list and then
+               // gradually shifted into the next column if they fit, and then the
+               // next to make a compact variable width listing but it's a lot more
+               // complicated...
+               // find width for columns
+               columnwidth = 0;
+               for (i = 0;i < numfiles;i++)
+               {
+                       l = strlen(search->filenames[i]);
+                       if (columnwidth < l)
+                               columnwidth = l;
+               }
+               // count the spacing character
+               columnwidth++;
+               // calculate number of columns
+               numcolumns = con_linewidth / columnwidth;
+               // don't bother with the column printing if it's only one column
+               if (numcolumns >= 2)
+               {
+                       numlines = (numfiles + numcolumns - 1) / numcolumns;
+                       for (i = 0;i < numlines;i++)
+                       {
+                               linebufpos = 0;
+                               for (k = 0;k < numcolumns;k++)
+                               {
+                                       l = i * numcolumns + k;
+                                       if (l < numfiles)
+                                       {
+                                               name = search->filenames[l];
+                                               for (j = 0;name[j] && j < (int)sizeof(linebuf) - 1;j++)
+                                                       linebuf[linebufpos++] = name[j];
+                                               // space out name unless it's the last on the line
+                                               if (k < (numcolumns - 1) && l < (numfiles - 1))
+                                                       for (;j < columnwidth && j < (int)sizeof(linebuf) - 1;j++)
+                                                               linebuf[linebufpos++] = ' ';
+                                       }
+                               }
+                               linebuf[linebufpos] = 0;
+                               Con_Printf("%s\n", linebuf);
+                       }
+               }
+               else
+                       oneperline = true;
+       }
+       if (oneperline)
+               for (i = 0;i < numfiles;i++)
+                       Con_Printf("%s\n", search->filenames[i]);
+       FS_FreeSearch(search);
+       return numfiles;
+}
+
+void FS_Dir_f(void)
+{
+       char pattern[MAX_OSPATH];
+       if (Cmd_Argc() > 3)
+       {
+               Con_Printf("usage:\ndir [path/pattern]\n");
+               return;
+       }
+       strcpy(pattern, "*");
+       if (Cmd_Argc() == 2)
+               snprintf(pattern, sizeof(pattern), "%s", Cmd_Argv(1));
+       if (!FS_ListDirectory(pattern, true))
+               Con_Printf("No files found.\n");
+}
+
+void FS_Ls_f(void)
+{
+       char pattern[MAX_OSPATH];
+       if (Cmd_Argc() > 3)
+       {
+               Con_Printf("usage:\nls [path/pattern]\n");
+               return;
+       }
+       strcpy(pattern, "*");
+       if (Cmd_Argc() == 2)
+               snprintf(pattern, sizeof(pattern), "%s", Cmd_Argv(1));
+       FS_ListDirectory(pattern, false);
+}
+