diff options
Diffstat (limited to 'iconv/gconv_trans.c')
-rw-r--r-- | iconv/gconv_trans.c | 180 |
1 files changed, 180 insertions, 0 deletions
diff --git a/iconv/gconv_trans.c b/iconv/gconv_trans.c index 269917b531..4d6e7766fd 100644 --- a/iconv/gconv_trans.c +++ b/iconv/gconv_trans.c @@ -18,9 +18,13 @@ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +#include <assert.h> #include <dlfcn.h> +#include <search.h> #include <stdint.h> +#include <string.h> +#include <bits/libc-lock.h> #include "gconv_int.h" #include "../locale/localeinfo.h" @@ -28,6 +32,7 @@ int __gconv_transliterate (struct __gconv_step *step, struct __gconv_step_data *step_data, + void *trans_data __attribute__ ((unused)), const unsigned char *inbufstart, const unsigned char **inbufp, const unsigned char *inbufend, @@ -218,3 +223,178 @@ __gconv_transliterate (struct __gconv_step *step, /* Haven't found a match. */ return __GCONV_ILLEGAL_INPUT; } + + +/* Structure to represent results of found (or not) transliteration + modules. */ +struct known_trans +{ + /* This structure must remain the first member. */ + struct trans_struct info; + + const char *fname; + void *handle; + int open_count; +}; + + +/* Tree with results of previous calls to __gconv_translit_find. */ +static void *search_tree; + +/* We modify global data. */ +__libc_lock_define_initialized (static, lock); + + +/* Compare two transliteration entries. */ +static int +trans_compare (const void *p1, const void *p2) +{ + struct known_trans *s1 = (struct known_trans *) p1; + struct known_trans *s2 = (struct known_trans *) p2; + + return strcmp (s1->info.name, s2->info.name); +} + + +/* Open (maybe reopen) the module named in the struct. Get the function + and data structure pointers we need. */ +static int +open_translit (struct known_trans *trans) +{ + __gconv_trans_query_fct queryfct; + + trans->handle = __libc_dlopen (trans->fname); + if (trans->handle == NULL) + /* Not available. */ + return 1; + + /* Find the required symbol. */ + queryfct = __libc_dlsym (trans->handle, "gconv_trans_context"); + if (queryfct == NULL) + { + /* We cannot live with that. */ + close_and_out: + __libc_dlclose (trans->handle); + trans->handle = NULL; + return 1; + } + + /* Get the context. */ + if (queryfct (trans->info.name, &trans->info.csnames, &trans->info.ncsnames) + != 0) + goto close_and_out; + + /* Of course we also have to have the actual function. */ + trans->info.trans_fct = __libc_dlsym (trans->handle, "gconv_trans"); + if (trans->info.trans_fct == NULL) + goto close_and_out; + + /* Now the optional functions. */ + trans->info.trans_init_fct = + __libc_dlsym (trans->handle, "gconv_trans_init"); + trans->info.trans_context_fct = + __libc_dlsym (trans->handle, "gconv_trans_context"); + trans->info.trans_end_fct = + __libc_dlsym (trans->handle, "gconv_trans_end"); + + trans->open_count = 1; + + return 0; +} + + +int +internal_function +__gconv_translit_find (struct trans_struct *trans) +{ + struct known_trans **found; + const struct path_elem *runp; + int res = 1; + + /* We have to have a name. */ + assert (trans->name != NULL); + + /* Acquire the lock. */ + __libc_lock_lock (lock); + + /* See whether we know this module already. */ + found = __tfind (trans, &search_tree, trans_compare); + if (found != NULL) + { + /* Is this module available? */ + if ((*found)->handle != NULL) + { + /* Maybe we have to reopen the file. */ + if ((*found)->handle != (void *) -1) + /* The object is not unloaded. */ + res = 0; + else if (open_translit (*found) == 0) + { + /* Copy the data. */ + *trans = (*found)->info; + res = 0; + } + } + } + else + { + size_t name_len = strlen (trans->name) + 1; + int need_so = 0; + struct known_trans *newp; + + /* We have to continue looking for the module. */ + if (__gconv_path_elem == NULL) + __gconv_get_path (); + + /* See whether we have to append .so. */ + if (name_len <= 3 || memcmp (&trans->name[name_len - 3], ".so", 3) != 0) + need_so = 1; + + /* Create a new entry. */ + newp = (struct known_trans *) malloc (sizeof (struct known_trans) + + (__gconv_max_path_elem_len + + name_len + 3) + + name_len); + if (newp != NULL) + { + char *cp; + + /* Clear the struct. */ + memset (newp, '\0', sizeof (struct known_trans)); + + /* Store a copy of the module name. */ + newp->info.name = (char *) (newp + 1); + cp = __mempcpy ((char *) newp->info.name, trans->name, name_len); + + newp->fname = cp; + + /* Seach in all the directories. */ + for (runp = __gconv_path_elem; runp->name != NULL; ++runp) + { + cp = __mempcpy (__stpcpy ((char *) newp->fname, runp->name), + trans->name, name_len); + if (need_so) + memcpy (cp, ".so", sizeof (".so")); + + if (open_translit (newp) == 0) + { + /* We found a module. */ + res = 0; + break; + } + } + + /* In any case we'll add the entry to our search tree. */ + if (__tsearch (newp, &search_tree, trans_compare) == NULL) + { + /* Yickes, this should not happen. Unload the object. */ + res = 1; + /* XXX unload here. */ + } + } + } + + __libc_lock_unlock (lock); + + return res; +} |