blob: c742bae43cf17fcf065c9279d8d2be820597acd5 [file] [log] [blame] [edit]
// Internal functions to obtain the current locales.
#include "third_party/glibc_locales/library/current_locale.h"
#include <locale.h>
#include <stdlib.h>
#include <string.h>
#include "third_party/glibc_locales/common/langinfo.h"
#include "third_party/glibc_locales/common/localeinfo.h"
#include "third_party/glibc_locales/glibc_locales.h"
#include "third_party/glibc_locales/locale_archive.h"
// We store the current locale values in thread-local static variables. This
// assumes that users never rely on setlocale() to change the locale that is
// seen by a thread that already exists at the time of the call. Doing so
// would require explicit synchronization to avoid data races (glibc is not
// quite thread-safe for a locale-aware function being called at the same time
// as setlocale()), and supporting it here would add substantial complexity.
__thread static struct google_locale_struct global_locale;
#define global_ctype_data (global_locale.category_data[GOOGLE_LC_CTYPE])
#define global_collate_data (global_locale.category_data[GOOGLE_LC_COLLATE])
// Pointer to the locale currently in use. This will be the global_locale
// object unless google_uselocale() has been called to set a thread-local
// locale, in which case this will point to that (user-owned) locale.
__thread static google_locale_t current_locale = NULL;
// If we have a locale passed in through uselocale(). we stash it here.
void google_set_current_locales(google_locale_t locale) {
// If we've been given a locale, use it.
if (locale != NULL) {
current_locale = locale;
return;
}
// Return if we've been called from setlocale() and this is a no-op
// because we have a thread-local locale overriding the setlocale state.
if (uselocale(NULL) != LC_GLOBAL_LOCALE) {
return;
}
// If we got here, we need to set our locales to match glibc's global locale.
// Get the current names from glibc.
char *ctype_name = setlocale(LC_CTYPE, NULL);
char *collate_name = setlocale(LC_COLLATE, NULL);
// Adjust the current pointer to use the global locale.
current_locale = &global_locale;
// Replace the locale components in global_locale, if needed.
const char *archive_data = 0;
struct locrecent *locale_data = 0;
if (global_ctype_data == NULL ||
strcmp(global_ctype_data->name, ctype_name)) {
archive_data = locale_archive_create()->data;
locale_data = google_find_locale_data(archive_data, ctype_name);
if (locale_data == NULL) return;
const void *addr =
archive_data + locale_data->record[GOOGLE_LC_CTYPE].offset;
size_t len = locale_data->record[GOOGLE_LC_CTYPE].len;
if (global_ctype_data) free(global_ctype_data);
global_ctype_data =
google_load_locale_data(ctype_name, GOOGLE_LC_CTYPE, addr, len);
/* Update the special members. */
if (global_locale.category_data[GOOGLE_LC_CTYPE] != NULL) {
union locale_data_value *ctypes =
global_locale.category_data[GOOGLE_LC_CTYPE]->values;
global_locale.ctype_b =
(const unsigned short int *)
ctypes[GOOGLE_NL_ITEM_INDEX(GOOGLE_NL_CTYPE_CLASS)]
.string +
128;
global_locale.ctype_tolower =
(const int *)ctypes[GOOGLE_NL_ITEM_INDEX(GOOGLE_NL_CTYPE_TOLOWER)]
.string +
128;
global_locale.ctype_toupper =
(const int *)ctypes[GOOGLE_NL_ITEM_INDEX(GOOGLE_NL_CTYPE_TOUPPER)]
.string +
128;
} else {
global_locale.ctype_b = NULL;
global_locale.ctype_tolower = NULL;
global_locale.ctype_toupper = NULL;
}
}
if (global_collate_data == NULL ||
strcmp(global_collate_data->name, collate_name)) {
// If we've already found the relevant data for the CTYPE component,
// re-use that rather than searching for it again.
if (!archive_data) archive_data = locale_archive_create()->data;
if (!locale_data || strcmp(ctype_name, collate_name))
locale_data = google_find_locale_data(archive_data, collate_name);
if (locale_data == NULL) return;
const void *addr =
archive_data + locale_data->record[GOOGLE_LC_COLLATE].offset;
size_t len = locale_data->record[GOOGLE_LC_COLLATE].len;
if (global_collate_data) free(global_collate_data);
global_collate_data =
google_load_locale_data(collate_name, GOOGLE_LC_COLLATE, addr, len);
}
}
google_locale_t google_get_current_locale() {
if (global_ctype_data == NULL || global_collate_data == NULL)
google_set_current_locales(NULL);
return current_locale;
}