about summary refs log tree commit diff
path: root/iconv/gconv_db.c
diff options
context:
space:
mode:
Diffstat (limited to 'iconv/gconv_db.c')
-rw-r--r--iconv/gconv_db.c72
1 files changed, 50 insertions, 22 deletions
diff --git a/iconv/gconv_db.c b/iconv/gconv_db.c
index ceb94be2b1..b1320ac7e8 100644
--- a/iconv/gconv_db.c
+++ b/iconv/gconv_db.c
@@ -99,7 +99,7 @@ derivation_lookup (const char *fromset, const char *toset,
   struct known_derivation key = { fromset, toset, NULL, 0 };
   struct known_derivation *result;
 
-  result = __tfind (&key, known_derivations, derivation_compare);
+  result = __tfind (&key, &known_derivations, derivation_compare);
 
   if (result == NULL)
     return GCONV_NOCONV;
@@ -169,11 +169,18 @@ gen_steps (struct derivation_step *best, const char *toset,
 					 * step_cnt);
   if (result != NULL)
     {
+      int failed = 0;
+
+      *nsteps = step_cnt;
       current = best;
       while (step_cnt-- > 0)
 	{
-	  result[step_cnt].from_name = current->last->result_set;
-	  result[step_cnt].to_name = current->result_set;
+	  result[step_cnt].from_name = (step_cnt == 0
+					? __strdup (fromset)
+					: current->last->result_set);
+	  result[step_cnt].to_name = (step_cnt + 1 == *nsteps
+				      ? __strdup (current->result_set)
+				      : result[step_cnt + 1].from_name);
 
 	  if (current->code->module_name[0] == '/')
 	    {
@@ -182,7 +189,10 @@ gen_steps (struct derivation_step *best, const char *toset,
 		__gconv_find_shlib (current->code->module_name);
 
 	      if (shlib_handle == NULL)
-		break;
+		{
+		  failed = 1;
+		  break;
+		}
 
 	      result[step_cnt].shlib_handle = shlib_handle;
 
@@ -192,6 +202,7 @@ gen_steps (struct derivation_step *best, const char *toset,
 		  /* Argh, no conversion function.  There is something
 		     wrong here.  */
 		  __gconv_release_shlib (result[step_cnt].shlib_handle);
+		  failed = 1;
 		  break;
 		}
 
@@ -208,18 +219,18 @@ gen_steps (struct derivation_step *best, const char *toset,
 	  current = current->last;
 	}
 
-      if (step_cnt != 0)
+      if (failed != 0)
 	{
 	  /* Something went wrong while initializing the modules.  */
-	  while (step_cnt-- > 0)
+	  while (++step_cnt < *nsteps)
 	    __gconv_release_shlib (result[step_cnt].shlib_handle);
 	  free (result);
+	  *nsteps = 0;
 	  status = GCONV_NOCONV;
 	}
       else
 	{
 	  *handle = result;
-	  *nsteps = step_cnt;
 	  status = GCONV_OK;
 	}
     }
@@ -231,12 +242,13 @@ gen_steps (struct derivation_step *best, const char *toset,
 /* The main function: find a possible derivation from the `fromset' (either
    the given name or the alias) to the `toset' (again with alias).  */
 static int
+internal_function
 find_derivation (const char *toset, const char *toset_expand,
 		 const char *fromset, const char *fromset_expand,
 		 struct gconv_step **handle, size_t *nsteps)
 {
   __libc_lock_define_initialized (static, lock)
-  struct derivation_step *current, **lastp, *best = NULL;
+  struct derivation_step *first, *current, **lastp, *best = NULL;
   int best_cost = 0;
   int result;
 
@@ -260,16 +272,17 @@ find_derivation (const char *toset, const char *toset_expand,
      The task is to match the `toset' with any of the available.  */
   if (fromset_expand != NULL)
     {
-      current = NEW_STEP (fromset_expand, NULL, NULL);
-      current->next = NEW_STEP (fromset, NULL, NULL);
-      lastp = &current->next->next;
+      first = NEW_STEP (fromset_expand, NULL, NULL);
+      first->next = NEW_STEP (fromset, NULL, NULL);
+      lastp = &first->next->next;
     }
   else
     {
-      current = NEW_STEP (fromset, NULL, NULL);
-      lastp = &current->next;
+      first = NEW_STEP (fromset, NULL, NULL);
+      lastp = &first->next;
     }
 
+  current = first;
   while (current != NULL)
     {
       /* Now match all the available module specifications against the
@@ -419,13 +432,28 @@ find_derivation (const char *toset, const char *toset_expand,
 		}
 	      else
 		{
-		  /* Append at the end.  */
-		  *lastp = NEW_STEP (result_set, __gconv_modules_db[cnt],
-				     current);
-		  lastp = &(*lastp)->next;
+		  /* Append at the end if there is no entry with this name.  */
+		  struct derivation_step *runp = first;
+
+		  while (runp != NULL)
+		    {
+		      if (__strcasecmp (result_set, runp->result_set) == 0)
+			break;
+		      runp = runp->next;
+		    }
+
+		  if (runp == NULL)
+		    {
+		      *lastp = NEW_STEP (result_set, __gconv_modules_db[cnt],
+					 current);
+		      lastp = &(*lastp)->next;
+		    }
                 }
 	    }
 	}
+
+      /* Go on with the next entry.  */
+      current = current->next;
     }
 
   if (best != NULL)
@@ -470,15 +498,15 @@ __gconv_find_transform (const char *toset, const char *fromset,
   if (__gconv_alias_db != NULL)
     {
       struct gconv_alias key;
-      struct gconv_alias *found;
+      struct gconv_alias **found;
 
       key.fromname = fromset;
-      found = __tfind (&key, __gconv_alias_db, __gconv_alias_compare);
-      fromset_expand = found != NULL ? found->toname : NULL;
+      found = __tfind (&key, &__gconv_alias_db, __gconv_alias_compare);
+      fromset_expand = found != NULL ? (*found)->toname : NULL;
 
       key.fromname = toset;
-      found = __tfind (&key, __gconv_alias_db, __gconv_alias_compare);
-      toset_expand = found != NULL ? found->toname : NULL;
+      found = __tfind (&key, &__gconv_alias_db, __gconv_alias_compare);
+      toset_expand = found != NULL ? (*found)->toname : NULL;
     }
 
   result = find_derivation (toset, toset_expand, fromset, fromset_expand,