xref: /titanic_41/usr/src/lib/libast/common/comp/setlocale.c (revision b7d3956b92a285d8dac2c7f5f7e28d2ef5347ef8)
1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *           Copyright (c) 1985-2007 AT&T Knowledge Ventures            *
5 *                      and is licensed under the                       *
6 *                  Common Public License, Version 1.0                  *
7 *                      by AT&T Knowledge Ventures                      *
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  * setlocale() intercept
26  * maintains a bitmask of non-default categories
27  * and a permanent locale namespace for pointer comparison
28  * and persistent private data for locale related functions
29  */
30 
31 #include <ast_standards.h>
32 
33 #include "lclib.h"
34 
35 #include <ast_wchar.h>
36 #include <ctype.h>
37 #include <mc.h>
38 #include <namval.h>
39 
40 #if ( _lib_wcwidth || _lib_wctomb ) && _hdr_wctype
41 #include <wctype.h>
42 #endif
43 
44 #if _lib_wcwidth
45 #undef	wcwidth
46 #else
47 #define wcwidth			0
48 #endif
49 
50 #if _lib_wctomb
51 #undef	wctomb
52 #else
53 #define wctomb			0
54 #endif
55 
56 #ifdef mblen
57 #undef	mblen
58 extern int		mblen(const char*, size_t);
59 #endif
60 
61 #undef	mbtowc
62 #undef	setlocale
63 #undef	strcmp
64 #undef	strcoll
65 #undef	strxfrm
66 #undef	valid
67 
68 #ifndef AST_LC_CANONICAL
69 #define AST_LC_CANONICAL	LC_abbreviated
70 #endif
71 
72 #if _UWIN
73 
74 #include <ast_windows.h>
75 
76 #undef	_lib_setlocale
77 #define _lib_setlocale		1
78 
79 #define setlocale(c,l)		native_setlocale(c,l)
80 
81 extern char*			uwin_setlocale(int, const char*);
82 
83 /*
84  * convert locale to native locale name in buf
85  */
86 
87 static char*
88 native_locale(const char* locale, char* buf, size_t siz)
89 {
90 	Lc_t*				lc;
91 	const Lc_attribute_list_t*	ap;
92 	int				i;
93 	unsigned long			lcid;
94 	unsigned long			lang;
95 	unsigned long			ctry;
96 	char				lbuf[128];
97 	char				cbuf[128];
98 
99 	if (locale && *locale)
100 	{
101 		if (!(lc = lcmake(locale)))
102 			return 0;
103 		lang = lc->language->index;
104 		ctry = 0;
105 		for (ap = lc->attributes; ap; ap = ap->next)
106 			if (ctry = ap->attribute->index)
107 				break;
108 		if (!ctry)
109 		{
110 			for (i = 0; i < elementsof(lc->territory->languages); i++)
111 				if (lc->territory->languages[i] == lc->language)
112 				{
113 					ctry = lc->territory->indices[i];
114 					break;
115 				}
116 			if (!ctry)
117 				ctry = SUBLANG_DEFAULT;
118 		}
119 		lcid = MAKELCID(MAKELANGID(lang, ctry), SORT_DEFAULT);
120 	}
121 	else
122 		lcid = GetUserDefaultLCID();
123 	if (GetLocaleInfo(lcid, LOCALE_SENGLANGUAGE, lbuf, sizeof(lbuf)) <= 0 ||
124 	    GetLocaleInfo(lcid, LOCALE_SENGCOUNTRY, cbuf, sizeof(cbuf)) <= 0)
125 		return 0;
126 	if (lc->charset->ms)
127 		sfsprintf(buf, siz, "%s_%s.%s", lbuf, cbuf, lc->charset->ms);
128 	else
129 		sfsprintf(buf, siz, "%s_%s", lbuf, cbuf);
130 	return buf;
131 }
132 
133 /*
134  * locale!=0 here
135  */
136 
137 static char*
138 native_setlocale(int category, const char* locale)
139 {
140 	char*		usr;
141 	char*		sys;
142 	char		buf[256];
143 
144 	/*
145 	 * win32 doesn't have LC_MESSAGES
146 	 */
147 
148 	if (category == LC_MESSAGES)
149 		return (char*)locale;
150 	if (!(usr = native_locale(locale, buf, sizeof(buf))))
151 		return 0;
152 	sys = uwin_setlocale(category, usr);
153 	if (ast.locale.set & AST_LC_debug)
154 		sfprintf(sfstderr, "locale uwin %17s %-24s %-24s\n", categories[lcindex(category, 0)].name, usr, sys);
155 	return sys;
156 }
157 
158 #else
159 
160 #define native_locale(a,b,c)	((char*)0)
161 
162 #endif
163 
164 /*
165  * LC_COLLATE and LC_CTYPE native support
166  */
167 
168 #if !_lib_mbtowc || MB_LEN_MAX <= 1
169 #define mblen		0
170 #define mbtowc		0
171 #endif
172 
173 #if !_lib_strcoll
174 #define	strcoll		0
175 #endif
176 
177 #if !_lib_strxfrm
178 #define	strxfrm		0
179 #endif
180 
181 /*
182  * LC_COLLATE and LC_CTYPE debug support
183  *
184  * mutibyte debug encoding
185  *
186  *	DL0 [ '0' .. '4' ] c1 ... c4 DR0
187  *	DL1 [ '0' .. '4' ] c1 ... c4 DR1
188  *
189  * with these ligatures
190  *
191  *	ch CH sst SST
192  *
193  * and private collation order
194  *
195  * wide character display width is the low order 3 bits
196  * wctomb() uses DL1...DR1
197  */
198 
199 #define DEBUG_MB_CUR_MAX	7
200 
201 #if DEBUG_MB_CUR_MAX < MB_LEN_MAX
202 #undef	DEBUG_MB_CUR_MAX
203 #define DEBUG_MB_CUR_MAX	MB_LEN_MAX
204 #endif
205 
206 #define DL0	'<'
207 #define DL1	0xab		/* 8-bit mini << on xterm	*/
208 #define DR0	'>'
209 #define DR1	0xbb		/* 8-bit mini >> on xterm	*/
210 
211 #define DB	((int)sizeof(wchar_t)*8-1)
212 #define DC	7		/* wchar_t embedded char bits	*/
213 #define DX	(DB/DC)		/* wchar_t max embedded chars	*/
214 #define DZ	(DB-DX*DC+1)	/* wchar_t embedded size bits	*/
215 #define DD	3		/* # mb delimiter chars <n...>	*/
216 
217 static unsigned char debug_order[] =
218 {
219 	  0,   1,   2,   3,   4,   5,   6,   7,
220 	  8,   9,  10,  11,  12,  13,  14,  15,
221 	 16,  17,  18,  19,  20,  21,  22,  23,
222 	 24,  25,  26,  27,  28,  29,  30,  31,
223 	 99, 100, 101, 102,  98, 103, 104, 105,
224 	106, 107, 108,  43, 109,  44,  42, 110,
225 	 32,  33,  34,  35,  36,  37,  38,  39,
226 	 40,  41, 111, 112, 113, 114, 115, 116,
227 	117,  71,  72,  73,  74,  75,  76,  77,
228 	 78,  79,  80,  81,  82,  83,  84,  85,
229 	 86,  87,  88,  89,  90,  91,  92,  93,
230 	 94,  95,  96, 118, 119, 120, 121,  97,
231 	122,  45,  46,  47,  48,  49,  50,  51,
232 	 52,  53,  54,  55,  56,  57,  58,  59,
233 	 60,  61,  62,  63,  64,  65,  66,  67,
234 	 68,  69,  70, 123, 124, 125, 126, 127,
235 	128, 129, 130, 131, 132, 133, 134, 135,
236 	136, 137, 138, 139, 140, 141, 142, 143,
237 	144, 145, 146, 147, 148, 149, 150, 151,
238 	152, 153, 154, 155, 156, 157, 158, 159,
239 	160, 161, 162, 163, 164, 165, 166, 167,
240 	168, 169, 170, 171, 172, 173, 174, 175,
241 	176, 177, 178, 179, 180, 181, 182, 183,
242 	184, 185, 186, 187, 188, 189, 190, 191,
243 	192, 193, 194, 195, 196, 197, 198, 199,
244 	200, 201, 202, 203, 204, 205, 206, 207,
245 	208, 209, 210, 211, 212, 213, 214, 215,
246 	216, 217, 218, 219, 220, 221, 222, 223,
247 	224, 225, 226, 227, 228, 229, 230, 231,
248 	232, 233, 234, 235, 236, 237, 238, 239,
249 	240, 241, 242, 243, 244, 245, 246, 247,
250 	248, 249, 250, 251, 252, 253, 254, 255,
251 };
252 
253 static int
254 debug_mbtowc(register wchar_t* p, register const char* s, size_t n)
255 {
256 	register const char*	q;
257 	register const char*	r;
258 	register int		w;
259 	register int		dr;
260 	wchar_t			c;
261 
262 	if (n < 1)
263 		return -1;
264 	if (!s || !*s)
265 		return 0;
266 	switch (((unsigned char*)s)[0])
267 	{
268 	case DL0:
269 		dr = DR0;
270 		break;
271 	case DL1:
272 		dr = DR1;
273 		break;
274 	default:
275 		if (p)
276 			*p = ((unsigned char*)s)[0] & ((1<<DC)-1);
277 		return 1;
278 	}
279 	if (n < 2)
280 		return -1;
281 	if ((w = ((unsigned char*)s)[1]) == ((unsigned char*)s)[0])
282 	{
283 		if (p)
284 			*p = w;
285 		return 2;
286 	}
287 	if (w < '0' || w > ('0' + DX))
288 		return -1;
289 	if ((w -= '0' - DD) > n)
290 		return -1;
291 	r = s + w - 1;
292 	q = s += 2;
293 	while (q < r && *q)
294 		q++;
295 	if (q != r || *((unsigned char*)q) != dr)
296 		return -1;
297 	if (p)
298 	{
299 		c = 0;
300 		while (--q >= s)
301 		{
302 			c <<= DC;
303 			c |= *((unsigned char*)q);
304 		}
305 		c <<= DZ;
306 		c |= w - DD;
307 		*p = c;
308 	}
309 	return w;
310 }
311 
312 static int
313 debug_wctomb(char* s, wchar_t c)
314 {
315 	int	w;
316 	int	i;
317 	int	k;
318 
319 	w = 0;
320 	if (c >= 0 && c <= UCHAR_MAX)
321 	{
322 		w++;
323 		if (s)
324 			*s = c;
325 	}
326 	else if ((i = c & ((1<<DZ)-1)) > DX)
327 		return -1;
328 	else
329 	{
330 		w++;
331 		if (s)
332 			*s++ = DL1;
333 		c >>= DZ;
334 		w++;
335 		if (s)
336 			*s++ = i + '0';
337 		while (i--)
338 		{
339 			w++;
340 			if (s)
341 				*s++ = (k = c & ((1<<DC)-1)) ? k : '?';
342 			c >>= DC;
343 		}
344 		w++;
345 		if (s)
346 			*s++ = DR1;
347 	}
348 	return w;
349 }
350 
351 static int
352 debug_mblen(const char* s, size_t n)
353 {
354 	return debug_mbtowc(NiL, s, n);
355 }
356 
357 static int
358 debug_wcwidth(wchar_t c)
359 {
360 	if (c >= 0 && c <= UCHAR_MAX)
361 		return 1;
362 	if ((c &= ((1<<DZ)-1)) > DX)
363 		return -1;
364 	return c + DD;
365 }
366 
367 static size_t
368 debug_strxfrm(register char* t, register const char* s, size_t n)
369 {
370 	register const char*	q;
371 	register const char*	r;
372 	register char*		e;
373 	register size_t		z;
374 	register int		w;
375 
376 	z = 0;
377 	if (e = t)
378 		e += n;
379 	while (s[0])
380 	{
381 		if ((((unsigned char*)s)[0] == DL0 || ((unsigned char*)s)[0] == DL1) && (w = s[1]) >= '0' && w <= ('0' + DC))
382 		{
383 			w -= '0';
384 			q = s + 2;
385 			r = q + w;
386 			while (q < r && *q)
387 				q++;
388 			if (*((unsigned char*)q) == DR0 || *((unsigned char*)q) == DR1)
389 			{
390 				if (t)
391 				{
392 					for (q = s + 2; q < r; q++)
393 						if (t < e)
394 							*t++ = debug_order[*q];
395 					while (w++ < DX)
396 						if (t < e)
397 							*t++ = 1;
398 				}
399 				s = r + 1;
400 				z += DX;
401 				continue;
402 			}
403 		}
404 		if ((s[0] == 'c' || s[0] == 'C') && (s[1] == 'h' || s[1] == 'H'))
405 		{
406 			if (t)
407 			{
408 				if (t < e)
409 					*t++ = debug_order[s[0]];
410 				if (t < e)
411 					*t++ = debug_order[s[1]];
412 				if (t < e)
413 					*t++ = 1;
414 				if (t < e)
415 					*t++ = 1;
416 			}
417 			s += 2;
418 			z += DX;
419 			continue;
420 		}
421 		if ((s[0] == 's' || s[0] == 'S') && (s[1] == 's' || s[1] == 'S') && (s[2] == 't' || s[2] == 'T'))
422 		{
423 			if (t)
424 			{
425 				if (t < e)
426 					*t++ = debug_order[s[0]];
427 				if (t < e)
428 					*t++ = debug_order[s[1]];
429 				if (t < e)
430 					*t++ = debug_order[s[2]];
431 				if (t < e)
432 					*t++ = 1;
433 			}
434 			s += 3;
435 			z += DX;
436 			continue;
437 		}
438 		if (t)
439 		{
440 			if (t < e)
441 				*t++ = debug_order[s[0]];
442 			if (t < e)
443 				*t++ = 1;
444 			if (t < e)
445 				*t++ = 1;
446 			if (t < e)
447 				*t++ = 1;
448 		}
449 		s++;
450 		z += DX;
451 	}
452 	if (t < e)
453 		*t = 0;
454 	return z;
455 }
456 
457 static int
458 debug_strcoll(const char* a, const char* b)
459 {
460 	char	ab[1024];
461 	char	bb[1024];
462 
463 	debug_strxfrm(ab, a, sizeof(ab) - 1);
464 	ab[sizeof(ab)-1] = 0;
465 	debug_strxfrm(bb, b, sizeof(bb) - 1);
466 	bb[sizeof(bb)-1] = 0;
467 	return strcmp(ab, bb);
468 }
469 
470 /*
471  * default locale
472  */
473 
474 static int
475 default_wcwidth(wchar_t w)
476 {
477 	return w >= 0 && w <= 255 && !iscntrl(w) ? 1 : -1;
478 }
479 
480 /*
481  * called when LC_COLLATE initialized or changes
482  */
483 
484 static int
485 set_collate(Lc_category_t* cp)
486 {
487 	if (locales[cp->internal]->flags & LC_debug)
488 	{
489 		ast.collate = debug_strcoll;
490 		ast.mb_xfrm = debug_strxfrm;
491 	}
492 	else if (locales[cp->internal]->flags & LC_default)
493 	{
494 		ast.collate = strcmp;
495 		ast.mb_xfrm = 0;
496 	}
497 	else
498 	{
499 		ast.collate = strcoll;
500 		ast.mb_xfrm = strxfrm;
501 	}
502 	return 0;
503 }
504 
505 /*
506  * called when LC_CTYPE initialized or changes
507  */
508 
509 static int
510 set_ctype(Lc_category_t* cp)
511 {
512 	if (locales[cp->internal]->flags & LC_debug)
513 	{
514 		ast.mb_cur_max = DEBUG_MB_CUR_MAX;
515 		ast.mb_len = debug_mblen;
516 		ast.mb_towc = debug_mbtowc;
517 		ast.mb_width = debug_wcwidth;
518 		ast.mb_conv = debug_wctomb;
519 	}
520 	else if ((locales[cp->internal]->flags & LC_default) || (ast.mb_cur_max = MB_CUR_MAX) <= 1 || !(ast.mb_len = mblen) || !(ast.mb_towc = mbtowc))
521 	{
522 		ast.mb_cur_max = 1;
523 		ast.mb_len = 0;
524 		ast.mb_towc = 0;
525 		ast.mb_width = default_wcwidth;
526 		ast.mb_conv = 0;
527 	}
528 	else
529 	{
530 		if (!(ast.mb_width = wcwidth))
531 			ast.mb_width = default_wcwidth;
532 		ast.mb_conv = wctomb;
533 	}
534 	return 0;
535 }
536 
537 /*
538  * called when LC_NUMERIC initialized or changes
539  */
540 
541 static int
542 set_numeric(Lc_category_t* cp)
543 {
544 	register int		category = cp->internal;
545 	struct lconv*		lp;
546 	Lc_numeric_t*		dp;
547 
548 	static Lc_numeric_t	default_numeric = { '.', -1 };
549 
550 	if (!LCINFO(category)->data)
551 	{
552 		if ((lp = localeconv()) && (dp = newof(0, Lc_numeric_t, 1, 0)))
553 		{
554 			dp->decimal = lp->decimal_point && *lp->decimal_point ? *(unsigned char*)lp->decimal_point : '.';
555 			dp->thousand = lp->thousands_sep && *lp->thousands_sep ? *(unsigned char*)lp->thousands_sep : -1;
556 		}
557 		else
558 			dp = &default_numeric;
559 		LCINFO(category)->data = (void*)dp;
560 		if (ast.locale.set & (AST_LC_debug|AST_LC_setlocale))
561 			sfprintf(sfstderr, "locale info %17s decimal '%c' thousands '%c'\n", categories[category].name, dp->decimal, dp->thousand >= 0 ? dp->thousand : 'X');
562 	}
563 	return 0;
564 }
565 
566 /*
567  * this table is indexed by AST_LC_[A-Z]*
568  */
569 
570 Lc_category_t		categories[] =
571 {
572 { "LC_ALL",           LC_ALL,           AST_LC_ALL,           0               },
573 { "LC_COLLATE",       LC_COLLATE,       AST_LC_COLLATE,       set_collate     },
574 { "LC_CTYPE",         LC_CTYPE,         AST_LC_CTYPE,         set_ctype       },
575 { "LC_MESSAGES",      LC_MESSAGES,      AST_LC_MESSAGES,      0               },
576 { "LC_MONETARY",      LC_MONETARY,      AST_LC_MONETARY,      0               },
577 { "LC_NUMERIC",       LC_NUMERIC,       AST_LC_NUMERIC,       set_numeric     },
578 { "LC_TIME",          LC_TIME,          AST_LC_TIME,          0               },
579 { "LC_IDENTIFICATION",LC_IDENTIFICATION,AST_LC_IDENTIFICATION,0               },
580 { "LC_ADDRESS",       LC_ADDRESS,       AST_LC_ADDRESS,       0               },
581 { "LC_NAME",          LC_NAME,          AST_LC_NAME,          0               },
582 { "LC_TELEPHONE",     LC_TELEPHONE,     AST_LC_TELEPHONE,     0               },
583 { "LC_XLITERATE",     LC_XLITERATE,     AST_LC_XLITERATE,     0               },
584 { "LC_MEASUREMENT",   LC_MEASUREMENT,   AST_LC_MEASUREMENT,   0               },
585 { "LC_PAPER",         LC_PAPER,         AST_LC_PAPER,         0               },
586 };
587 
588 static const Namval_t	options[] =
589 {
590 	"debug",		AST_LC_debug,
591 	"find",			AST_LC_find,
592 	"setlocale",		AST_LC_setlocale,
593 	"translate",		AST_LC_translate,
594 	0,			0
595 };
596 
597 /*
598  * called by stropt() to set options
599  */
600 
601 static int
602 setopt(void* a, const void* p, int n, const char* v)
603 {
604 	if (p)
605 	{
606 		if (n)
607 			ast.locale.set |= ((Namval_t*)p)->value;
608 		else
609 			ast.locale.set &= ~((Namval_t*)p)->value;
610 	}
611 	return 0;
612 }
613 
614 #if !_lib_setlocale
615 
616 #define setlocale(c,l)		default_setlocale(c,l)
617 
618 static char*
619 default_setlocale(int category, const char* locale)
620 {
621 	Lc_t*		lc;
622 
623 	if (locale)
624 	{
625 		if (!(lc = lcmake(locale)) || !(lc->flags & LC_default))
626 			return 0;
627 		locales[0]->flags &= ~lc->flags;
628 		locales[1]->flags &= ~lc->flags;
629 		return lc->name;
630 	}
631 	return (locales[1]->flags & (1<<category)) ? locales[1]->name : locales[0]->name;
632 }
633 
634 #endif
635 
636 /*
637  * set a single AST_LC_* locale category
638  * the caller must validate category
639  * lc==0 restores the previous state
640  */
641 
642 static char*
643 single(int category, Lc_t* lc)
644 {
645 	const char*	sys;
646 	int		i;
647 
648 	if (!lc && !(lc = categories[category].prev))
649 		lc = lcmake(NiL);
650 	if (locales[category] != lc)
651 	{
652 		if (categories[category].external == -categories[category].internal)
653 		{
654 			sys = 0;
655 			for (i = 1; i < AST_LC_COUNT; i++)
656 				if (locales[i] == lc)
657 				{
658 					sys = (char*)lc->name;
659 					break;
660 				}
661 		}
662 		else if (lc->flags & (LC_debug|LC_local))
663 			sys = setlocale(categories[category].external, lcmake(NiL)->name);
664 		else if (!(sys = setlocale(categories[category].external, lc->name)) &&
665 			 (streq(lc->name, lc->code) || !(sys = setlocale(categories[category].external, lc->code))) &&
666 			 !streq(lc->code, lc->language->code))
667 				sys = setlocale(categories[category].external, lc->language->code);
668 		if (ast.locale.set & (AST_LC_debug|AST_LC_setlocale))
669 			sfprintf(sfstderr, "locale set  %17s %-24s %-24s\n", categories[category].name, lc->name, sys);
670 		if (!sys)
671 		{
672 			/*
673 			 * check for local override
674 			 * currently this means an LC_MESSAGES dir exists
675 			 */
676 
677 			if (!(lc->flags & LC_checked))
678 			{
679 				char	path[PATH_MAX];
680 
681 				if (mcfind(path, lc->code, NiL, LC_MESSAGES, 0))
682 					lc->flags |= LC_local;
683 				lc->flags |= LC_checked;
684 			}
685 			if (!(lc->flags & LC_local))
686 				return 0;
687 			if (categories[category].external != -categories[category].internal)
688 				setlocale(categories[category].external, lcmake(NiL)->name);
689 		}
690 		locales[category] = lc;
691 		if (categories[category].setf && (*categories[category].setf)(&categories[category]))
692 		{
693 			locales[category] = categories[category].prev;
694 			return 0;
695 		}
696 		if (lc->flags & LC_default)
697 			ast.locale.set &= ~(1<<category);
698 		else
699 			ast.locale.set |= (1<<category);
700 	}
701 	return (char*)lc->name;
702 }
703 
704 /*
705  * set composite AST_LC_ALL locale categories
706  * return <0:composite-error 0:not-composite >0:composite-ok
707  */
708 
709 static int
710 composite(register const char* s, int initialize)
711 {
712 	register const char*	t;
713 	register int		i;
714 	register int		j;
715 	register int		k;
716 	int			n;
717 	const char*		w;
718 	Lc_t*			p;
719 	int			cat[AST_LC_COUNT];
720 	int			stk[AST_LC_COUNT];
721 	char			buf[PATH_MAX / 2];
722 
723 	k = n = 0;
724 	while (s[0] == 'L' && s[1] == 'C' && s[2] == '_')
725 	{
726 		n++;
727 		j = 0;
728 		w = s;
729 		for (i = 1; i < AST_LC_COUNT; i++)
730 		{
731 			s = w;
732 			t = categories[i].name;
733 			while (*t && *s++ == *t++);
734 			if (!*t && *s++ == '=')
735 			{
736 				cat[j++] = i;
737 				if (s[0] != 'L' || s[1] != 'C' || s[2] != '_')
738 					break;
739 				w = s;
740 				i = -1;
741 			}
742 		}
743 		for (s = w; *s && *s != '='; s++);
744 		if (!*s)
745 		{
746 			for (i = 0; i < k; i++)
747 				single(stk[i], NiL);
748 			return -1;
749 		}
750 		w = ++s;
751 		for (;;)
752 		{
753 			if (!*s)
754 			{
755 				p = lcmake(w);
756 				break;
757 			}
758 			else if (*s++ == ';')
759 			{
760 				if ((n = s - w - 1) >= sizeof(buf))
761 					n = sizeof(buf) - 1;
762 				memcpy(buf, w, n);
763 				buf[n] = 0;
764 				p = lcmake(buf);
765 				break;
766 			}
767 		}
768 		for (i = 0; i < j; i++)
769 			if (!initialize)
770 			{
771 				if (!single(cat[i], p))
772 				{
773 					for (i = 0; i < k; i++)
774 						single(stk[i], NiL);
775 					return -1;
776 				}
777 				stk[k++] = cat[i];
778 			}
779 			else if (!categories[cat[i]].prev)
780 				categories[cat[i]].prev = p;
781 	}
782 	while (s[0] == '/' && s[1] && n < AST_LC_COUNT)
783 	{
784 		n++;
785 		for (w = ++s; *s && *s != '/'; s++);
786 		if (!*s)
787 			p = lcmake(w);
788 		else
789 		{
790 			if ((j = s - w - 1) >= sizeof(buf))
791 				j = sizeof(buf) - 1;
792 			memcpy(buf, w, j);
793 			buf[j] = 0;
794 			p = lcmake(buf);
795 		}
796 		if (!initialize)
797 		{
798 			if (!single(n, p))
799 			{
800 				for (i = 1; i < n; i++)
801 					single(i, NiL);
802 				return -1;
803 			}
804 		}
805 		else if (!categories[n].prev)
806 			categories[n].prev = p;
807 	}
808 	return n;
809 }
810 
811 /*
812  * setlocale() intercept
813  */
814 
815 char*
816 _ast_setlocale(int category, const char* locale)
817 {
818 	register char*		s;
819 	register int		i;
820 	register int		j;
821 	int			k;
822 	char*			a;
823 	Lc_t*			p;
824 	int			cat[AST_LC_COUNT];
825 
826 	static Sfio_t*		sp;
827 	static int		initialized;
828 	static char		local[] = "local";
829 
830 	if ((category = lcindex(category, 0)) < 0)
831 		return 0;
832 	if (!locale)
833 	{
834 		/*
835 		 * return the current state
836 		 */
837 
838 	compose:
839 		if (category != AST_LC_ALL)
840 			return (char*)locales[category]->name;
841 		if (!sp && !(sp = sfstropen()))
842 			return 0;
843 		for (i = 1; i < AST_LC_COUNT; i++)
844 			cat[i] = -1;
845 		for (i = 1, k = 0; i < AST_LC_COUNT; i++)
846 			if (cat[i] < 0)
847 			{
848 				k++;
849 				cat[i] = i;
850 				for (j = i + 1; j < AST_LC_COUNT; j++)
851 					if (locales[j] == locales[i])
852 						cat[j] = i;
853 			}
854 		if (k == 1)
855 			return (char*)locales[1]->name;
856 		for (i = 1; i < AST_LC_COUNT; i++)
857 			if (cat[i] >= 0 && !(locales[i]->flags & LC_default))
858 			{
859 				if (sfstrtell(sp))
860 					sfprintf(sp, ";");
861 				for (j = i, k = cat[i]; j < AST_LC_COUNT; j++)
862 					if (cat[j] == k)
863 					{
864 						cat[j] = -1;
865 						sfprintf(sp, "%s=", categories[j].name);
866 					}
867 				sfprintf(sp, "%s", locales[i]->name);
868 			}
869 		if (!sfstrtell(sp))
870 			return (char*)locales[0]->name;
871 		return sfstruse(sp);
872 	}
873 	if (!ast.locale.serial++)
874 		stropt(getenv("LC_OPTIONS"), options, sizeof(*options), setopt, NiL);
875 	if (!*locale)
876 	{
877 		if (!initialized)
878 		{
879 			char*	u;
880 			char	tmp[256];
881 
882 			/*
883 			 * initialize from the environment
884 			 * precedence determined by X/Open
885 			 */
886 
887 			u = 0;
888 			if (!(a = getenv("LC_ALL")) || !*a)
889 			{
890 				for (i = 1; i < AST_LC_COUNT; i++)
891 					if ((s = getenv(categories[i].name)) && *s)
892 					{
893 						if (streq(s, local) && (u || (u = native_locale(locale, tmp, sizeof(tmp)))))
894 							s = u;
895 						categories[i].prev = lcmake(s);
896 					}
897 				a = getenv("LANG");
898 			}
899 			if (a)
900 			{
901 				if (streq(a, local) && (u || (u = native_locale(locale, tmp, sizeof(tmp)))))
902 					a = u;
903 				if (composite(a, 1))
904 					a = 0;
905 			}
906 			p = 0;
907 			for (i = 1; i < AST_LC_COUNT; i++)
908 			{
909 				if (!categories[i].prev)
910 				{
911 					if (!p && !(p = lcmake(a)))
912 						break;
913 					categories[i].prev = p;
914 				}
915 				if (!single(i, categories[i].prev))
916 				{
917 					while (i--)
918 						single(i, NiL);
919 					return 0;
920 				}
921 			}
922 			if (ast.locale.set & AST_LC_debug)
923 				for (i = 1; i < AST_LC_COUNT; i++)
924 					sfprintf(sfstderr, "locale env  %17s %s\n", categories[i].name, locales[i]->name);
925 			initialized = 1;
926 		}
927 		goto compose;
928 	}
929 	else if (category != AST_LC_ALL)
930 		return single(category, lcmake(locale));
931 	else if (!(i = composite(locale, 0)))
932 	{
933 		if (!(p = lcmake(locale)))
934 			return 0;
935 		for (i = 1; i < AST_LC_COUNT; i++)
936 			if (!single(i, p))
937 			{
938 				while (i--)
939 					single(i, NiL);
940 				return 0;
941 			}
942 	}
943 	else if (i < 0)
944 		return 0;
945 	goto compose;
946 }
947