push cc8bc80451cc24f4d7cf75168b569f0ebfe19547
[wine/hacks.git] / dlls / shlwapi / string.c
blobda0dee3cfd2e0b973eca5f967e5e6dd75d9669e8
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 * StrCmpIW [SHLWAPI.@]
324 * Compare two strings, ignoring case.
326 * PARAMS
327 * lpszStr [I] First string to compare
328 * lpszComp [I] Second string to compare
330 * RETURNS
331 * An integer less than, equal to or greater than 0, indicating that
332 * lpszStr is less than, the same, or greater than lpszComp.
334 int WINAPI StrCmpIW(LPCWSTR lpszStr, LPCWSTR lpszComp)
336 int iRet;
338 TRACE("(%s,%s)\n", debugstr_w(lpszStr),debugstr_w(lpszComp));
340 iRet = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, lpszStr, -1, lpszComp, -1);
341 return iRet == CSTR_LESS_THAN ? -1 : iRet == CSTR_GREATER_THAN ? 1 : 0;
344 /*************************************************************************
345 * StrCmpNA [SHLWAPI.@]
347 * Compare two strings, up to a maximum length.
349 * PARAMS
350 * lpszStr [I] First string to compare
351 * lpszComp [I] Second string to compare
352 * iLen [I] Maximum number of chars to compare.
354 * RETURNS
355 * An integer less than, equal to or greater than 0, indicating that
356 * lpszStr is less than, the same, or greater than lpszComp.
358 INT WINAPI StrCmpNA(LPCSTR lpszStr, LPCSTR lpszComp, INT iLen)
360 INT iRet;
362 TRACE("(%s,%s,%i)\n", debugstr_a(lpszStr), debugstr_a(lpszComp), iLen);
364 iRet = CompareStringA(GetThreadLocale(), 0, lpszStr, iLen, lpszComp, iLen);
365 return iRet == CSTR_LESS_THAN ? -1 : iRet == CSTR_GREATER_THAN ? 1 : 0;
368 /*************************************************************************
369 * StrCmpNW [SHLWAPI.@]
371 * See StrCmpNA.
373 INT WINAPI StrCmpNW(LPCWSTR lpszStr, LPCWSTR lpszComp, INT iLen)
375 INT iRet;
377 TRACE("(%s,%s,%i)\n", debugstr_w(lpszStr), debugstr_w(lpszComp), iLen);
379 iRet = CompareStringW(GetThreadLocale(), 0, lpszStr, iLen, lpszComp, iLen);
380 return iRet == CSTR_LESS_THAN ? -1 : iRet == CSTR_GREATER_THAN ? 1 : 0;
383 /*************************************************************************
384 * StrCmpNIA [SHLWAPI.@]
386 * Compare two strings, up to a maximum length, ignoring case.
388 * PARAMS
389 * lpszStr [I] First string to compare
390 * lpszComp [I] Second string to compare
391 * iLen [I] Maximum number of chars to compare.
393 * RETURNS
394 * An integer less than, equal to or greater than 0, indicating that
395 * lpszStr is less than, the same, or greater than lpszComp.
397 int WINAPI StrCmpNIA(LPCSTR lpszStr, LPCSTR lpszComp, int iLen)
399 INT iRet;
401 TRACE("(%s,%s,%i)\n", debugstr_a(lpszStr), debugstr_a(lpszComp), iLen);
403 iRet = CompareStringA(GetThreadLocale(), NORM_IGNORECASE, lpszStr, iLen, lpszComp, iLen);
404 return iRet == CSTR_LESS_THAN ? -1 : iRet == CSTR_GREATER_THAN ? 1 : 0;
407 /*************************************************************************
408 * StrCmpNIW [SHLWAPI.@]
410 * See StrCmpNIA.
412 INT WINAPI StrCmpNIW(LPCWSTR lpszStr, LPCWSTR lpszComp, int iLen)
414 INT iRet;
416 TRACE("(%s,%s,%i)\n", debugstr_w(lpszStr), debugstr_w(lpszComp), iLen);
418 iRet = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, lpszStr, iLen, lpszComp, iLen);
419 return iRet == CSTR_LESS_THAN ? -1 : iRet == CSTR_GREATER_THAN ? 1 : 0;
422 /*************************************************************************
423 * StrCmpW [SHLWAPI.@]
425 * Compare two strings.
427 * PARAMS
428 * lpszStr [I] First string to compare
429 * lpszComp [I] Second string to compare
431 * RETURNS
432 * An integer less than, equal to or greater than 0, indicating that
433 * lpszStr is less than, the same, or greater than lpszComp.
435 int WINAPI StrCmpW(LPCWSTR lpszStr, LPCWSTR lpszComp)
437 INT iRet;
439 TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszComp));
441 iRet = CompareStringW(GetThreadLocale(), 0, lpszStr, -1, lpszComp, -1);
442 return iRet == CSTR_LESS_THAN ? -1 : iRet == CSTR_GREATER_THAN ? 1 : 0;
445 /*************************************************************************
446 * StrCatW [SHLWAPI.@]
448 * Concatenate two strings.
450 * PARAMS
451 * lpszStr [O] Initial string
452 * lpszSrc [I] String to concatenate
454 * RETURNS
455 * lpszStr.
457 LPWSTR WINAPI StrCatW(LPWSTR lpszStr, LPCWSTR lpszSrc)
459 TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszSrc));
461 strcatW(lpszStr, lpszSrc);
462 return lpszStr;
465 /*************************************************************************
466 * StrCpyW [SHLWAPI.@]
468 * Copy a string to another string.
470 * PARAMS
471 * lpszStr [O] Destination string
472 * lpszSrc [I] Source string
474 * RETURNS
475 * lpszStr.
477 LPWSTR WINAPI StrCpyW(LPWSTR lpszStr, LPCWSTR lpszSrc)
479 TRACE("(%p,%s)\n", lpszStr, debugstr_w(lpszSrc));
481 strcpyW(lpszStr, lpszSrc);
482 return lpszStr;
485 /*************************************************************************
486 * StrCpyNW [SHLWAPI.@]
488 * Copy a string to another string, up to a maximum number of characters.
490 * PARAMS
491 * lpszStr [O] Destination string
492 * lpszSrc [I] Source string
493 * iLen [I] Maximum number of chars to copy
495 * RETURNS
496 * lpszStr.
498 LPWSTR WINAPI StrCpyNW(LPWSTR lpszStr, LPCWSTR lpszSrc, int iLen)
500 TRACE("(%p,%s,%i)\n", lpszStr, debugstr_w(lpszSrc), iLen);
502 lstrcpynW(lpszStr, lpszSrc, iLen);
503 return lpszStr;
508 /*************************************************************************
509 * SHLWAPI_StrStrHelperA
511 * Internal implementation of StrStrA/StrStrIA
513 static LPSTR SHLWAPI_StrStrHelperA(LPCSTR lpszStr, LPCSTR lpszSearch,
514 INT (WINAPI *pStrCmpFn)(LPCSTR,LPCSTR,INT))
516 size_t iLen;
518 if (!lpszStr || !lpszSearch || !*lpszSearch)
519 return NULL;
521 iLen = strlen(lpszSearch);
523 while (*lpszStr)
525 if (!pStrCmpFn(lpszStr, lpszSearch, iLen))
526 return (LPSTR)lpszStr;
527 lpszStr = CharNextA(lpszStr);
529 return NULL;
532 /*************************************************************************
533 * StrStrA [SHLWAPI.@]
535 * Find a substring within a string.
537 * PARAMS
538 * lpszStr [I] String to search in
539 * lpszSearch [I] String to look for
541 * RETURNS
542 * The start of lpszSearch within lpszStr, or NULL if not found.
544 LPSTR WINAPI StrStrA(LPCSTR lpszStr, LPCSTR lpszSearch)
546 TRACE("(%s,%s)\n", debugstr_a(lpszStr), debugstr_a(lpszSearch));
548 return SHLWAPI_StrStrHelperA(lpszStr, lpszSearch, StrCmpNA);
551 /*************************************************************************
552 * StrStrW [SHLWAPI.@]
554 * See StrStrA.
556 LPWSTR WINAPI StrStrW(LPCWSTR lpszStr, LPCWSTR lpszSearch)
558 if (!lpszStr || !lpszSearch) return NULL;
559 return strstrW( lpszStr, lpszSearch );
562 /*************************************************************************
563 * StrRStrIA [SHLWAPI.@]
565 * Find the last occurrence of a substring within a string.
567 * PARAMS
568 * lpszStr [I] String to search in
569 * lpszEnd [I] End of lpszStr
570 * lpszSearch [I] String to look for
572 * RETURNS
573 * The last occurrence lpszSearch within lpszStr, or NULL if not found.
575 LPSTR WINAPI StrRStrIA(LPCSTR lpszStr, LPCSTR lpszEnd, LPCSTR lpszSearch)
577 WORD ch1, ch2;
578 INT iLen;
580 TRACE("(%s,%s)\n", debugstr_a(lpszStr), debugstr_a(lpszSearch));
582 if (!lpszStr || !lpszSearch || !*lpszSearch)
583 return NULL;
585 if (!lpszEnd)
586 lpszEnd = lpszStr + lstrlenA(lpszStr);
587 if (lpszEnd == lpszStr)
588 return NULL;
590 if (IsDBCSLeadByte(*lpszSearch))
591 ch1 = *lpszSearch << 8 | (UCHAR)lpszSearch[1];
592 else
593 ch1 = *lpszSearch;
594 iLen = lstrlenA(lpszSearch);
598 lpszEnd = CharPrevA(lpszStr, lpszEnd);
599 ch2 = IsDBCSLeadByte(*lpszEnd)? *lpszEnd << 8 | (UCHAR)lpszEnd[1] : *lpszEnd;
600 if (!ChrCmpIA(ch1, ch2))
602 if (!StrCmpNIA(lpszEnd, lpszSearch, iLen))
603 return (LPSTR)lpszEnd;
605 } while (lpszEnd > lpszStr);
606 return NULL;
609 /*************************************************************************
610 * StrRStrIW [SHLWAPI.@]
612 * See StrRStrIA.
614 LPWSTR WINAPI StrRStrIW(LPCWSTR lpszStr, LPCWSTR lpszEnd, LPCWSTR lpszSearch)
616 INT iLen;
618 TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszSearch));
620 if (!lpszStr || !lpszSearch || !*lpszSearch)
621 return NULL;
623 if (!lpszEnd)
624 lpszEnd = lpszStr + strlenW(lpszStr);
626 iLen = strlenW(lpszSearch);
628 while (lpszEnd > lpszStr)
630 lpszEnd--;
631 if (!StrCmpNIW(lpszEnd, lpszSearch, iLen))
632 return (LPWSTR)lpszEnd;
634 return NULL;
637 /*************************************************************************
638 * StrStrIA [SHLWAPI.@]
640 * Find a substring within a string, ignoring case.
642 * PARAMS
643 * lpszStr [I] String to search in
644 * lpszSearch [I] String to look for
646 * RETURNS
647 * The start of lpszSearch within lpszStr, or NULL if not found.
649 LPSTR WINAPI StrStrIA(LPCSTR lpszStr, LPCSTR lpszSearch)
651 TRACE("(%s,%s)\n", debugstr_a(lpszStr), debugstr_a(lpszSearch));
653 return SHLWAPI_StrStrHelperA(lpszStr, lpszSearch, StrCmpNIA);
656 /*************************************************************************
657 * StrStrIW [SHLWAPI.@]
659 * See StrStrIA.
661 LPWSTR WINAPI StrStrIW(LPCWSTR lpszStr, LPCWSTR lpszSearch)
663 int iLen;
665 TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszSearch));
667 if (!lpszStr || !lpszSearch || !*lpszSearch)
668 return NULL;
670 iLen = strlenW(lpszSearch);
672 while (*lpszStr)
674 if (!StrCmpNIW(lpszStr, lpszSearch, iLen))
675 return (LPWSTR)lpszStr;
676 lpszStr++;
678 return NULL;
681 /*************************************************************************
682 * StrToIntA [SHLWAPI.@]
684 * Read a signed integer from a string.
686 * PARAMS
687 * lpszStr [I] String to read integer from
689 * RETURNS
690 * The signed integer value represented by the string, or 0 if no integer is
691 * present.
693 * NOTES
694 * No leading space is allowed before the number, although a leading '-' is.
696 int WINAPI StrToIntA(LPCSTR lpszStr)
698 int iRet = 0;
700 TRACE("(%s)\n", debugstr_a(lpszStr));
702 if (!lpszStr)
704 WARN("Invalid lpszStr would crash under Win32!\n");
705 return 0;
708 if (*lpszStr == '-' || isdigit(*lpszStr))
709 StrToIntExA(lpszStr, 0, &iRet);
710 return iRet;
713 /*************************************************************************
714 * StrToIntW [SHLWAPI.@]
716 * See StrToIntA.
718 int WINAPI StrToIntW(LPCWSTR lpszStr)
720 int iRet = 0;
722 TRACE("(%s)\n", debugstr_w(lpszStr));
724 if (!lpszStr)
726 WARN("Invalid lpszStr would crash under Win32!\n");
727 return 0;
730 if (*lpszStr == '-' || isdigitW(*lpszStr))
731 StrToIntExW(lpszStr, 0, &iRet);
732 return iRet;
735 /*************************************************************************
736 * StrToIntExA [SHLWAPI.@]
738 * Read an integer from a string.
740 * PARAMS
741 * lpszStr [I] String to read integer from
742 * dwFlags [I] Flags controlling the conversion
743 * lpiRet [O] Destination for read integer.
745 * RETURNS
746 * Success: TRUE. lpiRet contains the integer value represented by the string.
747 * Failure: FALSE, if the string is invalid, or no number is present.
749 * NOTES
750 * Leading whitespace, '-' and '+' are allowed before the number. If
751 * dwFlags includes STIF_SUPPORT_HEX, hexadecimal numbers are allowed, if
752 * preceded by '0x'. If this flag is not set, or there is no '0x' prefix,
753 * the string is treated as a decimal string. A leading '-' is ignored for
754 * hexadecimal numbers.
756 BOOL WINAPI StrToIntExA(LPCSTR lpszStr, DWORD dwFlags, LPINT lpiRet)
758 BOOL bNegative = FALSE;
759 int iRet = 0;
761 TRACE("(%s,%08X,%p)\n", debugstr_a(lpszStr), dwFlags, lpiRet);
763 if (!lpszStr || !lpiRet)
765 WARN("Invalid parameter would crash under Win32!\n");
766 return FALSE;
768 if (dwFlags > STIF_SUPPORT_HEX)
770 WARN("Unknown flags (%08lX)!\n", dwFlags & ~STIF_SUPPORT_HEX);
773 /* Skip leading space, '+', '-' */
774 while (isspace(*lpszStr))
775 lpszStr = CharNextA(lpszStr);
777 if (*lpszStr == '-')
779 bNegative = TRUE;
780 lpszStr++;
782 else if (*lpszStr == '+')
783 lpszStr++;
785 if (dwFlags & STIF_SUPPORT_HEX &&
786 *lpszStr == '0' && tolower(lpszStr[1]) == 'x')
788 /* Read hex number */
789 lpszStr += 2;
791 if (!isxdigit(*lpszStr))
792 return FALSE;
794 while (isxdigit(*lpszStr))
796 iRet = iRet * 16;
797 if (isdigit(*lpszStr))
798 iRet += (*lpszStr - '0');
799 else
800 iRet += 10 + (tolower(*lpszStr) - 'a');
801 lpszStr++;
803 *lpiRet = iRet;
804 return TRUE;
807 /* Read decimal number */
808 if (!isdigit(*lpszStr))
809 return FALSE;
811 while (isdigit(*lpszStr))
813 iRet = iRet * 10;
814 iRet += (*lpszStr - '0');
815 lpszStr++;
817 *lpiRet = bNegative ? -iRet : iRet;
818 return TRUE;
821 /*************************************************************************
822 * StrToIntExW [SHLWAPI.@]
824 * See StrToIntExA.
826 BOOL WINAPI StrToIntExW(LPCWSTR lpszStr, DWORD dwFlags, LPINT lpiRet)
828 BOOL bNegative = FALSE;
829 int iRet = 0;
831 TRACE("(%s,%08X,%p)\n", debugstr_w(lpszStr), dwFlags, lpiRet);
833 if (!lpszStr || !lpiRet)
835 WARN("Invalid parameter would crash under Win32!\n");
836 return FALSE;
838 if (dwFlags > STIF_SUPPORT_HEX)
840 WARN("Unknown flags (%08lX)!\n", dwFlags & ~STIF_SUPPORT_HEX);
843 /* Skip leading space, '+', '-' */
844 while (isspaceW(*lpszStr)) lpszStr++;
846 if (*lpszStr == '-')
848 bNegative = TRUE;
849 lpszStr++;
851 else if (*lpszStr == '+')
852 lpszStr++;
854 if (dwFlags & STIF_SUPPORT_HEX &&
855 *lpszStr == '0' && tolowerW(lpszStr[1]) == 'x')
857 /* Read hex number */
858 lpszStr += 2;
860 if (!isxdigitW(*lpszStr))
861 return FALSE;
863 while (isxdigitW(*lpszStr))
865 iRet = iRet * 16;
866 if (isdigitW(*lpszStr))
867 iRet += (*lpszStr - '0');
868 else
869 iRet += 10 + (tolowerW(*lpszStr) - 'a');
870 lpszStr++;
872 *lpiRet = iRet;
873 return TRUE;
876 /* Read decimal number */
877 if (!isdigitW(*lpszStr))
878 return FALSE;
880 while (isdigitW(*lpszStr))
882 iRet = iRet * 10;
883 iRet += (*lpszStr - '0');
884 lpszStr++;
886 *lpiRet = bNegative ? -iRet : iRet;
887 return TRUE;
890 /*************************************************************************
891 * StrDupA [SHLWAPI.@]
893 * Duplicate a string.
895 * PARAMS
896 * lpszStr [I] String to duplicate.
898 * RETURNS
899 * Success: A pointer to a new string containing the contents of lpszStr
900 * Failure: NULL, if memory cannot be allocated
902 * NOTES
903 * The string memory is allocated with LocalAlloc(), and so should be released
904 * by calling LocalFree().
906 LPSTR WINAPI StrDupA(LPCSTR lpszStr)
908 int iLen;
909 LPSTR lpszRet;
911 TRACE("(%s)\n",debugstr_a(lpszStr));
913 iLen = lpszStr ? strlen(lpszStr) + 1 : 1;
914 lpszRet = LocalAlloc(LMEM_FIXED, iLen);
916 if (lpszRet)
918 if (lpszStr)
919 memcpy(lpszRet, lpszStr, iLen);
920 else
921 *lpszRet = '\0';
923 return lpszRet;
926 /*************************************************************************
927 * StrDupW [SHLWAPI.@]
929 * See StrDupA.
931 LPWSTR WINAPI StrDupW(LPCWSTR lpszStr)
933 int iLen;
934 LPWSTR lpszRet;
936 TRACE("(%s)\n",debugstr_w(lpszStr));
938 iLen = (lpszStr ? strlenW(lpszStr) + 1 : 1) * sizeof(WCHAR);
939 lpszRet = LocalAlloc(LMEM_FIXED, iLen);
941 if (lpszRet)
943 if (lpszStr)
944 memcpy(lpszRet, lpszStr, iLen);
945 else
946 *lpszRet = '\0';
948 return lpszRet;
951 /*************************************************************************
952 * SHLWAPI_StrSpnHelperA
954 * Internal implementation of StrSpnA/StrCSpnA/StrCSpnIA
956 static int SHLWAPI_StrSpnHelperA(LPCSTR lpszStr, LPCSTR lpszMatch,
957 LPSTR (WINAPI *pStrChrFn)(LPCSTR,WORD),
958 BOOL bInvert)
960 LPCSTR lpszRead = lpszStr;
961 if (lpszStr && *lpszStr && lpszMatch)
963 while (*lpszRead)
965 LPCSTR lpszTest = pStrChrFn(lpszMatch, *lpszRead);
967 if (!bInvert && !lpszTest)
968 break;
969 if (bInvert && lpszTest)
970 break;
971 lpszRead = CharNextA(lpszRead);
974 return lpszRead - lpszStr;
977 /*************************************************************************
978 * StrSpnA [SHLWAPI.@]
980 * Find the length of the start of a string that contains only certain
981 * characters.
983 * PARAMS
984 * lpszStr [I] String to search
985 * lpszMatch [I] Characters that can be in the substring
987 * RETURNS
988 * The length of the part of lpszStr containing only chars from lpszMatch,
989 * or 0 if any parameter is invalid.
991 int WINAPI StrSpnA(LPCSTR lpszStr, LPCSTR lpszMatch)
993 TRACE("(%s,%s)\n",debugstr_a(lpszStr), debugstr_a(lpszMatch));
995 return SHLWAPI_StrSpnHelperA(lpszStr, lpszMatch, StrChrA, FALSE);
998 /*************************************************************************
999 * StrSpnW [SHLWAPI.@]
1001 * See StrSpnA.
1003 int WINAPI StrSpnW(LPCWSTR lpszStr, LPCWSTR lpszMatch)
1005 if (!lpszStr || !lpszMatch) return 0;
1006 return strspnW( lpszStr, lpszMatch );
1009 /*************************************************************************
1010 * StrCSpnA [SHLWAPI.@]
1012 * Find the length of the start of a string that does not contain certain
1013 * characters.
1015 * PARAMS
1016 * lpszStr [I] String to search
1017 * lpszMatch [I] Characters that cannot be in the substring
1019 * RETURNS
1020 * The length of the part of lpszStr containing only chars not in lpszMatch,
1021 * or 0 if any parameter is invalid.
1023 int WINAPI StrCSpnA(LPCSTR lpszStr, LPCSTR lpszMatch)
1025 TRACE("(%s,%s)\n",debugstr_a(lpszStr), debugstr_a(lpszMatch));
1027 return SHLWAPI_StrSpnHelperA(lpszStr, lpszMatch, StrChrA, TRUE);
1030 /*************************************************************************
1031 * StrCSpnW [SHLWAPI.@]
1033 * See StrCSpnA.
1035 int WINAPI StrCSpnW(LPCWSTR lpszStr, LPCWSTR lpszMatch)
1037 if (!lpszStr || !lpszMatch) return 0;
1038 return strcspnW( lpszStr, lpszMatch );
1041 /*************************************************************************
1042 * StrCSpnIA [SHLWAPI.@]
1044 * Find the length of the start of a string that does not contain certain
1045 * characters, ignoring case.
1047 * PARAMS
1048 * lpszStr [I] String to search
1049 * lpszMatch [I] Characters that cannot be in the substring
1051 * RETURNS
1052 * The length of the part of lpszStr containing only chars not in lpszMatch,
1053 * or 0 if any parameter is invalid.
1055 int WINAPI StrCSpnIA(LPCSTR lpszStr, LPCSTR lpszMatch)
1057 TRACE("(%s,%s)\n",debugstr_a(lpszStr), debugstr_a(lpszMatch));
1059 return SHLWAPI_StrSpnHelperA(lpszStr, lpszMatch, StrChrIA, TRUE);
1062 /*************************************************************************
1063 * StrCSpnIW [SHLWAPI.@]
1065 * See StrCSpnIA.
1067 int WINAPI StrCSpnIW(LPCWSTR lpszStr, LPCWSTR lpszMatch)
1069 LPCWSTR lpszRead = lpszStr;
1071 TRACE("(%s,%s)\n",debugstr_w(lpszStr), debugstr_w(lpszMatch));
1073 if (lpszStr && *lpszStr && lpszMatch)
1075 while (*lpszRead)
1077 if (StrChrIW(lpszMatch, *lpszRead)) break;
1078 lpszRead++;
1081 return lpszRead - lpszStr;
1084 /*************************************************************************
1085 * StrPBrkA [SHLWAPI.@]
1087 * Search a string for any of a group of characters.
1089 * PARAMS
1090 * lpszStr [I] String to search
1091 * lpszMatch [I] Characters to match
1093 * RETURNS
1094 * A pointer to the first matching character in lpszStr, or NULL if no
1095 * match was found.
1097 LPSTR WINAPI StrPBrkA(LPCSTR lpszStr, LPCSTR lpszMatch)
1099 TRACE("(%s,%s)\n",debugstr_a(lpszStr), debugstr_a(lpszMatch));
1101 if (lpszStr && lpszMatch && *lpszMatch)
1103 while (*lpszStr)
1105 if (StrChrA(lpszMatch, *lpszStr))
1106 return (LPSTR)lpszStr;
1107 lpszStr = CharNextA(lpszStr);
1110 return NULL;
1113 /*************************************************************************
1114 * StrPBrkW [SHLWAPI.@]
1116 * See StrPBrkA.
1118 LPWSTR WINAPI StrPBrkW(LPCWSTR lpszStr, LPCWSTR lpszMatch)
1120 if (!lpszStr || !lpszMatch) return NULL;
1121 return strpbrkW( lpszStr, lpszMatch );
1124 /*************************************************************************
1125 * SHLWAPI_StrRChrHelperA
1127 * Internal implementation of StrRChrA/StrRChrIA.
1129 static LPSTR SHLWAPI_StrRChrHelperA(LPCSTR lpszStr,
1130 LPCSTR lpszEnd, WORD ch,
1131 BOOL (WINAPI *pChrCmpFn)(WORD,WORD))
1133 LPCSTR lpszRet = NULL;
1135 if (lpszStr)
1137 WORD ch2;
1139 if (!lpszEnd)
1140 lpszEnd = lpszStr + lstrlenA(lpszStr);
1142 while (*lpszStr && lpszStr <= lpszEnd)
1144 ch2 = IsDBCSLeadByte(*lpszStr)? *lpszStr << 8 | lpszStr[1] : *lpszStr;
1146 if (!pChrCmpFn(ch, ch2))
1147 lpszRet = lpszStr;
1148 lpszStr = CharNextA(lpszStr);
1151 return (LPSTR)lpszRet;
1154 /**************************************************************************
1155 * StrRChrA [SHLWAPI.@]
1157 * Find the last occurrence of a character in string.
1159 * PARAMS
1160 * lpszStr [I] String to search in
1161 * lpszEnd [I] Place to end search, or NULL to search until the end of lpszStr
1162 * ch [I] Character to search for.
1164 * RETURNS
1165 * Success: A pointer to the last occurrence of ch in lpszStr before lpszEnd,
1166 * or NULL if not found.
1167 * Failure: NULL, if any arguments are invalid.
1169 LPSTR WINAPI StrRChrA(LPCSTR lpszStr, LPCSTR lpszEnd, WORD ch)
1171 TRACE("(%s,%s,%x)\n", debugstr_a(lpszStr), debugstr_a(lpszEnd), ch);
1173 return SHLWAPI_StrRChrHelperA(lpszStr, lpszEnd, ch, SHLWAPI_ChrCmpA);
1176 /**************************************************************************
1177 * StrRChrW [SHLWAPI.@]
1179 * See StrRChrA.
1181 LPWSTR WINAPI StrRChrW(LPCWSTR str, LPCWSTR end, WORD ch)
1183 WCHAR *ret = NULL;
1185 if (!str) return NULL;
1186 if (!end) end = str + strlenW(str);
1187 while (str < end)
1189 if (*str == ch) ret = (WCHAR *)str;
1190 str++;
1192 return ret;
1195 /**************************************************************************
1196 * StrRChrIA [SHLWAPI.@]
1198 * Find the last occurrence of a character in string, ignoring case.
1200 * PARAMS
1201 * lpszStr [I] String to search in
1202 * lpszEnd [I] Place to end search, or NULL to search until the end of lpszStr
1203 * ch [I] Character to search for.
1205 * RETURNS
1206 * Success: A pointer to the last occurrence of ch in lpszStr before lpszEnd,
1207 * or NULL if not found.
1208 * Failure: NULL, if any arguments are invalid.
1210 LPSTR WINAPI StrRChrIA(LPCSTR lpszStr, LPCSTR lpszEnd, WORD ch)
1212 TRACE("(%s,%s,%x)\n", debugstr_a(lpszStr), debugstr_a(lpszEnd), ch);
1214 return SHLWAPI_StrRChrHelperA(lpszStr, lpszEnd, ch, ChrCmpIA);
1217 /**************************************************************************
1218 * StrRChrIW [SHLWAPI.@]
1220 * See StrRChrIA.
1222 LPWSTR WINAPI StrRChrIW(LPCWSTR str, LPCWSTR end, WORD ch)
1224 WCHAR *ret = NULL;
1226 if (!str) return NULL;
1227 if (!end) end = str + strlenW(str);
1228 while (str < end)
1230 if (!ChrCmpIW(*str, ch)) ret = (WCHAR *)str;
1231 str++;
1233 return ret;
1236 /*************************************************************************
1237 * StrCatBuffA [SHLWAPI.@]
1239 * Concatenate two strings together.
1241 * PARAMS
1242 * lpszStr [O] String to concatenate to
1243 * lpszCat [I] String to add to lpszCat
1244 * cchMax [I] Maximum number of characters for the whole string
1246 * RETURNS
1247 * lpszStr.
1249 * NOTES
1250 * cchMax determines the number of characters in the final length of the
1251 * string, not the number appended to lpszStr from lpszCat.
1253 LPSTR WINAPI StrCatBuffA(LPSTR lpszStr, LPCSTR lpszCat, INT cchMax)
1255 INT iLen;
1257 TRACE("(%p,%s,%d)\n", lpszStr, debugstr_a(lpszCat), cchMax);
1259 if (!lpszStr)
1261 WARN("Invalid lpszStr would crash under Win32!\n");
1262 return NULL;
1265 iLen = strlen(lpszStr);
1266 cchMax -= iLen;
1268 if (cchMax > 0)
1269 StrCpyNA(lpszStr + iLen, lpszCat, cchMax);
1270 return lpszStr;
1273 /*************************************************************************
1274 * StrCatBuffW [SHLWAPI.@]
1276 * See StrCatBuffA.
1278 LPWSTR WINAPI StrCatBuffW(LPWSTR lpszStr, LPCWSTR lpszCat, INT cchMax)
1280 INT iLen;
1282 TRACE("(%p,%s,%d)\n", lpszStr, debugstr_w(lpszCat), cchMax);
1284 if (!lpszStr)
1286 WARN("Invalid lpszStr would crash under Win32!\n");
1287 return NULL;
1290 iLen = strlenW(lpszStr);
1291 cchMax -= iLen;
1293 if (cchMax > 0)
1294 StrCpyNW(lpszStr + iLen, lpszCat, cchMax);
1295 return lpszStr;
1298 /*************************************************************************
1299 * StrRetToBufA [SHLWAPI.@]
1301 * Convert a STRRET to a normal string.
1303 * PARAMS
1304 * lpStrRet [O] STRRET to convert
1305 * pIdl [I] ITEMIDLIST for lpStrRet->uType == STRRET_OFFSET
1306 * lpszDest [O] Destination for normal string
1307 * dwLen [I] Length of lpszDest
1309 * RETURNS
1310 * Success: S_OK. lpszDest contains up to dwLen characters of the string.
1311 * If lpStrRet is of type STRRET_WSTR, its memory is freed with
1312 * CoTaskMemFree() and its type set to STRRET_CSTRA.
1313 * Failure: E_FAIL, if any parameters are invalid.
1315 HRESULT WINAPI StrRetToBufA (LPSTRRET src, const ITEMIDLIST *pidl, LPSTR dest, UINT len)
1317 /* NOTE:
1318 * This routine is identical to that in dlls/shell32/shellstring.c.
1319 * It was duplicated because not every version of Shlwapi.dll exports
1320 * StrRetToBufA. If you change one routine, change them both.
1322 TRACE("dest=%p len=0x%x strret=%p pidl=%p stub\n",dest,len,src,pidl);
1324 if (!src)
1326 WARN("Invalid lpStrRet would crash under Win32!\n");
1327 if (dest)
1328 *dest = '\0';
1329 return E_FAIL;
1332 if (!dest || !len)
1333 return E_FAIL;
1335 *dest = '\0';
1337 switch (src->uType)
1339 case STRRET_WSTR:
1340 WideCharToMultiByte(CP_ACP, 0, src->u.pOleStr, -1, dest, len, NULL, NULL);
1341 CoTaskMemFree(src->u.pOleStr);
1342 break;
1344 case STRRET_CSTR:
1345 lstrcpynA(dest, src->u.cStr, len);
1346 break;
1348 case STRRET_OFFSET:
1349 lstrcpynA((LPSTR)dest, ((LPCSTR)&pidl->mkid)+src->u.uOffset, len);
1350 break;
1352 default:
1353 FIXME("unknown type!\n");
1354 return FALSE;
1356 return S_OK;
1359 /*************************************************************************
1360 * StrRetToBufW [SHLWAPI.@]
1362 * See StrRetToBufA.
1364 HRESULT WINAPI StrRetToBufW (LPSTRRET src, const ITEMIDLIST *pidl, LPWSTR dest, UINT len)
1366 TRACE("dest=%p len=0x%x strret=%p pidl=%p stub\n",dest,len,src,pidl);
1368 if (!src)
1370 WARN("Invalid lpStrRet would crash under Win32!\n");
1371 if (dest)
1372 *dest = '\0';
1373 return E_FAIL;
1376 if (!dest || !len)
1377 return E_FAIL;
1379 *dest = '\0';
1381 switch (src->uType)
1383 case STRRET_WSTR:
1384 lstrcpynW(dest, src->u.pOleStr, len);
1385 CoTaskMemFree(src->u.pOleStr);
1386 break;
1388 case STRRET_CSTR:
1389 if (!MultiByteToWideChar( CP_ACP, 0, src->u.cStr, -1, dest, len ) && len)
1390 dest[len-1] = 0;
1391 break;
1393 case STRRET_OFFSET:
1394 if (pidl)
1396 if (!MultiByteToWideChar( CP_ACP, 0, ((LPCSTR)&pidl->mkid)+src->u.uOffset, -1,
1397 dest, len ) && len)
1398 dest[len-1] = 0;
1400 break;
1402 default:
1403 FIXME("unknown type!\n");
1404 return FALSE;
1406 return S_OK;
1409 /*************************************************************************
1410 * StrRetToStrA [SHLWAPI.@]
1412 * Converts a STRRET to a normal string.
1414 * PARAMS
1415 * lpStrRet [O] STRRET to convert
1416 * pidl [I] ITEMIDLIST for lpStrRet->uType == STRRET_OFFSET
1417 * ppszName [O] Destination for converted string
1419 * RETURNS
1420 * Success: S_OK. ppszName contains the new string, allocated with CoTaskMemAlloc().
1421 * Failure: E_FAIL, if any parameters are invalid.
1423 HRESULT WINAPI StrRetToStrA(LPSTRRET lpStrRet, const ITEMIDLIST *pidl, LPSTR *ppszName)
1425 HRESULT hRet = E_FAIL;
1427 switch (lpStrRet->uType)
1429 case STRRET_WSTR:
1430 hRet = _SHStrDupAW(lpStrRet->u.pOleStr, ppszName);
1431 CoTaskMemFree(lpStrRet->u.pOleStr);
1432 break;
1434 case STRRET_CSTR:
1435 hRet = _SHStrDupAA(lpStrRet->u.cStr, ppszName);
1436 break;
1438 case STRRET_OFFSET:
1439 hRet = _SHStrDupAA(((LPCSTR)&pidl->mkid) + lpStrRet->u.uOffset, ppszName);
1440 break;
1442 default:
1443 *ppszName = NULL;
1446 return hRet;
1449 /*************************************************************************
1450 * StrRetToStrW [SHLWAPI.@]
1452 * See StrRetToStrA.
1454 HRESULT WINAPI StrRetToStrW(LPSTRRET lpStrRet, const ITEMIDLIST *pidl, LPWSTR *ppszName)
1456 HRESULT hRet = E_FAIL;
1458 switch (lpStrRet->uType)
1460 case STRRET_WSTR:
1461 hRet = SHStrDupW(lpStrRet->u.pOleStr, ppszName);
1462 CoTaskMemFree(lpStrRet->u.pOleStr);
1463 break;
1465 case STRRET_CSTR:
1466 hRet = SHStrDupA(lpStrRet->u.cStr, ppszName);
1467 break;
1469 case STRRET_OFFSET:
1470 hRet = SHStrDupA(((LPCSTR)&pidl->mkid) + lpStrRet->u.uOffset, ppszName);
1471 break;
1473 default:
1474 *ppszName = NULL;
1477 return hRet;
1480 /* Create an ASCII string copy using SysAllocString() */
1481 static HRESULT _SHStrDupAToBSTR(LPCSTR src, BSTR *pBstrOut)
1483 *pBstrOut = NULL;
1485 if (src)
1487 INT len = MultiByteToWideChar(CP_ACP, 0, src, -1, NULL, 0);
1488 WCHAR* szTemp = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1490 if (szTemp)
1492 MultiByteToWideChar(CP_ACP, 0, src, -1, szTemp, len);
1493 *pBstrOut = SysAllocString(szTemp);
1494 HeapFree(GetProcessHeap(), 0, szTemp);
1496 if (*pBstrOut)
1497 return S_OK;
1500 return E_OUTOFMEMORY;
1503 /*************************************************************************
1504 * StrRetToBSTR [SHLWAPI.@]
1506 * Converts a STRRET to a BSTR.
1508 * PARAMS
1509 * lpStrRet [O] STRRET to convert
1510 * pidl [I] ITEMIDLIST for lpStrRet->uType = STRRET_OFFSET
1511 * pBstrOut [O] Destination for converted BSTR
1513 * RETURNS
1514 * Success: S_OK. pBstrOut contains the new string.
1515 * Failure: E_FAIL, if any parameters are invalid.
1517 HRESULT WINAPI StrRetToBSTR(STRRET *lpStrRet, LPCITEMIDLIST pidl, BSTR* pBstrOut)
1519 HRESULT hRet = E_FAIL;
1521 switch (lpStrRet->uType)
1523 case STRRET_WSTR:
1524 *pBstrOut = SysAllocString(lpStrRet->u.pOleStr);
1525 if (*pBstrOut)
1526 hRet = S_OK;
1527 CoTaskMemFree(lpStrRet->u.pOleStr);
1528 break;
1530 case STRRET_CSTR:
1531 hRet = _SHStrDupAToBSTR(lpStrRet->u.cStr, pBstrOut);
1532 break;
1534 case STRRET_OFFSET:
1535 hRet = _SHStrDupAToBSTR(((LPCSTR)&pidl->mkid) + lpStrRet->u.uOffset, pBstrOut);
1536 break;
1538 default:
1539 *pBstrOut = NULL;
1542 return hRet;
1545 /*************************************************************************
1546 * StrFormatKBSizeA [SHLWAPI.@]
1548 * Create a formatted string containing a byte count in Kilobytes.
1550 * PARAMS
1551 * llBytes [I] Byte size to format
1552 * lpszDest [I] Destination for formatted string
1553 * cchMax [I] Size of lpszDest
1555 * RETURNS
1556 * lpszDest.
1558 LPSTR WINAPI StrFormatKBSizeA(LONGLONG llBytes, LPSTR lpszDest, UINT cchMax)
1560 WCHAR wszBuf[256];
1562 if (!StrFormatKBSizeW(llBytes, wszBuf, 256))
1563 return NULL;
1564 if (!WideCharToMultiByte(CP_ACP, 0, wszBuf, -1, lpszDest, cchMax, NULL, NULL))
1565 return NULL;
1566 return lpszDest;
1569 /*************************************************************************
1570 * StrFormatKBSizeW [SHLWAPI.@]
1572 * See StrFormatKBSizeA.
1574 LPWSTR WINAPI StrFormatKBSizeW(LONGLONG llBytes, LPWSTR lpszDest, UINT cchMax)
1576 static const WCHAR kb[] = {' ','K','B',0};
1577 LONGLONG llKB = (llBytes + 1023) >> 10;
1578 int len;
1580 TRACE("(0x%s,%p,%d)\n", wine_dbgstr_longlong(llBytes), lpszDest, cchMax);
1582 if (!FormatInt(llKB, lpszDest, cchMax))
1583 return NULL;
1585 len = lstrlenW(lpszDest);
1586 if (cchMax - len < 4)
1587 return NULL;
1588 lstrcatW(lpszDest, kb);
1589 return lpszDest;
1592 /*************************************************************************
1593 * StrNCatA [SHLWAPI.@]
1595 * Concatenate two strings together.
1597 * PARAMS
1598 * lpszStr [O] String to concatenate to
1599 * lpszCat [I] String to add to lpszCat
1600 * cchMax [I] Maximum number of characters to concatenate
1602 * RETURNS
1603 * lpszStr.
1605 * NOTES
1606 * cchMax determines the number of characters that are appended to lpszStr,
1607 * not the total length of the string.
1609 LPSTR WINAPI StrNCatA(LPSTR lpszStr, LPCSTR lpszCat, INT cchMax)
1611 LPSTR lpszRet = lpszStr;
1613 TRACE("(%s,%s,%i)\n", debugstr_a(lpszStr), debugstr_a(lpszCat), cchMax);
1615 if (!lpszStr)
1617 WARN("Invalid lpszStr would crash under Win32!\n");
1618 return NULL;
1621 StrCpyNA(lpszStr + strlen(lpszStr), lpszCat, cchMax);
1622 return lpszRet;
1625 /*************************************************************************
1626 * StrNCatW [SHLWAPI.@]
1628 * See StrNCatA.
1630 LPWSTR WINAPI StrNCatW(LPWSTR lpszStr, LPCWSTR lpszCat, INT cchMax)
1632 LPWSTR lpszRet = lpszStr;
1634 TRACE("(%s,%s,%i)\n", debugstr_w(lpszStr), debugstr_w(lpszCat), cchMax);
1636 if (!lpszStr)
1638 WARN("Invalid lpszStr would crash under Win32\n");
1639 return NULL;
1642 StrCpyNW(lpszStr + strlenW(lpszStr), lpszCat, cchMax);
1643 return lpszRet;
1646 /*************************************************************************
1647 * StrTrimA [SHLWAPI.@]
1649 * Remove characters from the start and end of a string.
1651 * PARAMS
1652 * lpszStr [O] String to remove characters from
1653 * lpszTrim [I] Characters to remove from lpszStr
1655 * RETURNS
1656 * TRUE If lpszStr was valid and modified
1657 * FALSE Otherwise
1659 BOOL WINAPI StrTrimA(LPSTR lpszStr, LPCSTR lpszTrim)
1661 DWORD dwLen;
1662 LPSTR lpszRead = lpszStr;
1663 BOOL bRet = FALSE;
1665 TRACE("(%s,%s)\n", debugstr_a(lpszStr), debugstr_a(lpszTrim));
1667 if (lpszRead && *lpszRead)
1669 while (*lpszRead && StrChrA(lpszTrim, *lpszRead))
1670 lpszRead = CharNextA(lpszRead); /* Skip leading matches */
1672 dwLen = strlen(lpszRead);
1674 if (lpszRead != lpszStr)
1676 memmove(lpszStr, lpszRead, dwLen + 1);
1677 bRet = TRUE;
1679 if (dwLen > 0)
1681 lpszRead = lpszStr + dwLen;
1682 while (StrChrA(lpszTrim, lpszRead[-1]))
1683 lpszRead = CharPrevA(lpszStr, lpszRead); /* Skip trailing matches */
1685 if (lpszRead != lpszStr + dwLen)
1687 *lpszRead = '\0';
1688 bRet = TRUE;
1692 return bRet;
1695 /*************************************************************************
1696 * StrTrimW [SHLWAPI.@]
1698 * See StrTrimA.
1700 BOOL WINAPI StrTrimW(LPWSTR lpszStr, LPCWSTR lpszTrim)
1702 DWORD dwLen;
1703 LPWSTR lpszRead = lpszStr;
1704 BOOL bRet = FALSE;
1706 TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszTrim));
1708 if (lpszRead && *lpszRead)
1710 while (*lpszRead && StrChrW(lpszTrim, *lpszRead)) lpszRead++;
1712 dwLen = strlenW(lpszRead);
1714 if (lpszRead != lpszStr)
1716 memmove(lpszStr, lpszRead, (dwLen + 1) * sizeof(WCHAR));
1717 bRet = TRUE;
1719 if (dwLen > 0)
1721 lpszRead = lpszStr + dwLen;
1722 while (StrChrW(lpszTrim, lpszRead[-1]))
1723 lpszRead--; /* Skip trailing matches */
1725 if (lpszRead != lpszStr + dwLen)
1727 *lpszRead = '\0';
1728 bRet = TRUE;
1732 return bRet;
1735 /*************************************************************************
1736 * _SHStrDupAA [INTERNAL]
1738 * Duplicates a ASCII string to ASCII. The destination buffer is allocated.
1740 static HRESULT _SHStrDupAA(LPCSTR src, LPSTR * dest)
1742 HRESULT hr;
1743 int len = 0;
1745 if (src) {
1746 len = lstrlenA(src) + 1;
1747 *dest = CoTaskMemAlloc(len);
1748 } else {
1749 *dest = NULL;
1752 if (*dest) {
1753 lstrcpynA(*dest,src, len);
1754 hr = S_OK;
1755 } else {
1756 hr = E_OUTOFMEMORY;
1759 TRACE("%s->(%p)\n", debugstr_a(src), *dest);
1760 return hr;
1763 /*************************************************************************
1764 * SHStrDupA [SHLWAPI.@]
1766 * Return a Unicode copy of a string, in memory allocated by CoTaskMemAlloc().
1768 * PARAMS
1769 * lpszStr [I] String to copy
1770 * lppszDest [O] Destination for the new string copy
1772 * RETURNS
1773 * Success: S_OK. lppszDest contains the new string in Unicode format.
1774 * Failure: E_OUTOFMEMORY, If any arguments are invalid or memory allocation
1775 * fails.
1777 HRESULT WINAPI SHStrDupA(LPCSTR lpszStr, LPWSTR * lppszDest)
1779 HRESULT hRet;
1780 int len = 0;
1782 if (lpszStr)
1784 len = MultiByteToWideChar(0, 0, lpszStr, -1, 0, 0) * sizeof(WCHAR);
1785 *lppszDest = CoTaskMemAlloc(len);
1787 else
1788 *lppszDest = NULL;
1790 if (*lppszDest)
1792 MultiByteToWideChar(0, 0, lpszStr, -1, *lppszDest, len/sizeof(WCHAR));
1793 hRet = S_OK;
1795 else
1796 hRet = E_OUTOFMEMORY;
1798 TRACE("%s->(%p)\n", debugstr_a(lpszStr), *lppszDest);
1799 return hRet;
1802 /*************************************************************************
1803 * _SHStrDupAW [INTERNAL]
1805 * Duplicates a UNICODE to a ASCII string. The destination buffer is allocated.
1807 static HRESULT _SHStrDupAW(LPCWSTR src, LPSTR * dest)
1809 HRESULT hr;
1810 int len = 0;
1812 if (src) {
1813 len = WideCharToMultiByte(CP_ACP, 0, src, -1, NULL, 0, NULL, NULL);
1814 *dest = CoTaskMemAlloc(len);
1815 } else {
1816 *dest = NULL;
1819 if (*dest) {
1820 WideCharToMultiByte(CP_ACP, 0, src, -1, *dest, len, NULL, NULL);
1821 hr = S_OK;
1822 } else {
1823 hr = E_OUTOFMEMORY;
1826 TRACE("%s->(%p)\n", debugstr_w(src), *dest);
1827 return hr;
1830 /*************************************************************************
1831 * SHStrDupW [SHLWAPI.@]
1833 * See SHStrDupA.
1835 HRESULT WINAPI SHStrDupW(LPCWSTR src, LPWSTR * dest)
1837 HRESULT hr;
1838 int len = 0;
1840 if (src) {
1841 len = (lstrlenW(src) + 1) * sizeof(WCHAR);
1842 *dest = CoTaskMemAlloc(len);
1843 } else {
1844 *dest = NULL;
1847 if (*dest) {
1848 memcpy(*dest, src, len);
1849 hr = S_OK;
1850 } else {
1851 hr = E_OUTOFMEMORY;
1854 TRACE("%s->(%p)\n", debugstr_w(src), *dest);
1855 return hr;
1858 /*************************************************************************
1859 * SHLWAPI_WriteReverseNum
1861 * Internal helper for SHLWAPI_WriteTimeClass.
1863 static inline LPWSTR SHLWAPI_WriteReverseNum(LPWSTR lpszOut, DWORD dwNum)
1865 *lpszOut-- = '\0';
1867 /* Write a decimal number to a string, backwards */
1870 DWORD dwNextDigit = dwNum % 10;
1871 *lpszOut-- = '0' + dwNextDigit;
1872 dwNum = (dwNum - dwNextDigit) / 10;
1873 } while (dwNum > 0);
1875 return lpszOut;
1878 /*************************************************************************
1879 * SHLWAPI_FormatSignificant
1881 * Internal helper for SHLWAPI_WriteTimeClass.
1883 static inline int SHLWAPI_FormatSignificant(LPWSTR lpszNum, int dwDigits)
1885 /* Zero non significant digits, return remaining significant digits */
1886 while (*lpszNum)
1888 lpszNum++;
1889 if (--dwDigits == 0)
1891 while (*lpszNum)
1892 *lpszNum++ = '0';
1893 return 0;
1896 return dwDigits;
1899 /*************************************************************************
1900 * SHLWAPI_WriteTimeClass
1902 * Internal helper for StrFromTimeIntervalW.
1904 static int SHLWAPI_WriteTimeClass(LPWSTR lpszOut, DWORD dwValue,
1905 UINT uClassStringId, int iDigits)
1907 WCHAR szBuff[64], *szOut = szBuff + 32;
1909 szOut = SHLWAPI_WriteReverseNum(szOut, dwValue);
1910 iDigits = SHLWAPI_FormatSignificant(szOut + 1, iDigits);
1911 *szOut = ' ';
1912 LoadStringW(shlwapi_hInstance, uClassStringId, szBuff + 32, 32);
1913 strcatW(lpszOut, szOut);
1914 return iDigits;
1917 /*************************************************************************
1918 * StrFromTimeIntervalA [SHLWAPI.@]
1920 * Format a millisecond time interval into a string
1922 * PARAMS
1923 * lpszStr [O] Output buffer for formatted time interval
1924 * cchMax [I] Size of lpszStr
1925 * dwMS [I] Number of milliseconds
1926 * iDigits [I] Number of digits to print
1928 * RETURNS
1929 * The length of the formatted string, or 0 if any parameter is invalid.
1931 * NOTES
1932 * This implementation mimics the Win32 behaviour of always writing a leading
1933 * space before the time interval begins.
1935 * iDigits is used to provide approximate times if accuracy is not important.
1936 * This number of digits will be written of the first non-zero time class
1937 * (hours/minutes/seconds). If this does not complete the time classification,
1938 * the remaining digits are changed to zeros (i.e. The time is _not_ rounded).
1939 * If there are digits remaining following the writing of a time class, the
1940 * next time class will be written.
1942 * For example, given dwMS represents 138 hours,43 minutes and 15 seconds, the
1943 * following will result from the given values of iDigits:
1945 *| iDigits 1 2 3 4 5 ...
1946 *| lpszStr "100 hr" "130 hr" "138 hr" "138 hr 40 min" "138 hr 43 min" ...
1948 INT WINAPI StrFromTimeIntervalA(LPSTR lpszStr, UINT cchMax, DWORD dwMS,
1949 int iDigits)
1951 INT iRet = 0;
1953 TRACE("(%p,%d,%d,%d)\n", lpszStr, cchMax, dwMS, iDigits);
1955 if (lpszStr && cchMax)
1957 WCHAR szBuff[128];
1958 StrFromTimeIntervalW(szBuff, sizeof(szBuff)/sizeof(WCHAR), dwMS, iDigits);
1959 WideCharToMultiByte(CP_ACP,0,szBuff,-1,lpszStr,cchMax,0,0);
1961 return iRet;
1965 /*************************************************************************
1966 * StrFromTimeIntervalW [SHLWAPI.@]
1968 * See StrFromTimeIntervalA.
1970 INT WINAPI StrFromTimeIntervalW(LPWSTR lpszStr, UINT cchMax, DWORD dwMS,
1971 int iDigits)
1973 INT iRet = 0;
1975 TRACE("(%p,%d,%d,%d)\n", lpszStr, cchMax, dwMS, iDigits);
1977 if (lpszStr && cchMax)
1979 WCHAR szCopy[128];
1980 DWORD dwHours, dwMinutes;
1982 if (!iDigits || cchMax == 1)
1984 *lpszStr = '\0';
1985 return 0;
1988 /* Calculate the time classes */
1989 dwMS = (dwMS + 500) / 1000;
1990 dwHours = dwMS / 3600;
1991 dwMS -= dwHours * 3600;
1992 dwMinutes = dwMS / 60;
1993 dwMS -= dwMinutes * 60;
1995 szCopy[0] = '\0';
1997 if (dwHours)
1998 iDigits = SHLWAPI_WriteTimeClass(szCopy, dwHours, IDS_TIME_INTERVAL_HOURS, iDigits);
2000 if (dwMinutes && iDigits)
2001 iDigits = SHLWAPI_WriteTimeClass(szCopy, dwMinutes, IDS_TIME_INTERVAL_MINUTES, iDigits);
2003 if (iDigits) /* Always write seconds if we have significant digits */
2004 SHLWAPI_WriteTimeClass(szCopy, dwMS, IDS_TIME_INTERVAL_SECONDS, iDigits);
2006 lstrcpynW(lpszStr, szCopy, cchMax);
2007 iRet = strlenW(lpszStr);
2009 return iRet;
2012 /*************************************************************************
2013 * StrIsIntlEqualA [SHLWAPI.@]
2015 * Compare two strings.
2017 * PARAMS
2018 * bCase [I] Whether to compare case sensitively
2019 * lpszStr [I] First string to compare
2020 * lpszComp [I] Second string to compare
2021 * iLen [I] Length to compare
2023 * RETURNS
2024 * TRUE If the strings are equal.
2025 * FALSE Otherwise.
2027 BOOL WINAPI StrIsIntlEqualA(BOOL bCase, LPCSTR lpszStr, LPCSTR lpszComp,
2028 int iLen)
2030 DWORD dwFlags;
2032 TRACE("(%d,%s,%s,%d)\n", bCase,
2033 debugstr_a(lpszStr), debugstr_a(lpszComp), iLen);
2035 /* FIXME: This flag is undocumented and unknown by our CompareString.
2036 * We need a define for it.
2038 dwFlags = 0x10000000;
2039 if (!bCase) dwFlags |= NORM_IGNORECASE;
2041 return (CompareStringA(GetThreadLocale(), dwFlags, lpszStr, iLen, lpszComp, iLen) == CSTR_EQUAL);
2044 /*************************************************************************
2045 * StrIsIntlEqualW [SHLWAPI.@]
2047 * See StrIsIntlEqualA.
2049 BOOL WINAPI StrIsIntlEqualW(BOOL bCase, LPCWSTR lpszStr, LPCWSTR lpszComp,
2050 int iLen)
2052 DWORD dwFlags;
2054 TRACE("(%d,%s,%s,%d)\n", bCase,
2055 debugstr_w(lpszStr),debugstr_w(lpszComp), iLen);
2057 /* FIXME: This flag is undocumented and unknown by our CompareString.
2058 * We need a define for it.
2060 dwFlags = 0x10000000;
2061 if (!bCase) dwFlags |= NORM_IGNORECASE;
2063 return (CompareStringW(GetThreadLocale(), dwFlags, lpszStr, iLen, lpszComp, iLen) == CSTR_EQUAL);
2066 /*************************************************************************
2067 * @ [SHLWAPI.399]
2069 * Copy a string to another string, up to a maximum number of characters.
2071 * PARAMS
2072 * lpszDest [O] Destination string
2073 * lpszSrc [I] Source string
2074 * iLen [I] Maximum number of chars to copy
2076 * RETURNS
2077 * Success: A pointer to the last character written to lpszDest.
2078 * Failure: lpszDest, if any arguments are invalid.
2080 LPSTR WINAPI StrCpyNXA(LPSTR lpszDest, LPCSTR lpszSrc, int iLen)
2082 TRACE("(%p,%s,%i)\n", lpszDest, debugstr_a(lpszSrc), iLen);
2084 if (lpszDest && lpszSrc && iLen > 0)
2086 while ((iLen-- > 1) && *lpszSrc)
2087 *lpszDest++ = *lpszSrc++;
2088 if (iLen >= 0)
2089 *lpszDest = '\0';
2091 return lpszDest;
2094 /*************************************************************************
2095 * @ [SHLWAPI.400]
2097 * Unicode version of StrCpyNXA.
2099 LPWSTR WINAPI StrCpyNXW(LPWSTR lpszDest, LPCWSTR lpszSrc, int iLen)
2101 TRACE("(%p,%s,%i)\n", lpszDest, debugstr_w(lpszSrc), iLen);
2103 if (lpszDest && lpszSrc && iLen > 0)
2105 while ((iLen-- > 1) && *lpszSrc)
2106 *lpszDest++ = *lpszSrc++;
2107 if (iLen >= 0)
2108 *lpszDest = '\0';
2110 return lpszDest;
2113 /*************************************************************************
2114 * StrCmpLogicalW [SHLWAPI.@]
2116 * Compare two strings, ignoring case and comparing digits as numbers.
2118 * PARAMS
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 INT WINAPI StrCmpLogicalW(LPCWSTR lpszStr, LPCWSTR lpszComp)
2129 INT iDiff;
2131 TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszComp));
2133 if (lpszStr && lpszComp)
2135 while (*lpszStr)
2137 if (!*lpszComp)
2138 return 1;
2139 else if (isdigitW(*lpszStr))
2141 int iStr, iComp;
2143 if (!isdigitW(*lpszComp))
2144 return -1;
2146 /* Compare the numbers */
2147 StrToIntExW(lpszStr, 0, &iStr);
2148 StrToIntExW(lpszComp, 0, &iComp);
2150 if (iStr < iComp)
2151 return -1;
2152 else if (iStr > iComp)
2153 return 1;
2155 /* Skip */
2156 while (isdigitW(*lpszStr))
2157 lpszStr++;
2158 while (isdigitW(*lpszComp))
2159 lpszComp++;
2161 else if (isdigitW(*lpszComp))
2162 return 1;
2163 else
2165 iDiff = ChrCmpIW(*lpszStr,*lpszComp);
2166 if (iDiff > 0)
2167 return 1;
2168 else if (iDiff < 0)
2169 return -1;
2171 lpszStr++;
2172 lpszComp++;
2175 if (*lpszComp)
2176 return -1;
2178 return 0;
2181 /* Structure for formatting byte strings */
2182 typedef struct tagSHLWAPI_BYTEFORMATS
2184 LONGLONG dLimit;
2185 double dDivisor;
2186 double dNormaliser;
2187 int nDecimals;
2188 WCHAR wPrefix;
2189 } SHLWAPI_BYTEFORMATS;
2191 /*************************************************************************
2192 * StrFormatByteSizeW [SHLWAPI.@]
2194 * Create a string containing an abbreviated byte count of up to 2^63-1.
2196 * PARAMS
2197 * llBytes [I] Byte size to format
2198 * lpszDest [I] Destination for formatted string
2199 * cchMax [I] Size of lpszDest
2201 * RETURNS
2202 * lpszDest.
2204 * NOTES
2205 * There is no StrFormatByteSize64W function, it is called StrFormatByteSizeW().
2207 LPWSTR WINAPI StrFormatByteSizeW(LONGLONG llBytes, LPWSTR lpszDest, UINT cchMax)
2209 #define KB ((ULONGLONG)1024)
2210 #define MB (KB*KB)
2211 #define GB (KB*KB*KB)
2212 #define TB (KB*KB*KB*KB)
2213 #define PB (KB*KB*KB*KB*KB)
2215 static const SHLWAPI_BYTEFORMATS bfFormats[] =
2217 { 10*KB, 10.24, 100.0, 2, 'K' }, /* 10 KB */
2218 { 100*KB, 102.4, 10.0, 1, 'K' }, /* 100 KB */
2219 { 1000*KB, 1024.0, 1.0, 0, 'K' }, /* 1000 KB */
2220 { 10*MB, 10485.76, 100.0, 2, 'M' }, /* 10 MB */
2221 { 100*MB, 104857.6, 10.0, 1, 'M' }, /* 100 MB */
2222 { 1000*MB, 1048576.0, 1.0, 0, 'M' }, /* 1000 MB */
2223 { 10*GB, 10737418.24, 100.0, 2, 'G' }, /* 10 GB */
2224 { 100*GB, 107374182.4, 10.0, 1, 'G' }, /* 100 GB */
2225 { 1000*GB, 1073741824.0, 1.0, 0, 'G' }, /* 1000 GB */
2226 { 10*TB, 10485.76, 100.0, 2, 'T' }, /* 10 TB */
2227 { 100*TB, 104857.6, 10.0, 1, 'T' }, /* 100 TB */
2228 { 1000*TB, 1048576.0, 1.0, 0, 'T' }, /* 1000 TB */
2229 { 10*PB, 10737418.24, 100.00, 2, 'P' }, /* 10 PB */
2230 { 100*PB, 107374182.4, 10.00, 1, 'P' }, /* 100 PB */
2231 { 1000*PB, 1073741824.0, 1.00, 0, 'P' }, /* 1000 PB */
2232 { 0, 10995116277.76, 100.00, 2, 'E' } /* EB's, catch all */
2234 WCHAR wszAdd[] = {' ','?','B',0};
2235 double dBytes;
2236 UINT i = 0;
2238 TRACE("(0x%s,%p,%d)\n", wine_dbgstr_longlong(llBytes), lpszDest, cchMax);
2240 if (!lpszDest || !cchMax)
2241 return lpszDest;
2243 if (llBytes < 1024) /* 1K */
2245 WCHAR wszBytesFormat[64];
2246 LoadStringW(shlwapi_hInstance, IDS_BYTES_FORMAT, wszBytesFormat, 64);
2247 snprintfW(lpszDest, cchMax, wszBytesFormat, (long)llBytes);
2248 return lpszDest;
2251 /* Note that if this loop completes without finding a match, i will be
2252 * pointing at the last entry, which is a catch all for > 1000 PB
2254 while (i < sizeof(bfFormats) / sizeof(SHLWAPI_BYTEFORMATS) - 1)
2256 if (llBytes < bfFormats[i].dLimit)
2257 break;
2258 i++;
2260 /* Above 1 TB we encounter problems with FP accuracy. So for amounts above
2261 * this number we integer shift down by 1 MB first. The table above has
2262 * the divisors scaled down from the '< 10 TB' entry onwards, to account
2263 * for this. We also add a small fudge factor to get the correct result for
2264 * counts that lie exactly on a 1024 byte boundary.
2266 if (i > 8)
2267 dBytes = (double)(llBytes >> 20) + 0.001; /* Scale down by I MB */
2268 else
2269 dBytes = (double)llBytes + 0.00001;
2271 dBytes = floor(dBytes / bfFormats[i].dDivisor) / bfFormats[i].dNormaliser;
2273 if (!FormatDouble(dBytes, bfFormats[i].nDecimals, lpszDest, cchMax))
2274 return NULL;
2275 wszAdd[1] = bfFormats[i].wPrefix;
2276 StrCatBuffW(lpszDest, wszAdd, cchMax);
2277 return lpszDest;
2280 /*************************************************************************
2281 * StrFormatByteSize64A [SHLWAPI.@]
2283 * See StrFormatByteSizeW.
2285 LPSTR WINAPI StrFormatByteSize64A(LONGLONG llBytes, LPSTR lpszDest, UINT cchMax)
2287 WCHAR wszBuff[32];
2289 StrFormatByteSizeW(llBytes, wszBuff, sizeof(wszBuff)/sizeof(WCHAR));
2291 if (lpszDest)
2292 WideCharToMultiByte(CP_ACP, 0, wszBuff, -1, lpszDest, cchMax, 0, 0);
2293 return lpszDest;
2296 /*************************************************************************
2297 * StrFormatByteSizeA [SHLWAPI.@]
2299 * Create a string containing an abbreviated byte count of up to 2^31-1.
2301 * PARAMS
2302 * dwBytes [I] Byte size to format
2303 * lpszDest [I] Destination for formatted string
2304 * cchMax [I] Size of lpszDest
2306 * RETURNS
2307 * lpszDest.
2309 * NOTES
2310 * The Ascii and Unicode versions of this function accept a different
2311 * integer type for dwBytes. See StrFormatByteSize64A().
2313 LPSTR WINAPI StrFormatByteSizeA(DWORD dwBytes, LPSTR lpszDest, UINT cchMax)
2315 TRACE("(%d,%p,%d)\n", dwBytes, lpszDest, cchMax);
2317 return StrFormatByteSize64A(dwBytes, lpszDest, cchMax);
2320 /*************************************************************************
2321 * @ [SHLWAPI.162]
2323 * Remove a hanging lead byte from the end of a string, if present.
2325 * PARAMS
2326 * lpStr [I] String to check for a hanging lead byte
2327 * size [I] Length of lpStr
2329 * RETURNS
2330 * Success: The new length of the string. Any hanging lead bytes are removed.
2331 * Failure: 0, if any parameters are invalid.
2333 DWORD WINAPI SHTruncateString(LPSTR lpStr, DWORD size)
2335 if (lpStr && size)
2337 LPSTR lastByte = lpStr + size - 1;
2339 while(lpStr < lastByte)
2340 lpStr += IsDBCSLeadByte(*lpStr) ? 2 : 1;
2342 if(lpStr == lastByte && IsDBCSLeadByte(*lpStr))
2344 *lpStr = '\0';
2345 size--;
2347 return size;
2349 return 0;
2352 /*************************************************************************
2353 * @ [SHLWAPI.203]
2355 * Remove a single non-trailing ampersand ('&') from a string.
2357 * PARAMS
2358 * lpszStr [I/O] String to remove ampersand from.
2360 * RETURNS
2361 * The character after the first ampersand in lpszStr, or the first character
2362 * in lpszStr if there is no ampersand in the string.
2364 char WINAPI SHStripMneumonicA(LPCSTR lpszStr)
2366 LPSTR lpszIter, lpszTmp;
2367 char ch;
2369 TRACE("(%s)\n", debugstr_a(lpszStr));
2371 ch = *lpszStr;
2373 if ((lpszIter = StrChrA(lpszStr, '&')))
2375 lpszTmp = CharNextA(lpszIter);
2376 if (lpszTmp && *lpszTmp)
2378 if (*lpszTmp != '&')
2379 ch = *lpszTmp;
2381 while (lpszIter && *lpszIter)
2383 lpszTmp = CharNextA(lpszIter);
2384 *lpszIter = *lpszTmp;
2385 lpszIter = lpszTmp;
2390 return ch;
2393 /*************************************************************************
2394 * @ [SHLWAPI.225]
2396 * Unicode version of SHStripMneumonicA.
2398 WCHAR WINAPI SHStripMneumonicW(LPCWSTR lpszStr)
2400 LPWSTR lpszIter, lpszTmp;
2401 WCHAR ch;
2403 TRACE("(%s)\n", debugstr_w(lpszStr));
2405 ch = *lpszStr;
2407 if ((lpszIter = StrChrW(lpszStr, '&')))
2409 lpszTmp = lpszIter + 1;
2410 if (lpszTmp && *lpszTmp)
2412 if (*lpszTmp != '&')
2413 ch = *lpszTmp;
2415 while (lpszIter && *lpszIter)
2417 lpszTmp = lpszIter + 1;
2418 *lpszIter = *lpszTmp;
2419 lpszIter = lpszTmp;
2424 return ch;
2427 /*************************************************************************
2428 * @ [SHLWAPI.216]
2430 * Convert an Ascii string to Unicode.
2432 * PARAMS
2433 * dwCp [I] Code page for the conversion
2434 * lpSrcStr [I] Source Ascii string to convert
2435 * lpDstStr [O] Destination for converted Unicode string
2436 * iLen [I] Length of lpDstStr
2438 * RETURNS
2439 * The return value of the MultiByteToWideChar() function called on lpSrcStr.
2441 DWORD WINAPI SHAnsiToUnicodeCP(DWORD dwCp, LPCSTR lpSrcStr, LPWSTR lpDstStr, int iLen)
2443 DWORD dwRet;
2445 dwRet = MultiByteToWideChar(dwCp, 0, lpSrcStr, -1, lpDstStr, iLen);
2446 TRACE("%s->%s,ret=%d\n", debugstr_a(lpSrcStr), debugstr_w(lpDstStr), dwRet);
2447 return dwRet;
2450 /*************************************************************************
2451 * @ [SHLWAPI.215]
2453 * Convert an Ascii string to Unicode.
2455 * PARAMS
2456 * lpSrcStr [I] Source Ascii string to convert
2457 * lpDstStr [O] Destination for converted Unicode string
2458 * iLen [I] Length of lpDstStr
2460 * RETURNS
2461 * The return value of the MultiByteToWideChar() function called on lpSrcStr.
2463 * NOTES
2464 * This function simply calls SHAnsiToUnicodeCP with code page CP_ACP.
2466 DWORD WINAPI SHAnsiToUnicode(LPCSTR lpSrcStr, LPWSTR lpDstStr, int iLen)
2468 return SHAnsiToUnicodeCP(CP_ACP, lpSrcStr, lpDstStr, iLen);
2471 /*************************************************************************
2472 * @ [SHLWAPI.218]
2474 * Convert a Unicode string to Ascii.
2476 * PARAMS
2477 * CodePage [I] Code page to use for the conversion
2478 * lpSrcStr [I] Source Unicode string to convert
2479 * lpDstStr [O] Destination for converted Ascii string
2480 * lpiLen [I/O] Input length of lpDstStr/destination for length of lpDstStr
2482 * RETURNS
2483 * Success: The number of characters that result from the conversion.
2484 * Failure: 0.
2486 INT WINAPI SHUnicodeToAnsiCP(UINT CodePage, LPCWSTR lpSrcStr, LPSTR lpDstStr,
2487 LPINT lpiLen)
2489 static const WCHAR emptyW[] = { '\0' };
2490 int len , reqLen;
2491 LPSTR mem;
2493 if (!lpDstStr || !lpiLen)
2494 return 0;
2496 if (!lpSrcStr)
2497 lpSrcStr = emptyW;
2499 *lpDstStr = '\0';
2501 len = strlenW(lpSrcStr) + 1;
2503 switch (CodePage)
2505 case CP_WINUNICODE:
2506 CodePage = CP_UTF8; /* Fall through... */
2507 case 0x0000C350: /* FIXME: CP_ #define */
2508 case CP_UTF7:
2509 case CP_UTF8:
2511 DWORD dwMode = 0;
2512 INT nWideCharCount = len - 1;
2514 if (ConvertINetUnicodeToMultiByte(&dwMode, CodePage, lpSrcStr,
2515 &nWideCharCount, lpDstStr,
2516 lpiLen) == S_OK)
2517 return 0;
2519 if (nWideCharCount < len - 1)
2521 mem = HeapAlloc(GetProcessHeap(), 0, *lpiLen);
2522 if (!mem)
2523 return 0;
2525 *lpiLen = 0;
2527 if (ConvertINetUnicodeToMultiByte(&dwMode, CodePage, lpSrcStr, &len,
2528 mem, lpiLen) != S_OK)
2530 SHTruncateString(mem, *lpiLen);
2531 lstrcpynA(lpDstStr, mem, *lpiLen + 1);
2532 HeapFree(GetProcessHeap(), 0, mem);
2533 return *lpiLen + 1;
2535 HeapFree(GetProcessHeap(), 0, mem);
2536 return *lpiLen;
2538 lpDstStr[*lpiLen] = '\0';
2539 return *lpiLen;
2541 default:
2542 break;
2545 reqLen = WideCharToMultiByte(CodePage, 0, lpSrcStr, len, lpDstStr,
2546 *lpiLen, NULL, NULL);
2548 if (!reqLen && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
2550 reqLen = WideCharToMultiByte(CodePage, 0, lpSrcStr, len, NULL, 0, NULL, NULL);
2551 if (reqLen)
2553 mem = HeapAlloc(GetProcessHeap(), 0, reqLen);
2554 if (mem)
2556 reqLen = WideCharToMultiByte(CodePage, 0, lpSrcStr, len, mem,
2557 reqLen, NULL, NULL);
2559 reqLen = SHTruncateString(mem, *lpiLen);
2560 reqLen++;
2562 lstrcpynA(lpDstStr, mem, *lpiLen);
2564 HeapFree(GetProcessHeap(), 0, mem);
2568 return reqLen;
2571 /*************************************************************************
2572 * @ [SHLWAPI.217]
2574 * Convert a Unicode string to Ascii.
2576 * PARAMS
2577 * lpSrcStr [I] Source Unicode string to convert
2578 * lpDstStr [O] Destination for converted Ascii string
2579 * iLen [O] Length of lpDstStr in characters
2581 * RETURNS
2582 * See SHUnicodeToAnsiCP
2584 * NOTES
2585 * This function simply calls SHUnicodeToAnsiCP() with CodePage = CP_ACP.
2587 INT WINAPI SHUnicodeToAnsi(LPCWSTR lpSrcStr, LPSTR lpDstStr, INT iLen)
2589 INT myint = iLen;
2591 return SHUnicodeToAnsiCP(CP_ACP, lpSrcStr, lpDstStr, &myint);
2594 /*************************************************************************
2595 * @ [SHLWAPI.345]
2597 * Copy one string to another.
2599 * PARAMS
2600 * lpszSrc [I] Source string to copy
2601 * lpszDst [O] Destination for copy
2602 * iLen [I] Length of lpszDst in characters
2604 * RETURNS
2605 * The length of the copied string, including the terminating NUL. lpszDst
2606 * contains iLen characters of lpszSrc.
2608 DWORD WINAPI SHAnsiToAnsi(LPCSTR lpszSrc, LPSTR lpszDst, int iLen)
2610 LPSTR lpszRet;
2612 TRACE("(%s,%p,0x%08x)\n", debugstr_a(lpszSrc), lpszDst, iLen);
2614 lpszRet = StrCpyNXA(lpszDst, lpszSrc, iLen);
2615 return lpszRet - lpszDst + 1;
2618 /*************************************************************************
2619 * @ [SHLWAPI.346]
2621 * Unicode version of SSHAnsiToAnsi.
2623 DWORD WINAPI SHUnicodeToUnicode(LPCWSTR lpszSrc, LPWSTR lpszDst, int iLen)
2625 LPWSTR lpszRet;
2627 TRACE("(%s,%p,0x%08x)\n", debugstr_w(lpszSrc), lpszDst, iLen);
2629 lpszRet = StrCpyNXW(lpszDst, lpszSrc, iLen);
2630 return lpszRet - lpszDst + 1;
2633 /*************************************************************************
2634 * @ [SHLWAPI.364]
2636 * Determine if an Ascii string converts to Unicode and back identically.
2638 * PARAMS
2639 * lpSrcStr [I] Source Unicode string to convert
2640 * lpDst [O] Destination for resulting Ascii string
2641 * iLen [I] Length of lpDst in characters
2643 * RETURNS
2644 * TRUE, since Ascii strings always convert identically.
2646 BOOL WINAPI DoesStringRoundTripA(LPCSTR lpSrcStr, LPSTR lpDst, INT iLen)
2648 lstrcpynA(lpDst, lpSrcStr, iLen);
2649 return TRUE;
2652 /*************************************************************************
2653 * @ [SHLWAPI.365]
2655 * Determine if a Unicode string converts to Ascii and back identically.
2657 * PARAMS
2658 * lpSrcStr [I] Source Unicode string to convert
2659 * lpDst [O] Destination for resulting Ascii string
2660 * iLen [I] Length of lpDst in characters
2662 * RETURNS
2663 * TRUE, if lpSrcStr converts to Ascii and back identically,
2664 * FALSE otherwise.
2666 BOOL WINAPI DoesStringRoundTripW(LPCWSTR lpSrcStr, LPSTR lpDst, INT iLen)
2668 WCHAR szBuff[MAX_PATH];
2670 SHUnicodeToAnsi(lpSrcStr, lpDst, iLen);
2671 SHAnsiToUnicode(lpDst, szBuff, MAX_PATH);
2672 return !strcmpW(lpSrcStr, szBuff);
2675 /*************************************************************************
2676 * SHLoadIndirectString [SHLWAPI.@]
2678 * If passed a string that begins with '@', extract the string from the
2679 * appropriate resource, otherwise do a straight copy.
2682 HRESULT WINAPI SHLoadIndirectString(LPCWSTR src, LPWSTR dst, UINT dst_len, void **reserved)
2684 WCHAR *dllname = NULL;
2685 HMODULE hmod = NULL;
2686 HRESULT hr = E_FAIL;
2688 TRACE("(%s %p %08x %p)\n", debugstr_w(src), dst, dst_len, reserved);
2690 if(src[0] == '@')
2692 WCHAR *index_str;
2693 int index;
2695 dst[0] = 0;
2696 dllname = StrDupW(src + 1);
2697 index_str = strchrW(dllname, ',');
2699 if(!index_str) goto end;
2701 *index_str = 0;
2702 index_str++;
2703 index = atoiW(index_str);
2705 hmod = LoadLibraryW(dllname);
2706 if(!hmod) goto end;
2708 if(index < 0)
2710 if(LoadStringW(hmod, -index, dst, dst_len))
2711 hr = S_OK;
2713 else
2714 FIXME("can't handle non-negative indices (%d)\n", index);
2716 else
2718 if(dst != src)
2719 lstrcpynW(dst, src, dst_len);
2720 hr = S_OK;
2723 TRACE("returning %s\n", debugstr_w(dst));
2724 end:
2725 if(hmod) FreeLibrary(hmod);
2726 HeapFree(GetProcessHeap(), 0, dllname);
2727 return hr;