schedsvc/tests: Add the current process id to the test summary line.
[wine.git] / dlls / shlwapi / string.c
blob0b34d43624c5756b68dbbec3bfdf8c4f996c5941
1 /*
2 * Shlwapi string functions
4 * Copyright 1998 Juergen Schmied
5 * Copyright 2002 Jon Griffiths
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.
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.
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
22 #include "config.h"
23 #include "wine/port.h"
25 #include <math.h>
26 #include <stdarg.h>
27 #include <stdio.h>
28 #include <string.h>
30 #define NONAMELESSUNION
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"
45 #include "resource.h"
47 WINE_DEFAULT_DEBUG_CHANNEL(shell);
49 extern HINSTANCE shlwapi_hInstance;
51 static HRESULT _SHStrDupAA(LPCSTR,LPSTR*);
52 static HRESULT _SHStrDupAW(LPCWSTR,LPSTR*);
55 static void FillNumberFmt(NUMBERFMTW *fmt, LPWSTR decimal_buffer, int decimal_bufwlen,
56 LPWSTR thousand_buffer, int thousand_bufwlen)
58 WCHAR grouping[64];
59 WCHAR *c;
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;
69 /*
70 * Converting grouping string to number as described on
71 * http://blogs.msdn.com/oldnewthing/archive/2006/04/18/578251.aspx
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')
78 fmt->Grouping *= 10;
79 fmt->Grouping += *c - '0';
82 if (fmt->Grouping % 10 == 0)
83 fmt->Grouping /= 10;
84 else
85 fmt->Grouping *= 10;
88 /*************************************************************************
89 * FormatInt [internal]
91 * Format an integer according to the current locale
93 * RETURNS
94 * The number of characters written on success or 0 on failure
96 static int FormatInt(LONGLONG qdwValue, LPWSTR pszBuf, int cchBuf)
98 NUMBERFMTW fmt;
99 WCHAR decimal[8], thousand[8];
100 WCHAR buf[24];
101 WCHAR *c;
102 BOOL neg = (qdwValue < 0);
104 FillNumberFmt(&fmt, decimal, sizeof decimal / sizeof (WCHAR),
105 thousand, sizeof thousand / sizeof (WCHAR));
107 c = &buf[24];
108 *(--c) = 0;
111 *(--c) = '0' + (qdwValue%10);
112 qdwValue /= 10;
113 } while (qdwValue > 0);
114 if (neg)
115 *(--c) = '-';
117 return GetNumberFormatW(LOCALE_USER_DEFAULT, 0, c, &fmt, pszBuf, cchBuf);
120 /*************************************************************************
121 * FormatDouble [internal]
123 * Format an integer according to the current locale. Prints the specified number of digits
124 * after the decimal point
126 * RETURNS
127 * The number of characters written on success or 0 on failure
129 static int FormatDouble(double value, int decimals, LPWSTR pszBuf, int cchBuf)
131 static const WCHAR flfmt[] = {'%','f',0};
132 WCHAR buf[64];
133 NUMBERFMTW fmt;
134 WCHAR decimal[8], thousand[8];
136 snprintfW(buf, 64, flfmt, value);
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);
144 /*************************************************************************
145 * SHLWAPI_ChrCmpHelperA
147 * Internal helper for SHLWAPI_ChrCmpA/ChrCMPIA.
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.
157 static BOOL SHLWAPI_ChrCmpHelperA(WORD ch1, WORD ch2, DWORD dwFlags)
159 char str1[3], str2[3];
161 str1[0] = LOBYTE(ch1);
162 if (IsDBCSLeadByte(str1[0]))
164 str1[1] = HIBYTE(ch1);
165 str1[2] = '\0';
167 else
168 str1[1] = '\0';
170 str2[0] = LOBYTE(ch2);
171 if (IsDBCSLeadByte(str2[0]))
173 str2[1] = HIBYTE(ch2);
174 str2[2] = '\0';
176 else
177 str2[1] = '\0';
179 return CompareStringA(GetThreadLocale(), dwFlags, str1, -1, str2, -1) - CSTR_EQUAL;
182 /*************************************************************************
183 * SHLWAPI_ChrCmpA
185 * Internal helper function.
187 static BOOL WINAPI SHLWAPI_ChrCmpA(WORD ch1, WORD ch2)
189 return SHLWAPI_ChrCmpHelperA(ch1, ch2, 0);
192 /*************************************************************************
193 * ChrCmpIA (SHLWAPI.385)
195 * Compare two characters, ignoring case.
197 * PARAMS
198 * ch1 [I] First character to compare
199 * ch2 [I] Second character to compare
201 * RETURNS
202 * FALSE, if the characters are equal.
203 * Non-zero otherwise.
205 BOOL WINAPI ChrCmpIA(WORD ch1, WORD ch2)
207 TRACE("(%d,%d)\n", ch1, ch2);
209 return SHLWAPI_ChrCmpHelperA(ch1, ch2, NORM_IGNORECASE);
212 /*************************************************************************
213 * ChrCmpIW [SHLWAPI.386]
215 * See ChrCmpIA.
217 BOOL WINAPI ChrCmpIW(WCHAR ch1, WCHAR ch2)
219 return CompareStringW(GetThreadLocale(), NORM_IGNORECASE, &ch1, 1, &ch2, 1) - CSTR_EQUAL;
222 /*************************************************************************
223 * StrChrA [SHLWAPI.@]
225 * Find a given character in a string.
227 * PARAMS
228 * lpszStr [I] String to search in.
229 * ch [I] Character to search for.
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.
236 LPSTR WINAPI StrChrA(LPCSTR lpszStr, WORD ch)
238 TRACE("(%s,%i)\n", debugstr_a(lpszStr), ch);
240 if (lpszStr)
242 while (*lpszStr)
244 if (!SHLWAPI_ChrCmpA(*lpszStr, ch))
245 return (LPSTR)lpszStr;
246 lpszStr = CharNextA(lpszStr);
249 return NULL;
252 /*************************************************************************
253 * StrChrW [SHLWAPI.@]
255 * See StrChrA.
257 LPWSTR WINAPI StrChrW(LPCWSTR lpszStr, WCHAR ch)
259 LPWSTR lpszRet = NULL;
261 TRACE("(%s,%i)\n", debugstr_w(lpszStr), ch);
263 if (lpszStr)
264 lpszRet = strchrW(lpszStr, ch);
265 return lpszRet;
268 /*************************************************************************
269 * StrChrIA [SHLWAPI.@]
271 * Find a given character in a string, ignoring case.
273 * PARAMS
274 * lpszStr [I] String to search in.
275 * ch [I] Character to search for.
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.
282 LPSTR WINAPI StrChrIA(LPCSTR lpszStr, WORD ch)
284 TRACE("(%s,%i)\n", debugstr_a(lpszStr), ch);
286 if (lpszStr)
288 while (*lpszStr)
290 if (!ChrCmpIA(*lpszStr, ch))
291 return (LPSTR)lpszStr;
292 lpszStr = CharNextA(lpszStr);
295 return NULL;
298 /*************************************************************************
299 * StrChrIW [SHLWAPI.@]
301 * See StrChrA.
303 LPWSTR WINAPI StrChrIW(LPCWSTR lpszStr, WCHAR ch)
305 TRACE("(%s,%i)\n", debugstr_w(lpszStr), ch);
307 if (lpszStr)
309 ch = toupperW(ch);
310 while (*lpszStr)
312 if (toupperW(*lpszStr) == ch)
313 return (LPWSTR)lpszStr;
314 lpszStr++;
316 lpszStr = NULL;
318 return (LPWSTR)lpszStr;
321 /*************************************************************************
322 * StrChrNW [SHLWAPI.@]
324 LPWSTR WINAPI StrChrNW(LPCWSTR lpszStr, WCHAR ch, UINT cchMax)
326 TRACE("(%s(%i),%i)\n", debugstr_wn(lpszStr,cchMax), cchMax, ch);
328 if (lpszStr)
330 while (*lpszStr && cchMax-- > 0)
332 if (*lpszStr == ch)
333 return (LPWSTR)lpszStr;
334 lpszStr++;
337 return NULL;
340 /*************************************************************************
341 * StrCmpIW [SHLWAPI.@]
343 * Compare two strings, ignoring case.
345 * PARAMS
346 * lpszStr [I] First string to compare
347 * lpszComp [I] Second string to compare
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.
353 int WINAPI StrCmpIW(LPCWSTR lpszStr, LPCWSTR lpszComp)
355 TRACE("(%s,%s)\n", debugstr_w(lpszStr),debugstr_w(lpszComp));
356 return CompareStringW(GetThreadLocale(), NORM_IGNORECASE, lpszStr, -1, lpszComp, -1) - CSTR_EQUAL;
359 /*************************************************************************
360 * StrCmpNA [SHLWAPI.@]
362 * Compare two strings, up to a maximum length.
364 * PARAMS
365 * lpszStr [I] First string to compare
366 * lpszComp [I] Second string to compare
367 * iLen [I] Number of chars to compare
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.
373 INT WINAPI StrCmpNA(LPCSTR lpszStr, LPCSTR lpszComp, INT iLen)
375 TRACE("(%s,%s,%i)\n", debugstr_a(lpszStr), debugstr_a(lpszComp), iLen);
376 return CompareStringA(GetThreadLocale(), 0, lpszStr, iLen, lpszComp, iLen) - CSTR_EQUAL;
379 /*************************************************************************
380 * StrCmpNW [SHLWAPI.@]
382 * See StrCmpNA.
384 INT WINAPI StrCmpNW(LPCWSTR lpszStr, LPCWSTR lpszComp, INT iLen)
386 TRACE("(%s,%s,%i)\n", debugstr_w(lpszStr), debugstr_w(lpszComp), iLen);
387 return CompareStringW(GetThreadLocale(), 0, lpszStr, iLen, lpszComp, iLen) - CSTR_EQUAL;
390 /*************************************************************************
391 * StrCmpNIA [SHLWAPI.@]
393 * Compare two strings, up to a maximum length, ignoring case.
395 * PARAMS
396 * lpszStr [I] First string to compare
397 * lpszComp [I] Second string to compare
398 * iLen [I] Number of chars to compare
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.
404 int WINAPI StrCmpNIA(LPCSTR lpszStr, LPCSTR lpszComp, int iLen)
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;
410 /*************************************************************************
411 * StrCmpNIW [SHLWAPI.@]
413 * See StrCmpNIA.
415 INT WINAPI StrCmpNIW(LPCWSTR lpszStr, LPCWSTR lpszComp, int iLen)
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;
421 /*************************************************************************
422 * StrCmpW [SHLWAPI.@]
424 * Compare two strings.
426 * PARAMS
427 * lpszStr [I] First string to compare
428 * lpszComp [I] Second string to compare
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.
434 int WINAPI StrCmpW(LPCWSTR lpszStr, LPCWSTR lpszComp)
436 TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszComp));
437 return CompareStringW(GetThreadLocale(), 0, lpszStr, -1, lpszComp, -1) - CSTR_EQUAL;
440 /*************************************************************************
441 * StrCatW [SHLWAPI.@]
443 * Concatenate two strings.
445 * PARAMS
446 * lpszStr [O] Initial string
447 * lpszSrc [I] String to concatenate
449 * RETURNS
450 * lpszStr.
452 LPWSTR WINAPI StrCatW(LPWSTR lpszStr, LPCWSTR lpszSrc)
454 TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszSrc));
456 if (lpszStr && lpszSrc)
457 strcatW(lpszStr, lpszSrc);
458 return lpszStr;
461 /*************************************************************************
462 * StrCatChainW [SHLWAPI.@]
464 * Concatenates two unicode strings.
466 * PARAMS
467 * lpszStr [O] Initial string
468 * cchMax [I] Length of destination buffer
469 * ichAt [I] Offset from the destination buffer to begin concatenation
470 * lpszCat [I] String to concatenate
472 * RETURNS
473 * The offset from the beginning of pszDst to the terminating NULL.
475 DWORD WINAPI StrCatChainW(LPWSTR lpszStr, DWORD cchMax, DWORD ichAt, LPCWSTR lpszCat)
477 TRACE("(%s,%u,%d,%s)\n", debugstr_w(lpszStr), cchMax, ichAt, debugstr_w(lpszCat));
479 if (ichAt == -1)
480 ichAt = strlenW(lpszStr);
482 if (!cchMax)
483 return ichAt;
485 if (ichAt == cchMax)
486 ichAt--;
488 if (lpszCat && ichAt < cchMax)
490 lpszStr += ichAt;
491 while (ichAt < cchMax - 1 && *lpszCat)
493 *lpszStr++ = *lpszCat++;
494 ichAt++;
496 *lpszStr = 0;
499 return ichAt;
502 /*************************************************************************
503 * StrCpyW [SHLWAPI.@]
505 * Copy a string to another string.
507 * PARAMS
508 * lpszStr [O] Destination string
509 * lpszSrc [I] Source string
511 * RETURNS
512 * lpszStr.
514 LPWSTR WINAPI StrCpyW(LPWSTR lpszStr, LPCWSTR lpszSrc)
516 TRACE("(%p,%s)\n", lpszStr, debugstr_w(lpszSrc));
518 if (lpszStr && lpszSrc)
519 strcpyW(lpszStr, lpszSrc);
520 return lpszStr;
523 /*************************************************************************
524 * StrCpyNW [SHLWAPI.@]
526 * Copy a string to another string, up to a maximum number of characters.
528 * PARAMS
529 * dst [O] Destination string
530 * src [I] Source string
531 * count [I] Maximum number of chars to copy
533 * RETURNS
534 * dst.
536 LPWSTR WINAPI StrCpyNW(LPWSTR dst, LPCWSTR src, int count)
538 LPWSTR d = dst;
539 LPCWSTR s = src;
541 TRACE("(%p,%s,%i)\n", dst, debugstr_w(src), count);
543 if (s)
545 while ((count > 1) && *s)
547 count--;
548 *d++ = *s++;
551 if (count) *d = 0;
553 return dst;
556 /*************************************************************************
557 * SHLWAPI_StrStrHelperA
559 * Internal implementation of StrStrA/StrStrIA
561 static LPSTR SHLWAPI_StrStrHelperA(LPCSTR lpszStr, LPCSTR lpszSearch,
562 INT (WINAPI *pStrCmpFn)(LPCSTR,LPCSTR,INT))
564 size_t iLen;
565 LPCSTR end;
567 if (!lpszStr || !lpszSearch || !*lpszSearch)
568 return NULL;
570 iLen = strlen(lpszSearch);
571 end = lpszStr + strlen(lpszStr);
573 while (lpszStr + iLen <= end)
575 if (!pStrCmpFn(lpszStr, lpszSearch, iLen))
576 return (LPSTR)lpszStr;
577 lpszStr = CharNextA(lpszStr);
579 return NULL;
582 /*************************************************************************
583 * StrStrA [SHLWAPI.@]
585 * Find a substring within a string.
587 * PARAMS
588 * lpszStr [I] String to search in
589 * lpszSearch [I] String to look for
591 * RETURNS
592 * The start of lpszSearch within lpszStr, or NULL if not found.
594 LPSTR WINAPI StrStrA(LPCSTR lpszStr, LPCSTR lpszSearch)
596 TRACE("(%s,%s)\n", debugstr_a(lpszStr), debugstr_a(lpszSearch));
598 return SHLWAPI_StrStrHelperA(lpszStr, lpszSearch, StrCmpNA);
601 /*************************************************************************
602 * StrStrW [SHLWAPI.@]
604 * See StrStrA.
606 LPWSTR WINAPI StrStrW(LPCWSTR lpszStr, LPCWSTR lpszSearch)
608 TRACE("(%s, %s)\n", debugstr_w(lpszStr), debugstr_w(lpszSearch));
610 if (!lpszStr || !lpszSearch || !*lpszSearch) return NULL;
611 return strstrW( lpszStr, lpszSearch );
614 /*************************************************************************
615 * StrRStrIA [SHLWAPI.@]
617 * Find the last occurrence of a substring within a string.
619 * PARAMS
620 * lpszStr [I] String to search in
621 * lpszEnd [I] End of lpszStr
622 * lpszSearch [I] String to look for
624 * RETURNS
625 * The last occurrence lpszSearch within lpszStr, or NULL if not found.
627 LPSTR WINAPI StrRStrIA(LPCSTR lpszStr, LPCSTR lpszEnd, LPCSTR lpszSearch)
629 LPSTR lpszRet = NULL;
630 WORD ch1, ch2;
631 INT iLen;
633 TRACE("(%s,%s)\n", debugstr_a(lpszStr), debugstr_a(lpszSearch));
635 if (!lpszStr || !lpszSearch || !*lpszSearch)
636 return NULL;
638 if (IsDBCSLeadByte(*lpszSearch))
639 ch1 = *lpszSearch << 8 | (UCHAR)lpszSearch[1];
640 else
641 ch1 = *lpszSearch;
642 iLen = lstrlenA(lpszSearch);
644 if (!lpszEnd)
645 lpszEnd = lpszStr + lstrlenA(lpszStr);
646 else /* reproduce the broken behaviour on Windows */
647 lpszEnd += min(iLen - 1, lstrlenA(lpszEnd));
649 while (lpszStr + iLen <= lpszEnd && *lpszStr)
651 ch2 = IsDBCSLeadByte(*lpszStr)? *lpszStr << 8 | (UCHAR)lpszStr[1] : *lpszStr;
652 if (!ChrCmpIA(ch1, ch2))
654 if (!StrCmpNIA(lpszStr, lpszSearch, iLen))
655 lpszRet = (LPSTR)lpszStr;
657 lpszStr = CharNextA(lpszStr);
659 return lpszRet;
662 /*************************************************************************
663 * StrRStrIW [SHLWAPI.@]
665 * See StrRStrIA.
667 LPWSTR WINAPI StrRStrIW(LPCWSTR lpszStr, LPCWSTR lpszEnd, LPCWSTR lpszSearch)
669 LPWSTR lpszRet = NULL;
670 INT iLen;
672 TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszSearch));
674 if (!lpszStr || !lpszSearch || !*lpszSearch)
675 return NULL;
677 iLen = strlenW(lpszSearch);
679 if (!lpszEnd)
680 lpszEnd = lpszStr + strlenW(lpszStr);
681 else /* reproduce the broken behaviour on Windows */
682 lpszEnd += min(iLen - 1, lstrlenW(lpszEnd));
684 while (lpszStr + iLen <= lpszEnd && *lpszStr)
686 if (!ChrCmpIW(*lpszSearch, *lpszStr))
688 if (!StrCmpNIW(lpszStr, lpszSearch, iLen))
689 lpszRet = (LPWSTR)lpszStr;
691 lpszStr++;
693 return lpszRet;
696 /*************************************************************************
697 * StrStrIA [SHLWAPI.@]
699 * Find a substring within a string, ignoring case.
701 * PARAMS
702 * lpszStr [I] String to search in
703 * lpszSearch [I] String to look for
705 * RETURNS
706 * The start of lpszSearch within lpszStr, or NULL if not found.
708 LPSTR WINAPI StrStrIA(LPCSTR lpszStr, LPCSTR lpszSearch)
710 TRACE("(%s,%s)\n", debugstr_a(lpszStr), debugstr_a(lpszSearch));
712 return SHLWAPI_StrStrHelperA(lpszStr, lpszSearch, StrCmpNIA);
715 /*************************************************************************
716 * StrStrIW [SHLWAPI.@]
718 * See StrStrIA.
720 LPWSTR WINAPI StrStrIW(LPCWSTR lpszStr, LPCWSTR lpszSearch)
722 int iLen;
723 LPCWSTR end;
725 TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszSearch));
727 if (!lpszStr || !lpszSearch || !*lpszSearch)
728 return NULL;
730 iLen = strlenW(lpszSearch);
731 end = lpszStr + strlenW(lpszStr);
733 while (lpszStr + iLen <= end)
735 if (!StrCmpNIW(lpszStr, lpszSearch, iLen))
736 return (LPWSTR)lpszStr;
737 lpszStr++;
739 return NULL;
742 /*************************************************************************
743 * StrStrNW [SHLWAPI.@]
745 * Find a substring within a string up to a given number of initial characters.
747 * PARAMS
748 * lpFirst [I] String to search in
749 * lpSrch [I] String to look for
750 * cchMax [I] Maximum number of initial search characters
752 * RETURNS
753 * The start of lpFirst within lpSrch, or NULL if not found.
755 LPWSTR WINAPI StrStrNW(LPCWSTR lpFirst, LPCWSTR lpSrch, UINT cchMax)
757 UINT i;
758 int len;
760 TRACE("(%s, %s, %u)\n", debugstr_w(lpFirst), debugstr_w(lpSrch), cchMax);
762 if (!lpFirst || !lpSrch || !*lpSrch || !cchMax)
763 return NULL;
765 len = strlenW(lpSrch);
767 for (i = cchMax; *lpFirst && (i > 0); i--, lpFirst++)
769 if (!strncmpW(lpFirst, lpSrch, len))
770 return (LPWSTR)lpFirst;
773 return NULL;
776 /*************************************************************************
777 * StrStrNIW [SHLWAPI.@]
779 * Find a substring within a string up to a given number of initial characters,
780 * ignoring case.
782 * PARAMS
783 * lpFirst [I] String to search in
784 * lpSrch [I] String to look for
785 * cchMax [I] Maximum number of initial search characters
787 * RETURNS
788 * The start of lpFirst within lpSrch, or NULL if not found.
790 LPWSTR WINAPI StrStrNIW(LPCWSTR lpFirst, LPCWSTR lpSrch, UINT cchMax)
792 UINT i;
793 int len;
795 TRACE("(%s, %s, %u)\n", debugstr_w(lpFirst), debugstr_w(lpSrch), cchMax);
797 if (!lpFirst || !lpSrch || !*lpSrch || !cchMax)
798 return NULL;
800 len = strlenW(lpSrch);
802 for (i = cchMax; *lpFirst && (i > 0); i--, lpFirst++)
804 if (!strncmpiW(lpFirst, lpSrch, len))
805 return (LPWSTR)lpFirst;
808 return NULL;
811 /*************************************************************************
812 * StrToIntA [SHLWAPI.@]
814 * Read a signed integer from a string.
816 * PARAMS
817 * lpszStr [I] String to read integer from
819 * RETURNS
820 * The signed integer value represented by the string, or 0 if no integer is
821 * present.
823 * NOTES
824 * No leading space is allowed before the number, although a leading '-' is.
826 int WINAPI StrToIntA(LPCSTR lpszStr)
828 int iRet = 0;
830 TRACE("(%s)\n", debugstr_a(lpszStr));
832 if (!lpszStr)
834 WARN("Invalid lpszStr would crash under Win32!\n");
835 return 0;
838 if (*lpszStr == '-' || isdigit(*lpszStr))
839 StrToIntExA(lpszStr, 0, &iRet);
840 return iRet;
843 /*************************************************************************
844 * StrToIntW [SHLWAPI.@]
846 * See StrToIntA.
848 int WINAPI StrToIntW(LPCWSTR lpszStr)
850 int iRet = 0;
852 TRACE("(%s)\n", debugstr_w(lpszStr));
854 if (!lpszStr)
856 WARN("Invalid lpszStr would crash under Win32!\n");
857 return 0;
860 if (*lpszStr == '-' || isdigitW(*lpszStr))
861 StrToIntExW(lpszStr, 0, &iRet);
862 return iRet;
865 /*************************************************************************
866 * StrToIntExA [SHLWAPI.@]
868 * Read an integer from a string.
870 * PARAMS
871 * lpszStr [I] String to read integer from
872 * dwFlags [I] Flags controlling the conversion
873 * lpiRet [O] Destination for read integer.
875 * RETURNS
876 * Success: TRUE. lpiRet contains the integer value represented by the string.
877 * Failure: FALSE, if the string is invalid, or no number is present.
879 * NOTES
880 * Leading whitespace, '-' and '+' are allowed before the number. If
881 * dwFlags includes STIF_SUPPORT_HEX, hexadecimal numbers are allowed, if
882 * preceded by '0x'. If this flag is not set, or there is no '0x' prefix,
883 * the string is treated as a decimal string. A leading '-' is ignored for
884 * hexadecimal numbers.
886 BOOL WINAPI StrToIntExA(LPCSTR lpszStr, DWORD dwFlags, LPINT lpiRet)
888 LONGLONG li;
889 BOOL bRes;
891 TRACE("(%s,%08X,%p)\n", debugstr_a(lpszStr), dwFlags, lpiRet);
893 bRes = StrToInt64ExA(lpszStr, dwFlags, &li);
894 if (bRes) *lpiRet = li;
895 return bRes;
898 /*************************************************************************
899 * StrToInt64ExA [SHLWAPI.@]
901 * See StrToIntExA.
903 BOOL WINAPI StrToInt64ExA(LPCSTR lpszStr, DWORD dwFlags, LONGLONG *lpiRet)
905 BOOL bNegative = FALSE;
906 LONGLONG iRet = 0;
908 TRACE("(%s,%08X,%p)\n", debugstr_a(lpszStr), dwFlags, lpiRet);
910 if (!lpszStr || !lpiRet)
912 WARN("Invalid parameter would crash under Win32!\n");
913 return FALSE;
915 if (dwFlags > STIF_SUPPORT_HEX) WARN("Unknown flags %08x\n", dwFlags);
917 /* Skip leading space, '+', '-' */
918 while (isspace(*lpszStr))
919 lpszStr = CharNextA(lpszStr);
921 if (*lpszStr == '-')
923 bNegative = TRUE;
924 lpszStr++;
926 else if (*lpszStr == '+')
927 lpszStr++;
929 if (dwFlags & STIF_SUPPORT_HEX &&
930 *lpszStr == '0' && tolower(lpszStr[1]) == 'x')
932 /* Read hex number */
933 lpszStr += 2;
935 if (!isxdigit(*lpszStr))
936 return FALSE;
938 while (isxdigit(*lpszStr))
940 iRet = iRet * 16;
941 if (isdigit(*lpszStr))
942 iRet += (*lpszStr - '0');
943 else
944 iRet += 10 + (tolower(*lpszStr) - 'a');
945 lpszStr++;
947 *lpiRet = iRet;
948 return TRUE;
951 /* Read decimal number */
952 if (!isdigit(*lpszStr))
953 return FALSE;
955 while (isdigit(*lpszStr))
957 iRet = iRet * 10;
958 iRet += (*lpszStr - '0');
959 lpszStr++;
961 *lpiRet = bNegative ? -iRet : iRet;
962 return TRUE;
965 /*************************************************************************
966 * StrToIntExW [SHLWAPI.@]
968 * See StrToIntExA.
970 BOOL WINAPI StrToIntExW(LPCWSTR lpszStr, DWORD dwFlags, LPINT lpiRet)
972 LONGLONG li;
973 BOOL bRes;
975 TRACE("(%s,%08X,%p)\n", debugstr_w(lpszStr), dwFlags, lpiRet);
977 bRes = StrToInt64ExW(lpszStr, dwFlags, &li);
978 if (bRes) *lpiRet = li;
979 return bRes;
982 /*************************************************************************
983 * StrToInt64ExW [SHLWAPI.@]
985 * See StrToIntExA.
987 BOOL WINAPI StrToInt64ExW(LPCWSTR lpszStr, DWORD dwFlags, LONGLONG *lpiRet)
989 BOOL bNegative = FALSE;
990 LONGLONG iRet = 0;
992 TRACE("(%s,%08X,%p)\n", debugstr_w(lpszStr), dwFlags, lpiRet);
994 if (!lpszStr || !lpiRet)
996 WARN("Invalid parameter would crash under Win32!\n");
997 return FALSE;
999 if (dwFlags > STIF_SUPPORT_HEX) WARN("Unknown flags %08x\n", dwFlags);
1001 /* Skip leading space, '+', '-' */
1002 while (isspaceW(*lpszStr)) lpszStr++;
1004 if (*lpszStr == '-')
1006 bNegative = TRUE;
1007 lpszStr++;
1009 else if (*lpszStr == '+')
1010 lpszStr++;
1012 if (dwFlags & STIF_SUPPORT_HEX &&
1013 *lpszStr == '0' && tolowerW(lpszStr[1]) == 'x')
1015 /* Read hex number */
1016 lpszStr += 2;
1018 if (!isxdigitW(*lpszStr))
1019 return FALSE;
1021 while (isxdigitW(*lpszStr))
1023 iRet = iRet * 16;
1024 if (isdigitW(*lpszStr))
1025 iRet += (*lpszStr - '0');
1026 else
1027 iRet += 10 + (tolowerW(*lpszStr) - 'a');
1028 lpszStr++;
1030 *lpiRet = iRet;
1031 return TRUE;
1034 /* Read decimal number */
1035 if (!isdigitW(*lpszStr))
1036 return FALSE;
1038 while (isdigitW(*lpszStr))
1040 iRet = iRet * 10;
1041 iRet += (*lpszStr - '0');
1042 lpszStr++;
1044 *lpiRet = bNegative ? -iRet : iRet;
1045 return TRUE;
1048 /*************************************************************************
1049 * StrDupA [SHLWAPI.@]
1051 * Duplicate a string.
1053 * PARAMS
1054 * lpszStr [I] String to duplicate.
1056 * RETURNS
1057 * Success: A pointer to a new string containing the contents of lpszStr
1058 * Failure: NULL, if memory cannot be allocated
1060 * NOTES
1061 * The string memory is allocated with LocalAlloc(), and so should be released
1062 * by calling LocalFree().
1064 LPSTR WINAPI StrDupA(LPCSTR lpszStr)
1066 int iLen;
1067 LPSTR lpszRet;
1069 TRACE("(%s)\n",debugstr_a(lpszStr));
1071 iLen = lpszStr ? strlen(lpszStr) + 1 : 1;
1072 lpszRet = LocalAlloc(LMEM_FIXED, iLen);
1074 if (lpszRet)
1076 if (lpszStr)
1077 memcpy(lpszRet, lpszStr, iLen);
1078 else
1079 *lpszRet = '\0';
1081 return lpszRet;
1084 /*************************************************************************
1085 * StrDupW [SHLWAPI.@]
1087 * See StrDupA.
1089 LPWSTR WINAPI StrDupW(LPCWSTR lpszStr)
1091 int iLen;
1092 LPWSTR lpszRet;
1094 TRACE("(%s)\n",debugstr_w(lpszStr));
1096 iLen = (lpszStr ? strlenW(lpszStr) + 1 : 1) * sizeof(WCHAR);
1097 lpszRet = LocalAlloc(LMEM_FIXED, iLen);
1099 if (lpszRet)
1101 if (lpszStr)
1102 memcpy(lpszRet, lpszStr, iLen);
1103 else
1104 *lpszRet = '\0';
1106 return lpszRet;
1109 /*************************************************************************
1110 * SHLWAPI_StrSpnHelperA
1112 * Internal implementation of StrSpnA/StrCSpnA/StrCSpnIA
1114 static int SHLWAPI_StrSpnHelperA(LPCSTR lpszStr, LPCSTR lpszMatch,
1115 LPSTR (WINAPI *pStrChrFn)(LPCSTR,WORD),
1116 BOOL bInvert)
1118 LPCSTR lpszRead = lpszStr;
1119 if (lpszStr && *lpszStr && lpszMatch)
1121 while (*lpszRead)
1123 LPCSTR lpszTest = pStrChrFn(lpszMatch, *lpszRead);
1125 if (!bInvert && !lpszTest)
1126 break;
1127 if (bInvert && lpszTest)
1128 break;
1129 lpszRead = CharNextA(lpszRead);
1132 return lpszRead - lpszStr;
1135 /*************************************************************************
1136 * StrSpnA [SHLWAPI.@]
1138 * Find the length of the start of a string that contains only certain
1139 * characters.
1141 * PARAMS
1142 * lpszStr [I] String to search
1143 * lpszMatch [I] Characters that can be in the substring
1145 * RETURNS
1146 * The length of the part of lpszStr containing only chars from lpszMatch,
1147 * or 0 if any parameter is invalid.
1149 int WINAPI StrSpnA(LPCSTR lpszStr, LPCSTR lpszMatch)
1151 TRACE("(%s,%s)\n",debugstr_a(lpszStr), debugstr_a(lpszMatch));
1153 return SHLWAPI_StrSpnHelperA(lpszStr, lpszMatch, StrChrA, FALSE);
1156 /*************************************************************************
1157 * StrSpnW [SHLWAPI.@]
1159 * See StrSpnA.
1161 int WINAPI StrSpnW(LPCWSTR lpszStr, LPCWSTR lpszMatch)
1163 if (!lpszStr || !lpszMatch) return 0;
1164 return strspnW( lpszStr, lpszMatch );
1167 /*************************************************************************
1168 * StrCSpnA [SHLWAPI.@]
1170 * Find the length of the start of a string that does not contain certain
1171 * characters.
1173 * PARAMS
1174 * lpszStr [I] String to search
1175 * lpszMatch [I] Characters that cannot be in the substring
1177 * RETURNS
1178 * The length of the part of lpszStr containing only chars not in lpszMatch,
1179 * or 0 if any parameter is invalid.
1181 int WINAPI StrCSpnA(LPCSTR lpszStr, LPCSTR lpszMatch)
1183 TRACE("(%s,%s)\n",debugstr_a(lpszStr), debugstr_a(lpszMatch));
1185 return SHLWAPI_StrSpnHelperA(lpszStr, lpszMatch, StrChrA, TRUE);
1188 /*************************************************************************
1189 * StrCSpnW [SHLWAPI.@]
1191 * See StrCSpnA.
1193 int WINAPI StrCSpnW(LPCWSTR lpszStr, LPCWSTR lpszMatch)
1195 if (!lpszStr || !lpszMatch) return 0;
1196 return strcspnW( lpszStr, lpszMatch );
1199 /*************************************************************************
1200 * StrCSpnIA [SHLWAPI.@]
1202 * Find the length of the start of a string that does not contain certain
1203 * characters, ignoring case.
1205 * PARAMS
1206 * lpszStr [I] String to search
1207 * lpszMatch [I] Characters that cannot be in the substring
1209 * RETURNS
1210 * The length of the part of lpszStr containing only chars not in lpszMatch,
1211 * or 0 if any parameter is invalid.
1213 int WINAPI StrCSpnIA(LPCSTR lpszStr, LPCSTR lpszMatch)
1215 TRACE("(%s,%s)\n",debugstr_a(lpszStr), debugstr_a(lpszMatch));
1217 return SHLWAPI_StrSpnHelperA(lpszStr, lpszMatch, StrChrIA, TRUE);
1220 /*************************************************************************
1221 * StrCSpnIW [SHLWAPI.@]
1223 * See StrCSpnIA.
1225 int WINAPI StrCSpnIW(LPCWSTR lpszStr, LPCWSTR lpszMatch)
1227 LPCWSTR lpszRead = lpszStr;
1229 TRACE("(%s,%s)\n",debugstr_w(lpszStr), debugstr_w(lpszMatch));
1231 if (lpszStr && *lpszStr && lpszMatch)
1233 while (*lpszRead)
1235 if (StrChrIW(lpszMatch, *lpszRead)) break;
1236 lpszRead++;
1239 return lpszRead - lpszStr;
1242 /*************************************************************************
1243 * StrPBrkA [SHLWAPI.@]
1245 * Search a string for any of a group of characters.
1247 * PARAMS
1248 * lpszStr [I] String to search
1249 * lpszMatch [I] Characters to match
1251 * RETURNS
1252 * A pointer to the first matching character in lpszStr, or NULL if no
1253 * match was found.
1255 LPSTR WINAPI StrPBrkA(LPCSTR lpszStr, LPCSTR lpszMatch)
1257 TRACE("(%s,%s)\n",debugstr_a(lpszStr), debugstr_a(lpszMatch));
1259 if (lpszStr && lpszMatch && *lpszMatch)
1261 while (*lpszStr)
1263 if (StrChrA(lpszMatch, *lpszStr))
1264 return (LPSTR)lpszStr;
1265 lpszStr = CharNextA(lpszStr);
1268 return NULL;
1271 /*************************************************************************
1272 * StrPBrkW [SHLWAPI.@]
1274 * See StrPBrkA.
1276 LPWSTR WINAPI StrPBrkW(LPCWSTR lpszStr, LPCWSTR lpszMatch)
1278 if (!lpszStr || !lpszMatch) return NULL;
1279 return strpbrkW( lpszStr, lpszMatch );
1282 /*************************************************************************
1283 * SHLWAPI_StrRChrHelperA
1285 * Internal implementation of StrRChrA/StrRChrIA.
1287 static LPSTR SHLWAPI_StrRChrHelperA(LPCSTR lpszStr,
1288 LPCSTR lpszEnd, WORD ch,
1289 BOOL (WINAPI *pChrCmpFn)(WORD,WORD))
1291 LPCSTR lpszRet = NULL;
1293 if (lpszStr)
1295 WORD ch2;
1297 if (!lpszEnd)
1298 lpszEnd = lpszStr + lstrlenA(lpszStr);
1300 while (*lpszStr && lpszStr <= lpszEnd)
1302 ch2 = IsDBCSLeadByte(*lpszStr)? *lpszStr << 8 | lpszStr[1] : *lpszStr;
1304 if (!pChrCmpFn(ch, ch2))
1305 lpszRet = lpszStr;
1306 lpszStr = CharNextA(lpszStr);
1309 return (LPSTR)lpszRet;
1312 /**************************************************************************
1313 * StrRChrA [SHLWAPI.@]
1315 * Find the last occurrence of a character in string.
1317 * PARAMS
1318 * lpszStr [I] String to search in
1319 * lpszEnd [I] Place to end search, or NULL to search until the end of lpszStr
1320 * ch [I] Character to search for.
1322 * RETURNS
1323 * Success: A pointer to the last occurrence of ch in lpszStr before lpszEnd,
1324 * or NULL if not found.
1325 * Failure: NULL, if any arguments are invalid.
1327 LPSTR WINAPI StrRChrA(LPCSTR lpszStr, LPCSTR lpszEnd, WORD ch)
1329 TRACE("(%s,%s,%x)\n", debugstr_a(lpszStr), debugstr_a(lpszEnd), ch);
1331 return SHLWAPI_StrRChrHelperA(lpszStr, lpszEnd, ch, SHLWAPI_ChrCmpA);
1334 /**************************************************************************
1335 * StrRChrW [SHLWAPI.@]
1337 * See StrRChrA.
1339 LPWSTR WINAPI StrRChrW(LPCWSTR str, LPCWSTR end, WORD ch)
1341 WCHAR *ret = NULL;
1343 if (!str) return NULL;
1344 if (!end) end = str + strlenW(str);
1345 while (str < end)
1347 if (*str == ch) ret = (WCHAR *)str;
1348 str++;
1350 return ret;
1353 /**************************************************************************
1354 * StrRChrIA [SHLWAPI.@]
1356 * Find the last occurrence of a character in string, ignoring case.
1358 * PARAMS
1359 * lpszStr [I] String to search in
1360 * lpszEnd [I] Place to end search, or NULL to search until the end of lpszStr
1361 * ch [I] Character to search for.
1363 * RETURNS
1364 * Success: A pointer to the last occurrence of ch in lpszStr before lpszEnd,
1365 * or NULL if not found.
1366 * Failure: NULL, if any arguments are invalid.
1368 LPSTR WINAPI StrRChrIA(LPCSTR lpszStr, LPCSTR lpszEnd, WORD ch)
1370 TRACE("(%s,%s,%x)\n", debugstr_a(lpszStr), debugstr_a(lpszEnd), ch);
1372 return SHLWAPI_StrRChrHelperA(lpszStr, lpszEnd, ch, ChrCmpIA);
1375 /**************************************************************************
1376 * StrRChrIW [SHLWAPI.@]
1378 * See StrRChrIA.
1380 LPWSTR WINAPI StrRChrIW(LPCWSTR str, LPCWSTR end, WORD ch)
1382 WCHAR *ret = NULL;
1384 if (!str) return NULL;
1385 if (!end) end = str + strlenW(str);
1386 while (str < end)
1388 if (!ChrCmpIW(*str, ch)) ret = (WCHAR *)str;
1389 str++;
1391 return ret;
1394 /*************************************************************************
1395 * StrCatBuffA [SHLWAPI.@]
1397 * Concatenate two strings together.
1399 * PARAMS
1400 * lpszStr [O] String to concatenate to
1401 * lpszCat [I] String to add to lpszCat
1402 * cchMax [I] Maximum number of characters for the whole string
1404 * RETURNS
1405 * lpszStr.
1407 * NOTES
1408 * cchMax determines the number of characters in the final length of the
1409 * string, not the number appended to lpszStr from lpszCat.
1411 LPSTR WINAPI StrCatBuffA(LPSTR lpszStr, LPCSTR lpszCat, INT cchMax)
1413 INT iLen;
1415 TRACE("(%p,%s,%d)\n", lpszStr, debugstr_a(lpszCat), cchMax);
1417 if (!lpszStr)
1419 WARN("Invalid lpszStr would crash under Win32!\n");
1420 return NULL;
1423 iLen = strlen(lpszStr);
1424 cchMax -= iLen;
1426 if (cchMax > 0)
1427 StrCpyNA(lpszStr + iLen, lpszCat, cchMax);
1428 return lpszStr;
1431 /*************************************************************************
1432 * StrCatBuffW [SHLWAPI.@]
1434 * See StrCatBuffA.
1436 LPWSTR WINAPI StrCatBuffW(LPWSTR lpszStr, LPCWSTR lpszCat, INT cchMax)
1438 INT iLen;
1440 TRACE("(%p,%s,%d)\n", lpszStr, debugstr_w(lpszCat), cchMax);
1442 if (!lpszStr)
1444 WARN("Invalid lpszStr would crash under Win32!\n");
1445 return NULL;
1448 iLen = strlenW(lpszStr);
1449 cchMax -= iLen;
1451 if (cchMax > 0)
1452 StrCpyNW(lpszStr + iLen, lpszCat, cchMax);
1453 return lpszStr;
1456 /*************************************************************************
1457 * StrRetToBufA [SHLWAPI.@]
1459 * Convert a STRRET to a normal string.
1461 * PARAMS
1462 * lpStrRet [O] STRRET to convert
1463 * pIdl [I] ITEMIDLIST for lpStrRet->uType == STRRET_OFFSET
1464 * lpszDest [O] Destination for normal string
1465 * dwLen [I] Length of lpszDest
1467 * RETURNS
1468 * Success: S_OK. lpszDest contains up to dwLen characters of the string.
1469 * If lpStrRet is of type STRRET_WSTR, its memory is freed with
1470 * CoTaskMemFree() and its type set to STRRET_CSTRA.
1471 * Failure: E_FAIL, if any parameters are invalid.
1473 HRESULT WINAPI StrRetToBufA (LPSTRRET src, const ITEMIDLIST *pidl, LPSTR dest, UINT len)
1475 /* NOTE:
1476 * This routine is identical to that in dlls/shell32/shellstring.c.
1477 * It was duplicated because not every version of Shlwapi.dll exports
1478 * StrRetToBufA. If you change one routine, change them both.
1480 TRACE("dest=%p len=0x%x strret=%p pidl=%p\n", dest, len, src, pidl);
1482 if (!src)
1484 WARN("Invalid lpStrRet would crash under Win32!\n");
1485 if (dest)
1486 *dest = '\0';
1487 return E_FAIL;
1490 if (!dest || !len)
1491 return E_FAIL;
1493 *dest = '\0';
1495 switch (src->uType)
1497 case STRRET_WSTR:
1498 WideCharToMultiByte(CP_ACP, 0, src->u.pOleStr, -1, dest, len, NULL, NULL);
1499 CoTaskMemFree(src->u.pOleStr);
1500 break;
1502 case STRRET_CSTR:
1503 lstrcpynA(dest, src->u.cStr, len);
1504 break;
1506 case STRRET_OFFSET:
1507 lstrcpynA((LPSTR)dest, ((LPCSTR)&pidl->mkid)+src->u.uOffset, len);
1508 break;
1510 default:
1511 FIXME("unknown type!\n");
1512 return E_NOTIMPL;
1514 return S_OK;
1517 /*************************************************************************
1518 * StrRetToBufW [SHLWAPI.@]
1520 * See StrRetToBufA.
1522 HRESULT WINAPI StrRetToBufW (LPSTRRET src, const ITEMIDLIST *pidl, LPWSTR dest, UINT len)
1524 TRACE("dest=%p len=0x%x strret=%p pidl=%p\n", dest, len, src, pidl);
1526 if (!dest || !len)
1527 return E_FAIL;
1529 if (!src)
1531 WARN("Invalid lpStrRet would crash under Win32!\n");
1532 if (dest)
1533 *dest = '\0';
1534 return E_FAIL;
1537 *dest = '\0';
1539 switch (src->uType) {
1540 case STRRET_WSTR: {
1541 size_t dst_len;
1542 if (!src->u.pOleStr)
1543 return E_FAIL;
1544 dst_len = strlenW(src->u.pOleStr);
1545 memcpy(dest, src->u.pOleStr, min(dst_len, len-1) * sizeof(WCHAR));
1546 dest[min(dst_len, len-1)] = 0;
1547 CoTaskMemFree(src->u.pOleStr);
1548 if (len <= dst_len)
1550 dest[0] = 0;
1551 return E_NOT_SUFFICIENT_BUFFER;
1553 break;
1556 case STRRET_CSTR:
1557 if (!MultiByteToWideChar( CP_ACP, 0, src->u.cStr, -1, dest, len ))
1558 dest[len-1] = 0;
1559 break;
1561 case STRRET_OFFSET:
1562 if (pidl)
1564 if (!MultiByteToWideChar( CP_ACP, 0, ((LPCSTR)&pidl->mkid)+src->u.uOffset, -1,
1565 dest, len ))
1566 dest[len-1] = 0;
1568 break;
1570 default:
1571 FIXME("unknown type!\n");
1572 return E_NOTIMPL;
1575 return S_OK;
1578 /*************************************************************************
1579 * StrRetToStrA [SHLWAPI.@]
1581 * Converts a STRRET to a normal string.
1583 * PARAMS
1584 * lpStrRet [O] STRRET to convert
1585 * pidl [I] ITEMIDLIST for lpStrRet->uType == STRRET_OFFSET
1586 * ppszName [O] Destination for converted string
1588 * RETURNS
1589 * Success: S_OK. ppszName contains the new string, allocated with CoTaskMemAlloc().
1590 * Failure: E_FAIL, if any parameters are invalid.
1592 HRESULT WINAPI StrRetToStrA(LPSTRRET lpStrRet, const ITEMIDLIST *pidl, LPSTR *ppszName)
1594 HRESULT hRet = E_FAIL;
1596 switch (lpStrRet->uType)
1598 case STRRET_WSTR:
1599 hRet = _SHStrDupAW(lpStrRet->u.pOleStr, ppszName);
1600 CoTaskMemFree(lpStrRet->u.pOleStr);
1601 break;
1603 case STRRET_CSTR:
1604 hRet = _SHStrDupAA(lpStrRet->u.cStr, ppszName);
1605 break;
1607 case STRRET_OFFSET:
1608 hRet = _SHStrDupAA(((LPCSTR)&pidl->mkid) + lpStrRet->u.uOffset, ppszName);
1609 break;
1611 default:
1612 *ppszName = NULL;
1615 return hRet;
1618 /*************************************************************************
1619 * StrRetToStrW [SHLWAPI.@]
1621 * See StrRetToStrA.
1623 HRESULT WINAPI StrRetToStrW(LPSTRRET lpStrRet, const ITEMIDLIST *pidl, LPWSTR *ppszName)
1625 HRESULT hRet = E_FAIL;
1627 switch (lpStrRet->uType)
1629 case STRRET_WSTR:
1630 hRet = SHStrDupW(lpStrRet->u.pOleStr, ppszName);
1631 CoTaskMemFree(lpStrRet->u.pOleStr);
1632 break;
1634 case STRRET_CSTR:
1635 hRet = SHStrDupA(lpStrRet->u.cStr, ppszName);
1636 break;
1638 case STRRET_OFFSET:
1639 hRet = SHStrDupA(((LPCSTR)&pidl->mkid) + lpStrRet->u.uOffset, ppszName);
1640 break;
1642 default:
1643 *ppszName = NULL;
1646 return hRet;
1649 /* Create an ASCII string copy using SysAllocString() */
1650 static HRESULT _SHStrDupAToBSTR(LPCSTR src, BSTR *pBstrOut)
1652 *pBstrOut = NULL;
1654 if (src)
1656 INT len = MultiByteToWideChar(CP_ACP, 0, src, -1, NULL, 0);
1657 WCHAR* szTemp = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1659 if (szTemp)
1661 MultiByteToWideChar(CP_ACP, 0, src, -1, szTemp, len);
1662 *pBstrOut = SysAllocString(szTemp);
1663 HeapFree(GetProcessHeap(), 0, szTemp);
1665 if (*pBstrOut)
1666 return S_OK;
1669 return E_OUTOFMEMORY;
1672 /*************************************************************************
1673 * StrRetToBSTR [SHLWAPI.@]
1675 * Converts a STRRET to a BSTR.
1677 * PARAMS
1678 * lpStrRet [O] STRRET to convert
1679 * pidl [I] ITEMIDLIST for lpStrRet->uType = STRRET_OFFSET
1680 * pBstrOut [O] Destination for converted BSTR
1682 * RETURNS
1683 * Success: S_OK. pBstrOut contains the new string.
1684 * Failure: E_FAIL, if any parameters are invalid.
1686 HRESULT WINAPI StrRetToBSTR(STRRET *lpStrRet, LPCITEMIDLIST pidl, BSTR* pBstrOut)
1688 HRESULT hRet = E_FAIL;
1690 switch (lpStrRet->uType)
1692 case STRRET_WSTR:
1693 *pBstrOut = SysAllocString(lpStrRet->u.pOleStr);
1694 if (*pBstrOut)
1695 hRet = S_OK;
1696 CoTaskMemFree(lpStrRet->u.pOleStr);
1697 break;
1699 case STRRET_CSTR:
1700 hRet = _SHStrDupAToBSTR(lpStrRet->u.cStr, pBstrOut);
1701 break;
1703 case STRRET_OFFSET:
1704 hRet = _SHStrDupAToBSTR(((LPCSTR)&pidl->mkid) + lpStrRet->u.uOffset, pBstrOut);
1705 break;
1707 default:
1708 *pBstrOut = NULL;
1711 return hRet;
1714 /*************************************************************************
1715 * StrFormatKBSizeA [SHLWAPI.@]
1717 * Create a formatted string containing a byte count in Kilobytes.
1719 * PARAMS
1720 * llBytes [I] Byte size to format
1721 * lpszDest [I] Destination for formatted string
1722 * cchMax [I] Size of lpszDest
1724 * RETURNS
1725 * lpszDest.
1727 LPSTR WINAPI StrFormatKBSizeA(LONGLONG llBytes, LPSTR lpszDest, UINT cchMax)
1729 WCHAR wszBuf[256];
1731 if (!StrFormatKBSizeW(llBytes, wszBuf, 256))
1732 return NULL;
1733 if (!WideCharToMultiByte(CP_ACP, 0, wszBuf, -1, lpszDest, cchMax, NULL, NULL))
1734 return NULL;
1735 return lpszDest;
1738 /*************************************************************************
1739 * StrFormatKBSizeW [SHLWAPI.@]
1741 * See StrFormatKBSizeA.
1743 LPWSTR WINAPI StrFormatKBSizeW(LONGLONG llBytes, LPWSTR lpszDest, UINT cchMax)
1745 static const WCHAR kb[] = {' ','K','B',0};
1746 LONGLONG llKB = (llBytes + 1023) >> 10;
1747 int len;
1749 TRACE("(0x%s,%p,%d)\n", wine_dbgstr_longlong(llBytes), lpszDest, cchMax);
1751 if (!FormatInt(llKB, lpszDest, cchMax))
1752 return NULL;
1754 len = lstrlenW(lpszDest);
1755 if (cchMax - len < 4)
1756 return NULL;
1757 lstrcatW(lpszDest, kb);
1758 return lpszDest;
1761 /*************************************************************************
1762 * StrNCatA [SHLWAPI.@]
1764 * Concatenate two strings together.
1766 * PARAMS
1767 * lpszStr [O] String to concatenate to
1768 * lpszCat [I] String to add to lpszCat
1769 * cchMax [I] Maximum number of characters to concatenate
1771 * RETURNS
1772 * lpszStr.
1774 * NOTES
1775 * cchMax determines the number of characters that are appended to lpszStr,
1776 * not the total length of the string.
1778 LPSTR WINAPI StrNCatA(LPSTR lpszStr, LPCSTR lpszCat, INT cchMax)
1780 LPSTR lpszRet = lpszStr;
1782 TRACE("(%s,%s,%i)\n", debugstr_a(lpszStr), debugstr_a(lpszCat), cchMax);
1784 if (!lpszStr)
1786 WARN("Invalid lpszStr would crash under Win32!\n");
1787 return NULL;
1790 StrCpyNA(lpszStr + strlen(lpszStr), lpszCat, cchMax);
1791 return lpszRet;
1794 /*************************************************************************
1795 * StrNCatW [SHLWAPI.@]
1797 * See StrNCatA.
1799 LPWSTR WINAPI StrNCatW(LPWSTR lpszStr, LPCWSTR lpszCat, INT cchMax)
1801 LPWSTR lpszRet = lpszStr;
1803 TRACE("(%s,%s,%i)\n", debugstr_w(lpszStr), debugstr_w(lpszCat), cchMax);
1805 if (!lpszStr)
1807 WARN("Invalid lpszStr would crash under Win32\n");
1808 return NULL;
1811 StrCpyNW(lpszStr + strlenW(lpszStr), lpszCat, cchMax);
1812 return lpszRet;
1815 /*************************************************************************
1816 * StrTrimA [SHLWAPI.@]
1818 * Remove characters from the start and end of a string.
1820 * PARAMS
1821 * lpszStr [O] String to remove characters from
1822 * lpszTrim [I] Characters to remove from lpszStr
1824 * RETURNS
1825 * TRUE If lpszStr was valid and modified
1826 * FALSE Otherwise
1828 BOOL WINAPI StrTrimA(LPSTR lpszStr, LPCSTR lpszTrim)
1830 DWORD dwLen;
1831 LPSTR lpszRead = lpszStr;
1832 BOOL bRet = FALSE;
1834 TRACE("(%s,%s)\n", debugstr_a(lpszStr), debugstr_a(lpszTrim));
1836 if (lpszRead && *lpszRead)
1838 while (*lpszRead && StrChrA(lpszTrim, *lpszRead))
1839 lpszRead = CharNextA(lpszRead); /* Skip leading matches */
1841 dwLen = strlen(lpszRead);
1843 if (lpszRead != lpszStr)
1845 memmove(lpszStr, lpszRead, dwLen + 1);
1846 bRet = TRUE;
1848 if (dwLen > 0)
1850 lpszRead = lpszStr + dwLen;
1851 while (StrChrA(lpszTrim, lpszRead[-1]))
1852 lpszRead = CharPrevA(lpszStr, lpszRead); /* Skip trailing matches */
1854 if (lpszRead != lpszStr + dwLen)
1856 *lpszRead = '\0';
1857 bRet = TRUE;
1861 return bRet;
1864 /*************************************************************************
1865 * StrTrimW [SHLWAPI.@]
1867 * See StrTrimA.
1869 BOOL WINAPI StrTrimW(LPWSTR lpszStr, LPCWSTR lpszTrim)
1871 DWORD dwLen;
1872 LPWSTR lpszRead = lpszStr;
1873 BOOL bRet = FALSE;
1875 TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszTrim));
1877 if (lpszRead && *lpszRead)
1879 while (*lpszRead && StrChrW(lpszTrim, *lpszRead)) lpszRead++;
1881 dwLen = strlenW(lpszRead);
1883 if (lpszRead != lpszStr)
1885 memmove(lpszStr, lpszRead, (dwLen + 1) * sizeof(WCHAR));
1886 bRet = TRUE;
1888 if (dwLen > 0)
1890 lpszRead = lpszStr + dwLen;
1891 while (StrChrW(lpszTrim, lpszRead[-1]))
1892 lpszRead--; /* Skip trailing matches */
1894 if (lpszRead != lpszStr + dwLen)
1896 *lpszRead = '\0';
1897 bRet = TRUE;
1901 return bRet;
1904 /*************************************************************************
1905 * _SHStrDupAA [INTERNAL]
1907 * Duplicates a ASCII string to ASCII. The destination buffer is allocated.
1909 static HRESULT _SHStrDupAA(LPCSTR src, LPSTR * dest)
1911 HRESULT hr;
1912 int len = 0;
1914 if (src) {
1915 len = lstrlenA(src) + 1;
1916 *dest = CoTaskMemAlloc(len);
1917 } else {
1918 *dest = NULL;
1921 if (*dest) {
1922 lstrcpynA(*dest,src, len);
1923 hr = S_OK;
1924 } else {
1925 hr = E_OUTOFMEMORY;
1928 TRACE("%s->(%p)\n", debugstr_a(src), *dest);
1929 return hr;
1932 /*************************************************************************
1933 * SHStrDupA [SHLWAPI.@]
1935 * Return a Unicode copy of a string, in memory allocated by CoTaskMemAlloc().
1937 * PARAMS
1938 * lpszStr [I] String to copy
1939 * lppszDest [O] Destination for the new string copy
1941 * RETURNS
1942 * Success: S_OK. lppszDest contains the new string in Unicode format.
1943 * Failure: E_OUTOFMEMORY, If any arguments are invalid or memory allocation
1944 * fails.
1946 HRESULT WINAPI SHStrDupA(LPCSTR lpszStr, LPWSTR * lppszDest)
1948 HRESULT hRet;
1949 int len = 0;
1951 if (lpszStr)
1953 len = MultiByteToWideChar(CP_ACP, 0, lpszStr, -1, NULL, 0) * sizeof(WCHAR);
1954 *lppszDest = CoTaskMemAlloc(len);
1956 else
1957 *lppszDest = NULL;
1959 if (*lppszDest)
1961 MultiByteToWideChar(CP_ACP, 0, lpszStr, -1, *lppszDest, len/sizeof(WCHAR));
1962 hRet = S_OK;
1964 else
1965 hRet = E_OUTOFMEMORY;
1967 TRACE("%s->(%p)\n", debugstr_a(lpszStr), *lppszDest);
1968 return hRet;
1971 /*************************************************************************
1972 * _SHStrDupAW [INTERNAL]
1974 * Duplicates a UNICODE to a ASCII string. The destination buffer is allocated.
1976 static HRESULT _SHStrDupAW(LPCWSTR src, LPSTR * dest)
1978 HRESULT hr;
1979 int len = 0;
1981 if (src) {
1982 len = WideCharToMultiByte(CP_ACP, 0, src, -1, NULL, 0, NULL, NULL);
1983 *dest = CoTaskMemAlloc(len);
1984 } else {
1985 *dest = NULL;
1988 if (*dest) {
1989 WideCharToMultiByte(CP_ACP, 0, src, -1, *dest, len, NULL, NULL);
1990 hr = S_OK;
1991 } else {
1992 hr = E_OUTOFMEMORY;
1995 TRACE("%s->(%p)\n", debugstr_w(src), *dest);
1996 return hr;
1999 /*************************************************************************
2000 * SHStrDupW [SHLWAPI.@]
2002 * See SHStrDupA.
2004 HRESULT WINAPI SHStrDupW(LPCWSTR src, LPWSTR * dest)
2006 HRESULT hr;
2007 int len = 0;
2009 if (src) {
2010 len = (lstrlenW(src) + 1) * sizeof(WCHAR);
2011 *dest = CoTaskMemAlloc(len);
2012 } else {
2013 *dest = NULL;
2016 if (*dest) {
2017 memcpy(*dest, src, len);
2018 hr = S_OK;
2019 } else {
2020 hr = E_OUTOFMEMORY;
2023 TRACE("%s->(%p)\n", debugstr_w(src), *dest);
2024 return hr;
2027 /*************************************************************************
2028 * SHLWAPI_WriteReverseNum
2030 * Internal helper for SHLWAPI_WriteTimeClass.
2032 static inline LPWSTR SHLWAPI_WriteReverseNum(LPWSTR lpszOut, DWORD dwNum)
2034 *lpszOut-- = '\0';
2036 /* Write a decimal number to a string, backwards */
2039 DWORD dwNextDigit = dwNum % 10;
2040 *lpszOut-- = '0' + dwNextDigit;
2041 dwNum = (dwNum - dwNextDigit) / 10;
2042 } while (dwNum > 0);
2044 return lpszOut;
2047 /*************************************************************************
2048 * SHLWAPI_FormatSignificant
2050 * Internal helper for SHLWAPI_WriteTimeClass.
2052 static inline int SHLWAPI_FormatSignificant(LPWSTR lpszNum, int dwDigits)
2054 /* Zero non significant digits, return remaining significant digits */
2055 while (*lpszNum)
2057 lpszNum++;
2058 if (--dwDigits == 0)
2060 while (*lpszNum)
2061 *lpszNum++ = '0';
2062 return 0;
2065 return dwDigits;
2068 /*************************************************************************
2069 * SHLWAPI_WriteTimeClass
2071 * Internal helper for StrFromTimeIntervalW.
2073 static int SHLWAPI_WriteTimeClass(LPWSTR lpszOut, DWORD dwValue,
2074 UINT uClassStringId, int iDigits)
2076 WCHAR szBuff[64], *szOut = szBuff + 32;
2078 szOut = SHLWAPI_WriteReverseNum(szOut, dwValue);
2079 iDigits = SHLWAPI_FormatSignificant(szOut + 1, iDigits);
2080 *szOut = ' ';
2081 LoadStringW(shlwapi_hInstance, uClassStringId, szBuff + 32, 32);
2082 strcatW(lpszOut, szOut);
2083 return iDigits;
2086 /*************************************************************************
2087 * StrFromTimeIntervalA [SHLWAPI.@]
2089 * Format a millisecond time interval into a string
2091 * PARAMS
2092 * lpszStr [O] Output buffer for formatted time interval
2093 * cchMax [I] Size of lpszStr
2094 * dwMS [I] Number of milliseconds
2095 * iDigits [I] Number of digits to print
2097 * RETURNS
2098 * The length of the formatted string, or 0 if any parameter is invalid.
2100 * NOTES
2101 * This implementation mimics the Win32 behaviour of always writing a leading
2102 * space before the time interval begins.
2104 * iDigits is used to provide approximate times if accuracy is not important.
2105 * This number of digits will be written of the first non-zero time class
2106 * (hours/minutes/seconds). If this does not complete the time classification,
2107 * the remaining digits are changed to zeros (i.e. The time is _not_ rounded).
2108 * If there are digits remaining following the writing of a time class, the
2109 * next time class will be written.
2111 * For example, given dwMS represents 138 hours,43 minutes and 15 seconds, the
2112 * following will result from the given values of iDigits:
2114 *| iDigits 1 2 3 4 5 ...
2115 *| lpszStr "100 hr" "130 hr" "138 hr" "138 hr 40 min" "138 hr 43 min" ...
2117 INT WINAPI StrFromTimeIntervalA(LPSTR lpszStr, UINT cchMax, DWORD dwMS,
2118 int iDigits)
2120 INT iRet = 0;
2122 TRACE("(%p,%d,%d,%d)\n", lpszStr, cchMax, dwMS, iDigits);
2124 if (lpszStr && cchMax)
2126 WCHAR szBuff[128];
2127 StrFromTimeIntervalW(szBuff, sizeof(szBuff)/sizeof(WCHAR), dwMS, iDigits);
2128 WideCharToMultiByte(CP_ACP,0,szBuff,-1,lpszStr,cchMax,0,0);
2130 return iRet;
2134 /*************************************************************************
2135 * StrFromTimeIntervalW [SHLWAPI.@]
2137 * See StrFromTimeIntervalA.
2139 INT WINAPI StrFromTimeIntervalW(LPWSTR lpszStr, UINT cchMax, DWORD dwMS,
2140 int iDigits)
2142 INT iRet = 0;
2144 TRACE("(%p,%d,%d,%d)\n", lpszStr, cchMax, dwMS, iDigits);
2146 if (lpszStr && cchMax)
2148 WCHAR szCopy[128];
2149 DWORD dwHours, dwMinutes;
2151 if (!iDigits || cchMax == 1)
2153 *lpszStr = '\0';
2154 return 0;
2157 /* Calculate the time classes */
2158 dwMS = (dwMS + 500) / 1000;
2159 dwHours = dwMS / 3600;
2160 dwMS -= dwHours * 3600;
2161 dwMinutes = dwMS / 60;
2162 dwMS -= dwMinutes * 60;
2164 szCopy[0] = '\0';
2166 if (dwHours)
2167 iDigits = SHLWAPI_WriteTimeClass(szCopy, dwHours, IDS_TIME_INTERVAL_HOURS, iDigits);
2169 if (dwMinutes && iDigits)
2170 iDigits = SHLWAPI_WriteTimeClass(szCopy, dwMinutes, IDS_TIME_INTERVAL_MINUTES, iDigits);
2172 if (iDigits) /* Always write seconds if we have significant digits */
2173 SHLWAPI_WriteTimeClass(szCopy, dwMS, IDS_TIME_INTERVAL_SECONDS, iDigits);
2175 lstrcpynW(lpszStr, szCopy, cchMax);
2176 iRet = strlenW(lpszStr);
2178 return iRet;
2181 /*************************************************************************
2182 * StrIsIntlEqualA [SHLWAPI.@]
2184 * Compare two strings.
2186 * PARAMS
2187 * bCase [I] Whether to compare case sensitively
2188 * lpszStr [I] First string to compare
2189 * lpszComp [I] Second string to compare
2190 * iLen [I] Length to compare
2192 * RETURNS
2193 * TRUE If the strings are equal.
2194 * FALSE Otherwise.
2196 BOOL WINAPI StrIsIntlEqualA(BOOL bCase, LPCSTR lpszStr, LPCSTR lpszComp,
2197 int iLen)
2199 DWORD dwFlags;
2201 TRACE("(%d,%s,%s,%d)\n", bCase,
2202 debugstr_a(lpszStr), debugstr_a(lpszComp), iLen);
2204 /* FIXME: This flag is undocumented and unknown by our CompareString.
2205 * We need a define for it.
2207 dwFlags = 0x10000000;
2208 if (!bCase) dwFlags |= NORM_IGNORECASE;
2210 return (CompareStringA(GetThreadLocale(), dwFlags, lpszStr, iLen, lpszComp, iLen) == CSTR_EQUAL);
2213 /*************************************************************************
2214 * StrIsIntlEqualW [SHLWAPI.@]
2216 * See StrIsIntlEqualA.
2218 BOOL WINAPI StrIsIntlEqualW(BOOL bCase, LPCWSTR lpszStr, LPCWSTR lpszComp,
2219 int iLen)
2221 DWORD dwFlags;
2223 TRACE("(%d,%s,%s,%d)\n", bCase,
2224 debugstr_w(lpszStr),debugstr_w(lpszComp), iLen);
2226 /* FIXME: This flag is undocumented and unknown by our CompareString.
2227 * We need a define for it.
2229 dwFlags = 0x10000000;
2230 if (!bCase) dwFlags |= NORM_IGNORECASE;
2232 return (CompareStringW(GetThreadLocale(), dwFlags, lpszStr, iLen, lpszComp, iLen) == CSTR_EQUAL);
2235 /*************************************************************************
2236 * @ [SHLWAPI.399]
2238 * Copy a string to another string, up to a maximum number of characters.
2240 * PARAMS
2241 * lpszDest [O] Destination string
2242 * lpszSrc [I] Source string
2243 * iLen [I] Maximum number of chars to copy
2245 * RETURNS
2246 * Success: A pointer to the last character written to lpszDest.
2247 * Failure: lpszDest, if any arguments are invalid.
2249 LPSTR WINAPI StrCpyNXA(LPSTR lpszDest, LPCSTR lpszSrc, int iLen)
2251 TRACE("(%p,%s,%i)\n", lpszDest, debugstr_a(lpszSrc), iLen);
2253 if (lpszDest && lpszSrc && iLen > 0)
2255 while ((iLen-- > 1) && *lpszSrc)
2256 *lpszDest++ = *lpszSrc++;
2257 if (iLen >= 0)
2258 *lpszDest = '\0';
2260 return lpszDest;
2263 /*************************************************************************
2264 * @ [SHLWAPI.400]
2266 * Unicode version of StrCpyNXA.
2268 LPWSTR WINAPI StrCpyNXW(LPWSTR lpszDest, LPCWSTR lpszSrc, int iLen)
2270 TRACE("(%p,%s,%i)\n", lpszDest, debugstr_w(lpszSrc), iLen);
2272 if (lpszDest && lpszSrc && iLen > 0)
2274 while ((iLen-- > 1) && *lpszSrc)
2275 *lpszDest++ = *lpszSrc++;
2276 if (iLen >= 0)
2277 *lpszDest = '\0';
2279 return lpszDest;
2282 /*************************************************************************
2283 * StrCmpLogicalW [SHLWAPI.@]
2285 * Compare two strings, ignoring case and comparing digits as numbers.
2287 * PARAMS
2288 * lpszStr [I] First string to compare
2289 * lpszComp [I] Second string to compare
2290 * iLen [I] Length to compare
2292 * RETURNS
2293 * TRUE If the strings are equal.
2294 * FALSE Otherwise.
2296 INT WINAPI StrCmpLogicalW(LPCWSTR lpszStr, LPCWSTR lpszComp)
2298 INT iDiff;
2300 TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszComp));
2302 if (lpszStr && lpszComp)
2304 while (*lpszStr)
2306 if (!*lpszComp)
2307 return 1;
2308 else if (isdigitW(*lpszStr))
2310 int iStr, iComp;
2312 if (!isdigitW(*lpszComp))
2313 return -1;
2315 /* Compare the numbers */
2316 StrToIntExW(lpszStr, 0, &iStr);
2317 StrToIntExW(lpszComp, 0, &iComp);
2319 if (iStr < iComp)
2320 return -1;
2321 else if (iStr > iComp)
2322 return 1;
2324 /* Skip */
2325 while (isdigitW(*lpszStr))
2326 lpszStr++;
2327 while (isdigitW(*lpszComp))
2328 lpszComp++;
2330 else if (isdigitW(*lpszComp))
2331 return 1;
2332 else
2334 iDiff = ChrCmpIW(*lpszStr,*lpszComp);
2335 if (iDiff > 0)
2336 return 1;
2337 else if (iDiff < 0)
2338 return -1;
2340 lpszStr++;
2341 lpszComp++;
2344 if (*lpszComp)
2345 return -1;
2347 return 0;
2350 /* Structure for formatting byte strings */
2351 typedef struct tagSHLWAPI_BYTEFORMATS
2353 LONGLONG dLimit;
2354 double dDivisor;
2355 double dNormaliser;
2356 int nDecimals;
2357 WCHAR wPrefix;
2358 } SHLWAPI_BYTEFORMATS;
2360 /*************************************************************************
2361 * StrFormatByteSizeW [SHLWAPI.@]
2363 * Create a string containing an abbreviated byte count of up to 2^63-1.
2365 * PARAMS
2366 * llBytes [I] Byte size to format
2367 * lpszDest [I] Destination for formatted string
2368 * cchMax [I] Size of lpszDest
2370 * RETURNS
2371 * lpszDest.
2373 * NOTES
2374 * There is no StrFormatByteSize64W function, it is called StrFormatByteSizeW().
2376 LPWSTR WINAPI StrFormatByteSizeW(LONGLONG llBytes, LPWSTR lpszDest, UINT cchMax)
2378 #define KB ((ULONGLONG)1024)
2379 #define MB (KB*KB)
2380 #define GB (KB*KB*KB)
2381 #define TB (KB*KB*KB*KB)
2382 #define PB (KB*KB*KB*KB*KB)
2384 static const SHLWAPI_BYTEFORMATS bfFormats[] =
2386 { 10*KB, 10.24, 100.0, 2, 'K' }, /* 10 KB */
2387 { 100*KB, 102.4, 10.0, 1, 'K' }, /* 100 KB */
2388 { 1000*KB, 1024.0, 1.0, 0, 'K' }, /* 1000 KB */
2389 { 10*MB, 10485.76, 100.0, 2, 'M' }, /* 10 MB */
2390 { 100*MB, 104857.6, 10.0, 1, 'M' }, /* 100 MB */
2391 { 1000*MB, 1048576.0, 1.0, 0, 'M' }, /* 1000 MB */
2392 { 10*GB, 10737418.24, 100.0, 2, 'G' }, /* 10 GB */
2393 { 100*GB, 107374182.4, 10.0, 1, 'G' }, /* 100 GB */
2394 { 1000*GB, 1073741824.0, 1.0, 0, 'G' }, /* 1000 GB */
2395 { 10*TB, 10485.76, 100.0, 2, 'T' }, /* 10 TB */
2396 { 100*TB, 104857.6, 10.0, 1, 'T' }, /* 100 TB */
2397 { 1000*TB, 1048576.0, 1.0, 0, 'T' }, /* 1000 TB */
2398 { 10*PB, 10737418.24, 100.00, 2, 'P' }, /* 10 PB */
2399 { 100*PB, 107374182.4, 10.00, 1, 'P' }, /* 100 PB */
2400 { 1000*PB, 1073741824.0, 1.00, 0, 'P' }, /* 1000 PB */
2401 { 0, 10995116277.76, 100.00, 2, 'E' } /* EB's, catch all */
2403 WCHAR wszAdd[] = {' ','?','B',0};
2404 double dBytes;
2405 UINT i = 0;
2407 TRACE("(0x%s,%p,%d)\n", wine_dbgstr_longlong(llBytes), lpszDest, cchMax);
2409 if (!lpszDest || !cchMax)
2410 return lpszDest;
2412 if (llBytes < 1024) /* 1K */
2414 WCHAR wszBytesFormat[64];
2415 LoadStringW(shlwapi_hInstance, IDS_BYTES_FORMAT, wszBytesFormat, 64);
2416 snprintfW(lpszDest, cchMax, wszBytesFormat, (int)llBytes);
2417 return lpszDest;
2420 /* Note that if this loop completes without finding a match, i will be
2421 * pointing at the last entry, which is a catch all for > 1000 PB
2423 while (i < sizeof(bfFormats) / sizeof(SHLWAPI_BYTEFORMATS) - 1)
2425 if (llBytes < bfFormats[i].dLimit)
2426 break;
2427 i++;
2429 /* Above 1 TB we encounter problems with FP accuracy. So for amounts above
2430 * this number we integer shift down by 1 MB first. The table above has
2431 * the divisors scaled down from the '< 10 TB' entry onwards, to account
2432 * for this. We also add a small fudge factor to get the correct result for
2433 * counts that lie exactly on a 1024 byte boundary.
2435 if (i > 8)
2436 dBytes = (double)(llBytes >> 20) + 0.001; /* Scale down by 1 MB */
2437 else
2438 dBytes = (double)llBytes + 0.00001;
2440 dBytes = floor(dBytes / bfFormats[i].dDivisor) / bfFormats[i].dNormaliser;
2442 if (!FormatDouble(dBytes, bfFormats[i].nDecimals, lpszDest, cchMax))
2443 return NULL;
2444 wszAdd[1] = bfFormats[i].wPrefix;
2445 StrCatBuffW(lpszDest, wszAdd, cchMax);
2446 return lpszDest;
2449 /*************************************************************************
2450 * StrFormatByteSize64A [SHLWAPI.@]
2452 * See StrFormatByteSizeW.
2454 LPSTR WINAPI StrFormatByteSize64A(LONGLONG llBytes, LPSTR lpszDest, UINT cchMax)
2456 WCHAR wszBuff[32];
2458 StrFormatByteSizeW(llBytes, wszBuff, sizeof(wszBuff)/sizeof(WCHAR));
2460 if (lpszDest)
2461 WideCharToMultiByte(CP_ACP, 0, wszBuff, -1, lpszDest, cchMax, 0, 0);
2462 return lpszDest;
2465 /*************************************************************************
2466 * StrFormatByteSizeA [SHLWAPI.@]
2468 * Create a string containing an abbreviated byte count of up to 2^31-1.
2470 * PARAMS
2471 * dwBytes [I] Byte size to format
2472 * lpszDest [I] Destination for formatted string
2473 * cchMax [I] Size of lpszDest
2475 * RETURNS
2476 * lpszDest.
2478 * NOTES
2479 * The Ascii and Unicode versions of this function accept a different
2480 * integer type for dwBytes. See StrFormatByteSize64A().
2482 LPSTR WINAPI StrFormatByteSizeA(DWORD dwBytes, LPSTR lpszDest, UINT cchMax)
2484 TRACE("(%d,%p,%d)\n", dwBytes, lpszDest, cchMax);
2486 return StrFormatByteSize64A(dwBytes, lpszDest, cchMax);
2489 /*************************************************************************
2490 * @ [SHLWAPI.162]
2492 * Remove a hanging lead byte from the end of a string, if present.
2494 * PARAMS
2495 * lpStr [I] String to check for a hanging lead byte
2496 * size [I] Length of lpStr
2498 * RETURNS
2499 * Success: The new length of the string. Any hanging lead bytes are removed.
2500 * Failure: 0, if any parameters are invalid.
2502 DWORD WINAPI SHTruncateString(LPSTR lpStr, DWORD size)
2504 if (lpStr && size)
2506 LPSTR lastByte = lpStr + size - 1;
2508 while(lpStr < lastByte)
2509 lpStr += IsDBCSLeadByte(*lpStr) ? 2 : 1;
2511 if(lpStr == lastByte && IsDBCSLeadByte(*lpStr))
2513 *lpStr = '\0';
2514 size--;
2516 return size;
2518 return 0;
2521 /*************************************************************************
2522 * @ [SHLWAPI.203]
2524 * Remove a single non-trailing ampersand ('&') from a string.
2526 * PARAMS
2527 * lpszStr [I/O] String to remove ampersand from.
2529 * RETURNS
2530 * The character after the first ampersand in lpszStr, or the first character
2531 * in lpszStr if there is no ampersand in the string.
2533 char WINAPI SHStripMneumonicA(LPCSTR lpszStr)
2535 LPSTR lpszIter, lpszTmp;
2536 char ch;
2538 TRACE("(%s)\n", debugstr_a(lpszStr));
2540 ch = *lpszStr;
2542 if ((lpszIter = StrChrA(lpszStr, '&')))
2544 lpszTmp = CharNextA(lpszIter);
2545 if (*lpszTmp)
2547 if (*lpszTmp != '&')
2548 ch = *lpszTmp;
2550 memmove( lpszIter, lpszTmp, strlen(lpszTmp) + 1 );
2554 return ch;
2557 /*************************************************************************
2558 * @ [SHLWAPI.225]
2560 * Unicode version of SHStripMneumonicA.
2562 WCHAR WINAPI SHStripMneumonicW(LPCWSTR lpszStr)
2564 LPWSTR lpszIter, lpszTmp;
2565 WCHAR ch;
2567 TRACE("(%s)\n", debugstr_w(lpszStr));
2569 ch = *lpszStr;
2571 if ((lpszIter = StrChrW(lpszStr, '&')))
2573 lpszTmp = lpszIter + 1;
2574 if (*lpszTmp)
2576 if (*lpszTmp != '&')
2577 ch = *lpszTmp;
2579 memmove( lpszIter, lpszTmp, (strlenW(lpszTmp) + 1) * sizeof(WCHAR) );
2583 return ch;
2586 /*************************************************************************
2587 * @ [SHLWAPI.216]
2589 * Convert an Ascii string to Unicode.
2591 * PARAMS
2592 * dwCp [I] Code page for the conversion
2593 * lpSrcStr [I] Source Ascii string to convert
2594 * lpDstStr [O] Destination for converted Unicode string
2595 * iLen [I] Length of lpDstStr
2597 * RETURNS
2598 * The return value of the MultiByteToWideChar() function called on lpSrcStr.
2600 DWORD WINAPI SHAnsiToUnicodeCP(DWORD dwCp, LPCSTR lpSrcStr, LPWSTR lpDstStr, int iLen)
2602 DWORD dwRet;
2604 dwRet = MultiByteToWideChar(dwCp, 0, lpSrcStr, -1, lpDstStr, iLen);
2605 TRACE("%s->%s,ret=%d\n", debugstr_a(lpSrcStr), debugstr_w(lpDstStr), dwRet);
2606 return dwRet;
2609 /*************************************************************************
2610 * @ [SHLWAPI.215]
2612 * Convert an Ascii string to Unicode.
2614 * PARAMS
2615 * lpSrcStr [I] Source Ascii string to convert
2616 * lpDstStr [O] Destination for converted Unicode string
2617 * iLen [I] Length of lpDstStr
2619 * RETURNS
2620 * The return value of the MultiByteToWideChar() function called on lpSrcStr.
2622 * NOTES
2623 * This function simply calls SHAnsiToUnicodeCP with code page CP_ACP.
2625 DWORD WINAPI SHAnsiToUnicode(LPCSTR lpSrcStr, LPWSTR lpDstStr, int iLen)
2627 return SHAnsiToUnicodeCP(CP_ACP, lpSrcStr, lpDstStr, iLen);
2630 /*************************************************************************
2631 * @ [SHLWAPI.218]
2633 * Convert a Unicode string to Ascii.
2635 * PARAMS
2636 * CodePage [I] Code page to use for the conversion
2637 * lpSrcStr [I] Source Unicode string to convert
2638 * lpDstStr [O] Destination for converted Ascii string
2639 * dstlen [I] Length of buffer at lpDstStr
2641 * RETURNS
2642 * Success: The length in bytes of the result at lpDstStr (including the terminator)
2643 * Failure: When using CP_UTF8, CP_UTF7 or 0xc350 as codePage, 0 is returned and
2644 * the result is not nul-terminated.
2645 * When using a different codepage, the length in bytes of the truncated
2646 * result at lpDstStr (including the terminator) is returned and
2647 * lpDstStr is always nul-terminated.
2650 DWORD WINAPI SHUnicodeToAnsiCP(UINT CodePage, LPCWSTR lpSrcStr, LPSTR lpDstStr, int dstlen)
2652 static const WCHAR emptyW[] = { '\0' };
2653 int len , reqLen;
2654 LPSTR mem;
2656 if (!lpDstStr || !dstlen)
2657 return 0;
2659 if (!lpSrcStr)
2660 lpSrcStr = emptyW;
2662 *lpDstStr = '\0';
2664 len = strlenW(lpSrcStr) + 1;
2666 switch (CodePage)
2668 case CP_WINUNICODE:
2669 CodePage = CP_UTF8; /* Fall through... */
2670 case 0x0000C350: /* FIXME: CP_ #define */
2671 case CP_UTF7:
2672 case CP_UTF8:
2674 DWORD dwMode = 0;
2675 INT lenW = len - 1;
2676 INT needed = dstlen - 1;
2677 HRESULT hr;
2679 /* try the user supplied buffer first */
2680 hr = ConvertINetUnicodeToMultiByte(&dwMode, CodePage, lpSrcStr, &lenW, lpDstStr, &needed);
2681 if (hr == S_OK)
2683 lpDstStr[needed] = '\0';
2684 return needed + 1;
2687 /* user buffer too small. exclude termination and copy as much as possible */
2688 lenW = len;
2689 hr = ConvertINetUnicodeToMultiByte(&dwMode, CodePage, lpSrcStr, &lenW, NULL, &needed);
2690 needed++;
2691 mem = HeapAlloc(GetProcessHeap(), 0, needed);
2692 if (!mem)
2693 return 0;
2695 hr = ConvertINetUnicodeToMultiByte(&dwMode, CodePage, lpSrcStr, &len, mem, &needed);
2696 if (hr == S_OK)
2698 reqLen = SHTruncateString(mem, dstlen);
2699 if (reqLen > 0) memcpy(lpDstStr, mem, reqLen-1);
2701 HeapFree(GetProcessHeap(), 0, mem);
2702 return 0;
2704 default:
2705 break;
2708 /* try the user supplied buffer first */
2709 reqLen = WideCharToMultiByte(CodePage, 0, lpSrcStr, len, lpDstStr, dstlen, NULL, NULL);
2711 if (!reqLen && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
2713 reqLen = WideCharToMultiByte(CodePage, 0, lpSrcStr, len, NULL, 0, NULL, NULL);
2714 if (reqLen)
2716 mem = HeapAlloc(GetProcessHeap(), 0, reqLen);
2717 if (mem)
2719 WideCharToMultiByte(CodePage, 0, lpSrcStr, len, mem, reqLen, NULL, NULL);
2721 reqLen = SHTruncateString(mem, dstlen -1);
2722 reqLen++;
2724 lstrcpynA(lpDstStr, mem, reqLen);
2725 HeapFree(GetProcessHeap(), 0, mem);
2726 lpDstStr[reqLen-1] = '\0';
2730 return reqLen;
2733 /*************************************************************************
2734 * @ [SHLWAPI.217]
2736 * Convert a Unicode string to Ascii.
2738 * PARAMS
2739 * lpSrcStr [I] Source Unicode string to convert
2740 * lpDstStr [O] Destination for converted Ascii string
2741 * iLen [O] Length of lpDstStr in characters
2743 * RETURNS
2744 * See SHUnicodeToAnsiCP
2746 * NOTES
2747 * This function simply calls SHUnicodeToAnsiCP() with CodePage = CP_ACP.
2749 INT WINAPI SHUnicodeToAnsi(LPCWSTR lpSrcStr, LPSTR lpDstStr, INT iLen)
2751 return SHUnicodeToAnsiCP(CP_ACP, lpSrcStr, lpDstStr, iLen);
2754 /*************************************************************************
2755 * @ [SHLWAPI.345]
2757 * Copy one string to another.
2759 * PARAMS
2760 * lpszSrc [I] Source string to copy
2761 * lpszDst [O] Destination for copy
2762 * iLen [I] Length of lpszDst in characters
2764 * RETURNS
2765 * The length of the copied string, including the terminating NUL. lpszDst
2766 * contains iLen characters of lpszSrc.
2768 DWORD WINAPI SHAnsiToAnsi(LPCSTR lpszSrc, LPSTR lpszDst, int iLen)
2770 LPSTR lpszRet;
2772 TRACE("(%s,%p,0x%08x)\n", debugstr_a(lpszSrc), lpszDst, iLen);
2774 lpszRet = StrCpyNXA(lpszDst, lpszSrc, iLen);
2775 return lpszRet - lpszDst + 1;
2778 /*************************************************************************
2779 * @ [SHLWAPI.346]
2781 * Unicode version of SSHAnsiToAnsi.
2783 DWORD WINAPI SHUnicodeToUnicode(LPCWSTR lpszSrc, LPWSTR lpszDst, int iLen)
2785 LPWSTR lpszRet;
2787 TRACE("(%s,%p,0x%08x)\n", debugstr_w(lpszSrc), lpszDst, iLen);
2789 lpszRet = StrCpyNXW(lpszDst, lpszSrc, iLen);
2790 return lpszRet - lpszDst + 1;
2793 /*************************************************************************
2794 * @ [SHLWAPI.364]
2796 * Determine if an Ascii string converts to Unicode and back identically.
2798 * PARAMS
2799 * lpSrcStr [I] Source Unicode string to convert
2800 * lpDst [O] Destination for resulting Ascii string
2801 * iLen [I] Length of lpDst in characters
2803 * RETURNS
2804 * TRUE, since Ascii strings always convert identically.
2806 BOOL WINAPI DoesStringRoundTripA(LPCSTR lpSrcStr, LPSTR lpDst, INT iLen)
2808 lstrcpynA(lpDst, lpSrcStr, iLen);
2809 return TRUE;
2812 /*************************************************************************
2813 * @ [SHLWAPI.365]
2815 * Determine if a Unicode string converts to Ascii and back identically.
2817 * PARAMS
2818 * lpSrcStr [I] Source Unicode string to convert
2819 * lpDst [O] Destination for resulting Ascii string
2820 * iLen [I] Length of lpDst in characters
2822 * RETURNS
2823 * TRUE, if lpSrcStr converts to Ascii and back identically,
2824 * FALSE otherwise.
2826 BOOL WINAPI DoesStringRoundTripW(LPCWSTR lpSrcStr, LPSTR lpDst, INT iLen)
2828 WCHAR szBuff[MAX_PATH];
2830 SHUnicodeToAnsi(lpSrcStr, lpDst, iLen);
2831 SHAnsiToUnicode(lpDst, szBuff, MAX_PATH);
2832 return !strcmpW(lpSrcStr, szBuff);
2835 /*************************************************************************
2836 * SHLoadIndirectString [SHLWAPI.@]
2838 * If passed a string that begins with '@', extract the string from the
2839 * appropriate resource, otherwise do a straight copy.
2842 HRESULT WINAPI SHLoadIndirectString(LPCWSTR src, LPWSTR dst, UINT dst_len, void **reserved)
2844 WCHAR *dllname = NULL;
2845 HMODULE hmod = NULL;
2846 HRESULT hr = E_FAIL;
2848 TRACE("(%s %p %08x %p)\n", debugstr_w(src), dst, dst_len, reserved);
2850 if(src[0] == '@')
2852 WCHAR *index_str;
2853 int index;
2855 dst[0] = 0;
2856 dllname = StrDupW(src + 1);
2857 index_str = strchrW(dllname, ',');
2859 if(!index_str) goto end;
2861 *index_str = 0;
2862 index_str++;
2863 index = atoiW(index_str);
2865 hmod = LoadLibraryW(dllname);
2866 if(!hmod) goto end;
2868 if(index < 0)
2870 if(LoadStringW(hmod, -index, dst, dst_len))
2871 hr = S_OK;
2873 else
2874 FIXME("can't handle non-negative indices (%d)\n", index);
2876 else
2878 if(dst != src)
2879 lstrcpynW(dst, src, dst_len);
2880 hr = S_OK;
2883 TRACE("returning %s\n", debugstr_w(dst));
2884 end:
2885 if(hmod) FreeLibrary(hmod);
2886 LocalFree(dllname);
2887 return hr;
2890 BOOL WINAPI IsCharSpaceA(CHAR c)
2892 WORD CharType;
2893 return GetStringTypeA(GetSystemDefaultLCID(), CT_CTYPE1, &c, 1, &CharType) && (CharType & C1_SPACE);
2896 /*************************************************************************
2897 * @ [SHLWAPI.29]
2899 * Determine if a Unicode character is a space.
2901 * PARAMS
2902 * wc [I] Character to check.
2904 * RETURNS
2905 * TRUE, if wc is a space,
2906 * FALSE otherwise.
2908 BOOL WINAPI IsCharSpaceW(WCHAR wc)
2910 WORD CharType;
2912 return GetStringTypeW(CT_CTYPE1, &wc, 1, &CharType) && (CharType & C1_SPACE);