diff options
author | Manuel Novoa III <mjn3@codepoet.org> | 2002-05-06 07:37:32 +0000 |
---|---|---|
committer | Manuel Novoa III <mjn3@codepoet.org> | 2002-05-06 07:37:32 +0000 |
commit | d07fdf8b9ece2c4339b325921add50792077bf97 (patch) | |
tree | b0886656bdd854728f2d1c05597368c4739ecc1b /libc/misc/locale/locale.c | |
parent | 7f09a14cabbec158d683542e53f53ccfe75031fa (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.c | 719 |
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]; -} +/**********************************************************************/ |