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