1 // HashCon.cpp
2
3 #include "StdAfx.h"
4
5 #include "../../../Common/IntToString.h"
6
7 #include "ConsoleClose.h"
8 #include "HashCon.h"
9
10 static const wchar_t *kEmptyFileAlias = L"[Content]";
11
12 static const char *kScanningMessage = "Scanning";
13
CheckBreak2()14 static HRESULT CheckBreak2()
15 {
16 return NConsoleClose::TestBreakSignal() ? E_ABORT : S_OK;
17 }
18
CheckBreak()19 HRESULT CHashCallbackConsole::CheckBreak()
20 {
21 return CheckBreak2();
22 }
23
StartScanning()24 HRESULT CHashCallbackConsole::StartScanning()
25 {
26 if (PrintHeaders && _so)
27 *_so << kScanningMessage << endl;
28 if (NeedPercents())
29 {
30 _percent.ClearCurState();
31 _percent.Command = "Scan";
32 }
33 return CheckBreak2();
34 }
35
ScanProgress(const CDirItemsStat & st,const FString & path,bool)36 HRESULT CHashCallbackConsole::ScanProgress(const CDirItemsStat &st, const FString &path, bool /* isDir */)
37 {
38 if (NeedPercents())
39 {
40 _percent.Files = st.NumDirs + st.NumFiles + st.NumAltStreams;
41 _percent.Completed = st.GetTotalBytes();
42 _percent.FileName = fs2us(path);
43 _percent.Print();
44 }
45 return CheckBreak2();
46 }
47
ScanError(const FString & path,DWORD systemError)48 HRESULT CHashCallbackConsole::ScanError(const FString &path, DWORD systemError)
49 {
50 return ScanError_Base(path, systemError);
51 }
52
53 void Print_DirItemsStat(AString &s, const CDirItemsStat &st);
54
FinishScanning(const CDirItemsStat & st)55 HRESULT CHashCallbackConsole::FinishScanning(const CDirItemsStat &st)
56 {
57 if (NeedPercents())
58 {
59 _percent.ClosePrint(true);
60 _percent.ClearCurState();
61 }
62 if (PrintHeaders && _so)
63 {
64 Print_DirItemsStat(_s, st);
65 *_so << _s << endl << endl;
66 }
67 return CheckBreak2();
68 }
69
SetNumFiles(UInt64)70 HRESULT CHashCallbackConsole::SetNumFiles(UInt64 /* numFiles */)
71 {
72 return CheckBreak2();
73 }
74
SetTotal(UInt64 size)75 HRESULT CHashCallbackConsole::SetTotal(UInt64 size)
76 {
77 if (NeedPercents())
78 {
79 _percent.Total = size;
80 _percent.Print();
81 }
82 return CheckBreak2();
83 }
84
SetCompleted(const UInt64 * completeValue)85 HRESULT CHashCallbackConsole::SetCompleted(const UInt64 *completeValue)
86 {
87 if (completeValue && NeedPercents())
88 {
89 _percent.Completed = *completeValue;
90 _percent.Print();
91 }
92 return CheckBreak2();
93 }
94
AddMinuses(AString & s,unsigned num)95 static void AddMinuses(AString &s, unsigned num)
96 {
97 for (unsigned i = 0; i < num; i++)
98 s += '-';
99 }
100
AddSpaces_if_Positive(AString & s,int num)101 static void AddSpaces_if_Positive(AString &s, int num)
102 {
103 for (int i = 0; i < num; i++)
104 s.Add_Space();
105 }
106
SetSpacesAndNul(char * s,unsigned num)107 static void SetSpacesAndNul(char *s, unsigned num)
108 {
109 for (unsigned i = 0; i < num; i++)
110 s[i] = ' ';
111 s[num] = 0;
112 }
113
114 static const unsigned kSizeField_Len = 13;
115 static const unsigned kNameField_Len = 12;
116
117 static const unsigned kHashColumnWidth_Min = 4 * 2;
118
GetColumnWidth(unsigned digestSize)119 static unsigned GetColumnWidth(unsigned digestSize)
120 {
121 unsigned width = digestSize * 2;
122 return width < kHashColumnWidth_Min ? kHashColumnWidth_Min: width;
123 }
124
PrintSeparatorLine(const CObjectVector<CHasherState> & hashers)125 void CHashCallbackConsole::PrintSeparatorLine(const CObjectVector<CHasherState> &hashers)
126 {
127 _s.Empty();
128
129 for (unsigned i = 0; i < hashers.Size(); i++)
130 {
131 if (i != 0)
132 _s.Add_Space();
133 const CHasherState &h = hashers[i];
134 AddMinuses(_s, GetColumnWidth(h.DigestSize));
135 }
136
137 if (PrintSize)
138 {
139 _s.Add_Space();
140 AddMinuses(_s, kSizeField_Len);
141 }
142
143 if (PrintName)
144 {
145 AddSpacesBeforeName();
146 AddMinuses(_s, kNameField_Len);
147 }
148
149 *_so << _s << endl;
150 }
151
BeforeFirstFile(const CHashBundle & hb)152 HRESULT CHashCallbackConsole::BeforeFirstFile(const CHashBundle &hb)
153 {
154 if (PrintHeaders && _so)
155 {
156 _s.Empty();
157 ClosePercents_for_so();
158
159 FOR_VECTOR (i, hb.Hashers)
160 {
161 if (i != 0)
162 _s.Add_Space();
163 const CHasherState &h = hb.Hashers[i];
164 _s += h.Name;
165 AddSpaces_if_Positive(_s, (int)GetColumnWidth(h.DigestSize) - (int)h.Name.Len());
166 }
167
168 if (PrintSize)
169 {
170 _s.Add_Space();
171 const AString s2 = "Size";
172 AddSpaces_if_Positive(_s, (int)kSizeField_Len - (int)s2.Len());
173 _s += s2;
174 }
175
176 if (PrintName)
177 {
178 AddSpacesBeforeName();
179 _s += "Name";
180 }
181
182 *_so << _s << endl;
183 PrintSeparatorLine(hb.Hashers);
184 }
185
186 return CheckBreak2();
187 }
188
OpenFileError(const FString & path,DWORD systemError)189 HRESULT CHashCallbackConsole::OpenFileError(const FString &path, DWORD systemError)
190 {
191 return OpenFileError_Base(path, systemError);
192 }
193
GetStream(const wchar_t * name,bool)194 HRESULT CHashCallbackConsole::GetStream(const wchar_t *name, bool /* isFolder */)
195 {
196 _fileName = name;
197
198 if (NeedPercents())
199 {
200 if (PrintNameInPercents)
201 {
202 _percent.FileName.Empty();
203 if (name)
204 _percent.FileName = name;
205 }
206 _percent.Print();
207 }
208 return CheckBreak2();
209 }
210
PrintResultLine(UInt64 fileSize,const CObjectVector<CHasherState> & hashers,unsigned digestIndex,bool showHash)211 void CHashCallbackConsole::PrintResultLine(UInt64 fileSize,
212 const CObjectVector<CHasherState> &hashers, unsigned digestIndex, bool showHash)
213 {
214 ClosePercents_for_so();
215
216 _s.Empty();
217
218 FOR_VECTOR (i, hashers)
219 {
220 const CHasherState &h = hashers[i];
221 char s[k_HashCalc_DigestSize_Max * 2 + 64];
222 s[0] = 0;
223 if (showHash)
224 AddHashHexToString(s, h.Digests[digestIndex], h.DigestSize);
225 SetSpacesAndNul(s + strlen(s), (int)GetColumnWidth(h.DigestSize) - (int)strlen(s));
226 if (i != 0)
227 _s.Add_Space();
228 _s += s;
229 }
230
231 if (PrintSize)
232 {
233 _s.Add_Space();
234
235 char s[kSizeField_Len + 32];
236 char *p = s;
237
238 if (showHash)
239 {
240 p = s + kSizeField_Len;
241 ConvertUInt64ToString(fileSize, p);
242 int numSpaces = kSizeField_Len - (int)strlen(p);
243 if (numSpaces > 0)
244 {
245 p -= (unsigned)numSpaces;
246 for (unsigned i = 0; i < (unsigned)numSpaces; i++)
247 p[i] = ' ';
248 }
249 }
250 else
251 SetSpacesAndNul(s, kSizeField_Len);
252
253 _s += p;
254 }
255
256 if (PrintName)
257 AddSpacesBeforeName();
258
259 *_so << _s;
260 }
261
SetOperationResult(UInt64 fileSize,const CHashBundle & hb,bool showHash)262 HRESULT CHashCallbackConsole::SetOperationResult(UInt64 fileSize, const CHashBundle &hb, bool showHash)
263 {
264 if (_so)
265 {
266 PrintResultLine(fileSize, hb.Hashers, k_HashCalc_Index_Current, showHash);
267 if (PrintName)
268 {
269 if (_fileName.IsEmpty())
270 *_so << kEmptyFileAlias;
271 else
272 *_so << _fileName;
273 }
274 *_so << endl;
275 }
276
277 if (NeedPercents())
278 {
279 _percent.Files++;
280 _percent.Print();
281 }
282
283 return CheckBreak2();
284 }
285
286 static const char * const k_DigestTitles[] =
287 {
288 " : "
289 , " for data: "
290 , " for data and names: "
291 , " for streams and names: "
292 };
293
PrintSum(CStdOutStream & so,const CHasherState & h,unsigned digestIndex)294 static void PrintSum(CStdOutStream &so, const CHasherState &h, unsigned digestIndex)
295 {
296 so << h.Name;
297
298 {
299 AString temp;
300 AddSpaces_if_Positive(temp, 6 - (int)h.Name.Len());
301 so << temp;
302 }
303
304 so << k_DigestTitles[digestIndex];
305
306 char s[k_HashCalc_DigestSize_Max * 2 + 64];
307 s[0] = 0;
308 AddHashHexToString(s, h.Digests[digestIndex], h.DigestSize);
309 so << s << endl;
310 }
311
PrintHashStat(CStdOutStream & so,const CHashBundle & hb)312 void PrintHashStat(CStdOutStream &so, const CHashBundle &hb)
313 {
314 FOR_VECTOR (i, hb.Hashers)
315 {
316 const CHasherState &h = hb.Hashers[i];
317 PrintSum(so, h, k_HashCalc_Index_DataSum);
318 if (hb.NumFiles != 1 || hb.NumDirs != 0)
319 PrintSum(so, h, k_HashCalc_Index_NamesSum);
320 if (hb.NumAltStreams != 0)
321 PrintSum(so, h, k_HashCalc_Index_StreamsSum);
322 so << endl;
323 }
324 }
325
PrintProperty(const char * name,UInt64 value)326 void CHashCallbackConsole::PrintProperty(const char *name, UInt64 value)
327 {
328 char s[32];
329 s[0] = ':';
330 s[1] = ' ';
331 ConvertUInt64ToString(value, s + 2);
332 *_so << name << s << endl;
333 }
334
AfterLastFile(const CHashBundle & hb)335 HRESULT CHashCallbackConsole::AfterLastFile(const CHashBundle &hb)
336 {
337 ClosePercents2();
338
339 if (PrintHeaders && _so)
340 {
341 PrintSeparatorLine(hb.Hashers);
342
343 PrintResultLine(hb.FilesSize, hb.Hashers, k_HashCalc_Index_DataSum, true);
344
345 *_so << endl << endl;
346
347 if (hb.NumFiles != 1 || hb.NumDirs != 0)
348 {
349 if (hb.NumDirs != 0)
350 PrintProperty("Folders", hb.NumDirs);
351 PrintProperty("Files", hb.NumFiles);
352 }
353
354 PrintProperty("Size", hb.FilesSize);
355
356 if (hb.NumAltStreams != 0)
357 {
358 PrintProperty("Alternate streams", hb.NumAltStreams);
359 PrintProperty("Alternate streams size", hb.AltStreamsSize);
360 }
361
362 *_so << endl;
363 PrintHashStat(*_so, hb);
364 }
365
366 return S_OK;
367 }
368