1// HashCon.cpp
2
3#include "StdAfx.h"
4
5#include "../../../Common/IntToString.h"
6#include "../../../Common/StringConvert.h"
7
8#include "../../../Windows/ErrorMsg.h"
9
10#include "ConsoleClose.h"
11#include "HashCon.h"
12
13static const wchar_t *kEmptyFileAlias = L"[Content]";
14
15static const char *kScanningMessage = "Scanning";
16
17HRESULT CHashCallbackConsole::CheckBreak()
18{
19  return NConsoleClose::TestBreakSignal() ? E_ABORT : S_OK;
20}
21
22HRESULT CHashCallbackConsole::StartScanning()
23{
24  (*OutStream) << kScanningMessage;
25  return CheckBreak();
26}
27
28HRESULT CHashCallbackConsole::ScanProgress(UInt64 /* numFolders */, UInt64 /* numFiles */, UInt64 /* totalSize */, const wchar_t * /* path */, bool /* isDir */)
29{
30  return CheckBreak();
31}
32
33HRESULT CHashCallbackConsole::CanNotFindError(const wchar_t *name, DWORD systemError)
34{
35  return CanNotFindError_Base(name, systemError);
36}
37
38HRESULT CHashCallbackConsole::FinishScanning()
39{
40  (*OutStream) << endl << endl;
41  return CheckBreak();
42}
43
44HRESULT CHashCallbackConsole::SetNumFiles(UInt64 /* numFiles */)
45{
46  return CheckBreak();
47}
48
49HRESULT CHashCallbackConsole::SetTotal(UInt64 size)
50{
51  if (EnablePercents)
52    m_PercentPrinter.SetTotal(size);
53  return CheckBreak();
54}
55
56HRESULT CHashCallbackConsole::SetCompleted(const UInt64 *completeValue)
57{
58  if (completeValue && EnablePercents)
59  {
60    m_PercentPrinter.SetRatio(*completeValue);
61    m_PercentPrinter.PrintRatio();
62  }
63  return CheckBreak();
64}
65
66static void AddMinuses(AString &s, unsigned num)
67{
68  for (unsigned i = 0; i < num; i++)
69    s += '-';
70}
71
72static void SetSpaces(char *s, int num)
73{
74  for (int i = 0; i < num; i++)
75    s[i] = ' ';
76}
77
78static void SetSpacesAndNul(char *s, int num)
79{
80  SetSpaces(s, num);
81  s[num] = 0;
82}
83
84static void AddSpaces(UString &s, int num)
85{
86  for (int i = 0; i < num; i++)
87    s += ' ';
88}
89
90static const int kSizeField_Len = 13;
91static const int kNameField_Len = 12;
92
93static unsigned GetColumnWidth(unsigned digestSize)
94{
95  unsigned width = digestSize * 2;
96  const unsigned kMinColumnWidth = 8;
97  return width < kMinColumnWidth ? kMinColumnWidth: width;
98}
99
100void CHashCallbackConsole::PrintSeparatorLine(const CObjectVector<CHasherState> &hashers)
101{
102  AString s;
103  for (unsigned i = 0; i < hashers.Size(); i++)
104  {
105    const CHasherState &h = hashers[i];
106    AddMinuses(s, GetColumnWidth(h.DigestSize));
107    s += ' ';
108  }
109  AddMinuses(s, kSizeField_Len);
110  s += "  ";
111  AddMinuses(s, kNameField_Len);
112  m_PercentPrinter.PrintString(s);
113  m_PercentPrinter.PrintNewLine();
114}
115
116HRESULT CHashCallbackConsole::BeforeFirstFile(const CHashBundle &hb)
117{
118  UString s;
119  FOR_VECTOR (i, hb.Hashers)
120  {
121    const CHasherState &h = hb.Hashers[i];
122    s += h.Name;
123    AddSpaces(s, (int)GetColumnWidth(h.DigestSize) - h.Name.Len() + 1);
124  }
125  UString s2 = L"Size";
126  AddSpaces(s, kSizeField_Len - s2.Len());
127  s += s2;
128  s += L"  ";
129  s += L"Name";
130  m_PercentPrinter.PrintString(s);
131  m_PercentPrinter.PrintNewLine();
132  PrintSeparatorLine(hb.Hashers);
133  return CheckBreak();
134}
135
136HRESULT CHashCallbackConsole::OpenFileError(const wchar_t *name, DWORD systemError)
137{
138  FailedCodes.Add(systemError);
139  FailedFiles.Add(name);
140  // if (systemError == ERROR_SHARING_VIOLATION)
141  {
142    m_PercentPrinter.PrintString(name);
143    m_PercentPrinter.PrintString(": WARNING: ");
144    m_PercentPrinter.PrintString(NWindows::NError::MyFormatMessage(systemError));
145    return S_FALSE;
146  }
147  // return systemError;
148}
149
150HRESULT CHashCallbackConsole::GetStream(const wchar_t *name, bool /* isFolder */)
151{
152  m_FileName = name;
153  return CheckBreak();
154}
155
156void CHashCallbackConsole::PrintResultLine(UInt64 fileSize,
157    const CObjectVector<CHasherState> &hashers, unsigned digestIndex, bool showHash)
158{
159  FOR_VECTOR (i, hashers)
160  {
161    const CHasherState &h = hashers[i];
162
163    char s[k_HashCalc_DigestSize_Max * 2 + 64];
164    s[0] = 0;
165    if (showHash)
166      AddHashHexToString(s, h.Digests[digestIndex], h.DigestSize);
167    SetSpacesAndNul(s + strlen(s), (int)GetColumnWidth(h.DigestSize) - (int)strlen(s) + 1);
168    m_PercentPrinter.PrintString(s);
169  }
170  char s[64];
171  s[0] = 0;
172  char *p = s;
173  if (showHash && fileSize != 0)
174  {
175    p = s + 32;
176    ConvertUInt64ToString(fileSize, p);
177    int numSpaces = kSizeField_Len - (int)strlen(p);
178    if (numSpaces > 0)
179    {
180      p -= numSpaces;
181      SetSpaces(p, numSpaces);
182    }
183  }
184  else
185    SetSpacesAndNul(s, kSizeField_Len - (int)strlen(s));
186  unsigned len = (unsigned)strlen(p);
187  p[len] = ' ';
188  p[len + 1] = ' ';
189  p[len + 2] = 0;
190  m_PercentPrinter.PrintString(p);
191}
192
193HRESULT CHashCallbackConsole::SetOperationResult(UInt64 fileSize, const CHashBundle &hb, bool showHash)
194{
195  PrintResultLine(fileSize, hb.Hashers, k_HashCalc_Index_Current, showHash);
196  if (m_FileName.IsEmpty())
197    m_PercentPrinter.PrintString(kEmptyFileAlias);
198  else
199    m_PercentPrinter.PrintString(m_FileName);
200  m_PercentPrinter.PrintNewLine();
201  return S_OK;
202}
203
204static const char *k_DigestTitles[] =
205{
206    " :"
207  , " for data:              "
208  , " for data and names:    "
209  , " for streams and names: "
210};
211
212static void PrintSum(CStdOutStream &p, const CHasherState &h, unsigned digestIndex)
213{
214  char s[k_HashCalc_DigestSize_Max * 2 + 64];
215  UString name = h.Name;
216  AddSpaces(name, 6 - (int)name.Len());
217  p << name;
218  p << k_DigestTitles[digestIndex];
219  s[0] = 0;
220  AddHashHexToString(s, h.Digests[digestIndex], h.DigestSize);
221  p << s;
222  p << "\n";
223}
224
225
226void PrintHashStat(CStdOutStream &p, const CHashBundle &hb)
227{
228  FOR_VECTOR (i, hb.Hashers)
229  {
230    const CHasherState &h = hb.Hashers[i];
231    p << "\n";
232    PrintSum(p, h, k_HashCalc_Index_DataSum);
233    if (hb.NumFiles != 1 || hb.NumDirs != 0)
234      PrintSum(p, h, k_HashCalc_Index_NamesSum);
235    if (hb.NumAltStreams != 0)
236      PrintSum(p, h, k_HashCalc_Index_StreamsSum);
237  }
238}
239
240void CHashCallbackConsole::PrintProperty(const char *name, UInt64 value)
241{
242  char s[32];
243  s[0] = ':';
244  s[1] = ' ';
245  ConvertUInt64ToString(value, s + 2);
246  m_PercentPrinter.PrintString(name);
247  m_PercentPrinter.PrintString(s);
248  m_PercentPrinter.PrintNewLine();
249}
250
251HRESULT CHashCallbackConsole::AfterLastFile(const CHashBundle &hb)
252{
253  PrintSeparatorLine(hb.Hashers);
254
255  PrintResultLine(hb.FilesSize, hb.Hashers, k_HashCalc_Index_DataSum, true);
256  m_PercentPrinter.PrintNewLine();
257  m_PercentPrinter.PrintNewLine();
258
259  if (hb.NumFiles != 1 || hb.NumDirs != 0)
260  {
261    if (hb.NumDirs != 0)
262      PrintProperty("Folders", hb.NumDirs);
263    PrintProperty("Files", hb.NumFiles);
264  }
265  PrintProperty("Size", hb.FilesSize);
266  if (hb.NumAltStreams != 0)
267  {
268    PrintProperty("AltStreams", hb.NumAltStreams);
269    PrintProperty("AltStreams size", hb.AltStreamsSize);
270  }
271  PrintHashStat(*m_PercentPrinter.OutStream, hb);
272  m_PercentPrinter.PrintNewLine();
273  return S_OK;
274}
275