file-digests 0.0.33 → 0.0.38

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. checksums.yaml +4 -4
  2. data/bin/file-digests +14 -0
  3. data/lib/file-digests.rb +200 -105
  4. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 68dea8d33894b138d5908dcf27fef7800a17af8f1441fd043876248414ad9525
4
- data.tar.gz: da5c89c26fc95ddeaa6b8443193293490e71f41bc4ed21e00415c5937661833e
3
+ metadata.gz: 1a7193a9795eb785cd22a7663fa4dae908d3349b854be8d3613750f24ce6b711
4
+ data.tar.gz: 32a8172cc7115240f5954b7e4775f841ddce10102c7e48e184690d2643e7a476
5
5
  SHA512:
6
- metadata.gz: 05e700dedeebde8e472e8adf6b0eedb6c3692b919b3d478a041c8e8110375daae7dd926cd9c33c46379976730f0fb0610880a57524aa44e0764fa169a4020fad
7
- data.tar.gz: c0e954b6250223e53ec0823f328bd808886f9566ecf373ceaaba5da7a079ae76e18dfba3cf81bfad0741ea8dfdb87c1715c81f8af72094b77abe014fc59727ef
6
+ metadata.gz: 584a6fc4c10e6a33d9e55d9647abcfce913fa18b046eddf7193807408d7ac4167b1b7114aa300531426e767668841f483ad66775bf7f492ff552f290d19f6e2a
7
+ data.tar.gz: 877dea6a6ba8f3ab091ed6d5b0379090a60cff4477ad9d4409fa9fef9674bd39b9134682ac534d49535a21ed58382b02088fa00f83524d31add7c23919e9c0c4
@@ -1,5 +1,19 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
+ # Copyright 2020 Stanislav Senotrusov <stan@senotrusov.com>
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
3
17
  require 'file-digests'
4
18
 
5
19
  FileDigests.run_cli_utility
@@ -1,3 +1,17 @@
1
+ # Copyright 2020 Stanislav Senotrusov <stan@senotrusov.com>
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
1
15
  require "date"
2
16
  require "digest"
3
17
  require "fileutils"
@@ -8,7 +22,8 @@ require "set"
8
22
  require "sqlite3"
9
23
 
10
24
  class FileDigests
11
- DIGEST_ALGORITHMS=["BLAKE2b512", "SHA3-256", "SHA512-256"]
25
+ VERSION = Gem.loaded_specs["file-digests"]&.version&.to_s
26
+ DIGEST_ALGORITHMS = ["BLAKE2b512", "SHA3-256", "SHA512-256"]
12
27
  LEGACY_DIGEST_ALGORITHMS = ["SHA512", "SHA256"]
13
28
 
14
29
  def self.canonical_digest_algorithm_name(string)
@@ -101,6 +116,7 @@ class FileDigests
101
116
 
102
117
  def initialize files_path, digest_database_path, options = {}
103
118
  @options = options
119
+ @user_input_wait_time = 0
104
120
 
105
121
  initialize_paths files_path, digest_database_path
106
122
  initialize_database
@@ -124,13 +140,13 @@ class FileDigests
124
140
 
125
141
  def initialize_paths files_path, digest_database_path
126
142
  @files_path = cleanup_path(files_path || ".")
127
-
128
143
  raise "Files path must be a readable directory" unless (File.directory?(@files_path) && File.readable?(@files_path))
144
+ @files_path = realpath_with_disk @files_path
129
145
 
130
146
  @digest_database_path = digest_database_path ? cleanup_path(digest_database_path) : @files_path
131
147
  @digest_database_path += ".file-digests.sqlite" if File.directory?(@digest_database_path)
132
148
  ensure_dir_exist @digest_database_path.dirname
133
-
149
+ @digest_database_path = realdirpath_with_disk @digest_database_path
134
150
  @digest_database_files = ["#{@digest_database_path}", "#{@digest_database_path}-wal", "#{@digest_database_path}-shm"]
