// List.cpp | |
#include "StdAfx.h" | |
#include "Common/IntToString.h" | |
#include "Common/MyCom.h" | |
#include "Common/StdOutStream.h" | |
#include "Common/StringConvert.h" | |
#include "Windows/Error.h" | |
#include "Windows/FileDir.h" | |
#include "Windows/PropVariant.h" | |
#include "Windows/PropVariantConversions.h" | |
#include "../../Archive/IArchive.h" | |
#include "../Common/OpenArchive.h" | |
#include "../Common/PropIDUtils.h" | |
#include "ConsoleClose.h" | |
#include "List.h" | |
#include "OpenCallbackConsole.h" | |
using namespace NWindows; | |
struct CPropIdToName | |
{ | |
PROPID PropID; | |
const wchar_t *Name; | |
}; | |
static const CPropIdToName kPropIdToName[] = | |
{ | |
{ kpidPath, L"Path" }, | |
{ kpidName, L"Name" }, | |
{ kpidIsDir, L"Folder" }, | |
{ kpidSize, L"Size" }, | |
{ kpidPackSize, L"Packed Size" }, | |
{ kpidAttrib, L"Attributes" }, | |
{ kpidCTime, L"Created" }, | |
{ kpidATime, L"Accessed" }, | |
{ kpidMTime, L"Modified" }, | |
{ kpidSolid, L"Solid" }, | |
{ kpidCommented, L"Commented" }, | |
{ kpidEncrypted, L"Encrypted" }, | |
{ kpidSplitBefore, L"Split Before" }, | |
{ kpidSplitAfter, L"Split After" }, | |
{ kpidDictionarySize, L"Dictionary Size" }, | |
{ kpidCRC, L"CRC" }, | |
{ kpidType, L"Type" }, | |
{ kpidIsAnti, L"Anti" }, | |
{ kpidMethod, L"Method" }, | |
{ kpidHostOS, L"Host OS" }, | |
{ kpidFileSystem, L"File System" }, | |
{ kpidUser, L"User" }, | |
{ kpidGroup, L"Group" }, | |
{ kpidBlock, L"Block" }, | |
{ kpidComment, L"Comment" }, | |
{ kpidPosition, L"Position" }, | |
{ kpidPrefix, L"Prefix" }, | |
{ kpidNumSubDirs, L"Folders" }, | |
{ kpidNumSubFiles, L"Files" }, | |
{ kpidUnpackVer, L"Version" }, | |
{ kpidVolume, L"Volume" }, | |
{ kpidIsVolume, L"Multivolume" }, | |
{ kpidOffset, L"Offset" }, | |
{ kpidLinks, L"Links" }, | |
{ kpidNumBlocks, L"Blocks" }, | |
{ kpidNumVolumes, L"Volumes" }, | |
{ kpidBit64, L"64-bit" }, | |
{ kpidBigEndian, L"Big-endian" }, | |
{ kpidCpu, L"CPU" }, | |
{ kpidPhySize, L"Physical Size" }, | |
{ kpidHeadersSize, L"Headers Size" }, | |
{ kpidChecksum, L"Checksum" }, | |
{ kpidCharacts, L"Characteristics" }, | |
{ kpidVa, L"Virtual Address" }, | |
{ kpidId, L"ID" }, | |
{ kpidShortName, L"Short Name" }, | |
{ kpidCreatorApp, L"Creator Application"}, | |
{ kpidSectorSize, L"Sector Size" }, | |
{ kpidPosixAttrib, L"Mode" }, | |
{ kpidLink, L"Link" }, | |
{ kpidError, L"Error" }, | |
{ kpidTotalSize, L"Total Size" }, | |
{ kpidFreeSpace, L"Free Space" }, | |
{ kpidClusterSize, L"Cluster Size" }, | |
{ kpidVolumeName, L"Label" } | |
}; | |
static const char kEmptyAttribChar = '.'; | |
static const char *kListing = "Listing archive: "; | |
static const wchar_t *kFilesMessage = L"files"; | |
static const wchar_t *kDirsMessage = L"folders"; | |
static void GetAttribString(DWORD wa, bool isDir, char *s) | |
{ | |
s[0] = ((wa & FILE_ATTRIBUTE_DIRECTORY) != 0 || isDir) ? 'D' : kEmptyAttribChar; | |
s[1] = ((wa & FILE_ATTRIBUTE_READONLY) != 0) ? 'R': kEmptyAttribChar; | |
s[2] = ((wa & FILE_ATTRIBUTE_HIDDEN) != 0) ? 'H': kEmptyAttribChar; | |
s[3] = ((wa & FILE_ATTRIBUTE_SYSTEM) != 0) ? 'S': kEmptyAttribChar; | |
s[4] = ((wa & FILE_ATTRIBUTE_ARCHIVE) != 0) ? 'A': kEmptyAttribChar; | |
s[5] = '\0'; | |
} | |
enum EAdjustment | |
{ | |
kLeft, | |
kCenter, | |
kRight | |
}; | |
struct CFieldInfo | |
{ | |
PROPID PropID; | |
UString Name; | |
EAdjustment TitleAdjustment; | |
EAdjustment TextAdjustment; | |
int PrefixSpacesWidth; | |
int Width; | |
}; | |
struct CFieldInfoInit | |
{ | |
PROPID PropID; | |
const wchar_t *Name; | |
EAdjustment TitleAdjustment; | |
EAdjustment TextAdjustment; | |
int PrefixSpacesWidth; | |
int Width; | |
}; | |
static CFieldInfoInit kStandardFieldTable[] = | |
{ | |
{ kpidMTime, L" Date Time", kLeft, kLeft, 0, 19 }, | |
{ kpidAttrib, L"Attr", kRight, kCenter, 1, 5 }, | |
{ kpidSize, L"Size", kRight, kRight, 1, 12 }, | |
{ kpidPackSize, L"Compressed", kRight, kRight, 1, 12 }, | |
{ kpidPath, L"Name", kLeft, kLeft, 2, 24 } | |
}; | |
static void PrintSpaces(int numSpaces) | |
{ | |
for (int i = 0; i < numSpaces; i++) | |
g_StdOut << ' '; | |
} | |
static void PrintString(EAdjustment adjustment, int width, const UString &textString) | |
{ | |
const int numSpaces = width - textString.Length(); | |
int numLeftSpaces = 0; | |
switch (adjustment) | |
{ | |
case kLeft: | |
numLeftSpaces = 0; | |
break; | |
case kCenter: | |
numLeftSpaces = numSpaces / 2; | |
break; | |
case kRight: | |
numLeftSpaces = numSpaces; | |
break; | |
} | |
PrintSpaces(numLeftSpaces); | |
g_StdOut << textString; | |
PrintSpaces(numSpaces - numLeftSpaces); | |
} | |
class CFieldPrinter | |
{ | |
CObjectVector<CFieldInfo> _fields; | |
public: | |
void Clear() { _fields.Clear(); } | |
void Init(const CFieldInfoInit *standardFieldTable, int numItems); | |
HRESULT Init(IInArchive *archive); | |
void PrintTitle(); | |
void PrintTitleLines(); | |
HRESULT PrintItemInfo(const CArc &arc, UInt32 index, bool techMode); | |
HRESULT PrintSummaryInfo(UInt64 numFiles, UInt64 numDirs, | |
const UInt64 *size, const UInt64 *compressedSize); | |
}; | |
void CFieldPrinter::Init(const CFieldInfoInit *standardFieldTable, int numItems) | |
{ | |
Clear(); | |
for (int i = 0; i < numItems; i++) | |
{ | |
CFieldInfo fieldInfo; | |
const CFieldInfoInit &fieldInfoInit = standardFieldTable[i]; | |
fieldInfo.PropID = fieldInfoInit.PropID; | |
fieldInfo.Name = fieldInfoInit.Name; | |
fieldInfo.TitleAdjustment = fieldInfoInit.TitleAdjustment; | |
fieldInfo.TextAdjustment = fieldInfoInit.TextAdjustment; | |
fieldInfo.PrefixSpacesWidth = fieldInfoInit.PrefixSpacesWidth; | |
fieldInfo.Width = fieldInfoInit.Width; | |
_fields.Add(fieldInfo); | |
} | |
} | |
static UString GetPropName(PROPID propID, BSTR name) | |
{ | |
for (int i = 0; i < sizeof(kPropIdToName) / sizeof(kPropIdToName[0]); i++) | |
{ | |
const CPropIdToName &propIdToName = kPropIdToName[i]; | |
if (propIdToName.PropID == propID) | |
return propIdToName.Name; | |
} | |
if (name) | |
return name; | |
wchar_t s[16]; | |
ConvertUInt32ToString(propID, s); | |
return s; | |
} | |
HRESULT CFieldPrinter::Init(IInArchive *archive) | |
{ | |
Clear(); | |
UInt32 numProps; | |
RINOK(archive->GetNumberOfProperties(&numProps)); | |
for (UInt32 i = 0; i < numProps; i++) | |
{ | |
CMyComBSTR name; | |
PROPID propID; | |
VARTYPE vt; | |
RINOK(archive->GetPropertyInfo(i, &name, &propID, &vt)); | |
CFieldInfo fieldInfo; | |
fieldInfo.PropID = propID; | |
fieldInfo.Name = GetPropName(propID, name); | |
_fields.Add(fieldInfo); | |
} | |
return S_OK; | |
} | |
void CFieldPrinter::PrintTitle() | |
{ | |
for (int i = 0; i < _fields.Size(); i++) | |
{ | |
const CFieldInfo &fieldInfo = _fields[i]; | |
PrintSpaces(fieldInfo.PrefixSpacesWidth); | |
PrintString(fieldInfo.TitleAdjustment, | |
((fieldInfo.PropID == kpidPath) ? 0: fieldInfo.Width), fieldInfo.Name); | |
} | |
} | |
void CFieldPrinter::PrintTitleLines() | |
{ | |
for (int i = 0; i < _fields.Size(); i++) | |
{ | |
const CFieldInfo &fieldInfo = _fields[i]; | |
PrintSpaces(fieldInfo.PrefixSpacesWidth); | |
for (int i = 0; i < fieldInfo.Width; i++) | |
g_StdOut << '-'; | |
} | |
} | |
static BOOL IsFileTimeZero(CONST FILETIME *lpFileTime) | |
{ | |
return (lpFileTime->dwLowDateTime == 0) && (lpFileTime->dwHighDateTime == 0); | |
} | |
static const char *kEmptyTimeString = " "; | |
static void PrintTime(const NCOM::CPropVariant &prop) | |
{ | |
if (prop.vt != VT_FILETIME) | |
throw "incorrect item"; | |
if (IsFileTimeZero(&prop.filetime)) | |
g_StdOut << kEmptyTimeString; | |
else | |
{ | |
FILETIME localFileTime; | |
if (!FileTimeToLocalFileTime(&prop.filetime, &localFileTime)) | |
throw "FileTimeToLocalFileTime error"; | |
char s[32]; | |
if (ConvertFileTimeToString(localFileTime, s, true, true)) | |
g_StdOut << s; | |
else | |
g_StdOut << kEmptyTimeString; | |
} | |
} | |
HRESULT CFieldPrinter::PrintItemInfo(const CArc &arc, UInt32 index, bool techMode) | |
{ | |
/* | |
if (techMode) | |
{ | |
g_StdOut << "Index = "; | |
g_StdOut << (UInt64)index; | |
g_StdOut << endl; | |
} | |
*/ | |
for (int i = 0; i < _fields.Size(); i++) | |
{ | |
const CFieldInfo &fieldInfo = _fields[i]; | |
if (!techMode) | |
PrintSpaces(fieldInfo.PrefixSpacesWidth); | |
NCOM::CPropVariant prop; | |
if (fieldInfo.PropID == kpidPath) | |
{ | |
UString s; | |
RINOK(arc.GetItemPath(index, s)); | |
prop = s; | |
} | |
else | |
{ | |
RINOK(arc.Archive->GetProperty(index, fieldInfo.PropID, &prop)); | |
} | |
if (techMode) | |
{ | |
g_StdOut << fieldInfo.Name << " = "; | |
} | |
int width = (fieldInfo.PropID == kpidPath) ? 0: fieldInfo.Width; | |
if (fieldInfo.PropID == kpidAttrib && (prop.vt == VT_EMPTY || prop.vt == VT_UI4)) | |
{ | |
UInt32 attrib = (prop.vt == VT_EMPTY) ? 0 : prop.ulVal; | |
bool isFolder; | |
RINOK(IsArchiveItemFolder(arc.Archive, index, isFolder)); | |
char s[8]; | |
GetAttribString(attrib, isFolder, s); | |
g_StdOut << s; | |
} | |
else if (prop.vt == VT_EMPTY) | |
{ | |
if (!techMode) | |
PrintSpaces(width); | |
} | |
else if (fieldInfo.PropID == kpidMTime) | |
{ | |
PrintTime(prop); | |
} | |
else if (prop.vt == VT_BSTR) | |
{ | |
if (techMode) | |
g_StdOut << prop.bstrVal; | |
else | |
PrintString(fieldInfo.TextAdjustment, width, prop.bstrVal); | |
} | |
else | |
{ | |
UString s = ConvertPropertyToString(prop, fieldInfo.PropID); | |
s.Replace(wchar_t(0xA), L' '); | |
s.Replace(wchar_t(0xD), L' '); | |
if (techMode) | |
g_StdOut << s; | |
else | |
PrintString(fieldInfo.TextAdjustment, width, s); | |
} | |
if (techMode) | |
g_StdOut << endl; | |
} | |
return S_OK; | |
} | |
static void PrintNumberString(EAdjustment adjustment, int width, const UInt64 *value) | |
{ | |
wchar_t textString[32] = { 0 }; | |
if (value != NULL) | |
ConvertUInt64ToString(*value, textString); | |
PrintString(adjustment, width, textString); | |
} | |
HRESULT CFieldPrinter::PrintSummaryInfo(UInt64 numFiles, UInt64 numDirs, | |
const UInt64 *size, const UInt64 *compressedSize) | |
{ | |
for (int i = 0; i < _fields.Size(); i++) | |
{ | |
const CFieldInfo &fieldInfo = _fields[i]; | |
PrintSpaces(fieldInfo.PrefixSpacesWidth); | |
NCOM::CPropVariant prop; | |
if (fieldInfo.PropID == kpidSize) | |
PrintNumberString(fieldInfo.TextAdjustment, fieldInfo.Width, size); | |
else if (fieldInfo.PropID == kpidPackSize) | |
PrintNumberString(fieldInfo.TextAdjustment, fieldInfo.Width, compressedSize); | |
else if (fieldInfo.PropID == kpidPath) | |
{ | |
wchar_t textString[32]; | |
ConvertUInt64ToString(numFiles, textString); | |
UString temp = textString; | |
temp += L" "; | |
temp += kFilesMessage; | |
temp += L", "; | |
ConvertUInt64ToString(numDirs, textString); | |
temp += textString; | |
temp += L" "; | |
temp += kDirsMessage; | |
PrintString(fieldInfo.TextAdjustment, 0, temp); | |
} | |
else | |
PrintString(fieldInfo.TextAdjustment, fieldInfo.Width, L""); | |
} | |
return S_OK; | |
} | |
bool GetUInt64Value(IInArchive *archive, UInt32 index, PROPID propID, UInt64 &value) | |
{ | |
NCOM::CPropVariant prop; | |
if (archive->GetProperty(index, propID, &prop) != S_OK) | |
throw "GetPropertyValue error"; | |
if (prop.vt == VT_EMPTY) | |
return false; | |
value = ConvertPropVariantToUInt64(prop); | |
return true; | |
} | |
static void PrintPropPair(const wchar_t *name, const wchar_t *value) | |
{ | |
g_StdOut << name << " = " << value << endl; | |
} | |
HRESULT ListArchives(CCodecs *codecs, const CIntVector &formatIndices, | |
bool stdInMode, | |
UStringVector &arcPaths, UStringVector &arcPathsFull, | |
const NWildcard::CCensorNode &wildcardCensor, | |
bool enableHeaders, bool techMode, | |
#ifndef _NO_CRYPTO | |
bool &passwordEnabled, UString &password, | |
#endif | |
UInt64 &numErrors) | |
{ | |
numErrors = 0; | |
CFieldPrinter fieldPrinter; | |
if (!techMode) | |
fieldPrinter.Init(kStandardFieldTable, sizeof(kStandardFieldTable) / sizeof(kStandardFieldTable[0])); | |
UInt64 numFiles2 = 0, numDirs2 = 0, totalPackSize2 = 0, totalUnPackSize2 = 0; | |
UInt64 *totalPackSizePointer2 = 0, *totalUnPackSizePointer2 = 0; | |
int numArcs = /* stdInMode ? 1 : */ arcPaths.Size(); | |
for (int i = 0; i < numArcs; i++) | |
{ | |
const UString &archiveName = arcPaths[i]; | |
UInt64 arcPackSize = 0; | |
if (!stdInMode) | |
{ | |
NFile::NFind::CFileInfoW fi; | |
if (!fi.Find(archiveName) || fi.IsDir()) | |
{ | |
g_StdOut << endl << "Error: " << archiveName << " is not file" << endl; | |
numErrors++; | |
continue; | |
} | |
arcPackSize = fi.Size; | |
} | |
CArchiveLink archiveLink; | |
COpenCallbackConsole openCallback; | |
openCallback.OutStream = &g_StdOut; | |
#ifndef _NO_CRYPTO | |
openCallback.PasswordIsDefined = passwordEnabled; | |
openCallback.Password = password; | |
#endif | |
HRESULT result = archiveLink.Open2(codecs, formatIndices, stdInMode, NULL, archiveName, &openCallback); | |
if (result != S_OK) | |
{ | |
if (result == E_ABORT) | |
return result; | |
g_StdOut << endl << "Error: " << archiveName << ": "; | |
if (result == S_FALSE) | |
{ | |
#ifndef _NO_CRYPTO | |
if (openCallback.Open_WasPasswordAsked()) | |
g_StdOut << "Can not open encrypted archive. Wrong password?"; | |
else | |
#endif | |
g_StdOut << "Can not open file as archive"; | |
} | |
else if (result == E_OUTOFMEMORY) | |
g_StdOut << "Can't allocate required memory"; | |
else | |
g_StdOut << NError::MyFormatMessage(result); | |
g_StdOut << endl; | |
numErrors++; | |
continue; | |
} | |
if (!stdInMode) | |
for (int v = 0; v < archiveLink.VolumePaths.Size(); v++) | |
{ | |
int index = arcPathsFull.FindInSorted(archiveLink.VolumePaths[v]); | |
if (index >= 0 && index > i) | |
{ | |
arcPaths.Delete(index); | |
arcPathsFull.Delete(index); | |
numArcs = arcPaths.Size(); | |
} | |
} | |
if (enableHeaders) | |
{ | |
g_StdOut << endl << kListing << archiveName << endl << endl; | |
for (int i = 0; i < archiveLink.Arcs.Size(); i++) | |
{ | |
const CArc &arc = archiveLink.Arcs[i]; | |
g_StdOut << "--\n"; | |
PrintPropPair(L"Path", arc.Path); | |
PrintPropPair(L"Type", codecs->Formats[arc.FormatIndex].Name); | |
if (!arc.ErrorMessage.IsEmpty()) | |
PrintPropPair(L"Error", arc.ErrorMessage); | |
UInt32 numProps; | |
IInArchive *archive = arc.Archive; | |
if (archive->GetNumberOfArchiveProperties(&numProps) == S_OK) | |
{ | |
for (UInt32 j = 0; j < numProps; j++) | |
{ | |
CMyComBSTR name; | |
PROPID propID; | |
VARTYPE vt; | |
RINOK(archive->GetArchivePropertyInfo(j, &name, &propID, &vt)); | |
NCOM::CPropVariant prop; | |
RINOK(archive->GetArchiveProperty(propID, &prop)); | |
UString s = ConvertPropertyToString(prop, propID); | |
if (!s.IsEmpty()) | |
PrintPropPair(GetPropName(propID, name), s); | |
} | |
} | |
if (i != archiveLink.Arcs.Size() - 1) | |
{ | |
UInt32 numProps; | |
g_StdOut << "----\n"; | |
if (archive->GetNumberOfProperties(&numProps) == S_OK) | |
{ | |
UInt32 mainIndex = archiveLink.Arcs[i + 1].SubfileIndex; | |
for (UInt32 j = 0; j < numProps; j++) | |
{ | |
CMyComBSTR name; | |
PROPID propID; | |
VARTYPE vt; | |
RINOK(archive->GetPropertyInfo(j, &name, &propID, &vt)); | |
NCOM::CPropVariant prop; | |
RINOK(archive->GetProperty(mainIndex, propID, &prop)); | |
UString s = ConvertPropertyToString(prop, propID); | |
if (!s.IsEmpty()) | |
PrintPropPair(GetPropName(propID, name), s); | |
} | |
} | |
} | |
} | |
g_StdOut << endl; | |
if (techMode) | |
g_StdOut << "----------\n"; | |
} | |
if (enableHeaders && !techMode) | |
{ | |
fieldPrinter.PrintTitle(); | |
g_StdOut << endl; | |
fieldPrinter.PrintTitleLines(); | |
g_StdOut << endl; | |
} | |
const CArc &arc = archiveLink.Arcs.Back(); | |
IInArchive *archive = arc.Archive; | |
if (techMode) | |
{ | |
RINOK(fieldPrinter.Init(archive)); | |
} | |
UInt64 numFiles = 0, numDirs = 0, totalPackSize = 0, totalUnPackSize = 0; | |
UInt64 *totalPackSizePointer = 0, *totalUnPackSizePointer = 0; | |
UInt32 numItems; | |
RINOK(archive->GetNumberOfItems(&numItems)); | |
for (UInt32 i = 0; i < numItems; i++) | |
{ | |
if (NConsoleClose::TestBreakSignal()) | |
return E_ABORT; | |
UString filePath; | |
HRESULT res = arc.GetItemPath(i, filePath); | |
if (stdInMode && res == E_INVALIDARG) | |
break; | |
RINOK(res); | |
bool isFolder; | |
RINOK(IsArchiveItemFolder(archive, i, isFolder)); | |
if (!wildcardCensor.CheckPath(filePath, !isFolder)) | |
continue; | |
fieldPrinter.PrintItemInfo(arc, i, techMode); | |
UInt64 packSize, unpackSize; | |
if (!GetUInt64Value(archive, i, kpidSize, unpackSize)) | |
unpackSize = 0; | |
else | |
totalUnPackSizePointer = &totalUnPackSize; | |
if (!GetUInt64Value(archive, i, kpidPackSize, packSize)) | |
packSize = 0; | |
else | |
totalPackSizePointer = &totalPackSize; | |
g_StdOut << endl; | |
if (isFolder) | |
numDirs++; | |
else | |
numFiles++; | |
totalPackSize += packSize; | |
totalUnPackSize += unpackSize; | |
} | |
if (!stdInMode && totalPackSizePointer == 0) | |
{ | |
if (archiveLink.VolumePaths.Size() != 0) | |
arcPackSize += archiveLink.VolumesSize; | |
totalPackSize = (numFiles == 0) ? 0 : arcPackSize; | |
totalPackSizePointer = &totalPackSize; | |
} | |
if (totalUnPackSizePointer == 0 && numFiles == 0) | |
{ | |
totalUnPackSize = 0; | |
totalUnPackSizePointer = &totalUnPackSize; | |
} | |
if (enableHeaders && !techMode) | |
{ | |
fieldPrinter.PrintTitleLines(); | |
g_StdOut << endl; | |
fieldPrinter.PrintSummaryInfo(numFiles, numDirs, totalUnPackSizePointer, totalPackSizePointer); | |
g_StdOut << endl; | |
} | |
if (totalPackSizePointer != 0) | |
{ | |
totalPackSizePointer2 = &totalPackSize2; | |
totalPackSize2 += totalPackSize; | |
} | |
if (totalUnPackSizePointer != 0) | |
{ | |
totalUnPackSizePointer2 = &totalUnPackSize2; | |
totalUnPackSize2 += totalUnPackSize; | |
} | |
numFiles2 += numFiles; | |
numDirs2 += numDirs; | |
} | |
if (enableHeaders && !techMode && numArcs > 1) | |
{ | |
g_StdOut << endl; | |
fieldPrinter.PrintTitleLines(); | |
g_StdOut << endl; | |
fieldPrinter.PrintSummaryInfo(numFiles2, numDirs2, totalUnPackSizePointer2, totalPackSizePointer2); | |
g_StdOut << endl; | |
g_StdOut << "Archives: " << numArcs << endl; | |
} | |
return S_OK; | |
} |