~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Wine Cross Reference
wine/dlls/shlwapi/string.c

Version: ~ [ wine-1.1.24 ] ~ [ wine-1.1.23 ] ~ [ wine-1.1.22 ] ~ [ wine-1.1.21 ] ~ [ wine-1.1.20 ] ~ [ wine-1.1.19 ] ~ [ wine-1.1.18 ] ~ [ wine-1.1.17 ] ~ [ wine-1.1.16 ] ~ [ wine-1.1.15 ] ~ [ wine-1.1.14 ] ~ [ wine-1.1.13 ] ~ [ wine-1.1.12 ] ~ [ wine-1.1.11 ] ~ [ wine-1.1.10 ] ~ [ wine-1.1.9 ] ~ [ wine-1.1.8 ] ~ [ wine-1.1.7 ] ~ [ wine-1.0.1 ] ~ [ wine-1.1.6 ] ~ [ wine-1.1.5 ] ~ [ wine-1.1.4 ] ~ [ wine-1.1.3 ] ~ [ wine-1.1.2 ] ~ [ wine-1.1.1 ] ~ [ wine-1.1.0 ] ~ [ wine-1.0 ] ~

  1 /*
  2  * Shlwapi string functions
  3  *
  4  * Copyright 1998 Juergen Schmied
  5  * Copyright 2002 Jon Griffiths
  6  *
  7  * This library is free software; you can redistribute it and/or
  8  * modify it under the terms of the GNU Lesser General Public
  9  * License as published by the Free Software Foundation; either
 10  * version 2.1 of the License, or (at your option) any later version.
 11  *
 12  * This library is distributed in the hope that it will be useful,
 13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 15  * Lesser General Public License for more details.
 16  *
 17  * You should have received a copy of the GNU Lesser General Public
 18  * License along with this library; if not, write to the Free Software
 19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 20  */
 21 
 22 #include "config.h"
 23 #include "wine/port.h"
 24 
 25 #include <math.h>
 26 #include <stdarg.h>
 27 #include <stdio.h>
 28 #include <string.h>
 29 
 30 #define NONAMELESSUNION
 31 #define NONAMELESSSTRUCT
 32 #include "windef.h"
 33 #include "winbase.h"
 34 #define NO_SHLWAPI_REG
 35 #define NO_SHLWAPI_STREAM
 36 #include "shlwapi.h"
 37 #include "wingdi.h"
 38 #include "winuser.h"
 39 #include "shlobj.h"
 40 #include "mlang.h"
 41 #include "ddeml.h"
 42 #include "wine/unicode.h"
 43 #include "wine/debug.h"
 44 
 45 #include "resource.h"
 46 
 47 WINE_DEFAULT_DEBUG_CHANNEL(shell);
 48 
 49 extern HINSTANCE shlwapi_hInstance;
 50 
 51 static HRESULT _SHStrDupAA(LPCSTR,LPSTR*);
 52 static HRESULT _SHStrDupAW(LPCWSTR,LPSTR*);
 53 
 54 
 55 static void FillNumberFmt(NUMBERFMTW *fmt, LPWSTR decimal_buffer, int decimal_bufwlen,
 56                           LPWSTR thousand_buffer, int thousand_bufwlen)
 57 {
 58   WCHAR grouping[64];
 59   WCHAR *c;
 60 
 61   GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_ILZERO|LOCALE_RETURN_NUMBER, (LPWSTR)&fmt->LeadingZero, sizeof(fmt->LeadingZero)/sizeof(WCHAR));
 62   GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_INEGNUMBER|LOCALE_RETURN_NUMBER, (LPWSTR)&fmt->LeadingZero, sizeof(fmt->NegativeOrder)/sizeof(WCHAR));
 63   fmt->NumDigits = 0;
 64   GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, decimal_buffer, decimal_bufwlen);
 65   GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, thousand_buffer, thousand_bufwlen);
 66   fmt->lpThousandSep = thousand_buffer;
 67   fmt->lpDecimalSep = decimal_buffer;
 68 
 69   /* 
 70    * Converting grouping string to number as described on 
 71    * http://blogs.msdn.com/oldnewthing/archive/2006/04/18/578251.aspx
 72    */
 73   fmt->Grouping = 0;
 74   GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SGROUPING, grouping, sizeof(grouping)/sizeof(WCHAR));
 75   for (c = grouping; *c; c++)
 76     if (*c >= '' && *c < '9')
 77     {
 78       fmt->Grouping *= 10;
 79       fmt->Grouping += *c - '';
 80     }
 81 
 82   if (fmt->Grouping % 10 == 0)
 83     fmt->Grouping /= 10;
 84   else
 85     fmt->Grouping *= 10;
 86 }
 87 
 88 /*************************************************************************
 89  * FormatInt   [internal]
 90  *
 91  * Format an integer according to the current locale
 92  *
 93  * RETURNS
 94  *  The number of 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];
100   WCHAR buf[24];
101   WCHAR *c;
102   BOOL neg = (qdwValue < 0);
103 
104   FillNumberFmt(&fmt, decimal, sizeof decimal / sizeof (WCHAR),
105                 thousand, sizeof thousand / sizeof (WCHAR));
106 
107   c = &buf[24];
108   *(--c) = 0;
109   do
110   {
111     *(--c) = '' + (qdwValue%10);
112     qdwValue /= 10;
113   } while (qdwValue > 0);
114   if (neg)
115     *(--c) = '-';
116   
117   return GetNumberFormatW(LOCALE_USER_DEFAULT, 0, c, &fmt, pszBuf, cchBuf);
118 }
119 
120 /*************************************************************************
121  * FormatDouble   [internal]
122  *
123  * Format an integer according to the current locale. Prints the specified number of digits
124  * after the decimal point
125  *
126  * RETURNS
127  *  The number of 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 
138   FillNumberFmt(&fmt, decimal, sizeof decimal / sizeof (WCHAR),
139                  thousand, sizeof thousand / sizeof (WCHAR));
140   fmt.NumDigits = decimals;
141   return GetNumberFormatW(LOCALE_USER_DEFAULT, 0, buf, &fmt, pszBuf, cchBuf);
142 }
143 
144 /*************************************************************************
145  * SHLWAPI_ChrCmpHelperA
146  *
147  * Internal helper for SHLWAPI_ChrCmpA/ChrCMPIA.
148  *
149  * NOTES
150  *  Both this function and its Unicode counterpart are very inefficient. To
151  *  fix this, CompareString must be completely implemented and optimised
152  *  first. Then the core character test can be taken out of that function and
153  *  placed here, so that it need never be called at all. Until then, do not
154  *  attempt to optimise this code unless you are willing to test that it
155  *  still performs correctly.
156  */
157 static BOOL SHLWAPI_ChrCmpHelperA(WORD ch1, WORD ch2, DWORD dwFlags)
158 {
159   char str1[3], str2[3];
160 
161   str1[0] = LOBYTE(ch1);
162   if (IsDBCSLeadByte(str1[0]))
163   {
164     str1[1] = HIBYTE(ch1);
165     str1[2] = '\0';
166   }
167   else
168     str1[1] = '\0';
169 
170   str2[0] = LOBYTE(ch2);
171   if (IsDBCSLeadByte(str2[0]))
172   {
173     str2[1] = HIBYTE(ch2);
174     str2[2] = '\0';
175   }
176   else
177     str2[1] = '\0';
178 
179   return CompareStringA(GetThreadLocale(), dwFlags, str1, -1, str2, -1) - 2;
180 }
181 
182 /*************************************************************************
183  * SHLWAPI_ChrCmpA
184  *
185  * Internal helper function.
186  */
187 static BOOL WINAPI SHLWAPI_ChrCmpA(WORD ch1, WORD ch2)
188 {
189   return SHLWAPI_ChrCmpHelperA(ch1, ch2, 0);
190 }
191 
192 /*************************************************************************
193  * ChrCmpIA     (SHLWAPI.385)
194  *
195  * Compare two characters, ignoring case.
196  *
197  * PARAMS
198  *  ch1 [I] First character to compare
199  *  ch2 [I] Second character to compare
200  *
201  * RETURNS
202  *  FALSE, if the characters are equal.
203  *  Non-zero otherwise.
204  */
205 BOOL WINAPI ChrCmpIA(WORD ch1, WORD ch2)
206 {
207   TRACE("(%d,%d)\n", ch1, ch2);
208 
209   return SHLWAPI_ChrCmpHelperA(ch1, ch2, NORM_IGNORECASE);
210 }
211 
212 /*************************************************************************
213  * ChrCmpIW     [SHLWAPI.386]
214  *
215  * See ChrCmpIA.
216  */
217 BOOL WINAPI ChrCmpIW(WCHAR ch1, WCHAR ch2)
218 {
219   return CompareStringW(GetThreadLocale(), NORM_IGNORECASE, &ch1, 1, &ch2, 1) - 2;
220 }
221 
222 /*************************************************************************
223  * StrChrA      [SHLWAPI.@]
224  *
225  * Find a given character in a string.
226  *
227  * PARAMS
228  *  lpszStr [I] String to search in.
229  *  ch      [I] Character to search for.
230  *
231  * RETURNS
232  *  Success: A pointer to the first occurrence of ch in lpszStr, or NULL if
233  *           not found.
234  *  Failure: NULL, if any arguments are invalid.
235  */
236 LPSTR WINAPI StrChrA(LPCSTR lpszStr, WORD ch)
237 {
238   TRACE("(%s,%i)\n", debugstr_a(lpszStr), ch);
239 
240   if (lpszStr)
241   {
242     while (*lpszStr)
243     {
244       if (!SHLWAPI_ChrCmpA(*lpszStr, ch))
245         return (LPSTR)lpszStr;
246       lpszStr = CharNextA(lpszStr);
247     }
248   }
249   return NULL;
250 }
251 
252 /*************************************************************************
253  * StrChrW      [SHLWAPI.@]
254  *
255  * See StrChrA.
256  */
257 LPWSTR WINAPI StrChrW(LPCWSTR lpszStr, WCHAR ch)
258 {
259   LPWSTR lpszRet = NULL;
260 
261   TRACE("(%s,%i)\n", debugstr_w(lpszStr), ch);
262 
263   if (lpszStr)
264     lpszRet = strchrW(lpszStr, ch);
265   return lpszRet;
266 }
267 
268 /*************************************************************************
269  * StrChrIA     [SHLWAPI.@]
270  *
271  * Find a given character in a string, ignoring case.
272  *
273  * PARAMS
274  *  lpszStr [I] String to search in.
275  *  ch      [I] Character to search for.
276  *
277  * RETURNS
278  *  Success: A pointer to the first occurrence of ch in lpszStr, or NULL if
279  *           not found.
280  *  Failure: NULL, if any arguments are invalid.
281  */
282 LPSTR WINAPI StrChrIA(LPCSTR lpszStr, WORD ch)
283 {
284   TRACE("(%s,%i)\n", debugstr_a(lpszStr), ch);
285 
286   if (lpszStr)
287   {
288     while (*lpszStr)
289     {
290       if (!ChrCmpIA(*lpszStr, ch))
291         return (LPSTR)lpszStr;
292       lpszStr = CharNextA(lpszStr);
293     }
294   }
295   return NULL;
296 }
297 
298 /*************************************************************************
299  * StrChrIW     [SHLWAPI.@]
300  *
301  * See StrChrA.
302  */
303 LPWSTR WINAPI StrChrIW(LPCWSTR lpszStr, WCHAR ch)
304 {
305   TRACE("(%s,%i)\n", debugstr_w(lpszStr), ch);
306 
307   if (lpszStr)
308   {
309     ch = toupperW(ch);
310     while (*lpszStr)
311     {
312       if (toupperW(*lpszStr) == ch)
313         return (LPWSTR)lpszStr;
314       lpszStr++;
315     }
316     lpszStr = NULL;
317   }
318   return (LPWSTR)lpszStr;
319 }
320 
321 /*************************************************************************
322  * StrCmpIW     [SHLWAPI.@]
323  *
324  * Compare two strings, ignoring case.
325  *
326  * PARAMS
327  *  lpszStr  [I] First string to compare
328  *  lpszComp [I] Second string to compare
329  *
330  * RETURNS
331  *  An integer less than, equal to or greater than 0, indicating that
332  *  lpszStr is less than, the same, or greater than lpszComp.
333  */
334 int WINAPI StrCmpIW(LPCWSTR lpszStr, LPCWSTR lpszComp)
335 {
336   int iRet;
337 
338   TRACE("(%s,%s)\n", debugstr_w(lpszStr),debugstr_w(lpszComp));
339 
340   iRet = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, lpszStr, -1, lpszComp, -1);
341   return iRet == CSTR_LESS_THAN ? -1 : iRet == CSTR_GREATER_THAN ? 1 : 0;
342 }
343 
344 /*************************************************************************
345  * StrCmpNA     [SHLWAPI.@]
346  *
347  * Compare two strings, up to a maximum length.
348  *
349  * PARAMS
350  *  lpszStr  [I] First string to compare
351  *  lpszComp [I] Second string to compare
352  *  iLen     [I] Maximum number of chars to compare.
353  *
354  * RETURNS
355  *  An integer less than, equal to or greater than 0, indicating that
356  *  lpszStr is less than, the same, or greater than lpszComp.
357  */
358 INT WINAPI StrCmpNA(LPCSTR lpszStr, LPCSTR lpszComp, INT iLen)
359 {
360   INT iRet;
361 
362   TRACE("(%s,%s,%i)\n", debugstr_a(lpszStr), debugstr_a(lpszComp), iLen);
363 
364   iRet = CompareStringA(GetThreadLocale(), 0, lpszStr, iLen, lpszComp, iLen);
365   return iRet == CSTR_LESS_THAN ? -1 : iRet == CSTR_GREATER_THAN ? 1 : 0;
366 }
367 
368 /*************************************************************************
369  * StrCmpNW     [SHLWAPI.@]
370  *
371  * See StrCmpNA.
372  */
373 INT WINAPI StrCmpNW(LPCWSTR lpszStr, LPCWSTR lpszComp, INT iLen)
374 {
375   INT iRet;
376 
377   TRACE("(%s,%s,%i)\n", debugstr_w(lpszStr), debugstr_w(lpszComp), iLen);
378 
379   iRet = CompareStringW(GetThreadLocale(), 0, lpszStr, iLen, lpszComp, iLen);
380   return iRet == CSTR_LESS_THAN ? -1 : iRet == CSTR_GREATER_THAN ? 1 : 0;
381 }
382 
383 /*************************************************************************
384  * StrCmpNIA    [SHLWAPI.@]
385  *
386  * Compare two strings, up to a maximum length, ignoring case.
387  *
388  * PARAMS
389  *  lpszStr  [I] First string to compare
390  *  lpszComp [I] Second string to compare
391  *  iLen     [I] Maximum number of chars to compare.
392  *
393  * RETURNS
394  *  An integer less than, equal to or greater than 0, indicating that
395  *  lpszStr is less than, the same, or greater than lpszComp.
396  */
397 int WINAPI StrCmpNIA(LPCSTR lpszStr, LPCSTR lpszComp, int iLen)
398 {
399   INT iRet;
400 
401   TRACE("(%s,%s,%i)\n", debugstr_a(lpszStr), debugstr_a(lpszComp), iLen);
402 
403   iRet = CompareStringA(GetThreadLocale(), NORM_IGNORECASE, lpszStr, iLen, lpszComp, iLen);
404   return iRet == CSTR_LESS_THAN ? -1 : iRet == CSTR_GREATER_THAN ? 1 : 0;
405 }
406 
407 /*************************************************************************
408  * StrCmpNIW    [SHLWAPI.@]
409  *
410  * See StrCmpNIA.
411  */
412 INT WINAPI StrCmpNIW(LPCWSTR lpszStr, LPCWSTR lpszComp, int iLen)
413 {
414   INT iRet;
415 
416   TRACE("(%s,%s,%i)\n", debugstr_w(lpszStr), debugstr_w(lpszComp), iLen);
417 
418   iRet = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, lpszStr, iLen, lpszComp, iLen);
419   return iRet == CSTR_LESS_THAN ? -1 : iRet == CSTR_GREATER_THAN ? 1 : 0;
420 }
421 
422 /*************************************************************************
423  * StrCmpW      [SHLWAPI.@]
424  *
425  * Compare two strings.
426  *
427  * PARAMS
428  *  lpszStr  [I] First string to compare
429  *  lpszComp [I] Second string to compare
430  *
431  * RETURNS
432  *  An integer less than, equal to or greater than 0, indicating that
433  *  lpszStr is less than, the same, or greater than lpszComp.
434  */
435 int WINAPI StrCmpW(LPCWSTR lpszStr, LPCWSTR lpszComp)
436 {
437   INT iRet;
438 
439   TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszComp));
440 
441   iRet = CompareStringW(GetThreadLocale(), 0, lpszStr, -1, lpszComp, -1);
442   return iRet == CSTR_LESS_THAN ? -1 : iRet == CSTR_GREATER_THAN ? 1 : 0;
443 }
444 
445 /*************************************************************************
446  * StrCatW      [SHLWAPI.@]
447  *
448  * Concatenate two strings.
449  *
450  * PARAMS
451  *  lpszStr [O] Initial string
452  *  lpszSrc [I] String to concatenate
453  *
454  * RETURNS
455  *  lpszStr.
456  */
457 LPWSTR WINAPI StrCatW(LPWSTR lpszStr, LPCWSTR lpszSrc)
458 {
459   TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszSrc));
460 
461   strcatW(lpszStr, lpszSrc);
462   return lpszStr;
463 }
464 
465 /*************************************************************************
466  * StrCpyW      [SHLWAPI.@]
467  *
468  * Copy a string to another string.
469  *
470  * PARAMS
471  *  lpszStr [O] Destination string
472  *  lpszSrc [I] Source string
473  *
474  * RETURNS
475  *  lpszStr.
476  */
477 LPWSTR WINAPI StrCpyW(LPWSTR lpszStr, LPCWSTR lpszSrc)
478 {
479   TRACE("(%p,%s)\n", lpszStr, debugstr_w(lpszSrc));
480 
481   strcpyW(lpszStr, lpszSrc);
482   return lpszStr;
483 }
484 
485 /*************************************************************************
486  * StrCpyNW     [SHLWAPI.@]
487  *
488  * Copy a string to another string, up to a maximum number of characters.
489  *
490  * PARAMS
491  *  lpszStr  [O] Destination string
492  *  lpszSrc  [I] Source string
493  *  iLen     [I] Maximum number of chars to copy
494  *
495  * RETURNS
496  *  lpszStr.
497  */
498 LPWSTR WINAPI StrCpyNW(LPWSTR lpszStr, LPCWSTR lpszSrc, int iLen)
499 {
500   TRACE("(%p,%s,%i)\n", lpszStr, debugstr_w(lpszSrc), iLen);
501 
502   lstrcpynW(lpszStr, lpszSrc, iLen);
503   return lpszStr;
504 }
505 
506 
507 
508 /*************************************************************************
509  * SHLWAPI_StrStrHelperA
510  *
511  * Internal implementation of StrStrA/StrStrIA
512  */
513 static LPSTR SHLWAPI_StrStrHelperA(LPCSTR lpszStr, LPCSTR lpszSearch,
514                                    INT (WINAPI *pStrCmpFn)(LPCSTR,LPCSTR,INT))
515 {
516   size_t iLen;
517 
518   if (!lpszStr || !lpszSearch || !*lpszSearch)
519     return NULL;
520 
521   iLen = strlen(lpszSearch);
522 
523   while (*lpszStr)
524   {
525     if (!pStrCmpFn(lpszStr, lpszSearch, iLen))
526       return (LPSTR)lpszStr;
527     lpszStr = CharNextA(lpszStr);
528   }
529   return NULL;
530 }
531 
532 /*************************************************************************
533  * StrStrA      [SHLWAPI.@]
534  *
535  * Find a substring within a string.
536  *
537  * PARAMS
538  *  lpszStr    [I] String to search in
539  *  lpszSearch [I] String to look for
540  *
541  * RETURNS
542  *  The start of lpszSearch within lpszStr, or NULL if not found.
543  */
544 LPSTR WINAPI StrStrA(LPCSTR lpszStr, LPCSTR lpszSearch)
545 {
546   TRACE("(%s,%s)\n", debugstr_a(lpszStr), debugstr_a(lpszSearch));
547 
548   return SHLWAPI_StrStrHelperA(lpszStr, lpszSearch, StrCmpNA);
549 }
550 
551 /*************************************************************************
552  * StrStrW      [SHLWAPI.@]
553  *
554  * See StrStrA.
555  */
556 LPWSTR WINAPI StrStrW(LPCWSTR lpszStr, LPCWSTR lpszSearch)
557 {
558     if (!lpszStr || !lpszSearch) return NULL;
559     return strstrW( lpszStr, lpszSearch );
560 }
561 
562 /*************************************************************************
563  * StrRStrIA    [SHLWAPI.@]
564  *
565  * Find the last occurrence of a substring within a string.
566  *
567  * PARAMS
568  *  lpszStr    [I] String to search in
569  *  lpszEnd    [I] End of lpszStr
570  *  lpszSearch [I] String to look for
571  *
572  * RETURNS
573  *  The last occurrence lpszSearch within lpszStr, or NULL if not found.
574  */
575 LPSTR WINAPI StrRStrIA(LPCSTR lpszStr, LPCSTR lpszEnd, LPCSTR lpszSearch)
576 {
577   WORD ch1, ch2;
578   INT iLen;
579 
580   TRACE("(%s,%s)\n", debugstr_a(lpszStr), debugstr_a(lpszSearch));
581 
582   if (!lpszStr || !lpszSearch || !*lpszSearch)
583     return NULL;
584 
585   if (!lpszEnd)
586     lpszEnd = lpszStr + lstrlenA(lpszStr);
587   if (lpszEnd == lpszStr)
588     return NULL;
589 
590   if (IsDBCSLeadByte(*lpszSearch))
591     ch1 = *lpszSearch << 8 | (UCHAR)lpszSearch[1];
592   else
593     ch1 = *lpszSearch;
594   iLen = lstrlenA(lpszSearch);
595 
596   do
597   {
598     lpszEnd = CharPrevA(lpszStr, lpszEnd);
599     ch2 = IsDBCSLeadByte(*lpszEnd)? *lpszEnd << 8 | (UCHAR)lpszEnd[1] : *lpszEnd;
600     if (!ChrCmpIA(ch1, ch2))
601     {
602       if (!StrCmpNIA(lpszEnd, lpszSearch, iLen))
603         return (LPSTR)lpszEnd;
604     }
605   } while (lpszEnd > lpszStr);
606   return NULL;
607 }
608 
609 /*************************************************************************
610  * StrRStrIW    [SHLWAPI.@]
611  *
612  * See StrRStrIA.
613  */
614 LPWSTR WINAPI StrRStrIW(LPCWSTR lpszStr, LPCWSTR lpszEnd, LPCWSTR lpszSearch)
615 {
616   INT iLen;
617 
618   TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszSearch));
619 
620   if (!lpszStr || !lpszSearch || !*lpszSearch)
621     return NULL;
622 
623   if (!lpszEnd)
624     lpszEnd = lpszStr + strlenW(lpszStr);
625 
626   iLen = strlenW(lpszSearch);
627 
628   while (lpszEnd > lpszStr)
629   {
630     lpszEnd--;
631     if (!StrCmpNIW(lpszEnd, lpszSearch, iLen))
632       return (LPWSTR)lpszEnd;
633   }
634   return NULL;
635 }
636 
637 /*************************************************************************
638  * StrStrIA     [SHLWAPI.@]
639  *
640  * Find a substring within a string, ignoring case.
641  *
642  * PARAMS
643  *  lpszStr    [I] String to search in
644  *  lpszSearch [I] String to look for
645  *
646  * RETURNS
647  *  The start of lpszSearch within lpszStr, or NULL if not found.
648  */
649 LPSTR WINAPI StrStrIA(LPCSTR lpszStr, LPCSTR lpszSearch)
650 {
651   TRACE("(%s,%s)\n", debugstr_a(lpszStr), debugstr_a(lpszSearch));
652 
653   return SHLWAPI_StrStrHelperA(lpszStr, lpszSearch, StrCmpNIA);
654 }
655 
656 /*************************************************************************
657  * StrStrIW     [SHLWAPI.@]
658  *
659  * See StrStrIA.
660  */
661 LPWSTR WINAPI StrStrIW(LPCWSTR lpszStr, LPCWSTR lpszSearch)
662 {
663   int iLen;
664 
665   TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszSearch));
666 
667   if (!lpszStr || !lpszSearch || !*lpszSearch)
668     return NULL;
669 
670   iLen = strlenW(lpszSearch);
671 
672   while (*lpszStr)
673   {
674     if (!StrCmpNIW(lpszStr, lpszSearch, iLen))
675       return (LPWSTR)lpszStr;
676     lpszStr++;
677   }
678   return NULL;
679 }
680 
681 /*************************************************************************
682  * StrToIntA    [SHLWAPI.@]
683  *
684  * Read a signed integer from a string.
685  *
686  * PARAMS
687  *  lpszStr [I] String to read integer from
688  *
689  * RETURNS
690  *   The signed integer value represented by the string, or 0 if no integer is
691  *   present.
692  *
693  * NOTES
694  *  No leading space is allowed before the number, although a leading '-' is.
695  */
696 int WINAPI StrToIntA(LPCSTR lpszStr)
697 {
698   int iRet = 0;
699 
700   TRACE("(%s)\n", debugstr_a(lpszStr));
701 
702   if (!lpszStr)
703   {
704     WARN("Invalid lpszStr would crash under Win32!\n");
705     return 0;
706   }
707 
708   if (*lpszStr == '-' || isdigit(*lpszStr))
709     StrToIntExA(lpszStr, 0, &iRet);
710   return iRet;
711 }
712 
713 /*************************************************************************
714  * StrToIntW    [SHLWAPI.@]
715  *
716  * See StrToIntA.
717  */
718 int WINAPI StrToIntW(LPCWSTR lpszStr)
719 {
720   int iRet = 0;
721 
722   TRACE("(%s)\n", debugstr_w(lpszStr));
723 
724   if (!lpszStr)
725   {
726     WARN("Invalid lpszStr would crash under Win32!\n");
727     return 0;
728   }
729 
730   if (*lpszStr == '-' || isdigitW(*lpszStr))
731     StrToIntExW(lpszStr, 0, &iRet);
732   return iRet;
733 }
734 
735 /*************************************************************************
736  * StrToIntExA  [SHLWAPI.@]
737  *
738  * Read an integer from a string.
739  *
740  * PARAMS
741  *  lpszStr [I] String to read integer from
742  *  dwFlags [I] Flags controlling the conversion
743  *  lpiRet  [O] Destination for read integer.
744  *
745  * RETURNS
746  *  Success: TRUE. lpiRet contains the integer value represented by the string.
747  *  Failure: FALSE, if the string is invalid, or no number is present.
748  *
749  * NOTES
750  *  Leading whitespace, '-' and '+' are allowed before the number. If
751  *  dwFlags includes STIF_SUPPORT_HEX, hexadecimal numbers are allowed, if
752  *  preceded by '0x'. If this flag is not set, or there is no '0x' prefix,
753  *  the string is treated as a decimal string. A leading '-' is ignored for
754  *  hexadecimal numbers.
755  */
756 BOOL WINAPI StrToIntExA(LPCSTR lpszStr, DWORD dwFlags, LPINT lpiRet)
757 {
758   BOOL bNegative = FALSE;
759   int iRet = 0;
760 
761   TRACE("(%s,%08X,%p)\n", debugstr_a(lpszStr), dwFlags, lpiRet);
762 
763   if (!lpszStr || !lpiRet)
764   {
765     WARN("Invalid parameter would crash under Win32!\n");
766     return FALSE;
767   }
768   if (dwFlags > STIF_SUPPORT_HEX)
769   {
770     WARN("Unknown flags (%08lX)!\n", dwFlags & ~STIF_SUPPORT_HEX);
771   }
772 
773   /* Skip leading space, '+', '-' */
774   while (isspace(*lpszStr))
775     lpszStr = CharNextA(lpszStr);
776 
777   if (*lpszStr == '-')
778   {
779     bNegative = TRUE;
780     lpszStr++;
781   }
782   else if (*lpszStr == '+')
783     lpszStr++;
784 
785   if (dwFlags & STIF_SUPPORT_HEX &&
786       *lpszStr == '' && tolower(lpszStr[1]) == 'x')
787   {
788     /* Read hex number */
789     lpszStr += 2;
790 
791     if (!isxdigit(*lpszStr))
792       return FALSE;
793 
794     while (isxdigit(*lpszStr))
795     {
796       iRet = iRet * 16;
797       if (isdigit(*lpszStr))
798         iRet += (*lpszStr - '');
799       else
800         iRet += 10 + (tolower(*lpszStr) - 'a');
801       lpszStr++;
802     }
803     *lpiRet = iRet;
804     return TRUE;
805   }
806 
807   /* Read decimal number */
808   if (!isdigit(*lpszStr))
809     return FALSE;
810 
811   while (isdigit(*lpszStr))
812   {
813     iRet = iRet * 10;
814     iRet += (*lpszStr - '');
815     lpszStr++;
816   }
817   *lpiRet = bNegative ? -iRet : iRet;
818   return TRUE;
819 }
820 
821 /*************************************************************************
822  * StrToIntExW  [SHLWAPI.@]
823  *
824  * See StrToIntExA.
825  */
826 BOOL WINAPI StrToIntExW(LPCWSTR lpszStr, DWORD dwFlags, LPINT lpiRet)
827 {
828   BOOL bNegative = FALSE;
829   int iRet = 0;
830 
831   TRACE("(%s,%08X,%p)\n", debugstr_w(lpszStr), dwFlags, lpiRet);
832 
833   if (!lpszStr || !lpiRet)
834   {
835     WARN("Invalid parameter would crash under Win32!\n");
836     return FALSE;
837   }
838   if (dwFlags > STIF_SUPPORT_HEX)
839   {
840     WARN("Unknown flags (%08lX)!\n", dwFlags & ~STIF_SUPPORT_HEX);
841   }
842 
843   /* Skip leading space, '+', '-' */
844   while (isspaceW(*lpszStr)) lpszStr++;
845 
846   if (*lpszStr == '-')
847   {
848     bNegative = TRUE;
849     lpszStr++;
850   }
851   else if (*lpszStr == '+')
852     lpszStr++;
853 
854   if (dwFlags & STIF_SUPPORT_HEX &&
855       *lpszStr == '' && tolowerW(lpszStr[1]) == 'x')
856   {
857     /* Read hex number */
858     lpszStr += 2;
859 
860     if (!isxdigitW(*lpszStr))
861       return FALSE;
862 
863     while (isxdigitW(*lpszStr))
864     {
865       iRet = iRet * 16;
866       if (isdigitW(*lpszStr))
867         iRet += (*lpszStr - '');
868       else
869         iRet += 10 + (tolowerW(*lpszStr) - 'a');
870       lpszStr++;
871     }
872     *lpiRet = iRet;
873     return TRUE;
874   }
875 
876   /* Read decimal number */
877   if (!isdigitW(*lpszStr))
878     return FALSE;
879 
880   while (isdigitW(*lpszStr))
881   {
882     iRet = iRet * 10;
883     iRet += (*lpszStr - '');
884     lpszStr++;
885   }
886   *lpiRet = bNegative ? -iRet : iRet;
887   return TRUE;
888 }
889 
890 /*************************************************************************
891  * StrDupA      [SHLWAPI.@]
892  *
893  * Duplicate a string.
894  *
895  * PARAMS
896  *  lpszStr [I] String to duplicate.
897  *
898  * RETURNS
899  *  Success: A pointer to a new string containing the contents of lpszStr
900  *  Failure: NULL, if memory cannot be allocated
901  *
902  * NOTES
903  *  The string memory is allocated with LocalAlloc(), and so should be released
904  *  by calling LocalFree().
905  */
906 LPSTR WINAPI StrDupA(LPCSTR lpszStr)
907 {
908   int iLen;
909   LPSTR lpszRet;
910 
911   TRACE("(%s)\n",debugstr_a(lpszStr));
912 
913   iLen = lpszStr ? strlen(lpszStr) + 1 : 1;
914   lpszRet = LocalAlloc(LMEM_FIXED, iLen);
915 
916   if (lpszRet)
917   {
918     if (lpszStr)
919       memcpy(lpszRet, lpszStr, iLen);
920     else
921       *lpszRet = '\0';
922   }
923   return lpszRet;
924 }
925 
926 /*************************************************************************
927  * StrDupW      [SHLWAPI.@]
928  *
929  * See StrDupA.
930  */
931 LPWSTR WINAPI StrDupW(LPCWSTR lpszStr)
932 {
933   int iLen;
934   LPWSTR lpszRet;
935 
936   TRACE("(%s)\n",debugstr_w(lpszStr));
937 
938   iLen = (lpszStr ? strlenW(lpszStr) + 1 : 1) * sizeof(WCHAR);
939   lpszRet = LocalAlloc(LMEM_FIXED, iLen);
940 
941   if (lpszRet)
942   {
943     if (lpszStr)
944       memcpy(lpszRet, lpszStr, iLen);
945     else
946       *lpszRet = '\0';
947   }
948   return lpszRet;
949 }
950 
951 /*************************************************************************
952  * SHLWAPI_StrSpnHelperA
953  *
954  * Internal implementation of StrSpnA/StrCSpnA/StrCSpnIA
955  */
956 static int SHLWAPI_StrSpnHelperA(LPCSTR lpszStr, LPCSTR lpszMatch,
957                                  LPSTR (WINAPI *pStrChrFn)(LPCSTR,WORD),
958                                  BOOL bInvert)
959 {
960   LPCSTR lpszRead = lpszStr;
961   if (lpszStr && *lpszStr && lpszMatch)
962   {
963     while (*lpszRead)
964     {
965       LPCSTR lpszTest = pStrChrFn(lpszMatch, *lpszRead);
966 
967       if (!bInvert && !lpszTest)
968         break;
969       if (bInvert && lpszTest)
970         break;
971       lpszRead = CharNextA(lpszRead);
972     };
973   }
974   return lpszRead - lpszStr;
975 }
976 
977 /*************************************************************************
978  * StrSpnA      [SHLWAPI.@]
979  *
980  * Find the length of the start of a string that contains only certain
981  * characters.
982  *
983  * PARAMS
984  *  lpszStr   [I] String to search
985  *  lpszMatch [I] Characters that can be in the substring
986  *
987  * RETURNS
988  *  The length of the part of lpszStr containing only chars from lpszMatch,
989  *  or 0 if any parameter is invalid.
990  */
991 int WINAPI StrSpnA(LPCSTR lpszStr, LPCSTR lpszMatch)
992 {
993   TRACE("(%s,%s)\n",debugstr_a(lpszStr), debugstr_a(lpszMatch));
994 
995   return SHLWAPI_StrSpnHelperA(lpszStr, lpszMatch, StrChrA, FALSE);
996 }
997 
998 /*************************************************************************
999  * StrSpnW      [SHLWAPI.@]
1000  *
1001  * See StrSpnA.
1002  */
1003 int WINAPI StrSpnW(LPCWSTR lpszStr, LPCWSTR lpszMatch)
1004 {
1005     if (!lpszStr || !lpszMatch) return 0;
1006     return strspnW( lpszStr, lpszMatch );
1007 }
1008 
1009 /*************************************************************************
1010  * StrCSpnA     [SHLWAPI.@]
1011  *
1012  * Find the length of the start of a string that does not contain certain
1013  * characters.
1014  *
1015  * PARAMS
1016  *  lpszStr   [I] String to search
1017  *  lpszMatch [I] Characters that cannot be in the substring
1018  *
1019  * RETURNS
1020  *  The length of the part of lpszStr containing only chars not in lpszMatch,
1021  *  or 0 if any parameter is invalid.
1022  */
1023 int WINAPI StrCSpnA(LPCSTR lpszStr, LPCSTR lpszMatch)
1024 {
1025   TRACE("(%s,%s)\n",debugstr_a(lpszStr), debugstr_a(lpszMatch));
1026 
1027   return SHLWAPI_StrSpnHelperA(lpszStr, lpszMatch, StrChrA, TRUE);
1028 }
1029 
1030 /*************************************************************************
1031  * StrCSpnW     [SHLWAPI.@]
1032  *
1033  * See StrCSpnA.
1034  */
1035 int WINAPI StrCSpnW(LPCWSTR lpszStr, LPCWSTR lpszMatch)
1036 {
1037     if (!lpszStr || !lpszMatch) return 0;
1038     return strcspnW( lpszStr, lpszMatch );
1039 }
1040 
1041 /*************************************************************************
1042  * StrCSpnIA    [SHLWAPI.@]
1043  *
1044  * Find the length of the start of a string that does not contain certain
1045  * characters, ignoring case.
1046  *
1047  * PARAMS
1048  *  lpszStr   [I] String to search
1049  *  lpszMatch [I] Characters that cannot be in the substring
1050  *
1051  * RETURNS
1052  *  The length of the part of lpszStr containing only chars not in lpszMatch,
1053  *  or 0 if any parameter is invalid.
1054  */
1055 int WINAPI StrCSpnIA(LPCSTR lpszStr, LPCSTR lpszMatch)
1056 {
1057   TRACE("(%s,%s)\n",debugstr_a(lpszStr), debugstr_a(lpszMatch));
1058 
1059   return SHLWAPI_StrSpnHelperA(lpszStr, lpszMatch, StrChrIA, TRUE);
1060 }
1061 
1062 /*************************************************************************
1063  * StrCSpnIW    [SHLWAPI.@]
1064  *
1065  * See StrCSpnIA.
1066  */
1067 int WINAPI StrCSpnIW(LPCWSTR lpszStr, LPCWSTR lpszMatch)
1068 {
1069   LPCWSTR lpszRead = lpszStr;
1070 
1071   TRACE("(%s,%s)\n",debugstr_w(lpszStr), debugstr_w(lpszMatch));
1072 
1073   if (lpszStr && *lpszStr && lpszMatch)
1074   {
1075     while (*lpszRead)
1076     {
1077       if (StrChrIW(lpszMatch, *lpszRead)) break;
1078       lpszRead++;
1079     }
1080   }
1081   return lpszRead - lpszStr;
1082 }
1083 
1084 /*************************************************************************
1085  * StrPBrkA     [SHLWAPI.@]
1086  *
1087  * Search a string for any of a group of characters.
1088  *
1089  * PARAMS
1090  *  lpszStr   [I] String to search
1091  *  lpszMatch [I] Characters to match
1092  *
1093  * RETURNS
1094  *  A pointer to the first matching character in lpszStr, or NULL if no
1095  *  match was found.
1096  */
1097 LPSTR WINAPI StrPBrkA(LPCSTR lpszStr, LPCSTR lpszMatch)
1098 {
1099   TRACE("(%s,%s)\n",debugstr_a(lpszStr), debugstr_a(lpszMatch));
1100 
1101   if (lpszStr && lpszMatch && *lpszMatch)
1102   {
1103     while (*lpszStr)
1104     {
1105       if (StrChrA(lpszMatch, *lpszStr))
1106         return (LPSTR)lpszStr;
1107       lpszStr = CharNextA(lpszStr);
1108     }
1109   }
1110   return NULL;
1111 }
1112 
1113 /*************************************************************************
1114  * StrPBrkW     [SHLWAPI.@]
1115  *
1116  * See StrPBrkA.
1117  */
1118 LPWSTR WINAPI StrPBrkW(LPCWSTR lpszStr, LPCWSTR lpszMatch)
1119 {
1120     if (!lpszStr || !lpszMatch) return NULL;
1121     return strpbrkW( lpszStr, lpszMatch );
1122 }
1123 
1124 /*************************************************************************
1125  * SHLWAPI_StrRChrHelperA
1126  *
1127  * Internal implementation of StrRChrA/StrRChrIA.
1128  */
1129 static LPSTR SHLWAPI_StrRChrHelperA(LPCSTR lpszStr,
1130                                     LPCSTR lpszEnd, WORD ch,
1131                                     BOOL (WINAPI *pChrCmpFn)(WORD,WORD))
1132 {
1133   LPCSTR lpszRet = NULL;
1134 
1135   if (lpszStr)
1136   {
1137     WORD ch2;
1138 
1139     if (!lpszEnd)
1140       lpszEnd = lpszStr + lstrlenA(lpszStr);
1141 
1142     while (*lpszStr && lpszStr <= lpszEnd)
1143     {
1144       ch2 = IsDBCSLeadByte(*lpszStr)? *lpszStr << 8 | lpszStr[1] : *lpszStr;
1145 
1146       if (!pChrCmpFn(ch, ch2))
1147         lpszRet = lpszStr;
1148       lpszStr = CharNextA(lpszStr);
1149     }
1150   }
1151   return (LPSTR)lpszRet;
1152 }
1153 
1154 /**************************************************************************
1155  * StrRChrA     [SHLWAPI.@]
1156  *
1157  * Find the last occurrence of a character in string.
1158  *
1159  * PARAMS
1160  *  lpszStr [I] String to search in
1161  *  lpszEnd [I] Place to end search, or NULL to search until the end of lpszStr
1162  *  ch      [I] Character to search for.
1163  *
1164  * RETURNS
1165  *  Success: A pointer to the last occurrence of ch in lpszStr before lpszEnd,
1166  *           or NULL if not found.
1167  *  Failure: NULL, if any arguments are invalid.
1168  */
1169 LPSTR WINAPI StrRChrA(LPCSTR lpszStr, LPCSTR lpszEnd, WORD ch)
1170 {
1171   TRACE("(%s,%s,%x)\n", debugstr_a(lpszStr), debugstr_a(lpszEnd), ch);
1172 
1173   return SHLWAPI_StrRChrHelperA(lpszStr, lpszEnd, ch, SHLWAPI_ChrCmpA);
1174 }
1175 
1176 /**************************************************************************
1177  * StrRChrW     [SHLWAPI.@]
1178  *
1179  * See StrRChrA.
1180  */
1181 LPWSTR WINAPI StrRChrW(LPCWSTR str, LPCWSTR end, WORD ch)
1182 {
1183     WCHAR *ret = NULL;
1184 
1185     if (!str) return NULL;
1186     if (!end) end = str + strlenW(str);
1187     while (str < end)
1188     {
1189         if (*str == ch) ret = (WCHAR *)str;
1190         str++;
1191     }
1192     return ret;
1193 }
1194 
1195 /**************************************************************************
1196  * StrRChrIA    [SHLWAPI.@]
1197  *
1198  * Find the last occurrence of a character in string, ignoring case.
1199  *
1200  * PARAMS