14887Schin /***********************************************************************
24887Schin * *
34887Schin * This software is part of the ast package *
4*12068SRoger.Faulkner@Oracle.COM * Copyright (c) 1985-2010 AT&T Intellectual Property *
54887Schin * and is licensed under the *
64887Schin * Common Public License, Version 1.0 *
78462SApril.Chin@Sun.COM * by AT&T Intellectual Property *
84887Schin * *
94887Schin * A copy of the License is available at *
104887Schin * http://www.opensource.org/licenses/cpl1.0.txt *
114887Schin * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
124887Schin * *
134887Schin * Information and Software Systems Research *
144887Schin * AT&T Research *
154887Schin * Florham Park NJ *
164887Schin * *
174887Schin * Glenn Fowler <gsf@research.att.com> *
184887Schin * David Korn <dgk@research.att.com> *
194887Schin * Phong Vo <kpv@research.att.com> *
204887Schin * *
214887Schin ***********************************************************************/
224887Schin #pragma prototyped
234887Schin
244887Schin /*
254887Schin * Glenn Fowler
264887Schin * AT&T Research
274887Schin *
284887Schin * machine independent binary message catalog implementation
294887Schin */
304887Schin
314887Schin #include "sfhdr.h"
324887Schin #include "lclib.h"
334887Schin
344887Schin #include <iconv.h>
354887Schin
364887Schin #define _MC_PRIVATE_ \
374887Schin size_t nstrs; \
384887Schin size_t nmsgs; \
394887Schin iconv_t cvt; \
404887Schin Sfio_t* tmp; \
414887Schin Vmalloc_t* vm;
424887Schin
434887Schin #include <vmalloc.h>
444887Schin #include <error.h>
454887Schin #include <mc.h>
464887Schin #include <nl_types.h>
474887Schin
484887Schin /*
494887Schin * find the binary message catalog path for <locale,catalog>
504887Schin * result placed in path of size PATH_MAX
514887Schin * pointer to path returned
524887Schin * catalog==0 tests for category directory or file
534887Schin * nls!=0 enables NLSPATH+LANG hack (not implemented yet)
544887Schin */
554887Schin
564887Schin char*
mcfind(char * path,const char * locale,const char * catalog,int category,int nls)574887Schin mcfind(char* path, const char* locale, const char* catalog, int category, int nls)
584887Schin {
594887Schin register int c;
604887Schin register char* s;
614887Schin register char* e;
624887Schin register char* p;
634887Schin register const char* v;
644887Schin int i;
654887Schin int first;
664887Schin int next;
674887Schin int last;
684887Schin int oerrno;
694887Schin Lc_t* lc;
704887Schin char file[PATH_MAX];
714887Schin char* paths[5];
724887Schin
734887Schin static char lc_messages[] = "LC_MESSAGES";
744887Schin
754887Schin if ((category = lcindex(category, 1)) < 0)
764887Schin return 0;
774887Schin if (!(lc = locale ? lcmake(locale) : locales[category]))
784887Schin return 0;
794887Schin oerrno = errno;
804887Schin if (catalog && *catalog == '/')
814887Schin {
824887Schin i = eaccess(catalog, R_OK);
834887Schin errno = oerrno;
844887Schin if (i)
854887Schin return 0;
864887Schin strncpy(path, catalog, PATH_MAX-1);
874887Schin return path;
884887Schin }
894887Schin i = 0;
904887Schin #if !_lib_catopen
914887Schin if ((p = getenv("NLSPATH")) && *p)
924887Schin paths[i++] = p;
934887Schin #endif
944887Schin paths[i++] = "share/lib/locale/%l/%C/%N";
954887Schin paths[i++] = "share/locale/%l/%C/%N";
964887Schin paths[i++] = "lib/locale/%l/%C/%N";
974887Schin paths[i] = 0;
984887Schin next = 1;
994887Schin for (i = 0; p = paths[i]; i += next)
1004887Schin {
1014887Schin first = 1;
1024887Schin last = 0;
1034887Schin e = &file[elementsof(file) - 1];
1044887Schin while (*p)
1054887Schin {
1064887Schin s = file;
1074887Schin for (;;)
1084887Schin {
1094887Schin switch (c = *p++)
1104887Schin {
1114887Schin case 0:
1124887Schin p--;
1134887Schin break;
1144887Schin case ':':
1154887Schin break;
1164887Schin case '%':
1174887Schin if (s < e)
1184887Schin {
1194887Schin switch (c = *p++)
1204887Schin {
1214887Schin case 0:
1224887Schin p--;
1234887Schin continue;
1244887Schin case 'N':
1254887Schin v = catalog;
1264887Schin break;
1274887Schin case 'L':
1284887Schin if (first)
1294887Schin {
1304887Schin first = 0;
1314887Schin if (next)
1324887Schin {
1334887Schin v = lc->code;
1344887Schin if (lc->code != lc->language->code)
1354887Schin next = 0;
1364887Schin }
1374887Schin else
1384887Schin {
1394887Schin next = 1;
1404887Schin v = lc->language->code;
1414887Schin }
1424887Schin }
1434887Schin break;
1444887Schin case 'l':
1454887Schin v = lc->language->code;
1464887Schin break;
1474887Schin case 't':
1484887Schin v = lc->territory->code;
1494887Schin break;
1504887Schin case 'c':
1514887Schin v = lc->charset->code;
1524887Schin break;
1534887Schin case 'C':
1544887Schin case_C:
1554887Schin if (!catalog)
1564887Schin last = 1;
1578462SApril.Chin@Sun.COM v = lc_categories[category].name;
1584887Schin break;
1594887Schin default:
1604887Schin *s++ = c;
1614887Schin continue;
1624887Schin }
1634887Schin if (v)
1644887Schin while (*v && s < e)
1654887Schin *s++ = *v++;
1664887Schin }
1674887Schin continue;
1684887Schin case '/':
1694887Schin if (last)
1704887Schin break;
1714887Schin if (category != AST_LC_MESSAGES && strneq(p, lc_messages, sizeof(lc_messages) - 1) && p[sizeof(lc_messages)-1] == '/')
1724887Schin {
1734887Schin p += sizeof(lc_messages) - 1;
1744887Schin goto case_C;
1754887Schin }
1764887Schin /*FALLTHROUGH*/
1774887Schin default:
1784887Schin if (s < e)
1794887Schin *s++ = c;
1804887Schin continue;
1814887Schin }
1824887Schin break;
1834887Schin }
1844887Schin if (s > file)
1854887Schin *s = 0;
1864887Schin else if (!catalog)
1874887Schin continue;
1884887Schin else
1894887Schin strncpy(file, catalog, elementsof(file));
1904887Schin if (ast.locale.set & AST_LC_find)
1914887Schin sfprintf(sfstderr, "locale find %s\n", file);
1924887Schin if (s = pathpath(path, file, "", (!catalog && category == AST_LC_MESSAGES) ? PATH_READ : (PATH_REGULAR|PATH_READ|PATH_ABSOLUTE)))
1934887Schin {
1944887Schin if (ast.locale.set & (AST_LC_find|AST_LC_setlocale))
1954887Schin sfprintf(sfstderr, "locale path %s\n", s);
1964887Schin errno = oerrno;
1974887Schin return s;
1984887Schin }
1994887Schin }
2004887Schin }
2014887Schin errno = oerrno;
2024887Schin return 0;
2034887Schin }
2044887Schin
2054887Schin /*
2064887Schin * allocate and read the binary message catalog ip
2074887Schin * if ip==0 then space is allocated for mcput()
2084887Schin * 0 returned on any error
2094887Schin */
2104887Schin
2114887Schin Mc_t*
mcopen(register Sfio_t * ip)2124887Schin mcopen(register Sfio_t* ip)
2134887Schin {
2144887Schin register Mc_t* mc;
2154887Schin register char** mp;
2164887Schin register char* sp;
2174887Schin Vmalloc_t* vm;
2184887Schin char* rp;
2194887Schin int i;
2204887Schin int j;
2214887Schin int oerrno;
2224887Schin size_t n;
2234887Schin char buf[MC_MAGIC_SIZE];
2244887Schin
2254887Schin oerrno = errno;
2264887Schin if (ip)
2274887Schin {
2284887Schin /*
2294887Schin * check the magic
2304887Schin */
2314887Schin
2324887Schin if (sfread(ip, buf, MC_MAGIC_SIZE) != MC_MAGIC_SIZE)
2334887Schin {
2344887Schin errno = oerrno;
2354887Schin return 0;
2364887Schin }
2374887Schin if (memcmp(buf, MC_MAGIC, MC_MAGIC_SIZE))
2384887Schin return 0;
2394887Schin }
2404887Schin
2414887Schin /*
2424887Schin * allocate the region
2434887Schin */
2444887Schin
2454887Schin if (!(vm = vmopen(Vmdcheap, Vmbest, 0)) || !(mc = vmnewof(vm, 0, Mc_t, 1, 0)))
2464887Schin {
2474887Schin errno = oerrno;
2484887Schin return 0;
2494887Schin }
2504887Schin mc->vm = vm;
2514887Schin mc->cvt = (iconv_t)(-1);
2524887Schin if (ip)
2534887Schin {
2544887Schin /*
2554887Schin * read the translation record
2564887Schin */
2574887Schin
2584887Schin if (!(sp = sfgetr(ip, 0, 0)) || !(mc->translation = vmstrdup(vm, sp)))
2594887Schin goto bad;
2604887Schin
2614887Schin /*
2624887Schin * read the optional header records
2634887Schin */
2644887Schin
2654887Schin do
2664887Schin {
2674887Schin if (!(sp = sfgetr(ip, 0, 0)))
2684887Schin goto bad;
2694887Schin } while (*sp);
2704887Schin
2714887Schin /*
2724887Schin * get the component dimensions
2734887Schin */
2744887Schin
2754887Schin mc->nstrs = sfgetu(ip);
2764887Schin mc->nmsgs = sfgetu(ip);
2774887Schin mc->num = sfgetu(ip);
2784887Schin if (sfeof(ip))
2794887Schin goto bad;
2804887Schin }
2814887Schin else if (!(mc->translation = vmnewof(vm, 0, char, 1, 0)))
2824887Schin goto bad;
2834887Schin
2844887Schin /*
2854887Schin * allocate the remaining space
2864887Schin */
2874887Schin
2884887Schin if (!(mc->set = vmnewof(vm, 0, Mcset_t, mc->num + 1, 0)))
2894887Schin goto bad;
2904887Schin if (!ip)
2914887Schin return mc;
2924887Schin if (!(mp = vmnewof(vm, 0, char*, mc->nmsgs + mc->num + 1, 0)))
2934887Schin goto bad;
2944887Schin if (!(rp = sp = vmalloc(vm, mc->nstrs + 1)))
2954887Schin goto bad;
2964887Schin
2974887Schin /*
2984887Schin * get the set dimensions and initialize the msg pointers
2994887Schin */
3004887Schin
3014887Schin while (i = sfgetu(ip))
3024887Schin {
3034887Schin if (i > mc->num)
3044887Schin goto bad;
3054887Schin n = sfgetu(ip);
3064887Schin mc->set[i].num = n;
3074887Schin mc->set[i].msg = mp;
3084887Schin mp += n + 1;
3094887Schin }
3104887Schin
3114887Schin /*
3124887Schin * read the msg sizes and set up the msg pointers
3134887Schin */
3144887Schin
3154887Schin for (i = 1; i <= mc->num; i++)
3164887Schin for (j = 1; j <= mc->set[i].num; j++)
3174887Schin if (n = sfgetu(ip))
3184887Schin {
3194887Schin mc->set[i].msg[j] = sp;
3204887Schin sp += n;
3214887Schin }
3224887Schin
3234887Schin /*
3244887Schin * read the string table
3254887Schin */
3264887Schin
3274887Schin if (sfread(ip, rp, mc->nstrs) != mc->nstrs || sfgetc(ip) != EOF)
3284887Schin goto bad;
3294887Schin if (!(mc->tmp = sfstropen()))
3304887Schin goto bad;
3314887Schin mc->cvt = iconv_open("", "utf");
3324887Schin errno = oerrno;
3334887Schin return mc;
3344887Schin bad:
3354887Schin vmclose(vm);
3364887Schin errno = oerrno;
3374887Schin return 0;
3384887Schin }
3394887Schin
3404887Schin /*
3414887Schin * return the <set,num> message in mc
3424887Schin * msg returned on error
3434887Schin * utf message text converted to ucs
3444887Schin */
3454887Schin
3464887Schin char*
mcget(register Mc_t * mc,int set,int num,const char * msg)3474887Schin mcget(register Mc_t* mc, int set, int num, const char* msg)
3484887Schin {
3494887Schin char* s;
3504887Schin size_t n;
3514887Schin int p;
3524887Schin
3534887Schin if (!mc || set < 0 || set > mc->num || num < 1 || num > mc->set[set].num || !(s = mc->set[set].msg[num]))
3544887Schin return (char*)msg;
3554887Schin if (mc->cvt == (iconv_t)(-1))
3564887Schin return s;
3574887Schin if ((p = sfstrtell(mc->tmp)) > sfstrsize(mc->tmp) / 2)
3584887Schin {
3594887Schin p = 0;
3604887Schin sfstrseek(mc->tmp, p, SEEK_SET);
3614887Schin }
3624887Schin n = strlen(s) + 1;
3634887Schin iconv_write(mc->cvt, mc->tmp, &s, &n, NiL);
3644887Schin return sfstrbase(mc->tmp) + p;
3654887Schin }
3664887Schin
3674887Schin /*
3684887Schin * set message <set,num> to msg
3694887Schin * msg==0 deletes the message
3704887Schin * the message and set counts are adjusted
3714887Schin * 0 returned on success, -1 otherwise
3724887Schin */
3734887Schin
3744887Schin int
mcput(register Mc_t * mc,int set,int num,const char * msg)3754887Schin mcput(register Mc_t* mc, int set, int num, const char* msg)
3764887Schin {
3774887Schin register int i;
3784887Schin register char* s;
3794887Schin register Mcset_t* sp;
3804887Schin register char** mp;
3814887Schin
3824887Schin /*
3834887Schin * validate the arguments
3844887Schin */
3854887Schin
3864887Schin if (!mc || set > MC_SET_MAX || num > MC_NUM_MAX)
3874887Schin return -1;
3884887Schin
3894887Schin /*
3904887Schin * deletions don't kick in allocations (duh)
3914887Schin */
3924887Schin
3934887Schin if (!msg)
3944887Schin {
3954887Schin if (set <= mc->num && num <= mc->set[set].num && (s = mc->set[set].msg[num]))
3964887Schin {
3974887Schin /*
3984887Schin * decrease the string table size
3994887Schin */
4004887Schin
4014887Schin mc->set[set].msg[num] = 0;
4024887Schin mc->nstrs -= strlen(s) + 1;
4034887Schin if (mc->set[set].num == num)
4044887Schin {
4054887Schin /*
4064887Schin * decrease the max msg num
4074887Schin */
4084887Schin
4094887Schin mp = mc->set[set].msg + num;
4104887Schin while (num && !mp[--num]);
4114887Schin mc->nmsgs -= mc->set[set].num - num;
4124887Schin if (!(mc->set[set].num = num) && mc->num == set)
4134887Schin {
4144887Schin /*
4154887Schin * decrease the max set num
4164887Schin */
4174887Schin
4184887Schin while (num && !mc->set[--num].num);
4194887Schin mc->num = num;
4204887Schin }
4214887Schin }
4224887Schin }
4234887Schin return 0;
4244887Schin }
4254887Schin
4264887Schin /*
4274887Schin * keep track of the highest set and allocate if necessary
4284887Schin */
4294887Schin
4304887Schin if (set > mc->num)
4314887Schin {
4324887Schin if (set > mc->gen)
4334887Schin {
4344887Schin i = MC_SET_MAX;
4354887Schin if (!(sp = vmnewof(mc->vm, 0, Mcset_t, i + 1, 0)))
4364887Schin return -1;
4374887Schin mc->gen = i;
4384887Schin for (i = 1; i <= mc->num; i++)
4394887Schin sp[i] = mc->set[i];
4404887Schin mc->set = sp;
4414887Schin }
4424887Schin mc->num = set;
4434887Schin }
4444887Schin sp = mc->set + set;
4454887Schin
4464887Schin /*
4474887Schin * keep track of the highest msg and allocate if necessary
4484887Schin */
4494887Schin
4504887Schin if (num > sp->num)
4514887Schin {
4524887Schin if (num > sp->gen)
4534887Schin {
4544887Schin if (!mc->gen)
4554887Schin {
4564887Schin i = (MC_NUM_MAX + 1) / 32;
4574887Schin if (i <= num)
4584887Schin i = 2 * num;
4594887Schin if (i > MC_NUM_MAX)
4604887Schin i = MC_NUM_MAX;
4614887Schin if (!(mp = vmnewof(mc->vm, 0, char*, i + 1, 0)))
4624887Schin return -1;
4634887Schin mc->gen = i;
4644887Schin sp->msg = mp;
4654887Schin for (i = 1; i <= sp->num; i++)
4664887Schin mp[i] = sp->msg[i];
4674887Schin }
4684887Schin else
4694887Schin {
4704887Schin i = 2 * mc->gen;
4714887Schin if (i > MC_NUM_MAX)
4724887Schin i = MC_NUM_MAX;
4734887Schin if (!(mp = vmnewof(mc->vm, sp->msg, char*, i + 1, 0)))
4744887Schin return -1;
4754887Schin sp->gen = i;
4764887Schin sp->msg = mp;
4774887Schin }
4784887Schin }
4794887Schin mc->nmsgs += num - sp->num;
4804887Schin sp->num = num;
4814887Schin }
4824887Schin
4834887Schin /*
4844887Schin * decrease the string table size
4854887Schin */
4864887Schin
4874887Schin if (s = sp->msg[num])
4884887Schin {
4894887Schin /*
4904887Schin * no-op if no change
4914887Schin */
4924887Schin
4934887Schin if (streq(s, msg))
4944887Schin return 0;
4954887Schin mc->nstrs -= strlen(s) + 1;
4964887Schin }
4974887Schin
4984887Schin /*
4994887Schin * allocate, add and adjust the string table size
5004887Schin */
5014887Schin
5024887Schin if (!(s = vmstrdup(mc->vm, msg)))
5034887Schin return -1;
5044887Schin sp->msg[num] = s;
5054887Schin mc->nstrs += strlen(s) + 1;
5064887Schin return 0;
5074887Schin }
5084887Schin
5094887Schin /*
5104887Schin * dump message catalog mc to op
5114887Schin * 0 returned on success, -1 otherwise
5124887Schin */
5134887Schin
5144887Schin int
mcdump(register Mc_t * mc,register Sfio_t * op)5154887Schin mcdump(register Mc_t* mc, register Sfio_t* op)
5164887Schin {
5174887Schin register int i;
5184887Schin register int j;
5194887Schin register int n;
5204887Schin register char* s;
5214887Schin register Mcset_t* sp;
5224887Schin
5234887Schin /*
5244887Schin * write the magic
5254887Schin */
5264887Schin
5274887Schin if (sfwrite(op, MC_MAGIC, MC_MAGIC_SIZE) != MC_MAGIC_SIZE)
5284887Schin return -1;
5294887Schin
5304887Schin /*
5314887Schin * write the translation record
5324887Schin */
5334887Schin
5344887Schin sfputr(op, mc->translation, 0);
5354887Schin
5364887Schin /* optional header records here */
5374887Schin
5384887Schin /*
5394887Schin * end of optional header records
5404887Schin */
5414887Schin
5424887Schin sfputu(op, 0);
5434887Schin
5444887Schin /*
5454887Schin * write the global dimensions
5464887Schin */
5474887Schin
5484887Schin sfputu(op, mc->nstrs);
5494887Schin sfputu(op, mc->nmsgs);
5504887Schin sfputu(op, mc->num);
5514887Schin
5524887Schin /*
5534887Schin * write the set dimensions
5544887Schin */
5554887Schin
5564887Schin for (i = 1; i <= mc->num; i++)
5574887Schin if (mc->set[i].num)
5584887Schin {
5594887Schin sfputu(op, i);
5604887Schin sfputu(op, mc->set[i].num);
5614887Schin }
5624887Schin sfputu(op, 0);
5634887Schin
5644887Schin /*
5654887Schin * write the message sizes
5664887Schin */
5674887Schin
5684887Schin for (i = 1; i <= mc->num; i++)
5694887Schin if (mc->set[i].num)
5704887Schin {
5714887Schin sp = mc->set + i;
5724887Schin for (j = 1; j <= sp->num; j++)
5734887Schin {
5744887Schin n = (s = sp->msg[j]) ? (strlen(s) + 1) : 0;
5754887Schin sfputu(op, n);
5764887Schin }
5774887Schin }
5784887Schin
5794887Schin /*
5804887Schin * write the string table
5814887Schin */
5824887Schin
5834887Schin for (i = 1; i <= mc->num; i++)
5844887Schin if (mc->set[i].num)
5854887Schin {
5864887Schin sp = mc->set + i;
5874887Schin for (j = 1; j <= sp->num; j++)
5884887Schin if (s = sp->msg[j])
5894887Schin sfputr(op, s, 0);
5904887Schin }
5914887Schin
5924887Schin /*
5934887Schin * sync and return
5944887Schin */
5954887Schin
5964887Schin return sfsync(op);
5974887Schin }
5984887Schin
5994887Schin /*
6004887Schin * parse <set,msg> number from s
6014887Schin * e!=0 is set to the next char after the parse
6024887Schin * set!=0 is set to message set number
6034887Schin * msg!=0 is set to message number
6044887Schin * the message set number is returned
6054887Schin *
6064887Schin * the base 36 hash gives reasonable values for these:
6074887Schin *
6084887Schin * "ast" : ((((36#a^36#s^36#t)-9)&63)+1) = 3
6094887Schin * "gnu" : ((((36#g^36#n^36#u)-9)&63)+1) = 17
6104887Schin * "sgi" : ((((36#s^36#g^36#i)-9)&63)+1) = 22
6114887Schin * "sun" : ((((36#s^36#u^36#n)-9)&63)+1) = 13
6124887Schin */
6134887Schin
6144887Schin int
mcindex(register const char * s,char ** e,int * set,int * msg)6154887Schin mcindex(register const char* s, char** e, int* set, int* msg)
6164887Schin {
6174887Schin register int c;
6184887Schin register int m;
6194887Schin register int n;
6204887Schin register int r;
6214887Schin register unsigned char* cv;
6224887Schin char* t;
6234887Schin
6244887Schin m = 0;
6254887Schin n = strtol(s, &t, 0);
6264887Schin if (t == (char*)s)
6274887Schin {
6284887Schin SFCVINIT();
6294887Schin cv = _Sfcv36;
6304887Schin for (n = m = 0; (c = cv[*s]) < 36; s++)
6314887Schin {
6324887Schin m++;
6334887Schin n ^= c;
6344887Schin }
6354887Schin m = (m <= 3) ? 63 : ((1 << (m + 3)) - 1);
6364887Schin n = ((n - 9) & m) + 1;
6374887Schin }
6384887Schin else
6394887Schin s = (const char*)t;
6404887Schin r = n;
6414887Schin if (*s)
6424887Schin m = strtol(s + 1, e, 0);
6434887Schin else
6444887Schin {
6454887Schin if (e)
6464887Schin *e = (char*)s;
6474887Schin if (m)
6484887Schin m = 0;
6494887Schin else
6504887Schin {
6514887Schin m = n;
6524887Schin n = 1;
6534887Schin }
6544887Schin }
6554887Schin if (set)
6564887Schin *set = n;
6574887Schin if (msg)
6584887Schin *msg = m;
6594887Schin return r;
6604887Schin }
6614887Schin
6624887Schin /*
6634887Schin * close the message catalog mc
6644887Schin */
6654887Schin
6664887Schin int
mcclose(register Mc_t * mc)6674887Schin mcclose(register Mc_t* mc)
6684887Schin {
6694887Schin if (!mc)
6704887Schin return -1;
6714887Schin if (mc->tmp)
6724887Schin sfclose(mc->tmp);
6734887Schin if (mc->cvt != (iconv_t)(-1))
6744887Schin iconv_close(mc->cvt);
6754887Schin vmclose(mc->vm);
6764887Schin return 0;
6774887Schin }
678