/*
 * Shlwapi string functions
 *
 * Copyright 1998 Juergen Schmied
 * Copyright 2002 Jon Griffiths
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include "config.h"
#include "wine/port.h"

#include <ctype.h>
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "winerror.h"
#include "windef.h"
#include "winbase.h"
#include "wingdi.h"
#include "winuser.h"
#include "winreg.h"
#define NO_SHLWAPI_STREAM
#include "shlwapi.h"
#include "shlobj.h"
#include "wine/unicode.h"
#include "wine/debug.h"

WINE_DEFAULT_DEBUG_CHANNEL(shell);

static HRESULT WINAPI _SHStrDupAA(LPCSTR src, LPSTR * dest);
static HRESULT WINAPI _SHStrDupAW(LPCWSTR src, LPSTR * dest);

/*************************************************************************
 * SHLWAPI_ChrCmpHelperA
 *
 * Internal helper for SHLWAPI_ChrCmpA/ChrCMPIA.
 *
 * NOTES
 *  Both this function and its Unicode counterpart are very inneficient. To
 *  fix this, CompareString must be completely implemented and optimised
 *  first. Then the core character test can be taken out of that function and
 *  placed here, so that it need never be called at all. Until then, do not
 *  attempt to optimise this code unless you are willing to test that it
 *  still performs correctly.
 */
static BOOL WINAPI SHLWAPI_ChrCmpHelperA(WORD ch1, WORD ch2, DWORD dwFlags)
{
  char str1[3], str2[3];

  str1[0] = LOBYTE(ch1);
  if (IsDBCSLeadByte(ch1))
  {
    str1[1] = HIBYTE(ch1);
    str1[2] = '\0';
  }
  else
    str1[1] = '\0';

  str2[0] = LOBYTE(ch2);
  if (IsDBCSLeadByte(ch2))
  {
    str2[1] = HIBYTE(ch2);
    str2[2] = '\0';
  }
  else
    str2[1] = '\0';

  return CompareStringA(GetThreadLocale(), dwFlags, str1, -1, str2, -1) - 2;
}

/*************************************************************************
 * SHLWAPI_ChrCmpHelperW
 *
 * Internal helper for SHLWAPI_ChrCmpW/ChrCmpIW.
 */
static BOOL WINAPI SHLWAPI_ChrCmpHelperW(WCHAR ch1, WCHAR ch2, DWORD dwFlags)
{
  WCHAR str1[2], str2[2];

  str1[0] = ch1;
  str1[1] = '\0';
  str2[0] = ch2;
  str2[1] = '\0';
  return CompareStringW(GetThreadLocale(), dwFlags, str1, 2, str2, 2) - 2;
}

/*************************************************************************
 * SHLWAPI_ChrCmpA
 *
 * Internal helper function.
 */
static BOOL WINAPI SHLWAPI_ChrCmpA(WORD ch1, WORD ch2)
{
  return SHLWAPI_ChrCmpHelperA(ch1, ch2, 0);
}

/*************************************************************************
 * ChrCmpIA	[SHLWAPI.385]
 *
 * Compare two characters, ignoring case.
 *
 * PARAMS
 *  ch1 [I] First character to compare
 *  ch2 [I] Second character to compare
 *
 * RETURNS
 *  FALSE, if the characters are equal.
 *  Non-zero otherwise.
 */
BOOL WINAPI ChrCmpIA(WORD ch1, WORD ch2)
{
  TRACE("(%d,%d)\n", ch1, ch2);

  return SHLWAPI_ChrCmpHelperA(ch1, ch2, NORM_IGNORECASE);
}

/*************************************************************************
 * SHLWAPI_ChrCmpW
 *
 * Internal helper function.
 */
static BOOL WINAPI SHLWAPI_ChrCmpW(WCHAR ch1, WCHAR ch2)
{
  return SHLWAPI_ChrCmpHelperW(ch1, ch2, 0);
}

/*************************************************************************
 * ChrCmpIW	[SHLWAPI.386]
 *
 * See ChrCmpIA.
 */
BOOL WINAPI ChrCmpIW(WCHAR ch1, WCHAR ch2)
{
  return SHLWAPI_ChrCmpHelperW(ch1, ch2, NORM_IGNORECASE);
}

/*************************************************************************
 * StrChrA	[SHLWAPI.@]
 *
 * Find a given character in a string.
 *
 * PARAMS
 *  lpszStr [I] String to search in.
 *  ch      [I] Character to search for.
 *
 * RETURNS
 *  Success: A pointer to the first occurrence of ch in lpszStr, or NULL if
 *           not found.
 *  Failure: NULL, if any arguments are invalid.
 */
LPSTR WINAPI StrChrA(LPCSTR lpszStr, WORD ch)
{
  TRACE("(%s,%i)\n", debugstr_a(lpszStr), ch);

  if (lpszStr)
  {
    while (*lpszStr)
    {
      if (!SHLWAPI_ChrCmpA(*lpszStr, ch))
        return (LPSTR)lpszStr;
      lpszStr = CharNextA(lpszStr);
    }
  }
  return NULL;
}

/*************************************************************************
 * StrChrW	[SHLWAPI.@]
 *
 * See StrChrA.
 */
LPWSTR WINAPI StrChrW(LPCWSTR lpszStr, WCHAR ch)
{
  LPWSTR lpszRet = NULL;

  TRACE("(%s,%i)\n", debugstr_w(lpszStr), ch);

  if (lpszStr)
    lpszRet = strchrW(lpszStr, ch);
  return lpszRet;
}

/*************************************************************************
 * StrChrIA	[SHLWAPI.@]
 *
 * Find a given character in a string, ignoring case.
 *
 * PARAMS
 *  lpszStr [I] String to search in.
 *  ch      [I] Character to search for.
 *
 * RETURNS
 *  Success: A pointer to the first occurrence of ch in lpszStr, or NULL if
 *           not found.
 *  Failure: NULL, if any arguments are invalid.
 */
LPSTR WINAPI StrChrIA(LPCSTR lpszStr, WORD ch)
{
  TRACE("(%s,%i)\n", debugstr_a(lpszStr), ch);

  if (lpszStr)
  {
    while (*lpszStr)
    {
      if (!ChrCmpIA(*lpszStr, ch))
        return (LPSTR)lpszStr;
      lpszStr = CharNextA(lpszStr);
    }
  }
  return NULL;
}

/*************************************************************************
 * StrChrIW	[SHLWAPI.@]
 *
 * See StrChrA.
 */
LPWSTR WINAPI StrChrIW(LPCWSTR lpszStr, WCHAR ch)
{
  TRACE("(%s,%i)\n", debugstr_w(lpszStr), ch);

  if (lpszStr)
  {
    ch = toupperW(ch);
    while (*lpszStr)
    {
      if (toupperW(*lpszStr) == ch)
        return (LPWSTR)lpszStr;
      lpszStr = CharNextW(lpszStr);
    }
    lpszStr = NULL;
  }
  return (LPWSTR)lpszStr;
}

/*************************************************************************
 * StrCmpIW	[SHLWAPI.@]
 *
 * Compare two strings, ignoring case.
 *
 * PARAMS
 *  lpszStr  [I] First string to compare
 *  lpszComp [I] Second string to compare
 *
 * RETURNS
 *  An integer less than, equal to or greater than 0, indicating that
 *  lpszStr is less than, the same, or greater than lpszComp.
 */
int WINAPI StrCmpIW(LPCWSTR lpszStr, LPCWSTR lpszComp)
{
  INT iRet;

  TRACE("(%s,%s)\n", debugstr_w(lpszStr),debugstr_w(lpszComp));

  iRet = strcmpiW(lpszStr, lpszComp);
  return iRet < 0 ? -1 : iRet ? 1 : 0;
}

/*************************************************************************
 * SHLWAPI_StrCmpNHelperA
 *
 * Internal helper for StrCmpNA/StrCmpNIA.
 */
static INT WINAPI SHLWAPI_StrCmpNHelperA(LPCSTR lpszStr, LPCSTR lpszComp,
                                         INT iLen,
                                         BOOL (WINAPI *pChrCmpFn)(WORD,WORD))
{
  if (!lpszStr)
  {
    if (!lpszComp)
      return 0;
    return 1;
  }
  else if (!lpszComp)
    return -1;

  while (iLen-- > 0)
  {
    int iDiff;
    WORD ch1, ch2;

    ch1 = IsDBCSLeadByte(*lpszStr)? *lpszStr << 8 | lpszStr[1] : *lpszStr;
    ch2 = IsDBCSLeadByte(*lpszComp)? *lpszComp << 8 | lpszComp[1] : *lpszComp;

    if ((iDiff = pChrCmpFn(ch1, ch2)) < 0)
      return -1;
    else if (iDiff > 0)
      return 1;
    else if (!*lpszStr && !*lpszComp)
      return 0;

    lpszStr = CharNextA(lpszStr);
    lpszComp = CharNextA(lpszComp);
  }
  return 0;
}

/*************************************************************************
 * StrCmpNA	[SHLWAPI.@]
 *
 * Compare two strings, up to a maximum length.
 *
 * PARAMS
 *  lpszStr  [I] First string to compare
 *  lpszComp [I] Second string to compare
 *  iLen     [I] Maximum number of chars to compare.
 *
 * RETURNS
 *  An integer less than, equal to or greater than 0, indicating that
 *  lpszStr is less than, the same, or greater than lpszComp.
 */
