diff options
Diffstat (limited to 'usr/src/lib/libast/common/port/mc.c')
-rw-r--r-- | usr/src/lib/libast/common/port/mc.c | 677 |
1 files changed, 677 insertions, 0 deletions
diff --git a/usr/src/lib/libast/common/port/mc.c b/usr/src/lib/libast/common/port/mc.c new file mode 100644 index 0000000000..8945841ae9 --- /dev/null +++ b/usr/src/lib/libast/common/port/mc.c @@ -0,0 +1,677 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1985-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler <gsf@research.att.com> * +* David Korn <dgk@research.att.com> * +* Phong Vo <kpv@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped + +/* + * Glenn Fowler + * AT&T Research + * + * machine independent binary message catalog implementation + */ + +#include "sfhdr.h" +#include "lclib.h" + +#include <iconv.h> + +#define _MC_PRIVATE_ \ + size_t nstrs; \ + size_t nmsgs; \ + iconv_t cvt; \ + Sfio_t* tmp; \ + Vmalloc_t* vm; + +#include <vmalloc.h> +#include <error.h> +#include <mc.h> +#include <nl_types.h> + +/* + * find the binary message catalog path for <locale,catalog> + * result placed in path of size PATH_MAX + * pointer to path returned + * catalog==0 tests for category directory or file + * nls!=0 enables NLSPATH+LANG hack (not implemented yet) + */ + +char* +mcfind(char* path, const char* locale, const char* catalog, int category, int nls) +{ + register int c; + register char* s; + register char* e; + register char* p; + register const char* v; + int i; + int first; + int next; + int last; + int oerrno; + Lc_t* lc; + char file[PATH_MAX]; + char* paths[5]; + + static char lc_messages[] = "LC_MESSAGES"; + + if ((category = lcindex(category, 1)) < 0) + return 0; + if (!(lc = locale ? lcmake(locale) : locales[category])) + return 0; + oerrno = errno; + if (catalog && *catalog == '/') + { + i = eaccess(catalog, R_OK); + errno = oerrno; + if (i) + return 0; + strncpy(path, catalog, PATH_MAX-1); + return path; + } + i = 0; +#if !_lib_catopen + if ((p = getenv("NLSPATH")) && *p) + paths[i++] = p; +#endif + paths[i++] = "share/lib/locale/%l/%C/%N"; + paths[i++] = "share/locale/%l/%C/%N"; + paths[i++] = "lib/locale/%l/%C/%N"; + paths[i] = 0; + next = 1; + for (i = 0; p = paths[i]; i += next) + { + first = 1; + last = 0; + e = &file[elementsof(file) - 1]; + while (*p) + { + s = file; + for (;;) + { + switch (c = *p++) + { + case 0: + p--; + break; + case ':': + break; + case '%': + if (s < e) + { + switch (c = *p++) + { + case 0: + p--; + continue; + case 'N': + v = catalog; + break; + case 'L': + if (first) + { + first = 0; + if (next) + { + v = lc->code; + if (lc->code != lc->language->code) + next = 0; + } + else + { + next = 1; + v = lc->language->code; + } + } + break; + case 'l': + v = lc->language->code; + break; + case 't': + v = lc->territory->code; + break; + case 'c': + v = lc->charset->code; + break; + case 'C': + case_C: + if (!catalog) + last = 1; + v = categories[category].name; + break; + default: + *s++ = c; + continue; + } + if (v) + while (*v && s < e) + *s++ = *v++; + } + continue; + case '/': + if (last) + break; + if (category != AST_LC_MESSAGES && strneq(p, lc_messages, sizeof(lc_messages) - 1) && p[sizeof(lc_messages)-1] == '/') + { + p += sizeof(lc_messages) - 1; + goto case_C; + } + /*FALLTHROUGH*/ + default: + if (s < e) + *s++ = c; + continue; + } + break; + } + if (s > file) + *s = 0; + else if (!catalog) + continue; + else + strncpy(file, catalog, elementsof(file)); + if (ast.locale.set & AST_LC_find) + sfprintf(sfstderr, "locale find %s\n", file); + if (s = pathpath(path, file, "", (!catalog && category == AST_LC_MESSAGES) ? PATH_READ : (PATH_REGULAR|PATH_READ|PATH_ABSOLUTE))) + { + if (ast.locale.set & (AST_LC_find|AST_LC_setlocale)) + sfprintf(sfstderr, "locale path %s\n", s); + errno = oerrno; + return s; + } + } + } + errno = oerrno; + return 0; +} + +/* + * allocate and read the binary message catalog ip + * if ip==0 then space is allocated for mcput() + * 0 returned on any error + */ + +Mc_t* +mcopen(register Sfio_t* ip) +{ + register Mc_t* mc; + register char** mp; + register char* sp; + Vmalloc_t* vm; + char* rp; + int i; + int j; + int oerrno; + size_t n; + char buf[MC_MAGIC_SIZE]; + + oerrno = errno; + if (ip) + { + /* + * check the magic + */ + + if (sfread(ip, buf, MC_MAGIC_SIZE) != MC_MAGIC_SIZE) + { + errno = oerrno; + return 0; + } + if (memcmp(buf, MC_MAGIC, MC_MAGIC_SIZE)) + return 0; + } + + /* + * allocate the region + */ + + if (!(vm = vmopen(Vmdcheap, Vmbest, 0)) || !(mc = vmnewof(vm, 0, Mc_t, 1, 0))) + { + errno = oerrno; + return 0; + } + mc->vm = vm; + mc->cvt = (iconv_t)(-1); + if (ip) + { + /* + * read the translation record + */ + + if (!(sp = sfgetr(ip, 0, 0)) || !(mc->translation = vmstrdup(vm, sp))) + goto bad; + + /* + * read the optional header records + */ + + do + { + if (!(sp = sfgetr(ip, 0, 0))) + goto bad; + } while (*sp); + + /* + * get the component dimensions + */ + + mc->nstrs = sfgetu(ip); + mc->nmsgs = sfgetu(ip); + mc->num = sfgetu(ip); + if (sfeof(ip)) + goto bad; + } + else if (!(mc->translation = vmnewof(vm, 0, char, 1, 0))) + goto bad; + + /* + * allocate the remaining space + */ + + if (!(mc->set = vmnewof(vm, 0, Mcset_t, mc->num + 1, 0))) + goto bad; + if (!ip) + return mc; + if (!(mp = vmnewof(vm, 0, char*, mc->nmsgs + mc->num + 1, 0))) + goto bad; + if (!(rp = sp = vmalloc(vm, mc->nstrs + 1))) + goto bad; + + /* + * get the set dimensions and initialize the msg pointers + */ + + while (i = sfgetu(ip)) + { + if (i > mc->num) + goto bad; + n = sfgetu(ip); + mc->set[i].num = n; + mc->set[i].msg = mp; + mp += n + 1; + } + + /* + * read the msg sizes and set up the msg pointers + */ + + for (i = 1; i <= mc->num; i++) + for (j = 1; j <= mc->set[i].num; j++) + if (n = sfgetu(ip)) + { + mc->set[i].msg[j] = sp; + sp += n; + } + + /* + * read the string table + */ + + if (sfread(ip, rp, mc->nstrs) != mc->nstrs || sfgetc(ip) != EOF) + goto bad; + if (!(mc->tmp = sfstropen())) + goto bad; + mc->cvt = iconv_open("", "utf"); + errno = oerrno; + return mc; + bad: + vmclose(vm); + errno = oerrno; + return 0; +} + +/* + * return the <set,num> message in mc + * msg returned on error + * utf message text converted to ucs + */ + +char* +mcget(register Mc_t* mc, int set, int num, const char* msg) +{ + char* s; + size_t n; + int p; + + if (!mc || set < 0 || set > mc->num || num < 1 || num > mc->set[set].num || !(s = mc->set[set].msg[num])) + return (char*)msg; + if (mc->cvt == (iconv_t)(-1)) + return s; + if ((p = sfstrtell(mc->tmp)) > sfstrsize(mc->tmp) / 2) + { + p = 0; + sfstrseek(mc->tmp, p, SEEK_SET); + } + n = strlen(s) + 1; + iconv_write(mc->cvt, mc->tmp, &s, &n, NiL); + return sfstrbase(mc->tmp) + p; +} + +/* + * set message <set,num> to msg + * msg==0 deletes the message + * the message and set counts are adjusted + * 0 returned on success, -1 otherwise + */ + +int +mcput(register Mc_t* mc, int set, int num, const char* msg) +{ + register int i; + register char* s; + register Mcset_t* sp; + register char** mp; + + /* + * validate the arguments + */ + + if (!mc || set > MC_SET_MAX || num > MC_NUM_MAX) + return -1; + + /* + * deletions don't kick in allocations (duh) + */ + + if (!msg) + { + if (set <= mc->num && num <= mc->set[set].num && (s = mc->set[set].msg[num])) + { + /* + * decrease the string table size + */ + + mc->set[set].msg[num] = 0; + mc->nstrs -= strlen(s) + 1; + if (mc->set[set].num == num) + { + /* + * decrease the max msg num + */ + + mp = mc->set[set].msg + num; + while (num && !mp[--num]); + mc->nmsgs -= mc->set[set].num - num; + if (!(mc->set[set].num = num) && mc->num == set) + { + /* + * decrease the max set num + */ + + while (num && !mc->set[--num].num); + mc->num = num; + } + } + } + return 0; + } + + /* + * keep track of the highest set and allocate if necessary + */ + + if (set > mc->num) + { + if (set > mc->gen) + { + i = MC_SET_MAX; + if (!(sp = vmnewof(mc->vm, 0, Mcset_t, i + 1, 0))) + return -1; + mc->gen = i; + for (i = 1; i <= mc->num; i++) + sp[i] = mc->set[i]; + mc->set = sp; + } + mc->num = set; + } + sp = mc->set + set; + + /* + * keep track of the highest msg and allocate if necessary + */ + + if (num > sp->num) + { + if (num > sp->gen) + { + if (!mc->gen) + { + i = (MC_NUM_MAX + 1) / 32; + if (i <= num) + i = 2 * num; + if (i > MC_NUM_MAX) + i = MC_NUM_MAX; + if (!(mp = vmnewof(mc->vm, 0, char*, i + 1, 0))) + return -1; + mc->gen = i; + sp->msg = mp; + for (i = 1; i <= sp->num; i++) + mp[i] = sp->msg[i]; + } + else + { + i = 2 * mc->gen; + if (i > MC_NUM_MAX) + i = MC_NUM_MAX; + if (!(mp = vmnewof(mc->vm, sp->msg, char*, i + 1, 0))) + return -1; + sp->gen = i; + sp->msg = mp; + } + } + mc->nmsgs += num - sp->num; + sp->num = num; + } + + /* + * decrease the string table size + */ + + if (s = sp->msg[num]) + { + /* + * no-op if no change + */ + + if (streq(s, msg)) + return 0; + mc->nstrs -= strlen(s) + 1; + } + + /* + * allocate, add and adjust the string table size + */ + + if (!(s = vmstrdup(mc->vm, msg))) + return -1; + sp->msg[num] = s; + mc->nstrs += strlen(s) + 1; + return 0; +} + +/* + * dump message catalog mc to op + * 0 returned on success, -1 otherwise + */ + +int +mcdump(register Mc_t* mc, register Sfio_t* op) +{ + register int i; + register int j; + register int n; + register char* s; + register Mcset_t* sp; + + /* + * write the magic + */ + + if (sfwrite(op, MC_MAGIC, MC_MAGIC_SIZE) != MC_MAGIC_SIZE) + return -1; + + /* + * write the translation record + */ + + sfputr(op, mc->translation, 0); + + /* optional header records here */ + + /* + * end of optional header records + */ + + sfputu(op, 0); + + /* + * write the global dimensions + */ + + sfputu(op, mc->nstrs); + sfputu(op, mc->nmsgs); + sfputu(op, mc->num); + + /* + * write the set dimensions + */ + + for (i = 1; i <= mc->num; i++) + if (mc->set[i].num) + { + sfputu(op, i); + sfputu(op, mc->set[i].num); + } + sfputu(op, 0); + + /* + * write the message sizes + */ + + for (i = 1; i <= mc->num; i++) + if (mc->set[i].num) + { + sp = mc->set + i; + for (j = 1; j <= sp->num; j++) + { + n = (s = sp->msg[j]) ? (strlen(s) + 1) : 0; + sfputu(op, n); + } + } + + /* + * write the string table + */ + + for (i = 1; i <= mc->num; i++) + if (mc->set[i].num) + { + sp = mc->set + i; + for (j = 1; j <= sp->num; j++) + if (s = sp->msg[j]) + sfputr(op, s, 0); + } + + /* + * sync and return + */ + + return sfsync(op); +} + +/* + * parse <set,msg> number from s + * e!=0 is set to the next char after the parse + * set!=0 is set to message set number + * msg!=0 is set to message number + * the message set number is returned + * + * the base 36 hash gives reasonable values for these: + * + * "ast" : ((((36#a^36#s^36#t)-9)&63)+1) = 3 + * "gnu" : ((((36#g^36#n^36#u)-9)&63)+1) = 17 + * "sgi" : ((((36#s^36#g^36#i)-9)&63)+1) = 22 + * "sun" : ((((36#s^36#u^36#n)-9)&63)+1) = 13 + */ + +int +mcindex(register const char* s, char** e, int* set, int* msg) +{ + register int c; + register int m; + register int n; + register int r; + register unsigned char* cv; + char* t; + + m = 0; + n = strtol(s, &t, 0); + if (t == (char*)s) + { + SFCVINIT(); + cv = _Sfcv36; + for (n = m = 0; (c = cv[*s]) < 36; s++) + { + m++; + n ^= c; + } + m = (m <= 3) ? 63 : ((1 << (m + 3)) - 1); + n = ((n - 9) & m) + 1; + } + else + s = (const char*)t; + r = n; + if (*s) + m = strtol(s + 1, e, 0); + else + { + if (e) + *e = (char*)s; + if (m) + m = 0; + else + { + m = n; + n = 1; + } + } + if (set) + *set = n; + if (msg) + *msg = m; + return r; +} + +/* + * close the message catalog mc + */ + +int +mcclose(register Mc_t* mc) +{ + if (!mc) + return -1; + if (mc->tmp) + sfclose(mc->tmp); + if (mc->cvt != (iconv_t)(-1)) + iconv_close(mc->cvt); + vmclose(mc->vm); + return 0; +} |