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