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