INT WINAPI StrCmpNA(LPCSTR lpszStr, LPCSTR lpszComp, INT iLen)
{
  TRACE("(%s,%s,%i)\n", debugstr_a(lpszStr), debugstr_a(lpszComp), iLen);

  return SHLWAPI_StrCmpNHelperA(lpszStr, lpszComp, iLen, SHLWAPI_ChrCmpA);
}

/*************************************************************************
 * StrCmpNW	[SHLWAPI.@]
 *
 * See StrCmpNA.
 */
INT WINAPI StrCmpNW(LPCWSTR lpszStr, LPCWSTR lpszComp, INT iLen)
{
  INT iRet;

  TRACE("(%s,%s,%i)\n", debugstr_w(lpszStr), debugstr_w(lpszComp), iLen);

  iRet = strncmpW(lpszStr, lpszComp, iLen);
  return iRet < 0 ? -1 : iRet ? 1 : 0;
}

/*************************************************************************
 * StrCmpNIA	[SHLWAPI.@]
 *
 * Compare two strings, up to a maximum length, ignoring case.
 *
 * PARAMS
 *  lpszStr  [I] First string to compare
 *  lpszComp [I] Second string to compare
 *  iLen     [I] Maximum number of chars to compare.
 *
 * RETURNS
 *  An integer less than, equal to or greater than 0, indicating that
 *  lpszStr is less than, the same, or greater than lpszComp.
 *
 * NOTES
 *  The Win32 version of this function is _completely_ broken for cases
 *  where iLen is greater than the length of lpszComp. Examples:
 *
 *  StrCmpNIA("foo.gif", "foo", 5) is -1 under Win32; Should return 1.
 *  StrCmpNIA("\", "\\", 3) is 0 under Win32; Should return -1.
 *  StrCmpNIA("\", "\..\foo\", 3) is 1 under Win32; Should return -1.
 *
 *  This implementation behaves correctly, since it is unlikely any
 *  applications actually rely on this function being broken.
 */
int WINAPI StrCmpNIA(LPCSTR lpszStr, LPCSTR lpszComp, int iLen)
{
  TRACE("(%s,%s,%i)\n", debugstr_a(lpszStr), debugstr_a(lpszComp), iLen);

  return SHLWAPI_StrCmpNHelperA(lpszStr, lpszComp, iLen, ChrCmpIA);
}

/*************************************************************************
 * StrCmpNIW	[SHLWAPI.@]
 *
 * See StrCmpNIA.
 */
INT WINAPI StrCmpNIW(LPCWSTR lpszStr, LPCWSTR lpszComp, int iLen)
{
  INT iRet;

  TRACE("(%s,%s,%i)\n", debugstr_w(lpszStr), debugstr_w(lpszComp), iLen);

  iRet = strncmpiW(lpszStr, lpszComp, iLen);
  return iRet < 0 ? -1 : iRet ? 1 : 0;
}

/*************************************************************************
 * StrCmpW	[SHLWAPI.@]
 *
 * Compare two strings.
 *
 * PARAMS
 *  lpszStr  [I] First string to compare
 *  lpszComp [I] Second string to compare
 *
 * RETURNS
 *  An integer less than, equal to or greater than 0, indicating that
 *  lpszStr is less than, the same, or greater than lpszComp.
 */
int WINAPI StrCmpW(LPCWSTR lpszStr, LPCWSTR lpszComp)
{
  INT iRet;

  TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszComp));

  iRet = strcmpW(lpszStr, lpszComp);
  return iRet < 0 ? -1 : iRet ? 1 : 0;
}

/*************************************************************************
 * StrCatW	[SHLWAPI.@]
 *
 * Concatanate two strings.
 *
 * PARAMS
 *  lpszStr [O] Initial string
 *  lpszSrc [I] String to concatanate
 *
 * RETURNS
 *  lpszStr.
 */
LPWSTR WINAPI StrCatW(LPWSTR lpszStr, LPCWSTR lpszSrc)
{
  TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszSrc));

  strcatW(lpszStr, lpszSrc);
  return lpszStr;
}

/*************************************************************************
 * StrCpyW	[SHLWAPI.@]
 *
 * Copy a string to another string.
 *
 * PARAMS
 *  lpszStr [O] Destination string
 *  lpszSrc [I] Source string
 *
 * RETURNS
 *  lpszStr.
 */
LPWSTR WINAPI StrCpyW(LPWSTR lpszStr, LPCWSTR lpszSrc)
{
  TRACE("(%p,%s)\n", lpszStr, debugstr_w(lpszSrc));

  strcpyW(lpszStr, lpszSrc);
  return lpszStr;
}

/*************************************************************************
 * StrCpyNW	[SHLWAPI.@]
 *
 * Copy a string to another string, up to a maximum number of characters.
 *
 * PARAMS
 *  lpszStr  [O] Destination string
 *  lpszSrc  [I] Source string
 *  iLen     [I] Maximum number of chars to copy
 *
 * RETURNS
 *  lpszStr.
 */
LPWSTR WINAPI StrCpyNW(LPWSTR lpszStr, LPCWSTR lpszSrc, int iLen)
{
  TRACE("(%p,%s,%i)\n", lpszStr, debugstr_w(lpszSrc), iLen);

  lstrcpynW(lpszStr, lpszSrc, iLen);
  return lpszStr;
}



/*************************************************************************
 * SHLWAPI_StrStrHelperA
 *
 * Internal implementation of StrStrA/StrStrIA
 */
static LPSTR WINAPI SHLWAPI_StrStrHelperA(LPCSTR lpszStr, LPCSTR lpszSearch,
                                         int (*pStrCmpFn)(LPCSTR,LPCSTR,size_t))
{
  size_t iLen;

  if (!lpszStr || !lpszSearch || !*lpszSearch)
    return NULL;

  iLen = strlen(lpszSearch);

  while (*lpszStr)
  {
    if (!pStrCmpFn(lpszStr, lpszSearch, iLen))
      return (LPSTR)lpszStr;
    lpszStr = CharNextA(lpszStr);
  }
  return NULL;
}

/*************************************************************************
 * SHLWAPI_StrStrHelperW
 *
 * Internal implementation of StrStrW/StrStrIW
 */
static LPWSTR WINAPI SHLWAPI_StrStrHelperW(LPCWSTR lpszStr, LPCWSTR lpszSearch,
                                          int (*pStrCmpFn)(LPCWSTR,LPCWSTR,int))
{
  int iLen;

  if (!lpszStr || !lpszSearch || !*lpszSearch)
    return NULL;

  iLen = strlenW(lpszSearch);

  while (*lpszStr)
  {
    if (!pStrCmpFn(lpszStr, lpszSearch, iLen))
      return (LPWSTR)lpszStr;
    lpszStr = CharNextW(lpszStr);
  }
  return NULL;
}

/*************************************************************************
 * StrStrA	[SHLWAPI.@]
 *
 * Find a substring within a string.
 *
 * PARAMS
 *  lpszStr    [I] String to search in
 *  lpszSearch [I] String to look for
 *
 * RETURNS
 *  The start of lpszSearch within lpszStr, or NULL if not found.
 */
LPSTR WINAPI StrStrA(LPCSTR lpszStr, LPCSTR lpszSearch)
{
  TRACE("(%s,%s)\n", debugstr_a(lpszStr), debugstr_a(lpszSearch));

  return SHLWAPI_StrStrHelperA(lpszStr, lpszSearch, strncmp);
}

/*************************************************************************
 * StrStrW	[SHLWAPI.@]
 *
 * See StrStrA.
 */
LPWSTR WINAPI StrStrW(LPCWSTR lpszStr, LPCWSTR lpszSearch)
{
  TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszSearch));

  return SHLWAPI_StrStrHelperW(lpszStr, lpszSearch, strncmpW);
}

/*************************************************************************
 * StrRStrIA	[SHLWAPI.@]
 *
 * Find the last occurence of a substring within a string.
 *
 * PARAMS
 *  lpszStr    [I] String to search in
 *  lpszEnd    [I] End of lpszStr
 *  lpszSearch [I] String to look for
 *
 * RETURNS
 *  The last occurence lpszSearch within lpszStr, or NULL if not found.
 */
LPSTR WINAPI StrRStrIA(LPCSTR lpszStr, LPCSTR lpszEnd, LPCSTR lpszSearch)
{
  LPSTR lpszRet = NULL;
  WORD ch1, ch2;
  INT iLen;

  TRACE("(%s,%s)\n", debugstr_a(lpszStr), debugstr_a(lpszSearch));

  if (!lpszStr || !lpszSearch || !*lpszSearch)
    return NULL;

  if (!lpszEnd)
    lpszEnd = lpszStr + lstrlenA(lpszStr);

  if (IsDBCSLeadByte(*lpszSearch))
    ch1 = *lpszSearch << 8 | lpszSearch[1];
  else
    ch1 = *lpszSearch;
  iLen = lstrlenA(lpszSearch);

  while (lpszStr <= lpszEnd  && *lpszStr)
  {
    ch2 = IsDBCSLeadByte(*lpszStr)? *lpszStr << 8 | lpszStr[1] : *lpszStr;
    if (!ChrCmpIA(ch1, ch2))
    {
      if (!StrCmpNIA(lpszStr, lpszSearch, iLen))
        lpszRet = (LPSTR)lpszStr;
    }
    lpszStr = CharNextA(lpszStr);
  }
  return lpszRet;
}

