enumerator: Implement globbing enumerator on Windows

We don't have glob() available there.  This replacement should work
similarly for simple cases like `include conf.d/*.conf`.

Fixes #3684.
This commit is contained in:
Tobias Brunner
2021-02-03 17:27:31 +01:00
parent 4525233b1e
commit 3de65f8d67
+139 -2
View File
@@ -27,6 +27,8 @@
#ifdef HAVE_GLOB_H
#include <glob.h>
#elif defined(WIN32)
#include <fileapi.h>
#endif /* HAVE_GLOB_H */
#include <utils/debug.h>
@@ -201,8 +203,6 @@ typedef struct {
glob_t glob;
/** iteration count */
u_int pos;
/** absolute path of current file */
char full[PATH_MAX];
} glob_enum_t;
METHOD(enumerator_t, destroy_glob_enum, void,
@@ -273,6 +273,143 @@ enumerator_t* enumerator_create_glob(const char *pattern)
return &this->public;
}
#elif defined(WIN32) /* HAVE_GLOB_H */
/**
* Enumerator implementation for glob enumerator on Windows
*/
typedef struct {
/** implements enumerator_t */
enumerator_t public;
/** search handle */
HANDLE handle;
/** current file path */
char path[PATH_MAX];
/** base path */
char *base;
} glob_enum_t;
METHOD(enumerator_t, destroy_glob_enum, void,
glob_enum_t *this)
{
if (this->handle != INVALID_HANDLE_VALUE)
{
FindClose(this->handle);
}
free(this->base);
free(this);
}
/**
* Create the combined path from the given file data
*/
static bool combine_glob_path(glob_enum_t *this, WIN32_FIND_DATA *data)
{
if (snprintf(this->path, sizeof(this->path), "%s%s%s", this->base,
DIRECTORY_SEPARATOR, data->cFileName) >= sizeof(this->path))
{
DBG1(DBG_LIB, "path for '%s' too long, ignored", data->cFileName);
return FALSE;
}
return TRUE;
}
/**
* Return the path and stat data for the current file
*/
static bool enumerate_glob_enum_data(glob_enum_t *this, va_list args)
{
struct stat *st;
char **file;
VA_ARGS_VGET(args, file, st);
if (file)
{
*file = this->path;
}
if (st && stat(this->path, st))
{
DBG1(DBG_LIB, "stat() on '%s' failed: %s", this->path,
strerror(errno));
return FALSE;
}
return TRUE;
}
METHOD(enumerator_t, enumerate_glob_enum, bool,
glob_enum_t *this, va_list args)
{
WIN32_FIND_DATA data;
do
{
if (!FindNextFile(this->handle, &data))
{
return FALSE;
}
}
while (!combine_glob_path(this, &data));
return enumerate_glob_enum_data(this, args);
}
METHOD(enumerator_t, enumerate_glob_enum_first, bool,
glob_enum_t *this, va_list args)
{
if (enumerate_glob_enum_data(this, args))
{
this->public.venumerate = _enumerate_glob_enum;
return TRUE;
}
return FALSE;
}
/*
* Described in header
*/
enumerator_t *enumerator_create_glob(const char *pattern)
{
glob_enum_t *this;
WIN32_FIND_DATA data;
if (!pattern)
{
return enumerator_create_empty();
}
INIT(this,
.public = {
.enumerate = enumerator_enumerate_default,
.venumerate = _enumerate_glob_enum_first,
.destroy = _destroy_glob_enum,
},
.base = path_dirname(pattern),
);
this->handle = FindFirstFile(pattern, &data);
if (this->handle == INVALID_HANDLE_VALUE)
{
if (GetLastError() == ERROR_FILE_NOT_FOUND ||
GetLastError() == ERROR_PATH_NOT_FOUND)
{
DBG1(DBG_LIB, "no files found matching '%s'", pattern);
}
else
{
DBG1(DBG_LIB, "FindFirstFile failed for pattern '%s' (%d)", pattern,
GetLastError());
}
destroy_glob_enum(this);
return enumerator_create_empty();
}
else if (!combine_glob_path(this, &data))
{ /* check the next file if we can't combine the path for the first one */
this->public.venumerate = _enumerate_glob_enum;
}
return &this->public;
}
#else /* HAVE_GLOB_H */
enumerator_t* enumerator_create_glob(const char *pattern)