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