implement locale file loading and state for remaining locale categories

there is still no code which actually uses the loaded locale files, so
the main observable effect of this commit is that calls to setlocale
store and give back the names of the selected locales for the
remaining categories (LC_TIME, LC_COLLATE, LC_MONETARY) if a locale
file by the requested name could be loaded.
This commit is contained in:
Rich Felker
2014-07-24 03:23:11 -04:00
parent 674e28af2d
commit 6cb4f91db7
4 changed files with 80 additions and 2 deletions

View File

@ -5,9 +5,12 @@
#include <stdio.h> #include <stdio.h>
#include <limits.h> #include <limits.h>
struct __locale_map;
struct __locale_struct { struct __locale_struct {
int ctype_utf8; int ctype_utf8;
char *messages_name; char *messages_name;
struct __locale_map *cat[4];
}; };
struct __libc { struct __libc {

View File

@ -5,6 +5,13 @@
#define LOCALE_NAME_MAX 15 #define LOCALE_NAME_MAX 15
struct __locale_map {
const void *map;
size_t map_size;
char name[LOCALE_NAME_MAX+1];
struct __locale_map *next;
};
int __setlocalecat(locale_t, int, const char *); int __setlocalecat(locale_t, int, const char *);
#define CURRENT_LOCALE \ #define CURRENT_LOCALE \

View File

@ -4,6 +4,58 @@
#include "libc.h" #include "libc.h"
#include "atomic.h" #include "atomic.h"
const unsigned char *__map_file(const char *, size_t *);
int __munmap(void *, size_t);
char *__strchrnul(const char *, int);
static struct __locale_map *findlocale(const char *name, size_t n)
{
static void *loc_head;
struct __locale_map *p, *new, *old_head;
const char *path = 0, *z;
char buf[256];
size_t l;
const void *map;
size_t map_size;
for (p=loc_head; p; p=p->next)
if (!strcmp(name, p->name)) return p;
if (strchr(name, '/')) return 0;
if (!libc.secure) path = getenv("MUSL_LOCPATH");
/* FIXME: add a default path? */
if (!path) return 0;
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, name, n);
buf[l+1+n] = 0;
map = __map_file(buf, &map_size);
if (map) {
new = malloc(sizeof *new);
if (!new) {
__munmap((void *)map, map_size);
return 0;
}
new->map = map;
new->map_size = map_size;
memcpy(new->name, name, n);
new->name[n] = 0;
do {
old_head = loc_head;
new->next = old_head;
} while (a_cas_p(&loc_head, old_head, new) != old_head);
return new;
}
}
return 0;
}
static const char envvars[][12] = { static const char envvars[][12] = {
"LC_CTYPE", "LC_CTYPE",
"LC_NUMERIC", "LC_NUMERIC",
@ -26,6 +78,7 @@ int __setlocalecat(locale_t loc, int cat, const char *val)
int builtin = (val[0]=='C' && !val[1]) int builtin = (val[0]=='C' && !val[1])
|| !strcmp(val, "C.UTF-8") || !strcmp(val, "C.UTF-8")
|| !strcmp(val, "POSIX"); || !strcmp(val, "POSIX");
struct __locale_map *data, *old;
switch (cat) { switch (cat) {
case LC_CTYPE: case LC_CTYPE:
@ -40,6 +93,11 @@ int __setlocalecat(locale_t loc, int cat, const char *val)
} }
/* fall through */ /* fall through */
default: default:
data = builtin ? 0 : findlocale(val, n);
if (data == loc->cat[cat-2]) break;
do old = loc->cat[cat-2];
while (a_cas_p(&loc->cat[cat-2], old, data) != old);
case LC_NUMERIC:
break; break;
} }
return 0; return 0;

View File

@ -9,6 +9,9 @@ static char buf[2+4*(LOCALE_NAME_MAX+1)];
char *setlocale(int cat, const char *name) char *setlocale(int cat, const char *name)
{ {
struct __locale_map *lm;
int i, j;
if (!libc.global_locale.messages_name) { if (!libc.global_locale.messages_name) {
libc.global_locale.messages_name = libc.global_locale.messages_name =
buf + 2 + 3*(LOCALE_NAME_MAX+1); buf + 2 + 3*(LOCALE_NAME_MAX+1);
@ -24,7 +27,6 @@ char *setlocale(int cat, const char *name)
if (cat == LC_ALL) { if (cat == LC_ALL) {
if (name) { if (name) {
char part[LOCALE_NAME_MAX+1]; char part[LOCALE_NAME_MAX+1];
int i, j;
if (name[0] && name[1]==';' if (name[0] && name[1]==';'
&& strlen(name) > 2 + 3*(LOCALE_NAME_MAX+1)) { && strlen(name) > 2 + 3*(LOCALE_NAME_MAX+1)) {
part[0] = name[0]; part[0] = name[0];
@ -45,6 +47,11 @@ char *setlocale(int cat, const char *name)
} }
memset(buf, ';', 2 + 3*(LOCALE_NAME_MAX+1)); memset(buf, ';', 2 + 3*(LOCALE_NAME_MAX+1));
buf[0] = libc.global_locale.ctype_utf8 ? 'U' : 'C'; buf[0] = libc.global_locale.ctype_utf8 ? 'U' : 'C';
for (i=LC_TIME; i<LC_MESSAGES; i++) {
lm = libc.global_locale.cat[i-2];
if (lm) memcpy(buf + 2 + (i-2)*(LOCALE_NAME_MAX+1),
lm->name, strlen(lm->name));
}
return buf; return buf;
} }
@ -58,10 +65,13 @@ char *setlocale(int cat, const char *name)
switch (cat) { switch (cat) {
case LC_CTYPE: case LC_CTYPE:
return libc.global_locale.ctype_utf8 ? "C.UTF-8" : "C"; return libc.global_locale.ctype_utf8 ? "C.UTF-8" : "C";
case LC_NUMERIC:
return "C";
case LC_MESSAGES: case LC_MESSAGES:
return libc.global_locale.messages_name[0] return libc.global_locale.messages_name[0]
? libc.global_locale.messages_name : "C"; ? libc.global_locale.messages_name : "C";
default: default:
return "C"; lm = libc.global_locale.cat[cat-2];
return lm ? lm->name : "C";
} }
} }