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