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

printf.c 21KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097
  1. /*
  2. * Copyright 2024 Vsevolod Stakhov
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. /* Copyright (C) 2002-2015 Igor Sysoev
  17. * Copyright (C) 2011-2015 Nginx, Inc.
  18. * All rights reserved.
  19. *
  20. * Redistribution and use in source and binary forms, with or without
  21. * modification, are permitted provided that the following conditions are met:
  22. * * Redistributions of source code must retain the above copyright
  23. * notice, this list of conditions and the following disclaimer.
  24. * * Redistributions in binary form must reproduce the above copyright
  25. * notice, this list of conditions and the following disclaimer in the
  26. * documentation and/or other materials provided with the distribution.
  27. *
  28. * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY
  29. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  30. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  31. * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
  32. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  33. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  34. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  35. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  36. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  37. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  38. */
  39. #include "printf.h"
  40. #include "str_util.h"
  41. #include "contrib/fpconv/fpconv.h"
  42. /**
  43. * From FreeBSD libutil code
  44. */
  45. static const int maxscale = 6;
  46. static const char _hex[] = "0123456789abcdef";
  47. static const char _HEX[] = "0123456789ABCDEF";
  48. static char *
  49. rspamd_humanize_number(char *buf, char *last, int64_t num, gboolean bytes)
  50. {
  51. const char *prefixes;
  52. int i, r, remainder, sign;
  53. int64_t divisor;
  54. gsize len = last - buf;
  55. remainder = 0;
  56. if (!bytes) {
  57. divisor = 1000;
  58. prefixes = "\0\0\0\0k\0\0\0M\0\0\0G\0\0\0T\0\0\0P\0\0\0E";
  59. }
  60. else {
  61. divisor = 1024;
  62. prefixes = "B\0\0\0KiB\0MiB\0GiB\0TiB\0PiB\0EiB";
  63. }
  64. #define SCALE2PREFIX(scale) (&prefixes[(scale) *4])
  65. if (num < 0) {
  66. sign = -1;
  67. num = -num;
  68. }
  69. else {
  70. sign = 1;
  71. }
  72. /*
  73. * Divide the number until it fits the given column.
  74. * If there will be an overflow by the rounding below,
  75. * divide once more.
  76. */
  77. for (i = 0; i < maxscale && num > divisor; i++) {
  78. remainder = num % divisor;
  79. num /= divisor;
  80. }
  81. if (remainder == 0 || num > divisor / 2) {
  82. r = rspamd_snprintf(buf, len, "%L%s",
  83. sign * (num + (remainder + 50) / divisor),
  84. SCALE2PREFIX(i));
  85. }
  86. else {
  87. /* Floating point version */
  88. r = rspamd_snprintf(buf, len, "%.2f%s",
  89. sign * (num + remainder / (double) divisor),
  90. SCALE2PREFIX(i));
  91. }
  92. #undef SCALE2PREFIX
  93. return buf + r;
  94. }
  95. static inline unsigned
  96. rspamd_decimal_digits32(uint32_t val)
  97. {
  98. static const uint32_t powers_of_10[] = {
  99. 0,
  100. 10,
  101. 100,
  102. 1000,
  103. 10000,
  104. 100000,
  105. 1000000,
  106. 10000000,
  107. 100000000,
  108. 1000000000};
  109. unsigned tmp;
  110. #if defined(_MSC_VER)
  111. unsigned long r = 0;
  112. _BitScanReverse(&r, val | 1);
  113. tmp = (r + 1) * 1233 >> 12;
  114. #elif defined(__GNUC__) && (__GNUC__ >= 3)
  115. tmp = (32 - __builtin_clz(val | 1U)) * 1233 >> 12;
  116. #else /* Software version */
  117. static const unsigned debruijn_tbl[32] = {0, 9, 1, 10, 13, 21, 2, 29,
  118. 11, 14, 16, 18, 22, 25, 3, 30,
  119. 8, 12, 20, 28, 15, 17, 24, 7,
  120. 19, 27, 23, 6, 26, 5, 4, 31};
  121. uint32_t v = val | 1;
  122. v |= v >> 1;
  123. v |= v >> 2;
  124. v |= v >> 4;
  125. v |= v >> 8;
  126. v |= v >> 16;
  127. tmp = (1 + debruijn_tbl[(v * 0x07C4ACDDU) >> 27]) * 1233 >> 12;
  128. #endif
  129. return tmp - (val < powers_of_10[tmp]) + 1;
  130. }
  131. static inline unsigned
  132. rspamd_decimal_digits64(uint64_t val)
  133. {
  134. static const uint64_t powers_of_10[] = {
  135. 0,
  136. 10ULL,
  137. 100ULL,
  138. 1000ULL,
  139. 10000ULL,
  140. 100000ULL,
  141. 1000000ULL,
  142. 10000000ULL,
  143. 100000000ULL,
  144. 1000000000ULL,
  145. 10000000000ULL,
  146. 100000000000ULL,
  147. 1000000000000ULL,
  148. 10000000000000ULL,
  149. 100000000000000ULL,
  150. 1000000000000000ULL,
  151. 10000000000000000ULL,
  152. 100000000000000000ULL,
  153. 1000000000000000000ULL,
  154. 10000000000000000000ULL};
  155. unsigned tmp;
  156. #if defined(_MSC_VER)
  157. #if _M_IX86
  158. unsigned long r = 0;
  159. uint64_t m = val | 1;
  160. if (_BitScanReverse(&r, m >> 32)) {
  161. r += 32;
  162. }
  163. else {
  164. _BitScanReverse(&r, m & 0xFFFFFFFF);
  165. }
  166. tmp = (r + 1) * 1233 >> 12;
  167. #else
  168. unsigned long r = 0;
  169. _BitScanReverse64(&r, val | 1);
  170. tmp = (r + 1) * 1233 >> 12;
  171. #endif
  172. #elif defined(__GNUC__) && (__GNUC__ >= 3)
  173. tmp = (64 - __builtin_clzll(val | 1ULL)) * 1233 >> 12;
  174. #else /* Software version */
  175. static const unsigned debruijn_tbl[32] = {0, 9, 1, 10, 13, 21, 2, 29,
  176. 11, 14, 16, 18, 22, 25, 3, 30,
  177. 8, 12, 20, 28, 15, 17, 24, 7,
  178. 19, 27, 23, 6, 26, 5, 4, 31};
  179. uint32_t v = val >> 32;
  180. if (v) {
  181. v |= 1;
  182. v |= v >> 1;
  183. v |= v >> 2;
  184. v |= v >> 4;
  185. v |= v >> 8;
  186. v |= v >> 16;
  187. tmp = 32 + debruijn_tbl[(v * 0x07C4ACDDU) >> 27];
  188. }
  189. else {
  190. v = val & 0xFFFFFFFF;
  191. v |= 1;
  192. v |= v >> 1;
  193. v |= v >> 2;
  194. v |= v >> 4;
  195. v |= v >> 8;
  196. v |= v >> 16;
  197. tmp = debruijn_tbl[(v * 0x07C4ACDDU) >> 27];
  198. }
  199. tmp = (tmp + 1) * 1233 >> 12;
  200. #endif
  201. return tmp - (val < powers_of_10[tmp]) + 1;
  202. }
  203. /*
  204. * Idea from https://github.com/miloyip/itoa-benchmark:
  205. * Uses lookup table (LUT) of digit pairs for division/modulo of 100.
  206. *
  207. * Mentioned in:
  208. * https://www.slideshare.net/andreialexandrescu1/three-optimization-tips-for-c-15708507
  209. */
  210. static const char int_lookup_table[200] = {
  211. '0', '0', '0', '1', '0', '2', '0', '3', '0', '4',
  212. '0', '5', '0', '6', '0', '7', '0', '8', '0', '9',
  213. '1', '0', '1', '1', '1', '2', '1', '3', '1', '4',
  214. '1', '5', '1', '6', '1', '7', '1', '8', '1', '9',
  215. '2', '0', '2', '1', '2', '2', '2', '3', '2', '4',
  216. '2', '5', '2', '6', '2', '7', '2', '8', '2', '9',
  217. '3', '0', '3', '1', '3', '2', '3', '3', '3', '4',
  218. '3', '5', '3', '6', '3', '7', '3', '8', '3', '9',
  219. '4', '0', '4', '1', '4', '2', '4', '3', '4', '4',
  220. '4', '5', '4', '6', '4', '7', '4', '8', '4', '9',
  221. '5', '0', '5', '1', '5', '2', '5', '3', '5', '4',
  222. '5', '5', '5', '6', '5', '7', '5', '8', '5', '9',
  223. '6', '0', '6', '1', '6', '2', '6', '3', '6', '4',
  224. '6', '5', '6', '6', '6', '7', '6', '8', '6', '9',
  225. '7', '0', '7', '1', '7', '2', '7', '3', '7', '4',
  226. '7', '5', '7', '6', '7', '7', '7', '8', '7', '9',
  227. '8', '0', '8', '1', '8', '2', '8', '3', '8', '4',
  228. '8', '5', '8', '6', '8', '7', '8', '8', '8', '9',
  229. '9', '0', '9', '1', '9', '2', '9', '3', '9', '4',
  230. '9', '5', '9', '6', '9', '7', '9', '8', '9', '9'};
  231. static inline unsigned int
  232. rspamd_uint32_print(uint32_t in, char *out)
  233. {
  234. unsigned int ndigits = rspamd_decimal_digits32(in);
  235. char *p;
  236. p = out + ndigits - 1;
  237. while (in >= 100) {
  238. unsigned idx = (in % 100) * 2;
  239. /* Do two digits at once */
  240. *p-- = int_lookup_table[idx + 1];
  241. *p-- = int_lookup_table[idx];
  242. in /= 100;
  243. }
  244. if (in < 10) {
  245. *p = ((char) in) + '0';
  246. }
  247. else {
  248. unsigned idx = in * 2;
  249. *p-- = int_lookup_table[idx + 1];
  250. *p = int_lookup_table[idx];
  251. }
  252. return ndigits;
  253. }
  254. static inline unsigned int
  255. rspamd_uint64_print(uint64_t in, char *out)
  256. {
  257. unsigned int ndigits = rspamd_decimal_digits64(in);
  258. uint32_t v32;
  259. char *p;
  260. p = out + ndigits - 1;
  261. while (in >= 100000000) {
  262. v32 = (uint32_t) (in % 100000000);
  263. uint32_t a, b, a1, a2, b1, b2;
  264. /* Initial spill */
  265. a = v32 / 10000;
  266. b = v32 % 10000;
  267. a1 = (a / 100) * 2;
  268. a2 = (a % 100) * 2;
  269. b1 = (b / 100) * 2;
  270. b2 = (b % 100) * 2;
  271. /* Fill 8 digits at once */
  272. *p-- = int_lookup_table[b2 + 1];
  273. *p-- = int_lookup_table[b2];
  274. *p-- = int_lookup_table[b1 + 1];
  275. *p-- = int_lookup_table[b1];
  276. *p-- = int_lookup_table[a2 + 1];
  277. *p-- = int_lookup_table[a2];
  278. *p-- = int_lookup_table[a1 + 1];
  279. *p-- = int_lookup_table[a1];
  280. in /= 100000000;
  281. }
  282. /* Remaining 32 bit */
  283. v32 = (uint32_t) in;
  284. while (v32 >= 100) {
  285. unsigned idx = (v32 % 100) << 1;
  286. /* Do 2 digits at once */
  287. *p-- = int_lookup_table[idx + 1];
  288. *p-- = int_lookup_table[idx];
  289. v32 /= 100;
  290. }
  291. if (v32 < 10) {
  292. *p = ((char) v32) + '0';
  293. }
  294. else {
  295. unsigned idx = v32 * 2;
  296. *p-- = int_lookup_table[idx + 1];
  297. *p = int_lookup_table[idx];
  298. }
  299. return ndigits;
  300. }
  301. static inline int
  302. rspamd_ffsll(long long n)
  303. {
  304. #ifdef __has_builtin
  305. #if __has_builtin(__builtin_ffsll)
  306. return __builtin_ffsll(n);
  307. #elif __has_builtin(__builtin_ctzll)
  308. if (n == 0) {
  309. return 0;
  310. }
  311. return __builtin_ctzll(n) + 1;
  312. #endif
  313. #endif /* __has_builtin */
  314. #ifdef HAVE_FFSL
  315. return ffsl(n);
  316. #else
  317. if (n == 0) {
  318. return 0;
  319. }
  320. int bit;
  321. for (bit = 1; !(n & 1); bit++) {
  322. n = ((unsigned long long) n) >> 1;
  323. }
  324. return bit;
  325. #endif
  326. }
  327. static char *
  328. rspamd_sprintf_num(char *buf, char *last, uint64_t ui64, char zero,
  329. unsigned int hexadecimal, unsigned int binary, unsigned int width)
  330. {
  331. char *p, temp[64];
  332. size_t len = 0;
  333. if (G_LIKELY(hexadecimal == 0 && binary == 0)) {
  334. p = temp;
  335. if (ui64 < G_MAXUINT32) {
  336. len = rspamd_uint32_print((uint32_t) ui64, temp);
  337. }
  338. else {
  339. len = rspamd_uint64_print(ui64, temp);
  340. }
  341. }
  342. else if (hexadecimal == 1) {
  343. p = temp + sizeof(temp);
  344. do {
  345. *--p = _hex[(uint32_t) (ui64 & 0xf)];
  346. } while (ui64 >>= 4);
  347. len = (temp + sizeof(temp)) - p;
  348. }
  349. else if (hexadecimal == 2) { /* hexadecimal == 2 */
  350. p = temp + sizeof(temp);
  351. do {
  352. *--p = _HEX[(uint32_t) (ui64 & 0xf)];
  353. } while (ui64 >>= 4);
  354. len = (temp + sizeof(temp)) - p;
  355. }
  356. else if (binary > 0) {
  357. int first_bit = MIN(sizeof(temp), rspamd_ffsll(ui64));
  358. p = temp + sizeof(temp);
  359. for (int i = 0; i <= first_bit; i++, ui64 >>= 1) {
  360. *--p = '0' + (ui64 & 0x1);
  361. }
  362. len = (temp + sizeof(temp)) - p;
  363. }
  364. /* zero or space padding */
  365. if (len < width) {
  366. width -= len;
  367. while (width-- > 0 && buf < last) {
  368. *buf++ = zero;
  369. }
  370. }
  371. /* number safe copy */
  372. if (buf + len > last) {
  373. len = last - buf;
  374. }
  375. return ((char *) memcpy(buf, p, len)) + len;
  376. }
  377. struct rspamd_printf_char_buf {
  378. char *begin;
  379. char *pos;
  380. glong remain;
  381. };
  382. static glong
  383. rspamd_printf_append_char(const char *buf, glong buflen, gpointer ud)
  384. {
  385. struct rspamd_printf_char_buf *dst = (struct rspamd_printf_char_buf *) ud;
  386. glong wr;
  387. if (dst->remain <= 0) {
  388. return dst->remain;
  389. }
  390. wr = MIN(dst->remain, buflen);
  391. memcpy(dst->pos, buf, wr);
  392. dst->remain -= wr;
  393. dst->pos += wr;
  394. return wr;
  395. }
  396. static glong
  397. rspamd_printf_append_file(const char *buf, glong buflen, gpointer ud)
  398. {
  399. FILE *dst = (FILE *) ud;
  400. if (buflen > 0) {
  401. return fwrite(buf, 1, buflen, dst);
  402. }
  403. else {
  404. return 0;
  405. }
  406. }
  407. static glong
  408. rspamd_printf_append_gstring(const char *buf, glong buflen, gpointer ud)
  409. {
  410. GString *dst = (GString *) ud;
  411. if (buflen > 0) {
  412. g_string_append_len(dst, buf, buflen);
  413. }
  414. return buflen;
  415. }
  416. static glong
  417. rspamd_printf_append_fstring(const char *buf, glong buflen, gpointer ud)
  418. {
  419. rspamd_fstring_t **dst = ud;
  420. if (buflen > 0) {
  421. *dst = rspamd_fstring_append(*dst, buf, buflen);
  422. }
  423. return buflen;
  424. }
  425. glong rspamd_fprintf(FILE *f, const char *fmt, ...)
  426. {
  427. va_list args;
  428. glong r;
  429. va_start(args, fmt);
  430. r = rspamd_vprintf_common(rspamd_printf_append_file, f, fmt, args);
  431. va_end(args);
  432. return r;
  433. }
  434. glong rspamd_printf(const char *fmt, ...)
  435. {
  436. va_list args;
  437. glong r;
  438. va_start(args, fmt);
  439. r = rspamd_vprintf_common(rspamd_printf_append_file, stdout, fmt, args);
  440. va_end(args);
  441. return r;
  442. }
  443. glong rspamd_log_fprintf(FILE *f, const char *fmt, ...)
  444. {
  445. va_list args;
  446. glong r;
  447. va_start(args, fmt);
  448. r = rspamd_vprintf_common(rspamd_printf_append_file, f, fmt, args);
  449. va_end(args);
  450. fflush(f);
  451. return r;
  452. }
  453. glong rspamd_snprintf(char *buf, glong max, const char *fmt, ...)
  454. {
  455. char *r;
  456. va_list args;
  457. va_start(args, fmt);
  458. r = rspamd_vsnprintf(buf, max, fmt, args);
  459. va_end(args);
  460. return (r - buf);
  461. }
  462. char *
  463. rspamd_vsnprintf(char *buf, glong max, const char *fmt, va_list args)
  464. {
  465. struct rspamd_printf_char_buf dst;
  466. dst.begin = buf;
  467. dst.pos = dst.begin;
  468. dst.remain = max - 1;
  469. (void) rspamd_vprintf_common(rspamd_printf_append_char, &dst, fmt, args);
  470. *dst.pos = '\0';
  471. return dst.pos;
  472. }
  473. glong rspamd_printf_gstring(GString *s, const char *fmt, ...)
  474. {
  475. va_list args;
  476. glong r;
  477. va_start(args, fmt);
  478. r = rspamd_vprintf_gstring(s, fmt, args);
  479. va_end(args);
  480. return r;
  481. }
  482. glong rspamd_vprintf_gstring(GString *s, const char *fmt, va_list args)
  483. {
  484. return rspamd_vprintf_common(rspamd_printf_append_gstring, s, fmt, args);
  485. }
  486. glong rspamd_printf_fstring(rspamd_fstring_t **s, const char *fmt, ...)
  487. {
  488. va_list args;
  489. glong r;
  490. va_start(args, fmt);
  491. r = rspamd_vprintf_fstring(s, fmt, args);
  492. va_end(args);
  493. return r;
  494. }
  495. glong rspamd_vprintf_fstring(rspamd_fstring_t **s, const char *fmt, va_list args)
  496. {
  497. return rspamd_vprintf_common(rspamd_printf_append_fstring, s, fmt, args);
  498. }
  499. #define RSPAMD_PRINTF_APPEND(buf, len) \
  500. do { \
  501. RSPAMD_PRINTF_APPEND_BUF(buf, len); \
  502. fmt++; \
  503. buf_start = fmt; \
  504. } while (0)
  505. #define RSPAMD_PRINTF_APPEND_BUF(buf, len) \
  506. do { \
  507. wr = func((buf), (len), apd); \
  508. if (wr < (__typeof(wr)) (len)) { \
  509. goto oob; \
  510. } \
  511. written += wr; \
  512. } while (0)
  513. glong rspamd_vprintf_common(rspamd_printf_append_func func,
  514. gpointer apd,
  515. const char *fmt,
  516. va_list args)
  517. {
  518. char zero, numbuf[G_ASCII_DTOSTR_BUF_SIZE], dtoabuf[32], *p, *last;
  519. unsigned char c;
  520. const char *buf_start = fmt;
  521. int d;
  522. double f;
  523. glong written = 0, wr, slen;
  524. int64_t i64;
  525. uint64_t ui64;
  526. unsigned int width, sign, hex, humanize, bytes, frac_width, b32, b64;
  527. rspamd_fstring_t *v;
  528. rspamd_ftok_t *tok;
  529. GString *gs;
  530. GError *err;
  531. while (*fmt) {
  532. /*
  533. * "buf < last" means that we could copy at least one character:
  534. * the plain character, "%%", "%c", and minus without the checking
  535. */
  536. if (*fmt == '%') {
  537. /* Append what we have in buf */
  538. if (fmt > buf_start) {
  539. wr = func(buf_start, fmt - buf_start, apd);
  540. if (wr <= 0) {
  541. goto oob;
  542. }
  543. written += wr;
  544. }
  545. i64 = 0;
  546. ui64 = 0;
  547. zero = (char) ((*++fmt == '0') ? '0' : ' ');
  548. width = 0;
  549. sign = 1;
  550. hex = 0;
  551. b32 = 0;
  552. b64 = 0;
  553. bytes = 0;
  554. humanize = 0;
  555. frac_width = 0;
  556. slen = -1;
  557. while (*fmt >= '0' && *fmt <= '9') {
  558. width = width * 10 + *fmt++ - '0';
  559. }
  560. for (;;) {
  561. switch (*fmt) {
  562. case 'u':
  563. sign = 0;
  564. fmt++;
  565. continue;
  566. case 'm':
  567. fmt++;
  568. continue;
  569. case 'X':
  570. hex = 2;
  571. sign = 0;
  572. fmt++;
  573. continue;
  574. case 'x':
  575. hex = 1;
  576. sign = 0;
  577. fmt++;
  578. continue;
  579. case 'b':
  580. b32 = 1;
  581. sign = 0;
  582. fmt++;
  583. continue;
  584. case 'B':
  585. b64 = 1;
  586. sign = 0;
  587. fmt++;
  588. continue;
  589. case 'H':
  590. humanize = 1;
  591. bytes = 1;
  592. sign = 0;
  593. fmt++;
  594. continue;
  595. case 'h':
  596. humanize = 1;
  597. sign = 0;
  598. fmt++;
  599. continue;
  600. case '.':
  601. fmt++;
  602. if (*fmt == '*') {
  603. d = (int) va_arg(args, int);
  604. if (G_UNLIKELY(d < 0)) {
  605. return 0;
  606. }
  607. frac_width = (unsigned int) d;
  608. fmt++;
  609. }
  610. else {
  611. while (*fmt >= '0' && *fmt <= '9') {
  612. frac_width = frac_width * 10 + *fmt++ - '0';
  613. }
  614. }
  615. break;
  616. case '*':
  617. d = (int) va_arg(args, int);
  618. if (G_UNLIKELY(d < 0)) {
  619. return 0;
  620. }
  621. slen = (glong) d;
  622. fmt++;
  623. continue;
  624. default:
  625. break;
  626. }
  627. break;
  628. }
  629. switch (*fmt) {
  630. case 'V':
  631. v = va_arg(args, rspamd_fstring_t *);
  632. if (v) {
  633. slen = v->len;
  634. if (G_UNLIKELY(width != 0)) {
  635. slen = MIN(v->len, width);
  636. }
  637. RSPAMD_PRINTF_APPEND(v->str, slen);
  638. }
  639. else {
  640. RSPAMD_PRINTF_APPEND("(NULL)", 6);
  641. }
  642. continue;
  643. case 'T':
  644. tok = va_arg(args, rspamd_ftok_t *);
  645. if (tok) {
  646. slen = tok->len;
  647. if (G_UNLIKELY(width != 0)) {
  648. slen = MIN(tok->len, width);
  649. }
  650. RSPAMD_PRINTF_APPEND(tok->begin, slen);
  651. }
  652. else {
  653. RSPAMD_PRINTF_APPEND("(NULL)", 6);
  654. }
  655. continue;
  656. case 'v':
  657. gs = va_arg(args, GString *);
  658. if (gs) {
  659. slen = gs->len;
  660. if (G_UNLIKELY(width != 0)) {
  661. slen = MIN(gs->len, width);
  662. }
  663. RSPAMD_PRINTF_APPEND(gs->str, slen);
  664. }
  665. else {
  666. RSPAMD_PRINTF_APPEND("(NULL)", 6);
  667. }
  668. continue;
  669. case 'e':
  670. err = va_arg(args, GError *);
  671. if (err) {
  672. p = err->message;
  673. if (p == NULL) {
  674. p = "(NULL)";
  675. }
  676. }
  677. else {
  678. p = "unknown error";
  679. }
  680. slen = strlen(p);
  681. RSPAMD_PRINTF_APPEND(p, slen);
  682. continue;
  683. case 's':
  684. p = va_arg(args, char *);
  685. if (p == NULL) {
  686. p = "(NULL)";
  687. slen = sizeof("(NULL)") - 1;
  688. }
  689. if (G_UNLIKELY(b32)) {
  690. char *b32buf;
  691. if (G_UNLIKELY(slen == -1)) {
  692. if (G_LIKELY(width != 0)) {
  693. slen = width;
  694. }
  695. else {
  696. /* NULL terminated string */
  697. slen = strlen(p);
  698. }
  699. }
  700. b32buf = rspamd_encode_base32(p, slen, RSPAMD_BASE32_DEFAULT);
  701. if (b32buf) {
  702. RSPAMD_PRINTF_APPEND(b32buf, strlen(b32buf));
  703. g_free(b32buf);
  704. }
  705. else {
  706. RSPAMD_PRINTF_APPEND("(NULL)", sizeof("(NULL)") - 1);
  707. }
  708. }
  709. else if (G_UNLIKELY(hex)) {
  710. char hexbuf[2];
  711. if (G_UNLIKELY(slen == -1)) {
  712. if (G_LIKELY(width != 0)) {
  713. slen = width;
  714. }
  715. else {
  716. /* NULL terminated string */
  717. slen = strlen(p);
  718. }
  719. }
  720. while (slen) {
  721. hexbuf[0] = hex == 2 ? _HEX[(*p >> 4u) & 0xfu] : _hex[(*p >> 4u) & 0xfu];
  722. hexbuf[1] = hex == 2 ? _HEX[*p & 0xfu] : _hex[*p & 0xfu];
  723. RSPAMD_PRINTF_APPEND_BUF(hexbuf, 2);
  724. p++;
  725. slen--;
  726. }
  727. fmt++;
  728. buf_start = fmt;
  729. }
  730. else if (G_UNLIKELY(b64)) {
  731. char *b64buf;
  732. gsize olen = 0;
  733. if (G_UNLIKELY(slen == -1)) {
  734. if (G_LIKELY(width != 0)) {
  735. slen = width;
  736. }
  737. else {
  738. /* NULL terminated string */
  739. slen = strlen(p);
  740. }
  741. }
  742. b64buf = rspamd_encode_base64(p, slen, 0, &olen);
  743. if (b64buf) {
  744. RSPAMD_PRINTF_APPEND(b64buf, olen);
  745. g_free(b64buf);
  746. }
  747. else {
  748. RSPAMD_PRINTF_APPEND("(NULL)", sizeof("(NULL)") - 1);
  749. }
  750. }
  751. else {
  752. if (slen == -1) {
  753. /* NULL terminated string */
  754. slen = strlen(p);
  755. }
  756. if (G_UNLIKELY(width != 0)) {
  757. slen = MIN(slen, width);
  758. }
  759. RSPAMD_PRINTF_APPEND(p, slen);
  760. }
  761. continue;
  762. case 'O':
  763. i64 = (int64_t) va_arg(args, off_t);
  764. sign = 1;
  765. break;
  766. case 'P':
  767. i64 = (int64_t) va_arg(args, pid_t);
  768. sign = 1;
  769. break;
  770. case 't':
  771. i64 = (int64_t) va_arg(args, time_t);
  772. sign = 1;
  773. break;
  774. case 'z':
  775. if (sign) {
  776. i64 = (int64_t) va_arg(args, ssize_t);
  777. }
  778. else {
  779. ui64 = (uint64_t) va_arg(args, size_t);
  780. }
  781. break;
  782. case 'd':
  783. if (sign) {
  784. i64 = (int64_t) va_arg(args, int);
  785. }
  786. else {
  787. ui64 = (uint64_t) va_arg(args, unsigned int);
  788. }
  789. break;
  790. case 'l':
  791. if (sign) {
  792. i64 = (int64_t) va_arg(args, glong);
  793. }
  794. else {
  795. ui64 = (uint64_t) va_arg(args, gulong);
  796. }
  797. break;
  798. case 'D':
  799. if (sign) {
  800. i64 = (int64_t) va_arg(args, int32_t);
  801. }
  802. else {
  803. ui64 = (uint64_t) va_arg(args, uint32_t);
  804. }
  805. break;
  806. case 'L':
  807. if (sign) {
  808. i64 = va_arg(args, int64_t);
  809. }
  810. else {
  811. ui64 = va_arg(args, uint64_t);
  812. }
  813. break;
  814. case 'f':
  815. f = (double) va_arg(args, double);
  816. slen = fpconv_dtoa(f, dtoabuf, frac_width, false);
  817. RSPAMD_PRINTF_APPEND(dtoabuf, slen);
  818. continue;
  819. case 'g':
  820. f = (double) va_arg(args, double);
  821. slen = fpconv_dtoa(f, dtoabuf, 0, true);
  822. RSPAMD_PRINTF_APPEND(dtoabuf, slen);
  823. continue;
  824. case 'F':
  825. f = (double) va_arg(args, long double);
  826. slen = fpconv_dtoa(f, dtoabuf, frac_width, false);
  827. RSPAMD_PRINTF_APPEND(dtoabuf, slen);
  828. continue;
  829. case 'G':
  830. f = (double) va_arg(args, long double);
  831. slen = fpconv_dtoa(f, dtoabuf, 0, true);
  832. RSPAMD_PRINTF_APPEND(dtoabuf, slen);
  833. continue;
  834. case 'p':
  835. ui64 = (uintptr_t) va_arg(args, void *);
  836. hex = 2;
  837. sign = 0;
  838. zero = '0';
  839. width = sizeof(void *) * 2;
  840. break;
  841. case 'c':
  842. c = va_arg(args, int);
  843. c &= 0xffu;
  844. if (G_UNLIKELY(hex)) {
  845. char hexbuf[2];
  846. hexbuf[0] = hex == 2 ? _HEX[(c >> 4u) & 0xfu] : _hex[(c >> 4u) & 0xfu];
  847. hexbuf[1] = hex == 2 ? _HEX[c & 0xfu] : _hex[c & 0xfu];
  848. RSPAMD_PRINTF_APPEND(hexbuf, 2);
  849. }
  850. else {
  851. RSPAMD_PRINTF_APPEND(&c, 1);
  852. }
  853. continue;
  854. case 'Z':
  855. c = '\0';
  856. RSPAMD_PRINTF_APPEND(&c, 1);
  857. continue;
  858. case 'N':
  859. c = '\n';
  860. RSPAMD_PRINTF_APPEND(&c, 1);
  861. continue;
  862. case '%':
  863. c = '%';
  864. RSPAMD_PRINTF_APPEND(&c, 1);
  865. continue;
  866. default:
  867. c = *fmt;
  868. RSPAMD_PRINTF_APPEND(&c, 1);
  869. continue;
  870. }
  871. /* Print number */
  872. p = numbuf;
  873. last = p + sizeof(numbuf);
  874. if (sign) {
  875. if (i64 < 0) {
  876. *p++ = '-';
  877. ui64 = (uint64_t) -i64;
  878. }
  879. else {
  880. ui64 = (uint64_t) i64;
  881. }
  882. }
  883. if (!humanize) {
  884. p = rspamd_sprintf_num(p, last, ui64, zero, hex, b64 + b32, width);
  885. }
  886. else {
  887. p = rspamd_humanize_number(p, last, ui64, bytes);
  888. }
  889. slen = p - numbuf;
  890. RSPAMD_PRINTF_APPEND(numbuf, slen);
  891. }
  892. else {
  893. fmt++;
  894. }
  895. }
  896. /* Finish buffer */
  897. if (fmt > buf_start) {
  898. wr = func(buf_start, fmt - buf_start, apd);
  899. if (wr <= 0) {
  900. goto oob;
  901. }
  902. written += wr;
  903. }
  904. oob:
  905. return written;
  906. }