file-digests 0.0.4 → 0.0.9

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e3c77b8e030df2ad0633b1a233986dce951a93f2653a4fb31c8a91881c83f3e1
4
- data.tar.gz: 2db1eb6db4cc08665a55ace46a737b524b6491ade9822eda4905a24aa9606680
3
+ metadata.gz: ee2508a2c4d70b5a4847f6403d74848db85145b181ff103c4b9dbc631ea03720
4
+ data.tar.gz: 7e0b68a73d34e54ba8b98b884f2184eda906ca9c11dac04d98607d8727ec1911
5
5
  SHA512:
6
- metadata.gz: 89b338536c8f6c026153e9eaa532d81530737484d6676e30fe0826b094c2c4ed3fdcbe176f373e43df7801ad13f5f528220a15ae9a5d7decedd91be4bc02415c
7
- data.tar.gz: 0fd05670f470308fbfb0b43416b3bebc4597c41950811193d1e571816d6a305a1e85ae562759990f5c39bbff3ded35c1b5ff81674cc312505e1718ba4ee6e788
6
+ metadata.gz: db27ac6edfbe7f6c243d4e2c54cf2c0d6419328e751151e6745bad97836881116ea5710490532cf87c1c4a5f6f50d7495b1ae05863f78b66436a20e5c83f7884
7
+ data.tar.gz: 3959e854c8b5539ba8e1882857730fd17f91ffb2f31252db070b31dcc69fb6fdf4ac02f99e2ce53ba1767085bada121840f2bb582f708ca6881128f92359cbb9
@@ -1,213 +1,8 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require 'date'
4
- require 'set'
5
- require 'digest'
6
- require 'fileutils'
7
- require 'pathname'
8
- require 'sqlite3'
3
+ require 'file-digests'
9
4
 
