Compare commits

...

7 Commits

Author SHA1 Message Date
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
19 changed files with 659 additions and 52 deletions

View File

@ -124,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
@ -145,19 +146,24 @@ 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

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

@ -0,0 +1,17 @@
#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);
int count = __scanf(&in, fmt, &args);
va_end(args);
return count;
}

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

@ -0,0 +1,17 @@
#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);
int count = __scanf(&in, fmt, &args);
va_end(args);
return count;
}

View File

@ -1,37 +1,498 @@
#include <stdio.h>
#include "../stdio_p.h"
#include "../../stdlib/stdlib_p.h"
#include <stdio.h>
#include <ctype.h>
#include <stdbool.h>
void __scanf_start(struct __scanf_input *__in)
void __scanf_start(struct __scanf_input *in)
{
if(__in->fp)
__in->buffer = fgetc(__in->fp);
if(in->fp)
in->buffer = fgetc(in->fp);
else {
__in->buffer = *__in->str;
__in->str += (__in->buffer != 0);
in->buffer = (*in->str ? *in->str : EOF);
in->str += (in->buffer != EOF);
}
}
int __scanf_fetch(struct __scanf_input *__in)
int __scanf_fetch(struct __scanf_input *in)
{
if(__in->fp)
return fgetc(__in->fp);
if(in->fp)
return fgetc(in->fp);
int c = *__in->str;
int c = *in->str;
if(c == 0)
return EOF;
__in->str++;
in->str++;
return c;
}
void __scanf_end(struct __scanf_input *__in)
void __scanf_end(struct __scanf_input *in)
{
if(__in->buffer == EOF)
if(in->buffer == EOF)
return;
if(__in->fp)
ungetc(__in->buffer, __in->fp);
if(in->fp)
ungetc(in->buffer, in->fp);
else
__in->str--;
in->str--;
}
enum
{
MODSKIP,
MODCHAR,
MODSHORT,
MODNORMAL,
MODLONG,
MODLONGLONG,
MODLONGDOUBLE,
MODINTMAXT,
MODSIZET,
MODPTRDIFFT,
};
void __purge_space( struct __scanf_input * __restrict__ in )
{
while (isspace(__scanf_peek(in))) __scanf_in(in);
}
void __scanf_store_i(int64_t value, int size, va_list *args)
{
if(size == MODSKIP) return;
if(size == MODCHAR)
*va_arg(*args, int8_t *) = value;
else if(size == MODSHORT)
*va_arg(*args, int16_t *) = value;
else if(size == MODLONGLONG)
*va_arg(*args, int64_t *) = value;
else if(size == MODINTMAXT)
*va_arg(*args, intmax_t *) = value;
else if(size == MODSIZET)
*va_arg(*args, size_t *) = value;
else if(size == MODPTRDIFFT)
*va_arg(*args, ptrdiff_t *) = value;
else
*va_arg(*args, int *) = value;
}
void __scanf_store_d(long double value, int size, va_list *args)
{
if (size==MODSKIP) return;
if(size == MODLONG)
*va_arg(*args, double *) = value;
else if(size == MODLONGDOUBLE)
*va_arg(*args, long double *) = value;
else
*va_arg(*args, float *) = value;
}
/* STATUS OF __scanf DEVELOPMENT */
// XX = not done yet (but will be done)
// OK = OK, done and tested
// -- = not applicable
// NO = not supported (and will not be done) only for %lc as long char are not supported by gint
/*************************************************************************************************************/
/* Specifier * Explanation * num * hh * h *none* l * ll * j * z * t * L */
/*************************************************************************************************************/
/* % * Parse literal '%' * -- * -- * -- * OK * -- * -- * -- * -- * -- * -- */
/*************************************************************************************************************/
/* c * match a char or several char * OK * -- * -- * OK * NO * -- * -- * -- * -- * -- */
/*************************************************************************************************************/
/* s * match a string * OK * -- * -- * OK * NO * -- * -- * -- * -- * -- */
/*************************************************************************************************************/
/* [set] * match a set of char * OK * -- * -- * OK * -- * -- * -- * -- * -- * -- */
/*************************************************************************************************************/
/* d * match a decimal integer * OK * OK * OK * OK * OK * OK * OK * OK * OK * -- */
/*************************************************************************************************************/
/* i * match an integer * OK * OK * OK * OK * OK * OK * OK * OK * OK * -- */
/*************************************************************************************************************/
/* d * match an unsigned decimal integer * OK * OK * OK * OK * OK * OK * OK * OK * OK * -- */
/*************************************************************************************************************/
/* o * match a unsigned octal integer * OK * OK * OK * OK * OK * OK * OK * OK * OK * -- */
/*************************************************************************************************************/
/* x,X * match a unsigned hexadecimal integer * OK * OK * OK * OK * OK * OK * OK * OK * OK * -- */
/*************************************************************************************************************/
/* n * return the nb of chars read so far * -- * -- * -- * OK * -- * -- * -- * -- * -- * -- */
/*************************************************************************************************************/
/* a,A * match a floating point number * OK * -- * -- * OK * OK * -- * -- * -- * -- * OK */
/* e,E * match a floating point number * OK * -- * -- * OK * OK * -- * -- * -- * -- * OK */
/* f,F * match a floating point number * OK * -- * -- * OK * OK * -- * -- * -- * -- * OK */
/* g,G * match a floating point number * OK * -- * -- * OK * OK * -- * -- * -- * -- * OK */
/*************************************************************************************************************/
/* p * match a pointer * -- * -- * -- * OK * -- * -- * -- * -- * -- * -- */
/*************************************************************************************************************/
// %ms and %m[set] are not implemented (with memory allocation while parsing a chain or a set of characters)
/* list of allowed char given by a set %[], this is updated at every set */
bool __asciiallowed[256] = { true };
/* unallow all the char for the current set */
void __unallow_all_set( void )
{
for(int u =0; u<=255; u++)
__asciiallowed[u]=false;
}
/* allow all the char for the current set */
void __allow_all_set( void )
{
for(int u =0; u<=255; u++)
__asciiallowed[u]=true;
}
/* allo a range of char for the current set */
/* note1 : c1 and c2 do not to be sorted */
/* note2 : not sur if C standard requires to be ordered or not */
void __define_set_range( char c1, char c2, bool value )
{
char beg = (c1 < c2 ? c1 : c2 );
char end = (c1 >= c2 ? c1 : c2 );
for (int u=beg; u<=end; u++)
__asciiallowed[u] = value;
}
/* return true if the char is in the allowed set or false otherwise */
bool __is_allowed(int c)
{
return (c != EOF) && __asciiallowed[c];
}
/* return 0 if Ok or -1 if syntax err in the set format */
int __scanset(char const * __restrict__ format, int *pos )
{
int __sor = 0;
int __eor = 0;
bool __neg = false;
__unallow_all_set();
(*pos)++;
// next will be a "negation" set
if (format[*pos] == '^' ) {
__neg = true;
(*pos)++;
__allow_all_set();
// the char ']' is part of the set
if (format[*pos] == ']' ) {
__asciiallowed[ ']' ] = !__neg;
(*pos)++;
}
}
// the char ']' is included in the allowed set
else if (format[*pos] == ']' ) {
__neg = false;
// the char ']' is part of the set
if (format[*pos] == ']' ) {
__asciiallowed[ ']' ] = !__neg;
(*pos)++;
}
}
while(1) {
// we find a '-' so need to check if we are considering a range or the char '-' only
if (format[*pos]=='-') {
// the char '-' is included in the allowed set
if (format[*pos+1]==']') {
__asciiallowed[ '-' ] = !__neg; // if set in very final position before']', this is the char '-' only
(*pos)++;
// we have now finished the reading of the set cause the following char is ']'
return 0;
}
// the char '-' indicates a range of char to be included into the set
else {
(*pos)++;
__eor = format[*pos];
__define_set_range( __sor, __eor, !__neg );
}
}
// we find the char ']' so it means we reach the end of this set
else if (format[*pos]==']') return 0;
// if we reach the '\0' we have a syntax problem
else if (format[*pos]=='\0') return -1;
// we are considering one particular char and prepare for a potential range if we find the char '-' later on
else {
__sor = format[*pos];
__asciiallowed[ __sor ] = !__neg;
(*pos)++;
}
}
}
int __scanf(
struct __scanf_input * __restrict__ in,
char const * __restrict__ format,
va_list *args)
{
bool skip = false;
int MOD = MODNORMAL;
in->readsofar = 0; // we haven't started to read char from the input stream
in->readmaxlength = -1; // no specific maximum length to read is defined yet
int validrets = 0; // to be incremented each time we successfully read and store an input as per the format
int err = 0; // err control on __strto_xx( ) functions
int user_length = 0; // length defined by user with a %xx modifier
int pos = 0; // current pos in the format string
__scanf_start( in );
// TODO: No __scanf_end() in any of the "return validrets"!!
while( format[pos] != 0 ) {
in->readmaxlength = -1;
user_length = 0;
MOD = MODNORMAL;
skip = false;
__allow_all_set();
if( format[pos] == ' ' ) {
__purge_space(in);
}
// we will have to manage a given format
else if( format[pos] == '%' ) {
in->readmaxlength = INT_MAX;
// main loop
loopagain:
pos++;
switch( format[pos] ) {
// we need to decrypt the corresponding scanf set of character
case '[': {
err = __scanset( format, &pos );
if (err!=0) return validrets;
int currentlength = 0;
// we need to assign the read char to the corresponding pointer
char *c = skip ? NULL : va_arg(*args, char *);
for(unsigned int u=0; u<in->readmaxlength; u++) {
int temp = __scanf_peek(in);
if(__is_allowed(temp)) {
__scanf_in(in);
if(c) *c++ = temp;
currentlength++;
}
else if(temp==EOF && !currentlength && !validrets)
return EOF;
else break;
}
if(!currentlength)
return validrets;
*c = '\0';
validrets += !skip;
break;
}
// return the number of char read so far (cannot be skipped %*n is not valid)
case 'n': {
*(int*) va_arg( *args, int* ) = in->readsofar;
break;
}
// we are expecting the char '%' to be in the input stream, if not err and return
case '%': {
if (__scanf_peek(in) != '%') return validrets;
else __scanf_in( in );
break;
}
// the next read, even if valid, will not be stored
case '*': {
skip = true;
goto loopagain;
break;
}
case 'h': {
if (MOD==MODNORMAL || MOD==MODSHORT) {
MOD--;
goto loopagain;
}
else return validrets; // we cannot have %hhh format modifier --> err
break;
}
case 'l': {
if (MOD==MODNORMAL || MOD==MODLONG) {
MOD++;
goto loopagain;
}
else return validrets; // we cannot have %ll format modifier --> err
break;
}
case 'L': {
if (MOD==MODNORMAL) {
MOD=MODLONGDOUBLE;
goto loopagain;
}
else return validrets; // we cannot have %LL format modifier --> err
break;
}
case 'j': {
if (MOD==MODNORMAL) {
MOD=MODINTMAXT;
goto loopagain;
}
else return validrets; // we cannot have %LL format modifier --> err
break;
}
case 'z': {
if (MOD==MODNORMAL) {
MOD=MODSIZET;
goto loopagain;
}
else return validrets; // we cannot have %LL format modifier --> err
break;
}
case 't': {
if (MOD==MODNORMAL) {
MOD=MODPTRDIFFT;
goto loopagain;
}
else return validrets; // we cannot have %LL format modifier --> err
break;
}
case '0' ... '9': {
user_length = user_length * 10 + (int) ( format[pos] - '0' );
in->readmaxlength = user_length;
goto loopagain;
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;
err = __strto_int(in, base, NULL, &temp, use_unsigned,
in->readmaxlength);
if (err == EOF && validrets == 0) return EOF;
if (err != 0) return validrets;
if (skip) __scanf_store_i( temp, MODSKIP, args );
else __scanf_store_i( temp, MOD, args );
validrets++;
break;
}
case 'a':
case 'A':
case 'e':
case 'E':
case 'f':
case 'F':
case 'g':
case 'G': {
// read a double from the current input stream
// and store in the corresponding arg as a char by reference
long double temp;
err = __strto_fp( in, NULL, NULL, &temp,
in->readmaxlength);
if (err == EOF && validrets == 0) return EOF;
if (err != 0) return validrets;
if (skip) __scanf_store_d( temp, MODSKIP, args );
else __scanf_store_d( temp, MOD, args );
validrets++;
break;
}
case 'p': {
long int temp;
if (!skip) {
void *p = (void *) va_arg( *args, void** ); // get the adress of the target pointer (void**)
err = __strto_int( in, 0, p, NULL, true,
in->readmaxlength);
}
else err = __strto_int( in, 0, &temp, NULL, true,
in->readmaxlength);
if (err == 0) validrets++;
else return validrets;
skip = false;
break;
}
case 'c': {
if(in->readmaxlength == INT_MAX)
in->readmaxlength = 1;
char *c = skip ? NULL : va_arg(*args, char *);
for(unsigned int u = 0; u < in->readmaxlength; u++) {
int temp = __scanf_in(in);
if(temp==EOF) return EOF;
else if(c) *c++ = temp;
}
validrets += !skip;
break;
}
case 's': {
char temp;
int curstrlength = 0;
__purge_space(in);
char *c = skip ? NULL : va_arg(*args, char *);
for(unsigned int u = 0; u < in->readmaxlength; u++) {
temp = __scanf_peek(in);
if(temp==EOF && curstrlength==0) return validrets;
if(isspace(temp) || ((temp==EOF && curstrlength!=0))) {
if(c) {
*c = 0;
validrets++;
}
break;
}
else {
int temp = __scanf_in( in );
if(c)
*c++ = temp;
curstrlength++;
}
}
break;
}
}
}
// we are looking for a specific character in the input stream
else {
// if the next char of the stream is corresponding, we validate the read and go to the following char
if(format[pos] == __scanf_peek( in )) {
__scanf_in( in );
pos++;
}
else return validrets; // else we return the number of valid read
}
pos++;
}
__scanf_end( in );
return validrets;
}

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

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

