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