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