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