/* 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 */