msvcr90: Added _get/set_invalid_parameter_handler implementation.
[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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21
22 #include "config.h"
23 #include "wine/port.h"
24
25 #include <math.h>
26 #include <stdarg.h>
27 #include <stdio.h>
28 #include <string.h>
29
30 #define NONAMELESSUNION
31 #define NONAMELESSSTRUCT
32 #include "windef.h"
33 #include "winbase.h"
34 #define NO_SHLWAPI_REG
35 #define NO_SHLWAPI_STREAM
36 #include "shlwapi.h"
37 #include "wingdi.h"
38 #include "winuser.h"
39 #include "shlobj.h"
40 #include "mlang.h"
41 #include "ddeml.h"
42 #include "wine/unicode.h"
43 #include "wine/debug.h"
44
45 #include "resource.h"
46
47 WINE_DEFAULT_DEBUG_CHANNEL(shell);
48
49 extern HINSTANCE shlwapi_hInstance;
50
51 static HRESULT _SHStrDupAA(LPCSTR,LPSTR*);
52 static HRESULT _SHStrDupAW(LPCWSTR,LPSTR*);
53
54
55 static void FillNumberFmt(NUMBERFMTW *fmt, LPWSTR decimal_buffer, int decimal_bufwlen,
56                           LPWSTR thousand_buffer, int thousand_bufwlen)
57 {
58   WCHAR grouping[64];
59   WCHAR *c;
60
61   GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_ILZERO|LOCALE_RETURN_NUMBER, (LPWSTR)&fmt->LeadingZero, sizeof(fmt->LeadingZero)/sizeof(WCHAR));
62   GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_INEGNUMBER|LOCALE_RETURN_NUMBER, (LPWSTR)&fmt->LeadingZero, sizeof(fmt->NegativeOrder)/sizeof(WCHAR));
63   fmt->NumDigits = 0;
64   GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, decimal_buffer, decimal_bufwlen);
65   GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, thousand_buffer, thousand_bufwlen);
66   fmt->lpThousandSep = thousand_buffer;
67   fmt->lpDecimalSep = decimal_buffer;
68
69   /* 
70    * Converting grouping string to number as described on 
71    * http://blogs.msdn.com/oldnewthing/archive/2006/04/18/578251.aspx
72    */
73   fmt->Grouping = 0;
74   GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SGROUPING, grouping, sizeof(grouping)/sizeof(WCHAR));
75   for (c = grouping; *c; c++)
76     if (*c >= '0' && *c < '9')
77     {
78       fmt->Grouping *= 10;
79       fmt->Grouping += *c - '0';
80     }
81
82   if (fmt->Grouping % 10 == 0)
83     fmt->Grouping /= 10;
84   else
85     fmt->Grouping *= 10;
86 }
87
88 /*************************************************************************
89  * FormatInt   [internal]
90  *
91  * Format an integer according to the current locale
92  *
93  * RETURNS
94  *  The number of bytes written on success or 0 on failure
95  */
96 static int FormatInt(LONGLONG qdwValue, LPWSTR pszBuf, int cchBuf)
97 {
98   NUMBERFMTW fmt;
99   WCHAR decimal[8], thousand[8];
100   WCHAR buf[24];
101   WCHAR *c;
102   BOOL neg = (qdwValue < 0);
103
104   FillNumberFmt(&fmt, decimal, sizeof decimal / sizeof (WCHAR),
105                 thousand, sizeof thousand / sizeof (WCHAR));
106
107   c = &buf[24];
108   *(--c) = 0;
109   do
110   {
111     *(--c) = '0' + (qdwValue%10);
112     qdwValue /= 10;
113   } while (qdwValue > 0);
114   if (neg)
115     *(--c) = '-';
116   
117   return GetNumberFormatW(LOCALE_USER_DEFAULT, 0, c, &fmt, pszBuf, cchBuf);
118 }
119
120 /*************************************************************************
121  * FormatDouble   [internal]
122  *
123  * Format an integer according to the current locale. Prints the specified number of digits
124  * after the decimal point
125  *
126  * RETURNS
127  *  The number of bytes written on success or 0 on failure
128  */
129 static int FormatDouble(double value, int decimals, LPWSTR pszBuf, int cchBuf)
130 {
131   static const WCHAR flfmt[] = {'%','f',0};
132   WCHAR buf[64];
133   NUMBERFMTW fmt;
134   WCHAR decimal[8], thousand[8];
135   
136   snprintfW(buf, 64, flfmt, value);
137
138   FillNumberFmt(&fmt, decimal, sizeof decimal / sizeof (WCHAR),
139                  thousand, sizeof thousand / sizeof (WCHAR));
140   fmt.NumDigits = decimals;
141   return GetNumberFormatW(LOCALE_USER_DEFAULT, 0, buf, &fmt, pszBuf, cchBuf);
142 }
143
144 /*************************************************************************
145  * SHLWAPI_ChrCmpHelperA
146  *
147  * Internal helper for SHLWAPI_ChrCmpA/ChrCMPIA.
148  *
149  * NOTES
150  *  Both this function and its Unicode counterpart are very inefficient. To
151  *  fix this, CompareString must be completely implemented and optimised
152  *  first. Then the core character test can be taken out of that function and
153  *  placed here, so that it need never be called at all. Until then, do not
154  *  attempt to optimise this code unless you are willing to test that it
155  *  still performs correctly.
156  */
157 static BOOL SHLWAPI_ChrCmpHelperA(WORD ch1, WORD ch2, DWORD dwFlags)
158 {
159   char str1[3], str2[3];
160
161   str1[0] = LOBYTE(ch1);
162   if (IsDBCSLeadByte(str1[0]))
163   {
164     str1[1] = HIBYTE(ch1);
165     str1[2] = '\0';
166   }
167   else
168     str1[1] = '\0';
169
170   str2[0] = LOBYTE(ch2);
171   if (IsDBCSLeadByte(str2[0]))
172   {
173     str2[1] = HIBYTE(ch2);
174     str2[2] = '\0';
175   }
176   else
177     str2[1] = '\0';
178
179   return CompareStringA(GetThreadLocale(), dwFlags, str1, -1, str2, -1) - 2;
180 }
181
182 /*************************************************************************
183  * SHLWAPI_ChrCmpA
184  *
185  * Internal helper function.
186  */
187 static BOOL WINAPI SHLWAPI_ChrCmpA(WORD ch1, WORD ch2)
188 {
189   return SHLWAPI_ChrCmpHelperA(ch1, ch2, 0);
190 }
191
192 /*************************************************************************
193  * ChrCmpIA     (SHLWAPI.385)
194  *
195  * Compare two characters, ignoring case.
196  *
197  * PARAMS
198  *  ch1 [I] First character to compare
199  *  ch2 [I] Second character to compare
200  *
201  * RETURNS
202  *  FALSE, if the characters are equal.
203  *  Non-zero otherwise.
204  */
205 BOOL WINAPI ChrCmpIA(WORD ch1, WORD ch2)
206 {
207   TRACE("(%d,%d)\n", ch1, ch2);
208
209   return SHLWAPI_ChrCmpHelperA(ch1, ch2, NORM_IGNORECASE);
210 }
211
212 /*************************************************************************
213  * ChrCmpIW     [SHLWAPI.386]
214  *
215  * See ChrCmpIA.
216  */
217 BOOL WINAPI ChrCmpIW(WCHAR ch1, WCHAR ch2)
218 {
219   return CompareStringW(GetThreadLocale(), NORM_IGNORECASE, &ch1, 1, &ch2, 1) - 2;
220 }
221
222 /*************************************************************************
223  * StrChrA      [SHLWAPI.@]
224  *
225  * Find a given character in a string.
226  *
227  * PARAMS
228  *  lpszStr [I] String to search in.
229  *  ch      [I] Character to search for.
230  *
231  * RETURNS
232  *  Success: A pointer to the first occurrence of ch in lpszStr, or NULL if
233  *           not found.
234  *  Failure: NULL, if any arguments are invalid.
235  */
236 LPSTR WINAPI StrChrA(LPCSTR lpszStr, WORD ch)
237 {
238   TRACE("(%s,%i)\n", debugstr_a(lpszStr), ch);
239
240   if (lpszStr)
241   {
242     while (*lpszStr)
243     {
244       if (!SHLWAPI_ChrCmpA(*lpszStr, ch))
245         return (LPSTR)lpszStr;
246       lpszStr = CharNextA(lpszStr);
247     }
248   }
249   return NULL;
250 }
251
252 /*************************************************************************
253  * StrChrW      [SHLWAPI.@]
254  *
255  * See StrChrA.
256  */
257 LPWSTR WINAPI StrChrW(LPCWSTR lpszStr, WCHAR ch)
258 {
259   LPWSTR lpszRet = NULL;
260
261   TRACE("(%s,%i)\n", debugstr_w(lpszStr), ch);
262
263   if (lpszStr)
264     lpszRet = strchrW(lpszStr, ch);
265   return lpszRet;
266 }
267
268 /*************************************************************************
269  * StrChrIA     [SHLWAPI.@]
270  *
271  * Find a given character in a string, ignoring case.
272  *
273  * PARAMS
274  *  lpszStr [I] String to search in.
275  *  ch      [I] Character to search for.
276  *
277  * RETURNS
278  *  Success: A pointer to the first occurrence of ch in lpszStr, or NULL if
279  *           not found.
280  *  Failure: NULL, if any arguments are invalid.
281  */
282 LPSTR WINAPI StrChrIA(LPCSTR lpszStr, WORD ch)
283 {
284   TRACE("(%s,%i)\n", debugstr_a(lpszStr), ch);
285
286   if (lpszStr)
287   {
288     while (*lpszStr)
289     {
290       if (!ChrCmpIA(*lpszStr, ch))
291         return (LPSTR)lpszStr;
292       lpszStr = CharNextA(lpszStr);
293     }
294   }
295   return NULL;
296 }
297
298 /*************************************************************************
299  * StrChrIW     [SHLWAPI.@]
300  *
301  * See StrChrA.
302  */
303 LPWSTR WINAPI StrChrIW(LPCWSTR lpszStr, WCHAR ch)
304 {
305   TRACE("(%s,%i)\n", debugstr_w(lpszStr), ch);
306
307   if (lpszStr)
308   {
309     ch = toupperW(ch);
310     while (*lpszStr)
311     {
312       if (toupperW(*lpszStr) == ch)
313         return (LPWSTR)lpszStr;
314       lpszStr++;
315     }
316     lpszStr = NULL;
317   }
318   return (LPWSTR)lpszStr;
319 }
320
321 /*************************************************************************
322  * StrChrNW     [SHLWAPI.@]
323  */
324 LPWSTR WINAPI StrChrNW(LPCWSTR lpszStr, WCHAR ch, UINT cchMax)
325 {
326   TRACE("(%s(%i),%i)\n", debugstr_wn(lpszStr,cchMax), cchMax, ch);
327
328   if (lpszStr)
329   {
330     while (*lpszStr && cchMax-- > 0)
331     {
332       if (*lpszStr == ch)
333         return (LPWSTR)lpszStr;
334       lpszStr++;
335     }
336   }
337   return NULL;
338 }
339
340 /*************************************************************************
341  * StrCmpIW     [SHLWAPI.@]
342  *
343  * Compare two strings, ignoring case.
344  *
345  * PARAMS
346  *  lpszStr  [I] First string to compare
347  *  lpszComp [I] Second string to compare
348  *
349  * RETURNS
350  *  An integer less than, equal to or greater than 0, indicating that
351  *  lpszStr is less than, the same, or greater than lpszComp.
352  */
353 int WINAPI StrCmpIW(LPCWSTR lpszStr, LPCWSTR lpszComp)
354 {
355   int iRet;
356
357   TRACE("(%s,%s)\n", debugstr_w(lpszStr),debugstr_w(lpszComp));
358
359   iRet = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, lpszStr, -1, lpszComp, -1);
360   return iRet == CSTR_LESS_THAN ? -1 : iRet == CSTR_GREATER_THAN ? 1 : 0;
361 }
362
363 /*************************************************************************
364  * StrCmpNA     [SHLWAPI.@]
365  *
366  * Compare two strings, up to a maximum length.
367  *
368  * PARAMS
369  *  lpszStr  [I] First string to compare
370  *  lpszComp [I] Second string to compare
371  *  iLen     [I] Maximum number of chars to compare.
372  *
373  * RETURNS
374  *  An integer less than, equal to or greater than 0, indicating that
375  *  lpszStr is less than, the same, or greater than lpszComp.
376  */
377 INT WINAPI StrCmpNA(LPCSTR lpszStr, LPCSTR lpszComp, INT iLen)
378 {
379   INT iRet;
380
381   TRACE("(%s,%s,%i)\n", debugstr_a(lpszStr), debugstr_a(lpszComp), iLen);
382
383   iRet = CompareStringA(GetThreadLocale(), 0, lpszStr, iLen, lpszComp, iLen);
384   return iRet == CSTR_LESS_THAN ? -1 : iRet == CSTR_GREATER_THAN ? 1 : 0;
385 }
386
387 /*************************************************************************
388  * StrCmpNW     [SHLWAPI.@]
389  *
390  * See StrCmpNA.
391  */
392 INT WINAPI StrCmpNW(LPCWSTR lpszStr, LPCWSTR lpszComp, INT iLen)
393 {
394   INT iRet;
395
396   TRACE("(%s,%s,%i)\n", debugstr_w(lpszStr), debugstr_w(lpszComp), iLen);
397
398   iRet = CompareStringW(GetThreadLocale(), 0, lpszStr, iLen, lpszComp, iLen);
399   return iRet == CSTR_LESS_THAN ? -1 : iRet == CSTR_GREATER_THAN ? 1 : 0;
400 }
401
402 /*************************************************************************
403  * StrCmpNIA    [SHLWAPI.@]
404  *
405  * Compare two strings, up to a maximum length, ignoring case.
406  *
407  * PARAMS
408  *  lpszStr  [I] First string to compare
409  *  lpszComp [I] Second string to compare
410  *  iLen     [I] Maximum number of chars to compare.
411  *
412  * RETURNS
413  *  An integer less than, equal to or greater than 0, indicating that
414  *  lpszStr is less than, the same, or greater than lpszComp.
415  */
416 int WINAPI StrCmpNIA(LPCSTR lpszStr, LPCSTR lpszComp, int iLen)
417 {
418   INT iRet;
419
420   TRACE("(%s,%s,%i)\n", debugstr_a(lpszStr), debugstr_a(lpszComp), iLen);
421
422   iRet = CompareStringA(GetThreadLocale(), NORM_IGNORECASE, lpszStr, iLen, lpszComp, iLen);
423   return iRet == CSTR_LESS_THAN ? -1 : iRet == CSTR_GREATER_THAN ? 1 : 0;
424 }
425
426 /*************************************************************************
427  * StrCmpNIW    [SHLWAPI.@]
428  *
429  * See StrCmpNIA.
430  */
431 INT WINAPI StrCmpNIW(LPCWSTR lpszStr, LPCWSTR lpszComp, int iLen)
432 {
433   INT iRet;
434
435   TRACE("(%s,%s,%i)\n", debugstr_w(lpszStr), debugstr_w(lpszComp), iLen);
436
437   iRet = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, lpszStr, iLen, lpszComp, iLen);
438   return iRet == CSTR_LESS_THAN ? -1 : iRet == CSTR_GREATER_THAN ? 1 : 0;
439 }
440
441 /*************************************************************************
442  * StrCmpW      [SHLWAPI.@]
443  *
444  * Compare two strings.
445  *
446  * PARAMS
447  *  lpszStr  [I] First string to compare
448  *  lpszComp [I] Second string to compare
449  *
450  * RETURNS
451  *  An integer less than, equal to or greater than 0, indicating that
452  *  lpszStr is less than, the same, or greater than lpszComp.
453  */
454 int WINAPI StrCmpW(LPCWSTR lpszStr, LPCWSTR lpszComp)
455 {
456   INT iRet;
457
458   TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszComp));
459
460   iRet = CompareStringW(GetThreadLocale(), 0, lpszStr, -1, lpszComp, -1);
461   return iRet == CSTR_LESS_THAN ? -1 : iRet == CSTR_GREATER_THAN ? 1 : 0;
462 }
463
464 /*************************************************************************
465  * StrCatW      [SHLWAPI.@]
466  *
467  * Concatenate two strings.
468  *
469  * PARAMS
470  *  lpszStr [O] Initial string
471  *  lpszSrc [I] String to concatenate
472  *
473  * RETURNS
474  *  lpszStr.
475  */
476 LPWSTR WINAPI StrCatW(LPWSTR lpszStr, LPCWSTR lpszSrc)
477 {
478   TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszSrc));
479
480   strcatW(lpszStr, lpszSrc);
481   return lpszStr;
482 }
483
484 /*************************************************************************
485  * StrCpyW      [SHLWAPI.@]
486  *
487  * Copy a string to another string.
488  *
489  * PARAMS
490  *  lpszStr [O] Destination string
491  *  lpszSrc [I] Source string
492  *
493  * RETURNS
494  *  lpszStr.
495  */
496 LPWSTR WINAPI StrCpyW(LPWSTR lpszStr, LPCWSTR lpszSrc)
497 {
498   TRACE("(%p,%s)\n", lpszStr, debugstr_w(lpszSrc));
499
500   strcpyW(lpszStr, lpszSrc);
501   return lpszStr;
502 }
503
504 /*************************************************************************
505  * StrCpyNW     [SHLWAPI.@]
506  *
507  * Copy a string to another string, up to a maximum number of characters.
508  *
509  * PARAMS
510  *  dst    [O] Destination string
511  *  src    [I] Source string
512  *  count  [I] Maximum number of chars to copy
513  *
514  * RETURNS
515  *  dst.
516  */
517 LPWSTR WINAPI StrCpyNW(LPWSTR dst, LPCWSTR src, int count)
518 {
519   LPWSTR d = dst;
520   LPCWSTR s = src;
521
522   TRACE("(%p,%s,%i)\n", dst, debugstr_w(src), count);
523
524   if (s)
525   {
526     while ((count > 1) && *s)
527     {
528       count--;
529       *d++ = *s++;
530     }
531   }
532   if (count) *d = 0;
533
534   return dst;
535 }
536
537 /*************************************************************************
538  * SHLWAPI_StrStrHelperA
539  *
540  * Internal implementation of StrStrA/StrStrIA
541  */
542 static LPSTR SHLWAPI_StrStrHelperA(LPCSTR lpszStr, LPCSTR lpszSearch,
543                                    INT (WINAPI *pStrCmpFn)(LPCSTR,LPCSTR,INT))
544 {
545   size_t iLen;
546
547   if (!lpszStr || !lpszSearch || !*lpszSearch)
548     return NULL;
549
550   iLen = strlen(lpszSearch);
551
552   while (*lpszStr)
553   {
554     if (!pStrCmpFn(lpszStr, lpszSearch, iLen))
555       return (LPSTR)lpszStr;
556     lpszStr = CharNextA(lpszStr);
557   }
558   return NULL;
559 }
560
561 /*************************************************************************
562  * StrStrA      [SHLWAPI.@]
563  *
564  * Find a substring within a string.
565  *
566  * PARAMS
567  *  lpszStr    [I] String to search in
568  *  lpszSearch [I] String to look for
569  *
570  * RETURNS
571  *  The start of lpszSearch within lpszStr, or NULL if not found.
572  */
573 LPSTR WINAPI StrStrA(LPCSTR lpszStr, LPCSTR lpszSearch)
574 {
575   TRACE("(%s,%s)\n", debugstr_a(lpszStr), debugstr_a(lpszSearch));
576
577   return SHLWAPI_StrStrHelperA(lpszStr, lpszSearch, StrCmpNA);
578 }
579
580 /*************************************************************************
581  * StrStrW      [SHLWAPI.@]
582  *
583  * See StrStrA.
584  */
585 LPWSTR WINAPI StrStrW(LPCWSTR lpszStr, LPCWSTR lpszSearch)
586 {
587     if (!lpszStr || !lpszSearch) return NULL;
588     return strstrW( lpszStr, lpszSearch );
589 }
590
591 /*************************************************************************
592  * StrRStrIA    [SHLWAPI.@]
593  *
594  * Find the last occurrence of a substring within a string.
595  *
596  * PARAMS
597  *  lpszStr    [I] String to search in
598  *  lpszEnd    [I] End of lpszStr
599  *  lpszSearch [I] String to look for
600  *
601  * RETURNS
602  *  The last occurrence lpszSearch within lpszStr, or NULL if not found.
603  */
604 LPSTR WINAPI StrRStrIA(LPCSTR lpszStr, LPCSTR lpszEnd, LPCSTR lpszSearch)
605 {
606   WORD ch1, ch2;
607   INT iLen;
608
609   TRACE("(%s,%s)\n", debugstr_a(lpszStr), debugstr_a(lpszSearch));
610
611   if (!lpszStr || !lpszSearch || !*lpszSearch)
612     return NULL;
613
614   if (!lpszEnd)
615     lpszEnd = lpszStr + lstrlenA(lpszStr);
616   if (lpszEnd == lpszStr)
617     return NULL;
618
619   if (IsDBCSLeadByte(*lpszSearch))
620     ch1 = *lpszSearch << 8 | (UCHAR)lpszSearch[1];
621   else
622     ch1 = *lpszSearch;
623   iLen = lstrlenA(lpszSearch);
624
625   do
626   {
627     lpszEnd = CharPrevA(lpszStr, lpszEnd);
628     ch2 = IsDBCSLeadByte(*lpszEnd)? *lpszEnd << 8 | (UCHAR)lpszEnd[1] : *lpszEnd;
629     if (!ChrCmpIA(ch1, ch2))
630     {
631       if (!StrCmpNIA(lpszEnd, lpszSearch, iLen))
632         return (LPSTR)lpszEnd;
633     }
634   } while (lpszEnd > lpszStr);
635   return NULL;
636 }
637
638 /*************************************************************************
639  * StrRStrIW    [SHLWAPI.@]
640  *
641  * See StrRStrIA.
642  */
643 LPWSTR WINAPI StrRStrIW(LPCWSTR lpszStr, LPCWSTR lpszEnd, LPCWSTR lpszSearch)
644 {
645   INT iLen;
646
647   TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszSearch));
648
649   if (!lpszStr || !lpszSearch || !*lpszSearch)
650     return NULL;
651
652   if (!lpszEnd)
653     lpszEnd = lpszStr + strlenW(lpszStr);
654
655   iLen = strlenW(lpszSearch);
656
657   while (lpszEnd > lpszStr)
658   {
659     lpszEnd--;
660     if (!StrCmpNIW(lpszEnd, lpszSearch, iLen))
661       return (LPWSTR)lpszEnd;
662   }
663   return NULL;
664 }
665
666 /*************************************************************************
667  * StrStrIA     [SHLWAPI.@]
668  *
669  * Find a substring within a string, ignoring case.
670  *
671  * PARAMS
672  *  lpszStr    [I] String to search in
673  *  lpszSearch [I] String to look for
674  *
675  * RETURNS
676  *  The start of lpszSearch within lpszStr, or NULL if not found.
677  */
678 LPSTR WINAPI StrStrIA(LPCSTR lpszStr, LPCSTR lpszSearch)
679 {
680   TRACE("(%s,%s)\n", debugstr_a(lpszStr), debugstr_a(lpszSearch));
681
682   return SHLWAPI_StrStrHelperA(lpszStr, lpszSearch, StrCmpNIA);
683 }
684
685 /*************************************************************************
686  * StrStrIW     [SHLWAPI.@]
687  *
688  * See StrStrIA.
689  */
690 LPWSTR WINAPI StrStrIW(LPCWSTR lpszStr, LPCWSTR lpszSearch)
691 {
692   int iLen;
693
694   TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszSearch));
695
696   if (!lpszStr || !lpszSearch || !*lpszSearch)
697     return NULL;
698
699   iLen = strlenW(lpszSearch);
700
701   while (*lpszStr)
702   {
703     if (!StrCmpNIW(lpszStr, lpszSearch, iLen))
704       return (LPWSTR)lpszStr;
705     lpszStr++;
706   }
707   return NULL;
708 }
709
710 /*************************************************************************
711  * StrToIntA    [SHLWAPI.@]
712  *
713  * Read a signed integer from a string.
714  *
715  * PARAMS
716  *  lpszStr [I] String to read integer from
717  *
718  * RETURNS
719  *   The signed integer value represented by the string, or 0 if no integer is
720  *   present.
721  *
722  * NOTES
723  *  No leading space is allowed before the number, although a leading '-' is.
724  */
725 int WINAPI StrToIntA(LPCSTR lpszStr)
726 {
727   int iRet = 0;
728
729   TRACE("(%s)\n", debugstr_a(lpszStr));
730
731   if (!lpszStr)
732   {
733     WARN("Invalid lpszStr would crash under Win32!\n");
734     return 0;
735   }
736
737   if (*lpszStr == '-' || isdigit(*lpszStr))
738     StrToIntExA(lpszStr, 0, &iRet);
739   return iRet;
740 }
741
742 /*************************************************************************
743  * StrToIntW    [SHLWAPI.@]
744  *
745  * See StrToIntA.
746  */
747 int WINAPI StrToIntW(LPCWSTR lpszStr)
748 {
749   int iRet = 0;
750
751   TRACE("(%s)\n", debugstr_w(lpszStr));
752
753   if (!lpszStr)
754   {
755     WARN("Invalid lpszStr would crash under Win32!\n");
756     return 0;
757   }
758
759   if (*lpszStr == '-' || isdigitW(*lpszStr))
760     StrToIntExW(lpszStr, 0, &iRet);
761   return iRet;
762 }
763
764 /*************************************************************************
765  * StrToIntExA  [SHLWAPI.@]
766  *
767  * Read an integer from a string.
768  *
769  * PARAMS
770  *  lpszStr [I] String to read integer from
771  *  dwFlags [I] Flags controlling the conversion
772  *  lpiRet  [O] Destination for read integer.
773  *
774  * RETURNS
775  *  Success: TRUE. lpiRet contains the integer value represented by the string.
776  *  Failure: FALSE, if the string is invalid, or no number is present.
777  *
778  * NOTES
779  *  Leading whitespace, '-' and '+' are allowed before the number. If
780  *  dwFlags includes STIF_SUPPORT_HEX, hexadecimal numbers are allowed, if
781  *  preceded by '0x'. If this flag is not set, or there is no '0x' prefix,
782  *  the string is treated as a decimal string. A leading '-' is ignored for
783  *  hexadecimal numbers.
784  */
785 BOOL WINAPI StrToIntExA(LPCSTR lpszStr, DWORD dwFlags, LPINT lpiRet)
786 {
787   BOOL bNegative = FALSE;
788   int iRet = 0;
789
790   TRACE("(%s,%08X,%p)\n", debugstr_a(lpszStr), dwFlags, lpiRet);
791
792   if (!lpszStr || !lpiRet)
793   {
794     WARN("Invalid parameter would crash under Win32!\n");
795     return FALSE;
796   }
797   if (dwFlags > STIF_SUPPORT_HEX)
798   {
799     WARN("Unknown flags (%08lX)!\n", dwFlags & ~STIF_SUPPORT_HEX);
800   }
801
802   /* Skip leading space, '+', '-' */
803   while (isspace(*lpszStr))
804     lpszStr = CharNextA(lpszStr);
805
806   if (*lpszStr == '-')
807   {
808     bNegative = TRUE;
809     lpszStr++;
810   }
811   else if (*lpszStr == '+')
812     lpszStr++;
813
814   if (dwFlags & STIF_SUPPORT_HEX &&
815       *lpszStr == '0' && tolower(lpszStr[1]) == 'x')
816   {
817     /* Read hex number */
818     lpszStr += 2;
819
820     if (!isxdigit(*lpszStr))
821       return FALSE;
822
823     while (isxdigit(*lpszStr))
824     {
825       iRet = iRet * 16;
826       if (isdigit(*lpszStr))
827         iRet += (*lpszStr - '0');
828       else
829         iRet += 10 + (tolower(*lpszStr) - 'a');
830       lpszStr++;
831     }
832     *lpiRet = iRet;
833     return TRUE;
834   }
835
836   /* Read decimal number */
837   if (!isdigit(*lpszStr))
838     return FALSE;
839
840   while (isdigit(*lpszStr))
841   {
842     iRet = iRet * 10;
843     iRet += (*lpszStr - '0');
844     lpszStr++;
845   }
846   *lpiRet = bNegative ? -iRet : iRet;
847   return TRUE;
848 }
849
850 /*************************************************************************
851  * StrToIntExW  [SHLWAPI.@]
852  *
853  * See StrToIntExA.
854  */
855 BOOL WINAPI StrToIntExW(LPCWSTR lpszStr, DWORD dwFlags, LPINT lpiRet)
856 {
857   BOOL bNegative = FALSE;
858   int iRet = 0;
859
860   TRACE("(%s,%08X,%p)\n", debugstr_w(lpszStr), dwFlags, lpiRet);
861
862   if (!lpszStr || !lpiRet)
863   {
864     WARN("Invalid parameter would crash under Win32!\n");
865     return FALSE;
866   }
867   if (dwFlags > STIF_SUPPORT_HEX)
868   {
869     WARN("Unknown flags (%08lX)!\n", dwFlags & ~STIF_SUPPORT_HEX);
870   }
871
872   /* Skip leading space, '+', '-' */
873   while (isspaceW(*lpszStr)) lpszStr++;
874
875   if (*lpszStr == '-')
876   {
877     bNegative = TRUE;
878     lpszStr++;
879   }
880   else if (*lpszStr == '+')
881     lpszStr++;
882
883   if (dwFlags & STIF_SUPPORT_HEX &&
884       *lpszStr == '0' && tolowerW(lpszStr[1]) == 'x')
885   {
886     /* Read hex number */
887     lpszStr += 2;
888
889     if (!isxdigitW(*lpszStr))
890       return FALSE;
891
892     while (isxdigitW(*lpszStr))
893     {
894       iRet = iRet * 16;
895       if (isdigitW(*lpszStr))
896         iRet += (*lpszStr - '0');
897       else
898         iRet += 10 + (tolowerW(*lpszStr) - 'a');
899       lpszStr++;
900     }
901     *lpiRet = iRet;
902     return TRUE;
903   }
904
905   /* Read decimal number */
906   if (!isdigitW(*lpszStr))
907     return FALSE;
908
909   while (isdigitW(*lpszStr))
910   {
911     iRet = iRet * 10;
912     iRet += (*lpszStr - '0');
913     lpszStr++;
914   }
915   *lpiRet = bNegative ? -iRet : iRet;
916   return TRUE;
917 }
918
919 /*************************************************************************
920  * StrDupA      [SHLWAPI.@]
921  *
922  * Duplicate a string.
923  *
924  * PARAMS
925  *  lpszStr [I] String to duplicate.
926  *
927  * RETURNS
928  *  Success: A pointer to a new string containing the contents of lpszStr
929  *  Failure: NULL, if memory cannot be allocated
930  *
931  * NOTES
932  *  The string memory is allocated with LocalAlloc(), and so should be released
933  *  by calling LocalFree().
934  */
935 LPSTR WINAPI StrDupA(LPCSTR lpszStr)
936 {
937   int iLen;
938   LPSTR lpszRet;
939
940   TRACE("(%s)\n",debugstr_a(lpszStr));
941
942   iLen = lpszStr ? strlen(lpszStr) + 1 : 1;
943   lpszRet = LocalAlloc(LMEM_FIXED, iLen);
944
945   if (lpszRet)
946   {
947     if (lpszStr)
948       memcpy(lpszRet, lpszStr, iLen);
949     else
950       *lpszRet = '\0';
951   }
952   return lpszRet;
953 }
954
955 /*************************************************************************
956  * StrDupW      [SHLWAPI.@]
957  *
958  * See StrDupA.
959  */
960 LPWSTR WINAPI StrDupW(LPCWSTR lpszStr)
961 {
962   int iLen;
963   LPWSTR lpszRet;
964
965   TRACE("(%s)\n",debugstr_w(lpszStr));
966
967   iLen = (lpszStr ? strlenW(lpszStr) + 1 : 1) * sizeof(WCHAR);
968   lpszRet = LocalAlloc(LMEM_FIXED, iLen);
969
970   if (lpszRet)
971   {
972     if (lpszStr)
973       memcpy(lpszRet, lpszStr, iLen);
974     else
975       *lpszRet = '\0';
976   }
977   return lpszRet;
978 }
979
980 /*************************************************************************
981  * SHLWAPI_StrSpnHelperA
982  *
983  * Internal implementation of StrSpnA/StrCSpnA/StrCSpnIA
984  */
985 static int SHLWAPI_StrSpnHelperA(LPCSTR lpszStr, LPCSTR lpszMatch,
986                                  LPSTR (WINAPI *pStrChrFn)(LPCSTR,WORD),
987                                  BOOL bInvert)
988 {
989   LPCSTR lpszRead = lpszStr;
990   if (lpszStr && *lpszStr && lpszMatch)
991   {
992     while (*lpszRead)
993     {
994       LPCSTR lpszTest = pStrChrFn(lpszMatch, *lpszRead);
995
996       if (!bInvert && !lpszTest)
997         break;
998       if (bInvert && lpszTest)
999         break;
1000       lpszRead = CharNextA(lpszRead);
1001     };
1002   }
1003   return lpszRead - lpszStr;
1004 }
1005
1006 /*************************************************************************
1007  * StrSpnA      [SHLWAPI.@]
1008  *
1009  * Find the length of the start of a string that contains only certain
1010  * characters.
1011  *
1012  * PARAMS
1013  *  lpszStr   [I] String to search
1014  *  lpszMatch [I] Characters that can be in the substring
1015  *
1016  * RETURNS
1017  *  The length of the part of lpszStr containing only chars from lpszMatch,
1018  *  or 0 if any parameter is invalid.
1019  */
1020 int WINAPI StrSpnA(LPCSTR lpszStr, LPCSTR lpszMatch)
1021 {
1022   TRACE("(%s,%s)\n",debugstr_a(lpszStr), debugstr_a(lpszMatch));
1023
1024   return SHLWAPI_StrSpnHelperA(lpszStr, lpszMatch, StrChrA, FALSE);
1025 }
1026
1027 /*************************************************************************
1028  * StrSpnW      [SHLWAPI.@]
1029  *
1030  * See StrSpnA.
1031  */
1032 int WINAPI StrSpnW(LPCWSTR lpszStr, LPCWSTR lpszMatch)
1033 {
1034     if (!lpszStr || !lpszMatch) return 0;
1035     return strspnW( lpszStr, lpszMatch );
1036 }
1037
1038 /*************************************************************************
1039  * StrCSpnA     [SHLWAPI.@]
1040  *
1041  * Find the length of the start of a string that does not contain certain
1042  * characters.
1043  *
1044  * PARAMS
1045  *  lpszStr   [I] String to search
1046  *  lpszMatch [I] Characters that cannot be in the substring
1047  *
1048  * RETURNS
1049  *  The length of the part of lpszStr containing only chars not in lpszMatch,
1050  *  or 0 if any parameter is invalid.
1051  */
1052 int WINAPI StrCSpnA(LPCSTR lpszStr, LPCSTR lpszMatch)
1053 {
1054   TRACE("(%s,%s)\n",debugstr_a(lpszStr), debugstr_a(lpszMatch));
1055
1056   return SHLWAPI_StrSpnHelperA(lpszStr, lpszMatch, StrChrA, TRUE);
1057 }
1058
1059 /*************************************************************************
1060  * StrCSpnW     [SHLWAPI.@]
1061  *
1062  * See StrCSpnA.
1063  */
1064 int WINAPI StrCSpnW(LPCWSTR lpszStr, LPCWSTR lpszMatch)
1065 {
1066     if (!lpszStr || !lpszMatch) return 0;
1067     return strcspnW( lpszStr, lpszMatch );
1068 }
1069
1070 /*************************************************************************
1071  * StrCSpnIA    [SHLWAPI.@]
1072  *
1073  * Find the length of the start of a string that does not contain certain
1074  * characters, ignoring case.
1075  *
1076  * PARAMS
1077  *  lpszStr   [I] String to search
1078  *  lpszMatch [I] Characters that cannot be in the substring
1079  *
1080  * RETURNS
1081  *  The length of the part of lpszStr containing only chars not in lpszMatch,
1082  *  or 0 if any parameter is invalid.
1083  */
1084 int WINAPI StrCSpnIA(LPCSTR lpszStr, LPCSTR lpszMatch)
1085 {
1086   TRACE("(%s,%s)\n",debugstr_a(lpszStr), debugstr_a(lpszMatch));
1087
1088   return SHLWAPI_StrSpnHelperA(lpszStr, lpszMatch, StrChrIA, TRUE);
1089 }
1090
1091 /*************************************************************************
1092  * StrCSpnIW    [SHLWAPI.@]
1093  *
1094  * See StrCSpnIA.
1095  */
1096 int WINAPI StrCSpnIW(LPCWSTR lpszStr, LPCWSTR lpszMatch)
1097 {
1098   LPCWSTR lpszRead = lpszStr;
1099
1100   TRACE("(%s,%s)\n",debugstr_w(lpszStr), debugstr_w(lpszMatch));
1101
1102   if (lpszStr && *lpszStr && lpszMatch)
1103   {
1104     while (*lpszRead)
1105     {
1106       if (StrChrIW(lpszMatch, *lpszRead)) break;
1107       lpszRead++;
1108     }
1109   }
1110   return lpszRead - lpszStr;
1111 }
1112
1113 /*************************************************************************
1114  * StrPBrkA     [SHLWAPI.@]
1115  *
1116  * Search a string for any of a group of characters.
1117  *
1118  * PARAMS
1119  *  lpszStr   [I] String to search
1120  *  lpszMatch [I] Characters to match
1121  *
1122  * RETURNS
1123  *  A pointer to the first matching character in lpszStr, or NULL if no
1124  *  match was found.
1125  */
1126 LPSTR WINAPI StrPBrkA(LPCSTR lpszStr, LPCSTR lpszMatch)
1127 {
1128   TRACE("(%s,%s)\n",debugstr_a(lpszStr), debugstr_a(lpszMatch));
1129
1130   if (lpszStr && lpszMatch && *lpszMatch)
1131   {
1132     while (*lpszStr)
1133     {
1134       if (StrChrA(lpszMatch, *lpszStr))
1135         return (LPSTR)lpszStr;
1136       lpszStr = CharNextA(lpszStr);
1137     }
1138   }
1139   return NULL;
1140 }
1141
1142 /*************************************************************************
1143  * StrPBrkW     [SHLWAPI.@]
1144  *
1145  * See StrPBrkA.
1146  */
1147 LPWSTR WINAPI StrPBrkW(LPCWSTR lpszStr, LPCWSTR lpszMatch)
1148 {
1149     if (!lpszStr || !lpszMatch) return NULL;
1150     return strpbrkW( lpszStr, lpszMatch );
1151 }
1152
1153 /*************************************************************************
1154  * SHLWAPI_StrRChrHelperA
1155  *
1156  * Internal implementation of StrRChrA/StrRChrIA.
1157  */
1158 static LPSTR SHLWAPI_StrRChrHelperA(LPCSTR lpszStr,
1159                                     LPCSTR lpszEnd, WORD ch,
1160                                     BOOL (WINAPI *pChrCmpFn)(WORD,WORD))
1161 {
1162   LPCSTR lpszRet = NULL;
1163
1164   if (lpszStr)
1165   {
1166     WORD ch2;
1167
1168     if (!lpszEnd)
1169       lpszEnd = lpszStr + lstrlenA(lpszStr);
1170
1171     while (*lpszStr && lpszStr <= lpszEnd)
1172     {
1173       ch2 = IsDBCSLeadByte(*lpszStr)? *lpszStr << 8 | lpszStr[1] : *lpszStr;
1174
1175       if (!pChrCmpFn(ch, ch2))
1176         lpszRet = lpszStr;
1177       lpszStr = CharNextA(lpszStr);
1178     }
1179   }
1180   return (LPSTR)lpszRet;
1181 }
1182
1183 /**************************************************************************
1184  * StrRChrA     [SHLWAPI.@]
1185  *
1186  * Find the last occurrence of a character in string.
1187  *
1188  * PARAMS
1189  *  lpszStr [I] String to search in
1190  *  lpszEnd [I] Place to end search, or NULL to search until the end of lpszStr
1191  *  ch      [I] Character to search for.
1192  *
1193  * RETURNS
1194  *  Success: A pointer to the last occurrence of ch in lpszStr before lpszEnd,
1195  *           or NULL if not found.
1196  *  Failure: NULL, if any arguments are invalid.
1197  */
1198 LPSTR WINAPI StrRChrA(LPCSTR lpszStr, LPCSTR lpszEnd, WORD ch)
1199 {
1200   TRACE("(%s,%s,%x)\n", debugstr_a(lpszStr), debugstr_a(lpszEnd), ch);
1201
1202   return SHLWAPI_StrRChrHelperA(lpszStr, lpszEnd, ch, SHLWAPI_ChrCmpA);
1203 }
1204
1205 /**************************************************************************
1206  * StrRChrW     [SHLWAPI.@]
1207  *
1208  * See StrRChrA.
1209  */
1210 LPWSTR WINAPI StrRChrW(LPCWSTR str, LPCWSTR end, WORD ch)
1211 {
1212     WCHAR *ret = NULL;
1213
1214     if (!str) return NULL;
1215     if (!end) end = str + strlenW(str);
1216     while (str < end)
1217     {
1218         if (*str == ch) ret = (WCHAR *)str;
1219         str++;
1220     }
1221     return ret;
1222 }
1223
1224 /**************************************************************************
1225  * StrRChrIA    [SHLWAPI.@]
1226  *
1227  * Find the last occurrence of a character in string, ignoring case.
1228  *
1229  * PARAMS
1230  *  lpszStr [I] String to search in
1231  *  lpszEnd [I] Place to end search, or NULL to search until the end of lpszStr
1232  *  ch      [I] Character to search for.
1233  *
1234  * RETURNS
1235  *  Success: A pointer to the last occurrence of ch in lpszStr before lpszEnd,
1236  *           or NULL if not found.
1237  *  Failure: NULL, if any arguments are invalid.
1238  */
1239 LPSTR WINAPI StrRChrIA(LPCSTR lpszStr, LPCSTR lpszEnd, WORD ch)
1240 {
1241   TRACE("(%s,%s,%x)\n", debugstr_a(lpszStr), debugstr_a(lpszEnd), ch);
1242
1243   return SHLWAPI_StrRChrHelperA(lpszStr, lpszEnd, ch, ChrCmpIA);
1244 }
1245
1246 /**************************************************************************
1247  * StrRChrIW    [SHLWAPI.@]
1248  *
1249  * See StrRChrIA.
1250  */
1251 LPWSTR WINAPI StrRChrIW(LPCWSTR str, LPCWSTR end, WORD ch)
1252 {
1253     WCHAR *ret = NULL;
1254
1255     if (!str) return NULL;
1256     if (!end) end = str + strlenW(str);
1257     while (str < end)
1258     {
1259         if (!ChrCmpIW(*str, ch)) ret = (WCHAR *)str;
1260         str++;
1261     }
1262     return ret;
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 determines 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_OFFSET
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, UINT 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%x 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(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, UINT len)
1394 {
1395         TRACE("dest=%p len=0x%x 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(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  * PARAMS
1444  *  lpStrRet [O] STRRET to convert
1445  *  pidl     [I] ITEMIDLIST for lpStrRet->uType == STRRET_OFFSET
1446  *  ppszName [O] Destination for converted string
1447  *
1448  * RETURNS
1449  *  Success: S_OK. ppszName contains the new string, allocated with CoTaskMemAlloc().
1450  *  Failure: E_FAIL, if any parameters are invalid.
1451  */
1452 HRESULT WINAPI StrRetToStrA(LPSTRRET lpStrRet, const ITEMIDLIST *pidl, LPSTR *ppszName)
1453 {
1454   HRESULT hRet = E_FAIL;
1455
1456   switch (lpStrRet->uType)
1457   {
1458   case STRRET_WSTR:
1459     hRet = _SHStrDupAW(lpStrRet->u.pOleStr, ppszName);
1460     CoTaskMemFree(lpStrRet->u.pOleStr);
1461     break;
1462
1463   case STRRET_CSTR:
1464     hRet = _SHStrDupAA(lpStrRet->u.cStr, ppszName);
1465     break;
1466
1467   case STRRET_OFFSET:
1468     hRet = _SHStrDupAA(((LPCSTR)&pidl->mkid) + lpStrRet->u.uOffset, ppszName);
1469     break;
1470
1471   default:
1472     *ppszName = NULL;
1473   }
1474
1475   return hRet;
1476 }
1477
1478 /*************************************************************************
1479  * StrRetToStrW                                 [SHLWAPI.@]
1480  *
1481  * See StrRetToStrA.
1482  */
1483 HRESULT WINAPI StrRetToStrW(LPSTRRET lpStrRet, const ITEMIDLIST *pidl, LPWSTR *ppszName)
1484 {
1485   HRESULT hRet = E_FAIL;
1486
1487   switch (lpStrRet->uType)
1488   {
1489   case STRRET_WSTR:
1490     hRet = SHStrDupW(lpStrRet->u.pOleStr, ppszName);
1491     CoTaskMemFree(lpStrRet->u.pOleStr);
1492     break;
1493
1494   case STRRET_CSTR:
1495     hRet = SHStrDupA(lpStrRet->u.cStr, ppszName);
1496     break;
1497
1498   case STRRET_OFFSET:
1499     hRet = SHStrDupA(((LPCSTR)&pidl->mkid) + lpStrRet->u.uOffset, ppszName);
1500     break;
1501
1502   default:
1503     *ppszName = NULL;
1504   }
1505
1506   return hRet;
1507 }
1508
1509 /* Create an ASCII string copy using SysAllocString() */
1510 static HRESULT _SHStrDupAToBSTR(LPCSTR src, BSTR *pBstrOut)
1511 {
1512     *pBstrOut = NULL;
1513
1514     if (src)
1515     {
1516         INT len = MultiByteToWideChar(CP_ACP, 0, src, -1, NULL, 0);
1517         WCHAR* szTemp = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1518
1519         if (szTemp)
1520         {
1521             MultiByteToWideChar(CP_ACP, 0, src, -1, szTemp, len);
1522             *pBstrOut = SysAllocString(szTemp);
1523             HeapFree(GetProcessHeap(), 0, szTemp);
1524
1525             if (*pBstrOut)
1526                 return S_OK;
1527         }
1528     }
1529     return E_OUTOFMEMORY;
1530 }
1531
1532 /*************************************************************************
1533  * StrRetToBSTR [SHLWAPI.@]
1534  *
1535  * Converts a STRRET to a BSTR.
1536  *
1537  * PARAMS
1538  *  lpStrRet [O] STRRET to convert
1539  *  pidl     [I] ITEMIDLIST for lpStrRet->uType = STRRET_OFFSET
1540  *  pBstrOut [O] Destination for converted BSTR
1541  *
1542  * RETURNS
1543  *  Success: S_OK. pBstrOut contains the new string.
1544  *  Failure: E_FAIL, if any parameters are invalid.
1545  */
1546 HRESULT WINAPI StrRetToBSTR(STRRET *lpStrRet, LPCITEMIDLIST pidl, BSTR* pBstrOut)
1547 {
1548   HRESULT hRet = E_FAIL;
1549
1550   switch (lpStrRet->uType)
1551   {
1552   case STRRET_WSTR:
1553     *pBstrOut = SysAllocString(lpStrRet->u.pOleStr);
1554     if (*pBstrOut)
1555       hRet = S_OK;
1556     CoTaskMemFree(lpStrRet->u.pOleStr);
1557     break;
1558
1559   case STRRET_CSTR:
1560     hRet = _SHStrDupAToBSTR(lpStrRet->u.cStr, pBstrOut);
1561     break;
1562
1563   case STRRET_OFFSET:
1564     hRet = _SHStrDupAToBSTR(((LPCSTR)&pidl->mkid) + lpStrRet->u.uOffset, pBstrOut);
1565     break;
1566
1567   default:
1568     *pBstrOut = NULL;
1569   }
1570
1571   return hRet;
1572 }
1573
1574 /*************************************************************************
1575  * StrFormatKBSizeA     [SHLWAPI.@]
1576  *
1577  * Create a formatted string containing a byte count in Kilobytes.
1578  *
1579  * PARAMS
1580  *  llBytes  [I] Byte size to format
1581  *  lpszDest [I] Destination for formatted string
1582  *  cchMax   [I] Size of lpszDest
1583  *
1584  * RETURNS
1585  *  lpszDest.
1586  */
1587 LPSTR WINAPI StrFormatKBSizeA(LONGLONG llBytes, LPSTR lpszDest, UINT cchMax)
1588 {
1589   WCHAR wszBuf[256];
1590   
1591   if (!StrFormatKBSizeW(llBytes, wszBuf, 256))
1592     return NULL;
1593   if (!WideCharToMultiByte(CP_ACP, 0, wszBuf, -1, lpszDest, cchMax, NULL, NULL))
1594     return NULL;
1595   return lpszDest;
1596 }
1597
1598 /*************************************************************************
1599  * StrFormatKBSizeW     [SHLWAPI.@]
1600  *
1601  * See StrFormatKBSizeA.
1602  */
1603 LPWSTR WINAPI StrFormatKBSizeW(LONGLONG llBytes, LPWSTR lpszDest, UINT cchMax)
1604 {
1605   static const WCHAR kb[] = {' ','K','B',0};
1606   LONGLONG llKB = (llBytes + 1023) >> 10;
1607   int len;
1608
1609   TRACE("(0x%s,%p,%d)\n", wine_dbgstr_longlong(llBytes), lpszDest, cchMax);
1610
1611   if (!FormatInt(llKB, lpszDest, cchMax))
1612     return NULL;
1613
1614   len = lstrlenW(lpszDest);
1615   if (cchMax - len < 4)
1616       return NULL;
1617   lstrcatW(lpszDest, kb);
1618   return lpszDest;
1619 }
1620
1621 /*************************************************************************
1622  * StrNCatA     [SHLWAPI.@]
1623  *
1624  * Concatenate two strings together.
1625  *
1626  * PARAMS
1627  *  lpszStr [O] String to concatenate to
1628  *  lpszCat [I] String to add to lpszCat
1629  *  cchMax  [I] Maximum number of characters to concatenate
1630  *
1631  * RETURNS
1632  *  lpszStr.
1633  *
1634  * NOTES
1635  *  cchMax determines the number of characters that are appended to lpszStr,
1636  *  not the total length of the string.
1637  */
1638 LPSTR WINAPI StrNCatA(LPSTR lpszStr, LPCSTR lpszCat, INT cchMax)
1639 {
1640   LPSTR lpszRet = lpszStr;
1641
1642   TRACE("(%s,%s,%i)\n", debugstr_a(lpszStr), debugstr_a(lpszCat), cchMax);
1643
1644   if (!lpszStr)
1645   {
1646     WARN("Invalid lpszStr would crash under Win32!\n");
1647     return NULL;
1648   }
1649
1650   StrCpyNA(lpszStr + strlen(lpszStr), lpszCat, cchMax);
1651   return lpszRet;
1652 }
1653
1654 /*************************************************************************
1655  * StrNCatW     [SHLWAPI.@]
1656  *
1657  * See StrNCatA.
1658  */
1659 LPWSTR WINAPI StrNCatW(LPWSTR lpszStr, LPCWSTR lpszCat, INT cchMax)
1660 {
1661   LPWSTR lpszRet = lpszStr;
1662
1663   TRACE("(%s,%s,%i)\n", debugstr_w(lpszStr), debugstr_w(lpszCat), cchMax);
1664
1665   if (!lpszStr)
1666   {
1667     WARN("Invalid lpszStr would crash under Win32\n");
1668     return NULL;
1669   }
1670
1671   StrCpyNW(lpszStr + strlenW(lpszStr), lpszCat, cchMax);
1672   return lpszRet;
1673 }
1674
1675 /*************************************************************************
1676  * StrTrimA     [SHLWAPI.@]
1677  *
1678  * Remove characters from the start and end of a string.
1679  *
1680  * PARAMS
1681  *  lpszStr  [O] String to remove characters from
1682  *  lpszTrim [I] Characters to remove from lpszStr
1683  *
1684  * RETURNS
1685  *  TRUE  If lpszStr was valid and modified
1686  *  FALSE Otherwise
1687  */
1688 BOOL WINAPI StrTrimA(LPSTR lpszStr, LPCSTR lpszTrim)
1689 {
1690   DWORD dwLen;
1691   LPSTR lpszRead = lpszStr;
1692   BOOL bRet = FALSE;
1693
1694   TRACE("(%s,%s)\n", debugstr_a(lpszStr), debugstr_a(lpszTrim));
1695
1696   if (lpszRead && *lpszRead)
1697   {
1698     while (*lpszRead && StrChrA(lpszTrim, *lpszRead))
1699       lpszRead = CharNextA(lpszRead); /* Skip leading matches */
1700
1701     dwLen = strlen(lpszRead);
1702
1703     if (lpszRead != lpszStr)
1704     {
1705       memmove(lpszStr, lpszRead, dwLen + 1);
1706       bRet = TRUE;
1707     }
1708     if (dwLen > 0)
1709     {
1710       lpszRead = lpszStr + dwLen;
1711       while (StrChrA(lpszTrim, lpszRead[-1]))
1712         lpszRead = CharPrevA(lpszStr, lpszRead); /* Skip trailing matches */
1713
1714       if (lpszRead != lpszStr + dwLen)
1715       {
1716         *lpszRead = '\0';
1717         bRet = TRUE;
1718       }
1719     }
1720   }
1721   return bRet;
1722 }
1723
1724 /*************************************************************************
1725  * StrTrimW     [SHLWAPI.@]
1726  *
1727  * See StrTrimA.
1728  */
1729 BOOL WINAPI StrTrimW(LPWSTR lpszStr, LPCWSTR lpszTrim)
1730 {
1731   DWORD dwLen;
1732   LPWSTR lpszRead = lpszStr;
1733   BOOL bRet = FALSE;
1734
1735   TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszTrim));
1736
1737   if (lpszRead && *lpszRead)
1738   {
1739     while (*lpszRead && StrChrW(lpszTrim, *lpszRead)) lpszRead++;
1740
1741     dwLen = strlenW(lpszRead);
1742
1743     if (lpszRead != lpszStr)
1744     {
1745       memmove(lpszStr, lpszRead, (dwLen + 1) * sizeof(WCHAR));
1746       bRet = TRUE;
1747     }
1748     if (dwLen > 0)
1749     {
1750       lpszRead = lpszStr + dwLen;
1751       while (StrChrW(lpszTrim, lpszRead[-1]))
1752         lpszRead--; /* Skip trailing matches */
1753
1754       if (lpszRead != lpszStr + dwLen)
1755       {
1756         *lpszRead = '\0';
1757         bRet = TRUE;
1758       }
1759     }
1760   }
1761   return bRet;
1762 }
1763
1764 /*************************************************************************
1765  *      _SHStrDupAA     [INTERNAL]
1766  *
1767  * Duplicates a ASCII string to ASCII. The destination buffer is allocated.
1768  */
1769 static HRESULT _SHStrDupAA(LPCSTR src, LPSTR * dest)
1770 {
1771         HRESULT hr;
1772         int len = 0;
1773
1774         if (src) {
1775             len = lstrlenA(src) + 1;
1776             *dest = CoTaskMemAlloc(len);
1777         } else {
1778             *dest = NULL;
1779         }
1780
1781         if (*dest) {
1782             lstrcpynA(*dest,src, len);
1783             hr = S_OK;
1784         } else {
1785             hr = E_OUTOFMEMORY;
1786         }
1787
1788         TRACE("%s->(%p)\n", debugstr_a(src), *dest);
1789         return hr;
1790 }
1791
1792 /*************************************************************************
1793  * SHStrDupA    [SHLWAPI.@]
1794  *
1795  * Return a Unicode copy of a string, in memory allocated by CoTaskMemAlloc().
1796  *
1797  * PARAMS
1798  *  lpszStr   [I] String to copy
1799  *  lppszDest [O] Destination for the new string copy
1800  *
1801  * RETURNS
1802  *  Success: S_OK. lppszDest contains the new string in Unicode format.
1803  *  Failure: E_OUTOFMEMORY, If any arguments are invalid or memory allocation
1804  *           fails.
1805  */
1806 HRESULT WINAPI SHStrDupA(LPCSTR lpszStr, LPWSTR * lppszDest)
1807 {
1808   HRESULT hRet;
1809   int len = 0;
1810
1811   if (lpszStr)
1812   {
1813     len = MultiByteToWideChar(0, 0, lpszStr, -1, 0, 0) * sizeof(WCHAR);
1814     *lppszDest = CoTaskMemAlloc(len);
1815   }
1816   else
1817     *lppszDest = NULL;
1818
1819   if (*lppszDest)
1820   {
1821     MultiByteToWideChar(0, 0, lpszStr, -1, *lppszDest, len/sizeof(WCHAR));
1822     hRet = S_OK;
1823   }
1824   else
1825     hRet = E_OUTOFMEMORY;
1826
1827   TRACE("%s->(%p)\n", debugstr_a(lpszStr), *lppszDest);
1828   return hRet;
1829 }
1830
1831 /*************************************************************************
1832  *      _SHStrDupAW     [INTERNAL]
1833  *
1834  * Duplicates a UNICODE to a ASCII string. The destination buffer is allocated.
1835  */
1836 static HRESULT _SHStrDupAW(LPCWSTR src, LPSTR * dest)
1837 {
1838         HRESULT hr;
1839         int len = 0;
1840
1841         if (src) {
1842             len = WideCharToMultiByte(CP_ACP, 0, src, -1, NULL, 0, NULL, NULL);
1843             *dest = CoTaskMemAlloc(len);
1844         } else {
1845             *dest = NULL;
1846         }
1847
1848         if (*dest) {
1849             WideCharToMultiByte(CP_ACP, 0, src, -1, *dest, len, NULL, NULL);
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  * SHStrDupW    [SHLWAPI.@]
1861  *
1862  * See SHStrDupA.
1863  */
1864 HRESULT WINAPI SHStrDupW(LPCWSTR src, LPWSTR * dest)
1865 {
1866         HRESULT hr;
1867         int len = 0;
1868
1869         if (src) {
1870             len = (lstrlenW(src) + 1) * sizeof(WCHAR);
1871             *dest = CoTaskMemAlloc(len);
1872         } else {
1873             *dest = NULL;
1874         }
1875
1876         if (*dest) {
1877             memcpy(*dest, src, len);
1878             hr = S_OK;
1879         } else {
1880             hr = E_OUTOFMEMORY;
1881         }
1882
1883         TRACE("%s->(%p)\n", debugstr_w(src), *dest);
1884         return hr;
1885 }
1886
1887 /*************************************************************************
1888  * SHLWAPI_WriteReverseNum
1889  *
1890  * Internal helper for SHLWAPI_WriteTimeClass.
1891  */
1892 static inline LPWSTR SHLWAPI_WriteReverseNum(LPWSTR lpszOut, DWORD dwNum)
1893 {
1894   *lpszOut-- = '\0';
1895
1896   /* Write a decimal number to a string, backwards */
1897   do
1898   {
1899     DWORD dwNextDigit = dwNum % 10;
1900     *lpszOut-- = '0' + dwNextDigit;
1901     dwNum = (dwNum - dwNextDigit) / 10;
1902   } while (dwNum > 0);
1903
1904   return lpszOut;
1905 }
1906
1907 /*************************************************************************
1908  * SHLWAPI_FormatSignificant
1909  *
1910  * Internal helper for SHLWAPI_WriteTimeClass.
1911  */
1912 static inline int SHLWAPI_FormatSignificant(LPWSTR lpszNum, int dwDigits)
1913 {
1914   /* Zero non significant digits, return remaining significant digits */
1915   while (*lpszNum)
1916   {
1917     lpszNum++;
1918     if (--dwDigits == 0)
1919     {
1920       while (*lpszNum)
1921         *lpszNum++ = '0';
1922       return 0;
1923     }
1924   }
1925   return dwDigits;
1926 }
1927
1928 /*************************************************************************
1929  * SHLWAPI_WriteTimeClass
1930  *
1931  * Internal helper for StrFromTimeIntervalW.
1932  */
1933 static int SHLWAPI_WriteTimeClass(LPWSTR lpszOut, DWORD dwValue,
1934                                   UINT uClassStringId, int iDigits)
1935 {
1936   WCHAR szBuff[64], *szOut = szBuff + 32;
1937
1938   szOut = SHLWAPI_WriteReverseNum(szOut, dwValue);
1939   iDigits = SHLWAPI_FormatSignificant(szOut + 1, iDigits);
1940   *szOut = ' ';
1941   LoadStringW(shlwapi_hInstance, uClassStringId, szBuff + 32, 32);
1942   strcatW(lpszOut, szOut);
1943   return iDigits;
1944 }
1945
1946 /*************************************************************************
1947  * StrFromTimeIntervalA [SHLWAPI.@]
1948  *
1949  * Format a millisecond time interval into a string
1950  *
1951  * PARAMS
1952  *  lpszStr  [O] Output buffer for formatted time interval
1953  *  cchMax   [I] Size of lpszStr
1954  *  dwMS     [I] Number of milliseconds
1955  *  iDigits  [I] Number of digits to print
1956  *
1957  * RETURNS
1958  *  The length of the formatted string, or 0 if any parameter is invalid.
1959  *
1960  * NOTES
1961  *  This implementation mimics the Win32 behaviour of always writing a leading
1962  *  space before the time interval begins.
1963  *
1964  *  iDigits is used to provide approximate times if accuracy is not important.
1965  *  This number of digits will be written of the first non-zero time class
1966  *  (hours/minutes/seconds). If this does not complete the time classification,
1967  *  the remaining digits are changed to zeros (i.e. The time is _not_ rounded).
1968  *  If there are digits remaining following the writing of a time class, the
1969  *  next time class will be written.
1970  *
1971  *  For example, given dwMS represents 138 hours,43 minutes and 15 seconds, the
1972  *  following will result from the given values of iDigits:
1973  *
1974  *|  iDigits    1        2        3        4               5               ...
1975  *|  lpszStr   "100 hr" "130 hr" "138 hr" "138 hr 40 min" "138 hr 43 min"  ...
1976  */
1977 INT WINAPI StrFromTimeIntervalA(LPSTR lpszStr, UINT cchMax, DWORD dwMS,
1978                                 int iDigits)
1979 {
1980   INT iRet = 0;
1981
1982   TRACE("(%p,%d,%d,%d)\n", lpszStr, cchMax, dwMS, iDigits);
1983
1984   if (lpszStr && cchMax)
1985   {
1986     WCHAR szBuff[128];
1987     StrFromTimeIntervalW(szBuff, sizeof(szBuff)/sizeof(WCHAR), dwMS, iDigits);
1988     WideCharToMultiByte(CP_ACP,0,szBuff,-1,lpszStr,cchMax,0,0);
1989   }
1990   return iRet;
1991 }
1992
1993
1994 /*************************************************************************
1995  * StrFromTimeIntervalW [SHLWAPI.@]
1996  *
1997  * See StrFromTimeIntervalA.
1998  */
1999 INT WINAPI StrFromTimeIntervalW(LPWSTR lpszStr, UINT cchMax, DWORD dwMS,
2000                                 int iDigits)
2001 {
2002   INT iRet = 0;
2003
2004   TRACE("(%p,%d,%d,%d)\n", lpszStr, cchMax, dwMS, iDigits);
2005
2006   if (lpszStr && cchMax)
2007   {
2008     WCHAR szCopy[128];
2009     DWORD dwHours, dwMinutes;
2010
2011     if (!iDigits || cchMax == 1)
2012     {
2013       *lpszStr = '\0';
2014       return 0;
2015     }
2016
2017     /* Calculate the time classes */
2018     dwMS = (dwMS + 500) / 1000;
2019     dwHours = dwMS / 3600;
2020     dwMS -= dwHours * 3600;
2021     dwMinutes = dwMS / 60;
2022     dwMS -= dwMinutes * 60;
2023
2024     szCopy[0] = '\0';
2025
2026     if (dwHours)
2027       iDigits = SHLWAPI_WriteTimeClass(szCopy, dwHours, IDS_TIME_INTERVAL_HOURS, iDigits);
2028
2029     if (dwMinutes && iDigits)
2030       iDigits = SHLWAPI_WriteTimeClass(szCopy, dwMinutes, IDS_TIME_INTERVAL_MINUTES, iDigits);
2031
2032     if (iDigits) /* Always write seconds if we have significant digits */
2033       SHLWAPI_WriteTimeClass(szCopy, dwMS, IDS_TIME_INTERVAL_SECONDS, iDigits);
2034
2035     lstrcpynW(lpszStr, szCopy, cchMax);
2036     iRet = strlenW(lpszStr);
2037   }
2038   return iRet;
2039 }
2040
2041 /*************************************************************************
2042  * StrIsIntlEqualA      [SHLWAPI.@]
2043  *
2044  * Compare two strings.
2045  *
2046  * PARAMS
2047  *  bCase    [I] Whether to compare case sensitively
2048  *  lpszStr  [I] First string to compare
2049  *  lpszComp [I] Second string to compare
2050  *  iLen     [I] Length to compare
2051  *
2052  * RETURNS
2053  *  TRUE  If the strings are equal.
2054  *  FALSE Otherwise.
2055  */
2056 BOOL WINAPI StrIsIntlEqualA(BOOL bCase, LPCSTR lpszStr, LPCSTR lpszComp,
2057                             int iLen)
2058 {
2059   DWORD dwFlags;
2060
2061   TRACE("(%d,%s,%s,%d)\n", bCase,
2062         debugstr_a(lpszStr), debugstr_a(lpszComp), iLen);
2063
2064   /* FIXME: This flag is undocumented and unknown by our CompareString.
2065    *        We need a define for it.
2066    */
2067   dwFlags = 0x10000000;
2068   if (!bCase) dwFlags |= NORM_IGNORECASE;
2069
2070   return (CompareStringA(GetThreadLocale(), dwFlags, lpszStr, iLen, lpszComp, iLen) == CSTR_EQUAL);
2071 }
2072
2073 /*************************************************************************
2074  * StrIsIntlEqualW      [SHLWAPI.@]
2075  *
2076  * See StrIsIntlEqualA.
2077  */
2078 BOOL WINAPI StrIsIntlEqualW(BOOL bCase, LPCWSTR lpszStr, LPCWSTR lpszComp,
2079                             int iLen)
2080 {
2081   DWORD dwFlags;
2082
2083   TRACE("(%d,%s,%s,%d)\n", bCase,
2084         debugstr_w(lpszStr),debugstr_w(lpszComp), iLen);
2085
2086   /* FIXME: This flag is undocumented and unknown by our CompareString.
2087    *        We need a define for it.
2088    */
2089   dwFlags = 0x10000000;
2090   if (!bCase) dwFlags |= NORM_IGNORECASE;
2091
2092   return (CompareStringW(GetThreadLocale(), dwFlags, lpszStr, iLen, lpszComp, iLen) == CSTR_EQUAL);
2093 }
2094
2095 /*************************************************************************
2096  * @    [SHLWAPI.399]
2097  *
2098  * Copy a string to another string, up to a maximum number of characters.
2099  *
2100  * PARAMS
2101  *  lpszDest [O] Destination string
2102  *  lpszSrc  [I] Source string
2103  *  iLen     [I] Maximum number of chars to copy
2104  *
2105  * RETURNS
2106  *  Success: A pointer to the last character written to lpszDest.
2107  *  Failure: lpszDest, if any arguments are invalid.
2108  */
2109 LPSTR WINAPI StrCpyNXA(LPSTR lpszDest, LPCSTR lpszSrc, int iLen)
2110 {
2111   TRACE("(%p,%s,%i)\n", lpszDest, debugstr_a(lpszSrc), iLen);
2112
2113   if (lpszDest && lpszSrc && iLen > 0)
2114   {
2115     while ((iLen-- > 1) && *lpszSrc)
2116       *lpszDest++ = *lpszSrc++;
2117     if (iLen >= 0)
2118      *lpszDest = '\0';
2119   }
2120   return lpszDest;
2121 }
2122
2123 /*************************************************************************
2124  * @    [SHLWAPI.400]
2125  *
2126  * Unicode version of StrCpyNXA.
2127  */
2128 LPWSTR WINAPI StrCpyNXW(LPWSTR lpszDest, LPCWSTR lpszSrc, int iLen)
2129 {
2130   TRACE("(%p,%s,%i)\n", lpszDest, debugstr_w(lpszSrc), iLen);
2131
2132   if (lpszDest && lpszSrc && iLen > 0)
2133   {
2134     while ((iLen-- > 1) && *lpszSrc)
2135       *lpszDest++ = *lpszSrc++;
2136     if (iLen >= 0)
2137      *lpszDest = '\0';
2138   }
2139   return lpszDest;
2140 }
2141
2142 /*************************************************************************
2143  * StrCmpLogicalW       [SHLWAPI.@]
2144  *
2145  * Compare two strings, ignoring case and comparing digits as numbers.
2146  *
2147  * PARAMS
2148  *  lpszStr  [I] First string to compare
2149  *  lpszComp [I] Second string to compare
2150  *  iLen     [I] Length to compare
2151  *
2152  * RETURNS
2153  *  TRUE  If the strings are equal.
2154  *  FALSE Otherwise.
2155  */
2156 INT WINAPI StrCmpLogicalW(LPCWSTR lpszStr, LPCWSTR lpszComp)
2157 {
2158   INT iDiff;
2159
2160   TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszComp));
2161
2162   if (lpszStr && lpszComp)
2163   {
2164     while (*lpszStr)
2165     {
2166       if (!*lpszComp)
2167         return 1;
2168       else if (isdigitW(*lpszStr))
2169       {
2170         int iStr, iComp;
2171
2172         if (!isdigitW(*lpszComp))
2173           return -1;
2174
2175         /* Compare the numbers */
2176         StrToIntExW(lpszStr, 0, &iStr);
2177         StrToIntExW(lpszComp, 0, &iComp);
2178
2179         if (iStr < iComp)
2180           return -1;
2181         else if (iStr > iComp)
2182           return 1;
2183
2184         /* Skip */
2185         while (isdigitW(*lpszStr))
2186           lpszStr++;
2187         while (isdigitW(*lpszComp))
2188           lpszComp++;
2189       }
2190       else if (isdigitW(*lpszComp))
2191         return 1;
2192       else
2193       {
2194         iDiff = ChrCmpIW(*lpszStr,*lpszComp);
2195         if (iDiff > 0)
2196           return 1;
2197         else if (iDiff < 0)
2198           return -1;
2199
2200         lpszStr++;
2201         lpszComp++;
2202       }
2203     }
2204     if (*lpszComp)
2205       return -1;
2206   }
2207   return 0;
2208 }
2209
2210 /* Structure for formatting byte strings */
2211 typedef struct tagSHLWAPI_BYTEFORMATS
2212 {
2213   LONGLONG dLimit;
2214   double   dDivisor;
2215   double   dNormaliser;
2216   int      nDecimals;
2217   WCHAR     wPrefix;
2218 } SHLWAPI_BYTEFORMATS;
2219
2220 /*************************************************************************
2221  * StrFormatByteSizeW   [SHLWAPI.@]
2222  *
2223  * Create a string containing an abbreviated byte count of up to 2^63-1.
2224  *
2225  * PARAMS
2226  *  llBytes  [I] Byte size to format
2227  *  lpszDest [I] Destination for formatted string
2228  *  cchMax   [I] Size of lpszDest
2229  *
2230  * RETURNS
2231  *  lpszDest.
2232  *
2233  * NOTES
2234  *  There is no StrFormatByteSize64W function, it is called StrFormatByteSizeW().
2235  */
2236 LPWSTR WINAPI StrFormatByteSizeW(LONGLONG llBytes, LPWSTR lpszDest, UINT cchMax)
2237 {
2238 #define KB ((ULONGLONG)1024)
2239 #define MB (KB*KB)
2240 #define GB (KB*KB*KB)
2241 #define TB (KB*KB*KB*KB)
2242 #define PB (KB*KB*KB*KB*KB)
2243
2244   static const SHLWAPI_BYTEFORMATS bfFormats[] =
2245   {
2246     { 10*KB, 10.24, 100.0, 2, 'K' }, /* 10 KB */
2247     { 100*KB, 102.4, 10.0, 1, 'K' }, /* 100 KB */
2248     { 1000*KB, 1024.0, 1.0, 0, 'K' }, /* 1000 KB */
2249     { 10*MB, 10485.76, 100.0, 2, 'M' }, /* 10 MB */
2250     { 100*MB, 104857.6, 10.0, 1, 'M' }, /* 100 MB */
2251     { 1000*MB, 1048576.0, 1.0, 0, 'M' }, /* 1000 MB */
2252     { 10*GB, 10737418.24, 100.0, 2, 'G' }, /* 10 GB */
2253     { 100*GB, 107374182.4, 10.0, 1, 'G' }, /* 100 GB */
2254     { 1000*GB, 1073741824.0, 1.0, 0, 'G' }, /* 1000 GB */
2255     { 10*TB, 10485.76, 100.0, 2, 'T' }, /* 10 TB */
2256     { 100*TB, 104857.6, 10.0, 1, 'T' }, /* 100 TB */
2257     { 1000*TB, 1048576.0, 1.0, 0, 'T' }, /* 1000 TB */
2258     { 10*PB, 10737418.24, 100.00, 2, 'P' }, /* 10 PB */
2259     { 100*PB, 107374182.4, 10.00, 1, 'P' }, /* 100 PB */
2260     { 1000*PB, 1073741824.0, 1.00, 0, 'P' }, /* 1000 PB */
2261     { 0, 10995116277.76, 100.00, 2, 'E' } /* EB's, catch all */
2262   };
2263   WCHAR wszAdd[] = {' ','?','B',0};
2264   double dBytes;
2265   UINT i = 0;
2266
2267   TRACE("(0x%s,%p,%d)\n", wine_dbgstr_longlong(llBytes), lpszDest, cchMax);
2268
2269   if (!lpszDest || !cchMax)
2270     return lpszDest;
2271
2272   if (llBytes < 1024)  /* 1K */
2273   {
2274     WCHAR wszBytesFormat[64];
2275     LoadStringW(shlwapi_hInstance, IDS_BYTES_FORMAT, wszBytesFormat, 64);
2276     snprintfW(lpszDest, cchMax, wszBytesFormat, (long)llBytes);
2277     return lpszDest;
2278   }
2279
2280   /* Note that if this loop completes without finding a match, i will be
2281    * pointing at the last entry, which is a catch all for > 1000 PB
2282    */
2283   while (i < sizeof(bfFormats) / sizeof(SHLWAPI_BYTEFORMATS) - 1)
2284   {
2285     if (llBytes < bfFormats[i].dLimit)
2286       break;
2287     i++;
2288   }
2289   /* Above 1 TB we encounter problems with FP accuracy. So for amounts above
2290    * this number we integer shift down by 1 MB first. The table above has
2291    * the divisors scaled down from the '< 10 TB' entry onwards, to account
2292    * for this. We also add a small fudge factor to get the correct result for
2293    * counts that lie exactly on a 1024 byte boundary.
2294    */
2295   if (i > 8)
2296     dBytes = (double)(llBytes >> 20) + 0.001; /* Scale down by I MB */
2297   else
2298     dBytes = (double)llBytes + 0.00001;
2299
2300   dBytes = floor(dBytes / bfFormats[i].dDivisor) / bfFormats[i].dNormaliser;
2301
2302   if (!FormatDouble(dBytes, bfFormats[i].nDecimals, lpszDest, cchMax))
2303     return NULL;
2304   wszAdd[1] = bfFormats[i].wPrefix;
2305   StrCatBuffW(lpszDest, wszAdd, cchMax);
2306   return lpszDest;
2307 }
2308
2309 /*************************************************************************
2310  * StrFormatByteSize64A [SHLWAPI.@]
2311  *
2312  * See StrFormatByteSizeW.
2313  */
2314 LPSTR WINAPI StrFormatByteSize64A(LONGLONG llBytes, LPSTR lpszDest, UINT cchMax)
2315 {
2316   WCHAR wszBuff[32];
2317
2318   StrFormatByteSizeW(llBytes, wszBuff, sizeof(wszBuff)/sizeof(WCHAR));
2319
2320   if (lpszDest)
2321     WideCharToMultiByte(CP_ACP, 0, wszBuff, -1, lpszDest, cchMax, 0, 0);
2322   return lpszDest;
2323 }
2324
2325 /*************************************************************************
2326  * StrFormatByteSizeA   [SHLWAPI.@]
2327  *
2328  * Create a string containing an abbreviated byte count of up to 2^31-1.
2329  *
2330  * PARAMS
2331  *  dwBytes  [I] Byte size to format
2332  *  lpszDest [I] Destination for formatted string
2333  *  cchMax   [I] Size of lpszDest
2334  *
2335  * RETURNS
2336  *  lpszDest.
2337  *
2338  * NOTES
2339  *  The Ascii and Unicode versions of this function accept a different
2340  *  integer type for dwBytes. See StrFormatByteSize64A().
2341  */
2342 LPSTR WINAPI StrFormatByteSizeA(DWORD dwBytes, LPSTR lpszDest, UINT cchMax)
2343 {
2344   TRACE("(%d,%p,%d)\n", dwBytes, lpszDest, cchMax);
2345
2346   return StrFormatByteSize64A(dwBytes, lpszDest, cchMax);
2347 }
2348
2349 /*************************************************************************
2350  *      @       [SHLWAPI.162]
2351  *
2352  * Remove a hanging lead byte from the end of a string, if present.
2353  *
2354  * PARAMS
2355  *  lpStr [I] String to check for a hanging lead byte
2356  *  size  [I] Length of lpStr
2357  *
2358  * RETURNS
2359  *  Success: The new length of the string. Any hanging lead bytes are removed.
2360  *  Failure: 0, if any parameters are invalid.
2361  */
2362 DWORD WINAPI SHTruncateString(LPSTR lpStr, DWORD size)
2363 {
2364   if (lpStr && size)
2365   {
2366     LPSTR lastByte = lpStr + size - 1;
2367
2368     while(lpStr < lastByte)
2369       lpStr += IsDBCSLeadByte(*lpStr) ? 2 : 1;
2370
2371     if(lpStr == lastByte && IsDBCSLeadByte(*lpStr))
2372     {
2373       *lpStr = '\0';
2374       size--;
2375     }
2376     return size;
2377   }
2378   return 0;
2379 }
2380
2381 /*************************************************************************
2382  *      @       [SHLWAPI.203]
2383  *
2384  * Remove a single non-trailing ampersand ('&') from a string.
2385  *
2386  * PARAMS
2387  *  lpszStr [I/O] String to remove ampersand from.
2388  *
2389  * RETURNS
2390  *  The character after the first ampersand in lpszStr, or the first character
2391  *  in lpszStr if there is no ampersand in the string.
2392  */
2393 char WINAPI SHStripMneumonicA(LPCSTR lpszStr)
2394 {
2395   LPSTR lpszIter, lpszTmp;
2396   char ch;
2397
2398   TRACE("(%s)\n", debugstr_a(lpszStr));
2399
2400   ch = *lpszStr;
2401
2402   if ((lpszIter = StrChrA(lpszStr, '&')))
2403   {
2404     lpszTmp = CharNextA(lpszIter);
2405     if (lpszTmp && *lpszTmp)
2406     {
2407       if (*lpszTmp != '&')
2408         ch =  *lpszTmp;
2409
2410       while (lpszIter && *lpszIter)
2411       {
2412         lpszTmp = CharNextA(lpszIter);
2413         *lpszIter = *lpszTmp;
2414         lpszIter = lpszTmp;
2415       }
2416     }
2417   }
2418
2419   return ch;
2420 }
2421
2422 /*************************************************************************
2423  *      @       [SHLWAPI.225]
2424  *
2425  * Unicode version of SHStripMneumonicA.
2426  */
2427 WCHAR WINAPI SHStripMneumonicW(LPCWSTR lpszStr)
2428 {
2429   LPWSTR lpszIter, lpszTmp;
2430   WCHAR ch;
2431
2432   TRACE("(%s)\n", debugstr_w(lpszStr));
2433
2434   ch = *lpszStr;
2435
2436   if ((lpszIter = StrChrW(lpszStr, '&')))
2437   {
2438     lpszTmp = lpszIter + 1;
2439     if (lpszTmp && *lpszTmp)
2440     {
2441       if (*lpszTmp != '&')
2442         ch =  *lpszTmp;
2443
2444       while (lpszIter && *lpszIter)
2445       {
2446         lpszTmp = lpszIter + 1;
2447         *lpszIter = *lpszTmp;
2448         lpszIter = lpszTmp;
2449       }
2450     }
2451   }
2452
2453   return ch;
2454 }
2455
2456 /*************************************************************************
2457  *      @       [SHLWAPI.216]
2458  *
2459  * Convert an Ascii string to Unicode.
2460  *
2461  * PARAMS
2462  * dwCp     [I] Code page for the conversion
2463  * lpSrcStr [I] Source Ascii string to convert
2464  * lpDstStr [O] Destination for converted Unicode string
2465  * iLen     [I] Length of lpDstStr
2466  *
2467  * RETURNS
2468  *  The return value of the MultiByteToWideChar() function called on lpSrcStr.
2469  */
2470 DWORD WINAPI SHAnsiToUnicodeCP(DWORD dwCp, LPCSTR lpSrcStr, LPWSTR lpDstStr, int iLen)
2471 {
2472   DWORD dwRet;
2473
2474   dwRet = MultiByteToWideChar(dwCp, 0, lpSrcStr, -1, lpDstStr, iLen);
2475   TRACE("%s->%s,ret=%d\n", debugstr_a(lpSrcStr), debugstr_w(lpDstStr), dwRet);
2476   return dwRet;
2477 }
2478
2479 /*************************************************************************
2480  *      @       [SHLWAPI.215]
2481  *
2482  * Convert an Ascii string to Unicode.
2483  *
2484  * PARAMS
2485  * lpSrcStr [I] Source Ascii string to convert
2486  * lpDstStr [O] Destination for converted Unicode string
2487  * iLen     [I] Length of lpDstStr
2488  *
2489  * RETURNS
2490  *  The return value of the MultiByteToWideChar() function called on lpSrcStr.
2491  *
2492  * NOTES
2493  *  This function simply calls SHAnsiToUnicodeCP with code page CP_ACP.
2494  */
2495 DWORD WINAPI SHAnsiToUnicode(LPCSTR lpSrcStr, LPWSTR lpDstStr, int iLen)
2496 {
2497   return SHAnsiToUnicodeCP(CP_ACP, lpSrcStr, lpDstStr, iLen);
2498 }
2499
2500 /*************************************************************************
2501  *      @       [SHLWAPI.218]
2502  *
2503  * Convert a Unicode string to Ascii.
2504  *
2505  * PARAMS
2506  *  CodePage [I] Code page to use for the conversion
2507  *  lpSrcStr [I] Source Unicode string to convert
2508  *  lpDstStr [O] Destination for converted Ascii string
2509  *  dstlen   [I] Length of buffer at lpDstStr
2510  *
2511  * RETURNS
2512  *  Success: The length in bytes of the result at lpDstStr (including the terminator)
2513  *  Failure: When using CP_UTF8, CP_UTF7 or 0xc350 as codePage, 0 is returned and
2514  *           the result is not nul-terminated.
2515  *           When using a different codepage, the length in bytes of the truncated
2516  *           result at lpDstStr (including the terminator) is returned and
2517  *           lpDstStr is always nul-terminated.
2518  *
2519  */
2520 DWORD WINAPI SHUnicodeToAnsiCP(UINT CodePage, LPCWSTR lpSrcStr, LPSTR lpDstStr, int dstlen)
2521 {
2522   static const WCHAR emptyW[] = { '\0' };
2523   int len , reqLen;
2524   LPSTR mem;
2525
2526   if (!lpDstStr || !dstlen)
2527     return 0;
2528
2529   if (!lpSrcStr)
2530     lpSrcStr = emptyW;
2531
2532   *lpDstStr = '\0';
2533
2534   len = strlenW(lpSrcStr) + 1;
2535
2536   switch (CodePage)
2537   {
2538   case CP_WINUNICODE:
2539     CodePage = CP_UTF8; /* Fall through... */
2540   case 0x0000C350: /* FIXME: CP_ #define */
2541   case CP_UTF7:
2542   case CP_UTF8:
2543     {
2544       DWORD dwMode = 0;
2545       INT lenW = len - 1;
2546       INT needed = dstlen - 1;
2547       HRESULT hr;
2548
2549       /* try the user supplied buffer first */
2550       hr = ConvertINetUnicodeToMultiByte(&dwMode, CodePage, lpSrcStr, &lenW, lpDstStr, &needed);
2551       if (hr == S_OK)
2552       {
2553         lpDstStr[needed] = '\0';
2554         return needed + 1;
2555       }
2556
2557       /* user buffer too small. exclude termination and copy as much as possible */
2558       lenW = len;
2559       hr = ConvertINetUnicodeToMultiByte(&dwMode, CodePage, lpSrcStr, &lenW, NULL, &needed);
2560       needed++;
2561       mem = HeapAlloc(GetProcessHeap(), 0, needed);
2562       if (!mem)
2563         return 0;
2564
2565       hr = ConvertINetUnicodeToMultiByte(&dwMode, CodePage, lpSrcStr, &len, mem, &needed);
2566       if (hr == S_OK)
2567       {
2568           reqLen = SHTruncateString(mem, dstlen);
2569           if (reqLen > 0) memcpy(lpDstStr, mem, reqLen-1);
2570       }
2571       HeapFree(GetProcessHeap(), 0, mem);
2572       return 0;
2573     }
2574   default:
2575     break;
2576   }
2577
2578   /* try the user supplied buffer first */
2579   reqLen = WideCharToMultiByte(CodePage, 0, lpSrcStr, len, lpDstStr, dstlen, NULL, NULL);
2580
2581   if (!reqLen && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
2582   {
2583     reqLen = WideCharToMultiByte(CodePage, 0, lpSrcStr, len, NULL, 0, NULL, NULL);
2584     if (reqLen)
2585     {
2586       mem = HeapAlloc(GetProcessHeap(), 0, reqLen);
2587       if (mem)
2588       {
2589         reqLen = WideCharToMultiByte(CodePage, 0, lpSrcStr, len, mem,
2590                                      reqLen, NULL, NULL);
2591
2592         reqLen = SHTruncateString(mem, dstlen -1);
2593         reqLen++;
2594
2595         lstrcpynA(lpDstStr, mem, reqLen);
2596         HeapFree(GetProcessHeap(), 0, mem);
2597         lpDstStr[reqLen-1] = '\0';
2598       }
2599     }
2600   }
2601   return reqLen;
2602 }
2603
2604 /*************************************************************************
2605  *      @       [SHLWAPI.217]
2606  *
2607  * Convert a Unicode string to Ascii.
2608  *
2609  * PARAMS
2610  *  lpSrcStr [I] Source Unicode string to convert
2611  *  lpDstStr [O] Destination for converted Ascii string
2612  *  iLen     [O] Length of lpDstStr in characters
2613  *
2614  * RETURNS
2615  *  See SHUnicodeToAnsiCP
2616
2617  * NOTES
2618  *  This function simply calls SHUnicodeToAnsiCP() with CodePage = CP_ACP.
2619  */
2620 INT WINAPI SHUnicodeToAnsi(LPCWSTR lpSrcStr, LPSTR lpDstStr, INT iLen)
2621 {
2622     return SHUnicodeToAnsiCP(CP_ACP, lpSrcStr, lpDstStr, iLen);
2623 }
2624
2625 /*************************************************************************
2626  *      @       [SHLWAPI.345]
2627  *
2628  * Copy one string to another.
2629  *
2630  * PARAMS
2631  *  lpszSrc [I] Source string to copy
2632  *  lpszDst [O] Destination for copy
2633  *  iLen    [I] Length of lpszDst in characters
2634  *
2635  * RETURNS
2636  *  The length of the copied string, including the terminating NUL. lpszDst
2637  *  contains iLen characters of lpszSrc.
2638  */
2639 DWORD WINAPI SHAnsiToAnsi(LPCSTR lpszSrc, LPSTR lpszDst, int iLen)
2640 {
2641     LPSTR lpszRet;
2642
2643     TRACE("(%s,%p,0x%08x)\n", debugstr_a(lpszSrc), lpszDst, iLen);
2644
2645     lpszRet = StrCpyNXA(lpszDst, lpszSrc, iLen);
2646     return lpszRet - lpszDst + 1;
2647 }
2648
2649 /*************************************************************************
2650  *      @       [SHLWAPI.346]
2651  *
2652  * Unicode version of SSHAnsiToAnsi.
2653  */
2654 DWORD WINAPI SHUnicodeToUnicode(LPCWSTR lpszSrc, LPWSTR lpszDst, int iLen)
2655 {
2656     LPWSTR lpszRet;
2657
2658     TRACE("(%s,%p,0x%08x)\n", debugstr_w(lpszSrc), lpszDst, iLen);
2659
2660     lpszRet = StrCpyNXW(lpszDst, lpszSrc, iLen);
2661     return lpszRet - lpszDst + 1;
2662 }
2663
2664 /*************************************************************************
2665  *      @       [SHLWAPI.364]
2666  *
2667  * Determine if an Ascii string converts to Unicode and back identically.
2668  *
2669  * PARAMS
2670  *  lpSrcStr [I] Source Unicode string to convert
2671  *  lpDst    [O] Destination for resulting Ascii string
2672  *  iLen     [I] Length of lpDst in characters
2673  *
2674  * RETURNS
2675  *  TRUE, since Ascii strings always convert identically.
2676  */
2677 BOOL WINAPI DoesStringRoundTripA(LPCSTR lpSrcStr, LPSTR lpDst, INT iLen)
2678 {
2679   lstrcpynA(lpDst, lpSrcStr, iLen);
2680   return TRUE;
2681 }
2682
2683 /*************************************************************************
2684  *      @       [SHLWAPI.365]
2685  *
2686  * Determine if a Unicode string converts to Ascii and back identically.
2687  *
2688  * PARAMS
2689  *  lpSrcStr [I] Source Unicode string to convert
2690  *  lpDst    [O] Destination for resulting Ascii string
2691  *  iLen     [I] Length of lpDst in characters
2692  *
2693  * RETURNS
2694  *  TRUE, if lpSrcStr converts to Ascii and back identically,
2695  *  FALSE otherwise.
2696  */
2697 BOOL WINAPI DoesStringRoundTripW(LPCWSTR lpSrcStr, LPSTR lpDst, INT iLen)
2698 {
2699     WCHAR szBuff[MAX_PATH];
2700
2701     SHUnicodeToAnsi(lpSrcStr, lpDst, iLen);
2702     SHAnsiToUnicode(lpDst, szBuff, MAX_PATH);
2703     return !strcmpW(lpSrcStr, szBuff);
2704 }
2705
2706 /*************************************************************************
2707  *      SHLoadIndirectString    [SHLWAPI.@]
2708  *
2709  * If passed a string that begins with '@', extract the string from the
2710  * appropriate resource, otherwise do a straight copy.
2711  *
2712  */
2713 HRESULT WINAPI SHLoadIndirectString(LPCWSTR src, LPWSTR dst, UINT dst_len, void **reserved)
2714 {
2715     WCHAR *dllname = NULL;
2716     HMODULE hmod = NULL;
2717     HRESULT hr = E_FAIL;
2718
2719     TRACE("(%s %p %08x %p)\n", debugstr_w(src), dst, dst_len, reserved);
2720
2721     if(src[0] == '@')
2722     {
2723         WCHAR *index_str;
2724         int index;
2725
2726         dst[0] = 0;
2727         dllname = StrDupW(src + 1);
2728         index_str = strchrW(dllname, ',');
2729
2730         if(!index_str) goto end;
2731
2732         *index_str = 0;
2733         index_str++;
2734         index = atoiW(index_str);
2735   
2736         hmod = LoadLibraryW(dllname);
2737         if(!hmod) goto end;
2738
2739         if(index < 0)
2740         {
2741             if(LoadStringW(hmod, -index, dst, dst_len))
2742                 hr = S_OK;
2743         }
2744         else
2745             FIXME("can't handle non-negative indices (%d)\n", index);
2746     }
2747     else
2748     {
2749         if(dst != src)
2750             lstrcpynW(dst, src, dst_len);
2751         hr = S_OK;
2752     }
2753
2754     TRACE("returning %s\n", debugstr_w(dst));
2755 end:
2756     if(hmod) FreeLibrary(hmod);
2757     HeapFree(GetProcessHeap(), 0, dllname);
2758     return hr;
2759 }