#!/bin/dash
#! also ash,bash,sh
# Poor man's sudo. No facility to avoid asking for password. It will __always__ ask for password.
# (C) James Budiono 2020
# License: GPL version 3 or later
# 20200628: step:
# - split out run-as-root section and rewrite
# - parse all sudo options for broader compatibility
# - unit test script tests/run-as-sudo.sh

[ ${SUDO_TEST:-0} -gt 1 ] && set -x

# In: $@
# Out: some $opt_<long_name> global variables.
# Out: command arguments in $CMD_ARGS.
# Each argument in $CMD_ARGS is quoted with exterior single quotes.
# The implemented sudo option spec is documented at EOF.
set_CMD_ARGS() {
# Take action on a small subset of sudo options.
[ "${SUDO_TEST:-0}" -gt 0 ] && printf "\033[31m(%s)\033[0m\n" "\$@" "$@" >&2
unset CMD_ARGS opt_help opt_user # global
local N get_2dash got_2dash=0 err

# Does "--" separate sudo options from command arguments?
get_2dash=${*%%\"*}; get_2dash=${*%%\'*}
case " $get_2dash " in *" -- "*) get_2dash=1 ;; *) unset get_2dash ;; esac

### In dash we need to shift with some care because 'shift N' with N > $# exits 2 unconditionally.

while true; do
	case "$1" in
	-A|--askpass) N=1;;
	-B|--bell) N=1;;
	-b|--background) N=1;;
	-C) N=2;;
	--close-from=*) N=1;;
	-E|--preserve-env|--preserve-env=*) N=1;;
	-e|--edit) N=1;;
	-g|--group) N=2;;
	-H|--set-home) N=1;;
	--help) opt_help=1; N=1;;
	-h) : "-h(help) or -h host"
		case $2 in
			''|--) opt_help=1; N=1;;
			*) N=2;;
		esac
		;;
	--host=*) N=1;;
	-i|--login) N=1;;
	-K|--remove-timestamp) N=1;;
	-k|--reset-timestamp) N=1;;
	-l|--list) N=1;;
	-n|--non-interactive) N=1;;
	-P|--preserve-groups) N=1;;
	-p|--prompt) N=2;;
	-r|--role) N=2;;
	-S|--stdin) N=1;;
	-s|--shell) N=1;;
	-t|--type) N=2;;
	-U|--other-user) N=2;;
	-T|--command-timeout) N=2;;
	-u|--user) opt_user="${2#\#}"; N=2;;
	-V|--version) N=1;;
	-v|--validate) N=1;;
	--) got_2dash=1; N=1; break;;
	-*) err="invalid sudo option: $1" N=1 ret=1; break;;
	* ) break;;
	esac

	# flag error if we can't shift $N
	if [ $N -le $# ]; then
		shift $N
	else
		ret=1
		break
	fi
done
# flag error if we didn't parse the "--" that was there
[ "$get_2dash" -a 1 != "$got_2dash" ] && ret=1

if [ 0 != ${ret:-0} ]; then
	echo "${0##*/}: ${err:-invalid sudo options}" >&2
	return $ret
fi

set_SQA "$@"
CMD_ARGS="$SQA"
}

# Single quote each argument.
set_SQA() {
	local p SQ
	for p; do
		_sq "$p"
		SQA="$SQA $_SQ"
	done
	SQA=${SQA# }
}

# Rewrite all single quotes as \'
# Adapted from https://unix.stackexchange.com/a/187787
_sq() {
	set \' "$1"
	while	case $2 in
			*\'*) : ;;
			*) ! _SQ="$1$2'" ;;
		esac
	do
		set "$1${2%%\'*}'\''" "${2#*\'}"
	done
}

### parse sudo options and keep just the command arguments
set_CMD_ARGS "$@"
if [ 0 != $? ]; then
	exit 1
fi

### as sudo a command is required
if ! [ "$CMD_ARGS" ] || [ "$opt_help" ]; then
	echo "Usage: sudo [-u user] [other sudo options ignored] [--] command [args]"
	exit 0
fi
[ "${SUDO_TEST:-0}" -gt 0 ] && printf "\033[32m(%s)\033[0m\n" "CMD_ARGS" "$CMD_ARGS" >&2

### process sudo options
case ${opt_user:-root} in
	root | 0 )
		exec sh -c ${SUDO_TEST:+./}"run-as-root $CMD_ARGS" run-as-root ;;
	spot | $(id -u spot) )
		exec sh -c ${SUDO_TEST:+./}"run-as-spot $CMD_ARGS" run-as-spot ;;
	* )
		echo "${0##*/}: only -u root and -u spot are implemented" >&2
		exit 1 ;;
esac

exit $?
: << \EOF
We recognize the following spec:
https://www.busybox.net/downloads/BusyBox.html#getopt
https://www.mankier.com/1/getopt
https://www.mankier.com/8/sudo
sudo	-h | -K | -k | -V
sudo	-v [-ABknS] [-g group] [-h host] [-p prompt] [-u user]
sudo	-l [-ABknS] [-g group] [-h host] [-p prompt] [-U user] [-u user] [command]
sudo	[-ABbEHnPS] [-C num] [-g group] [-h host] [-p prompt] [-r role] [-t type] [-T timeout] [-u user] [VAR=value] [-i | -s command]
-A, --askpass
-B, --bell
-b, --background
-C num, --close-from=num
-E, --preserve-env, --preserve-env=list
-e, --edit
-g group, --group=group
-H, --set-home
-h, --help
-h host, --host=host
-i, --login
-K, --remove-timestamp
-k, --reset-timestamp
-l, --list
-n, --non-interactive
-P, --preserve-groups
-p prompt, --prompt=prompt
-r role, --role=role
-S, --stdin
-s, --shell
-t type, --type=type
-U user, --other-user=user
-T timeout, --command-timeout=timeout
-u user, --user=user
-V, --version
-v, --validate
EOF