/*************************************************************************
 * StrRStrIW	[SHLWAPI.@]
 *
 * See StrRStrIA.
 */
LPWSTR WINAPI StrRStrIW(LPCWSTR lpszStr, LPCWSTR lpszEnd, LPCWSTR lpszSearch)
{
  LPWSTR lpszRet = NULL;
  INT iLen;

  TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszSearch));

  if (!lpszStr || !lpszSearch || !*lpszSearch)
    return NULL;

  if (!lpszEnd)
    lpszEnd = lpszStr + strlenW(lpszStr);

  iLen = strlenW(lpszSearch);

  while (lpszStr <= lpszEnd  && *lpszStr)
  {
    if (!ChrCmpIA(*lpszSearch, *lpszStr))
    {
      if (!StrCmpNIW(lpszStr, lpszSearch, iLen))
        lpszRet = (LPWSTR)lpszStr;
    }
    lpszStr = CharNextW(lpszStr);
  }
  return lpszRet;
}

/*************************************************************************
 * StrStrIA	[SHLWAPI.@]
 *
 * Find a substring within a string, ignoring case.
 *
 * PARAMS
 *  lpszStr    [I] String to search in
 *  lpszSearch [I] String to look for
 *
 * RETURNS
 *  The start of lpszSearch within lpszStr, or NULL if not found.
 */
LPSTR WINAPI StrStrIA(LPCSTR lpszStr, LPCSTR lpszSearch)
{
  TRACE("(%s,%s)\n", debugstr_a(lpszStr), debugstr_a(lpszSearch));

  return SHLWAPI_StrStrHelperA(lpszStr, lpszSearch, strncasecmp);
}

/*************************************************************************
 * StrStrIW	[SHLWAPI.@]
 *
 * See StrStrIA.
 */
LPWSTR WINAPI StrStrIW(LPCWSTR lpszStr, LPCWSTR lpszSearch)
{
  TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszSearch));

  return SHLWAPI_StrStrHelperW(lpszStr, lpszSearch, strncmpiW);
}

/*************************************************************************
 * StrToIntA	[SHLWAPI.@]
 *
 * Read an integer from a string.
 *
 * PARAMS
 *  lpszStr [I] String to read integer from
 *
 * RETURNS
 *   The integer value represented by the string, or 0 if no integer is
 *   present.
 *
 * NOTES
 *  No leading space is allowed before the number, although a leading '-' is.
 */
int WINAPI StrToIntA(LPCSTR lpszStr)
{
  int iRet = 0;

  TRACE("(%s)\n", debugstr_a(lpszStr));

  if (!lpszStr)
  {
    WARN("Invalid lpszStr would crash under Win32!\n");
    return 0;
  }

  if (*lpszStr == '-' || isdigit(*lpszStr))
    StrToIntExA(lpszStr, 0, &iRet);
  return iRet;
}

/*************************************************************************
 * StrToIntW	[SHLWAPI.@]
 *
 * See StrToIntA.
 */
int WINAPI StrToIntW(LPCWSTR lpszStr)
{
  int iRet = 0;

  TRACE("(%s)\n", debugstr_w(lpszStr));

  if (!lpszStr)
  {
    WARN("Invalid lpszStr would crash under Win32!\n");
    return 0;
  }

  if (*lpszStr == '-' || isdigitW(*lpszStr))
    StrToIntExW(lpszStr, 0, &iRet);
  return iRet;
}

/*************************************************************************
 * StrToIntExA	[SHLWAPI.@]
 *
 * Read an integer from a string.
 *
 * PARAMS
 *  lpszStr [I] String to read integer from
 *  dwFlags [I] Flags controlling the conversion
 *  lpiRet  [O] Destination for read integer.
 *
 * RETURNS
 *  Success: TRUE. lpiRet contains the integer value represented by the string.
 *  Failure: FALSE, if the string is invalid, or no number is present.
 *
 * NOTES
 *  Leading whitespace, '-' and '+' are allowed before the number. If
 *  dwFlags includes STIF_SUPPORT_HEX, hexidecimal numbers are allowed, if
 *  preceeded by '0x'. If this flag is not set, or there is no '0x' prefix,
 *  the string is treated as a decimal string. A leading '-' is ignored for
 *  hexidecimal numbers.
 */
BOOL WINAPI StrToIntExA(LPCSTR lpszStr, DWORD dwFlags, LPINT lpiRet)
{
  BOOL bNegative = FALSE;
  int iRet = 0;

  TRACE("(%s,%08lX,%p)\n", debugstr_a(lpszStr), dwFlags, lpiRet);

  if (!lpszStr || !lpiRet)
  {
    WARN("Invalid parameter would crash under Win32!\n");
    return FALSE;
  }
  if (dwFlags > STIF_SUPPORT_HEX)
  {
    WARN("Unknown flags (%08lX)!\n", dwFlags & ~STIF_SUPPORT_HEX);
  }

  /* Skip leading space, '+', '-' */
  while (isspace(*lpszStr))
    lpszStr = CharNextA(lpszStr);

  if (*lpszStr == '-')
  {
    bNegative = TRUE;
    lpszStr++;
  }
  else if (*lpszStr == '+')
    lpszStr++;

  if (dwFlags & STIF_SUPPORT_HEX &&
      *lpszStr == '0' && tolower(lpszStr[1]) == 'x')
  {
    /* Read hex number */
    lpszStr += 2;

    if (!isxdigit(*lpszStr))
      return FALSE;

    while (isxdigit(*lpszStr))
    {
      iRet = iRet * 16;
      if (isdigit(*lpszStr))
        iRet += (*lpszStr - '0');
      else
        iRet += 10 + (tolower(*lpszStr) - 'a');
      lpszStr++;
    }
    *lpiRet = iRet;
    return TRUE;
  }

  /* Read decimal number */
  if (!isdigit(*lpszStr))
    return FALSE;

  while (isdigit(*lpszStr))
  {
    iRet = iRet * 10;
    iRet += (*lpszStr - '0');
    lpszStr++;
  }
  *lpiRet = bNegative ? -iRet : iRet;
  return TRUE;
}

/*************************************************************************
 * StrToIntExW	[SHLWAPI.@]
 *
 * See StrToIntExA.
 */
BOOL WINAPI StrToIntExW(LPCWSTR lpszStr, DWORD dwFlags, LPINT lpiRet)
{
  BOOL bNegative = FALSE;
  int iRet = 0;

  TRACE("(%s,%08lX,%p)\n", debugstr_w(lpszStr), dwFlags, lpiRet);

  if (!lpszStr || !lpiRet)
  {
    WARN("Invalid parameter would crash under Win32!\n");
    return FALSE;
  }
  if (dwFlags > STIF_SUPPORT_HEX)
  {
    WARN("Unknown flags (%08lX)!\n", dwFlags & ~STIF_SUPPORT_HEX);
  }

  /* Skip leading space, '+', '-' */
  while (isspaceW(*lpszStr))
    lpszStr = CharNextW(lpszStr);

  if (*lpszStr == '-')
  {
    bNegative = TRUE;
    lpszStr++;
  }
  else if (*lpszStr == '+')
    lpszStr++;

  if (dwFlags & STIF_SUPPORT_HEX &&
      *lpszStr == '0' && tolowerW(lpszStr[1]) == 'x')
  {
    /* Read hex number */
    lpszStr += 2;

    if (!isxdigitW(*lpszStr))
      return FALSE;

    while (isxdigitW(*lpszStr))
    {
      iRet = iRet * 16;
      if (isdigitW(*lpszStr))
        iRet += (*lpszStr - '0');
      else
        iRet += 10 + (tolowerW(*lpszStr) - 'a');
      lpszStr++;
    }
    *lpiRet = iRet;
    return TRUE;
  }

  /* Read decimal number */
  if (!isdigitW(*lpszStr))
    return FALSE;

  while (isdigitW(*lpszStr))
  {
    iRet = iRet * 10;
    iRet += (*lpszStr - '0');
    lpszStr++;
  }
  *lpiRet = bNegative ? -iRet : iRet;
  return TRUE;
}

/*************************************************************************
 * StrDupA	[SHLWAPI.@]
 *
 * Duplicate a string.
 *
 * PARAMS
 *  lpszStr [I] String to duplicate.
 *
 * RETURNS
 *  Success: A pointer to a new string containing the contents of lpszStr
 *  Failure: NULL, if memory cannot be allocated
 *
 * NOTES
 *  The string memory is allocated with LocalAlloc, and so should be released
 *  by calling LocalFree.
 */
LPSTR WINAPI StrDupA(LPCSTR lpszStr)
{
  int iLen;
  LPSTR lpszRet;

  TRACE("(%s)\n",debugstr_a(lpszStr));

  iLen = lpszStr ? strlen(lpszStr) + 1 : 1;
  lpszRet = (LPSTR)LocalAlloc(LMEM_FIXED, iLen);

  if (lpszRet)
  {
    if (lpszStr)
      memcpy(lpszRet, lpszStr, iLen);
    else
      *lpszRet = '\0';
  }
  return lpszRet;
}

