Reactos
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 */
20
21#include <assert.h>
22#include <errno.h>
23#include <limits.h>
24#include <stdio.h>
25
26#include "wine/unicode.h"
27
28#ifdef __REACTOS__
29#define min(a,b) (((a) < (b)) ? (a) : (b))
30#endif
31
32int strcmpiW( const WCHAR *str1, const WCHAR *str2 )
33{
34 for (;;)
35 {
36 int ret = tolowerW(*str1) - tolowerW(*str2);
37 if (ret || !*str1) return ret;
38 str1++;
39 str2++;
40 }
41}
42
43int strncmpiW( const WCHAR *str1, const WCHAR *str2, int n )
44{
45 int ret = 0;
46 for ( ; n > 0; n--, str1++, str2++)
47 if ((ret = tolowerW(*str1) - tolowerW(*str2)) || !*str1) break;
48 return ret;
49}
50
51int memicmpW( const WCHAR *str1, const WCHAR *str2, int n )
52{
53 int ret = 0;
54 for ( ; n > 0; n--, str1++, str2++)
55 if ((ret = tolowerW(*str1) - tolowerW(*str2))) break;
56 return ret;
57}
58
59WCHAR *strstrW( const WCHAR *str, const WCHAR *sub )
60{
61 while (*str)
62 {
63 const WCHAR *p1 = str, *p2 = sub;
64 while (*p1 && *p2 && *p1 == *p2) { p1++; p2++; }
65 if (!*p2) return (WCHAR *)str;
66 str++;
67 }
68 return NULL;
69}
70
71/* strtolW and strtoulW implementation based on the GNU C library code */
72/* Copyright (C) 1991,92,94,95,96,97,98,99,2000,2001 Free Software Foundation, Inc. */
73
74long int strtolW( const WCHAR *nptr, WCHAR **endptr, int base )
75{
76 int negative;
77 register unsigned long int cutoff;
78 register unsigned int cutlim;
79 register unsigned long int i;
80 register const WCHAR *s;
81 register WCHAR c;
82 const WCHAR *save, *end;
83 int overflow;
84
85 if (base < 0 || base == 1 || base > 36) return 0;
86
87 save = s = nptr;
88
89 /* Skip white space. */
90 while (isspaceW (*s))
91 ++s;
92 if (!*s) goto noconv;
93
94 /* Check for a sign. */
95 negative = 0;
96 if (*s == '-')
97 {
98 negative = 1;
99 ++s;
100 }
101 else if (*s == '+')
102 ++s;
103
104 /* Recognize number prefix and if BASE is zero, figure it out ourselves. */
105 if (*s == '0')
106 {
107 if ((base == 0 || base == 16) && toupperW(s[1]) == 'X')
108 {
109 s += 2;
110 base = 16;
111 }
112 else if (base == 0)
113 base = 8;
114 }
115 else if (base == 0)
116 base = 10;
117
118 /* Save the pointer so we can check later if anything happened. */
119 save = s;
120 end = NULL;
121
122 cutoff = ULONG_MAX / (unsigned long int) base;
123 cutlim = ULONG_MAX % (unsigned long int) base;
124
125 overflow = 0;
126 i = 0;
127 c = *s;
128 for (;c != '\0'; c = *++s)
129 {
130 if (s == end)
131 break;
132 if (c >= '0' && c <= '9')
133 c -= '0';
134 else if (isalphaW (c))
135 c = toupperW (c) - 'A' + 10;
136 else
137 break;
138 if ((int) c >= base)
139 break;
140 /* Check for overflow. */
141 if (i > cutoff || (i == cutoff && c > cutlim))
142 overflow = 1;
143 else
144 {
145 i *= (unsigned long int) base;
146 i += c;
147 }
148 }
149
150 /* Check if anything actually happened. */
151 if (s == save)
152 goto noconv;
153
154 /* Store in ENDPTR the address of one character
155 past the last character we converted. */
156 if (endptr != NULL)
157 *endptr = (WCHAR *)s;
158
159 /* Check for a value that is within the range of
160 `unsigned LONG int', but outside the range of `LONG int'. */
161 if (overflow == 0
162 && i > (negative
163 ? -((unsigned long int) (LONG_MIN + 1)) + 1
164 : (unsigned long int) LONG_MAX))
165 overflow = 1;
166
167 if (overflow)
168 {
169 errno = ERANGE;
170 return negative ? LONG_MIN : LONG_MAX;
171 }
172
173 /* Return the result of the appropriate sign. */
174 return negative ? -i : i;
175
176noconv:
177 /* We must handle a special case here: the base is 0 or 16 and the
178 first two characters are '0' and 'x', but the rest are not
179 hexadecimal digits. This is no error case. We return 0 and
180 ENDPTR points to the `x`. */
181 if (endptr != NULL)
182 {
183 if (save - nptr >= 2 && toupperW (save[-1]) == 'X'
184 && save[-2] == '0')
185 *endptr = (WCHAR *)&save[-1];
186 else
187 /* There was no number to convert. */
188 *endptr = (WCHAR *)nptr;
189 }
190
191 return 0L;
192}
193
194
195unsigned long int strtoulW( const WCHAR *nptr, WCHAR **endptr, int base )
196{
197 int negative;
198 register unsigned long int cutoff;
199 register unsigned int cutlim;
200 register unsigned long int i;
201 register const WCHAR *s;
202 register WCHAR c;
203 const WCHAR *save, *end;
204 int overflow;
205
206 if (base < 0 || base == 1 || base > 36) return 0;
207
208 save = s = nptr;
209
210 /* Skip white space. */
211 while (isspaceW (*s))
212 ++s;
213 if (!*s) goto noconv;
214
215 /* Check for a sign. */
216 negative = 0;
217 if (*s == '-')
218 {
219 negative = 1;
220 ++s;
221 }
222 else if (*s == '+')
223 ++s;
224
225 /* Recognize number prefix and if BASE is zero, figure it out ourselves. */
226 if (*s == '0')
227 {
228 if ((base == 0 || base == 16) && toupperW(s[1]) == 'X')
229 {
230 s += 2;
231 base = 16;
232 }
233 else if (base == 0)
234 base = 8;
235 }
236 else if (base == 0)
237 base = 10;
238
239 /* Save the pointer so we can check later if anything happened. */
240 save = s;
241 end = NULL;
242
243 cutoff = ULONG_MAX / (unsigned long int) base;
244 cutlim = ULONG_MAX % (unsigned long int) base;
245
246 overflow = 0;
247 i = 0;
248 c = *s;
249 for (;c != '\0'; c = *++s)
250 {
251 if (s == end)
252 break;
253 if (c >= '0' && c <= '9')
254 c -= '0';
255 else if (isalphaW (c))
256 c = toupperW (c) - 'A' + 10;
257 else
258 break;
259 if ((int) c >= base)
260 break;
261 /* Check for overflow. */
262 if (i > cutoff || (i == cutoff && c > cutlim))
263 overflow = 1;
264 else
265 {
266 i *= (unsigned long int) base;
267 i += c;
268 }
269 }
270
271 /* Check if anything actually happened. */
272 if (s == save)
273 goto noconv;
274
275 /* Store in ENDPTR the address of one character
276 past the last character we converted. */
277 if (endptr != NULL)
278 *endptr = (WCHAR *)s;
279
280 if (overflow)
281 {
282 errno = ERANGE;
283 return ULONG_MAX;
284 }
285
286 /* Return the result of the appropriate sign. */
287 return negative ? -i : i;
288
289noconv:
290 /* We must handle a special case here: the base is 0 or 16 and the
291 first two characters are '0' and 'x', but the rest are not
292 hexadecimal digits. This is no error case. We return 0 and
293 ENDPTR points to the `x`. */
294 if (endptr != NULL)
295 {
296 if (save - nptr >= 2 && toupperW (save[-1]) == 'X'
297 && save[-2] == '0')
298 *endptr = (WCHAR *)&save[-1];
299 else
300 /* There was no number to convert. */
301 *endptr = (WCHAR *)nptr;
302 }
303
304 return 0L;
305}
306
307
308/* format a WCHAR string according to a printf format; helper for vsnprintfW */
309static size_t format_string( WCHAR *buffer, size_t len, const char *format, const WCHAR *str, int str_len )
310{
311 size_t count = 0;
312 int i, left_align = 0, width = 0, max = 0;
313
314 assert( *format == '%' );
315 format++; /* skip '%' */
316
317 while (*format == '0' || *format == '+' || *format == '-' || *format == ' ' || *format == '#')
318 {
319 if (*format == '-') left_align = 1;
320 format++;
321 }
322
323 while (isdigit(*format)) width = width * 10 + *format++ - '0';
324
325 if (str_len == -1) str_len = strlenW( str );
326 if (*format == '.')
327 {
328 format++;
329 while (isdigit(*format)) max = max * 10 + *format++ - '0';
330 if (max > str_len) max = str_len;
331 }
332 else max = str_len;
333
334 if (*format == 'h' || *format == 'l') format++;
335
336 assert( *format == 's' );
337
338 if (!left_align && width > max)
339 {
340 for (i = 0; i < width - max; i++)
341 {
342 if (count++ < len)
343 *buffer++ = ' ';
344 }
345 }
346
347 if (count < len)
348 memcpy( buffer, str, min( max, len - count ) * sizeof(WCHAR) );
349 count += max;
350 buffer += max;
351
352 if (left_align && width > max)
353 {
354 for (i = 0; i < width - max; i++)
355 {
356 if (count++ < len)
357 *buffer++ = ' ';
358 }
359 }
360 return count;
361}
362
363int vsnprintfW(WCHAR *str, size_t len, const WCHAR *format, va_list valist)
364{
365 unsigned int written = 0;
366 const WCHAR *iter = format;
367 char bufa[512], fmtbufa[64], *fmta;
368
369 while (*iter)
370 {
371 while (*iter && *iter != '%')
372 {
373 if (written++ < len)
374 *str++ = *iter;
375 iter++;
376 }
377 if (*iter == '%')
378 {
379 if (iter[1] == '%')
380 {
381 if (written++ < len)
382 *str++ = '%'; /* "%%"->'%' */
383 iter += 2;
384 continue;
385 }
386
387 fmta = fmtbufa;
388 *fmta++ = *iter++;
389 while (*iter == '0' ||
390 *iter == '+' ||
391 *iter == '-' ||
392 *iter == ' ' ||
393 *iter == '*' ||
394 *iter == '#')
395 {
396 if (*iter == '*')
397 {
398 char *buffiter = bufa;
399 int fieldlen = va_arg(valist, int);
400 sprintf(buffiter, "%d", fieldlen);
401 while (*buffiter)
402 *fmta++ = *buffiter++;
403 }
404 else
405 *fmta++ = *iter;
406 iter++;
407 }
408
409 while (isdigit(*iter))
410 *fmta++ = *iter++;
411
412 if (*iter == '.')
413 {
414 *fmta++ = *iter++;
415 if (*iter == '*')
416 {
417 char *buffiter = bufa;
418 int fieldlen = va_arg(valist, int);
419 sprintf(buffiter, "%d", fieldlen);
420 while (*buffiter)
421 *fmta++ = *buffiter++;
422 iter++;
423 }
424 else
425 while (isdigit(*iter))
426 *fmta++ = *iter++;
427 }
428 if (*iter == 'h' || *iter == 'l')
429 *fmta++ = *iter++;
430
431 switch (*iter)
432 {
433 case 's':
434 {
435 static const WCHAR none[] = { '(','n','u','l','l',')',0 };
436 const WCHAR *wstr = va_arg(valist, const WCHAR *);
437 size_t remaining = written < len ? len - written : 0;
438 size_t count;
439
440 *fmta++ = 's';
441 *fmta = 0;
442 count = format_string( str, remaining, fmtbufa, wstr ? wstr : none, -1 );
443 str += min( count, remaining );
444 written += count;
445 iter++;
446 break;
447 }
448
449 case 'c':
450 {
451 WCHAR wstr;
452 size_t remaining = written < len ? len - written : 0;
453 size_t count;
454
455 wstr = va_arg(valist, int);
456 *fmta++ = 's';
457 *fmta = 0;
458 count = format_string( str, remaining, fmtbufa, &wstr, 1 );
459 str += min( count, remaining );
460 written += count;
461 iter++;
462 break;
463 }
464
465 default:
466 {
467 /* For non wc types, use system sprintf and append to wide char output */
468 /* FIXME: for unrecognised types, should ignore % when printing */
469 char *bufaiter = bufa;
470 if (*iter == 'p')
471#ifdef __REACTOS__
472 sprintf(bufaiter, "%p", va_arg(valist, void*));
473#else
474 sprintf(bufaiter, "%0*lX", 2 * (int)sizeof(void*),
475 (unsigned long)va_arg(valist, void *));
476#endif
477 else
478 {
479 *fmta++ = *iter;
480 *fmta = '\0';
481 if (*iter == 'a' || *iter == 'A' ||
482 *iter == 'e' || *iter == 'E' ||
483 *iter == 'f' || *iter == 'F' ||
484 *iter == 'g' || *iter == 'G')
485 sprintf(bufaiter, fmtbufa, va_arg(valist, double));
486 else
487 {
488 /* FIXME: On 32 bit systems this doesn't handle int 64's. */
489 sprintf(bufaiter, fmtbufa, va_arg(valist, void *));
490 }
491 }
492 while (*bufaiter)
493 {
494 if (written++ < len)
495 *str++ = *bufaiter;
496 bufaiter++;
497 }
498 iter++;
499 break;
500 }
501 }
502 }
503 }
504 if (len)
505 {
506 if (written >= len)
507 str--;
508 *str++ = 0;
509 }
510
511 /* FIXME: POSIX [v]snprintf() returns the equivalent of written, not -1, on short buffer. */
512 return written < len ? (int)written : -1;
513}
514
515int vsprintfW( WCHAR *str, const WCHAR *format, va_list valist )
516{
517 return vsnprintfW( str, INT_MAX, format, valist );
518}
519
520int snprintfW( WCHAR *str, size_t len, const WCHAR *format, ...)
521{
522 int retval;
523 va_list valist;
524 va_start(valist, format);
525 retval = vsnprintfW(str, len, format, valist);
526 va_end(valist);
527 return retval;
528}
529
530int sprintfW( WCHAR *str, const WCHAR *format, ...)
531{
532 int retval;
533 va_list valist;
534 va_start(valist, format);
535 retval = vsnprintfW(str, INT_MAX, format, valist);
536 va_end(valist);
537 return retval;
538}