commit 21be9e204e7eead1f67ce6cf454e71591b311954
parent 58e2b26694b1ed73d04e61722295653cc94957cb
Author: Jaromil <jaromil@dyne.org>
Date: Wed, 3 Aug 2011 23:58:14 -0700
Merge pull request #18 from boyska/only_optparsing
New CLI option parsing supports contextual options following commands.
ZSH functions are used to parse options. Further testing will follow.
Diffstat:
M | src/tomb | | | 267 | +++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------- |
M | src/tomb-open | | | 10 | +++++----- |
2 files changed, 179 insertions(+), 98 deletions(-)
diff --git a/src/tomb b/src/tomb
@@ -24,20 +24,28 @@ VERSION=1.1
DATE=May/2011
TOMBEXEC=$0
TOMBOPENEXEC="tomb-open"
+typeset -a OLDARGS
+for arg in ${argv}; do OLDARGS+=($arg); done
STEGHIDE=1
MOUNTOPTS="rw,noatime,nodev"
+#declare global variables
+QUIET=0
+DEBUG=0
+typeset -A global_opts
+typeset -A opts
+
# PATH=/usr/bin:/usr/sbin:/bin:/sbin
autoload colors; colors
# standard output message routines
# it's always useful to wrap them, in case we change behaviour later
-notice() { if ! [ $QUIET ]; then print "$fg_bold[green][*]$fg_no_bold[white] $1" >&2; fi }
-error() { if ! [ $QUIET ]; then print "$fg[red][!]$fg[white] $1" >&2; fi }
-func() { if [ $DEBUG ]; then print "$fg[blue][D]$fg[white] $1" >&2; fi }
+notice() { if [[ $QUIET == 0 ]]; then print "$fg_bold[green][*]$fg_no_bold[white] $1" >&2; fi }
+error() { if [[ $QUIET == 0 ]]; then print "$fg[red][!]$fg[white] $1" >&2; fi }
+func() { if [[ $DEBUG == 1 ]]; then print "$fg[blue][D]$fg[white] $1" >&2; fi }
act() {
- if ! [ $QUIET ]; then
+ if [[ $QUIET == 0 ]]; then
if [ "$1" = "-n" ]; then
print -n "$fg_bold[white] . $fg_no_bold[white] $2" >&2;
else
@@ -156,7 +164,7 @@ exec_as_user() {
# escalate privileges
check_priv() {
if [ $UID != 0 ]; then
- func "Using sudo for root execution of 'tomb ${(f)ARGS}'"
+ func "Using sudo for root execution of 'tomb ${(f)OLDARGS}'"
# check if sudo has a timestamp active
sudok=false
sudo -n ${TOMBEXEC} 2> /dev/null
@@ -165,12 +173,12 @@ check_priv() {
OPTION ttyname=$TTY
OPTION lc-ctype=$LANG
SETTITLE Super user privileges required
-SETDESC Sudo execution of Tomb ${ARGS[@]}
+SETDESC Sudo execution of Tomb ${OLDARGS[@]}
SETPROMPT Insert your USER password:
GETPIN
EOF
fi
- sudo "${TOMBEXEC}" ${(s: :)ARGS}
+ sudo "${TOMBEXEC}" "${(@)OLDARGS}"
exit $?
fi # are we root already
return 0
@@ -312,8 +320,8 @@ EOF
create_tomb() {
if ! [ ${CMD2} ]; then
- error "no tomb name specified for creation"
- return 1
+ error "no tomb name specified for creation"
+ return 1
fi
tombfile=`basename ${CMD2}`
@@ -321,6 +329,7 @@ create_tomb() {
# make sure the file has a .tomb extension
tombname=${tombfile%%\.*}
tombfile=${tombname}.tomb
+ tombsize=$opts[-s]
if [ -e ${tombdir}/${tombfile} ]; then
error "tomb exists already. I'm not digging here:"
@@ -336,17 +345,11 @@ create_tomb() {
notice "Creating a new tomb in ${tombdir}/${tombfile}"
- if [ -z $SIZE ]; then
- if [ $CMD3 ]; then
- tombsize=${CMD3}
- else
- act "No size specified, summoning the Tomb Undertaker to guide us in the creation."
- "$TOMBOPENEXEC" &
- wait $!
- return 0
- fi
- else
- tombsize=${SIZE}
+ if [ -z $tombsize ]; then
+ act "No size specified, summoning the Tomb Undertaker to guide us in the creation."
+ "$TOMBOPENEXEC" &
+ wait $!
+ return 0
fi
tombsize_4k=`expr $tombsize \* 1024 / 4`
@@ -480,38 +483,45 @@ create_tomb() {
mount_tomb() {
notice "Commanded to open tomb $CMD2"
get_arg_tomb $CMD2
+ local tombkey
+ if option_is_set -k ; then
+ tombkey=`option_value -k`
+ else
+ tombkey="${PARAM[1]}.key"
+ fi
+ echo the key used is $tombkey
if [ $? != 0 ]; then
- error "operation aborted."
- return 1
+ error "operation aborted."
+ return 1
fi
if ! [ $CMD3 ]; then
- tombmount=/media/${tombfile}
- act "mountpoint not specified, using default: $tombmount"
+ tombmount=/media/${tombfile}
+ act "mountpoint not specified, using default: $tombmount"
elif ! [ -x $CMD3 ]; then
- error "mountpoint $CMD3 doesn't exist, operation aborted."
- if [ -n "$usbkey_mount" ]; then
- umount $usbkey_mount
- rmdir $usbkey_mount
- unset usbkey_mount
- fi
- return 1
+ error "mountpoint $CMD3 doesn't exist, operation aborted."
+ if [ -n "$usbkey_mount" ]; then
+ umount $usbkey_mount
+ rmdir $usbkey_mount
+ unset usbkey_mount
+ fi
+ return 1
else
- tombmount=$CMD3
+ tombmount=$CMD3
fi
# check if its already open
mount -l | grep "${tombname}.tomb.*\[$tombname\]$" 2>&1 > /dev/null
if [ $? = 0 ]; then
- error "$tombname is already mounted on $tombmount"
- act "tomb list - show all tombs currently open"
- if [ -n "$usbkey_mount" ]; then
- umount $usbkey_mount
- rmdir $usbkey_mount
- unset usbkey_mount
- fi
- error "operation aborted."
- return 1
+ error "$tombname is already mounted on $tombmount"
+ act "tomb list - show all tombs currently open"
+ if [ -n "$usbkey_mount" ]; then
+ umount $usbkey_mount
+ rmdir $usbkey_mount
+ unset usbkey_mount
+ fi
+ error "operation aborted."
+ return 1
fi
notice "mounting $tombfile on mountpoint $tombmount"
@@ -522,18 +532,18 @@ mount_tomb() {
nstloop=`losetup -f`
if [ $? = 255 ]; then
- error "too many tomb opened. Please close any of them to open another tomb"
- exit 1
+ error "too many tomb opened. Please close any of them to open another tomb"
+ exit 1
fi
losetup -f ${tombdir}/${tombfile}
act "check for a valid LUKS encrypted device"
cryptsetup isLuks ${nstloop}
if [ $? != 0 ]; then
- # is it a LUKS encrypted nest? see cryptsetup(1)
- error "$tombfile is not a valid Luks encrypted storage file"
- $norm || rmdir $tombmount 2>/dev/null
- return 1
+ # is it a LUKS encrypted nest? see cryptsetup(1)
+ error "$tombfile is not a valid Luks encrypted storage file"
+ $norm || rmdir $tombmount 2>/dev/null
+ return 1
fi
# save date of mount in minutes since 1970
@@ -562,10 +572,10 @@ mount_tomb() {
done
if ! [ -r /dev/mapper/${mapper} ]; then
- error "failure mounting the encrypted file"
- losetup -d ${nstloop}
- $norm || rmdir ${tombmount} 2>/dev/null
- return 1
+ error "failure mounting the encrypted file"
+ losetup -d ${nstloop}
+ $norm || rmdir ${tombmount} 2>/dev/null
+ return 1
fi
act "encrypted storage filesystem check"
@@ -582,8 +592,8 @@ mount_tomb() {
notice "encrypted storage $tombfile succesfully mounted on $tombmount"
if ! [ $NOBIND ]; then
- exec_safe_bind_hooks ${tombmount}
- exec_safe_post_hooks ${tombmount} open
+ exec_safe_bind_hooks ${tombmount}
+ exec_safe_post_hooks ${tombmount} open
fi
return 0
}
@@ -689,6 +699,9 @@ print "-----END PGP MESSAGE-----"
}
exec_safe_bind_hooks() {
+ if [[ -n ${(k)opts[-o]} ]]; then
+ MOUNTOPTS=${opts[-o]}
+ fi
local MOUNTPOINT="${1}"
local ME=${SUDO_USER:-$(whoami)}
local HOME=$(grep $ME /etc/passwd | sed "s/^${ME}:.*:.*:.*:.*:\([\/a-z]*\):.*$/\1/" 2>/dev/null)
@@ -1061,53 +1074,121 @@ EOF
act "Tomb is now installed."
}
-main () {
- echo $@ | grep '\-D' 2>&1 > /dev/null
- # ?????
- if [ $? = 0 ]; then
+option_is_set() {
+ #First argument, the option (something like "-s")
+ #Second (optional) argument: if it's "out", command will print it out 'set'/'unset'
+ # This is useful for if conditions
+ #Return 0 if is set, 1 otherwise
+ [[ -n ${(k)opts[$1]} ]];
+ r=$?
+ if [[ $2 == out ]]; then
+ if [[ $r == 0 ]]; then
+ echo 'set'
+ else
+ echo 'unset'
+ fi
fi
+ return $r;
+}
+option_value() {
+ #First argument, the option (something like "-s")
+ echo ${opts[$1]}
+}
- ARGS=$@[@]
-
- OPTS=`getopt -o hvqDs:k:no: -n 'tomb' -- "$@"`
- while true; do
- case "$1" in
- -h)
- usage
- exit 0 ;;
- -v)
- notice "Tomb - simple commandline tool for encrypted storage"
- act "version $VERSION ($DATE) by Jaromil @ dyne.org"
- # print out the GPL license in this file
- act ""
- cat ${TOMBEXEC} | awk 'BEGIN { license=0 } /^# This source/ { license=1 } { if(license==1) print " " $0 }
-/MA 02139, USA.$/ { license=0 }'
- act ""
- exit 0 ;;
- -q) QUIET=1; shift 1 ;;
- -D)
- print "[D] Tomb invoked with args \"${(f)@}\" "
- print "[D] running on `date`"
- DEBUG=1; shift 1 ;;
- -s) SIZE=$2; shift 2 ;;
- -k) KEY=$2; shift 2 ;;
- -n) NOBIND=1; shift 1 ;;
- -o) MOUNTOPTS=$2; shift 2;;
- --) shift; break ;;
- *) CMD=$1;
- FILE=$2; MOUNT=$3; # compat with old args
- CMD2=${2}; CMD3=${3}; break ;;
- esac
+main() {
+ local -A subcommands_opts
+ ### Options configuration
+ #Hi, dear developer! Are you trying to add a new subcommand, or to add some options?
+ #Well, keep in mind that:
+ # 1. An option CAN'T have differente meanings/behaviour in different subcommands.
+ # For example, "-s" means "size" and accept an argument. If you are tempted to add
+ # an option "-s" (that means, for example "silent", and doesn't accept an argument)
+ # DON'T DO IT!
+ # There are two reasons for that:
+ # I. usability; user expect that "-s" is "size
+ # II. Option parsing WILL EXPLODE if you do this kind of bad things
+ # (it will say "option defined more than once, and he's right)
+ main_opts=(q -quiet=q D -debug=D h -help=h v -verbose=v)
+ subcommands_opts[open]="n -nohook=n k: -key=k o: -mount-options=o"
+ subcommands_opts[mount]=${subcommands_opts[open]}
+ subcommands_opts[create]="s: -size=s"
+ subcommands_opts[close]=""
+ subcommands_opts[help]=""
+ subcommands_opts[slam]=""
+ subcommands_opts[list]=""
+ subcommands_opts[help]=""
+ subcommands_opts[bury]=""
+ subcommands_opts[exhume]=""
+ subcommands_opts[decompose]=""
+ subcommands_opts[recompose]=""
+ subcommands_opts[install]=""
+ subcommands_opts[askpass]=""
+ subcommands_opts[mktemp]=""
+ ### Detect subcommand
+ local -aU every_opts #every_opts behave like a set; that is, an array with unique elements
+ for optspec in $subcommands_opts$main_opts; do
+ for opt in ${=optspec}; do
+ every_opts+=${opt}
+ done
done
-
- if ! [ $CMD ]; then
- error "first argument missing, use -h for help"
- exit 0
+ local -a oldstar
+ oldstar=($argv)
+ zparseopts -M -E -D -Adiscardme ${every_opts}
+ unset discardme
+ subcommand=$1
+ if [[ -z ${(k)subcommands_opts[$subcommand]} ]]; then #there's no such subcommand
+ error "Subcommand '$subcommand' doesn't exist"
+ exit 127
fi
+ argv=(${oldstar})
+ unset oldstar
+
+ ### Parsing global + command-specific options
+ # zsh magic: ${=string} will split to multiple arguments when spaces occur
+ set -A cmd_opts ${main_opts} ${=subcommands_opts[$subcommand]}
+ if [[ -n $cmd_opts ]]; then #if there is no option, we don't need parsing
+ zparseopts -M -E -D -Aopts ${cmd_opts}
+ if [[ $? != 0 ]]; then
+ error "Some error occurred during option processing. See \"tomb help\" for more info"
+ exit 127
+ fi
+ fi
+ #build PARAM (array of arguments) and check if there are unrecognized options
+ ok=0
+ PARAM=()
+ for arg in $*; do
+ if [[ $arg == '--' || $arg == '-' ]]; then
+ ok=1
+ continue #it shouldnt be appended to PARAM
+ elif [[ $arg[1] == '-' ]]; then
+ if [[ $ok == 0 ]]; then
+ error "unrecognized option $arg"
+ exit 127
+ fi
+ fi
+ PARAM+=$arg
+ done
+ #first parameter actually is the subcommand: delete it and shift
+ PARAM[1]=()
+ shift
+ ### End parsing command-specific options
+
+ ### Set global options (useless, but for code retro-compatibility)
+ for opt in ${(k)global_opts}; do
+ if [[ $opt == '-q' ]]; then
+ QUIET=1
+ elif [[ $opt == '-D' ]]; then
+ DEBUG=1
+ fi
+ done
+
+ CMD=$subcommand
+ CMD2=$PARAM[1]
+ CMD3=$PARAM[2]
func "Tomb command: $CMD $CMD2 $CMD3"
- case "$CMD" in
+ case "$subcommand" in
create) check_priv ; create_tomb ;;
mount) check_priv ; mount_tomb ;;
open) check_priv ; mount_tomb ;;
diff --git a/src/tomb-open b/src/tomb-open
@@ -211,7 +211,7 @@ if [ $1 ]; then # is it a file?
exit 1
else
- "${TOMBEXEC}" -k ${tombkey} mount ${tombdir}/${tombfile}
+ "${TOMBEXEC}" mount -k ${tombkey} ${tombdir}/${tombfile}
success=$?
fi
@@ -255,7 +255,7 @@ if [ "$1" != "wizard" ]; then
if [ -z $DISPLAY ]; then
error "tomb-open is a wrapper for the command 'tomb'"
error "[!] type 'tomb-open wizard' if you want to be guided"
- "${TOMBEXEC}" -h
+ "${TOMBEXEC}" help
exit 1
fi
fi
@@ -338,7 +338,7 @@ cat <<EOF
EOF
tombfile=${tombname}.tomb
-"${TOMBEXEC}" -s $tombsize create ${tombfile}
+"${TOMBEXEC}" create -s $tombsize ${tombfile}
if [ $? != 0 ]; then
error "An error occurred creating tomb, operation aborted."
@@ -368,7 +368,7 @@ if [ $? = 0 ]; then
notice "${tombname}.key succesfully saved on your USB"
act "now we'll proceed opening your brand new tomb"
- "${TOMBEXEC}" -k ${tombfile}.key open ${tombfile}
+ "${TOMBEXEC}" open -k ${tombfile}.key ${tombfile}
if [ $? = 0 ]; then
launch_status ${tombname}
fi
@@ -392,7 +392,7 @@ cat <<EOF
EOF
-"${TOMBEXEC}" -k ${tombname}.tomb.key open ${tombfile}
+"${TOMBEXEC}" open -k ${tombname}.tomb.key ${tombfile}
if [ $? = 0 ]; then
launch_status ${tombname}
fi