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