p7zip-rar/CPP/7zip/UI/FileManager/FSFolder.cpp
2017-10-11 12:40:22 +02:00

1111 lines
26 KiB
C++

// FSFolder.cpp
#include "StdAfx.h"
#include "../../../Common/ComTry.h"
#include "../../../Common/Defs.h"
#include "../../../Common/StringConvert.h"
#include "../../../Common/UTFConvert.h"
#include "../../../Windows/FileDir.h"
#include "../../../Windows/FileIO.h"
#include "../../../Windows/FileName.h"
#include "../../../Windows/PropVariant.h"
#include "../../PropID.h"
#include "FSDrives.h"
#include "FSFolder.h"
#if 0 // #ifndef UNDER_CE
#include "NetFolder.h"
#endif
#include "SysIconUtils.h"
#if _WIN32_WINNT < 0x0501
#ifdef _APISETFILE_
// Windows SDK 8.1 defines in fileapi.h the function GetCompressedFileSizeW only if _WIN32_WINNT >= 0x0501
// But real support version for that function is NT 3.1 (probably)
// So we must define GetCompressedFileSizeW
EXTERN_C_BEGIN
WINBASEAPI DWORD WINAPI GetCompressedFileSizeW(LPCWSTR lpFileName, LPDWORD lpFileSizeHigh);
EXTERN_C_END
#endif
#endif
using namespace NWindows;
using namespace NFile;
using namespace NFind;
using namespace NDir;
using namespace NName;
#ifndef USE_UNICODE_FSTRING
int CompareFileNames_ForFolderList(const FChar *s1, const FChar *s2)
{
return CompareFileNames_ForFolderList(fs2us(s1), fs2us(s2));
}
#endif
namespace NFsFolder {
static const Byte kProps[] =
{
kpidName,
kpidSize,
kpidMTime,
kpidCTime,
kpidATime,
kpidAttrib,
kpidPackSize,
#ifdef FS_SHOW_LINKS_INFO
kpidINode,
kpidLinks,
#endif
kpidComment,
kpidNumSubDirs,
kpidNumSubFiles,
kpidPrefix
};
HRESULT CFSFolder::Init(const FString &path /* , IFolderFolder *parentFolder */)
{
// _parentFolder = parentFolder;
_path = path;
#ifdef _WIN32
_findChangeNotification.FindFirst(_path, false,
FILE_NOTIFY_CHANGE_FILE_NAME
| FILE_NOTIFY_CHANGE_DIR_NAME
| FILE_NOTIFY_CHANGE_ATTRIBUTES
| FILE_NOTIFY_CHANGE_SIZE
| FILE_NOTIFY_CHANGE_LAST_WRITE
/*
| FILE_NOTIFY_CHANGE_LAST_ACCESS
| FILE_NOTIFY_CHANGE_CREATION
| FILE_NOTIFY_CHANGE_SECURITY
*/
);
if (!_findChangeNotification.IsHandleAllocated())
{
DWORD lastError = GetLastError();
CFindFile findFile;
CFileInfo fi;
if (!findFile.FindFirst(_path + FCHAR_ANY_MASK, fi))
return lastError;
}
#endif
return S_OK;
}
HRESULT CFsFolderStat::Enumerate()
{
if (Progress)
{
RINOK(Progress->SetCompleted(NULL));
}
Path.Add_PathSepar();
unsigned len = Path.Len();
Path += FCHAR_ANY_MASK;
CEnumerator enumerator(Path);
CFileInfo fi;
while (enumerator.Next(fi))
{
if (fi.IsDir())
{
Path.DeleteFrom(len);
Path += fi.Name;
RINOK(Enumerate());
NumFolders++;
}
else
{
NumFiles++;
Size += fi.Size;
}
}
return S_OK;
}
#if 0 // FIXME #ifndef UNDER_CE
bool MyGetCompressedFileSizeW(CFSTR path, UInt64 &size)
{
DWORD highPart;
DWORD lowPart = INVALID_FILE_SIZE;
IF_USE_MAIN_PATH
{
lowPart = ::GetCompressedFileSizeW(fs2us(path), &highPart);
if (lowPart != INVALID_FILE_SIZE || ::GetLastError() == NO_ERROR)
{
size = ((UInt64)highPart << 32) | lowPart;
return true;
}
}
#ifdef WIN_LONG_PATH
if (USE_SUPER_PATH)
{
UString superPath;
if (GetSuperPath(path, superPath, USE_MAIN_PATH))
{
lowPart = ::GetCompressedFileSizeW(superPath, &highPart);
if (lowPart != INVALID_FILE_SIZE || ::GetLastError() == NO_ERROR)
{
size = ((UInt64)highPart << 32) | lowPart;
return true;
}
}
}
#endif
return false;
}
#endif
HRESULT CFSFolder::LoadSubItems(int dirItem, const FString &relPrefix)
{
unsigned startIndex = Folders.Size();
{
CEnumerator enumerator(_path + relPrefix + FCHAR_ANY_MASK);
CDirItem fi;
fi.FolderStat_Defined = false;
fi.NumFolders = 0;
fi.NumFiles = 0;
fi.Parent = dirItem;
while (enumerator.Next(fi))
{
if (fi.IsDir())
{
fi.Size = 0;
if (_flatMode)
Folders.Add(relPrefix + fi.Name + FCHAR_PATH_SEPARATOR);
}
else
{
/*
fi.PackSize_Defined = true;
if (!MyGetCompressedFileSizeW(_path + relPrefix + fi.Name, fi.PackSize))
fi.PackSize = fi.Size;
*/
}
#if 0 // FIXME #ifndef UNDER_CE
fi.Reparse.Free();
fi.PackSize_Defined = false;
#ifdef FS_SHOW_LINKS_INFO
fi.FileInfo_Defined = false;
fi.FileInfo_WasRequested = false;
fi.FileIndex = 0;
fi.NumLinks = 0;
#endif
fi.PackSize = fi.Size;
if (fi.HasReparsePoint())
{
fi.FileInfo_WasRequested = true;
BY_HANDLE_FILE_INFORMATION info;
NIO::GetReparseData(_path + relPrefix + fi.Name, fi.Reparse, &info);
fi.NumLinks = info.nNumberOfLinks;
fi.FileIndex = (((UInt64)info.nFileIndexHigh) << 32) + info.nFileIndexLow;
fi.FileInfo_Defined = true;
}
#endif
/* unsigned fileIndex = */ Files.Add(fi);
#if defined(_WIN32) && !defined(UNDER_CE)
/*
if (_scanAltStreams)
{
CStreamEnumerator enumerator(_path + relPrefix + fi.Name);
CStreamInfo si;
for (;;)
{
bool found;
if (!enumerator.Next(si, found))
{
// if (GetLastError() == ERROR_ACCESS_DENIED)
// break;
// return E_FAIL;
break;
}
if (!found)
break;
if (si.IsMainStream())
continue;
CAltStream ss;
ss.Parent = fileIndex;
ss.Name = si.GetReducedName();
ss.Size = si.Size;
ss.PackSize_Defined = false;
ss.PackSize = si.Size;
Streams.Add(ss);
}
}
*/
#endif
}
}
if (!_flatMode)
return S_OK;
unsigned endIndex = Folders.Size();
for (unsigned i = startIndex; i < endIndex; i++)
LoadSubItems(i, Folders[i]);
return S_OK;
}
STDMETHODIMP CFSFolder::LoadItems()
{
Int32 dummy;
WasChanged(&dummy);
Clear();
RINOK(LoadSubItems(-1, FString()));
_commentsAreLoaded = false;
return S_OK;
}
static CFSTR kDescriptionFileName = FTEXT("descript.ion");
bool CFSFolder::LoadComments()
{
_comments.Clear();
_commentsAreLoaded = true;
NIO::CInFile file;
if (!file.Open(_path + kDescriptionFileName))
return false;
UInt64 len;
if (!file.GetLength(len))
return false;
if (len >= (1 << 28))
return false;
AString s;
char *p = s.GetBuf((unsigned)(size_t)len);
UInt32 processedSize;
file.Read(p, (UInt32)len, processedSize);
s.ReleaseBuf_CalcLen((unsigned)(size_t)len);
if (processedSize != len)
return false;
file.Close();
UString unicodeString;
if (!ConvertUTF8ToUnicode(s, unicodeString))
return false;
return _comments.ReadFromString(unicodeString);
}
bool CFSFolder::SaveComments()
{
AString utf;
{
UString unicode;
_comments.SaveToString(unicode);
ConvertUnicodeToUTF8(unicode, utf);
}
if (!utf.IsAscii())
utf.Insert(0, "\xEF\xBB\xBF" "\r\n");
FString path = _path + kDescriptionFileName;
// We must set same attrib. COutFile::CreateAlways can fail, if file has another attrib.
DWORD attrib = FILE_ATTRIBUTE_NORMAL;
{
CFileInfo fi;
if (fi.Find(path))
attrib = fi.Attrib;
}
NIO::COutFile file;
if (!file.CreateAlways(path, attrib))
return false;
UInt32 processed;
file.Write(utf, utf.Len(), processed);
_commentsAreLoaded = false;
return true;
}
STDMETHODIMP CFSFolder::GetNumberOfItems(UInt32 *numItems)
{
*numItems = Files.Size() /* + Streams.Size() */;
return S_OK;
}
#ifdef USE_UNICODE_FSTRING
STDMETHODIMP CFSFolder::GetItemPrefix(UInt32 index, const wchar_t **name, unsigned *len)
{
*name = 0;
*len = 0;
/*
if (index >= Files.Size())
index = Streams[index - Files.Size()].Parent;
*/
CDirItem &fi = Files[index];
if (fi.Parent >= 0)
{
const FString &fo = Folders[fi.Parent];
USE_UNICODE_FSTRING
*name = fo;
*len = fo.Len();
}
return S_OK;
}
STDMETHODIMP CFSFolder::GetItemName(UInt32 index, const wchar_t **name, unsigned *len)
{
*name = 0;
*len = 0;
if (index < Files.Size())
{
CDirItem &fi = Files[index];
*name = fi.Name;
*len = fi.Name.Len();
return S_OK;
}
else
{
// const CAltStream &ss = Streams[index - Files.Size()];
// *name = ss.Name;
// *len = ss.Name.Len();
//
// change it;
}
return S_OK;
}
STDMETHODIMP_(UInt64) CFSFolder::GetItemSize(UInt32 index)
{
/*
if (index >= Files.Size())
return Streams[index - Files.Size()].Size;
*/
CDirItem &fi = Files[index];
return fi.IsDir() ? 0 : fi.Size;
}
#endif
#ifdef FS_SHOW_LINKS_INFO
bool CFSFolder::ReadFileInfo(CDirItem &di)
{
di.FileInfo_WasRequested = true;
BY_HANDLE_FILE_INFORMATION info;
if (!NIO::CFileBase::GetFileInformation(_path + GetRelPath(di), &info))
return false;
di.NumLinks = info.nNumberOfLinks;
di.FileIndex = (((UInt64)info.nFileIndexHigh) << 32) + info.nFileIndexLow;
di.FileInfo_Defined = true;
return true;
}
#endif
STDMETHODIMP CFSFolder::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)
{
NCOM::CPropVariant prop;
/*
if (index >= (UInt32)Files.Size())
{
CAltStream &ss = Streams[index - Files.Size()];
CDirItem &fi = Files[ss.Parent];
switch (propID)
{
case kpidIsDir: prop = false; break;
case kpidIsAltStream: prop = true; break;
case kpidName: prop = fs2us(fi.Name) + ss.Name; break;
case kpidSize: prop = ss.Size; break;
case kpidPackSize:
#ifdef UNDER_CE
prop = ss.Size;
#else
if (!ss.PackSize_Defined)
{
ss.PackSize_Defined = true;
if (!MyGetCompressedFileSizeW(_path + GetRelPath(fi) + us2fs(ss.Name), ss.PackSize))
ss.PackSize = ss.Size;
}
prop = ss.PackSize;
#endif
break;
case kpidComment: break;
default: index = ss.Parent;
}
if (index >= (UInt32)Files.Size())
{
prop.Detach(value);
return S_OK;
}
}
*/
CDirItem &fi = Files[index];
switch (propID)
{
case kpidIsDir: prop = fi.IsDir(); break;
case kpidIsAltStream: prop = false; break;
case kpidName: prop = fs2us(fi.Name); break;
case kpidSize: if (!fi.IsDir() || fi.FolderStat_Defined) prop = fi.Size; break;
case kpidPackSize:
#if 1 // ifdef UNDER_CE
prop = fi.Size;
#else
if (!fi.PackSize_Defined)
{
fi.PackSize_Defined = true;
if (fi.IsDir () || !MyGetCompressedFileSizeW(_path + GetRelPath(fi), fi.PackSize))
fi.PackSize = fi.Size;
}
prop = fi.PackSize;
#endif
break;
#ifdef FS_SHOW_LINKS_INFO
case kpidLinks:
#ifdef UNDER_CE
// prop = fi.NumLinks;
#else
if (!fi.FileInfo_WasRequested)
ReadFileInfo(fi);
if (fi.FileInfo_Defined)
prop = fi.NumLinks;
#endif
break;
case kpidINode:
#ifdef UNDER_CE
// prop = fi.FileIndex;
#else
if (!fi.FileInfo_WasRequested)
ReadFileInfo(fi);
if (fi.FileInfo_Defined)
prop = fi.FileIndex;
#endif
break;
#endif
case kpidAttrib: prop = (UInt32)fi.Attrib; break;
case kpidCTime: prop = fi.CTime; break;
case kpidATime: prop = fi.ATime; break;
case kpidMTime: prop = fi.MTime; break;
case kpidComment:
{
if (!_commentsAreLoaded)
LoadComments();
UString comment;
if (_comments.GetValue(fs2us(GetRelPath(fi)), comment))
{
int pos = comment.Find((wchar_t)4);
if (pos >= 0)
comment.DeleteFrom(pos);
prop = comment;
}
break;
}
case kpidPrefix:
if (fi.Parent >= 0)
prop = Folders[fi.Parent];
break;
case kpidNumSubDirs: if (fi.IsDir() && fi.FolderStat_Defined) prop = fi.NumFolders; break;
case kpidNumSubFiles: if (fi.IsDir() && fi.FolderStat_Defined) prop = fi.NumFiles; break;
}
prop.Detach(value);
return S_OK;
}
// ---------- IArchiveGetRawProps ----------
STDMETHODIMP CFSFolder::GetNumRawProps(UInt32 *numProps)
{
*numProps = 1;
return S_OK;
}
STDMETHODIMP CFSFolder::GetRawPropInfo(UInt32 index, BSTR *name, PROPID *propID)
{
index = index;
*name = NULL;
*propID = kpidNtReparse;
return S_OK;
}
STDMETHODIMP CFSFolder::GetParent(UInt32 /* index */, UInt32 * /* parent */, UInt32 * /* parentType */)
{
return E_FAIL;
}
STDMETHODIMP CFSFolder::GetRawProp(UInt32
#ifndef UNDER_CE
index
#endif
, PROPID
#ifndef UNDER_CE
propID
#endif
, const void **data, UInt32 *dataSize, UInt32 *propType)
{
*data = NULL;
*dataSize = 0;
*propType = 0;
#if 0 // #ifndef UNDER_CE
if (propID == kpidNtReparse)
{
const CDirItem &fi = Files[index];
const CByteBuffer &buf = fi.Reparse;
if (buf.Size() == 0)
return S_OK;
*data = buf;
*dataSize = (UInt32)buf.Size();
*propType = NPropDataType::kRaw;
return S_OK;
}
#endif
return S_OK;
}
// returns Position of extension including '.'
static inline CFSTR GetExtensionPtr(const FString &name)
{
int dotPos = name.ReverseFind_Dot();
return name.Ptr((dotPos < 0) ? name.Len() : dotPos);
}
STDMETHODIMP_(Int32) CFSFolder::CompareItems(UInt32 index1, UInt32 index2, PROPID propID, Int32 /* propIsRaw */)
{
/*
const CAltStream *ss1 = NULL;
const CAltStream *ss2 = NULL;
if (index1 >= (UInt32)Files.Size()) { ss1 = &Streams[index1 - Files.Size()]; index1 = ss1->Parent; }
if (index2 >= (UInt32)Files.Size()) { ss2 = &Streams[index2 - Files.Size()]; index2 = ss2->Parent; }
*/
CDirItem &fi1 = Files[index1];
CDirItem &fi2 = Files[index2];
switch (propID)
{
case kpidName:
{
int comp = CompareFileNames_ForFolderList(fi1.Name, fi2.Name);
/*
if (comp != 0)
return comp;
if (!ss1)
return ss2 ? -1 : 0;
if (!ss2)
return 1;
return MyStringCompareNoCase(ss1->Name, ss2->Name);
*/
return comp;
}
case kpidSize:
return MyCompare(
/* ss1 ? ss1->Size : */ fi1.Size,
/* ss2 ? ss2->Size : */ fi2.Size);
case kpidAttrib: return MyCompare(fi1.Attrib, fi2.Attrib);
case kpidCTime: return CompareFileTime(&fi1.CTime, &fi2.CTime);
case kpidATime: return CompareFileTime(&fi1.ATime, &fi2.ATime);
case kpidMTime: return CompareFileTime(&fi1.MTime, &fi2.MTime);
case kpidIsDir:
{
bool isDir1 = /* ss1 ? false : */ fi1.IsDir();
bool isDir2 = /* ss2 ? false : */ fi2.IsDir();
if (isDir1 == isDir2)
return 0;
return isDir1 ? -1 : 1;
}
case kpidPackSize:
{
#if 1 // #ifdef UNDER_CE
return MyCompare(fi1.Size, fi2.Size);
#else
// PackSize can be undefined here
return MyCompare(
/* ss1 ? ss1->PackSize : */ fi1.PackSize,
/* ss2 ? ss2->PackSize : */ fi2.PackSize);
#endif
}
#ifdef FS_SHOW_LINKS_INFO
case kpidINode:
{
#ifndef UNDER_CE
if (!fi1.FileInfo_WasRequested) ReadFileInfo(fi1);
if (!fi2.FileInfo_WasRequested) ReadFileInfo(fi2);
return MyCompare(
fi1.FileIndex,
fi2.FileIndex);
#endif
}
case kpidLinks:
{
#ifndef UNDER_CE
if (!fi1.FileInfo_WasRequested) ReadFileInfo(fi1);
if (!fi2.FileInfo_WasRequested) ReadFileInfo(fi2);
return MyCompare(
fi1.NumLinks,
fi2.NumLinks);
#endif
}
#endif
case kpidComment:
{
// change it !
UString comment1, comment2;
_comments.GetValue(fs2us(GetRelPath(fi1)), comment1);
_comments.GetValue(fs2us(GetRelPath(fi2)), comment2);
return MyStringCompareNoCase(comment1, comment2);
}
case kpidPrefix:
if (fi1.Parent < 0) return (fi2.Parent < 0) ? 0 : -1;
if (fi2.Parent < 0) return 1;
return CompareFileNames_ForFolderList(
Folders[fi1.Parent],
Folders[fi2.Parent]);
case kpidExtension:
return CompareFileNames_ForFolderList(
GetExtensionPtr(fi1.Name),
GetExtensionPtr(fi2.Name));
}
return 0;
}
HRESULT CFSFolder::BindToFolderSpec(CFSTR name, IFolderFolder **resultFolder)
{
*resultFolder = 0;
CFSFolder *folderSpec = new CFSFolder;
CMyComPtr<IFolderFolder> subFolder = folderSpec;
RINOK(folderSpec->Init(_path + name + FCHAR_PATH_SEPARATOR));
*resultFolder = subFolder.Detach();
return S_OK;
}
/*
void CFSFolder::GetPrefix(const CDirItem &item, FString &prefix) const
{
if (item.Parent >= 0)
prefix = Folders[item.Parent];
else
prefix.Empty();
}
*/
/*
void CFSFolder::GetPrefix(const CDirItem &item, FString &prefix) const
{
int parent = item.Parent;
unsigned len = 0;
while (parent >= 0)
{
const CDirItem &cur = Files[parent];
len += cur.Name.Len() + 1;
parent = cur.Parent;
}
wchar_t *p = prefix.GetBuf_SetEnd(len) + len;
parent = item.Parent;
while (parent >= 0)
{
const CDirItem &cur = Files[parent];
*(--p) = FCHAR_PATH_SEPARATOR;
p -= cur.Name.Len();
wmemcpy(p, cur.Name, cur.Name.Len());
parent = cur.Parent;
}
}
*/
FString CFSFolder::GetRelPath(const CDirItem &item) const
{
if (item.Parent < 0)
return item.Name;
return Folders[item.Parent] + item.Name;
}
STDMETHODIMP CFSFolder::BindToFolder(UInt32 index, IFolderFolder **resultFolder)
{
*resultFolder = 0;
const CDirItem &fi = Files[index];
if (!fi.IsDir())
return E_INVALIDARG;
return BindToFolderSpec(GetRelPath(fi), resultFolder);
}
STDMETHODIMP CFSFolder::BindToFolder(const wchar_t *name, IFolderFolder **resultFolder)
{
return BindToFolderSpec(us2fs(name), resultFolder);
}
static const CFSTR kSuperPrefix = FTEXT("\\\\?\\");
STDMETHODIMP CFSFolder::BindToParentFolder(IFolderFolder **resultFolder)
{
*resultFolder = 0;
/*
if (_parentFolder)
{
CMyComPtr<IFolderFolder> parentFolder = _parentFolder;
*resultFolder = parentFolder.Detach();
return S_OK;
}
*/
if (_path.IsEmpty())
return E_INVALIDARG;
int pos = _path.ReverseFind_PathSepar();
if (pos < 0 || pos != (int)_path.Len() - 1)
return E_FAIL;
FString parentPath = _path.Left(pos);
pos = parentPath.ReverseFind_PathSepar();
if (pos < 0)
{
#ifdef UNDER_CE
*resultFolder = 0;
#else
CFSDrives *drivesFolderSpec = new CFSDrives;
CMyComPtr<IFolderFolder> drivesFolder = drivesFolderSpec;
drivesFolderSpec->Init();
*resultFolder = drivesFolder.Detach();
#endif
return S_OK;
}
/*
parentPath.DeleteFrom(pos + 1);
if (parentPath == kSuperPrefix)
{
#ifdef UNDER_CE
*resultFolder = 0;
#else
CFSDrives *drivesFolderSpec = new CFSDrives;
CMyComPtr<IFolderFolder> drivesFolder = drivesFolderSpec;
drivesFolderSpec->Init(false, true);
*resultFolder = drivesFolder.Detach();
#endif
return S_OK;
}
FString parentPathReduced = parentPath.Left(pos);
#ifndef UNDER_CE
pos = parentPathReduced.ReverseFind_PathSepar();
if (pos == 1)
{
if (!IS_PATH_SEPAR_CHAR(parentPath[0]))
return E_FAIL;
CNetFolder *netFolderSpec = new CNetFolder;
CMyComPtr<IFolderFolder> netFolder = netFolderSpec;
netFolderSpec->Init(fs2us(parentPath));
*resultFolder = netFolder.Detach();
return S_OK;
}
#endif
CFSFolder *parentFolderSpec = new CFSFolder;
CMyComPtr<IFolderFolder> parentFolder = parentFolderSpec;
RINOK(parentFolderSpec->Init(parentPath, 0));
*resultFolder = parentFolder.Detach();
*/
return S_OK;
}
STDMETHODIMP CFSFolder::GetNumberOfProperties(UInt32 *numProperties)
{
*numProperties = ARRAY_SIZE(kProps);
if (!_flatMode)
(*numProperties)--;
return S_OK;
}
STDMETHODIMP CFSFolder::GetPropertyInfo IMP_IFolderFolder_GetProp(kProps)
STDMETHODIMP CFSFolder::GetFolderProperty(PROPID propID, PROPVARIANT *value)
{
COM_TRY_BEGIN
NWindows::NCOM::CPropVariant prop;
switch (propID)
{
case kpidType: prop = "FSFolder"; break;
case kpidPath: prop = fs2us(_path); break;
}
prop.Detach(value);
return S_OK;
COM_TRY_END
}
STDMETHODIMP CFSFolder::WasChanged(Int32 *wasChanged)
{
bool wasChangedMain = false;
#ifdef _WIN32
for (;;)
{
if (!_findChangeNotification.IsHandleAllocated())
{
*wasChanged = BoolToInt(false);
return S_OK;
}
DWORD waitResult = ::WaitForSingleObject(_findChangeNotification, 0);
bool wasChangedLoc = (waitResult == WAIT_OBJECT_0);
if (wasChangedLoc)
{
_findChangeNotification.FindNext();
wasChangedMain = true;
}
else
break;
}
#endif
*wasChanged = BoolToInt(wasChangedMain);
return S_OK;
}
STDMETHODIMP CFSFolder::Clone(IFolderFolder **resultFolder)
{
CFSFolder *fsFolderSpec = new CFSFolder;
CMyComPtr<IFolderFolder> folderNew = fsFolderSpec;
fsFolderSpec->Init(_path);
*resultFolder = folderNew.Detach();
return S_OK;
}
HRESULT CFSFolder::GetItemsFullSize(const UInt32 *indices, UInt32 numItems, CFsFolderStat &stat)
{
for (UInt32 i = 0; i < numItems; i++)
{
UInt32 index = indices[i];
/*
if (index >= Files.Size())
{
size += Streams[index - Files.Size()].Size;
// numFiles++;
continue;
}
*/
const CDirItem &fi = Files[index];
if (fi.IsDir())
{
stat.Path = _path;
stat.Path += GetRelPath(fi);
RINOK(stat.Enumerate());
stat.NumFolders++;
}
else
{
stat.NumFiles++;
stat.Size += fi.Size;
}
}
return S_OK;
}
/*
HRESULT CFSFolder::GetItemFullSize(unsigned index, UInt64 &size, IProgress *progress)
{
if (index >= Files.Size())
{
size = Streams[index - Files.Size()].Size;
return S_OK;
}
const CDirItem &fi = Files[index];
if (fi.IsDir())
{
UInt64 numFolders = 0, numFiles = 0;
size = 0;
return GetFolderSize(_path + GetRelPath(fi), numFolders, numFiles, size, progress);
}
size = fi.Size;
return S_OK;
}
STDMETHODIMP CFSFolder::GetItemFullSize(UInt32 index, PROPVARIANT *value, IProgress *progress)
{
NCOM::CPropVariant prop;
UInt64 size = 0;
HRESULT result = GetItemFullSize(index, size, progress);
prop = size;
prop.Detach(value);
return result;
}
*/
STDMETHODIMP CFSFolder::CalcItemFullSize(UInt32 index, IProgress *progress)
{
if (index >= (UInt32)Files.Size())
return S_OK;
CDirItem &fi = Files[index];
if (!fi.IsDir())
return S_OK;
CFsFolderStat stat(_path + GetRelPath(fi), progress);
RINOK(stat.Enumerate());
fi.Size = stat.Size;
fi.NumFolders = stat.NumFolders;
fi.NumFiles = stat.NumFiles;
fi.FolderStat_Defined = true;
return S_OK;
}
void CFSFolder::GetAbsPath(const wchar_t *name, FString &absPath)
{
absPath.Empty();
if (!IsAbsolutePath(name))
absPath += _path;
absPath += us2fs(name);
}
STDMETHODIMP CFSFolder::CreateFolder(const wchar_t *name, IProgress * /* progress */)
{
FString absPath;
GetAbsPath(name, absPath);
if (CreateDir(absPath))
return S_OK;
if (::GetLastError() == ERROR_ALREADY_EXISTS)
return ::GetLastError();
if (!CreateComplexDir(absPath))
return ::GetLastError();
return S_OK;
}
STDMETHODIMP CFSFolder::CreateFile(const wchar_t *name, IProgress * /* progress */)
{
FString absPath;
GetAbsPath(name, absPath);
NIO::COutFile outFile;
if (!outFile.Create(absPath, false))
return ::GetLastError();
return S_OK;
}
STDMETHODIMP CFSFolder::Rename(UInt32 index, const wchar_t *newName, IProgress * /* progress */)
{
if (index >= (UInt32)Files.Size())
return E_NOTIMPL;
const CDirItem &fi = Files[index];
// FString prefix;
// GetPrefix(fi, prefix);
FString fullPrefix = _path;
if (fi.Parent >= 0)
fullPrefix += Folders[fi.Parent];
if (!MyMoveFile(fullPrefix + fi.Name, fullPrefix + us2fs(newName)))
return GetLastError();
return S_OK;
}
STDMETHODIMP CFSFolder::Delete(const UInt32 *indices, UInt32 numItems,IProgress *progress)
{
RINOK(progress->SetTotal(numItems));
// int prevDeletedFileIndex = -1;
for (UInt32 i = 0; i < numItems; i++)
{
// Sleep(200);
UInt32 index = indices[i];
bool result = true;
/*
if (index >= (UInt32)Files.Size())
{
const CAltStream &ss = Streams[index - (UInt32)Files.Size()];
if (prevDeletedFileIndex != ss.Parent)
{
const CDirItem &fi = Files[ss.Parent];
result = DeleteFileAlways(_path + GetRelPath(fi) + us2fs(ss.Name));
}
}
else
*/
{
const CDirItem &fi = Files[index];
const FString fullPath = _path + GetRelPath(fi);
// prevDeletedFileIndex = index;
if (fi.IsDir())
result = RemoveDirWithSubItems(fullPath);
else
result = DeleteFileAlways(fullPath);
}
if (!result)
return GetLastError();
UInt64 completed = i;
RINOK(progress->SetCompleted(&completed));
}
return S_OK;
}
STDMETHODIMP CFSFolder::SetProperty(UInt32 index, PROPID propID,
const PROPVARIANT *value, IProgress * /* progress */)
{
if (index >= (UInt32)Files.Size())
return E_INVALIDARG;
CDirItem &fi = Files[index];
if (fi.Parent >= 0)
return E_NOTIMPL;
switch (propID)
{
case kpidComment:
{
UString filename = fs2us(fi.Name);
filename.Trim();
if (value->vt == VT_EMPTY)
_comments.DeletePair(filename);
else if (value->vt == VT_BSTR)
{
CTextPair pair;
pair.ID = filename;
pair.ID.Trim();
pair.Value.SetFromBstr(value->bstrVal);
pair.Value.Trim();
if (pair.Value.IsEmpty())
_comments.DeletePair(filename);
else
_comments.AddPair(pair);
}
else
return E_INVALIDARG;
SaveComments();
break;
}
default:
return E_NOTIMPL;
}
return S_OK;
}
STDMETHODIMP CFSFolder::GetSystemIconIndex(UInt32 index, Int32 *iconIndex)
{
if (index >= (UInt32)Files.Size())
return E_INVALIDARG;
const CDirItem &fi = Files[index];
*iconIndex = 0;
int iconIndexTemp;
if (GetRealIconIndex(_path + GetRelPath(fi), fi.Attrib, iconIndexTemp) != 0)
{
*iconIndex = iconIndexTemp;
return S_OK;
}
return GetLastError();
}
STDMETHODIMP CFSFolder::SetFlatMode(Int32 flatMode)
{
_flatMode = IntToBool(flatMode);
return S_OK;
}
/*
STDMETHODIMP CFSFolder::SetShowNtfsStreamsMode(Int32 showStreamsMode)
{
_scanAltStreams = IntToBool(showStreamsMode);
return S_OK;
}
*/
}