[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [tyndur-devel] [PATCH v2] libc: scanf-Familie



On Sun, Sep 27, 2009 at 12:48:23AM +0200, Antoine Kaufmann wrote:
> 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.

Ich fürchte, du hast recht. Wenn man das buf[i] = c hochverschiebt,
müsste es wieder passen, oder?


> 
> > +                }
> > +                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...

Mensch, wenn die Funktion einmal einen Kopfkommentar hat, dann lies den
doch auch. ;-) "+" ist für diese Funktion eine genauso tolle Zahl wie
"42" oder "0x". Gibt halt nachher im scanf einen Matching Error, aber
gelesen wird so weit.

> > +    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*

!! ist halt Normalisierung auf 0/1. Das wirklich erschreckende finde
ich, dass das mit Pointern geht. ;-)

Hm, war das schon alles an Kommentaren? Dann scheine ich ja doch mehr
Details umgesetzt zu haben als ich dachte.