xref: /titanic_41/usr/src/lib/libast/common/comp/setlocale.c (revision 4eaa471005973e11a6110b69fe990530b3b95a38)
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  * 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", lc_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 	char*			o;
374 	register size_t		z;
375 	register int		w;
376 
377 	o = t;
378 	z = 0;
379 	if (e = t)
380 		e += n;
381 	while (s[0])
382 	{
383 		if ((((unsigned char*)s)[0] == DL0 || ((unsigned char*)s)[0] == DL1) && (w = s[1]) >= '0' && w <= ('0' + DC))
384 		{
385 			w -= '0';
386 			q = s + 2;
387 			r = q + w;
388 			while (q < r && *q)
389 				q++;
390 			if (*((unsigned char*)q) == DR0 || *((unsigned char*)q) == DR1)
391 			{
392 				if (t)
393 				{
394 					for (q = s + 2; q < r; q++)
395 						if (t < e)
396 							*t++ = debug_order[*q];
397 					while (w++ < DX)
398 						if (t < e)
399 							*t++ = 1;
400 				}
401 				s = r + 1;
402 				z += DX;
403 				continue;
404 			}
405 		}
406 		if ((s[0] == 'c' || s[0] == 'C') && (s[1] == 'h' || s[1] == 'H'))
407 		{
408 			if (t)
409 			{
410 				if (t < e)
411 					*t++ = debug_order[s[0]];
412 				if (t < e)
413 					*t++ = debug_order[s[1]];
414 				if (t < e)
415 					*t++ = 1;
416 				if (t < e)
417 					*t++ = 1;
418 			}
419 			s += 2;
420 			z += DX;
421 			continue;
422 		}
423 		if ((s[0] == 's' || s[0] == 'S') && (s[1] == 's' || s[1] == 'S') && (s[2] == 't' || s[2] == 'T'))
424 		{
425 			if (t)
426 			{
427 				if (t < e)
428 					*t++ = debug_order[s[0]];
429 				if (t < e)
430 					*t++ = debug_order[s[1]];
431 				if (t < e)
432 					*t++ = debug_order[s[2]];
433 				if (t < e)
434 					*t++ = 1;
435 			}
436 			s += 3;
437 			z += DX;
438 			continue;
439 		}
440 		if (t)
441 		{
442 			if (t < e)
443 				*t++ = debug_order[s[0]];
444 			if (t < e)
445 				*t++ = 1;
446 			if (t < e)
447 				*t++ = 1;
448 			if (t < e)
449 				*t++ = 1;
450 		}
451 		s++;
452 		z += DX;
453 	}
454 	if (!t)
455 		return z;
456 	if (t < e)
457 		*t = 0;
458 	return t - o;
459 }
460 
461 static int
462 debug_strcoll(const char* a, const char* b)
463 {
464 	char	ab[1024];
465 	char	bb[1024];
466 
467 	debug_strxfrm(ab, a, sizeof(ab) - 1);
468 	ab[sizeof(ab)-1] = 0;
469 	debug_strxfrm(bb, b, sizeof(bb) - 1);
470 	bb[sizeof(bb)-1] = 0;
471 	return strcmp(ab, bb);
472 }
473 
474 /*
475  * default locale
476  */
477 
478 static int
479 default_wcwidth(wchar_t w)
480 {
481 	return w >= 0 && w <= 255 && !iscntrl(w) ? 1 : -1;
482 }
483 
484 /*
485  * called when LC_COLLATE initialized or changes
486  */
487 
488 static int
489 set_collate(Lc_category_t* cp)
490 {
491 	if (locales[cp->internal]->flags & LC_debug)
492 	{
493 		ast.collate = debug_strcoll;
494 		ast.mb_xfrm = debug_strxfrm;
495 	}
496 	else if (locales[cp->internal]->flags & LC_default)
497 	{
498 		ast.collate = strcmp;
499 		ast.mb_xfrm = 0;
500 	}
501 	else
502 	{
503 		ast.collate = strcoll;
504 		ast.mb_xfrm = strxfrm;
505 	}
506 	return 0;
507 }
508 
509 /*
510  * workaround the interesting sjis that translates unshifted 7 bit ascii!
511  */
512 
513 #if _hdr_wchar && _typ_mbstate_t && _lib_mbrtowc
514 
515 #define mb_state_zero	((mbstate_t*)&ast.pad[sizeof(ast.pad)-2*sizeof(mbstate_t)])
516 #define mb_state	((mbstate_t*)&ast.pad[sizeof(ast.pad)-sizeof(mbstate_t)])
517 
518 static int
519 sjis_mbtowc(register wchar_t* p, register const char* s, size_t n)
520 {
521 	if (n && p && s && (*s == '\\' || *s == '~') && !memcmp(mb_state, mb_state_zero, sizeof(mbstate_t)))
522 	{
523 		*p = *s;
524 		return 1;
525 	}
526 	return mbrtowc(p, s, n, mb_state);
527 }
528 
529 #endif
530 
531 /*
532  * called when LC_CTYPE initialized or changes
533  */
534 
535 static int
536 set_ctype(Lc_category_t* cp)
537 {
538 	if (locales[cp->internal]->flags & LC_debug)
539 	{
540 		ast.mb_cur_max = DEBUG_MB_CUR_MAX;
541 		ast.mb_len = debug_mblen;
542 		ast.mb_towc = debug_mbtowc;
543 		ast.mb_width = debug_wcwidth;
544 		ast.mb_conv = debug_wctomb;
545 	}
546 	else if ((locales[cp->internal]->flags & LC_default) || (ast.mb_cur_max = MB_CUR_MAX) <= 1 || !(ast.mb_len = mblen) || !(ast.mb_towc = mbtowc))
547 	{
548 		ast.mb_cur_max = 1;
549 		ast.mb_len = 0;
550 		ast.mb_towc = 0;
551 		ast.mb_width = default_wcwidth;
552 		ast.mb_conv = 0;
553 	}
554 	else
555 	{
556 		if (!(ast.mb_width = wcwidth))
557 			ast.mb_width = default_wcwidth;
558 		ast.mb_conv = wctomb;
559 #ifdef mb_state
560 		{
561 			/*
562 			 * check for sjis that translates unshifted 7 bit ascii!
563 			 */
564 
565 			char*	s;
566 			char	buf[2];
567 
568 			mbinit();
569 			buf[1] = 0;
570 			*(s = buf) = '\\';
571 			if (mbchar(s) != buf[0])
572 			{
573 				memcpy(mb_state, mb_state_zero, sizeof(mbstate_t));
574 				ast.mb_towc = sjis_mbtowc;
575 			}
576 		}
577 #endif
578 	}
579 	return 0;
580 }
581 
582 /*
583  * called when LC_NUMERIC initialized or changes
584  */
585 
586 static int
587 set_numeric(Lc_category_t* cp)
588 {
589 	register int		category = cp->internal;
590 	struct lconv*		lp;
591 	Lc_numeric_t*		dp;
592 
593 	static Lc_numeric_t	default_numeric = { '.', -1 };
594 
595 	if (!LCINFO(category)->data)
596 	{
597 		if ((lp = localeconv()) && (dp = newof(0, Lc_numeric_t, 1, 0)))
598 		{
599 			dp->decimal = lp->decimal_point && *lp->decimal_point ? *(unsigned char*)lp->decimal_point : '.';
600 			dp->thousand = lp->thousands_sep && *lp->thousands_sep ? *(unsigned char*)lp->thousands_sep : -1;
601 		}
602 		else
603 			dp = &default_numeric;
604 		LCINFO(category)->data = (void*)dp;
605 		if (ast.locale.set & (AST_LC_debug|AST_LC_setlocale))
606 			sfprintf(sfstderr, "locale info %17s decimal '%c' thousands '%c'\n", lc_categories[category].name, dp->decimal, dp->thousand >= 0 ? dp->thousand : 'X');
607 	}
608 	return 0;
609 }
610 
611 /*
612  * this table is indexed by AST_LC_[A-Z]*
613  */
614 
615 Lc_category_t		lc_categories[] =
616 {
617 { "LC_ALL",           LC_ALL,           AST_LC_ALL,           0               },
618 { "LC_COLLATE",       LC_COLLATE,       AST_LC_COLLATE,       set_collate     },
619 { "LC_CTYPE",         LC_CTYPE,         AST_LC_CTYPE,         set_ctype       },
620 { "LC_MESSAGES",      LC_MESSAGES,      AST_LC_MESSAGES,      0               },
621 { "LC_MONETARY",      LC_MONETARY,      AST_LC_MONETARY,      0               },
622 { "LC_NUMERIC",       LC_NUMERIC,       AST_LC_NUMERIC,       set_numeric     },
623 { "LC_TIME",          LC_TIME,          AST_LC_TIME,          0               },
624 { "LC_IDENTIFICATION",LC_IDENTIFICATION,AST_LC_IDENTIFICATION,0               },
625 { "LC_ADDRESS",       LC_ADDRESS,       AST_LC_ADDRESS,       0               },
626 { "LC_NAME",          LC_NAME,          AST_LC_NAME,          0               },
627 { "LC_TELEPHONE",     LC_TELEPHONE,     AST_LC_TELEPHONE,     0               },
628 { "LC_XLITERATE",     LC_XLITERATE,     AST_LC_XLITERATE,     0               },
629 { "LC_MEASUREMENT",   LC_MEASUREMENT,   AST_LC_MEASUREMENT,   0               },
630 { "LC_PAPER",         LC_PAPER,         AST_LC_PAPER,         0               },
631 };
632 
633 static const Namval_t	options[] =
634 {
635 	"debug",		AST_LC_debug,
636 	"find",			AST_LC_find,
637 	"setlocale",		AST_LC_setlocale,
638 	"translate",		AST_LC_translate,
639 	0,			0
640 };
641 
642 /*
643  * called by stropt() to set options
644  */
645 
646 static int
647 setopt(void* a, const void* p, int n, const char* v)
648 {
649 	if (p)
650 	{
651 		if (n)
652 			ast.locale.set |= ((Namval_t*)p)->value;
653 		else
654 			ast.locale.set &= ~((Namval_t*)p)->value;
655 	}
656 	return 0;
657 }
658 
659 #if !_lib_setlocale
660 
661 #define setlocale(c,l)		default_setlocale(c,l)
662 
663 static char*
664 default_setlocale(int category, const char* locale)
665 {
666 	Lc_t*		lc;
667 
668 	if (locale)
669 	{
670 		if (!(lc = lcmake(locale)) || !(lc->flags & LC_default))
671 			return 0;
672 		locales[0]->flags &= ~lc->flags;
673 		locales[1]->flags &= ~lc->flags;
674 		return lc->name;
675 	}
676 	return (locales[1]->flags & (1<<category)) ? locales[1]->name : locales[0]->name;
677 }
678 
679 #endif
680 
681 /*
682  * set a single AST_LC_* locale category
683  * the caller must validate category
684  * lc==0 restores the previous state
685  */
686 
687 static char*
688 single(int category, Lc_t* lc)
689 {
690 	const char*	sys;
691 	int		i;
692 
693 	if (!lc && !(lc = lc_categories[category].prev))
694 		lc = lcmake(NiL);
695 	if (locales[category] != lc)
696 	{
697 		if (lc_categories[category].external == -lc_categories[category].internal)
698 		{
699 			sys = 0;
700 			for (i = 1; i < AST_LC_COUNT; i++)
701 				if (locales[i] == lc)
702 				{
703 					sys = (char*)lc->name;
704 					break;
705 				}
706 		}
707 		else if (lc->flags & (LC_debug|LC_local))
708 			sys = setlocale(lc_categories[category].external, lcmake(NiL)->name);
709 		else if (!(sys = setlocale(lc_categories[category].external, lc->name)) &&
710 			 (streq(lc->name, lc->code) || !(sys = setlocale(lc_categories[category].external, lc->code))) &&
711 			 !streq(lc->code, lc->language->code))
712 				sys = setlocale(lc_categories[category].external, lc->language->code);
713 		if (ast.locale.set & (AST_LC_debug|AST_LC_setlocale))
714 			sfprintf(sfstderr, "locale set  %17s %-24s %-24s\n", lc_categories[category].name, lc->name, sys);
715 		if (!sys)
716 		{
717 			/*
718 			 * check for local override
719 			 * currently this means an LC_MESSAGES dir exists
720 			 */
721 
722 			if (!(lc->flags & LC_checked))
723 			{
724 				char	path[PATH_MAX];
725 
726 				if (mcfind(path, lc->code, NiL, LC_MESSAGES, 0))
727 					lc->flags |= LC_local;
728 				lc->flags |= LC_checked;
729 			}
730 			if (!(lc->flags & LC_local))
731 				return 0;
732 			if (lc_categories[category].external != -lc_categories[category].internal)
733 				setlocale(lc_categories[category].external, lcmake(NiL)->name);
734 		}
735 		locales[category] = lc;
736 		if (lc_categories[category].setf && (*lc_categories[category].setf)(&lc_categories[category]))
737 		{
738 			locales[category] = lc_categories[category].prev;
739 			return 0;
740 		}
741 		if (lc->flags & LC_default)
742 			ast.locale.set &= ~(1<<category);
743 		else
744 			ast.locale.set |= (1<<category);
745 	}
746 	return (char*)lc->name;
747 }
748 
749 /*
750  * set composite AST_LC_ALL locale categories
751  * return <0:composite-error 0:not-composite >0:composite-ok
752  */
753 
754 static int
755 composite(register const char* s, int initialize)
756 {
757 	register const char*	t;
758 	register int		i;
759 	register int		j;
760 	register int		k;
761 	int			n;
762 	const char*		w;
763 	Lc_t*			p;
764 	int			cat[AST_LC_COUNT];
765 	int			stk[AST_LC_COUNT];
766 	char			buf[PATH_MAX / 2];
767 
768 	k = n = 0;
769 	while (s[0] == 'L' && s[1] == 'C' && s[2] == '_')
770 	{
771 		n++;
772 		j = 0;
773 		w = s;
774 		for (i = 1; i < AST_LC_COUNT; i++)
775 		{
776 			s = w;
777 			t = lc_categories[i].name;
778 			while (*t && *s++ == *t++);
779 			if (!*t && *s++ == '=')
780 			{
781 				cat[j++] = i;
782 				if (s[0] != 'L' || s[1] != 'C' || s[2] != '_')
783 					break;
784 				w = s;
785 				i = -1;
786 			}
787 		}
788 		for (s = w; *s && *s != '='; s++);
789 		if (!*s)
790 		{
791 			for (i = 0; i < k; i++)
792 				single(stk[i], NiL);
793 			return -1;
794 		}
795 		w = ++s;
796 		for (;;)
797 		{
798 			if (!*s)
799 			{
800 				p = lcmake(w);
801 				break;
802 			}
803 			else if (*s++ == ';')
804 			{
805 				if ((n = s - w - 1) >= sizeof(buf))
806 					n = sizeof(buf) - 1;
807 				memcpy(buf, w, n);
808 				buf[n] = 0;
809 				p = lcmake(buf);
810 				break;
811 			}
812 		}
813 		for (i = 0; i < j; i++)
814 			if (!initialize)
815 			{
816 				if (!single(cat[i], p))
817 				{
818 					for (i = 0; i < k; i++)
819 						single(stk[i], NiL);
820 					return -1;
821 				}
822 				stk[k++] = cat[i];
823 			}
824 			else if (!lc_categories[cat[i]].prev)
825 				lc_categories[cat[i]].prev = p;
826 	}
827 	while (s[0] == '/' && s[1] && n < AST_LC_COUNT)
828 	{
829 		n++;
830 		for (w = ++s; *s && *s != '/'; s++);
831 		if (!*s)
832 			p = lcmake(w);
833 		else
834 		{
835 			if ((j = s - w - 1) >= sizeof(buf))
836 				j = sizeof(buf) - 1;
837 			memcpy(buf, w, j);
838 			buf[j] = 0;
839 			p = lcmake(buf);
840 		}
841 		if (!initialize)
842 		{
843 			if (!single(n, p))
844 			{
845 				for (i = 1; i < n; i++)
846 					single(i, NiL);
847 				return -1;
848 			}
849 		}
850 		else if (!lc_categories[n].prev)
851 			lc_categories[n].prev = p;
852 	}
853 	return n;
854 }
855 
856 /*
857  * setlocale() intercept
858  */
859 
860 char*
861 _ast_setlocale(int category, const char* locale)
862 {
863 	register char*		s;
864 	register int		i;
865 	register int		j;
866 	int			k;
867 	char*			a;
868 	Lc_t*			p;
869 	int			cat[AST_LC_COUNT];
870 
871 	static Sfio_t*		sp;
872 	static int		initialized;
873 	static char		local[] = "local";
874 
875 	if ((category = lcindex(category, 0)) < 0)
876 		return 0;
877 	if (!locale)
878 	{
879 		/*
880 		 * return the current state
881 		 */
882 
883 	compose:
884 		if (category != AST_LC_ALL)
885 			return (char*)locales[category]->name;
886 		if (!sp && !(sp = sfstropen()))
887 			return 0;
888 		for (i = 1; i < AST_LC_COUNT; i++)
889 			cat[i] = -1;
890 		for (i = 1, k = 0; i < AST_LC_COUNT; i++)
891 			if (cat[i] < 0)
892 			{
893 				k++;
894 				cat[i] = i;
895 				for (j = i + 1; j < AST_LC_COUNT; j++)
896 					if (locales[j] == locales[i])
897 						cat[j] = i;
898 			}
899 		if (k == 1)
900 			return (char*)locales[1]->name;
901 		for (i = 1; i < AST_LC_COUNT; i++)
902 			if (cat[i] >= 0 && !(locales[i]->flags & LC_default))
903 			{
904 				if (sfstrtell(sp))
905 					sfprintf(sp, ";");
906 				for (j = i, k = cat[i]; j < AST_LC_COUNT; j++)
907 					if (cat[j] == k)
908 					{
909 						cat[j] = -1;
910 						sfprintf(sp, "%s=", lc_categories[j].name);
911 					}
912 				sfprintf(sp, "%s", locales[i]->name);
913 			}
914 		if (!sfstrtell(sp))
915 			return (char*)locales[0]->name;
916 		return sfstruse(sp);
917 	}
918 	if (!ast.locale.serial++)
919 		stropt(getenv("LC_OPTIONS"), options, sizeof(*options), setopt, NiL);
920 	if (*locale)
921 		p = lcmake(locale);
922 	else if (!initialized)
923 	{
924 		char*	u;
925 		char	tmp[256];
926 
927 		/*
928 		 * initialize from the environment
929 		 * precedence determined by X/Open
930 		 */
931 
932 		u = 0;
933 		if (!(a = getenv("LC_ALL")) || !*a)
934 		{
935 			for (i = 1; i < AST_LC_COUNT; i++)
936 				if ((s = getenv(lc_categories[i].name)) && *s)
937 				{
938 					if (streq(s, local) && (u || (u = native_locale(locale, tmp, sizeof(tmp)))))
939 						s = u;
940 					lc_categories[i].prev = lcmake(s);
941 				}
942 			a = getenv("LANG");
943 		}
944 		if (a)
945 		{
946 			if (streq(a, local) && (u || (u = native_locale(locale, tmp, sizeof(tmp)))))
947 				a = u;
948 			if (composite(a, 1))
949 				a = 0;
950 		}
951 		p = 0;
952 		for (i = 1; i < AST_LC_COUNT; i++)
953 		{
954 			if (!lc_categories[i].prev)
955 			{
956 				if (!p && !(p = lcmake(a)))
957 					break;
958 				lc_categories[i].prev = p;
959 			}
960 			if (!single(i, lc_categories[i].prev))
961 			{
962 				while (i--)
963 					single(i, NiL);
964 				return 0;
965 			}
966 		}
967 		if (ast.locale.set & AST_LC_debug)
968 			for (i = 1; i < AST_LC_COUNT; i++)
969 				sfprintf(sfstderr, "locale env  %17s %s\n", lc_categories[i].name, locales[i]->name);
970 		initialized = 1;
971 		goto compose;
972 	}
973 	else if (!(p = lc_categories[category].prev))
974 		p = lcmake("C");
975 	if (category != AST_LC_ALL)
976 		return single(category, p);
977 	else if (!(i = composite(locale, 0)))
978 	{
979 		if (!p)
980 			return 0;
981 		for (i = 1; i < AST_LC_COUNT; i++)
982 			if (!single(i, p))
983 			{
984 				while (i--)
985 					single(i, NiL);
986 				return 0;
987 			}
988 	}
989 	else if (i < 0)
990 		return 0;
991 	goto compose;
992 }
993