+2025-01-03 Bruno Haible <bruno@clisp.org>
+
+ mbs_endswith: New module.
+ * lib/string.in.h (mbs_endswith): New declaration.
+ * lib/mbs_endswith.c: New file.
+ * m4/string_h.m4 (gl_STRING_H_REQUIRE_DEFAULTS): Initialize
+ GNULIB_MBS_ENDSWITH.
+ * modules/string-h (Makefile.am): Substitute GNULIB_MBS_ENDSWITH.
+ * modules/mbs_endswith: New file.
+
2025-01-03 Bruno Haible <bruno@clisp.org>
mbs_startswith: New module.
--- /dev/null
+/* mbs_endswith function.
+ Copyright (C) 2025 Free Software Foundation, Inc.
+
+ This file 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 3 of the
+ License, or (at your option) any later version.
+
+ This file 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 this program. If not, see <https://www.gnu.org/licenses/>. */
+
+/* Written by Bruno Haible <bruno@clisp.org>, 2025. */
+
+#include "config.h"
+
+/* Specification. */
+#include <string.h>
+
+#include "mbiter.h"
+
+#include <stdlib.h>
+
+int
+mbs_endswith (const char *string, const char *suffix)
+{
+ if ((unsigned char) suffix[0] < 0x30)
+ /* Some knowledge about the possible multibyte encodings:
+
+ Encoding First byte of character Which of these can occur
+ as second or later byte?
+
+ EUC-JP 0x00..0x7F, 0x8E..0xFE 0xA1..0xFE
+ EUC-KR 0x00..0x7F, 0xA1..0xFD 0xA1..0xFD
+ GB2312 0x00..0x7F, 0xA1..0xF7 0xA1..0xF7
+ EUC-TW 0x00..0x7F, 0x8E..0xFD 0xA1..0xFD
+ BIG5 0x00..0x7F, 0xA1..0xF9 0x40..0x7E, 0xA1..0xF9
+ GB18030 0x00..0x7F, 0x81..0xFE 0x30..0x39, 0x40..0x7E, 0x81..0xFE
+ SJIS 0x00..0x7F, 0x81..0xF9 0x40..0x7E, 0x81..0xF9
+ UTF-8 0x00..0x7F, 0xC2..0xF7 none
+
+ Therefore, if the first byte of SUFFIX is < 0x30, it cannot occur as
+ second or later byte, and therefore it is OK to do a bytewise search. */
+ return str_endswith (string, suffix);
+
+ /* Here, suffix is not empty. */
+
+ size_t nbytes = strlen (string);
+ if (nbytes >= strlen (suffix))
+ {
+ size_t len = mbslen (string);
+ size_t n = mbslen (suffix);
+ if (len >= n)
+ {
+ mbi_iterator_t iter;
+ mbi_init (iter, string, nbytes);
+ /* Advance past (len - n) multibyte characters. */
+ for (; len > n; len--)
+ {
+ if (!mbi_avail (iter))
+ abort ();
+ mbi_advance (iter);
+ }
+ if (!mbi_avail (iter))
+ abort ();
+ return strcmp (mbi_cur_ptr (iter), suffix) == 0;
+ }
+ }
+ return 0;
+}
# define mbs_startswith str_startswith
#endif
+#if @GNULIB_MBS_ENDSWITH@
+/* Returns true if STRING ends with SUFFIX.
+ Returns false otherwise.
+ Unlike str_endswith(), this function works correctly in multibyte locales.
+ */
+_GL_EXTERN_C int mbs_endswith (const char *string, const char *suffix)
+ _GL_ATTRIBUTE_PURE
+ _GL_ARG_NONNULL ((1, 2));
+#endif
+
/* Map any int, typically from errno, into an error message. */
#if @GNULIB_STRERROR@
# if @REPLACE_STRERROR@
# string_h.m4
-# serial 42
+# serial 43
dnl Copyright (C) 2007-2025 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_MBSSEP])
gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_MBSTOK_R])
gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_MBS_STARTSWITH])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_MBS_ENDSWITH])
gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_STRERROR])
gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_STRERROR_R])
gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_STRERRORNAME_NP])