1 /* Provide access to the collection of available transformation modules.
2 Copyright (C) 1997, 1998 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 Library General Public License as
8 published by the Free Software Foundation; either version 2 of the
9 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 Library General Public License for more details.
16 You should have received a copy of the GNU Library General Public
17 License along with the GNU C Library; see the file COPYING.LIB. If not,
18 write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 Boston, MA 02111-1307, USA. */
24 #include <bits/libc-lock.h>
25 #include <elf/ldsodefs.h>
27 #include <gconv_int.h>
30 /* Simple data structure for alias mapping. We have two names, `from'
32 void *__gconv_alias_db;
34 /* Array with available modules. */
35 size_t __gconv_nmodules;
36 struct gconv_module **__gconv_modules_db;
38 /* We modify global data. */
39 __libc_lock_define_initialized (static, lock)
42 /* Function for searching alias. */
44 __gconv_alias_compare (const void *p1, const void *p2)
46 struct gconv_alias *s1 = (struct gconv_alias *) p1;
47 struct gconv_alias *s2 = (struct gconv_alias *) p2;
48 return __strcasecmp (s1->fromname, s2->fromname);
52 /* To search for a derivation we create a list of intermediate steps.
53 Each element contains a pointer to the element which precedes it
54 in the derivation order. */
55 struct derivation_step
57 const char *result_set;
58 struct gconv_module *code;
59 struct derivation_step *last;
60 struct derivation_step *next;
63 #define NEW_STEP(result, module, last_mod) \
64 ({ struct derivation_step *newp = alloca (sizeof (struct derivation_step)); \
65 newp->result_set = result; \
66 newp->code = module; \
67 newp->last = last_mod; \
72 /* If a specific transformation is used more than once we should not need
73 to start looking for it again. Instead cache each successful result. */
74 struct known_derivation
78 struct gconv_step *steps;
82 /* Compare function for database of found derivations. */
84 derivation_compare (const void *p1, const void *p2)
86 struct known_derivation *s1 = (struct known_derivation *) p1;
87 struct known_derivation *s2 = (struct known_derivation *) p2;
90 result = strcmp (s1->from, s2->from);
92 result = strcmp (s1->to, s2->to);
96 /* The search tree for known derivations. */
97 static void *known_derivations;
99 /* Look up whether given transformation was already requested before. */
102 derivation_lookup (const char *fromset, const char *toset,
103 struct gconv_step **handle, size_t *nsteps)
105 struct known_derivation key = { fromset, toset, NULL, 0 };
106 struct known_derivation **result;
108 result = __tfind (&key, &known_derivations, derivation_compare);
113 *handle = (*result)->steps;
114 *nsteps = (*result)->nsteps;
116 /* Please note that we return GCONV_OK even if the last search for
117 this transformation was unsuccessful. */
121 /* Add new derivation to list of known ones. */
124 add_derivation (const char *fromset, const char *toset,
125 struct gconv_step *handle, size_t nsteps)
127 struct known_derivation *new_deriv;
128 size_t fromset_len = strlen (fromset) + 1;
129 size_t toset_len = strlen (toset) + 1;
131 new_deriv = (struct known_derivation *)
132 malloc (sizeof (struct known_derivation) + fromset_len + toset_len);
133 if (new_deriv != NULL)
135 new_deriv->from = memcpy (new_deriv + 1, fromset, fromset_len);
136 new_deriv->to = memcpy ((char *) new_deriv->from + fromset_len,
139 new_deriv->steps = handle;
140 new_deriv->nsteps = nsteps;
142 __tsearch (new_deriv, &known_derivations, derivation_compare);
144 /* Please note that we don't complain if the allocation failed. This
145 is not tragically but in case we use the memory debugging facilities
146 not all memory will be freed. */
151 free_derivation (void *p)
153 struct known_derivation *deriv = (struct known_derivation *) p;
156 for (cnt = 0; cnt < deriv->nsteps; ++cnt)
157 if (deriv->steps[cnt].end_fct)
158 _CALL_DL_FCT (deriv->steps[cnt].end_fct, (&deriv->steps[cnt]));
160 free ((struct gconv_step *) deriv->steps);
167 gen_steps (struct derivation_step *best, const char *toset,
168 const char *fromset, struct gconv_step **handle, size_t *nsteps)
171 struct gconv_step *result;
172 struct derivation_step *current;
173 int status = GCONV_NOMEM;
175 /* First determine number of steps. */
176 for (current = best; current->last != NULL; current = current->last)
179 result = (struct gconv_step *) malloc (sizeof (struct gconv_step)
187 while (step_cnt-- > 0)
189 result[step_cnt].from_name = (step_cnt == 0
191 : current->last->result_set);
192 result[step_cnt].to_name = (step_cnt + 1 == *nsteps
193 ? __strdup (current->result_set)
194 : result[step_cnt + 1].from_name);
197 if (current->code->module_name[0] == '/')
199 /* Load the module, return handle for it. */
200 struct gconv_loaded_object *shlib_handle =
201 __gconv_find_shlib (current->code->module_name);
203 if (shlib_handle == NULL)
209 result[step_cnt].shlib_handle = shlib_handle;
210 result[step_cnt].modname = shlib_handle->name;
211 result[step_cnt].counter = 0;
212 result[step_cnt].fct = shlib_handle->fct;
213 result[step_cnt].init_fct = shlib_handle->init_fct;
214 result[step_cnt].end_fct = shlib_handle->end_fct;
218 /* It's a builtin transformation. */
219 __gconv_get_builtin_trans (current->code->module_name,
222 /* Call the init function. */
223 if (result[step_cnt].init_fct != NULL)
224 _CALL_DL_FCT (result[step_cnt].init_fct, (&result[step_cnt]));
226 current = current->last;
231 /* Something went wrong while initializing the modules. */
232 while (++step_cnt < *nsteps)
234 if (result[step_cnt].end_fct != NULL)
235 _CALL_DL_FCT (result[step_cnt].end_fct, (&result[step_cnt]));
237 __gconv_release_shlib (result[step_cnt].shlib_handle);
243 status = GCONV_NOCONV;
261 /* The main function: find a possible derivation from the `fromset' (either
262 the given name or the alias) to the `toset' (again with alias). */
265 find_derivation (const char *toset, const char *toset_expand,
266 const char *fromset, const char *fromset_expand,
267 struct gconv_step **handle, size_t *nsteps)
269 __libc_lock_define_initialized (static, lock)
270 struct derivation_step *first, *current, **lastp, *best = NULL;
271 int best_cost_hi = 0;
272 int best_cost_lo = 0;
275 result = derivation_lookup (fromset_expand ?: fromset, toset_expand ?: toset,
277 if (result == GCONV_OK)
280 __libc_lock_lock (lock);
282 /* There is a small chance that this derivation is meanwhile found. This
283 can happen if in `find_derivation' we look for this derivation, didn't
284 find it but at the same time another thread looked for this derivation. */
285 result = derivation_lookup (fromset_expand ?: fromset, toset_expand ?: toset,
287 if (result == GCONV_OK)
291 For now we use a simple algorithm with quadratic runtime behaviour.
292 The task is to match the `toset' with any of the available rules,
293 starting from FROMSET. */
294 if (fromset_expand != NULL)
296 first = NEW_STEP (fromset_expand, NULL, NULL);
297 first->next = NEW_STEP (fromset, NULL, NULL);
298 lastp = &first->next->next;
302 first = NEW_STEP (fromset, NULL, NULL);
303 lastp = &first->next;
307 while (current != NULL)
309 /* Now match all the available module specifications against the
310 current charset name. If any of them matches check whether
311 we already have a derivation for this charset. If yes, use the
312 one with the lower costs. Otherwise add the new charset at the
316 for (cnt = 0; cnt < __gconv_nmodules; ++cnt)
318 const char *result_set = NULL;
320 if (__gconv_modules_db[cnt]->from_pattern == NULL)
322 if (__strcasecmp (current->result_set,
323 __gconv_modules_db[cnt]->from_constpfx) == 0)
325 if (strcmp (__gconv_modules_db[cnt]->to_string, "-") == 0)
326 result_set = toset_expand ?: toset;
328 result_set = __gconv_modules_db[cnt]->to_string;
332 /* We have a regular expression. First see if the prefix
334 if (__strncasecmp (current->result_set,
335 __gconv_modules_db[cnt]->from_constpfx,
336 __gconv_modules_db[cnt]->from_constpfx_len)
339 /* First compile the regex if not already done. */
340 if (__gconv_modules_db[cnt]->from_regex == NULL)
342 regex_t *newp = (regex_t *) malloc (sizeof (regex_t));
344 if (__regcomp (newp, __gconv_modules_db[cnt]->from_pattern,
345 REG_EXTENDED | REG_ICASE) != 0)
347 /* Something is wrong. Remember this. */
349 __gconv_modules_db[cnt]->from_regex = (regex_t *) -1L;
352 __gconv_modules_db[cnt]->from_regex = newp;
355 if (__gconv_modules_db[cnt]->from_regex != (regex_t *) -1L)
357 /* Try to match the from name. */
360 if (__regexec (__gconv_modules_db[cnt]->from_regex,
361 current->result_set, 4, match, 0) == 0
362 && match[0].rm_so == 0
363 && current->result_set[match[0].rm_eo] == '\0')
365 /* At least the whole <from> string is matched.
366 We must now match sed-like possible
367 subexpressions from the match to the
369 #define ENSURE_LEN(LEN) \
370 if (wp + (LEN) >= constr + len - 1) \
372 char *newp = alloca (len += 128); \
373 memcpy (newp, constr, wp - constr); \
374 wp = newp + (wp - constr); \
378 char *constr = alloca (len);
380 const char *cp = __gconv_modules_db[cnt]->to_string;
389 else if (cp[1] == '\0')
390 /* Backslash at end of string. */
400 else if (*cp < '1' || *cp > '3')
405 if (match[idx].rm_so == -1)
409 ENSURE_LEN (match[idx].rm_eo
412 ¤t->result_set[match[idx].rm_so],
419 if (*cp == '\0' && wp != constr)
421 /* Terminate the constructed string. */
429 if (result_set != NULL)
431 /* We managed to find a derivation. First see whether
432 this is what we are looking for. */
433 if (__strcasecmp (result_set, toset) == 0
434 || (toset_expand != NULL
435 && __strcasecmp (result_set, toset_expand) == 0))
437 /* Determine the costs. If they are lower than the
438 previous solution (or this is the first solution)
439 remember this solution. */
440 int cost_hi = __gconv_modules_db[cnt]->cost_hi;
441 int cost_lo = __gconv_modules_db[cnt]->cost_lo;
442 struct derivation_step *runp = current;
443 while (runp->code != NULL)
445 cost_hi += runp->code->cost_hi;
446 cost_lo += runp->code->cost_lo;
449 if (best == NULL || cost_hi < best_cost_hi
450 || (cost_hi == best_cost_hi && cost_lo < best_cost_lo))
452 best = NEW_STEP (result_set, __gconv_modules_db[cnt],
454 best_cost_hi = cost_hi;
455 best_cost_lo = cost_lo;
460 /* Append at the end if there is no entry with this name. */
461 struct derivation_step *runp = first;
465 if (__strcasecmp (result_set, runp->result_set) == 0)
472 *lastp = NEW_STEP (result_set, __gconv_modules_db[cnt],
474 lastp = &(*lastp)->next;
480 /* Go on with the next entry. */
481 current = current->next;
485 /* We really found a way to do the transformation. Now build a data
486 structure describing the transformation steps.*/
487 result = gen_steps (best, toset_expand ?: toset, fromset_expand ?: fromset,
491 /* We haven't found a transformation. Clear the result values. */
496 /* Add result in any case to list of known derivations. */
497 add_derivation (fromset_expand ?: fromset, toset_expand ?: toset,
500 __libc_lock_unlock (lock);
508 __gconv_find_transform (const char *toset, const char *fromset,
509 struct gconv_step **handle, size_t *nsteps)
511 __libc_once_define (static, once);
512 const char *fromset_expand = NULL;
513 const char *toset_expand = NULL;
516 /* Ensure that the configuration data is read. */
517 __libc_once (once, __gconv_read_conf);
519 /* Acquire the lock. */
520 __libc_lock_lock (lock);
522 /* If we don't have a module database return with an error. */
523 if (__gconv_modules_db == NULL)
526 /* See whether the names are aliases. */
527 if (__gconv_alias_db != NULL)
529 struct gconv_alias key;
530 struct gconv_alias **found;
532 key.fromname = fromset;
533 found = __tfind (&key, &__gconv_alias_db, __gconv_alias_compare);
534 fromset_expand = found != NULL ? (*found)->toname : NULL;
536 key.fromname = toset;
537 found = __tfind (&key, &__gconv_alias_db, __gconv_alias_compare);
538 toset_expand = found != NULL ? (*found)->toname : NULL;
541 result = find_derivation (toset, toset_expand, fromset, fromset_expand,
545 /* Increment the user counter. */
546 if (result == GCONV_OK)
548 size_t cnt = *nsteps;
549 struct gconv_step *steps = *handle;
552 if (steps[--cnt].counter++ == 0)
554 steps[cnt].shlib_handle =
555 __gconv_find_shlib (steps[cnt].modname);
556 if (steps[cnt].shlib_handle == NULL)
558 /* Oops, this is the second time we use this module (after
559 unloading) and this time loading failed!? */
560 while (++cnt < *nsteps)
561 __gconv_release_shlib (steps[cnt].shlib_handle);
562 result = GCONV_NOCONV;
570 /* Release the lock. */
571 __libc_lock_unlock (lock);
573 /* The following code is necessary since `find_derivation' will return
574 GCONV_OK even when no derivation was found but the same request
575 was processed before. I.e., negative results will also be cached. */
576 return (result == GCONV_OK
577 ? (*handle == NULL ? GCONV_NOCONV : GCONV_OK)
582 /* Release the entries of the modules list. */
585 __gconv_close_transform (struct gconv_step *steps, size_t nsteps)
587 int result = GCONV_OK;
590 /* Acquire the lock. */
591 __libc_lock_lock (lock);
594 if (steps[nsteps].shlib_handle != NULL
595 && --steps[nsteps].counter == 0)
597 result = __gconv_release_shlib (steps[nsteps].shlib_handle);
598 if (result != GCONV_OK)
600 steps[nsteps].shlib_handle = NULL;
603 /* Release the lock. */
604 __libc_lock_unlock (lock);
611 /* Free all resources if necessary. */
612 static void __attribute__ ((unused))
617 if (__gconv_alias_db != NULL)
618 __tdestroy (__gconv_alias_db, free);
620 for (cnt = 0; cnt < __gconv_nmodules; ++cnt)
622 if (__gconv_modules_db[cnt]->from_regex != NULL)
623 __regfree ((regex_t *) __gconv_modules_db[cnt]->from_regex);
625 /* Modules which names do not start with a slash are builtin
626 transformations and the memory is not allocated dynamically. */
627 if (__gconv_modules_db[cnt]->module_name[0] == '/')
628 free (__gconv_modules_db[cnt]);
631 if (known_derivations != NULL)
632 __tdestroy (known_derivations, free_derivation);
635 text_set_element (__libc_subfreeres, free_mem);