dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / lib / libast / common / comp / setlocale.c
blob3d13013da2f4d9f8da40a61ce89d6b1511e4b1ae
1 /***********************************************************************
2 * *
3 * This software is part of the ast package *
4 * Copyright (c) 1985-2010 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
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
31 #include <ast_standards.h>
33 #include "lclib.h"
35 #include <ast_wchar.h>
36 #include <ctype.h>
37 #include <mc.h>
38 #include <namval.h>
40 #if ( _lib_wcwidth || _lib_wctomb ) && _hdr_wctype
41 #include <wctype.h>
42 #endif
44 #if _lib_wcwidth
45 #undef wcwidth
46 #else
47 #define wcwidth 0
48 #endif
50 #if _lib_wctomb
51 #undef wctomb
52 #else
53 #define wctomb 0
54 #endif
56 #ifdef mblen
57 #undef mblen
58 extern int mblen(const char*, size_t);
59 #endif
61 #undef mbtowc
62 #undef setlocale
63 #undef strcmp
64 #undef strcoll
65 #undef strxfrm
66 #undef valid
68 #ifndef AST_LC_CANONICAL
69 #define AST_LC_CANONICAL LC_abbreviated
70 #endif
72 #ifndef AST_LC_test
73 #define AST_LC_test (1L<<27)
74 #endif
76 #if _UWIN
78 #include <ast_windows.h>
80 #undef _lib_setlocale
81 #define _lib_setlocale 1
83 #define setlocale(c,l) native_setlocale(c,l)
85 extern char* uwin_setlocale(int, const char*);
88 * convert locale to native locale name in buf
91 static char*
92 native_locale(const char* locale, char* buf, size_t siz)
94 Lc_t* lc;
95 const Lc_attribute_list_t* ap;
96 int i;
97 unsigned long lcid;
98 unsigned long lang;
99 unsigned long ctry;
100 char lbuf[128];
101 char cbuf[128];
103 if (locale && *locale)
105 if (!(lc = lcmake(locale)))
106 return 0;
107 lang = lc->language->index;
108 ctry = 0;
109 for (ap = lc->attributes; ap; ap = ap->next)
110 if (ctry = ap->attribute->index)
111 break;
112 if (!ctry)
114 for (i = 0; i < elementsof(lc->territory->languages); i++)
115 if (lc->territory->languages[i] == lc->language)
117 ctry = lc->territory->indices[i];
118 break;
120 if (!ctry)
122 if (!lang)
123 return 0;
124 ctry = SUBLANG_DEFAULT;
127 lcid = MAKELCID(MAKELANGID(lang, ctry), SORT_DEFAULT);
129 else
130 lcid = GetUserDefaultLCID();
131 if (GetLocaleInfo(lcid, LOCALE_SENGLANGUAGE, lbuf, sizeof(lbuf)) <= 0 ||
132 GetLocaleInfo(lcid, LOCALE_SENGCOUNTRY, cbuf, sizeof(cbuf)) <= 0)
133 return 0;
134 if (lc->charset->ms)
135 sfsprintf(buf, siz, "%s_%s.%s", lbuf, cbuf, lc->charset->ms);
136 else
137 sfsprintf(buf, siz, "%s_%s", lbuf, cbuf);
138 return buf;
142 * locale!=0 here
145 static char*
146 native_setlocale(int category, const char* locale)
148 char* usr;
149 char* sys;
150 char buf[256];
152 if (!(usr = native_locale(locale, buf, sizeof(buf))))
153 return 0;
156 * win32 doesn't have LC_MESSAGES
159 if (category == LC_MESSAGES)
160 return (char*)locale;
161 sys = uwin_setlocale(category, usr);
162 if (ast.locale.set & AST_LC_debug)
163 sfprintf(sfstderr, "locale uwin %17s %-24s %-24s\n", lc_categories[lcindex(category, 0)].name, usr, sys);
164 return sys;
167 #else
169 #define native_locale(a,b,c) ((char*)0)
171 #endif
174 * LC_COLLATE and LC_CTYPE native support
177 #if !_lib_mbtowc || MB_LEN_MAX <= 1
178 #define mblen 0
179 #define mbtowc 0
180 #endif
182 #if !_lib_strcoll
183 #define strcoll 0
184 #endif
186 #if !_lib_strxfrm
187 #define strxfrm 0
188 #endif
191 * LC_COLLATE and LC_CTYPE debug support
193 * mutibyte debug encoding
195 * DL0 [ '0' .. '4' ] c1 ... c4 DR0
196 * DL1 [ '0' .. '4' ] c1 ... c4 DR1
198 * with these ligatures
200 * ch CH sst SST
202 * and private collation order
204 * wide character display width is the low order 3 bits
205 * wctomb() uses DL1...DR1
208 #define DEBUG_MB_CUR_MAX 7
210 #if DEBUG_MB_CUR_MAX < MB_LEN_MAX
211 #undef DEBUG_MB_CUR_MAX
212 #define DEBUG_MB_CUR_MAX MB_LEN_MAX
213 #endif
215 #define DL0 '<'
216 #define DL1 0xab /* 8-bit mini << on xterm */
217 #define DR0 '>'
218 #define DR1 0xbb /* 8-bit mini >> on xterm */
220 #define DB ((int)sizeof(wchar_t)*8-1)
221 #define DC 7 /* wchar_t embedded char bits */
222 #define DX (DB/DC) /* wchar_t max embedded chars */
223 #define DZ (DB-DX*DC+1) /* wchar_t embedded size bits */
224 #define DD 3 /* # mb delimiter chars <n...> */
226 static unsigned char debug_order[] =
228 0, 1, 2, 3, 4, 5, 6, 7,
229 8, 9, 10, 11, 12, 13, 14, 15,
230 16, 17, 18, 19, 20, 21, 22, 23,
231 24, 25, 26, 27, 28, 29, 30, 31,
232 99, 100, 101, 102, 98, 103, 104, 105,
233 106, 107, 108, 43, 109, 44, 42, 110,
234 32, 33, 34, 35, 36, 37, 38, 39,
235 40, 41, 111, 112, 113, 114, 115, 116,
236 117, 71, 72, 73, 74, 75, 76, 77,
237 78, 79, 80, 81, 82, 83, 84, 85,
238 86, 87, 88, 89, 90, 91, 92, 93,
239 94, 95, 96, 118, 119, 120, 121, 97,
240 122, 45, 46, 47, 48, 49, 50, 51,
241 52, 53, 54, 55, 56, 57, 58, 59,
242 60, 61, 62, 63, 64, 65, 66, 67,
243 68, 69, 70, 123, 124, 125, 126, 127,
244 128, 129, 130, 131, 132, 133, 134, 135,
245 136, 137, 138, 139, 140, 141, 142, 143,
246 144, 145, 146, 147, 148, 149, 150, 151,
247 152, 153, 154, 155, 156, 157, 158, 159,
248 160, 161, 162, 163, 164, 165, 166, 167,
249 168, 169, 170, 171, 172, 173, 174, 175,
250 176, 177, 178, 179, 180, 181, 182, 183,
251 184, 185, 186, 187, 188, 189, 190, 191,
252 192, 193, 194, 195, 196, 197, 198, 199,
253 200, 201, 202, 203, 204, 205, 206, 207,
254 208, 209, 210, 211, 212, 213, 214, 215,
255 216, 217, 218, 219, 220, 221, 222, 223,
256 224, 225, 226, 227, 228, 229, 230, 231,
257 232, 233, 234, 235, 236, 237, 238, 239,
258 240, 241, 242, 243, 244, 245, 246, 247,
259 248, 249, 250, 251, 252, 253, 254, 255,
262 static int
263 debug_mbtowc(register wchar_t* p, register const char* s, size_t n)
265 register const char* q;
266 register const char* r;
267 register int w;
268 register int dr;
269 wchar_t c;
271 if (n < 1)
272 return -1;
273 if (!s || !*s)
274 return 0;
275 switch (((unsigned char*)s)[0])
277 case DL0:
278 dr = DR0;
279 break;
280 case DL1:
281 dr = DR1;
282 break;
283 default:
284 if (p)
285 *p = ((unsigned char*)s)[0] & ((1<<DC)-1);
286 return 1;
288 if (n < 2)
289 return -1;
290 if ((w = ((unsigned char*)s)[1]) == ((unsigned char*)s)[0])
292 if (p)
293 *p = w;
294 return 2;
296 if (w < '0' || w > ('0' + DX))
297 return -1;
298 if ((w -= '0' - DD) > n)
299 return -1;
300 r = s + w - 1;
301 q = s += 2;
302 while (q < r && *q)
303 q++;
304 if (q != r || *((unsigned char*)q) != dr)
305 return -1;
306 if (p)
308 c = 0;
309 while (--q >= s)
311 c <<= DC;
312 c |= *((unsigned char*)q);
314 c <<= DZ;
315 c |= w - DD;
316 *p = c;
318 return w;
321 static int
322 debug_wctomb(char* s, wchar_t c)
324 int w;
325 int i;
326 int k;
328 w = 0;
329 if (c >= 0 && c <= UCHAR_MAX)
331 w++;
332 if (s)
333 *s = c;
335 else if ((i = c & ((1<<DZ)-1)) > DX)
336 return -1;
337 else
339 w++;
340 if (s)
341 *s++ = DL1;
342 c >>= DZ;
343 w++;
344 if (s)
345 *s++ = i + '0';
346 while (i--)
348 w++;
349 if (s)
350 *s++ = (k = c & ((1<<DC)-1)) ? k : '?';
351 c >>= DC;
353 w++;
354 if (s)
355 *s++ = DR1;
357 return w;
360 static int
361 debug_mblen(const char* s, size_t n)
363 return debug_mbtowc(NiL, s, n);
366 static int
367 debug_wcwidth(wchar_t c)
369 if (c >= 0 && c <= UCHAR_MAX)
370 return 1;
371 if ((c &= ((1<<DZ)-1)) > DX)
372 return -1;
373 return c + DD;
376 static size_t
377 debug_strxfrm(register char* t, register const char* s, size_t n)
379 register const char* q;
380 register const char* r;
381 register char* e;
382 char* o;
383 register size_t z;
384 register int w;
386 o = t;
387 z = 0;
388 if (e = t)
389 e += n;
390 while (s[0])
392 if ((((unsigned char*)s)[0] == DL0 || ((unsigned char*)s)[0] == DL1) && (w = s[1]) >= '0' && w <= ('0' + DC))
394 w -= '0';
395 q = s + 2;
396 r = q + w;
397 while (q < r && *q)
398 q++;
399 if (*((unsigned char*)q) == DR0 || *((unsigned char*)q) == DR1)
401 if (t)
403 for (q = s + 2; q < r; q++)
404 if (t < e)
405 *t++ = debug_order[*q];
406 while (w++ < DX)
407 if (t < e)
408 *t++ = 1;
410 s = r + 1;
411 z += DX;
412 continue;
415 if ((s[0] == 'c' || s[0] == 'C') && (s[1] == 'h' || s[1] == 'H'))
417 if (t)
419 if (t < e)
420 *t++ = debug_order[s[0]];
421 if (t < e)
422 *t++ = debug_order[s[1]];
423 if (t < e)
424 *t++ = 1;
425 if (t < e)
426 *t++ = 1;
428 s += 2;
429 z += DX;
430 continue;
432 if ((s[0] == 's' || s[0] == 'S') && (s[1] == 's' || s[1] == 'S') && (s[2] == 't' || s[2] == 'T'))
434 if (t)
436 if (t < e)
437 *t++ = debug_order[s[0]];
438 if (t < e)
439 *t++ = debug_order[s[1]];
440 if (t < e)
441 *t++ = debug_order[s[2]];
442 if (t < e)
443 *t++ = 1;
445 s += 3;
446 z += DX;
447 continue;
449 if (t)
451 if (t < e)
452 *t++ = debug_order[s[0]];
453 if (t < e)
454 *t++ = 1;
455 if (t < e)
456 *t++ = 1;
457 if (t < e)
458 *t++ = 1;
460 s++;
461 z += DX;
463 if (!t)
464 return z;
465 if (t < e)
466 *t = 0;
467 return t - o;
470 static int
471 debug_strcoll(const char* a, const char* b)
473 char ab[1024];
474 char bb[1024];
476 debug_strxfrm(ab, a, sizeof(ab) - 1);
477 ab[sizeof(ab)-1] = 0;
478 debug_strxfrm(bb, b, sizeof(bb) - 1);
479 bb[sizeof(bb)-1] = 0;
480 return strcmp(ab, bb);
484 * default locale
487 static int
488 default_wcwidth(wchar_t w)
490 return w >= 0 && w <= 255 && !iscntrl(w) ? 1 : -1;
494 * called when LC_COLLATE initialized or changes
497 static int
498 set_collate(Lc_category_t* cp)
500 if (locales[cp->internal]->flags & LC_debug)
502 ast.collate = debug_strcoll;
503 ast.mb_xfrm = debug_strxfrm;
505 else if (locales[cp->internal]->flags & LC_default)
507 ast.collate = strcmp;
508 ast.mb_xfrm = 0;
510 else
512 ast.collate = strcoll;
513 ast.mb_xfrm = strxfrm;
515 return 0;
519 * workaround the interesting sjis that translates unshifted 7 bit ascii!
522 #if _hdr_wchar && _typ_mbstate_t && _lib_mbrtowc
524 #define mb_state_zero ((mbstate_t*)&ast.pad[sizeof(ast.pad)-2*sizeof(mbstate_t)])
525 #define mb_state ((mbstate_t*)&ast.pad[sizeof(ast.pad)-sizeof(mbstate_t)])
527 static int
528 sjis_mbtowc(register wchar_t* p, register const char* s, size_t n)
530 if (n && p && s && (*s == '\\' || *s == '~') && !memcmp(mb_state, mb_state_zero, sizeof(mbstate_t)))
532 *p = *s;
533 return 1;
535 return mbrtowc(p, s, n, mb_state);
538 #endif
540 #define utf8_wctomb wctomb
542 static const uint32_t utf8mask[] =
544 0x00000000,
545 0x00000000,
546 0xffffff80,
547 0xfffff800,
548 0xffff0000,
549 0xffe00000,
550 0xfc000000,
553 static const signed char utf8tab[256] =
555 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
556 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
557 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
558 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
559 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
560 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
561 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
562 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
563 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
564 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
565 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
566 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
567 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
568 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
569 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
570 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6,-1,-1,
573 static int
574 utf8_mbtowc(wchar_t* wp, const char* str, size_t n)
576 register unsigned char* sp = (unsigned char*)str;
577 register int m;
578 register int i;
579 register int c;
580 register wchar_t w = 0;
582 if (!sp || !n)
583 return 0;
584 if ((m = utf8tab[*sp]) > 0)
586 if (m > n)
587 return -1;
588 if (wp)
590 if (m == 1)
592 *wp = *sp;
593 return 1;
595 w = *sp & ((1<<(8-m))-1);
596 for (i = m - 1; i > 0; i--)
598 c = *++sp;
599 if ((c&0xc0) != 0x80)
600 goto invalid;
601 w = (w<<6) | (c&0x3f);
603 if (!(utf8mask[m] & w) || w >= 0xd800 && (w <= 0xdfff || w >= 0xfffe && w <= 0xffff))
604 goto invalid;
605 *wp = w;
607 return m;
609 if (!*sp)
610 return 0;
611 invalid:
612 #ifdef EILSEQ
613 errno = EILSEQ;
614 #endif
615 ast.mb_sync = (const char*)sp - str;
616 return -1;
619 static int
620 utf8_mblen(const char* str, size_t n)
622 wchar_t w;
624 return utf8_mbtowc(&w, str, n);
628 * called when LC_CTYPE initialized or changes
631 static int
632 set_ctype(Lc_category_t* cp)
634 ast.mb_sync = 0;
635 if (locales[cp->internal]->flags & LC_debug)
637 ast.mb_cur_max = DEBUG_MB_CUR_MAX;
638 ast.mb_len = debug_mblen;
639 ast.mb_towc = debug_mbtowc;
640 ast.mb_width = debug_wcwidth;
641 ast.mb_conv = debug_wctomb;
643 else if ((locales[cp->internal]->flags & LC_default) || (ast.mb_cur_max = MB_CUR_MAX) <= 1 || !(ast.mb_len = mblen) || !(ast.mb_towc = mbtowc))
645 ast.mb_cur_max = 1;
646 ast.mb_len = 0;
647 ast.mb_towc = 0;
648 ast.mb_width = default_wcwidth;
649 ast.mb_conv = 0;
651 else if ((locales[cp->internal]->flags & LC_utf8) && !(ast.locale.set & AST_LC_test))
653 ast.mb_cur_max = 6;
654 ast.mb_len = utf8_mblen;
655 ast.mb_towc = utf8_mbtowc;
656 if (!(ast.mb_width = wcwidth))
657 ast.mb_width = default_wcwidth;
658 ast.mb_conv = utf8_wctomb;
660 else
662 if (!(ast.mb_width = wcwidth))
663 ast.mb_width = default_wcwidth;
664 ast.mb_conv = wctomb;
665 #ifdef mb_state
668 * check for sjis that translates unshifted 7 bit ascii!
671 char* s;
672 char buf[2];
674 mbinit();
675 buf[1] = 0;
676 *(s = buf) = '\\';
677 if (mbchar(s) != buf[0])
679 memcpy(mb_state, mb_state_zero, sizeof(mbstate_t));
680 ast.mb_towc = sjis_mbtowc;
683 #endif
685 if (ast.locale.set & (AST_LC_debug|AST_LC_setlocale))
686 sfprintf(sfstderr, "locale info %17s MB_CUR_MAX=%d%s%s%s%s\n"
687 , cp->name
688 , ast.mb_cur_max
689 , ast.mb_len == debug_mblen ? " debug_mblen" : ast.mb_len == mblen ? " mblen" : ""
690 , ast.mb_towc == debug_mbtowc ? " debug_mbtowc" : ast.mb_towc == mbtowc ? " mbtowc"
691 #ifdef mb_state
692 : ast.mb_towc == sjis_mbtowc ? " sjis_mbtowc"
693 #endif
694 : ""
695 , ast.mb_width == debug_wcwidth ? " debug_wcwidth" : ast.mb_width == wcwidth ? " wcwidth" : ast.mb_width == default_wcwidth ? " default_wcwidth" : ""
696 , ast.mb_conv == debug_wctomb ? " debug_wctomb" : ast.mb_conv == wctomb ? " wctomb" : ""
698 return 0;
702 * called when LC_NUMERIC initialized or changes
705 static int
706 set_numeric(Lc_category_t* cp)
708 register int category = cp->internal;
709 struct lconv* lp;
710 Lc_numeric_t* dp;
712 static Lc_numeric_t default_numeric = { '.', -1 };
714 if (!LCINFO(category)->data)
716 if ((lp = localeconv()) && (dp = newof(0, Lc_numeric_t, 1, 0)))
718 dp->decimal = lp->decimal_point && *lp->decimal_point ? *(unsigned char*)lp->decimal_point : '.';
719 dp->thousand = lp->thousands_sep && *lp->thousands_sep ? *(unsigned char*)lp->thousands_sep : -1;
721 else
722 dp = &default_numeric;
723 LCINFO(category)->data = (void*)dp;
724 if (ast.locale.set & (AST_LC_debug|AST_LC_setlocale))
725 sfprintf(sfstderr, "locale info %17s decimal '%c' thousands '%c'\n", lc_categories[category].name, dp->decimal, dp->thousand >= 0 ? dp->thousand : 'X');
727 return 0;
731 * this table is indexed by AST_LC_[A-Z]*
734 Lc_category_t lc_categories[] =
736 { "LC_ALL", LC_ALL, AST_LC_ALL, 0 },
737 { "LC_COLLATE", LC_COLLATE, AST_LC_COLLATE, set_collate },
738 { "LC_CTYPE", LC_CTYPE, AST_LC_CTYPE, set_ctype },
739 { "LC_MESSAGES", LC_MESSAGES, AST_LC_MESSAGES, 0 },
740 { "LC_MONETARY", LC_MONETARY, AST_LC_MONETARY, 0 },
741 { "LC_NUMERIC", LC_NUMERIC, AST_LC_NUMERIC, set_numeric },
742 { "LC_TIME", LC_TIME, AST_LC_TIME, 0 },
743 { "LC_IDENTIFICATION",LC_IDENTIFICATION,AST_LC_IDENTIFICATION,0 },
744 { "LC_ADDRESS", LC_ADDRESS, AST_LC_ADDRESS, 0 },
745 { "LC_NAME", LC_NAME, AST_LC_NAME, 0 },
746 { "LC_TELEPHONE", LC_TELEPHONE, AST_LC_TELEPHONE, 0 },
747 { "LC_XLITERATE", LC_XLITERATE, AST_LC_XLITERATE, 0 },
748 { "LC_MEASUREMENT", LC_MEASUREMENT, AST_LC_MEASUREMENT, 0 },
749 { "LC_PAPER", LC_PAPER, AST_LC_PAPER, 0 },
752 static Lc_t* lang;
753 static Lc_t* lc_all;
755 typedef struct Unamval_s
757 char* name;
758 unsigned int value;
759 } Unamval_t;
761 static const Unamval_t options[] =
763 "debug", AST_LC_debug,
764 "find", AST_LC_find,
765 "setlocale", AST_LC_setlocale,
766 "test", AST_LC_test,
767 "translate", AST_LC_translate,
768 0, 0
772 * called by stropt() to set options
775 static int
776 setopt(void* a, const void* p, int n, const char* v)
778 if (p)
780 if (n)
781 ast.locale.set |= ((Unamval_t*)p)->value;
782 else
783 ast.locale.set &= ~((Unamval_t*)p)->value;
785 return 0;
788 #if !_lib_setlocale
790 #define setlocale(c,l) default_setlocale(c,l)
792 static char*
793 default_setlocale(int category, const char* locale)
795 Lc_t* lc;
797 if (locale)
799 if (!(lc = lcmake(locale)) || !(lc->flags & LC_default))
800 return 0;
801 locales[0]->flags &= ~lc->flags;
802 locales[1]->flags &= ~lc->flags;
803 return lc->name;
805 return (locales[1]->flags & (1<<category)) ? locales[1]->name : locales[0]->name;
808 #endif
811 * set a single AST_LC_* locale category
812 * the caller must validate category
813 * lc==0 restores the previous state
816 static char*
817 single(int category, Lc_t* lc, unsigned int flags)
819 const char* sys;
820 int i;
822 if (flags & (LC_setenv|LC_setlocale))
824 if (!(ast.locale.set & AST_LC_internal))
825 lc_categories[category].prev = lc;
826 if ((flags & LC_setenv) && lc_all && locales[category])
827 return (char*)locales[category]->name;
829 if (!lc && (!(lc_categories[category].flags & LC_setlocale) || !(lc = lc_categories[category].prev)) && !(lc = lc_all) && !(lc = lc_categories[category].prev) && !(lc = lang))
830 lc = lcmake(NiL);
831 sys = 0;
832 if (locales[category] != lc)
834 if (lc_categories[category].external == -lc_categories[category].internal)
836 for (i = 1; i < AST_LC_COUNT; i++)
837 if (locales[i] == lc)
839 sys = (char*)lc->name;
840 break;
843 else if (lc->flags & (LC_debug|LC_local))
844 sys = setlocale(lc_categories[category].external, lcmake(NiL)->name);
845 else if (!(sys = setlocale(lc_categories[category].external, lc->name)) &&
846 (streq(lc->name, lc->code) || !(sys = setlocale(lc_categories[category].external, lc->code))) &&
847 !streq(lc->code, lc->language->code))
848 sys = setlocale(lc_categories[category].external, lc->language->code);
849 if (!sys)
852 * check for local override
853 * currently this means an LC_MESSAGES dir exists
856 if (!(lc->flags & LC_checked))
858 char path[PATH_MAX];
860 if (mcfind(path, lc->code, NiL, LC_MESSAGES, 0))
861 lc->flags |= LC_local;
862 lc->flags |= LC_checked;
864 if (!(lc->flags & LC_local))
865 return 0;
866 if (lc_categories[category].external != -lc_categories[category].internal)
867 setlocale(lc_categories[category].external, lcmake(NiL)->name);
869 locales[category] = lc;
870 if (lc_categories[category].setf && (*lc_categories[category].setf)(&lc_categories[category]))
872 locales[category] = lc_categories[category].prev;
873 return 0;
875 if ((lc->flags & LC_default) || category == AST_LC_MESSAGES && lc->name[0] == 'e' && lc->name[1] == 'n' && (lc->name[2] == 0 || lc->name[2] == '_' && lc->name[3] == 'U'))
876 ast.locale.set &= ~(1<<category);
877 else
878 ast.locale.set |= (1<<category);
880 else if (lc_categories[category].flags ^ flags)
882 lc_categories[category].flags &= ~(LC_setenv|LC_setlocale);
883 lc_categories[category].flags |= flags;
885 else
886 return (char*)lc->name;
887 if ((ast.locale.set & (AST_LC_debug|AST_LC_setlocale)) && !(ast.locale.set & AST_LC_internal))
888 sfprintf(sfstderr, "locale set %17s %16s %16s %16s %s%s\n", lc_categories[category].name, lc->name, sys, lc_categories[category].prev ? lc_categories[category].prev->name : NiL, (lc_categories[category].flags & LC_setlocale) ? "[setlocale]" : "", (lc_categories[category].flags & LC_setenv) ? "[setenv]" : "");
889 return (char*)lc->name;
893 * set composite AST_LC_ALL locale categories
894 * return <0:composite-error 0:not-composite >0:composite-ok
897 static int
898 composite(register const char* s, int initialize)
900 register const char* t;
901 register int i;
902 register int j;
903 register int k;
904 int n;
905 int m;
906 const char* w;
907 Lc_t* p;
908 int cat[AST_LC_COUNT];
909 int stk[AST_LC_COUNT];
910 char buf[PATH_MAX / 2];
912 k = n = 0;
913 while (s[0] == 'L' && s[1] == 'C' && s[2] == '_')
915 n++;
916 j = 0;
917 w = s;
918 for (i = 1; i < AST_LC_COUNT; i++)
920 s = w;
921 t = lc_categories[i].name;
922 while (*t && *s++ == *t++);
923 if (!*t && *s++ == '=')
925 cat[j++] = i;
926 if (s[0] != 'L' || s[1] != 'C' || s[2] != '_')
927 break;
928 w = s;
929 i = -1;
932 for (s = w; *s && *s != '='; s++);
933 if (!*s)
935 for (i = 0; i < k; i++)
936 single(stk[i], NiL, 0);
937 return -1;
939 w = ++s;
940 for (;;)
942 if (!*s)
944 p = lcmake(w);
945 break;
947 else if (*s++ == ';')
949 if ((m = s - w - 1) >= sizeof(buf))
950 m = sizeof(buf) - 1;
951 memcpy(buf, w, m);
952 buf[m] = 0;
953 p = lcmake(buf);
954 break;
957 for (i = 0; i < j; i++)
958 if (!initialize)
960 if (!single(cat[i], p, 0))
962 for (i = 0; i < k; i++)
963 single(stk[i], NiL, 0);
964 return -1;
966 stk[k++] = cat[i];
968 else if (!lc_categories[cat[i]].prev && !(ast.locale.set & AST_LC_internal))
969 lc_categories[cat[i]].prev = p;
971 while (s[0] == '/' && s[1] && n < (AST_LC_COUNT - 1))
973 n++;
974 for (w = ++s; *s && *s != '/'; s++);
975 if (!*s)
976 p = lcmake(w);
977 else
979 if ((j = s - w - 1) >= sizeof(buf))
980 j = sizeof(buf) - 1;
981 memcpy(buf, w, j);
982 buf[j] = 0;
983 p = lcmake(buf);
985 if (!initialize)
987 if (!single(n, p, 0))
989 for (i = 1; i < n; i++)
990 single(i, NiL, 0);
991 return -1;
994 else if (!lc_categories[n].prev && !(ast.locale.set & AST_LC_internal))
995 lc_categories[n].prev = p;
997 return n;
1001 * setlocale() intercept
1003 * locale:
1004 * 0 query
1005 * "" initialize from environment (if LC_ALL)
1006 * "" AST_LC_setenv: value unset (defer to LANG)
1007 * "*" AST_LC_setenv: value set (defer to LC_ALL)
1008 * * set (override LC_ALL)
1011 char*
1012 _ast_setlocale(int category, const char* locale)
1014 register char* s;
1015 register int i;
1016 register int j;
1017 int k;
1018 int f;
1019 Lc_t* p;
1020 int cat[AST_LC_COUNT];
1022 static Sfio_t* sp;
1023 static int initialized;
1024 static const char local[] = "local";
1026 if ((category = lcindex(category, 0)) < 0)
1027 return 0;
1028 if (!locale)
1031 * return the current state
1034 compose:
1035 if (category != AST_LC_ALL && category != AST_LC_LANG)
1036 return (char*)locales[category]->name;
1037 if (!sp && !(sp = sfstropen()))
1038 return 0;
1039 for (i = 1; i < AST_LC_COUNT; i++)
1040 cat[i] = -1;
1041 for (i = 1, k = 0; i < AST_LC_COUNT; i++)
1042 if (cat[i] < 0)
1044 k++;
1045 cat[i] = i;
1046 for (j = i + 1; j < AST_LC_COUNT; j++)
1047 if (locales[j] == locales[i])
1048 cat[j] = i;
1050 if (k == 1)
1051 return (char*)locales[1]->name;
1052 for (i = 1; i < AST_LC_COUNT; i++)
1053 if (cat[i] >= 0 && !(locales[i]->flags & LC_default))
1055 if (sfstrtell(sp))
1056 sfprintf(sp, ";");
1057 for (j = i, k = cat[i]; j < AST_LC_COUNT; j++)
1058 if (cat[j] == k)
1060 cat[j] = -1;
1061 sfprintf(sp, "%s=", lc_categories[j].name);
1063 sfprintf(sp, "%s", locales[i]->name);
1065 if (!sfstrtell(sp))
1066 return (char*)locales[0]->name;
1067 return sfstruse(sp);
1069 if (!ast.locale.serial++)
1071 stropt(getenv("LC_OPTIONS"), options, sizeof(*options), setopt, NiL);
1072 initialized = 0;
1074 if ((ast.locale.set & (AST_LC_debug|AST_LC_setlocale)) && !(ast.locale.set & AST_LC_internal))
1075 sfprintf(sfstderr, "locale user %17s %16s %s%s\n", category == AST_LC_LANG ? "LANG" : lc_categories[category].name, locale && !*locale ? "''" : locale, initialized ? "" : "[initial]", (ast.locale.set & AST_LC_setenv) ? "[setenv]" : "");
1076 if (ast.locale.set & AST_LC_setenv)
1078 f = LC_setenv;
1079 p = *locale ? lcmake(locale) : (Lc_t*)0;
1081 else if (*locale)
1083 f = LC_setlocale;
1084 p = lcmake(locale);
1086 else if (category == AST_LC_ALL)
1088 if (!initialized)
1090 char* u;
1091 char tmp[256];
1094 * initialize from the environment
1095 * precedence determined by X/Open
1098 u = 0;
1099 if ((s = getenv("LANG")) && *s)
1101 if (streq(s, local) && (u || (u = native_locale(locale, tmp, sizeof(tmp)))))
1102 s = u;
1103 lang = lcmake(s);
1105 else
1106 lang = 0;
1107 if ((s = getenv("LC_ALL")) && *s)
1109 if (streq(s, local) && (u || (u = native_locale(locale, tmp, sizeof(tmp)))))
1110 s = u;
1111 lc_all = lcmake(s);
1113 else
1114 lc_all = 0;
1115 for (i = 1; i < AST_LC_COUNT; i++)
1116 if (lc_categories[i].flags & LC_setlocale)
1117 /* explicitly set by setlocale() */;
1118 else if ((s = getenv(lc_categories[i].name)) && *s)
1120 if (streq(s, local) && (u || (u = native_locale(locale, tmp, sizeof(tmp)))))
1121 s = u;
1122 lc_categories[i].prev = lcmake(s);
1124 else
1125 lc_categories[i].prev = 0;
1126 for (i = 1; i < AST_LC_COUNT; i++)
1127 if (!single(i, lc_all && !(lc_categories[i].flags & LC_setlocale) ? lc_all : lc_categories[i].prev, 0))
1129 while (i--)
1130 single(i, NiL, 0);
1131 return 0;
1133 if (ast.locale.set & AST_LC_debug)
1134 for (i = 1; i < AST_LC_COUNT; i++)
1135 sfprintf(sfstderr, "locale env %17s %16s %16s %16s\n", lc_categories[i].name, locales[i]->name, "", lc_categories[i].prev ? lc_categories[i].prev->name : (char*)0);
1136 initialized = 1;
1138 goto compose;
1140 else if (category == AST_LC_LANG || !(p = lc_categories[category].prev))
1142 f = 0;
1143 p = lcmake("C");
1145 else
1146 f = 0;
1147 if (category == AST_LC_LANG)
1149 if (lang != p)
1151 lang = p;
1152 if (!lc_all)
1153 for (i = 1; i < AST_LC_COUNT; i++)
1154 if (!single(i, lc_categories[i].prev, 0))
1156 while (i--)
1157 single(i, NiL, 0);
1158 return 0;
1162 else if (category != AST_LC_ALL)
1164 if (f || !lc_all)
1165 return single(category, p, f);
1166 if (p && !(ast.locale.set & AST_LC_internal))
1167 lc_categories[category].prev = p;
1168 return (char*)locales[category]->name;
1170 else if (composite(locale, 0) < 0)
1171 return 0;
1172 else if (lc_all != p)
1174 lc_all = p;
1175 for (i = 1; i < AST_LC_COUNT; i++)
1176 if (!single(i, lc_all && !(lc_categories[i].flags & LC_setlocale) ? lc_all : lc_categories[i].prev, 0))
1178 while (i--)
1179 single(i, NiL, 0);
1180 return 0;
1183 goto compose;