[klibc] add rt_sscanf

cherry-pick from: https://github.com/PetteriAimonen/Baselibc/blob/master/src/vsscanf.c

BSD license
This commit is contained in:
Meco Man 2024-11-24 00:57:32 -05:00 committed by Rbb666
parent b7520e262b
commit 8b4d1c0c5d
8 changed files with 553 additions and 2 deletions

View File

@ -12,6 +12,7 @@
#define __RT_KERRNO_H__
#include <rtconfig.h>
#include <rttypes.h>
#ifdef __cplusplus
extern "C" {

View File

@ -11,6 +11,9 @@
#ifndef __RT_KSTDIO_H__
#define __RT_KSTDIO_H__
#include <rttypes.h>
#include <stdarg.h>
#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
}

View File

@ -11,6 +11,8 @@
#ifndef __RT_KSTRING_H__
#define __RT_KSTRING_H__
#include <rttypes.h>
#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);

View File

@ -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')

14
src/klibc/SConscript Normal file
View File

@ -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')

View File

@ -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);

View File

@ -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
*/
/**

502
src/klibc/rt_vsscanf.c Normal file
View File

@ -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 <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.
*/
#include <ctype.h> /* for isspace */
#include <stdarg.h>
#include <limits.h> /* 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;
}