about summary refs log tree commit diff
path: root/locale
diff options
context:
space:
mode:
Diffstat (limited to 'locale')
-rw-r--r--locale/Makefile2
-rw-r--r--locale/global-locale.c63
-rw-r--r--locale/localeinfo.h73
-rw-r--r--locale/setlocale.c139
-rw-r--r--locale/uselocale.c42
-rw-r--r--locale/xlocale.c51
6 files changed, 225 insertions, 145 deletions
diff --git a/locale/Makefile b/locale/Makefile
index 0a5cac0043..523e03b4f3 100644
--- a/locale/Makefile
+++ b/locale/Makefile
@@ -42,7 +42,7 @@ tests		= tst-C-locale
 categories	= ctype messages monetary numeric time paper name \
 		  address telephone measurement identification collate
 aux		= $(categories:%=lc-%) $(categories:%=C-%) SYS_libc C_name \
-		  xlocale localename
+		  xlocale localename global-locale
 others		= localedef locale
 #others-static	= localedef locale
 install-bin	= localedef locale
diff --git a/locale/global-locale.c b/locale/global-locale.c
new file mode 100644
index 0000000000..70f10ab5ae
--- /dev/null
+++ b/locale/global-locale.c
@@ -0,0 +1,63 @@
+/* Locale object representing the global locale controlled by setlocale.
+   Copyright (C) 2002 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <locale.h>
+#include "localeinfo.h"
+
+#define DEFINE_CATEGORY(category, category_name, items, a) \
+extern struct locale_data _nl_C_##category; weak_extern (_nl_C_##category)
+#include "categories.def"
+#undef	DEFINE_CATEGORY
+
+/* Defined in locale/C-ctype.c.  */
+extern const char _nl_C_LC_CTYPE_class[] attribute_hidden;
+extern const char _nl_C_LC_CTYPE_toupper[] attribute_hidden;
+extern const char _nl_C_LC_CTYPE_tolower[] attribute_hidden;
+weak_extern (_nl_C_LC_CTYPE_class)
+weak_extern (_nl_C_LC_CTYPE_toupper)
+weak_extern (_nl_C_LC_CTYPE_tolower)
+
+/* Here we define the locale object maintained by setlocale.
+   The references in the initializer are weak, so the parts of
+   the structure that are never referred to will be zero.  */
+
+struct __locale_struct _nl_global_locale attribute_hidden =
+  {
+    .__locales =
+    {
+#define DEFINE_CATEGORY(category, category_name, items, a) \
+      [category] = &_nl_C_##category,
+#include "categories.def"
+#undef	DEFINE_CATEGORY
+    },
+    .__ctype_b = (const unsigned short int *) _nl_C_LC_CTYPE_class + 128,
+    .__ctype_tolower = (const int *) _nl_C_LC_CTYPE_tolower + 128,
+    .__ctype_toupper = (const int *) _nl_C_LC_CTYPE_toupper + 128
+  };
+
+#include <tls.h>
+#if USE_TLS && HAVE___THREAD
+/* The tsd macros don't permit an initializer.  */
+__thread void *__libc_tsd_LOCALE = &_nl_global_locale;
+#else
+__libc_tsd_define (, LOCALE)
+/* This is a bad kludge presuming the variable name used by the macros.
+   Using typeof makes sure to barf if we do not match the macro definition.  */
+__typeof (__libc_tsd_LOCALE_data) __libc_tsd_LOCALE_data = &_nl_global_locale;
+#endif
diff --git a/locale/localeinfo.h b/locale/localeinfo.h
index 44a10e1ca6..4e8c86edfa 100644
--- a/locale/localeinfo.h
+++ b/locale/localeinfo.h
@@ -155,54 +155,81 @@ extern const char _nl_C_codeset[] attribute_hidden;
    Each is malloc'd unless it is _nl_C_name.  */
 extern const char *_nl_current_names[] attribute_hidden;
 
+/* This is the internal locale_t object that holds the global locale
+   controlled by calls to setlocale.  A thread's TSD locale pointer
+   points to this when `uselocale (LC_GLOBAL_LOCALE)' is in effect.  */
+extern struct __locale_struct _nl_global_locale attribute_hidden;
+
+/* This fetches the thread-local locale_t pointer, either one set with
+   uselocale or &_nl_global_locale.  */
+#define _NL_CURRENT_LOCALE	((__locale_t) __libc_tsd_get (LOCALE))
+#include <bits/libc-tsd.h>
+__libc_tsd_define (extern, LOCALE)
 
