7
#include "Common/StringConvert.h"
8
#include "Common/UTFConvert.h"
9
#include "Common/ComTry.h"
11
#include "Windows/Defs.h"
12
#include "Windows/PropVariant.h"
13
#include "Windows/FileDir.h"
14
#include "Windows/FileIO.h"
16
#include "../../PropID.h"
18
#include "SysIconUtils.h"
21
#include "NetFolder.h"
27
bool GetLongPath(LPCWSTR path, UString &longPath);
31
using namespace NWindows;
32
using namespace NFile;
33
using namespace NFind;
37
static STATPROPSTG kProperties[] =
39
{ NULL, kpidName, VT_BSTR},
40
// { NULL, kpidIsDir, VT_BOOL},
41
{ NULL, kpidSize, VT_UI8},
42
{ NULL, kpidMTime, VT_FILETIME},
43
{ NULL, kpidCTime, VT_FILETIME},
44
{ NULL, kpidATime, VT_FILETIME},
45
{ NULL, kpidAttrib, VT_UI4},
46
{ NULL, kpidPackSize, VT_UI8},
47
{ NULL, kpidComment, VT_BSTR},
48
{ NULL, kpidPrefix, VT_BSTR}
51
HRESULT CFSFolder::Init(const UString &path, IFolderFolder *parentFolder)
53
_parentFolder = parentFolder;
57
_findChangeNotification.FindFirst(_path, false,
58
FILE_NOTIFY_CHANGE_FILE_NAME |
59
FILE_NOTIFY_CHANGE_DIR_NAME |
60
FILE_NOTIFY_CHANGE_ATTRIBUTES |
61
FILE_NOTIFY_CHANGE_SIZE |
62
FILE_NOTIFY_CHANGE_LAST_WRITE /*|
63
FILE_NOTIFY_CHANGE_LAST_ACCESS |
64
FILE_NOTIFY_CHANGE_CREATION |
65
FILE_NOTIFY_CHANGE_SECURITY */);
66
if (!_findChangeNotification.IsHandleAllocated())
68
DWORD lastError = GetLastError();
71
if (!findFile.FindFirst(_path + UString(L"*"), fileInfo))
78
HRESULT GetFolderSize(const UString &path, UInt64 &numFolders, UInt64 &numFiles, UInt64 &size, IProgress *progress)
80
RINOK(progress->SetCompleted(NULL));
81
numFiles = numFolders = size = 0;
82
CEnumeratorW enumerator(path + UString(WSTRING_PATH_SEPARATOR L"*"));
84
while (enumerator.Next(fileInfo))
88
UInt64 subFolders, subFiles, subSize;
89
RINOK(GetFolderSize(path + UString(WSTRING_PATH_SEPARATOR) + fileInfo.Name, subFolders, subFiles, subSize, progress));
90
numFolders += subFolders;
98
size += fileInfo.Size;
104
HRESULT CFSFolder::LoadSubItems(CDirItem &dirItem, const UString &path)
107
CEnumeratorW enumerator(path + L"*");
109
while (enumerator.Next(fileInfo))
111
fileInfo.CompressedSizeIsDefined = false;
113
if (!GetCompressedFileSize(_path + fileInfo.Name,
114
fileInfo.CompressedSize))
115
fileInfo.CompressedSize = fileInfo.Size;
117
if (fileInfo.IsDir())
119
// fileInfo.Size = GetFolderSize(_path + fileInfo.Name);
122
dirItem.Files.Add(fileInfo);
128
for (int i = 0; i < dirItem.Files.Size(); i++)
130
CDirItem &item = dirItem.Files[i];
132
LoadSubItems(item, path + item.Name + WCHAR_PATH_SEPARATOR);
137
void CFSFolder::AddRefs(CDirItem &dirItem)
140
for (i = 0; i < dirItem.Files.Size(); i++)
142
CDirItem &item = dirItem.Files[i];
143
item.Parent = &dirItem;
148
for (i = 0; i < dirItem.Files.Size(); i++)
150
CDirItem &item = dirItem.Files[i];
156
STDMETHODIMP CFSFolder::LoadItems()
158
// OutputDebugString(TEXT("Start\n"));
162
RINOK(LoadSubItems(_root, _path));
165
// OutputDebugString(TEXT("Finish\n"));
166
_commentsAreLoaded = false;
170
static const wchar_t *kDescriptionFileName = L"descript.ion";
172
bool CFSFolder::LoadComments()
174
if (_commentsAreLoaded)
177
_commentsAreLoaded = true;
179
if (!file.Open(_path + kDescriptionFileName))
182
if (!file.GetLength(length))
184
if (length >= (1 << 28))
187
char *p = s.GetBuffer((int)((size_t)length + 1));
188
UInt32 processedSize;
189
file.Read(p, (UInt32)length, processedSize);
192
if (processedSize != length)
195
UString unicodeString;
196
if (!ConvertUTF8ToUnicode(s, unicodeString))
198
return _comments.ReadFromString(unicodeString);
201
static bool IsAscii(const UString &testString)
203
for (int i = 0; i < testString.Length(); i++)
204
if (testString[i] >= 0x80)
209
bool CFSFolder::SaveComments()
212
if (!file.Create(_path + kDescriptionFileName, true))
214
UString unicodeString;
215
_comments.SaveToString(unicodeString);
217
ConvertUnicodeToUTF8(unicodeString, utfString);
218
UInt32 processedSize;
219
if (!IsAscii(unicodeString))
221
Byte bom [] = { 0xEF, 0xBB, 0xBF, 0x0D, 0x0A };
222
file.Write(bom , sizeof(bom), processedSize);
224
file.Write(utfString, utfString.Length(), processedSize);
225
_commentsAreLoaded = false;
229
STDMETHODIMP CFSFolder::GetNumberOfItems(UInt32 *numItems)
231
*numItems = _refs.Size();
236
STDMETHODIMP CFSFolder::GetNumberOfSubFolders(UInt32 *numSubFolders)
238
UInt32 numSubFoldersLoc = 0;
239
for (int i = 0; i < _files.Size(); i++)
240
if (_files[i].IsDir())
242
*numSubFolders = numSubFoldersLoc;
248
bool MyGetCompressedFileSizeW(LPCWSTR fileName, UInt64 &size)
251
DWORD lowPart = ::GetCompressedFileSizeW(fileName, &highPart);
252
if (lowPart == INVALID_FILE_SIZE && ::GetLastError() != NO_ERROR)
257
if (GetLongPath(fileName, longPath))
258
lowPart = ::GetCompressedFileSizeW(longPath, &highPart);
261
if (lowPart == INVALID_FILE_SIZE && ::GetLastError() != NO_ERROR)
264
size = (UInt64(highPart) << 32) | lowPart;
269
STDMETHODIMP CFSFolder::GetProperty(UInt32 itemIndex, PROPID propID, PROPVARIANT *value)
271
NCOM::CPropVariant prop;
272
if (itemIndex >= (UInt32)_refs.Size())
274
CDirItem &fileInfo = *_refs[itemIndex];
277
case kpidIsDir: prop = fileInfo.IsDir(); break;
278
case kpidName: prop = fileInfo.Name; break;
279
case kpidSize: if (!fileInfo.IsDir()) prop = fileInfo.Size; break;
281
if (!fileInfo.CompressedSizeIsDefined)
283
fileInfo.CompressedSizeIsDefined = true;
285
if (fileInfo.IsDir () ||
286
!MyGetCompressedFileSizeW(_path + GetRelPath(fileInfo), fileInfo.CompressedSize))
288
fileInfo.CompressedSize = fileInfo.Size;
290
prop = fileInfo.CompressedSize;
292
case kpidAttrib: prop = (UInt32)fileInfo.Attrib; break;
293
case kpidCTime: prop = fileInfo.CTime; break;
294
case kpidATime: prop = fileInfo.ATime; break;
295
case kpidMTime: prop = fileInfo.MTime; break;
300
if (_comments.GetValue(GetRelPath(fileInfo), comment))
308
prop = GetPrefix(fileInfo);
317
HRESULT CFSFolder::BindToFolderSpec(const wchar_t *name, IFolderFolder **resultFolder)
320
CFSFolder *folderSpec = new CFSFolder;
321
CMyComPtr<IFolderFolder> subFolder = folderSpec;
322
RINOK(folderSpec->Init(_path + name + UString(WCHAR_PATH_SEPARATOR), 0));
323
*resultFolder = subFolder.Detach();
327
UString CFSFolder::GetPrefix(const CDirItem &item) const
330
CDirItem *cur = item.Parent;
331
while (cur->Parent != 0)
333
path = cur->Name + UString(WCHAR_PATH_SEPARATOR) + path;
339
UString CFSFolder::GetRelPath(const CDirItem &item) const
341
return GetPrefix(item) + item.Name;
344
STDMETHODIMP CFSFolder::BindToFolder(UInt32 index, IFolderFolder **resultFolder)
347
const CDirItem &fileInfo = *_refs[index];
348
if (!fileInfo.IsDir())
350
return BindToFolderSpec(GetRelPath(fileInfo), resultFolder);
353
STDMETHODIMP CFSFolder::BindToFolder(const wchar_t *name, IFolderFolder **resultFolder)
355
return BindToFolderSpec(name, resultFolder);
358
STDMETHODIMP CFSFolder::BindToParentFolder(IFolderFolder **resultFolder)
363
CMyComPtr<IFolderFolder> parentFolder = _parentFolder;
364
*resultFolder = parentFolder.Detach();
369
printf("CFSFolder::BindToParentFolder path='%ls'\n",(const wchar_t *)_path);
370
int pos = _path.ReverseFind(WCHAR_PATH_SEPARATOR);
371
if (pos < 0 || pos != _path.Length() - 1)
373
UString parentPath = _path.Left(pos);
374
printf("CFSFolder::BindToParentFolder parentPath='%ls'\n",(const wchar_t *)parentPath);
375
pos = parentPath.ReverseFind(WCHAR_PATH_SEPARATOR);
380
CFSDrives *drivesFolderSpec = new CFSDrives;
381
CMyComPtr<IFolderFolder> drivesFolder = drivesFolderSpec;
382
drivesFolderSpec->Init();
383
*resultFolder = drivesFolder.Detach();
385
parentPath = WSTRING_PATH_SEPARATOR;
386
CFSFolder *parentFolderSpec = new CFSFolder;
387
CMyComPtr<IFolderFolder> parentFolder = parentFolderSpec;
388
printf("CFSFolder::BindToParentFolder Init-0 with parentPath='%ls'\n",(const wchar_t *)parentPath);
389
RINOK(parentFolderSpec->Init(parentPath, 0));
390
*resultFolder = parentFolder.Detach();
394
UString parentPathReduced = parentPath.Left(pos);
395
parentPath = parentPath.Left(pos + 1);
396
pos = parentPathReduced.ReverseFind(WCHAR_PATH_SEPARATOR);
400
if (parentPath[0] != WCHAR_PATH_SEPARATOR)
402
CNetFolder *netFolderSpec = new CNetFolder;
403
CMyComPtr<IFolderFolder> netFolder = netFolderSpec;
404
netFolderSpec->Init(parentPath);
405
*resultFolder = netFolder.Detach();
407
parentPath = WSTRING_PATH_SEPARATOR;
408
CFSFolder *parentFolderSpec = new CFSFolder;
409
CMyComPtr<IFolderFolder> parentFolder = parentFolderSpec;
410
printf("CFSFolder::BindToParentFolder Init-1 with parentPath='%ls'\n",(const wchar_t *)parentPath);
411
RINOK(parentFolderSpec->Init(parentPath, 0));
412
*resultFolder = parentFolder.Detach();
413
#endif // ifdef _WIN32
416
CFSFolder *parentFolderSpec = new CFSFolder;
417
CMyComPtr<IFolderFolder> parentFolder = parentFolderSpec;
418
printf("CFSFolder::BindToParentFolder Init-2 with parentPath='%ls'\n",(const wchar_t *)parentPath);
419
RINOK(parentFolderSpec->Init(parentPath, 0));
420
*resultFolder = parentFolder.Detach();
424
STDMETHODIMP CFSFolder::GetNumberOfProperties(UInt32 *numProperties)
426
*numProperties = sizeof(kProperties) / sizeof(kProperties[0]);
432
STDMETHODIMP CFSFolder::GetPropertyInfo(UInt32 index,
433
BSTR *name, PROPID *propID, VARTYPE *varType)
435
if (index >= sizeof(kProperties) / sizeof(kProperties[0]))
437
const STATPROPSTG &prop = kProperties[index];
438
*propID = prop.propid;
445
STDMETHODIMP CFSFolder::GetFolderProperty(PROPID propID, PROPVARIANT *value)
448
NWindows::NCOM::CPropVariant prop;
451
case kpidType: prop = L"FSFolder"; break;
452
case kpidPath: prop = _path; break;
459
STDMETHODIMP CFSFolder::WasChanged(INT32 *wasChanged)
461
bool wasChangedMain = false;
465
if (!_findChangeNotification.IsHandleAllocated())
467
*wasChanged = BoolToInt(false);
471
DWORD waitResult = ::WaitForSingleObject(_findChangeNotification, 0);
472
bool wasChangedLoc = (waitResult == WAIT_OBJECT_0);
475
_findChangeNotification.FindNext();
476
wasChangedMain = true;
482
*wasChanged = BoolToInt(wasChangedMain);
486
STDMETHODIMP CFSFolder::Clone(IFolderFolder **resultFolder)
488
CFSFolder *fsFolderSpec = new CFSFolder;
489
CMyComPtr<IFolderFolder> folderNew = fsFolderSpec;
490
fsFolderSpec->Init(_path, 0);
491
*resultFolder = folderNew.Detach();
495
HRESULT CFSFolder::GetItemsFullSize(const UInt32 *indices, UInt32 numItems,
496
UInt64 &numFolders, UInt64 &numFiles, UInt64 &size, IProgress *progress)
498
numFiles = numFolders = size = 0;
500
for (i = 0; i < numItems; i++)
502
int index = indices[i];
503
if (index >= _refs.Size())
505
const CDirItem &fileInfo = *_refs[index];
506
if (fileInfo.IsDir())
508
UInt64 subFolders, subFiles, subSize;
509
RINOK(GetFolderSize(_path + GetRelPath(fileInfo), subFolders, subFiles, subSize, progress));
510
numFolders += subFolders;
512
numFiles += subFiles;
518
size += fileInfo.Size;
524
HRESULT CFSFolder::GetItemFullSize(int index, UInt64 &size, IProgress *progress)
526
const CDirItem &fileInfo = *_refs[index];
527
if (fileInfo.IsDir())
530
CMyComPtr<IFolderFolder> subFolder;
531
RINOK(BindToFolder(index, &subFolder));
532
CMyComPtr<IFolderReload> aFolderReload;
533
subFolder.QueryInterface(&aFolderReload);
534
aFolderReload->Reload();
536
RINOK(subFolder->GetNumberOfItems(&numItems));
537
CMyComPtr<IFolderGetItemFullSize> aGetItemFullSize;
538
subFolder.QueryInterface(&aGetItemFullSize);
539
for (UInt32 i = 0; i < numItems; i++)
542
RINOK(aGetItemFullSize->GetItemFullSize(i, &size));
546
UInt64 numFolders, numFiles;
547
return GetFolderSize(_path + GetRelPath(fileInfo), numFolders, numFiles, size, progress);
549
size = fileInfo.Size;
553
STDMETHODIMP CFSFolder::GetItemFullSize(UInt32 index, PROPVARIANT *value, IProgress *progress)
555
NCOM::CPropVariant prop;
556
if (index >= (UInt32)_refs.Size())
559
HRESULT result = GetItemFullSize(index, size, progress);
565
HRESULT CFSFolder::GetComplexName(const wchar_t *name, UString &resultPath)
567
UString newName = name;
568
resultPath = _path + newName;
569
if (newName.Length() < 1)
571
if (newName[0] == WCHAR_PATH_SEPARATOR)
573
resultPath = newName;
576
if (newName.Length() < 2)
578
if (newName[1] == L':')
579
resultPath = newName;
583
STDMETHODIMP CFSFolder::CreateFolder(const wchar_t *name, IProgress * /* progress */)
585
UString processedName;
586
RINOK(GetComplexName(name, processedName));
587
if(NDirectory::MyCreateDirectory(processedName))
589
if(::GetLastError() == ERROR_ALREADY_EXISTS)
590
return ::GetLastError();
591
if (!NDirectory::CreateComplexDirectory(processedName))
592
return ::GetLastError();
596
STDMETHODIMP CFSFolder::CreateFile(const wchar_t *name, IProgress * /* progress */)
598
UString processedName;
599
RINOK(GetComplexName(name, processedName));
600
NIO::COutFile outFile;
601
if (!outFile.Create(processedName, false))
602
return ::GetLastError();
606
STDMETHODIMP CFSFolder::Rename(UInt32 index, const wchar_t *newName, IProgress * /* progress */)
608
const CDirItem &fileInfo = *_refs[index];
609
const UString fullPrefix = _path + GetPrefix(fileInfo);
610
if (!NDirectory::MyMoveFile(fullPrefix + fileInfo.Name, fullPrefix + newName))
611
return GetLastError();
615
STDMETHODIMP CFSFolder::Delete(const UInt32 *indices, UInt32 numItems,IProgress *progress)
618
RINOK(progress->SetTotal(numItems));
619
for (UInt32 i = 0; i < numItems; i++)
621
const CDirItem &fileInfo = *_refs[indices[i]];
622
const UString fullPath = _path + GetRelPath(fileInfo);
624
if (fileInfo.IsDir())
625
result = NDirectory::RemoveDirectoryWithSubItems(fullPath);
627
result = NDirectory::DeleteFileAlways(fullPath);
629
return GetLastError();
630
UInt64 completed = i;
631
RINOK(progress->SetCompleted(&completed));
637
STDMETHODIMP CFSFolder::SetProperty(UInt32 index, PROPID propID,
638
const PROPVARIANT *value, IProgress * /* progress */)
640
if (index >= (UInt32)_refs.Size())
642
CDirItem &fileInfo = *_refs[index];
643
if (fileInfo.Parent->Parent != 0)
649
UString filename = fileInfo.Name;
651
if (value->vt == VT_EMPTY)
652
_comments.DeletePair(filename);
653
else if (value->vt == VT_BSTR)
658
pair.Value = value->bstrVal;
660
if (pair.Value.IsEmpty())
661
_comments.DeletePair(filename);
663
_comments.AddPair(pair);
676
STDMETHODIMP CFSFolder::GetSystemIconIndex(UInt32 index, INT32 *iconIndex)
679
if (index >= (UInt32)_refs.Size())
681
const CDirItem &fileInfo = *_refs[index];
684
if (GetRealIconIndex(_path + GetRelPath(fileInfo), fileInfo.Attributes, iconIndexTemp) != 0)
686
*iconIndex = iconIndexTemp;
689
return GetLastError();
695
STDMETHODIMP CFSFolder::SetFlatMode(Int32 flatMode)
697
_flatMode = IntToBool(flatMode);