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