diff options
| -rw-r--r-- | src/env/__init_tls.c | 1 | ||||
| -rw-r--r-- | src/internal/libc.h | 8 | ||||
| -rw-r--r-- | src/internal/locale_impl.h | 18 | ||||
| -rw-r--r-- | src/locale/__setlocalecat.c | 46 | ||||
| -rw-r--r-- | src/locale/duplocale.c | 15 | ||||
| -rw-r--r-- | src/locale/newlocale.c | 24 | ||||
| -rw-r--r-- | src/locale/setlocale.c | 68 | ||||
| -rw-r--r-- | src/locale/uselocale.c | 21 | ||||
| -rw-r--r-- | src/thread/pthread_create.c | 7 | 
9 files changed, 186 insertions, 22 deletions
diff --git a/src/env/__init_tls.c b/src/env/__init_tls.c index 13cf2eea..efa07284 100644 --- a/src/env/__init_tls.c +++ b/src/env/__init_tls.c @@ -16,6 +16,7 @@ int __init_tp(void *p)  	if (!r) libc.can_do_threads = 1;  	libc.has_thread_pointer = 1;  	td->tid = td->pid = __syscall(SYS_set_tid_address, &td->tid); +	td->locale = &libc.global_locale;  	return 0;  } diff --git a/src/internal/libc.h b/src/internal/libc.h index fb4d9bc0..037d16b6 100644 --- a/src/internal/libc.h +++ b/src/internal/libc.h @@ -5,6 +5,11 @@  #include <stdio.h>  #include <limits.h> +struct __locale_struct { +	int ctype_utf8; +	char *messages_name; +}; +  struct __libc {  	int has_thread_pointer;  	int can_do_threads; @@ -16,6 +21,9 @@ struct __libc {  	int ofl_lock[2];  	size_t tls_size;  	size_t page_size; +	volatile int uselocale_cnt; +	volatile int bytelocale_cnt_minus_1; +	struct __locale_struct global_locale;  };  extern size_t __hwcap; diff --git a/src/internal/locale_impl.h b/src/internal/locale_impl.h index f41c6f24..2747b85a 100644 --- a/src/internal/locale_impl.h +++ b/src/internal/locale_impl.h @@ -1,5 +1,17 @@  #include <locale.h> +#include <stdlib.h> +#include "libc.h" +#include "pthread_impl.h" -struct __locale_struct { -	int dummy; -}; +#define LOCALE_NAME_MAX 15 + +int __setlocalecat(locale_t, int, const char *); + +#define CURRENT_LOCALE \ +	(libc.uselocale_cnt ? __pthread_self()->locale : &libc.global_locale) + +#define CURRENT_UTF8 \ +	(libc.bytelocale_cnt_minus_1<0 || __pthread_self()->locale->ctype_utf8) + +#undef MB_CUR_MAX +#define MB_CUR_MAX (CURRENT_UTF8 ? 4 : 1) diff --git a/src/locale/__setlocalecat.c b/src/locale/__setlocalecat.c new file mode 100644 index 00000000..f1e4bf07 --- /dev/null +++ b/src/locale/__setlocalecat.c @@ -0,0 +1,46 @@ +#include <locale.h> +#include <string.h> +#include "locale_impl.h" +#include "libc.h" +#include "atomic.h" + +static const char envvars[][12] = { +	"LC_CTYPE", +	"LC_NUMERIC", +	"LC_TIME", +	"LC_COLLATE", +	"LC_MONETARY", +	"LC_MESSAGES", +}; + +int __setlocalecat(locale_t loc, int cat, const char *val) +{ +	if (!*val) { +		(val = getenv("LC_ALL")) || +		(val = getenv(envvars[cat])) || +		(val = getenv("LANG")) || +		(val = "C.UTF-8"); +	} + +	size_t n = strnlen(val, LOCALE_NAME_MAX); +	int builtin = (val[0]=='C' && !val[1]) +		|| !strcmp(val, "C.UTF-8") +		|| !strcmp(val, "POSIX"); + +	switch (cat) { +	case LC_CTYPE: +		a_store(&loc->ctype_utf8, !builtin || val[1]=='.'); +		break; +	case LC_MESSAGES: +		if (builtin) { +			loc->messages_name[0] = 0; +		} else { +			memcpy(loc->messages_name, val, n); +			loc->messages_name[n] = 0; +		} +		/* fall through */ +	default: +		break; +	} +	return 0; +} diff --git a/src/locale/duplocale.c b/src/locale/duplocale.c index f9fc1ffa..13368707 100644 --- a/src/locale/duplocale.c +++ b/src/locale/duplocale.c @@ -3,12 +3,19 @@  #include "locale_impl.h"  #include "libc.h" -locale_t duplocale(locale_t old) +locale_t __duplocale(locale_t old)  { -	locale_t new; -	new = calloc(1, sizeof *new); +	locale_t new = calloc(1, sizeof *new + LOCALE_NAME_MAX + 1); +	if (!new) return 0; +	new->messages_name = (void *)(new+1); + +	if (old == LC_GLOBAL_LOCALE) old = &libc.global_locale; +	new->ctype_utf8 = old->ctype_utf8; +	if (old->messages_name) +		strcpy(new->messages_name, old->messages_name); +  	if (new && old != LC_GLOBAL_LOCALE) memcpy(new, old, sizeof *new);  	return new;  } -weak_alias(duplocale, __duplocale); +weak_alias(__duplocale, duplocale); diff --git a/src/locale/newlocale.c b/src/locale/newlocale.c index 447c8fc2..39501d0c 100644 --- a/src/locale/newlocale.c +++ b/src/locale/newlocale.c @@ -3,12 +3,24 @@  #include "locale_impl.h"  #include "libc.h" -locale_t newlocale(int mask, const char *name, locale_t base) +locale_t __newlocale(int mask, const char *name, locale_t loc)  { -	if (*name && strcmp(name, "C") && strcmp(name, "POSIX")) -		return 0; -	if (!base) base = calloc(1, sizeof *base); -	return base; +	int i; + +	if (!loc) { +		loc = calloc(1, sizeof *loc + LOCALE_NAME_MAX + 1); +		if (!loc) return 0; +		loc->messages_name = (void *)(loc+1); +		for (i=0; i<LC_ALL; i++) +			if (!(mask & (1<<i))) +				__setlocalecat(loc, i, ""); +	} + +	for (i=0; i<LC_ALL; i++) +		if (mask & (1<<i)) +			__setlocalecat(loc, i, name); + +	return loc;  } -weak_alias(newlocale, __newlocale); +weak_alias(__newlocale, newlocale); diff --git a/src/locale/setlocale.c b/src/locale/setlocale.c index 28f29b80..cbc0b551 100644 --- a/src/locale/setlocale.c +++ b/src/locale/setlocale.c @@ -1,9 +1,67 @@  #include <locale.h> +#include <stdlib.h> +#include <string.h> +#include "locale_impl.h" +#include "libc.h" +#include "atomic.h" -char *setlocale(int category, const char *locale) +static char buf[2+4*(LOCALE_NAME_MAX+1)]; + +char *setlocale(int cat, const char *name)  { -	/* Note: plain "C" would be better, but puts some broken -	 * software into legacy 8-bit-codepage mode, ignoring -	 * the standard library's multibyte encoding */ -	return "C.UTF-8"; +	if (!libc.global_locale.messages_name) { +		libc.global_locale.messages_name = +			buf + 2 + 3*(LOCALE_NAME_MAX+1); +	} + +	if ((unsigned)cat > LC_ALL) return 0; + +	/* For LC_ALL, setlocale is required to return a string which +	 * encodes the current setting for all categories. The format of +	 * this string is unspecified, and only the following code, which +	 * performs both the serialization and deserialization, depends +	 * on the format, so it can easily be changed if needed. */ +	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]; +				part[1] = 0; +				setlocale(LC_CTYPE, part); +				part[LOCALE_NAME_MAX] = 0; +				for (i=LC_TIME; i<LC_MESSAGES; i++) { +					memcpy(part, name + 2 + (i-2)*(LOCALE_NAME_MAX+1), LOCALE_NAME_MAX); +					for (j=LOCALE_NAME_MAX-1; j && part[j]==';'; j--) +						part[j] = 0; +					setlocale(i, part); +				} +				setlocale(LC_MESSAGES, name + 2 + 3*(LOCALE_NAME_MAX+1)); +			} else { +				for (i=0; i<LC_ALL; i++) +					setlocale(i, name); +			} +		} +		memset(buf, ';', 2 + 3*(LOCALE_NAME_MAX+1)); +		buf[0] = libc.global_locale.ctype_utf8 ? 'U' : 'C'; +		return buf; +	} + +	if (name) { +		int adj = libc.global_locale.ctype_utf8; +		__setlocalecat(&libc.global_locale, cat, name); +		adj -= libc.global_locale.ctype_utf8; +		if (adj) a_fetch_add(&libc.bytelocale_cnt_minus_1, adj); +	} + +	switch (cat) { +	case LC_CTYPE: +		return libc.global_locale.ctype_utf8 ? "C.UTF-8" : "C"; +	case LC_MESSAGES: +		return libc.global_locale.messages_name[0] +			? libc.global_locale.messages_name : "C"; +	default: +		return "C"; +	}  } diff --git a/src/locale/uselocale.c b/src/locale/uselocale.c index 4fc5c64e..51067957 100644 --- a/src/locale/uselocale.c +++ b/src/locale/uselocale.c @@ -2,12 +2,25 @@  #include "pthread_impl.h"  #include "libc.h" -locale_t uselocale(locale_t l) +locale_t __uselocale(locale_t new)  {  	pthread_t self = __pthread_self();  	locale_t old = self->locale; -	if (l) self->locale = l; -	return old; +	locale_t global = &libc.global_locale; + +	if (new == LC_GLOBAL_LOCALE) new = global; + +	if (new && new != old) { +		int adj = 0; +		if (new == global) a_dec(&libc.uselocale_cnt); +		else if (!new->ctype_utf8) adj++; +		if (old == global) a_inc(&libc.uselocale_cnt); +		else if (!old->ctype_utf8) adj--; +		a_fetch_add(&libc.bytelocale_cnt_minus_1, adj); +		self->locale = new; +	} + +	return old == global ? LC_GLOBAL_LOCALE : old;  } -weak_alias(uselocale, __uselocale); +weak_alias(__uselocale, uselocale); diff --git a/src/thread/pthread_create.c b/src/thread/pthread_create.c index e9c8160a..a7493c10 100644 --- a/src/thread/pthread_create.c +++ b/src/thread/pthread_create.c @@ -57,6 +57,12 @@ _Noreturn void pthread_exit(void *result)  		exit(0);  	} +	if (self->locale != &libc.global_locale) { +		a_dec(&libc.uselocale_cnt); +		if (self->locale->ctype_utf8) +			a_dec(&libc.bytelocale_cnt_minus_1); +	} +  	if (self->detached && self->map_base) {  		/* Detached threads must avoid the kernel clear_child_tid  		 * feature, since the virtual address will have been @@ -205,6 +211,7 @@ int pthread_create(pthread_t *restrict res, const pthread_attr_t *restrict attrp  	new->start_arg = arg;  	new->self = new;  	new->tsd = (void *)tsd; +	new->locale = &libc.global_locale;  	if (attr._a_detach) {  		new->detached = 1;  		flags -= CLONE_CHILD_CLEARTID;  | 
