search_path.cc
Go to the documentation of this file.
1 // ======================================================================
2 //
3 // search_path: Seek filename or pattern in a given list of pathnames
4 //
5 // ======================================================================
6 
7 #include "cetlib/search_path.h"
8 #include "cetlib/filesystem.h"
9 #include "cetlib/getenv.h"
10 #include "cetlib/split.h"
11 #include "cetlib_except/exception.h"
12 
13 #include <dirent.h>
14 #include <errno.h>
15 #include <functional>
16 #include <iterator>
17 #include <memory>
18 #include <ostream>
19 #include <regex>
20 
21 using namespace std;
22 using cet::search_path;
23 
24 namespace {
25  string const exception_category{"search_path"};
26 
27  string
28  get_env_if_colon_present(string const& arg)
29  {
30  // If no colon is present, assume the user is specifying an
31  // environment variable.
32  return arg.find(':') == string::npos ? arg : string{};
33  }
34 
35  vector<string>
36  get_dirs(std::string const& path_to_split)
37  {
38  vector<string> dirs;
39  cet::split(path_to_split, ':', back_inserter(dirs));
40 
41  if (dirs.empty()) {
42  dirs.emplace_back();
43  }
44  return dirs;
45  }
46 
47  vector<string>
48  get_dirs(std::string const& env, std::string const& paths)
49  {
50  return get_dirs(env.empty() ? paths : cet::getenv(env));
51  }
52 
53  vector<string>
54  get_dirs(std::string const& env, std::nothrow_t)
55  {
56  return get_dirs(cet::getenv(env, std::nothrow));
57  }
58 }
59 
61 
62 search_path::search_path(string const& env_name_or_path)
63  : env_{get_env_if_colon_present(env_name_or_path)}
64  , dirs_{get_dirs(env_, env_name_or_path)}
65 {}
66 
67 search_path::search_path(string const& env_name, std::nothrow_t)
68  : env_{env_name}, dirs_{get_dirs(env_, std::nothrow)}
69 {}
70 
72  : env_{}, dirs_{get_dirs(path)}
73 {}
74 
75 bool
77 {
78  return dirs_.empty();
79 }
80 
81 size_t
83 {
84  return dirs_.size();
85 }
86 
87 string const& search_path::operator[](size_t const k) const
88 {
89  return dirs_.at(k);
90 }
91 
92 // ----------------------------------------------------------------------
93 // find_file() overloads:
94 
95 string
96 search_path::find_file(string const& filename) const
97 {
98  string result;
99  if (find_file(filename, result))
100  return result;
101  throw cet::exception(exception_category)
102  << "Can't find file \"" << filename << '"';
103 }
104 
105 bool
106 search_path::find_file(string const& filename, string& result) const
107 {
108  if (filename.empty())
109  return false;
110 
111  for (auto const& dir : dirs_) {
112  string fullpath = dir + '/' + filename;
113  for (size_t k; (k = fullpath.find("//")) != string::npos;) {
114  fullpath.erase(k, 1);
115  }
116  if (cet::file_exists(fullpath)) {
117  result = fullpath;
118  return true;
119  }
120  }
121  return false;
122 }
123 
124 // ----------------------------------------------------------------------
125 size_t
126 search_path::find_files(string const& pat, vector<string>& out) const
127 {
128  regex const re{pat};
129  size_t count{};
130  for (auto const& dir : dirs_) {
131  unique_ptr<DIR, function<int(DIR*)>> dd(opendir(dir.c_str()), closedir);
132  if (dd.get() == nullptr) {
133  // The opendir() failed, we do not care why, skip it.
134  continue;
135  }
136  while (1) {
137  // Note: errno is a thread-local!
138  errno = 0;
139  // Note: This is thread-safe so long as each thread
140  // has their own dd, which is the case here.
141  auto entry = readdir(dd.get());
142  if (errno != 0) {
143  throw cet::exception(exception_category)
144  << "Failed to read directory \"" << dir
145  << "\"; error num = " << errno;
146  }
147  if (entry == nullptr) {
148  // We have reached the end of this directory stream.
149  break;
150  }
151  if (regex_match(entry->d_name, re)) {
152  out.push_back(dir + '/' + entry->d_name);
153  ++count;
154  }
155  }
156  }
157  return count;
158 }
159 
162 {
163  std::ostringstream oss;
164  oss << *this;
165  return oss.str();
166 }
167 
168 //======================================================================
169 
170 ostream&
171 cet::operator<<(ostream& os, search_path const& path)
172 {
173  auto const sz = path.size();
174  if (sz == 0)
175  return os;
176  os << path[0];
177  for (size_t k{1}; k != sz; ++k) {
178  os << ":" << path[k];
179  }
180  return os;
181 }
std::string to_string() const
Definition: search_path.cc:161
QList< Entry > entry
std::vector< std::string > dirs_
Definition: search_path.h:97
static QCString result
std::ostream & operator<<(std::ostream &, map_vector_key const &)
Definition: map_vector.h:342
std::string string
Definition: nybbler.cc:12
path_tag_t const path_tag
Definition: search_path.cc:60
STL namespace.
string dir
int errno
Contains the last error code.
Definition: structcmd.h:53
std::string env_
Definition: search_path.h:96
string filename
Definition: train.py:213
std::size_t size() const
Definition: search_path.cc:82
std::string getenv(std::string const &name)
Definition: getenv.cc:15
std::size_t find_files(std::string const &filename_pattern, std::vector< std::string > &result) const
Definition: search_path.cc:126
bool empty() const
Definition: search_path.cc:76
std::string const & operator[](std::size_t k) const
Definition: search_path.cc:87
std::string find_file(std::string const &filename) const
Definition: search_path.cc:96
search_path(std::string const &env_name_or_path)
Definition: search_path.cc:62
bool file_exists(std::string const &qualified_filename)
Definition: filesystem.cc:14
void split(std::string const &s, char c, OutIter dest)
Definition: split.h:35
cet::coded_exception< error, detail::translate > exception
Definition: exception.h:33