/*
 * Path Functions
 *
 * Copyright 1999, 2000 Juergen Schmied
 * Copyright 2001, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */

#include <stdarg.h>
#include <string.h>
#include <stdlib.h>

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

WINE_DEFAULT_DEBUG_CHANNEL(shell);

/* Get a function pointer from a DLL handle */
#define GET_FUNC(func, module, name, fail) \
  do { \
    if (!func) { \
      if (!SHLWAPI_h##module && !(SHLWAPI_h##module = LoadLibraryA(#module ".dll"))) return fail; \
      func = (fn##func)GetProcAddress(SHLWAPI_h##module, name); \
      if (!func) return fail; \
    } \
  } while (0)

/* DLL handles for late bound calls */
static HMODULE SHLWAPI_hshell32;

/* Function pointers for GET_FUNC macro; these need to be global because of gcc bug */
typedef BOOL (WINAPI *fnpIsNetDrive)(int);
static  fnpIsNetDrive pIsNetDrive;

HRESULT WINAPI SHGetWebFolderFilePathW(LPCWSTR,LPWSTR,DWORD);

/*************************************************************************
 * PathBuildRootA    [SHLWAPI.@]
 *
 * Create a root drive string (e.g. "A:\") from a drive number.
 *
 * PARAMS
 *  lpszPath [O] Destination for the drive string
 *
 * RETURNS
 *  lpszPath
 *
 * NOTES
 *  If lpszPath is NULL or drive is invalid, nothing is written to lpszPath.
 */
LPSTR WINAPI PathBuildRootA(LPSTR lpszPath, int drive)
{
  TRACE("(%p,%d)\n", lpszPath, drive);

  if (lpszPath && drive >= 0 && drive < 26)
  {
    lpszPath[0] = 'A' + drive;
    lpszPath[1] = ':';
    lpszPath[2] = '\\';
    lpszPath[3] = '\0';
  }
  return lpszPath;
}

/*************************************************************************
 * PathBuildRootW    [SHLWAPI.@]
 *
 * See PathBuildRootA.
 */
LPWSTR WINAPI PathBuildRootW(LPWSTR lpszPath, int drive)
{
  TRACE("(%p,%d)\n", lpszPath, drive);

  if (lpszPath && drive >= 0 && drive < 26)
  {
    lpszPath[0] = 'A' + drive;
    lpszPath[1] = ':';
    lpszPath[2] = '\\';
    lpszPath[3] = '\0';
  }
  return lpszPath;
}

/*************************************************************************
 * PathRemoveArgsA	[SHLWAPI.@]
 *
 * Strip space separated arguments from a path.
 *
 * PARAMS
 *  lpszPath [I/O] Path to remove arguments from
 *
 * RETURNS
 *  Nothing.
 */
void WINAPI PathRemoveArgsA(LPSTR lpszPath)
{
  TRACE("(%s)\n",debugstr_a(lpszPath));

  if(lpszPath)
  {
    LPSTR lpszArgs = PathGetArgsA(lpszPath);
    if (*lpszArgs)
      lpszArgs[-1] = '\0';
    else
    {
      LPSTR lpszLastChar = CharPrevA(lpszPath, lpszArgs);
      if(*lpszLastChar == ' ')
        *lpszLastChar = '\0';
    }
  }
}

/*************************************************************************
 * PathRemoveArgsW	[SHLWAPI.@]
 *
 * See PathRemoveArgsA.
 */
void WINAPI PathRemoveArgsW(LPWSTR lpszPath)
{
  TRACE("(%s)\n",debugstr_w(lpszPath));

  if(lpszPath)
  {
    LPWSTR lpszArgs = PathGetArgsW(lpszPath);
    if (*lpszArgs || (lpszArgs > lpszPath && lpszArgs[-1] == ' '))
      lpszArgs[-1] = '\0';
  }
}

/*************************************************************************
 * @	[SHLWAPI.4]
 *
 * Unicode version of PathFileExistsDefExtA.
 */
BOOL WINAPI PathFileExistsDefExtW(LPWSTR lpszPath,DWORD dwWhich)
{
  static const WCHAR pszExts[][5]  = { { '.', 'p', 'i', 'f', 0},
                                       { '.', 'c', 'o', 'm', 0},
                                       { '.', 'e', 'x', 'e', 0},
                                       { '.', 'b', 'a', 't', 0},
                                       { '.', 'l', 'n', 'k', 0},
                                       { '.', 'c', 'm', 'd', 0},
                                       { 0, 0, 0, 0, 0} };

  TRACE("(%s,%d)\n", debugstr_w(lpszPath), dwWhich);

  if (!lpszPath || PathIsUNCServerW(lpszPath) || PathIsUNCServerShareW(lpszPath))
    return FALSE;

  if (dwWhich)
  {
    LPCWSTR szExt = PathFindExtensionW(lpszPath);
    if (!*szExt || dwWhich & 0x40)
    {
      size_t iChoose = 0;
      int iLen = lstrlenW(lpszPath);
      if (iLen > (MAX_PATH - 5))
        return FALSE;
      while ( (dwWhich & 0x1) && pszExts[iChoose][0] )
      {
        lstrcpyW(lpszPath + iLen, pszExts[iChoose]);
        if (PathFileExistsW(lpszPath))
          return TRUE;
        iChoose++;
        dwWhich >>= 1;
      }
      *(lpszPath + iLen) = (WCHAR)'\0';
      return FALSE;
    }
  }
  return PathFileExistsW(lpszPath);
}

/*************************************************************************
 * @	[SHLWAPI.3]
 *
 * Determine if a file exists locally and is of an executable type.
 *
 * PARAMS
 *  lpszPath       [I/O] File to search for
 *  dwWhich        [I]   Type of executable to search for
 *
 * RETURNS
 *  TRUE  If the file was found. lpszPath contains the file name.
 *  FALSE Otherwise.
 *
 * NOTES
 *  lpszPath is modified in place and must be at least MAX_PATH in length.
 *  If the function returns FALSE, the path is modified to its original state.
 *  If the given path contains an extension or dwWhich is 0, executable
 *  extensions are not checked.
 *
 *  Ordinals 3-6 are a classic case of MS exposing limited functionality to
 *  users (here through PathFindOnPathA()) and keeping advanced functionality for
 *  their own developers exclusive use. Monopoly, anyone?
 */
BOOL WINAPI PathFileExistsDefExtA(LPSTR lpszPath,DWORD dwWhich)
{
  BOOL bRet = FALSE;

  TRACE("(%s,%d)\n", debugstr_a(lpszPath), dwWhich);

  if (lpszPath)
  {
    WCHAR szPath[MAX_PATH];
    MultiByteToWideChar(CP_ACP,0,lpszPath,-1,szPath,MAX_PATH);
    bRet = PathFileExistsDefExtW(szPath, dwWhich);
    if (bRet)
      WideCharToMultiByte(CP_ACP,0,szPath,-1,lpszPath,MAX_PATH,0,0);
  }
  return bRet;
}

/*************************************************************************
 * SHLWAPI_PathFindInOtherDirs
 *
 * Internal helper for SHLWAPI_PathFindOnPathExA/W.
 */
static BOOL SHLWAPI_PathFindInOtherDirs(LPWSTR lpszFile, DWORD dwWhich)
{
  static const WCHAR szSystem[] = { 'S','y','s','t','e','m','\0'};
  static const WCHAR szPath[] = { 'P','A','T','H','\0'};
  DWORD dwLenPATH;
  LPCWSTR lpszCurr;
  WCHAR *lpszPATH;
  WCHAR buff[MAX_PATH];

  TRACE("(%s,%08x)\n", debugstr_w(lpszFile), dwWhich);

  /* Try system directories */
  GetSystemDirectoryW(buff, MAX_PATH);
  if (!PathAppendW(buff, lpszFile))
     return FALSE;
  if (PathFileExistsDefExtW(buff, dwWhich))
  {
    lstrcpyW(lpszFile, buff);
    return TRUE;
  }
  GetWindowsDirectoryW(buff, MAX_PATH);
  if (!PathAppendW(buff, szSystem ) || !PathAppendW(buff, lpszFile))
    return FALSE;
  if (PathFileExistsDefExtW(buff, dwWhich))
  {
    lstrcpyW(lpszFile, buff);
    return TRUE;
  }
  GetWindowsDirectoryW(buff, MAX_PATH);
  if (!PathAppendW(buff, lpszFile))
    return FALSE;
  if (PathFileExistsDefExtW(buff, dwWhich))
  {
    lstrcpyW(lpszFile, buff);
    return TRUE;
  }
  /* Try dirs listed in %PATH% */
  dwLenPATH = GetEnvironmentVariableW(szPath, buff, MAX_PATH);

  if (!dwLenPATH || !(lpszPATH = HeapAlloc(GetProcessHeap(), 0, (dwLenPATH + 1) * sizeof (WCHAR))))
    return FALSE;

  GetEnvironmentVariableW(szPath, lpszPATH, dwLenPATH + 1);
  lpszCurr = lpszPATH;
  while (lpszCurr)
  {
    LPCWSTR lpszEnd = lpszCurr;
    LPWSTR pBuff = buff;

    while (*lpszEnd == ' ')
      lpszEnd++;
    while (*lpszEnd && *lpszEnd != ';')
      *pBuff++ = *lpszEnd++;
    *pBuff = '\0';

    if (*lpszEnd)
      lpszCurr = lpszEnd + 1;
    else
      lpszCurr = NULL; /* Last Path, terminate after this */

    if (!PathAppendW(buff, lpszFile))
    {
      HeapFree(GetProcessHeap(), 0, lpszPATH);
      return FALSE;
    }
    if (PathFileExistsDefExtW(buff, dwWhich))
    {
      lstrcpyW(lpszFile, buff);
      HeapFree(GetProcessHeap(), 0, lpszPATH);
      return TRUE;
    }
  }
  HeapFree(GetProcessHeap(), 0, lpszPATH);
  return FALSE;
}

/*************************************************************************
 * @	[SHLWAPI.5]
 *
 * Search a range of paths for a specific type of executable.
 *
 * PARAMS
 *  lpszFile       [I/O] File to search for
 *  lppszOtherDirs [I]   Other directories to look in
 *  dwWhich        [I]   Type of executable to search for
 *
 * RETURNS
 *  Success: TRUE. The path to the executable is stored in lpszFile.
 *  Failure: FALSE. The path to the executable is unchanged.
 */
BOOL WINAPI PathFindOnPathExA(LPSTR lpszFile,LPCSTR *lppszOtherDirs,DWORD dwWhich)
{
  WCHAR szFile[MAX_PATH];
  WCHAR buff[MAX_PATH];

  TRACE("(%s,%p,%08x)\n", debugstr_a(lpszFile), lppszOtherDirs, dwWhich);

  if (!lpszFile || !PathIsFileSpecA(lpszFile))
    return FALSE;

  MultiByteToWideChar(CP_ACP,0,lpszFile,-1,szFile,MAX_PATH);

  /* Search provided directories first */
  if (lppszOtherDirs && *lppszOtherDirs)
  {
    WCHAR szOther[MAX_PATH];
    LPCSTR *lpszOtherPath = lppszOtherDirs;

    while (lpszOtherPath && *lpszOtherPath && (*lpszOtherPath)[0])
    {
      MultiByteToWideChar(CP_ACP,0,*lpszOtherPath,-1,szOther,MAX_PATH);
      PathCombineW(buff, szOther, szFile);
      if (PathFileExistsDefExtW(buff, dwWhich))
      {
        WideCharToMultiByte(CP_ACP,0,buff,-1,lpszFile,MAX_PATH,0,0);
        return TRUE;
      }
      lpszOtherPath++;
    }
  }
  /* Not found, try system and path dirs */
  if (SHLWAPI_PathFindInOtherDirs(szFile, dwWhich))
  {
    WideCharToMultiByte(CP_ACP,0,szFile,-1,lpszFile,MAX_PATH,0,0);
    return TRUE;
  }
  return FALSE;
}

/*************************************************************************
 * @	[SHLWAPI.6]
 *
 * Unicode version of PathFindOnPathExA.
 */
BOOL WINAPI PathFindOnPathExW(LPWSTR lpszFile,LPCWSTR *lppszOtherDirs,DWORD dwWhich)
{
  WCHAR buff[MAX_PATH];

  TRACE("(%s,%p,%08x)\n", debugstr_w(lpszFile), lppszOtherDirs, dwWhich);

  if (!lpszFile || !PathIsFileSpecW(lpszFile))
    return FALSE;

  /* Search provided directories first */
  if (lppszOtherDirs && *lppszOtherDirs)
  {
    LPCWSTR *lpszOtherPath = lppszOtherDirs;
    while (lpszOtherPath && *lpszOtherPath && (*lpszOtherPath)[0])
    {
      PathCombineW(buff, *lpszOtherPath, lpszFile);
      if (PathFileExistsDefExtW(buff, dwWhich))
      {
        lstrcpyW(lpszFile, buff);
        return TRUE;
      }
      lpszOtherPath++;
    }
  }
  /* Not found, try system and path dirs */
  return SHLWAPI_PathFindInOtherDirs(lpszFile, dwWhich);
}

/*************************************************************************
 * PathFindOnPathA	[SHLWAPI.@]
 *
 * Search a range of paths for an executable.
 *
 * PARAMS
 *  lpszFile       [I/O] File to search for
 *  lppszOtherDirs [I]   Other directories to look in
 *
 * RETURNS
 *  Success: TRUE. The path to the executable is stored in lpszFile.
 *  Failure: FALSE. The path to the executable is unchanged.
 */
BOOL WINAPI PathFindOnPathA(LPSTR lpszFile, LPCSTR *lppszOtherDirs)
{
  TRACE("(%s,%p)\n", debugstr_a(lpszFile), lppszOtherDirs);
  return PathFindOnPathExA(lpszFile, lppszOtherDirs, 0);
 }

/*************************************************************************
 * PathFindOnPathW      [SHLWAPI.@]
 *
 * See PathFindOnPathA.
 */
BOOL WINAPI PathFindOnPathW(LPWSTR lpszFile, LPCWSTR *lppszOtherDirs)
{
  TRACE("(%s,%p)\n", debugstr_w(lpszFile), lppszOtherDirs);
  return PathFindOnPathExW(lpszFile,lppszOtherDirs, 0);
}

/*************************************************************************
 * PathCompactPathExA   [SHLWAPI.@]
 *
 * Compact a path into a given number of characters.
 *
 * PARAMS
 *  lpszDest [O] Destination for compacted path
 *  lpszPath [I] Source path
 *  cchMax   [I] Maximum size of compacted path
 *  dwFlags  [I] Reserved
 *
 * RETURNS
 *  Success: TRUE. The compacted path is written to lpszDest.
 *  Failure: FALSE. lpszPath is undefined.
 *
 * NOTES
 *  If cchMax is given as 0, lpszDest will still be NUL terminated.
 *
 *  The Win32 version of this function contains a bug: When cchMax == 7,
 *  8 bytes will be written to lpszDest. This bug is fixed in the Wine
 *  implementation.
 *
 *  Some relative paths will be different when cchMax == 5 or 6. This occurs
 *  because Win32 will insert a "\" in lpszDest, even if one is
 *  not present in the original path.
 */
BOOL WINAPI PathCompactPathExA(LPSTR lpszDest, LPCSTR lpszPath,
                               UINT cchMax, DWORD dwFlags)
{
  BOOL bRet = FALSE;

  TRACE("(%p,%s,%d,0x%08x)\n", lpszDest, debugstr_a(lpszPath), cchMax, dwFlags);

  if (lpszPath && lpszDest)
  {
    WCHAR szPath[MAX_PATH];
    WCHAR szDest[MAX_PATH];

    MultiByteToWideChar(CP_ACP,0,lpszPath,-1,szPath,MAX_PATH);
    szDest[0] = '\0';
    bRet = PathCompactPathExW(szDest, szPath, cchMax, dwFlags);
    WideCharToMultiByte(CP_ACP,0,szDest,-1,lpszDest,MAX_PATH,0,0);
  }
  return bRet;
}

/*************************************************************************
 * PathCompactPathExW   [SHLWAPI.@]
 *
 * See PathCompactPathExA.
 */
BOOL WINAPI PathCompactPathExW(LPWSTR lpszDest, LPCWSTR lpszPath,
                               UINT cchMax, DWORD dwFlags)
{
  static const WCHAR szEllipses[] = { '.', '.', '.', '\0' };
  LPCWSTR lpszFile;
  DWORD dwLen, dwFileLen = 0;

  TRACE("(%p,%s,%d,0x%08x)\n", lpszDest, debugstr_w(lpszPath), cchMax, dwFlags);

  if (!lpszPath)
    return FALSE;

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

  *lpszDest = '\0';

  if (cchMax < 2)
    return TRUE;

  dwLen = lstrlenW(lpszPath) + 1;

  if (dwLen < cchMax)
  {
    /* Don't need to compact */
    memcpy(lpszDest, lpszPath, dwLen * sizeof(WCHAR));
    return TRUE;
  }

  /* Path must be compacted to fit into lpszDest */
  lpszFile = PathFindFileNameW(lpszPath);
  dwFileLen = lpszPath + dwLen - lpszFile;

  if (dwFileLen == dwLen)
  {
    /* No root in psth */
    if (cchMax <= 4)
    {
      while (--cchMax > 0) /* No room left for anything but ellipses */
        *lpszDest++ = '.';
      *lpszDest = '\0';
      return TRUE;
    }
    /* Compact the file name with ellipses at the end */
    cchMax -= 4;
    memcpy(lpszDest, lpszFile, cchMax * sizeof(WCHAR));
    lstrcpyW(lpszDest + cchMax, szEllipses);
    return TRUE;
  }
  /* We have a root in the path */
  lpszFile--; /* Start compacted filename with the path separator */
  dwFileLen++;

  if (dwFileLen + 3 > cchMax)
  {
    /* Compact the file name */
    if (cchMax <= 4)
    {
      while (--cchMax > 0) /* No room left for anything but ellipses */
        *lpszDest++ = '.';
      *lpszDest = '\0';
      return TRUE;
    }
    lstrcpyW(lpszDest, szEllipses);
    lpszDest += 3;
    cchMax -= 4;
    *lpszDest++ = *lpszFile++;
    if (cchMax <= 4)
    {
      while (--cchMax > 0) /* No room left for anything but ellipses */
        *lpszDest++ = '.';
      *lpszDest = '\0';
      return TRUE;
    }
    cchMax -= 4;
    memcpy(lpszDest, lpszFile, cchMax * sizeof(WCHAR));
    lstrcpyW(lpszDest + cchMax, szEllipses);
    return TRUE;
  }

  /* Only the root needs to be Compacted */
  dwLen = cchMax - dwFileLen - 3;
  memcpy(lpszDest, lpszPath, dwLen * sizeof(WCHAR));
  lstrcpyW(lpszDest + dwLen, szEllipses);
  lstrcpyW(lpszDest + dwLen + 3, lpszFile);
  return TRUE;
}

/*************************************************************************
 * PathIsDirectoryA	[SHLWAPI.@]
 *
 * Determine if a path is a valid directory
 *
 * PARAMS
 *  lpszPath [I] Path to check.
 *
 * RETURNS
 *  FILE_ATTRIBUTE_DIRECTORY if lpszPath exists and can be read (See Notes)
 *  FALSE if lpszPath is invalid or not a directory.
 *
 * NOTES
 *  Although this function is prototyped as returning a BOOL, it returns
 *  FILE_ATTRIBUTE_DIRECTORY for success. This means that code such as:
 *
 *|  if (PathIsDirectoryA("c:\\windows\\") == TRUE)
 *|    ...
 *
 *  will always fail.
 */
BOOL WINAPI PathIsDirectoryA(LPCSTR lpszPath)
{
  DWORD dwAttr;

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

  if (!lpszPath || PathIsUNCServerA(lpszPath))
    return FALSE;

  if (PathIsUNCServerShareA(lpszPath))
  {
    FIXME("UNC Server Share not yet supported - FAILING\n");
    return FALSE;
  }

  if ((dwAttr = GetFileAttributesA(lpszPath)) == INVALID_FILE_ATTRIBUTES)
    return FALSE;
  return dwAttr & FILE_ATTRIBUTE_DIRECTORY;
}

/*************************************************************************
 * PathIsDirectoryW	[SHLWAPI.@]
 *
 * See PathIsDirectoryA.
 */
BOOL WINAPI PathIsDirectoryW(LPCWSTR lpszPath)
{
  DWORD dwAttr;

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

  if (!lpszPath || PathIsUNCServerW(lpszPath))
    return FALSE;

  if (PathIsUNCServerShareW(lpszPath))
  {
    FIXME("UNC Server Share not yet supported - FAILING\n");
    return FALSE;
  }

  if ((dwAttr = GetFileAttributesW(lpszPath)) == INVALID_FILE_ATTRIBUTES)
    return FALSE;
  return dwAttr & FILE_ATTRIBUTE_DIRECTORY;
}

/*************************************************************************
 * PathFileExistsAndAttributesA	[SHLWAPI.445]
 *
 * Determine if a file exists.
 *
 * PARAMS
 *  lpszPath [I] Path to check
 *  dwAttr   [O] attributes of file
 *
 * RETURNS
 *  TRUE  If the file exists and is readable
 *  FALSE Otherwise
 */
BOOL WINAPI PathFileExistsAndAttributesA(LPCSTR lpszPath, DWORD *dwAttr)
{
  UINT iPrevErrMode;
  DWORD dwVal = 0;

  TRACE("(%s %p)\n", debugstr_a(lpszPath), dwAttr);

  if (dwAttr)
    *dwAttr = INVALID_FILE_ATTRIBUTES;

  if (!lpszPath)
    return FALSE;

  iPrevErrMode = SetErrorMode(SEM_FAILCRITICALERRORS);
  dwVal = GetFileAttributesA(lpszPath);
  SetErrorMode(iPrevErrMode);
  if (dwAttr)
    *dwAttr = dwVal;
  return (dwVal != INVALID_FILE_ATTRIBUTES);
}

/*************************************************************************
 * PathFileExistsAndAttributesW	[SHLWAPI.446]
 *
 * See PathFileExistsA.
 */
BOOL WINAPI PathFileExistsAndAttributesW(LPCWSTR lpszPath, DWORD *dwAttr)
{
  UINT iPrevErrMode;
  DWORD dwVal;

  TRACE("(%s %p)\n", debugstr_w(lpszPath), dwAttr);

  if (!lpszPath)
    return FALSE;

  iPrevErrMode = SetErrorMode(SEM_FAILCRITICALERRORS);
  dwVal = GetFileAttributesW(lpszPath);
  SetErrorMode(iPrevErrMode);
  if (dwAttr)
    *dwAttr = dwVal;
  return (dwVal != INVALID_FILE_ATTRIBUTES);
}

/*************************************************************************
 * PathIsContentTypeA   [SHLWAPI.@]
 *
 * Determine if a file is of a given registered content type.
 *
 * PARAMS
 *  lpszPath        [I] File to check
 *  lpszContentType [I] Content type to check for
 *
 * RETURNS
 *  TRUE  If lpszPath is a given registered content type,
 *  FALSE Otherwise.
 *
 * NOTES
 *  This function looks up the registered content type for lpszPath. If
 *  a content type is registered, it is compared (case insensitively) to
 *  lpszContentType. Only if this matches does the function succeed.
 */
BOOL WINAPI PathIsContentTypeA(LPCSTR path, LPCSTR content_type)
{
    char buf[MAX_PATH];
    DWORD size = sizeof(buf);
    LPCSTR ext;

    TRACE("(%s,%s)\n", debugstr_a(path), debugstr_a(content_type));

    if(!path) return FALSE;
    if(!(ext = PathFindExtensionA(path)) || !*ext) return FALSE;
    if(SHGetValueA(HKEY_CLASSES_ROOT, ext, "Content Type", NULL, buf, &size)) return FALSE;
    return !lstrcmpiA(content_type, buf);
}

/*************************************************************************
 * PathIsContentTypeW   [SHLWAPI.@]
 *
 * See PathIsContentTypeA.
 */
BOOL WINAPI PathIsContentTypeW(LPCWSTR lpszPath, LPCWSTR lpszContentType)
{
  static const WCHAR szContentType[] = { 'C','o','n','t','e','n','t',' ','T','y','p','e','\0' };
  LPCWSTR szExt;
  DWORD dwDummy;
  WCHAR szBuff[MAX_PATH];

  TRACE("(%s,%s)\n", debugstr_w(lpszPath), debugstr_w(lpszContentType));

  if (lpszPath && (szExt = PathFindExtensionW(lpszPath)) && *szExt &&
      !SHGetValueW(HKEY_CLASSES_ROOT, szExt, szContentType,
                   REG_NONE, szBuff, &dwDummy) &&
      !wcsicmp(lpszContentType, szBuff))
  {
    return TRUE;
  }
  return FALSE;
}

/*************************************************************************
 * PathIsSystemFolderA   [SHLWAPI.@]
 *
 * Determine if a path or file attributes are a system folder.
 *
 * PARAMS
 *  lpszPath  [I] Path to check.
 *  dwAttrib  [I] Attributes to check, if lpszPath is NULL.
 *
 * RETURNS
 *  TRUE   If lpszPath or dwAttrib are a system folder.
 *  FALSE  If GetFileAttributesA() fails or neither parameter is a system folder.
 */
BOOL WINAPI PathIsSystemFolderA(LPCSTR lpszPath, DWORD dwAttrib)
{
  TRACE("(%s,0x%08x)\n", debugstr_a(lpszPath), dwAttrib);

  if (lpszPath && *lpszPath)
    dwAttrib = GetFileAttributesA(lpszPath);

  if (dwAttrib == INVALID_FILE_ATTRIBUTES || !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY) ||
      !(dwAttrib & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY)))
    return FALSE;
  return TRUE;
}

