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