lib: cbprintf: fix mishandling of precision string output
If a precision flag is included for s formatting that bounds the maximum output length, so we need to use strnlen rather than strlen to get the amount of data to emit. With that flag we can't expect there to be a terminating NUL following the text to print. Also fix handling of an empty precision, which should behave as if a precision of zero was provided. Signed-off-by: Peter Bigot <peter.bigot@nordicsemi.no>
This commit is contained in:
parent
5be8afcf73
commit
53762239c1
2 changed files with 21 additions and 17 deletions
|
@ -19,6 +19,11 @@
|
||||||
#include <sys/util.h>
|
#include <sys/util.h>
|
||||||
#include <sys/cbprintf.h>
|
#include <sys/cbprintf.h>
|
||||||
|
|
||||||
|
/* newlib doesn't declare this function unless __POSIX_VISIBLE >= 200809. No
|
||||||
|
* idea how to make that happen, so lets put it right here.
|
||||||
|
*/
|
||||||
|
size_t strnlen(const char *, size_t);
|
||||||
|
|
||||||
/* Provide typedefs used for signed and unsigned integral types
|
/* Provide typedefs used for signed and unsigned integral types
|
||||||
* capable of holding all convertable integral values.
|
* capable of holding all convertable integral values.
|
||||||
*/
|
*/
|
||||||
|
@ -342,8 +347,9 @@ static inline const char *extract_flags(struct conversion *conv,
|
||||||
static inline const char *extract_width(struct conversion *conv,
|
static inline const char *extract_width(struct conversion *conv,
|
||||||
const char *sp)
|
const char *sp)
|
||||||
{
|
{
|
||||||
|
conv->width_present = true;
|
||||||
|
|
||||||
if (*sp == '*') {
|
if (*sp == '*') {
|
||||||
conv->width_present = true;
|
|
||||||
conv->width_star = true;
|
conv->width_star = true;
|
||||||
return ++sp;
|
return ++sp;
|
||||||
}
|
}
|
||||||
|
@ -375,27 +381,24 @@ static inline const char *extract_width(struct conversion *conv,
|
||||||
static inline const char *extract_prec(struct conversion *conv,
|
static inline const char *extract_prec(struct conversion *conv,
|
||||||
const char *sp)
|
const char *sp)
|
||||||
{
|
{
|
||||||
if (*sp != '.') {
|
conv->prec_present = (*sp == '.');
|
||||||
|
|
||||||
|
if (!conv->prec_present) {
|
||||||
return sp;
|
return sp;
|
||||||
}
|
}
|
||||||
++sp;
|
++sp;
|
||||||
|
|
||||||
if (*sp == '*') {
|
if (*sp == '*') {
|
||||||
conv->prec_present = true;
|
|
||||||
conv->prec_star = true;
|
conv->prec_star = true;
|
||||||
return ++sp;
|
return ++sp;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *wp = sp;
|
|
||||||
size_t prec = extract_decimal(&sp);
|
size_t prec = extract_decimal(&sp);
|
||||||
|
|
||||||
if (sp != wp) {
|
conv->prec_value = prec;
|
||||||
conv->prec_present = true;
|
if (prec != conv->prec_value) {
|
||||||
conv->prec_value = prec;
|
/* Lost precision data */
|
||||||
if (prec != conv->prec_value) {
|
conv->unsupported = true;
|
||||||
/* Lost precision data */
|
|
||||||
conv->unsupported = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return sp;
|
return sp;
|
||||||
|
@ -1579,11 +1582,12 @@ int cbvprintf(cbprintf_cb out, void *ctx, const char *fp, va_list ap)
|
||||||
case 's': {
|
case 's': {
|
||||||
bps = (const char *)value->ptr;
|
bps = (const char *)value->ptr;
|
||||||
|
|
||||||
size_t len = strlen(bps);
|
size_t len;
|
||||||
|
|
||||||
if ((precision >= 0)
|
if (precision >= 0) {
|
||||||
&& ((size_t)precision < len)) {
|
len = strnlen(bps, precision);
|
||||||
len = (size_t)precision;
|
} else {
|
||||||
|
len = strlen(bps);
|
||||||
}
|
}
|
||||||
|
|
||||||
bpe = bps + len;
|
bpe = bps + len;
|
||||||
|
|
|
@ -341,8 +341,8 @@ static void test_s(void)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = TEST_PRF("/%.6s/%.2s/", s, s);
|
rc = TEST_PRF("/%.6s/%.2s/%.s/", s, s, s);
|
||||||
PRF_CHECK("/123/12/", rc);
|
PRF_CHECK("/123/12//", rc);
|
||||||
|
|
||||||
rc = TEST_PRF("%ls", ws);
|
rc = TEST_PRF("%ls", ws);
|
||||||
if (IS_ENABLED(USE_LIBC)) {
|
if (IS_ENABLED(USE_LIBC)) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue