Commit | Line | Data |
---|---|---|
16db8bac | 1 | /* |
58fddedb | 2 | * Copyright 2010 Nexenta Systems, Inc. All rights reserved. |
984263bc MD |
3 | * Copyright (c) 1995 Alex Tatmanjants <alex@elvisti.kiev.ua> |
4 | * at Electronni Visti IA, Kiev, Ukraine. | |
5 | * All rights reserved. | |
6 | * | |
0d5acd74 JM |
7 | * Copyright (c) 2011 The FreeBSD Foundation |
8 | * All rights reserved. | |
9 | * Portions of this software were developed by David Chisnall | |
10 | * under sponsorship from the FreeBSD Foundation. | |
11 | * | |
984263bc MD |
12 | * Redistribution and use in source and binary forms, with or without |
13 | * modification, are permitted provided that the following conditions | |
14 | * are met: | |
15 | * 1. Redistributions of source code must retain the above copyright | |
16 | * notice, this list of conditions and the following disclaimer. | |
17 | * 2. Redistributions in binary form must reproduce the above copyright | |
18 | * notice, this list of conditions and the following disclaimer in the | |
19 | * documentation and/or other materials provided with the distribution. | |
20 | * | |
21 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND | |
22 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE | |
25 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
26 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
27 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
28 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
29 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
30 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
31 | * SUCH DAMAGE. | |
1de703da | 32 | * |
16db8bac | 33 | * Adapted to xlocale by John Marino <draco@marino.st> |
984263bc MD |
34 | */ |
35 | ||
17ea2221 | 36 | #include "namespace.h" |
8d7b8f97 | 37 | #include <sys/types.h> |
38 | #include <sys/stat.h> | |
39 | #include <sys/mman.h> | |
40 | #include <assert.h> | |
984263bc MD |
41 | #include <stdio.h> |
42 | #include <stdlib.h> | |
43 | #include <string.h> | |
16db8bac | 44 | #include <wchar.h> |
984263bc MD |
45 | #include <errno.h> |
46 | #include <unistd.h> | |
16db8bac | 47 | #include <fcntl.h> |
17ea2221 | 48 | #include "un-namespace.h" |
984263bc MD |
49 | |
50 | #include "collate.h" | |
0d5acd74 JM |
51 | #include "setlocale.h" |
52 | #include "ldpart.h" | |
8d7b8f97 | 53 | #include "libc_private.h" |
0d5acd74 | 54 | |
0d5acd74 | 55 | struct xlocale_collate __xlocale_global_collate = { |
6173c3df SW |
56 | .header = {{0}, "C"}, |
57 | .__collate_load_error = 1 | |
0d5acd74 | 58 | }; |
984263bc | 59 | |
16db8bac | 60 | struct xlocale_collate __xlocale_C_collate = { |
6173c3df SW |
61 | .header = {{0}, "C"}, |
62 | .__collate_load_error = 1 | |
0d5acd74 | 63 | }; |
984263bc | 64 | |
8d7b8f97 | 65 | static int |
0d5acd74 JM |
66 | __collate_load_tables_l(const char *encoding, struct xlocale_collate *table); |
67 | ||
68 | static void | |
69 | destruct_collate(void *t) | |
70 | { | |
71 | struct xlocale_collate *table = t; | |
16db8bac JM |
72 | if (table->map && (table->maplen > 0)) { |
73 | (void) munmap(table->map, table->maplen); | |
0d5acd74 JM |
74 | } |
75 | free(t); | |
76 | } | |
77 | ||
78 | void * | |
de950ab3 | 79 | __collate_load(const char *encoding, __unused locale_t unused) |
0d5acd74 JM |
80 | { |
81 | if (strcmp(encoding, "C") == 0 || strcmp(encoding, "POSIX") == 0) { | |
82 | return &__xlocale_C_collate; | |
83 | } | |
84 | struct xlocale_collate *table = calloc(sizeof(struct xlocale_collate), 1); | |
85 | table->header.header.destructor = destruct_collate; | |
86 | // FIXME: Make sure that _LDP_CACHE is never returned. We should be doing | |
87 | // the caching outside of this section | |
88 | if (__collate_load_tables_l(encoding, table) != _LDP_LOADED) { | |
89 | xlocale_release(table); | |
90 | return NULL; | |
91 | } | |
92 | return table; | |
93 | } | |
94 | ||
95 | /** | |
96 | * Load the collation tables for the specified encoding into the global table. | |
97 | */ | |
984263bc MD |
98 | int |
99 | __collate_load_tables(const char *encoding) | |
0d5acd74 | 100 | { |
44783347 | 101 | return (__collate_load_tables_l(encoding, &__xlocale_global_collate)); |
0d5acd74 JM |
102 | } |
103 | ||
104 | int | |
105 | __collate_load_tables_l(const char *encoding, struct xlocale_collate *table) | |
984263bc | 106 | { |
16db8bac JM |
107 | int i, chains, z; |
108 | char buf[PATH_MAX]; | |
109 | char *TMP; | |
110 | char *map; | |
111 | collate_info_t *info; | |
112 | struct stat sbuf; | |
113 | int fd; | |
984263bc | 114 | |
58fddedb JM |
115 | table->__collate_load_error = 1; |
116 | ||
984263bc MD |
117 | /* 'encoding' must be already checked. */ |
118 | if (strcmp(encoding, "C") == 0 || strcmp(encoding, "POSIX") == 0) { | |
984263bc MD |
119 | return (_LDP_CACHE); |
120 | } | |
121 | ||
16db8bac JM |
122 | (void) snprintf(buf, sizeof (buf), "%s/%s/LC_COLLATE", |
123 | _PathLocale, encoding); | |
984263bc | 124 | |
16db8bac | 125 | if ((fd = _open(buf, O_RDONLY)) < 0) |
984263bc | 126 | return (_LDP_ERROR); |
16db8bac JM |
127 | if (_fstat(fd, &sbuf) < 0) { |
128 | (void) _close(fd); | |
984263bc MD |
129 | return (_LDP_ERROR); |
130 | } | |
16db8bac JM |
131 | if (sbuf.st_size < (COLLATE_STR_LEN + sizeof (info))) { |
132 | (void) _close(fd); | |
133 | errno = EINVAL; | |
984263bc MD |
134 | return (_LDP_ERROR); |
135 | } | |
16db8bac JM |
136 | map = mmap(NULL, sbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0); |
137 | (void) _close(fd); | |
138 | if ((TMP = map) == NULL) { | |
984263bc MD |
139 | return (_LDP_ERROR); |
140 | } | |
16db8bac JM |
141 | |
142 | if (strncmp(TMP, COLLATE_VERSION, COLLATE_STR_LEN) != 0) { | |
143 | (void) munmap(map, sbuf.st_size); | |
144 | errno = EINVAL; | |
984263bc MD |
145 | return (_LDP_ERROR); |
146 | } | |
16db8bac | 147 | TMP += COLLATE_STR_LEN; |
984263bc | 148 | |
16db8bac JM |
149 | info = (void *)TMP; |
150 | TMP += sizeof (*info); | |
984263bc | 151 | |
16db8bac JM |
152 | if ((info->directive_count < 1) || |
153 | (info->directive_count >= COLL_WEIGHTS_MAX) || | |
154 | ((chains = info->chain_count) < 0)) { | |
155 | (void) munmap(map, sbuf.st_size); | |
156 | errno = EINVAL; | |
157 | return (_LDP_ERROR); | |
0d5acd74 | 158 | } |
16db8bac JM |
159 | |
160 | i = (sizeof (collate_char_t) * (UCHAR_MAX + 1)) + | |
161 | (sizeof (collate_chain_t) * chains) + | |
162 | (sizeof (collate_large_t) * info->large_count); | |
58fddedb | 163 | for (z = 0; z < info->directive_count; z++) { |
16db8bac JM |
164 | i += sizeof (collate_subst_t) * info->subst_count[z]; |
165 | } | |
166 | if (i != (sbuf.st_size - (TMP - map))) { | |
167 | (void) munmap(map, sbuf.st_size); | |
168 | errno = EINVAL; | |
169 | return (_LDP_ERROR); | |
0d5acd74 | 170 | } |
16db8bac | 171 | |
58fddedb | 172 | table->info = info; |
16db8bac JM |
173 | table->char_pri_table = (void *)TMP; |
174 | TMP += sizeof (collate_char_t) * (UCHAR_MAX + 1); | |
175 | ||
176 | for (z = 0; z < info->directive_count; z++) { | |
177 | if (info->subst_count[z] > 0) { | |
178 | table->subst_table[z] = (void *)TMP; | |
179 | TMP += info->subst_count[z] * sizeof (collate_subst_t); | |
180 | } else { | |
181 | table->subst_table[z] = NULL; | |
984263bc MD |
182 | } |
183 | } | |
16db8bac JM |
184 | |
185 | if (chains > 0) { | |
186 | table->chain_pri_table = (void *)TMP; | |
187 | TMP += chains * sizeof (collate_chain_t); | |
188 | } else | |
189 | table->chain_pri_table = NULL; | |
190 | if (info->large_count > 0) | |
191 | table->large_pri_table = (void *)TMP; | |
192 | else | |
193 | table->large_pri_table = NULL; | |
194 | ||
0d5acd74 | 195 | table->__collate_load_error = 0; |
984263bc MD |
196 | return (_LDP_LOADED); |
197 | } | |
198 | ||
58fddedb | 199 | static const int32_t * |
16db8bac JM |
200 | substsearch(struct xlocale_collate *table, const wchar_t key, int pass) |
201 | { | |
58fddedb | 202 | const collate_subst_t *p; |
16db8bac JM |
203 | int n = table->info->subst_count[pass]; |
204 | ||
205 | if (n == 0) | |
206 | return (NULL); | |
207 | ||
208 | if (pass >= table->info->directive_count) | |
209 | return (NULL); | |
210 | ||
211 | if (!(key & COLLATE_SUBST_PRIORITY)) | |
212 | return (NULL); | |
213 | ||
214 | p = table->subst_table[pass] + (key & ~COLLATE_SUBST_PRIORITY); | |
58fddedb | 215 | assert(p->key == key); |
16db8bac JM |
216 | return (p->pri); |
217 | } | |
218 | ||
219 | static collate_chain_t * | |
220 | chainsearch(struct xlocale_collate *table, const wchar_t *key, int *len) | |
984263bc | 221 | { |
d705af0f JM |
222 | int low = 0; |
223 | int high = table->info->chain_count - 1;; | |
16db8bac JM |
224 | int next, compar, l; |
225 | collate_chain_t *p; | |
d705af0f | 226 | collate_chain_t *tab = table->chain_pri_table; |
16db8bac | 227 | |
d705af0f | 228 | if (high < 0) |
16db8bac JM |
229 | return (NULL); |
230 | ||
16db8bac JM |
231 | while (low <= high) { |
232 | next = (low + high) / 2; | |
233 | p = tab + next; | |
234 | compar = *key - *p->str; | |
235 | if (compar == 0) { | |
236 | l = wcsnlen(p->str, COLLATE_STR_LEN); | |
237 | compar = wcsncmp(key, p->str, l); | |
238 | if (compar == 0) { | |
239 | *len = l; | |
240 | return (p); | |
241 | } | |
984263bc | 242 | } |
16db8bac JM |
243 | if (compar > 0) |
244 | low = next + 1; | |
245 | else | |
246 | high = next - 1; | |
984263bc | 247 | } |
16db8bac JM |
248 | return (NULL); |
249 | } | |
250 | ||
251 | static collate_large_t * | |
252 | largesearch(struct xlocale_collate *table, const wchar_t key) | |
253 | { | |
254 | int low = 0; | |
255 | int high = table->info->large_count - 1; | |
256 | int next, compar; | |
257 | collate_large_t *p; | |
258 | collate_large_t *tab = table->large_pri_table; | |
259 | ||
d705af0f | 260 | if (high < 0) |
16db8bac JM |
261 | return (NULL); |
262 | ||
263 | while (low <= high) { | |
264 | next = (low + high) / 2; | |
265 | p = tab + next; | |
266 | compar = key - p->val; | |
267 | if (compar == 0) | |
268 | return (p); | |
269 | if (compar > 0) | |
270 | low = next + 1; | |
271 | else | |
272 | high = next - 1; | |
273 | } | |
274 | return (NULL); | |
984263bc MD |
275 | } |
276 | ||
277 | void | |
16db8bac JM |
278 | _collate_lookup(struct xlocale_collate *table, const wchar_t *t, int *len, |
279 | int *pri, int which, const int **state) | |
984263bc | 280 | { |
16db8bac JM |
281 | collate_chain_t *p2; |
282 | collate_large_t *match; | |
283 | int p, l; | |
284 | const int *sptr; | |
285 | ||
286 | /* | |
287 | * If this is the "last" pass for the UNDEFINED, then | |
288 | * we just return the priority itself. | |
289 | */ | |
290 | if (which >= table->info->directive_count) { | |
291 | *pri = *t; | |
292 | *len = 1; | |
293 | *state = NULL; | |
294 | return; | |
295 | } | |
984263bc | 296 | |
16db8bac JM |
297 | /* |
298 | * If we have remaining substitution data from a previous | |
299 | * call, consume it first. | |
300 | */ | |
301 | if ((sptr = *state) != NULL) { | |
302 | *pri = *sptr; | |
303 | sptr++; | |
62a9429e | 304 | if ((sptr == *state) || (sptr == NULL)) |
d705af0f JM |
305 | *state = NULL; |
306 | else | |
307 | *state = sptr; | |
16db8bac JM |
308 | *len = 0; |
309 | return; | |
310 | } | |
311 | ||
312 | /* No active substitutions */ | |
984263bc | 313 | *len = 1; |
16db8bac JM |
314 | |
315 | /* | |
8d7b8f97 | 316 | * Check for composites such as diphthongs that collate as a |
16db8bac JM |
317 | * single element (aka chains or collating-elements). |
318 | */ | |
319 | if (((p2 = chainsearch(table, t, &l)) != NULL) && | |
320 | ((p = p2->pri[which]) >= 0)) { | |
321 | ||
322 | *len = l; | |
323 | *pri = p; | |
324 | ||
325 | } else if (*t <= UCHAR_MAX) { | |
326 | ||
327 | /* | |
328 | * Character is a small (8-bit) character. | |
329 | * We just look these up directly for speed. | |
330 | */ | |
331 | *pri = table->char_pri_table[*t].pri[which]; | |
332 | ||
333 | } else if ((table->info->large_count > 0) && | |
334 | ((match = largesearch(table, *t)) != NULL)) { | |
335 | ||
336 | /* | |
337 | * Character was found in the extended table. | |
338 | */ | |
339 | *pri = match->pri.pri[which]; | |
340 | ||
341 | } else { | |
342 | /* | |
343 | * Character lacks a specific definition. | |
344 | */ | |
345 | if (table->info->directive[which] & DIRECTIVE_UNDEFINED) { | |
346 | /* Mask off sign bit to prevent ordering confusion. */ | |
347 | *pri = (*t & COLLATE_MAX_PRIORITY); | |
348 | } else { | |
349 | *pri = table->info->undef_pri[which]; | |
350 | } | |
351 | /* No substitutions for undefined characters! */ | |
352 | return; | |
353 | } | |
354 | ||
355 | /* | |
356 | * Try substituting (expanding) the character. We are | |
357 | * currently doing this *after* the chain compression. I | |
358 | * think it should not matter, but this way might be slightly | |
359 | * faster. | |
360 | * | |
361 | * We do this after the priority search, as this will help us | |
362 | * to identify a single key value. In order for this to work, | |
363 | * its important that the priority assigned to a given element | |
364 | * to be substituted be unique for that level. The localedef | |
365 | * code ensures this for us. | |
366 | */ | |
367 | if ((sptr = substsearch(table, *pri, which)) != NULL) { | |
d705af0f | 368 | if ((*pri = *sptr) > 0) { |
16db8bac JM |
369 | sptr++; |
370 | *state = *sptr ? sptr : NULL; | |
984263bc MD |
371 | } |
372 | } | |
16db8bac | 373 | |
984263bc MD |
374 | } |
375 | ||
16db8bac JM |
376 | /* |
377 | * This is the meaty part of wcsxfrm & strxfrm. Note that it does | |
378 | * NOT NULL terminate. That is left to the caller. | |
379 | */ | |
380 | size_t | |
381 | _collate_wxfrm(struct xlocale_collate *table, const wchar_t *src, wchar_t *xf, | |
382 | size_t room) | |
984263bc | 383 | { |
16db8bac JM |
384 | int pri; |
385 | int len; | |
386 | const wchar_t *t; | |
387 | wchar_t *tr = NULL; | |
388 | int direc; | |
389 | int pass; | |
390 | const int32_t *state; | |
391 | size_t want = 0; | |
392 | size_t need = 0; | |
58fddedb | 393 | int ndir = table->info->directive_count; |
16db8bac | 394 | |
58fddedb JM |
395 | assert(src); |
396 | ||
397 | for (pass = 0; pass <= ndir; pass++) { | |
16db8bac JM |
398 | |
399 | state = NULL; | |
400 | ||
401 | if (pass != 0) { | |
402 | /* insert level separator from the previous pass */ | |
403 | if (room) { | |
404 | *xf++ = 1; | |
405 | room--; | |
406 | } | |
407 | want++; | |
408 | } | |
409 | ||
410 | /* special pass for undefined */ | |
58fddedb | 411 | if (pass == ndir) { |
16db8bac JM |
412 | direc = DIRECTIVE_FORWARD | DIRECTIVE_UNDEFINED; |
413 | } else { | |
414 | direc = table->info->directive[pass]; | |
415 | } | |
416 | ||
417 | t = src; | |
418 | ||
419 | if (direc & DIRECTIVE_BACKWARD) { | |
420 | wchar_t *bp, *fp, c; | |
421 | if (tr) | |
422 | free(tr); | |
423 | if ((tr = wcsdup(t)) == NULL) { | |
424 | errno = ENOMEM; | |
425 | goto fail; | |
426 | } | |
427 | bp = tr; | |
428 | fp = tr + wcslen(tr) - 1; | |
429 | while (bp < fp) { | |
430 | c = *bp; | |
431 | *bp++ = *fp; | |
432 | *fp-- = c; | |
433 | } | |
434 | t = (const wchar_t *)tr; | |
435 | } | |
984263bc | 436 | |
16db8bac JM |
437 | if (direc & DIRECTIVE_POSITION) { |
438 | while (*t || state) { | |
439 | _collate_lookup(table, t, &len, &pri, pass, &state); | |
440 | t += len; | |
441 | if (pri <= 0) { | |
442 | if (pri < 0) { | |
443 | errno = EINVAL; | |
444 | goto fail; | |
445 | } | |
62a9429e | 446 | state = NULL; |
16db8bac JM |
447 | pri = COLLATE_MAX_PRIORITY; |
448 | } | |
449 | if (room) { | |
450 | *xf++ = pri; | |
451 | room--; | |
452 | } | |
453 | want++; | |
454 | need = want; | |
455 | } | |
456 | } else { | |
457 | while (*t || state) { | |
458 | _collate_lookup(table, t, &len, &pri, pass, &state); | |
459 | t += len; | |
460 | if (pri <= 0) { | |
461 | if (pri < 0) { | |
462 | errno = EINVAL; | |
463 | goto fail; | |
464 | } | |
62a9429e | 465 | state = NULL; |
16db8bac JM |
466 | continue; |
467 | } | |
468 | if (room) { | |
469 | *xf++ = pri; | |
470 | room--; | |
471 | } | |
472 | want++; | |
473 | need = want; | |
474 | } | |
475 | } | |
476 | } | |
58fddedb | 477 | free(tr); |
16db8bac JM |
478 | return (need); |
479 | ||
480 | fail: | |
58fddedb | 481 | free(tr); |
16db8bac | 482 | return ((size_t)(-1)); |
984263bc MD |
483 | } |
484 | ||
16db8bac JM |
485 | /* |
486 | * In the non-POSIX case, we transform each character into a string of | |
487 | * characters representing the character's priority. Since char is usually | |
488 | * signed, we are limited by 7 bits per byte. To avoid zero, we need to add | |
489 | * XFRM_OFFSET, so we can't use a full 7 bits. For simplicity, we choose 6 | |
490 | * bits per byte. | |
491 | * | |
492 | * It turns out that we sometimes have real priorities that are | |
493 | * 31-bits wide. (But: be careful using priorities where the high | |
494 | * order bit is set -- i.e. the priority is negative. The sort order | |
495 | * may be surprising!) | |
496 | * | |
497 | * TODO: This would be a good area to optimize somewhat. It turns out | |
498 | * that real prioririties *except for the last UNDEFINED pass* are generally | |
499 | * very small. We need the localedef code to precalculate the max | |
500 | * priority for us, and ideally also give us a mask, and then we could | |
501 | * severely limit what we expand to. | |
502 | */ | |
503 | #define XFRM_BYTES 6 | |
504 | #define XFRM_OFFSET ('0') /* make all printable characters */ | |
505 | #define XFRM_SHIFT 6 | |
506 | #define XFRM_MASK ((1 << XFRM_SHIFT) - 1) | |
507 | #define XFRM_SEP ('.') /* chosen to be less than XFRM_OFFSET */ | |
508 | ||
509 | static int | |
510 | xfrm(struct xlocale_collate *table, unsigned char *p, int pri, int pass) | |
984263bc | 511 | { |
16db8bac JM |
512 | /* we use unsigned to ensure zero fill on right shift */ |
513 | uint32_t val = (uint32_t)table->info->pri_count[pass]; | |
514 | int nc = 0; | |
515 | ||
516 | while (val) { | |
517 | *p = (pri & XFRM_MASK) + XFRM_OFFSET; | |
518 | pri >>= XFRM_SHIFT; | |
519 | val >>= XFRM_SHIFT; | |
520 | p++; | |
521 | nc++; | |
522 | } | |
523 | return (nc); | |
984263bc MD |
524 | } |
525 | ||
16db8bac JM |
526 | size_t |
527 | _collate_sxfrm(struct xlocale_collate *table, const wchar_t *src, char *xf, | |
528 | size_t room) | |
984263bc | 529 | { |
16db8bac JM |
530 | int pri; |
531 | int len; | |
532 | const wchar_t *t; | |
533 | wchar_t *tr = NULL; | |
534 | int direc; | |
535 | int pass; | |
536 | const int32_t *state; | |
537 | size_t want = 0; | |
538 | size_t need = 0; | |
539 | int b; | |
540 | uint8_t buf[XFRM_BYTES]; | |
58fddedb JM |
541 | int ndir = table->info->directive_count; |
542 | ||
543 | assert(src); | |
16db8bac | 544 | |
58fddedb | 545 | for (pass = 0; pass <= ndir; pass++) { |
16db8bac JM |
546 | |
547 | state = NULL; | |
548 | ||
549 | if (pass != 0) { | |
550 | /* insert level separator from the previous pass */ | |
551 | if (room) { | |
552 | *xf++ = XFRM_SEP; | |
553 | room--; | |
554 | } | |
555 | want++; | |
556 | } | |
557 | ||
558 | /* special pass for undefined */ | |
58fddedb | 559 | if (pass == ndir) { |
16db8bac JM |
560 | direc = DIRECTIVE_FORWARD | DIRECTIVE_UNDEFINED; |
561 | } else { | |
562 | direc = table->info->directive[pass]; | |
563 | } | |
564 | ||
565 | t = src; | |
566 | ||
567 | if (direc & DIRECTIVE_BACKWARD) { | |
568 | wchar_t *bp, *fp, c; | |
569 | if (tr) | |
570 | free(tr); | |
571 | if ((tr = wcsdup(t)) == NULL) { | |
572 | errno = ENOMEM; | |
573 | goto fail; | |
574 | } | |
575 | bp = tr; | |
576 | fp = tr + wcslen(tr) - 1; | |
577 | while (bp < fp) { | |
578 | c = *bp; | |
579 | *bp++ = *fp; | |
580 | *fp-- = c; | |
581 | } | |
582 | t = (const wchar_t *)tr; | |
583 | } | |
584 | ||
585 | if (direc & DIRECTIVE_POSITION) { | |
586 | while (*t || state) { | |
587 | ||
588 | _collate_lookup(table, t, &len, &pri, pass, &state); | |
589 | t += len; | |
590 | if (pri <= 0) { | |
591 | if (pri < 0) { | |
592 | errno = EINVAL; | |
593 | goto fail; | |
594 | } | |
62a9429e | 595 | state = NULL; |
16db8bac JM |
596 | pri = COLLATE_MAX_PRIORITY; |
597 | } | |
598 | ||
599 | b = xfrm(table, buf, pri, pass); | |
600 | want += b; | |
601 | if (room) { | |
602 | while (b) { | |
603 | b--; | |
604 | if (room) { | |
605 | *xf++ = buf[b]; | |
606 | room--; | |
607 | } | |
608 | } | |
609 | } | |
610 | need = want; | |
611 | } | |
612 | } else { | |
613 | while (*t || state) { | |
614 | _collate_lookup(table, t, &len, &pri, pass, &state); | |
615 | t += len; | |
616 | if (pri <= 0) { | |
617 | if (pri < 0) { | |
618 | errno = EINVAL; | |
619 | goto fail; | |
620 | } | |
62a9429e | 621 | state = NULL; |
16db8bac JM |
622 | continue; |
623 | } | |
624 | ||
625 | b = xfrm(table, buf, pri, pass); | |
626 | want += b; | |
627 | if (room) { | |
628 | ||
629 | while (b) { | |
630 | b--; | |
631 | if (room) { | |
632 | *xf++ = buf[b]; | |
633 | room--; | |
634 | } | |
635 | } | |
636 | } | |
637 | need = want; | |
638 | } | |
639 | } | |
640 | } | |
58fddedb | 641 | free(tr); |
16db8bac JM |
642 | return (need); |
643 | ||
644 | fail: | |
58fddedb | 645 | free(tr); |
16db8bac | 646 | return ((size_t)(-1)); |
984263bc | 647 | } |
903faafa JM |
648 | |
649 | /* | |
650 | * __collate_equiv_value returns the primary collation value for the given | |
651 | * collating symbol specified by str and len. Zero or negative is returned | |
652 | * if the collating symbol was not found. This function is used by bracket | |
653 | * code in the TRE regex library. | |
654 | */ | |
655 | int | |
656 | __collate_equiv_value(locale_t locale, const wchar_t *str, size_t len) | |
657 | { | |
658 | int32_t e; | |
659 | ||
660 | if (len < 1 || len >= COLLATE_STR_LEN) | |
661 | return (-1); | |
662 | ||
663 | FIX_LOCALE(locale); | |
664 | struct xlocale_collate *table = | |
665 | (struct xlocale_collate*)locale->components[XLC_COLLATE]; | |
666 | ||
667 | if (table->__collate_load_error) | |
668 | return ((len == 1 && *str <= UCHAR_MAX) ? *str : -1); | |
669 | ||
670 | if (len == 1) { | |
671 | e = -1; | |
672 | if (*str <= UCHAR_MAX) | |
673 | e = table->char_pri_table[*str].pri[0]; | |
674 | else if (table->info->large_count > 0) { | |
675 | collate_large_t *match_large; | |
676 | match_large = largesearch(table, *str); | |
677 | if (match_large) | |
678 | e = match_large->pri.pri[0]; | |
679 | } | |
680 | if (e == 0) | |
681 | return (1); | |
682 | return (e > 0 ? e : 0); | |
683 | } | |
684 | if (table->info->chain_count > 0) { | |
685 | wchar_t name[COLLATE_STR_LEN]; | |
686 | collate_chain_t *match_chain; | |
687 | int clen; | |
688 | ||
689 | wcsncpy (name, str, len); | |
690 | name[len] = 0; | |
691 | match_chain = chainsearch(table, name, &clen); | |
692 | if (match_chain) { | |
693 | e = match_chain->pri[0]; | |
694 | if (e == 0) | |
695 | return (1); | |
696 | return (e < 0 ? -e : e); | |
697 | } | |
698 | } | |
699 | return (0); | |
700 | } |