135
151
 
136
152
  if @options[:verbose]
@@ -144,8 +160,6 @@ class FileDigests
144
160
  @db.results_as_hash = true
145
161
  @db.busy_timeout = 5000
146
162
 
147
- file_digests_gem_version = Gem.loaded_specs["file-digests"]&.version&.to_s
148
-
149
163
  execute "PRAGMA encoding = 'UTF-8'"
150
164
  execute "PRAGMA locking_mode = 'EXCLUSIVE'"
151
165
  execute "PRAGMA journal_mode = 'WAL'"
@@ -160,14 +174,13 @@ class FileDigests
160
174
  execute "CREATE TABLE metadata (
161
175
  key TEXT NOT NULL PRIMARY KEY,
162
176
  value TEXT)"
163
- execute "CREATE UNIQUE INDEX metadata_key ON metadata(key)"
164
177
  metadata_table_was_created = true
165
178
  end
166
179
 
167
180
  prepare_method :set_metadata_query, "INSERT INTO metadata (key, value) VALUES (?, ?) ON CONFLICT (key) DO UPDATE SET value=excluded.value"
168
181
  prepare_method :get_metadata_query, "SELECT value FROM metadata WHERE key = ?"
169
182
 
170
- set_metadata("metadata_table_created_by_gem_version", file_digests_gem_version) if file_digests_gem_version && metadata_table_was_created
183
+ set_metadata("metadata_table_created_by_gem_version", FileDigests::VERSION) if FileDigests::VERSION && metadata_table_was_created
171
184
 
172
185
  # Heuristic to detect database version 1 (metadata was not stored back then)
173
186
  unless get_metadata("database_version")
@@ -184,20 +197,19 @@ class FileDigests
184
197
  digest TEXT NOT NULL,
185
198
  digest_check_time TEXT NOT NULL)"
186
199
  execute "CREATE UNIQUE INDEX digests_filename ON digests(filename)"
187
- set_metadata("digests_table_created_by_gem_version", file_digests_gem_version) if file_digests_gem_version
200
+ execute "CREATE INDEX digests_digest ON digests(digest)"
201
+ set_metadata("digests_table_created_by_gem_version", FileDigests::VERSION) if FileDigests::VERSION
188
202
  end
189
203
 
190
- prepare_method :insert, "INSERT INTO digests (filename, mtime, digest, digest_check_time) VALUES (?, ?, ?, datetime('now'))"
191
- prepare_method :find_by_filename_query, "SELECT id, mtime, digest FROM digests WHERE filename = ?"
192
- prepare_method :touch_digest_check_time, "UPDATE digests SET digest_check_time = datetime('now') WHERE id = ?"
193
- prepare_method :update_mtime_and_digest, "UPDATE digests SET mtime = ?, digest = ?, digest_check_time = datetime('now') WHERE id = ?"
194
- prepare_method :update_mtime, "UPDATE digests SET mtime = ?, digest_check_time = datetime('now') WHERE id = ?"
195
- prepare_method :delete_by_filename, "DELETE FROM digests WHERE filename = ?"
196
- prepare_method :query_duplicates, "SELECT digest, filename FROM digests WHERE digest IN (SELECT digest FROM digests GROUP BY digest HAVING count(*) > 1) ORDER BY digest, filename;"
197
- prepare_method :update_digest_to_new_digest, "UPDATE digests SET digest = ? WHERE digest = ?"
204
+ prepare_method :digests_insert, "INSERT INTO digests (filename, mtime, digest, digest_check_time) VALUES (?, ?, ?, datetime('now'))"
205
+ prepare_method :digests_find_by_filename_query, "SELECT id, mtime, digest FROM digests WHERE filename = ?"
206
+ prepare_method :digests_touch_check_time, "UPDATE digests SET digest_check_time = datetime('now') WHERE id = ?"
207
+ prepare_method :digests_update_mtime_and_digest, "UPDATE digests SET mtime = ?, digest = ?, digest_check_time = datetime('now') WHERE id = ?"
208
+ prepare_method :digests_update_mtime, "UPDATE digests SET mtime = ?, digest_check_time = datetime('now') WHERE id = ?"
209
+ prepare_method :digests_select_duplicates, "SELECT digest, filename FROM digests WHERE digest IN (SELECT digest FROM digests GROUP BY digest HAVING count(*) > 1) ORDER BY digest, filename;"
198
210
 
