exp2l: Work around a NetBSD 10.0/i386 bug.
[gnulib.git] / lib / mbmemcasecoll.c
blobdd07dea924f34eb366ab27aa3ea0f3bd8773dbd0
1 /* Locale-specific case-ignoring memory comparison.
2 Copyright (C) 2001, 2009-2024 Free Software Foundation, Inc.
3 Written by Bruno Haible <bruno@clisp.org>, 2001.
5 This file is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
10 This file is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <https://www.gnu.org/licenses/>. */
18 #include <config.h>
20 /* Specification. */
21 #include "mbmemcasecoll.h"
23 #include <errno.h>
24 #include <stdlib.h>
25 #include <string.h>
27 /* Get tolower(). */
28 #include <ctype.h>
30 /* Get mbstate_t. */
31 #include <wchar.h>
33 /* Get char32_t, mbrtoc32(), c32rtomb(), c32tolower(). */
34 #include <uchar.h>
36 #include "malloca.h"
37 #include "memcmp2.h"
38 #include "memcoll.h"
40 /* Apply c32tolower() to the multibyte character sequence in INBUF, storing the
41 result as a multibyte character sequence in OUTBUF. */
42 static size_t
43 apply_c32tolower (const char *inbuf, size_t inbufsize,
44 char *outbuf, size_t outbufsize)
46 char *outbuf_orig = outbuf;
47 size_t remaining;
49 remaining = inbufsize;
50 while (remaining > 0)
52 mbstate_t state;
54 mbszero (&state);
55 for (;;)
57 char32_t wc1;
58 size_t n1;
60 n1 = mbrtoc32 (&wc1, inbuf, remaining, &state);
62 if (n1 == (size_t)(-1))
64 /* Invalid multibyte character on input.
65 Copy one byte without modification. */
66 *outbuf++ = *inbuf++;
67 remaining -= 1;
68 break;
70 else if (n1 == (size_t)(-2))
72 /* Incomplete multibyte sequence on input.
73 Pass it through unmodified. */
74 while (remaining > 0)
76 *outbuf++ = *inbuf++;
77 remaining -= 1;
79 break;
81 else
83 wint_t wc2;
85 if (n1 == 0) /* NUL character? */
86 n1 = 1;
87 #if !GNULIB_MBRTOC32_REGULAR
88 else if (n1 == (size_t)(-3))
89 n1 = 0;
90 #endif
92 wc2 = c32tolower (wc1);
93 if (wc2 != wc1)
95 mbstate_t state2;
96 size_t n2;
98 mbszero (&state2);
99 n2 = c32rtomb (outbuf, wc2, &state2);
100 if (n2 != (size_t)(-1))
102 /* Store the translated multibyte character. */
103 outbuf += n2;
104 goto done_storing;
108 /* Nothing to translate. */
109 memcpy (outbuf, inbuf, n1);
110 outbuf += n1;
111 done_storing:
112 inbuf += n1;
113 remaining -= n1;
115 #if !GNULIB_MBRTOC32_REGULAR
116 if (mbsinit (&state))
117 #endif
118 break;
122 /* Verify the output buffer was large enough. */
123 if (outbuf - outbuf_orig > outbufsize)
124 abort ();
126 /* Return the number of written output bytes. */
127 return outbuf - outbuf_orig;
130 /* Apply tolower() to the unibyte character sequence in INBUF, storing the
131 result as a unibyte character sequence in OUTBUF. */
132 static void
133 apply_tolower (const char *inbuf, char *outbuf, size_t bufsize)
135 for (; bufsize > 0; bufsize--)
137 *outbuf = tolower ((unsigned char) *inbuf);
138 inbuf++;
139 outbuf++;
144 mbmemcasecoll (const char *s1, size_t s1len, const char *s2, size_t s2len,
145 bool hard_LC_COLLATE)
147 char *t1;
148 size_t t1len;
149 char *t2;
150 size_t t2len;
151 char *memory;
152 int cmp;
154 if (MB_CUR_MAX > 1)
156 /* Application of towlower grows each character by a factor 2
157 at most. */
158 t1len = 2 * s1len;
159 t2len = 2 * s2len;
161 else
163 /* Application of tolower doesn't change the size. */
164 t1len = s1len;
165 t2len = s2len;
167 /* Allocate memory for t1 and t2. */
168 memory = (char *) malloca (t1len + 1 + t2len + 1);
169 if (memory == NULL)
171 errno = ENOMEM;
172 return 0;
174 t1 = memory;
175 t2 = memory + t1len + 1;
177 /* Case-fold the two argument strings. */
178 if (MB_CUR_MAX > 1)
180 t1len = apply_c32tolower (s1, s1len, t1, t1len);
181 t2len = apply_c32tolower (s2, s2len, t2, t2len);
183 else
185 apply_tolower (s1, t1, s1len);
186 apply_tolower (s2, t2, s2len);
189 /* Compare the two case-folded strings. */
190 if (hard_LC_COLLATE)
191 cmp = memcoll (t1, t1len, t2, t2len);
192 else
194 cmp = memcmp2 (t1, t1len, t2, t2len);
195 errno = 0;
199 int saved_errno = errno;
200 freea (memory);
201 errno = saved_errno;
204 return cmp;