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