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