/*************************************************************************
 * PathIsSystemFolderW   [SHLWAPI.@]
 *
 * See PathIsSystemFolderA.
 */
BOOL WINAPI PathIsSystemFolderW(LPCWSTR lpszPath, DWORD dwAttrib)
{
  TRACE("(%s,0x%08x)\n", debugstr_w(lpszPath), dwAttrib);

  if (lpszPath && *lpszPath)
    dwAttrib = GetFileAttributesW(lpszPath);

  if (dwAttrib == INVALID_FILE_ATTRIBUTES || !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY) ||
      !(dwAttrib & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY)))
    return FALSE;
  return TRUE;
}

/*************************************************************************
 * PathMakePrettyA   [SHLWAPI.@]
 *
 * Convert an uppercase DOS filename into lowercase.
 *
 * PARAMS
 *  lpszPath [I/O] Path to convert.
 *
 * RETURNS
 *  TRUE  If the path was an uppercase DOS path and was converted,
 *  FALSE Otherwise.
 */
BOOL WINAPI PathMakePrettyA(LPSTR lpszPath)
{
  LPSTR pszIter = lpszPath;

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

  if (!pszIter)
    return FALSE;

  if (*pszIter)
  {
    do
    {
      if (islower(*pszIter) || IsDBCSLeadByte(*pszIter))
        return FALSE; /* Not DOS path */
      pszIter++;
    } while (*pszIter);
    pszIter = lpszPath + 1;
    while (*pszIter)
    {
      *pszIter = tolower(*pszIter);
      pszIter++;
    }
  }
  return TRUE;
}

