update nearly all FSF copyright year lists to include 2010
[gnulib.git] / lib / mbmemcasecoll.c
1 /* Locale-specific case-ignoring memory comparison.
2    Copyright (C) 2001, 2009, 2010 Free Software Foundation, Inc.
3    Written by Bruno Haible <bruno@clisp.org>, 2001.
4
5    This program is free software: you can redistribute it and/or modify it
6    under the terms of the GNU Lesser General Public License as published
7    by the Free Software Foundation; either version 3 of the License, or
8    (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14
15    You should have received a copy of the GNU Lesser General Public License
16    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
17
18 #include <config.h>
19
20 /* Specification.  */
21 #include "mbmemcasecoll.h"
22
23 #include <errno.h>
24 #include <stdlib.h>
25 #include <string.h>
26
27 /* Get tolower().  */
28 #include <ctype.h>
29
30 /* Get mbstate_t, mbrtowc(), wcrtomb().  */
31 #include <wchar.h>
32
33 /* Get towlower().  */
34 #include <wctype.h>
35
36 #include "malloca.h"
37 #include "memcmp2.h"
38 #include "memcoll.h"
39
40 #define TOLOWER(Ch) (isupper (Ch) ? tolower (Ch) : (Ch))
41
42 /* Apply towlower() to the multibyte character sequence in INBUF, storing the
43    result as a multibyte character sequence in OUTBUF.  */
44 static size_t
45 apply_towlower (const char *inbuf, size_t inbufsize,
46                 char *outbuf, size_t outbufsize)
47 {
48   char *outbuf_orig = outbuf;
49   size_t remaining;
50
51   remaining = inbufsize;
52   while (remaining > 0)
53     {
54       wchar_t wc1;
55       size_t n1;
56       mbstate_t state;
57
58       memset (&state, '\0', sizeof (mbstate_t));
59       n1 = mbrtowc (&wc1, inbuf, remaining, &state);
60       if (n1 == (size_t)(-2))
61         break;
62       if (n1 != (size_t)(-1))
63         {
64           wint_t wc2 = towlower (wc1);
65
66           if (wc2 != wc1)
67             {
68               size_t n2;
69
70               memset (&state, '\0', sizeof (mbstate_t));
71               n2 = wcrtomb (outbuf, wc2, &state);
72               if (n2 != (size_t)(-1))
73                 {
74                   /* Store the translated multibyte character.  */
75                   inbuf += n1;
76                   remaining -= n1;
77                   outbuf += n2;
78                   continue;
79                 }
80             }
81
82           /* Nothing to translate. */
83           memcpy (outbuf, inbuf, n1);
84           inbuf += n1;
85           remaining -= n1;
86           outbuf += n1;
87           continue;
88         }
89
90       /* Invalid multibyte character on input.
91          Copy one byte without modification.  */
92       *outbuf++ = *inbuf++;
93       remaining -= 1;
94     }
95   /* Incomplete multibyte sequence on input.
96      Pass it through unmodified.  */
97   while (remaining > 0)
98     {
99       *outbuf++ = *inbuf++;
100       remaining -= 1;
101     }
102
103   /* Verify the output buffer was large enough.  */
104   if (outbuf - outbuf_orig > outbufsize)
105     abort ();
106
107   /* Return the number of written output bytes.  */
108   return outbuf - outbuf_orig;
109 }
110
111 /* Apply tolower() to the unibyte character sequence in INBUF, storing the
112    result as a unibyte character sequence in OUTBUF.  */
113 static void
114 apply_tolower (const char *inbuf, char *outbuf, size_t bufsize)
115 {
116   for (; bufsize > 0; bufsize--)
117     {
118       *outbuf = TOLOWER ((unsigned char) *inbuf);
119       inbuf++;
120       outbuf++;
121     }
122 }
123
124 int
125 mbmemcasecoll (const char *s1, size_t s1len, const char *s2, size_t s2len,
126                bool hard_LC_COLLATE)
127 {
128   char *t1;
129   size_t t1len;
130   char *t2;
131   size_t t2len;
132   char *memory;
133   int cmp;
134
135   if (MB_CUR_MAX > 1)
136     {
137       /* Application of towlower grows each character by a factor 2
138          at most.  */
139       t1len = 2 * s1len;
140       t2len = 2 * s2len;
141     }
142   else
143     {
144       /* Application of tolower doesn't change the size.  */
145       t1len = s1len;
146       t2len = s2len;
147     }
148   /* Allocate memory for t1 and t2.  */
149   memory = (char *) malloca (t1len + 1 + t2len + 1);
150   if (memory == NULL)
151     {
152       errno = ENOMEM;
153       return 0;
154     }
155   t1 = memory;
156   t2 = memory + t1len + 1;
157
158   /* Csae-fold the two argument strings.  */
159   if (MB_CUR_MAX > 1)
160     {
161       t1len = apply_towlower (s1, s1len, t1, t1len);
162       t2len = apply_towlower (s2, s2len, t2, t2len);
163     }
164   else
165     {
166       apply_tolower (s1, t1, s1len);
167       apply_tolower (s2, t2, s2len);
168     }
169
170   /* Compare the two case-folded strings.  */
171   if (hard_LC_COLLATE)
172     cmp = memcoll (t1, t1len, t2, t2len);
173   else
174     {
175       cmp = memcmp2 (t1, t1len, t2, t2len);
176       errno = 0;
177     }
178
179   {
180     int saved_errno = errno;
181     freea (memory);
182     errno = saved_errno;
183   }
184
185   return cmp;
186 }