#include "verpriv.h"
#include "wchar.h"

/* Determine if a file is in use by Windows
 */
BOOL FileInUse(LPWSTR lpszFilePath, LPWSTR lpszFileName)
{
    HANDLE hFile;
    BOOL bResult = FALSE;

    //
    // Attempt to open the file exclusively.
    //

    hFile = CreateFile(lpszFilePath,
                       GENERIC_WRITE | GENERIC_READ,
                       0,
                       NULL,
                       OPEN_EXISTING,
                       FILE_ATTRIBUTE_NORMAL,
                       NULL);

    if (hFile == INVALID_HANDLE_VALUE) {

        //
        // If the last error is access denied,
        // then the file is in use by someone
        // else.  Return TRUE in this case.
        //

        if (GetLastError() == ERROR_SHARING_VIOLATION)
            bResult = TRUE;

    } else {

        //
        // CreateFile successfully opened the file.
        // Close the handle and return FALSE.
        //

        CloseHandle(hFile);
    }

    return bResult;
}


/* Take a Dir and Filename and make a full path from them
 */
DWORD MakeFileName(LPWSTR lpDst, LPWSTR lpDir, LPWSTR lpFile, int cchDst)
{
  DWORD wDirLen;
  WCHAR cTemp;

  wcsncpy(lpDst, lpDir, cchDst);
  lpDst[cchDst-1] = TEXT('\0');
  wDirLen=wcslen(lpDst);

  if ( wDirLen && (cTemp=*(lpDst+wDirLen-1))!=TEXT('\\') && cTemp!=TEXT(':'))
      lpDst[wDirLen++] = TEXT('\\');

  wcsncpy(lpDst+wDirLen, lpFile, cchDst - wDirLen);
  lpDst[cchDst-1] = TEXT('\0');

  return(wDirLen);
}


/* Given a filename and a list of directories, find the first directory
 * that contains the file, and copy it into the buffer.  Note that in the
 * library version, you can give an environment style path, but not in the
 * DLL version.
 */
INT
GetDirOfFile(LPWSTR lpszFileName,
    LPWSTR lpszPathName,
    DWORD wSize,
    LPWSTR *lplpszDirs)
{
  WCHAR szFileName[_MAX_PATH];
  HANDLE hfRes;
  INT nFileLen, nPathLen;
  BOOL bDoDefaultOpen = TRUE;
  LPWSTR *lplpFirstDir;
  LPWSTR lpszDir;

  nFileLen = wcslen(lpszFileName);

  for (lplpFirstDir=lplpszDirs; *lplpFirstDir && bDoDefaultOpen;
        ++lplpFirstDir)
    {
      lpszDir = *lplpFirstDir;

      if (nFileLen+wcslen(lpszDir) >= _MAX_PATH-1)
          continue;
      MakeFileName(szFileName, lpszDir, lpszFileName, ARRAYSIZE(szFileName));

TryOpen:
    if ((hfRes = CreateFile(szFileName, GENERIC_READ,
            FILE_SHARE_READ, NULL, OPEN_EXISTING,
            FILE_FLAG_SEQUENTIAL_SCAN, NULL)) != (HANDLE)-1)
        {
          CloseHandle(hfRes);
          for (lpszDir=szFileName; *lpszDir; lpszDir++)
              if (*lpszDir == TEXT('\\'))
                  nPathLen = lpszDir - (LPWSTR)szFileName;

          /* This gets rid of the '\' if this is not the root of a drive
           */
          if (nPathLen <= 3)
              ++nPathLen;

          /* Account for the terminating NULL, and make sure wSize is in bounds
           * then NULL terminate the string in the appropriate place so that
           * we can just do an wcscpy.
           */
          --wSize;
          szFileName[(int)wSize<nPathLen ? wSize : nPathLen] = 0;
          wcscpy(lpszPathName, szFileName);

          return(nPathLen);
        }
    }

  if (bDoDefaultOpen)
    {
      bDoDefaultOpen = FALSE;
      wcscpy(szFileName, lpszFileName);
      goto TryOpen;
    }

  return(0);
}


#define GetWindowsDir(x,y,z) GetWindowsDirectory(y,z)
#define GetSystemDir(x,y,z) GetSystemDirectory(y,z)


