summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorManuel Novoa III <mjn3@codepoet.org>2001-06-02 21:46:42 +0000
committerManuel Novoa III <mjn3@codepoet.org>2001-06-02 21:46:42 +0000
commitd85536af73dc5d327075d983abfa69d70e129d20 (patch)
tree21e6939a55be15151ab986f945a52587b950cf89
parent4f8c656e408ff31d081c8f1302cb7adff409ebb8 (diff)
Add locale-enabled strcoll function from vodz, plus supporting tool.
-rw-r--r--extra/locale/README10
-rw-r--r--extra/locale/gen_collate_from_glibc.c207
-rw-r--r--extra/locale/gen_ctype_from_glibc.c5
-rw-r--r--libc/misc/ctype/ctype.c2
-rw-r--r--libc/misc/locale/_locale.h (renamed from libc/misc/ctype/ctype.h)1
-rw-r--r--libc/misc/locale/locale.c321
-rw-r--r--libc/string/Makefile4
-rw-r--r--libc/string/string.c31
8 files changed, 499 insertions, 82 deletions
diff --git a/extra/locale/README b/extra/locale/README
index f97bac36f..6ac487c9a 100644
--- a/extra/locale/README
+++ b/extra/locale/README
@@ -1,6 +1,8 @@
-The program gen_ctype_from_glibc.c will generate data files which can be
-used by uClibc ctype functions to support locales. From the comments:
+The programs gen_ctype_from_glibc.c and gen_collate_from_glibc.c
+will generate data files which can be
+used by uClibc ctype, strcoll and setlocale functions to support locales.
+From the comments:
/*
* Generator locale ctype tables
@@ -10,18 +12,18 @@ used by uClibc ctype functions to support locales. From the comments:
* ./LOCALE/LC_CTYPE files for system with uclibc
*
* Written by Vladimir Oleynik <vodz@usa.net> 2001
- * Base on ideas Nickolay Saukh <nms@ussr.EU.net>
- *
*/
Sample usage to dump all the data files in a tmp directory:
gcc gen_ctype_from_glibc.c -o gen_ctype_from_glibc
+gcc gen_collate_from_glibc.c -o gen_ctype_from_glibc
mkdir tmp
cd tmp
../gen_ctype_from_glibc -d /usr/share/locale -c
+../gen_collate_from_glibc
Then just move the directory or directories you need (not the .c files)
to the uClibc locale file directory you set in Config.
diff --git a/extra/locale/gen_collate_from_glibc.c b/extra/locale/gen_collate_from_glibc.c
new file mode 100644
index 000000000..f6d12ba9c
--- /dev/null
+++ b/extra/locale/gen_collate_from_glibc.c
@@ -0,0 +1,207 @@
+/*
+ * Generator collate table from glibc special for Uclibc.
+ * Author Vladimir Oleynik. vodz@usa.net (c) 2001
+ *
+ * Require setuped work non-C LC_COLLATE
+ * This programm created ./LOCALE/LC_COLLATE file for Uclibc
+ * setlocale() and strcoll().
+ * Without argument this programm used setlocale(LC_COLLATE, "") -
+ * equivalent result setlocale(LC_COLLATE, getenv("LC_XXX"))
+ *
+ * Also, this programm have russian koi8 collate for test
+ * working Uclibc ;-)
+ *
+ */
+
+#include <ctype.h>
+#include <string.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/stat.h> /* mkdir() */
+#include <errno.h>
+
+
+/* For strong test russian locale LC_COLLATE="ru_RU.KOI8-R" */
+static const unsigned char koi8_weights[256] = {
+ 0, 99, 100, 101, 102, 103, 104, 105,
+106, 2, 5, 3, 6, 4, 107, 108,
+109, 110, 111, 112, 113, 114, 115, 116,
+117, 118, 119, 120, 121, 122, 123, 124,
+ 1, 12, 21, 34, 30, 35, 33, 20,
+ 22, 23, 31, 36, 9, 8, 15, 14,
+127, 128, 129, 131, 132, 133, 134, 135,
+136, 137, 11, 10, 38, 40, 42, 13,
+ 29, 138, 140, 142, 144, 146, 148, 150,
+152, 154, 156, 158, 160, 162, 164, 166,
+168, 170, 172, 174, 176, 178, 180, 182,
+184, 186, 188, 24, 32, 25, 17, 7,
+ 16, 139, 141, 143, 145, 147, 149, 151,
+153, 155, 157, 159, 161, 163, 165, 167,
+169, 171, 173, 175, 177, 179, 181, 183,
+185, 187, 189, 26, 43, 27, 18, 125,
+ 50, 52, 54, 58, 62, 66, 70, 74,
+ 78, 82, 86, 90, 91, 92, 93, 94,
+ 95, 96, 97, 48, 98, 45, 46, 47,
+ 39, 41, 126, 49, 44, 130, 19, 37,
+ 51, 53, 55, 203, 56, 57, 59, 60,
+ 61, 63, 64, 65, 67, 68, 69, 71,
+ 72, 73, 75, 202, 76, 77, 79, 80,
+ 81, 83, 84, 85, 87, 88, 89, 28,
+253, 191, 193, 237, 199, 201, 233, 197,
+235, 209, 211, 213, 215, 217, 219, 221,
+223, 255, 225, 227, 229, 231, 205, 195,
+249, 247, 207, 241, 251, 243, 239, 245,
+252, 190, 192, 236, 198, 200, 232, 196,
+234, 208, 210, 212, 214, 216, 218, 220,
+222, 254, 224, 226, 228, 230, 204, 194,
+248, 246, 206, 240, 250, 242, 238, 244
+};
+
+int gen_weights(const char *collate)
+{
+ int weights[256];
+ int i,j;
+ char probe_str1[2];
+ char probe_str2[2];
+ char print_buf[16];
+ int retcode = 0;
+ unsigned char out_weights[256];
+ FILE *out;
+
+ memset(weights, 0, sizeof(weights));
+ probe_str1[1]=probe_str2[1]=0;
+
+ for(i=0; i<256; i++) {
+ probe_str1[0] = i;
+ for(j=0; j<256; j++) {
+ probe_str2[0] = j;
+ if(strcoll(probe_str1, probe_str2)>0) {
+ weights[i]++;
+ if(i==j) {
+ fprintf(stderr, "\
+\nWarning! c1=%d == c2, but strcoll returned greater zero\n", i);
+ retcode++;
+ }
+ }
+ }
+ }
+ for(i=0; i<256; ) {
+ if(isprint(i))
+ sprintf(print_buf, " '%c'", i);
+ else {
+ if(i=='\0')
+ strcpy(print_buf, "'\\0'");
+ else if(i=='\a')
+ strcpy(print_buf, "'\\a'");
+ else if(i=='\b')
+ strcpy(print_buf, "'\\b'");
+ else if(i=='\f')
+ strcpy(print_buf, "'\\f'");
+ else if(i=='\r')
+ strcpy(print_buf, "'\\r'");
+ else if(i=='\t')
+ strcpy(print_buf, "'\\t'");
+ else sprintf(print_buf, " x%02X", i);
+ }
+ printf("weights[%s] = %3d ", print_buf, weights[i]);
+ i++;
+ if( (i%4) == 0)
+ printf("\n");
+ }
+
+ for(i=0; i<256; i++) {
+ if(weights[i]<0 || weights[i]>=256) {
+ fprintf(stderr, "Hmm, weights[%d]=%d\n", i, weights[i]);
+ retcode++;
+ }
+ for(j=0; j<256; j++) {
+ if(i==j)
+ continue;
+ if(weights[i]==weights[j]) {
+ fprintf(stderr, "\
+Warning! c1=%d c2=%d and strcoll returned equivalent weight\n", i, j);
+ retcode++;
+ }
+ }
+ }
+ if(retcode)
+ return 1;
+
+ if(strcasecmp(collate, "ru_RU.KOI8-R")==0 ||
+ strcmp(collate, "ru_RU")==0 ||
+ strcmp(collate, "koi8-r")==0) {
+ for(i=0; i<256; i++)
+ if(weights[i]!=koi8_weights[i]) {
+ fprintf(stderr, "\
+Error koi8-r collate compare, glibc weights[%d]=%d but current generation %d\n",
+ i, koi8_weights[i], weights[i]);
+ retcode++;
+ }
+ if(retcode)
+ return 5;
+ }
+ for(i=0; i<256; i++)
+ out_weights[i] = weights[i];
+ out = fopen("LC_COLLATE", "w");
+ if(out == NULL) {
+ fprintf(stderr, "Can`t create ./%s/LC_COLLATE file\n", collate);
+ return 10;
+ }
+ if(fwrite(out_weights, 1, 256, out)!=256) {
+ fprintf(stderr, "IO error in process write ./%s/LC_COLLATE file\n", collate);
+ return 11;
+ }
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ char *locale;
+ char *slr;
+ char *collate;
+
+ if(argc<1 || argc>2) {
+ fprintf(stderr, "Usage: %s [locale]\n", argv[0]);
+ }
+ locale = argc==1 ? "" : argv[1];
+
+ collate = setlocale(LC_COLLATE, locale);
+ fprintf(stderr, "setlocale(LC_COLLATE, \"%s\") returned %s\n", locale, collate);
+ if(collate==0) {
+ fprintf(stderr, "Can`t set LC_COLLATE\n");
+ return 2;
+ }
+ if(strcmp(collate, "C")==0) {
+ fprintf(stderr, "\
+LC_COLLATE=\"C\" is trivial and not interesting for this programm\n");
+ return 3;
+ }
+ slr = setlocale(LC_CTYPE, locale);
+ fprintf(stderr, "setlocale(LC_CTYPE, \"%s\") returned %s\n", locale, slr);
+ if(slr==0) {
+ slr = setlocale(LC_CTYPE, "POSIX");
+ if(slr==0) {
+ fprintf(stderr, "Hmm, can`t set setlocale(LC_CTYPE, \"POSIX\")\n");
+ return 4;
+ }
+ }
+ if(mkdir(collate, 0755)!=0 && errno!=EEXIST) {
+ fprintf(stderr, "Can`t make directory %s\n", collate);
+ return 6;
+ }
+ if(chdir(collate)) {
+ fprintf(stderr, "Hmm, can`t change directory to %s\n", collate);
+ return 7;
+ }
+ if(gen_weights(collate)) {
+ if(chdir("..")) {
+ fprintf(stderr, "Hmm, can`t change to current directory\n");
+ return 7;
+ }
+ rmdir(collate);
+ return 1;
+ }
+ return 0;
+}
diff --git a/extra/locale/gen_ctype_from_glibc.c b/extra/locale/gen_ctype_from_glibc.c
index 5cbceb052..0488048cd 100644
--- a/extra/locale/gen_ctype_from_glibc.c
+++ b/extra/locale/gen_ctype_from_glibc.c
@@ -19,8 +19,9 @@
#include <string.h>
#include <getopt.h>
#include <unistd.h>
+#include <errno.h>
-#include "../../misc/ctype/ctype.h"
+#include "../../libc/misc/locale/_locale.h"
#define DEFAULT_LOCALE_DIR "/usr/share/locale/"
@@ -229,7 +230,7 @@ Defaults:\n\
printf("setlocale(LC_CTYPE, %s) returned %s\n", ln, t);
if(t==0)
continue;
- if(mkdir(ln, 0755)) {
+ if(mkdir(ln, 0755)!=0 && errno!=EEXIST) {
fprintf(stderr, "Can`t create directory `%s'\n", ln);
continue;
}
diff --git a/libc/misc/ctype/ctype.c b/libc/misc/ctype/ctype.c
index 8d6a1dba7..18ffed4a5 100644
--- a/libc/misc/ctype/ctype.c
+++ b/libc/misc/ctype/ctype.c
@@ -157,7 +157,7 @@ toupper( int c )
#else /* __UCLIBC_HAS_LOCALE__ */
#include <limits.h>
-#include "./ctype.h"
+#include "../locale/_locale.h"
#define _UC_ISCTYPE(c, type) \
((c != -1) && ((_uc_ctype_b[(int)((unsigned char)c)] & type) != 0))
diff --git a/libc/misc/ctype/ctype.h b/libc/misc/locale/_locale.h
index f9a34cb18..139a862f9 100644
--- a/libc/misc/ctype/ctype.h
+++ b/libc/misc/locale/_locale.h
@@ -19,3 +19,4 @@ enum
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];
+}
diff --git a/libc/string/Makefile b/libc/string/Makefile
index e2d04c3f9..48d3a3426 100644
--- a/libc/string/Makefile
+++ b/libc/string/Makefile
@@ -28,6 +28,10 @@ MOBJ=strlen.o strcat.o strcpy.o strchr.o strcmp.o strncat.o strncpy.o \
strncmp.o strrchr.o strdup.o memcpy.o memccpy.o memset.o \
memmove.o memcmp.o memchr.o ffs.o strnlen.o strxfrm.o
+ifeq ($(HAS_LOCALE),true)
+ MOBJ += strcoll.o
+endif
+
MSRC1=strsignal.c
MOBJ1=strsignal.o psignal.o
diff --git a/libc/string/string.c b/libc/string/string.c
index b3070907c..0e2df303b 100644
--- a/libc/string/string.c
+++ b/libc/string/string.c
@@ -76,7 +76,38 @@ int strcmp(const char *s1, const char *s2)
return c1 - c2;
}
+#ifndef __UCLIBC_HAS_LOCALE__
__asm__(".weak strcoll; strcoll = strcmp");
+#endif /* __UCLIBC_HAS_LOCALE__ */
+#endif
+
+/***** Function strcoll (locale only, as non-locale is alias of strcmp *****/
+
+#ifdef L_strcoll
+#ifdef __UCLIBC_HAS_LOCALE__
+
+#include "../misc/locale/_locale.h"
+
+const unsigned char *_uc_collate_b; /* NULL for no collate, strcoll->strcmp */
+
+int strcoll(const char *s1, const char *s2)
+{
+ unsigned char c1, c2;
+
+ while(1) {
+ c1 = (unsigned char) *s1;
+ c2 = (unsigned char) *s2;
+ if(_uc_collate_b) { /* setuped non-C locale? */
+ c1 = _uc_collate_b[c1];
+ c2 = _uc_collate_b[c2];
+ }
+ if (*s1 == '\0' || c1 != c2)
+ return c1 - c2;
+ s1++;
+ s2++;
+ }
+}
+#endif /* __UCLIBC_HAS_LOCALE__ */
#endif
/********************** Function strncat ************************************/