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