/*************************************************************************
 * StrDupW	[SHLWAPI.@]
 *
 * See StrDupA.
 */
LPWSTR WINAPI StrDupW(LPCWSTR lpszStr)
{
  int iLen;
  LPWSTR lpszRet;

  TRACE("(%s)\n",debugstr_w(lpszStr));

  iLen = (lpszStr ? strlenW(lpszStr) + 1 : 1) * sizeof(WCHAR);
  lpszRet = (LPWSTR)LocalAlloc(LMEM_FIXED, iLen);

  if (lpszRet)
  {
    if (lpszStr)
      memcpy(lpszRet, lpszStr, iLen);
    else
      *lpszRet = '\0';
  }
  return lpszRet;
}

/*************************************************************************
 * SHLWAPI_StrSpnHelperA
 *
 * Internal implementation of StrSpnA/StrCSpnA/StrCSpnIA
 */
static int WINAPI SHLWAPI_StrSpnHelperA(LPCSTR lpszStr, LPCSTR lpszMatch,
                                        LPSTR (WINAPI *pStrChrFn)(LPCSTR,WORD),
                                        BOOL bInvert)
{
  LPCSTR lpszRead = lpszStr;
  if (lpszStr && *lpszStr && lpszMatch)
  {
    while (*lpszRead)
    {
      LPCSTR lpszTest = pStrChrFn(lpszMatch, *lpszRead);

      if (!bInvert && !lpszTest)
        break;
      if (bInvert && lpszTest)
        break;
      lpszRead = CharNextA(lpszRead);
    };
  }
  return lpszRead - lpszStr;
}

/*************************************************************************
 * SHLWAPI_StrSpnHelperW
 *
 * Internal implementation of StrSpnW/StrCSpnW/StrCSpnIW
 */
static int WINAPI SHLWAPI_StrSpnHelperW(LPCWSTR lpszStr, LPCWSTR lpszMatch,
                                      LPWSTR (WINAPI *pStrChrFn)(LPCWSTR,WCHAR),
                                      BOOL bInvert)
{
  LPCWSTR lpszRead = lpszStr;
  if (lpszStr && *lpszStr && lpszMatch)
  {
    while (*lpszRead)
    {
      LPCWSTR lpszTest = pStrChrFn(lpszMatch, *lpszRead);

      if (!bInvert && !lpszTest)
        break;
      if (bInvert && lpszTest)
        break;
      lpszRead = CharNextW(lpszRead);
    };
  }
  return lpszRead - lpszStr;
}

/*************************************************************************
 * StrSpnA	[SHLWAPI.@]
 *
 * Find the length of the start of a string that contains only certain
 * characters.
 *
 * PARAMS
 *  lpszStr   [I] String to search
 *  lpszMatch [I] Characters that can be in the substring
 *
 * RETURNS
 *  The length of the part of lpszStr containing only chars from lpszMatch,
 *  or 0 if any parameter is invalid.
 */
int WINAPI StrSpnA(LPCSTR lpszStr, LPCSTR lpszMatch)
{
  TRACE("(%s,%s)\n",debugstr_a(lpszStr), debugstr_a(lpszMatch));

  return SHLWAPI_StrSpnHelperA(lpszStr, lpszMatch, StrChrA, FALSE);
}

/*************************************************************************
 * StrSpnW	[SHLWAPI.@]
 *
 * See StrSpnA.
 */
int WINAPI StrSpnW(LPCWSTR lpszStr, LPCWSTR lpszMatch)
{
  TRACE("(%s,%s)\n",debugstr_w(lpszStr), debugstr_w(lpszMatch));

  return SHLWAPI_StrSpnHelperW(lpszStr, lpszMatch, StrChrW, FALSE);
}

/*************************************************************************
 * StrCSpnA	[SHLWAPI.@]
 *
 * Find the length of the start of a string that does not contain certain
 * characters.
 *
 * PARAMS
 *  lpszStr   [I] String to search
 *  lpszMatch [I] Characters that cannot be in the substring
 *
 * RETURNS
 *  The length of the part of lpszStr containing only chars not in lpszMatch,
 *  or 0 if any parameter is invalid.
 */
int WINAPI StrCSpnA(LPCSTR lpszStr, LPCSTR lpszMatch)
{
  TRACE("(%s,%s)\n",debugstr_a(lpszStr), debugstr_a(lpszMatch));

  return SHLWAPI_StrSpnHelperA(lpszStr, lpszMatch, StrChrA, TRUE);
}

/*************************************************************************
 * StrCSpnW	[SHLWAPI.@]
 *
 * See StrCSpnA.
 */
int WINAPI StrCSpnW(LPCWSTR lpszStr, LPCWSTR lpszMatch)
{
  TRACE("(%s,%s)\n",debugstr_w(lpszStr), debugstr_w(lpszMatch));

  return SHLWAPI_StrSpnHelperW(lpszStr, lpszMatch, StrChrW, TRUE);
}

/*************************************************************************
 * StrCSpnIA	[SHLWAPI.@]
 *
 * Find the length of the start of a string that does not contain certain
 * characters, ignoring case.
 *
 * PARAMS
 *  lpszStr   [I] String to search
 *  lpszMatch [I] Characters that cannot be in the substring
 *
 * RETURNS
 *  The length of the part of lpszStr containing only chars not in lpszMatch,
 *  or 0 if any parameter is invalid.
 */
int WINAPI StrCSpnIA(LPCSTR lpszStr, LPCSTR lpszMatch)
{
  TRACE("(%s,%s)\n",debugstr_a(lpszStr), debugstr_a(lpszMatch));

  return SHLWAPI_StrSpnHelperA(lpszStr, lpszMatch, StrChrIA, TRUE);
}

/*************************************************************************
 * StrCSpnIW	[SHLWAPI.@]
 *
 * See StrCSpnIA.
 */
int WINAPI StrCSpnIW(LPCWSTR lpszStr, LPCWSTR lpszMatch)
{
  TRACE("(%s,%s)\n",debugstr_w(lpszStr), debugstr_w(lpszMatch));

  return SHLWAPI_StrSpnHelperW(lpszStr, lpszMatch, StrChrIW, TRUE);
}

/*************************************************************************
 * StrPBrkA	[SHLWAPI.@]
 *
 * Search a string for any of a group of characters.
 *
 * PARAMS
 *  lpszStr   [I] String to search
 *  lpszMatch [I] Characters to match
 *
 * RETURNS
 *  A pointer to the first matching character in lpszStr, or NULL if no
 *  match was found.
 */
LPSTR WINAPI StrPBrkA(LPCSTR lpszStr, LPCSTR lpszMatch)
{
  TRACE("(%s,%s)\n",debugstr_a(lpszStr), debugstr_a(lpszMatch));

  if (lpszStr && lpszMatch && *lpszMatch)
  {
    while (*lpszStr)
    {
      if (StrChrA(lpszMatch, *lpszStr))
        return (LPSTR)lpszStr;
      lpszStr = CharNextA(lpszStr);
    };
  }
  return NULL;
}

/*************************************************************************
 * StrPBrkW	[SHLWAPI.@]
 *
 * See StrPBrkA.
 */
LPWSTR WINAPI StrPBrkW(LPCWSTR lpszStr, LPCWSTR lpszMatch)
{
  TRACE("(%s,%s)\n",debugstr_w(lpszStr), debugstr_w(lpszMatch));

  if (lpszStr && lpszMatch && *lpszMatch)
  {
    while (*lpszStr);
    {
      if (StrChrW(lpszMatch, *lpszStr))
        return (LPWSTR)lpszStr;
      lpszStr = CharNextW(lpszStr);
    } while (*lpszStr);
  }
  return NULL;
}

/*************************************************************************
 * SHLWAPI_StrRChrHelperA
 *
 * Internal implementation of StrRChrA/StrRChrIA.
 */
static LPSTR WINAPI SHLWAPI_StrRChrHelperA(LPCSTR lpszStr,
                                           LPCSTR lpszEnd, WORD ch,
                                           BOOL (WINAPI *pChrCmpFn)(WORD,WORD))
{
  LPCSTR lpszRet = NULL;

  if (lpszStr)
  {
    WORD ch2;

    if (!lpszEnd)
      lpszEnd = lpszStr + lstrlenA(lpszStr);

    while (*lpszStr && lpszStr <= lpszEnd)
    {
      ch2 = IsDBCSLeadByte(*lpszStr)? *lpszStr << 8 | lpszStr[1] : *lpszStr;

      if (!pChrCmpFn(ch, ch2))
        lpszRet = lpszStr;
      lpszStr = CharNextA(lpszStr);
    }
  }
  return (LPSTR)lpszRet;
}

/*************************************************************************
 * SHLWAPI_StrRChrHelperW
 *
 * Internal implementation of StrRChrW/StrRChrIW.
 */
static LPWSTR WINAPI SHLWAPI_StrRChrHelperW(LPCWSTR lpszStr,
                                         LPCWSTR lpszEnd, WCHAR ch,
                                         BOOL (WINAPI *pChrCmpFn)(WCHAR,WCHAR))
{
  LPCWSTR lpszRet = NULL;

  if (lpszStr)
  {
    if (!lpszEnd)
      lpszEnd = lpszStr + strlenW(lpszStr);

    while (*lpszStr && lpszStr <= lpszEnd)
    {
      if (!pChrCmpFn(ch, *lpszStr))
        lpszRet = lpszStr;
      lpszStr = CharNextW(lpszStr);
    }
  }
  return (LPWSTR)lpszRet;
}