199
211
  unless get_metadata("database_version")
200
- set_metadata "database_version", "2"
212
+ set_metadata "database_version", "3"
201
213
  end
202
214
 
203
215
  # Convert database from 1st to 2nd version
@@ -212,79 +224,110 @@ class FileDigests
212
224
  end
213
225
  end
214
226
 
215
- if get_metadata("database_version") != "2"
216
- STDERR.puts "This version of file-digests (#{file_digests_gem_version || "unknown"}) is only compartible with the database version 2. Current database version is #{get_metadata("database_version")}. To use this database, please install appropriate version if file-digest."
217
- raise "Incompatible database version"
227
+ if get_metadata("database_version") == "2"
228
+ execute "CREATE INDEX digests_digest ON digests(digest)"
229
+ set_metadata "database_version", "3"
218
230
  end
231
+
232
+ check_if_database_is_at_certain_version "3"
233
+
234
+ create_temporary_tables
219
235
  end
220
236
  end
221
237
 
222
- def perform_check
223
- perhaps_transaction(@new_digest_algorithm, :exclusive) do
224
- @counters = {good: 0, updated: 0, new: 0, renamed: 0, likely_damaged: 0, exceptions: 0}
225
- @new_files = {}
226
- @new_digests = {}
238
+ def create_temporary_tables
239
+ execute "CREATE TEMPORARY TABLE new_files (
240
+ filename TEXT NOT NULL PRIMARY KEY,
241
+ digest TEXT NOT NULL)"
242
+ execute "CREATE INDEX new_files_digest ON new_files(digest)"
243
+
244
+ prepare_method :new_files_insert, "INSERT INTO new_files (filename, digest) VALUES (?, ?)"
245
+ prepare_method :new_files_count_query, "SELECT count(*) FROM new_files"
246
+
247
+ execute "CREATE TEMPORARY TABLE missing_files (
248
+ filename TEXT NOT NULL PRIMARY KEY,
249
+ digest TEXT NOT NULL)"
250
+ execute "CREATE INDEX missing_files_digest ON missing_files(digest)"
227
251
 
228
- @missing_files = Hash[@db.prepare("SELECT filename, digest FROM digests").execute!]
252
+ execute "INSERT INTO missing_files (filename, digest) SELECT filename, digest FROM digests"
229
253
 
230
- measure_time do
231
- walk_files do |filename|
254
+ prepare_method :missing_files_delete, "DELETE FROM missing_files WHERE filename = ?"
255
+ prepare_method :missing_files_delete_renamed_files, "DELETE FROM missing_files WHERE digest IN (SELECT digest FROM new_files)"
256
+ prepare_method :missing_files_select_all_filenames, "SELECT filename FROM missing_files ORDER BY filename"
257
+ prepare_method :missing_files_delete_all, "DELETE FROM missing_files"
258
+ prepare_method :missing_files_count_query, "SELECT count(*) FROM missing_files"
259
+
260
+ prepare_method :digests_delete_renamed_files, "DELETE FROM digests WHERE filename IN (SELECT filename FROM missing_files WHERE digest IN (SELECT digest FROM new_files))"
261
+ prepare_method :digests_delete_all_missing_files, "DELETE FROM digests WHERE filename IN (SELECT filename FROM missing_files)"
262
+
263
+ execute "CREATE TEMPORARY TABLE new_digests (
264
+ filename TEXT NOT NULL PRIMARY KEY,
265
+ digest TEXT NOT NULL)"
266
+
267
+ prepare_method :new_digests_insert, "INSERT INTO new_digests (filename, digest) VALUES (?, ?)"
268
+ prepare_method :digests_update_digests_to_new_digests, "INSERT INTO digests (filename, digest, digest_check_time) SELECT filename, digest, false FROM new_digests WHERE true ON CONFLICT (filename) DO UPDATE SET digest=excluded.digest"
269
+ end
270
+
271
+ def perform_check
272
+ measure_time do
273
+ perhaps_transaction(@new_digest_algorithm, :exclusive) do
274
+ @counters = {good: 0, updated: 0, renamed: 0, likely_damaged: 0, exceptions: 0}
275
+
276
+ walk_files(@files_path.to_s) do |filename|
232
277
  process_file filename
