1 // List.cpp 2 3 #include "StdAfx.h" 4 5 #include "Common/IntToString.h" 6 #include "Common/MyCom.h" 7 #include "Common/StdOutStream.h" 8 #include "Common/StringConvert.h" 9 10 #include "Windows/Error.h" 11 #include "Windows/FileDir.h" 12 #include "Windows/PropVariant.h" 13 #include "Windows/PropVariantConversions.h" 14 15 #include "../../Archive/IArchive.h" 16 17 #include "../Common/OpenArchive.h" 18 #include "../Common/PropIDUtils.h" 19 20 #include "ConsoleClose.h" 21 #include "List.h" 22 #include "OpenCallbackConsole.h" 23 24 using namespace NWindows; 25 26 struct CPropIdToName 27 { 28 PROPID PropID; 29 const wchar_t *Name; 30 }; 31 32 static const CPropIdToName kPropIdToName[] = 33 { 34 { kpidPath, L"Path" }, 35 { kpidName, L"Name" }, 36 { kpidIsDir, L"Folder" }, 37 { kpidSize, L"Size" }, 38 { kpidPackSize, L"Packed Size" }, 39 { kpidAttrib, L"Attributes" }, 40 { kpidCTime, L"Created" }, 41 { kpidATime, L"Accessed" }, 42 { kpidMTime, L"Modified" }, 43 { kpidSolid, L"Solid" }, 44 { kpidCommented, L"Commented" }, 45 { kpidEncrypted, L"Encrypted" }, 46 { kpidSplitBefore, L"Split Before" }, 47 { kpidSplitAfter, L"Split After" }, 48 { kpidDictionarySize, L"Dictionary Size" }, 49 { kpidCRC, L"CRC" }, 50 { kpidType, L"Type" }, 51 { kpidIsAnti, L"Anti" }, 52 { kpidMethod, L"Method" }, 53 { kpidHostOS, L"Host OS" }, 54 { kpidFileSystem, L"File System" }, 55 { kpidUser, L"User" }, 56 { kpidGroup, L"Group" }, 57 { kpidBlock, L"Block" }, 58 { kpidComment, L"Comment" }, 59 { kpidPosition, L"Position" }, 60 { kpidPrefix, L"Prefix" }, 61 { kpidNumSubDirs, L"Folders" }, 62 { kpidNumSubFiles, L"Files" }, 63 { kpidUnpackVer, L"Version" }, 64 { kpidVolume, L"Volume" }, 65 { kpidIsVolume, L"Multivolume" }, 66 { kpidOffset, L"Offset" }, 67 { kpidLinks, L"Links" }, 68 { kpidNumBlocks, L"Blocks" }, 69 { kpidNumVolumes, L"Volumes" }, 70 71 { kpidBit64, L"64-bit" }, 72 { kpidBigEndian, L"Big-endian" }, 73 { kpidCpu, L"CPU" }, 74 { kpidPhySize, L"Physical Size" }, 75 { kpidHeadersSize, L"Headers Size" }, 76 { kpidChecksum, L"Checksum" }, 77 { kpidCharacts, L"Characteristics" }, 78 { kpidVa, L"Virtual Address" }, 79 { kpidId, L"ID" }, 80 { kpidShortName, L"Short Name" }, 81 { kpidCreatorApp, L"Creator Application"}, 82 { kpidSectorSize, L"Sector Size" }, 83 { kpidPosixAttrib, L"Mode" }, 84 { kpidLink, L"Link" }, 85 { kpidError, L"Error" }, 86 87 { kpidTotalSize, L"Total Size" }, 88 { kpidFreeSpace, L"Free Space" }, 89 { kpidClusterSize, L"Cluster Size" }, 90 { kpidVolumeName, L"Label" } 91 }; 92 93 static const char kEmptyAttribChar = '.'; 94 95 static const char *kListing = "Listing archive: "; 96 static const wchar_t *kFilesMessage = L"files"; 97 static const wchar_t *kDirsMessage = L"folders"; 98 99 static void GetAttribString(DWORD wa, bool isDir, char *s) 100 { 101 s[0] = ((wa & FILE_ATTRIBUTE_DIRECTORY) != 0 || isDir) ? 'D' : kEmptyAttribChar; 102 s[1] = ((wa & FILE_ATTRIBUTE_READONLY) != 0) ? 'R': kEmptyAttribChar; 103 s[2] = ((wa & FILE_ATTRIBUTE_HIDDEN) != 0) ? 'H': kEmptyAttribChar; 104 s[3] = ((wa & FILE_ATTRIBUTE_SYSTEM) != 0) ? 'S': kEmptyAttribChar; 105 s[4] = ((wa & FILE_ATTRIBUTE_ARCHIVE) != 0) ? 'A': kEmptyAttribChar; 106 s[5] = '\0'; 107 } 108 109 enum EAdjustment 110 { 111 kLeft, 112 kCenter, 113 kRight 114 }; 115 116 struct CFieldInfo 117 { 118 PROPID PropID; 119 UString Name; 120 EAdjustment TitleAdjustment; 121 EAdjustment TextAdjustment; 122 int PrefixSpacesWidth; 123 int Width; 124 }; 125 126 struct CFieldInfoInit 127 { 128 PROPID PropID; 129 const wchar_t *Name; 130 EAdjustment TitleAdjustment; 131 EAdjustment TextAdjustment; 132 int PrefixSpacesWidth; 133 int Width; 134 }; 135 136 static CFieldInfoInit kStandardFieldTable[] = 137 { 138 { kpidMTime, L" Date Time", kLeft, kLeft, 0, 19 }, 139 { kpidAttrib, L"Attr", kRight, kCenter, 1, 5 }, 140 { kpidSize, L"Size", kRight, kRight, 1, 12 }, 141 { kpidPackSize, L"Compressed", kRight, kRight, 1, 12 }, 142 { kpidPath, L"Name", kLeft, kLeft, 2, 24 } 143 }; 144 145 static void PrintSpaces(int numSpaces) 146 { 147 for (int i = 0; i < numSpaces; i++) 148 g_StdOut << ' '; 149 } 150 151 static void PrintString(EAdjustment adjustment, int width, const UString &textString) 152 { 153 const int numSpaces = width - textString.Length(); 154 int numLeftSpaces = 0; 155 switch (adjustment) 156 { 157 case kLeft: 158 numLeftSpaces = 0; 159 break; 160 case kCenter: 161 numLeftSpaces = numSpaces / 2; 162 break; 163 case kRight: 164 numLeftSpaces = numSpaces; 165 break; 166 } 167 PrintSpaces(numLeftSpaces); 168 g_StdOut << textString; 169 PrintSpaces(numSpaces - numLeftSpaces); 170 } 171 172 class CFieldPrinter 173 { 174 CObjectVector<CFieldInfo> _fields; 175 public: 176 void Clear() { _fields.Clear(); } 177 void Init(const CFieldInfoInit *standardFieldTable, int numItems); 178 HRESULT Init(IInArchive *archive); 179 void PrintTitle(); 180 void PrintTitleLines(); 181 HRESULT PrintItemInfo(const CArc &arc, UInt32 index, bool techMode); 182 HRESULT PrintSummaryInfo(UInt64 numFiles, UInt64 numDirs, 183 const UInt64 *size, const UInt64 *compressedSize); 184 }; 185 186 void CFieldPrinter::Init(const CFieldInfoInit *standardFieldTable, int numItems) 187 { 188 Clear(); 189 for (int i = 0; i < numItems; i++) 190 { 191 CFieldInfo fieldInfo; 192 const CFieldInfoInit &fieldInfoInit = standardFieldTable[i]; 193 fieldInfo.PropID = fieldInfoInit.PropID; 194 fieldInfo.Name = fieldInfoInit.Name; 195 fieldInfo.TitleAdjustment = fieldInfoInit.TitleAdjustment; 196 fieldInfo.TextAdjustment = fieldInfoInit.TextAdjustment; 197 fieldInfo.PrefixSpacesWidth = fieldInfoInit.PrefixSpacesWidth; 198 fieldInfo.Width = fieldInfoInit.Width; 199 _fields.Add(fieldInfo); 200 } 201 } 202 203 static UString GetPropName(PROPID propID, BSTR name) 204 { 205 for (int i = 0; i < sizeof(kPropIdToName) / sizeof(kPropIdToName[0]); i++) 206 { 207 const CPropIdToName &propIdToName = kPropIdToName[i]; 208 if (propIdToName.PropID == propID) 209 return propIdToName.Name; 210 } 211 if (name) 212 return name; 213 wchar_t s[16]; 214 ConvertUInt32ToString(propID, s); 215 return s; 216 } 217 218 HRESULT CFieldPrinter::Init(IInArchive *archive) 219 { 220 Clear(); 221 UInt32 numProps; 222 RINOK(archive->GetNumberOfProperties(&numProps)); 223 for (UInt32 i = 0; i < numProps; i++) 224 { 225 CMyComBSTR name; 226 PROPID propID; 227 VARTYPE vt; 228 RINOK(archive->GetPropertyInfo(i, &name, &propID, &vt)); 229 CFieldInfo fieldInfo; 230 fieldInfo.PropID = propID; 231 fieldInfo.Name = GetPropName(propID, name); 232 _fields.Add(fieldInfo); 233 } 234 return S_OK; 235 } 236 237 void CFieldPrinter::PrintTitle() 238 { 239 for (int i = 0; i < _fields.Size(); i++) 240 { 241 const CFieldInfo &fieldInfo = _fields[i]; 242 PrintSpaces(fieldInfo.PrefixSpacesWidth); 243 PrintString(fieldInfo.TitleAdjustment, 244 ((fieldInfo.PropID == kpidPath) ? 0: fieldInfo.Width), fieldInfo.Name); 245 } 246 } 247 248 void CFieldPrinter::PrintTitleLines() 249 { 250 for (int i = 0; i < _fields.Size(); i++) 251 { 252 const CFieldInfo &fieldInfo = _fields[i]; 253 PrintSpaces(fieldInfo.PrefixSpacesWidth); 254 for (int i = 0; i < fieldInfo.Width; i++) 255 g_StdOut << '-'; 256 } 257 } 258 259 260 static BOOL IsFileTimeZero(CONST FILETIME *lpFileTime) 261 { 262 return (lpFileTime->dwLowDateTime == 0) && (lpFileTime->dwHighDateTime == 0); 263 } 264 265 static const char *kEmptyTimeString = " "; 266 static void PrintTime(const NCOM::CPropVariant &prop) 267 { 268 if (prop.vt != VT_FILETIME) 269 throw "incorrect item"; 270 if (IsFileTimeZero(&prop.filetime)) 271 g_StdOut << kEmptyTimeString; 272 else 273 { 274 FILETIME localFileTime; 275 if (!FileTimeToLocalFileTime(&prop.filetime, &localFileTime)) 276 throw "FileTimeToLocalFileTime error"; 277 char s[32]; 278 if (ConvertFileTimeToString(localFileTime, s, true, true)) 279 g_StdOut << s; 280 else 281 g_StdOut << kEmptyTimeString; 282 } 283 } 284 285 HRESULT CFieldPrinter::PrintItemInfo(const CArc &arc, UInt32 index, bool techMode) 286 { 287 /* 288 if (techMode) 289 { 290 g_StdOut << "Index = "; 291 g_StdOut << (UInt64)index; 292 g_StdOut << endl; 293 } 294 */ 295 for (int i = 0; i < _fields.Size(); i++) 296 { 297 const CFieldInfo &fieldInfo = _fields[i]; 298 if (!techMode) 299 PrintSpaces(fieldInfo.PrefixSpacesWidth); 300 301 NCOM::CPropVariant prop; 302 if (fieldInfo.PropID == kpidPath) 303 { 304 UString s; 305 RINOK(arc.GetItemPath(index, s)); 306 prop = s; 307 } 308 else 309 { 310 RINOK(arc.Archive->GetProperty(index, fieldInfo.PropID, &prop)); 311 } 312 if (techMode) 313 { 314 g_StdOut << fieldInfo.Name << " = "; 315 } 316 int width = (fieldInfo.PropID == kpidPath) ? 0: fieldInfo.Width; 317 if (fieldInfo.PropID == kpidAttrib && (prop.vt == VT_EMPTY || prop.vt == VT_UI4)) 318 { 319 UInt32 attrib = (prop.vt == VT_EMPTY) ? 0 : prop.ulVal; 320 bool isFolder; 321 RINOK(IsArchiveItemFolder(arc.Archive, index, isFolder)); 322 char s[8]; 323 GetAttribString(attrib, isFolder, s); 324 g_StdOut << s; 325 } 326 else if (prop.vt == VT_EMPTY) 327 { 328 if (!techMode) 329 PrintSpaces(width); 330 } 331 else if (fieldInfo.PropID == kpidMTime) 332 { 333 PrintTime(prop); 334 } 335 else if (prop.vt == VT_BSTR) 336 { 337 if (techMode) 338 g_StdOut << prop.bstrVal; 339 else 340 PrintString(fieldInfo.TextAdjustment, width, prop.bstrVal); 341 } 342 else 343 { 344 UString s = ConvertPropertyToString(prop, fieldInfo.PropID); 345 s.Replace(wchar_t(0xA), L' '); 346 s.Replace(wchar_t(0xD), L' '); 347 348 if (techMode) 349 g_StdOut << s; 350 else 351 PrintString(fieldInfo.TextAdjustment, width, s); 352 } 353 if (techMode) 354 g_StdOut << endl; 355 } 356 return S_OK; 357 } 358 359 static void PrintNumberString(EAdjustment adjustment, int width, const UInt64 *value) 360 { 361 wchar_t textString[32] = { 0 }; 362 if (value != NULL) 363 ConvertUInt64ToString(*value, textString); 364 PrintString(adjustment, width, textString); 365 } 366 367 368 HRESULT CFieldPrinter::PrintSummaryInfo(UInt64 numFiles, UInt64 numDirs, 369 const UInt64 *size, const UInt64 *compressedSize) 370 { 371 for (int i = 0; i < _fields.Size(); i++) 372 { 373 const CFieldInfo &fieldInfo = _fields[i]; 374 PrintSpaces(fieldInfo.PrefixSpacesWidth); 375 NCOM::CPropVariant prop; 376 if (fieldInfo.PropID == kpidSize) 377 PrintNumberString(fieldInfo.TextAdjustment, fieldInfo.Width, size); 378 else if (fieldInfo.PropID == kpidPackSize) 379 PrintNumberString(fieldInfo.TextAdjustment, fieldInfo.Width, compressedSize); 380 else if (fieldInfo.PropID == kpidPath) 381 { 382 wchar_t textString[32]; 383 ConvertUInt64ToString(numFiles, textString); 384 UString temp = textString; 385 temp += L" "; 386 temp += kFilesMessage; 387 temp += L", "; 388 ConvertUInt64ToString(numDirs, textString); 389 temp += textString; 390 temp += L" "; 391 temp += kDirsMessage; 392 PrintString(fieldInfo.TextAdjustment, 0, temp); 393 } 394 else 395 PrintString(fieldInfo.TextAdjustment, fieldInfo.Width, L""); 396 } 397 return S_OK; 398 } 399 400 bool GetUInt64Value(IInArchive *archive, UInt32 index, PROPID propID, UInt64 &value) 401 { 402 NCOM::CPropVariant prop; 403 if (archive->GetProperty(index, propID, &prop) != S_OK) 404 throw "GetPropertyValue error"; 405 if (prop.vt == VT_EMPTY) 406 return false; 407 value = ConvertPropVariantToUInt64(prop); 408 return true; 409 } 410 411 static void PrintPropPair(const wchar_t *name, const wchar_t *value) 412 { 413 g_StdOut << name << " = " << value << endl; 414 } 415 416 HRESULT ListArchives(CCodecs *codecs, const CIntVector &formatIndices, 417 bool stdInMode, 418 UStringVector &arcPaths, UStringVector &arcPathsFull, 419 const NWildcard::CCensorNode &wildcardCensor, 420 bool enableHeaders, bool techMode, 421 #ifndef _NO_CRYPTO 422 bool &passwordEnabled, UString &password, 423 #endif 424 UInt64 &numErrors) 425 { 426 numErrors = 0; 427 CFieldPrinter fieldPrinter; 428 if (!techMode) 429 fieldPrinter.Init(kStandardFieldTable, sizeof(kStandardFieldTable) / sizeof(kStandardFieldTable[0])); 430 431 UInt64 numFiles2 = 0, numDirs2 = 0, totalPackSize2 = 0, totalUnPackSize2 = 0; 432 UInt64 *totalPackSizePointer2 = 0, *totalUnPackSizePointer2 = 0; 433 int numArcs = /* stdInMode ? 1 : */ arcPaths.Size(); 434 for (int i = 0; i < numArcs; i++) 435 { 436 const UString &archiveName = arcPaths[i]; 437 UInt64 arcPackSize = 0; 438 if (!stdInMode) 439 { 440 NFile::NFind::CFileInfoW fi; 441 if (!fi.Find(archiveName) || fi.IsDir()) 442 { 443 g_StdOut << endl << "Error: " << archiveName << " is not file" << endl; 444 numErrors++; 445 continue; 446 } 447 arcPackSize = fi.Size; 448 } 449 450 CArchiveLink archiveLink; 451 452 COpenCallbackConsole openCallback; 453 openCallback.OutStream = &g_StdOut; 454 455 #ifndef _NO_CRYPTO 456 457 openCallback.PasswordIsDefined = passwordEnabled; 458 openCallback.Password = password; 459 460 #endif 461 462 HRESULT result = archiveLink.Open2(codecs, formatIndices, stdInMode, NULL, archiveName, &openCallback); 463 if (result != S_OK) 464 { 465 if (result == E_ABORT) 466 return result; 467 g_StdOut << endl << "Error: " << archiveName << ": "; 468 if (result == S_FALSE) 469 { 470 #ifndef _NO_CRYPTO 471 if (openCallback.Open_WasPasswordAsked()) 472 g_StdOut << "Can not open encrypted archive. Wrong password?"; 473 else 474 #endif 475 g_StdOut << "Can not open file as archive"; 476 } 477 else if (result == E_OUTOFMEMORY) 478 g_StdOut << "Can't allocate required memory"; 479 else 480 g_StdOut << NError::MyFormatMessage(result); 481 g_StdOut << endl; 482 numErrors++; 483 continue; 484 } 485 486 if (!stdInMode) 487 for (int v = 0; v < archiveLink.VolumePaths.Size(); v++) 488 { 489 int index = arcPathsFull.FindInSorted(archiveLink.VolumePaths[v]); 490 if (index >= 0 && index > i) 491 { 492 arcPaths.Delete(index); 493 arcPathsFull.Delete(index); 494 numArcs = arcPaths.Size(); 495 } 496 } 497 498 if (enableHeaders) 499 { 500 g_StdOut << endl << kListing << archiveName << endl << endl; 501 502 for (int i = 0; i < archiveLink.Arcs.Size(); i++) 503 { 504 const CArc &arc = archiveLink.Arcs[i]; 505 506 g_StdOut << "--\n"; 507 PrintPropPair(L"Path", arc.Path); 508 PrintPropPair(L"Type", codecs->Formats[arc.FormatIndex].Name); 509 if (!arc.ErrorMessage.IsEmpty()) 510 PrintPropPair(L"Error", arc.ErrorMessage); 511 UInt32 numProps; 512 IInArchive *archive = arc.Archive; 513 if (archive->GetNumberOfArchiveProperties(&numProps) == S_OK) 514 { 515 for (UInt32 j = 0; j < numProps; j++) 516 { 517 CMyComBSTR name; 518 PROPID propID; 519 VARTYPE vt; 520 RINOK(archive->GetArchivePropertyInfo(j, &name, &propID, &vt)); 521 NCOM::CPropVariant prop; 522 RINOK(archive->GetArchiveProperty(propID, &prop)); 523 UString s = ConvertPropertyToString(prop, propID); 524 if (!s.IsEmpty()) 525 PrintPropPair(GetPropName(propID, name), s); 526 } 527 } 528 if (i != archiveLink.Arcs.Size() - 1) 529 { 530 UInt32 numProps; 531 g_StdOut << "----\n"; 532 if (archive->GetNumberOfProperties(&numProps) == S_OK) 533 { 534 UInt32 mainIndex = archiveLink.Arcs[i + 1].SubfileIndex; 535 for (UInt32 j = 0; j < numProps; j++) 536 { 537 CMyComBSTR name; 538 PROPID propID; 539 VARTYPE vt; 540 RINOK(archive->GetPropertyInfo(j, &name, &propID, &vt)); 541 NCOM::CPropVariant prop; 542 RINOK(archive->GetProperty(mainIndex, propID, &prop)); 543 UString s = ConvertPropertyToString(prop, propID); 544 if (!s.IsEmpty()) 545 PrintPropPair(GetPropName(propID, name), s); 546 } 547 } 548 } 549 550 } 551 g_StdOut << endl; 552 if (techMode) 553 g_StdOut << "----------\n"; 554 } 555 556 if (enableHeaders && !techMode) 557 { 558 fieldPrinter.PrintTitle(); 559 g_StdOut << endl; 560 fieldPrinter.PrintTitleLines(); 561 g_StdOut << endl; 562 } 563 564 const CArc &arc = archiveLink.Arcs.Back(); 565 IInArchive *archive = arc.Archive; 566 if (techMode) 567 { 568 RINOK(fieldPrinter.Init(archive)); 569 } 570 UInt64 numFiles = 0, numDirs = 0, totalPackSize = 0, totalUnPackSize = 0; 571 UInt64 *totalPackSizePointer = 0, *totalUnPackSizePointer = 0; 572 UInt32 numItems; 573 RINOK(archive->GetNumberOfItems(&numItems)); 574 for (UInt32 i = 0; i < numItems; i++) 575 { 576 if (NConsoleClose::TestBreakSignal()) 577 return E_ABORT; 578 579 UString filePath; 580 HRESULT res = arc.GetItemPath(i, filePath); 581 if (stdInMode && res == E_INVALIDARG) 582 break; 583 RINOK(res); 584 585 bool isFolder; 586 RINOK(IsArchiveItemFolder(archive, i, isFolder)); 587 if (!wildcardCensor.CheckPath(filePath, !isFolder)) 588 continue; 589 590 fieldPrinter.PrintItemInfo(arc, i, techMode); 591 592 UInt64 packSize, unpackSize; 593 if (!GetUInt64Value(archive, i, kpidSize, unpackSize)) 594 unpackSize = 0; 595 else 596 totalUnPackSizePointer = &totalUnPackSize; 597 if (!GetUInt64Value(archive, i, kpidPackSize, packSize)) 598 packSize = 0; 599 else 600 totalPackSizePointer = &totalPackSize; 601 602 g_StdOut << endl; 603 604 if (isFolder) 605 numDirs++; 606 else 607 numFiles++; 608 totalPackSize += packSize; 609 totalUnPackSize += unpackSize; 610 } 611 612 if (!stdInMode && totalPackSizePointer == 0) 613 { 614 if (archiveLink.VolumePaths.Size() != 0) 615 arcPackSize += archiveLink.VolumesSize; 616 totalPackSize = (numFiles == 0) ? 0 : arcPackSize; 617 totalPackSizePointer = &totalPackSize; 618 } 619 if (totalUnPackSizePointer == 0 && numFiles == 0) 620 { 621 totalUnPackSize = 0; 622 totalUnPackSizePointer = &totalUnPackSize; 623 } 624 if (enableHeaders && !techMode) 625 { 626 fieldPrinter.PrintTitleLines(); 627 g_StdOut << endl; 628 fieldPrinter.PrintSummaryInfo(numFiles, numDirs, totalUnPackSizePointer, totalPackSizePointer); 629 g_StdOut << endl; 630 } 631 if (totalPackSizePointer != 0) 632 { 633 totalPackSizePointer2 = &totalPackSize2; 634 totalPackSize2 += totalPackSize; 635 } 636 if (totalUnPackSizePointer != 0) 637 { 638 totalUnPackSizePointer2 = &totalUnPackSize2; 639 totalUnPackSize2 += totalUnPackSize; 640 } 641 numFiles2 += numFiles; 642 numDirs2 += numDirs; 643 } 644 if (enableHeaders && !techMode && numArcs > 1) 645 { 646 g_StdOut << endl; 647 fieldPrinter.PrintTitleLines(); 648 g_StdOut << endl; 649 fieldPrinter.PrintSummaryInfo(numFiles2, numDirs2, totalUnPackSizePointer2, totalPackSizePointer2); 650 g_StdOut << endl; 651 g_StdOut << "Archives: " << numArcs << endl; 652 } 653 return S_OK; 654 } 655