[klibc] add rt_sscanf
cherry-pick from: https://github.com/PetteriAimonen/Baselibc/blob/master/src/vsscanf.c BSD license
This commit is contained in:
parent
b7520e262b
commit
8b4d1c0c5d
|
@ -12,6 +12,7 @@
|
|||
#define __RT_KERRNO_H__
|
||||
|
||||
#include <rtconfig.h>
|
||||
#include <rttypes.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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')
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue