Fix subclassing to support nested messages.
[wine] / libs / unicode / string.c
1 /*
2  * Unicode string manipulation functions
3  *
4  * Copyright 2000 Alexandre Julliard
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library 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 GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include <limits.h>
22 #include <stdio.h>
23
24 #include "wine/unicode.h"
25
26 int strcmpiW( const WCHAR *str1, const WCHAR *str2 )
27 {
28     for (;;)
29     {
30         int ret = tolowerW(*str1) - tolowerW(*str2);
31         if (ret || !*str1) return ret;
32         str1++;
33         str2++;
34     }
35 }
36
37 int strncmpiW( const WCHAR *str1, const WCHAR *str2, int n )
38 {
39     int ret = 0;
40     for ( ; n > 0; n--, str1++, str2++)
41         if ((ret = tolowerW(*str1) - tolowerW(*str2)) || !*str1) break;
42     return ret;
43 }
44
45 WCHAR *strstrW( const WCHAR *str, const WCHAR *sub )
46 {
47     while (*str)
48     {
49         const WCHAR *p1 = str, *p2 = sub;
50         while (*p1 && *p2 && *p1 == *p2) { p1++; p2++; }
51         if (!*p2) return (WCHAR *)str;
52         str++;
53     }
54     return NULL;
55 }
56
57 /* strtolW and strtoulW implementation based on the GNU C library code */
58 /* Copyright (C) 1991,92,94,95,96,97,98,99,2000,2001 Free Software Foundation, Inc. */
59
60 long int strtolW( const WCHAR *nptr, WCHAR **endptr, int base )
61 {
62   int negative;
63   register unsigned long int cutoff;
64   register unsigned int cutlim;
65   register unsigned long int i;
66   register const WCHAR *s;
67   register WCHAR c;
68   const WCHAR *save, *end;
69   int overflow;
70
71   if (base < 0 || base == 1 || base > 36) return 0;
72
73   save = s = nptr;
74
75   /* Skip white space.  */
76   while (isspaceW (*s))
77     ++s;
78   if (!*s) goto noconv;
79
80   /* Check for a sign.  */
81   negative = 0;
82   if (*s == '-')
83     {
84       negative = 1;
85       ++s;
86     }
87   else if (*s == '+')
88     ++s;
89
90   /* Recognize number prefix and if BASE is zero, figure it out ourselves.  */
91   if (*s == '0')
92     {
93       if ((base == 0 || base == 16) && toupperW(s[1]) == 'X')
94         {
95           s += 2;
96           base = 16;
97         }
98       else if (base == 0)
99         base = 8;
100     }
101   else if (base == 0)
102     base = 10;
103
104   /* Save the pointer so we can check later if anything happened.  */
105   save = s;
106   end = NULL;
107
108   cutoff = ULONG_MAX / (unsigned long int) base;
109   cutlim = ULONG_MAX % (unsigned long int) base;
110
111   overflow = 0;
112   i = 0;
113   c = *s;
114   for (;c != '\0'; c = *++s)
115   {
116       if (s == end)
117           break;
118       if (c >= '0' && c <= '9')
119           c -= '0';
120       else if (isalphaW (c))
121           c = toupperW (c) - 'A' + 10;
122       else
123           break;
124       if ((int) c >= base)
125           break;
126       /* Check for overflow.  */
127       if (i > cutoff || (i == cutoff && c > cutlim))
128           overflow = 1;
129       else
130       {
131           i *= (unsigned long int) base;
132           i += c;
133       }
134   }
135
136   /* Check if anything actually happened.  */
137   if (s == save)
138     goto noconv;
139
140   /* Store in ENDPTR the address of one character
141      past the last character we converted.  */
142   if (endptr != NULL)
143     *endptr = (WCHAR *)s;
144
145   /* Check for a value that is within the range of
146      `unsigned LONG int', but outside the range of `LONG int'.  */
147   if (overflow == 0
148       && i > (negative
149               ? -((unsigned long int) (LONG_MIN + 1)) + 1
150               : (unsigned long int) LONG_MAX))
151     overflow = 1;
152
153   if (overflow)
154     {
155       return negative ? LONG_MIN : LONG_MAX;
156     }
157
158   /* Return the result of the appropriate sign.  */
159   return negative ? -i : i;
160
161 noconv:
162   /* We must handle a special case here: the base is 0 or 16 and the
163      first two characters are '0' and 'x', but the rest are not
164      hexadecimal digits.  This is no error case.  We return 0 and
165      ENDPTR points to the `x`.  */
166   if (endptr != NULL)
167     {
168       if (save - nptr >= 2 && toupperW (save[-1]) == 'X'
169           && save[-2] == '0')
170         *endptr = (WCHAR *)&save[-1];
171       else
172         /*  There was no number to convert.  */
173         *endptr = (WCHAR *)nptr;
174     }
175
176   return 0L;
177 }
178
179
180 unsigned long int strtoulW( const WCHAR *nptr, WCHAR **endptr, int base )
181 {
182   int negative;
183   register unsigned long int cutoff;
184   register unsigned int cutlim;
185   register unsigned long int i;
186   register const WCHAR *s;
187   register WCHAR c;
188   const WCHAR *save, *end;
189   int overflow;
190
191   if (base < 0 || base == 1 || base > 36) return 0;
192
193   save = s = nptr;
194
195   /* Skip white space.  */
196   while (isspaceW (*s))
197     ++s;
198   if (!*s) goto noconv;
199
200   /* Check for a sign.  */
201   negative = 0;
202   if (*s == '-')
203     {
204       negative = 1;
205       ++s;
206     }
207   else if (*s == '+')
208     ++s;
209
210   /* Recognize number prefix and if BASE is zero, figure it out ourselves.  */
211   if (*s == '0')
212     {
213       if ((base == 0 || base == 16) && toupperW(s[1]) == 'X')
214         {
215           s += 2;
216           base = 16;
217         }
218       else if (base == 0)
219         base = 8;
220     }
221   else if (base == 0)
222     base = 10;
223
224   /* Save the pointer so we can check later if anything happened.  */
225   save = s;
226   end = NULL;
227
228   cutoff = ULONG_MAX / (unsigned long int) base;
229   cutlim = ULONG_MAX % (unsigned long int) base;
230
231   overflow = 0;
232   i = 0;
233   c = *s;
234   for (;c != '\0'; c = *++s)
235   {
236       if (s == end)
237           break;
238       if (c >= '0' && c <= '9')
239           c -= '0';
240       else if (isalphaW (c))
241           c = toupperW (c) - 'A' + 10;
242       else
243           break;
244       if ((int) c >= base)
245           break;
246       /* Check for overflow.  */
247       if (i > cutoff || (i == cutoff && c > cutlim))
248           overflow = 1;
249       else
250       {
251           i *= (unsigned long int) base;
252           i += c;
253       }
254   }
255
256   /* Check if anything actually happened.  */
257   if (s == save)
258     goto noconv;
259
260   /* Store in ENDPTR the address of one character
261      past the last character we converted.  */
262   if (endptr != NULL)
263     *endptr = (WCHAR *)s;
264
265   if (overflow)
266     {
267       return ULONG_MAX;
268     }
269
270   /* Return the result of the appropriate sign.  */
271   return negative ? -i : i;
272
273 noconv:
274   /* We must handle a special case here: the base is 0 or 16 and the
275      first two characters are '0' and 'x', but the rest are not
276      hexadecimal digits.  This is no error case.  We return 0 and
277      ENDPTR points to the `x`.  */
278   if (endptr != NULL)
279     {
280       if (save - nptr >= 2 && toupperW (save[-1]) == 'X'
281           && save[-2] == '0')
282         *endptr = (WCHAR *)&save[-1];
283       else
284         /*  There was no number to convert.  */
285         *endptr = (WCHAR *)nptr;
286     }
287
288   return 0L;
289 }
290
291
292 int vsnprintfW(WCHAR *str, size_t len, const WCHAR *format, va_list valist)
293 {
294     unsigned int written = 0;
295     const WCHAR *iter = format;
296     char bufa[256], fmtbufa[64], *fmta;
297
298     while (*iter)
299     {
300         while (*iter && *iter != '%')
301         {
302             if (written++ >= len)
303                 return -1;
304             *str++ = *iter++;
305         }
306         if (*iter == '%')
307         {
308             if (iter[1] == '%')
309             {
310                 if (written++ >= len)
311                     return -1;
312                 *str++ = '%'; /* "%%"->'%' */
313                 iter += 2;
314                 continue;
315             }
316
317             fmta = fmtbufa;
318             *fmta++ = *iter++;
319             while (*iter == '0' ||
320                    *iter == '+' ||
321                    *iter == '-' ||
322                    *iter == ' ' ||
323                    *iter == '*' ||
324                    *iter == '#')
325             {
326                 if (*iter == '*')
327                 {
328                     char *buffiter = bufa;
329                     int fieldlen = va_arg(valist, int);
330                     sprintf(buffiter, "%d", fieldlen);
331                     while (*buffiter)
332                         *fmta++ = *buffiter++;
333                 }
334                 else
335                     *fmta++ = *iter;
336                 iter++;
337             }
338
339             while (isdigit(*iter))
340                 *fmta++ = *iter++;
341
342             if (*iter == '.')
343             {
344                 *fmta++ = *iter++;
345                 if (*iter == '*')
346                 {
347                     char *buffiter = bufa;
348                     int fieldlen = va_arg(valist, int);
349                     sprintf(buffiter, "%d", fieldlen);
350                     while (*buffiter)
351                         *fmta++ = *buffiter++;
352                 }
353                 else
354                     while (isdigit(*iter))
355                         *fmta++ = *iter++;
356             }
357             if (*iter == 'h' || *iter == 'l')
358                 *fmta++ = *iter++;
359
360             switch (*iter)
361             {
362             case 's':
363             {
364                 static const WCHAR none[] = { '(','n','u','l','l',')',0 };
365                 const WCHAR *wstr = va_arg(valist, const WCHAR *);
366                 const WCHAR *striter = wstr ? wstr : none;
367                 while (*striter)
368                 {
369                     if (written++ >= len)
370                         return -1;
371                     *str++ = *striter++;
372                 }
373                 iter++;
374                 break;
375             }
376
377             case 'c':
378                 if (written++ >= len)
379                     return -1;
380                 *str++ = (WCHAR)va_arg(valist, int);
381                 iter++;
382                 break;
383
384             default:
385             {
386                 /* For non wc types, use system sprintf and append to wide char output */
387                 /* FIXME: for unrecognised types, should ignore % when printing */
388                 char *bufaiter = bufa;
389                 if (*iter == 'p')
390                     sprintf(bufaiter, "%08lX", va_arg(valist, long));
391                 else
392                 {
393                     *fmta++ = *iter;
394                     *fmta = '\0';
395                     if (*iter == 'a' || *iter == 'A' ||
396                         *iter == 'e' || *iter == 'E' ||
397                         *iter == 'f' || *iter == 'F' || 
398                         *iter == 'g' || *iter == 'G')
399                         sprintf(bufaiter, fmtbufa, va_arg(valist, double));
400                     else
401                     {
402                         /* FIXME: On 32 bit systems this doesn't handle int 64's.
403                          *        on 64 bit systems this doesn't work for 32 bit types
404                          */
405                         sprintf(bufaiter, fmtbufa, va_arg(valist, void *));
406                     }
407                 }
408                 while (*bufaiter)
409                 {
410                     if (written++ >= len)
411                         return -1;
412                     *str++ = *bufaiter++;
413                 }
414                 iter++;
415                 break;
416             }
417             }
418         }
419     }
420     if (written >= len)
421         return -1;
422     *str++ = 0;
423     return (int)written;
424 }
425
426 int vsprintfW( WCHAR *str, const WCHAR *format, va_list valist )
427 {
428     return vsnprintfW( str, INT_MAX, format, valist );
429 }
430
431 int snprintfW( WCHAR *str, size_t len, const WCHAR *format, ...)
432 {
433     int retval;
434     va_list valist;
435     va_start(valist, format);
436     retval = vsnprintfW(str, len, format, valist);
437     va_end(valist);
438     return retval;
439 }
440
441 int sprintfW( WCHAR *str, const WCHAR *format, ...)
442 {
443     int retval;
444     va_list valist;
445     va_start(valist, format);
446     retval = vsnprintfW(str, INT_MAX, format, valist);
447     va_end(valist);
448     return retval;
449 }