View File

@ -2,6 +2,7 @@
# define __STDIO_P_H__
#include <stdio.h>
#include <stdbool.h>
/*
** General utilities for scanf(); we expose them here as we use subfunctions of
@ -16,9 +17,24 @@
struct __scanf_input {
char const * __restrict__ str;
FILE *fp;
// max char to read from the input stream as per user length modifier
unsigned int readmaxlength;
// total number of char read so far in the current call of a XYscanf() function (to return a %n when required)
int readsofar;
int buffer;
};
/* 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);
@ -30,9 +46,17 @@ static inline int __scanf_in(struct __scanf_input *__in)
{
int c = __in->buffer;
__in->buffer = __scanf_fetch(__in);
__in->readsofar++;
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)
{

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

@ -0,0 +1,11 @@
#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,
};
return __scanf(&in, fmt, &args);
}

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

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

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

@ -0,0 +1,12 @@
#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,
};
return __scanf(&in, fmt, &args);
}

View File

@ -3,6 +3,7 @@
#include <stdlib.h>
#include <stdbool.h>
#include <limits.h>
#include "../stdio/stdio_p.h"
/*
@ -22,13 +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(
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
@ -42,6 +47,7 @@ int __strto_fp(
struct __scanf_input *__input,
double *__out,
float *__outf,
long double *__outl);
long double *__outl,
int __N);
#endif /*__STDLIB_P_H__*/

