fix wide printf forms ignoring width for %lc format specifier
[musl] / src / locale / dcngettext.c
index b79b701..0b53286 100644 (file)
@@ -4,10 +4,18 @@
 #include <errno.h>
 #include <limits.h>
 #include <sys/stat.h>
+#include <sys/mman.h>
 #include <ctype.h>
 #include "locale_impl.h"
-#include "libc.h"
 #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;
@@ -32,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[2];
        struct binding *p, *q;
 
        if (!domainname) return 0;
@@ -98,8 +108,8 @@ struct msgcat {
        struct msgcat *next;
        const void *map;
        size_t map_size;
-       void *volatile plural_rule;
-       volatile int nplurals;
+       const char *plural_rule;
+       int nplurals;
        struct binding *binding;
        const struct __locale_map *lm;
        int cat;
@@ -112,10 +122,6 @@ static char *dummy_gettextdomain()
 
 weak_alias(dummy_gettextdomain, __gettextdomain);
 
-const unsigned char *__map_file(const char *, size_t *);
-int __munmap(void *, size_t);
-unsigned long __pleval(const char *, unsigned long);
-
 char *dcngettext(const char *domainname, const char *msgid1, const char *msgid2, unsigned long int n, int category)
 {
        static struct msgcat *volatile cats;
@@ -124,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;
 
@@ -140,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);
        }
 
@@ -202,20 +213,7 @@ notrans:
                p->lm = lm;
                p->map = map;
                p->map_size = map_size;
-               do {
-                       old_cats = cats;
-                       p->next = old_cats;
-               } while (a_cas_p(&cats, old_cats, p) != old_cats);
-       }
 
-       const char *trans = __mo_lookup(p->map, p->map_size, msgid1);
-       if (!trans) goto notrans;
-
-       /* Non-plural-processing gettext forms pass a null pointer as
-        * msgid2 to request that dcngettext suppress plural processing. */
-       if (!msgid2) return (char *)trans;
-
-       if (!p->plural_rule) {
                const char *rule = "n!=1;";
                unsigned long np = 2;
                const char *r = __mo_lookup(p->map, p->map_size, "");
@@ -239,10 +237,22 @@ notrans:
                                        rule = r+7;
                        }
                }
-               a_store(&p->nplurals, np);
-               a_cas_p(&p->plural_rule, 0, (void *)rule);
+               p->nplurals = np;
+               p->plural_rule = rule;
+
+               do {
+                       old_cats = cats;
+                       p->next = old_cats;
+               } while (a_cas_p(&cats, old_cats, p) != old_cats);
        }
-       if (p->nplurals) {
+
+       const char *trans = __mo_lookup(p->map, p->map_size, msgid1);
+       if (!trans) goto notrans;
+
+       /* Non-plural-processing gettext forms pass a null pointer as
+        * msgid2 to request that dcngettext suppress plural processing. */
+
+       if (msgid2 && p->nplurals) {
                unsigned long plural = __pleval(p->plural_rule, n);
                if (plural > p->nplurals) goto notrans;
                while (plural--) {
@@ -253,6 +263,7 @@ notrans:
                        trans += l+1;
                }
        }
+       errno = old_errno;
        return (char *)trans;
 }