summaryrefslogtreecommitdiff
path: root/src/locale
diff options
context:
space:
mode:
Diffstat (limited to 'src/locale')
-rw-r--r--src/locale/bind_textdomain_codeset.c6
-rw-r--r--src/locale/catclose.c8
-rw-r--r--src/locale/catgets.c34
-rw-r--r--src/locale/catopen.c75
-rw-r--r--src/locale/codepages.h11
-rw-r--r--src/locale/dcngettext.c16
-rw-r--r--src/locale/duplocale.c5
-rw-r--r--src/locale/freelocale.c5
-rw-r--r--src/locale/iconv.c17
-rw-r--r--src/locale/locale_map.c22
-rw-r--r--src/locale/newlocale.c70
-rw-r--r--src/locale/setlocale.c39
-rw-r--r--src/locale/strtod_l.c22
13 files changed, 270 insertions, 60 deletions
diff --git a/src/locale/bind_textdomain_codeset.c b/src/locale/bind_textdomain_codeset.c
index 5ebfd5e8..240e83ed 100644
--- a/src/locale/bind_textdomain_codeset.c
+++ b/src/locale/bind_textdomain_codeset.c
@@ -5,7 +5,9 @@
char *bind_textdomain_codeset(const char *domainname, const char *codeset)
{
- if (codeset && strcasecmp(codeset, "UTF-8"))
+ if (codeset && strcasecmp(codeset, "UTF-8")) {
errno = EINVAL;
- return NULL;
+ return 0;
+ }
+ return "UTF-8";
}
diff --git a/src/locale/catclose.c b/src/locale/catclose.c
index 02cd3e5c..54e24dd2 100644
--- a/src/locale/catclose.c
+++ b/src/locale/catclose.c
@@ -1,6 +1,14 @@
+#define _BSD_SOURCE
#include <nl_types.h>
+#include <stdint.h>
+#include <endian.h>
+#include <sys/mman.h>
+
+#define V(p) be32toh(*(uint32_t *)(p))
int catclose (nl_catd catd)
{
+ char *map = (char *)catd;
+ munmap(map, V(map+8)+20);
return 0;
}
diff --git a/src/locale/catgets.c b/src/locale/catgets.c
index bbee8986..71c31c1d 100644
--- a/src/locale/catgets.c
+++ b/src/locale/catgets.c
@@ -1,6 +1,38 @@
+#define _BSD_SOURCE
#include <nl_types.h>
+#include <endian.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <errno.h>
+
+#define V(p) be32toh(*(uint32_t *)(p))
+
+static int cmp(const void *a, const void *b)
+{
+ uint32_t x = V(a), y = V(b);
+ return x<y ? -1 : x>y ? 1 : 0;
+}
char *catgets (nl_catd catd, int set_id, int msg_id, const char *s)
{
- return (char *)s;
+ const char *map = (const char *)catd;
+ uint32_t nsets = V(map+4);
+ const char *sets = map+20;
+ const char *msgs = map+20+V(map+12);
+ const char *strings = map+20+V(map+16);
+ uint32_t set_id_be = htobe32(set_id);
+ uint32_t msg_id_be = htobe32(msg_id);
+ const char *set = bsearch(&set_id_be, sets, nsets, 12, cmp);
+ if (!set) {
+ errno = ENOMSG;
+ return (char *)s;
+ }
+ uint32_t nmsgs = V(set+4);
+ msgs += 12*V(set+8);
+ const char *msg = bsearch(&msg_id_be, msgs, nmsgs, 12, cmp);
+ if (!msg) {
+ errno = ENOMSG;
+ return (char *)s;
+ }
+ return (char *)(strings + V(msg+8));
}
diff --git a/src/locale/catopen.c b/src/locale/catopen.c
index 3fbc7717..97f2446d 100644
--- a/src/locale/catopen.c
+++ b/src/locale/catopen.c
@@ -1,8 +1,79 @@
+#define _BSD_SOURCE
#include <nl_types.h>
+#include <string.h>
+#include <stdint.h>
+#include <endian.h>
#include <errno.h>
+#include <langinfo.h>
+#include <locale.h>
+#include <sys/mman.h>
+#include "libc.h"
-nl_catd catopen (const char *name, int oflag)
+#define V(p) be32toh(*(uint32_t *)(p))
+
+static nl_catd do_catopen(const char *name)
+{
+ size_t size;
+ const unsigned char *map = __map_file(name, &size);
+ /* Size recorded in the file must match file size; otherwise
+ * the information needed to unmap the file will be lost. */
+ if (!map || V(map) != 0xff88ff89 || 20+V(map+8) != size) {
+ if(map) munmap((void *)map, size);
+ errno = ENOENT;
+ return (nl_catd)-1;
+ }
+ return (nl_catd)map;
+}
+
+nl_catd catopen(const char *name, int oflag)
{
- errno = EOPNOTSUPP;
+ nl_catd catd;
+
+ if (strchr(name, '/')) return do_catopen(name);
+
+ char buf[PATH_MAX];
+ size_t i;
+ const char *path, *lang, *p, *z;
+ if (libc.secure || !(path = getenv("NLSPATH"))) {
+ errno = ENOENT;
+ return (nl_catd)-1;
+ }
+ lang = oflag ? nl_langinfo(_NL_LOCALE_NAME(LC_MESSAGES)) : getenv("LANG");
+ if (!lang) lang = "";
+ for (p=path; *p; p=z) {
+ i = 0;
+ z = __strchrnul(p, ':');
+ for (; p<z; p++) {
+ const char *v;
+ size_t l;
+ if (*p!='%') v=p, l=1;
+ else switch (*++p) {
+ case 'N': v=name; l=strlen(v); break;
+ case 'L': v=lang; l=strlen(v); break;
+ case 'l': v=lang; l=strcspn(v,"_.@"); break;
+ case 't':
+ v=__strchrnul(lang,'_');
+ if (*v) v++;
+ l=strcspn(v,".@");
+ break;
+ case 'c': v="UTF-8"; l=5; break;
+ case '%': v="%"; l=1; break;
+ default: v=0;
+ }
+ if (!v || l >= sizeof buf - i) {
+ break;
+ }
+ memcpy(buf+i, v, l);
+ i += l;
+ }
+ if (!*z && (p<z || !i)) break;
+ if (p<z) continue;
+ if (*z) z++;
+ buf[i] = 0;
+ /* Leading : or :: in NLSPATH is same as %N */
+ catd = do_catopen(i ? buf : name);
+ if (catd != (nl_catd)-1) return catd;
+ }
+ errno = ENOENT;
return (nl_catd)-1;
}
diff --git a/src/locale/codepages.h b/src/locale/codepages.h
index 4e236ef3..a254f0f5 100644
--- a/src/locale/codepages.h
+++ b/src/locale/codepages.h
@@ -286,6 +286,17 @@
"\323\174\103\215\64\365\124\123\213\77\336\150\263\115\66\375\164\363\12\55"
"\255\304\42\261\57\266\234\162\17\56\260\240\162\113\56\263\310\62\66\50"
+"cp858\0"
+"\0\40"
+"\307\360\223\216\70\344\200\123\316\71\352\254\203\316\73\356\260\103\114\61"
+"\311\230\143\14\75\366\310\263\117\76\377\130\303\15\76\243\140\163\15\135"
+"\341\264\63\217\76\361\104\243\212\56\277\270\302\112\57\274\204\262\312\56"
+"\140\207\55\66\315\72\7\43\14\60\251\104\375\163\321\113\213\122\212\315"
+"\67\363\274\163\316\63\367\74\316\60\110\13\175\65\325\116\373\254\65\51"
+"\360\100\243\314\62\310\220\334\214\63\317\340\134\163\327\134\233\302\314\326"
+"\323\174\103\215\64\365\124\123\213\77\336\150\263\115\66\375\164\363\12\55"
+"\255\304\42\261\57\266\234\162\17\56\260\240\162\113\56\263\310\62\66\50"
+
"cp866\0"
"\0\40"
"\337\201\27\236\170\343\221\127\236\171\347\241\227\236\172"
diff --git a/src/locale/dcngettext.c b/src/locale/dcngettext.c
index 8b891d00..0b53286d 100644
--- a/src/locale/dcngettext.c
+++ b/src/locale/dcngettext.c
@@ -10,6 +10,12 @@
#include "atomic.h"
#include "pleval.h"
#include "lock.h"
+#include "fork_impl.h"
+
+#define malloc __libc_malloc
+#define calloc __libc_calloc
+#define realloc undef
+#define free undef
struct binding {
struct binding *next;
@@ -34,9 +40,11 @@ static char *gettextdir(const char *domainname, size_t *dirlen)
return 0;
}
+static volatile int lock[1];
+volatile int *const __gettext_lockptr = lock;
+
char *bindtextdomain(const char *domainname, const char *dirname)
{
- static volatile int lock[1];
struct binding *p, *q;
if (!domainname) return 0;
@@ -122,6 +130,10 @@ char *dcngettext(const char *domainname, const char *msgid1, const char *msgid2,
const struct __locale_map *lm;
size_t domlen;
struct binding *q;
+ int old_errno = errno;
+
+ /* match gnu gettext behaviour */
+ if (!msgid1) goto notrans;
if ((unsigned)category >= LC_ALL) goto notrans;
@@ -138,6 +150,7 @@ char *dcngettext(const char *domainname, const char *msgid1, const char *msgid2,
lm = loc->cat[category];
if (!lm) {
notrans:
+ errno = old_errno;
return (char *) ((n == 1) ? msgid1 : msgid2);
}
@@ -250,6 +263,7 @@ notrans:
trans += l+1;
}
}
+ errno = old_errno;
return (char *)trans;
}
diff --git a/src/locale/duplocale.c b/src/locale/duplocale.c
index 030b64cb..5ce33ae6 100644
--- a/src/locale/duplocale.c
+++ b/src/locale/duplocale.c
@@ -3,6 +3,11 @@
#include "locale_impl.h"
#include "libc.h"
+#define malloc __libc_malloc
+#define calloc undef
+#define realloc undef
+#define free undef
+
locale_t __duplocale(locale_t old)
{
locale_t new = malloc(sizeof *new);
diff --git a/src/locale/freelocale.c b/src/locale/freelocale.c
index 802b8bfe..385d1206 100644
--- a/src/locale/freelocale.c
+++ b/src/locale/freelocale.c
@@ -1,6 +1,11 @@
#include <stdlib.h>
#include "locale_impl.h"
+#define malloc undef
+#define calloc undef
+#define realloc undef
+#define free __libc_free
+
void freelocale(locale_t l)
{
if (__loc_is_allocated(l)) free(l);
diff --git a/src/locale/iconv.c b/src/locale/iconv.c
index 3047c27b..52178950 100644
--- a/src/locale/iconv.c
+++ b/src/locale/iconv.c
@@ -49,10 +49,10 @@ static const unsigned char charmaps[] =
"ucs4\0utf32\0\0\313"
"ucs2\0\0\314"
"eucjp\0\0\320"
-"shiftjis\0sjis\0\0\321"
+"shiftjis\0sjis\0cp932\0\0\321"
"iso2022jp\0\0\322"
"gb18030\0\0\330"
-"gbk\0\0\331"
+"gbk\0cp936\0windows936\0\0\331"
"gb2312\0\0\332"
"big5\0bigfive\0cp950\0big5hkscs\0\0\340"
"euckr\0ksc5601\0ksx1001\0cp949\0\0\350"
@@ -339,7 +339,10 @@ size_t iconv(iconv_t cd, char **restrict in, size_t *restrict inb, char **restri
} else if (d-159 <= 252-159) {
c++;
d -= 159;
+ } else {
+ goto ilseq;
}
+ if (c>=84) goto ilseq;
c = jis0208[c][d];
if (!c) goto ilseq;
break;
@@ -403,6 +406,10 @@ size_t iconv(iconv_t cd, char **restrict in, size_t *restrict inb, char **restri
if (c < 128) break;
if (c < 0xa1) goto ilseq;
case GBK:
+ if (c == 128) {
+ c = 0x20ac;
+ break;
+ }
case GB18030:
if (c < 128) break;
c -= 0x81;
@@ -495,7 +502,7 @@ size_t iconv(iconv_t cd, char **restrict in, size_t *restrict inb, char **restri
if (c >= 93 || d >= 94) {
c += (0xa1-0x81);
d += 0xa1;
- if (c >= 93 || c>=0xc6-0x81 && d>0x52)
+ if (c > 0xc6-0x81 || c==0xc6-0x81 && d>0x52)
goto ilseq;
if (d-'A'<26) d = d-'A';
else if (d-'a'<26) d = d-'a'+26;
@@ -538,6 +545,10 @@ size_t iconv(iconv_t cd, char **restrict in, size_t *restrict inb, char **restri
if (*outb < k) goto toobig;
memcpy(*out, tmp, k);
} else k = wctomb_utf8(*out, c);
+ /* This failure condition should be unreachable, but
+ * is included to prevent decoder bugs from translating
+ * into advancement outside the output buffer range. */
+ if (k>4) goto ilseq;
*out += k;
*outb -= k;
break;
diff --git a/src/locale/locale_map.c b/src/locale/locale_map.c
index 2321bac0..da61f7fc 100644
--- a/src/locale/locale_map.c
+++ b/src/locale/locale_map.c
@@ -1,9 +1,16 @@
#include <locale.h>
#include <string.h>
#include <sys/mman.h>
+#include <stdlib.h>
#include "locale_impl.h"
#include "libc.h"
#include "lock.h"
+#include "fork_impl.h"
+
+#define malloc __libc_malloc
+#define calloc undef
+#define realloc undef
+#define free undef
const char *__lctrans_impl(const char *msg, const struct __locale_map *lm)
{
@@ -21,9 +28,11 @@ static const char envvars[][12] = {
"LC_MESSAGES",
};
+volatile int __locale_lock[1];
+volatile int *const __locale_lockptr = __locale_lock;
+
const struct __locale_map *__get_locale(int cat, const char *val)
{
- static volatile int lock[1];
static void *volatile loc_head;
const struct __locale_map *p;
struct __locale_map *new = 0;
@@ -54,20 +63,12 @@ const struct __locale_map *__get_locale(int cat, const char *val)
for (p=loc_head; p; p=p->next)
if (!strcmp(val, p->name)) return p;
- LOCK(lock);
-
- for (p=loc_head; p; p=p->next)
- if (!strcmp(val, p->name)) {
- UNLOCK(lock);
- return p;
- }
-
if (!libc.secure) path = getenv("MUSL_LOCPATH");
/* FIXME: add a default path? */
if (path) for (; *path; path=z+!!*z) {
z = __strchrnul(path, ':');
- l = z - path - !!*z;
+ l = z - path;
if (l >= sizeof buf - n - 2) continue;
memcpy(buf, path, l);
buf[l] = '/';
@@ -108,6 +109,5 @@ const struct __locale_map *__get_locale(int cat, const char *val)
* requested name was "C" or "POSIX". */
if (!new && cat == LC_CTYPE) new = (void *)&__c_dot_utf8;
- UNLOCK(lock);
return new;
}
diff --git a/src/locale/newlocale.c b/src/locale/newlocale.c
index 8fb006a7..9ac3cd38 100644
--- a/src/locale/newlocale.c
+++ b/src/locale/newlocale.c
@@ -1,48 +1,70 @@
#include <stdlib.h>
#include <string.h>
+#include <pthread.h>
#include "locale_impl.h"
+#include "lock.h"
+
+#define malloc __libc_malloc
+#define calloc undef
+#define realloc undef
+#define free undef
+
+static int default_locale_init_done;
+static struct __locale_struct default_locale, default_ctype_locale;
int __loc_is_allocated(locale_t loc)
{
- return loc && loc != C_LOCALE && loc != UTF8_LOCALE;
+ return loc && loc != C_LOCALE && loc != UTF8_LOCALE
+ && loc != &default_locale && loc != &default_ctype_locale;
}
-locale_t __newlocale(int mask, const char *name, locale_t loc)
+static locale_t do_newlocale(int mask, const char *name, locale_t loc)
{
- int i, j;
struct __locale_struct tmp;
- const struct __locale_map *lm;
+
+ for (int i=0; i<LC_ALL; i++) {
+ tmp.cat[i] = (!(mask & (1<<i)) && loc) ? loc->cat[i] :
+ __get_locale(i, (mask & (1<<i)) ? name : "");
+ if (tmp.cat[i] == LOC_MAP_FAILED)
+ return 0;
+ }
/* For locales with allocated storage, modify in-place. */
if (__loc_is_allocated(loc)) {
- for (i=0; i<LC_ALL; i++)
- if (mask & (1<<i))
- loc->cat[i] = __get_locale(i, name);
+ *loc = tmp;
return loc;
}
- /* Otherwise, build a temporary locale object, which will only
- * be instantiated in allocated storage if it does not match
- * one of the built-in static locales. This makes the common
- * usage case for newlocale, getting a C locale with predictable
- * behavior, very fast, and more importantly, fail-safe. */
- for (j=i=0; i<LC_ALL; i++) {
- if (loc && !(mask & (1<<i)))
- lm = loc->cat[i];
- else
- lm = __get_locale(i, mask & (1<<i) ? name : "");
- if (lm) j++;
- tmp.cat[i] = lm;
- }
+ /* Otherwise, first see if we can use one of the builtin locales.
+ * This makes the common usage case for newlocale, getting a C locale
+ * with predictable behavior, very fast, and more importantly, fail-safe. */
+ if (!memcmp(&tmp, C_LOCALE, sizeof tmp)) return C_LOCALE;
+ if (!memcmp(&tmp, UTF8_LOCALE, sizeof tmp)) return UTF8_LOCALE;
- if (!j)
- return C_LOCALE;
- if (j==1 && tmp.cat[LC_CTYPE]==&__c_dot_utf8)
- return UTF8_LOCALE;
+ /* And provide builtins for the initial default locale, and a
+ * variant of the C locale honoring the default locale's encoding. */
+ if (!default_locale_init_done) {
+ for (int i=0; i<LC_ALL; i++)
+ default_locale.cat[i] = __get_locale(i, "");
+ default_ctype_locale.cat[LC_CTYPE] = default_locale.cat[LC_CTYPE];
+ default_locale_init_done = 1;
+ }
+ if (!memcmp(&tmp, &default_locale, sizeof tmp)) return &default_locale;
+ if (!memcmp(&tmp, &default_ctype_locale, sizeof tmp))
+ return &default_ctype_locale;
+ /* If no builtin locale matched, attempt to allocate and copy. */
if ((loc = malloc(sizeof *loc))) *loc = tmp;
return loc;
}
+locale_t __newlocale(int mask, const char *name, locale_t loc)
+{
+ LOCK(__locale_lock);
+ loc = do_newlocale(mask, name, loc);
+ UNLOCK(__locale_lock);
+ return loc;
+}
+
weak_alias(__newlocale, newlocale);
diff --git a/src/locale/setlocale.c b/src/locale/setlocale.c
index 11d823ce..360c4437 100644
--- a/src/locale/setlocale.c
+++ b/src/locale/setlocale.c
@@ -7,23 +7,13 @@
static char buf[LC_ALL*(LOCALE_NAME_MAX+1)];
-static char *setlocale_one_unlocked(int cat, const char *name)
-{
- const struct __locale_map *lm;
-
- if (name) libc.global_locale.cat[cat] = lm = __get_locale(cat, name);
- else lm = libc.global_locale.cat[cat];
-
- return lm ? (char *)lm->name : "C";
-}
-
char *setlocale(int cat, const char *name)
{
- static volatile int lock[1];
+ const struct __locale_map *lm;
if ((unsigned)cat > LC_ALL) return 0;
- LOCK(lock);
+ LOCK(__locale_lock);
/* For LC_ALL, setlocale is required to return a string which
* encodes the current setting for all categories. The format of
@@ -33,6 +23,7 @@ char *setlocale(int cat, const char *name)
if (cat == LC_ALL) {
int i;
if (name) {
+ struct __locale_struct tmp_locale;
char part[LOCALE_NAME_MAX+1] = "C.UTF-8";
const char *p = name;
for (i=0; i<LC_ALL; i++) {
@@ -42,8 +33,14 @@ char *setlocale(int cat, const char *name)
part[z-p] = 0;
if (*z) p = z+1;
}
- setlocale_one_unlocked(i, part);
+ lm = __get_locale(i, part);
+ if (lm == LOC_MAP_FAILED) {
+ UNLOCK(__locale_lock);
+ return 0;
+ }
+ tmp_locale.cat[i] = lm;
}
+ libc.global_locale = tmp_locale;
}
char *s = buf;
const char *part;
@@ -59,13 +56,23 @@ char *setlocale(int cat, const char *name)
s += l+1;
}
*--s = 0;
- UNLOCK(lock);
+ UNLOCK(__locale_lock);
return same==LC_ALL ? (char *)part : buf;
}
- char *ret = setlocale_one_unlocked(cat, name);
+ if (name) {
+ lm = __get_locale(cat, name);
+ if (lm == LOC_MAP_FAILED) {
+ UNLOCK(__locale_lock);
+ return 0;
+ }
+ libc.global_locale.cat[cat] = lm;
+ } else {
+ lm = libc.global_locale.cat[cat];
+ }
+ char *ret = lm ? (char *)lm->name : "C";
- UNLOCK(lock);
+ UNLOCK(__locale_lock);
return ret;
}
diff --git a/src/locale/strtod_l.c b/src/locale/strtod_l.c
new file mode 100644
index 00000000..574ba148
--- /dev/null
+++ b/src/locale/strtod_l.c
@@ -0,0 +1,22 @@
+#define _GNU_SOURCE
+#include <stdlib.h>
+#include <locale.h>
+
+float strtof_l(const char *restrict s, char **restrict p, locale_t l)
+{
+ return strtof(s, p);
+}
+
+double strtod_l(const char *restrict s, char **restrict p, locale_t l)
+{
+ return strtod(s, p);
+}
+
+long double strtold_l(const char *restrict s, char **restrict p, locale_t l)
+{
+ return strtold(s, p);
+}
+
+weak_alias(strtof_l, __strtof_l);
+weak_alias(strtod_l, __strtod_l);
+weak_alias(strtold_l, __strtold_l);