// Windows/FileDir.cpp

#include "StdAfx.h"

#ifndef _UNICODE
#include "../Common/StringConvert.h"
#endif

#include "FileDir.h"
#include "FileFind.h"
#include "FileName.h"

using namespace NWindows;
using namespace NFile;
using namespace NName;

#include "../Common/StringConvert.h"
#include "../Common/IntToString.h"

#define NEED_NAME_WINDOWS_TO_UNIX
#include "myPrivate.h"
#include "Windows/Synchronization.h"

#include <errno.h>

#include <glib/gstdio.h>
#include <sys/types.h>
#include <fcntl.h>

#include <utime.h>

// #define TRACEN(u) u;
#define TRACEN(u)  /* */

int g_filedir = 1;

static NWindows::NSynchronization::CCriticalSection g_CountCriticalSection;

class Umask
{
  public:
  mode_t  current_umask;
  mode_t  mask;
  Umask() {
    current_umask = umask (0);  /* get and set the umask */
    umask(current_umask);   /* restore the umask */
    mask = 0777 & (~current_umask);
  }
};

static Umask gbl_umask;
extern BOOLEAN WINAPI RtlTimeToSecondsSince1970( const LARGE_INTEGER *Time, DWORD *Seconds );


#ifdef _UNICODE
AString nameWindowToUnix2(LPCWSTR name) // FIXME : optimization ?
{
   AString astr = UnicodeStringToMultiByte(name);
   return AString(nameWindowToUnix((const char *)astr));
}
#endif

DWORD WINAPI GetFullPathNameW( LPCTSTR name, DWORD len, LPTSTR buffer, LPTSTR *lastpart ) { // FIXME
  if (name == 0) return 0;

  DWORD name_len = lstrlen(name);

  if (name[0] == '/') {
    DWORD ret = name_len+2;
    if (ret >= len) {
      TRACEN((printf("GetFullPathNameA(%ls,%d,)=0000 (case 0)\n",name, (int)len)))
      return 0;
    }
    lstrcpy(buffer,L"c:");
    lstrcat(buffer,name);

    *lastpart=buffer;
    TCHAR *ptr=buffer;
    while (*ptr) {
      if (*ptr == '/')
        *lastpart=ptr+1;
      ptr++;
    }
    TRACEN((printf("GetFullPathNameA(%ls,%d,%ls,%ls)=%d\n",name, (int)len,buffer, *lastpart,(int)ret)))
    return ret;
  }
  if (isascii(name[0]) && (name[1] == ':')) { // FIXME isascii
    DWORD ret = name_len;
    if (ret >= len) {
      TRACEN((printf("GetFullPathNameA(%ls,%d,)=0000 (case 1)\n",name, (int)len)))
      return 0;
    }
    lstrcpy(buffer,name);

    *lastpart=buffer;
    TCHAR *ptr=buffer;
    while (*ptr) {
      if (*ptr == '/')
        *lastpart=ptr+1;
      ptr++;
    }
    TRACEN((printf("GetFullPathNameA(%ls,%d,%ls,%ls)=%d\n",name, (int)len,buffer, *lastpart,(int)ret)))
    return ret;
  }

  // name is a relative pathname.
  //
  if (len < 2) {
    TRACEN((printf("GetFullPathNameA(%ls,%d,)=0000 (case 2)\n",name, (int)len)))
    return 0;
  }

  DWORD ret = 0;
  char begin[MAX_PATHNAME_LEN];
  /* DWORD begin_len = GetCurrentDirectoryA(MAX_PATHNAME_LEN,begin); */
  DWORD begin_len = 0;
  begin[0]='c';
  begin[1]=':';
  char * cret = getcwd(begin+2, MAX_PATHNAME_LEN - 3);
  if (cret) {
    begin_len = strlen(begin);
  }

  if (begin_len >= 1) {
    //    strlen(begin) + strlen("/") + strlen(name)
    ret = begin_len     +    1        + name_len;

    if (ret >= len) {
      TRACEN((printf("GetFullPathNameA(%ls,%d,)=0000 (case 4)\n",name, (int)len)))
      return 0;
    }
    UString wbegin = GetUnicodeString(begin);
    lstrcpy(buffer,wbegin);
    lstrcat(buffer,L"/");
    lstrcat(buffer,name);

    *lastpart=buffer + begin_len + 1;
    TCHAR *ptr=buffer;
    while (*ptr) {
      if (*ptr == '/')
        *lastpart=ptr+1;
      ptr++;
    }
    TRACEN((printf("GetFullPathNameA(%ls,%d,%ls,%ls)=%d\n",name, (int)len,buffer, *lastpart,(int)ret)))
  } else {
    ret = 0;
    TRACEN((printf("GetFullPathNameA(%ls,%d,)=0000 (case 5)\n",name, (int)len)))
  }
  return ret;
}

