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:
| A | bin/fileset.awk | | | 258 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | bin/fslist | | | 92 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| D | fileset.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() }