/*************************************************************************
 * PathMakePrettyW   [SHLWAPI.@]
 *
 * See PathMakePrettyA.
 */
BOOL WINAPI PathMakePrettyW(LPWSTR lpszPath)
{
  LPWSTR pszIter = lpszPath;

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

  if (!pszIter)
    return FALSE;

  if (*pszIter)
  {
    do
    {
      if (iswlower(*pszIter))
        return FALSE; /* Not DOS path */
      pszIter++;
    } while (*pszIter);
    pszIter = lpszPath + 1;
    while (*pszIter)
    {
      *pszIter = towlower(*pszIter);
      pszIter++;
    }
  }
  return TRUE;
}

/*************************************************************************
 * PathCompactPathA   [SHLWAPI.@]
 *
 * Make a path fit into a given width when printed to a DC.
 *
 * PARAMS
 *  hDc      [I]   Destination DC
 *  lpszPath [I/O] Path to be printed to hDc
 *  dx       [I]   Desired width
 *
 * RETURNS
 *  TRUE  If the path was modified/went well.
 *  FALSE Otherwise.
 */
BOOL WINAPI PathCompactPathA(HDC hDC, LPSTR lpszPath, UINT dx)
{
  BOOL bRet = FALSE;

  TRACE("(%p,%s,%d)\n", hDC, debugstr_a(lpszPath), dx);

  if (lpszPath)
  {
    WCHAR szPath[MAX_PATH];
    MultiByteToWideChar(CP_ACP,0,lpszPath,-1,szPath,MAX_PATH);
    bRet = PathCompactPathW(hDC, szPath, dx);
    WideCharToMultiByte(CP_ACP,0,szPath,-1,lpszPath,MAX_PATH,0,0);
  }
  return bRet;
}

