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
27 #define NONAMELESSUNION
31 #define NO_SHLWAPI_REG
32 #define NO_SHLWAPI_STREAM
39 #include "wine/debug.h"
43 WINE_DEFAULT_DEBUG_CHANNEL(shell
);
45 extern HINSTANCE shlwapi_hInstance
;
47 static HRESULT
_SHStrDupAA(LPCSTR
,LPSTR
*);
48 static HRESULT
_SHStrDupAW(LPCWSTR
,LPSTR
*);
50 DWORD WINAPI
SHTruncateString(LPSTR lpStr
, DWORD size
);
52 static void FillNumberFmt(NUMBERFMTW
*fmt
, LPWSTR decimal_buffer
, int decimal_bufwlen
,
53 LPWSTR thousand_buffer
, int thousand_bufwlen
)
58 GetLocaleInfoW(LOCALE_USER_DEFAULT
, LOCALE_ILZERO
|LOCALE_RETURN_NUMBER
, (LPWSTR
)&fmt
->LeadingZero
, sizeof(fmt
->LeadingZero
)/sizeof(WCHAR
));
59 GetLocaleInfoW(LOCALE_USER_DEFAULT
, LOCALE_INEGNUMBER
|LOCALE_RETURN_NUMBER
, (LPWSTR
)&fmt
->LeadingZero
, sizeof(fmt
->NegativeOrder
)/sizeof(WCHAR
));
61 GetLocaleInfoW(LOCALE_USER_DEFAULT
, LOCALE_SDECIMAL
, decimal_buffer
, decimal_bufwlen
);
62 GetLocaleInfoW(LOCALE_USER_DEFAULT
, LOCALE_STHOUSAND
, thousand_buffer
, thousand_bufwlen
);
63 fmt
->lpThousandSep
= thousand_buffer
;
64 fmt
->lpDecimalSep
= decimal_buffer
;
67 * Converting grouping string to number as described on
68 * http://blogs.msdn.com/oldnewthing/archive/2006/04/18/578251.aspx
71 GetLocaleInfoW(LOCALE_USER_DEFAULT
, LOCALE_SGROUPING
, grouping
, ARRAY_SIZE(grouping
));
72 for (c
= grouping
; *c
; c
++)
73 if (*c
>= '0' && *c
< '9')
76 fmt
->Grouping
+= *c
- '0';
79 if (fmt
->Grouping
% 10 == 0)
85 /*************************************************************************
86 * FormatInt [internal]
88 * Format an integer according to the current locale
91 * The number of characters written on success or 0 on failure
93 static int FormatInt(LONGLONG qdwValue
, LPWSTR pszBuf
, int cchBuf
)
96 WCHAR decimal
[8], thousand
[8];
99 BOOL neg
= (qdwValue
< 0);
101 FillNumberFmt(&fmt
, decimal
, ARRAY_SIZE(decimal
), thousand
, ARRAY_SIZE(thousand
));
107 *(--c
) = '0' + (qdwValue
%10);
109 } while (qdwValue
> 0);
113 return GetNumberFormatW(LOCALE_USER_DEFAULT
, 0, c
, &fmt
, pszBuf
, cchBuf
);
116 /*************************************************************************
117 * FormatDouble [internal]
119 * Format an integer according to the current locale. Prints the specified number of digits
120 * after the decimal point
123 * The number of characters written on success or 0 on failure
125 static int FormatDouble(double value
, int decimals
, LPWSTR pszBuf
, int cchBuf
)
127 static const WCHAR flfmt
[] = {'%','f',0};
130 WCHAR decimal
[8], thousand
[8];
132 swprintf(buf
, 64, flfmt
, value
);
134 FillNumberFmt(&fmt
, decimal
, ARRAY_SIZE(decimal
), thousand
, ARRAY_SIZE(thousand
));
135 fmt
.NumDigits
= decimals
;
136 return GetNumberFormatW(LOCALE_USER_DEFAULT
, 0, buf
, &fmt
, pszBuf
, cchBuf
);
139 /*************************************************************************
140 * StrCatW [SHLWAPI.@]
142 * Concatenate two strings.
145 * lpszStr [O] Initial string
146 * lpszSrc [I] String to concatenate
151 LPWSTR WINAPI
StrCatW(LPWSTR lpszStr
, LPCWSTR lpszSrc
)
153 TRACE("(%s,%s)\n", debugstr_w(lpszStr
), debugstr_w(lpszSrc
));
155 if (lpszStr
&& lpszSrc
)
156 lstrcatW(lpszStr
, lpszSrc
);
160 /*************************************************************************
161 * StrCpyW [SHLWAPI.@]
163 * Copy a string to another string.
166 * lpszStr [O] Destination string
167 * lpszSrc [I] Source string
172 LPWSTR WINAPI
StrCpyW(LPWSTR lpszStr
, LPCWSTR lpszSrc
)
174 TRACE("(%p,%s)\n", lpszStr
, debugstr_w(lpszSrc
));
176 if (lpszStr
&& lpszSrc
)
177 lstrcpyW(lpszStr
, lpszSrc
);
181 /*************************************************************************
182 * StrRetToBufA [SHLWAPI.@]
184 * Convert a STRRET to a normal string.
187 * lpStrRet [O] STRRET to convert
188 * pIdl [I] ITEMIDLIST for lpStrRet->uType == STRRET_OFFSET
189 * lpszDest [O] Destination for normal string
190 * dwLen [I] Length of lpszDest
193 * Success: S_OK. lpszDest contains up to dwLen characters of the string.
194 * If lpStrRet is of type STRRET_WSTR, its memory is freed with
195 * CoTaskMemFree() and its type set to STRRET_CSTRA.
196 * Failure: E_FAIL, if any parameters are invalid.
198 HRESULT WINAPI
StrRetToBufA (LPSTRRET src
, const ITEMIDLIST
*pidl
, LPSTR dest
, UINT len
)
201 * This routine is identical to that in dlls/shell32/shellstring.c.
202 * It was duplicated because not every version of Shlwapi.dll exports
203 * StrRetToBufA. If you change one routine, change them both.
205 TRACE("dest=%p len=0x%x strret=%p pidl=%p\n", dest
, len
, src
, pidl
);
209 WARN("Invalid lpStrRet would crash under Win32!\n");
223 WideCharToMultiByte(CP_ACP
, 0, src
->u
.pOleStr
, -1, dest
, len
, NULL
, NULL
);
224 CoTaskMemFree(src
->u
.pOleStr
);
228 lstrcpynA(dest
, src
->u
.cStr
, len
);
232 lstrcpynA((LPSTR
)dest
, ((LPCSTR
)&pidl
->mkid
)+src
->u
.uOffset
, len
);
236 FIXME("unknown type!\n");
242 /*************************************************************************
243 * StrRetToBufW [SHLWAPI.@]
247 HRESULT WINAPI
StrRetToBufW (LPSTRRET src
, const ITEMIDLIST
*pidl
, LPWSTR dest
, UINT len
)
249 TRACE("dest=%p len=0x%x strret=%p pidl=%p\n", dest
, len
, src
, pidl
);
256 WARN("Invalid lpStrRet would crash under Win32!\n");
263 switch (src
->uType
) {
268 dst_len
= lstrlenW(src
->u
.pOleStr
);
269 memcpy(dest
, src
->u
.pOleStr
, min(dst_len
, len
-1) * sizeof(WCHAR
));
270 dest
[min(dst_len
, len
-1)] = 0;
271 CoTaskMemFree(src
->u
.pOleStr
);
275 return E_NOT_SUFFICIENT_BUFFER
;
281 if (!MultiByteToWideChar( CP_ACP
, 0, src
->u
.cStr
, -1, dest
, len
))
288 if (!MultiByteToWideChar( CP_ACP
, 0, ((LPCSTR
)&pidl
->mkid
)+src
->u
.uOffset
, -1,
295 FIXME("unknown type!\n");
302 /*************************************************************************
303 * StrRetToStrA [SHLWAPI.@]
305 * Converts a STRRET to a normal string.
308 * lpStrRet [O] STRRET to convert
309 * pidl [I] ITEMIDLIST for lpStrRet->uType == STRRET_OFFSET
310 * ppszName [O] Destination for converted string
313 * Success: S_OK. ppszName contains the new string, allocated with CoTaskMemAlloc().
314 * Failure: E_FAIL, if any parameters are invalid.
316 HRESULT WINAPI
StrRetToStrA(LPSTRRET lpStrRet
, const ITEMIDLIST
*pidl
, LPSTR
*ppszName
)
318 HRESULT hRet
= E_FAIL
;
320 switch (lpStrRet
->uType
)
323 hRet
= _SHStrDupAW(lpStrRet
->u
.pOleStr
, ppszName
);
324 CoTaskMemFree(lpStrRet
->u
.pOleStr
);
328 hRet
= _SHStrDupAA(lpStrRet
->u
.cStr
, ppszName
);
332 hRet
= _SHStrDupAA(((LPCSTR
)&pidl
->mkid
) + lpStrRet
->u
.uOffset
, ppszName
);
342 /*************************************************************************
343 * StrRetToStrW [SHLWAPI.@]
347 HRESULT WINAPI
StrRetToStrW(LPSTRRET lpStrRet
, const ITEMIDLIST
*pidl
, LPWSTR
*ppszName
)
349 HRESULT hRet
= E_FAIL
;
351 switch (lpStrRet
->uType
)
354 hRet
= SHStrDupW(lpStrRet
->u
.pOleStr
, ppszName
);
355 CoTaskMemFree(lpStrRet
->u
.pOleStr
);
359 hRet
= SHStrDupA(lpStrRet
->u
.cStr
, ppszName
);
363 hRet
= SHStrDupA(((LPCSTR
)&pidl
->mkid
) + lpStrRet
->u
.uOffset
, ppszName
);
373 /* Create an ASCII string copy using SysAllocString() */
374 static HRESULT
_SHStrDupAToBSTR(LPCSTR src
, BSTR
*pBstrOut
)
380 INT len
= MultiByteToWideChar(CP_ACP
, 0, src
, -1, NULL
, 0);
381 WCHAR
* szTemp
= HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
));
385 MultiByteToWideChar(CP_ACP
, 0, src
, -1, szTemp
, len
);
386 *pBstrOut
= SysAllocString(szTemp
);
387 HeapFree(GetProcessHeap(), 0, szTemp
);
393 return E_OUTOFMEMORY
;
396 /*************************************************************************
397 * StrRetToBSTR [SHLWAPI.@]
399 * Converts a STRRET to a BSTR.
402 * lpStrRet [O] STRRET to convert
403 * pidl [I] ITEMIDLIST for lpStrRet->uType = STRRET_OFFSET
404 * pBstrOut [O] Destination for converted BSTR
407 * Success: S_OK. pBstrOut contains the new string.
408 * Failure: E_FAIL, if any parameters are invalid.
410 HRESULT WINAPI
StrRetToBSTR(STRRET
*lpStrRet
, LPCITEMIDLIST pidl
, BSTR
* pBstrOut
)
412 HRESULT hRet
= E_FAIL
;
414 switch (lpStrRet
->uType
)
417 *pBstrOut
= SysAllocString(lpStrRet
->u
.pOleStr
);
420 CoTaskMemFree(lpStrRet
->u
.pOleStr
);
424 hRet
= _SHStrDupAToBSTR(lpStrRet
->u
.cStr
, pBstrOut
);
428 hRet
= _SHStrDupAToBSTR(((LPCSTR
)&pidl
->mkid
) + lpStrRet
->u
.uOffset
, pBstrOut
);
438 /*************************************************************************
439 * StrFormatKBSizeA [SHLWAPI.@]
441 * Create a formatted string containing a byte count in Kilobytes.
444 * llBytes [I] Byte size to format
445 * lpszDest [I] Destination for formatted string
446 * cchMax [I] Size of lpszDest
451 LPSTR WINAPI
StrFormatKBSizeA(LONGLONG llBytes
, LPSTR lpszDest
, UINT cchMax
)
455 if (!StrFormatKBSizeW(llBytes
, wszBuf
, 256))
457 if (!WideCharToMultiByte(CP_ACP
, 0, wszBuf
, -1, lpszDest
, cchMax
, NULL
, NULL
))
462 /*************************************************************************
463 * StrFormatKBSizeW [SHLWAPI.@]
465 * See StrFormatKBSizeA.
467 LPWSTR WINAPI
StrFormatKBSizeW(LONGLONG llBytes
, LPWSTR lpszDest
, UINT cchMax
)
469 static const WCHAR kb
[] = {' ','K','B',0};
470 LONGLONG llKB
= (llBytes
+ 1023) >> 10;
473 TRACE("(0x%s,%p,%d)\n", wine_dbgstr_longlong(llBytes
), lpszDest
, cchMax
);
475 if (!FormatInt(llKB
, lpszDest
, cchMax
))
478 len
= lstrlenW(lpszDest
);
479 if (cchMax
- len
< 4)
481 lstrcatW(lpszDest
, kb
);
485 /*************************************************************************
486 * StrNCatA [SHLWAPI.@]
488 * Concatenate two strings together.
491 * lpszStr [O] String to concatenate to
492 * lpszCat [I] String to add to lpszCat
493 * cchMax [I] Maximum number of characters to concatenate
499 * cchMax determines the number of characters that are appended to lpszStr,
500 * not the total length of the string.
502 LPSTR WINAPI
StrNCatA(LPSTR lpszStr
, LPCSTR lpszCat
, INT cchMax
)
504 LPSTR lpszRet
= lpszStr
;
506 TRACE("(%s,%s,%i)\n", debugstr_a(lpszStr
), debugstr_a(lpszCat
), cchMax
);
510 WARN("Invalid lpszStr would crash under Win32!\n");
514 StrCpyNA(lpszStr
+ strlen(lpszStr
), lpszCat
, cchMax
);
518 /*************************************************************************
519 * StrNCatW [SHLWAPI.@]
523 LPWSTR WINAPI
StrNCatW(LPWSTR lpszStr
, LPCWSTR lpszCat
, INT cchMax
)
525 LPWSTR lpszRet
= lpszStr
;
527 TRACE("(%s,%s,%i)\n", debugstr_w(lpszStr
), debugstr_w(lpszCat
), cchMax
);
531 WARN("Invalid lpszStr would crash under Win32\n");
535 StrCpyNW(lpszStr
+ lstrlenW(lpszStr
), lpszCat
, cchMax
);
539 /*************************************************************************
540 * _SHStrDupAA [INTERNAL]
542 * Duplicates a ASCII string to ASCII. The destination buffer is allocated.
544 static HRESULT
_SHStrDupAA(LPCSTR src
, LPSTR
* dest
)
550 len
= lstrlenA(src
) + 1;
551 *dest
= CoTaskMemAlloc(len
);
557 lstrcpynA(*dest
,src
, len
);
563 TRACE("%s->(%p)\n", debugstr_a(src
), *dest
);
567 /*************************************************************************
568 * SHStrDupA [SHLWAPI.@]
570 * Return a Unicode copy of a string, in memory allocated by CoTaskMemAlloc().
573 * lpszStr [I] String to copy
574 * lppszDest [O] Destination for the new string copy
577 * Success: S_OK. lppszDest contains the new string in Unicode format.
578 * Failure: E_OUTOFMEMORY, If any arguments are invalid or memory allocation
581 HRESULT WINAPI
SHStrDupA(LPCSTR lpszStr
, LPWSTR
* lppszDest
)
588 len
= MultiByteToWideChar(CP_ACP
, 0, lpszStr
, -1, NULL
, 0) * sizeof(WCHAR
);
589 *lppszDest
= CoTaskMemAlloc(len
);
596 MultiByteToWideChar(CP_ACP
, 0, lpszStr
, -1, *lppszDest
, len
/sizeof(WCHAR
));
600 hRet
= E_OUTOFMEMORY
;
602 TRACE("%s->(%p)\n", debugstr_a(lpszStr
), *lppszDest
);
606 /*************************************************************************
607 * _SHStrDupAW [INTERNAL]
609 * Duplicates a UNICODE to a ASCII string. The destination buffer is allocated.
611 static HRESULT
_SHStrDupAW(LPCWSTR src
, LPSTR
* dest
)
617 len
= WideCharToMultiByte(CP_ACP
, 0, src
, -1, NULL
, 0, NULL
, NULL
);
618 *dest
= CoTaskMemAlloc(len
);
624 WideCharToMultiByte(CP_ACP
, 0, src
, -1, *dest
, len
, NULL
, NULL
);
630 TRACE("%s->(%p)\n", debugstr_w(src
), *dest
);
634 /*************************************************************************
635 * SHStrDupW [SHLWAPI.@]
639 HRESULT WINAPI
SHStrDupW(LPCWSTR src
, LPWSTR
* dest
)
645 len
= (lstrlenW(src
) + 1) * sizeof(WCHAR
);
646 *dest
= CoTaskMemAlloc(len
);
652 memcpy(*dest
, src
, len
);
658 TRACE("%s->(%p)\n", debugstr_w(src
), *dest
);
662 /*************************************************************************
663 * SHLWAPI_WriteReverseNum
665 * Internal helper for SHLWAPI_WriteTimeClass.
667 static inline LPWSTR
SHLWAPI_WriteReverseNum(LPWSTR lpszOut
, DWORD dwNum
)
671 /* Write a decimal number to a string, backwards */
674 DWORD dwNextDigit
= dwNum
% 10;
675 *lpszOut
-- = '0' + dwNextDigit
;
676 dwNum
= (dwNum
- dwNextDigit
) / 10;
682 /*************************************************************************
683 * SHLWAPI_FormatSignificant
685 * Internal helper for SHLWAPI_WriteTimeClass.
687 static inline int SHLWAPI_FormatSignificant(LPWSTR lpszNum
, int dwDigits
)
689 /* Zero non significant digits, return remaining significant digits */
703 /*************************************************************************
704 * SHLWAPI_WriteTimeClass
706 * Internal helper for StrFromTimeIntervalW.
708 static int SHLWAPI_WriteTimeClass(LPWSTR lpszOut
, DWORD dwValue
,
709 UINT uClassStringId
, int iDigits
)
711 WCHAR szBuff
[64], *szOut
= szBuff
+ 32;
713 szOut
= SHLWAPI_WriteReverseNum(szOut
, dwValue
);
714 iDigits
= SHLWAPI_FormatSignificant(szOut
+ 1, iDigits
);
716 LoadStringW(shlwapi_hInstance
, uClassStringId
, szBuff
+ 32, 32);
717 lstrcatW(lpszOut
, szOut
);
721 /*************************************************************************
722 * StrFromTimeIntervalA [SHLWAPI.@]
724 * Format a millisecond time interval into a string
727 * lpszStr [O] Output buffer for formatted time interval
728 * cchMax [I] Size of lpszStr
729 * dwMS [I] Number of milliseconds
730 * iDigits [I] Number of digits to print
733 * The length of the formatted string, or 0 if any parameter is invalid.
736 * This implementation mimics the Win32 behaviour of always writing a leading
737 * space before the time interval begins.
739 * iDigits is used to provide approximate times if accuracy is not important.
740 * This number of digits will be written of the first non-zero time class
741 * (hours/minutes/seconds). If this does not complete the time classification,
742 * the remaining digits are changed to zeros (i.e. The time is _not_ rounded).
743 * If there are digits remaining following the writing of a time class, the
744 * next time class will be written.
746 * For example, given dwMS represents 138 hours,43 minutes and 15 seconds, the
747 * following will result from the given values of iDigits:
749 *| iDigits 1 2 3 4 5 ...
750 *| lpszStr "100 hr" "130 hr" "138 hr" "138 hr 40 min" "138 hr 43 min" ...
752 INT WINAPI
StrFromTimeIntervalA(LPSTR lpszStr
, UINT cchMax
, DWORD dwMS
,
757 TRACE("(%p,%d,%d,%d)\n", lpszStr
, cchMax
, dwMS
, iDigits
);
759 if (lpszStr
&& cchMax
)
762 StrFromTimeIntervalW(szBuff
, ARRAY_SIZE(szBuff
), dwMS
, iDigits
);
763 WideCharToMultiByte(CP_ACP
,0,szBuff
,-1,lpszStr
,cchMax
,0,0);
769 /*************************************************************************
770 * StrFromTimeIntervalW [SHLWAPI.@]
772 * See StrFromTimeIntervalA.
774 INT WINAPI
StrFromTimeIntervalW(LPWSTR lpszStr
, UINT cchMax
, DWORD dwMS
,
779 TRACE("(%p,%d,%d,%d)\n", lpszStr
, cchMax
, dwMS
, iDigits
);
781 if (lpszStr
&& cchMax
)
784 DWORD dwHours
, dwMinutes
;
786 if (!iDigits
|| cchMax
== 1)
792 /* Calculate the time classes */
793 dwMS
= (dwMS
+ 500) / 1000;
794 dwHours
= dwMS
/ 3600;
795 dwMS
-= dwHours
* 3600;
796 dwMinutes
= dwMS
/ 60;
797 dwMS
-= dwMinutes
* 60;
802 iDigits
= SHLWAPI_WriteTimeClass(szCopy
, dwHours
, IDS_TIME_INTERVAL_HOURS
, iDigits
);
804 if (dwMinutes
&& iDigits
)
805 iDigits
= SHLWAPI_WriteTimeClass(szCopy
, dwMinutes
, IDS_TIME_INTERVAL_MINUTES
, iDigits
);
807 if (iDigits
) /* Always write seconds if we have significant digits */
808 SHLWAPI_WriteTimeClass(szCopy
, dwMS
, IDS_TIME_INTERVAL_SECONDS
, iDigits
);
810 lstrcpynW(lpszStr
, szCopy
, cchMax
);
811 iRet
= lstrlenW(lpszStr
);
816 /* Structure for formatting byte strings */
817 typedef struct tagSHLWAPI_BYTEFORMATS
824 } SHLWAPI_BYTEFORMATS
;
826 /*************************************************************************
827 * StrFormatByteSizeW [SHLWAPI.@]
829 * Create a string containing an abbreviated byte count of up to 2^63-1.
832 * llBytes [I] Byte size to format
833 * lpszDest [I] Destination for formatted string
834 * cchMax [I] Size of lpszDest
840 * There is no StrFormatByteSize64W function, it is called StrFormatByteSizeW().
842 LPWSTR WINAPI
StrFormatByteSizeW(LONGLONG llBytes
, LPWSTR lpszDest
, UINT cchMax
)
844 #define KB ((ULONGLONG)1024)
846 #define GB (KB*KB*KB)
847 #define TB (KB*KB*KB*KB)
848 #define PB (KB*KB*KB*KB*KB)
850 static const SHLWAPI_BYTEFORMATS bfFormats
[] =
852 { 10*KB
, 10.24, 100.0, 2, 'K' }, /* 10 KB */
853 { 100*KB
, 102.4, 10.0, 1, 'K' }, /* 100 KB */
854 { 1000*KB
, 1024.0, 1.0, 0, 'K' }, /* 1000 KB */
855 { 10*MB
, 10485.76, 100.0, 2, 'M' }, /* 10 MB */
856 { 100*MB
, 104857.6, 10.0, 1, 'M' }, /* 100 MB */
857 { 1000*MB
, 1048576.0, 1.0, 0, 'M' }, /* 1000 MB */
858 { 10*GB
, 10737418.24, 100.0, 2, 'G' }, /* 10 GB */
859 { 100*GB
, 107374182.4, 10.0, 1, 'G' }, /* 100 GB */
860 { 1000*GB
, 1073741824.0, 1.0, 0, 'G' }, /* 1000 GB */
861 { 10*TB
, 10485.76, 100.0, 2, 'T' }, /* 10 TB */
862 { 100*TB
, 104857.6, 10.0, 1, 'T' }, /* 100 TB */
863 { 1000*TB
, 1048576.0, 1.0, 0, 'T' }, /* 1000 TB */
864 { 10*PB
, 10737418.24, 100.00, 2, 'P' }, /* 10 PB */
865 { 100*PB
, 107374182.4, 10.00, 1, 'P' }, /* 100 PB */
866 { 1000*PB
, 1073741824.0, 1.00, 0, 'P' }, /* 1000 PB */
867 { 0, 10995116277.76, 100.00, 2, 'E' } /* EB's, catch all */
869 WCHAR wszAdd
[] = {' ','?','B',0};
873 TRACE("(0x%s,%p,%d)\n", wine_dbgstr_longlong(llBytes
), lpszDest
, cchMax
);
875 if (!lpszDest
|| !cchMax
)
878 if (llBytes
< 1024) /* 1K */
880 WCHAR wszBytesFormat
[64];
881 LoadStringW(shlwapi_hInstance
, IDS_BYTES_FORMAT
, wszBytesFormat
, 64);
882 swprintf(lpszDest
, cchMax
, wszBytesFormat
, (int)llBytes
);
886 /* Note that if this loop completes without finding a match, i will be
887 * pointing at the last entry, which is a catch all for > 1000 PB
889 while (i
< ARRAY_SIZE(bfFormats
) - 1)
891 if (llBytes
< bfFormats
[i
].dLimit
)
895 /* Above 1 TB we encounter problems with FP accuracy. So for amounts above
896 * this number we integer shift down by 1 MB first. The table above has
897 * the divisors scaled down from the '< 10 TB' entry onwards, to account
898 * for this. We also add a small fudge factor to get the correct result for
899 * counts that lie exactly on a 1024 byte boundary.
902 dBytes
= (double)(llBytes
>> 20) + 0.001; /* Scale down by 1 MB */
904 dBytes
= (double)llBytes
+ 0.00001;
906 dBytes
= floor(dBytes
/ bfFormats
[i
].dDivisor
) / bfFormats
[i
].dNormaliser
;
908 if (!FormatDouble(dBytes
, bfFormats
[i
].nDecimals
, lpszDest
, cchMax
))
910 wszAdd
[1] = bfFormats
[i
].wPrefix
;
911 StrCatBuffW(lpszDest
, wszAdd
, cchMax
);
915 /*************************************************************************
916 * StrFormatByteSize64A [SHLWAPI.@]
918 * See StrFormatByteSizeW.
920 LPSTR WINAPI
StrFormatByteSize64A(LONGLONG llBytes
, LPSTR lpszDest
, UINT cchMax
)
924 StrFormatByteSizeW(llBytes
, wszBuff
, ARRAY_SIZE(wszBuff
));
927 WideCharToMultiByte(CP_ACP
, 0, wszBuff
, -1, lpszDest
, cchMax
, 0, 0);
931 /*************************************************************************
932 * StrFormatByteSizeA [SHLWAPI.@]
934 * Create a string containing an abbreviated byte count of up to 2^31-1.
937 * dwBytes [I] Byte size to format
938 * lpszDest [I] Destination for formatted string
939 * cchMax [I] Size of lpszDest
945 * The Ascii and Unicode versions of this function accept a different
946 * integer type for dwBytes. See StrFormatByteSize64A().
948 LPSTR WINAPI
StrFormatByteSizeA(DWORD dwBytes
, LPSTR lpszDest
, UINT cchMax
)
950 TRACE("(%d,%p,%d)\n", dwBytes
, lpszDest
, cchMax
);
952 return StrFormatByteSize64A(dwBytes
, lpszDest
, cchMax
);
955 /*************************************************************************
958 * Remove a single non-trailing ampersand ('&') from a string.
961 * lpszStr [I/O] String to remove ampersand from.
964 * The character after the first ampersand in lpszStr, or the first character
965 * in lpszStr if there is no ampersand in the string.
967 char WINAPI
SHStripMneumonicA(LPCSTR lpszStr
)
969 LPSTR lpszIter
, lpszTmp
;
972 TRACE("(%s)\n", debugstr_a(lpszStr
));
976 if ((lpszIter
= StrChrA(lpszStr
, '&')))
978 lpszTmp
= CharNextA(lpszIter
);
984 memmove( lpszIter
, lpszTmp
, strlen(lpszTmp
) + 1 );
991 /*************************************************************************
994 * Unicode version of SHStripMneumonicA.
996 WCHAR WINAPI
SHStripMneumonicW(LPCWSTR lpszStr
)
998 LPWSTR lpszIter
, lpszTmp
;
1001 TRACE("(%s)\n", debugstr_w(lpszStr
));
1005 if ((lpszIter
= StrChrW(lpszStr
, '&')))
1007 lpszTmp
= lpszIter
+ 1;
1010 if (*lpszTmp
!= '&')
1013 memmove( lpszIter
, lpszTmp
, (lstrlenW(lpszTmp
) + 1) * sizeof(WCHAR
) );
1020 /*************************************************************************
1023 * Convert an Ascii string to Unicode.
1026 * dwCp [I] Code page for the conversion
1027 * lpSrcStr [I] Source Ascii string to convert
1028 * lpDstStr [O] Destination for converted Unicode string
1029 * iLen [I] Length of lpDstStr
1032 * The return value of the MultiByteToWideChar() function called on lpSrcStr.
1034 DWORD WINAPI
SHAnsiToUnicodeCP(DWORD dwCp
, LPCSTR lpSrcStr
, LPWSTR lpDstStr
, int iLen
)
1038 dwRet
= MultiByteToWideChar(dwCp
, 0, lpSrcStr
, -1, lpDstStr
, iLen
);
1039 TRACE("%s->%s,ret=%d\n", debugstr_a(lpSrcStr
), debugstr_w(lpDstStr
), dwRet
);
1043 /*************************************************************************
1046 * Convert an Ascii string to Unicode.
1049 * lpSrcStr [I] Source Ascii string to convert
1050 * lpDstStr [O] Destination for converted Unicode string
1051 * iLen [I] Length of lpDstStr
1054 * The return value of the MultiByteToWideChar() function called on lpSrcStr.
1057 * This function simply calls SHAnsiToUnicodeCP with code page CP_ACP.
1059 DWORD WINAPI
SHAnsiToUnicode(LPCSTR lpSrcStr
, LPWSTR lpDstStr
, int iLen
)
1061 return SHAnsiToUnicodeCP(CP_ACP
, lpSrcStr
, lpDstStr
, iLen
);
1064 /*************************************************************************
1067 * Convert a Unicode string to Ascii.
1070 * CodePage [I] Code page to use for the conversion
1071 * lpSrcStr [I] Source Unicode string to convert
1072 * lpDstStr [O] Destination for converted Ascii string
1073 * dstlen [I] Length of buffer at lpDstStr
1076 * Success: The length in bytes of the result at lpDstStr (including the terminator)
1077 * Failure: When using CP_UTF8, CP_UTF7 or 0xc350 as codePage, 0 is returned and
1078 * the result is not nul-terminated.
1079 * When using a different codepage, the length in bytes of the truncated
1080 * result at lpDstStr (including the terminator) is returned and
1081 * lpDstStr is always nul-terminated.
1084 DWORD WINAPI
SHUnicodeToAnsiCP(UINT CodePage
, LPCWSTR lpSrcStr
, LPSTR lpDstStr
, int dstlen
)
1086 static const WCHAR emptyW
[] = { '\0' };
1090 if (!lpDstStr
|| !dstlen
)
1098 len
= lstrlenW(lpSrcStr
) + 1;
1103 CodePage
= CP_UTF8
; /* Fall through... */
1104 case 0x0000C350: /* FIXME: CP_ #define */
1110 INT needed
= dstlen
- 1;
1113 /* try the user supplied buffer first */
1114 hr
= ConvertINetUnicodeToMultiByte(&dwMode
, CodePage
, lpSrcStr
, &lenW
, lpDstStr
, &needed
);
1117 lpDstStr
[needed
] = '\0';
1121 /* user buffer too small. exclude termination and copy as much as possible */
1123 hr
= ConvertINetUnicodeToMultiByte(&dwMode
, CodePage
, lpSrcStr
, &lenW
, NULL
, &needed
);
1125 mem
= HeapAlloc(GetProcessHeap(), 0, needed
);
1129 hr
= ConvertINetUnicodeToMultiByte(&dwMode
, CodePage
, lpSrcStr
, &len
, mem
, &needed
);
1132 reqLen
= SHTruncateString(mem
, dstlen
);
1133 if (reqLen
> 0) memcpy(lpDstStr
, mem
, reqLen
-1);
1135 HeapFree(GetProcessHeap(), 0, mem
);
1142 /* try the user supplied buffer first */
1143 reqLen
= WideCharToMultiByte(CodePage
, 0, lpSrcStr
, len
, lpDstStr
, dstlen
, NULL
, NULL
);
1145 if (!reqLen
&& GetLastError() == ERROR_INSUFFICIENT_BUFFER
)
1147 reqLen
= WideCharToMultiByte(CodePage
, 0, lpSrcStr
, len
, NULL
, 0, NULL
, NULL
);
1150 mem
= HeapAlloc(GetProcessHeap(), 0, reqLen
);
1153 WideCharToMultiByte(CodePage
, 0, lpSrcStr
, len
, mem
, reqLen
, NULL
, NULL
);
1155 reqLen
= SHTruncateString(mem
, dstlen
-1);
1158 lstrcpynA(lpDstStr
, mem
, reqLen
);
1159 HeapFree(GetProcessHeap(), 0, mem
);
1160 lpDstStr
[reqLen
-1] = '\0';
1167 /*************************************************************************
1170 * Convert a Unicode string to Ascii.
1173 * lpSrcStr [I] Source Unicode string to convert
1174 * lpDstStr [O] Destination for converted Ascii string
1175 * iLen [O] Length of lpDstStr in characters
1178 * See SHUnicodeToAnsiCP
1181 * This function simply calls SHUnicodeToAnsiCP() with CodePage = CP_ACP.
1183 INT WINAPI
SHUnicodeToAnsi(LPCWSTR lpSrcStr
, LPSTR lpDstStr
, INT iLen
)
1185 return SHUnicodeToAnsiCP(CP_ACP
, lpSrcStr
, lpDstStr
, iLen
);
1188 /*************************************************************************
1191 * Determine if an Ascii string converts to Unicode and back identically.
1194 * lpSrcStr [I] Source Unicode string to convert
1195 * lpDst [O] Destination for resulting Ascii string
1196 * iLen [I] Length of lpDst in characters
1199 * TRUE, since Ascii strings always convert identically.
1201 BOOL WINAPI
DoesStringRoundTripA(LPCSTR lpSrcStr
, LPSTR lpDst
, INT iLen
)
1203 lstrcpynA(lpDst
, lpSrcStr
, iLen
);
1207 /*************************************************************************
1210 * Determine if a Unicode string converts to Ascii and back identically.
1213 * lpSrcStr [I] Source Unicode string to convert
1214 * lpDst [O] Destination for resulting Ascii string
1215 * iLen [I] Length of lpDst in characters
1218 * TRUE, if lpSrcStr converts to Ascii and back identically,
1221 BOOL WINAPI
DoesStringRoundTripW(LPCWSTR lpSrcStr
, LPSTR lpDst
, INT iLen
)
1223 WCHAR szBuff
[MAX_PATH
];
1225 SHUnicodeToAnsi(lpSrcStr
, lpDst
, iLen
);
1226 SHAnsiToUnicode(lpDst
, szBuff
, MAX_PATH
);
1227 return !wcscmp(lpSrcStr
, szBuff
);