List.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579
  1. // List.cpp
  2. #include "StdAfx.h"
  3. #include "List.h"
  4. #include "ConsoleClose.h"
  5. #include "Common/StringConvert.h"
  6. #include "Common/StdOutStream.h"
  7. #include "Common/IntToString.h"
  8. #include "Common/MyCom.h"
  9. #include "Windows/PropVariant.h"
  10. #include "Windows/Defs.h"
  11. #include "Windows/PropVariantConversions.h"
  12. #include "Windows/FileDir.h"
  13. #include "../../Archive/IArchive.h"
  14. #include "../Common/PropIDUtils.h"
  15. #include "../Common/OpenArchive.h"
  16. #include "OpenCallbackConsole.h"
  17. using namespace NWindows;
  18. struct CPropIdToName
  19. {
  20. PROPID PropID;
  21. const wchar_t *Name;
  22. };
  23. static CPropIdToName kPropIdToName[] =
  24. {
  25. { kpidPath, L"Path" },
  26. { kpidName, L"Name" },
  27. { kpidIsFolder, L"Folder" },
  28. { kpidSize, L"Size" },
  29. { kpidPackedSize, L"Packed Size" },
  30. { kpidAttributes, L"Attributes" },
  31. { kpidCreationTime, L"Created" },
  32. { kpidLastAccessTime, L"Accessed" },
  33. { kpidLastWriteTime, L"Modified" },
  34. { kpidSolid, L"Solid" },
  35. { kpidCommented, L"Commented" },
  36. { kpidEncrypted, L"Encrypted" },
  37. { kpidSplitBefore, L"Split Before" },
  38. { kpidSplitAfter, L"Split After" },
  39. { kpidDictionarySize, L"Dictionary Size" },
  40. { kpidCRC, L"CRC" },
  41. { kpidType, L"Type" },
  42. { kpidIsAnti, L"Anti" },
  43. { kpidMethod, L"Method" },
  44. { kpidHostOS, L"Host OS" },
  45. { kpidFileSystem, L"File System" },
  46. { kpidUser, L"User" },
  47. { kpidGroup, L"Group" },
  48. { kpidBlock, L"Block" },
  49. { kpidComment, L"Comment" },
  50. { kpidPosition, L"Position" },
  51. { kpidPrefix, L"Prefix" },
  52. { kpidNumSubFolders, L"Folders" },
  53. { kpidNumSubFiles, L"Files" },
  54. { kpidUnpackVer, L"Version" },
  55. { kpidVolume, L"Volume" },
  56. { kpidIsVolume, L"Multivolume" },
  57. { kpidOffset, L"Offset" },
  58. { kpidLinks, L"Links" },
  59. { kpidNumBlocks, L"Blocks" },
  60. { kpidNumVolumes, L"Volumes" }
  61. };
  62. static const char kEmptyAttributeChar = '.';
  63. static const char kDirectoryAttributeChar = 'D';
  64. static const char kReadonlyAttributeChar = 'R';
  65. static const char kHiddenAttributeChar = 'H';
  66. static const char kSystemAttributeChar = 'S';
  67. static const char kArchiveAttributeChar = 'A';
  68. static const char *kListing = "Listing archive: ";
  69. static const wchar_t *kFilesMessage = L"files";
  70. static const wchar_t *kDirsMessage = L"folders";
  71. static void GetAttributesString(DWORD wa, bool directory, char *s)
  72. {
  73. s[0] = ((wa & FILE_ATTRIBUTE_DIRECTORY) != 0 || directory) ?
  74. kDirectoryAttributeChar: kEmptyAttributeChar;
  75. s[1] = ((wa & FILE_ATTRIBUTE_READONLY) != 0)?
  76. kReadonlyAttributeChar: kEmptyAttributeChar;
  77. s[2] = ((wa & FILE_ATTRIBUTE_HIDDEN) != 0) ?
  78. kHiddenAttributeChar: kEmptyAttributeChar;
  79. s[3] = ((wa & FILE_ATTRIBUTE_SYSTEM) != 0) ?
  80. kSystemAttributeChar: kEmptyAttributeChar;
  81. s[4] = ((wa & FILE_ATTRIBUTE_ARCHIVE) != 0) ?
  82. kArchiveAttributeChar: kEmptyAttributeChar;
  83. s[5] = '\0';
  84. }
  85. enum EAdjustment
  86. {
  87. kLeft,
  88. kCenter,
  89. kRight
  90. };
  91. struct CFieldInfo
  92. {
  93. PROPID PropID;
  94. UString Name;
  95. EAdjustment TitleAdjustment;
  96. EAdjustment TextAdjustment;
  97. int PrefixSpacesWidth;
  98. int Width;
  99. };
  100. struct CFieldInfoInit
  101. {
  102. PROPID PropID;
  103. const wchar_t *Name;
  104. EAdjustment TitleAdjustment;
  105. EAdjustment TextAdjustment;
  106. int PrefixSpacesWidth;
  107. int Width;
  108. };
  109. CFieldInfoInit kStandardFieldTable[] =
  110. {
  111. { kpidLastWriteTime, L" Date Time", kLeft, kLeft, 0, 19 },
  112. { kpidAttributes, L"Attr", kRight, kCenter, 1, 5 },
  113. { kpidSize, L"Size", kRight, kRight, 1, 12 },
  114. { kpidPackedSize, L"Compressed", kRight, kRight, 1, 12 },
  115. { kpidPath, L"Name", kLeft, kLeft, 2, 24 }
  116. };
  117. void PrintSpaces(int numSpaces)
  118. {
  119. for (int i = 0; i < numSpaces; i++)
  120. g_StdOut << ' ';
  121. }
  122. void PrintString(EAdjustment adjustment, int width, const UString &textString)
  123. {
  124. const int numSpaces = width - textString.Length();
  125. int numLeftSpaces = 0;
  126. switch (adjustment)
  127. {
  128. case kLeft:
  129. numLeftSpaces = 0;
  130. break;
  131. case kCenter:
  132. numLeftSpaces = numSpaces / 2;
  133. break;
  134. case kRight:
  135. numLeftSpaces = numSpaces;
  136. break;
  137. }
  138. PrintSpaces(numLeftSpaces);
  139. g_StdOut << textString;
  140. PrintSpaces(numSpaces - numLeftSpaces);
  141. }
  142. class CFieldPrinter
  143. {
  144. CObjectVector<CFieldInfo> _fields;
  145. public:
  146. void Clear() { _fields.Clear(); }
  147. void Init(const CFieldInfoInit *standardFieldTable, int numItems);
  148. HRESULT Init(IInArchive *archive);
  149. void PrintTitle();
  150. void PrintTitleLines();
  151. HRESULT PrintItemInfo(IInArchive *archive,
  152. const UString &defaultItemName,
  153. const NWindows::NFile::NFind::CFileInfoW &archiveFileInfo,
  154. UInt32 index,
  155. bool techMode);
  156. HRESULT PrintSummaryInfo(UInt64 numFiles, UInt64 numDirs,
  157. const UInt64 *size, const UInt64 *compressedSize);
  158. };
  159. void CFieldPrinter::Init(const CFieldInfoInit *standardFieldTable, int numItems)
  160. {
  161. Clear();
  162. for (int i = 0; i < numItems; i++)
  163. {
  164. CFieldInfo fieldInfo;
  165. const CFieldInfoInit &fieldInfoInit = standardFieldTable[i];
  166. fieldInfo.PropID = fieldInfoInit.PropID;
  167. fieldInfo.Name = fieldInfoInit.Name;
  168. fieldInfo.TitleAdjustment = fieldInfoInit.TitleAdjustment;
  169. fieldInfo.TextAdjustment = fieldInfoInit.TextAdjustment;
  170. fieldInfo.PrefixSpacesWidth = fieldInfoInit.PrefixSpacesWidth;
  171. fieldInfo.Width = fieldInfoInit.Width;
  172. _fields.Add(fieldInfo);
  173. }
  174. }
  175. static UString GetPropName(PROPID propID, BSTR name)
  176. {
  177. for (int i = 0; i < sizeof(kPropIdToName) / sizeof(kPropIdToName[0]); i++)
  178. {
  179. const CPropIdToName &propIdToName = kPropIdToName[i];
  180. if (propIdToName.PropID == propID)
  181. return propIdToName.Name;
  182. }
  183. if (name)
  184. return name;
  185. return L"?";
  186. }
  187. HRESULT CFieldPrinter::Init(IInArchive *archive)
  188. {
  189. Clear();
  190. UInt32 numProps;
  191. RINOK(archive->GetNumberOfProperties(&numProps));
  192. for (UInt32 i = 0; i < numProps; i++)
  193. {
  194. CMyComBSTR name;
  195. PROPID propID;
  196. VARTYPE vt;
  197. RINOK(archive->GetPropertyInfo(i, &name, &propID, &vt));
  198. CFieldInfo fieldInfo;
  199. fieldInfo.PropID = propID;
  200. fieldInfo.Name = GetPropName(propID, name);
  201. _fields.Add(fieldInfo);
  202. }
  203. return S_OK;
  204. }
  205. void CFieldPrinter::PrintTitle()
  206. {
  207. for (int i = 0; i < _fields.Size(); i++)
  208. {
  209. const CFieldInfo &fieldInfo = _fields[i];
  210. PrintSpaces(fieldInfo.PrefixSpacesWidth);
  211. PrintString(fieldInfo.TitleAdjustment,
  212. ((fieldInfo.PropID == kpidPath) ? 0: fieldInfo.Width), fieldInfo.Name);
  213. }
  214. }
  215. void CFieldPrinter::PrintTitleLines()
  216. {
  217. for (int i = 0; i < _fields.Size(); i++)
  218. {
  219. const CFieldInfo &fieldInfo = _fields[i];
  220. PrintSpaces(fieldInfo.PrefixSpacesWidth);
  221. for (int i = 0; i < fieldInfo.Width; i++)
  222. g_StdOut << '-';
  223. }
  224. }
  225. BOOL IsFileTimeZero(CONST FILETIME *lpFileTime)
  226. {
  227. return (lpFileTime->dwLowDateTime == 0) && (lpFileTime->dwHighDateTime == 0);
  228. }
  229. static const char *kEmptyTimeString = " ";
  230. void PrintTime(const NCOM::CPropVariant &propVariant)
  231. {
  232. if (propVariant.vt != VT_FILETIME)
  233. throw "incorrect item";
  234. if (IsFileTimeZero(&propVariant.filetime))
  235. g_StdOut << kEmptyTimeString;
  236. else
  237. {
  238. FILETIME localFileTime;
  239. if (!FileTimeToLocalFileTime(&propVariant.filetime, &localFileTime))
  240. throw "FileTimeToLocalFileTime error";
  241. char s[32];
  242. if (ConvertFileTimeToString(localFileTime, s, true, true))
  243. g_StdOut << s;
  244. else
  245. g_StdOut << kEmptyTimeString;
  246. }
  247. }
  248. HRESULT CFieldPrinter::PrintItemInfo(IInArchive *archive,
  249. const UString &defaultItemName,
  250. const NWindows::NFile::NFind::CFileInfoW &archiveFileInfo,
  251. UInt32 index,
  252. bool techMode)
  253. {
  254. /*
  255. if (techMode)
  256. {
  257. g_StdOut << "Index = ";
  258. g_StdOut << (UInt64)index;
  259. g_StdOut << endl;
  260. }
  261. */
  262. for (int i = 0; i < _fields.Size(); i++)
  263. {
  264. const CFieldInfo &fieldInfo = _fields[i];
  265. if (!techMode)
  266. PrintSpaces(fieldInfo.PrefixSpacesWidth);
  267. NCOM::CPropVariant propVariant;
  268. RINOK(archive->GetProperty(index, fieldInfo.PropID, &propVariant));
  269. if (techMode)
  270. {
  271. g_StdOut << fieldInfo.Name << " = ";
  272. }
  273. int width = (fieldInfo.PropID == kpidPath) ? 0: fieldInfo.Width;
  274. if (propVariant.vt == VT_EMPTY)
  275. {
  276. switch(fieldInfo.PropID)
  277. {
  278. case kpidPath:
  279. propVariant = defaultItemName;
  280. break;
  281. case kpidLastWriteTime:
  282. propVariant = archiveFileInfo.LastWriteTime;
  283. break;
  284. default:
  285. if (techMode)
  286. g_StdOut << endl;
  287. else
  288. PrintSpaces(width);
  289. continue;
  290. }
  291. }
  292. if (fieldInfo.PropID == kpidLastWriteTime)
  293. {
  294. PrintTime(propVariant);
  295. }
  296. else if (fieldInfo.PropID == kpidAttributes)
  297. {
  298. if (propVariant.vt != VT_UI4)
  299. throw "incorrect item";
  300. UInt32 attributes = propVariant.ulVal;
  301. bool isFolder;
  302. RINOK(IsArchiveItemFolder(archive, index, isFolder));
  303. char s[8];
  304. GetAttributesString(attributes, isFolder, s);
  305. g_StdOut << s;
  306. }
  307. else if (propVariant.vt == VT_BSTR)
  308. {
  309. if (techMode)
  310. g_StdOut << propVariant.bstrVal;
  311. else
  312. PrintString(fieldInfo.TextAdjustment, width, propVariant.bstrVal);
  313. }
  314. else
  315. {
  316. UString s = ConvertPropertyToString(propVariant, fieldInfo.PropID);
  317. s.Replace(wchar_t(0xA), L' ');
  318. s.Replace(wchar_t(0xD), L' ');
  319. if (techMode)
  320. g_StdOut << s;
  321. else
  322. PrintString(fieldInfo.TextAdjustment, width, s);
  323. }
  324. if (techMode)
  325. g_StdOut << endl;
  326. }
  327. return S_OK;
  328. }
  329. void PrintNumberString(EAdjustment adjustment, int width, const UInt64 *value)
  330. {
  331. wchar_t textString[32] = { 0 };
  332. if (value != NULL)
  333. ConvertUInt64ToString(*value, textString);
  334. PrintString(adjustment, width, textString);
  335. }
  336. HRESULT CFieldPrinter::PrintSummaryInfo(UInt64 numFiles, UInt64 numDirs,
  337. const UInt64 *size, const UInt64 *compressedSize)
  338. {
  339. for (int i = 0; i < _fields.Size(); i++)
  340. {
  341. const CFieldInfo &fieldInfo = _fields[i];
  342. PrintSpaces(fieldInfo.PrefixSpacesWidth);
  343. NCOM::CPropVariant propVariant;
  344. if (fieldInfo.PropID == kpidSize)
  345. PrintNumberString(fieldInfo.TextAdjustment, fieldInfo.Width, size);
  346. else if (fieldInfo.PropID == kpidPackedSize)
  347. PrintNumberString(fieldInfo.TextAdjustment, fieldInfo.Width, compressedSize);
  348. else if (fieldInfo.PropID == kpidPath)
  349. {
  350. wchar_t textString[32];
  351. ConvertUInt64ToString(numFiles, textString);
  352. UString temp = textString;
  353. temp += L" ";
  354. temp += kFilesMessage;
  355. temp += L", ";
  356. ConvertUInt64ToString(numDirs, textString);
  357. temp += textString;
  358. temp += L" ";
  359. temp += kDirsMessage;
  360. PrintString(fieldInfo.TextAdjustment, 0, temp);
  361. }
  362. else
  363. PrintString(fieldInfo.TextAdjustment, fieldInfo.Width, L"");
  364. }
  365. return S_OK;
  366. }
  367. bool GetUInt64Value(IInArchive *archive, UInt32 index, PROPID propID, UInt64 &value)
  368. {
  369. NCOM::CPropVariant propVariant;
  370. if (archive->GetProperty(index, propID, &propVariant) != S_OK)
  371. throw "GetPropertyValue error";
  372. if (propVariant.vt == VT_EMPTY)
  373. return false;
  374. value = ConvertPropVariantToUInt64(propVariant);
  375. return true;
  376. }
  377. HRESULT ListArchives(
  378. CCodecs *codecs,
  379. UStringVector &archivePaths, UStringVector &archivePathsFull,
  380. const NWildcard::CCensorNode &wildcardCensor,
  381. bool enableHeaders, bool techMode, bool &passwordEnabled, UString &password, UInt64 &numErrors)
  382. {
  383. numErrors = 0;
  384. CFieldPrinter fieldPrinter;
  385. if (!techMode)
  386. fieldPrinter.Init(kStandardFieldTable, sizeof(kStandardFieldTable) / sizeof(kStandardFieldTable[0]));
  387. UInt64 numFiles2 = 0, numDirs2 = 0, totalPackSize2 = 0, totalUnPackSize2 = 0;
  388. UInt64 *totalPackSizePointer2 = 0, *totalUnPackSizePointer2 = 0;
  389. for (int i = 0; i < archivePaths.Size(); i++)
  390. {
  391. const UString &archiveName = archivePaths[i];
  392. NFile::NFind::CFileInfoW archiveFileInfo;
  393. if (!NFile::NFind::FindFile(archiveName, archiveFileInfo) || archiveFileInfo.IsDirectory())
  394. {
  395. g_StdOut << endl << "Error: " << archiveName << " is not archive" << endl;
  396. numErrors++;
  397. continue;
  398. }
  399. if (archiveFileInfo.IsDirectory())
  400. {
  401. g_StdOut << endl << "Error: " << archiveName << " is not file" << endl;
  402. numErrors++;
  403. continue;
  404. }
  405. CArchiveLink archiveLink;
  406. COpenCallbackConsole openCallback;
  407. openCallback.OutStream = &g_StdOut;
  408. openCallback.PasswordIsDefined = passwordEnabled;
  409. openCallback.Password = password;
  410. HRESULT result = MyOpenArchive(codecs, archiveName, archiveLink, &openCallback);
  411. if (result != S_OK)
  412. {
  413. g_StdOut << endl << "Error: " << archiveName << " is not supported archive" << endl;
  414. numErrors++;
  415. continue;
  416. }
  417. for (int v = 0; v < archiveLink.VolumePaths.Size(); v++)
  418. {
  419. int index = archivePathsFull.FindInSorted(archiveLink.VolumePaths[v]);
  420. if (index >= 0 && index > i)
  421. {
  422. archivePaths.Delete(index);
  423. archivePathsFull.Delete(index);
  424. }
  425. }
  426. IInArchive *archive = archiveLink.GetArchive();
  427. const UString defaultItemName = archiveLink.GetDefaultItemName();
  428. if (enableHeaders)
  429. {
  430. g_StdOut << endl << kListing << archiveName << endl << endl;
  431. UInt32 numProps;
  432. if (archive->GetNumberOfArchiveProperties(&numProps) == S_OK)
  433. {
  434. for (UInt32 i = 0; i < numProps; i++)
  435. {
  436. CMyComBSTR name;
  437. PROPID propID;
  438. VARTYPE vt;
  439. if (archive->GetArchivePropertyInfo(i, &name, &propID, &vt) != S_OK)
  440. continue;
  441. NCOM::CPropVariant prop;
  442. if (archive->GetArchiveProperty(propID, &prop) != S_OK)
  443. continue;
  444. UString s = ConvertPropertyToString(prop, propID);
  445. if (!s.IsEmpty())
  446. g_StdOut << GetPropName(propID, name) << " = " << s << endl;
  447. }
  448. }
  449. if (techMode)
  450. g_StdOut << "----------\n";
  451. if (numProps > 0)
  452. g_StdOut << endl;
  453. }
  454. if (enableHeaders && !techMode)
  455. {
  456. fieldPrinter.PrintTitle();
  457. g_StdOut << endl;
  458. fieldPrinter.PrintTitleLines();
  459. g_StdOut << endl;
  460. }
  461. if (techMode)
  462. {
  463. RINOK(fieldPrinter.Init(archive));
  464. }
  465. UInt64 numFiles = 0, numDirs = 0, totalPackSize = 0, totalUnPackSize = 0;
  466. UInt64 *totalPackSizePointer = 0, *totalUnPackSizePointer = 0;
  467. UInt32 numItems;
  468. RINOK(archive->GetNumberOfItems(&numItems));
  469. for(UInt32 i = 0; i < numItems; i++)
  470. {
  471. if (NConsoleClose::TestBreakSignal())
  472. return E_ABORT;
  473. UString filePath;
  474. RINOK(GetArchiveItemPath(archive, i, defaultItemName, filePath));
  475. bool isFolder;
  476. RINOK(IsArchiveItemFolder(archive, i, isFolder));
  477. if (!wildcardCensor.CheckPath(filePath, !isFolder))
  478. continue;
  479. fieldPrinter.PrintItemInfo(archive, defaultItemName, archiveFileInfo, i, techMode);
  480. UInt64 packSize, unpackSize;
  481. if (!GetUInt64Value(archive, i, kpidSize, unpackSize))
  482. unpackSize = 0;
  483. else
  484. totalUnPackSizePointer = &totalUnPackSize;
  485. if (!GetUInt64Value(archive, i, kpidPackedSize, packSize))
  486. packSize = 0;
  487. else
  488. totalPackSizePointer = &totalPackSize;
  489. g_StdOut << endl;
  490. if (isFolder)
  491. numDirs++;
  492. else
  493. numFiles++;
  494. totalPackSize += packSize;
  495. totalUnPackSize += unpackSize;
  496. }
  497. if (enableHeaders && !techMode)
  498. {
  499. fieldPrinter.PrintTitleLines();
  500. g_StdOut << endl;
  501. fieldPrinter.PrintSummaryInfo(numFiles, numDirs, totalUnPackSizePointer, totalPackSizePointer);
  502. g_StdOut << endl;
  503. }
  504. if (totalPackSizePointer != 0)
  505. {
  506. totalPackSizePointer2 = &totalPackSize2;
  507. totalPackSize2 += totalPackSize;
  508. }
  509. if (totalUnPackSizePointer != 0)
  510. {
  511. totalUnPackSizePointer2 = &totalUnPackSize2;
  512. totalUnPackSize2 += totalUnPackSize;
  513. }
  514. numFiles2 += numFiles;
  515. numDirs2 += numDirs;
  516. }
  517. if (enableHeaders && !techMode && archivePaths.Size() > 1)
  518. {
  519. g_StdOut << endl;
  520. fieldPrinter.PrintTitleLines();
  521. g_StdOut << endl;
  522. fieldPrinter.PrintSummaryInfo(numFiles2, numDirs2, totalUnPackSizePointer2, totalPackSizePointer2);
  523. g_StdOut << endl;
  524. g_StdOut << "Archives: " << archivePaths.Size() << endl;
  525. }
  526. return S_OK;
  527. }