| 1 | #include <locale.h> |
| 2 | #include <string.h> |
| 3 | #include <sys/mman.h> |
| 4 | #include <stdlib.h> |
| 5 | #include "locale_impl.h" |
| 6 | #include "libc.h" |
| 7 | #include "lock.h" |
| 8 | #include "fork_impl.h" |
| 9 | |
| 10 | const char *__lctrans_impl(const char *msg, const struct __locale_map *lm) |
| 11 | { |
| 12 | 	const char *trans = 0; |
| 13 | 	if (lm) trans = __mo_lookup(lm->map, lm->map_size, msg); |
| 14 | 	return trans ? trans : msg; |
| 15 | } |
| 16 | |
| 17 | static const char envvars[][12] = { |
| 18 | 	"LC_CTYPE", |
| 19 | 	"LC_NUMERIC", |
| 20 | 	"LC_TIME", |
| 21 | 	"LC_COLLATE", |
| 22 | 	"LC_MONETARY", |
| 23 | 	"LC_MESSAGES", |
| 24 | }; |
| 25 | |
| 26 | volatile int __locale_lock[1]; |
| 27 | volatile int *const __locale_lockptr = __locale_lock; |
| 28 | |
| 29 | const struct __locale_map *__get_locale(int cat, const char *val) |
| 30 | { |
| 31 | 	static void *volatile loc_head; |
| 32 | 	const struct __locale_map *p; |
| 33 | 	struct __locale_map *new = 0; |
| 34 | 	const char *path = 0, *z; |
| 35 | 	char buf[256]; |
| 36 | 	size_t l, n; |
| 37 | |
| 38 | 	if (!*val) { |
| 39 | 		(val = getenv("LC_ALL")) && *val || |
| 40 | 		(val = getenv(envvars[cat])) && *val || |
| 41 | 		(val = getenv("LANG")) && *val || |
| 42 | 		(val = "C.UTF-8"); |
| 43 | 	} |
| 44 | |
| 45 | 	/* Limit name length and forbid leading dot or any slashes. */ |
| 46 | 	for (n=0; n<LOCALE_NAME_MAX && val[n] && val[n]!='/'; n++); |
| 47 | 	if (val[0]=='.' || val[n]) val = "C.UTF-8"; |
| 48 | 	int builtin = (val[0]=='C' && !val[1]) |
| 49 | 		|| !strcmp(val, "C.UTF-8") |
| 50 | 		|| !strcmp(val, "POSIX"); |
| 51 | |
| 52 | 	if (builtin) { |
| 53 | 		if (cat == LC_CTYPE && val[1]=='.') |
| 54 | 			return (void *)&__c_dot_utf8; |
| 55 | 		return 0; |
| 56 | 	} |
| 57 | |
| 58 | 	for (p=loc_head; p; p=p->next) |
| 59 | 		if (!strcmp(val, p->name)) return p; |
| 60 | |
| 61 | 	if (!libc.secure) path = getenv("MUSL_LOCPATH"); |
| 62 | 	/* FIXME: add a default path? */ |
| 63 | |
| 64 | 	if (path) for (; *path; path=z+!!*z) { |
| 65 | 		z = __strchrnul(path, ':'); |
| 66 | 		l = z - path; |
| 67 | 		if (l >= sizeof buf - n - 2) continue; |
| 68 | 		memcpy(buf, path, l); |
| 69 | 		buf[l] = '/'; |
| 70 | 		memcpy(buf+l+1, val, n); |
| 71 | 		buf[l+1+n] = 0; |
| 72 | 		size_t map_size; |
| 73 | 		const void *map = __map_file(buf, &map_size); |
| 74 | 		if (map) { |
| 75 | 			new = malloc(sizeof *new); |
| 76 | 			if (!new) { |
| 77 | 				__munmap((void *)map, map_size); |
| 78 | 				break; |
| 79 | 			} |
| 80 | 			new->map = map; |
| 81 | 			new->map_size = map_size; |
| 82 | 			memcpy(new->name, val, n); |
| 83 | 			new->name[n] = 0; |
| 84 | 			new->next = loc_head; |
| 85 | 			loc_head = new; |
| 86 | 			break; |
| 87 | 		} |
| 88 | 	} |
| 89 | |
| 90 | 	/* If no locale definition was found, make a locale map |
| 91 | 	 * object anyway to store the name, which is kept for the |
| 92 | 	 * sake of being able to do message translations at the |
| 93 | 	 * application level. */ |
| 94 | 	if (!new && (new = malloc(sizeof *new))) { |
| 95 | 		new->map = __c_dot_utf8.map; |
| 96 | 		new->map_size = __c_dot_utf8.map_size; |
| 97 | 		memcpy(new->name, val, n); |
| 98 | 		new->name[n] = 0; |
| 99 | 		new->next = loc_head; |
| 100 | 		loc_head = new; |
| 101 | 	} |
| 102 | |
| 103 | 	/* For LC_CTYPE, never return a null pointer unless the |
| 104 | 	 * requested name was "C" or "POSIX". */ |
| 105 | 	if (!new && cat == LC_CTYPE) new = (void *)&__c_dot_utf8; |
| 106 | |
| 107 | 	return new; |
| 108 | } |