8354 sync regcomp(3C) with upstream (fix make catalog)
[unleashed/tickless.git] / usr / src / lib / libast / common / port / mc.c
blob5c26513f2f36a320bd77608342788e0eb752133d
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
25 * Glenn Fowler
26 * AT&T Research
28 * machine independent binary message catalog implementation
31 #include "sfhdr.h"
32 #include "lclib.h"
34 #include <iconv.h>
36 #define _MC_PRIVATE_ \
37 size_t nstrs; \
38 size_t nmsgs; \
39 iconv_t cvt; \
40 Sfio_t* tmp; \
41 Vmalloc_t* vm;
43 #include <vmalloc.h>
44 #include <error.h>
45 #include <mc.h>
46 #include <nl_types.h>
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)
56 char*
57 mcfind(char* path, const char* locale, const char* catalog, int category, int nls)
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];
73 static char lc_messages[] = "LC_MESSAGES";
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 == '/')
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;
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)
101 first = 1;
102 last = 0;
103 e = &file[elementsof(file) - 1];
104 while (*p)
106 s = file;
107 for (;;)
109 switch (c = *p++)
111 case 0:
112 p--;
113 break;
114 case ':':
115 break;
116 case '%':
117 if (s < e)
119 switch (c = *p++)
121 case 0:
122 p--;
123 continue;
124 case 'N':
125 v = catalog;
126 break;
127 case 'L':
128 if (first)
130 first = 0;
131 if (next)
133 v = lc->code;
134 if (lc->code != lc->language->code)
135 next = 0;
137 else
139 next = 1;
140 v = lc->language->code;
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;
163 if (v)
164 while (*v && s < e)
165 *s++ = *v++;
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] == '/')
173 p += sizeof(lc_messages) - 1;
174 goto case_C;
176 /*FALLTHROUGH*/
177 default:
178 if (s < e)
179 *s++ = c;
180 continue;
182 break;
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)))
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;
201 errno = oerrno;
202 return 0;
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
211 Mc_t*
212 mcopen(register Sfio_t* ip)
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];
225 oerrno = errno;
226 if (ip)
229 * check the magic
232 if (sfread(ip, buf, MC_MAGIC_SIZE) != MC_MAGIC_SIZE)
234 errno = oerrno;
235 return 0;
237 if (memcmp(buf, MC_MAGIC, MC_MAGIC_SIZE))
238 return 0;
242 * allocate the region
245 if (!(vm = vmopen(Vmdcheap, Vmbest, 0)) || !(mc = vmnewof(vm, 0, Mc_t, 1, 0)))
247 errno = oerrno;
248 return 0;
250 mc->vm = vm;
251 mc->cvt = (iconv_t)(-1);
252 if (ip)
255 * read the translation record
258 if (!(sp = sfgetr(ip, 0, 0)) || !(mc->translation = vmstrdup(vm, sp)))
259 goto bad;
262 * read the optional header records
267 if (!(sp = sfgetr(ip, 0, 0)))
268 goto bad;
269 } while (*sp);
272 * get the component dimensions
275 mc->nstrs = sfgetu(ip);
276 mc->nmsgs = sfgetu(ip);
277 mc->num = sfgetu(ip);
278 if (sfeof(ip))
279 goto bad;
281 else if (!(mc->translation = vmnewof(vm, 0, char, 1, 0)))
282 goto bad;
285 * allocate the remaining space
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;
298 * get the set dimensions and initialize the msg pointers
301 while (i = sfgetu(ip))
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;
312 * read the msg sizes and set up the msg pointers
315 for (i = 1; i <= mc->num; i++)
316 for (j = 1; j <= mc->set[i].num; j++)
317 if (n = sfgetu(ip))
319 mc->set[i].msg[j] = sp;
320 sp += n;
324 * read the string table
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;
341 * return the <set,num> message in mc
342 * msg returned on error
343 * utf message text converted to ucs
346 char*
347 mcget(register Mc_t* mc, int set, int num, const char* msg)
349 char* s;
350 size_t n;
351 int p;
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)
359 p = 0;
360 sfstrseek(mc->tmp, p, SEEK_SET);
362 n = strlen(s) + 1;
363 iconv_write(mc->cvt, mc->tmp, &s, &n, NiL);
364 return sfstrbase(mc->tmp) + p;
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
375 mcput(register Mc_t* mc, int set, int num, const char* msg)
377 register int i;
378 register char* s;
379 register Mcset_t* sp;
380 register char** mp;
383 * validate the arguments
386 if (!mc || set > MC_SET_MAX || num > MC_NUM_MAX)
387 return -1;
390 * deletions don't kick in allocations (duh)
393 if (!msg)
395 if (set <= mc->num && num <= mc->set[set].num && (s = mc->set[set].msg[num]))
398 * decrease the string table size
401 mc->set[set].msg[num] = 0;
402 mc->nstrs -= strlen(s) + 1;
403 if (mc->set[set].num == num)
406 * decrease the max msg num
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)
415 * decrease the max set num
418 while (num && !mc->set[--num].num);
419 mc->num = num;
423 return 0;
427 * keep track of the highest set and allocate if necessary
430 if (set > mc->num)
432 if (set > mc->gen)
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;
442 mc->num = set;
444 sp = mc->set + set;
447 * keep track of the highest msg and allocate if necessary
450 if (num > sp->num)
452 if (num > sp->gen)
454 if (!mc->gen)
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];
468 else
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;
479 mc->nmsgs += num - sp->num;
480 sp->num = num;
484 * decrease the string table size
487 if (s = sp->msg[num])
490 * no-op if no change
493 if (streq(s, msg))
494 return 0;
495 mc->nstrs -= strlen(s) + 1;
499 * allocate, add and adjust the string table size
502 if (!(s = vmstrdup(mc->vm, msg)))
503 return -1;
504 sp->msg[num] = s;
505 mc->nstrs += strlen(s) + 1;
506 return 0;
510 * dump message catalog mc to op
511 * 0 returned on success, -1 otherwise
515 mcdump(register Mc_t* mc, register Sfio_t* op)
517 register int i;
518 register int j;
519 register int n;
520 register char* s;
521 register Mcset_t* sp;
524 * write the magic
527 if (sfwrite(op, MC_MAGIC, MC_MAGIC_SIZE) != MC_MAGIC_SIZE)
528 return -1;
531 * write the translation record
534 sfputr(op, mc->translation, 0);
536 /* optional header records here */
539 * end of optional header records
542 sfputu(op, 0);
545 * write the global dimensions
548 sfputu(op, mc->nstrs);
549 sfputu(op, mc->nmsgs);
550 sfputu(op, mc->num);
553 * write the set dimensions
556 for (i = 1; i <= mc->num; i++)
557 if (mc->set[i].num)
559 sfputu(op, i);
560 sfputu(op, mc->set[i].num);
562 sfputu(op, 0);
565 * write the message sizes
568 for (i = 1; i <= mc->num; i++)
569 if (mc->set[i].num)
571 sp = mc->set + i;
572 for (j = 1; j <= sp->num; j++)
574 n = (s = sp->msg[j]) ? (strlen(s) + 1) : 0;
575 sfputu(op, n);
580 * write the string table
583 for (i = 1; i <= mc->num; i++)
584 if (mc->set[i].num)
586 sp = mc->set + i;
587 for (j = 1; j <= sp->num; j++)
588 if (s = sp->msg[j])
589 sfputr(op, s, 0);
593 * sync and return
596 return sfsync(op);
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
606 * the base 36 hash gives reasonable values for these:
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
615 mcindex(register const char* s, char** e, int* set, int* msg)
617 register int c;
618 register int m;
619 register int n;
620 register int r;
621 register unsigned char* cv;
622 char* t;
624 m = 0;
625 n = strtol(s, &t, 0);
626 if (t == (char*)s)
628 SFCVINIT();
629 cv = _Sfcv36;
630 for (n = m = 0; (c = cv[*s]) < 36; s++)
632 m++;
633 n ^= c;
635 m = (m <= 3) ? 63 : ((1 << (m + 3)) - 1);
636 n = ((n - 9) & m) + 1;
638 else
639 s = (const char*)t;
640 r = n;
641 if (*s)
642 m = strtol(s + 1, e, 0);
643 else
645 if (e)
646 *e = (char*)s;
647 if (m)
648 m = 0;
649 else
651 m = n;
652 n = 1;
655 if (set)
656 *set = n;
657 if (msg)
658 *msg = m;
659 return r;
663 * close the message catalog mc
667 mcclose(register Mc_t* mc)
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;