about summary refs log tree commit diff
path: root/iconv
diff options
context:
space:
mode:
Diffstat (limited to 'iconv')
-rw-r--r--iconv/gconv.c3
-rw-r--r--iconv/gconv.h1
-rw-r--r--iconv/gconv_builtin.h6
-rw-r--r--iconv/gconv_close.c3
-rw-r--r--iconv/gconv_conf.c158
-rw-r--r--iconv/gconv_db.c72
-rw-r--r--iconv/gconv_dl.c5
-rw-r--r--iconv/gconv_open.c26
-rw-r--r--iconv/gconv_simple.c4
-rw-r--r--iconv/iconv.c38
-rw-r--r--iconv/iconv_close.c7
-rw-r--r--iconv/iconv_open.c13
12 files changed, 211 insertions, 125 deletions
diff --git a/iconv/gconv.c b/iconv/gconv.c
index 0cbb052a6d..5df16354b6 100644
--- a/iconv/gconv.c
+++ b/iconv/gconv.c
@@ -30,6 +30,9 @@ __gconv (gconv_t cd, const char **inbuf, size_t *inbytesleft, char **outbuf,
   size_t oldinbytes = *inbytesleft;
   int result;
 
+  if (cd == (gconv_t) -1L)
+    return GCONV_ILLEGAL_DESCRIPTOR;
+
   cd->data[last_step].outbuf = *outbuf;
   cd->data[last_step].outbufavail = 0;
   cd->data[last_step].outbufsize = *outbytesleft;
diff --git a/iconv/gconv.h b/iconv/gconv.h
index 2c42f99ace..879db49e1c 100644
--- a/iconv/gconv.h
+++ b/iconv/gconv.h
@@ -37,6 +37,7 @@ enum
   GCONV_EMPTY_INPUT,
   GCONV_FULL_OUTPUT,
   GCONV_ILLEGAL_INPUT,
+  GCONV_INCOMPLETE_INPUT,
 
   GCONV_ILLEGAL_DESCRIPTOR,
   GCONV_INTERNAL_ERROR
diff --git a/iconv/gconv_builtin.h b/iconv/gconv_builtin.h
index a3070a532f..8dcc3aaaa2 100644
--- a/iconv/gconv_builtin.h
+++ b/iconv/gconv_builtin.h
@@ -18,17 +18,17 @@
    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    Boston, MA 02111-1307, USA.  */
 
-BUILTIN_TRANSFORMATION ("\\([^/]+\\)/UCS4/\\([^/]*\\)", NULL, 0,
+BUILTIN_TRANSFORMATION ("([^/]+)/UCS4/([^/]*)", NULL, 0,
 			"\\1/UTF8/\\2", 1, "=ucs4->utf8",
 			__gconv_transform_ucs4_utf8,
 			__gconv_transform_init_rstate,
 			__gconv_transform_end_rstate)
 
-BUILTIN_TRANSFORMATION ("\\([^/]+\\)/UTF8/\\([^/]*\\)", NULL, 0,
+BUILTIN_TRANSFORMATION ("([^/]+)/UTF8/([^/]*)", NULL, 0,
 			"\\1/UCS4/\\2", 1, "=utf8->ucs4",
 			__gconv_transform_utf8_ucs4,
 			__gconv_transform_init_rstate,
 			__gconv_transform_end_rstate)
 
-BUILTIN_TRANSFORMATION ("\\(.*\\)", NULL, 0, "\\1", 1, "=dummy",
+BUILTIN_TRANSFORMATION ("(.*)", NULL, 0, "\\1", 1, "=dummy",
 			__gconv_transform_dummy, NULL, NULL)
diff --git a/iconv/gconv_close.c b/iconv/gconv_close.c
index d3d023f488..791c0259a3 100644
--- a/iconv/gconv_close.c
+++ b/iconv/gconv_close.c
@@ -48,9 +48,8 @@ __gconv_close (gconv_t cd)
 
       /* Next step.  */
       ++srunp;
-      ++drunp;
     }
-  while (!drunp->is_last);
+  while (!(drunp++)->is_last);
 
   /* Save the pointer, we need it below.  */
   srunp = cd->steps;
diff --git a/iconv/gconv_conf.c b/iconv/gconv_conf.c
index d3c13d75c9..8a72c7aac9 100644
--- a/iconv/gconv_conf.c
+++ b/iconv/gconv_conf.c
@@ -19,6 +19,7 @@
    Boston, MA 02111-1307, USA.  */
 
 #include <ctype.h>
+#include <errno.h>
 #include <gconv.h>
 #include <search.h>
 #include <stdio.h>
@@ -35,6 +36,12 @@ static const char default_gconv_path[] = GCONV_PATH;
    along the path.  */
 static const char gconv_conf_filename[] = "gconv-modules";
 
+/* Filename extension for the modules.  */
+#ifndef MODULE_EXT
+# define MODULE_EXT ".so"
+#endif
+static const char gconv_module_ext[] = MODULE_EXT;
+
 /* We have a few builtin transformations.  */
 static struct gconv_module builtin_modules[] =
 {
@@ -111,15 +118,18 @@ add_alias (char *rp)
 
   new_alias = (struct gconv_alias *)
     malloc (sizeof (struct gconv_alias) + (wp - from));
-  new_alias->fromname = memcpy ((char *) new_alias
-				+ sizeof (struct gconv_alias),
-				from, to - from);
-  new_alias->toname = memcpy ((char *) new_alias + sizeof (struct gconv_alias)
-			      + (to - from), to, wp - to);
-
-  if (__tsearch (new_alias, &__gconv_alias_db, __gconv_alias_compare) == NULL)
-    /* Something went wrong, free this entry.  */
-    free (new_alias);
+  if (new_alias != NULL)
+    {
+      new_alias->fromname = memcpy ((char *) new_alias
+				    + sizeof (struct gconv_alias),
+				    from, wp - from);
+      new_alias->toname = new_alias->fromname + (to - from);
+
+      if (__tsearch (new_alias, &__gconv_alias_db, __gconv_alias_compare)
+	  == NULL)
+	/* Something went wrong, free this entry.  */
+	free (new_alias);
+    }
 }
 
 
@@ -138,6 +148,7 @@ add_module (char *rp, const char *directory, size_t dir_len, void **modules,
   char *from, *to, *module, *wp;
   size_t const_len;
   int from_is_regex;
+  int need_ext;
   int cost;
 
   while (isspace (*rp))
@@ -195,65 +206,68 @@ add_module (char *rp, const char *directory, size_t dir_len, void **modules,
     /* Increment by one for the slash.  */
     ++dir_len;
 
+  /* See whether we must add the ending.  */
+  need_ext = 0;
+  if (wp - module < sizeof (gconv_module_ext)
+      || memcmp (wp - sizeof (gconv_module_ext), gconv_module_ext,
+		 sizeof (gconv_module_ext)) != 0)
+    /* We must add the module extension.  */
+    need_ext = sizeof (gconv_module_ext) - 1;
+
   /* We've collected all the information, now create an entry.  */
 
-  const_len = 0;
   if (from_is_regex)
-    do
-      ++const_len;
-    while (isalnum (from[const_len]) || from[const_len] == '-'
-	   || from[const_len] == '/' || from[const_len] == '.'
-	   || from[const_len] == '_');
+    {
+      const_len = 0;
+      while (isalnum (from[const_len]) || from[const_len] == '-'
+	     || from[const_len] == '/' || from[const_len] == '.'
+	     || from[const_len] == '_')
+	++const_len;
+    }
+  else
+    const_len = to - from - 1;
 
   new_module = (struct gconv_module *) malloc (sizeof (struct gconv_module)
-					       + (wp - from) + const_len
-					       + dir_len);
+					       + (wp - from)
+					       + dir_len + need_ext);
   if (new_module != NULL)
     {
+      char *tmp;
+
+      new_module->from_constpfx = memcpy ((char *) new_module
+					  + sizeof (struct gconv_module),
+					  from, to - from);
       if (from_is_regex)
-	{
-	  new_module->from_pattern = memcpy ((char *) new_module
-					     + sizeof (struct gconv_module),
-					     from, to - from);
-	  new_module->from_constpfx = memcpy ((char *) new_module->from_pattern
-					      + (to - from),
-					      from, const_len);
-	  ((char *) new_module->from_constpfx)[const_len] = '\0';
-	  new_module->from_constpfx_len = const_len;
-	  ++const_len;
-	}
+	new_module->from_pattern = new_module->from_constpfx;
       else
-	{
-	  new_module->from_pattern = NULL;
-	  new_module->from_constpfx = memcpy ((char *) new_module
-					      + sizeof (struct gconv_module),
-					      from, to - from);
-	  new_module->from_constpfx_len = to - from - 1;
-	  const_len = to - from;
-	}
+	new_module->from_pattern = NULL;
+
+      new_module->from_constpfx_len = const_len;
+
       new_module->from_regex = NULL;
 
       new_module->to_string = memcpy ((char *) new_module->from_constpfx
-				      + const_len + 1, to, module - to);
+				      + (to - from), to, module - to);
 
       new_module->cost = cost;
 
+      new_module->module_name = (char *) new_module->to_string + (module - to);
+
       if (dir_len == 0)
-	new_module->module_name = memcpy ((char *) new_module->to_string
-					  + (module - to),
-					  module, wp - module);
+	tmp = (char *) new_module->module_name;
       else
 	{
-	  char *tmp;
-	  new_module->module_name = ((char *) new_module->to_string
-				     + (module - to));
 	  tmp = __mempcpy ((char *) new_module->module_name,
 			   directory, dir_len - 1);
 	  *tmp++ = '/';
-	  memcpy (tmp, module, wp - module);
 	}
 
-      if (__tfind (new_module, *modules, module_compare) != NULL)
+      tmp = __mempcpy (tmp, module, wp - module);
+
+      if (need_ext)
+	memcpy (tmp - 1, gconv_module_ext, sizeof (gconv_module_ext));
+
+      if (__tfind (new_module, modules, module_compare) == NULL)
 	if (__tsearch (new_module, modules, module_compare) == NULL)
 	  /* Something went wrong while inserting the new module.  */
 	  free (new_module);
@@ -267,7 +281,7 @@ static void
 insert_module (const void *nodep, VISIT value, int level)
 {
   if (value == preorder || value == leaf)
-    __gconv_modules_db[__gconv_nmodules++] = (struct gconv_module *) nodep;
+    __gconv_modules_db[__gconv_nmodules++] = *(struct gconv_module **) nodep;
 }
 
 static void
@@ -302,8 +316,6 @@ read_conf_file (const char *filename, const char *directory, size_t dir_len,
 	break;
 
       rp = line;
-      while (isspace (*rp))
-	++rp;
       /* Terminate the line (excluding comments or newline) by an NUL byte
 	 to simplify the following code.  */
       endp = strchr (rp, '#');
@@ -316,6 +328,9 @@ read_conf_file (const char *filename, const char *directory, size_t dir_len,
 	    *endp = '\0';
 	}
 
+      while (isspace (*rp))
+	++rp;
+
       /* If this is an empty line go on with the next one.  */
       if (rp == endp)
 	continue;
@@ -325,10 +340,10 @@ read_conf_file (const char *filename, const char *directory, size_t dir_len,
 	++rp;
 
       if (rp - word == sizeof ("alias") - 1
-	  && memcpy (word, "alias", sizeof ("alias") - 1) == 0)
+	  && memcmp (word, "alias", sizeof ("alias") - 1) == 0)
 	add_alias (rp);
       else if (rp - word == sizeof ("module") - 1
-	       && memcpy (word, "module", sizeof ("module") - 1) == 0)
+	       && memcmp (word, "module", sizeof ("module") - 1) == 0)
 	add_module (rp, directory, dir_len, modules, nmodules);
       /* else */
 	/* Otherwise ignore the line.  */
@@ -349,6 +364,7 @@ __gconv_read_conf (void)
   char *gconv_path, *elem;
   void *modules = NULL;
   size_t nmodules = 0;
+  int save_errno = errno;
 
   if (user_path == NULL)
     /* No user-defined path.  Make a modifiable copy of the default path.  */
@@ -390,31 +406,31 @@ __gconv_read_conf (void)
 
   /* If the configuration files do not contain any valid module specification
      remember this by setting the pointer to the module array to NULL.  */
-  nmodules = sizeof (builtin_modules) / sizeof (struct gconv_module);
+  nmodules += sizeof (builtin_modules) / sizeof (builtin_modules[0]);
   if (nmodules == 0)
+    __gconv_modules_db = NULL;
+  else
     {
-      __gconv_modules_db = NULL;
-      return;
-    }
+      __gconv_modules_db =
+	(struct gconv_module **) malloc (nmodules
+					 * sizeof (struct gconv_module));
+      if (__gconv_modules_db != NULL)
+	{
+	  size_t cnt;
 
-  __gconv_modules_db =
-    (struct gconv_module **) malloc (nmodules * sizeof (struct gconv_module));
-  if (__gconv_modules_db == NULL)
-    /* We cannot do anything.  */
-    return;
+	  /* Insert all module entries into the array.  */
+	  __twalk (modules, insert_module);
 
-  /* First insert the builtin transformations.  */
-  while (__gconv_nmodules < (sizeof (builtin_modules)
-			     / sizeof (struct gconv_module)))
-    {
-      __gconv_modules_db[__gconv_nmodules] =
-	&builtin_modules[__gconv_nmodules];
-      ++__gconv_nmodules;
-    }
+	  /* No remove the tree data structure.  */
+	  __tdestroy (modules, nothing);
 
-  /* Insert all module entries into the array.  */
-  __twalk (modules, insert_module);
+	  /* Finally insert the builtin transformations.  */
+	  for (cnt = 0; cnt < (sizeof (builtin_modules)
+			       / sizeof (struct gconv_module)); ++cnt)
+	    __gconv_modules_db[__gconv_nmodules++] = &builtin_modules[cnt];
+	}
+    }
 
-  /* No remove the tree data structure.  */
-  __tdestroy (modules, nothing);
+  /* Restore the error number.  */
+  __set_errno (save_errno);
 }
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,
diff --git a/iconv/gconv_dl.c b/iconv/gconv_dl.c
index a0003a82db..a80e5ef4ef 100644
--- a/iconv/gconv_dl.c
+++ b/iconv/gconv_dl.c
@@ -84,6 +84,7 @@ do_open (void *a)
 
 
 static int
+internal_function
 dlerror_run (void (*operate) (void *), void *args)
 {
   char *last_errstring = NULL;
@@ -156,7 +157,7 @@ __gconv_find_shlib (const char *name)
      enough to a pointer to our structure to use as a lookup key that
      will be passed to `known_compare' (above).  */
 
-  found = __tfind (&name, loaded, known_compare);
+  found = __tfind (&name, &loaded, known_compare);
   if (found == NULL)
     {
       /* This name was not known before.  */
@@ -208,7 +209,7 @@ static void *release_handle;
 static void
 do_release_shlib (const void *nodep, VISIT value, int level)
 {
-  struct loaded_object *obj = (struct loaded_object *) nodep;
+  struct loaded_object *obj = *(struct loaded_object **) nodep;
 
   if (value != preorder && value != leaf)
     return;
diff --git a/iconv/gconv_open.c b/iconv/gconv_open.c
index 0e789df896..343cb70773 100644
--- a/iconv/gconv_open.c
+++ b/iconv/gconv_open.c
@@ -69,19 +69,19 @@ __gconv_open (const char *toset, const char *fromset, gconv_t *handle)
 		      if (res != GCONV_OK)
 			break;
 		    }
-		  else
-		    if (!data[cnt].is_last)
-		      {
-			data[cnt].outbufsize = GCONV_DEFAULT_BUFSIZE;
-			data[cnt].outbuf =
-			  (char *) malloc (data[cnt].outbufsize);
-			if (data[cnt].outbuf == NULL)
-			  {
-			    res = GCONV_NOMEM;
-			    break;
-			  }
-			data[cnt].outbufavail = 0;
-		      }
+
+		  if (!data[cnt].is_last && data[cnt].outbuf == NULL)
+		    {
+		      data[cnt].outbufsize = GCONV_DEFAULT_BUFSIZE;
+		      data[cnt].outbuf =
+			(char *) malloc (data[cnt].outbufsize);
+		      if (data[cnt].outbuf == NULL)
+			{
+			  res = GCONV_NOMEM;
+			  break;
+			}
+		      data[cnt].outbufavail = 0;
+		    }
 		}
 	    }
 	}
diff --git a/iconv/gconv_simple.c b/iconv/gconv_simple.c
index 582c6f5a27..f769795273 100644
--- a/iconv/gconv_simple.c
+++ b/iconv/gconv_simple.c
@@ -117,7 +117,7 @@ __gconv_transform_ucs4_utf8 (struct gconv_step *step,
       do
 	{
 	  const char *newinbuf = inbuf;
-	  size_t actually = __wcsnrtombs (&data->outbuf[data->outbufavail],
+	  size_t actually = __wmemrtombs (&data->outbuf[data->outbufavail],
 					  (const wchar_t **) &newinbuf,
 					  *inlen / sizeof (wchar_t),
 					  data->outbufsize - data->outbufavail,
@@ -206,7 +206,7 @@ __gconv_transform_utf8_ucs4 (struct gconv_step *step,
       do
 	{
 	  const char *newinbuf = inbuf;
-	  size_t actually = __mbsnrtowcs ((wchar_t *) &data->outbuf[data->outbufavail],
+	  size_t actually = __wmemrtowcs ((wchar_t *) &data->outbuf[data->outbufavail],
 					  &newinbuf, *inlen,
 					  ((data->outbufsize
 					    - data->outbufavail)
diff --git a/iconv/iconv.c b/iconv/iconv.c
index e5b0eb7c0d..8804e851b6 100644
--- a/iconv/iconv.c
+++ b/iconv/iconv.c
@@ -19,9 +19,12 @@
    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    Boston, MA 02111-1307, USA.  */
 
+#include <errno.h>
 #include <iconv.h>
 #include <gconv.h>
 
+#include <assert.h>
+
 
 size_t
 iconv (iconv_t cd, const char **inbuf, size_t *inbytesleft, char **outbuf,
@@ -29,10 +32,39 @@ iconv (iconv_t cd, const char **inbuf, size_t *inbytesleft, char **outbuf,
 {
   gconv_t gcd = (gconv_t) cd;
   size_t converted;
+  int result;
+
+  result = __gconv (gcd, inbuf, inbytesleft, outbuf, outbytesleft, &converted);
+  switch (result)
+    {
+    case GCONV_ILLEGAL_DESCRIPTOR:
+      __set_errno (EBADF);
+      converted = (size_t) -1L;
+      break;
+
+    case GCONV_ILLEGAL_INPUT:
+      __set_errno (EILSEQ);
+      converted = (size_t) -1L;
+      break;
+
+    case GCONV_FULL_OUTPUT:
+      __set_errno (E2BIG);
+      converted = (size_t) -1L;
+      break;
+
+    case GCONV_INCOMPLETE_INPUT:
+      __set_errno (EINVAL);
+      converted = (size_t) -1L;
+      break;
+
+    case GCONV_EMPTY_INPUT:
+    case GCONV_OK:
+      /* Nothing.  */
+      break;
 
-  if (__gconv (gcd, inbuf, inbytesleft, outbuf, outbytesleft, &converted)
-      != GCONV_OK)
-    return (size_t) -1;
+    default:
+      assert (!"Nothing like this should happen");
+    }
 
   return converted;
 }
diff --git a/iconv/iconv_close.c b/iconv/iconv_close.c
index d3123e21d8..ccd9d5f3ad 100644
--- a/iconv/iconv_close.c
+++ b/iconv/iconv_close.c
@@ -18,6 +18,7 @@
    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    Boston, MA 02111-1307, USA.  */
 
+#include <errno.h>
 #include <iconv.h>
 
 #include <gconv.h>
@@ -26,5 +27,11 @@
 int
 iconv_close (iconv_t cd)
 {
+  if (cd == (iconv_t *) -1L)
+    {
+      __set_errno (EBADF);
+      return -1;
+    }
+
   return __gconv_close ((gconv_t) cd) ? -1 : 0;
 }
diff --git a/iconv/iconv_open.c b/iconv/iconv_open.c
index 82802b7451..bfff00d917 100644
--- a/iconv/iconv_open.c
+++ b/iconv/iconv_open.c
@@ -27,11 +27,9 @@
 
 
 static inline void
-strip (char *s)
+strip (char *wp, const char *s)
 {
   int slash_count = 0;
-  char *wp;
-  wp = s;
 
   while (*s != '\0')
     {
@@ -39,7 +37,7 @@ strip (char *s)
 	*wp++ = *s;
       else if (*s == '/')
 	{
-	  if (++slash_count == 2)
+	  if (++slash_count == 3)
 	    break;
 	  *wp++ = '/';
 	}
@@ -67,14 +65,15 @@ iconv_open (const char *tocode, const char *fromcode)
      '_', '-', '/', and '.'.  */
   tocode_len = strlen (tocode);
   tocode_conv = alloca (tocode_len + 3);
-  strip (memcpy (tocode_conv, tocode, tocode_len + 1));
+  strip (tocode_conv, tocode);
 
   fromcode_len = strlen (fromcode);
   fromcode_conv = alloca (fromcode_len + 3);
-  strip (memcpy (fromcode_conv, fromcode, fromcode_len + 1));
+  strip (fromcode_conv, fromcode);
 
   res = __gconv_open (tocode_conv[2] == '\0' ? tocode : tocode_conv,
-		      fromcode_conv[2] == '\0' ? fromcode, fromcode_conv, &cd);
+		      fromcode_conv[2] == '\0' ? fromcode : fromcode_conv,
+		      &cd);
 
   if (res != GCONV_OK)
     {