diff options
-rw-r--r-- | src/internal/libc.h | 3 | ||||
-rw-r--r-- | src/internal/locale_impl.h | 7 | ||||
-rw-r--r-- | src/locale/__setlocalecat.c | 58 | ||||
-rw-r--r-- | src/locale/setlocale.c | 14 |
4 files changed, 80 insertions, 2 deletions
diff --git a/src/internal/libc.h b/src/internal/libc.h index 037d16b6..2eef98e4 100644 --- a/src/internal/libc.h +++ b/src/internal/libc.h @@ -5,9 +5,12 @@ #include <stdio.h> #include <limits.h> +struct __locale_map; + struct __locale_struct { int ctype_utf8; char *messages_name; + struct __locale_map *cat[4]; }; struct __libc { diff --git a/src/internal/locale_impl.h b/src/internal/locale_impl.h index 2747b85a..0ee72d3e 100644 --- a/src/internal/locale_impl.h +++ b/src/internal/locale_impl.h @@ -5,6 +5,13 @@ #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 *); #define CURRENT_LOCALE \ diff --git a/src/locale/__setlocalecat.c b/src/locale/__setlocalecat.c index a947dbff..bbecde41 100644 --- a/src/locale/__setlocalecat.c +++ b/src/locale/__setlocalecat.c @@ -4,6 +4,58 @@ #include "libc.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] = { "LC_CTYPE", "LC_NUMERIC", @@ -26,6 +78,7 @@ int __setlocalecat(locale_t loc, int cat, const char *val) int builtin = (val[0]=='C' && !val[1]) || !strcmp(val, "C.UTF-8") || !strcmp(val, "POSIX"); + struct __locale_map *data, *old; switch (cat) { case LC_CTYPE: @@ -40,6 +93,11 @@ int __setlocalecat(locale_t loc, int cat, const char *val) } /* fall through */ 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; } return 0; diff --git a/src/locale/setlocale.c b/src/locale/setlocale.c index cbc0b551..8ea389a3 100644 --- a/src/locale/setlocale.c +++ b/src/locale/setlocale.c @@ -9,6 +9,9 @@ static char buf[2+4*(LOCALE_NAME_MAX+1)]; char *setlocale(int cat, const char *name) { + struct __locale_map *lm; + int i, j; + if (!libc.global_locale.messages_name) { libc.global_locale.messages_name = buf + 2 + 3*(LOCALE_NAME_MAX+1); @@ -24,7 +27,6 @@ char *setlocale(int cat, const char *name) if (cat == LC_ALL) { if (name) { char part[LOCALE_NAME_MAX+1]; - int i, j; if (name[0] && name[1]==';' && strlen(name) > 2 + 3*(LOCALE_NAME_MAX+1)) { part[0] = name[0]; @@ -45,6 +47,11 @@ char *setlocale(int cat, const char *name) } memset(buf, ';', 2 + 3*(LOCALE_NAME_MAX+1)); 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; } @@ -58,10 +65,13 @@ char *setlocale(int cat, const char *name) switch (cat) { case LC_CTYPE: return libc.global_locale.ctype_utf8 ? "C.UTF-8" : "C"; + case LC_NUMERIC: + return "C"; case LC_MESSAGES: return libc.global_locale.messages_name[0] ? libc.global_locale.messages_name : "C"; default: - return "C"; + lm = libc.global_locale.cat[cat-2]; + return lm ? lm->name : "C"; } } |