Commit 6f79ca87 authored by Xavier Thompson's avatar Xavier Thompson

Add scan.pyx with TOML output

parents
# distutils: language = c++
# distutils: libraries = fmt crypto
from runtime.runtime cimport Scheduler, BatchMailBox
from stdlib.list cimport List
from stdlib.string cimport Str
from stdlib.format cimport format
cimport stdlib.hashlib as hashlib
cimport stdlib.os as os
from stdlib.os cimport FILE, DIR, Stat
cdef Str curdir = Str(".")
cdef Str pardir = Str("..")
cdef Str sep = Str("/")
cdef char * file_fmt = """\
[{}]
sha256 = {}
sha512 = {}
{}
"""
cdef char * dir_fmt = """\
[{}]
{}
"""
cdef char * symlink_fmt = """\
[{}]
target = {}
{}
"""
cdef char * stat_fmt = """\
dev = {}
ino = {}
mode = {}
nlink = {}
owner = {{ uid = {}, gid = {} }}
rdev = {}
sizes = {{ size = {}, blksize = {}, blocks = {} }}
times = {{ atim = [{}, {}], mtim = [{}, {}], ctim = [{}, {}] }}"""
cdef lock Scheduler scheduler = Scheduler()
cdef cypclass Node activable:
Str path
Stat stat
Str text
__init__(self, Str path, Stat stat):
self._active_queue_class = consume BatchMailBox(scheduler)
self.path = path
self.stat = stat
void build_node(self): pass
void format_node(self): pass
Str format_stat(self):
return format(stat_fmt,
self.stat.st_dev,
self.stat.st_ino,
self.stat.st_mode,
self.stat.st_nlink,
self.stat.st_uid,
self.stat.st_gid,
self.stat.st_rdev,
self.stat.st_size,
self.stat.st_blksize,
self.stat.st_blocks,
self.stat.st_atim.tv_sec,
self.stat.st_atim.tv_nsec,
self.stat.st_mtim.tv_sec,
self.stat.st_mtim.tv_nsec,
self.stat.st_ctim.tv_sec,
self.stat.st_ctim.tv_nsec,
)
void write_node(self, FILE * stream): pass
@staticmethod
iso Node create(iso Str path):
cdef Node node
p = <Str> consume path
s = os.stat(p)
if s is NULL:
node = NULL
elif s.is_symlink():
node = SymlinkNode(p, s)
elif s.is_dir():
node = DirNode(p, s)
elif s.is_regular():
node = FileNode(p, s)
else:
node = NULL
del p, s
return consume node
cdef cypclass DirNode(Node):
ctypedef List[active Node] Children
Children children
__init__(self, Str path, Stat stat):
Node.__init__(self, path, stat)
self.children = Children()
void build_node(self):
entries = os.listdir(self.path)
if entries is not NULL:
for name in entries:
if name == curdir or name == pardir:
continue
path = self.path
if Str(path[-1]) != sep:
path = path + sep
path = path + name
child = Node.create(consume path)
if child is not NULL:
self.children.append(activate(consume child))
self.format_node()
for active_child in self.children:
active_child.build_node(NULL)
void format_node(self):
self.text = format(dir_fmt,
self.path,
self.format_stat(),
)
void write_node(self, FILE * stream):
os.write(self.text, stream)
while self.children.__len__() > 0:
active_child = self.children.pop()
child = consume active_child
child.write_node(stream)
cdef enum:
CHUNK = 64 * 1024
cdef cypclass FileNode(Node):
Str sha256
Str sha512
__init__(self, Str path, Stat stat):
Node.__init__(self, path, stat)
void build_node(self):
cdef bint sha256_ok
cdef bint sha512_ok
cdef bint error = False
cdef FILE * file = os.open(self.path, 'rb')
if file is NULL:
self.format_node()
return
sha256 = hashlib.Hash(hashlib.sha256())
sha512 = hashlib.Hash(hashlib.sha512())
sha256_ok = sha256 is not NULL
sha512_ok = sha512 is not NULL
while (sha256_ok or sha512_ok):
s = os.read(file, CHUNK)
if s is NULL:
error = True
break
if sha256_ok:
sha256_ok = sha256.update(s) == 0
if sha512_ok:
sha512_ok = sha512.update(s) == 0
if s.__len__() != CHUNK:
break
os.close(file)
if not error:
self.sha256 = sha256.hexdigest() if sha256_ok else NULL
self.sha512 = sha512.hexdigest() if sha512_ok else NULL
self.format_node()
void format_node(self):
sha256 = self.sha256 if self.sha256 else Str("<error>")
sha512 = self.sha512 if self.sha512 else Str("<error>")
self.text = format(file_fmt,
self.path,
sha256,
sha512,
self.format_stat(),
)
void write_node(self, FILE * stream):
os.write(self.text, stream)
cdef cypclass SymlinkNode(Node):
Str target
void build_node(self):
self.target = os.readlink(self.path, self.stat.st_size)
self.format_node()
void format_node(self):
target = self.target if self.target is not NULL else Str("<error>")
self.text = format(symlink_fmt,
self.path,
target,
self.format_stat(),
)
void write_node(self, FILE * stream):
os.write(self.text, stream)
cdef int scan(iso Str root) nogil:
node = Node.create(consume root)
if node is NULL:
return -1
active_node = activate(consume node)
active_node.build_node(NULL)
scheduler.join()
node = consume active_node
node.write_node(os.stdout)
return 0
def main(s = b'.'):
cdef char * root = s
with nogil:
scan(consume(Str(root)))
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment