Rather than searching all search dirs per one basename, search for all basenames within per one search dir. pathfinder.h (check_path_access): Interchange dir- and basename-loops.
		
			
				
	
	
		
			209 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			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 */
 |