From ee42660ea52a3119c5b9ca6a06fc6d63c1a580e7 Mon Sep 17 00:00:00 2001 From: Heath Mitchell Date: Thu, 24 Nov 2022 13:30:46 +0100 Subject: [PATCH 01/12] Update 'src/string/strncmp.c' --- src/string/strncmp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/string/strncmp.c b/src/string/strncmp.c index 0469f47..d03448a 100644 --- a/src/string/strncmp.c +++ b/src/string/strncmp.c @@ -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]); } From aeeae3810d4263f2118f73bdb08bd34b5f243a40 Mon Sep 17 00:00:00 2001 From: Heath Mitchell Date: Thu, 24 Nov 2022 13:36:02 +0100 Subject: [PATCH 02/12] Update 'src/string/strcmp.c' --- src/string/strcmp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/string/strcmp.c b/src/string/strcmp.c index f3ec3cc..5ec8600 100644 --- a/src/string/strcmp.c +++ b/src/string/strcmp.c @@ -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); } From fda0d950ede1132415cd692371e4b361cdd802a8 Mon Sep 17 00:00:00 2001 From: Lephenixnoir Date: Mon, 22 Aug 2022 15:07:00 +0200 Subject: [PATCH 03/12] time: fix strftime being called strftime2 --- src/time/strftime.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/time/strftime.c b/src/time/strftime.c index 6a6f91a..39d4c59 100644 --- a/src/time/strftime.c +++ b/src/time/strftime.c @@ -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; From 26e54af8e0b185596d1eb08a41590123d85c7fd1 Mon Sep 17 00:00:00 2001 From: Lephenixnoir Date: Mon, 22 Aug 2022 19:00:13 +0200 Subject: [PATCH 04/12] stdlib: scanf-friendly strto* functions --- CMakeLists.txt | 1 + include/fxlibc/printf.h | 22 +++---- src/stdio/scanf/scan.c | 37 +++++++++++ src/stdio/stdio_p.h | 45 +++++++++++++ src/stdlib/stdlib_p.h | 13 ++-- src/stdlib/strto_fp.c | 141 +++++++++++++++++++++++----------------- src/stdlib/strto_int.c | 47 +++++++------- src/stdlib/strtod.c | 14 +++- src/stdlib/strtof.c | 14 +++- src/stdlib/strtol.c | 14 +++- src/stdlib/strtold.c | 14 +++- src/stdlib/strtoll.c | 14 +++- src/stdlib/strtoul.c | 14 +++- src/stdlib/strtoull.c | 14 +++- 14 files changed, 288 insertions(+), 116 deletions(-) create mode 100644 src/stdio/scanf/scan.c create mode 100644 src/stdio/stdio_p.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 71c6693..7333e3e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -140,6 +140,7 @@ set(SOURCES src/stdio/puts.c src/stdio/remove.c src/stdio/rewind.c + src/stdio/scanf/scan.c src/stdio/setbuf.c src/stdio/setvbuf.c src/stdio/snprintf.c diff --git a/include/fxlibc/printf.h b/include/fxlibc/printf.h index bac624e..a6b6c67 100644 --- a/include/fxlibc/printf.h +++ b/include/fxlibc/printf.h @@ -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: _ diff --git a/src/stdio/scanf/scan.c b/src/stdio/scanf/scan.c new file mode 100644 index 0000000..1bc93ee --- /dev/null +++ b/src/stdio/scanf/scan.c @@ -0,0 +1,37 @@ +#include +#include "../stdio_p.h" +#include "../../stdlib/stdlib_p.h" + +void __scanf_start(struct __scanf_input *__in) +{ + + if(__in->fp) + __in->buffer = fgetc(__in->fp); + else { + __in->buffer = *__in->str; + __in->str += (__in->buffer != 0); + } +} + +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--; +} diff --git a/src/stdio/stdio_p.h b/src/stdio/stdio_p.h new file mode 100644 index 0000000..150fc22 --- /dev/null +++ b/src/stdio/stdio_p.h @@ -0,0 +1,45 @@ +#ifndef __STDIO_P_H__ +# define __STDIO_P_H__ + +#include + +/* +** General utilities for scanf(); we expose them here as we use subfunctions of +** strto*() from 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; + int buffer; +}; + +/* 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); + return c; +} + +/* 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__ */ diff --git a/src/stdlib/stdlib_p.h b/src/stdlib/stdlib_p.h index 5831031..c368086 100644 --- a/src/stdlib/stdlib_p.h +++ b/src/stdlib/stdlib_p.h @@ -3,6 +3,7 @@ #include #include +#include "../stdio/stdio_p.h" /* ** Parse an integer from a string. This is the base function for strtol, @@ -23,8 +24,7 @@ ** expensive. */ int __strto_int( - char const * restrict __ptr, - char ** restrict __endptr, + struct __scanf_input *__input, int __base, long *__outl, long long *__outll, @@ -39,10 +39,9 @@ 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); #endif /*__STDLIB_P_H__*/ diff --git a/src/stdlib/strto_fp.c b/src/stdlib/strto_fp.c index d9b6cef..14e089e 100644 --- a/src/stdlib/strto_fp.c +++ b/src/stdlib/strto_fp.c @@ -1,3 +1,4 @@ +#include "stdlib_p.h" #include #include @@ -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, +static bool parse_digits(struct __scanf_input *input, SIGNIFICAND_TYPE *digits, long *exponent, bool hexadecimal) { - 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,14 @@ 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; true; i++) { + c = __scanf_peek(input); + if(!(isdigit(c) || + (hexadecimal && isxdigit(c)) || + (c == dot_character && !dot_found))) break; + __scanf_in(input); - /* 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 +68,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 +81,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 +89,54 @@ 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(input); + long e = 0; + if(__strto_int(input, 10, &e, NULL, false) == 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) +{ /* Skip initial whitespace */ - while(isspace(*ptr)) ptr++; + while(isspace(__scanf_peek(input))) __scanf_in(input); /* 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(input); int errno_value = 0; - bool valid = true; + bool valid = false; /* Result variable */ if(out) *out = 0.0; @@ -135,47 +144,64 @@ 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(input); + if(c == ')') break; + 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; - } - else if(!strncasecmp(ptr, "inf", 3)) { - if(out) *out = __builtin_inf(); - if(outf) *outf = __builtin_inff(); - if(outl) *outl = __builtin_infl(); - ptr += 3; + valid = true; } 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(input); + if(tolower(__scanf_peek(input)) == 'x') { + __scanf_in(input); + hexa = true; + } + /* Count the 0 as a digit */ + else valid = true; + } + if(hexa) { + valid |= parse_digits(input, &digits, &e, true); 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); - + valid |= parse_digits(input, &digits, &e, false); 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 +226,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; } diff --git a/src/stdlib/strto_int.c b/src/stdlib/strto_int.c index 8b6d298..6bbb20d 100644 --- a/src/stdlib/strto_int.c +++ b/src/stdlib/strto_int.c @@ -4,19 +4,17 @@ #include #include -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) { - /* 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); /* 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(input); /* Use unsigned variables as only these have defined overflow */ unsigned long xl = 0; @@ -26,29 +24,34 @@ 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(input); + if((base == 0 || base == 16) && + tolower(__scanf_peek(input)) == 'x') { + __scanf_in(input); + 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(base == 0) base = 10; - } /* Read digits */ while(1) { 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 +65,7 @@ int __strto_int(char const * restrict ptr, char ** restrict endptr, int base, errno_value = ERANGE; } - ptr++; + __scanf_in(input); } /* Handle sign and range */ @@ -101,6 +104,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; } diff --git a/src/stdlib/strtod.c b/src/stdlib/strtod.c index 80716fd..130233b 100644 --- a/src/stdlib/strtod.c +++ b/src/stdlib/strtod.c @@ -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); + __scanf_end(&in); + + if(err != 0) + errno = err; + if(err != EINVAL && endptr) + *endptr = (char *)in.str; return d; } diff --git a/src/stdlib/strtof.c b/src/stdlib/strtof.c index 271aed0..38240ea 100644 --- a/src/stdlib/strtof.c +++ b/src/stdlib/strtof.c @@ -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); + __scanf_end(&in); + + if(err != 0) + errno = err; + if(err != EINVAL && endptr) + *endptr = (char *)in.str; return f; } diff --git a/src/stdlib/strtol.c b/src/stdlib/strtol.c index b7da918..c0609b3 100644 --- a/src/stdlib/strtol.c +++ b/src/stdlib/strtol.c @@ -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); + __scanf_end(&in); + + if(err != 0) + errno = err; + if(err != EINVAL && endptr) + *endptr = (char *)in.str; return n; } diff --git a/src/stdlib/strtold.c b/src/stdlib/strtold.c index fcb8474..bf6421b 100644 --- a/src/stdlib/strtold.c +++ b/src/stdlib/strtold.c @@ -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); + __scanf_end(&in); + + if(err != 0) + errno = err; + if(err != EINVAL && endptr) + *endptr = (char *)in.str; return ld; } diff --git a/src/stdlib/strtoll.c b/src/stdlib/strtoll.c index ad1c972..2defd3d 100644 --- a/src/stdlib/strtoll.c +++ b/src/stdlib/strtoll.c @@ -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); + __scanf_end(&in); + + if(err != 0) + errno = err; + if(err != EINVAL && endptr) + *endptr = (char *)in.str; return n; } diff --git a/src/stdlib/strtoul.c b/src/stdlib/strtoul.c index 2a6294b..a92c265 100644 --- a/src/stdlib/strtoul.c +++ b/src/stdlib/strtoul.c @@ -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); + __scanf_end(&in); + + if(err != 0) + errno = err; + if(err != EINVAL && endptr) + *endptr = (char *)in.str; return n; } diff --git a/src/stdlib/strtoull.c b/src/stdlib/strtoull.c index d643947..aad4bd3 100644 --- a/src/stdlib/strtoull.c +++ b/src/stdlib/strtoull.c @@ -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); + __scanf_end(&in); + + if(err != 0) + errno = err; + if(err != EINVAL && endptr) + *endptr = (char *)in.str; return n; } From 95e33092ec6edd090173204a3c721d59a4ae0ce3 Mon Sep 17 00:00:00 2001 From: Lephenixnoir Date: Wed, 7 Sep 2022 21:02:01 +0200 Subject: [PATCH 05/12] stdlib: add fileno (DONE) --- CMakeLists.txt | 1 + STATUS | 1 + include/stdio.h | 1 + src/stdio/fileno.c | 6 ++++++ 4 files changed, 9 insertions(+) create mode 100644 src/stdio/fileno.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 7333e3e..81a9665 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -112,6 +112,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 diff --git a/STATUS b/STATUS index d6016d6..15d2be2 100644 --- a/STATUS +++ b/STATUS @@ -99,6 +99,7 @@ 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 diff --git a/include/stdio.h b/include/stdio.h index 205c33a..e489f7c 100644 --- a/include/stdio.h +++ b/include/stdio.h @@ -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); /* diff --git a/src/stdio/fileno.c b/src/stdio/fileno.c new file mode 100644 index 0000000..8d70343 --- /dev/null +++ b/src/stdio/fileno.c @@ -0,0 +1,6 @@ +#include + +int fileno(FILE *fp) +{ + return fp->fd; +} From 7c4de3e2950f36a05d6f62bfdfc13b4ef0b35fc4 Mon Sep 17 00:00:00 2001 From: Lephenixnoir Date: Wed, 7 Sep 2022 21:03:53 +0200 Subject: [PATCH 06/12] dso, stdlib: __cxa_atexit(), __dso_handle, atexit() (TEST) --- CMakeLists.txt | 3 +++ STATUS | 4 ++- include/stdlib.h | 1 + src/dso.c | 62 +++++++++++++++++++++++++++++++++++++++++++++ src/stdlib/atexit.c | 8 ++++++ src/stdlib/exit.c | 5 +++- 6 files changed, 81 insertions(+), 2 deletions(-) create mode 100644 src/dso.c create mode 100644 src/stdlib/atexit.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 81a9665..1405997 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -70,6 +70,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 @@ -157,6 +159,7 @@ set(SOURCES # 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 diff --git a/STATUS b/STATUS index 15d2be2..754cb1f 100644 --- a/STATUS +++ b/STATUS @@ -162,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 @@ -173,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 7.21.2.1 memcpy - diff --git a/include/stdlib.h b/include/stdlib.h index 8267c6a..5e796ad 100644 --- a/include/stdlib.h +++ b/include/stdlib.h @@ -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. */ diff --git a/src/dso.c b/src/dso.c new file mode 100644 index 0000000..0dc7215 --- /dev/null +++ b/src/dso.c @@ -0,0 +1,62 @@ +#include + +/* 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[ATEXIT_MAX]; +static int _dtor_count = 0; + +int __cxa_atexit(void (*f)(void *), void *p, void *d) +{ + if(_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; +} diff --git a/src/stdlib/atexit.c b/src/stdlib/atexit.c new file mode 100644 index 0000000..fb3cc88 --- /dev/null +++ b/src/stdlib/atexit.c @@ -0,0 +1,8 @@ +#include + +extern int __cxa_atexit(void (*f)(void *), void *p, void *d); + +int atexit(void (*f)(void)) +{ + return __cxa_atexit((void (*)(void *))f, NULL, NULL); +} diff --git a/src/stdlib/exit.c b/src/stdlib/exit.c index 596a48a..27025f4 100644 --- a/src/stdlib/exit.c +++ b/src/stdlib/exit.c @@ -1,8 +1,11 @@ #include +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 */ From 1da9d10226b24342dfba1c88be0304ebd5da539d Mon Sep 17 00:00:00 2001 From: Lephenixnoir Date: Sun, 30 Oct 2022 11:19:26 +0100 Subject: [PATCH 07/12] alloca: provide --- STATUS | 3 +++ include/alloca.h | 21 +++++++++++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 include/alloca.h diff --git a/STATUS b/STATUS index 754cb1f..f6d260e 100644 --- a/STATUS +++ b/STATUS @@ -225,6 +225,9 @@ TEST: Function/symbol/macro needs to be tested 7.25 TODO (not a priority) +(EXT) + (EXT) alloca - + # Supporting locales What if we wanted to support more locales? diff --git a/include/alloca.h b/include/alloca.h new file mode 100644 index 0000000..343d16d --- /dev/null +++ b/include/alloca.h @@ -0,0 +1,21 @@ +#ifndef __ALLOCA_H__ +# define __ALLOCA_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#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__*/ From 465655674b9c5687eaaa2e6c9463429f69eff0fd Mon Sep 17 00:00:00 2001 From: Lephenixnoir Date: Sun, 1 Jan 2023 19:23:08 +0100 Subject: [PATCH 08/12] dso: dynamically allocate destructor table This saves static memory on mono targets where it's scarce. --- src/dso.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/dso.c b/src/dso.c index 0dc7215..8b572ba 100644 --- a/src/dso.c +++ b/src/dso.c @@ -1,4 +1,4 @@ -#include +#include /* We don't support shared object loading, so provide a single handle */ __attribute__((visibility("hidden"))) @@ -13,12 +13,18 @@ struct dtor { void *d; }; -static struct dtor _dtors[ATEXIT_MAX]; +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(_dtor_count >= ATEXIT_MAX) + if(!_dtors || _dtor_count >= ATEXIT_MAX) return 1; _dtors[_dtor_count++] = (struct dtor){ f, p, d }; return 0; From c85182d07e367c41bbe857b10c77a368dccec5d4 Mon Sep 17 00:00:00 2001 From: Yann MAGNIN Date: Sun, 8 Jan 2023 13:11:13 +0100 Subject: [PATCH 09/12] update vxSDK integration --- .gitignore | 1 + CMakeLists.txt | 4 +++- cmake/toolchain-vhex.cmake | 11 ++++------- vxsdk.toml | 22 +++++++++++++++++----- 4 files changed, 25 insertions(+), 13 deletions(-) diff --git a/.gitignore b/.gitignore index 243922d..e87da89 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ /prefix *.txt !CMakeLists.txt +.vxsdk/ # GiteaPC config files giteapc-config.make diff --git a/CMakeLists.txt b/CMakeLists.txt index 1405997..73adc85 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/cmake/toolchain-vhex.cmake b/cmake/toolchain-vhex.cmake index a7de855..52bc5f2 100644 --- a/cmake/toolchain-vhex.cmake +++ b/cmake/toolchain-vhex.cmake @@ -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}) diff --git a/vxsdk.toml b/vxsdk.toml index 76aec72..f14a820 100644 --- a/vxsdk.toml +++ b/vxsdk.toml @@ -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' From 7eb2a6e8ab3f4a117154d05b7c0ef6e2c41fc5c0 Mon Sep 17 00:00:00 2001 From: Yann MAGNIN Date: Sun, 8 Jan 2023 16:07:43 +0100 Subject: [PATCH 10/12] add shared lib generation support for Vhex --- CMakeLists.txt | 39 ++++++++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 73adc85..23b31bb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -261,22 +261,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) From 74b805e8b55d196cf4944bc4049de13d5ba1fb54 Mon Sep 17 00:00:00 2001 From: Lephenixnoir Date: Sun, 19 Mar 2023 00:24:28 +0100 Subject: [PATCH 11/12] stdlib: automatically seed PRNG with srand(1) --- 3rdparty/tinymt32/rand.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/3rdparty/tinymt32/rand.c b/3rdparty/tinymt32/rand.c index a9266d1..64d18ef 100644 --- a/3rdparty/tinymt32/rand.c +++ b/3rdparty/tinymt32/rand.c @@ -12,3 +12,9 @@ int rand(void) { return tinymt32_generate_uint32(&random) & 0x7fffffff; } + +__attribute__((constructor)) +static void init_prng(void) +{ + srand(1); +} From 031e0ccb65fe55ac0c1d71769fe7dd536cdbd12a Mon Sep 17 00:00:00 2001 From: Lephenixnoir Date: Sat, 1 Apr 2023 20:25:18 +0200 Subject: [PATCH 12/12] bump version to 1.4.5 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 23b31bb..edbea0d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.15) -project(FxLibc VERSION 1.4.4 LANGUAGES C ASM) +project(FxLibc VERSION 1.4.5 LANGUAGES C ASM) set(CMAKE_INSTALL_MESSAGE LAZY)