Reactos
1/*
2 * Copyright 2019 Nikolay Sivov for CodeWeavers
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17 */
18
19#include <stdarg.h>
20
21#include "windef.h"
22#include "winbase.h"
23#include "winnls.h"
24#include "shlwapi.h"
25#include "winternl.h"
26
27#include "kernelbase.h"
28#include "wine/debug.h"
29#include "wine/exception.h"
30
31WINE_DEFAULT_DEBUG_CHANNEL(string);
32
33static BOOL char_compare(WORD ch1, WORD ch2, DWORD flags)
34{
35 char str1[3], str2[3];
36
37 str1[0] = LOBYTE(ch1);
38 if (IsDBCSLeadByte(str1[0]))
39 {
40 str1[1] = HIBYTE(ch1);
41 str1[2] = '\0';
42 }
43 else
44 str1[1] = '\0';
45
46 str2[0] = LOBYTE(ch2);
47 if (IsDBCSLeadByte(str2[0]))
48 {
49 str2[1] = HIBYTE(ch2);
50 str2[2] = '\0';
51 }
52 else
53 str2[1] = '\0';
54
55 return CompareStringA(GetThreadLocale(), flags, str1, -1, str2, -1) - CSTR_EQUAL;
56}
57
58int WINAPI lstrcmpA( LPCSTR str1, LPCSTR str2 )
59{
60 if (!str1 && !str2) return 0;
61 if (!str1) return -1;
62 if (!str2) return 1;
63 return CompareStringA( GetThreadLocale(), LOCALE_USE_CP_ACP, str1, -1, str2, -1 ) - 2;
64}
65
66int WINAPI lstrcmpW(LPCWSTR str1, LPCWSTR str2)
67{
68 if (!str1 && !str2) return 0;
69 if (!str1) return -1;
70 if (!str2) return 1;
71 return CompareStringW( GetThreadLocale(), 0, str1, -1, str2, -1 ) - 2;
72}
73
74int WINAPI lstrcmpiA(LPCSTR str1, LPCSTR str2)
75{
76 if (!str1 && !str2) return 0;
77 if (!str1) return -1;
78 if (!str2) return 1;
79 return CompareStringA( GetThreadLocale(), NORM_IGNORECASE|LOCALE_USE_CP_ACP, str1, -1, str2, -1 ) - 2;
80}
81
82int WINAPI lstrcmpiW(LPCWSTR str1, LPCWSTR str2)
83{
84 if (!str1 && !str2) return 0;
85 if (!str1) return -1;
86 if (!str2) return 1;
87 return CompareStringW( GetThreadLocale(), NORM_IGNORECASE, str1, -1, str2, -1 ) - 2;
88}
89
90LPSTR WINAPI KERNELBASE_lstrcpynA( LPSTR dst, LPCSTR src, INT n )
91{
92 /* Note: this function differs from the UNIX strncpy, it _always_ writes
93 * a terminating \0.
94 *
95 * Note: n is an INT but Windows treats it as unsigned, and will happily
96 * copy a gazillion chars if n is negative.
97 */
98 __TRY
99 {
100 LPSTR d = dst;
101 LPCSTR s = src;
102 UINT count = n;
103
104 while ((count > 1) && *s)
105 {
106 count--;
107 *d++ = *s++;
108 }
109 if (count) *d = 0;
110 }
111 __EXCEPT_PAGE_FAULT
112 {
113 SetLastError( ERROR_INVALID_PARAMETER );
114 return 0;
115 }
116 __ENDTRY
117 return dst;
118}
119
120LPWSTR WINAPI KERNELBASE_lstrcpynW( LPWSTR dst, LPCWSTR src, INT n )
121{
122 /* Note: this function differs from the UNIX strncpy, it _always_ writes
123 * a terminating \0
124 *
125 * Note: n is an INT but Windows treats it as unsigned, and will happily
126 * copy a gazillion chars if n is negative.
127 */
128 __TRY
129 {
130 LPWSTR d = dst;
131 LPCWSTR s = src;
132 UINT count = n;
133
134 while ((count > 1) && *s)
135 {
136 count--;
137 *d++ = *s++;
138 }
139 if (count) *d = 0;
140 }
141 __EXCEPT_PAGE_FAULT
142 {
143 SetLastError( ERROR_INVALID_PARAMETER );
144 return 0;
145 }
146 __ENDTRY
147 return dst;
148}
149
150INT WINAPI KERNELBASE_lstrlenA( LPCSTR str )
151{
152 INT ret;
153 __TRY
154 {
155 ret = strlen(str);
156 }
157 __EXCEPT_PAGE_FAULT
158 {
159 SetLastError( ERROR_INVALID_PARAMETER );
160 return 0;
161 }
162 __ENDTRY
163 return ret;
164}
165
166INT WINAPI KERNELBASE_lstrlenW( LPCWSTR str )
167{
168 INT ret;
169 __TRY
170 {
171 ret = wcslen(str);
172 }
173 __EXCEPT_PAGE_FAULT
174 {
175 SetLastError( ERROR_INVALID_PARAMETER );
176 return 0;
177 }
178 __ENDTRY
179 return ret;
180}
181
182DWORD WINAPI StrCmpCA(const char *str, const char *cmp)
183{
184 return lstrcmpA(str, cmp);
185}
186
187DWORD WINAPI StrCmpCW(const WCHAR *str, const WCHAR *cmp)
188{
189 return lstrcmpW(str, cmp);
190}
191
192DWORD WINAPI StrCmpICA(const char *str, const char *cmp)
193{
194 return lstrcmpiA(str, cmp);
195}
196
197DWORD WINAPI StrCmpICW(const WCHAR *str, const WCHAR *cmp)
198{
199 return lstrcmpiW(str, cmp);
200}
201
202DWORD WINAPI StrCmpNICA(const char *str, const char *cmp, DWORD len)
203{
204 return StrCmpNIA(str, cmp, len);
205}
206
207DWORD WINAPI StrCmpNICW(const WCHAR *str, const WCHAR *cmp, DWORD len)
208{
209 return StrCmpNIW(str, cmp, len);
210}
211
212char * WINAPI StrChrA(const char *str, WORD ch)
213{
214 TRACE("%s, %#x\n", wine_dbgstr_a(str), ch);
215
216 if (!str)
217 return NULL;
218
219 while (*str)
220 {
221 if (!char_compare(*str, ch, 0))
222 return (char *)str;
223 str = CharNextA(str);
224 }
225
226 return NULL;
227}
228
229WCHAR * WINAPI StrChrW(const WCHAR *str, WCHAR ch)
230{
231 TRACE("%s, %#x\n", wine_dbgstr_w(str), ch);
232
233 if (!str)
234 return NULL;
235
236 return wcschr(str, ch);
237}
238
239char * WINAPI StrChrIA(const char *str, WORD ch)
240{
241 TRACE("%s, %i\n", wine_dbgstr_a(str), ch);
242
243 if (!str)
244 return NULL;
245
246 while (*str)
247 {
248 if (!ChrCmpIA(*str, ch))
249 return (char *)str;
250 str = CharNextA(str);
251 }
252
253 return NULL;
254}
255
256WCHAR * WINAPI StrChrIW(const WCHAR *str, WCHAR ch)
257{
258 TRACE("%s, %#x\n", wine_dbgstr_w(str), ch);
259
260 if (!str)
261 return NULL;
262
263 ch = towupper(ch);
264 while (*str)
265 {
266 if (towupper(*str) == ch)
267 return (WCHAR *)str;
268 str++;
269 }
270 str = NULL;
271
272 return (WCHAR *)str;
273}
274
275WCHAR * WINAPI StrChrNW(const WCHAR *str, WCHAR ch, UINT max_len)
276{
277 TRACE("%s, %#x, %u\n", wine_dbgstr_wn(str, max_len), ch, max_len);
278
279 if (!str)
280 return NULL;
281
282 while (*str && max_len-- > 0)
283 {
284 if (*str == ch)
285 return (WCHAR *)str;
286 str++;
287 }
288
289 return NULL;
290}
291
292char * WINAPI StrDupA(const char *str)
293{
294 unsigned int len;
295 char *ret;
296
297 TRACE("%s\n", wine_dbgstr_a(str));
298
299 len = str ? strlen(str) + 1 : 1;
300 ret = LocalAlloc(LMEM_FIXED, len);
301
302 if (ret)
303 {
304 if (str)
305 memcpy(ret, str, len);
306 else
307 *ret = '\0';
308 }
309
310 return ret;
311}
312
313WCHAR * WINAPI StrDupW(const WCHAR *str)
314{
315 unsigned int len;
316 WCHAR *ret;
317
318 TRACE("%s\n", wine_dbgstr_w(str));
319
320 len = (str ? lstrlenW(str) + 1 : 1) * sizeof(WCHAR);
321 ret = LocalAlloc(LMEM_FIXED, len);
322
323 if (ret)
324 {
325 if (str)
326 memcpy(ret, str, len);
327 else
328 *ret = '\0';
329 }
330
331 return ret;
332}
333
334BOOL WINAPI ChrCmpIA(WORD ch1, WORD ch2)
335{
336 TRACE("%#x, %#x\n", ch1, ch2);
337
338 return char_compare(ch1, ch2, NORM_IGNORECASE);
339}
340
341BOOL WINAPI ChrCmpIW(WCHAR ch1, WCHAR ch2)
342{
343 return CompareStringW(GetThreadLocale(), NORM_IGNORECASE, &ch1, 1, &ch2, 1) - CSTR_EQUAL;
344}
345
346char * WINAPI StrStrA(const char *str, const char *search)
347{
348 const char *end;
349 size_t len;
350
351 TRACE("%s, %s\n", wine_dbgstr_a(str), wine_dbgstr_a(search));
352
353 if (!str || !search || !*search) return NULL;
354
355 len = strlen(search);
356 end = str + strlen(str);
357
358 while (str + len <= end)
359 {
360 if (!StrCmpNA(str, search, len)) return (char *)str;
361 str = CharNextA(str);
362 }
363 return NULL;
364}
365
366WCHAR * WINAPI StrStrW(const WCHAR *str, const WCHAR *search)
367{
368 TRACE("%s, %s\n", wine_dbgstr_w(str), wine_dbgstr_w(search));
369
370 if (!str || !search || !*search)
371 return NULL;
372
373 return wcsstr(str, search);
374}
375
376WCHAR * WINAPI StrStrNW(const WCHAR *str, const WCHAR *search, UINT max_len)
377{
378 unsigned int i, len;
379
380 TRACE("%s, %s, %u\n", wine_dbgstr_w(str), wine_dbgstr_w(search), max_len);
381
382 if (!str || !search || !*search || !max_len)
383 return NULL;
384
385 len = lstrlenW(search);
386
387 for (i = max_len; *str && (i > 0); i--, str++)
388 {
389 if (!wcsncmp(str, search, len))
390 return (WCHAR *)str;
391 }
392
393 return NULL;
394}
395
396int WINAPI StrCmpNIA(const char *str, const char *cmp, int len)
397{
398 TRACE("%s, %s, %i\n", wine_dbgstr_a(str), wine_dbgstr_a(cmp), len);
399 return CompareStringA(GetThreadLocale(), NORM_IGNORECASE, str, len, cmp, len) - CSTR_EQUAL;
400}
401
402WCHAR * WINAPI StrStrNIW(const WCHAR *str, const WCHAR *search, UINT max_len)
403{
404 unsigned int i, len;
405
406 TRACE("%s, %s, %u\n", wine_dbgstr_w(str), wine_dbgstr_w(search), max_len);
407
408 if (!str || !search || !*search || !max_len)
409 return NULL;
410
411 len = lstrlenW(search);
412
413 for (i = max_len; *str && (i > 0); i--, str++)
414 {
415 if (!StrCmpNIW(str, search, len))
416 return (WCHAR *)str;
417 }
418
419 return NULL;
420}
421
422int WINAPI StrCmpNA(const char *str, const char *comp, int len)
423{
424 TRACE("%s, %s, %i\n", wine_dbgstr_a(str), wine_dbgstr_a(comp), len);
425 return CompareStringA(GetThreadLocale(), 0, str, len, comp, len) - CSTR_EQUAL;
426}
427
428int WINAPI StrCmpNW(const WCHAR *str, const WCHAR *comp, int len)
429{
430 TRACE("%s, %s, %i\n", wine_dbgstr_w(str), wine_dbgstr_w(comp), len);
431 return CompareStringW(GetThreadLocale(), 0, str, len, comp, len) - CSTR_EQUAL;
432}
433
434DWORD WINAPI StrCmpNCA(const char *str, const char *comp, int len)
435{
436 return StrCmpNA(str, comp, len);
437}
438
439DWORD WINAPI StrCmpNCW(const WCHAR *str, const WCHAR *comp, int len)
440{
441 return StrCmpNW(str, comp, len);
442}
443
444int WINAPI StrCmpNIW(const WCHAR *str, const WCHAR *comp, int len)
445{
446 TRACE("%s, %s, %i\n", wine_dbgstr_w(str), wine_dbgstr_w(comp), len);
447 return CompareStringW(GetThreadLocale(), NORM_IGNORECASE, str, len, comp, len) - CSTR_EQUAL;
448}
449
450int WINAPI StrCmpW(const WCHAR *str, const WCHAR *comp)
451{
452 TRACE("%s, %s\n", wine_dbgstr_w(str), wine_dbgstr_w(comp));
453 return CompareStringW(GetThreadLocale(), 0, str, -1, comp, -1) - CSTR_EQUAL;
454}
455
456int WINAPI StrCmpIW(const WCHAR *str, const WCHAR *comp)
457{
458 TRACE("%s, %s\n", wine_dbgstr_w(str), wine_dbgstr_w(comp));
459 return CompareStringW(GetThreadLocale(), NORM_IGNORECASE, str, -1, comp, -1) - CSTR_EQUAL;
460}
461
462WCHAR * WINAPI StrCpyNW(WCHAR *dst, const WCHAR *src, int count)
463{
464 const WCHAR *s = src;
465 WCHAR *d = dst;
466
467 TRACE("%p, %s, %i\n", dst, wine_dbgstr_w(src), count);
468
469 if (s)
470 {
471 while ((count > 1) && *s)
472 {
473 count--;
474 *d++ = *s++;
475 }
476 }
477 if (count) *d = 0;
478
479 return dst;
480}
481
482char * WINAPI StrStrIA(const char *str, const char *search)
483{
484 const char *end;
485 size_t len;
486
487 TRACE("%s, %s\n", wine_dbgstr_a(str), debugstr_a(search));
488
489 if (!str || !search || !*search) return NULL;
490
491 len = strlen(search);
492 end = str + strlen(str);
493
494 while (str + len <= end)
495 {
496 if (!StrCmpNIA(str, search, len)) return (char *)str;
497 str = CharNextA(str);
498 }
499 return NULL;
500}
501
502WCHAR * WINAPI StrStrIW(const WCHAR *str, const WCHAR *search)
503{
504 unsigned int len;
505 const WCHAR *end;
506
507 TRACE("%s, %s\n", wine_dbgstr_w(str), wine_dbgstr_w(search));
508
509 if (!str || !search || !*search)
510 return NULL;
511
512 len = lstrlenW(search);
513 end = str + lstrlenW(str);
514
515 while (str + len <= end)
516 {
517 if (!StrCmpNIW(str, search, len))
518 return (WCHAR *)str;
519 str++;
520 }
521
522 return NULL;
523}
524
525int WINAPI StrSpnA(const char *str, const char *match)
526{
527 const char *ptr = str;
528
529 TRACE("%s, %s\n", wine_dbgstr_a(str), wine_dbgstr_a(match));
530
531 if (!str || !match) return 0;
532
533 while (*ptr)
534 {
535 if (!StrChrA(match, *ptr)) break;
536 ptr = CharNextA(ptr);
537 }
538 return ptr - str;
539}
540
541int WINAPI StrSpnW(const WCHAR *str, const WCHAR *match)
542{
543 if (!str || !match) return 0;
544 return wcsspn(str, match);
545}
546
547int WINAPI StrCSpnA(const char *str, const char *match)
548{
549 const char *ptr = str;
550
551 TRACE("%s, %s\n", wine_dbgstr_a(str), wine_dbgstr_a(match));
552
553 if (!str || !match) return 0;
554
555 while (*ptr)
556 {
557 if (StrChrA(match, *ptr)) break;
558 ptr = CharNextA(ptr);
559 }
560 return ptr - str;
561}
562
563int WINAPI StrCSpnW(const WCHAR *str, const WCHAR *match)
564{
565 if (!str || !match)
566 return 0;
567
568 return wcscspn(str, match);
569}
570
571int WINAPI StrCSpnIA(const char *str, const char *match)
572{
573 const char *ptr = str;
574
575 TRACE("%s, %s\n", wine_dbgstr_a(str), wine_dbgstr_a(match));
576
577 if (!str || !match) return 0;
578
579 while (*ptr)
580 {
581 if (StrChrIA(match, *ptr)) break;
582 ptr = CharNextA(ptr);
583 }
584 return ptr - str;
585}
586
587int WINAPI StrCSpnIW(const WCHAR *str, const WCHAR *match)
588{
589 const WCHAR *ptr = str;
590
591 TRACE("%s, %s\n", wine_dbgstr_w(str), wine_dbgstr_w(match));
592
593 if (!str || !*str || !match)
594 return 0;
595
596 while (*ptr)
597 {
598 if (StrChrIW(match, *ptr)) break;
599 ptr++;
600 }
601
602 return ptr - str;
603}
604
605char * WINAPI StrRChrA(const char *str, const char *end, WORD ch)
606{
607 const char *ret = NULL;
608
609 TRACE("%s, %s, %#x\n", wine_dbgstr_a(str), wine_dbgstr_a(end), ch);
610
611 if (!str) return NULL;
612 if (!end) end = str + lstrlenA(str);
613 while (*str && str <= end)
614 {
615 WORD ch2 = IsDBCSLeadByte(*str) ? *str << 8 | str[1] : *str;
616 if (!char_compare(ch, ch2, 0)) ret = str;
617 str = CharNextA(str);
618 }
619 return (char *)ret;
620}
621
622WCHAR * WINAPI StrRChrW(const WCHAR *str, const WCHAR *end, WORD ch)
623{
624 WCHAR *ret = NULL;
625
626 if (!str) return NULL;
627 if (!end) end = str + lstrlenW(str);
628 while (str < end)
629 {
630 if (*str == ch) ret = (WCHAR *)str;
631 str++;
632 }
633 return ret;
634}
635
636char * WINAPI StrRChrIA(const char *str, const char *end, WORD ch)
637{
638 const char *ret = NULL;
639
640 TRACE("%s, %s, %#x\n", wine_dbgstr_a(str), wine_dbgstr_a(end), ch);
641
642 if (!str) return NULL;
643 if (!end) end = str + lstrlenA(str);
644
645 while (*str && str <= end)
646 {
647 WORD ch2 = IsDBCSLeadByte(*str) ? *str << 8 | str[1] : *str;
648 if (!ChrCmpIA(ch, ch2)) ret = str;
649 str = CharNextA(str);
650 }
651 return (char *)ret;
652}
653
654WCHAR * WINAPI StrRChrIW(const WCHAR *str, const WCHAR *end, WORD ch)
655{
656 WCHAR *ret = NULL;
657
658 if (!str) return NULL;
659 if (!end) end = str + lstrlenW(str);
660 while (str < end)
661 {
662 if (!ChrCmpIW(*str, ch)) ret = (WCHAR *)str;
663 str++;
664 }
665 return ret;
666}
667
668char * WINAPI StrRStrIA(const char *str, const char *end, const char *search)
669{
670 char *ret = NULL;
671 WORD ch1, ch2;
672 int len;
673
674 TRACE("%s, %s\n", wine_dbgstr_a(str), wine_dbgstr_a(search));
675
676 if (!str || !search || !*search)
677 return NULL;
678
679 if (IsDBCSLeadByte(*search))
680 ch1 = *search << 8 | (UCHAR)search[1];
681 else
682 ch1 = *search;
683 len = lstrlenA(search);
684
685 if (!end)
686 end = str + lstrlenA(str);
687 else /* reproduce the broken behaviour on Windows */
688 end += min(len - 1, lstrlenA(end));
689
690 while (str + len <= end && *str)
691 {
692 ch2 = IsDBCSLeadByte(*str) ? *str << 8 | (UCHAR)str[1] : *str;
693 if (!ChrCmpIA(ch1, ch2))
694 {
695 if (!StrCmpNIA(str, search, len))
696 ret = (char *)str;
697 }
698
699 str = CharNextA(str);
700 }
701
702 return ret;
703}
704
705WCHAR * WINAPI StrRStrIW(const WCHAR *str, const WCHAR *end, const WCHAR *search)
706{
707 WCHAR *ret = NULL;
708 int len;
709
710 TRACE("%s, %s\n", wine_dbgstr_w(str), wine_dbgstr_w(search));
711
712 if (!str || !search || !*search)
713 return NULL;
714
715 len = lstrlenW(search);
716
717 if (!end)
718 end = str + lstrlenW(str);
719 else
720 end += min(len - 1, lstrlenW(end));
721
722 while (str + len <= end && *str)
723 {
724 if (!ChrCmpIW(*search, *str))
725 {
726 if (!StrCmpNIW(str, search, len))
727 ret = (WCHAR *)str;
728 }
729 str++;
730 }
731
732 return ret;
733}
734
735char * WINAPI StrPBrkA(const char *str, const char *match)
736{
737 TRACE("%s, %s\n", wine_dbgstr_a(str), wine_dbgstr_a(match));
738
739 if (!str || !match || !*match)
740 return NULL;
741
742 while (*str)
743 {
744 if (StrChrA(match, *str))
745 return (char *)str;
746 str = CharNextA(str);
747 }
748
749 return NULL;
750}
751
752WCHAR * WINAPI StrPBrkW(const WCHAR *str, const WCHAR *match)
753{
754 if (!str || !match) return NULL;
755 return wcspbrk(str, match);
756}
757
758BOOL WINAPI StrTrimA(char *str, const char *trim)
759{
760 unsigned int len;
761 BOOL ret = FALSE;
762 char *ptr = str;
763
764 TRACE("%s, %s\n", debugstr_a(str), debugstr_a(trim));
765
766 if (!str || !*str)
767 return FALSE;
768
769 while (*ptr && StrChrA(trim, *ptr))
770 ptr = CharNextA(ptr); /* Skip leading matches */
771
772 len = strlen(ptr);
773
774 if (ptr != str)
775 {
776 memmove(str, ptr, len + 1);
777 ret = TRUE;
778 }
779
780 if (len > 0)
781 {
782 ptr = str + len;
783 while (StrChrA(trim, ptr[-1]))
784 ptr = CharPrevA(str, ptr); /* Skip trailing matches */
785
786 if (ptr != str + len)
787 {
788 *ptr = '\0';
789 ret = TRUE;
790 }
791 }
792
793 return ret;
794}
795
796BOOL WINAPI StrTrimW(WCHAR *str, const WCHAR *trim)
797{
798 unsigned int len;
799 WCHAR *ptr = str;
800 BOOL ret = FALSE;
801
802 TRACE("%s, %s\n", wine_dbgstr_w(str), wine_dbgstr_w(trim));
803
804 if (!str || !*str)
805 return FALSE;
806
807 while (*ptr && StrChrW(trim, *ptr))
808 ptr++;
809
810 len = lstrlenW(ptr);
811
812 if (ptr != str)
813 {
814 memmove(str, ptr, (len + 1) * sizeof(WCHAR));
815 ret = TRUE;
816 }
817
818 if (len > 0)
819 {
820 ptr = str + len;
821 while (StrChrW(trim, ptr[-1]))
822 ptr--; /* Skip trailing matches */
823
824 if (ptr != str + len)
825 {
826 *ptr = '\0';
827 ret = TRUE;
828 }
829 }
830
831 return ret;
832}
833
834BOOL WINAPI StrToInt64ExA(const char *str, DWORD flags, LONGLONG *ret)
835{
836 BOOL negative = FALSE;
837 LONGLONG value = 0;
838
839 TRACE("%s, %#lx, %p\n", wine_dbgstr_a(str), flags, ret);
840
841 if (!str || !ret)
842 return FALSE;
843
844 if (flags > STIF_SUPPORT_HEX)
845 WARN("Unknown flags %#lx\n", flags);
846
847 /* Skip leading space, '+', '-' */
848 while (*str == ' ' || *str == '\t' || *str == '\n') str++;
849
850 if (*str == '-')
851 {
852 negative = TRUE;
853 str++;
854 }
855 else if (*str == '+')
856 str++;
857
858 if (flags & STIF_SUPPORT_HEX && *str == '0' && (str[1] == 'x' || str[1] == 'X'))
859 {
860 /* Read hex number */
861 str += 2;
862
863 if (!isxdigit(*str))
864 return FALSE;
865
866 while (isxdigit(*str))
867 {
868 value *= 16;
869 if (*str >= '0' && *str <= '9')
870 value += (*str - '0');
871 else if (*str >= 'A' && *str <= 'F')
872 value += 10 + *str - 'A';
873 else
874 value += 10 + *str - 'a';
875 str++;
876 }
877
878 *ret = value;
879 return TRUE;
880 }
881
882 /* Read decimal number */
883 if (*str < '0' || *str > '9')
884 return FALSE;
885
886 while (*str >= '0' && *str <= '9')
887 {
888 value *= 10;
889 value += (*str - '0');
890 str++;
891 }
892
893 *ret = negative ? -value : value;
894 return TRUE;
895}
896
897BOOL WINAPI StrToInt64ExW(const WCHAR *str, DWORD flags, LONGLONG *ret)
898{
899 BOOL negative = FALSE;
900 LONGLONG value = 0;
901
902 TRACE("%s, %#lx, %p\n", wine_dbgstr_w(str), flags, ret);
903
904 if (!str || !ret)
905 return FALSE;
906
907 if (flags > STIF_SUPPORT_HEX)
908 WARN("Unknown flags %#lx.\n", flags);
909
910 /* Skip leading space, '+', '-' */
911 while (*str == ' ' || *str == '\t' || *str == '\n') str++;
912
913 if (*str == '-')
914 {
915 negative = TRUE;
916 str++;
917 }
918 else if (*str == '+')
919 str++;
920
921 if (flags & STIF_SUPPORT_HEX && *str == '0' && (str[1] == 'x' || str[1] == 'X'))
922 {
923 /* Read hex number */
924 str += 2;
925
926 if (!isxdigit(*str))
927 return FALSE;
928
929 while (isxdigit(*str))
930 {
931 value *= 16;
932 if (*str >= '0' && *str <= '9')
933 value += (*str - '0');
934 else if (*str >= 'A' && *str <= 'Z')
935 value += 10 + (*str - 'A');
936 else
937 value += 10 + (*str - 'a');
938 str++;
939 }
940
941 *ret = value;
942 return TRUE;
943 }
944
945 /* Read decimal number */
946 if (*str < '0' || *str > '9')
947 return FALSE;
948
949 while (*str >= '0' && *str <= '9')
950 {
951 value *= 10;
952 value += (*str - '0');
953 str++;
954 }
955
956 *ret = negative ? -value : value;
957 return TRUE;
958}
959
960BOOL WINAPI StrToIntExA(const char *str, DWORD flags, INT *ret)
961{
962 LONGLONG value;
963 BOOL res;
964
965 TRACE("%s, %#lx, %p\n", wine_dbgstr_a(str), flags, ret);
966
967 res = StrToInt64ExA(str, flags, &value);
968 if (res) *ret = value;
969 return res;
970}
971
972BOOL WINAPI StrToIntExW(const WCHAR *str, DWORD flags, INT *ret)
973{
974 LONGLONG value;
975 BOOL res;
976
977 TRACE("%s, %#lx, %p\n", wine_dbgstr_w(str), flags, ret);
978
979 res = StrToInt64ExW(str, flags, &value);
980 if (res) *ret = value;
981 return res;
982}
983
984int WINAPI StrToIntA(const char *str)
985{
986 int value = 0;
987
988 TRACE("%s\n", wine_dbgstr_a(str));
989
990 if (!str)
991 return 0;
992
993 if (*str == '-' || (*str >= '0' && *str <= '9'))
994 StrToIntExA(str, 0, &value);
995
996 return value;
997}
998
999int WINAPI StrToIntW(const WCHAR *str)
1000{
1001 int value = 0;
1002
1003 TRACE("%s\n", wine_dbgstr_w(str));
1004
1005 if (!str)
1006 return 0;
1007
1008 if (*str == '-' || (*str >= '0' && *str <= '9'))
1009 StrToIntExW(str, 0, &value);
1010 return value;
1011}
1012
1013char * WINAPI StrCpyNXA(char *dst, const char *src, int len)
1014{
1015 TRACE("%p, %s, %i\n", dst, wine_dbgstr_a(src), len);
1016
1017 if (dst && src && len > 0)
1018 {
1019 while ((len-- > 1) && *src)
1020 *dst++ = *src++;
1021 if (len >= 0)
1022 *dst = '\0';
1023 }
1024
1025 return dst;
1026}
1027
1028WCHAR * WINAPI StrCpyNXW(WCHAR *dst, const WCHAR *src, int len)
1029{
1030 TRACE("%p, %s, %i\n", dst, wine_dbgstr_w(src), len);
1031
1032 if (dst && src && len > 0)
1033 {
1034 while ((len-- > 1) && *src)
1035 *dst++ = *src++;
1036 if (len >= 0)
1037 *dst = '\0';
1038 }
1039
1040 return dst;
1041}
1042
1043LPSTR WINAPI CharLowerA(char *str)
1044{
1045 if (IS_INTRESOURCE(str))
1046 {
1047 char ch = LOWORD(str);
1048 CharLowerBuffA( &ch, 1 );
1049 return (LPSTR)(UINT_PTR)(BYTE)ch;
1050 }
1051
1052 __TRY
1053 {
1054 CharLowerBuffA( str, strlen(str) );
1055 }
1056 __EXCEPT_PAGE_FAULT
1057 {
1058 SetLastError( ERROR_INVALID_PARAMETER );
1059 return NULL;
1060 }
1061 __ENDTRY
1062 return str;
1063}
1064
1065DWORD WINAPI CharLowerBuffA(char *str, DWORD len)
1066{
1067 DWORD lenW;
1068 WCHAR buffer[32];
1069 WCHAR *strW = buffer;
1070
1071 if (!str) return 0; /* YES */
1072
1073 lenW = MultiByteToWideChar(CP_ACP, 0, str, len, NULL, 0);
1074 if (lenW > ARRAY_SIZE(buffer))
1075 {
1076 strW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
1077 if (!strW) return 0;
1078 }
1079 MultiByteToWideChar(CP_ACP, 0, str, len, strW, lenW);
1080 CharLowerBuffW(strW, lenW);
1081 len = WideCharToMultiByte(CP_ACP, 0, strW, lenW, str, len, NULL, NULL);
1082 if (strW != buffer) HeapFree(GetProcessHeap(), 0, strW);
1083 return len;
1084}
1085
1086DWORD WINAPI CharLowerBuffW(WCHAR *str, DWORD len)
1087{
1088 if (!str) return 0; /* YES */
1089 return LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_LOWERCASE, str, len, str, len);
1090}
1091
1092LPWSTR WINAPI CharLowerW(WCHAR *str)
1093{
1094 if (!IS_INTRESOURCE(str))
1095 {
1096 CharLowerBuffW(str, lstrlenW(str));
1097 return str;
1098 }
1099 else
1100 {
1101 WCHAR ch = LOWORD(str);
1102 CharLowerBuffW(&ch, 1);
1103 return (LPWSTR)(UINT_PTR)ch;
1104 }
1105}
1106
1107LPSTR WINAPI CharNextA(const char *ptr)
1108{
1109 if (!*ptr) return (LPSTR)ptr;
1110 if (IsDBCSLeadByte( ptr[0] ) && ptr[1]) return (LPSTR)(ptr + 2);
1111 return (LPSTR)(ptr + 1);
1112}
1113
1114LPSTR WINAPI CharNextExA(WORD codepage, const char *ptr, DWORD flags)
1115{
1116 if (!*ptr) return (LPSTR)ptr;
1117 if (IsDBCSLeadByteEx( codepage, ptr[0] ) && ptr[1]) return (LPSTR)(ptr + 2);
1118 return (LPSTR)(ptr + 1);
1119}
1120
1121LPWSTR WINAPI CharNextW(const WCHAR *x)
1122{
1123 if (*x) x++;
1124
1125 return (WCHAR *)x;
1126}
1127
1128LPSTR WINAPI CharPrevA(const char *start, const char *ptr)
1129{
1130 while (*start && (start < ptr))
1131 {
1132 LPCSTR next = CharNextA(start);
1133 if (next >= ptr) break;
1134 start = next;
1135 }
1136 return (LPSTR)start;
1137}
1138
1139LPSTR WINAPI CharPrevExA(WORD codepage, const char *start, const char *ptr, DWORD flags)
1140{
1141 while (*start && (start < ptr))
1142 {
1143 LPCSTR next = CharNextExA(codepage, start, flags);
1144 if (next >= ptr) break;
1145 start = next;
1146 }
1147 return (LPSTR)start;
1148}
1149
1150LPWSTR WINAPI CharPrevW(const WCHAR *start, const WCHAR *x)
1151{
1152 if (x > start) return (LPWSTR)(x - 1);
1153 else return (LPWSTR)x;
1154}
1155
1156LPSTR WINAPI CharUpperA(LPSTR str)
1157{
1158 if (IS_INTRESOURCE(str))
1159 {
1160 char ch = LOWORD(str);
1161 CharUpperBuffA(&ch, 1);
1162 return (LPSTR)(UINT_PTR)(BYTE)ch;
1163 }
1164
1165 __TRY
1166 {
1167 CharUpperBuffA(str, strlen(str));
1168 }
1169 __EXCEPT_PAGE_FAULT
1170 {
1171 SetLastError(ERROR_INVALID_PARAMETER);
1172 return NULL;
1173 }
1174 __ENDTRY
1175 return str;
1176}
1177
1178DWORD WINAPI CharUpperBuffA(LPSTR str, DWORD len)
1179{
1180 DWORD lenW;
1181 WCHAR buffer[32];
1182 WCHAR *strW = buffer;
1183
1184 if (!str) return 0; /* YES */
1185
1186 lenW = MultiByteToWideChar(CP_ACP, 0, str, len, NULL, 0);
1187 if (lenW > ARRAY_SIZE(buffer))
1188 {
1189 strW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
1190 if (!strW) return 0;
1191 }
1192 MultiByteToWideChar(CP_ACP, 0, str, len, strW, lenW);
1193 CharUpperBuffW(strW, lenW);
1194 len = WideCharToMultiByte(CP_ACP, 0, strW, lenW, str, len, NULL, NULL);
1195 if (strW != buffer) HeapFree(GetProcessHeap(), 0, strW);
1196 return len;
1197}
1198
1199DWORD WINAPI CharUpperBuffW(WCHAR *str, DWORD len)
1200{
1201 if (!str) return 0; /* YES */
1202 return LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_UPPERCASE, str, len, str, len);
1203}
1204
1205LPWSTR WINAPI CharUpperW(WCHAR *str)
1206{
1207 if (!IS_INTRESOURCE(str))
1208 {
1209 CharUpperBuffW(str, lstrlenW(str));
1210 return str;
1211 }
1212 else
1213 {
1214 WCHAR ch = LOWORD(str);
1215 CharUpperBuffW(&ch, 1);
1216 return (LPWSTR)(UINT_PTR)ch;
1217 }
1218}
1219
1220INT WINAPI DECLSPEC_HOTPATCH LoadStringW(HINSTANCE instance, UINT resource_id, LPWSTR buffer, INT buflen)
1221{
1222 int string_num, i;
1223 HGLOBAL hmem;
1224 HRSRC hrsrc;
1225 WCHAR *p;
1226
1227 TRACE("instance = %p, id = %04x, buffer = %p, length = %d\n", instance, resource_id, buffer, buflen);
1228
1229 if (!buffer)
1230 return 0;
1231
1232 if (!(hrsrc = FindResourceW(instance, MAKEINTRESOURCEW((LOWORD(resource_id) >> 4) + 1), (LPWSTR)RT_STRING)) ||
1233 !(hmem = LoadResource(instance, hrsrc)))
1234 {
1235 TRACE( "Failed to load string.\n" );
1236 if (buflen > 0) buffer[0] = 0;
1237 return 0;
1238 }
1239
1240 p = LockResource(hmem);
1241 string_num = resource_id & 0x000f;
1242 for (i = 0; i < string_num; i++)
1243 p += *p + 1;
1244
1245 TRACE("strlen = %d\n", (int)*p );
1246
1247 /*if buflen == 0, then return a read-only pointer to the resource itself in buffer
1248 it is assumed that buffer is actually a (LPWSTR *) */
1249 if (buflen == 0)
1250 {
1251 *((LPWSTR *)buffer) = p + 1;
1252 return *p;
1253 }
1254
1255 i = min(buflen - 1, *p);
1256 memcpy(buffer, p + 1, i * sizeof(WCHAR));
1257 buffer[i] = 0;
1258
1259 TRACE("returning %s\n", debugstr_w(buffer));
1260 return i;
1261}
1262
1263INT WINAPI DECLSPEC_HOTPATCH LoadStringA(HINSTANCE instance, UINT resource_id, LPSTR buffer, INT buflen)
1264{
1265 DWORD retval = 0;
1266 HGLOBAL hmem;
1267 HRSRC hrsrc;
1268
1269 TRACE("instance = %p, id = %04x, buffer = %p, length = %d\n", instance, resource_id, buffer, buflen);
1270
1271 if (!buflen) return -1;
1272
1273 /* Use loword (incremented by 1) as resourceid */
1274 if ((hrsrc = FindResourceW(instance, MAKEINTRESOURCEW((LOWORD(resource_id) >> 4) + 1), (LPWSTR)RT_STRING )) &&
1275 (hmem = LoadResource(instance, hrsrc)))
1276 {
1277 const WCHAR *p = LockResource(hmem);
1278 unsigned int id = resource_id & 0x000f;
1279
1280 while (id--) p += *p + 1;
1281
1282 RtlUnicodeToMultiByteN(buffer, buflen - 1, &retval, p + 1, *p * sizeof(WCHAR));
1283 }
1284 buffer[retval] = 0;
1285 TRACE("returning %s\n", debugstr_a(buffer));
1286 return retval;
1287}
1288
1289int WINAPI StrCmpLogicalW(const WCHAR *str, const WCHAR *comp)
1290{
1291 TRACE("%s, %s\n", wine_dbgstr_w(str), wine_dbgstr_w(comp));
1292
1293 if (!str || !comp)
1294 return 0;
1295
1296 while (*str)
1297 {
1298 if (!*comp)
1299 return 1;
1300 else if (*str >= '0' && *str <= '9')
1301 {
1302 int str_value, comp_value;
1303
1304 if (*comp < '0' || *comp > '9')
1305 return -1;
1306
1307 /* Compare the numbers */
1308 StrToIntExW(str, 0, &str_value);
1309 StrToIntExW(comp, 0, &comp_value);
1310
1311 if (str_value < comp_value)
1312 return -1;
1313 else if (str_value > comp_value)
1314 return 1;
1315
1316 /* Skip */
1317 while (*str >= '0' && *str <= '9') str++;
1318 while (*comp >= '0' && *comp <= '9') comp++;
1319 }
1320 else if (*comp >= '0' && *comp <= '9')
1321 return 1;
1322 else
1323 {
1324 int diff = ChrCmpIW(*str, *comp);
1325 if (diff > 0)
1326 return 1;
1327 else if (diff < 0)
1328 return -1;
1329
1330 str++;
1331 comp++;
1332 }
1333 }
1334
1335 if (*comp)
1336 return -1;
1337
1338 return 0;
1339}
1340
1341BOOL WINAPI StrIsIntlEqualA(BOOL case_sensitive, const char *str, const char *cmp, int len)
1342{
1343 DWORD flags;
1344
1345 TRACE("%d, %s, %s, %d\n", case_sensitive, wine_dbgstr_a(str), wine_dbgstr_a(cmp), len);
1346
1347 /* FIXME: This flag is undocumented and unknown by our CompareString.
1348 * We need a define for it.
1349 */
1350 flags = 0x10000000;
1351 if (!case_sensitive)
1352 flags |= NORM_IGNORECASE;
1353
1354 return (CompareStringA(GetThreadLocale(), flags, str, len, cmp, len) == CSTR_EQUAL);
1355}
1356
1357BOOL WINAPI StrIsIntlEqualW(BOOL case_sensitive, const WCHAR *str, const WCHAR *cmp, int len)
1358{
1359 DWORD flags;
1360
1361 TRACE("%d, %s, %s, %d\n", case_sensitive, debugstr_w(str), debugstr_w(cmp), len);
1362
1363 /* FIXME: This flag is undocumented and unknown by our CompareString.
1364 * We need a define for it.
1365 */
1366 flags = 0x10000000;
1367 if (!case_sensitive)
1368 flags |= NORM_IGNORECASE;
1369
1370 return (CompareStringW(GetThreadLocale(), flags, str, len, cmp, len) == CSTR_EQUAL);
1371}
1372
1373char * WINAPI StrCatBuffA(char *str, const char *cat, INT max_len)
1374{
1375 INT len;
1376
1377 TRACE("%p, %s, %d\n", str, wine_dbgstr_a(cat), max_len);
1378
1379 if (!str)
1380 return NULL;
1381
1382 len = strlen(str);
1383 max_len -= len;
1384 if (max_len > 0)
1385 StrCpyNA(str + len, cat, max_len);
1386
1387 return str;
1388}
1389
1390WCHAR * WINAPI StrCatBuffW(WCHAR *str, const WCHAR *cat, INT max_len)
1391{
1392 INT len;
1393
1394 TRACE("%p, %s, %d\n", str, wine_dbgstr_w(cat), max_len);
1395
1396 if (!str)
1397 return NULL;
1398
1399 len = lstrlenW(str);
1400 max_len -= len;
1401 if (max_len > 0)
1402 StrCpyNW(str + len, cat, max_len);
1403
1404 return str;
1405}
1406
1407DWORD WINAPI StrCatChainW(WCHAR *str, DWORD max_len, DWORD at, const WCHAR *cat)
1408{
1409 TRACE("%s, %lu, %ld, %s\n", wine_dbgstr_w(str), max_len, at, wine_dbgstr_w(cat));
1410
1411 if (at == -1)
1412 at = lstrlenW(str);
1413
1414 if (!max_len)
1415 return at;
1416
1417 if (at == max_len)
1418 at--;
1419
1420 if (cat && at < max_len)
1421 {
1422 str += at;
1423 while (at < max_len - 1 && *cat)
1424 {
1425 *str++ = *cat++;
1426 at++;
1427 }
1428 *str = 0;
1429 }
1430
1431 return at;
1432}
1433
1434DWORD WINAPI SHTruncateString(char *str, DWORD size)
1435{
1436 char *last_byte;
1437
1438 if (!str || !size)
1439 return 0;
1440
1441 last_byte = str + size - 1;
1442
1443 while (str < last_byte)
1444 str += IsDBCSLeadByte(*str) ? 2 : 1;
1445
1446 if (str == last_byte && IsDBCSLeadByte(*str))
1447 {
1448 *str = '\0';
1449 size--;
1450 }
1451
1452 return size;
1453}
1454
1455HRESULT WINAPI SHLoadIndirectString(const WCHAR *src, WCHAR *dst, UINT dst_len, void **reserved)
1456{
1457 WCHAR *dllname = NULL;
1458 HMODULE hmod = NULL;
1459 HRESULT hr = E_FAIL;
1460
1461 TRACE("%s, %p, %#x, %p\n", debugstr_w(src), dst, dst_len, reserved);
1462
1463 if (src[0] == '@')
1464 {
1465 WCHAR *index_str;
1466 int index;
1467
1468 dst[0] = 0;
1469 dllname = StrDupW(src + 1);
1470 index_str = wcschr(dllname, ',');
1471
1472 if(!index_str) goto end;
1473
1474 *index_str = 0;
1475 index_str++;
1476 index = wcstol(index_str, NULL, 10);
1477
1478 hmod = LoadLibraryW(dllname);
1479 if (!hmod) goto end;
1480
1481 if (index < 0)
1482 {
1483 if (LoadStringW(hmod, -index, dst, dst_len))
1484 hr = S_OK;
1485 }
1486 else
1487 FIXME("can't handle non-negative indices (%d)\n", index);
1488 }
1489 else
1490 {
1491 if (dst != src)
1492 lstrcpynW(dst, src, dst_len);
1493 hr = S_OK;
1494 }
1495
1496 TRACE("returning %s\n", debugstr_w(dst));
1497end:
1498 if (hmod) FreeLibrary(hmod);
1499 LocalFree(dllname);
1500 return hr;
1501}