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
23using namespace NWindows;
24using namespace NCOM;
25
26extern CStdOutStream *g_StdStream;
27extern CStdOutStream *g_ErrStream;
28
29static 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
129static const char kEmptyAttribChar = '.';
130
131static const char *kListing = "Listing archive: ";
132
133static const char *kString_Files = "files";
134static const char *kString_Dirs = "folders";
135static const char *kString_AltStreams = "alternate streams";
136static const char *kString_Streams = "streams";
137
138static const char *kError = "ERROR: ";
139
140static 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
157enum EAdjustment
158{
159  kLeft,
160  kCenter,
161  kRight
162};
163
164struct 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
176struct CFieldInfoInit
177{
178  PROPID PropID;
179  const char *Name;
180  EAdjustment TitleAdjustment;
181  EAdjustment TextAdjustment;
182  unsigned PrefixSpacesWidth;
183  unsigned Width;
184};
185
186static 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
195const unsigned kNumSpacesMax = 32; // it must be larger than max CFieldInfoInit.Width
196static const char *g_Spaces =
197"                                " ;
198
199static void PrintSpaces(unsigned numSpaces)
200{
201  if (numSpaces > 0 && numSpaces <= kNumSpacesMax)
202    g_StdOut << g_Spaces + (kNumSpacesMax - numSpaces);
203}
204
205static 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
215static 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
248static 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
271static 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
296struct 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
306struct 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
322struct 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
340struct 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
358class CFieldPrinter
359{
360  CObjectVector<CFieldInfo> _fields;
361
362  void AddProp(const wchar_t *name, PROPID propID, bool isRawProp);
363public:
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
386void 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
409static 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
426void 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
453HRESULT 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
468HRESULT 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
482void 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
492void CFieldPrinter::PrintTitleLines()
493{
494  g_StdOut << LinesString;
495}
496
497static 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
510static inline char GetHex(Byte value)
511{
512  return (char)((value < 10) ? ('0' + value) : ('A' + (value - 10)));
513}
514
515static 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
531HRESULT 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
706static 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
715void Print_UInt64_and_String(AString &s, UInt64 val, const char *name);
716
717void 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
752void 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
764static 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
774static 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
791static void PrintPropNameAndNumber(CStdOutStream &so, const char *name, UInt64 val)
792{
793  so << name << ": " << val << endl;
794}
795
796static 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
810static void PrintPropNameAndNumber(CStdOutStream &so, PROPID propID, UInt64 val)
811{
812  PrintPropName_and_Eq(so, propID);
813  so << val << endl;
814}
815
816static void PrintPropNameAndNumber_Signed(CStdOutStream &so, PROPID propID, Int64 val)
817{
818  PrintPropName_and_Eq(so, propID);
819  so << val << endl;
820}
821
822static void PrintPropPair(CStdOutStream &so, const char *name, const wchar_t *val)
823{
824  so << name << " = " << val << endl;
825}
826
827
828static 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
844static 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
852static 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
861int Find_FileName_InSortedVector(const UStringVector &fileName, const UString& name);
862
863void PrintErrorFlags(CStdOutStream &so, const char *s, UInt32 errorFlags);
864
865static 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
876HRESULT 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
940HRESULT 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
964bool CensorNode_CheckPath(const NWildcard::CCensorNode &node, const CReadArcItem &item);
965
966HRESULT 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