summaryrefslogtreecommitdiff
path: root/libc/misc/locale
diff options
context:
space:
mode:
Diffstat (limited to 'libc/misc/locale')
-rw-r--r--libc/misc/locale/_locale.h22
-rw-r--r--libc/misc/locale/locale.c321
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];
+}