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