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