From f3436baf8ec19ee8f81441890d20bd5a096fb885 Mon Sep 17 00:00:00 2001 From: Bruno Haible Date: Fri, 3 Jan 2025 13:32:32 +0100 Subject: [PATCH] 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. --- ChangeLog | 10 ++++++ lib/mbs_endswith.c | 74 ++++++++++++++++++++++++++++++++++++++++++++ lib/string.in.h | 10 ++++++ m4/string_h.m4 | 3 +- modules/mbs_endswith | 26 ++++++++++++++++ modules/string-h | 1 + 6 files changed, 123 insertions(+), 1 deletion(-) create mode 100644 lib/mbs_endswith.c create mode 100644 modules/mbs_endswith diff --git a/ChangeLog b/ChangeLog index a7b87e7061..4dc577daf5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,13 @@ +2025-01-03 Bruno Haible + + 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 mbs_startswith: New module. diff --git a/lib/mbs_endswith.c b/lib/mbs_endswith.c new file mode 100644 index 0000000000..5007a445ec --- /dev/null +++ b/lib/mbs_endswith.c @@ -0,0 +1,74 @@ +/* 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 . */ + +/* Written by Bruno Haible , 2025. */ + +#include "config.h" + +/* Specification. */ +#include + +#include "mbiter.h" + +#include + +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; +} diff --git a/lib/string.in.h b/lib/string.in.h index ce5b493d71..6b9e342a1a 100644 --- a/lib/string.in.h +++ b/lib/string.in.h @@ -1327,6 +1327,16 @@ _GL_EXTERN_C int mbs_startswith (const char *string, const char *prefix) # 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@ diff --git a/m4/string_h.m4 b/m4/string_h.m4 index a2c8536f38..d0a6760811 100644 --- a/m4/string_h.m4 +++ b/m4/string_h.m4 @@ -1,5 +1,5 @@ # 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, @@ -87,6 +87,7 @@ AC_DEFUN([gl_STRING_H_REQUIRE_DEFAULTS], 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]) diff --git a/modules/mbs_endswith b/modules/mbs_endswith new file mode 100644 index 0000000000..19ead99cd5 --- /dev/null +++ b/modules/mbs_endswith @@ -0,0 +1,26 @@ +Description: +mbs_endswith() function: test whether a multibyte string ends with a given suffix. + +Files: +lib/mbs_endswith.c + +Depends-on: +string-h +str_endswith +mbslen +mbiter + +configure.ac: +gl_STRING_MODULE_INDICATOR([mbs_endswith]) + +Makefile.am: +lib_SOURCES += mbs_endswith.c + +Include: + + +License: +LGPL + +Maintainer: +all diff --git a/modules/string-h b/modules/string-h index 1114bcf9eb..4f9c205424 100644 --- a/modules/string-h +++ b/modules/string-h @@ -51,6 +51,7 @@ string.h: string.in.h $(top_builddir)/config.status $(CXXDEFS_H) $(ARG_NONNULL_H -e 's/@''GNULIB_MBSSPN''@/$(GNULIB_MBSSPN)/g' \ -e 's/@''GNULIB_MBSSEP''@/$(GNULIB_MBSSEP)/g' \ -e 's/@''GNULIB_MBSTOK_R''@/$(GNULIB_MBSTOK_R)/g' \ + -e 's/@''GNULIB_MBS_ENDSWITH''@/$(GNULIB_MBS_ENDSWITH)/g' \ -e 's/@''GNULIB_MBS_STARTSWITH''@/$(GNULIB_MBS_STARTSWITH)/g' \ -e 's/@''GNULIB_MEMCHR''@/$(GNULIB_MEMCHR)/g' \ -e 's/@''GNULIB_MEMMEM''@/$(GNULIB_MEMMEM)/g' \ -- 2.39.5