1 /***********************************************************************
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 *
9 * A copy of the License is available at *
10 * http://www.opensource.org/licenses/cpl1.0.txt *
11 * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
13 * Information and Software Systems Research *
17 * Glenn Fowler <gsf@research.att.com> *
18 * David Korn <dgk@research.att.com> *
19 * Phong Vo <kpv@research.att.com> *
21 ***********************************************************************/
28 * machine independent binary message catalog implementation
36 #define _MC_PRIVATE_ \
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)
57 mcfind(char* path
, const char* locale
, const char* catalog
, int category
, int nls
)
63 register const char* v
;
73 static char lc_messages
[] = "LC_MESSAGES";
75 if ((category
= lcindex(category
, 1)) < 0)
77 if (!(lc
= locale
? lcmake(locale
) : locales
[category
]))
80 if (catalog
&& *catalog
== '/')
82 i
= eaccess(catalog
, R_OK
);
86 strncpy(path
, catalog
, PATH_MAX
-1);
91 if ((p
= getenv("NLSPATH")) && *p
)
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";
99 for (i
= 0; p
= paths
[i
]; i
+= next
)
103 e
= &file
[elementsof(file
) - 1];
134 if (lc
->code
!= lc
->language
->code
)
140 v
= lc
->language
->code
;
145 v
= lc
->language
->code
;
148 v
= lc
->territory
->code
;
151 v
= lc
->charset
->code
;
157 v
= lc_categories
[category
].name
;
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;
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
);
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
212 mcopen(register Sfio_t
* ip
)
223 char buf
[MC_MAGIC_SIZE
];
232 if (sfread(ip
, buf
, MC_MAGIC_SIZE
) != MC_MAGIC_SIZE
)
237 if (memcmp(buf
, MC_MAGIC
, MC_MAGIC_SIZE
))
242 * allocate the region
245 if (!(vm
= vmopen(Vmdcheap
, Vmbest
, 0)) || !(mc
= vmnewof(vm
, 0, Mc_t
, 1, 0)))
251 mc
->cvt
= (iconv_t
)(-1);
255 * read the translation record
258 if (!(sp
= sfgetr(ip
, 0, 0)) || !(mc
->translation
= vmstrdup(vm
, sp
)))
262 * read the optional header records
267 if (!(sp
= sfgetr(ip
, 0, 0)))
272 * get the component dimensions
275 mc
->nstrs
= sfgetu(ip
);
276 mc
->nmsgs
= sfgetu(ip
);
277 mc
->num
= sfgetu(ip
);
281 else if (!(mc
->translation
= vmnewof(vm
, 0, char, 1, 0)))
285 * allocate the remaining space
288 if (!(mc
->set
= vmnewof(vm
, 0, Mcset_t
, mc
->num
+ 1, 0)))
292 if (!(mp
= vmnewof(vm
, 0, char*, mc
->nmsgs
+ mc
->num
+ 1, 0)))
294 if (!(rp
= sp
= vmalloc(vm
, mc
->nstrs
+ 1)))
298 * get the set dimensions and initialize the msg pointers
301 while (i
= sfgetu(ip
))
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
++)
319 mc
->set
[i
].msg
[j
] = sp
;
324 * read the string table
327 if (sfread(ip
, rp
, mc
->nstrs
) != mc
->nstrs
|| sfgetc(ip
) != EOF
)
329 if (!(mc
->tmp
= sfstropen()))
331 mc
->cvt
= iconv_open("", "utf");
341 * return the <set,num> message in mc
342 * msg returned on error
343 * utf message text converted to ucs
347 mcget(register Mc_t
* mc
, int set
, int num
, const char* msg
)
353 if (!mc
|| set
< 0 || set
> mc
->num
|| num
< 1 || num
> mc
->set
[set
].num
|| !(s
= mc
->set
[set
].msg
[num
]))
355 if (mc
->cvt
== (iconv_t
)(-1))
357 if ((p
= sfstrtell(mc
->tmp
)) > sfstrsize(mc
->tmp
) / 2)
360 sfstrseek(mc
->tmp
, p
, SEEK_SET
);
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
)
379 register Mcset_t
* sp
;
383 * validate the arguments
386 if (!mc
|| set
> MC_SET_MAX
|| num
> MC_NUM_MAX
)
390 * deletions don't kick in allocations (duh)
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
);
427 * keep track of the highest set and allocate if necessary
435 if (!(sp
= vmnewof(mc
->vm
, 0, Mcset_t
, i
+ 1, 0)))
438 for (i
= 1; i
<= mc
->num
; i
++)
447 * keep track of the highest msg and allocate if necessary
456 i
= (MC_NUM_MAX
+ 1) / 32;
461 if (!(mp
= vmnewof(mc
->vm
, 0, char*, i
+ 1, 0)))
465 for (i
= 1; i
<= sp
->num
; i
++)
473 if (!(mp
= vmnewof(mc
->vm
, sp
->msg
, char*, i
+ 1, 0)))
479 mc
->nmsgs
+= num
- sp
->num
;
484 * decrease the string table size
487 if (s
= sp
->msg
[num
])
495 mc
->nstrs
-= strlen(s
) + 1;
499 * allocate, add and adjust the string table size
502 if (!(s
= vmstrdup(mc
->vm
, msg
)))
505 mc
->nstrs
+= strlen(s
) + 1;
510 * dump message catalog mc to op
511 * 0 returned on success, -1 otherwise
515 mcdump(register Mc_t
* mc
, register Sfio_t
* op
)
521 register Mcset_t
* sp
;
527 if (sfwrite(op
, MC_MAGIC
, MC_MAGIC_SIZE
) != MC_MAGIC_SIZE
)
531 * write the translation record
534 sfputr(op
, mc
->translation
, 0);
536 /* optional header records here */
539 * end of optional header records
545 * write the global dimensions
548 sfputu(op
, mc
->nstrs
);
549 sfputu(op
, mc
->nmsgs
);
553 * write the set dimensions
556 for (i
= 1; i
<= mc
->num
; i
++)
560 sfputu(op
, mc
->set
[i
].num
);
565 * write the message sizes
568 for (i
= 1; i
<= mc
->num
; i
++)
572 for (j
= 1; j
<= sp
->num
; j
++)
574 n
= (s
= sp
->msg
[j
]) ? (strlen(s
) + 1) : 0;
580 * write the string table
583 for (i
= 1; i
<= mc
->num
; i
++)
587 for (j
= 1; j
<= sp
->num
; j
++)
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
)
621 register unsigned char* cv
;
625 n
= strtol(s
, &t
, 0);
630 for (n
= m
= 0; (c
= cv
[*s
]) < 36; s
++)
635 m
= (m
<= 3) ? 63 : ((1 << (m
+ 3)) - 1);
636 n
= ((n
- 9) & m
) + 1;
642 m
= strtol(s
+ 1, e
, 0);
663 * close the message catalog mc
667 mcclose(register Mc_t
* mc
)
673 if (mc
->cvt
!= (iconv_t
)(-1))
674 iconv_close(mc
->cvt
);