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