-#ifndef SHARED
 
-/* For each category declare the variable for the current locale data.  */
-/* XXX _nl_current_LC_CTYPE and _nl_current_LC_COLLATE were exported
-   but where are they used?  */
+/* For static linking it is desireable to avoid always linking in the code
+   and data for every category when we can tell at link time that they are
+   unused.  We can manage this playing some tricks with weak references.
+   But with thread-local locale settings, it becomes quite ungainly unless
+   we can use __thread variables.  So only in that case do we attempt this.  */
+#if !defined SHARED && defined HAVE___THREAD && defined HAVE_WEAK_SYMBOLS
+# include <tls.h>
+# if USE_TLS
+#  define NL_CURRENT_INDIRECT	1
+# endif
+#endif
+
+#ifdef NL_CURRENT_INDIRECT
+
+/* For each category declare the thread-local variable for the current
+   locale data.  This has an extra indirection so it points at the
+   __locales[CATEGORY] element in either _nl_global_locale or the current
+   locale object set by uselocale, which points at the actual data.  The
+   reason for having these variables is so that references to particular
+   categories will link in the lc-CATEGORY.c module to define this symbol,
+   and we arrange that linking that module is what brings in all the code
+   associated with this category.  */
 #define DEFINE_CATEGORY(category, category_name, items, a) \
-extern struct locale_data *_nl_current_##category attribute_hidden;
+extern __thread struct locale_data *const *_nl_current_##category \
+  attribute_hidden;
 #include "categories.def"
 #undef	DEFINE_CATEGORY
-extern struct locale_data * *const _nl_current[__LC_LAST] attribute_hidden;
 
 /* Return a pointer to the current `struct locale_data' for CATEGORY.  */
-#define _NL_CURRENT_DATA(category)	_nl_current_##category
-/* Hackety hack, don't talk back.  */
-#define _nl_current_category		(*_nl_current[category])
+#define _NL_CURRENT_DATA(category)	(*_nl_current_##category)
 
 /* Extract the current CATEGORY locale's string for ITEM.  */
 #define _NL_CURRENT(category, item) \
