diff options
author | patacongo <patacongo@42af7a65-404d-4744-a932-0658087f49c3> | 2013-03-18 00:41:42 +0000 |
---|---|---|
committer | patacongo <patacongo@42af7a65-404d-4744-a932-0658087f49c3> | 2013-03-18 00:41:42 +0000 |
commit | e043d545247953df1980d119fa1a477cbd9f9b18 (patch) | |
tree | 259d5c1dc75b5a806095ebd8b7a11a60d8b22331 | |
parent | 7330994698ed840623a487f1a394e39580c8ce97 (diff) | |
download | px4-nuttx-e043d545247953df1980d119fa1a477cbd9f9b18.tar.gz px4-nuttx-e043d545247953df1980d119fa1a477cbd9f9b18.tar.bz2 px4-nuttx-e043d545247953df1980d119fa1a477cbd9f9b18.zip |
Fix for trailing %n bug in sscanf (with help from Lorenz Meier)
git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@5754 42af7a65-404d-4744-a932-0658087f49c3
-rw-r--r-- | nuttx/ChangeLog | 4 | ||||
-rw-r--r-- | nuttx/libc/stdio/lib_sscanf.c | 360 |
2 files changed, 242 insertions, 122 deletions
diff --git a/nuttx/ChangeLog b/nuttx/ChangeLog index ef49788b4..a81b7eb86 100644 --- a/nuttx/ChangeLog +++ b/nuttx/ChangeLog @@ -4362,3 +4362,7 @@ happen only under one condition: When the kernel system call logic calls back into user space in order to allocate user space memory. So it is expected that the maximum nesting level will be only 2 (2013-03-17). + * libc/stdio/lib_sccanf.c: Correct an error in sscanf. If %n occurs in + the format statement after the input data stream has been fully + parsed, the %n format specifier will not be handled. Reported by + Lorenz Meier (and also earlier by Kate) (2013-03-17). diff --git a/nuttx/libc/stdio/lib_sscanf.c b/nuttx/libc/stdio/lib_sscanf.c index 3666cbf2b..e6ba5b5e8 100644 --- a/nuttx/libc/stdio/lib_sscanf.c +++ b/nuttx/libc/stdio/lib_sscanf.c @@ -197,12 +197,12 @@ int vsscanf(FAR char *buf, FAR const char *fmt, va_list ap) noassign = false; lflag = false; - /* NOTE that there is a flaw in this loop logic: The fmt string often - * terminates with %n which would have to be processes at the end of the - * buf string. That won't happen here. + /* 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 && *buf) + while (*fmt) { /* Skip over white space */ @@ -218,6 +218,7 @@ int vsscanf(FAR char *buf, FAR const char *fmt, va_list ap) lvdbg("vsscanf: Specifier found\n"); /* Check for qualifiers on the conversion specifier */ + fmt++; for (; *fmt; fmt++) { @@ -254,28 +255,50 @@ int vsscanf(FAR char *buf, FAR const char *fmt, va_list ap) { lvdbg("vsscanf: Performing string conversion\n"); - while (isspace(*buf)) + /* 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) { - buf++; + tv = va_arg(ap, char*); + tv[0] = '\0'; } - /* Was a fieldwidth specified? */ + /* But we only perform the data conversion is we still have + * bytes remaining in the input data stream. + */ - if (!width) + if (*buf) { - /* No... Guess a field width using some heuristics */ + while (isspace(*buf)) + { + buf++; + } - width = findwidth(buf, fmt); - } + /* Was a fieldwidth specified? */ - if (!noassign) - { - tv = va_arg(ap, char*); - strncpy(tv, buf, width); - tv[width] = '\0'; - } + if (!width) + { + /* No... Guess a field width using some heuristics */ - buf += width; + width = findwidth(buf, fmt); + } + + /* Copy the string (if we are making an assignment) */ + + if (!noassign) + { + strncpy(tv, buf, width); + tv[width] = '\0'; + } + + /* Update the buffer pointer past the string in the input */ + + buf += width; + } } /* Process %c: Character conversion */ @@ -284,23 +307,47 @@ int vsscanf(FAR char *buf, FAR const char *fmt, va_list ap) { lvdbg("vsscanf: Performing character conversion\n"); - /* Was a fieldwidth specified? */ + /* 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. + */ - if (!width) + tv = NULL; /* To avoid warnings about beign uninitialized */ + if (!noassign) { - /* No, then width is this one single character */ - - width = 1; + tv = va_arg(ap, char*); + tv[0] = '\0'; } - if (!noassign) + /* But we only perform the data conversion is we still have + * bytes remaining in the input data stream. + */ + + if (*buf) { - tv = va_arg(ap, char*); - strncpy(tv, buf, width); - tv[width] = '\0'; - } + /* Was a fieldwidth specified? */ + + if (!width) + { + /* No, then width is this one single character */ + + width = 1; + } - buf += width; + /* Copy the character(s) (if we are making an assignment) */ + + if (!noassign) + { + strncpy(tv, buf, width); + tv[width] = '\0'; + } + + /* Update the buffer pointer past the character(s) in the + * input + */ + + buf += width; + } } /* Process %d, %o, %b, %x, %u: Various integer conversions */ @@ -309,72 +356,110 @@ int vsscanf(FAR char *buf, FAR const char *fmt, va_list ap) { lvdbg("vsscanf: Performing integer conversion\n"); - /* Skip over any white space before the integer string */ + /* 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. + */ - while (isspace(*buf)) + FAR long *plong = NULL; + FAR int *pint = NULL; + if (!noassign) { - buf++; + /* We have to check whether we need to return a long or an + * int. + */ + + if (lflag) + { + plong = va_arg(ap, long*); + *plong = 0; + } + else + { + pint = va_arg(ap, int*); + *pint = 0; + } } - /* The base of the integer conversion depends on the specific - * conversion specification. + /* But we only perform the data conversion is we still have + * bytes remaining in the input data stream. */ - if (*fmt == 'd' || *fmt == 'u') - { - base = 10; - } - else if (*fmt == 'x') - { - base = 16; - } - else if (*fmt == 'o') - { - base = 8; - } - else if (*fmt == 'b') + if (*buf) { - base = 2; - } + /* Skip over any white space before the integer string */ - /* Was a fieldwidth specified? */ + while (isspace(*buf)) + { + buf++; + } - if (!width) - { - /* No... Guess a field width using some heuristics */ + /* The base of the integer conversion depends on the + * specific conversion specification. + */ - width = findwidth(buf, fmt); - } + if (*fmt == 'd' || *fmt == 'u') + { + base = 10; + } + else if (*fmt == 'x') + { + base = 16; + } + else if (*fmt == 'o') + { + base = 8; + } + else if (*fmt == 'b') + { + base = 2; + } - /* Copy the numeric string into a temporary working buffer. */ + /* Was a fieldwidth specified? */ - strncpy(tmp, buf, width); - tmp[width] = '\0'; + if (!width) + { + /* No... Guess a field width using some heuristics */ - lvdbg("vsscanf: tmp[]=\"%s\"\n", tmp); + width = findwidth(buf, fmt); + } - /* Perform the integer conversion */ + /* Copy the numeric string into a temporary working + * buffer. + */ - buf += width; - if (!noassign) - { + strncpy(tmp, buf, width); + tmp[width] = '\0'; + + lvdbg("vsscanf: tmp[]=\"%s\"\n", tmp); + + /* Perform the integer conversion */ + + buf += width; + if (!noassign) + { #ifdef SDCC - char *endptr; - long tmplong = strtol(tmp, &endptr, base); + char *endptr; + long tmplong = strtol(tmp, &endptr, base); #else - long tmplong = strtol(tmp, NULL, base); + long tmplong = strtol(tmp, NULL, base); #endif - if (lflag) - { - long *plong = va_arg(ap, long*); - lvdbg("vsscanf: Return %ld to 0x%p\n", tmplong, plong); - *plong = tmplong; - } - else - { - int *pint = va_arg(ap, int*); - lvdbg("vsscanf: Return %ld to 0x%p\n", tmplong, pint); - *pint = (int)tmplong; + /* We have to check whether we need to return a long + * or an int. + */ + + if (lflag) + { + lvdbg("vsscanf: Return %ld to 0x%p\n", + tmplong, plong); + *plong = tmplong; + } + else + { + lvdbg("vsscanf: Return %ld to 0x%p\n", + tmplong, pint); + *pint = (int)tmplong; + } } } } @@ -383,68 +468,95 @@ int vsscanf(FAR char *buf, FAR const char *fmt, va_list ap) else if (*fmt == 'f') { -#ifndef CONFIG_LIBC_FLOATINGPOINT - /* No floating point conversions */ - - void *pv = va_arg(ap, void*); - - lvdbg("vsscanf: Return 0.0 to %p\n", pv); - *((double_t*)pv) = 0.0; -#else lvdbg("vsscanf: Performing floating point conversion\n"); - /* Skip over any white space before the real string */ + /* 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. + */ - while (isspace(*buf)) +#ifdef CONFIG_HAVE_DOUBLE + FAR double_t *pd = NULL; +#endif + FAR float *pf = NULL; + if (!noassign) { - buf++; + /* We have to check whether we need to return a float or a + * double. + */ + +#ifdef CONFIG_HAVE_DOUBLE + if (lflag) + { + pd = va_arg(ap, double_t*); + *pd = 0.0; + } + else +#endif + { + pf = va_arg(ap, float*); + *pf = 0.0; + } } - /* Was a fieldwidth specified? */ +#ifdef CONFIG_LIBC_FLOATINGPOINT + /* But we only perform the data conversion is we still have + * bytes remaining in the input data stream. + */ - if (!width) + if (*buf) { - /* No... Guess a field width using some heuristics */ + /* Skip over any white space before the real string */ - width = findwidth(buf, fmt); - } + while (isspace(*buf)) + { + buf++; + } - /* Copy the real string into a temporary working buffer. */ + /* Was a fieldwidth specified? */ - strncpy(tmp, buf, width); - tmp[width] = '\0'; - buf += width; + if (!width) + { + /* No... Guess a field width using some heuristics */ - lvdbg("vsscanf: tmp[]=\"%s\"\n", tmp); + width = findwidth(buf, fmt); + } - /* Perform the floating point conversion */ + /* Copy the real string into a temporary working buffer. */ - if (!noassign) - { - /* strtod always returns a double */ + strncpy(tmp, buf, width); + tmp[width] = '\0'; + buf += width; + + lvdbg("vsscanf: tmp[]=\"%s\"\n", tmp); + + /* Perform the floating point conversion */ + + if (!noassign) + { + /* strtod always returns a double */ #ifdef SDCC - char *endptr; - double_t dvalue = strtod(tmp,&endptr); + FAR char *endptr; + double_t dvalue = strtod(tmp,&endptr); #else - double_t dvalue = strtod(tmp, NULL); + double_t dvalue = strtod(tmp, NULL); #endif - void *pv = va_arg(ap, void*); - - lvdbg("vsscanf: Return %f to %p\n", dvalue, pv); - - /* But we have to check whether we need to return a - * float or a double. - */ + /* We have to check whether we need to return a float + * or a double. + */ #ifdef CONFIG_HAVE_DOUBLE - if (lflag) - { - *((double_t*)pv) = dvalue; - } - else + if (lflag) + { + lvdbg("vsscanf: Return %f to %p\n", dvalue, pd); + *pd = dvalue; + } + else #endif - { - *((float*)pv) = (float)dvalue; + { + lvdbg("vsscanf: Return %f to %p\n", dvalue, pf); + *pf = (float)dvalue; + } } } #endif @@ -462,12 +574,12 @@ int vsscanf(FAR char *buf, FAR const char *fmt, va_list ap) if (lflag) { - long *plong = va_arg(ap, long*); + FAR long *plong = va_arg(ap, long*); *plong = (long)nchars; } else { - int *pint = va_arg(ap, int*); + FAR int *pint = va_arg(ap, int*); *pint = (int)nchars; } } @@ -489,13 +601,17 @@ int vsscanf(FAR char *buf, FAR const char *fmt, va_list ap) /* Its is not a conversion specifier */ - else + 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; |