Branch data Line data Source code
1 : : /*-----------------------------------------------------------------------
2 : : *
3 : : * PostgreSQL locale utilities for libc
4 : : *
5 : : * Portions Copyright (c) 2002-2026, PostgreSQL Global Development Group
6 : : *
7 : : * src/backend/utils/adt/pg_locale_libc.c
8 : : *
9 : : *-----------------------------------------------------------------------
10 : : */
11 : :
12 : : #include "postgres.h"
13 : :
14 : : #include <limits.h>
15 : : #include <wctype.h>
16 : :
17 : : #include "access/htup_details.h"
18 : : #include "catalog/pg_database.h"
19 : : #include "catalog/pg_collation.h"
20 : : #include "mb/pg_wchar.h"
21 : : #include "miscadmin.h"
22 : : #include "utils/builtins.h"
23 : : #include "utils/formatting.h"
24 : : #include "utils/memutils.h"
25 : : #include "utils/pg_locale.h"
26 : : #include "utils/syscache.h"
27 : :
28 : : #ifdef __GLIBC__
29 : : #include <gnu/libc-version.h>
30 : : #endif
31 : :
32 : : #ifdef WIN32
33 : : #include <shlwapi.h>
34 : : #endif
35 : :
36 : : /*
37 : : * For the libc provider, to provide as much functionality as possible on a
38 : : * variety of platforms without going so far as to implement everything from
39 : : * scratch, we use several implementation strategies depending on the
40 : : * situation:
41 : : *
42 : : * 1. In C/POSIX collations, we use hard-wired code. We can't depend on
43 : : * the <ctype.h> functions since those will obey LC_CTYPE. Note that these
44 : : * collations don't give a fig about multibyte characters.
45 : : *
46 : : * 2. When working in UTF8 encoding, we use the <wctype.h> functions.
47 : : * This assumes that every platform uses Unicode codepoints directly
48 : : * as the wchar_t representation of Unicode. On some platforms
49 : : * wchar_t is only 16 bits wide, so we have to punt for codepoints > 0xFFFF.
50 : : *
51 : : * 3. In all other encodings, we use the <ctype.h> functions for pg_wchar
52 : : * values up to 255, and punt for values above that. This is 100% correct
53 : : * only in single-byte encodings such as LATINn. However, non-Unicode
54 : : * multibyte encodings are mostly Far Eastern character sets for which the
55 : : * properties being tested here aren't very relevant for higher code values
56 : : * anyway. The difficulty with using the <wctype.h> functions with
57 : : * non-Unicode multibyte encodings is that we can have no certainty that
58 : : * the platform's wchar_t representation matches what we do in pg_wchar
59 : : * conversions.
60 : : *
61 : : * As a special case, in the "default" collation, (2) and (3) force ASCII
62 : : * letters to follow ASCII upcase/downcase rules, while in a non-default
63 : : * collation we just let the library functions do what they will. The case
64 : : * where this matters is treatment of I/i in Turkish, and the behavior is
65 : : * meant to match the upper()/lower() SQL functions.
66 : : *
67 : : * NB: the coding here assumes pg_wchar is an unsigned type.
68 : : */
69 : :
70 : : /*
71 : : * Size of stack buffer to use for string transformations, used to avoid heap
72 : : * allocations in typical cases. This should be large enough that most strings
73 : : * will fit, but small enough that we feel comfortable putting it on the
74 : : * stack.
75 : : */
76 : : #define TEXTBUFLEN 1024
77 : :
78 : : extern pg_locale_t create_pg_locale_libc(Oid collid, MemoryContext context);
79 : :
80 : : static int strncoll_libc(const char *arg1, size_t len1,
81 : : const char *arg2, size_t len2,
82 : : pg_locale_t locale);
83 : : static int strcoll_libc(const char *arg1, const char *arg2,
84 : : pg_locale_t locale);
85 : : static size_t strnxfrm_libc(char *dest, size_t destsize,
86 : : const char *src, size_t srclen,
87 : : pg_locale_t locale);
88 : : static size_t strxfrm_libc(char *dest, size_t destsize,
89 : : const char *src, pg_locale_t locale);
90 : : extern char *get_collation_actual_version_libc(const char *collcollate);
91 : : static locale_t make_libc_collator(const char *collate,
92 : : const char *ctype);
93 : :
94 : : #ifdef WIN32
95 : : static int strncoll_libc_win32_utf8(const char *arg1, size_t len1,
96 : : const char *arg2, size_t len2,
97 : : pg_locale_t locale);
98 : : static int strcoll_libc_win32_utf8(const char *arg1, const char *arg2,
99 : : pg_locale_t locale);
100 : : #endif
101 : :
102 : : static size_t char2wchar(wchar_t *to, size_t tolen, const char *from,
103 : : size_t fromlen, locale_t loc);
104 : :
105 : : static size_t strlower_libc_sb(char *dest, size_t destsize,
106 : : const char *src, size_t srclen,
107 : : pg_locale_t locale);
108 : : static size_t strlower_libc_mb(char *dest, size_t destsize,
109 : : const char *src, size_t srclen,
110 : : pg_locale_t locale);
111 : : static size_t strtitle_libc_sb(char *dest, size_t destsize,
112 : : const char *src, size_t srclen,
113 : : pg_locale_t locale);
114 : : static size_t strtitle_libc_mb(char *dest, size_t destsize,
115 : : const char *src, size_t srclen,
116 : : pg_locale_t locale);
117 : : static size_t strupper_libc_sb(char *dest, size_t destsize,
118 : : const char *src, size_t srclen,
119 : : pg_locale_t locale);
120 : : static size_t strupper_libc_mb(char *dest, size_t destsize,
121 : : const char *src, size_t srclen,
122 : : pg_locale_t locale);
123 : :
124 : : static bool
125 : 0 : wc_isdigit_libc_sb(pg_wchar wc, pg_locale_t locale)
126 : : {
127 [ # # ]: 0 : if (wc > UCHAR_MAX)
128 : 0 : return false;
129 : 0 : return isdigit_l((unsigned char) wc, locale->lt);
130 : : }
131 : :
132 : : static bool
133 : 0 : wc_isalpha_libc_sb(pg_wchar wc, pg_locale_t locale)
134 : : {
135 [ # # ]: 0 : if (wc > UCHAR_MAX)
136 : 0 : return false;
137 : 0 : return isalpha_l((unsigned char) wc, locale->lt);
138 : : }
139 : :
140 : : static bool
141 : 0 : wc_isalnum_libc_sb(pg_wchar wc, pg_locale_t locale)
142 : : {
143 [ # # ]: 0 : if (wc > UCHAR_MAX)
144 : 0 : return false;
145 : 0 : return isalnum_l((unsigned char) wc, locale->lt);
146 : : }
147 : :
148 : : static bool
149 : 0 : wc_isupper_libc_sb(pg_wchar wc, pg_locale_t locale)
150 : : {
151 [ # # ]: 0 : if (wc > UCHAR_MAX)
152 : 0 : return false;
153 : 0 : return isupper_l((unsigned char) wc, locale->lt);
154 : : }
155 : :
156 : : static bool
157 : 0 : wc_islower_libc_sb(pg_wchar wc, pg_locale_t locale)
158 : : {
159 [ # # ]: 0 : if (wc > UCHAR_MAX)
160 : 0 : return false;
161 : 0 : return islower_l((unsigned char) wc, locale->lt);
162 : : }
163 : :
164 : : static bool
165 : 0 : wc_isgraph_libc_sb(pg_wchar wc, pg_locale_t locale)
166 : : {
167 [ # # ]: 0 : if (wc > UCHAR_MAX)
168 : 0 : return false;
169 : 0 : return isgraph_l((unsigned char) wc, locale->lt);
170 : : }
171 : :
172 : : static bool
173 : 0 : wc_isprint_libc_sb(pg_wchar wc, pg_locale_t locale)
174 : : {
175 [ # # ]: 0 : if (wc > UCHAR_MAX)
176 : 0 : return false;
177 : 0 : return isprint_l((unsigned char) wc, locale->lt);
178 : : }
179 : :
180 : : static bool
181 : 0 : wc_ispunct_libc_sb(pg_wchar wc, pg_locale_t locale)
182 : : {
183 [ # # ]: 0 : if (wc > UCHAR_MAX)
184 : 0 : return false;
185 : 0 : return ispunct_l((unsigned char) wc, locale->lt);
186 : : }
187 : :
188 : : static bool
189 : 0 : wc_isspace_libc_sb(pg_wchar wc, pg_locale_t locale)
190 : : {
191 [ # # ]: 0 : if (wc > UCHAR_MAX)
192 : 0 : return false;
193 : 0 : return isspace_l((unsigned char) wc, locale->lt);
194 : : }
195 : :
196 : : static bool
197 : 0 : wc_isxdigit_libc_sb(pg_wchar wc, pg_locale_t locale)
198 : : {
199 [ # # ]: 0 : if (wc > UCHAR_MAX)
200 : 0 : return false;
201 : : #ifndef WIN32
202 : 0 : return isxdigit_l((unsigned char) wc, locale->lt);
203 : : #else
204 : : return _isxdigit_l((unsigned char) wc, locale->lt);
205 : : #endif
206 : : }
207 : :
208 : : static bool
209 : 0 : wc_iscased_libc_sb(pg_wchar wc, pg_locale_t locale)
210 : : {
211 [ # # ]: 0 : if (wc > UCHAR_MAX)
212 : 0 : return false;
213 [ # # ]: 0 : return isupper_l((unsigned char) wc, locale->lt) ||
214 [ # # ]: 0 : islower_l((unsigned char) wc, locale->lt);
215 : : }
216 : :
217 : : static bool
218 : 100877 : wc_isdigit_libc_mb(pg_wchar wc, pg_locale_t locale)
219 : : {
220 : : if (sizeof(wchar_t) < 4 && wc > (pg_wchar) 0xFFFF)
221 : : return false;
222 : 100877 : return iswdigit_l((wint_t) wc, locale->lt);
223 : : }
224 : :
225 : : static bool
226 : 75935 : wc_isalpha_libc_mb(pg_wchar wc, pg_locale_t locale)
227 : : {
228 : : if (sizeof(wchar_t) < 4 && wc > (pg_wchar) 0xFFFF)
229 : : return false;
230 : 75935 : return iswalpha_l((wint_t) wc, locale->lt);
231 : : }
232 : :
233 : : static bool
234 : 1642626 : wc_isalnum_libc_mb(pg_wchar wc, pg_locale_t locale)
235 : : {
236 : : if (sizeof(wchar_t) < 4 && wc > (pg_wchar) 0xFFFF)
237 : : return false;
238 : 1642626 : return iswalnum_l((wint_t) wc, locale->lt);
239 : : }
240 : :
241 : : static bool
242 : 2056 : wc_isupper_libc_mb(pg_wchar wc, pg_locale_t locale)
243 : : {
244 : : if (sizeof(wchar_t) < 4 && wc > (pg_wchar) 0xFFFF)
245 : : return false;
246 : 2056 : return iswupper_l((wint_t) wc, locale->lt);
247 : : }
248 : :
249 : : static bool
250 : 2051 : wc_islower_libc_mb(pg_wchar wc, pg_locale_t locale)
251 : : {
252 : : if (sizeof(wchar_t) < 4 && wc > (pg_wchar) 0xFFFF)
253 : : return false;
254 : 2051 : return iswlower_l((wint_t) wc, locale->lt);
255 : : }
256 : :
257 : : static bool
258 : 0 : wc_isgraph_libc_mb(pg_wchar wc, pg_locale_t locale)
259 : : {
260 : : if (sizeof(wchar_t) < 4 && wc > (pg_wchar) 0xFFFF)
261 : : return false;
262 : 0 : return iswgraph_l((wint_t) wc, locale->lt);
263 : : }
264 : :
265 : : static bool
266 : 0 : wc_isprint_libc_mb(pg_wchar wc, pg_locale_t locale)
267 : : {
268 : : if (sizeof(wchar_t) < 4 && wc > (pg_wchar) 0xFFFF)
269 : : return false;
270 : 0 : return iswprint_l((wint_t) wc, locale->lt);
271 : : }
272 : :
273 : : static bool
274 : 2051 : wc_ispunct_libc_mb(pg_wchar wc, pg_locale_t locale)
275 : : {
276 : : if (sizeof(wchar_t) < 4 && wc > (pg_wchar) 0xFFFF)
277 : : return false;
278 : 2051 : return iswpunct_l((wint_t) wc, locale->lt);
279 : : }
280 : :
281 : : static bool
282 : 34486 : wc_isspace_libc_mb(pg_wchar wc, pg_locale_t locale)
283 : : {
284 : : if (sizeof(wchar_t) < 4 && wc > (pg_wchar) 0xFFFF)
285 : : return false;
286 : 34486 : return iswspace_l((wint_t) wc, locale->lt);
287 : : }
288 : :
289 : : static bool
290 : 9 : wc_isxdigit_libc_mb(pg_wchar wc, pg_locale_t locale)
291 : : {
292 : : if (sizeof(wchar_t) < 4 && wc > (pg_wchar) 0xFFFF)
293 : : return false;
294 : : #ifndef WIN32
295 : 9 : return iswxdigit_l((wint_t) wc, locale->lt);
296 : : #else
297 : : return _iswxdigit_l((wint_t) wc, locale->lt);
298 : : #endif
299 : : }
300 : :
301 : : static bool
302 : 0 : wc_iscased_libc_other_mb(pg_wchar wc, pg_locale_t locale)
303 : : {
304 : : /*
305 : : * For non-UTF8 multibyte encodings, we conservatively assume that any
306 : : * non-ASCII character could be case-varying.
307 : : */
308 [ # # ]: 0 : if (wc > (pg_wchar) 127)
309 : 0 : return true;
310 : :
311 : : /* ASCII: pass directly to isupper_l()/islower_l() */
312 [ # # ]: 0 : return isupper_l((unsigned char) wc, locale->lt) ||
313 [ # # ]: 0 : islower_l((unsigned char) wc, locale->lt);
314 : : }
315 : :
316 : : static bool
317 : 0 : wc_iscased_libc_utf8(pg_wchar wc, pg_locale_t locale)
318 : : {
319 : : /*
320 : : * If sizeof(wchar_t) < 4 (that is, on Windows), then return false. This
321 : : * is consistent with the behavior of strlower_libc_mb(): the UTF8 string
322 : : * will be decoded into 16-bit wchar_t, so strlower_libc_mb() will never
323 : : * deal with codepoints beyond 0xFFFF. It may deal with surrogate pairs,
324 : : * but those characters map to themselves anyway.
325 : : */
326 : : if (sizeof(wchar_t) < 4 && wc > (pg_wchar) 0xFFFF)
327 : : return false;
328 : :
329 : : /*
330 : : * For UTF8, pg_wchar is a codepoint and we assume we can pass it directly
331 : : * to iswupper_l()/iswlower_l(). Some C libraries case-map characters
332 : : * that they do not classify as upper or lower, so also check the mapping,
333 : : * which is what ILIKE uses.
334 : : */
335 [ # # ]: 0 : return iswupper_l((wint_t) wc, locale->lt) ||
336 [ # # ]: 0 : iswlower_l((wint_t) wc, locale->lt) ||
337 [ # # # # ]: 0 : towupper_l((wint_t) wc, locale->lt) != (wint_t) wc ||
338 : 0 : towlower_l((wint_t) wc, locale->lt) != (wint_t) wc;
339 : : }
340 : :
341 : : static pg_wchar
342 : 0 : toupper_libc_sb(pg_wchar wc, pg_locale_t locale)
343 : : {
344 : : Assert(GetDatabaseEncoding() != PG_UTF8);
345 : :
346 : : /* force C behavior for ASCII characters, per comments above */
347 [ # # # # ]: 0 : if (locale->is_default && wc <= (pg_wchar) 127)
348 : 0 : return pg_ascii_toupper((unsigned char) wc);
349 [ # # ]: 0 : else if (wc <= (pg_wchar) UCHAR_MAX)
350 : 0 : return toupper_l((unsigned char) wc, locale->lt);
351 : : else
352 : 0 : return wc;
353 : : }
354 : :
355 : : static pg_wchar
356 : 4679 : toupper_libc_mb(pg_wchar wc, pg_locale_t locale)
357 : : {
358 : : Assert(GetDatabaseEncoding() == PG_UTF8);
359 : :
360 : : /* force C behavior for ASCII characters, per comments above */
361 [ + - + + ]: 4679 : if (locale->is_default && wc <= (pg_wchar) 127)
362 : 581 : return pg_ascii_toupper((unsigned char) wc);
363 : : else if (sizeof(wchar_t) >= 4 || wc <= (pg_wchar) 0xFFFF)
364 : 4098 : return towupper_l((wint_t) wc, locale->lt);
365 : : else
366 : : return wc;
367 : : }
368 : :
369 : : static pg_wchar
370 : 0 : tolower_libc_sb(pg_wchar wc, pg_locale_t locale)
371 : : {
372 : : Assert(GetDatabaseEncoding() != PG_UTF8);
373 : :
374 : : /* force C behavior for ASCII characters, per comments above */
375 [ # # # # ]: 0 : if (locale->is_default && wc <= (pg_wchar) 127)
376 : 0 : return pg_ascii_tolower((unsigned char) wc);
377 [ # # ]: 0 : else if (wc <= (pg_wchar) UCHAR_MAX)
378 : 0 : return tolower_l((unsigned char) wc, locale->lt);
379 : : else
380 : 0 : return wc;
381 : : }
382 : :
383 : : static pg_wchar
384 : 4681 : tolower_libc_mb(pg_wchar wc, pg_locale_t locale)
385 : : {
386 : : Assert(GetDatabaseEncoding() == PG_UTF8);
387 : :
388 : : /* force C behavior for ASCII characters, per comments above */
389 [ + - + + ]: 4681 : if (locale->is_default && wc <= (pg_wchar) 127)
390 : 583 : return pg_ascii_tolower((unsigned char) wc);
391 : : else if (sizeof(wchar_t) >= 4 || wc <= (pg_wchar) 0xFFFF)
392 : 4098 : return towlower_l((wint_t) wc, locale->lt);
393 : : else
394 : : return wc;
395 : : }
396 : :
397 : : static const struct ctype_methods ctype_methods_libc_sb = {
398 : : .strlower = strlower_libc_sb,
399 : : .strtitle = strtitle_libc_sb,
400 : : .strupper = strupper_libc_sb,
401 : : /* in libc, casefolding is the same as lowercasing */
402 : : .strfold = strlower_libc_sb,
403 : : .wc_isdigit = wc_isdigit_libc_sb,
404 : : .wc_isalpha = wc_isalpha_libc_sb,
405 : : .wc_isalnum = wc_isalnum_libc_sb,
406 : : .wc_isupper = wc_isupper_libc_sb,
407 : : .wc_islower = wc_islower_libc_sb,
408 : : .wc_isgraph = wc_isgraph_libc_sb,
409 : : .wc_isprint = wc_isprint_libc_sb,
410 : : .wc_ispunct = wc_ispunct_libc_sb,
411 : : .wc_isspace = wc_isspace_libc_sb,
412 : : .wc_isxdigit = wc_isxdigit_libc_sb,
413 : : .wc_iscased = wc_iscased_libc_sb,
414 : : .wc_toupper = toupper_libc_sb,
415 : : .wc_tolower = tolower_libc_sb,
416 : : };
417 : :
418 : : /*
419 : : * Non-UTF8 multibyte encodings use multibyte semantics for case mapping, but
420 : : * single-byte semantics for pattern matching (except wc_iscased which needs
421 : : * to be consistent with case mapping).
422 : : */
423 : : static const struct ctype_methods ctype_methods_libc_other_mb = {
424 : : .strlower = strlower_libc_mb,
425 : : .strtitle = strtitle_libc_mb,
426 : : .strupper = strupper_libc_mb,
427 : : /* in libc, casefolding is the same as lowercasing */
428 : : .strfold = strlower_libc_mb,
429 : : .wc_isdigit = wc_isdigit_libc_sb,
430 : : .wc_isalpha = wc_isalpha_libc_sb,
431 : : .wc_isalnum = wc_isalnum_libc_sb,
432 : : .wc_isupper = wc_isupper_libc_sb,
433 : : .wc_islower = wc_islower_libc_sb,
434 : : .wc_isgraph = wc_isgraph_libc_sb,
435 : : .wc_isprint = wc_isprint_libc_sb,
436 : : .wc_ispunct = wc_ispunct_libc_sb,
437 : : .wc_isspace = wc_isspace_libc_sb,
438 : : .wc_isxdigit = wc_isxdigit_libc_sb,
439 : : .wc_iscased = wc_iscased_libc_other_mb,
440 : : .wc_toupper = toupper_libc_sb,
441 : : .wc_tolower = tolower_libc_sb,
442 : : };
443 : :
444 : : static const struct ctype_methods ctype_methods_libc_utf8 = {
445 : : .strlower = strlower_libc_mb,
446 : : .strtitle = strtitle_libc_mb,
447 : : .strupper = strupper_libc_mb,
448 : : /* in libc, casefolding is the same as lowercasing */
449 : : .strfold = strlower_libc_mb,
450 : : .wc_isdigit = wc_isdigit_libc_mb,
451 : : .wc_isalpha = wc_isalpha_libc_mb,
452 : : .wc_isalnum = wc_isalnum_libc_mb,
453 : : .wc_isupper = wc_isupper_libc_mb,
454 : : .wc_islower = wc_islower_libc_mb,
455 : : .wc_isgraph = wc_isgraph_libc_mb,
456 : : .wc_isprint = wc_isprint_libc_mb,
457 : : .wc_ispunct = wc_ispunct_libc_mb,
458 : : .wc_isspace = wc_isspace_libc_mb,
459 : : .wc_isxdigit = wc_isxdigit_libc_mb,
460 : : .wc_iscased = wc_iscased_libc_utf8,
461 : : .wc_toupper = toupper_libc_mb,
462 : : .wc_tolower = tolower_libc_mb,
463 : : };
464 : :
465 : : static const struct collate_methods collate_methods_libc = {
466 : : .strncoll = strncoll_libc,
467 : : .strcoll = strcoll_libc,
468 : : .strnxfrm = strnxfrm_libc,
469 : : .strxfrm = strxfrm_libc,
470 : : .strnxfrm_prefix = NULL,
471 : : .strxfrm_prefix = NULL,
472 : :
473 : : /*
474 : : * Unfortunately, it seems that strxfrm() for non-C collations is broken
475 : : * on many common platforms; testing of multiple versions of glibc reveals
476 : : * that, for many locales, strcoll() and strxfrm() do not return
477 : : * consistent results. While no other libc other than Cygwin has so far
478 : : * been shown to have a problem, we take the conservative course of action
479 : : * for right now and disable this categorically. (Users who are certain
480 : : * this isn't a problem on their system can define TRUST_STRXFRM.)
481 : : */
482 : : #ifdef TRUST_STRXFRM
483 : : .strxfrm_is_safe = true,
484 : : #else
485 : : .strxfrm_is_safe = false,
486 : : #endif
487 : : };
488 : :
489 : : #ifdef WIN32
490 : : static const struct collate_methods collate_methods_libc_win32_utf8 = {
491 : : .strncoll = strncoll_libc_win32_utf8,
492 : : .strcoll = strcoll_libc_win32_utf8,
493 : : .strnxfrm = strnxfrm_libc,
494 : : .strxfrm = strxfrm_libc,
495 : : .strnxfrm_prefix = NULL,
496 : : #ifdef TRUST_STRXFRM
497 : : .strxfrm_is_safe = true,
498 : : #else
499 : : .strxfrm_is_safe = false,
500 : : #endif
501 : : };
502 : : #endif
503 : :
504 : : static size_t
505 : 0 : strlower_libc_sb(char *dest, size_t destsize, const char *src, size_t srclen,
506 : : pg_locale_t locale)
507 : : {
508 [ # # ]: 0 : if (srclen + 1 <= destsize)
509 : : {
510 : 0 : locale_t loc = locale->lt;
511 : : char *p;
512 : :
513 : 0 : memcpy(dest, src, srclen);
514 : 0 : dest[srclen] = '\0';
515 : :
516 : : /*
517 : : * Note: we assume that tolower_l() will not be so broken as to need
518 : : * an isupper_l() guard test. When using the default collation, we
519 : : * apply the traditional Postgres behavior that forces ASCII-style
520 : : * treatment of I/i, but in non-default collations you get exactly
521 : : * what the collation says.
522 : : */
523 [ # # ]: 0 : for (p = dest; *p; p++)
524 : : {
525 [ # # ]: 0 : if (locale->is_default)
526 : : {
527 [ # # # # ]: 0 : if (*p >= 'A' && *p <= 'Z')
528 : 0 : *p += 'a' - 'A';
529 [ # # # # ]: 0 : else if (IS_HIGHBIT_SET(*p) && isupper_l((unsigned char) *p, loc))
530 : 0 : *p = tolower_l((unsigned char) *p, loc);
531 : : }
532 : : else
533 : 0 : *p = tolower_l((unsigned char) *p, loc);
534 : : }
535 : : }
536 : :
537 : 0 : return srclen;
538 : : }
539 : :
540 : : static size_t
541 : 519533 : strlower_libc_mb(char *dest, size_t destsize, const char *src, size_t srclen,
542 : : pg_locale_t locale)
543 : : {
544 : 519533 : locale_t loc = locale->lt;
545 : : size_t result_size;
546 : : wchar_t *workspace;
547 : : char *result;
548 : : size_t curr_char;
549 : : size_t max_size;
550 : :
551 : : /* Overflow paranoia */
552 [ - + ]: 519533 : if ((srclen + 1) > (INT_MAX / sizeof(wchar_t)))
553 [ # # ]: 0 : ereport(ERROR,
554 : : (errcode(ERRCODE_OUT_OF_MEMORY),
555 : : errmsg("out of memory")));
556 : :
557 : : /* Output workspace cannot have more codes than input bytes */
558 : 519533 : workspace = palloc_array(wchar_t, srclen + 1);
559 : :
560 : 519533 : char2wchar(workspace, srclen + 1, src, srclen, loc);
561 : :
562 [ + + ]: 2495031 : for (curr_char = 0; workspace[curr_char] != 0; curr_char++)
563 : 1975498 : workspace[curr_char] = towlower_l(workspace[curr_char], loc);
564 : :
565 : : /*
566 : : * Make result large enough; case change might change number of bytes
567 : : */
568 : 519533 : max_size = curr_char * pg_database_encoding_max_length();
569 : 519533 : result = palloc(max_size + 1);
570 : :
571 : 519533 : result_size = wchar2char(result, workspace, max_size + 1, loc);
572 : :
573 [ + - ]: 519533 : if (destsize >= result_size + 1)
574 : : {
575 : 519533 : memcpy(dest, result, result_size);
576 : 519533 : dest[result_size] = '\0';
577 : : }
578 : :
579 : 519533 : pfree(workspace);
580 : 519533 : pfree(result);
581 : :
582 : 519533 : return result_size;
583 : : }
584 : :
585 : : static size_t
586 : 0 : strtitle_libc_sb(char *dest, size_t destsize, const char *src, size_t srclen,
587 : : pg_locale_t locale)
588 : : {
589 [ # # ]: 0 : if (srclen + 1 <= destsize)
590 : : {
591 : 0 : locale_t loc = locale->lt;
592 : 0 : int wasalnum = false;
593 : : char *p;
594 : :
595 : 0 : memcpy(dest, src, srclen);
596 : 0 : dest[srclen] = '\0';
597 : :
598 : : /*
599 : : * Note: we assume that toupper_l()/tolower_l() will not be so broken
600 : : * as to need guard tests. When using the default collation, we apply
601 : : * the traditional Postgres behavior that forces ASCII-style treatment
602 : : * of I/i, but in non-default collations you get exactly what the
603 : : * collation says.
604 : : */
605 [ # # ]: 0 : for (p = dest; *p; p++)
606 : : {
607 [ # # ]: 0 : if (locale->is_default)
608 : : {
609 [ # # ]: 0 : if (wasalnum)
610 : : {
611 [ # # # # ]: 0 : if (*p >= 'A' && *p <= 'Z')
612 : 0 : *p += 'a' - 'A';
613 [ # # # # ]: 0 : else if (IS_HIGHBIT_SET(*p) && isupper_l((unsigned char) *p, loc))
614 : 0 : *p = tolower_l((unsigned char) *p, loc);
615 : : }
616 : : else
617 : : {
618 [ # # # # ]: 0 : if (*p >= 'a' && *p <= 'z')
619 : 0 : *p -= 'a' - 'A';
620 [ # # # # ]: 0 : else if (IS_HIGHBIT_SET(*p) && islower_l((unsigned char) *p, loc))
621 : 0 : *p = toupper_l((unsigned char) *p, loc);
622 : : }
623 : : }
624 : : else
625 : : {
626 [ # # ]: 0 : if (wasalnum)
627 : 0 : *p = tolower_l((unsigned char) *p, loc);
628 : : else
629 : 0 : *p = toupper_l((unsigned char) *p, loc);
630 : : }
631 : 0 : wasalnum = isalnum_l((unsigned char) *p, loc);
632 : : }
633 : : }
634 : :
635 : 0 : return srclen;
636 : : }
637 : :
638 : : static size_t
639 : 18 : strtitle_libc_mb(char *dest, size_t destsize, const char *src, size_t srclen,
640 : : pg_locale_t locale)
641 : : {
642 : 18 : locale_t loc = locale->lt;
643 : 18 : int wasalnum = false;
644 : : size_t result_size;
645 : : wchar_t *workspace;
646 : : char *result;
647 : : size_t curr_char;
648 : : size_t max_size;
649 : :
650 : : /* Overflow paranoia */
651 [ - + ]: 18 : if ((srclen + 1) > (INT_MAX / sizeof(wchar_t)))
652 [ # # ]: 0 : ereport(ERROR,
653 : : (errcode(ERRCODE_OUT_OF_MEMORY),
654 : : errmsg("out of memory")));
655 : :
656 : : /* Output workspace cannot have more codes than input bytes */
657 : 18 : workspace = palloc_array(wchar_t, srclen + 1);
658 : :
659 : 18 : char2wchar(workspace, srclen + 1, src, srclen, loc);
660 : :
661 [ + + ]: 165 : for (curr_char = 0; workspace[curr_char] != 0; curr_char++)
662 : : {
663 [ + + ]: 147 : if (wasalnum)
664 : 111 : workspace[curr_char] = towlower_l(workspace[curr_char], loc);
665 : : else
666 : 36 : workspace[curr_char] = towupper_l(workspace[curr_char], loc);
667 : 147 : wasalnum = iswalnum_l(workspace[curr_char], loc);
668 : : }
669 : :
670 : : /*
671 : : * Make result large enough; case change might change number of bytes
672 : : */
673 : 18 : max_size = curr_char * pg_database_encoding_max_length();
674 : 18 : result = palloc(max_size + 1);
675 : :
676 : 18 : result_size = wchar2char(result, workspace, max_size + 1, loc);
677 : :
678 [ + - ]: 18 : if (destsize >= result_size + 1)
679 : : {
680 : 18 : memcpy(dest, result, result_size);
681 : 18 : dest[result_size] = '\0';
682 : : }
683 : :
684 : 18 : pfree(workspace);
685 : 18 : pfree(result);
686 : :
687 : 18 : return result_size;
688 : : }
689 : :
690 : : static size_t
691 : 0 : strupper_libc_sb(char *dest, size_t destsize, const char *src, size_t srclen,
692 : : pg_locale_t locale)
693 : : {
694 [ # # ]: 0 : if (srclen + 1 <= destsize)
695 : : {
696 : 0 : locale_t loc = locale->lt;
697 : : char *p;
698 : :
699 : 0 : memcpy(dest, src, srclen);
700 : 0 : dest[srclen] = '\0';
701 : :
702 : : /*
703 : : * Note: we assume that toupper_l() will not be so broken as to need
704 : : * an islower_l() guard test. When using the default collation, we
705 : : * apply the traditional Postgres behavior that forces ASCII-style
706 : : * treatment of I/i, but in non-default collations you get exactly
707 : : * what the collation says.
708 : : */
709 [ # # ]: 0 : for (p = dest; *p; p++)
710 : : {
711 [ # # ]: 0 : if (locale->is_default)
712 : : {
713 [ # # # # ]: 0 : if (*p >= 'a' && *p <= 'z')
714 : 0 : *p -= 'a' - 'A';
715 [ # # # # ]: 0 : else if (IS_HIGHBIT_SET(*p) && islower_l((unsigned char) *p, loc))
716 : 0 : *p = toupper_l((unsigned char) *p, loc);
717 : : }
718 : : else
719 : 0 : *p = toupper_l((unsigned char) *p, loc);
720 : : }
721 : : }
722 : :
723 : 0 : return srclen;
724 : : }
725 : :
726 : : static size_t
727 : 522040 : strupper_libc_mb(char *dest, size_t destsize, const char *src, size_t srclen,
728 : : pg_locale_t locale)
729 : : {
730 : 522040 : locale_t loc = locale->lt;
731 : : size_t result_size;
732 : : wchar_t *workspace;
733 : : char *result;
734 : : size_t curr_char;
735 : : size_t max_size;
736 : :
737 : : /* Overflow paranoia */
738 [ - + ]: 522040 : if ((srclen + 1) > (INT_MAX / sizeof(wchar_t)))
739 [ # # ]: 0 : ereport(ERROR,
740 : : (errcode(ERRCODE_OUT_OF_MEMORY),
741 : : errmsg("out of memory")));
742 : :
743 : : /* Output workspace cannot have more codes than input bytes */
744 : 522040 : workspace = palloc_array(wchar_t, srclen + 1);
745 : :
746 : 522040 : char2wchar(workspace, srclen + 1, src, srclen, loc);
747 : :
748 [ + + ]: 1653686 : for (curr_char = 0; workspace[curr_char] != 0; curr_char++)
749 : 1131646 : workspace[curr_char] = towupper_l(workspace[curr_char], loc);
750 : :
751 : : /*
752 : : * Make result large enough; case change might change number of bytes
753 : : */
754 : 522040 : max_size = curr_char * pg_database_encoding_max_length();
755 : 522040 : result = palloc(max_size + 1);
756 : :
757 : 522040 : result_size = wchar2char(result, workspace, max_size + 1, loc);
758 : :
759 [ + - ]: 522040 : if (destsize >= result_size + 1)
760 : : {
761 : 522040 : memcpy(dest, result, result_size);
762 : 522040 : dest[result_size] = '\0';
763 : : }
764 : :
765 : 522040 : pfree(workspace);
766 : 522040 : pfree(result);
767 : :
768 : 522040 : return result_size;
769 : : }
770 : :
771 : : pg_locale_t
772 : 18781 : create_pg_locale_libc(Oid collid, MemoryContext context)
773 : : {
774 : : const char *collate;
775 : : const char *ctype;
776 : : locale_t loc;
777 : : pg_locale_t result;
778 : :
779 [ + + ]: 18781 : if (collid == DEFAULT_COLLATION_OID)
780 : : {
781 : : HeapTuple tp;
782 : : Datum datum;
783 : :
784 : 18725 : tp = SearchSysCache1(DATABASEOID, ObjectIdGetDatum(MyDatabaseId));
785 [ - + ]: 18725 : if (!HeapTupleIsValid(tp))
786 [ # # ]: 0 : elog(ERROR, "cache lookup failed for database %u", MyDatabaseId);
787 : 18725 : datum = SysCacheGetAttrNotNull(DATABASEOID, tp,
788 : : Anum_pg_database_datcollate);
789 : 18725 : collate = TextDatumGetCString(datum);
790 : 18725 : datum = SysCacheGetAttrNotNull(DATABASEOID, tp,
791 : : Anum_pg_database_datctype);
792 : 18725 : ctype = TextDatumGetCString(datum);
793 : :
794 : 18725 : ReleaseSysCache(tp);
795 : : }
796 : : else
797 : : {
798 : : HeapTuple tp;
799 : : Datum datum;
800 : :
801 : 56 : tp = SearchSysCache1(COLLOID, ObjectIdGetDatum(collid));
802 [ - + ]: 56 : if (!HeapTupleIsValid(tp))
803 [ # # ]: 0 : elog(ERROR, "cache lookup failed for collation %u", collid);
804 : :
805 : 56 : datum = SysCacheGetAttrNotNull(COLLOID, tp,
806 : : Anum_pg_collation_collcollate);
807 : 56 : collate = TextDatumGetCString(datum);
808 : 56 : datum = SysCacheGetAttrNotNull(COLLOID, tp,
809 : : Anum_pg_collation_collctype);
810 : 56 : ctype = TextDatumGetCString(datum);
811 : :
812 : 56 : ReleaseSysCache(tp);
813 : : }
814 : :
815 : :
816 : 18781 : loc = make_libc_collator(collate, ctype);
817 : :
818 : 18781 : result = MemoryContextAllocZero(context, sizeof(struct pg_locale_struct));
819 : 18781 : result->deterministic = true;
820 [ + + ]: 36990 : result->collate_is_c = (strcmp(collate, "C") == 0) ||
821 [ + + ]: 18209 : (strcmp(collate, "POSIX") == 0);
822 [ + + ]: 36990 : result->ctype_is_c = (strcmp(ctype, "C") == 0) ||
823 [ + + ]: 18209 : (strcmp(ctype, "POSIX") == 0);
824 : 18781 : result->lt = loc;
825 [ + + ]: 18781 : if (!result->collate_is_c)
826 : : {
827 : : #ifdef WIN32
828 : : if (GetDatabaseEncoding() == PG_UTF8)
829 : : result->collate = &collate_methods_libc_win32_utf8;
830 : : else
831 : : #endif
832 : 18169 : result->collate = &collate_methods_libc;
833 : : }
834 [ + + ]: 18781 : if (!result->ctype_is_c)
835 : : {
836 [ + + ]: 18169 : if (GetDatabaseEncoding() == PG_UTF8)
837 : 18129 : result->ctype = &ctype_methods_libc_utf8;
838 [ - + ]: 40 : else if (pg_database_encoding_max_length() > 1)
839 : 0 : result->ctype = &ctype_methods_libc_other_mb;
840 : : else
841 : 40 : result->ctype = &ctype_methods_libc_sb;
842 : : }
843 : :
844 : 18781 : return result;
845 : : }
846 : :
847 : : /*
848 : : * Create a locale_t with the given collation and ctype.
849 : : *
850 : : * The "C" and "POSIX" locales are not actually handled by libc, so return
851 : : * NULL.
852 : : *
853 : : * Ensure that no path leaks a locale_t.
854 : : */
855 : : static locale_t
856 : 18781 : make_libc_collator(const char *collate, const char *ctype)
857 : : {
858 : 18781 : locale_t loc = 0;
859 : :
860 [ + - ]: 18781 : if (strcmp(collate, ctype) == 0)
861 : : {
862 [ + + + + ]: 18781 : if (strcmp(ctype, "C") != 0 && strcmp(ctype, "POSIX") != 0)
863 : : {
864 : : /* Normal case where they're the same */
865 : 18169 : errno = 0;
866 : : #ifndef WIN32
867 : 18169 : loc = newlocale(LC_COLLATE_MASK | LC_CTYPE_MASK, collate,
868 : : NULL);
869 : : #else
870 : : loc = _create_locale(LC_ALL, collate);
871 : : #endif
872 [ - + ]: 18169 : if (!loc)
873 : 0 : report_newlocale_failure(collate);
874 : : }
875 : : }
876 : : else
877 : : {
878 : : #ifndef WIN32
879 : : /* We need two newlocale() steps */
880 : 0 : locale_t loc1 = 0;
881 : :
882 [ # # # # ]: 0 : if (strcmp(collate, "C") != 0 && strcmp(collate, "POSIX") != 0)
883 : : {
884 : 0 : errno = 0;
885 : 0 : loc1 = newlocale(LC_COLLATE_MASK, collate, NULL);
886 [ # # ]: 0 : if (!loc1)
887 : 0 : report_newlocale_failure(collate);
888 : : }
889 : :
890 [ # # # # ]: 0 : if (strcmp(ctype, "C") != 0 && strcmp(ctype, "POSIX") != 0)
891 : : {
892 : 0 : errno = 0;
893 : 0 : loc = newlocale(LC_CTYPE_MASK, ctype, loc1);
894 [ # # ]: 0 : if (!loc)
895 : : {
896 [ # # ]: 0 : if (loc1)
897 : 0 : freelocale(loc1);
898 : 0 : report_newlocale_failure(ctype);
899 : : }
900 : : }
901 : : else
902 : 0 : loc = loc1;
903 : : #else
904 : :
905 : : /*
906 : : * XXX The _create_locale() API doesn't appear to support this. Could
907 : : * perhaps be worked around by changing pg_locale_t to contain two
908 : : * separate fields.
909 : : */
910 : : ereport(ERROR,
911 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
912 : : errmsg("collations with different collate and ctype values are not supported on this platform")));
913 : : #endif
914 : : }
915 : :
916 : 18781 : return loc;
917 : : }
918 : :
919 : : /*
920 : : * strncoll_libc
921 : : *
922 : : * NUL-terminate arguments and pass to strcoll_l().
923 : : */
924 : : static int
925 : 2783304 : strncoll_libc(const char *arg1, size_t len1, const char *arg2, size_t len2,
926 : : pg_locale_t locale)
927 : : {
928 : : char sbuf[TEXTBUFLEN];
929 : 2783304 : char *buf = sbuf;
930 : 2783304 : size_t bufsize1 = len1 + 1;
931 : 2783304 : size_t bufsize2 = len2 + 1;
932 : : char *buf1;
933 : : char *buf2;
934 : : const char *arg1n;
935 : : const char *arg2n;
936 : : int result;
937 : :
938 [ + + ]: 2783304 : if (bufsize1 + bufsize2 > TEXTBUFLEN)
939 : 318 : buf = palloc(bufsize1 + bufsize2);
940 : :
941 : 2783304 : buf1 = buf;
942 : 2783304 : buf2 = buf + bufsize1;
943 : :
944 : 2783304 : memcpy(buf1, arg1, len1);
945 : 2783304 : buf1[len1] = '\0';
946 : 2783304 : arg1n = buf1;
947 : :
948 : 2783304 : memcpy(buf2, arg2, len2);
949 : 2783304 : buf2[len2] = '\0';
950 : 2783304 : arg2n = buf2;
951 : :
952 : 2783304 : result = strcoll_l(arg1n, arg2n, locale->lt);
953 : :
954 [ + + ]: 2783304 : if (buf != sbuf)
955 : 318 : pfree(buf);
956 : :
957 : 2783304 : return result;
958 : : }
959 : :
960 : : /*
961 : : * strcoll_libc
962 : : */
963 : : static int
964 : 15129606 : strcoll_libc(const char *arg1, const char *arg2, pg_locale_t locale)
965 : : {
966 : 15129606 : return strcoll_l(arg1, arg2, locale->lt);
967 : : }
968 : :
969 : : /*
970 : : * strnxfrm_libc
971 : : *
972 : : * NUL-terminate src and pass to strxfrm_l().
973 : : *
974 : : * NB: it's possible for this function to return a different size needed for
975 : : * two calls with the same input string. If destsize is too small to hold the
976 : : * result, strxfrm() may return the upper bound of the size needed rather than
977 : : * the exact size needed.
978 : : */
979 : : static size_t
980 : 0 : strnxfrm_libc(char *dest, size_t destsize, const char *src, size_t srclen,
981 : : pg_locale_t locale)
982 : : {
983 : : char sbuf[TEXTBUFLEN];
984 : 0 : char *buf = sbuf;
985 : 0 : size_t bufsize = srclen + 1;
986 : : size_t result;
987 : :
988 [ # # ]: 0 : if (bufsize > TEXTBUFLEN)
989 : 0 : buf = palloc(bufsize);
990 : :
991 : : /* nul-terminate argument */
992 : 0 : memcpy(buf, src, srclen);
993 : 0 : buf[srclen] = '\0';
994 : :
995 : 0 : result = strxfrm_l(dest, buf, destsize, locale->lt);
996 : :
997 [ # # ]: 0 : if (buf != sbuf)
998 : 0 : pfree(buf);
999 : :
1000 : : /* if dest is defined, it should be nul-terminated */
1001 : : Assert(result >= destsize || dest[result] == '\0');
1002 : :
1003 : 0 : return result;
1004 : : }
1005 : :
1006 : : /*
1007 : : * strxfrm_libc
1008 : : *
1009 : : * NB: it's possible for this function to return a different size needed for
1010 : : * two calls with the same input string. If destsize is too small to hold the
1011 : : * result, strxfrm() may return the upper bound of the size needed rather than
1012 : : * the exact size needed.
1013 : : */
1014 : : static size_t
1015 : 132 : strxfrm_libc(char *dest, size_t destsize, const char *src, pg_locale_t locale)
1016 : : {
1017 : 132 : return strxfrm_l(dest, src, destsize, locale->lt);
1018 : : }
1019 : :
1020 : : char *
1021 : 18553 : get_collation_actual_version_libc(const char *collcollate)
1022 : : {
1023 : 18553 : char *collversion = NULL;
1024 : :
1025 [ + + + + ]: 37014 : if (pg_strcasecmp("C", collcollate) != 0 &&
1026 [ + + ]: 36808 : pg_strncasecmp("C.", collcollate, 2) != 0 &&
1027 : 18347 : pg_strcasecmp("POSIX", collcollate) != 0)
1028 : : {
1029 : : #if defined(__GLIBC__)
1030 : : /* Use the glibc version because we don't have anything better. */
1031 : 18333 : collversion = pstrdup(gnu_get_libc_version());
1032 : : #elif defined(LC_VERSION_MASK)
1033 : : locale_t loc;
1034 : :
1035 : : /* Look up FreeBSD collation version. */
1036 : : loc = newlocale(LC_COLLATE_MASK, collcollate, NULL);
1037 : : if (loc)
1038 : : {
1039 : : collversion =
1040 : : pstrdup(querylocale(LC_COLLATE_MASK | LC_VERSION_MASK, loc));
1041 : : freelocale(loc);
1042 : : }
1043 : : else
1044 : : ereport(ERROR,
1045 : : (errmsg("could not load locale \"%s\"", collcollate)));
1046 : : #elif defined(WIN32)
1047 : : /*
1048 : : * If we are targeting Windows Vista and above, we can ask for a name
1049 : : * given a collation name (earlier versions required a location code
1050 : : * that we don't have).
1051 : : */
1052 : : NLSVERSIONINFOEX version = {sizeof(NLSVERSIONINFOEX)};
1053 : : WCHAR wide_collcollate[LOCALE_NAME_MAX_LENGTH];
1054 : :
1055 : : MultiByteToWideChar(CP_ACP, 0, collcollate, -1, wide_collcollate,
1056 : : LOCALE_NAME_MAX_LENGTH);
1057 : : if (!GetNLSVersionEx(COMPARE_STRING, wide_collcollate, &version))
1058 : : {
1059 : : /*
1060 : : * GetNLSVersionEx() wants a language tag such as "en-US", not a
1061 : : * locale name like "English_United States.1252". Until those
1062 : : * values can be prevented from entering the system, or 100%
1063 : : * reliably converted to the more useful tag format, tolerate the
1064 : : * resulting error and report that we have no version data.
1065 : : */
1066 : : if (GetLastError() == ERROR_INVALID_PARAMETER)
1067 : : return NULL;
1068 : :
1069 : : ereport(ERROR,
1070 : : (errmsg("could not get collation version for locale \"%s\": error code %lu",
1071 : : collcollate,
1072 : : GetLastError())));
1073 : : }
1074 : : collversion = psprintf("%lu.%lu,%lu.%lu",
1075 : : (version.dwNLSVersion >> 8) & 0xFFFF,
1076 : : version.dwNLSVersion & 0xFF,
1077 : : (version.dwDefinedVersion >> 8) & 0xFFFF,
1078 : : version.dwDefinedVersion & 0xFF);
1079 : : #endif
1080 : : }
1081 : :
1082 : 18553 : return collversion;
1083 : : }
1084 : :
1085 : : /*
1086 : : * strncoll_libc_win32_utf8
1087 : : *
1088 : : * Win32 does not have UTF-8. Convert UTF8 arguments to wide characters and
1089 : : * invoke wcscoll_l().
1090 : : */
1091 : : #ifdef WIN32
1092 : : static int
1093 : : strncoll_libc_win32_utf8(const char *arg1, size_t len1, const char *arg2,
1094 : : size_t len2, pg_locale_t locale)
1095 : : {
1096 : : char sbuf[TEXTBUFLEN];
1097 : : char *buf = sbuf;
1098 : : char *a1p,
1099 : : *a2p;
1100 : : size_t a1len,
1101 : : a2len,
1102 : : buflen;
1103 : : int r;
1104 : : int result;
1105 : :
1106 : : Assert(GetDatabaseEncoding() == PG_UTF8);
1107 : :
1108 : : /*
1109 : : * In a 32-bit build, twice the input length can overflow size_t, so we
1110 : : * must be careful.
1111 : : */
1112 : : a1len = add_size(add_size(len1, len1), 2);
1113 : : a2len = add_size(add_size(len2, len2), 2);
1114 : : buflen = add_size(a1len, a2len);
1115 : :
1116 : : if (buflen > TEXTBUFLEN)
1117 : : buf = palloc(buflen);
1118 : :
1119 : : a1p = buf;
1120 : : a2p = buf + a1len;
1121 : :
1122 : : /* API does not work for zero-length input */
1123 : : if (len1 == 0)
1124 : : r = 0;
1125 : : else
1126 : : {
1127 : : r = MultiByteToWideChar(CP_UTF8, 0, arg1, len1,
1128 : : (LPWSTR) a1p, a1len / 2);
1129 : : if (!r)
1130 : : ereport(ERROR,
1131 : : (errmsg("could not convert string to UTF-16: error code %lu",
1132 : : GetLastError())));
1133 : : }
1134 : : ((LPWSTR) a1p)[r] = 0;
1135 : :
1136 : : if (len2 == 0)
1137 : : r = 0;
1138 : : else
1139 : : {
1140 : : r = MultiByteToWideChar(CP_UTF8, 0, arg2, len2,
1141 : : (LPWSTR) a2p, a2len / 2);
1142 : : if (!r)
1143 : : ereport(ERROR,
1144 : : (errmsg("could not convert string to UTF-16: error code %lu",
1145 : : GetLastError())));
1146 : : }
1147 : : ((LPWSTR) a2p)[r] = 0;
1148 : :
1149 : : errno = 0;
1150 : : result = wcscoll_l((LPWSTR) a1p, (LPWSTR) a2p, locale->lt);
1151 : : if (result == 2147483647) /* _NLSCMPERROR; missing from mingw headers */
1152 : : ereport(ERROR,
1153 : : (errmsg("could not compare Unicode strings: %m")));
1154 : :
1155 : : if (buf != sbuf)
1156 : : pfree(buf);
1157 : :
1158 : : return result;
1159 : : }
1160 : :
1161 : : static int
1162 : : strcoll_libc_win32_utf8(const char *arg1, const char *arg2,
1163 : : pg_locale_t locale)
1164 : : {
1165 : : size_t len1 = strlen(arg1);
1166 : : size_t len2 = strlen(arg2);
1167 : :
1168 : : return strncoll_libc_win32_utf8(arg1, len1, arg2, len2, locale);
1169 : : }
1170 : : #endif /* WIN32 */
1171 : :
1172 : : /* simple subroutine for reporting errors from newlocale() */
1173 : : void
1174 : 0 : report_newlocale_failure(const char *localename)
1175 : : {
1176 : : int save_errno;
1177 : :
1178 : : /*
1179 : : * Windows doesn't provide any useful error indication from
1180 : : * _create_locale(), and BSD-derived platforms don't seem to feel they
1181 : : * need to set errno either (even though POSIX is pretty clear that
1182 : : * newlocale should do so). So, if errno hasn't been set, assume ENOENT
1183 : : * is what to report.
1184 : : */
1185 [ # # ]: 0 : if (errno == 0)
1186 : 0 : errno = ENOENT;
1187 : :
1188 : : /*
1189 : : * ENOENT means "no such locale", not "no such file", so clarify that
1190 : : * errno with an errdetail message.
1191 : : */
1192 : 0 : save_errno = errno; /* auxiliary funcs might change errno */
1193 [ # # # # ]: 0 : ereport(ERROR,
1194 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1195 : : errmsg("could not create locale \"%s\": %m",
1196 : : localename),
1197 : : (save_errno == ENOENT ?
1198 : : errdetail("The operating system could not find any locale data for the locale name \"%s\".",
1199 : : localename) : 0)));
1200 : : }
1201 : :
1202 : : /*
1203 : : * POSIX doesn't define _l-variants of these functions, but several systems
1204 : : * have them. We provide our own replacements here.
1205 : : */
1206 : : #ifndef HAVE_MBSTOWCS_L
1207 : : static size_t
1208 : 1041591 : mbstowcs_l(wchar_t *dest, const char *src, size_t n, locale_t loc)
1209 : : {
1210 : : #ifdef WIN32
1211 : : return _mbstowcs_l(dest, src, n, loc);
1212 : : #else
1213 : : size_t result;
1214 : 1041591 : locale_t save_locale = uselocale(loc);
1215 : :
1216 : 1041591 : result = mbstowcs(dest, src, n);
1217 : 1041591 : uselocale(save_locale);
1218 : 1041591 : return result;
1219 : : #endif
1220 : : }
1221 : : #endif
1222 : : #ifndef HAVE_WCSTOMBS_L
1223 : : static size_t
1224 : 1041591 : wcstombs_l(char *dest, const wchar_t *src, size_t n, locale_t loc)
1225 : : {
1226 : : #ifdef WIN32
1227 : : return _wcstombs_l(dest, src, n, loc);
1228 : : #else
1229 : : size_t result;
1230 : 1041591 : locale_t save_locale = uselocale(loc);
1231 : :
1232 : 1041591 : result = wcstombs(dest, src, n);
1233 : 1041591 : uselocale(save_locale);
1234 : 1041591 : return result;
1235 : : #endif
1236 : : }
1237 : : #endif
1238 : :
1239 : : /*
1240 : : * These functions convert from/to libc's wchar_t, *not* pg_wchar.
1241 : : * Therefore we keep them here rather than with the mbutils code.
1242 : : */
1243 : :
1244 : : /*
1245 : : * wchar2char --- convert wide characters to multibyte format
1246 : : *
1247 : : * This has the same API as the standard wcstombs_l() function; in particular,
1248 : : * tolen is the maximum number of bytes to store at *to, and *from must be
1249 : : * zero-terminated. The output will be zero-terminated iff there is room.
1250 : : */
1251 : : size_t
1252 : 1041591 : wchar2char(char *to, const wchar_t *from, size_t tolen, locale_t loc)
1253 : : {
1254 : : size_t result;
1255 : :
1256 [ - + ]: 1041591 : if (tolen == 0)
1257 : 0 : return 0;
1258 : :
1259 : : #ifdef WIN32
1260 : :
1261 : : /*
1262 : : * On Windows, the "Unicode" locales assume UTF16 not UTF8 encoding, and
1263 : : * for some reason mbstowcs and wcstombs won't do this for us, so we use
1264 : : * MultiByteToWideChar().
1265 : : */
1266 : : if (GetDatabaseEncoding() == PG_UTF8)
1267 : : {
1268 : : result = WideCharToMultiByte(CP_UTF8, 0, from, -1, to, tolen,
1269 : : NULL, NULL);
1270 : : /* A zero return is failure */
1271 : : if (result <= 0)
1272 : : result = -1;
1273 : : else
1274 : : {
1275 : : Assert(result <= tolen);
1276 : : /* Microsoft counts the zero terminator in the result */
1277 : : result--;
1278 : : }
1279 : : }
1280 : : else
1281 : : #endif /* WIN32 */
1282 [ - + ]: 1041591 : if (loc == (locale_t) 0)
1283 : : {
1284 : : /* Use wcstombs directly for the default locale */
1285 : 0 : result = wcstombs(to, from, tolen);
1286 : : }
1287 : : else
1288 : : {
1289 : : /* Use wcstombs_l for nondefault locales */
1290 : 1041591 : result = wcstombs_l(to, from, tolen, loc);
1291 : : }
1292 : :
1293 : 1041591 : return result;
1294 : : }
1295 : :
1296 : : /*
1297 : : * char2wchar --- convert multibyte characters to wide characters
1298 : : *
1299 : : * This has almost the API of mbstowcs_l(), except that *from need not be
1300 : : * null-terminated; instead, the number of input bytes is specified as
1301 : : * fromlen. Also, we ereport() rather than returning -1 for invalid
1302 : : * input encoding. tolen is the maximum number of wchar_t's to store at *to.
1303 : : * The output will be zero-terminated iff there is room.
1304 : : */
1305 : : static size_t
1306 : 1041591 : char2wchar(wchar_t *to, size_t tolen, const char *from, size_t fromlen,
1307 : : locale_t loc)
1308 : : {
1309 : : size_t result;
1310 : :
1311 [ - + ]: 1041591 : if (tolen == 0)
1312 : 0 : return 0;
1313 : :
1314 : : #ifdef WIN32
1315 : : /* See WIN32 "Unicode" comment above */
1316 : : if (GetDatabaseEncoding() == PG_UTF8)
1317 : : {
1318 : : /* Win32 API does not work for zero-length input */
1319 : : if (fromlen == 0)
1320 : : result = 0;
1321 : : else
1322 : : {
1323 : : result = MultiByteToWideChar(CP_UTF8, 0, from, fromlen, to, tolen - 1);
1324 : : /* A zero return is failure */
1325 : : if (result == 0)
1326 : : result = -1;
1327 : : }
1328 : :
1329 : : if (result != -1)
1330 : : {
1331 : : Assert(result < tolen);
1332 : : /* Append trailing null wchar (MultiByteToWideChar() does not) */
1333 : : to[result] = 0;
1334 : : }
1335 : : }
1336 : : else
1337 : : #endif /* WIN32 */
1338 : : {
1339 : : /* mbstowcs requires ending '\0' */
1340 : 1041591 : char *str = pnstrdup(from, fromlen);
1341 : :
1342 [ - + ]: 1041591 : if (loc == (locale_t) 0)
1343 : : {
1344 : : /* Use mbstowcs directly for the default locale */
1345 : 0 : result = mbstowcs(to, str, tolen);
1346 : : }
1347 : : else
1348 : : {
1349 : : /* Use mbstowcs_l for nondefault locales */
1350 : 1041591 : result = mbstowcs_l(to, str, tolen, loc);
1351 : : }
1352 : :
1353 : 1041591 : pfree(str);
1354 : : }
1355 : :
1356 [ - + ]: 1041591 : if (result == -1)
1357 : : {
1358 : : /*
1359 : : * Invalid multibyte character encountered. We try to give a useful
1360 : : * error message by letting pg_verifymbstr check the string. But it's
1361 : : * possible that the string is OK to us, and not OK to mbstowcs ---
1362 : : * this suggests that the LC_CTYPE locale is different from the
1363 : : * database encoding. Give a generic error message if pg_verifymbstr
1364 : : * can't find anything wrong.
1365 : : */
1366 : 0 : pg_verifymbstr(from, fromlen, false); /* might not return */
1367 : : /* but if it does ... */
1368 [ # # ]: 0 : ereport(ERROR,
1369 : : (errcode(ERRCODE_CHARACTER_NOT_IN_REPERTOIRE),
1370 : : errmsg("invalid multibyte character for locale"),
1371 : : errhint("The server's LC_CTYPE locale is probably incompatible with the database encoding.")));
1372 : : }
1373 : :
1374 : 1041591 : return result;
1375 : : }
|