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