151 lines
4.1 KiB
Python
151 lines
4.1 KiB
Python
import os
|
|
import sys
|
|
import time
|
|
import argparse
|
|
import queue
|
|
import threading
|
|
from chkbit import IndexThread, Stat
|
|
|
|
STATUS_CODES = """
|
|
Status codes:
|
|
DMG: error, data damage detected
|
|
EIX: error, index damaged
|
|
old: warning, file replaced by an older version
|
|
new: new file
|
|
upd: file updated
|
|
ok : check ok
|
|
skp: skipped (see .chkbitignore)
|
|
EXC: internal exception
|
|
"""
|
|
|
|
|
|
class Main:
|
|
def __init__(self):
|
|
self.stdscr = None
|
|
self.dmg_list = []
|
|
self.err_list = []
|
|
self.modified = False
|
|
self.verbose = False
|
|
self.total = 0
|
|
self._parse_args()
|
|
|
|
def _log(self, idx, stat, path):
|
|
|
|
if stat == Stat.FLAG_MOD:
|
|
self.modified = True
|
|
else:
|
|
if stat == Stat.ERR_DMG:
|
|
self.dmg_list.append(path)
|
|
elif stat == Stat.INTERNALEXCEPTION:
|
|
self.err_list.append(path)
|
|
elif stat in [Stat.OK, Stat.UPDATE, Stat.NEW]:
|
|
self.total += 1
|
|
if self.verbose or not stat in [Stat.OK, Stat.SKIP]:
|
|
print(stat.value, path)
|
|
if not self.quiet and sys.stdout.isatty():
|
|
print(self.total, end="\r")
|
|
|
|
def _parse_args(self):
|
|
parser = argparse.ArgumentParser(
|
|
description="Checks the data integrity of your files. See https://github.com/laktak/chkbit-py",
|
|
epilog=STATUS_CODES,
|
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
)
|
|
parser.add_argument("PATH", nargs="*")
|
|
|
|
parser.add_argument(
|
|
"-u",
|
|
"--update",
|
|
action="store_true",
|
|
help="update indices (without this chkbit will only verify files)",
|
|
)
|
|
|
|
parser.add_argument(
|
|
"-f", "--force", action="store_true", help="force update of damaged items"
|
|
)
|
|
|
|
parser.add_argument(
|
|
"-i",
|
|
"--verify-index",
|
|
action="store_true",
|
|
help="verify files in the index only (will not report new files)",
|
|
)
|
|
|
|
parser.add_argument(
|
|
"-q",
|
|
"--quiet",
|
|
action="store_true",
|
|
help="quiet, don't show progress/information",
|
|
)
|
|
parser.add_argument(
|
|
"-v", "--verbose", action="store_true", help="verbose output"
|
|
)
|
|
|
|
self.args = parser.parse_args()
|
|
self.verbose = self.args.verbose
|
|
self.quiet = self.args.quiet
|
|
if not self.args.PATH:
|
|
parser.print_help()
|
|
|
|
def _res_worker(self):
|
|
while True:
|
|
item = self.res_queue.get()
|
|
if not item:
|
|
break
|
|
self._log(*item)
|
|
self.res_queue.task_done()
|
|
|
|
def process(self):
|
|
|
|
self.res_queue = queue.Queue()
|
|
todo_queue = queue.Queue()
|
|
|
|
for path in self.args.PATH:
|
|
todo_queue.put(path)
|
|
|
|
workers = [
|
|
IndexThread(idx, self.args, self.res_queue, todo_queue) for idx in range(5)
|
|
]
|
|
|
|
res_worker = threading.Thread(target=self._res_worker)
|
|
res_worker.daemon = True
|
|
res_worker.start()
|
|
|
|
todo_queue.join()
|
|
self.res_queue.join()
|
|
|
|
def print_result(self):
|
|
if not self.quiet:
|
|
print(
|
|
f"Processed {self.total} file(s){' in readonly mode' if not self.args.update else ''}."
|
|
)
|
|
if self.modified:
|
|
print("Indices were updated.")
|
|
|
|
if self.dmg_list:
|
|
print("chkbit detected damage in these files:", file=sys.stderr)
|
|
for err in self.dmg_list:
|
|
print(err, file=sys.stderr)
|
|
print(
|
|
f"error: detected {len(self.dmg_list)} file(s) with damage!",
|
|
file=sys.stderr,
|
|
)
|
|
if self.err_list:
|
|
print("chkbit ran into errors:", file=sys.stderr)
|
|
for err in self.err_list:
|
|
print(err, file=sys.stderr)
|
|
|
|
if self.dmg_list or self.err_list:
|
|
sys.exit(1)
|
|
|
|
|
|
def main():
|
|
try:
|
|
m = Main()
|
|
if m.args.PATH:
|
|
m.process()
|
|
m.print_result()
|
|
except KeyboardInterrupt:
|
|
print("abort")
|
|
sys.exit(1)
|