10
- def ensure_dir_exists path
11
- if File.exist?(path)
12
- unless File.directory?(path)
13
- raise "#{path} is not a directory"
14
- end
15
- else
16
- FileUtils.mkdir_p path
17
- end
18
- end
19
-
20
- def measure_time
21
- start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
22
- yield
23
- elapsed = (Process.clock_gettime(Process::CLOCK_MONOTONIC) - start).to_i
24
- puts "Elapsed time: #{elapsed / 3600}h #{(elapsed % 3600) / 60}m #{elapsed % 60}s" if VERBOSE
25
- end
26
-
27
- def patch_path_string path
28
- Gem.win_platform? ? path.gsub(/\\/, '/') : path
29
- end
30
-
31
- class DigestDatabase
32
- def initialize path
33
- @db = SQLite3::Database.new(path.to_s)
34
-
35
- unless @db.execute("SELECT name FROM sqlite_master WHERE type='table' AND name = 'digests'").length == 1
36
- @db.execute 'PRAGMA encoding = "UTF-8"'
37
- @db.execute "CREATE TABLE digests (
38
- id INTEGER PRIMARY KEY,
39
- filename TEXT,
40
- mtime TEXT,
41
- digest TEXT,
42
- digest_check_time TEXT)"
43
- @db.execute "CREATE UNIQUE INDEX digests_filename ON digests(filename)"
44
- end
45
-
46
- @db.results_as_hash = true
47
- @missing_files = Hash[@db.prepare("SELECT filename, digest FROM digests").execute!]
48
- @new_files = {}
49
-
50
-
51
- @insert = @db.prepare("INSERT INTO digests (filename, mtime, digest, digest_check_time) VALUES (?, ?, ?, datetime('now'))")
52
- @find_by_filename = @db.prepare("SELECT id, mtime, digest FROM digests WHERE filename = ?")
53
- @touch_digest_check_time = @db.prepare("UPDATE digests SET digest_check_time = datetime('now') WHERE id = ?")
54
- @update_mtime_and_digest = @db.prepare("UPDATE digests SET mtime = ?, digest = ?, digest_check_time = datetime('now') WHERE id = ?")
55
- @update_mtime = @db.prepare("UPDATE digests SET mtime = ?, digest_check_time = datetime('now') WHERE id = ?")
56
- @delete_by_filename = @db.prepare("DELETE FROM digests WHERE filename = ?")
57
- end
58
-
59
- def insert_or_update file_path, mtime, digest
60
- result = @find_by_filename.execute file_path
61
-
62
- if found = result.next_hash
63
- raise "Multiple records found" if result.next
64
-
65
- @missing_files.delete(file_path)
66
-
67
- if found['digest'] == digest
68
- COUNTS[:good] += 1
69
- puts "GOOD: #{file_path}" if VERBOSE
70
- unless TEST_ONLY
71
- if found['mtime'] == mtime
72
- @touch_digest_check_time.execute found['id']
73
- else
74
- @update_mtime.execute mtime, found['id']
75
- end
76
- end
77
- else
78
- if found['mtime'] == mtime # Digest is different and mtime is the same
79
- COUNTS[:likely_damaged] += 1
80
- puts "LIKELY DAMAGED: #{file_path}"
81
- else
82
- COUNTS[:updated] += 1
83
- puts "UPDATED: #{file_path}" if VERBOSE || TEST_ONLY
84
- unless TEST_ONLY
85
- @update_mtime_and_digest.execute mtime, digest, found['id']
86
- end
87
- end
88
- end
89
- else
90
- COUNTS[:new] += 1
91
- puts "NEW: #{file_path}" if VERBOSE || TEST_ONLY
92
- unless TEST_ONLY
93
- @new_files[file_path] = digest
94
- @insert.execute! file_path, mtime, digest
95
- end
96
- end
97
- end
98
-
99
- def process_missing_files
100
- @missing_files.delete_if do |filename, digest|
101
- if @new_files.value?(digest)
102
- COUNTS[:renamed] += 1
103
- unless TEST_ONLY
104
- @delete_by_filename.execute filename
105
- end
106
- true
107
- end
108
- end
109
-
110
- if (COUNTS[:missing] = @missing_files.length) > 0
111
- puts "MISSING FILES:"
112
- @missing_files.sort.to_h.each do |filename, digest|
113
- puts filename
114
- end
115
- unless TEST_ONLY
116
- puts "Remove missing files from the database (y/n)?"
117
- if STDIN.gets.strip == "y"
118
- @missing_files.each do |filename, digest|
119
- @delete_by_filename.execute filename
120
- end
121
- end
122
- end
123
- end
124
- end
125
- end
126
-
127
- class Checker
128
- def initialize files_path, digest_database_path
129
- @files_path = files_path
130
- ensure_dir_exists @files_path
131
-
132
- if digest_database_path
133
- @digest_database_path = digest_database_path
134
- ensure_dir_exists @digest_database_path.dirname
135
- else
136
- @digest_database_path = @files_path + '.file-digests.sqlite'
137
- @skip_file_digests_sqlite = true
138
- end
139
-
140
- @digest_database = DigestDatabase.new @digest_database_path
141
- end
142
-
143
- def check
144
- walk_files do |filename|
145
- begin
146
- process_file filename
147
- rescue => exception
148
- COUNTS[:exceptions] += 1
149
- STDERR.puts "EXCEPTION: #{filename}: #{exception.message}"
150
- end
151
- end
152
-
153
- @digest_database.process_missing_files
154
- end
155
-
156
- def walk_files
157
- Dir.glob(@files_path + '**' + '*', File::FNM_DOTMATCH) do |filename|
158
- next unless File.file? filename
159
- next if @skip_file_digests_sqlite && filename == '.file-digests.sqlite'
160
- yield filename
161
- end
162
- end
163
-
164
- def process_file filename
165
- @digest_database.insert_or_update(
166
- filename.delete_prefix(@files_path.to_s + '/'),
167
- File.mtime(filename).utc.strftime('%Y-%m-%d %H:%M:%S'),
168
- get_file_digest(filename)
169
- )
170
- end
171
-
172
- def get_file_digest filename
173
- File.open(filename, 'rb') do |io|
174
- digest = Digest::SHA512.new
175
- buffer = ""
176
- while io.read(40960, buffer)
177
- digest.update(buffer)
178
- end
179
- return digest.hexdigest
180
- end
181
- end
182
-
183
- end
184
-
185
- VERBOSE = (ENV["VERBOSE"] == "true")
5
+ QUIET = (ENV["QUIET"] == "true")
186
6
  TEST_ONLY = (ENV["TEST_ONLY"] == "true")
