str.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716
  1. /* Definitions for the string type.
  2. This file is part of khipu.
  3. khipu is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU Lesser General Public License as published by
  5. the Free Software Foundation; either version 3 of the License, or
  6. (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU Lesser General Public License for more details.
  11. You should have received a copy of the GNU Lesser General Public License
  12. along with this program. If not, see <https://www.gnu.org/licenses/>. */
  13. #include <cstdio> // For the SEEK_* constants.
  14. #include <cstdlib>
  15. #include <climits>
  16. #include "str.hpp"
  17. #include "memory.hpp"
  18. #include "stream.hpp"
  19. #include "utils/chmask.hpp"
  20. #include "integer.hpp"
  21. #include "io.hpp"
  22. KP_DECLS_BEGIN
  23. const uint8_t UTF8_SKIP[256] =
  24. {
  25. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  26. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  27. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  28. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  29. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  30. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  31. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  32. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  33. 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
  34. 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
  35. 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0
  36. };
  37. uint32_t utf8min (const char *s, uint32_t maxlen)
  38. {
  39. if (!maxlen)
  40. return (maxlen);
  41. const char *p = s;
  42. for (p += maxlen - 1; ((uint8_t)*p & 0xc0) == 0x80; --p) ;
  43. return (p + UTF8_SKIP[(uint8_t)*p] >
  44. s + maxlen ? (uint32_t)(p - s) : maxlen);
  45. }
  46. uint32_t ustrlen (const void *xs, uint32_t *lenp)
  47. {
  48. uint32_t i;
  49. const uint8_t *s = (const uint8_t *)xs, *p;
  50. for (i = 0, p = s; *p != 0; ++i)
  51. p += UTF8_SKIP[*p];
  52. *lenp = i;
  53. return ((uint32_t)(p - s));
  54. }
  55. uint32_t ustrnlen (const void *xs, uint32_t maxlen)
  56. {
  57. uint32_t i;
  58. const uint8_t *s = (const uint8_t *)xs, *end = s + maxlen;
  59. for (i = 0; s < end; ++i)
  60. s += UTF8_SKIP[*s];
  61. return (i);
  62. }
  63. uint32_t stridx (const string *sp, uint32_t idx)
  64. {
  65. uint32_t nbytes = sp->nbytes;
  66. const unsigned char *ptr, *datap = sp->data;
  67. if (sp->len == nbytes)
  68. // ASCII string - Fast path.
  69. return (idx);
  70. else if (idx > (sp->len * 3) / 4)
  71. { // Much closer to the end - Walk in reverse.
  72. ptr = datap + nbytes;
  73. idx = sp->len - idx;
  74. while (idx > 0)
  75. if ((*--ptr & 0xc0) != 0x80)
  76. --idx;
  77. }
  78. else
  79. { // Traverse string forward.
  80. ptr = datap;
  81. for (uint32_t i = 0; i < idx; ++i)
  82. ptr += UTF8_SKIP[*ptr];
  83. }
  84. return ((uint32_t)(ptr - datap));
  85. }
  86. uint32_t stroff (const string *sp, uint32_t off)
  87. {
  88. uint32_t nbytes = sp->nbytes;
  89. const unsigned char *datap = sp->data, *ptr = datap + off;
  90. if (sp->len == nbytes)
  91. // ASCII string.
  92. return (off);
  93. else if (off > (nbytes * 3) / 4)
  94. { // Much closer to the end - Walk in reverse.
  95. datap += nbytes;
  96. while (datap > ptr)
  97. if ((*--datap & 0xc0) != 0x80)
  98. --nbytes;
  99. }
  100. else
  101. // Traverse string forward.
  102. for (nbytes = 0; ptr > datap; ++nbytes)
  103. datap += UTF8_SKIP[*datap];
  104. return (nbytes);
  105. }
  106. uint32_t u8tou32 (const unsigned char *src, uint32_t len)
  107. {
  108. uint32_t ret = *src;
  109. if (len > 1)
  110. {
  111. int mask = 0x40;
  112. do
  113. {
  114. mask <<= 5;
  115. ret = (ret << 6) + ((unsigned char)*++src - 0x80);
  116. }
  117. while (--len > 1);
  118. ret &= --mask;
  119. }
  120. return (ret);
  121. }
  122. uint32_t u32tou8 (unsigned char *dst, uint32_t ch)
  123. {
  124. uint32_t ret;
  125. if (ch < 0x80)
  126. *dst = (unsigned char)ch, ret = 1;
  127. else
  128. {
  129. int step = 0;
  130. if ((ch & (~0u << 11)) == 0)
  131. step = 2;
  132. else if ((ch & (~0u << 16)) == 0)
  133. step = 3;
  134. else if ((ch & (~0u << 21)) == 0)
  135. step = 4;
  136. ret = step;
  137. *dst = (unsigned char)(~0xff >> step--);
  138. do
  139. {
  140. dst[step] = 0x80 | (ch & 0x3f);
  141. ch >>= 6;
  142. }
  143. while (--step > 0);
  144. *dst |= (unsigned char)ch;
  145. }
  146. return (ret);
  147. }
  148. result<object> string::make (interpreter *interp, const void *cstr)
  149. {
  150. uint32_t len, bytes = ustrlen (cstr, &len);
  151. object rv = KP_TRY (alloc_str (interp, bytes));
  152. string *ret = as_str (rv);
  153. memcpy (ret->data, cstr, bytes);
  154. ret->len = len;
  155. kp_return (interp->alval);
  156. }
  157. result<object> string::make (interpreter *interp, const void *buf, uint32_t len)
  158. {
  159. object rv = KP_TRY (alloc_str (interp, len));
  160. string *ret = as_str (rv);
  161. memcpy (ret->data, buf, len);
  162. ret->len = ustrnlen (buf, len);
  163. kp_return (interp->alval);
  164. }
  165. result<object> reverse_s (interpreter *interp, object obj)
  166. {
  167. const string *src = as_str (obj);
  168. object rv = KP_TRY (alloc_str (interp, src->nbytes));
  169. string *ret = as_str (rv);
  170. if (src->nbytes == src->len)
  171. for (uint32_t i = 0, j = src->nbytes - 1 ; ; --j)
  172. {
  173. ret->data[i] = src->data[j];
  174. if (!j)
  175. break;
  176. }
  177. else
  178. {
  179. const unsigned char *inp = src->data;
  180. unsigned char *outp = ret->data + ret->nbytes;
  181. while (inp < src->data + src->nbytes)
  182. {
  183. uint32_t nl = UTF8_SKIP[*inp];
  184. fscpy (outp -= nl, inp, nl);
  185. inp += nl;
  186. }
  187. }
  188. ret->len = src->len;
  189. kp_return (ret->as_obj ());
  190. }
  191. result<object> iter_s (interpreter *interp, object obj, object token, bool adv)
  192. {
  193. if (token == UNBOUND)
  194. kp_return (as_str(obj)->nbytes == 0 ? NIL : fixint (0));
  195. if (!fixint_p (token))
  196. return (interp->raise ("type-error", "token must be an int"));
  197. int ix = as_int (token);
  198. const unsigned char *dp = as_str(obj)->data + ix;
  199. if ((*dp & 0xc0) == 0x80)
  200. return (interp->raise ("arg-error", "invalid token"));
  201. else if (!adv)
  202. kp_return (charobj (u8tou32 (dp, UTF8_SKIP[*dp])));
  203. ix += UTF8_SKIP[*dp];
  204. kp_return ((uint32_t)ix >= as_str(obj)->nbytes ? NIL : fixint (ix));
  205. }
  206. result<object> last_s (interpreter *interp, object obj)
  207. {
  208. const string *sp = as_str (obj);
  209. if (!sp->len)
  210. return (interp->raise_oob (0, 0));
  211. auto up = sp->data + sp->nbytes;
  212. auto vp = up;
  213. do
  214. --up;
  215. while ((*up & 0xc0) == 0x80);
  216. kp_return (charobj (u8tou32 (up, vp - up)));
  217. }
  218. static inline object
  219. fixup_index (const string *sp, object obj)
  220. {
  221. return (fixint_p (obj) ? fixint (stridx (sp, as_int (obj))) : obj);
  222. }
  223. result<object> find_s (interpreter *interp, object obj, object key,
  224. object start, object end, object test)
  225. {
  226. const string *src = as_str (obj);
  227. start = fixup_index (src, start);
  228. end = fixup_index (src, end);
  229. local_varobj<bvector> bv;
  230. unsigned char buf[16];
  231. if (char_p (key))
  232. bv.local_init (buf, u32tou8 (buf, as_char (key)));
  233. else if (!str_p (key))
  234. return (interp->raise ("type-error", "key must be a string or character"));
  235. else
  236. bv.local_init (as_str(key)->data, as_str(key)->nbytes);
  237. object ret = KP_TRY (find_b (interp, obj, bv.as_obj (), start, end, test));
  238. if (ret != NIL)
  239. ret = fixint (stroff (src, as_int (ret)));
  240. kp_return (ret);
  241. }
  242. // Stream interface.
  243. struct sstream_data
  244. {
  245. unsigned char *datap;
  246. uint32_t curpos;
  247. uint32_t nmax;
  248. uint32_t nbytes;
  249. bool owned_p; // True if the buffer is ours.
  250. };
  251. static result<int64_t>
  252. str_read (interpreter *, stream& strm, void *dstp, uint64_t bytes)
  253. {
  254. sstream_data *dp = (sstream_data *)strm.cookie;
  255. unsigned char *ptr = dp->datap + dp->curpos;
  256. uint32_t rb = (uint32_t)(dp->nbytes - dp->curpos);
  257. rb = utf8min ((const char *)ptr, min ((uint64_t)rb, bytes));
  258. memcpy (dstp, ptr, rb);
  259. dp->curpos += rb;
  260. return ((int64_t)rb);
  261. }
  262. static result<int64_t>
  263. str_write (interpreter *, stream& strm, const void *src, uint64_t bytes)
  264. {
  265. sstream_data *dp = (sstream_data *)strm.cookie;
  266. if (dp->curpos + bytes >= dp->nmax)
  267. {
  268. uint32_t nsz = upsize (dp->curpos + bytes + 1);
  269. dp->datap = (unsigned char *)xrealloc (dp->datap, dp->nmax = nsz);
  270. }
  271. if (dp->curpos + bytes < dp->nbytes)
  272. { /* Writing to the middle of the string.
  273. * Be careful to preserve data consistency. */
  274. unsigned char *p1 = dp->datap + dp->curpos + bytes;
  275. if ((*p1 & 0xc0) == 0x80)
  276. { /* Performing the write would lead us to an incomplete
  277. * character. Move some bytes to make up for this. */
  278. unsigned char *p2;
  279. for (p2 = p1 + 1; (*p2 & 0xc0) == 0x80; ++p2) ;
  280. memmove (p1, p2, dp->nbytes - (p2 - dp->datap));
  281. dp->nbytes -= p2 - p1;
  282. }
  283. }
  284. memcpy (dp->datap + dp->curpos, src, bytes);
  285. if ((dp->curpos += bytes) > dp->nbytes)
  286. dp->nbytes = dp->curpos;
  287. return ((int64_t)bytes);
  288. }
  289. static result<bool>
  290. str_seek (interpreter *, stream& strm, spos& pos, int whence)
  291. {
  292. sstream_data *dp = (sstream_data *)strm.cookie;
  293. int64_t roff = pos.offset +
  294. (whence == SEEK_SET ? 0 : whence == SEEK_CUR ?
  295. dp->curpos : dp->nbytes);
  296. if (roff < 0)
  297. return (false);
  298. else if (roff > dp->nbytes)
  299. { // Seeking beyond the end of the string.
  300. if (!(strm.io_flags & STRM_WRITE) || roff > UINT32_MAX)
  301. return (false);
  302. else if (roff > dp->nmax)
  303. dp->datap = (unsigned char *)xrealloc (dp->datap,
  304. dp->nmax = upsize (roff + 1));
  305. memset (&dp->datap[dp->nbytes], 0, roff - dp->nbytes);
  306. }
  307. else
  308. // Make sure we don't end up in the middle of a character.
  309. if ((dp->datap[roff] & 0xc0) == 0x80)
  310. return (false);
  311. if ((dp->curpos = (uint32_t)roff) > dp->nbytes)
  312. dp->nbytes = dp->curpos;
  313. pos.offset = roff;
  314. return (true);
  315. }
  316. static bool
  317. str_close (interpreter *, stream& strm)
  318. {
  319. sstream_data *dp = (sstream_data *)strm.cookie;
  320. if (dp->owned_p)
  321. xfree (dp->datap);
  322. xfree (dp);
  323. strm.extra = UNBOUND;
  324. return (true);
  325. }
  326. static const stream::xops str_ops =
  327. {
  328. str_read,
  329. str_write,
  330. str_seek,
  331. str_close
  332. };
  333. result<stream*> strstream (interpreter *interp, object str, int mode)
  334. {
  335. if (!(mode & STRM_RDWR))
  336. return (nullptr);
  337. string *sp = as_str (str);
  338. sstream_data *dp = (sstream_data *)xmalloc (sizeof (*dp));
  339. mode |= STRM_UTF8;
  340. if (mode & STRM_WRITE)
  341. { // Make a copy of the string buffer.
  342. uint32_t sz = upsize (sp->nbytes + 1);
  343. dp->datap = (unsigned char *)xmalloc (sz);
  344. memcpy (dp->datap, sp->data, sp->nbytes);
  345. dp->datap[sp->nbytes] = '\0';
  346. dp->nmax = sz;
  347. dp->owned_p = true;
  348. }
  349. else
  350. { /* We can use the string buffer itself, but make sure to
  351. * save the string object in the stream 'extra' member. */
  352. dp->datap = sp->data;
  353. dp->nmax = sp->nbytes;
  354. dp->owned_p = false;
  355. }
  356. dp->nbytes = sp->nbytes;
  357. dp->curpos = 0;
  358. auto strm = stream::make (interp, mode, STRM_BUFSIZ, &str_ops, dp);
  359. if (strm.error_p ())
  360. {
  361. if (dp->owned_p)
  362. xfree (dp->datap);
  363. xfree (dp);
  364. return (exception ());
  365. }
  366. stream *ret = deref (strm);
  367. if (!(mode & STRM_WRITE))
  368. ret->extra = str;
  369. return (ret);
  370. }
  371. result<object> sstream_get (interpreter *interp, stream *strm)
  372. {
  373. if (strm->io_flags & STRM_CLOSED)
  374. return (interp->raise ("arg-error", "stream has been closed"));
  375. else if (!(strm->io_flags & STRM_WRITE))
  376. kp_return (strm->extra); // Cached string.
  377. bool rv = KP_TRY (strm->flush (interp));
  378. if (!rv)
  379. return (interp->raise ("io-error", "failed to flush stream"));
  380. // Make up the string from the accumulated bytes.
  381. sstream_data *dp = (sstream_data *)strm->cookie;
  382. return (string::make (interp, (const char *)dp->datap, dp->nbytes));
  383. }
  384. result<int64_t> write_s (interpreter *interp, stream *strm,
  385. object obj, io_info& info)
  386. {
  387. const string *sp = as_str (obj);
  388. if (info.flags & io_info::FLG_RAW)
  389. return (strm->write (interp, sp->data, sp->nbytes));
  390. // Bitmask of special characters.
  391. chmask mask ("\"\n\t\r\a\b\\\0", 8);
  392. int64_t ret = KP_TRY (strm->putb (interp, '"'));
  393. for (auto p = sp->data; p < sp->data + sp->nbytes; )
  394. {
  395. if (kp_likely (!mask.tst (*p)))
  396. {
  397. uint32_t len = UTF8_SKIP[*p];
  398. ret += KP_TRY (strm->write (interp, p, len));
  399. p += len;
  400. continue;
  401. }
  402. char buf[2] = { '\\' };
  403. switch (*p++)
  404. {
  405. case '"':
  406. buf[1] = '"';
  407. break;
  408. case '\n':
  409. buf[1] = 'n';
  410. break;
  411. case '\t':
  412. buf[1] = 't';
  413. break;
  414. case '\r':
  415. buf[1] = 'r';
  416. break;
  417. case '\a':
  418. buf[1] = 'a';
  419. break;
  420. case '\b':
  421. buf[1] = 'b';
  422. break;
  423. case '\\':
  424. buf[1] = '\\';
  425. break;
  426. case '\0':
  427. buf[1] = '0';
  428. break;
  429. }
  430. ret += KP_TRY (strm->write (interp, buf, 2));
  431. }
  432. ret += KP_TRY (strm->putb (interp, '"'));
  433. return (ret);
  434. }
  435. KP_EXPORT const char* chobj_repr (object);
  436. result<int64_t> write_c (interpreter *interp, stream *strm,
  437. object obj, io_info& info)
  438. {
  439. uint32_t ch = as_char (obj);
  440. if (info.flags & io_info::FLG_RAW)
  441. return (strm->putuc (interp, as_char (obj)));
  442. int ret = KP_TRY (strm->putb (interp, '\\'));
  443. const char *repr = chobj_repr (ch);
  444. if (repr != nullptr)
  445. { ret += KP_TRY (strm->write (interp, repr, strlen (repr))); }
  446. else if (ch <= 0x7f)
  447. { ret += KP_TRY (strm->putb (interp, (unsigned char)ch)); }
  448. else
  449. { // Beyond ASCII - Print it as uXXXX
  450. char buf[8];
  451. int len = sprintf (buf, "u%.*x", ch <= 0xffff ? 4 : 8, ch);
  452. ret += KP_TRY (strm->write (interp, buf, len));
  453. }
  454. return (ret);
  455. }
  456. result<int64_t> pack_c (interpreter *interp, stream *strm,
  457. object obj, pack_info&)
  458. {
  459. uint32_t ch = as_char (obj);
  460. return (strm->write (interp, &ch, sizeof (ch)));
  461. }
  462. result<object> unpack_c (interpreter *interp, stream *strm, pack_info& info, bool)
  463. {
  464. uint32_t ch;
  465. bool rv = KP_TRY (strm->sread (interp, &ch));
  466. if (rv)
  467. kp_return (charobj (ch));
  468. return (info.error ("invalid char read"));
  469. }
  470. struct fmt_info
  471. {
  472. io_info io;
  473. int arg_idx;
  474. int argc;
  475. int lidx;
  476. int spec;
  477. chmask spec_mask;
  478. chmask flg_mask;
  479. const char *last_pos;
  480. fmt_info (int nargs) : io (io_info::FLG_RAW), argc (nargs), lidx (1),
  481. spec_mask ("dxXofFgGeEaAscQ"), flg_mask ("#0- +'I")
  482. {
  483. }
  484. result<int> parse (interpreter *interp, const char *str, int nb)
  485. {
  486. this->arg_idx = this->spec = -1;
  487. while (this->flg_mask.tst (*str))
  488. switch (*str++)
  489. {
  490. case '#':
  491. this->io.flags |= io_info::FLG_ALT;
  492. break;
  493. case '0':
  494. this->io.flags |= io_info::FLG_ZERO;
  495. break;
  496. case '-':
  497. this->io.flags |= io_info::FLG_LJUST;
  498. break;
  499. case ' ':
  500. this->io.flags |= io_info::FLG_SPACE;
  501. break;
  502. case '+':
  503. this->io.flags |= io_info::FLG_SIGN;
  504. break;
  505. case '\'':
  506. this->io.flags |= io_info::FLG_I18N;
  507. break;
  508. default:
  509. break;
  510. }
  511. char *endp = 0;
  512. long val = strtol (str, &endp, 10);
  513. if (val == 0 && endp == str)
  514. goto end;
  515. this->io.width = (int)val;
  516. str = endp;
  517. if (*str == '@')
  518. {
  519. if (val >= this->argc)
  520. return (interp->raise_oob (val, this->argc));
  521. this->io.width = io_info::DFL_WIDTH;
  522. this->arg_idx = (int)val;
  523. val = strtol (++str, &endp, 10);
  524. if (val == 0 && endp == str && this->spec_mask.tst (*str))
  525. goto end;
  526. else if (val >= 0)
  527. this->io.width = (int)val;
  528. str = endp;
  529. }
  530. if (*str == '.')
  531. {
  532. val = strtol (++str, &endp, 10);
  533. if (val == 0 && endp == str)
  534. return (interp->raise ("arg-error", "invalid format string"));
  535. else if (val > 0)
  536. this->io.prec = (int)val;
  537. str = endp;
  538. }
  539. end:
  540. if (this->arg_idx < 0 && (this->arg_idx = this->lidx++) >= this->argc)
  541. return (interp->raise_oob (this->lidx, this->argc));
  542. switch (*str++)
  543. {
  544. case 'x': case 'X':
  545. this->io.radix = *str == 'x' ? 16 : -16;
  546. break;
  547. case 'o':
  548. this->io.radix = 8;
  549. break;
  550. case 'a': case 'A':
  551. this->io.radix = *str == 'a' ? 16 : -16;
  552. break;
  553. case 'd': case 'f': case 'F': case 'g': case 'G':
  554. case 'e': case 'E': case 'c': case 's': case 'Q':
  555. break;
  556. default:
  557. return (interp->raise ("arg-error", "invalid specifier"));
  558. }
  559. this->last_pos = str;
  560. return (0);
  561. }
  562. };
  563. result<object> p_fmt_str (interpreter *interp, object *argv, int argc)
  564. {
  565. if (!str_p (*argv))
  566. return (interp->raise ("type-error", "first argument must be a string"));
  567. const string *sp = as_str (*argv);
  568. fmt_info fi (argc);
  569. stream *strm = KP_TRY (strstream (interp, deref (alloc_str (interp, 0)),
  570. STRM_WRITE | STRM_NOLOCK));
  571. for (uint32_t i = 0; i < sp->nbytes; )
  572. {
  573. const char *csp = (const char *)&sp->data[i];
  574. if (*csp != '%')
  575. {
  576. KP_VTRY (strm->write (interp, csp, UTF8_SKIP[(uint8_t)*csp]));
  577. i += UTF8_SKIP[(uint8_t)*csp];
  578. continue;
  579. }
  580. else if (*++csp == '%')
  581. {
  582. KP_VTRY (strm->putb (interp, '%'));
  583. i += 2;
  584. continue;
  585. }
  586. KP_VTRY (fi.parse (interp, csp,
  587. sp->nbytes - (csp - (const char *)sp->data)),
  588. // TODO: Convert according to fi.spec.
  589. xwrite (interp, strm, argv[fi.arg_idx], fi.io));
  590. i += fi.last_pos - csp + 1;
  591. }
  592. return (sstream_get (interp, strm));
  593. }
  594. KP_DECLS_END