565 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/* -*- linux-c -*- ------------------------------------------------------- *3 *4 * Copyright (C) 1991, 1992 Linus Torvalds5 * Copyright 2007 rPath, Inc. - All Rights Reserved6 *7 * ----------------------------------------------------------------------- */8 9/*10 * Oh, it's a waste of space, but oh-so-yummy for debugging.11 */12 13#include <linux/stdarg.h>14 15#include <linux/compiler.h>16#include <linux/ctype.h>17#include <linux/kernel.h>18#include <linux/limits.h>19#include <linux/string.h>20#include <linux/types.h>21 22static23int skip_atoi(const char **s)24{25 int i = 0;26 27 while (isdigit(**s))28 i = i * 10 + *((*s)++) - '0';29 return i;30}31 32/*33 * put_dec_full4 handles numbers in the range 0 <= r < 10000.34 * The multiplier 0xccd is round(2^15/10), and the approximation35 * r/10 == (r * 0xccd) >> 15 is exact for all r < 16389.36 */37static38void put_dec_full4(char *end, unsigned int r)39{40 int i;41 42 for (i = 0; i < 3; i++) {43 unsigned int q = (r * 0xccd) >> 15;44 *--end = '0' + (r - q * 10);45 r = q;46 }47 *--end = '0' + r;48}49 50/* put_dec is copied from lib/vsprintf.c with small modifications */51 52/*53 * Call put_dec_full4 on x % 10000, return x / 10000.54 * The approximation x/10000 == (x * 0x346DC5D7) >> 4355 * holds for all x < 1,128,869,999. The largest value this56 * helper will ever be asked to convert is 1,125,520,955.57 * (second call in the put_dec code, assuming n is all-ones).58 */59static60unsigned int put_dec_helper4(char *end, unsigned int x)61{62 unsigned int q = (x * 0x346DC5D7ULL) >> 43;63 64 put_dec_full4(end, x - q * 10000);65 return q;66}67 68/* Based on code by Douglas W. Jones found at69 * <http://www.cs.uiowa.edu/~jones/bcd/decimal.html#sixtyfour>70 * (with permission from the author).71 * Performs no 64-bit division and hence should be fast on 32-bit machines.72 */73static74char *put_dec(char *end, unsigned long long n)75{76 unsigned int d3, d2, d1, q, h;77 char *p = end;78 79 d1 = ((unsigned int)n >> 16); /* implicit "& 0xffff" */80 h = (n >> 32);81 d2 = (h ) & 0xffff;82 d3 = (h >> 16); /* implicit "& 0xffff" */83 84 /* n = 2^48 d3 + 2^32 d2 + 2^16 d1 + d085 = 281_4749_7671_0656 d3 + 42_9496_7296 d2 + 6_5536 d1 + d0 */86 q = 656 * d3 + 7296 * d2 + 5536 * d1 + ((unsigned int)n & 0xffff);87 q = put_dec_helper4(p, q);88 p -= 4;89 90 q += 7671 * d3 + 9496 * d2 + 6 * d1;91 q = put_dec_helper4(p, q);92 p -= 4;93 94 q += 4749 * d3 + 42 * d2;95 q = put_dec_helper4(p, q);96 p -= 4;97 98 q += 281 * d3;99 q = put_dec_helper4(p, q);100 p -= 4;101 102 put_dec_full4(p, q);103 p -= 4;104 105 /* strip off the extra 0's we printed */106 while (p < end && *p == '0')107 ++p;108 109 return p;110}111 112static113char *number(char *end, unsigned long long num, int base, char locase)114{115 /*116 * locase = 0 or 0x20. ORing digits or letters with 'locase'117 * produces same digits or (maybe lowercased) letters118 */119 120 /* we are called with base 8, 10 or 16, only, thus don't need "G..." */121 static const char digits[16] = "0123456789ABCDEF"; /* "GHIJKLMNOPQRSTUVWXYZ"; */122 123 switch (base) {124 case 10:125 if (num != 0)126 end = put_dec(end, num);127 break;128 case 8:129 for (; num != 0; num >>= 3)130 *--end = '0' + (num & 07);131 break;132 case 16:133 for (; num != 0; num >>= 4)134 *--end = digits[num & 0xf] | locase;135 break;136 default:137 unreachable();138 }139 140 return end;141}142 143#define ZEROPAD 1 /* pad with zero */144#define SIGN 2 /* unsigned/signed long */145#define PLUS 4 /* show plus */146#define SPACE 8 /* space if plus */147#define LEFT 16 /* left justified */148#define SMALL 32 /* Must be 32 == 0x20 */149#define SPECIAL 64 /* 0x */150#define WIDE 128 /* UTF-16 string */151 152static153int get_flags(const char **fmt)154{155 int flags = 0;156 157 do {158 switch (**fmt) {159 case '-':160 flags |= LEFT;161 break;162 case '+':163 flags |= PLUS;164 break;165 case ' ':166 flags |= SPACE;167 break;168 case '#':169 flags |= SPECIAL;170 break;171 case '0':172 flags |= ZEROPAD;173 break;174 default:175 return flags;176 }177 ++(*fmt);178 } while (1);179}180 181static182int get_int(const char **fmt, va_list *ap)183{184 if (isdigit(**fmt))185 return skip_atoi(fmt);186 if (**fmt == '*') {187 ++(*fmt);188 /* it's the next argument */189 return va_arg(*ap, int);190 }191 return 0;192}193 194static195unsigned long long get_number(int sign, int qualifier, va_list *ap)196{197 if (sign) {198 switch (qualifier) {199 case 'L':200 return va_arg(*ap, long long);201 case 'l':202 return va_arg(*ap, long);203 case 'h':204 return (short)va_arg(*ap, int);205 case 'H':206 return (signed char)va_arg(*ap, int);207 default:208 return va_arg(*ap, int);209 };210 } else {211 switch (qualifier) {212 case 'L':213 return va_arg(*ap, unsigned long long);214 case 'l':215 return va_arg(*ap, unsigned long);216 case 'h':217 return (unsigned short)va_arg(*ap, int);218 case 'H':219 return (unsigned char)va_arg(*ap, int);220 default:221 return va_arg(*ap, unsigned int);222 }223 }224}225 226static227char get_sign(long long *num, int flags)228{229 if (!(flags & SIGN))230 return 0;231 if (*num < 0) {232 *num = -(*num);233 return '-';234 }235 if (flags & PLUS)236 return '+';237 if (flags & SPACE)238 return ' ';239 return 0;240}241 242static243size_t utf16s_utf8nlen(const u16 *s16, size_t maxlen)244{245 size_t len, clen;246 247 for (len = 0; len < maxlen && *s16; len += clen) {248 u16 c0 = *s16++;249 250 /* First, get the length for a BMP character */251 clen = 1 + (c0 >= 0x80) + (c0 >= 0x800);252 if (len + clen > maxlen)253 break;254 /*255 * If this is a high surrogate, and we're already at maxlen, we256 * can't include the character if it's a valid surrogate pair.257 * Avoid accessing one extra word just to check if it's valid258 * or not.259 */260 if ((c0 & 0xfc00) == 0xd800) {261 if (len + clen == maxlen)262 break;263 if ((*s16 & 0xfc00) == 0xdc00) {264 ++s16;265 ++clen;266 }267 }268 }269 270 return len;271}272 273static274u32 utf16_to_utf32(const u16 **s16)275{276 u16 c0, c1;277 278 c0 = *(*s16)++;279 /* not a surrogate */280 if ((c0 & 0xf800) != 0xd800)281 return c0;282 /* invalid: low surrogate instead of high */283 if (c0 & 0x0400)284 return 0xfffd;285 c1 = **s16;286 /* invalid: missing low surrogate */287 if ((c1 & 0xfc00) != 0xdc00)288 return 0xfffd;289 /* valid surrogate pair */290 ++(*s16);291 return (0x10000 - (0xd800 << 10) - 0xdc00) + (c0 << 10) + c1;292}293 294#define PUTC(c) \295do { \296 if (pos < size) \297 buf[pos] = (c); \298 ++pos; \299} while (0);300 301int vsnprintf(char *buf, size_t size, const char *fmt, va_list ap)302{303 /* The maximum space required is to print a 64-bit number in octal */304 char tmp[(sizeof(unsigned long long) * 8 + 2) / 3];305 char *tmp_end = &tmp[ARRAY_SIZE(tmp)];306 long long num;307 int base;308 const char *s;309 size_t len, pos;310 char sign;311 312 int flags; /* flags to number() */313 314 int field_width; /* width of output field */315 int precision; /* min. # of digits for integers; max316 number of chars for from string */317 int qualifier; /* 'h', 'hh', 'l' or 'll' for integer fields */318 319 va_list args;320 321 /*322 * We want to pass our input va_list to helper functions by reference,323 * but there's an annoying edge case. If va_list was originally passed324 * to us by value, we could just pass &ap down to the helpers. This is325 * the case on, for example, X86_32.326 * However, on X86_64 (and possibly others), va_list is actually a327 * size-1 array containing a structure. Our function parameter ap has328 * decayed from T[1] to T*, and &ap has type T** rather than T(*)[1],329 * which is what will be expected by a function taking a va_list *330 * parameter.331 * One standard way to solve this mess is by creating a copy in a local332 * variable of type va_list and then passing a pointer to that local333 * copy instead, which is what we do here.334 */335 va_copy(args, ap);336 337 for (pos = 0; *fmt; ++fmt) {338 if (*fmt != '%' || *++fmt == '%') {339 PUTC(*fmt);340 continue;341 }342 343 /* process flags */344 flags = get_flags(&fmt);345 346 /* get field width */347 field_width = get_int(&fmt, &args);348 if (field_width < 0) {349 field_width = -field_width;350 flags |= LEFT;351 }352 353 if (flags & LEFT)354 flags &= ~ZEROPAD;355 356 /* get the precision */357 precision = -1;358 if (*fmt == '.') {359 ++fmt;360 precision = get_int(&fmt, &args);361 if (precision >= 0)362 flags &= ~ZEROPAD;363 }364 365 /* get the conversion qualifier */366 qualifier = -1;367 if (*fmt == 'h' || *fmt == 'l') {368 qualifier = *fmt;369 ++fmt;370 if (qualifier == *fmt) {371 qualifier -= 'a'-'A';372 ++fmt;373 }374 }375 376 sign = 0;377 378 switch (*fmt) {379 case 'c':380 flags &= LEFT;381 s = tmp;382 if (qualifier == 'l') {383 ((u16 *)tmp)[0] = (u16)va_arg(args, unsigned int);384 ((u16 *)tmp)[1] = L'\0';385 precision = INT_MAX;386 goto wstring;387 } else {388 tmp[0] = (unsigned char)va_arg(args, int);389 precision = len = 1;390 }391 goto output;392 393 case 's':394 flags &= LEFT;395 if (precision < 0)396 precision = INT_MAX;397 s = va_arg(args, void *);398 if (!s)399 s = precision < 6 ? "" : "(null)";400 else if (qualifier == 'l') {401 wstring:402 flags |= WIDE;403 precision = len = utf16s_utf8nlen((const u16 *)s, precision);404 goto output;405 }406 precision = len = strnlen(s, precision);407 goto output;408 409 /* integer number formats - set up the flags and "break" */410 case 'o':411 base = 8;412 break;413 414 case 'p':415 if (precision < 0)416 precision = 2 * sizeof(void *);417 fallthrough;418 case 'x':419 flags |= SMALL;420 fallthrough;421 case 'X':422 base = 16;423 break;424 425 case 'd':426 case 'i':427 flags |= SIGN;428 fallthrough;429 case 'u':430 flags &= ~SPECIAL;431 base = 10;432 break;433 434 default:435 /*436 * Bail out if the conversion specifier is invalid.437 * There's probably a typo in the format string and the438 * remaining specifiers are unlikely to match up with439 * the arguments.440 */441 goto fail;442 }443 if (*fmt == 'p') {444 num = (unsigned long)va_arg(args, void *);445 } else {446 num = get_number(flags & SIGN, qualifier, &args);447 }448 449 sign = get_sign(&num, flags);450 if (sign)451 --field_width;452 453 s = number(tmp_end, num, base, flags & SMALL);454 len = tmp_end - s;455 /* default precision is 1 */456 if (precision < 0)457 precision = 1;458 /* precision is minimum number of digits to print */459 if (precision < len)460 precision = len;461 if (flags & SPECIAL) {462 /*463 * For octal, a leading 0 is printed only if necessary,464 * i.e. if it's not already there because of the465 * precision.466 */467 if (base == 8 && precision == len)468 ++precision;469 /*470 * For hexadecimal, the leading 0x is skipped if the471 * output is empty, i.e. both the number and the472 * precision are 0.473 */474 if (base == 16 && precision > 0)475 field_width -= 2;476 else477 flags &= ~SPECIAL;478 }479 /*480 * For zero padding, increase the precision to fill the field481 * width.482 */483 if ((flags & ZEROPAD) && field_width > precision)484 precision = field_width;485 486output:487 /* Calculate the padding necessary */488 field_width -= precision;489 /* Leading padding with ' ' */490 if (!(flags & LEFT))491 while (field_width-- > 0)492 PUTC(' ');493 /* sign */494 if (sign)495 PUTC(sign);496 /* 0x/0X for hexadecimal */497 if (flags & SPECIAL) {498 PUTC('0');499 PUTC( 'X' | (flags & SMALL));500 }501 /* Zero padding and excess precision */502 while (precision-- > len)503 PUTC('0');504 /* Actual output */505 if (flags & WIDE) {506 const u16 *ws = (const u16 *)s;507 508 while (len-- > 0) {509 u32 c32 = utf16_to_utf32(&ws);510 u8 *s8;511 size_t clen;512 513 if (c32 < 0x80) {514 PUTC(c32);515 continue;516 }517 518 /* Number of trailing octets */519 clen = 1 + (c32 >= 0x800) + (c32 >= 0x10000);520 521 len -= clen;522 s8 = (u8 *)&buf[pos];523 524 /* Avoid writing partial character */525 PUTC('\0');526 pos += clen;527 if (pos >= size)528 continue;529 530 /* Set high bits of leading octet */531 *s8 = (0xf00 >> 1) >> clen;532 /* Write trailing octets in reverse order */533 for (s8 += clen; clen; --clen, c32 >>= 6)534 *s8-- = 0x80 | (c32 & 0x3f);535 /* Set low bits of leading octet */536 *s8 |= c32;537 }538 } else {539 while (len-- > 0)540 PUTC(*s++);541 }542 /* Trailing padding with ' ' */543 while (field_width-- > 0)544 PUTC(' ');545 }546fail:547 va_end(args);548 549 if (size)550 buf[min(pos, size-1)] = '\0';551 552 return pos;553}554 555int snprintf(char *buf, size_t size, const char *fmt, ...)556{557 va_list args;558 int i;559 560 va_start(args, fmt);561 i = vsnprintf(buf, size, fmt, args);562 va_end(args);563 return i;564}565