DWORD
APIENTRY
VerFindFileW(
        DWORD wFlags,
        LPWSTR lpszFileName,
        LPWSTR lpszWinDir,
        LPWSTR lpszAppDir,
        LPWSTR lpszCurDir,
        PUINT puCurDirLen,
        LPWSTR lpszDestDir,
        PUINT puDestDirLen
        )
{
  static WORD wSharedDirLen = 0;
  static WCHAR gszSharedDir[_MAX_PATH];

  WCHAR szSysDir[_MAX_PATH], cTemp;
  WCHAR szWinDir[_MAX_PATH];
  WCHAR szCurDir[_MAX_PATH];
  LPWSTR lpszDir, lpszDirs[4];
  WORD wDestLen, wWinLen, wRetVal = 0, wTemp;
  int nRet;

  /* We want to really look in the Windows directory; we don't trust the app
   */
  GetWindowsDir(lpszWinDir ? lpszWinDir : "", szWinDir, _MAX_PATH);
  lpszWinDir = szWinDir;

  if (!GetSystemDir(lpszWinDir, szSysDir, _MAX_PATH))
      wcscpy(szSysDir, lpszWinDir);

  if (wFlags & VFFF_ISSHAREDFILE) {
     lpszDirs[0] = lpszWinDir;
     lpszDirs[1] = szSysDir;
     lpszDirs[2] = lpszAppDir;
  } else {
     lpszDirs[0] = lpszAppDir;
     lpszDirs[1] = lpszWinDir;
     lpszDirs[2] = szSysDir;
  }

  lpszDirs[3] = NULL;

  if (!(wTemp=GetDirOfFile(lpszFileName, szCurDir, _MAX_PATH, lpszDirs)))
      *szCurDir = 0;
  if (*puCurDirLen > wTemp)
      wcscpy(lpszCurDir, szCurDir);
  else
      wRetVal |= VFF_BUFFTOOSMALL;
  *puCurDirLen = wTemp + 1;

  if (lpszDestDir)
    {
      if (wFlags & VFFF_ISSHAREDFILE)
        {
          if (!wSharedDirLen)
            {
              if ((wWinLen = (WORD)wcslen(lpszWinDir)) &&
                    *(lpszWinDir-1)==TEXT('\\'))
                {
                  if (szSysDir[wWinLen-1] == TEXT('\\'))
                      goto doCompare;
                }
              else if (szSysDir[wWinLen] == TEXT('\\'))
                {
doCompare:
                  cTemp = szSysDir[wWinLen];
                  szSysDir[wWinLen] = 0;
                  nRet = _wcsicmp(lpszWinDir, szSysDir);
                  szSysDir[wWinLen] = cTemp;
                  if(nRet)
                      goto doCopyWinDir;
                  wcscpy(gszSharedDir, szSysDir);
                }
              else
                {
doCopyWinDir:
                  wcscpy(gszSharedDir, lpszWinDir);
                }
              wSharedDirLen = (WORD)wcslen(gszSharedDir);
            }

          wDestLen = wSharedDirLen;
          lpszDir = gszSharedDir;
        }
      else
        {
          wDestLen = (WORD)wcslen(lpszAppDir);
          lpszDir = lpszAppDir;
        }

      if (*puDestDirLen > wDestLen)
        {
          wcscpy(lpszDestDir, lpszDir);

          if ((wWinLen = (WORD)wcslen(lpszDestDir)) &&
                *(lpszDestDir-1)==TEXT('\\'))
              lpszDestDir[wWinLen-1] = 0;

          if (_wcsicmp(lpszCurDir, lpszDestDir))
              wRetVal |= VFF_CURNEDEST;
        }
      else
          wRetVal |= VFF_BUFFTOOSMALL;
      *puDestDirLen = wDestLen + 1;
    }

  if (*szCurDir)
    {
      MakeFileName(szSysDir, szCurDir, lpszFileName, ARRAYSIZE(szSysDir));
      if (FileInUse(szSysDir, lpszFileName))
          wRetVal |= VFF_FILEINUSE;
    }

  return(wRetVal);
}


/*
 *  DWORD
 *  APIENTRY
 *  VerLanguageNameW(
 *      DWORD wLang,
 *      LPWSTR szLang,
 *      DWORD wSize)
 *
 *  This routine was moved to NLSLIB.LIB so that it uses the WINNLS.RC file.
 *  NLSLIB.LIB is part of KERNEL32.DLL.
 */