# vim: ft=zsh noet ts=4 sts=4 sw=4 # # confz functions for file hierarchies and fileset miniformat # zmodload -F zsh/stat b:zstat zmodload -m -F zsh/files b:zf_\* typeset -g -A fileset_stat_cache fileset_stat_cur fileset_ftypes fileset_typenames typeset -g fileset_stat_cur fileset_stat_next_id fileset_stat_cur_type fileset_stat_cur_perm fileset_stat_cur_major fileset_stat_cur_minor fileset_ftypes=( # convert hex type to a word C S # unix socket c S A L # symlink, could also be 'h' a L 8 f # plain file 6 b # block device file 4 d # directory 2 c # character device file 1 p # FIFO pipe ) fileset_typenames=( S "unix socket" L "symbolic link" f "plain file" b "block device" c "character device" p "named pipe" ) fileset_reset_cache(){ # omit unsetting used variables for now # probably no particular gain in freeing them fileset_stat_cache=() fileset_stat_cur=() fileset_stat_next_id=1 } fileset_reset_cache # runs zstat on $1 if not already in cache # the result is available as $fileset_stat_cur associative array fileset_stat() { local id ret ftype fperm id=${fileset_stat_cache[$1]:-missing} if [[ $id == missing ]]; then typeset -gA fileset_stat_$fileset_stat_next_id zstat -L -H fileset_stat_$fileset_stat_next_id $1 &>/dev/null ret=$? if (($ret == 0)); then fileset_stat_cache[$1]=$fileset_stat_next_id fileset_stat_set_cur $fileset_stat_next_id fileset_stat_next_id=$[ $fileset_stat_next_id + 1 ] else fileset_stat_cache[$1]=-$ret fi return $ret elif (( $id >= 0 )); then fileset_stat_set_cur $id return 0 else return $[ -($id) ] fi } fileset_stat_set_cur(){ local param ftype param=fileset_stat_$1 fileset_stat_cur=( "${(@kvP)param}" ) ftype=$[ [##16] $fileset_stat_cur[mode] >> 12 ] fileset_stat_cur_type=$fileset_ftypes[$ftype] fileset_stat_cur_perm=$[ [##8] $fileset_stat_cur[mode] & 4095 ] fileset_stat_cur_major=$[ $fileset_stat_cur[rdev] >> 8 ] fileset_stat_cur_minor=$[ $fileset_stat_cur[rdev] & 255 ] } fileset_resetcmd() { fileset_reset_cache "$@" } confz_fs_p_check() { checkvars filename local parent parent=${${vars[filename]}:h} do_command=( fileset_resetcmd mkdir -p $parent ) fileset_stat $parent && [[ $fileset_stat_cur_type == d ]] } confz_fs_type_or_missing_check() { checkvars filename flags filetype if fileset_stat $vars[filename]; then if [[ $fileset_stat_cur_type == $vars[filetype] ]]; then return 0 elif [[ $vars[flags] == *'!'* ]]; then fail_reason="expected ${fileset_typenames[${vars[filetype]}]}, got ${fileset_typenames[$fileset_stat_cur_type]}: ${(qqq)vars[filename]}" return 1 else die "$0: expected ${fileset_typenames[${vars[filetype]}]}, got ${fileset_typenames[$fileset_stat_cur_type]}: ${(qqq)vars[filename]}" fi else return 0 fi } confz_fs_type_or_missing_do() { local is_dir if fileset_stat $vars[filename] && [[ $fileset_stat_cur_type == d ]]; then is_dir=1 else is_dir=0 fi fileset_reset_cache if [[ $vars[flags] == *r* ]]; then if [[ $vars[flags] == *f* ]]; then zf_rm -rf $vars[filename] else zf_rm -r $vars[filename] fi elif (($is_dir)); then zf_rmdir $vars[filename] else if [[ $vars[flags] == *f* ]]; then zf_rm -f $vars[filename] else zf_rm $vars[filename] fi fi } confz_fs_l_check() { checkvars filename destination defvar flags '' if [[ $vars[flags] == *p* ]]; then require fs_p :filename fi require fs_type_or_missing :filename :flags filetype=L fileset_stat $vars[filename] && \ [[ $fileset_stat_cur_type == L ]] && \ [[ $fileset_stat_cur[link] == $vars[destination] ]] } confz_fs_l_do() { local missing fileset_stat $vars[filename] missing=$? fileset_reset_cache if ! (($missing)); then rm $vars[filename] || return $? fi zf_ln -sh $vars[destination] $vars[filename] } confz_fs_device_check() { checkvars filename device_type major minor defvar flags '' if [[ $vars[flags] == *p* ]]; then require fs_p :filename fi case $vars[device_type] in (c) require fs_type_or_missing :filename :flags filetype=c ;; (b) require fs_type_or_missing :filename :flags filetype=b ;; (*) die "Incorrect device type: ${(qqq)vars[device_type]}" ;; esac fileset_stat $vars[filename] && \ [[ $fileset_stat_cur_type == $vars[device_type] ]] && \ [[ $fileset_stat_cur_major == $vars[major] ]] && \ [[ $fileset_stat_cur_minor == $vars[minor] ]] } confz_fs_device_do() { local missing fileset_stat $vars[filename] missing=$? fileset_reset_cache if ! (($missing)); then rm $vars[filename] || return $? fi mknod $vars[filename] $vars[device_type] $vars[major] $vars[minor] } confz_fs_pipe_check() { checkvars filename defvar flags '' if [[ $vars[flags] == *p* ]]; then require fs_p :filename fi require fs_type_or_missing :filename :flags filetype=p do_command=( fileset_resetcmd mkfifo $vars[filename] ) fileset_stat $vars[filename] && [[ $fileset_stat_cur_type == p ]] } confz_fs_r_check() { checkvars filename defvar flags '' do_command=( confz_fs_type_or_missing_do ) ! fileset_stat $vars[filename] } confz_fs_m_check() { checkvars filename mode do_command=( fileset_resetcmd chmod $vars[mode] $vars[filename] ) fileset_stat $vars[filename] && (( $fileset_stat_cur_perm == $vars[mode] )) } confz_fs_o_check() { checkvars filename owner do_command=( fileset_resetcmd zf_chown -h $vars[owner] $vars[filename] ) fileset_stat $vars[filename] || \ die "fs_o: could not access file ${(qqq)vars[filename]}" if [[ $vars[owner] =~ '^[0-9]+:[0-9]+$' ]]; then (( $fileset_stat_cur[uid] == ${${vars[owner]}%:*} && \ $fileset_stat_cur[gid] == ${${vars[owner]}#*:} )) elif [[ $vars[owner] =~ '^[0-9]+$' ]]; then (( $fileset_stat_cur[uid] == ${${vars[owner]}%:*} )) else die "fs_o does not support non-numeric user/group: ${(qqq)vars[owner]}" fi } confz_fs_f_check() { checkvars filename defvar flags '' if [[ $vars[flags] == *p* ]]; then require fs_p :filename fi require fs_type_or_missing :filename :flags filetype=f fileset_stat $vars[filename] && [[ $fileset_stat_cur_type == f ]] } confz_fs_f_do() { fileset_reset_cache printf '' >> $vars[filename] } confz_fs_d_check() { checkvars filename defvar flags '' require fs_type_or_missing :filename :flags filetype=d if [[ $vars[flags] == *p* ]]; then do_command=( fileset_resetcmd mkdir -p $vars[filename] ) else do_command=( fileset_resetcmd mkdir $vars[filename] ) fi fileset_stat $vars[filename] && [[ $fileset_stat_cur_type == d ]] } confz_fs_c_check() { checkvars filename content_call defvar flags '' require fs_f :filename :flags "${(Q@)${(z)vars[content_call]}}" | cmp -s - $vars[filename] } confz_fs_c_do() { "${(Q@)${(z)vars[content_call]}}" > $vars[filename] } confz_fs_contentnl_check() { checkvars filename content defvar flags '' require fs_f :filename :flags printf '%s\n' "$vars[content]" | cmp -s - $vars[filename] } confz_fs_contentnl_do() { printf '%s\n' "$vars[content]" > $vars[filename] }