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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #define COM_NO_WINDOWS_H
24 #include "wine/port.h"
31 #define NONAMELESSUNION
32 #define NONAMELESSSTRUCT
35 #define NO_SHLWAPI_REG
36 #define NO_SHLWAPI_STREAM
42 #include "wine/unicode.h"
43 #include "wine/debug.h"
45 WINE_DEFAULT_DEBUG_CHANNEL(shell);
47 /* Get a function pointer from a DLL handle */
48 #define GET_FUNC(func, module, name, fail) \
51 if (!SHLWAPI_h##module && !(SHLWAPI_h##module = LoadLibraryA(#module ".dll"))) return fail; \
52 func = (fn##func)GetProcAddress(SHLWAPI_h##module, name); \
53 if (!func) return fail; \
57 extern HMODULE SHLWAPI_hmlang;
59 typedef HRESULT (WINAPI *fnpConvertINetUnicodeToMultiByte)(LPDWORD,DWORD,LPCWSTR,LPINT,LPSTR,LPINT);
60 static fnpConvertINetUnicodeToMultiByte pConvertINetUnicodeToMultiByte;
62 static HRESULT WINAPI _SHStrDupAA(LPCSTR,LPSTR*);
63 static HRESULT WINAPI _SHStrDupAW(LPCWSTR,LPSTR*);
65 /*************************************************************************
66 * SHLWAPI_ChrCmpHelperA
68 * Internal helper for SHLWAPI_ChrCmpA/ChrCMPIA.
71 * Both this function and its Unicode counterpart are very inneficient. To
72 * fix this, CompareString must be completely implemented and optimised
73 * first. Then the core character test can be taken out of that function and
74 * placed here, so that it need never be called at all. Until then, do not
75 * attempt to optimise this code unless you are willing to test that it
76 * still performs correctly.
78 static BOOL WINAPI SHLWAPI_ChrCmpHelperA(WORD ch1, WORD ch2, DWORD dwFlags)
80 char str1[3], str2[3];
82 str1[0] = LOBYTE(ch1);
83 if (IsDBCSLeadByte(str1[0]))
85 str1[1] = HIBYTE(ch1);
91 str2[0] = LOBYTE(ch2);
92 if (IsDBCSLeadByte(str2[0]))
94 str2[1] = HIBYTE(ch2);
100 return CompareStringA(GetThreadLocale(), dwFlags, str1, -1, str2, -1) - 2;
103 /*************************************************************************
104 * SHLWAPI_ChrCmpHelperW
106 * Internal helper for SHLWAPI_ChrCmpW/ChrCmpIW.
108 static BOOL WINAPI SHLWAPI_ChrCmpHelperW(WCHAR ch1, WCHAR ch2, DWORD dwFlags)
110 WCHAR str1[2], str2[2];
116 return CompareStringW(GetThreadLocale(), dwFlags, str1, 2, str2, 2) - 2;
119 /*************************************************************************
122 * Internal helper function.
124 static BOOL WINAPI SHLWAPI_ChrCmpA(WORD ch1, WORD ch2)
126 return SHLWAPI_ChrCmpHelperA(ch1, ch2, 0);
129 /*************************************************************************
130 * ChrCmpIA (SHLWAPI.385)
132 * Compare two characters, ignoring case.
135 * ch1 [I] First character to compare
136 * ch2 [I] Second character to compare
139 * FALSE, if the characters are equal.
140 * Non-zero otherwise.
142 BOOL WINAPI ChrCmpIA(WORD ch1, WORD ch2)
144 TRACE("(%d,%d)\n", ch1, ch2);
146 return SHLWAPI_ChrCmpHelperA(ch1, ch2, NORM_IGNORECASE);
149 /*************************************************************************
152 * Internal helper function.
154 static BOOL WINAPI SHLWAPI_ChrCmpW(WCHAR ch1, WCHAR ch2)
156 return SHLWAPI_ChrCmpHelperW(ch1, ch2, 0);
159 /*************************************************************************
160 * ChrCmpIW [SHLWAPI.386]
164 BOOL WINAPI ChrCmpIW(WCHAR ch1, WCHAR ch2)
166 return SHLWAPI_ChrCmpHelperW(ch1, ch2, NORM_IGNORECASE);
169 /*************************************************************************
170 * StrChrA [SHLWAPI.@]
172 * Find a given character in a string.
175 * lpszStr [I] String to search in.
176 * ch [I] Character to search for.
179 * Success: A pointer to the first occurrence of ch in lpszStr, or NULL if
181 * Failure: NULL, if any arguments are invalid.
183 LPSTR WINAPI StrChrA(LPCSTR lpszStr, WORD ch)
185 TRACE("(%s,%i)\n", debugstr_a(lpszStr), ch);
191 if (!SHLWAPI_ChrCmpA(*lpszStr, ch))
192 return (LPSTR)lpszStr;
193 lpszStr = CharNextA(lpszStr);
199 /*************************************************************************
200 * StrChrW [SHLWAPI.@]
204 LPWSTR WINAPI StrChrW(LPCWSTR lpszStr, WCHAR ch)
206 LPWSTR lpszRet = NULL;
208 TRACE("(%s,%i)\n", debugstr_w(lpszStr), ch);
211 lpszRet = strchrW(lpszStr, ch);
215 /*************************************************************************
216 * StrChrIA [SHLWAPI.@]
218 * Find a given character in a string, ignoring case.
221 * lpszStr [I] String to search in.
222 * ch [I] Character to search for.
225 * Success: A pointer to the first occurrence of ch in lpszStr, or NULL if
227 * Failure: NULL, if any arguments are invalid.
229 LPSTR WINAPI StrChrIA(LPCSTR lpszStr, WORD ch)
231 TRACE("(%s,%i)\n", debugstr_a(lpszStr), ch);
237 if (!ChrCmpIA(*lpszStr, ch))
238 return (LPSTR)lpszStr;
239 lpszStr = CharNextA(lpszStr);
245 /*************************************************************************
246 * StrChrIW [SHLWAPI.@]
250 LPWSTR WINAPI StrChrIW(LPCWSTR lpszStr, WCHAR ch)
252 TRACE("(%s,%i)\n", debugstr_w(lpszStr), ch);
259 if (toupperW(*lpszStr) == ch)
260 return (LPWSTR)lpszStr;
261 lpszStr = CharNextW(lpszStr);
265 return (LPWSTR)lpszStr;
268 /*************************************************************************
269 * StrCmpIW [SHLWAPI.@]
271 * Compare two strings, ignoring case.
274 * lpszStr [I] First string to compare
275 * lpszComp [I] Second string to compare
278 * An integer less than, equal to or greater than 0, indicating that
279 * lpszStr is less than, the same, or greater than lpszComp.
281 int WINAPI StrCmpIW(LPCWSTR lpszStr, LPCWSTR lpszComp)
285 TRACE("(%s,%s)\n", debugstr_w(lpszStr),debugstr_w(lpszComp));
287 iRet = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, lpszStr, -1, lpszComp, -1);
288 return iRet == CSTR_LESS_THAN ? -1 : iRet == CSTR_GREATER_THAN ? 1 : 0;
291 /*************************************************************************
292 * StrCmpNA [SHLWAPI.@]
294 * Compare two strings, up to a maximum length.
297 * lpszStr [I] First string to compare
298 * lpszComp [I] Second string to compare
299 * iLen [I] Maximum number of chars to compare.
302 * An integer less than, equal to or greater than 0, indicating that
303 * lpszStr is less than, the same, or greater than lpszComp.
305 INT WINAPI StrCmpNA(LPCSTR lpszStr, LPCSTR lpszComp, INT iLen)
309 TRACE("(%s,%s,%i)\n", debugstr_a(lpszStr), debugstr_a(lpszComp), iLen);
311 iRet = CompareStringA(GetThreadLocale(), 0, lpszStr, iLen, lpszComp, iLen);
312 return iRet == CSTR_LESS_THAN ? -1 : iRet == CSTR_GREATER_THAN ? 1 : 0;
315 /*************************************************************************
316 * StrCmpNW [SHLWAPI.@]
320 INT WINAPI StrCmpNW(LPCWSTR lpszStr, LPCWSTR lpszComp, INT iLen)
324 TRACE("(%s,%s,%i)\n", debugstr_w(lpszStr), debugstr_w(lpszComp), iLen);
326 iRet = CompareStringW(GetThreadLocale(), 0, lpszStr, iLen, lpszComp, iLen);
327 return iRet == CSTR_LESS_THAN ? -1 : iRet == CSTR_GREATER_THAN ? 1 : 0;
330 /*************************************************************************
331 * StrCmpNIA [SHLWAPI.@]
333 * Compare two strings, up to a maximum length, ignoring case.
336 * lpszStr [I] First string to compare
337 * lpszComp [I] Second string to compare
338 * iLen [I] Maximum number of chars to compare.
341 * An integer less than, equal to or greater than 0, indicating that
342 * lpszStr is less than, the same, or greater than lpszComp.
344 int WINAPI StrCmpNIA(LPCSTR lpszStr, LPCSTR lpszComp, int iLen)
348 TRACE("(%s,%s,%i)\n", debugstr_a(lpszStr), debugstr_a(lpszComp), iLen);
350 iRet = CompareStringA(GetThreadLocale(), NORM_IGNORECASE, lpszStr, iLen, lpszComp, iLen);
351 return iRet == CSTR_LESS_THAN ? -1 : iRet == CSTR_GREATER_THAN ? 1 : 0;
354 /*************************************************************************
355 * StrCmpNIW [SHLWAPI.@]
359 INT WINAPI StrCmpNIW(LPCWSTR lpszStr, LPCWSTR lpszComp, int iLen)
363 TRACE("(%s,%s,%i)\n", debugstr_w(lpszStr), debugstr_w(lpszComp), iLen);
365 iRet = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, lpszStr, iLen, lpszComp, iLen);
366 return iRet == CSTR_LESS_THAN ? -1 : iRet == CSTR_GREATER_THAN ? 1 : 0;
369 /*************************************************************************
370 * StrCmpW [SHLWAPI.@]
372 * Compare two strings.
375 * lpszStr [I] First string to compare
376 * lpszComp [I] Second string to compare
379 * An integer less than, equal to or greater than 0, indicating that
380 * lpszStr is less than, the same, or greater than lpszComp.
382 int WINAPI StrCmpW(LPCWSTR lpszStr, LPCWSTR lpszComp)
386 TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszComp));
388 iRet = CompareStringW(GetThreadLocale(), 0, lpszStr, -1, lpszComp, -1);
389 return iRet == CSTR_LESS_THAN ? -1 : iRet == CSTR_GREATER_THAN ? 1 : 0;
392 /*************************************************************************
393 * StrCatW [SHLWAPI.@]
395 * Concatanate two strings.
398 * lpszStr [O] Initial string
399 * lpszSrc [I] String to concatanate
404 LPWSTR WINAPI StrCatW(LPWSTR lpszStr, LPCWSTR lpszSrc)
406 TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszSrc));
408 strcatW(lpszStr, lpszSrc);
412 /*************************************************************************
413 * StrCpyW [SHLWAPI.@]
415 * Copy a string to another string.
418 * lpszStr [O] Destination string
419 * lpszSrc [I] Source string
424 LPWSTR WINAPI StrCpyW(LPWSTR lpszStr, LPCWSTR lpszSrc)
426 TRACE("(%p,%s)\n", lpszStr, debugstr_w(lpszSrc));
428 strcpyW(lpszStr, lpszSrc);
432 /*************************************************************************
433 * StrCpyNW [SHLWAPI.@]
435 * Copy a string to another string, up to a maximum number of characters.
438 * lpszStr [O] Destination string
439 * lpszSrc [I] Source string
440 * iLen [I] Maximum number of chars to copy
445 LPWSTR WINAPI StrCpyNW(LPWSTR lpszStr, LPCWSTR lpszSrc, int iLen)
447 TRACE("(%p,%s,%i)\n", lpszStr, debugstr_w(lpszSrc), iLen);
449 lstrcpynW(lpszStr, lpszSrc, iLen);
455 /*************************************************************************
456 * SHLWAPI_StrStrHelperA
458 * Internal implementation of StrStrA/StrStrIA
460 static LPSTR WINAPI SHLWAPI_StrStrHelperA(LPCSTR lpszStr, LPCSTR lpszSearch,
461 int (*pStrCmpFn)(LPCSTR,LPCSTR,size_t))
465 if (!lpszStr || !lpszSearch || !*lpszSearch)
468 iLen = strlen(lpszSearch);
472 if (!pStrCmpFn(lpszStr, lpszSearch, iLen))
473 return (LPSTR)lpszStr;
474 lpszStr = CharNextA(lpszStr);
479 /*************************************************************************
480 * SHLWAPI_StrStrHelperW
482 * Internal implementation of StrStrW/StrStrIW
484 static LPWSTR WINAPI SHLWAPI_StrStrHelperW(LPCWSTR lpszStr, LPCWSTR lpszSearch,
485 int (*pStrCmpFn)(LPCWSTR,LPCWSTR,int))
489 if (!lpszStr || !lpszSearch || !*lpszSearch)
492 iLen = strlenW(lpszSearch);
496 if (!pStrCmpFn(lpszStr, lpszSearch, iLen))
497 return (LPWSTR)lpszStr;
498 lpszStr = CharNextW(lpszStr);
503 /*************************************************************************
504 * StrStrA [SHLWAPI.@]
506 * Find a substring within a string.
509 * lpszStr [I] String to search in
510 * lpszSearch [I] String to look for
513 * The start of lpszSearch within lpszStr, or NULL if not found.
515 LPSTR WINAPI StrStrA(LPCSTR lpszStr, LPCSTR lpszSearch)
517 TRACE("(%s,%s)\n", debugstr_a(lpszStr), debugstr_a(lpszSearch));
519 return SHLWAPI_StrStrHelperA(lpszStr, lpszSearch, strncmp);
522 /*************************************************************************
523 * StrStrW [SHLWAPI.@]
527 LPWSTR WINAPI StrStrW(LPCWSTR lpszStr, LPCWSTR lpszSearch)
529 TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszSearch));
531 return SHLWAPI_StrStrHelperW(lpszStr, lpszSearch, strncmpW);
534 /*************************************************************************
535 * StrRStrIA [SHLWAPI.@]
537 * Find the last occurrence of a substring within a string.
540 * lpszStr [I] String to search in
541 * lpszEnd [I] End of lpszStr
542 * lpszSearch [I] String to look for
545 * The last occurrence lpszSearch within lpszStr, or NULL if not found.
547 LPSTR WINAPI StrRStrIA(LPCSTR lpszStr, LPCSTR lpszEnd, LPCSTR lpszSearch)
549 LPSTR lpszRet = NULL;
553 TRACE("(%s,%s)\n", debugstr_a(lpszStr), debugstr_a(lpszSearch));
555 if (!lpszStr || !lpszSearch || !*lpszSearch)
559 lpszEnd = lpszStr + lstrlenA(lpszStr);
561 if (IsDBCSLeadByte(*lpszSearch))
562 ch1 = *lpszSearch << 8 | lpszSearch[1];
565 iLen = lstrlenA(lpszSearch);
567 while (lpszStr <= lpszEnd && *lpszStr)
569 ch2 = IsDBCSLeadByte(*lpszStr)? *lpszStr << 8 | lpszStr[1] : *lpszStr;
570 if (!ChrCmpIA(ch1, ch2))
572 if (!StrCmpNIA(lpszStr, lpszSearch, iLen))
573 lpszRet = (LPSTR)lpszStr;
575 lpszStr = CharNextA(lpszStr);
580 /*************************************************************************
581 * StrRStrIW [SHLWAPI.@]
585 LPWSTR WINAPI StrRStrIW(LPCWSTR lpszStr, LPCWSTR lpszEnd, LPCWSTR lpszSearch)
587 LPWSTR lpszRet = NULL;
590 TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszSearch));
592 if (!lpszStr || !lpszSearch || !*lpszSearch)
596 lpszEnd = lpszStr + strlenW(lpszStr);
598 iLen = strlenW(lpszSearch);
600 while (lpszStr <= lpszEnd && *lpszStr)
602 if (!ChrCmpIW(*lpszSearch, *lpszStr))
604 if (!StrCmpNIW(lpszStr, lpszSearch, iLen))
605 lpszRet = (LPWSTR)lpszStr;
607 lpszStr = CharNextW(lpszStr);
612 /*************************************************************************
613 * StrStrIA [SHLWAPI.@]
615 * Find a substring within a string, ignoring case.
618 * lpszStr [I] String to search in
619 * lpszSearch [I] String to look for
622 * The start of lpszSearch within lpszStr, or NULL if not found.
624 LPSTR WINAPI StrStrIA(LPCSTR lpszStr, LPCSTR lpszSearch)
626 TRACE("(%s,%s)\n", debugstr_a(lpszStr), debugstr_a(lpszSearch));
628 return SHLWAPI_StrStrHelperA(lpszStr, lpszSearch, strncasecmp);
631 /*************************************************************************
632 * StrStrIW [SHLWAPI.@]
636 LPWSTR WINAPI StrStrIW(LPCWSTR lpszStr, LPCWSTR lpszSearch)
638 TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszSearch));
640 return SHLWAPI_StrStrHelperW(lpszStr, lpszSearch, strncmpiW);
643 /*************************************************************************
644 * StrToIntA [SHLWAPI.@]
646 * Read a signed integer from a string.
649 * lpszStr [I] String to read integer from
652 * The signed integer value represented by the string, or 0 if no integer is
656 * No leading space is allowed before the number, although a leading '-' is.
658 int WINAPI StrToIntA(LPCSTR lpszStr)
662 TRACE("(%s)\n", debugstr_a(lpszStr));
666 WARN("Invalid lpszStr would crash under Win32!\n");
670 if (*lpszStr == '-' || isdigit(*lpszStr))
671 StrToIntExA(lpszStr, 0, &iRet);
675 /*************************************************************************
676 * StrToIntW [SHLWAPI.@]
680 int WINAPI StrToIntW(LPCWSTR lpszStr)
684 TRACE("(%s)\n", debugstr_w(lpszStr));
688 WARN("Invalid lpszStr would crash under Win32!\n");
692 if (*lpszStr == '-' || isdigitW(*lpszStr))
693 StrToIntExW(lpszStr, 0, &iRet);
697 /*************************************************************************
698 * StrToIntExA [SHLWAPI.@]
700 * Read an integer from a string.
703 * lpszStr [I] String to read integer from
704 * dwFlags [I] Flags controlling the conversion
705 * lpiRet [O] Destination for read integer.
708 * Success: TRUE. lpiRet contains the integer value represented by the string.
709 * Failure: FALSE, if the string is invalid, or no number is present.
712 * Leading whitespace, '-' and '+' are allowed before the number. If
713 * dwFlags includes STIF_SUPPORT_HEX, hexadecimal numbers are allowed, if
714 * preceded by '0x'. If this flag is not set, or there is no '0x' prefix,
715 * the string is treated as a decimal string. A leading '-' is ignored for
716 * hexadecimal numbers.
718 BOOL WINAPI StrToIntExA(LPCSTR lpszStr, DWORD dwFlags, LPINT lpiRet)
720 BOOL bNegative = FALSE;
723 TRACE("(%s,%08lX,%p)\n", debugstr_a(lpszStr), dwFlags, lpiRet);
725 if (!lpszStr || !lpiRet)
727 WARN("Invalid parameter would crash under Win32!\n");
730 if (dwFlags > STIF_SUPPORT_HEX)
732 WARN("Unknown flags (%08lX)!\n", dwFlags & ~STIF_SUPPORT_HEX);
735 /* Skip leading space, '+', '-' */
736 while (isspace(*lpszStr))
737 lpszStr = CharNextA(lpszStr);
744 else if (*lpszStr == '+')
747 if (dwFlags & STIF_SUPPORT_HEX &&
748 *lpszStr == '0' && tolower(lpszStr[1]) == 'x')
750 /* Read hex number */
753 if (!isxdigit(*lpszStr))
756 while (isxdigit(*lpszStr))
759 if (isdigit(*lpszStr))
760 iRet += (*lpszStr - '0');
762 iRet += 10 + (tolower(*lpszStr) - 'a');
769 /* Read decimal number */
770 if (!isdigit(*lpszStr))
773 while (isdigit(*lpszStr))
776 iRet += (*lpszStr - '0');
779 *lpiRet = bNegative ? -iRet : iRet;
783 /*************************************************************************
784 * StrToIntExW [SHLWAPI.@]
788 BOOL WINAPI StrToIntExW(LPCWSTR lpszStr, DWORD dwFlags, LPINT lpiRet)
790 BOOL bNegative = FALSE;
793 TRACE("(%s,%08lX,%p)\n", debugstr_w(lpszStr), dwFlags, lpiRet);
795 if (!lpszStr || !lpiRet)
797 WARN("Invalid parameter would crash under Win32!\n");
800 if (dwFlags > STIF_SUPPORT_HEX)
802 WARN("Unknown flags (%08lX)!\n", dwFlags & ~STIF_SUPPORT_HEX);
805 /* Skip leading space, '+', '-' */
806 while (isspaceW(*lpszStr))
807 lpszStr = CharNextW(lpszStr);
814 else if (*lpszStr == '+')
817 if (dwFlags & STIF_SUPPORT_HEX &&
818 *lpszStr == '0' && tolowerW(lpszStr[1]) == 'x')
820 /* Read hex number */
823 if (!isxdigitW(*lpszStr))
826 while (isxdigitW(*lpszStr))
829 if (isdigitW(*lpszStr))
830 iRet += (*lpszStr - '0');
832 iRet += 10 + (tolowerW(*lpszStr) - 'a');
839 /* Read decimal number */
840 if (!isdigitW(*lpszStr))
843 while (isdigitW(*lpszStr))
846 iRet += (*lpszStr - '0');
849 *lpiRet = bNegative ? -iRet : iRet;
853 /*************************************************************************
854 * StrDupA [SHLWAPI.@]
856 * Duplicate a string.
859 * lpszStr [I] String to duplicate.
862 * Success: A pointer to a new string containing the contents of lpszStr
863 * Failure: NULL, if memory cannot be allocated
866 * The string memory is allocated with LocalAlloc(), and so should be released
867 * by calling LocalFree().
869 LPSTR WINAPI StrDupA(LPCSTR lpszStr)
874 TRACE("(%s)\n",debugstr_a(lpszStr));
876 iLen = lpszStr ? strlen(lpszStr) + 1 : 1;
877 lpszRet = (LPSTR)LocalAlloc(LMEM_FIXED, iLen);
882 memcpy(lpszRet, lpszStr, iLen);
889 /*************************************************************************
890 * StrDupW [SHLWAPI.@]
894 LPWSTR WINAPI StrDupW(LPCWSTR lpszStr)
899 TRACE("(%s)\n",debugstr_w(lpszStr));
901 iLen = (lpszStr ? strlenW(lpszStr) + 1 : 1) * sizeof(WCHAR);
902 lpszRet = (LPWSTR)LocalAlloc(LMEM_FIXED, iLen);
907 memcpy(lpszRet, lpszStr, iLen);
914 /*************************************************************************
915 * SHLWAPI_StrSpnHelperA
917 * Internal implementation of StrSpnA/StrCSpnA/StrCSpnIA
919 static int WINAPI SHLWAPI_StrSpnHelperA(LPCSTR lpszStr, LPCSTR lpszMatch,
920 LPSTR (WINAPI *pStrChrFn)(LPCSTR,WORD),
923 LPCSTR lpszRead = lpszStr;
924 if (lpszStr && *lpszStr && lpszMatch)
928 LPCSTR lpszTest = pStrChrFn(lpszMatch, *lpszRead);
930 if (!bInvert && !lpszTest)
932 if (bInvert && lpszTest)
934 lpszRead = CharNextA(lpszRead);
937 return lpszRead - lpszStr;
940 /*************************************************************************
941 * SHLWAPI_StrSpnHelperW
943 * Internal implementation of StrSpnW/StrCSpnW/StrCSpnIW
945 static int WINAPI SHLWAPI_StrSpnHelperW(LPCWSTR lpszStr, LPCWSTR lpszMatch,
946 LPWSTR (WINAPI *pStrChrFn)(LPCWSTR,WCHAR),
949 LPCWSTR lpszRead = lpszStr;
950 if (lpszStr && *lpszStr && lpszMatch)
954 LPCWSTR lpszTest = pStrChrFn(lpszMatch, *lpszRead);
956 if (!bInvert && !lpszTest)
958 if (bInvert && lpszTest)
960 lpszRead = CharNextW(lpszRead);
963 return lpszRead - lpszStr;
966 /*************************************************************************
967 * StrSpnA [SHLWAPI.@]
969 * Find the length of the start of a string that contains only certain
973 * lpszStr [I] String to search
974 * lpszMatch [I] Characters that can be in the substring
977 * The length of the part of lpszStr containing only chars from lpszMatch,
978 * or 0 if any parameter is invalid.
980 int WINAPI StrSpnA(LPCSTR lpszStr, LPCSTR lpszMatch)
982 TRACE("(%s,%s)\n",debugstr_a(lpszStr), debugstr_a(lpszMatch));
984 return SHLWAPI_StrSpnHelperA(lpszStr, lpszMatch, StrChrA, FALSE);
987 /*************************************************************************
988 * StrSpnW [SHLWAPI.@]
992 int WINAPI StrSpnW(LPCWSTR lpszStr, LPCWSTR lpszMatch)
994 TRACE("(%s,%s)\n",debugstr_w(lpszStr), debugstr_w(lpszMatch));
996 return SHLWAPI_StrSpnHelperW(lpszStr, lpszMatch, StrChrW, FALSE);
999 /*************************************************************************
1000 * StrCSpnA [SHLWAPI.@]
1002 * Find the length of the start of a string that does not contain certain
1006 * lpszStr [I] String to search
1007 * lpszMatch [I] Characters that cannot be in the substring
1010 * The length of the part of lpszStr containing only chars not in lpszMatch,
1011 * or 0 if any parameter is invalid.
1013 int WINAPI StrCSpnA(LPCSTR lpszStr, LPCSTR lpszMatch)
1015 TRACE("(%s,%s)\n",debugstr_a(lpszStr), debugstr_a(lpszMatch));
1017 return SHLWAPI_StrSpnHelperA(lpszStr, lpszMatch, StrChrA, TRUE);
1020 /*************************************************************************
1021 * StrCSpnW [SHLWAPI.@]
1025 int WINAPI StrCSpnW(LPCWSTR lpszStr, LPCWSTR lpszMatch)
1027 TRACE("(%s,%s)\n",debugstr_w(lpszStr), debugstr_w(lpszMatch));
1029 return SHLWAPI_StrSpnHelperW(lpszStr, lpszMatch, StrChrW, TRUE);
1032 /*************************************************************************
1033 * StrCSpnIA [SHLWAPI.@]
1035 * Find the length of the start of a string that does not contain certain
1036 * characters, ignoring case.
1039 * lpszStr [I] String to search
1040 * lpszMatch [I] Characters that cannot be in the substring
1043 * The length of the part of lpszStr containing only chars not in lpszMatch,
1044 * or 0 if any parameter is invalid.
1046 int WINAPI StrCSpnIA(LPCSTR lpszStr, LPCSTR lpszMatch)
1048 TRACE("(%s,%s)\n",debugstr_a(lpszStr), debugstr_a(lpszMatch));
1050 return SHLWAPI_StrSpnHelperA(lpszStr, lpszMatch, StrChrIA, TRUE);
1053 /*************************************************************************
1054 * StrCSpnIW [SHLWAPI.@]
1058 int WINAPI StrCSpnIW(LPCWSTR lpszStr, LPCWSTR lpszMatch)
1060 TRACE("(%s,%s)\n",debugstr_w(lpszStr), debugstr_w(lpszMatch));
1062 return SHLWAPI_StrSpnHelperW(lpszStr, lpszMatch, StrChrIW, TRUE);
1065 /*************************************************************************
1066 * StrPBrkA [SHLWAPI.@]
1068 * Search a string for any of a group of characters.
1071 * lpszStr [I] String to search
1072 * lpszMatch [I] Characters to match
1075 * A pointer to the first matching character in lpszStr, or NULL if no
1078 LPSTR WINAPI StrPBrkA(LPCSTR lpszStr, LPCSTR lpszMatch)
1080 TRACE("(%s,%s)\n",debugstr_a(lpszStr), debugstr_a(lpszMatch));
1082 if (lpszStr && lpszMatch && *lpszMatch)
1086 if (StrChrA(lpszMatch, *lpszStr))
1087 return (LPSTR)lpszStr;
1088 lpszStr = CharNextA(lpszStr);
1094 /*************************************************************************
1095 * StrPBrkW [SHLWAPI.@]
1099 LPWSTR WINAPI StrPBrkW(LPCWSTR lpszStr, LPCWSTR lpszMatch)
1101 TRACE("(%s,%s)\n",debugstr_w(lpszStr), debugstr_w(lpszMatch));
1103 if (lpszStr && lpszMatch && *lpszMatch)
1107 if (StrChrW(lpszMatch, *lpszStr))
1108 return (LPWSTR)lpszStr;
1109 lpszStr = CharNextW(lpszStr);
1115 /*************************************************************************
1116 * SHLWAPI_StrRChrHelperA
1118 * Internal implementation of StrRChrA/StrRChrIA.
1120 static LPSTR WINAPI SHLWAPI_StrRChrHelperA(LPCSTR lpszStr,
1121 LPCSTR lpszEnd, WORD ch,
1122 BOOL (WINAPI *pChrCmpFn)(WORD,WORD))
1124 LPCSTR lpszRet = NULL;
1131 lpszEnd = lpszStr + lstrlenA(lpszStr);
1133 while (*lpszStr && lpszStr <= lpszEnd)
1135 ch2 = IsDBCSLeadByte(*lpszStr)? *lpszStr << 8 | lpszStr[1] : *lpszStr;
1137 if (!pChrCmpFn(ch, ch2))
1139 lpszStr = CharNextA(lpszStr);
1142 return (LPSTR)lpszRet;
1145 /*************************************************************************
1146 * SHLWAPI_StrRChrHelperW
1148 * Internal implementation of StrRChrW/StrRChrIW.
1150 static LPWSTR WINAPI SHLWAPI_StrRChrHelperW(LPCWSTR lpszStr,
1151 LPCWSTR lpszEnd, WCHAR ch,
1152 BOOL (WINAPI *pChrCmpFn)(WCHAR,WCHAR))
1154 LPCWSTR lpszRet = NULL;
1159 lpszEnd = lpszStr + strlenW(lpszStr);
1161 while (*lpszStr && lpszStr <= lpszEnd)
1163 if (!pChrCmpFn(ch, *lpszStr))
1165 lpszStr = CharNextW(lpszStr);
1168 return (LPWSTR)lpszRet;
1171 /**************************************************************************
1172 * StrRChrA [SHLWAPI.@]
1174 * Find the last occurrence of a character in string.
1177 * lpszStr [I] String to search in
1178 * lpszEnd [I] Place to end search, or NULL to search until the end of lpszStr
1179 * ch [I] Character to search for.
1182 * Success: A pointer to the last occurrence of ch in lpszStr before lpszEnd,
1183 * or NULL if not found.
1184 * Failure: NULL, if any arguments are invalid.
1186 LPSTR WINAPI StrRChrA(LPCSTR lpszStr, LPCSTR lpszEnd, WORD ch)
1188 TRACE("(%s,%s,%x)\n", debugstr_a(lpszStr), debugstr_a(lpszEnd), ch);
1190 return SHLWAPI_StrRChrHelperA(lpszStr, lpszEnd, ch, SHLWAPI_ChrCmpA);
1193 /**************************************************************************
1194 * StrRChrW [SHLWAPI.@]
1198 LPWSTR WINAPI StrRChrW(LPCWSTR lpszStr, LPCWSTR lpszEnd, WORD ch)
1200 TRACE("(%s,%s,%x)\n", debugstr_w(lpszStr), debugstr_w(lpszEnd), ch);
1202 return SHLWAPI_StrRChrHelperW(lpszStr, lpszEnd, ch, SHLWAPI_ChrCmpW);
1205 /**************************************************************************
1206 * StrRChrIA [SHLWAPI.@]
1208 * Find the last occurrence of a character in string, ignoring case.
1211 * lpszStr [I] String to search in
1212 * lpszEnd [I] Place to end search, or NULL to search until the end of lpszStr
1213 * ch [I] Character to search for.
1216 * Success: A pointer to the last occurrence of ch in lpszStr before lpszEnd,
1217 * or NULL if not found.
1218 * Failure: NULL, if any arguments are invalid.
1220 LPSTR WINAPI StrRChrIA(LPCSTR lpszStr, LPCSTR lpszEnd, WORD ch)
1222 TRACE("(%s,%s,%x)\n", debugstr_a(lpszStr), debugstr_a(lpszEnd), ch);
1224 return SHLWAPI_StrRChrHelperA(lpszStr, lpszEnd, ch, ChrCmpIA);
1227 /**************************************************************************
1228 * StrRChrIW [SHLWAPI.@]
1232 LPWSTR WINAPI StrRChrIW(LPCWSTR lpszStr, LPCWSTR lpszEnd, WORD ch)
1234 TRACE("(%s,%s,%x)\n", debugstr_w(lpszStr), debugstr_w(lpszEnd), ch);
1236 return SHLWAPI_StrRChrHelperW(lpszStr, lpszEnd, ch, ChrCmpIW);
1239 /*************************************************************************
1240 * StrCatBuffA [SHLWAPI.@]
1242 * Concatenate two strings together.
1245 * lpszStr [O] String to concatenate to
1246 * lpszCat [I] String to add to lpszCat
1247 * cchMax [I] Maximum number of characters for the whole string
1253 * cchMax determines the number of characters in the final length of the
1254 * string, not the number appended to lpszStr from lpszCat.
1256 LPSTR WINAPI StrCatBuffA(LPSTR lpszStr, LPCSTR lpszCat, INT cchMax)
1260 TRACE("(%p,%s,%d)\n", lpszStr, debugstr_a(lpszCat), cchMax);
1264 WARN("Invalid lpszStr would crash under Win32!\n");
1268 iLen = strlen(lpszStr);
1272 StrCpyNA(lpszStr + iLen, lpszCat, cchMax);
1276 /*************************************************************************
1277 * StrCatBuffW [SHLWAPI.@]
1281 LPWSTR WINAPI StrCatBuffW(LPWSTR lpszStr, LPCWSTR lpszCat, INT cchMax)
1285 TRACE("(%p,%s,%d)\n", lpszStr, debugstr_w(lpszCat), cchMax);
1289 WARN("Invalid lpszStr would crash under Win32!\n");
1293 iLen = strlenW(lpszStr);
1297 StrCpyNW(lpszStr + iLen, lpszCat, cchMax);
1301 /*************************************************************************
1302 * StrRetToBufA [SHLWAPI.@]
1304 * Convert a STRRET to a normal string.
1307 * lpStrRet [O] STRRET to convert
1308 * pIdl [I] ITEMIDLIST for lpStrRet->uType == STRRET_OFFSET
1309 * lpszDest [O] Destination for normal string
1310 * dwLen [I] Length of lpszDest
1313 * Success: S_OK. lpszDest contains up to dwLen characters of the string.
1314 * If lpStrRet is of type STRRET_WSTR, its memory is freed with
1315 * CoTaskMemFree() and its type set to STRRET_CSTRA.
1316 * Failure: E_FAIL, if any parameters are invalid.
1318 HRESULT WINAPI StrRetToBufA (LPSTRRET src, const ITEMIDLIST *pidl, LPSTR dest, UINT len)
1321 * This routine is identical to that in dlls/shell32/shellstring.c.
1322 * It was duplicated because not every version of Shlwapi.dll exports
1323 * StrRetToBufA. If you change one routine, change them both.
1325 TRACE("dest=%p len=0x%x strret=%p pidl=%p stub\n",dest,len,src,pidl);
1329 WARN("Invalid lpStrRet would crash under Win32!\n");
1343 WideCharToMultiByte(CP_ACP, 0, src->u.pOleStr, -1, dest, len, NULL, NULL);
1344 CoTaskMemFree(src->u.pOleStr);
1348 lstrcpynA((LPSTR)dest, src->u.cStr, len);
1352 lstrcpynA((LPSTR)dest, ((LPCSTR)&pidl->mkid)+src->u.uOffset, len);
1356 FIXME("unknown type!\n");
1362 /*************************************************************************
1363 * StrRetToBufW [SHLWAPI.@]
1367 HRESULT WINAPI StrRetToBufW (LPSTRRET src, const ITEMIDLIST *pidl, LPWSTR dest, UINT len)
1369 TRACE("dest=%p len=0x%x strret=%p pidl=%p stub\n",dest,len,src,pidl);
1373 WARN("Invalid lpStrRet would crash under Win32!\n");
1387 lstrcpynW((LPWSTR)dest, src->u.pOleStr, len);
1388 CoTaskMemFree(src->u.pOleStr);
1392 if (!MultiByteToWideChar( CP_ACP, 0, src->u.cStr, -1, dest, len ) && len)
1399 if (!MultiByteToWideChar( CP_ACP, 0, ((LPCSTR)&pidl->mkid)+src->u.uOffset, -1,
1406 FIXME("unknown type!\n");
1412 /*************************************************************************
1413 * StrRetToStrA [SHLWAPI.@]
1415 * Converts a STRRET to a normal string.
1418 * lpStrRet [O] STRRET to convert
1419 * pidl [I] ITEMIDLIST for lpStrRet->uType == STRRET_OFFSET
1420 * ppszName [O] Destination for converted string
1423 * Success: S_OK. ppszName contains the new string, allocated with CoTaskMemAlloc().
1424 * Failure: E_FAIL, if any parameters are invalid.
1426 HRESULT WINAPI StrRetToStrA(LPSTRRET lpStrRet, const ITEMIDLIST *pidl, LPSTR *ppszName)
1428 HRESULT hRet = E_FAIL;
1430 switch (lpStrRet->uType)
1433 hRet = _SHStrDupAW(lpStrRet->u.pOleStr, ppszName);
1434 CoTaskMemFree(lpStrRet->u.pOleStr);
1438 hRet = _SHStrDupAA(lpStrRet->u.cStr, ppszName);
1442 hRet = _SHStrDupAA(((LPCSTR)&pidl->mkid) + lpStrRet->u.uOffset, ppszName);
1452 /*************************************************************************
1453 * StrRetToStrW [SHLWAPI.@]
1457 HRESULT WINAPI StrRetToStrW(LPSTRRET lpStrRet, const ITEMIDLIST *pidl, LPWSTR *ppszName)
1459 HRESULT hRet = E_FAIL;
1461 switch (lpStrRet->uType)
1464 hRet = SHStrDupW(lpStrRet->u.pOleStr, ppszName);
1465 CoTaskMemFree(lpStrRet->u.pOleStr);
1469 hRet = SHStrDupA(lpStrRet->u.cStr, ppszName);
1473 hRet = SHStrDupA(((LPCSTR)&pidl->mkid) + lpStrRet->u.uOffset, ppszName);
1483 /* Create an ASCII string copy using SysAllocString() */
1484 static HRESULT _SHStrDupAToBSTR(LPCSTR src, BSTR *pBstrOut)
1490 INT len = MultiByteToWideChar(CP_ACP, 0, src, -1, NULL, 0);
1491 WCHAR* szTemp = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1495 MultiByteToWideChar(CP_ACP, 0, src, -1, szTemp, len);
1496 *pBstrOut = SysAllocString(szTemp);
1497 HeapFree(GetProcessHeap(), 0, szTemp);
1503 return E_OUTOFMEMORY;
1506 /*************************************************************************
1507 * StrRetToBSTR [SHLWAPI.@]
1509 * Converts a STRRET to a BSTR.
1512 * lpStrRet [O] STRRET to convert
1513 * pidl [I] ITEMIDLIST for lpStrRet->uType = STRRET_OFFSET
1514 * pBstrOut [O] Destination for converted BSTR
1517 * Success: S_OK. pBstrOut contains the new string.
1518 * Failure: E_FAIL, if any parameters are invalid.
1520 HRESULT WINAPI StrRetToBSTR(STRRET *lpStrRet, LPCITEMIDLIST pidl, BSTR* pBstrOut)
1522 HRESULT hRet = E_FAIL;
1524 switch (lpStrRet->uType)
1527 *pBstrOut = SysAllocString(lpStrRet->u.pOleStr);
1530 CoTaskMemFree(lpStrRet->u.pOleStr);
1534 hRet = _SHStrDupAToBSTR(lpStrRet->u.cStr, pBstrOut);
1538 hRet = _SHStrDupAToBSTR(((LPCSTR)&pidl->mkid) + lpStrRet->u.uOffset, pBstrOut);
1548 /*************************************************************************
1549 * StrFormatKBSizeA [SHLWAPI.@]
1551 * Create a formatted string containing a byte count in Kilobytes.
1554 * llBytes [I] Byte size to format
1555 * lpszDest [I] Destination for formatted string
1556 * cchMax [I] Size of lpszDest
1561 LPSTR WINAPI StrFormatKBSizeA(LONGLONG llBytes, LPSTR lpszDest, UINT cchMax)
1563 char szBuff[256], *szOut = szBuff + sizeof(szBuff) - 1;
1564 LONGLONG ulKB = (llBytes + 1023) >> 10;
1566 TRACE("(%lld,%p,%d)\n", llBytes, lpszDest, cchMax);
1575 LONGLONG ulNextDigit = ulKB % 10;
1576 *szOut-- = '0' + ulNextDigit;
1577 ulKB = (ulKB - ulNextDigit) / 10;
1580 lstrcpynA(lpszDest, szOut + 1, cchMax);
1584 /*************************************************************************
1585 * StrFormatKBSizeW [SHLWAPI.@]
1587 * See StrFormatKBSizeA.
1589 LPWSTR WINAPI StrFormatKBSizeW(LONGLONG llBytes, LPWSTR lpszDest, UINT cchMax)
1591 WCHAR szBuff[256], *szOut = szBuff + sizeof(szBuff)/sizeof(WCHAR) - 1;
1592 LONGLONG ulKB = (llBytes + 1023) >> 10;
1594 TRACE("(%lld,%p,%d)\n", llBytes, lpszDest, cchMax);
1603 LONGLONG ulNextDigit = ulKB % 10;
1604 *szOut-- = '0' + ulNextDigit;
1605 ulKB = (ulKB - ulNextDigit) / 10;
1608 lstrcpynW(lpszDest, szOut + 1, cchMax);
1612 /*************************************************************************
1613 * StrNCatA [SHLWAPI.@]
1615 * Concatenate two strings together.
1618 * lpszStr [O] String to concatenate to
1619 * lpszCat [I] String to add to lpszCat
1620 * cchMax [I] Maximum number of characters to concatenate
1626 * cchMax determines the number of characters that are appended to lpszStr,
1627 * not the total length of the string.
1629 LPSTR WINAPI StrNCatA(LPSTR lpszStr, LPCSTR lpszCat, INT cchMax)
1631 LPSTR lpszRet = lpszStr;
1633 TRACE("(%s,%s,%i)\n", debugstr_a(lpszStr), debugstr_a(lpszCat), cchMax);
1637 WARN("Invalid lpszStr would crash under Win32!\n");
1641 StrCpyNA(lpszStr + strlen(lpszStr), lpszCat, cchMax);
1645 /*************************************************************************
1646 * StrNCatW [SHLWAPI.@]
1650 LPWSTR WINAPI StrNCatW(LPWSTR lpszStr, LPCWSTR lpszCat, INT cchMax)
1652 LPWSTR lpszRet = lpszStr;
1654 TRACE("(%s,%s,%i)\n", debugstr_w(lpszStr), debugstr_w(lpszCat), cchMax);
1658 WARN("Invalid lpszStr would crash under Win32\n");
1662 StrCpyNW(lpszStr + strlenW(lpszStr), lpszCat, cchMax);
1666 /*************************************************************************
1667 * StrTrimA [SHLWAPI.@]
1669 * Remove characters from the start and end of a string.
1672 * lpszStr [O] String to remove characters from
1673 * lpszTrim [I] Characters to remove from lpszStr
1676 * TRUE If lpszStr was valid and modified
1679 BOOL WINAPI StrTrimA(LPSTR lpszStr, LPCSTR lpszTrim)
1682 LPSTR lpszRead = lpszStr;
1685 TRACE("(%s,%s)\n", debugstr_a(lpszStr), debugstr_a(lpszTrim));
1687 if (lpszRead && *lpszRead)
1689 while (*lpszRead && StrChrA(lpszTrim, *lpszRead))
1690 lpszRead = CharNextA(lpszRead); /* Skip leading matches */
1692 dwLen = strlen(lpszRead);
1694 if (lpszRead != lpszStr)
1696 memmove(lpszStr, lpszRead, dwLen + 1);
1701 lpszRead = lpszStr + dwLen;
1702 while (StrChrA(lpszTrim, lpszRead[-1]))
1703 lpszRead = CharPrevA(lpszStr, lpszRead); /* Skip trailing matches */
1705 if (lpszRead != lpszStr + dwLen)
1715 /*************************************************************************
1716 * StrTrimW [SHLWAPI.@]
1720 BOOL WINAPI StrTrimW(LPWSTR lpszStr, LPCWSTR lpszTrim)
1723 LPWSTR lpszRead = lpszStr;
1726 TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszTrim));
1728 if (lpszRead && *lpszRead)
1730 while (*lpszRead && StrChrW(lpszTrim, *lpszRead))
1731 lpszRead = CharNextW(lpszRead); /* Skip leading matches */
1733 dwLen = strlenW(lpszRead);
1735 if (lpszRead != lpszStr)
1737 memmove(lpszStr, lpszRead, (dwLen + 1) * sizeof(WCHAR));
1742 lpszRead = lpszStr + dwLen;
1743 while (StrChrW(lpszTrim, lpszRead[-1]))
1744 lpszRead = CharPrevW(lpszStr, lpszRead); /* Skip trailing matches */
1746 if (lpszRead != lpszStr + dwLen)
1756 /*************************************************************************
1757 * _SHStrDupAA [INTERNAL]
1759 * Duplicates a ASCII string to ASCII. The destination buffer is allocated.
1761 static HRESULT WINAPI _SHStrDupAA(LPCSTR src, LPSTR * dest)
1767 len = lstrlenA(src) + 1;
1768 *dest = CoTaskMemAlloc(len);
1774 lstrcpynA(*dest,src, len);
1780 TRACE("%s->(%p)\n", debugstr_a(src), *dest);
1784 /*************************************************************************
1785 * SHStrDupA [SHLWAPI.@]
1787 * Return a Unicode copy of a string, in memory allocated by CoTaskMemAlloc().
1790 * lpszStr [I] String to copy
1791 * lppszDest [O] Destination for the new string copy
1794 * Success: S_OK. lppszDest contains the new string in Unicode format.
1795 * Failure: E_OUTOFMEMORY, If any arguments are invalid or memory allocation
1798 HRESULT WINAPI SHStrDupA(LPCSTR lpszStr, LPWSTR * lppszDest)
1805 len = MultiByteToWideChar(0, 0, lpszStr, -1, 0, 0) * sizeof(WCHAR);
1806 *lppszDest = CoTaskMemAlloc(len);
1813 MultiByteToWideChar(0, 0, lpszStr, -1, *lppszDest, len/sizeof(WCHAR));
1817 hRet = E_OUTOFMEMORY;
1819 TRACE("%s->(%p)\n", debugstr_a(lpszStr), *lppszDest);
1823 /*************************************************************************
1824 * _SHStrDupAW [INTERNAL]
1826 * Duplicates a UNICODE to a ASCII string. The destination buffer is allocated.
1828 static HRESULT WINAPI _SHStrDupAW(LPCWSTR src, LPSTR * dest)
1834 len = WideCharToMultiByte(CP_ACP, 0, src, -1, NULL, 0, NULL, NULL);
1835 *dest = CoTaskMemAlloc(len);
1841 WideCharToMultiByte(CP_ACP, 0, src, -1, *dest, len, NULL, NULL);
1847 TRACE("%s->(%p)\n", debugstr_w(src), *dest);
1851 /*************************************************************************
1852 * SHStrDupW [SHLWAPI.@]
1856 HRESULT WINAPI SHStrDupW(LPCWSTR src, LPWSTR * dest)
1862 len = (lstrlenW(src) + 1) * sizeof(WCHAR);
1863 *dest = CoTaskMemAlloc(len);
1869 memcpy(*dest, src, len);
1875 TRACE("%s->(%p)\n", debugstr_w(src), *dest);
1879 /*************************************************************************
1880 * SHLWAPI_WriteReverseNum
1882 * Internal helper for SHLWAPI_WriteTimeClass.
1884 inline static LPWSTR SHLWAPI_WriteReverseNum(LPWSTR lpszOut, DWORD dwNum)
1888 /* Write a decimal number to a string, backwards */
1891 DWORD dwNextDigit = dwNum % 10;
1892 *lpszOut-- = '0' + dwNextDigit;
1893 dwNum = (dwNum - dwNextDigit) / 10;
1894 } while (dwNum > 0);
1899 /*************************************************************************
1900 * SHLWAPI_FormatSignificant
1902 * Internal helper for SHLWAPI_WriteTimeClass.
1904 inline static int SHLWAPI_FormatSignificant(LPWSTR lpszNum, int dwDigits)
1906 /* Zero non significant digits, return remaining significant digits */
1910 if (--dwDigits == 0)
1920 /*************************************************************************
1921 * SHLWAPI_WriteTimeClass
1923 * Internal helper for StrFromTimeIntervalW.
1925 static int WINAPI SHLWAPI_WriteTimeClass(LPWSTR lpszOut, DWORD dwValue,
1926 LPCWSTR lpszClass, int iDigits)
1928 WCHAR szBuff[64], *szOut = szBuff + 32;
1930 szOut = SHLWAPI_WriteReverseNum(szOut, dwValue);
1931 iDigits = SHLWAPI_FormatSignificant(szOut + 1, iDigits);
1933 strcpyW(szBuff + 32, lpszClass);
1934 strcatW(lpszOut, szOut);
1938 /*************************************************************************
1939 * StrFromTimeIntervalA [SHLWAPI.@]
1941 * Format a millisecond time interval into a string
1944 * lpszStr [O] Output buffer for formatted time interval
1945 * cchMax [I] Size of lpszStr
1946 * dwMS [I] Number of milliseconds
1947 * iDigits [I] Number of digits to print
1950 * The length of the formatted string, or 0 if any parameter is invalid.
1953 * This implementation mimics the Win32 behaviour of always writing a leading
1954 * space before the time interval begins.
1956 * iDigits is used to provide approximate times if accuracy is not important.
1957 * This number of digits will be written of the first non-zero time class
1958 * (hours/minutes/seconds). If this does not complete the time classification,
1959 * the remaining digits are changed to zeros (i.e. The time is _not_ rounded).
1960 * If there are digits remaining following the writing of a time class, the
1961 * next time class will be written.
1963 * For example, given dwMS represents 138 hours,43 minutes and 15 seconds, the
1964 * following will result from the given values of iDigits:
1966 *| iDigits 1 2 3 4 5 ...
1967 *| lpszStr "100 hr" "130 hr" "138 hr" "138 hr 40 min" "138 hr 43 min" ...
1969 INT WINAPI StrFromTimeIntervalA(LPSTR lpszStr, UINT cchMax, DWORD dwMS,
1974 TRACE("(%p,%d,%ld,%d)\n", lpszStr, cchMax, dwMS, iDigits);
1976 if (lpszStr && cchMax)
1979 StrFromTimeIntervalW(szBuff, sizeof(szBuff)/sizeof(WCHAR), dwMS, iDigits);
1980 WideCharToMultiByte(CP_ACP,0,szBuff,-1,lpszStr,cchMax,0,0);
1986 /*************************************************************************
1987 * StrFromTimeIntervalW [SHLWAPI.@]
1989 * See StrFromTimeIntervalA.
1991 INT WINAPI StrFromTimeIntervalW(LPWSTR lpszStr, UINT cchMax, DWORD dwMS,
1994 static const WCHAR szHr[] = {' ','h','r','\0'};
1995 static const WCHAR szMin[] = {' ','m','i','n','\0'};
1996 static const WCHAR szSec[] = {' ','s','e','c','\0'};
1999 TRACE("(%p,%d,%ld,%d)\n", lpszStr, cchMax, dwMS, iDigits);
2001 if (lpszStr && cchMax)
2004 DWORD dwHours, dwMinutes;
2006 if (!iDigits || cchMax == 1)
2012 /* Calculate the time classes */
2013 dwMS = (dwMS + 500) / 1000;
2014 dwHours = dwMS / 3600;
2015 dwMS -= dwHours * 3600;
2016 dwMinutes = dwMS / 60;
2017 dwMS -= dwMinutes * 60;
2022 iDigits = SHLWAPI_WriteTimeClass(szCopy, dwHours, szHr, iDigits);
2024 if (dwMinutes && iDigits)
2025 iDigits = SHLWAPI_WriteTimeClass(szCopy, dwMinutes, szMin, iDigits);
2027 if (iDigits) /* Always write seconds if we have significant digits */
2028 SHLWAPI_WriteTimeClass(szCopy, dwMS, szSec, iDigits);
2030 lstrcpynW(lpszStr, szCopy, cchMax);
2031 iRet = strlenW(lpszStr);
2036 /*************************************************************************
2037 * StrIsIntlEqualA [SHLWAPI.@]
2039 * Compare two strings.
2042 * bCase [I] Whether to compare case sensitively
2043 * lpszStr [I] First string to compare
2044 * lpszComp [I] Second string to compare
2045 * iLen [I] Length to compare
2048 * TRUE If the strings are equal.
2051 BOOL WINAPI StrIsIntlEqualA(BOOL bCase, LPCSTR lpszStr, LPCSTR lpszComp,
2056 TRACE("(%d,%s,%s,%d)\n", bCase,
2057 debugstr_a(lpszStr), debugstr_a(lpszComp), iLen);
2059 /* FIXME: This flag is undocumented and unknown by our CompareString.
2060 * We need a define for it.
2062 dwFlags = 0x10000000;
2063 if (!bCase) dwFlags |= NORM_IGNORECASE;
2065 return (CompareStringA(GetThreadLocale(), dwFlags, lpszStr, iLen, lpszComp, iLen) == CSTR_EQUAL);
2068 /*************************************************************************
2069 * StrIsIntlEqualW [SHLWAPI.@]
2071 * See StrIsIntlEqualA.
2073 BOOL WINAPI StrIsIntlEqualW(BOOL bCase, LPCWSTR lpszStr, LPCWSTR lpszComp,
2078 TRACE("(%d,%s,%s,%d)\n", bCase,
2079 debugstr_w(lpszStr),debugstr_w(lpszComp), iLen);
2081 /* FIXME: This flag is undocumented and unknown by our CompareString.
2082 * We need a define for it.
2084 dwFlags = 0x10000000;
2085 if (!bCase) dwFlags |= NORM_IGNORECASE;
2087 return (CompareStringW(GetThreadLocale(), dwFlags, lpszStr, iLen, lpszComp, iLen) == CSTR_EQUAL);
2090 /*************************************************************************
2093 * Copy a string to another string, up to a maximum number of characters.
2096 * lpszDest [O] Destination string
2097 * lpszSrc [I] Source string
2098 * iLen [I] Maximum number of chars to copy
2101 * Success: A pointer to the last character written to lpszDest..
2102 * Failure: lpszDest, if any arguments are invalid.
2104 LPSTR WINAPI StrCpyNXA(LPSTR lpszDest, LPCSTR lpszSrc, int iLen)
2106 TRACE("(%p,%s,%i)\n", lpszDest, debugstr_a(lpszSrc), iLen);
2108 if (lpszDest && lpszSrc && iLen > 0)
2110 while ((iLen-- > 1) && *lpszSrc)
2111 *lpszDest++ = *lpszSrc++;
2118 /*************************************************************************
2121 * Unicode version of StrCpyNXA.
2123 LPWSTR WINAPI StrCpyNXW(LPWSTR lpszDest, LPCWSTR lpszSrc, int iLen)
2125 TRACE("(%p,%s,%i)\n", lpszDest, debugstr_w(lpszSrc), iLen);
2127 if (lpszDest && lpszSrc && iLen > 0)
2129 while ((iLen-- > 1) && *lpszSrc)
2130 *lpszDest++ = *lpszSrc++;
2137 /*************************************************************************
2138 * StrCmpLogicalW [SHLWAPI.@]
2140 * Compare two strings, ignoring case and comparing digits as numbers.
2143 * lpszStr [I] First string to compare
2144 * lpszComp [I] Second string to compare
2145 * iLen [I] Length to compare
2148 * TRUE If the strings are equal.
2151 INT WINAPI StrCmpLogicalW(LPCWSTR lpszStr, LPCWSTR lpszComp)
2155 TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszComp));
2157 if (lpszStr && lpszComp)
2163 else if (isdigitW(*lpszStr))
2167 if (!isdigitW(*lpszComp))
2170 /* Compare the numbers */
2171 StrToIntExW(lpszStr, 0, &iStr);
2172 StrToIntExW(lpszComp, 0, &iComp);
2176 else if (iStr > iComp)
2180 while (isdigitW(*lpszStr))
2182 while (isdigitW(*lpszComp))
2185 else if (isdigitW(*lpszComp))
2189 iDiff = SHLWAPI_ChrCmpHelperW(*lpszStr,*lpszComp,NORM_IGNORECASE);
2205 /* Structure for formatting byte strings */
2206 typedef struct tagSHLWAPI_BYTEFORMATS
2211 LPCWSTR lpwszFormat;
2213 } SHLWAPI_BYTEFORMATS;
2215 /*************************************************************************
2216 * StrFormatByteSizeW [SHLWAPI.@]
2218 * Create a string containing an abbreviated byte count of up to 2^63-1.
2221 * llBytes [I] Byte size to format
2222 * lpszDest [I] Destination for formatted string
2223 * cchMax [I] Size of lpszDest
2229 * There is no StrFormatByteSize64W function, it is called StrFormatByteSizeW().
2231 LPWSTR WINAPI StrFormatByteSizeW(LONGLONG llBytes, LPWSTR lpszDest, UINT cchMax)
2233 static const WCHAR wszBytes[] = {'%','l','d',' ','b','y','t','e','s',0};
2234 static const WCHAR wsz3_0[] = {'%','3','.','0','f',0};
2235 static const WCHAR wsz3_1[] = {'%','3','.','1','f',0};
2236 static const WCHAR wsz3_2[] = {'%','3','.','2','f',0};
2238 #define KB ((ULONGLONG)1024)
2240 #define GB (KB*KB*KB)
2241 #define TB (KB*KB*KB*KB)
2242 #define PB (KB*KB*KB*KB*KB)
2244 static const SHLWAPI_BYTEFORMATS bfFormats[] =
2246 { 10*KB, 10.24, 100.0, wsz3_2, 'K' }, /* 10 KB */
2247 { 100*KB, 102.4, 10.0, wsz3_1, 'K' }, /* 100 KB */
2248 { 1000*KB, 1024.0, 1.0, wsz3_0, 'K' }, /* 1000 KB */
2249 { 10*MB, 10485.76, 100.0, wsz3_2, 'M' }, /* 10 MB */
2250 { 100*MB, 104857.6, 10.0, wsz3_1, 'M' }, /* 100 MB */
2251 { 1000*MB, 1048576.0, 1.0, wsz3_0, 'M' }, /* 1000 MB */
2252 { 10*GB, 10737418.24, 100.0, wsz3_2, 'G' }, /* 10 GB */
2253 { 100*GB, 107374182.4, 10.0, wsz3_1, 'G' }, /* 100 GB */
2254 { 1000*GB, 1073741824.0, 1.0, wsz3_0, 'G' }, /* 1000 GB */
2255 { 10*TB, 10485.76, 100.0, wsz3_2, 'T' }, /* 10 TB */
2256 { 100*TB, 104857.6, 10.0, wsz3_1, 'T' }, /* 100 TB */
2257 { 1000*TB, 1048576.0, 1.0, wsz3_0, 'T' }, /* 1000 TB */
2258 { 10*PB, 10737418.24, 100.00, wsz3_2, 'P' }, /* 10 PB */
2259 { 100*PB, 107374182.4, 10.00, wsz3_1, 'P' }, /* 100 PB */
2260 { 1000*PB, 1073741824.0, 1.00, wsz3_0, 'P' }, /* 1000 PB */
2261 { 0, 10995116277.76, 100.00, wsz3_2, 'E' } /* EB's, catch all */
2264 WCHAR wszAdd[] = {' ','?','B',0};
2268 TRACE("(%lld,%p,%d)\n", llBytes, lpszDest, cchMax);
2270 if (!lpszDest || !cchMax)
2273 if (llBytes < 1024) /* 1K */
2275 snprintfW(lpszDest, cchMax, wszBytes, (long)llBytes);
2279 /* Note that if this loop completes without finding a match, i will be
2280 * pointing at the last entry, which is a catch all for > 1000 PB
2282 while (i < sizeof(bfFormats) / sizeof(SHLWAPI_BYTEFORMATS) - 1)
2284 if (llBytes < bfFormats[i].dLimit)
2288 /* Above 1 TB we encounter problems with FP accuracy. So for amounts above
2289 * this number we integer shift down by 1 MB first. The table above has
2290 * the divisors scaled down from the '< 10 TB' entry onwards, to account
2291 * for this. We also add a small fudge factor to get the correct result for
2292 * counts that lie exactly on a 1024 byte boundary.
2295 dBytes = (double)(llBytes >> 20) + 0.001; /* Scale down by I MB */
2297 dBytes = (double)llBytes + 0.00001;
2299 dBytes = floor(dBytes / bfFormats[i].dDivisor) / bfFormats[i].dNormaliser;
2301 sprintfW(wszBuff, bfFormats[i].lpwszFormat, dBytes);
2302 wszAdd[1] = bfFormats[i].wPrefix;
2303 strcatW(wszBuff, wszAdd);
2304 lstrcpynW(lpszDest, wszBuff, cchMax);
2308 /*************************************************************************
2309 * StrFormatByteSize64A [SHLWAPI.@]
2311 * See StrFormatByteSizeW.
2313 LPSTR WINAPI StrFormatByteSize64A(LONGLONG llBytes, LPSTR lpszDest, UINT cchMax)
2317 StrFormatByteSizeW(llBytes, wszBuff, sizeof(wszBuff)/sizeof(WCHAR));
2320 WideCharToMultiByte(CP_ACP, 0, wszBuff, -1, lpszDest, cchMax, 0, 0);
2324 /*************************************************************************
2325 * StrFormatByteSizeA [SHLWAPI.@]
2327 * Create a string containing an abbreviated byte count of up to 2^31-1.
2330 * dwBytes [I] Byte size to format
2331 * lpszDest [I] Destination for formatted string
2332 * cchMax [I] Size of lpszDest
2338 * The Ascii and Unicode versions of this function accept a different
2339 * integer type for dwBytes. See StrFormatByteSize64A().
2341 LPSTR WINAPI StrFormatByteSizeA(DWORD dwBytes, LPSTR lpszDest, UINT cchMax)
2343 TRACE("(%ld,%p,%d)\n", dwBytes, lpszDest, cchMax);
2345 return StrFormatByteSize64A(dwBytes, lpszDest, cchMax);
2348 /*************************************************************************
2351 * Remove a hanging lead byte from the end of a string, if present.
2354 * lpStr [I] String to check for a hanging lead byte
2355 * size [I] Length of lpStr
2358 * Success: The new length of the string. Any hanging lead bytes are removed.
2359 * Failure: 0, if any parameters are invalid.
2361 DWORD WINAPI SHTruncateString(LPSTR lpStr, DWORD size)
2365 LPSTR lastByte = lpStr + size - 1;
2367 while(lpStr < lastByte)
2368 lpStr += IsDBCSLeadByte(*lpStr) ? 2 : 1;
2370 if(lpStr == lastByte && IsDBCSLeadByte(*lpStr))
2380 /*************************************************************************
2383 * Remove a single non-trailing ampersand ('&') from a string.
2386 * lpszStr [I/O] String to remove ampersand from.
2389 * The character after the first ampersand in lpszStr, or the first character
2390 * in lpszStr if there is no ampersand in the string.
2392 char WINAPI SHStripMneumonicA(LPCSTR lpszStr)
2394 LPSTR lpszIter, lpszTmp;
2397 TRACE("(%s)\n", debugstr_a(lpszStr));
2401 if ((lpszIter = StrChrA(lpszStr, '&')))
2403 lpszTmp = CharNextA(lpszIter);
2404 if (lpszTmp && *lpszTmp)
2406 if (*lpszTmp != '&')
2409 while (lpszIter && *lpszIter)
2411 lpszTmp = CharNextA(lpszIter);
2412 *lpszIter = *lpszTmp;
2421 /*************************************************************************
2424 * Unicode version of SHStripMneumonicA.
2426 WCHAR WINAPI SHStripMneumonicW(LPCWSTR lpszStr)
2428 LPWSTR lpszIter, lpszTmp;
2431 TRACE("(%s)\n", debugstr_w(lpszStr));
2435 if ((lpszIter = StrChrW(lpszStr, '&')))
2437 lpszTmp = CharNextW(lpszIter);
2438 if (lpszTmp && *lpszTmp)
2440 if (*lpszTmp != '&')
2443 while (lpszIter && *lpszIter)
2445 lpszTmp = CharNextW(lpszIter);
2446 *lpszIter = *lpszTmp;
2455 /*************************************************************************
2458 * Convert an Ascii string to Unicode.
2461 * dwCp [I] Code page for the conversion
2462 * lpSrcStr [I] Source Ascii string to convert
2463 * lpDstStr [O] Destination for converted Unicode string
2464 * iLen [I] Length of lpDstStr
2467 * The return value of the MultiByteToWideChar() function called on lpSrcStr.
2469 DWORD WINAPI SHAnsiToUnicodeCP(DWORD dwCp, LPCSTR lpSrcStr, LPWSTR lpDstStr, int iLen)
2473 dwRet = MultiByteToWideChar(dwCp, 0, lpSrcStr, -1, lpDstStr, iLen);
2474 TRACE("%s->%s,ret=%ld\n", debugstr_a(lpSrcStr), debugstr_w(lpDstStr), dwRet);
2478 /*************************************************************************
2481 * Convert an Ascii string to Unicode.
2484 * lpSrcStr [I] Source Ascii string to convert
2485 * lpDstStr [O] Destination for converted Unicode string
2486 * iLen [I] Length of lpDstStr
2489 * The return value of the MultiByteToWideChar() function called on lpSrcStr.
2492 * This function simply calls SHAnsiToUnicodeCP with code page CP_ACP.
2494 DWORD WINAPI SHAnsiToUnicode(LPCSTR lpSrcStr, LPWSTR lpDstStr, int iLen)
2496 return SHAnsiToUnicodeCP(CP_ACP, lpSrcStr, lpDstStr, iLen);
2499 /*************************************************************************
2502 * Convert a Unicode string to Ascii.
2505 * CodePage [I] Code page to use for the conversion
2506 * lpSrcStr [I] Source Unicode string to convert
2507 * lpDstStr [O] Destination for converted Ascii string
2508 * lpiLen [I/O] Input length of lpDstStr/destination for length of lpDstStr
2511 * Success: The number of characters that result from the conversion.
2514 INT WINAPI SHUnicodeToAnsiCP(UINT CodePage, LPCWSTR lpSrcStr, LPSTR lpDstStr,
2517 static const WCHAR emptyW[] = { '\0' };
2521 if (!lpDstStr || !lpiLen)
2529 len = strlenW(lpSrcStr) + 1;
2534 CodePage = CP_UTF8; /* Fall through... */
2535 case 0x0000C350: /* FIXME: CP_ #define */
2540 INT nWideCharCount = len - 1;
2542 GET_FUNC(pConvertINetUnicodeToMultiByte, mlang, "ConvertINetUnicodeToMultiByte", 0);
2543 if (!pConvertINetUnicodeToMultiByte(&dwMode, CodePage, lpSrcStr, &nWideCharCount, lpDstStr,
2547 if (nWideCharCount < len - 1)
2549 mem = HeapAlloc(GetProcessHeap(), 0, *lpiLen);
2555 if (pConvertINetUnicodeToMultiByte(&dwMode, CodePage, lpSrcStr, &len, mem, lpiLen))
2557 SHTruncateString(mem, *lpiLen);
2558 lstrcpynA(lpDstStr, mem, *lpiLen + 1);
2559 HeapFree(GetProcessHeap(), 0, mem);
2562 HeapFree(GetProcessHeap(), 0, mem);
2565 lpDstStr[*lpiLen] = '\0';
2572 reqLen = WideCharToMultiByte(CodePage, 0, lpSrcStr, len, lpDstStr,
2573 *lpiLen, NULL, NULL);
2575 if (!reqLen && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
2577 reqLen = WideCharToMultiByte(CodePage, 0, lpSrcStr, len, NULL, 0, NULL, NULL);
2580 mem = HeapAlloc(GetProcessHeap(), 0, reqLen);
2583 reqLen = WideCharToMultiByte(CodePage, 0, lpSrcStr, len, mem,
2584 reqLen, NULL, NULL);
2586 reqLen = SHTruncateString(mem, *lpiLen);
2589 lstrcpynA(lpDstStr, mem, *lpiLen);
2591 HeapFree(GetProcessHeap(), 0, mem);
2598 /*************************************************************************
2601 * Convert a Unicode string to Ascii.
2604 * lpSrcStr [I] Source Unicode string to convert
2605 * lpDstStr [O] Destination for converted Ascii string
2606 * iLen [O] Length of lpDstStr in characters
2609 * See SHUnicodeToAnsiCP
2612 * This function simply calls SHUnicodeToAnsiCP() with CodePage = CP_ACP.
2614 INT WINAPI SHUnicodeToAnsi(LPCWSTR lpSrcStr, LPSTR lpDstStr, INT iLen)
2618 return SHUnicodeToAnsiCP(CP_ACP, lpSrcStr, lpDstStr, &myint);
2621 /*************************************************************************
2624 * Copy one string to another.
2627 * lpszSrc [I] Source string to copy
2628 * lpszDst [O] Destination for copy
2629 * iLen [I] Length of lpszDst in characters
2632 * The length of the copied string, including the terminating NUL. lpszDst
2633 * contains iLen characters of lpszSrc.
2635 DWORD WINAPI SHAnsiToAnsi(LPCSTR lpszSrc, LPSTR lpszDst, int iLen)
2639 TRACE("(%s,%p,0x%08x)\n", debugstr_a(lpszSrc), lpszDst, iLen);
2641 lpszRet = StrCpyNXA(lpszDst, lpszSrc, iLen);
2642 return lpszRet - lpszDst + 1;
2645 /*************************************************************************
2648 * Unicode version of SSHAnsiToAnsi.
2650 DWORD WINAPI SHUnicodeToUnicode(LPCWSTR lpszSrc, LPWSTR lpszDst, int iLen)
2654 TRACE("(%s,%p,0x%08x)\n", debugstr_w(lpszSrc), lpszDst, iLen);
2656 lpszRet = StrCpyNXW(lpszDst, lpszSrc, iLen);
2657 return lpszRet - lpszDst + 1;
2660 /*************************************************************************
2663 * Determine if an Ascii string converts to Unicode and back identically.
2666 * lpSrcStr [I] Source Unicode string to convert
2667 * lpDst [O] Destination for resulting Ascii string
2668 * iLen [I] Length of lpDst in characters
2671 * TRUE, since Ascii strings always convert identically.
2673 BOOL WINAPI DoesStringRoundTripA(LPCSTR lpSrcStr, LPSTR lpDst, INT iLen)
2675 lstrcpynA(lpDst, lpSrcStr, iLen);
2679 /*************************************************************************
2682 * Determine if a Unicode string converts to Ascii and back identically.
2685 * lpSrcStr [I] Source Unicode string to convert
2686 * lpDst [O] Destination for resulting Ascii string
2687 * iLen [I] Length of lpDst in characters
2690 * TRUE, if lpSrcStr converts to Ascii and back identically,
2693 BOOL WINAPI DoesStringRoundTripW(LPCWSTR lpSrcStr, LPSTR lpDst, INT iLen)
2695 WCHAR szBuff[MAX_PATH];
2697 SHUnicodeToAnsi(lpSrcStr, lpDst, iLen);
2698 SHAnsiToUnicode(lpDst, szBuff, MAX_PATH);
2699 return !strcmpW(lpSrcStr, szBuff);
2702 /*************************************************************************
2703 * SHLoadIndirectString [SHLWAPI.@]
2705 * If passed a string that begins with a '@' extract the string from the
2706 * appropriate resource, otherwise do a straight copy.
2709 HRESULT WINAPI SHLoadIndirectString(LPCWSTR src, LPWSTR dst, UINT dst_len, void **reserved)
2711 WCHAR *dllname = NULL;
2712 HMODULE hmod = NULL;
2713 HRESULT hr = E_FAIL;
2715 TRACE("(%s %p %08x %p)\n", debugstr_w(src), dst, dst_len, reserved);
2723 dllname = StrDupW(src + 1);
2724 index_str = strchrW(dllname, ',');
2726 if(!index_str) goto end;
2730 index = atoiW(index_str);
2732 hmod = LoadLibraryW(dllname);
2737 if(LoadStringW(hmod, -index, dst, dst_len))
2741 FIXME("can't handle non-negative indicies (%d)\n", index);
2746 lstrcpynW(dst, src, dst_len);
2750 TRACE("returing %s\n", debugstr_w(dst));
2752 if(hmod) FreeLibrary(hmod);
2753 HeapFree(GetProcessHeap(), 0, dllname);