diff --git a/firestarter b/firestarter
index 50d5e0b..0c679bd 100755
--- a/firestarter
+++ b/firestarter
@@ -6,60 +6,76 @@
 #
 # Distributed under terms of the MIT license.
 #
+set -e
 
-# "Globals"
-_name="firestarter"
-_configdir="${XDG_CONFIG_HOME:-$HOME/.config}/$_name"
-_logdir="${XDG_DATA_HOME:-$HOME/.local/share}/$_name/logs"
-_firestarterrc="$HOME/.firestarterrc"
-_sessionid="$(< /proc/self/sessionid)"
-# Dummy fd for read sleeping
-exec 1023<> <(:)
+# Read-only set-once variables
+declare -r _name="$(basename -- "$0")"
+declare -r _sessionid="$(< /proc/self/sessionid)"
+# Options
+declare _optconfigdir="${XDG_CONFIG_HOME:-$HOME/.config}/${_name}"
+declare _optdryrun
+declare _optlogdir="$_optconfigdir/logs"
+declare _optpregen
+declare -i _opthelp
+declare -i _optverbose
+# Working variables
+declare -a _args
+declare _return
 
-# Basic functions
-print() {
-	# Write a message to STDOUT without a name attached
-	# 1: That message
-	[ -z "$1" ] && return 1
-	printf "%s\\n" \
-		"$1"
-}
+# Helper functions
 log() {
-	# Write a message to STDOUT
-	# 1: That message
+	# Print a line to the terminal if _optverbose is greater than $2
+	# $2 defaults to 0
+	# loglevel 0: Daily-use messages
+	# loglevel 1: Detailed but not quite debugging
+	# loglevel 2: Definitely debugging
 	[ -z "$1" ] && return 1
-	printf "%s log: %s\\n" \
-		"$_name" \
-		"$1" >&1
-}
-err() {
-	# Write a message to STDERR, also exit if arg 2 is specified
-	# 1: That message
-	# 2: An optional exit code (will only exit if provided)
-	[ -z "$1" ] && return 1
-	printf "%s err: %s\\n" \
-		"$_name" \
-		"$1" >&2
-	[ -z "$2" ] && return 0
-	if ! [ "$2" -ge "0" ] > /dev/null 2>&1; then
-		err "Attempted to exit with malformed exit code \"$2\"" 10
-	else
-		exit "$2"
+	if (( _optverbose >= ${2:-0} )); then
+		printf "%s\\n" "$1"
 	fi
 }
