123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332 |
- /* setlocale.c
- * Load LC_CTYPE and LC_COLLATE locale only special for uclibc
- *
- * Written by Vladimir Oleynik (c) vodz@usa.net
- *
- * This file is part of the uClibc C library and is distributed
- * under the GNU Library General Public License.
- * used ideas is part of the GNU C Library.
- */
- #include <locale.h>
- #include <stdio.h> /* NULL, fopen */
- #include <stdlib.h> /* malloc */
- #include <string.h>
- #include <limits.h> /* PATH_MAX */
- #include <errno.h> /* EINVAL */
- #include <unistd.h> /* get(e)[u|g]id */
- #include "_locale.h"
- static char C_LOCALE_NAME []="C";
- static char POSIX_LOCALE_NAME[]="POSIX";
- static char composite_name_C []=
- "LC_CTYPE=C;LC_NUMERIC=C;LC_TIME=C;LC_COLLATE=C;LC_MONETARY=C;LC_MESSAGES=C";
- #ifdef __UCLIBC_HAS_LOCALE__
- #ifdef TEST_LOCALE
- static const char PATH_LOCALE[]="./";
- #else
- static const char PATH_LOCALE[]=__UCLIBC_LOCALE_DIR;
- #endif
- struct SAV_LOADED_LOCALE {
- int category;
- char *locale;
- const unsigned char *buf;
- struct SAV_LOADED_LOCALE *next;
- };
- static struct SAV_LOADED_LOCALE sll_C_LC_MESSAGES = {
- LC_MESSAGES, C_LOCALE_NAME, 0, 0
- };
- static struct SAV_LOADED_LOCALE sll_C_LC_MONETARY = {
- LC_MONETARY, C_LOCALE_NAME, 0, &sll_C_LC_MESSAGES
- };
- static struct SAV_LOADED_LOCALE sll_C_LC_COLLATE = {
- LC_COLLATE, C_LOCALE_NAME, 0, &sll_C_LC_MONETARY
- };
- static struct SAV_LOADED_LOCALE sll_C_LC_TIME = {
- LC_TIME, C_LOCALE_NAME, 0, &sll_C_LC_COLLATE
- };
- static struct SAV_LOADED_LOCALE sll_C_LC_NUMERIC = {
- LC_NUMERIC, C_LOCALE_NAME, 0, &sll_C_LC_TIME
- };
- static struct SAV_LOADED_LOCALE sll_C_LC_CTYPE = {
- LC_CTYPE, C_LOCALE_NAME, _uc_ctype_b_C, &sll_C_LC_NUMERIC
- };
- static struct SAV_LOADED_LOCALE *sll = &sll_C_LC_CTYPE;
- #endif /* __UCLIBC_HAS_LOCALE__ */
- static char *nl_current[LC_ALL+1] = {
- C_LOCALE_NAME, C_LOCALE_NAME, C_LOCALE_NAME,
- C_LOCALE_NAME, C_LOCALE_NAME, C_LOCALE_NAME,
- composite_name_C
- };
- static const char * const LC_strs[LC_ALL+1] = {
- "/LC_CTYPE",
- "/LC_NUMERIC",
- "/LC_TIME",
- "/LC_COLLATE",
- "/LC_MONETARY",
- "/LC_MESSAGES",
- "/LC_ALL"
- };
- static char *find_locale(int c, const char **plocale)
- {
- #ifdef __UCLIBC_HAS_LOCALE__
- struct SAV_LOADED_LOCALE *cur;
- #endif
- const char *name = *plocale;
- if (name[0] == '\0') {
- /* The user decides which locale to use by setting environment
- variables. */
- name = getenv (&LC_strs[LC_ALL][1]);
- if (name == NULL || name[0] == '\0')
- name = getenv (&LC_strs[c][1]);
- if (name == NULL || name[0] == '\0')
- name = getenv ("LANG");
- if (name == NULL || name[0] == '\0')
- name = C_LOCALE_NAME;
- }
- if (strcmp (name, C_LOCALE_NAME) == 0 ||
- strcmp (name, POSIX_LOCALE_NAME) == 0 ||
- /* TODO! */ (c!=LC_CTYPE && c!=LC_COLLATE))
- name = C_LOCALE_NAME;
- *plocale = name;
- #ifdef __UCLIBC_HAS_LOCALE__
- for(cur = sll; cur; cur = cur->next)
- if(cur->category == c && strcmp(cur->locale, name)==0)
- return cur->locale;
- #else
- if(name == C_LOCALE_NAME)
- return C_LOCALE_NAME;
- #endif
- return NULL;
- }
- #ifdef __UCLIBC_HAS_LOCALE__
- static char *load_locale(int category, const char *locale)
- {
- FILE * fl;
- char full_path[PATH_MAX];
- char * buf = 0;
- struct SAV_LOADED_LOCALE *cur;
- struct SAV_LOADED_LOCALE *bottom;
- int bufsize;
- int l = strlen(locale);
- if((l+sizeof(PATH_LOCALE)+strlen(LC_strs[category]))>=PATH_MAX)
- return NULL;
- /* Not allow acces suid/sgid binaries to outside PATH_LOCALE */
- if((geteuid()!=getuid() || getegid()!=getgid()) &&
- strchr(locale, '/')!=NULL)
- return NULL;
- strcpy(full_path, PATH_LOCALE);
- strcat(full_path, locale);
- strcat(full_path, LC_strs[category]);
- fl = fopen(full_path, "r");
- if(fl==0)
- return NULL;
- switch(category) {
- case LC_CTYPE:
- bufsize = LOCALE_BUF_SIZE;
- break;
- case LC_COLLATE:
- bufsize = 256;
- break;
- default: /* TODO */
- bufsize = 0;
- break;
- }
- cur = malloc(sizeof(struct SAV_LOADED_LOCALE)+bufsize+l+2);
- if(cur) {
- buf = (char *)(cur+1);
- if(bufsize!=0 && fread(buf, 1, bufsize+1, fl)!=(bufsize)) {
- /* broken locale file */
- free(cur);
- buf = 0;
- #ifdef TEST_LOCALE
- fprintf(stderr, "\nbroken locale file\n");
- #endif
- }
- }
- fclose(fl);
- if(cur==0) /* not enough memory */
- return NULL;
- if(buf==0) { /* broken locale file, set to "C" */
- return C_LOCALE_NAME;
- }
- cur->next = 0;
- cur->buf = buf;
- cur->category = category;
- cur->locale = buf+bufsize;
- strcpy(cur->locale, locale);
- bottom = sll;
- while(bottom->next!=0)
- bottom = bottom->next;
- bottom->next = cur;
- return cur->locale;
- }
- static char *set_composite(int category, char *locale)
- {
- int i, l;
- char *old_composite_name = nl_current[LC_ALL];
- char *new_composite_name;
- struct SAV_LOADED_LOCALE *cur;
- for(l=i=0; i<LC_ALL; i++) {
- new_composite_name = i == category ? locale : nl_current[i];
- /* '=' + ';' or '\0' */
- l += strlen(&LC_strs[i][1])+strlen(new_composite_name)+2;
- }
- new_composite_name = malloc(l);
- if(new_composite_name==NULL)
- return NULL;
- if(old_composite_name!=composite_name_C)
- free(old_composite_name);
- nl_current[category] = locale; /* change after malloc */
- *new_composite_name = 0;
- for(i=0; i<LC_ALL; i++) {
- if(i)
- strcat(new_composite_name, ";");
- strcat(new_composite_name, &LC_strs[i][1]);
- strcat(new_composite_name, "=");
- strcat(new_composite_name, nl_current[i]);
- }
- nl_current[LC_ALL] = new_composite_name;
- /* set locale data for ctype and strcollate functions */
- for(cur = sll; ; cur = cur->next)
- if(cur->category == category && cur->locale == locale)
- break;
- switch(category) {
- case LC_CTYPE:
- _uc_ctype_b = cur->buf;
- _uc_ctype_trans = cur->buf+LOCALE_BUF_SIZE/2;
- break;
- case LC_COLLATE:
- _uc_collate_b = cur->buf;
- break;
- default: /* TODO */
- break;
- }
- return locale;
- }
- #endif /* __UCLIBC_HAS_LOCALE__ */
- char *setlocale(int category, const char *locale)
- {
- char * tl;
- #ifdef __UCLIBC_HAS_LOCALE__
- int i;
- #endif
- if (category < 0 || category > LC_ALL) {
- #ifdef __UCLIBC_HAS_LOCALE__
- einval:
- #endif
- errno = EINVAL;
- return NULL;
- }
- if(locale==NULL)
- return nl_current[category];
- if(category!=LC_ALL) {
- tl = find_locale(category, &locale);
- #ifdef __UCLIBC_HAS_LOCALE__
- if(tl==NULL)
- tl = load_locale(category, locale);
- if(tl) {
- if(nl_current[category] != tl)
- tl = set_composite(category, tl);
- }
- #endif
- return tl;
- }
- /* LC_ALL */
- #ifdef __UCLIBC_HAS_LOCALE__
- /* The user wants to set all categories. The desired locales
- for the individual categories can be selected by using a
- composite locale name. This is a semi-colon separated list
- of entries of the form `CATEGORY=VALUE'. */
- tl = strchr(locale, ';');
- if(tl==NULL) {
- /* This is not a composite name. Load the data for each category. */
- for(i=0; i<LC_ALL; i++)
- setlocale(i, locale); /* recursive */
- } else {
- /* This is a composite name. Make a copy and split it up. */
- const char *newnames[LC_ALL];
- char *np;
- char *cp;
- i = strlen(locale);
- np = alloca (i);
- if(np)
- strcpy(np, locale);
- else
- return NULL;
- for (i = 0; i < LC_ALL; ++i)
- newnames[i] = 0;
- while ((cp = strchr (np, '=')) != NULL) {
- for (i = 0; i < LC_ALL; ++i)
- if ((size_t) (cp - np) == strlen(&LC_strs[i][1])
- && memcmp (np, &LC_strs[i][1], (cp - np)) == 0)
- break;
- if (i == LC_ALL)
- /* Bogus category name. */
- goto einval;
- /* Found the category this clause sets. */
- newnames[i] = ++cp;
- cp = strchr (cp, ';');
- if (cp != NULL) {
- /* Examine the next clause. */
- *cp = '\0';
- np = cp + 1;
- } else
- /* This was the last clause. We are done. */
- break;
- }
- for (i = 0; i < LC_ALL; ++i)
- setlocale(i, newnames[i]); /* recursive */
- }
- #endif
- return nl_current[LC_ALL];
- }
|