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