static int copy_fd(int fin,int fout)
{
  char buffer[16384];
  ssize_t ret_in;
  ssize_t ret_out;

  do {
    ret_out = -1;
    do {
      ret_in = read(fin, buffer,sizeof(buffer));
    } while (ret_in < 0 && (errno == EINTR));
    if (ret_in >= 1) {
      do {
        ret_out = write (fout, buffer, ret_in);
      } while (ret_out < 0 && (errno == EINTR));
    } else if (ret_in == 0) {
      ret_out = 0;
    }
  } while (ret_out >= 1);
  return ret_out;
}

static BOOL CopyFile(const char *src,const char *dst)
{
  int ret = -1;

#ifdef O_BINARY
  int   flags = O_BINARY;
#else
  int   flags = 0;
#endif

#ifdef O_LARGEFILE
  flags |= O_LARGEFILE;
#endif

  // printf("##DBG CopyFile(%s,%s)\n",src,dst);
  int fout = g_open(dst,O_CREAT | O_WRONLY | O_EXCL | flags, 0600);
  if (fout != -1)
  {
    int fin = g_open(src,O_RDONLY | flags , 0600);
    if (fin != -1)
    {
      ret = copy_fd(fin,fout);
      if (ret == 0) ret = close(fin);
      else                close(fin);
    }
    if (ret == 0) ret = close(fout);
    else                close(fout);
  }
  if (ret == 0) return TRUE;
  return FALSE;
}


#ifndef _UNICODE
extern bool g_IsNT;
#endif

