os-inl.h 17 KB

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