6 #include "ConsoleClose.h"
8 #include "Common/StringConvert.h"
9 #include "Common/StdOutStream.h"
10 #include "Common/IntToString.h"
11 #include "Common/MyCom.h"
13 #include "Windows/PropVariant.h"
14 #include "Windows/Defs.h"
15 #include "Windows/PropVariantConversions.h"
16 #include "Windows/FileDir.h"
18 #include "../../Archive/IArchive.h"
20 #include "../Common/PropIDUtils.h"
21 #include "../Common/OpenArchive.h"
23 #include "OpenCallbackConsole.h"
25 using namespace NWindows;
33 static CPropIdToName kPropIdToName[] =
35 { kpidPath, L"Path" },
36 { kpidName, L"Name" },
37 { kpidIsFolder, L"Folder" },
38 { kpidSize, L"Size" },
39 { kpidPackedSize, L"Packed Size" },
40 { kpidAttributes, L"Attributes" },
41 { kpidCreationTime, L"Created" },
42 { kpidLastAccessTime, L"Accessed" },
43 { kpidLastWriteTime, L"Modified" },
44 { kpidSolid, L"Solid" },
45 { kpidCommented, L"Commented" },
46 { kpidEncrypted, L"Encrypted" },
47 { kpidSplitBefore, L"Split Before" },
48 { kpidSplitAfter, L"Split After" },
49 { kpidDictionarySize, L"Dictionary Size" },
51 { kpidType, L"Type" },
52 { kpidIsAnti, L"Anti" },
53 { kpidMethod, L"Method" },
54 { kpidHostOS, L"Host OS" },
55 { kpidFileSystem, L"File System" },
56 { kpidUser, L"User" },
57 { kpidGroup, L"Group" },
58 { kpidBlock, L"Block" },
59 { kpidComment, L"Comment" },
60 { kpidPosition, L"Position" },
61 { kpidPrefix, L"Prefix" },
62 { kpidNumSubFolders, L"Folders" },
63 { kpidNumSubFiles, L"Files" },
64 { kpidUnpackVer, L"Version" },
65 { kpidVolume, L"Volume" },
66 { kpidIsVolume, L"Multivolume" },
67 { kpidOffset, L"Offset" },
68 { kpidLinks, L"Links" },
69 { kpidNumBlocks, L"Blocks" },
70 { kpidNumVolumes, L"Volumes" }
73 static const char kEmptyAttributeChar = '.';
74 static const char kDirectoryAttributeChar = 'D';
75 static const char kReadonlyAttributeChar = 'R';
76 static const char kHiddenAttributeChar = 'H';
77 static const char kSystemAttributeChar = 'S';
78 static const char kArchiveAttributeChar = 'A';
80 static const char *kListing = "Listing archive: ";
81 static const wchar_t *kFilesMessage = L"files";
82 static const wchar_t *kDirsMessage = L"folders";
84 static void GetAttributesString(DWORD wa, bool directory, char *s)
86 s[0] = ((wa & FILE_ATTRIBUTE_DIRECTORY) != 0 || directory) ?
87 kDirectoryAttributeChar: kEmptyAttributeChar;
88 s[1] = ((wa & FILE_ATTRIBUTE_READONLY) != 0)?
89 kReadonlyAttributeChar: kEmptyAttributeChar;
90 s[2] = ((wa & FILE_ATTRIBUTE_HIDDEN) != 0) ?
91 kHiddenAttributeChar: kEmptyAttributeChar;
92 s[3] = ((wa & FILE_ATTRIBUTE_SYSTEM) != 0) ?
93 kSystemAttributeChar: kEmptyAttributeChar;
94 s[4] = ((wa & FILE_ATTRIBUTE_ARCHIVE) != 0) ?
95 kArchiveAttributeChar: kEmptyAttributeChar;
110 EAdjustment TitleAdjustment;
111 EAdjustment TextAdjustment;
112 int PrefixSpacesWidth;
116 struct CFieldInfoInit
120 EAdjustment TitleAdjustment;
121 EAdjustment TextAdjustment;
122 int PrefixSpacesWidth;
126 CFieldInfoInit kStandardFieldTable[] =
128 { kpidLastWriteTime, L" Date Time", kLeft, kLeft, 0, 19 },
129 { kpidAttributes, L"Attr", kRight, kCenter, 1, 5 },
130 { kpidSize, L"Size", kRight, kRight, 1, 12 },
131 { kpidPackedSize, L"Compressed", kRight, kRight, 1, 12 },
132 { kpidPath, L"Name", kLeft, kLeft, 2, 24 }
135 void PrintSpaces(int numSpaces)
137 for (int i = 0; i < numSpaces; i++)
141 void PrintString(EAdjustment adjustment, int width, const UString &textString)
143 const int numSpaces = width - textString.Length();
144 int numLeftSpaces = 0;
151 numLeftSpaces = numSpaces / 2;
154 numLeftSpaces = numSpaces;
157 PrintSpaces(numLeftSpaces);
158 g_StdOut << textString;
159 PrintSpaces(numSpaces - numLeftSpaces);
164 CObjectVector<CFieldInfo> _fields;
166 void Clear() { _fields.Clear(); }
167 void Init(const CFieldInfoInit *standardFieldTable, int numItems);
168 HRESULT Init(IInArchive *archive);
170 void PrintTitleLines();
171 HRESULT PrintItemInfo(IInArchive *archive,
172 const UString &defaultItemName,
173 const NWindows::NFile::NFind::CFileInfoW &archiveFileInfo,
176 HRESULT PrintSummaryInfo(UInt64 numFiles, UInt64 numDirs,
177 const UInt64 *size, const UInt64 *compressedSize);
180 void CFieldPrinter::Init(const CFieldInfoInit *standardFieldTable, int numItems)
183 for (int i = 0; i < numItems; i++)
185 CFieldInfo fieldInfo;
186 const CFieldInfoInit &fieldInfoInit = standardFieldTable[i];
187 fieldInfo.PropID = fieldInfoInit.PropID;
188 fieldInfo.Name = fieldInfoInit.Name;
189 fieldInfo.TitleAdjustment = fieldInfoInit.TitleAdjustment;
190 fieldInfo.TextAdjustment = fieldInfoInit.TextAdjustment;
191 fieldInfo.PrefixSpacesWidth = fieldInfoInit.PrefixSpacesWidth;
192 fieldInfo.Width = fieldInfoInit.Width;
193 _fields.Add(fieldInfo);
197 static UString GetPropName(PROPID propID, BSTR name)
199 for (int i = 0; i < sizeof(kPropIdToName) / sizeof(kPropIdToName[0]); i++)
201 const CPropIdToName &propIdToName = kPropIdToName[i];
202 if (propIdToName.PropID == propID)
203 return propIdToName.Name;
210 HRESULT CFieldPrinter::Init(IInArchive *archive)
214 RINOK(archive->GetNumberOfProperties(&numProps));
215 for (UInt32 i = 0; i < numProps; i++)
220 RINOK(archive->GetPropertyInfo(i, &name, &propID, &vt));
221 CFieldInfo fieldInfo;
222 fieldInfo.PropID = propID;
223 fieldInfo.Name = GetPropName(propID, name);
224 _fields.Add(fieldInfo);
229 void CFieldPrinter::PrintTitle()
231 for (int i = 0; i < _fields.Size(); i++)
233 const CFieldInfo &fieldInfo = _fields[i];
234 PrintSpaces(fieldInfo.PrefixSpacesWidth);
235 PrintString(fieldInfo.TitleAdjustment,
236 ((fieldInfo.PropID == kpidPath) ? 0: fieldInfo.Width), fieldInfo.Name);
240 void CFieldPrinter::PrintTitleLines()
242 for (int i = 0; i < _fields.Size(); i++)
244 const CFieldInfo &fieldInfo = _fields[i];
245 PrintSpaces(fieldInfo.PrefixSpacesWidth);
246 for (int i = 0; i < fieldInfo.Width; i++)
252 BOOL IsFileTimeZero(CONST FILETIME *lpFileTime)
254 return (lpFileTime->dwLowDateTime == 0) && (lpFileTime->dwHighDateTime == 0);
257 static const char *kEmptyTimeString = " ";
258 void PrintTime(const NCOM::CPropVariant &propVariant)
260 if (propVariant.vt != VT_FILETIME)
261 throw "incorrect item";
262 if (IsFileTimeZero(&propVariant.filetime))
263 g_StdOut << kEmptyTimeString;
266 FILETIME localFileTime;
267 if (!FileTimeToLocalFileTime(&propVariant.filetime, &localFileTime))
268 throw "FileTimeToLocalFileTime error";
270 if (ConvertFileTimeToString(localFileTime, s, true, true))
273 g_StdOut << kEmptyTimeString;
277 HRESULT CFieldPrinter::PrintItemInfo(IInArchive *archive,
278 const UString &defaultItemName,
279 const NWindows::NFile::NFind::CFileInfoW &archiveFileInfo,
286 g_StdOut << "Index = ";
287 g_StdOut << (UInt64)index;
291 for (int i = 0; i < _fields.Size(); i++)
293 const CFieldInfo &fieldInfo = _fields[i];
295 PrintSpaces(fieldInfo.PrefixSpacesWidth);
297 NCOM::CPropVariant propVariant;
298 RINOK(archive->GetProperty(index, fieldInfo.PropID, &propVariant));
301 g_StdOut << fieldInfo.Name << " = ";
303 int width = (fieldInfo.PropID == kpidPath) ? 0: fieldInfo.Width;
304 if (propVariant.vt == VT_EMPTY)
306 switch(fieldInfo.PropID)
309 propVariant = defaultItemName;
311 case kpidLastWriteTime:
312 propVariant = archiveFileInfo.LastWriteTime;
322 if (fieldInfo.PropID == kpidLastWriteTime)
324 PrintTime(propVariant);
326 else if (fieldInfo.PropID == kpidAttributes)
328 if (propVariant.vt != VT_UI4)
329 throw "incorrect item";
330 UInt32 attributes = propVariant.ulVal;
332 RINOK(IsArchiveItemFolder(archive, index, isFolder));
334 GetAttributesString(attributes, isFolder, s);
337 else if (propVariant.vt == VT_BSTR)
340 g_StdOut << propVariant.bstrVal;
342 PrintString(fieldInfo.TextAdjustment, width, propVariant.bstrVal);
346 UString s = ConvertPropertyToString(propVariant, fieldInfo.PropID);
347 s.Replace(wchar_t(0xA), L' ');
348 s.Replace(wchar_t(0xD), L' ');
353 PrintString(fieldInfo.TextAdjustment, width, s);
361 void PrintNumberString(EAdjustment adjustment, int width, const UInt64 *value)
363 wchar_t textString[32] = { 0 };
365 ConvertUInt64ToString(*value, textString);
366 PrintString(adjustment, width, textString);
370 HRESULT CFieldPrinter::PrintSummaryInfo(UInt64 numFiles, UInt64 numDirs,
371 const UInt64 *size, const UInt64 *compressedSize)
373 for (int i = 0; i < _fields.Size(); i++)
375 const CFieldInfo &fieldInfo = _fields[i];
376 PrintSpaces(fieldInfo.PrefixSpacesWidth);
377 NCOM::CPropVariant propVariant;
378 if (fieldInfo.PropID == kpidSize)
379 PrintNumberString(fieldInfo.TextAdjustment, fieldInfo.Width, size);
380 else if (fieldInfo.PropID == kpidPackedSize)
381 PrintNumberString(fieldInfo.TextAdjustment, fieldInfo.Width, compressedSize);
382 else if (fieldInfo.PropID == kpidPath)
384 wchar_t textString[32];
385 ConvertUInt64ToString(numFiles, textString);
386 UString temp = textString;
388 temp += kFilesMessage;
390 ConvertUInt64ToString(numDirs, textString);
393 temp += kDirsMessage;
394 PrintString(fieldInfo.TextAdjustment, 0, temp);
397 PrintString(fieldInfo.TextAdjustment, fieldInfo.Width, L"");
402 bool GetUInt64Value(IInArchive *archive, UInt32 index, PROPID propID, UInt64 &value)
404 NCOM::CPropVariant propVariant;
405 if (archive->GetProperty(index, propID, &propVariant) != S_OK)
406 throw "GetPropertyValue error";
407 if (propVariant.vt == VT_EMPTY)
409 value = ConvertPropVariantToUInt64(propVariant);
413 HRESULT ListArchives(
415 UStringVector &archivePaths, UStringVector &archivePathsFull,
416 const NWildcard::CCensorNode &wildcardCensor,
417 bool enableHeaders, bool techMode, bool &passwordEnabled, UString &password, UInt64 &numErrors)
420 CFieldPrinter fieldPrinter;
422 fieldPrinter.Init(kStandardFieldTable, sizeof(kStandardFieldTable) / sizeof(kStandardFieldTable[0]));
424 UInt64 numFiles2 = 0, numDirs2 = 0, totalPackSize2 = 0, totalUnPackSize2 = 0;
425 UInt64 *totalPackSizePointer2 = 0, *totalUnPackSizePointer2 = 0;
426 for (int i = 0; i < archivePaths.Size(); i++)
428 const UString &archiveName = archivePaths[i];
429 NFile::NFind::CFileInfoW archiveFileInfo;
430 if (!NFile::NFind::FindFile(archiveName, archiveFileInfo) || archiveFileInfo.IsDirectory())
432 g_StdOut << endl << "Error: " << archiveName << " is not archive" << endl;
436 if (archiveFileInfo.IsDirectory())
438 g_StdOut << endl << "Error: " << archiveName << " is not file" << endl;
443 CArchiveLink archiveLink;
445 COpenCallbackConsole openCallback;
446 openCallback.OutStream = &g_StdOut;
447 openCallback.PasswordIsDefined = passwordEnabled;
448 openCallback.Password = password;
450 HRESULT result = MyOpenArchive(codecs, archiveName, archiveLink, &openCallback);
453 g_StdOut << endl << "Error: " << archiveName << " is not supported archive" << endl;
458 for (int v = 0; v < archiveLink.VolumePaths.Size(); v++)
460 int index = archivePathsFull.FindInSorted(archiveLink.VolumePaths[v]);
461 if (index >= 0 && index > i)
463 archivePaths.Delete(index);
464 archivePathsFull.Delete(index);
468 IInArchive *archive = archiveLink.GetArchive();
469 const UString defaultItemName = archiveLink.GetDefaultItemName();
473 g_StdOut << endl << kListing << archiveName << endl << endl;
476 if (archive->GetNumberOfArchiveProperties(&numProps) == S_OK)
478 for (UInt32 i = 0; i < numProps; i++)
483 if (archive->GetArchivePropertyInfo(i, &name, &propID, &vt) != S_OK)
485 NCOM::CPropVariant prop;
486 if (archive->GetArchiveProperty(propID, &prop) != S_OK)
488 UString s = ConvertPropertyToString(prop, propID);
490 g_StdOut << GetPropName(propID, name) << " = " << s << endl;
494 g_StdOut << "----------\n";
499 if (enableHeaders && !techMode)
501 fieldPrinter.PrintTitle();
503 fieldPrinter.PrintTitleLines();
509 RINOK(fieldPrinter.Init(archive));
511 UInt64 numFiles = 0, numDirs = 0, totalPackSize = 0, totalUnPackSize = 0;
512 UInt64 *totalPackSizePointer = 0, *totalUnPackSizePointer = 0;
514 RINOK(archive->GetNumberOfItems(&numItems));
515 for(UInt32 i = 0; i < numItems; i++)
517 if (NConsoleClose::TestBreakSignal())
521 RINOK(GetArchiveItemPath(archive, i, defaultItemName, filePath));
524 RINOK(IsArchiveItemFolder(archive, i, isFolder));
525 if (!wildcardCensor.CheckPath(filePath, !isFolder))
528 fieldPrinter.PrintItemInfo(archive, defaultItemName, archiveFileInfo, i, techMode);
530 UInt64 packSize, unpackSize;
531 if (!GetUInt64Value(archive, i, kpidSize, unpackSize))
534 totalUnPackSizePointer = &totalUnPackSize;
535 if (!GetUInt64Value(archive, i, kpidPackedSize, packSize))
538 totalPackSizePointer = &totalPackSize;
546 totalPackSize += packSize;
547 totalUnPackSize += unpackSize;
549 if (enableHeaders && !techMode)
551 fieldPrinter.PrintTitleLines();
553 fieldPrinter.PrintSummaryInfo(numFiles, numDirs, totalUnPackSizePointer, totalPackSizePointer);
556 if (totalPackSizePointer != 0)
558 totalPackSizePointer2 = &totalPackSize2;
559 totalPackSize2 += totalPackSize;
561 if (totalUnPackSizePointer != 0)
563 totalUnPackSizePointer2 = &totalUnPackSize2;
564 totalUnPackSize2 += totalUnPackSize;
566 numFiles2 += numFiles;
569 if (enableHeaders && !techMode && archivePaths.Size() > 1)
572 fieldPrinter.PrintTitleLines();
574 fieldPrinter.PrintSummaryInfo(numFiles2, numDirs2, totalUnPackSizePointer2, totalPackSizePointer2);
576 g_StdOut << "Archives: " << archivePaths.Size() << endl;