/*************************************************************************
 * PathCompactPathW   [SHLWAPI.@]
 *
 * See PathCompactPathA.
 */
BOOL WINAPI PathCompactPathW(HDC hDC, LPWSTR lpszPath, UINT dx)
{
  static const WCHAR szEllipses[] = { '.', '.', '.', '\0' };
  BOOL bRet = TRUE;
  HDC hdc = 0;
  WCHAR buff[MAX_PATH];
  SIZE size;
  DWORD dwLen;

  TRACE("(%p,%s,%d)\n", hDC, debugstr_w(lpszPath), dx);

  if (!lpszPath)
    return FALSE;

  if (!hDC)
    hdc = hDC = GetDC(0);

  /* Get the length of the whole path */
  dwLen = lstrlenW(lpszPath);
  GetTextExtentPointW(hDC, lpszPath, dwLen, &size);

  if ((UINT)size.cx > dx)
  {
    /* Path too big, must reduce it */
    LPWSTR sFile;
    DWORD dwEllipsesLen = 0, dwPathLen = 0;

    sFile = PathFindFileNameW(lpszPath);
    if (sFile != lpszPath) sFile--;

    /* Get the size of ellipses */
    GetTextExtentPointW(hDC, szEllipses, 3, &size);
    dwEllipsesLen = size.cx;
    /* Get the size of the file name */
    GetTextExtentPointW(hDC, sFile, lstrlenW(sFile), &size);
    dwPathLen = size.cx;

    if (sFile != lpszPath)
    {
      LPWSTR sPath = sFile;
      BOOL bEllipses = FALSE;

      /* The path includes a file name. Include as much of the path prior to
       * the file name as possible, allowing for the ellipses, e.g:
       * c:\some very long path\filename ==> c:\some v...\filename
       */
      lstrcpynW(buff, sFile, MAX_PATH);

      do
      {
        DWORD dwTotalLen = bEllipses? dwPathLen + dwEllipsesLen : dwPathLen;

        GetTextExtentPointW(hDC, lpszPath, sPath - lpszPath, &size);
        dwTotalLen += size.cx;
        if (dwTotalLen <= dx)
          break;
        sPath--;
        if (!bEllipses)
        {
          bEllipses = TRUE;
          sPath -= 2;
        }
      } while (sPath > lpszPath);

      if (sPath > lpszPath)
      {
        if (bEllipses)
        {
          lstrcpyW(sPath, szEllipses);
          lstrcpyW(sPath+3, buff);
        }
        bRet = TRUE;
        goto end;
      }
      lstrcpyW(lpszPath, szEllipses);
      lstrcpyW(lpszPath+3, buff);
      bRet = FALSE;
      goto end;
    }

    /* Trim the path by adding ellipses to the end, e.g:
     * A very long file name.txt ==> A very...
     */
    dwLen = lstrlenW(lpszPath);

    if (dwLen > MAX_PATH - 3)
      dwLen =  MAX_PATH - 3;
    lstrcpynW(buff, sFile, dwLen);

    do {
      dwLen--;
      GetTextExtentPointW(hDC, buff, dwLen, &size);
    } while (dwLen && size.cx + dwEllipsesLen > dx);

   if (!dwLen)
   {
     DWORD dwWritten = 0;

     dwEllipsesLen /= 3; /* Size of a single '.' */

     /* Write as much of the Ellipses string as possible */
     while (dwWritten + dwEllipsesLen < dx && dwLen < 3)
     {
       *lpszPath++ = '.';
       dwWritten += dwEllipsesLen;
       dwLen++;
     }
     *lpszPath = '\0';
     bRet = FALSE;
   }
   else
   {
     lstrcpyW(buff + dwLen, szEllipses);
     lstrcpyW(lpszPath, buff);
    }
  }

end:
  if (hdc)
    ReleaseDC(0, hdc);

  return bRet;
}

