mirror of
https://github.com/xonsh/xonsh.git
synced 2025-03-04 08:24:40 +01:00
LS_COLORS add mh, change rs to fi; support ln=target
This commit is contained in:
parent
c0089a4cef
commit
129ce9e44a
7 changed files with 267 additions and 96 deletions
2
.github/workflows/pytest.yml
vendored
2
.github/workflows/pytest.yml
vendored
|
@ -23,5 +23,5 @@ jobs:
|
|||
- shell: bash -l {0}
|
||||
run: |
|
||||
python -m pip install . --no-deps
|
||||
python -m xonsh run-tests.xsh --timeout=90
|
||||
python -m xonsh run-tests.xsh --timeout=240
|
||||
|
||||
|
|
29
news/fix-ls-color-codes.rst
Normal file
29
news/fix-ls-color-codes.rst
Normal file
|
@ -0,0 +1,29 @@
|
|||
**Added:**
|
||||
|
||||
* $LS_COLORS code 'mh' now recognized for (multi) hard-linked files.
|
||||
* $LS_COLORS code 'ca' now recognized for files with security capabilities (linux only).
|
||||
|
||||
**Changed:**
|
||||
|
||||
* <news item>
|
||||
|
||||
**Deprecated:**
|
||||
|
||||
* <news item>
|
||||
|
||||
**Removed:**
|
||||
|
||||
* <news item>
|
||||
|
||||
**Fixed:**
|
||||
|
||||
* $LS_COLORS code 'fi' now used for "regular files", as it should have been all along. (was 'rs')
|
||||
See (#3608)[https://github.com/xonsh/xonsh/issues/3608].
|
||||
* pyghooks.color_files now follows implememntation of ls --color closely. Thanks @qwenger!
|
||||
However, a few documented differences remain due to use in Xonsh.
|
||||
|
||||
* $LS_COLORS['ln'] = 'target' now works. Also fixes #3578.
|
||||
|
||||
**Security:**
|
||||
|
||||
* <news item>
|
|
@ -171,6 +171,7 @@ def test_event_on_envvar_change(xonsh_builtins):
|
|||
xonsh_builtins.__xonsh__.env = env
|
||||
share = []
|
||||
# register
|
||||
|
||||
@xonsh_builtins.events.on_envvar_change
|
||||
def handler(name, oldvalue, newvalue, **kwargs):
|
||||
share.extend((name, oldvalue, newvalue))
|
||||
|
@ -186,6 +187,7 @@ def test_event_on_envvar_new(xonsh_builtins):
|
|||
xonsh_builtins.__xonsh__.env = env
|
||||
share = []
|
||||
# register
|
||||
|
||||
@xonsh_builtins.events.on_envvar_new
|
||||
def handler(name, value, **kwargs):
|
||||
share.extend((name, value))
|
||||
|
@ -201,6 +203,7 @@ def test_event_on_envvar_change_from_none_value(xonsh_builtins):
|
|||
xonsh_builtins.__xonsh__.env = env
|
||||
share = []
|
||||
# register
|
||||
|
||||
@xonsh_builtins.events.on_envvar_change
|
||||
def handler(name, oldvalue, newvalue, **kwargs):
|
||||
share.extend((name, oldvalue, newvalue))
|
||||
|
@ -217,6 +220,7 @@ def test_event_on_envvar_change_no_fire_when_value_is_same(val, xonsh_builtins):
|
|||
xonsh_builtins.__xonsh__.env = env
|
||||
share = []
|
||||
# register
|
||||
|
||||
@xonsh_builtins.events.on_envvar_change
|
||||
def handler(name, oldvalue, newvalue, **kwargs):
|
||||
share.extend((name, oldvalue, newvalue))
|
||||
|
@ -232,6 +236,7 @@ def test_events_on_envvar_called_in_right_order(xonsh_builtins):
|
|||
xonsh_builtins.__xonsh__.env = env
|
||||
share = []
|
||||
# register
|
||||
|
||||
@xonsh_builtins.events.on_envvar_new
|
||||
def handler(name, value, **kwargs):
|
||||
share[:] = ["new"]
|
||||
|
@ -303,21 +308,23 @@ def test_delitem_default():
|
|||
|
||||
def test_lscolors_target():
|
||||
lsc = LsColors.fromstring("ln=target")
|
||||
assert lsc["ln"] == ("TARGET",)
|
||||
assert lsc["ln"] == ("NO_COLOR",)
|
||||
assert lsc.is_target("ln")
|
||||
assert lsc.detype() == "ln=target"
|
||||
assert not (lsc.is_target("mi"))
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"key_in,old_in,new_in,test",
|
||||
[
|
||||
("rs", ("NO_COLOR",), ("BLUE",), "existing key, change value"),
|
||||
("rs", ("NO_COLOR",), ("NO_COLOR",), "existing key, no change in value"),
|
||||
("fi", ("NO_COLOR",), ("BLUE",), "existing key, change value"),
|
||||
("fi", ("NO_COLOR",), ("NO_COLOR",), "existing key, no change in value"),
|
||||
("tw", None, ("NO_COLOR",), "create new key"),
|
||||
("pi", ("BACKGROUND_BLACK", "YELLOW"), None, "delete existing key"),
|
||||
],
|
||||
)
|
||||
def test_lscolors_events(key_in, old_in, new_in, test, xonsh_builtins):
|
||||
lsc = LsColors.fromstring("rs=0:di=01;34:pi=40;33")
|
||||
lsc = LsColors.fromstring("fi=0:di=01;34:pi=40;33")
|
||||
# corresponding colors: [('NO_COLOR',), ('BOLD_CYAN',), ('BOLD_CYAN',), ('BACKGROUND_BLACK', 'YELLOW')]
|
||||
|
||||
event_fired = False
|
||||
|
|
|
@ -2,8 +2,10 @@
|
|||
import pytest
|
||||
import os
|
||||
import stat
|
||||
import pathlib
|
||||
|
||||
from tempfile import TemporaryDirectory
|
||||
from xonsh.platform import ON_WINDOWS
|
||||
|
||||
from xonsh.pyghooks import (
|
||||
XonshStyle,
|
||||
|
@ -15,7 +17,6 @@ from xonsh.pyghooks import (
|
|||
)
|
||||
|
||||
from xonsh.environ import LsColors
|
||||
from tools import skip_if_on_windows
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
|
@ -25,11 +26,10 @@ def xonsh_builtins_LS_COLORS(xonsh_builtins):
|
|||
lsc = LsColors(LsColors.default_settings)
|
||||
xonsh_builtins.__xonsh__.env["LS_COLORS"] = lsc
|
||||
xonsh_builtins.__xonsh__.shell.shell_type = "prompt_toolkit"
|
||||
# styler = XonshStyle() # default style
|
||||
# xonsh_builtins.__xonsh__.shell.shell.styler = styler
|
||||
# can't really instantiate XonshStyle separate from a shell??
|
||||
xonsh_builtins.__xonsh__.shell.shell.styler = XonshStyle() # default style
|
||||
|
||||
yield xonsh_builtins
|
||||
|
||||
xonsh_builtins.__xonsh__.env = e
|
||||
|
||||
|
||||
|
@ -157,29 +157,62 @@ def test_XonshStyle_init_file_color_tokens(xonsh_builtins_LS_COLORS):
|
|||
)
|
||||
|
||||
|
||||
_cf = {
|
||||
"rs": "regular",
|
||||
"di": "simple_dir",
|
||||
"ln": "simple_link",
|
||||
"mh": None,
|
||||
"pi": "pipe",
|
||||
"so": None,
|
||||
"do": None,
|
||||
# bug ci failures: 'bd': '/dev/sda',
|
||||
# bug ci failures:'cd': '/dev/tty',
|
||||
"or": "orphan_link",
|
||||
"mi": None,
|
||||
"su": "set_uid",
|
||||
"sg": "set_gid",
|
||||
"ca": None,
|
||||
"tw": "sticky_ow_dir",
|
||||
"ow": "other_writable_dir",
|
||||
"st": "sticky_dir",
|
||||
"ex": "executable",
|
||||
"*.emf": "foo.emf",
|
||||
"*.zip": "foo.zip",
|
||||
"*.ogg": "foo.ogg",
|
||||
}
|
||||
# parameterized tests for file colorization
|
||||
# note 'ca' is checked by standalone test.
|
||||
# requires privilege to create a file with capabilities
|
||||
|
||||
if ON_WINDOWS:
|
||||
# file coloring support is very limited on Windows, only test the cases we can easily make work
|
||||
# If you care about file colors, use Windows Subsystem for Linux, or another OS.
|
||||
|
||||
_cf = {
|
||||
"fi": "regular",
|
||||
"di": "simple_dir",
|
||||
"ln": "sym_link",
|
||||
"pi": None,
|
||||
"so": None,
|
||||
"do": None,
|
||||
# bug ci failures: 'bd': '/dev/sda',
|
||||
# bug ci failures:'cd': '/dev/tty',
|
||||
"or": "orphan",
|
||||
"mi": None, # never used
|
||||
"su": None,
|
||||
"sg": None,
|
||||
"ca": None, # Separate special case test,
|
||||
"tw": None,
|
||||
"ow": None,
|
||||
"st": None,
|
||||
"ex": None, # executable is a filetype test on Windows.
|
||||
"*.emf": "foo.emf",
|
||||
"*.zip": "foo.zip",
|
||||
"*.ogg": "foo.ogg",
|
||||
"mh": "hard_link",
|
||||
}
|
||||
else:
|
||||
# full-fledged, VT100 based infrastructure
|
||||
_cf = {
|
||||
"fi": "regular",
|
||||
"di": "simple_dir",
|
||||
"ln": "sym_link",
|
||||
"pi": "pipe",
|
||||
"so": None,
|
||||
"do": None,
|
||||
# bug ci failures: 'bd': '/dev/sda',
|
||||
# bug ci failures:'cd': '/dev/tty',
|
||||
"or": "orphan",
|
||||
"mi": None, # never used
|
||||
"su": "set_uid",
|
||||
"sg": "set_gid",
|
||||
"ca": None, # Separate special case test,
|
||||
"tw": "sticky_ow_dir",
|
||||
"ow": "other_writable_dir",
|
||||
"st": "sticky_dir",
|
||||
"ex": "executable",
|
||||
"*.emf": "foo.emf",
|
||||
"*.zip": "foo.zip",
|
||||
"*.ogg": "foo.ogg",
|
||||
"mh": "hard_link",
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
|
@ -203,18 +236,19 @@ def colorizable_files():
|
|||
os.mkdir(file_path)
|
||||
else:
|
||||
open(file_path, "a").close()
|
||||
if k in ("di", "rg"):
|
||||
if k in ("di", "fi"):
|
||||
pass
|
||||
elif k == "ex":
|
||||
os.chmod(file_path, stat.S_IXUSR)
|
||||
elif k == "ln":
|
||||
os.chmod(file_path, stat.S_IRWXU) # tmpdir on windows need u+w
|
||||
elif k == "ln": # cook ln test case.
|
||||
os.chmod(file_path, stat.S_IRWXU) # link to *executable* file
|
||||
os.rename(file_path, file_path + "_target")
|
||||
os.symlink(file_path + "_target", file_path)
|
||||
elif k == "or":
|
||||
os.rename(file_path, file_path + "_target")
|
||||
os.symlink(file_path + "_target", file_path)
|
||||
os.remove(file_path + "_target")
|
||||
elif k == "pi":
|
||||
elif k == "pi": # not on Windows
|
||||
os.remove(file_path)
|
||||
os.mkfifo(file_path)
|
||||
elif k == "su":
|
||||
|
@ -232,24 +266,72 @@ def colorizable_files():
|
|||
)
|
||||
elif k == "ow":
|
||||
os.chmod(file_path, stat.S_IWOTH | stat.S_IRUSR | stat.S_IWUSR)
|
||||
elif k == "mh":
|
||||
os.rename(file_path, file_path + "_target")
|
||||
os.link(file_path + "_target", file_path)
|
||||
else:
|
||||
pass # cauterize those elseless ifs!
|
||||
|
||||
os.symlink(file_path, file_path + "_symlink")
|
||||
|
||||
yield tempdir
|
||||
|
||||
pass # tempdir get cleaned up here.
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"key,file_path", [(key, file_path) for key, file_path in _cf.items() if file_path]
|
||||
"key,file_path", [(key, file_path) for key, file_path in _cf.items() if file_path],
|
||||
)
|
||||
@skip_if_on_windows
|
||||
def test_colorize_file(key, file_path, colorizable_files, xonsh_builtins_LS_COLORS):
|
||||
xonsh_builtins_LS_COLORS.__xonsh__.shell.shell.styler = (
|
||||
XonshStyle()
|
||||
) # default style
|
||||
"""test proper file codes with symlinks colored normally"""
|
||||
ffp = colorizable_files + "/" + file_path
|
||||
mode = (os.lstat(ffp)).st_mode
|
||||
color_token, color_key = color_file(ffp, mode)
|
||||
stat_result = os.lstat(ffp)
|
||||
color_token, color_key = color_file(ffp, stat_result)
|
||||
assert color_key == key, "File classified as expected kind"
|
||||
assert color_token == file_color_tokens[key], "Color token is as expected"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"key,file_path", [(key, file_path) for key, file_path in _cf.items() if file_path],
|
||||
)
|
||||
def test_colorize_file_symlink(
|
||||
key, file_path, colorizable_files, xonsh_builtins_LS_COLORS
|
||||
):
|
||||
"""test proper file codes with symlinks colored target."""
|
||||
xonsh_builtins_LS_COLORS.__xonsh__.env["LS_COLORS"]["ln"] = "target"
|
||||
ffp = colorizable_files + "/" + file_path + "_symlink"
|
||||
stat_result = os.lstat(ffp)
|
||||
assert stat.S_ISLNK(stat_result.st_mode)
|
||||
|
||||
_, color_key = color_file(ffp, stat_result)
|
||||
|
||||
try:
|
||||
tar_stat_result = os.stat(ffp) # stat the target of the link
|
||||
tar_ffp = str(pathlib.Path(ffp).resolve())
|
||||
_, tar_color_key = color_file(tar_ffp, tar_stat_result)
|
||||
if tar_color_key.startswith("*"):
|
||||
tar_color_key = (
|
||||
"fi" # all the *.* zoo, link is colored 'fi', not target type.
|
||||
)
|
||||
except FileNotFoundError: # orphan symlinks always colored 'or'
|
||||
tar_color_key = "or" # Fake if for missing file
|
||||
|
||||
assert color_key == tar_color_key, "File classified as expected kind, via symlink"
|
||||
|
||||
|
||||
import xonsh.lazyimps
|
||||
|
||||
|
||||
def test_colorize_file_ca(xonsh_builtins_LS_COLORS, monkeypatch):
|
||||
def mock_os_listxattr(*args, **kwards):
|
||||
return ["security.capability"]
|
||||
|
||||
monkeypatch.setattr(xonsh.pyghooks, "os_listxattr", mock_os_listxattr)
|
||||
|
||||
with TemporaryDirectory() as tmpdir:
|
||||
file_path = tmpdir + "/cap_file"
|
||||
open(file_path, "a").close()
|
||||
os.chmod(file_path, stat.S_IXUSR) # ca overrides ex
|
||||
color_token, color_key = color_file(file_path, os.lstat(file_path))
|
||||
|
||||
assert color_key == "ca"
|
||||
|
|
|
@ -198,8 +198,8 @@ def to_debug(x):
|
|||
|
||||
class LsColors(cabc.MutableMapping):
|
||||
"""Helps convert to/from $LS_COLORS format, respecting the xonsh color style.
|
||||
This accepts the same inputs as dict(). The link ``target`` is represented
|
||||
by the special ``"TARGET"`` color.
|
||||
This accepts the same inputs as dict(). The special value ``target`` is
|
||||
replaced by no color, but sets a flag for cognizant application (see is_target()).
|
||||
"""
|
||||
|
||||
default_settings = {
|
||||
|
@ -321,6 +321,7 @@ class LsColors(cabc.MutableMapping):
|
|||
"di": ("BOLD_BLUE",),
|
||||
"do": ("BOLD_PURPLE",),
|
||||
"ex": ("BOLD_GREEN",),
|
||||
"fi": ("NO_COLOR",),
|
||||
"ln": ("BOLD_CYAN",),
|
||||
"mh": ("NO_COLOR",),
|
||||
"mi": ("NO_COLOR",),
|
||||
|
@ -335,10 +336,17 @@ class LsColors(cabc.MutableMapping):
|
|||
"tw": ("BLACK", "BACKGROUND_GREEN"),
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self._d = dict(*args, **kwargs)
|
||||
target_value = "target" # special value to set for ln=target
|
||||
target_color = ("NO_COLOR",) # repres in color space
|
||||
|
||||
def __init__(self, ini_dict: dict = None):
|
||||
self._style = self._style_name = None
|
||||
self._detyped = None
|
||||
self._d = dict()
|
||||
self._targets = set()
|
||||
if ini_dict:
|
||||
for key, value in ini_dict.items():
|
||||
self[key] = value # in case init includes ln=target
|
||||
|
||||
def __getitem__(self, key):
|
||||
return self._d[key]
|
||||
|
@ -346,13 +354,20 @@ class LsColors(cabc.MutableMapping):
|
|||
def __setitem__(self, key, value):
|
||||
self._detyped = None
|
||||
old_value = self._d.get(key, None)
|
||||
self._targets.discard(key)
|
||||
if value == LsColors.target_value:
|
||||
value = LsColors.target_color
|
||||
self._targets.add(key)
|
||||
self._d[key] = value
|
||||
if old_value != value:
|
||||
if (
|
||||
old_value != value
|
||||
): # bug won't fire if new value is 'target' and old value happened to be no color.
|
||||
events.on_lscolors_change.fire(key=key, oldvalue=old_value, newvalue=value)
|
||||
|
||||
def __delitem__(self, key):
|
||||
self._detyped = None
|
||||
old_value = self._d.get(key, None)
|
||||
self._targets.discard(key)
|
||||
del self._d[key]
|
||||
events.on_lscolors_change.fire(key=key, oldvalue=old_value, newvalue=None)
|
||||
|
||||
|
@ -377,6 +392,10 @@ class LsColors(cabc.MutableMapping):
|
|||
p.break_()
|
||||
p.pretty(dict(self))
|
||||
|
||||
def is_target(self, key) -> bool:
|
||||
"Return True if key is 'target'"
|
||||
return key in self._targets
|
||||
|
||||
def detype(self):
|
||||
"""De-types the instance, allowing it to be exported to the environment."""
|
||||
style = self.style
|
||||
|
@ -387,8 +406,8 @@ class LsColors(cabc.MutableMapping):
|
|||
+ "="
|
||||
+ ";".join(
|
||||
[
|
||||
"target"
|
||||
if v == "TARGET"
|
||||
LsColors.target_value
|
||||
if key in self._targets
|
||||
else ansi_color_name_to_escape_code(v, cmap=style)
|
||||
for v in val
|
||||
]
|
||||
|
@ -426,23 +445,21 @@ class LsColors(cabc.MutableMapping):
|
|||
# string inputs always use default codes, so translating into
|
||||
# xonsh names should be done from defaults
|
||||
reversed_default = ansi_reverse_style(style="default")
|
||||
data = {}
|
||||
for item in s.split(":"):
|
||||
key, eq, esc = item.partition("=")
|
||||
if not eq:
|
||||
# not a valid item
|
||||
pass
|
||||
elif esc == "target":
|
||||
data[key] = ("TARGET",)
|
||||
elif esc == LsColors.target_value: # really only for 'ln'
|
||||
obj[key] = esc
|
||||
else:
|
||||
try:
|
||||
data[key] = ansi_color_escape_code_to_name(
|
||||
obj[key] = ansi_color_escape_code_to_name(
|
||||
esc, "default", reversed_style=reversed_default
|
||||
)
|
||||
except Exception as e:
|
||||
print("xonsh:warning:" + str(e), file=sys.stderr)
|
||||
data[key] = ("NO_COLOR",)
|
||||
obj._d = data
|
||||
obj[key] = ("NO_COLOR",)
|
||||
return obj
|
||||
|
||||
@classmethod
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
"""Lazy imports that may apply across the xonsh package."""
|
||||
import os
|
||||
import importlib
|
||||
|
||||
from xonsh.platform import ON_WINDOWS, ON_DARWIN
|
||||
|
@ -88,3 +89,11 @@ def terminal256():
|
|||
@lazyobject
|
||||
def html():
|
||||
return importlib.import_module("pygments.formatters.html")
|
||||
|
||||
|
||||
@lazyobject
|
||||
def os_listxattr():
|
||||
def dummy_listxattr(*args, **kwargs):
|
||||
return []
|
||||
|
||||
return getattr(os, "listxattr", dummy_listxattr)
|
||||
|
|
|
@ -8,6 +8,7 @@ import stat
|
|||
from collections import ChainMap
|
||||
from collections.abc import MutableMapping
|
||||
from keyword import iskeyword
|
||||
from xonsh.lazyimps import os_listxattr
|
||||
|
||||
from pygments.lexer import inherit, bygroups, include
|
||||
from pygments.lexers.agile import Python3Lexer
|
||||
|
@ -1356,15 +1357,16 @@ def on_lscolors_change(key, oldvalue, newvalue, **kwargs):
|
|||
events.on_lscolors_change(on_lscolors_change)
|
||||
|
||||
|
||||
def color_file(file_path: str, mode: int) -> (Color, str):
|
||||
"""Determine color to use for file as ls -c would, given stat() results and its name.
|
||||
def color_file(file_path: str, path_stat: os.stat_result) -> (Color, str):
|
||||
"""Determine color to use for file *approximately* as ls --color would,
|
||||
given lstat() results and its path.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
file_path : string
|
||||
file_path:
|
||||
relative path of file (as user typed it).
|
||||
mode : int
|
||||
stat() results for file_path.
|
||||
path_stat:
|
||||
lstat() results for file_path.
|
||||
|
||||
Returns
|
||||
-------
|
||||
|
@ -1373,54 +1375,79 @@ def color_file(file_path: str, mode: int) -> (Color, str):
|
|||
Notes
|
||||
-----
|
||||
|
||||
* doesn't handle CA (capability)
|
||||
* doesn't handle LS TARGET mapping
|
||||
|
||||
* implementation follows one authority:
|
||||
https://github.com/coreutils/coreutils/blob/master/src/ls.c#L4879
|
||||
* except:
|
||||
1. does not return 'mi'. That's the color ls uses to show the (missing) *target* of a symlink
|
||||
(in ls -l, not ls).
|
||||
2. in dircolors, setting type code to '0 or '00' bypasses that test and proceeds to others.
|
||||
In our implementation, setting code to '00' paints the file with no color.
|
||||
This is arguably a bug.
|
||||
"""
|
||||
|
||||
lsc = builtins.__xonsh__.env["LS_COLORS"]
|
||||
color_key = "rs"
|
||||
color_key = "fi"
|
||||
|
||||
if stat.S_ISLNK(mode): # must test link before S_ISREG (esp execute)
|
||||
color_key = "ln"
|
||||
# if symlink, get info on (final) target
|
||||
if stat.S_ISLNK(path_stat.st_mode):
|
||||
try:
|
||||
os.stat(file_path)
|
||||
except FileNotFoundError:
|
||||
color_key = "or"
|
||||
elif stat.S_ISREG(mode):
|
||||
if stat.S_IMODE(mode) & (stat.S_IXUSR + stat.S_IXGRP + stat.S_IXOTH):
|
||||
color_key = "ex"
|
||||
elif (
|
||||
mode & stat.S_ISUID
|
||||
): # too many tests before we get to the common case -- restructure?
|
||||
tar_path_stat = os.stat(file_path) # and work with its properties
|
||||
if lsc.is_target("ln"): # if ln=target
|
||||
path_stat = tar_path_stat
|
||||
except FileNotFoundError: # bug always color broken link 'or'
|
||||
color_key = "or" # early exit
|
||||
ret_color_token = file_color_tokens.get(color_key, Text)
|
||||
return ret_color_token, color_key
|
||||
|
||||
mode = path_stat.st_mode
|
||||
|
||||
if stat.S_ISREG(mode):
|
||||
if mode & stat.S_ISUID:
|
||||
color_key = "su"
|
||||
elif mode & stat.S_ISGID:
|
||||
color_key = "sg"
|
||||
else:
|
||||
match = color_file_extension_RE.match(file_path)
|
||||
if match:
|
||||
ext = "*" + match.group(1) # look for *.<fileExtension> coloring
|
||||
if ext in lsc:
|
||||
color_key = ext
|
||||
else:
|
||||
color_key = "rs"
|
||||
cap = os_listxattr(file_path, follow_symlinks=False)
|
||||
if cap and "security.capability" in cap: # protect None return on some OS?
|
||||
color_key = "ca"
|
||||
elif stat.S_IMODE(mode) & (stat.S_IXUSR + stat.S_IXGRP + stat.S_IXOTH):
|
||||
color_key = "ex"
|
||||
elif path_stat.st_nlink > 1:
|
||||
color_key = "mh"
|
||||
else:
|
||||
color_key = "rs"
|
||||
elif stat.S_ISDIR(mode): # ls -c doesn't colorize sticky or ow if not dirs...
|
||||
color_key = ("di", "ow", "st", "tw")[
|
||||
(mode & stat.S_ISVTX == stat.S_ISVTX) * 2
|
||||
+ (mode & stat.S_IWOTH == stat.S_IWOTH)
|
||||
]
|
||||
elif stat.S_ISCHR(mode):
|
||||
color_key = "cd"
|
||||
elif stat.S_ISBLK(mode):
|
||||
color_key = "bd"
|
||||
color_key = "fi"
|
||||
elif stat.S_ISDIR(mode): # ls --color doesn't colorize sticky or ow if not dirs...
|
||||
color_key = "di"
|
||||
if (mode & stat.S_ISVTX) and (mode & stat.S_IWOTH):
|
||||
color_key = "tw"
|
||||
elif mode & stat.S_IWOTH:
|
||||
color_key = "ow"
|
||||
elif mode & stat.S_ISVTX:
|
||||
color_key = "st"
|
||||
elif stat.S_ISLNK(mode):
|
||||
color_key = "ln"
|
||||
elif stat.S_ISFIFO(mode):
|
||||
color_key = "pi"
|
||||
elif stat.S_ISSOCK(mode):
|
||||
color_key = "so"
|
||||
elif stat.S_ISBLK(mode):
|
||||
color_key = "bd"
|
||||
elif stat.S_ISCHR(mode):
|
||||
color_key = "cd"
|
||||
elif stat.S_ISDOOR(mode):
|
||||
color_key = "do" # bug missing mapping for FMT based PORT and WHITEOUT ??
|
||||
color_key = "do"
|
||||
else:
|
||||
color_key = "or" # any other type --> orphan
|
||||
|
||||
# if still normal file -- try color by file extension.
|
||||
# note: symlink to *.<ext> will be colored 'fi' unless the symlink itself
|
||||
# ends with .<ext>. `ls` does the same. Bug-for-bug compatibility!
|
||||
if color_key == "fi":
|
||||
match = color_file_extension_RE.match(file_path)
|
||||
if match:
|
||||
ext = "*" + match.group(1) # look for *.<fileExtension> coloring
|
||||
if ext in lsc:
|
||||
color_key = ext
|
||||
|
||||
ret_color_token = file_color_tokens.get(color_key, Text)
|
||||
|
||||
|
@ -1464,8 +1491,8 @@ def subproc_arg_callback(_, match):
|
|||
yieldVal = Text
|
||||
try:
|
||||
path = os.path.expanduser(text)
|
||||
mode = (os.lstat(path)).st_mode # lstat() will raise FNF if not a real file
|
||||
yieldVal, _ = color_file(path, mode)
|
||||
path_stat = os.lstat(path) # lstat() will raise FNF if not a real file
|
||||
yieldVal, _ = color_file(path, path_stat)
|
||||
except (FileNotFoundError, OSError):
|
||||
pass
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue