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