/*************************************************************************
 * SHLWAPI_UseSystemForSystemFolders
 *
 * Internal helper for PathMakeSystemFolderW.
 */
static BOOL SHLWAPI_UseSystemForSystemFolders(void)
{
  static BOOL bCheckedReg = FALSE;
  static BOOL bUseSystemForSystemFolders = FALSE;

  if (!bCheckedReg)
  {
    bCheckedReg = TRUE;

    /* Key tells Win what file attributes to use on system folders */
    if (SHGetValueA(HKEY_LOCAL_MACHINE,
        "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer",
        "UseSystemForSystemFolders", 0, 0, 0))
      bUseSystemForSystemFolders = TRUE;
  }
  return bUseSystemForSystemFolders;
}

/*************************************************************************
 * PathMakeSystemFolderA   [SHLWAPI.@]
 *
 * Set system folder attribute for a path.
 *
 * PARAMS
 *  lpszPath [I] The path to turn into a system folder
 *
 * RETURNS
 *  TRUE  If the path was changed to/already was a system folder
 *  FALSE If the path is invalid or SetFileAttributesA() fails
 */
BOOL WINAPI PathMakeSystemFolderA(LPCSTR lpszPath)
{
  BOOL bRet = FALSE;

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

  if (lpszPath && *lpszPath)
  {
    WCHAR szPath[MAX_PATH];
    MultiByteToWideChar(CP_ACP,0,lpszPath,-1,szPath,MAX_PATH);
    bRet = PathMakeSystemFolderW(szPath);
  }
  return bRet;
}

