file-digests 0.0.31 → 0.0.36
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.
- checksums.yaml +4 -4
- data/bin/file-digests +14 -0
- data/lib/file-digests.rb +164 -99
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 624f7ec80a0cb15be2bc07a3dd592c33c7a178c22fb7f59ef326760d17cc564f
|
|
4
|
+
data.tar.gz: 92bb88c58bc152ad01649b0688861ea16af6801d2d6625c238b96e9ff00aaed4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9dbca39af63f07ddfe4d92618d0ff5e0813fa30561d42b629caa918f664f1149f8ad895e8555137b0f462eb4eac942adda07efed03859d17cb635a8d9bb25b57
|
|
7
|
+
data.tar.gz: 788d37c5d82e8892dfcfb6f4a8532f484f7cf17c5f937e8a64b09af902fe3a904ed034fc88d403852bbd8819a9d11ab61a672e2d6f3114420ed7777ea9db2961
|
data/bin/file-digests
CHANGED
|
@@ -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
|
data/lib/file-digests.rb
CHANGED
|
@@ -1,6 +1,21 @@
|
|
|
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"
|
|
18
|
+
require "find"
|
|
4
19
|
require "openssl"
|
|
5
20
|
require "optparse"
|
|
6
21
|
require "pathname"
|
|
@@ -8,7 +23,8 @@ require "set"
|
|
|
8
23
|
require "sqlite3"
|
|
9
24
|
|
|
10
25
|
class FileDigests
|
|
11
|
-
|
|
26
|
+
VERSION = Gem.loaded_specs["file-digests"]&.version&.to_s
|
|
27
|
+
DIGEST_ALGORITHMS = ["BLAKE2b512", "SHA3-256", "SHA512-256"]
|
|
12
28
|
LEGACY_DIGEST_ALGORITHMS = ["SHA512", "SHA256"]
|
|
13
29
|
|
|
14
30
|
def self.canonical_digest_algorithm_name(string)
|
|
@@ -75,7 +91,12 @@ class FileDigests
|
|
|
75
91
|
options[:quiet] = true
|
|
76
92
|
end
|
|
77
93
|
|
|
78
|
-
opts.on(
|
|
94
|
+
opts.on(
|
|
95
|
+
"-t", "--test",
|
|
96
|
+
"Perform a test to verify directory contents.",
|
|
97
|
+
"Compare actual files with the stored digests, check if any files are missing.",
|
|
98
|
+
"Digest database will not be modified."
|
|
99
|
+
) do
|
|
79
100
|
options[:test_only] = true
|
|
80
101
|
end
|
|
81
102
|
|
|
@@ -96,6 +117,7 @@ class FileDigests
|
|
|
96
117
|
|
|
97
118
|
def initialize files_path, digest_database_path, options = {}
|
|
98
119
|
@options = options
|
|
120
|
+
@user_input_wait_time = 0
|
|
99
121
|
|
|
100
122
|
initialize_paths files_path, digest_database_path
|
|
101
123
|
initialize_database
|
|
@@ -139,8 +161,6 @@ class FileDigests
|
|
|
139
161
|
@db.results_as_hash = true
|
|
140
162
|
@db.busy_timeout = 5000
|
|
141
163
|
|
|
142
|
-
file_digests_gem_version = Gem.loaded_specs["file-digests"]&.version&.to_s
|
|
143
|
-
|
|
144
164
|
execute "PRAGMA encoding = 'UTF-8'"
|
|
145
165
|
execute "PRAGMA locking_mode = 'EXCLUSIVE'"
|
|
146
166
|
execute "PRAGMA journal_mode = 'WAL'"
|
|
@@ -155,14 +175,13 @@ class FileDigests
|
|
|
155
175
|
execute "CREATE TABLE metadata (
|
|
156
176
|
key TEXT NOT NULL PRIMARY KEY,
|
|
157
177
|
value TEXT)"
|
|
158
|
-
execute "CREATE UNIQUE INDEX metadata_key ON metadata(key)"
|
|
159
178
|
metadata_table_was_created = true
|
|
160
179
|
end
|
|
161
180
|
|
|
162
181
|
prepare_method :set_metadata_query, "INSERT INTO metadata (key, value) VALUES (?, ?) ON CONFLICT (key) DO UPDATE SET value=excluded.value"
|
|
163
182
|
prepare_method :get_metadata_query, "SELECT value FROM metadata WHERE key = ?"
|
|
164
183
|
|
|
165
|
-
set_metadata("metadata_table_created_by_gem_version",
|
|
184
|
+
set_metadata("metadata_table_created_by_gem_version", FileDigests::VERSION) if FileDigests::VERSION && metadata_table_was_created
|
|
166
185
|
|
|
167
186
|
# Heuristic to detect database version 1 (metadata was not stored back then)
|
|
168
187
|
unless get_metadata("database_version")
|
|
@@ -179,20 +198,19 @@ class FileDigests
|
|
|
179
198
|
digest TEXT NOT NULL,
|
|
180
199
|
digest_check_time TEXT NOT NULL)"
|
|
181
200
|
execute "CREATE UNIQUE INDEX digests_filename ON digests(filename)"
|
|
182
|
-
|
|
201
|
+
execute "CREATE INDEX digests_digest ON digests(digest)"
|
|
202
|
+
set_metadata("digests_table_created_by_gem_version", FileDigests::VERSION) if FileDigests::VERSION
|
|
183
203
|
end
|
|
184
204
|
|
|
185
|
-
prepare_method :
|
|
186
|
-
prepare_method :
|
|
187
|
-
prepare_method :
|
|
188
|
-
prepare_method :
|
|
189
|
-
prepare_method :
|
|
190
|
-
prepare_method :
|
|
191
|
-
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;"
|
|
192
|
-
prepare_method :update_digest_to_new_digest, "UPDATE digests SET digest = ? WHERE digest = ?"
|
|
205
|
+
prepare_method :digests_insert, "INSERT INTO digests (filename, mtime, digest, digest_check_time) VALUES (?, ?, ?, datetime('now'))"
|
|
206
|
+
prepare_method :digests_find_by_filename_query, "SELECT id, mtime, digest FROM digests WHERE filename = ?"
|
|
207
|
+
prepare_method :digests_touch_check_time, "UPDATE digests SET digest_check_time = datetime('now') WHERE id = ?"
|
|
208
|
+
prepare_method :digests_update_mtime_and_digest, "UPDATE digests SET mtime = ?, digest = ?, digest_check_time = datetime('now') WHERE id = ?"
|
|
209
|
+
prepare_method :digests_update_mtime, "UPDATE digests SET mtime = ?, digest_check_time = datetime('now') WHERE id = ?"
|
|
210
|
+
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;"
|
|
193
211
|
|
|
194
212
|
unless get_metadata("database_version")
|
|
195
|
-
set_metadata "database_version", "
|
|
213
|
+
set_metadata "database_version", "3"
|
|
196
214
|
end
|
|
197
215
|
|
|
198
216
|
# Convert database from 1st to 2nd version
|
|
@@ -207,79 +225,110 @@ class FileDigests
|
|
|
207
225
|
end
|
|
208
226
|
end
|
|
209
227
|
|
|
210
|
-
if get_metadata("database_version")
|
|
211
|
-
|
|
212
|
-
|
|
228
|
+
if get_metadata("database_version") == "2"
|
|
229
|
+
execute "CREATE INDEX digests_digest ON digests(digest)"
|
|
230
|
+
set_metadata "database_version", "3"
|
|
213
231
|
end
|
|
232
|
+
|
|
233
|
+
check_if_database_is_at_certain_version "3"
|
|
234
|
+
|
|
235
|
+
create_temporary_tables
|
|
214
236
|
end
|
|
215
237
|
end
|
|
216
238
|
|
|
217
|
-
def
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
239
|
+
def create_temporary_tables
|
|
240
|
+
execute "CREATE TEMPORARY TABLE new_files (
|
|
241
|
+
filename TEXT NOT NULL PRIMARY KEY,
|
|
242
|
+
digest TEXT NOT NULL)"
|
|
243
|
+
execute "CREATE INDEX new_files_digest ON new_files(digest)"
|
|
244
|
+
|
|
245
|
+
prepare_method :new_files_insert, "INSERT INTO new_files (filename, digest) VALUES (?, ?)"
|
|
246
|
+
prepare_method :new_files_count_query, "SELECT count(*) FROM new_files"
|
|
247
|
+
|
|
248
|
+
execute "CREATE TEMPORARY TABLE missing_files (
|
|
249
|
+
filename TEXT NOT NULL PRIMARY KEY,
|
|
250
|
+
digest TEXT NOT NULL)"
|
|
251
|
+
execute "CREATE INDEX missing_files_digest ON missing_files(digest)"
|
|
252
|
+
|
|
253
|
+
execute "INSERT INTO missing_files (filename, digest) SELECT filename, digest FROM digests"
|
|
254
|
+
|
|
255
|
+
prepare_method :missing_files_delete, "DELETE FROM missing_files WHERE filename = ?"
|
|
256
|
+
prepare_method :missing_files_delete_renamed_files, "DELETE FROM missing_files WHERE digest IN (SELECT digest FROM new_files)"
|
|
257
|
+
prepare_method :missing_files_select_all_filenames, "SELECT filename FROM missing_files ORDER BY filename"
|
|
258
|
+
prepare_method :missing_files_delete_all, "DELETE FROM missing_files"
|
|
259
|
+
prepare_method :missing_files_count_query, "SELECT count(*) FROM missing_files"
|
|
260
|
+
|
|
261
|
+
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))"
|
|
262
|
+
prepare_method :digests_delete_all_missing_files, "DELETE FROM digests WHERE filename IN (SELECT filename FROM missing_files)"
|
|
263
|
+
|
|
264
|
+
execute "CREATE TEMPORARY TABLE new_digests (
|
|
265
|
+
filename TEXT NOT NULL PRIMARY KEY,
|
|
266
|
+
digest TEXT NOT NULL)"
|
|
267
|
+
|
|
268
|
+
prepare_method :new_digests_insert, "INSERT INTO new_digests (filename, digest) VALUES (?, ?)"
|
|
269
|
+
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"
|
|
270
|
+
end
|
|
222
271
|
|
|
223
|
-
|
|
272
|
+
def perform_check
|
|
273
|
+
measure_time do
|
|
274
|
+
perhaps_transaction(@new_digest_algorithm, :exclusive) do
|
|
275
|
+
@counters = {good: 0, updated: 0, renamed: 0, likely_damaged: 0, exceptions: 0}
|
|
224
276
|
|
|
225
|
-
measure_time do
|
|
226
277
|
walk_files do |filename|
|
|
227
278
|
process_file filename
|
|
228
279
|
end
|
|
229
|
-
end
|
|
230
280
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
281
|
+
nested_transaction do
|
|
282
|
+
puts "Tracking renames..." if @options[:verbose]
|
|
283
|
+
track_renames
|
|
284
|
+
end
|
|
235
285
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
286
|
+
if any_missing_files?
|
|
287
|
+
if any_exceptions?
|
|
288
|
+
STDERR.puts "Due to previously occurred errors, missing files will not removed from the database."
|
|
289
|
+
else
|
|
290
|
+
print_missing_files
|
|
291
|
+
if !@options[:test_only] && (@options[:auto] || confirm("Remove missing files from the database"))
|
|
292
|
+
nested_transaction do
|
|
293
|
+
puts "Removing missing files..." if @options[:verbose]
|
|
294
|
+
remove_missing_files
|
|
295
|
+
end
|
|
245
296
|
end
|
|
246
297
|
end
|
|
247
298
|
end
|
|
248
|
-
end
|
|
249
299
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
300
|
+
if @new_digest_algorithm && !@options[:test_only]
|
|
301
|
+
if any_missing_files? || any_likely_damaged? || any_exceptions?
|
|
302
|
+
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."
|
|
303
|
+
else
|
|
304
|
+
puts "Updating database to a new digest algorithm..." if @options[:verbose]
|
|
305
|
+
digests_update_digests_to_new_digests
|
|
306
|
+
set_metadata "digest_algorithm", @new_digest_algorithm
|
|
307
|
+
puts "Transition to a new digest algorithm complete: #{@new_digest_algorithm}"
|
|
257
308
|
end
|
|
258
|
-
set_metadata "digest_algorithm", @new_digest_algorithm
|
|
259
|
-
puts "Transition to a new digest algorithm complete: #{@new_digest_algorithm}"
|
|
260
309
|
end
|
|
261
|
-
end
|
|
262
310
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
311
|
+
if any_likely_damaged? || any_exceptions?
|
|
312
|
+
STDERR.puts "PLEASE REVIEW ERRORS THAT WERE OCCURRED!"
|
|
313
|
+
end
|
|
266
314
|
|
|
267
|
-
|
|
315
|
+
set_metadata(@options[:test_only] ? "latest_test_only_check_time" : "latest_complete_check_time", time_to_database(Time.now))
|
|
268
316
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
317
|
+
print_counters
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
puts "Performing database maintenance..." if @options[:verbose]
|
|
321
|
+
execute "PRAGMA optimize"
|
|
322
|
+
execute "VACUUM"
|
|
323
|
+
execute "PRAGMA wal_checkpoint(TRUNCATE)"
|
|
276
324
|
|
|
277
|
-
|
|
325
|
+
hide_database_files
|
|
326
|
+
end
|
|
278
327
|
end
|
|
279
328
|
|
|
280
329
|
def show_duplicates
|
|
281
330
|
current_digest = nil
|
|
282
|
-
|
|
331
|
+
digests_select_duplicates.each do |found|
|
|
283
332
|
if current_digest != found["digest"]
|
|
284
333
|
puts "" if current_digest
|
|
285
334
|
current_digest = found["digest"]
|
|
@@ -311,9 +360,10 @@ class FileDigests
|
|
|
311
360
|
|
|
312
361
|
normalized_filename = filename.delete_prefix("#{@files_path.to_s}/").encode("utf-8", universal_newline: true).unicode_normalize(:nfkc)
|
|
313
362
|
mtime_string = time_to_database stat.mtime
|
|
314
|
-
digest = get_file_digest(filename)
|
|
363
|
+
digest, new_digest = get_file_digest(filename)
|
|
315
364
|
|
|
316
365
|
nested_transaction do
|
|
366
|
+
new_digests_insert(normalized_filename, new_digest) if new_digest
|
|
317
367
|
process_file_indeed normalized_filename, mtime_string, digest
|
|
318
368
|
end
|
|
319
369
|
|
|
@@ -331,15 +381,15 @@ class FileDigests
|
|
|
331
381
|
end
|
|
332
382
|
|
|
333
383
|
def process_previously_seen_file found, filename, mtime, digest
|
|
334
|
-
|
|
384
|
+
missing_files_delete filename
|
|
335
385
|
if found["digest"] == digest
|
|
336
386
|
@counters[:good] += 1
|
|
337
387
|
puts "GOOD: #{filename}" if @options[:verbose]
|
|
338
388
|
unless @options[:test_only]
|
|
339
389
|
if found["mtime"] == mtime
|
|
340
|
-
|
|
390
|
+
digests_touch_check_time found["id"]
|
|
341
391
|
else
|
|
342
|
-
|
|
392
|
+
digests_update_mtime mtime, found["id"]
|
|
343
393
|
end
|
|
344
394
|
end
|
|
345
395
|
else
|
|
@@ -350,18 +400,17 @@ class FileDigests
|
|
|
350
400
|
@counters[:updated] += 1
|
|
351
401
|
puts "UPDATED#{" (FATE ACCEPTED)" if found["mtime"] == mtime && @options[:accept_fate]}: #{filename}" unless @options[:quiet]
|
|
352
402
|
unless @options[:test_only]
|
|
353
|
-
|
|
403
|
+
digests_update_mtime_and_digest mtime, digest, found["id"]
|
|
354
404
|
end
|
|
355
405
|
end
|
|
356
406
|
end
|
|
357
407
|
end
|
|
358
408
|
|
|
359
409
|
def process_new_file filename, mtime, digest
|
|
360
|
-
@counters[:new] += 1
|
|
361
410
|
puts "NEW: #{filename}" unless @options[:quiet]
|
|
411
|
+
new_files_insert filename, digest
|
|
362
412
|
unless @options[:test_only]
|
|
363
|
-
|
|
364
|
-
insert filename, mtime, digest
|
|
413
|
+
digests_insert filename, mtime, digest
|
|
365
414
|
end
|
|
366
415
|
end
|
|
367
416
|
|
|
@@ -369,29 +418,31 @@ class FileDigests
|
|
|
369
418
|
# Renames and missing files
|
|
370
419
|
|
|
371
420
|
def track_renames
|
|
372
|
-
@
|
|
373
|
-
|
|
374
|
-
@counters[:renamed] += 1
|
|
375
|
-
unless @options[:test_only]
|
|
376
|
-
delete_by_filename filename
|
|
377
|
-
end
|
|
378
|
-
true
|
|
379
|
-
end
|
|
421
|
+
unless @options[:test_only]
|
|
422
|
+
digests_delete_renamed_files
|
|
380
423
|
end
|
|
424
|
+
missing_files_delete_renamed_files
|
|
425
|
+
@counters[:renamed] = @db.changes
|
|
381
426
|
end
|
|
382
427
|
|
|
383
428
|
def print_missing_files
|
|
384
429
|
puts "\nMISSING FILES:"
|
|
385
|
-
|
|
386
|
-
puts filename
|
|
430
|
+
missing_files_select_all_filenames.each do |record|
|
|
431
|
+
puts record["filename"]
|
|
387
432
|
end
|
|
388
433
|
end
|
|
389
434
|
|
|
390
435
|
def remove_missing_files
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
436
|
+
digests_delete_all_missing_files
|
|
437
|
+
missing_files_delete_all
|
|
438
|
+
end
|
|
439
|
+
|
|
440
|
+
def missing_files_count
|
|
441
|
+
missing_files_count_query!&.first&.first
|
|
442
|
+
end
|
|
443
|
+
|
|
444
|
+
def any_missing_files?
|
|
445
|
+
missing_files_count > 0
|
|
395
446
|
end
|
|
396
447
|
|
|
397
448
|
|
|
@@ -402,6 +453,7 @@ class FileDigests
|
|
|
402
453
|
end
|
|
403
454
|
|
|
404
455
|
def integrity_check
|
|
456
|
+
puts "Checking database integrity..." if @options[:verbose]
|
|
405
457
|
if execute("PRAGMA integrity_check")&.first&.fetch("integrity_check") != "ok"
|
|
406
458
|
raise "Database integrity check failed"
|
|
407
459
|
end
|
|
@@ -456,7 +508,7 @@ class FileDigests
|
|
|
456
508
|
end
|
|
457
509
|
|
|
458
510
|
def find_by_filename filename
|
|
459
|
-
result =
|
|
511
|
+
result = digests_find_by_filename_query filename
|
|
460
512
|
found = result.next
|
|
461
513
|
raise "Multiple records found" if result.next
|
|
462
514
|
found
|
|
@@ -476,6 +528,18 @@ class FileDigests
|
|
|
476
528
|
end
|
|
477
529
|
end
|
|
478
530
|
|
|
531
|
+
def check_if_database_is_at_certain_version target_version
|
|
532
|
+
current_version = get_metadata("database_version")
|
|
533
|
+
if current_version != target_version
|
|
534
|
+
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."
|
|
535
|
+
raise "Incompatible database version"
|
|
536
|
+
end
|
|
537
|
+
end
|
|
538
|
+
|
|
539
|
+
def new_files_count
|
|
540
|
+
new_files_count_query!&.first&.first
|
|
541
|
+
end
|
|
542
|
+
|
|
479
543
|
|
|
480
544
|
# Filesystem-related helpers
|
|
481
545
|
|
|
@@ -498,8 +562,8 @@ class FileDigests
|
|
|
498
562
|
end
|
|
499
563
|
|
|
500
564
|
def walk_files
|
|
501
|
-
|
|
502
|
-
yield
|
|
565
|
+
Find.find(@files_path) do |path|
|
|
566
|
+
yield path
|
|
503
567
|
end
|
|
504
568
|
end
|
|
505
569
|
|
|
@@ -513,18 +577,13 @@ class FileDigests
|
|
|
513
577
|
digest.update(buffer)
|
|
514
578
|
new_digest.update(buffer) if @new_digest_algorithm
|
|
515
579
|
end
|
|
516
|
-
|
|
517
|
-
return digest.hexdigest
|
|
580
|
+
return [digest.hexdigest, (new_digest.hexdigest if @new_digest_algorithm)]
|
|
518
581
|
end
|
|
519
582
|
end
|
|
520
583
|
|
|
521
584
|
|
|
522
585
|
# Runtime state helpers
|
|
523
586
|
|
|
524
|
-
def any_missing_files?
|
|
525
|
-
@missing_files.length > 0
|
|
526
|
-
end
|
|
527
|
-
|
|
528
587
|
def any_exceptions?
|
|
529
588
|
@counters[:exceptions] > 0
|
|
530
589
|
end
|
|
@@ -538,14 +597,17 @@ class FileDigests
|
|
|
538
597
|
def confirm text
|
|
539
598
|
if STDIN.tty? && STDOUT.tty?
|
|
540
599
|
puts "#{text} (y/n)?"
|
|
541
|
-
|
|
600
|
+
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
601
|
+
result = (STDIN.gets.strip.downcase == "y")
|
|
602
|
+
@user_input_wait_time += (Process.clock_gettime(Process::CLOCK_MONOTONIC) - start)
|
|
603
|
+
result
|
|
542
604
|
end
|
|
543
605
|
end
|
|
544
606
|
|
|
545
607
|
def measure_time
|
|
546
608
|
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
547
609
|
yield
|
|
548
|
-
elapsed = (Process.clock_gettime(Process::CLOCK_MONOTONIC) - start)
|
|
610
|
+
elapsed = (Process.clock_gettime(Process::CLOCK_MONOTONIC) - start) - @user_input_wait_time
|
|
549
611
|
puts "Elapsed time: #{elapsed.to_i / 3600}h #{(elapsed.to_i % 3600) / 60}m #{"%.3f" % (elapsed % 60)}s" unless @options[:quiet]
|
|
550
612
|
end
|
|
551
613
|
|
|
@@ -563,11 +625,14 @@ class FileDigests
|
|
|
563
625
|
end
|
|
564
626
|
|
|
565
627
|
def print_counters
|
|
628
|
+
missing_files_count_result = missing_files_count
|
|
629
|
+
new_files_count_result = new_files_count - @counters[:renamed]
|
|
630
|
+
|
|
566
631
|
puts "#{@counters[:good]} file(s) passes digest check" if @counters[:good] > 0
|
|
567
632
|
puts "#{@counters[:updated]} file(s) are updated" if @counters[:updated] > 0
|
|
568
|
-
puts "#{
|
|
633
|
+
puts "#{new_files_count_result} file(s) are new" if new_files_count_result > 0
|
|
569
634
|
puts "#{@counters[:renamed]} file(s) are renamed" if @counters[:renamed] > 0
|
|
570
|
-
puts "#{
|
|
635
|
+
puts "#{missing_files_count_result} file(s) are missing" if missing_files_count_result > 0
|
|
571
636
|
puts "#{@counters[:likely_damaged]} file(s) are likely damaged (!)" if @counters[:likely_damaged] > 0
|
|
572
637
|
puts "#{@counters[:exceptions]} file(s) had exceptions occured during processing (!)" if @counters[:exceptions] > 0
|
|
573
638
|
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.
|
|
4
|
+
version: 0.0.36
|
|
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-
|
|
11
|
+
date: 2020-10-17 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: openssl
|