| // FSFolder.cpp |
| |
| #include "StdAfx.h" |
| |
| #ifdef __MINGW32_VERSION |
| // #if !defined(_MSC_VER) && (__GNUC__) && (__GNUC__ < 10) |
| // for old mingw |
| #include <ddk/ntddk.h> |
| #else |
| #ifndef Z7_OLD_WIN_SDK |
| #if !defined(_M_IA64) |
| #include <winternl.h> |
| #endif |
| #else |
| typedef LONG NTSTATUS; |
| typedef struct _IO_STATUS_BLOCK { |
| union { |
| NTSTATUS Status; |
| PVOID Pointer; |
| }; |
| ULONG_PTR Information; |
| } IO_STATUS_BLOCK, *PIO_STATUS_BLOCK; |
| #endif |
| #endif |
| |
| #include "../../../Common/ComTry.h" |
| #include "../../../Common/Defs.h" |
| #include "../../../Common/StringConvert.h" |
| #include "../../../Common/UTFConvert.h" |
| |
| #include "../../../Windows/DLL.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" |
| |
| #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, |
| #ifdef FS_SHOW_LINKS_INFO |
| kpidChangeTime, |
| #endif |
| 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()) |
| { |
| const HRESULT lastError = GetLastError_noZero_HRESULT(); |
| CFindFile findFile; |
| CFileInfo fi; |
| FString path2 = _path; |
| path2 += '*'; // CHAR_ANY_MASK; |
| if (!findFile.FindFirst(path2, fi)) |
| return lastError; |
| } |
| |
| #endif |
| |
| return S_OK; |
| } |
| |
| |
| HRESULT CFsFolderStat::Enumerate() |
| { |
| if (Progress) |
| { |
| RINOK(Progress->SetCompleted(NULL)) |
| } |
| Path.Add_PathSepar(); |
| const unsigned len = Path.Len(); |
| CEnumerator enumerator; |
| enumerator.SetDirPrefix(Path); |
| CDirEntry fi; |
| while (enumerator.Next(fi)) |
| { |
| if (fi.IsDir()) |
| { |
| NumFolders++; |
| Path.DeleteFrom(len); |
| Path += fi.Name; |
| RINOK(Enumerate()) |
| } |
| else |
| { |
| NumFiles++; |
| Size += fi.Size; |
| } |
| } |
| return S_OK; |
| } |
| |
| #ifndef UNDER_CE |
| |
| bool MyGetCompressedFileSizeW(CFSTR path, UInt64 &size); |
| 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 Z7_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) |
| { |
| const unsigned startIndex = Folders.Size(); |
| { |
| CEnumerator enumerator; |
| enumerator.SetDirPrefix(_path + relPrefix); |
| 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; |
| */ |
| } |
| |
| #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; |
| fi.ChangeTime_Defined = false; |
| fi.ChangeTime_WasRequested = false; |
| #endif |
| |
| fi.PackSize = fi.Size; |
| |
| #ifdef FS_SHOW_LINKS_INFO |
| 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 |
| |
| #endif // UNDER_CE |
| |
| /* 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; |
| |
| const unsigned endIndex = Folders.Size(); |
| for (unsigned i = startIndex; i < endIndex; i++) |
| LoadSubItems((int)i, Folders[i]); |
| return S_OK; |
| } |
| |
| Z7_COM7F_IMF(CFSFolder::LoadItems()) |
| { |
| Int32 dummy; |
| WasChanged(&dummy); |
| Clear(); |
| RINOK(LoadSubItems(-1, FString())) |
| _commentsAreLoaded = false; |
| return S_OK; |
| } |
| |
| static CFSTR const 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); |
| size_t processedSize; |
| if (!file.ReadFull(p, (unsigned)(size_t)len, processedSize)) |
| return false; |
| 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; |
| } |
| |
| Z7_COM7F_IMF(CFSFolder::GetNumberOfItems(UInt32 *numItems)) |
| { |
| *numItems = Files.Size() /* + Streams.Size() */; |
| return S_OK; |
| } |
| |
| #ifdef USE_UNICODE_FSTRING |
| |
| Z7_COM7F_IMF(CFSFolder::GetItemPrefix(UInt32 index, const wchar_t **name, unsigned *len)) |
| { |
| *name = NULL; |
| *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; |
| } |
| |
| Z7_COM7F_IMF(CFSFolder::GetItemName(UInt32 index, const wchar_t **name, unsigned *len)) |
| { |
| *name = NULL; |
| *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; |
| } |
| |
| Z7_COM7F_IMF2(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; |
| memset(&info, 0, sizeof(info)); // for vc6-O2 |
| 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; |
| } |
| |
| |
| EXTERN_C_BEGIN |
| |
| typedef struct |
| { |
| LARGE_INTEGER CreationTime; |
| LARGE_INTEGER LastAccessTime; |
| LARGE_INTEGER LastWriteTime; |
| LARGE_INTEGER ChangeTime; |
| ULONG FileAttributes; |
| UInt32 Reserved; // it's expected for alignment |
| } |
| Z7_WIN_FILE_BASIC_INFORMATION; |
| |
| |
| typedef enum |
| { |
| Z7_WIN_FileDirectoryInformation = 1, |
| Z7_WIN_FileFullDirectoryInformation, |
| Z7_WIN_FileBothDirectoryInformation, |
| Z7_WIN_FileBasicInformation |
| } |
| Z7_WIN_FILE_INFORMATION_CLASS; |
| |
| |
| #if (_WIN32_WINNT >= 0x0500) && !defined(_M_IA64) |
| #define Z7_WIN_NTSTATUS NTSTATUS |
| #define Z7_WIN_IO_STATUS_BLOCK IO_STATUS_BLOCK |
| #else |
| typedef LONG Z7_WIN_NTSTATUS; |
| typedef struct |
| { |
| union |
| { |
| Z7_WIN_NTSTATUS Status; |
| PVOID Pointer; |
| } DUMMYUNIONNAME; |
| ULONG_PTR Information; |
| } Z7_WIN_IO_STATUS_BLOCK; |
| #endif |
| |
| |
| typedef Z7_WIN_NTSTATUS (WINAPI * Func_NtQueryInformationFile)( |
| HANDLE handle, Z7_WIN_IO_STATUS_BLOCK *io, |
| void *ptr, LONG len, Z7_WIN_FILE_INFORMATION_CLASS cls); |
| |
| #define MY_STATUS_SUCCESS 0 |
| |
| EXTERN_C_END |
| |
| static Func_NtQueryInformationFile f_NtQueryInformationFile; |
| static bool g_NtQueryInformationFile_WasRequested = false; |
| |
| |
| void CFSFolder::ReadChangeTime(CDirItem &di) |
| { |
| di.ChangeTime_WasRequested = true; |
| |
| if (!g_NtQueryInformationFile_WasRequested) |
| { |
| g_NtQueryInformationFile_WasRequested = true; |
| f_NtQueryInformationFile = Z7_GET_PROC_ADDRESS( |
| Func_NtQueryInformationFile, ::GetModuleHandleW(L"ntdll.dll"), |
| "NtQueryInformationFile"); |
| } |
| if (!f_NtQueryInformationFile) |
| return; |
| |
| NIO::CInFile file; |
| if (!file.Open_for_ReadAttributes(_path + GetRelPath(di))) |
| return; |
| Z7_WIN_FILE_BASIC_INFORMATION fbi; |
| Z7_WIN_IO_STATUS_BLOCK IoStatusBlock; |
| const Z7_WIN_NTSTATUS status = f_NtQueryInformationFile(file.GetHandle(), &IoStatusBlock, |
| &fbi, sizeof(fbi), Z7_WIN_FileBasicInformation); |
| if (status != MY_STATUS_SUCCESS) |
| return; |
| if (IoStatusBlock.Information != sizeof(fbi)) |
| return; |
| di.ChangeTime.dwLowDateTime = fbi.ChangeTime.u.LowPart; |
| di.ChangeTime.dwHighDateTime = (DWORD)fbi.ChangeTime.u.HighPart; |
| di.ChangeTime_Defined = true; |
| } |
| |
| #endif // FS_SHOW_LINKS_INFO |
| |
| |
| Z7_COM7F_IMF(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: |
| #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; |
| |
| case kpidChangeTime: |
| if (!fi.ChangeTime_WasRequested) |
| ReadChangeTime(fi); |
| if (fi.ChangeTime_Defined) |
| prop = fi.ChangeTime; |
| 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((unsigned)pos); |
| prop = comment; |
| } |
| break; |
| } |
| case kpidPrefix: |
| if (fi.Parent >= 0) |
| prop = fs2us(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 ---------- |
| |
| |
| Z7_COM7F_IMF(CFSFolder::GetNumRawProps(UInt32 *numProps)) |
| { |
| *numProps = 1; |
| return S_OK; |
| } |
| |
| Z7_COM7F_IMF(CFSFolder::GetRawPropInfo(UInt32 /* index */, BSTR *name, PROPID *propID)) |
| { |
| *name = NULL; |
| *propID = kpidNtReparse; |
| return S_OK; |
| } |
| |
| Z7_COM7F_IMF(CFSFolder::GetParent(UInt32 /* index */, UInt32 * /* parent */, UInt32 * /* parentType */)) |
| { |
| return E_FAIL; |
| } |
| |
| Z7_COM7F_IMF(CFSFolder::GetRawProp(UInt32 index, PROPID propID, |
| const void **data, UInt32 *dataSize, UInt32 *propType)) |
| { |
| #ifdef UNDER_CE |
| UNUSED(index) |
| UNUSED(propID) |
| #endif |
| |
| *data = NULL; |
| *dataSize = 0; |
| *propType = 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) |
| { |
| const int dotPos = name.ReverseFind_Dot(); |
| return name.Ptr((dotPos < 0) ? name.Len() : (unsigned)dotPos); |
| } |
| |
| Z7_COM7F_IMF2(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: |
| { |
| const 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: |
| { |
| #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 = NULL; |
| 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; |
| } |
| |
| Z7_COM7F_IMF(CFSFolder::BindToFolder(UInt32 index, IFolderFolder **resultFolder)) |
| { |
| *resultFolder = NULL; |
| const CDirItem &fi = Files[index]; |
| if (!fi.IsDir()) |
| return E_INVALIDARG; |
| return BindToFolderSpec(GetRelPath(fi), resultFolder); |
| } |
| |
| Z7_COM7F_IMF(CFSFolder::BindToFolder(const wchar_t *name, IFolderFolder **resultFolder)) |
| { |
| return BindToFolderSpec(us2fs(name), resultFolder); |
| } |
| |
| Z7_COM7F_IMF(CFSFolder::BindToParentFolder(IFolderFolder **resultFolder)) |
| { |
| *resultFolder = NULL; |
| /* |
| if (_parentFolder) |
| { |
| CMyComPtr<IFolderFolder> parentFolder = _parentFolder; |
| *resultFolder = parentFolder.Detach(); |
| return S_OK; |
| } |
| */ |
| if (_path.IsEmpty()) |
| return E_INVALIDARG; |
| |
| #ifndef UNDER_CE |
| |
| if (IsDriveRootPath_SuperAllowed(_path)) |
| { |
| CFSDrives *drivesFolderSpec = new CFSDrives; |
| CMyComPtr<IFolderFolder> drivesFolder = drivesFolderSpec; |
| drivesFolderSpec->Init(false, IsSuperPath(_path)); |
| *resultFolder = drivesFolder.Detach(); |
| return S_OK; |
| } |
| |
| 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(); |
| parentPath.DeleteFrom((unsigned)(pos + 1)); |
| |
| if (NName::IsDrivePath_SuperAllowed(parentPath)) |
| { |
| CFSFolder *parentFolderSpec = new CFSFolder; |
| CMyComPtr<IFolderFolder> parentFolder = parentFolderSpec; |
| if (parentFolderSpec->Init(parentPath) == S_OK) |
| { |
| *resultFolder = parentFolder.Detach(); |
| return S_OK; |
| } |
| } |
| |
| /* |
| FString parentPathReduced = parentPath.Left(pos); |
| |
| 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 |
| |
| return S_OK; |
| } |
| |
| Z7_COM7F_IMF(CFSFolder::GetNumberOfProperties(UInt32 *numProperties)) |
| { |
| *numProperties = Z7_ARRAY_SIZE(kProps); |
| if (!_flatMode) |
| (*numProperties)--; |
| return S_OK; |
| } |
| |
| IMP_IFolderFolder_GetProp(CFSFolder::GetPropertyInfo, kProps) |
| |
| Z7_COM7F_IMF(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 |
| } |
| |
| Z7_COM7F_IMF(CFSFolder::WasChanged(Int32 *wasChanged)) |
| { |
| bool wasChangedMain = false; |
| |
| #ifdef _WIN32 |
| |
| for (;;) |
| { |
| if (!_findChangeNotification.IsHandleAllocated()) |
| break; |
| DWORD waitResult = ::WaitForSingleObject(_findChangeNotification, 0); |
| if (waitResult != WAIT_OBJECT_0) |
| break; |
| _findChangeNotification.FindNext(); |
| wasChangedMain = true; |
| } |
| |
| #endif |
| |
| *wasChanged = BoolToInt(wasChangedMain); |
| return S_OK; |
| } |
| |
| Z7_COM7F_IMF(CFSFolder::Clone(IFolderFolder **resultFolder)) |
| { |
| CFSFolder *fsFolderSpec = new CFSFolder; |
| CMyComPtr<IFolderFolder> folderNew = fsFolderSpec; |
| fsFolderSpec->Init(_path); |
| *resultFolder = folderNew.Detach(); |
| 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; |
| } |
| |
| Z7_COM7F_IMF(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; |
| } |
| */ |
| |
| Z7_COM7F_IMF(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); |
| } |
| |
| Z7_COM7F_IMF(CFSFolder::CreateFolder(const wchar_t *name, IProgress * /* progress */)) |
| { |
| FString absPath; |
| GetAbsPath(name, absPath); |
| if (CreateDir(absPath)) |
| return S_OK; |
| if (::GetLastError() != ERROR_ALREADY_EXISTS) |
| if (CreateComplexDir(absPath)) |
| return S_OK; |
| return GetLastError_noZero_HRESULT(); |
| } |
| |
| Z7_COM7F_IMF(CFSFolder::CreateFile(const wchar_t *name, IProgress * /* progress */)) |
| { |
| FString absPath; |
| GetAbsPath(name, absPath); |
| NIO::COutFile outFile; |
| if (!outFile.Create(absPath, false)) |
| return GetLastError_noZero_HRESULT(); |
| return S_OK; |
| } |
| |
| Z7_COM7F_IMF(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_noZero_HRESULT(); |
| return S_OK; |
| } |
| |
| Z7_COM7F_IMF(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_noZero_HRESULT(); |
| const UInt64 completed = i; |
| RINOK(progress->SetCompleted(&completed)) |
| } |
| return S_OK; |
| } |
| |
| Z7_COM7F_IMF(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; |
| } |
| |
| Z7_COM7F_IMF(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_noZero_HRESULT(); |
| } |
| |
| Z7_COM7F_IMF(CFSFolder::SetFlatMode(Int32 flatMode)) |
| { |
| _flatMode = IntToBool(flatMode); |
| return S_OK; |
| } |
| |
| /* |
| Z7_COM7F_IMF(CFSFolder::SetShowNtfsStreamsMode(Int32 showStreamsMode) |
| { |
| _scanAltStreams = IntToBool(showStreamsMode); |
| return S_OK; |
| } |
| */ |
| |
| } |