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";  	}  } | 
