#!/bin/sh
# ,-----------------------------------------,
# |            DEAR DEVELOPERS:             |
# | DON'T FORGET! IT MUST BE POSIX KONFORM! |
# |           TEST IT WITH DASH!            |
# | DON'T FORGET! FILES CAN INCLUDE SPACES! |
# `-----------------------------------------`

# ls as alias often has --color=none, which displays colors. we can't parse colors.
unalias -a

export RED='\E[31;01m'
export GREEN='\E[32;01m'
export YELLOW='\E[33;01m'
export NORMAL="\033[0m"

#Conf
export DEFAULT="default"
export confdir="/etc/initng"

export script_dirs="$(find "${confdir}" -type d)"

# TODO: ${confdir} includes a special char for sed. very dangerous!
# but /etc/initng isn't dangerous.
export all_levels="`ls \"${confdir}/runlevel\" | sed -e 's#\.[^\.]*$##'`"

alias error='echo -e ${RED}Error:${NORMAL}'
alias warning='echo -e ${YELLOW}Warning:${NORMAL}'
alias success='echo -e ${GREEN}Success:${NORMAL}'

rl_file() {
	local rl="${confdir}/runlevel/${1}"

	# if virtual exists and no runlevel, because runlevel has a higher prio.
	if [ -e "${rl}.virtual" -a ! -e "${rl}.runlevel" ]; then
		echo "${rl}.virtual"
	else
		echo "${rl}.runlevel"
	fi
}

usage() {
	cat <<EOF
usage: ng-update a|add script1 [script2 ...] [runlevel1] [runlevel2 ...]
       ng-update d|del|delete script1 [script2 ...] [runlevel1] [runlevel2 ...]
       ng-update s|show|view [script] [runlevel]

note:  After ng-update executes, the script dependency cache is automatically
       updated.

examples:
       ng-update add net/eth0 default
       Adds the eth0 script (in ${confdir}) to the "default" runlevel.

       ng-update del daemon/sysklogd
       Removes sysklogd from all runlevels.

       ng-update del net/eth2 default wumpus
       Removes net/eth2 from runlevel default and wumpus

EOF
	exit 1
}

add() {
	shift
	for q in "${@}"; do
		if check_script "${q}"; then
			myscripts="${myscripts} ${myscript}"
		elif check_level "${q}"; then
			mylevels="${mylevels} ${q}"
		else
			warning "\"${q}\" isn't a script or a runlevel, it's being removed from list"
		fi
	done

	if [ -z "$(echo ${myscripts} | tr -d " ")" ]; then
		error "you didn't specify any script"
		exit 1
	fi
	[ -z "${mylevels}" ] && mylevels="${DEFAULT}"

	for h in ${myscripts}; do
		# Print some info when ng-update add if any in file.
		display_itype_msg "${ifile}"

		for i in ${mylevels}; do
			local rlfile=$(rl_file "${i}")
			if ! grep -q -e "${h}\$" "${rlfile}"; then
				echo "${h}" | tr -d " " >> "${rlfile}"
				success "added \"${h}\" to runlevel \"${i}\""
			else
				warning "\"${h}\" already installed in runlevel \"${i}\""
			fi
		done
	done
	exit 0
}

display_itype_msg() {
	[ -e "${1}" ] && awk '
		BEGIN{ printf "\n"; }
		$1=="#" && $2=="ng-update" && $3~/^all|del$/ {
			$1 = $2 = "";
			o = 1;
			print;
		}
		END { if (o) printf "\n\n"; }' "${1}"
}

del() {
	shift
	for q in "${@}"; do
		if check_level "${q}"; then
			mylevels="${mylevels} ${q}"
		elif check_script "${q}"; then
			myscripts="${myscripts} ${myscript}"
		else
			myscripts="${myscripts} ${q}"
		fi
	done

	if [ -z "$(echo ${myscripts} | tr -d " " )" ]; then
		error "you didn't specify any script"
		exit 1
	fi
	[ -z "${mylevels}" ] && mylevels="${all_levels}"

	for h in ${myscripts}; do
		remlevels=""
		ifile="${confdir}/${h}.i"
		# Print some info when ng-update add if any in file.
		display_itype_msg "${ifile}"

		for i in ${mylevels}; do
			local rlfile=$(rl_file "${i}")
			if grep -q "${rlfile}" -x -e "${h}\$"; then
				grep "${rlfile}" -x -v -e "${h}" > "${rlfile}.new"
				mv "${rlfile}.new" "${rlfile}"
				remlevels="${remlevels} ${i}"
			fi
		done
		if [ -n "$(echo ${remlevels} | tr -d " ")" ]; then
			success "removed \"${h}\" from the runlevel(s):${remlevels}"
		else
			warning "\"${h}\" missing from all runlevels specified"
		fi
	done
	exit 0
}

show() {
	case "${#}" in
	1)
		for g in ${script_dirs}; do
			allscripts="${allscripts} $(find "${g}" -type f -name '*.i' 2>/dev/null | sed -e 's!'"${confdir}"'/\(.*\)\.[^\.]*$!\1!')"
		done

		for e in ${all_levels}; do
			local rlfile=$(rl_file "${e}")
			# adds only if net/
			allscripts="${allscripts} $(sed -ne 's/net\///p' "${rlfile}")"
			export myscript_${e//-/_}="$(cat "${rlfile}")"
		done

		for f in ${allscripts}; do
			printf "%20s | " "${f:0:19}"
			for c in ${all_levels}; do
				eval myscript=\$myscript_${c//-/_}
				if echo "$myscript" | grep -q "$f"; then
					echo -n "${c} "
				else
					printf "%${#c}s " " "
				fi
			done
			echo
		done
		;;

	2)
		shift

		if check_script "${1}"; then
			printf "%20s | " "${1:0:19}"

			for w in ${all_levels}; do
				local rlfile=$(rl_file "${w}")

				if grep -q ${rlfile} -e "${1}"; then
					echo -n "${w} "
				else
					printf "%${#w}s " " "
				fi
			done
			echo
		else
			if check_level "${1}"; then
				for o in $( cat `rl_file "${1}"` | cut -d / -f2 ); do
					printf "%20s | " "${o:0:19}"
					echo -n "${1} "
				done
				echo
			else
				error "\"${1}\" isn't a script or a runlevel"
			fi
		fi
		;;

	*)
		usage
		;;
	esac
}

check_script() {
	local spath
	if [ -e "${confdir}/${1}.i" ] || echo "${1}" | grep -q net/; then
		myscript="${1}"
		return 0
	fi

	for z in ${script_dirs}; do
		echo "${all_levels}" | grep -q "${1}" && break

		if [ -e "${confdir}/${z}/${1}.i" ]; then
			myscript="${z}/${1}"
			return 0
		fi
	done

	spath="${1}"
	while echo "${spath}" | grep -q /; do
		spath="$( echo "${spath}" | sed -r 's/^(.*)\/(.*)/\1/' )"
		echo "${spath}"
		if [ -e "${confdir}/${spath}.i" ]; then
			warning "Cannot check \"${1}\" is a valid script"
			myscript="${1}"
			return 0
		fi
	done

	myscript="${1}"
	return 1
}

check_level() {
	echo "${all_levels}" | grep -q "${1}"
}

check_am_root() {
	if [ "${EUID}" -ne 0 ]; then
		error "must be root"
		exit 1
	fi
}

case "${1}" in
add|a)
	check_am_root
	add $@
	;;

del|d|delete)
	check_am_root
	del $@
	;;

show|s|view)
	show $@
	;;

*)
	usage
	;;
esac