/**************************************************************************
 * StrRChrA	[SHLWAPI.@]
 *
 * Find the last occurence of a character in string.
 *
 * PARAMS
 *  lpszStr [I] String to search in
 *  lpszEnd [I] Place to end search, or NULL to search until the end of lpszStr
 *  ch      [I] Character to search for.
 *
 * RETURNS
 *  Success: A pointer to the last occurrence of ch in lpszStr before lpszEnd,
 *           or NULL if not found.
 *  Failure: NULL, if any arguments are invalid.
 */
LPSTR WINAPI StrRChrA(LPCSTR lpszStr, LPCSTR lpszEnd, WORD ch)
{
  TRACE("(%s,%s,%x)\n", debugstr_a(lpszStr), debugstr_a(lpszEnd), ch);

  return SHLWAPI_StrRChrHelperA(lpszStr, lpszEnd, ch, SHLWAPI_ChrCmpA);
}

/**************************************************************************
 * StrRChrW	[SHLWAPI.@]
 *
 * See StrRChrA.
 */
LPWSTR WINAPI StrRChrW(LPCWSTR lpszStr, LPCWSTR lpszEnd, WORD ch)
{
  TRACE("(%s,%s,%x)\n", debugstr_w(lpszStr), debugstr_w(lpszEnd), ch);

  return SHLWAPI_StrRChrHelperW(lpszStr, lpszEnd, ch, SHLWAPI_ChrCmpW);
}

/**************************************************************************
 * StrRChrIA	[SHLWAPI.@]
 *
 * Find the last occurence of a character in string, ignoring case.
 *
 * PARAMS
 *  lpszStr [I] String to search in
 *  lpszEnd [I] Place to end search, or NULL to search until the end of lpszStr
 *  ch      [I] Character to search for.
 *
 * RETURNS
 *  Success: A pointer to the last occurrence of ch in lpszStr before lpszEnd,
 *           or NULL if not found.
 *  Failure: NULL, if any arguments are invalid.
 */
LPSTR WINAPI StrRChrIA(LPCSTR lpszStr, LPCSTR lpszEnd, WORD ch)
{
  TRACE("(%s,%s,%x)\n", debugstr_a(lpszStr), debugstr_a(lpszEnd), ch);

  return SHLWAPI_StrRChrHelperA(lpszStr, lpszEnd, ch, ChrCmpIA);
}

/**************************************************************************
 * StrRChrIW	[SHLWAPI.@]
 *
 * See StrRChrIA.
 */
LPWSTR WINAPI StrRChrIW(LPCWSTR lpszStr, LPCWSTR lpszEnd, WORD ch)
{
  TRACE("(%s,%s,%x)\n", debugstr_w(lpszStr), debugstr_w(lpszEnd), ch);

  return SHLWAPI_StrRChrHelperW(lpszStr, lpszEnd, ch, ChrCmpIW);
}

/*************************************************************************
 * StrCatBuffA	[SHLWAPI.@]
 *
 * Concatenate two strings together.
 *
 * PARAMS
 *  lpszStr [O] String to concatenate to
 *  lpszCat [I] String to add to lpszCat
 *  cchMax  [I] Maximum number of characters for the whole string
 *
 * RETURNS
 *  lpszStr.
 *
 * NOTES
 *  cchMax dtermines the number of characters in the final length of the
 *  string, not the number appended to lpszStr from lpszCat.
 */
LPSTR WINAPI StrCatBuffA(LPSTR lpszStr, LPCSTR lpszCat, INT cchMax)
{
  INT iLen;

  TRACE("(%p,%s,%d)\n", lpszStr, debugstr_a(lpszCat), cchMax);

  if (!lpszStr)
  {
    WARN("Invalid lpszStr would crash under Win32!\n");
    return NULL;
  }

  iLen = strlen(lpszStr);
  cchMax -= iLen;

  if (cchMax > 0)
    StrCpyNA(lpszStr + iLen, lpszCat, cchMax);
  return lpszStr;
}

/*************************************************************************
 * StrCatBuffW	[SHLWAPI.@]
 *
 * See StrCatBuffA.
 */
LPWSTR WINAPI StrCatBuffW(LPWSTR lpszStr, LPCWSTR lpszCat, INT cchMax)
{
  INT iLen;

  TRACE("(%p,%s,%d)\n", lpszStr, debugstr_w(lpszCat), cchMax);

  if (!lpszStr)
  {
    WARN("Invalid lpszStr would crash under Win32!\n");
    return NULL;
  }

  iLen = strlenW(lpszStr);
  cchMax -= iLen;

  if (cchMax > 0)
    StrCpyNW(lpszStr + iLen, lpszCat, cchMax);
  return lpszStr;
}

/*************************************************************************
 * StrRetToBufA					[SHLWAPI.@]
 *
 * Convert a STRRET to a normal string.
 *
 * PARAMS
 *  lpStrRet [O] STRRET to convert
 *  pIdl     [I] ITEMIDLIST for lpStrRet->uType = STRRET_OFFSETA
 *  lpszDest [O] Destination for normal string
 *  dwLen    [I] Length of lpszDest
 *
 * RETURNS
 *  Success: S_OK. lpszDest contains up to dwLen characters of the string.
 *           If lpStrRet is of type STRRET_WSTR, its memory is freed with
 *           CoTaskMemFree and its type set to STRRET_CSTRA.
 *  Failure: E_FAIL, if any parameters are invalid.
 */
HRESULT WINAPI StrRetToBufA (LPSTRRET src, const ITEMIDLIST *pidl, LPSTR dest, DWORD len)
{
	/* NOTE:
	 *  This routine is identical to that in dlls/shell32/shellstring.c.
	 *  It was duplicated because not every version of Shlwapi.dll exports
	 *  StrRetToBufA. If you change one routine, change them both.
	 */
	TRACE("dest=%p len=0x%lx strret=%p pidl=%p stub\n",dest,len,src,pidl);

	if (!src)
	{
	  WARN("Invalid lpStrRet would crash under Win32!\n");
	  if (dest)
	    *dest = '\0';
	  return E_FAIL;
	}

	if (!dest || !len)
	  return E_FAIL;

	*dest = '\0';

	switch (src->uType)
	{
	  case STRRET_WSTR:
	    WideCharToMultiByte(CP_ACP, 0, src->u.pOleStr, -1, dest, len, NULL, NULL);
	    CoTaskMemFree(src->u.pOleStr);
	    break;

	  case STRRET_CSTR:
	    lstrcpynA((LPSTR)dest, src->u.cStr, len);
	    break;

	  case STRRET_OFFSET:
	    lstrcpynA((LPSTR)dest, ((LPCSTR)&pidl->mkid)+src->u.uOffset, len);
	    break;

	  default:
	    FIXME("unknown type!\n");
	    return FALSE;
	}
	return S_OK;
}

/*************************************************************************
 * StrRetToBufW	[SHLWAPI.@]
 *
 * See StrRetToBufA.
 */
HRESULT WINAPI StrRetToBufW (LPSTRRET src, const ITEMIDLIST *pidl, LPWSTR dest, DWORD len)
{
	TRACE("dest=%p len=0x%lx strret=%p pidl=%p stub\n",dest,len,src,pidl);

	if (!src)
	{
	  WARN("Invalid lpStrRet would crash under Win32!\n");
	  if (dest)
	    *dest = '\0';
	  return E_FAIL;
	}

	if (!dest || !len)
	  return E_FAIL;

	*dest = '\0';

	switch (src->uType)
	{
	  case STRRET_WSTR:
	    lstrcpynW((LPWSTR)dest, src->u.pOleStr, len);
	    CoTaskMemFree(src->u.pOleStr);
	    break;

	  case STRRET_CSTR:
              if (!MultiByteToWideChar( CP_ACP, 0, src->u.cStr, -1, dest, len ) && len)
                  dest[len-1] = 0;
	    break;

	  case STRRET_OFFSET:
	    if (pidl)
	    {
              if (!MultiByteToWideChar( CP_ACP, 0, ((LPCSTR)&pidl->mkid)+src->u.uOffset, -1,
                                        dest, len ) && len)
                  dest[len-1] = 0;
	    }
	    break;

	  default:
	    FIXME("unknown type!\n");
	    return FALSE;
	}
	return S_OK;
}

/*************************************************************************
 * StrRetToStrA					[SHLWAPI.@]
 *
 * converts a STRRET to a normal string
 */
HRESULT WINAPI StrRetToStrA(LPSTRRET pstr, const ITEMIDLIST * pidl, LPSTR* ppszName)
{
	HRESULT ret = E_FAIL;

	switch (pstr->uType) {
	    case STRRET_WSTR:
	        ret = _SHStrDupAW(pstr->u.pOleStr, ppszName);
	        CoTaskMemFree(pstr->u.pOleStr);
	        break;

	    case STRRET_CSTR:
	        ret = _SHStrDupAA(pstr->u.cStr, ppszName);
	        break;

	    case STRRET_OFFSET:
	        ret = _SHStrDupAA(((LPCSTR)&pidl->mkid)+pstr->u.uOffset, ppszName);
	        break;

	    default:
	        *ppszName = NULL;
	}
	return ret;
}

