vasnprintf.c can be compiled with -DENABLE_WCHAR_FALLBACK. Currently this option is not used by any package. But if some package was to use it, it would see the same bug as just fixed in <https://lists.gnu.org/archive/html/bug-gnulib/2023-01/msg00231.html>.
So let me fix that as well, and add corresponding unit tests. 2023-01-28 Bruno Haible <br...@clisp.org> vasnprintf-posix: Fix possible bug with negative width handling for %lc. * lib/vasnprintf.c (VASNPRINTF): In the code for %lc in vasnprintf, test for the FLAG_LEFT bit in the flags variable. * tests/test-vasnprintf-posix.c (test_function): Add tests for width given as argument for the directives %c, %lc. * tests/test-vasprintf-posix.c (test_function): Likewise. * tests/test-snprintf-posix.h (test_function): Likewise. * tests/test-sprintf-posix.h (test_function): Likewise. * tests/test-snprintf-posix.c: Include <wchar.h>, for wint_t. * tests/test-sprintf-posix.c: Likewise. diff --git a/lib/vasnprintf.c b/lib/vasnprintf.c index 5ab8edbab7..0e6220ae18 100644 --- a/lib/vasnprintf.c +++ b/lib/vasnprintf.c @@ -2977,7 +2977,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, /* w doesn't matter. */ w = 0; - if (w < width && !(dp->flags & FLAG_LEFT)) + if (w < width && !(flags & FLAG_LEFT)) { size_t n = width - w; ENSURE_ALLOCATION (xsum (length, n)); @@ -3033,7 +3033,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, length += tmpdst_len; # endif - if (w < width && (dp->flags & FLAG_LEFT)) + if (w < width && (flags & FLAG_LEFT)) { size_t n = width - w; ENSURE_ALLOCATION (xsum (length, n)); diff --git a/tests/test-snprintf-posix.c b/tests/test-snprintf-posix.c index d8d89fcd05..3dec989d7b 100644 --- a/tests/test-snprintf-posix.c +++ b/tests/test-snprintf-posix.c @@ -25,6 +25,7 @@ #include <stdint.h> #include <stdlib.h> #include <string.h> +#include <wchar.h> #include "macros.h" diff --git a/tests/test-snprintf-posix.h b/tests/test-snprintf-posix.h index e5c1d3f89d..4db76deb75 100644 --- a/tests/test-snprintf-posix.h +++ b/tests/test-snprintf-posix.h @@ -2987,4 +2987,70 @@ test_function (int (*my_snprintf) (char *, size_t, const char *, ...)) } } #endif + + /* Test the support of the %c format directive. */ + + { /* Width. */ + int retval = + my_snprintf (result, sizeof (result), + "%10c %d", (unsigned char) 'x', 33, 44, 55); + ASSERT (strcmp (result, " x 33") == 0); + ASSERT (retval == strlen (result)); + } + + { /* Width given as argument. */ + int retval = + my_snprintf (result, sizeof (result), + "%*c %d", 10, (unsigned char) 'x', 33, 44, 55); + ASSERT (strcmp (result, " x 33") == 0); + ASSERT (retval == strlen (result)); + } + + { /* Negative width given as argument (cf. FLAG_LEFT below). */ + int retval = + my_snprintf (result, sizeof (result), + "%*c %d", -10, (unsigned char) 'x', 33, 44, 55); + ASSERT (strcmp (result, "x 33") == 0); + ASSERT (retval == strlen (result)); + } + + { /* FLAG_LEFT. */ + int retval = + my_snprintf (result, sizeof (result), + "%-10c %d", (unsigned char) 'x', 33, 44, 55); + ASSERT (strcmp (result, "x 33") == 0); + ASSERT (retval == strlen (result)); + } + +#if HAVE_WCHAR_T + static wint_t L_x = (wchar_t) 'x'; + + { /* Width. */ + int retval = + my_snprintf (result, sizeof (result), "%10lc %d", L_x, 33, 44, 55); + ASSERT (strcmp (result, " x 33") == 0); + ASSERT (retval == strlen (result)); + } + + { /* Width given as argument. */ + int retval = + my_snprintf (result, sizeof (result), "%*lc %d", 10, L_x, 33, 44, 55); + ASSERT (strcmp (result, " x 33") == 0); + ASSERT (retval == strlen (result)); + } + + { /* Negative width given as argument (cf. FLAG_LEFT below). */ + int retval = + my_snprintf (result, sizeof (result), "%*lc %d", -10, L_x, 33, 44, 55); + ASSERT (strcmp (result, "x 33") == 0); + ASSERT (retval == strlen (result)); + } + + { /* FLAG_LEFT. */ + int retval = + my_snprintf (result, sizeof (result), "%-10lc %d", L_x, 33, 44, 55); + ASSERT (strcmp (result, "x 33") == 0); + ASSERT (retval == strlen (result)); + } +#endif } diff --git a/tests/test-sprintf-posix.c b/tests/test-sprintf-posix.c index 9404a6fc99..54c951c72c 100644 --- a/tests/test-sprintf-posix.c +++ b/tests/test-sprintf-posix.c @@ -28,6 +28,7 @@ SIGNATURE_CHECK (sprintf, int, (char *, char const *, ...)); #include <stdint.h> #include <stdlib.h> #include <string.h> +#include <wchar.h> #include "macros.h" diff --git a/tests/test-sprintf-posix.h b/tests/test-sprintf-posix.h index 011df3b46a..979254acc7 100644 --- a/tests/test-sprintf-posix.h +++ b/tests/test-sprintf-posix.h @@ -2973,4 +2973,66 @@ test_function (int (*my_sprintf) (char *, const char *, ...)) } } #endif + + /* Test the support of the %c format directive. */ + + { /* Width. */ + int retval = + my_sprintf (result, "%10c %d", (unsigned char) 'x', 33, 44, 55); + ASSERT (strcmp (result, " x 33") == 0); + ASSERT (retval == strlen (result)); + } + + { /* Width given as argument. */ + int retval = + my_sprintf (result, "%*c %d", 10, (unsigned char) 'x', 33, 44, 55); + ASSERT (strcmp (result, " x 33") == 0); + ASSERT (retval == strlen (result)); + } + + { /* Negative width given as argument (cf. FLAG_LEFT below). */ + int retval = + my_sprintf (result, "%*c %d", -10, (unsigned char) 'x', 33, 44, 55); + ASSERT (strcmp (result, "x 33") == 0); + ASSERT (retval == strlen (result)); + } + + { /* FLAG_LEFT. */ + int retval = + my_sprintf (result, "%-10c %d", (unsigned char) 'x', 33, 44, 55); + ASSERT (strcmp (result, "x 33") == 0); + ASSERT (retval == strlen (result)); + } + +#if HAVE_WCHAR_T + static wint_t L_x = (wchar_t) 'x'; + + { /* Width. */ + int retval = + my_sprintf (result, "%10lc %d", L_x, 33, 44, 55); + ASSERT (strcmp (result, " x 33") == 0); + ASSERT (retval == strlen (result)); + } + + { /* Width given as argument. */ + int retval = + my_sprintf (result, "%*lc %d", 10, L_x, 33, 44, 55); + ASSERT (strcmp (result, " x 33") == 0); + ASSERT (retval == strlen (result)); + } + + { /* Negative width given as argument (cf. FLAG_LEFT below). */ + int retval = + my_sprintf (result, "%*lc %d", -10, L_x, 33, 44, 55); + ASSERT (strcmp (result, "x 33") == 0); + ASSERT (retval == strlen (result)); + } + + { /* FLAG_LEFT. */ + int retval = + my_sprintf (result, "%-10lc %d", L_x, 33, 44, 55); + ASSERT (strcmp (result, "x 33") == 0); + ASSERT (retval == strlen (result)); + } +#endif } diff --git a/tests/test-vasnprintf-posix.c b/tests/test-vasnprintf-posix.c index 93d2bc5a38..4a83f6d243 100644 --- a/tests/test-vasnprintf-posix.c +++ b/tests/test-vasnprintf-posix.c @@ -27,6 +27,7 @@ #include <stdint.h> #include <stdlib.h> #include <string.h> +#include <wchar.h> #include "macros.h" #include "minus-zero.h" @@ -3883,6 +3884,96 @@ test_function (char * (*my_asnprintf) (char *, size_t *, const char *, ...)) } #endif + /* Test the support of the %c format directive. */ + + { /* Width. */ + size_t length; + char *result = + my_asnprintf (NULL, &length, + "%10c %d", (unsigned char) 'x', 33, 44, 55); + ASSERT (result != NULL); + ASSERT (strcmp (result, " x 33") == 0); + ASSERT (length == strlen (result)); + free (result); + } + + { /* Width given as argument. */ + size_t length; + char *result = + my_asnprintf (NULL, &length, + "%*c %d", 10, (unsigned char) 'x', 33, 44, 55); + ASSERT (result != NULL); + ASSERT (strcmp (result, " x 33") == 0); + ASSERT (length == strlen (result)); + free (result); + } + + { /* Negative width given as argument (cf. FLAG_LEFT below). */ + size_t length; + char *result = + my_asnprintf (NULL, &length, + "%*c %d", -10, (unsigned char) 'x', 33, 44, 55); + ASSERT (result != NULL); + ASSERT (strcmp (result, "x 33") == 0); + ASSERT (length == strlen (result)); + free (result); + } + + { /* FLAG_LEFT. */ + size_t length; + char *result = + my_asnprintf (NULL, &length, + "%-10c %d", (unsigned char) 'x', 33, 44, 55); + ASSERT (result != NULL); + ASSERT (strcmp (result, "x 33") == 0); + ASSERT (length == strlen (result)); + free (result); + } + +#if HAVE_WCHAR_T + static wint_t L_x = (wchar_t) 'x'; + + { /* Width. */ + size_t length; + char *result = + my_asnprintf (NULL, &length, "%10lc %d", L_x, 33, 44, 55); + ASSERT (result != NULL); + ASSERT (strcmp (result, " x 33") == 0); + ASSERT (length == strlen (result)); + free (result); + } + + { /* Width given as argument. */ + size_t length; + char *result = + my_asnprintf (NULL, &length, "%*lc %d", 10, L_x, 33, 44, 55); + ASSERT (result != NULL); + ASSERT (strcmp (result, " x 33") == 0); + ASSERT (length == strlen (result)); + free (result); + } + + { /* Negative width given as argument (cf. FLAG_LEFT below). */ + size_t length; + char *result = + my_asnprintf (NULL, &length, "%*lc %d", -10, L_x, 33, 44, 55); + ASSERT (result != NULL); + ASSERT (strcmp (result, "x 33") == 0); + ASSERT (length == strlen (result)); + free (result); + } + + { /* FLAG_LEFT. */ + size_t length; + char *result = + my_asnprintf (NULL, &length, "%-10lc %d", L_x, 33, 44, 55); + ASSERT (result != NULL); + ASSERT (strcmp (result, "x 33") == 0); + ASSERT (length == strlen (result)); + free (result); + } +#endif + #if (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 2)) && !defined __UCLIBC__ /* Test that the 'I' flag is supported. */ { diff --git a/tests/test-vasprintf-posix.c b/tests/test-vasprintf-posix.c index 3bfc18bb75..8563502c47 100644 --- a/tests/test-vasprintf-posix.c +++ b/tests/test-vasprintf-posix.c @@ -26,6 +26,7 @@ #include <stdint.h> #include <stdlib.h> #include <string.h> +#include <wchar.h> #include "macros.h" #include "minus-zero.h" @@ -3827,6 +3828,92 @@ test_function (int (*my_asprintf) (char **, const char *, ...)) } } #endif + + /* Test the support of the %c format directive. */ + + { /* Width. */ + char *result; + int retval = + my_asprintf (&result, "%10c %d", (unsigned char) 'x', 33, 44, 55); + ASSERT (result != NULL); + ASSERT (strcmp (result, " x 33") == 0); + ASSERT (retval == strlen (result)); + free (result); + } + + { /* Width given as argument. */ + char *result; + int retval = + my_asprintf (&result, "%*c %d", 10, (unsigned char) 'x', 33, 44, 55); + ASSERT (result != NULL); + ASSERT (strcmp (result, " x 33") == 0); + ASSERT (retval == strlen (result)); + free (result); + } + + { /* Negative width given as argument (cf. FLAG_LEFT below). */ + char *result; + int retval = + my_asprintf (&result, "%*c %d", -10, (unsigned char) 'x', 33, 44, 55); + ASSERT (result != NULL); + ASSERT (strcmp (result, "x 33") == 0); + ASSERT (retval == strlen (result)); + free (result); + } + + { /* FLAG_LEFT. */ + char *result; + int retval = + my_asprintf (&result, "%-10c %d", (unsigned char) 'x', 33, 44, 55); + ASSERT (result != NULL); + ASSERT (strcmp (result, "x 33") == 0); + ASSERT (retval == strlen (result)); + free (result); + } + +#if HAVE_WCHAR_T + static wint_t L_x = (wchar_t) 'x'; + + { /* Width. */ + char *result; + int retval = + my_asprintf (&result, "%10lc %d", L_x, 33, 44, 55); + ASSERT (result != NULL); + ASSERT (strcmp (result, " x 33") == 0); + ASSERT (retval == strlen (result)); + free (result); + } + + { /* Width given as argument. */ + char *result; + int retval = + my_asprintf (&result, "%*lc %d", 10, L_x, 33, 44, 55); + ASSERT (result != NULL); + ASSERT (strcmp (result, " x 33") == 0); + ASSERT (retval == strlen (result)); + free (result); + } + + { /* Negative width given as argument (cf. FLAG_LEFT below). */ + char *result; + int retval = + my_asprintf (&result, "%*lc %d", -10, L_x, 33, 44, 55); + ASSERT (result != NULL); + ASSERT (strcmp (result, "x 33") == 0); + ASSERT (retval == strlen (result)); + free (result); + } + + { /* FLAG_LEFT. */ + char *result; + int retval = + my_asprintf (&result, "%-10lc %d", L_x, 33, 44, 55); + ASSERT (result != NULL); + ASSERT (strcmp (result, "x 33") == 0); + ASSERT (retval == strlen (result)); + free (result); + } +#endif } static int