| /* |
| * Copyright (C) 2002 Manuel Novoa III |
| * Copyright (C) 2000-2005 Erik Andersen <andersen@uclibc.org> |
| * |
| * Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball. |
| */ |
| |
| /* Dec 20, 2002 |
| * Initial test implementation of strcoll, strxfrm, wcscoll, and wcsxfrm. |
| * The code needs to be cleaned up a good bit, but I'd like to see people |
| * test it out. |
| * |
| */ |
| |
| #include "_string.h" |
| #include <ctype.h> |
| #include <locale.h> |
| #include <stdlib.h> |
| #include <errno.h> |
| #include <assert.h> |
| |
| #ifdef __UCLIBC_HAS_LOCALE__ |
| #if defined(L_strxfrm) || defined(L_strxfrm_l) || defined(L_wcsxfrm) || defined(L_wcsxfrm_l) |
| |
| #ifdef L_strxfrm |
| #ifndef WANT_WIDE |
| #error WANT_WIDE should be defined for L_strxfrm |
| #endif |
| #ifdef L_wcsxfrm |
| #error L_wcsxfrm already defined for L_strxfrm |
| #endif |
| #endif /* L_strxfrm */ |
| |
| #if defined(L_strxfrm) || defined(L_strxfrm_l) |
| |
| #define wcscoll strcoll |
| #define wcscoll_l strcoll_l |
| #define wcsxfrm strxfrm |
| #define wcsxfrm_l strxfrm_l |
| |
| #undef WANT_WIDE |
| #undef Wvoid |
| #undef Wchar |
| #undef Wuchar |
| #undef Wint |
| |
| #define Wchar char |
| |
| #endif /* defined(L_strxfrm) || defined(L_strxfrm_l) */ |
| |
| #if defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) |
| |
| |
| int wcscoll (const Wchar *s0, const Wchar *s1) |
| { |
| return wcscoll_l(s0, s1, __UCLIBC_CURLOCALE ); |
| } |
| libc_hidden_def(wcscoll) |
| |
| |
| size_t wcsxfrm(Wchar *__restrict ws1, const Wchar *__restrict ws2, size_t n) |
| { |
| return wcsxfrm_l(ws1, ws2, n, __UCLIBC_CURLOCALE ); |
| } |
| |
| #else /* defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) */ |
| |
| |
| #if 0 |
| #define CUR_COLLATE (&__UCLIBC_CURLOCALE->collate) |
| #else |
| #define CUR_COLLATE (& __LOCALE_PTR->collate) |
| #endif |
| |
| #define MAX_PENDING 8 |
| |
| typedef struct { |
| const Wchar *s; |
| const Wchar *eob; /* end of backward */ |
| |
| __uwchar_t weight; |
| __uwchar_t ui_weight; /* undefined or invalid */ |
| int colitem; |
| int weightidx; |
| int rule; |
| size_t position; |
| /* should be wchar_t. if wchar < 0 do EILSEQ? */ |
| __uwchar_t *cip; |
| __uwchar_t ci_pending[MAX_PENDING]; /* nul-terminated */ |
| |
| char *back_buf; |
| char *bbe; /* end of back_buf (actual last... not 1 past end) */ |
| char *bp; /* ptr into backbuf, NULL if not in backward mode */ |
| char ibb[128]; |
| size_t bb_size; |
| |
| int ru_pushed; |
| } col_state_t; |
| |
| |
| #define WEIGHT_MASK 0x3fffU |
| #define RULE_MASK 0xc000U |
| |
| #define RULE_FORWARD (1 << 14) |
| #define RULE_POSITION (1 << 15) |
| |
| #define UI_IDX (WEIGHT_MASK-6) |
| #define POSIT_IDX (WEIGHT_MASK-5) |
| #define RANGE_IDX (WEIGHT_MASK-4) |
| #define UNDEF_IDX (WEIGHT_MASK-3) |
| #define INVAL_IDX (WEIGHT_MASK-2) |
| #define DITTO_IDX (WEIGHT_MASK-1) |
| |
| |
| #undef TRACE |
| #if 0 |
| #define TRACE(X) printf X |
| #else |
| #define TRACE(X) ((void)0) |
| #endif |
| |
| static int lookup(wchar_t wc __LOCALE_PARAM ) |
| { |
| unsigned int sc, n, i0, i1; |
| |
| if (((__uwchar_t) wc) > 0xffffU) { |
| return 0; |
| } |
| |
| sc = wc & CUR_COLLATE->ti_mask; |
| wc >>= CUR_COLLATE->ti_shift; |
| n = wc & CUR_COLLATE->ii_mask; |
| wc >>= CUR_COLLATE->ii_shift; |
| |
| i0 = CUR_COLLATE->wcs2colidt_tbl[wc]; |
| i0 <<= CUR_COLLATE->ii_shift; |
| i1 = CUR_COLLATE->wcs2colidt_tbl[CUR_COLLATE->ii_len + i0 + n]; |
| i1 <<= CUR_COLLATE->ti_shift; |
| return CUR_COLLATE->wcs2colidt_tbl[CUR_COLLATE->ii_len + CUR_COLLATE->ti_len + i1 + sc]; |
| |
| } |
| |
| static void init_col_state(col_state_t *cs, const Wchar *wcs) |
| { |
| memset(cs, 0, sizeof(col_state_t)); |
| cs->s = wcs; |
| cs->bp = cs->back_buf = cs->ibb; |
| cs->bb_size = 128; |
| cs->bbe = cs->back_buf + (cs->bb_size -1); |
| } |
| |
| static void next_weight(col_state_t *cs, int pass __LOCALE_PARAM ) |
| { |
| int r, w, ru, ri, popping_backup_stack; |
| ssize_t n; |
| const uint16_t *p; |
| #ifdef WANT_WIDE |
| #define WC (*cs->s) |
| #define N (1) |
| #else /* WANT_WIDE */ |
| wchar_t WC; |
| size_t n0, nx; |
| #define N n0 |
| |
| #endif /* WANT_WIDE */ |
| |
| do { |
| |
| if (cs->ru_pushed) { |
| ru = cs->ru_pushed; |
| TRACE(("ru_pushed = %d\n", ru)); |
| cs->ru_pushed = 0; |
| goto POSITION_SKIP; |
| } |
| |
| #ifdef __UCLIBC_MJN3_ONLY__ |
| #warning should we walk pendings backwards? |
| #endif |
| if (cs->cip) { /* possible pending weight */ |
| if ((r = *(cs->cip++)) == 0) { |
| cs->cip = NULL; |
| continue; |
| } |
| cs->weightidx = r & WEIGHT_MASK; |
| assert(cs->weightidx); |
| /* assert(cs->weightidx != WEIGHT_MASK); */ |
| } else { /* get the next collation item from the string */ |
| TRACE(("clearing popping flag\n")); |
| popping_backup_stack = 0; |
| |
| IGNORE_LOOP: |
| /* keep first pos as 0 for a sentinal */ |
| if (*cs->bp) { /* pending backward chars */ |
| POP_BACKUP: |
| popping_backup_stack = 1; |
| TRACE(("setting popping flag\n")); |
| n = 0; |
| if (*cs->bp > 0) { /* singles pending */ |
| cs->s -= 1; |
| if ((*cs->bp -= 1) == 0) { |
| cs->bp -= 1; |
| } |
| } else { /* last was a multi */ |
| cs->s += *cs->bp; |
| cs->bp -= 1; |
| } |
| } else if (!*cs->s) { /* not in backward mode and end of string */ |
| cs->weight = 0; |
| return; |
| } else { |
| cs->position += 1; |
| } |
| |
| BACK_LOOP: |
| #ifdef WANT_WIDE |
| n = 1; |
| cs->colitem = r = lookup(*cs->s __LOCALE_ARG ); |
| #else /* WANT_WIDE */ |
| n = n0 = __locale_mbrtowc_l(&WC, cs->s, __LOCALE_PTR); |
| if (n < 0) { |
| __set_errno(EILSEQ); |
| cs->weight = 0; |
| return; |
| } |
| cs->colitem = r = lookup(WC __LOCALE_ARG ); |
| #endif /* WANT_WIDE */ |
| |
| TRACE((" r=%d WC=%#lx\n", r, (unsigned long)(WC))); |
| |
| if (r > CUR_COLLATE->max_col_index) { /* starting char for one or more sequences */ |
| p = CUR_COLLATE->multistart_tbl; |
| p += p[r-CUR_COLLATE->max_col_index -1]; |
| do { |
| n = N; |
| r = *p++; |
| do { |
| if (!*p) { /* found it */ |
| cs->colitem = r; |
| TRACE((" found multi %d\n", n)); |
| goto FOUND; |
| } |
| #ifdef WANT_WIDE |
| /* the lookup check here is safe since we're assured that *p is a valid colidx */ |
| if (!cs->s[n] || (lookup(cs->s[n] __LOCALE_ARG ) != *p)) { |
| do {} while (*p++); |
| break; |
| } |
| ++p; |
| ++n; |
| #else /* WANT_WIDE */ |
| if (cs->s[n]) { |
| nx = __locale_mbrtowc_l(&WC, cs->s + n, __LOCALE_PTR); |
| if (nx < 0) { |
| __set_errno(EILSEQ); |
| cs->weight = 0; |
| return; |
| } |
| } |
| if (!cs->s[n] || (lookup(WC __LOCALE_ARG ) != *p)) { |
| do {} while (*p++); |
| break; |
| } |
| ++p; |
| n += nx; /* Only gets here if cs->s[n] != 0, so nx is set. */ |
| #endif /* WANT_WIDE */ |
| } while (1); |
| } while (1); |
| } else if (r == 0) { /* illegal, undefined, or part of a range */ |
| if ((CUR_COLLATE->range_count) |
| #ifdef __UCLIBC_MJN3_ONLY__ |
| #warning .. need to introduce range as a collating item? |
| #endif |
| && (((__uwchar_t)(WC - CUR_COLLATE->range_low)) <= CUR_COLLATE->range_count) |
| ) { /* part of a range */ |
| /* Note: cs->colitem = 0 already. */ |
| TRACE((" found range\n")); |
| ru = CUR_COLLATE->ruletable[CUR_COLLATE->range_rule_offset*CUR_COLLATE->MAX_WEIGHTS + pass]; |
| assert((ru & WEIGHT_MASK) != DITTO_IDX); |
| if ((ru & WEIGHT_MASK) == WEIGHT_MASK) { |
| ru = (ru & RULE_MASK) | RANGE_IDX; |
| cs->weight = CUR_COLLATE->range_base_weight + (WC - CUR_COLLATE->range_low); |
| } |
| goto RANGE_SKIP_TO; |
| } else if (((__uwchar_t)(WC)) <= 0x7fffffffUL) { /* legal but undefined */ |
| UNDEFINED: |
| /* Note: cs->colitem = 0 already. */ |
| ri = CUR_COLLATE->undefined_idx; |
| assert(ri != 0); /* implicit undefined isn't supported */ |
| |
| TRACE((" found explicit UNDEFINED\n")); |
| #ifdef __UCLIBC_MJN3_ONLY__ |
| #warning right now single weight locales do not support .. |
| #endif |
| if (CUR_COLLATE->num_weights == 1) { |
| TRACE((" single weight UNDEFINED\n")); |
| cs->weightidx = RANGE_IDX; |
| cs->weight = ri; |
| cs->s += n; |
| goto PROCESS_WEIGHT; |
| } |
| |
| ri = CUR_COLLATE->index2ruleidx[ri - 1]; |
| ru = CUR_COLLATE->ruletable[ri * CUR_COLLATE->MAX_WEIGHTS + pass]; |
| assert((ru & WEIGHT_MASK) != WEIGHT_MASK); /* TODO: handle ".." */ |
| if ((ru & WEIGHT_MASK) == DITTO_IDX) { |
| cs->colitem = CUR_COLLATE->undefined_idx; |
| } |
| goto RANGE_SKIP_TO; |
| } else { /* illegal */ |
| TRACE((" found illegal\n")); |
| __set_errno(EINVAL); |
| /* We put all illegals in the same equiv class with maximal weight, |
| * and ignore them after the first pass. */ |
| if (pass > 0) { |
| cs->s += n; |
| goto IGNORE_LOOP; |
| } |
| ru = (RULE_FORWARD | RANGE_IDX); |
| cs->weight = 0xffffU; |
| goto RANGE_SKIP_TO; |
| } |
| } else if (CUR_COLLATE->num_weights == 1) { |
| TRACE((" single weight\n")); |
| cs->weightidx = RANGE_IDX; |
| cs->weight = cs->colitem; |
| cs->s += n; |
| goto PROCESS_WEIGHT; |
| } else { |
| TRACE((" normal\n")); |
| } |
| |
| /* if we get here, it is a normal char either singlely weighted, undefined, or in a range */ |
| FOUND: |
| ri = CUR_COLLATE->index2ruleidx[cs->colitem - 1]; |
| TRACE((" ri=%d ", ri)); |
| #ifdef __UCLIBC_MJN3_ONLY__ |
| #warning make sure this is correct |
| #endif |
| if (!ri) { |
| TRACE(("NOT IN THIS LOCALE\n")); |
| goto UNDEFINED; |
| } |
| ru = CUR_COLLATE->ruletable[ri * CUR_COLLATE->MAX_WEIGHTS + pass]; |
| |
| RANGE_SKIP_TO: |
| |
| #ifdef __UCLIBC_MJN3_ONLY__ |
| #warning ignoreables probably should not interrupt backwards processing, but this is wrong |
| #endif |
| /* if (!(ru & WEIGHT_MASK)) { */ |
| /* TRACE(("IGNORE\n")); */ |
| /* cs->s += n; */ |
| /* continue; */ |
| /* } */ |
| |
| |
| TRACE((" rule = %#x weight = %#x popping = %d s = %p eob = %p\n", |
| ru & RULE_MASK, ru & WEIGHT_MASK, popping_backup_stack, |
| cs->s, cs->eob)); |
| /* now we need to check if we're going backwards... */ |
| |
| if (!popping_backup_stack) { |
| if (!(ru & RULE_MASK)) { /* backward */ |
| TRACE(("backwards\n")); |
| assert(cs->bp <= cs->bbe); |
| if (cs->bp == cs->bbe) { |
| if (cs->back_buf == cs->ibb) { /* was using internal buffer */ |
| cs->bp = malloc(cs->bb_size + 128); |
| if (!cs->bp) { |
| __set_errno(ENOMEM); |
| #ifdef __UCLIBC_MJN3_ONLY__ |
| #warning what to do here? |
| #endif |
| cs->weight = 0; |
| return; |
| } |
| memcpy(cs->bp, cs->back_buf, cs->bb_size); |
| |
| } else { |
| cs->bp = realloc(cs->back_buf, cs->bb_size + 128); |
| if (!cs->bp) { |
| __set_errno(ENOMEM); |
| #ifdef __UCLIBC_MJN3_ONLY__ |
| #warning what to do here? |
| #endif |
| cs->weight = 0; |
| return; |
| } |
| } |
| cs->bb_size += 128; |
| cs->bbe = cs->bp + (cs->bbe - cs->back_buf); |
| cs->back_buf = cs->bp; |
| cs->bp = cs->bbe; |
| |
| } |
| if (n==1) { /* single char */ |
| if (*cs->bp && (((unsigned char)(*cs->bp)) < CHAR_MAX)) { |
| *cs->bp += 1; /* increment last single's count */ |
| } else { /* last was a multi, or just starting */ |
| if (!cs->bp) { |
| cs->bp = cs->back_buf; |
| } else { |
| assert(cs->bp < cs->bbe); |
| ++cs->bp; |
| } |
| *cs->bp = 1; |
| } |
| } else { /* multichar */ |
| assert(n>1); |
| assert(cs->bp < cs->bbe); |
| *++cs->bp = -n; |
| } |
| cs->s += n; |
| if (*cs->s) { |
| goto BACK_LOOP; |
| } |
| /* end-of-string so start popping */ |
| cs->eob = cs->s; |
| TRACE(("popping\n")); |
| goto POP_BACKUP; |
| } else if (*cs->bp) { /* was going backward but this element isn't */ |
| /* discard current and use previous backward element */ |
| assert(!cs->cip); |
| cs->eob = cs->s; |
| TRACE(("popping\n")); |
| goto POP_BACKUP; |
| } else { /* was and still going forward */ |
| TRACE(("forwards\n")); |
| if ((ru & (RULE_POSITION|WEIGHT_MASK)) > RULE_POSITION) { |
| assert(ru & WEIGHT_MASK); |
| cs->ru_pushed = ru; |
| cs->weight = cs->position; |
| #ifdef __UCLIBC_MJN3_ONLY__ |
| #warning devel code |
| #endif |
| cs->position = 0; /* reset to reduce size for strcoll? */ |
| cs->s += n; |
| cs->weightidx = RANGE_IDX; |
| goto PROCESS_WEIGHT; |
| } |
| } |
| } else { /* popping backwards stack */ |
| TRACE(("popping (continued)\n")); |
| if (!*cs->bp) { |
| cs->s = cs->eob; |
| } |
| cs->s -= n; |
| } |
| |
| cs->s += n; |
| POSITION_SKIP: |
| cs->weightidx = ru & WEIGHT_MASK; |
| cs->rule = ru & RULE_MASK; |
| } |
| |
| #ifdef __UCLIBC_MJN3_ONLY__ |
| #warning for pending we only want the weight... _not_ the rule |
| #endif |
| if (!cs->weightidx) { /* ignore */ |
| continue; |
| } |
| |
| PROCESS_WEIGHT: |
| assert(cs->weightidx); |
| |
| |
| if (((unsigned int)(cs->weightidx - UI_IDX)) <= (INVAL_IDX-UI_IDX)) { |
| if (cs->weightidx == UI_IDX) { |
| cs->weight = cs->ui_weight; |
| } |
| return; |
| } |
| |
| assert(cs->weightidx != WEIGHT_MASK); |
| if (cs->weightidx == DITTO_IDX) { /* want the weight of the current collating item */ |
| TRACE(("doing ditto\n")); |
| w = CUR_COLLATE->index2weight[cs->colitem -1]; |
| } else if (cs->weightidx <= CUR_COLLATE->max_col_index) { /* normal */ |
| TRACE(("doing normal\n")); |
| w = CUR_COLLATE->index2weight[cs->weightidx -1]; |
| } else { /* a string */ |
| TRACE(("doing string\n")); |
| assert(!(cs->weightidx & RULE_MASK)); |
| /* note: iso14561 allows null string here */ |
| p = CUR_COLLATE->weightstr + (cs->weightidx - (CUR_COLLATE->max_col_index + 2)); |
| if (*p & WEIGHT_MASK) { |
| r = 0; |
| do { |
| assert(r < MAX_PENDING); |
| cs->ci_pending[r++] = *p++; |
| } while (*p & WEIGHT_MASK); |
| cs->cip = cs->ci_pending; |
| } |
| continue; |
| } |
| |
| cs->weight = w; |
| return; |
| } while (1); |
| } |
| |
| int __XL_NPP(wcscoll) (const Wchar *s0, const Wchar *s1 __LOCALE_PARAM ) |
| { |
| col_state_t ws[2]; |
| int pass; |
| |
| if (!CUR_COLLATE->num_weights) { /* C locale */ |
| #ifdef WANT_WIDE |
| return wcscmp(s0, s1); |
| #else |
| return strcmp(s0, s1); |
| #endif |
| } |
| |
| pass = 0; |
| do { /* loop through the weights levels */ |
| init_col_state(ws, s0); |
| init_col_state(ws+1, s1); |
| do { /* loop through the strings */ |
| /* for each string, get the next weight */ |
| next_weight(ws, pass __LOCALE_ARG ); |
| next_weight(ws+1, pass __LOCALE_ARG ); |
| TRACE(("w0=%lu w1=%lu\n", |
| (unsigned long) ws[0].weight, |
| (unsigned long) ws[1].weight)); |
| |
| if (ws[0].weight != ws[1].weight) { |
| return ws[0].weight - ws[1].weight; |
| } |
| } while (ws[0].weight); |
| } while (++pass < CUR_COLLATE->num_weights); |
| |
| return 0; |
| } |
| libc_hidden_def(__XL_NPP(wcscoll)) |
| |
| #ifdef WANT_WIDE |
| |
| size_t __XL_NPP(wcsxfrm)(wchar_t *__restrict ws1, const wchar_t *__restrict ws2, |
| size_t n __LOCALE_PARAM ) |
| { |
| col_state_t cs; |
| size_t count; |
| int pass; |
| |
| if (!CUR_COLLATE->num_weights) { /* C locale */ |
| return __wcslcpy(ws1, ws2, n); |
| } |
| |
| #ifdef __UCLIBC_MJN3_ONLY__ |
| #warning handle empty string as a special case |
| #endif |
| |
| count = pass = 0; |
| do { /* loop through the weights levels */ |
| init_col_state(&cs, ws2); |
| do { /* loop through the string */ |
| next_weight(&cs, pass __LOCALE_ARG ); |
| TRACE(("weight=%lu (%#lx)\n", (unsigned long) cs.weight, (unsigned long) cs.weight)); |
| if (count < n) { |
| ws1[count] = cs.weight +1; |
| } |
| ++count; |
| TRACE(("--------------------------------------------\n")); |
| } while (cs.weight); |
| if (count <= n) { /* overwrite the trailing 0 end-of-pass marker */ |
| ws1[count-1] = 1; |
| } |
| TRACE(("-------------------- pass %d --------------------\n", pass)); |
| } while (++pass < CUR_COLLATE->num_weights); |
| if (count <= n) { /* oops... change it back */ |
| ws1[count-1] = 0; |
| } |
| return count-1; |
| } |
| #if defined L_strxfrm_l || defined L_wcsxfrm_l |
| libc_hidden_def(__XL_NPP(wcsxfrm)) |
| #endif |
| |
| #else /* WANT_WIDE */ |
| |
| static const unsigned long bound[] = { |
| 1UL << 7, |
| 1UL << 11, |
| 1UL << 16, |
| 1UL << 21, |
| 1UL << 26, |
| }; |
| |
| static unsigned char first[] = { |
| 0x0, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc |
| }; |
| |
| /* Use an extension of UTF-8 to store a 32 bit val in max 6 bytes. */ |
| |
| static size_t store(unsigned char *s, size_t count, size_t n, __uwchar_t weight) |
| { |
| int i, r; |
| |
| i = 0; |
| do { |
| if (weight < bound[i]) { |
| break; |
| } |
| } while (++i < sizeof(bound)/sizeof(bound[0])); |
| |
| r = i+1; |
| if (i + count < n) { |
| s += count; |
| s[0] = first[i]; |
| while (i) { |
| s[i] = 0x80 | (weight & 0x3f); |
| weight >>= 6; |
| --i; |
| } |
| s[0] |= weight; |
| } |
| |
| return r; |
| } |
| |
| size_t __XL_NPP(strxfrm)(char *__restrict ws1, const char *__restrict ws2, size_t n |
| __LOCALE_PARAM ) |
| { |
| col_state_t cs; |
| size_t count, inc; |
| int pass; |
| |
| if (!CUR_COLLATE->num_weights) { /* C locale */ |
| return strlcpy(ws1, ws2, n); |
| } |
| |
| #ifdef __UCLIBC_MJN3_ONLY__ |
| #warning handle empty string as a special case |
| #endif |
| |
| inc = count = pass = 0; |
| do { /* loop through the weights levels */ |
| init_col_state(&cs, ws2); |
| do { /* loop through the string */ |
| next_weight(&cs, pass __LOCALE_ARG ); |
| TRACE(("weight=%lu (%#lx)\n", (unsigned long) cs.weight, (unsigned long) cs.weight)); |
| inc = store((unsigned char *)ws1, count, n, cs.weight + 1); |
| count += inc; |
| TRACE(("--------------------------------------------\n")); |
| } while (cs.weight); |
| /* overwrite the trailing 0 end-of-pass marker */ |
| assert(inc == 1); |
| if (count <= n) { |
| ws1[count-1] = 1; |
| } |
| TRACE(("-------------------- pass %d --------------------\n", pass)); |
| } while (++pass < CUR_COLLATE->num_weights); |
| if (count <= n) { /* oops... change it back */ |
| ws1[count-1] = 0; |
| } |
| return count-1; |
| } |
| #ifdef L_strxfrm_l |
| libc_hidden_def(__XL_NPP(strxfrm)) |
| #endif |
| |
| #endif /* WANT_WIDE */ |
| |
| #endif /* defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) */ |
| |
| #endif /* defined(L_strxfrm) || defined(L_strxfrm_l) || defined(L_wcsxfrm) || defined(L_wcsxfrm_l) */ |
| |
| #endif /* __UCLIBC_HAS_LOCALE__ */ |
| /**********************************************************************/ |