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