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