summaryrefslogtreecommitdiff
path: root/nuttx
diff options
context:
space:
mode:
authorpatacongo <patacongo@42af7a65-404d-4744-a932-0658087f49c3>2013-03-18 00:41:42 +0000
committerpatacongo <patacongo@42af7a65-404d-4744-a932-0658087f49c3>2013-03-18 00:41:42 +0000
commite043d545247953df1980d119fa1a477cbd9f9b18 (patch)
tree259d5c1dc75b5a806095ebd8b7a11a60d8b22331 /nuttx
parent7330994698ed840623a487f1a394e39580c8ce97 (diff)
downloadpx4-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
Diffstat (limited to 'nuttx')
-rw-r--r--nuttx/ChangeLog4
-rw-r--r--nuttx/libc/stdio/lib_sscanf.c360
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;