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