1 /*
2 * Shlwapi string functions
3 *
4 * Copyright 1998 Juergen Schmied
5 * Copyright 2002 Jon Griffiths
6 *
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.
11 *
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.
16 *
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
20 */
21
22 #include "config.h"
23 #include "wine/port.h"
24
25 #include <math.h>
26 #include <stdarg.h>
27 #include <stdio.h>
28 #include <string.h>
29
30 #define NONAMELESSUNION
31
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"
44
45 #include "resource.h"
46
47 WINE_DEFAULT_DEBUG_CHANNEL(shell);
48
49 extern HINSTANCE shlwapi_hInstance;
50
51 static HRESULT _SHStrDupAA(LPCSTR,LPSTR*);
52 static HRESULT _SHStrDupAW(LPCWSTR,LPSTR*);
53
54
FillNumberFmt(NUMBERFMTW * fmt,LPWSTR decimal_buffer,int decimal_bufwlen,LPWSTR thousand_buffer,int thousand_bufwlen)55 static void FillNumberFmt(NUMBERFMTW *fmt, LPWSTR decimal_buffer, int decimal_bufwlen,
56 LPWSTR thousand_buffer, int thousand_bufwlen)
57 {
58 WCHAR grouping[64];
59 WCHAR *c;
60
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;
68
69 /*
70 * Converting grouping string to number as described on
71 * http://blogs.msdn.com/oldnewthing/archive/2006/04/18/578251.aspx
72 */
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')
77 {
78 fmt->Grouping *= 10;
79 fmt->Grouping += *c - '0';
80 }
81
82 if (fmt->Grouping % 10 == 0)
83 fmt->Grouping /= 10;
84 else
85 fmt->Grouping *= 10;
86 }
87
88 /*************************************************************************
89 * FormatInt [internal]
90 *
91 * Format an integer according to the current locale
92 *
93 * RETURNS
94 * The number of characters written on success or 0 on failure
95 */
FormatInt(LONGLONG qdwValue,LPWSTR pszBuf,int cchBuf)96 static int FormatInt(LONGLONG qdwValue, LPWSTR pszBuf, int cchBuf)
97 {
98 NUMBERFMTW fmt;
99 WCHAR decimal[8], thousand[8];
100 WCHAR buf[24];
101 WCHAR *c;
102 BOOL neg = (qdwValue < 0);
103
104 FillNumberFmt(&fmt, decimal, sizeof decimal / sizeof (WCHAR),
105 thousand, sizeof thousand / sizeof (WCHAR));
106
107 c = &buf[24];
108 *(--c) = 0;
109 do
110 {
111 *(--c) = '0' + (qdwValue%10);
112 qdwValue /= 10;
113 } while (qdwValue > 0);
114 if (neg)
115 *(--c) = '-';
116
117 return GetNumberFormatW(LOCALE_USER_DEFAULT, 0, c, &fmt, pszBuf, cchBuf);
118 }
119
120 /*************************************************************************
121 * FormatDouble [internal]
122 *
123 * Format an integer according to the current locale. Prints the specified number of digits
124 * after the decimal point
125 *
126 * RETURNS
127 * The number of characters written on success or 0 on failure
128 */
FormatDouble(double value,int decimals,LPWSTR pszBuf,int cchBuf)129 static int FormatDouble(double value, int decimals, LPWSTR pszBuf, int cchBuf)
130 {
131 static const WCHAR flfmt[] = {'%','f',0};
132 WCHAR buf[64];
133 NUMBERFMTW fmt;
134 WCHAR decimal[8], thousand[8];
135
136 snprintfW(buf, 64, flfmt, value);
137
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);
142 }
143
144 /*************************************************************************
145 * SHLWAPI_ChrCmpHelperA
146 *
147 * Internal helper for SHLWAPI_ChrCmpA/ChrCMPIA.
148 *
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.
156 */
SHLWAPI_ChrCmpHelperA(WORD ch1,WORD ch2,DWORD dwFlags)157 static BOOL SHLWAPI_ChrCmpHelperA(WORD ch1, WORD ch2, DWORD dwFlags)
158 {
159 char str1[3], str2[3];
160
161 str1[0] = LOBYTE(ch1);
162 if (IsDBCSLeadByte(str1[0]))
163 {
164 str1[1] = HIBYTE(ch1);
165 str1[2] = '\0';
166 }
167 else
168 str1[1] = '\0';
169
170 str2[0] = LOBYTE(ch2);
171 if (IsDBCSLeadByte(str2[0]))
172 {
173 str2[1] = HIBYTE(ch2);
174 str2[2] = '\0';
175 }
176 else
177 str2[1] = '\0';
178
179 return CompareStringA(GetThreadLocale(), dwFlags, str1, -1, str2, -1) - CSTR_EQUAL;
180 }
181
182 /*************************************************************************
183 * SHLWAPI_ChrCmpA
184 *
185 * Internal helper function.
186 */
SHLWAPI_ChrCmpA(WORD ch1,WORD ch2)187 static BOOL WINAPI SHLWAPI_ChrCmpA(WORD ch1, WORD ch2)
188 {
189 return SHLWAPI_ChrCmpHelperA(ch1, ch2, 0);
190 }
191
192 /*************************************************************************
193 * ChrCmpIA (SHLWAPI.385)
194 *
195 * Compare two characters, ignoring case.
196 *
197 * PARAMS
198 * ch1 [I] First character to compare
199 * ch2 [I] Second character to compare
200 *
201 * RETURNS
202 * FALSE, if the characters are equal.
203 * Non-zero otherwise.
204 */
ChrCmpIA(WORD ch1,WORD ch2)205 BOOL WINAPI ChrCmpIA(WORD ch1, WORD ch2)
206 {
207 TRACE("(%d,%d)\n", ch1, ch2);
208
209 return SHLWAPI_ChrCmpHelperA(ch1, ch2, NORM_IGNORECASE);
210 }
211
212 /*************************************************************************
213 * ChrCmpIW [SHLWAPI.386]
214 *
215 * See ChrCmpIA.
216 */
ChrCmpIW(WCHAR ch1,WCHAR ch2)217 BOOL WINAPI ChrCmpIW(WCHAR ch1, WCHAR ch2)
218 {
219 return CompareStringW(GetThreadLocale(), NORM_IGNORECASE, &ch1, 1, &ch2, 1) - CSTR_EQUAL;
220 }
221
222 /*************************************************************************
223 * StrChrA [SHLWAPI.@]
224 *
225 * Find a given character in a string.
226 *
227 * PARAMS
228 * lpszStr [I] String to search in.
229 * ch [I] Character to search for.
230 *
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.
235 */
StrChrA(LPCSTR lpszStr,WORD ch)236 LPSTR WINAPI StrChrA(LPCSTR lpszStr, WORD ch)
237 {
238 TRACE("(%s,%i)\n", debugstr_a(lpszStr), ch);
239
240 if (lpszStr)
241 {
242 while (*lpszStr)
243 {
244 if (!SHLWAPI_ChrCmpA(*lpszStr, ch))
245 return (LPSTR)lpszStr;
246 lpszStr = CharNextA(lpszStr);
247 }
248 }
249 return NULL;
250 }
251
252 /*************************************************************************
253 * StrChrW [SHLWAPI.@]
254 *
255 * See StrChrA.
256 */
StrChrW(LPCWSTR lpszStr,WCHAR ch)257 LPWSTR WINAPI StrChrW(LPCWSTR lpszStr, WCHAR ch)
258 {
259 LPWSTR lpszRet = NULL;
260
261 TRACE("(%s,%i)\n", debugstr_w(lpszStr), ch);
262
263 if (lpszStr)
264 lpszRet = strchrW(lpszStr, ch);
265 return lpszRet;
266 }
267
268 /*************************************************************************
269 * StrChrIA [SHLWAPI.@]
270 *
271 * Find a given character in a string, ignoring case.
272 *
273 * PARAMS
274 * lpszStr [I] String to search in.
275 * ch [I] Character to search for.
276 *
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.
281 */
StrChrIA(LPCSTR lpszStr,WORD ch)282 LPSTR WINAPI StrChrIA(LPCSTR lpszStr, WORD ch)
283 {
284 TRACE("(%s,%i)\n", debugstr_a(lpszStr), ch);
285
286 if (lpszStr)
287 {
288 while (*lpszStr)
289 {
290 if (!ChrCmpIA(*lpszStr, ch))
291 return (LPSTR)lpszStr;
292 lpszStr = CharNextA(lpszStr);
293 }
294 }
295 return NULL;
296 }
297
298 /*************************************************************************
299 * StrChrIW [SHLWAPI.@]
300 *
301 * See StrChrA.
302 */
StrChrIW(LPCWSTR lpszStr,WCHAR ch)303 LPWSTR WINAPI StrChrIW(LPCWSTR lpszStr, WCHAR ch)
304 {
305 TRACE("(%s,%i)\n", debugstr_w(lpszStr), ch);
306
307 if (lpszStr)
308 {
309 ch = toupperW(ch);
310 while (*lpszStr)
311 {
312 if (toupperW(*lpszStr) == ch)
313 return (LPWSTR)lpszStr;
314 lpszStr++;
315 }
316 lpszStr = NULL;
317 }
318 return (LPWSTR)lpszStr;
319 }
320
321 /*************************************************************************
322 * StrChrNW [SHLWAPI.@]
323 */
StrChrNW(LPCWSTR lpszStr,WCHAR ch,UINT cchMax)324 LPWSTR WINAPI StrChrNW(LPCWSTR lpszStr, WCHAR ch, UINT cchMax)
325 {
326 TRACE("(%s(%i),%i)\n", debugstr_wn(lpszStr,cchMax), cchMax, ch);
327
328 if (lpszStr)
329 {
330 while (*lpszStr && cchMax-- > 0)
331 {
332 if (*lpszStr == ch)
333 return (LPWSTR)lpszStr;
334 lpszStr++;
335 }
336 }
337 return NULL;
338 }
339
340 /*************************************************************************
341 * StrCmpIW [SHLWAPI.@]
342 *
343 * Compare two strings, ignoring case.
344 *
345 * PARAMS
346 * lpszStr [I] First string to compare
347 * lpszComp [I] Second string to compare
348 *
349 * RETURNS
350 * An integer less than, equal to or greater than 0, indicating that
351 * lpszStr is less than, the same, or greater than lpszComp.
352 */
StrCmpIW(LPCWSTR lpszStr,LPCWSTR lpszComp)353 int WINAPI StrCmpIW(LPCWSTR lpszStr, LPCWSTR lpszComp)
354 {
355 TRACE("(%s,%s)\n", debugstr_w(lpszStr),debugstr_w(lpszComp));
356 return CompareStringW(GetThreadLocale(), NORM_IGNORECASE, lpszStr, -1, lpszComp, -1) - CSTR_EQUAL;
357 }
358
359 /*************************************************************************
360 * StrCmpNA [SHLWAPI.@]
361 *
362 * Compare two strings, up to a maximum length.
363 *
364 * PARAMS
365 * lpszStr [I] First string to compare
366 * lpszComp [I] Second string to compare
367 * iLen [I] Number of chars to compare
368 *
369 * RETURNS
370 * An integer less than, equal to or greater than 0, indicating that
371 * lpszStr is less than, the same, or greater than lpszComp.
372 */
StrCmpNA(LPCSTR lpszStr,LPCSTR lpszComp,INT iLen)373 INT WINAPI StrCmpNA(LPCSTR lpszStr, LPCSTR lpszComp, INT iLen)
374 {
375 TRACE("(%s,%s,%i)\n", debugstr_a(lpszStr), debugstr_a(lpszComp), iLen);
376 return CompareStringA(GetThreadLocale(), 0, lpszStr, iLen, lpszComp, iLen) - CSTR_EQUAL;
377 }
378
379 /*************************************************************************
380 * StrCmpNW [SHLWAPI.@]
381 *
382 * See StrCmpNA.
383 */
StrCmpNW(LPCWSTR lpszStr,LPCWSTR lpszComp,INT iLen)384 INT WINAPI StrCmpNW(LPCWSTR lpszStr, LPCWSTR lpszComp, INT iLen)
385 {
386 TRACE("(%s,%s,%i)\n", debugstr_w(lpszStr), debugstr_w(lpszComp), iLen);
387 return CompareStringW(GetThreadLocale(), 0, lpszStr, iLen, lpszComp, iLen) - CSTR_EQUAL;
388 }
389
390 /*************************************************************************
391 * StrCmpNIA [SHLWAPI.@]
392 *
393 * Compare two strings, up to a maximum length, ignoring case.
394 *
395 * PARAMS
396 * lpszStr [I] First string to compare
397 * lpszComp [I] Second string to compare
398 * iLen [I] Number of chars to compare
399 *
400 * RETURNS
401 * An integer less than, equal to or greater than 0, indicating that
402 * lpszStr is less than, the same, or greater than lpszComp.
403 */
StrCmpNIA(LPCSTR lpszStr,LPCSTR lpszComp,int iLen)404 int WINAPI StrCmpNIA(LPCSTR lpszStr, LPCSTR lpszComp, int iLen)
405 {
406 TRACE("(%s,%s,%i)\n", debugstr_a(lpszStr), debugstr_a(lpszComp), iLen);
407 return CompareStringA(GetThreadLocale(), NORM_IGNORECASE, lpszStr, iLen, lpszComp, iLen) - CSTR_EQUAL;
408 }
409
410 /*************************************************************************
411 * StrCmpNIW [SHLWAPI.@]
412 *
413 * See StrCmpNIA.
414 */
StrCmpNIW(LPCWSTR lpszStr,LPCWSTR lpszComp,int iLen)415 INT WINAPI StrCmpNIW(LPCWSTR lpszStr, LPCWSTR lpszComp, int iLen)
416 {
417 TRACE("(%s,%s,%i)\n", debugstr_w(lpszStr), debugstr_w(lpszComp), iLen);
418 return CompareStringW(GetThreadLocale(), NORM_IGNORECASE, lpszStr, iLen, lpszComp, iLen) - CSTR_EQUAL;
419 }
420
421 /*************************************************************************
422 * StrCmpW [SHLWAPI.@]
423 *
424 * Compare two strings.
425 *
426 * PARAMS
427 * lpszStr [I] First string to compare
428 * lpszComp [I] Second string to compare
429 *
430 * RETURNS
431 * An integer less than, equal to or greater than 0, indicating that
432 * lpszStr is less than, the same, or greater than lpszComp.
433 */
StrCmpW(LPCWSTR lpszStr,LPCWSTR lpszComp)434 int WINAPI StrCmpW(LPCWSTR lpszStr, LPCWSTR lpszComp)
435 {
436 TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszComp));
437 return CompareStringW(GetThreadLocale(), 0, lpszStr, -1, lpszComp, -1) - CSTR_EQUAL;
438 }
439
440 /*************************************************************************
441 * StrCatW [SHLWAPI.@]
442 *
443 * Concatenate two strings.
444 *
445 * PARAMS
446 * lpszStr [O] Initial string
447 * lpszSrc [I] String to concatenate
448 *
449 * RETURNS
450 * lpszStr.
451 */
StrCatW(LPWSTR lpszStr,LPCWSTR lpszSrc)452 LPWSTR WINAPI StrCatW(LPWSTR lpszStr, LPCWSTR lpszSrc)
453 {
454 TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszSrc));
455
456 if (lpszStr && lpszSrc)
457 strcatW(lpszStr, lpszSrc);
458 return lpszStr;
459 }
460
461 /*************************************************************************
462 * StrCatChainW [SHLWAPI.@]
463 *
464 * Concatenates two unicode strings.
465 *
466 * PARAMS
467 * lpszStr [O] Initial string
468 * cchMax [I] Length of destination buffer
469 * ichAt [I] Offset from the destination buffer to begin concatenation
470 * lpszCat [I] String to concatenate
471 *
472 * RETURNS
473 * The offset from the beginning of pszDst to the terminating NULL.
474 */
StrCatChainW(LPWSTR lpszStr,DWORD cchMax,DWORD ichAt,LPCWSTR lpszCat)475 DWORD WINAPI StrCatChainW(LPWSTR lpszStr, DWORD cchMax, DWORD ichAt, LPCWSTR lpszCat)
476 {
477 TRACE("(%s,%u,%d,%s)\n", debugstr_w(lpszStr), cchMax, ichAt, debugstr_w(lpszCat));
478
479 if (ichAt == -1)
480 ichAt = strlenW(lpszStr);
481
482 if (!cchMax)
483 return ichAt;
484
485 if (ichAt == cchMax)
486 ichAt--;
487
488 if (lpszCat && ichAt < cchMax)
489 {
490 lpszStr += ichAt;
491 while (ichAt < cchMax - 1 && *lpszCat)
492 {
493 *lpszStr++ = *lpszCat++;
494 ichAt++;
495 }
496 *lpszStr = 0;
497 }
498
499 return ichAt;
500 }
501
502 /*************************************************************************
503 * StrCpyW [SHLWAPI.@]
504 *
505 * Copy a string to another string.
506 *
507 * PARAMS
508 * lpszStr [O] Destination string
509 * lpszSrc [I] Source string
510 *
511 * RETURNS
512 * lpszStr.
513 */
StrCpyW(LPWSTR lpszStr,LPCWSTR lpszSrc)514 LPWSTR WINAPI StrCpyW(LPWSTR lpszStr, LPCWSTR lpszSrc)
515 {
516 TRACE("(%p,%s)\n", lpszStr, debugstr_w(lpszSrc));
517
518 if (lpszStr && lpszSrc)
519 strcpyW(lpszStr, lpszSrc);
520 return lpszStr;
521 }
522
523 /*************************************************************************
524 * StrCpyNW [SHLWAPI.@]
525 *
526 * Copy a string to another string, up to a maximum number of characters.
527 *
528 * PARAMS
529 * dst [O] Destination string
530 * src [I] Source string
531 * count [I] Maximum number of chars to copy
532 *
533 * RETURNS
534 * dst.
535 */
StrCpyNW(LPWSTR dst,LPCWSTR src,int count)536 LPWSTR WINAPI StrCpyNW(LPWSTR dst, LPCWSTR src, int count)
537 {
538 LPWSTR d = dst;
539 LPCWSTR s = src;
540
541 TRACE("(%p,%s,%i)\n", dst, debugstr_w(src), count);
542
543 if (s)
544 {
545 while ((count > 1) && *s)
546 {
547 count--;
548 *d++ = *s++;
549 }
550 }
551 if (count) *d = 0;
552
553 return dst;
554 }
555
556 /*************************************************************************
557 * SHLWAPI_StrStrHelperA
558 *
559 * Internal implementation of StrStrA/StrStrIA
560 */
SHLWAPI_StrStrHelperA(LPCSTR lpszStr,LPCSTR lpszSearch,INT (WINAPI * pStrCmpFn)(LPCSTR,LPCSTR,INT))561 static LPSTR SHLWAPI_StrStrHelperA(LPCSTR lpszStr, LPCSTR lpszSearch,
562 INT (WINAPI *pStrCmpFn)(LPCSTR,LPCSTR,INT))
563 {
564 size_t iLen;
565 LPCSTR end;
566
567 if (!lpszStr || !lpszSearch || !*lpszSearch)
568 return NULL;
569
570 iLen = strlen(lpszSearch);
571 end = lpszStr + strlen(lpszStr);
572
573 while (lpszStr + iLen <= end)
574 {
575 if (!pStrCmpFn(lpszStr, lpszSearch, iLen))
576 return (LPSTR)lpszStr;
577 lpszStr = CharNextA(lpszStr);
578 }
579 return NULL;
580 }
581
582 /*************************************************************************
583 * StrStrA [SHLWAPI.@]
584 *
585 * Find a substring within a string.
586 *
587 * PARAMS
588 * lpszStr [I] String to search in
589 * lpszSearch [I] String to look for
590 *
591 * RETURNS
592 * The start of lpszSearch within lpszStr, or NULL if not found.
593 */
StrStrA(LPCSTR lpszStr,LPCSTR lpszSearch)594 LPSTR WINAPI StrStrA(LPCSTR lpszStr, LPCSTR lpszSearch)
595 {
596 TRACE("(%s,%s)\n", debugstr_a(lpszStr), debugstr_a(lpszSearch));
597
598 return SHLWAPI_StrStrHelperA(lpszStr, lpszSearch, StrCmpNA);
599 }
600
601 /*************************************************************************
602 * StrStrW [SHLWAPI.@]
603 *
604 * See StrStrA.
605 */
StrStrW(LPCWSTR lpszStr,LPCWSTR lpszSearch)606 LPWSTR WINAPI StrStrW(LPCWSTR lpszStr, LPCWSTR lpszSearch)
607 {
608 TRACE("(%s, %s)\n", debugstr_w(lpszStr), debugstr_w(lpszSearch));
609
610 if (!lpszStr || !lpszSearch || !*lpszSearch) return NULL;
611 return strstrW( lpszStr, lpszSearch );
612 }
613
614 /*************************************************************************
615 * StrRStrIA [SHLWAPI.@]
616 *
617 * Find the last occurrence of a substring within a string.
618 *
619 * PARAMS
620 * lpszStr [I] String to search in
621 * lpszEnd [I] End of lpszStr
622 * lpszSearch [I] String to look for
623 *
624 * RETURNS
625 * The last occurrence lpszSearch within lpszStr, or NULL if not found.
626 */
StrRStrIA(LPCSTR lpszStr,LPCSTR lpszEnd,LPCSTR lpszSearch)627 LPSTR WINAPI StrRStrIA(LPCSTR lpszStr, LPCSTR lpszEnd, LPCSTR lpszSearch)
628 {
629 LPSTR lpszRet = NULL;
630 WORD ch1, ch2;
631 INT iLen;
632
633 TRACE("(%s,%s)\n", debugstr_a(lpszStr), debugstr_a(lpszSearch));
634
635 if (!lpszStr || !lpszSearch || !*lpszSearch)
636 return NULL;
637
638 if (IsDBCSLeadByte(*lpszSearch))
639 ch1 = *lpszSearch << 8 | (UCHAR)lpszSearch[1];
640 else
641 ch1 = *lpszSearch;
642 iLen = lstrlenA(lpszSearch);
643
644 if (!lpszEnd)
645 lpszEnd = lpszStr + lstrlenA(lpszStr);
646 else /* reproduce the broken behaviour on Windows */
647 lpszEnd += min(iLen - 1, lstrlenA(lpszEnd));
648
649 while (lpszStr + iLen <= lpszEnd && *lpszStr)
650 {
651 ch2 = IsDBCSLeadByte(*lpszStr)? *lpszStr << 8 | (UCHAR)lpszStr[1] : *lpszStr;
652 if (!ChrCmpIA(ch1, ch2))
653 {
654 if (!StrCmpNIA(lpszStr, lpszSearch, iLen))
655 lpszRet = (LPSTR)lpszStr;
656 }
657 lpszStr = CharNextA(lpszStr);
658 }
659 return lpszRet;
660 }
661
662 /*************************************************************************
663 * StrRStrIW [SHLWAPI.@]
664 *
665 * See StrRStrIA.
666 */
StrRStrIW(LPCWSTR lpszStr,LPCWSTR lpszEnd,LPCWSTR lpszSearch)667 LPWSTR WINAPI StrRStrIW(LPCWSTR lpszStr, LPCWSTR lpszEnd, LPCWSTR lpszSearch)
668 {
669 LPWSTR lpszRet = NULL;
670 INT iLen;
671
672 TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszSearch));
673
674 if (!lpszStr || !lpszSearch || !*lpszSearch)
675 return NULL;
676
677 iLen = strlenW(lpszSearch);
678
679 if (!lpszEnd)
680 lpszEnd = lpszStr + strlenW(lpszStr);
681 else /* reproduce the broken behaviour on Windows */
682 lpszEnd += min(iLen - 1, lstrlenW(lpszEnd));
683
684 while (lpszStr + iLen <= lpszEnd && *lpszStr)
685 {
686 if (!ChrCmpIW(*lpszSearch, *lpszStr))
687 {
688 if (!StrCmpNIW(lpszStr, lpszSearch, iLen))
689 lpszRet = (LPWSTR)lpszStr;
690 }
691 lpszStr++;
692 }
693 return lpszRet;
694 }
695
696 /*************************************************************************
697 * StrStrIA [SHLWAPI.@]
698 *
699 * Find a substring within a string, ignoring case.
700 *
701 * PARAMS
702 * lpszStr [I] String to search in
703 * lpszSearch [I] String to look for
704 *
705 * RETURNS
706 * The start of lpszSearch within lpszStr, or NULL if not found.
707 */
StrStrIA(LPCSTR lpszStr,LPCSTR lpszSearch)708 LPSTR WINAPI StrStrIA(LPCSTR lpszStr, LPCSTR lpszSearch)
709 {
710 TRACE("(%s,%s)\n", debugstr_a(lpszStr), debugstr_a(lpszSearch));
711
712 return SHLWAPI_StrStrHelperA(lpszStr, lpszSearch, StrCmpNIA);
713 }
714
715 /*************************************************************************
716 * StrStrIW [SHLWAPI.@]
717 *
718 * See StrStrIA.
719 */
StrStrIW(LPCWSTR lpszStr,LPCWSTR lpszSearch)720 LPWSTR WINAPI StrStrIW(LPCWSTR lpszStr, LPCWSTR lpszSearch)
721 {
722 int iLen;
723 LPCWSTR end;
724
725 TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszSearch));
726
727 if (!lpszStr || !lpszSearch || !*lpszSearch)
728 return NULL;
729
730 iLen = strlenW(lpszSearch);
731 end = lpszStr + strlenW(lpszStr);
732
733 while (lpszStr + iLen <= end)
734 {
735 if (!StrCmpNIW(lpszStr, lpszSearch, iLen))
736 return (LPWSTR)lpszStr;
737 lpszStr++;
738 }
739 return NULL;
740 }
741
742 /*************************************************************************
743 * StrStrNW [SHLWAPI.@]
744 *
745 * Find a substring within a string up to a given number of initial characters.
746 *
747 * PARAMS
748 * lpFirst [I] String to search in
749 * lpSrch [I] String to look for
750 * cchMax [I] Maximum number of initial search characters
751 *
752 * RETURNS
753 * The start of lpFirst within lpSrch, or NULL if not found.
754 */
StrStrNW(LPCWSTR lpFirst,LPCWSTR lpSrch,UINT cchMax)755 LPWSTR WINAPI StrStrNW(LPCWSTR lpFirst, LPCWSTR lpSrch, UINT cchMax)
756 {
757 UINT i;
758 int len;
759
760 TRACE("(%s, %s, %u)\n", debugstr_w(lpFirst), debugstr_w(lpSrch), cchMax);
761
762 if (!lpFirst || !lpSrch || !*lpSrch || !cchMax)
763 return NULL;
764
765 len = strlenW(lpSrch);
766
767 for (i = cchMax; *lpFirst && (i > 0); i--, lpFirst++)
768 {
769 if (!strncmpW(lpFirst, lpSrch, len))
770 return (LPWSTR)lpFirst;
771 }
772
773 return NULL;
774 }
775
776 /*************************************************************************
777 * StrStrNIW [SHLWAPI.@]
778 *
779 * Find a substring within a string up to a given number of initial characters,
780 * ignoring case.
781 *
782 * PARAMS
783 * lpFirst [I] String to search in
784 * lpSrch [I] String to look for
785 * cchMax [I] Maximum number of initial search characters
786 *
787 * RETURNS
788 * The start of lpFirst within lpSrch, or NULL if not found.
789 */
StrStrNIW(LPCWSTR lpFirst,LPCWSTR lpSrch,UINT cchMax)790 LPWSTR WINAPI StrStrNIW(LPCWSTR lpFirst, LPCWSTR lpSrch, UINT cchMax)
791 {
792 UINT i;
793 int len;
794
795 TRACE("(%s, %s, %u)\n", debugstr_w(lpFirst), debugstr_w(lpSrch), cchMax);
796
797 if (!lpFirst || !lpSrch || !*lpSrch || !cchMax)
798 return NULL;
799
800 len = strlenW(lpSrch);
801
802 for (i = cchMax; *lpFirst && (i > 0); i--, lpFirst++)
803 {
804 if (!strncmpiW(lpFirst, lpSrch, len))
805 return (LPWSTR)lpFirst;
806 }
807
808 return NULL;
809 }
810
811 /*************************************************************************
812 * StrToIntA [SHLWAPI.@]
813 *
814 * Read a signed integer from a string.
815 *
816 * PARAMS
817 * lpszStr [I] String to read integer from
818 *
819 * RETURNS
820 * The signed integer value represented by the string, or 0 if no integer is
821 * present.
822 *
823 * NOTES
824 * No leading space is allowed before the number, although a leading '-' is.
825 */
StrToIntA(LPCSTR lpszStr)826 int WINAPI StrToIntA(LPCSTR lpszStr)
827 {
828 int iRet = 0;
829
830 TRACE("(%s)\n", debugstr_a(lpszStr));
831
832 if (!lpszStr)
833 {
834 WARN("Invalid lpszStr would crash under Win32!\n");
835 return 0;
836 }
837
838 if (*lpszStr == '-' || isdigit(*lpszStr))
839 StrToIntExA(lpszStr, 0, &iRet);
840 return iRet;
841 }
842
843 /*************************************************************************
844 * StrToIntW [SHLWAPI.@]
845 *
846 * See StrToIntA.
847 */
StrToIntW(LPCWSTR lpszStr)848 int WINAPI StrToIntW(LPCWSTR lpszStr)
849 {
850 int iRet = 0;
851
852 TRACE("(%s)\n", debugstr_w(lpszStr));
853
854 if (!lpszStr)
855 {
856 WARN("Invalid lpszStr would crash under Win32!\n");
857 return 0;
858 }
859
860 if (*lpszStr == '-' || isdigitW(*lpszStr))
861 StrToIntExW(lpszStr, 0, &iRet);
862 return iRet;
863 }
864
865 /*************************************************************************
866 * StrToIntExA [SHLWAPI.@]
867 *
868 * Read an integer from a string.
869 *
870 * PARAMS
871 * lpszStr [I] String to read integer from
872 * dwFlags [I] Flags controlling the conversion
873 * lpiRet [O] Destination for read integer.
874 *
875 * RETURNS
876 * Success: TRUE. lpiRet contains the integer value represented by the string.
877 * Failure: FALSE, if the string is invalid, or no number is present.
878 *
879 * NOTES
880 * Leading whitespace, '-' and '+' are allowed before the number. If
881 * dwFlags includes STIF_SUPPORT_HEX, hexadecimal numbers are allowed, if
882 * preceded by '0x'. If this flag is not set, or there is no '0x' prefix,
883 * the string is treated as a decimal string. A leading '-' is ignored for
884 * hexadecimal numbers.
885 */
StrToIntExA(LPCSTR lpszStr,DWORD dwFlags,LPINT lpiRet)886 BOOL WINAPI StrToIntExA(LPCSTR lpszStr, DWORD dwFlags, LPINT lpiRet)
887 {
888 LONGLONG li;
889 BOOL bRes;
890
891 TRACE("(%s,%08X,%p)\n", debugstr_a(lpszStr), dwFlags, lpiRet);
892
893 bRes = StrToInt64ExA(lpszStr, dwFlags, &li);
894 if (bRes) *lpiRet = li;
895 return bRes;
896 }
897
898 /*************************************************************************
899 * StrToInt64ExA [SHLWAPI.@]
900 *
901 * See StrToIntExA.
902 */
StrToInt64ExA(LPCSTR lpszStr,DWORD dwFlags,LONGLONG * lpiRet)903 BOOL WINAPI StrToInt64ExA(LPCSTR lpszStr, DWORD dwFlags, LONGLONG *lpiRet)
904 {
905 BOOL bNegative = FALSE;
906 LONGLONG iRet = 0;
907
908 TRACE("(%s,%08X,%p)\n", debugstr_a(lpszStr), dwFlags, lpiRet);
909
910 if (!lpszStr || !lpiRet)
911 {
912 WARN("Invalid parameter would crash under Win32!\n");
913 return FALSE;
914 }
915 if (dwFlags > STIF_SUPPORT_HEX) WARN("Unknown flags %08x\n", dwFlags);
916
917 /* Skip leading space, '+', '-' */
918 while (isspace(*lpszStr))
919 lpszStr = CharNextA(lpszStr);
920
921 if (*lpszStr == '-')
922 {
923 bNegative = TRUE;
924 lpszStr++;
925 }
926 else if (*lpszStr == '+')
927 lpszStr++;
928
929 if (dwFlags & STIF_SUPPORT_HEX &&
930 *lpszStr == '0' && tolower(lpszStr[1]) == 'x')
931 {
932 /* Read hex number */
933 lpszStr += 2;
934
935 if (!isxdigit(*lpszStr))
936 return FALSE;
937
938 while (isxdigit(*lpszStr))
939 {
940 iRet = iRet * 16;
941 if (isdigit(*lpszStr))
942 iRet += (*lpszStr - '0');
943 else
944 iRet += 10 + (tolower(*lpszStr) - 'a');
945 lpszStr++;
946 }
947 *lpiRet = iRet;
948 return TRUE;
949 }
950
951 /* Read decimal number */
952 if (!isdigit(*lpszStr))
953 return FALSE;
954
955 while (isdigit(*lpszStr))
956 {
957 iRet = iRet * 10;
958 iRet += (*lpszStr - '0');
959 lpszStr++;
960 }
961 *lpiRet = bNegative ? -iRet : iRet;
962 return TRUE;
963 }
964
965 /*************************************************************************
966 * StrToIntExW [SHLWAPI.@]
967 *
968 * See StrToIntExA.
969 */
StrToIntExW(LPCWSTR lpszStr,DWORD dwFlags,LPINT lpiRet)970 BOOL WINAPI StrToIntExW(LPCWSTR lpszStr, DWORD dwFlags, LPINT lpiRet)
971 {
972 LONGLONG li;
973 BOOL bRes;
974
975 TRACE("(%s,%08X,%p)\n", debugstr_w(lpszStr), dwFlags, lpiRet);
976
977 bRes = StrToInt64ExW(lpszStr, dwFlags, &li);
978 if (bRes) *lpiRet = li;
979 return bRes;
980 }
981
982 /*************************************************************************
983 * StrToInt64ExW [SHLWAPI.@]
984 *
985 * See StrToIntExA.
986 */
StrToInt64ExW(LPCWSTR lpszStr,DWORD dwFlags,LONGLONG * lpiRet)987 BOOL WINAPI StrToInt64ExW(LPCWSTR lpszStr, DWORD dwFlags, LONGLONG *lpiRet)
988 {
989 BOOL bNegative = FALSE;
990 LONGLONG iRet = 0;
991
992 TRACE("(%s,%08X,%p)\n", debugstr_w(lpszStr), dwFlags, lpiRet);
993
994 if (!lpszStr || !lpiRet)
995 {
996 WARN("Invalid parameter would crash under Win32!\n");
997 return FALSE;
998 }
999 if (dwFlags > STIF_SUPPORT_HEX) WARN("Unknown flags %08x\n", dwFlags);
1000
1001 /* Skip leading space, '+', '-' */
1002 while (isspaceW(*lpszStr)) lpszStr++;
1003
1004 if (*lpszStr == '-')
1005 {
1006 bNegative = TRUE;
1007 lpszStr++;
1008 }
1009 else if (*lpszStr == '+')
1010 lpszStr++;
1011
1012 if (dwFlags & STIF_SUPPORT_HEX &&
1013 *lpszStr == '0' && tolowerW(lpszStr[1]) == 'x')
1014 {
1015 /* Read hex number */
1016 lpszStr += 2;
1017
1018 if (!isxdigitW(*lpszStr))
1019 return FALSE;
1020
1021 while (isxdigitW(*lpszStr))
1022 {
1023 iRet = iRet * 16;
1024 if (isdigitW(*lpszStr))
1025 iRet += (*lpszStr - '0');
1026 else
1027 iRet += 10 + (tolowerW(*lpszStr) - 'a');
1028 lpszStr++;
1029 }
1030 *lpiRet = iRet;
1031 return TRUE;
1032 }
1033
1034 /* Read decimal number */
1035 if (!isdigitW(*lpszStr))
1036 return FALSE;
1037
1038 while (isdigitW(*lpszStr))
1039 {
1040 iRet = iRet * 10;
1041 iRet += (*lpszStr - '0');
1042 lpszStr++;
1043 }
1044 *lpiRet = bNegative ? -iRet : iRet;
1045 return TRUE;
1046 }
1047
1048 /*************************************************************************
1049 * StrDupA [SHLWAPI.@]
1050 *
1051 * Duplicate a string.
1052 *
1053 * PARAMS
1054 * lpszStr [I] String to duplicate.
1055 *
1056 * RETURNS
1057 * Success: A pointer to a new string containing the contents of lpszStr
1058 * Failure: NULL, if memory cannot be allocated
1059 *
1060 * NOTES
1061 * The string memory is allocated with LocalAlloc(), and so should be released
1062 * by calling LocalFree().
1063 */
StrDupA(LPCSTR lpszStr)1064 LPSTR WINAPI StrDupA(LPCSTR lpszStr)
1065 {
1066 int iLen;
1067 LPSTR lpszRet;
1068
1069 TRACE("(%s)\n",debugstr_a(lpszStr));
1070
1071 #ifdef __REACTOS__
1072 if (!lpszStr)
1073 return NULL;
1074 #endif
1075 iLen = lpszStr ? strlen(lpszStr) + 1 : 1;
1076 lpszRet = LocalAlloc(LMEM_FIXED, iLen);
1077
1078 if (lpszRet)
1079 {
1080 if (lpszStr)
1081 memcpy(lpszRet, lpszStr, iLen);
1082 else
1083 *lpszRet = '\0';
1084 }
1085 return lpszRet;
1086 }
1087
1088 /*************************************************************************
1089 * StrDupW [SHLWAPI.@]
1090 *
1091 * See StrDupA.
1092 */
StrDupW(LPCWSTR lpszStr)1093 LPWSTR WINAPI StrDupW(LPCWSTR lpszStr)
1094 {
1095 int iLen;
1096 LPWSTR lpszRet;
1097
1098 TRACE("(%s)\n",debugstr_w(lpszStr));
1099
1100 #ifdef __REACTOS__
1101 if (!lpszStr)
1102 return NULL;
1103 #endif
1104 iLen = (lpszStr ? strlenW(lpszStr) + 1 : 1) * sizeof(WCHAR);
1105 lpszRet = LocalAlloc(LMEM_FIXED, iLen);
1106
1107 if (lpszRet)
1108 {
1109 if (lpszStr)
1110 memcpy(lpszRet, lpszStr, iLen);
1111 else
1112 *lpszRet = '\0';
1113 }
1114 return lpszRet;
1115 }
1116
1117 /*************************************************************************
1118 * SHLWAPI_StrSpnHelperA
1119 *
1120 * Internal implementation of StrSpnA/StrCSpnA/StrCSpnIA
1121 */
SHLWAPI_StrSpnHelperA(LPCSTR lpszStr,LPCSTR lpszMatch,LPSTR (WINAPI * pStrChrFn)(LPCSTR,WORD),BOOL bInvert)1122 static int SHLWAPI_StrSpnHelperA(LPCSTR lpszStr, LPCSTR lpszMatch,
1123 LPSTR (WINAPI *pStrChrFn)(LPCSTR,WORD),
1124 BOOL bInvert)
1125 {
1126 LPCSTR lpszRead = lpszStr;
1127 if (lpszStr && *lpszStr && lpszMatch)
1128 {
1129 while (*lpszRead)
1130 {
1131 LPCSTR lpszTest = pStrChrFn(lpszMatch, *lpszRead);
1132
1133 if (!bInvert && !lpszTest)
1134 break;
1135 if (bInvert && lpszTest)
1136 break;
1137 lpszRead = CharNextA(lpszRead);
1138 };
1139 }
1140 return lpszRead - lpszStr;
1141 }
1142
1143 /*************************************************************************
1144 * StrSpnA [SHLWAPI.@]
1145 *
1146 * Find the length of the start of a string that contains only certain
1147 * characters.
1148 *
1149 * PARAMS
1150 * lpszStr [I] String to search
1151 * lpszMatch [I] Characters that can be in the substring
1152 *
1153 * RETURNS
1154 * The length of the part of lpszStr containing only chars from lpszMatch,
1155 * or 0 if any parameter is invalid.
1156 */
StrSpnA(LPCSTR lpszStr,LPCSTR lpszMatch)1157 int WINAPI StrSpnA(LPCSTR lpszStr, LPCSTR lpszMatch)
1158 {
1159 TRACE("(%s,%s)\n",debugstr_a(lpszStr), debugstr_a(lpszMatch));
1160
1161 return SHLWAPI_StrSpnHelperA(lpszStr, lpszMatch, StrChrA, FALSE);
1162 }
1163
1164 /*************************************************************************
1165 * StrSpnW [SHLWAPI.@]
1166 *
1167 * See StrSpnA.
1168 */
StrSpnW(LPCWSTR lpszStr,LPCWSTR lpszMatch)1169 int WINAPI StrSpnW(LPCWSTR lpszStr, LPCWSTR lpszMatch)
1170 {
1171 if (!lpszStr || !lpszMatch) return 0;
1172 return strspnW( lpszStr, lpszMatch );
1173 }
1174
1175 /*************************************************************************
1176 * StrCSpnA [SHLWAPI.@]
1177 *
1178 * Find the length of the start of a string that does not contain certain
1179 * characters.
1180 *
1181 * PARAMS
1182 * lpszStr [I] String to search
1183 * lpszMatch [I] Characters that cannot be in the substring
1184 *
1185 * RETURNS
1186 * The length of the part of lpszStr containing only chars not in lpszMatch,
1187 * or 0 if any parameter is invalid.
1188 */
StrCSpnA(LPCSTR lpszStr,LPCSTR lpszMatch)1189 int WINAPI StrCSpnA(LPCSTR lpszStr, LPCSTR lpszMatch)
1190 {
1191 TRACE("(%s,%s)\n",debugstr_a(lpszStr), debugstr_a(lpszMatch));
1192
1193 return SHLWAPI_StrSpnHelperA(lpszStr, lpszMatch, StrChrA, TRUE);
1194 }
1195
1196 /*************************************************************************
1197 * StrCSpnW [SHLWAPI.@]
1198 *
1199 * See StrCSpnA.
1200 */
StrCSpnW(LPCWSTR lpszStr,LPCWSTR lpszMatch)1201 int WINAPI StrCSpnW(LPCWSTR lpszStr, LPCWSTR lpszMatch)
1202 {
1203 if (!lpszStr || !lpszMatch) return 0;
1204 return strcspnW( lpszStr, lpszMatch );
1205 }
1206
1207 /*************************************************************************
1208 * StrCSpnIA [SHLWAPI.@]
1209 *
1210 * Find the length of the start of a string that does not contain certain
1211 * characters, ignoring case.
1212 *
1213 * PARAMS
1214 * lpszStr [I] String to search
1215 * lpszMatch [I] Characters that cannot be in the substring
1216 *
1217 * RETURNS
1218 * The length of the part of lpszStr containing only chars not in lpszMatch,
1219 * or 0 if any parameter is invalid.
1220 */
StrCSpnIA(LPCSTR lpszStr,LPCSTR lpszMatch)1221 int WINAPI StrCSpnIA(LPCSTR lpszStr, LPCSTR lpszMatch)
1222 {
1223 TRACE("(%s,%s)\n",debugstr_a(lpszStr), debugstr_a(lpszMatch));
1224
1225 return SHLWAPI_StrSpnHelperA(lpszStr, lpszMatch, StrChrIA, TRUE);
1226 }
1227
1228 /*************************************************************************
1229 * StrCSpnIW [SHLWAPI.@]
1230 *
1231 * See StrCSpnIA.
1232 */
StrCSpnIW(LPCWSTR lpszStr,LPCWSTR lpszMatch)1233 int WINAPI StrCSpnIW(LPCWSTR lpszStr, LPCWSTR lpszMatch)
1234 {
1235 LPCWSTR lpszRead = lpszStr;
1236
1237 TRACE("(%s,%s)\n",debugstr_w(lpszStr), debugstr_w(lpszMatch));
1238
1239 if (lpszStr && *lpszStr && lpszMatch)
1240 {
1241 while (*lpszRead)
1242 {
1243 if (StrChrIW(lpszMatch, *lpszRead)) break;
1244 lpszRead++;
1245 }
1246 }
1247 return lpszRead - lpszStr;
1248 }
1249
1250 /*************************************************************************
1251 * StrPBrkA [SHLWAPI.@]
1252 *
1253 * Search a string for any of a group of characters.
1254 *
1255 * PARAMS
1256 * lpszStr [I] String to search
1257 * lpszMatch [I] Characters to match
1258 *
1259 * RETURNS
1260 * A pointer to the first matching character in lpszStr, or NULL if no
1261 * match was found.
1262 */
StrPBrkA(LPCSTR lpszStr,LPCSTR lpszMatch)1263 LPSTR WINAPI StrPBrkA(LPCSTR lpszStr, LPCSTR lpszMatch)
1264 {
1265 TRACE("(%s,%s)\n",debugstr_a(lpszStr), debugstr_a(lpszMatch));
1266
1267 if (lpszStr && lpszMatch && *lpszMatch)
1268 {
1269 while (*lpszStr)
1270 {
1271 if (StrChrA(lpszMatch, *lpszStr))
1272 return (LPSTR)lpszStr;
1273 lpszStr = CharNextA(lpszStr);
1274 }
1275 }
1276 return NULL;
1277 }
1278
1279 /*************************************************************************
1280 * StrPBrkW [SHLWAPI.@]
1281 *
1282 * See StrPBrkA.
1283 */
StrPBrkW(LPCWSTR lpszStr,LPCWSTR lpszMatch)1284 LPWSTR WINAPI StrPBrkW(LPCWSTR lpszStr, LPCWSTR lpszMatch)
1285 {
1286 if (!lpszStr || !lpszMatch) return NULL;
1287 return strpbrkW( lpszStr, lpszMatch );
1288 }
1289
1290 /*************************************************************************
1291 * SHLWAPI_StrRChrHelperA
1292 *
1293 * Internal implementation of StrRChrA/StrRChrIA.
1294 */
SHLWAPI_StrRChrHelperA(LPCSTR lpszStr,LPCSTR lpszEnd,WORD ch,BOOL (WINAPI * pChrCmpFn)(WORD,WORD))1295 static LPSTR SHLWAPI_StrRChrHelperA(LPCSTR lpszStr,
1296 LPCSTR lpszEnd, WORD ch,
1297 BOOL (WINAPI *pChrCmpFn)(WORD,WORD))
1298 {
1299 LPCSTR lpszRet = NULL;
1300
1301 if (lpszStr)
1302 {
1303 WORD ch2;
1304
1305 if (!lpszEnd)
1306 lpszEnd = lpszStr + lstrlenA(lpszStr);
1307
1308 while (*lpszStr && lpszStr <= lpszEnd)
1309 {
1310 ch2 = IsDBCSLeadByte(*lpszStr)? *lpszStr << 8 | lpszStr[1] : *lpszStr;
1311
1312 if (!pChrCmpFn(ch, ch2))
1313 lpszRet = lpszStr;
1314 lpszStr = CharNextA(lpszStr);
1315 }
1316 }
1317 return (LPSTR)lpszRet;
1318 }
1319
1320 /**************************************************************************
1321 * StrRChrA [SHLWAPI.@]
1322 *
1323 * Find the last occurrence of a character in string.
1324 *
1325 * PARAMS
1326 * lpszStr [I] String to search in
1327 * lpszEnd [I] Place to end search, or NULL to search until the end of lpszStr
1328 * ch [I] Character to search for.
1329 *
1330 * RETURNS
1331 * Success: A pointer to the last occurrence of ch in lpszStr before lpszEnd,
1332 * or NULL if not found.
1333 * Failure: NULL, if any arguments are invalid.
1334 */
StrRChrA(LPCSTR lpszStr,LPCSTR lpszEnd,WORD ch)1335 LPSTR WINAPI StrRChrA(LPCSTR lpszStr, LPCSTR lpszEnd, WORD ch)
1336 {
1337 TRACE("(%s,%s,%x)\n", debugstr_a(lpszStr), debugstr_a(lpszEnd), ch);
1338
1339 return SHLWAPI_StrRChrHelperA(lpszStr, lpszEnd, ch, SHLWAPI_ChrCmpA);
1340 }
1341
1342 /**************************************************************************
1343 * StrRChrW [SHLWAPI.@]
1344 *
1345 * See StrRChrA.
1346 */
StrRChrW(LPCWSTR str,LPCWSTR end,WORD ch)1347 LPWSTR WINAPI StrRChrW(LPCWSTR str, LPCWSTR end, WORD ch)
1348 {
1349 WCHAR *ret = NULL;
1350
1351 if (!str) return NULL;
1352 if (!end) end = str + strlenW(str);
1353 while (str < end)
1354 {
1355 if (*str == ch) ret = (WCHAR *)str;
1356 str++;
1357 }
1358 return ret;
1359 }
1360
1361 /**************************************************************************
1362 * StrRChrIA [SHLWAPI.@]
1363 *
1364 * Find the last occurrence of a character in string, ignoring case.
1365 *
1366 * PARAMS
1367 * lpszStr [I] String to search in
1368 * lpszEnd [I] Place to end search, or NULL to search until the end of lpszStr
1369 * ch [I] Character to search for.
1370 *
1371 * RETURNS
1372 * Success: A pointer to the last occurrence of ch in lpszStr before lpszEnd,
1373 * or NULL if not found.
1374 * Failure: NULL, if any arguments are invalid.
1375 */
StrRChrIA(LPCSTR lpszStr,LPCSTR lpszEnd,WORD ch)1376 LPSTR WINAPI StrRChrIA(LPCSTR lpszStr, LPCSTR lpszEnd, WORD ch)
1377 {
1378 TRACE("(%s,%s,%x)\n", debugstr_a(lpszStr), debugstr_a(lpszEnd), ch);
1379
1380 return SHLWAPI_StrRChrHelperA(lpszStr, lpszEnd, ch, ChrCmpIA);
1381 }
1382
1383 /**************************************************************************
1384 * StrRChrIW [SHLWAPI.@]
1385 *
1386 * See StrRChrIA.
1387 */
StrRChrIW(LPCWSTR str,LPCWSTR end,WORD ch)1388 LPWSTR WINAPI StrRChrIW(LPCWSTR str, LPCWSTR end, WORD ch)
1389 {
1390 WCHAR *ret = NULL;
1391
1392 if (!str) return NULL;
1393 if (!end) end = str + strlenW(str);
1394 while (str < end)
1395 {
1396 if (!ChrCmpIW(*str, ch)) ret = (WCHAR *)str;
1397 str++;
1398 }
1399 return ret;
1400 }
1401
1402 /*************************************************************************
1403 * StrCatBuffA [SHLWAPI.@]
1404 *
1405 * Concatenate two strings together.
1406 *
1407 * PARAMS
1408 * lpszStr [O] String to concatenate to
1409 * lpszCat [I] String to add to lpszCat
1410 * cchMax [I] Maximum number of characters for the whole string
1411 *
1412 * RETURNS
1413 * lpszStr.
1414 *
1415 * NOTES
1416 * cchMax determines the number of characters in the final length of the
1417 * string, not the number appended to lpszStr from lpszCat.
1418 */
StrCatBuffA(LPSTR lpszStr,LPCSTR lpszCat,INT cchMax)1419 LPSTR WINAPI StrCatBuffA(LPSTR lpszStr, LPCSTR lpszCat, INT cchMax)
1420 {
1421 INT iLen;
1422
1423 TRACE("(%p,%s,%d)\n", lpszStr, debugstr_a(lpszCat), cchMax);
1424
1425 if (!lpszStr)
1426 {
1427 WARN("Invalid lpszStr would crash under Win32!\n");
1428 return NULL;
1429 }
1430
1431 iLen = strlen(lpszStr);
1432 cchMax -= iLen;
1433
1434 if (cchMax > 0)
1435 StrCpyNA(lpszStr + iLen, lpszCat, cchMax);
1436 return lpszStr;
1437 }
1438
1439 /*************************************************************************
1440 * StrCatBuffW [SHLWAPI.@]
1441 *
1442 * See StrCatBuffA.
1443 */
StrCatBuffW(LPWSTR lpszStr,LPCWSTR lpszCat,INT cchMax)1444 LPWSTR WINAPI StrCatBuffW(LPWSTR lpszStr, LPCWSTR lpszCat, INT cchMax)
1445 {
1446 INT iLen;
1447
1448 TRACE("(%p,%s,%d)\n", lpszStr, debugstr_w(lpszCat), cchMax);
1449
1450 if (!lpszStr)
1451 {
1452 WARN("Invalid lpszStr would crash under Win32!\n");
1453 return NULL;
1454 }
1455
1456 iLen = strlenW(lpszStr);
1457 cchMax -= iLen;
1458
1459 if (cchMax > 0)
1460 StrCpyNW(lpszStr + iLen, lpszCat, cchMax);
1461 return lpszStr;
1462 }
1463
1464 /*************************************************************************
1465 * StrRetToBufA [SHLWAPI.@]
1466 *
1467 * Convert a STRRET to a normal string.
1468 *
1469 * PARAMS
1470 * lpStrRet [O] STRRET to convert
1471 * pIdl [I] ITEMIDLIST for lpStrRet->uType == STRRET_OFFSET
1472 * lpszDest [O] Destination for normal string
1473 * dwLen [I] Length of lpszDest
1474 *
1475 * RETURNS
1476 * Success: S_OK. lpszDest contains up to dwLen characters of the string.
1477 * If lpStrRet is of type STRRET_WSTR, its memory is freed with
1478 * CoTaskMemFree() and its type set to STRRET_CSTRA.
1479 * Failure: E_FAIL, if any parameters are invalid.
1480 */
StrRetToBufA(LPSTRRET src,const ITEMIDLIST * pidl,LPSTR dest,UINT len)1481 HRESULT WINAPI StrRetToBufA (LPSTRRET src, const ITEMIDLIST *pidl, LPSTR dest, UINT len)
1482 {
1483 /* NOTE:
1484 * This routine is identical to that in dlls/shell32/shellstring.c.
1485 * It was duplicated because not every version of Shlwapi.dll exports
1486 * StrRetToBufA. If you change one routine, change them both.
1487 */
1488 TRACE("dest=%p len=0x%x strret=%p pidl=%p\n", dest, len, src, pidl);
1489
1490 if (!src)
1491 {
1492 WARN("Invalid lpStrRet would crash under Win32!\n");
1493 if (dest)
1494 *dest = '\0';
1495 return E_FAIL;
1496 }
1497
1498 if (!dest || !len)
1499 return E_FAIL;
1500
1501 *dest = '\0';
1502
1503 switch (src->uType)
1504 {
1505 case STRRET_WSTR:
1506 WideCharToMultiByte(CP_ACP, 0, src->u.pOleStr, -1, dest, len, NULL, NULL);
1507 CoTaskMemFree(src->u.pOleStr);
1508 break;
1509
1510 case STRRET_CSTR:
1511 lstrcpynA(dest, src->u.cStr, len);
1512 break;
1513
1514 case STRRET_OFFSET:
1515 lstrcpynA((LPSTR)dest, ((LPCSTR)&pidl->mkid)+src->u.uOffset, len);
1516 break;
1517
1518 default:
1519 FIXME("unknown type!\n");
1520 return E_NOTIMPL;
1521 }
1522 return S_OK;
1523 }
1524
1525 /*************************************************************************
1526 * StrRetToBufW [SHLWAPI.@]
1527 *
1528 * See StrRetToBufA.
1529 */
StrRetToBufW(LPSTRRET src,const ITEMIDLIST * pidl,LPWSTR dest,UINT len)1530 HRESULT WINAPI StrRetToBufW (LPSTRRET src, const ITEMIDLIST *pidl, LPWSTR dest, UINT len)
1531 {
1532 TRACE("dest=%p len=0x%x strret=%p pidl=%p\n", dest, len, src, pidl);
1533
1534 if (!dest || !len)
1535 return E_FAIL;
1536
1537 if (!src)
1538 {
1539 WARN("Invalid lpStrRet would crash under Win32!\n");
1540 if (dest)
1541 *dest = '\0';
1542 return E_FAIL;
1543 }
1544
1545 *dest = '\0';
1546
1547 switch (src->uType) {
1548 case STRRET_WSTR: {
1549 size_t dst_len;
1550 if (!src->u.pOleStr)
1551 return E_FAIL;
1552 dst_len = strlenW(src->u.pOleStr);
1553 memcpy(dest, src->u.pOleStr, min(dst_len, len-1) * sizeof(WCHAR));
1554 dest[min(dst_len, len-1)] = 0;
1555 CoTaskMemFree(src->u.pOleStr);
1556 if (len <= dst_len)
1557 {
1558 dest[0] = 0;
1559 return E_NOT_SUFFICIENT_BUFFER;
1560 }
1561 break;
1562 }
1563
1564 case STRRET_CSTR:
1565 if (!MultiByteToWideChar( CP_ACP, 0, src->u.cStr, -1, dest, len ))
1566 dest[len-1] = 0;
1567 break;
1568
1569 case STRRET_OFFSET:
1570 if (pidl)
1571 {
1572 if (!MultiByteToWideChar( CP_ACP, 0, ((LPCSTR)&pidl->mkid)+src->u.uOffset, -1,
1573 dest, len ))
1574 dest[len-1] = 0;
1575 }
1576 break;
1577
1578 default:
1579 FIXME("unknown type!\n");
1580 return E_NOTIMPL;
1581 }
1582
1583 return S_OK;
1584 }
1585
1586 /*************************************************************************
1587 * StrRetToStrA [SHLWAPI.@]
1588 *
1589 * Converts a STRRET to a normal string.
1590 *
1591 * PARAMS
1592 * lpStrRet [O] STRRET to convert
1593 * pidl [I] ITEMIDLIST for lpStrRet->uType == STRRET_OFFSET
1594 * ppszName [O] Destination for converted string
1595 *
1596 * RETURNS
1597 * Success: S_OK. ppszName contains the new string, allocated with CoTaskMemAlloc().
1598 * Failure: E_FAIL, if any parameters are invalid.
1599 */
StrRetToStrA(LPSTRRET lpStrRet,const ITEMIDLIST * pidl,LPSTR * ppszName)1600 HRESULT WINAPI StrRetToStrA(LPSTRRET lpStrRet, const ITEMIDLIST *pidl, LPSTR *ppszName)
1601 {
1602 HRESULT hRet = E_FAIL;
1603
1604 switch (lpStrRet->uType)
1605 {
1606 case STRRET_WSTR:
1607 hRet = _SHStrDupAW(lpStrRet->u.pOleStr, ppszName);
1608 CoTaskMemFree(lpStrRet->u.pOleStr);
1609 break;
1610
1611 case STRRET_CSTR:
1612 hRet = _SHStrDupAA(lpStrRet->u.cStr, ppszName);
1613 break;
1614
1615 case STRRET_OFFSET:
1616 hRet = _SHStrDupAA(((LPCSTR)&pidl->mkid) + lpStrRet->u.uOffset, ppszName);
1617 break;
1618
1619 default:
1620 *ppszName = NULL;
1621 }
1622
1623 return hRet;
1624 }
1625
1626 /*************************************************************************
1627 * StrRetToStrW [SHLWAPI.@]
1628 *
1629 * See StrRetToStrA.
1630 */
StrRetToStrW(LPSTRRET lpStrRet,const ITEMIDLIST * pidl,LPWSTR * ppszName)1631 HRESULT WINAPI StrRetToStrW(LPSTRRET lpStrRet, const ITEMIDLIST *pidl, LPWSTR *ppszName)
1632 {
1633 HRESULT hRet = E_FAIL;
1634
1635 switch (lpStrRet->uType)
1636 {
1637 case STRRET_WSTR:
1638 hRet = SHStrDupW(lpStrRet->u.pOleStr, ppszName);
1639 CoTaskMemFree(lpStrRet->u.pOleStr);
1640 break;
1641
1642 case STRRET_CSTR:
1643 hRet = SHStrDupA(lpStrRet->u.cStr, ppszName);
1644 break;
1645
1646 case STRRET_OFFSET:
1647 hRet = SHStrDupA(((LPCSTR)&pidl->mkid) + lpStrRet->u.uOffset, ppszName);
1648 break;
1649
1650 default:
1651 *ppszName = NULL;
1652 }
1653
1654 return hRet;
1655 }
1656
1657 /* Create an ASCII string copy using SysAllocString() */
_SHStrDupAToBSTR(LPCSTR src,BSTR * pBstrOut)1658 static HRESULT _SHStrDupAToBSTR(LPCSTR src, BSTR *pBstrOut)
1659 {
1660 *pBstrOut = NULL;
1661
1662 if (src)
1663 {
1664 INT len = MultiByteToWideChar(CP_ACP, 0, src, -1, NULL, 0);
1665 WCHAR* szTemp = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1666
1667 if (szTemp)
1668 {
1669 MultiByteToWideChar(CP_ACP, 0, src, -1, szTemp, len);
1670 *pBstrOut = SysAllocString(szTemp);
1671 HeapFree(GetProcessHeap(), 0, szTemp);
1672
1673 if (*pBstrOut)
1674 return S_OK;
1675 }
1676 }
1677 return E_OUTOFMEMORY;
1678 }
1679
1680 /*************************************************************************
1681 * StrRetToBSTR [SHLWAPI.@]
1682 *
1683 * Converts a STRRET to a BSTR.
1684 *
1685 * PARAMS
1686 * lpStrRet [O] STRRET to convert
1687 * pidl [I] ITEMIDLIST for lpStrRet->uType = STRRET_OFFSET
1688 * pBstrOut [O] Destination for converted BSTR
1689 *
1690 * RETURNS
1691 * Success: S_OK. pBstrOut contains the new string.
1692 * Failure: E_FAIL, if any parameters are invalid.
1693 */
StrRetToBSTR(STRRET * lpStrRet,LPCITEMIDLIST pidl,BSTR * pBstrOut)1694 HRESULT WINAPI StrRetToBSTR(STRRET *lpStrRet, LPCITEMIDLIST pidl, BSTR* pBstrOut)
1695 {
1696 HRESULT hRet = E_FAIL;
1697
1698 switch (lpStrRet->uType)
1699 {
1700 case STRRET_WSTR:
1701 *pBstrOut = SysAllocString(lpStrRet->u.pOleStr);
1702 if (*pBstrOut)
1703 hRet = S_OK;
1704 CoTaskMemFree(lpStrRet->u.pOleStr);
1705 break;
1706
1707 case STRRET_CSTR:
1708 hRet = _SHStrDupAToBSTR(lpStrRet->u.cStr, pBstrOut);
1709 break;
1710
1711 case STRRET_OFFSET:
1712 hRet = _SHStrDupAToBSTR(((LPCSTR)&pidl->mkid) + lpStrRet->u.uOffset, pBstrOut);
1713 break;
1714
1715 default:
1716 *pBstrOut = NULL;
1717 }
1718
1719 return hRet;
1720 }
1721
1722 /*************************************************************************
1723 * StrFormatKBSizeA [SHLWAPI.@]
1724 *
1725 * Create a formatted string containing a byte count in Kilobytes.
1726 *
1727 * PARAMS
1728 * llBytes [I] Byte size to format
1729 * lpszDest [I] Destination for formatted string
1730 * cchMax [I] Size of lpszDest
1731 *
1732 * RETURNS
1733 * lpszDest.
1734 */
StrFormatKBSizeA(LONGLONG llBytes,LPSTR lpszDest,UINT cchMax)1735 LPSTR WINAPI StrFormatKBSizeA(LONGLONG llBytes, LPSTR lpszDest, UINT cchMax)
1736 {
1737 WCHAR wszBuf[256];
1738
1739 if (!StrFormatKBSizeW(llBytes, wszBuf, 256))
1740 return NULL;
1741 if (!WideCharToMultiByte(CP_ACP, 0, wszBuf, -1, lpszDest, cchMax, NULL, NULL))
1742 return NULL;
1743 return lpszDest;
1744 }
1745
1746 /*************************************************************************
1747 * StrFormatKBSizeW [SHLWAPI.@]
1748 *
1749 * See StrFormatKBSizeA.
1750 */
StrFormatKBSizeW(LONGLONG llBytes,LPWSTR lpszDest,UINT cchMax)1751 LPWSTR WINAPI StrFormatKBSizeW(LONGLONG llBytes, LPWSTR lpszDest, UINT cchMax)
1752 {
1753 static const WCHAR kb[] = {' ','K','B',0};
1754 LONGLONG llKB = (llBytes + 1023) >> 10;
1755 int len;
1756
1757 TRACE("(0x%s,%p,%d)\n", wine_dbgstr_longlong(llBytes), lpszDest, cchMax);
1758
1759 if (!FormatInt(llKB, lpszDest, cchMax))
1760 return NULL;
1761
1762 len = lstrlenW(lpszDest);
1763 if (cchMax - len < 4)
1764 return NULL;
1765 lstrcatW(lpszDest, kb);
1766 return lpszDest;
1767 }
1768
1769 /*************************************************************************
1770 * StrNCatA [SHLWAPI.@]
1771 *
1772 * Concatenate two strings together.
1773 *
1774 * PARAMS
1775 * lpszStr [O] String to concatenate to
1776 * lpszCat [I] String to add to lpszCat
1777 * cchMax [I] Maximum number of characters to concatenate
1778 *
1779 * RETURNS
1780 * lpszStr.
1781 *
1782 * NOTES
1783 * cchMax determines the number of characters that are appended to lpszStr,
1784 * not the total length of the string.
1785 */
StrNCatA(LPSTR lpszStr,LPCSTR lpszCat,INT cchMax)1786 LPSTR WINAPI StrNCatA(LPSTR lpszStr, LPCSTR lpszCat, INT cchMax)
1787 {
1788 LPSTR lpszRet = lpszStr;
1789
1790 TRACE("(%s,%s,%i)\n", debugstr_a(lpszStr), debugstr_a(lpszCat), cchMax);
1791
1792 if (!lpszStr)
1793 {
1794 WARN("Invalid lpszStr would crash under Win32!\n");
1795 return NULL;
1796 }
1797
1798 StrCpyNA(lpszStr + strlen(lpszStr), lpszCat, cchMax);
1799 return lpszRet;
1800 }
1801
1802 /*************************************************************************
1803 * StrNCatW [SHLWAPI.@]
1804 *
1805 * See StrNCatA.
1806 */
StrNCatW(LPWSTR lpszStr,LPCWSTR lpszCat,INT cchMax)1807 LPWSTR WINAPI StrNCatW(LPWSTR lpszStr, LPCWSTR lpszCat, INT cchMax)
1808 {
1809 LPWSTR lpszRet = lpszStr;
1810
1811 TRACE("(%s,%s,%i)\n", debugstr_w(lpszStr), debugstr_w(lpszCat), cchMax);
1812
1813 if (!lpszStr)
1814 {
1815 WARN("Invalid lpszStr would crash under Win32\n");
1816 return NULL;
1817 }
1818
1819 StrCpyNW(lpszStr + strlenW(lpszStr), lpszCat, cchMax);
1820 return lpszRet;
1821 }
1822
1823 /*************************************************************************
1824 * StrTrimA [SHLWAPI.@]
1825 *
1826 * Remove characters from the start and end of a string.
1827 *
1828 * PARAMS
1829 * lpszStr [O] String to remove characters from
1830 * lpszTrim [I] Characters to remove from lpszStr
1831 *
1832 * RETURNS
1833 * TRUE If lpszStr was valid and modified
1834 * FALSE Otherwise
1835 */
StrTrimA(LPSTR lpszStr,LPCSTR lpszTrim)1836 BOOL WINAPI StrTrimA(LPSTR lpszStr, LPCSTR lpszTrim)
1837 {
1838 DWORD dwLen;
1839 LPSTR lpszRead = lpszStr;
1840 BOOL bRet = FALSE;
1841
1842 TRACE("(%s,%s)\n", debugstr_a(lpszStr), debugstr_a(lpszTrim));
1843
1844 if (lpszRead && *lpszRead)
1845 {
1846 while (*lpszRead && StrChrA(lpszTrim, *lpszRead))
1847 lpszRead = CharNextA(lpszRead); /* Skip leading matches */
1848
1849 dwLen = strlen(lpszRead);
1850
1851 if (lpszRead != lpszStr)
1852 {
1853 memmove(lpszStr, lpszRead, dwLen + 1);
1854 bRet = TRUE;
1855 }
1856 if (dwLen > 0)
1857 {
1858 lpszRead = lpszStr + dwLen;
1859 while (StrChrA(lpszTrim, lpszRead[-1]))
1860 lpszRead = CharPrevA(lpszStr, lpszRead); /* Skip trailing matches */
1861
1862 if (lpszRead != lpszStr + dwLen)
1863 {
1864 *lpszRead = '\0';
1865 bRet = TRUE;
1866 }
1867 }
1868 }
1869 return bRet;
1870 }
1871
1872 /*************************************************************************
1873 * StrTrimW [SHLWAPI.@]
1874 *
1875 * See StrTrimA.
1876 */
StrTrimW(LPWSTR lpszStr,LPCWSTR lpszTrim)1877 BOOL WINAPI StrTrimW(LPWSTR lpszStr, LPCWSTR lpszTrim)
1878 {
1879 DWORD dwLen;
1880 LPWSTR lpszRead = lpszStr;
1881 BOOL bRet = FALSE;
1882
1883 TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszTrim));
1884
1885 if (lpszRead && *lpszRead)
1886 {
1887 while (*lpszRead && StrChrW(lpszTrim, *lpszRead)) lpszRead++;
1888
1889 dwLen = strlenW(lpszRead);
1890
1891 if (lpszRead != lpszStr)
1892 {
1893 memmove(lpszStr, lpszRead, (dwLen + 1) * sizeof(WCHAR));
1894 bRet = TRUE;
1895 }
1896 if (dwLen > 0)
1897 {
1898 lpszRead = lpszStr + dwLen;
1899 while (StrChrW(lpszTrim, lpszRead[-1]))
1900 lpszRead--; /* Skip trailing matches */
1901
1902 if (lpszRead != lpszStr + dwLen)
1903 {
1904 *lpszRead = '\0';
1905 bRet = TRUE;
1906 }
1907 }
1908 }
1909 return bRet;
1910 }
1911
1912 /*************************************************************************
1913 * _SHStrDupAA [INTERNAL]
1914 *
1915 * Duplicates a ASCII string to ASCII. The destination buffer is allocated.
1916 */
_SHStrDupAA(LPCSTR src,LPSTR * dest)1917 static HRESULT _SHStrDupAA(LPCSTR src, LPSTR * dest)
1918 {
1919 HRESULT hr;
1920 int len = 0;
1921
1922 if (src) {
1923 len = lstrlenA(src) + 1;
1924 *dest = CoTaskMemAlloc(len);
1925 } else {
1926 *dest = NULL;
1927 }
1928
1929 if (*dest) {
1930 lstrcpynA(*dest,src, len);
1931 hr = S_OK;
1932 } else {
1933 hr = E_OUTOFMEMORY;
1934 }
1935
1936 TRACE("%s->(%p)\n", debugstr_a(src), *dest);
1937 return hr;
1938 }
1939
1940 /*************************************************************************
1941 * SHStrDupA [SHLWAPI.@]
1942 *
1943 * Return a Unicode copy of a string, in memory allocated by CoTaskMemAlloc().
1944 *
1945 * PARAMS
1946 * lpszStr [I] String to copy
1947 * lppszDest [O] Destination for the new string copy
1948 *
1949 * RETURNS
1950 * Success: S_OK. lppszDest contains the new string in Unicode format.
1951 * Failure: E_OUTOFMEMORY, If any arguments are invalid or memory allocation
1952 * fails.
1953 */
SHStrDupA(LPCSTR lpszStr,LPWSTR * lppszDest)1954 HRESULT WINAPI SHStrDupA(LPCSTR lpszStr, LPWSTR * lppszDest)
1955 {
1956 HRESULT hRet;
1957 int len = 0;
1958
1959 if (lpszStr)
1960 {
1961 len = MultiByteToWideChar(CP_ACP, 0, lpszStr, -1, NULL, 0) * sizeof(WCHAR);
1962 *lppszDest = CoTaskMemAlloc(len);
1963 }
1964 else
1965 *lppszDest = NULL;
1966
1967 if (*lppszDest)
1968 {
1969 MultiByteToWideChar(CP_ACP, 0, lpszStr, -1, *lppszDest, len/sizeof(WCHAR));
1970 hRet = S_OK;
1971 }
1972 else
1973 hRet = E_OUTOFMEMORY;
1974
1975 TRACE("%s->(%p)\n", debugstr_a(lpszStr), *lppszDest);
1976 return hRet;
1977 }
1978
1979 /*************************************************************************
1980 * _SHStrDupAW [INTERNAL]
1981 *
1982 * Duplicates a UNICODE to a ASCII string. The destination buffer is allocated.
1983 */
_SHStrDupAW(LPCWSTR src,LPSTR * dest)1984 static HRESULT _SHStrDupAW(LPCWSTR src, LPSTR * dest)
1985 {
1986 HRESULT hr;
1987 int len = 0;
1988
1989 if (src) {
1990 len = WideCharToMultiByte(CP_ACP, 0, src, -1, NULL, 0, NULL, NULL);
1991 *dest = CoTaskMemAlloc(len);
1992 } else {
1993 *dest = NULL;
1994 }
1995
1996 if (*dest) {
1997 WideCharToMultiByte(CP_ACP, 0, src, -1, *dest, len, NULL, NULL);
1998 hr = S_OK;
1999 } else {
2000 hr = E_OUTOFMEMORY;
2001 }
2002
2003 TRACE("%s->(%p)\n", debugstr_w(src), *dest);
2004 return hr;
2005 }
2006
2007 /*************************************************************************
2008 * SHStrDupW [SHLWAPI.@]
2009 *
2010 * See SHStrDupA.
2011 */
SHStrDupW(LPCWSTR src,LPWSTR * dest)2012 HRESULT WINAPI SHStrDupW(LPCWSTR src, LPWSTR * dest)
2013 {
2014 HRESULT hr;
2015 int len = 0;
2016
2017 if (src) {
2018 len = (lstrlenW(src) + 1) * sizeof(WCHAR);
2019 *dest = CoTaskMemAlloc(len);
2020 } else {
2021 *dest = NULL;
2022 }
2023
2024 if (*dest) {
2025 memcpy(*dest, src, len);
2026 hr = S_OK;
2027 } else {
2028 hr = E_OUTOFMEMORY;
2029 }
2030
2031 TRACE("%s->(%p)\n", debugstr_w(src), *dest);
2032 return hr;
2033 }
2034
2035 /*************************************************************************
2036 * SHLWAPI_WriteReverseNum
2037 *
2038 * Internal helper for SHLWAPI_WriteTimeClass.
2039 */
SHLWAPI_WriteReverseNum(LPWSTR lpszOut,DWORD dwNum)2040 static inline LPWSTR SHLWAPI_WriteReverseNum(LPWSTR lpszOut, DWORD dwNum)
2041 {
2042 *lpszOut-- = '\0';
2043
2044 /* Write a decimal number to a string, backwards */
2045 do
2046 {
2047 DWORD dwNextDigit = dwNum % 10;
2048 *lpszOut-- = '0' + dwNextDigit;
2049 dwNum = (dwNum - dwNextDigit) / 10;
2050 } while (dwNum > 0);
2051
2052 return lpszOut;
2053 }
2054
2055 /*************************************************************************
2056 * SHLWAPI_FormatSignificant
2057 *
2058 * Internal helper for SHLWAPI_WriteTimeClass.
2059 */
SHLWAPI_FormatSignificant(LPWSTR lpszNum,int dwDigits)2060 static inline int SHLWAPI_FormatSignificant(LPWSTR lpszNum, int dwDigits)
2061 {
2062 /* Zero non significant digits, return remaining significant digits */
2063 while (*lpszNum)
2064 {
2065 lpszNum++;
2066 if (--dwDigits == 0)
2067 {
2068 while (*lpszNum)
2069 *lpszNum++ = '0';
2070 return 0;
2071 }
2072 }
2073 return dwDigits;
2074 }
2075
2076 /*************************************************************************
2077 * SHLWAPI_WriteTimeClass
2078 *
2079 * Internal helper for StrFromTimeIntervalW.
2080 */
SHLWAPI_WriteTimeClass(LPWSTR lpszOut,DWORD dwValue,UINT uClassStringId,int iDigits)2081 static int SHLWAPI_WriteTimeClass(LPWSTR lpszOut, DWORD dwValue,
2082 UINT uClassStringId, int iDigits)
2083 {
2084 WCHAR szBuff[64], *szOut = szBuff + 32;
2085
2086 szOut = SHLWAPI_WriteReverseNum(szOut, dwValue);
2087 iDigits = SHLWAPI_FormatSignificant(szOut + 1, iDigits);
2088 *szOut = ' ';
2089 LoadStringW(shlwapi_hInstance, uClassStringId, szBuff + 32, 32);
2090 strcatW(lpszOut, szOut);
2091 return iDigits;
2092 }
2093
2094 /*************************************************************************
2095 * StrFromTimeIntervalA [SHLWAPI.@]
2096 *
2097 * Format a millisecond time interval into a string
2098 *
2099 * PARAMS
2100 * lpszStr [O] Output buffer for formatted time interval
2101 * cchMax [I] Size of lpszStr
2102 * dwMS [I] Number of milliseconds
2103 * iDigits [I] Number of digits to print
2104 *
2105 * RETURNS
2106 * The length of the formatted string, or 0 if any parameter is invalid.
2107 *
2108 * NOTES
2109 * This implementation mimics the Win32 behaviour of always writing a leading
2110 * space before the time interval begins.
2111 *
2112 * iDigits is used to provide approximate times if accuracy is not important.
2113 * This number of digits will be written of the first non-zero time class
2114 * (hours/minutes/seconds). If this does not complete the time classification,
2115 * the remaining digits are changed to zeros (i.e. The time is _not_ rounded).
2116 * If there are digits remaining following the writing of a time class, the
2117 * next time class will be written.
2118 *
2119 * For example, given dwMS represents 138 hours,43 minutes and 15 seconds, the
2120 * following will result from the given values of iDigits:
2121 *
2122 *| iDigits 1 2 3 4 5 ...
2123 *| lpszStr "100 hr" "130 hr" "138 hr" "138 hr 40 min" "138 hr 43 min" ...
2124 */
StrFromTimeIntervalA(LPSTR lpszStr,UINT cchMax,DWORD dwMS,int iDigits)2125 INT WINAPI StrFromTimeIntervalA(LPSTR lpszStr, UINT cchMax, DWORD dwMS,
2126 int iDigits)
2127 {
2128 INT iRet = 0;
2129
2130 TRACE("(%p,%d,%d,%d)\n", lpszStr, cchMax, dwMS, iDigits);
2131
2132 if (lpszStr && cchMax)
2133 {
2134 WCHAR szBuff[128];
2135 StrFromTimeIntervalW(szBuff, sizeof(szBuff)/sizeof(WCHAR), dwMS, iDigits);
2136 WideCharToMultiByte(CP_ACP,0,szBuff,-1,lpszStr,cchMax,0,0);
2137 }
2138 return iRet;
2139 }
2140
2141
2142 /*************************************************************************
2143 * StrFromTimeIntervalW [SHLWAPI.@]
2144 *
2145 * See StrFromTimeIntervalA.
2146 */
StrFromTimeIntervalW(LPWSTR lpszStr,UINT cchMax,DWORD dwMS,int iDigits)2147 INT WINAPI StrFromTimeIntervalW(LPWSTR lpszStr, UINT cchMax, DWORD dwMS,
2148 int iDigits)
2149 {
2150 INT iRet = 0;
2151
2152 TRACE("(%p,%d,%d,%d)\n", lpszStr, cchMax, dwMS, iDigits);
2153
2154 if (lpszStr && cchMax)
2155 {
2156 WCHAR szCopy[128];
2157 DWORD dwHours, dwMinutes;
2158
2159 if (!iDigits || cchMax == 1)
2160 {
2161 *lpszStr = '\0';
2162 return 0;
2163 }
2164
2165 /* Calculate the time classes */
2166 dwMS = (dwMS + 500) / 1000;
2167 dwHours = dwMS / 3600;
2168 dwMS -= dwHours * 3600;
2169 dwMinutes = dwMS / 60;
2170 dwMS -= dwMinutes * 60;
2171
2172 szCopy[0] = '\0';
2173
2174 if (dwHours)
2175 iDigits = SHLWAPI_WriteTimeClass(szCopy, dwHours, IDS_TIME_INTERVAL_HOURS, iDigits);
2176
2177 if (dwMinutes && iDigits)
2178 iDigits = SHLWAPI_WriteTimeClass(szCopy, dwMinutes, IDS_TIME_INTERVAL_MINUTES, iDigits);
2179
2180 if (iDigits) /* Always write seconds if we have significant digits */
2181 SHLWAPI_WriteTimeClass(szCopy, dwMS, IDS_TIME_INTERVAL_SECONDS, iDigits);
2182
2183 lstrcpynW(lpszStr, szCopy, cchMax);
2184 iRet = strlenW(lpszStr);
2185 }
2186 return iRet;
2187 }
2188
2189 /*************************************************************************
2190 * StrIsIntlEqualA [SHLWAPI.@]
2191 *
2192 * Compare two strings.
2193 *
2194 * PARAMS
2195 * bCase [I] Whether to compare case sensitively
2196 * lpszStr [I] First string to compare
2197 * lpszComp [I] Second string to compare
2198 * iLen [I] Length to compare
2199 *
2200 * RETURNS
2201 * TRUE If the strings are equal.
2202 * FALSE Otherwise.
2203 */
StrIsIntlEqualA(BOOL bCase,LPCSTR lpszStr,LPCSTR lpszComp,int iLen)2204 BOOL WINAPI StrIsIntlEqualA(BOOL bCase, LPCSTR lpszStr, LPCSTR lpszComp,
2205 int iLen)
2206 {
2207 DWORD dwFlags;
2208
2209 TRACE("(%d,%s,%s,%d)\n", bCase,
2210 debugstr_a(lpszStr), debugstr_a(lpszComp), iLen);
2211
2212 /* FIXME: This flag is undocumented and unknown by our CompareString.
2213 * We need a define for it.
2214 */
2215 dwFlags = 0x10000000;
2216 if (!bCase) dwFlags |= NORM_IGNORECASE;
2217
2218 return (CompareStringA(GetThreadLocale(), dwFlags, lpszStr, iLen, lpszComp, iLen) == CSTR_EQUAL);
2219 }
2220
2221 /*************************************************************************
2222 * StrIsIntlEqualW [SHLWAPI.@]
2223 *
2224 * See StrIsIntlEqualA.
2225 */
StrIsIntlEqualW(BOOL bCase,LPCWSTR lpszStr,LPCWSTR lpszComp,int iLen)2226 BOOL WINAPI StrIsIntlEqualW(BOOL bCase, LPCWSTR lpszStr, LPCWSTR lpszComp,
2227 int iLen)
2228 {
2229 DWORD dwFlags;
2230
2231 TRACE("(%d,%s,%s,%d)\n", bCase,
2232 debugstr_w(lpszStr),debugstr_w(lpszComp), iLen);
2233
2234 /* FIXME: This flag is undocumented and unknown by our CompareString.
2235 * We need a define for it.
2236 */
2237 dwFlags = 0x10000000;
2238 if (!bCase) dwFlags |= NORM_IGNORECASE;
2239
2240 return (CompareStringW(GetThreadLocale(), dwFlags, lpszStr, iLen, lpszComp, iLen) == CSTR_EQUAL);
2241 }
2242
2243 /*************************************************************************
2244 * @ [SHLWAPI.399]
2245 *
2246 * Copy a string to another string, up to a maximum number of characters.
2247 *
2248 * PARAMS
2249 * lpszDest [O] Destination string
2250 * lpszSrc [I] Source string
2251 * iLen [I] Maximum number of chars to copy
2252 *
2253 * RETURNS
2254 * Success: A pointer to the last character written to lpszDest.
2255 * Failure: lpszDest, if any arguments are invalid.
2256 */
StrCpyNXA(LPSTR lpszDest,LPCSTR lpszSrc,int iLen)2257 LPSTR WINAPI StrCpyNXA(LPSTR lpszDest, LPCSTR lpszSrc, int iLen)
2258 {
2259 TRACE("(%p,%s,%i)\n", lpszDest, debugstr_a(lpszSrc), iLen);
2260
2261 if (lpszDest && lpszSrc && iLen > 0)
2262 {
2263 while ((iLen-- > 1) && *lpszSrc)
2264 *lpszDest++ = *lpszSrc++;
2265 if (iLen >= 0)
2266 *lpszDest = '\0';
2267 }
2268 return lpszDest;
2269 }
2270
2271 /*************************************************************************
2272 * @ [SHLWAPI.400]
2273 *
2274 * Unicode version of StrCpyNXA.
2275 */
StrCpyNXW(LPWSTR lpszDest,LPCWSTR lpszSrc,int iLen)2276 LPWSTR WINAPI StrCpyNXW(LPWSTR lpszDest, LPCWSTR lpszSrc, int iLen)
2277 {
2278 TRACE("(%p,%s,%i)\n", lpszDest, debugstr_w(lpszSrc), iLen);
2279
2280 if (lpszDest && lpszSrc && iLen > 0)
2281 {
2282 while ((iLen-- > 1) && *lpszSrc)
2283 *lpszDest++ = *lpszSrc++;
2284 if (iLen >= 0)
2285 *lpszDest = '\0';
2286 }
2287 return lpszDest;
2288 }
2289
2290 /*************************************************************************
2291 * StrCmpLogicalW [SHLWAPI.@]
2292 *
2293 * Compare two strings, ignoring case and comparing digits as numbers.
2294 *
2295 * PARAMS
2296 * lpszStr [I] First string to compare
2297 * lpszComp [I] Second string to compare
2298 * iLen [I] Length to compare
2299 *
2300 * RETURNS
2301 * TRUE If the strings are equal.
2302 * FALSE Otherwise.
2303 */
StrCmpLogicalW(LPCWSTR lpszStr,LPCWSTR lpszComp)2304 INT WINAPI StrCmpLogicalW(LPCWSTR lpszStr, LPCWSTR lpszComp)
2305 {
2306 INT iDiff;
2307
2308 TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszComp));
2309
2310 if (lpszStr && lpszComp)
2311 {
2312 while (*lpszStr)
2313 {
2314 if (!*lpszComp)
2315 return 1;
2316 else if (isdigitW(*lpszStr))
2317 {
2318 int iStr, iComp;
2319
2320 if (!isdigitW(*lpszComp))
2321 return -1;
2322
2323 /* Compare the numbers */
2324 StrToIntExW(lpszStr, 0, &iStr);
2325 StrToIntExW(lpszComp, 0, &iComp);
2326
2327 if (iStr < iComp)
2328 return -1;
2329 else if (iStr > iComp)
2330 return 1;
2331
2332 /* Skip */
2333 while (isdigitW(*lpszStr))
2334 lpszStr++;
2335 while (isdigitW(*lpszComp))
2336 lpszComp++;
2337 }
2338 else if (isdigitW(*lpszComp))
2339 return 1;
2340 else
2341 {
2342 iDiff = ChrCmpIW(*lpszStr,*lpszComp);
2343 if (iDiff > 0)
2344 return 1;
2345 else if (iDiff < 0)
2346 return -1;
2347
2348 lpszStr++;
2349 lpszComp++;
2350 }
2351 }
2352 if (*lpszComp)
2353 return -1;
2354 }
2355 return 0;
2356 }
2357
2358 /* Structure for formatting byte strings */
2359 typedef struct tagSHLWAPI_BYTEFORMATS
2360 {
2361 LONGLONG dLimit;
2362 double dDivisor;
2363 double dNormaliser;
2364 int nDecimals;
2365 #ifdef __REACTOS__
2366 UINT nFormatID;
2367 #else
2368 WCHAR wPrefix;
2369 #endif
2370 } SHLWAPI_BYTEFORMATS;
2371
2372 /*************************************************************************
2373 * StrFormatByteSizeW [SHLWAPI.@]
2374 *
2375 * Create a string containing an abbreviated byte count of up to 2^63-1.
2376 *
2377 * PARAMS
2378 * llBytes [I] Byte size to format
2379 * lpszDest [I] Destination for formatted string
2380 * cchMax [I] Size of lpszDest
2381 *
2382 * RETURNS
2383 * lpszDest.
2384 *
2385 * NOTES
2386 * There is no StrFormatByteSize64W function, it is called StrFormatByteSizeW().
2387 */
StrFormatByteSizeW(LONGLONG llBytes,LPWSTR lpszDest,UINT cchMax)2388 LPWSTR WINAPI StrFormatByteSizeW(LONGLONG llBytes, LPWSTR lpszDest, UINT cchMax)
2389 {
2390 #define KB ((ULONGLONG)1024)
2391 #define MB (KB*KB)
2392 #define GB (KB*KB*KB)
2393 #define TB (KB*KB*KB*KB)
2394 #define PB (KB*KB*KB*KB*KB)
2395
2396 static const SHLWAPI_BYTEFORMATS bfFormats[] =
2397 {
2398 #ifdef __REACTOS__
2399 { 10*KB, 10.24, 100.0, 2, IDS_KB_FORMAT }, /* 10 KB */
2400 { 100*KB, 102.4, 10.0, 1, IDS_KB_FORMAT }, /* 100 KB */
2401 { 1000*KB, 1024.0, 1.0, 0, IDS_KB_FORMAT }, /* 1000 KB */
2402 { 10*MB, 10485.76, 100.0, 2, IDS_MB_FORMAT }, /* 10 MB */
2403 { 100*MB, 104857.6, 10.0, 1, IDS_MB_FORMAT }, /* 100 MB */
2404 { 1000*MB, 1048576.0, 1.0, 0, IDS_MB_FORMAT }, /* 1000 MB */
2405 { 10*GB, 10737418.24, 100.0, 2, IDS_GB_FORMAT }, /* 10 GB */
2406 { 100*GB, 107374182.4, 10.0, 1, IDS_GB_FORMAT }, /* 100 GB */
2407 { 1000*GB, 1073741824.0, 1.0, 0, IDS_GB_FORMAT }, /* 1000 GB */
2408 { 10*TB, 10485.76, 100.0, 2, IDS_TB_FORMAT }, /* 10 TB */
2409 { 100*TB, 104857.6, 10.0, 1, IDS_TB_FORMAT }, /* 100 TB */
2410 { 1000*TB, 1048576.0, 1.0, 0, IDS_TB_FORMAT }, /* 1000 TB */
2411 { 10*PB, 10737418.24, 100.00, 2, IDS_PB_FORMAT }, /* 10 PB */
2412 { 100*PB, 107374182.4, 10.00, 1, IDS_PB_FORMAT }, /* 100 PB */
2413 { 1000*PB, 1073741824.0, 1.00, 0, IDS_PB_FORMAT }, /* 1000 PB */
2414 { 0, 10995116277.76, 100.00, 2, IDS_EB_FORMAT } /* EB's, catch all */
2415 #else
2416 { 10*KB, 10.24, 100.0, 2, 'K' }, /* 10 KB */
2417 { 100*KB, 102.4, 10.0, 1, 'K' }, /* 100 KB */
2418 { 1000*KB, 1024.0, 1.0, 0, 'K' }, /* 1000 KB */
2419 { 10*MB, 10485.76, 100.0, 2, 'M' }, /* 10 MB */
2420 { 100*MB, 104857.6, 10.0, 1, 'M' }, /* 100 MB */
2421 { 1000*MB, 1048576.0, 1.0, 0, 'M' }, /* 1000 MB */
2422 { 10*GB, 10737418.24, 100.0, 2, 'G' }, /* 10 GB */
2423 { 100*GB, 107374182.4, 10.0, 1, 'G' }, /* 100 GB */
2424 { 1000*GB, 1073741824.0, 1.0, 0, 'G' }, /* 1000 GB */
2425 { 10*TB, 10485.76, 100.0, 2, 'T' }, /* 10 TB */
2426 { 100*TB, 104857.6, 10.0, 1, 'T' }, /* 100 TB */
2427 { 1000*TB, 1048576.0, 1.0, 0, 'T' }, /* 1000 TB */
2428 { 10*PB, 10737418.24, 100.00, 2, 'P' }, /* 10 PB */
2429 { 100*PB, 107374182.4, 10.00, 1, 'P' }, /* 100 PB */
2430 { 1000*PB, 1073741824.0, 1.00, 0, 'P' }, /* 1000 PB */
2431 { 0, 10995116277.76, 100.00, 2, 'E' } /* EB's, catch all */
2432 #endif
2433 };
2434 #ifdef __REACTOS__
2435 WCHAR szBuff[40], wszFormat[40];
2436 #else
2437 WCHAR wszAdd[] = {' ','?','B',0};
2438 #endif
2439 double dBytes;
2440 UINT i = 0;
2441
2442 TRACE("(0x%s,%p,%d)\n", wine_dbgstr_longlong(llBytes), lpszDest, cchMax);
2443
2444 if (!lpszDest || !cchMax)
2445 return lpszDest;
2446
2447 if (llBytes < 1024) /* 1K */
2448 {
2449 WCHAR wszBytesFormat[64];
2450 LoadStringW(shlwapi_hInstance, IDS_BYTES_FORMAT, wszBytesFormat, 64);
2451 snprintfW(lpszDest, cchMax, wszBytesFormat, (int)llBytes);
2452 return lpszDest;
2453 }
2454
2455 /* Note that if this loop completes without finding a match, i will be
2456 * pointing at the last entry, which is a catch all for > 1000 PB
2457 */
2458 while (i < sizeof(bfFormats) / sizeof(SHLWAPI_BYTEFORMATS) - 1)
2459 {
2460 if (llBytes < bfFormats[i].dLimit)
2461 break;
2462 i++;
2463 }
2464 /* Above 1 TB we encounter problems with FP accuracy. So for amounts above
2465 * this number we integer shift down by 1 MB first. The table above has
2466 * the divisors scaled down from the '< 10 TB' entry onwards, to account
2467 * for this. We also add a small fudge factor to get the correct result for
2468 * counts that lie exactly on a 1024 byte boundary.
2469 */
2470 if (i > 8)
2471 dBytes = (double)(llBytes >> 20) + 0.001; /* Scale down by 1 MB */
2472 else
2473 dBytes = (double)llBytes + 0.00001;
2474
2475 dBytes = floor(dBytes / bfFormats[i].dDivisor) / bfFormats[i].dNormaliser;
2476
2477 #ifdef __REACTOS__
2478 if (!FormatDouble(dBytes, bfFormats[i].nDecimals, szBuff, ARRAYSIZE(szBuff)))
2479 return NULL;
2480 LoadStringW(shlwapi_hInstance, bfFormats[i].nFormatID, wszFormat, ARRAYSIZE(wszFormat));
2481 snprintfW(lpszDest, cchMax, wszFormat, szBuff);
2482 #else
2483 if (!FormatDouble(dBytes, bfFormats[i].nDecimals, lpszDest, cchMax))
2484 return NULL;
2485 wszAdd[1] = bfFormats[i].wPrefix;
2486 StrCatBuffW(lpszDest, wszAdd, cchMax);
2487 #endif
2488 return lpszDest;
2489 }
2490
2491 /*************************************************************************
2492 * StrFormatByteSize64A [SHLWAPI.@]
2493 *
2494 * See StrFormatByteSizeW.
2495 */
StrFormatByteSize64A(LONGLONG llBytes,LPSTR lpszDest,UINT cchMax)2496 LPSTR WINAPI StrFormatByteSize64A(LONGLONG llBytes, LPSTR lpszDest, UINT cchMax)
2497 {
2498 WCHAR wszBuff[32];
2499
2500 StrFormatByteSizeW(llBytes, wszBuff, sizeof(wszBuff)/sizeof(WCHAR));
2501
2502 if (lpszDest)
2503 WideCharToMultiByte(CP_ACP, 0, wszBuff, -1, lpszDest, cchMax, 0, 0);
2504 return lpszDest;
2505 }
2506
2507 /*************************************************************************
2508 * StrFormatByteSizeA [SHLWAPI.@]
2509 *
2510 * Create a string containing an abbreviated byte count of up to 2^31-1.
2511 *
2512 * PARAMS
2513 * dwBytes [I] Byte size to format
2514 * lpszDest [I] Destination for formatted string
2515 * cchMax [I] Size of lpszDest
2516 *
2517 * RETURNS
2518 * lpszDest.
2519 *
2520 * NOTES
2521 * The Ascii and Unicode versions of this function accept a different
2522 * integer type for dwBytes. See StrFormatByteSize64A().
2523 */
StrFormatByteSizeA(DWORD dwBytes,LPSTR lpszDest,UINT cchMax)2524 LPSTR WINAPI StrFormatByteSizeA(DWORD dwBytes, LPSTR lpszDest, UINT cchMax)
2525 {
2526 TRACE("(%d,%p,%d)\n", dwBytes, lpszDest, cchMax);
2527
2528 return StrFormatByteSize64A(dwBytes, lpszDest, cchMax);
2529 }
2530
2531 /*************************************************************************
2532 * @ [SHLWAPI.162]
2533 *
2534 * Remove a hanging lead byte from the end of a string, if present.
2535 *
2536 * PARAMS
2537 * lpStr [I] String to check for a hanging lead byte
2538 * size [I] Length of lpStr
2539 *
2540 * RETURNS
2541 * Success: The new length of the string. Any hanging lead bytes are removed.
2542 * Failure: 0, if any parameters are invalid.
2543 */
SHTruncateString(LPSTR lpStr,DWORD size)2544 DWORD WINAPI SHTruncateString(LPSTR lpStr, DWORD size)
2545 {
2546 if (lpStr && size)
2547 {
2548 LPSTR lastByte = lpStr + size - 1;
2549
2550 while(lpStr < lastByte)
2551 lpStr += IsDBCSLeadByte(*lpStr) ? 2 : 1;
2552
2553 if(lpStr == lastByte && IsDBCSLeadByte(*lpStr))
2554 {
2555 *lpStr = '\0';
2556 size--;
2557 }
2558 return size;
2559 }
2560 return 0;
2561 }
2562
2563 /*************************************************************************
2564 * @ [SHLWAPI.203]
2565 *
2566 * Remove a single non-trailing ampersand ('&') from a string.
2567 *
2568 * PARAMS
2569 * lpszStr [I/O] String to remove ampersand from.
2570 *
2571 * RETURNS
2572 * The character after the first ampersand in lpszStr, or the first character
2573 * in lpszStr if there is no ampersand in the string.
2574 */
SHStripMneumonicA(LPCSTR lpszStr)2575 char WINAPI SHStripMneumonicA(LPCSTR lpszStr)
2576 {
2577 LPSTR lpszIter, lpszTmp;
2578 char ch;
2579
2580 TRACE("(%s)\n", debugstr_a(lpszStr));
2581
2582 ch = *lpszStr;
2583
2584 if ((lpszIter = StrChrA(lpszStr, '&')))
2585 {
2586 lpszTmp = CharNextA(lpszIter);
2587 if (*lpszTmp)
2588 {
2589 if (*lpszTmp != '&')
2590 ch = *lpszTmp;
2591
2592 memmove( lpszIter, lpszTmp, strlen(lpszTmp) + 1 );
2593 }
2594 }
2595
2596 return ch;
2597 }
2598
2599 /*************************************************************************
2600 * @ [SHLWAPI.225]
2601 *
2602 * Unicode version of SHStripMneumonicA.
2603 */
SHStripMneumonicW(LPCWSTR lpszStr)2604 WCHAR WINAPI SHStripMneumonicW(LPCWSTR lpszStr)
2605 {
2606 LPWSTR lpszIter, lpszTmp;
2607 WCHAR ch;
2608
2609 TRACE("(%s)\n", debugstr_w(lpszStr));
2610
2611 ch = *lpszStr;
2612
2613 if ((lpszIter = StrChrW(lpszStr, '&')))
2614 {
2615 lpszTmp = lpszIter + 1;
2616 if (*lpszTmp)
2617 {
2618 if (*lpszTmp != '&')
2619 ch = *lpszTmp;
2620
2621 memmove( lpszIter, lpszTmp, (strlenW(lpszTmp) + 1) * sizeof(WCHAR) );
2622 }
2623 }
2624
2625 return ch;
2626 }
2627
2628 /*************************************************************************
2629 * @ [SHLWAPI.216]
2630 *
2631 * Convert an Ascii string to Unicode.
2632 *
2633 * PARAMS
2634 * dwCp [I] Code page for the conversion
2635 * lpSrcStr [I] Source Ascii string to convert
2636 * lpDstStr [O] Destination for converted Unicode string
2637 * iLen [I] Length of lpDstStr
2638 *
2639 * RETURNS
2640 * The return value of the MultiByteToWideChar() function called on lpSrcStr.
2641 */
SHAnsiToUnicodeCP(DWORD dwCp,LPCSTR lpSrcStr,LPWSTR lpDstStr,int iLen)2642 DWORD WINAPI SHAnsiToUnicodeCP(DWORD dwCp, LPCSTR lpSrcStr, LPWSTR lpDstStr, int iLen)
2643 {
2644 DWORD dwRet;
2645
2646 dwRet = MultiByteToWideChar(dwCp, 0, lpSrcStr, -1, lpDstStr, iLen);
2647 TRACE("%s->%s,ret=%d\n", debugstr_a(lpSrcStr), debugstr_w(lpDstStr), dwRet);
2648 return dwRet;
2649 }
2650
2651 /*************************************************************************
2652 * @ [SHLWAPI.215]
2653 *
2654 * Convert an Ascii string to Unicode.
2655 *
2656 * PARAMS
2657 * lpSrcStr [I] Source Ascii string to convert
2658 * lpDstStr [O] Destination for converted Unicode string
2659 * iLen [I] Length of lpDstStr
2660 *
2661 * RETURNS
2662 * The return value of the MultiByteToWideChar() function called on lpSrcStr.
2663 *
2664 * NOTES
2665 * This function simply calls SHAnsiToUnicodeCP with code page CP_ACP.
2666 */
SHAnsiToUnicode(LPCSTR lpSrcStr,LPWSTR lpDstStr,int iLen)2667 DWORD WINAPI SHAnsiToUnicode(LPCSTR lpSrcStr, LPWSTR lpDstStr, int iLen)
2668 {
2669 return SHAnsiToUnicodeCP(CP_ACP, lpSrcStr, lpDstStr, iLen);
2670 }
2671
2672 /*************************************************************************
2673 * @ [SHLWAPI.218]
2674 *
2675 * Convert a Unicode string to Ascii.
2676 *
2677 * PARAMS
2678 * CodePage [I] Code page to use for the conversion
2679 * lpSrcStr [I] Source Unicode string to convert
2680 * lpDstStr [O] Destination for converted Ascii string
2681 * dstlen [I] Length of buffer at lpDstStr
2682 *
2683 * RETURNS
2684 * Success: The length in bytes of the result at lpDstStr (including the terminator)
2685 * Failure: When using CP_UTF8, CP_UTF7 or 0xc350 as codePage, 0 is returned and
2686 * the result is not nul-terminated.
2687 * When using a different codepage, the length in bytes of the truncated
2688 * result at lpDstStr (including the terminator) is returned and
2689 * lpDstStr is always nul-terminated.
2690 *
2691 */
SHUnicodeToAnsiCP(UINT CodePage,LPCWSTR lpSrcStr,LPSTR lpDstStr,int dstlen)2692 DWORD WINAPI SHUnicodeToAnsiCP(UINT CodePage, LPCWSTR lpSrcStr, LPSTR lpDstStr, int dstlen)
2693 {
2694 static const WCHAR emptyW[] = { '\0' };
2695 int len , reqLen;
2696 LPSTR mem;
2697
2698 if (!lpDstStr || !dstlen)
2699 return 0;
2700
2701 if (!lpSrcStr)
2702 lpSrcStr = emptyW;
2703
2704 *lpDstStr = '\0';
2705
2706 len = strlenW(lpSrcStr) + 1;
2707
2708 switch (CodePage)
2709 {
2710 case CP_WINUNICODE:
2711 CodePage = CP_UTF8; /* Fall through... */
2712 case 0x0000C350: /* FIXME: CP_ #define */
2713 case CP_UTF7:
2714 case CP_UTF8:
2715 {
2716 DWORD dwMode = 0;
2717 INT lenW = len - 1;
2718 INT needed = dstlen - 1;
2719 HRESULT hr;
2720
2721 /* try the user supplied buffer first */
2722 hr = ConvertINetUnicodeToMultiByte(&dwMode, CodePage, lpSrcStr, &lenW, lpDstStr, &needed);
2723 if (hr == S_OK)
2724 {
2725 lpDstStr[needed] = '\0';
2726 return needed + 1;
2727 }
2728
2729 /* user buffer too small. exclude termination and copy as much as possible */
2730 lenW = len;
2731 hr = ConvertINetUnicodeToMultiByte(&dwMode, CodePage, lpSrcStr, &lenW, NULL, &needed);
2732 needed++;
2733 mem = HeapAlloc(GetProcessHeap(), 0, needed);
2734 if (!mem)
2735 return 0;
2736
2737 hr = ConvertINetUnicodeToMultiByte(&dwMode, CodePage, lpSrcStr, &len, mem, &needed);
2738 if (hr == S_OK)
2739 {
2740 reqLen = SHTruncateString(mem, dstlen);
2741 if (reqLen > 0) memcpy(lpDstStr, mem, reqLen-1);
2742 }
2743 HeapFree(GetProcessHeap(), 0, mem);
2744 return 0;
2745 }
2746 default:
2747 break;
2748 }
2749
2750 /* try the user supplied buffer first */
2751 reqLen = WideCharToMultiByte(CodePage, 0, lpSrcStr, len, lpDstStr, dstlen, NULL, NULL);
2752
2753 if (!reqLen && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
2754 {
2755 reqLen = WideCharToMultiByte(CodePage, 0, lpSrcStr, len, NULL, 0, NULL, NULL);
2756 if (reqLen)
2757 {
2758 mem = HeapAlloc(GetProcessHeap(), 0, reqLen);
2759 if (mem)
2760 {
2761 WideCharToMultiByte(CodePage, 0, lpSrcStr, len, mem, reqLen, NULL, NULL);
2762
2763 reqLen = SHTruncateString(mem, dstlen -1);
2764 reqLen++;
2765
2766 lstrcpynA(lpDstStr, mem, reqLen);
2767 HeapFree(GetProcessHeap(), 0, mem);
2768 lpDstStr[reqLen-1] = '\0';
2769 }
2770 }
2771 }
2772 return reqLen;
2773 }
2774
2775 /*************************************************************************
2776 * @ [SHLWAPI.217]
2777 *
2778 * Convert a Unicode string to Ascii.
2779 *
2780 * PARAMS
2781 * lpSrcStr [I] Source Unicode string to convert
2782 * lpDstStr [O] Destination for converted Ascii string
2783 * iLen [O] Length of lpDstStr in characters
2784 *
2785 * RETURNS
2786 * See SHUnicodeToAnsiCP
2787
2788 * NOTES
2789 * This function simply calls SHUnicodeToAnsiCP() with CodePage = CP_ACP.
2790 */
SHUnicodeToAnsi(LPCWSTR lpSrcStr,LPSTR lpDstStr,INT iLen)2791 INT WINAPI SHUnicodeToAnsi(LPCWSTR lpSrcStr, LPSTR lpDstStr, INT iLen)
2792 {
2793 return SHUnicodeToAnsiCP(CP_ACP, lpSrcStr, lpDstStr, iLen);
2794 }
2795
2796 /*************************************************************************
2797 * @ [SHLWAPI.345]
2798 *
2799 * Copy one string to another.
2800 *
2801 * PARAMS
2802 * lpszSrc [I] Source string to copy
2803 * lpszDst [O] Destination for copy
2804 * iLen [I] Length of lpszDst in characters
2805 *
2806 * RETURNS
2807 * The length of the copied string, including the terminating NUL. lpszDst
2808 * contains iLen characters of lpszSrc.
2809 */
SHAnsiToAnsi(LPCSTR lpszSrc,LPSTR lpszDst,int iLen)2810 DWORD WINAPI SHAnsiToAnsi(LPCSTR lpszSrc, LPSTR lpszDst, int iLen)
2811 {
2812 LPSTR lpszRet;
2813
2814 TRACE("(%s,%p,0x%08x)\n", debugstr_a(lpszSrc), lpszDst, iLen);
2815
2816 lpszRet = StrCpyNXA(lpszDst, lpszSrc, iLen);
2817 return lpszRet - lpszDst + 1;
2818 }
2819
2820 /*************************************************************************
2821 * @ [SHLWAPI.346]
2822 *
2823 * Unicode version of SSHAnsiToAnsi.
2824 */
SHUnicodeToUnicode(LPCWSTR lpszSrc,LPWSTR lpszDst,int iLen)2825 DWORD WINAPI SHUnicodeToUnicode(LPCWSTR lpszSrc, LPWSTR lpszDst, int iLen)
2826 {
2827 LPWSTR lpszRet;
2828
2829 TRACE("(%s,%p,0x%08x)\n", debugstr_w(lpszSrc), lpszDst, iLen);
2830
2831 lpszRet = StrCpyNXW(lpszDst, lpszSrc, iLen);
2832 return lpszRet - lpszDst + 1;
2833 }
2834
2835 /*************************************************************************
2836 * @ [SHLWAPI.364]
2837 *
2838 * Determine if an Ascii string converts to Unicode and back identically.
2839 *
2840 * PARAMS
2841 * lpSrcStr [I] Source Unicode string to convert
2842 * lpDst [O] Destination for resulting Ascii string
2843 * iLen [I] Length of lpDst in characters
2844 *
2845 * RETURNS
2846 * TRUE, since Ascii strings always convert identically.
2847 */
DoesStringRoundTripA(LPCSTR lpSrcStr,LPSTR lpDst,INT iLen)2848 BOOL WINAPI DoesStringRoundTripA(LPCSTR lpSrcStr, LPSTR lpDst, INT iLen)
2849 {
2850 lstrcpynA(lpDst, lpSrcStr, iLen);
2851 return TRUE;
2852 }
2853
2854 /*************************************************************************
2855 * @ [SHLWAPI.365]
2856 *
2857 * Determine if a Unicode string converts to Ascii and back identically.
2858 *
2859 * PARAMS
2860 * lpSrcStr [I] Source Unicode string to convert
2861 * lpDst [O] Destination for resulting Ascii string
2862 * iLen [I] Length of lpDst in characters
2863 *
2864 * RETURNS
2865 * TRUE, if lpSrcStr converts to Ascii and back identically,
2866 * FALSE otherwise.
2867 */
DoesStringRoundTripW(LPCWSTR lpSrcStr,LPSTR lpDst,INT iLen)2868 BOOL WINAPI DoesStringRoundTripW(LPCWSTR lpSrcStr, LPSTR lpDst, INT iLen)
2869 {
2870 WCHAR szBuff[MAX_PATH];
2871
2872 SHUnicodeToAnsi(lpSrcStr, lpDst, iLen);
2873 SHAnsiToUnicode(lpDst, szBuff, MAX_PATH);
2874 return !strcmpW(lpSrcStr, szBuff);
2875 }
2876
2877 /*************************************************************************
2878 * SHLoadIndirectString [SHLWAPI.@]
2879 *
2880 * If passed a string that begins with '@', extract the string from the
2881 * appropriate resource, otherwise do a straight copy.
2882 *
2883 */
SHLoadIndirectString(LPCWSTR src,LPWSTR dst,UINT dst_len,void ** reserved)2884 HRESULT WINAPI SHLoadIndirectString(LPCWSTR src, LPWSTR dst, UINT dst_len, void **reserved)
2885 {
2886 WCHAR *dllname = NULL;
2887 HMODULE hmod = NULL;
2888 HRESULT hr = E_FAIL;
2889 #ifdef __REACTOS__
2890 WCHAR szExpanded[512];
2891 #endif
2892
2893 TRACE("(%s %p %08x %p)\n", debugstr_w(src), dst, dst_len, reserved);
2894
2895 if(src[0] == '@')
2896 {
2897 WCHAR *index_str;
2898 int index;
2899
2900 #ifdef __REACTOS__
2901 if (wcschr(src, '%') != NULL)
2902 {
2903 ExpandEnvironmentStringsW(src, szExpanded, ARRAY_SIZE(szExpanded));
2904 src = szExpanded;
2905 }
2906 #endif
2907 dst[0] = 0;
2908 dllname = StrDupW(src + 1);
2909 index_str = strchrW(dllname, ',');
2910
2911 if(!index_str) goto end;
2912
2913 *index_str = 0;
2914 index_str++;
2915 index = atoiW(index_str);
2916
2917 #ifdef __REACTOS__
2918 hmod = LoadLibraryExW(dllname, NULL, LOAD_LIBRARY_AS_DATAFILE);
2919 #else
2920 hmod = LoadLibraryW(dllname);
2921 #endif
2922 if(!hmod) goto end;
2923
2924 if(index < 0)
2925 {
2926 if(LoadStringW(hmod, -index, dst, dst_len))
2927 hr = S_OK;
2928 }
2929 else
2930 FIXME("can't handle non-negative indices (%d)\n", index);
2931 }
2932 else
2933 {
2934 if(dst != src)
2935 lstrcpynW(dst, src, dst_len);
2936 hr = S_OK;
2937 }
2938
2939 TRACE("returning %s\n", debugstr_w(dst));
2940 end:
2941 if(hmod) FreeLibrary(hmod);
2942 LocalFree(dllname);
2943 return hr;
2944 }
2945
IsCharSpaceA(CHAR c)2946 BOOL WINAPI IsCharSpaceA(CHAR c)
2947 {
2948 WORD CharType;
2949 return GetStringTypeA(GetSystemDefaultLCID(), CT_CTYPE1, &c, 1, &CharType) && (CharType & C1_SPACE);
2950 }
2951
2952 /*************************************************************************
2953 * @ [SHLWAPI.29]
2954 *
2955 * Determine if a Unicode character is a space.
2956 *
2957 * PARAMS
2958 * wc [I] Character to check.
2959 *
2960 * RETURNS
2961 * TRUE, if wc is a space,
2962 * FALSE otherwise.
2963 */
IsCharSpaceW(WCHAR wc)2964 BOOL WINAPI IsCharSpaceW(WCHAR wc)
2965 {
2966 WORD CharType;
2967
2968 return GetStringTypeW(CT_CTYPE1, &wc, 1, &CharType) && (CharType & C1_SPACE);
2969 }
2970