forked from Vhex-Kernel-Core/fxlibc
426 lines
12 KiB
C
426 lines
12 KiB
C
#include <stdio.h>
|
|
#include <ctype.h>
|
|
#include <fxlibc/scanf.h>
|
|
#include <fxlibc/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--;
|
|
}
|
|
|
|
|
|
enum
|
|
{
|
|
MODSHORTSHORT,
|
|
MODSHORT,
|
|
MODNORMAL,
|
|
MODLONG,
|
|
MODLONGLONG,
|
|
MODLONGDOUBLE,
|
|
};
|
|
|
|
|
|
void __scanf_store_i(int64_t value, int size, va_list *args)
|
|
{
|
|
if(size == MODSHORTSHORT)
|
|
*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
|
|
*va_arg(*args, int *) = value;
|
|
}
|
|
|
|
void __scanf_store_d(long double value, int size, va_list *args)
|
|
{
|
|
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)
|
|
|
|
/*************************************************************************************************************/
|
|
/* 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 * XX * -- * -- * XX * NO * -- * -- * -- * -- * -- */
|
|
/*************************************************************************************************************/
|
|
/* d * match a decimal integer * OK * OK * OK * OK * OK * OK * XX * XX * XX * -- */
|
|
/*************************************************************************************************************/
|
|
/* i * match an integer * OK * OK * OK * OK * OK * OK * XX * XX * XX * -- */
|
|
/*************************************************************************************************************/
|
|
/* d * match an unsigned decimal integer * OK * OK * OK * OK * OK * OK * XX * XX * XX * -- */
|
|
/*************************************************************************************************************/
|
|
/* o * match a unsigned octal integer * OK * OK * OK * OK * OK * OK * XX * XX * XX * -- */
|
|
/*************************************************************************************************************/
|
|
/* x,X * match a unsigned hexadecimal integer * OK * OK * OK * OK * OK * OK * XX * XX * XX * -- */
|
|
/*************************************************************************************************************/
|
|
/* d * match a decimal integer * OK * OK * OK * OK * OK * OK * XX * XX * XX * -- */
|
|
/*************************************************************************************************************/
|
|
/* 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 * mathc a pointer * -- * -- * -- * OK * -- * -- * -- * -- * -- * -- */
|
|
/*************************************************************************************************************/
|
|
|
|
|
|
// TODO : add the support of in->readsofar into strto_fp(...) and str_to_int(...)
|
|
// to be able to get correct '%n' returned value
|
|
|
|
|
|
int __scanf(
|
|
struct __scanf_input * __restrict__ in,
|
|
char const * __restrict__ format,
|
|
va_list *args)
|
|
{
|
|
int i=0;
|
|
int ret = 0;
|
|
int err = 0;
|
|
|
|
int mod = MODNORMAL;
|
|
int length = 0;
|
|
|
|
in->readsofar = 0;
|
|
in->readmaxlength = -1;
|
|
|
|
|
|
__scanf_start( in );
|
|
|
|
|
|
while( format[i]!=0 )
|
|
{
|
|
in->readmaxlength = -1;
|
|
|
|
if ( format[i] == ' ' ) // a space in the format string mean that all white-space after the last read and before the next one are to be removed
|
|
{
|
|
char flush;
|
|
for(;;)
|
|
{
|
|
flush = __scanf_peek( in ); // check if the next character is a whitespace
|
|
if (isspace(flush))
|
|
{
|
|
flush = __scanf_in( in ); // if so, we remove it from the pile and advance
|
|
}
|
|
else break; // if not, we can move to the following
|
|
}
|
|
in->readmaxlength = -1;
|
|
i++;
|
|
}
|
|
else if ( format[i] == '%' ) // read conversion
|
|
{
|
|
|
|
mod = MODNORMAL;
|
|
length = 0;
|
|
in->readmaxlength=-1;
|
|
|
|
loopagain:
|
|
|
|
i++;
|
|
switch( format[i] )
|
|
{
|
|
|
|
case 'h': // we start with MODNORMAL and decrease the length first to MODSHORT and after to MODSHORTSHORT
|
|
{
|
|
if(mod==MODNORMAL)
|
|
{
|
|
mod=MODSHORT;
|
|
goto loopagain;
|
|
}
|
|
else if(mod==MODSHORT)
|
|
{
|
|
mod=MODSHORTSHORT;
|
|
goto loopagain;
|
|
}
|
|
else
|
|
{
|
|
mod=MODNORMAL;
|
|
break; // we cannot have %hhh
|
|
}
|
|
}
|
|
|
|
case 'l': // we start with MODNORMAL and decrease the length first to MODLONG and after to MODLONGLONG
|
|
{
|
|
if(mod==MODNORMAL)
|
|
{
|
|
mod=MODLONG;
|
|
goto loopagain;
|
|
}
|
|
else if(mod==MODLONG)
|
|
{
|
|
mod=MODLONGLONG;
|
|
goto loopagain;
|
|
}
|
|
else
|
|
{
|
|
mod=MODNORMAL;
|
|
break; // we cannot have %lll
|
|
}
|
|
}
|
|
|
|
case 'L': // idem, but only for Double and limiter only to MODLONG (no long long double available)
|
|
{
|
|
mod=MODLONGDOUBLE;
|
|
goto loopagain;
|
|
}
|
|
|
|
case '0':
|
|
case '1':
|
|
case '2':
|
|
case '3':
|
|
case '4':
|
|
case '5':
|
|
case '6':
|
|
case '7':
|
|
case '8':
|
|
case '9':
|
|
{
|
|
length = length * 10 + (int) ( format[i] - '0');
|
|
in->readmaxlength = length;
|
|
goto loopagain;
|
|
}
|
|
|
|
case '%':
|
|
{
|
|
// NOthing to do this is just the character '%'
|
|
in->readmaxlength = -1;
|
|
break;
|
|
}
|
|
|
|
case 'c':
|
|
{
|
|
// read a char from the current input stream
|
|
// and store in the corresponding arg as a char by reference
|
|
char *c = (char *) va_arg( *args, char* );
|
|
|
|
if (in->readmaxlength==-1)
|
|
{
|
|
*c = __scanf_in( in );
|
|
}
|
|
else
|
|
{
|
|
for(int u = 0; u < in->readmaxlength; u++ )
|
|
{
|
|
*c = __scanf_in( in );
|
|
c++;
|
|
}
|
|
}
|
|
in->readmaxlength=-1;
|
|
ret++;
|
|
|
|
break;
|
|
}
|
|
|
|
case 'd':
|
|
{
|
|
// read a long int (in base 10 - decimal) from the current input stream
|
|
// and store in the corresponding arg as a char by reference
|
|
long long int temp;
|
|
err = __strto_int( in, 10, NULL, &temp, false ); // base is 10 as for strtol
|
|
if (err == 0)
|
|
{
|
|
__scanf_store_i( temp, mod, args );
|
|
mod = MODNORMAL;
|
|
ret++;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 'i':
|
|
{
|
|
// read a long int (in base determined by the first character) from the current input stream
|
|
// and store in the corresponding arg as a char by reference
|
|
|
|
long long int temp;
|
|
err = __strto_int( in, 0, NULL, &temp, false ); // base is 0 as for strtol
|
|
if (err == 0)
|
|
{
|
|
__scanf_store_i( temp, mod, args );
|
|
mod = MODNORMAL;
|
|
ret++;
|
|
}
|
|
break;
|
|
|
|
}
|
|
|
|
case 'u':
|
|
{
|
|
// read a unsigned long int (in base 10 - decimal) from the current input stream
|
|
// and store in the corresponding arg as a char by reference
|
|
long long int temp;
|
|
err = __strto_int( in, 10, NULL, &temp, true ); // base is 10 as for strtol
|
|
if (err == 0)
|
|
{
|
|
__scanf_store_i( temp, mod, args );
|
|
mod = MODNORMAL;
|
|
ret++;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 'o':
|
|
{
|
|
// read a unsigned long int (in base 8 - octal) from the current input stream
|
|
// and store in the corresponding arg as a char by reference
|
|
long long int temp;
|
|
err = __strto_int( in, 8, NULL, &temp, true ); // base is 8 as for strtol
|
|
if (err == 0)
|
|
{
|
|
__scanf_store_i( temp, mod, args );
|
|
mod = MODNORMAL;
|
|
ret++;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 'x':
|
|
case 'X':
|
|
{
|
|
// read a unsigned long int (in base 16 - hexadecimal) from the current input stream
|
|
// and store in the corresponding arg as a char by reference
|
|
long long int temp;
|
|
err = __strto_int( in, 16, NULL, &temp, true ); // base is 16 as for strtol
|
|
if (err == 0)
|
|
{
|
|
__scanf_store_i( temp, mod, args );
|
|
mod = MODNORMAL;
|
|
ret++;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 'a':
|
|
case 'A':
|
|
case 'e':
|
|
case 'E':
|
|
case 'f':
|
|
case 'F':
|
|
case 'g':
|
|
case 'G':
|
|
{
|
|
long double temp;
|
|
err = __strto_fp( in, NULL, NULL, &temp );
|
|
if (err == 0)
|
|
{
|
|
__scanf_store_d( temp, mod, args );
|
|
mod = MODNORMAL;
|
|
ret++;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 's':
|
|
{
|
|
// read a serie of non whitespace char from the current input stream
|
|
// and store in the corresponding arg as a char by reference
|
|
char *c = (char *) va_arg( *args, char* );
|
|
char temp;
|
|
if (in->readmaxlength==-1) // read the maximum number of non-whitespace characters
|
|
{
|
|
for(;;)
|
|
{
|
|
temp = __scanf_peek( in );
|
|
if (!isspace(temp) && temp != EOF)
|
|
{
|
|
*c = __scanf_in( in );
|
|
c++;
|
|
}
|
|
else break;
|
|
}
|
|
*c = '\0'; // add the mandatory terminal '\0'
|
|
}
|
|
else
|
|
{
|
|
for(int u = 0; u < in->readmaxlength; u++ )
|
|
{
|
|
temp = __scanf_peek( in );
|
|
if (!isspace(temp) && temp != EOF)
|
|
{
|
|
*c = __scanf_in( in );
|
|
c++;
|
|
}
|
|
else break;
|
|
}
|
|
*c = '\0';
|
|
}
|
|
in->readmaxlength=-1;
|
|
ret++;
|
|
break;
|
|
}
|
|
|
|
case 'n':
|
|
{
|
|
// rreturn the number of characters read so far
|
|
int *n = (int *) va_arg( *args, int* );
|
|
*n = in->readsofar;
|
|
// TODO: need to chack if the '%n' with a valid results counts for the return of scanf()
|
|
ret++;
|
|
break;
|
|
}
|
|
|
|
case 'p':
|
|
{
|
|
void *p = (void *) va_arg( *args, void** ); // get the adress of the target pointer (void**)
|
|
err = __strto_int( in, 0, p, NULL, true );
|
|
if (err == 0) ret++;
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else i++;
|
|
|
|
if (err!=0) break; // an error occured somewhere whiloe parsing the values
|
|
}
|
|
|
|
__scanf_end( in );
|
|
|
|
return ret;
|
|
} |