summaryrefslogtreecommitdiff
path: root/libc/misc/locale/locale.c
diff options
context:
space:
mode:
authorManuel Novoa III <mjn3@codepoet.org>2002-05-06 07:37:32 +0000
committerManuel Novoa III <mjn3@codepoet.org>2002-05-06 07:37:32 +0000
commitd07fdf8b9ece2c4339b325921add50792077bf97 (patch)
treeb0886656bdd854728f2d1c05597368c4739ecc1b /libc/misc/locale/locale.c
parent7f09a14cabbec158d683542e53f53ccfe75031fa (diff)
New locale support (in development). Supports LC_CTYPE, LC_NUMERIC,
LC_TIME, LC_MONETARY, and LC_MESSAGES for the SUSv3 items. Also, nl_langinfo() when real locale support is enabled. New implementation of ctype.h. New implementation of wctype.h. New implementation of most of the string functions (smaller). New implementation of the wcs/wmem functions. These are untested, but they're also just preprocessor-modified versions ot the corresponding str/mem functions. Tweaked qsort and new bsearch. Stuff still pending: stdlib.h and wchar.h mb<->wc functions. I actually have working versions of the stdlib ones, but the reentrant versions from wchar.h require some reworking. Basic replacement and translit support for wc->mb conversions. (groundwork laid). Simple-minded collate support such as was provided by the previous locale implementation. (mostly done -- 8-bit codesets only) Shared mmaping of the locale data and strerror message text.
Diffstat (limited to 'libc/misc/locale/locale.c')
-rw-r--r--libc/misc/locale/locale.c719
1 files changed, 447 insertions, 272 deletions
diff --git a/libc/misc/locale/locale.c b/libc/misc/locale/locale.c
index d978ae37c..fd587429b 100644
--- a/libc/misc/locale/locale.c
+++ b/libc/misc/locale/locale.c
@@ -1,332 +1,507 @@
-/* setlocale.c
- * Load LC_CTYPE and LC_COLLATE locale only special for uclibc
+/* Copyright (C) 2002 Manuel Novoa III
*
- * Written by Vladimir Oleynik (c) vodz@usa.net
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
*
- * This file is part of the uClibc C library and is distributed
- * under the GNU Library General Public License.
- * used ideas is part of the GNU C Library.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* TODO:
+ * Implement the shared mmap code so non-mmu platforms can use this.
+ * Implement nl_langinfo() for the stub locale support.
+ * Add some basic collate functionality similar to what the previous
+ * locale support had (8-bit codesets only).
*/
+#define _GNU_SOURCE
#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 <stdlib.h>
+#include <stddef.h>
+#include <limits.h>
+#include <stdint.h>
+#include <assert.h>
-#include "_locale.h"
+#ifdef __LOCALE_C_ONLY
-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 __WCHAR_ENABLED
+#error wide char support requires full locale support
+#endif
-#ifdef __UCLIBC_HAS_LOCALE__
+#else /* __LOCALE_C_ONLY */
-#ifdef TEST_LOCALE
-static const char PATH_LOCALE[]="./";
-#else
-static const char PATH_LOCALE[]=__UCLIBC_LOCALE_DIR;
-#endif
+#define CUR_LOCALE_SPEC (__global_locale.cur_locale)
+#undef CODESET_LIST
+#define CODESET_LIST (__locale_mmap->codeset_list)
-struct SAV_LOADED_LOCALE {
- int category;
- char *locale;
- const unsigned char *buf;
- struct SAV_LOADED_LOCALE *next;
-};
+/* TODO: Optional... See below. */
+#define __LOCALE_STRICTER_SETLOCALE
-static struct SAV_LOADED_LOCALE sll_C_LC_MESSAGES = {
- LC_MESSAGES, C_LOCALE_NAME, 0, 0
-};
+#endif /* __LOCALE_C_ONLY */
-static struct SAV_LOADED_LOCALE sll_C_LC_MONETARY = {
- LC_MONETARY, C_LOCALE_NAME, 0, &sll_C_LC_MESSAGES
-};
+/**********************************************************************/
+#ifdef L_setlocale
-static struct SAV_LOADED_LOCALE sll_C_LC_COLLATE = {
- LC_COLLATE, C_LOCALE_NAME, 0, &sll_C_LC_MONETARY
-};
+#ifdef __LOCALE_C_ONLY
-static struct SAV_LOADED_LOCALE sll_C_LC_TIME = {
- LC_TIME, C_LOCALE_NAME, 0, &sll_C_LC_COLLATE
-};
+link_warning(setlocale,"the 'setlocale' function supports only C|POSIX locales");
-static struct SAV_LOADED_LOCALE sll_C_LC_NUMERIC = {
- LC_NUMERIC, C_LOCALE_NAME, 0, &sll_C_LC_TIME
-};
+static const char C_string[] = "C";
-static struct SAV_LOADED_LOCALE sll_C_LC_CTYPE = {
- LC_CTYPE, C_LOCALE_NAME, _uc_ctype_b_C, &sll_C_LC_NUMERIC
-};
+char *setlocale(int category, register const char *locale)
+{
+ return ( (((unsigned int)(category)) <= LC_ALL)
+ && ( (!locale) /* Request for locale category string. */
+ || (!*locale) /* Implementation-defined default is C. */
+ || ((*locale == 'C') && !locale[1])
+ || (!strcmp(locale, "POSIX"))) )
+ ? (char *) C_string /* Always in C/POSIX locale. */
+ : NULL;
+}
-static struct SAV_LOADED_LOCALE *sll = &sll_C_LC_CTYPE;
+#else /* ---------------------------------------------- __LOCALE_C_ONLY */
+#if !defined(NUM_LOCALES) || (NUM_LOCALES <= 1)
+#error locales enabled, but not data other than for C locale!
+#endif
-#endif /* __UCLIBC_HAS_LOCALE__ */
+static unsigned char setlocale_buf[LOCALE_STRING_SIZE];
-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
-};
+#define LOCALE_NAMES (__locale_mmap->locale_names5)
+#define LOCALES (__locale_mmap->locales)
+#define LOCALE_AT_MODIFIERS (__locale_mmap->locale_at_modifiers)
+#define CATEGORY_NAMES (__locale_mmap->lc_names)
-static const char * const LC_strs[LC_ALL+1] = {
- "/LC_CTYPE",
- "/LC_NUMERIC",
- "/LC_TIME",
- "/LC_COLLATE",
- "/LC_MONETARY",
- "/LC_MESSAGES",
- "/LC_ALL"
-};
+static const char posix[] = "POSIX";
-static char *find_locale(int c, const char **plocale)
+static int find_locale(int category, const char *p, unsigned char *new_locale)
{
-#ifdef __UCLIBC_HAS_LOCALE__
- struct SAV_LOADED_LOCALE *cur;
+ int i;
+ const unsigned char *s;
+ uint16_t n;
+ unsigned char lang_cult, codeset;
+
+#if defined(LOCALE_AT_MODIFIERS_LENGTH) && 1
+ /* Support standard locale handling for @-modifiers. */
+ char buf[18]; /* TODO: 7+{max codeset name length} */
+ const char *q;
+
+ if ((q = strchr(p,'@')) != NULL) {
+ if ((((size_t)((q-p)-5)) > (sizeof(buf) - 5)) || (p[2] != '_')) {
+ return 0;
+ }
+ /* locale name at least 5 chars long and 3rd char is '_' */
+ s = LOCALE_AT_MODIFIERS;
+ do {
+ if (!strcmp(s+2, q+1)) {
+ break;
+ }
+ s += 2 + *s; /* TODO - fix this throughout */
+ } while (*s);
+ if (!*s) {
+ return 0;
+ }
+ memcpy(buf, p, q-p);
+ buf[q-p] = 0;
+ buf[2] = s[1];
+ p = buf;
+ }
#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;
+
+ lang_cult = codeset = 0; /* Assume C and default codeset. */
+ if (((*p == 'C') && !p[1]) || !strcmp(p, posix)) {
+ goto FIND_LOCALE;
}
- 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 (p[5] == '.') { /* Codeset specified in locale name? */
+ /* TODO: maybe CODESET_LIST + *s ??? */
+ /* 7bit is 1, UTF-8 is 2, 8-bit is >= 3 */
+ codeset = 2;
+ if (strcmp("UTF-8",p+6) != 0) {/* TODO - fix! */
+ s = CODESET_LIST;
+ do {
+ ++codeset; /* Increment codeset first. */
+ if (!strcmp(CODESET_LIST+*s, p+6)) {
+ goto FIND_LANG_CULT;
+ }
+ } while (*++s);
+ return 0; /* No matching codeset! */
+ }
+ }
- *plocale = name;
+ FIND_LANG_CULT: /* Find language_culture number. */
+ s = LOCALE_NAMES;
+ do { /* TODO -- do a binary search? */
+ /* TODO -- fix gen_mmap!*/
+ ++lang_cult; /* Increment first since C/POSIX is 0. */
+ if (!strncmp(s,p,5)) { /* Found a matching locale name; */
+ goto FIND_LOCALE;
+ }
+ s += 5;
+ } while (lang_cult < NUM_LOCALE_NAMES);
+ return 0; /* No matching language_culture! */
+
+ FIND_LOCALE: /* Find locale row matching name and codeset */
+ s = LOCALES;
+ n = 1;
+ do { /* TODO -- do a binary search? */
+ if ((lang_cult == *s) && ((codeset == s[1]) || (codeset == s[2]))) {
+ i = ((category == LC_ALL) ? 0 : category);
+ s = new_locale + 2*i;
+ do {
+ /* Encode current locale row number. */
+ *((unsigned char *) ++s) = (n >> 8) | 0x80;
+ *((unsigned char *) ++s) = n & 0xff;
+ } while (++i < category);
+
+ return i; /* Return non-zero */
+ }
+ s += WIDTH_LOCALES;
+ ++n;
+ } while (n <= NUM_LOCALES); /* We started at 1!!! */
-#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;
+ return 0; /* Unsupported locale. */
}
-
-#ifdef __UCLIBC_HAS_LOCALE__
-static char *load_locale(int category, const char *locale)
+char *setlocale(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);
-
- 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_strs[category]);
- fl = fopen(full_path, "r");
- if(fl==0)
- return NULL;
+ const unsigned char *p;
+ unsigned char *s;
+ int i;
+ unsigned lc_mask;
+ unsigned char new_locale[LOCALE_STRING_SIZE];
+
+ if (((unsigned int)(category)) > LC_ALL) {
+ /* TODO - set errno? SUSv3 doesn't say too. */
+ return NULL; /* Illegal/unsupported category. */
+ }
- switch(category) {
- case LC_CTYPE:
- bufsize = LOCALE_BUF_SIZE;
- break;
- case LC_COLLATE:
- bufsize = 256;
- break;
- default: /* TODO */
- bufsize = 0;
- break;
+ lc_mask = 1 << category;
+ if (category == LC_ALL) {
+ --lc_mask;
}
- cur = malloc(sizeof(struct SAV_LOADED_LOCALE)+bufsize+l+2);
- if(cur) {
- buf = (char *)(cur+1);
- if(bufsize!=0 && fread(buf, 1, bufsize+1, fl)!=(bufsize)) {
- /* broken locale file */
- free(cur);
- buf = 0;
-#ifdef TEST_LOCALE
- fprintf(stderr, "\nbroken locale file\n");
-#endif
- }
+ if (!locale) { /* Request for locale category string... */
+ DONE:
+ strcpy(setlocale_buf, CUR_LOCALE_SPEC);
+#ifdef __LOCALE_STRICTER_SETLOCALE
+ /* The standard says you can only use the string returned to restore
+ * the category (categories) requested. This could be optional.
+ * See below as well. */
+ s = setlocale_buf + 1;
+ lc_mask |= (1 << LC_ALL);
+ do {
+ if (!(lc_mask & 1)) {
+ /* Encode non-selected locale flag. */
+ s[1] = *s = 0xff;
+ }
+ s += 2;
+ } while ((lc_mask >>= 1) > 1);
+#endif /* __LOCALE_STRICTER_SETLOCALE */
+ return (char *) setlocale_buf;
}
- fclose(fl);
- if(cur==0) /* not enough memory */
+ strcpy(new_locale, CUR_LOCALE_SPEC); /* Start with current. */
+
+ if (!*locale) { /* locale == "", so check environment. */
+ i = ((category == LC_ALL) ? 0 : category);
+ do {
+ /* Note: SUSv3 doesn't define a fallback mechanism here. So,
+ * if LC_ALL is invalid, we do _not_ continue trying the other
+ * environment vars. */
+ if (!(p = getenv("LC_ALL"))) {
+ if (!(p = getenv(CATEGORY_NAMES + CATEGORY_NAMES[i]))) {
+ if (!(p = getenv("LANG"))) {
+ p = posix;
+ }
+ }
+ }
+
+ /* The user set something... is it valid? */
+ /* Note: Since we don't support user-supplied locales and
+ * alternate paths, we don't need to worry about special
+ * handling for suid/sgid apps. */
+ if (!find_locale(i, p, new_locale)) {
+ return NULL;
+ }
+ } while (++i < category);
+ } else if (*locale == '#') { /* Previsouly returned value. */
+ assert(strlen(locale) == LOCALE_STRING_SIZE - 1);
+
+ i = ((category == LC_ALL) ? 0 : category);
+ p = locale + 2*i;
+ s = new_locale + 2*i;
+ do {
+#ifdef __LOCALE_STRICTER_SETLOCALE
+ /* Only set categories that were selected in the previous
+ * return value. Could be optional. See above as well.
+ * NOTE: This still isn't quite right for non-LC_ALL
+ * as it only checks the category selected to set. */
+ if ((*p == 0xff) && (p[1] == 0xff)) {
+ return NULL;
+ }
+#endif /* __LOCALE_STRICTER_SETLOCALE */
+ /* Note: Validate settings below. */
+ *++s = *++p;
+ *++s = *++p;
+ } while (++i < category);
+ } else if (!find_locale(category, locale, new_locale)) {
return NULL;
- if(buf==0) { /* broken locale file, set to "C" */
- return C_LOCALE_NAME;
}
- cur->next = 0;
- cur->buf = buf;
- cur->category = category;
- cur->locale = buf+bufsize;
- strcpy(cur->locale, locale);
- bottom = sll;
- while(bottom->next!=0)
- bottom = bottom->next;
- bottom->next = cur;
+ /* TODO: Ok, everything checks out, so install the new locale. */
+ _locale_set(new_locale);
- return cur->locale;
+ /* Everything ok, so make a copy in setlocale_buf and return. */
+ goto DONE;
}
-static char *set_composite(int category, char *locale)
+#endif /* __LOCALE_C_ONLY */
+
+#endif
+/**********************************************************************/
+#ifdef L_localeconv
+
+/* Note: We assume here that the compiler does the sane thing regarding
+ * placement of the fields in the struct. If necessary, we could ensure
+ * this usings an array of offsets but at some size cost. */
+
+#ifdef __LOCALE_C_ONLY
+
+#warning localeconv is hardwired for C/POSIX locale only
+link_warning(localeconv,"the 'localeconv' function is hardwired for C/POSIX locale only");
+
+static struct lconv the_lconv;
+
+static const char decpt[] = ".";
+
+struct lconv *localeconv(void)
{
- int i, l;
- char *old_composite_name = nl_current[LC_ALL];
- char *new_composite_name;
- struct SAV_LOADED_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;
- }
+ register char *p = (char *)(&the_lconv);
- 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;
+ *((char **)p) = (char *) decpt;
+ do {
+ p += sizeof(char **);
+ *((char **)p) = (char *) (decpt+1);
+ } while (p < (char *) &the_lconv.negative_sign);
+
+ p = (&the_lconv.int_frac_digits);
+ do {
+ *p = CHAR_MAX;
+ ++p;
+ } while (p <= &the_lconv.int_n_sign_posn);
+
+ return &the_lconv;
}
-#endif /* __UCLIBC_HAS_LOCALE__ */
+#else /* __LOCALE_C_ONLY */
-char *setlocale(int category, const char *locale)
+static struct lconv the_lconv;
+
+struct lconv *localeconv(void)
{
- char * tl;
-#ifdef __UCLIBC_HAS_LOCALE__
- int i;
+ register char *p = (char *) &the_lconv;
+ register char **q = (char **) &__global_locale.decimal_point;
+
+ do {
+ *((char **)p) = *q;
+ p += sizeof(char **);
+ ++q;
+ } while (p < &the_lconv.int_frac_digits);
+
+ do {
+ *p = **q;
+ ++p;
+ ++q;
+ } while (p <= &the_lconv.int_n_sign_posn);
+
+ return &the_lconv;
+}
+
+#endif /* __LOCALE_C_ONLY */
+
#endif
+/**********************************************************************/
+#ifdef L__locale_init
- if (category < 0 || category > LC_ALL) {
-#ifdef __UCLIBC_HAS_LOCALE__
-einval:
+#ifndef __LOCALE_C_ONLY
+
+#define C_LOCALE_SELECTOR "\x23\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01"
+#define LOCALE_INIT_FAILED "locale init failed!\n"
+
+#define CUR_LOCALE_SPEC (__global_locale.cur_locale)
+
+__locale_t __global_locale;
+
+void _locale_init(void)
+{
+ /* TODO: mmap the locale file */
+
+ /* TODO - ??? */
+ memset(CUR_LOCALE_SPEC, 0, LOCALE_STRING_SIZE);
+ CUR_LOCALE_SPEC[0] = '#';
+
+ memcpy(__global_locale.category_item_count,
+ __locale_mmap->lc_common_item_offsets_LEN,
+ LC_ALL);
+
+ __global_locale.category_offsets[0] = offsetof(__locale_t, codeset);
+ __global_locale.category_offsets[1] = offsetof(__locale_t, decimal_point);
+ __global_locale.category_offsets[2] = offsetof(__locale_t, int_curr_symbol);
+ __global_locale.category_offsets[3] = offsetof(__locale_t, abday_1);
+/* __global_locale.category_offsets[4] = offsetof(__locale_t, collate???); */
+ __global_locale.category_offsets[5] = offsetof(__locale_t, yesexpr);
+
+#ifdef __CTYPE_HAS_8_BIT_LOCALES
+ __global_locale.tbl8ctype
+ = (const unsigned char *) &__locale_mmap->tbl8ctype;
+ __global_locale.tbl8uplow
+ = (const unsigned char *) &__locale_mmap->tbl8uplow;
+#ifdef __WCHAR_ENABLED
+ __global_locale.tbl8c2wc
+ = (const uint16_t *) &__locale_mmap->tbl8c2wc;
+ __global_locale.tbl8wc2c
+ = (const unsigned char *) &__locale_mmap->tbl8wc2c;
+ /* translit */
+#endif /* __WCHAR_ENABLED */
+#endif /* __CTYPE_HAS_8_BIT_LOCALES */
+#ifdef __WCHAR_ENABLED
+ __global_locale.tblwctype
+ = (const unsigned char *) &__locale_mmap->tblwctype;
+ __global_locale.tblwuplow
+ = (const unsigned char *) &__locale_mmap->tblwuplow;
+ __global_locale.tblwuplow_diff
+ = (const uint16_t *) &__locale_mmap->tblwuplow_diff;
+ __global_locale.tblwcomb
+ = (const unsigned char *) &__locale_mmap->tblwcomb;
+ /* width?? */
+#endif /* __WCHAR_ENABLED */
+
+ _locale_set(C_LOCALE_SELECTOR);
+}
+
+static const char ascii[] = "ASCII";
+static const char utf8[] = "UTF-8";
+
+void _locale_set(const unsigned char *p)
+{
+ const char **x;
+ unsigned char *s = CUR_LOCALE_SPEC + 1;
+ const size_t *stp;
+ const unsigned char *r;
+ const uint16_t *io;
+ const uint16_t *ii;
+ const unsigned char *d;
+ int row; /* locale row */
+ int crow; /* category row */
+ int len;
+ int c;
+ int i = 0;
+
+ ++p;
+ do {
+ if ((*p != *s) || (p[1] != s[1])) {
+ row = (((int)(*p & 0x7f)) << 8) + p[1] - 1;
+#ifndef NDEBUG
+ assert(row < NUM_LOCALES);
#endif
- errno = EINVAL;
- return NULL;
- }
+ *s = *p;
+ s[1] = p[1];
+
+ if (i == LC_CTYPE) {
+ c = __locale_mmap->locales[ WIDTH_LOCALES * row + 2 ]; /* codeset */
+ if (c <= 2) {
+ if (c == 2) {
+ __global_locale.codeset = utf8;
+ __global_locale.encoding = __ctype_encoding_utf8;
+ /* TODO - fix for bcc */
+ __global_locale.mb_cur_max = 6;
+ } else {
+ assert(c==1);
+ __global_locale.codeset = ascii;
+ __global_locale.encoding = __ctype_encoding_7_bit;
+ __global_locale.mb_cur_max = 1;
+ }
+ } else {
+ const codeset_8_bit_t *c8b;
+ r = CODESET_LIST;
+ __global_locale.codeset = r + r[c -= 3];
+ __global_locale.encoding = __ctype_encoding_8_bit;
+#warning REMINDER: update 8 bit mb_cur_max when trasnlit implemented!
+ /* TODO - update when translit implemented! */
+ __global_locale.mb_cur_max = 1;
+ c8b = __locale_mmap->codeset_8_bit + c;
+#ifdef __CTYPE_HAS_8_BIT_LOCALES
+ __global_locale.idx8ctype = c8b->idx8ctype;
+ __global_locale.idx8uplow = c8b->idx8uplow;
+#ifdef __WCHAR_ENABLED
+ __global_locale.idx8c2wc = c8b->idx8c2wc;
+ __global_locale.idx8wc2c = c8b->idx8wc2c;
+ /* translit */
+#endif /* __WCHAR_ENABLED */
+#endif /* __CTYPE_HAS_8_BIT_LOCALES */
+ }
+
+ } else if ((len = __locale_mmap->lc_common_item_offsets_LEN[i]) != 0) {
+ crow = __locale_mmap->locales[ WIDTH_LOCALES * row + 3 + i ]
+ * len;
+ x = (const char **)(((char *) &__global_locale)
+ + __global_locale.category_offsets[i]);
+ stp = __locale_mmap->lc_common_tbl_offsets + 4*i;
+ r = (const unsigned char *)( ((char *)__locale_mmap) + *stp );
+ io = (const uint16_t *)( ((char *)__locale_mmap) + *++stp );
+ ii = (const uint16_t *)( ((char *)__locale_mmap) + *++stp );
+ d = (const unsigned char *)( ((char *)__locale_mmap) + *++stp );
+ for (c=0 ; c < len ; c++) {
+ *(x + c) = d + ii[ r[crow + c] + io[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);
}
+ ++i;
+ p += 2;
+ s += 2;
+ } while (i < LC_ALL);
+}
+
+#endif /* __LOCALE_C_ONLY */
+
#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 */
+/**********************************************************************/
+#ifdef L_nl_langinfo
+
+#ifndef __LOCALE_C_ONLY
+
+#include <langinfo.h>
+#include <nl_types.h>
+
+static const char empty[] = "";
+
+char *nl_langinfo(nl_item item)
+{
+ unsigned int c = _NL_ITEM_CATEGORY(item);
+ unsigned int i = _NL_ITEM_INDEX(item);
+
+ if ((c < LC_ALL) && (i < __global_locale.category_item_count[c])) {
+ return ((char **)(((char *) &__global_locale)
+ + __global_locale.category_offsets[c]))[i];
+
}
+ return (char *) empty;
+}
+
+#endif /* __LOCALE_C_ONLY */
#endif
- return nl_current[LC_ALL];
-}
+/**********************************************************************/