123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675 |
- /* Copyright (c) 2010, Vsevolod Stakhov
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
- #include "printf.h"
- #include "fstring.h"
- #include "main.h"
-
- /**
- * From FreeBSD libutil code
- */
- static const int maxscale = 7;
-
- static gchar *
- rspamd_humanize_number (gchar *buf, gchar *last, gint64 num, gboolean bytes)
- {
- const gchar *prefixes;
- int i, r, remainder, sign;
- gint64 divisor;
- gsize baselen, len = last - buf;
-
- remainder = 0;
-
- baselen = 1;
- if (!bytes) {
- divisor = 1000;
- prefixes = "\0\0\0k\0\0M\0\0G\0\0T\0\0P\0\0E";
- }
- else {
- divisor = 1024;
- prefixes = "B\0\0k\0\0M\0\0G\0\0T\0\0P\0\0E";
- }
-
-
- #define SCALE2PREFIX(scale) (&prefixes[(scale) * 3])
-
- if (num < 0) {
- sign = -1;
- num = -num;
- baselen += 2; /* sign, digit */
- }
- else {
- sign = 1;
- baselen += 1; /* digit */
- }
-
- /* Check if enough room for `x y' + suffix + `\0' */
- if (len < baselen + 1) {
- return buf;
- }
-
- /*
- * Divide the number until it fits the given column.
- * If there will be an overflow by the rounding below,
- * divide once more.
- */
- for (i = 0; i < maxscale && num > divisor; i++) {
- remainder = num % divisor;
- num /= divisor;
- }
-
- r = rspamd_snprintf (buf, len, "%L%s",
- sign * (num + (remainder + 50) / 1000),
- SCALE2PREFIX (i));
-
- #undef SCALE2PREFIX
-
- return buf + r;
- }
-
-
- static gchar *
- rspamd_sprintf_num (gchar *buf, gchar *last, guint64 ui64, gchar zero,
- guint hexadecimal, guint width)
- {
- gchar *p, temp[sizeof ("18446744073709551615")];
- size_t len;
- guint32 ui32;
- static gchar hex[] = "0123456789abcdef";
- static gchar HEX[] = "0123456789ABCDEF";
-
- p = temp + sizeof(temp);
-
- if (hexadecimal == 0) {
-
- if (ui64 <= G_MAXUINT32) {
-
- /*
- * To divide 64-bit numbers and to find remainders
- * on the x86 platform gcc and icc call the libc functions
- * [u]divdi3() and [u]moddi3(), they call another function
- * in its turn. On FreeBSD it is the qdivrem() function,
- * its source code is about 170 lines of the code.
- * The glibc counterpart is about 150 lines of the code.
- *
- * For 32-bit numbers and some divisors gcc and icc use
- * a inlined multiplication and shifts. For example,
- * guint "i32 / 10" is compiled to
- *
- * (i32 * 0xCCCCCCCD) >> 35
- */
-
- ui32 = (guint32) ui64;
-
- do {
- *--p = (gchar) (ui32 % 10 + '0');
- } while (ui32 /= 10);
-
- } else {
- do {
- *--p = (gchar) (ui64 % 10 + '0');
- } while (ui64 /= 10);
- }
-
- } else if (hexadecimal == 1) {
-
- do {
-
- /* the "(guint32)" cast disables the BCC's warning */
- *--p = hex[(guint32) (ui64 & 0xf)];
-
- } while (ui64 >>= 4);
-
- } else { /* hexadecimal == 2 */
-
- do {
-
- /* the "(guint32)" cast disables the BCC's warning */
- *--p = HEX[(guint32) (ui64 & 0xf)];
-
- } while (ui64 >>= 4);
- }
-
- /* zero or space padding */
-
- len = (temp + sizeof (temp)) - p;
-
- while (len++ < width && buf < last) {
- *buf++ = zero;
- }
-
- /* number safe copy */
-
- len = (temp + sizeof (temp)) - p;
-
- if (buf + len > last) {
- len = last - buf;
- }
-
- return ((gchar *)memcpy (buf, p, len)) + len;
- }
-
- gint
- rspamd_fprintf (FILE *f, const gchar *fmt, ...)
- {
- va_list args;
- gchar buf[BUFSIZ];
- gint r;
-
- va_start (args, fmt);
- rspamd_vsnprintf (buf, sizeof (buf), fmt, args);
- va_end (args);
-
- r = fprintf (f, "%s", buf);
-
- return r;
- }
-
- gint
- rspamd_log_fprintf (FILE *f, const gchar *fmt, ...)
- {
- va_list args;
- gchar buf[BUFSIZ];
- gint r;
-
- va_start (args, fmt);
- rspamd_vsnprintf (buf, sizeof (buf), fmt, args);
- va_end (args);
-
- r = fprintf (f, "%s\n", buf);
- fflush (f);
-
- return r;
- }
-
- gint
- rspamd_sprintf (gchar *buf, const gchar *fmt, ...)
- {
- gchar *p;
- va_list args;
-
- va_start (args, fmt);
- p = rspamd_vsnprintf (buf, /* STUB */ 65536, fmt, args);
- va_end (args);
-
- return p - buf;
- }
-
-
- gint
- rspamd_snprintf (gchar *buf, glong max, const gchar *fmt, ...)
- {
- gchar *p;
- va_list args;
-
- va_start (args, fmt);
- p = rspamd_vsnprintf (buf, max - 1, fmt, args);
- va_end (args);
- *p = '\0';
-
- return p - buf;
- }
-
- gchar *
- rspamd_escape_string (gchar *dst, const gchar *src, glong len)
- {
- gchar *buf = dst, *last = dst + len;
- guint8 c;
- const gchar *p = src;
- gunichar uc;
-
- if (len <= 0) {
- return dst;
- }
-
- while (*p && buf < last) {
- /* Detect utf8 */
- uc = g_utf8_get_char_validated (p, last - buf);
- if (uc > 0) {
- c = g_unichar_to_utf8 (uc, buf);
- buf += c;
- p += c;
- }
- else {
- c = *p ++;
- if (G_UNLIKELY ((c & 0x80))) {
- c &= 0x7F;
- if (last - buf >= 3) {
- *buf++ = 'M';
- *buf++ = '-';
- }
- }
- if (G_UNLIKELY ( g_ascii_iscntrl (c))) {
- if (c == '\n') {
- *buf++ = ' ';
- }
- else if (c == '\t') {
- *buf++ = '\t';
- }
- else {
- *buf++ = '^';
- if (buf != last) {
- *buf++ = c ^ 0100;
- }
- }
- }
- else {
- *buf++ = c;
- }
- }
- }
-
- *buf = '\0';
-
- return buf;
- }
-
- gchar *
- rspamd_vsnprintf (gchar *buf, glong max, const gchar *fmt, va_list args)
- {
- gchar *p, zero, *last;
- gint d;
- long double f, scale;
- size_t len, slen;
- gint64 i64;
- guint64 ui64;
- guint width, sign, hex, humanize, bytes, frac_width, i;
- f_str_t *v;
- GString *gs;
-
- if (max <= 0) {
- return buf;
- }
-
- last = buf + max;
-
- while (*fmt && buf < last) {
-
- /*
- * "buf < last" means that we could copy at least one character:
- * the plain character, "%%", "%c", and minus without the checking
- */
-
- if (*fmt == '%') {
-
- i64 = 0;
- ui64 = 0;
-
- zero = (gchar) ((*++fmt == '0') ? '0' : ' ');
- width = 0;
- sign = 1;
- hex = 0;
- bytes = 0;
- humanize = 0;
- frac_width = 0;
- slen = (size_t) -1;
-
- while (*fmt >= '0' && *fmt <= '9') {
- width = width * 10 + *fmt++ - '0';
- }
-
-
- for ( ;; ) {
- switch (*fmt) {
-
- case 'u':
- sign = 0;
- fmt++;
- continue;
-
- case 'm':
- fmt++;
- continue;
-
- case 'X':
- hex = 2;
- sign = 0;
- fmt++;
- continue;
-
- case 'x':
- hex = 1;
- sign = 0;
- fmt++;
- continue;
- case 'H':
- humanize = 1;
- bytes = 1;
- sign = 0;
- fmt ++;
- continue;
- case 'h':
- humanize = 1;
- sign = 0;
- fmt ++;
- continue;
- case '.':
- fmt++;
-
- while (*fmt >= '0' && *fmt <= '9') {
- frac_width = frac_width * 10 + *fmt++ - '0';
- }
-
- break;
-
- case '*':
- d = (gint)va_arg (args, gint);
- if (G_UNLIKELY (d < 0)) {
- msg_err ("crititcal error: size is less than 0");
- g_assert (0);
- }
- slen = (size_t)d;
- fmt++;
- continue;
-
- default:
- break;
- }
-
- break;
- }
-
-
- switch (*fmt) {
-
- case 'V':
- v = va_arg (args, f_str_t *);
-
- len = v->len;
- len = (buf + len < last) ? len : (size_t) (last - buf);
-
- buf = ((gchar *)memcpy (buf, v->begin, len)) + len;
- fmt++;
-
- continue;
-
- case 'v':
- gs = va_arg (args, GString *);
- len = gs->len;
- len = (buf + len < last) ? len : (size_t) (last - buf);
-
- buf = ((gchar *)memcpy (buf, gs->str, len)) + len;
- fmt++;
- break;
-
- case 's':
- p = va_arg(args, gchar *);
- if (p == NULL) {
- p = "(NULL)";
- }
-
- if (slen == (size_t) -1) {
- while (*p && buf < last) {
- *buf++ = *p++;
- }
-
- } else {
- len = (buf + slen < last) ? slen : (size_t) (last - buf);
-
- buf = ((gchar *)memcpy (buf, p, len)) + len;
- }
-
- fmt++;
-
- continue;
-
- case 'S':
- p = va_arg(args, gchar *);
- if (p == NULL) {
- p = "(NULL)";
- }
-
- if (slen == (size_t) -1) {
- buf = rspamd_escape_string (buf, p, last - buf);
-
- } else {
- len = (buf + slen < last) ? slen : (size_t) (last - buf);
-
- buf = rspamd_escape_string (buf, p, len);
- }
-
- fmt++;
-
- continue;
-
- case 'O':
- i64 = (gint64) va_arg (args, off_t);
- sign = 1;
- break;
-
- case 'P':
- i64 = (gint64) va_arg (args, pid_t);
- sign = 1;
- break;
-
- case 'T':
- i64 = (gint64) va_arg (args, time_t);
- sign = 1;
- break;
-
- case 'z':
- if (sign) {
- i64 = (gint64) va_arg (args, ssize_t);
- } else {
- ui64 = (guint64) va_arg (args, size_t);
- }
- break;
-
- case 'd':
- if (sign) {
- i64 = (gint64) va_arg (args, gint);
- } else {
- ui64 = (guint64) va_arg (args, guint);
- }
- break;
-
- case 'l':
- if (sign) {
- i64 = (gint64) va_arg(args, glong);
- } else {
- ui64 = (guint64) va_arg(args, gulong);
- }
- break;
-
- case 'D':
- if (sign) {
- i64 = (gint64) va_arg(args, gint32);
- } else {
- ui64 = (guint64) va_arg(args, guint32);
- }
- break;
-
- case 'L':
- if (sign) {
- i64 = va_arg (args, gint64);
- } else {
- ui64 = va_arg (args, guint64);
- }
- break;
-
-
- case 'f':
- f = (double) va_arg (args, double);
- if (f < 0) {
- *buf++ = '-';
- f = -f;
- }
-
- ui64 = (gint64) f;
-
- buf = rspamd_sprintf_num (buf, last, ui64, zero, 0, width);
-
- if (frac_width) {
-
- if (buf < last) {
- *buf++ = '.';
- }
-
- scale = 1.0;
-
- for (i = 0; i < frac_width; i++) {
- scale *= 10.0;
- }
-
- /*
- * (gint64) cast is required for msvc6:
- * it can not convert guint64 to double
- */
- ui64 = (guint64) ((f - (gint64) ui64) * scale);
-
- buf = rspamd_sprintf_num (buf, last, ui64, '0', 0, frac_width);
- }
-
- fmt++;
-
- continue;
-
- case 'F':
- f = (long double) va_arg (args, long double);
-
- if (f < 0) {
- *buf++ = '-';
- f = -f;
- }
-
- ui64 = (gint64) f;
-
- buf = rspamd_sprintf_num (buf, last, ui64, zero, 0, width);
-
- if (frac_width) {
-
- if (buf < last) {
- *buf++ = '.';
- }
-
- scale = 1.0;
-
- for (i = 0; i < frac_width; i++) {
- scale *= 10.0;
- }
-
- /*
- * (gint64) cast is required for msvc6:
- * it can not convert guint64 to double
- */
- ui64 = (guint64) ((f - (gint64) ui64) * scale);
-
- buf = rspamd_sprintf_num (buf, last, ui64, '0', 0, frac_width);
- }
-
- fmt++;
-
- continue;
-
- case 'g':
- f = (long double) va_arg (args, double);
-
- if (f < 0) {
- *buf++ = '-';
- f = -f;
- }
- g_ascii_formatd (buf, last - buf, "%g", (double)f);
- buf += strlen (buf);
- fmt++;
-
- continue;
-
- case 'G':
- f = (long double) va_arg (args, long double);
-
- if (f < 0) {
- *buf++ = '-';
- f = -f;
- }
- g_ascii_formatd (buf, last - buf, "%g", (double)f);
- buf += strlen (buf);
- fmt++;
-
- continue;
-
- case 'p':
- ui64 = (uintptr_t) va_arg (args, void *);
- hex = 2;
- sign = 0;
- zero = '0';
- width = sizeof (void *) * 2;
- break;
-
- case 'c':
- d = va_arg (args, gint);
- *buf++ = (gchar) (d & 0xff);
- fmt++;
-
- continue;
-
- case 'Z':
- *buf++ = '\0';
- fmt++;
-
- continue;
-
- case 'N':
- *buf++ = LF;
- fmt++;
-
- continue;
-
- case '%':
- *buf++ = '%';
- fmt++;
-
- continue;
-
- default:
- *buf++ = *fmt++;
-
- continue;
- }
-
- if (sign) {
- if (i64 < 0) {
- *buf++ = '-';
- ui64 = (guint64) -i64;
-
- } else {
- ui64 = (guint64) i64;
- }
- }
-
- if (!humanize) {
- buf = rspamd_sprintf_num (buf, last, ui64, zero, hex, width);
- }
- else {
- buf = rspamd_humanize_number (buf, last, ui64, bytes);
- }
-
- fmt++;
-
- } else {
- *buf++ = *fmt++;
- }
- }
-
- return buf;
- }
|