/* Test __GCONV_ENCOUNTERED_ILLEGAL_INPUT, as used by iconv -c (bug 32046). Copyright (C) 2024 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, see . */ #include #include #include #include #include #include #include #include #include /* FROM is the input character set, TO the output character set. If IGNORE is true, the iconv descriptor is set up in the same way as iconv -c would. INPUT is the input string, EXPECTED_OUTPUT the output. OUTPUT_LIMIT is a byte count, specifying how many input bytes are passed to the iconv function on each invocation. */ static void one_direction (const char *from, const char *to, bool ignore, const char *input, const char *expected_output, size_t output_limit) { if (test_verbose) { char *quoted_input = support_quote_string (input); char *quoted_output = support_quote_string (expected_output); printf ("info: testing from=\"%s\" to=\"%s\" ignore=%d input=\"%s\"" " expected_output=\"%s\" output_limit=%zu\n", from, to, (int) ignore, quoted_input, quoted_output, output_limit); free (quoted_output); free (quoted_input); } __gconv_t cd; if (ignore) { struct gconv_spec conv_spec; TEST_VERIFY_EXIT (__gconv_create_spec (&conv_spec, from, to) == &conv_spec); conv_spec.ignore = true; cd = (iconv_t) -1; TEST_COMPARE (__gconv_open (&conv_spec, &cd, 0), __GCONV_OK); __gconv_destroy_spec (&conv_spec); } else cd = iconv_open (to, from); TEST_VERIFY_EXIT (cd != (iconv_t) -1); char *input_ptr = (char *) input; size_t input_len = strlen (input); char output_buf[20]; char *output_ptr = output_buf; size_t output_len; do { output_len = array_end (output_buf) - output_ptr; if (output_len > output_limit) /* Limit the buffer size as requested by the caller. */ output_len = output_limit; TEST_VERIFY_EXIT (output_len > 0); if (input_len == 0) /* Trigger final flush. */ input_ptr = NULL; char *old_input_ptr = input_ptr; size_t ret = iconv (cd, &input_ptr, &input_len, &output_ptr, &output_len); if (ret == (size_t) -1) { if (errno != EILSEQ) TEST_COMPARE (errno, E2BIG); } if (input_ptr == old_input_ptr) /* Avoid endless loop if stuck on an invalid input character. */ break; } while (input_ptr != NULL); /* Test the sticky illegal input bit. */ TEST_VERIFY (__gconv_has_illegal_input (cd)); TEST_COMPARE_BLOB (expected_output, strlen (expected_output), output_buf, output_ptr - output_buf); TEST_COMPARE (iconv_close (cd), 0); } static int do_test (void) { static const char charsets[][14] = { "ASCII", "ASCII//IGNORE", "UTF-8", "UTF-8//IGNORE", }; for (size_t from_idx = 0; from_idx < array_length (charsets); ++from_idx) for (size_t to_idx = 0; to_idx < array_length (charsets); ++to_idx) for (int do_ignore = 0; do_ignore < 2; ++do_ignore) for (int limit = 1; limit < 5; ++limit) for (int skip = 0; skip < 3; ++skip) { const char *expected_output; if (do_ignore || strstr (charsets[to_idx], "//IGNORE") != NULL) expected_output = "ABXY" + skip; else expected_output = "AB" + skip; one_direction (charsets[from_idx], charsets[to_idx], do_ignore, "AB\xffXY" + skip, expected_output, limit); } return 0; } #include