drop lazy plural forms init in dcngettext
authorRich Felker <dalias@aerifal.cx>
Fri, 14 Sep 2018 17:00:41 +0000 (13:00 -0400)
committerRich Felker <dalias@aerifal.cx>
Fri, 14 Sep 2018 17:11:19 +0000 (13:11 -0400)
there is no good reason to wait to find and process the plural rules
for a translated message file until a gettext form requesting plural
rule processing is used. it just imposes additional synchronization,
here in the form of clunky use of atomics.

it looks like there may also have been a race condition where nplurals
could be seen without plural_rule being seen, possibly leading to null
pointer dereference. if so, this commit fixes it.

src/locale/dcngettext.c

index 7fbe7196c1732246ace3db69cae6f40bf6d56ef5..8b891d0001d3da1e6550e2d71e43c3cfdd530bd8 100644 (file)
@@ -100,8 +100,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;
@@ -200,20 +200,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, "");
@@ -237,10 +224,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--) {