-  (_nl_current_##category->values[_NL_ITEM_INDEX (item)].string)
+  ((*_nl_current_##category)->values[_NL_ITEM_INDEX (item)].string)
 
 /* Extract the current CATEGORY locale's string for ITEM.  */
 #define _NL_CURRENT_WSTR(category, item) \
-  ((wchar_t *) _nl_current_##category->values[_NL_ITEM_INDEX (item)].wstr)
+  ((wchar_t *) (*_nl_current_##category)->values[_NL_ITEM_INDEX (item)].wstr)
 
 /* Extract the current CATEGORY locale's word for ITEM.  */
 #define _NL_CURRENT_WORD(category, item) \
-  (_nl_current_##category->values[_NL_ITEM_INDEX (item)].word)
+  ((*_nl_current_##category)->values[_NL_ITEM_INDEX (item)].word)
 
 /* This is used in lc-CATEGORY.c to define _nl_current_CATEGORY.  */
 #define _NL_CURRENT_DEFINE(category) \
-  extern struct locale_data _nl_C_##category attribute_hidden; \
-  struct locale_data *_nl_current_##category = &_nl_C_##category
+  __thread struct locale_data *const *_nl_current_##category \
+    attribute_hidden = &_nl_global_locale.__locales[category]; \
+  asm (_NL_CURRENT_DEFINE_STRINGIFY (ASM_GLOBAL_DIRECTIVE) \
+       " " __SYMBOL_PREFIX "_nl_current_" #category "_used\n" \
+       _NL_CURRENT_DEFINE_ABS (_nl_current_##category##_used, 1));
+#define _NL_CURRENT_DEFINE_STRINGIFY(x) _NL_CURRENT_DEFINE_STRINGIFY_1 (x)
+#define _NL_CURRENT_DEFINE_STRINGIFY_1(x) #x
+#ifdef HAVE_ASM_SET_DIRECTIVE
+# define _NL_CURRENT_DEFINE_ABS(sym, val) ".set " #sym ", " #val
+#else
+# define _NL_CURRENT_DEFINE_ABS(sym, val) #sym " = " #val
+#endif
 
 #else
 
 /* All categories are always loaded in the shared library, so there is no
    point in having lots of separate symbols for linking.  */
 
-# include <bits/libc-tsd.h>
-
-__libc_tsd_define (extern, LOCALE)
-
-extern struct __locale_struct _nl_global_locale attribute_hidden;
-
-# define _NL_CURRENT_LOCALE \
-  ((__locale_t) __libc_tsd_get (LOCALE))
-
 /* Return a pointer to the current `struct locale_data' for CATEGORY.  */
 # define _NL_CURRENT_DATA(category) \
   (_NL_CURRENT_LOCALE->__locales[category])
diff --git a/locale/setlocale.c b/locale/setlocale.c
index 3c80379cf9..296903f24c 100644
--- a/locale/setlocale.c
+++ b/locale/setlocale.c
@@ -27,55 +27,39 @@
 
 #include "localeinfo.h"
 
-#ifndef SHARED
-
-/* For each category declare two external variables (with weak references):
-     extern const struct locale_data *_nl_current_CATEGORY;
-   This points to the current locale's in-core data for CATEGORY.
-     extern const struct locale_data _nl_C_CATEGORY;
-   This contains the built-in "C"/"POSIX" locale's data for CATEGORY.
-   Both are weak references; if &_nl_current_CATEGORY is zero,
-   then nothing is using the locale data.  */
-#define DEFINE_CATEGORY(category, category_name, items, a) \
-weak_extern (_nl_current_##category)					      \
-weak_extern (_nl_C_##category)						      \
-extern struct locale_data *_nl_current_##category;			      \
-extern struct locale_data _nl_C_##category;
-#include "categories.def"
-#undef	DEFINE_CATEGORY
-
-/* Array indexed by category of pointers to _nl_current_CATEGORY slots.
-   Elements are zero for categories whose data is never used.  */
-struct locale_data * *const _nl_current[] =
-  {
-#define DEFINE_CATEGORY(category, category_name, items, a) \
-    [category] = &_nl_current_##category,
-#include "categories.def"
-#undef	DEFINE_CATEGORY
-    /* We need this additional element to simplify the code.  It must
-       simply be != NULL.  */
-    [LC_ALL] = (struct locale_data **) ~0ul
-  };
-
-/* Array indexed by category of pointers to _nl_C_CATEGORY slots.
-   Elements are zero for categories whose data is never used.  */
-struct locale_data *const _nl_C[] attribute_hidden =
+#ifdef NL_CURRENT_INDIRECT
+
+/* For each category declare a special external symbol
+   _nl_current_CATEGORY_used with a weak reference.
+   This symbol will is defined in lc-CATEGORY.c and will be linked in
+   if anything uses _nl_current_CATEGORY (also defined in that module).
+   Also use a weak reference for the _nl_current_CATEGORY thread variable.  */
+
+# define DEFINE_CATEGORY(category, category_name, items, a) \
+    extern char _nl_current_##category##_used; \
+    weak_extern (_nl_current_##category##_used) \
+    weak_extern (_nl_current_##category)
+# include "categories.def"
+# undef	DEFINE_CATEGORY
+
+/* Now define a table of flags based on those special weak symbols' values.
+   _nl_current_used[CATEGORY] will be zero if _nl_current_CATEGORY is not
+   linked in.  */
+static char *const _nl_current_used[] =
   {
-#define DEFINE_CATEGORY(category, category_name, items, a) \
-    [category] = &_nl_C_##category,
-#include "categories.def"
-#undef	DEFINE_CATEGORY
+# define DEFINE_CATEGORY(category, category_name, items, a) \
+    [category] = &_nl_current_##category##_used,
+# include "categories.def"
+# undef	DEFINE_CATEGORY
   };
 
-# define CATEGORY_USED(category)	(_nl_current[category] != NULL)
+# define CATEGORY_USED(category)	(_nl_current_used[category] != 0)
 
 #else
 
 /* The shared library always loads all the categories,
    and the current global settings are kept in _nl_global_locale.  */
 
-# define _nl_C		(_nl_C_locobj.__locales)
-
 # define CATEGORY_USED(category)	(1)
 
 #endif
@@ -211,13 +195,7 @@ setdata (int category, struct locale_data *data)
 {
   if (CATEGORY_USED (category))
     {
-#ifdef SHARED
       _nl_global_locale.__locales[category] = data;
-#endif
-#ifndef SHARED
-# warning when uselocale exists it will need the line above too
-      *_nl_current[category] = data;
-#endif
       if (_nl_category_postload[category])
 	(*_nl_category_postload[category]) ();
     }
@@ -444,38 +422,57 @@ setlocale (int category, const char *locale)
 }
 libc_hidden_def (setlocale)
 
+static void
+free_category (int category,
+	       struct locale_data *here, struct locale_data *c_data)
+{
+  struct loaded_l10nfile *runp = _nl_locale_file_list[category];
+
+  /* If this category is already "C" don't do anything.  */
+  if (here != c_data)
+    {
+      /* We have to be prepared that sometime later we still
+	 might need the locale information.  */
+      setdata (category, c_data);
+      setname (category, _nl_C_name);
+    }
+
+  while (runp != NULL)
+    {
+      struct loaded_l10nfile *curr = runp;
+      struct locale_data *data = (struct locale_data *) runp->data;
+
+      if (data != NULL && data != c_data)
+	_nl_unload_locale (data);
+      runp = runp->next;
+      free ((char *) curr->filename);
+      free (curr);
+    }
+}
+
 static void __attribute__ ((unused))
 free_mem (void)
 {
+#ifdef NL_CURRENT_INDIRECT
+  /* We don't use the loop because we want to have individual weak
+     symbol references here.  */
+# define DEFINE_CATEGORY(category, category_name, items, a)		      \
+  if (CATEGORY_USED (category))						      \
+    {									      \
+      extern struct locale_data _nl_C_##category;			      \
+      weak_extern (_nl_C_##category)					      \
+      free_category (category, *_nl_current_##category, &_nl_C_##category);   \
+    }
+# include "categories.def"
+# undef	DEFINE_CATEGORY
+#else
   int category;
 
   for (category = 0; category < __LC_LAST; ++category)
     if (category != LC_ALL)
-      {
-	struct locale_data *here = _NL_CURRENT_DATA (category);
-	struct loaded_l10nfile *runp = _nl_locale_file_list[category];
-
-	/* If this category is already "C" don't do anything.  */
-	if (here != _nl_C[category])
-	  {
-	    /* We have to be prepared that sometime later we still
-	       might need the locale information.  */
-	    setdata (category, _nl_C[category]);
-	    setname (category, _nl_C_name);
-	  }
-
-	while (runp != NULL)
-	  {
-	    struct loaded_l10nfile *curr = runp;
-	    struct locale_data *data = (struct locale_data *) runp->data;
-
-	    if (data != NULL && data != _nl_C[category])
-	      _nl_unload_locale (data);
-	    runp = runp->next;
-	    free ((char *) curr->filename);
-	    free (curr);
-	  }
-      }
+      free_category (category, _NL_CURRENT_DATA (category),
+		     _nl_C_locobj.__locales[category]);
+#endif
 
   setname (LC_ALL, _nl_C_name);
 
diff --git a/locale/uselocale.c b/locale/uselocale.c
index 1e819381de..d5e53113c1 100644
--- a/locale/uselocale.c
+++ b/locale/uselocale.c
@@ -20,8 +20,6 @@
 #include <locale.h>
 #include "localeinfo.h"
 
-#ifdef SHARED
-
 /* Switch the current thread's locale to DATASET.
    If DATASET is null, instead just return the current setting.
    The special value LC_GLOBAL_LOCALE is the initial setting
@@ -35,18 +33,38 @@ __uselocale (locale_t newloc)
       locale_t loc = __libc_tsd_get (LOCALE);
       return loc == &_nl_global_locale ? LC_GLOBAL_LOCALE : loc;
     }
-  if (newloc == LC_GLOBAL_LOCALE)
+  else
     {
-      __libc_tsd_set (LOCALE, &_nl_global_locale);
-      return LC_GLOBAL_LOCALE;
+      const locale_t locobj
+	= newloc == LC_GLOBAL_LOCALE ? &_nl_global_locale : newloc;
+      __libc_tsd_set (LOCALE, locobj);
+
+#ifdef NL_CURRENT_INDIRECT
+      /* Now we must update all the per-category thread-local variables to
+	 point into the new current locale for this thread.  The magic
+	 symbols _nl_current_LC_FOO_used are defined to meaningless values
+	 if _nl_current_LC_FOO was linked in.  By using weak references to
+	 both symbols and testing the address of _nl_current_LC_FOO_used,
+	 we can avoid accessing the _nl_current_LC_FOO thread-local
+	 variable at all when no code referring to it was linked in.  We
+	 need the special bogus symbol because while TLS symbols can be
+	 weak, there is no reasonable way to test for the default-zero
+	 value as with a heap symbol (taking the address would just use
+	 some bogus offset from our thread pointer).  */
+
+# define DEFINE_CATEGORY(category, category_name, items, a) \
+      {									      \
+	extern char _nl_current_##category##_used;			      \
+	weak_extern (_nl_current_##category##_used)			      \
+	weak_extern (_nl_current_##category)				      \
+	if (&_nl_current_##category##_used != 0)			      \
+	  _nl_current_##category = &locobj->__locales[category];	      \
+      }
+# include "categories.def"
+# undef	DEFINE_CATEGORY
+#endif
     }
-  __libc_tsd_set (LOCALE, newloc);
+
   return newloc;
 }
 weak_alias (__uselocale, uselocale)
-
-#else
-
-# warning uselocale not implemented for static linking yet
-
-#endif
diff --git a/locale/xlocale.c b/locale/xlocale.c
index 854584cca4..2f9e198aef 100644
--- a/locale/xlocale.c
+++ b/locale/xlocale.c
@@ -32,41 +32,16 @@ extern const char _nl_C_LC_CTYPE_toupper[] attribute_hidden;
 extern const char _nl_C_LC_CTYPE_tolower[] attribute_hidden;
 
 
-#define NL_C_INITIALIZER						      \
-  {									      \
-    .__locales =							      \
-    {									      \
-      [LC_CTYPE] = &_nl_C_LC_CTYPE,					      \
-      [LC_NUMERIC] = &_nl_C_LC_NUMERIC,					      \
-      [LC_TIME] = &_nl_C_LC_TIME,					      \
-      [LC_COLLATE] = &_nl_C_LC_COLLATE,					      \
-      [LC_MONETARY] = &_nl_C_LC_MONETARY,				      \
-      [LC_MESSAGES] = &_nl_C_LC_MESSAGES,				      \
-      [LC_PAPER] = &_nl_C_LC_PAPER,					      \
-      [LC_NAME] = &_nl_C_LC_NAME,					      \
-      [LC_ADDRESS] = &_nl_C_LC_ADDRESS,					      \
-      [LC_TELEPHONE] = &_nl_C_LC_TELEPHONE,				      \
-      [LC_MEASUREMENT] = &_nl_C_LC_MEASUREMENT,				      \
-      [LC_IDENTIFICATION] = &_nl_C_LC_IDENTIFICATION			      \
-    },									      \
-    .__ctype_b = (const unsigned short int *) _nl_C_LC_CTYPE_class + 128,     \
-    .__ctype_tolower = (const int *) _nl_C_LC_CTYPE_tolower + 128,	      \
-    .__ctype_toupper = (const int *) _nl_C_LC_CTYPE_toupper + 128	      \
-  }
-
-struct __locale_struct _nl_C_locobj attribute_hidden = NL_C_INITIALIZER;
-
-#ifdef SHARED
-struct __locale_struct _nl_global_locale attribute_hidden = NL_C_INITIALIZER;
-
-# if USE_TLS && HAVE___THREAD
-/* The tsd macros don't permit an initializer.  */
-__thread void *__libc_tsd_LOCALE = &_nl_global_locale;
-# else
-__libc_tsd_define (, LOCALE)
-/* This is a bad kludge presuming the variable name used by the macros.
-   Using typeof makes sure to barf if we do not match the macro definition.  */
-__typeof (__libc_tsd_LOCALE_data) __libc_tsd_LOCALE_data = &_nl_global_locale;
-# endif
-
-#endif
+struct __locale_struct _nl_C_locobj attribute_hidden =
+  {
+    .__locales =
+    {
+#define DEFINE_CATEGORY(category, category_name, items, a) \
+      [category] = &_nl_C_##category,
+#include "categories.def"
+#undef	DEFINE_CATEGORY
+    },
+    .__ctype_b = (const unsigned short int *) _nl_C_LC_CTYPE_class + 128,
+    .__ctype_tolower = (const int *) _nl_C_LC_CTYPE_tolower + 128,
+    .__ctype_toupper = (const int *) _nl_C_LC_CTYPE_toupper + 128
+  };