]> sourceware.org Git - glibc.git/blame - iconv/gconv_db.c
syslog: Fix integer overflow in __vsyslog_internal (CVE-2023-6780)
[glibc.git] / iconv / gconv_db.c
CommitLineData
6973fc01 1/* Provide access to the collection of available transformation modules.
dff8da6b 2 Copyright (C) 1997-2024 Free Software Foundation, Inc.
6973fc01 3 This file is part of the GNU C Library.
6973fc01
UD
4
5 The GNU C Library is free software; you can redistribute it and/or
41bdb6e2
AJ
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
6973fc01
UD
9
10 The GNU C Library 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 GNU
41bdb6e2 13 Lesser General Public License for more details.
6973fc01 14
41bdb6e2 15 You should have received a copy of the GNU Lesser General Public
59ba27a6 16 License along with the GNU C Library; if not, see
5a82c748 17 <https://www.gnu.org/licenses/>. */
6973fc01 18
915a6c51 19#include <assert.h>
2bd60880 20#include <limits.h>
6973fc01
UD
21#include <search.h>
22#include <stdlib.h>
23#include <string.h>
2bd60880 24#include <sys/param.h>
ec999b8e 25#include <libc-lock.h>
7c11c4a1 26#include <locale/localeinfo.h>
816e6eb5 27
b3fc5f84 28#include <dlfcn.h>
e62c19f1 29#include <gconv_int.h>
88f4b692 30#include <pointer_guard.h>
e62c19f1 31
6973fc01
UD
32
33/* Simple data structure for alias mapping. We have two names, `from'
34 and `to'. */
35void *__gconv_alias_db;
36
37/* Array with available modules. */
2bd60880 38struct gconv_module *__gconv_modules_db;
6973fc01 39
0d9f6793 40/* We modify global data. */
9e26f129 41__libc_lock_define_initialized (, __gconv_lock)
0d9f6793 42
6973fc01 43
230491f0
UD
44/* Provide access to module database. */
45struct gconv_module *
46__gconv_get_modules_db (void)
47{
48 return __gconv_modules_db;
49}
50
51void *
52__gconv_get_alias_db (void)
53{
54 return __gconv_alias_db;
55}
56
57
6973fc01
UD
58/* Function for searching alias. */
59int
60__gconv_alias_compare (const void *p1, const void *p2)
61{
17427edd
UD
62 const struct gconv_alias *s1 = (const struct gconv_alias *) p1;
63 const struct gconv_alias *s2 = (const struct gconv_alias *) p2;
bd4848fb 64 return strcmp (s1->fromname, s2->fromname);
6973fc01
UD
65}
66
67
68/* To search for a derivation we create a list of intermediate steps.
69 Each element contains a pointer to the element which precedes it
70 in the derivation order. */
71struct derivation_step
72{
73 const char *result_set;
2bd60880
UD
74 size_t result_set_len;
75 int cost_lo;
76 int cost_hi;
6973fc01
UD
77 struct gconv_module *code;
78 struct derivation_step *last;
79 struct derivation_step *next;
80};
81
2bd60880 82#define NEW_STEP(result, hi, lo, module, last_mod) \
6973fc01
UD
83 ({ struct derivation_step *newp = alloca (sizeof (struct derivation_step)); \
84 newp->result_set = result; \
2bd60880
UD
85 newp->result_set_len = strlen (result); \
86 newp->cost_hi = hi; \
87 newp->cost_lo = lo; \
6973fc01
UD
88 newp->code = module; \
89 newp->last = last_mod; \
90 newp->next = NULL; \
91 newp; })
92
93
94/* If a specific transformation is used more than once we should not need
95 to start looking for it again. Instead cache each successful result. */
96struct known_derivation
97{
98 const char *from;
99 const char *to;
d64b6ad0 100 struct __gconv_step *steps;
6973fc01
UD
101 size_t nsteps;
102};
103
104/* Compare function for database of found derivations. */
105static int
106derivation_compare (const void *p1, const void *p2)
107{
17427edd
UD
108 const struct known_derivation *s1 = (const struct known_derivation *) p1;
109 const struct known_derivation *s2 = (const struct known_derivation *) p2;
6973fc01
UD
110 int result;
111
112 result = strcmp (s1->from, s2->from);
113 if (result == 0)
114 result = strcmp (s1->to, s2->to);
115 return result;
116}
117
118/* The search tree for known derivations. */
119static void *known_derivations;
120
121/* Look up whether given transformation was already requested before. */
122static int
123derivation_lookup (const char *fromset, const char *toset,
d64b6ad0 124 struct __gconv_step **handle, size_t *nsteps)
6973fc01
UD
125{
126 struct known_derivation key = { fromset, toset, NULL, 0 };
bd687f7a 127 struct known_derivation **result;
6973fc01 128
e34b0f29 129 result = __tfind (&key, &known_derivations, derivation_compare);
6973fc01
UD
130
131 if (result == NULL)
d64b6ad0 132 return __GCONV_NOCONV;
6973fc01 133
bd687f7a
UD
134 *handle = (*result)->steps;
135 *nsteps = (*result)->nsteps;
6973fc01
UD
136
137 /* Please note that we return GCONV_OK even if the last search for
138 this transformation was unsuccessful. */
d64b6ad0 139 return __GCONV_OK;
6973fc01
UD
140}
141
142/* Add new derivation to list of known ones. */
143static void
144add_derivation (const char *fromset, const char *toset,
d64b6ad0 145 struct __gconv_step *handle, size_t nsteps)
6973fc01
UD
146{
147 struct known_derivation *new_deriv;
148 size_t fromset_len = strlen (fromset) + 1;
149 size_t toset_len = strlen (toset) + 1;
150
151 new_deriv = (struct known_derivation *)
152 malloc (sizeof (struct known_derivation) + fromset_len + toset_len);
153 if (new_deriv != NULL)
154 {
390500b1
UD
155 new_deriv->from = (char *) (new_deriv + 1);
156 new_deriv->to = memcpy (__mempcpy (new_deriv + 1, fromset, fromset_len),
6973fc01
UD
157 toset, toset_len);
158
159 new_deriv->steps = handle;
160 new_deriv->nsteps = nsteps;
161
390500b1
UD
162 if (__tsearch (new_deriv, &known_derivations, derivation_compare)
163 == NULL)
164 /* There is some kind of memory allocation problem. */
165 free (new_deriv);
6973fc01
UD
166 }
167 /* Please note that we don't complain if the allocation failed. This
168 is not tragically but in case we use the memory debugging facilities
169 not all memory will be freed. */
170}
171
88677348 172static void
6973fc01
UD
173free_derivation (void *p)
174{
175 struct known_derivation *deriv = (struct known_derivation *) p;
0d9f6793
UD
176 size_t cnt;
177
178 for (cnt = 0; cnt < deriv->nsteps; ++cnt)
e7c18b9d
FW
179 if (deriv->steps[cnt].__counter > 0
180 && deriv->steps[cnt].__shlib_handle != NULL)
915a6c51 181 {
915a6c51 182 __gconv_end_fct end_fct = deriv->steps[cnt].__end_fct;
915a6c51 183 PTR_DEMANGLE (end_fct);
1cf1232c
PF
184 if (end_fct != NULL)
185 DL_CALL_FCT (end_fct, (&deriv->steps[cnt]));
915a6c51 186 }
6973fc01 187
74454183 188 /* Free the name strings. */
de2139a9
L
189 if (deriv->steps != NULL)
190 {
191 free ((char *) deriv->steps[0].__from_name);
192 free ((char *) deriv->steps[deriv->nsteps - 1].__to_name);
193 free ((struct __gconv_step *) deriv->steps);
194 }
74454183 195
6973fc01
UD
196 free (deriv);
197}
198
199
b79f74cd 200/* Decrement the reference count for a single step in a steps array. */
6b98979f 201void
6b98979f 202__gconv_release_step (struct __gconv_step *step)
b79f74cd 203{
915a6c51
UD
204 /* Skip builtin modules; they are not reference counted. */
205 if (step->__shlib_handle != NULL && --step->__counter == 0)
b79f74cd
UD
206 {
207 /* Call the destructor. */
1cf1232c 208 __gconv_end_fct end_fct = step->__end_fct;
1cf1232c 209 PTR_DEMANGLE (end_fct);
1cf1232c
PF
210 if (end_fct != NULL)
211 DL_CALL_FCT (end_fct, (step));
915a6c51
UD
212
213#ifndef STATIC_GCONV
214 /* Release the loaded module. */
215 __gconv_release_shlib (step->__shlib_handle);
216 step->__shlib_handle = NULL;
b79f74cd
UD
217#endif
218 }
74ed1159 219 else if (step->__shlib_handle == NULL)
915a6c51
UD
220 /* Builtin modules should not have end functions. */
221 assert (step->__end_fct == NULL);
b79f74cd
UD
222}
223
6973fc01 224static int
6973fc01 225gen_steps (struct derivation_step *best, const char *toset,
d64b6ad0 226 const char *fromset, struct __gconv_step **handle, size_t *nsteps)
6973fc01
UD
227{
228 size_t step_cnt = 0;
d64b6ad0 229 struct __gconv_step *result;
6973fc01 230 struct derivation_step *current;
d64b6ad0 231 int status = __GCONV_NOMEM;
be64c2ef
PP
232 char *from_name = NULL;
233 char *to_name = NULL;
6973fc01
UD
234
235 /* First determine number of steps. */
236 for (current = best; current->last != NULL; current = current->last)
237 ++step_cnt;
238
d64b6ad0
UD
239 result = (struct __gconv_step *) malloc (sizeof (struct __gconv_step)
240 * step_cnt);
6973fc01
UD
241 if (result != NULL)
242 {
e34b0f29
UD
243 int failed = 0;
244
d64b6ad0 245 status = __GCONV_OK;
e34b0f29 246 *nsteps = step_cnt;
6973fc01
UD
247 current = best;
248 while (step_cnt-- > 0)
249 {
be64c2ef
PP
250 if (step_cnt == 0)
251 {
252 result[step_cnt].__from_name = from_name = __strdup (fromset);
253 if (from_name == NULL)
254 {
255 failed = 1;
256 break;
257 }
258 }
259 else
260 result[step_cnt].__from_name = (char *)current->last->result_set;
261
262 if (step_cnt + 1 == *nsteps)
263 {
264 result[step_cnt].__to_name = to_name
265 = __strdup (current->result_set);
266 if (to_name == NULL)
267 {
268 failed = 1;
269 break;
270 }
271 }
272 else
273 result[step_cnt].__to_name = result[step_cnt + 1].__from_name;
6973fc01 274
6b98979f
UD
275 result[step_cnt].__counter = 1;
276 result[step_cnt].__data = NULL;
277
8619129f 278#ifndef STATIC_GCONV
6973fc01
UD
279 if (current->code->module_name[0] == '/')
280 {
281 /* Load the module, return handle for it. */
d64b6ad0 282 struct __gconv_loaded_object *shlib_handle =
6973fc01
UD
283 __gconv_find_shlib (current->code->module_name);
284
285 if (shlib_handle == NULL)
e34b0f29
UD
286 {
287 failed = 1;
288 break;
289 }
6973fc01 290
d64b6ad0
UD
291 result[step_cnt].__shlib_handle = shlib_handle;
292 result[step_cnt].__modname = shlib_handle->name;
d64b6ad0
UD
293 result[step_cnt].__fct = shlib_handle->fct;
294 result[step_cnt].__init_fct = shlib_handle->init_fct;
295 result[step_cnt].__end_fct = shlib_handle->end_fct;
6b98979f 296
f9ad060c
UD
297 /* These settings can be overridden by the init function. */
298 result[step_cnt].__btowc_fct = NULL;
299
6b98979f 300 /* Call the init function. */
915a6c51 301 __gconv_init_fct init_fct = result[step_cnt].__init_fct;
1cf1232c 302 PTR_DEMANGLE (init_fct);
1cf1232c
PF
303 if (init_fct != NULL)
304 {
915a6c51 305 status = DL_CALL_FCT (init_fct, (&result[step_cnt]));
6b98979f
UD
306
307 if (__builtin_expect (status, __GCONV_OK) != __GCONV_OK)
308 {
309 failed = 1;
251bccfa
FW
310 /* Do not call the end function because the init
311 function has failed. */
6b98979f 312 result[step_cnt].__end_fct = NULL;
251bccfa 313 PTR_MANGLE (result[step_cnt].__end_fct);
251bccfa
FW
314 /* Make sure we unload this module. */
315 --step_cnt;
6b98979f
UD
316 break;
317 }
e7c18b9d 318 }
e7c18b9d 319 PTR_MANGLE (result[step_cnt].__btowc_fct);
6973fc01
UD
320 }
321 else
8619129f 322#endif
6973fc01
UD
323 /* It's a builtin transformation. */
324 __gconv_get_builtin_trans (current->code->module_name,
325 &result[step_cnt]);
326
327 current = current->last;
328 }
329
5ea1a82d 330 if (__builtin_expect (failed, 0) != 0)
6973fc01
UD
331 {
332 /* Something went wrong while initializing the modules. */
e34b0f29 333 while (++step_cnt < *nsteps)
6b98979f 334 __gconv_release_step (&result[step_cnt]);
6973fc01 335 free (result);
be64c2ef
PP
336 free (from_name);
337 free (to_name);
e34b0f29 338 *nsteps = 0;
7a68c94a 339 *handle = NULL;
d64b6ad0
UD
340 if (status == __GCONV_OK)
341 status = __GCONV_NOCONV;
6973fc01
UD
342 }
343 else
c7ec9d75 344 *handle = result;
6973fc01 345 }
7a68c94a
UD
346 else
347 {
348 *nsteps = 0;
349 *handle = NULL;
350 }
6973fc01
UD
351
352 return status;
353}
354
355
76a2102b
UD
356#ifndef STATIC_GCONV
357static int
76a2102b
UD
358increment_counter (struct __gconv_step *steps, size_t nsteps)
359{
360 /* Increment the user counter. */
361 size_t cnt = nsteps;
362 int result = __GCONV_OK;
363
364 while (cnt-- > 0)
b79f74cd
UD
365 {
366 struct __gconv_step *step = &steps[cnt];
367
368 if (step->__counter++ == 0)
369 {
370 /* Skip builtin modules. */
371 if (step->__modname != NULL)
372 {
373 /* Reopen a previously used module. */
374 step->__shlib_handle = __gconv_find_shlib (step->__modname);
375 if (step->__shlib_handle == NULL)
376 {
377 /* Oops, this is the second time we use this module
378 (after unloading) and this time loading failed!? */
379 --step->__counter;
380 while (++cnt < nsteps)
6b98979f 381 __gconv_release_step (&steps[cnt]);
b79f74cd
UD
382 result = __GCONV_NOCONV;
383 break;
384 }
385
386 /* The function addresses defined by the module may
387 have changed. */
388 step->__fct = step->__shlib_handle->fct;
389 step->__init_fct = step->__shlib_handle->init_fct;
390 step->__end_fct = step->__shlib_handle->end_fct;
f9ad060c
UD
391
392 /* These settings can be overridden by the init function. */
393 step->__btowc_fct = NULL;
b79f74cd 394
e7c18b9d
FW
395 /* Call the init function. */
396 __gconv_init_fct init_fct = step->__init_fct;
e7c18b9d 397 PTR_DEMANGLE (init_fct);
e7c18b9d
FW
398 if (init_fct != NULL)
399 DL_CALL_FCT (init_fct, (step));
1cf1232c 400 PTR_MANGLE (step->__btowc_fct);
915a6c51 401 }
b79f74cd
UD
402 }
403 }
76a2102b
UD
404 return result;
405}
406#endif
407
408
6973fc01
UD
409/* The main function: find a possible derivation from the `fromset' (either
410 the given name or the alias) to the `toset' (again with alias). */
411static int
412find_derivation (const char *toset, const char *toset_expand,
413 const char *fromset, const char *fromset_expand,
d64b6ad0 414 struct __gconv_step **handle, size_t *nsteps)
6973fc01 415{
2bd60880
UD
416 struct derivation_step *first, *current, **lastp, *solution = NULL;
417 int best_cost_hi = INT_MAX;
418 int best_cost_lo = INT_MAX;
6973fc01
UD
419 int result;
420
b79f74cd
UD
421 /* Look whether an earlier call to `find_derivation' has already
422 computed a possible derivation. If so, return it immediately. */
6973fc01
UD
423 result = derivation_lookup (fromset_expand ?: fromset, toset_expand ?: toset,
424 handle, nsteps);
d64b6ad0 425 if (result == __GCONV_OK)
3c5edd4d 426 {
76a2102b
UD
427#ifndef STATIC_GCONV
428 result = increment_counter (*handle, *nsteps);
429#endif
3c5edd4d
UD
430 return result;
431 }
6973fc01 432
b79f74cd
UD
433 /* The task is to find a sequence of transformations, backed by the
434 existing modules - whether builtin or dynamically loadable -,
435 starting at `fromset' (or `fromset_expand') and ending at `toset'
436 (or `toset_expand'), and with minimal cost.
437
438 For computer scientists, this is a shortest path search in the
439 graph where the nodes are all possible charsets and the edges are
440 the transformations listed in __gconv_modules_db.
441
442 For now we use a simple algorithm with quadratic runtime behaviour.
443 A breadth-first search, starting at `fromset' and `fromset_expand'.
444 The list starting at `first' contains all nodes that have been
445 visited up to now, in the order in which they have been visited --
446 excluding the goal nodes `toset' and `toset_expand' which get
447 managed in the list starting at `solution'.
448 `current' walks through the list starting at `first' and looks
449 which nodes are reachable from the current node, adding them to
450 the end of the list [`first' or `solution' respectively] (if
451 they are visited the first time) or updating them in place (if
452 they have have already been visited).
453 In each node of either list, cost_lo and cost_hi contain the
454 minimum cost over any paths found up to now, starting at `fromset'
455 or `fromset_expand', ending at that node. best_cost_lo and
456 best_cost_hi represent the minimum over the elements of the
457 `solution' list. */
458
6973fc01
UD
459 if (fromset_expand != NULL)
460 {
2bd60880
UD
461 first = NEW_STEP (fromset_expand, 0, 0, NULL, NULL);
462 first->next = NEW_STEP (fromset, 0, 0, NULL, NULL);
e34b0f29 463 lastp = &first->next->next;
6973fc01
UD
464 }
465 else
466 {
2bd60880 467 first = NEW_STEP (fromset, 0, 0, NULL, NULL);
e34b0f29 468 lastp = &first->next;
6973fc01
UD
469 }
470
2bd60880 471 for (current = first; current != NULL; current = current->next)
6973fc01
UD
472 {
473 /* Now match all the available module specifications against the
474 current charset name. If any of them matches check whether
475 we already have a derivation for this charset. If yes, use the
476 one with the lower costs. Otherwise add the new charset at the
2bd60880
UD
477 end.
478
e8b1163e
AJ
479 The module database is organized in a tree form which allows
480 searching for prefixes. So we search for the first entry with a
2bd60880
UD
481 matching prefix and any other matching entry can be found from
482 this place. */
b79f74cd 483 struct gconv_module *node;
2bd60880
UD
484
485 /* Maybe it is not necessary anymore to look for a solution for
b79f74cd 486 this entry since the cost is already as high (or higher) as
2bd60880
UD
487 the cost for the best solution so far. */
488 if (current->cost_hi > best_cost_hi
489 || (current->cost_hi == best_cost_hi
490 && current->cost_lo >= best_cost_lo))
491 continue;
492
b79f74cd 493 node = __gconv_modules_db;
2bd60880
UD
494 while (node != NULL)
495 {
d2dfc5de 496 int cmpres = strcmp (current->result_set, node->from_string);
2bd60880 497 if (cmpres == 0)
6973fc01 498 {
2bd60880
UD
499 /* Walk through the list of modules with this prefix and
500 try to match the name. */
501 struct gconv_module *runp;
502
2bd60880
UD
503 /* Check all the modules with this prefix. */
504 runp = node;
505 do
6973fc01 506 {
d2dfc5de
UD
507 const char *result_set = (strcmp (runp->to_string, "-") == 0
508 ? (toset_expand ?: toset)
509 : runp->to_string);
510 int cost_hi = runp->cost_hi + current->cost_hi;
511 int cost_lo = runp->cost_lo + current->cost_lo;
512 struct derivation_step *step;
513
514 /* We managed to find a derivation. First see whether
b79f74cd 515 we have reached one of the goal nodes. */
d2dfc5de
UD
516 if (strcmp (result_set, toset) == 0
517 || (toset_expand != NULL
518 && strcmp (result_set, toset_expand) == 0))
2bd60880 519 {
b79f74cd
UD
520 /* Append to the `solution' list if there
521 is no entry with this name. */
522 for (step = solution; step != NULL; step = step->next)
523 if (strcmp (result_set, step->result_set) == 0)
524 break;
525
526 if (step == NULL)
527 {
528 step = NEW_STEP (result_set,
529 cost_hi, cost_lo,
530 runp, current);
531 step->next = solution;
532 solution = step;
533 }
534 else if (step->cost_hi > cost_hi
535 || (step->cost_hi == cost_hi
536 && step->cost_lo > cost_lo))
537 {
538 /* A better path was found for the node,
539 on the `solution' list. */
540 step->code = runp;
541 step->last = current;
542 step->cost_hi = cost_hi;
543 step->cost_lo = cost_lo;
544 }
545
546 /* Update best_cost accordingly. */
547 if (cost_hi < best_cost_hi
d2dfc5de
UD
548 || (cost_hi == best_cost_hi
549 && cost_lo < best_cost_lo))
2bd60880 550 {
d2dfc5de
UD
551 best_cost_hi = cost_hi;
552 best_cost_lo = cost_lo;
2bd60880 553 }
6973fc01 554 }
d2dfc5de
UD
555 else if (cost_hi < best_cost_hi
556 || (cost_hi == best_cost_hi
557 && cost_lo < best_cost_lo))
6973fc01 558 {
b79f74cd
UD
559 /* Append at the end of the `first' list if there
560 is no entry with this name. */
d2dfc5de
UD
561 for (step = first; step != NULL; step = step->next)
562 if (strcmp (result_set, step->result_set) == 0)
563 break;
2bd60880 564
d2dfc5de
UD
565 if (step == NULL)
566 {
567 *lastp = NEW_STEP (result_set,
568 cost_hi, cost_lo,
569 runp, current);
570 lastp = &(*lastp)->next;
2bd60880 571 }
d2dfc5de
UD
572 else if (step->cost_hi > cost_hi
573 || (step->cost_hi == cost_hi
574 && step->cost_lo > cost_lo))
2bd60880 575 {
b79f74cd
UD
576 /* A better path was found for the node,
577 on the `first' list. */
d2dfc5de
UD
578 step->code = runp;
579 step->last = current;
2bd60880 580
d2dfc5de
UD
581 /* Update the cost for all steps. */
582 for (step = first; step != NULL;
583 step = step->next)
b79f74cd
UD
584 /* But don't update the start nodes. */
585 if (step->code != NULL)
586 {
587 struct derivation_step *back;
588 int hi, lo;
589
590 hi = step->code->cost_hi;
591 lo = step->code->cost_lo;
592
593 for (back = step->last; back->code != NULL;
594 back = back->last)
595 {
596 hi += back->code->cost_hi;
597 lo += back->code->cost_lo;
598 }
599
600 step->cost_hi = hi;
601 step->cost_lo = lo;
602 }
603
604 /* Likewise for the nodes on the solution list.
605 Also update best_cost accordingly. */
d2dfc5de
UD
606 for (step = solution; step != NULL;
607 step = step->next)
608 {
609 step->cost_hi = (step->code->cost_hi
610 + step->last->cost_hi);
611 step->cost_lo = (step->code->cost_lo
612 + step->last->cost_lo);
613
614 if (step->cost_hi < best_cost_hi
615 || (step->cost_hi == best_cost_hi
616 && step->cost_lo < best_cost_lo))
2bd60880 617 {
d2dfc5de
UD
618 best_cost_hi = step->cost_hi;
619 best_cost_lo = step->cost_lo;
2bd60880
UD
620 }
621 }
622 }
6973fc01 623 }
2bd60880
UD
624
625 runp = runp->same;
6973fc01 626 }
2bd60880 627 while (runp != NULL);
e34b0f29 628
d2dfc5de 629 break;
6973fc01 630 }
2bd60880
UD
631 else if (cmpres < 0)
632 node = node->left;
633 else
634 node = node->right;
6973fc01
UD
635 }
636 }
637
2bd60880 638 if (solution != NULL)
b79f74cd
UD
639 {
640 /* We really found a way to do the transformation. */
641
642 /* Choose the best solution. This is easy because we know that
643 the solution list has at most length 2 (one for every possible
644 goal node). */
645 if (solution->next != NULL)
646 {
647 struct derivation_step *solution2 = solution->next;
648
649 if (solution2->cost_hi < solution->cost_hi
650 || (solution2->cost_hi == solution->cost_hi
651 && solution2->cost_lo < solution->cost_lo))
652 solution = solution2;
653 }
654
655 /* Now build a data structure describing the transformation steps. */
656 result = gen_steps (solution, toset_expand ?: toset,
657 fromset_expand ?: fromset, handle, nsteps);
658 }
6973fc01
UD
659 else
660 {
661 /* We haven't found a transformation. Clear the result values. */
662 *handle = NULL;
663 *nsteps = 0;
664 }
665
666 /* Add result in any case to list of known derivations. */
667 add_derivation (fromset_expand ?: fromset, toset_expand ?: toset,
668 *handle, *nsteps);
669
6973fc01
UD
670 return result;
671}
672
673
e7f21fa6
UD
674static const char *
675do_lookup_alias (const char *name)
676{
677 struct gconv_alias key;
678 struct gconv_alias **found;
679
680 key.fromname = (char *) name;
681 found = __tfind (&key, &__gconv_alias_db, __gconv_alias_compare);
682 return found != NULL ? (*found)->toname : NULL;
683}
684
685
9a018f6c 686int
9a018f6c 687__gconv_compare_alias (const char *name1, const char *name2)
e7f21fa6 688{
9a018f6c
UD
689 int result;
690
e7f21fa6 691 /* Ensure that the configuration data is read. */
c5288d37 692 __gconv_load_conf ();
e7f21fa6 693
9a018f6c
UD
694 if (__gconv_compare_alias_cache (name1, name2, &result) != 0)
695 result = strcmp (do_lookup_alias (name1) ?: name1,
696 do_lookup_alias (name2) ?: name2);
697
698 return result;
e7f21fa6
UD
699}
700
701
6973fc01
UD
702int
703__gconv_find_transform (const char *toset, const char *fromset,
60c53a12
UD
704 struct __gconv_step **handle, size_t *nsteps,
705 int flags)
6973fc01 706{
6b98979f
UD
707 const char *fromset_expand;
708 const char *toset_expand;
6973fc01
UD
709 int result;
710
711 /* Ensure that the configuration data is read. */
c5288d37 712 __gconv_load_conf ();
6973fc01 713
0d9f6793 714 /* Acquire the lock. */
9e26f129 715 __libc_lock_lock (__gconv_lock);
0d9f6793 716
6b98979f
UD
717 result = __gconv_lookup_cache (toset, fromset, handle, nsteps, flags);
718 if (result != __GCONV_NODB)
719 {
720 /* We have a cache and could resolve the request, successful or not. */
9e26f129 721 __libc_lock_unlock (__gconv_lock);
6b98979f
UD
722 return result;
723 }
724
6973fc01
UD
725 /* If we don't have a module database return with an error. */
726 if (__gconv_modules_db == NULL)
3c5edd4d 727 {
9e26f129 728 __libc_lock_unlock (__gconv_lock);
d64b6ad0 729 return __GCONV_NOCONV;
3c5edd4d 730 }
6973fc01
UD
731
732 /* See whether the names are aliases. */
6b98979f
UD
733 fromset_expand = do_lookup_alias (fromset);
734 toset_expand = do_lookup_alias (toset);
6973fc01 735
5ea1a82d 736 if (__builtin_expect (flags & GCONV_AVOID_NOCONV, 0)
60c53a12
UD
737 /* We are not supposed to create a pseudo transformation (means
738 copying) when the input and output character set are the same. */
739 && (strcmp (toset, fromset) == 0
740 || (toset_expand != NULL && strcmp (toset_expand, fromset) == 0)
741 || (fromset_expand != NULL
742 && (strcmp (toset, fromset_expand) == 0
743 || (toset_expand != NULL
744 && strcmp (toset_expand, fromset_expand) == 0)))))
745 {
746 /* Both character sets are the same. */
9e26f129 747 __libc_lock_unlock (__gconv_lock);
7b503bcc 748 return __GCONV_NULCONV;
60c53a12
UD
749 }
750
6973fc01
UD
751 result = find_derivation (toset, toset_expand, fromset, fromset_expand,
752 handle, nsteps);
753
0d9f6793 754 /* Release the lock. */
9e26f129 755 __libc_lock_unlock (__gconv_lock);
0d9f6793 756
6973fc01
UD
757 /* The following code is necessary since `find_derivation' will return
758 GCONV_OK even when no derivation was found but the same request
759 was processed before. I.e., negative results will also be cached. */
d64b6ad0
UD
760 return (result == __GCONV_OK
761 ? (*handle == NULL ? __GCONV_NOCONV : __GCONV_OK)
6973fc01
UD
762 : result);
763}
764
765
766/* Release the entries of the modules list. */
767int
d64b6ad0 768__gconv_close_transform (struct __gconv_step *steps, size_t nsteps)
6973fc01 769{
d64b6ad0 770 int result = __GCONV_OK;
0db59742 771 size_t cnt;
6973fc01 772
0d9f6793 773 /* Acquire the lock. */
9e26f129 774 __libc_lock_lock (__gconv_lock);
0d9f6793 775
0db59742
UD
776#ifndef STATIC_GCONV
777 cnt = nsteps;
778 while (cnt-- > 0)
779 __gconv_release_step (&steps[cnt]);
780#endif
781
782 /* If we use the cache we free a bit more since we don't keep any
783 transformation records around, they are cheap enough to
784 recreate. */
785 __gconv_release_cache (steps, nsteps);
6973fc01 786
0d9f6793 787 /* Release the lock. */
9e26f129 788 __libc_lock_unlock (__gconv_lock);
0d9f6793 789
6973fc01
UD
790 return result;
791}
792
793
2bd60880
UD
794/* Free the modules mentioned. */
795static void
2bd60880
UD
796free_modules_db (struct gconv_module *node)
797{
798 if (node->left != NULL)
799 free_modules_db (node->left);
800 if (node->right != NULL)
801 free_modules_db (node->right);
2bd60880
UD
802 do
803 {
804 struct gconv_module *act = node;
d2dfc5de 805 node = node->same;
8ce63ec0
UD
806 if (act->module_name[0] == '/')
807 free (act);
2bd60880
UD
808 }
809 while (node != NULL);
810}
811
812
6973fc01 813/* Free all resources if necessary. */
88677348
AZN
814void
815__gconv_db_freemem (void)
6973fc01 816{
be64c2ef
PP
817 /* First free locale memory. This needs to be done before freeing
818 derivations, as ctype cleanup functions dereference steps arrays which we
819 free below. */
7c11c4a1
UD
820 _nl_locale_subfreeres ();
821
9e365fe7
UD
822 /* finddomain.c has similar problem. */
823 extern void _nl_finddomain_subfreeres (void) attribute_hidden;
824 _nl_finddomain_subfreeres ();
825
6973fc01
UD
826 if (__gconv_alias_db != NULL)
827 __tdestroy (__gconv_alias_db, free);
828
0dbbad29
UD
829 if (__gconv_modules_db != NULL)
830 free_modules_db (__gconv_modules_db);
6973fc01
UD
831
832 if (known_derivations != NULL)
833 __tdestroy (known_derivations, free_derivation);
834}
This page took 0.607236 seconds and 5 git commands to generate.