fxlibc/src/stdio/scanf/scan.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;
}