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