shlwapi: Implement SHPropertyBag_ReadLONG.
[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  *  lpszStr  [O] Destination string
511  *  lpszSrc  [I] Source string
512  *  iLen     [I] Maximum number of chars to copy
513  *
514  * RETURNS
515  *  lpszStr.
516  */
517 LPWSTR WINAPI StrCpyNW(LPWSTR lpszStr, LPCWSTR lpszSrc, int iLen)
518 {
519   TRACE("(%p,%s,%i)\n", lpszStr, debugstr_w(lpszSrc), iLen);
520
521   lstrcpynW(lpszStr, lpszSrc, iLen);
522   return lpszStr;
523 }
524
525
526
527 /*************************************************************************
528  * SHLWAPI_StrStrHelperA
529  *
530  * Internal implementation of StrStrA/StrStrIA
531  */
532 static LPSTR SHLWAPI_StrStrHelperA(LPCSTR lpszStr, LPCSTR lpszSearch,
533                                    INT (WINAPI *pStrCmpFn)(LPCSTR,LPCSTR,INT))
534 {
535   size_t iLen;
536
537   if (!lpszStr || !lpszSearch || !*lpszSearch)
538     return NULL;
539
540   iLen = strlen(lpszSearch);
541
542   while (*lpszStr)
543   {
544     if (!pStrCmpFn(lpszStr, lpszSearch, iLen))
545       return (LPSTR)lpszStr;
546     lpszStr = CharNextA(lpszStr);
547   }
548   return NULL;
549 }
550
551 /*************************************************************************
552  * StrStrA      [SHLWAPI.@]
553  *
554  * Find a substring within a string.
555  *
556  * PARAMS
557  *  lpszStr    [I] String to search in
558  *  lpszSearch [I] String to look for
559  *
560  * RETURNS
561  *  The start of lpszSearch within lpszStr, or NULL if not found.
562  */
563 LPSTR WINAPI StrStrA(LPCSTR lpszStr, LPCSTR lpszSearch)
564 {
565   TRACE("(%s,%s)\n", debugstr_a(lpszStr), debugstr_a(lpszSearch));
566
567   return SHLWAPI_StrStrHelperA(lpszStr, lpszSearch, StrCmpNA);
568 }
569
570 /*************************************************************************
571  * StrStrW      [SHLWAPI.@]
572  *
573  * See StrStrA.
574  */
575 LPWSTR WINAPI StrStrW(LPCWSTR lpszStr, LPCWSTR lpszSearch)
576 {
577     if (!lpszStr || !lpszSearch) return NULL;
578     return strstrW( lpszStr, lpszSearch );
579 }
580
581 /*************************************************************************
582  * StrRStrIA    [SHLWAPI.@]
583  *
584  * Find the last occurrence of a substring within a string.
585  *
586  * PARAMS
587  *  lpszStr    [I] String to search in
588  *  lpszEnd    [I] End of lpszStr
589  *  lpszSearch [I] String to look for
590  *
591  * RETURNS
592  *  The last occurrence lpszSearch within lpszStr, or NULL if not found.
593  */
594 LPSTR WINAPI StrRStrIA(LPCSTR lpszStr, LPCSTR lpszEnd, LPCSTR lpszSearch)
595 {
596   WORD ch1, ch2;
597   INT iLen;
598
599   TRACE("(%s,%s)\n", debugstr_a(lpszStr), debugstr_a(lpszSearch));
600
601   if (!lpszStr || !lpszSearch || !*lpszSearch)
602     return NULL;
603
604   if (!lpszEnd)
605     lpszEnd = lpszStr + lstrlenA(lpszStr);
606   if (lpszEnd == lpszStr)
607     return NULL;
608
609   if (IsDBCSLeadByte(*lpszSearch))
610     ch1 = *lpszSearch << 8 | (UCHAR)lpszSearch[1];
611   else
612     ch1 = *lpszSearch;
613   iLen = lstrlenA(lpszSearch);
614
615   do
616   {
617     lpszEnd = CharPrevA(lpszStr, lpszEnd);
618     ch2 = IsDBCSLeadByte(*lpszEnd)? *lpszEnd << 8 | (UCHAR)lpszEnd[1] : *lpszEnd;
619     if (!ChrCmpIA(ch1, ch2))
620     {
621       if (!StrCmpNIA(lpszEnd, lpszSearch, iLen))
622         return (LPSTR)lpszEnd;
623     }
624   } while (lpszEnd > lpszStr);
625   return NULL;
626 }
627
628 /*************************************************************************
629  * StrRStrIW    [SHLWAPI.@]
630  *
631  * See StrRStrIA.
632  */
633 LPWSTR WINAPI StrRStrIW(LPCWSTR lpszStr, LPCWSTR lpszEnd, LPCWSTR lpszSearch)
634 {
635   INT iLen;
636
637   TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszSearch));
638
639   if (!lpszStr || !lpszSearch || !*lpszSearch)
640     return NULL;
641
642   if (!lpszEnd)
643     lpszEnd = lpszStr + strlenW(lpszStr);
644
645   iLen = strlenW(lpszSearch);
646
647   while (lpszEnd > lpszStr)
648   {
649     lpszEnd--;
650     if (!StrCmpNIW(lpszEnd, lpszSearch, iLen))
651       return (LPWSTR)lpszEnd;
652   }
653   return NULL;
654 }
655
656 /*************************************************************************
657  * StrStrIA     [SHLWAPI.@]
658  *
659  * Find a substring within a string, ignoring case.
660  *
661  * PARAMS
662  *  lpszStr    [I] String to search in
663  *  lpszSearch [I] String to look for
664  *
665  * RETURNS
666  *  The start of lpszSearch within lpszStr, or NULL if not found.
667  */
668 LPSTR WINAPI StrStrIA(LPCSTR lpszStr, LPCSTR lpszSearch)
669 {
670   TRACE("(%s,%s)\n", debugstr_a(lpszStr), debugstr_a(lpszSearch));
671
672   return SHLWAPI_StrStrHelperA(lpszStr, lpszSearch, StrCmpNIA);
673 }
674
675 /*************************************************************************
676  * StrStrIW     [SHLWAPI.@]
677  *
678  * See StrStrIA.
679  */
680 LPWSTR WINAPI StrStrIW(LPCWSTR lpszStr, LPCWSTR lpszSearch)
681 {
682   int iLen;
683
684   TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszSearch));
685
686   if (!lpszStr || !lpszSearch || !*lpszSearch)
687     return NULL;
688
689   iLen = strlenW(lpszSearch);
690
691   while (*lpszStr)
692   {
693     if (!StrCmpNIW(lpszStr, lpszSearch, iLen))
694       return (LPWSTR)lpszStr;
695     lpszStr++;
696   }
697   return NULL;
698 }
699
700 /*************************************************************************
701  * StrToIntA    [SHLWAPI.@]
702  *
703  * Read a signed integer from a string.
704  *
705  * PARAMS
706  *  lpszStr [I] String to read integer from
707  *
708  * RETURNS
709  *   The signed integer value represented by the string, or 0 if no integer is
710  *   present.
711  *
712  * NOTES
713  *  No leading space is allowed before the number, although a leading '-' is.
714  */
715 int WINAPI StrToIntA(LPCSTR lpszStr)
716 {
717   int iRet = 0;
718
719   TRACE("(%s)\n", debugstr_a(lpszStr));
720
721   if (!lpszStr)
722   {
723     WARN("Invalid lpszStr would crash under Win32!\n");
724     return 0;
725   }
726
727   if (*lpszStr == '-' || isdigit(*lpszStr))
728     StrToIntExA(lpszStr, 0, &iRet);
729   return iRet;
730 }
731
732 /*************************************************************************
733  * StrToIntW    [SHLWAPI.@]
734  *
735  * See StrToIntA.
736  */
737 int WINAPI StrToIntW(LPCWSTR lpszStr)
738 {
739   int iRet = 0;
740
741   TRACE("(%s)\n", debugstr_w(lpszStr));
742
743   if (!lpszStr)
744   {
745     WARN("Invalid lpszStr would crash under Win32!\n");
746     return 0;
747   }
748
749   if (*lpszStr == '-' || isdigitW(*lpszStr))
750     StrToIntExW(lpszStr, 0, &iRet);
751   return iRet;
752 }
753
754 /*************************************************************************
755  * StrToIntExA  [SHLWAPI.@]
756  *
757  * Read an integer from a string.
758  *
759  * PARAMS
760  *  lpszStr [I] String to read integer from
761  *  dwFlags [I] Flags controlling the conversion
762  *  lpiRet  [O] Destination for read integer.
763  *
764  * RETURNS
765  *  Success: TRUE. lpiRet contains the integer value represented by the string.
766  *  Failure: FALSE, if the string is invalid, or no number is present.
767  *
768  * NOTES
769  *  Leading whitespace, '-' and '+' are allowed before the number. If
770  *  dwFlags includes STIF_SUPPORT_HEX, hexadecimal numbers are allowed, if
771  *  preceded by '0x'. If this flag is not set, or there is no '0x' prefix,
772  *  the string is treated as a decimal string. A leading '-' is ignored for
773  *  hexadecimal numbers.
774  */
775 BOOL WINAPI StrToIntExA(LPCSTR lpszStr, DWORD dwFlags, LPINT lpiRet)
776 {
777   BOOL bNegative = FALSE;
778   int iRet = 0;
779
780   TRACE("(%s,%08X,%p)\n", debugstr_a(lpszStr), dwFlags, lpiRet);
781
782   if (!lpszStr || !lpiRet)
783   {
784     WARN("Invalid parameter would crash under Win32!\n");
785     return FALSE;
786   }
787   if (dwFlags > STIF_SUPPORT_HEX)
788   {
789     WARN("Unknown flags (%08lX)!\n", dwFlags & ~STIF_SUPPORT_HEX);
790   }
791
792   /* Skip leading space, '+', '-' */
793   while (isspace(*lpszStr))
794     lpszStr = CharNextA(lpszStr);
795
796   if (*lpszStr == '-')
797   {
798     bNegative = TRUE;
799     lpszStr++;
800   }
801   else if (*lpszStr == '+')
802     lpszStr++;
803
804   if (dwFlags & STIF_SUPPORT_HEX &&
805       *lpszStr == '0' && tolower(lpszStr[1]) == 'x')
806   {
807     /* Read hex number */
808     lpszStr += 2;
809
810     if (!isxdigit(*lpszStr))
811       return FALSE;
812
813     while (isxdigit(*lpszStr))
814     {
815       iRet = iRet * 16;
816       if (isdigit(*lpszStr))
817         iRet += (*lpszStr - '0');
818       else
819         iRet += 10 + (tolower(*lpszStr) - 'a');
820       lpszStr++;
821     }
822     *lpiRet = iRet;
823     return TRUE;
824   }
825
826   /* Read decimal number */
827   if (!isdigit(*lpszStr))
828     return FALSE;
829
830   while (isdigit(*lpszStr))
831   {
832     iRet = iRet * 10;
833     iRet += (*lpszStr - '0');
834     lpszStr++;
835   }
836   *lpiRet = bNegative ? -iRet : iRet;
837   return TRUE;
838 }
839
840 /*************************************************************************
841  * StrToIntExW  [SHLWAPI.@]
842  *
843  * See StrToIntExA.
844  */
845 BOOL WINAPI StrToIntExW(LPCWSTR lpszStr, DWORD dwFlags, LPINT lpiRet)
846 {
847   BOOL bNegative = FALSE;
848   int iRet = 0;
849
850   TRACE("(%s,%08X,%p)\n", debugstr_w(lpszStr), dwFlags, lpiRet);
851
852   if (!lpszStr || !lpiRet)
853   {
854     WARN("Invalid parameter would crash under Win32!\n");
855     return FALSE;
856   }
857   if (dwFlags > STIF_SUPPORT_HEX)
858   {
859     WARN("Unknown flags (%08lX)!\n", dwFlags & ~STIF_SUPPORT_HEX);
860   }
861
862   /* Skip leading space, '+', '-' */
863   while (isspaceW(*lpszStr)) lpszStr++;
864
865   if (*lpszStr == '-')
866   {
867     bNegative = TRUE;
868     lpszStr++;
869   }
870   else if (*lpszStr == '+')
871     lpszStr++;
872
873   if (dwFlags & STIF_SUPPORT_HEX &&
874       *lpszStr == '0' && tolowerW(lpszStr[1]) == 'x')
875   {
876     /* Read hex number */
877     lpszStr += 2;
878
879     if (!isxdigitW(*lpszStr))
880       return FALSE;
881
882     while (isxdigitW(*lpszStr))
883     {
884       iRet = iRet * 16;
885       if (isdigitW(*lpszStr))
886         iRet += (*lpszStr - '0');
887       else
888         iRet += 10 + (tolowerW(*lpszStr) - 'a');
889       lpszStr++;
890     }
891     *lpiRet = iRet;
892     return TRUE;
893   }
894
895   /* Read decimal number */
896   if (!isdigitW(*lpszStr))
897     return FALSE;
898
899   while (isdigitW(*lpszStr))
900   {
901     iRet = iRet * 10;
902     iRet += (*lpszStr - '0');
903     lpszStr++;
904   }
905   *lpiRet = bNegative ? -iRet : iRet;
906   return TRUE;
907 }
908
909 /*************************************************************************
910  * StrDupA      [SHLWAPI.@]
911  *
912  * Duplicate a string.
913  *
914  * PARAMS
915  *  lpszStr [I] String to duplicate.
916  *
917  * RETURNS
918  *  Success: A pointer to a new string containing the contents of lpszStr
919  *  Failure: NULL, if memory cannot be allocated
920  *
921  * NOTES
922  *  The string memory is allocated with LocalAlloc(), and so should be released
923  *  by calling LocalFree().
924  */
925 LPSTR WINAPI StrDupA(LPCSTR lpszStr)
926 {
927   int iLen;
928   LPSTR lpszRet;
929
930   TRACE("(%s)\n",debugstr_a(lpszStr));
931
932   iLen = lpszStr ? strlen(lpszStr) + 1 : 1;
933   lpszRet = LocalAlloc(LMEM_FIXED, iLen);
934
935   if (lpszRet)
936   {
937     if (lpszStr)
938       memcpy(lpszRet, lpszStr, iLen);
939     else
940       *lpszRet = '\0';
941   }
942   return lpszRet;
943 }
944
945 /*************************************************************************
946  * StrDupW      [SHLWAPI.@]
947  *
948  * See StrDupA.
949  */
950 LPWSTR WINAPI StrDupW(LPCWSTR lpszStr)
951 {
952   int iLen;
953   LPWSTR lpszRet;
954
955   TRACE("(%s)\n",debugstr_w(lpszStr));
956
957   iLen = (lpszStr ? strlenW(lpszStr) + 1 : 1) * sizeof(WCHAR);
958   lpszRet = LocalAlloc(LMEM_FIXED, iLen);
959
960   if (lpszRet)
961   {
962     if (lpszStr)
963       memcpy(lpszRet, lpszStr, iLen);
964     else
965       *lpszRet = '\0';
966   }
967   return lpszRet;
968 }
969
970 /*************************************************************************
971  * SHLWAPI_StrSpnHelperA
972  *
973  * Internal implementation of StrSpnA/StrCSpnA/StrCSpnIA
974  */
975 static int SHLWAPI_StrSpnHelperA(LPCSTR lpszStr, LPCSTR lpszMatch,
976                                  LPSTR (WINAPI *pStrChrFn)(LPCSTR,WORD),
977                                  BOOL bInvert)
978 {
979   LPCSTR lpszRead = lpszStr;
980   if (lpszStr && *lpszStr && lpszMatch)
981   {
982     while (*lpszRead)
983     {
984       LPCSTR lpszTest = pStrChrFn(lpszMatch, *lpszRead);
985
986       if (!bInvert && !lpszTest)
987         break;
988       if (bInvert && lpszTest)
989         break;
990       lpszRead = CharNextA(lpszRead);
991     };
992   }
993   return lpszRead - lpszStr;
994 }
995
996 /*************************************************************************
997  * StrSpnA      [SHLWAPI.@]
998  *
999  * Find the length of the start of a string that contains only certain
1000  * characters.
1001  *
1002  * PARAMS
1003  *  lpszStr   [I] String to search
1004  *  lpszMatch [I] Characters that can be in the substring
1005  *
1006  * RETURNS
1007  *  The length of the part of lpszStr containing only chars from lpszMatch,
1008  *  or 0 if any parameter is invalid.
1009  */
1010 int WINAPI StrSpnA(LPCSTR lpszStr, LPCSTR lpszMatch)
1011 {
1012   TRACE("(%s,%s)\n",debugstr_a(lpszStr), debugstr_a(lpszMatch));
1013
1014   return SHLWAPI_StrSpnHelperA(lpszStr, lpszMatch, StrChrA, FALSE);
1015 }
1016
1017 /*************************************************************************
1018  * StrSpnW      [SHLWAPI.@]
1019  *
1020  * See StrSpnA.
1021  */
1022 int WINAPI StrSpnW(LPCWSTR lpszStr, LPCWSTR lpszMatch)
1023 {
1024     if (!lpszStr || !lpszMatch) return 0;
1025     return strspnW( lpszStr, lpszMatch );
1026 }
1027
1028 /*************************************************************************
1029  * StrCSpnA     [SHLWAPI.@]
1030  *
1031  * Find the length of the start of a string that does not contain certain
1032  * characters.
1033  *
1034  * PARAMS
1035  *  lpszStr   [I] String to search
1036  *  lpszMatch [I] Characters that cannot be in the substring
1037  *
1038  * RETURNS
1039  *  The length of the part of lpszStr containing only chars not in lpszMatch,
1040  *  or 0 if any parameter is invalid.
1041  */
1042 int WINAPI StrCSpnA(LPCSTR lpszStr, LPCSTR lpszMatch)
1043 {
1044   TRACE("(%s,%s)\n",debugstr_a(lpszStr), debugstr_a(lpszMatch));
1045
1046   return SHLWAPI_StrSpnHelperA(lpszStr, lpszMatch, StrChrA, TRUE);
1047 }
1048
1049 /*************************************************************************
1050  * StrCSpnW     [SHLWAPI.@]
1051  *
1052  * See StrCSpnA.
1053  */
1054 int WINAPI StrCSpnW(LPCWSTR lpszStr, LPCWSTR lpszMatch)
1055 {
1056     if (!lpszStr || !lpszMatch) return 0;
1057     return strcspnW( lpszStr, lpszMatch );
1058 }
1059
1060 /*************************************************************************
1061  * StrCSpnIA    [SHLWAPI.@]
1062  *
1063  * Find the length of the start of a string that does not contain certain
1064  * characters, ignoring case.
1065  *
1066  * PARAMS
1067  *  lpszStr   [I] String to search
1068  *  lpszMatch [I] Characters that cannot be in the substring
1069  *
1070  * RETURNS
1071  *  The length of the part of lpszStr containing only chars not in lpszMatch,
1072  *  or 0 if any parameter is invalid.
1073  */
1074 int WINAPI StrCSpnIA(LPCSTR lpszStr, LPCSTR lpszMatch)
1075 {
1076   TRACE("(%s,%s)\n",debugstr_a(lpszStr), debugstr_a(lpszMatch));
1077
1078   return SHLWAPI_StrSpnHelperA(lpszStr, lpszMatch, StrChrIA, TRUE);
1079 }
1080
1081 /*************************************************************************
1082  * StrCSpnIW    [SHLWAPI.@]
1083  *
1084  * See StrCSpnIA.
1085  */
1086 int WINAPI StrCSpnIW(LPCWSTR lpszStr, LPCWSTR lpszMatch)
1087 {
1088   LPCWSTR lpszRead = lpszStr;
1089
1090   TRACE("(%s,%s)\n",debugstr_w(lpszStr), debugstr_w(lpszMatch));
1091
1092   if (lpszStr && *lpszStr && lpszMatch)
1093   {
1094     while (*lpszRead)
1095     {
1096       if (StrChrIW(lpszMatch, *lpszRead)) break;
1097       lpszRead++;
1098     }
1099   }
1100   return lpszRead - lpszStr;
1101 }
1102
1103 /*************************************************************************
1104  * StrPBrkA     [SHLWAPI.@]
1105  *
1106  * Search a string for any of a group of characters.
1107  *
1108  * PARAMS
1109  *  lpszStr   [I] String to search
1110  *  lpszMatch [I] Characters to match
1111  *
1112  * RETURNS
1113  *  A pointer to the first matching character in lpszStr, or NULL if no
1114  *  match was found.
1115  */
1116 LPSTR WINAPI StrPBrkA(LPCSTR lpszStr, LPCSTR lpszMatch)
1117 {
1118   TRACE("(%s,%s)\n",debugstr_a(lpszStr), debugstr_a(lpszMatch));
1119
1120   if (lpszStr && lpszMatch && *lpszMatch)
1121   {
1122     while (*lpszStr)
1123     {
1124       if (StrChrA(lpszMatch, *lpszStr))
1125         return (LPSTR)lpszStr;
1126       lpszStr = CharNextA(lpszStr);
1127     }
1128   }
1129   return NULL;
1130 }
1131
1132 /*************************************************************************
1133  * StrPBrkW     [SHLWAPI.@]
1134  *
1135  * See StrPBrkA.
1136  */
1137 LPWSTR WINAPI StrPBrkW(LPCWSTR lpszStr, LPCWSTR lpszMatch)
1138 {
1139     if (!lpszStr || !lpszMatch) return NULL;
1140     return strpbrkW( lpszStr, lpszMatch );
1141 }
1142
1143 /*************************************************************************
1144  * SHLWAPI_StrRChrHelperA
1145  *
1146  * Internal implementation of StrRChrA/StrRChrIA.
1147  */
1148 static LPSTR SHLWAPI_StrRChrHelperA(LPCSTR lpszStr,
1149                                     LPCSTR lpszEnd, WORD ch,
1150                                     BOOL (WINAPI *pChrCmpFn)(WORD,WORD))
1151 {
1152   LPCSTR lpszRet = NULL;
1153
1154   if (lpszStr)
1155   {
1156     WORD ch2;
1157
1158     if (!lpszEnd)
1159       lpszEnd = lpszStr + lstrlenA(lpszStr);
1160
1161     while (*lpszStr && lpszStr <= lpszEnd)
1162     {
1163       ch2 = IsDBCSLeadByte(*lpszStr)? *lpszStr << 8 | lpszStr[1] : *lpszStr;
1164
1165       if (!pChrCmpFn(ch, ch2))
1166         lpszRet = lpszStr;
1167       lpszStr = CharNextA(lpszStr);
1168     }
1169   }
1170   return (LPSTR)lpszRet;
1171 }
1172
1173 /**************************************************************************
1174  * StrRChrA     [SHLWAPI.@]
1175  *
1176  * Find the last occurrence of a character in string.
1177  *
1178  * PARAMS
1179  *  lpszStr [I] String to search in
1180  *  lpszEnd [I] Place to end search, or NULL to search until the end of lpszStr
1181  *  ch      [I] Character to search for.
1182  *
1183  * RETURNS
1184  *  Success: A pointer to the last occurrence of ch in lpszStr before lpszEnd,
1185  *           or NULL if not found.
1186  *  Failure: NULL, if any arguments are invalid.
1187  */
1188 LPSTR WINAPI StrRChrA(LPCSTR lpszStr, LPCSTR lpszEnd, WORD ch)
1189 {
1190   TRACE("(%s,%s,%x)\n", debugstr_a(lpszStr), debugstr_a(lpszEnd), ch);
1191
1192   return SHLWAPI_StrRChrHelperA(lpszStr, lpszEnd, ch, SHLWAPI_ChrCmpA);
1193 }
1194
1195 /**************************************************************************
1196  * StrRChrW     [SHLWAPI.@]
1197  *
1198  * See StrRChrA.
1199  */
1200 LPWSTR WINAPI StrRChrW(LPCWSTR str, LPCWSTR end, WORD ch)
1201 {
1202     WCHAR *ret = NULL;
1203
1204     if (!str) return NULL;
1205     if (!end) end = str + strlenW(str);
1206     while (str < end)
1207     {
1208         if (*str == ch) ret = (WCHAR *)str;
1209         str++;
1210     }
1211     return ret;
1212 }
1213
1214 /**************************************************************************
1215  * StrRChrIA    [SHLWAPI.@]
1216  *
1217  * Find the last occurrence of a character in string, ignoring case.
1218  *
1219  * PARAMS
1220  *  lpszStr [I] String to search in
1221  *  lpszEnd [I] Place to end search, or NULL to search until the end of lpszStr
1222  *  ch      [I] Character to search for.
1223  *
1224  * RETURNS
1225  *  Success: A pointer to the last occurrence of ch in lpszStr before lpszEnd,
1226  *           or NULL if not found.
1227  *  Failure: NULL, if any arguments are invalid.
1228  */
1229 LPSTR WINAPI StrRChrIA(LPCSTR lpszStr, LPCSTR lpszEnd, WORD ch)
1230 {
1231   TRACE("(%s,%s,%x)\n", debugstr_a(lpszStr), debugstr_a(lpszEnd), ch);
1232
1233   return SHLWAPI_StrRChrHelperA(lpszStr, lpszEnd, ch, ChrCmpIA);
1234 }
1235
1236 /**************************************************************************
1237  * StrRChrIW    [SHLWAPI.@]
1238  *
1239  * See StrRChrIA.
1240  */
1241 LPWSTR WINAPI StrRChrIW(LPCWSTR str, LPCWSTR end, WORD ch)
1242 {
1243     WCHAR *ret = NULL;
1244
1245     if (!str) return NULL;
1246     if (!end) end = str + strlenW(str);
1247     while (str < end)
1248     {
1249         if (!ChrCmpIW(*str, ch)) ret = (WCHAR *)str;
1250         str++;
1251     }
1252     return ret;
1253 }
1254
1255 /*************************************************************************
1256  * StrCatBuffA  [SHLWAPI.@]
1257  *
1258  * Concatenate two strings together.
1259  *
1260  * PARAMS
1261  *  lpszStr [O] String to concatenate to
1262  *  lpszCat [I] String to add to lpszCat
1263  *  cchMax  [I] Maximum number of characters for the whole string
1264  *
1265  * RETURNS
1266  *  lpszStr.
1267  *
1268  * NOTES
1269  *  cchMax determines the number of characters in the final length of the
1270  *  string, not the number appended to lpszStr from lpszCat.
1271  */
1272 LPSTR WINAPI StrCatBuffA(LPSTR lpszStr, LPCSTR lpszCat, INT cchMax)
1273 {
1274   INT iLen;
1275
1276   TRACE("(%p,%s,%d)\n", lpszStr, debugstr_a(lpszCat), cchMax);
1277
1278   if (!lpszStr)
1279   {
1280     WARN("Invalid lpszStr would crash under Win32!\n");
1281     return NULL;
1282   }
1283
1284   iLen = strlen(lpszStr);
1285   cchMax -= iLen;
1286
1287   if (cchMax > 0)
1288     StrCpyNA(lpszStr + iLen, lpszCat, cchMax);
1289   return lpszStr;
1290 }
1291
1292 /*************************************************************************
1293  * StrCatBuffW  [SHLWAPI.@]
1294  *
1295  * See StrCatBuffA.
1296  */
1297 LPWSTR WINAPI StrCatBuffW(LPWSTR lpszStr, LPCWSTR lpszCat, INT cchMax)
1298 {
1299   INT iLen;
1300
1301   TRACE("(%p,%s,%d)\n", lpszStr, debugstr_w(lpszCat), cchMax);
1302
1303   if (!lpszStr)
1304   {
1305     WARN("Invalid lpszStr would crash under Win32!\n");
1306     return NULL;
1307   }
1308
1309   iLen = strlenW(lpszStr);
1310   cchMax -= iLen;
1311
1312   if (cchMax > 0)
1313     StrCpyNW(lpszStr + iLen, lpszCat, cchMax);
1314   return lpszStr;
1315 }
1316
1317 /*************************************************************************
1318  * StrRetToBufA                                 [SHLWAPI.@]
1319  *
1320  * Convert a STRRET to a normal string.
1321  *
1322  * PARAMS
1323  *  lpStrRet [O] STRRET to convert
1324  *  pIdl     [I] ITEMIDLIST for lpStrRet->uType == STRRET_OFFSET
1325  *  lpszDest [O] Destination for normal string
1326  *  dwLen    [I] Length of lpszDest
1327  *
1328  * RETURNS
1329  *  Success: S_OK. lpszDest contains up to dwLen characters of the string.
1330  *           If lpStrRet is of type STRRET_WSTR, its memory is freed with
1331  *           CoTaskMemFree() and its type set to STRRET_CSTRA.
1332  *  Failure: E_FAIL, if any parameters are invalid.
1333  */
1334 HRESULT WINAPI StrRetToBufA (LPSTRRET src, const ITEMIDLIST *pidl, LPSTR dest, UINT len)
1335 {
1336         /* NOTE:
1337          *  This routine is identical to that in dlls/shell32/shellstring.c.
1338          *  It was duplicated because not every version of Shlwapi.dll exports
1339          *  StrRetToBufA. If you change one routine, change them both.
1340          */
1341         TRACE("dest=%p len=0x%x strret=%p pidl=%p stub\n",dest,len,src,pidl);
1342
1343         if (!src)
1344         {
1345           WARN("Invalid lpStrRet would crash under Win32!\n");
1346           if (dest)
1347             *dest = '\0';
1348           return E_FAIL;
1349         }
1350
1351         if (!dest || !len)
1352           return E_FAIL;
1353
1354         *dest = '\0';
1355
1356         switch (src->uType)
1357         {
1358           case STRRET_WSTR:
1359             WideCharToMultiByte(CP_ACP, 0, src->u.pOleStr, -1, dest, len, NULL, NULL);
1360             CoTaskMemFree(src->u.pOleStr);
1361             break;
1362
1363           case STRRET_CSTR:
1364             lstrcpynA(dest, src->u.cStr, len);
1365             break;
1366
1367           case STRRET_OFFSET:
1368             lstrcpynA((LPSTR)dest, ((LPCSTR)&pidl->mkid)+src->u.uOffset, len);
1369             break;
1370
1371           default:
1372             FIXME("unknown type!\n");
1373             return FALSE;
1374         }
1375         return S_OK;
1376 }
1377
1378 /*************************************************************************
1379  * StrRetToBufW [SHLWAPI.@]
1380  *
1381  * See StrRetToBufA.
1382  */
1383 HRESULT WINAPI StrRetToBufW (LPSTRRET src, const ITEMIDLIST *pidl, LPWSTR dest, UINT len)
1384 {
1385         TRACE("dest=%p len=0x%x strret=%p pidl=%p stub\n",dest,len,src,pidl);
1386
1387         if (!src)
1388         {
1389           WARN("Invalid lpStrRet would crash under Win32!\n");
1390           if (dest)
1391             *dest = '\0';
1392           return E_FAIL;
1393         }
1394
1395         if (!dest || !len)
1396           return E_FAIL;
1397
1398         *dest = '\0';
1399
1400         switch (src->uType)
1401         {
1402           case STRRET_WSTR:
1403             lstrcpynW(dest, src->u.pOleStr, len);
1404             CoTaskMemFree(src->u.pOleStr);
1405             break;
1406
1407           case STRRET_CSTR:
1408               if (!MultiByteToWideChar( CP_ACP, 0, src->u.cStr, -1, dest, len ) && len)
1409                   dest[len-1] = 0;
1410             break;
1411
1412           case STRRET_OFFSET:
1413             if (pidl)
1414             {
1415               if (!MultiByteToWideChar( CP_ACP, 0, ((LPCSTR)&pidl->mkid)+src->u.uOffset, -1,
1416                                         dest, len ) && len)
1417                   dest[len-1] = 0;
1418             }
1419             break;
1420
1421           default:
1422             FIXME("unknown type!\n");
1423             return FALSE;
1424         }
1425         return S_OK;
1426 }
1427
1428 /*************************************************************************
1429  * StrRetToStrA                                 [SHLWAPI.@]
1430  *
1431  * Converts a STRRET to a normal string.
1432  *
1433  * PARAMS
1434  *  lpStrRet [O] STRRET to convert
1435  *  pidl     [I] ITEMIDLIST for lpStrRet->uType == STRRET_OFFSET
1436  *  ppszName [O] Destination for converted string
1437  *
1438  * RETURNS
1439  *  Success: S_OK. ppszName contains the new string, allocated with CoTaskMemAlloc().
1440  *  Failure: E_FAIL, if any parameters are invalid.
1441  */
1442 HRESULT WINAPI StrRetToStrA(LPSTRRET lpStrRet, const ITEMIDLIST *pidl, LPSTR *ppszName)
1443 {
1444   HRESULT hRet = E_FAIL;
1445
1446   switch (lpStrRet->uType)
1447   {
1448   case STRRET_WSTR:
1449     hRet = _SHStrDupAW(lpStrRet->u.pOleStr, ppszName);
1450     CoTaskMemFree(lpStrRet->u.pOleStr);
1451     break;
1452
1453   case STRRET_CSTR:
1454     hRet = _SHStrDupAA(lpStrRet->u.cStr, ppszName);
1455     break;
1456
1457   case STRRET_OFFSET:
1458     hRet = _SHStrDupAA(((LPCSTR)&pidl->mkid) + lpStrRet->u.uOffset, ppszName);
1459     break;
1460
1461   default:
1462     *ppszName = NULL;
1463   }
1464
1465   return hRet;
1466 }
1467
1468 /*************************************************************************
1469  * StrRetToStrW                                 [SHLWAPI.@]
1470  *
1471  * See StrRetToStrA.
1472  */
1473 HRESULT WINAPI StrRetToStrW(LPSTRRET lpStrRet, const ITEMIDLIST *pidl, LPWSTR *ppszName)
1474 {
1475   HRESULT hRet = E_FAIL;
1476
1477   switch (lpStrRet->uType)
1478   {
1479   case STRRET_WSTR:
1480     hRet = SHStrDupW(lpStrRet->u.pOleStr, ppszName);
1481     CoTaskMemFree(lpStrRet->u.pOleStr);
1482     break;
1483
1484   case STRRET_CSTR:
1485     hRet = SHStrDupA(lpStrRet->u.cStr, ppszName);
1486     break;
1487
1488   case STRRET_OFFSET:
1489     hRet = SHStrDupA(((LPCSTR)&pidl->mkid) + lpStrRet->u.uOffset, ppszName);
1490     break;
1491
1492   default:
1493     *ppszName = NULL;
1494   }
1495
1496   return hRet;
1497 }
1498
1499 /* Create an ASCII string copy using SysAllocString() */
1500 static HRESULT _SHStrDupAToBSTR(LPCSTR src, BSTR *pBstrOut)
1501 {
1502     *pBstrOut = NULL;
1503
1504     if (src)
1505     {
1506         INT len = MultiByteToWideChar(CP_ACP, 0, src, -1, NULL, 0);
1507         WCHAR* szTemp = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1508
1509         if (szTemp)
1510         {
1511             MultiByteToWideChar(CP_ACP, 0, src, -1, szTemp, len);
1512             *pBstrOut = SysAllocString(szTemp);
1513             HeapFree(GetProcessHeap(), 0, szTemp);
1514
1515             if (*pBstrOut)
1516                 return S_OK;
1517         }
1518     }
1519     return E_OUTOFMEMORY;
1520 }
1521
1522 /*************************************************************************
1523  * StrRetToBSTR [SHLWAPI.@]
1524  *
1525  * Converts a STRRET to a BSTR.
1526  *
1527  * PARAMS
1528  *  lpStrRet [O] STRRET to convert
1529  *  pidl     [I] ITEMIDLIST for lpStrRet->uType = STRRET_OFFSET
1530  *  pBstrOut [O] Destination for converted BSTR
1531  *
1532  * RETURNS
1533  *  Success: S_OK. pBstrOut contains the new string.
1534  *  Failure: E_FAIL, if any parameters are invalid.
1535  */
1536 HRESULT WINAPI StrRetToBSTR(STRRET *lpStrRet, LPCITEMIDLIST pidl, BSTR* pBstrOut)
1537 {
1538   HRESULT hRet = E_FAIL;
1539
1540   switch (lpStrRet->uType)
1541   {
1542   case STRRET_WSTR:
1543     *pBstrOut = SysAllocString(lpStrRet->u.pOleStr);
1544     if (*pBstrOut)
1545       hRet = S_OK;
1546     CoTaskMemFree(lpStrRet->u.pOleStr);
1547     break;
1548
1549   case STRRET_CSTR:
1550     hRet = _SHStrDupAToBSTR(lpStrRet->u.cStr, pBstrOut);
1551     break;
1552
1553   case STRRET_OFFSET:
1554     hRet = _SHStrDupAToBSTR(((LPCSTR)&pidl->mkid) + lpStrRet->u.uOffset, pBstrOut);
1555     break;
1556
1557   default:
1558     *pBstrOut = NULL;
1559   }
1560
1561   return hRet;
1562 }
1563
1564 /*************************************************************************
1565  * StrFormatKBSizeA     [SHLWAPI.@]
1566  *
1567  * Create a formatted string containing a byte count in Kilobytes.
1568  *
1569  * PARAMS
1570  *  llBytes  [I] Byte size to format
1571  *  lpszDest [I] Destination for formatted string
1572  *  cchMax   [I] Size of lpszDest
1573  *
1574  * RETURNS
1575  *  lpszDest.
1576  */
1577 LPSTR WINAPI StrFormatKBSizeA(LONGLONG llBytes, LPSTR lpszDest, UINT cchMax)
1578 {
1579   WCHAR wszBuf[256];
1580   
1581   if (!StrFormatKBSizeW(llBytes, wszBuf, 256))
1582     return NULL;
1583   if (!WideCharToMultiByte(CP_ACP, 0, wszBuf, -1, lpszDest, cchMax, NULL, NULL))
1584     return NULL;
1585   return lpszDest;
1586 }
1587
1588 /*************************************************************************
1589  * StrFormatKBSizeW     [SHLWAPI.@]
1590  *
1591  * See StrFormatKBSizeA.
1592  */
1593 LPWSTR WINAPI StrFormatKBSizeW(LONGLONG llBytes, LPWSTR lpszDest, UINT cchMax)
1594 {
1595   static const WCHAR kb[] = {' ','K','B',0};
1596   LONGLONG llKB = (llBytes + 1023) >> 10;
1597   int len;
1598
1599   TRACE("(0x%s,%p,%d)\n", wine_dbgstr_longlong(llBytes), lpszDest, cchMax);
1600
1601   if (!FormatInt(llKB, lpszDest, cchMax))
1602     return NULL;
1603
1604   len = lstrlenW(lpszDest);
1605   if (cchMax - len < 4)
1606       return NULL;
1607   lstrcatW(lpszDest, kb);
1608   return lpszDest;
1609 }
1610
1611 /*************************************************************************
1612  * StrNCatA     [SHLWAPI.@]
1613  *
1614  * Concatenate two strings together.
1615  *
1616  * PARAMS
1617  *  lpszStr [O] String to concatenate to
1618  *  lpszCat [I] String to add to lpszCat
1619  *  cchMax  [I] Maximum number of characters to concatenate
1620  *
1621  * RETURNS
1622  *  lpszStr.
1623  *
1624  * NOTES
1625  *  cchMax determines the number of characters that are appended to lpszStr,
1626  *  not the total length of the string.
1627  */
1628 LPSTR WINAPI StrNCatA(LPSTR lpszStr, LPCSTR lpszCat, INT cchMax)
1629 {
1630   LPSTR lpszRet = lpszStr;
1631
1632   TRACE("(%s,%s,%i)\n", debugstr_a(lpszStr), debugstr_a(lpszCat), cchMax);
1633
1634   if (!lpszStr)
1635   {
1636     WARN("Invalid lpszStr would crash under Win32!\n");
1637     return NULL;
1638   }
1639
1640   StrCpyNA(lpszStr + strlen(lpszStr), lpszCat, cchMax);
1641   return lpszRet;
1642 }
1643
1644 /*************************************************************************
1645  * StrNCatW     [SHLWAPI.@]
1646  *
1647  * See StrNCatA.
1648  */
1649 LPWSTR WINAPI StrNCatW(LPWSTR lpszStr, LPCWSTR lpszCat, INT cchMax)
1650 {
1651   LPWSTR lpszRet = lpszStr;
1652
1653   TRACE("(%s,%s,%i)\n", debugstr_w(lpszStr), debugstr_w(lpszCat), cchMax);
1654
1655   if (!lpszStr)
1656   {
1657     WARN("Invalid lpszStr would crash under Win32\n");
1658     return NULL;
1659   }
1660
1661   StrCpyNW(lpszStr + strlenW(lpszStr), lpszCat, cchMax);
1662   return lpszRet;
1663 }
1664
1665 /*************************************************************************
1666  * StrTrimA     [SHLWAPI.@]
1667  *
1668  * Remove characters from the start and end of a string.
1669  *
1670  * PARAMS
1671  *  lpszStr  [O] String to remove characters from
1672  *  lpszTrim [I] Characters to remove from lpszStr
1673  *
1674  * RETURNS
1675  *  TRUE  If lpszStr was valid and modified
1676  *  FALSE Otherwise
1677  */
1678 BOOL WINAPI StrTrimA(LPSTR lpszStr, LPCSTR lpszTrim)
1679 {
1680   DWORD dwLen;
1681   LPSTR lpszRead = lpszStr;
1682   BOOL bRet = FALSE;
1683
1684   TRACE("(%s,%s)\n", debugstr_a(lpszStr), debugstr_a(lpszTrim));
1685
1686   if (lpszRead && *lpszRead)
1687   {
1688     while (*lpszRead && StrChrA(lpszTrim, *lpszRead))
1689       lpszRead = CharNextA(lpszRead); /* Skip leading matches */
1690
1691     dwLen = strlen(lpszRead);
1692
1693     if (lpszRead != lpszStr)
1694     {
1695       memmove(lpszStr, lpszRead, dwLen + 1);
1696       bRet = TRUE;
1697     }
1698     if (dwLen > 0)
1699     {
1700       lpszRead = lpszStr + dwLen;
1701       while (StrChrA(lpszTrim, lpszRead[-1]))
1702         lpszRead = CharPrevA(lpszStr, lpszRead); /* Skip trailing matches */
1703
1704       if (lpszRead != lpszStr + dwLen)
1705       {
1706         *lpszRead = '\0';
1707         bRet = TRUE;
1708       }
1709     }
1710   }
1711   return bRet;
1712 }
1713
1714 /*************************************************************************
1715  * StrTrimW     [SHLWAPI.@]
1716  *
1717  * See StrTrimA.
1718  */
1719 BOOL WINAPI StrTrimW(LPWSTR lpszStr, LPCWSTR lpszTrim)
1720 {
1721   DWORD dwLen;
1722   LPWSTR lpszRead = lpszStr;
1723   BOOL bRet = FALSE;
1724
1725   TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszTrim));
1726
1727   if (lpszRead && *lpszRead)
1728   {
1729     while (*lpszRead && StrChrW(lpszTrim, *lpszRead)) lpszRead++;
1730
1731     dwLen = strlenW(lpszRead);
1732
1733     if (lpszRead != lpszStr)
1734     {
1735       memmove(lpszStr, lpszRead, (dwLen + 1) * sizeof(WCHAR));
1736       bRet = TRUE;
1737     }
1738     if (dwLen > 0)
1739     {
1740       lpszRead = lpszStr + dwLen;
1741       while (StrChrW(lpszTrim, lpszRead[-1]))
1742         lpszRead--; /* Skip trailing matches */
1743
1744       if (lpszRead != lpszStr + dwLen)
1745       {
1746         *lpszRead = '\0';
1747         bRet = TRUE;
1748       }
1749     }
1750   }
1751   return bRet;
1752 }
1753
1754 /*************************************************************************
1755  *      _SHStrDupAA     [INTERNAL]
1756  *
1757  * Duplicates a ASCII string to ASCII. The destination buffer is allocated.
1758  */
1759 static HRESULT _SHStrDupAA(LPCSTR src, LPSTR * dest)
1760 {
1761         HRESULT hr;
1762         int len = 0;
1763
1764         if (src) {
1765             len = lstrlenA(src) + 1;
1766             *dest = CoTaskMemAlloc(len);
1767         } else {
1768             *dest = NULL;
1769         }
1770
1771         if (*dest) {
1772             lstrcpynA(*dest,src, len);
1773             hr = S_OK;
1774         } else {
1775             hr = E_OUTOFMEMORY;
1776         }
1777
1778         TRACE("%s->(%p)\n", debugstr_a(src), *dest);
1779         return hr;
1780 }
1781
1782 /*************************************************************************
1783  * SHStrDupA    [SHLWAPI.@]
1784  *
1785  * Return a Unicode copy of a string, in memory allocated by CoTaskMemAlloc().
1786  *
1787  * PARAMS
1788  *  lpszStr   [I] String to copy
1789  *  lppszDest [O] Destination for the new string copy
1790  *
1791  * RETURNS
1792  *  Success: S_OK. lppszDest contains the new string in Unicode format.
1793  *  Failure: E_OUTOFMEMORY, If any arguments are invalid or memory allocation
1794  *           fails.
1795  */
1796 HRESULT WINAPI SHStrDupA(LPCSTR lpszStr, LPWSTR * lppszDest)
1797 {
1798   HRESULT hRet;
1799   int len = 0;
1800
1801   if (lpszStr)
1802   {
1803     len = MultiByteToWideChar(0, 0, lpszStr, -1, 0, 0) * sizeof(WCHAR);
1804     *lppszDest = CoTaskMemAlloc(len);
1805   }
1806   else
1807     *lppszDest = NULL;
1808
1809   if (*lppszDest)
1810   {
1811     MultiByteToWideChar(0, 0, lpszStr, -1, *lppszDest, len/sizeof(WCHAR));
1812     hRet = S_OK;
1813   }
1814   else
1815     hRet = E_OUTOFMEMORY;
1816
1817   TRACE("%s->(%p)\n", debugstr_a(lpszStr), *lppszDest);
1818   return hRet;
1819 }
1820
1821 /*************************************************************************
1822  *      _SHStrDupAW     [INTERNAL]
1823  *
1824  * Duplicates a UNICODE to a ASCII string. The destination buffer is allocated.
1825  */
1826 static HRESULT _SHStrDupAW(LPCWSTR src, LPSTR * dest)
1827 {
1828         HRESULT hr;
1829         int len = 0;
1830
1831         if (src) {
1832             len = WideCharToMultiByte(CP_ACP, 0, src, -1, NULL, 0, NULL, NULL);
1833             *dest = CoTaskMemAlloc(len);
1834         } else {
1835             *dest = NULL;
1836         }
1837
1838         if (*dest) {
1839             WideCharToMultiByte(CP_ACP, 0, src, -1, *dest, len, NULL, NULL);
1840             hr = S_OK;
1841         } else {
1842             hr = E_OUTOFMEMORY;
1843         }
1844
1845         TRACE("%s->(%p)\n", debugstr_w(src), *dest);
1846         return hr;
1847 }
1848
1849 /*************************************************************************
1850  * SHStrDupW    [SHLWAPI.@]
1851  *
1852  * See SHStrDupA.
1853  */
1854 HRESULT WINAPI SHStrDupW(LPCWSTR src, LPWSTR * dest)
1855 {
1856         HRESULT hr;
1857         int len = 0;
1858
1859         if (src) {
1860             len = (lstrlenW(src) + 1) * sizeof(WCHAR);
1861             *dest = CoTaskMemAlloc(len);
1862         } else {
1863             *dest = NULL;
1864         }
1865
1866         if (*dest) {
1867             memcpy(*dest, src, len);
1868             hr = S_OK;
1869         } else {
1870             hr = E_OUTOFMEMORY;
1871         }
1872
1873         TRACE("%s->(%p)\n", debugstr_w(src), *dest);
1874         return hr;
1875 }
1876
1877 /*************************************************************************
1878  * SHLWAPI_WriteReverseNum
1879  *
1880  * Internal helper for SHLWAPI_WriteTimeClass.
1881  */
1882 static inline LPWSTR SHLWAPI_WriteReverseNum(LPWSTR lpszOut, DWORD dwNum)
1883 {
1884   *lpszOut-- = '\0';
1885
1886   /* Write a decimal number to a string, backwards */
1887   do
1888   {
1889     DWORD dwNextDigit = dwNum % 10;
1890     *lpszOut-- = '0' + dwNextDigit;
1891     dwNum = (dwNum - dwNextDigit) / 10;
1892   } while (dwNum > 0);
1893
1894   return lpszOut;
1895 }
1896
1897 /*************************************************************************
1898  * SHLWAPI_FormatSignificant
1899  *
1900  * Internal helper for SHLWAPI_WriteTimeClass.
1901  */
1902 static inline int SHLWAPI_FormatSignificant(LPWSTR lpszNum, int dwDigits)
1903 {
1904   /* Zero non significant digits, return remaining significant digits */
1905   while (*lpszNum)
1906   {
1907     lpszNum++;
1908     if (--dwDigits == 0)
1909     {
1910       while (*lpszNum)
1911         *lpszNum++ = '0';
1912       return 0;
1913     }
1914   }
1915   return dwDigits;
1916 }
1917
1918 /*************************************************************************
1919  * SHLWAPI_WriteTimeClass
1920  *
1921  * Internal helper for StrFromTimeIntervalW.
1922  */
1923 static int SHLWAPI_WriteTimeClass(LPWSTR lpszOut, DWORD dwValue,
1924                                   UINT uClassStringId, int iDigits)
1925 {
1926   WCHAR szBuff[64], *szOut = szBuff + 32;
1927
1928   szOut = SHLWAPI_WriteReverseNum(szOut, dwValue);
1929   iDigits = SHLWAPI_FormatSignificant(szOut + 1, iDigits);
1930   *szOut = ' ';
1931   LoadStringW(shlwapi_hInstance, uClassStringId, szBuff + 32, 32);
1932   strcatW(lpszOut, szOut);
1933   return iDigits;
1934 }
1935
1936 /*************************************************************************
1937  * StrFromTimeIntervalA [SHLWAPI.@]
1938  *
1939  * Format a millisecond time interval into a string
1940  *
1941  * PARAMS
1942  *  lpszStr  [O] Output buffer for formatted time interval
1943  *  cchMax   [I] Size of lpszStr
1944  *  dwMS     [I] Number of milliseconds
1945  *  iDigits  [I] Number of digits to print
1946  *
1947  * RETURNS
1948  *  The length of the formatted string, or 0 if any parameter is invalid.
1949  *
1950  * NOTES
1951  *  This implementation mimics the Win32 behaviour of always writing a leading
1952  *  space before the time interval begins.
1953  *
1954  *  iDigits is used to provide approximate times if accuracy is not important.
1955  *  This number of digits will be written of the first non-zero time class
1956  *  (hours/minutes/seconds). If this does not complete the time classification,
1957  *  the remaining digits are changed to zeros (i.e. The time is _not_ rounded).
1958  *  If there are digits remaining following the writing of a time class, the
1959  *  next time class will be written.
1960  *
1961  *  For example, given dwMS represents 138 hours,43 minutes and 15 seconds, the
1962  *  following will result from the given values of iDigits:
1963  *
1964  *|  iDigits    1        2        3        4               5               ...
1965  *|  lpszStr   "100 hr" "130 hr" "138 hr" "138 hr 40 min" "138 hr 43 min"  ...
1966  */
1967 INT WINAPI StrFromTimeIntervalA(LPSTR lpszStr, UINT cchMax, DWORD dwMS,
1968                                 int iDigits)
1969 {
1970   INT iRet = 0;
1971
1972   TRACE("(%p,%d,%d,%d)\n", lpszStr, cchMax, dwMS, iDigits);
1973
1974   if (lpszStr && cchMax)
1975   {
1976     WCHAR szBuff[128];
1977     StrFromTimeIntervalW(szBuff, sizeof(szBuff)/sizeof(WCHAR), dwMS, iDigits);
1978     WideCharToMultiByte(CP_ACP,0,szBuff,-1,lpszStr,cchMax,0,0);
1979   }
1980   return iRet;
1981 }
1982
1983
1984 /*************************************************************************
1985  * StrFromTimeIntervalW [SHLWAPI.@]
1986  *
1987  * See StrFromTimeIntervalA.
1988  */
1989 INT WINAPI StrFromTimeIntervalW(LPWSTR lpszStr, UINT cchMax, DWORD dwMS,
1990                                 int iDigits)
1991 {
1992   INT iRet = 0;
1993
1994   TRACE("(%p,%d,%d,%d)\n", lpszStr, cchMax, dwMS, iDigits);
1995
1996   if (lpszStr && cchMax)
1997   {
1998     WCHAR szCopy[128];
1999     DWORD dwHours, dwMinutes;
2000
2001     if (!iDigits || cchMax == 1)
2002     {
2003       *lpszStr = '\0';
2004       return 0;
2005     }
2006
2007     /* Calculate the time classes */
2008     dwMS = (dwMS + 500) / 1000;
2009     dwHours = dwMS / 3600;
2010     dwMS -= dwHours * 3600;
2011     dwMinutes = dwMS / 60;
2012     dwMS -= dwMinutes * 60;
2013
2014     szCopy[0] = '\0';
2015
2016     if (dwHours)
2017       iDigits = SHLWAPI_WriteTimeClass(szCopy, dwHours, IDS_TIME_INTERVAL_HOURS, iDigits);
2018
2019     if (dwMinutes && iDigits)
2020       iDigits = SHLWAPI_WriteTimeClass(szCopy, dwMinutes, IDS_TIME_INTERVAL_MINUTES, iDigits);
2021
2022     if (iDigits) /* Always write seconds if we have significant digits */
2023       SHLWAPI_WriteTimeClass(szCopy, dwMS, IDS_TIME_INTERVAL_SECONDS, iDigits);
2024
2025     lstrcpynW(lpszStr, szCopy, cchMax);
2026     iRet = strlenW(lpszStr);
2027   }
2028   return iRet;
2029 }
2030
2031 /*************************************************************************
2032  * StrIsIntlEqualA      [SHLWAPI.@]
2033  *
2034  * Compare two strings.
2035  *
2036  * PARAMS
2037  *  bCase    [I] Whether to compare case sensitively
2038  *  lpszStr  [I] First string to compare
2039  *  lpszComp [I] Second string to compare
2040  *  iLen     [I] Length to compare
2041  *
2042  * RETURNS
2043  *  TRUE  If the strings are equal.
2044  *  FALSE Otherwise.
2045  */
2046 BOOL WINAPI StrIsIntlEqualA(BOOL bCase, LPCSTR lpszStr, LPCSTR lpszComp,
2047                             int iLen)
2048 {
2049   DWORD dwFlags;
2050
2051   TRACE("(%d,%s,%s,%d)\n", bCase,
2052         debugstr_a(lpszStr), debugstr_a(lpszComp), iLen);
2053
2054   /* FIXME: This flag is undocumented and unknown by our CompareString.
2055    *        We need a define for it.
2056    */
2057   dwFlags = 0x10000000;
2058   if (!bCase) dwFlags |= NORM_IGNORECASE;
2059
2060   return (CompareStringA(GetThreadLocale(), dwFlags, lpszStr, iLen, lpszComp, iLen) == CSTR_EQUAL);
2061 }
2062
2063 /*************************************************************************
2064  * StrIsIntlEqualW      [SHLWAPI.@]
2065  *
2066  * See StrIsIntlEqualA.
2067  */
2068 BOOL WINAPI StrIsIntlEqualW(BOOL bCase, LPCWSTR lpszStr, LPCWSTR lpszComp,
2069                             int iLen)
2070 {
2071   DWORD dwFlags;
2072
2073   TRACE("(%d,%s,%s,%d)\n", bCase,
2074         debugstr_w(lpszStr),debugstr_w(lpszComp), iLen);
2075
2076   /* FIXME: This flag is undocumented and unknown by our CompareString.
2077    *        We need a define for it.
2078    */
2079   dwFlags = 0x10000000;
2080   if (!bCase) dwFlags |= NORM_IGNORECASE;
2081
2082   return (CompareStringW(GetThreadLocale(), dwFlags, lpszStr, iLen, lpszComp, iLen) == CSTR_EQUAL);
2083 }
2084
2085 /*************************************************************************
2086  * @    [SHLWAPI.399]
2087  *
2088  * Copy a string to another string, up to a maximum number of characters.
2089  *
2090  * PARAMS
2091  *  lpszDest [O] Destination string
2092  *  lpszSrc  [I] Source string
2093  *  iLen     [I] Maximum number of chars to copy
2094  *
2095  * RETURNS
2096  *  Success: A pointer to the last character written to lpszDest.
2097  *  Failure: lpszDest, if any arguments are invalid.
2098  */
2099 LPSTR WINAPI StrCpyNXA(LPSTR lpszDest, LPCSTR lpszSrc, int iLen)
2100 {
2101   TRACE("(%p,%s,%i)\n", lpszDest, debugstr_a(lpszSrc), iLen);
2102
2103   if (lpszDest && lpszSrc && iLen > 0)
2104   {
2105     while ((iLen-- > 1) && *lpszSrc)
2106       *lpszDest++ = *lpszSrc++;
2107     if (iLen >= 0)
2108      *lpszDest = '\0';
2109   }
2110   return lpszDest;
2111 }
2112
2113 /*************************************************************************
2114  * @    [SHLWAPI.400]
2115  *
2116  * Unicode version of StrCpyNXA.
2117  */
2118 LPWSTR WINAPI StrCpyNXW(LPWSTR lpszDest, LPCWSTR lpszSrc, int iLen)
2119 {
2120   TRACE("(%p,%s,%i)\n", lpszDest, debugstr_w(lpszSrc), iLen);
2121
2122   if (lpszDest && lpszSrc && iLen > 0)
2123   {
2124     while ((iLen-- > 1) && *lpszSrc)
2125       *lpszDest++ = *lpszSrc++;
2126     if (iLen >= 0)
2127      *lpszDest = '\0';
2128   }
2129   return lpszDest;
2130 }
2131
2132 /*************************************************************************
2133  * StrCmpLogicalW       [SHLWAPI.@]
2134  *
2135  * Compare two strings, ignoring case and comparing digits as numbers.
2136  *
2137  * PARAMS
2138  *  lpszStr  [I] First string to compare
2139  *  lpszComp [I] Second string to compare
2140  *  iLen     [I] Length to compare
2141  *
2142  * RETURNS
2143  *  TRUE  If the strings are equal.
2144  *  FALSE Otherwise.
2145  */
2146 INT WINAPI StrCmpLogicalW(LPCWSTR lpszStr, LPCWSTR lpszComp)
2147 {
2148   INT iDiff;
2149
2150   TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszComp));
2151
2152   if (lpszStr && lpszComp)
2153   {
2154     while (*lpszStr)
2155     {
2156       if (!*lpszComp)
2157         return 1;
2158       else if (isdigitW(*lpszStr))
2159       {
2160         int iStr, iComp;
2161
2162         if (!isdigitW(*lpszComp))
2163           return -1;
2164
2165         /* Compare the numbers */
2166         StrToIntExW(lpszStr, 0, &iStr);
2167         StrToIntExW(lpszComp, 0, &iComp);
2168
2169         if (iStr < iComp)
2170           return -1;
2171         else if (iStr > iComp)
2172           return 1;
2173
2174         /* Skip */
2175         while (isdigitW(*lpszStr))
2176           lpszStr++;
2177         while (isdigitW(*lpszComp))
2178           lpszComp++;
2179       }
2180       else if (isdigitW(*lpszComp))
2181         return 1;
2182       else
2183       {
2184         iDiff = ChrCmpIW(*lpszStr,*lpszComp);
2185         if (iDiff > 0)
2186           return 1;
2187         else if (iDiff < 0)
2188           return -1;
2189
2190         lpszStr++;
2191         lpszComp++;
2192       }
2193     }
2194     if (*lpszComp)
2195       return -1;
2196   }
2197   return 0;
2198 }
2199
2200 /* Structure for formatting byte strings */
2201 typedef struct tagSHLWAPI_BYTEFORMATS
2202 {
2203   LONGLONG dLimit;
2204   double   dDivisor;
2205   double   dNormaliser;
2206   int      nDecimals;
2207   WCHAR     wPrefix;
2208 } SHLWAPI_BYTEFORMATS;
2209
2210 /*************************************************************************
2211  * StrFormatByteSizeW   [SHLWAPI.@]
2212  *
2213  * Create a string containing an abbreviated byte count of up to 2^63-1.
2214  *
2215  * PARAMS
2216  *  llBytes  [I] Byte size to format
2217  *  lpszDest [I] Destination for formatted string
2218  *  cchMax   [I] Size of lpszDest
2219  *
2220  * RETURNS
2221  *  lpszDest.
2222  *
2223  * NOTES
2224  *  There is no StrFormatByteSize64W function, it is called StrFormatByteSizeW().
2225  */
2226 LPWSTR WINAPI StrFormatByteSizeW(LONGLONG llBytes, LPWSTR lpszDest, UINT cchMax)
2227 {
2228 #define KB ((ULONGLONG)1024)
2229 #define MB (KB*KB)
2230 #define GB (KB*KB*KB)
2231 #define TB (KB*KB*KB*KB)
2232 #define PB (KB*KB*KB*KB*KB)
2233
2234   static const SHLWAPI_BYTEFORMATS bfFormats[] =
2235   {
2236     { 10*KB, 10.24, 100.0, 2, 'K' }, /* 10 KB */
2237     { 100*KB, 102.4, 10.0, 1, 'K' }, /* 100 KB */
2238     { 1000*KB, 1024.0, 1.0, 0, 'K' }, /* 1000 KB */
2239     { 10*MB, 10485.76, 100.0, 2, 'M' }, /* 10 MB */
2240     { 100*MB, 104857.6, 10.0, 1, 'M' }, /* 100 MB */
2241     { 1000*MB, 1048576.0, 1.0, 0, 'M' }, /* 1000 MB */
2242     { 10*GB, 10737418.24, 100.0, 2, 'G' }, /* 10 GB */
2243     { 100*GB, 107374182.4, 10.0, 1, 'G' }, /* 100 GB */
2244     { 1000*GB, 1073741824.0, 1.0, 0, 'G' }, /* 1000 GB */
2245     { 10*TB, 10485.76, 100.0, 2, 'T' }, /* 10 TB */
2246     { 100*TB, 104857.6, 10.0, 1, 'T' }, /* 100 TB */
2247     { 1000*TB, 1048576.0, 1.0, 0, 'T' }, /* 1000 TB */
2248     { 10*PB, 10737418.24, 100.00, 2, 'P' }, /* 10 PB */
2249     { 100*PB, 107374182.4, 10.00, 1, 'P' }, /* 100 PB */
2250     { 1000*PB, 1073741824.0, 1.00, 0, 'P' }, /* 1000 PB */
2251     { 0, 10995116277.76, 100.00, 2, 'E' } /* EB's, catch all */
2252   };
2253   WCHAR wszAdd[] = {' ','?','B',0};
2254   double dBytes;
2255   UINT i = 0;
2256
2257   TRACE("(0x%s,%p,%d)\n", wine_dbgstr_longlong(llBytes), lpszDest, cchMax);
2258
2259   if (!lpszDest || !cchMax)
2260     return lpszDest;
2261
2262   if (llBytes < 1024)  /* 1K */
2263   {
2264     WCHAR wszBytesFormat[64];
2265     LoadStringW(shlwapi_hInstance, IDS_BYTES_FORMAT, wszBytesFormat, 64);
2266     snprintfW(lpszDest, cchMax, wszBytesFormat, (long)llBytes);
2267     return lpszDest;
2268   }
2269
2270   /* Note that if this loop completes without finding a match, i will be
2271    * pointing at the last entry, which is a catch all for > 1000 PB
2272    */
2273   while (i < sizeof(bfFormats) / sizeof(SHLWAPI_BYTEFORMATS) - 1)
2274   {
2275     if (llBytes < bfFormats[i].dLimit)
2276       break;
2277     i++;
2278   }
2279   /* Above 1 TB we encounter problems with FP accuracy. So for amounts above
2280    * this number we integer shift down by 1 MB first. The table above has
2281    * the divisors scaled down from the '< 10 TB' entry onwards, to account
2282    * for this. We also add a small fudge factor to get the correct result for
2283    * counts that lie exactly on a 1024 byte boundary.
2284    */
2285   if (i > 8)
2286     dBytes = (double)(llBytes >> 20) + 0.001; /* Scale down by I MB */
2287   else
2288     dBytes = (double)llBytes + 0.00001;
2289
2290   dBytes = floor(dBytes / bfFormats[i].dDivisor) / bfFormats[i].dNormaliser;
2291
2292   if (!FormatDouble(dBytes, bfFormats[i].nDecimals, lpszDest, cchMax))
2293     return NULL;
2294   wszAdd[1] = bfFormats[i].wPrefix;
2295   StrCatBuffW(lpszDest, wszAdd, cchMax);
2296   return lpszDest;
2297 }
2298
2299 /*************************************************************************
2300  * StrFormatByteSize64A [SHLWAPI.@]
2301  *
2302  * See StrFormatByteSizeW.
2303  */
2304 LPSTR WINAPI StrFormatByteSize64A(LONGLONG llBytes, LPSTR lpszDest, UINT cchMax)
2305 {
2306   WCHAR wszBuff[32];
2307
2308   StrFormatByteSizeW(llBytes, wszBuff, sizeof(wszBuff)/sizeof(WCHAR));
2309
2310   if (lpszDest)
2311     WideCharToMultiByte(CP_ACP, 0, wszBuff, -1, lpszDest, cchMax, 0, 0);
2312   return lpszDest;
2313 }
2314
2315 /*************************************************************************
2316  * StrFormatByteSizeA   [SHLWAPI.@]
2317  *
2318  * Create a string containing an abbreviated byte count of up to 2^31-1.
2319  *
2320  * PARAMS
2321  *  dwBytes  [I] Byte size to format
2322  *  lpszDest [I] Destination for formatted string
2323  *  cchMax   [I] Size of lpszDest
2324  *
2325  * RETURNS
2326  *  lpszDest.
2327  *
2328  * NOTES
2329  *  The Ascii and Unicode versions of this function accept a different
2330  *  integer type for dwBytes. See StrFormatByteSize64A().
2331  */
2332 LPSTR WINAPI StrFormatByteSizeA(DWORD dwBytes, LPSTR lpszDest, UINT cchMax)
2333 {
2334   TRACE("(%d,%p,%d)\n", dwBytes, lpszDest, cchMax);
2335
2336   return StrFormatByteSize64A(dwBytes, lpszDest, cchMax);
2337 }
2338
2339 /*************************************************************************
2340  *      @       [SHLWAPI.162]
2341  *
2342  * Remove a hanging lead byte from the end of a string, if present.
2343  *
2344  * PARAMS
2345  *  lpStr [I] String to check for a hanging lead byte
2346  *  size  [I] Length of lpStr
2347  *
2348  * RETURNS
2349  *  Success: The new length of the string. Any hanging lead bytes are removed.
2350  *  Failure: 0, if any parameters are invalid.
2351  */
2352 DWORD WINAPI SHTruncateString(LPSTR lpStr, DWORD size)
2353 {
2354   if (lpStr && size)
2355   {
2356     LPSTR lastByte = lpStr + size - 1;
2357
2358     while(lpStr < lastByte)
2359       lpStr += IsDBCSLeadByte(*lpStr) ? 2 : 1;
2360
2361     if(lpStr == lastByte && IsDBCSLeadByte(*lpStr))
2362     {
2363       *lpStr = '\0';
2364       size--;
2365     }
2366     return size;
2367   }
2368   return 0;
2369 }
2370
2371 /*************************************************************************
2372  *      @       [SHLWAPI.203]
2373  *
2374  * Remove a single non-trailing ampersand ('&') from a string.
2375  *
2376  * PARAMS
2377  *  lpszStr [I/O] String to remove ampersand from.
2378  *
2379  * RETURNS
2380  *  The character after the first ampersand in lpszStr, or the first character
2381  *  in lpszStr if there is no ampersand in the string.
2382  */
2383 char WINAPI SHStripMneumonicA(LPCSTR lpszStr)
2384 {
2385   LPSTR lpszIter, lpszTmp;
2386   char ch;
2387
2388   TRACE("(%s)\n", debugstr_a(lpszStr));
2389
2390   ch = *lpszStr;
2391
2392   if ((lpszIter = StrChrA(lpszStr, '&')))
2393   {
2394     lpszTmp = CharNextA(lpszIter);
2395     if (lpszTmp && *lpszTmp)
2396     {
2397       if (*lpszTmp != '&')
2398         ch =  *lpszTmp;
2399
2400       while (lpszIter && *lpszIter)
2401       {
2402         lpszTmp = CharNextA(lpszIter);
2403         *lpszIter = *lpszTmp;
2404         lpszIter = lpszTmp;
2405       }
2406     }
2407   }
2408
2409   return ch;
2410 }
2411
2412 /*************************************************************************
2413  *      @       [SHLWAPI.225]
2414  *
2415  * Unicode version of SHStripMneumonicA.
2416  */
2417 WCHAR WINAPI SHStripMneumonicW(LPCWSTR lpszStr)
2418 {
2419   LPWSTR lpszIter, lpszTmp;
2420   WCHAR ch;
2421
2422   TRACE("(%s)\n", debugstr_w(lpszStr));
2423
2424   ch = *lpszStr;
2425
2426   if ((lpszIter = StrChrW(lpszStr, '&')))
2427   {
2428     lpszTmp = lpszIter + 1;
2429     if (lpszTmp && *lpszTmp)
2430     {
2431       if (*lpszTmp != '&')
2432         ch =  *lpszTmp;
2433
2434       while (lpszIter && *lpszIter)
2435       {
2436         lpszTmp = lpszIter + 1;
2437         *lpszIter = *lpszTmp;
2438         lpszIter = lpszTmp;
2439       }
2440     }
2441   }
2442
2443   return ch;
2444 }
2445
2446 /*************************************************************************
2447  *      @       [SHLWAPI.216]
2448  *
2449  * Convert an Ascii string to Unicode.
2450  *
2451  * PARAMS
2452  * dwCp     [I] Code page for the conversion
2453  * lpSrcStr [I] Source Ascii string to convert
2454  * lpDstStr [O] Destination for converted Unicode string
2455  * iLen     [I] Length of lpDstStr
2456  *
2457  * RETURNS
2458  *  The return value of the MultiByteToWideChar() function called on lpSrcStr.
2459  */
2460 DWORD WINAPI SHAnsiToUnicodeCP(DWORD dwCp, LPCSTR lpSrcStr, LPWSTR lpDstStr, int iLen)
2461 {
2462   DWORD dwRet;
2463
2464   dwRet = MultiByteToWideChar(dwCp, 0, lpSrcStr, -1, lpDstStr, iLen);
2465   TRACE("%s->%s,ret=%d\n", debugstr_a(lpSrcStr), debugstr_w(lpDstStr), dwRet);
2466   return dwRet;
2467 }
2468
2469 /*************************************************************************
2470  *      @       [SHLWAPI.215]
2471  *
2472  * Convert an Ascii string to Unicode.
2473  *
2474  * PARAMS
2475  * lpSrcStr [I] Source Ascii string to convert
2476  * lpDstStr [O] Destination for converted Unicode string
2477  * iLen     [I] Length of lpDstStr
2478  *
2479  * RETURNS
2480  *  The return value of the MultiByteToWideChar() function called on lpSrcStr.
2481  *
2482  * NOTES
2483  *  This function simply calls SHAnsiToUnicodeCP with code page CP_ACP.
2484  */
2485 DWORD WINAPI SHAnsiToUnicode(LPCSTR lpSrcStr, LPWSTR lpDstStr, int iLen)
2486 {
2487   return SHAnsiToUnicodeCP(CP_ACP, lpSrcStr, lpDstStr, iLen);
2488 }
2489
2490 /*************************************************************************
2491  *      @       [SHLWAPI.218]
2492  *
2493  * Convert a Unicode string to Ascii.
2494  *
2495  * PARAMS
2496  *  CodePage [I] Code page to use for the conversion
2497  *  lpSrcStr [I] Source Unicode string to convert
2498  *  lpDstStr [O] Destination for converted Ascii string
2499  *  dstlen   [I] Length of buffer at lpDstStr
2500  *
2501  * RETURNS
2502  *  Success: The length in bytes of the result at lpDstStr (including the terminator)
2503  *  Failure: When using CP_UTF8, CP_UTF7 or 0xc350 as codePage, 0 is returned and
2504  *           the result is not nul-terminated.
2505  *           When using a different codepage, the length in bytes of the truncated
2506  *           result at lpDstStr (including the terminator) is returned and
2507  *           lpDstStr is always nul-terminated.
2508  *
2509  */
2510 DWORD WINAPI SHUnicodeToAnsiCP(UINT CodePage, LPCWSTR lpSrcStr, LPSTR lpDstStr, int dstlen)
2511 {
2512   static const WCHAR emptyW[] = { '\0' };
2513   int len , reqLen;
2514   LPSTR mem;
2515
2516   if (!lpDstStr || !dstlen)
2517     return 0;
2518
2519   if (!lpSrcStr)
2520     lpSrcStr = emptyW;
2521
2522   *lpDstStr = '\0';
2523
2524   len = strlenW(lpSrcStr) + 1;
2525
2526   switch (CodePage)
2527   {
2528   case CP_WINUNICODE:
2529     CodePage = CP_UTF8; /* Fall through... */
2530   case 0x0000C350: /* FIXME: CP_ #define */
2531   case CP_UTF7:
2532   case CP_UTF8:
2533     {
2534       DWORD dwMode = 0;
2535       INT lenW = len - 1;
2536       INT needed = dstlen - 1;
2537       HRESULT hr;
2538
2539       /* try the user supplied buffer first */
2540       hr = ConvertINetUnicodeToMultiByte(&dwMode, CodePage, lpSrcStr, &lenW, lpDstStr, &needed);
2541       if (hr == S_OK)
2542       {
2543         lpDstStr[needed] = '\0';
2544         return needed + 1;
2545       }
2546
2547       /* user buffer too small. exclude termination and copy as much as possible */
2548       lenW = len;
2549       hr = ConvertINetUnicodeToMultiByte(&dwMode, CodePage, lpSrcStr, &lenW, NULL, &needed);
2550       needed++;
2551       mem = HeapAlloc(GetProcessHeap(), 0, needed);
2552       if (!mem)
2553         return 0;
2554
2555       hr = ConvertINetUnicodeToMultiByte(&dwMode, CodePage, lpSrcStr, &len, mem, &needed);
2556       if (hr == S_OK)
2557       {
2558           reqLen = SHTruncateString(mem, dstlen);
2559           if (reqLen > 0) memcpy(lpDstStr, mem, reqLen-1);
2560       }
2561       HeapFree(GetProcessHeap(), 0, mem);
2562       return 0;
2563     }
2564   default:
2565     break;
2566   }
2567
2568   /* try the user supplied buffer first */
2569   reqLen = WideCharToMultiByte(CodePage, 0, lpSrcStr, len, lpDstStr, dstlen, NULL, NULL);
2570
2571   if (!reqLen && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
2572   {
2573     reqLen = WideCharToMultiByte(CodePage, 0, lpSrcStr, len, NULL, 0, NULL, NULL);
2574     if (reqLen)
2575     {
2576       mem = HeapAlloc(GetProcessHeap(), 0, reqLen);
2577       if (mem)
2578       {
2579         reqLen = WideCharToMultiByte(CodePage, 0, lpSrcStr, len, mem,
2580                                      reqLen, NULL, NULL);
2581
2582         reqLen = SHTruncateString(mem, dstlen -1);
2583         reqLen++;
2584
2585         lstrcpynA(lpDstStr, mem, reqLen);
2586         HeapFree(GetProcessHeap(), 0, mem);
2587         lpDstStr[reqLen-1] = '\0';
2588       }
2589     }
2590   }
2591   return reqLen;
2592 }
2593
2594 /*************************************************************************
2595  *      @       [SHLWAPI.217]
2596  *
2597  * Convert a Unicode string to Ascii.
2598  *
2599  * PARAMS
2600  *  lpSrcStr [I] Source Unicode string to convert
2601  *  lpDstStr [O] Destination for converted Ascii string
2602  *  iLen     [O] Length of lpDstStr in characters
2603  *
2604  * RETURNS
2605  *  See SHUnicodeToAnsiCP
2606
2607  * NOTES
2608  *  This function simply calls SHUnicodeToAnsiCP() with CodePage = CP_ACP.
2609  */
2610 INT WINAPI SHUnicodeToAnsi(LPCWSTR lpSrcStr, LPSTR lpDstStr, INT iLen)
2611 {
2612     return SHUnicodeToAnsiCP(CP_ACP, lpSrcStr, lpDstStr, iLen);
2613 }
2614
2615 /*************************************************************************
2616  *      @       [SHLWAPI.345]
2617  *
2618  * Copy one string to another.
2619  *
2620  * PARAMS
2621  *  lpszSrc [I] Source string to copy
2622  *  lpszDst [O] Destination for copy
2623  *  iLen    [I] Length of lpszDst in characters
2624  *
2625  * RETURNS
2626  *  The length of the copied string, including the terminating NUL. lpszDst
2627  *  contains iLen characters of lpszSrc.
2628  */
2629 DWORD WINAPI SHAnsiToAnsi(LPCSTR lpszSrc, LPSTR lpszDst, int iLen)
2630 {
2631     LPSTR lpszRet;
2632
2633     TRACE("(%s,%p,0x%08x)\n", debugstr_a(lpszSrc), lpszDst, iLen);
2634
2635     lpszRet = StrCpyNXA(lpszDst, lpszSrc, iLen);
2636     return lpszRet - lpszDst + 1;
2637 }
2638
2639 /*************************************************************************
2640  *      @       [SHLWAPI.346]
2641  *
2642  * Unicode version of SSHAnsiToAnsi.
2643  */
2644 DWORD WINAPI SHUnicodeToUnicode(LPCWSTR lpszSrc, LPWSTR lpszDst, int iLen)
2645 {
2646     LPWSTR lpszRet;
2647
2648     TRACE("(%s,%p,0x%08x)\n", debugstr_w(lpszSrc), lpszDst, iLen);
2649
2650     lpszRet = StrCpyNXW(lpszDst, lpszSrc, iLen);
2651     return lpszRet - lpszDst + 1;
2652 }
2653
2654 /*************************************************************************
2655  *      @       [SHLWAPI.364]
2656  *
2657  * Determine if an Ascii string converts to Unicode and back identically.
2658  *
2659  * PARAMS
2660  *  lpSrcStr [I] Source Unicode string to convert
2661  *  lpDst    [O] Destination for resulting Ascii string
2662  *  iLen     [I] Length of lpDst in characters
2663  *
2664  * RETURNS
2665  *  TRUE, since Ascii strings always convert identically.
2666  */
2667 BOOL WINAPI DoesStringRoundTripA(LPCSTR lpSrcStr, LPSTR lpDst, INT iLen)
2668 {
2669   lstrcpynA(lpDst, lpSrcStr, iLen);
2670   return TRUE;
2671 }
2672
2673 /*************************************************************************
2674  *      @       [SHLWAPI.365]
2675  *
2676  * Determine if a Unicode string converts to Ascii and back identically.
2677  *
2678  * PARAMS
2679  *  lpSrcStr [I] Source Unicode string to convert
2680  *  lpDst    [O] Destination for resulting Ascii string
2681  *  iLen     [I] Length of lpDst in characters
2682  *
2683  * RETURNS
2684  *  TRUE, if lpSrcStr converts to Ascii and back identically,
2685  *  FALSE otherwise.
2686  */
2687 BOOL WINAPI DoesStringRoundTripW(LPCWSTR lpSrcStr, LPSTR lpDst, INT iLen)
2688 {
2689     WCHAR szBuff[MAX_PATH];
2690
2691     SHUnicodeToAnsi(lpSrcStr, lpDst, iLen);
2692     SHAnsiToUnicode(lpDst, szBuff, MAX_PATH);
2693     return !strcmpW(lpSrcStr, szBuff);
2694 }
2695
2696 /*************************************************************************
2697  *      SHLoadIndirectString    [SHLWAPI.@]
2698  *
2699  * If passed a string that begins with '@', extract the string from the
2700  * appropriate resource, otherwise do a straight copy.
2701  *
2702  */
2703 HRESULT WINAPI SHLoadIndirectString(LPCWSTR src, LPWSTR dst, UINT dst_len, void **reserved)
2704 {
2705     WCHAR *dllname = NULL;
2706     HMODULE hmod = NULL;
2707     HRESULT hr = E_FAIL;
2708
2709     TRACE("(%s %p %08x %p)\n", debugstr_w(src), dst, dst_len, reserved);
2710
2711     if(src[0] == '@')
2712     {
2713         WCHAR *index_str;
2714         int index;
2715
2716         dst[0] = 0;
2717         dllname = StrDupW(src + 1);
2718         index_str = strchrW(dllname, ',');
2719
2720         if(!index_str) goto end;
2721
2722         *index_str = 0;
2723         index_str++;
2724         index = atoiW(index_str);
2725   
2726         hmod = LoadLibraryW(dllname);
2727         if(!hmod) goto end;
2728
2729         if(index < 0)
2730         {
2731             if(LoadStringW(hmod, -index, dst, dst_len))
2732                 hr = S_OK;
2733         }
2734         else
2735             FIXME("can't handle non-negative indices (%d)\n", index);
2736     }
2737     else
2738     {
2739         if(dst != src)
2740             lstrcpynW(dst, src, dst_len);
2741         hr = S_OK;
2742     }
2743
2744     TRACE("returning %s\n", debugstr_w(dst));
2745 end:
2746     if(hmod) FreeLibrary(hmod);
2747     HeapFree(GetProcessHeap(), 0, dllname);
2748     return hr;
2749 }