diff --git a/TODO b/TODO index 5d019a579c..188d6a70f8 100644 --- a/TODO +++ b/TODO @@ -1,4 +1,4 @@ -NuttX TODO List (Last updated February 9, 2019) +NuttX TODO List (Last updated February 14, 2019) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ This file summarizes known NuttX bugs, limitations, inconsistencies with @@ -22,7 +22,7 @@ nuttx/: (18) Network (net/, drivers/net) (4) USB (drivers/usbdev, drivers/usbhost) (2) Other drivers (drivers/) - (12) Libraries (libs/libc/, libs/libm/) + (11) Libraries (libs/libc/, libs/libm/) (12) File system/Generic drivers (fs/, drivers/) (10) Graphics Subsystem (graphics/) (1) Build system / Toolchains @@ -2049,16 +2049,6 @@ o Libraries (libs/libc/, libs/libm/) Status: Open Priority: Low - Title: MISSING fscanf() - Description: The standard C library function fscanf() has not been - implement. This should be pretty straight forward feature - to implement using input streams as defined in - include/nuttx/streams.h analogous to the same way that output - streams were used in lib_vsprintf() to support both sprintf() - and fprintf(). - Status: Open - Priority: Low - o File system / Generic drivers (fs/, drivers/) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/include/nuttx/streams.h b/include/nuttx/streams.h index a0dd2da923..be7b7e40d7 100644 --- a/include/nuttx/streams.h +++ b/include/nuttx/streams.h @@ -459,6 +459,19 @@ int lib_sprintf(FAR struct lib_outstream_s *obj, int lib_vsprintf(FAR struct lib_outstream_s *obj, FAR const IPTR char *src, va_list ap); +/**************************************************************************** + * Name: lib_sscanf and lib_vsscanf + * + * Description: + * Stream-oriented versions of sscanf and vsscanf. + * + ****************************************************************************/ + +int lib_sscanf(FAR struct lib_instream_s *obj, + FAR const IPTR char *fmt, ...); +int lib_vsscanf(FAR struct lib_instream_s *obj, FAR int *lastc, + FAR const IPTR char *src, va_list ap); + #undef EXTERN #if defined(__cplusplus) } diff --git a/include/stdio.h b/include/stdio.h index 9a13052b5b..a91bdc552b 100644 --- a/include/stdio.h +++ b/include/stdio.h @@ -168,6 +168,7 @@ FAR char *gets_s(FAR char *s, rsize_t n); void setbuf(FAR FILE *stream, FAR char *buf); int setvbuf(FAR FILE *stream, FAR char *buffer, int mode, size_t size); int ungetc(int c, FAR FILE *stream); +int fscanf(FAR FILE *stream, FAR const IPTR char *format, ...); /* Operations on the stdout stream, buffers, paths, and the whole printf-family */ @@ -178,7 +179,7 @@ int sprintf(FAR char *buf, FAR const IPTR char *format, ...); int asprintf (FAR char **ptr, FAR const IPTR char *fmt, ...); int snprintf(FAR char *buf, size_t size, FAR const IPTR char *format, ...); -int sscanf(FAR const char *buf, FAR const char *fmt, ...); +int sscanf(FAR const char *buf, FAR const IPTR char *fmt, ...); void perror(FAR const char *s); int vprintf(FAR const IPTR FAR char *format, va_list ap); @@ -189,6 +190,9 @@ int vasprintf(FAR char **ptr, FAR const IPTR char *fmt, va_list ap); int vsnprintf(FAR char *buf, size_t size, FAR const IPTR char *format, va_list ap); int vsscanf(FAR const char *buf, FAR const char *s, va_list ap); +int scanf(FAR const IPTR char *format, ...); +int vfscanf(FAR FILE *stream, FAR const IPTR char *format, + va_list ap); /* Operations on file descriptors including: * diff --git a/libs/libc/stdio/Make.defs b/libs/libc/stdio/Make.defs index 74766ca28a..49193f749e 100644 --- a/libs/libc/stdio/Make.defs +++ b/libs/libc/stdio/Make.defs @@ -43,7 +43,7 @@ CSRCS += lib_vsnprintf.c lib_dprintf.c lib_vdprintf.c CSRCS += lib_meminstream.c lib_memoutstream.c lib_memsistream.c CSRCS += lib_memsostream.c lib_lowoutstream.c CSRCS += lib_zeroinstream.c lib_nullinstream.c lib_nulloutstream.c -CSRCS += lib_sscanf.c lib_libnoflush.c lib_libsnoflush.c +CSRCS += lib_sscanf.c lib_libsscanf.c lib_libnoflush.c lib_libsnoflush.c ifeq ($(CONFIG_NANO_PRINTF),y) @@ -78,7 +78,7 @@ CSRCS += lib_rdflush.c lib_wrflush.c lib_fputc.c lib_puts.c lib_fputs.c CSRCS += lib_ungetc.c lib_vprintf.c lib_fprintf.c lib_vfprintf.c CSRCS += lib_stdinstream.c lib_stdoutstream.c lib_stdsistream.c CSRCS += lib_stdsostream.c lib_perror.c lib_feof.c lib_ferror.c -CSRCS += lib_clearerr.c +CSRCS += lib_clearerr.c lib_scanf.c lib_fscanf.c lib_vfscanf.c endif diff --git a/libs/libc/stdio/lib_fscanf.c b/libs/libc/stdio/lib_fscanf.c new file mode 100644 index 0000000000..7dafe8e487 --- /dev/null +++ b/libs/libc/stdio/lib_fscanf.c @@ -0,0 +1,62 @@ +/**************************************************************************** + * libs/libc/stdio/lib_fscanf.c + * + * Copyright (C) 2019 Gregory Nutt. All rights reserved. + * Author: Johannes Schock + * + * 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. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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 OWNER 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: fscanf + ****************************************************************************/ + +int fscanf(FAR FILE *stream, FAR const IPTR char *fmt, ...) +{ + va_list ap; + int n; + + /* vfprintf into the stream */ + + va_start(ap, fmt); + n = vfscanf(stream, fmt, ap); + va_end(ap); + + return n; +} diff --git a/libs/libc/stdio/lib_libsscanf.c b/libs/libc/stdio/lib_libsscanf.c new file mode 100644 index 0000000000..cb921519f5 --- /dev/null +++ b/libs/libc/stdio/lib_libsscanf.c @@ -0,0 +1,1244 @@ +/**************************************************************************** + * libs/libc/stdio/lib_libsscanf.c + * + * Copyright (C) 2007, 2008, 2011-2014, 2016, 2019 Gregory Nutt. All rights + * reserved. + * Author: Gregory Nutt + * Johannes Schock + * + * 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. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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 OWNER 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libc.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define MAXLN 128 + +#define HH_MOD -2 +#define H_MOD -1 +#define NO_MOD 0 +#define L_MOD 1 +#define LL_MOD 2 + +#ifndef MIN +# define MIN(a,b) (((a) < (b)) ? (a) : (b)) +#endif + +#ifndef MAX +# define MAX(a,b) (((a) > (b)) ? (a) : (b)) +#endif + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +int lib_vsscanf(FAR struct lib_instream_s *obj, FAR int *lastc, + FAR const IPTR char *fmt, va_list ap); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: findscanset + * + * Description: + * Fill in the given table from the scanset at the given format. + * Return a pointer to the character the closing ']'. + * The table has a 1 wherever characters should be considered part of the + * scanset. + * + * Function findscanset based on source function __sccl of FreeBSD + * (https://github.com/lattera/freebsd/blob/master/sys/kern/subr_scanf.c) + * + ****************************************************************************/ + +#ifdef CONFIG_LIBC_SCANSET +static FAR const char *findscanset(FAR const char *fmt, + FAR unsigned char set[32]) +{ + int c; + int n; + int v; + int i; + + fmt++; /* Skip '[' */ + + /* First `clear' the whole table */ + + c = *fmt++; /* First char hat => negated scanset */ + if (c == '^') + { + v = 1; /* Default => accept */ + c = *fmt++; /* Get new first char */ + } + else + { + v = 0; /* Default => reject */ + } + + memset(set, 0, 32); + if (c == 0) + { + goto doexit; + } + + /* Now set the entries corresponding to the actual scanset to the opposite + * of the above. The first character may be ']' (or '-') without being + * special; the last character may be '-'. + */ + + for (; ; ) + { + set[c / 8] |= (1 << (c % 8)); /* Take character c */ + + doswitch:n = *fmt++; /* Examine the next */ + switch (n) + { + case 0: /* Format ended too soon */ + case ']': /* End of scanset */ + goto doexit; + + case '-': + /* A scanset of the form [01+-] is defined as "the digit 0, the + * digit 1, the character +, the character -", but the effect of a + * scanset such as [a-zA-Z0-9] is implementation defined. The V7 + * Unix scanf treats "a-z" as "the letters a through z", but treats + * "a-a" as "the letter a, the character -, and the letter a". For + * compatibility, the `-' is not considerd to define a range if the + * character following it is either a close bracket (required by + * ANSI) or is not numerically greater than the character* we just + * stored in the table (c). + */ + + n = *fmt; + if (n == ']' || n < c) + { + c = '-'; + break; /* Resume the for(;;) */ + } + + fmt++; + do + { + /* Fill in the range */ + + c++; + set[c / 8] |= (1 << (c % 8)); /* Take character c */ + } + while (c < n); + + /* Alas, the V7 Unix scanf also treats formats such as [a-c-e] as + * "the letters a through e". This too is permitted by the + * standard. + */ + + goto doswitch; + + default: /* Just another character */ + c = n; + break; + } + } + +doexit: + if (v) /* Default => accept */ + { + for (i = 0; i < 32; i++) /* Invert all */ + { + set[i] ^= 0xff; + } + } + + return (fmt - 1); +} +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: lib_sscanf + * + * Description: + * Stream-oriented version of sscanf. + * + ****************************************************************************/ + +int lib_sscanf(FAR struct lib_instream_s *obj, FAR const IPTR char *fmt, ...) +{ + va_list ap; + int count; + + va_start(ap, fmt); + count = lib_vsscanf(obj, NULL, fmt, ap); + va_end(ap); + return count; +} + +/**************************************************************************** + * Name: lib_vsscanf + * + * Description: + * Stream-oriented version of vsscanf. + * + ****************************************************************************/ + +int lib_vsscanf(FAR struct lib_instream_s *obj, FAR int *lastc, + FAR const IPTR char *fmt, va_list ap) +{ + int c; + FAR char *tv; + FAR const char *tc; + int modifier; + bool noassign; + bool conv; + int assigncount; + int count; + int ngetstart; + int width; + int fwidth; + int base = 10; + char tmp[MAXLN]; + +#ifdef CONFIG_LIBC_LONG_LONG + FAR unsigned long long *plonglong = NULL; +#endif + FAR unsigned long *plong = NULL; + FAR unsigned int *pint = NULL; + FAR unsigned short *pshort = NULL; + FAR unsigned char *pchar = NULL; + +#ifdef CONFIG_LIBC_SCANSET + unsigned char set[32]; /* Bit field (256 / 8) */ +#endif + + /* keep this for future reference linfo("buf=\"%s\" fmt=\"%s\"\n", buf, fmt); */ + + /* Parse the format, extracting values from the input buffer as needed */ + + assigncount = 0; + count = 0; + width = 0; + conv = false; + noassign = false; + modifier = NO_MOD; + ngetstart = obj->nget; /* for %n calculations */ + + /* Loop until all characters in the fmt string have been processed. We may + * have to continue loop after reaching the end the input data in order to + * handle trailing %n format specifiers. + */ + + /* Get first character, we keep always the next character in c */ + + c = obj->get(obj); + + while (*fmt) + { + /* Skip over white spaces */ + + if (isspace(*fmt)) + { + while (isspace(c)) + { + c = obj->get(obj); + } + } + + while (isspace(*fmt)) + { + fmt++; + } + + /* Check for a conversion specifier */ + + if (*fmt == '%') + { + linfo("Specifier found\n"); + + /* Check for qualifiers on the conversion specifier */ + + fmt++; + for (; *fmt; fmt++) + { + linfo("Processing %c\n", *fmt); + +#ifdef CONFIG_LIBC_SCANSET + if (strchr("dibouxXcseEfFgGaAn[%", *fmt)) +#else + if (strchr("dibouxXcseEfFgGaAn%", *fmt)) +#endif + { + if (*fmt != '%') + { + conv = true; + } + break; + } + + if (*fmt == '*') + { + noassign = true; + } + else if (*fmt == 'l' || *fmt == 'L') + { + modifier = L_MOD; + + if (*(fmt + 1) == 'l' || *(fmt + 1) == 'L') + { + modifier = LL_MOD; + fmt++; + } + } + else if (*fmt == 'h' || *fmt == 'H') + { + modifier = H_MOD; + + if (*(fmt + 1) == 'h' || *(fmt + 1) == 'H') + { + modifier = HH_MOD; + fmt++; + } + } + else if (*fmt >= '1' && *fmt <= '9') + { + for (tc = fmt; isdigit(*fmt); fmt++) + ; + strncpy(tmp, tc, fmt - tc); + tmp[fmt - tc] = '\0'; + width = atoi(tmp); + fmt--; + } + } + + /* Process %s: String conversion */ + + if (*fmt == 's') + { + linfo("Performing string conversion\n"); + + /* Get a pointer to the char * value. We need to do this even + * of we have reached the end of the input data in order to + * update the 'ap' variable. + */ + + tv = NULL; /* To avoid warnings about begin uninitialized */ + if (!noassign) + { + tv = va_arg(ap, FAR char *); + tv[0] = '\0'; + } + + /* Skip over white space */ + + while (isspace(c)) + { + c = obj->get(obj); + } + + /* But we only perform the data conversion is we still have + * bytes remaining in the input data stream. + */ + + if (c > 0) + { + /* Use the actual field's width if 1) no fieldwidth + * specified or 2) the actual field's width is smaller + * than fieldwidth specified. + */ + + fwidth = 0; + while ((!width || (fwidth < width)) && (c > 0) && + !isspace(c)) + { + if (!noassign) + { + tv[fwidth] = c; + } + + fwidth++; + c = obj->get(obj); + } + + if (!noassign) + { + tv[fwidth] = '\0'; + assigncount++; + } + + count++; + } + } + +#ifdef CONFIG_LIBC_SCANSET + /* Process %[: Scanset conversion */ + + if (*fmt == '[') + { + linfo("Performing scanset conversion\n"); + + fmt = findscanset(fmt, set); /* find scanset */ + + /* Get a pointer to the char * value. We need to do this even + * if we have reached the end of the input data in order to + * update the 'ap' variable. + */ + + tv = NULL; /* To avoid warnings about begin uninitialized */ + if (!noassign) + { + tv = va_arg(ap, FAR char *); + tv[0] = '\0'; + } + + /* But we only perform the data conversion is we still have + * bytes remaining in the input data stream. + */ + + if (c > 0) + { + + /* Use the actual field's width if 1) no fieldwidth + * specified or 2) the actual field's width is smaller + * than the fieldwidth specified. + */ + + fwidth = 0; + while ((!width || (fwidth < width)) && (c > 0) + && ((set[c / 8] & (1 << (c % 8))) != 0)) + { + if (!noassign) + { + tv[fwidth] = c; + } + + fwidth++; + c = obj->get(obj); + } + + if (!fwidth) + { + if (lastc != NULL) + { + *lastc = c; + } + + return assigncount; + } + + if (!noassign) + { + tv[fwidth] = '\0'; + assigncount++; + } + + count++; + } + } +#endif + + /* Process %c: Character conversion */ + + else if (*fmt == 'c') + { + linfo("Performing character conversion\n"); + + /* Get a pointer to the char * value. We need to do this even + * if we have reached the end of the input data in order to + * update the 'ap' variable. + */ + + tv = NULL; /* To avoid warnings about being uninitialized */ + if (!noassign) + { + tv = va_arg(ap, FAR char *); + tv[0] = '\0'; + } + + /* But we only perform the data conversion is we still have + * bytes remaining in the input data stream. + */ + + if (c > 0) + { + /* Was a field width specified? */ + + if (!width) + { + /* No, then width is this one single character */ + + width = 1; + } + + /* Copy the character(s) (if we are making an assignment) */ + + fwidth = 0; + while ((fwidth < width) && c > 0) + { + if (!noassign) + { + tv[fwidth] = c; + } + + fwidth++; + c = obj->get(obj); + } + + if (fwidth != width) + { + if (lastc != NULL) + { + *lastc = c; + } + + return assigncount; + } + + if (!noassign) + { + assigncount++; + } + + count++; + } + } + + /* Process %d, %o, %b, %x, %u: Various integer conversions */ + + else if (strchr("dobxXui", *fmt)) + { + bool sign; + + linfo("Performing integer conversion\n"); + + /* Get a pointer to the integer value. We need to do this even + * if we have reached the end of the input data in order to + * update the 'ap' variable. + */ + + if (!noassign) + { + /* We have to check whether we need to return a long or an + * int. + */ + + switch (modifier) + { + case HH_MOD: + pchar = va_arg(ap, FAR unsigned char *); + *pchar = 0; + break; + + case H_MOD: + pshort = va_arg(ap, FAR unsigned short *); + *pshort = 0; + break; + + case NO_MOD: + pint = va_arg(ap, FAR unsigned int *); + *pint = 0; + break; + + default: + case L_MOD: + plong = va_arg(ap, FAR unsigned long *); + *plong = 0; + break; + +#ifdef CONFIG_LIBC_LONG_LONG + case LL_MOD: + plonglong = va_arg(ap, FAR unsigned long long *); + *plonglong = 0; + break; +#endif + } + } + + /* Skip over any white space before the integer string */ + + while (isspace(c)) + { + c = obj->get(obj); + } + + /* But we only perform the data conversion if we still have + * bytes remaining in the input data stream. + */ + + if (c > 0) + { + FAR char *endptr; + int prefix; + bool stopconv; + int errsave; + unsigned long tmplong; +#ifdef CONFIG_LIBC_LONG_LONG + unsigned long long tmplonglong; +#endif + /* Copy the real string into a temporary working buffer. */ + + if (!width || width > sizeof(tmp) - 1) + { + width = sizeof(tmp) - 1; + } + + /* The base of the integer conversion depends on the + * specific conversion specification. + */ + + fwidth = 0; + prefix = 0; + stopconv = false; + sign = false; + + switch (*fmt) + { + default: + case 'd': + sign = true; + + /* FALLTHROUGH */ + + case 'u': + while (fwidth < width && !stopconv) + { + if (c == '-' || c == '+') + { + if (fwidth != 0) + { + stopconv = true; + } + } + else if (!(c >= '0' && c <= '9')) + { + stopconv = true; + } + + if (!stopconv) + { + tmp[fwidth++] = c; + c = obj->get(obj); + } + } + + base = 10; + break; + + case 'x': + case 'X': + while (fwidth < width && !stopconv) + { + if (c == '-' || c == '+') + { + if (fwidth != 0) + { + stopconv = true; + } + } + else if (c == '0') + { + if (prefix == 0) + { + prefix = 1; + } + } + else if (c == 'x' || c == 'X') + { + if (prefix == 1) + { + prefix = 2; + } + else + { + stopconv = true; + } + } + else if (!((c >= '0' && c <= '9') || + (c >= 'a' && c <= 'f') || + (c >= 'A' && c <= 'F'))) + { + stopconv = true; + } + else + { + prefix = 2; + } + + if (!stopconv) + { + tmp[fwidth++] = c; + c = obj->get(obj); + } + } + + base = 16; + break; + + case 'o': + while (fwidth < width && !stopconv) + { + if (c == '-' || c == '+') + { + if (fwidth != 0) + { + stopconv = true; + } + } + else if (!(c >= '0' && c <= '7')) + { + stopconv = true; + } + + if (!stopconv) + { + tmp[fwidth++] = c; + c = obj->get(obj); + } + } + + base = 8; + break; + + case 'b': /* not official? */ + while (fwidth < width && !stopconv) + { + if (c == '-' || c == '+') + { + if (fwidth != 0) + { + stopconv = true; + } + } + else if (!(c >= '0' && c <= '1')) + { + stopconv = true; + } + + if (!stopconv) + { + tmp[fwidth++] = c; + c = obj->get(obj); + } + } + + base = 2; + break; + + case 'i': + sign = true; + base = 10; + + while (fwidth < width && !stopconv) + { + if (c == '-' || c == '+') + { + if (fwidth != 0) + { + stopconv = true; + } + } + else if (c == '0') + { + if (prefix == 0) + { + prefix = 1; + base = 8; + } + } + else if (c == 'x' || c == 'X') + { + if (prefix == 1) + { + prefix = 2; + base = 16; + } + else + { + stopconv = true; + } + } + else if (!((c >= '0' && c <= '7' && base >= 8) || + (c >= '8' && c <= '9' && base >= 10) || + (c >= 'a' && c <= 'f' && base == 16) || + (c >= 'A' && c <= 'F' && base == 16))) + { + stopconv = true; + } + else + { + prefix = 2; + } + + if (!stopconv) + { + tmp[fwidth++] = c; + c = obj->get(obj); + } + } + break; + } + + tmp[fwidth] = 0; + + linfo("tmp[]=\"%s\"\n", tmp); + + /* Perform the integer conversion */ + /* Preserve the errno value */ + + errsave = get_errno(); + set_errno(0); + + switch (modifier) + { + case HH_MOD: + case H_MOD: + case NO_MOD: + default: + case L_MOD: + if (sign) + { + tmplong = strtol(tmp, &endptr, base); + } + else + { + tmplong = strtoul(tmp, &endptr, base); + } + break; + +#ifdef CONFIG_LIBC_LONG_LONG + case LL_MOD: + if (sign) + { + tmplonglong = strtoll(tmp, &endptr, base); + } + else + { + tmplonglong = strtoull(tmp, &endptr, base); + } + break; +#endif + } + + /* Check if the number was successfully converted */ + + if (tmp == endptr || get_errno() == ERANGE) + { + if (lastc != NULL) + { + *lastc = c; + } + + return assigncount; + } + + set_errno(errsave); + if (!noassign) + { + + /* We have to check whether we need to return a long or + * an int. + */ + + switch (modifier) + { + case HH_MOD: + linfo("Return %ld to 0x%p\n", tmplong, pchar); + *pchar = (unsigned char)tmplong; + break; + + case H_MOD: + linfo("Return %ld to 0x%p\n", tmplong, pshort); + *pshort = (unsigned short)tmplong; + break; + + case NO_MOD: + linfo("Return %ld to 0x%p\n", tmplong, pint); + *pint = (unsigned int)tmplong; + break; + + default: + case L_MOD: + linfo("Return %ld to 0x%p\n", tmplong, plong); + *plong = tmplong; + break; + +#ifdef CONFIG_LIBC_LONG_LONG + case LL_MOD: + linfo("Return %lld to 0x%p\n", tmplonglong, + plonglong); + *plonglong = tmplonglong; + break; +#endif + } + + assigncount++; + } + + count++; + } + } + + /* Process %a, %A, %f, %F, %e, %E, %g, and %G: Floating point + * conversions. + */ + + else if (strchr("aAfFeEgG", *fmt) != NULL) + { +#ifdef CONFIG_HAVE_DOUBLE + FAR double *pd = NULL; +#endif + FAR float *pf = NULL; + + linfo("Performing floating point conversion\n"); + + /* Get a pointer to the double value. We need to do this even + * if we have reached the end of the input data in order to + * upate the 'ap' variable. + */ + + if (!noassign) + { + /* We have to check whether we need to return a float or a + * double. + */ + +#ifdef CONFIG_HAVE_DOUBLE + if (modifier >= L_MOD) + { + pd = va_arg(ap, FAR double *); + *pd = 0.0; + } + else +#endif + { + pf = va_arg(ap, FAR float *); + *pf = 0.0; + } + } + +#ifdef CONFIG_LIBC_FLOATINGPOINT + + /* Skip over any white space before the real string */ + + while (isspace(c)) + { + c = obj->get(obj); + } + + /* But we only perform the data conversion is we still have + * bytes remaining in the input data stream. + */ + + if (c > 0) + { + FAR char *endptr; + bool expnt; + bool dot; + bool sign; + bool stopconv; + int errsave; +# ifdef CONFIG_HAVE_DOUBLE + double dvalue; +# endif + float fvalue; + + /* Was a fieldwidth specified? */ + + if (!width || width > sizeof(tmp) - 1) + { + width = sizeof(tmp) - 1; + } + + /* Copy the real string into a temporary working buffer. */ + + fwidth = 0; + expnt = false; + sign = false; + dot = false; + stopconv = false; + + while (fwidth < width && !stopconv) + { + if (c == '-' || c == '+') + { + if (!sign) + { + sign = true; + } + else + { + stopconv = true; + } + } + else if (c == '.') + { + if (!dot) + { + dot = true; + } + else + { + stopconv = true; + } + } + else if (c == 'e' || c == 'E') + { + if (!expnt) + { + expnt = true; + sign = false; + } + else + { + stopconv = true; + } + } + else if (!(c >= '0' && c <= '9')) + { + stopconv = true; + } + + if (!stopconv) + { + tmp[fwidth++] = c; + c = obj->get(obj); + } + } + + tmp[fwidth] = 0; + + linfo("tmp[]=\"%s\"\n", tmp); + + /* Perform the floating point conversion */ + /* Preserve the errno value */ + + errsave = get_errno(); + set_errno(0); + +# ifdef CONFIG_HAVE_DOUBLE + if (modifier >= L_MOD) + { + /* Get the converted double value */ + + dvalue = strtod(tmp, &endptr); + } + else +# endif + { + fvalue = strtof(tmp, &endptr); + } + + /* Check if the number was successfully converted */ + + if (tmp == endptr || get_errno() == ERANGE) + { + if (lastc != NULL) + { + *lastc = c; + } + + return assigncount; + } + + set_errno(errsave); + + if (!noassign) + { + + /* We have to check whether we need to return a float or + * a double. + */ + +# ifdef CONFIG_HAVE_DOUBLE + if (modifier >= L_MOD) + { + /* Return the double value */ + + linfo("Return %f to %p\n", dvalue, pd); + *pd = dvalue; + } + else +# endif + { + /* Return the float value */ + + linfo("Return %f to %p\n", (double)fvalue, pf); + *pf = fvalue; + } + + assigncount++; + } + count++; + + } +#endif + } + + /* Process %n: Character count */ + + else if (*fmt == 'n') + { + linfo("Performing character count\n"); + + if (!noassign) + { + size_t nchars = (size_t) (obj->nget - ngetstart); + + if (c != EOF) + { + /* One more character already read */ + + nchars--; + } + + /* Note %n does not count as a conversion */ + + switch (modifier) + { + case HH_MOD: + pchar = va_arg(ap, FAR unsigned char *); + *pchar = (unsigned char)nchars; + break; + + case H_MOD: + pshort = va_arg(ap, FAR unsigned short *); + *pshort = (unsigned short)nchars; + break; + + case NO_MOD: + pint = va_arg(ap, FAR unsigned int *); + *pint = (unsigned int)nchars; + break; + + default: + case L_MOD: + plong = va_arg(ap, FAR unsigned long *); + *plong = (unsigned long)nchars; + break; + +#ifdef CONFIG_LIBC_LONG_LONG + case LL_MOD: + plonglong = va_arg(ap, FAR unsigned long long *); + *plonglong = (unsigned long long)nchars; + break; +#endif + } + } + count++; + + } + else if (*fmt == '%') + { + if (c != '%') + { + break; + } + else + { + c = obj->get(obj); + } + } + + width = 0; + noassign = false; + modifier = NO_MOD; + + fmt++; + } + + /* It is not a conversion specifier */ + + else if (c > 0) + { +#if 0 + /* Skip over any leading spaces in the input buffer */ + + while (isspace(c)) + { + c = obj->get(obj); + } +#endif + + /* Skip over matching characters in the buffer and format */ + + if (*fmt != c) + { + break; + } + else + { + fmt++; + c = obj->get(obj); + } + } + else + { + /* NULL terminator encountered */ + + break; + } + } + + /* sscanf is required to return EOF if the input ends before the first + * matching failure or conversion. + */ + + if (lastc != NULL) + { + *lastc = c; + } + + return (count || !conv) ? assigncount : EOF; +} diff --git a/libs/libc/stdio/lib_scanf.c b/libs/libc/stdio/lib_scanf.c new file mode 100644 index 0000000000..66f014b29e --- /dev/null +++ b/libs/libc/stdio/lib_scanf.c @@ -0,0 +1,61 @@ +/**************************************************************************** + * libs/libc/stdio/lib_scanf.c + * + * Copyright (C) 2019 Gregory Nutt. All rights reserved. + * Author: Johannes Schock + * + * 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. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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 OWNER 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include "libc.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: scanf + ****************************************************************************/ + +int scanf(FAR const IPTR char *fmt, ...) +{ + va_list ap; + int ret; + + va_start(ap, fmt); + ret = vfscanf(stdin, fmt, ap); + va_end(ap); + + return ret; +} diff --git a/libs/libc/stdio/lib_sscanf.c b/libs/libc/stdio/lib_sscanf.c index 3edafc5e78..67ffe4274a 100644 --- a/libs/libc/stdio/lib_sscanf.c +++ b/libs/libc/stdio/lib_sscanf.c @@ -1,9 +1,9 @@ /**************************************************************************** * libs/libc/stdio/lib_sscanf.c * - * Copyright (C) 2007, 2008, 2011-2014, 2016 Gregory Nutt. All rights - * reserved. + * Copyright (C) 2007-2009, 2011, 2019 Gregory Nutt. All rights reserved. * Author: Gregory Nutt + * Johannes Schock * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -38,903 +38,34 @@ * Included Files ****************************************************************************/ -#include - -#include - -#include -#include -#include #include -#include -#include -#include -#include - -/**************************************************************************** - * Pre-processor Definitions - ****************************************************************************/ - -#define MAXLN 128 - -#ifndef MIN -# define MIN(a,b) (((a) < (b)) ? (a) : (b)) -#endif - -#ifndef MAX -# define MAX(a,b) (((a) > (b)) ? (a) : (b)) -#endif - -/**************************************************************************** - * Public Function Prototypes - ****************************************************************************/ - -int vsscanf(FAR const char *buf, FAR const char *fmt, va_list ap); - -/**************************************************************************** - * Private Data - ****************************************************************************/ - -static const char spaces[] = " \t\n\r\f\v"; - -/**************************************************************************** - * Private Functions - ****************************************************************************/ - -/**************************************************************************** - * Name: findwidth - * - * Description: - * Try to figure out the width of the input data. - * - ****************************************************************************/ - -static int findwidth(FAR const char *buf, FAR const char *fmt) -{ -#if 0 /* Behavior no longer supported */ - FAR const char *next = fmt + 1; - - /* No... is there a space after the format? Or does the format string end - * here? - */ - - if (isspace(*next) || *next == 0) - { - /* Use the input up until the first white space is encountered. */ - - return strcspn(buf, spaces); - } - - /* No.. Another possibility is the format character is followed by - * some recognizable delimiting value. - */ - - if (*next != '%') - { - /* If so we will say that the string ends there if we can find that - * delimiter in the input string. - */ - - FAR const char *ptr = strchr(buf, *next); - if (ptr != NULL) - { - return (int)(ptr - buf); - } - } - - /* No... the format has no delimiter and is back-to-back with the next - * format (or is followed by a delimiter that does not exist in the - * input string). At this point we just bail and Use the input up until - * the first white space is encountered. - * - * NOTE: This means that values from the following format may be - * concatenated with the first. This is a bug. We have no generic way of - * determining the width of the data if there is no fieldwidth, no space - * separating the input, and no usable delimiter character. - */ -#endif - - /* Use the input up until the first white space is encountered. */ - - return strcspn(buf, spaces); -} - -/**************************************************************************** - * Name: findscanset - * - * Description: - * Fill in the given table from the scanset at the given format. - * Return a pointer to the character the closing ']'. - * The table has a 1 wherever characters should be considered part of the - * scanset. - * - * Function findscanset based on source function __sccl of FreeBSD - * (https://github.com/lattera/freebsd/blob/master/sys/kern/subr_scanf.c) - * - ****************************************************************************/ - -#ifdef CONFIG_LIBC_SCANSET -static FAR const char *findscanset(FAR const char *fmt, - FAR unsigned char set[32]) -{ - int c; - int n; - int v; - int i; - - fmt++; /* Skip '[' */ - - /* First `clear' the whole table */ - - c = *fmt++; /* First char hat => negated scanset */ - if (c == '^') - { - v = 1; /* Default => accept */ - c = *fmt++; /* Get new first char */ - } - else - { - v = 0; /* Default => reject */ - } - - memset(set, 0, 32); - if (c == 0) - { - goto doexit; - } - - /* Now set the entries corresponding to the actual scanset - * to the opposite of the above. - * - * The first character may be ']' (or '-') without being special; - * the last character may be '-'. - */ - - for (;;) - { - set[c / 8] |= (1 << (c % 8)); /* Take character c */ - -doswitch: - n = *fmt++; /* Examine the next */ - switch (n) - { - case 0: /* Format ended too soon */ - case ']': /* End of scanset */ - goto doexit; - - case '-': - /* A scanset of the form - * - * [01+-] - * - * is defined as "the digit 0, the digit 1, the character +, the - * character -", but the effect of a scanset such as - * - * [a-zA-Z0-9] - * - * is implementation defined. The V7 Unix scanf treats "a-z" as - * "the letters a through z", but treats "a-a" as "the letter a, - * the character -, and the letter a". - * - * For compatibility, the `-' is not considerd to define a range - * if the character following it is either a close bracket - * (required by ANSI) or is not numerically greater than the - * character* we just stored in the table (c). - */ - - n = *fmt; - if (n == ']' || n < c) - { - c = '-'; - break; /* Resume the for(;;) */ - } - - fmt++; - do - { - /* Fill in the range */ - - c++; - set[c / 8] |= (1 << (c % 8)); /* Take character c */ - } - while (c < n); - - /* Alas, the V7 Unix scanf also treats formats such as [a-c-e] as - * "the letters a through e". This too is permitted by the - * standard. - */ - - goto doswitch; - - default: /* Just another character */ - c = n; - break; - } - } - -doexit: - if (v) /* Default => accept */ - { - for (i = 0; i < 32; i++) /* Invert all */ - { - set[i] ^= 0xFF; - } - } - - return (fmt - 1); -} -#endif - -/**************************************************************************** - * Name: scansetwidth - ****************************************************************************/ - -#ifdef CONFIG_LIBC_SCANSET -static int scansetwidth(FAR const char *buf, - FAR const unsigned char set[32]) -{ - FAR const char *next = buf; - int c; - - while (*next) - { - c = *next; - if ((set[c / 8] & (1 << (c % 8))) == 0) - { - break; - } - - next++; - } - - return (next - buf); -} -#endif +#include +#include "libc.h" /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** - * Name: sscanf - * - * Description: - * ANSI standard sscanf implementation. - * + * sscanf ****************************************************************************/ -int sscanf(FAR const char *buf, FAR const char *fmt, ...) +int sscanf(FAR const char *buf, FAR const IPTR char *fmt, ...) { + struct lib_meminstream_s meminstream; va_list ap; - int count; + int n; + + /* Initialize a memory stream to write to the buffer */ + + lib_meminstream((FAR struct lib_meminstream_s *)&meminstream, buf, + LIB_BUFLEN_UNKNOWN); + + /* Then let lib_vsscanf do the real work */ va_start(ap, fmt); - count = vsscanf((FAR const char *)buf, fmt, ap); + n = lib_vsscanf((FAR struct lib_instream_s *)&meminstream.public, NULL, + fmt, ap); va_end(ap); - return count; -} - -/**************************************************************************** - * Name: vsscanf - * - * Description: - * ANSI standard vsscanf implementation. - * - ****************************************************************************/ - -int vsscanf(FAR const char *buf, FAR const char *fmt, va_list ap) -{ - FAR const char *bufstart; - FAR char *tv; - FAR const char *tc; - bool lflag; - bool noassign; - int count; - int width; - int fwidth; - int base = 10; - char tmp[MAXLN]; -#ifdef CONFIG_LIBC_SCANSET - unsigned char set[32]; /* Bit field (256 / 8) */ -#endif - - linfo("buf=\"%s\" fmt=\"%s\"\n", buf, fmt); - - /* Remember the start of the input buffer. We will need this for %n - * calculations. - */ - - bufstart = buf; - - /* Parse the format, extracting values from the input buffer as needed */ - - count = 0; - width = 0; - noassign = false; - lflag = false; - - /* Loop until all characters in the fmt string have been processed. We - * may have to continue loop after reaching the end the input data in - * order to handle trailing %n format specifiers. - */ - - while (*fmt) - { - /* Skip over white space */ - - while (isspace(*fmt)) - { - fmt++; - } - - /* Check for a conversion specifier */ - - if (*fmt == '%') - { - linfo("Specifier found\n"); - - /* Check for qualifiers on the conversion specifier */ - - fmt++; - for (; *fmt; fmt++) - { - linfo("Processing %c\n", *fmt); - -#ifdef CONFIG_LIBC_SCANSET - if (strchr("dibouxcsefgn[%", *fmt)) -#else - if (strchr("dibouxcsefgn%", *fmt)) -#endif - { - break; - } - - if (*fmt == '*') - { - noassign = true; - } - else if (*fmt == 'l' || *fmt == 'L') - { - /* NOTE: Missing check for long long ('ll') */ - - lflag = true; - } - else if (*fmt >= '1' && *fmt <= '9') - { - for (tc = fmt; isdigit(*fmt); fmt++); - strncpy(tmp, tc, fmt - tc); - tmp[fmt - tc] = '\0'; - width = atoi(tmp); - fmt--; - } - } - - /* Process %s: String conversion */ - - if (*fmt == 's') - { - linfo("Performing string conversion\n"); - - /* Get a pointer to the char * value. We need to do this even - * if we have reached the end of the input data in order to - * update the 'ap' variable. - */ - - tv = NULL; /* To avoid warnings about begin uninitialized */ - if (!noassign) - { - tv = va_arg(ap, FAR char *); - tv[0] = '\0'; - } - - /* But we only perform the data conversion is we still have - * bytes remaining in the input data stream. - */ - - if (*buf) - { - /* Skip over white space */ - - while (isspace(*buf)) - { - buf++; - } - - /* Guess a field width using some heuristics */ - - fwidth = findwidth(buf, fmt); - - /* Use the actual field's width if 1) no fieldwidth - * specified or 2) the actual field's width is smaller - * than fieldwidth specified - */ - - if (!width || fwidth < width) - { - width = fwidth; - } - - width = MIN(sizeof(tmp) - 1, width); - - /* Copy the string (if we are making an assignment) */ - - if (!noassign) - { - strncpy(tv, buf, width); - tv[width] = '\0'; - count++; - } - - /* Update the buffer pointer past the string in the input */ - - buf += width; - } - } - -#ifdef CONFIG_LIBC_SCANSET - /* Process %[: Scanset conversion */ - - if (*fmt == '[') - { - linfo("Performing scanset conversion\n"); - - fmt = findscanset(fmt, set); /* find scanset */ - - /* Get a pointer to the char * value. We need to do this even - * if we have reached the end of the input data in order to - * update the 'ap' variable. - */ - - tv = NULL; /* To avoid warnings about begin uninitialized */ - if (!noassign) - { - tv = va_arg(ap, FAR char *); - tv[0] = '\0'; - } - - /* But we only perform the data conversion is we still have - * bytes remaining in the input data stream. - */ - - if (*buf) - { - /* Skip over white space */ - - while (isspace(*buf)) - { - buf++; - } - - /* Guess a field width using some heuristics */ - - fwidth = scansetwidth(buf, set); - - /* Use the actual field's width if 1) no fieldwidth - * specified or 2) the actual field's width is smaller - * than fieldwidth specified - */ - - if (!width || fwidth < width) - { - width = fwidth; - } - - width = MIN(sizeof(tmp) - 1, width); - - /* Copy the string (if we are making an assignment) */ - - if (!noassign) - { - strncpy(tv, buf, width); - tv[width] = '\0'; - count++; - } - - /* Update the buffer pointer past the string in the input */ - - buf += width; - } - } -#endif - - /* Process %c: Character conversion */ - - else if (*fmt == 'c') - { - linfo("Performing character conversion\n"); - - /* Get a pointer to the char * value. We need to do this even - * if we have reached the end of the input data in order to - * update the 'ap' variable. - */ - - tv = NULL; /* To avoid warnings about beign uninitialized */ - if (!noassign) - { - tv = va_arg(ap, FAR char *); - tv[0] = '\0'; - } - - /* But we only perform the data conversion is we still have - * bytes remaining in the input data stream. - */ - - if (*buf) - { - /* Was a field width specified? */ - - if (!width) - { - /* No, then width is this one single character */ - - width = 1; - } - - /* Copy the character(s) (if we are making an assignment) */ - - if (!noassign) - { - strncpy(tv, buf, width); - count++; - } - - /* Update the buffer pointer past the character(s) in the - * input - */ - - buf += width; - } - } - - /* Process %d, %o, %b, %x, %u: Various integer conversions */ - - else if (strchr("dobxu", *fmt)) - { - FAR long *plong = NULL; - FAR int *pint = NULL; - bool sign; - - linfo("Performing integer conversion\n"); - - /* Get a pointer to the integer value. We need to do this even - * if we have reached the end of the input data in order to - * update the 'ap' variable. - */ - - if (!noassign) - { - /* We have to check whether we need to return a long or an - * int. - */ - - if (lflag) - { - plong = va_arg(ap, FAR long *); - *plong = 0; - } - else - { - pint = va_arg(ap, FAR int *); - *pint = 0; - } - } - - /* But we only perform the data conversion if we still have - * bytes remaining in the input data stream. - */ - - if (*buf) - { - FAR char *endptr; - int errsave; - long tmplong; - - /* Skip over any white space before the integer string */ - - while (isspace(*buf)) - { - buf++; - } - - /* The base of the integer conversion depends on the - * specific conversion specification. - */ - - sign = false; - switch (*fmt) - { - default: - case 'd': - sign = true; - /* FALLTHROUGH */ - case 'u': - base = 10; - break; - - case 'X': - case 'x': - base = 16; - break; - - case 'o': - base = 8; - break; - - case 'b': - base = 2; - break; - } - - /* Was a field width specified? */ - - if (!width) - { - /* No... Guess a field width using some heuristics */ - - int tmpwidth = findwidth(buf, fmt); - width = MIN(sizeof(tmp) - 1, tmpwidth); - } - - /* Copy the numeric string into a temporary working - * buffer. - */ - - strncpy(tmp, buf, width); - tmp[width] = '\0'; - - linfo("tmp[]=\"%s\"\n", tmp); - - /* Perform the integer conversion */ - /* Preserve the errno value */ - - errsave = get_errno(); - set_errno(0); - if (sign) - { - tmplong = strtol(tmp, &endptr, base); - } - else - { - tmplong = strtoul(tmp, &endptr, base); - } - - /* Check if the number was successfully converted */ - - if (tmp == endptr || get_errno() == ERANGE) - { - return count; - } - - /* Move by the actual number of characters converted */ - - buf += (endptr - tmp); - set_errno(errsave); - if (!noassign) - { - - /* We have to check whether we need to return a long - * or an int. - */ - - if (lflag) - { - linfo("Return %ld to 0x%p\n", tmplong, plong); - *plong = tmplong; - } - else - { - linfo("Return %ld to 0x%p\n", tmplong, pint); - *pint = (int)tmplong; - } - - count++; - } - } - } - - /* Process %a, %A, %f, %F, %e, %E, %g, and %G: Floating point - * conversions - */ - - else if (strchr("aAfFeEgG", *fmt) != NULL) - { -#ifdef CONFIG_HAVE_DOUBLE - FAR double *pd = NULL; -#endif - FAR float *pf = NULL; - - linfo("Performing floating point conversion\n"); - - /* Get a pointer to the double value. We need to do this even - * if we have reached the end of the input data in order to - * update the 'ap' variable. - */ - - if (!noassign) - { - /* We have to check whether we need to return a float or a - * double. - */ - -#ifdef CONFIG_HAVE_DOUBLE - if (lflag) - { - pd = va_arg(ap, FAR double *); - *pd = 0.0; - } - else -#endif - { - pf = va_arg(ap, FAR float *); - *pf = 0.0; - } - } - -#ifdef CONFIG_LIBC_FLOATINGPOINT - /* But we only perform the data conversion is we still have - * bytes remaining in the input data stream. - */ - - if (*buf) - { - FAR char *endptr; - int errsave; -#ifdef CONFIG_HAVE_DOUBLE - double dvalue; -#endif - float fvalue; - - /* Skip over any white space before the real string */ - - while (isspace(*buf)) - { - buf++; - } - - /* Was a fieldwidth specified? */ - - if (!width) - { - /* No... Guess a field width using some heuristics */ - - int tmpwidth = findwidth(buf, fmt); - width = MIN(sizeof(tmp) - 1, tmpwidth); - } - - /* Copy the real string into a temporary working buffer. */ - - strncpy(tmp, buf, width); - tmp[width] = '\0'; - - linfo("tmp[]=\"%s\"\n", tmp); - - /* Perform the floating point conversion */ - /* Preserve the errno value */ - - errsave = get_errno(); - set_errno(0); - -#ifdef CONFIG_HAVE_DOUBLE - if (lflag) - { - /* Get the converted double value */ - - dvalue = strtod(tmp, &endptr); - } - else -#endif - { - fvalue = strtof(tmp, &endptr); - } - - /* Check if the number was successfully converted */ - - if (tmp == endptr || get_errno() == ERANGE) - { - return count; - } - - /* Move by the actual number of characters converted */ - - buf += (endptr - tmp); - set_errno(errsave); - - if (!noassign) - { - - /* We have to check whether we need to return a float - * or a double. - */ - -#ifdef CONFIG_HAVE_DOUBLE - if (lflag) - { - /* Return the double value */ - - linfo("Return %f to %p\n", dvalue, pd); - *pd = dvalue; - } - else -#endif - { - /* Return the float value */ - - linfo("Return %f to %p\n", (double)fvalue, pf); - *pf = fvalue; - } - - count++; - } - } -#endif - } - - /* Process %n: Character count */ - - else if (*fmt == 'n') - { - linfo("Performing character count\n"); - - if (!noassign) - { - size_t nchars = (size_t)(buf - bufstart); - - /* Note %n does not count as a conversion */ - - if (lflag) - { - FAR long *plong = va_arg(ap, FAR long *); - *plong = (long)nchars; - } - else - { - FAR int *pint = va_arg(ap, FAR int *); - *pint = (int)nchars; - } - } - } - - width = 0; - noassign = false; - lflag = false; - - fmt++; - } - - /* It is not a conversion specifier */ - - else if (*buf) - { - /* Skip over any leading spaces in the input buffer */ - - while (isspace(*buf)) - { - buf++; - } - - /* Skip over matching characters in the buffer and format */ - - if (*fmt != *buf) - { - break; - } - else - { - fmt++; - buf++; - } - } - else - { - /* NULL terminator encountered */ - - break; - } - } - - /* sscanf is required to return EOF if the input ends before the first - * matching failure or conversion. - */ - - return count ? count : EOF; + return n; } diff --git a/libs/libc/stdio/lib_vfscanf.c b/libs/libc/stdio/lib_vfscanf.c new file mode 100644 index 0000000000..7c5cb21768 --- /dev/null +++ b/libs/libc/stdio/lib_vfscanf.c @@ -0,0 +1,85 @@ +/**************************************************************************** + * libs/libc/stdio/lib_vfscanf.c + * + * Copyright (C) 2019 Gregory Nutt. All rights reserved. + * Author: Johannes Shock + * + * 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. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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 OWNER 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +#include "libc.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int vfscanf(FAR FILE *stream, FAR const IPTR char *fmt, va_list ap) +{ + struct lib_stdinstream_s stdinstream; + int n = ERROR; + int lastc; + + if (stream) + { + /* Wrap the stream in a stream object and let lib_vsscanf do the work. */ + + lib_stdinstream(&stdinstream, stream); + + /* Hold the stream semaphore throughout the lib_vsscanf call so that + * this thread can get its entire message out before being pre-empted by + * the next thread. + */ + + lib_take_semaphore(stream); + + n = lib_vsscanf(&stdinstream.public, &lastc, fmt, ap); + + /* The lib_vsscanf function reads always one character more, this + * character needs to be written back. + */ + + if (lastc != EOF) + { + ungetc(lastc, stream); + } + + lib_give_semaphore(stream); + } + + return n; +} diff --git a/libs/libc/stdlib/lib_strtol.c b/libs/libc/stdlib/lib_strtol.c index 9fb07420ea..7412d96c07 100644 --- a/libs/libc/stdlib/lib_strtol.c +++ b/libs/libc/stdlib/lib_strtol.c @@ -1,7 +1,7 @@ /**************************************************************************** * libs/libc/stdlib/lib_strtol.c * - * Copyright (C) 2007, 2009, 2011 Gregory Nutt. All rights reserved. + * Copyright (C) 2007, 2009, 2011, 2019 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -45,10 +45,6 @@ #include "libc.h" -/**************************************************************************** - * Private Functions - ****************************************************************************/ - /**************************************************************************** * Public Functions ****************************************************************************/ @@ -74,7 +70,8 @@ long strtol(FAR const char *nptr, FAR char **endptr, int base) { unsigned long accum = 0; - bool negate = false; + long retval = 0; + char sign = 0; if (nptr) { @@ -84,13 +81,9 @@ long strtol(FAR const char *nptr, FAR char **endptr, int base) /* Check for leading + or - */ - if (*nptr == '-') - { - negate = true; - nptr++; - } - else if (*nptr == '+') + if (*nptr == '-' || *nptr == '+') { + sign = *nptr; nptr++; } @@ -100,25 +93,46 @@ long strtol(FAR const char *nptr, FAR char **endptr, int base) /* Correct the sign of the result and check for overflow */ - if (negate) + if (sign == '-') { const unsigned long limit = ((unsigned long)-(LONG_MIN + 1)) + 1; if (accum > limit) { set_errno(ERANGE); - return LONG_MIN; + retval = LONG_MIN; + } + else + { + retval = (accum == limit) ? LONG_MIN : -(long)accum; } - - return (accum == limit) ? LONG_MIN : -(long)accum; } - - if (accum > LONG_MAX) + else { - set_errno(ERANGE); - return LONG_MAX; + if (accum > LONG_MAX) + { + set_errno(ERANGE); + retval = LONG_MAX; + } + else + { + retval = accum; + } } } - return (long)accum; + /* Return the final pointer to the unused value */ + + if (endptr) + { + if (sign) + { + if (*((*endptr) - 1) == sign) + { + (*endptr)--; + } + } + } + + return retval; } diff --git a/libs/libc/stdlib/lib_strtoll.c b/libs/libc/stdlib/lib_strtoll.c index fabc06e969..9eb76f1825 100644 --- a/libs/libc/stdlib/lib_strtoll.c +++ b/libs/libc/stdlib/lib_strtoll.c @@ -1,7 +1,7 @@ /**************************************************************************** * libs/libc/stdlib/lib_strtoll.c * - * Copyright (C) 2009, 2011 Gregory Nutt. All rights reserved. + * Copyright (C) 2009, 2011, 2019 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -47,10 +47,6 @@ #ifdef CONFIG_HAVE_LONG_LONG -/**************************************************************************** - * Private Functions - ****************************************************************************/ - /**************************************************************************** * Public Functions ****************************************************************************/ @@ -76,7 +72,8 @@ long long strtoll(FAR const char *nptr, FAR char **endptr, int base) { unsigned long long accum = 0; - bool negate = false; + long long retval = 0; + char sign = 0; if (nptr) { @@ -86,13 +83,9 @@ long long strtoll(FAR const char *nptr, FAR char **endptr, int base) /* Check for leading + or - */ - if (*nptr == '-') - { - negate = true; - nptr++; - } - else if (*nptr == '+') + if (*nptr == '-' || *nptr == '+') { + sign = *nptr; nptr++; } @@ -102,27 +95,49 @@ long long strtoll(FAR const char *nptr, FAR char **endptr, int base) /* Correct the sign of the result and check for overflow */ - if (negate) + if (sign == '-') { - const unsigned long long limit = ((unsigned long long)-(LLONG_MIN + 1)) + 1; + const unsigned long long limit = + ((unsigned long long)-(LLONG_MIN + 1)) + 1; if (accum > limit) { set_errno(ERANGE); - return LLONG_MIN; + retval = LLONG_MIN; + } + else + { + retval = (accum == limit) ? LLONG_MIN : -(long long)accum; } - - return (accum == limit) ? LLONG_MIN : -(long long)accum; } - - if (accum > LLONG_MAX) + else { - set_errno(ERANGE); - return LLONG_MAX; + if (accum > LLONG_MAX) + { + set_errno(ERANGE); + return LLONG_MAX; + } + else + { + retval = accum; + } } } - return (long long)accum; + /* Return the final pointer to the unused value */ + + if (endptr) + { + if (sign) + { + if (*((*endptr) - 1) == sign) + { + (*endptr)--; + } + } + } + + return retval; } #endif diff --git a/libs/libc/stdlib/lib_strtoul.c b/libs/libc/stdlib/lib_strtoul.c index f902c3c66c..73d94aed19 100644 --- a/libs/libc/stdlib/lib_strtoul.c +++ b/libs/libc/stdlib/lib_strtoul.c @@ -1,7 +1,8 @@ /**************************************************************************** * /libs/libc/stdlib/lib_strtoul.c * - * Copyright (C) 2007, 2009, 2011, 2016-2017 Gregory Nutt. All rights reserved. + * Copyright (C) 2007, 2009, 2011, 2016-2017, 2019 Gregory Nutt. + * All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -70,6 +71,7 @@ unsigned long strtoul(FAR const char *nptr, FAR char **endptr, int base) unsigned long accum = 0; unsigned long prev; int value; + char sign = 0; if (nptr) { @@ -77,6 +79,14 @@ unsigned long strtoul(FAR const char *nptr, FAR char **endptr, int base) lib_skipspace(&nptr); + /* Check for leading + or - already done for strtol */ + + if (*nptr == '-' || *nptr == '+') + { + sign = *nptr; + nptr++; + } + /* Check for unspecified or incorrect base */ base = lib_checkbase(base, &nptr); @@ -84,33 +94,47 @@ unsigned long strtoul(FAR const char *nptr, FAR char **endptr, int base) if (base < 0) { set_errno(EINVAL); - return 0; + accum = 0; } - - /* Accumulate each "digit" */ - - while (lib_isbasedigit(*nptr, base, &value)) + else { - prev = accum; - accum = accum*base + value; - nptr++; + /* Accumulate each "digit" */ - /* Check for overflow */ - - if (accum < prev) + while (lib_isbasedigit(*nptr, base, &value)) { - set_errno(ERANGE); - accum = ULONG_MAX; - break; + prev = accum; + accum = accum * base + value; + nptr++; + + /* Check for overflow */ + + if (accum < prev) + { + set_errno(ERANGE); + accum = ULONG_MAX; + break; + } + } + + if (sign == '-') + { + accum = (~accum) + 1; } } + } - /* Return the final pointer to the unused value */ + /* Return the final pointer to the unused value */ - if (endptr) + if (endptr) + { + if (sign) { - *endptr = (FAR char *)nptr; + if (*(nptr - 1) == sign) + { + nptr--; + } } + *endptr = (FAR char *)nptr; } return accum; diff --git a/libs/libc/stdlib/lib_strtoull.c b/libs/libc/stdlib/lib_strtoull.c index 3b3b1127e6..cb6ac51153 100644 --- a/libs/libc/stdlib/lib_strtoull.c +++ b/libs/libc/stdlib/lib_strtoull.c @@ -1,7 +1,7 @@ /**************************************************************************** * /libs/libc/stdlib/lib_strtoull.c * - * Copyright (C) 2009, 2010, 2016 Gregory Nutt. All rights reserved. + * Copyright (C) 2009, 2010, 2016, 2019 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -73,6 +73,7 @@ unsigned long long strtoull(FAR const char *nptr, FAR char **endptr, int base) unsigned long long accum = 0; unsigned long long prev; int value; + char sign = 0; if (nptr) { @@ -80,40 +81,62 @@ unsigned long long strtoull(FAR const char *nptr, FAR char **endptr, int base) lib_skipspace(&nptr); - /* Check for unspecified base */ + /* Check for leading + or - already done for strtol */ + + if (*nptr == '-' || *nptr == '+') + { + sign = *nptr; + nptr++; + } + + /* Check for unspecified or incorrect base */ base = lib_checkbase(base, &nptr); if (base < 0) { set_errno(EINVAL); - return 0; + accum = 0; } - - /* Accumulate each "digit" */ - - while (lib_isbasedigit(*nptr, base, &value)) + else { - prev = accum; - accum = accum*base + value; - nptr++; + /* Accumulate each "digit" */ - /* Check for overflow */ - - if (accum < prev) + while (lib_isbasedigit(*nptr, base, &value)) { - set_errno(ERANGE); - accum = ULLONG_MAX; - break; + prev = accum; + accum = accum * base + value; + nptr++; + + /* Check for overflow */ + + if (accum < prev) + { + set_errno(ERANGE); + accum = ULLONG_MAX; + break; + } + } + + if (sign == '-') + { + accum = (~accum) + 1; } } + } - /* Return the final pointer to the unused value */ + /* Return the final pointer to the unused value */ - if (endptr) + if (endptr) + { + if (sign) { - *endptr = (char *)nptr; + if (*(nptr - 1) == sign) + { + nptr--; + } } + *endptr = (FAR char *)nptr; } return accum;