/*************************************************************************
 * StrRetToStrW					[SHLWAPI.@]
 *
 * converts a STRRET to a normal string
 */
HRESULT WINAPI StrRetToStrW(LPSTRRET pstr, const ITEMIDLIST * pidl, LPWSTR* ppszName)
{
	HRESULT ret = E_FAIL;

	switch (pstr->uType) {
	    case STRRET_WSTR:
	        ret = SHStrDupW(pstr->u.pOleStr, ppszName);
	        CoTaskMemFree(pstr->u.pOleStr);
	        break;

	    case STRRET_CSTR:
	        ret = SHStrDupA(pstr->u.cStr, ppszName);
		break;

	    case STRRET_OFFSET:
	        ret = SHStrDupA(((LPCSTR)&pidl->mkid)+pstr->u.uOffset, ppszName);
	        break;

	    default:
	        *ppszName = NULL;
	}
	return ret;
}

/*************************************************************************
 * StrFormatKBSizeA	[SHLWAPI.@]
 *
 * Create a formatted string containing a byte count in Kilobytes.
 *
 * PARAMS
 *  llBytes  [I] Byte size to format
 *  lpszDest [I] Destination for formatted string
 *  cchMax   [I] Size of lpszDest
 *
 * RETURNS
 *  lpszDest.
 */
LPSTR WINAPI StrFormatKBSizeA(LONGLONG llBytes, LPSTR lpszDest, UINT cchMax)
{
  char szBuff[256], *szOut = szBuff + sizeof(szBuff) - 1;
  LONGLONG ulKB = (llBytes + 1023) >> 10;

  TRACE("(%lld,%p,%d)\n", llBytes, lpszDest, cchMax);

  *szOut-- = '\0';
  *szOut-- = 'B';
  *szOut-- = 'K';
  *szOut-- = ' ';

  do
  {
    LONGLONG ulNextDigit = ulKB % 10;
    *szOut-- = '0' + ulNextDigit;
    ulKB = (ulKB - ulNextDigit) / 10;
  } while (ulKB > 0);

  strncpy(lpszDest, szOut + 1, cchMax);
  return lpszDest;
}

/*************************************************************************
 * StrFormatKBSizeW	[SHLWAPI.@]
 *
 * See StrFormatKBSizeA.
 */
LPWSTR WINAPI StrFormatKBSizeW(LONGLONG llBytes, LPWSTR lpszDest, UINT cchMax)
{
  WCHAR szBuff[256], *szOut = szBuff + sizeof(szBuff) - 1;
  LONGLONG ulKB = (llBytes + 1023) >> 10;

  TRACE("(%lld,%p,%d)\n", llBytes, lpszDest, cchMax);

  *szOut-- = '\0';
  *szOut-- = 'B';
  *szOut-- = 'K';
  *szOut-- = ' ';

  do
  {
    LONGLONG ulNextDigit = ulKB % 10;
    *szOut-- = '0' + ulNextDigit;
    ulKB = (ulKB - ulNextDigit) / 10;
  } while (ulKB > 0);

  strncpyW(lpszDest, szOut + 1, cchMax);
  return lpszDest;
}

/*************************************************************************
 * StrNCatA	[SHLWAPI.@]
 *
 * Concatenate two strings together.
 *
 * PARAMS
 *  lpszStr [O] String to concatenate to
 *  lpszCat [I] String to add to lpszCat
 *  cchMax  [I] Maximum number of characters to concatenate
 *
 * RETURNS
 *  lpszStr.
 *
 * NOTES
 *  cchMax dtermines the number of characters that are appended to lpszStr,
 *  not the total length of the string.
 */
LPSTR WINAPI StrNCatA(LPSTR lpszStr, LPCSTR lpszCat, INT cchMax)
{
  LPSTR lpszRet = lpszStr;

  TRACE("(%s,%s,%i)\n", debugstr_a(lpszStr), debugstr_a(lpszCat), cchMax);

  if (!lpszStr)
  {
    WARN("Invalid lpszStr would crash under Win32!\n");
    return NULL;
  }

  StrCpyNA(lpszStr + strlen(lpszStr), lpszCat, cchMax);
  return lpszRet;
}

/*************************************************************************
 * StrNCatW	[SHLWAPI.@]
 *
 * See StrNCatA.
 */
LPWSTR WINAPI StrNCatW(LPWSTR lpszStr, LPCWSTR lpszCat, INT cchMax)
{
  LPWSTR lpszRet = lpszStr;

  TRACE("(%s,%s,%i)\n", debugstr_w(lpszStr), debugstr_w(lpszCat), cchMax);

  if (!lpszStr)
  {
    WARN("Invalid lpszStr would crash under Win32\n");
    return NULL;
  }

  StrCpyNW(lpszStr + strlenW(lpszStr), lpszCat, cchMax);
  return lpszRet;
}

/*************************************************************************
 * StrTrimA	[SHLWAPI.@]
 *
 * Remove characters from the start and end of a string.
 *
 * PARAMS
 *  lpszStr  [O] String to remove characters from
 *  lpszTrim [I] Characters to remove from lpszStr
 *
 * RETURNS
 *  TRUE  If lpszStr was valid and modified
 *  FALSE Otherwise
 */
BOOL WINAPI StrTrimA(LPSTR lpszStr, LPCSTR lpszTrim)
{
  DWORD dwLen;
  LPSTR lpszRead = lpszStr;
  BOOL bRet = FALSE;

  TRACE("(%s,%s)\n", debugstr_a(lpszStr), debugstr_a(lpszTrim));

  if (lpszRead && *lpszRead)
  {
    while (*lpszRead && StrChrA(lpszTrim, *lpszRead))
      lpszRead = CharNextA(lpszRead); /* Skip leading matches */

    dwLen = strlen(lpszRead);

    if (lpszRead != lpszStr)
    {
      memmove(lpszStr, lpszRead, dwLen + 1);
      bRet = TRUE;
    }
    if (dwLen > 0)
    {
      lpszRead = lpszStr + dwLen;
      while (StrChrA(lpszTrim, lpszRead[-1]))
        lpszRead = CharPrevA(lpszStr, lpszRead); /* Skip trailing matches */

      if (lpszRead != lpszStr + dwLen)
      {
        *lpszRead = '\0';
        bRet = TRUE;
      }
    }
  }
  return bRet;
}

/*************************************************************************
 * StrTrimW	[SHLWAPI.@]
 *
 * See StrTrimA.
 */
BOOL WINAPI StrTrimW(LPWSTR lpszStr, LPCWSTR lpszTrim)
{
  DWORD dwLen;
  LPWSTR lpszRead = lpszStr;
  BOOL bRet = FALSE;

  TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszTrim));

  if (lpszRead && *lpszRead)
  {
    while (*lpszRead && StrChrW(lpszTrim, *lpszRead))
      lpszRead = CharNextW(lpszRead); /* Skip leading matches */

    dwLen = strlenW(lpszRead);

    if (lpszRead != lpszStr)
    {
      memmove(lpszStr, lpszRead, (dwLen + 1) * sizeof(WCHAR));
      bRet = TRUE;
    }
    if (dwLen > 0)
    {
      lpszRead = lpszStr + dwLen;
      while (StrChrW(lpszTrim, lpszRead[-1]))
        lpszRead = CharPrevW(lpszStr, lpszRead); /* Skip trailing matches */

      if (lpszRead != lpszStr + dwLen)
      {
        *lpszRead = '\0';
        bRet = TRUE;
      }
    }
  }
  return bRet;
}

/*************************************************************************
 *      _SHStrDupA	[INTERNAL]
 *
 * Duplicates a ASCII string to ASCII. The destination buffer is allocated.
 */
static HRESULT WINAPI _SHStrDupAA(LPCSTR src, LPSTR * dest)
{
	HRESULT hr;
	int len = 0;

	if (src) {
	    len = lstrlenA(src);
	    *dest = CoTaskMemAlloc(len);
	} else {
	    *dest = NULL;
	}

	if (*dest) {
	    lstrcpynA(*dest,src, len);
	    hr = S_OK;
	} else {
	    hr = E_OUTOFMEMORY;
	}

	TRACE("%s->(%p)\n", debugstr_a(src), *dest);
	return hr;
}

/*************************************************************************
 * SHStrDupA
 *
 * Return a Unicode copy of a string, in memory allocated by CoTaskMemAlloc.
 *
 * PARAMS
 *  lpszStr   [I] String to copy
 *  lppszDest [O] Destination for the new string copy
 *
 * RETURNS
 *  Success: S_OK. lppszDest contains the new string in Unicode format.
 *  Failure: E_OUTOFMEMORY, If any arguments are invalid or memory allocation
 *           fails.
 */
HRESULT WINAPI SHStrDupA(LPCSTR src, LPWSTR * dest)
{
	HRESULT hr;
	int len = 0;

	if (src) {
	    len = (MultiByteToWideChar(0,0,src,-1,0,0) + 1)* sizeof(WCHAR);
	    *dest = CoTaskMemAlloc(len);
	} else {
	    *dest = NULL;
	}

	if (*dest) {
	    MultiByteToWideChar(0,0,src,-1,*dest,len);
	    hr = S_OK;
	} else {
	    hr = E_OUTOFMEMORY;
	}

	TRACE("%s->(%p)\n", debugstr_a(src), *dest);
	return hr;
}

