Commit ff6e1bd7 authored by Xavier Thompson's avatar Xavier Thompson

Add main.pyx

parent 7d7f7233
# 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 sep = Str("/")
cdef char * file_fmt = """\
"{}": {{
"type": "file",
"sha256": "{}",
"sha512": "{}",
"stat": {}
}},
"""
cdef char * dir_fmt = """\
"{}": {{
"type": "dir",
"stat": {}
}},
"""
cdef char * symlink_fmt = """\
"{}": {{
"type": "symlink",
"target": "{}",
"stat": {}
}},
"""
cdef char * stat_fmt = """{{
"dev": {},
"ino": {},
"mode": {},
"nlink": {},
"uid": {},
"gid": {},
"rdev": {},
"size": {},
"blksize": {},
"blocks": {},
"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 write_node(self, FILE * stream): pass
Str to_json(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,
)
@staticmethod
iso Node node(iso Str path):
cdef Node node
p = <Str> consume path
s = os.stat(p)
if s is NULL:
return 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:
return 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:
path = self.path
if Str(path[-1]) != sep:
path = path + sep
path = path + name
child = Node.node(consume path)
if child is not NULL:
self.children.append(activate(consume child))
self.text = format(dir_fmt,
self.path,
self.to_json(),
)
for active_child in self.children:
active_child.build_node(NULL)
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 = False
cdef bint sha512_ok = False
file = os.open(self.path, 'rb')
if file is not NULL:
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:
sha256_ok = sha512_ok = False
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)
self.sha256 = sha256.hexdigest() if sha256_ok else NULL
self.sha512 = sha512.hexdigest() if sha512_ok else NULL
self.text = format(file_fmt,
self.path,
self.sha256 or Str("<error>"),
self.sha512 or Str("<error>"),
self.to_json(),
)
void write_node(self, FILE * stream):
os.write(self.text, stream)
cdef cypclass SymlinkNode(Node):
Str target
void build_node(self):
target = os.readlink(self.path, self.stat.st_size)
if target is NULL:
target = Str("<error>")
self.target = target
self.text = format(symlink_fmt,
self.path,
target,
self.to_json(),
)
void write_node(self, FILE * stream):
os.write(self.text, stream)
cdef int sign_file_tree(iso Str path) nogil:
root = Node.node(consume path)
if root is NULL:
return -1
root.build_node()
scheduler.join()
os.write(Str("{"), os.stdout)
root.write_node(os.stdout)
os.write(Str(' "": {}\n}'), os.stdout)
return 0
def main(s = b'.'):
cdef char * root = s
with nogil:
sign_file_tree(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