Compare commits

...

31 Commits

Author SHA1 Message Date
Lephenixnoir 46c73cbc87
stdio: fix %N.Ms miscalculating spacing if M > strlen(arg) 2024-02-04 20:13:25 +01:00
Lephenixnoir 05ff5e246d
bump version to 1.5.0 2024-01-14 23:02:59 +01:00
Lephenixnoir 1b87af5d83
stdio: mark *scanf() as TEST 2024-01-14 22:03:59 +01:00
Lephenixnoir be4c2b8d33
stdio: fix bypassed __scanf_end at end of scanf 2024-01-14 22:02:53 +01:00
Lephenixnoir 0cef8ca891
stdio: slight cleanup and documentation 2024-01-14 21:58:03 +01:00
Lephenixnoir 177c4eea3f
stdio: more compact scanf format parsing 2024-01-14 21:34:46 +01:00
Lephenixnoir 527c2e48fc
stdio: more syntaxic refactoring of scanf 2024-01-14 21:27:48 +01:00
Lephenixnoir 9f6e0c8039
stdio: factor out format parsing in scanf 2024-01-14 21:20:40 +01:00
Lephenixnoir 1caaa8ff63
stdio: use compact storage for %[] set in scanf
256 bytes of globals is a *lot* on the G-III.
2024-01-14 20:36:09 +01:00
Lephenixnoir 5b85d53826
(formatting: case indent) 2024-01-14 20:23:55 +01:00
Lephenixnoir 55ae7df318
stdio: simplify output size management in scanf 2024-01-14 20:23:21 +01:00
Lephenixnoir 69eadb67d2
stdio: deduplicate scanf cases and remove most gotos 2024-01-14 20:07:24 +01:00
Lephenixnoir b11c059c0f
stdio: start simplifying scanf limit tracking logic
Basically removing it from the __scanf_input structure and specializing
it at format sites. The reason is that pretending it's the end of the
stream after the limit is reached does not work because we have to
return EOF at end of stream but not when the limit is hit. So we have to
handle it explicitly, and since we do, no need to have it in the
structure too.
2024-01-14 19:28:36 +01:00
Lephenixnoir 2215b3c267
stdio: make all scanf tests pass
The tests are still far from exhaustive but that's a good start.
2024-01-14 17:31:21 +01:00
Lephenixnoir b61cc096d9
stdio: fix scanf buffering so all tests pass
Code factoring and performance improvements will follow.
2024-01-14 17:31:19 +01:00
Lephenixnoir c776336a0d
stdio: fix scanf bounds breaking strto*
Mostly an initialization problem. But I also optimized the check by
making the bound a maximal unsigned integer when there is no bound,
since __scanf_peek() is used a lot.
2024-01-14 17:31:15 +01:00
Lephenixnoir d8a55b728d
stdlib: restore private headers 2024-01-14 13:48:41 +01:00
Sylvain PILLOT 09b33ca2fa
stdlib: scanf implementation by SlyVTT
Authored-By: Slyvtt <pillot.sylvain@gmail.com>
2023-05-26 21:04:37 +02:00
Lephenixnoir 031e0ccb65
bump version to 1.4.5 2023-04-01 20:30:30 +02:00
Lephenixnoir 74b805e8b5
stdlib: automatically seed PRNG with srand(1) 2023-04-01 20:30:30 +02:00
Yann MAGNIN 7eb2a6e8ab
add shared lib generation support for Vhex 2023-04-01 20:30:30 +02:00
Yann MAGNIN c85182d07e
update vxSDK integration 2023-04-01 20:30:30 +02:00
Lephenixnoir 465655674b
dso: dynamically allocate destructor table
This saves static memory on mono targets where it's scarce.
2023-04-01 20:30:30 +02:00
Lephenixnoir 1da9d10226
alloca: provide <alloca.h> 2023-04-01 20:30:30 +02:00
Lephenixnoir 7c4de3e295
dso, stdlib: __cxa_atexit(), __dso_handle, atexit() (TEST) 2023-04-01 20:30:30 +02:00
Lephenixnoir 95e33092ec
stdlib: add fileno (DONE) 2023-04-01 20:30:29 +02:00
Lephenixnoir 26e54af8e0
stdlib: scanf-friendly strto* functions 2023-04-01 20:30:29 +02:00
Lephenixnoir fda0d950ed
time: fix strftime being called strftime2 2023-04-01 20:30:29 +02:00
Lephenixnoir eb83e7442f Merge pull request 'Fix signedness issues with strcmp and strncmp' (#4) from Heath123/fxlibc:heath123-patch-1 into master
Reviewed-on: https://gitea.planet-casio.com/Vhex-Kernel-Core/fxlibc/pulls/4
2022-11-24 14:23:21 +01:00
Heath Mitchell aeeae3810d Update 'src/string/strcmp.c' 2022-11-24 13:36:02 +01:00
Heath Mitchell ee42660ea5 Update 'src/string/strncmp.c' 2022-11-24 13:30:46 +01:00
36 changed files with 970 additions and 153 deletions

1
.gitignore vendored
View File

@ -3,6 +3,7 @@
/prefix
*.txt
!CMakeLists.txt
.vxsdk/
# GiteaPC config files
giteapc-config.make

View File

@ -12,3 +12,9 @@ int rand(void)
{
return tinymt32_generate_uint32(&random) & 0x7fffffff;
}
__attribute__((constructor))
static void init_prng(void)
{
srand(1);
}

View File

@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.15)
project(FxLibc VERSION 1.4.4 LANGUAGES C ASM)
project(FxLibc VERSION 1.5.0 LANGUAGES C ASM)
set(CMAKE_INSTALL_MESSAGE LAZY)
@ -15,7 +15,9 @@ if(FXLIBC_TARGET STREQUAL vhex-sh)
list(APPEND TARGET_FOLDERS vhex sh-generic)
set(FXLIBC_ARCH sh)
add_definitions(-D__SUPPORT_VHEX_KERNEL)
set(CMAKE_INSTALL_PREFIX "${FXSDK_COMPILER_INSTALL}" CACHE PATH "..." FORCE)
set(CMAKE_INSTALL_PREFIX "${VXSDK_COMPILER_INSTALL}" CACHE PATH "..." FORCE)
set(INCDIR "${VXSDK_COMPILER_INSTALL}/include")
set(LIBDIR "${VXSDK_COMPILER_INSTALL}/lib")
endif()
if(FXLIBC_TARGET STREQUAL gint)
@ -70,6 +72,8 @@ set(SOURCES
3rdparty/grisu2b_59_56/grisu2b_59_56.c
3rdparty/tinymt32/rand.c
3rdparty/tinymt32/tinymt32.c
# C++ API details
src/dso.c
# assert
src/assert/assert.c
# ctype
@ -112,6 +116,7 @@ set(SOURCES
src/stdio/fgetc.c
src/stdio/fgetpos.c
src/stdio/fgets.c
src/stdio/fileno.c
src/stdio/fileutil.c
src/stdio/fopen.c
src/stdio/fprintf.c
@ -119,6 +124,7 @@ set(SOURCES
src/stdio/fputs.c
src/stdio/fread.c
src/stdio/freopen.c
src/stdio/fscanf.c
src/stdio/fseek.c
src/stdio/fsetpos.c
src/stdio/ftell.c
@ -140,21 +146,28 @@ set(SOURCES
src/stdio/puts.c
src/stdio/remove.c
src/stdio/rewind.c
src/stdio/scanf.c
src/stdio/scanf/scan.c
src/stdio/setbuf.c
src/stdio/setvbuf.c
src/stdio/snprintf.c
src/stdio/sprintf.c
src/stdio/sscanf.c
src/stdio/streams.c
src/stdio/ungetc.c
src/stdio/vasprintf.c
src/stdio/vdprintf.c
src/stdio/vfprintf.c
src/stdio/vfscanf.c
src/stdio/vprintf.c
src/stdio/vscanf.c
src/stdio/vsnprintf.c
src/stdio/vsprintf.c
src/stdio/vsscanf.c
# stdlib
src/stdlib/abort.c
src/stdlib/abs.c
src/stdlib/atexit.c
src/stdlib/atof.c
src/stdlib/atoi.c
src/stdlib/atol.c
@ -254,22 +267,43 @@ if(gint IN_LIST TARGET_FOLDERS)
src/time/target/gint/time.c)
endif()
# TODO: All targets
add_library(fxlibc ${SOURCES})
target_include_directories(fxlibc PRIVATE include/)
foreach(FOLDER IN LISTS TARGET_FOLDERS)
target_include_directories(fxlibc PRIVATE include/target/${FOLDER}/)
#---
# Handle "target-specific" fxlibc output format
#---
if(FXLIBC_TARGET STREQUAL vhex-sh)
set_property(GLOBAL PROPERTY TARGET_SUPPORTS_SHARED_LIBS TRUE)
add_library(fxlibcStatic STATIC ${SOURCES})
add_library(fxlibcShared SHARED ${SOURCES})
set(FXLIBC_TARGET_LIBS "fxlibcStatic;fxlibcShared")
else()
add_library(fxlibcStatic STATIC ${SOURCES})
set(FXLIBC_TARGET_LIBS "fxlibcStatic")
endif()
foreach(FXLIBC_LIB IN LISTS FXLIBC_TARGET_LIBS)
target_include_directories(${FXLIBC_LIB} PRIVATE include/)
foreach(FOLDER IN LISTS TARGET_FOLDERS)
target_include_directories(${FXLIBC_LIB} PRIVATE include/target/${FOLDER}/)
endforeach()
set_target_properties(${FXLIBC_LIB} PROPERTIES OUTPUT_NAME "c")
install(TARGETS ${FXLIBC_LIB} DESTINATION ${LIBDIR})
endforeach()
set_target_properties(fxlibc PROPERTIES
OUTPUT_NAME "c") # libc.a
# Install
install(TARGETS fxlibc DESTINATION ${LIBDIR})
#---
# Do not forget to install headers
#---
install(DIRECTORY include/ DESTINATION ${INCDIR} PATTERN "target" EXCLUDE)
foreach(FOLDER IN LISTS TARGET_FOLDERS)

20
STATUS
View File

@ -99,21 +99,22 @@ TEST: Function/symbol/macro needs to be tested
7.19.5.4 freopen -
7.19.5.5 setbuf -
7.19.5.6 setvbuf -
(EXT) fileno -
7.19.6.1 fprintf -
7.19.6.2 fscanf TODO
7.19.6.2 fscanf TEST
7.19.6.3 printf -
7.19.6.4 scanf TODO
7.19.6.4 scanf TEST
7.19.6.5 snprintf -
7.19.6.6 sprintf -
7.19.6.7 sscanf TODO
7.19.6.7 sscanf TEST
7.19.6.8 vfprintf -
7.19.6.9 vfscanf TODO
7.19.6.9 vfscanf TEST
7.19.6.10 vprintf -
7.19.6.11 vscanf TODO
7.19.6.11 vscanf TEST
7.19.6.12 vsnprintf -
7.19.6.13 vsprintf -
7.19.6.14 vsscanf TODO
7.19.6.14 vsscanf TEST
(EXT) asprintf -
(EXT) vasprintf -
(EXT) dprintf -
@ -161,7 +162,7 @@ TEST: Function/symbol/macro needs to be tested
7.20.3.3 malloc - (gint)
7.20.3.4 realloc - (gint)
7.20.4.1 abort - (stream flushing/closing/etc?)
7.20.4.2 atexit TODO
7.20.4.2 atexit TEST
7.20.4.3 exit - (stream flushing/closing/etc?)
7.20.4.4 _Exit - (gint)
7.20.4.5 getenv TODO
@ -172,6 +173,8 @@ TEST: Function/symbol/macro needs to be tested
7.20.6.2 div, ldiv, lldiv -
7.20.7 Multibyte/wide char conv TODO
7.20.8 Multibyte/wide string conv TODO
(EXT) __cxa_atexit TEST
(EXT) __cxa_finalize TEST
7.21 <string.h>
7.21.2.1 memcpy -
@ -222,6 +225,9 @@ TEST: Function/symbol/macro needs to be tested
7.25 <wctype.h> TODO (not a priority)
(EXT) <alloca.h>
(EXT) alloca -
# Supporting locales
What if we wanted to support more locales?

View File

@ -12,16 +12,13 @@ set(CMAKE_CXX_FLAGS_INIT "")
add_compile_options(-nostdlib)
add_link_options(-nostdlib)
link_libraries(-lgcc)
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
# Determine compiler install path
execute_process(
COMMAND ${CMAKE_C_COMPILER} --print-file-name=.
OUTPUT_VARIABLE FXSDK_COMPILER_INSTALL
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if(NOT DEFINED ENV{VXSDK_COMPILER_SYSROOT})
message(FATAL_ERROR "You should use the vxSDK to build this project")
endif()
set(VXSDK_COMPILER_INSTALL $ENV{VXSDK_COMPILER_SYSROOT})

21
include/alloca.h Normal file
View File

@ -0,0 +1,21 @@
#ifndef __ALLOCA_H__
# define __ALLOCA_H__
#ifdef __cplusplus
extern "C" {
#endif
#include <stddef.h>
#undef alloca
/* Allocate a block of memory on the stack. */
extern void *alloca(size_t __size);
#define alloca(size) __builtin_alloca(size)
#ifdef __cplusplus
}
#endif
#endif /*__ALLOCA_H__*/

View File

@ -75,7 +75,6 @@ extern int __printf(
va_list *__args);
/* Format extension API. */
struct __printf_format {
@ -84,12 +83,8 @@ struct __printf_format {
/* How much significant characters of data, meaning varies. */
int16_t precision;
/*
** Size specifier for integers (%o, %x, %i, %d, %u), is equal to the
** sizeof() of the targeted type. Also used for %lc.
*/
/* Size of targeted integer type (%o, %x, %i, %d, %u), in bytes */
uint8_t size;
/* (#) Alternative form: base prefixes, decimal point. */
uint8_t alternative :1;
/* ( ) Add a blank sign before nonnegative numbers. */
@ -111,15 +106,14 @@ struct __printf_format {
/*
** Type of format functions.
** -> __spec is the specifier letter (eg. "d" in "%d")
** -> __opts are the length, precision, sign, alignment, etc. options
** -> __out specifies the output and is used when generating text
** -> __fmt contains the format options and specifier letter
** -> __args is a pointer to the variable list of arguments to read from
*/
typedef void __printf_formatter_t(
struct __printf_output *__out,
struct __printf_format *__opts,
va_list *__args
);
struct __printf_format *__fmt,
va_list *__args);
/*
** Register a new format.
@ -127,10 +121,10 @@ typedef void __printf_formatter_t(
** The formatter designated by the specified lowercase or uppercase letter
** (eg 'p' or 'P') is registered. This functions allows overriding default
** formatters, but this is very much discouraged. Letters with special meaning
** in the standard cannot be changed. A formatted can be removed of disabled by
** in the standard cannot be changed. A formatter can be removed of disabled by
** registering NULL.
**
** Here are used characters in the C standard:
** Here are the characters used/reserved in the C standard:
**
** a: Hexadecimal floating-point A: Hexadecimal floating-point
** b: _ B: _
@ -138,7 +132,7 @@ typedef void __printf_formatter_t(
** d: Decimal integer D: _
** e: Exponent floating-point E: Exponent floating-point
** f: Floating-point F: Floating-point
** g: General floating-point G: General: floating-point
** g: General floating-point G: General floating-point
** h: short or char size H: _
** i: Integer I: Locale-aware digits
** j: intmax_t size J: _

View File

@ -187,6 +187,7 @@ extern void setbuf(FILE * __restrict__ __fp, char * __restrict__ __buf);
extern int setvbuf(FILE * __restrict__ __fp, char * __restrict__ __buf,
int __mode, size_t __size);
/* Return the file descriptor associated with __fp. */
extern int fileno(FILE *__fp);
/*

View File

@ -38,6 +38,7 @@ extern void free(void *__ptr);
__attribute__((noreturn))
extern void abort(void);
/* Register a function to be called at program exit. */
extern int atexit(void (*__func)(void));
/* Exit; calls handlers, flushes and closes streams and temporary files. */

68
src/dso.c Normal file
View File

@ -0,0 +1,68 @@
#include <stdlib.h>
/* We don't support shared object loading, so provide a single handle */
__attribute__((visibility("hidden")))
void *__dso_handle = (void *)&__dso_handle;
/* Number of atexit() calls supported, must be at least 32 (7.20.4.2§3).*/
#define ATEXIT_MAX 32
struct dtor {
void (*f)(void *);
void *p;
void *d;
};
static struct dtor *_dtors;
static int _dtor_count = 0;
__attribute__((constructor))
static void alloc_dtors(void)
{
_dtors = malloc(ATEXIT_MAX * sizeof *_dtors);
}
int __cxa_atexit(void (*f)(void *), void *p, void *d)
{
if(!_dtors || _dtor_count >= ATEXIT_MAX)
return 1;
_dtors[_dtor_count++] = (struct dtor){ f, p, d };
return 0;
}
/* We walk the destructor list in reverse order. Destructors may themselves
call __cxa_atexit(), causing new destructors to be added. When that
happens, we must call the new ones first before resuming (7.20.4.3§3). We
track changes in _dtor_count to detect this situation.
This function calls destructs in the interval [low..high) that match DSO
handle d, plus any other destructors registered as a consequence.
_dtor_count may increase. */
static void call_dtors_in_interval(void *d, int low, int high)
{
int end = _dtor_count;
for(int i = high - 1; i >= low; i--) {
if(d == NULL || _dtors[i].d == d)
_dtors[i].f(_dtors[i].p);
if(_dtor_count > end) {
call_dtors_in_interval(d, end, _dtor_count);
end = _dtor_count;
}
}
}
void __cxa_finalize(void *d)
{
call_dtors_in_interval(d, 0, _dtor_count);
/* Re-compact the array to keep only destructors we didn't call. */
int j = 0;
for(int i = 0; i < _dtor_count; i++) {
if(d == NULL || _dtors[i].d == d)
continue;
_dtors[j++] = _dtors[i];
}
_dtor_count = j;
}

6
src/stdio/fileno.c Normal file
View File

@ -0,0 +1,6 @@
#include <stdio.h>
int fileno(FILE *fp)
{
return fp->fd;
}

19
src/stdio/fscanf.c Normal file
View File

@ -0,0 +1,19 @@
#include <stdio.h>
#include "stdio_p.h"
int fscanf(FILE * restrict fp, char const * restrict fmt, ...)
{
struct __scanf_input in = {
.fp = fp,
};
va_list args;
va_start(args, fmt);
__scanf_start(&in);
int count = __scanf(&in, fmt, &args);
__scanf_end(&in);
va_end(args);
return count;
}

View File

@ -59,6 +59,8 @@ void __printf_format_s(
size_t len = 0;
uint32_t precision = opt->precision ? opt->precision : (-1);
while(s[len] && len < precision) len++;
/* Cap precision to real value for __printf_compute_geometry() */
opt->precision = len;
struct __printf_geometry g = {
.prefix = 0, .sign = 0, .content = len,

19
src/stdio/scanf.c Normal file
View File

@ -0,0 +1,19 @@
#include <stdio.h>
#include "stdio_p.h"
int scanf(char const * restrict fmt, ...)
{
struct __scanf_input in = {
.fp = stdin,
};
va_list args;
va_start(args, fmt);
__scanf_start(&in);
int count = __scanf(&in, fmt, &args);
__scanf_end(&in);
va_end(args);
return count;
}

366
src/stdio/scanf/scan.c Normal file
View File

@ -0,0 +1,366 @@
#include "../stdio_p.h"
#include "../../stdlib/stdlib_p.h"
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdbool.h>
/* Features of this implementation:
- Specifiers:
* Integers (%i, %d, %o, %u, %x, %X)
* Floating-point numbers (%e, %f, %F, %g, %a)
* Strings (%c, %s, %[])
* Pointers (%p)
* Total number of bytes read so far (%n)
- Integer size modifiers: hh, h, l, ll, L, j, z, t. Supported for all
integer conversions, i.e. %i, %d, %u, %o, %x, %X and %n.
- Floating-point size modifiers: l, L for %a, %e, %f, %g.
- Assignment suppression character '*'.
- Maximum field width (but buggy for floating-point, see below).
NOT SUPPORTED:
- Wide characters: %lc, %ls.
- q size modifier.
- "'" (quote) specifier for locale-specific thousand separators.
- String allocation: %mc, %ms.
- Out-of-order assignments "%n$".
- TODO: Maximum field width for floating-point is mostly untested and likely
has bugs when the field ends in the middle of the number. */
void __scanf_start(struct __scanf_input *in)
{
if(in->fp)
in->buffer = fgetc(in->fp);
else {
in->buffer = (*in->str ? *in->str : EOF);
in->str += (in->buffer != EOF);
}
}
int __scanf_fetch(struct __scanf_input *in)
{
if(in->fp)
return fgetc(in->fp);
int c = *in->str;
if(c == 0)
return EOF;
in->str++;
return c;
}
void __scanf_end(struct __scanf_input *in)
{
if(in->buffer == EOF)
return;
if(in->fp)
ungetc(in->buffer, in->fp);
else
in->str--;
}
static void __skip_spaces(struct __scanf_input *in)
{
while(isspace(__scanf_peek(in)))
__scanf_in(in);
}
static void __scanf_store_i(int64_t value, int size, va_list *args)
{
if(size == 1)
*va_arg(*args, int8_t *) = value;
else if(size == 2)
*va_arg(*args, int16_t *) = value;
else if(size == 4)
*va_arg(*args, int32_t *) = value;
else if(size == 8)
*va_arg(*args, int64_t *) = value;
}
static void __scanf_store_d(long double value, int size, va_list *args)
{
if(size == sizeof(float))
*va_arg(*args, float *) = value;
else if(size == sizeof(double))
*va_arg(*args, double *) = value;
else if(size == sizeof(long double))
*va_arg(*args, long double *) = value;
}
struct scanf_format {
/* Maximum field width */
int field_width;
/* Size of the assigned (integer or floating-point) type, in bytes */
uint8_t size;
/* Whether to skip assignment */
bool skip;
/* Set of bytes allowed for bracket sets in %[] */
uint8_t bracket_set[32];
};
/* Allow/disallow the entire set */
static void bracket_set_init(uint8_t *set, bool allow)
{
memset(set, allow ? 0xff : 0x00, 32);
}
/* Allow/disallow a range of characters. Both ends are included. */
static void bracket_set_range(
uint8_t *set, uint8_t start, uint8_t end, bool allow)
{
for(int u = start; u <= end; u++) {
int byte = u >> 3;
int bit = 1 << (u & 7);
if(allow)
set[byte] |= bit;
else
set[byte] &= ~bit;
}
}
/* Check whether a byte is allowed by the bracket set. */
static bool bracket_set_test(uint8_t *set, int c)
{
return (c != EOF) && (set[c >> 3] & (1 << (c & 7)));
}
/* Parse a bracket set from a format string. Returns true on success. */
static bool bracket_set_parse(uint8_t *set, char const *format, int *pos)
{
int last = 0;
bool allow = true;
bracket_set_init(set, false);
/* '^' denotes a negated set */
if(format[*pos] == '^') {
allow = false;
(*pos)++;
bracket_set_init(set, true);
}
/* ']' as the first character adds ']' to the set */
if(format[*pos] == ']' ) {
bracket_set_range(set, ']', ']', allow);
(*pos)++;
}
for(; format[*pos] && format[*pos] != ']'; (*pos)++) {
/* '-' as the last character, thus explicitly in the set */
if(format[*pos] == '-' && format[*pos + 1] == ']')
bracket_set_range(set, '-', '-', allow);
/* '-' as denoting a range */
else if(format[*pos] == '-') {
(*pos)++;
bracket_set_range(set, last, format[*pos], allow);
}
/* Any other character */
else {
last = format[*pos];
bracket_set_range(set, last, last, allow);
}
}
return (format[*pos] == ']');
}
/* Parse a format in the format string. Returns specifier, 0 on error. */
static int parse_fmt(char const *fmt, int *pos, struct scanf_format *opt)
{
opt->field_width = INT_MAX;
opt->size = sizeof(int);
opt->skip = false;
int width = 0;
char size_letter = 0;
for((*pos)++;; (*pos)++) {
int c = fmt[*pos];
if(c == '*')
opt->skip = true;
else if(strchr("hlzjtL", c)) {
if(c == 'h')
opt->size = (size_letter=='h') ? sizeof(char) : sizeof(short);
else if(c == 'l')
opt->size = (size_letter=='l') ? sizeof(long long) :
sizeof(long);
else if(c == 'z')
opt->size = sizeof(size_t);
else if(c == 'j')
opt->size = sizeof(intmax_t);
else if(c == 't')
opt->size = sizeof(ptrdiff_t);
else if(c == 'L')
opt->size = sizeof(long double);
size_letter = c;
}
else if(isdigit(c)) {
width = width * 10 + (fmt[*pos] - '0');
opt->field_width = width;
}
else if(c == '[') {
(*pos)++;
return bracket_set_parse(opt->bracket_set, fmt, pos) ? '[' : 0;
}
else if(strchr("diouxXpsn", c))
return c;
else if(strchr("aAeEfFgG", c)) {
/* Adjust interpretation of no size / 'l' size */
if(size_letter == 0)
opt->size = sizeof(float);
if(size_letter == 'l')
opt->size = sizeof(double);
return c;
}
else if(c == 'c') {
if(opt->field_width == INT_MAX)
opt->field_width = 1;
return c;
}
else return 0;
}
return 0;
}
int __scanf(
struct __scanf_input * __restrict__ in,
char const * __restrict__ format,
va_list *args)
{
/* Number of successful assignments */
int validrets = 0;
for(int pos = 0; format[pos]; pos++) {
if(format[pos] == ' ') {
__skip_spaces(in);
continue;
}
else if(format[pos] != '%' || format[pos + 1] == '%') {
/* Expect this specific character */
if(__scanf_peek(in) != format[pos])
return validrets;
__scanf_in(in);
pos += (format[pos] == '%');
continue;
}
/* Perform a conversion */
struct scanf_format opt;
int spec = parse_fmt(format, &pos, &opt);
if(spec == 0)
return validrets;
switch(spec) {
case '[': {
char *c = opt.skip ? NULL : va_arg(*args, char *);
int i;
for(i = 0; i < opt.field_width; i++) {
int temp = __scanf_peek(in);
if(bracket_set_test(opt.bracket_set, temp)) {
__scanf_in(in);
if(c) *c++ = temp;
}
else if(temp==EOF && !i && !validrets)
return EOF;
else break;
}
if(!i)
return validrets;
*c = '\0';
validrets += !opt.skip;
break;
}
case 'n':
__scanf_store_i(in->bytes_read, opt.size, args);
break;
case 'd':
case 'i':
case 'o':
case 'u':
case 'x':
case 'X': {
int f = format[pos];
int base = (f == 'd' || f == 'u') ? 10 :
(f == 'o') ? 8:
(f == 'x' || f == 'X') ? 16 : 0;
bool use_unsigned = (f == 'o' || f == 'x' || f == 'X');
long long int temp;
int err = __strto_int(in, base, NULL, &temp, use_unsigned,
opt.field_width);
if(err == EOF && validrets == 0) return EOF;
if(err) return validrets;
if(!opt.skip)
__scanf_store_i(temp, opt.size, args);
validrets++;
break;
}
case 'a':
case 'A':
case 'e':
case 'E':
case 'f':
case 'F':
case 'g':
case 'G': {
long double temp;
int err = __strto_fp(in, NULL, NULL, &temp, opt.field_width);
if(err == EOF && validrets == 0) return EOF;
if(err) return validrets;
if(!opt.skip)
__scanf_store_d(temp, opt.size, args);
validrets++;
break;
}
case 'p': {
void *p = opt.skip ? NULL : va_arg(*args, void *);
_Static_assert(sizeof(p) == sizeof(long));
int err = __strto_int(in, 0, p, NULL, true, opt.field_width);
if(err) return validrets;
validrets += !opt.skip;
break;
}
case 'c': {
char *c = opt.skip ? NULL : va_arg(*args, char *);
for(int u = 0; u < opt.field_width; u++) {
int temp = __scanf_in(in);
if(temp==EOF) return EOF;
else if(c) *c++ = temp;
}
validrets += !opt.skip;
break;
}
case 's': {
char *c = opt.skip ? NULL : va_arg(*args, char *);
__skip_spaces(in);
for(int i = 0; i < opt.field_width; i++) {
int temp = __scanf_peek(in);
if(temp==EOF && !i) return validrets;
if(isspace(temp) || temp==EOF) {
if(c) {
*c = 0;
validrets++;
}
break;
}
else {
int temp = __scanf_in(in);
if(c) *c++ = temp;
}
}
break;
}
}
}
return validrets;
}

19
src/stdio/sscanf.c Normal file
View File

@ -0,0 +1,19 @@
#include <stdio.h>
#include "stdio_p.h"
int sscanf(const char * restrict str, char const * restrict fmt, ...)
{
struct __scanf_input in = {
.str = str,
};
va_list args;
va_start(args, fmt);
__scanf_start(&in);
int count = __scanf(&in, fmt, &args);
__scanf_end(&in);
va_end(args);
return count;
}

66
src/stdio/stdio_p.h Normal file
View File

@ -0,0 +1,66 @@
#ifndef __STDIO_P_H__
# define __STDIO_P_H__
#include <stdio.h>
#include <stdbool.h>
/*
** General utilities for scanf(); we expose them here as we use subfunctions of
** strto*() from <stdlib.h> to implement numerical specifiers.
*/
/*
** Input for scanf; exactly one of str and fp must be non-NULL. We include a
** single-character buffer for convenience for scanning functions to test the
** next character, which can be flushed back by ungetc().
*/
struct __scanf_input {
char const * __restrict__ str;
FILE *fp;
/* Single-character lookahead buffer */
int buffer;
/* Total numbers of bytes read in a scall to *scanf() */
int bytes_read;
};
/* Generic formatted scaning. */
extern int __scanf(
struct __scanf_input * __restrict__ __in,
char const * __restrict__ __format,
va_list *__args);
/* Initialize the input by feeding the buffer byte. */
void __scanf_start(struct __scanf_input *__in);
/* Fetch the next byte from the input and return it (don't call directly). */
int __scanf_fetch(struct __scanf_input *__in);
/* Read the next byte while maintaining the buffer. */
static inline int __scanf_in(struct __scanf_input *__in)
{
int c = __in->buffer;
__in->buffer = __scanf_fetch(__in);
__in->bytes_read++;
return c;
}
/* Read the next byte and also decrease a total count of available reads. */
static inline int __scanf_in_limit(struct __scanf_input *__in, int *__N)
{
(*__N)--;
return __scanf_in(__in);
}
/* Peek the next byte without advancing. */
static inline int __scanf_peek(struct __scanf_input *__in)
{
return __in->buffer;
}
/* Close the input by unsending the buffer once finished. */
void __scanf_end(struct __scanf_input *__in);
#endif /* __STDIO_P_H__ */

14
src/stdio/vfscanf.c Normal file
View File

@ -0,0 +1,14 @@
#include <stdio.h>
#include "stdio_p.h"
int vfscanf(FILE * restrict fp, char const * restrict fmt, va_list args)
{
struct __scanf_input in = {
.fp = fp,
};
__scanf_start(&in);
int rc = __scanf(&in, fmt, &args);
__scanf_end(&in);
return rc;
}

14
src/stdio/vscanf.c Normal file
View File

@ -0,0 +1,14 @@
#include <stdio.h>
#include "stdio_p.h"
int vscanf(char const * restrict fmt, va_list args)
{
struct __scanf_input in = {
.fp = stdin,
};
__scanf_start(&in);
int rc = __scanf(&in, fmt, &args);
__scanf_end(&in);
return rc;
}

15
src/stdio/vsscanf.c Normal file
View File

@ -0,0 +1,15 @@
#include <stdio.h>
#include "stdio_p.h"
int vsscanf(const char * restrict str, char const * restrict fmt, va_list args)
{
/* This is valid even if str=NULL. */
struct __scanf_input in = {
.str = str,
};
__scanf_start(&in);
int rc = __scanf(&in, fmt, &args);
__scanf_end(&in);
return rc;
}

8
src/stdlib/atexit.c Normal file
View File

@ -0,0 +1,8 @@
#include <stdlib.h>
extern int __cxa_atexit(void (*f)(void *), void *p, void *d);
int atexit(void (*f)(void))
{
return __cxa_atexit((void (*)(void *))f, NULL, NULL);
}

View File

@ -1,8 +1,11 @@
#include <stdlib.h>
extern void __cxa_finalize(void *d);
void exit(int rc)
{
/* TODO: invoke atexit callbacks */
__cxa_finalize(NULL);
/* TODO: exit: Flush all streams */
/* TODO: exit: Close all streams */
/* TODO: exit: Remove temporary files */

View File

@ -3,6 +3,8 @@
#include <stdlib.h>
#include <stdbool.h>
#include <limits.h>
#include "../stdio/stdio_p.h"
/*
** Parse an integer from a string. This is the base function for strtol,
@ -21,14 +23,17 @@
** On platforms where long is 32-bit, 64-bit operations are performed only if
** outll is non-NULL. This is because multiplications with overflow can be
** expensive.
**
** N is the bound on the number of characters to read. To disable the bound,
** specify INT_MAX.
*/
int __strto_int(
char const * restrict __ptr,
char ** restrict __endptr,
struct __scanf_input *__input,
int __base,
long *__outl,
long long *__outll,
bool __use_unsigned);
bool __use_unsigned,
int __N);
/*
** Parse a floating-point value from a string. This is the base function for
@ -39,10 +44,10 @@ int __strto_int(
** and outl is set.
*/
int __strto_fp(
char const * restrict __ptr,
char ** restrict __endptr,
double *__out,
float *__outf,
long double *__outl);
struct __scanf_input *__input,
double *__out,
float *__outf,
long double *__outl,
int __N);
#endif /*__STDLIB_P_H__*/

View File

@ -1,3 +1,4 @@
#include "stdlib_p.h"
#include <stdlib.h>
#include <stdbool.h>
@ -37,12 +38,11 @@
** -> In hexadecimal notation, we read as many bits as the mantissa of a long
** double, then later multiply by a power of 2. There are no approximations.
*/
static void parse_digits(char const * restrict *ptr0, bool *valid,
SIGNIFICAND_TYPE *digits, long *exponent, bool hexadecimal)
static int parse_digits(struct __scanf_input *input,
SIGNIFICAND_TYPE *digits, long *exponent, bool hexadecimal, int *N)
{
char const *ptr = *ptr0;
bool dot_found = false;
int digits_found = 0;
int digits_found=0, c=0;
*digits = 0;
*exponent = 0;
@ -53,13 +53,16 @@ static void parse_digits(char const * restrict *ptr0, bool *valid,
int dot_character = '.';
int exp_character = (hexadecimal ? 'p' : 'e');
for(int i = 0; isdigit(*ptr) || (hexadecimal && isxdigit(*ptr))
|| *ptr == dot_character; i++, ptr++) {
for(int i = 0; *N >= 0; i++) {
c = __scanf_peek(input);
if(i == 0 && c == EOF)
return EOF;
if(!(isdigit(c) ||
(hexadecimal && isxdigit(c)) ||
(c == dot_character && !dot_found))) break;
__scanf_in_limit(input, N);
/* Allow only one dot in the string, stop at the second one */
if(*ptr == dot_character && dot_found) break;
if(*ptr == dot_character) {
if(c == dot_character) {
dot_found = true;
continue;
}
@ -67,12 +70,12 @@ static void parse_digits(char const * restrict *ptr0, bool *valid,
/* Count digits only until SIGNIFICAND_DIGITS */
if(digits_found < max_digits) {
if(hexadecimal) {
int v = *ptr - '0';
if(!isdigit(*ptr)) v = tolower(*ptr)-'a'+10;
int v = c - '0';
if(!isdigit(c)) v = tolower(c) - 'a' + 10;
*digits = (*digits << 4) + v;
}
else {
*digits = (*digits * 10) + (*ptr - '0');
*digits = (*digits * 10) + (c - '0');
}
}
else (*exponent)++;
@ -80,7 +83,7 @@ static void parse_digits(char const * restrict *ptr0, bool *valid,
if(dot_found) (*exponent)--;
/* But also round at the first discarded one */
if(digits_found == max_digits && *ptr >= '5')
if(digits_found == max_digits && c >= '5')
(*digits)++;
digits_found++;
@ -88,46 +91,60 @@ static void parse_digits(char const * restrict *ptr0, bool *valid,
/* Require at least one digit to be present; if not, the whole string
is considered invalid */
if(!digits_found) {
*valid = false;
return;
}
if(!digits_found)
return false;
/* In hexadecimal, each character is worth 4 bits of exponent */
if(hexadecimal) (*exponent) *= 4;
/* Parse exponent */
if(tolower(*ptr) == exp_character) {
char *end;
long e = strtol(ptr + 1, &end, 10);
if(tolower(__scanf_peek(input)) == exp_character) {
/* Hack: Restore the str pointer if this fails (which we
cannot determine with a single lookahead) so that *endptr is
set correctly */
struct __scanf_input backup = *input;
/* If an integer cannot be parsed, ignore the 'e...' part */
if(end != ptr + 1) {
ptr = end;
__scanf_in_limit(input, N);
long e = 0;
// TODO: strto_fp: Pass limit to __strto_int
if(__strto_int(input, 10, &e, NULL, false, *N) == 0)
*exponent += e;
}
else
*input = backup;
}
*ptr0 = ptr;
*valid = true;
return true;
}
int __strto_fp(char const * restrict ptr, char ** restrict endptr, double *out,
float *outf, long double *outl)
static bool expect(struct __scanf_input *input, char const *sequence)
{
/* Save the value of ptr in endptr, in case format is invalid */
if(endptr) *endptr = (char *)ptr;
for(int i = 0; sequence[i]; i++) {
int c = __scanf_in(input);
if(tolower(c) != tolower(sequence[i]))
return false;
}
return true;
}
int __strto_fp(struct __scanf_input *input, double *out, float *outf,
long double *outl, int N)
{
/* Skip initial whitespace */
while(isspace(*ptr)) ptr++;
while(isspace(__scanf_peek(input))) __scanf_in(input);
// TODO: strto_fp() doesn't support size limits well, affecting %5f etc.
if(N <= 0)
return EOF;
/* Read optional sign */
bool negative = false;
if(*ptr == '-') negative = true;
if(*ptr == '-' || *ptr == '+') ptr++;
int sign = __scanf_peek(input);
if(sign == '-') negative = true;
if(sign == '-' || sign == '+') __scanf_in_limit(input, &N);
int errno_value = 0;
bool valid = true;
bool valid = false;
/* Result variable */
if(out) *out = 0.0;
@ -135,47 +152,75 @@ int __strto_fp(char const * restrict ptr, char ** restrict endptr, double *out,
if(outl) *outl = 0.0l;
/* NaN possibly with an argument */
if(!strncasecmp(ptr, "nan", 3)) {
char const *arg = "";
ptr += 3;
if(ptr[0] == '(') {
arg = ptr + 1;
do ptr++;
while(ptr[-1] != ')');
if(tolower(__scanf_peek(input)) == 'n') {
if(!expect(input, "nan"))
return EINVAL;
/* Get the argument for up to 32 bytes */
char arg[32];
int i = 0;
if(__scanf_peek(input) == '(') {
while(i < 31) {
int c = __scanf_in_limit(input, &N);
if(c == ')') break;
if(c == EOF || N <= 0)
return EOF;
arg[i++] = c;
}
arg[i] = 0;
}
if(out) *out = __builtin_nan(arg);
if(outf) *outf = __builtin_nanf(arg);
if(outl) *outl = __builtin_nanl(arg);
valid = true;
}
/* Infinity */
else if(!strncasecmp(ptr, "infinity", 8)) {
else if(tolower(__scanf_peek(input)) == 'i') {
if(!expect(input, "inf"))
return EINVAL;
if(tolower(__scanf_peek(input)) == 'i' &&
!expect(input, "inity"))
return EINVAL;
if(out) *out = __builtin_inf();
if(outf) *outf = __builtin_inff();
if(outl) *outl = __builtin_infl();
ptr += 8;
valid = true;
}
else if(!strncasecmp(ptr, "inf", 3)) {
if(out) *out = __builtin_inf();
if(outf) *outf = __builtin_inff();
if(outl) *outl = __builtin_infl();
ptr += 3;
else if(__scanf_peek(input) == EOF) {
return EOF;
}
else {
SIGNIFICAND_TYPE digits = 0;
long e = 0;
if(ptr[0] == '0' && tolower(ptr[1]) == 'x') {
ptr += 2;
parse_digits(&ptr, &valid, &digits, &e, true);
/* Check for the 0x prefix. Skipping a 0 if we start with 0 but
not 0x isn't a problem. */
bool hexa = false;
if(__scanf_peek(input) == '0') {
__scanf_in_limit(input, &N);
if(tolower(__scanf_peek(input)) == 'x') {
__scanf_in_limit(input, &N);
hexa = true;
}
/* Count the 0 as a digit */
else valid = true;
}
if(hexa) {
int rc = parse_digits(input, &digits, &e, true, &N);
if(!valid && rc == EOF)
return EOF;
valid |= rc;
if(out) *out = (double)digits * exp2(e);
if(outf) *outf = (float)digits * exp2f(e);
if(outl) *outl = (long double)digits * exp2l(e);
}
else {
parse_digits(&ptr, &valid, &digits, &e, false);
int rc = parse_digits(input, &digits, &e, false, &N);
if(!valid && rc == EOF)
return EOF;
valid |= rc;
if(out) *out = (double)digits * pow(10, e);
if(outf) *outf = (float)digits * powf(10, e);
if(outl) *outl = (long double)digits * powl(10, e);
@ -200,8 +245,5 @@ int __strto_fp(char const * restrict ptr, char ** restrict endptr, double *out,
if(outl) *outl = -(*outl);
}
/* Save the result pointer */
if(endptr && valid) *endptr = (char *)ptr;
return errno_value;
return valid ? errno_value : EINVAL;
}

View File

@ -4,19 +4,20 @@
#include <errno.h>
#include <limits.h>
int __strto_int(char const * restrict ptr, char ** restrict endptr, int base,
long *outl, long long *outll, bool use_unsigned)
int __strto_int(struct __scanf_input *input, int base, long *outl,
long long *outll, bool use_unsigned, int N)
{
/* Save the value of ptr in endptr now in case the format is invalid */
if(endptr) *endptr = (char *)ptr;
/* Skip initial whitespace */
while(isspace(*ptr)) ptr++;
while(isspace(__scanf_peek(input))) __scanf_in(input);
if(N <= 0)
return EOF;
/* Accept a sign character */
bool negative = false;
if(*ptr == '-') negative = true;
if(*ptr == '-' || *ptr == '+') ptr++;
int sign = __scanf_peek(input);
if(sign == '-') negative = true;
if(sign == '-' || sign == '+') __scanf_in_limit(input, &N);
/* Use unsigned variables as only these have defined overflow */
unsigned long xl = 0;
@ -26,29 +27,36 @@ int __strto_int(char const * restrict ptr, char ** restrict endptr, int base,
bool valid = false;
/* Read prefixes and determine base */
if((base == 0 || base == 16) && ptr[0]=='0' && tolower(ptr[1])=='x') {
ptr += 2;
base = 16;
if(__scanf_peek(input) == '0') {
__scanf_in_limit(input, &N);
if((base == 0 || base == 16) &&
tolower(__scanf_peek(input)) == 'x') {
__scanf_in_limit(input, &N);
base = 16;
}
/* If we don't consume the x then count the 0 as a digit */
else valid = true;
if(base == 0)
base = 8;
}
else if(base == 0 && ptr[0] == '0') {
ptr++;
base = 8;
}
else if(base == 0) {
if(!valid && (N <= 0 || __scanf_peek(input) == EOF))
return EOF;
if(base == 0)
base = 10;
}
/* Read digits */
while(1) {
while(N > 0) {
int v = -1;
if(isdigit(*ptr)) v = *ptr - '0';
if(islower(*ptr)) v = *ptr - 'a' + 10;
int c = __scanf_peek(input);
if(isdigit(c)) v = c - '0';
if(islower(c)) v = c - 'a' + 10;
if(v == -1 || v >= base) break;
/* The value is valid as long as there is at least one digit */
valid = true;
/* (x = base*x + v) but with overflow checks */
/* TODO: strto_int: We might fail to represent [L]LONG_MIN */
if(outl) {
if(__builtin_umull_overflow(xl, base, &xl))
errno_value = ERANGE;
@ -62,7 +70,7 @@ int __strto_int(char const * restrict ptr, char ** restrict endptr, int base,
errno_value = ERANGE;
}
ptr++;
__scanf_in_limit(input, &N);
}
/* Handle sign and range */
@ -101,6 +109,6 @@ int __strto_int(char const * restrict ptr, char ** restrict endptr, int base,
if(outl) *outl = xl;
if(outll) *outll = xll;
if(endptr && valid) *endptr = (char *)ptr;
return errno_value;
return valid ? errno_value : EINVAL;
}

View File

@ -4,7 +4,17 @@
double strtod(char const * restrict ptr, char ** restrict endptr)
{
double d = 0;
int err = __strto_fp(ptr, endptr, &d, NULL, NULL);
if(err != 0) errno = err;
if(endptr)
*endptr = (char *)ptr;
struct __scanf_input in = { .str = ptr, .fp = NULL };
__scanf_start(&in);
int err = __strto_fp(&in, &d, NULL, NULL, INT_MAX);
__scanf_end(&in);
if(err != 0)
errno = (err == EOF) ? EINVAL : err;
if(err != EINVAL && endptr)
*endptr = (char *)in.str;
return d;
}

View File

@ -4,7 +4,17 @@
float strtof(char const * restrict ptr, char ** restrict endptr)
{
float f = 0;
int err = __strto_fp(ptr, endptr, NULL, &f, NULL);
if(err != 0) errno = err;
if(endptr)
*endptr = (char *)ptr;
struct __scanf_input in = { .str = ptr, .fp = NULL };
__scanf_start(&in);
int err = __strto_fp(&in, NULL, &f, NULL, INT_MAX);
__scanf_end(&in);
if(err != 0)
errno = (err == EOF) ? EINVAL : err;
if(err != EINVAL && endptr)
*endptr = (char *)in.str;
return f;
}

View File

@ -4,7 +4,17 @@
long int strtol(char const * restrict ptr, char ** restrict endptr, int base)
{
long n = 0;
int err = __strto_int(ptr, endptr, base, &n, NULL, false);
if(err != 0) errno = err;
if(endptr)
*endptr = (char *)ptr;
struct __scanf_input in = { .str = ptr, .fp = NULL };
__scanf_start(&in);
int err = __strto_int(&in, base, &n, NULL, false, INT_MAX);
__scanf_end(&in);
if(err != 0)
errno = (err == EOF) ? EINVAL : err;
if(err != EINVAL && endptr)
*endptr = (char *)in.str;
return n;
}

View File

@ -4,7 +4,17 @@
long double strtold(char const * restrict ptr, char ** restrict endptr)
{
long double ld = 0;
int err = __strto_fp(ptr, endptr, NULL, NULL, &ld);
if(err != 0) errno = err;
if(endptr)
*endptr = (char *)ptr;
struct __scanf_input in = { .str = ptr, .fp = NULL };
__scanf_start(&in);
int err = __strto_fp(&in, NULL, NULL, &ld, INT_MAX);
__scanf_end(&in);
if(err != 0)
errno = (err == EOF) ? EINVAL : err;
if(err != EINVAL && endptr)
*endptr = (char *)in.str;
return ld;
}

View File

@ -5,7 +5,17 @@ long long int strtoll(char const * restrict ptr, char ** restrict endptr,
int base)
{
long long n = 0;
int err = __strto_int(ptr, endptr, base, NULL, &n, false);
if(err != 0) errno = err;
if(endptr)
*endptr = (char *)ptr;
struct __scanf_input in = { .str = ptr, .fp = NULL };
__scanf_start(&in);
int err = __strto_int(&in, base, NULL, &n, false, INT_MAX);
__scanf_end(&in);
if(err != 0)
errno = (err == EOF) ? EINVAL : err;
if(err != EINVAL && endptr)
*endptr = (char *)in.str;
return n;
}

View File

@ -5,7 +5,17 @@ unsigned long int strtoul(char const * restrict ptr, char ** restrict endptr,
int base)
{
unsigned long n = 0;
int err = __strto_int(ptr, endptr, base, (long *)&n, NULL, true);
if(err != 0) errno = err;
if(endptr)
*endptr = (char *)ptr;
struct __scanf_input in = { .str = ptr, .fp = NULL };
__scanf_start(&in);
int err = __strto_int(&in, base, (long *)&n, NULL, true, INT_MAX);
__scanf_end(&in);
if(err != 0)
errno = (err == EOF) ? EINVAL : err;
if(err != EINVAL && endptr)
*endptr = (char *)in.str;
return n;
}

View File

@ -5,7 +5,17 @@ unsigned long long int strtoull(char const * restrict ptr,
char ** restrict endptr, int base)
{
unsigned long long n = 0;
int err = __strto_int(ptr, endptr, base, NULL, (long long *)&n, true);
if(err != 0) errno = err;
if(endptr)
*endptr = (char *)ptr;
struct __scanf_input in = { .str = ptr, .fp = NULL };
__scanf_start(&in);
int err = __strto_int(&in, base, NULL, (long long *)&n, true, INT_MAX);
__scanf_end(&in);
if(err != 0)
errno = (err == EOF) ? EINVAL : err;
if(err != EINVAL && endptr)
*endptr = (char *)in.str;
return n;
}

View File

@ -6,5 +6,5 @@ int strcmp(const char *s1, const char *s2)
s1 += 1;
s2 += 1;
}
return (*s1 - *s2);
return ((unsigned char) *s1 - (unsigned char) *s2);
}

View File

@ -7,5 +7,5 @@ int strncmp(const char *s1, const char *s2, size_t n)
size_t i = -1;
while (++i < n - 1 && s1[i] != '\0' && s2[i] != '\0'
&& s1[i] == s2[i]) ;
return (s1[i] - s2[i]);
return ((unsigned char) s1[i] - (unsigned char) s2[i]);
}

View File

@ -18,12 +18,12 @@ static const char *month_names[12] = {
}; break
/* Perform a sub-call to strftime to expand a complex format */
#define do_strftime(fmt) { \
size_t _written = strftime2(s+len, n-len, fmt, time); \
size_t _written = strftime(s+len, n-len, fmt, time); \
if(_written == 0) goto full; \
len += _written; \
}; break
size_t strftime2(char * restrict s, size_t n, const char * restrict format,
size_t strftime(char * restrict s, size_t n, const char * restrict format,
const struct tm * restrict time)
{
size_t len = 0;

View File

@ -1,14 +1,26 @@
[project]
name = 'fxlibc'
version = '1.4.1'
type = 'app'
target = [
'superh'
]
[build]
configure = 'cmake -DFXLIBC_PIC=1 -DFXLIBC_TARGET=vhex-sh -B build-vhex -DCMAKE_TOOLCHAIN_FILE=cmake/toolchain-vhex.cmake'
build = 'make -C build-vhex'
install = 'make -C build-vhex install'
[superh.build]
configure = """ \
cmake \
-B build-vhex \
-DFXLIBC_PIC=1 \
-DFXLIBC_TARGET=vhex-sh \
-DCMAKE_TOOLCHAIN_FILE=cmake/toolchain-vhex.cmake
"""
build = 'cmake --build build-vhex'
install = 'cmake --install build-vhex'
uninstall = """ \
if [ -e build-vhex/install_manifest.txt ]; then \
xargs rm -f < build-vhex/install_manifest.txt; \
fi \
"""
[superh.dependencies]
vxOpenLibm = 'master@superh'
sh-elf-vhex = 'master@superh'