vsprintf.c (12109B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* -*- linux-c -*- ------------------------------------------------------- * 3 * 4 * Copyright (C) 1991, 1992 Linus Torvalds 5 * Copyright 2007 rPath, Inc. - All Rights Reserved 6 * 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 22static 23int 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 approximation 35 * r/10 == (r * 0xccd) >> 15 is exact for all r < 16389. 36 */ 37static 38void 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) >> 43 55 * holds for all x < 1,128,869,999. The largest value this 56 * 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 */ 59static 60unsigned 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 at 69 * <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 */ 73static 74char *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 + d0 85 = 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 112static 113char *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) letters 118 */ 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 152static 153int 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 181static 182int 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 194static 195unsigned 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 226static 227char 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 242static 243size_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, we 256 * can't include the character if it's a valid surrogate pair. 257 * Avoid accessing one extra word just to check if it's valid 258 * 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 273static 274u32 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; max 316 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 passed 324 * to us by value, we could just pass &ap down to the helpers. This is 325 * the case on, for example, X86_32. 326 * However, on X86_64 (and possibly others), va_list is actually a 327 * size-1 array containing a structure. Our function parameter ap has 328 * 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 local 332 * variable of type va_list and then passing a pointer to that local 333 * 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 the 438 * remaining specifiers are unlikely to match up with 439 * 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 the 465 * precision. 466 */ 467 if (base == 8 && precision == len) 468 ++precision; 469 /* 470 * For hexadecimal, the leading 0x is skipped if the 471 * output is empty, i.e. both the number and the 472 * precision are 0. 473 */ 474 if (base == 16 && precision > 0) 475 field_width -= 2; 476 else 477 flags &= ~SPECIAL; 478 } 479 /* 480 * For zero padding, increase the precision to fill the field 481 * 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}