View File

@ -38,8 +38,8 @@
** -> 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 bool parse_digits(struct __scanf_input *input,
SIGNIFICAND_TYPE *digits, long *exponent, bool hexadecimal)
static int parse_digits(struct __scanf_input *input,
SIGNIFICAND_TYPE *digits, long *exponent, bool hexadecimal, int *N)
{
bool dot_found = false;
int digits_found=0, c=0;
@ -53,12 +53,14 @@ static bool parse_digits(struct __scanf_input *input,
int dot_character = '.';
int exp_character = (hexadecimal ? 'p' : 'e');
for(int i = 0; true; i++) {
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(input);
__scanf_in_limit(input, N);
if(c == dot_character) {
dot_found = true;
@ -102,9 +104,10 @@ static bool parse_digits(struct __scanf_input *input,
set correctly */
struct __scanf_input backup = *input;
__scanf_in(input);
__scanf_in_limit(input, N);
long e = 0;
if(__strto_int(input, 10, &e, NULL, false) == 0)
// TODO: strto_fp: Pass limit to __strto_int
if(__strto_int(input, 10, &e, NULL, false, *N) == 0)
*exponent += e;
else
*input = backup;
@ -124,16 +127,21 @@ static bool expect(struct __scanf_input *input, char const *sequence)
}
int __strto_fp(struct __scanf_input *input, double *out, float *outf,
long double *outl)
long double *outl, int N)
{
/* Skip initial whitespace */
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;
int sign = __scanf_peek(input);
if(sign == '-') negative = true;
if(sign == '-' || sign == '+') __scanf_in(input);
if(sign == '-' || sign == '+') __scanf_in_limit(input, &N);
int errno_value = 0;
bool valid = false;
@ -154,8 +162,10 @@ int __strto_fp(struct __scanf_input *input, double *out, float *outf,
if(__scanf_peek(input) == '(') {
while(i < 31) {
int c = __scanf_in(input);
int c = __scanf_in_limit(input, &N);
if(c == ')') break;
if(c == EOF || N <= 0)
return EOF;
arg[i++] = c;
}
arg[i] = 0;
@ -177,6 +187,9 @@ int __strto_fp(struct __scanf_input *input, double *out, float *outf,
if(outl) *outl = __builtin_infl();
valid = true;
}
else if(__scanf_peek(input) == EOF) {
return EOF;
}
else {
SIGNIFICAND_TYPE digits = 0;
long e = 0;
@ -185,9 +198,9 @@ int __strto_fp(struct __scanf_input *input, double *out, float *outf,
not 0x isn't a problem. */
bool hexa = false;
if(__scanf_peek(input) == '0') {
__scanf_in(input);
__scanf_in_limit(input, &N);
if(tolower(__scanf_peek(input)) == 'x') {
__scanf_in(input);
__scanf_in_limit(input, &N);
hexa = true;
}
/* Count the 0 as a digit */
@ -195,13 +208,19 @@ int __strto_fp(struct __scanf_input *input, double *out, float *outf,
}
if(hexa) {
valid |= parse_digits(input, &digits, &e, true);
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 {
valid |= parse_digits(input, &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);

View File

@ -5,16 +5,19 @@
#include <limits.h>
int __strto_int(struct __scanf_input *input, int base, long *outl,
long long *outll, bool use_unsigned)
long long *outll, bool use_unsigned, int N)
{
/* Skip initial whitespace */
while(isspace(__scanf_peek(input))) __scanf_in(input);
if(N <= 0)
return EOF;
/* Accept a sign character */
bool negative = false;
int sign = __scanf_peek(input);
if(sign == '-') negative = true;
if(sign == '-' || sign == '+') __scanf_in(input);
if(sign == '-' || sign == '+') __scanf_in_limit(input, &N);
/* Use unsigned variables as only these have defined overflow */
unsigned long xl = 0;
@ -25,10 +28,10 @@ int __strto_int(struct __scanf_input *input, int base, long *outl,
/* Read prefixes and determine base */
if(__scanf_peek(input) == '0') {
__scanf_in(input);
__scanf_in_limit(input, &N);
if((base == 0 || base == 16) &&
tolower(__scanf_peek(input)) == 'x') {
__scanf_in(input);
__scanf_in_limit(input, &N);
base = 16;
}
/* If we don't consume the x then count the 0 as a digit */
@ -36,11 +39,13 @@ int __strto_int(struct __scanf_input *input, int base, long *outl,
if(base == 0)
base = 8;
}
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;
int c = __scanf_peek(input);
if(isdigit(c)) v = c - '0';
@ -65,7 +70,7 @@ int __strto_int(struct __scanf_input *input, int base, long *outl,
errno_value = ERANGE;
}
__scanf_in(input);
__scanf_in_limit(input, &N);
}
/* Handle sign and range */

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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