#include "../stdio_p.h" #include "../../stdlib/stdlib_p.h" #include #include #include #include void __scanf_start(struct __scanf_input *in) { if(in->fp) in->buffer = fgetc(in->fp); else { in->buffer = (*in->str ? *in->str : EOF); in->str += (in->buffer != EOF); } } 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--; } 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 == 1) *va_arg(*args, int8_t *) = value; else if(size == 2) *va_arg(*args, int16_t *) = value; else if(size == 4) *va_arg(*args, int32_t *) = value; else if(size == 8) *va_arg(*args, int64_t *) = value; } void __scanf_store_d(long double value, int size, va_list *args) { if(size == sizeof(float)) *va_arg(*args, float *) = value; else if(size == sizeof(double)) *va_arg(*args, double *) = value; else if(size == sizeof(long double)) *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; /* Size of the assigned (integer or floating-point) type, in bytes */ uint8_t size; /* Whether to skip assignment */ bool skip; /* Set of bytes allowed for bracket sets in %[] */ uint8_t bracket_set[32]; }; /* Allow/disallow the entire set */ static void bracket_set_init(uint8_t *set, bool allow) { memset(set, allow ? 0xff : 0x00, 32); } /* Allow/disallow a range of characters. Both ends are included. */ static void bracket_set_range( uint8_t *set, uint8_t start, uint8_t end, bool allow) { for(int u = start; u <= end; u++) { int byte = u >> 3; int bit = 1 << (u & 7); if(allow) set[byte] |= bit; else set[byte] &= ~bit; } } /* Check whether a byte is allowed by the bracket set. */ static bool bracket_set_test(uint8_t *set, int c) { return (c != EOF) && (set[c >> 3] & (1 << (c & 7))); } /* Parse a bracket set from a format string. Returns true on success. */ static bool bracket_set_parse(uint8_t *set, char const *format, int *pos) { int last = 0; bool allow = true; bracket_set_init(set, false); /* '^' denotes a negated set */ if(format[*pos] == '^') { allow = false; (*pos)++; bracket_set_init(set, true); } /* ']' as the first character adds ']' to the set */ if(format[*pos] == ']' ) { bracket_set_range(set, ']', ']', allow); (*pos)++; } for(; format[*pos] && format[*pos] != ']'; (*pos)++) { /* '-' as the last character, thus explicitly in the set */ if(format[*pos] == '-' && format[*pos + 1] == ']') bracket_set_range(set, '-', '-', allow); /* '-' as denoting a range */ else if(format[*pos] == '-') { (*pos)++; bracket_set_range(set, last, format[*pos], allow); } /* Any other character */ else { last = format[*pos]; bracket_set_range(set, last, last, allow); } } return (format[*pos] == ']'); } static int parse_fmt(char const *fmt, int *pos, struct scanf_format *opt) { opt->field_width = INT_MAX; opt->size = sizeof(int); opt->skip = false; int width = 0; char size_letter = 0; while(1) { (*pos)++; switch(fmt[*pos]) { case '*': opt->skip = true; break; 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': width = width * 10 + (fmt[*pos] - '0'); opt->field_width = width; break; case '[': (*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': /* 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': if(opt->field_width == INT_MAX) opt->field_width = 1; return 'c'; default: return 0; } } return 0; } int __scanf( struct __scanf_input * __restrict__ in, char const * __restrict__ format, va_list *args) { in->bytes_read = 0; // we haven't started to read char from the input stream 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 pos = 0; // current pos in the format string __scanf_start( in ); // TODO: No __scanf_end() in any of the "return validrets"!! for(; format[pos]; pos++) { if(format[pos] == ' ') { __purge_space(in); continue; } else if(format[pos] != '%') { // 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++; continue; } else return validrets; // else we return the number of valid read } else if(format[pos + 1] == '%') { if(__scanf_peek(in) != '%') return validrets; else __scanf_in( in ); pos++; continue; } /* Perform a conversion */ else { struct scanf_format opt; int spec = parse_fmt(format, &pos, &opt); if(spec == 0) 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; ubytes_read; 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, opt.field_width); if (err == EOF && validrets == 0) return EOF; if (err != 0) return validrets; if(!opt.skip) __scanf_store_i( temp, opt.size, 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, opt.field_width); if (err == EOF && validrets == 0) return EOF; if (err != 0) return validrets; if(!opt.skip) __scanf_store_d( temp, opt.size, args ); validrets++; break; } case 'p': { long int temp; if (!opt.skip) { void *p = (void *) va_arg( *args, void** ); // get the adress of the target pointer (void**) err = __strto_int( in, 0, p, NULL, true, opt.field_width); } else err = __strto_int( in, 0, &temp, NULL, true, opt.field_width); if (err == 0) validrets++; else return validrets; break; } case 'c': { char *c = opt.skip ? NULL : va_arg(*args, char *); for(int u = 0; u < opt.field_width; u++) { int temp = __scanf_in(in); if(temp==EOF) return EOF; else if(c) *c++ = temp; } validrets += !opt.skip; break; } 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))) { if(c) { *c = 0; validrets++; } break; } else { int temp = __scanf_in( in ); if(c) *c++ = temp; curstrlength++; } } break; } } } } __scanf_end( in ); return validrets; }