Upload 2.0.2
[physicsfs] / lzma / CPP / 7zip / UI / Console / List.cpp
1 // List.cpp
2
3 #include "StdAfx.h"
4
5 #include "List.h"
6 #include "ConsoleClose.h"
7
8 #include "Common/StringConvert.h"
9 #include "Common/StdOutStream.h"
10 #include "Common/IntToString.h"
11 #include "Common/MyCom.h"
12
13 #include "Windows/PropVariant.h"
14 #include "Windows/Defs.h"
15 #include "Windows/PropVariantConversions.h"
16 #include "Windows/FileDir.h"
17
18 #include "../../Archive/IArchive.h"
19
20 #include "../Common/PropIDUtils.h"
21 #include "../Common/OpenArchive.h"
22
23 #include "OpenCallbackConsole.h"
24
25 using namespace NWindows;
26
27 struct CPropIdToName
28 {
29   PROPID PropID;
30   const wchar_t *Name;
31 };
32
33 static CPropIdToName kPropIdToName[] =  
34 {
35   { kpidPath, L"Path" },
36   { kpidName, L"Name" },
37   { kpidIsFolder, L"Folder" }, 
38   { kpidSize, L"Size" },
39   { kpidPackedSize, L"Packed Size" },
40   { kpidAttributes, L"Attributes" },
41   { kpidCreationTime, L"Created" },
42   { kpidLastAccessTime, L"Accessed" },
43   { kpidLastWriteTime, L"Modified" },
44   { kpidSolid, L"Solid" },
45   { kpidCommented, L"Commented" },
46   { kpidEncrypted, L"Encrypted" },
47   { kpidSplitBefore, L"Split Before" },
48   { kpidSplitAfter, L"Split After" },
49   { kpidDictionarySize, L"Dictionary Size" },
50   { kpidCRC, L"CRC" },
51   { kpidType, L"Type" },
52   { kpidIsAnti, L"Anti" },
53   { kpidMethod, L"Method" },
54   { kpidHostOS, L"Host OS" },
55   { kpidFileSystem, L"File System" },
56   { kpidUser, L"User" },
57   { kpidGroup, L"Group" },
58   { kpidBlock, L"Block" },
59   { kpidComment, L"Comment" },
60   { kpidPosition, L"Position" },
61   { kpidPrefix, L"Prefix" },
62   { kpidNumSubFolders, L"Folders" },
63   { kpidNumSubFiles, L"Files" },
64   { kpidUnpackVer, L"Version" },
65   { kpidVolume, L"Volume" },
66   { kpidIsVolume, L"Multivolume" },
67   { kpidOffset, L"Offset" },
68   { kpidLinks, L"Links" },
69   { kpidNumBlocks, L"Blocks" },
70   { kpidNumVolumes, L"Volumes" }
71 };
72
73 static const char kEmptyAttributeChar = '.';
74 static const char kDirectoryAttributeChar = 'D';
75 static const char kReadonlyAttributeChar  = 'R';
76 static const char kHiddenAttributeChar    = 'H';
77 static const char kSystemAttributeChar    = 'S';
78 static const char kArchiveAttributeChar   = 'A';
79
80 static const char *kListing = "Listing archive: ";
81 static const wchar_t *kFilesMessage = L"files";
82 static const wchar_t *kDirsMessage = L"folders";
83
84 static void GetAttributesString(DWORD wa, bool directory, char *s)
85 {
86   s[0] = ((wa & FILE_ATTRIBUTE_DIRECTORY) != 0 || directory) ? 
87       kDirectoryAttributeChar: kEmptyAttributeChar;
88   s[1] = ((wa & FILE_ATTRIBUTE_READONLY) != 0)? 
89       kReadonlyAttributeChar: kEmptyAttributeChar;
90   s[2] = ((wa & FILE_ATTRIBUTE_HIDDEN) != 0) ? 
91       kHiddenAttributeChar: kEmptyAttributeChar;
92   s[3] = ((wa & FILE_ATTRIBUTE_SYSTEM) != 0) ? 
93       kSystemAttributeChar: kEmptyAttributeChar;
94   s[4] = ((wa & FILE_ATTRIBUTE_ARCHIVE) != 0) ? 
95       kArchiveAttributeChar: kEmptyAttributeChar;
96   s[5] = '\0';
97 }
98
99 enum EAdjustment
100 {
101   kLeft,
102   kCenter,
103   kRight
104 };
105
106 struct CFieldInfo
107 {
108   PROPID PropID;
109   UString Name;
110   EAdjustment TitleAdjustment;
111   EAdjustment TextAdjustment;
112   int PrefixSpacesWidth;
113   int Width;
114 };
115
116 struct CFieldInfoInit
117 {
118   PROPID PropID;
119   const wchar_t *Name;
120   EAdjustment TitleAdjustment;
121   EAdjustment TextAdjustment;
122   int PrefixSpacesWidth;
123   int Width;
124 };
125
126 CFieldInfoInit kStandardFieldTable[] = 
127 {
128   { kpidLastWriteTime, L"   Date      Time", kLeft, kLeft, 0, 19 },
129   { kpidAttributes, L"Attr", kRight, kCenter, 1, 5 },
130   { kpidSize, L"Size", kRight, kRight, 1, 12 },
131   { kpidPackedSize, L"Compressed", kRight, kRight, 1, 12 },
132   { kpidPath, L"Name", kLeft, kLeft, 2, 24 }
133 };
134
135 void PrintSpaces(int numSpaces)
136 {
137   for (int i = 0; i < numSpaces; i++)
138     g_StdOut << ' ';
139 }
140
141 void PrintString(EAdjustment adjustment, int width, const UString &textString)
142 {
143   const int numSpaces = width - textString.Length();
144   int numLeftSpaces = 0;
145   switch (adjustment)
146   {
147     case kLeft:
148       numLeftSpaces = 0;
149       break;
150     case kCenter:
151       numLeftSpaces = numSpaces / 2;
152       break;
153     case kRight:
154       numLeftSpaces = numSpaces;
155       break;
156   }
157   PrintSpaces(numLeftSpaces);
158   g_StdOut << textString;
159   PrintSpaces(numSpaces - numLeftSpaces);
160 }
161
162 class CFieldPrinter
163 {
164   CObjectVector<CFieldInfo> _fields;
165 public:
166   void Clear() { _fields.Clear(); }
167   void Init(const CFieldInfoInit *standardFieldTable, int numItems);
168   HRESULT Init(IInArchive *archive);
169   void PrintTitle();
170   void PrintTitleLines();
171   HRESULT PrintItemInfo(IInArchive *archive, 
172       const UString &defaultItemName,
173       const NWindows::NFile::NFind::CFileInfoW &archiveFileInfo,
174       UInt32 index,
175       bool techMode);
176   HRESULT PrintSummaryInfo(UInt64 numFiles, UInt64 numDirs, 
177       const UInt64 *size, const UInt64 *compressedSize);
178 };
179
180 void CFieldPrinter::Init(const CFieldInfoInit *standardFieldTable, int numItems)
181 {
182   Clear();
183   for (int i = 0; i < numItems; i++)
184   {
185     CFieldInfo fieldInfo;
186     const CFieldInfoInit &fieldInfoInit = standardFieldTable[i];
187     fieldInfo.PropID = fieldInfoInit.PropID;
188     fieldInfo.Name = fieldInfoInit.Name;
189     fieldInfo.TitleAdjustment = fieldInfoInit.TitleAdjustment;
190     fieldInfo.TextAdjustment = fieldInfoInit.TextAdjustment;
191     fieldInfo.PrefixSpacesWidth = fieldInfoInit.PrefixSpacesWidth;
192     fieldInfo.Width = fieldInfoInit.Width;
193     _fields.Add(fieldInfo);
194   }
195 }
196
197 static UString GetPropName(PROPID propID, BSTR name)
198 {
199   for (int i = 0; i < sizeof(kPropIdToName) / sizeof(kPropIdToName[0]); i++)
200   {
201     const CPropIdToName &propIdToName = kPropIdToName[i];
202     if (propIdToName.PropID == propID)
203       return propIdToName.Name;
204   }
205   if (name)
206     return name;
207   return L"?";
208 }
209
210 HRESULT CFieldPrinter::Init(IInArchive *archive)
211 {
212   Clear();
213   UInt32 numProps;
214   RINOK(archive->GetNumberOfProperties(&numProps));
215   for (UInt32 i = 0; i < numProps; i++)
216   {
217     CMyComBSTR name;
218     PROPID propID;
219     VARTYPE vt;
220     RINOK(archive->GetPropertyInfo(i, &name, &propID, &vt));
221     CFieldInfo fieldInfo;
222     fieldInfo.PropID = propID;
223     fieldInfo.Name = GetPropName(propID, name);
224     _fields.Add(fieldInfo);
225   }
226   return S_OK;
227 }
228
229 void CFieldPrinter::PrintTitle()
230 {
231   for (int i = 0; i < _fields.Size(); i++)
232   {
233     const CFieldInfo &fieldInfo = _fields[i];
234     PrintSpaces(fieldInfo.PrefixSpacesWidth);
235     PrintString(fieldInfo.TitleAdjustment, 
236       ((fieldInfo.PropID == kpidPath) ? 0: fieldInfo.Width), fieldInfo.Name);
237   }
238 }
239
240 void CFieldPrinter::PrintTitleLines()
241 {
242   for (int i = 0; i < _fields.Size(); i++)
243   {
244     const CFieldInfo &fieldInfo = _fields[i];
245     PrintSpaces(fieldInfo.PrefixSpacesWidth);
246     for (int i = 0; i < fieldInfo.Width; i++)
247       g_StdOut << '-';
248   }
249 }
250
251
252 BOOL IsFileTimeZero(CONST FILETIME *lpFileTime)
253 {
254   return (lpFileTime->dwLowDateTime == 0) && (lpFileTime->dwHighDateTime == 0);
255 }
256
257 static const char *kEmptyTimeString = "                   ";
258 void PrintTime(const NCOM::CPropVariant &propVariant)
259 {
260   if (propVariant.vt != VT_FILETIME)
261     throw "incorrect item";
262   if (IsFileTimeZero(&propVariant.filetime))
263     g_StdOut << kEmptyTimeString;
264   else
265   {
266     FILETIME localFileTime;
267     if (!FileTimeToLocalFileTime(&propVariant.filetime, &localFileTime))
268       throw "FileTimeToLocalFileTime error";
269     char s[32];
270     if (ConvertFileTimeToString(localFileTime, s, true, true))
271       g_StdOut << s;
272     else
273       g_StdOut << kEmptyTimeString;
274   }
275 }
276
277 HRESULT CFieldPrinter::PrintItemInfo(IInArchive *archive, 
278     const UString &defaultItemName, 
279     const NWindows::NFile::NFind::CFileInfoW &archiveFileInfo,
280     UInt32 index,
281     bool techMode)
282 {
283   /*
284   if (techMode)
285   {
286     g_StdOut << "Index = ";
287     g_StdOut << (UInt64)index;
288     g_StdOut << endl;
289   }
290   */
291   for (int i = 0; i < _fields.Size(); i++)
292   {
293     const CFieldInfo &fieldInfo = _fields[i];
294     if (!techMode)
295       PrintSpaces(fieldInfo.PrefixSpacesWidth);
296
297     NCOM::CPropVariant propVariant;
298     RINOK(archive->GetProperty(index, fieldInfo.PropID, &propVariant));
299     if (techMode)
300     {
301       g_StdOut << fieldInfo.Name << " = ";
302     }
303     int width = (fieldInfo.PropID == kpidPath) ? 0: fieldInfo.Width;
304     if (propVariant.vt == VT_EMPTY)
305     {
306       switch(fieldInfo.PropID)
307       {
308         case kpidPath:
309           propVariant = defaultItemName;
310           break;
311         case kpidLastWriteTime:
312           propVariant = archiveFileInfo.LastWriteTime;
313           break;
314         default:
315           if (techMode)
316             g_StdOut << endl;
317           else
318             PrintSpaces(width);
319           continue;
320       }
321     }
322     if (fieldInfo.PropID == kpidLastWriteTime)
323     {
324       PrintTime(propVariant);
325     }
326     else if (fieldInfo.PropID == kpidAttributes)
327     {
328       if (propVariant.vt != VT_UI4)
329         throw "incorrect item";
330       UInt32 attributes = propVariant.ulVal;
331       bool isFolder;
332       RINOK(IsArchiveItemFolder(archive, index, isFolder));
333       char s[8];
334       GetAttributesString(attributes, isFolder, s);
335       g_StdOut << s;
336     }
337     else if (propVariant.vt == VT_BSTR)
338     {
339       if (techMode)
340         g_StdOut << propVariant.bstrVal;
341       else
342         PrintString(fieldInfo.TextAdjustment, width, propVariant.bstrVal);
343     }
344     else
345     {
346       UString s = ConvertPropertyToString(propVariant, fieldInfo.PropID);
347       s.Replace(wchar_t(0xA), L' '); 
348       s.Replace(wchar_t(0xD), L' '); 
349
350       if (techMode)
351         g_StdOut << s;
352       else
353         PrintString(fieldInfo.TextAdjustment, width, s);
354     }
355     if (techMode)
356       g_StdOut << endl;
357   }
358   return S_OK;
359 }
360
361 void PrintNumberString(EAdjustment adjustment, int width, const UInt64 *value)
362 {
363   wchar_t textString[32] = { 0 };
364   if (value != NULL)
365     ConvertUInt64ToString(*value, textString);
366   PrintString(adjustment, width, textString);
367 }
368
369
370 HRESULT CFieldPrinter::PrintSummaryInfo(UInt64 numFiles, UInt64 numDirs, 
371     const UInt64 *size, const UInt64 *compressedSize)
372 {
373   for (int i = 0; i < _fields.Size(); i++)
374   {
375     const CFieldInfo &fieldInfo = _fields[i];
376     PrintSpaces(fieldInfo.PrefixSpacesWidth);
377     NCOM::CPropVariant propVariant;
378     if (fieldInfo.PropID == kpidSize)
379       PrintNumberString(fieldInfo.TextAdjustment, fieldInfo.Width, size);
380     else if (fieldInfo.PropID == kpidPackedSize)
381       PrintNumberString(fieldInfo.TextAdjustment, fieldInfo.Width, compressedSize);
382     else if (fieldInfo.PropID == kpidPath)
383     {
384       wchar_t textString[32];
385       ConvertUInt64ToString(numFiles, textString);
386       UString temp = textString;
387       temp += L" ";
388       temp += kFilesMessage;
389       temp += L", ";
390       ConvertUInt64ToString(numDirs, textString);
391       temp += textString;
392       temp += L" ";
393       temp += kDirsMessage;
394       PrintString(fieldInfo.TextAdjustment, 0, temp);
395     }
396     else 
397       PrintString(fieldInfo.TextAdjustment, fieldInfo.Width, L"");
398   }
399   return S_OK;
400 }
401
402 bool GetUInt64Value(IInArchive *archive, UInt32 index, PROPID propID, UInt64 &value)
403 {
404   NCOM::CPropVariant propVariant;
405   if (archive->GetProperty(index, propID, &propVariant) != S_OK)
406     throw "GetPropertyValue error";
407   if (propVariant.vt == VT_EMPTY)
408     return false;
409   value = ConvertPropVariantToUInt64(propVariant);
410   return true;
411 }
412
413 HRESULT ListArchives(
414     CCodecs *codecs,
415     UStringVector &archivePaths, UStringVector &archivePathsFull,
416     const NWildcard::CCensorNode &wildcardCensor,
417     bool enableHeaders, bool techMode, bool &passwordEnabled, UString &password, UInt64 &numErrors)
418 {
419   numErrors = 0;
420   CFieldPrinter fieldPrinter;
421   if (!techMode)
422     fieldPrinter.Init(kStandardFieldTable, sizeof(kStandardFieldTable) / sizeof(kStandardFieldTable[0]));
423
424   UInt64 numFiles2 = 0, numDirs2 = 0, totalPackSize2 = 0, totalUnPackSize2 = 0;
425   UInt64 *totalPackSizePointer2 = 0, *totalUnPackSizePointer2 = 0;
426   for (int i = 0; i < archivePaths.Size(); i++)
427   {
428     const UString &archiveName = archivePaths[i];
429     NFile::NFind::CFileInfoW archiveFileInfo;
430     if (!NFile::NFind::FindFile(archiveName, archiveFileInfo) || archiveFileInfo.IsDirectory())
431     {
432       g_StdOut << endl << "Error: " << archiveName << " is not archive" << endl;
433       numErrors++;
434       continue;
435     }
436     if (archiveFileInfo.IsDirectory())
437     {
438       g_StdOut << endl << "Error: " << archiveName << " is not file" << endl;
439       numErrors++;
440       continue;
441     }
442
443     CArchiveLink archiveLink;
444
445     COpenCallbackConsole openCallback;
446     openCallback.OutStream = &g_StdOut;
447     openCallback.PasswordIsDefined = passwordEnabled;
448     openCallback.Password = password;
449
450     HRESULT result = MyOpenArchive(codecs, archiveName, archiveLink, &openCallback);
451     if (result != S_OK)
452     {
453       g_StdOut << endl << "Error: " << archiveName << " is not supported archive" << endl;
454       numErrors++;
455       continue;
456     }
457
458     for (int v = 0; v < archiveLink.VolumePaths.Size(); v++)
459     {
460       int index = archivePathsFull.FindInSorted(archiveLink.VolumePaths[v]);
461       if (index >= 0 && index > i)
462       {
463         archivePaths.Delete(index);
464         archivePathsFull.Delete(index);
465       }
466     }
467
468     IInArchive *archive = archiveLink.GetArchive();
469     const UString defaultItemName = archiveLink.GetDefaultItemName();
470
471     if (enableHeaders)
472     {
473       g_StdOut << endl << kListing << archiveName << endl << endl;
474
475       UInt32 numProps;
476       if (archive->GetNumberOfArchiveProperties(&numProps) == S_OK)
477       {
478         for (UInt32 i = 0; i < numProps; i++)
479         {
480           CMyComBSTR name;
481           PROPID propID;
482           VARTYPE vt;
483           if (archive->GetArchivePropertyInfo(i, &name, &propID, &vt) != S_OK)
484             continue;
485           NCOM::CPropVariant prop;
486           if (archive->GetArchiveProperty(propID, &prop) != S_OK)
487             continue;
488           UString s = ConvertPropertyToString(prop, propID);
489           if (!s.IsEmpty())
490             g_StdOut << GetPropName(propID, name) << " = " << s << endl;
491         }
492       }
493       if (techMode)
494         g_StdOut << "----------\n";
495       if (numProps > 0)
496         g_StdOut << endl;
497     }
498
499     if (enableHeaders && !techMode)
500     {
501       fieldPrinter.PrintTitle();
502       g_StdOut << endl;
503       fieldPrinter.PrintTitleLines();
504       g_StdOut << endl;
505     }
506
507     if (techMode)
508     {
509       RINOK(fieldPrinter.Init(archive));
510     }
511     UInt64 numFiles = 0, numDirs = 0, totalPackSize = 0, totalUnPackSize = 0;
512     UInt64 *totalPackSizePointer = 0, *totalUnPackSizePointer = 0;
513     UInt32 numItems;
514     RINOK(archive->GetNumberOfItems(&numItems));
515     for(UInt32 i = 0; i < numItems; i++)
516     {
517       if (NConsoleClose::TestBreakSignal())
518         return E_ABORT;
519
520       UString filePath;
521       RINOK(GetArchiveItemPath(archive, i, defaultItemName, filePath));
522
523       bool isFolder;
524       RINOK(IsArchiveItemFolder(archive, i, isFolder));
525       if (!wildcardCensor.CheckPath(filePath, !isFolder))
526         continue;
527       
528       fieldPrinter.PrintItemInfo(archive, defaultItemName, archiveFileInfo, i, techMode);
529       
530       UInt64 packSize, unpackSize;
531       if (!GetUInt64Value(archive, i, kpidSize, unpackSize))
532         unpackSize = 0;
533       else
534         totalUnPackSizePointer = &totalUnPackSize;
535       if (!GetUInt64Value(archive, i, kpidPackedSize, packSize))
536         packSize = 0;
537       else
538         totalPackSizePointer = &totalPackSize;
539       
540       g_StdOut << endl;
541
542       if (isFolder)
543         numDirs++;
544       else
545         numFiles++;
546       totalPackSize += packSize;
547       totalUnPackSize += unpackSize;
548     }
549     if (enableHeaders && !techMode)
550     {
551       fieldPrinter.PrintTitleLines();
552       g_StdOut << endl;
553       fieldPrinter.PrintSummaryInfo(numFiles, numDirs, totalUnPackSizePointer, totalPackSizePointer);
554       g_StdOut << endl;
555     }
556     if (totalPackSizePointer != 0)
557     {
558       totalPackSizePointer2 = &totalPackSize2;
559       totalPackSize2 += totalPackSize;
560     }
561     if (totalUnPackSizePointer != 0)
562     {
563       totalUnPackSizePointer2 = &totalUnPackSize2;
564       totalUnPackSize2 += totalUnPackSize;
565     }
566     numFiles2 += numFiles;
567     numDirs2 += numDirs;
568   }
569   if (enableHeaders && !techMode && archivePaths.Size() > 1)
570   {
571     g_StdOut << endl;
572     fieldPrinter.PrintTitleLines();
573     g_StdOut << endl;
574     fieldPrinter.PrintSummaryInfo(numFiles2, numDirs2, totalUnPackSizePointer2, totalPackSizePointer2);
575     g_StdOut << endl;
576     g_StdOut << "Archives: " << archivePaths.Size() << endl;
577   }
578   return S_OK;
579 }