#include "EXTERN.h"
#define PERL_IN_LOCALE_C
#include "perl_langinfo.h"
#include "perl.h"
#include "reentr.h"
#ifdef I_WCHAR
# include <wchar.h>
#endif
#ifdef I_WCTYPE
# include <wctype.h>
#endif
#if ! defined(DEBUGGING)
# define debug_initialization 0
# define DEBUG_INITIALIZATION_set(v)
#else
static
bool
debug_initialization = FALSE;
# define DEBUG_INITIALIZATION_set(v) (debug_initialization = v)
#endif
#define GET_ERRNO saved_errno
#define STRLENs(s) (sizeof("" s "") - 1)
#define isNAME_C_OR_POSIX(name) \
( (name) != NULL \
&& (( *(name) ==
'C'
&& (*(name + 1)) ==
'\0'
) \
|| strEQ((name),
"POSIX"
)))
#ifdef USE_LOCALE
#define UTF8NESS_SEP "\v"
#define UTF8NESS_PREFIX "\f"
STATIC_ASSERT_DECL(STRLENs(UTF8NESS_SEP) == 1);
STATIC_ASSERT_DECL(STRLENs(UTF8NESS_PREFIX) == 1);
#define C_and_POSIX_utf8ness UTF8NESS_SEP "C" UTF8NESS_PREFIX "0" \
UTF8NESS_SEP
"POSIX"
UTF8NESS_PREFIX
"0"
STATIC
char
*
S_stdize_locale(pTHX_
char
*locs)
{
const
char
*
const
s =
strchr
(locs,
'='
);
bool
okay = TRUE;
PERL_ARGS_ASSERT_STDIZE_LOCALE;
if
(s) {
const
char
*
const
t =
strchr
(s,
'.'
);
okay = FALSE;
if
(t) {
const
char
*
const
u =
strchr
(t,
'\n'
);
if
(u && (u[1] == 0)) {
const
STRLEN len = u - s;
Move(s + 1, locs, len,
char
);
locs[len] = 0;
okay = TRUE;
}
}
}
if
(!okay)
Perl_croak(aTHX_
"Can't fix broken locale name \"%s\""
, locs);
return
locs;
}
const
int
categories[] = {
# ifdef USE_LOCALE_NUMERIC
LC_NUMERIC,
# endif
# ifdef USE_LOCALE_CTYPE
LC_CTYPE,
# endif
# ifdef USE_LOCALE_COLLATE
LC_COLLATE,
# endif
# ifdef USE_LOCALE_TIME
LC_TIME,
# endif
# ifdef USE_LOCALE_MESSAGES
LC_MESSAGES,
# endif
# ifdef USE_LOCALE_MONETARY
LC_MONETARY,
# endif
# ifdef USE_LOCALE_ADDRESS
LC_ADDRESS,
# endif
# ifdef USE_LOCALE_IDENTIFICATION
LC_IDENTIFICATION,
# endif
# ifdef USE_LOCALE_MEASUREMENT
LC_MEASUREMENT,
# endif
# ifdef USE_LOCALE_PAPER
LC_PAPER,
# endif
# ifdef USE_LOCALE_TELEPHONE
LC_TELEPHONE,
# endif
# ifdef USE_LOCALE_SYNTAX
LC_SYNTAX,
# endif
# ifdef USE_LOCALE_TOD
LC_TOD,
# endif
# ifdef LC_ALL
LC_ALL,
# endif
-1
};
const
char
*
const
category_names[] = {
# ifdef USE_LOCALE_NUMERIC
"LC_NUMERIC"
,
# endif
# ifdef USE_LOCALE_CTYPE
"LC_CTYPE"
,
# endif
# ifdef USE_LOCALE_COLLATE
"LC_COLLATE"
,
# endif
# ifdef USE_LOCALE_TIME
"LC_TIME"
,
# endif
# ifdef USE_LOCALE_MESSAGES
"LC_MESSAGES"
,
# endif
# ifdef USE_LOCALE_MONETARY
"LC_MONETARY"
,
# endif
# ifdef USE_LOCALE_ADDRESS
"LC_ADDRESS"
,
# endif
# ifdef USE_LOCALE_IDENTIFICATION
"LC_IDENTIFICATION"
,
# endif
# ifdef USE_LOCALE_MEASUREMENT
"LC_MEASUREMENT"
,
# endif
# ifdef USE_LOCALE_PAPER
"LC_PAPER"
,
# endif
# ifdef USE_LOCALE_TELEPHONE
"LC_TELEPHONE"
,
# endif
# ifdef USE_LOCALE_SYNTAX
"LC_SYNTAX"
,
# endif
# ifdef USE_LOCALE_TOD
"LC_TOD"
,
# endif
# ifdef LC_ALL
"LC_ALL"
,
# endif
NULL
};
# ifdef LC_ALL
# define NOMINAL_LC_ALL_INDEX (C_ARRAY_LENGTH(categories) - 2)
# else
# define NOMINAL_LC_ALL_INDEX (C_ARRAY_LENGTH(categories) - 1)
# endif
STATIC
const
char
*
S_category_name(
const
int
category)
{
unsigned
int
i;
#ifdef LC_ALL
if
(category == LC_ALL) {
return
"LC_ALL"
;
}
#endif
for
(i = 0; i < NOMINAL_LC_ALL_INDEX; i++) {
if
(category == categories[i]) {
return
category_names[i];
}
}
{
const
char
suffix[] =
" (unknown)"
;
int
temp = category;
Size_t length =
sizeof
(suffix) + 1;
char
* unknown;
dTHX;
if
(temp < 0) {
length++;
temp = - temp;
}
while
(temp >= 10) {
temp /= 10;
length++;
}
Newx(unknown, length,
char
);
my_snprintf(unknown, length,
"%d%s"
, category, suffix);
SAVEFREEPV(unknown);
return
unknown;
}
}
# ifdef USE_LOCALE_NUMERIC
# define LC_NUMERIC_INDEX 0
# define _DUMMY_NUMERIC LC_NUMERIC_INDEX
# else
# define _DUMMY_NUMERIC -1
# endif
# ifdef USE_LOCALE_CTYPE
# define LC_CTYPE_INDEX _DUMMY_NUMERIC + 1
# define _DUMMY_CTYPE LC_CTYPE_INDEX
# else
# define _DUMMY_CTYPE _DUMMY_NUMERIC
# endif
# ifdef USE_LOCALE_COLLATE
# define LC_COLLATE_INDEX _DUMMY_CTYPE + 1
# define _DUMMY_COLLATE LC_COLLATE_INDEX
# else
# define _DUMMY_COLLATE _DUMMY_CTYPE
# endif
# ifdef USE_LOCALE_TIME
# define LC_TIME_INDEX _DUMMY_COLLATE + 1
# define _DUMMY_TIME LC_TIME_INDEX
# else
# define _DUMMY_TIME _DUMMY_COLLATE
# endif
# ifdef USE_LOCALE_MESSAGES
# define LC_MESSAGES_INDEX _DUMMY_TIME + 1
# define _DUMMY_MESSAGES LC_MESSAGES_INDEX
# else
# define _DUMMY_MESSAGES _DUMMY_TIME
# endif
# ifdef USE_LOCALE_MONETARY
# define LC_MONETARY_INDEX _DUMMY_MESSAGES + 1
# define _DUMMY_MONETARY LC_MONETARY_INDEX
# else
# define _DUMMY_MONETARY _DUMMY_MESSAGES
# endif
# ifdef USE_LOCALE_ADDRESS
# define LC_ADDRESS_INDEX _DUMMY_MONETARY + 1
# define _DUMMY_ADDRESS LC_ADDRESS_INDEX
# else
# define _DUMMY_ADDRESS _DUMMY_MONETARY
# endif
# ifdef USE_LOCALE_IDENTIFICATION
# define LC_IDENTIFICATION_INDEX _DUMMY_ADDRESS + 1
# define _DUMMY_IDENTIFICATION LC_IDENTIFICATION_INDEX
# else
# define _DUMMY_IDENTIFICATION _DUMMY_ADDRESS
# endif
# ifdef USE_LOCALE_MEASUREMENT
# define LC_MEASUREMENT_INDEX _DUMMY_IDENTIFICATION + 1
# define _DUMMY_MEASUREMENT LC_MEASUREMENT_INDEX
# else
# define _DUMMY_MEASUREMENT _DUMMY_IDENTIFICATION
# endif
# ifdef USE_LOCALE_PAPER
# define LC_PAPER_INDEX _DUMMY_MEASUREMENT + 1
# define _DUMMY_PAPER LC_PAPER_INDEX
# else
# define _DUMMY_PAPER _DUMMY_MEASUREMENT
# endif
# ifdef USE_LOCALE_TELEPHONE
# define LC_TELEPHONE_INDEX _DUMMY_PAPER + 1
# define _DUMMY_TELEPHONE LC_TELEPHONE_INDEX
# else
# define _DUMMY_TELEPHONE _DUMMY_PAPER
# endif
# ifdef USE_LOCALE_SYNTAX
# define LC_SYNTAX_INDEX _DUMMY_TELEPHONE + 1
# define _DUMMY_SYNTAX LC_SYNTAX_INDEX
# else
# define _DUMMY_SYNTAX _DUMMY_TELEPHONE
# endif
# ifdef USE_LOCALE_TOD
# define LC_TOD_INDEX _DUMMY_SYNTAX + 1
# define _DUMMY_TOD LC_TOD_INDEX
# else
# define _DUMMY_TOD _DUMMY_SYNTAX
# endif
# ifdef LC_ALL
# define LC_ALL_INDEX _DUMMY_TOD + 1
# endif
#endif /* ifdef USE_LOCALE */
#ifdef WIN32
# define my_setlocale(cat, locale) win32_setlocale(cat, locale)
#else
# define my_setlocale(cat, locale) setlocale(cat, locale)
#endif
#ifndef USE_POSIX_2008_LOCALE
# define do_setlocale_c(cat, locale) my_setlocale(cat, locale)
# define do_setlocale_r(cat, locale) my_setlocale(cat, locale)
# define FIX_GLIBC_LC_MESSAGES_BUG(i)
#else /* Below uses POSIX 2008 */
# define do_setlocale_c(cat, locale) \
emulate_setlocale(cat, locale, cat ## _INDEX, TRUE)
# define do_setlocale_r(cat, locale) emulate_setlocale(cat, locale, 0, FALSE)
# if ! defined(__GLIBC__) || ! defined(USE_LOCALE_MESSAGES)
# define FIX_GLIBC_LC_MESSAGES_BUG(i)
# else /* Invalidate glibc cache of loaded translations, see [perl #134264] */
# include <libintl.h>
# define FIX_GLIBC_LC_MESSAGES_BUG(i) \
STMT_START { \
if
((i) == LC_MESSAGES_INDEX) { \
textdomain(textdomain(NULL)); \
} \
} STMT_END
# endif
const
int
category_masks[] = {
# ifdef USE_LOCALE_NUMERIC
LC_NUMERIC_MASK,
# endif
# ifdef USE_LOCALE_CTYPE
LC_CTYPE_MASK,
# endif
# ifdef USE_LOCALE_COLLATE
LC_COLLATE_MASK,
# endif
# ifdef USE_LOCALE_TIME
LC_TIME_MASK,
# endif
# ifdef USE_LOCALE_MESSAGES
LC_MESSAGES_MASK,
# endif
# ifdef USE_LOCALE_MONETARY
LC_MONETARY_MASK,
# endif
# ifdef USE_LOCALE_ADDRESS
LC_ADDRESS_MASK,
# endif
# ifdef USE_LOCALE_IDENTIFICATION
LC_IDENTIFICATION_MASK,
# endif
# ifdef USE_LOCALE_MEASUREMENT
LC_MEASUREMENT_MASK,
# endif
# ifdef USE_LOCALE_PAPER
LC_PAPER_MASK,
# endif
# ifdef USE_LOCALE_TELEPHONE
LC_TELEPHONE_MASK,
# endif
# ifdef USE_LOCALE_SYNTAX
LC_SYNTAX_MASK,
# endif
# ifdef USE_LOCALE_TOD
LC_TOD_MASK,
# endif
LC_ALL_MASK
};
STATIC
const
char
*
S_emulate_setlocale(
const
int
category,
const
char
* locale,
unsigned
int
index,
const
bool
is_index_valid
)
{
int
mask;
locale_t old_obj;
locale_t new_obj;
const
char
* safelocale = locale ? locale :
"(null)"
;
dTHX;
# ifdef DEBUGGING
if
(DEBUG_Lv_TEST || debug_initialization) {
PerlIO_printf(Perl_debug_log,
"%s:%d: emulate_setlocale input=%d (%s), \"%s\", %d, %d\n"
, __FILE__, __LINE__, category, category_name(category), safelocale, index, is_index_valid);
}
# endif
if
(! is_index_valid) {
unsigned
int
i;
# ifdef DEBUGGING
if
(DEBUG_Lv_TEST || debug_initialization) {
PerlIO_printf(Perl_debug_log,
"%s:%d: finding index of category %d (%s)\n"
, __FILE__, __LINE__, category, category_name(category));
}
# endif
for
(i = 0; i <= LC_ALL_INDEX; i++) {
if
(category == categories[i]) {
index = i;
goto
found_index;
}
}
Perl_warner(aTHX_ packWARN(WARN_LOCALE),
"Unknown locale category %d; can't set it to %s\n"
,
category, safelocale);
return
NULL;
found_index: ;
# ifdef DEBUGGING
if
(DEBUG_Lv_TEST || debug_initialization) {
PerlIO_printf(Perl_debug_log,
"%s:%d: index is %d for %s\n"
, __FILE__, __LINE__, index, category_name(category));
}
# endif
}
mask = category_masks[index];
# ifdef DEBUGGING
if
(DEBUG_Lv_TEST || debug_initialization) {
PerlIO_printf(Perl_debug_log,
"%s:%d: category name is %s; mask is 0x%x\n"
, __FILE__, __LINE__, category_names[index], mask);
}
# endif
if
(locale == NULL) {
locale_t cur_obj = uselocale((locale_t) 0);
# ifdef DEBUGGING
if
(DEBUG_Lv_TEST || debug_initialization) {
PerlIO_printf(Perl_debug_log,
"%s:%d: emulate_setlocale querying %p\n"
, __FILE__, __LINE__, cur_obj);
}
# endif
if
(cur_obj == LC_GLOBAL_LOCALE) {
return
my_setlocale(category, NULL);
}
# ifdef HAS_QUERYLOCALE
return
(
char
*) querylocale(mask, cur_obj);
# else
STATIC_ASSERT_STMT(C_ARRAY_LENGTH(PL_curlocales) > LC_ALL_INDEX);
# if defined(_NL_LOCALE_NAME) \
&& defined(DEBUGGING) \
\
&& ! defined(SETLOCALE_ACCEPTS_ANY_LOCALE_NAME)
{
char
* temp_name = nl_langinfo_l(_NL_LOCALE_NAME(category),
uselocale((locale_t) 0));
if
(temp_name && PL_curlocales[index] && strNE(temp_name,
""
)) {
if
( strNE(PL_curlocales[index], temp_name)
&& ! ( isNAME_C_OR_POSIX(temp_name)
&& isNAME_C_OR_POSIX(PL_curlocales[index]))) {
# ifdef USE_C_BACKTRACE
dump_c_backtrace(Perl_debug_log, 20, 1);
# endif
Perl_croak(aTHX_
"panic: Mismatch between what Perl thinks %s is"
" (%s) and what internal glibc thinks"
" (%s)\n"
, category_names[index],
PL_curlocales[index], temp_name);
}
return
temp_name;
}
}
# endif
if
(category != LC_ALL) {
# ifdef DEBUGGING
if
(DEBUG_Lv_TEST || debug_initialization) {
PerlIO_printf(Perl_debug_log,
"%s:%d: emulate_setlocale returning %s\n"
, __FILE__, __LINE__, PL_curlocales[index]);
}
# endif
return
PL_curlocales[index];
}
else
{
unsigned
int
i;
Size_t names_len = 0;
char
* all_string;
bool
are_all_categories_the_same_locale = TRUE;
if
(PL_curlocales[LC_ALL_INDEX]) {
# ifdef DEBUGGING
if
(DEBUG_Lv_TEST || debug_initialization) {
PerlIO_printf(Perl_debug_log,
"%s:%d: emulate_setlocale returning %s\n"
, __FILE__, __LINE__, PL_curlocales[LC_ALL_INDEX]);
}
# endif
return
PL_curlocales[LC_ALL_INDEX];
}
for
(i = 0; i < LC_ALL_INDEX; i++) {
# ifdef DEBUGGING
if
(DEBUG_Lv_TEST || debug_initialization) {
PerlIO_printf(Perl_debug_log,
"%s:%d: emulate_setlocale i=%d, name=%s, locale=%s\n"
, __FILE__, __LINE__, i, category_names[i], PL_curlocales[i]);
}
# endif
names_len +=
strlen
(category_names[i])
+ 1
+
strlen
(PL_curlocales[i])
+ 1;
if
(i > 0 && strNE(PL_curlocales[i], PL_curlocales[i-1])) {
are_all_categories_the_same_locale = FALSE;
}
}
if
(are_all_categories_the_same_locale) {
PL_curlocales[LC_ALL_INDEX] = savepv(PL_curlocales[0]);
return
PL_curlocales[LC_ALL_INDEX];
}
names_len++;
SAVEFREEPV(Newx(all_string, names_len,
char
));
*all_string =
'\0'
;
for
(i = 0; i < LC_ALL_INDEX; i++) {
# ifdef DEBUGGING
if
(DEBUG_Lv_TEST || debug_initialization) {
PerlIO_printf(Perl_debug_log,
"%s:%d: emulate_setlocale i=%d, name=%s, locale=%s\n"
, __FILE__, __LINE__, i, category_names[i], PL_curlocales[i]);
}
# endif
my_strlcat(all_string, category_names[i], names_len);
my_strlcat(all_string,
"="
, names_len);
my_strlcat(all_string, PL_curlocales[i], names_len);
my_strlcat(all_string,
";"
, names_len);
}
# ifdef DEBUGGING
if
(DEBUG_L_TEST || debug_initialization) {
PerlIO_printf(Perl_debug_log,
"%s:%d: emulate_setlocale returning %s\n"
, __FILE__, __LINE__, all_string);
}
#endif
return
all_string;
}
# ifdef EINVAL
SETERRNO(EINVAL, LIB_INVARG);
# endif
return
NULL;
# endif
}
# ifndef HAS_QUERYLOCALE
if
(strEQ(locale,
""
)) {
const
char
*
const
lc_all = PerlEnv_getenv(
"LC_ALL"
);
if
(lc_all && strNE(lc_all,
""
)) {
locale = lc_all;
}
else
{
const
char
* default_name;
default_name = PerlEnv_getenv(
"LANG"
);
if
(! default_name || strEQ(default_name,
""
)) {
default_name =
"C"
;
}
if
(category != LC_ALL) {
const
char
*
const
name = PerlEnv_getenv(category_names[index]);
locale = default_name;
if
(name && strNE(name,
""
)) {
locale = name;
}
}
else
{
bool
did_override = FALSE;
unsigned
int
i;
for
(i = 0; i < LC_ALL_INDEX; i++) {
const
char
*
const
env_override
= PerlEnv_getenv(category_names[i]);
const
char
* this_locale = ( env_override
&& strNE(env_override,
""
))
? env_override
: default_name;
if
(! emulate_setlocale(categories[i], this_locale, i, TRUE))
{
return
NULL;
}
if
(strNE(this_locale, default_name)) {
did_override = TRUE;
}
}
if
(! did_override) {
locale = default_name;
}
else
{
return
emulate_setlocale(LC_ALL, NULL, LC_ALL_INDEX, TRUE);
}
}
}
}
else
if
(
strchr
(locale,
';'
)) {
unsigned
int
i;
const
char
* s = locale;
const
char
* e = locale +
strlen
(locale);
const
char
* p = s;
const
char
* category_end;
const
char
* name_start;
const
char
* name_end;
for
(i = 0; i < LC_ALL_INDEX; i++) {
if
(! emulate_setlocale(categories[i],
"C"
, i, TRUE)) {
return
NULL;
}
}
while
(s < e) {
while
(isWORDCHAR(*p)) {
p++;
}
category_end = p;
if
(*p++ !=
'='
) {
Perl_croak(aTHX_
"panic: %s: %d: Unexpected character in locale name '%02X"
,
__FILE__, __LINE__, *(p-1));
}
name_start = p;
while
(p < e && *p !=
';'
) {
if
(! isGRAPH(*p)) {
Perl_croak(aTHX_
"panic: %s: %d: Unexpected character in locale name '%02X"
,
__FILE__, __LINE__, *(p-1));
}
p++;
}
name_end = p;
if
(p < e) {
p++;
}
for
(i = 0; i < LC_ALL_INDEX; i++) {
char
* individ_locale;
if
strnNE(s, category_names[i], category_end - s) {
continue
;
}
if
(category == categories[i]) {
locale = Perl_form(aTHX_
"%.*s"
,
(
int
) (name_end - name_start),
name_start);
goto
ready_to_set;
}
assert
(category == LC_ALL);
individ_locale = Perl_form(aTHX_
"%.*s"
,
(
int
) (name_end - name_start), name_start);
if
(! emulate_setlocale(categories[i], individ_locale, i, TRUE))
{
return
NULL;
}
}
s = p;
}
assert
(category == LC_ALL);
return
do_setlocale_c(LC_ALL, NULL);
}
ready_to_set: ;
# endif /* end of ! querylocale */
assert
(PL_C_locale_obj);
old_obj = uselocale(PL_C_locale_obj);
# ifdef DEBUGGING
if
(DEBUG_Lv_TEST || debug_initialization) {
PerlIO_printf(Perl_debug_log,
"%s:%d: emulate_setlocale was using %p\n"
, __FILE__, __LINE__, old_obj);
}
# endif
if
(! old_obj) {
# ifdef DEBUGGING
if
(DEBUG_L_TEST || debug_initialization) {
dSAVE_ERRNO;
PerlIO_printf(Perl_debug_log,
"%s:%d: emulate_setlocale switching to C failed: %d\n"
, __FILE__, __LINE__, GET_ERRNO);
RESTORE_ERRNO;
}
# endif
return
NULL;
}
# ifdef DEBUGGING
if
(DEBUG_Lv_TEST || debug_initialization) {
PerlIO_printf(Perl_debug_log,
"%s:%d: emulate_setlocale now using %p\n"
,
__FILE__, __LINE__, PL_C_locale_obj);
}
# endif
if
(mask == LC_ALL_MASK && isNAME_C_OR_POSIX(locale)) {
# ifdef DEBUGGING
if
(DEBUG_Lv_TEST || debug_initialization) {
PerlIO_printf(Perl_debug_log,
"%s:%d: will stay in C object\n"
, __FILE__, __LINE__);
}
# endif
new_obj = PL_C_locale_obj;
if
(old_obj != LC_GLOBAL_LOCALE && old_obj != PL_C_locale_obj) {
freelocale(old_obj);
}
}
else
{
if
(old_obj == LC_GLOBAL_LOCALE || old_obj == PL_C_locale_obj) {
old_obj = (locale_t) 0;
}
new_obj = newlocale(mask, locale, old_obj);
if
(! new_obj) {
dSAVE_ERRNO;
# ifdef DEBUGGING
if
(DEBUG_L_TEST || debug_initialization) {
PerlIO_printf(Perl_debug_log,
"%s:%d: emulate_setlocale creating new object"
" failed: %d\n"
, __FILE__, __LINE__, GET_ERRNO);
}
# endif
if
(! uselocale(old_obj)) {
# ifdef DEBUGGING
if
(DEBUG_L_TEST || debug_initialization) {
PerlIO_printf(Perl_debug_log,
"%s:%d: switching back failed: %d\n"
,
__FILE__, __LINE__, GET_ERRNO);
}
# endif
}
RESTORE_ERRNO;
return
NULL;
}
# ifdef DEBUGGING
if
(DEBUG_Lv_TEST || debug_initialization) {
PerlIO_printf(Perl_debug_log,
"%s:%d: emulate_setlocale created %p"
,
__FILE__, __LINE__, new_obj);
if
(old_obj) {
PerlIO_printf(Perl_debug_log,
"; should have freed %p"
, old_obj);
}
PerlIO_printf(Perl_debug_log,
"\n"
);
}
# endif
if
(! uselocale(new_obj)) {
dSAVE_ERRNO;
# ifdef DEBUGGING
if
(DEBUG_L_TEST || debug_initialization) {
PerlIO_printf(Perl_debug_log,
"%s:%d: emulate_setlocale switching to new object"
" failed\n"
, __FILE__, __LINE__);
}
# endif
if
(! uselocale(old_obj)) {
# ifdef DEBUGGING
if
(DEBUG_L_TEST || debug_initialization) {
PerlIO_printf(Perl_debug_log,
"%s:%d: switching back failed: %d\n"
,
__FILE__, __LINE__, GET_ERRNO);
}
# endif
}
freelocale(new_obj);
RESTORE_ERRNO;
return
NULL;
}
}
# ifdef DEBUGGING
if
(DEBUG_Lv_TEST || debug_initialization) {
PerlIO_printf(Perl_debug_log,
"%s:%d: emulate_setlocale now using %p\n"
,
__FILE__, __LINE__, new_obj);
}
# endif
# ifdef HAS_QUERYLOCALE
if
(strEQ(locale,
""
)) {
locale = querylocale(mask, new_obj);
}
# else
if
(category == LC_ALL) {
unsigned
int
i;
for
(i = 0; i <= LC_ALL_INDEX; i++) {
Safefree(PL_curlocales[i]);
PL_curlocales[i] = savepv(locale);
}
FIX_GLIBC_LC_MESSAGES_BUG(LC_MESSAGES_INDEX);
}
else
{
if
(PL_curlocales[LC_ALL_INDEX] && strNE(PL_curlocales[LC_ALL_INDEX], locale)) {
Safefree(PL_curlocales[LC_ALL_INDEX]);
PL_curlocales[LC_ALL_INDEX] = NULL;
}
Safefree(PL_curlocales[index]);
PL_curlocales[index] = savepv(locale);
FIX_GLIBC_LC_MESSAGES_BUG(index);
}
# endif
return
locale;
}
#endif /* USE_POSIX_2008_LOCALE */
#ifdef USE_LOCALE
STATIC
void
S_set_numeric_radix(pTHX_
const
bool
use_locale)
{
#if defined(USE_LOCALE_NUMERIC) && ( defined(HAS_LOCALECONV) \
|| defined(HAS_NL_LANGINFO))
const
char
* radix = (use_locale)
? my_nl_langinfo(RADIXCHAR, FALSE)
:
"."
;
sv_setpv(PL_numeric_radix_sv, radix);
if
(is_utf8_non_invariant_string((U8 *) SvPVX(PL_numeric_radix_sv),
SvCUR(PL_numeric_radix_sv))
&& _is_cur_LC_category_utf8(LC_NUMERIC))
{
SvUTF8_on(PL_numeric_radix_sv);
}
# ifdef DEBUGGING
if
(DEBUG_L_TEST || debug_initialization) {
PerlIO_printf(Perl_debug_log,
"Locale radix is '%s', ?UTF-8=%d\n"
,
SvPVX(PL_numeric_radix_sv),
cBOOL(SvUTF8(PL_numeric_radix_sv)));
}
# endif
#else
PERL_UNUSED_ARG(use_locale);
#endif /* USE_LOCALE_NUMERIC and can find the radix char */
}
STATIC
void
S_new_numeric(pTHX_
const
char
*newnum)
{
#ifndef USE_LOCALE_NUMERIC
PERL_UNUSED_ARG(newnum);
#else
char
*save_newnum;
if
(! newnum) {
Safefree(PL_numeric_name);
PL_numeric_name = NULL;
PL_numeric_standard = TRUE;
PL_numeric_underlying = TRUE;
PL_numeric_underlying_is_standard = TRUE;
return
;
}
save_newnum = stdize_locale(savepv(newnum));
PL_numeric_underlying = TRUE;
PL_numeric_standard = isNAME_C_OR_POSIX(save_newnum);
#ifndef TS_W32_BROKEN_LOCALECONV
if
(! PL_numeric_standard) {
PL_numeric_standard = cBOOL(strEQ(
"."
, my_nl_langinfo(RADIXCHAR,
FALSE
))
&& strEQ(
""
, my_nl_langinfo(THOUSEP, FALSE)));
}
#endif
if
(! PL_numeric_name || strNE(PL_numeric_name, save_newnum)) {
Safefree(PL_numeric_name);
PL_numeric_name = save_newnum;
}
else
{
Safefree(save_newnum);
}
PL_numeric_underlying_is_standard = PL_numeric_standard;
# ifdef HAS_POSIX_2008_LOCALE
PL_underlying_numeric_obj = newlocale(LC_NUMERIC_MASK,
PL_numeric_name,
PL_underlying_numeric_obj);
#endif
if
(DEBUG_L_TEST || debug_initialization) {
PerlIO_printf(Perl_debug_log,
"Called new_numeric with %s, PL_numeric_name=%s\n"
, newnum, PL_numeric_name);
}
if
(PL_numeric_standard) {
set_numeric_radix(0);
}
else
{
set_numeric_standard();
}
#endif /* USE_LOCALE_NUMERIC */
}
void
Perl_set_numeric_standard(pTHX)
{
#ifdef USE_LOCALE_NUMERIC
# ifdef DEBUGGING
if
(DEBUG_L_TEST || debug_initialization) {
PerlIO_printf(Perl_debug_log,
"Setting LC_NUMERIC locale to standard C\n"
);
}
# endif
do_setlocale_c(LC_NUMERIC,
"C"
);
PL_numeric_standard = TRUE;
PL_numeric_underlying = PL_numeric_underlying_is_standard;
set_numeric_radix(0);
#endif /* USE_LOCALE_NUMERIC */
}
void
Perl_set_numeric_underlying(pTHX)
{
#ifdef USE_LOCALE_NUMERIC
# ifdef DEBUGGING
if
(DEBUG_L_TEST || debug_initialization) {
PerlIO_printf(Perl_debug_log,
"Setting LC_NUMERIC locale to %s\n"
,
PL_numeric_name);
}
# endif
do_setlocale_c(LC_NUMERIC, PL_numeric_name);
PL_numeric_standard = PL_numeric_underlying_is_standard;
PL_numeric_underlying = TRUE;
set_numeric_radix(! PL_numeric_standard);
#endif /* USE_LOCALE_NUMERIC */
}
STATIC
void
S_new_ctype(pTHX_
const
char
*newctype)
{
#ifndef USE_LOCALE_CTYPE
PERL_UNUSED_ARG(newctype);
PERL_UNUSED_CONTEXT;
#else
unsigned
int
i;
bool
check_for_problems = ckWARN_d(WARN_LOCALE) || UNLIKELY(DEBUG_L_TEST);
bool
maybe_utf8_turkic = FALSE;
PERL_ARGS_ASSERT_NEW_CTYPE;
if
(PL_warn_locale) {
SvREFCNT_dec_NN(PL_warn_locale);
PL_warn_locale = NULL;
}
PL_in_utf8_CTYPE_locale = _is_cur_LC_category_utf8(LC_CTYPE);
if
(PL_in_utf8_CTYPE_locale) {
Copy(PL_fold_latin1, PL_fold_locale, 256, U8);
#if defined(HAS_TOWUPPER) && defined (HAS_TOWLOWER)
if
(towupper(
'i'
) == 0x130 && towlower(
'I'
) == 0x131) {
#else
if
(
toupper
(
'i'
) ==
'i'
&&
tolower
(
'I'
) ==
'I'
) {
#endif
check_for_problems = TRUE;
maybe_utf8_turkic = TRUE;
}
}
if
(check_for_problems || ! PL_in_utf8_CTYPE_locale) {
char
bad_chars_list[ (94 * 4) + (3 * 5) + 1 ] = {
'\0'
};
bool
multi_byte_locale = FALSE;
unsigned
int
bad_count = 0;
for
(i = 0; i < 256; i++) {
if
(! PL_in_utf8_CTYPE_locale) {
if
(
isupper
(i))
PL_fold_locale[i] = (U8)
tolower
(i);
else
if
(
islower
(i))
PL_fold_locale[i] = (U8)
toupper
(i);
else
PL_fold_locale[i] = (U8) i;
}
if
( check_for_problems
&& (isGRAPH_A(i) || isBLANK_A(i) || i ==
'\n'
))
{
bool
is_bad = FALSE;
char
name[4] = {
'\0'
};
if
(isGRAPH_A(i)) {
name[0] = i;
name[1] =
'\0'
;
}
else
if
(i ==
'\n'
) {
my_strlcpy(name,
"\\n"
,
sizeof
(name));
}
else
if
(i ==
'\t'
) {
my_strlcpy(name,
"\\t"
,
sizeof
(name));
}
else
{
assert
(i ==
' '
);
my_strlcpy(name,
"' '"
,
sizeof
(name));
}
if
(UNLIKELY(cBOOL(
isalnum
(i)) != cBOOL(isALPHANUMERIC_A(i)))) {
is_bad = TRUE;
DEBUG_L(PerlIO_printf(Perl_debug_log,
"isalnum('%s') unexpectedly is %d\n"
,
name, cBOOL(
isalnum
(i))));
}
if
(UNLIKELY(cBOOL(
isalpha
(i)) != cBOOL(isALPHA_A(i)))) {
is_bad = TRUE;
DEBUG_L(PerlIO_printf(Perl_debug_log,
"isalpha('%s') unexpectedly is %d\n"
,
name, cBOOL(
isalpha
(i))));
}
if
(UNLIKELY(cBOOL(
isdigit
(i)) != cBOOL(isDIGIT_A(i)))) {
is_bad = TRUE;
DEBUG_L(PerlIO_printf(Perl_debug_log,
"isdigit('%s') unexpectedly is %d\n"
,
name, cBOOL(
isdigit
(i))));
}
if
(UNLIKELY(cBOOL(
isgraph
(i)) != cBOOL(isGRAPH_A(i)))) {
is_bad = TRUE;
DEBUG_L(PerlIO_printf(Perl_debug_log,
"isgraph('%s') unexpectedly is %d\n"
,
name, cBOOL(
isgraph
(i))));
}
if
(UNLIKELY(cBOOL(
islower
(i)) != cBOOL(isLOWER_A(i)))) {
is_bad = TRUE;
DEBUG_L(PerlIO_printf(Perl_debug_log,
"islower('%s') unexpectedly is %d\n"
,
name, cBOOL(
islower
(i))));
}
if
(UNLIKELY(cBOOL(isprint(i)) != cBOOL(isPRINT_A(i)))) {
is_bad = TRUE;
DEBUG_L(PerlIO_printf(Perl_debug_log,
"isprint('%s') unexpectedly is %d\n"
,
name, cBOOL(isprint(i))));
}
if
(UNLIKELY(cBOOL(ispunct(i)) != cBOOL(isPUNCT_A(i)))) {
is_bad = TRUE;
DEBUG_L(PerlIO_printf(Perl_debug_log,
"ispunct('%s') unexpectedly is %d\n"
,
name, cBOOL(ispunct(i))));
}
if
(UNLIKELY(cBOOL(
isspace
(i)) != cBOOL(isSPACE_A(i)))) {
is_bad = TRUE;
DEBUG_L(PerlIO_printf(Perl_debug_log,
"isspace('%s') unexpectedly is %d\n"
,
name, cBOOL(
isspace
(i))));
}
if
(UNLIKELY(cBOOL(
isupper
(i)) != cBOOL(isUPPER_A(i)))) {
is_bad = TRUE;
DEBUG_L(PerlIO_printf(Perl_debug_log,
"isupper('%s') unexpectedly is %d\n"
,
name, cBOOL(
isupper
(i))));
}
if
(UNLIKELY(cBOOL(
isxdigit
(i))!= cBOOL(isXDIGIT_A(i)))) {
is_bad = TRUE;
DEBUG_L(PerlIO_printf(Perl_debug_log,
"isxdigit('%s') unexpectedly is %d\n"
,
name, cBOOL(
isxdigit
(i))));
}
if
(UNLIKELY(
tolower
(i) != (
int
) toLOWER_A(i))) {
is_bad = TRUE;
DEBUG_L(PerlIO_printf(Perl_debug_log,
"tolower('%s')=0x%x instead of the expected 0x%x\n"
,
name,
tolower
(i), (
int
) toLOWER_A(i)));
}
if
(UNLIKELY(
toupper
(i) != (
int
) toUPPER_A(i))) {
is_bad = TRUE;
DEBUG_L(PerlIO_printf(Perl_debug_log,
"toupper('%s')=0x%x instead of the expected 0x%x\n"
,
name,
toupper
(i), (
int
) toUPPER_A(i)));
}
if
(UNLIKELY((i ==
'\n'
&& ! isCNTRL_LC(i)))) {
is_bad = TRUE;
DEBUG_L(PerlIO_printf(Perl_debug_log,
"'\\n' (=%02X) is not a control\n"
, (
int
) i));
}
if
(is_bad) {
if
(bad_count) {
my_strlcat(bad_chars_list,
" "
,
sizeof
(bad_chars_list));
}
my_strlcat(bad_chars_list, name,
sizeof
(bad_chars_list));
bad_count++;
}
}
}
if
(bad_count == 2 && maybe_utf8_turkic) {
bad_count = 0;
*bad_chars_list =
'\0'
;
PL_fold_locale[
'I'
] =
'I'
;
PL_fold_locale[
'i'
] =
'i'
;
PL_in_utf8_turkic_locale = TRUE;
DEBUG_L(PerlIO_printf(Perl_debug_log,
"%s:%d: %s is turkic\n"
,
__FILE__, __LINE__, newctype));
}
else
{
PL_in_utf8_turkic_locale = FALSE;
}
# ifdef MB_CUR_MAX
DEBUG_Lv(PerlIO_printf(Perl_debug_log,
"%s:%d: check_for_problems=%d, MB_CUR_MAX=%d\n"
,
__FILE__, __LINE__, check_for_problems, (
int
) MB_CUR_MAX));
if
( check_for_problems && MB_CUR_MAX > 1
&& ! PL_in_utf8_CTYPE_locale
&& strNE(newctype,
"C"
) && strNE(newctype,
"POSIX"
))
{
multi_byte_locale = TRUE;
}
# endif
if
( (UNLIKELY(bad_count) || UNLIKELY(multi_byte_locale))
&& (LIKELY(ckWARN_d(WARN_LOCALE)) || UNLIKELY(DEBUG_L_TEST)))
{
if
(UNLIKELY(bad_count) && PL_in_utf8_CTYPE_locale) {
PL_warn_locale = Perl_newSVpvf(aTHX_
"Locale '%s' contains (at least) the following characters"
" which have\nunexpected meanings: %s\nThe Perl program"
" will use the expected meanings"
,
newctype, bad_chars_list);
}
else
{
PL_warn_locale = Perl_newSVpvf(aTHX_
"Locale '%s' may not work well.%s%s%s\n"
,
newctype,
(multi_byte_locale)
?
" Some characters in it are not recognized by"
" Perl."
:
""
,
(bad_count)
?
"\nThe following characters (and maybe others)"
" may not have the same meaning as the Perl"
" program expects:\n"
:
""
,
(bad_count)
? bad_chars_list
:
""
);
}
# ifdef HAS_NL_LANGINFO
Perl_sv_catpvf(aTHX_ PL_warn_locale,
"; codeset=%s"
,
my_nl_langinfo(CODESET, FALSE));
# endif
Perl_sv_catpvf(aTHX_ PL_warn_locale,
"\n"
);
if
(IN_LC(LC_CTYPE) || UNLIKELY(DEBUG_L_TEST)) {
Perl_warner(aTHX_ packWARN(WARN_LOCALE), SvPVX(PL_warn_locale), 0);
if
(IN_LC(LC_CTYPE)) {
SvREFCNT_dec_NN(PL_warn_locale);
PL_warn_locale = NULL;
}
}
}
}
#endif /* USE_LOCALE_CTYPE */
}
void
Perl__warn_problematic_locale()
{
#ifdef USE_LOCALE_CTYPE
dTHX;
if
(PL_warn_locale) {
Perl_ck_warner(aTHX_ packWARN(WARN_LOCALE),
SvPVX(PL_warn_locale),
0
);
SvREFCNT_dec_NN(PL_warn_locale);
PL_warn_locale = NULL;
}
#endif
}
STATIC
void
S_new_collate(pTHX_
const
char
*newcoll)
{
#ifndef USE_LOCALE_COLLATE
PERL_UNUSED_ARG(newcoll);
PERL_UNUSED_CONTEXT;
#else
if
(! newcoll) {
if
(PL_collation_name) {
++PL_collation_ix;
Safefree(PL_collation_name);
PL_collation_name = NULL;
}
PL_collation_standard = TRUE;
is_standard_collation:
PL_collxfrm_base = 0;
PL_collxfrm_mult = 2;
PL_in_utf8_COLLATE_locale = FALSE;
PL_strxfrm_NUL_replacement =
'\0'
;
PL_strxfrm_max_cp = 0;
return
;
}
if
(! PL_collation_name || strNE(PL_collation_name, newcoll)) {
++PL_collation_ix;
Safefree(PL_collation_name);
PL_collation_name = stdize_locale(savepv(newcoll));
PL_collation_standard = isNAME_C_OR_POSIX(newcoll);
if
(PL_collation_standard) {
goto
is_standard_collation;
}
PL_in_utf8_COLLATE_locale = _is_cur_LC_category_utf8(LC_COLLATE);
PL_strxfrm_NUL_replacement =
'\0'
;
PL_strxfrm_max_cp = 0;
{
const
char
longer[] =
"ABCDEFGHIJKLMnopqrstuvwxyz"
;
char
* x_longer;
Size_t x_len_longer;
char
* x_shorter;
Size_t x_len_shorter;
PL_collxfrm_base = 5;
PL_collxfrm_mult = 5 *
sizeof
(UV);
x_longer = _mem_collxfrm(longer,
sizeof
(longer) - 1,
&x_len_longer,
PL_in_utf8_COLLATE_locale);
Safefree(x_longer);
x_shorter = _mem_collxfrm(longer + 1,
sizeof
(longer) - 2,
&x_len_shorter,
PL_in_utf8_COLLATE_locale);
Safefree(x_shorter);
if
( x_len_shorter == 0
|| x_len_longer == 0
|| x_len_shorter >= x_len_longer)
{
PL_collxfrm_mult = 0;
PL_collxfrm_base = 0;
}
else
{
SSize_t base;
if
(x_len_longer > x_len_shorter) {
PL_collxfrm_mult = (STRLEN) x_len_longer - x_len_shorter;
}
else
{
PL_collxfrm_mult = 1;
}
base = x_len_longer - PL_collxfrm_mult * (
sizeof
(longer) - 1);
if
(base < 0) {
base = 0;
}
PL_collxfrm_base = base + 1;
}
# ifdef DEBUGGING
if
(DEBUG_L_TEST || debug_initialization) {
PerlIO_printf(Perl_debug_log,
"%s:%d: ?UTF-8 locale=%d; x_len_shorter=%zu, "
"x_len_longer=%zu,"
" collate multipler=%zu, collate base=%zu\n"
,
__FILE__, __LINE__,
PL_in_utf8_COLLATE_locale,
x_len_shorter, x_len_longer,
PL_collxfrm_mult, PL_collxfrm_base);
}
# endif
}
}
#endif /* USE_LOCALE_COLLATE */
}
#endif
#ifdef WIN32
#define USE_WSETLOCALE
#ifdef USE_WSETLOCALE
STATIC
char
*
S_wrap_wsetlocale(pTHX_
int
category,
const
char
*locale) {
wchar_t
*wlocale;
wchar_t
*wresult;
char
*result;
if
(locale) {
int
req_size =
MultiByteToWideChar(CP_UTF8, 0, locale, -1, NULL, 0);
if
(!req_size) {
errno
= EINVAL;
return
NULL;
}
Newx(wlocale, req_size,
wchar_t
);
if
(!MultiByteToWideChar(CP_UTF8, 0, locale, -1, wlocale, req_size)) {
Safefree(wlocale);
errno
= EINVAL;
return
NULL;
}
}
else
{
wlocale = NULL;
}
wresult = _wsetlocale(category, wlocale);
Safefree(wlocale);
if
(wresult) {
int
req_size =
WideCharToMultiByte(CP_UTF8, 0, wresult, -1, NULL, 0, NULL, NULL);
Newx(result, req_size,
char
);
SAVEFREEPV(result);
if
(!WideCharToMultiByte(CP_UTF8, 0, wresult, -1,
result, req_size, NULL, NULL)) {
errno
= EINVAL;
return
NULL;
}
}
else
{
result = NULL;
}
return
result;
}
#endif
STATIC
char
*
S_win32_setlocale(pTHX_
int
category,
const
char
* locale)
{
bool
override_LC_ALL = FALSE;
char
* result;
unsigned
int
i;
if
(locale && strEQ(locale,
""
)) {
# ifdef LC_ALL
locale = PerlEnv_getenv(
"LC_ALL"
);
if
(! locale) {
if
(category == LC_ALL) {
override_LC_ALL = TRUE;
}
else
{
# endif
for
(i = 0; i < NOMINAL_LC_ALL_INDEX; i++) {
if
(category == categories[i]) {
locale = PerlEnv_getenv(category_names[i]);
goto
found_locale;
}
}
locale = PerlEnv_getenv(
"LANG"
);
if
(! locale) {
locale =
""
;
}
found_locale: ;
# ifdef LC_ALL
}
}
# endif
}
#ifdef USE_WSETLOCALE
result = S_wrap_wsetlocale(aTHX_ category, locale);
#else
result =
setlocale
(category, locale);
#endif
DEBUG_L(STMT_START {
dSAVE_ERRNO;
PerlIO_printf(Perl_debug_log,
"%s:%d: %s\n"
, __FILE__, __LINE__,
setlocale_debug_string(category, locale, result));
RESTORE_ERRNO;
} STMT_END);
if
(! override_LC_ALL) {
return
result;
}
for
(i = 0; i < LC_ALL_INDEX; i++) {
result = PerlEnv_getenv(category_names[i]);
if
(result && strNE(result,
""
)) {
#ifdef USE_WSETLOCALE
S_wrap_wsetlocale(aTHX_ categories[i], result);
#else
setlocale
(categories[i], result);
#endif
DEBUG_Lv(PerlIO_printf(Perl_debug_log,
"%s:%d: %s\n"
,
__FILE__, __LINE__,
setlocale_debug_string(categories[i], result,
"not captured"
)));
}
}
result =
setlocale
(LC_ALL, NULL);
DEBUG_L(STMT_START {
dSAVE_ERRNO;
PerlIO_printf(Perl_debug_log,
"%s:%d: %s\n"
,
__FILE__, __LINE__,
setlocale_debug_string(LC_ALL, NULL, result));
RESTORE_ERRNO;
} STMT_END);
return
result;
}
#endif
const
char
*
Perl_setlocale(
const
int
category,
const
char
* locale)
{
#ifndef USE_LOCALE
PERL_UNUSED_ARG(category);
PERL_UNUSED_ARG(locale);
return
"C"
;
#else
const
char
* retval;
const
char
* newlocale;
dSAVEDERRNO;
dTHX;
DECLARATION_FOR_LC_NUMERIC_MANIPULATION;
#ifdef USE_LOCALE_NUMERIC
if
(locale == NULL) {
if
(category == LC_NUMERIC) {
return
PL_numeric_name;
}
# ifdef LC_ALL
else
if
(category == LC_ALL) {
STORE_LC_NUMERIC_FORCE_TO_UNDERLYING();
}
# endif
}
#endif
retval = save_to_buffer(do_setlocale_r(category, locale),
&PL_setlocale_buf, &PL_setlocale_bufsize, 0);
SAVE_ERRNO;
#if defined(USE_LOCALE_NUMERIC) && defined(LC_ALL)
if
(locale == NULL && category == LC_ALL) {
RESTORE_LC_NUMERIC();
}
#endif
DEBUG_L(PerlIO_printf(Perl_debug_log,
"%s:%d: %s\n"
, __FILE__, __LINE__,
setlocale_debug_string(category, locale, retval)));
RESTORE_ERRNO;
if
(! retval) {
return
NULL;
}
if
(locale == NULL) {
return
retval;
}
switch
(category) {
#ifdef USE_LOCALE_CTYPE
case
LC_CTYPE:
new_ctype(retval);
break
;
#endif
#ifdef USE_LOCALE_COLLATE
case
LC_COLLATE:
new_collate(retval);
break
;
#endif
#ifdef USE_LOCALE_NUMERIC
case
LC_NUMERIC:
new_numeric(retval);
break
;
#endif
#ifdef LC_ALL
case
LC_ALL:
# ifdef USE_LOCALE_CTYPE
newlocale = savepv(do_setlocale_c(LC_CTYPE, NULL));
new_ctype(newlocale);
Safefree(newlocale);
# endif /* USE_LOCALE_CTYPE */
# ifdef USE_LOCALE_COLLATE
newlocale = savepv(do_setlocale_c(LC_COLLATE, NULL));
new_collate(newlocale);
Safefree(newlocale);
# endif
# ifdef USE_LOCALE_NUMERIC
newlocale = savepv(do_setlocale_c(LC_NUMERIC, NULL));
new_numeric(newlocale);
Safefree(newlocale);
# endif /* USE_LOCALE_NUMERIC */
#endif /* LC_ALL */
default
:
break
;
}
return
retval;
#endif
}
PERL_STATIC_INLINE
const
char
*
S_save_to_buffer(
const
char
* string,
char
**buf, Size_t *buf_size,
const
Size_t offset)
{
Size_t string_size;
PERL_ARGS_ASSERT_SAVE_TO_BUFFER;
if
(! string) {
return
NULL;
}
string_size =
strlen
(string) + offset + 1;
if
(*buf_size == 0) {
Newx(*buf, string_size,
char
);
*buf_size = string_size;
}
else
if
(string_size > *buf_size) {
Renew(*buf, string_size,
char
);
*buf_size = string_size;
}
Copy(string, *buf + offset, string_size - offset,
char
);
return
*buf;
}
const
char
*
#ifdef HAS_NL_LANGINFO
Perl_langinfo(
const
nl_item item)
#else
Perl_langinfo(
const
int
item)
#endif
{
return
my_nl_langinfo(item, TRUE);
}
STATIC
const
char
*
#ifdef HAS_NL_LANGINFO
S_my_nl_langinfo(
const
nl_item item,
bool
toggle)
#else
S_my_nl_langinfo(
const
int
item,
bool
toggle)
#endif
{
dTHX;
const
char
* retval;
#ifdef USE_LOCALE_NUMERIC
if
(toggle && (( item != RADIXCHAR && item != THOUSEP)
|| PL_numeric_underlying))
#endif /* No toggling needed if not using LC_NUMERIC */
toggle = FALSE;
#if defined(HAS_NL_LANGINFO) /* nl_langinfo() is available. */
# if ! defined(HAS_THREAD_SAFE_NL_LANGINFO_L) \
|| ! defined(HAS_POSIX_2008_LOCALE)
{
DECLARATION_FOR_LC_NUMERIC_MANIPULATION;
if
(toggle) {
STORE_LC_NUMERIC_FORCE_TO_UNDERLYING();
}
NL_LANGINFO_LOCK;
retval = save_to_buffer(nl_langinfo(item),
&PL_langinfo_buf, &PL_langinfo_bufsize, 0);
NL_LANGINFO_UNLOCK;
if
(toggle) {
RESTORE_LC_NUMERIC();
}
}
# else /* Use nl_langinfo_l(), avoiding both a mutex and changing the locale */
{
bool
do_free = FALSE;
locale_t cur = uselocale((locale_t) 0);
if
(cur == LC_GLOBAL_LOCALE) {
cur = duplocale(LC_GLOBAL_LOCALE);
do_free = TRUE;
}
# ifdef USE_LOCALE_NUMERIC
if
(toggle) {
if
(PL_underlying_numeric_obj) {
cur = PL_underlying_numeric_obj;
}
else
{
cur = newlocale(LC_NUMERIC_MASK, PL_numeric_name, cur);
do_free = TRUE;
}
}
# endif
retval = save_to_buffer(nl_langinfo_l(item, cur),
&PL_langinfo_buf, &PL_langinfo_bufsize, 0);
if
(do_free) {
freelocale(cur);
}
}
# endif
if
(strEQ(retval,
""
)) {
if
(item == YESSTR) {
return
"yes"
;
}
if
(item == NOSTR) {
return
"no"
;
}
}
return
retval;
#else /* Below, emulate nl_langinfo as best we can */
{
# ifdef HAS_LOCALECONV
const
struct
lconv
* lc;
const
char
* temp;
DECLARATION_FOR_LC_NUMERIC_MANIPULATION;
# ifdef TS_W32_BROKEN_LOCALECONV
const
char
* save_global;
const
char
* save_thread;
int
needed_size;
char
* ptr;
char
* e;
char
* item_start;
# endif
# endif
# ifdef HAS_STRFTIME
struct
tm
tm
;
bool
return_format = FALSE;
const
char
* format;
# endif
switch
(item) {
Size_t len;
case
ERA:
default
:
return
""
;
case
YESEXPR:
return
"^[+1yY]"
;
case
YESSTR:
return
"yes"
;
case
NOEXPR:
return
"^[-0nN]"
;
case
NOSTR:
return
"no"
;
case
CODESET:
# ifndef WIN32
return
""
;
# else
{
const
char
* p;
const
char
* first;
Size_t offset = 0;
const
char
* name = my_setlocale(LC_CTYPE, NULL);
if
(isNAME_C_OR_POSIX(name)) {
return
"ANSI_X3.4-1968"
;
}
first = (
const
char
*)
strchr
(name,
'.'
);
if
(! first) {
first = name;
goto
has_nondigit;
}
first++;
p = first;
while
(*p) {
if
(! isDIGIT(*p)) {
goto
has_nondigit;
}
p++;
}
retval = save_to_buffer(
"CP"
, &PL_langinfo_buf,
&PL_langinfo_bufsize, 0);
offset = STRLENs(
"CP"
);
has_nondigit:
retval = save_to_buffer(first, &PL_langinfo_buf,
&PL_langinfo_bufsize, offset);
}
break
;
# endif
# ifdef HAS_LOCALECONV
case
CRNCYSTR:
LOCALECONV_LOCK;
# ifdef TS_W32_BROKEN_LOCALECONV
save_thread = savepv(my_setlocale(LC_ALL, NULL));
_configthreadlocale(_DISABLE_PER_THREAD_LOCALE);
save_global= savepv(my_setlocale(LC_ALL, NULL));
my_setlocale(LC_ALL, save_thread);
# endif
lc =
localeconv
();
if
( ! lc
|| ! lc->currency_symbol
|| strEQ(
""
, lc->currency_symbol))
{
LOCALECONV_UNLOCK;
return
""
;
}
retval = save_to_buffer(lc->currency_symbol, &PL_langinfo_buf,
&PL_langinfo_bufsize, 1);
if
(lc->mon_decimal_point && strEQ(lc->mon_decimal_point,
""
))
{
PL_langinfo_buf[0] =
'.'
;
}
else
if
(lc->p_cs_precedes) {
PL_langinfo_buf[0] =
'-'
;
}
else
{
PL_langinfo_buf[0] =
'+'
;
}
# ifdef TS_W32_BROKEN_LOCALECONV
my_setlocale(LC_ALL, save_global);
_configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
my_setlocale(LC_ALL, save_thread);
Safefree(save_global);
Safefree(save_thread);
# endif
LOCALECONV_UNLOCK;
break
;
# ifdef TS_W32_BROKEN_LOCALECONV
case
RADIXCHAR:
if
(toggle) {
STORE_LC_NUMERIC_FORCE_TO_UNDERLYING();
}
if
(PL_langinfo_bufsize < 10) {
PL_langinfo_bufsize = 10;
Renew(PL_langinfo_buf, PL_langinfo_bufsize,
char
);
}
needed_size = my_snprintf(PL_langinfo_buf, PL_langinfo_bufsize,
"%.1f"
, 1.5);
if
(needed_size >= (
int
) PL_langinfo_bufsize) {
PL_langinfo_bufsize = needed_size + 1;
Renew(PL_langinfo_buf, PL_langinfo_bufsize,
char
);
needed_size = my_snprintf(PL_langinfo_buf, PL_langinfo_bufsize,
"%.1f"
, 1.5);
assert
(needed_size < (
int
) PL_langinfo_bufsize);
}
ptr = PL_langinfo_buf;
e = PL_langinfo_buf + PL_langinfo_bufsize;
while
(ptr < e && *ptr !=
'1'
) {
ptr++;
}
ptr++;
item_start = ptr;
while
(ptr < e && *ptr !=
'5'
) {
ptr++;
}
if
(ptr >= e) {
PL_langinfo_buf[0] =
'?'
;
PL_langinfo_buf[1] =
'\0'
;
}
else
{
*ptr =
'\0'
;
Move(item_start, PL_langinfo_buf, ptr - PL_langinfo_buf,
char
);
}
if
(toggle) {
RESTORE_LC_NUMERIC();
}
retval = PL_langinfo_buf;
break
;
# else
case
RADIXCHAR:
# endif
case
THOUSEP:
if
(toggle) {
STORE_LC_NUMERIC_FORCE_TO_UNDERLYING();
}
LOCALECONV_LOCK;
# ifdef TS_W32_BROKEN_LOCALECONV
save_thread = savepv(my_setlocale(LC_ALL, NULL));
_configthreadlocale(_DISABLE_PER_THREAD_LOCALE);
save_global = savepv(my_setlocale(LC_ALL, NULL));
my_setlocale(LC_ALL, save_thread);
# if 0
needed_size = GetNumberFormatEx(PL_numeric_name, 0,
"1234.5"
, NULL, PL_langinfo_buf, PL_langinfo_bufsize);
DEBUG_L(PerlIO_printf(Perl_debug_log,
"%s: %d: return from GetNumber, count=%d, val=%s\n"
,
__FILE__, __LINE__, needed_size, PL_langinfo_buf));
# endif
# endif
lc =
localeconv
();
if
(! lc) {
temp =
""
;
}
else
{
temp = (item == RADIXCHAR)
? lc->decimal_point
: lc->thousands_sep;
if
(! temp) {
temp =
""
;
}
}
retval = save_to_buffer(temp, &PL_langinfo_buf,
&PL_langinfo_bufsize, 0);
# ifdef TS_W32_BROKEN_LOCALECONV
my_setlocale(LC_ALL, save_global);
_configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
my_setlocale(LC_ALL, save_thread);
Safefree(save_global);
Safefree(save_thread);
# endif
LOCALECONV_UNLOCK;
if
(toggle) {
RESTORE_LC_NUMERIC();
}
break
;
# endif
# ifdef HAS_STRFTIME
case
D_FMT:
return
"%x"
;
case
T_FMT:
return
"%X"
;
case
D_T_FMT:
return
"%c"
;
case
ERA_D_FMT:
case
ERA_T_FMT:
case
ERA_D_T_FMT:
case
T_FMT_AMPM:
case
ABDAY_1:
case
ABDAY_2:
case
ABDAY_3:
case
ABDAY_4:
case
ABDAY_5:
case
ABDAY_6:
case
ABDAY_7:
case
ALT_DIGITS:
case
AM_STR:
case
PM_STR:
case
ABMON_1:
case
ABMON_2:
case
ABMON_3:
case
ABMON_4:
case
ABMON_5:
case
ABMON_6:
case
ABMON_7:
case
ABMON_8:
case
ABMON_9:
case
ABMON_10:
case
ABMON_11:
case
ABMON_12:
case
DAY_1:
case
DAY_2:
case
DAY_3:
case
DAY_4:
case
DAY_5:
case
DAY_6:
case
DAY_7:
case
MON_1:
case
MON_2:
case
MON_3:
case
MON_4:
case
MON_5:
case
MON_6:
case
MON_7:
case
MON_8:
case
MON_9:
case
MON_10:
case
MON_11:
case
MON_12:
init_tm(&
tm
);
tm
.tm_sec = 30;
tm
.tm_min = 30;
tm
.tm_hour = 6;
tm
.tm_year = 2017 - 1900;
tm
.tm_wday = 0;
tm
.tm_mon = 0;
GCC_DIAG_IGNORE_STMT(-Wimplicit-fallthrough);
switch
(item) {
default
:
Perl_croak(aTHX_
"panic: %s: %d: switch case: %d problem"
,
__FILE__, __LINE__, item);
NOT_REACHED;
case
PM_STR:
tm
.tm_hour = 18;
case
AM_STR:
format =
"%p"
;
break
;
case
ABDAY_7:
tm
.tm_wday++;
case
ABDAY_6:
tm
.tm_wday++;
case
ABDAY_5:
tm
.tm_wday++;
case
ABDAY_4:
tm
.tm_wday++;
case
ABDAY_3:
tm
.tm_wday++;
case
ABDAY_2:
tm
.tm_wday++;
case
ABDAY_1:
format =
"%a"
;
break
;
case
DAY_7:
tm
.tm_wday++;
case
DAY_6:
tm
.tm_wday++;
case
DAY_5:
tm
.tm_wday++;
case
DAY_4:
tm
.tm_wday++;
case
DAY_3:
tm
.tm_wday++;
case
DAY_2:
tm
.tm_wday++;
case
DAY_1:
format =
"%A"
;
break
;
case
ABMON_12:
tm
.tm_mon++;
case
ABMON_11:
tm
.tm_mon++;
case
ABMON_10:
tm
.tm_mon++;
case
ABMON_9:
tm
.tm_mon++;
case
ABMON_8:
tm
.tm_mon++;
case
ABMON_7:
tm
.tm_mon++;
case
ABMON_6:
tm
.tm_mon++;
case
ABMON_5:
tm
.tm_mon++;
case
ABMON_4:
tm
.tm_mon++;
case
ABMON_3:
tm
.tm_mon++;
case
ABMON_2:
tm
.tm_mon++;
case
ABMON_1:
format =
"%b"
;
break
;
case
MON_12:
tm
.tm_mon++;
case
MON_11:
tm
.tm_mon++;
case
MON_10:
tm
.tm_mon++;
case
MON_9:
tm
.tm_mon++;
case
MON_8:
tm
.tm_mon++;
case
MON_7:
tm
.tm_mon++;
case
MON_6:
tm
.tm_mon++;
case
MON_5:
tm
.tm_mon++;
case
MON_4:
tm
.tm_mon++;
case
MON_3:
tm
.tm_mon++;
case
MON_2:
tm
.tm_mon++;
case
MON_1:
format =
"%B"
;
break
;
case
T_FMT_AMPM:
format =
"%r"
;
return_format = TRUE;
break
;
case
ERA_D_FMT:
format =
"%Ex"
;
return_format = TRUE;
break
;
case
ERA_T_FMT:
format =
"%EX"
;
return_format = TRUE;
break
;
case
ERA_D_T_FMT:
format =
"%Ec"
;
return_format = TRUE;
break
;
case
ALT_DIGITS:
tm
.tm_wday = 0;
format =
"%Ow"
;
break
;
}
GCC_DIAG_RESTORE_STMT;
while
(0 ==
strftime
(PL_langinfo_buf, PL_langinfo_bufsize,
format, &
tm
))
{
Size_t format_size =
strlen
(format) + 1;
Size_t mod_size = format_size + 1;
char
* mod_format;
char
* temp_result;
Newx(mod_format, mod_size,
char
);
Newx(temp_result, PL_langinfo_bufsize,
char
);
*mod_format =
' '
;
my_strlcpy(mod_format + 1, format, mod_size);
len =
strftime
(temp_result,
PL_langinfo_bufsize,
mod_format, &
tm
);
Safefree(mod_format);
Safefree(temp_result);
if
(len == 0) {
if
(PL_langinfo_bufsize > 100 * format_size) {
*PL_langinfo_buf =
'\0'
;
}
else
{
PL_langinfo_bufsize *= 2;
PL_langinfo_bufsize++;
Renew(PL_langinfo_buf, PL_langinfo_bufsize,
char
);
continue
;
}
}
break
;
}
if
( item == ALT_DIGITS
&& strEQ(PL_langinfo_buf,
"0"
))
{
*PL_langinfo_buf =
'\0'
;
}
retval = PL_langinfo_buf;
if
(return_format) {
if
(strEQ(PL_langinfo_buf, format)) {
*PL_langinfo_buf =
'\0'
;
}
else
{
retval = save_to_buffer(format, &PL_langinfo_buf,
&PL_langinfo_bufsize, 0);
}
}
break
;
# endif
}
}
return
retval;
#endif
}
int
Perl_init_i18nl10n(pTHX_
int
printwarn)
{
int
ok = 1;
#ifndef USE_LOCALE
PERL_UNUSED_ARG(printwarn);
#else /* USE_LOCALE */
# ifdef __GLIBC__
const
char
*
const
language = PerlEnv_getenv(
"LANGUAGE"
);
# endif
const
char
*
const
setlocale_init = (PerlEnv_getenv(
"PERL_SKIP_LOCALE_INIT"
))
? NULL
:
""
;
const
char
* trial_locales[5];
unsigned
int
trial_locales_count;
const
char
*
const
lc_all = PerlEnv_getenv(
"LC_ALL"
);
const
char
*
const
lang = PerlEnv_getenv(
"LANG"
);
bool
setlocale_failure = FALSE;
unsigned
int
i;
const
char
*
const
bad_lang_use_once = PerlEnv_getenv(
"PERL_BADLANG"
);
const
bool
locwarn = (printwarn > 1
|| ( printwarn
&& ( ! bad_lang_use_once
|| (
*bad_lang_use_once
&& strNE(
"0"
, bad_lang_use_once)))));
const
char
* sl_result[NOMINAL_LC_ALL_INDEX + 1];
const
char
* curlocales[NOMINAL_LC_ALL_INDEX + 1];
# ifdef WIN32
# define SYSTEM_DEFAULT_LOCALE
# endif
# ifdef SYSTEM_DEFAULT_LOCALE
const
char
*system_default_locale = NULL;
# endif
# ifndef DEBUGGING
# define DEBUG_LOCALE_INIT(a,b,c)
# else
DEBUG_INITIALIZATION_set(cBOOL(PerlEnv_getenv(
"PERL_DEBUG_LOCALE_INIT"
)));
# define DEBUG_LOCALE_INIT(category, locale, result) \
STMT_START { \
if
(debug_initialization) { \
PerlIO_printf(Perl_debug_log, \
"%s:%d: %s\n"
, \
__FILE__, __LINE__, \
setlocale_debug_string(category, \
locale, \
result)); \
} \
} STMT_END
# ifdef USE_LOCALE_NUMERIC
assert
(categories[LC_NUMERIC_INDEX] == LC_NUMERIC);
assert
(strEQ(category_names[LC_NUMERIC_INDEX],
"LC_NUMERIC"
));
# ifdef USE_POSIX_2008_LOCALE
assert
(category_masks[LC_NUMERIC_INDEX] == LC_NUMERIC_MASK);
# endif
# endif
# ifdef USE_LOCALE_CTYPE
assert
(categories[LC_CTYPE_INDEX] == LC_CTYPE);
assert
(strEQ(category_names[LC_CTYPE_INDEX],
"LC_CTYPE"
));
# ifdef USE_POSIX_2008_LOCALE
assert
(category_masks[LC_CTYPE_INDEX] == LC_CTYPE_MASK);
# endif
# endif
# ifdef USE_LOCALE_COLLATE
assert
(categories[LC_COLLATE_INDEX] == LC_COLLATE);
assert
(strEQ(category_names[LC_COLLATE_INDEX],
"LC_COLLATE"
));
# ifdef USE_POSIX_2008_LOCALE
assert
(category_masks[LC_COLLATE_INDEX] == LC_COLLATE_MASK);
# endif
# endif
# ifdef USE_LOCALE_TIME
assert
(categories[LC_TIME_INDEX] == LC_TIME);
assert
(strEQ(category_names[LC_TIME_INDEX],
"LC_TIME"
));
# ifdef USE_POSIX_2008_LOCALE
assert
(category_masks[LC_TIME_INDEX] == LC_TIME_MASK);
# endif
# endif
# ifdef USE_LOCALE_MESSAGES
assert
(categories[LC_MESSAGES_INDEX] == LC_MESSAGES);
assert
(strEQ(category_names[LC_MESSAGES_INDEX],
"LC_MESSAGES"
));
# ifdef USE_POSIX_2008_LOCALE
assert
(category_masks[LC_MESSAGES_INDEX] == LC_MESSAGES_MASK);
# endif
# endif
# ifdef USE_LOCALE_MONETARY
assert
(categories[LC_MONETARY_INDEX] == LC_MONETARY);
assert
(strEQ(category_names[LC_MONETARY_INDEX],
"LC_MONETARY"
));
# ifdef USE_POSIX_2008_LOCALE
assert
(category_masks[LC_MONETARY_INDEX] == LC_MONETARY_MASK);
# endif
# endif
# ifdef USE_LOCALE_ADDRESS
assert
(categories[LC_ADDRESS_INDEX] == LC_ADDRESS);
assert
(strEQ(category_names[LC_ADDRESS_INDEX],
"LC_ADDRESS"
));
# ifdef USE_POSIX_2008_LOCALE
assert
(category_masks[LC_ADDRESS_INDEX] == LC_ADDRESS_MASK);
# endif
# endif
# ifdef USE_LOCALE_IDENTIFICATION
assert
(categories[LC_IDENTIFICATION_INDEX] == LC_IDENTIFICATION);
assert
(strEQ(category_names[LC_IDENTIFICATION_INDEX],
"LC_IDENTIFICATION"
));
# ifdef USE_POSIX_2008_LOCALE
assert
(category_masks[LC_IDENTIFICATION_INDEX] == LC_IDENTIFICATION_MASK);
# endif
# endif
# ifdef USE_LOCALE_MEASUREMENT
assert
(categories[LC_MEASUREMENT_INDEX] == LC_MEASUREMENT);
assert
(strEQ(category_names[LC_MEASUREMENT_INDEX],
"LC_MEASUREMENT"
));
# ifdef USE_POSIX_2008_LOCALE
assert
(category_masks[LC_MEASUREMENT_INDEX] == LC_MEASUREMENT_MASK);
# endif
# endif
# ifdef USE_LOCALE_PAPER
assert
(categories[LC_PAPER_INDEX] == LC_PAPER);
assert
(strEQ(category_names[LC_PAPER_INDEX],
"LC_PAPER"
));
# ifdef USE_POSIX_2008_LOCALE
assert
(category_masks[LC_PAPER_INDEX] == LC_PAPER_MASK);
# endif
# endif
# ifdef USE_LOCALE_TELEPHONE
assert
(categories[LC_TELEPHONE_INDEX] == LC_TELEPHONE);
assert
(strEQ(category_names[LC_TELEPHONE_INDEX],
"LC_TELEPHONE"
));
# ifdef USE_POSIX_2008_LOCALE
assert
(category_masks[LC_TELEPHONE_INDEX] == LC_TELEPHONE_MASK);
# endif
# endif
# ifdef USE_LOCALE_SYNTAX
assert
(categories[LC_SYNTAX_INDEX] == LC_SYNTAX);
assert
(strEQ(category_names[LC_SYNTAX_INDEX],
"LC_SYNTAX"
));
# ifdef USE_POSIX_2008_LOCALE
assert
(category_masks[LC_SYNTAX_INDEX] == LC_SYNTAX_MASK);
# endif
# endif
# ifdef USE_LOCALE_TOD
assert
(categories[LC_TOD_INDEX] == LC_TOD);
assert
(strEQ(category_names[LC_TOD_INDEX],
"LC_TOD"
));
# ifdef USE_POSIX_2008_LOCALE
assert
(category_masks[LC_TOD_INDEX] == LC_TOD_MASK);
# endif
# endif
# ifdef LC_ALL
assert
(categories[LC_ALL_INDEX] == LC_ALL);
assert
(strEQ(category_names[LC_ALL_INDEX],
"LC_ALL"
));
assert
(NOMINAL_LC_ALL_INDEX == LC_ALL_INDEX);
# ifdef USE_POSIX_2008_LOCALE
assert
(category_masks[LC_ALL_INDEX] == LC_ALL_MASK);
# endif
# endif
# endif /* DEBUGGING */
#ifdef HAS_MBRLEN
memzero(&PL_mbrlen_ps,
sizeof
(PL_mbrlen_ps));
#endif
#ifdef HAS_MBRTOWC
memzero(&PL_mbrtowc_ps,
sizeof
(PL_mbrtowc_ps));
#endif
#ifdef HAS_WCTOMBR
wcrtomb(NULL, L
'\0'
, &PL_wcrtomb_ps);
#endif
my_strlcpy(PL_locale_utf8ness, C_and_POSIX_utf8ness,
sizeof
(PL_locale_utf8ness));
Zero(curlocales, NOMINAL_LC_ALL_INDEX,
char
*);
# ifdef USE_THREAD_SAFE_LOCALE
# ifdef WIN32
_configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
# endif
# endif
# ifdef USE_POSIX_2008_LOCALE
PL_C_locale_obj = newlocale(LC_ALL_MASK,
"C"
, (locale_t) 0);
if
(! PL_C_locale_obj) {
Perl_croak_nocontext(
"panic: Cannot create POSIX 2008 C locale object; errno=%d"
,
errno
);
}
if
(DEBUG_Lv_TEST || debug_initialization) {
PerlIO_printf(Perl_debug_log,
"%s:%d: created C object %p\n"
, __FILE__, __LINE__, PL_C_locale_obj);
}
# endif
# ifdef USE_LOCALE_NUMERIC
PL_numeric_radix_sv = newSVpvs(
"."
);
# endif
# if defined(USE_POSIX_2008_LOCALE) && ! defined(HAS_QUERYLOCALE)
do_setlocale_c(LC_ALL, my_setlocale(LC_ALL, NULL));
# endif
# ifdef LOCALE_ENVIRON_REQUIRED
# ifndef LC_ALL
# error Ultrix without LC_ALL not implemented
# else
{
bool
done = FALSE;
if
(lang) {
sl_result[LC_ALL_INDEX] = do_setlocale_c(LC_ALL, setlocale_init);
DEBUG_LOCALE_INIT(LC_ALL, setlocale_init, sl_result[LC_ALL_INDEX]);
if
(sl_result[LC_ALL_INDEX])
done = TRUE;
else
setlocale_failure = TRUE;
}
if
(! setlocale_failure) {
const
char
* locale_param;
for
(i = 0; i < LC_ALL_INDEX; i++) {
locale_param = (! done && (lang || PerlEnv_getenv(category_names[i])))
? setlocale_init
: NULL;
sl_result[i] = do_setlocale_r(categories[i], locale_param);
if
(! sl_result[i]) {
setlocale_failure = TRUE;
}
DEBUG_LOCALE_INIT(categories[i], locale_param, sl_result[i]);
}
}
}
# endif /* LC_ALL */
# endif /* LOCALE_ENVIRON_REQUIRED */
trial_locales[0] = setlocale_init;
trial_locales_count = 1;
for
(i= 0; i < trial_locales_count; i++) {
const
char
* trial_locale = trial_locales[i];
if
(i > 0) {
setlocale_failure = FALSE;
# ifdef SYSTEM_DEFAULT_LOCALE
# ifdef WIN32 /* Note that assumes Win32 has LC_ALL */
if
(strEQ(trial_locale,
""
)) {
unsigned
int
j;
system_default_locale = do_setlocale_c(LC_ALL,
""
);
DEBUG_LOCALE_INIT(LC_ALL,
""
, system_default_locale);
if
(! system_default_locale) {
goto
next_iteration;
}
for
(j = 0; j < trial_locales_count; j++) {
if
(strEQ(system_default_locale, trial_locales[j])) {
goto
next_iteration;
}
}
trial_locale = system_default_locale;
}
# else
# error SYSTEM_DEFAULT_LOCALE only implemented for Win32
# endif
# endif /* SYSTEM_DEFAULT_LOCALE */
}
# ifdef LC_ALL
sl_result[LC_ALL_INDEX] = do_setlocale_c(LC_ALL, trial_locale);
DEBUG_LOCALE_INIT(LC_ALL, trial_locale, sl_result[LC_ALL_INDEX]);
if
(! sl_result[LC_ALL_INDEX]) {
setlocale_failure = TRUE;
}
else
{
trial_locale = NULL;
}
# endif /* LC_ALL */
if
(! setlocale_failure) {
unsigned
int
j;
for
(j = 0; j < NOMINAL_LC_ALL_INDEX; j++) {
curlocales[j]
= savepv(do_setlocale_r(categories[j], trial_locale));
if
(! curlocales[j]) {
setlocale_failure = TRUE;
}
DEBUG_LOCALE_INIT(categories[j], trial_locale, curlocales[j]);
}
if
(LIKELY(! setlocale_failure)) {
break
;
}
}
ok = 0;
if
(i == 0) {
unsigned
int
j;
if
(locwarn) {
# ifdef LC_ALL
PerlIO_printf(Perl_error_log,
"perl: warning: Setting locale failed.\n"
);
# else /* !LC_ALL */
PerlIO_printf(Perl_error_log,
"perl: warning: Setting locale failed for the categories:\n\t"
);
for
(j = 0; j < NOMINAL_LC_ALL_INDEX; j++) {
if
(! curlocales[j]) {
PerlIO_printf(Perl_error_log, category_names[j]);
}
else
{
Safefree(curlocales[j]);
}
}
# endif /* LC_ALL */
PerlIO_printf(Perl_error_log,
"perl: warning: Please check that your locale settings:\n"
);
# ifdef __GLIBC__
PerlIO_printf(Perl_error_log,
"\tLANGUAGE = %c%s%c,\n"
,
language ?
'"'
:
'('
,
language ? language :
"unset"
,
language ?
'"'
:
')'
);
# endif
PerlIO_printf(Perl_error_log,
"\tLC_ALL = %c%s%c,\n"
,
lc_all ?
'"'
:
'('
,
lc_all ? lc_all :
"unset"
,
lc_all ?
'"'
:
')'
);
# if defined(USE_ENVIRON_ARRAY)
{
char
**e;
for
(e = environ; *e; e++) {
const
STRLEN prefix_len =
sizeof
(
"LC_"
) - 1;
STRLEN uppers_len;
if
( strBEGINs(*e,
"LC_"
)
&& ! strBEGINs(*e,
"LC_ALL="
)
&& (uppers_len =
strspn
(*e + prefix_len,
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
))
&& ((*e)[prefix_len + uppers_len] ==
'='
))
{
PerlIO_printf(Perl_error_log,
"\t%.*s = \"%s\",\n"
,
(
int
) (prefix_len + uppers_len), *e,
*e + prefix_len + uppers_len + 1);
}
}
}
# else
PerlIO_printf(Perl_error_log,
"\t(possibly more locale environment variables)\n"
);
# endif
PerlIO_printf(Perl_error_log,
"\tLANG = %c%s%c\n"
,
lang ?
'"'
:
'('
,
lang ? lang :
"unset"
,
lang ?
'"'
:
')'
);
PerlIO_printf(Perl_error_log,
" are supported and installed on your system.\n"
);
}
if
(lc_all) {
for
(j = 0; j < trial_locales_count; j++) {
if
(strEQ(lc_all, trial_locales[j])) {
goto
done_lc_all;
}
}
trial_locales[trial_locales_count++] = lc_all;
}
done_lc_all:
if
(lang) {
for
(j = 0; j < trial_locales_count; j++) {
if
(strEQ(lang, trial_locales[j])) {
goto
done_lang;
}
}
trial_locales[trial_locales_count++] = lang;
}
done_lang:
# if defined(WIN32) && defined(LC_ALL)
trial_locales[trial_locales_count++] =
""
;
# endif
for
(j = 0; j < trial_locales_count; j++) {
if
(strEQ(
"C"
, trial_locales[j])) {
goto
done_C;
}
}
trial_locales[trial_locales_count++] =
"C"
;
done_C: ;
}
# ifdef WIN32
next_iteration: ;
# endif
}
if
(ok < 1) {
const
char
* msg;
if
(! setlocale_failure) {
msg =
"Falling back to"
;
}
else
{
unsigned
int
j;
i--;
ok = -1;
msg =
"Failed to fall back to"
;
for
(j = 0; j < NOMINAL_LC_ALL_INDEX; j++) {
Safefree(curlocales[j]);
curlocales[j] = savepv(do_setlocale_r(categories[j], NULL));
DEBUG_LOCALE_INIT(categories[j], NULL, curlocales[j]);
}
}
if
(locwarn) {
const
char
* description;
const
char
* name =
""
;
if
(strEQ(trial_locales[i],
"C"
)) {
description =
"the standard locale"
;
name =
"C"
;
}
# ifdef SYSTEM_DEFAULT_LOCALE
else
if
(strEQ(trial_locales[i],
""
)) {
description =
"the system default locale"
;
if
(system_default_locale) {
name = system_default_locale;
}
}
# endif /* SYSTEM_DEFAULT_LOCALE */
else
{
description =
"a fallback locale"
;
name = trial_locales[i];
}
if
(name && strNE(name,
""
)) {
PerlIO_printf(Perl_error_log,
"perl: warning: %s %s (\"%s\").\n"
, msg, description, name);
}
else
{
PerlIO_printf(Perl_error_log,
"perl: warning: %s %s.\n"
, msg, description);
}
}
}
# ifdef USE_LOCALE_CTYPE
new_ctype(curlocales[LC_CTYPE_INDEX]);
# endif
# ifdef USE_LOCALE_COLLATE
new_collate(curlocales[LC_COLLATE_INDEX]);
# endif
# ifdef USE_LOCALE_NUMERIC
new_numeric(curlocales[LC_NUMERIC_INDEX]);
# endif
for
(i = 0; i < NOMINAL_LC_ALL_INDEX; i++) {
# if defined(USE_ITHREADS) && ! defined(USE_THREAD_SAFE_LOCALE)
(
void
) _is_cur_LC_category_utf8(categories[i]);
# endif
Safefree(curlocales[i]);
}
# if defined(USE_PERLIO) && defined(USE_LOCALE_CTYPE)
PL_utf8locale = PL_in_utf8_CTYPE_locale;
{
const
char
*p = PerlEnv_getenv(
"PERL_UNICODE"
);
PL_unicode = p ? parse_unicode_opts(&p) : 0;
if
(PL_unicode & PERL_UNICODE_UTF8CACHEASSERT_FLAG)
PL_utf8cache = -1;
}
# endif
#endif /* USE_LOCALE */
#ifdef DEBUGGING
DEBUG_INITIALIZATION_set(FALSE);
#endif
return
ok;
}
#ifdef USE_LOCALE_COLLATE
char
*
Perl__mem_collxfrm(pTHX_
const
char
*input_string,
STRLEN len,
STRLEN *xlen,
bool
utf8
)
{
#define COLLXFRM_HDR_LEN sizeof(PL_collation_ix)
char
* s = (
char
*) input_string;
STRLEN s_strlen =
strlen
(input_string);
char
*xbuf = NULL;
STRLEN xAlloc;
STRLEN length_in_chars;
bool
first_time = TRUE;
PERL_ARGS_ASSERT__MEM_COLLXFRM;
assert
(*(input_string + len) ==
'\0'
);
if
(PL_collxfrm_base == 0 && PL_collxfrm_mult == 0) {
DEBUG_L(PerlIO_printf(Perl_debug_log,
"_mem_collxfrm: locale's collation is defective\n"
));
goto
bad;
}
if
(UNLIKELY(s_strlen < len)) {
char
* e = s + len;
char
* sans_nuls;
STRLEN sans_nuls_len;
int
try_non_controls;
char
this_replacement_char[] =
"?\0"
;
STRLEN this_replacement_len;
if
(PL_strxfrm_NUL_replacement ==
'\0'
) {
int
j;
char
* cur_min_x = NULL;
DEBUG_Lv(PerlIO_printf(Perl_debug_log,
"Looking to replace NUL\n"
));
for
(try_non_controls = 0;
try_non_controls < 2;
try_non_controls++)
{
for
(j = 1; j < 256; j++) {
char
* x;
STRLEN x_len;
STRLEN trial_len = 1;
char
cur_source[] = {
'\0'
,
'\0'
};
if
(! try_non_controls && (PL_in_utf8_COLLATE_locale)
? ! isCNTRL_L1(j)
: ! isCNTRL_LC(j))
{
continue
;
}
cur_source[0] = (
char
) j;
x = _mem_collxfrm(cur_source, trial_len, &x_len,
0
);
if
(! x) {
continue
;
}
if
( cur_min_x == NULL
|| strLT(x + COLLXFRM_HDR_LEN,
cur_min_x + COLLXFRM_HDR_LEN))
{
PL_strxfrm_NUL_replacement = j;
Safefree(cur_min_x);
cur_min_x = x;
}
else
{
Safefree(x);
}
}
if
(cur_min_x) {
break
;
}
DEBUG_L(PerlIO_printf(Perl_debug_log,
"_mem_collxfrm: No control worked. Trying non-controls\n"
));
}
if
(! cur_min_x) {
DEBUG_L(PerlIO_printf(Perl_debug_log,
"_mem_collxfrm: Couldn't find any character to replace"
" embedded NULs in locale %s with"
, PL_collation_name));
goto
bad;
}
DEBUG_L(PerlIO_printf(Perl_debug_log,
"_mem_collxfrm: Replacing embedded NULs in locale %s with "
"0x%02X\n"
, PL_collation_name, PL_strxfrm_NUL_replacement));
Safefree(cur_min_x);
}
if
( ! UVCHR_IS_INVARIANT(PL_strxfrm_NUL_replacement) && utf8) {
this_replacement_char[0] =
UTF8_EIGHT_BIT_HI(PL_strxfrm_NUL_replacement);
this_replacement_char[1] =
UTF8_EIGHT_BIT_LO(PL_strxfrm_NUL_replacement);
this_replacement_len = 2;
}
else
{
this_replacement_char[0] = PL_strxfrm_NUL_replacement;
this_replacement_len = 1;
}
sans_nuls_len = (len * this_replacement_len) + 1;
Newx(sans_nuls, sans_nuls_len,
char
);
*sans_nuls =
'\0'
;
while
(s + s_strlen < e) {
my_strlcat(sans_nuls, s, sans_nuls_len);
my_strlcat(sans_nuls, this_replacement_char, sans_nuls_len);
s += s_strlen + 1;
s_strlen =
strlen
(s);
}
my_strlcat(sans_nuls, s, sans_nuls_len);
s = sans_nuls;
len =
strlen
(s);
}
if
(utf8 != PL_in_utf8_COLLATE_locale) {
const
char
*
const
t = s;
if
(! utf8) {
s = (
char
*) bytes_to_utf8((
const
U8 *) s, &len);
utf8 = TRUE;
}
else
{
s = (
char
*) bytes_from_utf8((
const
U8 *) s, &len, &utf8);
if
(UNLIKELY(utf8)) {
utf8 = FALSE;
if
(! PL_strxfrm_max_cp) {
int
j;
char
* cur_max_x = NULL;
for
(j = 1; j < 256; j++) {
char
* x;
STRLEN x_len;
char
cur_source[] = {
'\0'
,
'\0'
};
cur_source[0] = (
char
) j;
x = _mem_collxfrm(cur_source, 1, &x_len, FALSE);
if
(! x) {
continue
;
}
if
( cur_max_x == NULL
|| strGT(x + COLLXFRM_HDR_LEN,
cur_max_x + COLLXFRM_HDR_LEN))
{
PL_strxfrm_max_cp = j;
Safefree(cur_max_x);
cur_max_x = x;
}
else
{
Safefree(x);
}
}
if
(! cur_max_x) {
DEBUG_L(PerlIO_printf(Perl_debug_log,
"_mem_collxfrm: Couldn't find any character to"
" replace above-Latin1 chars in locale %s with"
,
PL_collation_name));
goto
bad;
}
DEBUG_L(PerlIO_printf(Perl_debug_log,
"_mem_collxfrm: highest 1-byte collating character"
" in locale %s is 0x%02X\n"
,
PL_collation_name,
PL_strxfrm_max_cp));
Safefree(cur_max_x);
}
Newx(s, len,
char
);
{
STRLEN i;
STRLEN d= 0;
char
* e = (
char
*) t + len;
for
(i = 0; i < len; i+= UTF8SKIP(t + i)) {
U8 cur_char = t[i];
if
(UTF8_IS_INVARIANT(cur_char)) {
s[d++] = cur_char;
}
else
if
(UTF8_IS_NEXT_CHAR_DOWNGRADEABLE(t + i, e)) {
s[d++] = EIGHT_BIT_UTF8_TO_NATIVE(cur_char, t[i+1]);
}
else
{
s[d++] = PL_strxfrm_max_cp;
}
}
s[d++] =
'\0'
;
Renew(s, d,
char
);
}
}
}
if
(t != input_string) {
Safefree(t);
}
}
length_in_chars = (utf8)
? utf8_length((U8 *) s, (U8 *) s + len)
: len;
xAlloc = COLLXFRM_HDR_LEN
+ PL_collxfrm_base
+ (PL_collxfrm_mult * length_in_chars);
Newx(xbuf, xAlloc,
char
);
if
(UNLIKELY(! xbuf)) {
DEBUG_L(PerlIO_printf(Perl_debug_log,
"_mem_collxfrm: Couldn't malloc %zu bytes\n"
, xAlloc));
goto
bad;
}
*(U32*)xbuf = PL_collation_ix;
for
(;;) {
*xlen =
strxfrm
(xbuf + COLLXFRM_HDR_LEN, s, xAlloc - COLLXFRM_HDR_LEN);
if
(*xlen < xAlloc - COLLXFRM_HDR_LEN) {
while
( (*xlen) > 0
&& *(xbuf + COLLXFRM_HDR_LEN + (*xlen) - 1) ==
'\0'
)
{
(*xlen)--;
}
if
(! first_time) {
STRLEN needed = *xlen + 1;
STRLEN computed_guess = PL_collxfrm_base
+ (PL_collxfrm_mult * length_in_chars);
const
STRLEN new_m = (length_in_chars != 0)
? needed / length_in_chars
: PL_collxfrm_mult;
DEBUG_Lv(PerlIO_printf(Perl_debug_log,
"%s: %d: initial size of %zu bytes for a length "
"%zu string was insufficient, %zu needed\n"
,
__FILE__, __LINE__,
computed_guess, length_in_chars, needed));
if
(length_in_chars > 1 && new_m > PL_collxfrm_mult) {
# ifdef DEBUGGING
STRLEN old_m = PL_collxfrm_mult;
STRLEN old_b = PL_collxfrm_base;
# endif
PL_collxfrm_mult = new_m;
PL_collxfrm_base = 1;
computed_guess = PL_collxfrm_base
+ (PL_collxfrm_mult * length_in_chars);
if
(computed_guess < needed) {
PL_collxfrm_base += needed - computed_guess;
}
DEBUG_Lv(PerlIO_printf(Perl_debug_log,
"%s: %d: slope is now %zu; was %zu, base "
"is now %zu; was %zu\n"
,
__FILE__, __LINE__,
PL_collxfrm_mult, old_m,
PL_collxfrm_base, old_b));
}
else
{
const
STRLEN new_b = needed
- computed_guess
+ PL_collxfrm_base;
DEBUG_Lv(PerlIO_printf(Perl_debug_log,
"%s: %d: base is now %zu; was %zu\n"
,
__FILE__, __LINE__,
new_b, PL_collxfrm_base));
PL_collxfrm_base = new_b;
}
}
break
;
}
if
(UNLIKELY(*xlen >= PERL_INT_MAX)) {
DEBUG_L(PerlIO_printf(Perl_debug_log,
"_mem_collxfrm: Needed %zu bytes, max permissible is %u\n"
,
*xlen, PERL_INT_MAX));
goto
bad;
}
if
(LIKELY(PL_strxfrm_is_behaved) && first_time) {
xAlloc = *xlen + COLLXFRM_HDR_LEN + 1;
}
else
{
xAlloc += (xAlloc / 4) + 1;
PL_strxfrm_is_behaved = FALSE;
# ifdef DEBUGGING
if
(DEBUG_Lv_TEST || debug_initialization) {
PerlIO_printf(Perl_debug_log,
"_mem_collxfrm required more space than previously calculated"
" for locale %s, trying again with new guess=%zu+%zu\n"
,
PL_collation_name, COLLXFRM_HDR_LEN,
xAlloc - COLLXFRM_HDR_LEN);
}
# endif
}
Renew(xbuf, xAlloc,
char
);
if
(UNLIKELY(! xbuf)) {
DEBUG_L(PerlIO_printf(Perl_debug_log,
"_mem_collxfrm: Couldn't realloc %zu bytes\n"
, xAlloc));
goto
bad;
}
first_time = FALSE;
}
# ifdef DEBUGGING
if
(DEBUG_Lv_TEST || debug_initialization) {
print_collxfrm_input_and_return(s, s + len, xlen, utf8);
PerlIO_printf(Perl_debug_log,
"Its xfrm is:"
);
PerlIO_printf(Perl_debug_log,
"%s\n"
,
_byte_dump_string((U8 *) xbuf + COLLXFRM_HDR_LEN,
*xlen, 1));
}
# endif
Renew(xbuf, COLLXFRM_HDR_LEN + *xlen + 1,
char
);
if
(s != input_string) {
Safefree(s);
}
return
xbuf;
bad:
# ifdef DEBUGGING
if
(DEBUG_Lv_TEST || debug_initialization) {
print_collxfrm_input_and_return(s, s + len, NULL, utf8);
}
# endif
Safefree(xbuf);
if
(s != input_string) {
Safefree(s);
}
*xlen = 0;
return
NULL;
}
# ifdef DEBUGGING
STATIC
void
S_print_collxfrm_input_and_return(pTHX_
const
char
*
const
s,
const
char
*
const
e,
const
STRLEN *
const
xlen,
const
bool
is_utf8)
{
PERL_ARGS_ASSERT_PRINT_COLLXFRM_INPUT_AND_RETURN;
PerlIO_printf(Perl_debug_log,
"_mem_collxfrm[%"
UVuf
"]: returning "
,
(UV)PL_collation_ix);
if
(xlen) {
PerlIO_printf(Perl_debug_log,
"%zu"
, *xlen);
}
else
{
PerlIO_printf(Perl_debug_log,
"NULL"
);
}
PerlIO_printf(Perl_debug_log,
" for locale '%s', string='"
,
PL_collation_name);
print_bytes_for_locale(s, e, is_utf8);
PerlIO_printf(Perl_debug_log,
"'\n"
);
}
# endif /* DEBUGGING */
#endif /* USE_LOCALE_COLLATE */
#ifdef USE_LOCALE
# ifdef DEBUGGING
STATIC
void
S_print_bytes_for_locale(pTHX_
const
char
*
const
s,
const
char
*
const
e,
const
bool
is_utf8)
{
const
char
* t = s;
bool
prev_was_printable = TRUE;
bool
first_time = TRUE;
PERL_ARGS_ASSERT_PRINT_BYTES_FOR_LOCALE;
while
(t < e) {
UV cp = (is_utf8)
? utf8_to_uvchr_buf((U8 *) t, e, NULL)
: * (U8 *) t;
if
(isPRINT(cp)) {
if
(! prev_was_printable) {
PerlIO_printf(Perl_debug_log,
" "
);
}
PerlIO_printf(Perl_debug_log,
"%c"
, (U8) cp);
prev_was_printable = TRUE;
}
else
{
if
(! first_time) {
PerlIO_printf(Perl_debug_log,
" "
);
}
PerlIO_printf(Perl_debug_log,
"%02"
UVXf, cp);
prev_was_printable = FALSE;
}
t += (is_utf8) ? UTF8SKIP(t) : 1;
first_time = FALSE;
}
}
# endif /* #ifdef DEBUGGING */
STATIC
const
char
*
S_switch_category_locale_to_template(pTHX_
const
int
switch_category,
const
int
template_category,
const
char
* template_locale)
{
char
* restore_to_locale = NULL;
if
(switch_category == template_category) {
return
NULL;
}
restore_to_locale = stdize_locale(savepv(do_setlocale_r(switch_category,
NULL)));
if
(! restore_to_locale) {
Perl_croak(aTHX_
"panic: %s: %d: Could not find current %s locale, errno=%d\n"
,
__FILE__, __LINE__, category_name(switch_category),
errno
);
}
if
(template_locale == NULL) {
template_locale = do_setlocale_r(template_category, NULL);
if
(! template_locale) {
Perl_croak(aTHX_
"panic: %s: %d: Could not find current %s locale, errno=%d\n"
,
__FILE__, __LINE__, category_name(template_category),
errno
);
}
}
if
(strEQ(restore_to_locale, template_locale)) {
Safefree(restore_to_locale);
DEBUG_Lv(PerlIO_printf(Perl_debug_log,
"%s locale unchanged as %s\n"
,
category_name(switch_category), template_locale));
return
NULL;
}
if
(! do_setlocale_r(switch_category, template_locale)) {
Perl_croak(aTHX_
"panic: %s: %d: Could not change %s locale to %s, errno=%d\n"
,
__FILE__, __LINE__, category_name(switch_category),
template_locale,
errno
);
}
DEBUG_Lv(PerlIO_printf(Perl_debug_log,
"%s locale switched to %s\n"
,
category_name(switch_category), template_locale));
return
restore_to_locale;
}
STATIC
void
S_restore_switched_locale(pTHX_
const
int
category,
const
char
*
const
original_locale)
{
if
(original_locale == NULL) {
return
;
}
if
(! do_setlocale_r(category, original_locale)) {
Perl_croak(aTHX_
"panic: %s: %d: setlocale %s restore to %s failed, errno=%d\n"
,
__FILE__, __LINE__,
category_name(category), original_locale,
errno
);
}
Safefree(original_locale);
}
#define CUR_LC_BUFFER_SIZE 64
bool
Perl__is_cur_LC_category_utf8(pTHX_
int
category)
{
const
char
*save_input_locale = NULL;
bool
is_utf8 = FALSE;
char
* utf8ness_cache = PL_locale_utf8ness + STRLENs(C_and_POSIX_utf8ness);
Size_t utf8ness_cache_size;
Size_t input_name_len;
Size_t input_name_len_with_overhead;
char
* delimited;
char
buffer[CUR_LC_BUFFER_SIZE];
char
* name_pos;
# ifdef LC_ALL
assert
(category != LC_ALL);
# endif
save_input_locale = stdize_locale(savepv(do_setlocale_r(category, NULL)));
if
(! save_input_locale) {
Perl_croak(aTHX_
"panic: %s: %d: Could not find current %s locale, errno=%d\n"
,
__FILE__, __LINE__, category_name(category),
errno
);
}
DEBUG_L(PerlIO_printf(Perl_debug_log,
"Current locale for %s is %s\n"
,
category_name(category), save_input_locale));
input_name_len =
strlen
(save_input_locale);
input_name_len_with_overhead = input_name_len + 3;
if
( input_name_len_with_overhead <= CUR_LC_BUFFER_SIZE ) {
delimited = buffer;
}
else
{
Newx(delimited, input_name_len_with_overhead,
char
);
}
delimited[0] = UTF8NESS_SEP[0];
Copy(save_input_locale, delimited + 1, input_name_len,
char
);
delimited[input_name_len+1] = UTF8NESS_PREFIX[0];
delimited[input_name_len+2] =
'\0'
;
name_pos = instr(PL_locale_utf8ness, delimited);
if
(name_pos) {
is_utf8 = *(name_pos + input_name_len_with_overhead - 1) -
'0'
;
# ifdef DEBUGGING
if
(DEBUG_Lv_TEST || debug_initialization) {
PerlIO_printf(Perl_debug_log,
"UTF8ness for locale %s=%d, \n"
,
save_input_locale, is_utf8);
}
# endif
if
(name_pos > utf8ness_cache) {
Move(utf8ness_cache,
utf8ness_cache + input_name_len_with_overhead,
name_pos - utf8ness_cache,
char
);
Copy(delimited,
utf8ness_cache,
input_name_len_with_overhead - 1,
char
);
utf8ness_cache[input_name_len_with_overhead - 1] = is_utf8 +
'0'
;
}
if
( delimited != buffer ) Safefree(delimited);
Safefree(save_input_locale);
return
is_utf8;
}
# if defined(USE_LOCALE_CTYPE) \
&& ( defined(HAS_NL_LANGINFO) \
|| (defined(HAS_MBTOWC) || defined(HAS_MBRTOWC)))
{
const
char
*original_ctype_locale
= switch_category_locale_to_template(LC_CTYPE,
category,
save_input_locale);
# ifdef MB_CUR_MAX /* But we can potentially rule out UTF-8ness, avoiding
calling the functions
if
we have
this
*/
DEBUG_L(PerlIO_printf(Perl_debug_log,
"%s: %d: MB_CUR_MAX=%d\n"
,
__FILE__, __LINE__, (
int
) MB_CUR_MAX));
if
((unsigned) MB_CUR_MAX < STRLENs(MAX_UNICODE_UTF8)) {
is_utf8 = FALSE;
restore_switched_locale(LC_CTYPE, original_ctype_locale);
goto
finish_and_return;
}
# endif
# if defined(HAS_NL_LANGINFO)
{
const
char
*codeset = my_nl_langinfo(CODESET, FALSE);
DEBUG_Lv(PerlIO_printf(Perl_debug_log,
"\tnllanginfo returned CODESET '%s'\n"
, codeset));
if
(codeset && strNE(codeset,
""
)) {
is_utf8 = cBOOL( foldEQ(codeset, STR_WITH_LEN(
"UTF-8"
))
|| foldEQ(codeset, STR_WITH_LEN(
"UTF8"
)));
DEBUG_L(PerlIO_printf(Perl_debug_log,
"\tnllanginfo returned CODESET '%s'; ?UTF8 locale=%d\n"
,
codeset, is_utf8));
restore_switched_locale(LC_CTYPE, original_ctype_locale);
goto
finish_and_return;
}
}
# endif
# if defined(HAS_MBTOWC) || defined(HAS_MBRTOWC)
{
wchar_t
wc;
int
len;
dSAVEDERRNO;
# if defined(HAS_MBRTOWC) && defined(USE_ITHREADS)
mbstate_t
ps;
# endif
# if defined(HAS_MBRTOWC) && defined(USE_ITHREADS)
memzero(&ps,
sizeof
(ps));;
PERL_UNUSED_RESULT(mbrtowc(&wc, NULL, 0, &ps));
SETERRNO(0, 0);
len = mbrtowc(&wc, STR_WITH_LEN(REPLACEMENT_CHARACTER_UTF8), &ps);
SAVE_ERRNO;
# else
MBTOWC_LOCK;
PERL_UNUSED_RESULT(
mbtowc
(&wc, NULL, 0));
SETERRNO(0, 0);
len =
mbtowc
(&wc, STR_WITH_LEN(REPLACEMENT_CHARACTER_UTF8));
SAVE_ERRNO;
MBTOWC_UNLOCK;
# endif
RESTORE_ERRNO;
DEBUG_Lv(PerlIO_printf(Perl_debug_log,
"\treturn from mbtowc; len=%d; code_point=%x; errno=%d\n"
,
len, (unsigned
int
) wc, GET_ERRNO));
is_utf8 = cBOOL( len == STRLENs(REPLACEMENT_CHARACTER_UTF8)
&& wc == (
wchar_t
) UNICODE_REPLACEMENT);
}
# endif
restore_switched_locale(LC_CTYPE, original_ctype_locale);
goto
finish_and_return;
}
# else
# ifdef USE_LOCALE_MONETARY
{
const
char
*original_monetary_locale
= switch_category_locale_to_template(LC_MONETARY,
category,
save_input_locale);
bool
only_ascii = FALSE;
const
U8 * currency_string
= (
const
U8 *) my_nl_langinfo(CRNCYSTR, FALSE);
const
U8 * first_variant;
assert
( *currency_string ==
'-'
|| *currency_string ==
'+'
|| *currency_string ==
'.'
);
currency_string++;
if
(is_utf8_invariant_string_loc(currency_string, 0, &first_variant))
{
DEBUG_L(PerlIO_printf(Perl_debug_log,
"Couldn't get currency symbol for %s, or contains only ASCII; can't use for determining if UTF-8 locale\n"
, save_input_locale));
only_ascii = TRUE;
}
else
{
is_utf8 = is_strict_utf8_string(first_variant, 0);
}
restore_switched_locale(LC_MONETARY, original_monetary_locale);
if
(! only_ascii) {
DEBUG_Lv(PerlIO_printf(Perl_debug_log,
"\t?Currency symbol for %s is UTF-8=%d\n"
,
save_input_locale, is_utf8));
goto
finish_and_return;
}
}
# endif /* USE_LOCALE_MONETARY */
# if defined(HAS_STRFTIME) && defined(USE_LOCALE_TIME)
{
const
char
*original_time_locale
= switch_category_locale_to_template(LC_TIME,
category,
save_input_locale);
int
hour = 10;
bool
is_dst = FALSE;
int
dom = 1;
int
month = 0;
int
i;
char
* formatted_time;
for
(i = 0; i < 7 + 12; i++) {
formatted_time = my_strftime(
"%A %B %Z %p"
,
0, 0, hour, dom, month, 2012 - 1900, 0, 0, is_dst);
if
( ! formatted_time
|| is_utf8_invariant_string((U8 *) formatted_time, 0))
{
is_dst = ! is_dst;
hour = (hour + 12) % 24;
dom++;
if
(i > 6) {
month++;
}
continue
;
}
restore_switched_locale(LC_TIME, original_time_locale);
DEBUG_Lv(PerlIO_printf(Perl_debug_log,
"\t?time-related strings for %s are UTF-8=%d\n"
,
save_input_locale,
is_utf8_string((U8 *) formatted_time, 0)));
is_utf8 = is_utf8_string((U8 *) formatted_time, 0);
goto
finish_and_return;
}
restore_switched_locale(LC_TIME, original_time_locale);
DEBUG_Lv(PerlIO_printf(Perl_debug_log,
"All time-related words for %s contain only ASCII; can't use for determining if UTF-8 locale\n"
, save_input_locale));
}
# endif
# if 0 && defined(USE_LOCALE_MESSAGES) && defined(HAS_SYS_ERRLIST)
{
int
e;
bool
non_ascii = FALSE;
const
char
*original_messages_locale
= switch_category_locale_to_template(LC_MESSAGES,
category,
save_input_locale);
const
char
* errmsg = NULL;
for
(e = 0; e <= sys_nerr; e++) {
errno
= 0;
errmsg = sys_errlist[e];
if
(
errno
|| !errmsg) {
break
;
}
errmsg = savepv(errmsg);
if
(! is_utf8_invariant_string((U8 *) errmsg, 0)) {
non_ascii = TRUE;
is_utf8 = is_utf8_string((U8 *) errmsg, 0);
break
;
}
}
Safefree(errmsg);
restore_switched_locale(LC_MESSAGES, original_messages_locale);
if
(non_ascii) {
DEBUG_Lv(PerlIO_printf(Perl_debug_log,
"\t?error messages for %s are UTF-8=%d\n"
,
save_input_locale,
is_utf8));
goto
finish_and_return;
}
DEBUG_L(PerlIO_printf(Perl_debug_log,
"All error messages for %s contain only ASCII; can't use for determining if UTF-8 locale\n"
, save_input_locale));
}
# endif
# ifndef EBCDIC /* On os390, even if the name ends with "UTF-8', it isn't a
UTF-8 locale */
{
const
Size_t final_pos =
strlen
(save_input_locale) - 1;
if
(final_pos >= 3) {
const
char
*name = save_input_locale;
while
((name +=
strcspn
(name,
"Uu"
) + 1)
<= save_input_locale + final_pos - 2)
{
if
( isALPHA_FOLD_NE(*name,
't'
)
|| isALPHA_FOLD_NE(*(name + 1),
'f'
))
{
continue
;
}
name += 2;
if
(*(name) ==
'-'
) {
if
((name > save_input_locale + final_pos - 1)) {
break
;
}
name++;
}
if
(*(name) ==
'8'
) {
DEBUG_L(PerlIO_printf(Perl_debug_log,
"Locale %s ends with UTF-8 in name\n"
,
save_input_locale));
is_utf8 = TRUE;
goto
finish_and_return;
}
}
DEBUG_L(PerlIO_printf(Perl_debug_log,
"Locale %s doesn't end with UTF-8 in name\n"
,
save_input_locale));
}
# ifdef WIN32
if
(memENDs(save_input_locale, final_pos,
"65001"
)) {
DEBUG_L(PerlIO_printf(Perl_debug_log,
"Locale %s ends with 65001 in name, is UTF-8 locale\n"
,
save_input_locale));
is_utf8 = TRUE;
goto
finish_and_return;
}
# endif
}
# endif
# if 0
if
(instr(save_input_locale,
"8859"
)) {
DEBUG_L(PerlIO_printf(Perl_debug_log,
"Locale %s has 8859 in name, not UTF-8 locale\n"
,
save_input_locale));
is_utf8 = FALSE;
goto
finish_and_return;
}
# endif
DEBUG_L(PerlIO_printf(Perl_debug_log,
"Assuming locale %s is not a UTF-8 locale\n"
,
save_input_locale));
is_utf8 = FALSE;
# endif /* the code that is compiled when no modern LC_CTYPE */
finish_and_return:
utf8ness_cache_size =
sizeof
(PL_locale_utf8ness)
- (utf8ness_cache - PL_locale_utf8ness);
if
(LIKELY(input_name_len_with_overhead < utf8ness_cache_size)) {
Size_t utf8ness_cache_len =
strlen
(utf8ness_cache);
if
(utf8ness_cache_len + input_name_len_with_overhead
>= utf8ness_cache_size)
{
char
* cutoff = (
char
*) my_memrchr(utf8ness_cache,
UTF8NESS_SEP[0],
utf8ness_cache_size
- input_name_len_with_overhead);
assert
(cutoff);
assert
(cutoff >= utf8ness_cache);
*cutoff =
'\0'
;
utf8ness_cache_len =
strlen
(utf8ness_cache);
}
Move(utf8ness_cache,
utf8ness_cache + input_name_len_with_overhead,
utf8ness_cache_len + 1
,
char
);
Copy(delimited, utf8ness_cache, input_name_len_with_overhead - 1,
char
);
utf8ness_cache[input_name_len_with_overhead - 1] = is_utf8 +
'0'
;
if
((PL_locale_utf8ness[
strlen
(PL_locale_utf8ness)-1] & ~1) !=
'0'
) {
Perl_croak(aTHX_
"panic: %s: %d: Corrupt utf8ness_cache=%s\nlen=%zu,"
" inserted_name=%s, its_len=%zu\n"
,
__FILE__, __LINE__,
PL_locale_utf8ness,
strlen
(PL_locale_utf8ness),
delimited, input_name_len_with_overhead);
}
}
# ifdef DEBUGGING
if
(DEBUG_Lv_TEST) {
const
char
* s = PL_locale_utf8ness;
while
(s < PL_locale_utf8ness +
strlen
(PL_locale_utf8ness)) {
const
char
*e;
if
(*s != UTF8NESS_SEP[0]) {
Perl_croak(aTHX_
"panic: %s: %d: Corrupt utf8ness_cache: missing"
" separator %.*s<-- HERE %s\n"
,
__FILE__, __LINE__,
(
int
) (s - PL_locale_utf8ness), PL_locale_utf8ness,
s);
}
s++;
e =
strchr
(s, UTF8NESS_PREFIX[0]);
if
(! e) {
e = PL_locale_utf8ness +
strlen
(PL_locale_utf8ness);
Perl_croak(aTHX_
"panic: %s: %d: Corrupt utf8ness_cache: missing"
" separator %.*s<-- HERE %s\n"
,
__FILE__, __LINE__,
(
int
) (e - PL_locale_utf8ness), PL_locale_utf8ness,
e);
}
e++;
if
(*e !=
'0'
&& *e !=
'1'
) {
Perl_croak(aTHX_
"panic: %s: %d: Corrupt utf8ness_cache: utf8ness"
" must be [01] %.*s<-- HERE %s\n"
,
__FILE__, __LINE__,
(
int
) (e + 1 - PL_locale_utf8ness),
PL_locale_utf8ness, e + 1);
}
if
(ninstr(PL_locale_utf8ness, s, s-1, e)) {
Perl_croak(aTHX_
"panic: %s: %d: Corrupt utf8ness_cache: entry"
" has duplicate %.*s<-- HERE %s\n"
,
__FILE__, __LINE__,
(
int
) (e - PL_locale_utf8ness), PL_locale_utf8ness,
e);
}
s = e + 1;
}
}
if
(DEBUG_Lv_TEST || debug_initialization) {
PerlIO_printf(Perl_debug_log,
"PL_locale_utf8ness is now %s; returning %d\n"
,
PL_locale_utf8ness, is_utf8);
}
# endif
if
( delimited != buffer ) Safefree(delimited);
Safefree(save_input_locale);
return
is_utf8;
}
#endif
bool
Perl__is_in_locale_category(pTHX_
const
bool
compiling,
const
int
category)
{
const
COP *
const
cop = (compiling) ? &PL_compiling : PL_curcop;
SV *these_categories = cop_hints_fetch_pvs(cop,
"locale"
, 0);
if
(! these_categories || these_categories == &PL_sv_placeholder) {
return
FALSE;
}
assert
(category >= -1);
return
cBOOL(SvUV(these_categories) & (1U << (category + 1)));
}
char
*
Perl_my_strerror(pTHX_
const
int
errnum)
{
char
*errstr;
#ifndef USE_LOCALE_MESSAGES
errstr = savepv(Strerror(errnum));
#else /* Has locale messages */
const
bool
within_locale_scope = IN_LC(LC_MESSAGES);
# ifndef USE_ITHREADS
if
(within_locale_scope) {
errstr = savepv(
strerror
(errnum));
}
else
{
const
char
* save_locale = savepv(do_setlocale_c(LC_MESSAGES, NULL));
do_setlocale_c(LC_MESSAGES,
"C"
);
errstr = savepv(
strerror
(errnum));
do_setlocale_c(LC_MESSAGES, save_locale);
Safefree(save_locale);
}
# elif defined(USE_POSIX_2008_LOCALE) \
&& defined(HAS_STRERROR_L)
# ifdef HAS_STRERROR_R
if
(within_locale_scope) {
errstr = savepv(
strerror
(errnum));
}
else
{
errstr = savepv(strerror_l(errnum, PL_C_locale_obj));
}
# else
bool
do_free = FALSE;
locale_t locale_to_use;
if
(within_locale_scope) {
locale_to_use = uselocale((locale_t) 0);
if
(locale_to_use == LC_GLOBAL_LOCALE) {
locale_to_use = duplocale(LC_GLOBAL_LOCALE);
do_free = TRUE;
}
}
else
{
locale_to_use = PL_C_locale_obj;
}
errstr = savepv(strerror_l(errnum, locale_to_use));
if
(do_free) {
freelocale(locale_to_use);
}
# endif
# else /* Doesn't have strerror_l() */
const
char
* save_locale = NULL;
bool
locale_is_C = FALSE;
SETLOCALE_LOCK;
DEBUG_Lv(PerlIO_printf(Perl_debug_log,
"my_strerror called with errnum %d\n"
, errnum));
if
(! within_locale_scope) {
save_locale = do_setlocale_c(LC_MESSAGES, NULL);
if
(! save_locale) {
SETLOCALE_UNLOCK;
Perl_croak(aTHX_
"panic: %s: %d: Could not find current LC_MESSAGES locale,"
" errno=%d\n"
, __FILE__, __LINE__,
errno
);
}
else
{
locale_is_C = isNAME_C_OR_POSIX(save_locale);
if
(! locale_is_C) {
save_locale = savepv(save_locale);
if
(! do_setlocale_c(LC_MESSAGES,
"C"
)) {
Safefree(save_locale);
save_locale = NULL;
}
}
}
}
else
{
DEBUG_Lv(PerlIO_printf(Perl_debug_log,
"%s: %d: WITHIN locale scope\n"
,
__FILE__, __LINE__));
}
DEBUG_Lv(PerlIO_printf(Perl_debug_log,
"Any locale change has been done; about to call Strerror\n"
));
errstr = savepv(Strerror(errnum));
if
(! within_locale_scope) {
if
(save_locale && ! locale_is_C) {
if
(! do_setlocale_c(LC_MESSAGES, save_locale)) {
SETLOCALE_UNLOCK;
Perl_croak(aTHX_
"panic: %s: %d: setlocale restore to '%s' failed, errno=%d\n"
,
__FILE__, __LINE__, save_locale,
errno
);
}
Safefree(save_locale);
}
}
SETLOCALE_UNLOCK;
# endif /* End of doesn't have strerror_l */
# ifdef DEBUGGING
if
(DEBUG_Lv_TEST) {
PerlIO_printf(Perl_debug_log,
"Strerror returned; saving a copy: '"
);
print_bytes_for_locale(errstr, errstr +
strlen
(errstr), 0);
PerlIO_printf(Perl_debug_log,
"'\n"
);
}
# endif
#endif /* End of does have locale messages */
SAVEFREEPV(errstr);
return
errstr;
}
void
Perl_switch_to_global_locale()
{
#ifdef USE_THREAD_SAFE_LOCALE
# ifdef WIN32
_configthreadlocale(_DISABLE_PER_THREAD_LOCALE);
# else
# ifdef HAS_QUERYLOCALE
setlocale
(LC_ALL, querylocale(LC_ALL_MASK, uselocale((locale_t) 0)));
# else
{
unsigned
int
i;
for
(i = 0; i < LC_ALL_INDEX; i++) {
setlocale
(categories[i], do_setlocale_r(categories[i], NULL));
}
}
# endif
uselocale(LC_GLOBAL_LOCALE);
# endif
#endif
}
bool
Perl_sync_locale()
{
#ifndef USE_LOCALE
return
TRUE;
#else
const
char
* newlocale;
dTHX;
# ifdef USE_POSIX_2008_LOCALE
bool
was_in_global_locale = FALSE;
locale_t cur_obj = uselocale((locale_t) 0);
if
(cur_obj == LC_GLOBAL_LOCALE) {
# ifdef HAS_QUERY_LOCALE
do_setlocale_c(LC_ALL,
setlocale
(LC_ALL, NULL));
# else
unsigned
int
i;
for
(i = 0; i < LC_ALL_INDEX; i++) {
do_setlocale_r(categories[i],
setlocale
(categories[i], NULL));
}
# endif
was_in_global_locale = TRUE;
}
# else
bool
was_in_global_locale = TRUE;
# endif
# ifdef USE_LOCALE_CTYPE
newlocale = savepv(do_setlocale_c(LC_CTYPE, NULL));
DEBUG_Lv(PerlIO_printf(Perl_debug_log,
"%s:%d: %s\n"
, __FILE__, __LINE__,
setlocale_debug_string(LC_CTYPE, NULL, newlocale)));
new_ctype(newlocale);
Safefree(newlocale);
# endif /* USE_LOCALE_CTYPE */
# ifdef USE_LOCALE_COLLATE
newlocale = savepv(do_setlocale_c(LC_COLLATE, NULL));
DEBUG_Lv(PerlIO_printf(Perl_debug_log,
"%s:%d: %s\n"
, __FILE__, __LINE__,
setlocale_debug_string(LC_COLLATE, NULL, newlocale)));
new_collate(newlocale);
Safefree(newlocale);
# endif
# ifdef USE_LOCALE_NUMERIC
newlocale = savepv(do_setlocale_c(LC_NUMERIC, NULL));
DEBUG_Lv(PerlIO_printf(Perl_debug_log,
"%s:%d: %s\n"
, __FILE__, __LINE__,
setlocale_debug_string(LC_NUMERIC, NULL, newlocale)));
new_numeric(newlocale);
Safefree(newlocale);
# endif /* USE_LOCALE_NUMERIC */
return
was_in_global_locale;
#endif
}
#if defined(DEBUGGING) && defined(USE_LOCALE)
STATIC
char
*
S_setlocale_debug_string(
const
int
category,
const
char
*
const
locale,
const
char
*
const
retval)
{
static
char
ret[256];
my_strlcpy(ret,
"setlocale("
,
sizeof
(ret));
my_strlcat(ret, category_name(category),
sizeof
(ret));
my_strlcat(ret,
", "
,
sizeof
(ret));
if
(locale) {
my_strlcat(ret,
"\""
,
sizeof
(ret));
my_strlcat(ret, locale,
sizeof
(ret));
my_strlcat(ret,
"\""
,
sizeof
(ret));
}
else
{
my_strlcat(ret,
"NULL"
,
sizeof
(ret));
}
my_strlcat(ret,
") returned "
,
sizeof
(ret));
if
(retval) {
my_strlcat(ret,
"\""
,
sizeof
(ret));
my_strlcat(ret, retval,
sizeof
(ret));
my_strlcat(ret,
"\""
,
sizeof
(ret));
}
else
{
my_strlcat(ret,
"NULL"
,
sizeof
(ret));
}
assert
(
strlen
(ret) <
sizeof
(ret));
return
ret;
}
#endif
void
Perl_thread_locale_init()
{
#ifdef USE_THREAD_SAFE_LOCALE
dTHX_DEBUGGING;
DEBUG_L(PerlIO_printf(Perl_debug_log,
"%s:%d: new thread, initial locale is %s; calling setlocale\n"
,
__FILE__, __LINE__,
setlocale
(LC_ALL, NULL)));
# ifdef WIN32
_configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
# else
Perl_setlocale(LC_ALL,
"C"
);
# endif
#endif
}
void
Perl_thread_locale_term()
{
#ifdef USE_THREAD_SAFE_LOCALE
# ifndef WIN32
{
locale_t cur_obj = uselocale(LC_GLOBAL_LOCALE);
if
(cur_obj != LC_GLOBAL_LOCALE && cur_obj != PL_C_locale_obj) {
freelocale(cur_obj);
}
}
# endif
#endif
}