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