xref: /titanic_51/usr/src/lib/libast/common/port/mc.c (revision 01ef659d9b1ead333ef0adc346e7051f7eae7520)
1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *          Copyright (c) 1985-2008 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*
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*
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*
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
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
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
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
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