+++ /dev/null
-/*
- * Taken from Linux kernel's linux/lib/vsprintf.c
- * and somewhat simplified.
- *
- * Copyright (C) 1991, 1992 Linus Torvalds
- */
-/* vsprintf.c -- Lars Wirzenius & Linus Torvalds. */
-/*
- * Wirzenius wrote this portably, Torvalds fucked it up :-)
- */
-
-#include "defs.h"
-
-#if USE_CUSTOM_PRINTF
-
-#include <stdarg.h>
-#include <limits.h>
-
-#ifndef HAVE_FPUTS_UNLOCKED
-# define fputs_unlocked fputs
-#endif
-
-#define noinline_for_stack /*nothing*/
-#define likely(expr) (expr)
-#define unlikely(expr) (expr)
-
-#define do_div(n, d) ({ __typeof(num) t = n % d; n /= d; t; })
-
-#undef isdigit
-#define isdigit(a) ((unsigned char)((a) - '0') <= 9)
-
-static inline
-int skip_atoi(const char **s)
-{
- int i = 0;
- const char *p = *s;
-
- while (isdigit(*p))
- i = i*10 + *p++ - '0';
-
- *s = p;
- return i;
-}
-
-/* Decimal conversion is by far the most typical, and is used
- * for /proc and /sys data. This directly impacts e.g. top performance
- * with many processes running. We optimize it for speed
- * using ideas described at <http://www.cs.uiowa.edu/~jones/bcd/divide.html>
- * (with permission from the author, Douglas W. Jones).
- */
-
-#if LONG_MAX != 0x7fffffffUL || LLONG_MAX != 0x7fffffffffffffffULL
-/* Formats correctly any integer in [0, 999999999] */
-static noinline_for_stack
-char *put_dec_full9(char *buf, unsigned q)
-{
- unsigned r;
-
- /* Possible ways to approx. divide by 10
- * (x * 0x1999999a) >> 32 x < 1073741829 (multiply must be 64-bit)
- * (x * 0xcccd) >> 19 x < 81920 (x < 262149 when 64-bit mul)
- * (x * 0x6667) >> 18 x < 43699
- * (x * 0x3334) >> 17 x < 16389
- * (x * 0x199a) >> 16 x < 16389
- * (x * 0x0ccd) >> 15 x < 16389
- * (x * 0x0667) >> 14 x < 2739
- * (x * 0x0334) >> 13 x < 1029
- * (x * 0x019a) >> 12 x < 1029
- * (x * 0x00cd) >> 11 x < 1029 shorter code than * 0x67 (on i386)
- * (x * 0x0067) >> 10 x < 179
- * (x * 0x0034) >> 9 x < 69 same
- * (x * 0x001a) >> 8 x < 69 same
- * (x * 0x000d) >> 7 x < 69 same, shortest code (on i386)
- * (x * 0x0007) >> 6 x < 19
- * See <http://www.cs.uiowa.edu/~jones/bcd/divide.html>
- */
- r = (q * (uint64_t)0x1999999a) >> 32;
- *buf++ = (q - 10 * r) + '0'; /* 1 */
- q = (r * (uint64_t)0x1999999a) >> 32;
- *buf++ = (r - 10 * q) + '0'; /* 2 */
- r = (q * (uint64_t)0x1999999a) >> 32;
- *buf++ = (q - 10 * r) + '0'; /* 3 */
- q = (r * (uint64_t)0x1999999a) >> 32;
- *buf++ = (r - 10 * q) + '0'; /* 4 */
- r = (q * (uint64_t)0x1999999a) >> 32;
- *buf++ = (q - 10 * r) + '0'; /* 5 */
- /* Now value is under 10000, can avoid 64-bit multiply */
- q = (r * 0x199a) >> 16;
- *buf++ = (r - 10 * q) + '0'; /* 6 */
- r = (q * 0xcd) >> 11;
- *buf++ = (q - 10 * r) + '0'; /* 7 */
- q = (r * 0xcd) >> 11;
- *buf++ = (r - 10 * q) + '0'; /* 8 */
- *buf++ = q + '0'; /* 9 */
- return buf;
-}
-#endif
-
-/* Similar to above but do not pad with zeros.
- * Code can be easily arranged to print 9 digits too, but our callers
- * always call put_dec_full9() instead when the number has 9 decimal digits.
- */
-static noinline_for_stack
-char *put_dec_trunc8(char *buf, unsigned r)
-{
- unsigned q;
-
- /* Copy of previous function's body with added early returns */
- q = (r * (uint64_t)0x1999999a) >> 32;
- *buf++ = (r - 10 * q) + '0'; /* 2 */
- if (q == 0) return buf;
- r = (q * (uint64_t)0x1999999a) >> 32;
- *buf++ = (q - 10 * r) + '0'; /* 3 */
- if (r == 0) return buf;
- q = (r * (uint64_t)0x1999999a) >> 32;
- *buf++ = (r - 10 * q) + '0'; /* 4 */
- if (q == 0) return buf;
- r = (q * (uint64_t)0x1999999a) >> 32;
- *buf++ = (q - 10 * r) + '0'; /* 5 */
- if (r == 0) return buf;
- q = (r * 0x199a) >> 16;
- *buf++ = (r - 10 * q) + '0'; /* 6 */
- if (q == 0) return buf;
- r = (q * 0xcd) >> 11;
- *buf++ = (q - 10 * r) + '0'; /* 7 */
- if (r == 0) return buf;
- q = (r * 0xcd) >> 11;
- *buf++ = (r - 10 * q) + '0'; /* 8 */
- if (q == 0) return buf;
- *buf++ = q + '0'; /* 9 */
- return buf;
-}
-
-/* There are two algorithms to print larger numbers.
- * One is generic: divide by 1000000000 and repeatedly print
- * groups of (up to) 9 digits. It's conceptually simple,
- * but requires a (unsigned long long) / 1000000000 division.
- *
- * Second algorithm splits 64-bit unsigned long long into 16-bit chunks,
- * manipulates them cleverly and generates groups of 4 decimal digits.
- * It so happens that it does NOT require long long division.
- *
- * If long is > 32 bits, division of 64-bit values is relatively easy,
- * and we will use the first algorithm.
- * If long long is > 64 bits (strange architecture with VERY large long long),
- * second algorithm can't be used, and we again use the first one.
- *
- * Else (if long is 32 bits and long long is 64 bits) we use second one.
- */
-
-#if LONG_MAX != 0x7fffffffUL || LLONG_MAX != 0x7fffffffffffffffULL
-
-/* First algorithm: generic */
-
-static
-char *put_dec(char *buf, unsigned long long n)
-{
- if (n >= 100*1000*1000) {
- while (n >= 1000*1000*1000)
- buf = put_dec_full9(buf, do_div(n, 1000*1000*1000));
- if (n >= 100*1000*1000)
- return put_dec_full9(buf, n);
- }
- return put_dec_trunc8(buf, n);
-}
-
-#else
-
-/* Second algorithm: valid only for 64-bit long longs */
-
-static noinline_for_stack
-char *put_dec_full4(char *buf, unsigned q)
-{
- unsigned r;
- r = (q * 0xcccd) >> 19;
- *buf++ = (q - 10 * r) + '0';
- q = (r * 0x199a) >> 16;
- *buf++ = (r - 10 * q) + '0';
- r = (q * 0xcd) >> 11;
- *buf++ = (q - 10 * r) + '0';
- *buf++ = r + '0';
- return buf;
-}
-
-/* Based on code by Douglas W. Jones found at
- * <http://www.cs.uiowa.edu/~jones/bcd/decimal.html#sixtyfour>
- * (with permission from the author).
- * Performs no 64-bit division and hence should be fast on 32-bit machines.
- */
-static
-char *put_dec(char *buf, unsigned long long n)
-{
- uint32_t d3, d2, d1, q, h;
-
- if (n < 100*1000*1000)
- return put_dec_trunc8(buf, n);
-
- d1 = ((uint32_t)n >> 16); /* implicit "& 0xffff" */
- h = (n >> 32);
- d2 = (h ) & 0xffff;
- d3 = (h >> 16); /* implicit "& 0xffff" */
-
- q = 656 * d3 + 7296 * d2 + 5536 * d1 + ((uint32_t)n & 0xffff);
-
- buf = put_dec_full4(buf, q % 10000);
- q = q / 10000;
-
- d1 = q + 7671 * d3 + 9496 * d2 + 6 * d1;
- buf = put_dec_full4(buf, d1 % 10000);
- q = d1 / 10000;
-
- d2 = q + 4749 * d3 + 42 * d2;
- buf = put_dec_full4(buf, d2 % 10000);
- q = d2 / 10000;
-
- d3 = q + 281 * d3;
- if (!d3)
- goto done;
- buf = put_dec_full4(buf, d3 % 10000);
- q = d3 / 10000;
- if (!q)
- goto done;
- buf = put_dec_full4(buf, q);
- done:
- while (buf[-1] == '0')
- --buf;
-
- return buf;
-}
-
-#endif
-
-/*
- * For strace, the following formats are not supported:
- * %h[h]u, %zu, %tu - use [unsigned] int/long/long long fmt instead
- * %8.4u - no precision field for integers allowed (ok for strings)
- * %+d, % d - no forced sign or force "space positive" sign
- * %-07u - use %-7u instead
- * %X - works as %x
- */
-
-#define ZEROPAD 1 /* pad with zero */
-#define SIGN 2 /* unsigned/signed long */
-//#define PLUS 4 /* show plus */
-//#define SPACE 8 /* space if plus */
-#define LEFT 16 /* left justified */
-//#deefine SMALL 32 /* use lowercase in hex (must be 32 == 0x20) */
-#define SPECIAL 64 /* prefix hex with "0x", octal with "0" */
-
-enum format_type {
- FORMAT_TYPE_NONE, /* Just a string part */
- FORMAT_TYPE_WIDTH,
- FORMAT_TYPE_PRECISION,
- FORMAT_TYPE_CHAR,
- FORMAT_TYPE_STR,
- FORMAT_TYPE_PTR,
- FORMAT_TYPE_PERCENT_CHAR,
- FORMAT_TYPE_INVALID,
- FORMAT_TYPE_LONG_LONG,
- FORMAT_TYPE_ULONG,
- FORMAT_TYPE_LONG,
- FORMAT_TYPE_UINT,
- FORMAT_TYPE_INT,
-};
-
-struct printf_spec {
- uint8_t type; /* format_type enum */
- uint8_t flags; /* flags to number() */
- uint8_t base; /* number base, 8, 10 or 16 only */
- uint8_t qualifier; /* number qualifier, one of 'hHlLtzZ' */
- int field_width; /* width of output field */
- int precision; /* # of digits/chars */
-};
-
-static noinline_for_stack
-char *number(char *buf, char *end, unsigned long long num,
- struct printf_spec spec)
-{
- /* we are called with base 8, 10 or 16, only, thus don't need "G..." */
- static const char digits[16] = "0123456789abcdef"; /* "GHIJKLMNOPQRSTUVWXYZ"; */
-
- char tmp[sizeof(long long)*3 + 4];
- char sign;
- int need_pfx = ((spec.flags & SPECIAL) && spec.base != 10);
- int i;
-
- /* We may overflow the buf. Crudely check for it */
- i = sizeof(long long)*3 + 4;
- if (i < spec.field_width)
- i = spec.field_width;
- if ((end - buf) <= i)
- return buf + i;
-
-//we don't use formats like "%-07u"
-// if (spec.flags & LEFT)
-// spec.flags &= ~ZEROPAD;
- sign = 0;
- if (spec.flags & SIGN) {
- if ((signed long long)num < 0) {
- sign = '-';
- num = -(signed long long)num;
- spec.field_width--;
-// } else if (spec.flags & PLUS) {
-// sign = '+';
-// spec.field_width--;
-// } else if (spec.flags & SPACE) {
-// sign = ' ';
-// spec.field_width--;
- }
- }
- if (need_pfx) {
- spec.field_width--;
- if (spec.base == 16)
- spec.field_width--;
- }
-
- /* generate full string in tmp[], in reverse order */
- i = 0;
- if (num < spec.base)
- tmp[i++] = digits[num];
- /* Generic code, for any base:
- else do {
- tmp[i++] = (digits[do_div(num,base)]);
- } while (num != 0);
- */
- else if (spec.base != 10) { /* 8 or 16 */
- int mask = spec.base - 1;
- int shift = 3;
-
- if (spec.base == 16)
- shift = 4;
- do {
- tmp[i++] = digits[((unsigned char)num) & mask];
- num >>= shift;
- } while (num);
- } else { /* base 10 */
- i = put_dec(tmp, num) - tmp;
- }
-
-//spec.precision is assumed 0 ("not specified")
-// /* printing 100 using %2d gives "100", not "00" */
-// if (i > spec.precision)
-// spec.precision = i;
-// /* leading space padding */
-// spec.field_width -= spec.precision;
- spec.field_width -= i;
- if (!(spec.flags & (ZEROPAD+LEFT))) {
- while (--spec.field_width >= 0) {
- ///if (buf < end)
- *buf = ' ';
- ++buf;
- }
- }
- /* sign */
- if (sign) {
- ///if (buf < end)
- *buf = sign;
- ++buf;
- }
- /* "0x" / "0" prefix */
- if (need_pfx) {
- ///if (buf < end)
- *buf = '0';
- ++buf;
- if (spec.base == 16) {
- ///if (buf < end)
- *buf = 'x';
- ++buf;
- }
- }
- /* zero or space padding */
- if (!(spec.flags & LEFT)) {
- char c = (spec.flags & ZEROPAD) ? '0' : ' ';
- while (--spec.field_width >= 0) {
- ///if (buf < end)
- *buf = c;
- ++buf;
- }
- }
-// /* hmm even more zero padding? */
-// while (i <= --spec.precision) {
-// ///if (buf < end)
-// *buf = '0';
-// ++buf;
-// }
- /* actual digits of result */
- while (--i >= 0) {
- ///if (buf < end)
- *buf = tmp[i];
- ++buf;
- }
- /* trailing space padding */
- while (--spec.field_width >= 0) {
- ///if (buf < end)
- *buf = ' ';
- ++buf;
- }
-
- return buf;
-}
-
-static noinline_for_stack
-char *string(char *buf, char *end, const char *s, struct printf_spec spec)
-{
- int len, i;
-
- if (!s)
- s = "(null)";
-
- len = strnlen(s, spec.precision);
-
- /* We may overflow the buf. Crudely check for it */
- i = len;
- if (i < spec.field_width)
- i = spec.field_width;
- if ((end - buf) <= i)
- return buf + i;
-
- if (!(spec.flags & LEFT)) {
- while (len < spec.field_width--) {
- ///if (buf < end)
- *buf = ' ';
- ++buf;
- }
- }
- for (i = 0; i < len; ++i) {
- ///if (buf < end)
- *buf = *s;
- ++buf; ++s;
- }
- while (len < spec.field_width--) {
- ///if (buf < end)
- *buf = ' ';
- ++buf;
- }
-
- return buf;
-}
-
-static noinline_for_stack
-char *pointer(const char *fmt, char *buf, char *end, void *ptr,
- struct printf_spec spec)
-{
-// spec.flags |= SMALL;
- if (spec.field_width == -1) {
- spec.field_width = 2 * sizeof(void *);
- spec.flags |= ZEROPAD;
- }
- spec.base = 16;
-
- return number(buf, end, (unsigned long) ptr, spec);
-}
-
-/*
- * Helper function to decode printf style format.
- * Each call decode a token from the format and return the
- * number of characters read (or likely the delta where it wants
- * to go on the next call).
- * The decoded token is returned through the parameters
- *
- * 'h', 'l', or 'L' for integer fields
- * 'z' support added 23/7/1999 S.H.
- * 'z' changed to 'Z' --davidm 1/25/99
- * 't' added for ptrdiff_t
- *
- * @fmt: the format string
- * @type of the token returned
- * @flags: various flags such as +, -, # tokens..
- * @field_width: overwritten width
- * @base: base of the number (octal, hex, ...)
- * @precision: precision of a number
- * @qualifier: qualifier of a number (long, size_t, ...)
- */
-static noinline_for_stack
-int format_decode(const char *fmt, struct printf_spec *spec)
-{
- const char *start = fmt;
-
- /* we finished early by reading the field width */
- if (spec->type == FORMAT_TYPE_WIDTH) {
- if (spec->field_width < 0) {
- spec->field_width = -spec->field_width;
- spec->flags |= LEFT;
- }
- spec->type = FORMAT_TYPE_NONE;
- goto precision;
- }
-
- /* we finished early by reading the precision */
- if (spec->type == FORMAT_TYPE_PRECISION) {
- if (spec->precision < 0)
- spec->precision = 0;
-
- spec->type = FORMAT_TYPE_NONE;
- goto qualifier;
- }
-
- /* By default */
- spec->type = FORMAT_TYPE_NONE;
-
- for (;;) {
- if (*fmt == '\0')
- return fmt - start;
- if (*fmt == '%')
- break;
- ++fmt;
- }
-
- /* Return the current non-format string */
- if (fmt != start)
- return fmt - start;
-
- /* Process flags */
- spec->flags = 0;
-
- while (1) { /* this also skips first '%' */
- bool found = true;
-
- ++fmt;
-
- switch (*fmt) {
- case '-': spec->flags |= LEFT; break;
-// case '+': spec->flags |= PLUS; break;
-// case ' ': spec->flags |= SPACE; break;
- case '#': spec->flags |= SPECIAL; break;
- case '0': spec->flags |= ZEROPAD; break;
- default: found = false;
- }
-
- if (!found)
- break;
- }
-
- /* get field width */
- spec->field_width = -1;
-
- if (isdigit(*fmt))
- spec->field_width = skip_atoi(&fmt);
- else if (*fmt == '*') {
- /* it's the next argument */
- spec->type = FORMAT_TYPE_WIDTH;
- return ++fmt - start;
- }
-
-precision:
- /* get the precision */
- spec->precision = -1;
- if (*fmt == '.') {
- ++fmt;
- if (isdigit(*fmt)) {
- spec->precision = skip_atoi(&fmt);
-// if (spec->precision < 0)
-// spec->precision = 0;
- } else if (*fmt == '*') {
- /* it's the next argument */
- spec->type = FORMAT_TYPE_PRECISION;
- return ++fmt - start;
- }
- }
-
-qualifier:
- /* get the conversion qualifier */
- spec->qualifier = -1;
- if (*fmt == 'l') {
- spec->qualifier = *fmt++;
- if (unlikely(spec->qualifier == *fmt)) {
- spec->qualifier = 'L';
- ++fmt;
- }
- }
-
- /* default base */
- spec->base = 10;
- switch (*fmt) {
- case 'c':
- spec->type = FORMAT_TYPE_CHAR;
- return ++fmt - start;
-
- case 's':
- spec->type = FORMAT_TYPE_STR;
- return ++fmt - start;
-
- case 'p':
- spec->type = FORMAT_TYPE_PTR;
- return ++fmt - start;
-
- case '%':
- spec->type = FORMAT_TYPE_PERCENT_CHAR;
- return ++fmt - start;
-
- /* integer number formats - set up the flags and "break" */
- case 'o':
- spec->base = 8;
- break;
-
- case 'x':
-// spec->flags |= SMALL;
-
- case 'X':
- spec->base = 16;
- break;
-
- case 'd':
- case 'i':
- spec->flags |= SIGN;
- case 'u':
- break;
-
- default:
- spec->type = FORMAT_TYPE_INVALID;
- return fmt - start;
- }
-
- if (spec->qualifier == 'L')
- spec->type = FORMAT_TYPE_LONG_LONG;
- else if (spec->qualifier == 'l') {
- if (spec->flags & SIGN)
- spec->type = FORMAT_TYPE_LONG;
- else
- spec->type = FORMAT_TYPE_ULONG;
- } else {
- if (spec->flags & SIGN)
- spec->type = FORMAT_TYPE_INT;
- else
- spec->type = FORMAT_TYPE_UINT;
- }
-
- return ++fmt - start;
-}
-
-/**
- * vsnprintf - Format a string and place it in a buffer
- * @buf: The buffer to place the result into
- * @size: The size of the buffer, including the trailing null space
- * @fmt: The format string to use
- * @args: Arguments for the format string
- *
- * The return value is the number of characters which would
- * be generated for the given input, excluding the trailing
- * '\0', as per ISO C99. If you want to have the exact
- * number of characters written into @buf as return value
- * (not including the trailing '\0'), use vscnprintf(). If the
- * return is greater than or equal to @size, the resulting
- * string is truncated.
- *
- * If you're not already dealing with a va_list consider using snprintf().
- */
-static
-int kernel_vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
-{
- unsigned long long num;
- char *str, *end;
- struct printf_spec spec = {0};
-
- str = buf;
- end = buf + size;
-
- while (*fmt) {
- const char *old_fmt = fmt;
- int read = format_decode(fmt, &spec);
-
- fmt += read;
-
- switch (spec.type) {
- case FORMAT_TYPE_NONE: {
- int copy = read;
- if (str < end) {
- if (copy > end - str)
- copy = end - str;
- memcpy(str, old_fmt, copy);
- }
- str += read;
- break;
- }
-
- case FORMAT_TYPE_WIDTH:
- spec.field_width = va_arg(args, int);
- break;
-
- case FORMAT_TYPE_PRECISION:
- spec.precision = va_arg(args, int);
- break;
-
- case FORMAT_TYPE_CHAR: {
- char c;
-
- if (!(spec.flags & LEFT)) {
- while (--spec.field_width > 0) {
- if (str < end)
- *str = ' ';
- ++str;
-
- }
- }
- c = (unsigned char) va_arg(args, int);
- if (str < end)
- *str = c;
- ++str;
- while (--spec.field_width > 0) {
- if (str < end)
- *str = ' ';
- ++str;
- }
- break;
- }
-
- case FORMAT_TYPE_STR:
- str = string(str, end, va_arg(args, char *), spec);
- break;
-
- case FORMAT_TYPE_PTR:
- str = pointer(fmt+1, str, end, va_arg(args, void *),
- spec);
-// while (isalnum(*fmt))
-// fmt++;
- break;
-
- case FORMAT_TYPE_PERCENT_CHAR:
- if (str < end)
- *str = '%';
- ++str;
- break;
-
- case FORMAT_TYPE_INVALID:
- if (str < end)
- *str = '%';
- ++str;
- break;
-
- default:
- switch (spec.type) {
- case FORMAT_TYPE_LONG_LONG:
- num = va_arg(args, long long);
- break;
- case FORMAT_TYPE_ULONG:
- num = va_arg(args, unsigned long);
- break;
- case FORMAT_TYPE_LONG:
- num = va_arg(args, long);
- break;
- case FORMAT_TYPE_INT:
- num = (int) va_arg(args, int);
- break;
- default:
- num = va_arg(args, unsigned int);
- }
-
- str = number(str, end, num, spec);
- }
- }
-
-// if (size > 0) {
- if (str < end)
- *str = '\0';
-// else
-// end[-1] = '\0';
-// }
-
- /* the trailing null byte doesn't count towards the total */
- return str-buf;
-
-}
-
-int strace_vfprintf(FILE *fp, const char *fmt, va_list args)
-{
- static char *buf = NULL;
- static unsigned buflen = 0;
-
- int r;
- va_list a1;
-
- va_copy(a1, args);
- unsigned len = kernel_vsnprintf(buf, buflen, fmt, a1);
- va_end(a1);
-
- if (len >= buflen) {
- buflen = len + 256;
- free(buf);
- buf = xmalloc(buflen);
- /*len =*/ kernel_vsnprintf(buf, buflen, fmt, args);
- }
-
- r = fputs_unlocked(buf, fp);
- if (r < 0) return r;
- return len;
-}
-
-#endif /* USE_CUSTOM_PRINTF */