Merged in david_s5/nuttx/upstream_to_greg_scanfwidth (pull request #149)

Fixes sscan %sn where strlen(data) < n
This commit is contained in:
Gregory Nutt
2016-10-15 16:10:08 +00:00
+59 -79
View File
@@ -93,8 +93,7 @@ static int findwidth(FAR const char *buf, FAR const char *fmt)
FAR const char *next = fmt + 1; FAR const char *next = fmt + 1;
/* No... is there a space after the format? Or does the format string end /* No... is there a space after the format? Or does the format string end
* here? * here? */
*/
if (isspace(*next) || *next == 0) if (isspace(*next) || *next == 0)
{ {
@@ -103,15 +102,13 @@ static int findwidth(FAR const char *buf, FAR const char *fmt)
return strcspn(buf, spaces); return strcspn(buf, spaces);
} }
/* No.. Another possibility is the format character is followed by /* No.. Another possibility is the format character is followed by some
* some recognizable delimiting value. * recognizable delimiting value. */
*/
if (*next != '%') if (*next != '%')
{ {
/* If so we will say that the string ends there if we can find that /* If so we will say that the string ends there if we can find that
* delimiter in the input string. * delimiter in the input string. */
*/
FAR const char *ptr = strchr(buf, *next); FAR const char *ptr = strchr(buf, *next);
if (ptr) if (ptr)
@@ -120,16 +117,14 @@ static int findwidth(FAR const char *buf, FAR const char *fmt)
} }
} }
/* No... the format has no delimiter and is back-to-back with the next /* No... the format has no delimiter and is back-to-back with the next format
* format (or is followed by a delimiter that does not exist in the * (or is followed by a delimiter that does not exist in the input string).
* input string). At this point we just bail and Use the input up until * At this point we just bail and Use the input up until the first white
* the first white space is encountered. * 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
* NOTE: This means that values from the following format may be * generic way of determining the width of the data if there is no
* concatenated with the first. This is a bug. We have no generic way of * fieldwidth, no space separating the input, and no usable delimiter
* determining the width of the data if there is no fieldwidth, no space * character. */
* separating the input, and no usable delimiter character.
*/
return strcspn(buf, spaces); return strcspn(buf, spaces);
} }
@@ -174,14 +169,14 @@ int vsscanf(FAR const char *buf, FAR const char *fmt, va_list ap)
bool noassign; bool noassign;
int count; int count;
int width; int width;
int fwidth;
int base = 10; int base = 10;
char tmp[MAXLN]; char tmp[MAXLN];
linfo("vsscanf: buf=\"%s\" fmt=\"%s\"\n", buf, fmt); linfo("vsscanf: buf=\"%s\" fmt=\"%s\"\n", buf, fmt);
/* Remember the start of the input buffer. We will need this for %n /* Remember the start of the input buffer. We will need this for %n
* calculations. * calculations. */
*/
bufstart = buf; bufstart = buf;
@@ -192,10 +187,9 @@ int vsscanf(FAR const char *buf, FAR const char *fmt, va_list ap)
noassign = false; noassign = false;
lflag = false; lflag = false;
/* Loop until all characters in the fmt string have been processed. We /* Loop until all characters in the fmt string have been processed. We may
* may have to continue loop after reaching the end the input data in * have to continue loop after reaching the end the input data in order to
* order to handle trailing %n format specifiers. * handle trailing %n format specifiers. */
*/
while (*fmt) while (*fmt)
{ {
@@ -250,10 +244,9 @@ int vsscanf(FAR const char *buf, FAR const char *fmt, va_list ap)
{ {
linfo("vsscanf: Performing string conversion\n"); linfo("vsscanf: Performing string conversion\n");
/* Get a pointer to the char * value. We need to do this even /* Get a pointer to the char * value. We need to do this even if
* if we have reached the end of the input data in order to * we have reached the end of the input data in order to update
* update the 'ap' variable. * the 'ap' variable. */
*/
tv = NULL; /* To avoid warnings about begin uninitialized */ tv = NULL; /* To avoid warnings about begin uninitialized */
if (!noassign) if (!noassign)
@@ -262,9 +255,8 @@ int vsscanf(FAR const char *buf, FAR const char *fmt, va_list ap)
tv[0] = '\0'; tv[0] = '\0';
} }
/* But we only perform the data conversion is we still have /* But we only perform the data conversion is we still have bytes
* bytes remaining in the input data stream. * remaining in the input data stream. */
*/
if (*buf) if (*buf)
{ {
@@ -275,16 +267,21 @@ int vsscanf(FAR const char *buf, FAR const char *fmt, va_list ap)
buf++; buf++;
} }
/* Was a fieldwidth specified? */ /* Guess a field width using some heuristics */
if (!width) 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)
{ {
/* No... Guess a field width using some heuristics */ width = fwidth;
int tmpwidth = findwidth(buf, fmt);
width = MIN(sizeof(tmp) - 1, tmpwidth);
} }
width = MIN(sizeof(tmp) - 1, width);
/* Copy the string (if we are making an assignment) */ /* Copy the string (if we are making an assignment) */
if (!noassign) if (!noassign)
@@ -306,10 +303,9 @@ int vsscanf(FAR const char *buf, FAR const char *fmt, va_list ap)
{ {
linfo("vsscanf: Performing character conversion\n"); linfo("vsscanf: Performing character conversion\n");
/* Get a pointer to the char * value. We need to do this even /* Get a pointer to the char * value. We need to do this even if
* if we have reached the end of the input data in order to * we have reached the end of the input data in order to update
* update the 'ap' variable. * the 'ap' variable. */
*/
tv = NULL; /* To avoid warnings about beign uninitialized */ tv = NULL; /* To avoid warnings about beign uninitialized */
if (!noassign) if (!noassign)
@@ -318,9 +314,8 @@ int vsscanf(FAR const char *buf, FAR const char *fmt, va_list ap)
tv[0] = '\0'; tv[0] = '\0';
} }
/* But we only perform the data conversion is we still have /* But we only perform the data conversion is we still have bytes
* bytes remaining in the input data stream. * remaining in the input data stream. */
*/
if (*buf) if (*buf)
{ {
@@ -343,8 +338,7 @@ int vsscanf(FAR const char *buf, FAR const char *fmt, va_list ap)
} }
/* Update the buffer pointer past the character(s) in the /* Update the buffer pointer past the character(s) in the
* input * input */
*/
buf += width; buf += width;
} }
@@ -362,14 +356,12 @@ int vsscanf(FAR const char *buf, FAR const char *fmt, va_list ap)
/* Get a pointer to the integer value. We need to do this even /* 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 * if we have reached the end of the input data in order to
* update the 'ap' variable. * update the 'ap' variable. */
*/
if (!noassign) if (!noassign)
{ {
/* We have to check whether we need to return a long or an /* We have to check whether we need to return a long or an
* int. * int. */
*/
if (lflag) if (lflag)
{ {
@@ -383,9 +375,8 @@ int vsscanf(FAR const char *buf, FAR const char *fmt, va_list ap)
} }
} }
/* But we only perform the data conversion if we still have /* But we only perform the data conversion if we still have bytes
* bytes remaining in the input data stream. * remaining in the input data stream. */
*/
if (*buf) if (*buf)
{ {
@@ -400,9 +391,8 @@ int vsscanf(FAR const char *buf, FAR const char *fmt, va_list ap)
buf++; buf++;
} }
/* The base of the integer conversion depends on the /* The base of the integer conversion depends on the specific
* specific conversion specification. * conversion specification. */
*/
sign = false; sign = false;
switch (*fmt) switch (*fmt)
@@ -438,9 +428,7 @@ int vsscanf(FAR const char *buf, FAR const char *fmt, va_list ap)
width = MIN(sizeof(tmp) - 1, tmpwidth); width = MIN(sizeof(tmp) - 1, tmpwidth);
} }
/* Copy the numeric string into a temporary working /* Copy the numeric string into a temporary working buffer. */
* buffer.
*/
strncpy(tmp, buf, width); strncpy(tmp, buf, width);
tmp[width] = '\0'; tmp[width] = '\0';
@@ -475,9 +463,8 @@ int vsscanf(FAR const char *buf, FAR const char *fmt, va_list ap)
if (!noassign) if (!noassign)
{ {
/* We have to check whether we need to return a long /* We have to check whether we need to return a long or
* or an int. * an int. */
*/
if (lflag) if (lflag)
{ {
@@ -487,8 +474,7 @@ int vsscanf(FAR const char *buf, FAR const char *fmt, va_list ap)
} }
else else
{ {
linfo("vsscanf: Return %ld to 0x%p\n", linfo("vsscanf: Return %ld to 0x%p\n", tmplong, pint);
tmplong, pint);
*pint = (int)tmplong; *pint = (int)tmplong;
} }
@@ -498,8 +484,7 @@ int vsscanf(FAR const char *buf, FAR const char *fmt, va_list ap)
} }
/* Process %a, %A, %f, %F, %e, %E, %g, and %G: Floating point /* Process %a, %A, %f, %F, %e, %E, %g, and %G: Floating point
* conversions * conversions */
*/
else if (strchr("aAfFeEgG", *fmt) != NULL) else if (strchr("aAfFeEgG", *fmt) != NULL)
{ {
@@ -510,16 +495,14 @@ int vsscanf(FAR const char *buf, FAR const char *fmt, va_list ap)
linfo("vsscanf: Performing floating point conversion\n"); linfo("vsscanf: Performing floating point conversion\n");
/* Get a pointer to the double value. We need to do this even /* Get a pointer to the double value. We need to do this even if
* if we have reached the end of the input data in order to * we have reached the end of the input data in order to update
* update the 'ap' variable. * the 'ap' variable. */
*/
if (!noassign) if (!noassign)
{ {
/* We have to check whether we need to return a float or a /* We have to check whether we need to return a float or a
* double. * double. */
*/
#ifdef CONFIG_HAVE_DOUBLE #ifdef CONFIG_HAVE_DOUBLE
if (lflag) if (lflag)
@@ -536,9 +519,8 @@ int vsscanf(FAR const char *buf, FAR const char *fmt, va_list ap)
} }
#ifdef CONFIG_LIBC_FLOATINGPOINT #ifdef CONFIG_LIBC_FLOATINGPOINT
/* But we only perform the data conversion is we still have /* But we only perform the data conversion is we still have bytes
* bytes remaining in the input data stream. * remaining in the input data stream. */
*/
if (*buf) if (*buf)
{ {
@@ -591,9 +573,8 @@ int vsscanf(FAR const char *buf, FAR const char *fmt, va_list ap)
set_errno(errsave); set_errno(errsave);
/* We have to check whether we need to return a float /* We have to check whether we need to return a float or
* or a double. * a double. */
*/
# ifdef CONFIG_HAVE_DOUBLE # ifdef CONFIG_HAVE_DOUBLE
if (lflag) if (lflag)
@@ -678,8 +659,7 @@ int vsscanf(FAR const char *buf, FAR const char *fmt, va_list ap)
} }
/* sscanf is required to return EOF if the input ends before the first /* sscanf is required to return EOF if the input ends before the first
* matching failure or conversion. * matching failure or conversion. */
*/
return count ? count : EOF; return count ? count : EOF;
} }