14887Schin /*********************************************************************** 24887Schin * * 34887Schin * This software is part of the ast package * 4*8462SApril.Chin@Sun.COM * Copyright (c) 1985-2008 AT&T Intellectual Property * 54887Schin * and is licensed under the * 64887Schin * Common Public License, Version 1.0 * 7*8462SApril.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* 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; 157*8462SApril.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* 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* 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 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 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 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 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