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