On Sun, Sep 20 14:49, Kevin Wolf wrote: > + libc: scanf, sscanf, fscanf, vscanf, vsscanf, vfscanf > Sollte bis auf Gleitkommazahlen und Multibyte-Supprt vollstaendig > sein > > Signed-off-by: Kevin Wolf <kevin@xxxxxxxxxx> > --- > src/modules/include/stdio.h | 13 +- > src/modules/lib/stdlibc/scanf.c | 671 ++++++++++++++++++++++++++++++++++++++ > src/modules/lib/stdlibc/sscanf.c | 40 --- > 3 files changed, 680 insertions(+), 44 deletions(-) > create mode 100644 src/modules/lib/stdlibc/scanf.c > delete mode 100644 src/modules/lib/stdlibc/sscanf.c > > diff --git a/src/modules/include/stdio.h b/src/modules/include/stdio.h > index b3375f2..a4d3bb3 100644 > --- a/src/modules/include/stdio.h > +++ b/src/modules/include/stdio.h > @@ -78,10 +78,15 @@ int vsnprintf(char * buffer, size_t size, const char * format, va_list); > int vfprintf(FILE * fp, const char * format, va_list); > int vasprintf(char ** buffer, const char * format, va_list); > > -#ifndef CONFIG_LIBC_NO_STUBS > -int fscanf(FILE* fp, const char* format, ...); > -int sscanf(const char* str, const char* format, ...); > -#endif > + > +int scanf(const char* fmt, ...); > +int sscanf(const char* input, const char* fmt, ...); > +int fscanf(FILE* f, const char* fmt, ...); > + > +int vscanf(const char* fmt, va_list ap); > +int vsscanf(const char* input, const char* fmt, va_list ap); > +int vfscanf(FILE* f, const char* fmt, va_list ap); > + > > //Dateihandling > FILE* fopen(const char* filename, const char* mode); > diff --git a/src/modules/lib/stdlibc/scanf.c b/src/modules/lib/stdlibc/scanf.c > new file mode 100644 > index 0000000..c181ff6 > --- /dev/null > +++ b/src/modules/lib/stdlibc/scanf.c > @@ -0,0 +1,671 @@ > +/* > + * Copyright (c) 2009 The tyndur Project. All rights reserved. > + * > + * This code is derived from software contributed to the tyndur Project > + * by Kevin Wolf. > + * > + * Redistribution and use in source and binary forms, with or without > + * modification, are permitted provided that the following conditions > + * are met: > + * 1. Redistributions of source code must retain the above copyright > + * notice, this list of conditions and the following disclaimer. > + * 2. Redistributions in binary form must reproduce the above copyright > + * notice, this list of conditions and the following disclaimer in the > + * documentation and/or other materials provided with the distribution. > + * > + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS > + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED > + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR > + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR > + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, > + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, > + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; > + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, > + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR > + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF > + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. > + */ > + > +#include <stdio.h> > +#include <stdarg.h> > +#include <stdlib.h> > +#include <stdint.h> > +#include <stddef.h> > +#include <string.h> > +#include <ctype.h> > + > +typedef int (*jscanf_getc)(void* state); > +typedef void (*jscanf_ungetc)(void* state, char c); > +typedef int (*jscanf_tell)(void* state); > + > +/** > + * Liest Zahlen passend fuer jscanf ein, d.h. es wird so lange gelesen, wie > + * theoretisch noch eine gueltige Zahl herauskommen koennte. Das Parsen und > + * Entscheiden, ob es tatsaechlich eine gueltige Zahl ist, bleibt dem Aufrufer > + * ueberlassen. > + * > + * @param buf Puffer, in den die Zeichen eingelesen werden > + * @param size Laenge des Puffers in einlesbaren Zeichen. Es muss mindestens > + * Platz fuer size + 1 Zeichen vorhanden sein, da noch ein Nullbyte geschrieben > + * wird. > + * @param jgetc Funktion, um ein Zeichen auszulesen > + * @param jungetc Funktion, um ein Zeichen zurueckzugeben, wenn es doch nicht > + * passt > + * @param state Zustand der Eingabe fuer jgetc/jungetc > + * @param base Basis der einzulesenden Zahl (8, 10 oder 16) > + * @param eof Wird auf 1 gesetzt, wenn die Eingabe wegen EOF beendet wurde, auf > + * 0, wenn ein unpassendes Zeichen gekommen ist > + * > + * @return Anzahl der in den Puffer eingelesenen Zeichen > + */ > +static int jscanf_read_number(char* buf, int size, jscanf_getc jgetc, > + jscanf_ungetc jungetc, void* state, int base, int* eof) > +{ > + int valid; > + int i; > + int c; > + int first_digit = 0; > + > + *eof = 0; > + i = 0; > + while (i < size) { > + c = jgetc(state); > + > + if (c == EOF) { > + *eof = 1; > + break; > + } > + > + valid = 0; > + switch (c) { > + case '+': > + case '-': > + if (i == 0) { > + valid = 1; > + first_digit++; > + } > + break; > + case '0': > + valid = 1; > + break; > + case '1' ... '7': > + if (base == 0) { > + base = buf[first_digit] == '0' ? 8 : 10; Wird hier nicht u.U. auf ein uninitialisiertes Arrayelement zugegriffen, wenn die erste Ziffer beispielsweise eine 1 ist? > + } > + valid = 1; > + break; > + case '8' ... '9': > + if (base == 0) { > + base = buf[first_digit] == '0' ? 8 : 10; Dito. > + } > + valid = (base != 8); > + break; > + case 'A' ... 'F': > + case 'a' ... 'f': > + valid = (base == 16); > + break; > + case 'x': > + if (base == 0) { > + base = 16; > + } > + if ((base == 16) && (i == first_digit + 1) && > + (buf[first_digit] == '0')) > + { > + valid = 1; > + } > + break; > + } > + > + if (!valid) { > + jungetc(state, c); > + break; > + } > + > + buf[i] = c; > + i++; > + } > + > + buf[i] = '\0'; > + Hm wird so ein + oder - nicht auch als Zahl erkannt? Ich nehme an, das ist so nicht wirklich gewünscht... > + return i; > +} > + > +/** > + * Weist eine Zahl einer Variablen zu. Die Groesse der Variablen wird dabei > + * als Parameter uebergeben. > + * > + * @param ptr Variable, in die die Zahl geschrieben werden soll > + * @param value Wert, der der Variablen zugewiesen werden soll > + * @param size Groesse der Variablen in Bytes > + */ > +static void assign_number(void* ptr, uint64_t value, int size) > +{ > + if (size == 1) { > + uint8_t* tptr = ptr; > + *tptr = (uint8_t) value; > + } else if (size == 2) { > + uint16_t* tptr = ptr; > + *tptr = (uint16_t) value; > + } else if (size == 4) { > + uint32_t* tptr = ptr; > + *tptr = (uint32_t) value; > + } else if (size == 8) { > + uint64_t* tptr = ptr; > + *tptr = (uint64_t) value; > + } else { > + abort(); > + } > +} > + > +/** > + * Die eigentliche scanf-Engine, die den Formatstring verarbeitet und die > + * eingelesenen Daten in die uebergebenen Parameter schreibt. Die > + * Eingabezeichen werden mit den uebergebenen Funktionen geholt. > + * > + * @param fmt scanf-Formatstring > + * @param ap Liste der Ausgabevariablen > + * @param jgetc Funktion, um ein Zeichen auszulesen > + * @param jungetc Funktion, um ein Zeichen zurueckzugeben, wenn es doch nicht > + * passt > + * @param jtell Funktion, die die Anzahl der bisher gelesenen Zeichen > + * zurueckgibt > + * @param state Zustand der Eingabe fuer jgetc/jungetc > + * > + * @return Anzahl der erfolgreich zugewiesenen Variablen; EOF, wenn ein > + * Eingabefehler auftritt, bevor mindestens eine Variable erfolgreich > + * eingelesen wurde. > + */ > +static int jscanf(const char* fmt, va_list ap, > + jscanf_getc jgetc, jscanf_ungetc jungetc, jscanf_tell jtell, void* state) > +{ > + int ret = 0; > + int assign; > + int len; > + int size; > + int c; > + char* endptr; > + uint64_t value; > + > + // 64 Bit oktal = 22 Zeichen, Vorzeichen und \0 > + char buf[24]; > + > + while (*fmt) { > + switch (*fmt) { > + case ' ': > + case '\t': > + case '\n': > + case '\f': > + case '\v': > + do { > + c = jgetc(state); > + } while (isspace(c)); > + break; > + > + case '%': > + fmt++; > + > + // Ein * bedeutet, dass der Wert nur eingelesen, aber keiner > + // Variablen zugewiesen wird > + if (*fmt == '*') { > + assign = 0; > + fmt++; > + } else { > + assign = 1; > + } > + > + // Optional kann jetzt die Feldlaenge kommen > + if (isdigit(*fmt)) { > + len = strtol(fmt, (char**) &fmt, 10); > + if (len == 0) { > + goto matching_error; > + } > + } else { > + len = 0; > + } > + > + // Und die Laenge der Variablen kann auch noch angegeben sein > + switch (*fmt) { > + case 'h': > + if (*++fmt == 'h') { > + fmt++; > + size = sizeof(char); > + } else { > + size = sizeof(short); > + } > + break; > + case 'l': > + if (*++fmt == 'l') { > + fmt++; > + size = sizeof(long long); > + } else { > + size = sizeof(long); > + } > + break; > + case 'j': > + size = sizeof(intmax_t); > + break; > + case 't': > + size = sizeof(ptrdiff_t); > + break; > + case 'z': > + size = sizeof(size_t); > + break; > + default: > + size = sizeof(int); > + break; > + } > + > + // Whitespace muss uebersprungen werden (ausser %[ %c %n) > + if ((*fmt != '[') && (*fmt != 'c') && (*fmt != 'n')) { > + while (isspace(c = jgetc(state))); > + if (c != EOF) { > + jungetc(state, c); > + } > + } > + > + // Eingabe parsen > + switch (*fmt) { > + int base; > + int eof; > + > + // %i - Integer (strtol mit Basis 0) > + // %d - Integer (strtol mit Basis 10) > + // %o - Integer (strtoul mit Basis 8 > + // %u - Integer (strtoul mit Basis 1) > + // %x - Integer (strtoul mit Basis 16) > + // %X - Integer (strtoul mit Basis 16) > + // %p - Pointer (strtoul mit Basis 16) > + case 'i': > + base = 0; > + goto convert_number; > + case 'o': > + base = 8; > + goto convert_number; > + case 'x': > + case 'X': > + base = 16; > + goto convert_number; > + case 'p': > + base = 16; > + size = sizeof(void*); > + len = 0; > + goto convert_number; > + case 'd': > + case 'u': > + base = 10; > + convert_number: > + if (len == 0 || len >= sizeof(buf)) { > + len = sizeof(buf) - 1; > + } > + len = jscanf_read_number(buf, len, jgetc, jungetc, > + state, base, &eof); > + > + value = strtoull(buf, &endptr, base); > + if ((endptr != buf + len) || (len == 0)) { > + if ((len == 0) && eof) { > + goto input_error; > + } > + goto matching_error; > + } > + > + if (assign) { > + void* ptr = va_arg(ap, void*); > + assign_number(ptr, value, size); > + ret++; > + } > + break; > + > + // %n - Anzahl der bisher gelesenen Zeichen > + case 'n': > + if (assign) { > + void* ptr = va_arg(ap, void*); > + assign_number(ptr, jtell(state), size); > + } > + break; > + > + // %c - char-Array (feste Laenge, kein Nullbyte) > + case 'c': > + { > + // TODO Multibyte-Zeichen > + int i; > + char* ptr = NULL; > + > + if (assign) { > + ptr = va_arg(ap, char*); > + } > + > + if (len == 0) { > + len = 1; > + } > + > + for (i = 0; i < len; i++) { > + c = jgetc(state); > + if (c == EOF) { > + goto input_error; > + } > + if (assign) { > + ptr[i] = c; > + } > + } > + > + if (assign) { > + ret++; > + } > + break; > + } > + > + // %s - Whitespaceterminierter String > + case 's': > + { > + // TODO MUltibyte-Zeichen > + char* ptr = NULL; > + int matched = 0; > + > + if (assign) { > + ptr = va_arg(ap, char*); > + } > + > + if (len == 0) { > + len = -1; > + } > + > + while ((len == -1) || len--) { > + c = jgetc(state); > + if (isspace(c)) { > + jungetc(state, c); > + break; > + } else if (c == EOF) { > + break; > + } > + matched = 1; > + if (assign) { > + *ptr++ = c; > + } > + } > + > + // Matching Error ist nicht moeglich, da alle > + // Leerzeichen bereits vorher weggelesen werden und > + // ansonsten alle Zeichen akzeptiert werden. > + if (!matched) { > + goto input_error; > + } > + > + if (assign) { > + *ptr = '\0'; > + ret++; > + } > + break; > + } > + > + // %[ - String aus einer Zeicheklasse > + case '[': > + { > + const char* p = fmt; > + const char* end; > + int negation = 0; > + int match = 0; > + char* ptr = NULL; > + > + if (assign) { > + ptr = va_arg(ap, char*); > + } > + > + // Wenn das Scanset mit ^ anfaengt, duerfen die Zeichen > + // des Scansets _nicht_ vorkommen > + if (*++p == '^') { > + negation = 1; > + p++; > + } > + > + // Scanset zusammenbasteln > + if (*p == '\0') { > + abort(); > + } > + > + end = strchr(p + 1, ']'); > + if (end == NULL) { > + abort(); > + } > + > + char scanset[end - p + 1]; > + scanset[end - p] = '\0'; > + strncpy(scanset, p, end - p); > + > + // Hier kommt das eigentliche Matching > + if (len == 0) { > + len = -1; > + } > + > + c = EOF; > + while ((len == -1) || len--) { > + c = jgetc(state); > + if (c == EOF) { > + break; > + } else if (!!strchr(scanset, c) == negation) { Hm also das !! finde ich ja schon etwas seltsam, da musste ich schon 2x hingucken um den Sinn zu sehen... ;-) vielleicht nur ! und dann ein != oder so? Naja eigentlich auch nicht besser. *g* > + jungetc(state, c); > + break; > + } > + match = 1; > + if (assign) { > + *ptr++ = c; > + } > + } > + > + if (!match) { > + if (c == EOF) { > + goto input_error; > + } else { > + goto matching_error; > + } > + } > + > + if (assign) { > + *ptr = '\0'; > + ret++; > + } > + > + fmt = end; > + > + break; > + } > + > + // %% - Prozentzeichen > + case '%': > + goto parse_percent; > + > + // Das Verhalten bei ungueltiger Conversion Specification > + // ist undefiniert. > + default: > + abort(); > + } > + break; > + > + // Ein einzelnes Zeichen wird direkt gematcht > + parse_percent: > + default: > + c = jgetc(state); > + if (c == EOF) { > + goto input_error; > + } > + if (c != *fmt) { > + goto matching_error; > + } > + break; > + } > + > + fmt++; > + } > + > +matching_error: > + return ret; > + > +input_error: > + if (ret == 0) { > + return EOF; > + } > + return ret; > +} > + > + > + > +/////////////////////////////////////////////////////////////////////////////// > +// sscanf - Eingabe aus einem String > + > +struct sscanf_state { > + const char* input; > + int pos; > +}; > + > +static int sscanf_getc(void* state) > +{ > + struct sscanf_state* s = state; > + int ret = EOF; > + > + if (s->input[s->pos]) { > + ret = s->input[s->pos]; > + s->pos++; > + } > + > + return ret; > +} > + > +static void sscanf_ungetc(void* state, char c) > +{ > + struct sscanf_state* s = state; > + > + if (s->pos > 0) { > + s->pos--; > + if (s->input[s->pos] != c) { > + abort(); > + } > + } else { > + abort(); > + } > +} > + > +static int sscanf_tell(void* state) > +{ > + struct sscanf_state* s = state; > + return s->pos; > +} > + > +/** > + * Fuehrt ein jscanf mit Eingabe aus einem String aus > + */ > +int vsscanf(const char* input, const char* fmt, va_list ap) > +{ > + struct sscanf_state state = { > + .input = input, > + .pos = 0, > + }; > + > + return jscanf(fmt, ap, sscanf_getc, sscanf_ungetc, sscanf_tell, &state); > +} > + > +/** > + * Fuehrt ein jscanf mit Eingabe aus einem String aus > + */ > +int sscanf(const char* input, const char* fmt, ...) > +{ > + va_list ap; > + int ret; > + > + va_start(ap, fmt); > + ret = vsscanf(input, fmt, ap); > + va_end(ap); > + > + return ret; > +} > + > + > + > +/////////////////////////////////////////////////////////////////////////////// > +// fscanf - Eingabe aus einer Datei > + > +struct fscanf_state { > + FILE* file; > + int pos; > +}; > + > +static int fscanf_getc(void* state) > +{ > + struct fscanf_state* s = state; > + FILE* f = s->file; > + int c; > + > + c = fgetc(f); > + if (c != EOF) { > + s->pos++; > + } > + > + return c; > +} > + > +static void fscanf_ungetc(void* state, char c) > +{ > + struct fscanf_state* s = state; > + FILE* f = s->file; > + > + s->pos--; > + ungetc(c, f); > +} > + > +static int fscanf_tell(void* state) > +{ > + struct fscanf_state* s = state; > + return s->pos; > +} > + > +/** > + * Fuehrt ein jscanf mit Eingabe aus einer Datei aus > + */ > +int vfscanf(FILE* f, const char* fmt, va_list ap) > +{ > + struct fscanf_state state = { > + .file = f, > + .pos = 0, > + }; > + return jscanf(fmt, ap, fscanf_getc, fscanf_ungetc, fscanf_tell, &state); > +} > + > +/** > + * Fuehrt ein jscanf mit Eingabe aus einer Datei aus > + */ > +int fscanf(FILE* f, const char* fmt, ...) > +{ > + va_list ap; > + int ret; > + > + va_start(ap, fmt); > + ret = vfscanf(f, fmt, ap); > + va_end(ap); > + > + return ret; > +} > + > + > +/////////////////////////////////////////////////////////////////////////////// > +// scanf - Eingabe aus der Standardeingabe > + > +/** > + * Fuehrt ein jscanf mit Eingabe aus der Standardeingabe aus > + */ > +int vscanf(const char* fmt, va_list ap) > +{ > + return vfscanf(stdin, fmt, ap); > +} > + > +/** > + * Fuehrt ein jscanf mit Eingabe aus der Standardeingabe aus > + */ > +int scanf(const char* fmt, ...) > +{ > + va_list ap; > + int ret; > + > + va_start(ap, fmt); > + ret = vscanf(fmt, ap); > + va_end(ap); > + > + return ret; > +} > diff --git a/src/modules/lib/stdlibc/sscanf.c b/src/modules/lib/stdlibc/sscanf.c > deleted file mode 100644 > index 3082e3d..0000000 > --- a/src/modules/lib/stdlibc/sscanf.c > +++ /dev/null > @@ -1,40 +0,0 @@ > -/* > - * Copyright (c) 2007 The tyndur Project. All rights reserved. > - * > - * This code is derived from software contributed to the tyndur Project > - * by Antoine Kaufmann. > - * > - * Redistribution and use in source and binary forms, with or without > - * modification, are permitted provided that the following conditions > - * are met: > - * 1. Redistributions of source code must retain the above copyright > - * notice, this list of conditions and the following disclaimer. > - * 2. Redistributions in binary form must reproduce the above copyright > - * notice, this list of conditions and the following disclaimer in the > - * documentation and/or other materials provided with the distribution. > - * > - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS > - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED > - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR > - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR > - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, > - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, > - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; > - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, > - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR > - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF > - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. > - */ > - > -#include <stdio.h> > -#include <lost/config.h> > - > -#ifndef CONFIG_LIBC_NO_STUBS > -int sscanf(const char* str, const char* format, ...) > -{ > - // FIXME > - puts("TODO in LOST-Libc: sscanf!"); > - return 0; > -} > -#endif > - -- Antoine Kaufmann <toni@xxxxxxxxxxxxxxxx>
Attachment:
pgp9D4CswkE9c.pgp
Description: PGP signature