summary refs log tree commit diff
path: root/wcsmbs/wcsmbsload.c
diff options
context:
space:
mode:
Diffstat (limited to 'wcsmbs/wcsmbsload.c')
-rw-r--r--wcsmbs/wcsmbsload.c140
1 files changed, 94 insertions, 46 deletions
diff --git a/wcsmbs/wcsmbsload.c b/wcsmbs/wcsmbsload.c
index b16aa6c500..b02acad68d 100644
--- a/wcsmbs/wcsmbsload.c
+++ b/wcsmbs/wcsmbsload.c
@@ -36,64 +36,76 @@ const struct locale_data *__wcsmbs_last_locale = &_nl_C_LC_CTYPE;
 
 
 /* These are the descriptions for the default conversion functions.  */
-static struct gconv_step to_wc =
+static struct __gconv_step to_wc =
 {
-  shlib_handle: NULL,
-  modname: NULL,
-  counter: INT_MAX,
-  from_name: "ANSI_X3.4-1968//",
-  to_name: "INTERNAL",
-  fct: __gconv_transform_ascii_internal,
-  init_fct: NULL,
-  end_fct: NULL,
-  min_needed_from: 1,
-  max_needed_from: 1,
-  min_needed_to: 4,
-  max_needed_to: 4,
-  stateful: 0,
-  data: NULL
+  .__shlib_handle = NULL,
+  .__modname = NULL,
+  .__counter = INT_MAX,
+  .__from_name = "ANSI_X3.4-1968//",
+  .__to_name = "INTERNAL",
+  .__fct = __gconv_transform_ascii_internal,
+  .__init_fct = NULL,
+  .__end_fct = NULL,
+  .__min_needed_from = 1,
+  .__max_needed_from = 1,
+  .__min_needed_to = 4,
+  .__max_needed_to = 4,
+  .__stateful = 0,
+  .__data = NULL
 };
 
-static struct gconv_step to_mb =
+static struct __gconv_step to_mb =
 {
-  shlib_handle: NULL,
-  modname: NULL,
-  counter: INT_MAX,
-  from_name: "INTERNAL",
-  to_name: "ANSI_X3.4-1968//",
-  fct: __gconv_transform_internal_ascii,
-  init_fct: NULL,
-  end_fct: NULL,
-  min_needed_from: 4,
-  max_needed_from: 4,
-  min_needed_to: 1,
-  max_needed_to: 1,
-  stateful: 0,
-  data: NULL
+  .__shlib_handle = NULL,
+  .__modname = NULL,
+  .__counter = INT_MAX,
+  .__from_name = "INTERNAL",
+  .__to_name = "ANSI_X3.4-1968//",
+  .__fct = __gconv_transform_internal_ascii,
+  .__init_fct = NULL,
+  .__end_fct = NULL,
+  .__min_needed_from = 4,
+  .__max_needed_from = 4,
+  .__min_needed_to = 1,
+  .__max_needed_to = 1,
+  .__stateful = 0,
+  .__data = NULL
 };
 
 
 /* For the default locale we only have to handle ANSI_X3.4-1968.  */
 struct gconv_fcts __wcsmbs_gconv_fcts =
 {
-  towc: &to_wc,
-  tomb: &to_mb
+  .towc = &to_wc,
+  .tomb = &to_mb
 };
 
 
-static inline struct gconv_step *
+static inline struct __gconv_step *
 getfct (const char *to, const char *from)
 {
   size_t nsteps;
-  struct gconv_step *result;
+  struct __gconv_step *result;
+  size_t nstateful;
+  size_t cnt;
 
-  if (__gconv_find_transform (to, from, &result, &nsteps) != GCONV_OK)
+  if (__gconv_find_transform (to, from, &result, &nsteps) != __GCONV_OK)
     /* Loading the conversion step is not possible.  */
     return NULL;
 
-  /* We must only have one step in this conversion.  */
-  if (nsteps != 1)
-    return NULL;
+  /* Count the number of stateful conversions.  Since we will only
+     have one 'mbstate_t' object available we can only deal with one
+     stateful conversion.  */
+  nstateful = 0;
+  for (cnt = 0; cnt < nsteps; ++cnt)
+    if (result[cnt].__stateful)
+      ++nstateful;
+  if (nstateful > 1)
+    {
+      /* We cannot handle this case.  */
+      __gconv_close_transform (result, nsteps);
+      result = NULL;
+    }
 
   return result;
 }
@@ -148,14 +160,15 @@ getfct (const char *to, const char *from)
   })
 
 
+/* We must modify global data.  */
+__libc_lock_define_initialized (static, lock)
+
+
 /* Load conversion functions for the currently selected locale.  */
 void
 internal_function
 __wcsmbs_load_conv (const struct locale_data *new_category)
 {
-  /* We must modify global data.  */
-  __libc_lock_define_initialized (static, lock)
-
   /* Acquire the lock.  */
   __libc_lock_lock (lock);
 
@@ -174,6 +187,12 @@ __wcsmbs_load_conv (const struct locale_data *new_category)
 	  /* We must find the real functions.  */
 	  const char *charset_name;
 	  const char *complete_name;
+	  struct __gconv_step *new_towc;
+	  struct __gconv_step *new_tomb;
+
+	  /* Free the old conversions.  */
+	  __gconv_close_transform (__wcsmbs_gconv_fcts.tomb, 1);
+	  __gconv_close_transform (__wcsmbs_gconv_fcts.towc, 1);
 
 	  /* Get name of charset of the locale.  We first examine
 	     whether we have a character set mentioned in the locale
@@ -188,15 +207,23 @@ __wcsmbs_load_conv (const struct locale_data *new_category)
              complete lookup.  */
 	  complete_name = norm_add_slashes (charset_name);
 
-	  __wcsmbs_gconv_fcts.tomb = getfct (complete_name, "INTERNAL");
-	  __wcsmbs_gconv_fcts.towc = getfct ("INTERNAL", complete_name);
+	  new_towc = getfct ("INTERNAL", complete_name);
+	  if (new_towc != NULL)
+	    new_tomb = getfct (complete_name, "INTERNAL");
 
 	  /* If any of the conversion functions is not available we don't
 	     use any since this would mean we cannot convert back and
 	     forth.*/
-	  if (__wcsmbs_gconv_fcts.towc == NULL
-	      || __wcsmbs_gconv_fcts.tomb == NULL)
-	    goto failed;
+	  if (new_towc == NULL || new_tomb == NULL)
+	    {
+	      if (new_towc != NULL)
+		__gconv_close_transform (new_towc, 1);
+
+	      goto failed;
+	    }
+
+	  __wcsmbs_gconv_fcts.tomb = new_tomb;
+	  __wcsmbs_gconv_fcts.towc = new_towc;
 	}
 
       /* Set last-used variable for current locale.  */
@@ -205,3 +232,24 @@ __wcsmbs_load_conv (const struct locale_data *new_category)
 
   __libc_lock_unlock (lock);
 }
+
+
+/* Clone the current conversion function set.  */
+void
+internal_function
+__wcsmbs_clone_conv (struct gconv_fcts *copy)
+{
+  /* Make sure the data structures remain the same until we are finished.  */
+  __libc_lock_lock (lock);
+
+  /* Copy the data.  */
+  *copy = __wcsmbs_gconv_fcts;
+
+  /* Now increment the usage counters.  */
+  if (copy->towc->__shlib_handle != NULL)
+    ++copy->towc->__counter;
+  if (copy->tomb->__shlib_handle != NULL)
+    ++copy->tomb->__counter;
+
+  __libc_lock_unlock (lock);
+}