From bb3e590915bf81a3409e925d1ff84330060d7c82 Mon Sep 17 00:00:00 2001 From: Jonas 'Sortie' Termansen Date: Fri, 7 Sep 2012 20:36:27 +0200 Subject: [PATCH] Add proper implementation of scanf family. There are still a few non-standard quirks and things that needs to be properly implemented but that shouldn't be too hard and the most important features are now implemented. --- libmaxsi/Makefile | 7 +- libmaxsi/fscanf.cpp | 35 ++++ libmaxsi/include/stdio.h | 16 +- libmaxsi/scanf.cpp | 35 ++++ libmaxsi/sscanf.cpp | 35 ++++ libmaxsi/vfscanf.cpp | 317 +++++++++++++++++++++++++++++ libmaxsi/vscanf.cpp | 31 +++ libmaxsi/{scan.cpp => vsscanf.cpp} | 53 ++--- 8 files changed, 488 insertions(+), 41 deletions(-) create mode 100644 libmaxsi/fscanf.cpp create mode 100644 libmaxsi/scanf.cpp create mode 100644 libmaxsi/sscanf.cpp create mode 100644 libmaxsi/vfscanf.cpp create mode 100644 libmaxsi/vscanf.cpp rename libmaxsi/{scan.cpp => vsscanf.cpp} (53%) diff --git a/libmaxsi/Makefile b/libmaxsi/Makefile index 26327335..1c04a677 100644 --- a/libmaxsi/Makefile +++ b/libmaxsi/Makefile @@ -95,7 +95,12 @@ print.o \ read.o \ readdirents.o \ rmdir.o \ -scan.o \ +scanf.o \ +fscanf.o \ +sscanf.o \ +vscanf.o \ +vfscanf.o \ +vsscanf.o \ stat.o \ truncate.o \ umask.o \ diff --git a/libmaxsi/fscanf.cpp b/libmaxsi/fscanf.cpp new file mode 100644 index 00000000..930d0e26 --- /dev/null +++ b/libmaxsi/fscanf.cpp @@ -0,0 +1,35 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2012. + + This file is part of LibMaxsi. + + LibMaxsi is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + LibMaxsi is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + details. + + You should have received a copy of the GNU Lesser General Public License + along with LibMaxsi. If not, see . + + fscanf.cpp + Input format conversion. + +*******************************************************************************/ + +#include +#include + +extern "C" int fscanf(FILE* fp, const char* format, ...) +{ + va_list ap; + va_start(ap, format); + int ret = vfscanf(fp, format, ap); + va_end(ap); + return ret; +} diff --git a/libmaxsi/include/stdio.h b/libmaxsi/include/stdio.h index 6af6d534..cf787a52 100644 --- a/libmaxsi/include/stdio.h +++ b/libmaxsi/include/stdio.h @@ -86,6 +86,8 @@ extern int fprintf(FILE* restrict stream, const char* restrict format, ...); extern int fputc(int c, FILE* stream); extern int fputs(const char* restrict s, FILE* restrict stream); extern size_t fread(void* restrict ptr, size_t size, size_t nitems, FILE* restrict stream); +extern FILE* freopen(const char* restrict filename, const char *restrict mode, FILE* restrict stream); +extern int fscanf(FILE* restrict stream, const char* restrict format, ... ); extern int fseek(FILE* stream, long offset, int whence); extern int fseeko(FILE* stream, off_t offset, int whence); extern long ftell(FILE* stream); @@ -101,29 +103,33 @@ extern int putc(int c, FILE* stream); extern int putchar(int c); extern int puts(const char* str); extern int remove(const char* path); +extern int rename(const char* oldname, const char* newname); extern void rewind(FILE* stream); extern int snprintf(char* restrict s, size_t n, const char* restrict format, ...); +extern void setbuf(FILE* restrict stream, char* restrict buf); extern char* sortix_gets(void); extern int sortix_puts(const char* str); extern int sprintf(char* restrict s, const char* restrict format, ...); +extern int scanf(const char* restrict format, ...); extern int sscanf(const char* restrict s, const char* restrict format, ...); extern int ungetc(int c, FILE* stream); extern int vfprintf(FILE* restrict stream, const char* restrict format, __gnuc_va_list ap); +extern int vfscanf(FILE* restrict stream, const char* restrict format, __gnuc_va_list arg); extern int vprintf(const char* restrict format, __gnuc_va_list ap); +extern int vscanf(const char* restrict format, __gnuc_va_list arg); extern int vsnprintf(char* restrict, size_t, const char* restrict, __gnuc_va_list); extern int vsprintf(char* restrict s, const char* restrict format, __gnuc_va_list ap); +extern int vsscanf(const char* restrict s, const char* restrict format, __gnuc_va_list arg); /* TODO: These are not implemented in libmaxsi/sortix yet. */ #if defined(__SORTIX_SHOW_UNIMPLEMENTED) extern char* ctermid(char* s); extern FILE *fmemopen(void* restrict buf, size_t size, const char* restrict mode); -extern FILE* freopen(const char* restrict filename, const char *restrict mode, FILE* restrict stream); extern FILE* open_memstream(char** bufp, size_t* sizep); extern FILE* popen(const char* command, const char* mode); extern FILE* tmpfile(void); extern int dprintf(int fildes, const char* restrict format, ...); extern int fgetpos(FILE* restrict stream, fpos_t* restrict pos); -extern int fscanf(FILE* restrict stream, const char* restrict format, ... ); extern int fsetpos(FILE* stream, const fpos_t* pos); extern int ftrylockfile(FILE* file); extern int getchar_unlocked(void); @@ -131,17 +137,11 @@ extern int getc_unlocked(FILE* stream); extern int pclose(FILE* steam); extern int putchar_unlocked(int c); extern int putc_unlocked(int c, FILE* steam); -extern int rename(const char* oldname, const char* newname); extern int renameat(int oldfd, const char* oldname, int newfd, const char* newname); -extern int scanf(const char* restrict format, ...); extern int setvbuf(FILE* restrict stream, char* restrict buf, int type, size_t size); extern int vdprintf(int fildes, const char* restrict format, __gnuc_va_list ap); -extern int vfscanf(FILE* restrict stream, const char* restrict format, __gnuc_va_list arg); -extern int vscanf(const char* restrict format, __gnuc_va_list arg); -extern int vsscanf(const char* restrict s, const char* restrict format, __gnuc_va_list arg); extern void flockfile(FILE* file); extern void funlockfile(FILE* file); -extern void setbuf(FILE* restrict stream, char* restrict buf); #if __POSIX_OBSOLETE <= 200801 extern char* tmpnam(char* s); diff --git a/libmaxsi/scanf.cpp b/libmaxsi/scanf.cpp new file mode 100644 index 00000000..82edf268 --- /dev/null +++ b/libmaxsi/scanf.cpp @@ -0,0 +1,35 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2012. + + This file is part of LibMaxsi. + + LibMaxsi is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + LibMaxsi is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + details. + + You should have received a copy of the GNU Lesser General Public License + along with LibMaxsi. If not, see . + + scanf.cpp + Input format conversion. + +*******************************************************************************/ + +#include +#include + +extern "C" int scanf(const char* format, ...) +{ + va_list ap; + va_start(ap, format); + int ret = vscanf(format, ap); + va_end(ap); + return ret; +} diff --git a/libmaxsi/sscanf.cpp b/libmaxsi/sscanf.cpp new file mode 100644 index 00000000..78f15477 --- /dev/null +++ b/libmaxsi/sscanf.cpp @@ -0,0 +1,35 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2012. + + This file is part of LibMaxsi. + + LibMaxsi is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + LibMaxsi is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + details. + + You should have received a copy of the GNU Lesser General Public License + along with LibMaxsi. If not, see . + + sscanf.cpp + Input format conversion. + +*******************************************************************************/ + +#include +#include + +extern "C" int sscanf(const char* str, const char* format, ...) +{ + va_list ap; + va_start(ap, format); + int ret = vsscanf(str, format, ap); + va_end(ap); + return ret; +} diff --git a/libmaxsi/vfscanf.cpp b/libmaxsi/vfscanf.cpp new file mode 100644 index 00000000..9f00d2c9 --- /dev/null +++ b/libmaxsi/vfscanf.cpp @@ -0,0 +1,317 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2012. + + This file is part of LibMaxsi. + + LibMaxsi is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + LibMaxsi is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + details. + + You should have received a copy of the GNU Lesser General Public License + along with LibMaxsi. If not, see . + + vfscanf.cpp + Input format conversion. + +*******************************************************************************/ + +#define __STDC_LIMIT_MACROS +#include +#include +#include +#include +#include +#include + +enum scanmode +{ + MODE_INIT, + MODE_CONVSPEC, + MODE_SCANINT, + MODE_SCANINT_REAL, + MODE_SCANSTRING, + MODE_SCANSTRING_REAL, +}; + +enum scantype +{ + TYPE_SHORT, + TYPE_SHORTSHORT, + TYPE_INT, + TYPE_LONG, + TYPE_LONGLONG, + TYPE_SIZE, + TYPE_PTRDIFF, + TYPE_MAX, +}; + +static bool IsTypeModifier(char c) +{ + return c == 'h' || c == 'j' || c == 'l' || c == 'L' || c == 't' || c == 'z'; +} + +static int debase(char c, int base) +{ + if ( c == '0' ) + return 0; + int ret = -1; + if ( '0' <= c && c <= '9' ) { ret = c - '0' + 0; } + if ( 'a' <= c && c <= 'f' ) { ret = c - 'a' + 10; } + if ( 'A' <= c && c <= 'F' ) { ret = c - 'A' + 10; } + if ( base <= ret ) + return -1; + return ret; +} + +extern "C" int vfscanf(FILE* fp, const char* origformat, va_list ap) +{ + union { const char* format; const unsigned char* formatuc; }; + format = origformat; + int matcheditems = 0; + size_t fieldwidth; + bool escaped = false; + bool discard; + bool negint; + bool intunsigned; + bool leadingzero; + bool hasprefix; + bool string; + size_t intparsed; + uintmax_t intvalue; + int ic; + int base; + int cval; + const size_t UNDO_MAX = 4; + int undodata[UNDO_MAX]; + size_t undoable = 0; + size_t strwritten; + char* strdest; + enum scantype scantype; + enum scanmode scanmode = MODE_INIT; + while ( true ) + { + ic = fgetc(fp); + unsigned char uc = ic; char c = uc; + switch (scanmode) + { + case MODE_INIT: + if ( !*format ) + goto break_loop; + if ( isspace(*formatuc) ) + { + if ( isspace(ic) ) + continue; + else + do format++; + while ( isspace(*formatuc) ); + } + if ( *format == '%' && !escaped ) + { + format++; + scanmode = MODE_CONVSPEC; + ungetc(ic, fp); + continue; + } + escaped = false; + if ( *format != c ) { ungetc(ic, fp); goto break_loop; } + format++; + break; + case MODE_CONVSPEC: + discard = false; + if ( *format == '*' ) { discard = true; format++; } + fieldwidth = 0; + while ( '0'<= *format && *format <= '9' ) + fieldwidth = fieldwidth * 10 + *format++ - '0'; + scantype = TYPE_INT; + while ( IsTypeModifier(*format) ) + switch ( *format++ ) + { + case 'h': scantype = scantype == TYPE_SHORT ? TYPE_SHORTSHORT + : TYPE_SHORT; break; + case 'j': scantype = TYPE_MAX; break; + case 'l': scantype = scantype == TYPE_LONG ? TYPE_LONGLONG + : TYPE_LONG; break; + case 'L': scantype = TYPE_LONGLONG; break; + case 't': scantype = TYPE_PTRDIFF; break; + case 'z': scantype = TYPE_SIZE; break; + } + + switch ( char convc = *format++ ) + { + case '%': + escaped = true; + default: + fprintf(stderr, "Warning: scanf does not support %c (%i)\n", + convc, convc); + fprintf(stderr, "Bailing out to prevent problems.\n"); + errno = ENOTSUP; + return -1; + continue; + case 'd': + base = 10; scanmode = MODE_SCANINT; intunsigned = false; break; + case 'i': + base = 0; scanmode = MODE_SCANINT; intunsigned = false; break; + case 'o': + base = 0; scanmode = MODE_SCANINT; intunsigned = true; break; + case 'u': + base = 10; scanmode = MODE_SCANINT; intunsigned = true; break; + case 'x': + case 'X': + base = 16; scanmode = MODE_SCANINT; intunsigned = true; break; + case 'c': + string = false; scanmode = MODE_SCANSTRING; break; + case 's': + string = true; scanmode = MODE_SCANSTRING; break; + } + ungetc(ic, fp); + continue; + case MODE_SCANINT: + intparsed = 0; + intvalue = 0; + leadingzero = false; + negint = false; + hasprefix = false; + undoable = 0; + scanmode = MODE_SCANINT_REAL; + case MODE_SCANINT_REAL: + if ( fieldwidth ) + { + fprintf(stderr, "Error: field width not supported for integers in scanf.\n"); + errno = ENOTSUP; + return -1; + } + if ( !undoable && isspace(ic) ) + continue; + if ( undoable < UNDO_MAX ) + undodata[undoable++] = ic; + if ( c == '-' && !intunsigned && !negint ) + { + negint = true; + continue; + } + if ( !intparsed && c == '0' && !hasprefix && + (!base || base == 8 || base == 16) && !leadingzero ) + leadingzero = true; + if ( intparsed == 1 && (c == 'x' || c == 'X') && !hasprefix && + (!base || base == 16) && leadingzero ) + { + base = 16; + leadingzero = false; + hasprefix = true; + intparsed = 0; + continue; + } + else if ( intparsed == 1 && '1' <= c && c <= '7' && !hasprefix && + (!base || base == 8) && leadingzero ) + { + base = 8; + hasprefix = true; + leadingzero = false; + } + else if ( !intparsed && '0' <= c && c <= '9' && !hasprefix && + (!base || base == 10) && !leadingzero ) + { + base = 10; + leadingzero = false; + hasprefix = true; + } + cval = debase(c, base); + if ( cval < 0 ) + { + if ( !intparsed ) + { + while ( undoable ) + ungetc(undodata[--undoable], fp); + goto break_loop; + } + scanmode = MODE_INIT; + undoable = 0; + ungetc(ic, fp); + if ( discard ) { discard = false; continue; } + uintmax_t uintmaxval = intvalue; + // TODO: Possible truncation of INTMAX_MIN! + intmax_t intmaxval = uintmaxval; + if ( negint ) intmaxval = -intmaxval; + bool un = intunsigned; + switch ( scantype ) + { + case TYPE_SHORTSHORT: + if ( un ) *va_arg(ap, unsigned char*) = uintmaxval; + else *va_arg(ap, signed char*) = intmaxval; + break; + case TYPE_SHORT: + if ( un ) *va_arg(ap, unsigned short*) = uintmaxval; + else *va_arg(ap, signed short*) = intmaxval; + break; + case TYPE_INT: + if ( un ) *va_arg(ap, unsigned int*) = uintmaxval; + else *va_arg(ap, signed int*) = intmaxval; + break; + case TYPE_LONG: + if ( un ) *va_arg(ap, unsigned long*) = uintmaxval; + else *va_arg(ap, signed long*) = intmaxval; + break; + case TYPE_LONGLONG: + if ( un ) *va_arg(ap, unsigned long long*) = uintmaxval; + else *va_arg(ap, signed long long*) = intmaxval; + break; + case TYPE_PTRDIFF: + *va_arg(ap, ptrdiff_t*) = intmaxval; + break; + case TYPE_SIZE: + if ( un ) *va_arg(ap, size_t*) = uintmaxval; + else *va_arg(ap, ssize_t*) = intmaxval; + break; + case TYPE_MAX: + if ( un ) *va_arg(ap, uintmax_t*) = uintmaxval; + else *va_arg(ap, intmax_t*) = intmaxval; + break; + } + matcheditems++; + continue; + } + intvalue = intvalue * (uintmax_t) base + (uintmax_t) cval; + intparsed++; + continue; + case MODE_SCANSTRING: + if ( !fieldwidth ) + fieldwidth = string ? SIZE_MAX : 1; + scanmode = MODE_SCANSTRING_REAL; + strwritten = 0; + strdest = discard ? NULL : va_arg(ap, char*); + case MODE_SCANSTRING_REAL: + if ( string && !strwritten && isspace(ic) ) + continue; + if ( string && strwritten && + (ic == EOF || isspace(ic) || strwritten == fieldwidth) ) + { + ungetc(ic, fp); + if ( !discard ) + strdest[strwritten] = '\0'; + matcheditems++; + scanmode = MODE_INIT; + continue; + } + if ( !string && strwritten == fieldwidth ) + { + ungetc(ic, fp); + scanmode = MODE_INIT; + continue; + } + if ( ic == EOF ) + goto break_loop; + if ( !discard ) + strdest[strwritten++] = c; + continue; + } + } +break_loop: + return matcheditems; +} diff --git a/libmaxsi/vscanf.cpp b/libmaxsi/vscanf.cpp new file mode 100644 index 00000000..34fb9fdb --- /dev/null +++ b/libmaxsi/vscanf.cpp @@ -0,0 +1,31 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2012. + + This file is part of LibMaxsi. + + LibMaxsi is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + LibMaxsi is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + details. + + You should have received a copy of the GNU Lesser General Public License + along with LibMaxsi. If not, see . + + vscanf.cpp + Input format conversion. + +*******************************************************************************/ + +#include +#include + +extern "C" int vscanf(const char* format, va_list ap) +{ + return vfscanf(stdin, format, ap); +} diff --git a/libmaxsi/scan.cpp b/libmaxsi/vsscanf.cpp similarity index 53% rename from libmaxsi/scan.cpp rename to libmaxsi/vsscanf.cpp index 6d4076f7..9d619996 100644 --- a/libmaxsi/scan.cpp +++ b/libmaxsi/vsscanf.cpp @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. + Copyright(C) Jonas 'Sortie' Termansen 2012. This file is part of LibMaxsi. @@ -17,42 +17,31 @@ You should have received a copy of the GNU Lesser General Public License along with LibMaxsi. If not, see . - scan.cpp - The scanf family of functions. + vsscanf.cpp + Input format conversion. *******************************************************************************/ -#include -#include -#include +#include #include -#include +#include #include +#include -namespace Maxsi { - - -// TODO: This is an ugly hack to help build binutils. -#warning Ugly sscanf hack to help build binutils -extern "C" int sscanf(const char* s, const char* format, ...) +extern "C" int vsscanf(const char* str, const char* format, va_list ap) { - if ( strcmp(format, "%x") != 0 ) - { - fprintf(stderr, "sscanf hack doesn't implement: '%s'\n", format); - abort(); - } - - va_list list; - va_start(list, format); - unsigned* dec = va_arg(list, unsigned*); - *dec = strtol(s, NULL, 16); - return strlen(s); + const char* filename = "/ugly-vsscanf-hack"; + FILE* fp = fopen(filename, "w+"); + if ( !fp ) + return -1; + int ret = -1; + size_t len = strlen(str); + if ( fwrite(str, sizeof(char), len, fp) == len ) + if ( fseek(fp, 0, SEEK_SET) == 0 ) + ret = vfscanf(fp, format, ap); + int savederrno = errno; + fclose(fp); + unlink(filename); + errno = savederrno; + return ret; } - -extern "C" int fscanf(FILE* /*fp*/, const char* /*format*/, ...) -{ - fprintf(stderr, "fscanf(3) is not implemented\n"); - abort(); -} - -} // namespace Maxsi