-has() {
-	# See if a program exists in $PATH
-	# 1: A program
+warn() {
+	# Print a yellow line to the terminal, respecting _optverbose
 	[ -z "$1" ] && return 1
-	command -v "$1" > /dev/null 2>&1
+	if (( _optverbose >= ${2:-0} )); then
+		if [ -t 1 ]; then
+			printf "\\e[33m%s\\e[0m\\n" "$1"
+		else
+			printf "WARN: %s\\n" "$1"
+		fi
+	fi
 }
-gettarget() {
-	# Parse a defaults file to get the target program
-	# 1: A defaults file
+error() {
+	# Print a red line to the terminal, exit if $2 is specified
 	[ -z "$1" ] && return 1
+	if [ -t 2 ]; then
+		printf "\\e[31m%s\\e[0m\\n" "$1" 1>&2
+	else
+		printf "ERROR: %s\\n" "$1" 1>&2
+	fi
+	[ -z "$2" ] && return
+	exit "${2:-1}"
+}
+has() {
+	# Parse out all arguments and try to find them in path
+	# If an argument cannot be found, set _return and fail
+	for prog in "$@"; do
+		if ! command -v "$prog" > /dev/null 2>&1; then
+			_return="$prog"
+			return 1
+		fi
+	done
+	return 0
+}
+
+# Core program functions
+gettarget() {
+	# Parse a defaults file to get a target program
+	[ -z "$1" ] && return 1
+	[ -d "$1" ] && return 1
 	[ -r "$1" ] || return 1
-	# Every odd line is the check line
-	# Every even one is the exec line
+	# Every odd line is a condition
+	# Every even one is a target
 	local firstline
 	while read -r checkline; do
 		if [ -z "$firstline" ]; then
@@ -70,9 +86,7 @@ gettarget() {
 				return 50
 			fi
 		fi
-		if [ "${checkline#"#"}" != "$checkline" ]; then
-			continue
-		fi
+		[ "${checkline#"#"}" != "$checkline" ] && continue
 		read -r execline
 		if bash -c "$checkline" > /dev/null 2>&1; then
 			_return="$execline"
@@ -83,19 +97,17 @@ gettarget() {
 	done < "$1"
 	return 2
 }
-
-# Steps in execution
-step_generate() {
-	log "Creating default config setup in \"$_configdir\""
+genconfigs() {
+	log "Creating default config setup in \"$_optconfigdir\""
 	log "See firestarter -h for more information"
 	# Audio daemon
-	cat << EOF > "$_configdir/audio-daemon"
+	cat << EOF > "$_optconfigdir/audio-daemon"
 #.fsdefaults
 command -v pulseaudio
 pulseaudio
 EOF
 	# Information bars
-	cat << EOF > "$_configdir/bar"
+	cat << EOF > "$_optconfigdir/bar"
 #.fsdefaults
 command -v polybar && [ -r "$HOME/.config/polybar/launch.sh" ]
 "$HOME/.config/polybar/launch.sh"
@@ -111,7 +123,7 @@ command -v xfce4-panel
 xfce4-panel
 EOF
 	# Blue light filter
-	cat << EOF > "$_configdir/blue-light-filter"
+	cat << EOF > "$_optconfigdir/blue-light-filter"
 #.fsdefaults
 command -v redshift-gtk
 redshift-gtk
@@ -119,7 +131,7 @@ command -v redshift
 redshift
 EOF
 	# Compositor
-	cat << EOF > "$_configdir/compositor"
+	cat << EOF > "$_optconfigdir/compositor"
 #.fsdefaults
 [ -z "\$DISPLAY" ]
 :
@@ -131,7 +143,7 @@ command -v xcompmgr
 xcompmgr
 EOF
 	# Polkit authentication agents
-	cat << EOF > "$_configdir/polkit-agent"
+	cat << EOF > "$_optconfigdir/polkit-agent"
 #.fsdefaults
 command -v lxqt-policykit-agent
 lxqt-policykit-agent
@@ -176,7 +188,7 @@ polkit-efl-authentication-agent-1
 /usr/libexec/polkit-gnome-authentication-agent-1
 EOF
 	# Hotkey daemon
-	cat << EOF > "$_configdir/hotkey-daemon"
+	cat << EOF > "$_optconfigdir/hotkey-daemon"
 #.fsdefaults
 [ -z "\$DISPLAY" ]
 :
@@ -186,13 +198,13 @@ command -v lxqt-globalkeysd
 lxqt-globalkeysd
 EOF
 	# Network daemon
-	cat << EOF > "$_configdir/network-daemon"
+	cat << EOF > "$_optconfigdir/network-daemon"
 #.fsdefaults
 command -v nm-applet
 nm-applet
 EOF
 	# Notification daemon
-	cat << EOF > "$_configdir/notification-daemon"
+	cat << EOF > "$_optconfigdir/notification-daemon"
 #.fsdefaults
 [ -z "\$DISPLAY" ]
 :
@@ -202,7 +214,7 @@ command -v lxqt-notificationd
 notificationd
 EOF
 	# Power daemons
-	cat << EOF > "$_configdir/power-daemon"
+	cat << EOF > "$_optconfigdir/power-daemon"
 #.fsdefaults
 command -v batterymon
 batterymon
@@ -219,13 +231,13 @@ gnome-power-manager
 EOF
 	# Runners
 	# Note that rofi is not a daemon and is not included here
-	cat << EOF > "$_configdir/runner"
+	cat << EOF > "$_optconfigdir/runner"
 #.fsdefaults
 command -v krunner
 krunner
 EOF
 	# Settings daemons
-	cat << EOF > "$_configdir/settings-daemon"
+	cat << EOF > "$_optconfigdir/settings-daemon"
 #.fsdefaults
 command -v xsettingsd
 xsettingsd
@@ -241,7 +253,7 @@ command -v gnome-settings-daemon
 gnome-settings-daemon
 EOF
 	# System statistics glances
-	cat << EOF > "$_configdir/stat-glances"
+	cat << EOF > "$_optconfigdir/stat-glances"
 #.fsdefaults
 [ -z "\$DISPLAY" ]
 :
@@ -251,7 +263,7 @@ command -v conky && [ -r "\${XDG_CONFIG_HOME:-$HOME/.config}/conky/conky.conf" ]
 sleep 5 && conky
 EOF
 	# Wallpaper setters
-	cat << EOF > "$_configdir/wallpaper"
+	cat << EOF > "$_optconfigdir/wallpaper"
 #.fsdefaults
 [ -z "\$DISPLAY" ]
 :
@@ -261,7 +273,7 @@ command -v nitrogen
 nitrogen --restore
 EOF
 	# Window managers
-	cat << EOF > "$_configdir/wm"
+	cat << EOF > "$_optconfigdir/wm"
 #.fsdefaults
 [ -z "\$DISPLAY" ]
 :
@@ -327,125 +339,70 @@ command -v tinywm
 tinywm
 EOF
 }
-step_printhelp() {
-	cat << EOF
-Usage: $(basename -- "$0") [OPTION...]
-Start or generate a desktop environment configuration
-
-  -h		Show this help text
-  -g		Generate a default configuration. This will clobber
-  -d		Perform a dry run. This will print the results of all decisions
-		without executing them.
-
-Additionally, $(basename -- "$0") responds to the following environment variables:
-
-  FS_DIEONWM	If nonempty, end the session when the WM dies. This is useful
-		both for preventing session lock-ins and for using built-in WM
-		features to log out of the session.
-  FS_NOLOG	If nonempty, create no log files
-  FS_NOWAITWM	If nonempty, skip waiting on the WM to initialize
-
-Copyright (c) 2019 rehashedsalt@cock.li
-Licensed under the MIT License
-https://gitlab.com/rehashedsalt/firestarter
-EOF
-}
-step_check() {
-	if [ -z "$_dryrun" ] && [ -z "$_generate" ]; then
-		for pid in $(pgrep firestarter); do
-			# Skip invalid PIDs
-			! [ -d "/proc/$pid" ] && continue
-			# If it's not our session then who cares
-			[ "$_sessionid" != "$(< "/proc/$pid/sessionid")" ] && continue
-			if [ "$pid" != "$BASHPID" ]; then
-				err "Firestarter is already running: $pid" 40
-			fi
-		done
-	fi
-	if ! [ -d "$HOME" ] || ! [ -r "$HOME" ]; then
-		err "Inaccessible home directory: \"$HOME\"" 54
-	fi
-	if ! [ -d "$_configdir" ]; then
-		if ! mkdir -p "$_configdir" > /dev/null 2>&1; then
-			err "Failed to create configuration directory: \"$_configdir\"" 52
-		fi
-	fi
-	return 0
-}
 step_preexecute() {
 	# Special things that can't use simple configuration files
-	[ -n "$_dryrun" ] && return 0
-
-	# Xsessionrc
-	[ -r "$HOME/.xsessionrc" ] && source "$HOME/.xsessionrc"
-
-	# Exports
+	[ -n "$_optdryrun" ] && return 0
+	[ -r "$HOME/.xsessionrc" ] && . "$HOME/.xsessionrc"
 	export XDG_CURRENT_DESKTOP="${XDG_CURRENT_DESKTOP:-firestarter}"
-
 	# dbus
 	if \
 		[ -z "$DBUS_SESSION_BUS_ADDRESS" ] && \
 		[ -n "$XDG_RUNTIME_DIR" ] && \
 		[ "$XDG_RUNTIME_DIR" = "/run/user/$(id -u)" ] && \
 		[ -S "$XDG_RUNTIME_DIR/bus" ]; then
-		# We already have a bus started, use it
+		# We already have a bus started; use it
 		export DBUS_SESSION_BUS_ADDRESS="unix:path=$XDG_RUNTIME_DIR/bus"
 		hasdbus=1
 	elif \
 		[ -z "$DBUS_SESSION_BUS_ADDRESS" ] && \
 		has dbus-launch; then
-		# We have dbus but haven't started up a bus yet
-		eval "$(dbus-launch --exit-with-session --sh-syntax)"
+		# We have dbus but haven't started it yet
+		eval "$(dbus-laucnh --exit-with-session --sh-syntax)"
 		hasdbus=1
 	else
-		log "Did not start dbus; some applications may misbehave"
+		warn "Did not start dbus; some applications may misbehave"
 	fi
 	if [ -n "$hasdbus" ]; then
-		if has dbus-update-activation-environment; then
-			dbus-update-activation-environment --verbose --systemd --all > /dev/null 2>&1
-		fi
+		has dbus-update-activation-environment && \
+			dbus-update-activation-environment --verbose --systemd --all >/dev/null 2>&1
 	fi
 	unset hasdbus
-
 	# kcminit/Qt settings
 	if has kcminit; then
 		log "Initializing KDE Control Module settings"
-		kcminit > /dev/null 2>&1
+		kcminit >/dev/null 2>&1
 		export XDG_CURRENT_DESKTOP="KDE"
 	elif has qt5ct; then
-		log "Integrating qt5ct"
+		log "Initializing qt5ct"
 		if [ -z "$QT_QPA_PLATFORMTHEME" ]; then
 			export QT_QPA_PLATFORMTHEME="qt5ct"
-			log "Exporting QT_QPA_PLATFORMTHEME as \"$QT_QPA_PLATFORMTHEME\""
+			log "Exporting QT_QPA_PLATFORMTHEME as \"$QT_QPA_PLATFORMTHEME\"" 2
 		else
-			log "Using existing theme setting \"$QT_QPA_PLATFORMTHEME\""
+			log "Using existing theme setting \"$QT_QPA_PLATFORMTHEME\"" 2
 		fi
 		if [ -z "$QT_AUTO_SCREEN_SCALE_FACTOR" ]; then
 			export QT_AUTO_SCREEN_SCALE_FACTOR="0"
-			log "Exporting QT_AUTO_SCREEN_SCALE_FACTOR as $QT_AUTO_SCREEN_SCALE_FACTOR"
+			log "Exporting QT_AUTO_SCREEN_SCALE_FACTOR as \"$QT_AUTO_SCREEN_SCALE_FACTOR\"" 2
 		else
-			log "Using existing scale setting \"$QT_AUTO_SCREEN_SCALE_FACTOR\""
+			log "Using existing scale factor \"$QT_AUTO_SCREEN_SCALE_FACTOR\"" 2
 		fi
 	fi
-
 	# xhost
 	if has xhost; then
-		if xhost +si:localuser:"$(id -un)" > /dev/null 2>&1; then
-			log "Session can be accessed in other sessions by this user"
+		if xhost +si:localuser:"$(id -un)" >/dev/null 2>&1; then
+			log "Session open to other sessions by this user"
 		else
-			log "Failed to open session up via xhost"
+			warn "Failed to open session via xhost"
 		fi
 	fi
-
 	# xresources
 	if [ -n "$DISPLAY" ] && has xrdb && [ -r "$HOME/.Xresources" ]; then
-		if xrdb "$HOME/.Xresources" > /dev/null 2>&1; then
+		if xrdb "$HOME/.Xresources" >/dev/null 2>&1; then
 			log "Loaded in .Xresources"
 		else
-			log "Failed to load .Xresources"
+			warn "Failed to load .Xresources"
 		fi
 	fi
-
 	# xset
 	if has xset; then
 		log "Disabling bell"
@@ -453,84 +410,81 @@ step_preexecute() {
 	fi
 }
 step_execute() {
-	# Ensure we can log if we have to
-	if [ -n "$FS_NOLOG" ]; then
-		log "No logs will be created per FS_NOLOG"
-	elif ! [ -d "$_logdir" ]; then
-		if ! mkdir -p "$_logdir" > /dev/null 2>&1; then
-			err "Failed to create log directory: \"$_logdir\"" 53
+	# Parse out our defaults lists and execute their targets
+	if ! [ -d "$_optlogdir" ]; then
+		if ! mkdir -p "$_optlogdir" >/dev/null 2>&1; then
+			error "Failed to create log directory: \"$_optlogdir\"" 53
 		fi
 	fi
-	# Parse configs
-	for file in "$_configdir"/*; do
+	for file in "$_optconfigdir"/*; do
 		if ! [ -e "$file" ]; then
 			log "No configuration files found; generating defaults"
-			step_generate
+			genconfigs
 			step_execute
-			break
+			return
 		fi
+		# Skip our logs directory
+		[ "$_optlogdir" == "$file" ] && continue
 		local filename="$(basename -- "$file")"
-		local logfile="$_logdir/$filename"
-		[ -n "$FS_NOLOG" ] && logfile="/dev/null"
+		local logfile="$_optlogdir/$filename.log"
 		if gettarget "$file"; then
 			# It's a defaults file with a selected target
 			target="$_return"
-			log "Found target for \"$filename\": \"$target\""
-			if [ -z "$_dryrun" ]; then
-				if [ -f "$logfile" ]; then
-					[ -f "$logfile.old" ] && rm "$logfile.old"
-					mv "$logfile" "$logfile.old"
-				fi
-				bash -c "$target" > "$logfile" 2>&1 &
+			log "Found target for $filename: \"$target\""
+			[ -n "$_optdryrun" ] && continue
+			if [ -f "$logfile" ]; then
+				[ -f "$logfile.old" ] && rm "$logfile.old"
+				mv "$logfile" "$logfile.old"
 			fi
+			bash -c "$target" > "$logfile" 2>&1 &
 		elif [ $? = 50 ] && [ -x "$file" ]; then
-			# It's a shell script or something
-			log "Executing file straight out: \"$filename\""
-			if [ -z "$_dryrun" ]; then
-				"$file" > "$logfile" 2>&1 &
-			fi
+			# It's a shell script or executable symlink
+			log "Executing file: \"$filename\""
+			[ -n "$_optdryrun" ] && continue
+			"$file" > "$logfile" 2>&1 &
 		else
-			err "Could not execute file: \"$filename\""
+			warn "Could not execute file: \"$filename\""
 		fi
 	done
 }
 step_postexecute() {
 	# Wait for the WM to initialize, if one was found and we have the tools
-	if [ -z "$FS_NOWAITWM" ] && gettarget "$_configdir/wm" && has xprop && has grep; then
+	if [ -z "$FS_NOWAITWM" ] && gettarget "$_configdir/wm" && has xprop grep; then
 		log "Waiting for WM to initialize: \"$_return\""
 		for (( i=0; i<10; i++ )); do
 			line="$(xprop -root | grep ^_NET_WM_NAME | grep -o '"\S*"$')"
 			if [ -n "$line" ]; then
-				log "WM has intiailized, _NET_WM_NAME atom reads: $line"
+				log "WM has initialized, _NET_WM_NAME atom reads: $line"
 				break
 			fi
 			read -t 1 -u 1023
 		done
 	fi
-	# Dumb Polybar workaround
+	# Dumb polybar workaround
 	killall polybar -SIGUSR1
-	# Execute a user script if it exists
-	if [ -r "$_firestarterrc" ] && [ -z "$_dryrun" ]; then
-		log "Executing .firestarterrc"
-		"$_firestarterrc"
+	# Execute a user rc if it exists
+	local firestarterrc="$HOME/.firestarterrc"
+	if [ -r "$firestarterrc" ] && [ -z "$dryrun" ]; then
+		log "Executing rc script: $firestarterrc"
+		"$firestarterrc"
 	fi
 	# Start XDG autostarters, if they exist
-	if [ -z "$_dryrun" ]; then
+	if [ -z "$_optdryrun" ]; then
 		if has dex; then
-			dex -a > /dev/null 2>&1
+			dex -a >/dev/null 2>&1
 		elif has fbautostart; then
 			fbautostart > /dev/null 2>&1
 		elif has xdg-autostart; then
 			xdg-autostart ${XDG_CURRENT_DESKTOP:-firestarter}
 		else
-			err "Could not find an XDG autostarter"
+			warn "Could not find an XDG autostarter"
 		fi
 	fi
 }
 step_wait() {
-	[ -n "$_dryrun" ] && exit 0
+	[ -n "$_optdryrun" ] && exit 0
 	trap step_logout EXIT
-	if [ -n "$FS_DIEONWM" ] && gettarget "$_configdir/wm" && has readlink && has "$_return"; then
+	if [ -n "$FS_DIEONWM" ] && gettarget "$_configdir/wm" && has readlink "$_return"; then
 		target="$(command -v "$_return")"
 		for job in $(jobs -p); do
 			if [ "$target" = "$(readlink /proc/$job/exe)" ]; then
@@ -539,7 +493,7 @@ step_wait() {
 				exit 0
 			fi
 		done
-		err "Could not find WM: \"$target\""
+		warn "Could not find WM: \"$target\""
 	fi
 	log "Waiting for programs to exit"
 	wait
@@ -553,45 +507,108 @@ step_logout() {
 			loginctl terminate-session "$_sessionid"
 		fi
 	else
-		# Otherwise just brute it
+		# Otherwise just brute it out
 		kill $(jobs -p)
 	fi
 	return 0
 }
 
+printhelp() {
+	cat << EOF
+Usage: $_name [OPTION]...
+
+  -d			Perform a dry run; print what programs would have been
+			executed instead of doing so
+  -g			Regenerate default configs. This will clobber
+  -h			Print this help text
+  -v			Print more status messages. Stacks
+
+Environment Variables:
+
+  FS_DIEONWM		If nonempty, end the session when the WM dies.
+  FS_NOWAITWM		If nonempty, skip waiting for the WM to initialize
+
+Copyright (c) 2019 rehashedsalt@cock.li
+Licensed under the MIT license
+EOF
+}
+
 # Main
 main() {
-	while getopts ":dgh" opt; do
-		case $opt in
-			d)
-				_dryrun=1
-				;;
-			g)
-				_generate=1
-				;;
-			h)
-				step_printhelp
-				exit $?
-				;;
-			*)
-				err "Unrecognized argument: \"$OPTARG\"" 50
-				;;
-			:)
-				err "Invalid option: \"$OPTARG\"" 51
-				;;
-		esac
+	# Parse out arguments
+	while [ -n "$1" ]; do
+		# Parse out flags
+		while getopts ":dghv" opt; do
+			case $opt in
+				d)
+					_optdryrun=1
+					;;
+				g)
+					_optpregen=1
+					;;
+				h)
+					_opthelp=1
+					;;
+				v)
+					_optverbose+=1
+					;;
+				:)
+					error "Option requires argument: -$OPTARG" 2
+					;;
+				*)
+					error "Invalid option: -$OPTARG" 2
+					;;
+			esac
+		done
+		# Store arguments
+		shift $((OPTIND - 1))
+		if [ -n "$1" ]; then
+			_args+=("$1")
+			shift
+		fi
+		unset OPTIND
 	done
-	step_check
-	if [ -n "$_generate" ]; then
-		step_generate
-	else
-		[ -n "$_dryrun" ] && log "Performing a dry run"
-		step_preexecute
-		step_execute
-		step_postexecute
-		step_wait
+	# Early hook for help
+	[ -n "$_opthelp" ] && printhelp && exit 0
+	# Early hook for generating configs
+	[ -n "$_optpregen" ] && genconfigs && exit $?
+	# Ensure our running environment is sane and that we're not about to nest
+	if [ -z "$_optdryrun" ] && [ -z "$_optpregen" ]; then
+		for pid in $(pgrep firestarter); do
+			# Skip invalid PIDs
+			! [ -d "$/proc/$pid" ] && continue
+			# If it's not our session then who cares
+			[ "$_sessionid" != "$(< "/proc/$pid/sessionid")" ] && continue
+			# If it's us then who cares
+			[ "$pid" == "$BASHPID" ] && continue
+			# We care
+			error "Firestarter is already running: $pid" 40
+		done
 	fi
-	return 0
+	if ! [ -d "$HOME" ] || ! [ -r "$HOME" ]; then
+		error "Home directory not found or inaccessable: \"$HOME\"" 54
+	fi
+	if ! [ -d "$_optconfigdir" ]; then
+		if [ -f "$_optconfigdir" ]; then
+			error "Config directory is a file, should be directory: \"$_optconfigdir\"" 52
+		fi
+		if ! mkdir -p "$_optconfigdir" > /dev/null 2>&1; then
+			error "Failed to find or create config directory: \"$_optconfigdir\"" 52
+		fi
+	fi
+	# Validate core program dependencies
+	log "Validating dependencies" 2
+	if ! has basename; then
+		error "Failed to find program: $_return" 1
+	fi
+
+	# Do the do
+	[ -n "$_optdryrun" ] && log "Performing a dry run"
+	step_preexecute
+	step_execute
+	step_wait
+	step_logout
+	exit 0
 }
 
 main "$@"
diff --git a/firestarter2.bash b/firestarter-old
similarity index 57%
rename from firestarter2.bash
rename to firestarter-old
index 0c679bd..50d5e0b 100755
--- a/firestarter2.bash
+++ b/firestarter-old
@@ -6,76 +6,60 @@
 #
 # Distributed under terms of the MIT license.
 #
-set -e
 
-# Read-only set-once variables
-declare -r _name="$(basename -- "$0")"
-declare -r _sessionid="$(< /proc/self/sessionid)"
-# Options
-declare _optconfigdir="${XDG_CONFIG_HOME:-$HOME/.config}/${_name}"
-declare _optdryrun
-declare _optlogdir="$_optconfigdir/logs"
-declare _optpregen
-declare -i _opthelp
-declare -i _optverbose
-# Working variables
-declare -a _args
-declare _return
+# "Globals"
+_name="firestarter"
+_configdir="${XDG_CONFIG_HOME:-$HOME/.config}/$_name"
+_logdir="${XDG_DATA_HOME:-$HOME/.local/share}/$_name/logs"
+_firestarterrc="$HOME/.firestarterrc"
+_sessionid="$(< /proc/self/sessionid)"
+# Dummy fd for read sleeping
+exec 1023<> <(:)
 
-# Helper functions
+# Basic functions
+print() {
+	# Write a message to STDOUT without a name attached
+	# 1: That message
+	[ -z "$1" ] && return 1
+	printf "%s\\n" \
+		"$1"
+}
 log() {
-	# Print a line to the terminal if _optverbose is greater than $2
-	# $2 defaults to 0
-	# loglevel 0: Daily-use messages
-	# loglevel 1: Detailed but not quite debugging
-	# loglevel 2: Definitely debugging
+	# Write a message to STDOUT
+	# 1: That message
 	[ -z "$1" ] && return 1
-	if (( _optverbose >= ${2:-0} )); then
-		printf "%s\\n" "$1"
-	fi
+	printf "%s log: %s\\n" \
+		"$_name" \
+		"$1" >&1
 }
-warn() {
-	# Print a yellow line to the terminal, respecting _optverbose
+err() {
+	# Write a message to STDERR, also exit if arg 2 is specified
+	# 1: That message
+	# 2: An optional exit code (will only exit if provided)
 	[ -z "$1" ] && return 1
-	if (( _optverbose >= ${2:-0} )); then
-		if [ -t 1 ]; then
-			printf "\\e[33m%s\\e[0m\\n" "$1"
-		else
-			printf "WARN: %s\\n" "$1"
-		fi
-	fi
-}
-error() {
-	# Print a red line to the terminal, exit if $2 is specified
-	[ -z "$1" ] && return 1
-	if [ -t 2 ]; then
-		printf "\\e[31m%s\\e[0m\\n" "$1" 1>&2
+	printf "%s err: %s\\n" \
+		"$_name" \
+		"$1" >&2
+	[ -z "$2" ] && return 0
+	if ! [ "$2" -ge "0" ] > /dev/null 2>&1; then
+		err "Attempted to exit with malformed exit code \"$2\"" 10
 	else
-		printf "ERROR: %s\\n" "$1" 1>&2
+		exit "$2"
 	fi
-	[ -z "$2" ] && return
-	exit "${2:-1}"
 }
 has() {
-	# Parse out all arguments and try to find them in path
-	# If an argument cannot be found, set _return and fail
-	for prog in "$@"; do
-		if ! command -v "$prog" > /dev/null 2>&1; then
-			_return="$prog"
-			return 1
-		fi
-	done
-	return 0
-}
-
-# Core program functions
-gettarget() {
-	# Parse a defaults file to get a target program
+	# See if a program exists in $PATH
+	# 1: A program
+	[ -z "$1" ] && return 1
+	command -v "$1" > /dev/null 2>&1
+}
+gettarget() {
+	# Parse a defaults file to get the target program
+	# 1: A defaults file
 	[ -z "$1" ] && return 1
-	[ -d "$1" ] && return 1
 	[ -r "$1" ] || return 1
-	# Every odd line is a condition
-	# Every even one is a target
+	# Every odd line is the check line
+	# Every even one is the exec line
 	local firstline
 	while read -r checkline; do
 		if [ -z "$firstline" ]; then
@@ -86,7 +70,9 @@ gettarget() {
 				return 50
 			fi
 		fi
-		[ "${checkline#"#"}" != "$checkline" ] && continue
+		if [ "${checkline#"#"}" != "$checkline" ]; then
+			continue
+		fi
 		read -r execline
 		if bash -c "$checkline" > /dev/null 2>&1; then
 			_return="$execline"
@@ -97,17 +83,19 @@ gettarget() {
 	done < "$1"
 	return 2
 }
-genconfigs() {
-	log "Creating default config setup in \"$_optconfigdir\""
+
+# Steps in execution
+step_generate() {
+	log "Creating default config setup in \"$_configdir\""
 	log "See firestarter -h for more information"
 	# Audio daemon
-	cat << EOF > "$_optconfigdir/audio-daemon"
+	cat << EOF > "$_configdir/audio-daemon"
 #.fsdefaults
 command -v pulseaudio
 pulseaudio
 EOF
 	# Information bars
-	cat << EOF > "$_optconfigdir/bar"
+	cat << EOF > "$_configdir/bar"
 #.fsdefaults
 command -v polybar && [ -r "$HOME/.config/polybar/launch.sh" ]
 "$HOME/.config/polybar/launch.sh"
@@ -123,7 +111,7 @@ command -v xfce4-panel
 xfce4-panel
 EOF
 	# Blue light filter
-	cat << EOF > "$_optconfigdir/blue-light-filter"
+	cat << EOF > "$_configdir/blue-light-filter"
 #.fsdefaults
 command -v redshift-gtk
 redshift-gtk
@@ -131,7 +119,7 @@ command -v redshift
 redshift
 EOF
 	# Compositor
-	cat << EOF > "$_optconfigdir/compositor"
+	cat << EOF > "$_configdir/compositor"
 #.fsdefaults
 [ -z "\$DISPLAY" ]
 :
@@ -143,7 +131,7 @@ command -v xcompmgr
 xcompmgr
 EOF
 	# Polkit authentication agents
-	cat << EOF > "$_optconfigdir/polkit-agent"
+	cat << EOF > "$_configdir/polkit-agent"
 #.fsdefaults
 command -v lxqt-policykit-agent
 lxqt-policykit-agent
@@ -188,7 +176,7 @@ polkit-efl-authentication-agent-1
 /usr/libexec/polkit-gnome-authentication-agent-1
 EOF
 	# Hotkey daemon
-	cat << EOF > "$_optconfigdir/hotkey-daemon"
+	cat << EOF > "$_configdir/hotkey-daemon"
 #.fsdefaults
 [ -z "\$DISPLAY" ]
 :
@@ -198,13 +186,13 @@ command -v lxqt-globalkeysd
 lxqt-globalkeysd
 EOF
 	# Network daemon
-	cat << EOF > "$_optconfigdir/network-daemon"
+	cat << EOF > "$_configdir/network-daemon"
 #.fsdefaults
 command -v nm-applet
 nm-applet
 EOF
 	# Notification daemon
-	cat << EOF > "$_optconfigdir/notification-daemon"
+	cat << EOF > "$_configdir/notification-daemon"
 #.fsdefaults
 [ -z "\$DISPLAY" ]
 :
@@ -214,7 +202,7 @@ command -v lxqt-notificationd
 notificationd
 EOF
 	# Power daemons
-	cat << EOF > "$_optconfigdir/power-daemon"
+	cat << EOF > "$_configdir/power-daemon"
 #.fsdefaults
 command -v batterymon
 batterymon
@@ -231,13 +219,13 @@ gnome-power-manager
 EOF
 	# Runners
 	# Note that rofi is not a daemon and is not included here
-	cat << EOF > "$_optconfigdir/runner"
+	cat << EOF > "$_configdir/runner"
 #.fsdefaults
 command -v krunner
 krunner
 EOF
 	# Settings daemons
-	cat << EOF > "$_optconfigdir/settings-daemon"
+	cat << EOF > "$_configdir/settings-daemon"
 #.fsdefaults
 command -v xsettingsd
 xsettingsd
@@ -253,7 +241,7 @@ command -v gnome-settings-daemon
 gnome-settings-daemon
 EOF
 	# System statistics glances
-	cat << EOF > "$_optconfigdir/stat-glances"
+	cat << EOF > "$_configdir/stat-glances"
 #.fsdefaults
 [ -z "\$DISPLAY" ]
 :
@@ -263,7 +251,7 @@ command -v conky && [ -r "\${XDG_CONFIG_HOME:-$HOME/.config}/conky/conky.conf" ]
 sleep 5 && conky
 EOF
 	# Wallpaper setters
-	cat << EOF > "$_optconfigdir/wallpaper"
+	cat << EOF > "$_configdir/wallpaper"
 #.fsdefaults
 [ -z "\$DISPLAY" ]
 :
@@ -273,7 +261,7 @@ command -v nitrogen
 nitrogen --restore
 EOF
 	# Window managers
-	cat << EOF > "$_optconfigdir/wm"
+	cat << EOF > "$_configdir/wm"
 #.fsdefaults
 [ -z "\$DISPLAY" ]
 :
@@ -339,70 +327,125 @@ command -v tinywm
 tinywm
 EOF
 }
+step_printhelp() {
+	cat << EOF
+Usage: $(basename -- "$0") [OPTION...]
+Start or generate a desktop environment configuration
+
+  -h		Show this help text
+  -g		Generate a default configuration. This will clobber
+  -d		Perform a dry run. This will print the results of all decisions
+		without executing them.
+
+Additionally, $(basename -- "$0") responds to the following environment variables:
+
+  FS_DIEONWM	If nonempty, end the session when the WM dies. This is useful
+		both for preventing session lock-ins and for using built-in WM
+		features to log out of the session.
+  FS_NOLOG	If nonempty, create no log files
+  FS_NOWAITWM	If nonempty, skip waiting on the WM to initialize
+
+Copyright (c) 2019 rehashedsalt@cock.li
+Licensed under the MIT License
+https://gitlab.com/rehashedsalt/firestarter
+EOF
+}
+step_check() {
+	if [ -z "$_dryrun" ] && [ -z "$_generate" ]; then
+		for pid in $(pgrep firestarter); do
+			# Skip invalid PIDs
+			! [ -d "/proc/$pid" ] && continue
+			# If it's not our session then who cares
+			[ "$_sessionid" != "$(< "/proc/$pid/sessionid")" ] && continue
+			if [ "$pid" != "$BASHPID" ]; then
+				err "Firestarter is already running: $pid" 40
+			fi
+		done
+	fi
+	if ! [ -d "$HOME" ] || ! [ -r "$HOME" ]; then
+		err "Inaccessible home directory: \"$HOME\"" 54
+	fi
+	if ! [ -d "$_configdir" ]; then
+		if ! mkdir -p "$_configdir" > /dev/null 2>&1; then
+			err "Failed to create configuration directory: \"$_configdir\"" 52
+		fi
+	fi
+	return 0
+}
 step_preexecute() {
 	# Special things that can't use simple configuration files
-	[ -n "$_optdryrun" ] && return 0
-	[ -r "$HOME/.xsessionrc" ] && . "$HOME/.xsessionrc"
+	[ -n "$_dryrun" ] && return 0
+
+	# Xsessionrc
+	[ -r "$HOME/.xsessionrc" ] && source "$HOME/.xsessionrc"
+
+	# Exports
 	export XDG_CURRENT_DESKTOP="${XDG_CURRENT_DESKTOP:-firestarter}"
+
 	# dbus
 	if \
 		[ -z "$DBUS_SESSION_BUS_ADDRESS" ] && \
 		[ -n "$XDG_RUNTIME_DIR" ] && \
 		[ "$XDG_RUNTIME_DIR" = "/run/user/$(id -u)" ] && \
 		[ -S "$XDG_RUNTIME_DIR/bus" ]; then
-		# We already have a bus started; use it
+		# We already have a bus started, use it
 		export DBUS_SESSION_BUS_ADDRESS="unix:path=$XDG_RUNTIME_DIR/bus"
 		hasdbus=1
 	elif \
 		[ -z "$DBUS_SESSION_BUS_ADDRESS" ] && \
 		has dbus-launch; then
-		# We have dbus but haven't started it yet
-		eval "$(dbus-laucnh --exit-with-session --sh-syntax)"
+		# We have dbus but haven't started up a bus yet
+		eval "$(dbus-launch --exit-with-session --sh-syntax)"
 		hasdbus=1
 	else
-		warn "Did not start dbus; some applications may misbehave"
+		log "Did not start dbus; some applications may misbehave"
 	fi
 	if [ -n "$hasdbus" ]; then
-		has dbus-update-activation-environment && \
-			dbus-update-activation-environment --verbose --systemd --all >/dev/null 2>&1
+		if has dbus-update-activation-environment; then
+			dbus-update-activation-environment --verbose --systemd --all > /dev/null 2>&1
+		fi
 	fi
 	unset hasdbus
+
 	# kcminit/Qt settings
 	if has kcminit; then
 		log "Initializing KDE Control Module settings"
-		kcminit >/dev/null 2>&1
+		kcminit > /dev/null 2>&1
 		export XDG_CURRENT_DESKTOP="KDE"
 	elif has qt5ct; then
-		log "Initializing qt5ct"
+		log "Integrating qt5ct"
 		if [ -z "$QT_QPA_PLATFORMTHEME" ]; then
 			export QT_QPA_PLATFORMTHEME="qt5ct"
-			log "Exporting QT_QPA_PLATFORMTHEME as \"$QT_QPA_PLATFORMTHEME\"" 2
+			log "Exporting QT_QPA_PLATFORMTHEME as \"$QT_QPA_PLATFORMTHEME\""
 		else
-			log "Using existing theme setting \"$QT_QPA_PLATFORMTHEME\"" 2
+			log "Using existing theme setting \"$QT_QPA_PLATFORMTHEME\""
 		fi
 		if [ -z "$QT_AUTO_SCREEN_SCALE_FACTOR" ]; then
 			export QT_AUTO_SCREEN_SCALE_FACTOR="0"
-			log "Exporting QT_AUTO_SCREEN_SCALE_FACTOR as \"$QT_AUTO_SCREEN_SCALE_FACTOR\"" 2
+			log "Exporting QT_AUTO_SCREEN_SCALE_FACTOR as $QT_AUTO_SCREEN_SCALE_FACTOR"
 		else
-			log "Using existing scale factor \"$QT_AUTO_SCREEN_SCALE_FACTOR\"" 2
+			log "Using existing scale setting \"$QT_AUTO_SCREEN_SCALE_FACTOR\""
 		fi
 	fi
+
 	# xhost
 	if has xhost; then
-		if xhost +si:localuser:"$(id -un)" >/dev/null 2>&1; then
-			log "Session open to other sessions by this user"
+		if xhost +si:localuser:"$(id -un)" > /dev/null 2>&1; then
+			log "Session can be accessed in other sessions by this user"
 		else
-			warn "Failed to open session via xhost"
+			log "Failed to open session up via xhost"
 		fi
 	fi
+
 	# xresources
 	if [ -n "$DISPLAY" ] && has xrdb && [ -r "$HOME/.Xresources" ]; then
-		if xrdb "$HOME/.Xresources" >/dev/null 2>&1; then
+		if xrdb "$HOME/.Xresources" > /dev/null 2>&1; then
 			log "Loaded in .Xresources"
 		else
-			warn "Failed to load .Xresources"
+			log "Failed to load .Xresources"
 		fi
 	fi
+
 	# xset
 	if has xset; then
 		log "Disabling bell"
@@ -410,81 +453,84 @@ step_preexecute() {
 	fi
 }
 step_execute() {
-	# Parse out our defaults lists and execute their targets
-	if ! [ -d "$_optlogdir" ]; then
-		if ! mkdir -p "$_optlogdir" >/dev/null 2>&1; then
-			error "Failed to create log directory: \"$_optlogdir\"" 53
+	# Ensure we can log if we have to
+	if [ -n "$FS_NOLOG" ]; then
+		log "No logs will be created per FS_NOLOG"
+	elif ! [ -d "$_logdir" ]; then
+		if ! mkdir -p "$_logdir" > /dev/null 2>&1; then
+			err "Failed to create log directory: \"$_logdir\"" 53
 		fi
 	fi
-	for file in "$_optconfigdir"/*; do
+	# Parse configs
+	for file in "$_configdir"/*; do
 		if ! [ -e "$file" ]; then
 			log "No configuration files found; generating defaults"
-			genconfigs
+			step_generate
 			step_execute
-			return
+			break
 		fi
-		# Skip our logs directory
-		[ "$_optlogdir" == "$file" ] && continue
 		local filename="$(basename -- "$file")"
-		local logfile="$_optlogdir/$filename.log"
+		local logfile="$_logdir/$filename"
+		[ -n "$FS_NOLOG" ] && logfile="/dev/null"
 		if gettarget "$file"; then
 			# It's a defaults file with a selected target
 			target="$_return"
-			log "Found target for $filename: \"$target\""
-			[ -n "$_optdryrun" ] && continue
-			if [ -f "$logfile" ]; then
-				[ -f "$logfile.old" ] && rm "$logfile.old"
-				mv "$logfile" "$logfile.old"
+			log "Found target for \"$filename\": \"$target\""
+			if [ -z "$_dryrun" ]; then
+				if [ -f "$logfile" ]; then
+					[ -f "$logfile.old" ] && rm "$logfile.old"
+					mv "$logfile" "$logfile.old"
+				fi
+				bash -c "$target" > "$logfile" 2>&1 &
 			fi
-			bash -c "$target" > "$logfile" 2>&1 &
 		elif [ $? = 50 ] && [ -x "$file" ]; then
-			# It's a shell script or executable symlink
-			log "Executing file: \"$filename\""
-			[ -n "$_optdryrun" ] && continue
-			"$file" > "$logfile" 2>&1 &
+			# It's a shell script or something
+			log "Executing file straight out: \"$filename\""
+			if [ -z "$_dryrun" ]; then
+				"$file" > "$logfile" 2>&1 &
+			fi
 		else
-			warn "Could not execute file: \"$filename\""
+			err "Could not execute file: \"$filename\""
 		fi
 	done
 }
 step_postexecute() {
 	# Wait for the WM to initialize, if one was found and we have the tools
-	if [ -z "$FS_NOWAITWM" ] && gettarget "$_configdir/wm" && has xprop grep; then
+	if [ -z "$FS_NOWAITWM" ] && gettarget "$_configdir/wm" && has xprop && has grep; then
 		log "Waiting for WM to initialize: \"$_return\""
 		for (( i=0; i<10; i++ )); do
 			line="$(xprop -root | grep ^_NET_WM_NAME | grep -o '"\S*"$')"
 			if [ -n "$line" ]; then
-				log "WM has initialized, _NET_WM_NAME atom reads: $line"
+				log "WM has intiailized, _NET_WM_NAME atom reads: $line"
 				break
 			fi
 			read -t 1 -u 1023
 		done
 	fi
-	# Dumb polybar workaround
+	# Dumb Polybar workaround
 	killall polybar -SIGUSR1
-	# Execute a user rc if it exists
-	local firestarterrc="$HOME/.firestarterrc"
-	if [ -r "$firestarterrc" ] && [ -z "$dryrun" ]; then
-		log "Executing rc script: $firestarterrc"
-		"$firestarterrc"
+	# Execute a user script if it exists
+	if [ -r "$_firestarterrc" ] && [ -z "$_dryrun" ]; then
+		log "Executing .firestarterrc"
+		"$_firestarterrc"
 	fi
 	# Start XDG autostarters, if they exist
-	if [ -z "$_optdryrun" ]; then
+	if [ -z "$_dryrun" ]; then
 		if has dex; then
-			dex -a >/dev/null 2>&1
+			dex -a > /dev/null 2>&1
 		elif has fbautostart; then
 			fbautostart > /dev/null 2>&1
 		elif has xdg-autostart; then
 			xdg-autostart ${XDG_CURRENT_DESKTOP:-firestarter}
 		else
-			warn "Could not find an XDG autostarter"
+			err "Could not find an XDG autostarter"
 		fi
 	fi
 }
 step_wait() {
-	[ -n "$_optdryrun" ] && exit 0
+	[ -n "$_dryrun" ] && exit 0
 	trap step_logout EXIT
-	if [ -n "$FS_DIEONWM" ] && gettarget "$_configdir/wm" && has readlink "$_return"; then
+	if [ -n "$FS_DIEONWM" ] && gettarget "$_configdir/wm" && has readlink && has "$_return"; then
 		target="$(command -v "$_return")"
 		for job in $(jobs -p); do
 			if [ "$target" = "$(readlink /proc/$job/exe)" ]; then
@@ -493,7 +539,7 @@ step_wait() {
 				exit 0
 			fi
 		done
-		warn "Could not find WM: \"$target\""
+		err "Could not find WM: \"$target\""
 	fi
 	log "Waiting for programs to exit"
 	wait
@@ -507,108 +553,45 @@ step_logout() {
 			loginctl terminate-session "$_sessionid"
 		fi
 	else
-		# Otherwise just brute it out
+		# Otherwise just brute it
 		kill $(jobs -p)
 	fi
 	return 0
 }
 
-printhelp() {
-	cat << EOF
-Usage: $_name [OPTION]...
-
-  -d			Perform a dry run; print what programs would have been
-			executed instead of doing so
-  -g			Regenerate default configs. This will clobber
-  -h			Print this help text
-  -v			Print more status messages. Stacks
-
-Environment Variables:
-
-  FS_DIEONWM		If nonempty, end the session when the WM dies.
-  FS_NOWAITWM		If nonempty, skip waiting for the WM to initialize
-
-Copyright (c) 2019 rehashedsalt@cock.li
-Licensed under the MIT license
-EOF
-}
-
 # Main
 main() {
-	# Parse out arguments
-	while [ -n "$1" ]; do
-		# Parse out flags
-		while getopts ":dghv" opt; do
-			case $opt in
-				d)
-					_optdryrun=1
-					;;
-				g)
-					_optpregen=1
-					;;
-				h)
-					_opthelp=1
-					;;
-				v)
-					_optverbose+=1
-					;;
-				:)
-					error "Option requires argument: -$OPTARG" 2
-					;;
-				*)
-					error "Invalid option: -$OPTARG" 2
-					;;
-			esac
-		done
-		# Store arguments
-		shift $((OPTIND - 1))
-		if [ -n "$1" ]; then
-			_args+=("$1")
-			shift
-		fi
-		unset OPTIND
+	while getopts ":dgh" opt; do
+		case $opt in
+			d)
+				_dryrun=1
+				;;
+			g)
+				_generate=1
+				;;
+			h)
+				step_printhelp
+				exit $?
+				;;
+			*)
+				err "Unrecognized argument: \"$OPTARG\"" 50
+				;;
+			:)
+				err "Invalid option: \"$OPTARG\"" 51
+				;;
+		esac
 	done
-	# Early hook for help
-	[ -n "$_opthelp" ] && printhelp && exit 0
-	# Early hook for generating configs
-	[ -n "$_optpregen" ] && genconfigs && exit $?
-	# Ensure our running environment is sane and that we're not about to nest
-	if [ -z "$_optdryrun" ] && [ -z "$_optpregen" ]; then
-		for pid in $(pgrep firestarter); do
-			# Skip invalid PIDs
-			! [ -d "$/proc/$pid" ] && continue
-			# If it's not our session then who cares
-			[ "$_sessionid" != "$(< "/proc/$pid/sessionid")" ] && continue
-			# If it's us then who cares
-			[ "$pid" == "$BASHPID" ] && continue
-			# We care
-			error "Firestarter is already running: $pid" 40
-		done
+	step_check
+	if [ -n "$_generate" ]; then
+		step_generate
+	else
+		[ -n "$_dryrun" ] && log "Performing a dry run"
+		step_preexecute
+		step_execute
+		step_postexecute
+		step_wait
 	fi
-	if ! [ -d "$HOME" ] || ! [ -r "$HOME" ]; then
-		error "Home directory not found or inaccessable: \"$HOME\"" 54
-	fi
-	if ! [ -d "$_optconfigdir" ]; then
-		if [ -f "$_optconfigdir" ]; then
-			error "Config directory is a file, should be directory: \"$_optconfigdir\"" 52
-		fi
-		if ! mkdir -p "$_optconfigdir" > /dev/null 2>&1; then
-			error "Failed to find or create config directory: \"$_optconfigdir\"" 52
-		fi
-	fi
-	# Validate core program dependencies
-	log "Validating dependencies" 2
-	if ! has basename; then
-		error "Failed to find program: $_return" 1
-	fi
-
-	# Do the do
-	[ -n "$_optdryrun" ] && log "Performing a dry run"
-	step_preexecute
-	step_execute
-	step_wait
-	step_logout
-	exit 0
+	return 0
 }
 
 main "$@"