ntdll: Use a separate memory allocation for the kernel stack.
[wine.git] / dlls / shlwapi / string.c
blob77e3b978cee7ddbe941ff54112586ebe19328df5
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 <math.h>
23 #include <stdarg.h>
24 #include <stdio.h>
25 #include <string.h>
27 #define NONAMELESSUNION
29 #include "windef.h"
30 #include "winbase.h"
31 #define NO_SHLWAPI_REG
32 #define NO_SHLWAPI_STREAM
33 #include "shlwapi.h"
34 #include "wingdi.h"
35 #include "winuser.h"
36 #include "shlobj.h"
37 #include "mlang.h"
38 #include "ddeml.h"
39 #include "wine/debug.h"
41 #include "resource.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)
55 WCHAR grouping[64];
56 WCHAR *c;
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->NegativeOrder, sizeof(fmt->NegativeOrder)/sizeof(WCHAR));
60 fmt->NumDigits = 0;
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;
66 /*
67 * Converting grouping string to number as described on
68 * http://blogs.msdn.com/oldnewthing/archive/2006/04/18/578251.aspx
70 fmt->Grouping = 0;
71 GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SGROUPING, grouping, ARRAY_SIZE(grouping));
72 for (c = grouping; *c; c++)
73 if (*c >= '0' && *c < '9')
75 fmt->Grouping *= 10;
76 fmt->Grouping += *c - '0';
79 if (fmt->Grouping % 10 == 0)
80 fmt->Grouping /= 10;
81 else
82 fmt->Grouping *= 10;
85 /*************************************************************************
86 * FormatInt [internal]
88 * Format an integer according to the current locale
90 * RETURNS
91 * The number of characters written on success or 0 on failure
93 static int FormatInt(LONGLONG qdwValue, LPWSTR pszBuf, int cchBuf)
95 NUMBERFMTW fmt;
96 WCHAR decimal[8], thousand[8];
97 WCHAR buf[24];
98 WCHAR *c;
99 BOOL neg = (qdwValue < 0);
101 FillNumberFmt(&fmt, decimal, ARRAY_SIZE(decimal), thousand, ARRAY_SIZE(thousand));
103 c = &buf[24];
104 *(--c) = 0;
107 *(--c) = '0' + (qdwValue%10);
108 qdwValue /= 10;
109 } while (qdwValue > 0);
110 if (neg)
111 *(--c) = '-';
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
122 * RETURNS
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};
128 WCHAR buf[64];
129 NUMBERFMTW fmt;
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.
144 * PARAMS
145 * lpszStr [O] Initial string
146 * lpszSrc [I] String to concatenate
148 * RETURNS
149 * lpszStr.
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);
157 return lpszStr;
160 /*************************************************************************
161 * StrCpyW [SHLWAPI.@]
163 * Copy a string to another string.
165 * PARAMS
166 * lpszStr [O] Destination string
167 * lpszSrc [I] Source string
169 * RETURNS
170 * lpszStr.
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);
178 return lpszStr;
181 /*************************************************************************
182 * StrRetToBufA [SHLWAPI.@]
184 * Convert a STRRET to a normal string.
186 * PARAMS
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
192 * RETURNS
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)
200 /* NOTE:
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);
207 if (!src)
209 WARN("Invalid lpStrRet would crash under Win32!\n");
210 if (dest)
211 *dest = '\0';
212 return E_FAIL;
215 if (!dest || !len)
216 return E_FAIL;
218 *dest = '\0';
220 switch (src->uType)
222 case STRRET_WSTR:
223 WideCharToMultiByte(CP_ACP, 0, src->u.pOleStr, -1, dest, len, NULL, NULL);
224 CoTaskMemFree(src->u.pOleStr);
225 break;
227 case STRRET_CSTR:
228 lstrcpynA(dest, src->u.cStr, len);
229 break;
231 case STRRET_OFFSET:
232 lstrcpynA(dest, ((LPCSTR)&pidl->mkid)+src->u.uOffset, len);
233 break;
235 default:
236 FIXME("unknown type!\n");
237 return E_NOTIMPL;
239 return S_OK;
242 /*************************************************************************
243 * StrRetToBufW [SHLWAPI.@]
245 * See StrRetToBufA.
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);
251 if (!dest || !len)
252 return E_FAIL;
254 if (!src)
256 WARN("Invalid lpStrRet would crash under Win32!\n");
257 *dest = '\0';
258 return E_FAIL;
261 *dest = '\0';
263 switch (src->uType) {
264 case STRRET_WSTR: {
265 size_t dst_len;
266 if (!src->u.pOleStr)
267 return E_FAIL;
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);
272 if (len <= dst_len)
274 dest[0] = 0;
275 return E_NOT_SUFFICIENT_BUFFER;
277 break;
280 case STRRET_CSTR:
281 if (!MultiByteToWideChar( CP_ACP, 0, src->u.cStr, -1, dest, len ))
282 dest[len-1] = 0;
283 break;
285 case STRRET_OFFSET:
286 if (pidl)
288 if (!MultiByteToWideChar( CP_ACP, 0, ((LPCSTR)&pidl->mkid)+src->u.uOffset, -1,
289 dest, len ))
290 dest[len-1] = 0;
292 break;
294 default:
295 FIXME("unknown type!\n");
296 return E_NOTIMPL;
299 return S_OK;
302 /*************************************************************************
303 * StrRetToStrA [SHLWAPI.@]
305 * Converts a STRRET to a normal string.
307 * PARAMS
308 * lpStrRet [O] STRRET to convert
309 * pidl [I] ITEMIDLIST for lpStrRet->uType == STRRET_OFFSET
310 * ppszName [O] Destination for converted string
312 * RETURNS
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)
322 case STRRET_WSTR:
323 hRet = _SHStrDupAW(lpStrRet->u.pOleStr, ppszName);
324 CoTaskMemFree(lpStrRet->u.pOleStr);
325 break;
327 case STRRET_CSTR:
328 hRet = _SHStrDupAA(lpStrRet->u.cStr, ppszName);
329 break;
331 case STRRET_OFFSET:
332 hRet = _SHStrDupAA(((LPCSTR)&pidl->mkid) + lpStrRet->u.uOffset, ppszName);
333 break;
335 default:
336 *ppszName = NULL;
339 return hRet;
342 /*************************************************************************
343 * StrRetToStrW [SHLWAPI.@]
345 * See StrRetToStrA.
347 HRESULT WINAPI StrRetToStrW(LPSTRRET lpStrRet, const ITEMIDLIST *pidl, LPWSTR *ppszName)
349 HRESULT hRet = E_FAIL;
351 switch (lpStrRet->uType)
353 case STRRET_WSTR:
354 hRet = SHStrDupW(lpStrRet->u.pOleStr, ppszName);
355 CoTaskMemFree(lpStrRet->u.pOleStr);
356 break;
358 case STRRET_CSTR:
359 hRet = SHStrDupA(lpStrRet->u.cStr, ppszName);
360 break;
362 case STRRET_OFFSET:
363 hRet = SHStrDupA(((LPCSTR)&pidl->mkid) + lpStrRet->u.uOffset, ppszName);
364 break;
366 default:
367 *ppszName = NULL;
370 return hRet;
373 /* Makes a Unicode copy of an ANSI string using SysAllocString() */
374 static HRESULT _SHStrDupAToBSTR(LPCSTR src, BSTR *pBstrOut)
376 *pBstrOut = NULL;
378 if (src)
380 INT len = MultiByteToWideChar(CP_ACP, 0, src, -1, NULL, 0);
381 WCHAR* szTemp = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
383 if (szTemp)
385 MultiByteToWideChar(CP_ACP, 0, src, -1, szTemp, len);
386 *pBstrOut = SysAllocString(szTemp);
387 HeapFree(GetProcessHeap(), 0, szTemp);
389 if (*pBstrOut)
390 return S_OK;
393 return E_OUTOFMEMORY;
396 /*************************************************************************
397 * StrRetToBSTR [SHLWAPI.@]
399 * Converts a STRRET to a BSTR.
401 * PARAMS
402 * lpStrRet [O] STRRET to convert
403 * pidl [I] ITEMIDLIST for lpStrRet->uType = STRRET_OFFSET
404 * pBstrOut [O] Destination for converted BSTR
406 * RETURNS
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)
416 case STRRET_WSTR:
417 *pBstrOut = SysAllocString(lpStrRet->u.pOleStr);
418 if (*pBstrOut)
419 hRet = S_OK;
420 CoTaskMemFree(lpStrRet->u.pOleStr);
421 break;
423 case STRRET_CSTR:
424 hRet = _SHStrDupAToBSTR(lpStrRet->u.cStr, pBstrOut);
425 break;
427 case STRRET_OFFSET:
428 hRet = _SHStrDupAToBSTR(((LPCSTR)&pidl->mkid) + lpStrRet->u.uOffset, pBstrOut);
429 break;
431 default:
432 *pBstrOut = NULL;
435 return hRet;
438 /*************************************************************************
439 * StrFormatKBSizeA [SHLWAPI.@]
441 * Create a formatted string containing a byte count in Kilobytes.
443 * PARAMS
444 * llBytes [I] Byte size to format
445 * lpszDest [I] Destination for formatted string
446 * cchMax [I] Size of lpszDest
448 * RETURNS
449 * lpszDest.
451 LPSTR WINAPI StrFormatKBSizeA(LONGLONG llBytes, LPSTR lpszDest, UINT cchMax)
453 WCHAR wszBuf[256];
455 if (!StrFormatKBSizeW(llBytes, wszBuf, 256))
456 return NULL;
457 if (!WideCharToMultiByte(CP_ACP, 0, wszBuf, -1, lpszDest, cchMax, NULL, NULL))
458 return NULL;
459 return lpszDest;
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;
471 int len;
473 TRACE("(0x%s,%p,%d)\n", wine_dbgstr_longlong(llBytes), lpszDest, cchMax);
475 if (!FormatInt(llKB, lpszDest, cchMax))
476 return NULL;
478 len = lstrlenW(lpszDest);
479 if (cchMax - len < 4)
480 return NULL;
481 lstrcatW(lpszDest, kb);
482 return lpszDest;
485 /*************************************************************************
486 * StrNCatA [SHLWAPI.@]
488 * Concatenate two strings together.
490 * PARAMS
491 * lpszStr [O] String to concatenate to
492 * lpszCat [I] String to add to lpszCat
493 * cchMax [I] Maximum number of characters to concatenate
495 * RETURNS
496 * lpszStr.
498 * NOTES
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);
508 if (!lpszStr)
510 WARN("Invalid lpszStr would crash under Win32!\n");
511 return NULL;
514 StrCpyNA(lpszStr + strlen(lpszStr), lpszCat, cchMax);
515 return lpszRet;
518 /*************************************************************************
519 * StrNCatW [SHLWAPI.@]
521 * See StrNCatA.
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);
529 if (!lpszStr)
531 WARN("Invalid lpszStr would crash under Win32\n");
532 return NULL;
535 StrCpyNW(lpszStr + lstrlenW(lpszStr), lpszCat, cchMax);
536 return lpszRet;
539 /*************************************************************************
540 * _SHStrDupAA [INTERNAL]
542 * Duplicates a ANSI string to ANSI. The destination buffer is allocated.
544 static HRESULT _SHStrDupAA(LPCSTR src, LPSTR * dest)
546 HRESULT hr;
547 int len = 0;
549 if (src) {
550 len = lstrlenA(src) + 1;
551 *dest = CoTaskMemAlloc(len);
552 } else {
553 *dest = NULL;
556 if (*dest) {
557 lstrcpynA(*dest,src, len);
558 hr = S_OK;
559 } else {
560 hr = E_OUTOFMEMORY;
563 TRACE("%s->(%p)\n", debugstr_a(src), *dest);
564 return hr;
567 /*************************************************************************
568 * SHStrDupA [SHLWAPI.@]
570 * Return a Unicode copy of a string, in memory allocated by CoTaskMemAlloc().
572 * PARAMS
573 * lpszStr [I] String to copy
574 * lppszDest [O] Destination for the new string copy
576 * RETURNS
577 * Success: S_OK. lppszDest contains the new string in Unicode format.
578 * Failure: E_OUTOFMEMORY, If any arguments are invalid or memory allocation
579 * fails.
581 HRESULT WINAPI SHStrDupA(LPCSTR lpszStr, LPWSTR * lppszDest)
583 HRESULT hRet;
584 int len = 0;
586 if (lpszStr)
588 len = MultiByteToWideChar(CP_ACP, 0, lpszStr, -1, NULL, 0) * sizeof(WCHAR);
589 *lppszDest = CoTaskMemAlloc(len);
591 else
592 *lppszDest = NULL;
594 if (*lppszDest)
596 MultiByteToWideChar(CP_ACP, 0, lpszStr, -1, *lppszDest, len/sizeof(WCHAR));
597 hRet = S_OK;
599 else
600 hRet = E_OUTOFMEMORY;
602 TRACE("%s->(%p)\n", debugstr_a(lpszStr), *lppszDest);
603 return hRet;
606 /*************************************************************************
607 * _SHStrDupAW [INTERNAL]
609 * Duplicates a UNICODE to a ANSI string. The destination buffer is allocated.
611 static HRESULT _SHStrDupAW(LPCWSTR src, LPSTR * dest)
613 HRESULT hr;
614 int len = 0;
616 if (src) {
617 len = WideCharToMultiByte(CP_ACP, 0, src, -1, NULL, 0, NULL, NULL);
618 *dest = CoTaskMemAlloc(len);
619 } else {
620 *dest = NULL;
623 if (*dest) {
624 WideCharToMultiByte(CP_ACP, 0, src, -1, *dest, len, NULL, NULL);
625 hr = S_OK;
626 } else {
627 hr = E_OUTOFMEMORY;
630 TRACE("%s->(%p)\n", debugstr_w(src), *dest);
631 return hr;
634 /*************************************************************************
635 * SHStrDupW [SHLWAPI.@]
637 * See SHStrDupA.
639 HRESULT WINAPI SHStrDupW(LPCWSTR src, LPWSTR * dest)
641 HRESULT hr;
642 int len = 0;
644 if (src) {
645 len = (lstrlenW(src) + 1) * sizeof(WCHAR);
646 *dest = CoTaskMemAlloc(len);
647 } else {
648 *dest = NULL;
651 if (*dest) {
652 memcpy(*dest, src, len);
653 hr = S_OK;
654 } else {
655 hr = E_OUTOFMEMORY;
658 TRACE("%s->(%p)\n", debugstr_w(src), *dest);
659 return hr;
662 /*************************************************************************
663 * SHLWAPI_WriteReverseNum
665 * Internal helper for SHLWAPI_WriteTimeClass.
667 static inline LPWSTR SHLWAPI_WriteReverseNum(LPWSTR lpszOut, DWORD dwNum)
669 *lpszOut-- = '\0';
671 /* Write a decimal number to a string, backwards */
674 DWORD dwNextDigit = dwNum % 10;
675 *lpszOut-- = '0' + dwNextDigit;
676 dwNum = (dwNum - dwNextDigit) / 10;
677 } while (dwNum > 0);
679 return lpszOut;
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 */
690 while (*lpszNum)
692 lpszNum++;
693 if (--dwDigits == 0)
695 while (*lpszNum)
696 *lpszNum++ = '0';
697 return 0;
700 return dwDigits;
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);
715 *szOut = ' ';
716 LoadStringW(shlwapi_hInstance, uClassStringId, szBuff + 32, 32);
717 lstrcatW(lpszOut, szOut);
718 return iDigits;
721 /*************************************************************************
722 * StrFromTimeIntervalA [SHLWAPI.@]
724 * Format a millisecond time interval into a string
726 * PARAMS
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
732 * RETURNS
733 * The length of the formatted string, or 0 if any parameter is invalid.
735 * NOTES
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,
753 int iDigits)
755 INT iRet = 0;
757 TRACE("(%p,%d,%ld,%d)\n", lpszStr, cchMax, dwMS, iDigits);
759 if (lpszStr && cchMax)
761 WCHAR szBuff[128];
762 StrFromTimeIntervalW(szBuff, ARRAY_SIZE(szBuff), dwMS, iDigits);
763 WideCharToMultiByte(CP_ACP,0,szBuff,-1,lpszStr,cchMax,0,0);
765 return iRet;
769 /*************************************************************************
770 * StrFromTimeIntervalW [SHLWAPI.@]
772 * See StrFromTimeIntervalA.
774 INT WINAPI StrFromTimeIntervalW(LPWSTR lpszStr, UINT cchMax, DWORD dwMS,
775 int iDigits)
777 INT iRet = 0;
779 TRACE("(%p,%d,%ld,%d)\n", lpszStr, cchMax, dwMS, iDigits);
781 if (lpszStr && cchMax)
783 WCHAR szCopy[128];
784 DWORD dwHours, dwMinutes;
786 if (!iDigits || cchMax == 1)
788 *lpszStr = '\0';
789 return 0;
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;
799 szCopy[0] = '\0';
801 if (dwHours)
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);
813 return iRet;
816 /* Structure for formatting byte strings */
817 typedef struct tagSHLWAPI_BYTEFORMATS
819 LONGLONG dLimit;
820 double dDivisor;
821 double dNormaliser;
822 int nDecimals;
823 WCHAR wPrefix;
824 } SHLWAPI_BYTEFORMATS;
826 /*************************************************************************
827 * StrFormatByteSizeW [SHLWAPI.@]
829 * Create a string containing an abbreviated byte count of up to 2^63-1.
831 * PARAMS
832 * llBytes [I] Byte size to format
833 * lpszDest [I] Destination for formatted string
834 * cchMax [I] Size of lpszDest
836 * RETURNS
837 * lpszDest.
839 * NOTES
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)
845 #define MB (KB*KB)
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};
870 double dBytes;
871 UINT i = 0;
873 TRACE("(0x%s,%p,%d)\n", wine_dbgstr_longlong(llBytes), lpszDest, cchMax);
875 if (!lpszDest || !cchMax)
876 return lpszDest;
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);
883 return lpszDest;
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)
892 break;
893 i++;
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.
901 if (i > 8)
902 dBytes = (double)(llBytes >> 20) + 0.001; /* Scale down by 1 MB */
903 else
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))
909 return NULL;
910 wszAdd[1] = bfFormats[i].wPrefix;
911 StrCatBuffW(lpszDest, wszAdd, cchMax);
912 return lpszDest;
915 /*************************************************************************
916 * StrFormatByteSize64A [SHLWAPI.@]
918 * See StrFormatByteSizeW.
920 LPSTR WINAPI StrFormatByteSize64A(LONGLONG llBytes, LPSTR lpszDest, UINT cchMax)
922 WCHAR wszBuff[32];
924 StrFormatByteSizeW(llBytes, wszBuff, ARRAY_SIZE(wszBuff));
926 if (lpszDest)
927 WideCharToMultiByte(CP_ACP, 0, wszBuff, -1, lpszDest, cchMax, 0, 0);
928 return lpszDest;
931 /*************************************************************************
932 * StrFormatByteSizeA [SHLWAPI.@]
934 * Create a string containing an abbreviated byte count of up to 2^31-1.
936 * PARAMS
937 * dwBytes [I] Byte size to format
938 * lpszDest [I] Destination for formatted string
939 * cchMax [I] Size of lpszDest
941 * RETURNS
942 * lpszDest.
944 * NOTES
945 * The ANSI 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("(%ld,%p,%d)\n", dwBytes, lpszDest, cchMax);
952 return StrFormatByteSize64A(dwBytes, lpszDest, cchMax);
955 /*************************************************************************
956 * @ [SHLWAPI.203]
958 * Remove a single non-trailing ampersand ('&') from a string.
960 * PARAMS
961 * lpszStr [I/O] String to remove ampersand from.
963 * RETURNS
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;
970 char ch;
972 TRACE("(%s)\n", debugstr_a(lpszStr));
974 ch = *lpszStr;
976 if ((lpszIter = StrChrA(lpszStr, '&')))
978 lpszTmp = CharNextA(lpszIter);
979 if (*lpszTmp)
981 if (*lpszTmp != '&')
982 ch = *lpszTmp;
984 memmove( lpszIter, lpszTmp, strlen(lpszTmp) + 1 );
988 return ch;
991 /*************************************************************************
992 * @ [SHLWAPI.225]
994 * Unicode version of SHStripMneumonicA.
996 WCHAR WINAPI SHStripMneumonicW(LPCWSTR lpszStr)
998 LPWSTR lpszIter, lpszTmp;
999 WCHAR ch;
1001 TRACE("(%s)\n", debugstr_w(lpszStr));
1003 ch = *lpszStr;
1005 if ((lpszIter = StrChrW(lpszStr, '&')))
1007 lpszTmp = lpszIter + 1;
1008 if (*lpszTmp)
1010 if (*lpszTmp != '&')
1011 ch = *lpszTmp;
1013 memmove( lpszIter, lpszTmp, (lstrlenW(lpszTmp) + 1) * sizeof(WCHAR) );
1017 return ch;
1020 /*************************************************************************
1021 * @ [SHLWAPI.216]
1023 * Convert an ANSI string to Unicode.
1025 * PARAMS
1026 * dwCp [I] Code page for the conversion
1027 * lpSrcStr [I] Source ANSI string to convert
1028 * lpDstStr [O] Destination for converted Unicode string
1029 * iLen [I] Length of lpDstStr
1031 * RETURNS
1032 * The return value of the MultiByteToWideChar() function called on lpSrcStr.
1034 DWORD WINAPI SHAnsiToUnicodeCP(DWORD dwCp, LPCSTR lpSrcStr, LPWSTR lpDstStr, int iLen)
1036 DWORD dwRet;
1038 dwRet = MultiByteToWideChar(dwCp, 0, lpSrcStr, -1, lpDstStr, iLen);
1039 TRACE("%s->%s,ret=%ld\n", debugstr_a(lpSrcStr), debugstr_w(lpDstStr), dwRet);
1040 return dwRet;
1043 /*************************************************************************
1044 * @ [SHLWAPI.215]
1046 * Convert an ANSI string to Unicode.
1048 * PARAMS
1049 * lpSrcStr [I] Source ANSI string to convert
1050 * lpDstStr [O] Destination for converted Unicode string
1051 * iLen [I] Length of lpDstStr
1053 * RETURNS
1054 * The return value of the MultiByteToWideChar() function called on lpSrcStr.
1056 * NOTES
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 /*************************************************************************
1065 * @ [SHLWAPI.218]
1067 * Convert a Unicode string to ANSI.
1069 * PARAMS
1070 * CodePage [I] Code page to use for the conversion
1071 * lpSrcStr [I] Source Unicode string to convert
1072 * lpDstStr [O] Destination for converted ANSI string
1073 * dstlen [I] Length of buffer at lpDstStr
1075 * RETURNS
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' };
1087 int len , reqLen;
1088 LPSTR mem;
1090 if (!lpDstStr || !dstlen)
1091 return 0;
1093 if (!lpSrcStr)
1094 lpSrcStr = emptyW;
1096 *lpDstStr = '\0';
1098 len = lstrlenW(lpSrcStr) + 1;
1100 switch (CodePage)
1102 case CP_WINUNICODE:
1103 CodePage = CP_UTF8; /* Fall through... */
1104 case 0x0000C350: /* FIXME: CP_ #define */
1105 case CP_UTF7:
1106 case CP_UTF8:
1108 DWORD dwMode = 0;
1109 INT lenW = len - 1;
1110 INT needed = dstlen - 1;
1111 HRESULT hr;
1113 /* try the user supplied buffer first */
1114 hr = ConvertINetUnicodeToMultiByte(&dwMode, CodePage, lpSrcStr, &lenW, lpDstStr, &needed);
1115 if (hr == S_OK)
1117 lpDstStr[needed] = '\0';
1118 return needed + 1;
1121 /* user buffer too small. exclude termination and copy as much as possible */
1122 lenW = len;
1123 hr = ConvertINetUnicodeToMultiByte(&dwMode, CodePage, lpSrcStr, &lenW, NULL, &needed);
1124 needed++;
1125 mem = HeapAlloc(GetProcessHeap(), 0, needed);
1126 if (!mem)
1127 return 0;
1129 hr = ConvertINetUnicodeToMultiByte(&dwMode, CodePage, lpSrcStr, &len, mem, &needed);
1130 if (hr == S_OK)
1132 reqLen = SHTruncateString(mem, dstlen);
1133 if (reqLen > 0) memcpy(lpDstStr, mem, reqLen-1);
1135 HeapFree(GetProcessHeap(), 0, mem);
1136 return 0;
1138 default:
1139 break;
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);
1148 if (reqLen)
1150 mem = HeapAlloc(GetProcessHeap(), 0, reqLen);
1151 if (mem)
1153 WideCharToMultiByte(CodePage, 0, lpSrcStr, len, mem, reqLen, NULL, NULL);
1155 reqLen = SHTruncateString(mem, dstlen -1);
1156 reqLen++;
1158 lstrcpynA(lpDstStr, mem, reqLen);
1159 HeapFree(GetProcessHeap(), 0, mem);
1160 lpDstStr[reqLen-1] = '\0';
1164 return reqLen;
1167 /*************************************************************************
1168 * @ [SHLWAPI.217]
1170 * Convert a Unicode string to ANSI.
1172 * PARAMS
1173 * lpSrcStr [I] Source Unicode string to convert
1174 * lpDstStr [O] Destination for converted ANSI string
1175 * iLen [O] Length of lpDstStr in characters
1177 * RETURNS
1178 * See SHUnicodeToAnsiCP
1180 * NOTES
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 /*************************************************************************
1189 * @ [SHLWAPI.364]
1191 * Determine if an ANSI string converts to Unicode and back identically.
1193 * PARAMS
1194 * lpSrcStr [I] Source Unicode string to convert
1195 * lpDst [O] Destination for resulting ANSI string
1196 * iLen [I] Length of lpDst in characters
1198 * RETURNS
1199 * TRUE, since ANSI strings always convert identically.
1201 BOOL WINAPI DoesStringRoundTripA(LPCSTR lpSrcStr, LPSTR lpDst, INT iLen)
1203 lstrcpynA(lpDst, lpSrcStr, iLen);
1204 return TRUE;
1207 /*************************************************************************
1208 * @ [SHLWAPI.365]
1210 * Determine if a Unicode string converts to ANSI and back identically.
1212 * PARAMS
1213 * lpSrcStr [I] Source Unicode string to convert
1214 * lpDst [O] Destination for resulting ANSI string
1215 * iLen [I] Length of lpDst in characters
1217 * RETURNS
1218 * TRUE, if lpSrcStr converts to ANSI and back identically,
1219 * FALSE otherwise.
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);