1 /***********************************************************************
2 * *
3 * This software is part of the ast package *
4 * Copyright (c) 1985-2011 AT&T Intellectual Property *
5 * and is licensed under the *
6 * Eclipse Public License, Version 1.0 *
7 * by AT&T Intellectual Property *
8 * *
9 * A copy of the License is available at *
10 * http://www.eclipse.org/org/documents/epl-v10.html *
11 * (with md5 checksum b35adb5213ca9657e911e9befb180842) *
12 * *
13 * Information and Software Systems Research *
14 * AT&T Research *
15 * Florham Park NJ *
16 * *
17 * Glenn Fowler <gsf@research.att.com> *
18 * David Korn <dgk@research.att.com> *
19 * Phong Vo <kpv@research.att.com> *
20 * *
21 ***********************************************************************/
22 #pragma prototyped
23
24 /*
25 * Glenn Fowler
26 * AT&T Research
27 *
28 * machine independent binary message catalog implementation
29 */
30
31 #include "sfhdr.h"
32 #include "lclib.h"
33
34 #include <iconv.h>
35
36 #define _MC_PRIVATE_ \
37 size_t nstrs; \
38 size_t nmsgs; \
39 iconv_t cvt; \
40 Sfio_t* tmp; \
41 Vmalloc_t* vm;
42
43 #include <vmalloc.h>
44 #include <error.h>
45 #include <mc.h>
46 #include <nl_types.h>
47
48 /*
49 * find the binary message catalog path for <locale,catalog>
50 * result placed in path of size PATH_MAX
51 * pointer to path returned
52 * catalog==0 tests for category directory or file
53 * nls!=0 enables NLSPATH+LANG hack (not implemented yet)
54 */
55
56 char*
mcfind(const char * locale,const char * catalog,int category,int nls,char * path,size_t size)57 mcfind(const char* locale, const char* catalog, int category, int nls, char* path, size_t size)
58 {
59 register int c;
60 register char* s;
61 register char* e;
62 register char* p;
63 register const char* v;
64 int i;
65 int first;
66 int next;
67 int last;
68 int oerrno;
69 Lc_t* lc;
70 char file[PATH_MAX];
71 char* paths[5];
72
73 static char lc_messages[] = "LC_MESSAGES";
74
75 if ((category = lcindex(category, 1)) < 0)
76 return 0;
77 if (!(lc = locale ? lcmake(locale) : locales[category]))
78 return 0;
79 oerrno = errno;
80 if (catalog && *catalog == '/')
81 {
82 i = eaccess(catalog, R_OK);
83 errno = oerrno;
84 if (i)
85 return 0;
86 strlcpy(path, catalog, size);
87 return path;
88 }
89 i = 0;
90 if ((p = getenv("NLSPATH")) && *p)
91 paths[i++] = p;
92 paths[i++] = "share/lib/locale/%l/%C/%N";
93 paths[i++] = "share/locale/%l/%C/%N";
94 paths[i++] = "lib/locale/%l/%C/%N";
95 paths[i] = 0;
96 next = 1;
97 for (i = 0; p = paths[i]; i += next)
98 {
99 first = 1;
100 last = 0;
101 e = &file[elementsof(file) - 1];
102 while (*p)
103 {
104 s = file;
105 for (;;)
106 {
107 switch (c = *p++)
108 {
109 case 0:
110 p--;
111 break;
112 case ':':
113 break;
114 case '%':
115 if (s < e)
116 {
117 switch (c = *p++)
118 {
119 case 0:
120 p--;
121 continue;
122 case 'N':
123 v = catalog;
124 break;
125 case 'L':
126 if (first)
127 {
128 first = 0;
129 if (next)
130 {
131 v = lc->code;
132 if (lc->code != lc->language->code)
133 next = 0;
134 }
135 else
136 {
137 next = 1;
138 v = lc->language->code;
139 }
140 }
141 break;
142 case 'l':
143 v = lc->language->code;
144 break;
145 case 't':
146 v = lc->territory->code;
147 break;
148 case 'c':
149 v = lc->charset->code;
150 break;
151 case 'C':
152 case_C:
153 if (!catalog)
154 last = 1;
155 v = lc_categories[category].name;
156 break;
157 default:
158 *s++ = c;
159 continue;
160 }
161 if (v)
162 while (*v && s < e)
163 *s++ = *v++;
164 }
165 continue;
166 case '/':
167 if (last)
168 break;
169 if (category != AST_LC_MESSAGES && strneq(p, lc_messages, sizeof(lc_messages) - 1) && p[sizeof(lc_messages)-1] == '/')
170 {
171 p += sizeof(lc_messages) - 1;
172 goto case_C;
173 }
174 /*FALLTHROUGH*/
175 default:
176 if (s < e)
177 *s++ = c;
178 continue;
179 }
180 break;
181 }
182 if (s > file)
183 *s = 0;
184 else if (!catalog)
185 continue;
186 else
187 strlcpy(file, catalog, elementsof(file));
188 if (ast.locale.set & AST_LC_find)
189 sfprintf(sfstderr, "locale find %s\n", file);
190 if (s = pathpath(file, "", (!catalog && category == AST_LC_MESSAGES) ? PATH_READ : (PATH_REGULAR|PATH_READ|PATH_ABSOLUTE), path, size))
191 {
192 if (ast.locale.set & (AST_LC_find|AST_LC_setlocale))
193 sfprintf(sfstderr, "locale path %s\n", s);
194 errno = oerrno;
195 return s;
196 }
197 }
198 }
199 errno = oerrno;
200 return 0;
201 }
202
203 /*
204 * allocate and read the binary message catalog ip
205 * if ip==0 then space is allocated for mcput()
206 * 0 returned on any error
207 */
208
209 Mc_t*
mcopen(register Sfio_t * ip)210 mcopen(register Sfio_t* ip)
211 {
212 register Mc_t* mc;
213 register char** mp;
214 register char* sp;
215 Vmalloc_t* vm;
216 char* rp;
217 int i;
218 int j;
219 int oerrno;
220 size_t n;
221 char buf[MC_MAGIC_SIZE];
222
223 oerrno = errno;
224 if (ip)
225 {
226 /*
227 * check the magic
228 */
229
230 if (sfread(ip, buf, MC_MAGIC_SIZE) != MC_MAGIC_SIZE)
231 {
232 errno = oerrno;
233 return 0;
234 }
235 if (memcmp(buf, MC_MAGIC, MC_MAGIC_SIZE))
236 return 0;
237 }
238
239 /*
240 * allocate the region
241 */
242
243 if (!(vm = vmopen(Vmdcheap, Vmbest, 0)) || !(mc = vmnewof(vm, 0, Mc_t, 1, 0)))
244 {
245 errno = oerrno;
246 return 0;
247 }
248 mc->vm = vm;
249 mc->cvt = (iconv_t)(-1);
250 if (ip)
251 {
252 /*
253 * read the translation record
254 */
255
256 if (!(sp = sfgetr(ip, 0, 0)) || !(mc->translation = vmstrdup(vm, sp)))
257 goto bad;
258
259 /*
260 * read the optional header records
261 */
262
263 do
264 {
265 if (!(sp = sfgetr(ip, 0, 0)))
266 goto bad;
267 } while (*sp);
268
269 /*
270 * get the component dimensions
271 */
272
273 mc->nstrs = sfgetu(ip);
274 mc->nmsgs = sfgetu(ip);
275 mc->num = sfgetu(ip);
276 if (sfeof(ip))
277 goto bad;
278 }
279 else if (!(mc->translation = vmnewof(vm, 0, char, 1, 0)))
280 goto bad;
281
282 /*
283 * allocate the remaining space
284 */
285
286 if (!(mc->set = vmnewof(vm, 0, Mcset_t, mc->num + 1, 0)))
287 goto bad;
288 if (!ip)
289 return mc;
290 if (!(mp = vmnewof(vm, 0, char*, mc->nmsgs + mc->num + 1, 0)))
291 goto bad;
292 if (!(rp = sp = vmalloc(vm, mc->nstrs + 1)))
293 goto bad;
294
295 /*
296 * get the set dimensions and initialize the msg pointers
297 */
298
299 while (i = sfgetu(ip))
300 {
301 if (i > mc->num)
302 goto bad;
303 n = sfgetu(ip);
304 mc->set[i].num = n;
305 mc->set[i].msg = mp;
306 mp += n + 1;
307 }
308
309 /*
310 * read the msg sizes and set up the msg pointers
311 */
312
313 for (i = 1; i <= mc->num; i++)
314 for (j = 1; j <= mc->set[i].num; j++)
315 if (n = sfgetu(ip))
316 {
317 mc->set[i].msg[j] = sp;
318 sp += n;
319 }
320
321 /*
322 * read the string table
323 */
324
325 if (sfread(ip, rp, mc->nstrs) != mc->nstrs || sfgetc(ip) != EOF)
326 goto bad;
327 if (!(mc->tmp = sfstropen()))
328 goto bad;
329 mc->cvt = iconv_open("", "utf");
330 errno = oerrno;
331 return mc;
332 bad:
333 vmclose(vm);
334 errno = oerrno;
335 return 0;
336 }
337
338 /*
339 * return the <set,num> message in mc
340 * msg returned on error
341 * utf message text converted to ucs
342 */
343
344 char*
mcget(register Mc_t * mc,int set,int num,const char * msg)345 mcget(register Mc_t* mc, int set, int num, const char* msg)
346 {
347 char* s;
348 size_t n;
349 int p;
350
351 if (!mc || set < 0 || set > mc->num || num < 1 || num > mc->set[set].num || !(s = mc->set[set].msg[num]))
352 return (char*)msg;
353 if (mc->cvt == (iconv_t)(-1))
354 return s;
355 if ((p = sfstrtell(mc->tmp)) > sfstrsize(mc->tmp) / 2)
356 {
357 p = 0;
358 sfstrseek(mc->tmp, p, SEEK_SET);
359 }
360 n = strlen(s) + 1;
361 iconv_write(mc->cvt, mc->tmp, &s, &n, NiL);
362 return sfstrbase(mc->tmp) + p;
363 }
364
365 /*
366 * set message <set,num> to msg
367 * msg==0 deletes the message
368 * the message and set counts are adjusted
369 * 0 returned on success, -1 otherwise
370 */
371
372 int
mcput(register Mc_t * mc,int set,int num,const char * msg)373 mcput(register Mc_t* mc, int set, int num, const char* msg)
374 {
375 register int i;
376 register char* s;
377 register Mcset_t* sp;
378 register char** mp;
379
380 /*
381 * validate the arguments
382 */
383
384 if (!mc || set > MC_SET_MAX || num > MC_NUM_MAX)
385 return -1;
386
387 /*
388 * deletions don't kick in allocations (duh)
389 */
390
391 if (!msg)
392 {
393 if (set <= mc->num && num <= mc->set[set].num && (s = mc->set[set].msg[num]))
394 {
395 /*
396 * decrease the string table size
397 */
398
399 mc->set[set].msg[num] = 0;
400 mc->nstrs -= strlen(s) + 1;
401 if (mc->set[set].num == num)
402 {
403 /*
404 * decrease the max msg num
405 */
406
407 mp = mc->set[set].msg + num;
408 while (num && !mp[--num]);
409 mc->nmsgs -= mc->set[set].num - num;
410 if (!(mc->set[set].num = num) && mc->num == set)
411 {
412 /*
413 * decrease the max set num
414 */
415
416 while (num && !mc->set[--num].num);
417 mc->num = num;
418 }
419 }
420 }
421 return 0;
422 }
423
424 /*
425 * keep track of the highest set and allocate if necessary
426 */
427
428 if (set > mc->num)
429 {
430 if (set > mc->gen)
431 {
432 i = MC_SET_MAX;
433 if (!(sp = vmnewof(mc->vm, 0, Mcset_t, i + 1, 0)))
434 return -1;
435 mc->gen = i;
436 for (i = 1; i <= mc->num; i++)
437 sp[i] = mc->set[i];
438 mc->set = sp;
439 }
440 mc->num = set;
441 }
442 sp = mc->set + set;
443
444 /*
445 * keep track of the highest msg and allocate if necessary
446 */
447
448 if (num > sp->num)
449 {
450 if (num > sp->gen)
451 {
452 if (!mc->gen)
453 {
454 i = (MC_NUM_MAX + 1) / 32;
455 if (i <= num)
456 i = 2 * num;
457 if (i > MC_NUM_MAX)
458 i = MC_NUM_MAX;
459 if (!(mp = vmnewof(mc->vm, 0, char*, i + 1, 0)))
460 return -1;
461 mc->gen = i;
462 sp->msg = mp;
463 for (i = 1; i <= sp->num; i++)
464 mp[i] = sp->msg[i];
465 }
466 else
467 {
468 i = 2 * mc->gen;
469 if (i > MC_NUM_MAX)
470 i = MC_NUM_MAX;
471 if (!(mp = vmnewof(mc->vm, sp->msg, char*, i + 1, 0)))
472 return -1;
473 sp->gen = i;
474 sp->msg = mp;
475 }
476 }
477 mc->nmsgs += num - sp->num;
478 sp->num = num;
479 }
480
481 /*
482 * decrease the string table size
483 */
484
485 if (s = sp->msg[num])
486 {
487 /*
488 * no-op if no change
489 */
490
491 if (streq(s, msg))
492 return 0;
493 mc->nstrs -= strlen(s) + 1;
494 }
495
496 /*
497 * allocate, add and adjust the string table size
498 */
499
500 if (!(s = vmstrdup(mc->vm, msg)))
501 return -1;
502 sp->msg[num] = s;
503 mc->nstrs += strlen(s) + 1;
504 return 0;
505 }
506
507 /*
508 * dump message catalog mc to op
509 * 0 returned on success, -1 otherwise
510 */
511
512 int
mcdump(register Mc_t * mc,register Sfio_t * op)513 mcdump(register Mc_t* mc, register Sfio_t* op)
514 {
515 register int i;
516 register int j;
517 register int n;
518 register char* s;
519 register Mcset_t* sp;
520
521 /*
522 * write the magic
523 */
524
525 if (sfwrite(op, MC_MAGIC, MC_MAGIC_SIZE) != MC_MAGIC_SIZE)
526 return -1;
527
528 /*
529 * write the translation record
530 */
531
532 sfputr(op, mc->translation, 0);
533
534 /* optional header records here */
535
536 /*
537 * end of optional header records
538 */
539
540 sfputu(op, 0);
541
542 /*
543 * write the global dimensions
544 */
545
546 sfputu(op, mc->nstrs);
547 sfputu(op, mc->nmsgs);
548 sfputu(op, mc->num);
549
550 /*
551 * write the set dimensions
552 */
553
554 for (i = 1; i <= mc->num; i++)
555 if (mc->set[i].num)
556 {
557 sfputu(op, i);
558 sfputu(op, mc->set[i].num);
559 }
560 sfputu(op, 0);
561
562 /*
563 * write the message sizes
564 */
565
566 for (i = 1; i <= mc->num; i++)
567 if (mc->set[i].num)
568 {
569 sp = mc->set + i;
570 for (j = 1; j <= sp->num; j++)
571 {
572 n = (s = sp->msg[j]) ? (strlen(s) + 1) : 0;
573 sfputu(op, n);
574 }
575 }
576
577 /*
578 * write the string table
579 */
580
581 for (i = 1; i <= mc->num; i++)
582 if (mc->set[i].num)
583 {
584 sp = mc->set + i;
585 for (j = 1; j <= sp->num; j++)
586 if (s = sp->msg[j])
587 sfputr(op, s, 0);
588 }
589
590 /*
591 * sync and return
592 */
593
594 return sfsync(op);
595 }
596
597 /*
598 * parse <set,msg> number from s
599 * e!=0 is set to the next char after the parse
600 * set!=0 is set to message set number
601 * msg!=0 is set to message number
602 * the message set number is returned
603 *
604 * the base 36 hash gives reasonable values for these:
605 *
606 * "ast" : ((((36#a^36#s^36#t)-9)&63)+1) = 3
607 * "gnu" : ((((36#g^36#n^36#u)-9)&63)+1) = 17
608 * "sgi" : ((((36#s^36#g^36#i)-9)&63)+1) = 22
609 * "sun" : ((((36#s^36#u^36#n)-9)&63)+1) = 13
610 */
611
612 int
mcindex(register const char * s,char ** e,int * set,int * msg)613 mcindex(register const char* s, char** e, int* set, int* msg)
614 {
615 register int c;
616 register int m;
617 register int n;
618 register int r;
619 register unsigned char* cv;
620 char* t;
621
622 m = 0;
623 n = strtol(s, &t, 0);
624 if (t == (char*)s)
625 {
626 SFCVINIT();
627 cv = _Sfcv36;
628 for (n = m = 0; (c = cv[*s]) < 36; s++)
629 {
630 m++;
631 n ^= c;
632 }
633 m = (m <= 3) ? 63 : ((1 << (m + 3)) - 1);
634 n = ((n - 9) & m) + 1;
635 }
636 else
637 s = (const char*)t;
638 r = n;
639 if (*s)
640 m = strtol(s + 1, e, 0);
641 else
642 {
643 if (e)
644 *e = (char*)s;
645 if (m)
646 m = 0;
647 else
648 {
649 m = n;
650 n = 1;
651 }
652 }
653 if (set)
654 *set = n;
655 if (msg)
656 *msg = m;
657 return r;
658 }
659
660 /*
661 * close the message catalog mc
662 */
663
664 int
mcclose(register Mc_t * mc)665 mcclose(register Mc_t* mc)
666 {
667 if (!mc)
668 return -1;
669 if (mc->tmp)
670 sfclose(mc->tmp);
671 if (mc->cvt != (iconv_t)(-1))
672 iconv_close(mc->cvt);
673 vmclose(mc->vm);
674 return 0;
675 }
676