wchar.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744
  1. /*
  2. * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
  3. * Copyright (c) 2022, Tim Schumacher <timschumi@serenityos.org>
  4. *
  5. * SPDX-License-Identifier: BSD-2-Clause
  6. */
  7. #include <AK/Assertions.h>
  8. #include <AK/Format.h>
  9. #include <AK/ScopeGuard.h>
  10. #include <AK/UnicodeUtils.h>
  11. #include <errno.h>
  12. #include <string.h>
  13. #include <time.h>
  14. #include <wchar.h>
  15. static unsigned int mbstate_expected_bytes(mbstate_t* state)
  16. {
  17. if (state->stored_bytes == 0) {
  18. return 0;
  19. }
  20. unsigned char first = state->bytes[0];
  21. // Single-byte sequences have their first bit unset
  22. if ((first & 0b10000000) == 0) {
  23. return 1;
  24. }
  25. // Two-byte sequences start with 0b110xxxxx
  26. if ((first & 0b11100000) == 0b11000000) {
  27. return 2;
  28. }
  29. // Three-byte sequences start with 0b1110xxxx
  30. if ((first & 0b11110000) == 0b11100000) {
  31. return 3;
  32. }
  33. // Four-byte sequences start with 0b11110xxx
  34. if ((first & 0b11111000) == 0b11110000) {
  35. return 4;
  36. }
  37. // Everything else is invalid
  38. return 0;
  39. }
  40. extern "C" {
  41. // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wcslen.html
  42. size_t wcslen(wchar_t const* str)
  43. {
  44. size_t len = 0;
  45. while (*(str++))
  46. ++len;
  47. return len;
  48. }
  49. // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wcscpy.html
  50. wchar_t* wcscpy(wchar_t* dest, wchar_t const* src)
  51. {
  52. wchar_t* original_dest = dest;
  53. while ((*dest++ = *src++) != '\0')
  54. ;
  55. return original_dest;
  56. }
  57. // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wcsdup.html
  58. wchar_t* wcsdup(wchar_t const* str)
  59. {
  60. size_t length = wcslen(str);
  61. wchar_t* new_str = (wchar_t*)malloc(sizeof(wchar_t) * (length + 1));
  62. if (!new_str) {
  63. errno = ENOMEM;
  64. return nullptr;
  65. }
  66. return wcscpy(new_str, str);
  67. }
  68. // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wcsncpy.html
  69. wchar_t* wcsncpy(wchar_t* dest, wchar_t const* src, size_t num)
  70. {
  71. wchar_t* original_dest = dest;
  72. while (((*dest++ = *src++) != '\0') && ((size_t)(dest - original_dest) < num))
  73. ;
  74. return original_dest;
  75. }
  76. size_t wcslcpy(wchar_t* dest, wchar_t const* src, size_t n)
  77. {
  78. size_t i;
  79. for (i = 0; i + 1 < n && src[i] != L'\0'; ++i)
  80. dest[i] = src[i];
  81. if (n)
  82. dest[i] = L'\0';
  83. for (; src[i] != L'\0'; ++i)
  84. ; // Determine the length of src, don't copy.
  85. return i;
  86. }
  87. // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wcscmp.html
  88. int wcscmp(wchar_t const* s1, wchar_t const* s2)
  89. {
  90. while (*s1 == *s2++)
  91. if (*s1++ == 0)
  92. return 0;
  93. return *(wchar_t const*)s1 - *(wchar_t const*)--s2;
  94. }
  95. // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wcsncmp.html
  96. int wcsncmp(wchar_t const* s1, wchar_t const* s2, size_t n)
  97. {
  98. if (!n)
  99. return 0;
  100. do {
  101. if (*s1 != *s2++)
  102. return *(wchar_t const*)s1 - *(wchar_t const*)--s2;
  103. if (*s1++ == 0)
  104. break;
  105. } while (--n);
  106. return 0;
  107. }
  108. // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wcschr.html
  109. wchar_t* wcschr(wchar_t const* str, int c)
  110. {
  111. wchar_t ch = c;
  112. for (;; ++str) {
  113. if (*str == ch)
  114. return const_cast<wchar_t*>(str);
  115. if (!*str)
  116. return nullptr;
  117. }
  118. }
  119. // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wcsrchr.html
  120. wchar_t* wcsrchr(wchar_t const* str, wchar_t wc)
  121. {
  122. wchar_t* last = nullptr;
  123. wchar_t c;
  124. for (; (c = *str); ++str) {
  125. if (c == wc)
  126. last = const_cast<wchar_t*>(str);
  127. }
  128. return last;
  129. }
  130. // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wcscat.html
  131. wchar_t* wcscat(wchar_t* dest, wchar_t const* src)
  132. {
  133. size_t dest_length = wcslen(dest);
  134. size_t i;
  135. for (i = 0; src[i] != '\0'; i++)
  136. dest[dest_length + i] = src[i];
  137. dest[dest_length + i] = '\0';
  138. return dest;
  139. }
  140. // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wcsncat.html
  141. wchar_t* wcsncat(wchar_t* dest, wchar_t const* src, size_t n)
  142. {
  143. size_t dest_length = wcslen(dest);
  144. size_t i;
  145. for (i = 0; i < n && src[i] != '\0'; i++)
  146. dest[dest_length + i] = src[i];
  147. dest[dest_length + i] = '\0';
  148. return dest;
  149. }
  150. // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wcstok.html
  151. wchar_t* wcstok(wchar_t* str, wchar_t const* delim, wchar_t** ptr)
  152. {
  153. wchar_t* used_str = str;
  154. if (!used_str) {
  155. used_str = *ptr;
  156. }
  157. size_t token_start = 0;
  158. size_t token_end = 0;
  159. size_t str_len = wcslen(used_str);
  160. size_t delim_len = wcslen(delim);
  161. for (size_t i = 0; i < str_len; ++i) {
  162. bool is_proper_delim = false;
  163. for (size_t j = 0; j < delim_len; ++j) {
  164. if (used_str[i] == delim[j]) {
  165. // Skip beginning delimiters
  166. if (token_end - token_start == 0) {
  167. ++token_start;
  168. break;
  169. }
  170. is_proper_delim = true;
  171. }
  172. }
  173. ++token_end;
  174. if (is_proper_delim && token_end > 0) {
  175. --token_end;
  176. break;
  177. }
  178. }
  179. if (used_str[token_start] == '\0')
  180. return nullptr;
  181. if (token_end == 0) {
  182. return &used_str[token_start];
  183. }
  184. used_str[token_end] = '\0';
  185. return &used_str[token_start];
  186. }
  187. // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wcstol.html
  188. long wcstol(wchar_t const*, wchar_t**, int)
  189. {
  190. dbgln("FIXME: Implement wcstol()");
  191. TODO();
  192. }
  193. // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wcstoll.html
  194. long long wcstoll(wchar_t const*, wchar_t**, int)
  195. {
  196. dbgln("FIXME: Implement wcstoll()");
  197. TODO();
  198. }
  199. // https://pubs.opengroup.org/onlinepubs/9699919799/functions/btowc.html
  200. wint_t btowc(int c)
  201. {
  202. if (c == EOF) {
  203. return WEOF;
  204. }
  205. // Multi-byte sequences in UTF-8 have their highest bit set
  206. if (c & (1 << 7)) {
  207. return WEOF;
  208. }
  209. return c;
  210. }
  211. // https://pubs.opengroup.org/onlinepubs/9699919799/functions/mbrtowc.html
  212. size_t mbrtowc(wchar_t* pwc, char const* s, size_t n, mbstate_t* state)
  213. {
  214. static mbstate_t _anonymous_state = {};
  215. if (state == nullptr) {
  216. state = &_anonymous_state;
  217. }
  218. // s being a null pointer is a shorthand for reading a single null byte.
  219. if (s == nullptr) {
  220. pwc = nullptr;
  221. s = "";
  222. n = 1;
  223. }
  224. // Stop early if we can't read anything
  225. if (n == 0) {
  226. return 0;
  227. }
  228. size_t consumed_bytes = 0;
  229. // Fill the first byte if we haven't done that yet
  230. if (state->stored_bytes == 0) {
  231. state->bytes[state->stored_bytes++] = s[0];
  232. consumed_bytes++;
  233. }
  234. size_t expected_bytes = mbstate_expected_bytes(state);
  235. // Check if the first byte is invalid
  236. if (expected_bytes == 0) {
  237. *state = {};
  238. errno = EILSEQ;
  239. return -1;
  240. }
  241. while (state->stored_bytes < expected_bytes) {
  242. if (consumed_bytes == n) {
  243. // No complete multibyte character
  244. return -2;
  245. }
  246. unsigned char c = s[consumed_bytes];
  247. // Continuation bytes have to start with 0b10xxxxxx
  248. if ((c & 0b11000000) != 0b10000000) {
  249. // Invalid multibyte character
  250. *state = {};
  251. errno = EILSEQ;
  252. return -1;
  253. }
  254. state->bytes[state->stored_bytes++] = c;
  255. consumed_bytes++;
  256. }
  257. wchar_t codepoint = state->bytes[0];
  258. // Mask out the "length" bits if necessary
  259. if (expected_bytes > 1) {
  260. codepoint &= (1 << (7 - expected_bytes)) - 1;
  261. }
  262. for (unsigned int i = 1; i < expected_bytes; i++) {
  263. // Each continuation byte contains 6 bits of data
  264. codepoint = codepoint << 6;
  265. codepoint |= state->bytes[i] & 0b111111;
  266. }
  267. if (pwc) {
  268. *pwc = codepoint;
  269. }
  270. // We want to read the next multibyte character, but keep all other properties.
  271. state->stored_bytes = 0;
  272. if (codepoint == 0) {
  273. *state = {};
  274. return 0;
  275. }
  276. return consumed_bytes;
  277. }
  278. // https://pubs.opengroup.org/onlinepubs/9699919799/functions/mbrlen.html
  279. size_t mbrlen(char const* s, size_t n, mbstate_t* ps)
  280. {
  281. static mbstate_t anonymous_state = {};
  282. if (ps == nullptr)
  283. ps = &anonymous_state;
  284. return mbrtowc(nullptr, s, n, ps);
  285. }
  286. // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wcrtomb.html
  287. size_t wcrtomb(char* s, wchar_t wc, mbstate_t*)
  288. {
  289. if (s == nullptr)
  290. wc = L'\0';
  291. auto nwritten = AK::UnicodeUtils::code_point_to_utf8(wc, [&s](char byte) {
  292. if (s != nullptr)
  293. *s++ = byte;
  294. });
  295. if (nwritten < 0) {
  296. errno = EILSEQ;
  297. return (size_t)-1;
  298. } else {
  299. return nwritten;
  300. }
  301. }
  302. // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wcscoll.html
  303. int wcscoll(wchar_t const* ws1, wchar_t const* ws2)
  304. {
  305. // TODO: Actually implement a sensible sort order for this,
  306. // because right now we are doing what LC_COLLATE=C would do.
  307. return wcscmp(ws1, ws2);
  308. }
  309. // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wcsxfrm.html
  310. size_t wcsxfrm(wchar_t* dest, wchar_t const* src, size_t n)
  311. {
  312. // TODO: This needs to be changed when wcscoll is not just doing wcscmp
  313. return wcslcpy(dest, src, n);
  314. }
  315. // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wctob.html
  316. int wctob(wint_t c)
  317. {
  318. if (c > 0x7f)
  319. return EOF;
  320. return static_cast<unsigned char>(c);
  321. }
  322. // https://pubs.opengroup.org/onlinepubs/9699919799/functions/mbsinit.html
  323. int mbsinit(mbstate_t const* state)
  324. {
  325. if (!state) {
  326. return 1;
  327. }
  328. if (state->stored_bytes != 0) {
  329. return 0;
  330. }
  331. return 1;
  332. }
  333. // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wcspbrk.html
  334. wchar_t* wcspbrk(wchar_t const* wcs, wchar_t const* accept)
  335. {
  336. for (wchar_t const* cur = accept; *cur; cur++) {
  337. wchar_t* res = wcschr(wcs, *cur);
  338. if (res)
  339. return res;
  340. }
  341. return nullptr;
  342. }
  343. // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wcsstr.html
  344. wchar_t* wcsstr(wchar_t const* haystack, wchar_t const* needle)
  345. {
  346. size_t nlen = wcslen(needle);
  347. if (nlen == 0)
  348. return const_cast<wchar_t*>(haystack);
  349. size_t hlen = wcslen(haystack);
  350. while (hlen >= nlen) {
  351. if (wcsncmp(haystack, needle, nlen) == 0)
  352. return const_cast<wchar_t*>(haystack);
  353. haystack++;
  354. hlen--;
  355. }
  356. return nullptr;
  357. }
  358. // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wmemchr.html
  359. wchar_t* wmemchr(wchar_t const* s, wchar_t c, size_t n)
  360. {
  361. for (size_t i = 0; i < n; i++) {
  362. if (s[i] == c)
  363. return const_cast<wchar_t*>(&s[i]);
  364. }
  365. return nullptr;
  366. }
  367. // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wmemcpy.html
  368. wchar_t* wmemcpy(wchar_t* dest, wchar_t const* src, size_t n)
  369. {
  370. for (size_t i = 0; i < n; i++)
  371. dest[i] = src[i];
  372. return dest;
  373. }
  374. // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wmemset.html
  375. wchar_t* wmemset(wchar_t* wcs, wchar_t wc, size_t n)
  376. {
  377. for (size_t i = 0; i < n; i++) {
  378. wcs[i] = wc;
  379. }
  380. return wcs;
  381. }
  382. // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wmemmove.html
  383. wchar_t* wmemmove(wchar_t* dest, wchar_t const* src, size_t n)
  384. {
  385. if (dest > src) {
  386. for (size_t i = 1; i <= n; i++) {
  387. dest[n - i] = src[n - i];
  388. }
  389. } else if (dest < src) {
  390. for (size_t i = 0; i < n; i++) {
  391. dest[i] = src[i];
  392. }
  393. }
  394. return dest;
  395. }
  396. // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wcstoul.html
  397. unsigned long wcstoul(wchar_t const*, wchar_t**, int)
  398. {
  399. dbgln("TODO: Implement wcstoul()");
  400. TODO();
  401. }
  402. // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wcstoull.html
  403. unsigned long long wcstoull(wchar_t const*, wchar_t**, int)
  404. {
  405. dbgln("TODO: Implement wcstoull()");
  406. TODO();
  407. }
  408. // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wcstof.html
  409. float wcstof(wchar_t const*, wchar_t**)
  410. {
  411. dbgln("TODO: Implement wcstof()");
  412. TODO();
  413. }
  414. // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wcstod.html
  415. double wcstod(wchar_t const*, wchar_t**)
  416. {
  417. dbgln("TODO: Implement wcstod()");
  418. TODO();
  419. }
  420. // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wcstold.html
  421. long double wcstold(wchar_t const*, wchar_t**)
  422. {
  423. dbgln("TODO: Implement wcstold()");
  424. TODO();
  425. }
  426. // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wcwidth.html
  427. int wcwidth(wchar_t wc)
  428. {
  429. if (wc == L'\0')
  430. return 0;
  431. // Printable ASCII.
  432. if (wc >= 0x20 && wc <= 0x7e)
  433. return 1;
  434. // Non-printable ASCII.
  435. if (wc <= 0x7f)
  436. return -1;
  437. // TODO: Implement wcwidth for non-ASCII characters.
  438. return 1;
  439. }
  440. // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wcswidth.html
  441. int wcswidth(wchar_t const* pwcs, size_t n)
  442. {
  443. int len = 0;
  444. for (size_t i = 0; i < n && pwcs[i]; i++) {
  445. int char_len = wcwidth(pwcs[i]);
  446. if (char_len == -1)
  447. return -1;
  448. len += char_len;
  449. }
  450. return len;
  451. }
  452. // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wcsnrtombs.html
  453. size_t wcsnrtombs(char* dest, wchar_t const** src, size_t nwc, size_t len, mbstate_t* ps)
  454. {
  455. static mbstate_t _anonymous_state = {};
  456. if (ps == nullptr)
  457. ps = &_anonymous_state;
  458. size_t written = 0;
  459. size_t read = 0;
  460. while (read < nwc) {
  461. size_t ret = 0;
  462. char buf[MB_LEN_MAX];
  463. // Convert next wchar to multibyte.
  464. ret = wcrtomb(buf, **src, ps);
  465. // wchar can't be represented as multibyte.
  466. if (ret == (size_t)-1) {
  467. errno = EILSEQ;
  468. return (size_t)-1;
  469. }
  470. // New bytes don't fit the buffer.
  471. if (dest && len < written + ret) {
  472. return written;
  473. }
  474. if (dest) {
  475. memcpy(dest, buf, ret);
  476. dest += ret;
  477. }
  478. // Null character has been reached
  479. if (**src == L'\0') {
  480. *src = nullptr;
  481. return written;
  482. }
  483. *src += 1;
  484. read += 1;
  485. written += ret;
  486. }
  487. return written;
  488. }
  489. // https://pubs.opengroup.org/onlinepubs/9699919799/functions/mbsnrtowcs.html
  490. size_t mbsnrtowcs(wchar_t* dst, char const** src, size_t nms, size_t len, mbstate_t* ps)
  491. {
  492. static mbstate_t _anonymous_state = {};
  493. if (ps == nullptr)
  494. ps = &_anonymous_state;
  495. size_t written = 0;
  496. while (written < len || !dst) {
  497. // End of source buffer, no incomplete character.
  498. // src continues to point to the next byte.
  499. if (nms == 0) {
  500. return written;
  501. }
  502. // Convert next multibyte to wchar.
  503. size_t ret = mbrtowc(dst, *src, nms, ps);
  504. // Multibyte sequence is incomplete.
  505. if (ret == -2ul) {
  506. // Point just past the last processed byte.
  507. *src += nms;
  508. return written;
  509. }
  510. // Multibyte sequence is invalid.
  511. if (ret == -1ul) {
  512. errno = EILSEQ;
  513. return (size_t)-1;
  514. }
  515. // Null byte has been reached.
  516. if (**src == '\0') {
  517. *src = nullptr;
  518. return written;
  519. }
  520. *src += ret;
  521. nms -= ret;
  522. written += 1;
  523. if (dst)
  524. dst += 1;
  525. }
  526. // If we are here, we have written `len` wchars, but not reached the null byte.
  527. return written;
  528. }
  529. // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wmemcmp.html
  530. int wmemcmp(wchar_t const* s1, wchar_t const* s2, size_t n)
  531. {
  532. while (n-- > 0) {
  533. if (*s1++ != *s2++)
  534. return s1[-1] < s2[-1] ? -1 : 1;
  535. }
  536. return 0;
  537. }
  538. // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wcsrtombs.html
  539. size_t wcsrtombs(char* dest, wchar_t const** src, size_t len, mbstate_t* ps)
  540. {
  541. static mbstate_t anonymous_state = {};
  542. if (ps == nullptr)
  543. ps = &anonymous_state;
  544. // SIZE_MAX is as close as we are going to get to "unlimited".
  545. return wcsnrtombs(dest, src, SIZE_MAX, len, ps);
  546. }
  547. // https://pubs.opengroup.org/onlinepubs/9699919799/functions/mbsrtowcs.html
  548. size_t mbsrtowcs(wchar_t* dst, char const** src, size_t len, mbstate_t* ps)
  549. {
  550. static mbstate_t anonymous_state = {};
  551. if (ps == nullptr)
  552. ps = &anonymous_state;
  553. // SIZE_MAX is as close as we are going to get to "unlimited".
  554. return mbsnrtowcs(dst, src, SIZE_MAX, len, ps);
  555. }
  556. // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wcscspn.html
  557. size_t wcscspn(wchar_t const* wcs, wchar_t const* reject)
  558. {
  559. for (auto const* wc_pointer = wcs;;) {
  560. auto c = *wc_pointer++;
  561. wchar_t rc;
  562. auto const* reject_copy = reject;
  563. do {
  564. if ((rc = *reject_copy++) == c)
  565. return wc_pointer - 1 - wcs;
  566. } while (rc != 0);
  567. }
  568. }
  569. // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wcsspn.html
  570. size_t wcsspn(wchar_t const* wcs, wchar_t const* accept)
  571. {
  572. for (auto const* wc_pointer = wcs;;) {
  573. auto c = *wc_pointer++;
  574. wchar_t rc;
  575. auto const* accept_copy = accept;
  576. do {
  577. if ((rc = *accept_copy++) != c)
  578. return wc_pointer - 1 - wcs;
  579. } while (rc != 0);
  580. }
  581. }
  582. // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wcsftime.html
  583. size_t wcsftime(wchar_t* destination, size_t maxsize, wchar_t const* format, const struct tm* tm)
  584. {
  585. // FIXME: Add actual wide char support for this.
  586. char* ascii_format = static_cast<char*>(malloc(wcslen(format) + 1));
  587. char* ascii_destination = static_cast<char*>(malloc(maxsize));
  588. VERIFY(ascii_format && ascii_destination);
  589. // These are copied by value because we will change the pointers without rolling them back.
  590. ScopeGuard free_ascii = [ascii_format, ascii_destination] {
  591. free(ascii_format);
  592. free(ascii_destination);
  593. };
  594. char* ascii_format_copy = ascii_format;
  595. do {
  596. VERIFY(*format <= 0x7f);
  597. *ascii_format_copy++ = static_cast<char>(*format);
  598. } while (*format++ != L'\0');
  599. #pragma GCC diagnostic push
  600. #pragma GCC diagnostic ignored "-Wformat-nonliteral"
  601. size_t ret = strftime(ascii_destination, maxsize, ascii_format, tm);
  602. #pragma GCC diagnostic pop
  603. if (ret == 0)
  604. return 0;
  605. do {
  606. *destination++ = *ascii_destination;
  607. } while (*ascii_destination++ != '\0');
  608. return ret;
  609. }
  610. }