1 /*
2 * Copyright (c) 2006-2025, RT-Thread Development Team
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date Author Notes
8 * 2024-11-24 Meco Man port to klibc
9 * 2025-01-04 Meco Man using Phoenix version
10 */
11
12 /*
13 * Copyright 2017, 2022-2023 Phoenix Systems
14 * Author: Adrian Kepka, Gerard Swiderski
15 */
16
17 #include <rtthread.h>
18 #include <stdlib.h> /* for strtod */
19 #include <ctype.h> /* for isspace */
20 #include <stdarg.h> /* for va_list */
21
22 #define FORMAT_NIL_STR "(nil)"
23 #define FORMAT_NIL_STR_LEN (sizeof(FORMAT_NIL_STR) - 1)
24
25 #define LONG 0x01 /* l: long or double */
26 #define LONGDOUBLE 0x02 /* L: long double */
27 #define SHORT 0x04 /* h: short */
28 #define SUPPRESS 0x08 /* *: suppress assignment */
29 #define POINTER 0x10 /* p: void * (as hex) */
30 #define NOSKIP 0x20 /* [ or c: do not skip blanks */
31 #define LONGLONG 0x400 /* ll: long long (+ deprecated q: quad) */
32 #define PTRDIFF 0x800 /* t: ptrdiff_t */
33 #define SHORTSHORT 0x4000 /* hh: char */
34 #define UNSIGNED 0x8000 /* %[oupxX] conversions */
35
36 #define SIGNOK 0x40 /* +/- is (still) legal */
37 #define NDIGITS 0x80 /* no digits detected */
38 #define PFXOK 0x100 /* 0x prefix is (still) legal */
39 #define NZDIGITS 0x200 /* no zero digits detected */
40
41 #define CT_CHAR 0 /* %c conversion */
42 #define CT_CCL 1 /* %[...] conversion */
43 #define CT_STRING 2 /* %s conversion */
44 #define CT_INT 3 /* %[dioupxX] conversion */
45 #define CT_FLOAT 4 /* %[aefgAEFG] conversion */
46 #define CT_NONE 5 /* No conversion (ex. %n) */
47
__sccl(char * tab,const unsigned char * fmt)48 static const unsigned char *__sccl(char *tab, const unsigned char *fmt)
49 {
50 int c, n, v;
51
52 c = *fmt++;
53 if (c == '^') {
54 v = 1;
55 c = *fmt++;
56 }
57 else {
58 v = 0;
59 }
60
61 rt_memset(tab, (uint8_t)v, 256);
62
63 if (c == 0) {
64 return (fmt - 1);
65 }
66
67 v = 1 - v;
68 tab[c] = v;
69 for (;;) {
70 n = *fmt++;
71 switch (n) {
72
73 case 0:
74 return (fmt - 1);
75
76 case '-':
77 n = *fmt;
78 if ((n == ']') || (n < c)) {
79 c = '-';
80 tab[c] = v;
81 break;
82 }
83 fmt++;
84
85 do {
86 tab[++c] = v;
87 } while (c < n);
88 c = n;
89 break;
90
91 case ']':
92 return (fmt);
93
94 default:
95 c = n;
96 tab[c] = v;
97 break;
98 }
99 }
100 }
101
scanf_parse(char * ccltab,const char * inp,int * inr,char const * fmt0,va_list ap)102 static int scanf_parse(char *ccltab, const char *inp, int *inr, char const *fmt0, va_list ap)
103 {
104 const unsigned char *fmt = (const unsigned char *)fmt0;
105 int c, n, flags, nassigned, nconversions, nread, base;
106 rt_size_t width;
107 char *p, *p0;
108 char buf[32];
109
110 static const short basefix[17] = { 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
111
112 *inr = rt_strlen(inp);
113
114 nassigned = 0;
115 nconversions = 0;
116 nread = 0;
117 base = 0;
118 for (;;) {
119 int convType = CT_NONE;
120 c = *fmt++;
121 if (c == '\0') {
122 return (nassigned);
123 }
124
125 if (isspace(c) != 0) {
126 while ((*inr > 0) && (isspace((int)*inp) != 0)) {
127 nread++;
128 (*inr)--;
129 inp++;
130 }
131 continue;
132 }
133
134 if (c != '%') {
135 if (*inr <= 0) {
136 return (nconversions != 0 ? nassigned : -1);
137 }
138
139 if (*inp != c) {
140 return nassigned;
141 }
142
143 nread++;
144 (*inr)--;
145 inp++;
146 continue;
147 }
148
149 width = 0;
150 flags = 0;
151 for (;;) {
152 c = *fmt++;
153 if (c == '\0') {
154 return nassigned;
155 }
156
157 if (c == '%') {
158 if (*inr <= 0) {
159 return (nconversions != 0 ? nassigned : -1);
160 }
161
162 if (*inp != c) {
163 return nassigned;
164 }
165
166 nread++;
167 (*inr)--;
168 inp++;
169 break;
170 }
171
172 switch (c) {
173 case '*':
174 flags |= SUPPRESS;
175 continue;
176
177 case 'l':
178 if ((flags & LONG) != 0) {
179 flags &= ~LONG;
180 flags |= LONGLONG;
181 }
182 else {
183 flags |= LONG;
184 }
185 continue;
186
187 case 'L':
188 flags |= LONGDOUBLE;
189 continue;
190
191 case 'q':
192 case 'j':
193 flags |= LONGLONG;
194 continue;
195
196 case 't':
197 flags |= PTRDIFF;
198 continue;
199
200 case 'z':
201 if (sizeof(rt_size_t) == sizeof(uint64_t)) {
202 flags |= LONGLONG;
203 }
204 continue;
205
206 case 'h':
207 if ((flags & SHORT) != 0) {
208 flags &= ~SHORT;
209 flags |= SHORTSHORT;
210 }
211 else {
212 flags |= SHORT;
213 }
214 continue;
215
216 case '0':
217 case '1':
218 case '2':
219 case '3':
220 case '4':
221 case '5':
222 case '6':
223 case '7':
224 case '8':
225 case '9':
226 width = width * 10 + c - '0';
227 continue;
228 default:
229 break;
230 }
231
232 /* conversions */
233 switch (c) {
234 case 'd':
235 convType = CT_INT;
236 base = 10;
237 break;
238
239 case 'i':
240 convType = CT_INT;
241 base = 0;
242 break;
243
244 case 'o':
245 convType = CT_INT;
246 flags |= UNSIGNED;
247 base = 8;
248 break;
249
250 case 'u':
251 convType = CT_INT;
252 flags |= UNSIGNED;
253 base = 10;
254 break;
255
256 case 'X':
257 case 'x':
258 flags |= PFXOK; /* enable 0x prefixing */
259 convType = CT_INT;
260 flags |= UNSIGNED;
261 base = 16;
262 break;
263
264 case 'A':
265 case 'E':
266 case 'F':
267 case 'G':
268 case 'a':
269 case 'e':
270 case 'f':
271 case 'g':
272 convType = CT_FLOAT;
273 break;
274
275
276 case 's':
277 convType = CT_STRING;
278 break;
279
280 case '[':
281 fmt = __sccl(ccltab, fmt);
282 flags |= NOSKIP;
283 convType = CT_CCL;
284 break;
285
286 case 'c':
287 flags |= NOSKIP;
288 convType = CT_CHAR;
289 break;
290
291 case 'p':
292 flags |= POINTER | PFXOK | UNSIGNED;
293 convType = CT_INT;
294 base = 16;
295 break;
296
297 case 'n':
298 nconversions++;
299 if ((flags & SUPPRESS) != 0) {
300 break;
301 }
302 if ((flags & SHORTSHORT) != 0) {
303 *va_arg(ap, char *) = nread;
304 }
305 else if ((flags & SHORT) != 0) {
306 *va_arg(ap, short *) = nread;
307 }
308 else if ((flags & LONG) != 0) {
309 *va_arg(ap, long *) = nread;
310 }
311 else if ((flags & LONGLONG) != 0) {
312 *va_arg(ap, long long *) = nread;
313 }
314 else if ((flags & PTRDIFF) != 0) {
315 *va_arg(ap, ptrdiff_t *) = nread;
316 }
317 else {
318 *va_arg(ap, int *) = nread;
319 }
320 break;
321
322 default:
323 /* Character not a conversion specifier; end parsing */
324 return nassigned;
325 }
326
327 break;
328 }
329
330 if (convType == CT_NONE) {
331 continue;
332 }
333
334 if (*inr <= 0) {
335 return (nconversions != 0 ? nassigned : -1);
336 }
337
338 if ((flags & NOSKIP) == 0) {
339 while (isspace((int)*inp) != 0) {
340 nread++;
341 if (--(*inr) > 0) {
342 inp++;
343 }
344 else {
345 return (nconversions != 0 ? nassigned : -1);
346 }
347 }
348 }
349
350 /* do the conversion */
351 switch (convType) {
352 case CT_CHAR:
353 if (width == 0) {
354 width = 1;
355 }
356
357 if (*inr <= 0) {
358 return (nconversions != 0 ? nassigned : -1);
359 }
360
361 if (width > *inr) {
362 width = *inr;
363 }
364
365 if ((flags & SUPPRESS) == 0) {
366 rt_memcpy(va_arg(ap, char *), inp, width);
367 nassigned++;
368 }
369
370 *inr -= width;
371 inp += width;
372 nread += width;
373 nconversions++;
374 break;
375
376 case CT_CCL:
377 if (width == 0) {
378 width = (rt_size_t)~0;
379 }
380 if ((flags & SUPPRESS) != 0) {
381 n = 0;
382 while (ccltab[(unsigned char)*inp] != 0) {
383 n++;
384 (*inr)--;
385 inp++;
386 if (--width == 0) {
387 break;
388 }
389 if (*inr <= 0) {
390 if (n == 0) {
391 return (nconversions != 0 ? nassigned : -1);
392 }
393 break;
394 }
395 }
396 if (n == 0) {
397 return nassigned;
398 }
399 }
400 else {
401 p0 = p = va_arg(ap, char *);
402 while (ccltab[(unsigned char)*inp] != 0) {
403 (*inr)--;
404 *p++ = *inp++;
405 if (--width == 0) {
406 break;
407 }
408 if (*inr <= 0) {
409 if (p == p0) {
410 return (nconversions != 0 ? nassigned : -1);
411 }
412 break;
413 }
414 }
415 n = p - p0;
416 if (n == 0) {
417 return nassigned;
418 }
419 *p = 0;
420 nassigned++;
421 }
422 nread += n;
423 nconversions++;
424 break;
425
426 case CT_STRING:
427 if (width == 0) {
428 width = (rt_size_t)~0;
429 }
430 if ((flags & SUPPRESS) != 0) {
431 while (isspace((int)*inp) == 0) {
432 nread++;
433 (*inr)--;
434 inp++;
435 if (--width == 0) {
436 break;
437 }
438 if (*inr <= 0) {
439 break;
440 }
441 }
442 }
443 else {
444 p0 = p = va_arg(ap, char *);
445 while (isspace((int)*inp) == 0) {
446 (*inr)--;
447 *p++ = *inp++;
448 if (--width == 0) {
449 break;
450 }
451 if (*inr <= 0) {
452 break;
453 }
454 }
455 *p = 0;
456 nread += p - p0;
457 nassigned++;
458 }
459 nconversions++;
460 continue;
461
462 case CT_INT:
463 if (((flags & POINTER) != 0) && ((*inr) >= FORMAT_NIL_STR_LEN) && (rt_strncmp(FORMAT_NIL_STR, inp, FORMAT_NIL_STR_LEN) == 0)) {
464 *va_arg(ap, void **) = RT_NULL;
465 nassigned++;
466 nconversions++;
467 nread += FORMAT_NIL_STR_LEN;
468 inp += FORMAT_NIL_STR_LEN;
469 (*inr) -= FORMAT_NIL_STR_LEN;
470 break;
471 }
472
473 if (--width > (sizeof(buf) - 2)) {
474 width = sizeof(buf) - 2;
475 }
476 width++;
477
478 if ((flags & SUPPRESS) != 0) {
479 width = ~0;
480 }
481
482 flags |= SIGNOK | NDIGITS | NZDIGITS;
483 for (p = buf; width; width--) {
484 int ok = 0;
485 c = *inp;
486 switch (c) {
487 case '0':
488 if (base == 0) {
489 base = 8;
490 flags |= PFXOK;
491 }
492 if ((flags & NZDIGITS) != 0) {
493 flags &= ~(SIGNOK | NZDIGITS | NDIGITS);
494 }
495 else {
496 flags &= ~(SIGNOK | PFXOK | NDIGITS);
497 }
498 ok = 1;
499 break;
500
501 case '1':
502 case '2':
503 case '3':
504 case '4':
505 case '5':
506 case '6':
507 case '7':
508 base = basefix[base];
509 flags &= ~(SIGNOK | PFXOK | NDIGITS);
510 ok = 1;
511 break;
512
513 case '8':
514 case '9':
515 base = basefix[base];
516 if (base <= 8) {
517 break; /* not legal here */
518 }
519 flags &= ~(SIGNOK | PFXOK | NDIGITS);
520 ok = 1;
521 break;
522
523 case 'A':
524 case 'B':
525 case 'C':
526 case 'D':
527 case 'E':
528 case 'F':
529 case 'a':
530 case 'b':
531 case 'c':
532 case 'd':
533 case 'e':
534 case 'f':
535 if (base <= 10) {
536 break;
537 }
538 flags &= ~(SIGNOK | PFXOK | NDIGITS);
539 ok = 1;
540 break;
541
542 case '+':
543 case '-':
544 if ((flags & SIGNOK) != 0) {
545 flags &= ~SIGNOK;
546 ok = 1;
547 }
548 break;
549
550 case 'x':
551 case 'X':
552 if (((flags & PFXOK) != 0) && (p == buf + 1)) {
553 base = 16; /* if %i */
554 flags &= ~PFXOK;
555 ok = 1;
556 }
557 break;
558 }
559 if (!ok)
560 break;
561
562 if ((flags & SUPPRESS) == 0) {
563 *p++ = c;
564 }
565 if (--(*inr) > 0) {
566 inp++;
567 }
568 else {
569 break;
570 }
571 }
572 if ((flags & NDIGITS) != 0) {
573 return (nconversions != 0 ? nassigned : -1);
574 }
575
576 c = ((unsigned char *)p)[-1];
577 if ((c == 'x') || (c == 'X')) {
578 --p;
579 inp--;
580 (*inr)++;
581 }
582
583 if ((flags & SUPPRESS) == 0) {
584 uint64_t res;
585
586 *p = 0;
587 if ((flags & UNSIGNED) == 0) {
588 res = strtoll(buf, (char **)RT_NULL, base);
589 }
590 else {
591 res = strtoull(buf, (char **)RT_NULL, base);
592 }
593 if ((flags & POINTER) != 0) {
594 *va_arg(ap, void **) = (void *)(unsigned long)res;
595 }
596 else if ((flags & SHORTSHORT) != 0) {
597 *va_arg(ap, char *) = res;
598 }
599 else if ((flags & SHORT) != 0) {
600 *va_arg(ap, short *) = res;
601 }
602 else if ((flags & LONG) != 0) {
603 *va_arg(ap, long *) = res;
604 }
605 else if ((flags & LONGLONG) != 0) {
606 *va_arg(ap, long long *) = res;
607 }
608 else if ((flags & PTRDIFF) != 0) {
609 *va_arg(ap, ptrdiff_t *) = res;
610 }
611 else {
612 *va_arg(ap, int *) = res;
613 }
614 nassigned++;
615 }
616
617 nread += p - buf;
618 nconversions++;
619 break;
620
621 case CT_FLOAT: {
622 union {
623 float f;
624 double d;
625 long double ld;
626 } res;
627
628 const char *srcbuf = inp;
629 if ((width != 0) && (width < *inr)) {
630 /* TODO: handle larger widths */
631 if (width > (sizeof(buf) - 1)) {
632 return (nconversions != 0 ? nassigned : -1);
633 }
634
635 rt_memcpy(buf, inp, width);
636 buf[width] = '\0';
637 srcbuf = buf;
638 }
639
640 int is_zero;
641 if ((flags & LONGDOUBLE) != 0) {
642 res.ld = strtold(srcbuf, &p);
643 is_zero = res.ld == 0;
644 }
645 else if ((flags & LONG) != 0) {
646 res.d = strtod(srcbuf, &p);
647 is_zero = res.d == 0;
648 }
649 else {
650 res.f = strtof(srcbuf, &p);
651 is_zero = res.f == 0;
652 }
653
654 if (is_zero && (srcbuf == p)) {
655 return (nconversions != 0 ? nassigned : -1);
656 }
657
658 int consumed = p - srcbuf;
659 *inr -= consumed;
660 inp += consumed;
661 nread += consumed;
662 nconversions++;
663 if ((flags & SUPPRESS) == 0) {
664 if ((flags & LONGDOUBLE) != 0) {
665 *va_arg(ap, long double *) = res.ld;
666 }
667 else if ((flags & LONG) != 0) {
668 *va_arg(ap, double *) = res.d;
669 }
670 else {
671 *va_arg(ap, float *) = res.f;
672 }
673
674 nassigned++;
675 }
676
677 break;
678 }
679
680 default:
681 break;
682 }
683 }
684 /* never reached */
685 }
686
rt_vsscanf(const char * str,const char * format,va_list ap)687 int rt_vsscanf(const char *str, const char *format, va_list ap)
688 {
689 int ret, nremain;
690 char *ccltab = rt_malloc(256);
691
692 if (ccltab == RT_NULL) {
693 return -1;
694 }
695
696 ret = scanf_parse(ccltab, str, &nremain, format, ap);
697 rt_free(ccltab);
698
699 return ret;
700 }
701