about summary refs log tree commit diff
path: root/locale
diff options
context:
space:
mode:
Diffstat (limited to 'locale')
-rw-r--r--locale/duplocale.c51
-rw-r--r--locale/freelocale.c19
-rw-r--r--locale/newlocale.c118
-rw-r--r--locale/xlocale.h4
4 files changed, 99 insertions, 93 deletions
diff --git a/locale/duplocale.c b/locale/duplocale.c
index 867232e5a9..2fa29d14d6 100644
--- a/locale/duplocale.c
+++ b/locale/duplocale.c
@@ -35,54 +35,47 @@ __duplocale (__locale_t dataset)
 {
   __locale_t result;
   int cnt;
+  size_t names_len = 0;
 
-  /* We modify global data.  */
-  __libc_lock_lock (__libc_setlocale_lock);
+  /* Calculate the total space we need to store all the names.  */
+  for (cnt = 0; cnt < __LC_LAST; ++cnt)
+    if (cnt != LC_ALL && dataset->__names[cnt] != _nl_C_name)
+      names_len += strlen (dataset->__names[cnt]) + 1;
 
   /* Get memory.  */
-  result = (__locale_t) malloc (sizeof (struct __locale_struct));
-
-  if (result != NULL)
-    /* Duplicate the names in a separate loop first so we can
-       bail out if strdup fails and not have touched usage_counts.  */
-    for (cnt = 0; cnt < __LC_LAST; ++cnt)
-      if (cnt != LC_ALL)
-	{
-	  if (dataset->__names[cnt] == _nl_C_name)
-	    result->__names[cnt] = _nl_C_name;
-	  else
-	    {
-	      result->__names[cnt] = __strdup (dataset->__names[cnt]);
-	      if (result->__names[cnt] == NULL)
-		{
-		  while (cnt-- > 0)
-		    if (result->__names[cnt] != _nl_C_name)
-		      free ((char *) result->__names[cnt]);
-		  free (result);
-		  result = NULL;
-		  break;
-		}
-	    }
-	}
+  result = malloc (sizeof (struct __locale_struct) + names_len);
 
   if (result != NULL)
     {
+      char *namep = (char *) (result + 1);
+
+      /* We modify global data (the usage counts).  */
+      __libc_lock_lock (__libc_setlocale_lock);
+
       for (cnt = 0; cnt < __LC_LAST; ++cnt)
 	if (cnt != LC_ALL)
 	  {
 	    result->__locales[cnt] = dataset->__locales[cnt];
 	    if (result->__locales[cnt]->usage_count < MAX_USAGE_COUNT)
 	      ++result->__locales[cnt]->usage_count;
+
+	    if (dataset->__names[cnt] == _nl_C_name)
+	      result->__names[cnt] = _nl_C_name;
+	    else
+	      {
+		result->__names[cnt] = namep;
+		namep = __stpcpy (namep, dataset->__names[cnt]) + 1;
+	      }
 	  }
 
       /* Update the special members.  */
       result->__ctype_b = dataset->__ctype_b;
       result->__ctype_tolower = dataset->__ctype_tolower;
       result->__ctype_toupper = dataset->__ctype_toupper;
-    }
 
-  /* It's done.  */
-  __libc_lock_unlock (__libc_setlocale_lock);
+      /* It's done.  */
+      __libc_lock_unlock (__libc_setlocale_lock);
+    }
 
   return result;
 }
