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