Home | History | Annotate | Line # | Download | only in locale
setlocale.c revision 1.52.6.1
      1  1.52.6.1      yamt /*	$NetBSD: setlocale.c,v 1.52.6.1 2008/05/18 12:30:17 yamt Exp $	*/
      2      1.10    kleink 
      3       1.1       cgd /*
      4  1.52.6.1      yamt  * Copyright (c) 1991, 1993, 2008
      5       1.5       jtc  *	The Regents of the University of California.  All rights reserved.
      6       1.5       jtc  *
      7       1.5       jtc  * This code is derived from software contributed to Berkeley by
      8       1.5       jtc  * Paul Borman at Krystal Technologies.
      9       1.1       cgd  *
     10       1.1       cgd  * Redistribution and use in source and binary forms, with or without
     11       1.1       cgd  * modification, are permitted provided that the following conditions
     12       1.1       cgd  * are met:
     13       1.1       cgd  * 1. Redistributions of source code must retain the above copyright
     14       1.1       cgd  *    notice, this list of conditions and the following disclaimer.
     15       1.1       cgd  * 2. Redistributions in binary form must reproduce the above copyright
     16       1.1       cgd  *    notice, this list of conditions and the following disclaimer in the
     17       1.1       cgd  *    documentation and/or other materials provided with the distribution.
     18      1.43       agc  * 3. Neither the name of the University nor the names of its contributors
     19       1.1       cgd  *    may be used to endorse or promote products derived from this software
     20       1.1       cgd  *    without specific prior written permission.
     21       1.1       cgd  *
     22       1.1       cgd  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     23       1.1       cgd  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     24       1.1       cgd  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     25       1.1       cgd  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     26       1.1       cgd  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     27       1.1       cgd  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     28       1.1       cgd  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     29       1.1       cgd  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     30       1.1       cgd  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     31       1.1       cgd  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     32       1.1       cgd  * SUCH DAMAGE.
     33       1.1       cgd  */
     34       1.1       cgd 
     35      1.12  christos #include <sys/cdefs.h>
     36       1.1       cgd #if defined(LIBC_SCCS) && !defined(lint)
     37      1.10    kleink #if 0
     38       1.5       jtc static char sccsid[] = "@(#)setlocale.c	8.1 (Berkeley) 7/4/93";
     39      1.10    kleink #else
     40  1.52.6.1      yamt __RCSID("$NetBSD: setlocale.c,v 1.52.6.1 2008/05/18 12:30:17 yamt Exp $");
     41      1.10    kleink #endif
     42       1.1       cgd #endif /* LIBC_SCCS and not lint */
     43       1.1       cgd 
     44      1.11    kleink #define _CTYPE_PRIVATE
     45      1.11    kleink 
     46      1.14    kleink #include "namespace.h"
     47       1.5       jtc #include <sys/localedef.h>
     48      1.37      yamt #include <sys/types.h>
     49      1.37      yamt #include <sys/stat.h>
     50      1.37      yamt #include <assert.h>
     51      1.35    kleink #include <limits.h>
     52      1.11    kleink #include <ctype.h>
     53      1.18  tshiozak #define __SETLOCALE_SOURCE__
     54       1.1       cgd #include <locale.h>
     55      1.11    kleink #include <paths.h>
     56       1.6       jtc #include <stdio.h>
     57       1.5       jtc #include <stdlib.h>
     58       1.1       cgd #include <string.h>
     59      1.21     veego #include <unistd.h>
     60      1.32    itojun #ifdef WITH_RUNE
     61      1.24    itojun #include "rune.h"
     62      1.24    itojun #include "rune_local.h"
     63      1.32    itojun #else
     64      1.32    itojun #include "ctypeio.h"
     65      1.32    itojun #endif
     66  1.52.6.1      yamt #include "lcmessages.h"
     67  1.52.6.1      yamt #include "lcmonetary.h"
     68  1.52.6.1      yamt #include "lcnumeric.h"
     69  1.52.6.1      yamt #include "lctime.h"
     70       1.1       cgd 
     71      1.47  tshiozak #ifdef CITRUS
     72      1.44  tshiozak #include <citrus/citrus_namespace.h>
     73      1.44  tshiozak #include <citrus/citrus_region.h>
     74      1.44  tshiozak #include <citrus/citrus_lookup.h>
     75      1.46  tshiozak #include <citrus/citrus_bcs.h>
     76      1.47  tshiozak #else
     77      1.47  tshiozak #include <locale/aliasname_local.h>
     78      1.47  tshiozak #define _lookup_alias(p, a, b, s, c)	__unaliasname((p), (a), (b), (s))
     79      1.47  tshiozak #define _bcs_strcasecmp(a, b)		strcasecmp((a), (b))
     80      1.47  tshiozak #endif
     81      1.44  tshiozak 
     82      1.46  tshiozak #define _LOCALE_SYM_FORCE	"/force"
     83      1.44  tshiozak 
     84       1.5       jtc /*
     85       1.5       jtc  * Category names for getenv()
     86       1.5       jtc  */
     87      1.13   mycroft static const char *const categories[_LC_LAST] = {
     88       1.5       jtc     "LC_ALL",
     89       1.5       jtc     "LC_COLLATE",
     90       1.5       jtc     "LC_CTYPE",
     91       1.5       jtc     "LC_MONETARY",
     92       1.5       jtc     "LC_NUMERIC",
     93       1.5       jtc     "LC_TIME",
     94       1.5       jtc     "LC_MESSAGES"
     95       1.5       jtc };
     96       1.1       cgd 
     97       1.1       cgd /*
     98       1.5       jtc  * Current locales for each category
     99       1.5       jtc  */
    100       1.5       jtc static char current_categories[_LC_LAST][32] = {
    101       1.5       jtc     "C",
    102       1.5       jtc     "C",
    103       1.5       jtc     "C",
    104       1.5       jtc     "C",
    105       1.5       jtc     "C",
    106       1.5       jtc     "C",
    107       1.5       jtc     "C"
    108       1.5       jtc };
    109      1.19    kleink 
    110       1.5       jtc /*
    111       1.5       jtc  * The locales we are going to try and load
    112       1.1       cgd  */
    113       1.5       jtc static char new_categories[_LC_LAST][32];
    114       1.5       jtc 
    115       1.5       jtc static char current_locale_string[_LC_LAST * 33];
    116       1.5       jtc 
    117      1.34    itojun static char *currentlocale __P((void));
    118      1.46  tshiozak static void revert_to_default __P((int));
    119      1.46  tshiozak static int force_locale_enable __P((int));
    120      1.46  tshiozak static int load_locale_sub __P((int, const char *, int));
    121      1.34    itojun static char *loadlocale __P((int));
    122      1.37      yamt static const char *__get_locale_env __P((int));
    123      1.24    itojun 
    124      1.24    itojun char *
    125      1.24    itojun __setlocale(category, locale)
    126      1.24    itojun 	int category;
    127      1.24    itojun 	const char *locale;
    128      1.24    itojun {
    129      1.27  jdolecek 	int i, loadlocale_success;
    130      1.16  christos 	size_t len;
    131      1.37      yamt 	const char *env, *r;
    132       1.5       jtc 
    133      1.20  tshiozak 	if (issetugid() ||
    134      1.24    itojun 	    (!_PathLocale && !(_PathLocale = getenv("PATH_LOCALE"))))
    135      1.49      yamt 		_PathLocale = _PATH_LOCALE;
    136       1.5       jtc 
    137       1.5       jtc 	if (category < 0 || category >= _LC_LAST)
    138       1.1       cgd 		return (NULL);
    139       1.5       jtc 
    140       1.5       jtc 	if (!locale)
    141       1.5       jtc 		return (category ?
    142       1.5       jtc 		    current_categories[category] : currentlocale());
    143       1.5       jtc 
    144       1.5       jtc 	/*
    145       1.5       jtc 	 * Default to the current locale for everything.
    146       1.5       jtc 	 */
    147       1.5       jtc 	for (i = 1; i < _LC_LAST; ++i)
    148      1.23    itojun 		(void)strlcpy(new_categories[i], current_categories[i],
    149      1.23    itojun 		    sizeof(new_categories[i]));
    150       1.5       jtc 
    151       1.5       jtc 	/*
    152       1.5       jtc 	 * Now go fill up new_categories from the locale argument
    153       1.5       jtc 	 */
    154       1.5       jtc 	if (!*locale) {
    155      1.37      yamt 		if (category == LC_ALL) {
    156       1.5       jtc 			for (i = 1; i < _LC_LAST; ++i) {
    157      1.37      yamt 				env = __get_locale_env(i);
    158      1.23    itojun 				(void)strlcpy(new_categories[i], env,
    159      1.23    itojun 				    sizeof(new_categories[i]));
    160       1.5       jtc 			}
    161       1.5       jtc 		}
    162      1.37      yamt 		else {
    163      1.37      yamt 			env = __get_locale_env(category);
    164      1.37      yamt 			(void)strlcpy(new_categories[category], env,
    165      1.37      yamt 				sizeof(new_categories[category]));
    166      1.37      yamt 		}
    167       1.8       mrg 	} else if (category) {
    168      1.23    itojun 		(void)strlcpy(new_categories[category], locale,
    169      1.23    itojun 		    sizeof(new_categories[category]));
    170       1.5       jtc 	} else {
    171       1.5       jtc 		if ((r = strchr(locale, '/')) == 0) {
    172       1.5       jtc 			for (i = 1; i < _LC_LAST; ++i) {
    173      1.23    itojun 				(void)strlcpy(new_categories[i], locale,
    174      1.23    itojun 				    sizeof(new_categories[i]));
    175       1.5       jtc 			}
    176       1.5       jtc 		} else {
    177      1.42     enami 			for (i = 1;;) {
    178      1.42     enami 				_DIAGASSERT(*r == '/' || *r == 0);
    179      1.42     enami 				_DIAGASSERT(*locale != 0);
    180      1.42     enami 				if (*locale == '/')
    181      1.42     enami 					return (NULL);	/* invalid format. */
    182      1.40  tshiozak 				len = r - locale;
    183      1.39    itojun 				if (len + 1 > sizeof(new_categories[i]))
    184      1.39    itojun 					return (NULL);	/* too long */
    185      1.39    itojun 				(void)memcpy(new_categories[i], locale, len);
    186      1.39    itojun 				new_categories[i][len] = '\0';
    187      1.42     enami 				if (*r == 0)
    188      1.42     enami 					break;
    189      1.42     enami 				_DIAGASSERT(*r == '/');
    190      1.42     enami 				if (*(locale = ++r) == 0)
    191      1.42     enami 					/* slash followed by NUL */
    192      1.42     enami 					return (NULL);
    193      1.42     enami 				/* skip until NUL or '/' */
    194      1.42     enami 				while (*r && *r != '/')
    195      1.42     enami 					r++;
    196      1.42     enami 				if (++i == _LC_LAST)
    197      1.42     enami 					return (NULL);	/* too many slashes. */
    198      1.38  tshiozak 			}
    199      1.42     enami 			if (i + 1 != _LC_LAST)
    200      1.42     enami 				return (NULL);	/* too few slashes. */
    201       1.5       jtc 		}
    202       1.5       jtc 	}
    203       1.5       jtc 
    204       1.5       jtc 	if (category)
    205       1.5       jtc 		return (loadlocale(category));
    206       1.5       jtc 
    207      1.27  jdolecek 	loadlocale_success = 0;
    208      1.22    itojun 	for (i = 1; i < _LC_LAST; ++i) {
    209      1.27  jdolecek 		if (loadlocale(i) != NULL)
    210      1.27  jdolecek 			loadlocale_success = 1;
    211      1.22    itojun 	}
    212      1.27  jdolecek 
    213      1.27  jdolecek 	/*
    214      1.27  jdolecek 	 * If all categories failed, return NULL; we don't need to back
    215      1.27  jdolecek 	 * changes off, since none happened.
    216      1.27  jdolecek 	 */
    217      1.27  jdolecek 	if (!loadlocale_success)
    218      1.27  jdolecek 		return NULL;
    219      1.27  jdolecek 
    220       1.9    kleink 	return (currentlocale());
    221       1.5       jtc }
    222       1.5       jtc 
    223       1.5       jtc static char *
    224       1.5       jtc currentlocale()
    225       1.5       jtc {
    226       1.5       jtc 	int i;
    227       1.5       jtc 
    228      1.23    itojun 	(void)strlcpy(current_locale_string, current_categories[1],
    229      1.23    itojun 	    sizeof(current_locale_string));
    230       1.5       jtc 
    231       1.5       jtc 	for (i = 2; i < _LC_LAST; ++i)
    232       1.5       jtc 		if (strcmp(current_categories[1], current_categories[i])) {
    233       1.5       jtc 			(void)snprintf(current_locale_string,
    234      1.36      yamt 			    sizeof(current_locale_string), "%s/%s/%s/%s/%s/%s",
    235       1.5       jtc 			    current_categories[1], current_categories[2],
    236       1.5       jtc 			    current_categories[3], current_categories[4],
    237      1.36      yamt 			    current_categories[5], current_categories[6]);
    238       1.5       jtc 			break;
    239       1.5       jtc 		}
    240       1.5       jtc 	return (current_locale_string);
    241       1.5       jtc }
    242       1.5       jtc 
    243      1.46  tshiozak static void
    244      1.46  tshiozak revert_to_default(category)
    245      1.46  tshiozak 	int category;
    246      1.46  tshiozak {
    247      1.46  tshiozak 	switch (category) {
    248      1.46  tshiozak 	case LC_CTYPE:
    249      1.46  tshiozak #ifdef WITH_RUNE
    250      1.46  tshiozak 		(void)_xpg4_setrunelocale("C");
    251      1.46  tshiozak #else
    252      1.46  tshiozak 		if (_ctype_ != _C_ctype_) {
    253      1.46  tshiozak 			/* LINTED const castaway */
    254      1.46  tshiozak 			free((void *)_ctype_);
    255      1.46  tshiozak 			_ctype_ = _C_ctype_;
    256      1.46  tshiozak 		}
    257      1.46  tshiozak 		if (_toupper_tab_ != _C_toupper_) {
    258      1.46  tshiozak 			/* LINTED const castaway */
    259      1.46  tshiozak 			free((void *)_toupper_tab_);
    260      1.46  tshiozak 			_toupper_tab_ = _C_toupper_;
    261      1.46  tshiozak 		}
    262      1.46  tshiozak 		if (_tolower_tab_ != _C_tolower_) {
    263      1.46  tshiozak 			/* LINTED const castaway */
    264      1.46  tshiozak 			free((void *)_tolower_tab_);
    265      1.46  tshiozak 			_tolower_tab_ = _C_tolower_;
    266      1.46  tshiozak 		}
    267      1.46  tshiozak #endif
    268      1.46  tshiozak 		break;
    269      1.51      manu 	case LC_TIME:
    270      1.51      manu 		if (_CurrentTimeLocale != &_DefaultTimeLocale) {
    271  1.52.6.1      yamt 			free(__UNCONST(_CurrentTimeLocale));
    272      1.51      manu 			_CurrentTimeLocale = &_DefaultTimeLocale;
    273      1.51      manu 		}
    274      1.51      manu 		break;
    275      1.46  tshiozak 	case LC_MESSAGES:
    276  1.52.6.1      yamt 		if (_CurrentMessagesLocale != &_DefaultMessagesLocale) {
    277  1.52.6.1      yamt 			free(__UNCONST(_CurrentMessagesLocale));
    278  1.52.6.1      yamt 			_CurrentMessagesLocale = &_DefaultMessagesLocale;
    279  1.52.6.1      yamt 		}
    280  1.52.6.1      yamt 		break;
    281      1.46  tshiozak 	case LC_COLLATE:
    282  1.52.6.1      yamt 		break;
    283      1.46  tshiozak 	case LC_MONETARY:
    284  1.52.6.1      yamt 		if (_CurrentMonetaryLocale != &_DefaultMonetaryLocale) {
    285  1.52.6.1      yamt 			free(__UNCONST(_CurrentMonetaryLocale));
    286  1.52.6.1      yamt 			_CurrentMonetaryLocale = &_DefaultMonetaryLocale;
    287  1.52.6.1      yamt 		}
    288  1.52.6.1      yamt 		break;
    289      1.46  tshiozak 	case LC_NUMERIC:
    290  1.52.6.1      yamt 		if (_CurrentNumericLocale != &_DefaultNumericLocale) {
    291  1.52.6.1      yamt 			free(__UNCONST(_CurrentNumericLocale));
    292  1.52.6.1      yamt 			_CurrentNumericLocale = &_DefaultNumericLocale;
    293  1.52.6.1      yamt 		}
    294      1.46  tshiozak 		break;
    295      1.46  tshiozak 	}
    296      1.46  tshiozak }
    297      1.46  tshiozak 
    298      1.44  tshiozak static int
    299      1.46  tshiozak force_locale_enable(category)
    300      1.46  tshiozak 	int category;
    301      1.46  tshiozak {
    302      1.46  tshiozak 	revert_to_default(category);
    303      1.46  tshiozak 
    304      1.46  tshiozak 	return 0;
    305      1.46  tshiozak }
    306      1.46  tshiozak 
    307      1.46  tshiozak static int
    308      1.46  tshiozak load_locale_sub(category, locname, isspecial)
    309      1.44  tshiozak 	int category;
    310      1.44  tshiozak 	const char *locname;
    311      1.46  tshiozak 	int isspecial;
    312      1.44  tshiozak {
    313      1.44  tshiozak 	char name[PATH_MAX];
    314      1.44  tshiozak 
    315      1.46  tshiozak 	/* check for the default locales */
    316      1.46  tshiozak 	if (!strcmp(new_categories[category], "C") ||
    317      1.46  tshiozak 	    !strcmp(new_categories[category], "POSIX")) {
    318      1.46  tshiozak 		revert_to_default(category);
    319      1.46  tshiozak 		return 0;
    320      1.46  tshiozak 	}
    321      1.46  tshiozak 
    322      1.46  tshiozak 	/* check whether special symbol */
    323      1.46  tshiozak 	if (isspecial && _bcs_strcasecmp(locname, _LOCALE_SYM_FORCE) == 0)
    324      1.46  tshiozak 		return force_locale_enable(category);
    325      1.46  tshiozak 
    326      1.44  tshiozak 	/* sanity check */
    327      1.44  tshiozak 	if (strchr(locname, '/') != NULL)
    328      1.44  tshiozak 		return -1;
    329      1.44  tshiozak 
    330      1.44  tshiozak 	(void)snprintf(name, sizeof(name), "%s/%s/%s",
    331      1.44  tshiozak 		       _PathLocale, locname, categories[category]);
    332      1.44  tshiozak 
    333      1.44  tshiozak 	switch (category) {
    334      1.44  tshiozak 	case LC_CTYPE:
    335      1.44  tshiozak #ifdef WITH_RUNE
    336      1.44  tshiozak 		if (_xpg4_setrunelocale(__UNCONST(locname)))
    337      1.44  tshiozak 			return -1;
    338      1.44  tshiozak #else
    339      1.44  tshiozak 		if (!__loadctype(name))
    340      1.44  tshiozak 			return -1;
    341      1.44  tshiozak #endif
    342      1.44  tshiozak 		break;
    343      1.44  tshiozak 
    344      1.44  tshiozak 	case LC_MESSAGES:
    345  1.52.6.1      yamt #ifdef OLD_NO_LC_MESSAGES
    346      1.44  tshiozak 		/*
    347      1.44  tshiozak 		 * XXX we don't have LC_MESSAGES support yet,
    348      1.44  tshiozak 		 * but catopen may use the value of LC_MESSAGES category.
    349      1.44  tshiozak 		 * so return successfully if locale directory is present.
    350      1.44  tshiozak 		 */
    351      1.44  tshiozak 		(void)snprintf(name, sizeof(name), "%s/%s",
    352      1.44  tshiozak 			_PathLocale, locname);
    353      1.44  tshiozak 		/* local */
    354      1.44  tshiozak 		{
    355      1.44  tshiozak 			struct stat st;
    356      1.44  tshiozak 			if (stat(name, &st) < 0)
    357      1.44  tshiozak 				return -1;
    358      1.44  tshiozak 			if (!S_ISDIR(st.st_mode))
    359      1.44  tshiozak 				return -1;
    360      1.44  tshiozak 		}
    361  1.52.6.1      yamt #else
    362  1.52.6.1      yamt 		if (!__loadmessages(name))
    363  1.52.6.1      yamt 			return -1;
    364  1.52.6.1      yamt #endif
    365      1.44  tshiozak 		break;
    366      1.44  tshiozak 
    367      1.51      manu 	case LC_TIME:
    368      1.51      manu 		if (!__loadtime(name))
    369      1.51      manu 			return -1;
    370      1.51      manu 		break;
    371      1.44  tshiozak 	case LC_COLLATE:
    372  1.52.6.1      yamt 		return -1;
    373      1.44  tshiozak 	case LC_MONETARY:
    374  1.52.6.1      yamt 		if (!__loadmonetary(name))
    375  1.52.6.1      yamt 			return -1;
    376  1.52.6.1      yamt 		break;
    377      1.44  tshiozak 	case LC_NUMERIC:
    378  1.52.6.1      yamt 		if (!__loadnumeric(name))
    379  1.52.6.1      yamt 			return -1;
    380  1.52.6.1      yamt 		break;
    381      1.44  tshiozak 	}
    382      1.44  tshiozak 
    383      1.44  tshiozak 	return 0;
    384      1.44  tshiozak }
    385      1.44  tshiozak 
    386      1.15    kleink static char *
    387       1.5       jtc loadlocale(category)
    388       1.5       jtc 	int category;
    389       1.5       jtc {
    390      1.44  tshiozak 	char aliaspath[PATH_MAX], loccat[PATH_MAX], buf[PATH_MAX];
    391      1.44  tshiozak 	const char *alias;
    392       1.5       jtc 
    393      1.37      yamt 	_DIAGASSERT(0 < category && category < _LC_LAST);
    394      1.37      yamt 
    395      1.11    kleink 	if (strcmp(new_categories[category], current_categories[category]) == 0)
    396       1.5       jtc 		return (current_categories[category]);
    397       1.5       jtc 
    398      1.44  tshiozak 	/* (1) non-aliased file */
    399      1.46  tshiozak 	if (!load_locale_sub(category, new_categories[category], 0))
    400      1.44  tshiozak 		goto success;
    401      1.44  tshiozak 
    402      1.44  tshiozak 	/* (2) lookup locname/catname type alias */
    403      1.44  tshiozak 	(void)snprintf(aliaspath, sizeof(aliaspath),
    404      1.44  tshiozak 		       "%s/" _LOCALE_ALIAS_NAME, _PathLocale);
    405      1.44  tshiozak 	(void)snprintf(loccat, sizeof(loccat), "%s/%s",
    406      1.44  tshiozak 		       new_categories[category], categories[category]);
    407      1.44  tshiozak 	alias = _lookup_alias(aliaspath, loccat, buf, sizeof(buf),
    408      1.44  tshiozak 			      _LOOKUP_CASE_SENSITIVE);
    409      1.46  tshiozak 	if (!load_locale_sub(category, alias, 1))
    410      1.44  tshiozak 		goto success;
    411      1.44  tshiozak 
    412      1.44  tshiozak 	/* (3) lookup locname type alias */
    413      1.44  tshiozak 	alias = _lookup_alias(aliaspath, new_categories[category],
    414      1.44  tshiozak 			      buf, sizeof(buf), _LOOKUP_CASE_SENSITIVE);
    415      1.46  tshiozak 	if (!load_locale_sub(category, alias, 1))
    416      1.44  tshiozak 		goto success;
    417      1.11    kleink 
    418      1.44  tshiozak 	return NULL;
    419      1.11    kleink 
    420      1.44  tshiozak success:
    421      1.37      yamt 	(void)strlcpy(current_categories[category],
    422      1.37      yamt 		new_categories[category],
    423      1.37      yamt 		sizeof(current_categories[category]));
    424      1.37      yamt 	return current_categories[category];
    425      1.37      yamt }
    426      1.37      yamt 
    427      1.37      yamt static const char *
    428      1.37      yamt __get_locale_env(category)
    429      1.37      yamt 	int category;
    430      1.37      yamt {
    431      1.37      yamt 	const char *env;
    432      1.37      yamt 
    433      1.37      yamt 	_DIAGASSERT(category != LC_ALL);
    434      1.37      yamt 
    435      1.37      yamt 	/* 1. check LC_ALL. */
    436      1.37      yamt 	env = getenv(categories[0]);
    437      1.37      yamt 
    438      1.37      yamt 	/* 2. check LC_* */
    439      1.37      yamt 	if (!env || !*env)
    440      1.37      yamt 		env = getenv(categories[category]);
    441      1.37      yamt 
    442      1.37      yamt 	/* 3. check LANG */
    443      1.37      yamt 	if (!env || !*env)
    444      1.37      yamt 		env = getenv("LANG");
    445      1.37      yamt 
    446      1.37      yamt 	/* 4. if none is set, fall to "C" */
    447      1.37      yamt 	if (!env || !*env || strchr(env, '/'))
    448      1.37      yamt 		env = "C";
    449      1.37      yamt 
    450      1.37      yamt 	return env;
    451       1.1       cgd }
    452