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