diff --git a/locale/freelocale.c b/locale/freelocale.c
index 2ba1432a27..ba0ae85d84 100644
--- a/locale/freelocale.c
+++ b/locale/freelocale.c
@@ -34,23 +34,18 @@ __freelocale (__locale_t dataset)
 {
   int cnt;
 
-  /* We modify global data.  */
+  /* We modify global data (the usage counts).  */
   __libc_lock_lock (__libc_setlocale_lock);
 
   for (cnt = 0; cnt < __LC_LAST; ++cnt)
-    if (cnt != LC_ALL)
-      {
-	if (dataset->__locales[cnt]->usage_count != UNDELETABLE)
-	  /* We can remove the data.  */
-	  _nl_remove_locale (cnt, dataset->__locales[cnt]);
-	if (dataset->__names[cnt] != _nl_C_name)
-	  free ((char *) dataset->__names[cnt]);
-      }
-
-  /* Free the locale_t handle itself.  */
-  free (dataset);
+    if (cnt != LC_ALL && dataset->__locales[cnt]->usage_count != UNDELETABLE)
+      /* We can remove the data.  */
+      _nl_remove_locale (cnt, dataset->__locales[cnt]);
 
   /* It's done.  */
   __libc_lock_unlock (__libc_setlocale_lock);
+
+  /* Free the locale_t handle itself.  */
+  free (dataset);
 }
 weak_alias (__freelocale, freelocale)
diff --git a/locale/newlocale.c b/locale/newlocale.c
index ef6f94befb..3b8676ceeb 100644
--- a/locale/newlocale.c
+++ b/locale/newlocale.c
@@ -46,6 +46,7 @@ __newlocale (int category_mask, const char *locale, __locale_t base)
   size_t locale_path_len;
   const char *locpath_var;
   int cnt;
+  size_t names_len;
 
   /* We treat LC_ALL in the same way as if all bits were set.  */
   if (category_mask == 1 << LC_ALL)
@@ -143,83 +144,98 @@ __newlocale (int category_mask, const char *locale, __locale_t base)
     }
 
   /* Now process all categories we are interested in.  */
+  names_len = 0;
   for (cnt = 0; cnt < __LC_LAST; ++cnt)
-    if ((category_mask & 1 << cnt) != 0)
-      {
-	result.__locales[cnt] = _nl_find_locale (locale_path, locale_path_len,
-						 cnt, &newnames[cnt]);
-	if (result.__locales[cnt] == NULL)
-	  {
-	  free_data_and_exit:
-	    while (cnt-- > 0)
-	      if (((category_mask & 1 << cnt) != 0)
-		  && result.__locales[cnt]->usage_count != UNDELETABLE)
-		/* We can remove the data.  */
-		_nl_remove_locale (cnt, result.__locales[cnt]);
-	    return NULL;
-	  }
-      }
+    {
+      if ((category_mask & 1 << cnt) != 0)
+	{
+	  result.__locales[cnt] = _nl_find_locale (locale_path,
+						   locale_path_len,
+						   cnt, &newnames[cnt]);
+	  if (result.__locales[cnt] == NULL)
+	    {
+	    free_cnt_data_and_exit:
+	      while (cnt-- > 0)
+		if (((category_mask & 1 << cnt) != 0)
+		    && result.__locales[cnt]->usage_count != UNDELETABLE)
+		  /* We can remove the data.  */
+		  _nl_remove_locale (cnt, result.__locales[cnt]);
+	      return NULL;
+	    }
+
+	  if (newnames[cnt] != _nl_C_name)
+	    names_len += strlen (newnames[cnt]) + 1;
+	}
+      else if (cnt != LC_ALL && result.__names[cnt] != _nl_C_name)
+	/* Tally up the unchanged names from BASE as well.  */
+	names_len += strlen (result.__names[cnt]) + 1;
+    }
+
+  /* We successfully loaded all required data.  Allocate a new structure.
+     We can't just reuse the BASE pointer, because the name strings are
+     changing and we need the old name string area intact so we can copy
+     out of it into the new one without overlap problems should some
+     category's name be getting longer.  */
+  result_ptr = malloc (sizeof (struct __locale_struct) + names_len);
+  if (result_ptr == NULL)
+    {
+      cnt = __LC_LAST;
+      goto free_cnt_data_and_exit;
+    }
 
