xref: /reactos/dll/win32/shlwapi/string.c (revision 190b3da9)
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 #ifdef __REACTOS__
1072   if (!lpszStr)
1073     return NULL;
1074 #endif
1075   iLen = lpszStr ? strlen(lpszStr) + 1 : 1;
1076   lpszRet = LocalAlloc(LMEM_FIXED, iLen);
1077 
1078   if (lpszRet)
1079   {
1080     if (lpszStr)
1081       memcpy(lpszRet, lpszStr, iLen);
1082     else
1083       *lpszRet = '\0';
1084   }
1085   return lpszRet;
1086 }
1087 
1088 /*************************************************************************
1089  * StrDupW	[SHLWAPI.@]
1090  *
1091  * See StrDupA.
1092  */
1093 LPWSTR WINAPI StrDupW(LPCWSTR lpszStr)
1094 {
1095   int iLen;
1096   LPWSTR lpszRet;
1097 
1098   TRACE("(%s)\n",debugstr_w(lpszStr));
1099 
1100 #ifdef __REACTOS__
1101   if (!lpszStr)
1102     return NULL;
1103 #endif
1104   iLen = (lpszStr ? strlenW(lpszStr) + 1 : 1) * sizeof(WCHAR);
1105   lpszRet = LocalAlloc(LMEM_FIXED, iLen);
1106 
1107   if (lpszRet)
1108   {
1109     if (lpszStr)
1110       memcpy(lpszRet, lpszStr, iLen);
1111     else
1112       *lpszRet = '\0';
1113   }
1114   return lpszRet;
1115 }
1116 
1117 /*************************************************************************
1118  * SHLWAPI_StrSpnHelperA
1119  *
1120  * Internal implementation of StrSpnA/StrCSpnA/StrCSpnIA
1121  */
1122 static int SHLWAPI_StrSpnHelperA(LPCSTR lpszStr, LPCSTR lpszMatch,
1123                                  LPSTR (WINAPI *pStrChrFn)(LPCSTR,WORD),
1124                                  BOOL bInvert)
1125 {
1126   LPCSTR lpszRead = lpszStr;
1127   if (lpszStr && *lpszStr && lpszMatch)
1128   {
1129     while (*lpszRead)
1130     {
1131       LPCSTR lpszTest = pStrChrFn(lpszMatch, *lpszRead);
1132 
1133       if (!bInvert && !lpszTest)
1134         break;
1135       if (bInvert && lpszTest)
1136         break;
1137       lpszRead = CharNextA(lpszRead);
1138     };
1139   }
1140   return lpszRead - lpszStr;
1141 }
1142 
1143 /*************************************************************************
1144  * StrSpnA	[SHLWAPI.@]
1145  *
1146  * Find the length of the start of a string that contains only certain
1147  * characters.
1148  *
1149  * PARAMS
1150  *  lpszStr   [I] String to search
1151  *  lpszMatch [I] Characters that can be in the substring
1152  *
1153  * RETURNS
1154  *  The length of the part of lpszStr containing only chars from lpszMatch,
1155  *  or 0 if any parameter is invalid.
1156  */
1157 int WINAPI StrSpnA(LPCSTR lpszStr, LPCSTR lpszMatch)
1158 {
1159   TRACE("(%s,%s)\n",debugstr_a(lpszStr), debugstr_a(lpszMatch));
1160 
1161   return SHLWAPI_StrSpnHelperA(lpszStr, lpszMatch, StrChrA, FALSE);
1162 }
1163 
1164 /*************************************************************************
1165  * StrSpnW	[SHLWAPI.@]
1166  *
1167  * See StrSpnA.
1168  */
1169 int WINAPI StrSpnW(LPCWSTR lpszStr, LPCWSTR lpszMatch)
1170 {
1171     if (!lpszStr || !lpszMatch) return 0;
1172     return strspnW( lpszStr, lpszMatch );
1173 }
1174 
1175 /*************************************************************************
1176  * StrCSpnA	[SHLWAPI.@]
1177  *
1178  * Find the length of the start of a string that does not contain certain
1179  * characters.
1180  *
1181  * PARAMS
1182  *  lpszStr   [I] String to search
1183  *  lpszMatch [I] Characters that cannot be in the substring
1184  *
1185  * RETURNS
1186  *  The length of the part of lpszStr containing only chars not in lpszMatch,
1187  *  or 0 if any parameter is invalid.
1188  */
1189 int WINAPI StrCSpnA(LPCSTR lpszStr, LPCSTR lpszMatch)
1190 {
1191   TRACE("(%s,%s)\n",debugstr_a(lpszStr), debugstr_a(lpszMatch));
1192 
1193   return SHLWAPI_StrSpnHelperA(lpszStr, lpszMatch, StrChrA, TRUE);
1194 }
1195 
1196 /*************************************************************************
1197  * StrCSpnW	[SHLWAPI.@]
1198  *
1199  * See StrCSpnA.
1200  */
1201 int WINAPI StrCSpnW(LPCWSTR lpszStr, LPCWSTR lpszMatch)
1202 {
1203     if (!lpszStr || !lpszMatch) return 0;
1204     return strcspnW( lpszStr, lpszMatch );
1205 }
1206 
1207 /*************************************************************************
1208  * StrCSpnIA	[SHLWAPI.@]
1209  *
1210  * Find the length of the start of a string that does not contain certain
1211  * characters, ignoring case.
1212  *
1213  * PARAMS
1214  *  lpszStr   [I] String to search
1215  *  lpszMatch [I] Characters that cannot be in the substring
1216  *
1217  * RETURNS
1218  *  The length of the part of lpszStr containing only chars not in lpszMatch,
1219  *  or 0 if any parameter is invalid.
1220  */
1221 int WINAPI StrCSpnIA(LPCSTR lpszStr, LPCSTR lpszMatch)
1222 {
1223   TRACE("(%s,%s)\n",debugstr_a(lpszStr), debugstr_a(lpszMatch));
1224 
1225   return SHLWAPI_StrSpnHelperA(lpszStr, lpszMatch, StrChrIA, TRUE);
1226 }
1227 
1228 /*************************************************************************
1229  * StrCSpnIW	[SHLWAPI.@]
1230  *
1231  * See StrCSpnIA.
1232  */
1233 int WINAPI StrCSpnIW(LPCWSTR lpszStr, LPCWSTR lpszMatch)
1234 {
1235   LPCWSTR lpszRead = lpszStr;
1236 
1237   TRACE("(%s,%s)\n",debugstr_w(lpszStr), debugstr_w(lpszMatch));
1238 
1239   if (lpszStr && *lpszStr && lpszMatch)
1240   {
1241     while (*lpszRead)
1242     {
1243       if (StrChrIW(lpszMatch, *lpszRead)) break;
1244       lpszRead++;
1245     }
1246   }
1247   return lpszRead - lpszStr;
1248 }
1249 
1250 /*************************************************************************
1251  * StrPBrkA	[SHLWAPI.@]
1252  *
1253  * Search a string for any of a group of characters.
1254  *
1255  * PARAMS
1256  *  lpszStr   [I] String to search
1257  *  lpszMatch [I] Characters to match
1258  *
1259  * RETURNS
1260  *  A pointer to the first matching character in lpszStr, or NULL if no
1261  *  match was found.
1262  */
1263 LPSTR WINAPI StrPBrkA(LPCSTR lpszStr, LPCSTR lpszMatch)
1264 {
1265   TRACE("(%s,%s)\n",debugstr_a(lpszStr), debugstr_a(lpszMatch));
1266 
1267   if (lpszStr && lpszMatch && *lpszMatch)
1268   {
1269     while (*lpszStr)
1270     {
1271       if (StrChrA(lpszMatch, *lpszStr))
1272         return (LPSTR)lpszStr;
1273       lpszStr = CharNextA(lpszStr);
1274     }
1275   }
1276   return NULL;
1277 }
1278 
1279 /*************************************************************************
1280  * StrPBrkW	[SHLWAPI.@]
1281  *
1282  * See StrPBrkA.
1283  */
1284 LPWSTR WINAPI StrPBrkW(LPCWSTR lpszStr, LPCWSTR lpszMatch)
1285 {
1286     if (!lpszStr || !lpszMatch) return NULL;
1287     return strpbrkW( lpszStr, lpszMatch );
1288 }
1289 
1290 /*************************************************************************
1291  * SHLWAPI_StrRChrHelperA
1292  *
1293  * Internal implementation of StrRChrA/StrRChrIA.
1294  */
1295 static LPSTR SHLWAPI_StrRChrHelperA(LPCSTR lpszStr,
1296                                     LPCSTR lpszEnd, WORD ch,
1297                                     BOOL (WINAPI *pChrCmpFn)(WORD,WORD))
1298 {
1299   LPCSTR lpszRet = NULL;
1300 
1301   if (lpszStr)
1302   {
1303     WORD ch2;
1304 
1305     if (!lpszEnd)
1306       lpszEnd = lpszStr + lstrlenA(lpszStr);
1307 
1308     while (*lpszStr && lpszStr <= lpszEnd)
1309     {
1310       ch2 = IsDBCSLeadByte(*lpszStr)? *lpszStr << 8 | lpszStr[1] : *lpszStr;
1311 
1312       if (!pChrCmpFn(ch, ch2))
1313         lpszRet = lpszStr;
1314       lpszStr = CharNextA(lpszStr);
1315     }
1316   }
1317   return (LPSTR)lpszRet;
1318 }
1319 
1320 /**************************************************************************
1321  * StrRChrA	[SHLWAPI.@]
1322  *
1323  * Find the last occurrence of a character in string.
1324  *
1325  * PARAMS
1326  *  lpszStr [I] String to search in
1327  *  lpszEnd [I] Place to end search, or NULL to search until the end of lpszStr
1328  *  ch      [I] Character to search for.
1329  *
1330  * RETURNS
1331  *  Success: A pointer to the last occurrence of ch in lpszStr before lpszEnd,
1332  *           or NULL if not found.
1333  *  Failure: NULL, if any arguments are invalid.
1334  */
1335 LPSTR WINAPI StrRChrA(LPCSTR lpszStr, LPCSTR lpszEnd, WORD ch)
1336 {
1337   TRACE("(%s,%s,%x)\n", debugstr_a(lpszStr), debugstr_a(lpszEnd), ch);
1338 
1339   return SHLWAPI_StrRChrHelperA(lpszStr, lpszEnd, ch, SHLWAPI_ChrCmpA);
1340 }
1341 
1342 /**************************************************************************
1343  * StrRChrW	[SHLWAPI.@]
1344  *
1345  * See StrRChrA.
1346  */
1347 LPWSTR WINAPI StrRChrW(LPCWSTR str, LPCWSTR end, WORD ch)
1348 {
1349     WCHAR *ret = NULL;
1350 
1351     if (!str) return NULL;
1352     if (!end) end = str + strlenW(str);
1353     while (str < end)
1354     {
1355         if (*str == ch) ret = (WCHAR *)str;
1356         str++;
1357     }
1358     return ret;
1359 }
1360 
1361 /**************************************************************************
1362  * StrRChrIA	[SHLWAPI.@]
1363  *
1364  * Find the last occurrence of a character in string, ignoring case.
1365  *
1366  * PARAMS
1367  *  lpszStr [I] String to search in
1368  *  lpszEnd [I] Place to end search, or NULL to search until the end of lpszStr
1369  *  ch      [I] Character to search for.
1370  *
1371  * RETURNS
1372  *  Success: A pointer to the last occurrence of ch in lpszStr before lpszEnd,
1373  *           or NULL if not found.
1374  *  Failure: NULL, if any arguments are invalid.
1375  */
1376 LPSTR WINAPI StrRChrIA(LPCSTR lpszStr, LPCSTR lpszEnd, WORD ch)
1377 {
1378   TRACE("(%s,%s,%x)\n", debugstr_a(lpszStr), debugstr_a(lpszEnd), ch);
1379 
1380   return SHLWAPI_StrRChrHelperA(lpszStr, lpszEnd, ch, ChrCmpIA);
1381 }
1382 
1383 /**************************************************************************
1384  * StrRChrIW	[SHLWAPI.@]
1385  *
1386  * See StrRChrIA.
1387  */
1388 LPWSTR WINAPI StrRChrIW(LPCWSTR str, LPCWSTR end, WORD ch)
1389 {
1390     WCHAR *ret = NULL;
1391 
1392     if (!str) return NULL;
1393     if (!end) end = str + strlenW(str);
1394     while (str < end)
1395     {
1396         if (!ChrCmpIW(*str, ch)) ret = (WCHAR *)str;
1397         str++;
1398     }
1399     return ret;
1400 }
1401 
1402 /*************************************************************************
1403  * StrCatBuffA	[SHLWAPI.@]
1404  *
1405  * Concatenate two strings together.
1406  *
1407  * PARAMS
1408  *  lpszStr [O] String to concatenate to
1409  *  lpszCat [I] String to add to lpszCat
1410  *  cchMax  [I] Maximum number of characters for the whole string
1411  *
1412  * RETURNS
1413  *  lpszStr.
1414  *
1415  * NOTES
1416  *  cchMax determines the number of characters in the final length of the
1417  *  string, not the number appended to lpszStr from lpszCat.
1418  */
1419 LPSTR WINAPI StrCatBuffA(LPSTR lpszStr, LPCSTR lpszCat, INT cchMax)
1420 {
1421   INT iLen;
1422 
1423   TRACE("(%p,%s,%d)\n", lpszStr, debugstr_a(lpszCat), cchMax);
1424 
1425   if (!lpszStr)
1426   {
1427     WARN("Invalid lpszStr would crash under Win32!\n");
1428     return NULL;
1429   }
1430 
1431   iLen = strlen(lpszStr);
1432   cchMax -= iLen;
1433 
1434   if (cchMax > 0)
1435     StrCpyNA(lpszStr + iLen, lpszCat, cchMax);
1436   return lpszStr;
1437 }
1438 
1439 /*************************************************************************
1440  * StrCatBuffW	[SHLWAPI.@]
1441  *
1442  * See StrCatBuffA.
1443  */
1444 LPWSTR WINAPI StrCatBuffW(LPWSTR lpszStr, LPCWSTR lpszCat, INT cchMax)
1445 {
1446   INT iLen;
1447 
1448   TRACE("(%p,%s,%d)\n", lpszStr, debugstr_w(lpszCat), cchMax);
1449 
1450   if (!lpszStr)
1451   {
1452     WARN("Invalid lpszStr would crash under Win32!\n");
1453     return NULL;
1454   }
1455 
1456   iLen = strlenW(lpszStr);
1457   cchMax -= iLen;
1458 
1459   if (cchMax > 0)
1460     StrCpyNW(lpszStr + iLen, lpszCat, cchMax);
1461   return lpszStr;
1462 }
1463 
1464 /*************************************************************************
1465  * StrRetToBufA					[SHLWAPI.@]
1466  *
1467  * Convert a STRRET to a normal string.
1468  *
1469  * PARAMS
1470  *  lpStrRet [O] STRRET to convert
1471  *  pIdl     [I] ITEMIDLIST for lpStrRet->uType == STRRET_OFFSET
1472  *  lpszDest [O] Destination for normal string
1473  *  dwLen    [I] Length of lpszDest
1474  *
1475  * RETURNS
1476  *  Success: S_OK. lpszDest contains up to dwLen characters of the string.
1477  *           If lpStrRet is of type STRRET_WSTR, its memory is freed with
1478  *           CoTaskMemFree() and its type set to STRRET_CSTRA.
1479  *  Failure: E_FAIL, if any parameters are invalid.
1480  */
1481 HRESULT WINAPI StrRetToBufA (LPSTRRET src, const ITEMIDLIST *pidl, LPSTR dest, UINT len)
1482 {
1483 	/* NOTE:
1484 	 *  This routine is identical to that in dlls/shell32/shellstring.c.
1485 	 *  It was duplicated because not every version of Shlwapi.dll exports
1486 	 *  StrRetToBufA. If you change one routine, change them both.
1487 	 */
1488         TRACE("dest=%p len=0x%x strret=%p pidl=%p\n", dest, len, src, pidl);
1489 
1490 	if (!src)
1491 	{
1492 	  WARN("Invalid lpStrRet would crash under Win32!\n");
1493 	  if (dest)
1494 	    *dest = '\0';
1495 	  return E_FAIL;
1496 	}
1497 
1498 	if (!dest || !len)
1499 	  return E_FAIL;
1500 
1501 	*dest = '\0';
1502 
1503 	switch (src->uType)
1504 	{
1505 	  case STRRET_WSTR:
1506 	    WideCharToMultiByte(CP_ACP, 0, src->u.pOleStr, -1, dest, len, NULL, NULL);
1507 	    CoTaskMemFree(src->u.pOleStr);
1508 	    break;
1509 
1510 	  case STRRET_CSTR:
1511             lstrcpynA(dest, src->u.cStr, len);
1512 	    break;
1513 
1514 	  case STRRET_OFFSET:
1515 	    lstrcpynA((LPSTR)dest, ((LPCSTR)&pidl->mkid)+src->u.uOffset, len);
1516 	    break;
1517 
1518 	  default:
1519 	    FIXME("unknown type!\n");
1520 	    return E_NOTIMPL;
1521 	}
1522 	return S_OK;
1523 }
1524 
1525 /*************************************************************************
1526  * StrRetToBufW	[SHLWAPI.@]
1527  *
1528  * See StrRetToBufA.
1529  */
1530 HRESULT WINAPI StrRetToBufW (LPSTRRET src, const ITEMIDLIST *pidl, LPWSTR dest, UINT len)
1531 {
1532     TRACE("dest=%p len=0x%x strret=%p pidl=%p\n", dest, len, src, pidl);
1533 
1534     if (!dest || !len)
1535         return E_FAIL;
1536 
1537     if (!src)
1538     {
1539         WARN("Invalid lpStrRet would crash under Win32!\n");
1540         if (dest)
1541             *dest = '\0';
1542         return E_FAIL;
1543     }
1544 
1545     *dest = '\0';
1546 
1547     switch (src->uType) {
1548     case STRRET_WSTR: {
1549         size_t dst_len;
1550         if (!src->u.pOleStr)
1551             return E_FAIL;
1552         dst_len = strlenW(src->u.pOleStr);
1553         memcpy(dest, src->u.pOleStr, min(dst_len, len-1) * sizeof(WCHAR));
1554         dest[min(dst_len, len-1)] = 0;
1555         CoTaskMemFree(src->u.pOleStr);
1556         if (len <= dst_len)
1557         {
1558             dest[0] = 0;
1559             return E_NOT_SUFFICIENT_BUFFER;
1560         }
1561         break;
1562     }
1563 
1564     case STRRET_CSTR:
1565         if (!MultiByteToWideChar( CP_ACP, 0, src->u.cStr, -1, dest, len ))
1566             dest[len-1] = 0;
1567         break;
1568 
1569     case STRRET_OFFSET:
1570         if (pidl)
1571 	{
1572             if (!MultiByteToWideChar( CP_ACP, 0, ((LPCSTR)&pidl->mkid)+src->u.uOffset, -1,
1573                                       dest, len ))
1574                 dest[len-1] = 0;
1575         }
1576         break;
1577 
1578     default:
1579         FIXME("unknown type!\n");
1580         return E_NOTIMPL;
1581     }
1582 
1583     return S_OK;
1584 }
1585 
1586 /*************************************************************************
1587  * StrRetToStrA					[SHLWAPI.@]
1588  *
1589  * Converts a STRRET to a normal string.
1590  *
1591  * PARAMS
1592  *  lpStrRet [O] STRRET to convert
1593  *  pidl     [I] ITEMIDLIST for lpStrRet->uType == STRRET_OFFSET
1594  *  ppszName [O] Destination for converted string
1595  *
1596  * RETURNS
1597  *  Success: S_OK. ppszName contains the new string, allocated with CoTaskMemAlloc().
1598  *  Failure: E_FAIL, if any parameters are invalid.
1599  */
1600 HRESULT WINAPI StrRetToStrA(LPSTRRET lpStrRet, const ITEMIDLIST *pidl, LPSTR *ppszName)
1601 {
1602   HRESULT hRet = E_FAIL;
1603 
1604   switch (lpStrRet->uType)
1605   {
1606   case STRRET_WSTR:
1607     hRet = _SHStrDupAW(lpStrRet->u.pOleStr, ppszName);
1608     CoTaskMemFree(lpStrRet->u.pOleStr);
1609     break;
1610 
1611   case STRRET_CSTR:
1612     hRet = _SHStrDupAA(lpStrRet->u.cStr, ppszName);
1613     break;
1614 
1615   case STRRET_OFFSET:
1616     hRet = _SHStrDupAA(((LPCSTR)&pidl->mkid) + lpStrRet->u.uOffset, ppszName);
1617     break;
1618 
1619   default:
1620     *ppszName = NULL;
1621   }
1622 
1623   return hRet;
1624 }
1625 
1626 /*************************************************************************
1627  * StrRetToStrW					[SHLWAPI.@]
1628  *
1629  * See StrRetToStrA.
1630  */
1631 HRESULT WINAPI StrRetToStrW(LPSTRRET lpStrRet, const ITEMIDLIST *pidl, LPWSTR *ppszName)
1632 {
1633   HRESULT hRet = E_FAIL;
1634 
1635   switch (lpStrRet->uType)
1636   {
1637   case STRRET_WSTR:
1638     hRet = SHStrDupW(lpStrRet->u.pOleStr, ppszName);
1639     CoTaskMemFree(lpStrRet->u.pOleStr);
1640     break;
1641 
1642   case STRRET_CSTR:
1643     hRet = SHStrDupA(lpStrRet->u.cStr, ppszName);
1644     break;
1645 
1646   case STRRET_OFFSET:
1647     hRet = SHStrDupA(((LPCSTR)&pidl->mkid) + lpStrRet->u.uOffset, ppszName);
1648     break;
1649 
1650   default:
1651     *ppszName = NULL;
1652   }
1653 
1654   return hRet;
1655 }
1656 
1657 /* Create an ASCII string copy using SysAllocString() */
1658 static HRESULT _SHStrDupAToBSTR(LPCSTR src, BSTR *pBstrOut)
1659 {
1660     *pBstrOut = NULL;
1661 
1662     if (src)
1663     {
1664         INT len = MultiByteToWideChar(CP_ACP, 0, src, -1, NULL, 0);
1665         WCHAR* szTemp = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1666 
1667         if (szTemp)
1668         {
1669             MultiByteToWideChar(CP_ACP, 0, src, -1, szTemp, len);
1670             *pBstrOut = SysAllocString(szTemp);
1671             HeapFree(GetProcessHeap(), 0, szTemp);
1672 
1673             if (*pBstrOut)
1674                 return S_OK;
1675         }
1676     }
1677     return E_OUTOFMEMORY;
1678 }
1679 
1680 /*************************************************************************
1681  * StrRetToBSTR	[SHLWAPI.@]
1682  *
1683  * Converts a STRRET to a BSTR.
1684  *
1685  * PARAMS
1686  *  lpStrRet [O] STRRET to convert
1687  *  pidl     [I] ITEMIDLIST for lpStrRet->uType = STRRET_OFFSET
1688  *  pBstrOut [O] Destination for converted BSTR
1689  *
1690  * RETURNS
1691  *  Success: S_OK. pBstrOut contains the new string.
1692  *  Failure: E_FAIL, if any parameters are invalid.
1693  */
1694 HRESULT WINAPI StrRetToBSTR(STRRET *lpStrRet, LPCITEMIDLIST pidl, BSTR* pBstrOut)
1695 {
1696   HRESULT hRet = E_FAIL;
1697 
1698   switch (lpStrRet->uType)
1699   {
1700   case STRRET_WSTR:
1701     *pBstrOut = SysAllocString(lpStrRet->u.pOleStr);
1702     if (*pBstrOut)
1703       hRet = S_OK;
1704     CoTaskMemFree(lpStrRet->u.pOleStr);
1705     break;
1706 
1707   case STRRET_CSTR:
1708     hRet = _SHStrDupAToBSTR(lpStrRet->u.cStr, pBstrOut);
1709     break;
1710 
1711   case STRRET_OFFSET:
1712     hRet = _SHStrDupAToBSTR(((LPCSTR)&pidl->mkid) + lpStrRet->u.uOffset, pBstrOut);
1713     break;
1714 
1715   default:
1716     *pBstrOut = NULL;
1717   }
1718 
1719   return hRet;
1720 }
1721 
1722 /*************************************************************************
1723  * StrFormatKBSizeA	[SHLWAPI.@]
1724  *
1725  * Create a formatted string containing a byte count in Kilobytes.
1726  *
1727  * PARAMS
1728  *  llBytes  [I] Byte size to format
1729  *  lpszDest [I] Destination for formatted string
1730  *  cchMax   [I] Size of lpszDest
1731  *
1732  * RETURNS
1733  *  lpszDest.
1734  */
1735 LPSTR WINAPI StrFormatKBSizeA(LONGLONG llBytes, LPSTR lpszDest, UINT cchMax)
1736 {
1737   WCHAR wszBuf[256];
1738 
1739   if (!StrFormatKBSizeW(llBytes, wszBuf, 256))
1740     return NULL;
1741   if (!WideCharToMultiByte(CP_ACP, 0, wszBuf, -1, lpszDest, cchMax, NULL, NULL))
1742     return NULL;
1743   return lpszDest;
1744 }
1745 
1746 /*************************************************************************
1747  * StrFormatKBSizeW	[SHLWAPI.@]
1748  *
1749  * See StrFormatKBSizeA.
1750  */
1751 LPWSTR WINAPI StrFormatKBSizeW(LONGLONG llBytes, LPWSTR lpszDest, UINT cchMax)
1752 {
1753   static const WCHAR kb[] = {' ','K','B',0};
1754   LONGLONG llKB = (llBytes + 1023) >> 10;
1755   int len;
1756 
1757   TRACE("(0x%s,%p,%d)\n", wine_dbgstr_longlong(llBytes), lpszDest, cchMax);
1758 
1759   if (!FormatInt(llKB, lpszDest, cchMax))
1760     return NULL;
1761 
1762   len = lstrlenW(lpszDest);
1763   if (cchMax - len < 4)
1764       return NULL;
1765   lstrcatW(lpszDest, kb);
1766   return lpszDest;
1767 }
1768 
1769 /*************************************************************************
1770  * StrNCatA	[SHLWAPI.@]
1771  *
1772  * Concatenate two strings together.
1773  *
1774  * PARAMS
1775  *  lpszStr [O] String to concatenate to
1776  *  lpszCat [I] String to add to lpszCat
1777  *  cchMax  [I] Maximum number of characters to concatenate
1778  *
1779  * RETURNS
1780  *  lpszStr.
1781  *
1782  * NOTES
1783  *  cchMax determines the number of characters that are appended to lpszStr,
1784  *  not the total length of the string.
1785  */
1786 LPSTR WINAPI StrNCatA(LPSTR lpszStr, LPCSTR lpszCat, INT cchMax)
1787 {
1788   LPSTR lpszRet = lpszStr;
1789 
1790   TRACE("(%s,%s,%i)\n", debugstr_a(lpszStr), debugstr_a(lpszCat), cchMax);
1791 
1792   if (!lpszStr)
1793   {
1794     WARN("Invalid lpszStr would crash under Win32!\n");
1795     return NULL;
1796   }
1797 
1798   StrCpyNA(lpszStr + strlen(lpszStr), lpszCat, cchMax);
1799   return lpszRet;
1800 }
1801 
1802 /*************************************************************************
1803  * StrNCatW	[SHLWAPI.@]
1804  *
1805  * See StrNCatA.
1806  */
1807 LPWSTR WINAPI StrNCatW(LPWSTR lpszStr, LPCWSTR lpszCat, INT cchMax)
1808 {
1809   LPWSTR lpszRet = lpszStr;
1810 
1811   TRACE("(%s,%s,%i)\n", debugstr_w(lpszStr), debugstr_w(lpszCat), cchMax);
1812 
1813   if (!lpszStr)
1814   {
1815     WARN("Invalid lpszStr would crash under Win32\n");
1816     return NULL;
1817   }
1818 
1819   StrCpyNW(lpszStr + strlenW(lpszStr), lpszCat, cchMax);
1820   return lpszRet;
1821 }
1822 
1823 /*************************************************************************
1824  * StrTrimA	[SHLWAPI.@]
1825  *
1826  * Remove characters from the start and end of a string.
1827  *
1828  * PARAMS
1829  *  lpszStr  [O] String to remove characters from
1830  *  lpszTrim [I] Characters to remove from lpszStr
1831  *
1832  * RETURNS
1833  *  TRUE  If lpszStr was valid and modified
1834  *  FALSE Otherwise
1835  */
1836 BOOL WINAPI StrTrimA(LPSTR lpszStr, LPCSTR lpszTrim)
1837 {
1838   DWORD dwLen;
1839   LPSTR lpszRead = lpszStr;
1840   BOOL bRet = FALSE;
1841 
1842   TRACE("(%s,%s)\n", debugstr_a(lpszStr), debugstr_a(lpszTrim));
1843 
1844   if (lpszRead && *lpszRead)
1845   {
1846     while (*lpszRead && StrChrA(lpszTrim, *lpszRead))
1847       lpszRead = CharNextA(lpszRead); /* Skip leading matches */
1848 
1849     dwLen = strlen(lpszRead);
1850 
1851     if (lpszRead != lpszStr)
1852     {
1853       memmove(lpszStr, lpszRead, dwLen + 1);
1854       bRet = TRUE;
1855     }
1856     if (dwLen > 0)
1857     {
1858       lpszRead = lpszStr + dwLen;
1859       while (StrChrA(lpszTrim, lpszRead[-1]))
1860         lpszRead = CharPrevA(lpszStr, lpszRead); /* Skip trailing matches */
1861 
1862       if (lpszRead != lpszStr + dwLen)
1863       {
1864         *lpszRead = '\0';
1865         bRet = TRUE;
1866       }
1867     }
1868   }
1869   return bRet;
1870 }
1871 
1872 /*************************************************************************
1873  * StrTrimW	[SHLWAPI.@]
1874  *
1875  * See StrTrimA.
1876  */
1877 BOOL WINAPI StrTrimW(LPWSTR lpszStr, LPCWSTR lpszTrim)
1878 {
1879   DWORD dwLen;
1880   LPWSTR lpszRead = lpszStr;
1881   BOOL bRet = FALSE;
1882 
1883   TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszTrim));
1884 
1885   if (lpszRead && *lpszRead)
1886   {
1887     while (*lpszRead && StrChrW(lpszTrim, *lpszRead)) lpszRead++;
1888 
1889     dwLen = strlenW(lpszRead);
1890 
1891     if (lpszRead != lpszStr)
1892     {
1893       memmove(lpszStr, lpszRead, (dwLen + 1) * sizeof(WCHAR));
1894       bRet = TRUE;
1895     }
1896     if (dwLen > 0)
1897     {
1898       lpszRead = lpszStr + dwLen;
1899       while (StrChrW(lpszTrim, lpszRead[-1]))
1900         lpszRead--; /* Skip trailing matches */
1901 
1902       if (lpszRead != lpszStr + dwLen)
1903       {
1904         *lpszRead = '\0';
1905         bRet = TRUE;
1906       }
1907     }
1908   }
1909   return bRet;
1910 }
1911 
1912 /*************************************************************************
1913  *      _SHStrDupAA	[INTERNAL]
1914  *
1915  * Duplicates a ASCII string to ASCII. The destination buffer is allocated.
1916  */
1917 static HRESULT _SHStrDupAA(LPCSTR src, LPSTR * dest)
1918 {
1919 	HRESULT hr;
1920 	int len = 0;
1921 
1922 	if (src) {
1923 	    len = lstrlenA(src) + 1;
1924 	    *dest = CoTaskMemAlloc(len);
1925 	} else {
1926 	    *dest = NULL;
1927 	}
1928 
1929 	if (*dest) {
1930 	    lstrcpynA(*dest,src, len);
1931 	    hr = S_OK;
1932 	} else {
1933 	    hr = E_OUTOFMEMORY;
1934 	}
1935 
1936 	TRACE("%s->(%p)\n", debugstr_a(src), *dest);
1937 	return hr;
1938 }
1939 
1940 /*************************************************************************
1941  * SHStrDupA	[SHLWAPI.@]
1942  *
1943  * Return a Unicode copy of a string, in memory allocated by CoTaskMemAlloc().
1944  *
1945  * PARAMS
1946  *  lpszStr   [I] String to copy
1947  *  lppszDest [O] Destination for the new string copy
1948  *
1949  * RETURNS
1950  *  Success: S_OK. lppszDest contains the new string in Unicode format.
1951  *  Failure: E_OUTOFMEMORY, If any arguments are invalid or memory allocation
1952  *           fails.
1953  */
1954 HRESULT WINAPI SHStrDupA(LPCSTR lpszStr, LPWSTR * lppszDest)
1955 {
1956   HRESULT hRet;
1957   int len = 0;
1958 
1959   if (lpszStr)
1960   {
1961     len = MultiByteToWideChar(CP_ACP, 0, lpszStr, -1, NULL, 0) * sizeof(WCHAR);
1962     *lppszDest = CoTaskMemAlloc(len);
1963   }
1964   else
1965     *lppszDest = NULL;
1966 
1967   if (*lppszDest)
1968   {
1969     MultiByteToWideChar(CP_ACP, 0, lpszStr, -1, *lppszDest, len/sizeof(WCHAR));
1970     hRet = S_OK;
1971   }
1972   else
1973     hRet = E_OUTOFMEMORY;
1974 
1975   TRACE("%s->(%p)\n", debugstr_a(lpszStr), *lppszDest);
1976   return hRet;
1977 }
1978 
1979 /*************************************************************************
1980  *      _SHStrDupAW	[INTERNAL]
1981  *
1982  * Duplicates a UNICODE to a ASCII string. The destination buffer is allocated.
1983  */
1984 static HRESULT _SHStrDupAW(LPCWSTR src, LPSTR * dest)
1985 {
1986 	HRESULT hr;
1987 	int len = 0;
1988 
1989 	if (src) {
1990 	    len = WideCharToMultiByte(CP_ACP, 0, src, -1, NULL, 0, NULL, NULL);
1991 	    *dest = CoTaskMemAlloc(len);
1992 	} else {
1993 	    *dest = NULL;
1994 	}
1995 
1996 	if (*dest) {
1997 	    WideCharToMultiByte(CP_ACP, 0, src, -1, *dest, len, NULL, NULL);
1998 	    hr = S_OK;
1999 	} else {
2000 	    hr = E_OUTOFMEMORY;
2001 	}
2002 
2003 	TRACE("%s->(%p)\n", debugstr_w(src), *dest);
2004 	return hr;
2005 }
2006 
2007 /*************************************************************************
2008  * SHStrDupW	[SHLWAPI.@]
2009  *
2010  * See SHStrDupA.
2011  */
2012 HRESULT WINAPI SHStrDupW(LPCWSTR src, LPWSTR * dest)
2013 {
2014 	HRESULT hr;
2015 	int len = 0;
2016 
2017 	if (src) {
2018 	    len = (lstrlenW(src) + 1) * sizeof(WCHAR);
2019 	    *dest = CoTaskMemAlloc(len);
2020 	} else {
2021 	    *dest = NULL;
2022 	}
2023 
2024 	if (*dest) {
2025 	    memcpy(*dest, src, len);
2026 	    hr = S_OK;
2027 	} else {
2028 	    hr = E_OUTOFMEMORY;
2029 	}
2030 
2031 	TRACE("%s->(%p)\n", debugstr_w(src), *dest);
2032 	return hr;
2033 }
2034 
2035 /*************************************************************************
2036  * SHLWAPI_WriteReverseNum
2037  *
2038  * Internal helper for SHLWAPI_WriteTimeClass.
2039  */
2040 static inline LPWSTR SHLWAPI_WriteReverseNum(LPWSTR lpszOut, DWORD dwNum)
2041 {
2042   *lpszOut-- = '\0';
2043 
2044   /* Write a decimal number to a string, backwards */
2045   do
2046   {
2047     DWORD dwNextDigit = dwNum % 10;
2048     *lpszOut-- = '0' + dwNextDigit;
2049     dwNum = (dwNum - dwNextDigit) / 10;
2050   } while (dwNum > 0);
2051 
2052   return lpszOut;
2053 }
2054 
2055 /*************************************************************************
2056  * SHLWAPI_FormatSignificant
2057  *
2058  * Internal helper for SHLWAPI_WriteTimeClass.
2059  */
2060 static inline int SHLWAPI_FormatSignificant(LPWSTR lpszNum, int dwDigits)
2061 {
2062   /* Zero non significant digits, return remaining significant digits */
2063   while (*lpszNum)
2064   {
2065     lpszNum++;
2066     if (--dwDigits == 0)
2067     {
2068       while (*lpszNum)
2069         *lpszNum++ = '0';
2070       return 0;
2071     }
2072   }
2073   return dwDigits;
2074 }
2075 
2076 /*************************************************************************
2077  * SHLWAPI_WriteTimeClass
2078  *
2079  * Internal helper for StrFromTimeIntervalW.
2080  */
2081 static int SHLWAPI_WriteTimeClass(LPWSTR lpszOut, DWORD dwValue,
2082                                   UINT uClassStringId, int iDigits)
2083 {
2084   WCHAR szBuff[64], *szOut = szBuff + 32;
2085 
2086   szOut = SHLWAPI_WriteReverseNum(szOut, dwValue);
2087   iDigits = SHLWAPI_FormatSignificant(szOut + 1, iDigits);
2088   *szOut = ' ';
2089   LoadStringW(shlwapi_hInstance, uClassStringId, szBuff + 32, 32);
2090   strcatW(lpszOut, szOut);
2091   return iDigits;
2092 }
2093 
2094 /*************************************************************************
2095  * StrFromTimeIntervalA	[SHLWAPI.@]
2096  *
2097  * Format a millisecond time interval into a string
2098  *
2099  * PARAMS
2100  *  lpszStr  [O] Output buffer for formatted time interval
2101  *  cchMax   [I] Size of lpszStr
2102  *  dwMS     [I] Number of milliseconds
2103  *  iDigits  [I] Number of digits to print
2104  *
2105  * RETURNS
2106  *  The length of the formatted string, or 0 if any parameter is invalid.
2107  *
2108  * NOTES
2109  *  This implementation mimics the Win32 behaviour of always writing a leading
2110  *  space before the time interval begins.
2111  *
2112  *  iDigits is used to provide approximate times if accuracy is not important.
2113  *  This number of digits will be written of the first non-zero time class
2114  *  (hours/minutes/seconds). If this does not complete the time classification,
2115  *  the remaining digits are changed to zeros (i.e. The time is _not_ rounded).
2116  *  If there are digits remaining following the writing of a time class, the
2117  *  next time class will be written.
2118  *
2119  *  For example, given dwMS represents 138 hours,43 minutes and 15 seconds, the
2120  *  following will result from the given values of iDigits:
2121  *
2122  *|  iDigits    1        2        3        4               5               ...
2123  *|  lpszStr   "100 hr" "130 hr" "138 hr" "138 hr 40 min" "138 hr 43 min"  ...
2124  */
2125 INT WINAPI StrFromTimeIntervalA(LPSTR lpszStr, UINT cchMax, DWORD dwMS,
2126                                 int iDigits)
2127 {
2128   INT iRet = 0;
2129 
2130   TRACE("(%p,%d,%d,%d)\n", lpszStr, cchMax, dwMS, iDigits);
2131 
2132   if (lpszStr && cchMax)
2133   {
2134     WCHAR szBuff[128];
2135     StrFromTimeIntervalW(szBuff, sizeof(szBuff)/sizeof(WCHAR), dwMS, iDigits);
2136     WideCharToMultiByte(CP_ACP,0,szBuff,-1,lpszStr,cchMax,0,0);
2137   }
2138   return iRet;
2139 }
2140 
2141 
2142 /*************************************************************************
2143  * StrFromTimeIntervalW	[SHLWAPI.@]
2144  *
2145  * See StrFromTimeIntervalA.
2146  */
2147 INT WINAPI StrFromTimeIntervalW(LPWSTR lpszStr, UINT cchMax, DWORD dwMS,
2148                                 int iDigits)
2149 {
2150   INT iRet = 0;
2151 
2152   TRACE("(%p,%d,%d,%d)\n", lpszStr, cchMax, dwMS, iDigits);
2153 
2154   if (lpszStr && cchMax)
2155   {
2156     WCHAR szCopy[128];
2157     DWORD dwHours, dwMinutes;
2158 
2159     if (!iDigits || cchMax == 1)
2160     {
2161       *lpszStr = '\0';
2162       return 0;
2163     }
2164 
2165     /* Calculate the time classes */
2166     dwMS = (dwMS + 500) / 1000;
2167     dwHours = dwMS / 3600;
2168     dwMS -= dwHours * 3600;
2169     dwMinutes = dwMS / 60;
2170     dwMS -= dwMinutes * 60;
2171 
2172     szCopy[0] = '\0';
2173 
2174     if (dwHours)
2175       iDigits = SHLWAPI_WriteTimeClass(szCopy, dwHours, IDS_TIME_INTERVAL_HOURS, iDigits);
2176 
2177     if (dwMinutes && iDigits)
2178       iDigits = SHLWAPI_WriteTimeClass(szCopy, dwMinutes, IDS_TIME_INTERVAL_MINUTES, iDigits);
2179 
2180     if (iDigits) /* Always write seconds if we have significant digits */
2181       SHLWAPI_WriteTimeClass(szCopy, dwMS, IDS_TIME_INTERVAL_SECONDS, iDigits);
2182 
2183     lstrcpynW(lpszStr, szCopy, cchMax);
2184     iRet = strlenW(lpszStr);
2185   }
2186   return iRet;
2187 }
2188 
2189 /*************************************************************************
2190  * StrIsIntlEqualA	[SHLWAPI.@]
2191  *
2192  * Compare two strings.
2193  *
2194  * PARAMS
2195  *  bCase    [I] Whether to compare case sensitively
2196  *  lpszStr  [I] First string to compare
2197  *  lpszComp [I] Second string to compare
2198  *  iLen     [I] Length to compare
2199  *
2200  * RETURNS
2201  *  TRUE  If the strings are equal.
2202  *  FALSE Otherwise.
2203  */
2204 BOOL WINAPI StrIsIntlEqualA(BOOL bCase, LPCSTR lpszStr, LPCSTR lpszComp,
2205                             int iLen)
2206 {
2207   DWORD dwFlags;
2208 
2209   TRACE("(%d,%s,%s,%d)\n", bCase,
2210         debugstr_a(lpszStr), debugstr_a(lpszComp), iLen);
2211 
2212   /* FIXME: This flag is undocumented and unknown by our CompareString.
2213    *        We need a define for it.
2214    */
2215   dwFlags = 0x10000000;
2216   if (!bCase) dwFlags |= NORM_IGNORECASE;
2217 
2218   return (CompareStringA(GetThreadLocale(), dwFlags, lpszStr, iLen, lpszComp, iLen) == CSTR_EQUAL);
2219 }
2220 
2221 /*************************************************************************
2222  * StrIsIntlEqualW	[SHLWAPI.@]
2223  *
2224  * See StrIsIntlEqualA.
2225  */
2226 BOOL WINAPI StrIsIntlEqualW(BOOL bCase, LPCWSTR lpszStr, LPCWSTR lpszComp,
2227                             int iLen)
2228 {
2229   DWORD dwFlags;
2230 
2231   TRACE("(%d,%s,%s,%d)\n", bCase,
2232         debugstr_w(lpszStr),debugstr_w(lpszComp), iLen);
2233 
2234   /* FIXME: This flag is undocumented and unknown by our CompareString.
2235    *        We need a define for it.
2236    */
2237   dwFlags = 0x10000000;
2238   if (!bCase) dwFlags |= NORM_IGNORECASE;
2239 
2240   return (CompareStringW(GetThreadLocale(), dwFlags, lpszStr, iLen, lpszComp, iLen) == CSTR_EQUAL);
2241 }
2242 
2243 /*************************************************************************
2244  * @    [SHLWAPI.399]
2245  *
2246  * Copy a string to another string, up to a maximum number of characters.
2247  *
2248  * PARAMS
2249  *  lpszDest [O] Destination string
2250  *  lpszSrc  [I] Source string
2251  *  iLen     [I] Maximum number of chars to copy
2252  *
2253  * RETURNS
2254  *  Success: A pointer to the last character written to lpszDest.
2255  *  Failure: lpszDest, if any arguments are invalid.
2256  */
2257 LPSTR WINAPI StrCpyNXA(LPSTR lpszDest, LPCSTR lpszSrc, int iLen)
2258 {
2259   TRACE("(%p,%s,%i)\n", lpszDest, debugstr_a(lpszSrc), iLen);
2260 
2261   if (lpszDest && lpszSrc && iLen > 0)
2262   {
2263     while ((iLen-- > 1) && *lpszSrc)
2264       *lpszDest++ = *lpszSrc++;
2265     if (iLen >= 0)
2266      *lpszDest = '\0';
2267   }
2268   return lpszDest;
2269 }
2270 
2271 /*************************************************************************
2272  * @    [SHLWAPI.400]
2273  *
2274  * Unicode version of StrCpyNXA.
2275  */
2276 LPWSTR WINAPI StrCpyNXW(LPWSTR lpszDest, LPCWSTR lpszSrc, int iLen)
2277 {
2278   TRACE("(%p,%s,%i)\n", lpszDest, debugstr_w(lpszSrc), iLen);
2279 
2280   if (lpszDest && lpszSrc && iLen > 0)
2281   {
2282     while ((iLen-- > 1) && *lpszSrc)
2283       *lpszDest++ = *lpszSrc++;
2284     if (iLen >= 0)
2285      *lpszDest = '\0';
2286   }
2287   return lpszDest;
2288 }
2289 
2290 /*************************************************************************
2291  * StrCmpLogicalW	[SHLWAPI.@]
2292  *
2293  * Compare two strings, ignoring case and comparing digits as numbers.
2294  *
2295  * PARAMS
2296  *  lpszStr  [I] First string to compare
2297  *  lpszComp [I] Second string to compare
2298  *  iLen     [I] Length to compare
2299  *
2300  * RETURNS
2301  *  TRUE  If the strings are equal.
2302  *  FALSE Otherwise.
2303  */
2304 INT WINAPI StrCmpLogicalW(LPCWSTR lpszStr, LPCWSTR lpszComp)
2305 {
2306   INT iDiff;
2307 
2308   TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszComp));
2309 
2310   if (lpszStr && lpszComp)
2311   {
2312     while (*lpszStr)
2313     {
2314       if (!*lpszComp)
2315         return 1;
2316       else if (isdigitW(*lpszStr))
2317       {
2318         int iStr, iComp;
2319 
2320         if (!isdigitW(*lpszComp))
2321           return -1;
2322 
2323         /* Compare the numbers */
2324         StrToIntExW(lpszStr, 0, &iStr);
2325         StrToIntExW(lpszComp, 0, &iComp);
2326 
2327         if (iStr < iComp)
2328           return -1;
2329         else if (iStr > iComp)
2330           return 1;
2331 
2332         /* Skip */
2333         while (isdigitW(*lpszStr))
2334           lpszStr++;
2335         while (isdigitW(*lpszComp))
2336           lpszComp++;
2337       }
2338       else if (isdigitW(*lpszComp))
2339         return 1;
2340       else
2341       {
2342         iDiff = ChrCmpIW(*lpszStr,*lpszComp);
2343         if (iDiff > 0)
2344           return 1;
2345         else if (iDiff < 0)
2346           return -1;
2347 
2348         lpszStr++;
2349         lpszComp++;
2350       }
2351     }
2352     if (*lpszComp)
2353       return -1;
2354   }
2355   return 0;
2356 }
2357 
2358 /* Structure for formatting byte strings */
2359 typedef struct tagSHLWAPI_BYTEFORMATS
2360 {
2361   LONGLONG dLimit;
2362   double   dDivisor;
2363   double   dNormaliser;
2364   int      nDecimals;
2365 #ifdef __REACTOS__
2366   UINT     nFormatID;
2367 #else
2368   WCHAR     wPrefix;
2369 #endif
2370 } SHLWAPI_BYTEFORMATS;
2371 
2372 /*************************************************************************
2373  * StrFormatByteSizeW	[SHLWAPI.@]
2374  *
2375  * Create a string containing an abbreviated byte count of up to 2^63-1.
2376  *
2377  * PARAMS
2378  *  llBytes  [I] Byte size to format
2379  *  lpszDest [I] Destination for formatted string
2380  *  cchMax   [I] Size of lpszDest
2381  *
2382  * RETURNS
2383  *  lpszDest.
2384  *
2385  * NOTES
2386  *  There is no StrFormatByteSize64W function, it is called StrFormatByteSizeW().
2387  */
2388 LPWSTR WINAPI StrFormatByteSizeW(LONGLONG llBytes, LPWSTR lpszDest, UINT cchMax)
2389 {
2390 #define KB ((ULONGLONG)1024)
2391 #define MB (KB*KB)
2392 #define GB (KB*KB*KB)
2393 #define TB (KB*KB*KB*KB)
2394 #define PB (KB*KB*KB*KB*KB)
2395 
2396   static const SHLWAPI_BYTEFORMATS bfFormats[] =
2397   {
2398 #ifdef __REACTOS__
2399     { 10*KB, 10.24, 100.0, 2, IDS_KB_FORMAT }, /* 10 KB */
2400     { 100*KB, 102.4, 10.0, 1, IDS_KB_FORMAT }, /* 100 KB */
2401     { 1000*KB, 1024.0, 1.0, 0, IDS_KB_FORMAT }, /* 1000 KB */
2402     { 10*MB, 10485.76, 100.0, 2, IDS_MB_FORMAT }, /* 10 MB */
2403     { 100*MB, 104857.6, 10.0, 1, IDS_MB_FORMAT }, /* 100 MB */
2404     { 1000*MB, 1048576.0, 1.0, 0, IDS_MB_FORMAT }, /* 1000 MB */
2405     { 10*GB, 10737418.24, 100.0, 2, IDS_GB_FORMAT }, /* 10 GB */
2406     { 100*GB, 107374182.4, 10.0, 1, IDS_GB_FORMAT }, /* 100 GB */
2407     { 1000*GB, 1073741824.0, 1.0, 0, IDS_GB_FORMAT }, /* 1000 GB */
2408     { 10*TB, 10485.76, 100.0, 2, IDS_TB_FORMAT }, /* 10 TB */
2409     { 100*TB, 104857.6, 10.0, 1, IDS_TB_FORMAT }, /* 100 TB */
2410     { 1000*TB, 1048576.0, 1.0, 0, IDS_TB_FORMAT }, /* 1000 TB */
2411     { 10*PB, 10737418.24, 100.00, 2, IDS_PB_FORMAT }, /* 10 PB */
2412     { 100*PB, 107374182.4, 10.00, 1, IDS_PB_FORMAT }, /* 100 PB */
2413     { 1000*PB, 1073741824.0, 1.00, 0, IDS_PB_FORMAT }, /* 1000 PB */
2414     { 0, 10995116277.76, 100.00, 2, IDS_EB_FORMAT } /* EB's, catch all */
2415 #else
2416     { 10*KB, 10.24, 100.0, 2, 'K' }, /* 10 KB */
2417     { 100*KB, 102.4, 10.0, 1, 'K' }, /* 100 KB */
2418     { 1000*KB, 1024.0, 1.0, 0, 'K' }, /* 1000 KB */
2419     { 10*MB, 10485.76, 100.0, 2, 'M' }, /* 10 MB */
2420     { 100*MB, 104857.6, 10.0, 1, 'M' }, /* 100 MB */
2421     { 1000*MB, 1048576.0, 1.0, 0, 'M' }, /* 1000 MB */
2422     { 10*GB, 10737418.24, 100.0, 2, 'G' }, /* 10 GB */
2423     { 100*GB, 107374182.4, 10.0, 1, 'G' }, /* 100 GB */
2424     { 1000*GB, 1073741824.0, 1.0, 0, 'G' }, /* 1000 GB */
2425     { 10*TB, 10485.76, 100.0, 2, 'T' }, /* 10 TB */
2426     { 100*TB, 104857.6, 10.0, 1, 'T' }, /* 100 TB */
2427     { 1000*TB, 1048576.0, 1.0, 0, 'T' }, /* 1000 TB */
2428     { 10*PB, 10737418.24, 100.00, 2, 'P' }, /* 10 PB */
2429     { 100*PB, 107374182.4, 10.00, 1, 'P' }, /* 100 PB */
2430     { 1000*PB, 1073741824.0, 1.00, 0, 'P' }, /* 1000 PB */
2431     { 0, 10995116277.76, 100.00, 2, 'E' } /* EB's, catch all */
2432 #endif
2433   };
2434 #ifdef __REACTOS__
2435   WCHAR szBuff[40], wszFormat[40];
2436 #else
2437   WCHAR wszAdd[] = {' ','?','B',0};
2438 #endif
2439   double dBytes;
2440   UINT i = 0;
2441 
2442   TRACE("(0x%s,%p,%d)\n", wine_dbgstr_longlong(llBytes), lpszDest, cchMax);
2443 
2444   if (!lpszDest || !cchMax)
2445     return lpszDest;
2446 
2447   if (llBytes < 1024)  /* 1K */
2448   {
2449     WCHAR wszBytesFormat[64];
2450     LoadStringW(shlwapi_hInstance, IDS_BYTES_FORMAT, wszBytesFormat, 64);
2451     snprintfW(lpszDest, cchMax, wszBytesFormat, (int)llBytes);
2452     return lpszDest;
2453   }
2454 
2455   /* Note that if this loop completes without finding a match, i will be
2456    * pointing at the last entry, which is a catch all for > 1000 PB
2457    */
2458   while (i < sizeof(bfFormats) / sizeof(SHLWAPI_BYTEFORMATS) - 1)
2459   {
2460     if (llBytes < bfFormats[i].dLimit)
2461       break;
2462     i++;
2463   }
2464   /* Above 1 TB we encounter problems with FP accuracy. So for amounts above
2465    * this number we integer shift down by 1 MB first. The table above has
2466    * the divisors scaled down from the '< 10 TB' entry onwards, to account
2467    * for this. We also add a small fudge factor to get the correct result for
2468    * counts that lie exactly on a 1024 byte boundary.
2469    */
2470   if (i > 8)
2471     dBytes = (double)(llBytes >> 20) + 0.001; /* Scale down by 1 MB */
2472   else
2473     dBytes = (double)llBytes + 0.00001;
2474 
2475   dBytes = floor(dBytes / bfFormats[i].dDivisor) / bfFormats[i].dNormaliser;
2476 
2477 #ifdef __REACTOS__
2478   if (!FormatDouble(dBytes, bfFormats[i].nDecimals, szBuff, ARRAYSIZE(szBuff)))
2479     return NULL;
2480   LoadStringW(shlwapi_hInstance, bfFormats[i].nFormatID, wszFormat, ARRAYSIZE(wszFormat));
2481   snprintfW(lpszDest, cchMax, wszFormat, szBuff);
2482 #else
2483   if (!FormatDouble(dBytes, bfFormats[i].nDecimals, lpszDest, cchMax))
2484     return NULL;
2485   wszAdd[1] = bfFormats[i].wPrefix;
2486   StrCatBuffW(lpszDest, wszAdd, cchMax);
2487 #endif
2488   return lpszDest;
2489 }
2490 
2491 /*************************************************************************
2492  * StrFormatByteSize64A	[SHLWAPI.@]
2493  *
2494  * See StrFormatByteSizeW.
2495  */
2496 LPSTR WINAPI StrFormatByteSize64A(LONGLONG llBytes, LPSTR lpszDest, UINT cchMax)
2497 {
2498   WCHAR wszBuff[32];
2499 
2500   StrFormatByteSizeW(llBytes, wszBuff, sizeof(wszBuff)/sizeof(WCHAR));
2501 
2502   if (lpszDest)
2503     WideCharToMultiByte(CP_ACP, 0, wszBuff, -1, lpszDest, cchMax, 0, 0);
2504   return lpszDest;
2505 }
2506 
2507 /*************************************************************************
2508  * StrFormatByteSizeA	[SHLWAPI.@]
2509  *
2510  * Create a string containing an abbreviated byte count of up to 2^31-1.
2511  *
2512  * PARAMS
2513  *  dwBytes  [I] Byte size to format
2514  *  lpszDest [I] Destination for formatted string
2515  *  cchMax   [I] Size of lpszDest
2516  *
2517  * RETURNS
2518  *  lpszDest.
2519  *
2520  * NOTES
2521  *  The Ascii and Unicode versions of this function accept a different
2522  *  integer type for dwBytes. See StrFormatByteSize64A().
2523  */
2524 LPSTR WINAPI StrFormatByteSizeA(DWORD dwBytes, LPSTR lpszDest, UINT cchMax)
2525 {
2526   TRACE("(%d,%p,%d)\n", dwBytes, lpszDest, cchMax);
2527 
2528   return StrFormatByteSize64A(dwBytes, lpszDest, cchMax);
2529 }
2530 
2531 /*************************************************************************
2532  *      @	[SHLWAPI.162]
2533  *
2534  * Remove a hanging lead byte from the end of a string, if present.
2535  *
2536  * PARAMS
2537  *  lpStr [I] String to check for a hanging lead byte
2538  *  size  [I] Length of lpStr
2539  *
2540  * RETURNS
2541  *  Success: The new length of the string. Any hanging lead bytes are removed.
2542  *  Failure: 0, if any parameters are invalid.
2543  */
2544 DWORD WINAPI SHTruncateString(LPSTR lpStr, DWORD size)
2545 {
2546   if (lpStr && size)
2547   {
2548     LPSTR lastByte = lpStr + size - 1;
2549 
2550     while(lpStr < lastByte)
2551       lpStr += IsDBCSLeadByte(*lpStr) ? 2 : 1;
2552 
2553     if(lpStr == lastByte && IsDBCSLeadByte(*lpStr))
2554     {
2555       *lpStr = '\0';
2556       size--;
2557     }
2558     return size;
2559   }
2560   return 0;
2561 }
2562 
2563 /*************************************************************************
2564  *      @	[SHLWAPI.203]
2565  *
2566  * Remove a single non-trailing ampersand ('&') from a string.
2567  *
2568  * PARAMS
2569  *  lpszStr [I/O] String to remove ampersand from.
2570  *
2571  * RETURNS
2572  *  The character after the first ampersand in lpszStr, or the first character
2573  *  in lpszStr if there is no ampersand in the string.
2574  */
2575 char WINAPI SHStripMneumonicA(LPCSTR lpszStr)
2576 {
2577   LPSTR lpszIter, lpszTmp;
2578   char ch;
2579 
2580   TRACE("(%s)\n", debugstr_a(lpszStr));
2581 
2582   ch = *lpszStr;
2583 
2584   if ((lpszIter = StrChrA(lpszStr, '&')))
2585   {
2586     lpszTmp = CharNextA(lpszIter);
2587     if (*lpszTmp)
2588     {
2589       if (*lpszTmp != '&')
2590         ch =  *lpszTmp;
2591 
2592       memmove( lpszIter, lpszTmp, strlen(lpszTmp) + 1 );
2593     }
2594   }
2595 
2596   return ch;
2597 }
2598 
2599 /*************************************************************************
2600  *      @	[SHLWAPI.225]
2601  *
2602  * Unicode version of SHStripMneumonicA.
2603  */
2604 WCHAR WINAPI SHStripMneumonicW(LPCWSTR lpszStr)
2605 {
2606   LPWSTR lpszIter, lpszTmp;
2607   WCHAR ch;
2608 
2609   TRACE("(%s)\n", debugstr_w(lpszStr));
2610 
2611   ch = *lpszStr;
2612 
2613   if ((lpszIter = StrChrW(lpszStr, '&')))
2614   {
2615     lpszTmp = lpszIter + 1;
2616     if (*lpszTmp)
2617     {
2618       if (*lpszTmp != '&')
2619         ch =  *lpszTmp;
2620 
2621       memmove( lpszIter, lpszTmp, (strlenW(lpszTmp) + 1) * sizeof(WCHAR) );
2622     }
2623   }
2624 
2625   return ch;
2626 }
2627 
2628 /*************************************************************************
2629  *      @	[SHLWAPI.216]
2630  *
2631  * Convert an Ascii string to Unicode.
2632  *
2633  * PARAMS
2634  * dwCp     [I] Code page for the conversion
2635  * lpSrcStr [I] Source Ascii string to convert
2636  * lpDstStr [O] Destination for converted Unicode string
2637  * iLen     [I] Length of lpDstStr
2638  *
2639  * RETURNS
2640  *  The return value of the MultiByteToWideChar() function called on lpSrcStr.
2641  */
2642 DWORD WINAPI SHAnsiToUnicodeCP(DWORD dwCp, LPCSTR lpSrcStr, LPWSTR lpDstStr, int iLen)
2643 {
2644   DWORD dwRet;
2645 
2646   dwRet = MultiByteToWideChar(dwCp, 0, lpSrcStr, -1, lpDstStr, iLen);
2647   TRACE("%s->%s,ret=%d\n", debugstr_a(lpSrcStr), debugstr_w(lpDstStr), dwRet);
2648   return dwRet;
2649 }
2650 
2651 /*************************************************************************
2652  *      @	[SHLWAPI.215]
2653  *
2654  * Convert an Ascii string to Unicode.
2655  *
2656  * PARAMS
2657  * lpSrcStr [I] Source Ascii string to convert
2658  * lpDstStr [O] Destination for converted Unicode string
2659  * iLen     [I] Length of lpDstStr
2660  *
2661  * RETURNS
2662  *  The return value of the MultiByteToWideChar() function called on lpSrcStr.
2663  *
2664  * NOTES
2665  *  This function simply calls SHAnsiToUnicodeCP with code page CP_ACP.
2666  */
2667 DWORD WINAPI SHAnsiToUnicode(LPCSTR lpSrcStr, LPWSTR lpDstStr, int iLen)
2668 {
2669   return SHAnsiToUnicodeCP(CP_ACP, lpSrcStr, lpDstStr, iLen);
2670 }
2671 
2672 /*************************************************************************
2673  *      @	[SHLWAPI.218]
2674  *
2675  * Convert a Unicode string to Ascii.
2676  *
2677  * PARAMS
2678  *  CodePage [I] Code page to use for the conversion
2679  *  lpSrcStr [I] Source Unicode string to convert
2680  *  lpDstStr [O] Destination for converted Ascii string
2681  *  dstlen   [I] Length of buffer at lpDstStr
2682  *
2683  * RETURNS
2684  *  Success: The length in bytes of the result at lpDstStr (including the terminator)
2685  *  Failure: When using CP_UTF8, CP_UTF7 or 0xc350 as codePage, 0 is returned and
2686  *           the result is not nul-terminated.
2687  *           When using a different codepage, the length in bytes of the truncated
2688  *           result at lpDstStr (including the terminator) is returned and
2689  *           lpDstStr is always nul-terminated.
2690  *
2691  */
2692 DWORD WINAPI SHUnicodeToAnsiCP(UINT CodePage, LPCWSTR lpSrcStr, LPSTR lpDstStr, int dstlen)
2693 {
2694   static const WCHAR emptyW[] = { '\0' };
2695   int len , reqLen;
2696   LPSTR mem;
2697 
2698   if (!lpDstStr || !dstlen)
2699     return 0;
2700 
2701   if (!lpSrcStr)
2702     lpSrcStr = emptyW;
2703 
2704   *lpDstStr = '\0';
2705 
2706   len = strlenW(lpSrcStr) + 1;
2707 
2708   switch (CodePage)
2709   {
2710   case CP_WINUNICODE:
2711     CodePage = CP_UTF8; /* Fall through... */
2712   case 0x0000C350: /* FIXME: CP_ #define */
2713   case CP_UTF7:
2714   case CP_UTF8:
2715     {
2716       DWORD dwMode = 0;
2717       INT lenW = len - 1;
2718       INT needed = dstlen - 1;
2719       HRESULT hr;
2720 
2721       /* try the user supplied buffer first */
2722       hr = ConvertINetUnicodeToMultiByte(&dwMode, CodePage, lpSrcStr, &lenW, lpDstStr, &needed);
2723       if (hr == S_OK)
2724       {
2725         lpDstStr[needed] = '\0';
2726         return needed + 1;
2727       }
2728 
2729       /* user buffer too small. exclude termination and copy as much as possible */
2730       lenW = len;
2731       hr = ConvertINetUnicodeToMultiByte(&dwMode, CodePage, lpSrcStr, &lenW, NULL, &needed);
2732       needed++;
2733       mem = HeapAlloc(GetProcessHeap(), 0, needed);
2734       if (!mem)
2735         return 0;
2736 
2737       hr = ConvertINetUnicodeToMultiByte(&dwMode, CodePage, lpSrcStr, &len, mem, &needed);
2738       if (hr == S_OK)
2739       {
2740           reqLen = SHTruncateString(mem, dstlen);
2741           if (reqLen > 0) memcpy(lpDstStr, mem, reqLen-1);
2742       }
2743       HeapFree(GetProcessHeap(), 0, mem);
2744       return 0;
2745     }
2746   default:
2747     break;
2748   }
2749 
2750   /* try the user supplied buffer first */
2751   reqLen = WideCharToMultiByte(CodePage, 0, lpSrcStr, len, lpDstStr, dstlen, NULL, NULL);
2752 
2753   if (!reqLen && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
2754   {
2755     reqLen = WideCharToMultiByte(CodePage, 0, lpSrcStr, len, NULL, 0, NULL, NULL);
2756     if (reqLen)
2757     {
2758       mem = HeapAlloc(GetProcessHeap(), 0, reqLen);
2759       if (mem)
2760       {
2761         WideCharToMultiByte(CodePage, 0, lpSrcStr, len, mem, reqLen, NULL, NULL);
2762 
2763         reqLen = SHTruncateString(mem, dstlen -1);
2764         reqLen++;
2765 
2766         lstrcpynA(lpDstStr, mem, reqLen);
2767         HeapFree(GetProcessHeap(), 0, mem);
2768         lpDstStr[reqLen-1] = '\0';
2769       }
2770     }
2771   }
2772   return reqLen;
2773 }
2774 
2775 /*************************************************************************
2776  *      @	[SHLWAPI.217]
2777  *
2778  * Convert a Unicode string to Ascii.
2779  *
2780  * PARAMS
2781  *  lpSrcStr [I] Source Unicode string to convert
2782  *  lpDstStr [O] Destination for converted Ascii string
2783  *  iLen     [O] Length of lpDstStr in characters
2784  *
2785  * RETURNS
2786  *  See SHUnicodeToAnsiCP
2787 
2788  * NOTES
2789  *  This function simply calls SHUnicodeToAnsiCP() with CodePage = CP_ACP.
2790  */
2791 INT WINAPI SHUnicodeToAnsi(LPCWSTR lpSrcStr, LPSTR lpDstStr, INT iLen)
2792 {
2793     return SHUnicodeToAnsiCP(CP_ACP, lpSrcStr, lpDstStr, iLen);
2794 }
2795 
2796 /*************************************************************************
2797  *      @	[SHLWAPI.345]
2798  *
2799  * Copy one string to another.
2800  *
2801  * PARAMS
2802  *  lpszSrc [I] Source string to copy
2803  *  lpszDst [O] Destination for copy
2804  *  iLen    [I] Length of lpszDst in characters
2805  *
2806  * RETURNS
2807  *  The length of the copied string, including the terminating NUL. lpszDst
2808  *  contains iLen characters of lpszSrc.
2809  */
2810 DWORD WINAPI SHAnsiToAnsi(LPCSTR lpszSrc, LPSTR lpszDst, int iLen)
2811 {
2812     LPSTR lpszRet;
2813 
2814     TRACE("(%s,%p,0x%08x)\n", debugstr_a(lpszSrc), lpszDst, iLen);
2815 
2816     lpszRet = StrCpyNXA(lpszDst, lpszSrc, iLen);
2817     return lpszRet - lpszDst + 1;
2818 }
2819 
2820 /*************************************************************************
2821  *      @	[SHLWAPI.346]
2822  *
2823  * Unicode version of SSHAnsiToAnsi.
2824  */
2825 DWORD WINAPI SHUnicodeToUnicode(LPCWSTR lpszSrc, LPWSTR lpszDst, int iLen)
2826 {
2827     LPWSTR lpszRet;
2828 
2829     TRACE("(%s,%p,0x%08x)\n", debugstr_w(lpszSrc), lpszDst, iLen);
2830 
2831     lpszRet = StrCpyNXW(lpszDst, lpszSrc, iLen);
2832     return lpszRet - lpszDst + 1;
2833 }
2834 
2835 /*************************************************************************
2836  *      @	[SHLWAPI.364]
2837  *
2838  * Determine if an Ascii string converts to Unicode and back identically.
2839  *
2840  * PARAMS
2841  *  lpSrcStr [I] Source Unicode string to convert
2842  *  lpDst    [O] Destination for resulting Ascii string
2843  *  iLen     [I] Length of lpDst in characters
2844  *
2845  * RETURNS
2846  *  TRUE, since Ascii strings always convert identically.
2847  */
2848 BOOL WINAPI DoesStringRoundTripA(LPCSTR lpSrcStr, LPSTR lpDst, INT iLen)
2849 {
2850   lstrcpynA(lpDst, lpSrcStr, iLen);
2851   return TRUE;
2852 }
2853 
2854 /*************************************************************************
2855  *      @	[SHLWAPI.365]
2856  *
2857  * Determine if a Unicode string converts to Ascii and back identically.
2858  *
2859  * PARAMS
2860  *  lpSrcStr [I] Source Unicode string to convert
2861  *  lpDst    [O] Destination for resulting Ascii string
2862  *  iLen     [I] Length of lpDst in characters
2863  *
2864  * RETURNS
2865  *  TRUE, if lpSrcStr converts to Ascii and back identically,
2866  *  FALSE otherwise.
2867  */
2868 BOOL WINAPI DoesStringRoundTripW(LPCWSTR lpSrcStr, LPSTR lpDst, INT iLen)
2869 {
2870     WCHAR szBuff[MAX_PATH];
2871 
2872     SHUnicodeToAnsi(lpSrcStr, lpDst, iLen);
2873     SHAnsiToUnicode(lpDst, szBuff, MAX_PATH);
2874     return !strcmpW(lpSrcStr, szBuff);
2875 }
2876 
2877 /*************************************************************************
2878  *      SHLoadIndirectString    [SHLWAPI.@]
2879  *
2880  * If passed a string that begins with '@', extract the string from the
2881  * appropriate resource, otherwise do a straight copy.
2882  *
2883  */
2884 HRESULT WINAPI SHLoadIndirectString(LPCWSTR src, LPWSTR dst, UINT dst_len, void **reserved)
2885 {
2886     WCHAR *dllname = NULL;
2887     HMODULE hmod = NULL;
2888     HRESULT hr = E_FAIL;
2889 #ifdef __REACTOS__
2890     WCHAR szExpanded[512];
2891 #endif
2892 
2893     TRACE("(%s %p %08x %p)\n", debugstr_w(src), dst, dst_len, reserved);
2894 
2895     if(src[0] == '@')
2896     {
2897         WCHAR *index_str;
2898         int index;
2899 
2900 #ifdef __REACTOS__
2901         if (wcschr(src, '%') != NULL)
2902         {
2903             ExpandEnvironmentStringsW(src, szExpanded, ARRAY_SIZE(szExpanded));
2904             src = szExpanded;
2905         }
2906 #endif
2907         dst[0] = 0;
2908         dllname = StrDupW(src + 1);
2909         index_str = strchrW(dllname, ',');
2910 
2911         if(!index_str) goto end;
2912 
2913         *index_str = 0;
2914         index_str++;
2915         index = atoiW(index_str);
2916 
2917 #ifdef __REACTOS__
2918         hmod = LoadLibraryExW(dllname, NULL, LOAD_LIBRARY_AS_DATAFILE);
2919 #else
2920         hmod = LoadLibraryW(dllname);
2921 #endif
2922         if(!hmod) goto end;
2923 
2924         if(index < 0)
2925         {
2926             if(LoadStringW(hmod, -index, dst, dst_len))
2927                 hr = S_OK;
2928         }
2929         else
2930             FIXME("can't handle non-negative indices (%d)\n", index);
2931     }
2932     else
2933     {
2934         if(dst != src)
2935             lstrcpynW(dst, src, dst_len);
2936         hr = S_OK;
2937     }
2938 
2939     TRACE("returning %s\n", debugstr_w(dst));
2940 end:
2941     if(hmod) FreeLibrary(hmod);
2942     LocalFree(dllname);
2943     return hr;
2944 }
2945 
2946 BOOL WINAPI IsCharSpaceA(CHAR c)
2947 {
2948     WORD CharType;
2949     return GetStringTypeA(GetSystemDefaultLCID(), CT_CTYPE1, &c, 1, &CharType) && (CharType & C1_SPACE);
2950 }
2951 
2952 /*************************************************************************
2953  *      @	[SHLWAPI.29]
2954  *
2955  * Determine if a Unicode character is a space.
2956  *
2957  * PARAMS
2958  *  wc [I] Character to check.
2959  *
2960  * RETURNS
2961  *  TRUE, if wc is a space,
2962  *  FALSE otherwise.
2963  */
2964 BOOL WINAPI IsCharSpaceW(WCHAR wc)
2965 {
2966     WORD CharType;
2967 
2968     return GetStringTypeW(CT_CTYPE1, &wc, 1, &CharType) && (CharType & C1_SPACE);
2969 }
2970