mirror of
https://github.com/fabiangreffrath/woof.git
synced 2025-09-23 03:52:12 -04:00
add the autoload folder feature from Chocolate Doom (#159)
* add the autoload folder feature from Chocolate Doom #Fixes #141 * add win_opendir.c to allow for building with MSVC * add win_opendir.c to the build system * run cppcheck with -DHAVE_DIRENT_H * apply build fixes thanks @rfomin * outright ignore Source/win_opendir.c during cppcheck * some more fixes * fixed the wrong warning in the wrong file 🤦 * move implementations to d_main.c * move autoload folders down into an "autoload" folder Also, use M_BaseName() instead of re-inventing the wheel. Add comments and some clean-up. * add back "D_dehout()" parameter to ProcessDehFile() invocation
This commit is contained in:
parent
a82c0c0d22
commit
4be5ef5efe
@ -1,6 +1,6 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
if [ "$ANALYZE" = "true" ] ; then
|
if [ "$ANALYZE" = "true" ] ; then
|
||||||
cppcheck --error-exitcode=1 -j2 -DRANGECHECK -ISource Source toolsrc 2> stderr.txt
|
cppcheck --error-exitcode=1 -j2 -DRANGECHECK -iSource/win_opendir.c -ISource Source toolsrc 2> stderr.txt
|
||||||
RET=$?
|
RET=$?
|
||||||
if [ -s stderr.txt ]
|
if [ -s stderr.txt ]
|
||||||
then
|
then
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
include(CheckLibraryExists)
|
include(CheckLibraryExists)
|
||||||
|
include(CheckIncludeFile)
|
||||||
include(ExternalProject)
|
include(ExternalProject)
|
||||||
|
|
||||||
# Adds the cmake directory to the CMake include path.
|
# Adds the cmake directory to the CMake include path.
|
||||||
@ -43,6 +44,7 @@ option("${PROJECT_NAME}_STRICT" "Prefer original MBF code paths over demo compat
|
|||||||
|
|
||||||
# Compiler environment requirements.
|
# Compiler environment requirements.
|
||||||
check_library_exists(m pow "" m_FOUND)
|
check_library_exists(m pow "" m_FOUND)
|
||||||
|
check_include_file("dirent.h" HAVE_DIRENT_H)
|
||||||
|
|
||||||
# Library requirements.
|
# Library requirements.
|
||||||
#
|
#
|
||||||
|
@ -27,6 +27,7 @@ set(WOOF_SOURCES
|
|||||||
g_game.c g_game.h
|
g_game.c g_game.h
|
||||||
hu_lib.c hu_lib.h
|
hu_lib.c hu_lib.h
|
||||||
hu_stuff.c hu_stuff.h
|
hu_stuff.c hu_stuff.h
|
||||||
|
i_glob.c i_glob.h
|
||||||
i_main.c
|
i_main.c
|
||||||
i_midipipe.c i_midipipe.h
|
i_midipipe.c i_midipipe.h
|
||||||
i_net.c i_net.h
|
i_net.c i_net.h
|
||||||
@ -92,6 +93,12 @@ set(WOOF_SOURCES
|
|||||||
wi_stuff.c wi_stuff.h
|
wi_stuff.c wi_stuff.h
|
||||||
z_zone.c z_zone.h)
|
z_zone.c z_zone.h)
|
||||||
|
|
||||||
|
if(MSVC)
|
||||||
|
list(APPEND
|
||||||
|
WOOF_SOURCES
|
||||||
|
win_opendir.c win_opendir.h)
|
||||||
|
endif()
|
||||||
|
|
||||||
# Some platforms require standard libraries to be linked against.
|
# Some platforms require standard libraries to be linked against.
|
||||||
if(m_FOUND)
|
if(m_FOUND)
|
||||||
list(APPEND WOOF_LIBRARIES m)
|
list(APPEND WOOF_LIBRARIES m)
|
||||||
|
@ -1400,7 +1400,7 @@ actionf_t deh_codeptr[NUMSTATES];
|
|||||||
// killough 10/98:
|
// killough 10/98:
|
||||||
// substantially modified to allow input from wad lumps instead of .deh files.
|
// substantially modified to allow input from wad lumps instead of .deh files.
|
||||||
|
|
||||||
void ProcessDehFile(char *filename, char *outfilename, int lumpnum)
|
void ProcessDehFile(const char *filename, char *outfilename, int lumpnum)
|
||||||
{
|
{
|
||||||
static FILE *fileout; // In case -dehout was used
|
static FILE *fileout; // In case -dehout was used
|
||||||
DEHFILE infile, *filein = &infile; // killough 10/98
|
DEHFILE infile, *filein = &infile; // killough 10/98
|
||||||
|
102
Source/d_main.c
102
Source/d_main.c
@ -67,11 +67,12 @@
|
|||||||
#include "d_deh.h" // Ty 04/08/98 - Externalizations
|
#include "d_deh.h" // Ty 04/08/98 - Externalizations
|
||||||
#include "statdump.h" // [FG] StatDump()
|
#include "statdump.h" // [FG] StatDump()
|
||||||
#include "u_mapinfo.h" // U_ParseMapInfo()
|
#include "u_mapinfo.h" // U_ParseMapInfo()
|
||||||
|
#include "i_glob.h" // [FG] I_StartMultiGlob()
|
||||||
|
|
||||||
// DEHacked support - Ty 03/09/97
|
// DEHacked support - Ty 03/09/97
|
||||||
// killough 10/98:
|
// killough 10/98:
|
||||||
// Add lump number as third argument, for use when filename==NULL
|
// Add lump number as third argument, for use when filename==NULL
|
||||||
void ProcessDehFile(char *filename, char *outfilename, int lump);
|
void ProcessDehFile(const char *filename, char *outfilename, int lump);
|
||||||
|
|
||||||
// killough 10/98: support -dehout filename
|
// killough 10/98: support -dehout filename
|
||||||
static char *D_dehout(void)
|
static char *D_dehout(void)
|
||||||
@ -532,7 +533,7 @@ static char title[128];
|
|||||||
// killough 11/98: remove limit on number of files
|
// killough 11/98: remove limit on number of files
|
||||||
//
|
//
|
||||||
|
|
||||||
void D_AddFile(char *file)
|
void D_AddFile(const char *file)
|
||||||
{
|
{
|
||||||
static int numwadfiles, numwadfiles_alloc;
|
static int numwadfiles, numwadfiles_alloc;
|
||||||
|
|
||||||
@ -630,6 +631,31 @@ char *D_DoomPrefDir(void)
|
|||||||
return dir;
|
return dir;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Calculate the path to the directory for autoloaded WADs/DEHs.
|
||||||
|
// Creates the directory as necessary.
|
||||||
|
|
||||||
|
static char *autoload_path = NULL;
|
||||||
|
|
||||||
|
static char *GetAutoloadDir(const char *iwadname)
|
||||||
|
{
|
||||||
|
char *result;
|
||||||
|
|
||||||
|
if (autoload_path == NULL)
|
||||||
|
{
|
||||||
|
char *prefdir;
|
||||||
|
prefdir = D_DoomPrefDir();
|
||||||
|
autoload_path = M_StringJoin(prefdir, DIR_SEPARATOR_S, "autoload", NULL);
|
||||||
|
(free)(prefdir);
|
||||||
|
}
|
||||||
|
|
||||||
|
M_MakeDirectory(autoload_path);
|
||||||
|
|
||||||
|
result = M_StringJoin(autoload_path, DIR_SEPARATOR_S, iwadname, NULL);
|
||||||
|
M_MakeDirectory(result);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// CheckIWAD
|
// CheckIWAD
|
||||||
//
|
//
|
||||||
@ -1362,6 +1388,28 @@ static void D_ProcessDehCommandLine(void)
|
|||||||
// ty 03/09/98 end of do dehacked stuff
|
// ty 03/09/98 end of do dehacked stuff
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Load all WAD files from the given directory.
|
||||||
|
|
||||||
|
static void AutoLoadWADs(const char *path)
|
||||||
|
{
|
||||||
|
glob_t *glob;
|
||||||
|
const char *filename;
|
||||||
|
|
||||||
|
glob = I_StartMultiGlob(path, GLOB_FLAG_NOCASE|GLOB_FLAG_SORTED,
|
||||||
|
"*.wad", "*.lmp", NULL);
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
filename = I_NextGlob(glob);
|
||||||
|
if (filename == NULL)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
D_AddFile(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
I_EndGlob(glob);
|
||||||
|
}
|
||||||
|
|
||||||
// killough 10/98: support preloaded wads
|
// killough 10/98: support preloaded wads
|
||||||
|
|
||||||
static void D_ProcessWadPreincludes(void)
|
static void D_ProcessWadPreincludes(void)
|
||||||
@ -1385,8 +1433,44 @@ static void D_ProcessWadPreincludes(void)
|
|||||||
printf("\nWarning: could not open %s\n", file);
|
printf("\nWarning: could not open %s\n", file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// auto-loading of .wad and .deh files.
|
||||||
|
{
|
||||||
|
char *autoload_dir;
|
||||||
|
|
||||||
|
// common auto-loaded files for all Doom flavors
|
||||||
|
autoload_dir = GetAutoloadDir("doom-all");
|
||||||
|
AutoLoadWADs(autoload_dir);
|
||||||
|
(free)(autoload_dir);
|
||||||
|
|
||||||
|
// auto-loaded files per IWAD
|
||||||
|
autoload_dir = GetAutoloadDir(M_BaseName(wadfiles[0]));
|
||||||
|
AutoLoadWADs(autoload_dir);
|
||||||
|
(free)(autoload_dir);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load all dehacked patches from the given directory.
|
||||||
|
|
||||||
|
static void AutoLoadPatches(const char *path)
|
||||||
|
{
|
||||||
|
const char *filename;
|
||||||
|
glob_t *glob;
|
||||||
|
|
||||||
|
glob = I_StartMultiGlob(path, GLOB_FLAG_NOCASE|GLOB_FLAG_SORTED,
|
||||||
|
"*.deh", "*.bex", NULL);
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
filename = I_NextGlob(glob);
|
||||||
|
if (filename == NULL)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ProcessDehFile(filename, D_dehout(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
I_EndGlob(glob);
|
||||||
|
}
|
||||||
|
|
||||||
// killough 10/98: support preloaded deh/bex files
|
// killough 10/98: support preloaded deh/bex files
|
||||||
|
|
||||||
@ -1417,6 +1501,20 @@ static void D_ProcessDehPreincludes(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// auto-loading of .wad and .deh files.
|
||||||
|
{
|
||||||
|
char *autoload_dir;
|
||||||
|
|
||||||
|
// common auto-loaded files for all Doom flavors
|
||||||
|
autoload_dir = GetAutoloadDir("doom-all");
|
||||||
|
AutoLoadPatches(autoload_dir);
|
||||||
|
(free)(autoload_dir);
|
||||||
|
|
||||||
|
// auto-loaded files per IWAD
|
||||||
|
autoload_dir = GetAutoloadDir(M_BaseName(wadfiles[0]));
|
||||||
|
AutoLoadPatches(autoload_dir);
|
||||||
|
(free)(autoload_dir);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@
|
|||||||
|
|
||||||
extern char **wadfiles; // killough 11/98
|
extern char **wadfiles; // killough 11/98
|
||||||
|
|
||||||
void D_AddFile(char *file);
|
void D_AddFile(const char *file);
|
||||||
|
|
||||||
char *D_DoomExeDir(void); // killough 2/16/98: path to executable's dir
|
char *D_DoomExeDir(void); // killough 2/16/98: path to executable's dir
|
||||||
char *D_DoomExeName(void); // killough 10/98: executable's name
|
char *D_DoomExeName(void); // killough 10/98: executable's name
|
||||||
|
376
Source/i_glob.c
Normal file
376
Source/i_glob.c
Normal file
@ -0,0 +1,376 @@
|
|||||||
|
//
|
||||||
|
// Copyright(C) 2018 Simon Howard
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU General Public License
|
||||||
|
// as published by the Free Software Foundation; either version 2
|
||||||
|
// of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// File globbing API. This allows the contents of the filesystem
|
||||||
|
// to be interrogated.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
#include "i_glob.h"
|
||||||
|
#include "m_misc.h"
|
||||||
|
#include "m_misc2.h"
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
// For Visual C++, we need to include the win_opendir module.
|
||||||
|
#include "win_opendir.h"
|
||||||
|
#define strcasecmp _stricmp
|
||||||
|
#define strncasecmp _strnicmp
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#define S_ISDIR(m) (((m)& S_IFMT) == S_IFDIR)
|
||||||
|
#elif defined(HAVE_DIRENT_H)
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#elif defined(__WATCOMC__)
|
||||||
|
// Watcom has the same API in a different header.
|
||||||
|
#include <direct.h>
|
||||||
|
#else
|
||||||
|
#define NO_DIRENT_IMPLEMENTATION
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef NO_DIRENT_IMPLEMENTATION
|
||||||
|
|
||||||
|
// Only the fields d_name and (as an XSI extension) d_ino are specified
|
||||||
|
// in POSIX.1. Other than Linux, the d_type field is available mainly
|
||||||
|
// only on BSD systems. The remaining fields are available on many, but
|
||||||
|
// not all systems.
|
||||||
|
static boolean IsDirectory(char *dir, struct dirent *de)
|
||||||
|
{
|
||||||
|
#if defined(_DIRENT_HAVE_D_TYPE)
|
||||||
|
if (de->d_type != DT_UNKNOWN && de->d_type != DT_LNK)
|
||||||
|
{
|
||||||
|
return de->d_type == DT_DIR;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
char *filename;
|
||||||
|
struct stat sb;
|
||||||
|
int result;
|
||||||
|
|
||||||
|
filename = M_StringJoin(dir, DIR_SEPARATOR_S, de->d_name, NULL);
|
||||||
|
result = stat(filename, &sb);
|
||||||
|
(free)(filename);
|
||||||
|
|
||||||
|
if (result != 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return S_ISDIR(sb.st_mode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct glob_s
|
||||||
|
{
|
||||||
|
char **globs;
|
||||||
|
int num_globs;
|
||||||
|
int flags;
|
||||||
|
DIR *dir;
|
||||||
|
char *directory;
|
||||||
|
char *last_filename;
|
||||||
|
// These fields are only used when the GLOB_FLAG_SORTED flag is set:
|
||||||
|
char **filenames;
|
||||||
|
int filenames_len;
|
||||||
|
int next_index;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void FreeStringList(char **globs, int num_globs)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < num_globs; ++i)
|
||||||
|
{
|
||||||
|
(free)(globs[i]);
|
||||||
|
}
|
||||||
|
free(globs);
|
||||||
|
}
|
||||||
|
|
||||||
|
glob_t *I_StartMultiGlob(const char *directory, int flags,
|
||||||
|
const char *glob, ...)
|
||||||
|
{
|
||||||
|
char **globs;
|
||||||
|
int num_globs;
|
||||||
|
glob_t *result;
|
||||||
|
va_list args;
|
||||||
|
|
||||||
|
globs = malloc(sizeof(char *));
|
||||||
|
if (globs == NULL)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
globs[0] = M_StringDuplicate(glob);
|
||||||
|
num_globs = 1;
|
||||||
|
|
||||||
|
va_start(args, glob);
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
const char *arg = va_arg(args, const char *);
|
||||||
|
char **new_globs;
|
||||||
|
|
||||||
|
if (arg == NULL)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
new_globs = realloc(globs, sizeof(char *) * (num_globs + 1));
|
||||||
|
if (new_globs == NULL)
|
||||||
|
{
|
||||||
|
FreeStringList(globs, num_globs);
|
||||||
|
}
|
||||||
|
globs = new_globs;
|
||||||
|
globs[num_globs] = M_StringDuplicate(arg);
|
||||||
|
++num_globs;
|
||||||
|
}
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
result = malloc(sizeof(glob_t));
|
||||||
|
if (result == NULL)
|
||||||
|
{
|
||||||
|
FreeStringList(globs, num_globs);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
result->dir = opendir(directory);
|
||||||
|
if (result->dir == NULL)
|
||||||
|
{
|
||||||
|
FreeStringList(globs, num_globs);
|
||||||
|
free(result);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
result->directory = M_StringDuplicate(directory);
|
||||||
|
result->globs = globs;
|
||||||
|
result->num_globs = num_globs;
|
||||||
|
result->flags = flags;
|
||||||
|
result->last_filename = NULL;
|
||||||
|
result->filenames = NULL;
|
||||||
|
result->filenames_len = 0;
|
||||||
|
result->next_index = -1;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
glob_t *I_StartGlob(const char *directory, const char *glob, int flags)
|
||||||
|
{
|
||||||
|
return I_StartMultiGlob(directory, flags, glob, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void I_EndGlob(glob_t *glob)
|
||||||
|
{
|
||||||
|
if (glob == NULL)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
FreeStringList(glob->globs, glob->num_globs);
|
||||||
|
FreeStringList(glob->filenames, glob->filenames_len);
|
||||||
|
|
||||||
|
(free)(glob->directory);
|
||||||
|
free(glob->last_filename);
|
||||||
|
(void) closedir(glob->dir);
|
||||||
|
free(glob);
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean MatchesGlob(const char *name, const char *glob, int flags)
|
||||||
|
{
|
||||||
|
int n, g;
|
||||||
|
|
||||||
|
while (*glob != '\0')
|
||||||
|
{
|
||||||
|
n = *name;
|
||||||
|
g = *glob;
|
||||||
|
|
||||||
|
if ((flags & GLOB_FLAG_NOCASE) != 0)
|
||||||
|
{
|
||||||
|
n = tolower(n);
|
||||||
|
g = tolower(g);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g == '*')
|
||||||
|
{
|
||||||
|
// To handle *-matching we skip past the * and recurse
|
||||||
|
// to check each subsequent character in turn. If none
|
||||||
|
// match then the whole match is a failure.
|
||||||
|
while (*name != '\0')
|
||||||
|
{
|
||||||
|
if (MatchesGlob(name, glob + 1, flags))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
++name;
|
||||||
|
}
|
||||||
|
return glob[1] == '\0';
|
||||||
|
}
|
||||||
|
else if (g != '?' && n != g)
|
||||||
|
{
|
||||||
|
// For normal characters the name must match the glob,
|
||||||
|
// but for ? we don't care what the character is.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
++name;
|
||||||
|
++glob;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match successful when glob and name end at the same time.
|
||||||
|
return *name == '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean MatchesAnyGlob(const char *name, glob_t *glob)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < glob->num_globs; ++i)
|
||||||
|
{
|
||||||
|
if (MatchesGlob(name, glob->globs[i], glob->flags))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *NextGlob(glob_t *glob)
|
||||||
|
{
|
||||||
|
struct dirent *de;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
de = readdir(glob->dir);
|
||||||
|
if (de == NULL)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
} while (IsDirectory(glob->directory, de)
|
||||||
|
|| !MatchesAnyGlob(de->d_name, glob));
|
||||||
|
|
||||||
|
// Return the fully-qualified path, not just the bare filename.
|
||||||
|
return M_StringJoin(glob->directory, DIR_SEPARATOR_S, de->d_name, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ReadAllFilenames(glob_t *glob)
|
||||||
|
{
|
||||||
|
char *name;
|
||||||
|
|
||||||
|
glob->filenames = NULL;
|
||||||
|
glob->filenames_len = 0;
|
||||||
|
glob->next_index = 0;
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
name = NextGlob(glob);
|
||||||
|
if (name == NULL)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
glob->filenames = realloc(glob->filenames,
|
||||||
|
(glob->filenames_len + 1) * sizeof(char *));
|
||||||
|
glob->filenames[glob->filenames_len] = name;
|
||||||
|
++glob->filenames_len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void SortFilenames(char **filenames, int len, int flags)
|
||||||
|
{
|
||||||
|
char *pivot, *tmp;
|
||||||
|
int i, left_len, cmp;
|
||||||
|
|
||||||
|
if (len <= 1)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pivot = filenames[len - 1];
|
||||||
|
left_len = 0;
|
||||||
|
for (i = 0; i < len-1; ++i)
|
||||||
|
{
|
||||||
|
if ((flags & GLOB_FLAG_NOCASE) != 0)
|
||||||
|
{
|
||||||
|
cmp = strcasecmp(filenames[i], pivot);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cmp = strcmp(filenames[i], pivot);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cmp < 0)
|
||||||
|
{
|
||||||
|
tmp = filenames[i];
|
||||||
|
filenames[i] = filenames[left_len];
|
||||||
|
filenames[left_len] = tmp;
|
||||||
|
++left_len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
filenames[len - 1] = filenames[left_len];
|
||||||
|
filenames[left_len] = pivot;
|
||||||
|
|
||||||
|
SortFilenames(filenames, left_len, flags);
|
||||||
|
SortFilenames(&filenames[left_len + 1], len - left_len - 1, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *I_NextGlob(glob_t *glob)
|
||||||
|
{
|
||||||
|
const char *result;
|
||||||
|
|
||||||
|
if (glob == NULL)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// In unsorted mode we just return the filenames as we read
|
||||||
|
// them back from the system API.
|
||||||
|
if ((glob->flags & GLOB_FLAG_SORTED) == 0)
|
||||||
|
{
|
||||||
|
free(glob->last_filename);
|
||||||
|
glob->last_filename = NextGlob(glob);
|
||||||
|
return glob->last_filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
// In sorted mode we read the whole list of filenames into memory,
|
||||||
|
// sort them and return them one at a time.
|
||||||
|
if (glob->next_index < 0)
|
||||||
|
{
|
||||||
|
ReadAllFilenames(glob);
|
||||||
|
SortFilenames(glob->filenames, glob->filenames_len, glob->flags);
|
||||||
|
}
|
||||||
|
if (glob->next_index >= glob->filenames_len)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
result = glob->filenames[glob->next_index];
|
||||||
|
++glob->next_index;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else /* #ifdef NO_DIRENT_IMPLEMENTATION */
|
||||||
|
|
||||||
|
#warning No native implementation of file globbing.
|
||||||
|
|
||||||
|
glob_t *I_StartGlob(const char *directory, const char *glob, int flags)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void I_EndGlob(glob_t *glob)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *I_NextGlob(glob_t *glob)
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* #ifdef NO_DIRENT_IMPLEMENTATION */
|
||||||
|
|
44
Source/i_glob.h
Normal file
44
Source/i_glob.h
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
//
|
||||||
|
// Copyright(C) 2018 Simon Howard
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU General Public License
|
||||||
|
// as published by the Free Software Foundation; either version 2
|
||||||
|
// of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// DESCRIPTION:
|
||||||
|
// System specific file globbing interface.
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef __I_GLOB__
|
||||||
|
#define __I_GLOB__
|
||||||
|
|
||||||
|
#define GLOB_FLAG_NOCASE 0x01
|
||||||
|
#define GLOB_FLAG_SORTED 0x02
|
||||||
|
|
||||||
|
typedef struct glob_s glob_t;
|
||||||
|
|
||||||
|
// Start reading a list of file paths from the given directory which match
|
||||||
|
// the given glob pattern. I_EndGlob() must be called on completion.
|
||||||
|
glob_t *I_StartGlob(const char *directory, const char *glob, int flags);
|
||||||
|
|
||||||
|
// Same as I_StartGlob but multiple glob patterns can be provided. The list
|
||||||
|
// of patterns must be terminated with NULL.
|
||||||
|
glob_t *I_StartMultiGlob(const char *directory, int flags,
|
||||||
|
const char *glob, ...);
|
||||||
|
|
||||||
|
// Finish reading file list.
|
||||||
|
void I_EndGlob(glob_t *glob);
|
||||||
|
|
||||||
|
// Read the name of the next globbed filename. NULL is returned if there
|
||||||
|
// are no more found.
|
||||||
|
const char *I_NextGlob(glob_t *glob);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
336
Source/win_opendir.c
Normal file
336
Source/win_opendir.c
Normal file
@ -0,0 +1,336 @@
|
|||||||
|
//
|
||||||
|
// 03/10/2006 James Haley
|
||||||
|
//
|
||||||
|
// For this module only:
|
||||||
|
// This code is public domain. No change sufficient enough to constitute a
|
||||||
|
// significant or original work has been made, and thus it remains as such.
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// DESCRIPTION:
|
||||||
|
//
|
||||||
|
// Implementation of POSIX opendir for Visual C++.
|
||||||
|
// Derived from the MinGW C Library Extensions Source (released to the
|
||||||
|
// public domain). As with other Win32 modules, don't include most DOOM
|
||||||
|
// headers into this or conflicts will occur.
|
||||||
|
//
|
||||||
|
// Original Header:
|
||||||
|
//
|
||||||
|
// * dirent.c
|
||||||
|
// * This file has no copyright assigned and is placed in the Public Domain.
|
||||||
|
// * This file is a part of the mingw-runtime package.
|
||||||
|
// * No warranty is given; refer to the file DISCLAIMER within the package.
|
||||||
|
// *
|
||||||
|
// * Derived from DIRLIB.C by Matt J. Weinstein
|
||||||
|
// * This note appears in the DIRLIB.H
|
||||||
|
// * DIRLIB.H by M. J. Weinstein Released to public domain 1-Jan-89
|
||||||
|
// *
|
||||||
|
// * Updated by Jeremy Bettis <jeremy@hksys.com>
|
||||||
|
// * Significantly revised and rewinddir, seekdir and telldir added by Colin
|
||||||
|
// * Peters <colin@fu.is.saga-u.ac.jp>
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef _MSC_VER
|
||||||
|
#error i_opndir.c is for Microsoft Visual C++ only
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#include <windows.h> /* for GetFileAttributes */
|
||||||
|
|
||||||
|
#include <tchar.h>
|
||||||
|
#define SUFFIX _T("*")
|
||||||
|
#define SLASH _T("\\")
|
||||||
|
|
||||||
|
#include "win_opendir.h"
|
||||||
|
|
||||||
|
//
|
||||||
|
// opendir
|
||||||
|
//
|
||||||
|
// Returns a pointer to a DIR structure appropriately filled in to begin
|
||||||
|
// searching a directory.
|
||||||
|
//
|
||||||
|
DIR *opendir(const _TCHAR *szPath)
|
||||||
|
{
|
||||||
|
DIR *nd;
|
||||||
|
unsigned int rc;
|
||||||
|
_TCHAR szFullPath[MAX_PATH];
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
|
||||||
|
if(!szPath)
|
||||||
|
{
|
||||||
|
errno = EFAULT;
|
||||||
|
return (DIR *)0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(szPath[0] == _T('\0'))
|
||||||
|
{
|
||||||
|
errno = ENOTDIR;
|
||||||
|
return (DIR *)0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Attempt to determine if the given path really is a directory. */
|
||||||
|
rc = GetFileAttributes(szPath);
|
||||||
|
if(rc == (unsigned int)-1)
|
||||||
|
{
|
||||||
|
/* call GetLastError for more error info */
|
||||||
|
errno = ENOENT;
|
||||||
|
return (DIR *)0;
|
||||||
|
}
|
||||||
|
if(!(rc & FILE_ATTRIBUTE_DIRECTORY))
|
||||||
|
{
|
||||||
|
/* Error, entry exists but not a directory. */
|
||||||
|
errno = ENOTDIR;
|
||||||
|
return (DIR *)0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make an absolute pathname. */
|
||||||
|
_tfullpath(szFullPath, szPath, MAX_PATH);
|
||||||
|
|
||||||
|
/* Allocate enough space to store DIR structure and the complete
|
||||||
|
* directory path given. */
|
||||||
|
nd = (DIR *)(malloc(sizeof(DIR) + (_tcslen(szFullPath)
|
||||||
|
+ _tcslen(SLASH)
|
||||||
|
+ _tcslen(SUFFIX) + 1)
|
||||||
|
* sizeof(_TCHAR)));
|
||||||
|
|
||||||
|
if(!nd)
|
||||||
|
{
|
||||||
|
/* Error, out of memory. */
|
||||||
|
errno = ENOMEM;
|
||||||
|
return (DIR *)0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create the search expression. */
|
||||||
|
_tcscpy(nd->dd_name, szFullPath);
|
||||||
|
|
||||||
|
/* Add on a slash if the path does not end with one. */
|
||||||
|
if(nd->dd_name[0] != _T('\0')
|
||||||
|
&& _tcsrchr(nd->dd_name, _T('/')) != nd->dd_name
|
||||||
|
+ _tcslen(nd->dd_name) - 1
|
||||||
|
&& _tcsrchr(nd->dd_name, _T('\\')) != nd->dd_name
|
||||||
|
+ _tcslen(nd->dd_name) - 1)
|
||||||
|
{
|
||||||
|
_tcscat(nd->dd_name, SLASH);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add on the search pattern */
|
||||||
|
_tcscat(nd->dd_name, SUFFIX);
|
||||||
|
|
||||||
|
/* Initialize handle to -1 so that a premature closedir doesn't try
|
||||||
|
* to call _findclose on it. */
|
||||||
|
nd->dd_handle = -1;
|
||||||
|
|
||||||
|
/* Initialize the status. */
|
||||||
|
nd->dd_stat = 0;
|
||||||
|
|
||||||
|
/* Initialize the dirent structure. ino and reclen are invalid under
|
||||||
|
* Win32, and name simply points at the appropriate part of the
|
||||||
|
* findfirst_t structure. */
|
||||||
|
nd->dd_dir.d_ino = 0;
|
||||||
|
nd->dd_dir.d_reclen = 0;
|
||||||
|
nd->dd_dir.d_namlen = 0;
|
||||||
|
memset(nd->dd_dir.d_name, 0, FILENAME_MAX);
|
||||||
|
|
||||||
|
return nd;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// readdir
|
||||||
|
//
|
||||||
|
// Return a pointer to a dirent structure filled with the information on the
|
||||||
|
// next entry in the directory.
|
||||||
|
//
|
||||||
|
struct dirent *readdir(DIR *dirp)
|
||||||
|
{
|
||||||
|
errno = 0;
|
||||||
|
|
||||||
|
/* Check for valid DIR struct. */
|
||||||
|
if(!dirp)
|
||||||
|
{
|
||||||
|
errno = EFAULT;
|
||||||
|
return (struct dirent *)0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dirp->dd_stat < 0)
|
||||||
|
{
|
||||||
|
/* We have already returned all files in the directory
|
||||||
|
* (or the structure has an invalid dd_stat). */
|
||||||
|
return (struct dirent *)0;
|
||||||
|
}
|
||||||
|
else if (dirp->dd_stat == 0)
|
||||||
|
{
|
||||||
|
/* We haven't started the search yet. */
|
||||||
|
/* Start the search */
|
||||||
|
dirp->dd_handle = _tfindfirst(dirp->dd_name, &(dirp->dd_dta));
|
||||||
|
|
||||||
|
if(dirp->dd_handle == -1)
|
||||||
|
{
|
||||||
|
/* Whoops! Seems there are no files in that
|
||||||
|
* directory. */
|
||||||
|
dirp->dd_stat = -1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dirp->dd_stat = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Get the next search entry. */
|
||||||
|
if(_tfindnext(dirp->dd_handle, &(dirp->dd_dta)))
|
||||||
|
{
|
||||||
|
/* We are off the end or otherwise error.
|
||||||
|
_findnext sets errno to ENOENT if no more file
|
||||||
|
Undo this. */
|
||||||
|
DWORD winerr = GetLastError();
|
||||||
|
if(winerr == ERROR_NO_MORE_FILES)
|
||||||
|
errno = 0;
|
||||||
|
_findclose(dirp->dd_handle);
|
||||||
|
dirp->dd_handle = -1;
|
||||||
|
dirp->dd_stat = -1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Update the status to indicate the correct
|
||||||
|
* number. */
|
||||||
|
dirp->dd_stat++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dirp->dd_stat > 0)
|
||||||
|
{
|
||||||
|
/* Successfully got an entry. Everything about the file is
|
||||||
|
* already appropriately filled in except the length of the
|
||||||
|
* file name. */
|
||||||
|
dirp->dd_dir.d_namlen = _tcslen(dirp->dd_dta.name);
|
||||||
|
_tcscpy(dirp->dd_dir.d_name, dirp->dd_dta.name);
|
||||||
|
return &dirp->dd_dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (struct dirent *)0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// closedir
|
||||||
|
//
|
||||||
|
// Frees up resources allocated by opendir.
|
||||||
|
//
|
||||||
|
int closedir(DIR *dirp)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
rc = 0;
|
||||||
|
|
||||||
|
if(!dirp)
|
||||||
|
{
|
||||||
|
errno = EFAULT;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(dirp->dd_handle != -1)
|
||||||
|
{
|
||||||
|
rc = _findclose(dirp->dd_handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Delete the dir structure. */
|
||||||
|
free(dirp);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// rewinddir
|
||||||
|
//
|
||||||
|
// Return to the beginning of the directory "stream". We simply call findclose
|
||||||
|
// and then reset things like an opendir.
|
||||||
|
//
|
||||||
|
void rewinddir(DIR * dirp)
|
||||||
|
{
|
||||||
|
errno = 0;
|
||||||
|
|
||||||
|
if(!dirp)
|
||||||
|
{
|
||||||
|
errno = EFAULT;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(dirp->dd_handle != -1)
|
||||||
|
{
|
||||||
|
_findclose(dirp->dd_handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
dirp->dd_handle = -1;
|
||||||
|
dirp->dd_stat = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// telldir
|
||||||
|
//
|
||||||
|
// Returns the "position" in the "directory stream" which can be used with
|
||||||
|
// seekdir to go back to an old entry. We simply return the value in stat.
|
||||||
|
//
|
||||||
|
long telldir(DIR *dirp)
|
||||||
|
{
|
||||||
|
errno = 0;
|
||||||
|
|
||||||
|
if(!dirp)
|
||||||
|
{
|
||||||
|
errno = EFAULT;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return dirp->dd_stat;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// seekdir
|
||||||
|
//
|
||||||
|
// Seek to an entry previously returned by telldir. We rewind the directory
|
||||||
|
// and call readdir repeatedly until either dd_stat is the position number
|
||||||
|
// or -1 (off the end). This is not perfect, in that the directory may
|
||||||
|
// have changed while we weren't looking. But that is probably the case with
|
||||||
|
// any such system.
|
||||||
|
//
|
||||||
|
void seekdir(DIR *dirp, long lPos)
|
||||||
|
{
|
||||||
|
errno = 0;
|
||||||
|
|
||||||
|
if(!dirp)
|
||||||
|
{
|
||||||
|
errno = EFAULT;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(lPos < -1)
|
||||||
|
{
|
||||||
|
/* Seeking to an invalid position. */
|
||||||
|
errno = EINVAL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if(lPos == -1)
|
||||||
|
{
|
||||||
|
/* Seek past end. */
|
||||||
|
if(dirp->dd_handle != -1)
|
||||||
|
{
|
||||||
|
_findclose(dirp->dd_handle);
|
||||||
|
}
|
||||||
|
dirp->dd_handle = -1;
|
||||||
|
dirp->dd_stat = -1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Rewind and read forward to the appropriate index. */
|
||||||
|
rewinddir(dirp);
|
||||||
|
|
||||||
|
while((dirp->dd_stat < lPos) && readdir(dirp))
|
||||||
|
; /* do-nothing loop */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// EOF
|
||||||
|
|
73
Source/win_opendir.h
Normal file
73
Source/win_opendir.h
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
//
|
||||||
|
// 03/10/2006 James Haley
|
||||||
|
//
|
||||||
|
// For this module only:
|
||||||
|
// This code is public domain. No change sufficient enough to constitute a
|
||||||
|
// significant or original work has been made, and thus it remains as such.
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// DESCRIPTION:
|
||||||
|
//
|
||||||
|
// Implementation of POSIX opendir for Visual C++.
|
||||||
|
// Derived from the MinGW C Library Extensions Source (released to the
|
||||||
|
// public domain).
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef I_OPNDIR_H__
|
||||||
|
#define I_OPNDIR_H__
|
||||||
|
|
||||||
|
#include <io.h>
|
||||||
|
|
||||||
|
#ifndef FILENAME_MAX
|
||||||
|
#define FILENAME_MAX 260
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct dirent
|
||||||
|
{
|
||||||
|
long d_ino; /* Always zero. */
|
||||||
|
unsigned short d_reclen; /* Always zero. */
|
||||||
|
unsigned short d_namlen; /* Length of name in d_name. */
|
||||||
|
char d_name[FILENAME_MAX]; /* File name. */
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is an internal data structure. Good programmers will not use it
|
||||||
|
* except as an argument to one of the functions below.
|
||||||
|
* dd_stat field is now int (was short in older versions).
|
||||||
|
*/
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
/* disk transfer area for this dir */
|
||||||
|
struct _finddata_t dd_dta;
|
||||||
|
|
||||||
|
/* dirent struct to return from dir (NOTE: this makes this thread
|
||||||
|
* safe as long as only one thread uses a particular DIR struct at
|
||||||
|
* a time) */
|
||||||
|
struct dirent dd_dir;
|
||||||
|
|
||||||
|
/* _findnext handle */
|
||||||
|
intptr_t dd_handle;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Status of search:
|
||||||
|
* 0 = not started yet (next entry to read is first entry)
|
||||||
|
* -1 = off the end
|
||||||
|
* positive = 0 based index of next entry
|
||||||
|
*/
|
||||||
|
int dd_stat;
|
||||||
|
|
||||||
|
/* given path for dir with search pattern (struct is extended) */
|
||||||
|
char dd_name[1];
|
||||||
|
} DIR;
|
||||||
|
|
||||||
|
DIR *opendir(const char *);
|
||||||
|
struct dirent *readdir(DIR *);
|
||||||
|
int closedir(DIR *);
|
||||||
|
void rewinddir(DIR *);
|
||||||
|
long telldir(DIR *);
|
||||||
|
void seekdir(DIR *, long);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// EOF
|
||||||
|
|
@ -2,3 +2,4 @@
|
|||||||
#cmakedefine PROJECT_STRING "@PROJECT_STRING@"
|
#cmakedefine PROJECT_STRING "@PROJECT_STRING@"
|
||||||
#cmakedefine PROJECT_TARNAME "@PROJECT_TARNAME@"
|
#cmakedefine PROJECT_TARNAME "@PROJECT_TARNAME@"
|
||||||
#cmakedefine m_FOUND
|
#cmakedefine m_FOUND
|
||||||
|
#cmakedefine HAVE_DIRENT_H
|
||||||
|
Loading…
x
Reference in New Issue
Block a user