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
23 #include "wine/port.h"
30 #define NONAMELESSUNION
31 #define NONAMELESSSTRUCT
34 #define NO_SHLWAPI_REG
35 #define NO_SHLWAPI_STREAM
42 #include "wine/unicode.h"
43 #include "wine/debug.h"
47 WINE_DEFAULT_DEBUG_CHANNEL(shell);
49 extern HINSTANCE shlwapi_hInstance;
51 static HRESULT WINAPI _SHStrDupAA(LPCSTR,LPSTR*);
52 static HRESULT WINAPI _SHStrDupAW(LPCWSTR,LPSTR*);
55 static void FillNumberFmt(NUMBERFMTW *fmt, LPWSTR decimal_buffer, int decimal_bufwlen,
56 LPWSTR thousand_buffer, int thousand_bufwlen)
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));
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;
70 * Converting grouping string to number as described on
71 * http://blogs.msdn.com/oldnewthing/archive/2006/04/18/578251.aspx
74 GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SGROUPING, grouping, sizeof(grouping)/sizeof(WCHAR));
75 for (c = grouping; *c; c++)
76 if (*c >= '0' && *c < '9')
79 fmt->Grouping += *c - '0';
82 if (fmt->Grouping % 10 == 0)
88 /*************************************************************************
89 * FormatInt [internal]
91 * Format an integer according to the current locale
94 * The number of bytes written on success or 0 on failure
96 static int FormatInt(LONGLONG qdwValue, LPWSTR pszBuf, int cchBuf)
99 WCHAR decimal[8], thousand[8];
102 BOOL neg = (qdwValue < 0);
104 FillNumberFmt(&fmt, decimal, sizeof decimal / sizeof (WCHAR),
105 thousand, sizeof thousand / sizeof (WCHAR));
111 *(--c) = '0' + (qdwValue%10);
113 } while (qdwValue > 0);
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
127 * The number of bytes 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};
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.
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 WINAPI 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);
170 str2[0] = LOBYTE(ch2);
171 if (IsDBCSLeadByte(str2[0]))
173 str2[1] = HIBYTE(ch2);
179 return CompareStringA(GetThreadLocale(), dwFlags, str1, -1, str2, -1) - 2;
182 /*************************************************************************
183 * SHLWAPI_ChrCmpHelperW
185 * Internal helper for SHLWAPI_ChrCmpW/ChrCmpIW.
187 static BOOL WINAPI SHLWAPI_ChrCmpHelperW(WCHAR ch1, WCHAR ch2, DWORD dwFlags)
189 WCHAR str1[2], str2[2];
195 return CompareStringW(GetThreadLocale(), dwFlags, str1, 2, str2, 2) - 2;
198 /*************************************************************************
201 * Internal helper function.
203 static BOOL WINAPI SHLWAPI_ChrCmpA(WORD ch1, WORD ch2)
205 return SHLWAPI_ChrCmpHelperA(ch1, ch2, 0);
208 /*************************************************************************
209 * ChrCmpIA (SHLWAPI.385)
211 * Compare two characters, ignoring case.
214 * ch1 [I] First character to compare
215 * ch2 [I] Second character to compare
218 * FALSE, if the characters are equal.
219 * Non-zero otherwise.
221 BOOL WINAPI ChrCmpIA(WORD ch1, WORD ch2)
223 TRACE("(%d,%d)\n", ch1, ch2);
225 return SHLWAPI_ChrCmpHelperA(ch1, ch2, NORM_IGNORECASE);
228 /*************************************************************************
231 * Internal helper function.
233 static BOOL WINAPI SHLWAPI_ChrCmpW(WCHAR ch1, WCHAR ch2)
235 return SHLWAPI_ChrCmpHelperW(ch1, ch2, 0);
238 /*************************************************************************
239 * ChrCmpIW [SHLWAPI.386]
243 BOOL WINAPI ChrCmpIW(WCHAR ch1, WCHAR ch2)
245 return SHLWAPI_ChrCmpHelperW(ch1, ch2, NORM_IGNORECASE);
248 /*************************************************************************
249 * StrChrA [SHLWAPI.@]
251 * Find a given character in a string.
254 * lpszStr [I] String to search in.
255 * ch [I] Character to search for.
258 * Success: A pointer to the first occurrence of ch in lpszStr, or NULL if
260 * Failure: NULL, if any arguments are invalid.
262 LPSTR WINAPI StrChrA(LPCSTR lpszStr, WORD ch)
264 TRACE("(%s,%i)\n", debugstr_a(lpszStr), ch);
270 if (!SHLWAPI_ChrCmpA(*lpszStr, ch))
271 return (LPSTR)lpszStr;
272 lpszStr = CharNextA(lpszStr);
278 /*************************************************************************
279 * StrChrW [SHLWAPI.@]
283 LPWSTR WINAPI StrChrW(LPCWSTR lpszStr, WCHAR ch)
285 LPWSTR lpszRet = NULL;
287 TRACE("(%s,%i)\n", debugstr_w(lpszStr), ch);
290 lpszRet = strchrW(lpszStr, ch);
294 /*************************************************************************
295 * StrChrIA [SHLWAPI.@]
297 * Find a given character in a string, ignoring case.
300 * lpszStr [I] String to search in.
301 * ch [I] Character to search for.
304 * Success: A pointer to the first occurrence of ch in lpszStr, or NULL if
306 * Failure: NULL, if any arguments are invalid.
308 LPSTR WINAPI StrChrIA(LPCSTR lpszStr, WORD ch)
310 TRACE("(%s,%i)\n", debugstr_a(lpszStr), ch);
316 if (!ChrCmpIA(*lpszStr, ch))
317 return (LPSTR)lpszStr;
318 lpszStr = CharNextA(lpszStr);
324 /*************************************************************************
325 * StrChrIW [SHLWAPI.@]
329 LPWSTR WINAPI StrChrIW(LPCWSTR lpszStr, WCHAR ch)
331 TRACE("(%s,%i)\n", debugstr_w(lpszStr), ch);
338 if (toupperW(*lpszStr) == ch)
339 return (LPWSTR)lpszStr;
340 lpszStr = CharNextW(lpszStr);
344 return (LPWSTR)lpszStr;
347 /*************************************************************************
348 * StrCmpIW [SHLWAPI.@]
350 * Compare two strings, ignoring case.
353 * lpszStr [I] First string to compare
354 * lpszComp [I] Second string to compare
357 * An integer less than, equal to or greater than 0, indicating that
358 * lpszStr is less than, the same, or greater than lpszComp.
360 int WINAPI StrCmpIW(LPCWSTR lpszStr, LPCWSTR lpszComp)
364 TRACE("(%s,%s)\n", debugstr_w(lpszStr),debugstr_w(lpszComp));
366 iRet = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, lpszStr, -1, lpszComp, -1);
367 return iRet == CSTR_LESS_THAN ? -1 : iRet == CSTR_GREATER_THAN ? 1 : 0;
370 /*************************************************************************
371 * StrCmpNA [SHLWAPI.@]
373 * Compare two strings, up to a maximum length.
376 * lpszStr [I] First string to compare
377 * lpszComp [I] Second string to compare
378 * iLen [I] Maximum number of chars to compare.
381 * An integer less than, equal to or greater than 0, indicating that
382 * lpszStr is less than, the same, or greater than lpszComp.
384 INT WINAPI StrCmpNA(LPCSTR lpszStr, LPCSTR lpszComp, INT iLen)
388 TRACE("(%s,%s,%i)\n", debugstr_a(lpszStr), debugstr_a(lpszComp), iLen);
390 iRet = CompareStringA(GetThreadLocale(), 0, lpszStr, iLen, lpszComp, iLen);
391 return iRet == CSTR_LESS_THAN ? -1 : iRet == CSTR_GREATER_THAN ? 1 : 0;
394 /*************************************************************************
395 * StrCmpNW [SHLWAPI.@]
399 INT WINAPI StrCmpNW(LPCWSTR lpszStr, LPCWSTR lpszComp, INT iLen)
403 TRACE("(%s,%s,%i)\n", debugstr_w(lpszStr), debugstr_w(lpszComp), iLen);
405 iRet = CompareStringW(GetThreadLocale(), 0, lpszStr, iLen, lpszComp, iLen);
406 return iRet == CSTR_LESS_THAN ? -1 : iRet == CSTR_GREATER_THAN ? 1 : 0;
409 /*************************************************************************
410 * StrCmpNIA [SHLWAPI.@]
412 * Compare two strings, up to a maximum length, ignoring case.
415 * lpszStr [I] First string to compare
416 * lpszComp [I] Second string to compare
417 * iLen [I] Maximum number of chars to compare.
420 * An integer less than, equal to or greater than 0, indicating that
421 * lpszStr is less than, the same, or greater than lpszComp.
423 int WINAPI StrCmpNIA(LPCSTR lpszStr, LPCSTR lpszComp, int iLen)
427 TRACE("(%s,%s,%i)\n", debugstr_a(lpszStr), debugstr_a(lpszComp), iLen);
429 iRet = CompareStringA(GetThreadLocale(), NORM_IGNORECASE, lpszStr, iLen, lpszComp, iLen);
430 return iRet == CSTR_LESS_THAN ? -1 : iRet == CSTR_GREATER_THAN ? 1 : 0;
433 /*************************************************************************
434 * StrCmpNIW [SHLWAPI.@]
438 INT WINAPI StrCmpNIW(LPCWSTR lpszStr, LPCWSTR lpszComp, int iLen)
442 TRACE("(%s,%s,%i)\n", debugstr_w(lpszStr), debugstr_w(lpszComp), iLen);
444 iRet = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, lpszStr, iLen, lpszComp, iLen);
445 return iRet == CSTR_LESS_THAN ? -1 : iRet == CSTR_GREATER_THAN ? 1 : 0;
448 /*************************************************************************
449 * StrCmpW [SHLWAPI.@]
451 * Compare two strings.
454 * lpszStr [I] First string to compare
455 * lpszComp [I] Second string to compare
458 * An integer less than, equal to or greater than 0, indicating that
459 * lpszStr is less than, the same, or greater than lpszComp.
461 int WINAPI StrCmpW(LPCWSTR lpszStr, LPCWSTR lpszComp)
465 TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszComp));
467 iRet = CompareStringW(GetThreadLocale(), 0, lpszStr, -1, lpszComp, -1);
468 return iRet == CSTR_LESS_THAN ? -1 : iRet == CSTR_GREATER_THAN ? 1 : 0;
471 /*************************************************************************
472 * StrCatW [SHLWAPI.@]
474 * Concatanate two strings.
477 * lpszStr [O] Initial string
478 * lpszSrc [I] String to concatanate
483 LPWSTR WINAPI StrCatW(LPWSTR lpszStr, LPCWSTR lpszSrc)
485 TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszSrc));
487 strcatW(lpszStr, lpszSrc);
491 /*************************************************************************
492 * StrCpyW [SHLWAPI.@]
494 * Copy a string to another string.
497 * lpszStr [O] Destination string
498 * lpszSrc [I] Source string
503 LPWSTR WINAPI StrCpyW(LPWSTR lpszStr, LPCWSTR lpszSrc)
505 TRACE("(%p,%s)\n", lpszStr, debugstr_w(lpszSrc));
507 strcpyW(lpszStr, lpszSrc);
511 /*************************************************************************
512 * StrCpyNW [SHLWAPI.@]
514 * Copy a string to another string, up to a maximum number of characters.
517 * lpszStr [O] Destination string
518 * lpszSrc [I] Source string
519 * iLen [I] Maximum number of chars to copy
524 LPWSTR WINAPI StrCpyNW(LPWSTR lpszStr, LPCWSTR lpszSrc, int iLen)
526 TRACE("(%p,%s,%i)\n", lpszStr, debugstr_w(lpszSrc), iLen);
528 lstrcpynW(lpszStr, lpszSrc, iLen);
534 /*************************************************************************
535 * SHLWAPI_StrStrHelperA
537 * Internal implementation of StrStrA/StrStrIA
539 static LPSTR WINAPI SHLWAPI_StrStrHelperA(LPCSTR lpszStr, LPCSTR lpszSearch,
540 int (*pStrCmpFn)(LPCSTR,LPCSTR,size_t))
544 if (!lpszStr || !lpszSearch || !*lpszSearch)
547 iLen = strlen(lpszSearch);
551 if (!pStrCmpFn(lpszStr, lpszSearch, iLen))
552 return (LPSTR)lpszStr;
553 lpszStr = CharNextA(lpszStr);
558 /*************************************************************************
559 * SHLWAPI_StrStrHelperW
561 * Internal implementation of StrStrW/StrStrIW
563 static LPWSTR WINAPI SHLWAPI_StrStrHelperW(LPCWSTR lpszStr, LPCWSTR lpszSearch,
564 int (*pStrCmpFn)(LPCWSTR,LPCWSTR,int))
568 if (!lpszStr || !lpszSearch || !*lpszSearch)
571 iLen = strlenW(lpszSearch);
575 if (!pStrCmpFn(lpszStr, lpszSearch, iLen))
576 return (LPWSTR)lpszStr;
577 lpszStr = CharNextW(lpszStr);
582 /*************************************************************************
583 * StrStrA [SHLWAPI.@]
585 * Find a substring within a string.
588 * lpszStr [I] String to search in
589 * lpszSearch [I] String to look for
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, strncmp);
601 /*************************************************************************
602 * StrStrW [SHLWAPI.@]
606 LPWSTR WINAPI StrStrW(LPCWSTR lpszStr, LPCWSTR lpszSearch)
608 TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszSearch));
610 return SHLWAPI_StrStrHelperW(lpszStr, lpszSearch, strncmpW);
613 /*************************************************************************
614 * StrRStrIA [SHLWAPI.@]
616 * Find the last occurrence of a substring within a string.
619 * lpszStr [I] String to search in
620 * lpszEnd [I] End of lpszStr
621 * lpszSearch [I] String to look for
624 * The last occurrence lpszSearch within lpszStr, or NULL if not found.
626 LPSTR WINAPI StrRStrIA(LPCSTR lpszStr, LPCSTR lpszEnd, LPCSTR lpszSearch)
631 TRACE("(%s,%s)\n", debugstr_a(lpszStr), debugstr_a(lpszSearch));
633 if (!lpszStr || !lpszSearch || !*lpszSearch)
637 lpszEnd = lpszStr + lstrlenA(lpszStr);
638 if (lpszEnd == lpszStr)
641 if (IsDBCSLeadByte(*lpszSearch))
642 ch1 = *lpszSearch << 8 | (UCHAR)lpszSearch[1];
645 iLen = lstrlenA(lpszSearch);
649 lpszEnd = CharPrevA(lpszStr, lpszEnd);
650 ch2 = IsDBCSLeadByte(*lpszEnd)? *lpszEnd << 8 | (UCHAR)lpszEnd[1] : *lpszEnd;
651 if (!ChrCmpIA(ch1, ch2))
653 if (!StrCmpNIA(lpszEnd, lpszSearch, iLen))
654 return (LPSTR)lpszEnd;
656 } while (lpszEnd > lpszStr);
660 /*************************************************************************
661 * StrRStrIW [SHLWAPI.@]
665 LPWSTR WINAPI StrRStrIW(LPCWSTR lpszStr, LPCWSTR lpszEnd, LPCWSTR lpszSearch)
669 TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszSearch));
671 if (!lpszStr || !lpszSearch || !*lpszSearch)
675 lpszEnd = lpszStr + strlenW(lpszStr);
676 if (lpszEnd == lpszStr)
679 iLen = strlenW(lpszSearch);
683 lpszEnd = CharPrevW(lpszStr, lpszEnd);
684 if (!ChrCmpIW(*lpszSearch, *lpszEnd))
686 if (!StrCmpNIW(lpszEnd, lpszSearch, iLen))
687 return (LPWSTR)lpszEnd;
689 } while (lpszEnd > lpszStr);
693 /*************************************************************************
694 * StrStrIA [SHLWAPI.@]
696 * Find a substring within a string, ignoring case.
699 * lpszStr [I] String to search in
700 * lpszSearch [I] String to look for
703 * The start of lpszSearch within lpszStr, or NULL if not found.
705 LPSTR WINAPI StrStrIA(LPCSTR lpszStr, LPCSTR lpszSearch)
707 TRACE("(%s,%s)\n", debugstr_a(lpszStr), debugstr_a(lpszSearch));
709 return SHLWAPI_StrStrHelperA(lpszStr, lpszSearch, strncasecmp);
712 /*************************************************************************
713 * StrStrIW [SHLWAPI.@]
717 LPWSTR WINAPI StrStrIW(LPCWSTR lpszStr, LPCWSTR lpszSearch)
719 TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszSearch));
721 return SHLWAPI_StrStrHelperW(lpszStr, lpszSearch, strncmpiW);
724 /*************************************************************************
725 * StrToIntA [SHLWAPI.@]
727 * Read a signed integer from a string.
730 * lpszStr [I] String to read integer from
733 * The signed integer value represented by the string, or 0 if no integer is
737 * No leading space is allowed before the number, although a leading '-' is.
739 int WINAPI StrToIntA(LPCSTR lpszStr)
743 TRACE("(%s)\n", debugstr_a(lpszStr));
747 WARN("Invalid lpszStr would crash under Win32!\n");
751 if (*lpszStr == '-' || isdigit(*lpszStr))
752 StrToIntExA(lpszStr, 0, &iRet);
756 /*************************************************************************
757 * StrToIntW [SHLWAPI.@]
761 int WINAPI StrToIntW(LPCWSTR lpszStr)
765 TRACE("(%s)\n", debugstr_w(lpszStr));
769 WARN("Invalid lpszStr would crash under Win32!\n");
773 if (*lpszStr == '-' || isdigitW(*lpszStr))
774 StrToIntExW(lpszStr, 0, &iRet);
778 /*************************************************************************
779 * StrToIntExA [SHLWAPI.@]
781 * Read an integer from a string.
784 * lpszStr [I] String to read integer from
785 * dwFlags [I] Flags controlling the conversion
786 * lpiRet [O] Destination for read integer.
789 * Success: TRUE. lpiRet contains the integer value represented by the string.
790 * Failure: FALSE, if the string is invalid, or no number is present.
793 * Leading whitespace, '-' and '+' are allowed before the number. If
794 * dwFlags includes STIF_SUPPORT_HEX, hexadecimal numbers are allowed, if
795 * preceded by '0x'. If this flag is not set, or there is no '0x' prefix,
796 * the string is treated as a decimal string. A leading '-' is ignored for
797 * hexadecimal numbers.
799 BOOL WINAPI StrToIntExA(LPCSTR lpszStr, DWORD dwFlags, LPINT lpiRet)
801 BOOL bNegative = FALSE;
804 TRACE("(%s,%08X,%p)\n", debugstr_a(lpszStr), dwFlags, lpiRet);
806 if (!lpszStr || !lpiRet)
808 WARN("Invalid parameter would crash under Win32!\n");
811 if (dwFlags > STIF_SUPPORT_HEX)
813 WARN("Unknown flags (%08lX)!\n", dwFlags & ~STIF_SUPPORT_HEX);
816 /* Skip leading space, '+', '-' */
817 while (isspace(*lpszStr))
818 lpszStr = CharNextA(lpszStr);
825 else if (*lpszStr == '+')
828 if (dwFlags & STIF_SUPPORT_HEX &&
829 *lpszStr == '0' && tolower(lpszStr[1]) == 'x')
831 /* Read hex number */
834 if (!isxdigit(*lpszStr))
837 while (isxdigit(*lpszStr))
840 if (isdigit(*lpszStr))
841 iRet += (*lpszStr - '0');
843 iRet += 10 + (tolower(*lpszStr) - 'a');
850 /* Read decimal number */
851 if (!isdigit(*lpszStr))
854 while (isdigit(*lpszStr))
857 iRet += (*lpszStr - '0');
860 *lpiRet = bNegative ? -iRet : iRet;
864 /*************************************************************************
865 * StrToIntExW [SHLWAPI.@]
869 BOOL WINAPI StrToIntExW(LPCWSTR lpszStr, DWORD dwFlags, LPINT lpiRet)
871 BOOL bNegative = FALSE;
874 TRACE("(%s,%08X,%p)\n", debugstr_w(lpszStr), dwFlags, lpiRet);
876 if (!lpszStr || !lpiRet)
878 WARN("Invalid parameter would crash under Win32!\n");
881 if (dwFlags > STIF_SUPPORT_HEX)
883 WARN("Unknown flags (%08lX)!\n", dwFlags & ~STIF_SUPPORT_HEX);
886 /* Skip leading space, '+', '-' */
887 while (isspaceW(*lpszStr))
888 lpszStr = CharNextW(lpszStr);
895 else if (*lpszStr == '+')
898 if (dwFlags & STIF_SUPPORT_HEX &&
899 *lpszStr == '0' && tolowerW(lpszStr[1]) == 'x')
901 /* Read hex number */
904 if (!isxdigitW(*lpszStr))
907 while (isxdigitW(*lpszStr))
910 if (isdigitW(*lpszStr))
911 iRet += (*lpszStr - '0');
913 iRet += 10 + (tolowerW(*lpszStr) - 'a');
920 /* Read decimal number */
921 if (!isdigitW(*lpszStr))
924 while (isdigitW(*lpszStr))
927 iRet += (*lpszStr - '0');
930 *lpiRet = bNegative ? -iRet : iRet;
934 /*************************************************************************
935 * StrDupA [SHLWAPI.@]
937 * Duplicate a string.
940 * lpszStr [I] String to duplicate.
943 * Success: A pointer to a new string containing the contents of lpszStr
944 * Failure: NULL, if memory cannot be allocated
947 * The string memory is allocated with LocalAlloc(), and so should be released
948 * by calling LocalFree().
950 LPSTR WINAPI StrDupA(LPCSTR lpszStr)
955 TRACE("(%s)\n",debugstr_a(lpszStr));
957 iLen = lpszStr ? strlen(lpszStr) + 1 : 1;
958 lpszRet = (LPSTR)LocalAlloc(LMEM_FIXED, iLen);
963 memcpy(lpszRet, lpszStr, iLen);
970 /*************************************************************************
971 * StrDupW [SHLWAPI.@]
975 LPWSTR WINAPI StrDupW(LPCWSTR lpszStr)
980 TRACE("(%s)\n",debugstr_w(lpszStr));
982 iLen = (lpszStr ? strlenW(lpszStr) + 1 : 1) * sizeof(WCHAR);
983 lpszRet = (LPWSTR)LocalAlloc(LMEM_FIXED, iLen);
988 memcpy(lpszRet, lpszStr, iLen);
995 /*************************************************************************
996 * SHLWAPI_StrSpnHelperA
998 * Internal implementation of StrSpnA/StrCSpnA/StrCSpnIA
1000 static int WINAPI SHLWAPI_StrSpnHelperA(LPCSTR lpszStr, LPCSTR lpszMatch,
1001 LPSTR (WINAPI *pStrChrFn)(LPCSTR,WORD),
1004 LPCSTR lpszRead = lpszStr;
1005 if (lpszStr && *lpszStr && lpszMatch)
1009 LPCSTR lpszTest = pStrChrFn(lpszMatch, *lpszRead);
1011 if (!bInvert && !lpszTest)
1013 if (bInvert && lpszTest)
1015 lpszRead = CharNextA(lpszRead);
1018 return lpszRead - lpszStr;
1021 /*************************************************************************
1022 * SHLWAPI_StrSpnHelperW
1024 * Internal implementation of StrSpnW/StrCSpnW/StrCSpnIW
1026 static int WINAPI SHLWAPI_StrSpnHelperW(LPCWSTR lpszStr, LPCWSTR lpszMatch,
1027 LPWSTR (WINAPI *pStrChrFn)(LPCWSTR,WCHAR),
1030 LPCWSTR lpszRead = lpszStr;
1031 if (lpszStr && *lpszStr && lpszMatch)
1035 LPCWSTR lpszTest = pStrChrFn(lpszMatch, *lpszRead);
1037 if (!bInvert && !lpszTest)
1039 if (bInvert && lpszTest)
1041 lpszRead = CharNextW(lpszRead);
1044 return lpszRead - lpszStr;
1047 /*************************************************************************
1048 * StrSpnA [SHLWAPI.@]
1050 * Find the length of the start of a string that contains only certain
1054 * lpszStr [I] String to search
1055 * lpszMatch [I] Characters that can be in the substring
1058 * The length of the part of lpszStr containing only chars from lpszMatch,
1059 * or 0 if any parameter is invalid.
1061 int WINAPI StrSpnA(LPCSTR lpszStr, LPCSTR lpszMatch)
1063 TRACE("(%s,%s)\n",debugstr_a(lpszStr), debugstr_a(lpszMatch));
1065 return SHLWAPI_StrSpnHelperA(lpszStr, lpszMatch, StrChrA, FALSE);
1068 /*************************************************************************
1069 * StrSpnW [SHLWAPI.@]
1073 int WINAPI StrSpnW(LPCWSTR lpszStr, LPCWSTR lpszMatch)
1075 TRACE("(%s,%s)\n",debugstr_w(lpszStr), debugstr_w(lpszMatch));
1077 return SHLWAPI_StrSpnHelperW(lpszStr, lpszMatch, StrChrW, FALSE);
1080 /*************************************************************************
1081 * StrCSpnA [SHLWAPI.@]
1083 * Find the length of the start of a string that does not contain certain
1087 * lpszStr [I] String to search
1088 * lpszMatch [I] Characters that cannot be in the substring
1091 * The length of the part of lpszStr containing only chars not in lpszMatch,
1092 * or 0 if any parameter is invalid.
1094 int WINAPI StrCSpnA(LPCSTR lpszStr, LPCSTR lpszMatch)
1096 TRACE("(%s,%s)\n",debugstr_a(lpszStr), debugstr_a(lpszMatch));
1098 return SHLWAPI_StrSpnHelperA(lpszStr, lpszMatch, StrChrA, TRUE);
1101 /*************************************************************************
1102 * StrCSpnW [SHLWAPI.@]
1106 int WINAPI StrCSpnW(LPCWSTR lpszStr, LPCWSTR lpszMatch)
1108 TRACE("(%s,%s)\n",debugstr_w(lpszStr), debugstr_w(lpszMatch));
1110 return SHLWAPI_StrSpnHelperW(lpszStr, lpszMatch, StrChrW, TRUE);
1113 /*************************************************************************
1114 * StrCSpnIA [SHLWAPI.@]
1116 * Find the length of the start of a string that does not contain certain
1117 * characters, ignoring case.
1120 * lpszStr [I] String to search
1121 * lpszMatch [I] Characters that cannot be in the substring
1124 * The length of the part of lpszStr containing only chars not in lpszMatch,
1125 * or 0 if any parameter is invalid.
1127 int WINAPI StrCSpnIA(LPCSTR lpszStr, LPCSTR lpszMatch)
1129 TRACE("(%s,%s)\n",debugstr_a(lpszStr), debugstr_a(lpszMatch));
1131 return SHLWAPI_StrSpnHelperA(lpszStr, lpszMatch, StrChrIA, TRUE);
1134 /*************************************************************************
1135 * StrCSpnIW [SHLWAPI.@]
1139 int WINAPI StrCSpnIW(LPCWSTR lpszStr, LPCWSTR lpszMatch)
1141 TRACE("(%s,%s)\n",debugstr_w(lpszStr), debugstr_w(lpszMatch));
1143 return SHLWAPI_StrSpnHelperW(lpszStr, lpszMatch, StrChrIW, TRUE);
1146 /*************************************************************************
1147 * StrPBrkA [SHLWAPI.@]
1149 * Search a string for any of a group of characters.
1152 * lpszStr [I] String to search
1153 * lpszMatch [I] Characters to match
1156 * A pointer to the first matching character in lpszStr, or NULL if no
1159 LPSTR WINAPI StrPBrkA(LPCSTR lpszStr, LPCSTR lpszMatch)
1161 TRACE("(%s,%s)\n",debugstr_a(lpszStr), debugstr_a(lpszMatch));
1163 if (lpszStr && lpszMatch && *lpszMatch)
1167 if (StrChrA(lpszMatch, *lpszStr))
1168 return (LPSTR)lpszStr;
1169 lpszStr = CharNextA(lpszStr);
1175 /*************************************************************************
1176 * StrPBrkW [SHLWAPI.@]
1180 LPWSTR WINAPI StrPBrkW(LPCWSTR lpszStr, LPCWSTR lpszMatch)
1182 TRACE("(%s,%s)\n",debugstr_w(lpszStr), debugstr_w(lpszMatch));
1184 if (lpszStr && lpszMatch && *lpszMatch)
1188 if (StrChrW(lpszMatch, *lpszStr))
1189 return (LPWSTR)lpszStr;
1190 lpszStr = CharNextW(lpszStr);
1196 /*************************************************************************
1197 * SHLWAPI_StrRChrHelperA
1199 * Internal implementation of StrRChrA/StrRChrIA.
1201 static LPSTR WINAPI SHLWAPI_StrRChrHelperA(LPCSTR lpszStr,
1202 LPCSTR lpszEnd, WORD ch,
1203 BOOL (WINAPI *pChrCmpFn)(WORD,WORD))
1205 LPCSTR lpszRet = NULL;
1212 lpszEnd = lpszStr + lstrlenA(lpszStr);
1214 while (*lpszStr && lpszStr <= lpszEnd)
1216 ch2 = IsDBCSLeadByte(*lpszStr)? *lpszStr << 8 | lpszStr[1] : *lpszStr;
1218 if (!pChrCmpFn(ch, ch2))
1220 lpszStr = CharNextA(lpszStr);
1223 return (LPSTR)lpszRet;
1226 /*************************************************************************
1227 * SHLWAPI_StrRChrHelperW
1229 * Internal implementation of StrRChrW/StrRChrIW.
1231 static LPWSTR WINAPI SHLWAPI_StrRChrHelperW(LPCWSTR lpszStr,
1232 LPCWSTR lpszEnd, WCHAR ch,
1233 BOOL (WINAPI *pChrCmpFn)(WCHAR,WCHAR))
1235 LPCWSTR lpszRet = NULL;
1240 lpszEnd = lpszStr + strlenW(lpszStr);
1242 while (*lpszStr && lpszStr <= lpszEnd)
1244 if (!pChrCmpFn(ch, *lpszStr))
1246 lpszStr = CharNextW(lpszStr);
1249 return (LPWSTR)lpszRet;
1252 /**************************************************************************
1253 * StrRChrA [SHLWAPI.@]
1255 * Find the last occurrence of a character in string.
1258 * lpszStr [I] String to search in
1259 * lpszEnd [I] Place to end search, or NULL to search until the end of lpszStr
1260 * ch [I] Character to search for.
1263 * Success: A pointer to the last occurrence of ch in lpszStr before lpszEnd,
1264 * or NULL if not found.
1265 * Failure: NULL, if any arguments are invalid.
1267 LPSTR WINAPI StrRChrA(LPCSTR lpszStr, LPCSTR lpszEnd, WORD ch)
1269 TRACE("(%s,%s,%x)\n", debugstr_a(lpszStr), debugstr_a(lpszEnd), ch);
1271 return SHLWAPI_StrRChrHelperA(lpszStr, lpszEnd, ch, SHLWAPI_ChrCmpA);
1274 /**************************************************************************
1275 * StrRChrW [SHLWAPI.@]
1279 LPWSTR WINAPI StrRChrW(LPCWSTR lpszStr, LPCWSTR lpszEnd, WORD ch)
1281 TRACE("(%s,%s,%x)\n", debugstr_w(lpszStr), debugstr_w(lpszEnd), ch);
1283 return SHLWAPI_StrRChrHelperW(lpszStr, lpszEnd, ch, SHLWAPI_ChrCmpW);
1286 /**************************************************************************
1287 * StrRChrIA [SHLWAPI.@]
1289 * Find the last occurrence of a character in string, ignoring case.
1292 * lpszStr [I] String to search in
1293 * lpszEnd [I] Place to end search, or NULL to search until the end of lpszStr
1294 * ch [I] Character to search for.
1297 * Success: A pointer to the last occurrence of ch in lpszStr before lpszEnd,
1298 * or NULL if not found.
1299 * Failure: NULL, if any arguments are invalid.
1301 LPSTR WINAPI StrRChrIA(LPCSTR lpszStr, LPCSTR lpszEnd, WORD ch)
1303 TRACE("(%s,%s,%x)\n", debugstr_a(lpszStr), debugstr_a(lpszEnd), ch);
1305 return SHLWAPI_StrRChrHelperA(lpszStr, lpszEnd, ch, ChrCmpIA);
1308 /**************************************************************************
1309 * StrRChrIW [SHLWAPI.@]
1313 LPWSTR WINAPI StrRChrIW(LPCWSTR lpszStr, LPCWSTR lpszEnd, WORD ch)
1315 TRACE("(%s,%s,%x)\n", debugstr_w(lpszStr), debugstr_w(lpszEnd), ch);
1317 return SHLWAPI_StrRChrHelperW(lpszStr, lpszEnd, ch, ChrCmpIW);
1320 /*************************************************************************
1321 * StrCatBuffA [SHLWAPI.@]
1323 * Concatenate two strings together.
1326 * lpszStr [O] String to concatenate to
1327 * lpszCat [I] String to add to lpszCat
1328 * cchMax [I] Maximum number of characters for the whole string
1334 * cchMax determines the number of characters in the final length of the
1335 * string, not the number appended to lpszStr from lpszCat.
1337 LPSTR WINAPI StrCatBuffA(LPSTR lpszStr, LPCSTR lpszCat, INT cchMax)
1341 TRACE("(%p,%s,%d)\n", lpszStr, debugstr_a(lpszCat), cchMax);
1345 WARN("Invalid lpszStr would crash under Win32!\n");
1349 iLen = strlen(lpszStr);
1353 StrCpyNA(lpszStr + iLen, lpszCat, cchMax);
1357 /*************************************************************************
1358 * StrCatBuffW [SHLWAPI.@]
1362 LPWSTR WINAPI StrCatBuffW(LPWSTR lpszStr, LPCWSTR lpszCat, INT cchMax)
1366 TRACE("(%p,%s,%d)\n", lpszStr, debugstr_w(lpszCat), cchMax);
1370 WARN("Invalid lpszStr would crash under Win32!\n");
1374 iLen = strlenW(lpszStr);
1378 StrCpyNW(lpszStr + iLen, lpszCat, cchMax);
1382 /*************************************************************************
1383 * StrRetToBufA [SHLWAPI.@]
1385 * Convert a STRRET to a normal string.
1388 * lpStrRet [O] STRRET to convert
1389 * pIdl [I] ITEMIDLIST for lpStrRet->uType == STRRET_OFFSET
1390 * lpszDest [O] Destination for normal string
1391 * dwLen [I] Length of lpszDest
1394 * Success: S_OK. lpszDest contains up to dwLen characters of the string.
1395 * If lpStrRet is of type STRRET_WSTR, its memory is freed with
1396 * CoTaskMemFree() and its type set to STRRET_CSTRA.
1397 * Failure: E_FAIL, if any parameters are invalid.
1399 HRESULT WINAPI StrRetToBufA (LPSTRRET src, const ITEMIDLIST *pidl, LPSTR dest, UINT len)
1402 * This routine is identical to that in dlls/shell32/shellstring.c.
1403 * It was duplicated because not every version of Shlwapi.dll exports
1404 * StrRetToBufA. If you change one routine, change them both.
1406 TRACE("dest=%p len=0x%x strret=%p pidl=%p stub\n",dest,len,src,pidl);
1410 WARN("Invalid lpStrRet would crash under Win32!\n");
1424 WideCharToMultiByte(CP_ACP, 0, src->u.pOleStr, -1, dest, len, NULL, NULL);
1425 CoTaskMemFree(src->u.pOleStr);
1429 lstrcpynA((LPSTR)dest, src->u.cStr, len);
1433 lstrcpynA((LPSTR)dest, ((LPCSTR)&pidl->mkid)+src->u.uOffset, len);
1437 FIXME("unknown type!\n");
1443 /*************************************************************************
1444 * StrRetToBufW [SHLWAPI.@]
1448 HRESULT WINAPI StrRetToBufW (LPSTRRET src, const ITEMIDLIST *pidl, LPWSTR dest, UINT len)
1450 TRACE("dest=%p len=0x%x strret=%p pidl=%p stub\n",dest,len,src,pidl);
1454 WARN("Invalid lpStrRet would crash under Win32!\n");
1468 lstrcpynW((LPWSTR)dest, src->u.pOleStr, len);
1469 CoTaskMemFree(src->u.pOleStr);
1473 if (!MultiByteToWideChar( CP_ACP, 0, src->u.cStr, -1, dest, len ) && len)
1480 if (!MultiByteToWideChar( CP_ACP, 0, ((LPCSTR)&pidl->mkid)+src->u.uOffset, -1,
1487 FIXME("unknown type!\n");
1493 /*************************************************************************
1494 * StrRetToStrA [SHLWAPI.@]
1496 * Converts a STRRET to a normal string.
1499 * lpStrRet [O] STRRET to convert
1500 * pidl [I] ITEMIDLIST for lpStrRet->uType == STRRET_OFFSET
1501 * ppszName [O] Destination for converted string
1504 * Success: S_OK. ppszName contains the new string, allocated with CoTaskMemAlloc().
1505 * Failure: E_FAIL, if any parameters are invalid.
1507 HRESULT WINAPI StrRetToStrA(LPSTRRET lpStrRet, const ITEMIDLIST *pidl, LPSTR *ppszName)
1509 HRESULT hRet = E_FAIL;
1511 switch (lpStrRet->uType)
1514 hRet = _SHStrDupAW(lpStrRet->u.pOleStr, ppszName);
1515 CoTaskMemFree(lpStrRet->u.pOleStr);
1519 hRet = _SHStrDupAA(lpStrRet->u.cStr, ppszName);
1523 hRet = _SHStrDupAA(((LPCSTR)&pidl->mkid) + lpStrRet->u.uOffset, ppszName);
1533 /*************************************************************************
1534 * StrRetToStrW [SHLWAPI.@]
1538 HRESULT WINAPI StrRetToStrW(LPSTRRET lpStrRet, const ITEMIDLIST *pidl, LPWSTR *ppszName)
1540 HRESULT hRet = E_FAIL;
1542 switch (lpStrRet->uType)
1545 hRet = SHStrDupW(lpStrRet->u.pOleStr, ppszName);
1546 CoTaskMemFree(lpStrRet->u.pOleStr);
1550 hRet = SHStrDupA(lpStrRet->u.cStr, ppszName);
1554 hRet = SHStrDupA(((LPCSTR)&pidl->mkid) + lpStrRet->u.uOffset, ppszName);
1564 /* Create an ASCII string copy using SysAllocString() */
1565 static HRESULT _SHStrDupAToBSTR(LPCSTR src, BSTR *pBstrOut)
1571 INT len = MultiByteToWideChar(CP_ACP, 0, src, -1, NULL, 0);
1572 WCHAR* szTemp = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1576 MultiByteToWideChar(CP_ACP, 0, src, -1, szTemp, len);
1577 *pBstrOut = SysAllocString(szTemp);
1578 HeapFree(GetProcessHeap(), 0, szTemp);
1584 return E_OUTOFMEMORY;
1587 /*************************************************************************
1588 * StrRetToBSTR [SHLWAPI.@]
1590 * Converts a STRRET to a BSTR.
1593 * lpStrRet [O] STRRET to convert
1594 * pidl [I] ITEMIDLIST for lpStrRet->uType = STRRET_OFFSET
1595 * pBstrOut [O] Destination for converted BSTR
1598 * Success: S_OK. pBstrOut contains the new string.
1599 * Failure: E_FAIL, if any parameters are invalid.
1601 HRESULT WINAPI StrRetToBSTR(STRRET *lpStrRet, LPCITEMIDLIST pidl, BSTR* pBstrOut)
1603 HRESULT hRet = E_FAIL;
1605 switch (lpStrRet->uType)
1608 *pBstrOut = SysAllocString(lpStrRet->u.pOleStr);
1611 CoTaskMemFree(lpStrRet->u.pOleStr);
1615 hRet = _SHStrDupAToBSTR(lpStrRet->u.cStr, pBstrOut);
1619 hRet = _SHStrDupAToBSTR(((LPCSTR)&pidl->mkid) + lpStrRet->u.uOffset, pBstrOut);
1629 /*************************************************************************
1630 * StrFormatKBSizeA [SHLWAPI.@]
1632 * Create a formatted string containing a byte count in Kilobytes.
1635 * llBytes [I] Byte size to format
1636 * lpszDest [I] Destination for formatted string
1637 * cchMax [I] Size of lpszDest
1642 LPSTR WINAPI StrFormatKBSizeA(LONGLONG llBytes, LPSTR lpszDest, UINT cchMax)
1646 if (!StrFormatKBSizeW(llBytes, wszBuf, 256))
1648 if (!WideCharToMultiByte(CP_ACP, 0, wszBuf, -1, lpszDest, cchMax, NULL, NULL))
1653 /*************************************************************************
1654 * StrFormatKBSizeW [SHLWAPI.@]
1656 * See StrFormatKBSizeA.
1658 LPWSTR WINAPI StrFormatKBSizeW(LONGLONG llBytes, LPWSTR lpszDest, UINT cchMax)
1660 static const WCHAR kb[] = {' ','K','B',0};
1661 LONGLONG llKB = (llBytes + 1023) >> 10;
1664 TRACE("(0x%s,%p,%d)\n", wine_dbgstr_longlong(llBytes), lpszDest, cchMax);
1666 if (!FormatInt(llKB, lpszDest, cchMax))
1669 len = lstrlenW(lpszDest);
1670 if (cchMax - len < 4)
1672 lstrcatW(lpszDest, kb);
1676 /*************************************************************************
1677 * StrNCatA [SHLWAPI.@]
1679 * Concatenate two strings together.
1682 * lpszStr [O] String to concatenate to
1683 * lpszCat [I] String to add to lpszCat
1684 * cchMax [I] Maximum number of characters to concatenate
1690 * cchMax determines the number of characters that are appended to lpszStr,
1691 * not the total length of the string.
1693 LPSTR WINAPI StrNCatA(LPSTR lpszStr, LPCSTR lpszCat, INT cchMax)
1695 LPSTR lpszRet = lpszStr;
1697 TRACE("(%s,%s,%i)\n", debugstr_a(lpszStr), debugstr_a(lpszCat), cchMax);
1701 WARN("Invalid lpszStr would crash under Win32!\n");
1705 StrCpyNA(lpszStr + strlen(lpszStr), lpszCat, cchMax);
1709 /*************************************************************************
1710 * StrNCatW [SHLWAPI.@]
1714 LPWSTR WINAPI StrNCatW(LPWSTR lpszStr, LPCWSTR lpszCat, INT cchMax)
1716 LPWSTR lpszRet = lpszStr;
1718 TRACE("(%s,%s,%i)\n", debugstr_w(lpszStr), debugstr_w(lpszCat), cchMax);
1722 WARN("Invalid lpszStr would crash under Win32\n");
1726 StrCpyNW(lpszStr + strlenW(lpszStr), lpszCat, cchMax);
1730 /*************************************************************************
1731 * StrTrimA [SHLWAPI.@]
1733 * Remove characters from the start and end of a string.
1736 * lpszStr [O] String to remove characters from
1737 * lpszTrim [I] Characters to remove from lpszStr
1740 * TRUE If lpszStr was valid and modified
1743 BOOL WINAPI StrTrimA(LPSTR lpszStr, LPCSTR lpszTrim)
1746 LPSTR lpszRead = lpszStr;
1749 TRACE("(%s,%s)\n", debugstr_a(lpszStr), debugstr_a(lpszTrim));
1751 if (lpszRead && *lpszRead)
1753 while (*lpszRead && StrChrA(lpszTrim, *lpszRead))
1754 lpszRead = CharNextA(lpszRead); /* Skip leading matches */
1756 dwLen = strlen(lpszRead);
1758 if (lpszRead != lpszStr)
1760 memmove(lpszStr, lpszRead, dwLen + 1);
1765 lpszRead = lpszStr + dwLen;
1766 while (StrChrA(lpszTrim, lpszRead[-1]))
1767 lpszRead = CharPrevA(lpszStr, lpszRead); /* Skip trailing matches */
1769 if (lpszRead != lpszStr + dwLen)
1779 /*************************************************************************
1780 * StrTrimW [SHLWAPI.@]
1784 BOOL WINAPI StrTrimW(LPWSTR lpszStr, LPCWSTR lpszTrim)
1787 LPWSTR lpszRead = lpszStr;
1790 TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszTrim));
1792 if (lpszRead && *lpszRead)
1794 while (*lpszRead && StrChrW(lpszTrim, *lpszRead))
1795 lpszRead = CharNextW(lpszRead); /* Skip leading matches */
1797 dwLen = strlenW(lpszRead);
1799 if (lpszRead != lpszStr)
1801 memmove(lpszStr, lpszRead, (dwLen + 1) * sizeof(WCHAR));
1806 lpszRead = lpszStr + dwLen;
1807 while (StrChrW(lpszTrim, lpszRead[-1]))
1808 lpszRead = CharPrevW(lpszStr, lpszRead); /* Skip trailing matches */
1810 if (lpszRead != lpszStr + dwLen)
1820 /*************************************************************************
1821 * _SHStrDupAA [INTERNAL]
1823 * Duplicates a ASCII string to ASCII. The destination buffer is allocated.
1825 static HRESULT WINAPI _SHStrDupAA(LPCSTR src, LPSTR * dest)
1831 len = lstrlenA(src) + 1;
1832 *dest = CoTaskMemAlloc(len);
1838 lstrcpynA(*dest,src, len);
1844 TRACE("%s->(%p)\n", debugstr_a(src), *dest);
1848 /*************************************************************************
1849 * SHStrDupA [SHLWAPI.@]
1851 * Return a Unicode copy of a string, in memory allocated by CoTaskMemAlloc().
1854 * lpszStr [I] String to copy
1855 * lppszDest [O] Destination for the new string copy
1858 * Success: S_OK. lppszDest contains the new string in Unicode format.
1859 * Failure: E_OUTOFMEMORY, If any arguments are invalid or memory allocation
1862 HRESULT WINAPI SHStrDupA(LPCSTR lpszStr, LPWSTR * lppszDest)
1869 len = MultiByteToWideChar(0, 0, lpszStr, -1, 0, 0) * sizeof(WCHAR);
1870 *lppszDest = CoTaskMemAlloc(len);
1877 MultiByteToWideChar(0, 0, lpszStr, -1, *lppszDest, len/sizeof(WCHAR));
1881 hRet = E_OUTOFMEMORY;
1883 TRACE("%s->(%p)\n", debugstr_a(lpszStr), *lppszDest);
1887 /*************************************************************************
1888 * _SHStrDupAW [INTERNAL]
1890 * Duplicates a UNICODE to a ASCII string. The destination buffer is allocated.
1892 static HRESULT WINAPI _SHStrDupAW(LPCWSTR src, LPSTR * dest)
1898 len = WideCharToMultiByte(CP_ACP, 0, src, -1, NULL, 0, NULL, NULL);
1899 *dest = CoTaskMemAlloc(len);
1905 WideCharToMultiByte(CP_ACP, 0, src, -1, *dest, len, NULL, NULL);
1911 TRACE("%s->(%p)\n", debugstr_w(src), *dest);
1915 /*************************************************************************
1916 * SHStrDupW [SHLWAPI.@]
1920 HRESULT WINAPI SHStrDupW(LPCWSTR src, LPWSTR * dest)
1926 len = (lstrlenW(src) + 1) * sizeof(WCHAR);
1927 *dest = CoTaskMemAlloc(len);
1933 memcpy(*dest, src, len);
1939 TRACE("%s->(%p)\n", debugstr_w(src), *dest);
1943 /*************************************************************************
1944 * SHLWAPI_WriteReverseNum
1946 * Internal helper for SHLWAPI_WriteTimeClass.
1948 static inline LPWSTR SHLWAPI_WriteReverseNum(LPWSTR lpszOut, DWORD dwNum)
1952 /* Write a decimal number to a string, backwards */
1955 DWORD dwNextDigit = dwNum % 10;
1956 *lpszOut-- = '0' + dwNextDigit;
1957 dwNum = (dwNum - dwNextDigit) / 10;
1958 } while (dwNum > 0);
1963 /*************************************************************************
1964 * SHLWAPI_FormatSignificant
1966 * Internal helper for SHLWAPI_WriteTimeClass.
1968 static inline int SHLWAPI_FormatSignificant(LPWSTR lpszNum, int dwDigits)
1970 /* Zero non significant digits, return remaining significant digits */
1974 if (--dwDigits == 0)
1984 /*************************************************************************
1985 * SHLWAPI_WriteTimeClass
1987 * Internal helper for StrFromTimeIntervalW.
1989 static int WINAPI SHLWAPI_WriteTimeClass(LPWSTR lpszOut, DWORD dwValue,
1990 UINT uClassStringId, int iDigits)
1992 WCHAR szBuff[64], *szOut = szBuff + 32;
1994 szOut = SHLWAPI_WriteReverseNum(szOut, dwValue);
1995 iDigits = SHLWAPI_FormatSignificant(szOut + 1, iDigits);
1997 LoadStringW(shlwapi_hInstance, uClassStringId, szBuff + 32, 32);
1998 strcatW(lpszOut, szOut);
2002 /*************************************************************************
2003 * StrFromTimeIntervalA [SHLWAPI.@]
2005 * Format a millisecond time interval into a string
2008 * lpszStr [O] Output buffer for formatted time interval
2009 * cchMax [I] Size of lpszStr
2010 * dwMS [I] Number of milliseconds
2011 * iDigits [I] Number of digits to print
2014 * The length of the formatted string, or 0 if any parameter is invalid.
2017 * This implementation mimics the Win32 behaviour of always writing a leading
2018 * space before the time interval begins.
2020 * iDigits is used to provide approximate times if accuracy is not important.
2021 * This number of digits will be written of the first non-zero time class
2022 * (hours/minutes/seconds). If this does not complete the time classification,
2023 * the remaining digits are changed to zeros (i.e. The time is _not_ rounded).
2024 * If there are digits remaining following the writing of a time class, the
2025 * next time class will be written.
2027 * For example, given dwMS represents 138 hours,43 minutes and 15 seconds, the
2028 * following will result from the given values of iDigits:
2030 *| iDigits 1 2 3 4 5 ...
2031 *| lpszStr "100 hr" "130 hr" "138 hr" "138 hr 40 min" "138 hr 43 min" ...
2033 INT WINAPI StrFromTimeIntervalA(LPSTR lpszStr, UINT cchMax, DWORD dwMS,
2038 TRACE("(%p,%d,%d,%d)\n", lpszStr, cchMax, dwMS, iDigits);
2040 if (lpszStr && cchMax)
2043 StrFromTimeIntervalW(szBuff, sizeof(szBuff)/sizeof(WCHAR), dwMS, iDigits);
2044 WideCharToMultiByte(CP_ACP,0,szBuff,-1,lpszStr,cchMax,0,0);
2050 /*************************************************************************
2051 * StrFromTimeIntervalW [SHLWAPI.@]
2053 * See StrFromTimeIntervalA.
2055 INT WINAPI StrFromTimeIntervalW(LPWSTR lpszStr, UINT cchMax, DWORD dwMS,
2060 TRACE("(%p,%d,%d,%d)\n", lpszStr, cchMax, dwMS, iDigits);
2062 if (lpszStr && cchMax)
2065 DWORD dwHours, dwMinutes;
2067 if (!iDigits || cchMax == 1)
2073 /* Calculate the time classes */
2074 dwMS = (dwMS + 500) / 1000;
2075 dwHours = dwMS / 3600;
2076 dwMS -= dwHours * 3600;
2077 dwMinutes = dwMS / 60;
2078 dwMS -= dwMinutes * 60;
2083 iDigits = SHLWAPI_WriteTimeClass(szCopy, dwHours, IDS_TIME_INTERVAL_HOURS, iDigits);
2085 if (dwMinutes && iDigits)
2086 iDigits = SHLWAPI_WriteTimeClass(szCopy, dwMinutes, IDS_TIME_INTERVAL_MINUTES, iDigits);
2088 if (iDigits) /* Always write seconds if we have significant digits */
2089 SHLWAPI_WriteTimeClass(szCopy, dwMS, IDS_TIME_INTERVAL_SECONDS, iDigits);
2091 lstrcpynW(lpszStr, szCopy, cchMax);
2092 iRet = strlenW(lpszStr);
2097 /*************************************************************************
2098 * StrIsIntlEqualA [SHLWAPI.@]
2100 * Compare two strings.
2103 * bCase [I] Whether to compare case sensitively
2104 * lpszStr [I] First string to compare
2105 * lpszComp [I] Second string to compare
2106 * iLen [I] Length to compare
2109 * TRUE If the strings are equal.
2112 BOOL WINAPI StrIsIntlEqualA(BOOL bCase, LPCSTR lpszStr, LPCSTR lpszComp,
2117 TRACE("(%d,%s,%s,%d)\n", bCase,
2118 debugstr_a(lpszStr), debugstr_a(lpszComp), iLen);
2120 /* FIXME: This flag is undocumented and unknown by our CompareString.
2121 * We need a define for it.
2123 dwFlags = 0x10000000;
2124 if (!bCase) dwFlags |= NORM_IGNORECASE;
2126 return (CompareStringA(GetThreadLocale(), dwFlags, lpszStr, iLen, lpszComp, iLen) == CSTR_EQUAL);
2129 /*************************************************************************
2130 * StrIsIntlEqualW [SHLWAPI.@]
2132 * See StrIsIntlEqualA.
2134 BOOL WINAPI StrIsIntlEqualW(BOOL bCase, LPCWSTR lpszStr, LPCWSTR lpszComp,
2139 TRACE("(%d,%s,%s,%d)\n", bCase,
2140 debugstr_w(lpszStr),debugstr_w(lpszComp), iLen);
2142 /* FIXME: This flag is undocumented and unknown by our CompareString.
2143 * We need a define for it.
2145 dwFlags = 0x10000000;
2146 if (!bCase) dwFlags |= NORM_IGNORECASE;
2148 return (CompareStringW(GetThreadLocale(), dwFlags, lpszStr, iLen, lpszComp, iLen) == CSTR_EQUAL);
2151 /*************************************************************************
2154 * Copy a string to another string, up to a maximum number of characters.
2157 * lpszDest [O] Destination string
2158 * lpszSrc [I] Source string
2159 * iLen [I] Maximum number of chars to copy
2162 * Success: A pointer to the last character written to lpszDest.
2163 * Failure: lpszDest, if any arguments are invalid.
2165 LPSTR WINAPI StrCpyNXA(LPSTR lpszDest, LPCSTR lpszSrc, int iLen)
2167 TRACE("(%p,%s,%i)\n", lpszDest, debugstr_a(lpszSrc), iLen);
2169 if (lpszDest && lpszSrc && iLen > 0)
2171 while ((iLen-- > 1) && *lpszSrc)
2172 *lpszDest++ = *lpszSrc++;
2179 /*************************************************************************
2182 * Unicode version of StrCpyNXA.
2184 LPWSTR WINAPI StrCpyNXW(LPWSTR lpszDest, LPCWSTR lpszSrc, int iLen)
2186 TRACE("(%p,%s,%i)\n", lpszDest, debugstr_w(lpszSrc), iLen);
2188 if (lpszDest && lpszSrc && iLen > 0)
2190 while ((iLen-- > 1) && *lpszSrc)
2191 *lpszDest++ = *lpszSrc++;
2198 /*************************************************************************
2199 * StrCmpLogicalW [SHLWAPI.@]
2201 * Compare two strings, ignoring case and comparing digits as numbers.
2204 * lpszStr [I] First string to compare
2205 * lpszComp [I] Second string to compare
2206 * iLen [I] Length to compare
2209 * TRUE If the strings are equal.
2212 INT WINAPI StrCmpLogicalW(LPCWSTR lpszStr, LPCWSTR lpszComp)
2216 TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszComp));
2218 if (lpszStr && lpszComp)
2224 else if (isdigitW(*lpszStr))
2228 if (!isdigitW(*lpszComp))
2231 /* Compare the numbers */
2232 StrToIntExW(lpszStr, 0, &iStr);
2233 StrToIntExW(lpszComp, 0, &iComp);
2237 else if (iStr > iComp)
2241 while (isdigitW(*lpszStr))
2243 while (isdigitW(*lpszComp))
2246 else if (isdigitW(*lpszComp))
2250 iDiff = SHLWAPI_ChrCmpHelperW(*lpszStr,*lpszComp,NORM_IGNORECASE);
2266 /* Structure for formatting byte strings */
2267 typedef struct tagSHLWAPI_BYTEFORMATS
2274 } SHLWAPI_BYTEFORMATS;
2276 /*************************************************************************
2277 * StrFormatByteSizeW [SHLWAPI.@]
2279 * Create a string containing an abbreviated byte count of up to 2^63-1.
2282 * llBytes [I] Byte size to format
2283 * lpszDest [I] Destination for formatted string
2284 * cchMax [I] Size of lpszDest
2290 * There is no StrFormatByteSize64W function, it is called StrFormatByteSizeW().
2292 LPWSTR WINAPI StrFormatByteSizeW(LONGLONG llBytes, LPWSTR lpszDest, UINT cchMax)
2294 #define KB ((ULONGLONG)1024)
2296 #define GB (KB*KB*KB)
2297 #define TB (KB*KB*KB*KB)
2298 #define PB (KB*KB*KB*KB*KB)
2300 static const SHLWAPI_BYTEFORMATS bfFormats[] =
2302 { 10*KB, 10.24, 100.0, 2, 'K' }, /* 10 KB */
2303 { 100*KB, 102.4, 10.0, 1, 'K' }, /* 100 KB */
2304 { 1000*KB, 1024.0, 1.0, 0, 'K' }, /* 1000 KB */
2305 { 10*MB, 10485.76, 100.0, 2, 'M' }, /* 10 MB */
2306 { 100*MB, 104857.6, 10.0, 1, 'M' }, /* 100 MB */
2307 { 1000*MB, 1048576.0, 1.0, 0, 'M' }, /* 1000 MB */
2308 { 10*GB, 10737418.24, 100.0, 2, 'G' }, /* 10 GB */
2309 { 100*GB, 107374182.4, 10.0, 1, 'G' }, /* 100 GB */
2310 { 1000*GB, 1073741824.0, 1.0, 0, 'G' }, /* 1000 GB */
2311 { 10*TB, 10485.76, 100.0, 2, 'T' }, /* 10 TB */
2312 { 100*TB, 104857.6, 10.0, 1, 'T' }, /* 100 TB */
2313 { 1000*TB, 1048576.0, 1.0, 0, 'T' }, /* 1000 TB */
2314 { 10*PB, 10737418.24, 100.00, 2, 'P' }, /* 10 PB */
2315 { 100*PB, 107374182.4, 10.00, 1, 'P' }, /* 100 PB */
2316 { 1000*PB, 1073741824.0, 1.00, 0, 'P' }, /* 1000 PB */
2317 { 0, 10995116277.76, 100.00, 2, 'E' } /* EB's, catch all */
2319 WCHAR wszAdd[] = {' ','?','B',0};
2323 TRACE("(0x%s,%p,%d)\n", wine_dbgstr_longlong(llBytes), lpszDest, cchMax);
2325 if (!lpszDest || !cchMax)
2328 if (llBytes < 1024) /* 1K */
2330 WCHAR wszBytesFormat[64];
2331 LoadStringW(shlwapi_hInstance, IDS_BYTES_FORMAT, wszBytesFormat, 64);
2332 snprintfW(lpszDest, cchMax, wszBytesFormat, (long)llBytes);
2336 /* Note that if this loop completes without finding a match, i will be
2337 * pointing at the last entry, which is a catch all for > 1000 PB
2339 while (i < sizeof(bfFormats) / sizeof(SHLWAPI_BYTEFORMATS) - 1)
2341 if (llBytes < bfFormats[i].dLimit)
2345 /* Above 1 TB we encounter problems with FP accuracy. So for amounts above
2346 * this number we integer shift down by 1 MB first. The table above has
2347 * the divisors scaled down from the '< 10 TB' entry onwards, to account
2348 * for this. We also add a small fudge factor to get the correct result for
2349 * counts that lie exactly on a 1024 byte boundary.
2352 dBytes = (double)(llBytes >> 20) + 0.001; /* Scale down by I MB */
2354 dBytes = (double)llBytes + 0.00001;
2356 dBytes = floor(dBytes / bfFormats[i].dDivisor) / bfFormats[i].dNormaliser;
2358 if (!FormatDouble(dBytes, bfFormats[i].nDecimals, lpszDest, cchMax))
2360 wszAdd[1] = bfFormats[i].wPrefix;
2361 StrCatBuffW(lpszDest, wszAdd, cchMax);
2365 /*************************************************************************
2366 * StrFormatByteSize64A [SHLWAPI.@]
2368 * See StrFormatByteSizeW.
2370 LPSTR WINAPI StrFormatByteSize64A(LONGLONG llBytes, LPSTR lpszDest, UINT cchMax)
2374 StrFormatByteSizeW(llBytes, wszBuff, sizeof(wszBuff)/sizeof(WCHAR));
2377 WideCharToMultiByte(CP_ACP, 0, wszBuff, -1, lpszDest, cchMax, 0, 0);
2381 /*************************************************************************
2382 * StrFormatByteSizeA [SHLWAPI.@]
2384 * Create a string containing an abbreviated byte count of up to 2^31-1.
2387 * dwBytes [I] Byte size to format
2388 * lpszDest [I] Destination for formatted string
2389 * cchMax [I] Size of lpszDest
2395 * The Ascii and Unicode versions of this function accept a different
2396 * integer type for dwBytes. See StrFormatByteSize64A().
2398 LPSTR WINAPI StrFormatByteSizeA(DWORD dwBytes, LPSTR lpszDest, UINT cchMax)
2400 TRACE("(%d,%p,%d)\n", dwBytes, lpszDest, cchMax);
2402 return StrFormatByteSize64A(dwBytes, lpszDest, cchMax);
2405 /*************************************************************************
2408 * Remove a hanging lead byte from the end of a string, if present.
2411 * lpStr [I] String to check for a hanging lead byte
2412 * size [I] Length of lpStr
2415 * Success: The new length of the string. Any hanging lead bytes are removed.
2416 * Failure: 0, if any parameters are invalid.
2418 DWORD WINAPI SHTruncateString(LPSTR lpStr, DWORD size)
2422 LPSTR lastByte = lpStr + size - 1;
2424 while(lpStr < lastByte)
2425 lpStr += IsDBCSLeadByte(*lpStr) ? 2 : 1;
2427 if(lpStr == lastByte && IsDBCSLeadByte(*lpStr))
2437 /*************************************************************************
2440 * Remove a single non-trailing ampersand ('&') from a string.
2443 * lpszStr [I/O] String to remove ampersand from.
2446 * The character after the first ampersand in lpszStr, or the first character
2447 * in lpszStr if there is no ampersand in the string.
2449 char WINAPI SHStripMneumonicA(LPCSTR lpszStr)
2451 LPSTR lpszIter, lpszTmp;
2454 TRACE("(%s)\n", debugstr_a(lpszStr));
2458 if ((lpszIter = StrChrA(lpszStr, '&')))
2460 lpszTmp = CharNextA(lpszIter);
2461 if (lpszTmp && *lpszTmp)
2463 if (*lpszTmp != '&')
2466 while (lpszIter && *lpszIter)
2468 lpszTmp = CharNextA(lpszIter);
2469 *lpszIter = *lpszTmp;
2478 /*************************************************************************
2481 * Unicode version of SHStripMneumonicA.
2483 WCHAR WINAPI SHStripMneumonicW(LPCWSTR lpszStr)
2485 LPWSTR lpszIter, lpszTmp;
2488 TRACE("(%s)\n", debugstr_w(lpszStr));
2492 if ((lpszIter = StrChrW(lpszStr, '&')))
2494 lpszTmp = CharNextW(lpszIter);
2495 if (lpszTmp && *lpszTmp)
2497 if (*lpszTmp != '&')
2500 while (lpszIter && *lpszIter)
2502 lpszTmp = CharNextW(lpszIter);
2503 *lpszIter = *lpszTmp;
2512 /*************************************************************************
2515 * Convert an Ascii string to Unicode.
2518 * dwCp [I] Code page for the conversion
2519 * lpSrcStr [I] Source Ascii string to convert
2520 * lpDstStr [O] Destination for converted Unicode string
2521 * iLen [I] Length of lpDstStr
2524 * The return value of the MultiByteToWideChar() function called on lpSrcStr.
2526 DWORD WINAPI SHAnsiToUnicodeCP(DWORD dwCp, LPCSTR lpSrcStr, LPWSTR lpDstStr, int iLen)
2530 dwRet = MultiByteToWideChar(dwCp, 0, lpSrcStr, -1, lpDstStr, iLen);
2531 TRACE("%s->%s,ret=%d\n", debugstr_a(lpSrcStr), debugstr_w(lpDstStr), dwRet);
2535 /*************************************************************************
2538 * Convert an Ascii string to Unicode.
2541 * lpSrcStr [I] Source Ascii string to convert
2542 * lpDstStr [O] Destination for converted Unicode string
2543 * iLen [I] Length of lpDstStr
2546 * The return value of the MultiByteToWideChar() function called on lpSrcStr.
2549 * This function simply calls SHAnsiToUnicodeCP with code page CP_ACP.
2551 DWORD WINAPI SHAnsiToUnicode(LPCSTR lpSrcStr, LPWSTR lpDstStr, int iLen)
2553 return SHAnsiToUnicodeCP(CP_ACP, lpSrcStr, lpDstStr, iLen);
2556 /*************************************************************************
2559 * Convert a Unicode string to Ascii.
2562 * CodePage [I] Code page to use for the conversion
2563 * lpSrcStr [I] Source Unicode string to convert
2564 * lpDstStr [O] Destination for converted Ascii string
2565 * lpiLen [I/O] Input length of lpDstStr/destination for length of lpDstStr
2568 * Success: The number of characters that result from the conversion.
2571 INT WINAPI SHUnicodeToAnsiCP(UINT CodePage, LPCWSTR lpSrcStr, LPSTR lpDstStr,
2574 static const WCHAR emptyW[] = { '\0' };
2578 if (!lpDstStr || !lpiLen)
2586 len = strlenW(lpSrcStr) + 1;
2591 CodePage = CP_UTF8; /* Fall through... */
2592 case 0x0000C350: /* FIXME: CP_ #define */
2597 INT nWideCharCount = len - 1;
2599 if (!ConvertINetUnicodeToMultiByte(&dwMode, CodePage, lpSrcStr, &nWideCharCount, lpDstStr,
2603 if (nWideCharCount < len - 1)
2605 mem = HeapAlloc(GetProcessHeap(), 0, *lpiLen);
2611 if (ConvertINetUnicodeToMultiByte(&dwMode, CodePage, lpSrcStr, &len, mem, lpiLen))
2613 SHTruncateString(mem, *lpiLen);
2614 lstrcpynA(lpDstStr, mem, *lpiLen + 1);
2615 HeapFree(GetProcessHeap(), 0, mem);
2618 HeapFree(GetProcessHeap(), 0, mem);
2621 lpDstStr[*lpiLen] = '\0';
2628 reqLen = WideCharToMultiByte(CodePage, 0, lpSrcStr, len, lpDstStr,
2629 *lpiLen, NULL, NULL);
2631 if (!reqLen && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
2633 reqLen = WideCharToMultiByte(CodePage, 0, lpSrcStr, len, NULL, 0, NULL, NULL);
2636 mem = HeapAlloc(GetProcessHeap(), 0, reqLen);
2639 reqLen = WideCharToMultiByte(CodePage, 0, lpSrcStr, len, mem,
2640 reqLen, NULL, NULL);
2642 reqLen = SHTruncateString(mem, *lpiLen);
2645 lstrcpynA(lpDstStr, mem, *lpiLen);
2647 HeapFree(GetProcessHeap(), 0, mem);
2654 /*************************************************************************
2657 * Convert a Unicode string to Ascii.
2660 * lpSrcStr [I] Source Unicode string to convert
2661 * lpDstStr [O] Destination for converted Ascii string
2662 * iLen [O] Length of lpDstStr in characters
2665 * See SHUnicodeToAnsiCP
2668 * This function simply calls SHUnicodeToAnsiCP() with CodePage = CP_ACP.
2670 INT WINAPI SHUnicodeToAnsi(LPCWSTR lpSrcStr, LPSTR lpDstStr, INT iLen)
2674 return SHUnicodeToAnsiCP(CP_ACP, lpSrcStr, lpDstStr, &myint);
2677 /*************************************************************************
2680 * Copy one string to another.
2683 * lpszSrc [I] Source string to copy
2684 * lpszDst [O] Destination for copy
2685 * iLen [I] Length of lpszDst in characters
2688 * The length of the copied string, including the terminating NUL. lpszDst
2689 * contains iLen characters of lpszSrc.
2691 DWORD WINAPI SHAnsiToAnsi(LPCSTR lpszSrc, LPSTR lpszDst, int iLen)
2695 TRACE("(%s,%p,0x%08x)\n", debugstr_a(lpszSrc), lpszDst, iLen);
2697 lpszRet = StrCpyNXA(lpszDst, lpszSrc, iLen);
2698 return lpszRet - lpszDst + 1;
2701 /*************************************************************************
2704 * Unicode version of SSHAnsiToAnsi.
2706 DWORD WINAPI SHUnicodeToUnicode(LPCWSTR lpszSrc, LPWSTR lpszDst, int iLen)
2710 TRACE("(%s,%p,0x%08x)\n", debugstr_w(lpszSrc), lpszDst, iLen);
2712 lpszRet = StrCpyNXW(lpszDst, lpszSrc, iLen);
2713 return lpszRet - lpszDst + 1;
2716 /*************************************************************************
2719 * Determine if an Ascii string converts to Unicode and back identically.
2722 * lpSrcStr [I] Source Unicode string to convert
2723 * lpDst [O] Destination for resulting Ascii string
2724 * iLen [I] Length of lpDst in characters
2727 * TRUE, since Ascii strings always convert identically.
2729 BOOL WINAPI DoesStringRoundTripA(LPCSTR lpSrcStr, LPSTR lpDst, INT iLen)
2731 lstrcpynA(lpDst, lpSrcStr, iLen);
2735 /*************************************************************************
2738 * Determine if a Unicode string converts to Ascii and back identically.
2741 * lpSrcStr [I] Source Unicode string to convert
2742 * lpDst [O] Destination for resulting Ascii string
2743 * iLen [I] Length of lpDst in characters
2746 * TRUE, if lpSrcStr converts to Ascii and back identically,
2749 BOOL WINAPI DoesStringRoundTripW(LPCWSTR lpSrcStr, LPSTR lpDst, INT iLen)
2751 WCHAR szBuff[MAX_PATH];
2753 SHUnicodeToAnsi(lpSrcStr, lpDst, iLen);
2754 SHAnsiToUnicode(lpDst, szBuff, MAX_PATH);
2755 return !strcmpW(lpSrcStr, szBuff);
2758 /*************************************************************************
2759 * SHLoadIndirectString [SHLWAPI.@]
2761 * If passed a string that begins with '@', extract the string from the
2762 * appropriate resource, otherwise do a straight copy.
2765 HRESULT WINAPI SHLoadIndirectString(LPCWSTR src, LPWSTR dst, UINT dst_len, void **reserved)
2767 WCHAR *dllname = NULL;
2768 HMODULE hmod = NULL;
2769 HRESULT hr = E_FAIL;
2771 TRACE("(%s %p %08x %p)\n", debugstr_w(src), dst, dst_len, reserved);
2779 dllname = StrDupW(src + 1);
2780 index_str = strchrW(dllname, ',');
2782 if(!index_str) goto end;
2786 index = atoiW(index_str);
2788 hmod = LoadLibraryW(dllname);
2793 if(LoadStringW(hmod, -index, dst, dst_len))
2797 FIXME("can't handle non-negative indices (%d)\n", index);
2802 lstrcpynW(dst, src, dst_len);
2806 TRACE("returning %s\n", debugstr_w(dst));
2808 if(hmod) FreeLibrary(hmod);
2809 HeapFree(GetProcessHeap(), 0, dllname);