187 lines
4.6 KiB
Bash
187 lines
4.6 KiB
Bash
#!/usr/bin/env zsh
|
|
|
|
# set the prefix for all functions
|
|
local pfx=${1:-'ls-color'}
|
|
|
|
# {{{ From mode
|
|
# Usage:
|
|
# $1: filename
|
|
# $2: The value of struct stat st_mode
|
|
# If empty, modecolors lookup will be skipped
|
|
# $3: (If symlink) The value of struct stat st_mode
|
|
# for the target of $1's symlink. If unset,
|
|
# interpret as a broken link.
|
|
# Sets REPLY to the console code
|
|
${pfx}::from-mode () {
|
|
|
|
emulate -L zsh
|
|
setopt cbases octalzeroes extendedglob
|
|
|
|
[[ -z $2 ]] && return 1
|
|
|
|
local -i reg=0
|
|
local -a codes
|
|
|
|
local -i st_mode=$(($2))
|
|
# See man 7 inode for more info
|
|
# file type
|
|
case $(( st_mode & 0170000 )) in
|
|
$(( 0140000 )) ) codes=( $modecolors[so] ) ;;
|
|
$(( 0120000 )) ) # symlink, special handling
|
|
if ! (($+3)); then
|
|
REPLY=$modecolors[or]
|
|
elif [[ $modecolors[ln] = target ]]; then
|
|
"$0" "$1" "${@:3}"
|
|
else
|
|
REPLY=$modecolors[ln]
|
|
fi
|
|
return
|
|
;;
|
|
$(( 0100000 )) ) codes=( ); reg=1 ;; # regular file
|
|
$(( 0060000 )) ) codes=( $modecolors[bd] ) ;;
|
|
$(( 0040000 )) ) codes=( $modecolors[di] ) ;;
|
|
$(( 0020000 )) ) codes=( $modecolors[cd] ) ;;
|
|
$(( 0010000 )) ) codes=( $modecolors[pi] ) ;;
|
|
esac
|
|
|
|
# setuid/setgid/sticky/other-writable
|
|
(( st_mode & 04000 )) && codes+=( $modecolors[su] )
|
|
(( st_mode & 02000 )) && codes+=( $modecolors[sg] )
|
|
(( ! reg )) && case $(( st_mode & 01002 )) in
|
|
# sticky
|
|
$(( 01000 )) ) codes+=( $modecolors[st] ) ;;
|
|
# other-writable
|
|
$(( 00002 )) ) codes+=( $modecolors[ow] ) ;;
|
|
# other-writable and sticky
|
|
$(( 01002 )) ) codes+=( $modecolors[tw] ) ;;
|
|
esac
|
|
|
|
# executable
|
|
if (( ! $#codes )); then
|
|
(( st_mode & 0111 )) && codes+=( $modecolors[ex] )
|
|
fi
|
|
|
|
# return nonzero if no matching code
|
|
[[ ${REPLY::=${(j:;:)codes}} ]]
|
|
} # }}}
|
|
# {{{ From name
|
|
# Usage:
|
|
# $1: filename
|
|
#
|
|
# Sets REPLY to the console code
|
|
${pfx}::from-name () {
|
|
|
|
emulate -L zsh
|
|
setopt extendedglob
|
|
|
|
# Return non-zero if no keys match
|
|
[[ ${REPLY::=$namecolors[(k)$1]} ]]
|
|
} # }}}
|
|
# {{{ Init
|
|
# WARNING: initializes namecolors and modecolors in global scope
|
|
${pfx}::init () {
|
|
emulate -L zsh
|
|
|
|
# Use $1 if provided, otherwise use LS_COLORS
|
|
# Use LSCOLORS on BSD
|
|
local LS_COLORS=${1:-${LS_COLORS:-$LSCOLORS}}
|
|
|
|
# read in LS_COLORS
|
|
typeset -gA namecolors=(${(@s:=:)${(@s.:.)LS_COLORS}:#[[:alpha:]][[:alpha:]]=*})
|
|
typeset -gA modecolors=(${(@Ms:=:)${(@s.:.)LS_COLORS}:#[[:alpha:]][[:alpha:]]=*})
|
|
}
|
|
# }}}
|
|
# {{{ Match by
|
|
# Usage:
|
|
# $1: filename
|
|
# Optional (must be $2): g[lobal]: Use existing stat | lstat in parent scope
|
|
# ${@:2}: Append to reply:
|
|
# - l[stat] : Look up using lstat (don't follow symlink), if empty match name
|
|
# - s[tat] : Look up using stat (do follow symlink), if empty match name
|
|
# - n[ame] : Only match name
|
|
# - f[ollow]: Get resolution path of symlink
|
|
# - L[stat] : Same as above but don't match name
|
|
# - S[tat] : Same as above but don't match name
|
|
# - a[ll] : If a broken symlink: lstat follow lstat
|
|
# : If a symlink : lstat follow stat
|
|
# : Otherwise : lstat
|
|
# - A[ll] : If a broken symlink: Lstat follow Lstat
|
|
# : If a symlink : Lstat follow Stat
|
|
# : Otherwise : Lstat
|
|
#
|
|
# or returns non-zero
|
|
${pfx}::match-by () {
|
|
emulate -L zsh
|
|
setopt extendedglob cbases octalzeroes
|
|
|
|
local arg REPLY name=$1 pfx=${0%::match-by}
|
|
shift
|
|
|
|
# init in local scope if not using global params
|
|
if ! [[ -v namecolors && -v modecolors ]]; then
|
|
local -A namecolors modecolors
|
|
${pfx}::init
|
|
fi
|
|
|
|
if [[ ${1:l} = (g|global) ]]; then
|
|
shift
|
|
else
|
|
local -a stat lstat
|
|
declare -ga reply=()
|
|
fi
|
|
|
|
zmodload -F zsh/stat b:zstat
|
|
for arg; do
|
|
case ${arg[1]:l} in
|
|
n|name)
|
|
${pfx}::from-name $name
|
|
reply+=("$REPLY")
|
|
;;
|
|
l|lstat)
|
|
(($#lstat)) || zstat -A lstat -L $name || return 1
|
|
if ((lstat[3] & 0170000 )); then
|
|
# follow symlink
|
|
(($#stat)) || zstat -A stat $name 2>/dev/null
|
|
fi
|
|
${pfx}::from-mode "$name" "$lstat[3]" $stat[3]
|
|
if [[ $REPLY || ${2[1]} = L ]]; then
|
|
reply+=("$REPLY")
|
|
else # fall back to name
|
|
"$0" "$name" g n
|
|
fi
|
|
;;
|
|
s|stat)
|
|
(($#stat)) || zstat -A stat $name || return 1
|
|
${pfx}::from-mode $name $stat[3]
|
|
reply+=("$REPLY")
|
|
if [[ $REPLY || ${arg[1]} = S ]]; then
|
|
reply+=("$REPLY")
|
|
else # fall back to name
|
|
"$0" "$name" g n
|
|
fi
|
|
;;
|
|
f|follow)
|
|
(($#lstat)) || zstat -A lstat -L $name || return 1
|
|
reply+=("$lstat[14]")
|
|
;;
|
|
a|all)
|
|
# Match case
|
|
"$0" "$name" g ${${${arg[1]%a}:+L}:-l}
|
|
# won't append if empty
|
|
reply+=($lstat[14])
|
|
# $stat[14] will be empty if not a symlink
|
|
if [[ $lstat[14] ]]; then
|
|
if [[ -e $name ]]; then
|
|
"$0" "$name" g ${${${arg[1]%a}:+S}:-s}
|
|
else
|
|
reply+=($reply[-2])
|
|
fi
|
|
fi
|
|
;;
|
|
*) return 2 ;;
|
|
esac
|
|
done
|
|
}
|
|
# }}}
|
|
# vim: set foldmethod=marker:
|