From 05067532e2ee3ffa74c3c249a86c5eaa51cea683 Mon Sep 17 00:00:00 2001 From: stephentoth Date: Thu, 7 Mar 2024 02:04:53 -0600 Subject: [PATCH] restructured data in use --- ls.py | 121 ++++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 75 insertions(+), 46 deletions(-) diff --git a/ls.py b/ls.py index f6358a1..42afb3d 100755 --- a/ls.py +++ b/ls.py @@ -8,6 +8,7 @@ import argparse from enum import Enum import subprocess import functools +from collections import namedtuple # ANSI escape codes for colors COLORS = { @@ -34,21 +35,26 @@ class Filetype(Enum): S_IFIFO = 0o010000 ERR = 0 -def colorize(text, color): +Permissions = namedtuple("Permissions", "mode type char color prettybits") + +FileString = namedtuple("FileString", ["path", "file", "file_color", + "path_color", "colored_path", + "colored_file", "colored_filepath", + "link_sep", "link_dest", + "link_dest_full", "link_color", + "colored_link_dest" + ], defaults=['','','','','']) + +def colorwrap(text, color): return f"{COLORS[color]}{text}{COLORS['RESET']}" - -def gen_color_file_string(filepath, mode): - path, file = os.path.split(filepath) - perms = parse_permissions(mode) - color = perms['color'] - filestring = f"{path and colorize(path+'/', 'DIM')}{colorize(file, color)}" - # dircolors var? - if perms['char'] == 'l': - pointfile = os.readlink(filepath) - pointfilereal = os.path.join(os.path.dirname(filepath), os.readlink(filepath)) - filestring += ' -> ' + colorize(pointfile, "GREEN" if os.path.exists(pointfilereal) else "RED" ) - return filestring +def sizeof_fmt(num): + for unit in ("", "K", "M", "G", "T", "P", "E", "Z"): + if abs(num) < 1024.0: + if unit == "": return f"{num}" #exception to not do .0 for bytes + return f"{num:3.1f}{unit}" + num /= 1024.0 + return f"{num:.1f}Y" def parse_permissions(mode): @@ -98,15 +104,35 @@ def parse_permissions(mode): "x" if mode & 0o001 else "-" ]) - return {'mode':mode, 'type': filetype, 'char': char, 'color': color, 'prettybits': prettybits } + #return {'mode':mode, 'type': filetype, 'char': char, 'color': color, 'prettybits': prettybits } + return Permissions(mode, filetype, char, color, prettybits) + + + +def file_string_gen(filepath, mode): + path, file = os.path.split(filepath) + perms = parse_permissions(mode) + file_color = perms.color + path_color = 'DIM' + + colored_path = colorwrap(path+'/', path_color) if path else '' + colored_file = colorwrap(file, file_color) + colored_filepath = colored_path + colored_file + + # links + if perms.type == Filetype.S_IFLNK: + link_sep = ' -> ' + link_dest = os.readlink(filepath) + link_dest_real = os.path.join(os.path.dirname(filepath), os.readlink(filepath)) + link_color = "GREEN" if os.path.exists(link_dest_real) else "RED" + colored_link_dest = colorwrap(link_dest, link_color) + return FileString(path, file, file_color, path_color, colored_path, colored_file, colored_filepath, link_sep, link_dest, link_dest_real, link_color, colored_link_dest) + + return FileString(path, file, file_color, path_color, colored_path, colored_file, colored_filepath) + + + -def sizeof_fmt(num): - for unit in ("", "K", "M", "G", "T", "P", "E", "Z"): - if abs(num) < 1024.0: - if unit == "": return f"{num}" #exception to not do .0 for bytes - return f"{num:3.1f}{unit}" - num /= 1024.0 - return f"{num:.1f}Y" def is_path_git_repo_dir(path, mode): #I hate having to take in the mode here, but it makes it easier for usage @@ -120,9 +146,9 @@ def git_gud(path): mods = len([x for x in lines if x[0] == 'M']) unt = len([x for x in lines if x[0] == '?']) ding = '' - ding += colorize('!'+str(mods), "YELLOW") if mods > 0 else '' + ding += colorwrap('!'+str(mods), "YELLOW") if mods > 0 else '' ding += ' ' if mods > 0 and unt > 0 else '' - ding += colorize('?'+str(unt), "CYAN") if unt > 0 else '' + ding += colorwrap('?'+str(unt), "CYAN") if unt > 0 else '' return ding # output = subprocess.check_output(['git', 'rev-list', '--count', '--left-only', '@{u}...HEAD'], cwd=file_path, stderr=subprocess.DEVNULL) @@ -145,58 +171,55 @@ def list_files(directory='.', args={}): #get file metadata + FileMeta = namedtuple("FileMeta", "name rel_file_path abs_file_path stats") def get_file_metadata(path, name): rel_file_path = os.path.join(path, name) # for rel paths abs_file_path = os.path.abspath(rel_file_path) # for full path stats = os.lstat(rel_file_path) - return { - "name": name, - "rel_file_path": rel_file_path, - "abs_file_path": abs_file_path, - "stats": stats - } + return FileMeta(name, rel_file_path, abs_file_path, stats) + files = [ get_file_metadata(directory, name) for name in os.listdir(directory) ] - longest_size_length = len(str(functools.reduce(lambda a, b: a if len(str(a['stats'].st_size)) > len(str(b['stats'].st_size)) else b, files)['stats'].st_size)) + longest_size_length = len(str(functools.reduce(lambda a, b: a if len(str(a.stats.st_size)) > len(str(b.stats.st_size)) else b, files).stats.st_size)) print(f"files: {len(files)}") # Sort stuff if args.time_sorted: - files.sort(key=lambda f: f["stats"].st_mtime, reverse=(not args.reverse)) + files.sort(key=lambda f: f.stats.st_mtime, reverse=(not args.reverse)) elif args.size_sorted: - files.sort(key=lambda f: f["stats"].st_size, reverse=(not args.reverse)) + files.sort(key=lambda f: f.stats.st_size, reverse=(not args.reverse)) else: - files.sort(key=lambda f:f["name"], reverse=(args.reverse)) + files.sort(key=lambda f:f.name, reverse=(args.reverse)) # filter out hidden if not specify -a if not args.all: - files = [file for file in files if not file['name'].startswith('.')] + files = [file for file in files if not file.name.startswith('.')] if not args.long: for file in files: - colored_file = gen_color_file_string(file["abs_file_path"], file["stats"].st_mode) + colored_file = gen_color_file_string(file.abs_file_path, file.stats.st_mode) # rel is broken print(colored_file) return for file in files: - size = file['stats'].st_size - user = pwd.getpwuid(file['stats'].st_uid).pw_name - group = grp.getgrgid(file['stats'].st_gid).gr_name - permissions = parse_permissions(file['stats'].st_mode) - nlinks = file['stats'].st_nlink + size = file.stats.st_size + user = pwd.getpwuid(file.stats.st_uid).pw_name + group = grp.getgrgid(file.stats.st_gid).gr_name + permissions = parse_permissions(file.stats.st_mode) + nlinks = file.stats.st_nlink - perm_prettybits = permissions['prettybits'] - timestring = time.strftime("%b %d %H:%M", time.localtime(file["stats"].st_mtime)) + perm_prettybits = permissions.prettybits + timestring = time.strftime("%b %d %H:%M", time.localtime(file.stats.st_mtime)) if args.sparse: if perm_prettybits == 'l': perm_prettybits = 'l ' - if permissions['type'] == Filetype.S_IFDIR: + if permissions.type == Filetype.S_IFDIR: size="" if args.human: @@ -206,11 +229,17 @@ def list_files(directory='.', args={}): timestring = time.strftime("%b %d %Y %X %z", time.localtime(file["stats"].st_mtime)) # -0400 #mtime = time.strftime("%b %d %X %Z", time.localtime(stats.st_mtime)) # CST - colored_file = gen_color_file_string(file["abs_file_path"], file["stats"].st_mode) - gitstat = git_gud(file["abs_file_path"]) if (args.extend and is_path_git_repo_dir(file["abs_file_path"], file["stats"].st_mode)) else '' + file_strings = file_string_gen(file.abs_file_path, file.stats.st_mode) + file_str = ' ' + file_strings.colored_filepath + file_strings.link_sep + file_strings.colored_link_dest + #so hacky + if ' ' in file.abs_file_path: + file_str = '\'' + file_strings.colored_filepath + '\' ' + file_strings.link_sep + file_strings.colored_link_dest + + #print(file_string_gen(file["abs_file_path"], file["stats"].st_mode)) + gitstat = git_gud(file.abs_file_path) if (args.extend and is_path_git_repo_dir(file.abs_file_path, file.stats.st_mode)) else '' ## do printing - print(f"{perm_prettybits} {nlinks:2d} {user:<8s} {group:<8s} {size:>{longest_size_length}} {timestring} {colored_file} {gitstat}") + print(f"{perm_prettybits} {nlinks:2d} {user:<8s} {group:<8s} {size:>{longest_size_length}} {timestring} {file_str} {gitstat}")