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