[PowerPC][NFC] Cleanup PPCCTRLoopsVerify pass
[llvm-project.git] / libcxx / src / filesystem / filesystem_common.h
bloba82f6511368a49c6ffb94dd247bf30404580f8d2
1 //===----------------------------------------------------------------------===////
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===////
9 #ifndef FILESYSTEM_COMMON_H
10 #define FILESYSTEM_COMMON_H
12 #include "__config"
13 #include "filesystem"
14 #include "array"
15 #include "chrono"
16 #include "climits"
17 #include "cstdlib"
18 #include "ctime"
20 #include <unistd.h>
21 #include <sys/stat.h>
22 #include <sys/statvfs.h>
23 #include <sys/time.h> // for ::utimes as used in __last_write_time
24 #include <fcntl.h> /* values for fchmodat */
26 #include "../include/apple_availability.h"
28 #if !defined(__APPLE__)
29 // We can use the presence of UTIME_OMIT to detect platforms that provide
30 // utimensat.
31 #if defined(UTIME_OMIT)
32 #define _LIBCPP_USE_UTIMENSAT
33 #endif
34 #endif
36 #if defined(__GNUC__)
37 #pragma GCC diagnostic push
38 #pragma GCC diagnostic ignored "-Wunused-function"
39 #endif
41 _LIBCPP_BEGIN_NAMESPACE_FILESYSTEM
43 namespace detail {
44 namespace {
46 static string format_string_imp(const char* msg, ...) {
47 // we might need a second shot at this, so pre-emptivly make a copy
48 struct GuardVAList {
49 va_list& target;
50 bool active = true;
51 GuardVAList(va_list& tgt) : target(tgt), active(true) {}
52 void clear() {
53 if (active)
54 va_end(target);
55 active = false;
57 ~GuardVAList() {
58 if (active)
59 va_end(target);
62 va_list args;
63 va_start(args, msg);
64 GuardVAList args_guard(args);
66 va_list args_cp;
67 va_copy(args_cp, args);
68 GuardVAList args_copy_guard(args_cp);
70 std::string result;
72 array<char, 256> local_buff;
73 size_t size_with_null = local_buff.size();
74 auto ret = ::vsnprintf(local_buff.data(), size_with_null, msg, args_cp);
76 args_copy_guard.clear();
78 // handle empty expansion
79 if (ret == 0)
80 return result;
81 if (static_cast<size_t>(ret) < size_with_null) {
82 result.assign(local_buff.data(), static_cast<size_t>(ret));
83 return result;
86 // we did not provide a long enough buffer on our first attempt. The
87 // return value is the number of bytes (excluding the null byte) that are
88 // needed for formatting.
89 size_with_null = static_cast<size_t>(ret) + 1;
90 result.__resize_default_init(size_with_null - 1);
91 ret = ::vsnprintf(&result[0], size_with_null, msg, args);
92 _LIBCPP_ASSERT(static_cast<size_t>(ret) == (size_with_null - 1), "TODO");
94 return result;
97 const char* unwrap(string const& s) { return s.c_str(); }
98 const char* unwrap(path const& p) { return p.native().c_str(); }
99 template <class Arg>
100 Arg const& unwrap(Arg const& a) {
101 static_assert(!is_class<Arg>::value, "cannot pass class here");
102 return a;
105 template <class... Args>
106 string format_string(const char* fmt, Args const&... args) {
107 return format_string_imp(fmt, unwrap(args)...);
110 error_code capture_errno() {
111 _LIBCPP_ASSERT(errno, "Expected errno to be non-zero");
112 return error_code(errno, generic_category());
115 template <class T>
116 T error_value();
117 template <>
118 _LIBCPP_CONSTEXPR_AFTER_CXX11 void error_value<void>() {}
119 template <>
120 bool error_value<bool>() {
121 return false;
123 template <>
124 uintmax_t error_value<uintmax_t>() {
125 return uintmax_t(-1);
127 template <>
128 _LIBCPP_CONSTEXPR_AFTER_CXX11 file_time_type error_value<file_time_type>() {
129 return file_time_type::min();
131 template <>
132 path error_value<path>() {
133 return {};
136 template <class T>
137 struct ErrorHandler {
138 const char* func_name_;
139 error_code* ec_ = nullptr;
140 const path* p1_ = nullptr;
141 const path* p2_ = nullptr;
143 ErrorHandler(const char* fname, error_code* ec, const path* p1 = nullptr,
144 const path* p2 = nullptr)
145 : func_name_(fname), ec_(ec), p1_(p1), p2_(p2) {
146 if (ec_)
147 ec_->clear();
150 T report(const error_code& ec) const {
151 if (ec_) {
152 *ec_ = ec;
153 return error_value<T>();
155 string what = string("in ") + func_name_;
156 switch (bool(p1_) + bool(p2_)) {
157 case 0:
158 __throw_filesystem_error(what, ec);
159 case 1:
160 __throw_filesystem_error(what, *p1_, ec);
161 case 2:
162 __throw_filesystem_error(what, *p1_, *p2_, ec);
164 _LIBCPP_UNREACHABLE();
167 template <class... Args>
168 T report(const error_code& ec, const char* msg, Args const&... args) const {
169 if (ec_) {
170 *ec_ = ec;
171 return error_value<T>();
173 string what =
174 string("in ") + func_name_ + ": " + format_string(msg, args...);
175 switch (bool(p1_) + bool(p2_)) {
176 case 0:
177 __throw_filesystem_error(what, ec);
178 case 1:
179 __throw_filesystem_error(what, *p1_, ec);
180 case 2:
181 __throw_filesystem_error(what, *p1_, *p2_, ec);
183 _LIBCPP_UNREACHABLE();
186 T report(errc const& err) const { return report(make_error_code(err)); }
188 template <class... Args>
189 T report(errc const& err, const char* msg, Args const&... args) const {
190 return report(make_error_code(err), msg, args...);
193 private:
194 ErrorHandler(ErrorHandler const&) = delete;
195 ErrorHandler& operator=(ErrorHandler const&) = delete;
198 using chrono::duration;
199 using chrono::duration_cast;
201 using TimeSpec = struct timespec;
202 using TimeVal = struct timeval;
203 using StatT = struct stat;
205 template <class FileTimeT, class TimeT,
206 bool IsFloat = is_floating_point<typename FileTimeT::rep>::value>
207 struct time_util_base {
208 using rep = typename FileTimeT::rep;
209 using fs_duration = typename FileTimeT::duration;
210 using fs_seconds = duration<rep>;
211 using fs_nanoseconds = duration<rep, nano>;
212 using fs_microseconds = duration<rep, micro>;
214 static constexpr rep max_seconds =
215 duration_cast<fs_seconds>(FileTimeT::duration::max()).count();
217 static constexpr rep max_nsec =
218 duration_cast<fs_nanoseconds>(FileTimeT::duration::max() -
219 fs_seconds(max_seconds))
220 .count();
222 static constexpr rep min_seconds =
223 duration_cast<fs_seconds>(FileTimeT::duration::min()).count();
225 static constexpr rep min_nsec_timespec =
226 duration_cast<fs_nanoseconds>(
227 (FileTimeT::duration::min() - fs_seconds(min_seconds)) +
228 fs_seconds(1))
229 .count();
231 private:
232 #if _LIBCPP_STD_VER > 11 && !defined(_LIBCPP_HAS_NO_CXX14_CONSTEXPR)
233 static constexpr fs_duration get_min_nsecs() {
234 return duration_cast<fs_duration>(
235 fs_nanoseconds(min_nsec_timespec) -
236 duration_cast<fs_nanoseconds>(fs_seconds(1)));
238 // Static assert that these values properly round trip.
239 static_assert(fs_seconds(min_seconds) + get_min_nsecs() ==
240 FileTimeT::duration::min(),
241 "value doesn't roundtrip");
243 static constexpr bool check_range() {
244 // This kinda sucks, but it's what happens when we don't have __int128_t.
245 if (sizeof(TimeT) == sizeof(rep)) {
246 typedef duration<long long, ratio<3600 * 24 * 365> > Years;
247 return duration_cast<Years>(fs_seconds(max_seconds)) > Years(250) &&
248 duration_cast<Years>(fs_seconds(min_seconds)) < Years(-250);
250 return max_seconds >= numeric_limits<TimeT>::max() &&
251 min_seconds <= numeric_limits<TimeT>::min();
253 static_assert(check_range(), "the representable range is unacceptable small");
254 #endif
257 template <class FileTimeT, class TimeT>
258 struct time_util_base<FileTimeT, TimeT, true> {
259 using rep = typename FileTimeT::rep;
260 using fs_duration = typename FileTimeT::duration;
261 using fs_seconds = duration<rep>;
262 using fs_nanoseconds = duration<rep, nano>;
263 using fs_microseconds = duration<rep, micro>;
265 static const rep max_seconds;
266 static const rep max_nsec;
267 static const rep min_seconds;
268 static const rep min_nsec_timespec;
271 template <class FileTimeT, class TimeT>
272 const typename FileTimeT::rep
273 time_util_base<FileTimeT, TimeT, true>::max_seconds =
274 duration_cast<fs_seconds>(FileTimeT::duration::max()).count();
276 template <class FileTimeT, class TimeT>
277 const typename FileTimeT::rep time_util_base<FileTimeT, TimeT, true>::max_nsec =
278 duration_cast<fs_nanoseconds>(FileTimeT::duration::max() -
279 fs_seconds(max_seconds))
280 .count();
282 template <class FileTimeT, class TimeT>
283 const typename FileTimeT::rep
284 time_util_base<FileTimeT, TimeT, true>::min_seconds =
285 duration_cast<fs_seconds>(FileTimeT::duration::min()).count();
287 template <class FileTimeT, class TimeT>
288 const typename FileTimeT::rep
289 time_util_base<FileTimeT, TimeT, true>::min_nsec_timespec =
290 duration_cast<fs_nanoseconds>((FileTimeT::duration::min() -
291 fs_seconds(min_seconds)) +
292 fs_seconds(1))
293 .count();
295 template <class FileTimeT, class TimeT, class TimeSpecT>
296 struct time_util : time_util_base<FileTimeT, TimeT> {
297 using Base = time_util_base<FileTimeT, TimeT>;
298 using Base::max_nsec;
299 using Base::max_seconds;
300 using Base::min_nsec_timespec;
301 using Base::min_seconds;
303 using typename Base::fs_duration;
304 using typename Base::fs_microseconds;
305 using typename Base::fs_nanoseconds;
306 using typename Base::fs_seconds;
308 public:
309 template <class CType, class ChronoType>
310 static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool checked_set(CType* out,
311 ChronoType time) {
312 using Lim = numeric_limits<CType>;
313 if (time > Lim::max() || time < Lim::min())
314 return false;
315 *out = static_cast<CType>(time);
316 return true;
319 static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool is_representable(TimeSpecT tm) {
320 if (tm.tv_sec >= 0) {
321 return tm.tv_sec < max_seconds ||
322 (tm.tv_sec == max_seconds && tm.tv_nsec <= max_nsec);
323 } else if (tm.tv_sec == (min_seconds - 1)) {
324 return tm.tv_nsec >= min_nsec_timespec;
325 } else {
326 return tm.tv_sec >= min_seconds;
330 static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool is_representable(FileTimeT tm) {
331 auto secs = duration_cast<fs_seconds>(tm.time_since_epoch());
332 auto nsecs = duration_cast<fs_nanoseconds>(tm.time_since_epoch() - secs);
333 if (nsecs.count() < 0) {
334 secs = secs + fs_seconds(1);
335 nsecs = nsecs + fs_seconds(1);
337 using TLim = numeric_limits<TimeT>;
338 if (secs.count() >= 0)
339 return secs.count() <= TLim::max();
340 return secs.count() >= TLim::min();
343 static _LIBCPP_CONSTEXPR_AFTER_CXX11 FileTimeT
344 convert_from_timespec(TimeSpecT tm) {
345 if (tm.tv_sec >= 0 || tm.tv_nsec == 0) {
346 return FileTimeT(fs_seconds(tm.tv_sec) +
347 duration_cast<fs_duration>(fs_nanoseconds(tm.tv_nsec)));
348 } else { // tm.tv_sec < 0
349 auto adj_subsec = duration_cast<fs_duration>(fs_seconds(1) -
350 fs_nanoseconds(tm.tv_nsec));
351 auto Dur = fs_seconds(tm.tv_sec + 1) - adj_subsec;
352 return FileTimeT(Dur);
356 template <class SubSecT>
357 static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool
358 set_times_checked(TimeT* sec_out, SubSecT* subsec_out, FileTimeT tp) {
359 auto dur = tp.time_since_epoch();
360 auto sec_dur = duration_cast<fs_seconds>(dur);
361 auto subsec_dur = duration_cast<fs_nanoseconds>(dur - sec_dur);
362 // The tv_nsec and tv_usec fields must not be negative so adjust accordingly
363 if (subsec_dur.count() < 0) {
364 if (sec_dur.count() > min_seconds) {
365 sec_dur = sec_dur - fs_seconds(1);
366 subsec_dur = subsec_dur + fs_seconds(1);
367 } else {
368 subsec_dur = fs_nanoseconds::zero();
371 return checked_set(sec_out, sec_dur.count()) &&
372 checked_set(subsec_out, subsec_dur.count());
374 static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool convert_to_timespec(TimeSpecT& dest,
375 FileTimeT tp) {
376 if (!is_representable(tp))
377 return false;
378 return set_times_checked(&dest.tv_sec, &dest.tv_nsec, tp);
382 using fs_time = time_util<file_time_type, time_t, TimeSpec>;
384 #if defined(__APPLE__)
385 inline TimeSpec extract_mtime(StatT const& st) { return st.st_mtimespec; }
386 inline TimeSpec extract_atime(StatT const& st) { return st.st_atimespec; }
387 #elif defined(__MVS__)
388 inline TimeSpec extract_mtime(StatT const& st) {
389 TimeSpec TS = {st.st_mtime, 0};
390 return TS;
392 inline TimeSpec extract_atime(StatT const& st) {
393 TimeSpec TS = {st.st_atime, 0};
394 return TS;
396 #else
397 inline TimeSpec extract_mtime(StatT const& st) { return st.st_mtim; }
398 inline TimeSpec extract_atime(StatT const& st) { return st.st_atim; }
399 #endif
401 inline TimeVal make_timeval(TimeSpec const& ts) {
402 using namespace chrono;
403 auto Convert = [](long nsec) {
404 using int_type = decltype(std::declval<TimeVal>().tv_usec);
405 auto dur = duration_cast<microseconds>(nanoseconds(nsec)).count();
406 return static_cast<int_type>(dur);
408 TimeVal TV = {};
409 TV.tv_sec = ts.tv_sec;
410 TV.tv_usec = Convert(ts.tv_nsec);
411 return TV;
414 inline bool posix_utimes(const path& p, std::array<TimeSpec, 2> const& TS,
415 error_code& ec) {
416 TimeVal ConvertedTS[2] = {make_timeval(TS[0]), make_timeval(TS[1])};
417 if (::utimes(p.c_str(), ConvertedTS) == -1) {
418 ec = capture_errno();
419 return true;
421 return false;
424 #if defined(_LIBCPP_USE_UTIMENSAT)
425 bool posix_utimensat(const path& p, std::array<TimeSpec, 2> const& TS,
426 error_code& ec) {
427 if (::utimensat(AT_FDCWD, p.c_str(), TS.data(), 0) == -1) {
428 ec = capture_errno();
429 return true;
431 return false;
433 #endif
435 bool set_file_times(const path& p, std::array<TimeSpec, 2> const& TS,
436 error_code& ec) {
437 #if !defined(_LIBCPP_USE_UTIMENSAT)
438 return posix_utimes(p, TS, ec);
439 #else
440 return posix_utimensat(p, TS, ec);
441 #endif
444 } // namespace
445 } // end namespace detail
447 _LIBCPP_END_NAMESPACE_FILESYSTEM
449 #endif // FILESYSTEM_COMMON_H