Apache Mesos
which.hpp
Go to the documentation of this file.
1 // Licensed under the Apache License, Version 2.0 (the "License");
2 // you may not use this file except in compliance with the License.
3 // You may obtain a copy of the License at
4 //
5 // http://www.apache.org/licenses/LICENSE-2.0
6 //
7 // Unless required by applicable law or agreed to in writing, software
8 // distributed under the License is distributed on an "AS IS" BASIS,
9 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 // See the License for the specific language governing permissions and
11 // limitations under the License.
12 
13 #ifndef __STOUT_OS_WINDOWS_WHICH_HPP__
14 #define __STOUT_OS_WINDOWS_WHICH_HPP__
15 
16 #include <string>
17 #include <vector>
18 
19 #include <stout/none.hpp>
20 #include <stout/option.hpp>
21 #include <stout/os.hpp>
22 #include <stout/path.hpp>
23 #include <stout/strings.hpp>
24 
25 #include <stout/os/exists.hpp>
26 
27 
28 namespace os {
29 
30 // This behaves "like the user expects" of POSIX `which`, but on Windows. That
31 // is, if a path is not specified, we search through the `PATH` environment
32 // variable, but explicitly do not search the current working directory (which
33 // `CreateProcess` and Windows' `where` would do).
34 //
35 // Because the executable permission does not work on Windows, the closest
36 // equivalent is to check the path extension against those listed in `PATHEXT`.
37 // However, we first search for exactly the file name the user specified with
38 // `command`, regardless of extension, because it could be an executable. If an
39 // exact match is not found, we continue the search with the environment's
40 // "executable" extensions.
42  const std::string& command,
43  const Option<std::string>& _path = None())
44 {
45  Option<std::string> path = _path;
46 
47  if (path.isNone()) {
48  path = os::getenv("PATH");
49 
50  if (path.isNone()) {
51  return None();
52  }
53  }
54 
55  Option<std::string> pathext = os::getenv("PATHEXT");
56 
57  if (pathext.isNone()) {
58  pathext = ".COM;.EXE;.BAT;.CMD";
59  }
60 
61  std::vector<std::string> tokens = strings::tokenize(path.get(), ";");
62  std::vector<std::string> exts = strings::tokenize(pathext.get(), ";");
63 
64  // NOTE: This handles the edge case of `command` already having an extension,
65  // e.g. `docker.exe`. By starting with the case of "", we don't have to
66  // special case the loops below.
67  exts.insert(exts.begin(), "");
68 
69  // Nested loops, but fairly finite. This is how `where` works on Windows
70  // (which is the equivalent of `which`). The loops are nested such that we
71  // first search through `PATH` for `command`, then through `PATH` for
72  // `command.COM` and so on.
73  foreach (const std::string& ext, exts) {
74  foreach (const std::string& token, tokens) {
75  const std::string commandPath = path::join(token, command + ext);
76  if (!os::exists(commandPath)) {
77  continue;
78  }
79 
80  return commandPath;
81  }
82  }
83 
84  return None();
85 }
86 
87 } // namespace os {
88 
89 
90 #endif // __STOUT_OS_WINDOWS_WHICH_HPP__
Definition: path.hpp:29
bool exists(const std::string &path)
Definition: exists.hpp:26
Definition: posix_signalhandler.hpp:23
std::string join(const std::string &path1, const std::string &path2, const char _separator=os::PATH_SEPARATOR)
Definition: path.hpp:116
Option< std::string > which(const std::string &command, const Option< std::string > &_path=None())
Definition: which.hpp:31
std::vector< std::string > tokenize(const std::string &s, const std::string &delims, const Option< size_t > &maxTokens=None())
Definition: strings.hpp:139
const T & get() const &
Definition: option.hpp:119
Option< std::string > getenv(const std::string &key)
Definition: getenv.hpp:29
Definition: none.hpp:27
bool isNone() const
Definition: option.hpp:117