Compare commits
7 Commits
031e0ccb65
...
69eadb67d2
Author | SHA1 | Date |
---|---|---|
Lephenixnoir | 69eadb67d2 | |
Lephenixnoir | b11c059c0f | |
Lephenixnoir | 2215b3c267 | |
Lephenixnoir | b61cc096d9 | |
Lephenixnoir | c776336a0d | |
Lephenixnoir | d8a55b728d | |
Sylvain PILLOT | 09b33ca2fa |
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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__*/
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue