maint: update copyright
[gnulib.git] / lib / mbmemcasecoll.c
1 /* Locale-specific case-ignoring memory comparison.
2    Copyright (C) 2001, 2009-2014 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;
65
66           if (n1 == 0) /* NUL character? */
67             n1 = 1;
68
69           wc2 = towlower (wc1);
70           if (wc2 != wc1)
71             {
72               size_t n2;
73
74               memset (&state, '\0', sizeof (mbstate_t));
75               n2 = wcrtomb (outbuf, wc2, &state);
76               if (n2 != (size_t)(-1))
77                 {
78                   /* Store the translated multibyte character.  */
79                   inbuf += n1;
80                   remaining -= n1;
81                   outbuf += n2;
82                   continue;
83                 }
84             }
85
86           /* Nothing to translate. */
87           memcpy (outbuf, inbuf, n1);
88           inbuf += n1;
89           remaining -= n1;
90           outbuf += n1;
91           continue;
92         }
93
94       /* Invalid multibyte character on input.
95          Copy one byte without modification.  */
96       *outbuf++ = *inbuf++;
97       remaining -= 1;
98     }
99   /* Incomplete multibyte sequence on input.
100      Pass it through unmodified.  */
101   while (remaining > 0)
102     {
103       *outbuf++ = *inbuf++;
104       remaining -= 1;
105     }
106
107   /* Verify the output buffer was large enough.  */
108   if (outbuf - outbuf_orig > outbufsize)
109     abort ();
110
111   /* Return the number of written output bytes.  */
112   return outbuf - outbuf_orig;
113 }
114
115 /* Apply tolower() to the unibyte character sequence in INBUF, storing the
116    result as a unibyte character sequence in OUTBUF.  */
117 static void
118 apply_tolower (const char *inbuf, char *outbuf, size_t bufsize)
119 {
120   for (; bufsize > 0; bufsize--)
121     {
122       *outbuf = TOLOWER ((unsigned char) *inbuf);
123       inbuf++;
124       outbuf++;
125     }
126 }
127
128 int
129 mbmemcasecoll (const char *s1, size_t s1len, const char *s2, size_t s2len,
130                bool hard_LC_COLLATE)
131 {
132   char *t1;
133   size_t t1len;
134   char *t2;
135   size_t t2len;
136   char *memory;
137   int cmp;
138
139   if (MB_CUR_MAX > 1)
140     {
141       /* Application of towlower grows each character by a factor 2
142          at most.  */
143       t1len = 2 * s1len;
144       t2len = 2 * s2len;
145     }
146   else
147     {
148       /* Application of tolower doesn't change the size.  */
149       t1len = s1len;
150       t2len = s2len;
151     }
152   /* Allocate memory for t1 and t2.  */
153   memory = (char *) malloca (t1len + 1 + t2len + 1);
154   if (memory == NULL)
155     {
156       errno = ENOMEM;
157       return 0;
158     }
159   t1 = memory;
160   t2 = memory + t1len + 1;
161
162   /* Csae-fold the two argument strings.  */
163   if (MB_CUR_MAX > 1)
164     {
165       t1len = apply_towlower (s1, s1len, t1, t1len);
166       t2len = apply_towlower (s2, s2len, t2, t2len);
167     }
168   else
169     {
170       apply_tolower (s1, t1, s1len);
171       apply_tolower (s2, t2, s2len);
172     }
173
174   /* Compare the two case-folded strings.  */
175   if (hard_LC_COLLATE)
176     cmp = memcoll (t1, t1len, t2, t2len);
177   else
178     {
179       cmp = memcmp2 (t1, t1len, t2, t2len);
180       errno = 0;
181     }
182
183   {
184     int saved_errno = errno;
185     freea (memory);
186     errno = saved_errno;
187   }
188
189   return cmp;
190 }