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