summaryrefslogtreecommitdiff
path: root/src/locale
diff options
context:
space:
mode:
Diffstat (limited to 'src/locale')
-rw-r--r--src/locale/dcngettext.c87
1 files changed, 55 insertions, 32 deletions
diff --git a/src/locale/dcngettext.c b/src/locale/dcngettext.c
index b68e24bc..b79b7010 100644
--- a/src/locale/dcngettext.c
+++ b/src/locale/dcngettext.c
@@ -100,7 +100,9 @@ struct msgcat {
size_t map_size;
void *volatile plural_rule;
volatile int nplurals;
- char name[];
+ struct binding *binding;
+ const struct __locale_map *lm;
+ int cat;
};
static char *dummy_gettextdomain()
@@ -120,8 +122,8 @@ char *dcngettext(const char *domainname, const char *msgid1, const char *msgid2,
struct msgcat *p;
struct __locale_struct *loc = CURRENT_LOCALE;
const struct __locale_map *lm;
- const char *dirname, *locname, *catname;
- size_t dirlen, loclen, catlen, domlen;
+ size_t domlen;
+ struct binding *q;
if ((unsigned)category >= LC_ALL) goto notrans;
@@ -130,55 +132,76 @@ char *dcngettext(const char *domainname, const char *msgid1, const char *msgid2,
domlen = strnlen(domainname, NAME_MAX+1);
if (domlen > NAME_MAX) goto notrans;
- dirname = gettextdir(domainname, &dirlen);
- if (!dirname) goto notrans;
+ for (q=bindings; q; q=q->next)
+ if (!strcmp(q->domainname, domainname) && q->active)
+ break;
+ if (!q) goto notrans;
lm = loc->cat[category];
if (!lm) {
notrans:
return (char *) ((n == 1) ? msgid1 : msgid2);
}
- locname = lm->name;
-
- catname = catnames[category];
- catlen = catlens[category];
- loclen = strlen(locname);
-
- size_t namelen = dirlen+1 + loclen+1 + catlen+1 + domlen+3;
- char name[namelen+1], *s = name;
-
- memcpy(s, dirname, dirlen);
- s[dirlen] = '/';
- s += dirlen + 1;
- memcpy(s, locname, loclen);
- s[loclen] = '/';
- s += loclen + 1;
- memcpy(s, catname, catlen);
- s[catlen] = '/';
- s += catlen + 1;
- memcpy(s, domainname, domlen);
- s[domlen] = '.';
- s[domlen+1] = 'm';
- s[domlen+2] = 'o';
- s[domlen+3] = 0;
for (p=cats; p; p=p->next)
- if (!strcmp(p->name, name))
+ if (p->binding == q && p->lm == lm && p->cat == category)
break;
if (!p) {
+ const char *dirname, *locname, *catname, *modname, *locp;
+ size_t dirlen, loclen, catlen, modlen, alt_modlen;
void *old_cats;
size_t map_size;
- const void *map = __map_file(name, &map_size);
+
+ dirname = q->dirname;
+ locname = lm->name;
+ catname = catnames[category];
+
+ dirlen = q->dirlen;
+ loclen = strlen(locname);
+ catlen = catlens[category];
+
+ /* Logically split @mod suffix from locale name. */
+ modname = memchr(locname, '@', loclen);
+ if (!modname) modname = locname + loclen;
+ alt_modlen = modlen = loclen - (modname-locname);
+ loclen = modname-locname;
+
+ /* Drop .charset identifier; it is not used. */
+ const char *csp = memchr(locname, '.', loclen);
+ if (csp) loclen = csp-locname;
+
+ char name[dirlen+1 + loclen+modlen+1 + catlen+1 + domlen+3 + 1];
+ const void *map;
+
+ for (;;) {
+ snprintf(name, sizeof name, "%s/%.*s%.*s/%s/%s.mo\0",
+ dirname, (int)loclen, locname,
+ (int)alt_modlen, modname, catname, domainname);
+ if (map = __map_file(name, &map_size)) break;
+
+ /* Try dropping @mod, _YY, then both. */
+ if (alt_modlen) {
+ alt_modlen = 0;
+ } else if ((locp = memchr(locname, '_', loclen))) {
+ loclen = locp-locname;
+ alt_modlen = modlen;
+ } else {
+ break;
+ }
+ }
if (!map) goto notrans;
- p = calloc(sizeof *p + namelen + 1, 1);
+
+ p = calloc(sizeof *p, 1);
if (!p) {
__munmap((void *)map, map_size);
goto notrans;
}
+ p->cat = category;
+ p->binding = q;
+ p->lm = lm;
p->map = map;
p->map_size = map_size;
- memcpy(p->name, name, namelen+1);
do {
old_cats = cats;
p->next = old_cats;