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   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     << ": Cannot 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 
929 HRESULT Print_OpenArchive_Props(CStdOutStream &so, const CCodecs *codecs, const CArchiveLink &arcLink);
Print_OpenArchive_Props(CStdOutStream & so,const CCodecs * codecs,const CArchiveLink & arcLink)930 HRESULT Print_OpenArchive_Props(CStdOutStream &so, const CCodecs *codecs, const CArchiveLink &arcLink)
931 {
932   FOR_VECTOR (r, arcLink.Arcs)
933   {
934     const CArc &arc = arcLink.Arcs[r];
935     const CArcErrorInfo &er = arc.ErrorInfo;
936 
937     so << "--\n";
938     PrintPropPair(so, "Path", arc.Path, false);
939     if (er.ErrorFormatIndex >= 0)
940     {
941       if (er.ErrorFormatIndex == arc.FormatIndex)
942         so << "Warning: The archive is open with offset" << endl;
943       else
944         PrintArcTypeError(so, codecs->GetFormatNamePtr(er.ErrorFormatIndex), true);
945     }
946     PrintPropPair(so, "Type", codecs->GetFormatNamePtr(arc.FormatIndex), false);
947 
948     ErrorInfo_Print(so, er);
949 
950     Int64 offset = arc.GetGlobalOffset();
951     if (offset != 0)
952       PrintPropNameAndNumber_Signed(so, kpidOffset, offset);
953     IInArchive *archive = arc.Archive;
954     RINOK(PrintArcProp(so, archive, kpidPhySize, NULL));
955     if (er.TailSize != 0)
956       PrintPropNameAndNumber(so, kpidTailSize, er.TailSize);
957     {
958       UInt32 numProps;
959       RINOK(archive->GetNumberOfArchiveProperties(&numProps));
960 
961       for (UInt32 j = 0; j < numProps; j++)
962       {
963         CMyComBSTR name;
964         PROPID propID;
965         VARTYPE vt;
966         RINOK(archive->GetArchivePropertyInfo(j, &name, &propID, &vt));
967         RINOK(PrintArcProp(so, archive, propID, name));
968       }
969     }
970 
971     if (r != arcLink.Arcs.Size() - 1)
972     {
973       UInt32 numProps;
974       so << "----\n";
975       if (archive->GetNumberOfProperties(&numProps) == S_OK)
976       {
977         UInt32 mainIndex = arcLink.Arcs[r + 1].SubfileIndex;
978         for (UInt32 j = 0; j < numProps; j++)
979         {
980           CMyComBSTR name;
981           PROPID propID;
982           VARTYPE vt;
983           RINOK(archive->GetPropertyInfo(j, &name, &propID, &vt));
984           CPropVariant prop;
985           RINOK(archive->GetProperty(mainIndex, propID, &prop));
986           PrintPropertyPair2(so, propID, name, prop);
987         }
988       }
989     }
990   }
991   return S_OK;
992 }
993 
994 HRESULT Print_OpenArchive_Error(CStdOutStream &so, const CCodecs *codecs, const CArchiveLink &arcLink);
Print_OpenArchive_Error(CStdOutStream & so,const CCodecs * codecs,const CArchiveLink & arcLink)995 HRESULT Print_OpenArchive_Error(CStdOutStream &so, const CCodecs *codecs, const CArchiveLink &arcLink)
996 {
997   #ifndef _NO_CRYPTO
998   if (arcLink.PasswordWasAsked)
999     so << "Cannot open encrypted archive. Wrong password?";
1000   else
1001   #endif
1002   {
1003     if (arcLink.NonOpen_ErrorInfo.ErrorFormatIndex >= 0)
1004     {
1005       so.NormalizePrint_UString(arcLink.NonOpen_ArcPath);
1006       so << endl;
1007       PrintArcTypeError(so, codecs->Formats[(unsigned)arcLink.NonOpen_ErrorInfo.ErrorFormatIndex].Name, false);
1008     }
1009     else
1010       so << "Cannot open the file as archive";
1011   }
1012 
1013   so << endl;
1014   so << endl;
1015   ErrorInfo_Print(so, arcLink.NonOpen_ErrorInfo);
1016 
1017   return S_OK;
1018 }
1019 
1020 bool CensorNode_CheckPath(const NWildcard::CCensorNode &node, const CReadArcItem &item);
1021 
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)1022 HRESULT ListArchives(
1023     const CListOptions &listOptions,
1024     CCodecs *codecs,
1025     const CObjectVector<COpenType> &types,
1026     const CIntVector &excludedFormats,
1027     bool stdInMode,
1028     UStringVector &arcPaths, UStringVector &arcPathsFull,
1029     bool processAltStreams, bool showAltStreams,
1030     const NWildcard::CCensorNode &wildcardCensor,
1031     bool enableHeaders, bool techMode,
1032     #ifndef _NO_CRYPTO
1033     bool &passwordEnabled, UString &password,
1034     #endif
1035     #ifndef _SFX
1036     const CObjectVector<CProperty> *props,
1037     #endif
1038     UInt64 &numErrors,
1039     UInt64 &numWarnings)
1040 {
1041   bool allFilesAreAllowed = wildcardCensor.AreAllAllowed();
1042 
1043   numErrors = 0;
1044   numWarnings = 0;
1045 
1046   CFieldPrinter fp;
1047   if (!techMode)
1048     fp.Init(kStandardFieldTable, ARRAY_SIZE(kStandardFieldTable));
1049 
1050   CListStat2 stat2total;
1051 
1052   CBoolArr skipArcs(arcPaths.Size());
1053   unsigned arcIndex;
1054   for (arcIndex = 0; arcIndex < arcPaths.Size(); arcIndex++)
1055     skipArcs[arcIndex] = false;
1056   UInt64 numVolumes = 0;
1057   UInt64 numArcs = 0;
1058   UInt64 totalArcSizes = 0;
1059 
1060   HRESULT lastError = 0;
1061 
1062   for (arcIndex = 0; arcIndex < arcPaths.Size(); arcIndex++)
1063   {
1064     if (skipArcs[arcIndex])
1065       continue;
1066     const UString &arcPath = arcPaths[arcIndex];
1067     UInt64 arcPackSize = 0;
1068 
1069     if (!stdInMode)
1070     {
1071       NFile::NFind::CFileInfo fi;
1072       if (!fi.Find_FollowLink(us2fs(arcPath)))
1073       {
1074         DWORD errorCode = GetLastError();
1075         if (errorCode == 0)
1076           errorCode = ERROR_FILE_NOT_FOUND;
1077         lastError = HRESULT_FROM_WIN32(errorCode);
1078         g_StdOut.Flush();
1079         if (g_ErrStream)
1080         {
1081           *g_ErrStream << endl << kError << NError::MyFormatMessage(errorCode) << endl;
1082           g_ErrStream->NormalizePrint_UString(arcPath);
1083           *g_ErrStream << endl << endl;
1084         }
1085         numErrors++;
1086         continue;
1087       }
1088       if (fi.IsDir())
1089       {
1090         g_StdOut.Flush();
1091         if (g_ErrStream)
1092         {
1093           *g_ErrStream << endl << kError;
1094           g_ErrStream->NormalizePrint_UString(arcPath);
1095           *g_ErrStream << " is not a file" << endl << endl;
1096         }
1097         numErrors++;
1098         continue;
1099       }
1100       arcPackSize = fi.Size;
1101       totalArcSizes += arcPackSize;
1102     }
1103 
1104     CArchiveLink arcLink;
1105 
1106     COpenCallbackConsole openCallback;
1107     openCallback.Init(&g_StdOut, g_ErrStream, NULL);
1108 
1109     #ifndef _NO_CRYPTO
1110 
1111     openCallback.PasswordIsDefined = passwordEnabled;
1112     openCallback.Password = password;
1113 
1114     #endif
1115 
1116     /*
1117     CObjectVector<COptionalOpenProperties> optPropsVector;
1118     COptionalOpenProperties &optProps = optPropsVector.AddNew();
1119     optProps.Props = *props;
1120     */
1121 
1122     COpenOptions options;
1123     #ifndef _SFX
1124     options.props = props;
1125     #endif
1126     options.codecs = codecs;
1127     options.types = &types;
1128     options.excludedFormats = &excludedFormats;
1129     options.stdInMode = stdInMode;
1130     options.stream = NULL;
1131     options.filePath = arcPath;
1132 
1133     if (enableHeaders)
1134     {
1135       g_StdOut << endl << kListing;
1136       g_StdOut.NormalizePrint_UString(arcPath);
1137       g_StdOut << endl << endl;
1138     }
1139 
1140     HRESULT result = arcLink.Open_Strict(options, &openCallback);
1141 
1142     if (result != S_OK)
1143     {
1144       if (result == E_ABORT)
1145         return result;
1146       if (result != S_FALSE)
1147         lastError = result;
1148       g_StdOut.Flush();
1149       if (g_ErrStream)
1150       {
1151         *g_ErrStream << endl << kError;
1152         g_ErrStream->NormalizePrint_UString(arcPath);
1153         *g_ErrStream << " : ";
1154         if (result == S_FALSE)
1155         {
1156           Print_OpenArchive_Error(*g_ErrStream, codecs, arcLink);
1157         }
1158         else
1159         {
1160           *g_ErrStream << "opening : ";
1161           if (result == E_OUTOFMEMORY)
1162             *g_ErrStream << "Can't allocate required memory";
1163           else
1164             *g_ErrStream << NError::MyFormatMessage(result);
1165         }
1166         *g_ErrStream << endl;
1167       }
1168       numErrors++;
1169       continue;
1170     }
1171 
1172     {
1173       FOR_VECTOR (r, arcLink.Arcs)
1174       {
1175         const CArcErrorInfo &arc = arcLink.Arcs[r].ErrorInfo;
1176         if (!arc.WarningMessage.IsEmpty())
1177           numWarnings++;
1178         if (arc.AreThereWarnings())
1179           numWarnings++;
1180         if (arc.ErrorFormatIndex >= 0)
1181           numWarnings++;
1182         if (arc.AreThereErrors())
1183         {
1184           numErrors++;
1185           // break;
1186         }
1187         if (!arc.ErrorMessage.IsEmpty())
1188           numErrors++;
1189       }
1190     }
1191 
1192     numArcs++;
1193     numVolumes++;
1194 
1195     if (!stdInMode)
1196     {
1197       numVolumes += arcLink.VolumePaths.Size();
1198       totalArcSizes += arcLink.VolumesSize;
1199       FOR_VECTOR (v, arcLink.VolumePaths)
1200       {
1201         int index = Find_FileName_InSortedVector(arcPathsFull, arcLink.VolumePaths[v]);
1202         if (index >= 0 && (unsigned)index > arcIndex)
1203           skipArcs[(unsigned)index] = true;
1204       }
1205     }
1206 
1207 
1208     if (enableHeaders)
1209     {
1210       RINOK(Print_OpenArchive_Props(g_StdOut, codecs, arcLink));
1211 
1212       g_StdOut << endl;
1213       if (techMode)
1214         g_StdOut << "----------\n";
1215     }
1216 
1217     if (enableHeaders && !techMode)
1218     {
1219       fp.PrintTitle();
1220       g_StdOut << endl;
1221       fp.PrintTitleLines();
1222       g_StdOut << endl;
1223     }
1224 
1225     const CArc &arc = arcLink.Arcs.Back();
1226     fp.Arc = &arc;
1227     fp.TechMode = techMode;
1228     IInArchive *archive = arc.Archive;
1229     if (techMode)
1230     {
1231       fp.Clear();
1232       RINOK(fp.AddMainProps(archive));
1233       if (arc.GetRawProps)
1234       {
1235         RINOK(fp.AddRawProps(arc.GetRawProps));
1236       }
1237     }
1238 
1239     CListStat2 stat2;
1240 
1241     UInt32 numItems;
1242     RINOK(archive->GetNumberOfItems(&numItems));
1243 
1244     CReadArcItem item;
1245     UStringVector pathParts;
1246 
1247     for (UInt32 i = 0; i < numItems; i++)
1248     {
1249       if (NConsoleClose::TestBreakSignal())
1250         return E_ABORT;
1251 
1252       HRESULT res = arc.GetItemPath2(i, fp.FilePath);
1253 
1254       if (stdInMode && res == E_INVALIDARG)
1255         break;
1256       RINOK(res);
1257 
1258       if (arc.Ask_Aux)
1259       {
1260         bool isAux;
1261         RINOK(Archive_IsItem_Aux(archive, i, isAux));
1262         if (isAux)
1263           continue;
1264       }
1265 
1266       bool isAltStream = false;
1267       if (arc.Ask_AltStream)
1268       {
1269         RINOK(Archive_IsItem_AltStream(archive, i, isAltStream));
1270         if (isAltStream && !processAltStreams)
1271           continue;
1272       }
1273 
1274       RINOK(Archive_IsItem_Dir(archive, i, fp.IsDir));
1275 
1276       if (fp.IsDir ? listOptions.ExcludeDirItems : listOptions.ExcludeFileItems)
1277         continue;
1278 
1279       if (!allFilesAreAllowed)
1280       {
1281         if (isAltStream)
1282         {
1283           RINOK(arc.GetItem(i, item));
1284           if (!CensorNode_CheckPath(wildcardCensor, item))
1285             continue;
1286         }
1287         else
1288         {
1289           SplitPathToParts(fp.FilePath, pathParts);
1290           bool include;
1291           if (!wildcardCensor.CheckPathVect(pathParts, !fp.IsDir, include))
1292             continue;
1293           if (!include)
1294             continue;
1295         }
1296       }
1297 
1298       CListStat st;
1299 
1300       RINOK(GetUInt64Value(archive, i, kpidSize, st.Size));
1301       RINOK(GetUInt64Value(archive, i, kpidPackSize, st.PackSize));
1302       RINOK(GetItemMTime(archive, i, st.MTime));
1303 
1304       if (fp.IsDir)
1305         stat2.NumDirs++;
1306       else
1307         st.NumFiles = 1;
1308       stat2.GetStat(isAltStream).Update(st);
1309 
1310       if (isAltStream && !showAltStreams)
1311         continue;
1312       RINOK(fp.PrintItemInfo(i, st));
1313     }
1314 
1315     UInt64 numStreams = stat2.GetNumStreams();
1316     if (!stdInMode
1317         && !stat2.MainFiles.PackSize.Def
1318         && !stat2.AltStreams.PackSize.Def)
1319     {
1320       if (arcLink.VolumePaths.Size() != 0)
1321         arcPackSize += arcLink.VolumesSize;
1322       stat2.MainFiles.PackSize.Add((numStreams == 0) ? 0 : arcPackSize);
1323     }
1324 
1325     stat2.MainFiles.SetSizeDefIfNoFiles();
1326     stat2.AltStreams.SetSizeDefIfNoFiles();
1327 
1328     if (enableHeaders && !techMode)
1329     {
1330       fp.PrintTitleLines();
1331       g_StdOut << endl;
1332       fp.PrintSum(stat2);
1333     }
1334 
1335     if (enableHeaders)
1336     {
1337       if (arcLink.NonOpen_ErrorInfo.ErrorFormatIndex >= 0)
1338       {
1339         g_StdOut << "----------\n";
1340         PrintPropPair(g_StdOut, "Path", arcLink.NonOpen_ArcPath, false);
1341         PrintArcTypeError(g_StdOut, codecs->Formats[(unsigned)arcLink.NonOpen_ErrorInfo.ErrorFormatIndex].Name, false);
1342       }
1343     }
1344 
1345     stat2total.Update(stat2);
1346 
1347     g_StdOut.Flush();
1348   }
1349 
1350   if (enableHeaders && !techMode && (arcPaths.Size() > 1 || numVolumes > 1))
1351   {
1352     g_StdOut << endl;
1353     fp.PrintTitleLines();
1354     g_StdOut << endl;
1355     fp.PrintSum(stat2total);
1356     g_StdOut << endl;
1357     PrintPropNameAndNumber(g_StdOut, "Archives", numArcs);
1358     PrintPropNameAndNumber(g_StdOut, "Volumes", numVolumes);
1359     PrintPropNameAndNumber(g_StdOut, "Total archives size", totalArcSizes);
1360   }
1361 
1362   if (numErrors == 1 && lastError != 0)
1363     return lastError;
1364 
1365   return S_OK;
1366 }
1367