Compare commits

...

3 Commits

7 changed files with 94 additions and 144 deletions

View File

@ -10,7 +10,9 @@ int fscanf(FILE * restrict fp, char const * restrict fmt, ...)
va_list args;
va_start(args, fmt);
__scanf_start(&in);
int count = __scanf(&in, fmt, &args);
__scanf_end(&in);
va_end(args);
return count;

View File

@ -10,7 +10,9 @@ int scanf(char const * restrict fmt, ...)
va_list args;
va_start(args, fmt);
__scanf_start(&in);
int count = __scanf(&in, fmt, &args);
__scanf_end(&in);
va_end(args);
return count;

View File

@ -5,6 +5,28 @@
#include <string.h>
#include <stdbool.h>
/* Features of this implementation:
- Specifiers:
* Integers (%i, %d, %o, %u, %x, %X)
* Floating-point numbers (%e, %f, %F, %g, %a)
* Strings (%c, %s, %[])
* Pointers (%p)
* Total number of bytes read so far (%n)
- Integer size modifiers: hh, h, l, ll, L, j, z, t. Supported for all
integer conversions, i.e. %i, %d, %u, %o, %x, %X and %n.
- Floating-point size modifiers: l, L for %a, %e, %f, %g.
- Assignment suppression character '*'.
- Maximum field width (but buggy for floating-point, see below).
NOT SUPPORTED:
- Wide characters: %lc, %ls.
- q size modifier.
- "'" (quote) specifier for locale-specific thousand separators.
- String allocation: %mc, %ms.
- Out-of-order assignments "%n$".
- TODO: Maximum field width for floating-point is mostly untested and likely
has bugs when the field ends in the middle of the number. */
void __scanf_start(struct __scanf_input *in)
{
if(in->fp)
@ -38,12 +60,13 @@ void __scanf_end(struct __scanf_input *in)
in->str--;
}
void __purge_space( struct __scanf_input * __restrict__ in )
static void __skip_spaces(struct __scanf_input *in)
{
while (isspace(__scanf_peek(in))) __scanf_in(in);
while(isspace(__scanf_peek(in)))
__scanf_in(in);
}
void __scanf_store_i(int64_t value, int size, va_list *args)
static void __scanf_store_i(int64_t value, int size, va_list *args)
{
if(size == 1)
*va_arg(*args, int8_t *) = value;
@ -55,7 +78,7 @@ void __scanf_store_i(int64_t value, int size, va_list *args)
*va_arg(*args, int64_t *) = value;
}
void __scanf_store_d(long double value, int size, va_list *args)
static void __scanf_store_d(long double value, int size, va_list *args)
{
if(size == sizeof(float))
*va_arg(*args, float *) = value;
@ -65,47 +88,6 @@ void __scanf_store_d(long double value, int size, va_list *args)
*va_arg(*args, long double *) = 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)
struct scanf_format {
/* Maximum field width */
int field_width;
@ -113,7 +95,6 @@ struct scanf_format {
uint8_t size;
/* Whether to skip assignment */
bool skip;
/* Set of bytes allowed for bracket sets in %[] */
uint8_t bracket_set[32];
};
@ -182,6 +163,7 @@ static bool bracket_set_parse(uint8_t *set, char const *format, int *pos)
return (format[*pos] == ']');
}
/* Parse a format in the format string. Returns specifier, 0 on error. */
static int parse_fmt(char const *fmt, int *pos, struct scanf_format *opt)
{
opt->field_width = INT_MAX;
@ -191,79 +173,52 @@ static int parse_fmt(char const *fmt, int *pos, struct scanf_format *opt)
int width = 0;
char size_letter = 0;
while(1) {
(*pos)++;
for((*pos)++;; (*pos)++) {
int c = fmt[*pos];
switch(fmt[*pos]) {
case '*':
if(c == '*')
opt->skip = true;
break;
else if(strchr("hlzjtL", c)) {
if(c == 'h')
opt->size = (size_letter=='h') ? sizeof(char) : sizeof(short);
else if(c == 'l')
opt->size = (size_letter=='l') ? sizeof(long long) :
sizeof(long);
else if(c == 'z')
opt->size = sizeof(size_t);
else if(c == 'j')
opt->size = sizeof(intmax_t);
else if(c == 't')
opt->size = sizeof(ptrdiff_t);
else if(c == 'L')
opt->size = sizeof(long double);
case 'h':
opt->size = (size_letter=='h') ? sizeof(char) : sizeof(short);
size_letter = 'h';
break;
case 'l':
opt->size = (size_letter=='l') ? sizeof(long long) : sizeof(long);
size_letter = 'l';
break;
case 'L':
opt->size = sizeof(long double);
size_letter = 'L';
break;
case 'j':
opt->size = sizeof(intmax_t);
break;
case 'z':
opt->size = sizeof(size_t);
break;
case 't':
opt->size = sizeof(ptrdiff_t);
break;
case '0' ... '9':
size_letter = c;
}
else if(isdigit(c)) {
width = width * 10 + (fmt[*pos] - '0');
opt->field_width = width;
break;
case '[':
}
else if(c == '[') {
(*pos)++;
return bracket_set_parse(opt->bracket_set, fmt, pos) ? '[' : 0;
case 'd':
case 'i':
case 'o':
case 'u':
case 'x':
case 'X':
case 'p':
case 's':
case 'n':
return fmt[*pos];
case 'a':
case 'A':
case 'e':
case 'E':
case 'f':
case 'F':
case 'g':
case 'G':
}
else if(strchr("diouxXpsn", c))
return c;
else if(strchr("aAeEfFgG", c)) {
/* Adjust interpretation of no size / 'l' size */
if(size_letter == 0)
opt->size = sizeof(float);
if(size_letter == 'l')
opt->size = sizeof(double);
return fmt[*pos];
case 'c':
return c;
}
else if(c == 'c') {
if(opt->field_width == INT_MAX)
opt->field_width = 1;
return 'c';
default:
return 0;
return c;
}
else return 0;
}
return 0;
}
@ -276,13 +231,9 @@ int __scanf(
/* Number of successful assignments */
int validrets = 0;
__scanf_start( in );
// TODO: No __scanf_end() in any of the "return validrets"!!
for(int pos = 0; format[pos]; pos++) {
if(format[pos] == ' ') {
__purge_space(in);
__skip_spaces(in);
continue;
}
else if(format[pos] != '%' || format[pos + 1] == '%') {
@ -301,32 +252,28 @@ int __scanf(
return validrets;
switch(spec) {
// we need to decrypt the corresponding scanf set of character
case '[': {
int currentlength = 0;
// we need to assign the read char to the corresponding pointer
char *c = opt.skip ? NULL : va_arg(*args, char *);
for(int u=0; u<opt.field_width; u++) {
int i;
for(i = 0; i < opt.field_width; i++) {
int temp = __scanf_peek(in);
if(bracket_set_test(opt.bracket_set, temp)) {
__scanf_in(in);
if(c) *c++ = temp;
currentlength++;
}
else if(temp==EOF && !currentlength && !validrets)
else if(temp==EOF && !i && !validrets)
return EOF;
else break;
}
if(!currentlength)
if(!i)
return validrets;
*c = '\0';
validrets += !opt.skip;
break;
}
// return the number of char read so far (cannot be skipped %*n is not valid)
case 'n':
*va_arg(*args, int *) = in->bytes_read;
__scanf_store_i(in->bytes_read, opt.size, args);
break;
case 'd':
@ -360,8 +307,6 @@ int __scanf(
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;
int err = __strto_fp(in, NULL, NULL, &temp, opt.field_width);
if(err == EOF && validrets == 0) return EOF;
@ -373,15 +318,11 @@ int __scanf(
}
case 'p': {
long int temp;
int err = 0;
if(!opt.skip) {
void *p = va_arg(*args, void *);
err = __strto_int(in, 0, p, NULL, true, opt.field_width);
validrets += (err == 0);
}
else err = __strto_int(in, 0, &temp, NULL, true, opt.field_width);
void *p = opt.skip ? NULL : va_arg(*args, void *);
_Static_assert(sizeof(p) == sizeof(long));
int err = __strto_int(in, 0, p, NULL, true, opt.field_width);
if(err) return validrets;
validrets += !opt.skip;
break;
}
@ -398,15 +339,13 @@ int __scanf(
}
case 's': {
char temp;
int curstrlength = 0;
__purge_space(in);
char *c = opt.skip ? NULL : va_arg(*args, char *);
for(int u = 0; u < opt.field_width; u++) {
temp = __scanf_peek(in);
if(temp==EOF && curstrlength==0) return validrets;
if(isspace(temp) || ((temp==EOF && curstrlength!=0))) {
__skip_spaces(in);
for(int i = 0; i < opt.field_width; i++) {
int temp = __scanf_peek(in);
if(temp==EOF && !i) return validrets;
if(isspace(temp) || temp==EOF) {
if(c) {
*c = 0;
validrets++;
@ -415,9 +354,7 @@ int __scanf(
}
else {
int temp = __scanf_in(in);
if(c)
*c++ = temp;
curstrlength++;
if(c) *c++ = temp;
}
}
break;
@ -425,6 +362,5 @@ int __scanf(
}
}
__scanf_end( in );
return validrets;
}

View File

@ -3,7 +3,6 @@
int sscanf(const char * restrict str, char const * restrict fmt, ...)
{
/* This is valid even if str=NULL. */
struct __scanf_input in = {
.str = str,
};
@ -11,7 +10,9 @@ int sscanf(const char * restrict str, char const * restrict fmt, ...)
va_list args;
va_start(args, fmt);
__scanf_start(&in);
int count = __scanf(&in, fmt, &args);
__scanf_end(&in);
va_end(args);
return count;

View File

@ -7,5 +7,8 @@ int vfscanf(FILE * restrict fp, char const * restrict fmt, va_list args)
.fp = fp,
};
return __scanf(&in, fmt, &args);
__scanf_start(&in);
int rc = __scanf(&in, fmt, &args);
__scanf_end(&in);
return rc;
}

View File

@ -7,5 +7,8 @@ int vscanf(char const * restrict fmt, va_list args)
.fp = stdin,
};
return __scanf(&in, fmt, &args);
__scanf_start(&in);
int rc = __scanf(&in, fmt, &args);
__scanf_end(&in);
return rc;
}

View File

@ -8,5 +8,8 @@ int vsscanf(const char * restrict str, char const * restrict fmt, va_list args)
.str = str,
};
return __scanf(&in, fmt, &args);
__scanf_start(&in);
int rc = __scanf(&in, fmt, &args);
__scanf_end(&in);
return rc;
}