#!/bin/zsh
# there is no posix stat(1) and the implementations are riddled with locale-speciffic stuff
# better use sane version from zsh
setopt extendedglob globdots
zmodload zsh/stat
typeset -A ftypes hardlinks s
ftypes=( # convert hex type to mnemonic character
C s # socket
c s
A l # symbolic link
a l
8 f # regular file
6 b # block device
4 d # directory
2 u # character device
1 p # FIFO
)
delim=''
statement() {
# start on new line for multiline statements - more readable
if [[ -n delim && $1 == *$'\n'* ]]; then
delim=$'\n'
fi
printf '%s%s' $delim ${1//$'\n'/$'\n\t'}
delim=${2:-$'\t'}
}
statement_end() {
printf '\n'
delim=''
}
ROOT=${${${ROOT:-$PWD}:A}%/}
: ${compact:=1}
: ${print_m:=1}
: ${print_o:=1}
: ${print_c:=1}
if (($+commands[file])) && (($+commands[base64])); then
: ${print_b:=1}
else
: ${print_b:=0}
if (($+commands[file])) && (($+commands[xxd])); then
: ${print_x:=1}
fi
fi
: ${print_x:=0}
: ${max_bin_size:=}
fnames=( )
for arg in "${@:-$ROOT}"; do
fnames+=( $arg:a )
[[ -d $arg ]] && fnames+=( $arg:a/**/* )
done
for fname in $fnames; do
if ! [[ $fname == $ROOT || $fname == $ROOT/* ]]; then
printf "skipping out-of-root file: %s\n" "$fname" >&2
continue
fi
relname=${${fname#$ROOT}#/}
zstat -LH s $fname || continue
ftype=$(( [##16] $s[mode] >> 12 ))
fmode=$(( [##8] $s[mode] & 8#7777 ))
t=$ftypes[$ftype]
(($compact)) || printf '\n'
if [[ $relname == *$'\t'* || $relname == *$'\n'* ]]; then
statement $'P\t'$relname $'\t'
else
if (($compact)); then
statement /$relname
else
statement /$relname $'\n'
fi
fi
if [[ $t != d && $s[nlink] -gt 1 ]]; then
id=$s[device]:$s[inode]
if (($+hardlinks[$id])); then
statement $'H\t'$hardlinks[$id] $'\n'
continue
else
hardlinks[$id]=./$relname
fi
fi
if [[ $t == [bu] ]]; then
statement $t$(( $s[rdev] >> 8 )):$(( $s[rdev] & 255 ))
elif [[ $t == l ]]; then
statement $'l\t'$s[link] $'\t'
elif [[ $t == f ]]; then
if (($print_c)); then
if (($s[size])) && (($print_b + $print_x)) && \
[[ $(file -bi $fname) != text/* ]]; then
if [[ -n $max_bin_size && $s[size] -gt $max_bin_size ]]; then
statement s$'\tSHA512:'${"$(sha512sum < $fname)"%% *}
elif (($print_x)); then
statement X$'\t'"$(xxd $fname)" $'\n'
else
statement B$'\t'"$(base64 <$fname)" $'\n'
fi
else
IFS= read -r -d '' content <$fname
flags=''
if [[ $content == *$'\n' ]]; then
content=${content%$'\n'}
if [[ $content == *$'\n' ]]; then
# force appending newline
flags+=n
fi
else
flags+=N
fi
if ! (($compact)) || [[ $content == *$'\t'* || $content == *$'\n'* ]]; then
statement C$flags$'\t'$content $'\n'
else
statement c$flags$'\t'$content
fi
fi
else
statement f
fi
else
statement $t
fi
(($print_o)) && statement o$s[uid]:$s[gid]
(($print_m)) && statement m$fmode
statement_end
done
# while getopts omcaOMC o
# do case "$o" in
# ([?])
# print >&2 "Usage: $(basename "$0") [-omcaOMC] [dir [...]]"
# exit 1;;
# esac
# done
# find "${@:-$PWD}" -exec stat -c '%F %U:%G %a %h:%i:%d %t:%T %n' '{}' + | awk '
# BEGIN {
# FS="\t"
# }
# /\t/
# '