Make strcasecmp() work right in multibyte locales.
[pspp] / lib / strcasecmp.c
1 /* Case-insensitive string comparison function.
2    Copyright (C) 1998, 1999, 2005 Free Software Foundation, Inc.
3    Written by Bruno Haible <bruno@clisp.org>, 2005,
4    based on earlier glibc code.
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2, or (at your option)
9    any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software Foundation,
18    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
19
20 #ifdef HAVE_CONFIG_H
21 # include <config.h>
22 #endif
23
24 /* Specification.  */
25 #include "strcase.h"
26
27 #include <ctype.h>
28
29 #if HAVE_MBRTOWC
30
31 #include "strnlen1.h"
32
33 /* Like mbiter.h, except it doesn't look at the entire string.  */
34
35 #include "mbchar.h"
36
37 #include <assert.h>
38 #include <stdbool.h>
39 #include <stdlib.h>
40 #include <wchar.h>
41 #include <wctype.h>
42
43 struct mbiter_multi
44 {
45   bool at_end;          /* true if the end of the string has been reached */
46   bool in_shift;        /* true if next byte may not be interpreted as ASCII */
47   mbstate_t state;      /* if in_shift: current shift state */
48   bool next_done;       /* true if mbi_avail has already filled the following */
49   struct mbchar cur;    /* the current character:
50         const char *cur.ptr             pointer to current character
51         The following are only valid after mbi_avail.
52         size_t cur.bytes                number of bytes of current character
53         bool cur.wc_valid               true if wc is a valid wide character
54         wchar_t cur.wc                  if wc_valid: the current character
55         */
56 };
57
58 static inline void
59 mbiter_multi_next (struct mbiter_multi *iter)
60 {
61   if (iter->next_done)
62     return;
63   if (iter->in_shift)
64     goto with_shift;
65   /* Handle most ASCII characters quickly, without calling mbrtowc().  */
66   if (is_basic (*iter->cur.ptr))
67     {
68       /* These characters are part of the basic character set.  ISO C 99
69          guarantees that their wide character code is identical to their
70          char code.  */
71       iter->cur.bytes = 1;
72       iter->cur.wc = *iter->cur.ptr;
73       iter->cur.wc_valid = true;
74     }
75   else
76     {
77       assert (mbsinit (&iter->state));
78       iter->in_shift = true;
79     with_shift:
80       iter->cur.bytes = mbrtowc (&iter->cur.wc, iter->cur.ptr,
81                                  strnlen1 (iter->cur.ptr, MB_CUR_MAX),
82                                  &iter->state);
83       if (iter->cur.bytes == (size_t) -1)
84         {
85           /* An invalid multibyte sequence was encountered.  */
86           iter->cur.bytes = 1;
87           iter->cur.wc_valid = false;
88           /* Whether to set iter->in_shift = false and reset iter->state
89              or not is not very important; the string is bogus anyway.  */
90         }
91       else if (iter->cur.bytes == (size_t) -2)
92         {
93           /* An incomplete multibyte character at the end.  */
94           iter->cur.bytes = strlen (iter->cur.ptr) + 1;
95           iter->cur.wc_valid = false;
96           /* Whether to set iter->in_shift = false and reset iter->state
97              or not is not important; the string end is reached anyway.  */
98         }
99       else
100         {
101           if (iter->cur.bytes == 0)
102             {
103               /* A null wide character was encountered.  */
104               iter->cur.bytes = 1;
105               assert (*iter->cur.ptr == '\0');
106               assert (iter->cur.wc == 0);
107             }
108           iter->cur.wc_valid = true;
109
110           /* When in the initial state, we can go back treating ASCII
111              characters more quickly.  */
112           if (mbsinit (&iter->state))
113             iter->in_shift = false;
114         }
115     }
116   iter->next_done = true;
117 }
118
119 static inline void
120 mbiter_multi_reloc (struct mbiter_multi *iter, ptrdiff_t ptrdiff)
121 {
122   iter->cur.ptr += ptrdiff;
123 }
124
125 /* Iteration macros.  */
126 typedef struct mbiter_multi mbi_iterator_t;
127 #define mbi_init(iter, startptr) \
128   ((iter).cur.ptr = (startptr), (iter).at_end = false, \
129    (iter).in_shift = false, memset (&(iter).state, '\0', sizeof (mbstate_t)), \
130    (iter).next_done = false)
131 #define mbi_avail(iter) \
132   (!(iter).at_end && (mbiter_multi_next (&(iter)), true))
133 #define mbi_advance(iter) \
134   ((mb_isnul ((iter).cur) ? ((iter).at_end = true) : 0), \
135    (iter).cur.ptr += (iter).cur.bytes, (iter).next_done = false)
136
137 /* Access to the current character.  */
138 #define mbi_cur(iter) (iter).cur
139 #define mbi_cur_ptr(iter) (iter).cur.ptr
140
141 #endif
142
143 #define TOLOWER(Ch) (isupper (Ch) ? tolower (Ch) : (Ch))
144
145 /* Compare strings S1 and S2, ignoring case, returning less than, equal to or
146    greater than zero if S1 is lexicographically less than, equal to or greater
147    than S2.
148    Note: This function may, in multibyte locales, return 0 for strings of
149    different lengths!  */
150 int
151 strcasecmp (const char *s1, const char *s2)
152 {
153   if (s1 == s2)
154     return 0;
155
156   /* Be careful not to look at the entire extent of s1 or s2 until needed.
157      This is useful because when two strings differ, the difference is
158      most often already in the very few first characters.  */
159 #if HAVE_MBRTOWC
160   if (MB_CUR_MAX > 1)
161     {
162       mbi_iterator_t iter1;
163       mbi_iterator_t iter2;
164
165       mbi_init (iter1, s1);
166       mbi_init (iter2, s2);
167
168       while (mbi_avail (iter1) && mbi_avail (iter2))
169         {
170           /* Sort invalid characters after all valid ones.  */
171           if (!mbi_cur (iter1).wc_valid)
172             {
173               if (!mbi_cur (iter2).wc_valid)
174                 {
175                   /* Compare two invalid characters.  */
176                   int cmp;
177
178                   if (mbi_cur (iter1).bytes > mbi_cur (iter2).bytes)
179                     return 1;
180                   if (mbi_cur (iter1).bytes < mbi_cur (iter2).bytes)
181                     return -1;
182                   cmp = memcmp (mbi_cur_ptr (iter1), mbi_cur_ptr (iter2),
183                                 mbi_cur (iter1).bytes);
184                   if (cmp != 0)
185                     return cmp;
186                 }
187               else
188                 /* mbi_cur (iter1) invalid, mbi_cur (iter2) valid.  */
189                 return 1;
190             }
191           else
192             {
193               if (!mbi_cur (iter2).wc_valid)
194                 /* mbi_cur (iter1) valid, mbi_cur (iter2) invalid.  */
195                 return -1;
196               else
197                 {
198                   /* Compare two valid characters.  */
199                   wchar_t c1 = towlower (mbi_cur (iter1).wc);
200                   wchar_t c2 = towlower (mbi_cur (iter2).wc);
201
202                   if (c1 > c2)
203                     return 1;
204                   if (c1 < c2)
205                     return -1;
206                 }
207             }
208           mbi_advance (iter1);
209           mbi_advance (iter2);
210         }
211       if (mbi_avail (iter1))
212         /* s2 terminated before s1.  */
213         return 1;
214       if (mbi_avail (iter2))
215         /* s1 terminated before s2.  */
216         return -1;
217       return 0;
218     }
219   else
220 #endif
221     {
222       const unsigned char *p1 = (const unsigned char *) s1;
223       const unsigned char *p2 = (const unsigned char *) s2;
224       unsigned char c1, c2;
225
226       do
227         {
228           c1 = TOLOWER (*p1);
229           c2 = TOLOWER (*p2);
230
231           if (c1 == '\0')
232             break;
233
234           ++p1;
235           ++p2;
236         }
237       while (c1 == c2);
238
239       return c1 - c2;
240     }
241 }