1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
|
// Copyright (C) 2019-2023 Joel Rosdahl and other contributors
//
// See doc/AUTHORS.adoc for a complete list of contributors.
//
// This program is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 3 of the License, or (at your option)
// any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License along with
// this program; if not, write to the Free Software Foundation, Inc., 51
// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#pragma once
#include <Stat.hpp>
#include <util/TimePoint.hpp>
#include <util/Tokenizer.hpp>
#include <cstdint>
#include <filesystem>
#include <functional>
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
class Config;
class Context;
namespace Util {
using TraverseVisitor =
std::function<void(const std::string& path, bool is_dir)>;
enum class UnlinkLog { log_failure, ignore_failure };
// Get base name of path.
std::string_view base_name(std::string_view path);
// Get an integer value from bytes in big endian order.
//
// Parameters:
// - buffer: Bytes to read.
// - count: Number of bytes to read.
template<typename T>
void
big_endian_to_int(const uint8_t* buffer, T& value)
{
value = 0;
for (size_t i = 0; i < sizeof(T); ++i) {
value <<= 8;
value |= buffer[i];
}
}
template<>
inline void
big_endian_to_int(const uint8_t* buffer, int8_t& value)
{
value = buffer[0];
}
template<>
inline void
big_endian_to_int(const uint8_t* buffer, uint8_t& value)
{
value = buffer[0];
}
// Remove the extension via `remove_extension()`, then add `new_ext`. `new_ext`
// should start with a dot, no extra dot is inserted.
std::string change_extension(std::string_view path, std::string_view new_ext);
// Clone a file from `src` to `dest`. If `via_tmp_file` is true, `src` is cloned
// to a temporary file and then renamed to `dest`. Throws `core::Error` on
// error.
void clone_file(const std::string& src,
const std::string& dest,
bool via_tmp_file = false);
// Clone, hard link or copy a file from `source` to `dest` depending on settings
// in `ctx`. If cloning or hard linking cannot and should not be done the file
// will be copied instead. Throws `core::Error` on error.
void clone_hard_link_or_copy_file(const Config& config,
const std::string& source,
const std::string& dest,
bool via_tmp_file = false);
// Compute the length of the longest directory path that is common to paths
// `dir` (a directory) and `path` (any path).
size_t common_dir_prefix_length(std::string_view dir, std::string_view path);
// Copy all data from `fd_in` to `fd_out`. Throws `core::Error` on error.
void copy_fd(int fd_in, int fd_out);
// Copy a file from `src` to `dest`. If via_tmp_file is true, `src` is copied to
// a temporary file and then renamed to dest. Throws `core::Error` on error.
void copy_file(const std::string& src,
const std::string& dest,
bool via_tmp_file = false);
// Create a directory if needed, including its parents if needed.
//
// Returns true if the directory exists or could be created, otherwise false.
bool create_dir(std::string_view dir);
// Get directory name of path.
std::string_view dir_name(std::string_view path);
// Like create_dir but throws Fatal on error.
void ensure_dir_exists(std::string_view dir);
// Expand all instances of $VAR or ${VAR}, where VAR is an environment variable,
// in `str`. Throws `core::Error` if one of the environment variables.
[[nodiscard]] std::string expand_environment_variables(const std::string& str);
// Extends file size to at least new_size by calling posix_fallocate() if
// supported, otherwise by writing zeros last to the file.
//
// Note that existing holes are not filled in case posix_fallocate() is not
// supported.
//
// Returns 0 on success, an error number otherwise.
int fallocate(int fd, long new_size);
// Format `argv` as a simple string for logging purposes. That is, the result is
// not intended to be machine parsable. `argv` must be terminated by a nullptr.
std::string format_argv_for_logging(const char* const* argv);
// Format a hexadecimal string representing `size` bytes of `data`. The returned
// string will be `2 * size` long.
std::string format_base16(const uint8_t* data, size_t size);
// Format a lowercase base32hex string representing `size` bytes of `data`. No
// padding characters will be added.
std::string format_base32hex(const uint8_t* data, size_t size);
// Return current working directory (CWD) as returned from getcwd(3) (i.e.,
// normalized path without symlink parts). Returns the empty string on error.
std::string get_actual_cwd();
// Return current working directory (CWD) by reading the environment variable
// PWD (thus keeping any symlink parts in the path and potentially ".." or "//"
// parts). If PWD does not resolve to the same i-node as `actual_cwd` then
// `actual_cwd` is returned instead.
std::string get_apparent_cwd(const std::string& actual_cwd);
// Return the file extension (including the dot) as a view into `path`. If
// `path` has no file extension, an empty string_view is returned.
std::string_view get_extension(std::string_view path);
// Return the current user's home directory, or throw `Fatal` if it can't
// be determined.
std::string get_home_directory();
// Return a static string with the current hostname.
const char* get_hostname();
// Compute a relative path from `dir` (an absolute path to a directory) to
// `path` (an absolute path). Assumes that both `dir` and `path` are normalized.
// The algorithm does *not* follow symlinks, so the result may not actually
// resolve to the same file as `path`.
std::string get_relative_path(std::string_view dir, std::string_view path);
// Get process umask.
mode_t get_umask();
// Hard-link `oldpath` to `newpath`. Throws `core::Error` on error.
void hard_link(const std::string& oldpath, const std::string& newpath);
// Write bytes in big endian order from an integer value.
//
// Parameters:
// - value: Integer value to read.
// - buffer: Buffer to write bytes to.
template<typename T>
void
int_to_big_endian(T value, uint8_t* buffer)
{
for (size_t i = 0; i < sizeof(T); ++i) {
buffer[sizeof(T) - i - 1] = value & 0xFF;
value >>= 8;
}
}
template<>
inline void
int_to_big_endian(uint8_t value, uint8_t* buffer)
{
buffer[0] = value;
}
template<>
inline void
int_to_big_endian(int8_t value, uint8_t* buffer)
{
buffer[0] = value;
}
// Determine if `path` is an absolute path with prefix, returning the split
// point.
std::optional<size_t> is_absolute_path_with_prefix(std::string_view path);
// Detmine if `path` refers to a ccache executable.
bool is_ccache_executable(std::string_view path);
// Return whether `ch` is a directory separator, i.e. '/' on POSIX systems and
// '/' or '\\' on Windows systems.
inline bool
is_dir_separator(char ch)
{
return ch == '/'
#ifdef _WIN32
|| ch == '\\'
#endif
;
}
// Return whether `path` represents a precompiled header (see "Precompiled
// Headers" in GCC docs).
bool is_precompiled_header(std::string_view path);
// Thread-safe version of `localtime(3)`. If `time` is not specified the current
// time of day is used.
std::optional<tm> localtime(std::optional<util::TimePoint> time = {});
// Construct a normalized native path.
//
// Example:
//
// std::string path = Util::make_path("usr", "local", "bin");
template<typename... T>
std::string
make_path(const T&... args)
{
return (std::filesystem::path{} / ... / args).lexically_normal().string();
}
// Make a relative path from current working directory (either `actual_cwd` or
// `apparent_cwd`) to `path` if `path` is under `base_dir`.
std::string make_relative_path(const std::string& base_dir,
const std::string& actual_cwd,
const std::string& apparent_cwd,
std::string_view path);
// Like above but with base directory and apparent/actual CWD taken from `ctx`.
std::string make_relative_path(const Context& ctx, std::string_view path);
// Return whether `path` is equal to `dir_prefix_or_file` or if
// `dir_prefix_or_file` is a directory prefix of `path`.
bool matches_dir_prefix_or_file(std::string_view dir_prefix_or_file,
std::string_view path);
// Normalize absolute path `path`, not taking symlinks into account.
//
// Normalization here means syntactically removing redundant slashes and
// resolving "." and ".." parts. The algorithm does however *not* follow
// symlinks, so the result may not actually resolve to the same filesystem entry
// as `path` (nor to any existing file system entry for that matter).
//
// On Windows: Backslashes are replaced with forward slashes.
std::string normalize_abstract_absolute_path(std::string_view path);
// Like normalize_abstract_absolute_path, but returns `path` unchanged if the
// normalized result doesn't resolve to the same file system entry as `path`.
std::string normalize_concrete_absolute_path(const std::string& path);
// Parse `duration`, an unsigned integer with d (days) or s (seconds) suffix,
// into seconds. Throws `core::Error` on error.
uint64_t parse_duration(std::string_view duration);
#ifndef _WIN32
// Like readlink(2) but returns the string (or the empty string on failure).
std::string read_link(const std::string& path);
#endif
// Return a normalized absolute path of `path`. On error (e.g. if the `path`
// doesn't exist) the empty string is returned if return_empty_on_error is true,
// otherwise `path` unmodified.
std::string real_path(const std::string& path,
bool return_empty_on_error = false);
// Return a view into `path` containing the given path without the filename
// extension as determined by `get_extension()`.
std::string_view remove_extension(std::string_view path);
// Rename `oldpath` to `newpath` (deleting `newpath`). Throws `core::Error` on
// error.
void rename(const std::string& oldpath, const std::string& newpath);
// Send `text` to file descriptor `fd`, optionally stripping ANSI color
// sequences if `ctx.args_info.strip_diagnostics_colors` is true and rewriting
// paths to absolute if `ctx.config.absolute_paths_in_stderr` is true. Throws
// `core::Error` on error.
void send_to_fd(const Context& ctx, std::string_view text, int fd);
// Set the FD_CLOEXEC on file descriptor `fd`. This is a NOP on Windows.
void set_cloexec_flag(int fd);
// Set process umask. Returns the previous mask.
mode_t set_umask(mode_t mask);
// Set environment variable `name` to `value`.
void setenv(const std::string& name, const std::string& value);
// Return size change in KiB between `old_stat` and `new_stat`.
inline int64_t
size_change_kibibyte(const Stat& old_stat, const Stat& new_stat)
{
return (static_cast<int64_t>(new_stat.size_on_disk())
- static_cast<int64_t>(old_stat.size_on_disk()))
/ 1024;
}
// Split `string` into tokens at any of the characters in `separators`. These
// tokens are views into `string`. `separators` must neither be the empty string
// nor a nullptr.
std::vector<std::string_view>
split_into_views(std::string_view string,
const char* separators,
util::Tokenizer::Mode mode = util::Tokenizer::Mode::skip_empty,
util::Tokenizer::IncludeDelimiter include_delimiter =
util::Tokenizer::IncludeDelimiter::no);
// Same as `split_into_views` but the tokens are copied from `string`.
std::vector<std::string> split_into_strings(
std::string_view string,
const char* separators,
util::Tokenizer::Mode mode = util::Tokenizer::Mode::skip_empty,
util::Tokenizer::IncludeDelimiter include_delimiter =
util::Tokenizer::IncludeDelimiter::no);
// Returns a copy of string with the specified ANSI CSI sequences removed.
[[nodiscard]] std::string strip_ansi_csi_seqs(std::string_view string);
// Convert a string to lowercase.
[[nodiscard]] std::string to_lowercase(std::string_view string);
// Traverse `path` recursively (postorder, i.e. files are visited before their
// parent directory).
//
// Throws core::Error on error.
void traverse(const std::string& path, const TraverseVisitor& visitor);
// Remove `path` (non-directory), NFS safe. Logs according to `unlink_log`.
//
// Returns whether removal was successful. A nonexistent `path` is considered a
// failure.
bool unlink_safe(const std::string& path,
UnlinkLog unlink_log = UnlinkLog::log_failure);
// Remove `path` (non-directory), NFS hazardous. Use only for files that will
// not exist on other systems. Logs according to `unlink_log`.
//
// Returns whether removal was successful. A nonexistent `path` is considered
// successful.
bool unlink_tmp(const std::string& path,
UnlinkLog unlink_log = UnlinkLog::log_failure);
// Unset environment variable `name`.
void unsetenv(const std::string& name);
// Remove `path` (and its contents if it's a directory). A nonexistent path is
// not considered an error.
//
// Throws core::Error on error.
void wipe_path(const std::string& path);
} // namespace Util
|