0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013 #include <linux/stdarg.h>
0014
0015 #include <linux/compiler.h>
0016 #include <linux/ctype.h>
0017 #include <linux/kernel.h>
0018 #include <linux/limits.h>
0019 #include <linux/string.h>
0020 #include <linux/types.h>
0021
0022 static
0023 int skip_atoi(const char **s)
0024 {
0025 int i = 0;
0026
0027 while (isdigit(**s))
0028 i = i * 10 + *((*s)++) - '0';
0029 return i;
0030 }
0031
0032
0033
0034
0035
0036
0037 static
0038 void put_dec_full4(char *end, unsigned int r)
0039 {
0040 int i;
0041
0042 for (i = 0; i < 3; i++) {
0043 unsigned int q = (r * 0xccd) >> 15;
0044 *--end = '0' + (r - q * 10);
0045 r = q;
0046 }
0047 *--end = '0' + r;
0048 }
0049
0050
0051
0052
0053
0054
0055
0056
0057
0058
0059 static
0060 unsigned int put_dec_helper4(char *end, unsigned int x)
0061 {
0062 unsigned int q = (x * 0x346DC5D7ULL) >> 43;
0063
0064 put_dec_full4(end, x - q * 10000);
0065 return q;
0066 }
0067
0068
0069
0070
0071
0072
0073 static
0074 char *put_dec(char *end, unsigned long long n)
0075 {
0076 unsigned int d3, d2, d1, q, h;
0077 char *p = end;
0078
0079 d1 = ((unsigned int)n >> 16);
0080 h = (n >> 32);
0081 d2 = (h ) & 0xffff;
0082 d3 = (h >> 16);
0083
0084
0085
0086 q = 656 * d3 + 7296 * d2 + 5536 * d1 + ((unsigned int)n & 0xffff);
0087 q = put_dec_helper4(p, q);
0088 p -= 4;
0089
0090 q += 7671 * d3 + 9496 * d2 + 6 * d1;
0091 q = put_dec_helper4(p, q);
0092 p -= 4;
0093
0094 q += 4749 * d3 + 42 * d2;
0095 q = put_dec_helper4(p, q);
0096 p -= 4;
0097
0098 q += 281 * d3;
0099 q = put_dec_helper4(p, q);
0100 p -= 4;
0101
0102 put_dec_full4(p, q);
0103 p -= 4;
0104
0105
0106 while (p < end && *p == '0')
0107 ++p;
0108
0109 return p;
0110 }
0111
0112 static
0113 char *number(char *end, unsigned long long num, int base, char locase)
0114 {
0115
0116
0117
0118
0119
0120
0121 static const char digits[16] = "0123456789ABCDEF";
0122
0123 switch (base) {
0124 case 10:
0125 if (num != 0)
0126 end = put_dec(end, num);
0127 break;
0128 case 8:
0129 for (; num != 0; num >>= 3)
0130 *--end = '0' + (num & 07);
0131 break;
0132 case 16:
0133 for (; num != 0; num >>= 4)
0134 *--end = digits[num & 0xf] | locase;
0135 break;
0136 default:
0137 unreachable();
0138 }
0139
0140 return end;
0141 }
0142
0143 #define ZEROPAD 1
0144 #define SIGN 2
0145 #define PLUS 4
0146 #define SPACE 8
0147 #define LEFT 16
0148 #define SMALL 32
0149 #define SPECIAL 64
0150 #define WIDE 128
0151
0152 static
0153 int get_flags(const char **fmt)
0154 {
0155 int flags = 0;
0156
0157 do {
0158 switch (**fmt) {
0159 case '-':
0160 flags |= LEFT;
0161 break;
0162 case '+':
0163 flags |= PLUS;
0164 break;
0165 case ' ':
0166 flags |= SPACE;
0167 break;
0168 case '#':
0169 flags |= SPECIAL;
0170 break;
0171 case '0':
0172 flags |= ZEROPAD;
0173 break;
0174 default:
0175 return flags;
0176 }
0177 ++(*fmt);
0178 } while (1);
0179 }
0180
0181 static
0182 int get_int(const char **fmt, va_list *ap)
0183 {
0184 if (isdigit(**fmt))
0185 return skip_atoi(fmt);
0186 if (**fmt == '*') {
0187 ++(*fmt);
0188
0189 return va_arg(*ap, int);
0190 }
0191 return 0;
0192 }
0193
0194 static
0195 unsigned long long get_number(int sign, int qualifier, va_list *ap)
0196 {
0197 if (sign) {
0198 switch (qualifier) {
0199 case 'L':
0200 return va_arg(*ap, long long);
0201 case 'l':
0202 return va_arg(*ap, long);
0203 case 'h':
0204 return (short)va_arg(*ap, int);
0205 case 'H':
0206 return (signed char)va_arg(*ap, int);
0207 default:
0208 return va_arg(*ap, int);
0209 };
0210 } else {
0211 switch (qualifier) {
0212 case 'L':
0213 return va_arg(*ap, unsigned long long);
0214 case 'l':
0215 return va_arg(*ap, unsigned long);
0216 case 'h':
0217 return (unsigned short)va_arg(*ap, int);
0218 case 'H':
0219 return (unsigned char)va_arg(*ap, int);
0220 default:
0221 return va_arg(*ap, unsigned int);
0222 }
0223 }
0224 }
0225
0226 static
0227 char get_sign(long long *num, int flags)
0228 {
0229 if (!(flags & SIGN))
0230 return 0;
0231 if (*num < 0) {
0232 *num = -(*num);
0233 return '-';
0234 }
0235 if (flags & PLUS)
0236 return '+';
0237 if (flags & SPACE)
0238 return ' ';
0239 return 0;
0240 }
0241
0242 static
0243 size_t utf16s_utf8nlen(const u16 *s16, size_t maxlen)
0244 {
0245 size_t len, clen;
0246
0247 for (len = 0; len < maxlen && *s16; len += clen) {
0248 u16 c0 = *s16++;
0249
0250
0251 clen = 1 + (c0 >= 0x80) + (c0 >= 0x800);
0252 if (len + clen > maxlen)
0253 break;
0254
0255
0256
0257
0258
0259
0260 if ((c0 & 0xfc00) == 0xd800) {
0261 if (len + clen == maxlen)
0262 break;
0263 if ((*s16 & 0xfc00) == 0xdc00) {
0264 ++s16;
0265 ++clen;
0266 }
0267 }
0268 }
0269
0270 return len;
0271 }
0272
0273 static
0274 u32 utf16_to_utf32(const u16 **s16)
0275 {
0276 u16 c0, c1;
0277
0278 c0 = *(*s16)++;
0279
0280 if ((c0 & 0xf800) != 0xd800)
0281 return c0;
0282
0283 if (c0 & 0x0400)
0284 return 0xfffd;
0285 c1 = **s16;
0286
0287 if ((c1 & 0xfc00) != 0xdc00)
0288 return 0xfffd;
0289
0290 ++(*s16);
0291 return (0x10000 - (0xd800 << 10) - 0xdc00) + (c0 << 10) + c1;
0292 }
0293
0294 #define PUTC(c) \
0295 do { \
0296 if (pos < size) \
0297 buf[pos] = (c); \
0298 ++pos; \
0299 } while (0);
0300
0301 int vsnprintf(char *buf, size_t size, const char *fmt, va_list ap)
0302 {
0303
0304 char tmp[(sizeof(unsigned long long) * 8 + 2) / 3];
0305 char *tmp_end = &tmp[ARRAY_SIZE(tmp)];
0306 long long num;
0307 int base;
0308 const char *s;
0309 size_t len, pos;
0310 char sign;
0311
0312 int flags;
0313
0314 int field_width;
0315 int precision;
0316
0317 int qualifier;
0318
0319 va_list args;
0320
0321
0322
0323
0324
0325
0326
0327
0328
0329
0330
0331
0332
0333
0334
0335 va_copy(args, ap);
0336
0337 for (pos = 0; *fmt; ++fmt) {
0338 if (*fmt != '%' || *++fmt == '%') {
0339 PUTC(*fmt);
0340 continue;
0341 }
0342
0343
0344 flags = get_flags(&fmt);
0345
0346
0347 field_width = get_int(&fmt, &args);
0348 if (field_width < 0) {
0349 field_width = -field_width;
0350 flags |= LEFT;
0351 }
0352
0353 if (flags & LEFT)
0354 flags &= ~ZEROPAD;
0355
0356
0357 precision = -1;
0358 if (*fmt == '.') {
0359 ++fmt;
0360 precision = get_int(&fmt, &args);
0361 if (precision >= 0)
0362 flags &= ~ZEROPAD;
0363 }
0364
0365
0366 qualifier = -1;
0367 if (*fmt == 'h' || *fmt == 'l') {
0368 qualifier = *fmt;
0369 ++fmt;
0370 if (qualifier == *fmt) {
0371 qualifier -= 'a'-'A';
0372 ++fmt;
0373 }
0374 }
0375
0376 sign = 0;
0377
0378 switch (*fmt) {
0379 case 'c':
0380 flags &= LEFT;
0381 s = tmp;
0382 if (qualifier == 'l') {
0383 ((u16 *)tmp)[0] = (u16)va_arg(args, unsigned int);
0384 ((u16 *)tmp)[1] = L'\0';
0385 precision = INT_MAX;
0386 goto wstring;
0387 } else {
0388 tmp[0] = (unsigned char)va_arg(args, int);
0389 precision = len = 1;
0390 }
0391 goto output;
0392
0393 case 's':
0394 flags &= LEFT;
0395 if (precision < 0)
0396 precision = INT_MAX;
0397 s = va_arg(args, void *);
0398 if (!s)
0399 s = precision < 6 ? "" : "(null)";
0400 else if (qualifier == 'l') {
0401 wstring:
0402 flags |= WIDE;
0403 precision = len = utf16s_utf8nlen((const u16 *)s, precision);
0404 goto output;
0405 }
0406 precision = len = strnlen(s, precision);
0407 goto output;
0408
0409
0410 case 'o':
0411 base = 8;
0412 break;
0413
0414 case 'p':
0415 if (precision < 0)
0416 precision = 2 * sizeof(void *);
0417 fallthrough;
0418 case 'x':
0419 flags |= SMALL;
0420 fallthrough;
0421 case 'X':
0422 base = 16;
0423 break;
0424
0425 case 'd':
0426 case 'i':
0427 flags |= SIGN;
0428 fallthrough;
0429 case 'u':
0430 flags &= ~SPECIAL;
0431 base = 10;
0432 break;
0433
0434 default:
0435
0436
0437
0438
0439
0440
0441 goto fail;
0442 }
0443 if (*fmt == 'p') {
0444 num = (unsigned long)va_arg(args, void *);
0445 } else {
0446 num = get_number(flags & SIGN, qualifier, &args);
0447 }
0448
0449 sign = get_sign(&num, flags);
0450 if (sign)
0451 --field_width;
0452
0453 s = number(tmp_end, num, base, flags & SMALL);
0454 len = tmp_end - s;
0455
0456 if (precision < 0)
0457 precision = 1;
0458
0459 if (precision < len)
0460 precision = len;
0461 if (flags & SPECIAL) {
0462
0463
0464
0465
0466
0467 if (base == 8 && precision == len)
0468 ++precision;
0469
0470
0471
0472
0473
0474 if (base == 16 && precision > 0)
0475 field_width -= 2;
0476 else
0477 flags &= ~SPECIAL;
0478 }
0479
0480
0481
0482
0483 if ((flags & ZEROPAD) && field_width > precision)
0484 precision = field_width;
0485
0486 output:
0487
0488 field_width -= precision;
0489
0490 if (!(flags & LEFT))
0491 while (field_width-- > 0)
0492 PUTC(' ');
0493
0494 if (sign)
0495 PUTC(sign);
0496
0497 if (flags & SPECIAL) {
0498 PUTC('0');
0499 PUTC( 'X' | (flags & SMALL));
0500 }
0501
0502 while (precision-- > len)
0503 PUTC('0');
0504
0505 if (flags & WIDE) {
0506 const u16 *ws = (const u16 *)s;
0507
0508 while (len-- > 0) {
0509 u32 c32 = utf16_to_utf32(&ws);
0510 u8 *s8;
0511 size_t clen;
0512
0513 if (c32 < 0x80) {
0514 PUTC(c32);
0515 continue;
0516 }
0517
0518
0519 clen = 1 + (c32 >= 0x800) + (c32 >= 0x10000);
0520
0521 len -= clen;
0522 s8 = (u8 *)&buf[pos];
0523
0524
0525 PUTC('\0');
0526 pos += clen;
0527 if (pos >= size)
0528 continue;
0529
0530
0531 *s8 = (0xf00 >> 1) >> clen;
0532
0533 for (s8 += clen; clen; --clen, c32 >>= 6)
0534 *s8-- = 0x80 | (c32 & 0x3f);
0535
0536 *s8 |= c32;
0537 }
0538 } else {
0539 while (len-- > 0)
0540 PUTC(*s++);
0541 }
0542
0543 while (field_width-- > 0)
0544 PUTC(' ');
0545 }
0546 fail:
0547 va_end(args);
0548
0549 if (size)
0550 buf[min(pos, size-1)] = '\0';
0551
0552 return pos;
0553 }
0554
0555 int snprintf(char *buf, size_t size, const char *fmt, ...)
0556 {
0557 va_list args;
0558 int i;
0559
0560 va_start(args, fmt);
0561 i = vsnprintf(buf, size, fmt, args);
0562 va_end(args);
0563 return i;
0564 }