187
7
 
188
- COUNTS = {good: 0, updated: 0, new: 0, missing: 0, renamed: 0, likely_damaged: 0, exceptions: 0}
189
-
190
- begin
191
- if ARGV[0]
192
- files_path = Pathname.new patch_path_string(ARGV[0])
193
- else
194
- files_path = Pathname.new patch_path_string(".")
195
- end
196
-
197
- digest_database_path = Pathname.new patch_path_string(ARGV[1]) if ARGV[1]
198
-
199
- measure_time do
200
- checker = Checker.new files_path, digest_database_path
201
- checker.check
202
- end
203
-
204
- if COUNTS[:likely_damaged] > 0 || COUNTS[:exceptions] > 0
205
- puts "ERRORS WERE OCCURRED, PLEASE CHECK FOR THEM"
206
- end
207
-
208
- puts COUNTS.inspect
209
-
210
- rescue => exception
211
- STDERR.puts "EXCEPTION: #{exception.message}"
212
- raise exception
213
- end
8
+ FileDigests.perform_check
@@ -1,213 +1,8 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require 'date'
4
- require 'set'
5
- require 'digest'
6
- require 'fileutils'
7
- require 'pathname'
8
- require 'sqlite3'
3
+ require 'file-digests'
9
4
 
10
- def ensure_dir_exists path
11
- if File.exist?(path)
12
- unless File.directory?(path)
13
- raise "#{path} is not a directory"
14
- end
15
- else
16
- FileUtils.mkdir_p path
17
- end
18
- end
19
-
20
- def measure_time
21
- start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
22
- yield
23
- elapsed = (Process.clock_gettime(Process::CLOCK_MONOTONIC) - start).to_i
24
- puts "Elapsed time: #{elapsed / 3600}h #{(elapsed % 3600) / 60}m #{elapsed % 60}s" if VERBOSE
25
- end
26
-
27
- def patch_path_string path
28
- Gem.win_platform? ? path.gsub(/\\/, '/') : path
29
- end
30
-
31
- class DigestDatabase
32
- def initialize path
33
- @db = SQLite3::Database.new(path.to_s)
34
-
35
- unless @db.execute("SELECT name FROM sqlite_master WHERE type='table' AND name = 'digests'").length == 1
36
- @db.execute 'PRAGMA encoding = "UTF-8"'
37
- @db.execute "CREATE TABLE digests (
38
- id INTEGER PRIMARY KEY,
39
- filename TEXT,
40
- mtime TEXT,
41
- digest TEXT,
42
- digest_check_time TEXT)"
43
- @db.execute "CREATE UNIQUE INDEX digests_filename ON digests(filename)"
44
- end
45
-
46
- @db.results_as_hash = true
47
- @missing_files = Hash[@db.prepare("SELECT filename, digest FROM digests").execute!]
48
- @new_files = {}
49
-
50
-
51
- @insert = @db.prepare("INSERT INTO digests (filename, mtime, digest, digest_check_time) VALUES (?, ?, ?, datetime('now'))")
52
- @find_by_filename = @db.prepare("SELECT id, mtime, digest FROM digests WHERE filename = ?")
53
- @touch_digest_check_time = @db.prepare("UPDATE digests SET digest_check_time = datetime('now') WHERE id = ?")
54
- @update_mtime_and_digest = @db.prepare("UPDATE digests SET mtime = ?, digest = ?, digest_check_time = datetime('now') WHERE id = ?")
55
- @update_mtime = @db.prepare("UPDATE digests SET mtime = ?, digest_check_time = datetime('now') WHERE id = ?")
56
- @delete_by_filename = @db.prepare("DELETE FROM digests WHERE filename = ?")
57
- end
58
-
59
- def insert_or_update file_path, mtime, digest
60
- result = @find_by_filename.execute file_path
61
-
62
- if found = result.next_hash
63
- raise "Multiple records found" if result.next
64
-
65
- @missing_files.delete(file_path)
66
-
67
- if found['digest'] == digest
68
- COUNTS[:good] += 1
69
- puts "GOOD: #{file_path}" if VERBOSE
70
- unless TEST_ONLY
71
- if found['mtime'] == mtime
72
- @touch_digest_check_time.execute found['id']
73
- else
74
- @update_mtime.execute mtime, found['id']
75
- end
76
- end
77
- else
78
- if found['mtime'] == mtime # Digest is different and mtime is the same
79
- COUNTS[:likely_damaged] += 1
80
- puts "LIKELY DAMAGED: #{file_path}"
81
- else
82
- COUNTS[:updated] += 1
83
- puts "UPDATED: #{file_path}" if VERBOSE || TEST_ONLY
84
- unless TEST_ONLY
85
- @update_mtime_and_digest.execute mtime, digest, found['id']
86
- end
87
- end
88
- end
89
- else
90
- COUNTS[:new] += 1
91
- puts "NEW: #{file_path}" if VERBOSE || TEST_ONLY
92
- unless TEST_ONLY
93
- @new_files[file_path] = digest
94
- @insert.execute! file_path, mtime, digest
95
- end
96
- end
97
- end
98
-
99
- def process_missing_files
100
- @missing_files.delete_if do |filename, digest|
101
- if @new_files.value?(digest)
102
- COUNTS[:renamed] += 1
103
- unless TEST_ONLY
104
- @delete_by_filename.execute filename
105
- end
106
- true
107
- end
108
- end
109
-
110
- if (COUNTS[:missing] = @missing_files.length) > 0
111
- puts "MISSING FILES:"
112
- @missing_files.sort.to_h.each do |filename, digest|
113
- puts filename
114
- end
115
- unless TEST_ONLY
116
- puts "Remove missing files from the database (y/n)?"
117
- if STDIN.gets.strip == "y"
118
- @missing_files.each do |filename, digest|
119
- @delete_by_filename.execute filename
120
- end
121
- end
122
- end
123
- end
124
- end
125
- end
126
-
127
- class Checker
128
- def initialize files_path, digest_database_path
129
- @files_path = files_path
130
- ensure_dir_exists @files_path
131
-
132
- if digest_database_path
133
- @digest_database_path = digest_database_path
134
- ensure_dir_exists @digest_database_path.dirname
135
- else
136
- @digest_database_path = @files_path + '.file-digests.sqlite'
137
- @skip_file_digests_sqlite = true
138
- end
139
-
140
- @digest_database = DigestDatabase.new @digest_database_path
141
- end
142
-
143
- def check
144
- walk_files do |filename|
145
- begin
146
- process_file filename
147
- rescue => exception
148
- COUNTS[:exceptions] += 1
149
- STDERR.puts "EXCEPTION: #{filename}: #{exception.message}"
150
- end
151
- end
152
-
153
- @digest_database.process_missing_files
154
- end
155
-
156
- def walk_files
157
- Dir.glob(@files_path + '**' + '*', File::FNM_DOTMATCH) do |filename|
158
- next unless File.file? filename
159
- next if @skip_file_digests_sqlite && filename == '.file-digests.sqlite'
160
- yield filename
161
- end
162
- end
163
-
164
- def process_file filename
165
- @digest_database.insert_or_update(
166
- filename.delete_prefix(@files_path.to_s + '/'),
167
- File.mtime(filename).utc.strftime('%Y-%m-%d %H:%M:%S'),
168
- get_file_digest(filename)
169
- )
170
- end
171
-
172
- def get_file_digest filename
173
- File.open(filename, 'rb') do |io|
174
- digest = Digest::SHA512.new
175
- buffer = ""
176
- while io.read(40960, buffer)
177
- digest.update(buffer)
178
- end
179
- return digest.hexdigest
180
- end
181
- end
182
-
183
- end
184
-
185
- VERBOSE = (ENV["VERBOSE"] == "true")
5
+ QUIET = (ENV["QUIET"] == "true")
186
6
  TEST_ONLY = true
