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 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675
  1. /* Copyright (c) 2010, Vsevolod Stakhov
  2. * All rights reserved.
  3. *
  4. * Redistribution and use in source and binary forms, with or without
  5. * modification, are permitted provided that the following conditions are met:
  6. * * Redistributions of source code must retain the above copyright
  7. * notice, this list of conditions and the following disclaimer.
  8. * * Redistributions in binary form must reproduce the above copyright
  9. * notice, this list of conditions and the following disclaimer in the
  10. * documentation and/or other materials provided with the distribution.
  11. *
  12. * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY
  13. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  14. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  15. * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
  16. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  17. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  18. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  19. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  20. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  21. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  22. */
  23. #include "printf.h"
  24. #include "fstring.h"
  25. #include "main.h"
  26. /**
  27. * From FreeBSD libutil code
  28. */
  29. static const int maxscale = 7;
  30. static gchar *
  31. rspamd_humanize_number (gchar *buf, gchar *last, gint64 num, gboolean bytes)
  32. {
  33. const gchar *prefixes;
  34. int i, r, remainder, sign;
  35. gint64 divisor;
  36. gsize baselen, len = last - buf;
  37. remainder = 0;
  38. baselen = 1;
  39. if (!bytes) {
  40. divisor = 1000;
  41. prefixes = "\0\0\0k\0\0M\0\0G\0\0T\0\0P\0\0E";
  42. }
  43. else {
  44. divisor = 1024;
  45. prefixes = "B\0\0k\0\0M\0\0G\0\0T\0\0P\0\0E";
  46. }
  47. #define SCALE2PREFIX(scale) (&prefixes[(scale) * 3])
  48. if (num < 0) {
  49. sign = -1;
  50. num = -num;
  51. baselen += 2; /* sign, digit */
  52. }
  53. else {
  54. sign = 1;
  55. baselen += 1; /* digit */
  56. }
  57. /* Check if enough room for `x y' + suffix + `\0' */
  58. if (len < baselen + 1) {
  59. return buf;
  60. }
  61. /*
  62. * Divide the number until it fits the given column.
  63. * If there will be an overflow by the rounding below,
  64. * divide once more.
  65. */
  66. for (i = 0; i < maxscale && num > divisor; i++) {
  67. remainder = num % divisor;
  68. num /= divisor;
  69. }
  70. r = rspamd_snprintf (buf, len, "%L%s",
  71. sign * (num + (remainder + 50) / 1000),
  72. SCALE2PREFIX (i));
  73. #undef SCALE2PREFIX
  74. return buf + r;
  75. }
  76. static gchar *
  77. rspamd_sprintf_num (gchar *buf, gchar *last, guint64 ui64, gchar zero,
  78. guint hexadecimal, guint width)
  79. {
  80. gchar *p, temp[sizeof ("18446744073709551615")];
  81. size_t len;
  82. guint32 ui32;
  83. static gchar hex[] = "0123456789abcdef";
  84. static gchar HEX[] = "0123456789ABCDEF";
  85. p = temp + sizeof(temp);
  86. if (hexadecimal == 0) {
  87. if (ui64 <= G_MAXUINT32) {
  88. /*
  89. * To divide 64-bit numbers and to find remainders
  90. * on the x86 platform gcc and icc call the libc functions
  91. * [u]divdi3() and [u]moddi3(), they call another function
  92. * in its turn. On FreeBSD it is the qdivrem() function,
  93. * its source code is about 170 lines of the code.
  94. * The glibc counterpart is about 150 lines of the code.
  95. *
  96. * For 32-bit numbers and some divisors gcc and icc use
  97. * a inlined multiplication and shifts. For example,
  98. * guint "i32 / 10" is compiled to
  99. *
  100. * (i32 * 0xCCCCCCCD) >> 35
  101. */
  102. ui32 = (guint32) ui64;
  103. do {
  104. *--p = (gchar) (ui32 % 10 + '0');
  105. } while (ui32 /= 10);
  106. } else {
  107. do {
  108. *--p = (gchar) (ui64 % 10 + '0');
  109. } while (ui64 /= 10);
  110. }
  111. } else if (hexadecimal == 1) {
  112. do {
  113. /* the "(guint32)" cast disables the BCC's warning */
  114. *--p = hex[(guint32) (ui64 & 0xf)];
  115. } while (ui64 >>= 4);
  116. } else { /* hexadecimal == 2 */
  117. do {
  118. /* the "(guint32)" cast disables the BCC's warning */
  119. *--p = HEX[(guint32) (ui64 & 0xf)];
  120. } while (ui64 >>= 4);
  121. }
  122. /* zero or space padding */
  123. len = (temp + sizeof (temp)) - p;
  124. while (len++ < width && buf < last) {
  125. *buf++ = zero;
  126. }
  127. /* number safe copy */
  128. len = (temp + sizeof (temp)) - p;
  129. if (buf + len > last) {
  130. len = last - buf;
  131. }
  132. return ((gchar *)memcpy (buf, p, len)) + len;
  133. }
  134. gint
  135. rspamd_fprintf (FILE *f, const gchar *fmt, ...)
  136. {
  137. va_list args;
  138. gchar buf[BUFSIZ];
  139. gint r;
  140. va_start (args, fmt);
  141. rspamd_vsnprintf (buf, sizeof (buf), fmt, args);
  142. va_end (args);
  143. r = fprintf (f, "%s", buf);
  144. return r;
  145. }
  146. gint
  147. rspamd_log_fprintf (FILE *f, const gchar *fmt, ...)
  148. {
  149. va_list args;
  150. gchar buf[BUFSIZ];
  151. gint r;
  152. va_start (args, fmt);
  153. rspamd_vsnprintf (buf, sizeof (buf), fmt, args);
  154. va_end (args);
  155. r = fprintf (f, "%s\n", buf);
  156. fflush (f);
  157. return r;
  158. }
  159. gint
  160. rspamd_sprintf (gchar *buf, const gchar *fmt, ...)
  161. {
  162. gchar *p;
  163. va_list args;
  164. va_start (args, fmt);
  165. p = rspamd_vsnprintf (buf, /* STUB */ 65536, fmt, args);
  166. va_end (args);
  167. return p - buf;
  168. }
  169. gint
  170. rspamd_snprintf (gchar *buf, glong max, const gchar *fmt, ...)
  171. {
  172. gchar *p;
  173. va_list args;
  174. va_start (args, fmt);
  175. p = rspamd_vsnprintf (buf, max - 1, fmt, args);
  176. va_end (args);
  177. *p = '\0';
  178. return p - buf;
  179. }
  180. gchar *
  181. rspamd_escape_string (gchar *dst, const gchar *src, glong len)
  182. {
  183. gchar *buf = dst, *last = dst + len;
  184. guint8 c;
  185. const gchar *p = src;
  186. gunichar uc;
  187. if (len <= 0) {
  188. return dst;
  189. }
  190. while (*p && buf < last) {
  191. /* Detect utf8 */
  192. uc = g_utf8_get_char_validated (p, last - buf);
  193. if (uc > 0) {
  194. c = g_unichar_to_utf8 (uc, buf);
  195. buf += c;
  196. p += c;
  197. }
  198. else {
  199. c = *p ++;
  200. if (G_UNLIKELY ((c & 0x80))) {
  201. c &= 0x7F;
  202. if (last - buf >= 3) {
  203. *buf++ = 'M';
  204. *buf++ = '-';
  205. }
  206. }
  207. if (G_UNLIKELY ( g_ascii_iscntrl (c))) {
  208. if (c == '\n') {
  209. *buf++ = ' ';
  210. }
  211. else if (c == '\t') {
  212. *buf++ = '\t';
  213. }
  214. else {
  215. *buf++ = '^';
  216. if (buf != last) {
  217. *buf++ = c ^ 0100;
  218. }
  219. }
  220. }
  221. else {
  222. *buf++ = c;
  223. }
  224. }
  225. }
  226. *buf = '\0';
  227. return buf;
  228. }
  229. gchar *
  230. rspamd_vsnprintf (gchar *buf, glong max, const gchar *fmt, va_list args)
  231. {
  232. gchar *p, zero, *last;
  233. gint d;
  234. long double f, scale;
  235. size_t len, slen;
  236. gint64 i64;
  237. guint64 ui64;
  238. guint width, sign, hex, humanize, bytes, frac_width, i;
  239. f_str_t *v;
  240. GString *gs;
  241. if (max <= 0) {
  242. return buf;
  243. }
  244. last = buf + max;
  245. while (*fmt && buf < last) {
  246. /*
  247. * "buf < last" means that we could copy at least one character:
  248. * the plain character, "%%", "%c", and minus without the checking
  249. */
  250. if (*fmt == '%') {
  251. i64 = 0;
  252. ui64 = 0;
  253. zero = (gchar) ((*++fmt == '0') ? '0' : ' ');
  254. width = 0;
  255. sign = 1;
  256. hex = 0;
  257. bytes = 0;
  258. humanize = 0;
  259. frac_width = 0;
  260. slen = (size_t) -1;
  261. while (*fmt >= '0' && *fmt <= '9') {
  262. width = width * 10 + *fmt++ - '0';
  263. }
  264. for ( ;; ) {
  265. switch (*fmt) {
  266. case 'u':
  267. sign = 0;
  268. fmt++;
  269. continue;
  270. case 'm':
  271. fmt++;
  272. continue;
  273. case 'X':
  274. hex = 2;
  275. sign = 0;
  276. fmt++;
  277. continue;
  278. case 'x':
  279. hex = 1;
  280. sign = 0;
  281. fmt++;
  282. continue;
  283. case 'H':
  284. humanize = 1;
  285. bytes = 1;
  286. sign = 0;
  287. fmt ++;
  288. continue;
  289. case 'h':
  290. humanize = 1;
  291. sign = 0;
  292. fmt ++;
  293. continue;
  294. case '.':
  295. fmt++;
  296. while (*fmt >= '0' && *fmt <= '9') {
  297. frac_width = frac_width * 10 + *fmt++ - '0';
  298. }
  299. break;
  300. case '*':
  301. d = (gint)va_arg (args, gint);
  302. if (G_UNLIKELY (d < 0)) {
  303. msg_err ("crititcal error: size is less than 0");
  304. g_assert (0);
  305. }
  306. slen = (size_t)d;
  307. fmt++;
  308. continue;
  309. default:
  310. break;
  311. }
  312. break;
  313. }
  314. switch (*fmt) {
  315. case 'V':
  316. v = va_arg (args, f_str_t *);
  317. len = v->len;
  318. len = (buf + len < last) ? len : (size_t) (last - buf);
  319. buf = ((gchar *)memcpy (buf, v->begin, len)) + len;
  320. fmt++;
  321. continue;
  322. case 'v':
  323. gs = va_arg (args, GString *);
  324. len = gs->len;
  325. len = (buf + len < last) ? len : (size_t) (last - buf);
  326. buf = ((gchar *)memcpy (buf, gs->str, len)) + len;
  327. fmt++;
  328. break;
  329. case 's':
  330. p = va_arg(args, gchar *);
  331. if (p == NULL) {
  332. p = "(NULL)";
  333. }
  334. if (slen == (size_t) -1) {
  335. while (*p && buf < last) {
  336. *buf++ = *p++;
  337. }
  338. } else {
  339. len = (buf + slen < last) ? slen : (size_t) (last - buf);
  340. buf = ((gchar *)memcpy (buf, p, len)) + len;
  341. }
  342. fmt++;
  343. continue;
  344. case 'S':
  345. p = va_arg(args, gchar *);
  346. if (p == NULL) {
  347. p = "(NULL)";
  348. }
  349. if (slen == (size_t) -1) {
  350. buf = rspamd_escape_string (buf, p, last - buf);
  351. } else {
  352. len = (buf + slen < last) ? slen : (size_t) (last - buf);
  353. buf = rspamd_escape_string (buf, p, len);
  354. }
  355. fmt++;
  356. continue;
  357. case 'O':
  358. i64 = (gint64) va_arg (args, off_t);
  359. sign = 1;
  360. break;
  361. case 'P':
  362. i64 = (gint64) va_arg (args, pid_t);
  363. sign = 1;
  364. break;
  365. case 'T':
  366. i64 = (gint64) va_arg (args, time_t);
  367. sign = 1;
  368. break;
  369. case 'z':
  370. if (sign) {
  371. i64 = (gint64) va_arg (args, ssize_t);
  372. } else {
  373. ui64 = (guint64) va_arg (args, size_t);
  374. }
  375. break;
  376. case 'd':
  377. if (sign) {
  378. i64 = (gint64) va_arg (args, gint);
  379. } else {
  380. ui64 = (guint64) va_arg (args, guint);
  381. }
  382. break;
  383. case 'l':
  384. if (sign) {
  385. i64 = (gint64) va_arg(args, glong);
  386. } else {
  387. ui64 = (guint64) va_arg(args, gulong);
  388. }
  389. break;
  390. case 'D':
  391. if (sign) {
  392. i64 = (gint64) va_arg(args, gint32);
  393. } else {
  394. ui64 = (guint64) va_arg(args, guint32);
  395. }
  396. break;
  397. case 'L':
  398. if (sign) {
  399. i64 = va_arg (args, gint64);
  400. } else {
  401. ui64 = va_arg (args, guint64);
  402. }
  403. break;
  404. case 'f':
  405. f = (double) va_arg (args, double);
  406. if (f < 0) {
  407. *buf++ = '-';
  408. f = -f;
  409. }
  410. ui64 = (gint64) f;
  411. buf = rspamd_sprintf_num (buf, last, ui64, zero, 0, width);
  412. if (frac_width) {
  413. if (buf < last) {
  414. *buf++ = '.';
  415. }
  416. scale = 1.0;
  417. for (i = 0; i < frac_width; i++) {
  418. scale *= 10.0;
  419. }
  420. /*
  421. * (gint64) cast is required for msvc6:
  422. * it can not convert guint64 to double
  423. */
  424. ui64 = (guint64) ((f - (gint64) ui64) * scale);
  425. buf = rspamd_sprintf_num (buf, last, ui64, '0', 0, frac_width);
  426. }
  427. fmt++;
  428. continue;
  429. case 'F':
  430. f = (long double) va_arg (args, long double);
  431. if (f < 0) {
  432. *buf++ = '-';
  433. f = -f;
  434. }
  435. ui64 = (gint64) f;
  436. buf = rspamd_sprintf_num (buf, last, ui64, zero, 0, width);
  437. if (frac_width) {
  438. if (buf < last) {
  439. *buf++ = '.';
  440. }
  441. scale = 1.0;
  442. for (i = 0; i < frac_width; i++) {
  443. scale *= 10.0;
  444. }
  445. /*
  446. * (gint64) cast is required for msvc6:
  447. * it can not convert guint64 to double
  448. */
  449. ui64 = (guint64) ((f - (gint64) ui64) * scale);
  450. buf = rspamd_sprintf_num (buf, last, ui64, '0', 0, frac_width);
  451. }
  452. fmt++;
  453. continue;
  454. case 'g':
  455. f = (long double) va_arg (args, double);
  456. if (f < 0) {
  457. *buf++ = '-';
  458. f = -f;
  459. }
  460. g_ascii_formatd (buf, last - buf, "%g", (double)f);
  461. buf += strlen (buf);
  462. fmt++;
  463. continue;
  464. case 'G':
  465. f = (long double) va_arg (args, long double);
  466. if (f < 0) {
  467. *buf++ = '-';
  468. f = -f;
  469. }
  470. g_ascii_formatd (buf, last - buf, "%g", (double)f);
  471. buf += strlen (buf);
  472. fmt++;
  473. continue;
  474. case 'p':
  475. ui64 = (uintptr_t) va_arg (args, void *);
  476. hex = 2;
  477. sign = 0;
  478. zero = '0';
  479. width = sizeof (void *) * 2;
  480. break;
  481. case 'c':
  482. d = va_arg (args, gint);
  483. *buf++ = (gchar) (d & 0xff);
  484. fmt++;
  485. continue;
  486. case 'Z':
  487. *buf++ = '\0';
  488. fmt++;
  489. continue;
  490. case 'N':
  491. *buf++ = LF;
  492. fmt++;
  493. continue;
  494. case '%':
  495. *buf++ = '%';
  496. fmt++;
  497. continue;
  498. default:
  499. *buf++ = *fmt++;
  500. continue;
  501. }
  502. if (sign) {
  503. if (i64 < 0) {
  504. *buf++ = '-';
  505. ui64 = (guint64) -i64;
  506. } else {
  507. ui64 = (guint64) i64;
  508. }
  509. }
  510. if (!humanize) {
  511. buf = rspamd_sprintf_num (buf, last, ui64, zero, hex, width);
  512. }
  513. else {
  514. buf = rspamd_humanize_number (buf, last, ui64, bytes);
  515. }
  516. fmt++;
  517. } else {
  518. *buf++ = *fmt++;
  519. }
  520. }
  521. return buf;
  522. }