summaryrefslogtreecommitdiff
path: root/src/locale
diff options
context:
space:
mode:
Diffstat (limited to 'src/locale')
-rw-r--r--src/locale/__setlocalecat.c46
-rw-r--r--src/locale/duplocale.c15
-rw-r--r--src/locale/newlocale.c24
-rw-r--r--src/locale/setlocale.c68
-rw-r--r--src/locale/uselocale.c21
5 files changed, 155 insertions, 19 deletions
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);