summaryrefslogtreecommitdiff
path: root/usr/src/lib/libast/common/port/mc.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib/libast/common/port/mc.c')
-rw-r--r--usr/src/lib/libast/common/port/mc.c677
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;
+}