Home | History | Annotate | Download | only in Console
      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 
     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 
    145 static void PrintSpaces(int numSpaces)
    146 {
    147   for (int i = 0; i < numSpaces; i++)
    148     g_StdOut << ' ';
    149 }
    150 
    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:
    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 
    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 
    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 
    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 
    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 
    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 
    260 static BOOL IsFileTimeZero(CONST FILETIME *lpFileTime)
    261 {
    262   return (lpFileTime->dwLowDateTime == 0) && (lpFileTime->dwHighDateTime == 0);
    263 }
    264 
    265 static const char *kEmptyTimeString = "                   ";
    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 
    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 
    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 
    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 
    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 
    411 static void PrintPropPair(const wchar_t *name, const wchar_t *value)
    412 {
    413   g_StdOut << name << " = " << value << endl;
    414 }
    415 
    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::CFileInfoW fi;
    441       if (!fi.Find(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::MyFormatMessage(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