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.
- checksums.yaml +4 -4
- data/bin/file-digests +14 -0
- data/lib/file-digests.rb +200 -105
- 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: 1a7193a9795eb785cd22a7663fa4dae908d3349b854be8d3613750f24ce6b711
|
4
|
+
data.tar.gz: 32a8172cc7115240f5954b7e4775f841ddce10102c7e48e184690d2643e7a476
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 584a6fc4c10e6a33d9e55d9647abcfce913fa18b046eddf7193807408d7ac4167b1b7114aa300531426e767668841f483ad66775bf7f492ff552f290d19f6e2a
|
7
|
+
data.tar.gz: 877dea6a6ba8f3ab091ed6d5b0379090a60cff4477ad9d4409fa9fef9674bd39b9134682ac534d49535a21ed58382b02088fa00f83524d31add7c23919e9c0c4
|
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,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
|
-
|
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",
|
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
|
-
|
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 :
|
191
|
-
prepare_method :
|
192
|
-
prepare_method :
|
193
|
-
prepare_method :
|
194
|
-
prepare_method :
|
195
|
-
prepare_method :
|
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", "
|
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")
|
216
|
-
|
217
|
-
|
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
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
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
|
-
|
252
|
+
execute "INSERT INTO missing_files (filename, digest) SELECT filename, digest FROM digests"
|
229
253
|
|
230
|
-
|
231
|
-
|
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
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
280
|
+
nested_transaction do
|
281
|
+
puts "Tracking renames..." if @options[:verbose]
|
282
|
+
track_renames
|
283
|
+
end
|
240
284
|
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
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
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
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
|
-
|
269
|
-
|
270
|
-
|
310
|
+
if any_likely_damaged? || any_exceptions?
|
311
|
+
STDERR.puts "PLEASE REVIEW ERRORS THAT WERE OCCURRED!"
|
312
|
+
end
|
271
313
|
|
272
|
-
|
314
|
+
set_metadata(@options[:test_only] ? "latest_test_only_check_time" : "latest_complete_check_time", time_to_database(Time.now))
|
273
315
|
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
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
|
-
|
324
|
+
hide_database_files
|
325
|
+
end
|
283
326
|
end
|
284
327
|
|
285
328
|
def show_duplicates
|
286
329
|
current_digest = nil
|
287
|
-
|
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
|
-
|
343
|
+
perhaps_nt_filename = perhaps_nt_path filename
|
344
|
+
|
345
|
+
return if File.symlink? perhaps_nt_filename
|
301
346
|
|
302
|
-
stat = File.stat
|
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(
|
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
|
-
|
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
|
-
|
391
|
+
digests_touch_check_time found["id"]
|
346
392
|
else
|
347
|
-
|
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
|
-
|
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
|
-
|
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
|
-
@
|
378
|
-
|
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
|
-
|
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
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
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 =
|
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
|
-
|
507
|
-
|
508
|
-
|
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
|
-
|
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
|
-
|
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 "#{
|
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 "#{
|
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.
|
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-
|
11
|
+
date: 2020-10-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: openssl
|