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