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 
10 #include "Windows/Error.h"
11 #include "Windows/FileDir.h"
12 #include "Windows/PropVariant.h"
13 #include "Windows/PropVariantConversions.h"
14 
15 #include "../../Archive/IArchive.h"
16 
17 #include "../Common/OpenArchive.h"
18 #include "../Common/PropIDUtils.h"
19 
20 #include "ConsoleClose.h"
21 #include "List.h"
22 #include "OpenCallbackConsole.h"
23 
24 using namespace NWindows;
25 
26 struct CPropIdToName
27 {
28   PROPID PropID;
29   const wchar_t *Name;
30 };
31 
32 static const CPropIdToName kPropIdToName[] =
33 {
34   { kpidPath, L"Path" },
35   { kpidName, L"Name" },
36   { kpidIsDir, L"Folder" },
37   { kpidSize, L"Size" },
38   { kpidPackSize, L"Packed Size" },
39   { kpidAttrib, L"Attributes" },
40   { kpidCTime, L"Created" },
41   { kpidATime, L"Accessed" },
42   { kpidMTime, L"Modified" },
43   { kpidSolid, L"Solid" },
44   { kpidCommented, L"Commented" },
45   { kpidEncrypted, L"Encrypted" },
46   { kpidSplitBefore, L"Split Before" },
47   { kpidSplitAfter, L"Split After" },
48   { kpidDictionarySize, L"Dictionary Size" },
49   { kpidCRC, L"CRC" },
50   { kpidType, L"Type" },
51   { kpidIsAnti, L"Anti" },
52   { kpidMethod, L"Method" },
53   { kpidHostOS, L"Host OS" },
54   { kpidFileSystem, L"File System" },
55   { kpidUser, L"User" },
56   { kpidGroup, L"Group" },
57   { kpidBlock, L"Block" },
58   { kpidComment, L"Comment" },
59   { kpidPosition, L"Position" },
60   { kpidPrefix, L"Prefix" },
61   { kpidNumSubDirs, L"Folders" },
62   { kpidNumSubFiles, L"Files" },
63   { kpidUnpackVer, L"Version" },
64   { kpidVolume, L"Volume" },
65   { kpidIsVolume, L"Multivolume" },
66   { kpidOffset, L"Offset" },
67   { kpidLinks, L"Links" },
68   { kpidNumBlocks, L"Blocks" },
69   { kpidNumVolumes, L"Volumes" },
70 
71   { kpidBit64, L"64-bit" },
72   { kpidBigEndian, L"Big-endian" },
73   { kpidCpu, L"CPU" },
74   { kpidPhySize, L"Physical Size" },
75   { kpidHeadersSize, L"Headers Size" },
76   { kpidChecksum, L"Checksum" },
77   { kpidCharacts, L"Characteristics" },
78   { kpidVa, L"Virtual Address" },
79   { kpidId, L"ID" },
80   { kpidShortName, L"Short Name" },
81   { kpidCreatorApp, L"Creator Application"},
82   { kpidSectorSize, L"Sector Size" },
83   { kpidPosixAttrib, L"Mode" },
84   { kpidLink, L"Link" },
85   { kpidError, L"Error" },
86 
87   { kpidTotalSize, L"Total Size" },
88   { kpidFreeSpace, L"Free Space" },
89   { kpidClusterSize, L"Cluster Size" },
90   { kpidVolumeName, L"Label" }
91 };
92 
93 static const char kEmptyAttribChar = '.';
94 
95 static const char *kListing = "Listing archive: ";
96 static const wchar_t *kFilesMessage = L"files";
97 static const wchar_t *kDirsMessage = L"folders";
98 
GetAttribString(DWORD wa,bool isDir,char * s)99 static void GetAttribString(DWORD wa, bool isDir, char *s)
100 {
101   s[0] = ((wa & FILE_ATTRIBUTE_DIRECTORY) != 0 || isDir) ? 'D' : kEmptyAttribChar;
102   s[1] = ((wa & FILE_ATTRIBUTE_READONLY) != 0) ? 'R': kEmptyAttribChar;
103   s[2] = ((wa & FILE_ATTRIBUTE_HIDDEN) != 0) ? 'H': kEmptyAttribChar;
104   s[3] = ((wa & FILE_ATTRIBUTE_SYSTEM) != 0) ? 'S': kEmptyAttribChar;
105   s[4] = ((wa & FILE_ATTRIBUTE_ARCHIVE) != 0) ? 'A': kEmptyAttribChar;
106   s[5] = '\0';
107 }
108 
109 enum EAdjustment
110 {
111   kLeft,
112   kCenter,
113   kRight
114 };
115 
116 struct CFieldInfo
117 {
118   PROPID PropID;
119   UString Name;
120   EAdjustment TitleAdjustment;
121   EAdjustment TextAdjustment;
122   int PrefixSpacesWidth;
123   int Width;
124 };
125 
126 struct CFieldInfoInit
127 {
128   PROPID PropID;
129   const wchar_t *Name;
130   EAdjustment TitleAdjustment;
131   EAdjustment TextAdjustment;
132   int PrefixSpacesWidth;
133   int Width;
134 };
135 
136 static CFieldInfoInit kStandardFieldTable[] =
137 {
138   { kpidMTime, L"   Date      Time", kLeft, kLeft, 0, 19 },
139   { kpidAttrib, L"Attr", kRight, kCenter, 1, 5 },
140   { kpidSize, L"Size", kRight, kRight, 1, 12 },
141   { kpidPackSize, L"Compressed", kRight, kRight, 1, 12 },
142   { kpidPath, L"Name", kLeft, kLeft, 2, 24 }
143 };
144 
PrintSpaces(int numSpaces)145 static void PrintSpaces(int numSpaces)
146 {
147   for (int i = 0; i < numSpaces; i++)
148     g_StdOut << ' ';
149 }
150 
PrintString(EAdjustment adjustment,int width,const UString & textString)151 static void PrintString(EAdjustment adjustment, int width, const UString &textString)
152 {
153   const int numSpaces = width - textString.Length();
154   int numLeftSpaces = 0;
155   switch (adjustment)
156   {
157     case kLeft:
158       numLeftSpaces = 0;
159       break;
160     case kCenter:
161       numLeftSpaces = numSpaces / 2;
162       break;
163     case kRight:
164       numLeftSpaces = numSpaces;
165       break;
166   }
167   PrintSpaces(numLeftSpaces);
168   g_StdOut << textString;
169   PrintSpaces(numSpaces - numLeftSpaces);
170 }
171 
172 class CFieldPrinter
173 {
174   CObjectVector<CFieldInfo> _fields;
175 public:
Clear()176   void Clear() { _fields.Clear(); }
177   void Init(const CFieldInfoInit *standardFieldTable, int numItems);
178   HRESULT Init(IInArchive *archive);
179   void PrintTitle();
180   void PrintTitleLines();
181   HRESULT PrintItemInfo(const CArc &arc, UInt32 index, bool techMode);
182   HRESULT PrintSummaryInfo(UInt64 numFiles, UInt64 numDirs,
183       const UInt64 *size, const UInt64 *compressedSize);
184 };
185 
Init(const CFieldInfoInit * standardFieldTable,int numItems)186 void CFieldPrinter::Init(const CFieldInfoInit *standardFieldTable, int numItems)
187 {
188   Clear();
189   for (int i = 0; i < numItems; i++)
190   {
191     CFieldInfo fieldInfo;
192     const CFieldInfoInit &fieldInfoInit = standardFieldTable[i];
193     fieldInfo.PropID = fieldInfoInit.PropID;
194     fieldInfo.Name = fieldInfoInit.Name;
195     fieldInfo.TitleAdjustment = fieldInfoInit.TitleAdjustment;
196     fieldInfo.TextAdjustment = fieldInfoInit.TextAdjustment;
197     fieldInfo.PrefixSpacesWidth = fieldInfoInit.PrefixSpacesWidth;
198     fieldInfo.Width = fieldInfoInit.Width;
199     _fields.Add(fieldInfo);
200   }
201 }
202 
GetPropName(PROPID propID,BSTR name)203 static UString GetPropName(PROPID propID, BSTR name)
204 {
205   for (int i = 0; i < sizeof(kPropIdToName) / sizeof(kPropIdToName[0]); i++)
206   {
207     const CPropIdToName &propIdToName = kPropIdToName[i];
208     if (propIdToName.PropID == propID)
209       return propIdToName.Name;
210   }
211   if (name)
212     return name;
213   wchar_t s[16];
214   ConvertUInt32ToString(propID, s);
215   return s;
216 }
217 
Init(IInArchive * archive)218 HRESULT CFieldPrinter::Init(IInArchive *archive)
219 {
220   Clear();
221   UInt32 numProps;
222   RINOK(archive->GetNumberOfProperties(&numProps));
223   for (UInt32 i = 0; i < numProps; i++)
224   {
225     CMyComBSTR name;
226     PROPID propID;
227     VARTYPE vt;
228     RINOK(archive->GetPropertyInfo(i, &name, &propID, &vt));
229     CFieldInfo fieldInfo;
230     fieldInfo.PropID = propID;
231     fieldInfo.Name = GetPropName(propID, name);
232     _fields.Add(fieldInfo);
233   }
234   return S_OK;
235 }
236 
PrintTitle()237 void CFieldPrinter::PrintTitle()
238 {
239   for (int i = 0; i < _fields.Size(); i++)
240   {
241     const CFieldInfo &fieldInfo = _fields[i];
242     PrintSpaces(fieldInfo.PrefixSpacesWidth);
243     PrintString(fieldInfo.TitleAdjustment,
244       ((fieldInfo.PropID == kpidPath) ? 0: fieldInfo.Width), fieldInfo.Name);
245   }
246 }
247 
PrintTitleLines()248 void CFieldPrinter::PrintTitleLines()
249 {
250   for (int i = 0; i < _fields.Size(); i++)
251   {
252     const CFieldInfo &fieldInfo = _fields[i];
253     PrintSpaces(fieldInfo.PrefixSpacesWidth);
254     for (int i = 0; i < fieldInfo.Width; i++)
255       g_StdOut << '-';
256   }
257 }
258 
259 
IsFileTimeZero(CONST FILETIME * lpFileTime)260 static BOOL IsFileTimeZero(CONST FILETIME *lpFileTime)
261 {
262   return (lpFileTime->dwLowDateTime == 0) && (lpFileTime->dwHighDateTime == 0);
263 }
264 
265 static const char *kEmptyTimeString = "                   ";
PrintTime(const NCOM::CPropVariant & prop)266 static void PrintTime(const NCOM::CPropVariant &prop)
267 {
268   if (prop.vt != VT_FILETIME)
269     throw "incorrect item";
270   if (IsFileTimeZero(&prop.filetime))
271     g_StdOut << kEmptyTimeString;
272   else
273   {
274     FILETIME localFileTime;
275     if (!FileTimeToLocalFileTime(&prop.filetime, &localFileTime))
276       throw "FileTimeToLocalFileTime error";
277     char s[32];
278     if (ConvertFileTimeToString(localFileTime, s, true, true))
279       g_StdOut << s;
280     else
281       g_StdOut << kEmptyTimeString;
282   }
283 }
284 
PrintItemInfo(const CArc & arc,UInt32 index,bool techMode)285 HRESULT CFieldPrinter::PrintItemInfo(const CArc &arc, UInt32 index, bool techMode)
286 {
287   /*
288   if (techMode)
289   {
290     g_StdOut << "Index = ";
291     g_StdOut << (UInt64)index;
292     g_StdOut << endl;
293   }
294   */
295   for (int i = 0; i < _fields.Size(); i++)
296   {
297     const CFieldInfo &fieldInfo = _fields[i];
298     if (!techMode)
299       PrintSpaces(fieldInfo.PrefixSpacesWidth);
300 
301     NCOM::CPropVariant prop;
302     if (fieldInfo.PropID == kpidPath)
303     {
304       UString s;
305       RINOK(arc.GetItemPath(index, s));
306       prop = s;
307     }
308     else
309     {
310       RINOK(arc.Archive->GetProperty(index, fieldInfo.PropID, &prop));
311     }
312     if (techMode)
313     {
314       g_StdOut << fieldInfo.Name << " = ";
315     }
316     int width = (fieldInfo.PropID == kpidPath) ? 0: fieldInfo.Width;
317     if (fieldInfo.PropID == kpidAttrib && (prop.vt == VT_EMPTY || prop.vt == VT_UI4))
318     {
319       UInt32 attrib = (prop.vt == VT_EMPTY) ? 0 : prop.ulVal;
320       bool isFolder;
321       RINOK(IsArchiveItemFolder(arc.Archive, index, isFolder));
322       char s[8];
323       GetAttribString(attrib, isFolder, s);
324       g_StdOut << s;
325     }
326     else if (prop.vt == VT_EMPTY)
327     {
328       if (!techMode)
329         PrintSpaces(width);
330     }
331     else if (fieldInfo.PropID == kpidMTime)
332     {
333       PrintTime(prop);
334     }
335     else if (prop.vt == VT_BSTR)
336     {
337       if (techMode)
338         g_StdOut << prop.bstrVal;
339       else
340         PrintString(fieldInfo.TextAdjustment, width, prop.bstrVal);
341     }
342     else
343     {
344       UString s = ConvertPropertyToString(prop, fieldInfo.PropID);
345       s.Replace(wchar_t(0xA), L' ');
346       s.Replace(wchar_t(0xD), L' ');
347 
348       if (techMode)
349         g_StdOut << s;
350       else
351         PrintString(fieldInfo.TextAdjustment, width, s);
352     }
353     if (techMode)
354       g_StdOut << endl;
355   }
356   return S_OK;
357 }
358 
PrintNumberString(EAdjustment adjustment,int width,const UInt64 * value)359 static void PrintNumberString(EAdjustment adjustment, int width, const UInt64 *value)
360 {
361   wchar_t textString[32] = { 0 };
362   if (value != NULL)
363     ConvertUInt64ToString(*value, textString);
364   PrintString(adjustment, width, textString);
365 }
366 
367 
PrintSummaryInfo(UInt64 numFiles,UInt64 numDirs,const UInt64 * size,const UInt64 * compressedSize)368 HRESULT CFieldPrinter::PrintSummaryInfo(UInt64 numFiles, UInt64 numDirs,
369     const UInt64 *size, const UInt64 *compressedSize)
370 {
371   for (int i = 0; i < _fields.Size(); i++)
372   {
373     const CFieldInfo &fieldInfo = _fields[i];
374     PrintSpaces(fieldInfo.PrefixSpacesWidth);
375     NCOM::CPropVariant prop;
376     if (fieldInfo.PropID == kpidSize)
377       PrintNumberString(fieldInfo.TextAdjustment, fieldInfo.Width, size);
378     else if (fieldInfo.PropID == kpidPackSize)
379       PrintNumberString(fieldInfo.TextAdjustment, fieldInfo.Width, compressedSize);
380     else if (fieldInfo.PropID == kpidPath)
381     {
382       wchar_t textString[32];
383       ConvertUInt64ToString(numFiles, textString);
384       UString temp = textString;
385       temp += L" ";
386       temp += kFilesMessage;
387       temp += L", ";
388       ConvertUInt64ToString(numDirs, textString);
389       temp += textString;
390       temp += L" ";
391       temp += kDirsMessage;
392       PrintString(fieldInfo.TextAdjustment, 0, temp);
393     }
394     else
395       PrintString(fieldInfo.TextAdjustment, fieldInfo.Width, L"");
396   }
397   return S_OK;
398 }
399 
GetUInt64Value(IInArchive * archive,UInt32 index,PROPID propID,UInt64 & value)400 bool GetUInt64Value(IInArchive *archive, UInt32 index, PROPID propID, UInt64 &value)
401 {
402   NCOM::CPropVariant prop;
403   if (archive->GetProperty(index, propID, &prop) != S_OK)
404     throw "GetPropertyValue error";
405   if (prop.vt == VT_EMPTY)
406     return false;
407   value = ConvertPropVariantToUInt64(prop);
408   return true;
409 }
410 
PrintPropPair(const wchar_t * name,const wchar_t * value)411 static void PrintPropPair(const wchar_t *name, const wchar_t *value)
412 {
413   g_StdOut << name << " = " << value << endl;
414 }
415 
ListArchives(CCodecs * codecs,const CIntVector & formatIndices,bool stdInMode,UStringVector & arcPaths,UStringVector & arcPathsFull,const NWildcard::CCensorNode & wildcardCensor,bool enableHeaders,bool techMode,bool & passwordEnabled,UString & password,UInt64 & numErrors)416 HRESULT ListArchives(CCodecs *codecs, const CIntVector &formatIndices,
417     bool stdInMode,
418     UStringVector &arcPaths, UStringVector &arcPathsFull,
419     const NWildcard::CCensorNode &wildcardCensor,
420     bool enableHeaders, bool techMode,
421     #ifndef _NO_CRYPTO
422     bool &passwordEnabled, UString &password,
423     #endif
424     UInt64 &numErrors)
425 {
426   numErrors = 0;
427   CFieldPrinter fieldPrinter;
428   if (!techMode)
429     fieldPrinter.Init(kStandardFieldTable, sizeof(kStandardFieldTable) / sizeof(kStandardFieldTable[0]));
430 
431   UInt64 numFiles2 = 0, numDirs2 = 0, totalPackSize2 = 0, totalUnPackSize2 = 0;
432   UInt64 *totalPackSizePointer2 = 0, *totalUnPackSizePointer2 = 0;
433   int numArcs = /* stdInMode ? 1 : */ arcPaths.Size();
434   for (int i = 0; i < numArcs; i++)
435   {
436     const UString &archiveName = arcPaths[i];
437     UInt64 arcPackSize = 0;
438     if (!stdInMode)
439     {
440       NFile::NFind::CFileInfo fi;
441       if (!fi.Find(us2fs(archiveName)) || fi.IsDir())
442       {
443         g_StdOut << endl << "Error: " << archiveName << " is not file" << endl;
444         numErrors++;
445         continue;
446       }
447       arcPackSize = fi.Size;
448     }
449 
450     CArchiveLink archiveLink;
451 
452     COpenCallbackConsole openCallback;
453     openCallback.OutStream = &g_StdOut;
454 
455     #ifndef _NO_CRYPTO
456 
457     openCallback.PasswordIsDefined = passwordEnabled;
458     openCallback.Password = password;
459 
460     #endif
461 
462     HRESULT result = archiveLink.Open2(codecs, formatIndices, stdInMode, NULL, archiveName, &openCallback);
463     if (result != S_OK)
464     {
465       if (result == E_ABORT)
466         return result;
467       g_StdOut << endl << "Error: " << archiveName << ": ";
468       if (result == S_FALSE)
469       {
470         #ifndef _NO_CRYPTO
471         if (openCallback.Open_WasPasswordAsked())
472           g_StdOut << "Can not open encrypted archive. Wrong password?";
473         else
474         #endif
475           g_StdOut << "Can not open file as archive";
476       }
477       else if (result == E_OUTOFMEMORY)
478         g_StdOut << "Can't allocate required memory";
479       else
480         g_StdOut << NError::MyFormatMessageW(result);
481       g_StdOut << endl;
482       numErrors++;
483       continue;
484     }
485 
486     if (!stdInMode)
487     for (int v = 0; v < archiveLink.VolumePaths.Size(); v++)
488     {
489       int index = arcPathsFull.FindInSorted(archiveLink.VolumePaths[v]);
490       if (index >= 0 && index > i)
491       {
492         arcPaths.Delete(index);
493         arcPathsFull.Delete(index);
494         numArcs = arcPaths.Size();
495       }
496     }
497 
498     if (enableHeaders)
499     {
500       g_StdOut << endl << kListing << archiveName << endl << endl;
501 
502       for (int i = 0; i < archiveLink.Arcs.Size(); i++)
503       {
504         const CArc &arc = archiveLink.Arcs[i];
505 
506         g_StdOut << "--\n";
507         PrintPropPair(L"Path", arc.Path);
508         PrintPropPair(L"Type", codecs->Formats[arc.FormatIndex].Name);
509         if (!arc.ErrorMessage.IsEmpty())
510           PrintPropPair(L"Error", arc.ErrorMessage);
511         UInt32 numProps;
512         IInArchive *archive = arc.Archive;
513         if (archive->GetNumberOfArchiveProperties(&numProps) == S_OK)
514         {
515           for (UInt32 j = 0; j < numProps; j++)
516           {
517             CMyComBSTR name;
518             PROPID propID;
519             VARTYPE vt;
520             RINOK(archive->GetArchivePropertyInfo(j, &name, &propID, &vt));
521             NCOM::CPropVariant prop;
522             RINOK(archive->GetArchiveProperty(propID, &prop));
523             UString s = ConvertPropertyToString(prop, propID);
524             if (!s.IsEmpty())
525               PrintPropPair(GetPropName(propID, name), s);
526           }
527         }
528         if (i != archiveLink.Arcs.Size() - 1)
529         {
530           UInt32 numProps;
531           g_StdOut << "----\n";
532           if (archive->GetNumberOfProperties(&numProps) == S_OK)
533           {
534             UInt32 mainIndex = archiveLink.Arcs[i + 1].SubfileIndex;
535             for (UInt32 j = 0; j < numProps; j++)
536             {
537               CMyComBSTR name;
538               PROPID propID;
539               VARTYPE vt;
540               RINOK(archive->GetPropertyInfo(j, &name, &propID, &vt));
541               NCOM::CPropVariant prop;
542               RINOK(archive->GetProperty(mainIndex, propID, &prop));
543               UString s = ConvertPropertyToString(prop, propID);
544               if (!s.IsEmpty())
545                 PrintPropPair(GetPropName(propID, name), s);
546             }
547           }
548         }
549 
550       }
551       g_StdOut << endl;
552       if (techMode)
553         g_StdOut << "----------\n";
554     }
555 
556     if (enableHeaders && !techMode)
557     {
558       fieldPrinter.PrintTitle();
559       g_StdOut << endl;
560       fieldPrinter.PrintTitleLines();
561       g_StdOut << endl;
562     }
563 
564     const CArc &arc = archiveLink.Arcs.Back();
565     IInArchive *archive = arc.Archive;
566     if (techMode)
567     {
568       RINOK(fieldPrinter.Init(archive));
569     }
570     UInt64 numFiles = 0, numDirs = 0, totalPackSize = 0, totalUnPackSize = 0;
571     UInt64 *totalPackSizePointer = 0, *totalUnPackSizePointer = 0;
572     UInt32 numItems;
573     RINOK(archive->GetNumberOfItems(&numItems));
574     for (UInt32 i = 0; i < numItems; i++)
575     {
576       if (NConsoleClose::TestBreakSignal())
577         return E_ABORT;
578 
579       UString filePath;
580       HRESULT res = arc.GetItemPath(i, filePath);
581       if (stdInMode && res == E_INVALIDARG)
582         break;
583       RINOK(res);
584 
585       bool isFolder;
586       RINOK(IsArchiveItemFolder(archive, i, isFolder));
587       if (!wildcardCensor.CheckPath(filePath, !isFolder))
588         continue;
589 
590       fieldPrinter.PrintItemInfo(arc, i, techMode);
591 
592       UInt64 packSize, unpackSize;
593       if (!GetUInt64Value(archive, i, kpidSize, unpackSize))
594         unpackSize = 0;
595       else
596         totalUnPackSizePointer = &totalUnPackSize;
597       if (!GetUInt64Value(archive, i, kpidPackSize, packSize))
598         packSize = 0;
599       else
600         totalPackSizePointer = &totalPackSize;
601 
602       g_StdOut << endl;
603 
604       if (isFolder)
605         numDirs++;
606       else
607         numFiles++;
608       totalPackSize += packSize;
609       totalUnPackSize += unpackSize;
610     }
611 
612     if (!stdInMode && totalPackSizePointer == 0)
613     {
614       if (archiveLink.VolumePaths.Size() != 0)
615         arcPackSize += archiveLink.VolumesSize;
616       totalPackSize = (numFiles == 0) ? 0 : arcPackSize;
617       totalPackSizePointer = &totalPackSize;
618     }
619     if (totalUnPackSizePointer == 0 && numFiles == 0)
620     {
621       totalUnPackSize = 0;
622       totalUnPackSizePointer = &totalUnPackSize;
623     }
624     if (enableHeaders && !techMode)
625     {
626       fieldPrinter.PrintTitleLines();
627       g_StdOut << endl;
628       fieldPrinter.PrintSummaryInfo(numFiles, numDirs, totalUnPackSizePointer, totalPackSizePointer);
629       g_StdOut << endl;
630     }
631     if (totalPackSizePointer != 0)
632     {
633       totalPackSizePointer2 = &totalPackSize2;
634       totalPackSize2 += totalPackSize;
635     }
636     if (totalUnPackSizePointer != 0)
637     {
638       totalUnPackSizePointer2 = &totalUnPackSize2;
639       totalUnPackSize2 += totalUnPackSize;
640     }
641     numFiles2 += numFiles;
642     numDirs2 += numDirs;
643   }
644   if (enableHeaders && !techMode && numArcs > 1)
645   {
646     g_StdOut << endl;
647     fieldPrinter.PrintTitleLines();
648     g_StdOut << endl;
649     fieldPrinter.PrintSummaryInfo(numFiles2, numDirs2, totalUnPackSizePointer2, totalPackSizePointer2);
650     g_StdOut << endl;
651     g_StdOut << "Archives: " << numArcs << endl;
652   }
653   return S_OK;
654 }
655