djcev.com // dotfiles / lc.sh


Git Repos / dotfiles / bin / lc.sh

Last commit to this repo was on 2021-12-13 at 16:05.

#!/bin/sh
#
# Copyright (c) 2017-2021, Cameron Vanderzanden. Available under the
# terms of the 2-clause BSD license. Please see the file "LICENSE" in
# the root dir of this repository for more information.
#
#- lc.sh 2017/01/30 cev, last updated 2021/12/07.
## Usage: lc.sh [-1dDhxvV] <file or directory...>
##
## List directory contents with pretty (Gruvbox) colors. Requires FreeBSD
## stat(1). Should not be aliased as ls.
##
## I repeat: DO NOT USE AS A REPLACEMENT FOR LS.
##
## -1 Single column output.
## -d List directories as plain files.
## -D Don't group directories first.
## -h Show help.
## -v Verbose output (for debugging? purposes).
## -V Print version info.
## -x Sort multi-column output across rather than down the columns.
##
#

[ -x $HOME/bin/cev_print.sh ] && . $HOME/bin/cev_print.sh

self="${0##*'/'}"

if [ "$COLUMNS" ]; then
termwidth=$COLUMNS
else
termwidth=$(tput cols)
fi

entries=0
numcols=1
numrows=1
maxwidth="$termwidth"
inputfields=2

across=""
directories_as_files=""
group_directories_first="Y"
single_column=""
verbose=""

die_filenotfound() {
print_filenotfound "$*"
exit 1
}

