You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

466 satır
12KB

  1. // Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
  2. // Distributed under the MIT License (http://opensource.org/licenses/MIT)
  3. #pragma once
  4. #ifndef SPDLOG_HEADER_ONLY
  5. #include "spdlog/details/os.h"
  6. #endif
  7. #include "spdlog/common.h"
  8. #include <algorithm>
  9. #include <chrono>
  10. #include <cstdio>
  11. #include <cstdlib>
  12. #include <cstring>
  13. #include <ctime>
  14. #include <string>
  15. #include <thread>
  16. #include <array>
  17. #include <sys/stat.h>
  18. #include <sys/types.h>
  19. #ifdef _WIN32
  20. #ifndef NOMINMAX
  21. #define NOMINMAX // prevent windows redefining min/max
  22. #endif
  23. #ifndef WIN32_LEAN_AND_MEAN
  24. #define WIN32_LEAN_AND_MEAN
  25. #endif
  26. #include <io.h> // _get_osfhandle and _isatty support
  27. #include <process.h> // _get_pid support
  28. #include <windows.h>
  29. #ifdef __MINGW32__
  30. #include <share.h>
  31. #endif
  32. #if defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)
  33. #include <limits>
  34. #endif
  35. #else // unix
  36. #include <fcntl.h>
  37. #include <unistd.h>
  38. #ifdef __linux__
  39. #include <sys/syscall.h> //Use gettid() syscall under linux to get thread id
  40. #elif defined(_AIX)
  41. #include <pthread.h> // for pthread_getthreadid_np
  42. #elif defined(__DragonFly__) || defined(__FreeBSD__)
  43. #include <pthread_np.h> // for pthread_getthreadid_np
  44. #elif defined(__NetBSD__)
  45. #include <lwp.h> // for _lwp_self
  46. #elif defined(__sun)
  47. #include <thread.h> // for thr_self
  48. #endif
  49. #endif // unix
  50. #ifndef __has_feature // Clang - feature checking macros.
  51. #define __has_feature(x) 0 // Compatibility with non-clang compilers.
  52. #endif
  53. namespace spdlog {
  54. namespace details {
  55. namespace os {
  56. SPDLOG_INLINE spdlog::log_clock::time_point now() SPDLOG_NOEXCEPT
  57. {
  58. #if defined __linux__ && defined SPDLOG_CLOCK_COARSE
  59. timespec ts;
  60. ::clock_gettime(CLOCK_REALTIME_COARSE, &ts);
  61. return std::chrono::time_point<log_clock, typename log_clock::duration>(
  62. std::chrono::duration_cast<typename log_clock::duration>(std::chrono::seconds(ts.tv_sec) + std::chrono::nanoseconds(ts.tv_nsec)));
  63. #else
  64. return log_clock::now();
  65. #endif
  66. }
  67. SPDLOG_INLINE std::tm localtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT
  68. {
  69. #ifdef _WIN32
  70. std::tm tm;
  71. localtime_s(&tm, &time_tt);
  72. #else
  73. std::tm tm;
  74. localtime_r(&time_tt, &tm);
  75. #endif
  76. return tm;
  77. }
  78. SPDLOG_INLINE std::tm localtime() SPDLOG_NOEXCEPT
  79. {
  80. std::time_t now_t = time(nullptr);
  81. return localtime(now_t);
  82. }
  83. SPDLOG_INLINE std::tm gmtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT
  84. {
  85. #ifdef _WIN32
  86. std::tm tm;
  87. gmtime_s(&tm, &time_tt);
  88. #else
  89. std::tm tm;
  90. gmtime_r(&time_tt, &tm);
  91. #endif
  92. return tm;
  93. }
  94. SPDLOG_INLINE std::tm gmtime() SPDLOG_NOEXCEPT
  95. {
  96. std::time_t now_t = time(nullptr);
  97. return gmtime(now_t);
  98. }
  99. SPDLOG_INLINE void prevent_child_fd(FILE *f)
  100. {
  101. #ifdef _WIN32
  102. #if !defined(__cplusplus_winrt)
  103. auto file_handle = reinterpret_cast<HANDLE>(_get_osfhandle(_fileno(f)));
  104. if (!::SetHandleInformation(file_handle, HANDLE_FLAG_INHERIT, 0))
  105. SPDLOG_THROW(spdlog_ex("SetHandleInformation failed", errno));
  106. #endif
  107. #else
  108. auto fd = fileno(f);
  109. if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
  110. {
  111. SPDLOG_THROW(spdlog_ex("fcntl with FD_CLOEXEC failed", errno));
  112. }
  113. #endif
  114. }
  115. // fopen_s on non windows for writing
  116. SPDLOG_INLINE bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mode)
  117. {
  118. #ifdef _WIN32
  119. #ifdef SPDLOG_WCHAR_FILENAMES
  120. *fp = _wfsopen((filename.c_str()), mode.c_str(), _SH_DENYNO);
  121. #else
  122. *fp = _fsopen((filename.c_str()), mode.c_str(), _SH_DENYNO);
  123. #endif
  124. #else // unix
  125. *fp = fopen((filename.c_str()), mode.c_str());
  126. #endif
  127. #ifdef SPDLOG_PREVENT_CHILD_FD
  128. if (*fp != nullptr)
  129. {
  130. prevent_child_fd(*fp);
  131. }
  132. #endif
  133. return *fp == nullptr;
  134. }
  135. SPDLOG_INLINE int remove(const filename_t &filename) SPDLOG_NOEXCEPT
  136. {
  137. #if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
  138. return _wremove(filename.c_str());
  139. #else
  140. return std::remove(filename.c_str());
  141. #endif
  142. }
  143. SPDLOG_INLINE int remove_if_exists(const filename_t &filename) SPDLOG_NOEXCEPT
  144. {
  145. return file_exists(filename) ? remove(filename) : 0;
  146. }
  147. SPDLOG_INLINE int rename(const filename_t &filename1, const filename_t &filename2) SPDLOG_NOEXCEPT
  148. {
  149. #if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
  150. return _wrename(filename1.c_str(), filename2.c_str());
  151. #else
  152. return std::rename(filename1.c_str(), filename2.c_str());
  153. #endif
  154. }
  155. // Return true if file exists
  156. SPDLOG_INLINE bool file_exists(const filename_t &filename) SPDLOG_NOEXCEPT
  157. {
  158. #ifdef _WIN32
  159. #ifdef SPDLOG_WCHAR_FILENAMES
  160. auto attribs = GetFileAttributesW(filename.c_str());
  161. #else
  162. auto attribs = GetFileAttributesA(filename.c_str());
  163. #endif
  164. return (attribs != INVALID_FILE_ATTRIBUTES && !(attribs & FILE_ATTRIBUTE_DIRECTORY));
  165. #else // common linux/unix all have the stat system call
  166. struct stat buffer;
  167. return (::stat(filename.c_str(), &buffer) == 0);
  168. #endif
  169. }
  170. // Return file size according to open FILE* object
  171. SPDLOG_INLINE size_t filesize(FILE *f)
  172. {
  173. if (f == nullptr)
  174. {
  175. SPDLOG_THROW(spdlog_ex("Failed getting file size. fd is null"));
  176. }
  177. #if defined(_WIN32) && !defined(__CYGWIN__)
  178. int fd = _fileno(f);
  179. #if _WIN64 // 64 bits
  180. __int64 ret = _filelengthi64(fd);
  181. if (ret >= 0)
  182. {
  183. return static_cast<size_t>(ret);
  184. }
  185. #else // windows 32 bits
  186. long ret = _filelength(fd);
  187. if (ret >= 0)
  188. {
  189. return static_cast<size_t>(ret);
  190. }
  191. #endif
  192. #else // unix
  193. int fd = fileno(f);
  194. // 64 bits(but not in osx or cygwin, where fstat64 is deprecated)
  195. #if (defined(__linux__) || defined(__sun) || defined(_AIX)) && (defined(__LP64__) || defined(_LP64))
  196. struct stat64 st;
  197. if (::fstat64(fd, &st) == 0)
  198. {
  199. return static_cast<size_t>(st.st_size);
  200. }
  201. #else // unix 32 bits or cygwin
  202. struct stat st;
  203. if (::fstat(fd, &st) == 0)
  204. {
  205. return static_cast<size_t>(st.st_size);
  206. }
  207. #endif
  208. #endif
  209. SPDLOG_THROW(spdlog_ex("Failed getting file size from fd", errno));
  210. }
  211. // Return utc offset in minutes or throw spdlog_ex on failure
  212. SPDLOG_INLINE int utc_minutes_offset(const std::tm &tm)
  213. {
  214. #ifdef _WIN32
  215. #if _WIN32_WINNT < _WIN32_WINNT_WS08
  216. TIME_ZONE_INFORMATION tzinfo;
  217. auto rv = GetTimeZoneInformation(&tzinfo);
  218. #else
  219. DYNAMIC_TIME_ZONE_INFORMATION tzinfo;
  220. auto rv = GetDynamicTimeZoneInformation(&tzinfo);
  221. #endif
  222. if (rv == TIME_ZONE_ID_INVALID)
  223. SPDLOG_THROW(spdlog::spdlog_ex("Failed getting timezone info. ", errno));
  224. int offset = -tzinfo.Bias;
  225. if (tm.tm_isdst)
  226. {
  227. offset -= tzinfo.DaylightBias;
  228. }
  229. else
  230. {
  231. offset -= tzinfo.StandardBias;
  232. }
  233. return offset;
  234. #else
  235. #if defined(sun) || defined(__sun) || defined(_AIX)
  236. // 'tm_gmtoff' field is BSD extension and it's missing on SunOS/Solaris
  237. struct helper
  238. {
  239. static long int calculate_gmt_offset(const std::tm &localtm = details::os::localtime(), const std::tm &gmtm = details::os::gmtime())
  240. {
  241. int local_year = localtm.tm_year + (1900 - 1);
  242. int gmt_year = gmtm.tm_year + (1900 - 1);
  243. long int days = (
  244. // difference in day of year
  245. localtm.tm_yday -
  246. gmtm.tm_yday
  247. // + intervening leap days
  248. + ((local_year >> 2) - (gmt_year >> 2)) - (local_year / 100 - gmt_year / 100) +
  249. ((local_year / 100 >> 2) - (gmt_year / 100 >> 2))
  250. // + difference in years * 365 */
  251. + (long int)(local_year - gmt_year) * 365);
  252. long int hours = (24 * days) + (localtm.tm_hour - gmtm.tm_hour);
  253. long int mins = (60 * hours) + (localtm.tm_min - gmtm.tm_min);
  254. long int secs = (60 * mins) + (localtm.tm_sec - gmtm.tm_sec);
  255. return secs;
  256. }
  257. };
  258. auto offset_seconds = helper::calculate_gmt_offset(tm);
  259. #else
  260. auto offset_seconds = tm.tm_gmtoff;
  261. #endif
  262. return static_cast<int>(offset_seconds / 60);
  263. #endif
  264. }
  265. // Return current thread id as size_t
  266. // It exists because the std::this_thread::get_id() is much slower(especially
  267. // under VS 2013)
  268. SPDLOG_INLINE size_t _thread_id() SPDLOG_NOEXCEPT
  269. {
  270. #ifdef _WIN32
  271. return static_cast<size_t>(::GetCurrentThreadId());
  272. #elif defined(__linux__)
  273. #if defined(__ANDROID__) && defined(__ANDROID_API__) && (__ANDROID_API__ < 21)
  274. #define SYS_gettid __NR_gettid
  275. #endif
  276. return static_cast<size_t>(syscall(SYS_gettid));
  277. #elif defined(_AIX) || defined(__DragonFly__) || defined(__FreeBSD__)
  278. return static_cast<size_t>(pthread_getthreadid_np());
  279. #elif defined(__NetBSD__)
  280. return static_cast<size_t>(_lwp_self());
  281. #elif defined(__OpenBSD__)
  282. return static_cast<size_t>(getthrid());
  283. #elif defined(__sun)
  284. return static_cast<size_t>(thr_self());
  285. #elif __APPLE__
  286. uint64_t tid;
  287. pthread_threadid_np(nullptr, &tid);
  288. return static_cast<size_t>(tid);
  289. #else // Default to standard C++11 (other Unix)
  290. return static_cast<size_t>(std::hash<std::thread::id>()(std::this_thread::get_id()));
  291. #endif
  292. }
  293. // Return current thread id as size_t (from thread local storage)
  294. SPDLOG_INLINE size_t thread_id() SPDLOG_NOEXCEPT
  295. {
  296. #if defined(SPDLOG_NO_TLS)
  297. return _thread_id();
  298. #else // cache thread id in tls
  299. static thread_local const size_t tid = _thread_id();
  300. return tid;
  301. #endif
  302. }
  303. // This is avoid msvc issue in sleep_for that happens if the clock changes.
  304. // See https://github.com/gabime/spdlog/issues/609
  305. SPDLOG_INLINE void sleep_for_millis(int milliseconds) SPDLOG_NOEXCEPT
  306. {
  307. #if defined(_WIN32)
  308. ::Sleep(milliseconds);
  309. #else
  310. std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds));
  311. #endif
  312. }
  313. // wchar support for windows file names (SPDLOG_WCHAR_FILENAMES must be defined)
  314. #if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
  315. SPDLOG_INLINE std::string filename_to_str(const filename_t &filename)
  316. {
  317. memory_buf_t buf;
  318. wstr_to_utf8buf(filename, buf);
  319. return fmt::to_string(buf);
  320. }
  321. #else
  322. SPDLOG_INLINE std::string filename_to_str(const filename_t &filename)
  323. {
  324. return filename;
  325. }
  326. #endif
  327. SPDLOG_INLINE int pid() SPDLOG_NOEXCEPT
  328. {
  329. #ifdef _WIN32
  330. return static_cast<int>(::GetCurrentProcessId());
  331. #else
  332. return static_cast<int>(::getpid());
  333. #endif
  334. }
  335. // Determine if the terminal supports colors
  336. // Source: https://github.com/agauniyal/rang/
  337. SPDLOG_INLINE bool is_color_terminal() SPDLOG_NOEXCEPT
  338. {
  339. #ifdef _WIN32
  340. return true;
  341. #else
  342. static constexpr std::array<const char *, 14> Terms = {
  343. {"ansi", "color", "console", "cygwin", "gnome", "konsole", "kterm", "linux", "msys", "putty", "rxvt", "screen", "vt100", "xterm"}};
  344. const char *env_p = std::getenv("TERM");
  345. if (env_p == nullptr)
  346. {
  347. return false;
  348. }
  349. static const bool result =
  350. std::any_of(std::begin(Terms), std::end(Terms), [&](const char *term) { return std::strstr(env_p, term) != nullptr; });
  351. return result;
  352. #endif
  353. }
  354. // Detrmine if the terminal attached
  355. // Source: https://github.com/agauniyal/rang/
  356. SPDLOG_INLINE bool in_terminal(FILE *file) SPDLOG_NOEXCEPT
  357. {
  358. #ifdef _WIN32
  359. return _isatty(_fileno(file)) != 0;
  360. #else
  361. return isatty(fileno(file)) != 0;
  362. #endif
  363. }
  364. #if (defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)) && defined(_WIN32)
  365. SPDLOG_INLINE void wstr_to_utf8buf(wstring_view_t wstr, memory_buf_t &target)
  366. {
  367. if (wstr.size() > static_cast<size_t>(std::numeric_limits<int>::max()))
  368. {
  369. SPDLOG_THROW(spdlog::spdlog_ex("UTF-16 string is too big to be converted to UTF-8"));
  370. }
  371. int wstr_size = static_cast<int>(wstr.size());
  372. if (wstr_size == 0)
  373. {
  374. target.resize(0);
  375. return;
  376. }
  377. int result_size = static_cast<int>(target.capacity());
  378. if ((wstr_size + 1) * 2 > result_size)
  379. {
  380. result_size = ::WideCharToMultiByte(CP_UTF8, 0, wstr.data(), wstr_size, NULL, 0, NULL, NULL);
  381. }
  382. if (result_size > 0)
  383. {
  384. target.resize(result_size);
  385. result_size = ::WideCharToMultiByte(CP_UTF8, 0, wstr.data(), wstr_size, target.data(), result_size, NULL, NULL);
  386. if (result_size > 0)
  387. {
  388. target.resize(result_size);
  389. return;
  390. }
  391. }
  392. SPDLOG_THROW(spdlog::spdlog_ex(fmt::format("WideCharToMultiByte failed. Last error: {}", ::GetLastError())));
  393. }
  394. #endif // (defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)) && defined(_WIN32)
  395. } // namespace os
  396. } // namespace details
  397. } // namespace spdlog