fileset

git mirror of https://ccx.te2000.cz/bzr/fileset
git clone https://ccx.te2000.cz/git/fileset
Log | Files | Refs | README

fslist3 (4609B)


      1 #!/bin/zsh
      2 setopt no_unset warn_create_global no_multibyte
      3 zmodload zsh/stat
      4 
      5 typeset -gA ftypes hardlinks s
      6 typeset -g delim find fname ftype fmode
      7 
      8 ftypes=(  # convert hex type to mnemonic character
      9 	C  s  # socket
     10 	c  s
     11 	A  l  # symbolic link
     12 	a  l
     13 	8  f  # regular file
     14 	6  b  # block device
     15 	4  d  # directory
     16 	2  u  # character device
     17 	1  p  # FIFO
     18 )
     19 
     20 ### Defaults for options passed by env {{{1
     21 
     22 # TODO: make into command-line arguments
     23 : ${compact:=1}
     24 : ${print_m:=1}
     25 : ${print_o:=1}
     26 : ${print_c:=1}
     27 : ${print_s:=1}
     28 if (($+commands[file])) && (($+commands[base64])); then
     29 	: ${print_b:=1}
     30 else
     31 	: ${print_b:=0}
     32 	if (($+commands[file])) && (($+commands[xxd])); then
     33 		: ${print_x:=1}
     34 	fi
     35 fi
     36 : ${print_x:=0}
     37 : ${max_bin_size:=}
     38 : ${max_newline_size:=1024}
     39 
     40 ### Generic helpers {{{1
     41 
     42 die() {
     43 	printf '%s\n' "$@"
     44 	exit 1
     45 }
     46 
     47 ### FileSet writer functions {{{1
     48 
     49 statement() {
     50 	# start on new line for multiline statements - more readable
     51 	if [[ -n "$delim" && $1 == *$'\n'* ]]; then
     52 		delim=$'\n'
     53 	fi
     54 	printf '%s%s' $delim ${1//$'\n'/$'\n\t'}
     55 	delim=${2:-$'\t'}
     56 }
     57 
     58 statement_end() {
     59 	printf '\n'
     60 	delim=''
     61 }
     62 
     63 
     64 process_file() {
     65 	local -a find_info
     66 	local t fmode owner size filename
     67 	find_info=( $=1 )
     68 	t=$find_info[1]
     69 	fmode=$find_info[2]
     70 	owner=$find_info[3]
     71 	size=$find_info[4]
     72 
     73 	# --- print stuff ---
     74 
     75 	(($compact)) || printf '\n'
     76 
     77 	filename="$fname"
     78 	if [[ $filename == . ]]; then
     79 		filename=/
     80 	elif [[ $filename == ./* ]]; then
     81 		filename=$filename[2,-1]
     82 	fi
     83 	filename=${filename#/}
     84 
     85 	# printf "%s %s %s\n" >&2 $t $fmode "${(qqq)filename}"
     86 
     87 	if [[ $filename == *$'\t'* || $filename == *$'\n'* ]]; then
     88 		statement $'P\t'$filename $'\t'
     89 	else
     90 		if (($compact)); then
     91 			statement /${filename}
     92 		else
     93 			statement /${filename} $'\n'
     94 		fi
     95 	fi
     96 
     97 	# Note: not supporting hardlinks (yet)
     98 
     99 	if [[ $t == [bu] ]]; then
    100 		zstat -LH s $fname || die "stat failed on ${(qqq)fname}"
    101 		statement $t$(( $s[rdev] >> 8 )):$(( $s[rdev] & 255 ))
    102 	elif [[ $t == l ]]; then
    103 		zstat -LH s $fname || die "stat failed on ${(qqq)fname}"
    104 		statement $'l\t'$s[link] $'\t'
    105 	elif [[ $t == f ]]; then
    106 		if (($print_c)); then
    107 			process_file_data $size
    108 		else
    109 			statement f
    110 			if (($print_s)); then
    111 				statement s$'\tSHA512:'${"$(sha512sum <$fname)"%% *} \
    112 					|| die "Could not read ${(qqq)fname}"
    113 			fi
    114 		fi
    115 	else
    116 		statement $t
    117 	fi
    118 
    119 	(($print_o)) && statement o$owner
    120 	(($print_m)) && statement m$fmode
    121 	statement_end
    122 }
    123 
    124 process_file_data() {
    125 	local size
    126 	size=$1
    127 	if ! (($size)); then
    128 		statement cN$'\t'  # empty file
    129 		return
    130 	fi
    131 
    132 	# print binary representation?
    133 	if (($print_b | $print_x)) && \
    134 		[[ $(file -bi "$fname") != text/* ]]
    135 	then
    136 		if (($size > 256)); then
    137 			if [[ -n $max_bin_size && $size -gt $max_bin_size ]]; then
    138 				statement s$'\tSHA512:'${"$(sha512sum <$fname)"%% *} \
    139 					|| die "Could not read ${(qqq)fname}"
    140 			elif (($print_x)); then
    141 				if [[ -n "$delim" ]]; then
    142 					printf '\nX'
    143 				else
    144 					printf 'X'
    145 				fi
    146 				xxd <$fname | sed 's/^/\t/'
    147 				((${(j.|.)pipestatus})) && die "Could not read ${(qqq)fname}"
    148 				statement_end
    149 			else
    150 				if [[ -n "$delim" ]]; then
    151 					printf '\nB'
    152 				else
    153 					printf 'B'
    154 				fi
    155 				 base64 <$fname | sed 's/^/\t/'
    156 				 ((${(j.|.)pipestatus})) && die "Could not read ${(qqq)fname}"
    157 				statement_end
    158 			fi
    159 		else
    160 			if [[ -n $max_bin_size && $size -gt $max_bin_size ]]; then
    161 				statement s$'\tSHA512:'${"$(sha512sum <$fname)"%% *}
    162 			elif (($print_x)); then
    163 				statement X$'\t'"$(xxd <$fname)" $'\n'
    164 			else
    165 				statement B$'\t'"$(base64 <$fname)" $'\n'
    166 			fi
    167 		fi
    168 		return
    169 	fi
    170 	# print text
    171 
    172 	# if file is longer than this, always use CN
    173 	if (($size > $max_newline_size)); then
    174 		if [[ -n "$delim" ]]; then
    175 			printf '\nCN\t'
    176 		else
    177 			printf 'CN\t'
    178 		fi
    179 		# Swap NL with @ so trailing newline is handled correctly
    180 		tr <$fname '\n@' '@\n' \
    181 			| sed 's/@/@\t/g' \
    182 			| tr '@\n' '\n@'
    183 		((${(j.|.)pipestatus})) && die "Could not read ${(qqq)fname}"
    184 		statement_end
    185 		return
    186 	fi
    187 
    188 	# read and then print out, determinig trailing newline flags
    189 	local content flags
    190 	content="$(<$fname)"
    191 	flags=''
    192 	if [[ $content == *$'\n' ]]; then
    193 		content=${content%$'\n'}
    194 		if [[ $content == *$'\n' ]]; then
    195 			# force appending newline
    196 			flags+=n
    197 		fi
    198 	else
    199 		flags+=N
    200 	fi
    201 	if ! (($compact)) || [[ $content == *$'\t'* || $content == *$'\n'* ]]; then
    202 		statement C$flags$'\t'$content $'\n'
    203 	else
    204 		statement c$flags$'\t'$content
    205 	fi
    206 }
    207 
    208 ### Mainloop {{{1
    209 if (($+ROOT)) && [[ -n $ROOT ]]; then
    210 	cd $ROOT || exit $?
    211 fi
    212 
    213 find "$@" -printf '%y %m %U:%G %s\t%p\0' \
    214 	| sort -z -t $'\t' -k 2 \
    215 	| while IFS=$'\t' read -r -d $'\0' find fname
    216 do
    217 	process_file "$find" "$fname"
    218 done