OSDN Git Service

Fix a few bugs in the new extended locale functions.
[uclinux-h8/uClibc.git] / libc / string / wstring.c
index 08927ed..250f987 100644 (file)
  *
  *  ATTENTION!   ATTENTION!   ATTENTION!   ATTENTION!   ATTENTION! */
 
+/*  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.
+ */
+
 #define _STDIO_UTILITY
 #define _GNU_SOURCE
 #include <string.h>
 #include <stdlib.h>
 #include <errno.h>
 #include <signal.h>
+#include <assert.h>
+#include <locale.h>
 
 #ifdef WANT_WIDE
 #include <wchar.h>
 #include <wctype.h>
-#include <locale.h>
+#include <bits/uClibc_uwchar.h>
 
 #define Wvoid                  wchar_t
 #define Wchar                  wchar_t
@@ -69,12 +78,22 @@ typedef unsigned char       __string_uchar_t;
 #define _SYS_NERR                      126
 #endif
 
+#ifdef __UCLIBC_HAS_ERRNO_MESSAGES__
 #define _SYS_ERRMSG_MAXLEN      50
+#else  /* __UCLIBC_HAS_ERRNO_MESSAGES__ */
+#define _SYS_ERRMSG_MAXLEN      0
+#endif /* __UCLIBC_HAS_ERRNO_MESSAGES__ */
+
 
 extern const char _string_syserrmsgs[];
 
 #define _SYS_NSIG                      32
+
+#ifdef __UCLIBC_HAS_SIGNUM_MESSAGES__
 #define _SYS_SIGMSG_MAXLEN     25
+#else  /* __UCLIBC_HAS_SIGNUM_MESSAGES__ */
+#define _SYS_SIGMSG_MAXLEN     0
+#endif /* __UCLIBC_HAS_SIGNUM_MESSAGES__ */
 
 extern const char _string_syssigmsgs[];
 
@@ -85,14 +104,14 @@ extern const char _string_syssigmsgs[];
 #define _STRERROR_BUFSIZE _SYS_ERRMSG_MAXLEN
 #endif
 
-#if _SYS_SIGMSG_MAXLEN < __UIM_BUFLEN_INT + 14
-#define _STRSIGNAL_BUFSIZE (__UIM_BUFLEN_INT + 14)
+#if _SYS_SIGMSG_MAXLEN < __UIM_BUFLEN_INT + 15
+#define _STRSIGNAL_BUFSIZE (__UIM_BUFLEN_INT + 15)
 #else
 #define _STRSIGNAL_BUFSIZE _SYS_SIGMSG_MAXLEN
 #endif
 
 /**********************************************************************/