233
278
  end
234
- end
235
279
 
236
- nested_transaction do
237
- puts "Tracking renames..." if @options[:verbose]
238
- track_renames
239
- end
280
+ nested_transaction do
281
+ puts "Tracking renames..." if @options[:verbose]
282
+ track_renames
283
+ end
240
284
 
241
- if any_missing_files?
242
- if any_exceptions?
243
- STDERR.puts "Due to previously occurred errors, database cleanup from missing files will be skipped this time."
244
- else
245
- print_missing_files
246
- if !@options[:test_only] && (@options[:auto] || confirm("Remove missing files from the database"))
247
- nested_transaction do
248
- puts "Removing missing files..." if @options[:verbose]
249
- remove_missing_files
285
+ if any_missing_files?
286
+ if any_exceptions?
287
+ STDERR.puts "Due to previously occurred errors, missing files will not removed from the database."
288
+ else
289
+ print_missing_files
290
+ if !@options[:test_only] && (@options[:auto] || confirm("Remove missing files from the database"))
291
+ nested_transaction do
292
+ puts "Removing missing files..." if @options[:verbose]
293
+ remove_missing_files
294
+ end
250
295
  end
251
296
  end
252
297
  end
253
- end
254
298
 
255
- if @new_digest_algorithm && !@options[:test_only]
256
- if any_missing_files? || any_likely_damaged? || any_exceptions?
257
- STDERR.puts "ERROR: New digest algorithm will not be in effect until there are files that are missing, likely damaged, or processed with an exception."
258
- else
259
- puts "Updating database to a new digest algorithm..." if @options[:verbose]
260
- @new_digests.each do |old_digest, new_digest|
261
- update_digest_to_new_digest new_digest, old_digest
299
+ if @new_digest_algorithm && !@options[:test_only]
300
+ if any_missing_files? || any_likely_damaged? || any_exceptions?
301
+ STDERR.puts "ERROR: New digest algorithm will not be in effect until there are files that are missing, likely damaged, or processed with an exception."
302
+ else
303
+ puts "Updating database to a new digest algorithm..." if @options[:verbose]
304
+ digests_update_digests_to_new_digests
305
+ set_metadata "digest_algorithm", @new_digest_algorithm
306
+ puts "Transition to a new digest algorithm complete: #{@new_digest_algorithm}"
262
307
  end
263
- set_metadata "digest_algorithm", @new_digest_algorithm
264
- puts "Transition to a new digest algorithm complete: #{@new_digest_algorithm}"
265
308
  end
266
- end
267
309
 
268
- if any_likely_damaged? || any_exceptions?
269
- STDERR.puts "PLEASE REVIEW ERRORS THAT WERE OCCURRED!"
270
- end
310
+ if any_likely_damaged? || any_exceptions?
311
+ STDERR.puts "PLEASE REVIEW ERRORS THAT WERE OCCURRED!"
312
+ end
271
313
 
272
- set_metadata(@options[:test_only] ? "latest_test_only_check_time" : "latest_complete_check_time", time_to_database(Time.now))
314
+ set_metadata(@options[:test_only] ? "latest_test_only_check_time" : "latest_complete_check_time", time_to_database(Time.now))
273
315
 
