11573Srgrimes/* 289739Sphantom * Copyright (c) 1996 - 2002 FreeBSD Project 31573Srgrimes * Copyright (c) 1991, 1993 41573Srgrimes * The Regents of the University of California. All rights reserved. 51573Srgrimes * 61573Srgrimes * This code is derived from software contributed to Berkeley by 71573Srgrimes * Paul Borman at Krystal Technologies. 81573Srgrimes * 91573Srgrimes * Redistribution and use in source and binary forms, with or without 101573Srgrimes * modification, are permitted provided that the following conditions 111573Srgrimes * are met: 121573Srgrimes * 1. Redistributions of source code must retain the above copyright 131573Srgrimes * notice, this list of conditions and the following disclaimer. 141573Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 151573Srgrimes * notice, this list of conditions and the following disclaimer in the 161573Srgrimes * documentation and/or other materials provided with the distribution. 171573Srgrimes * 4. Neither the name of the University nor the names of its contributors 181573Srgrimes * may be used to endorse or promote products derived from this software 191573Srgrimes * without specific prior written permission. 201573Srgrimes * 211573Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 221573Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 231573Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 241573Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 251573Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 261573Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 271573Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 281573Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 291573Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 301573Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 311573Srgrimes * SUCH DAMAGE. 321573Srgrimes */ 331573Srgrimes 341573Srgrimes#if defined(LIBC_SCCS) && !defined(lint) 351573Srgrimesstatic char sccsid[] = "@(#)setlocale.c 8.1 (Berkeley) 7/4/93"; 361573Srgrimes#endif /* LIBC_SCCS and not lint */ 3792986Sobrien#include <sys/cdefs.h> 3892986Sobrien__FBSDID("$FreeBSD$"); 391573Srgrimes 4019988Sache#include <sys/types.h> 4119988Sache#include <sys/stat.h> 42101259Sache#include <errno.h> 431573Srgrimes#include <limits.h> 441573Srgrimes#include <locale.h> 45116845Sphantom#include <paths.h> /* for _PATH_LOCALE */ 461573Srgrimes#include <stdlib.h> 471573Srgrimes#include <string.h> 4819988Sache#include <unistd.h> 4911695Sache#include "collate.h" 5072165Sphantom#include "lmonetary.h" /* for __monetary_load_locale() */ 5172165Sphantom#include "lnumeric.h" /* for __numeric_load_locale() */ 5272165Sphantom#include "lmessages.h" /* for __messages_load_locale() */ 5322330Sache#include "setlocale.h" 54101498Sache#include "ldpart.h" 5589739Sphantom#include "../stdtime/timelocal.h" /* for __time_load_locale() */ 561573Srgrimes 5711695Sache/* 5811695Sache * Category names for getenv() 5911695Sache */ 60228921Sjillesstatic const char categories[_LC_LAST][12] = { 6111695Sache "LC_ALL", 6211695Sache "LC_COLLATE", 6311695Sache "LC_CTYPE", 6411695Sache "LC_MONETARY", 6511695Sache "LC_NUMERIC", 6611695Sache "LC_TIME", 6735523Sache "LC_MESSAGES", 6811695Sache}; 6911695Sache 7011695Sache/* 7111695Sache * Current locales for each category 7211695Sache */ 7322330Sachestatic char current_categories[_LC_LAST][ENCODING_LEN + 1] = { 7411695Sache "C", 7511695Sache "C", 7611695Sache "C", 7711695Sache "C", 7811695Sache "C", 7911695Sache "C", 8035523Sache "C", 8111695Sache}; 8211695Sache 8311695Sache/* 84116846Sphantom * Path to locale storage directory 85116846Sphantom */ 86116846Sphantomchar *_PathLocale; 87116846Sphantom 88116846Sphantom/* 8911695Sache * The locales we are going to try and load 9011695Sache */ 9122330Sachestatic char new_categories[_LC_LAST][ENCODING_LEN + 1]; 9222330Sachestatic char saved_categories[_LC_LAST][ENCODING_LEN + 1]; 9311695Sache 9422330Sachestatic char current_locale_string[_LC_LAST * (ENCODING_LEN + 1/*"/"*/ + 1)]; 951573Srgrimes 9692905Sobrienstatic char *currentlocale(void); 9792905Sobrienstatic char *loadlocale(int); 98227753Stheravenconst char *__get_locale_env(int); 991573Srgrimes 1001573Srgrimeschar * 1011573Srgrimessetlocale(category, locale) 1021573Srgrimes int category; 1031573Srgrimes const char *locale; 1041573Srgrimes{ 105101366Sache int i, j, len, saverr; 106125274Sache const char *env, *r; 1071573Srgrimes 108101259Sache if (category < LC_ALL || category >= _LC_LAST) { 109101259Sache errno = EINVAL; 1101573Srgrimes return (NULL); 111101259Sache } 1121573Srgrimes 113101366Sache if (locale == NULL) 11419964Sache return (category != LC_ALL ? 1151573Srgrimes current_categories[category] : currentlocale()); 1161573Srgrimes 1171573Srgrimes /* 1181573Srgrimes * Default to the current locale for everything. 1191573Srgrimes */ 12019971Sache for (i = 1; i < _LC_LAST; ++i) 1211573Srgrimes (void)strcpy(new_categories[i], current_categories[i]); 1221573Srgrimes 1231573Srgrimes /* 1241573Srgrimes * Now go fill up new_categories from the locale argument 1251573Srgrimes */ 1261573Srgrimes if (!*locale) { 12719964Sache if (category == LC_ALL) { 1281573Srgrimes for (i = 1; i < _LC_LAST; ++i) { 129125274Sache env = __get_locale_env(i); 130125274Sache if (strlen(env) > ENCODING_LEN) { 131101366Sache errno = EINVAL; 132101366Sache return (NULL); 133101366Sache } 134101366Sache (void)strcpy(new_categories[i], env); 1351573Srgrimes } 136125274Sache } else { 137125274Sache env = __get_locale_env(category); 138125274Sache if (strlen(env) > ENCODING_LEN) { 139125274Sache errno = EINVAL; 140125274Sache return (NULL); 141125274Sache } 142125274Sache (void)strcpy(new_categories[category], env); 1431573Srgrimes } 144101366Sache } else if (category != LC_ALL) { 145101366Sache if (strlen(locale) > ENCODING_LEN) { 146101366Sache errno = EINVAL; 147101366Sache return (NULL); 148101366Sache } 149101366Sache (void)strcpy(new_categories[category], locale); 150101366Sache } else { 15122330Sache if ((r = strchr(locale, '/')) == NULL) { 152101366Sache if (strlen(locale) > ENCODING_LEN) { 153101366Sache errno = EINVAL; 154101366Sache return (NULL); 155101366Sache } 156101193Sache for (i = 1; i < _LC_LAST; ++i) 157101366Sache (void)strcpy(new_categories[i], locale); 1581573Srgrimes } else { 159101259Sache for (i = 1; r[1] == '/'; ++r) 160101259Sache ; 161101259Sache if (!r[1]) { 162101259Sache errno = EINVAL; 1631573Srgrimes return (NULL); /* Hmm, just slashes... */ 164101259Sache } 1651573Srgrimes do { 166101193Sache if (i == _LC_LAST) 167101223Sache break; /* Too many slashes... */ 168101366Sache if ((len = r - locale) > ENCODING_LEN) { 169101366Sache errno = EINVAL; 170101366Sache return (NULL); 171101366Sache } 172114443Snectar (void)strlcpy(new_categories[i], locale, 173101259Sache len + 1); 17422330Sache i++; 175123801Sache while (*r == '/') 176123801Sache r++; 1771573Srgrimes locale = r; 178123801Sache while (*r && *r != '/') 179123801Sache r++; 1801573Srgrimes } while (*locale); 18153050Sache while (i < _LC_LAST) { 1821573Srgrimes (void)strcpy(new_categories[i], 183101259Sache new_categories[i-1]); 18453050Sache i++; 18553050Sache } 1861573Srgrimes } 1871573Srgrimes } 1881573Srgrimes 18965420Simp if (category != LC_ALL) 19019971Sache return (loadlocale(category)); 1911573Srgrimes 19219971Sache for (i = 1; i < _LC_LAST; ++i) { 19319971Sache (void)strcpy(saved_categories[i], current_categories[i]); 19419964Sache if (loadlocale(i) == NULL) { 195101366Sache saverr = errno; 19619964Sache for (j = 1; j < i; j++) { 19719964Sache (void)strcpy(new_categories[j], 198101259Sache saved_categories[j]); 199101498Sache if (loadlocale(j) == NULL) { 200101498Sache (void)strcpy(new_categories[j], "C"); 201101498Sache (void)loadlocale(j); 202101498Sache } 20319964Sache } 204101259Sache errno = saverr; 20519964Sache return (NULL); 20619964Sache } 20719971Sache } 20819964Sache return (currentlocale()); 2091573Srgrimes} 2101573Srgrimes 2111573Srgrimesstatic char * 21211695Sachecurrentlocale() 21311695Sache{ 21419964Sache int i; 21511695Sache 21611695Sache (void)strcpy(current_locale_string, current_categories[1]); 21711695Sache 21811695Sache for (i = 2; i < _LC_LAST; ++i) 21911695Sache if (strcmp(current_categories[1], current_categories[i])) { 22035523Sache for (i = 2; i < _LC_LAST; ++i) { 221101259Sache (void)strcat(current_locale_string, "/"); 222101259Sache (void)strcat(current_locale_string, 223101259Sache current_categories[i]); 22435523Sache } 22511695Sache break; 22611695Sache } 22711695Sache return (current_locale_string); 22811695Sache} 22911695Sache 23011695Sachestatic char * 2311573Srgrimesloadlocale(category) 2321573Srgrimes int category; 2331573Srgrimes{ 23419971Sache char *new = new_categories[category]; 23519971Sache char *old = current_categories[category]; 236101498Sache int (*func)(const char *); 237116847Sphantom int saved_errno; 23819964Sache 239101259Sache if ((new[0] == '.' && 240101259Sache (new[1] == '\0' || (new[1] == '.' && new[2] == '\0'))) || 241101259Sache strchr(new, '/') != NULL) { 242101259Sache errno = EINVAL; 243101259Sache return (NULL); 244101259Sache } 245101259Sache 246116847Sphantom saved_errno = errno; 247116847Sphantom errno = __detect_path_locale(); 248116847Sphantom if (errno != 0) 249116847Sphantom return (NULL); 250116847Sphantom errno = saved_errno; 25119964Sache 252101259Sache switch (category) { 253101259Sache case LC_CTYPE: 254117270Sache func = __wrap_setrunelocale; 255101292Sache break; 256101259Sache case LC_COLLATE: 257101292Sache func = __collate_load_tables; 258101292Sache break; 259101259Sache case LC_TIME: 260101292Sache func = __time_load_locale; 261101292Sache break; 262101259Sache case LC_NUMERIC: 263101292Sache func = __numeric_load_locale; 264101292Sache break; 265101259Sache case LC_MONETARY: 266101292Sache func = __monetary_load_locale; 267101292Sache break; 268101259Sache case LC_MESSAGES: 269101292Sache func = __messages_load_locale; 270101292Sache break; 271101259Sache default: 272101259Sache errno = EINVAL; 273101259Sache return (NULL); 2746485Sache } 275101292Sache 276101292Sache if (strcmp(new, old) == 0) 277101292Sache return (old); 278101292Sache 279101498Sache if (func(new) != _LDP_ERROR) { 280101292Sache (void)strcpy(old, new); 281227753Stheraven (void)strcpy(__xlocale_global_locale.components[category-1]->locale, new); 282101498Sache return (old); 283101498Sache } 284101292Sache 285101498Sache return (NULL); 28619964Sache} 28719964Sache 288227753Stheravenconst char * 289125274Sache__get_locale_env(category) 290125274Sache int category; 291125274Sache{ 292125274Sache const char *env; 293125274Sache 294125274Sache /* 1. check LC_ALL. */ 295125274Sache env = getenv(categories[0]); 296125274Sache 297125274Sache /* 2. check LC_* */ 298125274Sache if (env == NULL || !*env) 299125274Sache env = getenv(categories[category]); 300125274Sache 301125274Sache /* 3. check LANG */ 302125274Sache if (env == NULL || !*env) 303125274Sache env = getenv("LANG"); 304125274Sache 305125274Sache /* 4. if none is set, fall to "C" */ 306125274Sache if (env == NULL || !*env) 307125274Sache env = "C"; 308125274Sache 309125274Sache return (env); 310125274Sache} 311125274Sache 312116847Sphantom/* 313116847Sphantom * Detect locale storage location and store its value to _PathLocale variable 314116847Sphantom */ 315116847Sphantomint 316116847Sphantom__detect_path_locale(void) 317116847Sphantom{ 318116847Sphantom if (_PathLocale == NULL) { 319116847Sphantom char *p = getenv("PATH_LOCALE"); 320116847Sphantom 321121667Stjr if (p != NULL && !issetugid()) { 322116847Sphantom if (strlen(p) + 1/*"/"*/ + ENCODING_LEN + 323116847Sphantom 1/*"/"*/ + CATEGORY_LEN >= PATH_MAX) 324116847Sphantom return (ENAMETOOLONG); 325116847Sphantom _PathLocale = strdup(p); 326116847Sphantom if (_PathLocale == NULL) 327116847Sphantom return (errno == 0 ? ENOMEM : errno); 328116847Sphantom } else 329116847Sphantom _PathLocale = _PATH_LOCALE; 330116847Sphantom } 331116847Sphantom return (0); 332116847Sphantom} 333116847Sphantom 334