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