import argparse
import datetime
import os
import re
import shutil
import sys
from itertools import zip_longest
from subprocess import Popen, PIPE, STDOUT
class FileSystem:
def __init__(self):
self.home = os.environ['HOME']
self.dates = dict()
def remove_dir(self, path):
path = self.append_path(path)
try:
shutil.rmtree(path)
print(f'Removed dir: {path}')
except FileNotFoundError:
pass
def create_dir(self, mode, size, date, path, link=None):
path = self.append_path(path)
if link:
link = self.append_path(link)
try:
self.create_dir(mode, size, date, link)
os.symlink(link, path)
except FileExistsError:
pass
else:
if date:
self.dates[path] = date
try:
os.mkdir(path)
print(f'Created dir: {path}')
except FileExistsError:
pass
os.chmod(path, mode)
def create_file(self, mode, size, date, path, link=None):
path = self.append_path(path)
if link:
link = self.append_path(link)
try:
self.create_file(mode, size, date, link)
os.symlink(link, path)
except FileExistsError:
pass
else:
if date:
self.dates[path] = date
with open(path, 'wb') as file:
data = bytes(size)
file.write(data)
print(f'Created file: {path}')
os.chmod(path, mode)
def append_path(self, path):
return os.path.join(self.home, path)
def set_times(self):
for path, date in reversed(self.dates.items()):
date = datetime.date.fromisoformat(date)
time = datetime.datetime.combine(date, datetime.datetime.min.time())
time = time.timestamp()
os.utime(path, (time, time))
class FileModifier:
def __init__(self):
self.program = os.path.join(os.path.dirname(__file__), 'target', 'debug', 'ex')
def modify_paths(self, paths):
if paths:
for path in paths:
temp = f'{path}.___'
with open(path, 'r') as reader:
with open(temp, 'w') as writer:
dirty = self.modify_stream(reader, writer)
if dirty:
print(path)
os.rename(temp, path)
else:
os.remove(temp)
else:
self.modify_stream(sys.stdin, sys.stdout)
def modify_stream(self, reader, writer):
dirty = False
quoted = False
for line in reader:
print(line, end='', file=writer)
if quoted:
if match := re.search(r'^(?:~/(\w+) \$|C:\\Users\\username\\(\w+)>) ex(?:\.exe)? ?(.*)$', line):
linux, windows, args = match.groups()
args = args.split()
if 'xargs' not in args:
if linux:
dirty |= self.modify_chunk(reader, writer, linux, args, False)
if windows:
dirty |= self.modify_chunk(reader, writer, windows, args, True)
quoted = False
else:
if re.search(r'^```$', line):
quoted = True
return dirty
def modify_chunk(self, reader, writer, directory, args, windows):
dirty = False
olds = self.read_existing(reader)
news = self.read_process(directory, args, windows)
for old, new in zip_longest(olds, news):
if new is not None:
print(new, end='', file=writer)
dirty |= (new != old)
elif old is not None:
dirty = True
print('```', file=writer)
return dirty
@staticmethod
def read_existing(reader):
for line in reader:
if re.search(r'^```$', line):
break
yield line
def read_process(self, directory, args, windows):
if self.modify_args(args):
windows = True
command = [self.program, '--now', '2024-01-01T00:00:00Z']
command.extend(args)
cwd = os.path.expanduser(f'~/{directory}')
user = os.environ['USER']
regex = re.compile(rf'\b{user}\b')
trans = str.maketrans('\u251c\u2514\u2500\u2502', '+\\-|')
process = Popen(command, stdout=PIPE, stderr=STDOUT, cwd=cwd, text=True)
for line in process.stdout:
line = regex.sub('username', line)
line = line.translate(trans)
if windows:
line = re.sub(r'^([dl-])([r-][w-][x-]).{6}', r'\1\2\2\2', line)
line = re.sub(r'/', r'\\', line)
line = re.sub(r'\\home\b', r'C:\\Users', line)
yield line
@staticmethod
def modify_args(args):
windows = False
try:
index = args.index('|')
del args[index:]
except ValueError:
args.append('--terminal')
for index in range(len(args)):
if match := re.search(r'^-(\w*)w(\w*)$', args[index]):
prefix, suffix = match.groups()
args[index] = f'-{prefix}{suffix}'
windows = True
return windows
def parse_args():
parser = argparse.ArgumentParser()
parser.add_argument('-c', '--create', action='store_true', help='create example directories')
parser.add_argument('-r', '--remove', action='store_true', help='remove example directories')
parser.add_argument('paths', nargs='*', default=[], help='file to modify')
return parser.parse_args()
def run_main():
settings = parse_args()
if settings.create:
system = FileSystem()
system.remove_dir('example')
system.remove_dir('numbers')
system.remove_dir('ordered')
system.create_dir(0o755, 0, None, 'example')
system.create_dir(0o755, 0, None, 'numbers')
system.create_dir(0o755, 0, None, 'ordered')
system.create_file(0o744, 10, '2023-11-01', 'example/find.sh')
system.create_dir(0o755, 0, '2023-12-31', 'example/.hidden')
system.create_file(0o744, 15, '2023-12-31', 'example/.hidden/password.dat')
system.create_file(0o744, 15, '2023-12-31', 'example/.hidden/secret.dat')
system.create_dir(0o755, 0, '2023-12-31', 'example/files')
system.create_dir(0o755, 0, '2023-12-31', 'example/files/colours')
system.create_file(0o744, 20, '2023-10-01', 'example/files/colours/alpha.sh')
system.create_file(0o644, 30, '2023-09-01', 'example/files/colours/blue.txt')
system.create_file(0o644, 40, '2023-08-01', 'example/files/colours/green.txt')
system.create_file(0o644, 50, '2023-07-01', 'example/files/colours/red.txt')
system.create_dir(0o755, 0, '2023-12-31', 'example/files/numbers')
system.create_file(0o744, 60, '2023-06-01', 'example/files/numbers/count.sh', 'numbers/count.sh')
system.create_file(0o644, 999999, '2023-05-01', 'example/files/numbers/googolplex.gz', 'numbers/googolplex.gz')
system.create_dir(0o644, 0, '2023-04-01', 'example/files/numbers/ordinals', 'numbers/ordinals')
system.create_dir(0o755, 0, '2023-12-31', 'example/files/numbers/one two')
system.create_file(0o644, 70, '2023-03-01', 'example/files/numbers/one two/"three" \'four\'.txt')
system.create_file(0o664, 0, '2023-01-01', 'ordered/file8.txt')
system.create_file(0o664, 0, '2023-01-01', 'ordered/file9.txt')
system.create_file(0o664, 0, '2023-01-01', 'ordered/file10.txt')
system.create_file(0o664, 0, '2023-01-01', 'ordered/file11.txt')
system.create_file(0o664, 0, '2023-01-01', 'ordered/file98.txt')
system.create_file(0o664, 0, '2023-01-01', 'ordered/file99.txt')
system.create_file(0o664, 0, '2023-01-01', 'ordered/file100.txt')
system.create_file(0o664, 0, '2023-01-01', 'ordered/file101.txt')
system.set_times()
elif settings.remove:
system = FileSystem()
system.remove_dir('example')
system.remove_dir('numbers')
system.remove_dir('ordered')
else:
modifier = FileModifier()
modifier.modify_paths(settings.paths)
try:
run_main()
except OSError as error:
print(error)
except KeyboardInterrupt:
pass