ini.hpp 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272
  1. /*
  2. * Copyright (c) 2017-2023 zhllxt
  3. *
  4. * author : zhllxt
  5. * email : 37792738@qq.com
  6. *
  7. * Distributed under the Boost Software License, Version 1.0. (See accompanying
  8. * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  9. */
  10. #ifndef __ASIO2_INI_HPP__
  11. #define __ASIO2_INI_HPP__
  12. #if defined(_MSC_VER) && (_MSC_VER >= 1200)
  13. #pragma once
  14. #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
  15. #if defined(__GNUC__) || defined(__GNUG__)
  16. # pragma GCC diagnostic push
  17. # pragma GCC diagnostic ignored "-Warray-bounds"
  18. #endif
  19. #include <cstring>
  20. #include <cctype>
  21. #include <cstdarg>
  22. #include <clocale>
  23. #include <climits>
  24. #include <cstddef>
  25. #include <cstdint>
  26. #include <cstdlib>
  27. #include <cassert>
  28. #include <ctime>
  29. #include <memory>
  30. #include <string>
  31. #include <locale>
  32. #include <string_view>
  33. #include <vector>
  34. #include <mutex>
  35. #include <shared_mutex>
  36. #include <fstream>
  37. #include <sstream>
  38. #include <type_traits>
  39. #include <system_error>
  40. #include <limits>
  41. #include <algorithm>
  42. #include <tuple>
  43. #include <chrono>
  44. /*
  45. *
  46. * How to determine whether to use <filesystem> or <experimental/filesystem>
  47. * https://stackoverflow.com/questions/53365538/how-to-determine-whether-to-use-filesystem-or-experimental-filesystem/53365539#53365539
  48. *
  49. *
  50. */
  51. // We haven't checked which filesystem to include yet
  52. #ifndef INCLUDE_STD_FILESYSTEM_EXPERIMENTAL
  53. // Check for feature test macro for <filesystem>
  54. # if defined(__cpp_lib_filesystem)
  55. # define INCLUDE_STD_FILESYSTEM_EXPERIMENTAL 0
  56. // Check for feature test macro for <experimental/filesystem>
  57. # elif defined(__cpp_lib_experimental_filesystem)
  58. # define INCLUDE_STD_FILESYSTEM_EXPERIMENTAL 1
  59. // We can't check if headers exist...
  60. // Let's assume experimental to be safe
  61. # elif !defined(__has_include)
  62. # define INCLUDE_STD_FILESYSTEM_EXPERIMENTAL 1
  63. // Check if the header "<filesystem>" exists
  64. # elif __has_include(<filesystem>)
  65. // If we're compiling on Visual Studio and are not compiling with C++17, we need to use experimental
  66. # ifdef _MSC_VER
  67. // Check and include header that defines "_HAS_CXX17"
  68. # if __has_include(<yvals_core.h>)
  69. # include <yvals_core.h>
  70. // Check for enabled C++17 support
  71. # if defined(_HAS_CXX17) && _HAS_CXX17
  72. // We're using C++17, so let's use the normal version
  73. # define INCLUDE_STD_FILESYSTEM_EXPERIMENTAL 0
  74. # endif
  75. # endif
  76. // If the marco isn't defined yet, that means any of the other VS specific checks failed, so we need to use experimental
  77. # ifndef INCLUDE_STD_FILESYSTEM_EXPERIMENTAL
  78. # define INCLUDE_STD_FILESYSTEM_EXPERIMENTAL 1
  79. # endif
  80. // Not on Visual Studio. Let's use the normal version
  81. # else // #ifdef _MSC_VER
  82. # define INCLUDE_STD_FILESYSTEM_EXPERIMENTAL 0
  83. # endif
  84. // Check if the header "<filesystem>" exists
  85. # elif __has_include(<experimental/filesystem>)
  86. # define INCLUDE_STD_FILESYSTEM_EXPERIMENTAL 1
  87. // Fail if neither header is available with a nice error message
  88. # else
  89. # error Could not find system header "<filesystem>" or "<experimental/filesystem>"
  90. # endif
  91. // We priously determined that we need the exprimental version
  92. # if INCLUDE_STD_FILESYSTEM_EXPERIMENTAL
  93. // Include it
  94. # include <experimental/filesystem>
  95. // We need the alias from std::experimental::filesystem to std::filesystem
  96. namespace std {
  97. namespace filesystem = experimental::filesystem;
  98. }
  99. // We have a decent compiler and can use the normal version
  100. # else
  101. // Include it
  102. # include <filesystem>
  103. # endif
  104. #endif // #ifndef INCLUDE_STD_FILESYSTEM_EXPERIMENTAL
  105. // when compiled with "Visual Studio 2017 - Windows XP (v141_xp)"
  106. // there is hasn't shared_mutex
  107. #ifndef ASIO2_HAS_SHARED_MUTEX
  108. #if defined(_MSC_VER)
  109. #if defined(_HAS_SHARED_MUTEX)
  110. #if _HAS_SHARED_MUTEX
  111. #define ASIO2_HAS_SHARED_MUTEX 1
  112. #define asio2_shared_mutex std::shared_mutex
  113. #define asio2_shared_lock std::shared_lock
  114. #define asio2_unique_lock std::unique_lock
  115. #else
  116. #define ASIO2_HAS_SHARED_MUTEX 0
  117. #define asio2_shared_mutex std::mutex
  118. #define asio2_shared_lock std::lock_guard
  119. #define asio2_unique_lock std::lock_guard
  120. #endif
  121. #else
  122. #define ASIO2_HAS_SHARED_MUTEX 1
  123. #define asio2_shared_mutex std::shared_mutex
  124. #define asio2_shared_lock std::shared_lock
  125. #define asio2_unique_lock std::unique_lock
  126. #endif
  127. #else
  128. #define ASIO2_HAS_SHARED_MUTEX 1
  129. #define asio2_shared_mutex std::shared_mutex
  130. #define asio2_shared_lock std::shared_lock
  131. #define asio2_unique_lock std::unique_lock
  132. #endif
  133. #endif
  134. #if defined(unix) || defined(__unix) || defined(_XOPEN_SOURCE) || defined(_POSIX_SOURCE) || \
  135. defined(linux) || defined(__linux) || defined(__linux__) || defined(__gnu_linux__)
  136. # if __has_include(<unistd.h>)
  137. # include <unistd.h>
  138. # endif
  139. # if __has_include(<sys/types.h>)
  140. # include <sys/types.h>
  141. # endif
  142. # if __has_include(<sys/stat.h>)
  143. # include <sys/stat.h>
  144. # endif
  145. # if __has_include(<dirent.h>)
  146. # include <dirent.h>
  147. # endif
  148. #elif defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(_WIN64) || \
  149. defined(_WINDOWS_) || defined(__WINDOWS__) || defined(__TOS_WIN__)
  150. # ifndef WIN32_LEAN_AND_MEAN
  151. # define WIN32_LEAN_AND_MEAN
  152. # endif
  153. # if __has_include(<Windows.h>)
  154. # include <Windows.h>
  155. # endif
  156. # if __has_include(<tchar.h>)
  157. # include <tchar.h>
  158. # endif
  159. # if __has_include(<io.h>)
  160. # include <io.h>
  161. # endif
  162. # if __has_include(<direct.h>)
  163. # include <direct.h>
  164. # endif
  165. #elif defined(__APPLE__) && defined(__MACH__)
  166. # if __has_include(<mach-o/dyld.h>)
  167. # include <mach-o/dyld.h>
  168. # endif
  169. #endif
  170. /*
  171. * mutex:
  172. * Linux platform needs to add -lpthread option in link libraries
  173. */
  174. namespace asio2
  175. {
  176. template<class T>
  177. struct convert;
  178. template<>
  179. struct convert<bool>
  180. {
  181. /**
  182. * @brief Returns `true` if two strings are equal, using a case-insensitive comparison.
  183. */
  184. template<typename = void>
  185. inline static bool iequals(std::string_view lhs, std::string_view rhs) noexcept
  186. {
  187. auto n = lhs.size();
  188. if (rhs.size() != n)
  189. return false;
  190. auto p1 = lhs.data();
  191. auto p2 = rhs.data();
  192. char a, b;
  193. // fast loop
  194. while (n--)
  195. {
  196. a = *p1++;
  197. b = *p2++;
  198. if (a != b)
  199. goto slow;
  200. }
  201. return true;
  202. slow:
  203. do
  204. {
  205. if (std::tolower(a) != std::tolower(b))
  206. return false;
  207. a = *p1++;
  208. b = *p2++;
  209. } while (n--);
  210. return true;
  211. }
  212. template<
  213. class CharT,
  214. class Traits = std::char_traits<CharT>,
  215. class Allocator = std::allocator<CharT>
  216. >
  217. inline static bool stov(std::basic_string<CharT, Traits, Allocator>& val)
  218. {
  219. if (iequals(val, "true"))
  220. return true;
  221. if (iequals(val, "false"))
  222. return false;
  223. return (!(std::stoi(val) == 0));
  224. }
  225. };
  226. template<>
  227. struct convert<char>
  228. {
  229. template<class ...Args>
  230. inline static char stov(Args&&... args)
  231. { return static_cast<char>(std::stoi(std::forward<Args>(args)...)); }
  232. };
  233. template<>
  234. struct convert<signed char>
  235. {
  236. template<class ...Args>
  237. inline static signed char stov(Args&&... args)
  238. { return static_cast<signed char>(std::stoi(std::forward<Args>(args)...)); }
  239. };
  240. template<>
  241. struct convert<unsigned char>
  242. {
  243. template<class ...Args>
  244. inline static unsigned char stov(Args&&... args)
  245. { return static_cast<unsigned char>(std::stoul(std::forward<Args>(args)...)); }
  246. };
  247. template<>
  248. struct convert<short>
  249. {
  250. template<class ...Args>
  251. inline static short stov(Args&&... args)
  252. { return static_cast<short>(std::stoi(std::forward<Args>(args)...)); }
  253. };
  254. template<>
  255. struct convert<unsigned short>
  256. {
  257. template<class ...Args>
  258. inline static unsigned short stov(Args&&... args)
  259. { return static_cast<unsigned short>(std::stoul(std::forward<Args>(args)...)); }
  260. };
  261. template<>
  262. struct convert<int>
  263. {
  264. template<class ...Args>
  265. inline static int stov(Args&&... args)
  266. { return std::stoi(std::forward<Args>(args)...); }
  267. };
  268. template<>
  269. struct convert<unsigned int>
  270. {
  271. template<class ...Args>
  272. inline static unsigned int stov(Args&&... args)
  273. { return static_cast<unsigned int>(std::stoul(std::forward<Args>(args)...)); }
  274. };
  275. template<>
  276. struct convert<long>
  277. {
  278. template<class ...Args>
  279. inline static long stov(Args&&... args)
  280. { return std::stol(std::forward<Args>(args)...); }
  281. };
  282. template<>
  283. struct convert<unsigned long>
  284. {
  285. template<class ...Args>
  286. inline static unsigned long stov(Args&&... args)
  287. { return std::stoul(std::forward<Args>(args)...); }
  288. };
  289. template<>
  290. struct convert<long long>
  291. {
  292. template<class ...Args>
  293. inline static long long stov(Args&&... args)
  294. { return std::stoll(std::forward<Args>(args)...); }
  295. };
  296. template<>
  297. struct convert<unsigned long long>
  298. {
  299. template<class ...Args>
  300. inline static unsigned long long stov(Args&&... args)
  301. { return std::stoull(std::forward<Args>(args)...); }
  302. };
  303. template<>
  304. struct convert<float>
  305. {
  306. template<class ...Args>
  307. inline static float stov(Args&&... args)
  308. { return std::stof(std::forward<Args>(args)...); }
  309. };
  310. template<>
  311. struct convert<double>
  312. {
  313. template<class ...Args>
  314. inline static double stov(Args&&... args)
  315. { return std::stod(std::forward<Args>(args)...); }
  316. };
  317. template<>
  318. struct convert<long double>
  319. {
  320. template<class ...Args>
  321. inline static long double stov(Args&&... args)
  322. { return std::stold(std::forward<Args>(args)...); }
  323. };
  324. template<class CharT, class Traits, class Allocator>
  325. struct convert<std::basic_string<CharT, Traits, Allocator>>
  326. {
  327. template<class ...Args>
  328. inline static std::basic_string<CharT, Traits, Allocator> stov(Args&&... args)
  329. { return std::basic_string<CharT, Traits, Allocator>(std::forward<Args>(args)...); }
  330. };
  331. template<class CharT, class Traits>
  332. struct convert<std::basic_string_view<CharT, Traits>>
  333. {
  334. template<class ...Args>
  335. inline static std::basic_string_view<CharT, Traits> stov(Args&&... args)
  336. { return std::basic_string_view<CharT, Traits>(std::forward<Args>(args)...); }
  337. };
  338. template<class Rep, class Period>
  339. struct convert<std::chrono::duration<Rep, Period>>
  340. {
  341. // referenced from: C# TimeSpan
  342. // 30 - 30 seconds
  343. // 00:00:00.0000036 - 36 milliseconds
  344. // 00:00:00 - 0 seconds
  345. // 2.10:36:45 - 2 days 10 hours 36 minutes 45 seconds
  346. // 2.00:00:00.0000036 -
  347. template<class S>
  348. inline static std::chrono::duration<Rep, Period> stov(S&& s)
  349. {
  350. std::size_t n1 = s.find(':');
  351. if (n1 == std::string::npos)
  352. return std::chrono::seconds(std::stoll(s));
  353. int day = 0, hour = 0, min = 0, sec = 0, msec = 0;
  354. std::size_t m1 = s.find('.');
  355. if (m1 < n1)
  356. {
  357. day = std::stoi(s.substr(0, m1));
  358. s.erase(0, m1 + 1);
  359. }
  360. n1 = s.find(':');
  361. hour = std::stoi(s.substr(0, n1));
  362. s.erase(0, n1 + 1);
  363. n1 = s.find(':');
  364. min = std::stoi(s.substr(0, n1));
  365. s.erase(0, n1 + 1);
  366. n1 = s.find('.');
  367. sec = std::stoi(s.substr(0, n1));
  368. if (n1 != std::string::npos)
  369. {
  370. s.erase(0, n1 + 1);
  371. msec = std::stoi(s);
  372. }
  373. return
  374. std::chrono::hours(day * 24) +
  375. std::chrono::hours(hour) +
  376. std::chrono::minutes(min) +
  377. std::chrono::seconds(sec) +
  378. std::chrono::milliseconds(msec);
  379. }
  380. };
  381. template<class Clock, class Duration>
  382. struct convert<std::chrono::time_point<Clock, Duration>>
  383. {
  384. template<class S>
  385. inline static std::chrono::time_point<Clock, Duration> stov(S&& s)
  386. {
  387. std::stringstream ss;
  388. ss << std::forward<S>(s);
  389. std::tm t{};
  390. if (s.find('/') != std::string::npos)
  391. ss >> std::get_time(&t, "%m/%d/%Y %H:%M:%S");
  392. else
  393. ss >> std::get_time(&t, "%Y-%m-%d %H:%M:%S");
  394. return Clock::from_time_t(std::mktime(&t));
  395. }
  396. };
  397. }
  398. namespace asio2
  399. {
  400. // use namespace asio2::detail::util to avoid conflict with asio2::detail in file "asio2/base/detail/util.hpp"
  401. // is_string_view ...
  402. namespace detail::util
  403. {
  404. template<typename, typename = void>
  405. struct is_fstream : std::false_type {};
  406. template<typename T>
  407. struct is_fstream<T, std::void_t<typename T::char_type, typename T::traits_type,
  408. typename std::enable_if_t<std::is_same_v<T,
  409. std::basic_fstream<typename T::char_type, typename T::traits_type>>>>> : std::true_type {};
  410. template<class T>
  411. inline constexpr bool is_fstream_v = is_fstream<std::remove_cv_t<std::remove_reference_t<T>>>::value;
  412. template<typename, typename = void>
  413. struct is_ifstream : std::false_type {};
  414. template<typename T>
  415. struct is_ifstream<T, std::void_t<typename T::char_type, typename T::traits_type,
  416. typename std::enable_if_t<std::is_same_v<T,
  417. std::basic_ifstream<typename T::char_type, typename T::traits_type>>>>> : std::true_type {};
  418. template<class T>
  419. inline constexpr bool is_ifstream_v = is_ifstream<std::remove_cv_t<std::remove_reference_t<T>>>::value;
  420. template<typename, typename = void>
  421. struct is_ofstream : std::false_type {};
  422. template<typename T>
  423. struct is_ofstream<T, std::void_t<typename T::char_type, typename T::traits_type,
  424. typename std::enable_if_t<std::is_same_v<T,
  425. std::basic_ofstream<typename T::char_type, typename T::traits_type>>>>> : std::true_type {};
  426. template<class T>
  427. inline constexpr bool is_ofstream_v = is_ofstream<std::remove_cv_t<std::remove_reference_t<T>>>::value;
  428. template<class T>
  429. inline constexpr bool is_file_stream_v = is_fstream_v<T> || is_ifstream_v<T> || is_ofstream_v<T>;
  430. template<typename, typename = void>
  431. struct is_string_view : std::false_type {};
  432. template<typename T>
  433. struct is_string_view<T, std::void_t<typename T::value_type, typename T::traits_type,
  434. typename std::enable_if_t<std::is_same_v<T,
  435. std::basic_string_view<typename T::value_type, typename T::traits_type>>>>> : std::true_type {};
  436. template<class T>
  437. inline constexpr bool is_string_view_v = is_string_view<T>::value;
  438. template<typename, typename = void>
  439. struct is_char_pointer : std::false_type {};
  440. // char const *
  441. // std::remove_cv_t<std::remove_pointer_t<std::remove_cv_t<std::remove_reference_t<T>>>
  442. // char
  443. template<typename T>
  444. struct is_char_pointer<T, std::void_t<typename std::enable_if_t <
  445. std::is_pointer_v< std::remove_cv_t<std::remove_reference_t<T>>> &&
  446. !std::is_pointer_v<std::remove_pointer_t<std::remove_cv_t<std::remove_reference_t<T>>>> &&
  447. (
  448. std::is_same_v<std::remove_cv_t<std::remove_pointer_t<std::remove_cv_t<std::remove_reference_t<T>>>>, char > ||
  449. std::is_same_v<std::remove_cv_t<std::remove_pointer_t<std::remove_cv_t<std::remove_reference_t<T>>>>, wchar_t > ||
  450. #if defined(__cpp_lib_char8_t)
  451. std::is_same_v<std::remove_cv_t<std::remove_pointer_t<std::remove_cv_t<std::remove_reference_t<T>>>>, char8_t > ||
  452. #endif
  453. std::is_same_v<std::remove_cv_t<std::remove_pointer_t<std::remove_cv_t<std::remove_reference_t<T>>>>, char16_t> ||
  454. std::is_same_v<std::remove_cv_t<std::remove_pointer_t<std::remove_cv_t<std::remove_reference_t<T>>>>, char32_t>
  455. )
  456. >>> : std::true_type {};
  457. template<class T>
  458. inline constexpr bool is_char_pointer_v = is_char_pointer<T>::value;
  459. template<typename, typename = void>
  460. struct is_char_array : std::false_type {};
  461. template<typename T>
  462. struct is_char_array<T, std::void_t<typename std::enable_if_t <
  463. std::is_array_v<std::remove_cv_t<std::remove_reference_t<T>>> &&
  464. (
  465. std::is_same_v<std::remove_cv_t<std::remove_all_extents_t<std::remove_cv_t<std::remove_reference_t<T>>>>, char > ||
  466. std::is_same_v<std::remove_cv_t<std::remove_all_extents_t<std::remove_cv_t<std::remove_reference_t<T>>>>, wchar_t > ||
  467. #if defined(__cpp_lib_char8_t)
  468. std::is_same_v<std::remove_cv_t<std::remove_all_extents_t<std::remove_cv_t<std::remove_reference_t<T>>>>, char8_t > ||
  469. #endif
  470. std::is_same_v<std::remove_cv_t<std::remove_all_extents_t<std::remove_cv_t<std::remove_reference_t<T>>>>, char16_t> ||
  471. std::is_same_v<std::remove_cv_t<std::remove_all_extents_t<std::remove_cv_t<std::remove_reference_t<T>>>>, char32_t>
  472. )
  473. >>> : std::true_type {};
  474. template<class T>
  475. inline constexpr bool is_char_array_v = is_char_array<T>::value;
  476. template<class R>
  477. struct return_type
  478. {
  479. template<class T, bool> struct string_view_traits { using type = T; };
  480. template<class T> struct string_view_traits<T, true>
  481. {
  482. using type = std::basic_string<typename std::remove_cv_t<std::remove_reference_t<R>>::value_type>;
  483. };
  484. using type = typename std::conditional_t<is_char_pointer_v<R> || is_char_array_v<R>,
  485. std::basic_string<std::remove_cv_t<std::remove_all_extents_t<
  486. std::remove_pointer_t<std::remove_cv_t<std::remove_reference_t<R>>>>>>,
  487. typename string_view_traits<R, is_string_view_v<R>>::type>;
  488. };
  489. }
  490. namespace detail
  491. {
  492. template<class Stream>
  493. class basic_file_ini_impl : public Stream
  494. {
  495. public:
  496. template<class ...Args>
  497. basic_file_ini_impl(Args&&... args)
  498. {
  499. std::ios_base::openmode mode{};
  500. if constexpr /**/ (sizeof...(Args) == 0)
  501. {
  502. #if defined(unix) || defined(__unix) || defined(_XOPEN_SOURCE) || defined(_POSIX_SOURCE) || \
  503. defined(linux) || defined(__linux) || defined(__linux__) || defined(__gnu_linux__)
  504. filepath_.resize(PATH_MAX);
  505. auto r = readlink("/proc/self/exe", (char *)filepath_.data(), PATH_MAX);
  506. std::ignore = r; // gcc 7 warning: ignoring return value of ... [-Wunused-result]
  507. #elif defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(_WIN64) || \
  508. defined(_WINDOWS_) || defined(__WINDOWS__) || defined(__TOS_WIN__)
  509. filepath_.resize(MAX_PATH);
  510. filepath_.resize(::GetModuleFileNameA(NULL, (LPSTR)filepath_.data(), MAX_PATH));
  511. #elif defined(__APPLE__) && defined(__MACH__)
  512. filepath_.resize(PATH_MAX);
  513. std::uint32_t bufsize = std::uint32_t(PATH_MAX);
  514. _NSGetExecutablePath(filepath_.data(), std::addressof(bufsize));
  515. #endif
  516. if (std::string::size_type pos = filepath_.find('\0'); pos != std::string::npos)
  517. filepath_.resize(pos);
  518. #if defined(_DEBUG) || defined(DEBUG)
  519. assert(!filepath_.empty());
  520. #endif
  521. std::filesystem::path path{ filepath_ };
  522. std::string name = path.filename().string();
  523. std::string ext = path.extension().string();
  524. name.resize(name.size() - ext.size());
  525. filepath_ = path.parent_path().append(name).string() + ".ini";
  526. }
  527. else if constexpr (sizeof...(Args) == 1)
  528. {
  529. filepath_ = std::move(std::get<0>(std::make_tuple(std::forward<Args>(args)...)));
  530. }
  531. else if constexpr (sizeof...(Args) == 2)
  532. {
  533. auto t = std::make_tuple(std::forward<Args>(args)...);
  534. filepath_ = std::move(std::get<0>(t));
  535. mode |= std::get<1>(t);
  536. }
  537. else
  538. {
  539. std::ignore = true;
  540. }
  541. std::error_code ec;
  542. // if file is not exists, create it
  543. if (bool b = std::filesystem::exists(filepath_, ec); !b && !ec)
  544. {
  545. Stream f(filepath_, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
  546. }
  547. if constexpr /**/ (detail::util::is_fstream_v<Stream>)
  548. {
  549. mode |= std::ios_base::in | std::ios_base::out | std::ios_base::binary;
  550. }
  551. else if constexpr (detail::util::is_ifstream_v<Stream>)
  552. {
  553. mode |= std::ios_base::in | std::ios_base::binary;
  554. }
  555. else if constexpr (detail::util::is_ofstream_v<Stream>)
  556. {
  557. mode |= std::ios_base::out | std::ios_base::binary;
  558. }
  559. else
  560. {
  561. mode |= std::ios_base::in | std::ios_base::out | std::ios_base::binary;
  562. }
  563. Stream::open(filepath_, mode);
  564. }
  565. ~basic_file_ini_impl()
  566. {
  567. using pos_type = typename Stream::pos_type;
  568. Stream::clear();
  569. Stream::seekg(0, std::ios::end);
  570. auto filesize = Stream::tellg();
  571. if (filesize)
  572. {
  573. pos_type spaces = pos_type(0);
  574. do
  575. {
  576. Stream::clear();
  577. Stream::seekg(filesize - spaces - pos_type(1));
  578. char c;
  579. if (!Stream::get(c))
  580. break;
  581. if (c == ' ' || c == '\0')
  582. spaces = spaces + pos_type(1);
  583. else
  584. break;
  585. } while (true);
  586. if (spaces)
  587. {
  588. std::error_code ec;
  589. std::filesystem::resize_file(filepath_, filesize - spaces, ec);
  590. }
  591. }
  592. }
  593. inline std::string filepath() { return filepath_; }
  594. protected:
  595. std::string filepath_;
  596. };
  597. template<class Stream>
  598. class basic_ini_impl : public Stream
  599. {
  600. public:
  601. template<class ...Args>
  602. basic_ini_impl(Args&&... args) : Stream(std::forward<Args>(args)...) {}
  603. };
  604. template<class... Ts>
  605. class basic_ini_impl<std::basic_fstream<Ts...>> : public basic_file_ini_impl<std::basic_fstream<Ts...>>
  606. {
  607. public:
  608. template<class ...Args>
  609. basic_ini_impl(Args&&... args) : basic_file_ini_impl<std::basic_fstream<Ts...>>(std::forward<Args>(args)...) {}
  610. };
  611. template<class... Ts>
  612. class basic_ini_impl<std::basic_ifstream<Ts...>> : public basic_file_ini_impl<std::basic_ifstream<Ts...>>
  613. {
  614. public:
  615. template<class ...Args>
  616. basic_ini_impl(Args&&... args) : basic_file_ini_impl<std::basic_ifstream<Ts...>>(std::forward<Args>(args)...) {}
  617. };
  618. template<class... Ts>
  619. class basic_ini_impl<std::basic_ofstream<Ts...>> : public basic_file_ini_impl<std::basic_ofstream<Ts...>>
  620. {
  621. public:
  622. template<class ...Args>
  623. basic_ini_impl(Args&&... args) : basic_file_ini_impl<std::basic_ofstream<Ts...>>(std::forward<Args>(args)...) {}
  624. };
  625. }
  626. /**
  627. * basic_ini operator class
  628. */
  629. template<class Stream>
  630. class basic_ini : public detail::basic_ini_impl<Stream>
  631. {
  632. public:
  633. using char_type = typename Stream::char_type;
  634. using pos_type = typename Stream::pos_type;
  635. using size_type = typename std::basic_string<char_type>::size_type;
  636. template<class ...Args>
  637. basic_ini(Args&&... args) : detail::basic_ini_impl<Stream>(std::forward<Args>(args)...)
  638. {
  639. this->endl_ = { '\n' };
  640. #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(_WIN64) || \
  641. defined(_WINDOWS_) || defined(__WINDOWS__) || defined(__TOS_WIN__)
  642. this->endl_ = { '\r','\n' };
  643. #elif defined(__APPLE__) && defined(__MACH__)
  644. // on the macos 9, the newline character is '\r'.
  645. // the last macos 9 version is 9.2.2 (20011205)
  646. //this->endl_ = { '\r' };
  647. #endif
  648. }
  649. protected:
  650. template<class Traits = std::char_traits<char_type>, class Allocator = std::allocator<char_type>>
  651. bool _get(
  652. std::basic_string_view<char_type, Traits> sec,
  653. std::basic_string_view<char_type, Traits> key,
  654. std::basic_string<char_type, Traits, Allocator> & val)
  655. {
  656. asio2_shared_lock guard(this->mutex_);
  657. Stream::clear();
  658. if (Stream::operator bool())
  659. {
  660. std::basic_string<char_type, Traits, Allocator> line;
  661. std::basic_string<char_type, Traits, Allocator> s, k, v;
  662. pos_type posg;
  663. Stream::seekg(0, std::ios::beg);
  664. char ret;
  665. while ((ret = this->_getline(line, s, k, v, posg)) != 'n')
  666. {
  667. switch (ret)
  668. {
  669. case 'a':break;
  670. case 's':
  671. if (s == sec)
  672. {
  673. do
  674. {
  675. ret = this->_getline(line, s, k, v, posg);
  676. if (ret == 'k' && k == key)
  677. {
  678. val = std::move(v);
  679. return true;
  680. }
  681. } while (ret == 'k' || ret == 'a' || ret == 'o');
  682. return false;
  683. }
  684. break;
  685. case 'k':
  686. if (s == sec)
  687. {
  688. if (k == key)
  689. {
  690. val = std::move(v);
  691. return true;
  692. }
  693. }
  694. break;
  695. case 'o':break;
  696. default:break;
  697. }
  698. }
  699. }
  700. return false;
  701. }
  702. public:
  703. /**
  704. * get the value associated with a key in the specified section of an ini file.
  705. * This function does not throw an exception.
  706. * example :
  707. * asio2::ini ini("config.ini");
  708. * std::string host = ini.get("main", "host", "127.0.0.1");
  709. * std::uint16_t port = ini.get("main", "port", 8080);
  710. * or :
  711. * std::string host = ini.get<std::string >("main", "host");
  712. * std::uint16_t port = ini.get<std::uint16_t>("main", "port");
  713. */
  714. template<class R, class Sec, class Key, class Traits = std::char_traits<char_type>,
  715. class Allocator = std::allocator<char_type>>
  716. inline typename detail::util::return_type<R>::type
  717. get(const Sec& sec, const Key& key, R default_val = R())
  718. {
  719. using return_t = typename detail::util::return_type<R>::type;
  720. try
  721. {
  722. std::basic_string<char_type, Traits, Allocator> val;
  723. bool flag = this->_get(
  724. std::basic_string_view<char_type, Traits>(sec),
  725. std::basic_string_view<char_type, Traits>(key),
  726. val);
  727. if constexpr (detail::util::is_char_pointer_v<R> || detail::util::is_char_array_v<R>)
  728. {
  729. return (flag ? val : return_t{ default_val });
  730. }
  731. else if constexpr (detail::util::is_string_view_v<R>)
  732. {
  733. return (flag ? val : return_t{ default_val });
  734. }
  735. else
  736. {
  737. return (flag ? asio2::convert<R>::stov(val) : default_val);
  738. }
  739. }
  740. catch (std::invalid_argument const&)
  741. {
  742. }
  743. catch (std::out_of_range const&)
  744. {
  745. }
  746. catch (std::exception const&)
  747. {
  748. }
  749. return return_t{ default_val };
  750. }
  751. /**
  752. * set the value associated with a key in the specified section of an ini file.
  753. * example :
  754. * asio2::ini ini("config.ini");
  755. * ini.set("main", "host", "127.0.0.1");
  756. * ini.set("main", "port", 8080);
  757. */
  758. template<class Sec, class Key, class Val, class Traits = std::char_traits<char_type>>
  759. inline typename std::enable_if_t<std::is_same_v<decltype(
  760. std::basic_string_view<char_type, Traits>(std::declval<Sec>()),
  761. std::basic_string_view<char_type, Traits>(std::declval<Key>()),
  762. std::basic_string_view<char_type, Traits>(std::declval<Val>()),
  763. std::true_type()), std::true_type>, bool>
  764. set(const Sec& sec, const Key& key, const Val& val)
  765. {
  766. return this->set(
  767. std::basic_string_view<char_type, Traits>(sec),
  768. std::basic_string_view<char_type, Traits>(key),
  769. std::basic_string_view<char_type, Traits>(val));
  770. }
  771. /**
  772. * set the value associated with a key in the specified section of an ini file.
  773. * example :
  774. * asio2::ini ini("config.ini");
  775. * ini.set("main", "host", "127.0.0.1");
  776. * ini.set("main", "port", 8080);
  777. */
  778. template<class Sec, class Key, class Val, class Traits = std::char_traits<char_type>>
  779. inline typename std::enable_if_t<std::is_same_v<decltype(
  780. std::basic_string_view<char_type, Traits>(std::declval<Sec>()),
  781. std::basic_string_view<char_type, Traits>(std::declval<Key>()),
  782. std::to_string(std::declval<Val>()),
  783. std::true_type()), std::true_type>, bool>
  784. set(const Sec& sec, const Key& key, Val val)
  785. {
  786. std::basic_string<char_type, Traits> v = std::to_string(val);
  787. return this->set(
  788. std::basic_string_view<char_type, Traits>(sec),
  789. std::basic_string_view<char_type, Traits>(key),
  790. std::basic_string_view<char_type, Traits>(v));
  791. }
  792. /**
  793. * set the value associated with a key in the specified section of an ini file.
  794. * example :
  795. * asio2::ini ini("config.ini");
  796. * ini.set("main", "host", "127.0.0.1");
  797. * ini.set("main", "port", 8080);
  798. */
  799. template<class Traits = std::char_traits<char_type>, class Allocator = std::allocator<char_type>>
  800. bool set(
  801. std::basic_string_view<char_type, Traits> sec,
  802. std::basic_string_view<char_type, Traits> key,
  803. std::basic_string_view<char_type, Traits> val)
  804. {
  805. asio2_unique_lock guard(this->mutex_);
  806. Stream::clear();
  807. if (Stream::operator bool())
  808. {
  809. std::basic_string<char_type, Traits, Allocator> line;
  810. std::basic_string<char_type, Traits, Allocator> s, k, v;
  811. pos_type posg = 0;
  812. char ret;
  813. auto update_v = [&]() mutable -> bool
  814. {
  815. try
  816. {
  817. if (val != v)
  818. {
  819. Stream::clear();
  820. Stream::seekg(0, std::ios::end);
  821. auto filesize = Stream::tellg();
  822. std::basic_string<char_type, Traits, Allocator> content;
  823. auto pos = line.find_first_of('=');
  824. ++pos;
  825. while (pos < line.size() && (line[pos] == ' ' || line[pos] == '\t'))
  826. ++pos;
  827. content += line.substr(0, pos);
  828. content += val;
  829. content += this->endl_;
  830. int pos_diff = int(line.size() + 1 - content.size());
  831. Stream::clear();
  832. Stream::seekg(posg + pos_type(line.size() + 1));
  833. int remain = int(filesize - (posg + pos_type(line.size() + 1)));
  834. if (remain > 0)
  835. {
  836. content.resize(size_type(content.size() + remain));
  837. Stream::read(content.data() + content.size() - remain, remain);
  838. }
  839. if (pos_diff > 0) content.append(pos_diff, ' ');
  840. while (!content.empty() &&
  841. (pos_type(content.size()) + posg > filesize) &&
  842. content.back() == ' ')
  843. {
  844. content.erase(content.size() - 1);
  845. }
  846. Stream::clear();
  847. Stream::seekp(posg);
  848. //*this << content;
  849. Stream::write(content.data(), content.size());
  850. Stream::flush();
  851. }
  852. return true;
  853. }
  854. catch (std::exception &) {}
  855. return false;
  856. };
  857. Stream::seekg(0, std::ios::beg);
  858. while ((ret = this->_getline(line, s, k, v, posg)) != 'n')
  859. {
  860. switch (ret)
  861. {
  862. case 'a':break;
  863. case 's':
  864. if (s == sec)
  865. {
  866. do
  867. {
  868. ret = this->_getline(line, s, k, v, posg);
  869. if (ret == 'k' && k == key)
  870. {
  871. return update_v();
  872. }
  873. } while (ret == 'k' || ret == 'a' || ret == 'o');
  874. // can't find the key, add a new key
  875. std::basic_string<char_type, Traits, Allocator> content;
  876. if (posg == pos_type(-1))
  877. {
  878. Stream::clear();
  879. Stream::seekg(0, std::ios::end);
  880. posg = Stream::tellg();
  881. }
  882. content += this->endl_;
  883. content += key;
  884. content += '=';
  885. content += val;
  886. content += this->endl_;
  887. Stream::clear();
  888. Stream::seekg(0, std::ios::end);
  889. auto filesize = Stream::tellg();
  890. Stream::clear();
  891. Stream::seekg(posg);
  892. int remain = int(filesize - posg);
  893. if (remain > 0)
  894. {
  895. content.resize(size_type(content.size() + remain));
  896. Stream::read(content.data() + content.size() - remain, remain);
  897. }
  898. while (!content.empty() &&
  899. (pos_type(content.size()) + posg > filesize) &&
  900. content.back() == ' ')
  901. {
  902. content.erase(content.size() - 1);
  903. }
  904. Stream::clear();
  905. Stream::seekp(posg);
  906. //*this << content;
  907. Stream::write(content.data(), content.size());
  908. Stream::flush();
  909. return true;
  910. }
  911. break;
  912. case 'k':
  913. if (s == sec)
  914. {
  915. if (k == key)
  916. {
  917. return update_v();
  918. }
  919. }
  920. break;
  921. case 'o':break;
  922. default:break;
  923. }
  924. }
  925. // can't find the sec and key, add a new sec and key.
  926. std::basic_string<char_type, Traits, Allocator> content;
  927. Stream::clear();
  928. Stream::seekg(0, std::ios::end);
  929. auto filesize = Stream::tellg();
  930. content.resize(size_type(filesize));
  931. Stream::clear();
  932. Stream::seekg(0, std::ios::beg);
  933. Stream::read(content.data(), content.size());
  934. if (!content.empty() && content.back() == '\n') content.erase(content.size() - 1);
  935. if (!content.empty() && content.back() == '\r') content.erase(content.size() - 1);
  936. std::basic_string<char_type, Traits, Allocator> buffer;
  937. if (!sec.empty())
  938. {
  939. buffer += '[';
  940. buffer += sec;
  941. buffer += ']';
  942. buffer += this->endl_;
  943. }
  944. buffer += key;
  945. buffer += '=';
  946. buffer += val;
  947. buffer += this->endl_;
  948. if (!sec.empty())
  949. {
  950. if (content.empty())
  951. content = std::move(buffer);
  952. else
  953. content = content + this->endl_ + buffer;
  954. }
  955. // if the sec is empty ( mean global), must add the key at the begin.
  956. else
  957. {
  958. if (content.empty())
  959. content = std::move(buffer);
  960. else
  961. content = buffer + content;
  962. }
  963. while (!content.empty() &&
  964. (pos_type(content.size()) > filesize) &&
  965. content.back() == ' ')
  966. {
  967. content.erase(content.size() - 1);
  968. }
  969. Stream::clear();
  970. Stream::seekp(0, std::ios::beg);
  971. //*this << content;
  972. Stream::write(content.data(), content.size());
  973. Stream::flush();
  974. return true;
  975. }
  976. return false;
  977. }
  978. protected:
  979. template<class Traits = std::char_traits<char_type>, class Allocator = std::allocator<char_type>>
  980. char _getline(
  981. std::basic_string<char_type, Traits, Allocator> & line,
  982. std::basic_string<char_type, Traits, Allocator> & sec,
  983. std::basic_string<char_type, Traits, Allocator> & key,
  984. std::basic_string<char_type, Traits, Allocator> & val,
  985. pos_type & posg)
  986. {
  987. Stream::clear();
  988. if (Stream::good() && !Stream::eof())
  989. {
  990. posg = Stream::tellg();
  991. if (posg != pos_type(-1) && std::getline(*this, line, this->endl_.back()))
  992. {
  993. auto trim_line = line;
  994. trim_left(trim_line);
  995. trim_right(trim_line);
  996. if (trim_line.empty())
  997. {
  998. return 'o'; // other
  999. }
  1000. // current line is code annotation
  1001. if (
  1002. (trim_line.size() > 0 && (trim_line[0] == ';' || trim_line[0] == '#' || trim_line[0] == ':'))
  1003. ||
  1004. (trim_line.size() > 1 && trim_line[0] == '/' && trim_line[1] == '/')
  1005. )
  1006. {
  1007. return 'a'; // annotation
  1008. }
  1009. auto pos1 = trim_line.find_first_of('[');
  1010. auto pos2 = trim_line.find_first_of(']');
  1011. // current line is section
  1012. if (
  1013. pos1 == 0
  1014. &&
  1015. pos2 != std::basic_string<char_type, Traits, Allocator>::npos
  1016. &&
  1017. pos2 > pos1
  1018. )
  1019. {
  1020. sec = trim_line.substr(pos1 + 1, pos2 - pos1 - 1);
  1021. trim_left(sec);
  1022. trim_right(sec);
  1023. return 's'; // section
  1024. }
  1025. auto sep = trim_line.find_first_of('=');
  1026. // current line is key and val
  1027. if (sep != std::basic_string<char_type, Traits, Allocator>::npos && sep > 0)
  1028. {
  1029. key = trim_line.substr(0, sep);
  1030. trim_left(key);
  1031. trim_right(key);
  1032. val = trim_line.substr(sep + 1);
  1033. trim_left(val);
  1034. trim_right(val);
  1035. return 'k'; // kv
  1036. }
  1037. return 'o'; // other
  1038. }
  1039. }
  1040. return 'n'; // null
  1041. }
  1042. template<
  1043. class CharT,
  1044. class Traits = std::char_traits<CharT>,
  1045. class Allocator = std::allocator<CharT>
  1046. >
  1047. std::basic_string<CharT, Traits, Allocator>& trim_left(std::basic_string<CharT, Traits, Allocator>& s)
  1048. {
  1049. if (s.empty())
  1050. return s;
  1051. using size_type = typename std::basic_string<CharT, Traits, Allocator>::size_type;
  1052. size_type pos = 0;
  1053. for (; pos < s.size(); ++pos)
  1054. {
  1055. if (!std::isspace(static_cast<unsigned char>(s[pos])))
  1056. break;
  1057. }
  1058. s.erase(0, pos);
  1059. return s;
  1060. }
  1061. template<
  1062. class CharT,
  1063. class Traits = std::char_traits<CharT>,
  1064. class Allocator = std::allocator<CharT>
  1065. >
  1066. std::basic_string<CharT, Traits, Allocator>& trim_right(std::basic_string<CharT, Traits, Allocator>& s)
  1067. {
  1068. if (s.empty())
  1069. return s;
  1070. using size_type = typename std::basic_string<CharT, Traits, Allocator>::size_type;
  1071. size_type pos = s.size() - 1;
  1072. for (; pos != size_type(-1); pos--)
  1073. {
  1074. if (!std::isspace(static_cast<unsigned char>(s[pos])))
  1075. break;
  1076. }
  1077. s.erase(pos + 1);
  1078. return s;
  1079. }
  1080. protected:
  1081. mutable asio2_shared_mutex mutex_;
  1082. std::basic_string<char_type> endl_;
  1083. };
  1084. using ini = basic_ini<std::fstream>;
  1085. }
  1086. #if defined(__GNUC__) || defined(__GNUG__)
  1087. # pragma GCC diagnostic pop
  1088. #endif
  1089. #endif // !__ASIO2_INI_HPP__