On Tue, Apr 09, 2013 at 12:25:23PM -0500, Vladimir Támara Patiño wrote:
> Although the behavior of strcasecmp is unsepecified for multibyte
> encodings (Is that right?)
> http://pubs.opengroup.org/onlinepubs/9699919799/
> 
> I wish the attached test (encoded in UTF-8) would pass,
> so I'm also attaching a patch for strcasecmp and strncasecmp that makes
> this test pass, it uses only LC_CTYPE (but not LC_COLLATE) and a
> simple numeric comparision when the strings are different (as the
> previous version was doing).
> 
> -- 
> Dios, gracias por tu amor infinito.
> --   Vladimir Támara Patiño.  http://vtamara.pasosdeJesus.org/
>  http://www.pasosdejesus.org/dominio_publico_colombia.html
> 

> #include <locale.h>
> #include <stdio.h>
> #include <string.h>
> 
> int main()
> {
>       char *nl = setlocale(LC_ALL, "es_CO.UTF-8");
>       if (strcasecmp("ñ", "Ñ") == 0) {
>               printf("OK");
>       } else {
>               printf("Error");
>       }
>       
>       return 0;
> }

> --- src53/lib/libc/string/strcasecmp.c        Mon Mar 25 18:28:29 2013
> +++ src/lib/libc/string/strcasecmp.c  Tue Apr  9 11:34:44 2013
> @@ -30,6 +30,8 @@
>   */
>  
>  #include <string.h>
> +#include <wchar.h>
> +#include <wctype.h>
>  
>  typedef unsigned char u_char;
>  
> @@ -76,29 +78,36 @@
>  int
>  strcasecmp(const char *s1, const char *s2)
>  {
> -     const u_char *cm = charmap;
> -     const u_char *us1 = (const u_char *)s1;
> -     const u_char *us2 = (const u_char *)s2;
> -
> -     while (cm[*us1] == cm[*us2++])
> -             if (*us1++ == '\0')
> -                     return (0);
> -     return (cm[*us1] - cm[*--us2]);
> +     return strncasecmp(s1, s2, strlen(s1) + 1);
>  }
>  
> +/** Uses LC_CTYPE but not LC_COLLATE */
>  int
>  strncasecmp(const char *s1, const char *s2, size_t n)
>  {
> +     mbstate_t mb1, mb2;

Please put a blank line between declarations and code.

> +     bzero(&mb1, sizeof(mb1));
> +     bzero(&mb2, sizeof(mb2));
> +     mbsinit(&mb1);
> +     mbsinit(&mb2);

These mbsinit() calls make no sense. mbsinit does not initialize anything.
See the mbsinit man page. The bzero() calls are sufficient.

> +
>       if (n != 0) {
> -             const u_char *cm = charmap;
>               const u_char *us1 = (const u_char *)s1;
>               const u_char *us2 = (const u_char *)s2;
> +             size_t lus1 = strlen(us1);
> +             size_t lus2 = strlen(us2);
>  
>               do {
> -                     if (cm[*us1] != cm[*us2++])
> -                             return (cm[*us1] - cm[*--us2]);
> -                     if (*us1++ == '\0')
> +                     wchar_t w1, w2, l1, l2;
> +                     size_t d1 = mbrtowc(&w1, us1, lus1, &mb1);
> +                     size_t d2 = mbrtowc(&w2, us2, lus2, &mb2);

How are you going to handle errors from mbrtowc()?

It seems strncasecmp() cannot return errors according to POSIX.
Only the _l variants have errors defined.

> +                     if ((l1 = towlower(w1)) != (l2 = towlower(w2))) {

Here, you could be comparing (size_t)-1 to (size_t)-2, for example.
Should this code fall back to byte-to-byte comparison if mbrtowc() fails?

While I recognize that POSIX mandates the current locale to be used for
case conversion, I'm not sure this change is worth the extra complexity.
As you point out, the standard seems to throw its hands up in the air
when the question about conversion errors with strcasecmp() and strncasecmp()
is raised, possibly because these functions are older than the locale concept.

I would say leave these functions as they are, and rely on applications
to use wide characters or the _l variants of these functions instead.


> +                             return l1 - l2;
> +                     }
> +                     if (*us1 == '\0')
>                               break;
> +                     us2 += d2;
> +                     us1 += d1;
>               } while (--n != 0);
>       }
>       return (0);

Reply via email to