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
25 #include <sys/param.h>
26 #include <bits/libc-lock.h>
29 #include <gconv_int.h>
32 /* Simple data structure for alias mapping. We have two names, `from'
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. */
45 __gconv_get_modules_db (void)
47 return __gconv_modules_db
;
51 __gconv_get_alias_db (void)
53 return __gconv_alias_db
;
57 /* Function for searching alias. */
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
;
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); \
87 newp->code = module; \
88 newp->last = last_mod; \
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
99 struct __gconv_step
*steps
;
103 /* Compare function for database of found derivations. */
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
;
111 result
= strcmp (s1
->from
, s2
->from
);
113 result
= strcmp (s1
->to
, s2
->to
);
117 /* The search tree for known derivations. */
118 static void *known_derivations
;
120 /* Look up whether given transformation was already requested before. */
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
);
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. */
142 /* Add new derivation to list of known ones. */
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
),
160 new_deriv
->steps
= handle
;
161 new_deriv
->nsteps
= nsteps
;
163 if (__tsearch (new_deriv
, &known_derivations
, derivation_compare
)
165 /* There is some kind of memory allocation problem. */
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. */
174 free_derivation (void *p
)
176 struct known_derivation
*deriv
= (struct known_derivation
*) p
;
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
);
193 /* Decrement the reference count for a single step in a steps array. */
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
));
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
;
218 gen_steps (struct derivation_step
*best
, const char *toset
,
219 const char *fromset
, struct __gconv_step
**handle
, size_t *nsteps
)
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
)
230 result
= (struct __gconv_step
*) malloc (sizeof (struct __gconv_step
)
239 while (step_cnt
-- > 0)
241 result
[step_cnt
].__from_name
= (step_cnt
== 0
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
;
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
)
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
)
282 /* Make sure we unload this modules. */
284 result
[step_cnt
].__end_fct
= NULL
;
291 /* It's a builtin transformation. */
292 __gconv_get_builtin_trans (current
->code
->module_name
,
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
]);
306 if (status
== __GCONV_OK
)
307 status
= __GCONV_NOCONV
;
325 increment_counter (struct __gconv_step
*steps
, size_t nsteps
)
327 /* Increment the user counter. */
329 int result
= __GCONV_OK
;
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!? */
347 while (++cnt
< nsteps
)
348 __gconv_release_step (&steps
[cnt
]);
349 result
= __GCONV_NOCONV
;
353 /* The function addresses defined by the module may
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
));
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). */
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
;
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
,
390 if (result
== __GCONV_OK
)
393 result
= increment_counter (*handle
, *nsteps
);
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
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
;
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
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
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
))
458 node
= __gconv_modules_db
;
461 int cmpres
= strcmp (current
->result_set
, node
->from_string
);
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. */
472 const char *result_set
= (strcmp (runp
->to_string
, "-") == 0
473 ? (toset_expand
?: toset
)
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)
493 step
= NEW_STEP (result_set
,
496 step
->next
= solution
;
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. */
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)
532 *lastp
= NEW_STEP (result_set
,
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. */
544 step
->last
= current
;
546 /* Update the cost for all steps. */
547 for (step
= first
; step
!= NULL
;
549 /* But don't update the start nodes. */
550 if (step
->code
!= NULL
)
552 struct derivation_step
*back
;
555 hi
= step
->code
->cost_hi
;
556 lo
= step
->code
->cost_lo
;
558 for (back
= step
->last
; back
->code
!= NULL
;
561 hi
+= back
->code
->cost_hi
;
562 lo
+= back
->code
->cost_lo
;
569 /* Likewise for the nodes on the solution list.
570 Also update best_cost accordingly. */
571 for (step
= solution
; step
!= NULL
;
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
;
592 while (runp
!= NULL
);
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
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
);
626 /* We haven't found a transformation. Clear the result values. */
631 /* Add result in any case to list of known derivations. */
632 add_derivation (fromset_expand
?: fromset
, toset_expand
?: toset
,
639 /* Control of initialization. */
640 __libc_once_define (static, once
);
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
;
657 __gconv_compare_alias (const char *name1
, const char *name2
)
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
);
674 __gconv_find_transform (const char *toset
, const char *fromset
,
675 struct __gconv_step
**handle
, size_t *nsteps
,
678 const char *fromset_expand
;
679 const char *toset_expand
;
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
);
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
,
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
)
737 /* Release the entries of the modules list. */
740 __gconv_close_transform (struct __gconv_step
*steps
, size_t nsteps
)
742 int result
= __GCONV_OK
;
745 /* Acquire the lock. */
746 __libc_lock_lock (lock
);
751 __gconv_release_step (&steps
[cnt
]);
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
757 __gconv_release_cache (steps
, nsteps
);
759 /* Release the lock. */
760 __libc_lock_unlock (lock
);
766 /* Free the modules mentioned. */
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
;
779 if (act
->module_name
[0] == '/')
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
);