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 * const kListing = "Listing archive: ";
    132 
    133 static const char * const kString_Files = "files";
    134 static const char * const kString_Dirs = "folders";
    135 static const char * const kString_AltStreams = "alternate streams";
    136 static const char * const kString_Streams = "streams";
    137 
    138 static const char * const 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     nameA.Empty();
    421     nameA.Add_UInt32(propID);
    422   }
    423 }
    424 
    425 void CFieldPrinter::AddProp(const wchar_t *name, PROPID propID, bool isRawProp)
    426 {
    427   CFieldInfo f;
    428   f.PropID = propID;
    429   f.IsRawProp = isRawProp;
    430   GetPropName(propID, name, f.NameA, f.NameU);
    431   f.NameU += " = ";
    432   if (!f.NameA.IsEmpty())
    433     f.NameA += " = ";
    434   else
    435   {
    436     const UString &s = f.NameU;
    437     AString sA;
    438     unsigned i;
    439     for (i = 0; i < s.Len(); i++)
    440     {
    441       wchar_t c = s[i];
    442       if (c >= 0x80)
    443         break;
    444       sA += (char)c;
    445     }
    446     if (i == s.Len())
    447       f.NameA = sA;
    448   }
    449   _fields.Add(f);
    450 }
    451 
    452 HRESULT CFieldPrinter::AddMainProps(IInArchive *archive)
    453 {
    454   UInt32 numProps;
    455   RINOK(archive->GetNumberOfProperties(&numProps));
    456   for (UInt32 i = 0; i < numProps; i++)
    457   {
    458     CMyComBSTR name;
    459     PROPID propID;
    460     VARTYPE vt;
    461     RINOK(archive->GetPropertyInfo(i, &name, &propID, &vt));
    462     AddProp(name, propID, false);
    463   }
    464   return S_OK;
    465 }
    466 
    467 HRESULT CFieldPrinter::AddRawProps(IArchiveGetRawProps *getRawProps)
    468 {
    469   UInt32 numProps;
    470   RINOK(getRawProps->GetNumRawProps(&numProps));
    471   for (UInt32 i = 0; i < numProps; i++)
    472   {
    473     CMyComBSTR name;
    474     PROPID propID;
    475     RINOK(getRawProps->GetRawPropInfo(i, &name, &propID));
    476     AddProp(name, propID, true);
    477   }
    478   return S_OK;
    479 }
    480 
    481 void CFieldPrinter::PrintTitle()
    482 {
    483   FOR_VECTOR (i, _fields)
    484   {
    485     const CFieldInfo &f = _fields[i];
    486     PrintSpaces(f.PrefixSpacesWidth);
    487     PrintString(f.TitleAdjustment, ((f.PropID == kpidPath) ? 0: f.Width), f.NameA);
    488   }
    489 }
    490 
    491 void CFieldPrinter::PrintTitleLines()
    492 {
    493   g_StdOut << LinesString;
    494 }
    495 
    496 static void PrintTime(char *dest, const FILETIME *ft)
    497 {
    498   *dest = 0;
    499   if (ft->dwLowDateTime == 0 && ft->dwHighDateTime == 0)
    500     return;
    501   ConvertUtcFileTimeToString(*ft, dest, kTimestampPrintLevel_SEC);
    502 }
    503 
    504 #ifndef _SFX
    505 
    506 static inline char GetHex(Byte value)
    507 {
    508   return (char)((value < 10) ? ('0' + value) : ('A' + (value - 10)));
    509 }
    510 
    511 static void HexToString(char *dest, const Byte *data, UInt32 size)
    512 {
    513   for (UInt32 i = 0; i < size; i++)
    514   {
    515     Byte b = data[i];
    516     dest[0] = GetHex((Byte)((b >> 4) & 0xF));
    517     dest[1] = GetHex((Byte)(b & 0xF));
    518     dest += 2;
    519   }
    520   *dest = 0;
    521 }
    522 
    523 #endif
    524 
    525 #define MY_ENDL endl
    526 
    527 HRESULT CFieldPrinter::PrintItemInfo(UInt32 index, const CListStat &st)
    528 {
    529   char temp[128];
    530   size_t tempPos = 0;
    531 
    532   bool techMode = this->TechMode;
    533   /*
    534   if (techMode)
    535   {
    536     g_StdOut << "Index = ";
    537     g_StdOut << (UInt64)index;
    538     g_StdOut << endl;
    539   }
    540   */
    541   FOR_VECTOR (i, _fields)
    542   {
    543     const CFieldInfo &f = _fields[i];
    544 
    545     if (!techMode)
    546     {
    547       PrintSpacesToString(temp + tempPos, f.PrefixSpacesWidth);
    548       tempPos += f.PrefixSpacesWidth;
    549     }
    550 
    551     if (techMode)
    552     {
    553       if (!f.NameA.IsEmpty())
    554         g_StdOut << f.NameA;
    555       else
    556         g_StdOut << f.NameU;
    557     }
    558 
    559     if (f.PropID == kpidPath)
    560     {
    561       if (!techMode)
    562         g_StdOut << temp;
    563       g_StdOut.NormalizePrint_UString(FilePath, TempWString, TempAString);
    564       if (techMode)
    565         g_StdOut << MY_ENDL;
    566       continue;
    567     }
    568 
    569     const unsigned width = f.Width;
    570 
    571     if (f.IsRawProp)
    572     {
    573       #ifndef _SFX
    574 
    575       const void *data;
    576       UInt32 dataSize;
    577       UInt32 propType;
    578       RINOK(Arc->GetRawProps->GetRawProp(index, f.PropID, &data, &dataSize, &propType));
    579 
    580       if (dataSize != 0)
    581       {
    582         bool needPrint = true;
    583 
    584         if (f.PropID == kpidNtSecure)
    585         {
    586           if (propType != NPropDataType::kRaw)
    587             return E_FAIL;
    588           #ifndef _SFX
    589           ConvertNtSecureToString((const Byte *)data, dataSize, TempAString);
    590           g_StdOut << TempAString;
    591           needPrint = false;
    592           #endif
    593         }
    594         else if (f.PropID == kpidNtReparse)
    595         {
    596           UString s;
    597           if (ConvertNtReparseToString((const Byte *)data, dataSize, s))
    598           {
    599             needPrint = false;
    600             g_StdOut.PrintUString(s, TempAString);
    601           }
    602         }
    603 
    604         if (needPrint)
    605         {
    606           if (propType != NPropDataType::kRaw)
    607             return E_FAIL;
    608 
    609           const UInt32 kMaxDataSize = 64;
    610 
    611           if (dataSize > kMaxDataSize)
    612           {
    613             g_StdOut << "data:";
    614             g_StdOut << dataSize;
    615           }
    616           else
    617           {
    618             char hexStr[kMaxDataSize * 2 + 4];
    619             HexToString(hexStr, (const Byte *)data, dataSize);
    620             g_StdOut << hexStr;
    621           }
    622         }
    623       }
    624 
    625       #endif
    626     }
    627     else
    628     {
    629       CPropVariant prop;
    630       switch (f.PropID)
    631       {
    632         case kpidSize: if (st.Size.Def) prop = st.Size.Val; break;
    633         case kpidPackSize: if (st.PackSize.Def) prop = st.PackSize.Val; break;
    634         case kpidMTime: if (st.MTime.Def) prop = st.MTime.Val; break;
    635         default:
    636           RINOK(Arc->Archive->GetProperty(index, f.PropID, &prop));
    637       }
    638       if (f.PropID == kpidAttrib && (prop.vt == VT_EMPTY || prop.vt == VT_UI4))
    639       {
    640         GetAttribString((prop.vt == VT_EMPTY) ? 0 : prop.ulVal, IsDir, techMode, temp + tempPos);
    641         if (techMode)
    642           g_StdOut << temp + tempPos;
    643         else
    644           tempPos += strlen(temp + tempPos);
    645       }
    646       else if (prop.vt == VT_EMPTY)
    647       {
    648         if (!techMode)
    649         {
    650           PrintSpacesToString(temp + tempPos, width);
    651           tempPos += width;
    652         }
    653       }
    654       else if (prop.vt == VT_FILETIME)
    655       {
    656         PrintTime(temp + tempPos, &prop.filetime);
    657         if (techMode)
    658           g_StdOut << temp + tempPos;
    659         else
    660         {
    661           size_t len = strlen(temp + tempPos);
    662           tempPos += len;
    663           if (len < (unsigned)f.Width)
    664           {
    665             len = f.Width - len;
    666             PrintSpacesToString(temp + tempPos, (unsigned)len);
    667             tempPos += len;
    668           }
    669         }
    670       }
    671       else if (prop.vt == VT_BSTR)
    672       {
    673         TempWString.SetFromBstr(prop.bstrVal);
    674         // do we need multi-line support here ?
    675         g_StdOut.Normalize_UString(TempWString);
    676         if (techMode)
    677         {
    678           g_StdOut.PrintUString(TempWString, TempAString);
    679         }
    680         else
    681           PrintUString(f.TextAdjustment, width, TempWString, TempAString);
    682       }
    683       else
    684       {
    685         char s[64];
    686         ConvertPropertyToShortString2(s, prop, f.PropID);
    687         if (techMode)
    688           g_StdOut << s;
    689         else
    690         {
    691           PrintStringToString(temp + tempPos, f.TextAdjustment, width, s);
    692           tempPos += strlen(temp + tempPos);
    693         }
    694       }
    695     }
    696     if (techMode)
    697       g_StdOut << MY_ENDL;
    698   }
    699   g_StdOut << MY_ENDL;
    700   return S_OK;
    701 }
    702 
    703 static void PrintNumber(EAdjustment adj, unsigned width, const CListUInt64Def &value)
    704 {
    705   char s[32];
    706   s[0] = 0;
    707   if (value.Def)
    708     ConvertUInt64ToString(value.Val, s);
    709   PrintString(adj, width, s);
    710 }
    711 
    712 void Print_UInt64_and_String(AString &s, UInt64 val, const char *name);
    713 
    714 void CFieldPrinter::PrintSum(const CListStat &st, UInt64 numDirs, const char *str)
    715 {
    716   FOR_VECTOR (i, _fields)
    717   {
    718     const CFieldInfo &f = _fields[i];
    719     PrintSpaces(f.PrefixSpacesWidth);
    720     if (f.PropID == kpidSize)
    721       PrintNumber(f.TextAdjustment, f.Width, st.Size);
    722     else if (f.PropID == kpidPackSize)
    723       PrintNumber(f.TextAdjustment, f.Width, st.PackSize);
    724     else if (f.PropID == kpidMTime)
    725     {
    726       char s[64];
    727       s[0] = 0;
    728       if (st.MTime.Def)
    729         PrintTime(s, &st.MTime.Val);
    730       PrintString(f.TextAdjustment, f.Width, s);
    731     }
    732     else if (f.PropID == kpidPath)
    733     {
    734       AString s;
    735       Print_UInt64_and_String(s, st.NumFiles, str);
    736       if (numDirs != 0)
    737       {
    738         s += ", ";
    739         Print_UInt64_and_String(s, numDirs, kString_Dirs);
    740       }
    741       PrintString(f.TextAdjustment, 0, s);
    742     }
    743     else
    744       PrintString(f.TextAdjustment, f.Width, "");
    745   }
    746   g_StdOut << endl;
    747 }
    748 
    749 void CFieldPrinter::PrintSum(const CListStat2 &stat2)
    750 {
    751   PrintSum(stat2.MainFiles, stat2.NumDirs, kString_Files);
    752   if (stat2.AltStreams.NumFiles != 0)
    753   {
    754     PrintSum(stat2.AltStreams, 0, kString_AltStreams);;
    755     CListStat st = stat2.MainFiles;
    756     st.Update(stat2.AltStreams);
    757     PrintSum(st, 0, kString_Streams);
    758   }
    759 }
    760 
    761 static HRESULT GetUInt64Value(IInArchive *archive, UInt32 index, PROPID propID, CListUInt64Def &value)
    762 {
    763   value.Val = 0;
    764   value.Def = false;
    765   CPropVariant prop;
    766   RINOK(archive->GetProperty(index, propID, &prop));
    767   value.Def = ConvertPropVariantToUInt64(prop, value.Val);
    768   return S_OK;
    769 }
    770 
    771 static HRESULT GetItemMTime(IInArchive *archive, UInt32 index, CListFileTimeDef &t)
    772 {
    773   t.Val.dwLowDateTime = 0;
    774   t.Val.dwHighDateTime = 0;
    775   t.Def = false;
    776   CPropVariant prop;
    777   RINOK(archive->GetProperty(index, kpidMTime, &prop));
    778   if (prop.vt == VT_FILETIME)
    779   {
    780     t.Val = prop.filetime;
    781     t.Def = true;
    782   }
    783   else if (prop.vt != VT_EMPTY)
    784     return E_FAIL;
    785   return S_OK;
    786 }
    787 
    788 static void PrintPropNameAndNumber(CStdOutStream &so, const char *name, UInt64 val)
    789 {
    790   so << name << ": " << val << endl;
    791 }
    792 
    793 static void PrintPropName_and_Eq(CStdOutStream &so, PROPID propID)
    794 {
    795   const char *s;
    796   char temp[16];
    797   if (propID < ARRAY_SIZE(kPropIdToName))
    798     s = kPropIdToName[propID];
    799   else
    800   {
    801     ConvertUInt32ToString(propID, temp);
    802     s = temp;
    803   }
    804   so << s << " = ";
    805 }
    806 
    807 static void PrintPropNameAndNumber(CStdOutStream &so, PROPID propID, UInt64 val)
    808 {
    809   PrintPropName_and_Eq(so, propID);
    810   so << val << endl;
    811 }
    812 
    813 static void PrintPropNameAndNumber_Signed(CStdOutStream &so, PROPID propID, Int64 val)
    814 {
    815   PrintPropName_and_Eq(so, propID);
    816   so << val << endl;
    817 }
    818 
    819 
    820 static void UString_Replace_CRLF_to_LF(UString &s)
    821 {
    822   // s.Replace(L"\r\n", L"\n");
    823   wchar_t *src = s.GetBuf();
    824   wchar_t *dest = src;
    825   for (;;)
    826   {
    827     wchar_t c = *src++;
    828     if (c == 0)
    829       break;
    830     if (c == '\r' && *src == '\n')
    831     {
    832       src++;
    833       c = '\n';
    834     }
    835     *dest++ = c;
    836   }
    837   s.ReleaseBuf_SetEnd((unsigned)(dest - s.GetBuf()));
    838 }
    839 
    840 
    841 static void PrintPropVal_MultiLine(CStdOutStream &so, const wchar_t *val)
    842 {
    843   UString s = val;
    844   if (s.Find(L'\n') >= 0)
    845   {
    846     so << endl;
    847     so << "{";
    848     so << endl;
    849     UString_Replace_CRLF_to_LF(s);
    850     so.Normalize_UString__LF_Allowed(s);
    851     so << s;
    852     so << endl;
    853     so << "}";
    854   }
    855   else
    856   {
    857     so.Normalize_UString(s);
    858     so << s;
    859   }
    860   so << endl;
    861 }
    862 
    863 
    864 static void PrintPropPair(CStdOutStream &so, const char *name, const wchar_t *val, bool multiLine)
    865 {
    866   so << name << " = ";
    867   if (multiLine)
    868   {
    869     PrintPropVal_MultiLine(so, val);
    870     return;
    871   }
    872   UString s = val;
    873   so.Normalize_UString(s);
    874   so << s;
    875   so << endl;
    876 }
    877 
    878 
    879 static void PrintPropertyPair2(CStdOutStream &so, PROPID propID, const wchar_t *name, const CPropVariant &prop)
    880 {
    881   UString s;
    882   ConvertPropertyToString2(s, prop, propID);
    883   if (!s.IsEmpty())
    884   {
    885     AString nameA;
    886     UString nameU;
    887     GetPropName(propID, name, nameA, nameU);
    888     if (!nameA.IsEmpty())
    889       so << nameA;
    890     else
    891       so << nameU;
    892     so << " = ";
    893     PrintPropVal_MultiLine(so, s);
    894   }
    895 }
    896 
    897 static HRESULT PrintArcProp(CStdOutStream &so, IInArchive *archive, PROPID propID, const wchar_t *name)
    898 {
    899   CPropVariant prop;
    900   RINOK(archive->GetArchiveProperty(propID, &prop));
    901   PrintPropertyPair2(so, propID, name, prop);
    902   return S_OK;
    903 }
    904 
    905 static void PrintArcTypeError(CStdOutStream &so, const UString &type, bool isWarning)
    906 {
    907   so << "Open " << (isWarning ? "WARNING" : "ERROR")
    908     << ": Can not open the file as ["
    909     << type
    910     << "] archive"
    911     << endl;
    912 }
    913 
    914 int Find_FileName_InSortedVector(const UStringVector &fileName, const UString& name);
    915 
    916 void PrintErrorFlags(CStdOutStream &so, const char *s, UInt32 errorFlags);
    917 
    918 static void ErrorInfo_Print(CStdOutStream &so, const CArcErrorInfo &er)
    919 {
    920   PrintErrorFlags(so, "ERRORS:", er.GetErrorFlags());
    921   if (!er.ErrorMessage.IsEmpty())
    922     PrintPropPair(so, "ERROR", er.ErrorMessage, true);
    923 
    924   PrintErrorFlags(so, "WARNINGS:", er.GetWarningFlags());
    925   if (!er.WarningMessage.IsEmpty())
    926     PrintPropPair(so, "WARNING", er.WarningMessage, true);
    927 }
    928 
    929 HRESULT Print_OpenArchive_Props(CStdOutStream &so, const CCodecs *codecs, const CArchiveLink &arcLink)
    930 {
    931   FOR_VECTOR (r, arcLink.Arcs)
    932   {
    933     const CArc &arc = arcLink.Arcs[r];
    934     const CArcErrorInfo &er = arc.ErrorInfo;
    935 
    936     so << "--\n";
    937     PrintPropPair(so, "Path", arc.Path, false);
    938     if (er.ErrorFormatIndex >= 0)
    939     {
    940       if (er.ErrorFormatIndex == arc.FormatIndex)
    941         so << "Warning: The archive is open with offset" << endl;
    942       else
    943         PrintArcTypeError(so, codecs->GetFormatNamePtr(er.ErrorFormatIndex), true);
    944     }
    945     PrintPropPair(so, "Type", codecs->GetFormatNamePtr(arc.FormatIndex), false);
    946 
    947     ErrorInfo_Print(so, er);
    948 
    949     Int64 offset = arc.GetGlobalOffset();
    950     if (offset != 0)
    951       PrintPropNameAndNumber_Signed(so, kpidOffset, offset);
    952     IInArchive *archive = arc.Archive;
    953     RINOK(PrintArcProp(so, archive, kpidPhySize, NULL));
    954     if (er.TailSize != 0)
    955       PrintPropNameAndNumber(so, kpidTailSize, er.TailSize);
    956     {
    957       UInt32 numProps;
    958       RINOK(archive->GetNumberOfArchiveProperties(&numProps));
    959 
    960       for (UInt32 j = 0; j < numProps; j++)
    961       {
    962         CMyComBSTR name;
    963         PROPID propID;
    964         VARTYPE vt;
    965         RINOK(archive->GetArchivePropertyInfo(j, &name, &propID, &vt));
    966         RINOK(PrintArcProp(so, archive, propID, name));
    967       }
    968     }
    969 
    970     if (r != arcLink.Arcs.Size() - 1)
    971     {
    972       UInt32 numProps;
    973       so << "----\n";
    974       if (archive->GetNumberOfProperties(&numProps) == S_OK)
    975       {
    976         UInt32 mainIndex = arcLink.Arcs[r + 1].SubfileIndex;
    977         for (UInt32 j = 0; j < numProps; j++)
    978         {
    979           CMyComBSTR name;
    980           PROPID propID;
    981           VARTYPE vt;
    982           RINOK(archive->GetPropertyInfo(j, &name, &propID, &vt));
    983           CPropVariant prop;
    984           RINOK(archive->GetProperty(mainIndex, propID, &prop));
    985           PrintPropertyPair2(so, propID, name, prop);
    986         }
    987       }
    988     }
    989   }
    990   return S_OK;
    991 }
    992 
    993 HRESULT Print_OpenArchive_Error(CStdOutStream &so, const CCodecs *codecs, const CArchiveLink &arcLink)
    994 {
    995   #ifndef _NO_CRYPTO
    996   if (arcLink.PasswordWasAsked)
    997     so << "Can not open encrypted archive. Wrong password?";
    998   else
    999   #endif
   1000   {
   1001     if (arcLink.NonOpen_ErrorInfo.ErrorFormatIndex >= 0)
   1002     {
   1003       so.NormalizePrint_UString(arcLink.NonOpen_ArcPath);
   1004       so << endl;
   1005       PrintArcTypeError(so, codecs->Formats[arcLink.NonOpen_ErrorInfo.ErrorFormatIndex].Name, false);
   1006     }
   1007     else
   1008       so << "Can not open the file as archive";
   1009   }
   1010 
   1011   so << endl;
   1012   so << endl;
   1013   ErrorInfo_Print(so, arcLink.NonOpen_ErrorInfo);
   1014 
   1015   return S_OK;
   1016 }
   1017 
   1018 bool CensorNode_CheckPath(const NWildcard::CCensorNode &node, const CReadArcItem &item);
   1019 
   1020 HRESULT ListArchives(CCodecs *codecs,
   1021     const CObjectVector<COpenType> &types,
   1022     const CIntVector &excludedFormats,
   1023     bool stdInMode,
   1024     UStringVector &arcPaths, UStringVector &arcPathsFull,
   1025     bool processAltStreams, bool showAltStreams,
   1026     const NWildcard::CCensorNode &wildcardCensor,
   1027     bool enableHeaders, bool techMode,
   1028     #ifndef _NO_CRYPTO
   1029     bool &passwordEnabled, UString &password,
   1030     #endif
   1031     #ifndef _SFX
   1032     const CObjectVector<CProperty> *props,
   1033     #endif
   1034     UInt64 &numErrors,
   1035     UInt64 &numWarnings)
   1036 {
   1037   bool allFilesAreAllowed = wildcardCensor.AreAllAllowed();
   1038 
   1039   numErrors = 0;
   1040   numWarnings = 0;
   1041 
   1042   CFieldPrinter fp;
   1043   if (!techMode)
   1044     fp.Init(kStandardFieldTable, ARRAY_SIZE(kStandardFieldTable));
   1045 
   1046   CListStat2 stat2total;
   1047 
   1048   CBoolArr skipArcs(arcPaths.Size());
   1049   unsigned arcIndex;
   1050   for (arcIndex = 0; arcIndex < arcPaths.Size(); arcIndex++)
   1051     skipArcs[arcIndex] = false;
   1052   UInt64 numVolumes = 0;
   1053   UInt64 numArcs = 0;
   1054   UInt64 totalArcSizes = 0;
   1055 
   1056   HRESULT lastError = 0;
   1057 
   1058   for (arcIndex = 0; arcIndex < arcPaths.Size(); arcIndex++)
   1059   {
   1060     if (skipArcs[arcIndex])
   1061       continue;
   1062     const UString &arcPath = arcPaths[arcIndex];
   1063     UInt64 arcPackSize = 0;
   1064 
   1065     if (!stdInMode)
   1066     {
   1067       NFile::NFind::CFileInfo fi;
   1068       if (!fi.Find(us2fs(arcPath)))
   1069       {
   1070         DWORD errorCode = GetLastError();
   1071         if (errorCode == 0)
   1072           errorCode = ERROR_FILE_NOT_FOUND;
   1073         lastError = HRESULT_FROM_WIN32(lastError);;
   1074         g_StdOut.Flush();
   1075         if (g_ErrStream)
   1076         {
   1077           *g_ErrStream << endl << kError << NError::MyFormatMessage(errorCode) << endl;
   1078           g_ErrStream->NormalizePrint_UString(arcPath);
   1079           *g_ErrStream << endl << endl;
   1080         }
   1081         numErrors++;
   1082         continue;
   1083       }
   1084       if (fi.IsDir())
   1085       {
   1086         g_StdOut.Flush();
   1087         if (g_ErrStream)
   1088         {
   1089           *g_ErrStream << endl << kError;
   1090           g_ErrStream->NormalizePrint_UString(arcPath);
   1091           *g_ErrStream << " is not a file" << endl << endl;
   1092         }
   1093         numErrors++;
   1094         continue;
   1095       }
   1096       arcPackSize = fi.Size;
   1097       totalArcSizes += arcPackSize;
   1098     }
   1099 
   1100     CArchiveLink arcLink;
   1101 
   1102     COpenCallbackConsole openCallback;
   1103     openCallback.Init(&g_StdOut, g_ErrStream, NULL);
   1104 
   1105     #ifndef _NO_CRYPTO
   1106 
   1107     openCallback.PasswordIsDefined = passwordEnabled;
   1108     openCallback.Password = password;
   1109 
   1110     #endif
   1111 
   1112     /*
   1113     CObjectVector<COptionalOpenProperties> optPropsVector;
   1114     COptionalOpenProperties &optProps = optPropsVector.AddNew();
   1115     optProps.Props = *props;
   1116     */
   1117 
   1118     COpenOptions options;
   1119     #ifndef _SFX
   1120     options.props = props;
   1121     #endif
   1122     options.codecs = codecs;
   1123     options.types = &types;
   1124     options.excludedFormats = &excludedFormats;
   1125     options.stdInMode = stdInMode;
   1126     options.stream = NULL;
   1127     options.filePath = arcPath;
   1128 
   1129     if (enableHeaders)
   1130     {
   1131       g_StdOut << endl << kListing;
   1132       g_StdOut.NormalizePrint_UString(arcPath);
   1133       g_StdOut << endl << endl;
   1134     }
   1135 
   1136     HRESULT result = arcLink.Open_Strict(options, &openCallback);
   1137 
   1138     if (result != S_OK)
   1139     {
   1140       if (result == E_ABORT)
   1141         return result;
   1142       if (result != S_FALSE)
   1143         lastError = result;
   1144       g_StdOut.Flush();
   1145       if (g_ErrStream)
   1146       {
   1147         *g_ErrStream << endl << kError;
   1148         g_ErrStream->NormalizePrint_UString(arcPath);
   1149         *g_ErrStream << " : ";
   1150         if (result == S_FALSE)
   1151         {
   1152           Print_OpenArchive_Error(*g_ErrStream, codecs, arcLink);
   1153         }
   1154         else
   1155         {
   1156           *g_ErrStream << "opening : ";
   1157           if (result == E_OUTOFMEMORY)
   1158             *g_ErrStream << "Can't allocate required memory";
   1159           else
   1160             *g_ErrStream << NError::MyFormatMessage(result);
   1161         }
   1162         *g_ErrStream << endl;
   1163       }
   1164       numErrors++;
   1165       continue;
   1166     }
   1167 
   1168     {
   1169       FOR_VECTOR (r, arcLink.Arcs)
   1170       {
   1171         const CArcErrorInfo &arc = arcLink.Arcs[r].ErrorInfo;
   1172         if (!arc.WarningMessage.IsEmpty())
   1173           numWarnings++;
   1174         if (arc.AreThereWarnings())
   1175           numWarnings++;
   1176         if (arc.ErrorFormatIndex >= 0)
   1177           numWarnings++;
   1178         if (arc.AreThereErrors())
   1179         {
   1180           numErrors++;
   1181           // break;
   1182         }
   1183         if (!arc.ErrorMessage.IsEmpty())
   1184           numErrors++;
   1185       }
   1186     }
   1187 
   1188     numArcs++;
   1189     numVolumes++;
   1190 
   1191     if (!stdInMode)
   1192     {
   1193       numVolumes += arcLink.VolumePaths.Size();
   1194       totalArcSizes += arcLink.VolumesSize;
   1195       FOR_VECTOR (v, arcLink.VolumePaths)
   1196       {
   1197         int index = Find_FileName_InSortedVector(arcPathsFull, arcLink.VolumePaths[v]);
   1198         if (index >= 0 && (unsigned)index > arcIndex)
   1199           skipArcs[(unsigned)index] = true;
   1200       }
   1201     }
   1202 
   1203 
   1204     if (enableHeaders)
   1205     {
   1206       RINOK(Print_OpenArchive_Props(g_StdOut, codecs, arcLink));
   1207 
   1208       g_StdOut << endl;
   1209       if (techMode)
   1210         g_StdOut << "----------\n";
   1211     }
   1212 
   1213     if (enableHeaders && !techMode)
   1214     {
   1215       fp.PrintTitle();
   1216       g_StdOut << endl;
   1217       fp.PrintTitleLines();
   1218       g_StdOut << endl;
   1219     }
   1220 
   1221     const CArc &arc = arcLink.Arcs.Back();
   1222     fp.Arc = &arc;
   1223     fp.TechMode = techMode;
   1224     IInArchive *archive = arc.Archive;
   1225     if (techMode)
   1226     {
   1227       fp.Clear();
   1228       RINOK(fp.AddMainProps(archive));
   1229       if (arc.GetRawProps)
   1230       {
   1231         RINOK(fp.AddRawProps(arc.GetRawProps));
   1232       }
   1233     }
   1234 
   1235     CListStat2 stat2;
   1236 
   1237     UInt32 numItems;
   1238     RINOK(archive->GetNumberOfItems(&numItems));
   1239 
   1240     CReadArcItem item;
   1241     UStringVector pathParts;
   1242 
   1243     for (UInt32 i = 0; i < numItems; i++)
   1244     {
   1245       if (NConsoleClose::TestBreakSignal())
   1246         return E_ABORT;
   1247 
   1248       HRESULT res = arc.GetItemPath2(i, fp.FilePath);
   1249 
   1250       if (stdInMode && res == E_INVALIDARG)
   1251         break;
   1252       RINOK(res);
   1253 
   1254       if (arc.Ask_Aux)
   1255       {
   1256         bool isAux;
   1257         RINOK(Archive_IsItem_Aux(archive, i, isAux));
   1258         if (isAux)
   1259           continue;
   1260       }
   1261 
   1262       bool isAltStream = false;
   1263       if (arc.Ask_AltStream)
   1264       {
   1265         RINOK(Archive_IsItem_AltStream(archive, i, isAltStream));
   1266         if (isAltStream && !processAltStreams)
   1267           continue;
   1268       }
   1269 
   1270       RINOK(Archive_IsItem_Dir(archive, i, fp.IsDir));
   1271 
   1272       if (!allFilesAreAllowed)
   1273       {
   1274         if (isAltStream)
   1275         {
   1276           RINOK(arc.GetItem(i, item));
   1277           if (!CensorNode_CheckPath(wildcardCensor, item))
   1278             continue;
   1279         }
   1280         else
   1281         {
   1282           SplitPathToParts(fp.FilePath, pathParts);;
   1283           bool include;
   1284           if (!wildcardCensor.CheckPathVect(pathParts, !fp.IsDir, include))
   1285             continue;
   1286           if (!include)
   1287             continue;
   1288         }
   1289       }
   1290 
   1291       CListStat st;
   1292 
   1293       RINOK(GetUInt64Value(archive, i, kpidSize, st.Size));
   1294       RINOK(GetUInt64Value(archive, i, kpidPackSize, st.PackSize));
   1295       RINOK(GetItemMTime(archive, i, st.MTime));
   1296 
   1297       if (fp.IsDir)
   1298         stat2.NumDirs++;
   1299       else
   1300         st.NumFiles = 1;
   1301       stat2.GetStat(isAltStream).Update(st);
   1302 
   1303       if (isAltStream && !showAltStreams)
   1304         continue;
   1305       RINOK(fp.PrintItemInfo(i, st));
   1306     }
   1307 
   1308     UInt64 numStreams = stat2.GetNumStreams();
   1309     if (!stdInMode
   1310         && !stat2.MainFiles.PackSize.Def
   1311         && !stat2.AltStreams.PackSize.Def)
   1312     {
   1313       if (arcLink.VolumePaths.Size() != 0)
   1314         arcPackSize += arcLink.VolumesSize;
   1315       stat2.MainFiles.PackSize.Add((numStreams == 0) ? 0 : arcPackSize);
   1316     }
   1317 
   1318     stat2.MainFiles.SetSizeDefIfNoFiles();
   1319     stat2.AltStreams.SetSizeDefIfNoFiles();
   1320 
   1321     if (enableHeaders && !techMode)
   1322     {
   1323       fp.PrintTitleLines();
   1324       g_StdOut << endl;
   1325       fp.PrintSum(stat2);
   1326     }
   1327 
   1328     if (enableHeaders)
   1329     {
   1330       if (arcLink.NonOpen_ErrorInfo.ErrorFormatIndex >= 0)
   1331       {
   1332         g_StdOut << "----------\n";
   1333         PrintPropPair(g_StdOut, "Path", arcLink.NonOpen_ArcPath, false);
   1334         PrintArcTypeError(g_StdOut, codecs->Formats[arcLink.NonOpen_ErrorInfo.ErrorFormatIndex].Name, false);
   1335       }
   1336     }
   1337 
   1338     stat2total.Update(stat2);
   1339 
   1340     g_StdOut.Flush();
   1341   }
   1342 
   1343   if (enableHeaders && !techMode && (arcPaths.Size() > 1 || numVolumes > 1))
   1344   {
   1345     g_StdOut << endl;
   1346     fp.PrintTitleLines();
   1347     g_StdOut << endl;
   1348     fp.PrintSum(stat2total);
   1349     g_StdOut << endl;
   1350     PrintPropNameAndNumber(g_StdOut, "Archives", numArcs);
   1351     PrintPropNameAndNumber(g_StdOut, "Volumes", numVolumes);
   1352     PrintPropNameAndNumber(g_StdOut, "Total archives size", totalArcSizes);
   1353   }
   1354 
   1355   if (numErrors == 1 && lastError != 0)
   1356     return lastError;
   1357 
   1358   return S_OK;
   1359 }
   1360