/*************************************************************************
 * PathMakeSystemFolderW   [SHLWAPI.@]
 *
 * See PathMakeSystemFolderA.
 */
BOOL WINAPI PathMakeSystemFolderW(LPCWSTR lpszPath)
{
  DWORD dwDefaultAttr = FILE_ATTRIBUTE_READONLY, dwAttr;
  WCHAR buff[MAX_PATH];

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

  if (!lpszPath || !*lpszPath)
    return FALSE;

  /* If the directory is already a system directory, don't do anything */
  GetSystemDirectoryW(buff, MAX_PATH);
  if (!wcscmp(buff, lpszPath))
    return TRUE;

  GetWindowsDirectoryW(buff, MAX_PATH);
  if (!wcscmp(buff, lpszPath))
    return TRUE;

  /* "UseSystemForSystemFolders" Tells Win what attributes to use */
  if (SHLWAPI_UseSystemForSystemFolders())
    dwDefaultAttr = FILE_ATTRIBUTE_SYSTEM;

  if ((dwAttr = GetFileAttributesW(lpszPath)) == INVALID_FILE_ATTRIBUTES)
    return FALSE;

  /* Change file attributes to system attributes */
  dwAttr &= ~(FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_READONLY);
  return SetFileAttributesW(lpszPath, dwAttr | dwDefaultAttr);
}

/*************************************************************************
 * PathUnmakeSystemFolderA   [SHLWAPI.@]
 *
 * Remove the system folder attributes from a path.
 *
 * PARAMS
 *  lpszPath [I] The path to remove attributes from
 *
 * RETURNS
 *  Success: TRUE.
 *  Failure: FALSE, if lpszPath is NULL, empty, not a directory, or calling
 *           SetFileAttributesA() fails.
 */
BOOL WINAPI PathUnmakeSystemFolderA(LPCSTR lpszPath)
{
  DWORD dwAttr;

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

  if (!lpszPath || !*lpszPath || (dwAttr = GetFileAttributesA(lpszPath)) == INVALID_FILE_ATTRIBUTES ||
      !(dwAttr & FILE_ATTRIBUTE_DIRECTORY))
    return FALSE;

  dwAttr &= ~(FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM);
  return SetFileAttributesA(lpszPath, dwAttr);
}

/*************************************************************************
 * PathUnmakeSystemFolderW   [SHLWAPI.@]
 *
 * See PathUnmakeSystemFolderA.
 */
BOOL WINAPI PathUnmakeSystemFolderW(LPCWSTR lpszPath)
{
  DWORD dwAttr;

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

  if (!lpszPath || !*lpszPath || (dwAttr = GetFileAttributesW(lpszPath)) == INVALID_FILE_ATTRIBUTES ||
    !(dwAttr & FILE_ATTRIBUTE_DIRECTORY))
    return FALSE;

  dwAttr &= ~(FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM);
  return SetFileAttributesW(lpszPath, dwAttr);
}


/*************************************************************************
 * PathSetDlgItemPathA   [SHLWAPI.@]
 *
 * Set the text of a dialog item to a path, shrinking the path to fit
 * if it is too big for the item.
 *
 * PARAMS
 *  hDlg     [I] Dialog handle
 *  id       [I] ID of item in the dialog
 *  lpszPath [I] Path to set as the items text
 *
 * RETURNS
 *  Nothing.
 *
 * NOTES
 *  If lpszPath is NULL, a blank string ("") is set (i.e. The previous
 *  window text is erased).
 */
