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