📄 gconv_db.c
字号:
/* Provide access to the collection of available transformation modules. Copyright (C) 1997,98,99,2000,2001 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */#include <limits.h>#include <search.h>#include <stdlib.h>#include <string.h>#include <sys/param.h>#include <dirent.h>#include <dlfcn.h>#include <gconv_int.h>#include <gconv_charset.h>/* Simple data structure for alias mapping. We have two names, `from' and `to'. */void *__gconv_alias_db;/* Array with available modules. */struct gconv_module *__gconv_modules_db;/* We modify global data. */__LOCK_INIT(static, lock);/* Function for searching alias. */int__gconv_alias_compare (const void *p1, const void *p2){ const struct gconv_alias *s1 = (const struct gconv_alias *) p1; const struct gconv_alias *s2 = (const struct gconv_alias *) p2; return strcmp (s1->fromname, s2->fromname);}/* To search for a derivation we create a list of intermediate steps. Each element contains a pointer to the element which precedes it in the derivation order. */struct derivation_step{ const char *result_set; size_t result_set_len; int cost_lo; int cost_hi; struct gconv_module *code; struct derivation_step *last; struct derivation_step *next;};#define NEW_STEP(result, hi, lo, module, last_mod) \ ({ struct derivation_step *newp = alloca (sizeof (struct derivation_step)); \ newp->result_set = result; \ newp->result_set_len = strlen (result); \ newp->cost_hi = hi; \ newp->cost_lo = lo; \ newp->code = module; \ newp->last = last_mod; \ newp->next = NULL; \ newp; })/* If a specific transformation is used more than once we should not need to start looking for it again. Instead cache each successful result. */struct known_derivation{ const char *from; const char *to; struct __gconv_step *steps; size_t nsteps;};/* Compare function for database of found derivations. */static intderivation_compare (const void *p1, const void *p2){ const struct known_derivation *s1 = (const struct known_derivation *) p1; const struct known_derivation *s2 = (const struct known_derivation *) p2; int result; result = strcmp (s1->from, s2->from); if (result == 0) result = strcmp (s1->to, s2->to); return result;}/* The search tree for known derivations. */static void *known_derivations;/* Look up whether given transformation was already requested before. */static intinternal_functionderivation_lookup (const char *fromset, const char *toset, struct __gconv_step **handle, size_t *nsteps){ struct known_derivation key = { fromset, toset, NULL, 0 }; struct known_derivation **result; result = tfind (&key, &known_derivations, derivation_compare); if (result == NULL) return __GCONV_NOCONV; *handle = (*result)->steps; *nsteps = (*result)->nsteps; /* Please note that we return GCONV_OK even if the last search for this transformation was unsuccessful. */ return __GCONV_OK;}/* Add new derivation to list of known ones. */static voidinternal_functionadd_derivation (const char *fromset, const char *toset, struct __gconv_step *handle, size_t nsteps){ struct known_derivation *new_deriv; size_t fromset_len = strlen (fromset) + 1; size_t toset_len = strlen (toset) + 1; new_deriv = (struct known_derivation *) malloc (sizeof (struct known_derivation) + fromset_len + toset_len); if (new_deriv != NULL) { char *tmp; new_deriv->from = (char *) (new_deriv + 1); tmp = memcpy (new_deriv + 1, fromset, fromset_len); tmp += fromset_len; new_deriv->to = memcpy (tmp, toset, toset_len); new_deriv->steps = handle; new_deriv->nsteps = nsteps; if (tsearch (new_deriv, &known_derivations, derivation_compare) == NULL) /* There is some kind of memory allocation problem. */ free (new_deriv); } /* Please note that we don't complain if the allocation failed. This is not tragically but in case we use the memory debugging facilities not all memory will be freed. */}static voidfree_derivation (void *p){ struct known_derivation *deriv = (struct known_derivation *) p; size_t cnt; for (cnt = 0; cnt < deriv->nsteps; ++cnt) if (deriv->steps[cnt].__counter > 0 && deriv->steps[cnt].__end_fct != NULL) deriv->steps[cnt].__end_fct (&deriv->steps[cnt]); /* Free the name strings. */ free ((char *) deriv->steps[0].__from_name); free ((char *) deriv->steps[deriv->nsteps - 1].__to_name); free ((struct __gconv_step *) deriv->steps); free (deriv);}/* Decrement the reference count for a single step in a steps array. */voidinternal_function__gconv_release_step (struct __gconv_step *step){ if (--step->__counter == 0) { /* Call the destructor. */ if (step->__end_fct != NULL) step->__end_fct (step);#ifndef STATIC_GCONV /* Skip builtin modules; they are not reference counted. */ if (step->__shlib_handle != NULL) { /* Release the loaded module. */ __gconv_release_shlib (step->__shlib_handle); step->__shlib_handle = NULL; }#endif }}static intinternal_functiongen_steps (struct derivation_step *best, const char *toset, const char *fromset, struct __gconv_step **handle, size_t *nsteps){ size_t step_cnt = 0; struct __gconv_step *result; struct derivation_step *current; int status = __GCONV_NOMEM; /* First determine number of steps. */ for (current = best; current->last != NULL; current = current->last) ++step_cnt; result = (struct __gconv_step *) malloc (sizeof (struct __gconv_step) * step_cnt); if (result != NULL) { int failed = 0; status = __GCONV_OK; *nsteps = step_cnt; current = best; while (step_cnt-- > 0) { result[step_cnt].__from_name = (step_cnt == 0 ? strdup (fromset) : (char *)current->last->result_set); result[step_cnt].__to_name = (step_cnt + 1 == *nsteps ? strdup (current->result_set) : result[step_cnt + 1].__from_name); result[step_cnt].__counter = 1; result[step_cnt].__data = NULL;#ifndef STATIC_GCONV if (current->code->module_name[0] == '/') { /* Load the module, return handle for it. */ struct __gconv_loaded_object *shlib_handle = __gconv_find_shlib (current->code->module_name); if (shlib_handle == NULL) { failed = 1; break; } result[step_cnt].__shlib_handle = shlib_handle; result[step_cnt].__modname = shlib_handle->name; result[step_cnt].__fct = shlib_handle->fct; result[step_cnt].__init_fct = shlib_handle->init_fct; result[step_cnt].__end_fct = shlib_handle->end_fct; /* Call the init function. */ if (result[step_cnt].__init_fct != NULL) { status = result[step_cnt].__init_fct (&result[step_cnt]); if (__builtin_expect (status, __GCONV_OK) != __GCONV_OK) { failed = 1; /* Make sure we unload this modules. */ --step_cnt; result[step_cnt].__end_fct = NULL; break; } } } else#endif /* It's a builtin transformation. */ __gconv_get_builtin_trans (current->code->module_name, &result[step_cnt]); current = current->last; } if (__builtin_expect (failed, 0) != 0) { /* Something went wrong while initializing the modules. */ while (++step_cnt < *nsteps) __gconv_release_step (&result[step_cnt]); free (result); *nsteps = 0; *handle = NULL; if (status == __GCONV_OK) status = __GCONV_NOCONV; } else *handle = result; } else { *nsteps = 0; *handle = NULL; } return status;}#ifndef STATIC_GCONVstatic intinternal_functionincrement_counter (struct __gconv_step *steps, size_t nsteps){ /* Increment the user counter. */ size_t cnt = nsteps; int result = __GCONV_OK; while (cnt-- > 0) { struct __gconv_step *step = &steps[cnt]; if (step->__counter++ == 0) { /* Skip builtin modules. */ if (step->__modname != NULL) { /* Reopen a previously used module. */ step->__shlib_handle = __gconv_find_shlib (step->__modname); if (step->__shlib_handle == NULL) { /* Oops, this is the second time we use this module (after unloading) and this time loading failed!? */ --step->__counter; while (++cnt < nsteps) __gconv_release_step (&steps[cnt]); result = __GCONV_NOCONV; break; } /* The function addresses defined by the module may have changed. */ step->__fct = step->__shlib_handle->fct; step->__init_fct = step->__shlib_handle->init_fct; step->__end_fct = step->__shlib_handle->end_fct; } if (step->__init_fct != NULL) step->__init_fct (step); } } return result;}#endif/* The main function: find a possible derivation from the `fromset' (either the given name or the alias) to the `toset' (again with alias). */static intinternal_functionfind_derivation (const char *toset, const char *toset_expand, const char *fromset, const char *fromset_expand, struct __gconv_step **handle, size_t *nsteps){ struct derivation_step *first, *current, **lastp, *solution = NULL; int best_cost_hi = INT_MAX; int best_cost_lo = INT_MAX; int result; /* Look whether an earlier call to `find_derivation' has already computed a possible derivation. If so, return it immediately. */ result = derivation_lookup (fromset_expand ?: fromset, toset_expand ?: toset, handle, nsteps); if (result == __GCONV_OK) {#ifndef STATIC_GCONV result = increment_counter (*handle, *nsteps);#endif return result; } /* The task is to find a sequence of transformations, backed by the existing modules - whether builtin or dynamically loadable -, starting at `fromset' (or `fromset_expand') and ending at `toset' (or `toset_expand'), and with minimal cost. For computer scientists, this is a shortest path search in the graph where the nodes are all possible charsets and the edges are the transformations listed in __gconv_modules_db. For now we use a simple algorithm with quadratic runtime behaviour. A breadth-first search, starting at `fromset' and `fromset_expand'. The list starting at `first' contains all nodes that have been visited up to now, in the order in which they have been visited -- excluding the goal nodes `toset' and `toset_expand' which get managed in the list starting at `solution'. `current' walks through the list starting at `first' and looks which nodes are reachable from the current node, adding them to the end of the list [`first' or `solution' respectively] (if they are visited the first time) or updating them in place (if they have have already been visited). In each node of either list, cost_lo and cost_hi contain the minimum cost over any paths found up to now, starting at `fromset'
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -