user32: Convert menu and string table resources to po files.
[wine.git] / dlls / shlwapi / string.c
blob6102f3c928c51acd7ff6218e3da8699d8873958d
1 /*
2 * Shlwapi string functions
4 * Copyright 1998 Juergen Schmied
5 * Copyright 2002 Jon Griffiths
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #include "config.h"
23 #include "wine/port.h"
25 #include <math.h>
26 #include <stdarg.h>
27 #include <stdio.h>
28 #include <string.h>
30 #define NONAMELESSUNION
31 #define NONAMELESSSTRUCT
32 #include "windef.h"
33 #include "winbase.h"
34 #define NO_SHLWAPI_REG
35 #define NO_SHLWAPI_STREAM
36 #include "shlwapi.h"
37 #include "wingdi.h"
38 #include "winuser.h"
39 #include "shlobj.h"
40 #include "mlang.h"
41 #include "ddeml.h"
42 #include "wine/unicode.h"
43 #include "wine/debug.h"
45 #include "resource.h"
47 WINE_DEFAULT_DEBUG_CHANNEL(shell);
49 extern HINSTANCE shlwapi_hInstance;
51 static HRESULT _SHStrDupAA(LPCSTR,LPSTR*);
52 static HRESULT _SHStrDupAW(LPCWSTR,LPSTR*);
55 static void FillNumberFmt(NUMBERFMTW *fmt, LPWSTR decimal_buffer, int decimal_bufwlen,
56 LPWSTR thousand_buffer, int thousand_bufwlen)
58 WCHAR grouping[64];
59 WCHAR *c;
61 GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_ILZERO|LOCALE_RETURN_NUMBER, (LPWSTR)&fmt->LeadingZero, sizeof(fmt->LeadingZero)/sizeof(WCHAR));
62 GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_INEGNUMBER|LOCALE_RETURN_NUMBER, (LPWSTR)&fmt->LeadingZero, sizeof(fmt->NegativeOrder)/sizeof(WCHAR));
63 fmt->NumDigits = 0;
64 GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, decimal_buffer, decimal_bufwlen);
65 GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, thousand_buffer, thousand_bufwlen);
66 fmt->lpThousandSep = thousand_buffer;
67 fmt->lpDecimalSep = decimal_buffer;
69 /*
70 * Converting grouping string to number as described on
71 * http://blogs.msdn.com/oldnewthing/archive/2006/04/18/578251.aspx
73 fmt->Grouping = 0;
74 GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SGROUPING, grouping, sizeof(grouping)/sizeof(WCHAR));
75 for (c = grouping; *c; c++)
76 if (*c >= '0' && *c < '9')
78 fmt->Grouping *= 10;
79 fmt->Grouping += *c - '0';
82 if (fmt->Grouping % 10 == 0)
83 fmt->Grouping /= 10;
84 else
85 fmt->Grouping *= 10;
88 /*************************************************************************
89 * FormatInt [internal]
91 * Format an integer according to the current locale
93 * RETURNS
94 * The number of bytes written on success or 0 on failure
96 static int FormatInt(LONGLONG qdwValue, LPWSTR pszBuf, int cchBuf)
98 NUMBERFMTW fmt;
99 WCHAR decimal[8], thousand[8];
100 WCHAR buf[24];
101 WCHAR *c;
102 BOOL neg = (qdwValue < 0);
104 FillNumberFmt(&fmt, decimal, sizeof decimal / sizeof (WCHAR),
105 thousand, sizeof thousand / sizeof (WCHAR));
107 c = &buf[24];
108 *(--c) = 0;
111 *(--c) = '0' + (qdwValue%10);
112 qdwValue /= 10;
113 } while (qdwValue > 0);
114 if (neg)
115 *(--c) = '-';
117 return GetNumberFormatW(LOCALE_USER_DEFAULT, 0, c, &fmt, pszBuf, cchBuf);
120 /*************************************************************************
121 * FormatDouble [internal]
123 * Format an integer according to the current locale. Prints the specified number of digits
124 * after the decimal point
126 * RETURNS
127 * The number of 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};
132 WCHAR buf[64];
133 NUMBERFMTW fmt;
134 WCHAR decimal[8], thousand[8];
136 snprintfW(buf, 64, flfmt, value);
138 FillNumberFmt(&fmt, decimal, sizeof decimal / sizeof (WCHAR),
139 thousand, sizeof thousand / sizeof (WCHAR));
140 fmt.NumDigits = decimals;
141 return GetNumberFormatW(LOCALE_USER_DEFAULT, 0, buf, &fmt, pszBuf, cchBuf);
144 /*************************************************************************
145 * SHLWAPI_ChrCmpHelperA
147 * Internal helper for SHLWAPI_ChrCmpA/ChrCMPIA.
149 * NOTES
150 * Both this function and its Unicode counterpart are very inefficient. To
151 * fix this, CompareString must be completely implemented and optimised
152 * first. Then the core character test can be taken out of that function and
153 * placed here, so that it need never be called at all. Until then, do not
154 * attempt to optimise this code unless you are willing to test that it
155 * still performs correctly.
157 static BOOL SHLWAPI_ChrCmpHelperA(WORD ch1, WORD ch2, DWORD dwFlags)
159 char str1[3], str2[3];
161 str1[0] = LOBYTE(ch1);
162 if (IsDBCSLeadByte(str1[0]))
164 str1[1] = HIBYTE(ch1);
165 str1[2] = '\0';
167 else
168 str1[1] = '\0';
170 str2[0] = LOBYTE(ch2);
171 if (IsDBCSLeadByte(str2[0]))
173 str2[1] = HIBYTE(ch2);
174 str2[2] = '\0';
176 else
177 str2[1] = '\0';
179 return CompareStringA(GetThreadLocale(), dwFlags, str1, -1, str2, -1) - 2;
182 /*************************************************************************
183 * SHLWAPI_ChrCmpA
185 * Internal helper function.
187 static BOOL WINAPI SHLWAPI_ChrCmpA(WORD ch1, WORD ch2)
189 return SHLWAPI_ChrCmpHelperA(ch1, ch2, 0);
192 /*************************************************************************
193 * ChrCmpIA (SHLWAPI.385)
195 * Compare two characters, ignoring case.
197 * PARAMS
198 * ch1 [I] First character to compare
199 * ch2 [I] Second character to compare
201 * RETURNS
202 * FALSE, if the characters are equal.
203 * Non-zero otherwise.
205 BOOL WINAPI ChrCmpIA(WORD ch1, WORD ch2)
207 TRACE("(%d,%d)\n", ch1, ch2);
209 return SHLWAPI_ChrCmpHelperA(ch1, ch2, NORM_IGNORECASE);
212 /*************************************************************************
213 * ChrCmpIW [SHLWAPI.386]
215 * See ChrCmpIA.
217 BOOL WINAPI ChrCmpIW(WCHAR ch1, WCHAR ch2)
219 return CompareStringW(GetThreadLocale(), NORM_IGNORECASE, &ch1, 1, &ch2, 1) - 2;
222 /*************************************************************************
223 * StrChrA [SHLWAPI.@]
225 * Find a given character in a string.
227 * PARAMS
228 * lpszStr [I] String to search in.
229 * ch [I] Character to search for.
231 * RETURNS
232 * Success: A pointer to the first occurrence of ch in lpszStr, or NULL if
233 * not found.
234 * Failure: NULL, if any arguments are invalid.
236 LPSTR WINAPI StrChrA(LPCSTR lpszStr, WORD ch)
238 TRACE("(%s,%i)\n", debugstr_a(lpszStr), ch);
240 if (lpszStr)
242 while (*lpszStr)
244 if (!SHLWAPI_ChrCmpA(*lpszStr, ch))
245 return (LPSTR)lpszStr;
246 lpszStr = CharNextA(lpszStr);
249 return NULL;
252 /*************************************************************************
253 * StrChrW [SHLWAPI.@]
255 * See StrChrA.
257 LPWSTR WINAPI StrChrW(LPCWSTR lpszStr, WCHAR ch)
259 LPWSTR lpszRet = NULL;
261 TRACE("(%s,%i)\n", debugstr_w(lpszStr), ch);
263 if (lpszStr)
264 lpszRet = strchrW(lpszStr, ch);
265 return lpszRet;
268 /*************************************************************************
269 * StrChrIA [SHLWAPI.@]
271 * Find a given character in a string, ignoring case.
273 * PARAMS
274 * lpszStr [I] String to search in.
275 * ch [I] Character to search for.
277 * RETURNS
278 * Success: A pointer to the first occurrence of ch in lpszStr, or NULL if
279 * not found.
280 * Failure: NULL, if any arguments are invalid.
282 LPSTR WINAPI StrChrIA(LPCSTR lpszStr, WORD ch)
284 TRACE("(%s,%i)\n", debugstr_a(lpszStr), ch);
286 if (lpszStr)
288 while (*lpszStr)
290 if (!ChrCmpIA(*lpszStr, ch))
291 return (LPSTR)lpszStr;
292 lpszStr = CharNextA(lpszStr);
295 return NULL;
298 /*************************************************************************
299 * StrChrIW [SHLWAPI.@]
301 * See StrChrA.
303 LPWSTR WINAPI StrChrIW(LPCWSTR lpszStr, WCHAR ch)
305 TRACE("(%s,%i)\n", debugstr_w(lpszStr), ch);
307 if (lpszStr)
309 ch = toupperW(ch);
310 while (*lpszStr)
312 if (toupperW(*lpszStr) == ch)
313 return (LPWSTR)lpszStr;
314 lpszStr++;
316 lpszStr = NULL;
318 return (LPWSTR)lpszStr;
321 /*************************************************************************
322 * StrChrNW [SHLWAPI.@]
324 LPWSTR WINAPI StrChrNW(LPCWSTR lpszStr, WCHAR ch, UINT cchMax)
326 TRACE("(%s(%i),%i)\n", debugstr_wn(lpszStr,cchMax), cchMax, ch);
328 if (lpszStr)
330 while (*lpszStr && cchMax-- > 0)
332 if (*lpszStr == ch)
333 return (LPWSTR)lpszStr;
334 lpszStr++;
337 return NULL;
340 /*************************************************************************
341 * StrCmpIW [SHLWAPI.@]
343 * Compare two strings, ignoring case.
345 * PARAMS
346 * lpszStr [I] First string to compare
347 * lpszComp [I] Second string to compare
349 * RETURNS
350 * An integer less than, equal to or greater than 0, indicating that
351 * lpszStr is less than, the same, or greater than lpszComp.
353 int WINAPI StrCmpIW(LPCWSTR lpszStr, LPCWSTR lpszComp)
355 int iRet;
357 TRACE("(%s,%s)\n", debugstr_w(lpszStr),debugstr_w(lpszComp));
359 iRet = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, lpszStr, -1, lpszComp, -1);
360 return iRet == CSTR_LESS_THAN ? -1 : iRet == CSTR_GREATER_THAN ? 1 : 0;
363 /*************************************************************************
364 * StrCmpNA [SHLWAPI.@]
366 * Compare two strings, up to a maximum length.
368 * PARAMS
369 * lpszStr [I] First string to compare
370 * lpszComp [I] Second string to compare
371 * iLen [I] Maximum number of chars to compare.
373 * RETURNS
374 * An integer less than, equal to or greater than 0, indicating that
375 * lpszStr is less than, the same, or greater than lpszComp.
377 INT WINAPI StrCmpNA(LPCSTR lpszStr, LPCSTR lpszComp, INT iLen)
379 INT iRet;
381 TRACE("(%s,%s,%i)\n", debugstr_a(lpszStr), debugstr_a(lpszComp), iLen);
383 iRet = CompareStringA(GetThreadLocale(), 0, lpszStr, iLen, lpszComp, iLen);
384 return iRet == CSTR_LESS_THAN ? -1 : iRet == CSTR_GREATER_THAN ? 1 : 0;
387 /*************************************************************************
388 * StrCmpNW [SHLWAPI.@]
390 * See StrCmpNA.
392 INT WINAPI StrCmpNW(LPCWSTR lpszStr, LPCWSTR lpszComp, INT iLen)
394 INT iRet;
396 TRACE("(%s,%s,%i)\n", debugstr_w(lpszStr), debugstr_w(lpszComp), iLen);
398 iRet = CompareStringW(GetThreadLocale(), 0, lpszStr, iLen, lpszComp, iLen);
399 return iRet == CSTR_LESS_THAN ? -1 : iRet == CSTR_GREATER_THAN ? 1 : 0;
402 /*************************************************************************
403 * StrCmpNIA [SHLWAPI.@]
405 * Compare two strings, up to a maximum length, ignoring case.
407 * PARAMS
408 * lpszStr [I] First string to compare
409 * lpszComp [I] Second string to compare
410 * iLen [I] Maximum number of chars to compare.
412 * RETURNS
413 * An integer less than, equal to or greater than 0, indicating that
414 * lpszStr is less than, the same, or greater than lpszComp.
416 int WINAPI StrCmpNIA(LPCSTR lpszStr, LPCSTR lpszComp, int iLen)
418 INT iRet;
420 TRACE("(%s,%s,%i)\n", debugstr_a(lpszStr), debugstr_a(lpszComp), iLen);
422 iRet = CompareStringA(GetThreadLocale(), NORM_IGNORECASE, lpszStr, iLen, lpszComp, iLen);
423 return iRet == CSTR_LESS_THAN ? -1 : iRet == CSTR_GREATER_THAN ? 1 : 0;
426 /*************************************************************************
427 * StrCmpNIW [SHLWAPI.@]
429 * See StrCmpNIA.
431 INT WINAPI StrCmpNIW(LPCWSTR lpszStr, LPCWSTR lpszComp, int iLen)
433 INT iRet;
435 TRACE("(%s,%s,%i)\n", debugstr_w(lpszStr), debugstr_w(lpszComp), iLen);
437 iRet = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, lpszStr, iLen, lpszComp, iLen);
438 return iRet == CSTR_LESS_THAN ? -1 : iRet == CSTR_GREATER_THAN ? 1 : 0;
441 /*************************************************************************
442 * StrCmpW [SHLWAPI.@]
444 * Compare two strings.
446 * PARAMS
447 * lpszStr [I] First string to compare
448 * lpszComp [I] Second string to compare
450 * RETURNS
451 * An integer less than, equal to or greater than 0, indicating that
452 * lpszStr is less than, the same, or greater than lpszComp.
454 int WINAPI StrCmpW(LPCWSTR lpszStr, LPCWSTR lpszComp)
456 INT iRet;
458 TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszComp));
460 iRet = CompareStringW(GetThreadLocale(), 0, lpszStr, -1, lpszComp, -1);
461 return iRet == CSTR_LESS_THAN ? -1 : iRet == CSTR_GREATER_THAN ? 1 : 0;
464 /*************************************************************************
465 * StrCatW [SHLWAPI.@]
467 * Concatenate two strings.
469 * PARAMS
470 * lpszStr [O] Initial string
471 * lpszSrc [I] String to concatenate
473 * RETURNS
474 * lpszStr.
476 LPWSTR WINAPI StrCatW(LPWSTR lpszStr, LPCWSTR lpszSrc)
478 TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszSrc));
480 strcatW(lpszStr, lpszSrc);
481 return lpszStr;
484 /*************************************************************************
485 * StrCpyW [SHLWAPI.@]
487 * Copy a string to another string.
489 * PARAMS
490 * lpszStr [O] Destination string
491 * lpszSrc [I] Source string
493 * RETURNS
494 * lpszStr.
496 LPWSTR WINAPI StrCpyW(LPWSTR lpszStr, LPCWSTR lpszSrc)
498 TRACE("(%p,%s)\n", lpszStr, debugstr_w(lpszSrc));
500 strcpyW(lpszStr, lpszSrc);
501 return lpszStr;
504 /*************************************************************************
505 * StrCpyNW [SHLWAPI.@]
507 * Copy a string to another string, up to a maximum number of characters.
509 * PARAMS
510 * dst [O] Destination string
511 * src [I] Source string
512 * count [I] Maximum number of chars to copy
514 * RETURNS
515 * dst.
517 LPWSTR WINAPI StrCpyNW(LPWSTR dst, LPCWSTR src, int count)
519 LPWSTR d = dst;
520 LPCWSTR s = src;
522 TRACE("(%p,%s,%i)\n", dst, debugstr_w(src), count);
524 if (s)
526 while ((count > 1) && *s)
528 count--;
529 *d++ = *s++;
532 if (count) *d = 0;
534 return dst;
537 /*************************************************************************
538 * SHLWAPI_StrStrHelperA
540 * Internal implementation of StrStrA/StrStrIA
542 static LPSTR SHLWAPI_StrStrHelperA(LPCSTR lpszStr, LPCSTR lpszSearch,
543 INT (WINAPI *pStrCmpFn)(LPCSTR,LPCSTR,INT))
545 size_t iLen;
547 if (!lpszStr || !lpszSearch || !*lpszSearch)
548 return NULL;
550 iLen = strlen(lpszSearch);
552 while (*lpszStr)
554 if (!pStrCmpFn(lpszStr, lpszSearch, iLen))
555 return (LPSTR)lpszStr;
556 lpszStr = CharNextA(lpszStr);
558 return NULL;
561 /*************************************************************************
562 * StrStrA [SHLWAPI.@]
564 * Find a substring within a string.
566 * PARAMS
567 * lpszStr [I] String to search in
568 * lpszSearch [I] String to look for
570 * RETURNS
571 * The start of lpszSearch within lpszStr, or NULL if not found.
573 LPSTR WINAPI StrStrA(LPCSTR lpszStr, LPCSTR lpszSearch)
575 TRACE("(%s,%s)\n", debugstr_a(lpszStr), debugstr_a(lpszSearch));
577 return SHLWAPI_StrStrHelperA(lpszStr, lpszSearch, StrCmpNA);
580 /*************************************************************************
581 * StrStrW [SHLWAPI.@]
583 * See StrStrA.
585 LPWSTR WINAPI StrStrW(LPCWSTR lpszStr, LPCWSTR lpszSearch)
587 TRACE("(%s, %s)\n", debugstr_w(lpszStr), debugstr_w(lpszSearch));
589 if (!lpszStr || !lpszSearch || !*lpszSearch) return NULL;
590 return strstrW( lpszStr, lpszSearch );
593 /*************************************************************************
594 * StrRStrIA [SHLWAPI.@]
596 * Find the last occurrence of a substring within a string.
598 * PARAMS
599 * lpszStr [I] String to search in
600 * lpszEnd [I] End of lpszStr
601 * lpszSearch [I] String to look for
603 * RETURNS
604 * The last occurrence lpszSearch within lpszStr, or NULL if not found.
606 LPSTR WINAPI StrRStrIA(LPCSTR lpszStr, LPCSTR lpszEnd, LPCSTR lpszSearch)
608 WORD ch1, ch2;
609 INT iLen;
611 TRACE("(%s,%s)\n", debugstr_a(lpszStr), debugstr_a(lpszSearch));
613 if (!lpszStr || !lpszSearch || !*lpszSearch)
614 return NULL;
616 if (!lpszEnd)
617 lpszEnd = lpszStr + lstrlenA(lpszStr);
618 if (lpszEnd == lpszStr)
619 return NULL;
621 if (IsDBCSLeadByte(*lpszSearch))
622 ch1 = *lpszSearch << 8 | (UCHAR)lpszSearch[1];
623 else
624 ch1 = *lpszSearch;
625 iLen = lstrlenA(lpszSearch);
629 lpszEnd = CharPrevA(lpszStr, lpszEnd);
630 ch2 = IsDBCSLeadByte(*lpszEnd)? *lpszEnd << 8 | (UCHAR)lpszEnd[1] : *lpszEnd;
631 if (!ChrCmpIA(ch1, ch2))
633 if (!StrCmpNIA(lpszEnd, lpszSearch, iLen))
634 return (LPSTR)lpszEnd;
636 } while (lpszEnd > lpszStr);
637 return NULL;
640 /*************************************************************************
641 * StrRStrIW [SHLWAPI.@]
643 * See StrRStrIA.
645 LPWSTR WINAPI StrRStrIW(LPCWSTR lpszStr, LPCWSTR lpszEnd, LPCWSTR lpszSearch)
647 INT iLen;
649 TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszSearch));
651 if (!lpszStr || !lpszSearch || !*lpszSearch)
652 return NULL;
654 if (!lpszEnd)
655 lpszEnd = lpszStr + strlenW(lpszStr);
657 iLen = strlenW(lpszSearch);
659 while (lpszEnd > lpszStr)
661 lpszEnd--;
662 if (!StrCmpNIW(lpszEnd, lpszSearch, iLen))
663 return (LPWSTR)lpszEnd;
665 return NULL;
668 /*************************************************************************
669 * StrStrIA [SHLWAPI.@]
671 * Find a substring within a string, ignoring case.
673 * PARAMS
674 * lpszStr [I] String to search in
675 * lpszSearch [I] String to look for
677 * RETURNS
678 * The start of lpszSearch within lpszStr, or NULL if not found.
680 LPSTR WINAPI StrStrIA(LPCSTR lpszStr, LPCSTR lpszSearch)
682 TRACE("(%s,%s)\n", debugstr_a(lpszStr), debugstr_a(lpszSearch));
684 return SHLWAPI_StrStrHelperA(lpszStr, lpszSearch, StrCmpNIA);
687 /*************************************************************************
688 * StrStrIW [SHLWAPI.@]
690 * See StrStrIA.
692 LPWSTR WINAPI StrStrIW(LPCWSTR lpszStr, LPCWSTR lpszSearch)
694 int iLen;
696 TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszSearch));
698 if (!lpszStr || !lpszSearch || !*lpszSearch)
699 return NULL;
701 iLen = strlenW(lpszSearch);
703 while (*lpszStr)
705 if (!StrCmpNIW(lpszStr, lpszSearch, iLen))
706 return (LPWSTR)lpszStr;
707 lpszStr++;
709 return NULL;
712 /*************************************************************************
713 * StrStrNW [SHLWAPI.@]
715 * Find a substring within a string up to a given number of initial characters.
717 * PARAMS
718 * lpFirst [I] String to search in
719 * lpSrch [I] String to look for
720 * cchMax [I] Maximum number of initial search characters
722 * RETURNS
723 * The start of lpFirst within lpSrch, or NULL if not found.
725 LPWSTR WINAPI StrStrNW(LPCWSTR lpFirst, LPCWSTR lpSrch, UINT cchMax)
727 UINT i;
728 int len;
730 TRACE("(%s, %s, %u)\n", debugstr_w(lpFirst), debugstr_w(lpSrch), cchMax);
732 if (!lpFirst || !lpSrch || !*lpSrch || !cchMax)
733 return NULL;
735 len = strlenW(lpSrch);
737 for (i = cchMax; *lpFirst && (i > 0); i--, lpFirst++)
739 if (!strncmpW(lpFirst, lpSrch, len))
740 return (LPWSTR)lpFirst;
743 return NULL;
746 /*************************************************************************
747 * StrStrNIW [SHLWAPI.@]
749 * Find a substring within a string up to a given number of initial characters,
750 * ignoring case.
752 * PARAMS
753 * lpFirst [I] String to search in
754 * lpSrch [I] String to look for
755 * cchMax [I] Maximum number of initial search characters
757 * RETURNS
758 * The start of lpFirst within lpSrch, or NULL if not found.
760 LPWSTR WINAPI StrStrNIW(LPCWSTR lpFirst, LPCWSTR lpSrch, UINT cchMax)
762 UINT i;
763 int len;
765 TRACE("(%s, %s, %u)\n", debugstr_w(lpFirst), debugstr_w(lpSrch), cchMax);
767 if (!lpFirst || !lpSrch || !*lpSrch || !cchMax)
768 return NULL;
770 len = strlenW(lpSrch);
772 for (i = cchMax; *lpFirst && (i > 0); i--, lpFirst++)
774 if (!strncmpiW(lpFirst, lpSrch, len))
775 return (LPWSTR)lpFirst;
778 return NULL;
781 /*************************************************************************
782 * StrToIntA [SHLWAPI.@]
784 * Read a signed integer from a string.
786 * PARAMS
787 * lpszStr [I] String to read integer from
789 * RETURNS
790 * The signed integer value represented by the string, or 0 if no integer is
791 * present.
793 * NOTES
794 * No leading space is allowed before the number, although a leading '-' is.
796 int WINAPI StrToIntA(LPCSTR lpszStr)
798 int iRet = 0;
800 TRACE("(%s)\n", debugstr_a(lpszStr));
802 if (!lpszStr)
804 WARN("Invalid lpszStr would crash under Win32!\n");
805 return 0;
808 if (*lpszStr == '-' || isdigit(*lpszStr))
809 StrToIntExA(lpszStr, 0, &iRet);
810 return iRet;
813 /*************************************************************************
814 * StrToIntW [SHLWAPI.@]
816 * See StrToIntA.
818 int WINAPI StrToIntW(LPCWSTR lpszStr)
820 int iRet = 0;
822 TRACE("(%s)\n", debugstr_w(lpszStr));
824 if (!lpszStr)
826 WARN("Invalid lpszStr would crash under Win32!\n");
827 return 0;
830 if (*lpszStr == '-' || isdigitW(*lpszStr))
831 StrToIntExW(lpszStr, 0, &iRet);
832 return iRet;
835 /*************************************************************************
836 * StrToIntExA [SHLWAPI.@]
838 * Read an integer from a string.
840 * PARAMS
841 * lpszStr [I] String to read integer from
842 * dwFlags [I] Flags controlling the conversion
843 * lpiRet [O] Destination for read integer.
845 * RETURNS
846 * Success: TRUE. lpiRet contains the integer value represented by the string.
847 * Failure: FALSE, if the string is invalid, or no number is present.
849 * NOTES
850 * Leading whitespace, '-' and '+' are allowed before the number. If
851 * dwFlags includes STIF_SUPPORT_HEX, hexadecimal numbers are allowed, if
852 * preceded by '0x'. If this flag is not set, or there is no '0x' prefix,
853 * the string is treated as a decimal string. A leading '-' is ignored for
854 * hexadecimal numbers.
856 BOOL WINAPI StrToIntExA(LPCSTR lpszStr, DWORD dwFlags, LPINT lpiRet)
858 BOOL bNegative = FALSE;
859 int iRet = 0;
861 TRACE("(%s,%08X,%p)\n", debugstr_a(lpszStr), dwFlags, lpiRet);
863 if (!lpszStr || !lpiRet)
865 WARN("Invalid parameter would crash under Win32!\n");
866 return FALSE;
868 if (dwFlags > STIF_SUPPORT_HEX)
870 WARN("Unknown flags (%08lX)!\n", dwFlags & ~STIF_SUPPORT_HEX);
873 /* Skip leading space, '+', '-' */
874 while (isspace(*lpszStr))
875 lpszStr = CharNextA(lpszStr);
877 if (*lpszStr == '-')
879 bNegative = TRUE;
880 lpszStr++;
882 else if (*lpszStr == '+')
883 lpszStr++;
885 if (dwFlags & STIF_SUPPORT_HEX &&
886 *lpszStr == '0' && tolower(lpszStr[1]) == 'x')
888 /* Read hex number */
889 lpszStr += 2;
891 if (!isxdigit(*lpszStr))
892 return FALSE;
894 while (isxdigit(*lpszStr))
896 iRet = iRet * 16;
897 if (isdigit(*lpszStr))
898 iRet += (*lpszStr - '0');
899 else
900 iRet += 10 + (tolower(*lpszStr) - 'a');
901 lpszStr++;
903 *lpiRet = iRet;
904 return TRUE;
907 /* Read decimal number */
908 if (!isdigit(*lpszStr))
909 return FALSE;
911 while (isdigit(*lpszStr))
913 iRet = iRet * 10;
914 iRet += (*lpszStr - '0');
915 lpszStr++;
917 *lpiRet = bNegative ? -iRet : iRet;
918 return TRUE;
921 /*************************************************************************
922 * StrToIntExW [SHLWAPI.@]
924 * See StrToIntExA.
926 BOOL WINAPI StrToIntExW(LPCWSTR lpszStr, DWORD dwFlags, LPINT lpiRet)
928 BOOL bNegative = FALSE;
929 int iRet = 0;
931 TRACE("(%s,%08X,%p)\n", debugstr_w(lpszStr), dwFlags, lpiRet);
933 if (!lpszStr || !lpiRet)
935 WARN("Invalid parameter would crash under Win32!\n");
936 return FALSE;
938 if (dwFlags > STIF_SUPPORT_HEX)
940 WARN("Unknown flags (%08lX)!\n", dwFlags & ~STIF_SUPPORT_HEX);
943 /* Skip leading space, '+', '-' */
944 while (isspaceW(*lpszStr)) lpszStr++;
946 if (*lpszStr == '-')
948 bNegative = TRUE;
949 lpszStr++;
951 else if (*lpszStr == '+')
952 lpszStr++;
954 if (dwFlags & STIF_SUPPORT_HEX &&
955 *lpszStr == '0' && tolowerW(lpszStr[1]) == 'x')
957 /* Read hex number */
958 lpszStr += 2;
960 if (!isxdigitW(*lpszStr))
961 return FALSE;
963 while (isxdigitW(*lpszStr))
965 iRet = iRet * 16;
966 if (isdigitW(*lpszStr))
967 iRet += (*lpszStr - '0');
968 else
969 iRet += 10 + (tolowerW(*lpszStr) - 'a');
970 lpszStr++;
972 *lpiRet = iRet;
973 return TRUE;
976 /* Read decimal number */
977 if (!isdigitW(*lpszStr))
978 return FALSE;
980 while (isdigitW(*lpszStr))
982 iRet = iRet * 10;
983 iRet += (*lpszStr - '0');
984 lpszStr++;
986 *lpiRet = bNegative ? -iRet : iRet;
987 return TRUE;
990 /*************************************************************************
991 * StrDupA [SHLWAPI.@]
993 * Duplicate a string.
995 * PARAMS
996 * lpszStr [I] String to duplicate.
998 * RETURNS
999 * Success: A pointer to a new string containing the contents of lpszStr
1000 * Failure: NULL, if memory cannot be allocated
1002 * NOTES
1003 * The string memory is allocated with LocalAlloc(), and so should be released
1004 * by calling LocalFree().
1006 LPSTR WINAPI StrDupA(LPCSTR lpszStr)
1008 int iLen;
1009 LPSTR lpszRet;
1011 TRACE("(%s)\n",debugstr_a(lpszStr));
1013 iLen = lpszStr ? strlen(lpszStr) + 1 : 1;
1014 lpszRet = LocalAlloc(LMEM_FIXED, iLen);
1016 if (lpszRet)
1018 if (lpszStr)
1019 memcpy(lpszRet, lpszStr, iLen);
1020 else
1021 *lpszRet = '\0';
1023 return lpszRet;
1026 /*************************************************************************
1027 * StrDupW [SHLWAPI.@]
1029 * See StrDupA.
1031 LPWSTR WINAPI StrDupW(LPCWSTR lpszStr)
1033 int iLen;
1034 LPWSTR lpszRet;
1036 TRACE("(%s)\n",debugstr_w(lpszStr));
1038 iLen = (lpszStr ? strlenW(lpszStr) + 1 : 1) * sizeof(WCHAR);
1039 lpszRet = LocalAlloc(LMEM_FIXED, iLen);
1041 if (lpszRet)
1043 if (lpszStr)
1044 memcpy(lpszRet, lpszStr, iLen);
1045 else
1046 *lpszRet = '\0';
1048 return lpszRet;
1051 /*************************************************************************
1052 * SHLWAPI_StrSpnHelperA
1054 * Internal implementation of StrSpnA/StrCSpnA/StrCSpnIA
1056 static int SHLWAPI_StrSpnHelperA(LPCSTR lpszStr, LPCSTR lpszMatch,
1057 LPSTR (WINAPI *pStrChrFn)(LPCSTR,WORD),
1058 BOOL bInvert)
1060 LPCSTR lpszRead = lpszStr;
1061 if (lpszStr && *lpszStr && lpszMatch)
1063 while (*lpszRead)
1065 LPCSTR lpszTest = pStrChrFn(lpszMatch, *lpszRead);
1067 if (!bInvert && !lpszTest)
1068 break;
1069 if (bInvert && lpszTest)
1070 break;
1071 lpszRead = CharNextA(lpszRead);
1074 return lpszRead - lpszStr;
1077 /*************************************************************************
1078 * StrSpnA [SHLWAPI.@]
1080 * Find the length of the start of a string that contains only certain
1081 * characters.
1083 * PARAMS
1084 * lpszStr [I] String to search
1085 * lpszMatch [I] Characters that can be in the substring
1087 * RETURNS
1088 * The length of the part of lpszStr containing only chars from lpszMatch,
1089 * or 0 if any parameter is invalid.
1091 int WINAPI StrSpnA(LPCSTR lpszStr, LPCSTR lpszMatch)
1093 TRACE("(%s,%s)\n",debugstr_a(lpszStr), debugstr_a(lpszMatch));
1095 return SHLWAPI_StrSpnHelperA(lpszStr, lpszMatch, StrChrA, FALSE);
1098 /*************************************************************************
1099 * StrSpnW [SHLWAPI.@]
1101 * See StrSpnA.
1103 int WINAPI StrSpnW(LPCWSTR lpszStr, LPCWSTR lpszMatch)
1105 if (!lpszStr || !lpszMatch) return 0;
1106 return strspnW( lpszStr, lpszMatch );
1109 /*************************************************************************
1110 * StrCSpnA [SHLWAPI.@]
1112 * Find the length of the start of a string that does not contain certain
1113 * characters.
1115 * PARAMS
1116 * lpszStr [I] String to search
1117 * lpszMatch [I] Characters that cannot be in the substring
1119 * RETURNS
1120 * The length of the part of lpszStr containing only chars not in lpszMatch,
1121 * or 0 if any parameter is invalid.
1123 int WINAPI StrCSpnA(LPCSTR lpszStr, LPCSTR lpszMatch)
1125 TRACE("(%s,%s)\n",debugstr_a(lpszStr), debugstr_a(lpszMatch));
1127 return SHLWAPI_StrSpnHelperA(lpszStr, lpszMatch, StrChrA, TRUE);
1130 /*************************************************************************
1131 * StrCSpnW [SHLWAPI.@]
1133 * See StrCSpnA.
1135 int WINAPI StrCSpnW(LPCWSTR lpszStr, LPCWSTR lpszMatch)
1137 if (!lpszStr || !lpszMatch) return 0;
1138 return strcspnW( lpszStr, lpszMatch );
1141 /*************************************************************************
1142 * StrCSpnIA [SHLWAPI.@]
1144 * Find the length of the start of a string that does not contain certain
1145 * characters, ignoring case.
1147 * PARAMS
1148 * lpszStr [I] String to search
1149 * lpszMatch [I] Characters that cannot be in the substring
1151 * RETURNS
1152 * The length of the part of lpszStr containing only chars not in lpszMatch,
1153 * or 0 if any parameter is invalid.
1155 int WINAPI StrCSpnIA(LPCSTR lpszStr, LPCSTR lpszMatch)
1157 TRACE("(%s,%s)\n",debugstr_a(lpszStr), debugstr_a(lpszMatch));
1159 return SHLWAPI_StrSpnHelperA(lpszStr, lpszMatch, StrChrIA, TRUE);
1162 /*************************************************************************
1163 * StrCSpnIW [SHLWAPI.@]
1165 * See StrCSpnIA.
1167 int WINAPI StrCSpnIW(LPCWSTR lpszStr, LPCWSTR lpszMatch)
1169 LPCWSTR lpszRead = lpszStr;
1171 TRACE("(%s,%s)\n",debugstr_w(lpszStr), debugstr_w(lpszMatch));
1173 if (lpszStr && *lpszStr && lpszMatch)
1175 while (*lpszRead)
1177 if (StrChrIW(lpszMatch, *lpszRead)) break;
1178 lpszRead++;
1181 return lpszRead - lpszStr;
1184 /*************************************************************************
1185 * StrPBrkA [SHLWAPI.@]
1187 * Search a string for any of a group of characters.
1189 * PARAMS
1190 * lpszStr [I] String to search
1191 * lpszMatch [I] Characters to match
1193 * RETURNS
1194 * A pointer to the first matching character in lpszStr, or NULL if no
1195 * match was found.
1197 LPSTR WINAPI StrPBrkA(LPCSTR lpszStr, LPCSTR lpszMatch)
1199 TRACE("(%s,%s)\n",debugstr_a(lpszStr), debugstr_a(lpszMatch));
1201 if (lpszStr && lpszMatch && *lpszMatch)
1203 while (*lpszStr)
1205 if (StrChrA(lpszMatch, *lpszStr))
1206 return (LPSTR)lpszStr;
1207 lpszStr = CharNextA(lpszStr);
1210 return NULL;
1213 /*************************************************************************
1214 * StrPBrkW [SHLWAPI.@]
1216 * See StrPBrkA.
1218 LPWSTR WINAPI StrPBrkW(LPCWSTR lpszStr, LPCWSTR lpszMatch)
1220 if (!lpszStr || !lpszMatch) return NULL;
1221 return strpbrkW( lpszStr, lpszMatch );
1224 /*************************************************************************
1225 * SHLWAPI_StrRChrHelperA
1227 * Internal implementation of StrRChrA/StrRChrIA.
1229 static LPSTR SHLWAPI_StrRChrHelperA(LPCSTR lpszStr,
1230 LPCSTR lpszEnd, WORD ch,
1231 BOOL (WINAPI *pChrCmpFn)(WORD,WORD))
1233 LPCSTR lpszRet = NULL;
1235 if (lpszStr)
1237 WORD ch2;
1239 if (!lpszEnd)
1240 lpszEnd = lpszStr + lstrlenA(lpszStr);
1242 while (*lpszStr && lpszStr <= lpszEnd)
1244 ch2 = IsDBCSLeadByte(*lpszStr)? *lpszStr << 8 | lpszStr[1] : *lpszStr;
1246 if (!pChrCmpFn(ch, ch2))
1247 lpszRet = lpszStr;
1248 lpszStr = CharNextA(lpszStr);
1251 return (LPSTR)lpszRet;
1254 /**************************************************************************
1255 * StrRChrA [SHLWAPI.@]
1257 * Find the last occurrence of a character in string.
1259 * PARAMS
1260 * lpszStr [I] String to search in
1261 * lpszEnd [I] Place to end search, or NULL to search until the end of lpszStr
1262 * ch [I] Character to search for.
1264 * RETURNS
1265 * Success: A pointer to the last occurrence of ch in lpszStr before lpszEnd,
1266 * or NULL if not found.
1267 * Failure: NULL, if any arguments are invalid.
1269 LPSTR WINAPI StrRChrA(LPCSTR lpszStr, LPCSTR lpszEnd, WORD ch)
1271 TRACE("(%s,%s,%x)\n", debugstr_a(lpszStr), debugstr_a(lpszEnd), ch);
1273 return SHLWAPI_StrRChrHelperA(lpszStr, lpszEnd, ch, SHLWAPI_ChrCmpA);
1276 /**************************************************************************
1277 * StrRChrW [SHLWAPI.@]
1279 * See StrRChrA.
1281 LPWSTR WINAPI StrRChrW(LPCWSTR str, LPCWSTR end, WORD ch)
1283 WCHAR *ret = NULL;
1285 if (!str) return NULL;
1286 if (!end) end = str + strlenW(str);
1287 while (str < end)
1289 if (*str == ch) ret = (WCHAR *)str;
1290 str++;
1292 return ret;
1295 /**************************************************************************
1296 * StrRChrIA [SHLWAPI.@]
1298 * Find the last occurrence of a character in string, ignoring case.
1300 * PARAMS
1301 * lpszStr [I] String to search in
1302 * lpszEnd [I] Place to end search, or NULL to search until the end of lpszStr
1303 * ch [I] Character to search for.
1305 * RETURNS
1306 * Success: A pointer to the last occurrence of ch in lpszStr before lpszEnd,
1307 * or NULL if not found.
1308 * Failure: NULL, if any arguments are invalid.
1310 LPSTR WINAPI StrRChrIA(LPCSTR lpszStr, LPCSTR lpszEnd, WORD ch)
1312 TRACE("(%s,%s,%x)\n", debugstr_a(lpszStr), debugstr_a(lpszEnd), ch);
1314 return SHLWAPI_StrRChrHelperA(lpszStr, lpszEnd, ch, ChrCmpIA);
1317 /**************************************************************************
1318 * StrRChrIW [SHLWAPI.@]
1320 * See StrRChrIA.
1322 LPWSTR WINAPI StrRChrIW(LPCWSTR str, LPCWSTR end, WORD ch)
1324 WCHAR *ret = NULL;
1326 if (!str) return NULL;
1327 if (!end) end = str + strlenW(str);
1328 while (str < end)
1330 if (!ChrCmpIW(*str, ch)) ret = (WCHAR *)str;
1331 str++;
1333 return ret;
1336 /*************************************************************************
1337 * StrCatBuffA [SHLWAPI.@]
1339 * Concatenate two strings together.
1341 * PARAMS
1342 * lpszStr [O] String to concatenate to
1343 * lpszCat [I] String to add to lpszCat
1344 * cchMax [I] Maximum number of characters for the whole string
1346 * RETURNS
1347 * lpszStr.
1349 * NOTES
1350 * cchMax determines the number of characters in the final length of the
1351 * string, not the number appended to lpszStr from lpszCat.
1353 LPSTR WINAPI StrCatBuffA(LPSTR lpszStr, LPCSTR lpszCat, INT cchMax)
1355 INT iLen;
1357 TRACE("(%p,%s,%d)\n", lpszStr, debugstr_a(lpszCat), cchMax);
1359 if (!lpszStr)
1361 WARN("Invalid lpszStr would crash under Win32!\n");
1362 return NULL;
1365 iLen = strlen(lpszStr);
1366 cchMax -= iLen;
1368 if (cchMax > 0)
1369 StrCpyNA(lpszStr + iLen, lpszCat, cchMax);
1370 return lpszStr;
1373 /*************************************************************************
1374 * StrCatBuffW [SHLWAPI.@]
1376 * See StrCatBuffA.
1378 LPWSTR WINAPI StrCatBuffW(LPWSTR lpszStr, LPCWSTR lpszCat, INT cchMax)
1380 INT iLen;
1382 TRACE("(%p,%s,%d)\n", lpszStr, debugstr_w(lpszCat), cchMax);
1384 if (!lpszStr)
1386 WARN("Invalid lpszStr would crash under Win32!\n");
1387 return NULL;
1390 iLen = strlenW(lpszStr);
1391 cchMax -= iLen;
1393 if (cchMax > 0)
1394 StrCpyNW(lpszStr + iLen, lpszCat, cchMax);
1395 return lpszStr;
1398 /*************************************************************************
1399 * StrRetToBufA [SHLWAPI.@]
1401 * Convert a STRRET to a normal string.
1403 * PARAMS
1404 * lpStrRet [O] STRRET to convert
1405 * pIdl [I] ITEMIDLIST for lpStrRet->uType == STRRET_OFFSET
1406 * lpszDest [O] Destination for normal string
1407 * dwLen [I] Length of lpszDest
1409 * RETURNS
1410 * Success: S_OK. lpszDest contains up to dwLen characters of the string.
1411 * If lpStrRet is of type STRRET_WSTR, its memory is freed with
1412 * CoTaskMemFree() and its type set to STRRET_CSTRA.
1413 * Failure: E_FAIL, if any parameters are invalid.
1415 HRESULT WINAPI StrRetToBufA (LPSTRRET src, const ITEMIDLIST *pidl, LPSTR dest, UINT len)
1417 /* NOTE:
1418 * This routine is identical to that in dlls/shell32/shellstring.c.
1419 * It was duplicated because not every version of Shlwapi.dll exports
1420 * StrRetToBufA. If you change one routine, change them both.
1422 TRACE("dest=%p len=0x%x strret=%p pidl=%p stub\n",dest,len,src,pidl);
1424 if (!src)
1426 WARN("Invalid lpStrRet would crash under Win32!\n");
1427 if (dest)
1428 *dest = '\0';
1429 return E_FAIL;
1432 if (!dest || !len)
1433 return E_FAIL;
1435 *dest = '\0';
1437 switch (src->uType)
1439 case STRRET_WSTR:
1440 WideCharToMultiByte(CP_ACP, 0, src->u.pOleStr, -1, dest, len, NULL, NULL);
1441 CoTaskMemFree(src->u.pOleStr);
1442 break;
1444 case STRRET_CSTR:
1445 lstrcpynA(dest, src->u.cStr, len);
1446 break;
1448 case STRRET_OFFSET:
1449 lstrcpynA((LPSTR)dest, ((LPCSTR)&pidl->mkid)+src->u.uOffset, len);
1450 break;
1452 default:
1453 FIXME("unknown type!\n");
1454 return FALSE;
1456 return S_OK;
1459 /*************************************************************************
1460 * StrRetToBufW [SHLWAPI.@]
1462 * See StrRetToBufA.
1464 HRESULT WINAPI StrRetToBufW (LPSTRRET src, const ITEMIDLIST *pidl, LPWSTR dest, UINT len)
1466 TRACE("dest=%p len=0x%x strret=%p pidl=%p stub\n",dest,len,src,pidl);
1468 if (!src)
1470 WARN("Invalid lpStrRet would crash under Win32!\n");
1471 if (dest)
1472 *dest = '\0';
1473 return E_FAIL;
1476 if (!dest || !len)
1477 return E_FAIL;
1479 *dest = '\0';
1481 switch (src->uType)
1483 case STRRET_WSTR:
1484 lstrcpynW(dest, src->u.pOleStr, len);
1485 CoTaskMemFree(src->u.pOleStr);
1486 break;
1488 case STRRET_CSTR:
1489 if (!MultiByteToWideChar( CP_ACP, 0, src->u.cStr, -1, dest, len ))
1490 dest[len-1] = 0;
1491 break;
1493 case STRRET_OFFSET:
1494 if (pidl)
1496 if (!MultiByteToWideChar( CP_ACP, 0, ((LPCSTR)&pidl->mkid)+src->u.uOffset, -1,
1497 dest, len ))
1498 dest[len-1] = 0;
1500 break;
1502 default:
1503 FIXME("unknown type!\n");
1504 return FALSE;
1506 return S_OK;
1509 /*************************************************************************
1510 * StrRetToStrA [SHLWAPI.@]
1512 * Converts a STRRET to a normal string.
1514 * PARAMS
1515 * lpStrRet [O] STRRET to convert
1516 * pidl [I] ITEMIDLIST for lpStrRet->uType == STRRET_OFFSET
1517 * ppszName [O] Destination for converted string
1519 * RETURNS
1520 * Success: S_OK. ppszName contains the new string, allocated with CoTaskMemAlloc().
1521 * Failure: E_FAIL, if any parameters are invalid.
1523 HRESULT WINAPI StrRetToStrA(LPSTRRET lpStrRet, const ITEMIDLIST *pidl, LPSTR *ppszName)
1525 HRESULT hRet = E_FAIL;
1527 switch (lpStrRet->uType)
1529 case STRRET_WSTR:
1530 hRet = _SHStrDupAW(lpStrRet->u.pOleStr, ppszName);
1531 CoTaskMemFree(lpStrRet->u.pOleStr);
1532 break;
1534 case STRRET_CSTR:
1535 hRet = _SHStrDupAA(lpStrRet->u.cStr, ppszName);
1536 break;
1538 case STRRET_OFFSET:
1539 hRet = _SHStrDupAA(((LPCSTR)&pidl->mkid) + lpStrRet->u.uOffset, ppszName);
1540 break;
1542 default:
1543 *ppszName = NULL;
1546 return hRet;
1549 /*************************************************************************
1550 * StrRetToStrW [SHLWAPI.@]
1552 * See StrRetToStrA.
1554 HRESULT WINAPI StrRetToStrW(LPSTRRET lpStrRet, const ITEMIDLIST *pidl, LPWSTR *ppszName)
1556 HRESULT hRet = E_FAIL;
1558 switch (lpStrRet->uType)
1560 case STRRET_WSTR:
1561 hRet = SHStrDupW(lpStrRet->u.pOleStr, ppszName);
1562 CoTaskMemFree(lpStrRet->u.pOleStr);
1563 break;
1565 case STRRET_CSTR:
1566 hRet = SHStrDupA(lpStrRet->u.cStr, ppszName);
1567 break;
1569 case STRRET_OFFSET:
1570 hRet = SHStrDupA(((LPCSTR)&pidl->mkid) + lpStrRet->u.uOffset, ppszName);
1571 break;
1573 default:
1574 *ppszName = NULL;
1577 return hRet;
1580 /* Create an ASCII string copy using SysAllocString() */
1581 static HRESULT _SHStrDupAToBSTR(LPCSTR src, BSTR *pBstrOut)
1583 *pBstrOut = NULL;
1585 if (src)
1587 INT len = MultiByteToWideChar(CP_ACP, 0, src, -1, NULL, 0);
1588 WCHAR* szTemp = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1590 if (szTemp)
1592 MultiByteToWideChar(CP_ACP, 0, src, -1, szTemp, len);
1593 *pBstrOut = SysAllocString(szTemp);
1594 HeapFree(GetProcessHeap(), 0, szTemp);
1596 if (*pBstrOut)
1597 return S_OK;
1600 return E_OUTOFMEMORY;
1603 /*************************************************************************
1604 * StrRetToBSTR [SHLWAPI.@]
1606 * Converts a STRRET to a BSTR.
1608 * PARAMS
1609 * lpStrRet [O] STRRET to convert
1610 * pidl [I] ITEMIDLIST for lpStrRet->uType = STRRET_OFFSET
1611 * pBstrOut [O] Destination for converted BSTR
1613 * RETURNS
1614 * Success: S_OK. pBstrOut contains the new string.
1615 * Failure: E_FAIL, if any parameters are invalid.
1617 HRESULT WINAPI StrRetToBSTR(STRRET *lpStrRet, LPCITEMIDLIST pidl, BSTR* pBstrOut)
1619 HRESULT hRet = E_FAIL;
1621 switch (lpStrRet->uType)
1623 case STRRET_WSTR:
1624 *pBstrOut = SysAllocString(lpStrRet->u.pOleStr);
1625 if (*pBstrOut)
1626 hRet = S_OK;
1627 CoTaskMemFree(lpStrRet->u.pOleStr);
1628 break;
1630 case STRRET_CSTR:
1631 hRet = _SHStrDupAToBSTR(lpStrRet->u.cStr, pBstrOut);
1632 break;
1634 case STRRET_OFFSET:
1635 hRet = _SHStrDupAToBSTR(((LPCSTR)&pidl->mkid) + lpStrRet->u.uOffset, pBstrOut);
1636 break;
1638 default:
1639 *pBstrOut = NULL;
1642 return hRet;
1645 /*************************************************************************
1646 * StrFormatKBSizeA [SHLWAPI.@]
1648 * Create a formatted string containing a byte count in Kilobytes.
1650 * PARAMS
1651 * llBytes [I] Byte size to format
1652 * lpszDest [I] Destination for formatted string
1653 * cchMax [I] Size of lpszDest
1655 * RETURNS
1656 * lpszDest.
1658 LPSTR WINAPI StrFormatKBSizeA(LONGLONG llBytes, LPSTR lpszDest, UINT cchMax)
1660 WCHAR wszBuf[256];
1662 if (!StrFormatKBSizeW(llBytes, wszBuf, 256))
1663 return NULL;
1664 if (!WideCharToMultiByte(CP_ACP, 0, wszBuf, -1, lpszDest, cchMax, NULL, NULL))
1665 return NULL;
1666 return lpszDest;
1669 /*************************************************************************
1670 * StrFormatKBSizeW [SHLWAPI.@]
1672 * See StrFormatKBSizeA.
1674 LPWSTR WINAPI StrFormatKBSizeW(LONGLONG llBytes, LPWSTR lpszDest, UINT cchMax)
1676 static const WCHAR kb[] = {' ','K','B',0};
1677 LONGLONG llKB = (llBytes + 1023) >> 10;
1678 int len;
1680 TRACE("(0x%s,%p,%d)\n", wine_dbgstr_longlong(llBytes), lpszDest, cchMax);
1682 if (!FormatInt(llKB, lpszDest, cchMax))
1683 return NULL;
1685 len = lstrlenW(lpszDest);
1686 if (cchMax - len < 4)
1687 return NULL;
1688 lstrcatW(lpszDest, kb);
1689 return lpszDest;
1692 /*************************************************************************
1693 * StrNCatA [SHLWAPI.@]
1695 * Concatenate two strings together.
1697 * PARAMS
1698 * lpszStr [O] String to concatenate to
1699 * lpszCat [I] String to add to lpszCat
1700 * cchMax [I] Maximum number of characters to concatenate
1702 * RETURNS
1703 * lpszStr.
1705 * NOTES
1706 * cchMax determines the number of characters that are appended to lpszStr,
1707 * not the total length of the string.
1709 LPSTR WINAPI StrNCatA(LPSTR lpszStr, LPCSTR lpszCat, INT cchMax)
1711 LPSTR lpszRet = lpszStr;
1713 TRACE("(%s,%s,%i)\n", debugstr_a(lpszStr), debugstr_a(lpszCat), cchMax);
1715 if (!lpszStr)
1717 WARN("Invalid lpszStr would crash under Win32!\n");
1718 return NULL;
1721 StrCpyNA(lpszStr + strlen(lpszStr), lpszCat, cchMax);
1722 return lpszRet;
1725 /*************************************************************************
1726 * StrNCatW [SHLWAPI.@]
1728 * See StrNCatA.
1730 LPWSTR WINAPI StrNCatW(LPWSTR lpszStr, LPCWSTR lpszCat, INT cchMax)
1732 LPWSTR lpszRet = lpszStr;
1734 TRACE("(%s,%s,%i)\n", debugstr_w(lpszStr), debugstr_w(lpszCat), cchMax);
1736 if (!lpszStr)
1738 WARN("Invalid lpszStr would crash under Win32\n");
1739 return NULL;
1742 StrCpyNW(lpszStr + strlenW(lpszStr), lpszCat, cchMax);
1743 return lpszRet;
1746 /*************************************************************************
1747 * StrTrimA [SHLWAPI.@]
1749 * Remove characters from the start and end of a string.
1751 * PARAMS
1752 * lpszStr [O] String to remove characters from
1753 * lpszTrim [I] Characters to remove from lpszStr
1755 * RETURNS
1756 * TRUE If lpszStr was valid and modified
1757 * FALSE Otherwise
1759 BOOL WINAPI StrTrimA(LPSTR lpszStr, LPCSTR lpszTrim)
1761 DWORD dwLen;
1762 LPSTR lpszRead = lpszStr;
1763 BOOL bRet = FALSE;
1765 TRACE("(%s,%s)\n", debugstr_a(lpszStr), debugstr_a(lpszTrim));
1767 if (lpszRead && *lpszRead)
1769 while (*lpszRead && StrChrA(lpszTrim, *lpszRead))
1770 lpszRead = CharNextA(lpszRead); /* Skip leading matches */
1772 dwLen = strlen(lpszRead);
1774 if (lpszRead != lpszStr)
1776 memmove(lpszStr, lpszRead, dwLen + 1);
1777 bRet = TRUE;
1779 if (dwLen > 0)
1781 lpszRead = lpszStr + dwLen;
1782 while (StrChrA(lpszTrim, lpszRead[-1]))
1783 lpszRead = CharPrevA(lpszStr, lpszRead); /* Skip trailing matches */
1785 if (lpszRead != lpszStr + dwLen)
1787 *lpszRead = '\0';
1788 bRet = TRUE;
1792 return bRet;
1795 /*************************************************************************
1796 * StrTrimW [SHLWAPI.@]
1798 * See StrTrimA.
1800 BOOL WINAPI StrTrimW(LPWSTR lpszStr, LPCWSTR lpszTrim)
1802 DWORD dwLen;
1803 LPWSTR lpszRead = lpszStr;
1804 BOOL bRet = FALSE;
1806 TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszTrim));
1808 if (lpszRead && *lpszRead)
1810 while (*lpszRead && StrChrW(lpszTrim, *lpszRead)) lpszRead++;
1812 dwLen = strlenW(lpszRead);
1814 if (lpszRead != lpszStr)
1816 memmove(lpszStr, lpszRead, (dwLen + 1) * sizeof(WCHAR));
1817 bRet = TRUE;
1819 if (dwLen > 0)
1821 lpszRead = lpszStr + dwLen;
1822 while (StrChrW(lpszTrim, lpszRead[-1]))
1823 lpszRead--; /* Skip trailing matches */
1825 if (lpszRead != lpszStr + dwLen)
1827 *lpszRead = '\0';
1828 bRet = TRUE;
1832 return bRet;
1835 /*************************************************************************
1836 * _SHStrDupAA [INTERNAL]
1838 * Duplicates a ASCII string to ASCII. The destination buffer is allocated.
1840 static HRESULT _SHStrDupAA(LPCSTR src, LPSTR * dest)
1842 HRESULT hr;
1843 int len = 0;
1845 if (src) {
1846 len = lstrlenA(src) + 1;
1847 *dest = CoTaskMemAlloc(len);
1848 } else {
1849 *dest = NULL;
1852 if (*dest) {
1853 lstrcpynA(*dest,src, len);
1854 hr = S_OK;
1855 } else {
1856 hr = E_OUTOFMEMORY;
1859 TRACE("%s->(%p)\n", debugstr_a(src), *dest);
1860 return hr;
1863 /*************************************************************************
1864 * SHStrDupA [SHLWAPI.@]
1866 * Return a Unicode copy of a string, in memory allocated by CoTaskMemAlloc().
1868 * PARAMS
1869 * lpszStr [I] String to copy
1870 * lppszDest [O] Destination for the new string copy
1872 * RETURNS
1873 * Success: S_OK. lppszDest contains the new string in Unicode format.
1874 * Failure: E_OUTOFMEMORY, If any arguments are invalid or memory allocation
1875 * fails.
1877 HRESULT WINAPI SHStrDupA(LPCSTR lpszStr, LPWSTR * lppszDest)
1879 HRESULT hRet;
1880 int len = 0;
1882 if (lpszStr)
1884 len = MultiByteToWideChar(0, 0, lpszStr, -1, 0, 0) * sizeof(WCHAR);
1885 *lppszDest = CoTaskMemAlloc(len);
1887 else
1888 *lppszDest = NULL;
1890 if (*lppszDest)
1892 MultiByteToWideChar(0, 0, lpszStr, -1, *lppszDest, len/sizeof(WCHAR));
1893 hRet = S_OK;
1895 else
1896 hRet = E_OUTOFMEMORY;
1898 TRACE("%s->(%p)\n", debugstr_a(lpszStr), *lppszDest);
1899 return hRet;
1902 /*************************************************************************
1903 * _SHStrDupAW [INTERNAL]
1905 * Duplicates a UNICODE to a ASCII string. The destination buffer is allocated.
1907 static HRESULT _SHStrDupAW(LPCWSTR src, LPSTR * dest)
1909 HRESULT hr;
1910 int len = 0;
1912 if (src) {
1913 len = WideCharToMultiByte(CP_ACP, 0, src, -1, NULL, 0, NULL, NULL);
1914 *dest = CoTaskMemAlloc(len);
1915 } else {
1916 *dest = NULL;
1919 if (*dest) {
1920 WideCharToMultiByte(CP_ACP, 0, src, -1, *dest, len, NULL, NULL);
1921 hr = S_OK;
1922 } else {
1923 hr = E_OUTOFMEMORY;
1926 TRACE("%s->(%p)\n", debugstr_w(src), *dest);
1927 return hr;
1930 /*************************************************************************
1931 * SHStrDupW [SHLWAPI.@]
1933 * See SHStrDupA.
1935 HRESULT WINAPI SHStrDupW(LPCWSTR src, LPWSTR * dest)
1937 HRESULT hr;
1938 int len = 0;
1940 if (src) {
1941 len = (lstrlenW(src) + 1) * sizeof(WCHAR);
1942 *dest = CoTaskMemAlloc(len);
1943 } else {
1944 *dest = NULL;
1947 if (*dest) {
1948 memcpy(*dest, src, len);
1949 hr = S_OK;
1950 } else {
1951 hr = E_OUTOFMEMORY;
1954 TRACE("%s->(%p)\n", debugstr_w(src), *dest);
1955 return hr;
1958 /*************************************************************************
1959 * SHLWAPI_WriteReverseNum
1961 * Internal helper for SHLWAPI_WriteTimeClass.
1963 static inline LPWSTR SHLWAPI_WriteReverseNum(LPWSTR lpszOut, DWORD dwNum)
1965 *lpszOut-- = '\0';
1967 /* Write a decimal number to a string, backwards */
1970 DWORD dwNextDigit = dwNum % 10;
1971 *lpszOut-- = '0' + dwNextDigit;
1972 dwNum = (dwNum - dwNextDigit) / 10;
1973 } while (dwNum > 0);
1975 return lpszOut;
1978 /*************************************************************************
1979 * SHLWAPI_FormatSignificant
1981 * Internal helper for SHLWAPI_WriteTimeClass.
1983 static inline int SHLWAPI_FormatSignificant(LPWSTR lpszNum, int dwDigits)
1985 /* Zero non significant digits, return remaining significant digits */
1986 while (*lpszNum)
1988 lpszNum++;
1989 if (--dwDigits == 0)
1991 while (*lpszNum)
1992 *lpszNum++ = '0';
1993 return 0;
1996 return dwDigits;
1999 /*************************************************************************
2000 * SHLWAPI_WriteTimeClass
2002 * Internal helper for StrFromTimeIntervalW.
2004 static int SHLWAPI_WriteTimeClass(LPWSTR lpszOut, DWORD dwValue,
2005 UINT uClassStringId, int iDigits)
2007 WCHAR szBuff[64], *szOut = szBuff + 32;
2009 szOut = SHLWAPI_WriteReverseNum(szOut, dwValue);
2010 iDigits = SHLWAPI_FormatSignificant(szOut + 1, iDigits);
2011 *szOut = ' ';
2012 LoadStringW(shlwapi_hInstance, uClassStringId, szBuff + 32, 32);
2013 strcatW(lpszOut, szOut);
2014 return iDigits;
2017 /*************************************************************************
2018 * StrFromTimeIntervalA [SHLWAPI.@]
2020 * Format a millisecond time interval into a string
2022 * PARAMS
2023 * lpszStr [O] Output buffer for formatted time interval
2024 * cchMax [I] Size of lpszStr
2025 * dwMS [I] Number of milliseconds
2026 * iDigits [I] Number of digits to print
2028 * RETURNS
2029 * The length of the formatted string, or 0 if any parameter is invalid.
2031 * NOTES
2032 * This implementation mimics the Win32 behaviour of always writing a leading
2033 * space before the time interval begins.
2035 * iDigits is used to provide approximate times if accuracy is not important.
2036 * This number of digits will be written of the first non-zero time class
2037 * (hours/minutes/seconds). If this does not complete the time classification,
2038 * the remaining digits are changed to zeros (i.e. The time is _not_ rounded).
2039 * If there are digits remaining following the writing of a time class, the
2040 * next time class will be written.
2042 * For example, given dwMS represents 138 hours,43 minutes and 15 seconds, the
2043 * following will result from the given values of iDigits:
2045 *| iDigits 1 2 3 4 5 ...
2046 *| lpszStr "100 hr" "130 hr" "138 hr" "138 hr 40 min" "138 hr 43 min" ...
2048 INT WINAPI StrFromTimeIntervalA(LPSTR lpszStr, UINT cchMax, DWORD dwMS,
2049 int iDigits)
2051 INT iRet = 0;
2053 TRACE("(%p,%d,%d,%d)\n", lpszStr, cchMax, dwMS, iDigits);
2055 if (lpszStr && cchMax)
2057 WCHAR szBuff[128];
2058 StrFromTimeIntervalW(szBuff, sizeof(szBuff)/sizeof(WCHAR), dwMS, iDigits);
2059 WideCharToMultiByte(CP_ACP,0,szBuff,-1,lpszStr,cchMax,0,0);
2061 return iRet;
2065 /*************************************************************************
2066 * StrFromTimeIntervalW [SHLWAPI.@]
2068 * See StrFromTimeIntervalA.
2070 INT WINAPI StrFromTimeIntervalW(LPWSTR lpszStr, UINT cchMax, DWORD dwMS,
2071 int iDigits)
2073 INT iRet = 0;
2075 TRACE("(%p,%d,%d,%d)\n", lpszStr, cchMax, dwMS, iDigits);
2077 if (lpszStr && cchMax)
2079 WCHAR szCopy[128];
2080 DWORD dwHours, dwMinutes;
2082 if (!iDigits || cchMax == 1)
2084 *lpszStr = '\0';
2085 return 0;
2088 /* Calculate the time classes */
2089 dwMS = (dwMS + 500) / 1000;
2090 dwHours = dwMS / 3600;
2091 dwMS -= dwHours * 3600;
2092 dwMinutes = dwMS / 60;
2093 dwMS -= dwMinutes * 60;
2095 szCopy[0] = '\0';
2097 if (dwHours)
2098 iDigits = SHLWAPI_WriteTimeClass(szCopy, dwHours, IDS_TIME_INTERVAL_HOURS, iDigits);
2100 if (dwMinutes && iDigits)
2101 iDigits = SHLWAPI_WriteTimeClass(szCopy, dwMinutes, IDS_TIME_INTERVAL_MINUTES, iDigits);
2103 if (iDigits) /* Always write seconds if we have significant digits */
2104 SHLWAPI_WriteTimeClass(szCopy, dwMS, IDS_TIME_INTERVAL_SECONDS, iDigits);
2106 lstrcpynW(lpszStr, szCopy, cchMax);
2107 iRet = strlenW(lpszStr);
2109 return iRet;
2112 /*************************************************************************
2113 * StrIsIntlEqualA [SHLWAPI.@]
2115 * Compare two strings.
2117 * PARAMS
2118 * bCase [I] Whether to compare case sensitively
2119 * lpszStr [I] First string to compare
2120 * lpszComp [I] Second string to compare
2121 * iLen [I] Length to compare
2123 * RETURNS
2124 * TRUE If the strings are equal.
2125 * FALSE Otherwise.
2127 BOOL WINAPI StrIsIntlEqualA(BOOL bCase, LPCSTR lpszStr, LPCSTR lpszComp,
2128 int iLen)
2130 DWORD dwFlags;
2132 TRACE("(%d,%s,%s,%d)\n", bCase,
2133 debugstr_a(lpszStr), debugstr_a(lpszComp), iLen);
2135 /* FIXME: This flag is undocumented and unknown by our CompareString.
2136 * We need a define for it.
2138 dwFlags = 0x10000000;
2139 if (!bCase) dwFlags |= NORM_IGNORECASE;
2141 return (CompareStringA(GetThreadLocale(), dwFlags, lpszStr, iLen, lpszComp, iLen) == CSTR_EQUAL);
2144 /*************************************************************************
2145 * StrIsIntlEqualW [SHLWAPI.@]
2147 * See StrIsIntlEqualA.
2149 BOOL WINAPI StrIsIntlEqualW(BOOL bCase, LPCWSTR lpszStr, LPCWSTR lpszComp,
2150 int iLen)
2152 DWORD dwFlags;
2154 TRACE("(%d,%s,%s,%d)\n", bCase,
2155 debugstr_w(lpszStr),debugstr_w(lpszComp), iLen);
2157 /* FIXME: This flag is undocumented and unknown by our CompareString.
2158 * We need a define for it.
2160 dwFlags = 0x10000000;
2161 if (!bCase) dwFlags |= NORM_IGNORECASE;
2163 return (CompareStringW(GetThreadLocale(), dwFlags, lpszStr, iLen, lpszComp, iLen) == CSTR_EQUAL);
2166 /*************************************************************************
2167 * @ [SHLWAPI.399]
2169 * Copy a string to another string, up to a maximum number of characters.
2171 * PARAMS
2172 * lpszDest [O] Destination string
2173 * lpszSrc [I] Source string
2174 * iLen [I] Maximum number of chars to copy
2176 * RETURNS
2177 * Success: A pointer to the last character written to lpszDest.
2178 * Failure: lpszDest, if any arguments are invalid.
2180 LPSTR WINAPI StrCpyNXA(LPSTR lpszDest, LPCSTR lpszSrc, int iLen)
2182 TRACE("(%p,%s,%i)\n", lpszDest, debugstr_a(lpszSrc), iLen);
2184 if (lpszDest && lpszSrc && iLen > 0)
2186 while ((iLen-- > 1) && *lpszSrc)
2187 *lpszDest++ = *lpszSrc++;
2188 if (iLen >= 0)
2189 *lpszDest = '\0';
2191 return lpszDest;
2194 /*************************************************************************
2195 * @ [SHLWAPI.400]
2197 * Unicode version of StrCpyNXA.
2199 LPWSTR WINAPI StrCpyNXW(LPWSTR lpszDest, LPCWSTR lpszSrc, int iLen)
2201 TRACE("(%p,%s,%i)\n", lpszDest, debugstr_w(lpszSrc), iLen);
2203 if (lpszDest && lpszSrc && iLen > 0)
2205 while ((iLen-- > 1) && *lpszSrc)
2206 *lpszDest++ = *lpszSrc++;
2207 if (iLen >= 0)
2208 *lpszDest = '\0';
2210 return lpszDest;
2213 /*************************************************************************
2214 * StrCmpLogicalW [SHLWAPI.@]
2216 * Compare two strings, ignoring case and comparing digits as numbers.
2218 * PARAMS
2219 * lpszStr [I] First string to compare
2220 * lpszComp [I] Second string to compare
2221 * iLen [I] Length to compare
2223 * RETURNS
2224 * TRUE If the strings are equal.
2225 * FALSE Otherwise.
2227 INT WINAPI StrCmpLogicalW(LPCWSTR lpszStr, LPCWSTR lpszComp)
2229 INT iDiff;
2231 TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszComp));
2233 if (lpszStr && lpszComp)
2235 while (*lpszStr)
2237 if (!*lpszComp)
2238 return 1;
2239 else if (isdigitW(*lpszStr))
2241 int iStr, iComp;
2243 if (!isdigitW(*lpszComp))
2244 return -1;
2246 /* Compare the numbers */
2247 StrToIntExW(lpszStr, 0, &iStr);
2248 StrToIntExW(lpszComp, 0, &iComp);
2250 if (iStr < iComp)
2251 return -1;
2252 else if (iStr > iComp)
2253 return 1;
2255 /* Skip */
2256 while (isdigitW(*lpszStr))
2257 lpszStr++;
2258 while (isdigitW(*lpszComp))
2259 lpszComp++;
2261 else if (isdigitW(*lpszComp))
2262 return 1;
2263 else
2265 iDiff = ChrCmpIW(*lpszStr,*lpszComp);
2266 if (iDiff > 0)
2267 return 1;
2268 else if (iDiff < 0)
2269 return -1;
2271 lpszStr++;
2272 lpszComp++;
2275 if (*lpszComp)
2276 return -1;
2278 return 0;
2281 /* Structure for formatting byte strings */
2282 typedef struct tagSHLWAPI_BYTEFORMATS
2284 LONGLONG dLimit;
2285 double dDivisor;
2286 double dNormaliser;
2287 int nDecimals;
2288 WCHAR wPrefix;
2289 } SHLWAPI_BYTEFORMATS;
2291 /*************************************************************************
2292 * StrFormatByteSizeW [SHLWAPI.@]
2294 * Create a string containing an abbreviated byte count of up to 2^63-1.
2296 * PARAMS
2297 * llBytes [I] Byte size to format
2298 * lpszDest [I] Destination for formatted string
2299 * cchMax [I] Size of lpszDest
2301 * RETURNS
2302 * lpszDest.
2304 * NOTES
2305 * There is no StrFormatByteSize64W function, it is called StrFormatByteSizeW().
2307 LPWSTR WINAPI StrFormatByteSizeW(LONGLONG llBytes, LPWSTR lpszDest, UINT cchMax)
2309 #define KB ((ULONGLONG)1024)
2310 #define MB (KB*KB)
2311 #define GB (KB*KB*KB)
2312 #define TB (KB*KB*KB*KB)
2313 #define PB (KB*KB*KB*KB*KB)
2315 static const SHLWAPI_BYTEFORMATS bfFormats[] =
2317 { 10*KB, 10.24, 100.0, 2, 'K' }, /* 10 KB */
2318 { 100*KB, 102.4, 10.0, 1, 'K' }, /* 100 KB */
2319 { 1000*KB, 1024.0, 1.0, 0, 'K' }, /* 1000 KB */
2320 { 10*MB, 10485.76, 100.0, 2, 'M' }, /* 10 MB */
2321 { 100*MB, 104857.6, 10.0, 1, 'M' }, /* 100 MB */
2322 { 1000*MB, 1048576.0, 1.0, 0, 'M' }, /* 1000 MB */
2323 { 10*GB, 10737418.24, 100.0, 2, 'G' }, /* 10 GB */
2324 { 100*GB, 107374182.4, 10.0, 1, 'G' }, /* 100 GB */
2325 { 1000*GB, 1073741824.0, 1.0, 0, 'G' }, /* 1000 GB */
2326 { 10*TB, 10485.76, 100.0, 2, 'T' }, /* 10 TB */
2327 { 100*TB, 104857.6, 10.0, 1, 'T' }, /* 100 TB */
2328 { 1000*TB, 1048576.0, 1.0, 0, 'T' }, /* 1000 TB */
2329 { 10*PB, 10737418.24, 100.00, 2, 'P' }, /* 10 PB */
2330 { 100*PB, 107374182.4, 10.00, 1, 'P' }, /* 100 PB */
2331 { 1000*PB, 1073741824.0, 1.00, 0, 'P' }, /* 1000 PB */
2332 { 0, 10995116277.76, 100.00, 2, 'E' } /* EB's, catch all */
2334 WCHAR wszAdd[] = {' ','?','B',0};
2335 double dBytes;
2336 UINT i = 0;
2338 TRACE("(0x%s,%p,%d)\n", wine_dbgstr_longlong(llBytes), lpszDest, cchMax);
2340 if (!lpszDest || !cchMax)
2341 return lpszDest;
2343 if (llBytes < 1024) /* 1K */
2345 WCHAR wszBytesFormat[64];
2346 LoadStringW(shlwapi_hInstance, IDS_BYTES_FORMAT, wszBytesFormat, 64);
2347 snprintfW(lpszDest, cchMax, wszBytesFormat, (long)llBytes);
2348 return lpszDest;
2351 /* Note that if this loop completes without finding a match, i will be
2352 * pointing at the last entry, which is a catch all for > 1000 PB
2354 while (i < sizeof(bfFormats) / sizeof(SHLWAPI_BYTEFORMATS) - 1)
2356 if (llBytes < bfFormats[i].dLimit)
2357 break;
2358 i++;
2360 /* Above 1 TB we encounter problems with FP accuracy. So for amounts above
2361 * this number we integer shift down by 1 MB first. The table above has
2362 * the divisors scaled down from the '< 10 TB' entry onwards, to account
2363 * for this. We also add a small fudge factor to get the correct result for
2364 * counts that lie exactly on a 1024 byte boundary.
2366 if (i > 8)
2367 dBytes = (double)(llBytes >> 20) + 0.001; /* Scale down by I MB */
2368 else
2369 dBytes = (double)llBytes + 0.00001;
2371 dBytes = floor(dBytes / bfFormats[i].dDivisor) / bfFormats[i].dNormaliser;
2373 if (!FormatDouble(dBytes, bfFormats[i].nDecimals, lpszDest, cchMax))
2374 return NULL;
2375 wszAdd[1] = bfFormats[i].wPrefix;
2376 StrCatBuffW(lpszDest, wszAdd, cchMax);
2377 return lpszDest;
2380 /*************************************************************************
2381 * StrFormatByteSize64A [SHLWAPI.@]
2383 * See StrFormatByteSizeW.
2385 LPSTR WINAPI StrFormatByteSize64A(LONGLONG llBytes, LPSTR lpszDest, UINT cchMax)
2387 WCHAR wszBuff[32];
2389 StrFormatByteSizeW(llBytes, wszBuff, sizeof(wszBuff)/sizeof(WCHAR));
2391 if (lpszDest)
2392 WideCharToMultiByte(CP_ACP, 0, wszBuff, -1, lpszDest, cchMax, 0, 0);
2393 return lpszDest;
2396 /*************************************************************************
2397 * StrFormatByteSizeA [SHLWAPI.@]
2399 * Create a string containing an abbreviated byte count of up to 2^31-1.
2401 * PARAMS
2402 * dwBytes [I] Byte size to format
2403 * lpszDest [I] Destination for formatted string
2404 * cchMax [I] Size of lpszDest
2406 * RETURNS
2407 * lpszDest.
2409 * NOTES
2410 * The Ascii and Unicode versions of this function accept a different
2411 * integer type for dwBytes. See StrFormatByteSize64A().
2413 LPSTR WINAPI StrFormatByteSizeA(DWORD dwBytes, LPSTR lpszDest, UINT cchMax)
2415 TRACE("(%d,%p,%d)\n", dwBytes, lpszDest, cchMax);
2417 return StrFormatByteSize64A(dwBytes, lpszDest, cchMax);
2420 /*************************************************************************
2421 * @ [SHLWAPI.162]
2423 * Remove a hanging lead byte from the end of a string, if present.
2425 * PARAMS
2426 * lpStr [I] String to check for a hanging lead byte
2427 * size [I] Length of lpStr
2429 * RETURNS
2430 * Success: The new length of the string. Any hanging lead bytes are removed.
2431 * Failure: 0, if any parameters are invalid.
2433 DWORD WINAPI SHTruncateString(LPSTR lpStr, DWORD size)
2435 if (lpStr && size)
2437 LPSTR lastByte = lpStr + size - 1;
2439 while(lpStr < lastByte)
2440 lpStr += IsDBCSLeadByte(*lpStr) ? 2 : 1;
2442 if(lpStr == lastByte && IsDBCSLeadByte(*lpStr))
2444 *lpStr = '\0';
2445 size--;
2447 return size;
2449 return 0;
2452 /*************************************************************************
2453 * @ [SHLWAPI.203]
2455 * Remove a single non-trailing ampersand ('&') from a string.
2457 * PARAMS
2458 * lpszStr [I/O] String to remove ampersand from.
2460 * RETURNS
2461 * The character after the first ampersand in lpszStr, or the first character
2462 * in lpszStr if there is no ampersand in the string.
2464 char WINAPI SHStripMneumonicA(LPCSTR lpszStr)
2466 LPSTR lpszIter, lpszTmp;
2467 char ch;
2469 TRACE("(%s)\n", debugstr_a(lpszStr));
2471 ch = *lpszStr;
2473 if ((lpszIter = StrChrA(lpszStr, '&')))
2475 lpszTmp = CharNextA(lpszIter);
2476 if (lpszTmp && *lpszTmp)
2478 if (*lpszTmp != '&')
2479 ch = *lpszTmp;
2481 while (lpszIter && *lpszIter)
2483 lpszTmp = CharNextA(lpszIter);
2484 *lpszIter = *lpszTmp;
2485 lpszIter = lpszTmp;
2490 return ch;
2493 /*************************************************************************
2494 * @ [SHLWAPI.225]
2496 * Unicode version of SHStripMneumonicA.
2498 WCHAR WINAPI SHStripMneumonicW(LPCWSTR lpszStr)
2500 LPWSTR lpszIter, lpszTmp;
2501 WCHAR ch;
2503 TRACE("(%s)\n", debugstr_w(lpszStr));
2505 ch = *lpszStr;
2507 if ((lpszIter = StrChrW(lpszStr, '&')))
2509 lpszTmp = lpszIter + 1;
2510 if (lpszTmp && *lpszTmp)
2512 if (*lpszTmp != '&')
2513 ch = *lpszTmp;
2515 while (lpszIter && *lpszIter)
2517 lpszTmp = lpszIter + 1;
2518 *lpszIter = *lpszTmp;
2519 lpszIter = lpszTmp;
2524 return ch;
2527 /*************************************************************************
2528 * @ [SHLWAPI.216]
2530 * Convert an Ascii string to Unicode.
2532 * PARAMS
2533 * dwCp [I] Code page for the conversion
2534 * lpSrcStr [I] Source Ascii string to convert
2535 * lpDstStr [O] Destination for converted Unicode string
2536 * iLen [I] Length of lpDstStr
2538 * RETURNS
2539 * The return value of the MultiByteToWideChar() function called on lpSrcStr.
2541 DWORD WINAPI SHAnsiToUnicodeCP(DWORD dwCp, LPCSTR lpSrcStr, LPWSTR lpDstStr, int iLen)
2543 DWORD dwRet;
2545 dwRet = MultiByteToWideChar(dwCp, 0, lpSrcStr, -1, lpDstStr, iLen);
2546 TRACE("%s->%s,ret=%d\n", debugstr_a(lpSrcStr), debugstr_w(lpDstStr), dwRet);
2547 return dwRet;
2550 /*************************************************************************
2551 * @ [SHLWAPI.215]
2553 * Convert an Ascii string to Unicode.
2555 * PARAMS
2556 * lpSrcStr [I] Source Ascii string to convert
2557 * lpDstStr [O] Destination for converted Unicode string
2558 * iLen [I] Length of lpDstStr
2560 * RETURNS
2561 * The return value of the MultiByteToWideChar() function called on lpSrcStr.
2563 * NOTES
2564 * This function simply calls SHAnsiToUnicodeCP with code page CP_ACP.
2566 DWORD WINAPI SHAnsiToUnicode(LPCSTR lpSrcStr, LPWSTR lpDstStr, int iLen)
2568 return SHAnsiToUnicodeCP(CP_ACP, lpSrcStr, lpDstStr, iLen);
2571 /*************************************************************************
2572 * @ [SHLWAPI.218]
2574 * Convert a Unicode string to Ascii.
2576 * PARAMS
2577 * CodePage [I] Code page to use for the conversion
2578 * lpSrcStr [I] Source Unicode string to convert
2579 * lpDstStr [O] Destination for converted Ascii string
2580 * dstlen [I] Length of buffer at lpDstStr
2582 * RETURNS
2583 * Success: The length in bytes of the result at lpDstStr (including the terminator)
2584 * Failure: When using CP_UTF8, CP_UTF7 or 0xc350 as codePage, 0 is returned and
2585 * the result is not nul-terminated.
2586 * When using a different codepage, the length in bytes of the truncated
2587 * result at lpDstStr (including the terminator) is returned and
2588 * lpDstStr is always nul-terminated.
2591 DWORD WINAPI SHUnicodeToAnsiCP(UINT CodePage, LPCWSTR lpSrcStr, LPSTR lpDstStr, int dstlen)
2593 static const WCHAR emptyW[] = { '\0' };
2594 int len , reqLen;
2595 LPSTR mem;
2597 if (!lpDstStr || !dstlen)
2598 return 0;
2600 if (!lpSrcStr)
2601 lpSrcStr = emptyW;
2603 *lpDstStr = '\0';
2605 len = strlenW(lpSrcStr) + 1;
2607 switch (CodePage)
2609 case CP_WINUNICODE:
2610 CodePage = CP_UTF8; /* Fall through... */
2611 case 0x0000C350: /* FIXME: CP_ #define */
2612 case CP_UTF7:
2613 case CP_UTF8:
2615 DWORD dwMode = 0;
2616 INT lenW = len - 1;
2617 INT needed = dstlen - 1;
2618 HRESULT hr;
2620 /* try the user supplied buffer first */
2621 hr = ConvertINetUnicodeToMultiByte(&dwMode, CodePage, lpSrcStr, &lenW, lpDstStr, &needed);
2622 if (hr == S_OK)
2624 lpDstStr[needed] = '\0';
2625 return needed + 1;
2628 /* user buffer too small. exclude termination and copy as much as possible */
2629 lenW = len;
2630 hr = ConvertINetUnicodeToMultiByte(&dwMode, CodePage, lpSrcStr, &lenW, NULL, &needed);
2631 needed++;
2632 mem = HeapAlloc(GetProcessHeap(), 0, needed);
2633 if (!mem)
2634 return 0;
2636 hr = ConvertINetUnicodeToMultiByte(&dwMode, CodePage, lpSrcStr, &len, mem, &needed);
2637 if (hr == S_OK)
2639 reqLen = SHTruncateString(mem, dstlen);
2640 if (reqLen > 0) memcpy(lpDstStr, mem, reqLen-1);
2642 HeapFree(GetProcessHeap(), 0, mem);
2643 return 0;
2645 default:
2646 break;
2649 /* try the user supplied buffer first */
2650 reqLen = WideCharToMultiByte(CodePage, 0, lpSrcStr, len, lpDstStr, dstlen, NULL, NULL);
2652 if (!reqLen && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
2654 reqLen = WideCharToMultiByte(CodePage, 0, lpSrcStr, len, NULL, 0, NULL, NULL);
2655 if (reqLen)
2657 mem = HeapAlloc(GetProcessHeap(), 0, reqLen);
2658 if (mem)
2660 reqLen = WideCharToMultiByte(CodePage, 0, lpSrcStr, len, mem,
2661 reqLen, NULL, NULL);
2663 reqLen = SHTruncateString(mem, dstlen -1);
2664 reqLen++;
2666 lstrcpynA(lpDstStr, mem, reqLen);
2667 HeapFree(GetProcessHeap(), 0, mem);
2668 lpDstStr[reqLen-1] = '\0';
2672 return reqLen;
2675 /*************************************************************************
2676 * @ [SHLWAPI.217]
2678 * Convert a Unicode string to Ascii.
2680 * PARAMS
2681 * lpSrcStr [I] Source Unicode string to convert
2682 * lpDstStr [O] Destination for converted Ascii string
2683 * iLen [O] Length of lpDstStr in characters
2685 * RETURNS
2686 * See SHUnicodeToAnsiCP
2688 * NOTES
2689 * This function simply calls SHUnicodeToAnsiCP() with CodePage = CP_ACP.
2691 INT WINAPI SHUnicodeToAnsi(LPCWSTR lpSrcStr, LPSTR lpDstStr, INT iLen)
2693 return SHUnicodeToAnsiCP(CP_ACP, lpSrcStr, lpDstStr, iLen);
2696 /*************************************************************************
2697 * @ [SHLWAPI.345]
2699 * Copy one string to another.
2701 * PARAMS
2702 * lpszSrc [I] Source string to copy
2703 * lpszDst [O] Destination for copy
2704 * iLen [I] Length of lpszDst in characters
2706 * RETURNS
2707 * The length of the copied string, including the terminating NUL. lpszDst
2708 * contains iLen characters of lpszSrc.
2710 DWORD WINAPI SHAnsiToAnsi(LPCSTR lpszSrc, LPSTR lpszDst, int iLen)
2712 LPSTR lpszRet;
2714 TRACE("(%s,%p,0x%08x)\n", debugstr_a(lpszSrc), lpszDst, iLen);
2716 lpszRet = StrCpyNXA(lpszDst, lpszSrc, iLen);
2717 return lpszRet - lpszDst + 1;
2720 /*************************************************************************
2721 * @ [SHLWAPI.346]
2723 * Unicode version of SSHAnsiToAnsi.
2725 DWORD WINAPI SHUnicodeToUnicode(LPCWSTR lpszSrc, LPWSTR lpszDst, int iLen)
2727 LPWSTR lpszRet;
2729 TRACE("(%s,%p,0x%08x)\n", debugstr_w(lpszSrc), lpszDst, iLen);
2731 lpszRet = StrCpyNXW(lpszDst, lpszSrc, iLen);
2732 return lpszRet - lpszDst + 1;
2735 /*************************************************************************
2736 * @ [SHLWAPI.364]
2738 * Determine if an Ascii string converts to Unicode and back identically.
2740 * PARAMS
2741 * lpSrcStr [I] Source Unicode string to convert
2742 * lpDst [O] Destination for resulting Ascii string
2743 * iLen [I] Length of lpDst in characters
2745 * RETURNS
2746 * TRUE, since Ascii strings always convert identically.
2748 BOOL WINAPI DoesStringRoundTripA(LPCSTR lpSrcStr, LPSTR lpDst, INT iLen)
2750 lstrcpynA(lpDst, lpSrcStr, iLen);
2751 return TRUE;
2754 /*************************************************************************
2755 * @ [SHLWAPI.365]
2757 * Determine if a Unicode string converts to Ascii and back identically.
2759 * PARAMS
2760 * lpSrcStr [I] Source Unicode string to convert
2761 * lpDst [O] Destination for resulting Ascii string
2762 * iLen [I] Length of lpDst in characters
2764 * RETURNS
2765 * TRUE, if lpSrcStr converts to Ascii and back identically,
2766 * FALSE otherwise.
2768 BOOL WINAPI DoesStringRoundTripW(LPCWSTR lpSrcStr, LPSTR lpDst, INT iLen)
2770 WCHAR szBuff[MAX_PATH];
2772 SHUnicodeToAnsi(lpSrcStr, lpDst, iLen);
2773 SHAnsiToUnicode(lpDst, szBuff, MAX_PATH);
2774 return !strcmpW(lpSrcStr, szBuff);
2777 /*************************************************************************
2778 * SHLoadIndirectString [SHLWAPI.@]
2780 * If passed a string that begins with '@', extract the string from the
2781 * appropriate resource, otherwise do a straight copy.
2784 HRESULT WINAPI SHLoadIndirectString(LPCWSTR src, LPWSTR dst, UINT dst_len, void **reserved)
2786 WCHAR *dllname = NULL;
2787 HMODULE hmod = NULL;
2788 HRESULT hr = E_FAIL;
2790 TRACE("(%s %p %08x %p)\n", debugstr_w(src), dst, dst_len, reserved);
2792 if(src[0] == '@')
2794 WCHAR *index_str;
2795 int index;
2797 dst[0] = 0;
2798 dllname = StrDupW(src + 1);
2799 index_str = strchrW(dllname, ',');
2801 if(!index_str) goto end;
2803 *index_str = 0;
2804 index_str++;
2805 index = atoiW(index_str);
2807 hmod = LoadLibraryW(dllname);
2808 if(!hmod) goto end;
2810 if(index < 0)
2812 if(LoadStringW(hmod, -index, dst, dst_len))
2813 hr = S_OK;
2815 else
2816 FIXME("can't handle non-negative indices (%d)\n", index);
2818 else
2820 if(dst != src)
2821 lstrcpynW(dst, src, dst_len);
2822 hr = S_OK;
2825 TRACE("returning %s\n", debugstr_w(dst));
2826 end:
2827 if(hmod) FreeLibrary(hmod);
2828 HeapFree(GetProcessHeap(), 0, dllname);
2829 return hr;