This is an automated email from the ASF dual-hosted git repository. xiaoxiang781216 pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit 284d4d4ea4d427ff533735f94418023c003d7f1e Author: Marco Casaroli <[email protected]> AuthorDate: Fri Oct 9 10:49:07 2026 +0200 libs/libc/stream: Support %lc and %ls in printf() with CONFIG_LIBC_LOCALE. C99 specifies %lc for a wide character (wint_t) and %ls for a wide string (wchar_t *), written as their multibyte sequences. printf() ignored the l and handled them as %c and %s: %lc wrote only the low byte of the character, and %ls read the wide string as bytes and stopped at the first zero byte, which for ASCII text is after the first character. With CONFIG_LIBC_LOCALE, convert them with wcrtomb(). For %ls the precision limits the number of bytes, and a character that does not fit is not written in part. A character that cannot be converted makes printf() fail with EILSEQ, as C99 specifies. printf() is in almost every image, so the conversion is built only with the existing locale support; without it %lc and %ls behave as before. Assisted-by: Claude Code:claude-opus-5-5 Signed-off-by: Marco Casaroli <[email protected]> --- libs/libc/stream/lib_libvsprintf.c | 87 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 84 insertions(+), 3 deletions(-) diff --git a/libs/libc/stream/lib_libvsprintf.c b/libs/libc/stream/lib_libvsprintf.c index ed4e6c89916..72af74e4311 100644 --- a/libs/libc/stream/lib_libvsprintf.c +++ b/libs/libc/stream/lib_libvsprintf.c @@ -47,7 +47,9 @@ #endif #include <assert.h> +#include <limits.h> #include <string.h> +#include <wchar.h> #include <sys/param.h> #include "lib_dtoa_engine.h" @@ -166,6 +168,7 @@ static int vsprintf_internal(FAR struct lib_outstream_s *stream, FAR const char *pnt; size_t size; + int ch; unsigned char len; int total_len = 0; @@ -874,16 +877,39 @@ flt_oper: #ifdef CONFIG_LIBC_NUMBERED_ARGS if ((flags & FL_ARGNUMBER) != 0) { - buf[0] = (int)arglist->value[argnumber - 1].u; + ch = (int)arglist->value[argnumber - 1].u; } else #endif { - buf[0] = va_arg(ap, int); + ch = va_arg(ap, int); + } + +#ifdef CONFIG_LIBC_LOCALE + if ((flags & FL_LONG) != 0) + { + mbstate_t ps; + + /* %lc: a wide character, written as its multibyte + * sequence. + */ + + memset(&ps, 0, sizeof(ps)); + size = wcrtomb(buf, (wchar_t)ch, &ps); + if (size == (size_t)-1) + { + total_len = -1; + goto ret; + } + } + else +#endif + { + buf[0] = ch; + size = 1; } pnt = buf; - size = 1; goto str_lpad; case 's': @@ -903,6 +929,61 @@ flt_oper: { pnt = g_nullstring; } +#ifdef CONFIG_LIBC_LOCALE + else if (c == 's' && (flags & FL_LONG) != 0) + { + FAR const wchar_t *ws = (FAR const wchar_t *)pnt; + char mb[MB_LEN_MAX]; + mbstate_t ps; + size_t n; + int i; + + /* %ls: a wide string, written as multibyte characters. The + * precision limits the bytes, and a character that does not + * fit is not written in part. First count the bytes. + */ + + memset(&ps, 0, sizeof(ps)); + for (size = 0, i = 0; ws[i] != L'\0'; i++) + { + n = wcrtomb(mb, ws[i], &ps); + if (n == (size_t)-1) + { + total_len = -1; + goto ret; + } + + if ((flags & FL_PREC) != 0 && size + n > (size_t)prec) + { + break; + } + + size += n; + } + + if ((flags & FL_LPAD) == 0) + { + while (size < width) + { + stream_putc(' ', stream); + width--; + } + } + + memset(&ps, 0, sizeof(ps)); + for (n = 0, i = 0; n < size; i++) + { + size_t m = wcrtomb(mb, ws[i], &ps); + + stream_puts(mb, m, stream); + n += m; + } + + width = width >= size ? width - size : 0; + size = 0; + goto tail; + } +#endif size = strnlen(pnt, (flags & FL_PREC) ? prec : ~0);
