From dd32deac6e24b674d91a2015fcd9d5ecdeee8569 Mon Sep 17 00:00:00 2001 From: icaema Date: Sat, 17 Feb 2024 21:42:03 -0600 Subject: [PATCH] ls.py v1 --- ls.py | 179 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 179 insertions(+) create mode 100755 ls.py diff --git a/ls.py b/ls.py new file mode 100755 index 0000000..f3b1641 --- /dev/null +++ b/ls.py @@ -0,0 +1,179 @@ +#!/usr/bin/python3 +import os +import sys +import pwd +import grp +import time +import argparse +from enum import Enum + +# ANSI escape codes for colors +COLORS = { + 'RESET': '\033[0m', + 'BOLD': '\033[1m', + 'RED': '\033[91m', + 'GREEN': '\033[92m', + 'YELLOW': '\033[93m', + 'BLUE': '\033[94m', + 'MAGENTA': '\033[95m', + 'CYAN': '\033[96m', + 'WHITE': '\033[97m' +} + +class Filetype(Enum): + S_IFMT = 0o0170000 + S_IFSOCK = 0o140000 + S_IFLNK = 0o120000 + S_IFREG = 0o100000 + S_IFBLK = 0o060000 + S_IFDIR = 0o040000 + S_IFCHR = 0o020000 + S_IFIFO = 0o010000 + +def colorize(text, color): + return f"{COLORS[color]}{text}{COLORS['RESET']}" + + +def color_file(text, mode): + match Filetype(mode & Filetype.S_IFMT.value): + case Filetype.S_IFREG: + if mode & 0o100 or mode & 0o010 or mode & 0o001: + return colorize(text, "GREEN") # file is executable, return green + return colorize(text, "WHITE") + case Filetype.S_IFDIR: + return colorize(text, "BLUE") + case Filetype.S_IFLNK: + return colorize(text, "CYAN") + case Filetype.S_IFSOCK: + return colorize(text, "MAGENTA") + case Filetype.S_IFBLK: + return colorize(text, "YELLOW") + case Filetype.S_IFCHR: + return colorize(text, "YELLOW") + case Filetype.S_IFIFO: + return colorize(text, "MAGENTA") + case _: + return colorize(text, "RED") + + +def parse_permission(mode): + permissions = "-" + match Filetype(mode & Filetype.S_IFMT.value): + case Filetype.S_IFSOCK: + permissions = 's' + case Filetype.S_IFLNK: + permissions = 'l' + case Filetype.S_IFREG: + permissions = '-' + case Filetype.S_IFBLK: + permissions = 'b' + case Filetype.S_IFDIR: + permissions = 'd' + case Filetype.S_IFCHR: + permissions = 'c' + case Filetype.S_IFIFO: + permissions = 'f' + case _: + permissions = '?' + + permissions += ''.join([ + "r" if mode & 0o400 else "-", + "w" if mode & 0o200 else "-", + "x" if mode & 0o100 else "-", + "r" if mode & 0o040 else "-", + "w" if mode & 0o020 else "-", + "x" if mode & 0o010 else "-", + "r" if mode & 0o004 else "-", + "w" if mode & 0o002 else "-", + "x" if mode & 0o001 else "-" + ]) + return permissions + +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 list_files(directory='.', args={}): + files = os.listdir(directory) + + print(f"files: {len(files)}") + + if args.time_sorted: + files.sort(key=lambda name: os.lstat(name).st_mtime, reverse=(not args.reverse)) + else: + files.sort() + + # filter out hid files if needed + if not args.all: + files = [file for file in files if not file.startswith('.')] + + if args.long: + for file in files: + #file_path = os.path.join(directory, file) # for rel paths + file_path = os.path.abspath(file) # for full path + stats = os.lstat(file_path) + mode = stats.st_mode + permissions = parse_permission(mode) + if args.sparse and permissions[0] == 'l': + permissions = 'l ' + nlink = stats.st_nlink + user = pwd.getpwuid(stats.st_uid).pw_name + group = grp.getgrgid(stats.st_gid).gr_name + + size = stats.st_size + if args.human: + size=sizeof_fmt(size) + if args.sparse and Filetype(mode & Filetype.S_IFMT.value) == Filetype.S_IFDIR: + size="" + + if args.full_time: + mtime = time.strftime("%b %d %Y %X %z", time.localtime(stats.st_mtime)) # -0400 + #mtime = time.strftime("%b %d %X %Z", time.localtime(stats.st_mtime)) # CST + else: + mtime = time.strftime("%b %d %H:%M", time.localtime(stats.st_mtime)) + + colored_file = color_file(file_path, mode) + + if os.path.islink(file_path): + color = "RED" + if os.path.exists(os.readlink(file_path)): + color = "GREEN" + print(f"{permissions} {nlink:2d} {user:<8s} {group:<8s} {size:>6} {mtime} {colored_file} -> {colorize(os.readlink(file_path), color)}") + else: + print(f"{permissions} {nlink:2d} {user:<8s} {group:<8s} {size:>6} {mtime} {colored_file}") + + else: + for file in files: + file_path = os.path.join(directory, file) + if os.path.islink(file_path): + print(colorize(file + " -> " + os.readlink(file_path), 'CYAN')) + elif os.path.isdir(file_path): + print(colorize(file, 'BLUE')) + else: + print(file) + + + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="List files in a directory", add_help=False) + parser.add_argument('directory', nargs='?', default='.', help="Directory path") + parser.add_argument('-a', '--all', action='store_true', help="Show hidden files") + parser.add_argument('-l', '--long', action='store_true', help="Use a long listing format") + parser.add_argument('-F', '--full-time', action='store_true', help="Show full full time") + parser.add_argument('-s', '--sparse', action='store_true', help="Blank irrelevant information") + + parser.add_argument('-t', '--time-sorted', action='store_true', help="Sort by time") + parser.add_argument('-r', '--reverse', action='store_true', help="Reverse sort order") + + parser.add_argument('-h', '--human', action='store_true', help="Human Readable size") + + args = parser.parse_args() + + print(f"ls.py {' '.join(sys.argv[1:])}") + list_files(args.directory, args)