-/* Copyright (C) 1998 Free Software Foundation, Inc.
+/* Copyright (C) 1998-2014 Free Software Foundation, Inc.
This file is part of the GNU C Library.
Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
The GNU C Library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Library General Public License as
- published by the Free Software Foundation; either version 2 of the
- License, or (at your option) any later version.
+ 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
- Library General Public License for more details.
+ Lesser General Public License for more details.
- You should have received a copy of the GNU Library General Public
- License along with the GNU C Library; see the file COPYING.LIB. If not,
- write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- Boston, MA 02111-1307, USA. */
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <http://www.gnu.org/licenses/>. */
+#include <ctype.h>
#include <langinfo.h>
#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <locale/localeinfo.h>
#include <wcsmbsload.h>
#include <bits/libc-lock.h>
-#include <iconv/gconv_int.h>
-
-
-/* Last loaded locale for LC_CTYPE. We initialize for the C locale
- which is enabled at startup. */
-extern const struct locale_data _nl_C_LC_CTYPE;
-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 const struct __gconv_step to_wc =
{
- shlib_handle: NULL,
- modname: NULL,
- counter: INT_MAX,
- from_name: "ANSI_X3.4-1968",
- to_name: "ISO-10646/UCS4/",
- fct: __gconv_transform_ascii_ucs4,
- init_fct: NULL,
- end_fct: NULL,
- data: NULL
+ .__shlib_handle = NULL,
+ .__modname = NULL,
+ .__counter = INT_MAX,
+ .__from_name = (char *) "ANSI_X3.4-1968//TRANSLIT",
+ .__to_name = (char *) "INTERNAL",
+ .__fct = __gconv_transform_ascii_internal,
+ .__btowc_fct = __gconv_btwoc_ascii,
+ .__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 const struct __gconv_step to_mb =
{
- shlib_handle: NULL,
- modname: NULL,
- counter: INT_MAX,
- from_name: "ISO-10646/UCS4/",
- to_name: "ANSI_X3.4-1968",
- fct: __gconv_transform_ucs4_ascii,
- init_fct: NULL,
- end_fct: NULL,
- data: NULL
+ .__shlib_handle = NULL,
+ .__modname = NULL,
+ .__counter = INT_MAX,
+ .__from_name = (char *) "INTERNAL",
+ .__to_name = (char *) "ANSI_X3.4-1968//TRANSLIT",
+ .__fct = __gconv_transform_internal_ascii,
+ .__btowc_fct = NULL,
+ .__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 =
+const struct gconv_fcts __wcsmbs_gconv_fcts_c =
{
- towc: &to_wc,
- tomb: &to_mb
+ .towc = (struct __gconv_step *) &to_wc,
+ .towc_nsteps = 1,
+ .tomb = (struct __gconv_step *) &to_mb,
+ .tomb_nsteps = 1,
};
-static inline struct gconv_step *
-getfct (const char *to, const char *from)
+attribute_hidden
+struct __gconv_step *
+__wcsmbs_getfct (const char *to, const char *from, size_t *nstepsp)
{
size_t nsteps;
- struct gconv_step *result;
+ struct __gconv_step *result;
+#if 0
+ size_t nstateful;
+ size_t cnt;
+#endif
- if (__gconv_find_transform (to, from, &result, &nsteps) != GCONV_OK)
+ if (__gconv_find_transform (to, from, &result, &nsteps, 0) != __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;
+ /* Maybe it is someday necessary to allow more than one step.
+ Currently this is not the case since the conversions handled here
+ are from and to INTERNAL and there always is a converted for
+ that. It the directly following code is enabled the libio
+ functions will have to allocate appropriate __gconv_step_data
+ elements instead of only one. */
+#if 0
+ /* 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)
+#else
+ if (nsteps > 1)
+#endif
+ {
+ /* We cannot handle this case. */
+ __gconv_close_transform (result, nsteps);
+ result = NULL;
+ }
+ else
+ *nstepsp = nsteps;
return result;
}
+/* Extract from the given locale name the character set portion. Since
+ only the XPG form of the name includes this information we don't have
+ to take care for the CEN form. */
+#define extract_charset_name(str) \
+ ({ \
+ const char *cp = str; \
+ char *result = NULL; \
+ \
+ cp += strcspn (cp, "@.+,"); \
+ if (*cp == '.') \
+ { \
+ const char *endp = ++cp; \
+ while (*endp != '\0' && *endp != '@') \
+ ++endp; \
+ if (endp != cp) \
+ result = strndupa (cp, endp - cp); \
+ } \
+ result; \
+ })
+
+
+/* Some of the functions here must not be used while setlocale is called. */
+__libc_rwlock_define (extern, __libc_setlocale_lock attribute_hidden)
+
/* Load conversion functions for the currently selected locale. */
void
-__wcsmbs_load_conv (const struct locale_data *new_category)
+internal_function
+__wcsmbs_load_conv (struct __locale_data *new_category)
{
- /* We must modify global data. */
- __libc_lock_define_initialized (static, lock)
-
/* Acquire the lock. */
- __libc_lock_lock (lock);
+ __libc_rwlock_wrlock (__libc_setlocale_lock);
- /* We should repest the test since while we waited some other thread
+ /* We should repeat the test since while we waited some other thread
might have run this function. */
- if (__wcsmbs_last_locale != new_category)
+ if (__glibc_likely (new_category->private.ctype == NULL))
{
- if (new_category->name == _nl_C_name) /* Yes, pointer comparison. */
+ /* We must find the real functions. */
+ const char *charset_name;
+ const char *complete_name;
+ struct gconv_fcts *new_fcts;
+ int use_translit;
+
+ /* Allocate the gconv_fcts structure. */
+ new_fcts = calloc (1, sizeof *new_fcts);
+ if (new_fcts == NULL)
+ goto failed;
+
+ /* Get name of charset of the locale. */
+ charset_name = new_category->values[_NL_ITEM_INDEX(CODESET)].string;
+
+ /* Does the user want transliteration? */
+ use_translit = new_category->use_translit;
+
+ /* Normalize the name and add the slashes necessary for a
+ complete lookup. */
+ complete_name = norm_add_slashes (charset_name,
+ use_translit ? "TRANSLIT" : "");
+
+ /* It is not necessary to use transliteration in this direction
+ since the internal character set is supposed to be able to
+ represent all others. */
+ new_fcts->towc = __wcsmbs_getfct ("INTERNAL", complete_name,
+ &new_fcts->towc_nsteps);
+ if (new_fcts->towc != NULL)
+ new_fcts->tomb = __wcsmbs_getfct (complete_name, "INTERNAL",
+ &new_fcts->tomb_nsteps);
+
+ /* If any of the conversion functions is not available we don't
+ use any since this would mean we cannot convert back and
+ forth. NB: NEW_FCTS was allocated with calloc. */
+ if (new_fcts->tomb == NULL)
{
+ if (new_fcts->towc != NULL)
+ __gconv_close_transform (new_fcts->towc, new_fcts->towc_nsteps);
+
+ free (new_fcts);
+
failed:
- __wcsmbs_gconv_fcts.towc = &to_wc;
- __wcsmbs_gconv_fcts.tomb = &to_mb;
+ new_category->private.ctype = &__wcsmbs_gconv_fcts_c;
}
else
{
- /* We must find the real functions. */
- const char *charset_name;
+ new_category->private.ctype = new_fcts;
+ new_category->private.cleanup = &_nl_cleanup_ctype;
+ }
+ }
- /* Get name of charset of the locale. */
- charset_name = new_category->values[_NL_ITEM_INDEX(CODESET)].string;
+ __libc_rwlock_unlock (__libc_setlocale_lock);
+}
- __wcsmbs_gconv_fcts.tomb = getfct (charset_name, "ISO-10646/UCS4/");
- __wcsmbs_gconv_fcts.towc = getfct ("ISO-10646/UCS4/", charset_name);
- /* 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;
- }
+/* Clone the current conversion function set. */
+void
+internal_function
+__wcsmbs_clone_conv (struct gconv_fcts *copy)
+{
+ const struct gconv_fcts *orig;
+
+ orig = get_gconv_fcts (_NL_CURRENT_DATA (LC_CTYPE));
+
+ /* Copy the data. */
+ *copy = *orig;
+
+ /* Now increment the usage counters.
+ Note: This assumes copy->*_nsteps == 1. */
+ if (copy->towc->__shlib_handle != NULL)
+ ++copy->towc->__counter;
+ if (copy->tomb->__shlib_handle != NULL)
+ ++copy->tomb->__counter;
+}
+
- /* Set last-used variable for current locale. */
- __wcsmbs_last_locale = new_category;
+/* Get converters for named charset. */
+int
+internal_function
+__wcsmbs_named_conv (struct gconv_fcts *copy, const char *name)
+{
+ copy->towc = __wcsmbs_getfct ("INTERNAL", name, ©->towc_nsteps);
+ if (copy->towc == NULL)
+ return 1;
+
+ copy->tomb = __wcsmbs_getfct (name, "INTERNAL", ©->tomb_nsteps);
+ if (copy->tomb == NULL)
+ {
+ __gconv_close_transform (copy->towc, copy->towc_nsteps);
+ return 1;
}
- __libc_lock_unlock (lock);
+ return 0;
+}
+
+void internal_function
+_nl_cleanup_ctype (struct __locale_data *locale)
+{
+ const struct gconv_fcts *const data = locale->private.ctype;
+ if (data != NULL)
+ {
+ locale->private.ctype = NULL;
+ locale->private.cleanup = NULL;
+
+ /* Free the old conversions. */
+ __gconv_close_transform (data->tomb, data->tomb_nsteps);
+ __gconv_close_transform (data->towc, data->towc_nsteps);
+ free ((char *) data);
+ }
}