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