aboutsummaryrefslogtreecommitdiff
path: root/iconv/gconv_db.c
diff options
context:
space:
mode:
Diffstat (limited to 'iconv/gconv_db.c')
-rw-r--r--iconv/gconv_db.c87
1 files changed, 65 insertions, 22 deletions
diff --git a/iconv/gconv_db.c b/iconv/gconv_db.c
index 5a3932c..e0a94e4 100644
--- a/iconv/gconv_db.c
+++ b/iconv/gconv_db.c
@@ -34,6 +34,9 @@ void *__gconv_alias_db;
size_t __gconv_nmodules;
struct gconv_module **__gconv_modules_db;
+/* We modify global data. */
+__libc_lock_define_initialized (static, lock)
+
/* Function for searching alias. */
int
@@ -128,9 +131,7 @@ add_derivation (const char *fromset, const char *toset,
malloc (sizeof (struct known_derivation) + fromset_len + toset_len);
if (new_deriv != NULL)
{
- new_deriv->from = memcpy ((char *) new_deriv
- + sizeof (struct known_derivation),
- fromset, fromset_len);
+ new_deriv->from = memcpy (new_deriv + 1, fromset, fromset_len);
new_deriv->to = memcpy ((char *) new_deriv->from + fromset_len,
toset, toset_len);
@@ -149,6 +150,11 @@ internal_function
free_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].end_fct)
+ (*deriv->steps[cnt].end_fct) (&deriv->steps[cnt]);
free ((struct gconv_step *) deriv->steps);
free (deriv);
@@ -189,7 +195,7 @@ gen_steps (struct derivation_step *best, const char *toset,
if (current->code->module_name[0] == '/')
{
/* Load the module, return handle for it. */
- void *shlib_handle =
+ struct gconv_loaded_object *shlib_handle =
__gconv_find_shlib (current->code->module_name);
if (shlib_handle == NULL)
@@ -199,27 +205,21 @@ gen_steps (struct derivation_step *best, const char *toset,
}
result[step_cnt].shlib_handle = shlib_handle;
-
- result[step_cnt].fct = __gconv_find_func (shlib_handle, "gconv");
- if (result[step_cnt].fct == NULL)
- {
- /* Argh, no conversion function. There is something
- wrong here. */
- __gconv_release_shlib (result[step_cnt].shlib_handle);
- failed = 1;
- break;
- }
-
- result[step_cnt].init_fct = __gconv_find_func (shlib_handle,
- "gconv_init");
- result[step_cnt].end_fct = __gconv_find_func (shlib_handle,
- "gconv_end");
+ result[step_cnt].modname = shlib_handle->name;
+ result[step_cnt].counter = 0;
+ 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;
}
else
/* It's a builtin transformation. */
__gconv_get_builtin_trans (current->code->module_name,
&result[step_cnt]);
+ /* Call the init function. */
+ if (result[step_cnt].init_fct != NULL)
+ (*result[step_cnt].init_fct) (&result[step_cnt]);
+
current = current->last;
}
@@ -227,7 +227,11 @@ gen_steps (struct derivation_step *best, const char *toset,
{
/* Something went wrong while initializing the modules. */
while (++step_cnt < *nsteps)
- __gconv_release_shlib (result[step_cnt].shlib_handle);
+ {
+ if (result[step_cnt].end_fct != NULL)
+ (*result[step_cnt].end_fct) (&result[step_cnt]);
+ __gconv_release_shlib (result[step_cnt].shlib_handle);
+ }
free (result);
*nsteps = 0;
status = GCONV_NOCONV;
@@ -273,7 +277,8 @@ find_derivation (const char *toset, const char *toset_expand,
/* ### TODO
For now we use a simple algorithm with quadratic runtime behaviour.
- The task is to match the `toset' with any of the available. */
+ The task is to match the `toset' with any of the available rules,
+ starting from FROMSET. */
if (fromset_expand != NULL)
{
first = NEW_STEP (fromset_expand, NULL, NULL);
@@ -495,6 +500,9 @@ __gconv_find_transform (const char *toset, const char *fromset,
/* Ensure that the configuration data is read. */
__libc_once (once, __gconv_read_conf);
+ /* Acquire the lock. */
+ __libc_lock_lock (lock);
+
/* If we don't have a module database return with an error. */
if (__gconv_modules_db == NULL)
return GCONV_NOCONV;
@@ -517,6 +525,33 @@ __gconv_find_transform (const char *toset, const char *fromset,
result = find_derivation (toset, toset_expand, fromset, fromset_expand,
handle, nsteps);
+ /* Increment the user counter. */
+ if (result == GCONV_OK)
+ {
+ size_t cnt = *nsteps;
+ struct gconv_step *steps = *handle;
+
+ do
+ if (steps[--cnt].counter++ == 0)
+ {
+ steps[--cnt].shlib_handle =
+ __gconv_find_shlib (steps[--cnt].modname);
+ if (steps[--cnt].shlib_handle == NULL)
+ {
+ /* Oops, this is the second time we use this module (after
+ unloading) and this time loading failed!? */
+ while (++cnt < *nsteps)
+ __gconv_release_shlib (steps[cnt].shlib_handle);
+ result = GCONV_NOCONV;
+ break;
+ }
+ }
+ while (cnt > 0);
+ }
+
+ /* Release the lock. */
+ __libc_lock_unlock (lock);
+
/* The following code is necessary since `find_derivation' will return
GCONV_OK even when no derivation was found but the same request
was processed before. I.e., negative results will also be cached. */
@@ -533,14 +568,22 @@ __gconv_close_transform (struct gconv_step *steps, size_t nsteps)
{
int result = GCONV_OK;
+ /* Acquire the lock. */
+ __libc_lock_lock (lock);
+
while (nsteps-- > 0)
- if (steps[nsteps].shlib_handle != NULL)
+ if (steps[nsteps].shlib_handle != NULL
+ && --steps[nsteps].counter == 0)
{
result = __gconv_release_shlib (steps[nsteps].shlib_handle);
if (result != GCONV_OK)
break;
+ steps[nsteps].shlib_handle = NULL;
}
+ /* Release the lock. */
+ __libc_lock_unlock (lock);
+
return result;
}