2
0
mirror of https://gitlab.isc.org/isc-projects/bind9 synced 2025-08-26 03:58:15 +00:00
bind/lib/isc/glob.c

244 lines
5.3 KiB
C
Raw Normal View History

/*
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* See the COPYRIGHT file distributed with this work for additional
* information regarding copyright ownership.
*/
2020-02-25 09:07:45 +01:00
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <isc/errno.h>
#include <isc/glob.h>
#include <isc/print.h>
#include <isc/result.h>
#include <isc/types.h>
#include <isc/util.h>
#if HAVE_GLOB_H
#include <glob.h>
#elif defined(_WIN32)
#include <stdlib.h>
#include <windows.h>
2020-02-25 09:07:45 +01:00
#include <isc/list.h>
#define GLOB_ERR 0x0004 /* Return on error. */
#define GLOB_NOSPACE (-1)
#define GLOB_NOMATCH (-3)
/* custom glob implementation for windows */
static int
glob(const char *pattern, int flags, void *unused, glob_t *pglob);
static void
globfree(glob_t *pglob);
#else
#error "Required header missing: glob.h"
#endif
isc_result_t
isc_glob(const char *pattern, glob_t *pglob) {
REQUIRE(pattern != NULL);
REQUIRE(*pattern != '\0');
REQUIRE(pglob != NULL);
int rc = glob(pattern, GLOB_ERR, NULL, pglob);
switch (rc) {
2020-02-25 09:07:45 +01:00
case 0:
return (ISC_R_SUCCESS);
2020-02-25 09:07:45 +01:00
case GLOB_NOMATCH:
return (ISC_R_FILENOTFOUND);
2020-02-25 09:07:45 +01:00
case GLOB_NOSPACE:
return (ISC_R_NOMEMORY);
2020-02-25 09:07:45 +01:00
default:
return (errno != 0 ? isc_errno_toresult(errno) : ISC_R_IOERROR);
}
}
void
isc_globfree(glob_t *pglob) {
REQUIRE(pglob != NULL);
globfree(pglob);
}
#if defined(_WIN32)
typedef struct file_path file_path_t;
struct file_path {
char *path;
ISC_LINK(file_path_t) link;
};
typedef ISC_LIST(file_path_t) file_list_t;
/* map a winapi error to a convenient errno code */
static int
map_error(DWORD win_err_code) {
switch (win_err_code) {
case ERROR_FILE_NOT_FOUND:
case ERROR_PATH_NOT_FOUND:
return (GLOB_NOMATCH);
case ERROR_ACCESS_DENIED:
return (EACCES);
case ERROR_NOT_ENOUGH_MEMORY:
return (GLOB_NOSPACE);
default:
return (EIO);
}
}
/* add file in directory dir, that matches glob expression
* provided in function glob(), to the linked list fl */
static void
2020-02-25 09:07:45 +01:00
append_file(isc_mem_t *mctx, file_list_t *fl, const char *dir, const char *file,
size_t full_path_len) {
file_path_t *fp = isc_mem_get(mctx, sizeof(file_path_t));
fp->path = isc_mem_get(mctx, full_path_len + 1);
_snprintf(fp->path, full_path_len + 1, "%s%s", dir, file);
ISC_LINK_INIT(fp, link);
ISC_LIST_PREPEND(*fl, fp, link);
}
/* sort files alphabetically case insensitive on windows */
static int
path_cmp(const void *path1, const void *path2) {
return _stricmp((const char *)path1, (const char *)path2);
}
static int
glob(const char *pattern, int flags, void *unused, glob_t *pglob) {
char path[MAX_PATH];
2020-02-25 09:07:45 +01:00
WIN32_FIND_DATAA find_data;
int ec;
HANDLE hnd;
REQUIRE(pattern != NULL);
REQUIRE(pglob != NULL);
UNUSED(flags);
UNUSED(unused);
pglob->mctx = NULL;
pglob->gl_pathc = 0;
pglob->gl_pathv = NULL;
hnd = FindFirstFileA(pattern, &find_data);
if (hnd == INVALID_HANDLE_VALUE) {
return (map_error(GetLastError()));
}
path[MAX_PATH - 1] = 0;
strncpy(path, pattern, MAX_PATH);
if (path[MAX_PATH - 1] != 0) {
errno = ENAMETOOLONG;
goto fail;
}
// strip filename from path.
size_t dir_len = strlen(path);
2020-02-25 09:07:45 +01:00
while (dir_len > 0 && path[dir_len - 1] != '/' &&
path[dir_len - 1] != '\\') {
dir_len--;
}
path[dir_len] = '\0';
isc_mem_create(&pglob->mctx);
pglob->reserved = isc_mem_get(pglob->mctx, sizeof(file_list_t));
ISC_LIST_INIT(*(file_list_t *)pglob->reserved);
size_t entries = 0;
do {
size_t file_len = strlen(find_data.cFileName);
size_t full_path_len = dir_len + file_len;
if (full_path_len > MAX_PATH) {
errno = ENAMETOOLONG;
goto fail;
}
2020-02-25 09:07:45 +01:00
append_file(pglob->mctx, (file_list_t *)pglob->reserved, path,
find_data.cFileName, full_path_len);
entries++;
} while (FindNextFileA(hnd, &find_data));
FindClose(hnd);
2020-02-25 09:07:45 +01:00
pglob->gl_pathv = isc_mem_get(pglob->mctx,
(entries + 1) * sizeof(char *));
pglob->gl_pathv[entries] = NULL;
pglob->gl_pathc = entries;
file_list_t *fl = (file_list_t *)pglob->reserved;
size_t e = 0;
file_path_t *fp;
2020-02-25 09:07:45 +01:00
for (fp = ISC_LIST_HEAD(*fl); fp != NULL; fp = ISC_LIST_NEXT(fp, link))
{
pglob->gl_pathv[e++] = fp->path;
}
2020-02-25 09:07:45 +01:00
qsort(pglob->gl_pathv, pglob->gl_pathc, sizeof(char *), path_cmp);
return (0);
fail:
ec = errno;
FindClose(hnd);
if (pglob->mctx) {
globfree(pglob);
}
return ec;
}
void
2020-02-25 09:07:45 +01:00
globfree(glob_t *pglob) {
REQUIRE(pglob != NULL);
REQUIRE(pglob->mctx != NULL);
/* first free memory used by char ** gl_pathv */
if (pglob->gl_pathv) {
2020-02-25 09:07:45 +01:00
isc_mem_put(pglob->mctx, pglob->gl_pathv,
(pglob->gl_pathc + 1) * sizeof(char *));
pglob->gl_pathv = NULL;
}
file_list_t *fl = (file_list_t *)pglob->reserved;
file_path_t *p, *next;
/* next free each individual file path string + nodes in list */
for (p = ISC_LIST_HEAD(*fl); p != NULL; p = next) {
next = ISC_LIST_NEXT(p, link);
isc_mem_put(pglob->mctx, p->path, strlen(p->path) + 1);
isc_mem_put(pglob->mctx, p, sizeof(file_path_t));
}
/* free linked list of files */
isc_mem_put(pglob->mctx, pglob->reserved, sizeof(file_list_t));
pglob->reserved = NULL;
pglob->gl_pathc = 0;
pglob->gl_pathv = NULL;
isc_mem_destroy(&pglob->mctx);
pglob->mctx = NULL;
}
#endif /* defined(_WIN32) */