setlocale.c revision 7c2fbfb345896881c631598ee3852ce9ce33fb07
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk/***********************************************************************
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk* This software is part of the ast package *
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk* Copyright (c) 1985-2008 AT&T Intellectual Property *
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk* and is licensed under the *
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk* Common Public License, Version 1.0 *
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk* by AT&T Intellectual Property *
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk* A copy of the License is available at *
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk* http://www.opensource.org/licenses/cpl1.0.txt *
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk* Information and Software Systems Research *
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk* AT&T Research *
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk* Florham Park NJ *
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk* Glenn Fowler <gsf@research.att.com> *
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk* David Korn <dgk@research.att.com> *
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk* Phong Vo <kpv@research.att.com> *
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk***********************************************************************/
dff2cc5646d4437ab9e0cb1dcb59da65462a5938jeff.schenk * setlocale() intercept
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk * maintains a bitmask of non-default categories
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk * and a permanent locale namespace for pointer comparison
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk * and persistent private data for locale related functions
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk#if ( _lib_wcwidth || _lib_wctomb ) && _hdr_wctype
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenkextern char* uwin_setlocale(int, const char*);
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk * convert locale to native locale name in buf
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenknative_locale(const char* locale, char* buf, size_t siz)
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk unsigned long lcid;
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk unsigned long lang;
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk unsigned long ctry;
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk for (i = 0; i < elementsof(lc->territory->languages); i++)
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk if (lc->territory->languages[i] == lc->language)
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk lcid = MAKELCID(MAKELANGID(lang, ctry), SORT_DEFAULT);
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk if (GetLocaleInfo(lcid, LOCALE_SENGLANGUAGE, lbuf, sizeof(lbuf)) <= 0 ||
dff2cc5646d4437ab9e0cb1dcb59da65462a5938jeff.schenk GetLocaleInfo(lcid, LOCALE_SENGCOUNTRY, cbuf, sizeof(cbuf)) <= 0)
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk sfsprintf(buf, siz, "%s_%s.%s", lbuf, cbuf, lc->charset->ms);
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk * locale!=0 here
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenknative_setlocale(int category, const char* locale)
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk * win32 doesn't have LC_MESSAGES
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk return (char*)locale;
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk if (!(usr = native_locale(locale, buf, sizeof(buf))))
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk sfprintf(sfstderr, "locale uwin %17s %-24s %-24s\n", lc_categories[lcindex(category, 0)].name, usr, sys);
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk#define native_locale(a,b,c) ((char*)0)
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk * LC_COLLATE and LC_CTYPE native support
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk * LC_COLLATE and LC_CTYPE debug support
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk * mutibyte debug encoding
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk * DL0 [ '0' .. '4' ] c1 ... c4 DR0
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk * DL1 [ '0' .. '4' ] c1 ... c4 DR1
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk * with these ligatures
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk * ch CH sst SST
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk * and private collation order
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk * wide character display width is the low order 3 bits
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk * wctomb() uses DL1...DR1
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk#define DX (DB/DC) /* wchar_t max embedded chars */
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk#define DZ (DB-DX*DC+1) /* wchar_t embedded size bits */
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenkstatic unsigned char debug_order[] =
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenkdebug_mbtowc(register wchar_t* p, register const char* s, size_t n)
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk register const char* q;
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk register const char* r;
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk register int w;
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk register int dr;
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk if (!s || !*s)
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk switch (((unsigned char*)s)[0])
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk if ((w = ((unsigned char*)s)[1]) == ((unsigned char*)s)[0])
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk r = s + w - 1;
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk while (q < r && *q)
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk if (q != r || *((unsigned char*)q) != dr)
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk while (--q >= s)
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk c |= *((unsigned char*)q);
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk if (c >= 0 && c <= UCHAR_MAX)
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk *s++ = i + '0';
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk if (c >= 0 && c <= UCHAR_MAX)
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenkdebug_strxfrm(register char* t, register const char* s, size_t n)
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk register const char* q;
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk register const char* r;
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk register char* e;
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk register int w;
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk while (s[0])
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk if ((((unsigned char*)s)[0] == DL0 || ((unsigned char*)s)[0] == DL1) && (w = s[1]) >= '0' && w <= ('0' + DC))
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk while (q < r && *q)
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk if (*((unsigned char*)q) == DR0 || *((unsigned char*)q) == DR1)
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk for (q = s + 2; q < r; q++)
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk while (w++ < DX)
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk if ((s[0] == 'c' || s[0] == 'C') && (s[1] == 'h' || s[1] == 'H'))
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk if ((s[0] == 's' || s[0] == 'S') && (s[1] == 's' || s[1] == 'S') && (s[2] == 't' || s[2] == 'T'))
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk return t - o;
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenkdebug_strcoll(const char* a, const char* b)
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk * default locale
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk return w >= 0 && w <= 255 && !iscntrl(w) ? 1 : -1;
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk * called when LC_COLLATE initialized or changes
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk else if (locales[cp->internal]->flags & LC_default)
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk * workaround the interesting sjis that translates unshifted 7 bit ascii!
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk#define mb_state_zero ((mbstate_t*)&ast.pad[sizeof(ast.pad)-2*sizeof(mbstate_t)])
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk#define mb_state ((mbstate_t*)&ast.pad[sizeof(ast.pad)-sizeof(mbstate_t)])
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenksjis_mbtowc(register wchar_t* p, register const char* s, size_t n)
dff2cc5646d4437ab9e0cb1dcb59da65462a5938jeff.schenk if (n && p && s && (*s == '\\' || *s == '~') && !memcmp(mb_state, mb_state_zero, sizeof(mbstate_t)))
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk * called when LC_CTYPE initialized or changes
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk else if ((locales[cp->internal]->flags & LC_default) || (ast.mb_cur_max = MB_CUR_MAX) <= 1 || !(ast.mb_len = mblen) || !(ast.mb_towc = mbtowc))
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk * check for sjis that translates unshifted 7 bit ascii!
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk memcpy(mb_state, mb_state_zero, sizeof(mbstate_t));
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk * called when LC_NUMERIC initialized or changes
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk static Lc_numeric_t default_numeric = { '.', -1 };
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk if ((lp = localeconv()) && (dp = newof(0, Lc_numeric_t, 1, 0)))
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk dp->decimal = lp->decimal_point && *lp->decimal_point ? *(unsigned char*)lp->decimal_point : '.';
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk dp->thousand = lp->thousands_sep && *lp->thousands_sep ? *(unsigned char*)lp->thousands_sep : -1;
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk if (ast.locale.set & (AST_LC_debug|AST_LC_setlocale))
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk sfprintf(sfstderr, "locale info %17s decimal '%c' thousands '%c'\n", lc_categories[category].name, dp->decimal, dp->thousand >= 0 ? dp->thousand : 'X');
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk * this table is indexed by AST_LC_[A-Z]*
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk{ "LC_COLLATE", LC_COLLATE, AST_LC_COLLATE, set_collate },
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk{ "LC_CTYPE", LC_CTYPE, AST_LC_CTYPE, set_ctype },
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk{ "LC_MESSAGES", LC_MESSAGES, AST_LC_MESSAGES, 0 },
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk{ "LC_MONETARY", LC_MONETARY, AST_LC_MONETARY, 0 },
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk{ "LC_NUMERIC", LC_NUMERIC, AST_LC_NUMERIC, set_numeric },
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk{ "LC_IDENTIFICATION",LC_IDENTIFICATION,AST_LC_IDENTIFICATION,0 },
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk{ "LC_TELEPHONE", LC_TELEPHONE, AST_LC_TELEPHONE, 0 },
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk{ "LC_XLITERATE", LC_XLITERATE, AST_LC_XLITERATE, 0 },
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk{ "LC_MEASUREMENT", LC_MEASUREMENT, AST_LC_MEASUREMENT, 0 },
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk * called by stropt() to set options
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenksetopt(void* a, const void* p, int n, const char* v)
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenkdefault_setlocale(int category, const char* locale)
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk if (!(lc = lcmake(locale)) || !(lc->flags & LC_default))
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk return (locales[1]->flags & (1<<category)) ? locales[1]->name : locales[0]->name;
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk * set a single AST_LC_* locale category
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk * the caller must validate category
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk * lc==0 restores the previous state
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk const char* sys;
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk if (!lc && !(lc = lc_categories[category].prev))
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk if (lc_categories[category].external == -lc_categories[category].internal)
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk sys = setlocale(lc_categories[category].external, lcmake(NiL)->name);
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk else if (!(sys = setlocale(lc_categories[category].external, lc->name)) &&
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk (streq(lc->name, lc->code) || !(sys = setlocale(lc_categories[category].external, lc->code))) &&
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk sys = setlocale(lc_categories[category].external, lc->language->code);
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk if (ast.locale.set & (AST_LC_debug|AST_LC_setlocale))
dff2cc5646d4437ab9e0cb1dcb59da65462a5938jeff.schenk sfprintf(sfstderr, "locale set %17s %-24s %-24s\n", lc_categories[category].name, lc->name, sys);
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk * check for local override
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk * currently this means an LC_MESSAGES dir exists
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk if (mcfind(path, lc->code, NiL, LC_MESSAGES, 0))
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk if (lc_categories[category].external != -lc_categories[category].internal)
dff2cc5646d4437ab9e0cb1dcb59da65462a5938jeff.schenk setlocale(lc_categories[category].external, lcmake(NiL)->name);
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk if (lc_categories[category].setf && (*lc_categories[category].setf)(&lc_categories[category]))
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk locales[category] = lc_categories[category].prev;
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk * set composite AST_LC_ALL locale categories
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk * return <0:composite-error 0:not-composite >0:composite-ok
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenkcomposite(register const char* s, int initialize)
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk register const char* t;
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk register int i;
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk register int j;
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk register int k;
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk const char* w;
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk while (s[0] == 'L' && s[1] == 'C' && s[2] == '_')
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk while (*t && *s++ == *t++);
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk if (!*t && *s++ == '=')
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk for (s = w; *s && *s != '='; s++);
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk for (i = 0; i < k; i++)
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk else if (*s++ == ';')
5b64d5d44892834ba97f003080f3467299b7c5c5jeff.schenk for (i = 0; i < j; i++)
dff2cc5646d4437ab9e0cb1dcb59da65462a5938jeff.schenk for (i = 0; i < k; i++)
p = lcmake(w);
buf[j] = 0;
if (!initialize)
if (!single(n, p))
Lc_t* p;
static int initialized;
if (!locale)
if (cat[i] < 0)
cat[i] = i;
cat[j] = i;
if (cat[j] == k)
if (*locale)
else if (!initialized)
if (!p && !(p = lcmake(a)))
goto compose;
if (!single(i, p))
goto compose;