/*************************************************************************
 *      _SHStrDupAW	[INTERNAL]
 *
 * Duplicates a UNICODE to a ASCII string. The destination buffer is allocated.
 */
static HRESULT WINAPI _SHStrDupAW(LPCWSTR src, LPSTR * dest)
{
	HRESULT hr;
	int len = 0;

	if (src) {
	    len = WideCharToMultiByte(CP_ACP, 0, src, -1, NULL, 0, NULL, NULL);
	    *dest = CoTaskMemAlloc(len);
	} else {
	    *dest = NULL;
	}

	if (*dest) {
	    WideCharToMultiByte(CP_ACP, 0, src, -1, *dest, len, NULL, NULL);
	    hr = S_OK;
	} else {
	    hr = E_OUTOFMEMORY;
	}

	TRACE("%s->(%p)\n", debugstr_w(src), *dest);
	return hr;
}

/*************************************************************************
 * SHStrDupW
 *
 * See SHStrDupA.
 */
HRESULT WINAPI SHStrDupW(LPCWSTR src, LPWSTR * dest)
{
	HRESULT hr;
	int len = 0;

	if (src) {
	    len = (lstrlenW(src) + 1) * sizeof(WCHAR);
	    *dest = CoTaskMemAlloc(len);
	} else {
	    *dest = NULL;
	}

	if (*dest) {
	    memcpy(*dest, src, len);
	    hr = S_OK;
	} else {
	    hr = E_OUTOFMEMORY;
	}

	TRACE("%s->(%p)\n", debugstr_w(src), *dest);
	return hr;
}

/*************************************************************************
 * SHLWAPI_WriteReverseNum
 *
 * Internal helper for SHLWAPI_WriteTimeClass.
 */
inline static LPWSTR SHLWAPI_WriteReverseNum(LPWSTR lpszOut, DWORD dwNum)
{
  *lpszOut-- = '\0';

  /* Write a decimal number to a string, backwards */
  do
  {
    DWORD dwNextDigit = dwNum % 10;
    *lpszOut-- = '0' + dwNextDigit;
    dwNum = (dwNum - dwNextDigit) / 10;
  } while (dwNum > 0);

  return lpszOut;
}

/*************************************************************************
 * SHLWAPI_FormatSignificant
 *
 * Internal helper for SHLWAPI_WriteTimeClass.
 */
inline static int SHLWAPI_FormatSignificant(LPWSTR lpszNum, int dwDigits)
{
  /* Zero non significant digits, return remaining significant digits */
  while (*lpszNum)
  {
    lpszNum++;
    if (--dwDigits == 0)
    {
      while (*lpszNum)
        *lpszNum++ = '0';
      return 0;
    }
  }
  return dwDigits;
}

/*************************************************************************
 * SHLWAPI_WriteTimeClass
 *
 * Internal helper for StrFromTimeIntervalW.
 */
static int SHLWAPI_WriteTimeClass(LPWSTR lpszOut, DWORD dwValue,
                                  LPCWSTR lpszClass, int iDigits)
{
  WCHAR szBuff[64], *szOut = szBuff + 32;

  szOut = SHLWAPI_WriteReverseNum(szOut, dwValue);
  iDigits = SHLWAPI_FormatSignificant(szOut + 1, iDigits);
  *szOut = ' ';
  strcpyW(szBuff + 32, lpszClass);
  strcatW(lpszOut, szOut);
  return iDigits;
}

/*************************************************************************
 * StrFromTimeIntervalA	[SHLWAPI.@]
 *
 * Format a millisecond time interval into a string
 *
 * PARAMS
 *  lpszStr  [O] Output buffer for formatted time interval
 *  cchMax   [I] Size of lpszStr
 *  dwMS     [I] Number of milliseconds
 *  iDigits  [I] Number of digits to print
 *
 * RETURNS
 *  The length of the formatted string, or 0 if any parameter is invalid.
 *
 * NOTES
 *  This implementation mimics the Win32 behaviour of always writing a leading
 *  space before the time interval begins.
 *  iDigits is used to provide approximate times if accuracy is not important.
 *  This number of digits will be written of the first non-zero time class
 *  (hours/minutes/seconds). If this does not complete the time classification,
 *  the remaining digits are changed to zeros (i.e. The time is _not_ rounded).
 *  If there are digits remaining following the writing of a time class, the
 *  next time class will be written.
 *  For example, given dwMS represents 138 hours,43 minutes and 15 seconds, the
 *  following will result from the given values of iDigits:
 *
 *  iDigits    1        2        3        4               5               ...
 *  lpszStr   "100 hr" "130 hr" "138 hr" "138 hr 40 min" "138 hr 43 min"  ...
 */
INT WINAPI StrFromTimeIntervalA(LPSTR lpszStr, UINT cchMax, DWORD dwMS,
                                int iDigits)
{
  INT iRet = 0;

  TRACE("(%p,%d,%ld,%d)\n", lpszStr, cchMax, dwMS, iDigits);

  if (lpszStr && cchMax)
  {
    WCHAR szBuff[128];
    StrFromTimeIntervalW(szBuff, sizeof(szBuff)/sizeof(WCHAR), dwMS, iDigits);
    WideCharToMultiByte(CP_ACP,0,szBuff,-1,lpszStr,cchMax,0,0);
  }
  return iRet;
}


/*************************************************************************
 * StrFromTimeIntervalW	[SHLWAPI.@]
 *
 * See StrFromTimeIntervalA.
 */
INT WINAPI StrFromTimeIntervalW(LPWSTR lpszStr, UINT cchMax, DWORD dwMS,
                                int iDigits)
{
  static const WCHAR szHr[] = {' ','h','r','\0'};
  static const WCHAR szMin[] = {' ','m','i','n','\0'};
  static const WCHAR szSec[] = {' ','s','e','c','\0'};
  INT iRet = 0;

  TRACE("(%p,%d,%ld,%d)\n", lpszStr, cchMax, dwMS, iDigits);

  if (lpszStr && cchMax)
  {
    WCHAR szCopy[128];
    DWORD dwHours, dwMinutes;

    if (!iDigits || cchMax == 1)
    {
      *lpszStr = '\0';
      return 0;
    }

    /* Calculate the time classes */
    dwMS = (dwMS + 500) / 1000;
    dwHours = dwMS / 3600;
    dwMS -= dwHours * 3600;
    dwMinutes = dwMS / 60;
    dwMS -= dwMinutes * 60;

    szCopy[0] = '\0';

    if (dwHours)
      iDigits = SHLWAPI_WriteTimeClass(szCopy, dwHours, szHr, iDigits);

    if (dwMinutes && iDigits)
      iDigits = SHLWAPI_WriteTimeClass(szCopy, dwMinutes, szMin, iDigits);

    if (iDigits) /* Always write seconds if we have significant digits */
      SHLWAPI_WriteTimeClass(szCopy, dwMS, szSec, iDigits);

    strncpyW(lpszStr, szCopy, cchMax);
    iRet = strlenW(lpszStr);
  }
  return iRet;
}

/*************************************************************************
 * StrIsIntlEqualA	[SHLWAPI.@]
 *
 * Compare two strings.
 *
 * PARAMS
 *  bCase    [I] Whether to compare case sensitively
 *  lpszStr  [I] First string to compare
 *  lpszComp [I] Second string to compare
 *  iLen     [I] Length to compare
 *
 * RETURNS
 *  TRUE  If the strings are equal.
 *  FALSE Otherwise.
 */
BOOL WINAPI StrIsIntlEqualA(BOOL bCase, LPCSTR lpszStr, LPCSTR lpszComp,
                            int iLen)
{
  DWORD dwFlags = LOCALE_USE_CP_ACP;
  int iRet;

  TRACE("(%d,%s,%s,%d)\n", bCase,
        debugstr_a(lpszStr), debugstr_a(lpszComp), iLen);

  /* FIXME: These flags are undocumented and unknown by our CompareString.
   *        We need defines for them.
   */
  dwFlags |= bCase ? 0x10000000 : 0x10000001;

  iRet = CompareStringA(GetThreadLocale(),
                        dwFlags, lpszStr, iLen, lpszComp, iLen);

  if (!iRet)
    iRet = CompareStringA(2048, dwFlags, lpszStr, iLen, lpszComp, iLen);

  return iRet == 2 ? TRUE : FALSE;
}

/*************************************************************************
 * StrIsIntlEqualW	[SHLWAPI.@]
 *
 * See StrIsIntlEqualA.
 */
BOOL WINAPI StrIsIntlEqualW(BOOL bCase, LPCWSTR lpszStr, LPCWSTR lpszComp,
                            int iLen)
{
  DWORD dwFlags;
  int iRet;

  TRACE("(%d,%s,%s,%d)\n", bCase,
        debugstr_w(lpszStr),debugstr_w(lpszComp), iLen);

  /* FIXME: These flags are undocumented and unknown by our CompareString.
   *        We need defines for them.
   */
  dwFlags = bCase ? 0x10000000 : 0x10000001;

  iRet = CompareStringW(GetThreadLocale(),
                        dwFlags, lpszStr, iLen, lpszComp, iLen);

  if (!iRet)
    iRet = CompareStringW(2048, dwFlags, lpszStr, iLen, lpszComp, iLen);

  return iRet == 2 ? TRUE : FALSE;
}

