mirror of
https://gitlab.com/gnuwget/wget2.git
synced 2026-02-01 04:10:48 +00:00
371 lines
7.4 KiB
C
371 lines
7.4 KiB
C
/*
|
|
* Copyright (c) 2017-2023 Free Software Foundation, Inc.
|
|
*
|
|
* This file is part of Wget.
|
|
*
|
|
* Wget 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 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* Wget 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.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with Wget. If not, see <https://www.gnu.org/licenses/>.
|
|
*
|
|
*
|
|
* Dynamic loading abstraction for object files
|
|
*
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
#include <string.h>
|
|
#include <sys/stat.h>
|
|
#include <dirent.h>
|
|
#include <stdarg.h>
|
|
|
|
#include <wget.h>
|
|
|
|
#include "wget_main.h"
|
|
#include "wget_dl.h"
|
|
|
|
// Error reporting functions
|
|
|
|
static void dl_error_set_noalloc(dl_error_t *e, const char *msg)
|
|
{
|
|
if (msg && e->msg)
|
|
wget_error_printf(_("Piling up error '%s' over error '%s'"), msg, e->msg);
|
|
|
|
wget_free((void *) e->msg);
|
|
e->msg = msg;
|
|
}
|
|
|
|
// Set an error message. Call with msg=NULL to clear error.
|
|
void dl_error_set(dl_error_t *e, const char *msg)
|
|
{
|
|
dl_error_set_noalloc(e, wget_strdup(msg));
|
|
}
|
|
|
|
// Set an error message with printf format.
|
|
void dl_error_set_printf (dl_error_t *e, const char *format, ...)
|
|
{
|
|
va_list arglist;
|
|
|
|
va_start(arglist, format);
|
|
dl_error_set_noalloc(e, wget_vaprintf(format, arglist));
|
|
va_end(arglist);
|
|
}
|
|
|
|
#ifdef PLUGIN_SUPPORT
|
|
// If the string is not a path, converts to path by prepending "./" to it,
|
|
// else returns NULL
|
|
static char *convert_to_path_if_not(const char *str)
|
|
{
|
|
if (str && !strchr(str, '/'))
|
|
return wget_aprintf("./%s", str);
|
|
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
#if defined PLUGIN_SUPPORT_LIBDL
|
|
#include <dlfcn.h>
|
|
|
|
int dl_supported(void)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
struct dl_file_st
|
|
{
|
|
void *handle;
|
|
};
|
|
|
|
// Opens an object file
|
|
dl_file_t *dl_file_open(const char *filename, dl_error_t *e)
|
|
{
|
|
dl_file_t *dm = NULL;
|
|
dl_file_t dm_st;
|
|
char *buf = convert_to_path_if_not(filename);
|
|
|
|
dm_st.handle = dlopen(buf ? buf : filename, RTLD_LAZY | RTLD_LOCAL);
|
|
wget_xfree(buf);
|
|
|
|
if (dm_st.handle)
|
|
dm = wget_memdup(&dm_st, sizeof(dl_file_t));
|
|
else
|
|
dl_error_set(e, dlerror());
|
|
|
|
return dm;
|
|
}
|
|
|
|
void *dl_file_lookup(dl_file_t *dm, const char *symbol, dl_error_t *e)
|
|
{
|
|
void *res;
|
|
char *error;
|
|
|
|
res = dlsym(dm->handle, symbol);
|
|
error = dlerror();
|
|
if (error) {
|
|
dl_error_set(e, error);
|
|
return NULL;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
void dl_file_close(dl_file_t *dm)
|
|
{
|
|
dlclose(dm->handle);
|
|
|
|
wget_free(dm);
|
|
}
|
|
|
|
#elif defined PLUGIN_SUPPORT_WINDOWS
|
|
#include <windows.h>
|
|
|
|
int dl_supported(void)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
struct dl_file_st
|
|
{
|
|
HMODULE handle;
|
|
};
|
|
|
|
static void dl_win32_set_last_error(dl_error_t *e)
|
|
{
|
|
char *buf;
|
|
|
|
DWORD error_code = GetLastError();
|
|
|
|
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
|
|
| FORMAT_MESSAGE_IGNORE_INSERTS
|
|
| FORMAT_MESSAGE_FROM_SYSTEM,
|
|
NULL, error_code, 0,
|
|
(LPTSTR) &buf, 0, NULL);
|
|
|
|
if (buf) {
|
|
dl_error_set(e, buf);
|
|
LocalFree(buf);
|
|
} else {
|
|
dl_error_set_printf(e, "Unknown error %d", (int) error_code);
|
|
}
|
|
}
|
|
|
|
dl_file_t *dl_file_open(const char *filename, dl_error_t *e)
|
|
{
|
|
dl_file_t *dm = NULL;
|
|
dl_file_t dm_st;
|
|
char *buf = convert_to_path_if_not(filename);
|
|
|
|
dm_st.handle = LoadLibrary(buf ? buf : filename);
|
|
wget_xfree(buf);
|
|
|
|
if (dm_st.handle)
|
|
dm = wget_memdup(&dm_st, sizeof(dl_file_t));
|
|
else
|
|
dl_win32_set_last_error(e);
|
|
|
|
return dm;
|
|
}
|
|
|
|
void *dl_file_lookup(dl_file_t *dm, const char *symbol, dl_error_t *e)
|
|
{
|
|
void *res = GetProcAddress(dm->handle, symbol);
|
|
if (! res)
|
|
dl_win32_set_last_error(e);
|
|
return res;
|
|
}
|
|
|
|
void dl_file_close(dl_file_t *dm)
|
|
{
|
|
FreeLibrary(dm->handle);
|
|
wget_free(dm);
|
|
}
|
|
|
|
#else
|
|
|
|
static const char *dl_unsupported = "Dynamic loading is not supported on the current platform.";
|
|
|
|
int dl_supported(void)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
dl_file_t *dl_file_open(WGET_GCC_UNUSED const char *filename, dl_error_t *e)
|
|
{
|
|
dl_error_set(e, dl_unsupported);
|
|
return NULL;
|
|
}
|
|
|
|
void *dl_file_lookup(WGET_GCC_UNUSED dl_file_t *dm, WGET_GCC_UNUSED const char *symbol, dl_error_t *e)
|
|
{
|
|
dl_error_set(e, dl_unsupported);
|
|
return NULL;
|
|
}
|
|
|
|
void dl_file_close(WGET_GCC_UNUSED dl_file_t *dm)
|
|
{
|
|
}
|
|
|
|
#endif
|
|
|
|
typedef struct {
|
|
const char *prefix;
|
|
const char *suffix;
|
|
} object_pattern_t;
|
|
#if defined _WIN32
|
|
#define PATTERNS {"lib", ".dll"}, {"", ".dll"}
|
|
#elif defined __APPLE__
|
|
#define PATTERNS {"lib", ".so"}, {"lib", ".bundle"}, {"lib", ".dylib"}
|
|
#elif defined __CYGWIN__
|
|
#define PATTERNS {"cyg", ".dll"}
|
|
#else
|
|
#define PATTERNS {"lib", ".so"}
|
|
#endif
|
|
static const object_pattern_t dl_patterns[] = {PATTERNS, {NULL, NULL}};
|
|
#undef PATTERNS
|
|
|
|
// Matches the given path with the patterns of a loadable object file
|
|
// and returns a range to use as a name
|
|
static int dl_match(const char *path, size_t *start_out, size_t *len_out)
|
|
{
|
|
size_t i, mark;
|
|
size_t start, len;
|
|
|
|
// Strip everything but the filename
|
|
mark = 0;
|
|
for (i = 0; path[i]; i++) {
|
|
if (path[i] == '/')
|
|
mark = i + 1;
|
|
#ifdef _WIN32
|
|
if (path[i] == '\\')
|
|
mark = i + 1;
|
|
#endif // _WIN32
|
|
}
|
|
start = mark;
|
|
len = i - start;
|
|
|
|
// Match for the pattern and extract the name
|
|
for (i = 0; dl_patterns[i].prefix; i++) {
|
|
const char *p = dl_patterns[i].prefix;
|
|
const char *s = dl_patterns[i].suffix;
|
|
size_t pl = strlen(p);
|
|
size_t sl = strlen(s);
|
|
if (pl + sl >= len)
|
|
continue;
|
|
if (memcmp(path + start + len - sl, s, sl) == 0 && memcmp(path + start, p, pl) == 0) {
|
|
start += pl;
|
|
len -= (pl + sl);
|
|
break;
|
|
}
|
|
}
|
|
|
|
*start_out = start;
|
|
*len_out = len;
|
|
return dl_patterns[i].prefix ? 1 : 0;
|
|
}
|
|
|
|
static int is_regular_file(const char *filename)
|
|
{
|
|
struct stat statbuf;
|
|
|
|
if (stat(filename, &statbuf) < 0)
|
|
return 0;
|
|
if (S_ISREG(statbuf.st_mode))
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
char *dl_get_name_from_path(const char *path, int strict)
|
|
{
|
|
size_t start, len;
|
|
int match = dl_match(path, &start, &len);
|
|
|
|
if (!match && strict)
|
|
return NULL;
|
|
else
|
|
return wget_strmemdup(path + start, len);
|
|
}
|
|
|
|
char *dl_search(const char *name, const wget_vector *dirs)
|
|
{
|
|
int n_dirs = wget_vector_size(dirs);
|
|
|
|
for (int i = 0; i < n_dirs; i++) {
|
|
const char *dir = wget_vector_get(dirs, i);
|
|
if (dir && *dir) {
|
|
for (int j = 0; dl_patterns[j].prefix; j++) {
|
|
char *filename = wget_aprintf("%s/%s%s%s", dir,
|
|
dl_patterns[j].prefix, name, dl_patterns[j].suffix);
|
|
|
|
if (is_regular_file(filename))
|
|
return filename;
|
|
|
|
wget_free(filename);
|
|
}
|
|
} else {
|
|
for (int j = 0; dl_patterns[j].prefix; j++) {
|
|
char *filename = wget_aprintf("%s%s%s",
|
|
dl_patterns[j].prefix, name, dl_patterns[j].suffix);
|
|
|
|
if (is_regular_file(filename))
|
|
return filename;
|
|
|
|
wget_free(filename);
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void dl_list(const wget_vector *dirs, wget_vector *names_out)
|
|
{
|
|
int n_dirs = wget_vector_size(dirs);
|
|
|
|
for (int i = 0; i < n_dirs; i++) {
|
|
DIR *dirp;
|
|
struct dirent *ent;
|
|
const char *dir = wget_vector_get(dirs, i);
|
|
|
|
dirp = opendir(dir);
|
|
if (!dirp)
|
|
continue;
|
|
|
|
while((ent = readdir(dirp)) != NULL) {
|
|
char *fname;
|
|
char *name;
|
|
|
|
fname = ent->d_name;
|
|
|
|
// Ignore entries that don't match the pattern
|
|
name = dl_get_name_from_path(fname, 1);
|
|
if (! name)
|
|
continue;
|
|
|
|
// Ignore entries that are not regular files
|
|
{
|
|
char *sfname = wget_aprintf("%s/%s", dir, fname);
|
|
int x = is_regular_file(sfname);
|
|
wget_free(sfname);
|
|
if (!x) {
|
|
wget_free(name);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// Add to the list
|
|
wget_vector_add(names_out, name);
|
|
}
|
|
|
|
closedir(dirp);
|
|
}
|
|
}
|