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