diff options
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); +} |