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