18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* -*- linux-c -*- ------------------------------------------------------- * 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 1991, 1992 Linus Torvalds 58c2ecf20Sopenharmony_ci * Copyright 2007 rPath, Inc. - All Rights Reserved 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * ----------------------------------------------------------------------- */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci/* 108c2ecf20Sopenharmony_ci * Oh, it's a waste of space, but oh-so-yummy for debugging. 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <stdarg.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <linux/compiler.h> 168c2ecf20Sopenharmony_ci#include <linux/ctype.h> 178c2ecf20Sopenharmony_ci#include <linux/kernel.h> 188c2ecf20Sopenharmony_ci#include <linux/limits.h> 198c2ecf20Sopenharmony_ci#include <linux/string.h> 208c2ecf20Sopenharmony_ci#include <linux/types.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistatic 238c2ecf20Sopenharmony_ciint skip_atoi(const char **s) 248c2ecf20Sopenharmony_ci{ 258c2ecf20Sopenharmony_ci int i = 0; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci while (isdigit(**s)) 288c2ecf20Sopenharmony_ci i = i * 10 + *((*s)++) - '0'; 298c2ecf20Sopenharmony_ci return i; 308c2ecf20Sopenharmony_ci} 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci/* 338c2ecf20Sopenharmony_ci * put_dec_full4 handles numbers in the range 0 <= r < 10000. 348c2ecf20Sopenharmony_ci * The multiplier 0xccd is round(2^15/10), and the approximation 358c2ecf20Sopenharmony_ci * r/10 == (r * 0xccd) >> 15 is exact for all r < 16389. 368c2ecf20Sopenharmony_ci */ 378c2ecf20Sopenharmony_cistatic 388c2ecf20Sopenharmony_civoid put_dec_full4(char *end, unsigned int r) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci int i; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci for (i = 0; i < 3; i++) { 438c2ecf20Sopenharmony_ci unsigned int q = (r * 0xccd) >> 15; 448c2ecf20Sopenharmony_ci *--end = '0' + (r - q * 10); 458c2ecf20Sopenharmony_ci r = q; 468c2ecf20Sopenharmony_ci } 478c2ecf20Sopenharmony_ci *--end = '0' + r; 488c2ecf20Sopenharmony_ci} 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci/* put_dec is copied from lib/vsprintf.c with small modifications */ 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci/* 538c2ecf20Sopenharmony_ci * Call put_dec_full4 on x % 10000, return x / 10000. 548c2ecf20Sopenharmony_ci * The approximation x/10000 == (x * 0x346DC5D7) >> 43 558c2ecf20Sopenharmony_ci * holds for all x < 1,128,869,999. The largest value this 568c2ecf20Sopenharmony_ci * helper will ever be asked to convert is 1,125,520,955. 578c2ecf20Sopenharmony_ci * (second call in the put_dec code, assuming n is all-ones). 588c2ecf20Sopenharmony_ci */ 598c2ecf20Sopenharmony_cistatic 608c2ecf20Sopenharmony_ciunsigned int put_dec_helper4(char *end, unsigned int x) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci unsigned int q = (x * 0x346DC5D7ULL) >> 43; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci put_dec_full4(end, x - q * 10000); 658c2ecf20Sopenharmony_ci return q; 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci/* Based on code by Douglas W. Jones found at 698c2ecf20Sopenharmony_ci * <http://www.cs.uiowa.edu/~jones/bcd/decimal.html#sixtyfour> 708c2ecf20Sopenharmony_ci * (with permission from the author). 718c2ecf20Sopenharmony_ci * Performs no 64-bit division and hence should be fast on 32-bit machines. 728c2ecf20Sopenharmony_ci */ 738c2ecf20Sopenharmony_cistatic 748c2ecf20Sopenharmony_cichar *put_dec(char *end, unsigned long long n) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci unsigned int d3, d2, d1, q, h; 778c2ecf20Sopenharmony_ci char *p = end; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci d1 = ((unsigned int)n >> 16); /* implicit "& 0xffff" */ 808c2ecf20Sopenharmony_ci h = (n >> 32); 818c2ecf20Sopenharmony_ci d2 = (h ) & 0xffff; 828c2ecf20Sopenharmony_ci d3 = (h >> 16); /* implicit "& 0xffff" */ 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci /* n = 2^48 d3 + 2^32 d2 + 2^16 d1 + d0 858c2ecf20Sopenharmony_ci = 281_4749_7671_0656 d3 + 42_9496_7296 d2 + 6_5536 d1 + d0 */ 868c2ecf20Sopenharmony_ci q = 656 * d3 + 7296 * d2 + 5536 * d1 + ((unsigned int)n & 0xffff); 878c2ecf20Sopenharmony_ci q = put_dec_helper4(p, q); 888c2ecf20Sopenharmony_ci p -= 4; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci q += 7671 * d3 + 9496 * d2 + 6 * d1; 918c2ecf20Sopenharmony_ci q = put_dec_helper4(p, q); 928c2ecf20Sopenharmony_ci p -= 4; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci q += 4749 * d3 + 42 * d2; 958c2ecf20Sopenharmony_ci q = put_dec_helper4(p, q); 968c2ecf20Sopenharmony_ci p -= 4; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci q += 281 * d3; 998c2ecf20Sopenharmony_ci q = put_dec_helper4(p, q); 1008c2ecf20Sopenharmony_ci p -= 4; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci put_dec_full4(p, q); 1038c2ecf20Sopenharmony_ci p -= 4; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci /* strip off the extra 0's we printed */ 1068c2ecf20Sopenharmony_ci while (p < end && *p == '0') 1078c2ecf20Sopenharmony_ci ++p; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci return p; 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic 1138c2ecf20Sopenharmony_cichar *number(char *end, unsigned long long num, int base, char locase) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci /* 1168c2ecf20Sopenharmony_ci * locase = 0 or 0x20. ORing digits or letters with 'locase' 1178c2ecf20Sopenharmony_ci * produces same digits or (maybe lowercased) letters 1188c2ecf20Sopenharmony_ci */ 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci /* we are called with base 8, 10 or 16, only, thus don't need "G..." */ 1218c2ecf20Sopenharmony_ci static const char digits[16] = "0123456789ABCDEF"; /* "GHIJKLMNOPQRSTUVWXYZ"; */ 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci switch (base) { 1248c2ecf20Sopenharmony_ci case 10: 1258c2ecf20Sopenharmony_ci if (num != 0) 1268c2ecf20Sopenharmony_ci end = put_dec(end, num); 1278c2ecf20Sopenharmony_ci break; 1288c2ecf20Sopenharmony_ci case 8: 1298c2ecf20Sopenharmony_ci for (; num != 0; num >>= 3) 1308c2ecf20Sopenharmony_ci *--end = '0' + (num & 07); 1318c2ecf20Sopenharmony_ci break; 1328c2ecf20Sopenharmony_ci case 16: 1338c2ecf20Sopenharmony_ci for (; num != 0; num >>= 4) 1348c2ecf20Sopenharmony_ci *--end = digits[num & 0xf] | locase; 1358c2ecf20Sopenharmony_ci break; 1368c2ecf20Sopenharmony_ci default: 1378c2ecf20Sopenharmony_ci unreachable(); 1388c2ecf20Sopenharmony_ci } 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci return end; 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci#define ZEROPAD 1 /* pad with zero */ 1448c2ecf20Sopenharmony_ci#define SIGN 2 /* unsigned/signed long */ 1458c2ecf20Sopenharmony_ci#define PLUS 4 /* show plus */ 1468c2ecf20Sopenharmony_ci#define SPACE 8 /* space if plus */ 1478c2ecf20Sopenharmony_ci#define LEFT 16 /* left justified */ 1488c2ecf20Sopenharmony_ci#define SMALL 32 /* Must be 32 == 0x20 */ 1498c2ecf20Sopenharmony_ci#define SPECIAL 64 /* 0x */ 1508c2ecf20Sopenharmony_ci#define WIDE 128 /* UTF-16 string */ 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic 1538c2ecf20Sopenharmony_ciint get_flags(const char **fmt) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci int flags = 0; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci do { 1588c2ecf20Sopenharmony_ci switch (**fmt) { 1598c2ecf20Sopenharmony_ci case '-': 1608c2ecf20Sopenharmony_ci flags |= LEFT; 1618c2ecf20Sopenharmony_ci break; 1628c2ecf20Sopenharmony_ci case '+': 1638c2ecf20Sopenharmony_ci flags |= PLUS; 1648c2ecf20Sopenharmony_ci break; 1658c2ecf20Sopenharmony_ci case ' ': 1668c2ecf20Sopenharmony_ci flags |= SPACE; 1678c2ecf20Sopenharmony_ci break; 1688c2ecf20Sopenharmony_ci case '#': 1698c2ecf20Sopenharmony_ci flags |= SPECIAL; 1708c2ecf20Sopenharmony_ci break; 1718c2ecf20Sopenharmony_ci case '0': 1728c2ecf20Sopenharmony_ci flags |= ZEROPAD; 1738c2ecf20Sopenharmony_ci break; 1748c2ecf20Sopenharmony_ci default: 1758c2ecf20Sopenharmony_ci return flags; 1768c2ecf20Sopenharmony_ci } 1778c2ecf20Sopenharmony_ci ++(*fmt); 1788c2ecf20Sopenharmony_ci } while (1); 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cistatic 1828c2ecf20Sopenharmony_ciint get_int(const char **fmt, va_list *ap) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci if (isdigit(**fmt)) 1858c2ecf20Sopenharmony_ci return skip_atoi(fmt); 1868c2ecf20Sopenharmony_ci if (**fmt == '*') { 1878c2ecf20Sopenharmony_ci ++(*fmt); 1888c2ecf20Sopenharmony_ci /* it's the next argument */ 1898c2ecf20Sopenharmony_ci return va_arg(*ap, int); 1908c2ecf20Sopenharmony_ci } 1918c2ecf20Sopenharmony_ci return 0; 1928c2ecf20Sopenharmony_ci} 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_cistatic 1958c2ecf20Sopenharmony_ciunsigned long long get_number(int sign, int qualifier, va_list *ap) 1968c2ecf20Sopenharmony_ci{ 1978c2ecf20Sopenharmony_ci if (sign) { 1988c2ecf20Sopenharmony_ci switch (qualifier) { 1998c2ecf20Sopenharmony_ci case 'L': 2008c2ecf20Sopenharmony_ci return va_arg(*ap, long long); 2018c2ecf20Sopenharmony_ci case 'l': 2028c2ecf20Sopenharmony_ci return va_arg(*ap, long); 2038c2ecf20Sopenharmony_ci case 'h': 2048c2ecf20Sopenharmony_ci return (short)va_arg(*ap, int); 2058c2ecf20Sopenharmony_ci case 'H': 2068c2ecf20Sopenharmony_ci return (signed char)va_arg(*ap, int); 2078c2ecf20Sopenharmony_ci default: 2088c2ecf20Sopenharmony_ci return va_arg(*ap, int); 2098c2ecf20Sopenharmony_ci }; 2108c2ecf20Sopenharmony_ci } else { 2118c2ecf20Sopenharmony_ci switch (qualifier) { 2128c2ecf20Sopenharmony_ci case 'L': 2138c2ecf20Sopenharmony_ci return va_arg(*ap, unsigned long long); 2148c2ecf20Sopenharmony_ci case 'l': 2158c2ecf20Sopenharmony_ci return va_arg(*ap, unsigned long); 2168c2ecf20Sopenharmony_ci case 'h': 2178c2ecf20Sopenharmony_ci return (unsigned short)va_arg(*ap, int); 2188c2ecf20Sopenharmony_ci case 'H': 2198c2ecf20Sopenharmony_ci return (unsigned char)va_arg(*ap, int); 2208c2ecf20Sopenharmony_ci default: 2218c2ecf20Sopenharmony_ci return va_arg(*ap, unsigned int); 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci } 2248c2ecf20Sopenharmony_ci} 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_cistatic 2278c2ecf20Sopenharmony_cichar get_sign(long long *num, int flags) 2288c2ecf20Sopenharmony_ci{ 2298c2ecf20Sopenharmony_ci if (!(flags & SIGN)) 2308c2ecf20Sopenharmony_ci return 0; 2318c2ecf20Sopenharmony_ci if (*num < 0) { 2328c2ecf20Sopenharmony_ci *num = -(*num); 2338c2ecf20Sopenharmony_ci return '-'; 2348c2ecf20Sopenharmony_ci } 2358c2ecf20Sopenharmony_ci if (flags & PLUS) 2368c2ecf20Sopenharmony_ci return '+'; 2378c2ecf20Sopenharmony_ci if (flags & SPACE) 2388c2ecf20Sopenharmony_ci return ' '; 2398c2ecf20Sopenharmony_ci return 0; 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_cistatic 2438c2ecf20Sopenharmony_cisize_t utf16s_utf8nlen(const u16 *s16, size_t maxlen) 2448c2ecf20Sopenharmony_ci{ 2458c2ecf20Sopenharmony_ci size_t len, clen; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci for (len = 0; len < maxlen && *s16; len += clen) { 2488c2ecf20Sopenharmony_ci u16 c0 = *s16++; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci /* First, get the length for a BMP character */ 2518c2ecf20Sopenharmony_ci clen = 1 + (c0 >= 0x80) + (c0 >= 0x800); 2528c2ecf20Sopenharmony_ci if (len + clen > maxlen) 2538c2ecf20Sopenharmony_ci break; 2548c2ecf20Sopenharmony_ci /* 2558c2ecf20Sopenharmony_ci * If this is a high surrogate, and we're already at maxlen, we 2568c2ecf20Sopenharmony_ci * can't include the character if it's a valid surrogate pair. 2578c2ecf20Sopenharmony_ci * Avoid accessing one extra word just to check if it's valid 2588c2ecf20Sopenharmony_ci * or not. 2598c2ecf20Sopenharmony_ci */ 2608c2ecf20Sopenharmony_ci if ((c0 & 0xfc00) == 0xd800) { 2618c2ecf20Sopenharmony_ci if (len + clen == maxlen) 2628c2ecf20Sopenharmony_ci break; 2638c2ecf20Sopenharmony_ci if ((*s16 & 0xfc00) == 0xdc00) { 2648c2ecf20Sopenharmony_ci ++s16; 2658c2ecf20Sopenharmony_ci ++clen; 2668c2ecf20Sopenharmony_ci } 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci } 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci return len; 2718c2ecf20Sopenharmony_ci} 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_cistatic 2748c2ecf20Sopenharmony_ciu32 utf16_to_utf32(const u16 **s16) 2758c2ecf20Sopenharmony_ci{ 2768c2ecf20Sopenharmony_ci u16 c0, c1; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci c0 = *(*s16)++; 2798c2ecf20Sopenharmony_ci /* not a surrogate */ 2808c2ecf20Sopenharmony_ci if ((c0 & 0xf800) != 0xd800) 2818c2ecf20Sopenharmony_ci return c0; 2828c2ecf20Sopenharmony_ci /* invalid: low surrogate instead of high */ 2838c2ecf20Sopenharmony_ci if (c0 & 0x0400) 2848c2ecf20Sopenharmony_ci return 0xfffd; 2858c2ecf20Sopenharmony_ci c1 = **s16; 2868c2ecf20Sopenharmony_ci /* invalid: missing low surrogate */ 2878c2ecf20Sopenharmony_ci if ((c1 & 0xfc00) != 0xdc00) 2888c2ecf20Sopenharmony_ci return 0xfffd; 2898c2ecf20Sopenharmony_ci /* valid surrogate pair */ 2908c2ecf20Sopenharmony_ci ++(*s16); 2918c2ecf20Sopenharmony_ci return (0x10000 - (0xd800 << 10) - 0xdc00) + (c0 << 10) + c1; 2928c2ecf20Sopenharmony_ci} 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci#define PUTC(c) \ 2958c2ecf20Sopenharmony_cido { \ 2968c2ecf20Sopenharmony_ci if (pos < size) \ 2978c2ecf20Sopenharmony_ci buf[pos] = (c); \ 2988c2ecf20Sopenharmony_ci ++pos; \ 2998c2ecf20Sopenharmony_ci} while (0); 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ciint vsnprintf(char *buf, size_t size, const char *fmt, va_list ap) 3028c2ecf20Sopenharmony_ci{ 3038c2ecf20Sopenharmony_ci /* The maximum space required is to print a 64-bit number in octal */ 3048c2ecf20Sopenharmony_ci char tmp[(sizeof(unsigned long long) * 8 + 2) / 3]; 3058c2ecf20Sopenharmony_ci char *tmp_end = &tmp[ARRAY_SIZE(tmp)]; 3068c2ecf20Sopenharmony_ci long long num; 3078c2ecf20Sopenharmony_ci int base; 3088c2ecf20Sopenharmony_ci const char *s; 3098c2ecf20Sopenharmony_ci size_t len, pos; 3108c2ecf20Sopenharmony_ci char sign; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci int flags; /* flags to number() */ 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci int field_width; /* width of output field */ 3158c2ecf20Sopenharmony_ci int precision; /* min. # of digits for integers; max 3168c2ecf20Sopenharmony_ci number of chars for from string */ 3178c2ecf20Sopenharmony_ci int qualifier; /* 'h', 'hh', 'l' or 'll' for integer fields */ 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci va_list args; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci /* 3228c2ecf20Sopenharmony_ci * We want to pass our input va_list to helper functions by reference, 3238c2ecf20Sopenharmony_ci * but there's an annoying edge case. If va_list was originally passed 3248c2ecf20Sopenharmony_ci * to us by value, we could just pass &ap down to the helpers. This is 3258c2ecf20Sopenharmony_ci * the case on, for example, X86_32. 3268c2ecf20Sopenharmony_ci * However, on X86_64 (and possibly others), va_list is actually a 3278c2ecf20Sopenharmony_ci * size-1 array containing a structure. Our function parameter ap has 3288c2ecf20Sopenharmony_ci * decayed from T[1] to T*, and &ap has type T** rather than T(*)[1], 3298c2ecf20Sopenharmony_ci * which is what will be expected by a function taking a va_list * 3308c2ecf20Sopenharmony_ci * parameter. 3318c2ecf20Sopenharmony_ci * One standard way to solve this mess is by creating a copy in a local 3328c2ecf20Sopenharmony_ci * variable of type va_list and then passing a pointer to that local 3338c2ecf20Sopenharmony_ci * copy instead, which is what we do here. 3348c2ecf20Sopenharmony_ci */ 3358c2ecf20Sopenharmony_ci va_copy(args, ap); 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci for (pos = 0; *fmt; ++fmt) { 3388c2ecf20Sopenharmony_ci if (*fmt != '%' || *++fmt == '%') { 3398c2ecf20Sopenharmony_ci PUTC(*fmt); 3408c2ecf20Sopenharmony_ci continue; 3418c2ecf20Sopenharmony_ci } 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci /* process flags */ 3448c2ecf20Sopenharmony_ci flags = get_flags(&fmt); 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci /* get field width */ 3478c2ecf20Sopenharmony_ci field_width = get_int(&fmt, &args); 3488c2ecf20Sopenharmony_ci if (field_width < 0) { 3498c2ecf20Sopenharmony_ci field_width = -field_width; 3508c2ecf20Sopenharmony_ci flags |= LEFT; 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci if (flags & LEFT) 3548c2ecf20Sopenharmony_ci flags &= ~ZEROPAD; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci /* get the precision */ 3578c2ecf20Sopenharmony_ci precision = -1; 3588c2ecf20Sopenharmony_ci if (*fmt == '.') { 3598c2ecf20Sopenharmony_ci ++fmt; 3608c2ecf20Sopenharmony_ci precision = get_int(&fmt, &args); 3618c2ecf20Sopenharmony_ci if (precision >= 0) 3628c2ecf20Sopenharmony_ci flags &= ~ZEROPAD; 3638c2ecf20Sopenharmony_ci } 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci /* get the conversion qualifier */ 3668c2ecf20Sopenharmony_ci qualifier = -1; 3678c2ecf20Sopenharmony_ci if (*fmt == 'h' || *fmt == 'l') { 3688c2ecf20Sopenharmony_ci qualifier = *fmt; 3698c2ecf20Sopenharmony_ci ++fmt; 3708c2ecf20Sopenharmony_ci if (qualifier == *fmt) { 3718c2ecf20Sopenharmony_ci qualifier -= 'a'-'A'; 3728c2ecf20Sopenharmony_ci ++fmt; 3738c2ecf20Sopenharmony_ci } 3748c2ecf20Sopenharmony_ci } 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci sign = 0; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci switch (*fmt) { 3798c2ecf20Sopenharmony_ci case 'c': 3808c2ecf20Sopenharmony_ci flags &= LEFT; 3818c2ecf20Sopenharmony_ci s = tmp; 3828c2ecf20Sopenharmony_ci if (qualifier == 'l') { 3838c2ecf20Sopenharmony_ci ((u16 *)tmp)[0] = (u16)va_arg(args, unsigned int); 3848c2ecf20Sopenharmony_ci ((u16 *)tmp)[1] = L'\0'; 3858c2ecf20Sopenharmony_ci precision = INT_MAX; 3868c2ecf20Sopenharmony_ci goto wstring; 3878c2ecf20Sopenharmony_ci } else { 3888c2ecf20Sopenharmony_ci tmp[0] = (unsigned char)va_arg(args, int); 3898c2ecf20Sopenharmony_ci precision = len = 1; 3908c2ecf20Sopenharmony_ci } 3918c2ecf20Sopenharmony_ci goto output; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci case 's': 3948c2ecf20Sopenharmony_ci flags &= LEFT; 3958c2ecf20Sopenharmony_ci if (precision < 0) 3968c2ecf20Sopenharmony_ci precision = INT_MAX; 3978c2ecf20Sopenharmony_ci s = va_arg(args, void *); 3988c2ecf20Sopenharmony_ci if (!s) 3998c2ecf20Sopenharmony_ci s = precision < 6 ? "" : "(null)"; 4008c2ecf20Sopenharmony_ci else if (qualifier == 'l') { 4018c2ecf20Sopenharmony_ci wstring: 4028c2ecf20Sopenharmony_ci flags |= WIDE; 4038c2ecf20Sopenharmony_ci precision = len = utf16s_utf8nlen((const u16 *)s, precision); 4048c2ecf20Sopenharmony_ci goto output; 4058c2ecf20Sopenharmony_ci } 4068c2ecf20Sopenharmony_ci precision = len = strnlen(s, precision); 4078c2ecf20Sopenharmony_ci goto output; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci /* integer number formats - set up the flags and "break" */ 4108c2ecf20Sopenharmony_ci case 'o': 4118c2ecf20Sopenharmony_ci base = 8; 4128c2ecf20Sopenharmony_ci break; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci case 'p': 4158c2ecf20Sopenharmony_ci if (precision < 0) 4168c2ecf20Sopenharmony_ci precision = 2 * sizeof(void *); 4178c2ecf20Sopenharmony_ci fallthrough; 4188c2ecf20Sopenharmony_ci case 'x': 4198c2ecf20Sopenharmony_ci flags |= SMALL; 4208c2ecf20Sopenharmony_ci fallthrough; 4218c2ecf20Sopenharmony_ci case 'X': 4228c2ecf20Sopenharmony_ci base = 16; 4238c2ecf20Sopenharmony_ci break; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci case 'd': 4268c2ecf20Sopenharmony_ci case 'i': 4278c2ecf20Sopenharmony_ci flags |= SIGN; 4288c2ecf20Sopenharmony_ci fallthrough; 4298c2ecf20Sopenharmony_ci case 'u': 4308c2ecf20Sopenharmony_ci flags &= ~SPECIAL; 4318c2ecf20Sopenharmony_ci base = 10; 4328c2ecf20Sopenharmony_ci break; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci default: 4358c2ecf20Sopenharmony_ci /* 4368c2ecf20Sopenharmony_ci * Bail out if the conversion specifier is invalid. 4378c2ecf20Sopenharmony_ci * There's probably a typo in the format string and the 4388c2ecf20Sopenharmony_ci * remaining specifiers are unlikely to match up with 4398c2ecf20Sopenharmony_ci * the arguments. 4408c2ecf20Sopenharmony_ci */ 4418c2ecf20Sopenharmony_ci goto fail; 4428c2ecf20Sopenharmony_ci } 4438c2ecf20Sopenharmony_ci if (*fmt == 'p') { 4448c2ecf20Sopenharmony_ci num = (unsigned long)va_arg(args, void *); 4458c2ecf20Sopenharmony_ci } else { 4468c2ecf20Sopenharmony_ci num = get_number(flags & SIGN, qualifier, &args); 4478c2ecf20Sopenharmony_ci } 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci sign = get_sign(&num, flags); 4508c2ecf20Sopenharmony_ci if (sign) 4518c2ecf20Sopenharmony_ci --field_width; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci s = number(tmp_end, num, base, flags & SMALL); 4548c2ecf20Sopenharmony_ci len = tmp_end - s; 4558c2ecf20Sopenharmony_ci /* default precision is 1 */ 4568c2ecf20Sopenharmony_ci if (precision < 0) 4578c2ecf20Sopenharmony_ci precision = 1; 4588c2ecf20Sopenharmony_ci /* precision is minimum number of digits to print */ 4598c2ecf20Sopenharmony_ci if (precision < len) 4608c2ecf20Sopenharmony_ci precision = len; 4618c2ecf20Sopenharmony_ci if (flags & SPECIAL) { 4628c2ecf20Sopenharmony_ci /* 4638c2ecf20Sopenharmony_ci * For octal, a leading 0 is printed only if necessary, 4648c2ecf20Sopenharmony_ci * i.e. if it's not already there because of the 4658c2ecf20Sopenharmony_ci * precision. 4668c2ecf20Sopenharmony_ci */ 4678c2ecf20Sopenharmony_ci if (base == 8 && precision == len) 4688c2ecf20Sopenharmony_ci ++precision; 4698c2ecf20Sopenharmony_ci /* 4708c2ecf20Sopenharmony_ci * For hexadecimal, the leading 0x is skipped if the 4718c2ecf20Sopenharmony_ci * output is empty, i.e. both the number and the 4728c2ecf20Sopenharmony_ci * precision are 0. 4738c2ecf20Sopenharmony_ci */ 4748c2ecf20Sopenharmony_ci if (base == 16 && precision > 0) 4758c2ecf20Sopenharmony_ci field_width -= 2; 4768c2ecf20Sopenharmony_ci else 4778c2ecf20Sopenharmony_ci flags &= ~SPECIAL; 4788c2ecf20Sopenharmony_ci } 4798c2ecf20Sopenharmony_ci /* 4808c2ecf20Sopenharmony_ci * For zero padding, increase the precision to fill the field 4818c2ecf20Sopenharmony_ci * width. 4828c2ecf20Sopenharmony_ci */ 4838c2ecf20Sopenharmony_ci if ((flags & ZEROPAD) && field_width > precision) 4848c2ecf20Sopenharmony_ci precision = field_width; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_cioutput: 4878c2ecf20Sopenharmony_ci /* Calculate the padding necessary */ 4888c2ecf20Sopenharmony_ci field_width -= precision; 4898c2ecf20Sopenharmony_ci /* Leading padding with ' ' */ 4908c2ecf20Sopenharmony_ci if (!(flags & LEFT)) 4918c2ecf20Sopenharmony_ci while (field_width-- > 0) 4928c2ecf20Sopenharmony_ci PUTC(' '); 4938c2ecf20Sopenharmony_ci /* sign */ 4948c2ecf20Sopenharmony_ci if (sign) 4958c2ecf20Sopenharmony_ci PUTC(sign); 4968c2ecf20Sopenharmony_ci /* 0x/0X for hexadecimal */ 4978c2ecf20Sopenharmony_ci if (flags & SPECIAL) { 4988c2ecf20Sopenharmony_ci PUTC('0'); 4998c2ecf20Sopenharmony_ci PUTC( 'X' | (flags & SMALL)); 5008c2ecf20Sopenharmony_ci } 5018c2ecf20Sopenharmony_ci /* Zero padding and excess precision */ 5028c2ecf20Sopenharmony_ci while (precision-- > len) 5038c2ecf20Sopenharmony_ci PUTC('0'); 5048c2ecf20Sopenharmony_ci /* Actual output */ 5058c2ecf20Sopenharmony_ci if (flags & WIDE) { 5068c2ecf20Sopenharmony_ci const u16 *ws = (const u16 *)s; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci while (len-- > 0) { 5098c2ecf20Sopenharmony_ci u32 c32 = utf16_to_utf32(&ws); 5108c2ecf20Sopenharmony_ci u8 *s8; 5118c2ecf20Sopenharmony_ci size_t clen; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci if (c32 < 0x80) { 5148c2ecf20Sopenharmony_ci PUTC(c32); 5158c2ecf20Sopenharmony_ci continue; 5168c2ecf20Sopenharmony_ci } 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci /* Number of trailing octets */ 5198c2ecf20Sopenharmony_ci clen = 1 + (c32 >= 0x800) + (c32 >= 0x10000); 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci len -= clen; 5228c2ecf20Sopenharmony_ci s8 = (u8 *)&buf[pos]; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci /* Avoid writing partial character */ 5258c2ecf20Sopenharmony_ci PUTC('\0'); 5268c2ecf20Sopenharmony_ci pos += clen; 5278c2ecf20Sopenharmony_ci if (pos >= size) 5288c2ecf20Sopenharmony_ci continue; 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci /* Set high bits of leading octet */ 5318c2ecf20Sopenharmony_ci *s8 = (0xf00 >> 1) >> clen; 5328c2ecf20Sopenharmony_ci /* Write trailing octets in reverse order */ 5338c2ecf20Sopenharmony_ci for (s8 += clen; clen; --clen, c32 >>= 6) 5348c2ecf20Sopenharmony_ci *s8-- = 0x80 | (c32 & 0x3f); 5358c2ecf20Sopenharmony_ci /* Set low bits of leading octet */ 5368c2ecf20Sopenharmony_ci *s8 |= c32; 5378c2ecf20Sopenharmony_ci } 5388c2ecf20Sopenharmony_ci } else { 5398c2ecf20Sopenharmony_ci while (len-- > 0) 5408c2ecf20Sopenharmony_ci PUTC(*s++); 5418c2ecf20Sopenharmony_ci } 5428c2ecf20Sopenharmony_ci /* Trailing padding with ' ' */ 5438c2ecf20Sopenharmony_ci while (field_width-- > 0) 5448c2ecf20Sopenharmony_ci PUTC(' '); 5458c2ecf20Sopenharmony_ci } 5468c2ecf20Sopenharmony_cifail: 5478c2ecf20Sopenharmony_ci va_end(args); 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci if (size) 5508c2ecf20Sopenharmony_ci buf[min(pos, size-1)] = '\0'; 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci return pos; 5538c2ecf20Sopenharmony_ci} 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ciint snprintf(char *buf, size_t size, const char *fmt, ...) 5568c2ecf20Sopenharmony_ci{ 5578c2ecf20Sopenharmony_ci va_list args; 5588c2ecf20Sopenharmony_ci int i; 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci va_start(args, fmt); 5618c2ecf20Sopenharmony_ci i = vsnprintf(buf, size, fmt, args); 5628c2ecf20Sopenharmony_ci va_end(args); 5638c2ecf20Sopenharmony_ci return i; 5648c2ecf20Sopenharmony_ci} 565