1192595Sdes/* 292559Sdes * Copyright (c) 1996 - 2002 FreeBSD Project 365668Skris * Copyright (c) 1991, 1993 465668Skris * The Regents of the University of California. All rights reserved. 565668Skris * 665668Skris * This code is derived from software contributed to Berkeley by 765668Skris * Paul Borman at Krystal Technologies. 865668Skris * 965668Skris * Redistribution and use in source and binary forms, with or without 1065668Skris * modification, are permitted provided that the following conditions 1165668Skris * are met: 1265668Skris * 1. Redistributions of source code must retain the above copyright 1365668Skris * notice, this list of conditions and the following disclaimer. 1465668Skris * 2. Redistributions in binary form must reproduce the above copyright 1592559Sdes * notice, this list of conditions and the following disclaimer in the 1665668Skris * documentation and/or other materials provided with the distribution. 1765668Skris * 4. Neither the name of the University nor the names of its contributors 1865668Skris * may be used to endorse or promote products derived from this software 1965668Skris * without specific prior written permission. 2065668Skris * 2165668Skris * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2265668Skris * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2365668Skris * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2465668Skris * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2565668Skris * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2665668Skris * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2765668Skris * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2865668Skris * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2965668Skris * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3065668Skris * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3165668Skris * SUCH DAMAGE. 3265668Skris */ 3365668Skris 3465668Skris#if defined(LIBC_SCCS) && !defined(lint) 3565668Skrisstatic char sccsid[] = "@(#)setlocale.c 8.1 (Berkeley) 7/4/93"; 3665668Skris#endif /* LIBC_SCCS and not lint */ 3757429Smarkm#include <sys/cdefs.h> 3892559Sdes__FBSDID("$FreeBSD: releng/10.2/lib/libc/locale/setlocale.c 228921 2011-12-27 23:28:01Z jilles $"); 3992559Sdes 4057429Smarkm#include <sys/types.h> 4157429Smarkm#include <sys/stat.h> 4257429Smarkm#include <errno.h> 4357429Smarkm#include <limits.h> 4457429Smarkm#include <locale.h> 4557429Smarkm#include <paths.h> /* for _PATH_LOCALE */ 4657429Smarkm#include <stdlib.h> 4760573Skris#include <string.h> 4860573Skris#include <unistd.h> 4960573Skris#include "collate.h" 5060573Skris#include "lmonetary.h" /* for __monetary_load_locale() */ 5160573Skris#include "lnumeric.h" /* for __numeric_load_locale() */ 5276262Sgreen#include "lmessages.h" /* for __messages_load_locale() */ 5376262Sgreen#include "setlocale.h" 5476262Sgreen#include "ldpart.h" 5592559Sdes#include "../stdtime/timelocal.h" /* for __time_load_locale() */ 5692559Sdes 5757429Smarkm/* 5865668Skris * Category names for getenv() 5965668Skris */ 6065668Skrisstatic const char categories[_LC_LAST][12] = { 6192559Sdes "LC_ALL", 62157019Sdes "LC_COLLATE", 63181111Sdes "LC_CTYPE", 64157019Sdes "LC_MONETARY", 6557429Smarkm "LC_NUMERIC", 66181111Sdes "LC_TIME", 67181111Sdes "LC_MESSAGES", 68181111Sdes}; 69181111Sdes 70181111Sdes/* 71181111Sdes * Current locales for each category 72181111Sdes */ 73181111Sdesstatic char current_categories[_LC_LAST][ENCODING_LEN + 1] = { 74181111Sdes "C", 75181111Sdes "C", 76181111Sdes "C", 77181111Sdes "C", 78181111Sdes "C", 79181111Sdes "C", 80181111Sdes "C", 81181111Sdes}; 82181111Sdes 83181111Sdes/* 8465668Skris * Path to locale storage directory 8557429Smarkm */ 8657429Smarkmchar *_PathLocale; 8757429Smarkm 8892559Sdes/* 8992559Sdes * The locales we are going to try and load 9060573Skris */ 9160573Skrisstatic char new_categories[_LC_LAST][ENCODING_LEN + 1]; 9260573Skrisstatic char saved_categories[_LC_LAST][ENCODING_LEN + 1]; 9360573Skris 9460573Skrisstatic char current_locale_string[_LC_LAST * (ENCODING_LEN + 1/*"/"*/ + 1)]; 95137019Sdes 9674500Sgreenstatic char *currentlocale(void); 97106130Sdesstatic char *loadlocale(int); 98147005Sdesconst char *__get_locale_env(int); 9992559Sdes 10092559Sdeschar * 10157429Smarkmsetlocale(category, locale) 10257429Smarkm int category; 10357429Smarkm const char *locale; 10457429Smarkm{ 10560573Skris int i, j, len, saverr; 106192595Sdes const char *env, *r; 10792559Sdes 10857429Smarkm if (category < LC_ALL || category >= _LC_LAST) { 10957429Smarkm errno = EINVAL; 11057429Smarkm return (NULL); 11160573Skris } 11299063Sdes 11399063Sdes if (locale == NULL) 11499063Sdes return (category != LC_ALL ? 11599063Sdes current_categories[category] : currentlocale()); 11699063Sdes 11799063Sdes /* 11860573Skris * Default to the current locale for everything. 11992559Sdes */ 12060573Skris for (i = 1; i < _LC_LAST; ++i) 12160573Skris (void)strcpy(new_categories[i], current_categories[i]); 12260573Skris 12360573Skris /* 124181111Sdes * Now go fill up new_categories from the locale argument 125181111Sdes */ 12692559Sdes if (!*locale) { 127157019Sdes if (category == LC_ALL) { 128181111Sdes for (i = 1; i < _LC_LAST; ++i) { 12960573Skris env = __get_locale_env(i); 13065668Skris if (strlen(env) > ENCODING_LEN) { 131157019Sdes errno = EINVAL; 132157019Sdes return (NULL); 133181111Sdes } 134181111Sdes (void)strcpy(new_categories[i], env); 135157019Sdes } 136181111Sdes } else { 137181111Sdes env = __get_locale_env(category); 138181111Sdes if (strlen(env) > ENCODING_LEN) { 139181111Sdes errno = EINVAL; 140181111Sdes return (NULL); 14165668Skris } 14265668Skris (void)strcpy(new_categories[category], env); 14360573Skris } 14460573Skris } else if (category != LC_ALL) { 14560573Skris if (strlen(locale) > ENCODING_LEN) { 14660573Skris errno = EINVAL; 14765668Skris return (NULL); 14892559Sdes } 149181111Sdes (void)strcpy(new_categories[category], locale); 15092559Sdes } else { 151181111Sdes if ((r = strchr(locale, '/')) == NULL) { 15292559Sdes if (strlen(locale) > ENCODING_LEN) { 15392559Sdes errno = EINVAL; 15465668Skris return (NULL); 15592559Sdes } 15692559Sdes for (i = 1; i < _LC_LAST; ++i) 15792559Sdes (void)strcpy(new_categories[i], locale); 15892559Sdes } else { 15992559Sdes for (i = 1; r[1] == '/'; ++r) 16065668Skris ; 16192559Sdes if (!r[1]) { 16292559Sdes errno = EINVAL; 16392559Sdes return (NULL); /* Hmm, just slashes... */ 16492559Sdes } 16592559Sdes do { 16660573Skris if (i == _LC_LAST) 16792559Sdes break; /* Too many slashes... */ 16892559Sdes if ((len = r - locale) > ENCODING_LEN) { 16998684Sdes errno = EINVAL; 17098684Sdes return (NULL); 17160573Skris } 172157019Sdes (void)strlcpy(new_categories[i], locale, 173157019Sdes len + 1); 17498684Sdes i++; 17598684Sdes while (*r == '/') 17698684Sdes r++; 17798684Sdes locale = r; 17898684Sdes while (*r && *r != '/') 17998684Sdes r++; 18098684Sdes } while (*locale); 181149753Sdes while (i < _LC_LAST) { 18298684Sdes (void)strcpy(new_categories[i], 18398684Sdes new_categories[i-1]); 18492559Sdes i++; 18560573Skris } 186157019Sdes } 18792559Sdes } 18899063Sdes 189181111Sdes if (category != LC_ALL) 19092559Sdes return (loadlocale(category)); 19192559Sdes 19292559Sdes for (i = 1; i < _LC_LAST; ++i) { 19369587Sgreen (void)strcpy(saved_categories[i], current_categories[i]); 19492559Sdes if (loadlocale(i) == NULL) { 19592559Sdes saverr = errno; 196157019Sdes for (j = 1; j < i; j++) { 197181111Sdes (void)strcpy(new_categories[j], 198181111Sdes saved_categories[j]); 199181111Sdes if (loadlocale(j) == NULL) { 200181111Sdes (void)strcpy(new_categories[j], "C"); 201181111Sdes (void)loadlocale(j); 20292559Sdes } 20392559Sdes } 204137019Sdes errno = saverr; 20560573Skris return (NULL); 20692559Sdes } 20760573Skris } 20892559Sdes return (currentlocale()); 20992559Sdes} 21092559Sdes 21192559Sdesstatic char * 21292559Sdescurrentlocale() 21392559Sdes{ 21492559Sdes int i; 21592559Sdes 21692559Sdes (void)strcpy(current_locale_string, current_categories[1]); 21792559Sdes 218181111Sdes for (i = 2; i < _LC_LAST; ++i) 21960573Skris if (strcmp(current_categories[1], current_categories[i])) { 22092559Sdes for (i = 2; i < _LC_LAST; ++i) { 22160573Skris (void)strcat(current_locale_string, "/"); 222137019Sdes (void)strcat(current_locale_string, 22392559Sdes current_categories[i]); 22492559Sdes } 22560573Skris break; 22692559Sdes } 22792559Sdes return (current_locale_string); 22892559Sdes} 22992559Sdes 23092559Sdesstatic char * 23160573Skrisloadlocale(category) 23292559Sdes int category; 23392559Sdes{ 23492559Sdes char *new = new_categories[category]; 23592559Sdes char *old = current_categories[category]; 236162856Sdes int (*func)(const char *); 23792559Sdes int saved_errno; 238162856Sdes 239181111Sdes if ((new[0] == '.' && 240162856Sdes (new[1] == '\0' || (new[1] == '.' && new[2] == '\0'))) || 241181111Sdes strchr(new, '/') != NULL) { 242181111Sdes errno = EINVAL; 243162856Sdes return (NULL); 244147005Sdes } 245147005Sdes 246147005Sdes saved_errno = errno; 247147005Sdes errno = __detect_path_locale(); 248192595Sdes if (errno != 0) 249137019Sdes return (NULL); 25060573Skris errno = saved_errno; 25192559Sdes 25260573Skris switch (category) { 25392559Sdes case LC_CTYPE: 254149753Sdes func = __wrap_setrunelocale; 25592559Sdes break; 256149753Sdes case LC_COLLATE: 257181111Sdes func = __collate_load_tables; 25892559Sdes break; 25960573Skris case LC_TIME: 26092559Sdes func = __time_load_locale; 26160573Skris break; 26292559Sdes case LC_NUMERIC: 26360573Skris func = __numeric_load_locale; 26492559Sdes break; 26560573Skris case LC_MONETARY: 26692559Sdes func = __monetary_load_locale; 26792559Sdes break; 26860573Skris case LC_MESSAGES: 26992559Sdes func = __messages_load_locale; 27060573Skris break; 27192559Sdes default: 272181111Sdes errno = EINVAL; 27392559Sdes return (NULL); 27492559Sdes } 27576262Sgreen 27692559Sdes if (strcmp(new, old) == 0) 27792559Sdes return (old); 27892559Sdes 27976262Sgreen if (func(new) != _LDP_ERROR) { 28057429Smarkm (void)strcpy(old, new); 281 (void)strcpy(__xlocale_global_locale.components[category-1]->locale, new); 282 return (old); 283 } 284 285 return (NULL); 286} 287 288const char * 289__get_locale_env(category) 290 int category; 291{ 292 const char *env; 293 294 /* 1. check LC_ALL. */ 295 env = getenv(categories[0]); 296 297 /* 2. check LC_* */ 298 if (env == NULL || !*env) 299 env = getenv(categories[category]); 300 301 /* 3. check LANG */ 302 if (env == NULL || !*env) 303 env = getenv("LANG"); 304 305 /* 4. if none is set, fall to "C" */ 306 if (env == NULL || !*env) 307 env = "C"; 308 309 return (env); 310} 311 312/* 313 * Detect locale storage location and store its value to _PathLocale variable 314 */ 315int 316__detect_path_locale(void) 317{ 318 if (_PathLocale == NULL) { 319 char *p = getenv("PATH_LOCALE"); 320 321 if (p != NULL && !issetugid()) { 322 if (strlen(p) + 1/*"/"*/ + ENCODING_LEN + 323 1/*"/"*/ + CATEGORY_LEN >= PATH_MAX) 324 return (ENAMETOOLONG); 325 _PathLocale = strdup(p); 326 if (_PathLocale == NULL) 327 return (errno == 0 ? ENOMEM : errno); 328 } else 329 _PathLocale = _PATH_LOCALE; 330 } 331 return (0); 332} 333 334