Commit | Line | Data |
---|---|---|
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 | 47 | WINE_DEFAULT_DEBUG_CHANNEL(shell); |
6430d93a | 48 | |
c0e6c94a | 49 | extern HINSTANCE shlwapi_hInstance; |
2e2d6ec7 | 50 | |
ae0c24fd AJ |
51 | static HRESULT _SHStrDupAA(LPCSTR,LPSTR*); |
52 | static HRESULT _SHStrDupAW(LPCWSTR,LPSTR*); | |
53e5bd50 | 53 | |
c18b0b7b | 54 | |
b19c9848 AT |
55 | static 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 | */ | |
96 | static 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 | */ | |
129 | static int FormatDouble(double value, int decimals, LPWSTR pszBuf, int cchBuf) | |
130 | { | |
131 | static const WCHAR flfmt[] = {'%','f',0}; | |
132 | WCHAR buf[64]; | |
133 | NUMBERFMTW fmt; | |
134 | WCHAR decimal[8], thousand[8]; | |
135 | ||
136 | snprintfW(buf, 64, flfmt, value); | |
137 | ||
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 | 157 | static 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 | 187 | static 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 | 205 | BOOL 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 | 217 | BOOL 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 | 236 | LPSTR 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 | 257 | LPWSTR 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 | 282 | LPSTR 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 | 303 | LPWSTR 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 | */ | |
324 | LPWSTR WINAPI StrChrNW(LPCWSTR lpszStr, WCHAR ch, UINT cchMax) | |
325 | { | |
326 | TRACE("(%s(%i),%i)\n", debugstr_wn(lpszStr,cchMax), cchMax, ch); | |
327 | ||
328 | if (lpszStr) | |
329 | { | |
330 | while (*lpszStr && cchMax-- > 0) | |
331 | { | |
332 | if (*lpszStr == ch) | |
333 | return (LPWSTR)lpszStr; | |
334 | lpszStr++; | |
335 | } | |
336 | } | |
337 | return NULL; | |
338 | } | |
339 | ||
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 | 353 | int 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 | 377 | INT 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 | */ | |
392 | INT 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 | 416 | int 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 | */ | |
431 | INT 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 | 454 | int 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 | */ | |
476 | LPWSTR 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 | */ | |
496 | LPWSTR 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 | 517 | LPWSTR 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 | 542 | static 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 | */ | |
573 | LPSTR 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 | */ | |
585 | LPWSTR 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 | 606 | LPSTR 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 | 645 | LPWSTR 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 | 680 | LPSTR 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 | 692 | LPWSTR 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 | */ | |
725 | LPWSTR 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 | */ | |
760 | LPWSTR 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 | 796 | int 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 | 818 | int 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 | 856 | BOOL 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 | */ | |
926 | BOOL 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 | */ |
1006 | LPSTR 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 | 1031 | LPWSTR 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 |
1056 | static 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 | 1091 | int 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 | */ | |
1103 | int 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 | 1123 | int 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 | */ | |
1135 | int 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 | */ | |
1155 | int 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 | 1167 | int 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 | */ | |
1197 | LPSTR 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 | */ | |
1218 | LPWSTR 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 |
1229 | static 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 | */ | |
1269 | LPSTR 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 | 1281 | LPWSTR 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 | */ | |
1310 | LPSTR 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 | 1322 | LPWSTR 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 | 1353 | LPSTR 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 | 1378 | LPWSTR 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 | 1415 | HRESULT 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 | 1464 | HRESULT 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 | 1523 | HRESULT 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 | 1554 | HRESULT 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() */ |
1581 | static 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 | */ | |
1617 | HRESULT 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 | */ | |
1658 | LPSTR 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 | */ | |
1674 | LPWSTR 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 | */ | |
1709 | LPSTR 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 | 1730 | LPWSTR 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 | 1759 | BOOL 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 | 1800 | BOOL 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 | 1840 | static 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 | 1877 | HRESULT 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 | 1907 | static 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 | */ |
1935 | HRESULT 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 | 1963 | static 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 | 1983 | static 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 |
2004 | static 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 | */ |
2048 | INT 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 | */ | |
2070 | INT 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 | */ | |
2127 | BOOL 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 | */ | |
2149 | BOOL 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 | 2180 | LPSTR 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 | 2199 | LPWSTR 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 | */ | |
2227 | INT 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 */ | |
2282 | typedef 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 | 2307 | LPWSTR 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 | 2385 | LPSTR 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 | */ |
2413 | LPSTR 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 | */ | |
2433 | DWORD 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 | 2464 | char 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 | */ | |
2498 | WCHAR 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 | 2541 | DWORD 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 | */ | |
2566 | DWORD 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 | 2591 | DWORD 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 | */ | |
2691 | INT 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 | */ | |
2710 | DWORD WINAPI SHAnsiToAnsi(LPCSTR lpszSrc, LPSTR lpszDst, int iLen) | |
2711 | { | |
2712 | LPSTR lpszRet; | |
2713 | ||
2714 | TRACE("(%s,%p,0x%08x)\n", debugstr_a(lpszSrc), lpszDst, iLen); | |
2715 | ||
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 | */ | |
2725 | DWORD WINAPI SHUnicodeToUnicode(LPCWSTR lpszSrc, LPWSTR lpszDst, int iLen) | |
2726 | { | |
2727 | LPWSTR lpszRet; | |
2728 | ||
2729 | TRACE("(%s,%p,0x%08x)\n", debugstr_w(lpszSrc), lpszDst, iLen); | |
2730 | ||
2731 | lpszRet = StrCpyNXW(lpszDst, lpszSrc, iLen); | |
2732 | return lpszRet - lpszDst + 1; | |
2733 | } | |
2734 | ||
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 | */ | |
2748 | BOOL WINAPI DoesStringRoundTripA(LPCSTR lpSrcStr, LPSTR lpDst, INT iLen) | |
2749 | { | |
2750 | lstrcpynA(lpDst, lpSrcStr, iLen); | |
2751 | return TRUE; | |
2752 | } | |
2753 | ||
2754 | /************************************************************************* | |
2755 | * @ [SHLWAPI.365] | |
2756 | * | |
2757 | * Determine if a Unicode string converts to Ascii and back identically. | |
2758 | * | |
2759 | * PARAMS | |
2760 | * lpSrcStr [I] Source Unicode string to convert | |
2761 | * lpDst [O] Destination for resulting Ascii string | |
2762 | * iLen [I] Length of lpDst in characters | |
2763 | * | |
2764 | * RETURNS | |
2765 | * TRUE, if lpSrcStr converts to Ascii and back identically, | |
2766 | * FALSE otherwise. | |
2767 | */ | |
2768 | BOOL WINAPI DoesStringRoundTripW(LPCWSTR lpSrcStr, LPSTR lpDst, INT iLen) | |
2769 | { | |
2770 | WCHAR szBuff[MAX_PATH]; | |
2771 | ||
2772 | SHUnicodeToAnsi(lpSrcStr, lpDst, iLen); | |
2773 | SHAnsiToUnicode(lpDst, szBuff, MAX_PATH); | |
2774 | return !strcmpW(lpSrcStr, szBuff); | |
2775 | } | |
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 | */ | |
2784 | HRESULT WINAPI SHLoadIndirectString(LPCWSTR src, LPWSTR dst, UINT dst_len, void **reserved) | |
2785 | { | |
2786 | WCHAR *dllname = NULL; | |
2787 | HMODULE hmod = NULL; | |
2788 | HRESULT hr = E_FAIL; | |
2789 | ||
2790 | TRACE("(%s %p %08x %p)\n", debugstr_w(src), dst, dst_len, reserved); | |
2791 | ||
2792 | if(src[0] == '@') | |
2793 | { | |
2794 | WCHAR *index_str; | |
2795 | int index; | |
2796 | ||
2797 | dst[0] = 0; | |
2798 | dllname = StrDupW(src + 1); | |
2799 | index_str = strchrW(dllname, ','); | |
2800 | ||
2801 | if(!index_str) goto end; | |
2802 | ||
2803 | *index_str = 0; | |
2804 | index_str++; | |
2805 | index = atoiW(index_str); | |
2806 | ||
2807 | hmod = LoadLibraryW(dllname); | |
2808 | if(!hmod) goto end; | |
2809 | ||
2810 | if(index < 0) | |
2811 | { | |
2812 | if(LoadStringW(hmod, -index, dst, dst_len)) | |
2813 | hr = S_OK; | |
2814 | } | |
2815 | else | |
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 |
2826 | end: |
2827 | if(hmod) FreeLibrary(hmod); | |
2828 | HeapFree(GetProcessHeap(), 0, dllname); | |
2829 | return hr; | |
2830 | } |