|
@@ -1,5 +1,5 @@
|
|
|
/* setlocale.c
|
|
/* 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
|
|
* Written by Vladimir Oleynik (c) vodz@usa.net
|
|
|
*
|
|
*
|
|
@@ -8,103 +8,162 @@
|
|
|
* used ideas is part of the GNU C Library.
|
|
* used ideas is part of the GNU C Library.
|
|
|
*/
|
|
*/
|
|
|
|
|
|
|
|
-/*
|
|
|
|
|
- * No-locale-support setlocale() added.
|
|
|
|
|
- */
|
|
|
|
|
-
|
|
|
|
|
#include <locale.h>
|
|
#include <locale.h>
|
|
|
#include <stdio.h> /* NULL, fopen */
|
|
#include <stdio.h> /* NULL, fopen */
|
|
|
#include <stdlib.h> /* malloc */
|
|
#include <stdlib.h> /* malloc */
|
|
|
#include <string.h>
|
|
#include <string.h>
|
|
|
#include <limits.h> /* PATH_MAX */
|
|
#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__
|
|
#ifdef __UCLIBC_HAS_LOCALE__
|
|
|
|
|
|
|
|
-static char C_LOCALE_NAME[]="C";
|
|
|
|
|
-
|
|
|
|
|
#ifdef TEST_LOCALE
|
|
#ifdef TEST_LOCALE
|
|
|
static const char PATH_LOCALE[]="./";
|
|
static const char PATH_LOCALE[]="./";
|
|
|
#else
|
|
#else
|
|
|
static const char PATH_LOCALE[]=__UCLIBC_LOCALE_DIR;
|
|
static const char PATH_LOCALE[]=__UCLIBC_LOCALE_DIR;
|
|
|
#endif
|
|
#endif
|
|
|
|
|
|
|
|
-static const char LC_CTYPE_STR[]="/LC_CTYPE";
|
|
|
|
|
-
|
|
|
|
|
struct SAV_LOADED_LOCALE {
|
|
struct SAV_LOADED_LOCALE {
|
|
|
|
|
+ int category;
|
|
|
char *locale;
|
|
char *locale;
|
|
|
const unsigned char *buf;
|
|
const unsigned char *buf;
|
|
|
struct SAV_LOADED_LOCALE *next;
|
|
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 *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;
|
|
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);
|
|
strcpy(full_path, PATH_LOCALE);
|
|
|
strcat(full_path, locale);
|
|
strcat(full_path, locale);
|
|
|
- strcat(full_path, LC_CTYPE_STR);
|
|
|
|
|
|
|
+ strcat(full_path, LC_strs[category]);
|
|
|
fl = fopen(full_path, "r");
|
|
fl = fopen(full_path, "r");
|
|
|
if(fl==0)
|
|
if(fl==0)
|
|
|
return NULL;
|
|
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) {
|
|
if(cur) {
|
|
|
buf = (char *)(cur+1);
|
|
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 */
|
|
/* broken locale file */
|
|
|
free(cur);
|
|
free(cur);
|
|
|
buf = 0;
|
|
buf = 0;
|
|
@@ -117,45 +176,157 @@ char *setlocale(int category, const char *locale)
|
|
|
fclose(fl);
|
|
fclose(fl);
|
|
|
if(cur==0) /* not enough memory */
|
|
if(cur==0) /* not enough memory */
|
|
|
return NULL;
|
|
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)
|
|
while(bottom->next!=0)
|
|
|
bottom = bottom->next;
|
|
bottom = bottom->next;
|
|
|
bottom->next = cur;
|
|
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)
|
|
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 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
|
|
#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];
|
|
|
|
|
+}
|