Hash :
a5ce928d
Author :
Date :
2025-02-14T05:56:55
duplocale: Support all platforms. * lib/locale.in.h (duplocale): Declare also on platforms that don't already have a duplocale function. Don't define HAVE_WORKING_DUPLOCALE. * lib/duplocale.c: Include <stdlib.h>. (duplocale): Renamed from rpl_duplocale. Add implementation for platforms without native locale_t. * modules/duplocale (Depends-on): Add newlocale, freelocale. (configure.ac): Compile also on platforms without native locale_t. * tests/test-duplocale.c: Ignore HAVE_WORKING_DUPLOCALE. * tests/test-locale-h-c++.cc: Likewise. * doc/posix-functions/duplocale.texi: Mention the change.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
/* Duplicate a locale object.
Copyright (C) 2009-2025 Free Software Foundation, Inc.
This file 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.
This file 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 this program. If not, see <https://www.gnu.org/licenses/>. */
/* Written by Bruno Haible <bruno@clisp.org>, 2007. */
#include <config.h>
/* Specification. */
#include <locale.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#define SIZEOF(a) (sizeof(a) / sizeof(a[0]))
locale_t
duplocale (locale_t locale)
#undef duplocale
{
/* Implement duplocale(LC_GLOBAL_LOCALE) on platforms without locale_t.
Also, work around crash in the duplocale function in glibc < 2.12.
See <https://sourceware.org/bugzilla/show_bug.cgi?id=10969>.
Also, on AIX 7.1, duplocale(LC_GLOBAL_LOCALE) returns (locale_t)0 with
errno set to EINVAL.
Also, on NetBSD 7.0, duplocale(LC_GLOBAL_LOCALE) returns a locale that
corresponds to the C locale. */
if (locale == LC_GLOBAL_LOCALE)
{
/* Create a copy of the locale by fetching the name of each locale
category, starting with LC_CTYPE. */
static struct { int cat; int mask; } const categories[] =
{
{ LC_NUMERIC, LC_NUMERIC_MASK },
{ LC_TIME, LC_TIME_MASK },
{ LC_COLLATE, LC_COLLATE_MASK },
{ LC_MONETARY, LC_MONETARY_MASK },
{ LC_MESSAGES, LC_MESSAGES_MASK }
#ifdef LC_PAPER
, { LC_PAPER, LC_PAPER_MASK }
#endif
#ifdef LC_NAME
, { LC_NAME, LC_NAME_MASK }
#endif
#ifdef LC_ADDRESS
, { LC_ADDRESS, LC_ADDRESS_MASK }
#endif
#ifdef LC_TELEPHONE
, { LC_TELEPHONE, LC_TELEPHONE_MASK }
#endif
#ifdef LC_MEASUREMENT
, { LC_MEASUREMENT, LC_MEASUREMENT_MASK }
#endif
#ifdef LC_IDENTIFICATION
, { LC_IDENTIFICATION, LC_IDENTIFICATION_MASK }
#endif
};
char base_name[SETLOCALE_NULL_MAX];
int err;
locale_t base_copy;
unsigned int i;
err = setlocale_null_r (LC_CTYPE, base_name, sizeof (base_name));
if (err)
{
errno = err;
return NULL;
}
base_copy = newlocale (LC_ALL_MASK, base_name, NULL);
if (base_copy == NULL)
return NULL;
for (i = 0; i < SIZEOF (categories); i++)
{
int category = categories[i].cat;
int category_mask = categories[i].mask;
char name[SETLOCALE_NULL_MAX];
err = setlocale_null_r (category, name, sizeof (name));
if (err)
{
errno = err;
return NULL;
}
if (strcmp (name, base_name) != 0)
{
locale_t copy = newlocale (category_mask, name, base_copy);
if (copy == NULL)
{
int saved_errno = errno;
freelocale (base_copy);
errno = saved_errno;
return NULL;
}
/* No need to call freelocale (base_copy) if copy != base_copy;
the newlocale function already takes care of doing it. */
base_copy = copy;
}
}
return base_copy;
}
#if GNULIB_defined_locale_t
locale_t result = (struct gl_locale_t *) malloc (sizeof (struct gl_locale_t));
if (result == NULL)
{
errno = ENOMEM;
return NULL;
}
int i;
int err;
for (i = 0; i < 6; i++)
{
int log2_lcmask = gl_index_to_log2_lcmask (i);
result->category[i].name = strdup (locale->category[i].name);
if (result->category[i].name == NULL)
{
err = ENOMEM;
goto fail_with_err;
}
result->category[i].is_c_locale = locale->category[i].is_c_locale;
# if HAVE_WINDOWS_LOCALE_T
if (log2_lcmask == gl_log2_lc_mask (LC_MESSAGES)
|| result->category[i].is_c_locale)
{
/* Just to initialize it. */
result->category[i].system_locale = NULL;
}
else
{
int cat = log2_lcmask;
(void) cat;
/* Documentation:
<https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/create-locale-wcreate-locale> */
result->category[i].system_locale =
_create_locale (LC_ALL /* or cat */, result->category[i].name);
if (result->category[i].system_locale == NULL)
{
free (result->category[i].name);
err = ENOENT;
goto fail_with_err;
}
}
# endif
}
/* Success. */
return result;
fail_with_err:
while (--i >= 0)
{
# if HAVE_WINDOWS_LOCALE_T
if (!(i == gl_log2_lcmask_to_index (gl_log2_lc_mask (LC_MESSAGES))
|| result->category[i].is_c_locale))
/* Documentation:
<https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/free-locale> */
_free_locale (result->category[i].system_locale);
# endif
free (result->category[i].name);
}
free (result);
errno = err;
return NULL;
#else
return duplocale (locale);
#endif
}