-#ifdef L__string_syserrmsgs
+#if defined(L__string_syserrmsgs) && defined(__UCLIBC_HAS_ERRNO_MESSAGES__)
 
 const char _string_syserrmsgs[] = {
        /*   0:    0,  8 */ "Success\0"
@@ -230,6 +249,166 @@ const char _string_syserrmsgs[] = {
 
 #endif
 /**********************************************************************/
+#if defined(L_sys_errlist) && defined(__UCLIBC_HAS_SYS_ERRLIST__)
+
+link_warning(_sys_errlist, "sys_nerr and sys_errlist are obsolete and uClibc support for them (in at least some configurations) will probably be unavailable in the near future.")
+
+const char *const sys_errlist[] = {
+       [0] =                           _string_syserrmsgs + 0,
+       [EPERM] =                       _string_syserrmsgs + 8,
+       [ENOENT] =                      _string_syserrmsgs + 32,
+       [ESRCH] =                       _string_syserrmsgs + 58,
+       [EINTR] =                       _string_syserrmsgs + 74,
+       [EIO] =                         _string_syserrmsgs + 98,
+       [ENXIO] =                       _string_syserrmsgs + 117,
+       [E2BIG] =                       _string_syserrmsgs + 143,
+       [ENOEXEC] =                     _string_syserrmsgs + 166,
+       [EBADF] =                       _string_syserrmsgs + 184,
+       [ECHILD] =                      _string_syserrmsgs + 204,
+       [EAGAIN] =                      _string_syserrmsgs + 223,
+       [ENOMEM] =                      _string_syserrmsgs + 256,
+       [EACCES] =                      _string_syserrmsgs + 279,
+       [EFAULT] =                      _string_syserrmsgs + 297,
+       [ENOTBLK] =                     _string_syserrmsgs + 309,
+       [EBUSY] =                       _string_syserrmsgs + 331,
+       [EEXIST] =                      _string_syserrmsgs + 355,
+       [EXDEV] =                       _string_syserrmsgs + 367,
+       [ENODEV] =                      _string_syserrmsgs + 393,
+       [ENOTDIR] =                     _string_syserrmsgs + 408,
+       [EISDIR] =                      _string_syserrmsgs + 424,
+       [EINVAL] =                      _string_syserrmsgs + 439,
+       [ENFILE] =                      _string_syserrmsgs + 456,
+       [EMFILE] =                      _string_syserrmsgs + 486,
+       [ENOTTY] =                      _string_syserrmsgs + 506,
+       [ETXTBSY] =                     _string_syserrmsgs + 537,
+       [EFBIG] =                       _string_syserrmsgs + 552,
+       [ENOSPC] =                      _string_syserrmsgs + 567,
+       [ESPIPE] =                      _string_syserrmsgs + 591,
+       [EROFS] =                       _string_syserrmsgs + 604,
+       [EMLINK] =                      _string_syserrmsgs + 626,
+       [EPIPE] =                       _string_syserrmsgs + 641,
+       [EDOM] =                        _string_syserrmsgs + 653,
+       [ERANGE] =                      _string_syserrmsgs + 686,
+       [EDEADLK] =                     _string_syserrmsgs + 716,
+       [ENAMETOOLONG] =        _string_syserrmsgs + 742,
+       [ENOLCK] =                      _string_syserrmsgs + 761,
+       [ENOSYS] =                      _string_syserrmsgs + 780,
+       [ENOTEMPTY] =           _string_syserrmsgs + 805,
+       [ELOOP] =                       _string_syserrmsgs + 825,
+       /*      _string_syserrmsgs + 859, */
+       [ENOMSG] =                      _string_syserrmsgs + 860,
+       [EIDRM] =                       _string_syserrmsgs + 887,
+       [ECHRNG] =                      _string_syserrmsgs + 906,
+       [EL2NSYNC] =            _string_syserrmsgs + 934,
+       [EL3HLT] =                      _string_syserrmsgs + 959,
+       [EL3RST] =                      _string_syserrmsgs + 974,
+       [ELNRNG] =                      _string_syserrmsgs + 988,
+       [EUNATCH] =                     _string_syserrmsgs + 1013,
+       [ENOCSI] =                      _string_syserrmsgs + 1042,
+       [EL2HLT] =                      _string_syserrmsgs + 1069,
+       [EBADE] =                       _string_syserrmsgs + 1084,
+       [EBADR] =                       _string_syserrmsgs + 1101,
+       [EXFULL] =                      _string_syserrmsgs + 1128,
+       [ENOANO] =                      _string_syserrmsgs + 1142,
+       [EBADRQC] =                     _string_syserrmsgs + 1151,
+       [EBADSLT] =                     _string_syserrmsgs + 1172,
+       /*      _string_syserrmsgs + 1185, */
+       [EBFONT] =                      _string_syserrmsgs + 1186,
+       [ENOSTR] =                      _string_syserrmsgs + 1207,
+       [ENODATA] =                     _string_syserrmsgs + 1227,
+       [ETIME] =                       _string_syserrmsgs + 1245,
+       [ENOSR] =                       _string_syserrmsgs + 1259,
+       [ENONET] =                      _string_syserrmsgs + 1284,
+       [ENOPKG] =                      _string_syserrmsgs + 1314,
+       [EREMOTE] =                     _string_syserrmsgs + 1336,
+       [ENOLINK] =                     _string_syserrmsgs + 1353,
+       [EADV] =                        _string_syserrmsgs + 1375,
+       [ESRMNT] =                      _string_syserrmsgs + 1391,
+       [ECOMM] =                       _string_syserrmsgs + 1405,
+       [EPROTO] =                      _string_syserrmsgs + 1433,
+       [EMULTIHOP] =           _string_syserrmsgs + 1448,
+       [EDOTDOT] =                     _string_syserrmsgs + 1467,
+       [EBADMSG] =                     _string_syserrmsgs + 1486,
+       [EOVERFLOW] =           _string_syserrmsgs + 1498,
+       [ENOTUNIQ] =            _string_syserrmsgs + 1536,
+       [EBADFD] =                      _string_syserrmsgs + 1563,
+       [EREMCHG] =                     _string_syserrmsgs + 1592,
+       [ELIBACC] =                     _string_syserrmsgs + 1615,
+       [ELIBBAD] =                     _string_syserrmsgs + 1654,
+       [ELIBSCN] =                     _string_syserrmsgs + 1691,
+       [ELIBMAX] =                     _string_syserrmsgs + 1723,
+       [ELIBEXEC] =            _string_syserrmsgs + 1771,
+       [EILSEQ] =                      _string_syserrmsgs + 1809,
+       [ERESTART] =            _string_syserrmsgs + 1859,
+       [ESTRPIPE] =            _string_syserrmsgs + 1903,
+       [EUSERS] =                      _string_syserrmsgs + 1922,
+       [ENOTSOCK] =            _string_syserrmsgs + 1937,
+       [EDESTADDRREQ] =        _string_syserrmsgs + 1968,
+       [EMSGSIZE] =            _string_syserrmsgs + 1997,
+       [EPROTOTYPE] =          _string_syserrmsgs + 2014,
+       [ENOPROTOOPT] =         _string_syserrmsgs + 2045,
+       [EPROTONOSUPPORT] =     _string_syserrmsgs + 2068,
+       [ESOCKTNOSUPPORT] =     _string_syserrmsgs + 2091,
+       [EOPNOTSUPP] =          _string_syserrmsgs + 2117,
+       [EPFNOSUPPORT] =        _string_syserrmsgs + 2141,
+       [EAFNOSUPPORT] =        _string_syserrmsgs + 2171,
+       [EADDRINUSE] =          _string_syserrmsgs + 2212,
+       [EADDRNOTAVAIL] =       _string_syserrmsgs + 2235,
+       [ENETDOWN] =            _string_syserrmsgs + 2267,
+       [ENETUNREACH] =         _string_syserrmsgs + 2283,
+       [ENETRESET] =           _string_syserrmsgs + 2306,
+       [ECONNABORTED] =        _string_syserrmsgs + 2342,
+       [ECONNRESET] =          _string_syserrmsgs + 2375,
+       [ENOBUFS] =                     _string_syserrmsgs + 2400,
+       [EISCONN] =                     _string_syserrmsgs + 2426,
+       [ENOTCONN] =            _string_syserrmsgs + 2466,
+       [ESHUTDOWN] =           _string_syserrmsgs + 2502,
+       [ETOOMANYREFS] =        _string_syserrmsgs + 2548,
+       [ETIMEDOUT] =           _string_syserrmsgs + 2583,
+       [ECONNREFUSED] =        _string_syserrmsgs + 2604,
+       [EHOSTDOWN] =           _string_syserrmsgs + 2623,
+       [EHOSTUNREACH] =        _string_syserrmsgs + 2636,
+       [EALREADY] =            _string_syserrmsgs + 2653,
+       [EINPROGRESS] =         _string_syserrmsgs + 2683,
+       [ESTALE] =                      _string_syserrmsgs + 2709,
+       [EUCLEAN] =                     _string_syserrmsgs + 2731,
+       [ENOTNAM] =                     _string_syserrmsgs + 2756,
+       [ENAVAIL] =                     _string_syserrmsgs + 2784,
+       [EISNAM] =                      _string_syserrmsgs + 2814,
+       [EREMOTEIO] =           _string_syserrmsgs + 2835,
+       [EDQUOT] =                      _string_syserrmsgs + 2852,
+       [ENOMEDIUM] =           _string_syserrmsgs + 2872,
+       [EMEDIUMTYPE] =         _string_syserrmsgs + 2888,
+
+#if EDEADLOCK != EDEADLK
+       [EDEADLOCK] =           _string_syserrmsgs + 2906,
+#endif
+
+#if EWOULDBLOCK != EAGAIN
+#error EWOULDBLOCK does not equal EAGAIN
+#endif
+
+       /* For now, ignore the other arch-specific errors.  glibc only maps EPROCLIM. */
+
+       /* some other mips errors */
+#ifdef ECANCELED
+#endif
+#ifdef EINIT
+#endif
+#ifdef EREMDEV
+#endif
+
+       /* some other sparc errors */
+#ifdef EPROCLIM
+#endif
+#ifdef ERREMOTE
+#endif
+};
+
+int sys_nerr = sizeof(sys_errlist)/sizeof(sys_errlist[0]);
+
+#endif
+/**********************************************************************/
 #ifdef L_wmemcpy
 #define L_memcpy
 #define Wmemcpy wmemcpy
@@ -467,10 +646,13 @@ int Wmemcmp(const Wvoid *s1, const Wvoid *s2, size_t n)
 
 #ifdef L_strcmp
 
-#ifndef L_wcscmp
-#warning implement strcoll and remove weak alias (or enable for C locale only)
+#ifdef __LOCALE_C_ONLY
+#ifdef L_wcscmp
+weak_alias(wcscmp,wcscoll);
+#else  /* L_wcscmp */
 weak_alias(strcmp,strcoll);
-#endif
+#endif /* L_wcscmp */
+#endif /* __LOCALE_C_ONLY */
 
 int Wstrcmp(register const Wchar *s1, register const Wchar *s2)
 {
@@ -494,23 +676,6 @@ int Wstrcmp(register const Wchar *s1, register const Wchar *s2)
 }
 #endif
 /**********************************************************************/
-#ifdef L_strcoll
-#error implement strcoll and remove weak_alias!!
-
-#if 0
-extern unsigned char *_ctype_collate;
-int strcoll(register const char *s1, const char *s2)
-{
-    int r;
-
-    while (!(r = (_ctype_collate[(int)(*s1++)]-_ctype_collate[(int)(*s2++)])));
-
-    return r;
-}
-#endif
-
-#endif
-/**********************************************************************/
 #ifdef L_wcsncmp
 #define L_strncmp
 #define Wstrncmp wcsncmp
@@ -546,11 +711,6 @@ int Wstrncmp(register const Wchar *s1, register const Wchar *s2, size_t n)
 
 #endif
 /**********************************************************************/
-#ifdef L_strxfrm
-#error implement strxfrm
-/* size_t strxfrm(char *dst, const char *src, size_t len); */
-#endif 
-/**********************************************************************/
 #ifdef L_wmemchr
 #define L_memchr
 #define Wmemchr wmemchr
@@ -921,54 +1081,108 @@ int ffs(int i)
 
 #endif
 /**********************************************************************/
-#ifdef L_wcscasecmp
-#define L_strcasecmp
-#define Wstrcasecmp wcscasecmp
+#if defined(L_strcasecmp) || defined(L_strcasecmp_l) || defined(L_wcscasecmp) || defined(L_wcscasecmp_l)
+
+#if defined(L_wcscasecmp) || defined(L_wcscasecmp_l)
+
+#define strcasecmp wcscasecmp
+#define strcasecmp_l wcscasecmp_l
+#define __strcasecmp_l __wcscasecmp_l
+#ifdef __UCLIBC_DO_XLOCALE
+#define TOLOWER(C) __towlower_l((C), locale_arg)
+#else
+#define TOLOWER(C) towlower((C))
+#endif
+
+#else  /* defined(L_wcscasecmp) || defined(L_wcscasecmp_l) */
+
+#ifdef __UCLIBC_DO_XLOCALE
+#define TOLOWER(C) __tolower_l((C), locale_arg)
 #else
-#define Wstrcasecmp strcasecmp
+#define TOLOWER(C) tolower((C))
 #endif
 
-#ifdef L_strcasecmp
+#endif /* defined(L_wcscasecmp) || defined(L_wcscasecmp_l) */
+
 
-int Wstrcasecmp(register const Wchar *s1, register const Wchar *s2)
+#if defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE)
+
+int strcasecmp(register const Wchar *s1, register const Wchar *s2)
+{
+       return __strcasecmp_l(s1, s2, __UCLIBC_CURLOCALE);
+}
+
+#else  /* defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) */
+
+int __XL(strcasecmp)(register const Wchar *s1, register const Wchar *s2
+                                         __LOCALE_PARAM )
 {
 #ifdef WANT_WIDE
-       while ((*s1 == *s2) || (towlower(*s1) == towlower(*s2))) {
+       while ((*s1 == *s2) || (TOLOWER(*s1) == TOLOWER(*s2))) {
                if (!*s1++) {
                        return 0;
                }
                ++s2;
        }
 
-       return (((Wuchar)towlower(*s1)) < ((Wuchar)towlower(*s2))) ? -1 : 1;
+       return (((Wuchar)TOLOWER(*s1)) < ((Wuchar)TOLOWER(*s2))) ? -1 : 1;
        /* TODO -- should wide cmp funcs do wchar or Wuchar compares? */
 #else
        int r = 0;
 
        while ( ((s1 == s2) ||
-                        !(r = ((int)( tolower(*((Wuchar *)s1))))
-                          - tolower(*((Wuchar *)s2))))
+                        !(r = ((int)( TOLOWER(*((Wuchar *)s1))))
+                          - TOLOWER(*((Wuchar *)s2))))
                        && (++s2, *s1++));
 
        return r;
 #endif
 }
 
+__XL_ALIAS(strcasecmp)
+
+#endif /* defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) */
+
 #endif
 /**********************************************************************/
-#ifdef L_wcsncasecmp
-#define L_strncasecmp
-#define Wstrncasecmp wcsncasecmp
+#if defined(L_strncasecmp) || defined(L_strncasecmp_l) || defined(L_wcsncasecmp) || defined(L_wcsncasecmp_l)
+
+#if defined(L_wcsncasecmp) || defined(L_wcsncasecmp_l)
+
+#define strncasecmp wcsncasecmp
+#define strncasecmp_l wcsncasecmp_l
+#define __strncasecmp_l __wcsncasecmp_l
+#ifdef __UCLIBC_DO_XLOCALE
+#define TOLOWER(C) __towlower_l((C), locale_arg)
 #else
-#define Wstrncasecmp strncasecmp
+#define TOLOWER(C) towlower((C))
 #endif
 
-#ifdef L_strncasecmp
+#else  /* defined(L_wcsncasecmp) || defined(L_wcsncasecmp_l) */
+
+#ifdef __UCLIBC_DO_XLOCALE
+#define TOLOWER(C) __tolower_l((C), locale_arg)
+#else
+#define TOLOWER(C) tolower((C))
+#endif
+
+#endif /* defined(L_wcsncasecmp) || defined(L_wcsncasecmp_l) */
+
+
+#if defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE)
+
+int strncasecmp(register const Wchar *s1, register const Wchar *s2, size_t n)
+{
+       return __strncasecmp_l(s1, s2, n, __UCLIBC_CURLOCALE);
+}
+
+#else  /* defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) */
 
-int Wstrncasecmp(register const Wchar *s1, register const Wchar *s2, size_t n)
+int __XL(strncasecmp)(register const Wchar *s1, register const Wchar *s2,
+                                         size_t n   __LOCALE_PARAM )
 {
 #ifdef WANT_WIDE
-       while (n && ((*s1 == *s2) || (towlower(*s1) == towlower(*s2)))) {
+       while (n && ((*s1 == *s2) || (TOLOWER(*s1) == TOLOWER(*s2)))) {
                if (!*s1++) {
                        return 0;
                }
@@ -978,19 +1192,24 @@ int Wstrncasecmp(register const Wchar *s1, register const Wchar *s2, size_t n)
 
        return (n == 0)
                ? 0
-               : ((((Wuchar)towlower(*s1)) < ((Wuchar)towlower(*s2))) ? -1 : 1);
+               : ((((Wuchar)TOLOWER(*s1)) < ((Wuchar)TOLOWER(*s2))) ? -1 : 1);
        /* TODO -- should wide cmp funcs do wchar or Wuchar compares? */
 #else
        int r = 0;
 
        while ( n
                        && ((s1 == s2) ||
-                               !(r = ((int)( tolower(*((unsigned char *)s1))))
-                                 - tolower(*((unsigned char *)s2))))
+                               !(r = ((int)( TOLOWER(*((unsigned char *)s1))))
+                                 - TOLOWER(*((unsigned char *)s2))))
                        && (--n, ++s2, *s1++));
        return r;
 #endif
 }
+
+__XL_ALIAS(strncasecmp)
+
+#endif /* defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) */
+
 #endif
 /**********************************************************************/
 #ifdef L_wcsnlen
@@ -1071,7 +1290,7 @@ Wchar *Wstrdup(register const Wchar *s1)
 
 char *strerror(int errnum)
 {
-    static char buf[_SYS_ERRMSG_MAXLEN];
+    static char buf[_STRERROR_BUFSIZE];
 
        _susv3_strerror_r(errnum, buf, sizeof(buf));
 
@@ -1084,6 +1303,7 @@ char *strerror(int errnum)
 /**********************************************************************/
 #ifdef L__susv3_strerror_r
 
+#ifdef __UCLIBC_HAS_ERRNO_MESSAGES__
 #if defined(__alpha__) || defined(__mips__) || defined(__sparc__)
 
 static const unsigned char estridx[] = {
@@ -1223,19 +1443,20 @@ static const unsigned char estridx[] = {
 
 #endif
 
-
 int _susv3_strerror_r(int errnum, char *strerrbuf, size_t buflen)
 {
     register char *s;
     int i, retval;
-    char buf[_SYS_ERRMSG_MAXLEN];
-    static const char unknown[14] = {
+    char buf[_STRERROR_BUFSIZE];
+    static const char unknown[] = {
                'U', 'n', 'k', 'n', 'o', 'w', 'n', ' ', 'e', 'r', 'r', 'o', 'r', ' '
     };
 
     retval = EINVAL;
 
 
+#ifdef __UCLIBC_HAS_ERRNO_MESSAGES__
+
 #if defined(__alpha__) || defined(__mips__) || defined(__sparc__)
        /* Need to translate errno to string index. */
        for (i = 0 ; i < sizeof(estridx)/sizeof(estridx[0]) ; i++) {
@@ -1271,6 +1492,8 @@ int _susv3_strerror_r(int errnum, char *strerrbuf, size_t buflen)
                }
     }
 
+#endif /* __UCLIBC_HAS_ERRNO_MESSAGES__ */
+
     s = _int10tostr(buf+sizeof(buf)-1, errnum) - sizeof(unknown);
     memcpy(s, unknown, sizeof(unknown));
 
@@ -1296,6 +1519,45 @@ int _susv3_strerror_r(int errnum, char *strerrbuf, size_t buflen)
     return retval;
 }
 
+#else  /* __UCLIBC_HAS_ERRNO_MESSAGES__ */
+
+int _susv3_strerror_r(int errnum, char *strerrbuf, size_t buflen)
+{
+    register char *s;
+    int i, retval;
+    char buf[_STRERROR_BUFSIZE];
+    static const char unknown[] = {
+               'U', 'n', 'k', 'n', 'o', 'w', 'n', ' ', 'e', 'r', 'r', 'o', 'r', ' '
+    };
+
+    s = _int10tostr(buf+sizeof(buf)-1, errnum) - sizeof(unknown);
+    memcpy(s, unknown, sizeof(unknown));
+
+    if (!strerrbuf) {          /* SUSv3  */
+               buflen = 0;
+    }
+
+    retval = EINVAL;
+
+       i = buf + sizeof(buf) - s;
+
+    if (i > buflen) {
+               i = buflen;
+               retval = ERANGE;
+    }
+
+    if (i) {
+               memcpy(strerrbuf, s, i);
+               strerrbuf[i-1] = 0;     /* In case buf was too small. */
+    }
+
+       __set_errno(retval);
+
+    return retval;
+}
+
+#endif /* __UCLIBC_HAS_ERRNO_MESSAGES__ */
+
 #endif
 /**********************************************************************/
 /* GNU extension functions. */
@@ -1313,6 +1575,40 @@ char *_glibc_strerror_r(int errnum, char *strerrbuf, size_t buflen)
 
 #endif
 /**********************************************************************/
+#ifdef L_memmem
+
+void *memmem(const void *haystack, size_t haystacklen,
+                    const void *needle, size_t needlelen)
+{
+       register const char *ph;
+       register const char *pn;
+       const char *plast;
+       size_t n;
+
+       if (needlelen == 0) {
+               return (void *) haystack;
+       }
+
+       if (haystacklen >= needlelen) {
+               ph = (const char *) haystack;
+               pn = (const char *) needle;
+               plast = ph + (haystacklen - needlelen);
+
+               do {
+                       n = 0;
+                       while (ph[n] == pn[n]) {
+                               if (++n == needlelen) {
+                                       return (void *) ph;
+                               }
+                       }
+               } while (++ph <= plast);
+       }
+
+       return NULL;
+}
+
+#endif
+/**********************************************************************/
 #ifdef L_wmempcpy
 #define L_mempcpy
 #define Wmempcpy wmempcpy
@@ -1590,14 +1886,19 @@ char *strsep(char ** __restrict s1, const char * __restrict s2)
 /**********************************************************************/
 #ifdef L_wcschrnul
 #define L_strchrnul
+#define __Wstrchrnul __wcschrnul
 #define Wstrchrnul wcschrnul
 #else
+#define __Wstrchrnul __strchrnul
 #define Wstrchrnul strchrnul
 #endif
 
 #ifdef L_strchrnul
 
-Wchar *Wstrchrnul(register const Wchar *s, Wint c)
+extern Wchar *__Wstrchrnul(register const Wchar *s, Wint c);
+weak_alias(__Wstrchrnul, Wstrchrnul);
+
+Wchar *__Wstrchrnul(register const Wchar *s, Wint c)
 {
        --s;
        while (*++s && (*s != ((Wchar)c)));
@@ -1751,18 +2052,40 @@ size_t strlcat(register char *__restrict dst,
 
 #endif
 /**********************************************************************/
+#ifdef WANT_WIDE
+extern size_t __wcslcpy(wchar_t *__restrict dst,
+                                               const wchar_t *__restrict src,
+                                               size_t n);
+#endif
+
+
+#ifdef L___wcslcpy
+#define L_strlcpy
+#define Wstrlcpy __wcslcpy
+#ifdef __LOCALE_C_ONLY
+weak_alias(__wcslcpy,wcsxfrm);
+#endif
+#endif
+
 #ifdef L_strlcpy
 
+#ifndef L___wcslcpy
+#define Wstrlcpy strlcpy
+#ifdef __LOCALE_C_ONLY
+weak_alias(strlcpy,strxfrm);
+#endif
+#endif
+
 /* OpenBSD function:
  * Copy at most n-1 chars from src to dst and nul-terminate dst.
  * Returns strlen(src), so truncation occurred if the return value is >= n. */
 
-size_t strlcpy(register char *__restrict dst,
-                          register const char *__restrict src,
-                          size_t n)
+size_t Wstrlcpy(register Wchar *__restrict dst,
+                                 register const Wchar *__restrict src,
+                                 size_t n)
 {
-       const char *src0 = src;
-       char dummy[1];
+       const Wchar *src0 = src;
+       Wchar dummy[1];
 
        if (!n) {
                dst = dummy;
@@ -1783,7 +2106,7 @@ size_t strlcpy(register char *__restrict dst,
 
 #endif
 /**********************************************************************/
-#ifdef L__string_syssigmsgs
+#if defined(L__string_syssigmsgs) && defined(__UCLIBC_HAS_SIGNUM_MESSAGES__)
 
 const char _string_syssigmsgs[] = {
        /*   0:    0,  1 */ "\0"
@@ -1823,7 +2146,7 @@ const char _string_syssigmsgs[] = {
 #endif
 
 /**********************************************************************/
-#ifdef L_sys_siglist
+#if defined(L_sys_siglist) && defined(__UCLIBC_HAS_SYS_SIGLIST__)
 
 const char *const sys_siglist[_NSIG] = {
        NULL,
@@ -1898,12 +2221,14 @@ const char *const sys_siglist[_NSIG] = {
 
 /* TODO: make a threadsafe version? */
 
+#ifdef __UCLIBC_HAS_SIGNUM_MESSAGES__
+
 char *strsignal(int signum)
 {
     register char *s;
     int i;
     static char buf[_STRSIGNAL_BUFSIZE];
-    static const char unknown[15] = {
+    static const char unknown[] = {
                'U', 'n', 'k', 'n', 'o', 'w', 'n', ' ', 's', 'i', 'g', 'n', 'a', 'l', ' '
     };
 
@@ -1929,6 +2254,22 @@ char *strsignal(int signum)
        return s;
 }
 
+#else  /* __UCLIBC_HAS_SIGNUM_MESSAGES__ */
+
+char *strsignal(int signum)
+{
+    static char buf[_STRSIGNAL_BUFSIZE];
+    static const char unknown[] = {
+               'U', 'n', 'k', 'n', 'o', 'w', 'n', ' ', 's', 'i', 'g', 'n', 'a', 'l', ' '
+    };
+
+    return (char *) memcpy(_int10tostr(buf+sizeof(buf)-1, signum)
+                                                  - sizeof(unknown),
+                                                  unknown, sizeof(unknown));
+}
+
+#endif /* __UCLIBC_HAS_SIGNUM_MESSAGES__ */
+
 #endif
 /**********************************************************************/
 #ifdef L_psignal
@@ -1960,3 +2301,650 @@ void psignal(int signum, register const char *message)
 
 #endif
 /**********************************************************************/
+#ifndef __LOCALE_C_ONLY
+#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 __wcscoll_l __strcoll_l
+#define wcsxfrm   strxfrm
+#define wcsxfrm_l strxfrm_l
+#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 );
+}
+
+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_DATA.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(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  /* WANT_WIDE */
+               return strcmp(s0, s1);
+#endif /* WANT_WIDE */
+       }
+
+       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;
+}
+
+__XL_ALIAS(wcscoll)
+
+#ifdef WANT_WIDE
+
+size_t __XL(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;
+}
+
+__XL_ALIAS(wcsxfrm)
+
+#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(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;
+}
+
+__XL_ALIAS(strxfrm)
+
+#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 /* __LOCALE_C_ONLY */
+/**********************************************************************/