about summary refs log tree commit diff
path: root/src/locale/setlocale.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/locale/setlocale.c')
-rw-r--r--src/locale/setlocale.c68
1 files changed, 63 insertions, 5 deletions
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";
+	}
 }