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 #include "../../../Common/UTFConvert.h" 10 11 #include "../../../Windows/ErrorMsg.h" 12 #include "../../../Windows/FileDir.h" 13 #include "../../../Windows/PropVariant.h" 14 #include "../../../Windows/PropVariantConv.h" 15 16 #include "../Common/OpenArchive.h" 17 #include "../Common/PropIDUtils.h" 18 19 #include "ConsoleClose.h" 20 #include "List.h" 21 #include "OpenCallbackConsole.h" 22 23 using namespace NWindows; 24 using namespace NCOM; 25 26 extern CStdOutStream *g_StdStream; 27 extern CStdOutStream *g_ErrStream; 28 29 static const char * const kPropIdToName[] = 30 { 31 "0" 32 , "1" 33 , "2" 34 , "Path" 35 , "Name" 36 , "Extension" 37 , "Folder" 38 , "Size" 39 , "Packed Size" 40 , "Attributes" 41 , "Created" 42 , "Accessed" 43 , "Modified" 44 , "Solid" 45 , "Commented" 46 , "Encrypted" 47 , "Split Before" 48 , "Split After" 49 , "Dictionary Size" 50 , "CRC" 51 , "Type" 52 , "Anti" 53 , "Method" 54 , "Host OS" 55 , "File System" 56 , "User" 57 , "Group" 58 , "Block" 59 , "Comment" 60 , "Position" 61 , "Path Prefix" 62 , "Folders" 63 , "Files" 64 , "Version" 65 , "Volume" 66 , "Multivolume" 67 , "Offset" 68 , "Links" 69 , "Blocks" 70 , "Volumes" 71 , "Time Type" 72 , "64-bit" 73 , "Big-endian" 74 , "CPU" 75 , "Physical Size" 76 , "Headers Size" 77 , "Checksum" 78 , "Characteristics" 79 , "Virtual Address" 80 , "ID" 81 , "Short Name" 82 , "Creator Application" 83 , "Sector Size" 84 , "Mode" 85 , "Symbolic Link" 86 , "Error" 87 , "Total Size" 88 , "Free Space" 89 , "Cluster Size" 90 , "Label" 91 , "Local Name" 92 , "Provider" 93 , "NT Security" 94 , "Alternate Stream" 95 , "Aux" 96 , "Deleted" 97 , "Tree" 98 , "SHA-1" 99 , "SHA-256" 100 , "Error Type" 101 , "Errors" 102 , "Errors" 103 , "Warnings" 104 , "Warning" 105 , "Streams" 106 , "Alternate Streams" 107 , "Alternate Streams Size" 108 , "Virtual Size" 109 , "Unpack Size" 110 , "Total Physical Size" 111 , "Volume Index" 112 , "SubType" 113 , "Short Comment" 114 , "Code Page" 115 , "Is not archive type" 116 , "Physical Size can't be detected" 117 , "Zeros Tail Is Allowed" 118 , "Tail Size" 119 , "Embedded Stub Size" 120 , "Link" 121 , "Hard Link" 122 , "iNode" 123 , "Stream ID" 124 , "Read-only" 125 , "Out Name" 126 , "Copy Link" 127 }; 128 129 static const char kEmptyAttribChar = '.'; 130 131 static const char * const kListing = "Listing archive: "; 132 133 static const char * const kString_Files = "files"; 134 static const char * const kString_Dirs = "folders"; 135 static const char * const kString_AltStreams = "alternate streams"; 136 static const char * const kString_Streams = "streams"; 137 138 static const char * const kError = "ERROR: "; 139 140 static void GetAttribString(UInt32 wa, bool isDir, bool allAttribs, char *s) 141 { 142 if (isDir) 143 wa |= FILE_ATTRIBUTE_DIRECTORY; 144 if (allAttribs) 145 { 146 ConvertWinAttribToString(s, wa); 147 return; 148 } 149 s[0] = ((wa & FILE_ATTRIBUTE_DIRECTORY) != 0) ? 'D': kEmptyAttribChar; 150 s[1] = ((wa & FILE_ATTRIBUTE_READONLY) != 0) ? 'R': kEmptyAttribChar; 151 s[2] = ((wa & FILE_ATTRIBUTE_HIDDEN) != 0) ? 'H': kEmptyAttribChar; 152 s[3] = ((wa & FILE_ATTRIBUTE_SYSTEM) != 0) ? 'S': kEmptyAttribChar; 153 s[4] = ((wa & FILE_ATTRIBUTE_ARCHIVE) != 0) ? 'A': kEmptyAttribChar; 154 s[5] = 0; 155 } 156 157 enum EAdjustment 158 { 159 kLeft, 160 kCenter, 161 kRight 162 }; 163 164 struct CFieldInfo 165 { 166 PROPID PropID; 167 bool IsRawProp; 168 UString NameU; 169 AString NameA; 170 EAdjustment TitleAdjustment; 171 EAdjustment TextAdjustment; 172 unsigned PrefixSpacesWidth; 173 unsigned Width; 174 }; 175 176 struct CFieldInfoInit 177 { 178 PROPID PropID; 179 const char *Name; 180 EAdjustment TitleAdjustment; 181 EAdjustment TextAdjustment; 182 unsigned PrefixSpacesWidth; 183 unsigned Width; 184 }; 185 186 static const CFieldInfoInit kStandardFieldTable[] = 187 { 188 { kpidMTime, " Date Time", kLeft, kLeft, 0, 19 }, 189 { kpidAttrib, "Attr", kRight, kCenter, 1, 5 }, 190 { kpidSize, "Size", kRight, kRight, 1, 12 }, 191 { kpidPackSize, "Compressed", kRight, kRight, 1, 12 }, 192 { kpidPath, "Name", kLeft, kLeft, 2, 24 } 193 }; 194 195 const unsigned kNumSpacesMax = 32; // it must be larger than max CFieldInfoInit.Width 196 static const char *g_Spaces = 197 " " ; 198 199 static void PrintSpaces(unsigned numSpaces) 200 { 201 if (numSpaces > 0 && numSpaces <= kNumSpacesMax) 202 g_StdOut << g_Spaces + (kNumSpacesMax - numSpaces); 203 } 204 205 static void PrintSpacesToString(char *dest, unsigned numSpaces) 206 { 207 unsigned i; 208 for (i = 0; i < numSpaces; i++) 209 dest[i] = ' '; 210 dest[i] = 0; 211 } 212 213 // extern int g_CodePage; 214 215 static void PrintUString(EAdjustment adj, unsigned width, const UString &s, AString &temp) 216 { 217 /* 218 // we don't need multibyte align. 219 int codePage = g_CodePage; 220 if (codePage == -1) 221 codePage = CP_OEMCP; 222 if (codePage == CP_UTF8) 223 ConvertUnicodeToUTF8(s, temp); 224 else 225 UnicodeStringToMultiByte2(temp, s, (UINT)codePage); 226 */ 227 228 unsigned numSpaces = 0; 229 230 if (width > s.Len()) 231 { 232 numSpaces = width - s.Len(); 233 unsigned numLeftSpaces = 0; 234 switch (adj) 235 { 236 case kLeft: numLeftSpaces = 0; break; 237 case kCenter: numLeftSpaces = numSpaces / 2; break; 238 case kRight: numLeftSpaces = numSpaces; break; 239 } 240 PrintSpaces(numLeftSpaces); 241 numSpaces -= numLeftSpaces; 242 } 243 244 g_StdOut.PrintUString(s, temp); 245 PrintSpaces(numSpaces); 246 } 247 248 static void PrintString(EAdjustment adj, unsigned width, const char *s) 249 { 250 unsigned numSpaces = 0; 251 unsigned len = (unsigned)strlen(s); 252 253 if (width > len) 254 { 255 numSpaces = width - len; 256 unsigned numLeftSpaces = 0; 257 switch (adj) 258 { 259 case kLeft: numLeftSpaces = 0; break; 260 case kCenter: numLeftSpaces = numSpaces / 2; break; 261 case kRight: numLeftSpaces = numSpaces; break; 262 } 263 PrintSpaces(numLeftSpaces); 264 numSpaces -= numLeftSpaces; 265 } 266 267 g_StdOut << s; 268 PrintSpaces(numSpaces); 269 } 270 271 static void PrintStringToString(char *dest, EAdjustment adj, unsigned width, const char *textString) 272 { 273 unsigned numSpaces = 0; 274 unsigned len = (unsigned)strlen(textString); 275 276 if (width > len) 277 { 278 numSpaces = width - len; 279 unsigned numLeftSpaces = 0; 280 switch (adj) 281 { 282 case kLeft: numLeftSpaces = 0; break; 283 case kCenter: numLeftSpaces = numSpaces / 2; break; 284 case kRight: numLeftSpaces = numSpaces; break; 285 } 286 PrintSpacesToString(dest, numLeftSpaces); 287 dest += numLeftSpaces; 288 numSpaces -= numLeftSpaces; 289 } 290 291 memcpy(dest, textString, len); 292 dest += len; 293 PrintSpacesToString(dest, numSpaces); 294 } 295 296 struct CListUInt64Def 297 { 298 UInt64 Val; 299 bool Def; 300 301 CListUInt64Def(): Val(0), Def(false) {} 302 void Add(UInt64 v) { Val += v; Def = true; } 303 void Add(const CListUInt64Def &v) { if (v.Def) Add(v.Val); } 304 }; 305 306 struct CListFileTimeDef 307 { 308 FILETIME Val; 309 bool Def; 310 311 CListFileTimeDef(): Def(false) { Val.dwLowDateTime = 0; Val.dwHighDateTime = 0; } 312 void Update(const CListFileTimeDef &t) 313 { 314 if (t.Def && (!Def || CompareFileTime(&Val, &t.Val) < 0)) 315 { 316 Val = t.Val; 317 Def = true; 318 } 319 } 320 }; 321 322 struct CListStat 323 { 324 CListUInt64Def Size; 325 CListUInt64Def PackSize; 326 CListFileTimeDef MTime; 327 UInt64 NumFiles; 328 329 CListStat(): NumFiles(0) {} 330 void Update(const CListStat &st) 331 { 332 Size.Add(st.Size); 333 PackSize.Add(st.PackSize); 334 MTime.Update(st.MTime); 335 NumFiles += st.NumFiles; 336 } 337 void SetSizeDefIfNoFiles() { if (NumFiles == 0) Size.Def = true; } 338 }; 339 340 struct CListStat2 341 { 342 CListStat MainFiles; 343 CListStat AltStreams; 344 UInt64 NumDirs; 345 346 CListStat2(): NumDirs(0) {} 347 348 void Update(const CListStat2 &st) 349 { 350 MainFiles.Update(st.MainFiles); 351 AltStreams.Update(st.AltStreams); 352 NumDirs += st.NumDirs; 353 } 354 const UInt64 GetNumStreams() const { return MainFiles.NumFiles + AltStreams.NumFiles; } 355 CListStat &GetStat(bool altStreamsMode) { return altStreamsMode ? AltStreams : MainFiles; } 356 }; 357 358 class CFieldPrinter 359 { 360 CObjectVector<CFieldInfo> _fields; 361 362 void AddProp(const wchar_t *name, PROPID propID, bool isRawProp); 363 public: 364 const CArc *Arc; 365 bool TechMode; 366 UString FilePath; 367 AString TempAString; 368 UString TempWString; 369 bool IsDir; 370 371 AString LinesString; 372 373 void Clear() { _fields.Clear(); LinesString.Empty(); } 374 void Init(const CFieldInfoInit *standardFieldTable, unsigned numItems); 375 376 HRESULT AddMainProps(IInArchive *archive); 377 HRESULT AddRawProps(IArchiveGetRawProps *getRawProps); 378 379 void PrintTitle(); 380 void PrintTitleLines(); 381 HRESULT PrintItemInfo(UInt32 index, const CListStat &st); 382 void PrintSum(const CListStat &st, UInt64 numDirs, const char *str); 383 void PrintSum(const CListStat2 &stat2); 384 }; 385 386 void CFieldPrinter::Init(const CFieldInfoInit *standardFieldTable, unsigned numItems) 387 { 388 Clear(); 389 for (unsigned i = 0; i < numItems; i++) 390 { 391 CFieldInfo &f = _fields.AddNew(); 392 const CFieldInfoInit &fii = standardFieldTable[i]; 393 f.PropID = fii.PropID; 394 f.IsRawProp = false; 395 f.NameA = fii.Name; 396 f.TitleAdjustment = fii.TitleAdjustment; 397 f.TextAdjustment = fii.TextAdjustment; 398 f.PrefixSpacesWidth = fii.PrefixSpacesWidth; 399 f.Width = fii.Width; 400 401 unsigned k; 402 for (k = 0; k < fii.PrefixSpacesWidth; k++) 403 LinesString.Add_Space(); 404 for (k = 0; k < fii.Width; k++) 405 LinesString += '-'; 406 } 407 } 408 409 static void GetPropName(PROPID propID, const wchar_t *name, AString &nameA, UString &nameU) 410 { 411 if (propID < ARRAY_SIZE(kPropIdToName)) 412 { 413 nameA = kPropIdToName[propID]; 414 return; 415 } 416 if (name) 417 nameU = name; 418 else 419 { 420 nameA.Empty(); 421 nameA.Add_UInt32(propID); 422 } 423 } 424 425 void CFieldPrinter::AddProp(const wchar_t *name, PROPID propID, bool isRawProp) 426 { 427 CFieldInfo f; 428 f.PropID = propID; 429 f.IsRawProp = isRawProp; 430 GetPropName(propID, name, f.NameA, f.NameU); 431 f.NameU += " = "; 432 if (!f.NameA.IsEmpty()) 433 f.NameA += " = "; 434 else 435 { 436 const UString &s = f.NameU; 437 AString sA; 438 unsigned i; 439 for (i = 0; i < s.Len(); i++) 440 { 441 wchar_t c = s[i]; 442 if (c >= 0x80) 443 break; 444 sA += (char)c; 445 } 446 if (i == s.Len()) 447 f.NameA = sA; 448 } 449 _fields.Add(f); 450 } 451 452 HRESULT CFieldPrinter::AddMainProps(IInArchive *archive) 453 { 454 UInt32 numProps; 455 RINOK(archive->GetNumberOfProperties(&numProps)); 456 for (UInt32 i = 0; i < numProps; i++) 457 { 458 CMyComBSTR name; 459 PROPID propID; 460 VARTYPE vt; 461 RINOK(archive->GetPropertyInfo(i, &name, &propID, &vt)); 462 AddProp(name, propID, false); 463 } 464 return S_OK; 465 } 466 467 HRESULT CFieldPrinter::AddRawProps(IArchiveGetRawProps *getRawProps) 468 { 469 UInt32 numProps; 470 RINOK(getRawProps->GetNumRawProps(&numProps)); 471 for (UInt32 i = 0; i < numProps; i++) 472 { 473 CMyComBSTR name; 474 PROPID propID; 475 RINOK(getRawProps->GetRawPropInfo(i, &name, &propID)); 476 AddProp(name, propID, true); 477 } 478 return S_OK; 479 } 480 481 void CFieldPrinter::PrintTitle() 482 { 483 FOR_VECTOR (i, _fields) 484 { 485 const CFieldInfo &f = _fields[i]; 486 PrintSpaces(f.PrefixSpacesWidth); 487 PrintString(f.TitleAdjustment, ((f.PropID == kpidPath) ? 0: f.Width), f.NameA); 488 } 489 } 490 491 void CFieldPrinter::PrintTitleLines() 492 { 493 g_StdOut << LinesString; 494 } 495 496 static void PrintTime(char *dest, const FILETIME *ft) 497 { 498 *dest = 0; 499 if (ft->dwLowDateTime == 0 && ft->dwHighDateTime == 0) 500 return; 501 ConvertUtcFileTimeToString(*ft, dest, kTimestampPrintLevel_SEC); 502 } 503 504 #ifndef _SFX 505 506 static inline char GetHex(Byte value) 507 { 508 return (char)((value < 10) ? ('0' + value) : ('A' + (value - 10))); 509 } 510 511 static void HexToString(char *dest, const Byte *data, UInt32 size) 512 { 513 for (UInt32 i = 0; i < size; i++) 514 { 515 Byte b = data[i]; 516 dest[0] = GetHex((Byte)((b >> 4) & 0xF)); 517 dest[1] = GetHex((Byte)(b & 0xF)); 518 dest += 2; 519 } 520 *dest = 0; 521 } 522 523 #endif 524 525 #define MY_ENDL endl 526 527 HRESULT CFieldPrinter::PrintItemInfo(UInt32 index, const CListStat &st) 528 { 529 char temp[128]; 530 size_t tempPos = 0; 531 532 bool techMode = this->TechMode; 533 /* 534 if (techMode) 535 { 536 g_StdOut << "Index = "; 537 g_StdOut << (UInt64)index; 538 g_StdOut << endl; 539 } 540 */ 541 FOR_VECTOR (i, _fields) 542 { 543 const CFieldInfo &f = _fields[i]; 544 545 if (!techMode) 546 { 547 PrintSpacesToString(temp + tempPos, f.PrefixSpacesWidth); 548 tempPos += f.PrefixSpacesWidth; 549 } 550 551 if (techMode) 552 { 553 if (!f.NameA.IsEmpty()) 554 g_StdOut << f.NameA; 555 else 556 g_StdOut << f.NameU; 557 } 558 559 if (f.PropID == kpidPath) 560 { 561 if (!techMode) 562 g_StdOut << temp; 563 g_StdOut.NormalizePrint_UString(FilePath, TempWString, TempAString); 564 if (techMode) 565 g_StdOut << MY_ENDL; 566 continue; 567 } 568 569 const unsigned width = f.Width; 570 571 if (f.IsRawProp) 572 { 573 #ifndef _SFX 574 575 const void *data; 576 UInt32 dataSize; 577 UInt32 propType; 578 RINOK(Arc->GetRawProps->GetRawProp(index, f.PropID, &data, &dataSize, &propType)); 579 580 if (dataSize != 0) 581 { 582 bool needPrint = true; 583 584 if (f.PropID == kpidNtSecure) 585 { 586 if (propType != NPropDataType::kRaw) 587 return E_FAIL; 588 #ifndef _SFX 589 ConvertNtSecureToString((const Byte *)data, dataSize, TempAString); 590 g_StdOut << TempAString; 591 needPrint = false; 592 #endif 593 } 594 else if (f.PropID == kpidNtReparse) 595 { 596 UString s; 597 if (ConvertNtReparseToString((const Byte *)data, dataSize, s)) 598 { 599 needPrint = false; 600 g_StdOut.PrintUString(s, TempAString); 601 } 602 } 603 604 if (needPrint) 605 { 606 if (propType != NPropDataType::kRaw) 607 return E_FAIL; 608 609 const UInt32 kMaxDataSize = 64; 610 611 if (dataSize > kMaxDataSize) 612 { 613 g_StdOut << "data:"; 614 g_StdOut << dataSize; 615 } 616 else 617 { 618 char hexStr[kMaxDataSize * 2 + 4]; 619 HexToString(hexStr, (const Byte *)data, dataSize); 620 g_StdOut << hexStr; 621 } 622 } 623 } 624 625 #endif 626 } 627 else 628 { 629 CPropVariant prop; 630 switch (f.PropID) 631 { 632 case kpidSize: if (st.Size.Def) prop = st.Size.Val; break; 633 case kpidPackSize: if (st.PackSize.Def) prop = st.PackSize.Val; break; 634 case kpidMTime: if (st.MTime.Def) prop = st.MTime.Val; break; 635 default: 636 RINOK(Arc->Archive->GetProperty(index, f.PropID, &prop)); 637 } 638 if (f.PropID == kpidAttrib && (prop.vt == VT_EMPTY || prop.vt == VT_UI4)) 639 { 640 GetAttribString((prop.vt == VT_EMPTY) ? 0 : prop.ulVal, IsDir, techMode, temp + tempPos); 641 if (techMode) 642 g_StdOut << temp + tempPos; 643 else 644 tempPos += strlen(temp + tempPos); 645 } 646 else if (prop.vt == VT_EMPTY) 647 { 648 if (!techMode) 649 { 650 PrintSpacesToString(temp + tempPos, width); 651 tempPos += width; 652 } 653 } 654 else if (prop.vt == VT_FILETIME) 655 { 656 PrintTime(temp + tempPos, &prop.filetime); 657 if (techMode) 658 g_StdOut << temp + tempPos; 659 else 660 { 661 size_t len = strlen(temp + tempPos); 662 tempPos += len; 663 if (len < (unsigned)f.Width) 664 { 665 len = f.Width - len; 666 PrintSpacesToString(temp + tempPos, (unsigned)len); 667 tempPos += len; 668 } 669 } 670 } 671 else if (prop.vt == VT_BSTR) 672 { 673 TempWString.SetFromBstr(prop.bstrVal); 674 // do we need multi-line support here ? 675 g_StdOut.Normalize_UString(TempWString); 676 if (techMode) 677 { 678 g_StdOut.PrintUString(TempWString, TempAString); 679 } 680 else 681 PrintUString(f.TextAdjustment, width, TempWString, TempAString); 682 } 683 else 684 { 685 char s[64]; 686 ConvertPropertyToShortString2(s, prop, f.PropID); 687 if (techMode) 688 g_StdOut << s; 689 else 690 { 691 PrintStringToString(temp + tempPos, f.TextAdjustment, width, s); 692 tempPos += strlen(temp + tempPos); 693 } 694 } 695 } 696 if (techMode) 697 g_StdOut << MY_ENDL; 698 } 699 g_StdOut << MY_ENDL; 700 return S_OK; 701 } 702 703 static void PrintNumber(EAdjustment adj, unsigned width, const CListUInt64Def &value) 704 { 705 char s[32]; 706 s[0] = 0; 707 if (value.Def) 708 ConvertUInt64ToString(value.Val, s); 709 PrintString(adj, width, s); 710 } 711 712 void Print_UInt64_and_String(AString &s, UInt64 val, const char *name); 713 714 void CFieldPrinter::PrintSum(const CListStat &st, UInt64 numDirs, const char *str) 715 { 716 FOR_VECTOR (i, _fields) 717 { 718 const CFieldInfo &f = _fields[i]; 719 PrintSpaces(f.PrefixSpacesWidth); 720 if (f.PropID == kpidSize) 721 PrintNumber(f.TextAdjustment, f.Width, st.Size); 722 else if (f.PropID == kpidPackSize) 723 PrintNumber(f.TextAdjustment, f.Width, st.PackSize); 724 else if (f.PropID == kpidMTime) 725 { 726 char s[64]; 727 s[0] = 0; 728 if (st.MTime.Def) 729 PrintTime(s, &st.MTime.Val); 730 PrintString(f.TextAdjustment, f.Width, s); 731 } 732 else if (f.PropID == kpidPath) 733 { 734 AString s; 735 Print_UInt64_and_String(s, st.NumFiles, str); 736 if (numDirs != 0) 737 { 738 s += ", "; 739 Print_UInt64_and_String(s, numDirs, kString_Dirs); 740 } 741 PrintString(f.TextAdjustment, 0, s); 742 } 743 else 744 PrintString(f.TextAdjustment, f.Width, ""); 745 } 746 g_StdOut << endl; 747 } 748 749 void CFieldPrinter::PrintSum(const CListStat2 &stat2) 750 { 751 PrintSum(stat2.MainFiles, stat2.NumDirs, kString_Files); 752 if (stat2.AltStreams.NumFiles != 0) 753 { 754 PrintSum(stat2.AltStreams, 0, kString_AltStreams);; 755 CListStat st = stat2.MainFiles; 756 st.Update(stat2.AltStreams); 757 PrintSum(st, 0, kString_Streams); 758 } 759 } 760 761 static HRESULT GetUInt64Value(IInArchive *archive, UInt32 index, PROPID propID, CListUInt64Def &value) 762 { 763 value.Val = 0; 764 value.Def = false; 765 CPropVariant prop; 766 RINOK(archive->GetProperty(index, propID, &prop)); 767 value.Def = ConvertPropVariantToUInt64(prop, value.Val); 768 return S_OK; 769 } 770 771 static HRESULT GetItemMTime(IInArchive *archive, UInt32 index, CListFileTimeDef &t) 772 { 773 t.Val.dwLowDateTime = 0; 774 t.Val.dwHighDateTime = 0; 775 t.Def = false; 776 CPropVariant prop; 777 RINOK(archive->GetProperty(index, kpidMTime, &prop)); 778 if (prop.vt == VT_FILETIME) 779 { 780 t.Val = prop.filetime; 781 t.Def = true; 782 } 783 else if (prop.vt != VT_EMPTY) 784 return E_FAIL; 785 return S_OK; 786 } 787 788 static void PrintPropNameAndNumber(CStdOutStream &so, const char *name, UInt64 val) 789 { 790 so << name << ": " << val << endl; 791 } 792 793 static void PrintPropName_and_Eq(CStdOutStream &so, PROPID propID) 794 { 795 const char *s; 796 char temp[16]; 797 if (propID < ARRAY_SIZE(kPropIdToName)) 798 s = kPropIdToName[propID]; 799 else 800 { 801 ConvertUInt32ToString(propID, temp); 802 s = temp; 803 } 804 so << s << " = "; 805 } 806 807 static void PrintPropNameAndNumber(CStdOutStream &so, PROPID propID, UInt64 val) 808 { 809 PrintPropName_and_Eq(so, propID); 810 so << val << endl; 811 } 812 813 static void PrintPropNameAndNumber_Signed(CStdOutStream &so, PROPID propID, Int64 val) 814 { 815 PrintPropName_and_Eq(so, propID); 816 so << val << endl; 817 } 818 819 820 static void UString_Replace_CRLF_to_LF(UString &s) 821 { 822 // s.Replace(L"\r\n", L"\n"); 823 wchar_t *src = s.GetBuf(); 824 wchar_t *dest = src; 825 for (;;) 826 { 827 wchar_t c = *src++; 828 if (c == 0) 829 break; 830 if (c == '\r' && *src == '\n') 831 { 832 src++; 833 c = '\n'; 834 } 835 *dest++ = c; 836 } 837 s.ReleaseBuf_SetEnd((unsigned)(dest - s.GetBuf())); 838 } 839 840 841 static void PrintPropVal_MultiLine(CStdOutStream &so, const wchar_t *val) 842 { 843 UString s = val; 844 if (s.Find(L'\n') >= 0) 845 { 846 so << endl; 847 so << "{"; 848 so << endl; 849 UString_Replace_CRLF_to_LF(s); 850 so.Normalize_UString__LF_Allowed(s); 851 so << s; 852 so << endl; 853 so << "}"; 854 } 855 else 856 { 857 so.Normalize_UString(s); 858 so << s; 859 } 860 so << endl; 861 } 862 863 864 static void PrintPropPair(CStdOutStream &so, const char *name, const wchar_t *val, bool multiLine) 865 { 866 so << name << " = "; 867 if (multiLine) 868 { 869 PrintPropVal_MultiLine(so, val); 870 return; 871 } 872 UString s = val; 873 so.Normalize_UString(s); 874 so << s; 875 so << endl; 876 } 877 878 879 static void PrintPropertyPair2(CStdOutStream &so, PROPID propID, const wchar_t *name, const CPropVariant &prop) 880 { 881 UString s; 882 ConvertPropertyToString2(s, prop, propID); 883 if (!s.IsEmpty()) 884 { 885 AString nameA; 886 UString nameU; 887 GetPropName(propID, name, nameA, nameU); 888 if (!nameA.IsEmpty()) 889 so << nameA; 890 else 891 so << nameU; 892 so << " = "; 893 PrintPropVal_MultiLine(so, s); 894 } 895 } 896 897 static HRESULT PrintArcProp(CStdOutStream &so, IInArchive *archive, PROPID propID, const wchar_t *name) 898 { 899 CPropVariant prop; 900 RINOK(archive->GetArchiveProperty(propID, &prop)); 901 PrintPropertyPair2(so, propID, name, prop); 902 return S_OK; 903 } 904 905 static void PrintArcTypeError(CStdOutStream &so, const UString &type, bool isWarning) 906 { 907 so << "Open " << (isWarning ? "WARNING" : "ERROR") 908 << ": Can not open the file as [" 909 << type 910 << "] archive" 911 << endl; 912 } 913 914 int Find_FileName_InSortedVector(const UStringVector &fileName, const UString& name); 915 916 void PrintErrorFlags(CStdOutStream &so, const char *s, UInt32 errorFlags); 917 918 static void ErrorInfo_Print(CStdOutStream &so, const CArcErrorInfo &er) 919 { 920 PrintErrorFlags(so, "ERRORS:", er.GetErrorFlags()); 921 if (!er.ErrorMessage.IsEmpty()) 922 PrintPropPair(so, "ERROR", er.ErrorMessage, true); 923 924 PrintErrorFlags(so, "WARNINGS:", er.GetWarningFlags()); 925 if (!er.WarningMessage.IsEmpty()) 926 PrintPropPair(so, "WARNING", er.WarningMessage, true); 927 } 928 929 HRESULT Print_OpenArchive_Props(CStdOutStream &so, const CCodecs *codecs, const CArchiveLink &arcLink) 930 { 931 FOR_VECTOR (r, arcLink.Arcs) 932 { 933 const CArc &arc = arcLink.Arcs[r]; 934 const CArcErrorInfo &er = arc.ErrorInfo; 935 936 so << "--\n"; 937 PrintPropPair(so, "Path", arc.Path, false); 938 if (er.ErrorFormatIndex >= 0) 939 { 940 if (er.ErrorFormatIndex == arc.FormatIndex) 941 so << "Warning: The archive is open with offset" << endl; 942 else 943 PrintArcTypeError(so, codecs->GetFormatNamePtr(er.ErrorFormatIndex), true); 944 } 945 PrintPropPair(so, "Type", codecs->GetFormatNamePtr(arc.FormatIndex), false); 946 947 ErrorInfo_Print(so, er); 948 949 Int64 offset = arc.GetGlobalOffset(); 950 if (offset != 0) 951 PrintPropNameAndNumber_Signed(so, kpidOffset, offset); 952 IInArchive *archive = arc.Archive; 953 RINOK(PrintArcProp(so, archive, kpidPhySize, NULL)); 954 if (er.TailSize != 0) 955 PrintPropNameAndNumber(so, kpidTailSize, er.TailSize); 956 { 957 UInt32 numProps; 958 RINOK(archive->GetNumberOfArchiveProperties(&numProps)); 959 960 for (UInt32 j = 0; j < numProps; j++) 961 { 962 CMyComBSTR name; 963 PROPID propID; 964 VARTYPE vt; 965 RINOK(archive->GetArchivePropertyInfo(j, &name, &propID, &vt)); 966 RINOK(PrintArcProp(so, archive, propID, name)); 967 } 968 } 969 970 if (r != arcLink.Arcs.Size() - 1) 971 { 972 UInt32 numProps; 973 so << "----\n"; 974 if (archive->GetNumberOfProperties(&numProps) == S_OK) 975 { 976 UInt32 mainIndex = arcLink.Arcs[r + 1].SubfileIndex; 977 for (UInt32 j = 0; j < numProps; j++) 978 { 979 CMyComBSTR name; 980 PROPID propID; 981 VARTYPE vt; 982 RINOK(archive->GetPropertyInfo(j, &name, &propID, &vt)); 983 CPropVariant prop; 984 RINOK(archive->GetProperty(mainIndex, propID, &prop)); 985 PrintPropertyPair2(so, propID, name, prop); 986 } 987 } 988 } 989 } 990 return S_OK; 991 } 992 993 HRESULT Print_OpenArchive_Error(CStdOutStream &so, const CCodecs *codecs, const CArchiveLink &arcLink) 994 { 995 #ifndef _NO_CRYPTO 996 if (arcLink.PasswordWasAsked) 997 so << "Can not open encrypted archive. Wrong password?"; 998 else 999 #endif 1000 { 1001 if (arcLink.NonOpen_ErrorInfo.ErrorFormatIndex >= 0) 1002 { 1003 so.NormalizePrint_UString(arcLink.NonOpen_ArcPath); 1004 so << endl; 1005 PrintArcTypeError(so, codecs->Formats[arcLink.NonOpen_ErrorInfo.ErrorFormatIndex].Name, false); 1006 } 1007 else 1008 so << "Can not open the file as archive"; 1009 } 1010 1011 so << endl; 1012 so << endl; 1013 ErrorInfo_Print(so, arcLink.NonOpen_ErrorInfo); 1014 1015 return S_OK; 1016 } 1017 1018 bool CensorNode_CheckPath(const NWildcard::CCensorNode &node, const CReadArcItem &item); 1019 1020 HRESULT ListArchives(CCodecs *codecs, 1021 const CObjectVector<COpenType> &types, 1022 const CIntVector &excludedFormats, 1023 bool stdInMode, 1024 UStringVector &arcPaths, UStringVector &arcPathsFull, 1025 bool processAltStreams, bool showAltStreams, 1026 const NWildcard::CCensorNode &wildcardCensor, 1027 bool enableHeaders, bool techMode, 1028 #ifndef _NO_CRYPTO 1029 bool &passwordEnabled, UString &password, 1030 #endif 1031 #ifndef _SFX 1032 const CObjectVector<CProperty> *props, 1033 #endif 1034 UInt64 &numErrors, 1035 UInt64 &numWarnings) 1036 { 1037 bool allFilesAreAllowed = wildcardCensor.AreAllAllowed(); 1038 1039 numErrors = 0; 1040 numWarnings = 0; 1041 1042 CFieldPrinter fp; 1043 if (!techMode) 1044 fp.Init(kStandardFieldTable, ARRAY_SIZE(kStandardFieldTable)); 1045 1046 CListStat2 stat2total; 1047 1048 CBoolArr skipArcs(arcPaths.Size()); 1049 unsigned arcIndex; 1050 for (arcIndex = 0; arcIndex < arcPaths.Size(); arcIndex++) 1051 skipArcs[arcIndex] = false; 1052 UInt64 numVolumes = 0; 1053 UInt64 numArcs = 0; 1054 UInt64 totalArcSizes = 0; 1055 1056 HRESULT lastError = 0; 1057 1058 for (arcIndex = 0; arcIndex < arcPaths.Size(); arcIndex++) 1059 { 1060 if (skipArcs[arcIndex]) 1061 continue; 1062 const UString &arcPath = arcPaths[arcIndex]; 1063 UInt64 arcPackSize = 0; 1064 1065 if (!stdInMode) 1066 { 1067 NFile::NFind::CFileInfo fi; 1068 if (!fi.Find(us2fs(arcPath))) 1069 { 1070 DWORD errorCode = GetLastError(); 1071 if (errorCode == 0) 1072 errorCode = ERROR_FILE_NOT_FOUND; 1073 lastError = HRESULT_FROM_WIN32(lastError);; 1074 g_StdOut.Flush(); 1075 if (g_ErrStream) 1076 { 1077 *g_ErrStream << endl << kError << NError::MyFormatMessage(errorCode) << endl; 1078 g_ErrStream->NormalizePrint_UString(arcPath); 1079 *g_ErrStream << endl << endl; 1080 } 1081 numErrors++; 1082 continue; 1083 } 1084 if (fi.IsDir()) 1085 { 1086 g_StdOut.Flush(); 1087 if (g_ErrStream) 1088 { 1089 *g_ErrStream << endl << kError; 1090 g_ErrStream->NormalizePrint_UString(arcPath); 1091 *g_ErrStream << " is not a file" << endl << endl; 1092 } 1093 numErrors++; 1094 continue; 1095 } 1096 arcPackSize = fi.Size; 1097 totalArcSizes += arcPackSize; 1098 } 1099 1100 CArchiveLink arcLink; 1101 1102 COpenCallbackConsole openCallback; 1103 openCallback.Init(&g_StdOut, g_ErrStream, NULL); 1104 1105 #ifndef _NO_CRYPTO 1106 1107 openCallback.PasswordIsDefined = passwordEnabled; 1108 openCallback.Password = password; 1109 1110 #endif 1111 1112 /* 1113 CObjectVector<COptionalOpenProperties> optPropsVector; 1114 COptionalOpenProperties &optProps = optPropsVector.AddNew(); 1115 optProps.Props = *props; 1116 */ 1117 1118 COpenOptions options; 1119 #ifndef _SFX 1120 options.props = props; 1121 #endif 1122 options.codecs = codecs; 1123 options.types = &types; 1124 options.excludedFormats = &excludedFormats; 1125 options.stdInMode = stdInMode; 1126 options.stream = NULL; 1127 options.filePath = arcPath; 1128 1129 if (enableHeaders) 1130 { 1131 g_StdOut << endl << kListing; 1132 g_StdOut.NormalizePrint_UString(arcPath); 1133 g_StdOut << endl << endl; 1134 } 1135 1136 HRESULT result = arcLink.Open_Strict(options, &openCallback); 1137 1138 if (result != S_OK) 1139 { 1140 if (result == E_ABORT) 1141 return result; 1142 if (result != S_FALSE) 1143 lastError = result; 1144 g_StdOut.Flush(); 1145 if (g_ErrStream) 1146 { 1147 *g_ErrStream << endl << kError; 1148 g_ErrStream->NormalizePrint_UString(arcPath); 1149 *g_ErrStream << " : "; 1150 if (result == S_FALSE) 1151 { 1152 Print_OpenArchive_Error(*g_ErrStream, codecs, arcLink); 1153 } 1154 else 1155 { 1156 *g_ErrStream << "opening : "; 1157 if (result == E_OUTOFMEMORY) 1158 *g_ErrStream << "Can't allocate required memory"; 1159 else 1160 *g_ErrStream << NError::MyFormatMessage(result); 1161 } 1162 *g_ErrStream << endl; 1163 } 1164 numErrors++; 1165 continue; 1166 } 1167 1168 { 1169 FOR_VECTOR (r, arcLink.Arcs) 1170 { 1171 const CArcErrorInfo &arc = arcLink.Arcs[r].ErrorInfo; 1172 if (!arc.WarningMessage.IsEmpty()) 1173 numWarnings++; 1174 if (arc.AreThereWarnings()) 1175 numWarnings++; 1176 if (arc.ErrorFormatIndex >= 0) 1177 numWarnings++; 1178 if (arc.AreThereErrors()) 1179 { 1180 numErrors++; 1181 // break; 1182 } 1183 if (!arc.ErrorMessage.IsEmpty()) 1184 numErrors++; 1185 } 1186 } 1187 1188 numArcs++; 1189 numVolumes++; 1190 1191 if (!stdInMode) 1192 { 1193 numVolumes += arcLink.VolumePaths.Size(); 1194 totalArcSizes += arcLink.VolumesSize; 1195 FOR_VECTOR (v, arcLink.VolumePaths) 1196 { 1197 int index = Find_FileName_InSortedVector(arcPathsFull, arcLink.VolumePaths[v]); 1198 if (index >= 0 && (unsigned)index > arcIndex) 1199 skipArcs[(unsigned)index] = true; 1200 } 1201 } 1202 1203 1204 if (enableHeaders) 1205 { 1206 RINOK(Print_OpenArchive_Props(g_StdOut, codecs, arcLink)); 1207 1208 g_StdOut << endl; 1209 if (techMode) 1210 g_StdOut << "----------\n"; 1211 } 1212 1213 if (enableHeaders && !techMode) 1214 { 1215 fp.PrintTitle(); 1216 g_StdOut << endl; 1217 fp.PrintTitleLines(); 1218 g_StdOut << endl; 1219 } 1220 1221 const CArc &arc = arcLink.Arcs.Back(); 1222 fp.Arc = &arc; 1223 fp.TechMode = techMode; 1224 IInArchive *archive = arc.Archive; 1225 if (techMode) 1226 { 1227 fp.Clear(); 1228 RINOK(fp.AddMainProps(archive)); 1229 if (arc.GetRawProps) 1230 { 1231 RINOK(fp.AddRawProps(arc.GetRawProps)); 1232 } 1233 } 1234 1235 CListStat2 stat2; 1236 1237 UInt32 numItems; 1238 RINOK(archive->GetNumberOfItems(&numItems)); 1239 1240 CReadArcItem item; 1241 UStringVector pathParts; 1242 1243 for (UInt32 i = 0; i < numItems; i++) 1244 { 1245 if (NConsoleClose::TestBreakSignal()) 1246 return E_ABORT; 1247 1248 HRESULT res = arc.GetItemPath2(i, fp.FilePath); 1249 1250 if (stdInMode && res == E_INVALIDARG) 1251 break; 1252 RINOK(res); 1253 1254 if (arc.Ask_Aux) 1255 { 1256 bool isAux; 1257 RINOK(Archive_IsItem_Aux(archive, i, isAux)); 1258 if (isAux) 1259 continue; 1260 } 1261 1262 bool isAltStream = false; 1263 if (arc.Ask_AltStream) 1264 { 1265 RINOK(Archive_IsItem_AltStream(archive, i, isAltStream)); 1266 if (isAltStream && !processAltStreams) 1267 continue; 1268 } 1269 1270 RINOK(Archive_IsItem_Dir(archive, i, fp.IsDir)); 1271 1272 if (!allFilesAreAllowed) 1273 { 1274 if (isAltStream) 1275 { 1276 RINOK(arc.GetItem(i, item)); 1277 if (!CensorNode_CheckPath(wildcardCensor, item)) 1278 continue; 1279 } 1280 else 1281 { 1282 SplitPathToParts(fp.FilePath, pathParts);; 1283 bool include; 1284 if (!wildcardCensor.CheckPathVect(pathParts, !fp.IsDir, include)) 1285 continue; 1286 if (!include) 1287 continue; 1288 } 1289 } 1290 1291 CListStat st; 1292 1293 RINOK(GetUInt64Value(archive, i, kpidSize, st.Size)); 1294 RINOK(GetUInt64Value(archive, i, kpidPackSize, st.PackSize)); 1295 RINOK(GetItemMTime(archive, i, st.MTime)); 1296 1297 if (fp.IsDir) 1298 stat2.NumDirs++; 1299 else 1300 st.NumFiles = 1; 1301 stat2.GetStat(isAltStream).Update(st); 1302 1303 if (isAltStream && !showAltStreams) 1304 continue; 1305 RINOK(fp.PrintItemInfo(i, st)); 1306 } 1307 1308 UInt64 numStreams = stat2.GetNumStreams(); 1309 if (!stdInMode 1310 && !stat2.MainFiles.PackSize.Def 1311 && !stat2.AltStreams.PackSize.Def) 1312 { 1313 if (arcLink.VolumePaths.Size() != 0) 1314 arcPackSize += arcLink.VolumesSize; 1315 stat2.MainFiles.PackSize.Add((numStreams == 0) ? 0 : arcPackSize); 1316 } 1317 1318 stat2.MainFiles.SetSizeDefIfNoFiles(); 1319 stat2.AltStreams.SetSizeDefIfNoFiles(); 1320 1321 if (enableHeaders && !techMode) 1322 { 1323 fp.PrintTitleLines(); 1324 g_StdOut << endl; 1325 fp.PrintSum(stat2); 1326 } 1327 1328 if (enableHeaders) 1329 { 1330 if (arcLink.NonOpen_ErrorInfo.ErrorFormatIndex >= 0) 1331 { 1332 g_StdOut << "----------\n"; 1333 PrintPropPair(g_StdOut, "Path", arcLink.NonOpen_ArcPath, false); 1334 PrintArcTypeError(g_StdOut, codecs->Formats[arcLink.NonOpen_ErrorInfo.ErrorFormatIndex].Name, false); 1335 } 1336 } 1337 1338 stat2total.Update(stat2); 1339 1340 g_StdOut.Flush(); 1341 } 1342 1343 if (enableHeaders && !techMode && (arcPaths.Size() > 1 || numVolumes > 1)) 1344 { 1345 g_StdOut << endl; 1346 fp.PrintTitleLines(); 1347 g_StdOut << endl; 1348 fp.PrintSum(stat2total); 1349 g_StdOut << endl; 1350 PrintPropNameAndNumber(g_StdOut, "Archives", numArcs); 1351 PrintPropNameAndNumber(g_StdOut, "Volumes", numVolumes); 1352 PrintPropNameAndNumber(g_StdOut, "Total archives size", totalArcSizes); 1353 } 1354 1355 if (numErrors == 1 && lastError != 0) 1356 return lastError; 1357 1358 return S_OK; 1359 } 1360