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