add note about license
[small-misc-bash] / ll-function
1 #!/bin/bash
2 # I, Ian Kelling, follow the GNU license recommendations at
3 # https://www.gnu.org/licenses/license-recommendations.en.html. They
4 # recommend that small programs, < 300 lines, be licensed under the
5 # Apache License 2.0. This file contains or is part of one or more small
6 # programs. If a small program grows beyond 300 lines, I plan to switch
7 # its license to GPL.
8
9 # Copyright 2024 Ian Kelling
10
11 # Licensed under the Apache License, Version 2.0 (the "License");
12 # you may not use this file except in compliance with the License.
13 # You may obtain a copy of the License at
14
15 # http://www.apache.org/licenses/LICENSE-2.0
16
17 # Unless required by applicable law or agreed to in writing, software
18 # distributed under the License is distributed on an "AS IS" BASIS,
19 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 # See the License for the specific language governing permissions and
21 # limitations under the License.
22
23
24
25 ll() {
26 case $1 in
27 -h|--help)
28 cat <<'EOF'
29 usage: ll [ARGS_TO_LS]
30 ls -lA with enhanced output
31
32 * octal permissions
33 * omited acl type specifier
34 * better hard link count: number of subdirectories or the
35 number of linked files or omitted if it is 0
36 * better human readable size
37 * more natural date/time format for my American raised eyes
38 * remove first line size summary
39
40 EOF
41 ;;
42 esac
43 local x y perm line binls sizePadding middle tail size \
44 max_hl_digits hardlinks initial_space hardlink_spacing
45 local max_hl_digits=0
46 local -a lines hl
47 binls=$(type -P ls)
48 binlslink=$(readlink -f $binls)
49 if [[ $binlslink == */busybox ]]; then
50 $binls -lA "$@"
51 return
52 fi
53 local aclchar=false
54 # there's no way to tell if ls uses the acl specifier unless we loop over the data twice
55 # the 11th char is either
56 # . for selinux context
57 # + for any other kind of acl
58 # or blank for no other kind of acl
59 # I don't want to see this generally.
60 while read -r line; do
61 # if we did want the first line, it would need to be stripped of non-printing chars:
62 #line=${line#$'\E[00m'}
63 # lines like "total 123M", we don't want
64 if [[ ! $line =~ ^total\ [0-9][^\ ]*$ ]]; then
65 lines+=("$line")
66 if ! [[ $line == [-dscbl][-r][-w][-xsS][-r][-w][-xsS][-r][-w][-xtT]* ]]; then
67 hardlinks=
68 else
69 [[ ! ${line:10:1} == " " ]] && aclchar=true
70
71 # we also need to parse the hardlinks on the first pass, because for
72 # example ls could see the highest count as 11, and thus use 3
73 # places for hardlinks, " 10", but then we use 9 or 8 for a more
74 # useful count, and would then use 2 places. So we have to look
75 # through them all because we can't rely on the spacing that ls
76 # decided on.
77 y="${line:11}"
78 initial_space="${y%%[![:space:]]*}"
79 hardlinks="${y#$initial_space}" # remove any initial spaces
80 hardlinks="${hardlinks%%[[:space:]]*}" # remove everything beyond first word
81 # ignore the hardlinks that files/dirs always have
82 hardlinks=$(( hardlinks - 1 ))
83 [[ $hardlinks == 0 ]] && hardlinks=
84 if (( ${#hardlinks} > max_hl_digits )); then
85 max_hl_digits=${#hardlinks}
86 fi
87 fi
88 hl+=("$hardlinks")
89 fi
90 done< <( "$binls" -lAh --color=always "--time-style=+%m-%d %Y
91 %m-%d %I:%M %P" "$@" )
92
93 hardlink_spacing=$((max_hl_digits + 1))
94
95 for index in "${!lines[@]}"; do
96 line=${lines[index]}
97 hardlinks=${hl[index]}
98 if [[ $line != [-a-zA-Z][-r][-w][-xsS][-r][-w][-xsS][-r][-w][-xtT]* ]]; then
99 # line we don't understand
100 printf "%s\n" "$line"
101 continue
102 fi
103 perm=0
104 for (( x=0; x<=8; x++ )); do
105 y=${line:$(( -1*x + 9 )):1}
106 [[ $y == [tT] ]] && perm=$(( perm + 512 ))
107 if [[ $y == [sS] ]]; then
108 [[ $x == 3 ]] && perm=$(( perm + 1024 ))
109 [[ $x == 6 ]] && perm=$(( perm + 2048 ))
110 fi
111 [[ $y != [-ST] ]] && perm=$(( perm + 2**x ))
112 done
113 if $aclchar; then
114 y="${line:11}"
115 else
116 y="${line:10}"
117 fi
118 middle=${y#*[^ ]* }
119 size=${middle#*[^ ]* *[^ ]* }
120 middle=${middle%"$size"}
121 tail=${size#*[^ ]* }
122 size=${size%"$tail"}
123 declare -i sizePadding="${#size} - 1"
124 size=( $size ) # remove spaces
125 size=${size/.?/}
126
127 printf "%s%4o%${hardlink_spacing}s%s%${sizePadding}s%s\n" \
128 "${line:0:1}" $perm "$hardlinks" " $middle" "$size" " $tail"
129 done
130 }