mirror of
git://sourceware.org/git/newlib-cygwin.git
synced 2025-02-18 23:12:15 +08:00
dlopen: switch to new pathfinder class
Instead of find_exec, without changing behaviour use new pathfinder class with new allocator_interface around tmp_pathbuf and new vstrlist class. * pathfinder.h (pathfinder): New file. * vstrlist.h (allocator_interface, allocated_type, vstrlist): New file. * dlfcn.cc (dlopen): Avoid redundant GetModuleHandleExW with RTLD_NOLOAD and RTLD_NODELETE. Switch to new pathfinder class, using (tmp_pathbuf_allocator): New class. (get_full_path_of_dll): Drop.
This commit is contained in:
parent
091a0ac120
commit
97974e3076
@ -20,6 +20,74 @@ details. */
|
|||||||
#include "cygtls.h"
|
#include "cygtls.h"
|
||||||
#include "tls_pbuf.h"
|
#include "tls_pbuf.h"
|
||||||
#include "ntdll.h"
|
#include "ntdll.h"
|
||||||
|
#include "pathfinder.h"
|
||||||
|
|
||||||
|
/* Dumb allocator using memory from tmp_pathbuf.w_get ().
|
||||||
|
|
||||||
|
Does not reuse free'd memory areas. Instead, memory
|
||||||
|
is released when the tmp_pathbuf goes out of scope.
|
||||||
|
|
||||||
|
ATTENTION: Requesting memory from an instance of tmp_pathbuf breaks
|
||||||
|
when another instance on a newer stack frame has provided memory. */
|
||||||
|
class tmp_pathbuf_allocator
|
||||||
|
: public allocator_interface
|
||||||
|
{
|
||||||
|
tmp_pathbuf & tp_;
|
||||||
|
union
|
||||||
|
{
|
||||||
|
PWCHAR wideptr;
|
||||||
|
void * voidptr;
|
||||||
|
char * byteptr;
|
||||||
|
} freemem_;
|
||||||
|
size_t freesize_;
|
||||||
|
|
||||||
|
/* allocator_interface */
|
||||||
|
virtual void * alloc (size_t need)
|
||||||
|
{
|
||||||
|
if (need == 0)
|
||||||
|
need = 1; /* GNU-ish */
|
||||||
|
size_t chunksize = NT_MAX_PATH * sizeof (WCHAR);
|
||||||
|
if (need > chunksize)
|
||||||
|
api_fatal ("temporary buffer too small for %d bytes", need);
|
||||||
|
if (need > freesize_)
|
||||||
|
{
|
||||||
|
/* skip remaining, use next chunk */
|
||||||
|
freemem_.wideptr = tp_.w_get ();
|
||||||
|
freesize_ = chunksize;
|
||||||
|
}
|
||||||
|
|
||||||
|
void * ret = freemem_.voidptr;
|
||||||
|
|
||||||
|
/* adjust remaining, aligned at 8 byte boundary */
|
||||||
|
need = need + 7 - (need - 1) % 8;
|
||||||
|
freemem_.byteptr += need;
|
||||||
|
if (need > freesize_)
|
||||||
|
freesize_ = 0;
|
||||||
|
else
|
||||||
|
freesize_ -= need;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* allocator_interface */
|
||||||
|
virtual void free (void *)
|
||||||
|
{
|
||||||
|
/* no-op: released by tmp_pathbuf at end of scope */
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp_pathbuf_allocator ();
|
||||||
|
tmp_pathbuf_allocator (tmp_pathbuf_allocator const &);
|
||||||
|
tmp_pathbuf_allocator & operator = (tmp_pathbuf_allocator const &);
|
||||||
|
|
||||||
|
public:
|
||||||
|
/* use tmp_pathbuf of current stack frame */
|
||||||
|
tmp_pathbuf_allocator (tmp_pathbuf & tp)
|
||||||
|
: allocator_interface ()
|
||||||
|
, tp_ (tp)
|
||||||
|
, freemem_ ()
|
||||||
|
, freesize_ (0)
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
static void
|
static void
|
||||||
set_dl_error (const char *str)
|
set_dl_error (const char *str)
|
||||||
@ -28,84 +96,61 @@ set_dl_error (const char *str)
|
|||||||
_my_tls.locals.dl_error = 1;
|
_my_tls.locals.dl_error = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Look for an executable file given the name and the environment
|
/* Identify basename and baselen within name,
|
||||||
variable to use for searching (eg., PATH); returns the full
|
return true if there is a dir in name. */
|
||||||
pathname (static buffer) if found or NULL if not. */
|
|
||||||
inline const char *
|
|
||||||
check_path_access (const char *mywinenv, const char *name, path_conv& buf)
|
|
||||||
{
|
|
||||||
return find_exec (name, buf, mywinenv, FE_NNF | FE_DLL);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Search LD_LIBRARY_PATH for dll, if it exists. Search /usr/bin and /usr/lib
|
|
||||||
by default. Return valid full path in path_conv real_filename. */
|
|
||||||
static inline bool
|
|
||||||
gfpod_helper (const char *name, path_conv &real_filename)
|
|
||||||
{
|
|
||||||
if (strchr (name, '/'))
|
|
||||||
real_filename.check (name, PC_SYM_FOLLOW | PC_NULLEMPTY);
|
|
||||||
else if (!check_path_access ("LD_LIBRARY_PATH", name, real_filename))
|
|
||||||
check_path_access ("/usr/bin:/usr/lib", name, real_filename);
|
|
||||||
if (!real_filename.exists ())
|
|
||||||
real_filename.error = ENOENT;
|
|
||||||
return !real_filename.error;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
get_full_path_of_dll (const char* str, path_conv &real_filename)
|
spot_basename (const char * &basename, int &baselen, const char * name)
|
||||||
{
|
{
|
||||||
int len = strlen (str);
|
basename = strrchr (name, '/');
|
||||||
|
|
||||||
/* empty string? */
|
|
||||||
if (len == 0)
|
|
||||||
{
|
|
||||||
set_errno (EINVAL);
|
|
||||||
return false; /* Yes. Let caller deal with it. */
|
|
||||||
}
|
|
||||||
|
|
||||||
tmp_pathbuf tp;
|
|
||||||
char *name = tp.c_get ();
|
|
||||||
|
|
||||||
strcpy (name, str); /* Put it somewhere where we can manipulate it. */
|
|
||||||
|
|
||||||
char *basename = strrchr (name, '/');
|
|
||||||
basename = basename ? basename + 1 : name;
|
basename = basename ? basename + 1 : name;
|
||||||
char *suffix = strrchr (name, '.');
|
baselen = name + strlen (name) - basename;
|
||||||
if (suffix && suffix < basename)
|
return basename > name;
|
||||||
suffix = NULL;
|
}
|
||||||
|
|
||||||
/* Is suffix ".so"? */
|
/* Setup basenames using basename and baselen,
|
||||||
if (suffix && !strcmp (suffix, ".so"))
|
return true if basenames do have some suffix. */
|
||||||
|
static void
|
||||||
|
collect_basenames (pathfinder::basenamelist & basenames,
|
||||||
|
const char * basename, int baselen)
|
||||||
|
{
|
||||||
|
/* like strrchr (basename, '.'), but limited to baselen */
|
||||||
|
const char *suffix = basename + baselen;
|
||||||
|
while (--suffix >= basename)
|
||||||
|
if (*suffix == '.')
|
||||||
|
break;
|
||||||
|
|
||||||
|
int suffixlen;
|
||||||
|
if (suffix >= basename)
|
||||||
|
suffixlen = basename + baselen - suffix;
|
||||||
|
else
|
||||||
{
|
{
|
||||||
/* Does the file exist? */
|
suffixlen = 0;
|
||||||
if (gfpod_helper (name, real_filename))
|
suffix = NULL;
|
||||||
return true;
|
|
||||||
/* No, replace ".so" with ".dll". */
|
|
||||||
strcpy (suffix, ".dll");
|
|
||||||
}
|
}
|
||||||
/* Does the filename start with "lib"? */
|
|
||||||
|
char const * ext = "";
|
||||||
|
/* Without some suffix, reserve space for a trailing dot to override
|
||||||
|
GetModuleHandleExA's automatic adding of the ".dll" suffix. */
|
||||||
|
int extlen = suffix ? 0 : 1;
|
||||||
|
|
||||||
|
/* If we have the ".so" suffix, ... */
|
||||||
|
if (suffixlen == 3 && !strncmp (suffix, ".so", 3))
|
||||||
|
{
|
||||||
|
/* ... keep the basename with original suffix, before ... */
|
||||||
|
basenames.appendv (basename, baselen, NULL);
|
||||||
|
/* ... replacing the ".so" with the ".dll" suffix. */
|
||||||
|
baselen -= 3;
|
||||||
|
ext = ".dll";
|
||||||
|
extlen = 4;
|
||||||
|
}
|
||||||
|
/* If the basename starts with "lib", ... */
|
||||||
if (!strncmp (basename, "lib", 3))
|
if (!strncmp (basename, "lib", 3))
|
||||||
{
|
{
|
||||||
/* Yes, replace "lib" with "cyg". */
|
/* ... replace "lib" with "cyg", before ... */
|
||||||
strncpy (basename, "cyg", 3);
|
basenames.appendv ("cyg", 3, basename+3, baselen-3, ext, extlen, NULL);
|
||||||
/* Does the file exist? */
|
|
||||||
if (gfpod_helper (name, real_filename))
|
|
||||||
return true;
|
|
||||||
/* No, revert back to "lib". */
|
|
||||||
strncpy (basename, "lib", 3);
|
|
||||||
}
|
}
|
||||||
if (gfpod_helper (name, real_filename))
|
/* ... using original basename with new suffix. */
|
||||||
return true;
|
basenames.appendv (basename, baselen, ext, extlen, NULL);
|
||||||
|
|
||||||
/* If nothing worked, create a relative path from the original incoming
|
|
||||||
filename and let LoadLibrary search for it using the system default
|
|
||||||
DLL search path. */
|
|
||||||
real_filename.check (str, PC_SYM_FOLLOW | PC_NOFULL | PC_NULLEMPTY);
|
|
||||||
if (!real_filename.error)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
set_errno (real_filename.error);
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void *
|
extern "C" void *
|
||||||
@ -113,64 +158,111 @@ dlopen (const char *name, int flags)
|
|||||||
{
|
{
|
||||||
void *ret = NULL;
|
void *ret = NULL;
|
||||||
|
|
||||||
if (name == NULL)
|
do
|
||||||
{
|
{
|
||||||
ret = (void *) GetModuleHandle (NULL); /* handle for the current module */
|
if (name == NULL || *name == '\0')
|
||||||
|
{
|
||||||
|
ret = (void *) GetModuleHandle (NULL); /* handle for the current module */
|
||||||
|
if (!ret)
|
||||||
|
__seterrno ();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD gmheflags = (flags & RTLD_NODELETE)
|
||||||
|
? GET_MODULE_HANDLE_EX_FLAG_PIN
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
tmp_pathbuf tp; /* single one per stack frame */
|
||||||
|
tmp_pathbuf_allocator allocator (tp);
|
||||||
|
pathfinder::basenamelist basenames (allocator);
|
||||||
|
|
||||||
|
const char *basename;
|
||||||
|
int baselen;
|
||||||
|
bool have_dir = spot_basename (basename, baselen, name);
|
||||||
|
collect_basenames (basenames, basename, baselen);
|
||||||
|
|
||||||
|
/* handle for the named library */
|
||||||
|
path_conv real_filename;
|
||||||
|
wchar_t *wpath = tp.w_get ();
|
||||||
|
|
||||||
|
pathfinder finder (allocator, basenames); /* eats basenames */
|
||||||
|
|
||||||
|
if (have_dir)
|
||||||
|
{
|
||||||
|
/* search the specified dir */
|
||||||
|
finder.add_searchdir (name, basename - 1 - name);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* NOTE: The Windows loader (for linked dlls) does
|
||||||
|
not use the LD_LIBRARY_PATH environment variable. */
|
||||||
|
finder.add_envsearchpath ("LD_LIBRARY_PATH");
|
||||||
|
|
||||||
|
/* Finally we better have some fallback. */
|
||||||
|
finder.add_searchdir ("/usr/bin", 8);
|
||||||
|
finder.add_searchdir ("/usr/lib", 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* now search the file system */
|
||||||
|
if (!finder.find (pathfinder::
|
||||||
|
exists_and_not_dir (real_filename,
|
||||||
|
PC_SYM_FOLLOW | PC_POSIX)))
|
||||||
|
{
|
||||||
|
/* If nothing worked, create a relative path from the original
|
||||||
|
incoming filename and let LoadLibrary search for it using the
|
||||||
|
system default DLL search path. */
|
||||||
|
real_filename.check (name, PC_SYM_FOLLOW | PC_NOFULL | PC_NULLEMPTY);
|
||||||
|
if (real_filename.error)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
real_filename.get_wide_win32_path (wpath);
|
||||||
|
/* Check if the last path component contains a dot. If so,
|
||||||
|
leave the filename alone. Otherwise add a trailing dot
|
||||||
|
to override LoadLibrary's automatic adding of a ".dll" suffix. */
|
||||||
|
wchar_t *last_bs = wcsrchr (wpath, L'\\') ?: wpath;
|
||||||
|
if (last_bs && !wcschr (last_bs, L'.'))
|
||||||
|
wcscat (last_bs, L".");
|
||||||
|
|
||||||
|
if (flags & RTLD_NOLOAD)
|
||||||
|
{
|
||||||
|
GetModuleHandleExW (gmheflags, wpath, (HMODULE *) &ret);
|
||||||
|
if (ret)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef __x86_64__
|
||||||
|
/* Workaround for broken DLLs built against Cygwin versions 1.7.0-49
|
||||||
|
up to 1.7.0-57. They override the cxx_malloc pointer in their
|
||||||
|
DLL initialization code even if loaded dynamically. This is a
|
||||||
|
no-no since a later dlclose lets cxx_malloc point into nirvana.
|
||||||
|
The below kludge "fixes" that by reverting the original cxx_malloc
|
||||||
|
pointer after LoadLibrary. This implies that their overrides
|
||||||
|
won't be applied; that's OK. All overrides should be present at
|
||||||
|
final link time, as Windows doesn't allow undefined references;
|
||||||
|
it would actually be wrong for a dlopen'd DLL to opportunistically
|
||||||
|
override functions in a way that wasn't known then. We're not
|
||||||
|
going to try and reproduce the full ELF dynamic loader here! */
|
||||||
|
|
||||||
|
/* Store original cxx_malloc pointer. */
|
||||||
|
struct per_process_cxx_malloc *tmp_malloc;
|
||||||
|
tmp_malloc = __cygwin_user_data.cxx_malloc;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ret = (void *) LoadLibraryW (wpath);
|
||||||
|
|
||||||
|
#ifndef __x86_64__
|
||||||
|
/* Restore original cxx_malloc pointer. */
|
||||||
|
__cygwin_user_data.cxx_malloc = tmp_malloc;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (ret && gmheflags)
|
||||||
|
GetModuleHandleExW (gmheflags, wpath, (HMODULE *) &ret);
|
||||||
|
|
||||||
if (!ret)
|
if (!ret)
|
||||||
__seterrno ();
|
__seterrno ();
|
||||||
}
|
}
|
||||||
else
|
while (0);
|
||||||
{
|
|
||||||
/* handle for the named library */
|
|
||||||
path_conv pc;
|
|
||||||
if (get_full_path_of_dll (name, pc))
|
|
||||||
{
|
|
||||||
tmp_pathbuf tp;
|
|
||||||
wchar_t *path = tp.w_get ();
|
|
||||||
|
|
||||||
pc.get_wide_win32_path (path);
|
|
||||||
/* Check if the last path component contains a dot. If so,
|
|
||||||
leave the filename alone. Otherwise add a trailing dot
|
|
||||||
to override LoadLibrary's automatic adding of a ".dll" suffix. */
|
|
||||||
wchar_t *last_bs = wcsrchr (path, L'\\') ?: path;
|
|
||||||
if (last_bs && !wcschr (last_bs, L'.'))
|
|
||||||
wcscat (last_bs, L".");
|
|
||||||
|
|
||||||
#ifndef __x86_64__
|
|
||||||
/* Workaround for broken DLLs built against Cygwin versions 1.7.0-49
|
|
||||||
up to 1.7.0-57. They override the cxx_malloc pointer in their
|
|
||||||
DLL initialization code even if loaded dynamically. This is a
|
|
||||||
no-no since a later dlclose lets cxx_malloc point into nirvana.
|
|
||||||
The below kludge "fixes" that by reverting the original cxx_malloc
|
|
||||||
pointer after LoadLibrary. This implies that their overrides
|
|
||||||
won't be applied; that's OK. All overrides should be present at
|
|
||||||
final link time, as Windows doesn't allow undefined references;
|
|
||||||
it would actually be wrong for a dlopen'd DLL to opportunistically
|
|
||||||
override functions in a way that wasn't known then. We're not
|
|
||||||
going to try and reproduce the full ELF dynamic loader here! */
|
|
||||||
|
|
||||||
/* Store original cxx_malloc pointer. */
|
|
||||||
struct per_process_cxx_malloc *tmp_malloc;
|
|
||||||
tmp_malloc = __cygwin_user_data.cxx_malloc;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (flags & RTLD_NOLOAD)
|
|
||||||
GetModuleHandleExW (0, path, (HMODULE *) &ret);
|
|
||||||
else
|
|
||||||
ret = (void *) LoadLibraryW (path);
|
|
||||||
if (ret && (flags & RTLD_NODELETE))
|
|
||||||
GetModuleHandleExW (GET_MODULE_HANDLE_EX_FLAG_PIN, path,
|
|
||||||
(HMODULE *) &ret);
|
|
||||||
|
|
||||||
#ifndef __x86_64__
|
|
||||||
/* Restore original cxx_malloc pointer. */
|
|
||||||
__cygwin_user_data.cxx_malloc = tmp_malloc;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (!ret)
|
|
||||||
__seterrno ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ret)
|
if (!ret)
|
||||||
set_dl_error ("dlopen");
|
set_dl_error ("dlopen");
|
||||||
|
208
winsup/cygwin/pathfinder.h
Normal file
208
winsup/cygwin/pathfinder.h
Normal file
@ -0,0 +1,208 @@
|
|||||||
|
/* pathfinder.h: find one of multiple file names in path list
|
||||||
|
|
||||||
|
This file is part of Cygwin.
|
||||||
|
|
||||||
|
This software is a copyrighted work licensed under the terms of the
|
||||||
|
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
|
||||||
|
details. */
|
||||||
|
|
||||||
|
#include "vstrlist.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
|
||||||
|
/* Search a list of directory names for first occurrence of a file,
|
||||||
|
which's file name matches one out of a list of file names. */
|
||||||
|
class pathfinder
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef vstrlist searchdirlist;
|
||||||
|
typedef vstrlist basenamelist;
|
||||||
|
|
||||||
|
private:
|
||||||
|
pathfinder ();
|
||||||
|
pathfinder (pathfinder const &);
|
||||||
|
pathfinder & operator = (pathfinder const &);
|
||||||
|
|
||||||
|
basenamelist basenames_;
|
||||||
|
size_t basenames_maxlen_;
|
||||||
|
|
||||||
|
/* Add to searchdirs_ with extra buffer for any basename we may search for.
|
||||||
|
This is an optimization for the loops in check_path_access method. */
|
||||||
|
searchdirlist searchdirs_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
~pathfinder () {}
|
||||||
|
|
||||||
|
/* We need the basenames to search for first, to allow for optimized
|
||||||
|
memory allocation of each searchpath + longest basename combination.
|
||||||
|
The incoming list of basenames is emptied (ownership take over). */
|
||||||
|
pathfinder (allocator_interface & a, basenamelist & basenames)
|
||||||
|
: basenames_ (a)
|
||||||
|
, basenames_maxlen_ ()
|
||||||
|
, searchdirs_(a)
|
||||||
|
{
|
||||||
|
basenames_.swap(basenames);
|
||||||
|
|
||||||
|
for (basenamelist::buffer_iterator basename (basenames_.begin ());
|
||||||
|
basename != basenames_.end ();
|
||||||
|
++ basename)
|
||||||
|
{
|
||||||
|
if (basenames_maxlen_ < basename->bufferlength ())
|
||||||
|
basenames_maxlen_ = basename->bufferlength ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_searchdir (const char *dir, int dirlen)
|
||||||
|
{
|
||||||
|
if (dirlen < 0)
|
||||||
|
dirlen = strlen (dir);
|
||||||
|
|
||||||
|
if (!dirlen)
|
||||||
|
return;
|
||||||
|
|
||||||
|
searchdirs_.appendv (dir, dirlen, "/", 1 + basenames_maxlen_, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_searchpath (const char *path)
|
||||||
|
{
|
||||||
|
while (path && *path)
|
||||||
|
{
|
||||||
|
const char *next = strchr (path, ':');
|
||||||
|
add_searchdir (path, next ? next - path : -1);
|
||||||
|
path = next ? next + 1 : next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_envsearchpath (const char *envpath)
|
||||||
|
{
|
||||||
|
add_searchpath (getenv (envpath));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* pathfinder::criterion_interface
|
||||||
|
Overload this test method when you need separate dir and basename. */
|
||||||
|
struct criterion_interface
|
||||||
|
{
|
||||||
|
virtual char const * name () const { return NULL; }
|
||||||
|
|
||||||
|
virtual bool test (searchdirlist::iterator dir,
|
||||||
|
basenamelist::iterator name) const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* pathfinder::simple_criterion_interface
|
||||||
|
Overload this test method when you need a single filename. */
|
||||||
|
class simple_criterion_interface
|
||||||
|
: public criterion_interface
|
||||||
|
{
|
||||||
|
virtual bool test (searchdirlist::iterator dir,
|
||||||
|
basenamelist::iterator name) const
|
||||||
|
{
|
||||||
|
/* Complete the filename path to search for within dir,
|
||||||
|
We have allocated enough memory above. */
|
||||||
|
searchdirlist::buffer_iterator dirbuf (dir);
|
||||||
|
memcpy (dirbuf->buffer () + dirbuf->stringlength (),
|
||||||
|
name->string (), name->stringlength () + 1);
|
||||||
|
bool ret = test (dirbuf->string ());
|
||||||
|
/* reset original dir */
|
||||||
|
dirbuf->buffer ()[dirbuf->stringlength ()] = '\0';
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual bool test (const char * filename) const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* pathfinder::path_conv_criterion_interface
|
||||||
|
Overload this test method when you need a path_conv. */
|
||||||
|
class path_conv_criterion_interface
|
||||||
|
: public simple_criterion_interface
|
||||||
|
{
|
||||||
|
path_conv mypc_;
|
||||||
|
path_conv & pc_;
|
||||||
|
unsigned opt_;
|
||||||
|
|
||||||
|
/* simple_criterion_interface */
|
||||||
|
virtual bool test (const char * filename) const
|
||||||
|
{
|
||||||
|
pc_.check (filename, opt_);
|
||||||
|
return test (pc_);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
path_conv_criterion_interface (unsigned opt = PC_SYM_FOLLOW)
|
||||||
|
: mypc_ ()
|
||||||
|
, pc_ (mypc_)
|
||||||
|
, opt_ (opt)
|
||||||
|
{}
|
||||||
|
|
||||||
|
path_conv_criterion_interface (path_conv & ret, unsigned opt = PC_SYM_FOLLOW)
|
||||||
|
: mypc_ ()
|
||||||
|
, pc_ (ret)
|
||||||
|
, opt_ (opt)
|
||||||
|
{}
|
||||||
|
|
||||||
|
virtual bool test (path_conv & pc) const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* pathfinder::exists_and_not_dir
|
||||||
|
Test if path_conv argument does exist and is not a directory. */
|
||||||
|
struct exists_and_not_dir
|
||||||
|
: public path_conv_criterion_interface
|
||||||
|
{
|
||||||
|
virtual char const * name () const { return "exists and not dir"; }
|
||||||
|
|
||||||
|
exists_and_not_dir (path_conv & pc, unsigned opt = PC_SYM_FOLLOW)
|
||||||
|
: path_conv_criterion_interface (pc, opt)
|
||||||
|
{}
|
||||||
|
|
||||||
|
/* path_conv_criterion_interface */
|
||||||
|
virtual bool test (path_conv & pc) const
|
||||||
|
{
|
||||||
|
if (pc.exists () && !pc.isdir ())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
pc.error = ENOENT;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* Find the single dir + basename that matches criterion.
|
||||||
|
|
||||||
|
Calls criterion.test method for each registered dir + basename
|
||||||
|
until returning true:
|
||||||
|
Returns true with found_dir + found_basename set.
|
||||||
|
If criterion.test method never returns true:
|
||||||
|
Returns false, not modifying found_dir nor found_basename. */
|
||||||
|
bool find (criterion_interface const & criterion,
|
||||||
|
searchdirlist::member const ** found_dir = NULL,
|
||||||
|
basenamelist::member const ** found_basename = NULL)
|
||||||
|
{
|
||||||
|
char const * critname = criterion.name ();
|
||||||
|
for (basenamelist::iterator name = basenames_.begin ();
|
||||||
|
name != basenames_.end ();
|
||||||
|
++name)
|
||||||
|
for (searchdirlist::iterator dir(searchdirs_.begin ());
|
||||||
|
dir != searchdirs_.end ();
|
||||||
|
++dir)
|
||||||
|
if (criterion.test (dir, name))
|
||||||
|
{
|
||||||
|
debug_printf ("(%s), take %s%s", critname,
|
||||||
|
dir->string(), name->string ());
|
||||||
|
if (found_dir)
|
||||||
|
*found_dir = dir.operator -> ();
|
||||||
|
if (found_basename)
|
||||||
|
*found_basename = name.operator -> ();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
debug_printf ("not (%s), skip %s%s", critname,
|
||||||
|
dir->string(), name->string ());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* __cplusplus */
|
373
winsup/cygwin/vstrlist.h
Normal file
373
winsup/cygwin/vstrlist.h
Normal file
@ -0,0 +1,373 @@
|
|||||||
|
/* vstrlist.h: class vstrlist
|
||||||
|
|
||||||
|
This file is part of Cygwin.
|
||||||
|
|
||||||
|
This software is a copyrighted work licensed under the terms of the
|
||||||
|
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
|
||||||
|
details. */
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
|
||||||
|
struct allocator_interface
|
||||||
|
{
|
||||||
|
virtual void * alloc (size_t) = 0;
|
||||||
|
virtual void free (void *) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* The allocated_type makes sure to use the free () method of the
|
||||||
|
same allocator_interface than the alloc () method was used of.
|
||||||
|
|
||||||
|
Stores the allocator_interface address before the real object,
|
||||||
|
to hide it from (construction & destruction of) real object. */
|
||||||
|
class allocated_type
|
||||||
|
{
|
||||||
|
union allocator_store
|
||||||
|
{
|
||||||
|
allocator_interface * allocator_;
|
||||||
|
char alignment_[8];
|
||||||
|
|
||||||
|
union pointer
|
||||||
|
{
|
||||||
|
void * vptr;
|
||||||
|
allocator_store * real;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
void * operator new (size_t class_size, allocator_interface & allocator)
|
||||||
|
{
|
||||||
|
allocator_store::pointer astore;
|
||||||
|
astore.vptr = allocator.alloc (sizeof (allocator_store) + class_size);
|
||||||
|
astore.real->allocator_ = &allocator;
|
||||||
|
++ astore.real;
|
||||||
|
return astore.vptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator delete (void * p)
|
||||||
|
{
|
||||||
|
allocator_store::pointer astore;
|
||||||
|
astore.vptr = p;
|
||||||
|
-- astore.real;
|
||||||
|
astore.real->allocator_->free (astore.vptr);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* Double linked list of char arrays, each being a string buffer,
|
||||||
|
which's final buffer size and initial string content is defined
|
||||||
|
by a NULL terminated variable argument list of STRING+LEN pairs,
|
||||||
|
where each STRING (up to LEN) is concatenated for the initial
|
||||||
|
string buffer content, and each LEN is added to the final size
|
||||||
|
of the allocated string buffer.
|
||||||
|
If LEN is -1, strlen(STRING) is used for LEN.
|
||||||
|
|
||||||
|
Needs:
|
||||||
|
An implementation of the allocator_interface.
|
||||||
|
|
||||||
|
Provides:
|
||||||
|
iterator:
|
||||||
|
short name for the string_iterator
|
||||||
|
string_iterator:
|
||||||
|
provides readonly access via member methods:
|
||||||
|
string (): readonly string buffer
|
||||||
|
stringlength (): length (readonly) of initial string
|
||||||
|
buffer_iterator:
|
||||||
|
extends string_iterator
|
||||||
|
provides writeable access via member methods:
|
||||||
|
buffer (): writeable string buffer
|
||||||
|
bufferlength (): length (readonly) of allocated buffer
|
||||||
|
|
||||||
|
Usage sample:
|
||||||
|
char * text = "snipe";
|
||||||
|
vstrlist l;
|
||||||
|
l.appendv (text, 4, text+3, 2, "", 2, NULL);
|
||||||
|
buffer_iterator it (l.begin ());
|
||||||
|
strcpy (it->buffer () + it->stringlength (), "ts");
|
||||||
|
printf ("Sample result is: '%s'", it->string ());
|
||||||
|
Sample result is: 'snippets' */
|
||||||
|
class vstrlist
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
class member
|
||||||
|
: public allocated_type
|
||||||
|
{
|
||||||
|
friend class vstrlist;
|
||||||
|
friend class string_iterator;
|
||||||
|
|
||||||
|
member * prev_;
|
||||||
|
member * next_;
|
||||||
|
size_t bufferlength_;
|
||||||
|
size_t stringlength_;
|
||||||
|
char buffer_[1]; /* we always have space for the trailing zero */
|
||||||
|
|
||||||
|
/* no copy, just swap */
|
||||||
|
member (member const &);
|
||||||
|
member & operator = (member const &);
|
||||||
|
|
||||||
|
/* anchor */
|
||||||
|
void * operator new (size_t class_size, allocator_interface & allocator)
|
||||||
|
{
|
||||||
|
return allocated_type::operator new (class_size, allocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* anchor */
|
||||||
|
member ()
|
||||||
|
: allocated_type ()
|
||||||
|
, prev_ (this)
|
||||||
|
, next_ (this)
|
||||||
|
, bufferlength_ (0)
|
||||||
|
, stringlength_ (0)
|
||||||
|
, buffer_ ()
|
||||||
|
{}
|
||||||
|
|
||||||
|
/* entry: determine memory size from args */
|
||||||
|
void * operator new (size_t class_size, allocator_interface & allocator,
|
||||||
|
char const * part0, va_list parts)
|
||||||
|
{
|
||||||
|
char const * part = part0;
|
||||||
|
va_list partsdup;
|
||||||
|
va_copy (partsdup, parts);
|
||||||
|
while (part)
|
||||||
|
{
|
||||||
|
int partlen = va_arg (partsdup, int);
|
||||||
|
if (partlen < 0)
|
||||||
|
partlen = strlen (part);
|
||||||
|
class_size += partlen;
|
||||||
|
part = va_arg (partsdup, char const *);
|
||||||
|
}
|
||||||
|
va_end (partsdup);
|
||||||
|
|
||||||
|
return allocated_type::operator new (class_size, allocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* entry: instantly insert into list */
|
||||||
|
member (member * before, char const * part0, va_list parts)
|
||||||
|
: allocated_type ()
|
||||||
|
, prev_ (NULL)
|
||||||
|
, next_ (NULL)
|
||||||
|
, bufferlength_ (0)
|
||||||
|
, stringlength_ ()
|
||||||
|
, buffer_ ()
|
||||||
|
{
|
||||||
|
prev_ = before->prev_;
|
||||||
|
next_ = before;
|
||||||
|
|
||||||
|
prev_->next_ = this;
|
||||||
|
next_->prev_ = this;
|
||||||
|
|
||||||
|
char * dest = buffer_;
|
||||||
|
char const * part = part0;
|
||||||
|
va_list partsdup;
|
||||||
|
va_copy (partsdup, parts);
|
||||||
|
while (part)
|
||||||
|
{
|
||||||
|
int partlen = va_arg (partsdup, int);
|
||||||
|
if (partlen < 0)
|
||||||
|
{
|
||||||
|
char * old = dest;
|
||||||
|
dest = stpcpy (old, part);
|
||||||
|
partlen = dest - old;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
dest = stpncpy (dest, part, partlen);
|
||||||
|
bufferlength_ += partlen;
|
||||||
|
part = va_arg (partsdup, const char *);
|
||||||
|
}
|
||||||
|
va_end (partsdup);
|
||||||
|
*dest = (char)0;
|
||||||
|
stringlength_ = dest - buffer_;
|
||||||
|
if (bufferlength_ > stringlength_)
|
||||||
|
memset (++dest, 0, bufferlength_ - stringlength_);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* remove entry from list */
|
||||||
|
~member ()
|
||||||
|
{
|
||||||
|
member * next = next_;
|
||||||
|
member * prev = prev_;
|
||||||
|
if (next)
|
||||||
|
next->prev_ = prev;
|
||||||
|
if (prev)
|
||||||
|
prev->next_ = next;
|
||||||
|
prev_ = NULL;
|
||||||
|
next_ = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
member const * next () const { return next_; }
|
||||||
|
member * next () { return next_; }
|
||||||
|
member const * prev () const { return next_; }
|
||||||
|
member * prev () { return next_; }
|
||||||
|
|
||||||
|
/* readonly access */
|
||||||
|
char const * string () const { return buffer_; }
|
||||||
|
size_t stringlength () const { return stringlength_; }
|
||||||
|
|
||||||
|
/* writeable access */
|
||||||
|
char * buffer () { return buffer_; }
|
||||||
|
size_t bufferlength () { return bufferlength_; }
|
||||||
|
};
|
||||||
|
|
||||||
|
/* readonly access */
|
||||||
|
class string_iterator
|
||||||
|
{
|
||||||
|
friend class vstrlist;
|
||||||
|
friend class buffer_iterator;
|
||||||
|
|
||||||
|
member * current_;
|
||||||
|
|
||||||
|
string_iterator ();
|
||||||
|
|
||||||
|
string_iterator (member * current)
|
||||||
|
: current_ (current)
|
||||||
|
{}
|
||||||
|
|
||||||
|
public:
|
||||||
|
string_iterator (string_iterator const & rhs)
|
||||||
|
: current_ (rhs.current_)
|
||||||
|
{}
|
||||||
|
|
||||||
|
string_iterator & operator = (string_iterator const & rhs)
|
||||||
|
{
|
||||||
|
current_ = rhs.current_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
string_iterator & operator ++ ()
|
||||||
|
{
|
||||||
|
current_ = current_->next ();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
string_iterator operator ++ (int)
|
||||||
|
{
|
||||||
|
string_iterator ret (*this);
|
||||||
|
current_ = current_->next ();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
string_iterator & operator -- ()
|
||||||
|
{
|
||||||
|
current_ = current_->prev ();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
string_iterator operator -- (int)
|
||||||
|
{
|
||||||
|
string_iterator ret (*this);
|
||||||
|
current_ = current_->prev ();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator == (string_iterator const & rhs) const
|
||||||
|
{
|
||||||
|
return current_ == rhs.current_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator != (string_iterator const & rhs) const
|
||||||
|
{
|
||||||
|
return current_ != rhs.current_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* readonly member access */
|
||||||
|
member const & operator * () const { return *current_; }
|
||||||
|
member const * operator -> () const { return current_; }
|
||||||
|
|
||||||
|
void remove ()
|
||||||
|
{
|
||||||
|
member * old = current_;
|
||||||
|
++ *this;
|
||||||
|
delete old;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* writeable access */
|
||||||
|
class buffer_iterator
|
||||||
|
: public string_iterator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit /* can be used with vstrlist.begin () */
|
||||||
|
buffer_iterator (string_iterator const & begin)
|
||||||
|
: string_iterator (begin)
|
||||||
|
{}
|
||||||
|
|
||||||
|
buffer_iterator (buffer_iterator const & rhs)
|
||||||
|
: string_iterator (rhs)
|
||||||
|
{}
|
||||||
|
|
||||||
|
buffer_iterator & operator = (buffer_iterator const & rhs)
|
||||||
|
{
|
||||||
|
string_iterator::operator = (rhs);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* writeable member access */
|
||||||
|
member & operator * () const { return *current_; }
|
||||||
|
member * operator -> () const { return current_; }
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
allocator_interface & allocator_;
|
||||||
|
member * anchor_;
|
||||||
|
|
||||||
|
/* not without an allocator */
|
||||||
|
vstrlist ();
|
||||||
|
|
||||||
|
/* no copy, just swap () */
|
||||||
|
vstrlist (vstrlist const &);
|
||||||
|
vstrlist & operator = (vstrlist const &);
|
||||||
|
|
||||||
|
public:
|
||||||
|
/* iterator is the string_iterator */
|
||||||
|
typedef class string_iterator iterator;
|
||||||
|
|
||||||
|
iterator begin () { return iterator (anchor_->next ()); }
|
||||||
|
iterator end () { return iterator (anchor_ ); }
|
||||||
|
iterator rbegin () { return iterator (anchor_->prev ()); }
|
||||||
|
iterator rend () { return iterator (anchor_ ); }
|
||||||
|
|
||||||
|
vstrlist (allocator_interface & a)
|
||||||
|
: allocator_ (a)
|
||||||
|
, anchor_ (NULL) /* exception safety */
|
||||||
|
{
|
||||||
|
anchor_ = new (allocator_) member ();
|
||||||
|
}
|
||||||
|
|
||||||
|
~vstrlist ()
|
||||||
|
{
|
||||||
|
if (anchor_ != NULL)
|
||||||
|
{
|
||||||
|
for (iterator it = begin (); it != end (); it.remove ());
|
||||||
|
delete anchor_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void swap (vstrlist & that)
|
||||||
|
{
|
||||||
|
allocator_interface & a = allocator_;
|
||||||
|
member * m = anchor_;
|
||||||
|
allocator_ = that.allocator_;
|
||||||
|
anchor_ = that.anchor_;
|
||||||
|
that.allocator_ = a;
|
||||||
|
that.anchor_ = m;
|
||||||
|
}
|
||||||
|
|
||||||
|
string_iterator appendv (char const * part0, va_list parts)
|
||||||
|
{
|
||||||
|
member * ret = new (allocator_, part0, parts)
|
||||||
|
member (anchor_, part0, parts);
|
||||||
|
return string_iterator (ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
string_iterator appendv (char const * part0, ...)
|
||||||
|
{
|
||||||
|
va_list parts;
|
||||||
|
va_start (parts, part0);
|
||||||
|
string_iterator ret = appendv (part0, parts);
|
||||||
|
va_end (parts);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* __cplusplus */
|
Loading…
x
Reference in New Issue
Block a user