summaryrefslogtreecommitdiff
path: root/src/locale
diff options
context:
space:
mode:
authorRich Felker <dalias@aerifal.cx>2014-07-02 19:33:19 -0400
committerRich Felker <dalias@aerifal.cx>2014-07-02 19:33:19 -0400
commit0bc03091bb674ebb9fa6fe69e4aec1da3ac484f2 (patch)
tree7f43063dd039e09a6a74d2677e1138d345bf7b9d /src/locale
parent984c25b74da085c6ae6b44a87bbd5f8afc9be331 (diff)
downloadmusl-0bc03091bb674ebb9fa6fe69e4aec1da3ac484f2.tar.gz
musl-0bc03091bb674ebb9fa6fe69e4aec1da3ac484f2.tar.bz2
musl-0bc03091bb674ebb9fa6fe69e4aec1da3ac484f2.tar.xz
musl-0bc03091bb674ebb9fa6fe69e4aec1da3ac484f2.zip
add locale framework
this commit adds non-stub implementations of setlocale, duplocale, newlocale, and uselocale, along with the data structures and minimal code needed for representing the active locale on a per-thread basis and optimizing the common case where thread-local locale settings are not in use. at this point, the data structures only contain what is necessary to represent LC_CTYPE (a single flag) and LC_MESSAGES (a name for use in finding message translation files). representation for the other categories will be added later; the expectation is that a single pointer will suffice for each. for LC_CTYPE, the strings "C" and "POSIX" are treated as special; any other string is accepted and treated as "C.UTF-8". for other categories, any string is accepted after being truncated to a maximum supported length (currently 15 bytes). for LC_MESSAGES, the name is kept regardless of whether libc itself can use such a message translation locale, since applications using catgets or gettext should be able to use message locales libc is not aware of. for other categories, names which are not successfully loaded as locales (which, at present, means all names) are treated as aliases for "C". setlocale never fails. locale settings are not yet used anywhere, so this commit should have no visible effects except for the contents of the string returned by setlocale.
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);