OSDN Git Service

remove a few more empty #if/#endif pairs
[uclinux-h8/uClibc.git] / libc / string / _collate.c
1 /*
2  * Copyright (C) 2002     Manuel Novoa III
3  * Copyright (C) 2000-2005 Erik Andersen <andersen@uclibc.org>
4  *
5  * Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball.
6  */
7
8 /*  Dec 20, 2002
9  *  Initial test implementation of strcoll, strxfrm, wcscoll, and wcsxfrm.
10  *  The code needs to be cleaned up a good bit, but I'd like to see people
11  *  test it out.
12  *
13  */
14
15 #include "_string.h"
16 #include <ctype.h>
17 #include <locale.h>
18 #include <stdlib.h>
19 #include <errno.h>
20 #include <assert.h>
21
22 #ifdef __UCLIBC_HAS_LOCALE__
23 #if defined(L_strxfrm) || defined(L_strxfrm_l) || defined(L_wcsxfrm) || defined(L_wcsxfrm_l)
24
25 #ifdef L_strxfrm
26 #ifndef WANT_WIDE
27 #error WANT_WIDE should be defined for L_strxfrm
28 #endif
29 #ifdef L_wcsxfrm
30 #error L_wcsxfrm already defined for L_strxfrm
31 #endif
32 #endif /* L_strxfrm */
33
34 #if defined(L_strxfrm) || defined(L_strxfrm_l)
35
36 #define wcscoll   strcoll
37 #define wcscoll_l strcoll_l
38 #define wcsxfrm   strxfrm
39 #define wcsxfrm_l strxfrm_l
40
41 #undef WANT_WIDE
42 #undef Wvoid
43 #undef Wchar
44 #undef Wuchar
45 #undef Wint
46
47 #define Wchar char
48
49 #endif /* defined(L_strxfrm) || defined(L_strxfrm_l) */
50
51 #if defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE)
52
53
54 int wcscoll (const Wchar *s0, const Wchar *s1)
55 {
56         return wcscoll_l(s0, s1, __UCLIBC_CURLOCALE );
57 }
58 libc_hidden_def(wcscoll)
59
60
61 size_t wcsxfrm(Wchar *__restrict ws1, const Wchar *__restrict ws2, size_t n)
62 {
63         return wcsxfrm_l(ws1, ws2, n, __UCLIBC_CURLOCALE );
64 }
65 libc_hidden_def(wcsxfrm)
66
67 #else  /* defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) */
68
69
70 #if 0
71 #define CUR_COLLATE (&__UCLIBC_CURLOCALE->collate)
72 #else
73 #define CUR_COLLATE (& __LOCALE_PTR->collate)
74 #endif
75
76 #define MAX_PENDING 8
77
78 typedef struct {
79         const Wchar *s;
80         const Wchar *eob;                       /* end of backward */
81
82         __uwchar_t weight;
83         __uwchar_t ui_weight;           /* undefined or invalid */
84         int colitem;
85         int weightidx;
86         int rule;
87         size_t position;
88         /* should be wchar_t.  if wchar < 0 do EILSEQ? */
89         __uwchar_t *cip;
90         __uwchar_t ci_pending[MAX_PENDING];     /* nul-terminated */
91
92         char *back_buf;
93         char *bbe;                                      /* end of back_buf (actual last... not 1 past end) */
94         char *bp;                                       /* ptr into backbuf, NULL if not in backward mode */
95         char ibb[128];
96         size_t bb_size;
97
98         int ru_pushed;
99 } col_state_t;
100
101
102 #define WEIGHT_MASK     0x3fffU
103 #define RULE_MASK       0xc000U
104
105 #define RULE_FORWARD  (1 << 14)
106 #define RULE_POSITION (1 << 15)
107
108 #define UI_IDX          (WEIGHT_MASK-6)
109 #define POSIT_IDX       (WEIGHT_MASK-5)
110 #define RANGE_IDX       (WEIGHT_MASK-4)
111 #define UNDEF_IDX       (WEIGHT_MASK-3)
112 #define INVAL_IDX       (WEIGHT_MASK-2)
113 #define DITTO_IDX   (WEIGHT_MASK-1)
114
115
116 #undef TRACE
117 #if 0
118 #define TRACE(X)        printf X
119 #else
120 #define TRACE(X)        ((void)0)
121 #endif
122
123 static int lookup(wchar_t wc   __LOCALE_PARAM )
124 {
125         unsigned int sc, n, i0, i1;
126
127         if (((__uwchar_t) wc) > 0xffffU) {
128                 return 0;
129         }
130
131         sc = wc & CUR_COLLATE->ti_mask;
132         wc >>= CUR_COLLATE->ti_shift;
133         n = wc & CUR_COLLATE->ii_mask;
134         wc >>= CUR_COLLATE->ii_shift;
135
136         i0 = CUR_COLLATE->wcs2colidt_tbl[wc];
137         i0 <<= CUR_COLLATE->ii_shift;
138         i1 = CUR_COLLATE->wcs2colidt_tbl[CUR_COLLATE->ii_len + i0 + n];
139         i1 <<= CUR_COLLATE->ti_shift;
140         return CUR_COLLATE->wcs2colidt_tbl[CUR_COLLATE->ii_len + CUR_COLLATE->ti_len + i1 + sc];
141
142 }
143
144 static void init_col_state(col_state_t *cs, const Wchar *wcs)
145 {
146         memset(cs, 0, sizeof(col_state_t));
147         cs->s = wcs;
148         cs->bp = cs->back_buf = cs->ibb;
149         cs->bb_size = 128;
150         cs->bbe = cs->back_buf + (cs->bb_size -1);
151 }
152
153 static void next_weight(col_state_t *cs, int pass   __LOCALE_PARAM )
154 {
155         int r, w, ru, ri, popping_backup_stack;
156         ssize_t n;
157         const uint16_t *p;
158 #ifdef WANT_WIDE
159 #define WC (*cs->s)
160 #define N (1)
161 #else  /* WANT_WIDE */
162         wchar_t WC;
163         size_t n0, nx;
164 #define N n0
165
166 #endif /* WANT_WIDE */
167
168         do {
169
170                 if (cs->ru_pushed) {
171                         ru = cs->ru_pushed;
172                         TRACE(("ru_pushed = %d\n", ru));
173                         cs->ru_pushed = 0;
174                         goto POSITION_SKIP;
175                 }
176
177 #ifdef __UCLIBC_MJN3_ONLY__
178 #warning should we walk pendings backwards?
179 #endif
180                 if (cs->cip) {                  /* possible pending weight */
181                         if ((r = *(cs->cip++)) == 0) {
182                                 cs->cip = NULL;
183                                 continue;
184                         }
185                         cs->weightidx = r & WEIGHT_MASK;
186                         assert(cs->weightidx);
187 /*                      assert(cs->weightidx != WEIGHT_MASK); */
188                 } else {                                /* get the next collation item from the string */
189                         TRACE(("clearing popping flag\n"));
190                         popping_backup_stack = 0;
191
192                 IGNORE_LOOP:
193                         /* keep first pos as 0 for a sentinal */
194                         if (*cs->bp) {                          /* pending backward chars */
195                         POP_BACKUP:
196                                 popping_backup_stack = 1;
197                                 TRACE(("setting popping flag\n"));
198                                 n = 0;
199                                 if (*cs->bp > 0) {              /* singles pending */
200                                         cs->s -= 1;
201                                         if ((*cs->bp -= 1) == 0) {
202                                                 cs->bp -= 1;
203                                         }
204                                 } else {                                /* last was a multi */
205                                         cs->s += *cs->bp;
206                                         cs->bp -= 1;
207                                 }
208                         } else if (!*cs->s) { /* not in backward mode and end of string */
209                                 cs->weight = 0;
210                                 return;
211                         } else {
212                                 cs->position += 1;
213                         }
214
215                 BACK_LOOP:
216 #ifdef WANT_WIDE
217                         n = 1;
218                         cs->colitem = r = lookup(*cs->s   __LOCALE_ARG );
219 #else  /* WANT_WIDE */
220                         n = n0 = __locale_mbrtowc_l(&WC, cs->s, __LOCALE_PTR);
221                         if (n < 0) {
222                                 __set_errno(EILSEQ);
223                                 cs->weight = 0;
224                                 return;
225                         }
226                         cs->colitem = r = lookup(WC   __LOCALE_ARG );
227 #endif /* WANT_WIDE */
228
229                         TRACE((" r=%d WC=%#lx\n", r, (unsigned long)(WC)));
230
231                         if (r > CUR_COLLATE->max_col_index) { /* starting char for one or more sequences */
232                                 p = CUR_COLLATE->multistart_tbl;
233                                 p += p[r-CUR_COLLATE->max_col_index -1];
234                                 do {
235                                         n = N;
236                                         r = *p++;
237                                         do {
238                                                 if (!*p) {              /* found it */
239                                                         cs->colitem = r;
240                                                         TRACE(("    found multi %d\n", n));
241                                                         goto FOUND;
242                                                 }
243 #ifdef WANT_WIDE
244                                                 /* the lookup check here is safe since we're assured that *p is a valid colidx */
245                                                 if (!cs->s[n] || (lookup(cs->s[n]   __LOCALE_ARG ) != *p)) {
246                                                         do {} while (*p++);
247                                                         break;
248                                                 }
249                                                 ++p;
250                                                 ++n;
251 #else  /* WANT_WIDE */
252                                                 if (cs->s[n]) {
253                                                         nx = __locale_mbrtowc_l(&WC, cs->s + n, __LOCALE_PTR);
254                                                         if (nx < 0) {
255                                                                 __set_errno(EILSEQ);
256                                                                 cs->weight = 0;
257                                                                 return;
258                                                         }
259                                                 }
260                                                 if (!cs->s[n] || (lookup(WC   __LOCALE_ARG ) != *p)) {
261                                                         do {} while (*p++);
262                                                         break;
263                                                 }
264                                                 ++p;
265                                                 n += nx; /* Only gets here if cs->s[n] != 0, so nx is set. */
266 #endif /* WANT_WIDE */
267                                         } while (1);
268                                 } while (1);
269                         } else if (r == 0) {            /* illegal, undefined, or part of a range */
270                                 if ((CUR_COLLATE->range_count)
271 #ifdef __UCLIBC_MJN3_ONLY__
272 #warning .. need to introduce range as a collating item?
273 #endif
274                                         && (((__uwchar_t)(WC - CUR_COLLATE->range_low)) <= CUR_COLLATE->range_count)
275                                         ) {                                     /* part of a range */
276                                         /* Note: cs->colitem = 0 already. */
277                                         TRACE(("    found range\n"));
278                                         ru = CUR_COLLATE->ruletable[CUR_COLLATE->range_rule_offset*CUR_COLLATE->MAX_WEIGHTS + pass];
279                                         assert((ru & WEIGHT_MASK) != DITTO_IDX);
280                                         if ((ru & WEIGHT_MASK) == WEIGHT_MASK) {
281                                                 ru = (ru & RULE_MASK) | RANGE_IDX;
282                                                 cs->weight = CUR_COLLATE->range_base_weight + (WC - CUR_COLLATE->range_low);
283                                         }
284                                         goto RANGE_SKIP_TO;
285                                 } else if (((__uwchar_t)(WC)) <= 0x7fffffffUL) { /* legal but undefined */
286                                 UNDEFINED:
287                                         /* Note: cs->colitem = 0 already. */
288                                         ri = CUR_COLLATE->undefined_idx;
289                                         assert(ri != 0); /* implicit undefined isn't supported */
290
291                                         TRACE(("    found explicit UNDEFINED\n"));
292 #ifdef __UCLIBC_MJN3_ONLY__
293 #warning right now single weight locales do not support ..
294 #endif
295                                         if (CUR_COLLATE->num_weights == 1) {
296                                                 TRACE(("    single weight UNDEFINED\n"));
297                                                 cs->weightidx = RANGE_IDX;
298                                                 cs->weight = ri;
299                                                 cs->s += n;
300                                                 goto PROCESS_WEIGHT;
301                                         }
302
303                                         ri = CUR_COLLATE->index2ruleidx[ri - 1];
304                                         ru = CUR_COLLATE->ruletable[ri * CUR_COLLATE->MAX_WEIGHTS + pass];
305                                         assert((ru & WEIGHT_MASK) != WEIGHT_MASK); /* TODO: handle ".." */
306                                         if ((ru & WEIGHT_MASK) == DITTO_IDX) {
307                                                 cs->colitem = CUR_COLLATE->undefined_idx;
308                                         }
309                                         goto RANGE_SKIP_TO;
310                                 } else {                /* illegal */
311                                         TRACE(("    found illegal\n"));
312                                         __set_errno(EINVAL);
313                                         /* We put all illegals in the same equiv class with maximal weight,
314                                          * and ignore them after the first pass. */
315                                         if (pass > 0) {
316                                                 cs->s += n;
317                                                 goto IGNORE_LOOP;
318                                         }
319                                         ru = (RULE_FORWARD | RANGE_IDX);
320                                         cs->weight = 0xffffU;
321                                         goto RANGE_SKIP_TO;
322                                 }
323                         } else if (CUR_COLLATE->num_weights == 1) {
324                                 TRACE(("    single weight\n"));
325                                 cs->weightidx = RANGE_IDX;
326                                 cs->weight = cs->colitem;
327                                 cs->s += n;
328                                 goto PROCESS_WEIGHT;
329                         } else {
330                                 TRACE(("    normal\n"));
331                         }
332
333                         /* if we get here, it is a normal char either singlely weighted, undefined, or in a range */
334                 FOUND:
335                         ri = CUR_COLLATE->index2ruleidx[cs->colitem - 1];
336                         TRACE((" ri=%d ", ri));
337 #ifdef __UCLIBC_MJN3_ONLY__
338 #warning make sure this is correct
339 #endif
340                         if (!ri) {
341                                 TRACE(("NOT IN THIS LOCALE\n"));
342                                 goto UNDEFINED;
343                         }
344                         ru = CUR_COLLATE->ruletable[ri * CUR_COLLATE->MAX_WEIGHTS + pass];
345
346                 RANGE_SKIP_TO:
347
348 #ifdef __UCLIBC_MJN3_ONLY__
349 #warning ignoreables probably should not interrupt backwards processing, but this is wrong
350 #endif
351 /*                      if (!(ru & WEIGHT_MASK)) { */
352 /*                              TRACE(("IGNORE\n")); */
353 /*                              cs->s += n; */
354 /*                              continue; */
355 /*                      } */
356
357
358                         TRACE((" rule = %#x  weight = %#x  popping = %d  s = %p  eob = %p\n",
359                                    ru & RULE_MASK, ru & WEIGHT_MASK, popping_backup_stack,
360                                    cs->s, cs->eob));
361                         /* now we need to check if we're going backwards... */
362
363                         if (!popping_backup_stack) {
364                                 if (!(ru & RULE_MASK)) { /* backward */
365                                         TRACE(("backwards\n"));
366                                         assert(cs->bp <= cs->bbe);
367                                         if (cs->bp == cs->bbe) {
368                                                 if (cs->back_buf == cs->ibb) { /* was using internal buffer */
369                                                         cs->bp = malloc(cs->bb_size + 128);
370                                                         if (!cs->bp) {
371                                                                 __set_errno(ENOMEM);
372 #ifdef __UCLIBC_MJN3_ONLY__
373 #warning what to do here?
374 #endif
375                                                                 cs->weight = 0;
376                                                                 return;
377                                                         }
378                                                         memcpy(cs->bp, cs->back_buf, cs->bb_size);
379
380                                                 } else {
381                                                         cs->bp = realloc(cs->back_buf, cs->bb_size + 128);
382                                                         if (!cs->bp) {
383                                                                 __set_errno(ENOMEM);
384 #ifdef __UCLIBC_MJN3_ONLY__
385 #warning what to do here?
386 #endif
387                                                                 cs->weight = 0;
388                                                                 return;
389                                                         }
390                                                 }
391                                                 cs->bb_size += 128;
392                                                 cs->bbe = cs->bp + (cs->bbe - cs->back_buf);
393                                                 cs->back_buf = cs->bp;
394                                                 cs->bp = cs->bbe;
395
396                                         }
397                                         if (n==1) {                     /* single char */
398                                                 if (*cs->bp && (((unsigned char)(*cs->bp)) < CHAR_MAX)) {
399                                                         *cs->bp += 1; /* increment last single's count */
400                                                 } else {          /* last was a multi, or just starting */
401                                                         if (!cs->bp) {
402                                                                 cs->bp = cs->back_buf;
403                                                         } else {
404                                                                 assert(cs->bp < cs->bbe);
405                                                                 ++cs->bp;
406                                                         }
407                                                         *cs->bp = 1;
408                                                 }
409                                         } else {                        /* multichar */
410                                                 assert(n>1);
411                                                 assert(cs->bp < cs->bbe);
412                                                 *++cs->bp = -n;
413                                         }
414                                         cs->s += n;
415                                         if (*cs->s) {
416                                                 goto BACK_LOOP;
417                                         }
418                                         /* end-of-string so start popping */
419                                         cs->eob = cs->s;
420                                         TRACE(("popping\n"));
421                                         goto POP_BACKUP;
422                                 } else if (*cs->bp) { /* was going backward but this element isn't */
423                                         /* discard current and use previous backward element */
424                                         assert(!cs->cip);
425                                         cs->eob = cs->s;
426                                         TRACE(("popping\n"));
427                                         goto POP_BACKUP;
428                                 } else {                                /* was and still going forward */
429                                         TRACE(("forwards\n"));
430                                         if ((ru & (RULE_POSITION|WEIGHT_MASK)) > RULE_POSITION) {
431                                                 assert(ru & WEIGHT_MASK);
432                                                 cs->ru_pushed = ru;
433                                                 cs->weight = cs->position;
434 #ifdef __UCLIBC_MJN3_ONLY__
435 #warning devel code
436 #endif
437                                                 cs->position = 0;       /* reset to reduce size for strcoll? */
438                                                 cs->s += n;
439                                                 cs->weightidx = RANGE_IDX;
440                                                 goto PROCESS_WEIGHT;
441                                         }
442                                 }
443                         } else {                                        /* popping backwards stack */
444                                 TRACE(("popping (continued)\n"));
445                                 if (!*cs->bp) {
446                                         cs->s = cs->eob;
447                                 }
448                                 cs->s -= n;
449                         }
450
451                         cs->s += n;
452                 POSITION_SKIP:
453                         cs->weightidx = ru & WEIGHT_MASK;
454                         cs->rule = ru & RULE_MASK;
455                 }
456
457 #ifdef __UCLIBC_MJN3_ONLY__
458 #warning for pending we only want the weight... _not_ the rule
459 #endif
460                 if (!cs->weightidx) {   /* ignore */
461                         continue;
462                 }
463
464         PROCESS_WEIGHT:
465                 assert(cs->weightidx);
466
467
468                 if (((unsigned int)(cs->weightidx - UI_IDX)) <= (INVAL_IDX-UI_IDX)) {
469                         if (cs->weightidx == UI_IDX) {
470                                 cs->weight = cs->ui_weight;
471                         }
472                         return;
473                 }
474
475                 assert(cs->weightidx != WEIGHT_MASK);
476                 if (cs->weightidx == DITTO_IDX) { /* want the weight of the current collating item */
477                         TRACE(("doing ditto\n"));
478                         w = CUR_COLLATE->index2weight[cs->colitem -1];
479                 } else if (cs->weightidx <= CUR_COLLATE->max_col_index) { /* normal */
480                         TRACE(("doing normal\n"));
481                         w = CUR_COLLATE->index2weight[cs->weightidx -1];
482                 } else {                                /* a string */
483                         TRACE(("doing string\n"));
484                         assert(!(cs->weightidx & RULE_MASK));
485                         /* note: iso14561 allows null string here */
486                         p = CUR_COLLATE->weightstr + (cs->weightidx - (CUR_COLLATE->max_col_index + 2));
487                         if (*p & WEIGHT_MASK) {
488                                 r = 0;
489                                 do {
490                                         assert(r < MAX_PENDING);
491                                         cs->ci_pending[r++] = *p++;
492                                 } while (*p & WEIGHT_MASK);
493                                 cs->cip = cs->ci_pending;
494                         }
495                         continue;
496                 }
497
498                 cs->weight = w;
499                 return;
500         } while (1);
501 }
502
503 libc_hidden_proto(__XL_NPP(wcscoll))
504 int __XL_NPP(wcscoll) (const Wchar *s0, const Wchar *s1   __LOCALE_PARAM )
505 {
506         col_state_t ws[2];
507         int pass;
508
509         if (!CUR_COLLATE->num_weights) { /* C locale */
510 #ifdef WANT_WIDE
511                 return wcscmp(s0, s1);
512 #else
513                 return strcmp(s0, s1);
514 #endif
515         }
516
517         pass = 0;
518         do {                                            /* loop through the weights levels */
519                 init_col_state(ws, s0);
520                 init_col_state(ws+1, s1);
521                 do {                                    /* loop through the strings */
522                         /* for each string, get the next weight */
523                         next_weight(ws, pass   __LOCALE_ARG );
524                         next_weight(ws+1, pass   __LOCALE_ARG );
525                         TRACE(("w0=%lu  w1=%lu\n",
526                                    (unsigned long) ws[0].weight,
527                                    (unsigned long) ws[1].weight));
528
529                         if (ws[0].weight != ws[1].weight) {
530                                 return ws[0].weight - ws[1].weight;
531                         }
532                 } while (ws[0].weight);
533         } while (++pass < CUR_COLLATE->num_weights);
534
535         return 0;
536 }
537 libc_hidden_def(__XL_NPP(wcscoll))
538
539 #ifdef WANT_WIDE
540
541 extern size_t __wcslcpy(wchar_t *__restrict dst,
542                 const wchar_t *__restrict src, size_t n);
543
544 libc_hidden_proto(__XL_NPP(wcsxfrm))
545 size_t __XL_NPP(wcsxfrm)(wchar_t *__restrict ws1, const wchar_t *__restrict ws2,
546                                          size_t n   __LOCALE_PARAM )
547 {
548         col_state_t cs;
549         size_t count;
550         int pass;
551
552         if (!CUR_COLLATE->num_weights) { /* C locale */
553                 return __wcslcpy(ws1, ws2, n);
554         }
555
556 #ifdef __UCLIBC_MJN3_ONLY__
557 #warning handle empty string as a special case
558 #endif
559
560         count = pass = 0;
561         do {                                            /* loop through the weights levels */
562                 init_col_state(&cs, ws2);
563                 do {                                    /* loop through the string */
564                         next_weight(&cs, pass   __LOCALE_ARG );
565                         TRACE(("weight=%lu (%#lx)\n", (unsigned long) cs.weight, (unsigned long) cs.weight));
566                         if (count < n) {
567                                 ws1[count] = cs.weight +1;
568                         }
569                         ++count;
570                         TRACE(("--------------------------------------------\n"));
571                 } while (cs.weight);
572                 if (count <= n) {               /* overwrite the trailing 0 end-of-pass marker */
573                         ws1[count-1] = 1;
574                 }
575                 TRACE(("--------------------  pass %d  --------------------\n", pass));
576         } while (++pass < CUR_COLLATE->num_weights);
577         if (count <= n) {                       /* oops... change it back */
578                 ws1[count-1] = 0;
579         }
580         return count-1;
581 }
582 libc_hidden_def(__XL_NPP(wcsxfrm))
583
584 #else  /* WANT_WIDE */
585
586 static const unsigned long bound[] = {
587         1UL << 7,
588         1UL << 11,
589         1UL << 16,
590         1UL << 21,
591         1UL << 26,
592 };
593
594 static unsigned char first[] = {
595         0x0, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc
596 };
597
598 /* Use an extension of UTF-8 to store a 32 bit val in max 6 bytes. */
599
600 static size_t store(unsigned char *s, size_t count, size_t n, __uwchar_t weight)
601 {
602         int i, r;
603
604         i = 0;
605         do {
606                 if (weight < bound[i]) {
607                         break;
608                 }
609         } while (++i < sizeof(bound)/sizeof(bound[0]));
610
611         r = i+1;
612         if (i + count < n) {
613                 s += count;
614                 s[0] = first[i];
615                 while (i) {
616                         s[i] = 0x80 | (weight & 0x3f);
617                         weight >>= 6;
618                         --i;
619                 }
620                 s[0] |= weight;
621         }
622
623         return r;
624 }
625
626 libc_hidden_proto(__XL_NPP(strxfrm))
627 size_t __XL_NPP(strxfrm)(char *__restrict ws1, const char *__restrict ws2, size_t n
628                                          __LOCALE_PARAM )
629 {
630         col_state_t cs;
631         size_t count, inc;
632         int pass;
633
634         if (!CUR_COLLATE->num_weights) { /* C locale */
635                 return strlcpy(ws1, ws2, n);
636         }
637
638 #ifdef __UCLIBC_MJN3_ONLY__
639 #warning handle empty string as a special case
640 #endif
641
642         inc = count = pass = 0;
643         do {                                            /* loop through the weights levels */
644                 init_col_state(&cs, ws2);
645                 do {                                    /* loop through the string */
646                         next_weight(&cs, pass   __LOCALE_ARG );
647                         TRACE(("weight=%lu (%#lx)\n", (unsigned long) cs.weight, (unsigned long) cs.weight));
648                         inc = store((unsigned char *)ws1, count, n, cs.weight + 1);
649                         count += inc;
650                         TRACE(("--------------------------------------------\n"));
651                 } while (cs.weight);
652                 /* overwrite the trailing 0 end-of-pass marker */
653                 assert(inc == 1);
654                 if (count <= n) {
655                         ws1[count-1] = 1;
656                 }
657                 TRACE(("--------------------  pass %d  --------------------\n", pass));
658         } while (++pass < CUR_COLLATE->num_weights);
659         if (count <= n) {                       /* oops... change it back */
660                 ws1[count-1] = 0;
661         }
662         return count-1;
663 }
664 libc_hidden_def(__XL_NPP(strxfrm))
665
666 #endif /* WANT_WIDE */
667
668 #endif /* defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) */
669
670 #endif /* defined(L_strxfrm) || defined(L_strxfrm_l) || defined(L_wcsxfrm) || defined(L_wcsxfrm_l) */
671
672 #endif /* __UCLIBC_HAS_LOCALE__ */
673 /**********************************************************************/