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
29 #define NO_SHLWAPI_REG
30 #define NO_SHLWAPI_STREAM
37 #include "wine/debug.h"
41 WINE_DEFAULT_DEBUG_CHANNEL(shell
);
43 extern HINSTANCE shlwapi_hInstance
;
45 static HRESULT
_SHStrDupAA(LPCSTR
,LPSTR
*);
46 static HRESULT
_SHStrDupAW(LPCWSTR
,LPSTR
*);
48 DWORD WINAPI
SHTruncateString(LPSTR lpStr
, DWORD size
);
50 static void FillNumberFmt(NUMBERFMTW
*fmt
, LPWSTR decimal_buffer
, int decimal_bufwlen
,
51 LPWSTR thousand_buffer
, int thousand_bufwlen
)
56 GetLocaleInfoW(LOCALE_USER_DEFAULT
, LOCALE_ILZERO
|LOCALE_RETURN_NUMBER
, (LPWSTR
)&fmt
->LeadingZero
, sizeof(fmt
->LeadingZero
)/sizeof(WCHAR
));
57 GetLocaleInfoW(LOCALE_USER_DEFAULT
, LOCALE_INEGNUMBER
|LOCALE_RETURN_NUMBER
, (LPWSTR
)&fmt
->NegativeOrder
, sizeof(fmt
->NegativeOrder
)/sizeof(WCHAR
));
59 GetLocaleInfoW(LOCALE_USER_DEFAULT
, LOCALE_SDECIMAL
, decimal_buffer
, decimal_bufwlen
);
60 GetLocaleInfoW(LOCALE_USER_DEFAULT
, LOCALE_STHOUSAND
, thousand_buffer
, thousand_bufwlen
);
61 fmt
->lpThousandSep
= thousand_buffer
;
62 fmt
->lpDecimalSep
= decimal_buffer
;
65 * Converting grouping string to number as described on
66 * http://blogs.msdn.com/oldnewthing/archive/2006/04/18/578251.aspx
69 GetLocaleInfoW(LOCALE_USER_DEFAULT
, LOCALE_SGROUPING
, grouping
, ARRAY_SIZE(grouping
));
70 for (c
= grouping
; *c
; c
++)
71 if (*c
>= '0' && *c
< '9')
74 fmt
->Grouping
+= *c
- '0';
77 if (fmt
->Grouping
% 10 == 0)
83 /*************************************************************************
84 * FormatInt [internal]
86 * Format an integer according to the current locale
89 * The number of characters written on success or 0 on failure
91 static int FormatInt(LONGLONG qdwValue
, LPWSTR pszBuf
, int cchBuf
)
94 WCHAR decimal
[8], thousand
[8];
97 BOOL neg
= (qdwValue
< 0);
99 FillNumberFmt(&fmt
, decimal
, ARRAY_SIZE(decimal
), thousand
, ARRAY_SIZE(thousand
));
105 *(--c
) = '0' + (qdwValue
%10);
107 } while (qdwValue
> 0);
111 return GetNumberFormatW(LOCALE_USER_DEFAULT
, 0, c
, &fmt
, pszBuf
, cchBuf
);
114 /*************************************************************************
115 * FormatDouble [internal]
117 * Format an integer according to the current locale. Prints the specified number of digits
118 * after the decimal point
121 * The number of characters written on success or 0 on failure
123 static int FormatDouble(double value
, int decimals
, LPWSTR pszBuf
, int cchBuf
)
125 static const WCHAR flfmt
[] = {'%','f',0};
128 WCHAR decimal
[8], thousand
[8];
130 swprintf(buf
, 64, flfmt
, value
);
132 FillNumberFmt(&fmt
, decimal
, ARRAY_SIZE(decimal
), thousand
, ARRAY_SIZE(thousand
));
133 fmt
.NumDigits
= decimals
;
134 return GetNumberFormatW(LOCALE_USER_DEFAULT
, 0, buf
, &fmt
, pszBuf
, cchBuf
);
137 /*************************************************************************
138 * StrCatW [SHLWAPI.@]
140 * Concatenate two strings.
143 * lpszStr [O] Initial string
144 * lpszSrc [I] String to concatenate
149 LPWSTR WINAPI
StrCatW(LPWSTR lpszStr
, LPCWSTR lpszSrc
)
151 TRACE("(%s,%s)\n", debugstr_w(lpszStr
), debugstr_w(lpszSrc
));
153 if (lpszStr
&& lpszSrc
)
154 lstrcatW(lpszStr
, lpszSrc
);
158 /*************************************************************************
159 * StrCpyW [SHLWAPI.@]
161 * Copy a string to another string.
164 * lpszStr [O] Destination string
165 * lpszSrc [I] Source string
170 LPWSTR WINAPI
StrCpyW(LPWSTR lpszStr
, LPCWSTR lpszSrc
)
172 TRACE("(%p,%s)\n", lpszStr
, debugstr_w(lpszSrc
));
174 if (lpszStr
&& lpszSrc
)
175 lstrcpyW(lpszStr
, lpszSrc
);
179 /*************************************************************************
180 * StrRetToBufA [SHLWAPI.@]
182 * Convert a STRRET to a normal string.
185 * lpStrRet [O] STRRET to convert
186 * pIdl [I] ITEMIDLIST for lpStrRet->uType == STRRET_OFFSET
187 * lpszDest [O] Destination for normal string
188 * dwLen [I] Length of lpszDest
191 * Success: S_OK. lpszDest contains up to dwLen characters of the string.
192 * If lpStrRet is of type STRRET_WSTR, its memory is freed with
193 * CoTaskMemFree() and its type set to STRRET_CSTRA.
194 * Failure: E_FAIL, if any parameters are invalid.
196 HRESULT WINAPI
StrRetToBufA (LPSTRRET src
, const ITEMIDLIST
*pidl
, LPSTR dest
, UINT len
)
199 * This routine is identical to that in dlls/shell32/shellstring.c.
200 * It was duplicated because not every version of Shlwapi.dll exports
201 * StrRetToBufA. If you change one routine, change them both.
203 TRACE("dest=%p len=0x%x strret=%p pidl=%p\n", dest
, len
, src
, pidl
);
207 WARN("Invalid lpStrRet would crash under Win32!\n");
221 WideCharToMultiByte(CP_ACP
, 0, src
->pOleStr
, -1, dest
, len
, NULL
, NULL
);
222 CoTaskMemFree(src
->pOleStr
);
226 lstrcpynA(dest
, src
->cStr
, len
);
230 lstrcpynA(dest
, ((LPCSTR
)&pidl
->mkid
)+src
->uOffset
, len
);
234 FIXME("unknown type!\n");
240 /*************************************************************************
241 * StrRetToBufW [SHLWAPI.@]
245 HRESULT WINAPI
StrRetToBufW (LPSTRRET src
, const ITEMIDLIST
*pidl
, LPWSTR dest
, UINT len
)
247 TRACE("dest=%p len=0x%x strret=%p pidl=%p\n", dest
, len
, src
, pidl
);
254 WARN("Invalid lpStrRet would crash under Win32!\n");
261 switch (src
->uType
) {
266 dst_len
= lstrlenW(src
->pOleStr
);
267 memcpy(dest
, src
->pOleStr
, min(dst_len
, len
-1) * sizeof(WCHAR
));
268 dest
[min(dst_len
, len
-1)] = 0;
269 CoTaskMemFree(src
->pOleStr
);
273 return E_NOT_SUFFICIENT_BUFFER
;
279 if (!MultiByteToWideChar( CP_ACP
, 0, src
->cStr
, -1, dest
, len
))
286 if (!MultiByteToWideChar( CP_ACP
, 0, ((LPCSTR
)&pidl
->mkid
)+src
->uOffset
, -1,
293 FIXME("unknown type!\n");
300 /*************************************************************************
301 * StrRetToStrA [SHLWAPI.@]
303 * Converts a STRRET to a normal string.
306 * lpStrRet [O] STRRET to convert
307 * pidl [I] ITEMIDLIST for lpStrRet->uType == STRRET_OFFSET
308 * ppszName [O] Destination for converted string
311 * Success: S_OK. ppszName contains the new string, allocated with CoTaskMemAlloc().
312 * Failure: E_FAIL, if any parameters are invalid.
314 HRESULT WINAPI
StrRetToStrA(LPSTRRET lpStrRet
, const ITEMIDLIST
*pidl
, LPSTR
*ppszName
)
316 HRESULT hRet
= E_FAIL
;
318 switch (lpStrRet
->uType
)
321 hRet
= _SHStrDupAW(lpStrRet
->pOleStr
, ppszName
);
322 CoTaskMemFree(lpStrRet
->pOleStr
);
326 hRet
= _SHStrDupAA(lpStrRet
->cStr
, ppszName
);
330 hRet
= _SHStrDupAA(((LPCSTR
)&pidl
->mkid
) + lpStrRet
->uOffset
, ppszName
);
340 /*************************************************************************
341 * StrRetToStrW [SHLWAPI.@]
345 HRESULT WINAPI
StrRetToStrW(LPSTRRET lpStrRet
, const ITEMIDLIST
*pidl
, LPWSTR
*ppszName
)
347 HRESULT hRet
= E_FAIL
;
349 switch (lpStrRet
->uType
)
352 hRet
= SHStrDupW(lpStrRet
->pOleStr
, ppszName
);
353 CoTaskMemFree(lpStrRet
->pOleStr
);
357 hRet
= SHStrDupA(lpStrRet
->cStr
, ppszName
);
361 hRet
= SHStrDupA(((LPCSTR
)&pidl
->mkid
) + lpStrRet
->uOffset
, ppszName
);
371 /* Makes a Unicode copy of an ANSI string using SysAllocString() */
372 static HRESULT
_SHStrDupAToBSTR(LPCSTR src
, BSTR
*pBstrOut
)
378 INT len
= MultiByteToWideChar(CP_ACP
, 0, src
, -1, NULL
, 0);
379 WCHAR
*szTemp
= malloc(len
* sizeof(WCHAR
));
383 MultiByteToWideChar(CP_ACP
, 0, src
, -1, szTemp
, len
);
384 *pBstrOut
= SysAllocString(szTemp
);
391 return E_OUTOFMEMORY
;
394 /*************************************************************************
395 * StrRetToBSTR [SHLWAPI.@]
397 * Converts a STRRET to a BSTR.
400 * lpStrRet [O] STRRET to convert
401 * pidl [I] ITEMIDLIST for lpStrRet->uType = STRRET_OFFSET
402 * pBstrOut [O] Destination for converted BSTR
405 * Success: S_OK. pBstrOut contains the new string.
406 * Failure: E_FAIL, if any parameters are invalid.
408 HRESULT WINAPI
StrRetToBSTR(STRRET
*lpStrRet
, LPCITEMIDLIST pidl
, BSTR
* pBstrOut
)
410 HRESULT hRet
= E_FAIL
;
412 switch (lpStrRet
->uType
)
415 *pBstrOut
= SysAllocString(lpStrRet
->pOleStr
);
418 CoTaskMemFree(lpStrRet
->pOleStr
);
422 hRet
= _SHStrDupAToBSTR(lpStrRet
->cStr
, pBstrOut
);
426 hRet
= _SHStrDupAToBSTR(((LPCSTR
)&pidl
->mkid
) + lpStrRet
->uOffset
, pBstrOut
);
436 /*************************************************************************
437 * StrFormatKBSizeA [SHLWAPI.@]
439 * Create a formatted string containing a byte count in Kilobytes.
442 * llBytes [I] Byte size to format
443 * lpszDest [I] Destination for formatted string
444 * cchMax [I] Size of lpszDest
449 LPSTR WINAPI
StrFormatKBSizeA(LONGLONG llBytes
, LPSTR lpszDest
, UINT cchMax
)
453 if (!StrFormatKBSizeW(llBytes
, wszBuf
, 256))
455 if (!WideCharToMultiByte(CP_ACP
, 0, wszBuf
, -1, lpszDest
, cchMax
, NULL
, NULL
))
460 /*************************************************************************
461 * StrFormatKBSizeW [SHLWAPI.@]
463 * See StrFormatKBSizeA.
465 LPWSTR WINAPI
StrFormatKBSizeW(LONGLONG llBytes
, LPWSTR lpszDest
, UINT cchMax
)
467 static const WCHAR kb
[] = {' ','K','B',0};
468 LONGLONG llKB
= (llBytes
+ 1023) >> 10;
471 TRACE("(0x%s,%p,%d)\n", wine_dbgstr_longlong(llBytes
), lpszDest
, cchMax
);
473 if (!FormatInt(llKB
, lpszDest
, cchMax
))
476 len
= lstrlenW(lpszDest
);
477 if (cchMax
- len
< 4)
479 lstrcatW(lpszDest
, kb
);
483 /*************************************************************************
484 * StrNCatA [SHLWAPI.@]
486 * Concatenate two strings together.
489 * lpszStr [O] String to concatenate to
490 * lpszCat [I] String to add to lpszCat
491 * cchMax [I] Maximum number of characters to concatenate
497 * cchMax determines the number of characters that are appended to lpszStr,
498 * not the total length of the string.
500 LPSTR WINAPI
StrNCatA(LPSTR lpszStr
, LPCSTR lpszCat
, INT cchMax
)
502 LPSTR lpszRet
= lpszStr
;
504 TRACE("(%s,%s,%i)\n", debugstr_a(lpszStr
), debugstr_a(lpszCat
), cchMax
);
508 WARN("Invalid lpszStr would crash under Win32!\n");
512 StrCpyNA(lpszStr
+ strlen(lpszStr
), lpszCat
, cchMax
);
516 /*************************************************************************
517 * StrNCatW [SHLWAPI.@]
521 LPWSTR WINAPI
StrNCatW(LPWSTR lpszStr
, LPCWSTR lpszCat
, INT cchMax
)
523 LPWSTR lpszRet
= lpszStr
;
525 TRACE("(%s,%s,%i)\n", debugstr_w(lpszStr
), debugstr_w(lpszCat
), cchMax
);
529 WARN("Invalid lpszStr would crash under Win32\n");
533 StrCpyNW(lpszStr
+ lstrlenW(lpszStr
), lpszCat
, cchMax
);
537 /*************************************************************************
538 * _SHStrDupAA [INTERNAL]
540 * Duplicates a ANSI string to ANSI. The destination buffer is allocated.
542 static HRESULT
_SHStrDupAA(LPCSTR src
, LPSTR
* dest
)
548 len
= lstrlenA(src
) + 1;
549 *dest
= CoTaskMemAlloc(len
);
555 lstrcpynA(*dest
,src
, len
);
561 TRACE("%s->(%p)\n", debugstr_a(src
), *dest
);
565 /*************************************************************************
566 * SHStrDupA [SHLWAPI.@]
568 * Return a Unicode copy of a string, in memory allocated by CoTaskMemAlloc().
571 * lpszStr [I] String to copy
572 * lppszDest [O] Destination for the new string copy
575 * Success: S_OK. lppszDest contains the new string in Unicode format.
576 * Failure: E_OUTOFMEMORY, If any arguments are invalid or memory allocation
579 HRESULT WINAPI
SHStrDupA(LPCSTR lpszStr
, LPWSTR
* lppszDest
)
586 len
= MultiByteToWideChar(CP_ACP
, 0, lpszStr
, -1, NULL
, 0) * sizeof(WCHAR
);
587 *lppszDest
= CoTaskMemAlloc(len
);
594 MultiByteToWideChar(CP_ACP
, 0, lpszStr
, -1, *lppszDest
, len
/sizeof(WCHAR
));
598 hRet
= E_OUTOFMEMORY
;
600 TRACE("%s->(%p)\n", debugstr_a(lpszStr
), *lppszDest
);
604 /*************************************************************************
605 * _SHStrDupAW [INTERNAL]
607 * Duplicates a UNICODE to a ANSI string. The destination buffer is allocated.
609 static HRESULT
_SHStrDupAW(LPCWSTR src
, LPSTR
* dest
)
615 len
= WideCharToMultiByte(CP_ACP
, 0, src
, -1, NULL
, 0, NULL
, NULL
);
616 *dest
= CoTaskMemAlloc(len
);
622 WideCharToMultiByte(CP_ACP
, 0, src
, -1, *dest
, len
, NULL
, NULL
);
628 TRACE("%s->(%p)\n", debugstr_w(src
), *dest
);
632 /*************************************************************************
633 * SHStrDupW [SHLWAPI.@]
637 HRESULT WINAPI
SHStrDupW(LPCWSTR src
, LPWSTR
* dest
)
643 len
= (lstrlenW(src
) + 1) * sizeof(WCHAR
);
644 *dest
= CoTaskMemAlloc(len
);
650 memcpy(*dest
, src
, len
);
656 TRACE("%s->(%p)\n", debugstr_w(src
), *dest
);
660 /*************************************************************************
661 * SHLWAPI_WriteReverseNum
663 * Internal helper for SHLWAPI_WriteTimeClass.
665 static inline LPWSTR
SHLWAPI_WriteReverseNum(LPWSTR lpszOut
, DWORD dwNum
)
669 /* Write a decimal number to a string, backwards */
672 DWORD dwNextDigit
= dwNum
% 10;
673 *lpszOut
-- = '0' + dwNextDigit
;
674 dwNum
= (dwNum
- dwNextDigit
) / 10;
680 /*************************************************************************
681 * SHLWAPI_FormatSignificant
683 * Internal helper for SHLWAPI_WriteTimeClass.
685 static inline int SHLWAPI_FormatSignificant(LPWSTR lpszNum
, int dwDigits
)
687 /* Zero non significant digits, return remaining significant digits */
701 /*************************************************************************
702 * SHLWAPI_WriteTimeClass
704 * Internal helper for StrFromTimeIntervalW.
706 static int SHLWAPI_WriteTimeClass(LPWSTR lpszOut
, DWORD dwValue
,
707 UINT uClassStringId
, int iDigits
)
709 WCHAR szBuff
[64], *szOut
= szBuff
+ 32;
711 szOut
= SHLWAPI_WriteReverseNum(szOut
, dwValue
);
712 iDigits
= SHLWAPI_FormatSignificant(szOut
+ 1, iDigits
);
714 LoadStringW(shlwapi_hInstance
, uClassStringId
, szBuff
+ 32, 32);
715 lstrcatW(lpszOut
, szOut
);
719 /*************************************************************************
720 * StrFromTimeIntervalA [SHLWAPI.@]
722 * Format a millisecond time interval into a string
725 * lpszStr [O] Output buffer for formatted time interval
726 * cchMax [I] Size of lpszStr
727 * dwMS [I] Number of milliseconds
728 * iDigits [I] Number of digits to print
731 * The length of the formatted string, or 0 if any parameter is invalid.
734 * This implementation mimics the Win32 behaviour of always writing a leading
735 * space before the time interval begins.
737 * iDigits is used to provide approximate times if accuracy is not important.
738 * This number of digits will be written of the first non-zero time class
739 * (hours/minutes/seconds). If this does not complete the time classification,
740 * the remaining digits are changed to zeros (i.e. The time is _not_ rounded).
741 * If there are digits remaining following the writing of a time class, the
742 * next time class will be written.
744 * For example, given dwMS represents 138 hours,43 minutes and 15 seconds, the
745 * following will result from the given values of iDigits:
747 *| iDigits 1 2 3 4 5 ...
748 *| lpszStr "100 hr" "130 hr" "138 hr" "138 hr 40 min" "138 hr 43 min" ...
750 INT WINAPI
StrFromTimeIntervalA(LPSTR lpszStr
, UINT cchMax
, DWORD dwMS
,
755 TRACE("(%p,%d,%ld,%d)\n", lpszStr
, cchMax
, dwMS
, iDigits
);
757 if (lpszStr
&& cchMax
)
760 StrFromTimeIntervalW(szBuff
, ARRAY_SIZE(szBuff
), dwMS
, iDigits
);
761 WideCharToMultiByte(CP_ACP
,0,szBuff
,-1,lpszStr
,cchMax
,0,0);
767 /*************************************************************************
768 * StrFromTimeIntervalW [SHLWAPI.@]
770 * See StrFromTimeIntervalA.
772 INT WINAPI
StrFromTimeIntervalW(LPWSTR lpszStr
, UINT cchMax
, DWORD dwMS
,
777 TRACE("(%p,%d,%ld,%d)\n", lpszStr
, cchMax
, dwMS
, iDigits
);
779 if (lpszStr
&& cchMax
)
782 DWORD dwHours
, dwMinutes
;
784 if (!iDigits
|| cchMax
== 1)
790 /* Calculate the time classes */
791 dwMS
= (dwMS
+ 500) / 1000;
792 dwHours
= dwMS
/ 3600;
793 dwMS
-= dwHours
* 3600;
794 dwMinutes
= dwMS
/ 60;
795 dwMS
-= dwMinutes
* 60;
800 iDigits
= SHLWAPI_WriteTimeClass(szCopy
, dwHours
, IDS_TIME_INTERVAL_HOURS
, iDigits
);
802 if (dwMinutes
&& iDigits
)
803 iDigits
= SHLWAPI_WriteTimeClass(szCopy
, dwMinutes
, IDS_TIME_INTERVAL_MINUTES
, iDigits
);
805 if (iDigits
) /* Always write seconds if we have significant digits */
806 SHLWAPI_WriteTimeClass(szCopy
, dwMS
, IDS_TIME_INTERVAL_SECONDS
, iDigits
);
808 lstrcpynW(lpszStr
, szCopy
, cchMax
);
809 iRet
= lstrlenW(lpszStr
);
814 /* Structure for formatting byte strings */
815 typedef struct tagSHLWAPI_BYTEFORMATS
822 } SHLWAPI_BYTEFORMATS
;
824 /*************************************************************************
825 * StrFormatByteSizeW [SHLWAPI.@]
827 * Create a string containing an abbreviated byte count of up to 2^63-1.
830 * llBytes [I] Byte size to format
831 * lpszDest [I] Destination for formatted string
832 * cchMax [I] Size of lpszDest
838 * There is no StrFormatByteSize64W function, it is called StrFormatByteSizeW().
840 LPWSTR WINAPI
StrFormatByteSizeW(LONGLONG llBytes
, LPWSTR lpszDest
, UINT cchMax
)
844 TRACE("(0x%s,%p,%d)\n", wine_dbgstr_longlong(llBytes
), lpszDest
, cchMax
);
846 if (!lpszDest
|| !cchMax
)
849 hr
= StrFormatByteSizeEx(llBytes
, SFBS_FLAGS_TRUNCATE_UNDISPLAYED_DECIMAL_DIGITS
,
858 /*************************************************************************
859 * StrFormatByteSizeEx [SHLWAPI.@]
863 HRESULT WINAPI
StrFormatByteSizeEx(LONGLONG llBytes
, SFBS_FLAGS flags
, LPWSTR lpszDest
,
866 #define KB ((ULONGLONG)1024)
868 #define GB (KB*KB*KB)
869 #define TB (KB*KB*KB*KB)
870 #define PB (KB*KB*KB*KB*KB)
872 static const SHLWAPI_BYTEFORMATS bfFormats
[] =
874 { 10*KB
, 10.24, 100.0, 2, 'K' }, /* 10 KB */
875 { 100*KB
, 102.4, 10.0, 1, 'K' }, /* 100 KB */
876 { 1000*KB
, 1024.0, 1.0, 0, 'K' }, /* 1000 KB */
877 { 10*MB
, 10485.76, 100.0, 2, 'M' }, /* 10 MB */
878 { 100*MB
, 104857.6, 10.0, 1, 'M' }, /* 100 MB */
879 { 1000*MB
, 1048576.0, 1.0, 0, 'M' }, /* 1000 MB */
880 { 10*GB
, 10737418.24, 100.0, 2, 'G' }, /* 10 GB */
881 { 100*GB
, 107374182.4, 10.0, 1, 'G' }, /* 100 GB */
882 { 1000*GB
, 1073741824.0, 1.0, 0, 'G' }, /* 1000 GB */
883 { 10*TB
, 10485.76, 100.0, 2, 'T' }, /* 10 TB */
884 { 100*TB
, 104857.6, 10.0, 1, 'T' }, /* 100 TB */
885 { 1000*TB
, 1048576.0, 1.0, 0, 'T' }, /* 1000 TB */
886 { 10*PB
, 10737418.24, 100.00, 2, 'P' }, /* 10 PB */
887 { 100*PB
, 107374182.4, 10.00, 1, 'P' }, /* 100 PB */
888 { 1000*PB
, 1073741824.0, 1.00, 0, 'P' }, /* 1000 PB */
889 { 0, 10995116277.76, 100.00, 2, 'E' } /* EB's, catch all */
891 WCHAR wszAdd
[] = {' ','?','B',0};
895 TRACE("(0x%s,%d,%p,%d)\n", wine_dbgstr_longlong(llBytes
), flags
, lpszDest
, cchMax
);
900 if (llBytes
< 1024) /* 1K */
902 WCHAR wszBytesFormat
[64];
903 LoadStringW(shlwapi_hInstance
, IDS_BYTES_FORMAT
, wszBytesFormat
, 64);
904 swprintf(lpszDest
, cchMax
, wszBytesFormat
, (int)llBytes
);
908 /* Note that if this loop completes without finding a match, i will be
909 * pointing at the last entry, which is a catch all for > 1000 PB
911 while (i
< ARRAY_SIZE(bfFormats
) - 1)
913 if (llBytes
< bfFormats
[i
].dLimit
)
917 /* Above 1 TB we encounter problems with FP accuracy. So for amounts above
918 * this number we integer shift down by 1 MB first. The table above has
919 * the divisors scaled down from the '< 10 TB' entry onwards, to account
920 * for this. We also add a small fudge factor to get the correct result for
921 * counts that lie exactly on a 1024 byte boundary.
924 dBytes
= (double)(llBytes
>> 20) + 0.001; /* Scale down by 1 MB */
926 dBytes
= (double)llBytes
+ 0.00001;
930 case SFBS_FLAGS_ROUND_TO_NEAREST_DISPLAYED_DIGIT
:
931 dBytes
= round(dBytes
/ bfFormats
[i
].dDivisor
) / bfFormats
[i
].dNormaliser
;
933 case SFBS_FLAGS_TRUNCATE_UNDISPLAYED_DECIMAL_DIGITS
:
934 dBytes
= floor(dBytes
/ bfFormats
[i
].dDivisor
) / bfFormats
[i
].dNormaliser
;
940 if (!FormatDouble(dBytes
, bfFormats
[i
].nDecimals
, lpszDest
, cchMax
))
943 wszAdd
[1] = bfFormats
[i
].wPrefix
;
944 StrCatBuffW(lpszDest
, wszAdd
, cchMax
);
948 /*************************************************************************
949 * StrFormatByteSize64A [SHLWAPI.@]
951 * See StrFormatByteSizeW.
953 LPSTR WINAPI
StrFormatByteSize64A(LONGLONG llBytes
, LPSTR lpszDest
, UINT cchMax
)
957 StrFormatByteSizeW(llBytes
, wszBuff
, ARRAY_SIZE(wszBuff
));
960 WideCharToMultiByte(CP_ACP
, 0, wszBuff
, -1, lpszDest
, cchMax
, 0, 0);
964 /*************************************************************************
965 * StrFormatByteSizeA [SHLWAPI.@]
967 * Create a string containing an abbreviated byte count of up to 2^31-1.
970 * dwBytes [I] Byte size to format
971 * lpszDest [I] Destination for formatted string
972 * cchMax [I] Size of lpszDest
978 * The ANSI and Unicode versions of this function accept a different
979 * integer type for dwBytes. See StrFormatByteSize64A().
981 LPSTR WINAPI
StrFormatByteSizeA(DWORD dwBytes
, LPSTR lpszDest
, UINT cchMax
)
983 TRACE("(%ld,%p,%d)\n", dwBytes
, lpszDest
, cchMax
);
985 return StrFormatByteSize64A(dwBytes
, lpszDest
, cchMax
);
988 /*************************************************************************
991 * Remove a single non-trailing ampersand ('&') from a string.
994 * lpszStr [I/O] String to remove ampersand from.
997 * The character after the first ampersand in lpszStr, or the first character
998 * in lpszStr if there is no ampersand in the string.
1000 char WINAPI
SHStripMneumonicA(LPCSTR lpszStr
)
1002 LPSTR lpszIter
, lpszTmp
;
1005 TRACE("(%s)\n", debugstr_a(lpszStr
));
1009 if ((lpszIter
= StrChrA(lpszStr
, '&')))
1011 lpszTmp
= CharNextA(lpszIter
);
1014 if (*lpszTmp
!= '&')
1017 memmove( lpszIter
, lpszTmp
, strlen(lpszTmp
) + 1 );
1024 /*************************************************************************
1027 * Unicode version of SHStripMneumonicA.
1029 WCHAR WINAPI
SHStripMneumonicW(LPCWSTR lpszStr
)
1031 LPWSTR lpszIter
, lpszTmp
;
1034 TRACE("(%s)\n", debugstr_w(lpszStr
));
1038 if ((lpszIter
= StrChrW(lpszStr
, '&')))
1040 lpszTmp
= lpszIter
+ 1;
1043 if (*lpszTmp
!= '&')
1046 memmove( lpszIter
, lpszTmp
, (lstrlenW(lpszTmp
) + 1) * sizeof(WCHAR
) );
1053 /*************************************************************************
1056 * Convert an ANSI string to Unicode.
1059 * dwCp [I] Code page for the conversion
1060 * lpSrcStr [I] Source ANSI string to convert
1061 * lpDstStr [O] Destination for converted Unicode string
1062 * iLen [I] Length of lpDstStr
1065 * The return value of the MultiByteToWideChar() function called on lpSrcStr.
1067 DWORD WINAPI
SHAnsiToUnicodeCP(DWORD dwCp
, LPCSTR lpSrcStr
, LPWSTR lpDstStr
, int iLen
)
1071 dwRet
= MultiByteToWideChar(dwCp
, 0, lpSrcStr
, -1, lpDstStr
, iLen
);
1072 TRACE("%s->%s,ret=%ld\n", debugstr_a(lpSrcStr
), debugstr_w(lpDstStr
), dwRet
);
1076 /*************************************************************************
1079 * Convert an ANSI string to Unicode.
1082 * lpSrcStr [I] Source ANSI string to convert
1083 * lpDstStr [O] Destination for converted Unicode string
1084 * iLen [I] Length of lpDstStr
1087 * The return value of the MultiByteToWideChar() function called on lpSrcStr.
1090 * This function simply calls SHAnsiToUnicodeCP with code page CP_ACP.
1092 DWORD WINAPI
SHAnsiToUnicode(LPCSTR lpSrcStr
, LPWSTR lpDstStr
, int iLen
)
1094 return SHAnsiToUnicodeCP(CP_ACP
, lpSrcStr
, lpDstStr
, iLen
);
1097 /*************************************************************************
1100 * Convert a Unicode string to ANSI.
1103 * CodePage [I] Code page to use for the conversion
1104 * lpSrcStr [I] Source Unicode string to convert
1105 * lpDstStr [O] Destination for converted ANSI string
1106 * dstlen [I] Length of buffer at lpDstStr
1109 * Success: The length in bytes of the result at lpDstStr (including the terminator)
1110 * Failure: When using CP_UTF8, CP_UTF7 or 0xc350 as codePage, 0 is returned and
1111 * the result is not nul-terminated.
1112 * When using a different codepage, the length in bytes of the truncated
1113 * result at lpDstStr (including the terminator) is returned and
1114 * lpDstStr is always nul-terminated.
1117 DWORD WINAPI
SHUnicodeToAnsiCP(UINT CodePage
, LPCWSTR lpSrcStr
, LPSTR lpDstStr
, int dstlen
)
1119 static const WCHAR emptyW
[] = { '\0' };
1123 if (!lpDstStr
|| !dstlen
)
1131 len
= lstrlenW(lpSrcStr
) + 1;
1136 CodePage
= CP_UTF8
; /* Fall through... */
1137 case 0x0000C350: /* FIXME: CP_ #define */
1143 INT needed
= dstlen
- 1;
1146 /* try the user supplied buffer first */
1147 hr
= ConvertINetUnicodeToMultiByte(&dwMode
, CodePage
, lpSrcStr
, &lenW
, lpDstStr
, &needed
);
1150 lpDstStr
[needed
] = '\0';
1154 /* user buffer too small. exclude termination and copy as much as possible */
1156 hr
= ConvertINetUnicodeToMultiByte(&dwMode
, CodePage
, lpSrcStr
, &lenW
, NULL
, &needed
);
1158 mem
= malloc(needed
);
1162 hr
= ConvertINetUnicodeToMultiByte(&dwMode
, CodePage
, lpSrcStr
, &len
, mem
, &needed
);
1165 reqLen
= SHTruncateString(mem
, dstlen
);
1166 if (reqLen
> 0) memcpy(lpDstStr
, mem
, reqLen
-1);
1175 /* try the user supplied buffer first */
1176 reqLen
= WideCharToMultiByte(CodePage
, 0, lpSrcStr
, len
, lpDstStr
, dstlen
, NULL
, NULL
);
1178 if (!reqLen
&& GetLastError() == ERROR_INSUFFICIENT_BUFFER
)
1180 reqLen
= WideCharToMultiByte(CodePage
, 0, lpSrcStr
, len
, NULL
, 0, NULL
, NULL
);
1183 mem
= malloc(reqLen
);
1186 WideCharToMultiByte(CodePage
, 0, lpSrcStr
, len
, mem
, reqLen
, NULL
, NULL
);
1188 reqLen
= SHTruncateString(mem
, dstlen
-1);
1191 lstrcpynA(lpDstStr
, mem
, reqLen
);
1193 lpDstStr
[reqLen
-1] = '\0';
1200 /*************************************************************************
1203 * Convert a Unicode string to ANSI.
1206 * lpSrcStr [I] Source Unicode string to convert
1207 * lpDstStr [O] Destination for converted ANSI string
1208 * iLen [O] Length of lpDstStr in characters
1211 * See SHUnicodeToAnsiCP
1214 * This function simply calls SHUnicodeToAnsiCP() with CodePage = CP_ACP.
1216 INT WINAPI
SHUnicodeToAnsi(LPCWSTR lpSrcStr
, LPSTR lpDstStr
, INT iLen
)
1218 return SHUnicodeToAnsiCP(CP_ACP
, lpSrcStr
, lpDstStr
, iLen
);
1221 /*************************************************************************
1224 * Determine if an ANSI string converts to Unicode and back identically.
1227 * lpSrcStr [I] Source Unicode string to convert
1228 * lpDst [O] Destination for resulting ANSI string
1229 * iLen [I] Length of lpDst in characters
1232 * TRUE, since ANSI strings always convert identically.
1234 BOOL WINAPI
DoesStringRoundTripA(LPCSTR lpSrcStr
, LPSTR lpDst
, INT iLen
)
1236 lstrcpynA(lpDst
, lpSrcStr
, iLen
);
1240 /*************************************************************************
1243 * Determine if a Unicode string converts to ANSI and back identically.
1246 * lpSrcStr [I] Source Unicode string to convert
1247 * lpDst [O] Destination for resulting ANSI string
1248 * iLen [I] Length of lpDst in characters
1251 * TRUE, if lpSrcStr converts to ANSI and back identically,
1254 BOOL WINAPI
DoesStringRoundTripW(LPCWSTR lpSrcStr
, LPSTR lpDst
, INT iLen
)
1256 WCHAR szBuff
[MAX_PATH
];
1258 SHUnicodeToAnsi(lpSrcStr
, lpDst
, iLen
);
1259 SHAnsiToUnicode(lpDst
, szBuff
, MAX_PATH
);
1260 return !wcscmp(lpSrcStr
, szBuff
);