namespace NWindows {
namespace NFile {

// SetCurrentDirectory doesn't support \\?\ prefix

#ifdef WIN_LONG_PATH
bool GetLongPathBase(CFSTR fileName, UString &res);
bool GetLongPath(CFSTR fileName, UString &res);
#endif

namespace NDir {

#ifndef _LIB_FOR_CHERRYTREE
bool SetDirTime(CFSTR fileName, const FILETIME * /* cTime */ , const FILETIME *aTime, const FILETIME *mTime)
{
  AString  cfilename = UnicodeStringToMultiByte(fileName);
  const char * unix_filename = nameWindowToUnix((const char *)cfilename);

  struct utimbuf buf;

  GStatBuf oldbuf;
  int ret = g_stat(unix_filename,&oldbuf);
  if (ret == 0) {
    buf.actime  = oldbuf.st_atime;
    buf.modtime = oldbuf.st_mtime;
  } else {
    time_t current_time = time(0);
    buf.actime  = current_time;
    buf.modtime = current_time;
  }

  if (aTime)
  {
    LARGE_INTEGER  ltime;
    DWORD dw;
    ltime.QuadPart = aTime->dwHighDateTime;
    ltime.QuadPart = (ltime.QuadPart << 32) | aTime->dwLowDateTime;
    RtlTimeToSecondsSince1970( &ltime, &dw );
    buf.actime = dw;
  }

  if (mTime)
  {
    LARGE_INTEGER  ltime;
    DWORD dw;
    ltime.QuadPart = mTime->dwHighDateTime;
    ltime.QuadPart = (ltime.QuadPart << 32) | mTime->dwLowDateTime;
    RtlTimeToSecondsSince1970( &ltime, &dw );
    buf.modtime = dw;
  }

  /* ret = */ g_utime(unix_filename, &buf);

  return true;
}
#endif // _LIB_FOR_CHERRYTREE

#ifdef WIN_LONG_PATH
bool GetLongPaths(CFSTR s1, CFSTR s2, UString &d1, UString &d2)
{
  if (!GetLongPathBase(s1, d1) ||
      !GetLongPathBase(s2, d2))
    return false;
  if (d1.IsEmpty() && d2.IsEmpty())
    return false;
  if (d1.IsEmpty()) d1 = fs2us(s1);
  if (d2.IsEmpty()) d2 = fs2us(s2);
  return true;
}
#endif

#ifndef _LIB_FOR_CHERRYTREE
static int convert_to_symlink(const char * name) {
  TRACEN(printf("LINK(%s)\n",name))
  FILE *file = g_fopen(name,"rb");
  if (file) {
    char buf[MAX_PATHNAME_LEN+1];
    char * ret = fgets(buf,sizeof(buf)-1,file);
    fclose(file);
    if (ret) {
      int ir = g_unlink(name);
      if (ir == 0) {
        ir = symlink(buf,name);
        TRACEN(printf("TO(%s)\n",buf))
      }
      return ir;
    }
  }
  return -1;
}

bool SetFileAttrib(CFSTR fileName, DWORD fileAttributes,CObjectVector<CDelayedSymLink> *delayedSymLinks)
{
  if (!fileName) {
    SetLastError(ERROR_PATH_NOT_FOUND);
    TRACEN((printf("SetFileAttrib(NULL,%d) : false-1\n",fileAttributes)))
    return false;
  }
#ifdef _UNICODE
  AString name = nameWindowToUnix2(fileName);
#else
  const char * name = nameWindowToUnix(fileName);
#endif
  GStatBuf stat_info;
#ifdef ENV_HAVE_LSTAT
  if (global_use_lstat) {
    if(g_lstat(name,&stat_info)!=0) {
      TRACEN((printf("SetFileAttrib(%s,%d) : false-2-1\n",(const char *)name,fileAttributes)))
      return false;
    }
  } else
#endif
  {
    if(g_stat(name,&stat_info)!=0) {
      TRACEN((printf("SetFileAttrib(%s,%d) : false-2-2\n",(const char *)name,fileAttributes)))
      return false;
    }
  }

  if (fileAttributes & FILE_ATTRIBUTE_UNIX_EXTENSION) {
     stat_info.st_mode = fileAttributes >> 16;
#ifdef ENV_HAVE_LSTAT
     if (S_ISLNK(stat_info.st_mode)) {
         if (delayedSymLinks) {
           delayedSymLinks->Add(CDelayedSymLink(name));
         } else if ( convert_to_symlink(name) != 0) {
          TRACEN((printf("SetFileAttrib(%s,%d) : false-3\n",(const char *)name,fileAttributes)))
          return false;
        }
     } else
#endif
     if (S_ISREG(stat_info.st_mode)) {
       TRACEN((printf("##DBG chmod-2(%s,%o)\n",(const char *)name,(unsigned)stat_info.st_mode & gbl_umask.mask)))
       g_chmod(name,stat_info.st_mode & gbl_umask.mask);
     } else if (S_ISDIR(stat_info.st_mode)) {
       // user/7za must be able to create files in this directory
       stat_info.st_mode |= (S_IRUSR | S_IWUSR | S_IXUSR);
       TRACEN((printf("##DBG chmod-3(%s,%o)\n",(const char *)name,(unsigned)stat_info.st_mode & gbl_umask.mask)))
       g_chmod(name,stat_info.st_mode & gbl_umask.mask);
     }
#ifdef ENV_HAVE_LSTAT
  } else if (!S_ISLNK(stat_info.st_mode)) {
    // do not use chmod on a link
#else
  } else {
#endif

    /* Only Windows Attributes */
    if( S_ISDIR(stat_info.st_mode)) {
       /* Remark : FILE_ATTRIBUTE_READONLY ignored for directory. */
       TRACEN((printf("##DBG chmod-4(%s,%o)\n",(const char *)name,(unsigned)stat_info.st_mode & gbl_umask.mask)))
       g_chmod(name,stat_info.st_mode & gbl_umask.mask);
    } else {
       if (fileAttributes & FILE_ATTRIBUTE_READONLY) stat_info.st_mode &= ~0222; /* octal!, clear write permission bits */
       TRACEN((printf("##DBG chmod-5(%s,%o)\n",(const char *)name,(unsigned)stat_info.st_mode & gbl_umask.mask)))
       g_chmod(name,stat_info.st_mode & gbl_umask.mask);
    }
  }
  TRACEN((printf("SetFileAttrib(%s,%d) : true\n",(const char *)name,fileAttributes)))

  return true;
}
#endif // _LIB_FOR_CHERRYTREE

bool RemoveDir(CFSTR path)
{
  if (!path || !*path) {
    SetLastError(ERROR_PATH_NOT_FOUND);
    return FALSE;
  }
  AString name = nameWindowToUnix2(path);
  TRACEN((printf("RemoveDirectoryA(%s)\n",(const char *)name)))

  if (g_rmdir ( (const char *)name ) != 0) {
    return FALSE;
  }
  return TRUE;
}

bool MyMoveFile(CFSTR existFileName, CFSTR newFileName)
{
#ifdef _UNICODE
  AString src = nameWindowToUnix2(existFileName);
  AString dst = nameWindowToUnix2(newFileName);
#else
  const char * src = nameWindowToUnix(existFileName);
  const char * dst = nameWindowToUnix(newFileName);
#endif

  TRACEN((printf("MyMoveFile(%s,%s)\n",(const char *)src,(const char *)dst)))

  int ret = g_rename(src,dst);
  if (ret != 0)
  {
#ifndef _LIB_FOR_CHERRYTREE
    if (errno == EXDEV) // FIXED : bug #1112167 (Temporary directory must be on same partition as target)
    {
      BOOL bret = CopyFile(src,dst);
      if (bret == FALSE) return false;

      GStatBuf info_file;
      ret = g_stat(src,&info_file);
      if (ret == 0) {
    TRACEN((printf("##DBG chmod-1(%s,%o)\n",(const char *)dst,(unsigned)info_file.st_mode & gbl_umask.mask)))
        ret = g_chmod(dst,info_file.st_mode & gbl_umask.mask);
      }
      if (ret == 0) {
         ret = g_unlink(src);
      }
      if (ret == 0) return true;
    }
#endif // _LIB_FOR_CHERRYTREE
    return false;
  }
  return true;
}

bool CreateDir(CFSTR path)
{
  if (!path || !*path) {
    SetLastError(ERROR_PATH_NOT_FOUND);
    return false;
  }

#ifdef _UNICODE
  AString name = nameWindowToUnix2(path);
#else
  const char * name = nameWindowToUnix(path);
#endif
  bool bret = false;
  if (g_mkdir( name, 0700 ) == 0) bret = true;
  TRACEN((printf("CreateDir(%s)=%d\n",(const char *)name,(int)bret)))
  return bret;
}

bool CreateComplexDir(CFSTR _aPathName)
{
  AString name = nameWindowToUnix2(_aPathName);
  TRACEN((printf("CreateComplexDir(%s)\n",(const char *)name)))


  FString pathName = _aPathName;
  int pos = pathName.ReverseFind(FCHAR_PATH_SEPARATOR);
  if (pos > 0 && pos == pathName.Len() - 1)
  {
    if (pathName.Len() == 3 && pathName[1] == L':')
      return true; // Disk folder;
    pathName.Delete(pos);
  }
  FString pathName2 = pathName;
  pos = pathName.Len();
  TRACEN((printf("CreateComplexDir(%s) pathName2=%ls\n",(const char *)name,(CFSTR)pathName2)))
  for (;;)
  {
    if (CreateDir(pathName))
      break;
    TRACEN((printf("CreateComplexDir(%s) GetLastError=%d (ERROR_ALREADY_EXISTS=%d)\n",(const char *)name,::GetLastError(), ERROR_ALREADY_EXISTS)))
    if (::GetLastError() == ERROR_ALREADY_EXISTS)
    {
      break;
    }
    pos = pathName.ReverseFind(FCHAR_PATH_SEPARATOR);
    if (pos < 0 || pos == 0)
      return false;
    if (pathName[pos - 1] == L':')
      return false;
    pathName = pathName.Left(pos);
  }
  pathName = pathName2;
  while (pos < pathName.Len())
  {
    pos = pathName.Find(FCHAR_PATH_SEPARATOR, pos + 1);
    if (pos < 0)
      pos = pathName.Len();
    if (!CreateDir(pathName.Left(pos)))
      return false;
  }
  return true;
}

bool DeleteFileAlways(CFSTR name)
{
  if (!name || !*name) {
    SetLastError(ERROR_PATH_NOT_FOUND);
    return false;
  }
#ifdef _UNICODE
   AString unixname = nameWindowToUnix2(name);
#else
   const char * unixname = nameWindowToUnix(name);
#endif
   bool bret = false;
   if (g_remove(unixname) == 0) bret = true;
   TRACEN((printf("DeleteFileAlways(%s)=%d\n",(const char *)unixname,(int)bret)))
   return bret;
}

bool RemoveDirWithSubItems(const FString &path)
{
  bool needRemoveSubItems = true;
  {
    NFind::CFileInfo fi;
    if (!fi.Find(path))
      return false;
    if (!fi.IsDir())
    {
      ::SetLastError(ERROR_DIRECTORY);
      return false;
    }
    if (fi.HasReparsePoint())
      needRemoveSubItems = false;
  }

  if (needRemoveSubItems)
  {
    FString s = path;
    s += FCHAR_PATH_SEPARATOR;
    unsigned prefixSize = s.Len();
    s += FCHAR_ANY_MASK;
    NFind::CEnumerator enumerator(s);
    NFind::CFileInfo fi;
    while (enumerator.Next(fi))
    {
      s.DeleteFrom(prefixSize);
      s += fi.Name;
      if (fi.IsDir())
      {
        if (!RemoveDirWithSubItems(s))
          return false;
      }
      else if (!DeleteFileAlways(s))
        return false;
    }
  }

#ifndef _LIB_FOR_CHERRYTREE
  if (!SetFileAttrib(path, 0))
    return false;
#endif // _LIB_FOR_CHERRYTREE
  return RemoveDir(path);
}


bool RemoveDirectoryWithSubItems(const FString &path); // FIXME
static bool RemoveDirectorySubItems2(const FString pathPrefix, const NFind::CFileInfo &fileInfo)
{
  if (fileInfo.IsDir())
    return RemoveDirectoryWithSubItems(pathPrefix + fileInfo.Name);
  return DeleteFileAlways(pathPrefix + fileInfo.Name);
}
bool RemoveDirectoryWithSubItems(const FString &path)
{
  NFind::CFileInfo fileInfo;
  FString pathPrefix = path + FCHAR_PATH_SEPARATOR;
  {
    NFind::CEnumerator enumerator(pathPrefix + FCHAR_ANY_MASK);
    while (enumerator.Next(fileInfo))
      if (!RemoveDirectorySubItems2(pathPrefix, fileInfo))
        return false;
  }
#ifndef _LIB_FOR_CHERRYTREE
  if (!SetFileAttrib(path, 0))
    return false;
#endif // _LIB_FOR_CHERRYTREE
  return RemoveDir(path);
}

#ifdef UNDER_CE

bool MyGetFullPathName(CFSTR fileName, FString &resFullPath)
{
  resFullPath = fileName;
  return true;
}

#else

bool MyGetFullPathName(CFSTR path, FString &resFullPath)
{
  return GetFullPath(path, resFullPath);
}

bool SetCurrentDir(CFSTR path)
{
   AString apath = UnicodeStringToMultiByte(path);

   return g_chdir((const char*)apath) == 0;
}

bool GetCurrentDir(FString &path)
{
  char begin[MAX_PATHNAME_LEN];
  begin[0]='c';
  begin[1]=':';
  char * cret = getcwd(begin+2, MAX_PATHNAME_LEN - 3);
  if (cret)
  {
#ifdef _UNICODE
    path = GetUnicodeString(begin);
#else
    path = begin;
#endif
    return true;
  }
  return false;
}

#endif

bool GetFullPathAndSplit(CFSTR path, FString &resDirPrefix, FString &resFileName)
{
  bool res = MyGetFullPathName(path, resDirPrefix);
  if (!res)
    resDirPrefix = path;
  int pos = resDirPrefix.ReverseFind(FCHAR_PATH_SEPARATOR);
  resFileName = resDirPrefix.Ptr(pos + 1);
  resDirPrefix.DeleteFrom(pos + 1);
  return res;
}

bool GetOnlyDirPrefix(CFSTR path, FString &resDirPrefix)
{
  FString resFileName;
  return GetFullPathAndSplit(path, resDirPrefix, resFileName);
}

bool MyGetTempPath(FString &path)
{
  path = L"c:/tmp/"; // final '/' is needed
  return true;
}

static bool CreateTempFile(CFSTR prefix, bool addRandom, FString &path, NIO::COutFile *outFile)
{
  static UInt32 memo_count = 0;
  UInt32 count;

  g_CountCriticalSection.Enter();
  count = memo_count++;
  g_CountCriticalSection.Leave();
  UINT number = (UINT)getpid();

  UInt32 d = (GetTickCount() << 12) ^ (count << 14) ^ number;

  for (unsigned i = 0; i < 100; i++)
  {
    path = prefix;
    if (addRandom)
    {
      FChar s[16];
      UInt32 value = d;
      unsigned k;
      for (k = 0; k < 8; k++)
      {
        unsigned t = value & 0xF;
        value >>= 4;
        s[k] = (char)((t < 10) ? ('0' + t) : ('A' + (t - 10)));
      }
      s[k] = '\0';
      if (outFile)
        path += FChar('.');
      path += s;
      UInt32 step = GetTickCount() + 2;
      if (step == 0)
        step = 1;
      d += step;
    }
    addRandom = true;
    if (outFile)
      path += FTEXT(".tmp");
    if (NFind::DoesFileOrDirExist(path))
    {
      SetLastError(ERROR_ALREADY_EXISTS);
      continue;
    }
    if (outFile)
    {
      if (outFile->Create(path, false))
        return true;
    }
    else
    {
      if (CreateDir(path))
        return true;
    }
    DWORD error = GetLastError();
    if (error != ERROR_FILE_EXISTS &&
        error != ERROR_ALREADY_EXISTS)
      break;
  }
  path.Empty();
  return false;
}

bool CTempFile::Create(CFSTR prefix, NIO::COutFile *outFile)
{
  if (!Remove())
    return false;
  if (!CreateTempFile(prefix, false, _path, outFile))
    return false;
  _mustBeDeleted = true;
  return true;
}

bool CTempFile::CreateRandomInTempFolder(CFSTR namePrefix, NIO::COutFile *outFile)
{
  if (!Remove())
    return false;
  FString tempPath;
  if (!MyGetTempPath(tempPath))
    return false;
  if (!CreateTempFile(tempPath + namePrefix, true, _path, outFile))
    return false;
  _mustBeDeleted = true;
  return true;
}

bool CTempFile::Remove()
{
  if (!_mustBeDeleted)
    return true;
  _mustBeDeleted = !DeleteFileAlways(_path);
  return !_mustBeDeleted;
}

bool CTempFile::MoveTo(CFSTR name, bool deleteDestBefore)
{
  if (deleteDestBefore)
    if (NFind::DoesFileExist(name))
      if (!DeleteFileAlways(name))
        return false;
  DisableDeleting();
  return MyMoveFile(_path, name);
}

bool CTempDir::Create(CFSTR prefix)
{
  if (!Remove())
    return false;
  FString tempPath;
  if (!MyGetTempPath(tempPath))
    return false;
  if (!CreateTempFile(tempPath + prefix, true, _path, NULL))
    return false;
  _mustBeDeleted = true;
  return true;
}

bool CTempDir::Remove()
{
  if (!_mustBeDeleted)
    return true;
  _mustBeDeleted = !RemoveDirectoryWithSubItems(_path);
  return !_mustBeDeleted;
}

#ifdef ENV_UNIX

CDelayedSymLink::CDelayedSymLink(const char * source)
  : _source(source)
{
  GStatBuf st;

  if (g_lstat(_source, &st) == 0) {
    _dev = st.st_dev;
    _ino = st.st_ino;
  } else {
    _dev = 0;
  }
}

bool CDelayedSymLink::Create()
{
  GStatBuf st;

  if (_dev == 0) {
    errno = EPERM;
    return false;
  }
  if (g_lstat(_source, &st) != 0)
    return false;
  if (_dev != st.st_dev || _ino != st.st_ino) {
    // Placeholder file has been overwritten or moved by another
    // symbolic link creation
    errno = EPERM;
    return false;
  }

  return convert_to_symlink(_source) == 0;
}

#endif // ENV_UNIX

}}}

bool cherrytree_remove_dir_with_subs(const char* path)
{
    UString dir = MultiByteToUnicodeString(path, CP_ACP);
    return NWindows::NFile::NDir::RemoveDirWithSubItems(dir);
}

namespace NWindows {
namespace NDLL {

FString GetModuleDirPrefix()
{
  FString s;

  const char *p7zip_home_dir = getenv("P7ZIP_HOME_DIR");
  if (p7zip_home_dir) {
    return MultiByteToUnicodeString(p7zip_home_dir,CP_ACP);
  }

  return FTEXT(".") FSTRING_PATH_SEPARATOR;
}

}}
