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
+94 -114
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);
} }
@@ -149,7 +144,7 @@ static int findwidth(FAR const char *buf, FAR const char *fmt)
int sscanf(FAR const char *buf, FAR const char *fmt, ...) int sscanf(FAR const char *buf, FAR const char *fmt, ...)
{ {
va_list ap; va_list ap;
int count; int count;
va_start(ap, fmt); va_start(ap, fmt);
count = vsscanf((FAR const char *)buf, fmt, ap); count = vsscanf((FAR const char *)buf, fmt, ap);
@@ -168,34 +163,33 @@ int sscanf(FAR const char *buf, FAR const char *fmt, ...)
int vsscanf(FAR const char *buf, FAR const char *fmt, va_list ap) int vsscanf(FAR const char *buf, FAR const char *fmt, va_list ap)
{ {
FAR const char *bufstart; FAR const char *bufstart;
FAR char *tv; FAR char *tv;
FAR const char *tc; FAR const char *tc;
bool lflag; bool lflag;
bool noassign; bool noassign;
int count; int count;
int width; int width;
int base = 10; int fwidth;
char tmp[MAXLN]; int base = 10;
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;
/* Parse the format, extracting values from the input buffer as needed */ /* Parse the format, extracting values from the input buffer as needed */
count = 0; count = 0;
width = 0; width = 0;
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)
{ {
@@ -244,27 +238,25 @@ int vsscanf(FAR const char *buf, FAR const char *fmt, va_list ap)
} }
} }
/* Process %s: String conversion */ /* Process %s: String conversion */
if (*fmt == 's') if (*fmt == 's')
{ {
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)
{ {
tv = va_arg(ap, FAR char *); tv = va_arg(ap, FAR char *);
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)
@@ -300,27 +297,25 @@ int vsscanf(FAR const char *buf, FAR const char *fmt, va_list ap)
} }
} }
/* Process %c: Character conversion */ /* Process %c: Character conversion */
else if (*fmt == 'c') else if (*fmt == 'c')
{ {
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)
{ {
tv = va_arg(ap, FAR char *); tv = va_arg(ap, FAR char *);
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,33 +338,30 @@ 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;
} }
} }
/* Process %d, %o, %b, %x, %u: Various integer conversions */ /* Process %d, %o, %b, %x, %u: Various integer conversions */
else if (strchr("dobxu", *fmt)) else if (strchr("dobxu", *fmt))
{ {
FAR long *plong = NULL; FAR long *plong = NULL;
FAR int *pint = NULL; FAR int *pint = NULL;
bool sign; bool sign;
linfo("vsscanf: Performing integer conversion\n"); linfo("vsscanf: Performing integer conversion\n");
/* 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,15 +375,14 @@ 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)
{ {
FAR char *endptr; FAR char *endptr;
int errsave; int errsave;
long tmplong; long tmplong;
/* Skip over any white space before the integer string */ /* Skip over any white space before the integer string */
@@ -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;
} }
@@ -497,48 +483,44 @@ 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)
{ {
#ifdef CONFIG_HAVE_DOUBLE #ifdef CONFIG_HAVE_DOUBLE
FAR double_t *pd = NULL; FAR double_t *pd = NULL;
#endif #endif
FAR float *pf = NULL; FAR float *pf = NULL;
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)
{ {
pd = va_arg(ap, FAR double_t *); pd = va_arg(ap, FAR double_t *);
*pd = 0.0; *pd = 0.0;
} }
else else
#endif #endif
{ {
pf = va_arg(ap, FAR float *); pf = va_arg(ap, FAR float *);
*pf = 0.0; *pf = 0.0;
} }
} }
#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)
{ {
@@ -573,14 +555,14 @@ int vsscanf(FAR const char *buf, FAR const char *fmt, va_list ap)
/* strtod always returns a double */ /* strtod always returns a double */
FAR char *endptr; FAR char *endptr;
int errsave; int errsave;
double_t dvalue; double_t dvalue;
/* Preserve the errno value */ /* Preserve the errno value */
errsave = get_errno(); errsave = get_errno();
set_errno(0); set_errno(0);
dvalue = strtod(tmp, &endptr); dvalue = strtod(tmp, &endptr);
/* Check if the number was successfully converted */ /* Check if the number was successfully converted */
@@ -591,18 +573,17 @@ 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)
{ {
linfo("vsscanf: Return %f to %p\n", dvalue, pd); linfo("vsscanf: Return %f to %p\n", dvalue, pd);
*pd = dvalue; *pd = dvalue;
} }
else else
#endif # endif
{ {
linfo("vsscanf: Return %f to %p\n", dvalue, pf); linfo("vsscanf: Return %f to %p\n", dvalue, pf);
*pf = (float)dvalue; *pf = (float)dvalue;
@@ -614,7 +595,7 @@ int vsscanf(FAR const char *buf, FAR const char *fmt, va_list ap)
#endif #endif
} }
/* Process %n: Character count */ /* Process %n: Character count */
else if (*fmt == 'n') else if (*fmt == 'n')
{ {
@@ -622,7 +603,7 @@ int vsscanf(FAR const char *buf, FAR const char *fmt, va_list ap)
if (!noassign) if (!noassign)
{ {
size_t nchars = (size_t)(buf - bufstart); size_t nchars = (size_t) (buf - bufstart);
/* Note %n does not count as a conversion */ /* Note %n does not count as a conversion */
@@ -639,14 +620,14 @@ int vsscanf(FAR const char *buf, FAR const char *fmt, va_list ap)
} }
} }
width = 0; width = 0;
noassign = false; noassign = false;
lflag = false; lflag = false;
fmt++; fmt++;
} }
/* It is not a conversion specifier */ /* It is not a conversion specifier */
else if (*buf) else if (*buf)
{ {
@@ -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;
} }