version based on SQLite 3
This commit is contained in:
parent
b244bdd4d9
commit
6d56beaacc
@ -13,8 +13,8 @@ Go to the desired directory and simply invoke::
|
|||||||
$ bitrot
|
$ bitrot
|
||||||
|
|
||||||
This will start digging through your directory structure recursively indexing
|
This will start digging through your directory structure recursively indexing
|
||||||
all files found. The index is stored in a ``.bitrot.db`` file which is a DBM
|
all files found. The index is stored in a ``.bitrot.db`` file which is a SQLite
|
||||||
database.
|
3 database.
|
||||||
|
|
||||||
Next time you run ``bitrot`` it will add new files and update the index for
|
Next time you run ``bitrot`` it will add new files and update the index for
|
||||||
files with a changed modification date. Most importantly however, it will
|
files with a changed modification date. Most importantly however, it will
|
||||||
|
@ -28,9 +28,9 @@ from __future__ import unicode_literals
|
|||||||
|
|
||||||
import atexit
|
import atexit
|
||||||
import datetime
|
import datetime
|
||||||
import dbm
|
|
||||||
import hashlib
|
import hashlib
|
||||||
import os
|
import os
|
||||||
|
import sqlite3
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
|
||||||
@ -38,6 +38,7 @@ CHUNK_SIZE = 16384
|
|||||||
DOT_THRESHOLD = 200
|
DOT_THRESHOLD = 200
|
||||||
VERSION = (0, 1, 0)
|
VERSION = (0, 1, 0)
|
||||||
|
|
||||||
|
|
||||||
def sha1(path):
|
def sha1(path):
|
||||||
digest = hashlib.sha1()
|
digest = hashlib.sha1()
|
||||||
with open(path) as f:
|
with open(path) as f:
|
||||||
@ -48,12 +49,24 @@ def sha1(path):
|
|||||||
return digest.hexdigest()
|
return digest.hexdigest()
|
||||||
|
|
||||||
|
|
||||||
|
def get_sqlite3_cursor(path):
|
||||||
|
conn = sqlite3.connect(path)
|
||||||
|
atexit.register(conn.close)
|
||||||
|
cur = conn.cursor()
|
||||||
|
for name, in cur.execute('SELECT name FROM sqlite_master'):
|
||||||
|
if name == 'bitrot':
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
cur.execute('CREATE TABLE bitrot (path TEXT PRIMARY KEY, '
|
||||||
|
'mtime INTEGER, hash TEXT, timestamp TEXT)')
|
||||||
|
return conn
|
||||||
|
|
||||||
|
|
||||||
def run():
|
def run():
|
||||||
current_dir = b'.' # sic, relative path
|
current_dir = b'.' # sic, relative path
|
||||||
bitrot_db = os.path.join(current_dir, b'.bitrot')
|
bitrot_db = os.path.join(current_dir, b'.bitrot.db')
|
||||||
db = dbm.open(bitrot_db, 'c')
|
conn = get_sqlite3_cursor(bitrot_db)
|
||||||
bitrot_db += b'.db'
|
cur = conn.cursor()
|
||||||
atexit.register(db.close)
|
|
||||||
new_count = 0
|
new_count = 0
|
||||||
update_count = 0
|
update_count = 0
|
||||||
error_count = 0
|
error_count = 0
|
||||||
@ -69,30 +82,39 @@ def run():
|
|||||||
continue
|
continue
|
||||||
new_mtime = int(os.stat(p).st_mtime)
|
new_mtime = int(os.stat(p).st_mtime)
|
||||||
new_sha1 = sha1(p)
|
new_sha1 = sha1(p)
|
||||||
try:
|
update_ts = datetime.datetime.utcnow().strftime(
|
||||||
stored_mtime, stored_sha1, update_ts = db[p].split(b' ')
|
"%Y-%m-%d %H:%M:%S%z"
|
||||||
if int(stored_mtime) != new_mtime:
|
)
|
||||||
new_count -= 1
|
p_uni = p.decode('utf8')
|
||||||
update_count += 1
|
cur.execute('SELECT mtime, hash, timestamp FROM bitrot WHERE '
|
||||||
raise KeyError("out of date")
|
'path=?', (p_uni,))
|
||||||
except (KeyError, ValueError):
|
row = cur.fetchone()
|
||||||
|
if not row:
|
||||||
new_count += 1
|
new_count += 1
|
||||||
update_ts = datetime.datetime.utcnow().strftime(
|
cur.execute('INSERT INTO bitrot VALUES (?, ?, ?, ?)',
|
||||||
"%Y-%m-%d\u00a0%H:%M:%S%z".encode('utf8')
|
(p_uni, new_mtime, new_sha1, update_ts))
|
||||||
|
conn.commit()
|
||||||
|
continue
|
||||||
|
stored_mtime, stored_sha1, update_ts = row
|
||||||
|
if int(stored_mtime) != new_mtime:
|
||||||
|
update_count += 1
|
||||||
|
cur.execute('UPDATE bitrot SET mtime=?, hash=?, timestamp=? '
|
||||||
|
'WHERE path=?',
|
||||||
|
(new_mtime, new_sha1, update_ts, p_uni))
|
||||||
|
conn.commit()
|
||||||
|
elif stored_sha1 != new_sha1:
|
||||||
|
error_count += 1
|
||||||
|
print("\rerror: SHA1 mismatch for {}: expected {}, got {}."
|
||||||
|
" Original info from {}.".format(
|
||||||
|
p, stored_sha1, new_sha1, update_ts
|
||||||
|
),
|
||||||
|
file=sys.stderr,
|
||||||
)
|
)
|
||||||
db[p] = b'{} {} {}'.format(new_mtime, new_sha1, update_ts)
|
cur.execute('SELECT COUNT(path) FROM bitrot')
|
||||||
else:
|
all_count = cur.fetchone()[0]
|
||||||
if stored_sha1 != new_sha1:
|
|
||||||
error_count += 1
|
|
||||||
print("\rerror: SHA1 mismatch for {}: expected {}, got {}."
|
|
||||||
" Original info from {}.".format(
|
|
||||||
p, stored_sha1, new_sha1, update_ts
|
|
||||||
),
|
|
||||||
file=sys.stderr,
|
|
||||||
)
|
|
||||||
print("\nFinished. {} errors found.".format(error_count))
|
print("\nFinished. {} errors found.".format(error_count))
|
||||||
print("{} entries in the database, {} new, {} updated.".format(
|
print("{} entries in the database, {} new, {} updated.".format(
|
||||||
len(db), new_count, update_count
|
all_count, new_count, update_count
|
||||||
))
|
))
|
||||||
if error_count:
|
if error_count:
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
Reference in New Issue
Block a user