fileset

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

commit 5fe2703c9df556e8dfed96ba6765fb288a647e48
parent 42b626be13aeb30c7b6db5be8e8273292a9cd8dc
Author: Jan Pobrislo <ccx@webprojekty.cz>
Date:   Thu, 26 Sep 2013 20:03:45 +0200

fslist prototype in zsh, fix escaping in fileset.awk
Diffstat:
Abin/fileset.awk | 258+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Abin/fslist | 92+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dfileset.awk | 237-------------------------------------------------------------------------------
3 files changed, 350 insertions(+), 237 deletions(-)

diff --git a/bin/fileset.awk b/bin/fileset.awk @@ -0,0 +1,258 @@ +#!/bin/awk -f +BEGIN { + fname="\"$fname\"" # constants so I don't have to write it out + dirname="\"$dirname\"" + or_die="|| exit $?" + + # header + print "#!/bin/sh" + print "export fname dirname" + print "die() { printf '%s\n' \"$*\"; exit 1 }" +} + +function escaped(str) { + gsub(/\\/, "\\\\", str) +# gsub(/\n/, "\\n", str) + gsub(/([\\"`$])/, "\\\\&", str) + return str +} + +function quoted(str) { + if(str ~ /^[a-zA-Z0-9_./]+$/) + return str + else + return "\"" escaped(str) "\"" +} + +function print_i(str) { # print with indent + print indent str +} + +function print_d(str) { # print or die + print indent str " " or_die +} + +function print_b(str) { # print begin block + print indent str + indent = indent "\t" +} + +function print_m(str) { # print mid-block deindented + print substr(indent, 2) str +} + +function print_e(str) { # print end block + indent = substr(indent, 2) + print indent str +} + +# take tab-delimited token from statement variable and return it +function get_till_tab( result) { + if(match(statement, /\t/)) { + result = substr(statement, 1, RSTART-1) + statement = substr(statement, RSTART+1) + } else { + result = statement + statement = "" + } + return result +} + +# common code to create file-like stuff +function cmd_mknod(test_cmd, err_msg_type, create_cmd) { + print_b("if ! "test_cmd"; then") + print_b("if test -e "fname"; then") + if(crest ~ /!/) { + if(crest ~ /f/) { + print_d("rm -rf "fname) + } else { + print_d("rm -r "fname) + } + } else { + print_i("die \"already present but not a "err_msg_type":\" "fname) + } + print_e("fi") + if(crest ~ /p/) { + print_d("mkdir -p "dirname) + } + if(create_cmd) { + print_d(create_cmd) + } + print_e("fi") +} + +function process_statement() { + while(statement) { + command = get_till_tab() + if(!command) continue + cchar = substr(command, 1, 1) + crest = substr(command, 2) + + # set current fname all subsequent operations will be performed on + if(cchar == "/") { + # fix up the path + if(match(crest, "/+$")) { + crest = substr(crest, 1, length(crest) - RLENGTH) + } + crest = "./" crest + + print "" + print_i("fname=" quoted(crest)) + match(crest, /.*\//) + print_i("dirname=" quoted(substr(crest, 1, RLENGTH-1))) + continue + } + + if(cchar == "o") { + print_d("chown "crest" "fname) + continue + } + + if(cchar == "m") { + print_d("chmod "crest" "fname) + continue + } + + if(cchar == "u") { + print_i("umask "crest) + continue + } + + # remove + if(cchar == "r") { + print_b("if test -e "fname"; then") + if(crest ~ /r/) { + if(crest ~ /f/) { + print_d("rm -rf "fname) + } else { + print_d("rm -r "fname) + } + } else { + } + if(crest ~ /f/) { + print_d("rm -f "fname) + } else { + print_d("rm "fname) + } + print_e("fi") + continue + } + + if(cchar == "f") { + cmd_mknod("test -f "fname, "file", "touch fname") + continue + } + + # needs special handling as we may not have readline + if(cchar == "l") { + link_dest = quoted(get_till_tab()) + print_b("if ! test -x /bin/readlink || ! test -l "fname" || test x\"$(readlink "fname")\" != x"link_dest"; then") + if(crest ~ /!/) { + print_b("if test -e "fname"; then") + } else { + print_b("if test -L "fname"; then") + } + if(crest ~ /f/) { + print_d("rm -f "fname) + } else { + print_d("rm "fname) + } + if(crest !~ /!/) { + print_m("else") + print_i("die \"already present but not a symbolic link:\" "fname) + } + print_e("fi") + if(crest ~ /p/) { + print_d("mkdir -p "dirname) + } + print_d("ln -s "link_dest" "fname) + print_e("fi") + continue + } + + # could be folded to mknod at the expense of superfluous mkdir -p + if(cchar == "d") { + print_b("if ! test -d "fname"; then") + print_b("if test -e "fname"; then") + if(crest ~ /!/) { + if(crest ~ /f/) { + print_d("rm -f "fname) + } else { + print_d("rm "fname) + } + } else { + print_i("die \"already present but not a directory:\" "fname) + } + print_e("fi") + if(crest ~ /p/) { + print_d("mkdir -p "fname) + } else { + print_d("mkdir "fname) + } + print_e("fi") + continue + } + + # cat, copy, content; eats rest of statement and puts it into the file + if(cchar == "c") { + cmd_mknod("test -f "fname, "file") + # unless disabled with the N flag, append newline at the end of + # last line, if not already present + printf_fmt = (crest !~ /N/ && statement !~ /\n$/) ? \ + "%s\n" : "%s" + printf_redir = (crest ~ /a/ ? ">>" : ">") fname + print_d("printf "printf_redir" '"printf_fmt"' "quoted(statement)) + statement = "" + continue + } + + # run shell command + if(cchar == "!") { + if(crest ~ /f/) { + if(crest ~ /c/) { + cmd_mknod("test -f "fname, "file", "touch "fname) + } else { + cmd_mknod("test -f "fname, "file") + } + print_d("{ "statement"\n} <"fname" >"fname".tmp.$$") + print_d("cat >"fname" "fname".tmp.$$") + print_d("rm "fname".tmp.$$") + } else if(crest ~ /i/) { + print_d("{ "statement"\n} <"fname) + } else if(crest ~ /o/) { + cmd_mknod("test -f "fname, "file") + print_d("{ "statement"\n} >"fname) + } else if(crest ~ /a/) { + cmd_mknod("test -f "fname, "file") + print_d("{ "statement"\n} >>"fname) + } else { + print_d("{ "statement"\n}") + } + statement = "" + continue + } + + # if none above matched + printf "unrecognised statement: %s\n", cchar crest statement >"/dev/stderr" + statement = "" + } +} + +function parse_line(line) { + if(!line) { # empty line, ignore + return + } + if(line ~ /^#/) { # comment, ignore + return + } + if(line ~ /^\t/) { # continuation, append to statement + statement = statement "\n" substr(line, 2) + } else { # new statement + process_statement() + statement = line + } +} + +{ parse_line($0) } + +END { process_statement() } diff --git a/bin/fslist b/bin/fslist @@ -0,0 +1,92 @@ +#!/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 +ftypes=( # convert hex type to mnemonic character + c S # socket + a l # symbolic link + 8 f # regular file + 6 B # block device + 4 d # directory + 2 C # 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='' +} + +for arg in "${@:-$PWD}"; do + for fname in $arg:a/**/*; do + zstat -LH s $fname || continue + ftype=$(( [##16] $s[mode] >> 12 )) + fmode=$(( [##8] $s[mode] & 8#7777 )) + t=$ftypes[$ftype] + + if [[ $fname == *$'\t'* || $fname == *$'\n'* ]]; then + statement $'P\t'$fname $'\t' + else + statement $fname + 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]=$fname + fi + fi + + if [[ $t == [BC] ]]; then + statement $t$(( $s[rdev] >> 8 )):$(( $s[rdev] & 255 )) + elif [[ $t == l ]]; then + statement $'l\t'$s[link] $'\t' + elif [[ $t == f ]]; then + IFS= read -r -d '' content <$fname + flags='' + if [[ $content == *$'\n' ]]; then + content=${content%$'\n'} + else + flags+=N + fi + statement c$flags$'\t'$content $'\n' + fi + statement o$s[uid]:$s[gid] + statement m$fmode + statement_end + done +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/ +# ' diff --git a/fileset.awk b/fileset.awk @@ -1,237 +0,0 @@ -#!/bin/awk -f -BEGIN { - fname="\"$fname\"" # constants so I don't have to write it out - dirname="\"$dirname\"" - or_die="|| exit $?" - - # header - print "#!/bin/sh" - print "export fname dirname" - print "die() { printf '%s' \"$*\"; exit 1 }" -} - -function escaped(str) { - gsub(/\\/, "\\\\", str) - gsub(/\n/, "\\n", str) - gsub(/\([\\"`$]\)/, "\\\1", str) - return str -} - -function quoted(str) { - if(str ~ /^[a-zA-Z0-9_./]+$/) - return str - else - return "\"" escaped(str) "\"" -} - -function print_i(str) { # print with indent - print indent str -} - -function print_d(str) { # print or die - print indent str " " or_die -} - -function print_b(str) { # print begin block - print indent str - indent = indent "\t" -} - -function print_m(str) { # print mid-block deindented - print substr(indent, 2) str -} - -function print_e(str) { # print end block - indent = substr(indent, 2) - print indent str -} - -# take tab-delimited token from statement variable and return it -function get_till_tab( result) { - if(match(statement, /\t/)) { - result = substr(statement, 1, RSTART-1) - statement = substr(statement, RSTART+1) - } else { - result = statement - statement = "" - } - return result -} - -# common code to create file-like stuff -function cmd_mknod(test_cmd, err_msg_type, create_cmd) { - print_b("if ! "test_cmd"; then") - print_b("if test -e "fname"; then") - if(crest ~ /!/) { - if(crest ~ /f/) { - print_d("rm -rf "fname) - } else { - print_d("rm -r "fname) - } - } else { - print_i("die \"already present but not a "err_msg_type":\" "fname) - } - print_e("fi") - if(crest ~ /p/) { - print_d("mkdir -p "dirname) - } - if(create_cmd) { - print_d(create_cmd) - } - print_e("fi") -} - -function process_statement() { - while(statement) { - command = get_till_tab() - if(!command) continue - cchar = substr(command, 1, 1) - crest = substr(command, 2) - - # set current fname all subsequent operations will be performed on - if(cchar == "/") { - # fix up the path - if(match(crest, "/+$")) { - crest = substr(crest, 1, length(crest) - RLENGTH) - } - crest = "./" crest - - print "" - print_i("fname=" quoted(crest)) - match(crest, /.*\//) - print_i("dirname=" quoted(substr(crest, 1, RLENGTH-1))) - continue - } - - if(cchar == "o") { - print_d("chown "crest" "fname) - continue - } - - if(cchar == "m") { - print_d("chmod "crest" "fname) - continue - } - - if(cchar == "f") { - cmd_mknod("test -f "fname, "file", "touch fname") - continue - } - - # needs special handling as we may not have readline - if(cchar == "l") { - link_dest = quoted(get_till_tab()) - print_b("if ! test -x /bin/readlink || ! test -l "fname" || test x\"$(readlink "fname")\" != x"link_dest"; then") - if(crest ~ /!/) { - print_b("if test -e "fname"; then") - } else { - print_b("if test -L "fname"; then") - } - if(crest ~ /f/) { - print_d("rm -f "fname) - } else { - print_d("rm "fname) - } - if(crest !~ /!/) { - print_m("else") - print_i("die \"already present but not a symbolic link:\" "fname) - } - print_e("fi") - if(crest ~ /p/) { - print_d("mkdir -p "dirname) - } - print_d("ln -s "link_dest" "fname) - print_e("fi") - continue - } - - # could be folded to mknod at the expense of superfluous mkdir -p - if(cchar == "d") { - print_b("if ! test -d "fname"; then") - print_b("if test -e "fname"; then") - if(crest ~ /!/) { - if(crest ~ /f/) { - print_d("rm -f "fname) - } else { - print_d("rm "fname) - } - } else { - print_i("die \"already present but not a directory:\" "fname) - } - print_e("fi") - if(crest ~ /p/) { - print_d("mkdir -p "fname) - } else { - print_d("mkdir "fname) - } - print_e("fi") - continue - } - - # cat, copy, content; eats rest of statement and puts it into the file - if(cchar == "c") { - cmd_mknod("test -f "fname, "file") - # unless disabled with the N flag, append newline at the end of - # last line, if not already present - if(crest !~ /N/ && statement !~ /\n$/) { - statement = statement "\n" - } - if(crest ~ /a/) { - print_d("printf >>"fname" '%s' "quoted(statement)) - } else { - print_d("printf >"fname" '%s' "quoted(statement)) - } - statement = "" - continue - } - - # run shell command - if(cchar == "!") { - if(crest ~ /f/) { - if(crest ~ /c/) { - cmd_mknod("test -f "fname, "file", "touch "fname) - } else { - cmd_mknod("test -f "fname, "file") - } - print_d("{ "statement"\n} <"fname" >"fname".tmp.$$") - print_d("cat >"fname" "fname".tmp.$$") - print_d("rm "fname".tmp.$$") - } else if(crest ~ /i/) { - print_d("{ "statement"\n} <"fname) - } else if(crest ~ /o/) { - cmd_mknod("test -f "fname, "file") - print_d("{ "statement"\n} >"fname) - } else if(crest ~ /a/) { - cmd_mknod("test -f "fname, "file") - print_d("{ "statement"\n} >>"fname) - } else { - print_d("{ "statement"\n}") - } - statement = "" - continue - } - - # if none above matched - printf "unrecognised statement: %s\n", cchar crest statement >"/dev/stderr" - statement = "" - } -} - -function parse_line(line) { - if(!line) { # empty line, ignore - return - } - if(line ~ /^#/) { # comment, ignore - return - } - if(line ~ /^\t/) { # continuation, append to statement - statement = statement "\n" substr(line, 2) - } else { # new statement - process_statement() - statement = line - } -} - -{ parse_line($0) } - -END { process_statement() }