diff --git a/include/klibc/kerrno.h b/include/klibc/kerrno.h index 2f7699923f..55209d9811 100644 --- a/include/klibc/kerrno.h +++ b/include/klibc/kerrno.h @@ -12,6 +12,7 @@ #define __RT_KERRNO_H__ #include +#include #ifdef __cplusplus extern "C" { diff --git a/include/klibc/kstdio.h b/include/klibc/kstdio.h index 215e9a9245..3eee8fa5b5 100644 --- a/include/klibc/kstdio.h +++ b/include/klibc/kstdio.h @@ -11,6 +11,9 @@ #ifndef __RT_KSTDIO_H__ #define __RT_KSTDIO_H__ +#include +#include + #ifdef __cplusplus extern "C" { #endif @@ -19,6 +22,8 @@ int rt_vsprintf(char *dest, const char *format, va_list arg_ptr); int rt_vsnprintf(char *buf, rt_size_t size, const char *fmt, va_list args); int rt_sprintf(char *buf, const char *format, ...); int rt_snprintf(char *buf, rt_size_t size, const char *format, ...); +int rt_vsscanf(const char *buffer, const char *format, va_list ap); +int rt_sscanf(const char *str, const char *format, ...); #ifdef __cplusplus } diff --git a/include/klibc/kstring.h b/include/klibc/kstring.h index 48fbe60091..fc6c3bfa7d 100644 --- a/include/klibc/kstring.h +++ b/include/klibc/kstring.h @@ -11,6 +11,8 @@ #ifndef __RT_KSTRING_H__ #define __RT_KSTRING_H__ +#include + #ifdef __cplusplus extern "C" { #endif @@ -19,6 +21,7 @@ void *rt_memset(void *src, int c, rt_ubase_t n); void *rt_memcpy(void *dest, const void *src, rt_ubase_t n); void *rt_memmove(void *dest, const void *src, rt_size_t n); rt_int32_t rt_memcmp(const void *cs, const void *ct, rt_size_t count); + char *rt_strdup(const char *s); rt_size_t rt_strnlen(const char *s, rt_ubase_t maxlen); char *rt_strstr(const char *str1, const char *str2); diff --git a/src/SConscript b/src/SConscript index 08a7d81e78..8407e6ef18 100644 --- a/src/SConscript +++ b/src/SConscript @@ -2,7 +2,6 @@ from building import * import os src = Glob('*.c') -src += Glob('klibc/*.c') cwd = GetCurrentDir() inc = [os.path.join(cwd, '..', 'include')] @@ -49,4 +48,9 @@ group = DefineGroup('Kernel', src, depend=[''], CPPPATH=inc, LINKFLAGS=LINKFLAGS, LOCAL_CFLAGS=LOCAL_CFLAGS, CPPDEFINES=['__RTTHREAD__'], LOCAL_CPPDEFINES=['__RT_KERNEL_SOURCE__']) +list = os.listdir(cwd) +for item in list: + if os.path.isfile(os.path.join(cwd, item, 'SConscript')): + group = group + SConscript(os.path.join(item, 'SConscript')) + Return('group') diff --git a/src/klibc/SConscript b/src/klibc/SConscript new file mode 100644 index 0000000000..4a17403c20 --- /dev/null +++ b/src/klibc/SConscript @@ -0,0 +1,14 @@ +from building import * +import os + +cwd = GetCurrentDir() +src = Glob('*.c') + +group = DefineGroup('klibc', src, depend = ['']) + +list = os.listdir(cwd) +for item in list: + if os.path.isfile(os.path.join(cwd, item, 'SConscript')): + group = group + SConscript(os.path.join(item, 'SConscript')) + +Return('group') diff --git a/src/klibc/kstdio.c b/src/klibc/kstdio.c index 0da07ec0bc..ceb891f325 100644 --- a/src/klibc/kstdio.c +++ b/src/klibc/kstdio.c @@ -72,3 +72,25 @@ int rt_sprintf(char *buf, const char *format, ...) return n; } RTM_EXPORT(rt_sprintf); + +/** + * @brief This function parses a formatted string from the input string. + * + * @param str the input string to be parsed. + * + * @param format the format string that specifies how to interpret the input. + * + * @return The number of input items successfully matched and assigned. + */ +int rt_sscanf(const char *str, const char *format, ...) +{ + va_list ap; + int rv; + + va_start(ap, format); + rv = rt_vsscanf(str, format, ap); + va_end(ap); + + return rv; +} +RTM_EXPORT(rt_sscanf); diff --git a/src/klibc/rt_vsnprintf_std.c b/src/klibc/rt_vsnprintf_std.c index 3d919d522e..520061d277 100644 --- a/src/klibc/rt_vsnprintf_std.c +++ b/src/klibc/rt_vsnprintf_std.c @@ -6,7 +6,7 @@ * Change Logs: * Date Author Notes * 2021-11-27 Meco Man porting for rt_vsnprintf as the fully functional version - * 2024-11-19 Meco Man move into rtklibc + * 2024-11-19 Meco Man move to klibc */ /** diff --git a/src/klibc/rt_vsscanf.c b/src/klibc/rt_vsscanf.c new file mode 100644 index 0000000000..25c25c20a1 --- /dev/null +++ b/src/klibc/rt_vsscanf.c @@ -0,0 +1,502 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-11-24 Meco Man port to klibc + */ + +#include + +/* + * Copyright (c) 2012 Petteri Aimonen + * 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. + */ + +#include /* for isspace */ +#include +#include /* for CHAR_BIT */ + +static inline int digitval(int ch) +{ + 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; + } +} + +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; + + while (n && isspace((unsigned char)*nptr)) { + nptr++; + n--; + } + + /* Single optional + or - */ + if (n) { + char c = *nptr; + if (c == '-' || c == '+') { + minus = (c == '-'); + nptr++; + 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; + } + } + + while (n && (d = digitval(*nptr)) >= 0 && d < base) { + v = v * base + d; + n--; + nptr++; + } + + if (endptr) + *endptr = (char *)nptr; + + return minus ? -v : v; +} + +#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; +} + +static inline void _set_bit(unsigned long *bitmap, unsigned int bit) +{ + bitmap[bit / LONG_BIT] |= 1UL << (bit % LONG_BIT); +} + +static inline int _test_bit(unsigned long *bitmap, unsigned int bit) +{ + return (int)(bitmap[bit / LONG_BIT] >> (bit % LONG_BIT)) & 1; +} + +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 */ + } + 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; + } + break; + + case st_width: + if (ch >= '0' && ch <= '9') { + width = width * 10 + (ch - '0'); + } else { + state = st_modifiers; + p--; /* Process this character again */ + } + 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 */ + 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; + + case 'i': /* Base-independent integer */ + base = 0; + sign = 1; + goto scan_int; + + case 'd': /* Decimal integer */ + base = 10; + sign = 1; + goto scan_int; + + case 'o': /* Octal integer */ + base = 8; + sign = 0; + goto scan_int; + + case 'u': /* Unsigned decimal integer */ + base = 10; + sign = 0; + goto scan_int; + + case 'x': /* Hexadecimal integer */ + case 'X': + base = 16; + sign = 0; + goto scan_int; + + case 'n': /* # of characters consumed */ + val = (q - buffer); + goto set_integer; + + scan_int: + q = skipspace(q); + if (!*q) { + bail = bail_eof; + break; + } + val = + _strntoumax(q, (char **)&qq, base, + width); + if (qq == q) { + bail = bail_err; + 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; + break; + case rank_short: + *va_arg(ap, + unsigned short + *) = val; + break; + case rank_int: + *va_arg(ap, + unsigned int *) + = val; + break; + case rank_long: + *va_arg(ap, + unsigned long *) + = val; + break; + case rank_longlong: + *va_arg(ap, + unsigned long + long *) = val; + break; + case rank_ptr: + *va_arg(ap, void **) = + (void *) + (uintptr_t)val; + break; + } + } + 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; + } + } + } else { + sarg = va_arg(ap, char *); + while (width--) { + if (!*q) { + bail = bail_eof; + break; + } + *sarg++ = *q++; + } + if (!bail) + converted++; + } + 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 (!uc) + bail = bail_eof; + break; + + 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; + + case '%': /* %% sequence */ + if (*q == '%') + q++; + else + bail = bail_err; + break; + + default: /* Anything else */ + /* Unknown sequence */ + bail = bail_err; + break; + } + } + 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; + + 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 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; + + 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; + } + if (!uc) + bail = bail_eof; + break; + } + } + + if (bail == bail_eof && !converted) + converted = -1; /* Return EOF (-1) */ + + return converted; +}