"""Give coloring for file types as in the ls command.""" import os import os.path import stat import syslog __version__ = "v2022.04.18" FILE_KEY = "fi" DIRECTORY_KEY = "di" OTHER_WRITABLE_KEY = "ow" EXECUTABLE_KEY = "ex" SETUID_KEY = "su" SETGUID_KEY = "sg" SYMLINK_KEY = "ln" ORPHAN_KEY = "or" PIPE_KEY = "pi" CHARACTER_DEVICE_KEY = "cd" BLOCK_DEVICE_KEY = "bd" STICKY_KEY = "st" STICKY_OTHER_WRITABLE_KEY = "tw" SOCKET_KEY = "so" MISSING_KEY = "mi" MULTI_HARDLINK_KEY = "mh" def _parse_ls_colors(ls_codes): color_codes = {} for entry in ls_codes.split(":"): if "=" not in entry: continue entry_key, entry_value = entry.split("=") if entry_key.startswith("*."): entry_key = entry_key[1:] color_codes[entry_key] = entry_value assert color_codes != {}, color_codes return color_codes _DEFAULT_COLOR_CODES = \ {BLOCK_DEVICE_KEY: '01;33', SYMLINK_KEY: '01;36', STICKY_OTHER_WRITABLE_KEY: '30;42', DIRECTORY_KEY: '01;34', SETUID_KEY: '37;41', CHARACTER_DEVICE_KEY: '01;33', SOCKET_KEY: '01;35', EXECUTABLE_KEY: '01;32', STICKY_KEY: '37;44', OTHER_WRITABLE_KEY: '34;42', PIPE_KEY: '33', SETGUID_KEY: '30;43', ORPHAN_KEY: '40;31;01'} def get_color_codes(environment): """Get a dictionary of the color of every file type.""" if "LS_COLORS" in environment: try: return _parse_ls_colors(environment["LS_COLORS"]) except Exception: syslog.syslog("Syntax error in LS_COLORS environment variable. Using default colors.") return _DEFAULT_COLOR_CODES def color_key_for_path(path, color_codes, is_link_target=True): """Get the high level type (key) of a file.""" # see print_color_indicator in the file 'ls.c' in the coreutils codebase if not os.path.lexists(path): return MISSING_KEY elif os.path.islink(path): if is_link_target: try: link_path = os.path.join(os.path.dirname(path), os.readlink(path)) file_stat = os.stat(link_path) except OSError: return ORPHAN_KEY else: return SYMLINK_KEY else: file_stat = os.stat(path) mode = file_stat.st_mode if stat.S_ISREG(mode): if mode & stat.S_ISUID and SETUID_KEY in color_codes: return SETUID_KEY elif mode & stat.S_ISGID and SETGUID_KEY in color_codes: return SETGUID_KEY elif ((mode & stat.S_IXUSR or mode & stat.S_IXGRP or mode & stat.S_IXOTH) and EXECUTABLE_KEY in color_codes): return EXECUTABLE_KEY elif file_stat.st_nlink > 1 and MULTI_HARDLINK_KEY in color_codes: return MULTI_HARDLINK_KEY else: return FILE_KEY elif stat.S_ISDIR(mode): if mode & stat.S_ISVTX and mode & stat.S_IWOTH and STICKY_OTHER_WRITABLE_KEY in color_codes: return STICKY_OTHER_WRITABLE_KEY elif (mode & stat.S_IWOTH) != 0 and OTHER_WRITABLE_KEY in color_codes: return OTHER_WRITABLE_KEY elif (mode & stat.S_ISVTX) != 0 and STICKY_KEY in color_codes: return STICKY_KEY else: return DIRECTORY_KEY for test_function, color_key in [(stat.S_ISFIFO, PIPE_KEY), (stat.S_ISSOCK, SOCKET_KEY), (stat.S_ISBLK, BLOCK_DEVICE_KEY), (stat.S_ISCHR, CHARACTER_DEVICE_KEY)]: if test_function(mode): return color_key return ORPHAN_KEY def color_code_for_path(path, color_codes): """Get the color of a file.""" def get_extension(basename, color_codes): parts = basename.split(".") if len(parts) == 2: extension = "." + parts[1] if extension in color_codes: return extension elif len(parts) > 2: for extension in color_codes: if extension.startswith(".") and basename.endswith(extension): return extension target_link = color_codes.get(SYMLINK_KEY, None) color_key = color_key_for_path(path, color_codes, target_link == "target") if color_key == FILE_KEY: filename = os.path.basename(path) if "." in filename: extension = get_extension(filename, color_codes) if extension is not None: color_key = extension return color_codes.get(color_key, None)