diff options
Diffstat (limited to 'libc/misc/locale')
-rw-r--r-- | libc/misc/locale/_locale.h | 22 | ||||
-rw-r--r-- | libc/misc/locale/locale.c | 321 |
2 files changed, 268 insertions, 75 deletions
diff --git a/libc/misc/locale/_locale.h b/libc/misc/locale/_locale.h new file mode 100644 index 000000000..139a862f9 --- /dev/null +++ b/libc/misc/locale/_locale.h @@ -0,0 +1,22 @@ +extern const unsigned char *_uc_ctype_b; +extern const unsigned char *_uc_ctype_trans; + +extern const unsigned char _uc_ctype_b_C[256+256]; + +#define LOCALE_BUF_SIZE (sizeof(_uc_ctype_b_C)) + +#define ISbit(bit) (1 << bit) + +enum +{ + ISprint = ISbit (0), /* 1 Printable. */ + ISupper = ISbit (1), /* 2 UPPERCASE. */ + ISlower = ISbit (2), /* 4 lowercase. */ + IScntrl = ISbit (3), /* 8 Control character. */ + ISspace = ISbit (4), /* 16 Whitespace. */ + ISpunct = ISbit (5), /* 32 Punctuation. */ + ISalpha = ISbit (6), /* 64 Alphabetic. */ + ISxdigit = ISbit (7), /* 128 Hexnumeric. */ +}; + +extern const unsigned char *_uc_collate_b; 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 <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 "../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; 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; } -#else /* no locale support */ +#endif /* __UCLIBC_HAS_LOCALE__ */ char *setlocale(int category, const char *locale) { - /* Allow locales "C" and "" (native). Both are "C" for our purposes. */ - if (locale) { - if (*locale == 'C') { - ++locale; - } - if (*locale) { /* locale wasn't "C" or ""!! */ - return NULL; - } - } + char * tl; +#ifdef __UCLIBC_HAS_LOCALE__ + int i; +#endif - /* Allow any legal category for "C" or "" (native) locale. */ - if((category < LC_CTYPE) || (category > 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<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]; +} |