From 7590203c486d9002522019045d34ee3dee0a66f5 Mon Sep 17 00:00:00 2001 From: Rich Felker Date: Wed, 7 Aug 2019 21:15:53 -0400 Subject: [PATCH] add non-stub implementation of catgets localization functions these accept the netbsd/openbsd message catalog file format, consisting of a sorted list of set headers and a sorted list of message headers for each set, admitting trivial binary search for lookups. the gnu format was not chosen because it's unusably bad. it does not admit efficient (log time or better) lookups; rather, it requires linear search or hash table lookups, and the hash function is awful: it's literally set_id*msg_id. --- src/locale/catclose.c | 8 +++++ src/locale/catgets.c | 34 +++++++++++++++++++- src/locale/catopen.c | 75 +++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 114 insertions(+), 3 deletions(-) diff --git a/src/locale/catclose.c b/src/locale/catclose.c index 02cd3e5c..54e24dd2 100644 --- a/src/locale/catclose.c +++ b/src/locale/catclose.c @@ -1,6 +1,14 @@ +#define _BSD_SOURCE #include +#include +#include +#include + +#define V(p) be32toh(*(uint32_t *)(p)) int catclose (nl_catd catd) { + char *map = (char *)catd; + munmap(map, V(map+8)+20); return 0; } diff --git a/src/locale/catgets.c b/src/locale/catgets.c index bbee8986..d4a44b35 100644 --- a/src/locale/catgets.c +++ b/src/locale/catgets.c @@ -1,6 +1,38 @@ +#define _BSD_SOURCE #include +#include +#include +#include +#include + +#define V(p) be32toh(*(uint32_t *)(p)) + +int cmp(const void *a, const void *b) +{ + uint32_t x = V(a), y = V(b); + return xy ? 1 : 0; +} char *catgets (nl_catd catd, int set_id, int msg_id, const char *s) { - return (char *)s; + const char *map = (const char *)catd; + uint32_t nsets = V(map+4); + const char *sets = map+20; + const char *msgs = map+20+V(map+12); + const char *strings = map+20+V(map+16); + uint32_t set_id_be = htobe32(set_id); + uint32_t msg_id_be = htobe32(msg_id); + const char *set = bsearch(&set_id_be, sets, nsets, 12, cmp); + if (!set) { + errno = ENOMSG; + return (char *)s; + } + uint32_t nmsgs = V(set+4); + msgs += 12*V(set+8); + const char *msg = bsearch(&msg_id_be, msgs, nmsgs, 12, cmp); + if (!msg) { + errno = ENOMSG; + return (char *)s; + } + return (char *)(strings + V(msg+8)); } diff --git a/src/locale/catopen.c b/src/locale/catopen.c index 3fbc7717..97f2446d 100644 --- a/src/locale/catopen.c +++ b/src/locale/catopen.c @@ -1,8 +1,79 @@ +#define _BSD_SOURCE #include +#include +#include +#include #include +#include +#include +#include +#include "libc.h" -nl_catd catopen (const char *name, int oflag) +#define V(p) be32toh(*(uint32_t *)(p)) + +static nl_catd do_catopen(const char *name) +{ + size_t size; + const unsigned char *map = __map_file(name, &size); + /* Size recorded in the file must match file size; otherwise + * the information needed to unmap the file will be lost. */ + if (!map || V(map) != 0xff88ff89 || 20+V(map+8) != size) { + if(map) munmap((void *)map, size); + errno = ENOENT; + return (nl_catd)-1; + } + return (nl_catd)map; +} + +nl_catd catopen(const char *name, int oflag) { - errno = EOPNOTSUPP; + nl_catd catd; + + if (strchr(name, '/')) return do_catopen(name); + + char buf[PATH_MAX]; + size_t i; + const char *path, *lang, *p, *z; + if (libc.secure || !(path = getenv("NLSPATH"))) { + errno = ENOENT; + return (nl_catd)-1; + } + lang = oflag ? nl_langinfo(_NL_LOCALE_NAME(LC_MESSAGES)) : getenv("LANG"); + if (!lang) lang = ""; + for (p=path; *p; p=z) { + i = 0; + z = __strchrnul(p, ':'); + for (; p= sizeof buf - i) { + break; + } + memcpy(buf+i, v, l); + i += l; + } + if (!*z && (p