print_name() {
local width
if [ $maxwidth -gt $termwidth ]; then
width=${#2}
else
width=$maxwidth
fi
printf "%b%-${width}s%b" "$1" "$2" "$3"
}

# gruvbox red
# #5E000A; 94; 0; 10
# #9D0006; 157; 0; 6
# #B01E18; 176; 30; 24
# #CC241D; 204; 36; 29
# #FB4934; 251; 73; 52
color_executable() {
if [ $st_size -lt 10000 ]; then # 10kb
f_color="\033[00;38;2;94;0;10m"
elif [ $st_size -lt 100000 ]; then # 100kb
f_color="\033[00;38;2;157;0;6m"
elif [ $st_size -lt 1000000 ]; then # 1mb
f_color="\033[00;38;2;176;30;24m"
elif [ $st_size -lt 10000000 ]; then # 10mb
f_color="\033[00;38;2;204;36;29m"
else
f_color="\033[00;38;2;251;73;52m"
fi
}

# gruvbox blue
# #83A598; 131; 165; 152
# #458588; 69; 133; 136
# #076678; 7; 102; 120
color_audio() {
if [ $st_size -gt 10000000 ]; then # 10 megs
f_color="\033[00;38;2;131;165;152m" # bright blue
elif [ $st_size -gt 1000000 ]; then # 1 meg
f_color="\033[00;38;2;69;133;136m" # blue
else
f_color="\033[00;38;2;7;102;120m" # faded blue
fi
}

# gruvbox greenish blue / cyan
# 24 #076678; 7; 102; 120
# 67 #458588; 69; 133; 136
# 109 #83A598; 131; 165; 152
# 152 #9EC7B7; 158; 199; 183
# 195 #BFF0DD; 191; 240; 221
color_video() {
if [ $st_size -lt 10000000 ]; then # 10 megs
f_color="\033[00;38;2;7;102;120m"
elif [ $st_size -lt 100000000 ]; then # 100 megs
f_color="\033[00;38;2;69;133;136m"
elif [ $st_size -lt 1000000000 ]; then # 1 gig
f_color="\033[00;38;2;131;165;152m"
elif [ $st_size -lt 2000000000 ]; then # 2 gig
f_color="\033[00;38;2;158;199;183m"
else
f_color="\033[00;38;2;191;240;221m"
fi
}

# gruvbox cyan
# 23 #044F20; 4; 79; 32
# 66 #427B58; 66; 123; 88
# 73 #689D6A; 104; 157; 106
# 108 #8EC07C; 142; 192; 124
# 151 #ABC7A1; 171; 199; 161
# 194 #BDD7B4; 189; 215; 180
color_image() {
if [ $st_size -lt 1000 ]; then # 1kb
f_color="\033[00;38;2;4;79;32m"
elif [ $st_size -lt 10000 ]; then # 10kb
f_color="\033[00;38;2;66;123;88m"
elif [ $st_size -lt 100000 ]; then # 100kb
f_color="\033[00;38;2;104;157;106m"
elif [ $st_size -lt 1000000 ]; then # 1 meg
f_color="\033[00;38;2;142;192;124m"
elif [ $st_size -lt 10000000 ]; then # 10 meg
f_color="\033[00;38;2;171;199;161m"
else
f_color="\033[00;38;2;189;215;180m"
fi
}

# gruvbox; very slightly reddish yellow
# 94 #784E0D; 120; 78; 13
# 136 #B57614; 181; 118; 20
# 178 #D79921; 215; 153; 33
# 214 #FABD2F; 250; 189; 47
# 226 #F0C645; 240; 198; 69
color_archive() {
if [ $st_size -lt 10000000 ]; then # 10 meg
f_color="\033[00;38;2;120;78;13m"
elif [ $st_size -lt 100000000 ]; then # 100 megs
f_color="\033[00;38;2;181;118;20m"
elif [ $st_size -lt 500000000 ]; then # 500 megs
f_color="\033[00;38;2;215;153;33m"
elif [ $st_size -lt 1000000000 ]; then # 1 gig
f_color="\033[00;38;2;250;189;47m"
else
f_color="\033[00;38;2;240;198;69m"
fi
}

# gruvbox; equal parts green and yellow
# 58 #4F4909; 79; 73; 9
# 100 #79740E; 121; 116; 14
# 142 #98971A; 152; 151; 26
# 184 #B8BB26; 184; 187; 38
# 226 #F0C645; 240; 198; 69
color_meta() {
if [ $st_size -lt 500 ]; then # 500 bytes
f_color="\033[00;38;2;79;73;9m"
elif [ $st_size -lt 1000 ]; then # 1kb
f_color="\033[00;38;2;121;116;14m"
elif [ $st_size -lt 100000 ]; then # 100kb
f_color="\033[00;38;2;152;151;26m"
else
f_color="\033[00;38;2;184;187;38m"
fi
}

# gruvbox; slightly reddish magenta
# 53 #4F223E; 79; 34; 62
# 96 #8F3F71; 143; 63; 113
# 133 #B16286; 177; 98; 134
# 175 #D3869B; 211; 134; 155
# 218 #F0A6BA; 240; 166; 186
color_code() {
if [ $st_size -lt 1000 ]; then # 1kb
f_color="\033[00;38;2;79;34;62m"
elif [ $st_size -lt 10000 ]; then # 10kb
f_color="\033[00;38;2;143;63;113m"
elif [ $st_size -lt 100000 ]; then # 100kb
f_color="\033[00;38;2;177;98;134m"
elif [ $st_size -lt 1000000 ]; then # 1 meg
f_color="\033[00;38;2;211;134;155m"
else
f_color="\033[00;38;2;240;166;186m"
fi
}

# gruvbox light greyscale - some new intermediary values
# 244 #928374; 146; 131; 116
# 248 #BDAE93; 189; 174; 147
# 249 #CABA9D; 202; 186; 157
# 250 #D5C4A1; 213; 196; 161
# 251 #E1CFAA; 225; 207; 170
# 252 #EBDBB2; 235; 219; 178
# 253 #F2E5BC; 242; 229; 188
# 254 #FBF1C7; 251; 241; 199
color_regular() {
if [ -z $st_size ]; then # empty file
f_color="\033[00;38;2;146;131;116m"
elif [ $st_size -eq 0 ]; then
f_color="\033[00;38;2;146;131;116m"
elif [ $st_size -lt 500 ]; then # 500 bytes
f_color="\033[00;38;2;189;174;147m"
elif [ $st_size -lt 1000 ]; then # 1kb
f_color="\033[00;38;2;202;186;157m"
elif [ $st_size -lt 10000 ]; then # 10kb
f_color="\033[00;38;2;213;196;161m"
elif [ $st_size -lt 100000 ]; then # 100kb
f_color="\033[00;38;2;225;207;170m"
elif [ $st_size -lt 1000000 ]; then # 1 meg
f_color="\033[00;38;2;235;219;178m"
elif [ $st_size -lt 10000000 ]; then # 10 meg
f_color="\033[00;38;2;242;229;188m"
else
f_color="\033[00;38;2;251;241;199m"
fi
}

color_file() {
# let's walk the plank
case "$shortname" in
# audio
*.[aA][cC][cC]) color_audio ;;
*.[aA][uU]) color_audio ;;
*.[aA][xX][aA]) color_audio ;;
*.[bB][mM][xX]) color_audio ;;
*.[fF][lL][aA][cC]) color_audio ;;
*.[iI][tT]) color_audio ;;
*.[mM][4][aA]) color_audio ;;
*.[mM][iI][dD]) color_audio ;;
*.[mM][iI][dD][iI]) color_audio ;;
*.[mM][kK][aA]) color_audio ;;
*.[mM][oO][dD]) color_audio ;;
*.[mM][pP]3) color_audio ;;
*.[mM][pP][cC]) color_audio ;;
*.[oO][gG][gG]) color_audio ;;
*.[rR][aA]) color_audio ;;
*.[sS]3[mM]) color_audio ;;
*.[sS]3[zZ]) color_audio ;;
*.[sS][tT][mM]) color_audio ;;
*.[sS][pP][xX]) color_audio ;;
*.[wW][aA][vV]) color_audio ;;
*.[xX][mM]) color_audio ;;
*.[xX][mM][zZ]) color_audio ;;
*.[xX][rR][nN][iI]) color_audio ;;
*.[xX][rR][nN][lL]) color_audio ;;
*.[xX][rR][nN][sS]) color_audio ;;
# video
*.[aA][vV][iI]) color_video ;;
*.[fF][lL][vV]) color_video ;;
*.[mM]4[vV]) color_video ;;
*.[mM][kK][vV]) color_video ;;
*.[mM][oO][vV]) color_video ;;
*.[mM][pP]4) color_video ;;
*.[mM][pP][gG]) color_video ;;
*.[mM][pP][eE][gG]) color_video ;;
*.[oO][gG][mM]) color_video ;;
*.[vV][oO][bB]) color_video ;;
*.[wW][eE][bB][mM]) color_video ;;
*.[wW][mM][vV]) color_video ;;
# images
*.[bB][mM][pP]) color_image ;;
*.[dD][dD][sS]) color_image ;;
*.[gG][iI][fF]) color_image ;;
*.[iI][cC][oO]) color_image ;;
*.[jJ][pP][gG]) color_image ;;
*.[jJ][pP][eE][gG]) color_image ;;
*.[mM][nN][gG]) color_image ;;
*.[nN][iI][fF]) color_image ;;
*.[pP][bB][mM]) color_image ;;
*.[pP][gG][mM]) color_image ;;
*.[pP][nN][gG]) color_image ;;
*.[pP][pP][mM]) color_image ;;
*.[sS][vV][gG]) color_image ;;
*.[sS][vV][gG][zZ]) color_image ;;
*.[tT][gG][aA]) color_image ;;
*.[tT][iI][fF]) color_image ;;
*.[tT][iI][fF][fF]) color_image ;;
*.[xX][bB][mM]) color_image ;;
*.[xX][cC][fF]) color_image ;;
*.[xX][pP][mM]) color_image ;;
# archive formats
*.7[zZ]) color_archive ;;
*.[aA][rR][jJ]) color_archive ;;
*.[bB][zZ]2) color_archive ;;
*.[cC][pP][iI][oO]) color_archive ;;
*.[dD][eE][bB]) color_archive ;;
*.[gG][pP][gG]) color_archive ;;
*.[gG][zZ]) color_archive ;;
*.[iI][mM][gG]) color_archive ;;
*.[iI][sS][oO]) color_archive ;;
*.[lL][zZ][hH]) color_archive ;;
*.[pP][kK]3) color_archive ;;
*.[rR][aA][rR]) color_archive ;;
*.[rR][pP][mM]) color_archive ;;
*.[tT][aA][rR]) color_archive ;;
*.[tT][gG][zZ]) color_archive ;;
*.[xX][zZ]) color_archive ;;
*.[zZ][iI][pP]) color_archive ;;
# "metadata" files
*.[aA][sS][sS]) color_meta ;;
*.[cC][uU][eE]) color_meta ;;
*.[dD][iI][zZ]) color_meta ;;
*.[eE][gG][gG]) color_meta ;;
*.[lL][oO][gG]) color_meta ;;
*.[mM]3[uU]) color_meta ;;
*.[mM]3[uU]8) color_meta ;;
*.[nN][fF][oO]) color_meta ;;
*.[rR][eE][aA][dD][mM][eE]) color_meta ;;
*.[sS][fF][vV]) color_meta ;;
*.[sS][rR][tT]) color_meta ;;
*.[tT][rR][aA][cC][kK][lL][iI][sS][tT]) color_meta ;;
# software source code
*.[aA]) color_code ;;
*.[bB][sS][pP]) color_code ;;
*.[cC]) color_code ;;
*.[cC][cC]) color_code ;;
*.[cC][pP][pP]) color_code ;;
*.[dD]) color_code ;;
*.[dD][iI][fF][fF]) color_code ;;
*.[hH]) color_code ;;
*.[hH]++) color_code ;;
*.[hH][hH]) color_code ;;
*.[hH][pP][pP]) color_code ;;
*.[hH][xX][xX]) color_code ;;
*.[lL][aA]) color_code ;;
*.[lL][pP][iI]) color_code ;;
*.[lL][uU][aA]) color_code ;;
*.[mM][aA][pP]) color_code ;;
*.[mM][kK]) color_code ;;
*.[oO]) color_code ;;
*.[pP][aA][sS]) color_code ;;
*.[pP][lL]) color_code ;;
*.[pP][yY]) color_code ;;
*.[pP][yY][cC]) color_code ;;
*.[pP][yY][oO]) color_code ;;
*.[qQ][cC]) color_code ;;
*.[qQ][hH]) color_code ;;
*.[sS][hH]) color_code ;;
*.[sS][rR][cC]) color_code ;;
Makefile) color_code ;;
# backup files
*.default) f_color="\033[00;38;2;124;111;100m" ;;
*.orig) f_color="\033[00;38;2;124;111;100m" ;;
*.backup|*.old|*.bak|*.back) f_color="\033[00;38;2;124;111;100m" ;;
*.backup1|*.old1|*.bak1|*.back1) f_color="\033[00;38;2;102;92;84m" ;;
*.backup2|*.old2|*.bak2|*.back2) f_color="\033[00;38;2;80;73;69m" ;;
*.backup3|*.old3|*.bak3|*.back3) f_color="\033[00;38;2;60;56;52m" ;;
*) color_regular ;;
esac
}

