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