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