Browse Source

Add locale-enabled strcoll function from vodz, plus supporting tool.

Manuel Novoa III 24 years ago
parent
commit
d85536af73

+ 6 - 4
extra/locale/README

@@ -1,6 +1,8 @@
 
 
-The program gen_ctype_from_glibc.c will generate data files which can be
+The programs gen_ctype_from_glibc.c and gen_collate_from_glibc.c
-used by uClibc ctype functions to support locales.  From the comments:
+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
  * 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
  * ./LOCALE/LC_CTYPE files for system with uclibc
  *
  *
  * Written by Vladimir Oleynik <vodz@usa.net> 2001
  * 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:
 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_ctype_from_glibc.c -o gen_ctype_from_glibc
+gcc gen_collate_from_glibc.c -o gen_ctype_from_glibc
 
 
 mkdir tmp
 mkdir tmp
 cd tmp
 cd tmp
 ../gen_ctype_from_glibc -d /usr/share/locale -c
 ../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)
 Then just move the directory or directories you need (not the .c files)
 to the uClibc locale file directory you set in Config.
 to the uClibc locale file directory you set in Config.

+ 207 - 0
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;
+}

+ 3 - 2
extra/locale/gen_ctype_from_glibc.c

@@ -19,8 +19,9 @@
 #include <string.h>
 #include <string.h>
 #include <getopt.h>
 #include <getopt.h>
 #include <unistd.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/"
 #define DEFAULT_LOCALE_DIR      "/usr/share/locale/"
@@ -229,7 +230,7 @@ Defaults:\n\
       printf("setlocale(LC_CTYPE, %s) returned %s\n", ln, t);
       printf("setlocale(LC_CTYPE, %s) returned %s\n", ln, t);
       if(t==0)
       if(t==0)
 		continue;
 		continue;
-      if(mkdir(ln, 0755)) {
+      if(mkdir(ln, 0755)!=0 && errno!=EEXIST) {
 		fprintf(stderr, "Can`t create directory `%s'\n", ln);
 		fprintf(stderr, "Can`t create directory `%s'\n", ln);
 		continue;
 		continue;
       }
       }

+ 1 - 1
libc/misc/ctype/ctype.c

@@ -157,7 +157,7 @@ toupper( int c )
 #else   /* __UCLIBC_HAS_LOCALE__ */
 #else   /* __UCLIBC_HAS_LOCALE__ */
 
 
 #include <limits.h>
 #include <limits.h>
-#include "./ctype.h"
+#include "../locale/_locale.h"
 
 
 #define _UC_ISCTYPE(c, type) \
 #define _UC_ISCTYPE(c, type) \
 ((c != -1) && ((_uc_ctype_b[(int)((unsigned char)c)] & type) != 0))
 ((c != -1) && ((_uc_ctype_b[(int)((unsigned char)c)] & type) != 0))

+ 1 - 0
libc/misc/ctype/ctype.h → libc/misc/locale/_locale.h

@@ -19,3 +19,4 @@ enum
   ISxdigit = ISbit (7),        /* 128 Hexnumeric.        */
   ISxdigit = ISbit (7),        /* 128 Hexnumeric.        */
 };
 };
 
 
+extern const unsigned char *_uc_collate_b;

+ 246 - 75
libc/misc/locale/locale.c

@@ -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"
+#include "_locale.h"
-
-#undef TEST_LOCALE
 
 
+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] = {
+static struct SAV_LOADED_LOCALE sll_C_LC_MONETARY = {
-	{ C_LOCALE_NAME, _uc_ctype_b_C, 0 }
+  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)
+static struct SAV_LOADED_LOCALE sll_C_LC_TIME = {
-{
+  LC_TIME,     C_LOCALE_NAME, 0, &sll_C_LC_COLLATE
-	_uc_ctype_b     = s_locale->buf;
+};
-	_uc_ctype_trans = s_locale->buf+LOCALE_BUF_SIZE/2;
-	old_locale      = s_locale;
-	return            s_locale->locale;
-}
 
 
-/* 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;
+#endif
-	char   full_path[PATH_MAX];
+	const char *name = *plocale;
-	char * buf = 0;
+
-	int    l;
+	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)
+	if (strcmp (name, C_LOCALE_NAME) == 0 ||
-		return NULL;
+				strcmp (name, POSIX_LOCALE_NAME) == 0 ||
+		/* TODO! */     (c!=LC_CTYPE && c!=LC_COLLATE))
+	    name = C_LOCALE_NAME;
 
 
-	if(locale==0)
+	*plocale = name;
-		return set_new_locale(old_locale);
 
 
-	if(strcmp(locale, "POSIX")==0)
+#ifdef __UCLIBC_HAS_LOCALE__
-		return set_new_locale(sav_loaded_locale);
+	for(cur = sll; cur; cur = cur->next)
-	 else if(*locale == '\0') {
+		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)
+#ifdef __UCLIBC_HAS_LOCALE__
-		if(strcmp(cur->locale, locale)==0)
+static char *load_locale(int category, const char *locale)
-			return set_new_locale(cur);
+{
+	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)+strlen(LC_strs[category]))>=PATH_MAX)
-	if((l+sizeof(PATH_LOCALE)+sizeof(LC_CTYPE_STR))>=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" */
+	if(buf==0) {    /* broken locale file, set to "C" */
-		return set_new_locale(sav_loaded_locale);
+		return C_LOCALE_NAME;
+	}
 
 
-	cur->next = 0;
+	cur->next     = 0;
-	strcpy(buf+LOCALE_BUF_SIZE, locale);
+	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 */
+	return cur->locale;
-	cur->buf    = buf;
+}
-	cur->locale = buf+LOCALE_BUF_SIZE;
+
+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. */
+	char * tl;
-	if (locale) {
+#ifdef __UCLIBC_HAS_LOCALE__
-		if (*locale == 'C') {
+	int    i;
-			++locale;
+#endif
-		}
-		if (*locale) {				/* locale wasn't "C" or ""!! */
-			return NULL;
-		}
-	}
 
 
-	/* Allow any legal category for "C" or "" (native) locale. */
+	if (category < 0 || category > LC_ALL) {
-	if((category < LC_CTYPE) || (category > LC_ALL)) { /* Illegal category! */
+#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];
+}

+ 4 - 0
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 \
 	strncmp.o strrchr.o strdup.o memcpy.o memccpy.o memset.o \
 	memmove.o memcmp.o memchr.o ffs.o strnlen.o strxfrm.o
 	memmove.o memcmp.o memchr.o ffs.o strnlen.o strxfrm.o
 
 
+ifeq ($(HAS_LOCALE),true)
+	MOBJ += strcoll.o
+endif
+
 MSRC1=strsignal.c
 MSRC1=strsignal.c
 MOBJ1=strsignal.o psignal.o
 MOBJ1=strsignal.o psignal.o
 
 

+ 31 - 0
libc/string/string.c

@@ -76,7 +76,38 @@ int strcmp(const char *s1, const char *s2)
 	return c1 - c2;
 	return c1 - c2;
 }
 }
 
 
+#ifndef __UCLIBC_HAS_LOCALE__
 __asm__(".weak strcoll; strcoll = strcmp");
 __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
 #endif
 
 
 /********************** Function strncat ************************************/
 /********************** Function strncat ************************************/