diff options
author | Rich Felker <dalias@aerifal.cx> | 2014-07-27 04:29:56 -0400 |
---|---|---|
committer | Rich Felker <dalias@aerifal.cx> | 2014-07-27 04:29:56 -0400 |
commit | 2068b4e8911a3a49cded44b4568f6c943a8c98f8 (patch) | |
tree | d662b28c1a9f87674d0bc25be051a8fb69312316 /src/locale | |
parent | c5b8f1930512d206a7c1cf1093a4a47e1722a414 (diff) | |
download | musl-2068b4e8911a3a49cded44b4568f6c943a8c98f8.tar.gz musl-2068b4e8911a3a49cded44b4568f6c943a8c98f8.tar.bz2 musl-2068b4e8911a3a49cded44b4568f6c943a8c98f8.tar.xz musl-2068b4e8911a3a49cded44b4568f6c943a8c98f8.zip |
implement gettext message translation functions
this commit replaces the stub implementations with working message
translation functions. translation units are factored so as to prevent
pulling in the legacy, non-library-safe functions which use a global
textdomain in modern code which is using the versions with an explicit
domain argument. bind_textdomain_codeset is also placed in its own
file since it should not be needed by most programs.
this implementation is still missing some features: the LANGUAGE
environment variable (for multiple fallback languages) is not honored,
and non-default plural-form rules are not supported. these issues will
be addressed in a later commit.
one notable difference from the GNU implementation is that there is no
default path for loading translation files. in principle one could be
added, but since the documented correct usage is to call the
bindtextdomain function, a default path is probably unnecessary.
Diffstat (limited to 'src/locale')
-rw-r--r-- | src/locale/bind_textdomain_codeset.c | 11 | ||||
-rw-r--r-- | src/locale/dcngettext.c | 216 | ||||
-rw-r--r-- | src/locale/intl.c | 68 | ||||
-rw-r--r-- | src/locale/textdomain.c | 44 |
4 files changed, 271 insertions, 68 deletions
diff --git a/src/locale/bind_textdomain_codeset.c b/src/locale/bind_textdomain_codeset.c new file mode 100644 index 00000000..5ebfd5e8 --- /dev/null +++ b/src/locale/bind_textdomain_codeset.c @@ -0,0 +1,11 @@ +#include <libintl.h> +#include <string.h> +#include <strings.h> +#include <errno.h> + +char *bind_textdomain_codeset(const char *domainname, const char *codeset) +{ + if (codeset && strcasecmp(codeset, "UTF-8")) + errno = EINVAL; + return NULL; +} diff --git a/src/locale/dcngettext.c b/src/locale/dcngettext.c new file mode 100644 index 00000000..4f9e4174 --- /dev/null +++ b/src/locale/dcngettext.c @@ -0,0 +1,216 @@ +#include <libintl.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <limits.h> +#include <sys/stat.h> +#include "locale_impl.h" +#include "libc.h" +#include "atomic.h" + +struct binding { + struct binding *next; + int dirlen; + int active; + char *domainname; + char *dirname; + char buf[]; +}; + +static void *volatile bindings; + +static char *gettextdir(const char *domainname, size_t *dirlen) +{ + struct binding *p; + for (p=bindings; p; p=p->next) { + if (!strcmp(p->domainname, domainname) && p->active) { + *dirlen = p->dirlen; + return (char *)p->dirname; + } + } + return 0; +} + +char *bindtextdomain(const char *domainname, const char *dirname) +{ + static int lock[2]; + struct binding *p, *q; + + if (!domainname) return 0; + if (!dirname) return gettextdir(domainname, &(size_t){0}); + + size_t domlen = strlen(domainname); + size_t dirlen = strlen(dirname); + if (domlen > NAME_MAX || dirlen >= PATH_MAX) { + errno = EINVAL; + return 0; + } + + LOCK(lock); + + for (p=bindings; p; p=p->next) { + if (!strcmp(p->domainname, domainname) && + !strcmp(p->dirname, dirname)) { + break; + } + } + + if (!p) { + p = malloc(sizeof *p + domlen + dirlen + 2); + if (!p) { + UNLOCK(lock); + return 0; + } + p->next = bindings; + p->dirlen = dirlen; + p->domainname = p->buf; + p->dirname = p->buf + domlen + 1; + memcpy(p->domainname, domainname, domlen+1); + memcpy(p->dirname, dirname, dirlen+1); + a_cas_p(&bindings, bindings, p); + } + + a_store(&p->active, 1); + + for (q=bindings; q; q=q->next) { + if (!strcmp(p->domainname, domainname) && q != p) + a_store(&q->active, 0); + } + + UNLOCK(lock); + + return (char *)p->dirname; +} + +static const char catnames[][12] = { + "LC_TIME", + "LC_COLLATE", + "LC_MONETARY", + "LC_MESSAGES", +}; + +static const char catlens[] = { 7, 10, 11, 11 }; + +struct msgcat { + struct msgcat *next; + const void *map; + size_t map_size; + char name[]; +}; + +static char *dummy_gettextdomain() +{ + return "messages"; +} + +weak_alias(dummy_gettextdomain, __gettextdomain); + +const unsigned char *__map_file(const char *, size_t *); +int __munmap(void *, size_t); + +char *dcngettext(const char *domainname, const char *msgid1, const char *msgid2, unsigned long int n, int category) +{ + static struct msgcat *volatile cats; + struct msgcat *p; + struct __locale_struct *loc = CURRENT_LOCALE; + struct __locale_map *lm; + const char *dirname, *locname, *catname; + size_t dirlen, loclen, catlen, domlen; + + if (!domainname) domainname = __gettextdomain(); + + domlen = strlen(domainname); + if (domlen > NAME_MAX) goto notrans; + + dirname = gettextdir(domainname, &dirlen); + if (!dirname) goto notrans; + + switch (category) { + case LC_MESSAGES: + locname = loc->messages_name; + if (!*locname) goto notrans; + break; + case LC_TIME: + case LC_MONETARY: + case LC_COLLATE: + lm = loc->cat[category-2]; + if (!lm) goto notrans; + locname = lm->name; + break; + default: +notrans: + return (char *) ((n == 1) ? msgid1 : msgid2); + } + + catname = catnames[category-2]; + catlen = catlens[category-2]; + 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)) + break; + + if (!p) { + void *old_cats; + size_t map_size; + const void *map = __map_file(name, &map_size); + if (!map) goto notrans; + p = malloc(sizeof *p + namelen + 1); + if (!p) { + __munmap((void *)map, map_size); + goto notrans; + } + p->map = map; + p->map_size = map_size; + memcpy(p->name, name, namelen+1); + 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; + + /* FIXME: support alternate plural rules */ + if (n != 1) { + size_t l = strlen(trans); + if (l+1 >= p->map_size - (trans - (char *)p->map)) + goto notrans; + trans += l+1; + } + return (char *)trans; +} + +char *dcgettext(const char *domainname, const char *msgid, int category) +{ + return dcngettext(domainname, msgid, msgid, 1, category); +} + +char *dngettext(const char *domainname, const char *msgid1, const char *msgid2, unsigned long int n) +{ + return dcngettext(domainname, msgid1, msgid2, n, LC_MESSAGES); +} + +char *dgettext(const char *domainname, const char *msgid) +{ + return dcngettext(domainname, msgid, msgid, 1, LC_MESSAGES); +} diff --git a/src/locale/intl.c b/src/locale/intl.c deleted file mode 100644 index ad040524..00000000 --- a/src/locale/intl.c +++ /dev/null @@ -1,68 +0,0 @@ -#include <libintl.h> -#include <stdlib.h> -#include <string.h> -#include <strings.h> -#include <errno.h> - -char *gettext(const char *msgid) -{ - return (char *) msgid; -} - -char *dgettext(const char *domainname, const char *msgid) -{ - return (char *) msgid; -} - -char *dcgettext(const char *domainname, const char *msgid, int category) -{ - return (char *) msgid; -} - -char *ngettext(const char *msgid1, const char *msgid2, unsigned long int n) -{ - return (char *) ((n == 1) ? msgid1 : msgid2); -} - -char *dngettext(const char *domainname, const char *msgid1, const char *msgid2, unsigned long int n) -{ - return (char *) ((n == 1) ? msgid1 : msgid2); -} - -char *dcngettext(const char *domainname, const char *msgid1, const char *msgid2, unsigned long int n, int category) -{ - return (char *) ((n == 1) ? msgid1 : msgid2); -} - -char *textdomain(const char *domainname) -{ - static const char default_str[] = "messages"; - - if (domainname && *domainname && strcmp(domainname, default_str)) { - errno = EINVAL; - return NULL; - } - return (char *) default_str; -} - -char *bindtextdomain(const char *domainname, const char *dirname) -{ - static const char dir[] = "/"; - - if (!domainname || !*domainname - || (dirname && ((dirname[0] != '/') || dirname[1])) - ) { - errno = EINVAL; - return NULL; - } - - return (char *) dir; -} - -char *bind_textdomain_codeset(const char *domainname, const char *codeset) -{ - if (!domainname || !*domainname || (codeset && strcasecmp(codeset, "UTF-8"))) { - errno = EINVAL; - } - return NULL; -} diff --git a/src/locale/textdomain.c b/src/locale/textdomain.c new file mode 100644 index 00000000..c501501d --- /dev/null +++ b/src/locale/textdomain.c @@ -0,0 +1,44 @@ +#include <libintl.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <limits.h> +#include "libc.h" +#include "atomic.h" + +static char *current_domain; + +char *__gettextdomain() +{ + return current_domain ? current_domain : "messages"; +} + +char *textdomain(const char *domainname) +{ + if (!domainname) return __gettextdomain(); + + size_t domlen = strlen(domainname); + if (domlen > NAME_MAX) { + errno = EINVAL; + return 0; + } + + if (!current_domain) { + current_domain = malloc(NAME_MAX+1); + if (!current_domain) return 0; + } + + memcpy(current_domain, domainname, domlen+1); + + return current_domain; +} + +char *gettext(const char *msgid) +{ + return dgettext(0, msgid); +} + +char *ngettext(const char *msgid1, const char *msgid2, unsigned long int n) +{ + return dngettext(0, msgid1, msgid2, n); +} |