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