libc/winsup/cygwin/pathfinder.h

209 lines
5.5 KiB
C++

/* 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 (searchdirlist::iterator dir(searchdirs_.begin ());
dir != searchdirs_.end ();
++dir)
for (basenamelist::iterator name = basenames_.begin ();
name != basenames_.end ();
++name)
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 */