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