VOID WINAPI PathSetDlgItemPathA(HWND hDlg, int id, LPCSTR lpszPath)
{
  WCHAR szPath[MAX_PATH];

  TRACE("(%p,%8x,%s)\n",hDlg, id, debugstr_a(lpszPath));

  if (lpszPath)
    MultiByteToWideChar(CP_ACP,0,lpszPath,-1,szPath,MAX_PATH);
  else
    szPath[0] = '\0';
  PathSetDlgItemPathW(hDlg, id, szPath);
}

/*************************************************************************
 * PathSetDlgItemPathW   [SHLWAPI.@]
 *
 * See PathSetDlgItemPathA.
 */
VOID WINAPI PathSetDlgItemPathW(HWND hDlg, int id, LPCWSTR lpszPath)
{
  WCHAR path[MAX_PATH + 1];
  HWND hwItem;
  RECT rect;
  HDC hdc;
  HGDIOBJ hPrevObj;

  TRACE("(%p,%8x,%s)\n",hDlg, id, debugstr_w(lpszPath));

  if (!(hwItem = GetDlgItem(hDlg, id)))
    return;

  if (lpszPath)
    lstrcpynW(path, lpszPath, ARRAY_SIZE(path));
  else
    path[0] = '\0';

  GetClientRect(hwItem, &rect);
  hdc = GetDC(hDlg);
  hPrevObj = SelectObject(hdc, (HGDIOBJ)SendMessageW(hwItem,WM_GETFONT,0,0));

  if (hPrevObj)
  {
    PathCompactPathW(hdc, path, rect.right);
    SelectObject(hdc, hPrevObj);
  }

  ReleaseDC(hDlg, hdc);
  SetWindowTextW(hwItem, path);
}

/*************************************************************************
 * PathIsNetworkPathA [SHLWAPI.@]
 *
 * Determine if the given path is a network path.
 *
 * PARAMS
 *  lpszPath [I] Path to check
 *
 * RETURNS
 *  TRUE  If lpszPath is a UNC share or mapped network drive, or
 *  FALSE If lpszPath is a local drive or cannot be determined
 */
BOOL WINAPI PathIsNetworkPathA(LPCSTR lpszPath)
{
  int dwDriveNum;

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

  if (!lpszPath)
    return FALSE;
  if (*lpszPath == '\\' && lpszPath[1] == '\\')
    return TRUE;
  dwDriveNum = PathGetDriveNumberA(lpszPath);
  if (dwDriveNum == -1)
    return FALSE;
  GET_FUNC(pIsNetDrive, shell32, (LPCSTR)66, FALSE); /* ord 66 = shell32.IsNetDrive */
  return pIsNetDrive(dwDriveNum);
}

/*************************************************************************
 * PathIsNetworkPathW [SHLWAPI.@]
 *
 * See PathIsNetworkPathA.
 */
BOOL WINAPI PathIsNetworkPathW(LPCWSTR lpszPath)
{
  int dwDriveNum;

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

  if (!lpszPath)
    return FALSE;
  if (*lpszPath == '\\' && lpszPath[1] == '\\')
    return TRUE;
  dwDriveNum = PathGetDriveNumberW(lpszPath);
  if (dwDriveNum == -1)
    return FALSE;
  GET_FUNC(pIsNetDrive, shell32, (LPCSTR)66, FALSE); /* ord 66 = shell32.IsNetDrive */
  return pIsNetDrive(dwDriveNum);
}

/*************************************************************************
 * PathIsDirectoryEmptyA [SHLWAPI.@]
 *
 * Determine if a given directory is empty.
 *
 * PARAMS
 *  lpszPath [I] Directory to check
 *
 * RETURNS
 *  TRUE  If the directory exists and contains no files,
 *  FALSE Otherwise
 */
BOOL WINAPI PathIsDirectoryEmptyA(LPCSTR lpszPath)
{
  BOOL bRet = FALSE;

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

  if (lpszPath)
  {
    WCHAR szPath[MAX_PATH];
    MultiByteToWideChar(CP_ACP,0,lpszPath,-1,szPath,MAX_PATH);
    bRet = PathIsDirectoryEmptyW(szPath);
  }
  return bRet;
}

/*************************************************************************
 * PathIsDirectoryEmptyW [SHLWAPI.@]
 *
 * See PathIsDirectoryEmptyA.
 */
BOOL WINAPI PathIsDirectoryEmptyW(LPCWSTR lpszPath)
{
  static const WCHAR szAllFiles[] = { '*', '.', '*', '\0' };
  WCHAR szSearch[MAX_PATH];
  DWORD dwLen;
  HANDLE hfind;
  BOOL retVal = TRUE;
  WIN32_FIND_DATAW find_data;

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

  if (!lpszPath || !PathIsDirectoryW(lpszPath))
    return FALSE;

  lstrcpynW(szSearch, lpszPath, MAX_PATH);
  PathAddBackslashW(szSearch);
  dwLen = lstrlenW(szSearch);
  if (dwLen > MAX_PATH - 4)
    return FALSE;

  lstrcpyW(szSearch + dwLen, szAllFiles);
  hfind = FindFirstFileW(szSearch, &find_data);
  if (hfind == INVALID_HANDLE_VALUE)
    return FALSE;

  do
  {
    if (find_data.cFileName[0] == '.')
    {
      if (find_data.cFileName[1] == '\0') continue;
      if (find_data.cFileName[1] == '.' && find_data.cFileName[2] == '\0') continue;
    }

    retVal = FALSE;
    break;
  }
  while (FindNextFileW(hfind, &find_data));

  FindClose(hfind);
  return retVal;
}


/*************************************************************************
 * PathFindSuffixArrayA [SHLWAPI.@]
 *
 * Find a suffix string in an array of suffix strings
 *
 * PARAMS
 *  lpszSuffix [I] Suffix string to search for
 *  lppszArray [I] Array of suffix strings to search
 *  dwCount    [I] Number of elements in lppszArray
 *
 * RETURNS
 *  Success: The index of the position of lpszSuffix in lppszArray
 *  Failure: 0, if any parameters are invalid or lpszSuffix is not found
 *
 * NOTES
 *  The search is case sensitive.
 *  The match is made against the end of the suffix string, so for example:
 *  lpszSuffix="fooBAR" matches "BAR", but lpszSuffix="fooBARfoo" does not.
 */
