From d85536af73dc5d327075d983abfa69d70e129d20 Mon Sep 17 00:00:00 2001 From: Manuel Novoa III Date: Sat, 2 Jun 2001 21:46:42 +0000 Subject: Add locale-enabled strcoll function from vodz, plus supporting tool. --- libc/misc/locale/locale.c | 321 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 246 insertions(+), 75 deletions(-) (limited to 'libc/misc/locale/locale.c') diff --git a/libc/misc/locale/locale.c b/libc/misc/locale/locale.c index 2abdde34e..d978ae37c 100644 --- a/libc/misc/locale/locale.c +++ b/libc/misc/locale/locale.c @@ -1,5 +1,5 @@ /* setlocale.c - * Load LC_CTYPE locale only special for uclibc + * Load LC_CTYPE and LC_COLLATE locale only special for uclibc * * Written by Vladimir Oleynik (c) vodz@usa.net * @@ -8,103 +8,162 @@ * used ideas is part of the GNU C Library. */ -/* - * No-locale-support setlocale() added. - */ - #include #include /* NULL, fopen */ #include /* malloc */ #include #include /* PATH_MAX */ +#include /* EINVAL */ +#include /* get(e)[u|g]id */ -#include "../ctype/ctype.h" - -#undef TEST_LOCALE +#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__ -static char C_LOCALE_NAME[]="C"; - #ifdef TEST_LOCALE static const char PATH_LOCALE[]="./"; #else static const char PATH_LOCALE[]=__UCLIBC_LOCALE_DIR; #endif -static const char LC_CTYPE_STR[]="/LC_CTYPE"; - 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 sav_loaded_locale [1] = { - { C_LOCALE_NAME, _uc_ctype_b_C, 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 * old_locale = sav_loaded_locale; +static struct SAV_LOADED_LOCALE sll_C_LC_COLLATE = { + LC_COLLATE, C_LOCALE_NAME, 0, &sll_C_LC_MONETARY +}; -static char *set_new_locale(struct SAV_LOADED_LOCALE * s_locale) -{ - _uc_ctype_b = s_locale->buf; - _uc_ctype_trans = s_locale->buf+LOCALE_BUF_SIZE/2; - old_locale = s_locale; - return s_locale->locale; -} +static struct SAV_LOADED_LOCALE sll_C_LC_TIME = { + LC_TIME, C_LOCALE_NAME, 0, &sll_C_LC_COLLATE +}; -/* Current support only LC_CTYPE or LC_ALL category */ +static struct SAV_LOADED_LOCALE sll_C_LC_NUMERIC = { + LC_NUMERIC, C_LOCALE_NAME, 0, &sll_C_LC_TIME +}; -char *setlocale(int category, const char *locale) +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) { - FILE * fl; +#ifdef __UCLIBC_HAS_LOCALE__ struct SAV_LOADED_LOCALE *cur; - struct SAV_LOADED_LOCALE *bottom; - char full_path[PATH_MAX]; - char * buf = 0; - int l; +#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(category!=LC_CTYPE && category!=LC_ALL) - return NULL; + if (strcmp (name, C_LOCALE_NAME) == 0 || + strcmp (name, POSIX_LOCALE_NAME) == 0 || + /* TODO! */ (c!=LC_CTYPE && c!=LC_COLLATE)) + name = C_LOCALE_NAME; - if(locale==0) - return set_new_locale(old_locale); + *plocale = name; - if(strcmp(locale, "POSIX")==0) - return set_new_locale(sav_loaded_locale); - else if(*locale == '\0') { +#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; +} - locale = getenv(LC_CTYPE_STR+1); - if(locale == 0 || *locale == 0) - locale = getenv("LANG"); - if(locale == 0 || *locale == '\0') - return set_new_locale(old_locale); - if(strcmp(locale, "POSIX")==0) - return set_new_locale(sav_loaded_locale); - } - for(cur = sav_loaded_locale; cur; cur = cur->next) - if(strcmp(cur->locale, locale)==0) - return set_new_locale(cur); +#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); - l = strlen(locale); - if((l+sizeof(PATH_LOCALE)+sizeof(LC_CTYPE_STR))>=PATH_MAX) + 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_CTYPE_STR); + strcat(full_path, LC_strs[category]); fl = fopen(full_path, "r"); if(fl==0) return NULL; - cur = malloc(sizeof(struct SAV_LOADED_LOCALE)+LOCALE_BUF_SIZE+l); + 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(fread(buf, 1, LOCALE_BUF_SIZE+1, fl)!=(LOCALE_BUF_SIZE)) { + if(bufsize!=0 && fread(buf, 1, bufsize+1, fl)!=(bufsize)) { /* broken locale file */ free(cur); buf = 0; @@ -117,45 +176,157 @@ char *setlocale(int category, const char *locale) fclose(fl); if(cur==0) /* not enough memory */ return NULL; - if(buf==0) /* broken locale file, set to "C" */ - return set_new_locale(sav_loaded_locale); + if(buf==0) { /* broken locale file, set to "C" */ + return C_LOCALE_NAME; + } - cur->next = 0; - strcpy(buf+LOCALE_BUF_SIZE, locale); + cur->next = 0; + cur->buf = buf; + cur->category = category; + cur->locale = buf+bufsize; + strcpy(cur->locale, locale); - bottom = sav_loaded_locale; + bottom = sll; while(bottom->next!=0) bottom = bottom->next; bottom->next = cur; - /* next two line only pedantic */ - cur->buf = buf; - cur->locale = buf+LOCALE_BUF_SIZE; + 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; - return set_new_locale(cur); + for(l=i=0; i LC_ALL)) { /* Illegal category! */ + if (category < 0 || category > LC_ALL) { +#ifdef __UCLIBC_HAS_LOCALE__ +einval: +#endif + errno = EINVAL; return NULL; } - return "C"; -} + 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