1 /*
   2  * Copyright 2013 Garrett D'Amore <garrett@damore.org>
   3  * Copyright 2010 Nexenta Systems, Inc.  All rights reserved.
   4  * Copyright (c) 2002 Tim J. Robbins
   5  * All rights reserved.
   6  *
   7  * Redistribution and use in source and binary forms, with or without
   8  * modification, are permitted provided that the following conditions
   9  * are met:
  10  * 1. Redistributions of source code must retain the above copyright
  11  *    notice, this list of conditions and the following disclaimer.
  12  * 2. Redistributions in binary form must reproduce the above copyright
  13  *    notice, this list of conditions and the following disclaimer in the
  14  *    documentation and/or other materials provided with the distribution.
  15  *
  16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
  17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
  20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  26  * SUCH DAMAGE.
  27  */
  28 
  29 #include "lint.h"
  30 #include <errno.h>
  31 #include <stdlib.h>
  32 #include <string.h>
  33 #include <wchar.h>
  34 #include <assert.h>
  35 #include "collate.h"
  36 #include "localeimpl.h"
  37 
  38 int
  39 wcscoll_l(const wchar_t *ws1, const wchar_t *ws2, locale_t loc)
  40 {
  41         int len1, len2, pri1, pri2, ret;
  42         wchar_t *tr1 = NULL, *tr2 = NULL;
  43         int direc, pass;
  44         const struct lc_collate *lcc = loc->collate;
  45 
  46         if (lcc->lc_is_posix)
  47                 /*
  48                  * Locale has no special collating order or could not be
  49                  * loaded, do a fast binary comparison.
  50                  */
  51                 return (wcscmp(ws1, ws2));
  52 
  53         ret = 0;
  54 
  55         /*
  56          * Once upon a time we had code to try to optimize this, but
  57          * it turns out that you really can't make many assumptions
  58          * safely.  You absolutely have to run this pass by pass,
  59          * because some passes will be ignored for a given character,
  60          * while others will not.  Simpler locales will benefit from
  61          * having fewer passes, and most comparisions should resolve
  62          * during the primary pass anyway.
  63          *
  64          * Note that we do one final extra pass at the end to pick
  65          * up UNDEFINED elements.  There is special handling for them.
  66          */
  67         for (pass = 0; pass <= lcc->lc_directive_count; pass++) {
  68 
  69                 const int32_t *st1 = NULL;
  70                 const int32_t *st2 = NULL;
  71                 const wchar_t   *w1 = ws1;
  72                 const wchar_t   *w2 = ws2;
  73 
  74                 /* special pass for UNDEFINED */
  75                 if (pass == lcc->lc_directive_count) {
  76                         direc = DIRECTIVE_FORWARD | DIRECTIVE_UNDEFINED;
  77                 } else {
  78                         direc = lcc->lc_directive[pass];
  79                 }
  80 
  81                 if (direc & DIRECTIVE_BACKWARD) {
  82                         wchar_t *bp, *fp, c;
  83                         if ((tr1 = wcsdup(w1)) == NULL)
  84                                 goto fail;
  85                         bp = tr1;
  86                         fp = tr1 + wcslen(tr1) - 1;
  87                         while (bp < fp) {
  88                                 c = *bp;
  89                                 *bp++ = *fp;
  90                                 *fp-- = c;
  91                         }
  92                         if ((tr2 = wcsdup(w2)) == NULL)
  93                                 goto fail;
  94                         bp = tr2;
  95                         fp = tr2 + wcslen(tr2) - 1;
  96                         while (bp < fp) {
  97                                 c = *bp;
  98                                 *bp++ = *fp;
  99                                 *fp-- = c;
 100                         }
 101                         w1 = tr1;
 102                         w2 = tr2;
 103                 }
 104 
 105                 if (direc & DIRECTIVE_POSITION) {
 106                         while ((*w1 || st1) && (*w2 || st2)) {
 107                                 pri1 = pri2 = 0;
 108                                 _collate_lookup(lcc, w1, &len1, &pri1, pass,
 109                                     &st1);
 110                                 if (pri1 <= 0) {
 111                                         if (pri1 < 0) {
 112                                                 errno = EINVAL;
 113                                                 goto fail;
 114                                         }
 115                                         pri1 = COLLATE_MAX_PRIORITY;
 116                                 }
 117                                 _collate_lookup(lcc, w2, &len2, &pri2, pass,
 118                                     &st2);
 119                                 if (pri2 <= 0) {
 120                                         if (pri2 < 0) {
 121                                                 errno = EINVAL;
 122                                                 goto fail;
 123                                         }
 124                                         pri2 = COLLATE_MAX_PRIORITY;
 125                                 }
 126                                 if (pri1 != pri2) {
 127                                         ret = pri1 - pri2;
 128                                         goto end;
 129                                 }
 130                                 w1 += len1;
 131                                 w2 += len2;
 132                         }
 133                 } else {
 134                         while ((*w1 || st1) && (*w2 || st2)) {
 135                                 pri1 = pri2 = 0;
 136                                 while (*w1) {
 137                                         _collate_lookup(lcc, w1, &len1,
 138                                             &pri1, pass, &st1);
 139                                         if (pri1 > 0)
 140                                                 break;
 141                                         if (pri1 < 0) {
 142                                                 errno = EINVAL;
 143                                                 goto fail;
 144                                         }
 145                                         w1 += len1;
 146                                 }
 147                                 while (*w2) {
 148                                         _collate_lookup(lcc, w2, &len2,
 149                                             &pri2, pass, &st2);
 150                                         if (pri2 > 0)
 151                                                 break;
 152                                         if (pri2 < 0) {
 153                                                 errno = EINVAL;
 154                                                 goto fail;
 155                                         }
 156                                         w2 += len2;
 157                                 }
 158                                 if (!pri1 || !pri2)
 159                                         break;
 160                                 if (pri1 != pri2) {
 161                                         ret = pri1 - pri2;
 162                                         goto end;
 163                                 }
 164                                 w1 += len1;
 165                                 w2 += len2;
 166                         }
 167                 }
 168                 if (!*w1) {
 169                         if (*w2) {
 170                                 ret = -(int)*w2;
 171                                 goto end;
 172                         }
 173                 } else {
 174                         ret = *w1;
 175                         goto end;
 176                 }
 177         }
 178         ret = 0;
 179 
 180 end:
 181         if (tr1)
 182                 free(tr1);
 183         if (tr2)
 184                 free(tr2);
 185 
 186         return (ret);
 187 
 188 fail:
 189         ret = wcscmp(ws1, ws2);
 190         goto end;
 191 }
 192 
 193 
 194 int
 195 wcscoll(const wchar_t *ws1, const wchar_t *ws2)
 196 {
 197         return (wcscoll_l(ws1, ws2, uselocale(NULL)));
 198 }