Implemented SHCreateStdEnumFmtEtc.
[wine] / dlls / shlwapi / string.c
1 /*
2  * Shlwapi string functions
3  *
4  * Copyright 1998 Juergen Schmied
5  * Copyright 2002 Jon Griffiths
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21
22 #include "config.h"
23 #include "wine/port.h"
24
25 #include <ctype.h>
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <string.h>
29
30 #include "winerror.h"
31 #include "windef.h"
32 #include "winbase.h"
33 #include "wingdi.h"
34 #include "winuser.h"
35 #include "winreg.h"
36 #define NO_SHLWAPI_STREAM
37 #include "shlwapi.h"
38 #include "shlobj.h"
39 #include "wine/unicode.h"
40 #include "wine/debug.h"
41
42 WINE_DEFAULT_DEBUG_CHANNEL(shell);
43
44 static HRESULT WINAPI _SHStrDupAA(LPCSTR src, LPSTR * dest);
45 static HRESULT WINAPI _SHStrDupAW(LPCWSTR src, LPSTR * dest);
46
47 /*************************************************************************
48  * SHLWAPI_ChrCmpHelperA
49  *
50  * Internal helper for SHLWAPI_ChrCmpA/ChrCMPIA.
51  *
52  * NOTES
53  *  Both this function and its Unicode counterpart are very inneficient. To
54  *  fix this, CompareString must be completely implemented and optimised
55  *  first. Then the core character test can be taken out of that function and
56  *  placed here, so that it need never be called at all. Until then, do not
57  *  attempt to optimise this code unless you are willing to test that it
58  *  still performs correctly.
59  */
60 static BOOL WINAPI SHLWAPI_ChrCmpHelperA(WORD ch1, WORD ch2, DWORD dwFlags)
61 {
62   char str1[3], str2[3];
63
64   str1[0] = LOBYTE(ch1);
65   if (IsDBCSLeadByte(ch1))
66   {
67     str1[1] = HIBYTE(ch1);
68     str1[2] = '\0';
69   }
70   else
71     str1[1] = '\0';
72
73   str2[0] = LOBYTE(ch2);
74   if (IsDBCSLeadByte(ch2))
75   {
76     str2[1] = HIBYTE(ch2);
77     str2[2] = '\0';
78   }
79   else
80     str2[1] = '\0';
81
82   return CompareStringA(GetThreadLocale(), dwFlags, str1, -1, str2, -1) - 2;
83 }
84
85 /*************************************************************************
86  * SHLWAPI_ChrCmpHelperW
87  *
88  * Internal helper for SHLWAPI_ChrCmpW/ChrCmpIW.
89  */
90 static BOOL WINAPI SHLWAPI_ChrCmpHelperW(WCHAR ch1, WCHAR ch2, DWORD dwFlags)
91 {
92   WCHAR str1[2], str2[2];
93
94   str1[0] = ch1;
95   str1[1] = '\0';
96   str2[0] = ch2;
97   str2[1] = '\0';
98   return CompareStringW(GetThreadLocale(), dwFlags, str1, 2, str2, 2) - 2;
99 }
100
101 /*************************************************************************
102  * SHLWAPI_ChrCmpA
103  *
104  * Internal helper function.
105  */
106 static BOOL WINAPI SHLWAPI_ChrCmpA(WORD ch1, WORD ch2)
107 {
108   return SHLWAPI_ChrCmpHelperA(ch1, ch2, 0);
109 }
110
111 /*************************************************************************
112  * ChrCmpIA     [SHLWAPI.385]
113  *
114  * Compare two characters, ignoring case.
115  *
116  * PARAMS
117  *  ch1 [I] First character to compare
118  *  ch2 [I] Second character to compare
119  *
120  * RETURNS
121  *  FALSE, if the characters are equal.
122  *  Non-zero otherwise.
123  */
124 BOOL WINAPI ChrCmpIA(WORD ch1, WORD ch2)
125 {
126   TRACE("(%d,%d)\n", ch1, ch2);
127
128   return SHLWAPI_ChrCmpHelperA(ch1, ch2, NORM_IGNORECASE);
129 }
130
131 /*************************************************************************
132  * SHLWAPI_ChrCmpW
133  *
134  * Internal helper function.
135  */
136 static BOOL WINAPI SHLWAPI_ChrCmpW(WCHAR ch1, WCHAR ch2)
137 {
138   return SHLWAPI_ChrCmpHelperW(ch1, ch2, 0);
139 }
140
141 /*************************************************************************
142  * ChrCmpIW     [SHLWAPI.386]
143  *
144  * See ChrCmpIA.
145  */
146 BOOL WINAPI ChrCmpIW(WCHAR ch1, WCHAR ch2)
147 {
148   return SHLWAPI_ChrCmpHelperW(ch1, ch2, NORM_IGNORECASE);
149 }
150
151 /*************************************************************************
152  * StrChrA      [SHLWAPI.@]
153  *
154  * Find a given character in a string.
155  *
156  * PARAMS
157  *  lpszStr [I] String to search in.
158  *  ch      [I] Character to search for.
159  *
160  * RETURNS
161  *  Success: A pointer to the first occurrence of ch in lpszStr, or NULL if
162  *           not found.
163  *  Failure: NULL, if any arguments are invalid.
164  */
165 LPSTR WINAPI StrChrA(LPCSTR lpszStr, WORD ch)
166 {
167   TRACE("(%s,%i)\n", debugstr_a(lpszStr), ch);
168
169   if (lpszStr)
170   {
171     while (*lpszStr)
172     {
173       if (!SHLWAPI_ChrCmpA(*lpszStr, ch))
174         return (LPSTR)lpszStr;
175       lpszStr = CharNextA(lpszStr);
176     }
177   }
178   return NULL;
179 }
180
181 /*************************************************************************
182  * StrChrW      [SHLWAPI.@]
183  *
184  * See StrChrA.
185  */
186 LPWSTR WINAPI StrChrW(LPCWSTR lpszStr, WCHAR ch)
187 {
188   LPWSTR lpszRet = NULL;
189
190   TRACE("(%s,%i)\n", debugstr_w(lpszStr), ch);
191
192   if (lpszStr)
193     lpszRet = strchrW(lpszStr, ch);
194   return lpszRet;
195 }
196
197 /*************************************************************************
198  * StrChrIA     [SHLWAPI.@]
199  *
200  * Find a given character in a string, ignoring case.
201  *
202  * PARAMS
203  *  lpszStr [I] String to search in.
204  *  ch      [I] Character to search for.
205  *
206  * RETURNS
207  *  Success: A pointer to the first occurrence of ch in lpszStr, or NULL if
208  *           not found.
209  *  Failure: NULL, if any arguments are invalid.
210  */
211 LPSTR WINAPI StrChrIA(LPCSTR lpszStr, WORD ch)
212 {
213   TRACE("(%s,%i)\n", debugstr_a(lpszStr), ch);
214
215   if (lpszStr)
216   {
217     while (*lpszStr)
218     {
219       if (!ChrCmpIA(*lpszStr, ch))
220         return (LPSTR)lpszStr;
221       lpszStr = CharNextA(lpszStr);
222     }
223   }
224   return NULL;
225 }
226
227 /*************************************************************************
228  * StrChrIW     [SHLWAPI.@]
229  *
230  * See StrChrA.
231  */
232 LPWSTR WINAPI StrChrIW(LPCWSTR lpszStr, WCHAR ch)
233 {
234   TRACE("(%s,%i)\n", debugstr_w(lpszStr), ch);
235
236   if (lpszStr)
237   {
238     ch = toupperW(ch);
239     while (*lpszStr)
240     {
241       if (toupperW(*lpszStr) == ch)
242         return (LPWSTR)lpszStr;
243       lpszStr = CharNextW(lpszStr);
244     }
245     lpszStr = NULL;
246   }
247   return (LPWSTR)lpszStr;
248 }
249
250 /*************************************************************************
251  * StrCmpIW     [SHLWAPI.@]
252  *
253  * Compare two strings, ignoring case.
254  *
255  * PARAMS
256  *  lpszStr  [I] First string to compare
257  *  lpszComp [I] Second string to compare
258  *
259  * RETURNS
260  *  An integer less than, equal to or greater than 0, indicating that
261  *  lpszStr is less than, the same, or greater than lpszComp.
262  */
263 int WINAPI StrCmpIW(LPCWSTR lpszStr, LPCWSTR lpszComp)
264 {
265   INT iRet;
266
267   TRACE("(%s,%s)\n", debugstr_w(lpszStr),debugstr_w(lpszComp));
268
269   iRet = strcmpiW(lpszStr, lpszComp);
270   return iRet < 0 ? -1 : iRet ? 1 : 0;
271 }
272
273 /*************************************************************************
274  * SHLWAPI_StrCmpNHelperA
275  *
276  * Internal helper for StrCmpNA/StrCmpNIA.
277  */
278 static INT WINAPI SHLWAPI_StrCmpNHelperA(LPCSTR lpszStr, LPCSTR lpszComp,
279                                          INT iLen,
280                                          BOOL (WINAPI *pChrCmpFn)(WORD,WORD))
281 {
282   if (!lpszStr)
283   {
284     if (!lpszComp)
285       return 0;
286     return 1;
287   }
288   else if (!lpszComp)
289     return -1;
290
291   while (iLen-- > 0)
292   {
293     int iDiff;
294     WORD ch1, ch2;
295
296     ch1 = IsDBCSLeadByte(*lpszStr)? *lpszStr << 8 | lpszStr[1] : *lpszStr;
297     ch2 = IsDBCSLeadByte(*lpszComp)? *lpszComp << 8 | lpszComp[1] : *lpszComp;
298
299     if ((iDiff = pChrCmpFn(ch1, ch2)) < 0)
300       return -1;
301     else if (iDiff > 0)
302       return 1;
303     else if (!*lpszStr && !*lpszComp)
304       return 0;
305
306     lpszStr = CharNextA(lpszStr);
307     lpszComp = CharNextA(lpszComp);
308   }
309   return 0;
310 }
311
312 /*************************************************************************
313  * StrCmpNA     [SHLWAPI.@]
314  *
315  * Compare two strings, up to a maximum length.
316  *
317  * PARAMS
318  *  lpszStr  [I] First string to compare
319  *  lpszComp [I] Second string to compare
320  *  iLen     [I] Maximum number of chars to compare.
321  *
322  * RETURNS
323  *  An integer less than, equal to or greater than 0, indicating that
324  *  lpszStr is less than, the same, or greater than lpszComp.
325  */
326 INT WINAPI StrCmpNA(LPCSTR lpszStr, LPCSTR lpszComp, INT iLen)
327 {
328   TRACE("(%s,%s,%i)\n", debugstr_a(lpszStr), debugstr_a(lpszComp), iLen);
329
330   return SHLWAPI_StrCmpNHelperA(lpszStr, lpszComp, iLen, SHLWAPI_ChrCmpA);
331 }
332
333 /*************************************************************************
334  * StrCmpNW     [SHLWAPI.@]
335  *
336  * See StrCmpNA.
337  */
338 INT WINAPI StrCmpNW(LPCWSTR lpszStr, LPCWSTR lpszComp, INT iLen)
339 {
340   INT iRet;
341
342   TRACE("(%s,%s,%i)\n", debugstr_w(lpszStr), debugstr_w(lpszComp), iLen);
343
344   iRet = strncmpW(lpszStr, lpszComp, iLen);
345   return iRet < 0 ? -1 : iRet ? 1 : 0;
346 }
347
348 /*************************************************************************
349  * StrCmpNIA    [SHLWAPI.@]
350  *
351  * Compare two strings, up to a maximum length, ignoring case.
352  *
353  * PARAMS
354  *  lpszStr  [I] First string to compare
355  *  lpszComp [I] Second string to compare
356  *  iLen     [I] Maximum number of chars to compare.
357  *
358  * RETURNS
359  *  An integer less than, equal to or greater than 0, indicating that
360  *  lpszStr is less than, the same, or greater than lpszComp.
361  *
362  * NOTES
363  *  The Win32 version of this function is _completely_ broken for cases
364  *  where iLen is greater than the length of lpszComp. Examples:
365  *
366  *  StrCmpNIA("foo.gif", "foo", 5) is -1 under Win32; Should return 1.
367  *  StrCmpNIA("\", "\\", 3) is 0 under Win32; Should return -1.
368  *  StrCmpNIA("\", "\..\foo\", 3) is 1 under Win32; Should return -1.
369  *
370  *  This implementation behaves correctly, since it is unlikely any
371  *  applications actually rely on this function being broken.
372  */
373 int WINAPI StrCmpNIA(LPCSTR lpszStr, LPCSTR lpszComp, int iLen)
374 {
375   TRACE("(%s,%s,%i)\n", debugstr_a(lpszStr), debugstr_a(lpszComp), iLen);
376
377   return SHLWAPI_StrCmpNHelperA(lpszStr, lpszComp, iLen, ChrCmpIA);
378 }
379
380 /*************************************************************************
381  * StrCmpNIW    [SHLWAPI.@]
382  *
383  * See StrCmpNIA.
384  */
385 INT WINAPI StrCmpNIW(LPCWSTR lpszStr, LPCWSTR lpszComp, int iLen)
386 {
387   INT iRet;
388
389   TRACE("(%s,%s,%i)\n", debugstr_w(lpszStr), debugstr_w(lpszComp), iLen);
390
391   iRet = strncmpiW(lpszStr, lpszComp, iLen);
392   return iRet < 0 ? -1 : iRet ? 1 : 0;
393 }
394
395 /*************************************************************************
396  * StrCmpW      [SHLWAPI.@]
397  *
398  * Compare two strings.
399  *
400  * PARAMS
401  *  lpszStr  [I] First string to compare
402  *  lpszComp [I] Second string to compare
403  *
404  * RETURNS
405  *  An integer less than, equal to or greater than 0, indicating that
406  *  lpszStr is less than, the same, or greater than lpszComp.
407  */
408 int WINAPI StrCmpW(LPCWSTR lpszStr, LPCWSTR lpszComp)
409 {
410   INT iRet;
411
412   TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszComp));
413
414   iRet = strcmpW(lpszStr, lpszComp);
415   return iRet < 0 ? -1 : iRet ? 1 : 0;
416 }
417
418 /*************************************************************************
419  * StrCatW      [SHLWAPI.@]
420  *
421  * Concatanate two strings.
422  *
423  * PARAMS
424  *  lpszStr [O] Initial string
425  *  lpszSrc [I] String to concatanate
426  *
427  * RETURNS
428  *  lpszStr.
429  */
430 LPWSTR WINAPI StrCatW(LPWSTR lpszStr, LPCWSTR lpszSrc)
431 {
432   TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszSrc));
433
434   strcatW(lpszStr, lpszSrc);
435   return lpszStr;
436 }
437
438 /*************************************************************************
439  * StrCpyW      [SHLWAPI.@]
440  *
441  * Copy a string to another string.
442  *
443  * PARAMS
444  *  lpszStr [O] Destination string
445  *  lpszSrc [I] Source string
446  *
447  * RETURNS
448  *  lpszStr.
449  */
450 LPWSTR WINAPI StrCpyW(LPWSTR lpszStr, LPCWSTR lpszSrc)
451 {
452   TRACE("(%p,%s)\n", lpszStr, debugstr_w(lpszSrc));
453
454   strcpyW(lpszStr, lpszSrc);
455   return lpszStr;
456 }
457
458 /*************************************************************************
459  * StrCpyNW     [SHLWAPI.@]
460  *
461  * Copy a string to another string, up to a maximum number of characters.
462  *
463  * PARAMS
464  *  lpszStr  [O] Destination string
465  *  lpszSrc  [I] Source string
466  *  iLen     [I] Maximum number of chars to copy
467  *
468  * RETURNS
469  *  lpszStr.
470  */
471 LPWSTR WINAPI StrCpyNW(LPWSTR lpszStr, LPCWSTR lpszSrc, int iLen)
472 {
473   TRACE("(%p,%s,%i)\n", lpszStr, debugstr_w(lpszSrc), iLen);
474
475   lstrcpynW(lpszStr, lpszSrc, iLen);
476   return lpszStr;
477 }
478
479
480
481 /*************************************************************************
482  * SHLWAPI_StrStrHelperA
483  *
484  * Internal implementation of StrStrA/StrStrIA
485  */
486 static LPSTR WINAPI SHLWAPI_StrStrHelperA(LPCSTR lpszStr, LPCSTR lpszSearch,
487                                          int (*pStrCmpFn)(LPCSTR,LPCSTR,size_t))
488 {
489   size_t iLen;
490
491   if (!lpszStr || !lpszSearch || !*lpszSearch)
492     return NULL;
493
494   iLen = strlen(lpszSearch);
495
496   while (*lpszStr)
497   {
498     if (!pStrCmpFn(lpszStr, lpszSearch, iLen))
499       return (LPSTR)lpszStr;
500     lpszStr = CharNextA(lpszStr);
501   }
502   return NULL;
503 }
504
505 /*************************************************************************
506  * SHLWAPI_StrStrHelperW
507  *
508  * Internal implementation of StrStrW/StrStrIW
509  */
510 static LPWSTR WINAPI SHLWAPI_StrStrHelperW(LPCWSTR lpszStr, LPCWSTR lpszSearch,
511                                           int (*pStrCmpFn)(LPCWSTR,LPCWSTR,int))
512 {
513   int iLen;
514
515   if (!lpszStr || !lpszSearch || !*lpszSearch)
516     return NULL;
517
518   iLen = strlenW(lpszSearch);
519
520   while (*lpszStr)
521   {
522     if (!pStrCmpFn(lpszStr, lpszSearch, iLen))
523       return (LPWSTR)lpszStr;
524     lpszStr = CharNextW(lpszStr);
525   }
526   return NULL;
527 }
528
529 /*************************************************************************
530  * StrStrA      [SHLWAPI.@]
531  *
532  * Find a substring within a string.
533  *
534  * PARAMS
535  *  lpszStr    [I] String to search in
536  *  lpszSearch [I] String to look for
537  *
538  * RETURNS
539  *  The start of lpszSearch within lpszStr, or NULL if not found.
540  */
541 LPSTR WINAPI StrStrA(LPCSTR lpszStr, LPCSTR lpszSearch)
542 {
543   TRACE("(%s,%s)\n", debugstr_a(lpszStr), debugstr_a(lpszSearch));
544
545   return SHLWAPI_StrStrHelperA(lpszStr, lpszSearch, strncmp);
546 }
547
548 /*************************************************************************
549  * StrStrW      [SHLWAPI.@]
550  *
551  * See StrStrA.
552  */
553 LPWSTR WINAPI StrStrW(LPCWSTR lpszStr, LPCWSTR lpszSearch)
554 {
555   TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszSearch));
556
557   return SHLWAPI_StrStrHelperW(lpszStr, lpszSearch, strncmpW);
558 }
559
560 /*************************************************************************
561  * StrRStrIA    [SHLWAPI.@]
562  *
563  * Find the last occurence of a substring within a string.
564  *
565  * PARAMS
566  *  lpszStr    [I] String to search in
567  *  lpszEnd    [I] End of lpszStr
568  *  lpszSearch [I] String to look for
569  *
570  * RETURNS
571  *  The last occurence lpszSearch within lpszStr, or NULL if not found.
572  */
573 LPSTR WINAPI StrRStrIA(LPCSTR lpszStr, LPCSTR lpszEnd, LPCSTR lpszSearch)
574 {
575   LPSTR lpszRet = NULL;
576   WORD ch1, ch2;
577   INT iLen;
578
579   TRACE("(%s,%s)\n", debugstr_a(lpszStr), debugstr_a(lpszSearch));
580
581   if (!lpszStr || !lpszSearch || !*lpszSearch)
582     return NULL;
583
584   if (!lpszEnd)
585     lpszEnd = lpszStr + lstrlenA(lpszStr);
586
587   if (IsDBCSLeadByte(*lpszSearch))
588     ch1 = *lpszSearch << 8 | lpszSearch[1];
589   else
590     ch1 = *lpszSearch;
591   iLen = lstrlenA(lpszSearch);
592
593   while (lpszStr <= lpszEnd  && *lpszStr)
594   {
595     ch2 = IsDBCSLeadByte(*lpszStr)? *lpszStr << 8 | lpszStr[1] : *lpszStr;
596     if (!ChrCmpIA(ch1, ch2))
597     {
598       if (!StrCmpNIA(lpszStr, lpszSearch, iLen))
599         lpszRet = (LPSTR)lpszStr;
600     }
601     lpszStr = CharNextA(lpszStr);
602   }
603   return lpszRet;
604 }
605
606 /*************************************************************************
607  * StrRStrIW    [SHLWAPI.@]
608  *
609  * See StrRStrIA.
610  */
611 LPWSTR WINAPI StrRStrIW(LPCWSTR lpszStr, LPCWSTR lpszEnd, LPCWSTR lpszSearch)
612 {
613   LPWSTR lpszRet = NULL;
614   INT iLen;
615
616   TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszSearch));
617
618   if (!lpszStr || !lpszSearch || !*lpszSearch)
619     return NULL;
620
621   if (!lpszEnd)
622     lpszEnd = lpszStr + strlenW(lpszStr);
623
624   iLen = strlenW(lpszSearch);
625
626   while (lpszStr <= lpszEnd  && *lpszStr)
627   {
628     if (!ChrCmpIA(*lpszSearch, *lpszStr))
629     {
630       if (!StrCmpNIW(lpszStr, lpszSearch, iLen))
631         lpszRet = (LPWSTR)lpszStr;
632     }
633     lpszStr = CharNextW(lpszStr);
634   }
635   return lpszRet;
636 }
637
638 /*************************************************************************
639  * StrStrIA     [SHLWAPI.@]
640  *
641  * Find a substring within a string, ignoring case.
642  *
643  * PARAMS
644  *  lpszStr    [I] String to search in
645  *  lpszSearch [I] String to look for
646  *
647  * RETURNS
648  *  The start of lpszSearch within lpszStr, or NULL if not found.
649  */
650 LPSTR WINAPI StrStrIA(LPCSTR lpszStr, LPCSTR lpszSearch)
651 {
652   TRACE("(%s,%s)\n", debugstr_a(lpszStr), debugstr_a(lpszSearch));
653
654   return SHLWAPI_StrStrHelperA(lpszStr, lpszSearch, strncasecmp);
655 }
656
657 /*************************************************************************
658  * StrStrIW     [SHLWAPI.@]
659  *
660  * See StrStrIA.
661  */
662 LPWSTR WINAPI StrStrIW(LPCWSTR lpszStr, LPCWSTR lpszSearch)
663 {
664   TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszSearch));
665
666   return SHLWAPI_StrStrHelperW(lpszStr, lpszSearch, strncmpiW);
667 }
668
669 /*************************************************************************
670  * StrToIntA    [SHLWAPI.@]
671  *
672  * Read an integer from a string.
673  *
674  * PARAMS
675  *  lpszStr [I] String to read integer from
676  *
677  * RETURNS
678  *   The integer value represented by the string, or 0 if no integer is
679  *   present.
680  *
681  * NOTES
682  *  No leading space is allowed before the number, although a leading '-' is.
683  */
684 int WINAPI StrToIntA(LPCSTR lpszStr)
685 {
686   int iRet = 0;
687
688   TRACE("(%s)\n", debugstr_a(lpszStr));
689
690   if (!lpszStr)
691   {
692     WARN("Invalid lpszStr would crash under Win32!\n");
693     return 0;
694   }
695
696   if (*lpszStr == '-' || isdigit(*lpszStr))
697     StrToIntExA(lpszStr, 0, &iRet);
698   return iRet;
699 }
700
701 /*************************************************************************
702  * StrToIntW    [SHLWAPI.@]
703  *
704  * See StrToIntA.
705  */
706 int WINAPI StrToIntW(LPCWSTR lpszStr)
707 {
708   int iRet = 0;
709
710   TRACE("(%s)\n", debugstr_w(lpszStr));
711
712   if (!lpszStr)
713   {
714     WARN("Invalid lpszStr would crash under Win32!\n");
715     return 0;
716   }
717
718   if (*lpszStr == '-' || isdigitW(*lpszStr))
719     StrToIntExW(lpszStr, 0, &iRet);
720   return iRet;
721 }
722
723 /*************************************************************************
724  * StrToIntExA  [SHLWAPI.@]
725  *
726  * Read an integer from a string.
727  *
728  * PARAMS
729  *  lpszStr [I] String to read integer from
730  *  dwFlags [I] Flags controlling the conversion
731  *  lpiRet  [O] Destination for read integer.
732  *
733  * RETURNS
734  *  Success: TRUE. lpiRet contains the integer value represented by the string.
735  *  Failure: FALSE, if the string is invalid, or no number is present.
736  *
737  * NOTES
738  *  Leading whitespace, '-' and '+' are allowed before the number. If
739  *  dwFlags includes STIF_SUPPORT_HEX, hexidecimal numbers are allowed, if
740  *  preceeded by '0x'. If this flag is not set, or there is no '0x' prefix,
741  *  the string is treated as a decimal string. A leading '-' is ignored for
742  *  hexidecimal numbers.
743  */
744 BOOL WINAPI StrToIntExA(LPCSTR lpszStr, DWORD dwFlags, LPINT lpiRet)
745 {
746   BOOL bNegative = FALSE;
747   int iRet = 0;
748
749   TRACE("(%s,%08lX,%p)\n", debugstr_a(lpszStr), dwFlags, lpiRet);
750
751   if (!lpszStr || !lpiRet)
752   {
753     WARN("Invalid parameter would crash under Win32!\n");
754     return FALSE;
755   }
756   if (dwFlags > STIF_SUPPORT_HEX)
757   {
758     WARN("Unknown flags (%08lX)!\n", dwFlags & ~STIF_SUPPORT_HEX);
759   }
760
761   /* Skip leading space, '+', '-' */
762   while (isspace(*lpszStr))
763     lpszStr = CharNextA(lpszStr);
764
765   if (*lpszStr == '-')
766   {
767     bNegative = TRUE;
768     lpszStr++;
769   }
770   else if (*lpszStr == '+')
771     lpszStr++;
772
773   if (dwFlags & STIF_SUPPORT_HEX &&
774       *lpszStr == '0' && tolower(lpszStr[1]) == 'x')
775   {
776     /* Read hex number */
777     lpszStr += 2;
778
779     if (!isxdigit(*lpszStr))
780       return FALSE;
781
782     while (isxdigit(*lpszStr))
783     {
784       iRet = iRet * 16;
785       if (isdigit(*lpszStr))
786         iRet += (*lpszStr - '0');
787       else
788         iRet += 10 + (tolower(*lpszStr) - 'a');
789       lpszStr++;
790     }
791     *lpiRet = iRet;
792     return TRUE;
793   }
794
795   /* Read decimal number */
796   if (!isdigit(*lpszStr))
797     return FALSE;
798
799   while (isdigit(*lpszStr))
800   {
801     iRet = iRet * 10;
802     iRet += (*lpszStr - '0');
803     lpszStr++;
804   }
805   *lpiRet = bNegative ? -iRet : iRet;
806   return TRUE;
807 }
808
809 /*************************************************************************
810  * StrToIntExW  [SHLWAPI.@]
811  *
812  * See StrToIntExA.
813  */
814 BOOL WINAPI StrToIntExW(LPCWSTR lpszStr, DWORD dwFlags, LPINT lpiRet)
815 {
816   BOOL bNegative = FALSE;
817   int iRet = 0;
818
819   TRACE("(%s,%08lX,%p)\n", debugstr_w(lpszStr), dwFlags, lpiRet);
820
821   if (!lpszStr || !lpiRet)
822   {
823     WARN("Invalid parameter would crash under Win32!\n");
824     return FALSE;
825   }
826   if (dwFlags > STIF_SUPPORT_HEX)
827   {
828     WARN("Unknown flags (%08lX)!\n", dwFlags & ~STIF_SUPPORT_HEX);
829   }
830
831   /* Skip leading space, '+', '-' */
832   while (isspaceW(*lpszStr))
833     lpszStr = CharNextW(lpszStr);
834
835   if (*lpszStr == '-')
836   {
837     bNegative = TRUE;
838     lpszStr++;
839   }
840   else if (*lpszStr == '+')
841     lpszStr++;
842
843   if (dwFlags & STIF_SUPPORT_HEX &&
844       *lpszStr == '0' && tolowerW(lpszStr[1]) == 'x')
845   {
846     /* Read hex number */
847     lpszStr += 2;
848
849     if (!isxdigitW(*lpszStr))
850       return FALSE;
851
852     while (isxdigitW(*lpszStr))
853     {
854       iRet = iRet * 16;
855       if (isdigitW(*lpszStr))
856         iRet += (*lpszStr - '0');
857       else
858         iRet += 10 + (tolowerW(*lpszStr) - 'a');
859       lpszStr++;
860     }
861     *lpiRet = iRet;
862     return TRUE;
863   }
864
865   /* Read decimal number */
866   if (!isdigitW(*lpszStr))
867     return FALSE;
868
869   while (isdigitW(*lpszStr))
870   {
871     iRet = iRet * 10;
872     iRet += (*lpszStr - '0');
873     lpszStr++;
874   }
875   *lpiRet = bNegative ? -iRet : iRet;
876   return TRUE;
877 }
878
879 /*************************************************************************
880  * StrDupA      [SHLWAPI.@]
881  *
882  * Duplicate a string.
883  *
884  * PARAMS
885  *  lpszStr [I] String to duplicate.
886  *
887  * RETURNS
888  *  Success: A pointer to a new string containing the contents of lpszStr
889  *  Failure: NULL, if memory cannot be allocated
890  *
891  * NOTES
892  *  The string memory is allocated with LocalAlloc, and so should be released
893  *  by calling LocalFree.
894  */
895 LPSTR WINAPI StrDupA(LPCSTR lpszStr)
896 {
897   int iLen;
898   LPSTR lpszRet;
899
900   TRACE("(%s)\n",debugstr_a(lpszStr));
901
902   iLen = lpszStr ? strlen(lpszStr) + 1 : 1;
903   lpszRet = (LPSTR)LocalAlloc(LMEM_FIXED, iLen);
904
905   if (lpszRet)
906   {
907     if (lpszStr)
908       memcpy(lpszRet, lpszStr, iLen);
909     else
910       *lpszRet = '\0';
911   }
912   return lpszRet;
913 }
914
915 /*************************************************************************
916  * StrDupW      [SHLWAPI.@]
917  *
918  * See StrDupA.
919  */
920 LPWSTR WINAPI StrDupW(LPCWSTR lpszStr)
921 {
922   int iLen;
923   LPWSTR lpszRet;
924
925   TRACE("(%s)\n",debugstr_w(lpszStr));
926
927   iLen = (lpszStr ? strlenW(lpszStr) + 1 : 1) * sizeof(WCHAR);
928   lpszRet = (LPWSTR)LocalAlloc(LMEM_FIXED, iLen);
929
930   if (lpszRet)
931   {
932     if (lpszStr)
933       memcpy(lpszRet, lpszStr, iLen);
934     else
935       *lpszRet = '\0';
936   }
937   return lpszRet;
938 }
939
940 /*************************************************************************
941  * SHLWAPI_StrSpnHelperA
942  *
943  * Internal implementation of StrSpnA/StrCSpnA/StrCSpnIA
944  */
945 static int WINAPI SHLWAPI_StrSpnHelperA(LPCSTR lpszStr, LPCSTR lpszMatch,
946                                         LPSTR (WINAPI *pStrChrFn)(LPCSTR,WORD),
947                                         BOOL bInvert)
948 {
949   LPCSTR lpszRead = lpszStr;
950   if (lpszStr && *lpszStr && lpszMatch)
951   {
952     while (*lpszRead)
953     {
954       LPCSTR lpszTest = pStrChrFn(lpszMatch, *lpszRead);
955
956       if (!bInvert && !lpszTest)
957         break;
958       if (bInvert && lpszTest)
959         break;
960       lpszRead = CharNextA(lpszRead);
961     };
962   }
963   return lpszRead - lpszStr;
964 }
965
966 /*************************************************************************
967  * SHLWAPI_StrSpnHelperW
968  *
969  * Internal implementation of StrSpnW/StrCSpnW/StrCSpnIW
970  */
971 static int WINAPI SHLWAPI_StrSpnHelperW(LPCWSTR lpszStr, LPCWSTR lpszMatch,
972                                       LPWSTR (WINAPI *pStrChrFn)(LPCWSTR,WCHAR),
973                                       BOOL bInvert)
974 {
975   LPCWSTR lpszRead = lpszStr;
976   if (lpszStr && *lpszStr && lpszMatch)
977   {
978     while (*lpszRead)
979     {
980       LPCWSTR lpszTest = pStrChrFn(lpszMatch, *lpszRead);
981
982       if (!bInvert && !lpszTest)
983         break;
984       if (bInvert && lpszTest)
985         break;
986       lpszRead = CharNextW(lpszRead);
987     };
988   }
989   return lpszRead - lpszStr;
990 }
991
992 /*************************************************************************
993  * StrSpnA      [SHLWAPI.@]
994  *
995  * Find the length of the start of a string that contains only certain
996  * characters.
997  *
998  * PARAMS
999  *  lpszStr   [I] String to search
1000  *  lpszMatch [I] Characters that can be in the substring
1001  *
1002  * RETURNS
1003  *  The length of the part of lpszStr containing only chars from lpszMatch,
1004  *  or 0 if any parameter is invalid.
1005  */
1006 int WINAPI StrSpnA(LPCSTR lpszStr, LPCSTR lpszMatch)
1007 {
1008   TRACE("(%s,%s)\n",debugstr_a(lpszStr), debugstr_a(lpszMatch));
1009
1010   return SHLWAPI_StrSpnHelperA(lpszStr, lpszMatch, StrChrA, FALSE);
1011 }
1012
1013 /*************************************************************************
1014  * StrSpnW      [SHLWAPI.@]
1015  *
1016  * See StrSpnA.
1017  */
1018 int WINAPI StrSpnW(LPCWSTR lpszStr, LPCWSTR lpszMatch)
1019 {
1020   TRACE("(%s,%s)\n",debugstr_w(lpszStr), debugstr_w(lpszMatch));
1021
1022   return SHLWAPI_StrSpnHelperW(lpszStr, lpszMatch, StrChrW, FALSE);
1023 }
1024
1025 /*************************************************************************
1026  * StrCSpnA     [SHLWAPI.@]
1027  *
1028  * Find the length of the start of a string that does not contain certain
1029  * characters.
1030  *
1031  * PARAMS
1032  *  lpszStr   [I] String to search
1033  *  lpszMatch [I] Characters that cannot be in the substring
1034  *
1035  * RETURNS
1036  *  The length of the part of lpszStr containing only chars not in lpszMatch,
1037  *  or 0 if any parameter is invalid.
1038  */
1039 int WINAPI StrCSpnA(LPCSTR lpszStr, LPCSTR lpszMatch)
1040 {
1041   TRACE("(%s,%s)\n",debugstr_a(lpszStr), debugstr_a(lpszMatch));
1042
1043   return SHLWAPI_StrSpnHelperA(lpszStr, lpszMatch, StrChrA, TRUE);
1044 }
1045
1046 /*************************************************************************
1047  * StrCSpnW     [SHLWAPI.@]
1048  *
1049  * See StrCSpnA.
1050  */
1051 int WINAPI StrCSpnW(LPCWSTR lpszStr, LPCWSTR lpszMatch)
1052 {
1053   TRACE("(%s,%s)\n",debugstr_w(lpszStr), debugstr_w(lpszMatch));
1054
1055   return SHLWAPI_StrSpnHelperW(lpszStr, lpszMatch, StrChrW, TRUE);
1056 }
1057
1058 /*************************************************************************
1059  * StrCSpnIA    [SHLWAPI.@]
1060  *
1061  * Find the length of the start of a string that does not contain certain
1062  * characters, ignoring case.
1063  *
1064  * PARAMS
1065  *  lpszStr   [I] String to search
1066  *  lpszMatch [I] Characters that cannot be in the substring
1067  *
1068  * RETURNS
1069  *  The length of the part of lpszStr containing only chars not in lpszMatch,
1070  *  or 0 if any parameter is invalid.
1071  */
1072 int WINAPI StrCSpnIA(LPCSTR lpszStr, LPCSTR lpszMatch)
1073 {
1074   TRACE("(%s,%s)\n",debugstr_a(lpszStr), debugstr_a(lpszMatch));
1075
1076   return SHLWAPI_StrSpnHelperA(lpszStr, lpszMatch, StrChrIA, TRUE);
1077 }
1078
1079 /*************************************************************************
1080  * StrCSpnIW    [SHLWAPI.@]
1081  *
1082  * See StrCSpnIA.
1083  */
1084 int WINAPI StrCSpnIW(LPCWSTR lpszStr, LPCWSTR lpszMatch)
1085 {
1086   TRACE("(%s,%s)\n",debugstr_w(lpszStr), debugstr_w(lpszMatch));
1087
1088   return SHLWAPI_StrSpnHelperW(lpszStr, lpszMatch, StrChrIW, TRUE);
1089 }
1090
1091 /*************************************************************************
1092  * StrPBrkA     [SHLWAPI.@]
1093  *
1094  * Search a string for any of a group of characters.
1095  *
1096  * PARAMS
1097  *  lpszStr   [I] String to search
1098  *  lpszMatch [I] Characters to match
1099  *
1100  * RETURNS
1101  *  A pointer to the first matching character in lpszStr, or NULL if no
1102  *  match was found.
1103  */
1104 LPSTR WINAPI StrPBrkA(LPCSTR lpszStr, LPCSTR lpszMatch)
1105 {
1106   TRACE("(%s,%s)\n",debugstr_a(lpszStr), debugstr_a(lpszMatch));
1107
1108   if (lpszStr && lpszMatch && *lpszMatch)
1109   {
1110     while (*lpszStr)
1111     {
1112       if (StrChrA(lpszMatch, *lpszStr))
1113         return (LPSTR)lpszStr;
1114       lpszStr = CharNextA(lpszStr);
1115     };
1116   }
1117   return NULL;
1118 }
1119
1120 /*************************************************************************
1121  * StrPBrkW     [SHLWAPI.@]
1122  *
1123  * See StrPBrkA.
1124  */
1125 LPWSTR WINAPI StrPBrkW(LPCWSTR lpszStr, LPCWSTR lpszMatch)
1126 {
1127   TRACE("(%s,%s)\n",debugstr_w(lpszStr), debugstr_w(lpszMatch));
1128
1129   if (lpszStr && lpszMatch && *lpszMatch)
1130   {
1131     while (*lpszStr);
1132     {
1133       if (StrChrW(lpszMatch, *lpszStr))
1134         return (LPWSTR)lpszStr;
1135       lpszStr = CharNextW(lpszStr);
1136     } while (*lpszStr);
1137   }
1138   return NULL;
1139 }
1140
1141 /*************************************************************************
1142  * SHLWAPI_StrRChrHelperA
1143  *
1144  * Internal implementation of StrRChrA/StrRChrIA.
1145  */
1146 static LPSTR WINAPI SHLWAPI_StrRChrHelperA(LPCSTR lpszStr,
1147                                            LPCSTR lpszEnd, WORD ch,
1148                                            BOOL (WINAPI *pChrCmpFn)(WORD,WORD))
1149 {
1150   LPCSTR lpszRet = NULL;
1151
1152   if (lpszStr)
1153   {
1154     WORD ch2;
1155
1156     if (!lpszEnd)
1157       lpszEnd = lpszStr + lstrlenA(lpszStr);
1158
1159     while (*lpszStr && lpszStr <= lpszEnd)
1160     {
1161       ch2 = IsDBCSLeadByte(*lpszStr)? *lpszStr << 8 | lpszStr[1] : *lpszStr;
1162
1163       if (!pChrCmpFn(ch, ch2))
1164         lpszRet = lpszStr;
1165       lpszStr = CharNextA(lpszStr);
1166     }
1167   }
1168   return (LPSTR)lpszRet;
1169 }
1170
1171 /*************************************************************************
1172  * SHLWAPI_StrRChrHelperW
1173  *
1174  * Internal implementation of StrRChrW/StrRChrIW.
1175  */
1176 static LPWSTR WINAPI SHLWAPI_StrRChrHelperW(LPCWSTR lpszStr,
1177                                          LPCWSTR lpszEnd, WCHAR ch,
1178                                          BOOL (WINAPI *pChrCmpFn)(WCHAR,WCHAR))
1179 {
1180   LPCWSTR lpszRet = NULL;
1181
1182   if (lpszStr)
1183   {
1184     if (!lpszEnd)
1185       lpszEnd = lpszStr + strlenW(lpszStr);
1186
1187     while (*lpszStr && lpszStr <= lpszEnd)
1188     {
1189       if (!pChrCmpFn(ch, *lpszStr))
1190         lpszRet = lpszStr;
1191       lpszStr = CharNextW(lpszStr);
1192     }
1193   }
1194   return (LPWSTR)lpszRet;
1195 }
1196
1197 /**************************************************************************
1198  * StrRChrA     [SHLWAPI.@]
1199  *
1200  * Find the last occurence of a character in string.
1201  *
1202  * PARAMS
1203  *  lpszStr [I] String to search in
1204  *  lpszEnd [I] Place to end search, or NULL to search until the end of lpszStr
1205  *  ch      [I] Character to search for.
1206  *
1207  * RETURNS
1208  *  Success: A pointer to the last occurrence of ch in lpszStr before lpszEnd,
1209  *           or NULL if not found.
1210  *  Failure: NULL, if any arguments are invalid.
1211  */
1212 LPSTR WINAPI StrRChrA(LPCSTR lpszStr, LPCSTR lpszEnd, WORD ch)
1213 {
1214   TRACE("(%s,%s,%x)\n", debugstr_a(lpszStr), debugstr_a(lpszEnd), ch);
1215
1216   return SHLWAPI_StrRChrHelperA(lpszStr, lpszEnd, ch, SHLWAPI_ChrCmpA);
1217 }
1218
1219 /**************************************************************************
1220  * StrRChrW     [SHLWAPI.@]
1221  *
1222  * See StrRChrA.
1223  */
1224 LPWSTR WINAPI StrRChrW(LPCWSTR lpszStr, LPCWSTR lpszEnd, WORD ch)
1225 {
1226   TRACE("(%s,%s,%x)\n", debugstr_w(lpszStr), debugstr_w(lpszEnd), ch);
1227
1228   return SHLWAPI_StrRChrHelperW(lpszStr, lpszEnd, ch, SHLWAPI_ChrCmpW);
1229 }
1230
1231 /**************************************************************************
1232  * StrRChrIA    [SHLWAPI.@]
1233  *
1234  * Find the last occurence of a character in string, ignoring case.
1235  *
1236  * PARAMS
1237  *  lpszStr [I] String to search in
1238  *  lpszEnd [I] Place to end search, or NULL to search until the end of lpszStr
1239  *  ch      [I] Character to search for.
1240  *
1241  * RETURNS
1242  *  Success: A pointer to the last occurrence of ch in lpszStr before lpszEnd,
1243  *           or NULL if not found.
1244  *  Failure: NULL, if any arguments are invalid.
1245  */
1246 LPSTR WINAPI StrRChrIA(LPCSTR lpszStr, LPCSTR lpszEnd, WORD ch)
1247 {
1248   TRACE("(%s,%s,%x)\n", debugstr_a(lpszStr), debugstr_a(lpszEnd), ch);
1249
1250   return SHLWAPI_StrRChrHelperA(lpszStr, lpszEnd, ch, ChrCmpIA);
1251 }
1252
1253 /**************************************************************************
1254  * StrRChrIW    [SHLWAPI.@]
1255  *
1256  * See StrRChrIA.
1257  */
1258 LPWSTR WINAPI StrRChrIW(LPCWSTR lpszStr, LPCWSTR lpszEnd, WORD ch)
1259 {
1260   TRACE("(%s,%s,%x)\n", debugstr_w(lpszStr), debugstr_w(lpszEnd), ch);
1261
1262   return SHLWAPI_StrRChrHelperW(lpszStr, lpszEnd, ch, ChrCmpIW);
1263 }
1264
1265 /*************************************************************************
1266  * StrCatBuffA  [SHLWAPI.@]
1267  *
1268  * Concatenate two strings together.
1269  *
1270  * PARAMS
1271  *  lpszStr [O] String to concatenate to
1272  *  lpszCat [I] String to add to lpszCat
1273  *  cchMax  [I] Maximum number of characters for the whole string
1274  *
1275  * RETURNS
1276  *  lpszStr.
1277  *
1278  * NOTES
1279  *  cchMax dtermines the number of characters in the final length of the
1280  *  string, not the number appended to lpszStr from lpszCat.
1281  */
1282 LPSTR WINAPI StrCatBuffA(LPSTR lpszStr, LPCSTR lpszCat, INT cchMax)
1283 {
1284   INT iLen;
1285
1286   TRACE("(%p,%s,%d)\n", lpszStr, debugstr_a(lpszCat), cchMax);
1287
1288   if (!lpszStr)
1289   {
1290     WARN("Invalid lpszStr would crash under Win32!\n");
1291     return NULL;
1292   }
1293
1294   iLen = strlen(lpszStr);
1295   cchMax -= iLen;
1296
1297   if (cchMax > 0)
1298     StrCpyNA(lpszStr + iLen, lpszCat, cchMax);
1299   return lpszStr;
1300 }
1301
1302 /*************************************************************************
1303  * StrCatBuffW  [SHLWAPI.@]
1304  *
1305  * See StrCatBuffA.
1306  */
1307 LPWSTR WINAPI StrCatBuffW(LPWSTR lpszStr, LPCWSTR lpszCat, INT cchMax)
1308 {
1309   INT iLen;
1310
1311   TRACE("(%p,%s,%d)\n", lpszStr, debugstr_w(lpszCat), cchMax);
1312
1313   if (!lpszStr)
1314   {
1315     WARN("Invalid lpszStr would crash under Win32!\n");
1316     return NULL;
1317   }
1318
1319   iLen = strlenW(lpszStr);
1320   cchMax -= iLen;
1321
1322   if (cchMax > 0)
1323     StrCpyNW(lpszStr + iLen, lpszCat, cchMax);
1324   return lpszStr;
1325 }
1326
1327 /*************************************************************************
1328  * StrRetToBufA                                 [SHLWAPI.@]
1329  *
1330  * Convert a STRRET to a normal string.
1331  *
1332  * PARAMS
1333  *  lpStrRet [O] STRRET to convert
1334  *  pIdl     [I] ITEMIDLIST for lpStrRet->uType = STRRET_OFFSETA
1335  *  lpszDest [O] Destination for normal string
1336  *  dwLen    [I] Length of lpszDest
1337  *
1338  * RETURNS
1339  *  Success: S_OK. lpszDest contains up to dwLen characters of the string.
1340  *           If lpStrRet is of type STRRET_WSTR, its memory is freed with
1341  *           CoTaskMemFree and its type set to STRRET_CSTRA.
1342  *  Failure: E_FAIL, if any parameters are invalid.
1343  */
1344 HRESULT WINAPI StrRetToBufA (LPSTRRET src, const ITEMIDLIST *pidl, LPSTR dest, DWORD len)
1345 {
1346         /* NOTE:
1347          *  This routine is identical to that in dlls/shell32/shellstring.c.
1348          *  It was duplicated because not every version of Shlwapi.dll exports
1349          *  StrRetToBufA. If you change one routine, change them both.
1350          */
1351         TRACE("dest=%p len=0x%lx strret=%p pidl=%p stub\n",dest,len,src,pidl);
1352
1353         if (!src)
1354         {
1355           WARN("Invalid lpStrRet would crash under Win32!\n");
1356           if (dest)
1357             *dest = '\0';
1358           return E_FAIL;
1359         }
1360
1361         if (!dest || !len)
1362           return E_FAIL;
1363
1364         *dest = '\0';
1365
1366         switch (src->uType)
1367         {
1368           case STRRET_WSTR:
1369             WideCharToMultiByte(CP_ACP, 0, src->u.pOleStr, -1, dest, len, NULL, NULL);
1370             CoTaskMemFree(src->u.pOleStr);
1371             break;
1372
1373           case STRRET_CSTR:
1374             lstrcpynA((LPSTR)dest, src->u.cStr, len);
1375             break;
1376
1377           case STRRET_OFFSET:
1378             lstrcpynA((LPSTR)dest, ((LPCSTR)&pidl->mkid)+src->u.uOffset, len);
1379             break;
1380
1381           default:
1382             FIXME("unknown type!\n");
1383             return FALSE;
1384         }
1385         return S_OK;
1386 }
1387
1388 /*************************************************************************
1389  * StrRetToBufW [SHLWAPI.@]
1390  *
1391  * See StrRetToBufA.
1392  */
1393 HRESULT WINAPI StrRetToBufW (LPSTRRET src, const ITEMIDLIST *pidl, LPWSTR dest, DWORD len)
1394 {
1395         TRACE("dest=%p len=0x%lx strret=%p pidl=%p stub\n",dest,len,src,pidl);
1396
1397         if (!src)
1398         {
1399           WARN("Invalid lpStrRet would crash under Win32!\n");
1400           if (dest)
1401             *dest = '\0';
1402           return E_FAIL;
1403         }
1404
1405         if (!dest || !len)
1406           return E_FAIL;
1407
1408         *dest = '\0';
1409
1410         switch (src->uType)
1411         {
1412           case STRRET_WSTR:
1413             lstrcpynW((LPWSTR)dest, src->u.pOleStr, len);
1414             CoTaskMemFree(src->u.pOleStr);
1415             break;
1416
1417           case STRRET_CSTR:
1418               if (!MultiByteToWideChar( CP_ACP, 0, src->u.cStr, -1, dest, len ) && len)
1419                   dest[len-1] = 0;
1420             break;
1421
1422           case STRRET_OFFSET:
1423             if (pidl)
1424             {
1425               if (!MultiByteToWideChar( CP_ACP, 0, ((LPCSTR)&pidl->mkid)+src->u.uOffset, -1,
1426                                         dest, len ) && len)
1427                   dest[len-1] = 0;
1428             }
1429             break;
1430
1431           default:
1432             FIXME("unknown type!\n");
1433             return FALSE;
1434         }
1435         return S_OK;
1436 }
1437
1438 /*************************************************************************
1439  * StrRetToStrA                                 [SHLWAPI.@]
1440  *
1441  * converts a STRRET to a normal string
1442  */
1443 HRESULT WINAPI StrRetToStrA(LPSTRRET pstr, const ITEMIDLIST * pidl, LPSTR* ppszName)
1444 {
1445         HRESULT ret = E_FAIL;
1446
1447         switch (pstr->uType) {
1448             case STRRET_WSTR:
1449                 ret = _SHStrDupAW(pstr->u.pOleStr, ppszName);
1450                 CoTaskMemFree(pstr->u.pOleStr);
1451                 break;
1452
1453             case STRRET_CSTR:
1454                 ret = _SHStrDupAA(pstr->u.cStr, ppszName);
1455                 break;
1456
1457             case STRRET_OFFSET:
1458                 ret = _SHStrDupAA(((LPCSTR)&pidl->mkid)+pstr->u.uOffset, ppszName);
1459                 break;
1460
1461             default:
1462                 *ppszName = NULL;
1463         }
1464         return ret;
1465 }
1466
1467 /*************************************************************************
1468  * StrRetToStrW                                 [SHLWAPI.@]
1469  *
1470  * converts a STRRET to a normal string
1471  */
1472 HRESULT WINAPI StrRetToStrW(LPSTRRET pstr, const ITEMIDLIST * pidl, LPWSTR* ppszName)
1473 {
1474         HRESULT ret = E_FAIL;
1475
1476         switch (pstr->uType) {
1477             case STRRET_WSTR:
1478                 ret = SHStrDupW(pstr->u.pOleStr, ppszName);
1479                 CoTaskMemFree(pstr->u.pOleStr);
1480                 break;
1481
1482             case STRRET_CSTR:
1483                 ret = SHStrDupA(pstr->u.cStr, ppszName);
1484                 break;
1485
1486             case STRRET_OFFSET:
1487                 ret = SHStrDupA(((LPCSTR)&pidl->mkid)+pstr->u.uOffset, ppszName);
1488                 break;
1489
1490             default:
1491                 *ppszName = NULL;
1492         }
1493         return ret;
1494 }
1495
1496 /*************************************************************************
1497  * StrFormatByteSizeA                           [SHLWAPI.@]
1498  */
1499 LPSTR WINAPI StrFormatByteSizeA ( DWORD dw, LPSTR pszBuf, UINT cchBuf )
1500 {       char buf[64];
1501         TRACE("%lx %p %i\n", dw, pszBuf, cchBuf);
1502         if ( dw<1024L )
1503         { sprintf (buf,"%ld bytes", dw);
1504         }
1505         else if ( dw<1048576L)
1506         { sprintf (buf,"%3.1f KB", (FLOAT)dw/1024);
1507         }
1508         else if ( dw < 1073741824L)
1509         { sprintf (buf,"%3.1f MB", (FLOAT)dw/1048576L);
1510         }
1511         else
1512         { sprintf (buf,"%3.1f GB", (FLOAT)dw/1073741824L);
1513         }
1514         lstrcpynA (pszBuf, buf, cchBuf);
1515         return pszBuf;
1516 }
1517
1518 /*************************************************************************
1519  * StrFormatByteSizeW                           [SHLWAPI.@]
1520  */
1521 LPWSTR WINAPI StrFormatByteSizeW ( DWORD dw, LPWSTR pszBuf, UINT cchBuf )
1522 {
1523         char buf[64];
1524         StrFormatByteSizeA( dw, buf, sizeof(buf) );
1525         if (!MultiByteToWideChar( CP_ACP, 0, buf, -1, pszBuf, cchBuf ) && cchBuf)
1526             pszBuf[cchBuf-1] = 0;
1527         return pszBuf;
1528 }
1529
1530 /*************************************************************************
1531  * StrFormatKBSizeA     [SHLWAPI.@]
1532  *
1533  * Create a formatted string containing a byte count in Kilobytes.
1534  *
1535  * PARAMS
1536  *  llBytes  [I] Byte size to format
1537  *  lpszDest [I] Destination for formatted string
1538  *  cchMax   [I] Size of lpszDest
1539  *
1540  * RETURNS
1541  *  lpszDest.
1542  */
1543 LPSTR WINAPI StrFormatKBSizeA(LONGLONG llBytes, LPSTR lpszDest, UINT cchMax)
1544 {
1545   char szBuff[256], *szOut = szBuff + sizeof(szBuff) - 1;
1546   LONGLONG ulKB = (llBytes + 1023) >> 10;
1547
1548   TRACE("(%lld,%p,%d)\n", llBytes, lpszDest, cchMax);
1549
1550   *szOut-- = '\0';
1551   *szOut-- = 'B';
1552   *szOut-- = 'K';
1553   *szOut-- = ' ';
1554
1555   do
1556   {
1557     LONGLONG ulNextDigit = ulKB % 10;
1558     *szOut-- = '0' + ulNextDigit;
1559     ulKB = (ulKB - ulNextDigit) / 10;
1560   } while (ulKB > 0);
1561
1562   strncpy(lpszDest, szOut + 1, cchMax);
1563   return lpszDest;
1564 }
1565
1566 /*************************************************************************
1567  * StrFormatKBSizeW     [SHLWAPI.@]
1568  *
1569  * See StrFormatKBSizeA.
1570  */
1571 LPWSTR WINAPI StrFormatKBSizeW(LONGLONG llBytes, LPWSTR lpszDest, UINT cchMax)
1572 {
1573   WCHAR szBuff[256], *szOut = szBuff + sizeof(szBuff) - 1;
1574   LONGLONG ulKB = (llBytes + 1023) >> 10;
1575
1576   TRACE("(%lld,%p,%d)\n", llBytes, lpszDest, cchMax);
1577
1578   *szOut-- = '\0';
1579   *szOut-- = 'B';
1580   *szOut-- = 'K';
1581   *szOut-- = ' ';
1582
1583   do
1584   {
1585     LONGLONG ulNextDigit = ulKB % 10;
1586     *szOut-- = '0' + ulNextDigit;
1587     ulKB = (ulKB - ulNextDigit) / 10;
1588   } while (ulKB > 0);
1589
1590   strncpyW(lpszDest, szOut + 1, cchMax);
1591   return lpszDest;
1592 }
1593
1594 /*************************************************************************
1595  * StrNCatA     [SHLWAPI.@]
1596  *
1597  * Concatenate two strings together.
1598  *
1599  * PARAMS
1600  *  lpszStr [O] String to concatenate to
1601  *  lpszCat [I] String to add to lpszCat
1602  *  cchMax  [I] Maximum number of characters to concatenate
1603  *
1604  * RETURNS
1605  *  lpszStr.
1606  *
1607  * NOTES
1608  *  cchMax dtermines the number of characters that are appended to lpszStr,
1609  *  not the total length of the string.
1610  */
1611 LPSTR WINAPI StrNCatA(LPSTR lpszStr, LPCSTR lpszCat, INT cchMax)
1612 {
1613   LPSTR lpszRet = lpszStr;
1614
1615   TRACE("(%s,%s,%i)\n", debugstr_a(lpszStr), debugstr_a(lpszCat), cchMax);
1616
1617   if (!lpszStr)
1618   {
1619     WARN("Invalid lpszStr would crash under Win32!\n");
1620     return NULL;
1621   }
1622
1623   StrCpyNA(lpszStr + strlen(lpszStr), lpszCat, cchMax);
1624   return lpszRet;
1625 }
1626
1627 /*************************************************************************
1628  * StrNCatW     [SHLWAPI.@]
1629  *
1630  * See StrNCatA.
1631  */
1632 LPWSTR WINAPI StrNCatW(LPWSTR lpszStr, LPCWSTR lpszCat, INT cchMax)
1633 {
1634   LPWSTR lpszRet = lpszStr;
1635
1636   TRACE("(%s,%s,%i)\n", debugstr_w(lpszStr), debugstr_w(lpszCat), cchMax);
1637
1638   if (!lpszStr)
1639   {
1640     WARN("Invalid lpszStr would crash under Win32\n");
1641     return NULL;
1642   }
1643
1644   StrCpyNW(lpszStr + strlenW(lpszStr), lpszCat, cchMax);
1645   return lpszRet;
1646 }
1647
1648 /*************************************************************************
1649  * StrTrimA     [SHLWAPI.@]
1650  *
1651  * Remove characters from the start and end of a string.
1652  *
1653  * PARAMS
1654  *  lpszStr  [O] String to remove characters from
1655  *  lpszTrim [I] Characters to remove from lpszStr
1656  *
1657  * RETURNS
1658  *  TRUE  If lpszStr was valid and modified
1659  *  FALSE Otherwise
1660  */
1661 BOOL WINAPI StrTrimA(LPSTR lpszStr, LPCSTR lpszTrim)
1662 {
1663   DWORD dwLen;
1664   LPSTR lpszRead = lpszStr;
1665   BOOL bRet = FALSE;
1666
1667   TRACE("(%s,%s)\n", debugstr_a(lpszStr), debugstr_a(lpszTrim));
1668
1669   if (lpszRead && *lpszRead)
1670   {
1671     while (*lpszRead && StrChrA(lpszTrim, *lpszRead))
1672       lpszRead = CharNextA(lpszRead); /* Skip leading matches */
1673
1674     dwLen = strlen(lpszRead);
1675
1676     if (lpszRead != lpszStr)
1677     {
1678       memmove(lpszStr, lpszRead, dwLen + 1);
1679       bRet = TRUE;
1680     }
1681     if (dwLen > 0)
1682     {
1683       lpszRead = lpszStr + dwLen;
1684       while (StrChrA(lpszTrim, lpszRead[-1]))
1685         lpszRead = CharPrevA(lpszStr, lpszRead); /* Skip trailing matches */
1686
1687       if (lpszRead != lpszStr + dwLen)
1688       {
1689         *lpszRead = '\0';
1690         bRet = TRUE;
1691       }
1692     }
1693   }
1694   return bRet;
1695 }
1696
1697 /*************************************************************************
1698  * StrTrimW     [SHLWAPI.@]
1699  *
1700  * See StrTrimA.
1701  */
1702 BOOL WINAPI StrTrimW(LPWSTR lpszStr, LPCWSTR lpszTrim)
1703 {
1704   DWORD dwLen;
1705   LPWSTR lpszRead = lpszStr;
1706   BOOL bRet = FALSE;
1707
1708   TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszTrim));
1709
1710   if (lpszRead && *lpszRead)
1711   {
1712     while (*lpszRead && StrChrW(lpszTrim, *lpszRead))
1713       lpszRead = CharNextW(lpszRead); /* Skip leading matches */
1714
1715     dwLen = strlenW(lpszRead);
1716
1717     if (lpszRead != lpszStr)
1718     {
1719       memmove(lpszStr, lpszRead, (dwLen + 1) * sizeof(WCHAR));
1720       bRet = TRUE;
1721     }
1722     if (dwLen > 0)
1723     {
1724       lpszRead = lpszStr + dwLen;
1725       while (StrChrW(lpszTrim, lpszRead[-1]))
1726         lpszRead = CharPrevW(lpszStr, lpszRead); /* Skip trailing matches */
1727
1728       if (lpszRead != lpszStr + dwLen)
1729       {
1730         *lpszRead = '\0';
1731         bRet = TRUE;
1732       }
1733     }
1734   }
1735   return bRet;
1736 }
1737
1738 /*************************************************************************
1739  *      _SHStrDupA      [INTERNAL]
1740  *
1741  * Duplicates a ASCII string to ASCII. The destination buffer is allocated.
1742  */
1743 static HRESULT WINAPI _SHStrDupAA(LPCSTR src, LPSTR * dest)
1744 {
1745         HRESULT hr;
1746         int len = 0;
1747
1748         if (src) {
1749             len = lstrlenA(src);
1750             *dest = CoTaskMemAlloc(len);
1751         } else {
1752             *dest = NULL;
1753         }
1754
1755         if (*dest) {
1756             lstrcpynA(*dest,src, len);
1757             hr = S_OK;
1758         } else {
1759             hr = E_OUTOFMEMORY;
1760         }
1761
1762         TRACE("%s->(%p)\n", debugstr_a(src), *dest);
1763         return hr;
1764 }
1765
1766 /*************************************************************************
1767  * SHStrDupA
1768  *
1769  * Return a Unicode copy of a string, in memory allocated by CoTaskMemAlloc.
1770  *
1771  * PARAMS
1772  *  lpszStr   [I] String to copy
1773  *  lppszDest [O] Destination for the new string copy
1774  *
1775  * RETURNS
1776  *  Success: S_OK. lppszDest contains the new string in Unicode format.
1777  *  Failure: E_OUTOFMEMORY, If any arguments are invalid or memory allocation
1778  *           fails.
1779  */
1780 HRESULT WINAPI SHStrDupA(LPCSTR src, LPWSTR * dest)
1781 {
1782         HRESULT hr;
1783         int len = 0;
1784
1785         if (src) {
1786             len = (MultiByteToWideChar(0,0,src,-1,0,0) + 1)* sizeof(WCHAR);
1787             *dest = CoTaskMemAlloc(len);
1788         } else {
1789             *dest = NULL;
1790         }
1791
1792         if (*dest) {
1793             MultiByteToWideChar(0,0,src,-1,*dest,len);
1794             hr = S_OK;
1795         } else {
1796             hr = E_OUTOFMEMORY;
1797         }
1798
1799         TRACE("%s->(%p)\n", debugstr_a(src), *dest);
1800         return hr;
1801 }
1802
1803 /*************************************************************************
1804  *      _SHStrDupAW     [INTERNAL]
1805  *
1806  * Duplicates a UNICODE to a ASCII string. The destination buffer is allocated.
1807  */
1808 static HRESULT WINAPI _SHStrDupAW(LPCWSTR src, LPSTR * dest)
1809 {
1810         HRESULT hr;
1811         int len = 0;
1812
1813         if (src) {
1814             len = WideCharToMultiByte(CP_ACP, 0, src, -1, NULL, 0, NULL, NULL);
1815             *dest = CoTaskMemAlloc(len);
1816         } else {
1817             *dest = NULL;
1818         }
1819
1820         if (*dest) {
1821             WideCharToMultiByte(CP_ACP, 0, src, -1, *dest, len, NULL, NULL);
1822             hr = S_OK;
1823         } else {
1824             hr = E_OUTOFMEMORY;
1825         }
1826
1827         TRACE("%s->(%p)\n", debugstr_w(src), *dest);
1828         return hr;
1829 }
1830
1831 /*************************************************************************
1832  * SHStrDupW
1833  *
1834  * See SHStrDupA.
1835  */
1836 HRESULT WINAPI SHStrDupW(LPCWSTR src, LPWSTR * dest)
1837 {
1838         HRESULT hr;
1839         int len = 0;
1840
1841         if (src) {
1842             len = (lstrlenW(src) + 1) * sizeof(WCHAR);
1843             *dest = CoTaskMemAlloc(len);
1844         } else {
1845             *dest = NULL;
1846         }
1847
1848         if (*dest) {
1849             memcpy(*dest, src, len);
1850             hr = S_OK;
1851         } else {
1852             hr = E_OUTOFMEMORY;
1853         }
1854
1855         TRACE("%s->(%p)\n", debugstr_w(src), *dest);
1856         return hr;
1857 }
1858
1859 /*************************************************************************
1860  * SHLWAPI_WriteReverseNum
1861  *
1862  * Internal helper for SHLWAPI_WriteTimeClass.
1863  */
1864 inline static LPWSTR SHLWAPI_WriteReverseNum(LPWSTR lpszOut, DWORD dwNum)
1865 {
1866   *lpszOut-- = '\0';
1867
1868   /* Write a decimal number to a string, backwards */
1869   do
1870   {
1871     DWORD dwNextDigit = dwNum % 10;
1872     *lpszOut-- = '0' + dwNextDigit;
1873     dwNum = (dwNum - dwNextDigit) / 10;
1874   } while (dwNum > 0);
1875
1876   return lpszOut;
1877 }
1878
1879 /*************************************************************************
1880  * SHLWAPI_FormatSignificant
1881  *
1882  * Internal helper for SHLWAPI_WriteTimeClass.
1883  */
1884 inline static int SHLWAPI_FormatSignificant(LPWSTR lpszNum, int dwDigits)
1885 {
1886   /* Zero non significant digits, return remaining significant digits */
1887   while (*lpszNum)
1888   {
1889     lpszNum++;
1890     if (--dwDigits == 0)
1891     {
1892       while (*lpszNum)
1893         *lpszNum++ = '0';
1894       return 0;
1895     }
1896   }
1897   return dwDigits;
1898 }
1899
1900 /*************************************************************************
1901  * SHLWAPI_WriteTimeClass
1902  *
1903  * Internal helper for StrFromTimeIntervalW.
1904  */
1905 static int SHLWAPI_WriteTimeClass(LPWSTR lpszOut, DWORD dwValue,
1906                                   LPCWSTR lpszClass, int iDigits)
1907 {
1908   WCHAR szBuff[64], *szOut = szBuff + 32;
1909
1910   szOut = SHLWAPI_WriteReverseNum(szOut, dwValue);
1911   iDigits = SHLWAPI_FormatSignificant(szOut + 1, iDigits);
1912   *szOut = ' ';
1913   strcpyW(szBuff + 32, lpszClass);
1914   strcatW(lpszOut, szOut);
1915   return iDigits;
1916 }
1917
1918 /*************************************************************************
1919  * StrFromTimeIntervalA [SHLWAPI.@]
1920  *
1921  * Format a millisecond time interval into a string
1922  *
1923  * PARAMS
1924  *  lpszStr  [O] Output buffer for formatted time interval
1925  *  cchMax   [I] Size of lpszStr
1926  *  dwMS     [I] Number of milliseconds
1927  *  iDigits  [I] Number of digits to print
1928  *
1929  * RETURNS
1930  *  The length of the formatted string, or 0 if any parameter is invalid.
1931  *
1932  * NOTES
1933  *  This implementation mimics the Win32 behaviour of always writing a leading
1934  *  space before the time interval begins.
1935  *  iDigits is used to provide approximate times if accuracy is not important.
1936  *  This number of digits will be written of the first non-zero time class
1937  *  (hours/minutes/seconds). If this does not complete the time classification,
1938  *  the remaining digits are changed to zeros (i.e. The time is _not_ rounded).
1939  *  If there are digits remaining following the writing of a time class, the
1940  *  next time class will be written.
1941  *  For example, given dwMS represents 138 hours,43 minutes and 15 seconds, the
1942  *  following will result from the given values of iDigits:
1943  *
1944  *  iDigits    1        2        3        4               5               ...
1945  *  lpszStr   "100 hr" "130 hr" "138 hr" "138 hr 40 min" "138 hr 43 min"  ...
1946  */
1947 INT WINAPI StrFromTimeIntervalA(LPSTR lpszStr, UINT cchMax, DWORD dwMS,
1948                                 int iDigits)
1949 {
1950   INT iRet = 0;
1951
1952   TRACE("(%p,%d,%ld,%d)\n", lpszStr, cchMax, dwMS, iDigits);
1953
1954   if (lpszStr && cchMax)
1955   {
1956     WCHAR szBuff[128];
1957     StrFromTimeIntervalW(szBuff, sizeof(szBuff)/sizeof(WCHAR), dwMS, iDigits);
1958     WideCharToMultiByte(CP_ACP,0,szBuff,-1,lpszStr,cchMax,0,0);
1959   }
1960   return iRet;
1961 }
1962
1963
1964 /*************************************************************************
1965  * StrFromTimeIntervalW [SHLWAPI.@]
1966  *
1967  * See StrFromTimeIntervalA.
1968  */
1969 INT WINAPI StrFromTimeIntervalW(LPWSTR lpszStr, UINT cchMax, DWORD dwMS,
1970                                 int iDigits)
1971 {
1972   static const WCHAR szHr[] = {' ','h','r','\0'};
1973   static const WCHAR szMin[] = {' ','m','i','n','\0'};
1974   static const WCHAR szSec[] = {' ','s','e','c','\0'};
1975   INT iRet = 0;
1976
1977   TRACE("(%p,%d,%ld,%d)\n", lpszStr, cchMax, dwMS, iDigits);
1978
1979   if (lpszStr && cchMax)
1980   {
1981     WCHAR szCopy[128];
1982     DWORD dwHours, dwMinutes;
1983
1984     if (!iDigits || cchMax == 1)
1985     {
1986       *lpszStr = '\0';
1987       return 0;
1988     }
1989
1990     /* Calculate the time classes */
1991     dwMS = (dwMS + 500) / 1000;
1992     dwHours = dwMS / 3600;
1993     dwMS -= dwHours * 3600;
1994     dwMinutes = dwMS / 60;
1995     dwMS -= dwMinutes * 60;
1996
1997     szCopy[0] = '\0';
1998
1999     if (dwHours)
2000       iDigits = SHLWAPI_WriteTimeClass(szCopy, dwHours, szHr, iDigits);
2001
2002     if (dwMinutes && iDigits)
2003       iDigits = SHLWAPI_WriteTimeClass(szCopy, dwMinutes, szMin, iDigits);
2004
2005     if (iDigits) /* Always write seconds if we have significant digits */
2006       SHLWAPI_WriteTimeClass(szCopy, dwMS, szSec, iDigits);
2007
2008     strncpyW(lpszStr, szCopy, cchMax);
2009     iRet = strlenW(lpszStr);
2010   }
2011   return iRet;
2012 }
2013
2014 /*************************************************************************
2015  * StrIsIntlEqualA      [SHLWAPI.@]
2016  *
2017  * Compare two strings.
2018  *
2019  * PARAMS
2020  *  bCase    [I] Whether to compare case sensitively
2021  *  lpszStr  [I] First string to compare
2022  *  lpszComp [I] Second string to compare
2023  *  iLen     [I] Length to compare
2024  *
2025  * RETURNS
2026  *  TRUE  If the strings are equal.
2027  *  FALSE Otherwise.
2028  */
2029 BOOL WINAPI StrIsIntlEqualA(BOOL bCase, LPCSTR lpszStr, LPCSTR lpszComp,
2030                             int iLen)
2031 {
2032   DWORD dwFlags = LOCALE_USE_CP_ACP;
2033   int iRet;
2034
2035   TRACE("(%d,%s,%s,%d)\n", bCase,
2036         debugstr_a(lpszStr), debugstr_a(lpszComp), iLen);
2037
2038   /* FIXME: These flags are undocumented and unknown by our CompareString.
2039    *        We need defines for them.
2040    */
2041   dwFlags |= bCase ? 0x10000000 : 0x10000001;
2042
2043   iRet = CompareStringA(GetThreadLocale(),
2044                         dwFlags, lpszStr, iLen, lpszComp, iLen);
2045
2046   if (!iRet)
2047     iRet = CompareStringA(2048, dwFlags, lpszStr, iLen, lpszComp, iLen);
2048
2049   return iRet == 2 ? TRUE : FALSE;
2050 }
2051
2052 /*************************************************************************
2053  * StrIsIntlEqualW      [SHLWAPI.@]
2054  *
2055  * See StrIsIntlEqualA.
2056  */
2057 BOOL WINAPI StrIsIntlEqualW(BOOL bCase, LPCWSTR lpszStr, LPCWSTR lpszComp,
2058                             int iLen)
2059 {
2060   DWORD dwFlags;
2061   int iRet;
2062
2063   TRACE("(%d,%s,%s,%d)\n", bCase,
2064         debugstr_w(lpszStr),debugstr_w(lpszComp), iLen);
2065
2066   /* FIXME: These flags are undocumented and unknown by our CompareString.
2067    *        We need defines for them.
2068    */
2069   dwFlags = bCase ? 0x10000000 : 0x10000001;
2070
2071   iRet = CompareStringW(GetThreadLocale(),
2072                         dwFlags, lpszStr, iLen, lpszComp, iLen);
2073
2074   if (!iRet)
2075     iRet = CompareStringW(2048, dwFlags, lpszStr, iLen, lpszComp, iLen);
2076
2077   return iRet == 2 ? TRUE : FALSE;
2078 }
2079
2080 /*************************************************************************
2081  * @    [SHLWAPI.399]
2082  *
2083  * Copy a string to another string, up to a maximum number of characters.
2084  *
2085  * PARAMS
2086  *  lpszDest [O] Destination string
2087  *  lpszSrc  [I] Source string
2088  *  iLen     [I] Maximum number of chars to copy
2089  *
2090  * RETURNS
2091  *  Success: A pointer to the last character written.
2092  *  Failure: lpszDest, if any arguments are invalid.
2093  */
2094 LPSTR WINAPI SHLWAPI_399(LPSTR lpszDest, LPCSTR lpszSrc, int iLen)
2095 {
2096   TRACE("(%p,%s,%i)\n", lpszDest, debugstr_a(lpszSrc), iLen);
2097
2098   if (lpszDest && lpszSrc && iLen > 0)
2099   {
2100     while ((iLen-- > 1) && *lpszSrc)
2101       *lpszDest++ = *lpszSrc++;
2102     if (iLen >= 0)
2103      *lpszDest = '\0';
2104   }
2105   return lpszDest;
2106 }
2107
2108 /*************************************************************************
2109  * @    [SHLWAPI.400]
2110  *
2111  * Unicode version of SHLWAPI_399.
2112  */
2113 LPWSTR WINAPI SHLWAPI_400(LPWSTR lpszDest, LPCWSTR lpszSrc, int iLen)
2114 {
2115   TRACE("(%p,%s,%i)\n", lpszDest, debugstr_w(lpszSrc), iLen);
2116
2117   if (lpszDest && lpszSrc && iLen > 0)
2118   {
2119     while ((iLen-- > 1) && *lpszSrc)
2120       *lpszDest++ = *lpszSrc++;
2121     if (iLen >= 0)
2122      *lpszDest = '\0';
2123   }
2124   return lpszDest;
2125 }