|
@@ -1,502 +1,700 @@
|
|
|
/*
|
|
|
- * Copyright (c) 2006-2024, RT-Thread Development Team
|
|
|
+ * Copyright (c) 2006-2025, RT-Thread Development Team
|
|
|
*
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
|
*
|
|
|
* Change Logs:
|
|
|
* Date Author Notes
|
|
|
* 2024-11-24 Meco Man port to klibc
|
|
|
+ * 2025-01-04 Meco Man using Phoenix version
|
|
|
*/
|
|
|
|
|
|
-#include <rtklibc.h>
|
|
|
-
|
|
|
/*
|
|
|
- * Copyright (c) 2012 Petteri Aimonen <jpa at blc.mail.kapsi.fi>
|
|
|
- * All rights reserved.
|
|
|
- *
|
|
|
- * Redistribution and use in source and binary forms, with or without
|
|
|
- * modification, are permitted provided that the following conditions are met:
|
|
|
- * 1. Redistributions of source code must retain the above copyright
|
|
|
- * notice, this list of conditions and the following disclaimer.
|
|
|
- * 2. Redistributions in binary form must reproduce the above copyright
|
|
|
- * notice, this list of conditions and the following disclaimer in the
|
|
|
- * documentation and/or other materials provided with the distribution.
|
|
|
- * 3. Neither the name of Kustaa Nyholm or SpareTimeLabs nor the
|
|
|
- * names of its contributors may be used to endorse or promote products
|
|
|
- * derived from this software without specific prior written permission.
|
|
|
- *
|
|
|
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
|
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
|
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
|
- * DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER BE LIABLE FOR ANY
|
|
|
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
|
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
|
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
|
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
|
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
+ * Copyright 2017, 2022-2023 Phoenix Systems
|
|
|
+ * Author: Adrian Kepka, Gerard Swiderski
|
|
|
*/
|
|
|
|
|
|
+#include <rtthread.h>
|
|
|
+#include <stdlib.h> /* for strtod */
|
|
|
#include <ctype.h> /* for isspace */
|
|
|
-#include <stdarg.h>
|
|
|
-#include <limits.h> /* for CHAR_BIT */
|
|
|
-
|
|
|
-static inline int digitval(int ch)
|
|
|
+#include <stdarg.h> /* for va_list */
|
|
|
+
|
|
|
+#define FORMAT_NIL_STR "(nil)"
|
|
|
+#define FORMAT_NIL_STR_LEN (sizeof(FORMAT_NIL_STR) - 1)
|
|
|
+
|
|
|
+#define LONG 0x01 /* l: long or double */
|
|
|
+#define LONGDOUBLE 0x02 /* L: long double */
|
|
|
+#define SHORT 0x04 /* h: short */
|
|
|
+#define SUPPRESS 0x08 /* *: suppress assignment */
|
|
|
+#define POINTER 0x10 /* p: void * (as hex) */
|
|
|
+#define NOSKIP 0x20 /* [ or c: do not skip blanks */
|
|
|
+#define LONGLONG 0x400 /* ll: long long (+ deprecated q: quad) */
|
|
|
+#define PTRDIFF 0x800 /* t: ptrdiff_t */
|
|
|
+#define SHORTSHORT 0x4000 /* hh: char */
|
|
|
+#define UNSIGNED 0x8000 /* %[oupxX] conversions */
|
|
|
+
|
|
|
+#define SIGNOK 0x40 /* +/- is (still) legal */
|
|
|
+#define NDIGITS 0x80 /* no digits detected */
|
|
|
+#define PFXOK 0x100 /* 0x prefix is (still) legal */
|
|
|
+#define NZDIGITS 0x200 /* no zero digits detected */
|
|
|
+
|
|
|
+#define CT_CHAR 0 /* %c conversion */
|
|
|
+#define CT_CCL 1 /* %[...] conversion */
|
|
|
+#define CT_STRING 2 /* %s conversion */
|
|
|
+#define CT_INT 3 /* %[dioupxX] conversion */
|
|
|
+#define CT_FLOAT 4 /* %[aefgAEFG] conversion */
|
|
|
+#define CT_NONE 5 /* No conversion (ex. %n) */
|
|
|
+
|
|
|
+static const unsigned char *__sccl(char *tab, const unsigned char *fmt)
|
|
|
{
|
|
|
- if (ch >= '0' && ch <= '9') {
|
|
|
- return ch - '0';
|
|
|
- } else if (ch >= 'A' && ch <= 'Z') {
|
|
|
- return ch - 'A' + 10;
|
|
|
- } else if (ch >= 'a' && ch <= 'z') {
|
|
|
- return ch - 'a' + 10;
|
|
|
- } else {
|
|
|
- return -1;
|
|
|
+ int c, n, v;
|
|
|
+
|
|
|
+ c = *fmt++;
|
|
|
+ if (c == '^') {
|
|
|
+ v = 1;
|
|
|
+ c = *fmt++;
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ v = 0;
|
|
|
}
|
|
|
-}
|
|
|
|
|
|
-static rt_size_t _strntoumax(const char *nptr, char **endptr, int base, rt_size_t n)
|
|
|
-{
|
|
|
- int minus = 0;
|
|
|
- rt_size_t v = 0;
|
|
|
- int d;
|
|
|
+ rt_memset(tab, (uint8_t)v, 256);
|
|
|
|
|
|
- while (n && isspace((unsigned char)*nptr)) {
|
|
|
- nptr++;
|
|
|
- n--;
|
|
|
+ if (c == 0) {
|
|
|
+ return (fmt - 1);
|
|
|
}
|
|
|
|
|
|
- /* Single optional + or - */
|
|
|
- if (n) {
|
|
|
- char c = *nptr;
|
|
|
- if (c == '-' || c == '+') {
|
|
|
- minus = (c == '-');
|
|
|
- nptr++;
|
|
|
- n--;
|
|
|
- }
|
|
|
- }
|
|
|
+ v = 1 - v;
|
|
|
+ tab[c] = v;
|
|
|
+ for (;;) {
|
|
|
+ n = *fmt++;
|
|
|
+ switch (n) {
|
|
|
|
|
|
- if (base == 0) {
|
|
|
- if (n >= 2 && nptr[0] == '0' &&
|
|
|
- (nptr[1] == 'x' || nptr[1] == 'X')) {
|
|
|
- n -= 2;
|
|
|
- nptr += 2;
|
|
|
- base = 16;
|
|
|
- } else if (n >= 1 && nptr[0] == '0') {
|
|
|
- n--;
|
|
|
- nptr++;
|
|
|
- base = 8;
|
|
|
- } else {
|
|
|
- base = 10;
|
|
|
- }
|
|
|
- } else if (base == 16) {
|
|
|
- if (n >= 2 && nptr[0] == '0' &&
|
|
|
- (nptr[1] == 'x' || nptr[1] == 'X')) {
|
|
|
- n -= 2;
|
|
|
- nptr += 2;
|
|
|
- }
|
|
|
- }
|
|
|
+ case 0:
|
|
|
+ return (fmt - 1);
|
|
|
|
|
|
- while (n && (d = digitval(*nptr)) >= 0 && d < base) {
|
|
|
- v = v * base + d;
|
|
|
- n--;
|
|
|
- nptr++;
|
|
|
- }
|
|
|
+ case '-':
|
|
|
+ n = *fmt;
|
|
|
+ if ((n == ']') || (n < c)) {
|
|
|
+ c = '-';
|
|
|
+ tab[c] = v;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ fmt++;
|
|
|
|
|
|
- if (endptr)
|
|
|
- *endptr = (char *)nptr;
|
|
|
+ do {
|
|
|
+ tab[++c] = v;
|
|
|
+ } while (c < n);
|
|
|
+ c = n;
|
|
|
+ break;
|
|
|
|
|
|
- return minus ? -v : v;
|
|
|
-}
|
|
|
+ case ']':
|
|
|
+ return (fmt);
|
|
|
|
|
|
-#ifndef LONG_BIT
|
|
|
-#define LONG_BIT (CHAR_BIT*sizeof(long))
|
|
|
-#endif
|
|
|
-
|
|
|
-enum flags {
|
|
|
- FL_SPLAT = 0x01, /* Drop the value, do not assign */
|
|
|
- FL_INV = 0x02, /* Character-set with inverse */
|
|
|
- FL_WIDTH = 0x04, /* Field width specified */
|
|
|
- FL_MINUS = 0x08, /* Negative number */
|
|
|
-};
|
|
|
-
|
|
|
-enum ranks {
|
|
|
- rank_char = -2,
|
|
|
- rank_short = -1,
|
|
|
- rank_int = 0,
|
|
|
- rank_long = 1,
|
|
|
- rank_longlong = 2,
|
|
|
- rank_ptr = INT_MAX /* Special value used for pointers */
|
|
|
-};
|
|
|
-
|
|
|
-#define MIN_RANK rank_char
|
|
|
-#define MAX_RANK rank_longlong
|
|
|
-
|
|
|
-#define INTMAX_RANK rank_longlong
|
|
|
-#define SIZE_T_RANK rank_long
|
|
|
-#define PTRDIFF_T_RANK rank_long
|
|
|
-
|
|
|
-enum bail {
|
|
|
- bail_none = 0, /* No error condition */
|
|
|
- bail_eof, /* Hit EOF */
|
|
|
- bail_err /* Conversion mismatch */
|
|
|
-};
|
|
|
-
|
|
|
-static inline const char *skipspace(const char *p)
|
|
|
-{
|
|
|
- while (isspace((unsigned char)*p))
|
|
|
- p++;
|
|
|
- return p;
|
|
|
+ default:
|
|
|
+ c = n;
|
|
|
+ tab[c] = v;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
-static inline void _set_bit(unsigned long *bitmap, unsigned int bit)
|
|
|
+static int scanf_parse(char *ccltab, const char *inp, int *inr, char const *fmt0, va_list ap)
|
|
|
{
|
|
|
- bitmap[bit / LONG_BIT] |= 1UL << (bit % LONG_BIT);
|
|
|
-}
|
|
|
+ const unsigned char *fmt = (const unsigned char *)fmt0;
|
|
|
+ int c, n, flags, nassigned, nconversions, nread, base;
|
|
|
+ rt_size_t width;
|
|
|
+ char *p, *p0;
|
|
|
+ char buf[32];
|
|
|
+
|
|
|
+ static const short basefix[17] = { 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
|
|
|
+
|
|
|
+ *inr = rt_strlen(inp);
|
|
|
+
|
|
|
+ nassigned = 0;
|
|
|
+ nconversions = 0;
|
|
|
+ nread = 0;
|
|
|
+ base = 0;
|
|
|
+ for (;;) {
|
|
|
+ int convType = CT_NONE;
|
|
|
+ c = *fmt++;
|
|
|
+ if (c == '\0') {
|
|
|
+ return (nassigned);
|
|
|
+ }
|
|
|
|
|
|
-static inline int _test_bit(unsigned long *bitmap, unsigned int bit)
|
|
|
-{
|
|
|
- return (int)(bitmap[bit / LONG_BIT] >> (bit % LONG_BIT)) & 1;
|
|
|
-}
|
|
|
+ if (isspace(c) != 0) {
|
|
|
+ while ((*inr > 0) && (isspace((int)*inp) != 0)) {
|
|
|
+ nread++;
|
|
|
+ (*inr)--;
|
|
|
+ inp++;
|
|
|
+ }
|
|
|
+ continue;
|
|
|
+ }
|
|
|
|
|
|
-int rt_vsscanf(const char *buffer, const char *format, va_list ap)
|
|
|
-{
|
|
|
- const char *p = format;
|
|
|
- char ch;
|
|
|
- unsigned char uc;
|
|
|
- const char *q = buffer;
|
|
|
- const char *qq;
|
|
|
- rt_size_t val = 0;
|
|
|
- int rank = rank_int; /* Default rank */
|
|
|
- unsigned int width = UINT_MAX;
|
|
|
- int base;
|
|
|
- enum flags flags = 0;
|
|
|
- enum {
|
|
|
- st_normal, /* Ground state */
|
|
|
- st_flags, /* Special flags */
|
|
|
- st_width, /* Field width */
|
|
|
- st_modifiers, /* Length or conversion modifiers */
|
|
|
- st_match_init, /* Initial state of %[ sequence */
|
|
|
- st_match, /* Main state of %[ sequence */
|
|
|
- st_match_range, /* After - in a %[ sequence */
|
|
|
- } state = st_normal;
|
|
|
- char *sarg = NULL; /* %s %c or %[ string argument */
|
|
|
- enum bail bail = bail_none;
|
|
|
- int sign;
|
|
|
- int converted = 0; /* Successful conversions */
|
|
|
- unsigned long matchmap[((1 << CHAR_BIT) + (LONG_BIT - 1)) / LONG_BIT];
|
|
|
- int matchinv = 0; /* Is match map inverted? */
|
|
|
- unsigned char range_start = 0;
|
|
|
- (void)sign;
|
|
|
-
|
|
|
- while ((ch = *p++) && !bail) {
|
|
|
- switch (state) {
|
|
|
- case st_normal:
|
|
|
- if (ch == '%') {
|
|
|
- state = st_flags;
|
|
|
- flags = 0;
|
|
|
- rank = rank_int;
|
|
|
- width = UINT_MAX;
|
|
|
- } else if (isspace((unsigned char)ch)) {
|
|
|
- q = skipspace(q);
|
|
|
- } else {
|
|
|
- if (*q == ch)
|
|
|
- q++;
|
|
|
- else
|
|
|
- bail = bail_err; /* Match failure */
|
|
|
+ if (c != '%') {
|
|
|
+ if (*inr <= 0) {
|
|
|
+ return (nconversions != 0 ? nassigned : -1);
|
|
|
}
|
|
|
- break;
|
|
|
|
|
|
- case st_flags:
|
|
|
- switch (ch) {
|
|
|
- case '*':
|
|
|
- flags |= FL_SPLAT;
|
|
|
- break;
|
|
|
- case '0': /* falls-through */
|
|
|
- case '1': /* falls-through */
|
|
|
- case '2': /* falls-through */
|
|
|
- case '3': /* falls-through */
|
|
|
- case '4': /* falls-through */
|
|
|
- case '5': /* falls-through */
|
|
|
- case '6': /* falls-through */
|
|
|
- case '7': /* falls-through */
|
|
|
- case '8': /* falls-through */
|
|
|
- case '9':
|
|
|
- width = (ch - '0');
|
|
|
- state = st_width;
|
|
|
- flags |= FL_WIDTH;
|
|
|
- break;
|
|
|
- default:
|
|
|
- state = st_modifiers;
|
|
|
- p--; /* Process this character again */
|
|
|
- break;
|
|
|
+ if (*inp != c) {
|
|
|
+ return nassigned;
|
|
|
}
|
|
|
- break;
|
|
|
|
|
|
- case st_width:
|
|
|
- if (ch >= '0' && ch <= '9') {
|
|
|
- width = width * 10 + (ch - '0');
|
|
|
- } else {
|
|
|
- state = st_modifiers;
|
|
|
- p--; /* Process this character again */
|
|
|
+ nread++;
|
|
|
+ (*inr)--;
|
|
|
+ inp++;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ width = 0;
|
|
|
+ flags = 0;
|
|
|
+ for (;;) {
|
|
|
+ c = *fmt++;
|
|
|
+ if (c == '\0') {
|
|
|
+ return nassigned;
|
|
|
}
|
|
|
- break;
|
|
|
|
|
|
- case st_modifiers:
|
|
|
- switch (ch) {
|
|
|
- /* Length modifiers - nonterminal sequences */
|
|
|
- case 'h':
|
|
|
- rank--; /* Shorter rank */
|
|
|
- break;
|
|
|
- case 'l':
|
|
|
- rank++; /* Longer rank */
|
|
|
- break;
|
|
|
- case 'j':
|
|
|
- rank = INTMAX_RANK;
|
|
|
- break;
|
|
|
- case 'z':
|
|
|
- rank = SIZE_T_RANK;
|
|
|
- break;
|
|
|
- case 't':
|
|
|
- rank = PTRDIFF_T_RANK;
|
|
|
- break;
|
|
|
- case 'L':
|
|
|
- case 'q':
|
|
|
- rank = rank_longlong; /* long double/long long */
|
|
|
+ if (c == '%') {
|
|
|
+ if (*inr <= 0) {
|
|
|
+ return (nconversions != 0 ? nassigned : -1);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (*inp != c) {
|
|
|
+ return nassigned;
|
|
|
+ }
|
|
|
+
|
|
|
+ nread++;
|
|
|
+ (*inr)--;
|
|
|
+ inp++;
|
|
|
break;
|
|
|
+ }
|
|
|
|
|
|
- default:
|
|
|
- /* Output modifiers - terminal sequences */
|
|
|
- /* Next state will be normal */
|
|
|
- state = st_normal;
|
|
|
-
|
|
|
- /* Canonicalize rank */
|
|
|
- if (rank < MIN_RANK)
|
|
|
- rank = MIN_RANK;
|
|
|
- else if (rank > MAX_RANK)
|
|
|
- rank = MAX_RANK;
|
|
|
-
|
|
|
- switch (ch) {
|
|
|
- case 'P': /* Upper case pointer */
|
|
|
- case 'p': /* Pointer */
|
|
|
- rank = rank_ptr;
|
|
|
- base = 0;
|
|
|
- sign = 0;
|
|
|
- goto scan_int;
|
|
|
+ switch (c) {
|
|
|
+ case '*':
|
|
|
+ flags |= SUPPRESS;
|
|
|
+ continue;
|
|
|
|
|
|
- case 'i': /* Base-independent integer */
|
|
|
- base = 0;
|
|
|
- sign = 1;
|
|
|
- goto scan_int;
|
|
|
+ case 'l':
|
|
|
+ if ((flags & LONG) != 0) {
|
|
|
+ flags &= ~LONG;
|
|
|
+ flags |= LONGLONG;
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ flags |= LONG;
|
|
|
+ }
|
|
|
+ continue;
|
|
|
+
|
|
|
+ case 'L':
|
|
|
+ flags |= LONGDOUBLE;
|
|
|
+ continue;
|
|
|
|
|
|
- case 'd': /* Decimal integer */
|
|
|
+ case 'q':
|
|
|
+ case 'j':
|
|
|
+ flags |= LONGLONG;
|
|
|
+ continue;
|
|
|
+
|
|
|
+ case 't':
|
|
|
+ flags |= PTRDIFF;
|
|
|
+ continue;
|
|
|
+
|
|
|
+ case 'z':
|
|
|
+ if (sizeof(rt_size_t) == sizeof(uint64_t)) {
|
|
|
+ flags |= LONGLONG;
|
|
|
+ }
|
|
|
+ continue;
|
|
|
+
|
|
|
+ case 'h':
|
|
|
+ if ((flags & SHORT) != 0) {
|
|
|
+ flags &= ~SHORT;
|
|
|
+ flags |= SHORTSHORT;
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ flags |= SHORT;
|
|
|
+ }
|
|
|
+ continue;
|
|
|
+
|
|
|
+ case '0':
|
|
|
+ case '1':
|
|
|
+ case '2':
|
|
|
+ case '3':
|
|
|
+ case '4':
|
|
|
+ case '5':
|
|
|
+ case '6':
|
|
|
+ case '7':
|
|
|
+ case '8':
|
|
|
+ case '9':
|
|
|
+ width = width * 10 + c - '0';
|
|
|
+ continue;
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* conversions */
|
|
|
+ switch (c) {
|
|
|
+ case 'd':
|
|
|
+ convType = CT_INT;
|
|
|
base = 10;
|
|
|
- sign = 1;
|
|
|
- goto scan_int;
|
|
|
+ break;
|
|
|
|
|
|
- case 'o': /* Octal integer */
|
|
|
+ case 'i':
|
|
|
+ convType = CT_INT;
|
|
|
+ base = 0;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'o':
|
|
|
+ convType = CT_INT;
|
|
|
+ flags |= UNSIGNED;
|
|
|
base = 8;
|
|
|
- sign = 0;
|
|
|
- goto scan_int;
|
|
|
+ break;
|
|
|
|
|
|
- case 'u': /* Unsigned decimal integer */
|
|
|
+ case 'u':
|
|
|
+ convType = CT_INT;
|
|
|
+ flags |= UNSIGNED;
|
|
|
base = 10;
|
|
|
- sign = 0;
|
|
|
- goto scan_int;
|
|
|
+ break;
|
|
|
|
|
|
- case 'x': /* Hexadecimal integer */
|
|
|
case 'X':
|
|
|
+ case 'x':
|
|
|
+ flags |= PFXOK; /* enable 0x prefixing */
|
|
|
+ convType = CT_INT;
|
|
|
+ flags |= UNSIGNED;
|
|
|
base = 16;
|
|
|
- sign = 0;
|
|
|
- goto scan_int;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'A':
|
|
|
+ case 'E':
|
|
|
+ case 'F':
|
|
|
+ case 'G':
|
|
|
+ case 'a':
|
|
|
+ case 'e':
|
|
|
+ case 'f':
|
|
|
+ case 'g':
|
|
|
+ convType = CT_FLOAT;
|
|
|
+ break;
|
|
|
+
|
|
|
+
|
|
|
+ case 's':
|
|
|
+ convType = CT_STRING;
|
|
|
+ break;
|
|
|
|
|
|
- case 'n': /* # of characters consumed */
|
|
|
- val = (q - buffer);
|
|
|
- goto set_integer;
|
|
|
+ case '[':
|
|
|
+ fmt = __sccl(ccltab, fmt);
|
|
|
+ flags |= NOSKIP;
|
|
|
+ convType = CT_CCL;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'c':
|
|
|
+ flags |= NOSKIP;
|
|
|
+ convType = CT_CHAR;
|
|
|
+ break;
|
|
|
|
|
|
- scan_int:
|
|
|
- q = skipspace(q);
|
|
|
- if (!*q) {
|
|
|
- bail = bail_eof;
|
|
|
+ case 'p':
|
|
|
+ flags |= POINTER | PFXOK | UNSIGNED;
|
|
|
+ convType = CT_INT;
|
|
|
+ base = 16;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'n':
|
|
|
+ nconversions++;
|
|
|
+ if ((flags & SUPPRESS) != 0) {
|
|
|
break;
|
|
|
}
|
|
|
- val =
|
|
|
- _strntoumax(q, (char **)&qq, base,
|
|
|
- width);
|
|
|
- if (qq == q) {
|
|
|
- bail = bail_err;
|
|
|
- break;
|
|
|
+ if ((flags & SHORTSHORT) != 0) {
|
|
|
+ *va_arg(ap, char *) = nread;
|
|
|
+ }
|
|
|
+ else if ((flags & SHORT) != 0) {
|
|
|
+ *va_arg(ap, short *) = nread;
|
|
|
+ }
|
|
|
+ else if ((flags & LONG) != 0) {
|
|
|
+ *va_arg(ap, long *) = nread;
|
|
|
+ }
|
|
|
+ else if ((flags & LONGLONG) != 0) {
|
|
|
+ *va_arg(ap, long long *) = nread;
|
|
|
+ }
|
|
|
+ else if ((flags & PTRDIFF) != 0) {
|
|
|
+ *va_arg(ap, ptrdiff_t *) = nread;
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ *va_arg(ap, int *) = nread;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+
|
|
|
+ default:
|
|
|
+ /* Character not a conversion specifier; end parsing */
|
|
|
+ return nassigned;
|
|
|
+ }
|
|
|
+
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (convType == CT_NONE) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (*inr <= 0) {
|
|
|
+ return (nconversions != 0 ? nassigned : -1);
|
|
|
+ }
|
|
|
+
|
|
|
+ if ((flags & NOSKIP) == 0) {
|
|
|
+ while (isspace((int)*inp) != 0) {
|
|
|
+ nread++;
|
|
|
+ if (--(*inr) > 0) {
|
|
|
+ inp++;
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ return (nconversions != 0 ? nassigned : -1);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* do the conversion */
|
|
|
+ switch (convType) {
|
|
|
+ case CT_CHAR:
|
|
|
+ if (width == 0) {
|
|
|
+ width = 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (*inr <= 0) {
|
|
|
+ return (nconversions != 0 ? nassigned : -1);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (width > *inr) {
|
|
|
+ width = *inr;
|
|
|
+ }
|
|
|
+
|
|
|
+ if ((flags & SUPPRESS) == 0) {
|
|
|
+ rt_memcpy(va_arg(ap, char *), inp, width);
|
|
|
+ nassigned++;
|
|
|
+ }
|
|
|
+
|
|
|
+ *inr -= width;
|
|
|
+ inp += width;
|
|
|
+ nread += width;
|
|
|
+ nconversions++;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case CT_CCL:
|
|
|
+ if (width == 0) {
|
|
|
+ width = (rt_size_t)~0;
|
|
|
+ }
|
|
|
+ if ((flags & SUPPRESS) != 0) {
|
|
|
+ n = 0;
|
|
|
+ while (ccltab[(unsigned char)*inp] != 0) {
|
|
|
+ n++;
|
|
|
+ (*inr)--;
|
|
|
+ inp++;
|
|
|
+ if (--width == 0) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ if (*inr <= 0) {
|
|
|
+ if (n == 0) {
|
|
|
+ return (nconversions != 0 ? nassigned : -1);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
}
|
|
|
- q = qq;
|
|
|
- if (!(flags & FL_SPLAT))
|
|
|
- converted++;
|
|
|
- /* fall through */
|
|
|
-
|
|
|
- set_integer:
|
|
|
- if (!(flags & FL_SPLAT)) {
|
|
|
- switch (rank) {
|
|
|
- case rank_char:
|
|
|
- *va_arg(ap,
|
|
|
- unsigned char *)
|
|
|
- = val;
|
|
|
+ if (n == 0) {
|
|
|
+ return nassigned;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ p0 = p = va_arg(ap, char *);
|
|
|
+ while (ccltab[(unsigned char)*inp] != 0) {
|
|
|
+ (*inr)--;
|
|
|
+ *p++ = *inp++;
|
|
|
+ if (--width == 0) {
|
|
|
break;
|
|
|
- case rank_short:
|
|
|
- *va_arg(ap,
|
|
|
- unsigned short
|
|
|
- *) = val;
|
|
|
+ }
|
|
|
+ if (*inr <= 0) {
|
|
|
+ if (p == p0) {
|
|
|
+ return (nconversions != 0 ? nassigned : -1);
|
|
|
+ }
|
|
|
break;
|
|
|
- case rank_int:
|
|
|
- *va_arg(ap,
|
|
|
- unsigned int *)
|
|
|
- = val;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ n = p - p0;
|
|
|
+ if (n == 0) {
|
|
|
+ return nassigned;
|
|
|
+ }
|
|
|
+ *p = 0;
|
|
|
+ nassigned++;
|
|
|
+ }
|
|
|
+ nread += n;
|
|
|
+ nconversions++;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case CT_STRING:
|
|
|
+ if (width == 0) {
|
|
|
+ width = (rt_size_t)~0;
|
|
|
+ }
|
|
|
+ if ((flags & SUPPRESS) != 0) {
|
|
|
+ while (isspace((int)*inp) == 0) {
|
|
|
+ nread++;
|
|
|
+ (*inr)--;
|
|
|
+ inp++;
|
|
|
+ if (--width == 0) {
|
|
|
break;
|
|
|
- case rank_long:
|
|
|
- *va_arg(ap,
|
|
|
- unsigned long *)
|
|
|
- = val;
|
|
|
+ }
|
|
|
+ if (*inr <= 0) {
|
|
|
break;
|
|
|
- case rank_longlong:
|
|
|
- *va_arg(ap,
|
|
|
- unsigned long
|
|
|
- long *) = val;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ p0 = p = va_arg(ap, char *);
|
|
|
+ while (isspace((int)*inp) == 0) {
|
|
|
+ (*inr)--;
|
|
|
+ *p++ = *inp++;
|
|
|
+ if (--width == 0) {
|
|
|
break;
|
|
|
- case rank_ptr:
|
|
|
- *va_arg(ap, void **) =
|
|
|
- (void *)
|
|
|
- (uintptr_t)val;
|
|
|
+ }
|
|
|
+ if (*inr <= 0) {
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
+ *p = 0;
|
|
|
+ nread += p - p0;
|
|
|
+ nassigned++;
|
|
|
+ }
|
|
|
+ nconversions++;
|
|
|
+ continue;
|
|
|
+
|
|
|
+ case CT_INT:
|
|
|
+ if (((flags & POINTER) != 0) && ((*inr) >= FORMAT_NIL_STR_LEN) && (rt_strncmp(FORMAT_NIL_STR, inp, FORMAT_NIL_STR_LEN) == 0)) {
|
|
|
+ *va_arg(ap, void **) = RT_NULL;
|
|
|
+ nassigned++;
|
|
|
+ nconversions++;
|
|
|
+ nread += FORMAT_NIL_STR_LEN;
|
|
|
+ inp += FORMAT_NIL_STR_LEN;
|
|
|
+ (*inr) -= FORMAT_NIL_STR_LEN;
|
|
|
break;
|
|
|
+ }
|
|
|
|
|
|
- case 'c': /* Character */
|
|
|
- /* Default width == 1 */
|
|
|
- width = (flags & FL_WIDTH) ? width : 1;
|
|
|
- if (flags & FL_SPLAT) {
|
|
|
- while (width--) {
|
|
|
- if (!*q) {
|
|
|
- bail = bail_eof;
|
|
|
- break;
|
|
|
+ if (--width > (sizeof(buf) - 2)) {
|
|
|
+ width = sizeof(buf) - 2;
|
|
|
+ }
|
|
|
+ width++;
|
|
|
+
|
|
|
+ if ((flags & SUPPRESS) != 0) {
|
|
|
+ width = ~0;
|
|
|
+ }
|
|
|
+
|
|
|
+ flags |= SIGNOK | NDIGITS | NZDIGITS;
|
|
|
+ for (p = buf; width; width--) {
|
|
|
+ int ok = 0;
|
|
|
+ c = *inp;
|
|
|
+ switch (c) {
|
|
|
+ case '0':
|
|
|
+ if (base == 0) {
|
|
|
+ base = 8;
|
|
|
+ flags |= PFXOK;
|
|
|
}
|
|
|
- }
|
|
|
- } else {
|
|
|
- sarg = va_arg(ap, char *);
|
|
|
- while (width--) {
|
|
|
- if (!*q) {
|
|
|
- bail = bail_eof;
|
|
|
+ if ((flags & NZDIGITS) != 0) {
|
|
|
+ flags &= ~(SIGNOK | NZDIGITS | NDIGITS);
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ flags &= ~(SIGNOK | PFXOK | NDIGITS);
|
|
|
+ }
|
|
|
+ ok = 1;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case '1':
|
|
|
+ case '2':
|
|
|
+ case '3':
|
|
|
+ case '4':
|
|
|
+ case '5':
|
|
|
+ case '6':
|
|
|
+ case '7':
|
|
|
+ base = basefix[base];
|
|
|
+ flags &= ~(SIGNOK | PFXOK | NDIGITS);
|
|
|
+ ok = 1;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case '8':
|
|
|
+ case '9':
|
|
|
+ base = basefix[base];
|
|
|
+ if (base <= 8) {
|
|
|
+ break; /* not legal here */
|
|
|
+ }
|
|
|
+ flags &= ~(SIGNOK | PFXOK | NDIGITS);
|
|
|
+ ok = 1;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'A':
|
|
|
+ case 'B':
|
|
|
+ case 'C':
|
|
|
+ case 'D':
|
|
|
+ case 'E':
|
|
|
+ case 'F':
|
|
|
+ case 'a':
|
|
|
+ case 'b':
|
|
|
+ case 'c':
|
|
|
+ case 'd':
|
|
|
+ case 'e':
|
|
|
+ case 'f':
|
|
|
+ if (base <= 10) {
|
|
|
break;
|
|
|
}
|
|
|
- *sarg++ = *q++;
|
|
|
- }
|
|
|
- if (!bail)
|
|
|
- converted++;
|
|
|
+ flags &= ~(SIGNOK | PFXOK | NDIGITS);
|
|
|
+ ok = 1;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case '+':
|
|
|
+ case '-':
|
|
|
+ if ((flags & SIGNOK) != 0) {
|
|
|
+ flags &= ~SIGNOK;
|
|
|
+ ok = 1;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'x':
|
|
|
+ case 'X':
|
|
|
+ if (((flags & PFXOK) != 0) && (p == buf + 1)) {
|
|
|
+ base = 16; /* if %i */
|
|
|
+ flags &= ~PFXOK;
|
|
|
+ ok = 1;
|
|
|
+ }
|
|
|
+ break;
|
|
|
}
|
|
|
- break;
|
|
|
+ if (!ok)
|
|
|
+ break;
|
|
|
|
|
|
- case 's': /* String */
|
|
|
- uc = 1; /* Anything nonzero */
|
|
|
- if (flags & FL_SPLAT) {
|
|
|
- while (width-- && (uc = *q) &&
|
|
|
- !isspace(uc)) {
|
|
|
- q++;
|
|
|
- }
|
|
|
- } else {
|
|
|
- char *sp;
|
|
|
- sp = sarg = va_arg(ap, char *);
|
|
|
- while (width-- && (uc = *q) &&
|
|
|
- !isspace(uc)) {
|
|
|
- *sp++ = uc;
|
|
|
- q++;
|
|
|
- }
|
|
|
- if (sarg != sp) {
|
|
|
- /* Terminate output */
|
|
|
- *sp = '\0';
|
|
|
- converted++;
|
|
|
- }
|
|
|
+ if ((flags & SUPPRESS) == 0) {
|
|
|
+ *p++ = c;
|
|
|
}
|
|
|
- if (!uc)
|
|
|
- bail = bail_eof;
|
|
|
- break;
|
|
|
+ if (--(*inr) > 0) {
|
|
|
+ inp++;
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if ((flags & NDIGITS) != 0) {
|
|
|
+ return (nconversions != 0 ? nassigned : -1);
|
|
|
+ }
|
|
|
|
|
|
- case '[': /* Character range */
|
|
|
- sarg = (flags & FL_SPLAT) ? NULL
|
|
|
- : va_arg(ap, char *);
|
|
|
- state = st_match_init;
|
|
|
- matchinv = 0;
|
|
|
- rt_memset(matchmap, 0, sizeof matchmap);
|
|
|
- break;
|
|
|
+ c = ((unsigned char *)p)[-1];
|
|
|
+ if ((c == 'x') || (c == 'X')) {
|
|
|
+ --p;
|
|
|
+ inp--;
|
|
|
+ (*inr)++;
|
|
|
+ }
|
|
|
|
|
|
- case '%': /* %% sequence */
|
|
|
- if (*q == '%')
|
|
|
- q++;
|
|
|
- else
|
|
|
- bail = bail_err;
|
|
|
- break;
|
|
|
+ if ((flags & SUPPRESS) == 0) {
|
|
|
+ uint64_t res;
|
|
|
|
|
|
- default: /* Anything else */
|
|
|
- /* Unknown sequence */
|
|
|
- bail = bail_err;
|
|
|
- break;
|
|
|
+ *p = 0;
|
|
|
+ if ((flags & UNSIGNED) == 0) {
|
|
|
+ res = strtoll(buf, (char **)RT_NULL, base);
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ res = strtoull(buf, (char **)RT_NULL, base);
|
|
|
+ }
|
|
|
+ if ((flags & POINTER) != 0) {
|
|
|
+ *va_arg(ap, void **) = (void *)(unsigned long)res;
|
|
|
+ }
|
|
|
+ else if ((flags & SHORTSHORT) != 0) {
|
|
|
+ *va_arg(ap, char *) = res;
|
|
|
+ }
|
|
|
+ else if ((flags & SHORT) != 0) {
|
|
|
+ *va_arg(ap, short *) = res;
|
|
|
+ }
|
|
|
+ else if ((flags & LONG) != 0) {
|
|
|
+ *va_arg(ap, long *) = res;
|
|
|
+ }
|
|
|
+ else if ((flags & LONGLONG) != 0) {
|
|
|
+ *va_arg(ap, long long *) = res;
|
|
|
+ }
|
|
|
+ else if ((flags & PTRDIFF) != 0) {
|
|
|
+ *va_arg(ap, ptrdiff_t *) = res;
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ *va_arg(ap, int *) = res;
|
|
|
+ }
|
|
|
+ nassigned++;
|
|
|
}
|
|
|
- }
|
|
|
- break;
|
|
|
|
|
|
- case st_match_init: /* Initial state for %[ match */
|
|
|
- if (ch == '^' && !(flags & FL_INV)) {
|
|
|
- matchinv = 1;
|
|
|
- } else {
|
|
|
- _set_bit(matchmap, (unsigned char)ch);
|
|
|
- state = st_match;
|
|
|
- }
|
|
|
- break;
|
|
|
+ nread += p - buf;
|
|
|
+ nconversions++;
|
|
|
+ break;
|
|
|
|
|
|
- case st_match: /* Main state for %[ match */
|
|
|
- if (ch == ']') {
|
|
|
- goto match_run;
|
|
|
- } else if (ch == '-') {
|
|
|
- range_start = (unsigned char)ch;
|
|
|
- state = st_match_range;
|
|
|
- } else {
|
|
|
- _set_bit(matchmap, (unsigned char)ch);
|
|
|
- }
|
|
|
- break;
|
|
|
+ case CT_FLOAT: {
|
|
|
+ union {
|
|
|
+ float f;
|
|
|
+ double d;
|
|
|
+ long double ld;
|
|
|
+ } res;
|
|
|
+
|
|
|
+ const char *srcbuf = inp;
|
|
|
+ if ((width != 0) && (width < *inr)) {
|
|
|
+ /* TODO: handle larger widths */
|
|
|
+ if (width > (sizeof(buf) - 1)) {
|
|
|
+ return (nconversions != 0 ? nassigned : -1);
|
|
|
+ }
|
|
|
|
|
|
- case st_match_range: /* %[ match after - */
|
|
|
- if (ch == ']') {
|
|
|
- /* - was last character */
|
|
|
- _set_bit(matchmap, (unsigned char)'-');
|
|
|
- goto match_run;
|
|
|
- } else {
|
|
|
- int i;
|
|
|
- for (i = range_start; i < (unsigned char)ch;
|
|
|
- i++)
|
|
|
- _set_bit(matchmap, i);
|
|
|
- state = st_match;
|
|
|
- }
|
|
|
- break;
|
|
|
+ rt_memcpy(buf, inp, width);
|
|
|
+ buf[width] = '\0';
|
|
|
+ srcbuf = buf;
|
|
|
+ }
|
|
|
|
|
|
- match_run: /* Match expression finished */
|
|
|
- qq = q;
|
|
|
- uc = 1; /* Anything nonzero */
|
|
|
- while (width && (uc = *q)
|
|
|
- && _test_bit(matchmap, uc)^matchinv) {
|
|
|
- if (sarg)
|
|
|
- *sarg++ = uc;
|
|
|
- q++;
|
|
|
- }
|
|
|
- if (q != qq && sarg) {
|
|
|
- *sarg = '\0';
|
|
|
- converted++;
|
|
|
- } else {
|
|
|
- bail = bail_err;
|
|
|
+ int is_zero;
|
|
|
+ if ((flags & LONGDOUBLE) != 0) {
|
|
|
+ res.ld = strtold(srcbuf, &p);
|
|
|
+ is_zero = res.ld == 0;
|
|
|
+ }
|
|
|
+ else if ((flags & LONG) != 0) {
|
|
|
+ res.d = strtod(srcbuf, &p);
|
|
|
+ is_zero = res.d == 0;
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ res.f = strtof(srcbuf, &p);
|
|
|
+ is_zero = res.f == 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (is_zero && (srcbuf == p)) {
|
|
|
+ return (nconversions != 0 ? nassigned : -1);
|
|
|
+ }
|
|
|
+
|
|
|
+ int consumed = p - srcbuf;
|
|
|
+ *inr -= consumed;
|
|
|
+ inp += consumed;
|
|
|
+ nread += consumed;
|
|
|
+ nconversions++;
|
|
|
+ if ((flags & SUPPRESS) == 0) {
|
|
|
+ if ((flags & LONGDOUBLE) != 0) {
|
|
|
+ *va_arg(ap, long double *) = res.ld;
|
|
|
+ }
|
|
|
+ else if ((flags & LONG) != 0) {
|
|
|
+ *va_arg(ap, double *) = res.d;
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ *va_arg(ap, float *) = res.f;
|
|
|
+ }
|
|
|
+
|
|
|
+ nassigned++;
|
|
|
+ }
|
|
|
+
|
|
|
+ break;
|
|
|
}
|
|
|
- if (!uc)
|
|
|
- bail = bail_eof;
|
|
|
- break;
|
|
|
+
|
|
|
+ default:
|
|
|
+ break;
|
|
|
}
|
|
|
}
|
|
|
+ /* never reached */
|
|
|
+}
|
|
|
+
|
|
|
+int rt_vsscanf(const char *str, const char *format, va_list ap)
|
|
|
+{
|
|
|
+ int ret, nremain;
|
|
|
+ char *ccltab = rt_malloc(256);
|
|
|
+
|
|
|
+ if (ccltab == RT_NULL) {
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
|
|
|
- if (bail == bail_eof && !converted)
|
|
|
- converted = -1; /* Return EOF (-1) */
|
|
|
+ ret = scanf_parse(ccltab, str, &nremain, format, ap);
|
|
|
+ rt_free(ccltab);
|
|
|
|
|
|
- return converted;
|
|
|
+ return ret;
|
|
|
}
|