]>
Commit | Line | Data |
---|---|---|
e87dfb05 AA |
1 | // SPDX-License-Identifier: BSD-3-Clause |
2 | /* | |
3 | * Copyright (c) 1990, 1993 | |
4 | * The Regents of the University of California. All rights reserved. | |
5 | * | |
6 | * This code is derived from software contributed to Berkeley by | |
7 | * Chris Torek. | |
8 | * | |
9 | * Copyright (c) 2011 The FreeBSD Foundation | |
10 | * All rights reserved. | |
11 | * Portions of this software were developed by David Chisnall | |
12 | * under sponsorship from the FreeBSD Foundation. | |
13 | * | |
14 | * Author: Juergen Gross <[email protected]> | |
15 | * Date: Jun 2016 | |
16 | */ | |
17 | ||
18 | #if !defined HAVE_LIBC | |
19 | ||
20 | #include <os.h> | |
21 | #include <linux/kernel.h> | |
22 | #include <linux/ctype.h> | |
23 | #include <vsprintf.h> | |
24 | #include <linux/string.h> | |
25 | #include <malloc.h> | |
26 | #define __DECONST(type, var) ((type)(uintptr_t)(const void *)(var)) | |
27 | ||
28 | /** | |
29 | * struct str_info - Input string parameters | |
30 | * @neg: negative number or not | |
31 | * 0 - not negative | |
32 | * 1 - negative | |
33 | * @any: set any if any `digits' consumed; make it negative to indicate | |
34 | * overflow | |
35 | * @acc: accumulated value | |
36 | */ | |
37 | struct str_info { | |
38 | int neg, any; | |
39 | u64 acc; | |
40 | }; | |
41 | ||
42 | /** | |
43 | * str_to_int_convert() - Write string data to structure | |
44 | * @nptr: pointer to string | |
45 | * @base: number's base | |
46 | * @unsign: describes what integer is expected | |
47 | * 0 - not unsigned | |
48 | * 1 - unsigned | |
49 | * | |
50 | * Ignores `locale' stuff. Assumes that the upper and lower case | |
51 | * alphabets and digits are each contiguous. | |
52 | * | |
53 | * Return: struct str_info *, which contains string data to future process | |
54 | */ | |
55 | static struct str_info * | |
56 | str_to_int_convert(const char **nptr, int base, unsigned int unsign) | |
57 | { | |
58 | const char *s = *nptr; | |
59 | u64 acc; | |
60 | unsigned char c; | |
61 | u64 cutoff; | |
62 | int neg, any, cutlim; | |
63 | u64 qbase; | |
64 | struct str_info *info; | |
65 | ||
66 | /* | |
67 | * Skip white space and pick up leading +/- sign if any. | |
68 | * If base is 0, allow 0x for hex and 0 for octal, else | |
69 | * assume decimal; if base is already 16, allow 0x. | |
70 | */ | |
71 | info = (struct str_info *)malloc(sizeof(struct str_info)); | |
72 | if (!info) | |
73 | return NULL; | |
74 | ||
75 | do { | |
76 | c = *s++; | |
77 | } while (isspace(c)); | |
78 | if (c == '-') { | |
79 | neg = 1; | |
80 | c = *s++; | |
81 | } else { | |
82 | neg = 0; | |
83 | if (c == '+') | |
84 | c = *s++; | |
85 | } | |
86 | if ((base == 0 || base == 16) && | |
87 | c == '0' && (*s == 'x' || *s == 'X')) { | |
88 | c = s[1]; | |
89 | s += 2; | |
90 | base = 16; | |
91 | } | |
92 | if (base == 0) | |
93 | base = c == '0' ? 8 : 10; | |
94 | ||
95 | /* | |
96 | * Compute the cutoff value between legal numbers and illegal | |
97 | * numbers. That is the largest legal value, divided by the | |
98 | * base. An input number that is greater than this value, if | |
99 | * followed by a legal input character, is too big. One that | |
100 | * is equal to this value may be valid or not; the limit | |
101 | * between valid and invalid numbers is then based on the last | |
102 | * digit. For instance, if the range for quads is | |
103 | * [-9223372036854775808..9223372036854775807] and the input base | |
104 | * is 10, cutoff will be set to 922337203685477580 and cutlim to | |
105 | * either 7 (neg==0) or 8 (neg==1), meaning that if we have | |
106 | * accumulated a value > 922337203685477580, or equal but the | |
107 | * next digit is > 7 (or 8), the number is too big, and we will | |
108 | * return a range error. | |
109 | * | |
110 | * Set any if any `digits' consumed; make it negative to indicate | |
111 | * overflow. | |
112 | */ | |
113 | qbase = (unsigned int)base; | |
114 | ||
115 | if (!unsign) { | |
116 | cutoff = neg ? (u64)-(LLONG_MIN + LLONG_MAX) + LLONG_MAX : LLONG_MAX; | |
117 | cutlim = cutoff % qbase; | |
118 | cutoff /= qbase; | |
119 | } else { | |
120 | cutoff = (u64)ULLONG_MAX / qbase; | |
121 | cutlim = (u64)ULLONG_MAX % qbase; | |
122 | } | |
123 | ||
124 | for (acc = 0, any = 0;; c = *s++) { | |
125 | if (!isascii(c)) | |
126 | break; | |
127 | if (isdigit(c)) | |
128 | c -= '0'; | |
129 | else if (isalpha(c)) | |
130 | c -= isupper(c) ? 'A' - 10 : 'a' - 10; | |
131 | else | |
132 | break; | |
133 | if (c >= base) | |
134 | break; | |
135 | if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim)) { | |
136 | any = -1; | |
137 | } else { | |
138 | any = 1; | |
139 | acc *= qbase; | |
140 | acc += c; | |
141 | } | |
142 | } | |
143 | ||
144 | info->any = any; | |
145 | info->neg = neg; | |
146 | info->acc = acc; | |
147 | ||
148 | *nptr = s; | |
149 | ||
150 | return info; | |
151 | } | |
152 | ||
153 | /** | |
154 | * strtoq() - Convert a string to a quad integer | |
155 | * @nptr: pointer to string | |
156 | * @endptr: pointer to number's end in the string | |
157 | * @base: number's base | |
158 | * | |
159 | * Return: s64 quad integer number converted from input string | |
160 | */ | |
161 | static s64 | |
162 | strtoq(const char *nptr, char **endptr, int base) | |
163 | { | |
164 | const char *s = nptr; | |
165 | u64 acc; | |
166 | int unsign = 0; | |
167 | struct str_info *info; | |
168 | ||
169 | info = str_to_int_convert(&s, base, unsign); | |
170 | if (!info) | |
171 | return -1; | |
172 | ||
173 | acc = info->acc; | |
174 | ||
175 | if (info->any < 0) | |
176 | acc = info->neg ? LLONG_MIN : LLONG_MAX; | |
177 | else if (info->neg) | |
178 | acc = -acc; | |
179 | if (endptr != 0) | |
180 | *endptr = __DECONST(char *, info->any ? s - 1 : nptr); | |
181 | ||
182 | free(info); | |
183 | ||
184 | return acc; | |
185 | } | |
186 | ||
187 | /** | |
188 | * strtouq() - Convert a string to an unsigned quad integer | |
189 | * @nptr: pointer to string | |
190 | * @endptr: pointer to number's end in the string | |
191 | * @base: number's base | |
192 | * | |
193 | * Return: s64 unsigned quad integer number converted from | |
194 | * input string | |
195 | */ | |
196 | u64 | |
197 | strtouq(const char *nptr, char **endptr, int base) | |
198 | { | |
199 | const char *s = nptr; | |
200 | u64 acc; | |
201 | int unsign = 1; | |
202 | struct str_info *info; | |
203 | ||
204 | info = str_to_int_convert(&s, base, unsign); | |
205 | if (!info) | |
206 | return -1; | |
207 | ||
208 | acc = info->acc; | |
209 | ||
210 | if (info->any < 0) | |
211 | acc = ULLONG_MAX; | |
212 | else if (info->neg) | |
213 | acc = -acc; | |
214 | if (endptr != 0) | |
215 | *endptr = __DECONST(char *, info->any ? s - 1 : nptr); | |
216 | ||
217 | free(info); | |
218 | ||
219 | return acc; | |
220 | } | |
221 | ||
222 | /** | |
223 | * __sccl() - Fill in the given table from the scanset at the given format | |
224 | * (just after `[') | |
225 | * @tab: table to fill in | |
226 | * @fmt: format of buffer | |
227 | * | |
228 | * The table has a 1 wherever characters should be considered part of the | |
229 | * scanset. | |
230 | * | |
231 | * Return: pointer to the character past the closing `]' | |
232 | */ | |
233 | static const u_char * | |
234 | __sccl(char *tab, const u_char *fmt) | |
235 | { | |
236 | int c, n, v; | |
237 | ||
238 | /* first `clear' the whole table */ | |
239 | c = *fmt++; /* first char hat => negated scanset */ | |
240 | if (c == '^') { | |
241 | v = 1; /* default => accept */ | |
242 | c = *fmt++; /* get new first char */ | |
243 | } else { | |
244 | v = 0; /* default => reject */ | |
245 | } | |
246 | ||
247 | /* XXX: Will not work if sizeof(tab*) > sizeof(char) */ | |
248 | for (n = 0; n < 256; n++) | |
249 | tab[n] = v; /* memset(tab, v, 256) */ | |
250 | ||
251 | if (c == 0) | |
252 | return (fmt - 1);/* format ended before closing ] */ | |
253 | ||
254 | /* | |
255 | * Now set the entries corresponding to the actual scanset | |
256 | * to the opposite of the above. | |
257 | * | |
258 | * The first character may be ']' (or '-') without being special; | |
259 | * the last character may be '-'. | |
260 | */ | |
261 | v = 1 - v; | |
262 | for (;;) { | |
263 | tab[c] = v; /* take character c */ | |
264 | doswitch: | |
265 | n = *fmt++; /* and examine the next */ | |
266 | switch (n) { | |
267 | case 0: /* format ended too soon */ | |
268 | return (fmt - 1); | |
269 | ||
270 | case '-': | |
271 | /* | |
272 | * A scanset of the form | |
273 | * [01+-] | |
274 | * is defined as `the digit 0, the digit 1, | |
275 | * the character +, the character -', but | |
276 | * the effect of a scanset such as | |
277 | * [a-zA-Z0-9] | |
278 | * is implementation defined. The V7 Unix | |
279 | * scanf treats `a-z' as `the letters a through | |
280 | * z', but treats `a-a' as `the letter a, the | |
281 | * character -, and the letter a'. | |
282 | * | |
283 | * For compatibility, the `-' is not considerd | |
284 | * to define a range if the character following | |
285 | * it is either a close bracket (required by ANSI) | |
286 | * or is not numerically greater than the character | |
287 | * we just stored in the table (c). | |
288 | */ | |
289 | n = *fmt; | |
290 | if (n == ']' || n < c) { | |
291 | c = '-'; | |
292 | break; /* resume the for(;;) */ | |
293 | } | |
294 | fmt++; | |
295 | /* fill in the range */ | |
296 | do { | |
297 | tab[++c] = v; | |
298 | } while (c < n); | |
299 | c = n; | |
300 | /* | |
301 | * Alas, the V7 Unix scanf also treats formats | |
302 | * such as [a-c-e] as `the letters a through e'. | |
303 | * This too is permitted by the standard.... | |
304 | */ | |
305 | goto doswitch; | |
306 | break; | |
307 | ||
308 | case ']': /* end of scanset */ | |
309 | return (fmt); | |
310 | ||
311 | default: /* just another character */ | |
312 | c = n; | |
313 | break; | |
314 | } | |
315 | } | |
316 | /* NOTREACHED */ | |
317 | } | |
318 | ||
319 | /** | |
320 | * vsscanf - Unformat a buffer into a list of arguments | |
321 | * @buf: input buffer | |
322 | * @fmt: format of buffer | |
323 | * @args: arguments | |
324 | */ | |
325 | #define BUF 32 /* Maximum length of numeric string. */ | |
326 | ||
327 | /* | |
328 | * Flags used during conversion. | |
329 | */ | |
330 | #define LONG 0x01 /* l: long or double */ | |
331 | #define SHORT 0x04 /* h: short */ | |
332 | #define SUPPRESS 0x08 /* suppress assignment */ | |
333 | #define POINTER 0x10 /* weird %p pointer (`fake hex') */ | |
334 | #define NOSKIP 0x20 /* do not skip blanks */ | |
335 | #define QUAD 0x400 | |
336 | #define SHORTSHORT 0x4000 /** hh: char */ | |
337 | ||
338 | /* | |
339 | * The following are used in numeric conversions only: | |
340 | * SIGNOK, NDIGITS, DPTOK, and EXPOK are for floating point; | |
341 | * SIGNOK, NDIGITS, PFXOK, and NZDIGITS are for integral. | |
342 | */ | |
343 | #define SIGNOK 0x40 /* +/- is (still) legal */ | |
344 | #define NDIGITS 0x80 /* no digits detected */ | |
345 | ||
346 | #define DPTOK 0x100 /* (float) decimal point is still legal */ | |
347 | #define EXPOK 0x200 /* (float) exponent (e+3, etc) still legal */ | |
348 | ||
349 | #define PFXOK 0x100 /* 0x prefix is (still) legal */ | |
350 | #define NZDIGITS 0x200 /* no zero digits detected */ | |
351 | ||
352 | /* | |
353 | * Conversion types. | |
354 | */ | |
355 | #define CT_CHAR 0 /* %c conversion */ | |
356 | #define CT_CCL 1 /* %[...] conversion */ | |
357 | #define CT_STRING 2 /* %s conversion */ | |
358 | #define CT_INT 3 /* integer, i.e., strtoq or strtouq */ | |
359 | typedef u64 (*ccfntype)(const char *, char **, int); | |
360 | ||
361 | int | |
362 | vsscanf(const char *inp, char const *fmt0, va_list ap) | |
363 | { | |
364 | int inr; | |
365 | const u_char *fmt = (const u_char *)fmt0; | |
366 | int c; /* character from format, or conversion */ | |
367 | size_t width; /* field width, or 0 */ | |
368 | char *p; /* points into all kinds of strings */ | |
369 | int n; /* handy integer */ | |
370 | int flags; /* flags as defined above */ | |
371 | char *p0; /* saves original value of p when necessary */ | |
372 | int nassigned; /* number of fields assigned */ | |
373 | int nconversions; /* number of conversions */ | |
374 | int nread; /* number of characters consumed from fp */ | |
375 | int base; /* base argument to strtoq/strtouq */ | |
376 | ccfntype ccfn; /* conversion function (strtoq/strtouq) */ | |
377 | char ccltab[256]; /* character class table for %[...] */ | |
378 | char buf[BUF]; /* buffer for numeric conversions */ | |
379 | ||
380 | /* `basefix' is used to avoid `if' tests in the integer scanner */ | |
381 | static short basefix[17] = { 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, | |
382 | 12, 13, 14, 15, 16 }; | |
383 | ||
384 | inr = strlen(inp); | |
385 | ||
386 | nassigned = 0; | |
387 | nconversions = 0; | |
388 | nread = 0; | |
389 | base = 0; /* XXX just to keep gcc happy */ | |
390 | ccfn = NULL; /* XXX just to keep gcc happy */ | |
391 | for (;;) { | |
392 | c = *fmt++; | |
393 | if (c == 0) | |
394 | return (nassigned); | |
395 | if (isspace(c)) { | |
396 | while (inr > 0 && isspace(*inp)) | |
397 | nread++, inr--, inp++; | |
398 | continue; | |
399 | } | |
400 | if (c != '%') | |
401 | goto literal; | |
402 | width = 0; | |
403 | flags = 0; | |
404 | /* | |
405 | * switch on the format. continue if done; | |
406 | * break once format type is derived. | |
407 | */ | |
408 | again: c = *fmt++; | |
409 | switch (c) { | |
410 | case '%': | |
411 | literal: | |
412 | if (inr <= 0) | |
413 | goto input_failure; | |
414 | if (*inp != c) | |
415 | goto match_failure; | |
416 | inr--, inp++; | |
417 | nread++; | |
418 | continue; | |
419 | ||
420 | case '*': | |
421 | flags |= SUPPRESS; | |
422 | goto again; | |
423 | case 'l': | |
424 | if (flags & LONG) { | |
425 | flags &= ~LONG; | |
426 | flags |= QUAD; | |
427 | } else { | |
428 | flags |= LONG; | |
429 | } | |
430 | goto again; | |
431 | case 'q': | |
432 | flags |= QUAD; | |
433 | goto again; | |
434 | case 'h': | |
435 | if (flags & SHORT) { | |
436 | flags &= ~SHORT; | |
437 | flags |= SHORTSHORT; | |
438 | } else { | |
439 | flags |= SHORT; | |
440 | } | |
441 | goto again; | |
442 | ||
443 | case '0': case '1': case '2': case '3': case '4': | |
444 | case '5': case '6': case '7': case '8': case '9': | |
445 | width = width * 10 + c - '0'; | |
446 | goto again; | |
447 | ||
448 | /* | |
449 | * Conversions. | |
450 | * | |
451 | */ | |
452 | case 'd': | |
453 | c = CT_INT; | |
454 | ccfn = (ccfntype)strtoq; | |
455 | base = 10; | |
456 | break; | |
457 | ||
458 | case 'i': | |
459 | c = CT_INT; | |
460 | ccfn = (ccfntype)strtoq; | |
461 | base = 0; | |
462 | break; | |
463 | ||
464 | case 'o': | |
465 | c = CT_INT; | |
466 | ccfn = strtouq; | |
467 | base = 8; | |
468 | break; | |
469 | ||
470 | case 'u': | |
471 | c = CT_INT; | |
472 | ccfn = strtouq; | |
473 | base = 10; | |
474 | break; | |
475 | ||
476 | case 'x': | |
477 | flags |= PFXOK; /* enable 0x prefixing */ | |
478 | c = CT_INT; | |
479 | ccfn = strtouq; | |
480 | base = 16; | |
481 | break; | |
482 | ||
483 | case 's': | |
484 | c = CT_STRING; | |
485 | break; | |
486 | ||
487 | case '[': | |
488 | fmt = __sccl(ccltab, fmt); | |
489 | flags |= NOSKIP; | |
490 | c = CT_CCL; | |
491 | break; | |
492 | ||
493 | case 'c': | |
494 | flags |= NOSKIP; | |
495 | c = CT_CHAR; | |
496 | break; | |
497 | ||
498 | case 'p': /* pointer format is like hex */ | |
499 | flags |= POINTER | PFXOK; | |
500 | c = CT_INT; | |
501 | ccfn = strtouq; | |
502 | base = 16; | |
503 | break; | |
504 | ||
505 | case 'n': | |
506 | nconversions++; | |
507 | if (flags & SUPPRESS) /* ??? */ | |
508 | continue; | |
509 | if (flags & SHORTSHORT) | |
510 | *va_arg(ap, char *) = nread; | |
511 | else if (flags & SHORT) | |
512 | *va_arg(ap, short *) = nread; | |
513 | else if (flags & LONG) | |
514 | *va_arg(ap, long *) = nread; | |
515 | else if (flags & QUAD) | |
516 | *va_arg(ap, s64 *) = nread; | |
517 | else | |
518 | *va_arg(ap, int *) = nread; | |
519 | continue; | |
520 | } | |
521 | ||
522 | /* | |
523 | * We have a conversion that requires input. | |
524 | */ | |
525 | if (inr <= 0) | |
526 | goto input_failure; | |
527 | ||
528 | /* | |
529 | * Consume leading white space, except for formats | |
530 | * that suppress this. | |
531 | */ | |
532 | if ((flags & NOSKIP) == 0) { | |
533 | while (isspace(*inp)) { | |
534 | nread++; | |
535 | if (--inr > 0) | |
536 | inp++; | |
537 | else | |
538 | goto input_failure; | |
539 | } | |
540 | /* | |
541 | * Note that there is at least one character in | |
542 | * the buffer, so conversions that do not set NOSKIP | |
543 | * can no longer result in an input failure. | |
544 | */ | |
545 | } | |
546 | ||
547 | /* | |
548 | * Do the conversion. | |
549 | */ | |
550 | switch (c) { | |
551 | case CT_CHAR: | |
552 | /* scan arbitrary characters (sets NOSKIP) */ | |
553 | if (width == 0) | |
554 | width = 1; | |
555 | if (flags & SUPPRESS) { | |
556 | size_t sum = 0; | |
557 | ||
3337b291 AL |
558 | n = inr; |
559 | if (n < width) { | |
e87dfb05 AA |
560 | sum += n; |
561 | width -= n; | |
562 | inp += n; | |
563 | if (sum == 0) | |
564 | goto input_failure; | |
565 | } else { | |
566 | sum += width; | |
567 | inr -= width; | |
568 | inp += width; | |
569 | } | |
570 | nread += sum; | |
571 | } else { | |
572 | memcpy(va_arg(ap, char *), inp, width); | |
573 | inr -= width; | |
574 | inp += width; | |
575 | nread += width; | |
576 | nassigned++; | |
577 | } | |
578 | nconversions++; | |
579 | break; | |
580 | ||
581 | case CT_CCL: | |
582 | /* scan a (nonempty) character class (sets NOSKIP) */ | |
583 | if (width == 0) | |
584 | width = (size_t)~0; /* `infinity' */ | |
585 | /* take only those things in the class */ | |
586 | if (flags & SUPPRESS) { | |
587 | n = 0; | |
588 | while (ccltab[(unsigned char)*inp]) { | |
589 | n++, inr--, inp++; | |
590 | if (--width == 0) | |
591 | break; | |
592 | if (inr <= 0) { | |
593 | if (n == 0) | |
594 | goto input_failure; | |
595 | break; | |
596 | } | |
597 | } | |
598 | if (n == 0) | |
599 | goto match_failure; | |
600 | } else { | |
601 | p = va_arg(ap, char *); | |
602 | p0 = p; | |
603 | while (ccltab[(unsigned char)*inp]) { | |
604 | inr--; | |
605 | *p++ = *inp++; | |
606 | if (--width == 0) | |
607 | break; | |
608 | if (inr <= 0) { | |
609 | if (p == p0) | |
610 | goto input_failure; | |
611 | break; | |
612 | } | |
613 | } | |
614 | n = p - p0; | |
615 | if (n == 0) | |
616 | goto match_failure; | |
617 | *p = 0; | |
618 | nassigned++; | |
619 | } | |
620 | nread += n; | |
621 | nconversions++; | |
622 | break; | |
623 | ||
624 | case CT_STRING: | |
625 | /* like CCL, but zero-length string OK, & no NOSKIP */ | |
626 | if (width == 0) | |
627 | width = (size_t)~0; | |
628 | if (flags & SUPPRESS) { | |
629 | n = 0; | |
630 | while (!isspace(*inp)) { | |
631 | n++, inr--, inp++; | |
632 | if (--width == 0) | |
633 | break; | |
634 | if (inr <= 0) | |
635 | break; | |
636 | } | |
637 | nread += n; | |
638 | } else { | |
639 | p = va_arg(ap, char *); | |
640 | p0 = p; | |
641 | while (!isspace(*inp)) { | |
642 | inr--; | |
643 | *p++ = *inp++; | |
644 | if (--width == 0) | |
645 | break; | |
646 | if (inr <= 0) | |
647 | break; | |
648 | } | |
649 | *p = 0; | |
650 | nread += p - p0; | |
651 | nassigned++; | |
652 | } | |
653 | nconversions++; | |
654 | continue; | |
655 | ||
656 | case CT_INT: | |
657 | /* scan an integer as if by strtoq/strtouq */ | |
658 | #ifdef hardway | |
659 | if (width == 0 || width > sizeof(buf) - 1) | |
660 | width = sizeof(buf) - 1; | |
661 | #else | |
662 | /* size_t is unsigned, hence this optimisation */ | |
663 | if (--width > sizeof(buf) - 2) | |
664 | width = sizeof(buf) - 2; | |
665 | width++; | |
666 | #endif | |
667 | flags |= SIGNOK | NDIGITS | NZDIGITS; | |
668 | for (p = buf; width; width--) { | |
669 | c = *inp; | |
670 | /* | |
671 | * Switch on the character; `goto ok' | |
672 | * if we accept it as a part of number. | |
673 | */ | |
674 | switch (c) { | |
675 | /* | |
676 | * The digit 0 is always legal, but is | |
677 | * special. For %i conversions, if no | |
678 | * digits (zero or nonzero) have been | |
679 | * scanned (only signs), we will have | |
680 | * base==0. In that case, we should set | |
681 | * it to 8 and enable 0x prefixing. | |
682 | * Also, if we have not scanned zero digits | |
683 | * before this, do not turn off prefixing | |
684 | * (someone else will turn it off if we | |
685 | * have scanned any nonzero digits). | |
686 | */ | |
687 | case '0': | |
688 | if (base == 0) { | |
689 | base = 8; | |
690 | flags |= PFXOK; | |
691 | } | |
692 | if (flags & NZDIGITS) | |
693 | flags &= ~(SIGNOK | NZDIGITS | NDIGITS); | |
694 | else | |
695 | flags &= ~(SIGNOK | PFXOK | NDIGITS); | |
696 | goto ok; | |
697 | ||
698 | /* 1 through 7 always legal */ | |
699 | case '1': case '2': case '3': | |
700 | case '4': case '5': case '6': case '7': | |
701 | base = basefix[base]; | |
702 | flags &= ~(SIGNOK | PFXOK | NDIGITS); | |
703 | goto ok; | |
704 | ||
705 | /* digits 8 and 9 ok iff decimal or hex */ | |
706 | case '8': case '9': | |
707 | base = basefix[base]; | |
708 | if (base <= 8) | |
709 | break; /* not legal here */ | |
710 | flags &= ~(SIGNOK | PFXOK | NDIGITS); | |
711 | goto ok; | |
712 | ||
713 | /* letters ok iff hex */ | |
714 | case 'A': case 'B': case 'C': | |
715 | case 'D': case 'E': case 'F': | |
716 | case 'a': case 'b': case 'c': | |
717 | case 'd': case 'e': case 'f': | |
718 | /* no need to fix base here */ | |
719 | if (base <= 10) | |
720 | break; /* not legal here */ | |
721 | flags &= ~(SIGNOK | PFXOK | NDIGITS); | |
722 | goto ok; | |
723 | ||
724 | /* sign ok only as first character */ | |
725 | case '+': case '-': | |
726 | if (flags & SIGNOK) { | |
727 | flags &= ~SIGNOK; | |
728 | goto ok; | |
729 | } | |
730 | break; | |
731 | ||
732 | /* x ok iff flag still set & 2nd char */ | |
733 | case 'x': case 'X': | |
734 | if (flags & PFXOK && p == buf + 1) { | |
735 | base = 16; /* if %i */ | |
736 | flags &= ~PFXOK; | |
737 | goto ok; | |
738 | } | |
739 | break; | |
740 | } | |
741 | ||
742 | /* | |
743 | * If we got here, c is not a legal character | |
744 | * for a number. Stop accumulating digits. | |
745 | */ | |
746 | break; | |
747 | ok: | |
748 | /* | |
749 | * c is legal: store it and look at the next. | |
750 | */ | |
751 | *p++ = c; | |
752 | if (--inr > 0) | |
753 | inp++; | |
754 | else | |
755 | break; /* end of input */ | |
756 | } | |
757 | /* | |
758 | * If we had only a sign, it is no good; push | |
759 | * back the sign. If the number ends in `x', | |
760 | * it was [sign] '' 'x', so push back the x | |
761 | * and treat it as [sign] ''. | |
762 | */ | |
763 | if (flags & NDIGITS) { | |
764 | if (p > buf) { | |
765 | inp--; | |
766 | inr++; | |
767 | } | |
768 | goto match_failure; | |
769 | } | |
770 | c = ((u_char *)p)[-1]; | |
771 | if (c == 'x' || c == 'X') { | |
772 | --p; | |
773 | inp--; | |
774 | inr++; | |
775 | } | |
776 | if ((flags & SUPPRESS) == 0) { | |
777 | u64 res; | |
778 | ||
779 | *p = 0; | |
780 | res = (*ccfn)(buf, (char **)NULL, base); | |
781 | if (flags & POINTER) | |
782 | *va_arg(ap, void **) = | |
783 | (void *)(uintptr_t)res; | |
784 | else if (flags & SHORTSHORT) | |
785 | *va_arg(ap, char *) = res; | |
786 | else if (flags & SHORT) | |
787 | *va_arg(ap, short *) = res; | |
788 | else if (flags & LONG) | |
789 | *va_arg(ap, long *) = res; | |
790 | else if (flags & QUAD) | |
791 | *va_arg(ap, s64 *) = res; | |
792 | else | |
793 | *va_arg(ap, int *) = res; | |
794 | nassigned++; | |
795 | } | |
796 | nread += p - buf; | |
797 | nconversions++; | |
798 | break; | |
799 | } | |
800 | } | |
801 | input_failure: | |
802 | return (nconversions != 0 ? nassigned : -1); | |
803 | match_failure: | |
804 | return (nassigned); | |
805 | } | |
806 | ||
807 | /** | |
808 | * sscanf - Unformat a buffer into a list of arguments | |
809 | * @buf: input buffer | |
810 | * @fmt: formatting of buffer | |
811 | * @...: resulting arguments | |
812 | */ | |
813 | int sscanf(const char *buf, const char *fmt, ...) | |
814 | { | |
815 | va_list args; | |
816 | int i; | |
817 | ||
818 | va_start(args, fmt); | |
819 | i = vsscanf(buf, fmt, args); | |
820 | va_end(args); | |
821 | return i; | |
822 | } | |
823 | ||
824 | #endif |