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

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



On Sun, Sep 27 08:07, Kevin Wolf wrote:
> 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?

Jo das dürfte reichen.

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

Bäh ;-)

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

Naja ich habe jetzt nur darauf geachtet ob da was kaputt sein könnte,
aber mit der Spezifikation verglichen ob es vollständig ist, habe ich
jetzt noch nicht. ;-)

Aber pack halt mal rein so, wenn du das oben gefixt hast.

Acked-by: Antoine Kaufmann <toni@xxxxxxxxxx>
-- 
Antoine Kaufmann
<toni@xxxxxxxxxxxxxxxx>

Attachment: pgpa6fgTcDgrp.pgp
Description: PGP signature