do_entry() {
local f_color f_clear
shortname="${st_name##*'/'}"
if [ -L "$st_name" ]; then # link
f_color="\033[01;37m"
elif [ -d "$st_name" ]; then # directory
f_color="\033[01;33m"
elif [ -b "$st_name" ]; then # block special
f_color="\033[00;38;2;254;128;25m" # 208 #FE8019; 254; 128; 25
elif [ -c "$st_name" ]; then # character special
f_color="\033[00;38;2;214;93;14m" # 172 #D65D0E; 214; 93; 14
elif [ -p "$st_name" ]; then # named pipe
f_color="\033[00;38;2;175;58;3m" # 130 #AF3A03; 175; 58; 3
elif [ -S "$st_name" ]; then # socket
f_color="\033[00;38;2;175;58;3m" # 130 #AF3A03; 175; 58; 3
elif [ -x "$st_name" ]; then # executable
color_executable
else
color_file
fi
f_clear="\033[0m"
print_name "$f_color" "$shortname" "$f_clear"
}

sort_directories_first() {
local i=1
newout=""
while [ $i -lt $# ]; do
eval st_name="\${$i}"
if [ -d "$st_name" ]; then
eval st_size="\${$((i + 1))}"
# yes, those are tab characters in the next line.
newout="${newout}${st_name} ${st_size} "
fi
i=$((i + inputfields))
done
i=1
while [ $i -lt $# ]; do
eval st_name="\${$i}"
if [ ! -d "$st_name" ]; then
eval st_size="\${$((i + 1))}"
# tab characters again.
newout="${newout}${st_name} ${st_size} "
fi
i=$((i + inputfields))
done
statout=${newout}
unset newout
}

find_widths() {
local i=1
maxwidth=1
while [ $i -lt $# ]; do
eval st_name="\${$i}"
shortname="${st_name##*'/'}"
[ ${#shortname} -gt $maxwidth ] && maxwidth=${#shortname}
i=$((i + inputfields))
done
maxwidth=$((maxwidth + 1))
entries=$#
entries=$((entries / inputfields))
if [ $maxwidth -ge $termwidth ]; then
numcols=1
else
numcols=$((termwidth / maxwidth))
fi
numrows=$((entries / numcols))
if [ $((entries % numcols)) -ne 0 ]; then
numrows=$((numrows + 1))
fi
}

find_widths_single() {
entries=$#
entries=$((entries / inputfields))
numrows=$entries
}

loop_across() {
local i=1
local col=1
while [ $i -lt $# ]; do
eval st_name="\${$i}"
eval st_size="\${$((i + 1))}"
do_entry
if [ $col -ge $numcols ]; then
printf "\n"
col=1
else
col=$((col + 1))
fi
i=$((i + inputfields))
done
[ $col -gt 1 ] && printf "\n"
}

# based on FreeBSD column(1), specifically /usr/src/usr.bin/column/column.c
# also available at https://cgit.freebsd.org/src/tree/usr.bin/column/column.c .
loop_down() {
local base=0
local row=1
local col=0
while [ $row -lt $((numrows * inputfields)) ]; do
col=0
base=$row
while [ $col -lt $numcols ]; do
eval st_name="\${$base}"
eval st_size="\${$((base + 1))}"
do_entry
base=$((base + (numrows * inputfields)))
col=$((col + 1))
done
printf "\n"
row=$((row + inputfields))
done
}

while getopts "1dDhvVx?" option; do
case $option in
1) single_column="Y" ;;
d) directories_as_files="Y" ;;
D) unset group_directories_first ;;
h) print_help && exit ;;
v) verbose="Y" ;;
V) print_version && exit ;;
x) across="Y" ;;
"?") print_help && exit ;;
esac
shift $((OPTIND - 1))
done

target="$*"
[ $# -lt 1 ] && target="."
[ ! -e "$target" ] && die_filenotfound "$target"

zIFS="$IFS" && IFS=$(printf "\n\t")
if [ -d "$target" -a -z "$directories_as_files" ]; then
statout=$(stat -f "%N%t%z%t" "$target"/* | sort -t " " -d -k1)
else
statout=$(stat -f "%N%t%z%t" "$target" | sort -t " " -d -k1)
fi
if [ -z "$single_column" ]; then
find_widths $statout
else
find_widths_single $statout
fi
[ "$group_directories_first" ] && sort_directories_first $statout
if [ "$verbose" ]; then
print_self
printf "entries: %s, columns: %s, rows: %s\n" \
"$entries" "$numcols" "$numrows"
fi
if [ "$across" ]; then
loop_across $statout
else
loop_down $statout
fi
IFS="$zIFS" && unset zIFS

Return to top of this page or return to the overview of this repository