Line data Source code
1 : // Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
2 : // Distributed under the MIT License (http://opensource.org/licenses/MIT)
3 :
4 : #pragma once
5 :
6 : #ifndef SPDLOG_HEADER_ONLY
7 : # include <spdlog/details/os.h>
8 : #endif
9 :
10 : #include <spdlog/common.h>
11 :
12 : #include <algorithm>
13 : #include <chrono>
14 : #include <cstdio>
15 : #include <cstdlib>
16 : #include <cstring>
17 : #include <ctime>
18 : #include <string>
19 : #include <thread>
20 : #include <array>
21 : #include <sys/stat.h>
22 : #include <sys/types.h>
23 :
24 : #ifdef _WIN32
25 :
26 : # include <io.h> // for _get_osfhandle, _isatty, _fileno
27 : # include <process.h> // for _get_pid
28 : # include <spdlog/details/windows_include.h>
29 : # include <fileapi.h> // for FlushFileBuffers
30 :
31 : # ifdef __MINGW32__
32 : # include <share.h>
33 : # endif
34 :
35 : # if defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)
36 : # include <limits>
37 : # include <cassert>
38 : # endif
39 :
40 : # include <direct.h> // for _mkdir/_wmkdir
41 :
42 : #else // unix
43 :
44 : # include <fcntl.h>
45 : # include <unistd.h>
46 :
47 : # ifdef __linux__
48 : # include <sys/syscall.h> //Use gettid() syscall under linux to get thread id
49 :
50 : # elif defined(_AIX)
51 : # include <pthread.h> // for pthread_getthrds_np
52 :
53 : # elif defined(__DragonFly__) || defined(__FreeBSD__)
54 : # include <pthread_np.h> // for pthread_getthreadid_np
55 :
56 : # elif defined(__NetBSD__)
57 : # include <lwp.h> // for _lwp_self
58 :
59 : # elif defined(__sun)
60 : # include <thread.h> // for thr_self
61 : # endif
62 :
63 : #endif // unix
64 :
65 : #if defined __APPLE__
66 : # include <AvailabilityMacros.h>
67 : #endif
68 :
69 : #ifndef __has_feature // Clang - feature checking macros.
70 : # define __has_feature(x) 0 // Compatibility with non-clang compilers.
71 : #endif
72 :
73 : namespace spdlog {
74 : namespace details {
75 : namespace os {
76 :
77 0 : SPDLOG_INLINE spdlog::log_clock::time_point now() SPDLOG_NOEXCEPT
78 : {
79 :
80 : #if defined __linux__ && defined SPDLOG_CLOCK_COARSE
81 : timespec ts;
82 : ::clock_gettime(CLOCK_REALTIME_COARSE, &ts);
83 : return std::chrono::time_point<log_clock, typename log_clock::duration>(
84 : std::chrono::duration_cast<typename log_clock::duration>(std::chrono::seconds(ts.tv_sec) + std::chrono::nanoseconds(ts.tv_nsec)));
85 :
86 : #else
87 0 : return log_clock::now();
88 : #endif
89 : }
90 0 : SPDLOG_INLINE std::tm localtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT
91 : {
92 :
93 : #ifdef _WIN32
94 : std::tm tm;
95 : ::localtime_s(&tm, &time_tt);
96 : #else
97 0 : std::tm tm;
98 0 : ::localtime_r(&time_tt, &tm);
99 : #endif
100 0 : return tm;
101 : }
102 :
103 : SPDLOG_INLINE std::tm localtime() SPDLOG_NOEXCEPT
104 : {
105 : std::time_t now_t = ::time(nullptr);
106 : return localtime(now_t);
107 : }
108 :
109 : SPDLOG_INLINE std::tm gmtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT
110 : {
111 :
112 : #ifdef _WIN32
113 : std::tm tm;
114 : ::gmtime_s(&tm, &time_tt);
115 : #else
116 : std::tm tm;
117 : ::gmtime_r(&time_tt, &tm);
118 : #endif
119 : return tm;
120 : }
121 :
122 : SPDLOG_INLINE std::tm gmtime() SPDLOG_NOEXCEPT
123 : {
124 : std::time_t now_t = ::time(nullptr);
125 : return gmtime(now_t);
126 : }
127 :
128 : // fopen_s on non windows for writing
129 : SPDLOG_INLINE bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mode)
130 : {
131 : #ifdef _WIN32
132 : # ifdef SPDLOG_WCHAR_FILENAMES
133 : *fp = ::_wfsopen((filename.c_str()), mode.c_str(), _SH_DENYNO);
134 : # else
135 : *fp = ::_fsopen((filename.c_str()), mode.c_str(), _SH_DENYNO);
136 : # endif
137 : # if defined(SPDLOG_PREVENT_CHILD_FD)
138 : if (*fp != nullptr)
139 : {
140 : auto file_handle = reinterpret_cast<HANDLE>(_get_osfhandle(::_fileno(*fp)));
141 : if (!::SetHandleInformation(file_handle, HANDLE_FLAG_INHERIT, 0))
142 : {
143 : ::fclose(*fp);
144 : *fp = nullptr;
145 : }
146 : }
147 : # endif
148 : #else // unix
149 : # if defined(SPDLOG_PREVENT_CHILD_FD)
150 : const int mode_flag = mode == SPDLOG_FILENAME_T("ab") ? O_APPEND : O_TRUNC;
151 : const int fd = ::open((filename.c_str()), O_CREAT | O_WRONLY | O_CLOEXEC | mode_flag, mode_t(0644));
152 : if (fd == -1)
153 : {
154 : return true;
155 : }
156 : *fp = ::fdopen(fd, mode.c_str());
157 : if (*fp == nullptr)
158 : {
159 : ::close(fd);
160 : }
161 : # else
162 : *fp = ::fopen((filename.c_str()), mode.c_str());
163 : # endif
164 : #endif
165 :
166 : return *fp == nullptr;
167 : }
168 :
169 : SPDLOG_INLINE int remove(const filename_t &filename) SPDLOG_NOEXCEPT
170 : {
171 : #if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
172 : return ::_wremove(filename.c_str());
173 : #else
174 : return std::remove(filename.c_str());
175 : #endif
176 : }
177 :
178 : SPDLOG_INLINE int remove_if_exists(const filename_t &filename) SPDLOG_NOEXCEPT
179 : {
180 : return path_exists(filename) ? remove(filename) : 0;
181 : }
182 :
183 : SPDLOG_INLINE int rename(const filename_t &filename1, const filename_t &filename2) SPDLOG_NOEXCEPT
184 : {
185 : #if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
186 : return ::_wrename(filename1.c_str(), filename2.c_str());
187 : #else
188 : return std::rename(filename1.c_str(), filename2.c_str());
189 : #endif
190 : }
191 :
192 : // Return true if path exists (file or directory)
193 : SPDLOG_INLINE bool path_exists(const filename_t &filename) SPDLOG_NOEXCEPT
194 : {
195 : #ifdef _WIN32
196 : # ifdef SPDLOG_WCHAR_FILENAMES
197 : auto attribs = ::GetFileAttributesW(filename.c_str());
198 : # else
199 : auto attribs = ::GetFileAttributesA(filename.c_str());
200 : # endif
201 : return attribs != INVALID_FILE_ATTRIBUTES;
202 : #else // common linux/unix all have the stat system call
203 : struct stat buffer;
204 : return (::stat(filename.c_str(), &buffer) == 0);
205 : #endif
206 : }
207 :
208 : #ifdef _MSC_VER
209 : // avoid warning about unreachable statement at the end of filesize()
210 : # pragma warning(push)
211 : # pragma warning(disable : 4702)
212 : #endif
213 :
214 : // Return file size according to open FILE* object
215 : SPDLOG_INLINE size_t filesize(FILE *f)
216 : {
217 : if (f == nullptr)
218 : {
219 : throw_spdlog_ex("Failed getting file size. fd is null");
220 : }
221 : #if defined(_WIN32) && !defined(__CYGWIN__)
222 : int fd = ::_fileno(f);
223 : # if defined(_WIN64) // 64 bits
224 : __int64 ret = ::_filelengthi64(fd);
225 : if (ret >= 0)
226 : {
227 : return static_cast<size_t>(ret);
228 : }
229 :
230 : # else // windows 32 bits
231 : long ret = ::_filelength(fd);
232 : if (ret >= 0)
233 : {
234 : return static_cast<size_t>(ret);
235 : }
236 : # endif
237 :
238 : #else // unix
239 : // OpenBSD and AIX doesn't compile with :: before the fileno(..)
240 : # if defined(__OpenBSD__) || defined(_AIX)
241 : int fd = fileno(f);
242 : # else
243 : int fd = ::fileno(f);
244 : # endif
245 : // 64 bits(but not in osx, linux/musl or cygwin, where fstat64 is deprecated)
246 : # if ((defined(__linux__) && defined(__GLIBC__)) || defined(__sun) || defined(_AIX)) && (defined(__LP64__) || defined(_LP64))
247 : struct stat64 st;
248 : if (::fstat64(fd, &st) == 0)
249 : {
250 : return static_cast<size_t>(st.st_size);
251 : }
252 : # else // other unix or linux 32 bits or cygwin
253 : struct stat st;
254 : if (::fstat(fd, &st) == 0)
255 : {
256 : return static_cast<size_t>(st.st_size);
257 : }
258 : # endif
259 : #endif
260 : throw_spdlog_ex("Failed getting file size from fd", errno);
261 : return 0; // will not be reached.
262 : }
263 :
264 : #ifdef _MSC_VER
265 : # pragma warning(pop)
266 : #endif
267 :
268 : // Return utc offset in minutes or throw spdlog_ex on failure
269 0 : SPDLOG_INLINE int utc_minutes_offset(const std::tm &tm)
270 : {
271 :
272 : #ifdef _WIN32
273 : # if _WIN32_WINNT < _WIN32_WINNT_WS08
274 : TIME_ZONE_INFORMATION tzinfo;
275 : auto rv = ::GetTimeZoneInformation(&tzinfo);
276 : # else
277 : DYNAMIC_TIME_ZONE_INFORMATION tzinfo;
278 : auto rv = ::GetDynamicTimeZoneInformation(&tzinfo);
279 : # endif
280 : if (rv == TIME_ZONE_ID_INVALID)
281 : throw_spdlog_ex("Failed getting timezone info. ", errno);
282 :
283 : int offset = -tzinfo.Bias;
284 : if (tm.tm_isdst)
285 : {
286 : offset -= tzinfo.DaylightBias;
287 : }
288 : else
289 : {
290 : offset -= tzinfo.StandardBias;
291 : }
292 : return offset;
293 : #else
294 :
295 : # if defined(sun) || defined(__sun) || defined(_AIX) || (defined(__NEWLIB__) && !defined(__TM_GMTOFF)) || \
296 : (!defined(_BSD_SOURCE) && !defined(_GNU_SOURCE))
297 : // 'tm_gmtoff' field is BSD extension and it's missing on SunOS/Solaris
298 : struct helper
299 : {
300 : static long int calculate_gmt_offset(const std::tm &localtm = details::os::localtime(), const std::tm &gmtm = details::os::gmtime())
301 : {
302 : int local_year = localtm.tm_year + (1900 - 1);
303 : int gmt_year = gmtm.tm_year + (1900 - 1);
304 :
305 : long int days = (
306 : // difference in day of year
307 : localtm.tm_yday -
308 : gmtm.tm_yday
309 :
310 : // + intervening leap days
311 : + ((local_year >> 2) - (gmt_year >> 2)) - (local_year / 100 - gmt_year / 100) +
312 : ((local_year / 100 >> 2) - (gmt_year / 100 >> 2))
313 :
314 : // + difference in years * 365 */
315 : + static_cast<long int>(local_year - gmt_year) * 365);
316 :
317 : long int hours = (24 * days) + (localtm.tm_hour - gmtm.tm_hour);
318 : long int mins = (60 * hours) + (localtm.tm_min - gmtm.tm_min);
319 : long int secs = (60 * mins) + (localtm.tm_sec - gmtm.tm_sec);
320 :
321 : return secs;
322 : }
323 : };
324 :
325 : auto offset_seconds = helper::calculate_gmt_offset(tm);
326 : # else
327 0 : auto offset_seconds = tm.tm_gmtoff;
328 : # endif
329 :
330 0 : return static_cast<int>(offset_seconds / 60);
331 : #endif
332 : }
333 :
334 : // Return current thread id as size_t
335 : // It exists because the std::this_thread::get_id() is much slower(especially
336 : // under VS 2013)
337 0 : SPDLOG_INLINE size_t _thread_id() SPDLOG_NOEXCEPT
338 : {
339 : #ifdef _WIN32
340 : return static_cast<size_t>(::GetCurrentThreadId());
341 : #elif defined(__linux__)
342 : # if defined(__ANDROID__) && defined(__ANDROID_API__) && (__ANDROID_API__ < 21)
343 : # define SYS_gettid __NR_gettid
344 : # endif
345 0 : return static_cast<size_t>(::syscall(SYS_gettid));
346 : #elif defined(_AIX)
347 : struct __pthrdsinfo buf;
348 : int reg_size = 0;
349 : pthread_t pt = pthread_self();
350 : int retval = pthread_getthrds_np(&pt, PTHRDSINFO_QUERY_TID, &buf, sizeof(buf), NULL, ®_size);
351 : int tid = (!retval) ? buf.__pi_tid : 0;
352 : return static_cast<size_t>(tid);
353 : #elif defined(__DragonFly__) || defined(__FreeBSD__)
354 : return static_cast<size_t>(::pthread_getthreadid_np());
355 : #elif defined(__NetBSD__)
356 : return static_cast<size_t>(::_lwp_self());
357 : #elif defined(__OpenBSD__)
358 : return static_cast<size_t>(::getthrid());
359 : #elif defined(__sun)
360 : return static_cast<size_t>(::thr_self());
361 : #elif __APPLE__
362 : uint64_t tid;
363 : // There is no pthread_threadid_np prior to 10.6, and it is not supported on any PPC,
364 : // including 10.6.8 Rosetta. __POWERPC__ is Apple-specific define encompassing ppc and ppc64.
365 : # if (MAC_OS_X_VERSION_MAX_ALLOWED < 1060) || defined(__POWERPC__)
366 : tid = pthread_mach_thread_np(pthread_self());
367 : # elif MAC_OS_X_VERSION_MIN_REQUIRED < 1060
368 : if (&pthread_threadid_np)
369 : {
370 : pthread_threadid_np(nullptr, &tid);
371 : }
372 : else
373 : {
374 : tid = pthread_mach_thread_np(pthread_self());
375 : }
376 : # else
377 : pthread_threadid_np(nullptr, &tid);
378 : # endif
379 : return static_cast<size_t>(tid);
380 : #else // Default to standard C++11 (other Unix)
381 : return static_cast<size_t>(std::hash<std::thread::id>()(std::this_thread::get_id()));
382 : #endif
383 : }
384 :
385 : // Return current thread id as size_t (from thread local storage)
386 0 : SPDLOG_INLINE size_t thread_id() SPDLOG_NOEXCEPT
387 : {
388 : #if defined(SPDLOG_NO_TLS)
389 : return _thread_id();
390 : #else // cache thread id in tls
391 0 : static thread_local const size_t tid = _thread_id();
392 0 : return tid;
393 : #endif
394 : }
395 :
396 : // This is avoid msvc issue in sleep_for that happens if the clock changes.
397 : // See https://github.com/gabime/spdlog/issues/609
398 : SPDLOG_INLINE void sleep_for_millis(unsigned int milliseconds) SPDLOG_NOEXCEPT
399 : {
400 : #if defined(_WIN32)
401 : ::Sleep(milliseconds);
402 : #else
403 : std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds));
404 : #endif
405 : }
406 :
407 : // wchar support for windows file names (SPDLOG_WCHAR_FILENAMES must be defined)
408 : #if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
409 : SPDLOG_INLINE std::string filename_to_str(const filename_t &filename)
410 : {
411 : memory_buf_t buf;
412 : wstr_to_utf8buf(filename, buf);
413 : return SPDLOG_BUF_TO_STRING(buf);
414 : }
415 : #else
416 : SPDLOG_INLINE std::string filename_to_str(const filename_t &filename)
417 : {
418 : return filename;
419 : }
420 : #endif
421 :
422 0 : SPDLOG_INLINE int pid() SPDLOG_NOEXCEPT
423 : {
424 :
425 : #ifdef _WIN32
426 : return conditional_static_cast<int>(::GetCurrentProcessId());
427 : #else
428 0 : return conditional_static_cast<int>(::getpid());
429 : #endif
430 : }
431 :
432 : // Determine if the terminal supports colors
433 : // Based on: https://github.com/agauniyal/rang/
434 0 : SPDLOG_INLINE bool is_color_terminal() SPDLOG_NOEXCEPT
435 : {
436 : #ifdef _WIN32
437 : return true;
438 : #else
439 :
440 0 : static const bool result = []() {
441 0 : const char *env_colorterm_p = std::getenv("COLORTERM");
442 0 : if (env_colorterm_p != nullptr)
443 : {
444 : return true;
445 : }
446 :
447 0 : static constexpr std::array<const char *, 16> terms = {{"ansi", "color", "console", "cygwin", "gnome", "konsole", "kterm", "linux",
448 : "msys", "putty", "rxvt", "screen", "vt100", "xterm", "alacritty", "vt102"}};
449 :
450 0 : const char *env_term_p = std::getenv("TERM");
451 0 : if (env_term_p == nullptr)
452 : {
453 : return false;
454 : }
455 :
456 0 : return std::any_of(terms.begin(), terms.end(), [&](const char *term) { return std::strstr(env_term_p, term) != nullptr; });
457 0 : }();
458 :
459 0 : return result;
460 : #endif
461 : }
462 :
463 : // Determine if the terminal attached
464 : // Source: https://github.com/agauniyal/rang/
465 : SPDLOG_INLINE bool in_terminal(FILE *file) SPDLOG_NOEXCEPT
466 : {
467 :
468 : #ifdef _WIN32
469 : return ::_isatty(_fileno(file)) != 0;
470 : #else
471 : return ::isatty(fileno(file)) != 0;
472 : #endif
473 : }
474 :
475 : #if (defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)) && defined(_WIN32)
476 : SPDLOG_INLINE void wstr_to_utf8buf(wstring_view_t wstr, memory_buf_t &target)
477 : {
478 : if (wstr.size() > static_cast<size_t>((std::numeric_limits<int>::max)()) / 2 - 1)
479 : {
480 : throw_spdlog_ex("UTF-16 string is too big to be converted to UTF-8");
481 : }
482 :
483 : int wstr_size = static_cast<int>(wstr.size());
484 : if (wstr_size == 0)
485 : {
486 : target.resize(0);
487 : return;
488 : }
489 :
490 : int result_size = static_cast<int>(target.capacity());
491 : if ((wstr_size + 1) * 2 > result_size)
492 : {
493 : result_size = ::WideCharToMultiByte(CP_UTF8, 0, wstr.data(), wstr_size, NULL, 0, NULL, NULL);
494 : }
495 :
496 : if (result_size > 0)
497 : {
498 : target.resize(result_size);
499 : result_size = ::WideCharToMultiByte(CP_UTF8, 0, wstr.data(), wstr_size, target.data(), result_size, NULL, NULL);
500 :
501 : if (result_size > 0)
502 : {
503 : target.resize(result_size);
504 : return;
505 : }
506 : }
507 :
508 : throw_spdlog_ex(fmt_lib::format("WideCharToMultiByte failed. Last error: {}", ::GetLastError()));
509 : }
510 :
511 : SPDLOG_INLINE void utf8_to_wstrbuf(string_view_t str, wmemory_buf_t &target)
512 : {
513 : if (str.size() > static_cast<size_t>((std::numeric_limits<int>::max)()) - 1)
514 : {
515 : throw_spdlog_ex("UTF-8 string is too big to be converted to UTF-16");
516 : }
517 :
518 : int str_size = static_cast<int>(str.size());
519 : if (str_size == 0)
520 : {
521 : target.resize(0);
522 : return;
523 : }
524 :
525 : // find the size to allocate for the result buffer
526 : int result_size = ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, str.data(), str_size, NULL, 0);
527 :
528 : if (result_size > 0)
529 : {
530 : target.resize(result_size);
531 : result_size = ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, str.data(), str_size, target.data(), result_size);
532 : if (result_size > 0)
533 : {
534 : assert(result_size == target.size());
535 : return;
536 : }
537 : }
538 :
539 : throw_spdlog_ex(fmt_lib::format("MultiByteToWideChar failed. Last error: {}", ::GetLastError()));
540 : }
541 : #endif // (defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)) && defined(_WIN32)
542 :
543 : // return true on success
544 : static SPDLOG_INLINE bool mkdir_(const filename_t &path)
545 : {
546 : #ifdef _WIN32
547 : # ifdef SPDLOG_WCHAR_FILENAMES
548 : return ::_wmkdir(path.c_str()) == 0;
549 : # else
550 : return ::_mkdir(path.c_str()) == 0;
551 : # endif
552 : #else
553 : return ::mkdir(path.c_str(), mode_t(0755)) == 0;
554 : #endif
555 : }
556 :
557 : // create the given directory - and all directories leading to it
558 : // return true on success or if the directory already exists
559 : SPDLOG_INLINE bool create_dir(const filename_t &path)
560 : {
561 : if (path_exists(path))
562 : {
563 : return true;
564 : }
565 :
566 : if (path.empty())
567 : {
568 : return false;
569 : }
570 :
571 : size_t search_offset = 0;
572 : do
573 : {
574 : auto token_pos = path.find_first_of(folder_seps_filename, search_offset);
575 : // treat the entire path as a folder if no folder separator not found
576 : if (token_pos == filename_t::npos)
577 : {
578 : token_pos = path.size();
579 : }
580 :
581 : auto subdir = path.substr(0, token_pos);
582 :
583 : if (!subdir.empty() && !path_exists(subdir) && !mkdir_(subdir))
584 : {
585 : return false; // return error if failed creating dir
586 : }
587 : search_offset = token_pos + 1;
588 : } while (search_offset < path.size());
589 :
590 : return true;
591 : }
592 :
593 : // Return directory name from given path or empty string
594 : // "abc/file" => "abc"
595 : // "abc/" => "abc"
596 : // "abc" => ""
597 : // "abc///" => "abc//"
598 : SPDLOG_INLINE filename_t dir_name(const filename_t &path)
599 : {
600 : auto pos = path.find_last_of(folder_seps_filename);
601 : return pos != filename_t::npos ? path.substr(0, pos) : filename_t{};
602 : }
603 :
604 : std::string SPDLOG_INLINE getenv(const char *field)
605 : {
606 :
607 : #if defined(_MSC_VER)
608 : # if defined(__cplusplus_winrt)
609 : return std::string{}; // not supported under uwp
610 : # else
611 : size_t len = 0;
612 : char buf[128];
613 : bool ok = ::getenv_s(&len, buf, sizeof(buf), field) == 0;
614 : return ok ? buf : std::string{};
615 : # endif
616 : #else // revert to getenv
617 : char *buf = ::getenv(field);
618 : return buf ? buf : std::string{};
619 : #endif
620 : }
621 :
622 : // Do fsync by FILE handlerpointer
623 : // Return true on success
624 : SPDLOG_INLINE bool fsync(FILE *fp)
625 : {
626 : #ifdef _WIN32
627 : return FlushFileBuffers(reinterpret_cast<HANDLE>(_get_osfhandle(_fileno(fp)))) != 0;
628 : #else
629 : return ::fsync(fileno(fp)) == 0;
630 : #endif
631 : }
632 :
633 : } // namespace os
634 : } // namespace details
635 : } // namespace spdlog
|