Skip to content

Commit

Permalink
S_find_locale_from_environment(): Combine logic
Browse files Browse the repository at this point in the history
Prior to this commit, the logic was repeated for non-LC_ALL versus
LC_ALL; the latter being executed in a loop.  The logic is not really
trivial and it would be better to have just one instance, so that it
can't get out-of-sync.  To do this, this commit moves everything to the
loop, but sets it up for the non-LC_ALL case to just do a single
iteration.

It also avoids going out to the environment and fetching LANG unless
needed.  The previous behavior got LANG unconditionally if the loop was
used.  It is quite likely that not every locale category would have a
corresponding environment variable set, so that fetch likely wasn't
wasted.  But in order to combine things, the checking for LANG is done
in the loop.

The bottom line is that there is extra code required to combine the two
pieces of logic, but I believe the combining of this into a single
paradigm is worth it.
  • Loading branch information
khwilliamson committed May 12, 2023
1 parent 582d2d6 commit 363fa93
Showing 1 changed file with 67 additions and 48 deletions.
115 changes: 67 additions & 48 deletions locale.c
Expand Up @@ -2489,75 +2489,94 @@ S_find_locale_from_environment(pTHX_ const unsigned int index)
*/

const char * const lc_all = PerlEnv_getenv("LC_ALL");
const char * locale_names[LC_ALL_INDEX_] = { NULL };

/* Use any "LC_ALL" environment variable, as it overrides everything else.
* */
if (lc_all && strNE(lc_all, "")) {
return lc_all;
}

/* If setting an individual category, use its corresponding value found in
* the environment, if any */
if (index != LC_ALL_INDEX_) {
const char * const new_value = PerlEnv_getenv(category_names[index]);

if (new_value && strNE(new_value, "")) {
return new_value;
}

/* If no corresponding environment variable, see if LANG exists. If
* so, use it. */
const char * LANG = PerlEnv_getenv("LANG");
if (LANG && strNE(LANG, "")) {
return LANG;
}

/* If no LANG, use "C" on POSIX 2008, the system default on Windows */
# ifndef WIN32
return "C";
# else
return wrap_wsetlocale(categories[index], ".ACP");
# endif

/* Here, no usable LC_ALL environment variable. We have to handle each
* category separately. If all categories are desired, we loop through
* them all. If only an individual category is desired, to avoid
* duplicating logic, we use the same loop, but set up the limits so it is
* only executed once, for that particular category. */
unsigned int lower, upper, offset;
if (index == LC_ALL_INDEX_) {
lower = 0;
upper = LC_ALL_INDEX_ - 1;
offset = 0;
}
else {
lower = index;
upper = index;

/* Here is LC_ALL, and no LC_ALL environment variable. LANG is used as a
* default, but overridden for individual categories that have
* corresponding environment variables. If no LANG exists, the default is
* "C" on POSIX 2008, or the system default for the category on Windows. */
const char * LANG = PerlEnv_getenv("LANG");

/* Convert "" to NULL to save conditionals in the loop below */
if (LANG != NULL && strEQ(LANG, "")) {
LANG = NULL;
/* 'offset' is used so that the result of the single loop iteration is
* stored into output[0] */
offset = lower;
}

/* Loop through all the individual categories, setting each to any
* corresponding environment variable; or to the default if none exists for
* the category */
const char * locale_names[LC_ALL_INDEX_];
for (unsigned i = 0; i < LC_ALL_INDEX_; i++) {
/* When no LC_ALL environment variable, LANG is used as a default, but
* overridden for individual categories that have corresponding environment
* variables. If no LANG exists, the default is "C" on POSIX 2008, or the
* system default for the category on Windows. */
const char * LANG = NULL;

/* For each desired category, use any corresponding environment variable;
* or the default if none such exists. */
bool is_disparate = false; /* Assume is uniform until proven otherwise */
for (unsigned i = lower; i <= upper; i++) {
const char * const env_override = PerlEnv_getenv(category_names[i]);
unsigned int j = i - offset;

if (env_override && strNE(env_override, "")) {
locale_names[i] = env_override;
}
else if (LANG) {
locale_names[i] = LANG;
locale_names[j] = env_override;
}
else {
else { /* Here, no corresponding environment variable, see if LANG
exists and is usable. Done this way to avoid fetching LANG
unless it is actually needed */
if (LANG == NULL) {
LANG = PerlEnv_getenv("LANG");

/* If not usable, set it to a non-NULL illegal value so won't
* try to use it below */
if (LANG == NULL || strEQ(LANG, "")) {
LANG = (const char *) 1;
}
}

# ifndef WIN32
locale_names[i] = "C";
# else
locale_names[i] = wrap_wsetlocale(categories[index], ".ACP");
/* If a usable LANG exists, use it. */
if (LANG != NULL && LANG != (const char *) 1) {
locale_names[j] = LANG;
}
else {

# ifdef WIN32
/* If no LANG, use the system default on Windows. */
locale_names[j] = wrap_wsetlocale(categories[i], ".ACP");
if (! locale_names[j])
# endif
{ /* If nothing was found or worked, use C */
locale_names[j] = "C";
}
}
}

if (j > 0 && ! is_disparate && strNE(locale_names[0], locale_names[j]))
{
is_disparate = true;
}

DEBUG_Lv(PerlIO_printf(Perl_debug_log,
"find_locale_from_environment i=%d, name=%s, locale=%s\n",
i, category_names[i], locale_names[i]));
"find_locale_from_environment i=%u, j=%u, name=%s,"
" locale=%s, locale of 0th category=%s, disparate=%d\n",
i, j, category_names[i],
locale_names[j], locale_names[0], is_disparate));
}

if (! is_disparate) {
return locale_names[0];
}

return calculate_LC_ALL_string(locale_names, INTERNAL_FORMAT, __LINE__);
Expand Down

0 comments on commit 363fa93

Please sign in to comment.