-  /* We successfully loaded all required data.  */
   if (base == NULL)
     {
-      /* Allocate new structure.  */
-      result_ptr = (__locale_t) malloc (sizeof (struct __locale_struct));
-      if (result_ptr == NULL)
-	goto free_data_and_exit;
+      /* Fill in this new structure from scratch.  */
+
+      char *namep = (char *) (result_ptr + 1);
 
-      /* Install strdup'd names in the new structure's __names array.
+      /* Install copied new names in the new structure's __names array.
 	 If resolved to "C", that is already in RESULT.__names to start.  */
       for (cnt = 0; cnt < __LC_LAST; ++cnt)
 	if ((category_mask & 1 << cnt) != 0 && newnames[cnt] != _nl_C_name)
 	  {
-	    result.__names[cnt] = __strdup (newnames[cnt]);
-	    if (result.__names[cnt] == NULL)
-	      {
-		free (result_ptr);
-		while (cnt-- > 0)
-		  if (result.__names[cnt] != _nl_C_name)
-		    free ((char *) result.__names[cnt]);
-		goto free_data_and_exit;
-	      }
+	    result.__names[cnt] = namep;
+	    namep = __stpcpy (namep, newnames[cnt]) + 1;
 	  }
 
       *result_ptr = result;
     }
   else
     {
-      /* We modify the base structure.
-         First strdup the names we were given for the new locale.  */
+      /* We modify the base structure.  */
 
-      for (cnt = 0; cnt < __LC_LAST; ++cnt)
-	if ((category_mask & 1 << cnt) != 0 && newnames[cnt] != _nl_C_name)
-	  {
-	    newnames[cnt] = __strdup (newnames[cnt]);
-	    if (newnames[cnt] == NULL)
-	      {
-		while (cnt-- > 0)
-		  if ((category_mask & 1 << cnt) != 0 &&
-		      newnames[cnt] != _nl_C_name)
-		    free ((char *) newnames[cnt]);
-		goto free_data_and_exit;
-	      }
-	  }
+      char *namep = (char *) (result_ptr + 1);
 
-      /* Now that we can't lose, install the new data.  */
       for (cnt = 0; cnt < __LC_LAST; ++cnt)
 	if ((category_mask & 1 << cnt) != 0)
 	  {
 	    if (base->__locales[cnt]->usage_count != UNDELETABLE)
 	      /* We can remove the old data.  */
 	      _nl_remove_locale (cnt, base->__locales[cnt]);
-	    base->__locales[cnt] = result.__locales[cnt];
+	    result_ptr->__locales[cnt] = result.__locales[cnt];
 
-	    if (base->__names[cnt] != _nl_C_name)
-	      free ((char *) base->__names[cnt]);
-	    base->__names[cnt] = newnames[cnt];
+	    if (newnames[cnt] == _nl_C_name)
+	      result_ptr->__names[cnt] = _nl_C_name;
+	    else
+	      {
+		result_ptr->__names[cnt] = namep;
+		namep = __stpcpy (namep, newnames[cnt]) + 1;
+	      }
+	  }
+	else if (cnt != LC_ALL)
+	  {
+	    /* The RESULT members point into the old BASE structure.  */
+	    result_ptr->__locales[cnt] = result.__locales[cnt];
+	    if (result.__names[cnt] == _nl_C_name)
+	      result_ptr->__names[cnt] = _nl_C_name;
+	    else
+	      {
+		result_ptr->__names[cnt] = namep;
+		namep = __stpcpy (namep, result.__names[cnt]) + 1;
+	      }
 	  }
 
-      result_ptr = base;
+      free (base);
     }
 
   /* Update the special members.  */
diff --git a/locale/xlocale.h b/locale/xlocale.h
index 4fb557d3bf..2b17d6973f 100644
--- a/locale/xlocale.h
+++ b/locale/xlocale.h
@@ -29,12 +29,14 @@ typedef struct __locale_struct
 {
   /* Note: LC_ALL is not a valid index into this array.  */
   struct locale_data *__locales[13]; /* 13 = __LC_LAST. */
-  const char *__names[13];
 
   /* To increase the speed of this solution we add some special members.  */
   const unsigned short int *__ctype_b;
   const int *__ctype_tolower;
   const int *__ctype_toupper;
+
+  /* Note: LC_ALL is not a valid index into this array.  */
+  const char *__names[13];
 } *__locale_t;
 
 #endif /* xlocale.h */