Improve the process for GNU tools
[minix3.git] / external / bsd / kyua-cli / dist / utils / fs / operations.cpp
blobf835208f42ed14ce0e183b97af2ca6b11284f91e
1 // Copyright 2010 Google Inc.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 // * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above copyright
11 // notice, this list of conditions and the following disclaimer in the
12 // documentation and/or other materials provided with the distribution.
13 // * Neither the name of Google Inc. nor the names of its contributors
14 // may be used to endorse or promote products derived from this software
15 // without specific prior written permission.
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #include "utils/fs/operations.hpp"
31 #if defined(HAVE_CONFIG_H)
32 # include "config.h"
33 #endif
35 extern "C" {
36 #include <sys/stat.h>
38 #include <dirent.h>
39 #include <unistd.h>
42 #include <cerrno>
43 #include <cstdlib>
44 #include <cstring>
45 #include <sstream>
46 #include <string>
48 #include "utils/auto_array.ipp"
49 #include "utils/defs.hpp"
50 #include "utils/env.hpp"
51 #include "utils/format/macros.hpp"
52 #include "utils/fs/exceptions.hpp"
53 #include "utils/fs/path.hpp"
54 #include "utils/logging/macros.hpp"
55 #include "utils/optional.ipp"
56 #include "utils/sanity.hpp"
58 namespace fs = utils::fs;
60 using utils::optional;
63 namespace {
66 /// Stats a file, without following links.
67 ///
68 /// \param path The file to stat.
69 ///
70 /// \return The stat structure on success.
71 ///
72 /// \throw system_error An error on failure.
73 static struct ::stat
74 safe_stat(const fs::path& path)
76 struct ::stat sb;
77 if (::lstat(path.c_str(), &sb) == -1) {
78 const int original_errno = errno;
79 throw fs::system_error(F("Cannot get information about %s") % path,
80 original_errno);
82 return sb;
86 } // anonymous namespace
89 /// Queries the path to the current directory.
90 ///
91 /// \return The path to the current directory.
92 ///
93 /// \throw fs::error If there is a problem querying the current directory.
94 fs::path
95 fs::current_path(void)
97 char* cwd;
98 #if defined(HAVE_GETCWD_DYN)
99 cwd = ::getcwd(NULL, 0);
100 #else
101 cwd = ::getcwd(NULL, MAXPATHLEN);
102 #endif
103 if (cwd == NULL) {
104 const int original_errno = errno;
105 throw fs::system_error(F("Failed to get current working directory"),
106 original_errno);
109 try {
110 const fs::path result(cwd);
111 std::free(cwd);
112 return result;
113 } catch (...) {
114 std::free(cwd);
115 throw;
120 /// Checks if a file exists.
122 /// Be aware that this is racy in the same way as access(2) is.
124 /// \param path The file to check the existance of.
126 /// \return True if the file exists; false otherwise.
127 bool
128 fs::exists(const fs::path& path)
130 return ::access(path.c_str(), F_OK) == 0;
134 /// Locates a file in the PATH.
136 /// \param name The file to locate.
138 /// \return The path to the located file or none if it was not found. The
139 /// returned path is always absolute.
140 optional< fs::path >
141 fs::find_in_path(const char* name)
143 const optional< std::string > current_path = utils::getenv("PATH");
144 if (!current_path || current_path.get().empty())
145 return none;
147 std::istringstream path_input(current_path.get() + ":");
148 std::string path_component;
149 while (std::getline(path_input, path_component, ':').good()) {
150 const fs::path candidate = path_component.empty() ?
151 fs::path(name) : (fs::path(path_component) / name);
152 if (exists(candidate)) {
153 if (candidate.is_absolute())
154 return utils::make_optional(candidate);
155 else
156 return utils::make_optional(candidate.to_absolute());
159 return none;
163 /// Creates a directory.
165 /// \param dir The path to the directory to create.
166 /// \param mode The permissions for the new directory.
168 /// \throw system_error If the call to mkdir(2) fails.
169 void
170 fs::mkdir(const fs::path& dir, const int mode)
172 if (::mkdir(dir.c_str(), static_cast< mode_t >(mode)) == -1) {
173 const int original_errno = errno;
174 throw fs::system_error(F("Failed to create directory %s") % dir,
175 original_errno);
180 /// Creates a directory and any missing parents.
182 /// This is separate from the fs::mkdir function to clearly differentiate the
183 /// libc wrapper from the more complex algorithm implemented here.
185 /// \param dir The path to the directory to create.
186 /// \param mode The permissions for the new directories.
188 /// \throw system_error If any call to mkdir(2) fails.
189 void
190 fs::mkdir_p(const fs::path& dir, const int mode)
192 try {
193 fs::mkdir(dir, mode);
194 } catch (const fs::system_error& e) {
195 if (e.original_errno() == ENOENT) {
196 fs::mkdir_p(dir.branch_path(), mode);
197 fs::mkdir(dir, mode);
198 } else if (e.original_errno() != EEXIST)
199 throw e;
204 /// Creates a temporary directory.
206 /// The temporary directory is created using mkdtemp(3) using the provided
207 /// template. This should be most likely used in conjunction with
208 /// fs::auto_directory.
210 /// \param path_template The template for the temporary path, which is a
211 /// basename that is created within the TMPDIR. Must contain the XXXXXX
212 /// pattern, which is atomically replaced by a random unique string.
214 /// \return The generated path for the temporary directory.
216 /// \throw fs::system_error If the call to mkdtemp(3) fails.
217 fs::path
218 fs::mkdtemp(const std::string& path_template)
220 PRE(path_template.find("XXXXXX") != std::string::npos);
222 const fs::path tmpdir(utils::getenv_with_default("TMPDIR", "/tmp"));
223 const fs::path full_template = tmpdir / path_template;
225 utils::auto_array< char > buf(new char[full_template.str().length() + 1]);
226 std::strcpy(buf.get(), full_template.c_str());
227 if (::mkdtemp(buf.get()) == NULL) {
228 const int original_errno = errno;
229 throw fs::system_error(F("Cannot create temporary directory using "
230 "template %s") % full_template,
231 original_errno);
233 return fs::path(buf.get());
237 /// Creates a temporary file.
239 /// The temporary file is created using mkstemp(3) using the provided template.
240 /// This should be most likely used in conjunction with fs::auto_file.
242 /// \param path_template The template for the temporary path, which is a
243 /// basename that is created within the TMPDIR. Must contain the XXXXXX
244 /// pattern, which is atomically replaced by a random unique string.
246 /// \return The generated path for the temporary directory.
248 /// \throw fs::system_error If the call to mkstemp(3) fails.
249 fs::path
250 fs::mkstemp(const std::string& path_template)
252 PRE(path_template.find("XXXXXX") != std::string::npos);
254 const fs::path tmpdir(utils::getenv_with_default("TMPDIR", "/tmp"));
255 const fs::path full_template = tmpdir / path_template;
257 utils::auto_array< char > buf(new char[full_template.str().length() + 1]);
258 std::strcpy(buf.get(), full_template.c_str());
259 if (::mkstemp(buf.get()) == -1) {
260 const int original_errno = errno;
261 throw fs::system_error(F("Cannot create temporary file using template "
262 "%s") % full_template, original_errno);
264 return fs::path(buf.get());
268 /// Recursively removes a directory.
270 /// This operation simulates a "rm -r". No effort is made to forcibly delete
271 /// files and no attention is paid to mount points.
273 /// \param directory The directory to remove.
275 /// \throw fs::error If there is a problem removing any directory or file.
276 void
277 fs::rm_r(const fs::path& directory)
279 DIR* dirp = ::opendir(directory.c_str());
280 if (dirp == NULL) {
281 const int original_errno = errno;
282 throw fs::system_error(F("Failed to open directory %s") %
283 directory.str(), original_errno);
285 try {
286 ::dirent* dp;
287 while ((dp = ::readdir(dirp)) != NULL) {
288 const std::string name = dp->d_name;
289 if (name == "." || name == "..")
290 continue;
292 const fs::path entry = directory / dp->d_name;
294 const struct ::stat sb = safe_stat(entry);
295 if (S_ISDIR(sb.st_mode)) {
296 LD(F("Descending into %s") % entry);
297 fs::rm_r(entry);
298 } else {
299 LD(F("Removing file %s") % entry);
300 fs::unlink(entry);
303 } catch (...) {
304 ::closedir(dirp);
305 throw;
307 ::closedir(dirp);
309 LD(F("Removing empty directory %s") % directory);
310 fs::rmdir(directory);
314 /// Removes an empty directory.
316 /// \param file The directory to remove.
318 /// \throw fs::system_error If the call to rmdir(2) fails.
319 void
320 fs::rmdir(const path& file)
322 if (::rmdir(file.c_str()) == -1) {
323 const int original_errno = errno;
324 throw fs::system_error(F("Removal of %s failed") % file,
325 original_errno);
330 /// Removes a file.
332 /// \param file The file to remove.
334 /// \throw fs::system_error If the call to unlink(2) fails.
335 void
336 fs::unlink(const path& file)
338 if (::unlink(file.c_str()) == -1) {
339 const int original_errno = errno;
340 throw fs::system_error(F("Removal of %s failed") % file,
341 original_errno);