LPCSTR WINAPI PathFindSuffixArrayA(LPCSTR lpszSuffix, LPCSTR *lppszArray, int dwCount)
{
  size_t dwLen;
  int dwRet = 0;

  TRACE("(%s,%p,%d)\n",debugstr_a(lpszSuffix), lppszArray, dwCount);

  if (lpszSuffix && lppszArray && dwCount > 0)
  {
    dwLen = strlen(lpszSuffix);

    while (dwRet < dwCount)
    {
      size_t dwCompareLen = strlen(*lppszArray);
      if (dwCompareLen < dwLen)
      {
        if (!strcmp(lpszSuffix + dwLen - dwCompareLen, *lppszArray))
          return *lppszArray; /* Found */
      }
      dwRet++;
      lppszArray++;
    }
  }
  return NULL;
}

/*************************************************************************
 * PathFindSuffixArrayW [SHLWAPI.@]
 *
 * See PathFindSuffixArrayA.
 */
LPCWSTR WINAPI PathFindSuffixArrayW(LPCWSTR lpszSuffix, LPCWSTR *lppszArray, int dwCount)
{
  size_t dwLen;
  int dwRet = 0;

  TRACE("(%s,%p,%d)\n",debugstr_w(lpszSuffix), lppszArray, dwCount);

  if (lpszSuffix && lppszArray && dwCount > 0)
  {
    dwLen = lstrlenW(lpszSuffix);

    while (dwRet < dwCount)
    {
      size_t dwCompareLen = lstrlenW(*lppszArray);
      if (dwCompareLen < dwLen)
      {
        if (!wcscmp(lpszSuffix + dwLen - dwCompareLen, *lppszArray))
          return *lppszArray; /* Found */
      }
      dwRet++;
      lppszArray++;
    }
  }
  return NULL;
}

/*************************************************************************
 * PathUndecorateA [SHLWAPI.@]
 *
 * Undecorate a file path
 *
 * PARAMS
 *  lpszPath [I/O] Path to remove any decoration from
 *
 * RETURNS
 *  Nothing
 *
 * NOTES
 *  A decorations form is "path[n].ext" where "n" is an optional decimal number.
 */
void WINAPI PathUndecorateA(char *path)
{
  char *ext, *skip;

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

  if (!path) return;

  ext = PathFindExtensionA(path);
  if (ext == path || ext[-1] != ']') return;

  skip = ext - 2;
  while (skip > path && '0' <= *skip && *skip <= '9')
      skip--;

  if (skip > path && *skip == '[' && skip[-1] != '\\')
      memmove(skip, ext, strlen(ext) + 1);
}

/*************************************************************************
 * PathUndecorateW [SHLWAPI.@]
 *
 * See PathUndecorateA.
 */
void WINAPI PathUndecorateW(WCHAR *path)
{
  WCHAR *ext, *skip;

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

  if (!path) return;

  ext = PathFindExtensionW(path);
  if (ext == path || ext[-1] != ']') return;

  skip = ext - 2;
  while (skip > path && '0' <= *skip && *skip <= '9')
      skip--;

  if (skip > path && *skip == '[' && skip[-1] != '\\')
      memmove(skip, ext, (wcslen(ext) + 1) * sizeof(WCHAR));
}

/*************************************************************************
 * @     [SHLWAPI.440]
 *
 * Find localised or default web content in "%WINDOWS%\web\".
 *
 * PARAMS
 *  lpszFile  [I] File name containing content to look for
 *  lpszPath  [O] Buffer to contain the full path to the file
 *  dwPathLen [I] Length of lpszPath
 *
 * RETURNS
 *  Success: S_OK. lpszPath contains the full path to the content.
 *  Failure: E_FAIL. The content does not exist or lpszPath is too short.
 */
HRESULT WINAPI SHGetWebFolderFilePathA(LPCSTR lpszFile, LPSTR lpszPath, DWORD dwPathLen)
{
  WCHAR szFile[MAX_PATH], szPath[MAX_PATH];
  HRESULT hRet;

  TRACE("(%s,%p,%d)\n", lpszFile, lpszPath, dwPathLen);

  MultiByteToWideChar(CP_ACP, 0, lpszFile, -1, szFile, MAX_PATH);
  szPath[0] = '\0';
  hRet = SHGetWebFolderFilePathW(szFile, szPath, dwPathLen);
  WideCharToMultiByte(CP_ACP, 0, szPath, -1, lpszPath, dwPathLen, 0, 0);
  return hRet;
}

/*************************************************************************
 * @     [SHLWAPI.441]
 *
 * Unicode version of SHGetWebFolderFilePathA.
 */
HRESULT WINAPI SHGetWebFolderFilePathW(LPCWSTR lpszFile, LPWSTR lpszPath, DWORD dwPathLen)
{
  static const WCHAR szWeb[] = {'\\','W','e','b','\\','\0'};
  static const WCHAR szWebMui[] = {'m','u','i','\\','%','0','4','x','\\','\0'};
  DWORD dwLen, dwFileLen;
  LANGID lidSystem, lidUser;

  TRACE("(%s,%p,%d)\n", debugstr_w(lpszFile), lpszPath, dwPathLen);

  /* Get base directory for web content */
  dwLen = GetSystemWindowsDirectoryW(lpszPath, dwPathLen);
  if (dwLen > 0 && lpszPath[dwLen-1] == '\\')
    dwLen--;

  dwFileLen = lstrlenW(lpszFile);

  if (dwLen + dwFileLen + ARRAY_SIZE(szWeb) >= dwPathLen)
    return E_FAIL; /* lpszPath too short */

  lstrcpyW(lpszPath+dwLen, szWeb);
  dwLen += ARRAY_SIZE(szWeb);
  dwPathLen = dwPathLen - dwLen; /* Remaining space */

  lidSystem = GetSystemDefaultUILanguage();
  lidUser = GetUserDefaultUILanguage();

  if (lidSystem != lidUser)
  {
    if (dwFileLen + ARRAY_SIZE(szWebMui) < dwPathLen)
    {
      /* Use localised content in the users UI language if present */
      wsprintfW(lpszPath + dwLen, szWebMui, lidUser);
      lstrcpyW(lpszPath + dwLen + ARRAY_SIZE(szWebMui), lpszFile);
      if (PathFileExistsW(lpszPath))
        return S_OK;
    }
  }

  /* Fall back to OS default installed content */
  lstrcpyW(lpszPath + dwLen, lpszFile);
  if (PathFileExistsW(lpszPath))
    return S_OK;
  return E_FAIL;
}