274
- print_counters
275
- end
276
-
277
- puts "Performing database maintenance..." if @options[:verbose]
278
- execute "PRAGMA optimize"
279
- execute "VACUUM"
280
- execute "PRAGMA wal_checkpoint(TRUNCATE)"
316
+ print_counters
317
+ end
318
+
319
+ puts "Performing database maintenance..." if @options[:verbose]
320
+ execute "PRAGMA optimize"
321
+ execute "VACUUM"
322
+ execute "PRAGMA wal_checkpoint(TRUNCATE)"
281
323
 
282
- hide_database_files
324
+ hide_database_files
325
+ end
283
326
  end
284
327
 
285
328
  def show_duplicates
286
329
  current_digest = nil
287
- query_duplicates.each do |found|
330
+ digests_select_duplicates.each do |found|
288
331
  if current_digest != found["digest"]
289
332
  puts "" if current_digest
290
333
  current_digest = found["digest"]
@@ -297,9 +340,11 @@ class FileDigests
297
340
  private
298
341
 
299
342
  def process_file filename
300
- return if File.symlink? filename
343
+ perhaps_nt_filename = perhaps_nt_path filename
344
+
345
+ return if File.symlink? perhaps_nt_filename
301
346
 
302
- stat = File.stat filename
347
+ stat = File.stat perhaps_nt_filename
303
348
 
304
349
  return if stat.blockdev?
305
350
  return if stat.chardev?
@@ -316,9 +361,10 @@ class FileDigests
316
361
 
317
362
  normalized_filename = filename.delete_prefix("#{@files_path.to_s}/").encode("utf-8", universal_newline: true).unicode_normalize(:nfkc)
318
363
  mtime_string = time_to_database stat.mtime
319
- digest = get_file_digest(filename)
364
+ digest, new_digest = get_file_digest(perhaps_nt_filename)
320
365
 
321
366
  nested_transaction do
367
+ new_digests_insert(normalized_filename, new_digest) if new_digest
322
368
  process_file_indeed normalized_filename, mtime_string, digest
323
369
  end
324
370
 
@@ -336,15 +382,15 @@ class FileDigests
336
382
  end
337
383
 
338
384
  def process_previously_seen_file found, filename, mtime, digest
339
- @missing_files.delete(filename)
385
+ missing_files_delete filename
340
386
  if found["digest"] == digest
341
387
  @counters[:good] += 1
342
388
  puts "GOOD: #{filename}" if @options[:verbose]
343
389
  unless @options[:test_only]
344
390
  if found["mtime"] == mtime
345
- touch_digest_check_time found["id"]
391
+ digests_touch_check_time found["id"]
346
392
  else
347
- update_mtime mtime, found["id"]
393
+ digests_update_mtime mtime, found["id"]
348
394
  end
349
395
  end
350
396
  else
@@ -355,18 +401,17 @@ class FileDigests
355
401
  @counters[:updated] += 1
356
402
  puts "UPDATED#{" (FATE ACCEPTED)" if found["mtime"] == mtime && @options[:accept_fate]}: #{filename}" unless @options[:quiet]
357
403
  unless @options[:test_only]
358
- update_mtime_and_digest mtime, digest, found["id"]
404
+ digests_update_mtime_and_digest mtime, digest, found["id"]
359
405
  end
360
406
  end
361
407
  end
362
408
  end
363
409
 
364
410
  def process_new_file filename, mtime, digest
365
- @counters[:new] += 1
366
411
  puts "NEW: #{filename}" unless @options[:quiet]
412
+ new_files_insert filename, digest
367
413
  unless @options[:test_only]
368
- @new_files[filename] = digest
369
- insert filename, mtime, digest
414
+ digests_insert filename, mtime, digest
370
415
  end
371
416
  end
372
417
 