187
7
 
188
- COUNTS = {good: 0, updated: 0, new: 0, missing: 0, renamed: 0, likely_damaged: 0, exceptions: 0}
189
-
190
- begin
191
- if ARGV[0]
192
- files_path = Pathname.new patch_path_string(ARGV[0])
193
- else
194
- files_path = Pathname.new patch_path_string(".")
195
- end
196
-
197
- digest_database_path = Pathname.new patch_path_string(ARGV[1]) if ARGV[1]
198
-
199
- measure_time do
200
- checker = Checker.new files_path, digest_database_path
201
- checker.check
202
- end
203
-
204
- if COUNTS[:likely_damaged] > 0 || COUNTS[:exceptions] > 0
205
- puts "ERRORS WERE OCCURRED, PLEASE CHECK FOR THEM"
206
- end
207
-
208
- puts COUNTS.inspect
209
-
210
- rescue => exception
211
- STDERR.puts "EXCEPTION: #{exception.message}"
212
- raise exception
213
- end
8
+ FileDigests.perform_check
@@ -0,0 +1,237 @@
1
+
2
+ require 'date'
3
+ require 'set'
4
+ require 'digest'
5
+ require 'fileutils'
6
+ require 'pathname'
7
+ require 'sqlite3'
8
+
9
+ module FileDigests
10
+
11
+ def self.ensure_dir_exists path
12
+ if File.exist?(path)
13
+ unless File.directory?(path)
14
+ raise "#{path} is not a directory"
15
+ end
16
+ else
17
+ FileUtils.mkdir_p path
18
+ end
19
+ end
20
+
21
+ def self.measure_time
22
+ start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
23
+ yield
24
+ elapsed = (Process.clock_gettime(Process::CLOCK_MONOTONIC) - start).to_i
25
+ puts "Elapsed time: #{elapsed / 3600}h #{(elapsed % 3600) / 60}m #{elapsed % 60}s" unless QUIET
26
+ end
27
+
28
+ def self.patch_path_string path
29
+ Gem.win_platform? ? path.gsub(/\\/, '/') : path
30
+ end
31
+
32
+ def self.perform_check
33
+ files_path = Pathname.new patch_path_string(ARGV[0] || ".")
34
+ digest_database_path = Pathname.new patch_path_string(ARGV[1]) if ARGV[1]
35
+ checker = Checker.new files_path, digest_database_path
36
+ checker.check
37
+ end
38
+
39
+ class DigestDatabase
40
+ def initialize path
41
+ @db = SQLite3::Database.new path.to_s
42
+ @db.results_as_hash = true
43
+
44
+ execute 'PRAGMA journal_mode = "WAL"'
45
+ execute 'PRAGMA synchronous = "NORMAL"'
46
+ execute 'PRAGMA locking_mode = "EXCLUSIVE"'
47
+ execute 'PRAGMA cache_size = "5000"'
48
+
49
+ unless execute("SELECT name FROM sqlite_master WHERE type='table' AND name = 'digests'").length == 1
50
+ execute 'PRAGMA encoding = "UTF-8"'
51
+ execute "CREATE TABLE digests (
52
+ id INTEGER PRIMARY KEY,
53
+ filename TEXT,
54
+ mtime TEXT,
55
+ digest TEXT,
56
+ digest_check_time TEXT)"
57
+ execute "CREATE UNIQUE INDEX digests_filename ON digests(filename)"
58
+ end
59
+
60
+ @missing_files = Hash[@db.prepare("SELECT filename, digest FROM digests").execute!]
61
+ @new_files = {}
62
+
63
+ prepare_method :insert, "INSERT INTO digests (filename, mtime, digest, digest_check_time) VALUES (?, ?, ?, datetime('now'))"
64
+ prepare_method :find_by_filename, "SELECT id, mtime, digest FROM digests WHERE filename = ?"
65
+ prepare_method :touch_digest_check_time, "UPDATE digests SET digest_check_time = datetime('now') WHERE id = ?"
66
+ prepare_method :update_mtime_and_digest, "UPDATE digests SET mtime = ?, digest = ?, digest_check_time = datetime('now') WHERE id = ?"
67
+ prepare_method :update_mtime, "UPDATE digests SET mtime = ?, digest_check_time = datetime('now') WHERE id = ?"
68
+ prepare_method :delete_by_filename, "DELETE FROM digests WHERE filename = ?"
69
+ end
70
+
71
+ def insert_or_update file_path, mtime, digest, counters
72
+ result = find_by_filename file_path
73
+
74
+ if found = result.next_hash
75
+ raise "Multiple records found" if result.next
76
+
77
+ @missing_files.delete(file_path)
78
+
79
+ if found['digest'] == digest
80
+ counters[:good] += 1
81
+ # puts "GOOD: #{file_path}" unless QUIET
82
+ unless TEST_ONLY
83
+ if found['mtime'] == mtime
84
+ touch_digest_check_time found['id']
85
+ else
86
+ update_mtime mtime, found['id']
87
+ end
88
+ end
89
+ else
90
+ if found['mtime'] == mtime # Digest is different and mtime is the same
91
+ counters[:likely_damaged] += 1
92
+ STDERR.puts "LIKELY DAMAGED: #{file_path}"
93
+ else
94
+ counters[:updated] += 1
95
+ puts "UPDATED: #{file_path}" unless QUIET
96
+ unless TEST_ONLY
97
+ update_mtime_and_digest mtime, digest, found['id']
98
+ end
99
+ end
100
+ end
101
+ else
102
+ counters[:new] += 1
103
+ puts "NEW: #{file_path}" unless QUIET
104
+ unless TEST_ONLY
105
+ @new_files[file_path] = digest
106
+ insert file_path, mtime, digest
107
+ end
108
+ end
109
+ end
110
+
111
+ def process_missing_files counters
112
+ @missing_files.delete_if do |filename, digest|
113
+ if @new_files.value?(digest)
114
+ counters[:renamed] += 1
115
+ unless TEST_ONLY
116
+ delete_by_filename filename
117
+ end
118
+ true
119
+ end
120
+ end
121
+
122
+ if (counters[:missing] = @missing_files.length) > 0
123
+ puts "\nMISSING FILES:"
124
+ @missing_files.sort.to_h.each do |filename, digest|
125
+ puts filename
126
+ end
127
+ unless TEST_ONLY
128
+ puts "Remove missing files from the database (y/n)?"
129
+ if STDIN.gets.strip.downcase == "y"
130
+ @db.transaction do
131
+ @missing_files.each do |filename, digest|
132
+ delete_by_filename filename
133
+ end
134
+ end
135
+ end
136
+ end
137
+ end
138
+ end
139
+
140
+ private
141
+
142
+ def execute *args, &block
143
+ @db.execute *args, &block
144
+ end
145
+
146
+ def prepare_method name, query
147
+ variable = "@#{name}"
148
+ instance_variable_set(variable, @db.prepare(query))
149
+ define_singleton_method name do |*args, &block|
150
+ instance_variable_get(variable).execute(*args, &block)
151
+ end
152
+ end
153
+ end
154
+
155
+ class Checker
156
+ def initialize files_path, digest_database_path
157
+ @counters = {good: 0, updated: 0, new: 0, missing: 0, renamed: 0, likely_damaged: 0, exceptions: 0}
158
+ @files_path = files_path
159
+ @prefix_to_remove = @files_path.to_s + '/'
160
+
161
+ unless digest_database_path
162
+ digest_database_path = @files_path + '.file-digests.sqlite'
163
+ @skip_file_digests_sqlite = true
164
+ end
165
+
166
+ FileDigests::ensure_dir_exists @files_path
167
+ FileDigests::ensure_dir_exists digest_database_path.dirname
168
+
169
+ @digest_database = DigestDatabase.new digest_database_path
170
+ end
171
+
172
+ def check
173
+ FileDigests::measure_time do
174
+ walk_files do |filename|
175
+ process_file filename
176
+ end
177
+ end
178
+
179
+ @digest_database.process_missing_files @counters
180
+
181
+ if @counters[:likely_damaged] > 0 || @counters[:exceptions] > 0
182
+ STDERR.puts "ERRORS WERE OCCURRED"
183
+ end
184
+
185
+ puts @counters.inspect
186
+ end
187
+
188
+ def walk_files
189
+ Dir.glob(@files_path + '**' + '*', File::FNM_DOTMATCH) do |filename|
190
+ yield filename
191
+ end
192
+ end
193
+
194
+ def process_file filename
195
+ return if File.symlink? filename
196
+
197
+ stat = File.stat filename
198
+
199
+ return if stat.blockdev?
200
+ return if stat.chardev?
201
+ return if stat.directory?
202
+ return if stat.pipe?
203
+ unless stat.readable?
204
+ raise "File is not readable"
205
+ end
206
+ return if stat.socket?
207
+
208
+ if @skip_file_digests_sqlite
209
+ return if filename == '.file-digests.sqlite'
210
+ return if filename == '.file-digests.sqlite-wal'
211
+ return if filename == '.file-digests.sqlite-shm'
212
+ end
213
+
214
+ @digest_database.insert_or_update(
215
+ filename.delete_prefix(@prefix_to_remove).unicode_normalize(:nfkc),
216
+ stat.mtime.utc.strftime('%Y-%m-%d %H:%M:%S'),
217
+ get_file_digest(filename),
218
+ @counters
219
+ )
220
+ rescue => exception
221
+ @counters[:exceptions] += 1
222
+ STDERR.puts "EXCEPTION: #{filename}: #{exception.message}"
223
+ end
224
+
225
+ def get_file_digest filename
226
+ File.open(filename, 'rb') do |io|
227
+ digest = Digest::SHA512.new
228
+ buffer = ""
229
+ while io.read(40960, buffer)
230
+ digest.update(buffer)
231
+ end
232
+ return digest.hexdigest
233
+ end
234
+ end
235
+
236
+ end
237
+ end
metadata CHANGED
@@ -1,11 +1,11 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: file-digests
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stanislav Senotrusov
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
  date: 2020-10-08 00:00:00.000000000 Z
@@ -34,11 +34,12 @@ extra_rdoc_files: []
34
34
  files:
35
35
  - bin/file-digests
36
36
  - bin/file-digests-test
37
+ - lib/file-digests.rb
37
38
  homepage: https://github.com/senotrusov/file-digests
38
39
  licenses:
39
40
  - Apache-2.0
40
41
  metadata: {}
41
- post_install_message:
42
+ post_install_message:
42
43
  rdoc_options: []
43
44
  require_paths:
44
45
  - lib
@@ -54,7 +55,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
54
55
  version: '0'
55
56
  requirements: []
56
57
  rubygems_version: 3.1.2
57
- signing_key:
58
+ signing_key:
58
59
  specification_version: 4
59
60
  summary: file-digests
60
61
  test_files: []