/*************************************************************************
 * @    [SHLWAPI.399]
 *
 * Copy a string to another string, up to a maximum number of characters.
 *
 * PARAMS
 *  lpszDest [O] Destination string
 *  lpszSrc  [I] Source string
 *  iLen     [I] Maximum number of chars to copy
 *
 * RETURNS
 *  Success: A pointer to the last character written.
 *  Failure: lpszDest, if any arguments are invalid.
 */
LPSTR WINAPI SHLWAPI_399(LPSTR lpszDest, LPCSTR lpszSrc, int iLen)
{
  TRACE("(%p,%s,%i)\n", lpszDest, debugstr_a(lpszSrc), iLen);

  if (lpszDest && lpszSrc && iLen > 0)
  {
    while ((iLen-- > 1) && *lpszSrc)
      *lpszDest++ = *lpszSrc++;
    if (iLen >= 0)
     *lpszDest = '\0';
  }
  return lpszDest;
}

/*************************************************************************
 * @    [SHLWAPI.400]
 *
 * Unicode version of SHLWAPI_399.
 */
LPWSTR WINAPI SHLWAPI_400(LPWSTR lpszDest, LPCWSTR lpszSrc, int iLen)
{
  TRACE("(%p,%s,%i)\n", lpszDest, debugstr_w(lpszSrc), iLen);

  if (lpszDest && lpszSrc && iLen > 0)
  {
    while ((iLen-- > 1) && *lpszSrc)
      *lpszDest++ = *lpszSrc++;
    if (iLen >= 0)
     *lpszDest = '\0';
  }
  return lpszDest;
}

/*************************************************************************
 * StrCmpLogicalW	[SHLWAPI.@]
 *
 * Compare two strings, ignoring case and comparing digits as numbers.
 *
 * PARAMS
 *  lpszStr  [I] First string to compare
 *  lpszComp [I] Second string to compare
 *  iLen     [I] Length to compare
 *
 * RETURNS
 *  TRUE  If the strings are equal.
 *  FALSE Otherwise.
 */
INT WINAPI StrCmpLogicalW(LPCWSTR lpszStr, LPCWSTR lpszComp)
{
  INT iDiff;

  TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszComp));

  if (lpszStr && lpszComp)
  {
    while (*lpszStr)
    {
      if (!*lpszComp)
        return 1;
      else if (isdigitW(*lpszStr))
      {
        int iStr, iComp;

        if (!isdigitW(*lpszComp))
          return -1;

        /* Compare the numbers */
        StrToIntExW(lpszStr, 0, &iStr);
        StrToIntExW(lpszComp, 0, &iComp);

        if (iStr < iComp)
          return -1;
        else if (iStr > iComp)
          return 1;

        /* Skip */
        while (isdigitW(*lpszStr))
          lpszStr++;
        while (isdigitW(*lpszComp))
          lpszComp++;
      }
      else if (isdigitW(*lpszComp))
        return 1;
      else
      {
        iDiff = SHLWAPI_ChrCmpHelperA(*lpszStr,*lpszComp,NORM_IGNORECASE);
        if (iDiff > 0)
          return 1;
        else if (iDiff < 0)
          return -1;

        lpszStr++;
        lpszComp++;
      }
    }
    if (*lpszComp)
      return -1;
  }
  return 0;
}

/* Structure for formatting byte strings */
typedef struct tagSHLWAPI_BYTEFORMATS
{
  LONGLONG dLimit;
  double   dDivisor;
  double   dNormaliser;
  LPCSTR   lpszFormat;
  CHAR     wPrefix;
} SHLWAPI_BYTEFORMATS;

/*************************************************************************
 * StrFormatByteSize64A	[SHLWAPI.@]
 *
 * Create a string containing an abbreviated byte count of up to 2^63-1.
 *
 * PARAMS
 *  llBytes  [I] Byte size to format
 *  lpszDest [I] Destination for formatted string
 *  cchMax   [I] Size of lpszDest
 *
 * RETURNS
 *  lpszDest.
 *
 * NOTES
 *  There is no StrFormatByteSize64W function, it is called StrFormatByteSizeW.
 */
LPSTR WINAPI StrFormatByteSize64A(LONGLONG llBytes, LPSTR lpszDest, UINT cchMax)
{
  static const char szBytes[] = "%ld bytes";
  static const char sz3_0[] = "%3.0f";
  static const char sz3_1[] = "%3.1f";
  static const char sz3_2[] = "%3.2f";

  static const SHLWAPI_BYTEFORMATS bfFormats[] =
  {
    { 10240, 10.24, 100.0, sz3_2, 'K' }, /* 10 KB */
    { 102400, 102.4, 10.0, sz3_1, 'K' }, /* 100 KB */
    { 1024000, 1024.0, 1.0, sz3_0, 'K' }, /* 1000 KB */
    { 10485760, 10485.76, 100.0, sz3_2, 'M' }, /* 10 MB */
    { 104857600, 104857.6, 10.0, sz3_1, 'M' }, /* 100 MB */
    { 1048576000, 1048576.0, 1.0, sz3_0, 'M' }, /* 1000 MB */
    { 10737418240, 10737418.24, 100.0, sz3_2, 'G' }, /* 10 GB */
    { 107374182400, 107374182.4, 10.0, sz3_1, 'G' }, /* 100 GB */
    { 1073741824000, 1073741824.0, 1.0, sz3_0, 'G' }, /* 1000 GB */
    { 10995116277760, 10485.76, 100.0, sz3_2, 'T' }, /* 10 TB */
    { 109951162777600, 104857.6, 10.0, sz3_1, 'T' }, /* 100 TB */
    { 1099511627776000, 1048576.0, 1.0, sz3_0, 'T' }, /* 1000 TB */
    { 11258999068426240, 10737418.24, 100.00, sz3_2, 'P' }, /* 10 PB */
    { 112589990684262400, 107374182.4, 10.00, sz3_1, 'P' }, /* 100 PB */
    { 1125899906842624000, 1073741824.0, 1.00, sz3_0, 'P' }, /* 1000 PB */
    { 0, 10995116277.76, 100.00, sz3_2, 'E' } /* EB's, catch all */
  };
  char szBuff[32];
  char szAdd[4];
  double dBytes;
  UINT i = 0;

  TRACE("(%lld,%p,%d)\n", llBytes, lpszDest, cchMax);

  if (!lpszDest || !cchMax)
    return lpszDest;

  if (llBytes < 1024)  /* 1K */
  {
    snprintf (lpszDest, cchMax, szBytes, (long)llBytes);
    return lpszDest;
  }

  /* Note that if this loop completes without finding a match, i will be
   * pointing at the last entry, which is a catch all for > 1000 PB
   */
  while (i < sizeof(bfFormats) / sizeof(SHLWAPI_BYTEFORMATS) - 1)
  {
    if (llBytes < bfFormats[i].dLimit)
      break;
    i++;
  }
  /* Above 1 TB we encounter problems with FP accuracy. So for amounts above
   * this number we integer shift down by 1 MB first. The table above has
   * the divisors scaled down from the '< 10 TB' entry onwards, to account
   * for this. We also add a small fudge factor to get the correct result for
   * counts that lie exactly on a 1024 byte boundary.
   */
  if (i > 8)
    dBytes = (double)(llBytes >> 20) + 0.001; /* Scale down by I MB */
  else
    dBytes = (double)llBytes + 0.00001;

  dBytes = floor(dBytes / bfFormats[i].dDivisor) / bfFormats[i].dNormaliser;

  sprintf(szBuff, bfFormats[i].lpszFormat, dBytes);
  szAdd[0] = ' ';
  szAdd[1] = bfFormats[i].wPrefix;
  szAdd[2] = 'B';
  szAdd[3] = '\0';
  strcat(szBuff, szAdd);
  strncpy(lpszDest, szBuff, cchMax);
  return lpszDest;
}

/*************************************************************************
 * StrFormatByteSizeW	[SHLWAPI.@]
 *
 * See StrFormatByteSize64A.
 */
LPWSTR WINAPI StrFormatByteSizeW(LONGLONG llBytes, LPWSTR lpszDest,
                                 UINT cchMax)
{
  char szBuff[32];

  StrFormatByteSize64A(llBytes, szBuff, sizeof(szBuff));

  if (lpszDest)
    MultiByteToWideChar(CP_ACP, 0, szBuff, -1, lpszDest, cchMax);
  return lpszDest;
}

/*************************************************************************
 * StrFormatByteSizeA	[SHLWAPI.@]
 *
 * Create a string containing an abbreviated byte count of up to 2^31-1.
 *
 * PARAMS
 *  dwBytes  [I] Byte size to format
 *  lpszDest [I] Destination for formatted string
 *  cchMax   [I] Size of lpszDest
 *
 * RETURNS
 *  lpszDest.
 *
 * NOTES
 *  The ASCII and Unicode versions of this function accept a different
 *  integer size for dwBytes. See StrFormatByteSize64A.
 */
LPSTR WINAPI StrFormatByteSizeA(DWORD dwBytes, LPSTR lpszDest, UINT cchMax)
{
  TRACE("(%ld,%p,%d)\n", dwBytes, lpszDest, cchMax);

  return StrFormatByteSize64A(dwBytes, lpszDest, cchMax);
}