@@ -374,29 +419,31 @@ class FileDigests
374
419
  # Renames and missing files
375
420
 
376
421
  def track_renames
377
- @missing_files.delete_if do |filename, digest|
378
- if @new_files.value?(digest)
379
- @counters[:renamed] += 1
380
- unless @options[:test_only]
381
- delete_by_filename filename
382
- end
383
- true
384
- end
422
+ unless @options[:test_only]
423
+ digests_delete_renamed_files
385
424
  end
425
+ missing_files_delete_renamed_files
426
+ @counters[:renamed] = @db.changes
386
427
  end
387
428
 
388
429
  def print_missing_files
389
430
  puts "\nMISSING FILES:"
390
- @missing_files.sort.to_h.each do |filename, digest|
391
- puts filename
431
+ missing_files_select_all_filenames.each do |record|
432
+ puts record["filename"]
392
433
  end
393
434
  end
394
435
 
395
436
  def remove_missing_files
396
- @missing_files.each do |filename, digest|
397
- delete_by_filename filename
398
- end
399
- @missing_files = {}
437
+ digests_delete_all_missing_files
438
+ missing_files_delete_all
439
+ end
440
+
441
+ def missing_files_count
442
+ missing_files_count_query!&.first&.first
443
+ end
444
+
445
+ def any_missing_files?
446
+ missing_files_count > 0
400
447
  end
401
448
 
402
449
 
@@ -407,6 +454,7 @@ class FileDigests
407
454
  end
408
455
 
409
456
  def integrity_check
457
+ puts "Checking database integrity..." if @options[:verbose]
410
458
  if execute("PRAGMA integrity_check")&.first&.fetch("integrity_check") != "ok"
411
459
  raise "Database integrity check failed"
412
460
  end
@@ -461,7 +509,7 @@ class FileDigests
461
509
  end
462
510
 
463
511
  def find_by_filename filename
464
- result = find_by_filename_query filename
512
+ result = digests_find_by_filename_query filename
465
513
  found = result.next
466
514
  raise "Multiple records found" if result.next
467
515
  found
@@ -481,9 +529,37 @@ class FileDigests
481
529
  end
482
530
  end
483
531
 
532
+ def check_if_database_is_at_certain_version target_version
533
+ current_version = get_metadata("database_version")
534
+ if current_version != target_version
535
+ STDERR.puts "This version of file-digests (#{FileDigests::VERSION || "unknown"}) is only compartible with the database version #{target_version}. Current database version is #{current_version}. To use this database, please install appropriate version if file-digest."
536
+ raise "Incompatible database version"
537
+ end
538
+ end
539
+
540
+ def new_files_count
541
+ new_files_count_query!&.first&.first
542
+ end
543
+
484
544
 
485
545
  # Filesystem-related helpers
486
546
 
547
+ def realpath_with_disk path
548
+ path = path.realpath
549
+ if Gem.win_platform? && path.to_s[0] == "/"
550
+ return Pathname(Dir.pwd[0, 2] + path.to_s)
551
+ end
552
+ path
553
+ end
554
+
555
+ def realdirpath_with_disk path
556
+ path = path.realdirpath
557
+ if Gem.win_platform? && path.to_s[0] == "/"
558
+ return Pathname(Dir.pwd[0, 2] + path.to_s)
559
+ end
560
+ path
561
+ end
562
+
487
563
  def patch_path_string path
488
564
  Gem.win_platform? ? path.gsub(/\\/, "/") : path
489
565
  end
@@ -502,10 +578,28 @@ class FileDigests
502
578
  end
503
579
  end
504
580
 
