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