Support cancellation in librt.
[glibc.git] / iconv / gconv_db.c
blob158e0e186e4dd951812e97034cd53b2310ec195d
1 /* Provide access to the collection of available transformation modules.
2 Copyright (C) 1997,98,99,2000,2001,2002,2003 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4 Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997.
6 The GNU C Library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
11 The GNU C Library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with the GNU C Library; if not, write to the Free
18 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19 02111-1307 USA. */
21 #include <limits.h>
22 #include <search.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <sys/param.h>
26 #include <bits/libc-lock.h>
28 #include <dlfcn.h>
29 #include <gconv_int.h>
32 /* Simple data structure for alias mapping. We have two names, `from'
33 and `to'. */
34 void *__gconv_alias_db;
36 /* Array with available modules. */
37 struct gconv_module *__gconv_modules_db;
39 /* We modify global data. */
40 __libc_lock_define_initialized (static, lock)
43 /* Provide access to module database. */
44 struct gconv_module *
45 __gconv_get_modules_db (void)
47 return __gconv_modules_db;
50 void *
51 __gconv_get_alias_db (void)
53 return __gconv_alias_db;
57 /* Function for searching alias. */
58 int
59 __gconv_alias_compare (const void *p1, const void *p2)
61 const struct gconv_alias *s1 = (const struct gconv_alias *) p1;
62 const struct gconv_alias *s2 = (const struct gconv_alias *) p2;
63 return strcmp (s1->fromname, s2->fromname);
67 /* To search for a derivation we create a list of intermediate steps.
68 Each element contains a pointer to the element which precedes it
69 in the derivation order. */
70 struct derivation_step
72 const char *result_set;
73 size_t result_set_len;
74 int cost_lo;
75 int cost_hi;
76 struct gconv_module *code;
77 struct derivation_step *last;
78 struct derivation_step *next;
81 #define NEW_STEP(result, hi, lo, module, last_mod) \
82 ({ struct derivation_step *newp = alloca (sizeof (struct derivation_step)); \
83 newp->result_set = result; \
84 newp->result_set_len = strlen (result); \
85 newp->cost_hi = hi; \
86 newp->cost_lo = lo; \
87 newp->code = module; \
88 newp->last = last_mod; \
89 newp->next = NULL; \
90 newp; })
93 /* If a specific transformation is used more than once we should not need
94 to start looking for it again. Instead cache each successful result. */
95 struct known_derivation
97 const char *from;
98 const char *to;
99 struct __gconv_step *steps;
100 size_t nsteps;
103 /* Compare function for database of found derivations. */
104 static int
105 derivation_compare (const void *p1, const void *p2)
107 const struct known_derivation *s1 = (const struct known_derivation *) p1;
108 const struct known_derivation *s2 = (const struct known_derivation *) p2;
109 int result;
111 result = strcmp (s1->from, s2->from);
112 if (result == 0)
113 result = strcmp (s1->to, s2->to);
114 return result;
117 /* The search tree for known derivations. */
118 static void *known_derivations;
120 /* Look up whether given transformation was already requested before. */
121 static int
122 internal_function
123 derivation_lookup (const char *fromset, const char *toset,
124 struct __gconv_step **handle, size_t *nsteps)
126 struct known_derivation key = { fromset, toset, NULL, 0 };
127 struct known_derivation **result;
129 result = __tfind (&key, &known_derivations, derivation_compare);
131 if (result == NULL)
132 return __GCONV_NOCONV;
134 *handle = (*result)->steps;
135 *nsteps = (*result)->nsteps;
137 /* Please note that we return GCONV_OK even if the last search for
138 this transformation was unsuccessful. */
139 return __GCONV_OK;
142 /* Add new derivation to list of known ones. */
143 static void
144 internal_function
145 add_derivation (const char *fromset, const char *toset,
146 struct __gconv_step *handle, size_t nsteps)
148 struct known_derivation *new_deriv;
149 size_t fromset_len = strlen (fromset) + 1;
150 size_t toset_len = strlen (toset) + 1;
152 new_deriv = (struct known_derivation *)
153 malloc (sizeof (struct known_derivation) + fromset_len + toset_len);
154 if (new_deriv != NULL)
156 new_deriv->from = (char *) (new_deriv + 1);
157 new_deriv->to = memcpy (__mempcpy (new_deriv + 1, fromset, fromset_len),
158 toset, toset_len);
160 new_deriv->steps = handle;
161 new_deriv->nsteps = nsteps;
163 if (__tsearch (new_deriv, &known_derivations, derivation_compare)
164 == NULL)
165 /* There is some kind of memory allocation problem. */
166 free (new_deriv);
168 /* Please note that we don't complain if the allocation failed. This
169 is not tragically but in case we use the memory debugging facilities
170 not all memory will be freed. */
173 static void
174 free_derivation (void *p)
176 struct known_derivation *deriv = (struct known_derivation *) p;
177 size_t cnt;
179 for (cnt = 0; cnt < deriv->nsteps; ++cnt)
180 if (deriv->steps[cnt].__counter > 0
181 && deriv->steps[cnt].__end_fct != NULL)
182 DL_CALL_FCT (deriv->steps[cnt].__end_fct, (&deriv->steps[cnt]));
184 /* Free the name strings. */
185 free ((char *) deriv->steps[0].__from_name);
186 free ((char *) deriv->steps[deriv->nsteps - 1].__to_name);
188 free ((struct __gconv_step *) deriv->steps);
189 free (deriv);
193 /* Decrement the reference count for a single step in a steps array. */
194 void
195 internal_function
196 __gconv_release_step (struct __gconv_step *step)
198 if (--step->__counter == 0)
200 /* Call the destructor. */
201 if (step->__end_fct != NULL)
202 DL_CALL_FCT (step->__end_fct, (step));
204 #ifndef STATIC_GCONV
205 /* Skip builtin modules; they are not reference counted. */
206 if (step->__shlib_handle != NULL)
208 /* Release the loaded module. */
209 __gconv_release_shlib (step->__shlib_handle);
210 step->__shlib_handle = NULL;
212 #endif
216 static int
217 internal_function
218 gen_steps (struct derivation_step *best, const char *toset,
219 const char *fromset, struct __gconv_step **handle, size_t *nsteps)
221 size_t step_cnt = 0;
222 struct __gconv_step *result;
223 struct derivation_step *current;
224 int status = __GCONV_NOMEM;
226 /* First determine number of steps. */
227 for (current = best; current->last != NULL; current = current->last)
228 ++step_cnt;
230 result = (struct __gconv_step *) malloc (sizeof (struct __gconv_step)
231 * step_cnt);
232 if (result != NULL)
234 int failed = 0;
236 status = __GCONV_OK;
237 *nsteps = step_cnt;
238 current = best;
239 while (step_cnt-- > 0)
241 result[step_cnt].__from_name = (step_cnt == 0
242 ? __strdup (fromset)
243 : (char *)current->last->result_set);
244 result[step_cnt].__to_name = (step_cnt + 1 == *nsteps
245 ? __strdup (current->result_set)
246 : result[step_cnt + 1].__from_name);
248 result[step_cnt].__counter = 1;
249 result[step_cnt].__data = NULL;
251 #ifndef STATIC_GCONV
252 if (current->code->module_name[0] == '/')
254 /* Load the module, return handle for it. */
255 struct __gconv_loaded_object *shlib_handle =
256 __gconv_find_shlib (current->code->module_name);
258 if (shlib_handle == NULL)
260 failed = 1;
261 break;
264 result[step_cnt].__shlib_handle = shlib_handle;
265 result[step_cnt].__modname = shlib_handle->name;
266 result[step_cnt].__fct = shlib_handle->fct;
267 result[step_cnt].__init_fct = shlib_handle->init_fct;
268 result[step_cnt].__end_fct = shlib_handle->end_fct;
270 /* These settings can be overridden by the init function. */
271 result[step_cnt].__btowc_fct = NULL;
273 /* Call the init function. */
274 if (result[step_cnt].__init_fct != NULL)
276 status = DL_CALL_FCT (result[step_cnt].__init_fct,
277 (&result[step_cnt]));
279 if (__builtin_expect (status, __GCONV_OK) != __GCONV_OK)
281 failed = 1;
282 /* Make sure we unload this modules. */
283 --step_cnt;
284 result[step_cnt].__end_fct = NULL;
285 break;
289 else
290 #endif
291 /* It's a builtin transformation. */
292 __gconv_get_builtin_trans (current->code->module_name,
293 &result[step_cnt]);
295 current = current->last;
298 if (__builtin_expect (failed, 0) != 0)
300 /* Something went wrong while initializing the modules. */
301 while (++step_cnt < *nsteps)
302 __gconv_release_step (&result[step_cnt]);
303 free (result);
304 *nsteps = 0;
305 *handle = NULL;
306 if (status == __GCONV_OK)
307 status = __GCONV_NOCONV;
309 else
310 *handle = result;
312 else
314 *nsteps = 0;
315 *handle = NULL;
318 return status;
322 #ifndef STATIC_GCONV
323 static int
324 internal_function
325 increment_counter (struct __gconv_step *steps, size_t nsteps)
327 /* Increment the user counter. */
328 size_t cnt = nsteps;
329 int result = __GCONV_OK;
331 while (cnt-- > 0)
333 struct __gconv_step *step = &steps[cnt];
335 if (step->__counter++ == 0)
337 /* Skip builtin modules. */
338 if (step->__modname != NULL)
340 /* Reopen a previously used module. */
341 step->__shlib_handle = __gconv_find_shlib (step->__modname);
342 if (step->__shlib_handle == NULL)
344 /* Oops, this is the second time we use this module
345 (after unloading) and this time loading failed!? */
346 --step->__counter;
347 while (++cnt < nsteps)
348 __gconv_release_step (&steps[cnt]);
349 result = __GCONV_NOCONV;
350 break;
353 /* The function addresses defined by the module may
354 have changed. */
355 step->__fct = step->__shlib_handle->fct;
356 step->__init_fct = step->__shlib_handle->init_fct;
357 step->__end_fct = step->__shlib_handle->end_fct;
359 /* These settings can be overridden by the init function. */
360 step->__btowc_fct = NULL;
363 /* Call the init function. */
364 if (step->__init_fct != NULL)
365 DL_CALL_FCT (step->__init_fct, (step));
368 return result;
370 #endif
373 /* The main function: find a possible derivation from the `fromset' (either
374 the given name or the alias) to the `toset' (again with alias). */
375 static int
376 internal_function
377 find_derivation (const char *toset, const char *toset_expand,
378 const char *fromset, const char *fromset_expand,
379 struct __gconv_step **handle, size_t *nsteps)
381 struct derivation_step *first, *current, **lastp, *solution = NULL;
382 int best_cost_hi = INT_MAX;
383 int best_cost_lo = INT_MAX;
384 int result;
386 /* Look whether an earlier call to `find_derivation' has already
387 computed a possible derivation. If so, return it immediately. */
388 result = derivation_lookup (fromset_expand ?: fromset, toset_expand ?: toset,
389 handle, nsteps);
390 if (result == __GCONV_OK)
392 #ifndef STATIC_GCONV
393 result = increment_counter (*handle, *nsteps);
394 #endif
395 return result;
398 /* The task is to find a sequence of transformations, backed by the
399 existing modules - whether builtin or dynamically loadable -,
400 starting at `fromset' (or `fromset_expand') and ending at `toset'
401 (or `toset_expand'), and with minimal cost.
403 For computer scientists, this is a shortest path search in the
404 graph where the nodes are all possible charsets and the edges are
405 the transformations listed in __gconv_modules_db.
407 For now we use a simple algorithm with quadratic runtime behaviour.
408 A breadth-first search, starting at `fromset' and `fromset_expand'.
409 The list starting at `first' contains all nodes that have been
410 visited up to now, in the order in which they have been visited --
411 excluding the goal nodes `toset' and `toset_expand' which get
412 managed in the list starting at `solution'.
413 `current' walks through the list starting at `first' and looks
414 which nodes are reachable from the current node, adding them to
415 the end of the list [`first' or `solution' respectively] (if
416 they are visited the first time) or updating them in place (if
417 they have have already been visited).
418 In each node of either list, cost_lo and cost_hi contain the
419 minimum cost over any paths found up to now, starting at `fromset'
420 or `fromset_expand', ending at that node. best_cost_lo and
421 best_cost_hi represent the minimum over the elements of the
422 `solution' list. */
424 if (fromset_expand != NULL)
426 first = NEW_STEP (fromset_expand, 0, 0, NULL, NULL);
427 first->next = NEW_STEP (fromset, 0, 0, NULL, NULL);
428 lastp = &first->next->next;
430 else
432 first = NEW_STEP (fromset, 0, 0, NULL, NULL);
433 lastp = &first->next;
436 for (current = first; current != NULL; current = current->next)
438 /* Now match all the available module specifications against the
439 current charset name. If any of them matches check whether
440 we already have a derivation for this charset. If yes, use the
441 one with the lower costs. Otherwise add the new charset at the
442 end.
444 The module database is organized in a tree form which allows
445 searching for prefixes. So we search for the first entry with a
446 matching prefix and any other matching entry can be found from
447 this place. */
448 struct gconv_module *node;
450 /* Maybe it is not necessary anymore to look for a solution for
451 this entry since the cost is already as high (or higher) as
452 the cost for the best solution so far. */
453 if (current->cost_hi > best_cost_hi
454 || (current->cost_hi == best_cost_hi
455 && current->cost_lo >= best_cost_lo))
456 continue;
458 node = __gconv_modules_db;
459 while (node != NULL)
461 int cmpres = strcmp (current->result_set, node->from_string);
462 if (cmpres == 0)
464 /* Walk through the list of modules with this prefix and
465 try to match the name. */
466 struct gconv_module *runp;
468 /* Check all the modules with this prefix. */
469 runp = node;
472 const char *result_set = (strcmp (runp->to_string, "-") == 0
473 ? (toset_expand ?: toset)
474 : runp->to_string);
475 int cost_hi = runp->cost_hi + current->cost_hi;
476 int cost_lo = runp->cost_lo + current->cost_lo;
477 struct derivation_step *step;
479 /* We managed to find a derivation. First see whether
480 we have reached one of the goal nodes. */
481 if (strcmp (result_set, toset) == 0
482 || (toset_expand != NULL
483 && strcmp (result_set, toset_expand) == 0))
485 /* Append to the `solution' list if there
486 is no entry with this name. */
487 for (step = solution; step != NULL; step = step->next)
488 if (strcmp (result_set, step->result_set) == 0)
489 break;
491 if (step == NULL)
493 step = NEW_STEP (result_set,
494 cost_hi, cost_lo,
495 runp, current);
496 step->next = solution;
497 solution = step;
499 else if (step->cost_hi > cost_hi
500 || (step->cost_hi == cost_hi
501 && step->cost_lo > cost_lo))
503 /* A better path was found for the node,
504 on the `solution' list. */
505 step->code = runp;
506 step->last = current;
507 step->cost_hi = cost_hi;
508 step->cost_lo = cost_lo;
511 /* Update best_cost accordingly. */
512 if (cost_hi < best_cost_hi
513 || (cost_hi == best_cost_hi
514 && cost_lo < best_cost_lo))
516 best_cost_hi = cost_hi;
517 best_cost_lo = cost_lo;
520 else if (cost_hi < best_cost_hi
521 || (cost_hi == best_cost_hi
522 && cost_lo < best_cost_lo))
524 /* Append at the end of the `first' list if there
525 is no entry with this name. */
526 for (step = first; step != NULL; step = step->next)
527 if (strcmp (result_set, step->result_set) == 0)
528 break;
530 if (step == NULL)
532 *lastp = NEW_STEP (result_set,
533 cost_hi, cost_lo,
534 runp, current);
535 lastp = &(*lastp)->next;
537 else if (step->cost_hi > cost_hi
538 || (step->cost_hi == cost_hi
539 && step->cost_lo > cost_lo))
541 /* A better path was found for the node,
542 on the `first' list. */
543 step->code = runp;
544 step->last = current;
546 /* Update the cost for all steps. */
547 for (step = first; step != NULL;
548 step = step->next)
549 /* But don't update the start nodes. */
550 if (step->code != NULL)
552 struct derivation_step *back;
553 int hi, lo;
555 hi = step->code->cost_hi;
556 lo = step->code->cost_lo;
558 for (back = step->last; back->code != NULL;
559 back = back->last)
561 hi += back->code->cost_hi;
562 lo += back->code->cost_lo;
565 step->cost_hi = hi;
566 step->cost_lo = lo;
569 /* Likewise for the nodes on the solution list.
570 Also update best_cost accordingly. */
571 for (step = solution; step != NULL;
572 step = step->next)
574 step->cost_hi = (step->code->cost_hi
575 + step->last->cost_hi);
576 step->cost_lo = (step->code->cost_lo
577 + step->last->cost_lo);
579 if (step->cost_hi < best_cost_hi
580 || (step->cost_hi == best_cost_hi
581 && step->cost_lo < best_cost_lo))
583 best_cost_hi = step->cost_hi;
584 best_cost_lo = step->cost_lo;
590 runp = runp->same;
592 while (runp != NULL);
594 break;
596 else if (cmpres < 0)
597 node = node->left;
598 else
599 node = node->right;
603 if (solution != NULL)
605 /* We really found a way to do the transformation. */
607 /* Choose the best solution. This is easy because we know that
608 the solution list has at most length 2 (one for every possible
609 goal node). */
610 if (solution->next != NULL)
612 struct derivation_step *solution2 = solution->next;
614 if (solution2->cost_hi < solution->cost_hi
615 || (solution2->cost_hi == solution->cost_hi
616 && solution2->cost_lo < solution->cost_lo))
617 solution = solution2;
620 /* Now build a data structure describing the transformation steps. */
621 result = gen_steps (solution, toset_expand ?: toset,
622 fromset_expand ?: fromset, handle, nsteps);
624 else
626 /* We haven't found a transformation. Clear the result values. */
627 *handle = NULL;
628 *nsteps = 0;
631 /* Add result in any case to list of known derivations. */
632 add_derivation (fromset_expand ?: fromset, toset_expand ?: toset,
633 *handle, *nsteps);
635 return result;
639 /* Control of initialization. */
640 __libc_once_define (static, once);
643 static const char *
644 do_lookup_alias (const char *name)
646 struct gconv_alias key;
647 struct gconv_alias **found;
649 key.fromname = (char *) name;
650 found = __tfind (&key, &__gconv_alias_db, __gconv_alias_compare);
651 return found != NULL ? (*found)->toname : NULL;
656 internal_function
657 __gconv_compare_alias (const char *name1, const char *name2)
659 int result;
661 /* Ensure that the configuration data is read. */
662 __libc_once (once, __gconv_read_conf);
664 if (__gconv_compare_alias_cache (name1, name2, &result) != 0)
665 result = strcmp (do_lookup_alias (name1) ?: name1,
666 do_lookup_alias (name2) ?: name2);
668 return result;
673 internal_function
674 __gconv_find_transform (const char *toset, const char *fromset,
675 struct __gconv_step **handle, size_t *nsteps,
676 int flags)
678 const char *fromset_expand;
679 const char *toset_expand;
680 int result;
682 /* Ensure that the configuration data is read. */
683 __libc_once (once, __gconv_read_conf);
685 /* Acquire the lock. */
686 __libc_lock_lock (lock);
688 result = __gconv_lookup_cache (toset, fromset, handle, nsteps, flags);
689 if (result != __GCONV_NODB)
691 /* We have a cache and could resolve the request, successful or not. */
692 __libc_lock_unlock (lock);
693 return result;
696 /* If we don't have a module database return with an error. */
697 if (__gconv_modules_db == NULL)
699 __libc_lock_unlock (lock);
700 return __GCONV_NOCONV;
703 /* See whether the names are aliases. */
704 fromset_expand = do_lookup_alias (fromset);
705 toset_expand = do_lookup_alias (toset);
707 if (__builtin_expect (flags & GCONV_AVOID_NOCONV, 0)
708 /* We are not supposed to create a pseudo transformation (means
709 copying) when the input and output character set are the same. */
710 && (strcmp (toset, fromset) == 0
711 || (toset_expand != NULL && strcmp (toset_expand, fromset) == 0)
712 || (fromset_expand != NULL
713 && (strcmp (toset, fromset_expand) == 0
714 || (toset_expand != NULL
715 && strcmp (toset_expand, fromset_expand) == 0)))))
717 /* Both character sets are the same. */
718 __libc_lock_unlock (lock);
719 return __GCONV_NOCONV;
722 result = find_derivation (toset, toset_expand, fromset, fromset_expand,
723 handle, nsteps);
725 /* Release the lock. */
726 __libc_lock_unlock (lock);
728 /* The following code is necessary since `find_derivation' will return
729 GCONV_OK even when no derivation was found but the same request
730 was processed before. I.e., negative results will also be cached. */
731 return (result == __GCONV_OK
732 ? (*handle == NULL ? __GCONV_NOCONV : __GCONV_OK)
733 : result);
737 /* Release the entries of the modules list. */
739 internal_function
740 __gconv_close_transform (struct __gconv_step *steps, size_t nsteps)
742 int result = __GCONV_OK;
743 size_t cnt;
745 /* Acquire the lock. */
746 __libc_lock_lock (lock);
748 #ifndef STATIC_GCONV
749 cnt = nsteps;
750 while (cnt-- > 0)
751 __gconv_release_step (&steps[cnt]);
752 #endif
754 /* If we use the cache we free a bit more since we don't keep any
755 transformation records around, they are cheap enough to
756 recreate. */
757 __gconv_release_cache (steps, nsteps);
759 /* Release the lock. */
760 __libc_lock_unlock (lock);
762 return result;
766 /* Free the modules mentioned. */
767 static void
768 internal_function
769 free_modules_db (struct gconv_module *node)
771 if (node->left != NULL)
772 free_modules_db (node->left);
773 if (node->right != NULL)
774 free_modules_db (node->right);
777 struct gconv_module *act = node;
778 node = node->same;
779 if (act->module_name[0] == '/')
780 free (act);
782 while (node != NULL);
786 /* Free all resources if necessary. */
787 libc_freeres_fn (free_mem)
789 if (__gconv_alias_db != NULL)
790 __tdestroy (__gconv_alias_db, free);
792 if (__gconv_modules_db != NULL)
793 free_modules_db (__gconv_modules_db);
795 if (known_derivations != NULL)
796 __tdestroy (known_derivations, free_derivation);