505
- def walk_files
506
- puts "Gathering the list of files..." if @options[:verbose]
507
- Dir.glob(@files_path + "**" + "*", File::FNM_DOTMATCH) do |filename|
508
- yield filename
581
+ def walk_files(path, &block)
582
+ Dir.each_child(path, encoding: "UTF-8") do |item|
583
+ item = "#{path}#{File::SEPARATOR}#{item}"
584
+ test_item = perhaps_nt_path item
585
+ if File.readable?(test_item)
586
+ if File.directory?(test_item)
587
+ walk_files(item, &block)
588
+ else
589
+ yield item
590
+ end
591
+ else
592
+ STDERR.puts "ERROR: Directory entry is not readable: #{item}"
593
+ @counters[:exceptions] += 1
594
+ end
595
+ end
596
+ end
597
+
598
+ def perhaps_nt_path path
599
+ if Gem.win_platform?
600
+ "\\??\\#{path.gsub(/\//,"\\")}"
601
+ else
602
+ path
509
603
  end
510
604
  end
511
605
 
@@ -519,18 +613,13 @@ class FileDigests
519
613
  digest.update(buffer)
520
614
  new_digest.update(buffer) if @new_digest_algorithm
521
615
  end
522
- @new_digests[digest.hexdigest] = new_digest.hexdigest if @new_digest_algorithm
523
- return digest.hexdigest
616
+ return [digest.hexdigest, (new_digest.hexdigest if @new_digest_algorithm)]
524
617
  end
525
618
  end
526
619
 
527
620
 
528
621
  # Runtime state helpers
529
622
 
530
- def any_missing_files?
531
- @missing_files.length > 0
532
- end
533
-
534
623
  def any_exceptions?
535
624
  @counters[:exceptions] > 0
536
625
  end
@@ -544,14 +633,17 @@ class FileDigests
544
633
  def confirm text
545
634
  if STDIN.tty? && STDOUT.tty?
546
635
  puts "#{text} (y/n)?"
547
- STDIN.gets.strip.downcase == "y"
636
+ start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
637
+ result = (STDIN.gets.strip.downcase == "y")
638
+ @user_input_wait_time += (Process.clock_gettime(Process::CLOCK_MONOTONIC) - start)
639
+ result
548
640
  end
549
641
  end
550
642
 
551
643
  def measure_time
552
644
  start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
553
645
  yield
554
- elapsed = (Process.clock_gettime(Process::CLOCK_MONOTONIC) - start)
646
+ elapsed = (Process.clock_gettime(Process::CLOCK_MONOTONIC) - start) - @user_input_wait_time
555
647
  puts "Elapsed time: #{elapsed.to_i / 3600}h #{(elapsed.to_i % 3600) / 60}m #{"%.3f" % (elapsed % 60)}s" unless @options[:quiet]
556
648
  end
557
649
 
@@ -569,11 +661,14 @@ class FileDigests
569
661
  end
570
662
 
571
663
  def print_counters
664
+ missing_files_count_result = missing_files_count
665
+ new_files_count_result = new_files_count - @counters[:renamed]
666
+
572
667
  puts "#{@counters[:good]} file(s) passes digest check" if @counters[:good] > 0
573
668
  puts "#{@counters[:updated]} file(s) are updated" if @counters[:updated] > 0
574
- puts "#{@counters[:new]} file(s) are new" if @counters[:new] > 0
669
+ puts "#{new_files_count_result} file(s) are new" if new_files_count_result > 0
575
670
  puts "#{@counters[:renamed]} file(s) are renamed" if @counters[:renamed] > 0
576
- puts "#{@missing_files.length} file(s) are missing" if @missing_files.length > 0
671
+ puts "#{missing_files_count_result} file(s) are missing" if missing_files_count_result > 0
577
672
  puts "#{@counters[:likely_damaged]} file(s) are likely damaged (!)" if @counters[:likely_damaged] > 0
578
673
  puts "#{@counters[:exceptions]} file(s) had exceptions occured during processing (!)" if @counters[:exceptions] > 0
579
674
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: file-digests
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.33
4
+ version: 0.0.38
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stanislav Senotrusov
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-10-15 00:00:00.000000000 Z
11
+ date: 2020-10-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: openssl