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