blob: 9f6e094732216c54bdb3e002397f89a465edc2dc [file] [log] [blame]
#include "locale_impl.h"
#include <locale.h>
#include <string.h>
#include <threads.h>
const char* __lctrans(const char* msg, const struct __locale_map* lm) {
const char* trans = 0;
if (lm)
trans = __mo_lookup(lm->map, lm->map_size, msg);
return trans ? trans : msg;
}
const unsigned char* __map_file(const char*, size_t*);
int __munmap(void*, size_t);
char* __strchrnul(const char*, int);
static const char envvars[][12] = {
"LC_CTYPE", "LC_NUMERIC", "LC_TIME", "LC_COLLATE", "LC_MONETARY", "LC_MESSAGES",
};
const struct __locale_map* __get_locale(int cat, const char* val) {
static mtx_t lock;
static void* volatile loc_head;
const struct __locale_map* p;
struct __locale_map* new = 0;
const char* z;
char buf[256];
size_t l, n;
if (!*val) {
((val = getenv("LC_ALL")) && *val) || ((val = getenv(envvars[cat])) && *val) ||
((val = getenv("LANG")) && *val) || (val = "C.UTF-8");
}
/* Limit name length and forbid leading dot or any slashes. */
for (n = 0; n < LOCALE_NAME_MAX && val[n] && val[n] != '/'; n++)
;
if (val[0] == '.' || val[n])
val = "C.UTF-8";
int builtin = (val[0] == 'C' && !val[1]) || !strcmp(val, "C.UTF-8") || !strcmp(val, "POSIX");
if (builtin) {
if (cat == LC_CTYPE && val[1] == '.')
return (void*)&__c_dot_utf8;
return 0;
}
for (p = loc_head; p; p = p->next)
if (!strcmp(val, p->name))
return p;
mtx_lock(&lock);
for (p = loc_head; p; p = p->next)
if (!strcmp(val, p->name)) {
mtx_unlock(&lock);
return p;
}
const char* path = getenv("MUSL_LOCPATH");
/* FIXME: add a default path? */
if (path)
for (; *path; path = z + !!*z) {
z = __strchrnul(path, ':');
l = z - path - !!*z;
if (l >= sizeof buf - n - 2)
continue;
memcpy(buf, path, l);
buf[l] = '/';
memcpy(buf + l + 1, val, n);
buf[l + 1 + n] = 0;
size_t map_size;
const void* map = __map_file(buf, &map_size);
if (map) {
new = malloc(sizeof *new);
if (!new) {
__munmap((void*)map, map_size);
break;
}
new->map = map;
new->map_size = map_size;
memcpy(new->name, val, n);
new->name[n] = 0;
new->next = loc_head;
loc_head = new;
break;
}
}
/* If no locale definition was found, make a locale map
* object anyway to store the name, which is kept for the
* sake of being able to do message translations at the
* application level. */
if (!new && (new = malloc(sizeof *new))) {
new->map = __c_dot_utf8.map;
new->map_size = __c_dot_utf8.map_size;
memcpy(new->name, val, n);
new->name[n] = 0;
new->next = loc_head;
loc_head = new;
}
/* For LC_CTYPE, never return a null pointer unless the
* requested name was "C" or "POSIX". */
if (!new&& cat == LC_CTYPE)
new = (void*)&__c_dot_utf8;
mtx_unlock(&lock);
return new;
}