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