#!/bin/bash
# {{{ Copyright
# Soma - a command line/dialog radio player
#
# Copyright 2010 David Woodfall <dave@dawoodfall.net>
# All rights reserved.
# Copyright tmproot modifications Patrick Volkerding, with thanks.
#
# Redistribution and use of this script, with or without modification, is
# permitted provided that the following conditions are met:
#
# 1. Redistributions of this script must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED
# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
# EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# Thanks to Patrick Volkerding for the mktemp patch.

# #shellcheck --exclude=SC2086,SC1117,SC1090
# SC2086 we need double quoting for dialog command options
# SC1117 we want literal backslashes
# SC1090 we want to source non-consistent files


# }}}
# {{{ Misc - Init
init() {
	trap "cleanup" SEGV ABRT HUP INT QUIT TERM PIPE TSTP

	TERM=xterm-color
	SOMAROOT=$(mktemp -d /tmp/soma-XXXXXX)
	STATIONS=$SOMAROOT/soma-stations
	MSTATUS=$SOMAROOT/soma-mstatus
	STATUS=$SOMAROOT/soma-status
	PLAYING=$SOMAROOT/soma-playing
	TMP=$SOMAROOT/soma-tmp
	FIFO=$SOMAROOT/soma.fifo
	MIXERS=$SOMAROOT/mixers
	SYSSOMADIR=/etc/soma
	DEFSTATIONS=$SYSSOMADIR/stations.conf
	SOMADIR=$HOME/.soma
	SYSOPTIONS=$SYSSOMADIR/options.conf
	SOMAOPTIONS=$SOMADIR/options.conf
	USRSTATIONS=$SOMADIR/stations.conf
	SAVEDLIST=$SOMADIR/savedlist
	SCHEDULES=$SOMADIR/schedules.conf
	BOOKMARKS=$SOMADIR/bookmarks.conf
	SYSTHEMEDIR=$SYSSOMADIR/themes
	SYSGENREDIR=$SYSSOMADIR/genres
	USRTHEMEDIR=$SOMADIR/themes
	USRGENREDIR=$SOMADIR/genres
	export DIALOGRC=$USRTHEMEDIR/defaultsrc
	touch "$STATUS" "$MSTATUS"

	# Make our local dirs
	[ ! -d "$USRTHEMEDIR" ] && mkdir -p "$USRTHEMEDIR"
	[ ! -d "$USRGENREDIR" ] && mkdir -p "$USRGENREDIR"
	[ ! -f "$USRSTATIONS" ] && touch "$USRSTATIONS"

	# Copy the global options if local doesn't exist
	[ ! -r "$SOMAOPTIONS" ] && cat "$SYSOPTIONS" > "$SOMAOPTIONS"

	# Check to see if we have latest options
	if ! grep -qE "^DIALOGOPTS=" "$SOMAOPTIONS" ; then
		echo "
# Dialog options
# Set to \"\" for no formatting/colour in titles and also unset TITCOL.
# Add \"--no-mouse\" to disable the mouse.
# Default = \"--colors\"
DIALOGOPTS=\"--colors\"" >> "$SOMAOPTIONS"
	fi

	if ! grep -qE "^SETVOL=" "$SOMAOPTIONS" ; then
		echo "
# Saved volume from last session
SETVOL=\"50 50\"" >> "$SOMAOPTIONS"
	fi

	if ! grep -qE "^CACHE=" "$SOMAOPTIONS" ; then
		echo "
# MPlayer cache. Default 320
CACHE=320" >> "$SOMAOPTIONS"
	fi

	if ! grep -qE "^SAVE_LIST_AT_QUIT=" "$SOMAOPTIONS" ; then
		echo "
# Save the track list automatically when quitting.
# Allowed values: 0 or 1
# Default 0
SAVE_LIST_AT_QUIT=0" >> "$SOMAOPTIONS"
	fi

	if ! grep -qE  "^GENRE=" "$SOMAOPTIONS" ; then
		echo "
# Saved genre
GENRE=\"All\"" >> "$SOMAOPTIONS"
	fi

	if ! grep -qE "^TITCOL=" "$SOMAOPTIONS" ; then
		echo '
# TITCOL - Dialog Title Colour
# This setting controls the dialog title colour separate to the
# dialogrc file if needed.
#
# By default it is set to "\Zb" (bold default dialog foreground colour).
# You can mix bold and colour by eg: "\Zb\Z4" etc. See man dialog for
# more info.
#
# Colours are set by \Zn where n =:
#
# 0 black
# 1 red
# 2 green
# 3 yellow
# 4 blue
# 5 magenta
# 6 cyan
# 7 white
# 8 default dialogrc foreground
TITCOL="\Zb"' >> "$SOMAOPTIONS"
	fi

	if ! grep -qE "^CONNECTTIMEOUT=" "$SOMAOPTIONS" ; then
		echo "
# CONNECTTIMEOUT (Connection Timeout)
#
# This controls how long soma will wait for a stream before giving up.
# If you have problems with your internet you may want to adjust it.
# Default: 150
CONNECTTIMEOUT=150" >> "$SOMAOPTIONS"
	fi

	if ! grep -qE "^TIMEFORMAT=" "$SOMAOPTIONS" ; then
		echo "
# TIMEFORMAT (Format for time display)
#
# This controls how the time is displayed in the status window.
# It defaults to 12 hour format.
# You can change it here.
# Example: 24 hour format would be \"%H:%M\"
# See the date man page for full options.
# Default: \"%I:%M\"
TIMEFORMAT=\"%I:%M\"" >> "$SOMAOPTIONS"
	fi

	if ! grep -qE "^CARET=" "$SOMAOPTIONS" ; then
		echo "
# CARET (Caret Character)
#
# This is the character(s) used in listings.
# Do not use an empty string!
# Default: \"——>\"
CARET=\"——>\"" >> "$SOMAOPTIONS"
	fi

	if ! grep -qE "^SETTERMTITLE=" "$SOMAOPTIONS" ; then
		echo "
# SETTERMTITLE (Set The Terminal/Screen Title to 'Soma')
# Allowed values: 0 or 1
# Default: 0
SETTERMTITLE=0" >> "$SOMAOPTIONS"
	fi

	if ! grep -qE "^AUTORECONNECT=" "$SOMAOPTIONS" ; then
		echo "
# AUTORECONNECT (Autoreconnect to Lost Streams rather than show a popup dialog)
# Allowed values: 0 or 1
# Default: 0
AUTORECONNECT=0" >> "$SOMAOPTIONS"
	fi

	if ! grep -qE "^SHOWAUDIOINFO=" "$SOMAOPTIONS" ; then
		echo "
# SHOWAUDIOINFO (Show Audio and Codec in Stream Info)
# Allowed values: 0 or 1
# Default: 1
SHOWAUDIOINFO=1" >> "$SOMAOPTIONS"
	fi

	if ! grep -qE "^SHOWVOLUME=" "$SOMAOPTIONS" ; then
		echo "
# SHOWVOLUME (Show Current Volume in Title Bar)
# Allowed values: 0 or 1
# Default: 1
SHOWVOLUME=1" >> "$SOMAOPTIONS"
	fi

	if [ "$TERM" = "linux" ] && [ "$CARET" = "——>" ]; then
		CARET="-->"
	fi

	. "$SYSOPTIONS"
	. "$SOMAOPTIONS"
	touch "$SCHEDULES"

	[ -z "$CARET" ] && CARET="——>"
	[ -z "$GENRE" ] && GENRE="All"
	[ "$SETTERMTITLE" -eq 1 ] && printf "\033ksoma\033\\"

	count=$(aplay -l | grep -c "^card")
	aplay -l | grep "^card" > "$TMP"

	DEVICE[0]="pulse"
	DEVICE[1]="alsa"

	for card in $(seq 2 $((count+1)))
	do
		SOUNDCARD[$card]=$(head -n $((card-1)) "$TMP" | tail -n 1)
		major=$(echo "${SOUNDCARD[card]}" | sed -n "s/.* \(.*\):.*:.*/\1/p")
		minor=$(echo "${SOUNDCARD[card]}" | sed -n "s/.* \(.*\):.*/\1/p")
		DEVICE[$card]="device=hw=$major,$minor"
		HWDEVICE[$card]="${SOUNDCARD[card]}"
	done

	NUMDEVICES="$card"

	DEVICE[$((card+1))]="jack"
	DEVICE[$((card+2))]="NAS"

	alias grep="grep --binary-file=text"

	# Various vars
	SCHED_ENABLED=$((1))
	DEFIFS="$IFS"
	EXTIFS=$'\n'

	#List types
	__ALL=$((0))
	__GENRE=$((1))
	__BOOKMARKS=$((2))
	__LOCAL=$((3))

	# Checkbox settings
	boxval=("off" "on")

	# Category names
	category=("All" "$GENRE" "Bookmarks" "User Stations")

	# Non-breaking spaces
	BLANK="                                                    "
	LBLANK=$((${#BLANK}))
}
# }}}
# {{{ Misc - Command line
get_commandline() {
	do_bookmarks=$((0))
	do_stations=$((0))
	do_all_stations=$((0))
	do_genres=$((0))
	do_local_genres=$((0))
	DEBUG=$((0))

	until [ -z "$1" ]
	do
		[ "$1" = "-h" ] && usage && cleanup
		[ "$1" = "-v" ] && version && cleanup
		[ "$1" = "-d" ] && DEBUG=1
		[ "$1" = "-l" ] && do_bookmarks=1
		[ "$1" = "-b" ] && do_stations=1
		[ "$1" = "-B" ] && do_all_stations=1
		[ "$1" = "-g" ] && do_local_genres=1
		[ "$1" = "-G" ] && do_genres=1
		shift
	done

	[ $do_bookmarks -eq 1 ] && station_selector $__BOOKMARKS
	[ $do_stations -eq 1 ] && station_selector $__GENRE
	[ $do_all_stations -eq 1 ] && station_selector $__ALL
	[ $do_genres -eq 1 ] && genre_selector
	[ $do_local_genres -eq 1 ] && local_genre_selector
}
# }}}
# {{{ Misc - Cleanup/exit
cleanup() {
	if [ "$SAVE_LIST_AT_QUIT" -eq 1 ]; then
		save
	fi

	pgrep -x mplayer | grep -qw "$MPLAYERPID" && echo quit > "$FIFO"

	rm -rf "$SOMAROOT"
	cursor on
	exit 0
}
# }}}
# {{{ Misc - Version
version() {
	echo "Soma $VERSION"
}
# }}}
# {{{ Misc - Save Tracklist
save() {
	rows=$(stty size | cut -f1 -d' ')
	cols=$(stty size | cut -f2 -d' ')
	[ "$CHANNEL" = "Idle" ] && CHANNEL="$OLDCHANNEL"
	echo -n "$CHANNEL "; date; grep -E "^Title" "$STATUS"; echo >> "$SAVEDLIST"
	do_msg "Saved Tracks" "Tracks list appended to $SAVEDLIST" 1
}
# }}}
# {{{ Misc - Purge local files
purge_local()
{
	if [ -z "$USRTHEMEDIR" ] || [ -z "$USRGENREDIR" ]; then
		echo "Problem with local dirs. Aborting."
		return
	fi

	ls "$USRTHEMEDIR"/ >/dev/null 2>&1 || return

	# Purge local files that are identical to global
	# This will be removed at some point in the future
	for th in "$USRTHEMEDIR"/*
	do
		bt=$(basename "$th")
		if [ -r "$SYSTHEMEDIR/$bt" ]; then
			if [ "$(md5sum "$USRTHEMEDIR/$bt" | awk '{print $1}')" \
				= "$(md5sum "$SYSTHEMEDIR/$bt" | awk '{print $1}')" ]; then
				rm -f "$USRTHEMEDIR/$bt"
		fi
	fi
	done

	ls "$USRGENREDIR"/*.conf >/dev/null 2>&1 || return
	IFS=$EXTIFS

	for gn in "$USRGENREDIR"/*.conf
	do
		bg=$(basename "$gn")
		if [ -r "$SYSGENREDIR/$bg" ]; then
			if [ "$(md5sum "$USRGENREDIR/$bg" | awk '{print $1}')" \
				= "$(md5sum "$SYSGENREDIR/$bg" | awk '{print $1}')" ]; then
				rm -f "$USRGENREDIR/$bg"
		fi
	fi
	done

	IFS=$DEFIFS
}
# }}}
# {{{ Help - Usage
usage() {
cat << EOF
Soma $VERSION
A dialog + mplayer radio player.

$(get_opts)

Config Files:

User:                        Global:

~/.soma/options.conf         /etc/soma/options.conf
~/.soma/stations.conf        /etc/soma/stations.conf
~/.soma/bookmarks.conf       /etc/soma/themes/*rc
~/.soma/themes/*rc           /etc/soma/genres/*.conf
~/.soma/genres/*.conf
~/.soma/schedules.conf

Custom genre files can be made and will be listed along with the default set.

EOF
}
# }}}
# {{{ Help - Get Options
get_opts()
{
	echo "Keys supported in status window:

1 - 0      Volume 10%, 20%, ... 100%
(          Volume -5                   g          List genres
)          Volume +5                   h          List local genres
Space      Pause/Play                  k          Bookmark current station
a          Add schedule                j          Add current station to genre
b          Station browser (genre)     l          Open bookmark list
B          Station browser (all)       m          Toggle mute
d          Disconnect/Reconnect        o          Options menu
D          Toggle debug mode           s          Scheduler menu
?          Show keys                   S          Toggle schedules temporarily
q          Quit                        t          Change theme

Startup flags:

soma -l    go straight to bookmarks list
soma -b    go straight to stations browser for genre
soma -B    go straight to all stations browser
soma -g    go straight to local genre browser
soma -G    go straight to global genre browser
soma -d    to start in debug mode
"
}
# }}}
# {{{ Help - Show Options
show_opts() {
	clear
	cat << EOF
$(get_opts)

EOF

	echo "Press any key to return"
	read -rsn1
}
# }}}
# {{{ Help - Halp!
help()
{
	clear
	show_opts
	echo "Press any key to return"
	read -rsn1
}
# }}}
# {{{ Play - Play
play()
{
	. "$SOMAOPTIONS"

	if pgrep -x mplayer | grep -qw "$MPLAYERPID" >/dev/null 2>&1 ; then
		# MPlayer has a loadfile command which you can echo into the fifo
		# It fails most of the time and doesn't recognise -playlist
		# so the best thing to do is quit it and start a new instance.
		echo quit > "$FIFO"
	else
		if [ ! -r "$FIFO" ]; then
			mkfifo "$FIFO"
		fi
	fi

	OLDCHANNEL="$CHANNEL"
	rm -f "$MSTATUS"

# It seems that -af has no effect when using -volume, so it's disabled for now.
#	# Radio 3 seems to have much lower output level than the others
#	# This should balance it out by boosting volume
#	if [ -n "$AFFILTER" ] && [ "$CHANNEL" = "BBC Radio 3" ]; then
#		AF="$AFFILTER"
#	else
#		unset AF
#	fi

	if echo "$STATIONURL" | grep -qP '\.(pls|m3u|ram|asx|wvx|m3u8)' ; then
		PLAYLIST="-playlist"
	else
		unset PLAYLIST
	fi

	[ -n "$SETVOL" ] && VOL="-volume $SETVOL"

	mplayer -ao "$OUTCARD" -slave -input file="$FIFO" -msglevel all=4 \
		"$PLAYLIST" "$STATIONURL" -cache "$CACHE" \
		"$SOFTVOL" $VOL > "$MSTATUS" 2>/dev/null &
		MPLAYERPID=$!

	TERM=xterm-color
	if [ "$DEBUG" -eq 0 ]; then
		dialog $DIALOGOPTS --title "${TITCOL}Status" --ok-label "Menu" \
			--timeout 1 --msgbox \
			"Connecting to $CHANNEL ..."  "$rows" "$cols" 2> /dev/null
	else
		TIME=$(date +"$TIMEFORMAT")
		dialog $DIALOGOPTS --exit-label "Menu" --keep-window --timeout 1 \
			--title "${TITCOL}[$TIME] $CHANNEL - Raw MPlayer Output" \
			--tailbox "$MSTATUS" "$rows" "$cols" 2> "$TMP"
	fi

}
# }}}
# {{{ Play - Setup
# setup the info. loop until we have some
# this is really to shorten the title loop
setup()
{
	unset TITLE
	unset COUNT

	# wait for info to appear
	while ! grep -qE '^AUDIO' "$MSTATUS"
	do
		if [ "$DEBUG" -eq 1 ]; then
			TIME=$(date +"$TIMEFORMAT")
			dialog $DIALOGOPTS --exit-label "Menu" --keep-window --timeout 1 \
				--title "${TITCOL}[$TIME] $CHANNEL - Raw MPlayer Output" \
				--tailbox "$MSTATUS" "$rows" "$cols" 2> "$TMP"
			break
		fi

		if [ "$DEBUG" -eq 0 ]; then
			sleep 0.1
			COUNT=$((COUNT+1))

			if ! pgrep -x mplayer | grep -qw "$MPLAYERPID" ; then
				do_msg " Error" "    No stream!    " 0
				sed -i '$d' "$STATUS"
				lasttitle="Idle"
				CHANNEL="Idle"
				unset MPLAYERPID
				return
			fi

			if [ "$COUNT" -gt "$CONNECTTIMEOUT" ]; then
				dialog --ok-label "Menu" \
					--msgbox "Connection timeout occurred.\n\n\
	If your internet connection is slow, \
	you may want to try\nadjusting the CONNECTTIMEOUT \
	value in ~/.soma/options.conf\nor through the options menu." \
				"$rows" "$cols"

			sed -i '$d' "$STATUS"
			echo quit > "$FIFO"
			lasttitle="Idle"
			CHANNEL="Idle"
			unset MPLAYERPID
			mainmenu
			return
			fi
	fi
	done

	NAME="$(sed -nr "s/^[nN]ame\s*: (.*)/\1/p" "$MSTATUS")"
	if echo "$NAME" | grep -qE "^bbc" ; then NAME="$CHANNEL"; fi
	MGENRE="$(sed -nr "s/^Genre\s*: ([^:].*)/\1/p" "$MSTATUS")"
	WEBSITE="$(sed -nr "s/^Website\s*: (.*:.*).*/\1/p" "$MSTATUS")"
	BITRATE="$(sed -nr "s/^Bitrate\s*: ([^:].*)/\1/p" "$MSTATUS")"
	AUTHOR="$(sed -nr "s/^[aA]uthor\s*: ([^:].*)/\1/p" "$MSTATUS")"
	COPY="$(sed -nr "s/^[cC]opyright\s*: ([^:].*)/\1/p" "$MSTATUS")"
	AUDIO="$(sed -nr "s/^AUDIO\s*: (.*)/\1/p" "$MSTATUS")"
	CODEC="$(sed -nr "s/.*codec: (.*[^:].*)/\1/p" "$MSTATUS")"

	if [ "$STATIONURL" != "$laststation" ]; then
		[ -n "$(< $STATUS)" ] && echo >> "$STATUS"
		[ -n "$NAME" ] &&   echo    "Name    : $NAME" >> "$STATUS"
		[ -n "$MGENRE" ] && echo    "Genre   : $MGENRE" >> "$STATUS"
		[ -n "$WEBSITE" ] &&  echo  "Website : $WEBSITE" >> "$STATUS"
		[ -n "$AUTHOR" ] && echo    "Author  : $AUTHOR" >> "$STATUS"
		[ -n "$COPY" ] && echo      "(C)     : $COPY" >> "$STATUS"

		if [ "$SHOWAUDIOINFO" -eq 1 ]; then
			[ -n "$BITRATE" ] && echo "Bitrate : $BITRATE" >> "$STATUS"
			[ -n "$AUDIO" ] && echo   "Audio   : $AUDIO" >> "$STATUS"
			[ -n "$CODEC" ] && echo   "Codec   : $CODEC" >> "$STATUS"
		fi
	fi
}
# }}}
# {{{ Play - Get Current title
get_title() {
	unset TITLE

	TITLE=$(grep StreamTitle "$MSTATUS" | cut -d\; -f1 | cut -d= -f2 | tail -n 1)

	# Probably a station ID. Don't print it.
		if echo "$TITLE" | grep -qi SomaFM \
			&& echo "$lasttitle" | grep -qi SomaFM; then
			TITLE="$lasttitle"
			return
		fi

	if [ -z "$TITLE" ]; then
		TITLE=$(grep name "$MSTATUS" | cut -d: -f2 | tail -n 1)
	fi
	if [ -z "$TITLE" ]; then
		TITLE=$(grep title "$MSTATUS" | cut -d: -f2 | tail -n 1)
	fi

	TITLE="${TITLE/#\'/}"
	TITLE="${TITLE/%\'/}"
	TITLE="${TITLE/#Now playing:/}"
	TITLE="${TITLE/# /}"
	TITLE="${TITLE//  / }"

	while echo "$TITLE" | grep -q '| |'
	do
		TITLE="${TITLE//| |/|}"
	done

	[ -z "$TITLE" ] && TITLE="$CHANNEL"

	if [ "Title   : $TITLE" != "$(tail -n 1 "$STATUS")" ] \
		&& [ "$TITLE" != "Idle" ] && [ "$CHANNEL" != "Idle" ]; then
		if ! echo "$NAME" | grep -qE "^BBC" && [ "$NAME" != "Radio 3HD" ]; then
			if [ "$TITLE" != "$lasttitle" ]; then
				echo "Title   : $TITLE" >> "$STATUS"
			fi
		fi
		echo "$CHANNEL:$TITLE" > "$PLAYING"
	fi
}
# }}}
# {{{ Play - Print Title
print_title()
{
	if [ "Title     : $TITLE" != "$(tail -n 1 "$STATUS")" ] \
		&& [ "$TITLE" != "Idle" ] && [ "$CHANNEL" != "Idle" ]; then
		if ! echo $NAME | grep -qE ^BBC && [ "$NAME" != "Radio 3HD" ]; then
			echo "Title     : $TITLE" >> "$STATUS"
		fi
		echo "$CHANNEL:$TITLE" > "$PLAYING"
	fi

}
# }}}
# {{{ Play - Status
status() {
	rows=$(stty size | cut -f1 -d' ')
	cols=$(stty size | cut -f2 -d' ')
	TIME=$(date +"$TIMEFORMAT")
	TIME24=$(date +"%H:%M")

	if [ -n "$(< "$SCHEDULES")" ]; then
		[ "$SCHED_ENABLED" -eq 1 ] && DASH="[S]" || DASH="[-]"
	fi

	if ! pgrep -x mplayer | grep -qw "$MPLAYERPID"; then
		CHANNEL="Idle"
	fi

	pgrep -x mplayer | grep -qE "^$MPLAYERPID$" && get_title

	{ [ "$SHOWVOLUME" -eq 1 ] && VOLUME="[${SETVOL/ */}]"; } || unset VOLUME
	dialog $DIALOGOPTS --exit-label "Menu" --keep-window --timeout 1 \
		--title "${TITCOL}[$TIME]$DASH $CHANNEL $VOLUME" --tailbox "$STATUS" \
		"$rows" "$cols" 2>/dev/null

	while :
	do
		rows=$(stty size | cut -f1 -d' ')
		cols=$(stty size | cut -f2 -d' ')

		pgrep -x mplayer | grep -qE "^$MPLAYERPID$" && get_title

		if [ -n "$(< "$SCHEDULES")" ]; then
			[ "$SCHED_ENABLED" -eq 1 ] && DASH="[S]" || DASH="[-]"
		fi

		if [ -n "$MPLAYERPID" ]; then
			# No mplayer
			if ! pgrep -x mplayer | grep -qw "$MPLAYERPID"; then
				if [ "$AUTORECONNECT" -eq 0 ]; then
					dialog $DIALOGOPTS --title "${TITCOL}Status" \
					--yes-label "Menu" --no-label "Reconnect" --yesno \
					"Lost the stream!\n\nPress R to reconnect.\nPress Enter/M to go to the menu." 10 40

					if [ "$?" -eq 1 ]; then
						CHANNEL="$OLDCHANNEL"
						unset MPLAYERPID
						play
						setup
						status
					else
						unset MPLAYERPID
						CHANNEL="Idle"
						mainmenu
					fi
				else
					do_msg "Autoreconnect" "Lost the stream.\nReconnecting in 10 seconds..." 10
					CHANNEL="$OLDCHANNEL"
					unset MPLAYERPID
					play
					setup
					status
				fi
			fi
		else
			# Idling
			unset MPLAYERPID
			CHANNEL="Idle"
		fi

		# Normal playing
		lasttime="$TIME24"
		TIME=$(date +"$TIMEFORMAT")
		TIME24=$(date +"%H:%M")
		DATE=$(date +"%Y%m%d")
		FULLDAY=$(date +"%a")
		DAY="${FULLDAY:0:2}"

		if [ "$TIME24" != "$lasttime" ]; then
			SCHEDCHAN=$(grep -E "($DATE|$DAY|\*),$TIME24" "$SCHEDULES" | cut -d, -f3)
			if [ "$SCHED_ENABLED" -eq 1 ] && [ -n "$SCHEDCHAN" ] && [ "$SCHEDCHAN" != "$CHANNEL" ]; then
				schedule_change_station
			fi
		fi

		lasttitle="$TITLE"
		input

		{ [ "$SHOWVOLUME" -eq 1 ] && VOLUME="[${SETVOL/ */}]"; } || unset VOLUME
		dialog $DIALOGOPTS  --exit-label "Menu" --keep-window --timeout 1 \
			--title "${TITCOL}[$TIME]$DASH $CHANNEL $VOLUME" --tailbox "$STATUS" \
			"$rows" "$cols" 2> "$TMP"
	done
}
# }}}
# {{{ Play - Raw Status
rawstatus() {
	rows=$(stty size | cut -f1 -d' ')
	cols=$(stty size | cut -f2 -d' ')

	TIME=$(date +"$TIMEFORMAT")
	TIME24=$(date +"%H:%M")

	if [ -n "$(< "$SCHEDULES")" ]; then
		[ "$SCHED_ENABLED" -eq 1 ] && DASH="[S]" || DASH="[-]"
	fi

	if ! pgrep -x mplayer | grep -qw "$MPLAYERPID"; then
		CHANNEL="Idle"
	fi

	get_title

	{ [ "$SHOWVOLUME" -eq 1 ] && VOLUME="[${SETVOL/ */}] ";} || unset VOLUME
	dialog $DIALOGOPTS --exit-label "Menu" --keep-window --timeout 1 \
		--title "${TITCOL}[$TIME]$DASH $CHANNEL $VOLUME- Raw MPlayer Output" \
		--tailbox "$MSTATUS" "$rows" "$cols" 2> "$TMP"

	while :
	do
		rows=$(stty size | cut -f1 -d' ')
		cols=$(stty size | cut -f2 -d' ')

		[ "$(pgrep -x mplayer)" = "$MPLAYERPID" ] && get_title

		lasttime="$TIME24"
		TIME=$(date +"$TIMEFORMAT")
		TIME24="$(date +"%H:%M")"
		DATE=$(date +"%Y%m%d")
		FULLDAY=$(date +"%a")
		DAY="${FULLDAY:0:2}"

		if [ "$TIME24" != "$lasttime" ]; then
			SCHEDCHAN=$(grep -E "($DATE|$DAY|\*),$TIME24" "$SCHEDULES" | cut -d, -f3)
			if [ "$SCHED_ENABLED" -eq 1 ] && [ -n "$SCHEDCHAN" ] && [ "$SCHEDCHAN" != "$CHANNEL" ]; then
				schedule_change_station
			fi
		fi

		lasttitle="$TITLE"
		input

		{ [ "$SHOWVOLUME" -eq 1 ] && VOLUME="[${SETVOL/ */}] ";} || unset VOLUME
		dialog $DIALOGOPTS --exit-label "Menu" --keep-window --timeout 1 \
			--title "${TITCOL}[$TIME]$DASH $CHANNEL $VOLUME- Raw MPlayer Output" \
			--tailbox "$MSTATUS" "$rows" "$cols" 2> "$TMP"

	done
}
# }}}
# {{{ Play - Input
input() {
	unset REPLY
	read -rsn1 -t2 || return

	case $REPLY in
		' ')
			echo "pause"  > "$FIFO"
			;;
		'1')
			SETVOL="10 10"
			sed -i "s/^SETVOL=\".*\"/SETVOL=\"$SETVOL\"/" "$SOMAOPTIONS"
			echo "volume $SETVOL"  > "$FIFO"
			;;
		'2')
			SETVOL="20 20"
			sed -i "s/^SETVOL=\".*\"/SETVOL=\"$SETVOL\"/" "$SOMAOPTIONS"
			echo "volume $SETVOL"  > "$FIFO"
			;;
		'3')
			SETVOL="30 30"
			sed -i "s/^SETVOL=\".*\"/SETVOL=\"$SETVOL\"/" "$SOMAOPTIONS"
			echo "volume $SETVOL"  > "$FIFO"
			;;
		'4')
			SETVOL="40 40"
			sed -i "s/^SETVOL=\".*\"/SETVOL=\"$SETVOL\"/" "$SOMAOPTIONS"
			echo "volume $SETVOL"  > "$FIFO"
			;;
		'5')
			SETVOL="50 50"
			sed -i "s/^SETVOL=\".*\"/SETVOL=\"$SETVOL\"/" "$SOMAOPTIONS"
			echo "volume $SETVOL"  > "$FIFO"
			;;
		'6')
			SETVOL="60 60"
			sed -i "s/^SETVOL=\".*\"/SETVOL=\"$SETVOL\"/" "$SOMAOPTIONS"
			echo "volume $SETVOL"  > "$FIFO"
			;;
		'7')
			SETVOL="70 70"
			sed -i "s/^SETVOL=\".*\"/SETVOL=\"$SETVOL\"/" "$SOMAOPTIONS"
			echo "volume $SETVOL"  > "$FIFO"
			;;
		'8')
			SETVOL="80 80"
			sed -i "s/^SETVOL=\".*\"/SETVOL=\"$SETVOL\"/" "$SOMAOPTIONS"
			echo "volume $SETVOL"  > "$FIFO"
			;;
		'9')
			SETVOL="90 90"
			sed -i "s/^SETVOL=\".*\"/SETVOL=\"$SETVOL\"/" "$SOMAOPTIONS"
			echo "volume $SETVOL"  > "$FIFO"
			;;
		'0')
			SETVOL="100 100"
			sed -i "s/^SETVOL=\".*\"/SETVOL=\"$SETVOL\"/" "$SOMAOPTIONS"
			echo "volume $SETVOL"  > "$FIFO"
			;;
		'd')
			if [ "$CHANNEL" = "Idle" ]; then
				if [ -n "$OLDCHANNEL" ]; then
					CHANNEL="$OLDCHANNEL"
					play
					setup
					status
				fi
			else
				echo quit > "$FIFO"
				unset MPLAYERPID
				CHANNEL="Idle"
			fi
			;;
		'')
			mainmenu
			;;
		'(')
			echo "volume -50"  > "$FIFO"
			;;
		')')
			echo "volume +50"  > "$FIFO"
			;;
		'm')
			echo "mute" > "$FIFO"
			;;
		'q')
			cleanup
			;;
		'b')
			station_selector $__GENRE
			;;
		'B')
			station_selector $__ALL
			;;
		's')
			schedule_main
			;;
		'S')
			[ -z "$(< "$SCHEDULES")" ] && return
			{ [ "$SCHED_ENABLED" -eq 1 ] && SCHED_ENABLED=0; } || SCHED_ENABLED=1
			if [ "$SCHED_ENABLED" -eq 1 ]; then
				do_msg "Schedules" "Schedules have been enabled." 1
				DASH="[S]"
			else
				do_msg "Schedules" "Schedules have been temporarily disabled." 1
				DASH="[-]"
			fi
			;;
		'a')
			schedule_add
			;;
		'?')
			show_opts
			;;
		'o')
			edit_options
			;;
		't')
			loop_themes
			;;
		'g')
			genre_selector
			;;
		'h')
			local_genre_selector
			;;
		'j')
			add_current_station_to_genre
			;;
		'k')
			add_bookmark
			;;
		'l')
			station_selector $__BOOKMARKS
			;;
		'D')
			{ [ "$DEBUG" -eq 0 ] && DEBUG=$((1)); } || DEBUG=$((0))
			{ [ "$DEBUG" -eq 0 ] && status; } || rawstatus
			;;
		'C')
			[[ -x /usr/bin/xclip ]] && echo -n "$STATIONURL" | xclip -sel clip
			[[ -x /usr/bin/xsel ]] && echo -n "$STATIONURL" | xsel -b
			;;
		*)
			;;
	esac
}
# }}}
# {{{ Stations - Make a Nice List
# Use TMP for input and output
nice_list()
{
	rows=$(stty size | cut -f1 -d' ')
	cols=$(stty size | cut -f2 -d' ')
	unset NICELIST
	TITLE=$1
	BUTT=$2
	if [ -n "$3" ]; then
		HELP="--help-button --help-status --help-label \"$3\""
	else
		unset HELP
	fi

	while read -r item
	do
		if [ -z "$WITHURL" ]; then
			len=$LBLANK-${#item}

			# This makes menu item have a blank filler line
			NICELIST=$NICELIST$(echo "\"$item\"" \
				| sed "s#\([^\"]\)\" *#\1${BLANK:1:$len}\" #")" \"$CARET\" "

			# This just prints menu items with no filler
			#NICELIST=$NICELIST"\"$item\" \"$CARET\" "
		else
			NICELIST=$NICELIST"$item "
		fi
	done < "$TMP"

	# sh -c "" properly sends the quoted strings to dialog
	sh -c "dialog $DIALOGOPTS --title \"${TITCOL}$TITLE\" --ok-label $BUTT $HELP \
		--cancel-label \"Back\" --menu \"\" $rows $cols $rows $NICELIST" 2> "$TMP"

	return $?
}
# }}}
# {{{ Stations - Make a Nice List With URL
# Use TMP for input and output
nice_list_with_url()
{
	rows=$(stty size | cut -f1 -d' ')
	cols=$(stty size | cut -f2 -d' ')
	unset NICELIST
	TITLE=$1
	BUTT=$2

	if [ -n "$3" ]; then
		HELP="--help-button --help-status --help-label \"$3\""
	else
		unset HELP
	fi

	while read -r item
	do
		# This prints station and URL
		NICELIST=$NICELIST"$item "
	done < "$TMP"

	# sh -c "" properly sends the quoted strings to dialog
	sh -c "dialog $DIALOGOPTS --ok-label \"$BUTT\" $HELP --cancel-label \"Back\" \
		--menu \"${TITCOL}$TITLE\" $rows $cols $rows \
		$NICELIST" 2> "$TMP"

	return $?
}
# }}}
# {{{ Stations - Get Stations List
get_station_list()
{
	unset TMPLIST
	unset STATIONLIST

	cat "$USRSTATIONS" "$DEFSTATIONS" \
		| sed 's#\s*,#,#g;s#,\s*#,#g;s#^\s*##g;s#\s*$##g;#^$#d' \
		> "$STATIONS"

	case $1 in
		$__ALL)
			TMPLIST="$STATIONS"
			;;
		$__BOOKMARKS)
			TMPLIST="$BOOKMARKS"
			;;
		$__GENRE)
			if [ -r "$SYSGENREDIR/$GENRE.conf" ]; then
				TMPLIST="$SYSGENREDIR/$GENRE.conf"
			fi
			if [ -r "$USRGENREDIR/$GENRE.conf" ]; then
				TMPLIST="$USRGENREDIR/$GENRE.conf"
			fi
			;;
		$__LOCAL)
			TMPLIST="$USRSTATIONS"
			;;
		*)
			TMPLIST="$STATIONS"
			;;
	esac

		STATIONLIST=$(sed \
					"s#\s*,#,#g
					s#,\s*#,#g
					s#^\s*##g
					s/\s*$//g
					s#^#\"#g
					s#,#\" \"#g
					s/$/\"/g" "$TMPLIST")
}
# }}}
# {{{ Stations - Station Selector
station_selector() {
	SELECTOR=$1
	DELETE="Delete"

	[ -z "$GENRE" ] && GENRE="All"
	[ "$GENRE" = "All" ] && [ "$SELECTOR" -eq "$__GENRE" ] && SELECTOR="$__ALL"
	[ "$CHANNEL" != "Idle" ] && OLDCHANNEL="$CHANNEL"
	[ "$SELECTOR" -eq "$__ALL" ] && unset DELETE

	while :
	do
		get_station_list "$SELECTOR"

		if [ -z "$STATIONLIST" ]; then
			if [ "$SELECTOR" -eq "$__GENRE" ]; then
				do_msg "List Stations" "No stations in current genre." 1
				return
			else
				return
			fi
		fi

		[ "$1" -eq $__ALL ] && GEN="All"
		[ "$1" -eq $__GENRE ] && GEN="$GENRE"
		[ "$1" -eq $__BOOKMARKS ] && GEN="Bookmarks"
		[ "$1" -eq $__LOCAL ] && GEN="User Stations"

		if [ -z "$WITHURL" ]; then
			echo "$STATIONLIST" | sed -n "s/\"\(.*\)\" \".*$/\1/p" > "$TMP"
		else
			echo "${STATIONLIST//\\/}" > "$TMP"
		fi
		nice_list "$GEN" "Play" $DELETE
		retval=$?

		[ $retval -ne 0 ] && [ $retval -ne 2 ] && return
		# Next line contains a non-breaking space!
		CHAN=$(sed "s/ *$//" "$TMP")

		if [ $retval -eq 2 ]; then
			CHAN=${CHAN#HELP }
			remove_single_station "$CHAN" "$1" || return
		else
			CHANNEL=$CHAN
			STATIONURL=$(sed -n "s/^$CHAN,\(.*\)/\1/p" "$TMPLIST")
			break
		fi
	done

	[ "$STATIONURL" = "$laststation" ] && status
#	[ -n "$(< "$STATUS")" ] && echo >> "$STATUS"

	play
	setup
	laststation=$STATIONURL
	{ [ "$DEBUG" -eq 0 ] && status; } || rawstatus
}

# }}}
# {{{ Output - Select Soundcard
selectoutput() {
	rows=$(stty size | cut -f1 -d' ')
	cols=$(stty size | cut -f2 -d' ')
	lasttitle=$TITLE
	unset CARDLIST
	tmp=$OUTCARD
	status="off"

	[ "$tmp" = "pulse" ] && status="on" || status="off"
	CARDLIST="0 \"Pulseaudio.\" $status "
	[ "$tmp" = "alsa" ] && status="on" || status="off"
	CARDLIST+="1 \"Alsa default softmix.\" $status "

	for i in $(seq 2 "$NUMDEVICES")
	do
		[ "$OUTCARD" = "alsa:${DEVICE[i]}" ] && status="on" || status="off"
		CARDLIST+="$i \"alsa:${HWDEVICE[i]}\" $status "
	done

	[ "$tmp" = "jack" ] && status="on" || status="off"
	CARDLIST="$CARDLIST $((i+1)) \"Jack.\" $status "

	[ "$tmp" = "NAS" ] && status="on" || status="off"
	CARDLIST="$CARDLIST $((i+2)) \"NAS.\" $status "

	sh -c "dialog $DIALOGOPTS --radiolist \"${TITCOL}Output Panel\" \
		$rows $cols $rows $CARDLIST 2>$TMP" || return

	RESULT=$(< "$TMP")

	if [ "$RESULT" -eq 0 ]; then
		OUTCARD="pulse"
	elif [ "$RESULT" -eq 1 ]; then
		OUTCARD="alsa"
	elif [ "$RESULT" -eq $((i+1)) ]; then
		OUTCARD="jack"
	elif [ "$RESULT" -eq $((i+2)) ]; then
		OUTCARD="NAS"
	else
		OUTCARD="alsa:${DEVICE[RESULT]}"
	fi

	if ! grep -qE "^OUTCARD=" "$SOMAOPTIONS"; then
		echo -e "# Output Card\nOUTCARD=\"$OUTCARD\"" >> "$SOMAOPTIONS"
	else
		sed -i "s/^OUTCARD=.*/OUTCARD=\"$OUTCARD\"/" "$SOMAOPTIONS"
	fi

	[ "$OUTCARD" = "$tmp" ] && return
	[ -z "$TITLE" ] && [ "$TITLE" != "Idle" ] && return
	play
	setup
	status
}
# }}}
# {{{ Output - Select Mixer Card
selectmixer() {
	rows=$(stty size | cut -f1 -d' ')
	cols=$(stty size | cut -f2 -d' ')

	unset MIXLIST
	NUMMIXER=$((0))
	tmp=$((MIXER))
	status="off"

	aplay -l | grep card | cut -d, -f1 | sort -u > "$MIXERS"

	while read -r card
	do
		[ $tmp -eq $NUMMIXER ] && status="on" || status="off"

		MIXLIST+="$NUMMIXER \"$card\" $status "
		NUMMIXER+=1
	done < "$MIXERS"

	sh -c "/usr/bin/dialog $DIALOGOPTS --radiolist \"${TITCOL}Output Panel\" \
		$rows $cols $rows $MIXLIST 2> $TMP" || return

	MIXER=$(< "$TMP")

	if ! grep -qE "^MIXER=" "$SOMAOPTIONS"; then
		echo -e "\n# Mixer Device\nMIXER=$MIXER" >> "$SOMAOPTIONS"
	fi

	sed -i "s/MIXER=$tmp/MIXER=$MIXER/" "$SOMAOPTIONS"

	[ "$MIXER" -eq $tmp ] && return
}

# }}}
# {{{ Output - Mixer
mixerpanel() {
	alsamixer -c "$MIXER"
}
# }}}
# {{{ Dialog and Cursor Routines
# {{{ Get Station
get_station()
{
	rows=$(stty size | cut -f1 -d' ')
	cols=$(stty size | cut -f2 -d' ')

	get_station_list $__ALL

	TMPLIST=$(echo "$STATIONLIST" | tr '\n' ' ')

	# sh -c properly sends the quoted strings to dialog
	sh -c "dialog $DIALOGOPTS --menu \"${TITCOL}Stations\" $rows $cols $rows \
		$TMPLIST" 2> "$TMP" || return

	returned_station="$(< "$TMP")"
}
# }}}
# {{{ An inputbox for strings. $1 = title, $2 = description text, $3 = option name
general_inputbox()
{
	dialog --cancel-label "Back" $DIALOGOPTS --title "${TITCOL}$1" \
		--inputbox "$2" "$rows" "$cols" -- "${!3}" 2> "$TMP" || return

	[ -z "$(< "$TMP")" ] && return
	# Escape slashes
	VALUE="\"$(sed 's%\\%\\\\%g' "$TMP")\""
	sed -i "s#^$3=.*#$3=$VALUE#" "$SOMAOPTIONS"
}
# }}}
# {{{ An inputbox for numbers. $1 = title, $2 = description text, $3 = option name
general_num_inputbox()
{
	dialog --cancel-label "Back" $DIALOGOPTS --title "${TITCOL}$1" \
		--inputbox "$2" "$rows" "$cols" -- "${!3}" 2> "$TMP" || return

	[ -z "$(< "$TMP")" ] && return
	sed -i "s#^$3=.*#$3=$(< "$TMP")#" "$SOMAOPTIONS"
}
# }}}
# {{{ A checkbox. $1 = title, $2 = description text, $3 = option name
general_checkbox()
{
	# Tried --no-items but that doesn't seem to work in 14.2's dialog
	dialog --cancel-label "Back" --no-tags $DIALOGOPTS --title "${TITCOL}$1" \
		--checklist "$2" "$rows" "$cols" 1 "$3" "$3" "${boxval[$3]}" 2> "$TMP" || return

	[ -n "$(< "$TMP")" ] && val=1 || val=0
	sed -i "s#^$3=.*#$3=$val#" "$SOMAOPTIONS"
}
# }}}
# {{{ A Simple Msgbox
do_msg()
{
	mrows=$(($(echo -e "$2" | wc -l)+4))
	mcols=$(($(echo -e "$2" | wc -L)+4))
	OLDIFS="$IFS"
	IFS="$DEFIFS"
	[ -n "$3" ] && t=$3 || t=0
	dialog $DIALOGOPTS --title "${TITCOL}$1" --timeout $t --msgbox "$2" "$mrows" "$mcols" 2>/dev/null
	IFS="$OLDIFS"
}
# }}}
# {{{ Cursor on/off
cursor()
{
	# Need to use xterm-color to make dialog colours correct
	# but cursor won't turn off with xterm-color
	TERM=xterm-256color
	setterm -cursor "$1"
	TERM=xterm-color
}
# }}}
# }}}
# {{{ General Sort Routine
# Slightly ripped off from sbopkg but with modifications
# Sort entries in $INFILE, output to $TMP
general_sort()
{
	INFILE=$SOMAROOT/sort_infile

	if [ -z "$(< "$INFILE")" ]; then
		do_msg "Sort" "File is empty!" 1
		return 1
	fi

	BMCOUNT=$(wc -l "$INFILE" | awk '{print $1}')
	[ "$BMCOUNT" -lt 2 ] && do_msg "Sort" "Only one entry to sort!" 1 && return 1

	sed -i '#^$#d' "$INFILE"

	rows=$(stty size | cut -f1 -d' ')
	cols=$(stty size | cut -f2 -d' ')

	get_station_list "$1"
	TMPENTRIES="$SOMAROOT"/tmp-infile
	TMPNUMENTRIES="$SOMAROOT"/tmp-numbered-infile
	echo "$STATIONLIST" | cut -d'"' -f1-2 | sed 's#$#"#g' > "$TMPENTRIES"
	TMPLIST=$(nl "$TMPENTRIES" | sed 's#^\s*##g;s#$##g')
	echo -n $TMPLIST > "$TMPNUMENTRIES"

	local PARTIALSORT="$SOMAROOT"/soma_sort_tempfile
	local TMPSORTQUEUE="$SOMAROOT"/soma_tmp_sort_queue
	local CHOICE
	local SELECTED
	local DEFAULTITEM
	cp "$TMPENTRIES" "$TMPSORTQUEUE"

	while :
	do
		sh -c "dialog --title \"Sort Stations\" --ok-label Up \
				--extra-button --extra-label Down --cancel-label Done \
				--default-item \"$DEFAULTITEM\" \
				--menu \"Use the <Up/Down> buttons to sort the entries. \
				Press <OK> when done, or press <ESC> to abort \
				changes.\" $rows $cols $rows \
				$(cat $TMPNUMENTRIES)" 2> "$TMP"

		CHOICE="$?"
		SELECTED=$(< "$TMP")
		DEFAULTITEM="$SELECTED"

		case "$CHOICE" in
			0 ) # Up
				if [ "$SELECTED" -eq 1 ]; then
					continue
				fi
				head -n $((SELECTED-2)) "$TMPSORTQUEUE" > "$PARTIALSORT"
				head -n $((SELECTED)) "$TMPSORTQUEUE" |
					tail -n 1 >> "$PARTIALSORT"
				head -n $((SELECTED-1)) "$TMPSORTQUEUE" |
					tail -n 1 >> "$PARTIALSORT"
				tail -n $((BMCOUNT-SELECTED)) "$TMPSORTQUEUE" >> \
					"$PARTIALSORT"
				mv "$PARTIALSORT" "$TMPSORTQUEUE"
				TMPLIST=$(nl "$TMPSORTQUEUE" | sed 's#^\s*##g;s#$##g')
				echo -n $TMPLIST > "$TMPNUMENTRIES"
				DEFAULTITEM=$((SELECTED-1))
				continue
				;;
			1 ) # OK
				mv "$TMPSORTQUEUE" "$TMPENTRIES"
				break
				;;
			3 ) # Down
				if [ "$SELECTED" -eq "$BMCOUNT" ]; then
					continue
				fi
				head -n $((SELECTED-1)) "$TMPSORTQUEUE" > "$PARTIALSORT"
				head -n $((SELECTED+1)) "$TMPSORTQUEUE" |
					tail -n 1 >> "$PARTIALSORT"
				head -n $((SELECTED)) "$TMPSORTQUEUE" |
					tail -n 1 >> "$PARTIALSORT"
				tail -n $((BMCOUNT-SELECTED-1)) "$TMPSORTQUEUE" >> \
					"$PARTIALSORT"
				mv "$PARTIALSORT" "$TMPSORTQUEUE"
				TMPLIST=$(nl "$TMPSORTQUEUE" | sed 's#^\s*##g;s#$##g')
				echo -n $TMPLIST > "$TMPNUMENTRIES"
				DEFAULTITEM=$((SELECTED+1))
				continue
				;;
			* ) # Cancel or ESC
				rm -f "$TMPSORTQUEUE"
				break
				;;
		esac
	done

	echo -n > "$TMP"

	while read -r station
	do
		URL=$(echo "$STATIONLIST" | grep -E "$station" | cut -d'"' -f4)
		echo "$station,$URL" | sed 's#"##g' >> "$TMP"
	done < "$TMPENTRIES"

	rm -f "$TMPENTRIES"
	rm -f "$TMPNUMENTRIES"
	rm -f "$PARTIALSORT"
	rm -f "$TMPSORTQUEUE"
}
# }}}
# {{{ General Add Station
general_add_station()
{
	cursor on
	dialog $DIALOGOPTS --title "${TITCOL}Add Station" \
		--inputbox "Station name" 10 60 2> "$TMP" || { cursor off; return 1; }

	name="$(< "$TMP")"
	[ -z "$name" ] && cursor off && return 1

	if grep -qE "^$name," "$1"; then
		do_msg "Add Station" "\"$name\" already in list!" 1
		cursor off
		return
	fi

	dialog $DIALOGOPTS --title "${TITCOL}Add Station" \
		--inputbox "Station URL" 10 60 2> "$TMP" || { cursor off; return 1;}

	URL=$(< "$TMP")
	cursor off
	[ -z "$URL" ] && return 1

	echo "$name,$URL" >> "$1"
	do_msg "Add Station" "\"$name\" added." 1
	if ! grep -qE "^$name," "$USRSTATIONS"; then
		echo "$name,$URL" >> "$USRSTATIONS"
	fi
}
#}}}
# {{{ General Delete Routine
general_delete()
{
	[ "$1" -eq "$__GENRE" ] && category[$__GENRE]="Stations in $GENRE"

	if [ -z "$(< "$TMPLIST")" ]; then
		do_msg "${category[$1]}" "File is empty." 1
		return
	fi

	while :
	do
		[ -z "$(< "$TMPLIST")" ] && return
		get_station_list "$1"
		echo "$STATIONLIST" | cut -d'"' -f2 > "$TMP"
		nice_list "Delete ${category[$1]}" "Delete" || return

		CHAN="$(sed 's# *$##' "$TMP")"
		sed -i "/^$CHAN,/d" "$TMPLIST"
	done

}
# }}}
# {{{ General Edit FIle
general_edit_file()
{
	[ -z "$EDITOR" ] && EDITOR=/usr/bin/vim
	cursor on
	TERM=xterm-256color
	$EDITOR "$1"
	cursor off
}
# }}}
# {{{ Scheduler - Main Menu
schedule_main()
{
	touch "$SCHEDULES"

	while :
	do
		rows=$(stty size | cut -f1 -d' ')
		cols=$(stty size | cut -f2 -d' ')

		dialog --cancel-label Back $DIALOGOPTS --menu "${TITCOL}Scheduler" \
			"$rows" "$cols" "$rows" \
			View "View Schedule" \
			Add "Add Schedule" \
			Remove "Remove a Schedule" \
			2> "$TMP"

		[ "$?" -eq 1 ] && break

		choice="$(< "$TMP")"
		case "$choice" in
		View)
			schedule_view
		;;
		Add)
			schedule_add
		;;
		Remove)
			schedule_remove
		;;
		*)
		;;
		esac
	done
}
# }}}
# {{{ Scheduler - View Sched
schedule_view()
{
	rm -f "$TMP"

	while read -r line
	do
		# Make it ape-readable
		date=$(echo "$line" | cut -d, -f1)
		year=${date:0:4}
		month=${date:4:2}
		day=${date:6:2}
		time=$(echo "$line" | cut -d, -f2)
		channel=$(echo "$line" | cut -d, -f3)

		echo "\Zn$day/$month/$year $time ${TITCOL}$channel" >> "$TMP"
	done < "$SCHEDULES"

	[ ! -r "$TMP" ] && return
	rows=$(($(wc -l "$TMP" | awk '{print $1}')+5))

	dialog $DIALOGOPTS --title "${TITCOL}Set Schedules" \
		--msgbox "$(< "$TMP")" "$rows" 50
}
# }}}
# {{{ Scheduler - Add Sched
schedule_add()
{
	cursor on
	get_schedule_time
	cursor off

	[ -z "$sched_time" ] && return

	get_schedule_recur

	[ "$sched_recur" = "0" ] && return

	if [ "$sched_recur" = "O" ]; then
		get_schedule_date
		[ -z "$sched_date" ] && return
	elif [ "$sched_recur" = "W" ]; then
		get_schedule_days
		[ -z "$sched_days" ] && return
	else
		sched_date="********"
		day="**"
		month="**"
		year="****"
	fi

	get_station

	[ -z "$returned_station" ] && return
	[ "$SCHED_ENABLED" -eq 1 ] && DASH="[S]" || DASH="[-]"

	if [ "$sched_recur" = "W" ]; then
		for i in $sched_days
		do
			j=${i:0:2}
			sched_date="******$j"
			month="**"
			year="****"
			sed -i "/[*,$sched_date],$sched_time/d" "$SCHEDULES"
		done
	else
		sed -i "/[*,$sched_date],$sched_time/d" "$SCHEDULES"
	fi

	if [ "$day" = "**" ]; then
		sed -i "/$sched_time/d" "$SCHEDULES"
	fi

	if [ "$sched_recur" = "W" ]; then
		for i in $sched_days
		do
			j=${i:0:2}
			sched_date="******$j"
			month="**"
			year="****"
			day=".."
	echo "$sched_date,$sched_time,$returned_station" >> "$SCHEDULES"
		done
	else
		echo "$sched_date,$sched_time,$returned_station" >> "$SCHEDULES"
	fi

	sched_msg="${TITCOL}Schedule set:\Zn\n\n$day/$month/$year (DD/MM/YYYY)"
	sched_msg=$sched_msg"\n$sched_time\n$returned_station\n"

	if [ "$sched_recur" = "D" ]; then
		sched_msg=$sched_msg"Daily"
	elif [ "$sched_recur" = "W" ]; then
		sched_msg="${sched_msg}Every $sched_days"
	else
		sched_msg=$sched_msg"Once"
	fi

	dialog $DIALOGOPTS --title "Schedule" --msgbox "$sched_msg" 11 40
}
# }}}
# {{{ Scheduler - Remove Sched
schedule_remove()
{
	while :
	do
		unset sched
		while read -r line
		do
			# More ape-like readable stuff
			date=$(echo "$line" | cut -d, -f1)
			year=${date:0:4}
			month=${date:4:2}
			day=${date:6:2}
			time=$(echo "$line" | cut -d, -f2)
			channel=$(echo "$line" | cut -d, -f3)
			sched="$sched \"$day\\$month\\$year $time\" \"$channel\""
		done < "$SCHEDULES"

		[ -z "$sched" ] && unset DASH && return

		rows=$(($(wc -l "$SCHEDULES" | awk '{print $1}')+7))

		sh -c "dialog $DIALOGOPTS --ok-label Delete --cancel-label Done \
			--title \"${TITCOL}Schedules\" --menu \"${TITCOL}Remove a Schedule\" \
			$rows 50 $rows $sched" 2> "$TMP" || return

		# Now make it machine readable (ish)
		torem="$(sed 's# #,#' "$TMP")"
		year=${torem:6:4}
		month=${torem:3:2}
		day=${torem:0:2}
		time=${torem:11:5}

		[ "$year" = "****" ] && year="\*\*\*\*"
		[ "$month" = "**" ] && month="\*\*"
		[ "$day" = "**" ] && day="\*\*"

		sed -i -E "/^$year$month$day,$time/d" "$SCHEDULES"
	done
}
# }}}
# {{{ Scheduler - Sched Station Changer
schedule_change_station()
{
	STATIONURL=$(grep "$SCHEDCHAN" "$STATIONS" | head -n 1 | cut -d, -f2)
	SCHEDRECUR=$(grep "\*,$TIME24" "$SCHEDULES")

	[ "$SCHEDCHAN" = "$OLDCHANNEL" ] && [ "$CHANNEL" != "Idle" ] && return
	
	[ -z "$SCHEDRECUR" ] && sed -i "/$DATE,$TIME24/d" "$SCHEDULES"
	sed -i "/^$DATE,$TIME24/d" "$SCHEDULES"

	CHANNEL="$SCHEDCHAN"
	play
	setup
	laststation="$STATIONURL"
}
# }}}
#{{{ Scheduler - Sched Time
get_schedule_time()
{
	unset sched_time

	while [ ${#sched_time} -ne 5 ]
	do
		dialog $DIALOGOPTS --title "${TITCOL}Schedule Time" \
			--inputbox "Type in a time in 24 hour format (HH:MM)" 10 25 \
			2> "$TMP" || return

		sched_time=$(< "$TMP")
	done
}
# }}}
# {{{ Scheduler - Sched Recurring?
get_schedule_recur()
{
	dialog $DIALOGOPTS --menu "${TITCOL}Recurrance" \
		10 30 3 \
			"Once" "Once Only" \
			"Daily" "Daily Schedule" \
			"Weekly" "Weekly Schedule" \
			2> "$TMP" || { sched_recur=0; return; }

	if [ "$(< "$TMP")" = "Once" ]; then
		sched_recur="O"
	elif [ "$(< "$TMP")" = "Daily" ]; then
		sched_recur="D"
	elif [ "$(< "$TMP")" = "Weekly" ]; then
		sched_recur="W"
	fi
}
# }}}
# {{{ Scheduler - Sched Days
get_schedule_days()
{
	dialog $DIALOGOPTS --separate-output --checklist "${TITCOL}Select A Day" \
		14 25 8 \
			Monday " " off \
			Tuesday " " off \
			Wednesday " " off \
			Thursday " " off \
			Friday " " off \
			Saturday " " off \
			Sunday " " off \
			2> "$TMP" || { sched_days=""; return; }

	sched_days=$(< "$TMP")
}
# }}}
# {{{ Scheduler - Sched Date
get_schedule_date()
{
	unset sched_date

	dialog $DIALOGOPTS --title "${TITCOL}Schedule Date" \
		--calendar "\nUse tab to traverse windows\n" 5 50 2> "$TMP"

	date=$(< "$TMP")

	[ -z "$date" ] && return

	year=$(echo "$date" | cut -d/ -f3)
	month=$(echo "$date" | cut -d/ -f2)
	day=$(echo "$date" | cut -d/ -f1)
	sched_date="$year$month$day"
}
# }}}
# {{{ Edit OPTIONS - Main Menu
edit_options()
{
	local DEFAULTOPT
	DEFAULTOPT="DIALOGOPTS"

	while :
	do
		rows=$(stty size | cut -f1 -d' ')
		cols=$(stty size | cut -f2 -d' ')

		dialog $DIALOGOPTS --cancel-label "Back" \
			--default-item "$DEFAULTOPT" \
			--menu "${TITCOL}Manually Edit Settings" \
			"$rows" "$cols" "$rows" \
			"DIALOGOPTS" "Dialog Options" \
			"OUTCARD" "Output Device" \
			"SOFTVOL" "MPlayer Software Volume" \
			"CACHE" "MPlayer Cache" \
			"MIXER" "Mixer Device" \
			"TITCOL" "Dialog Title Colour" \
			"CONNECTTIMEOUT" "Connection Timeout" \
			"TIMEFORMAT" "Time Format" \
			"CARET" "Caret Character" \
			"SAVE_LIST_AT_QUIT" "Save List" \
			"SETTERMTITLE" "Whether To Set The Term/Screen Title" \
			"AUTORECONNECT" "Reconnect to Lost Streams" \
			"SHOWAUDIOINFO" "Show Stream Audio and Codec Info" \
			"SHOWVOLUME" "Show Current Volume in Title Bar" \
			2> "$TMP" || break

		option=$(< "$TMP")
		DEFAULTOPT=$option

		cursor on
		[ "$option" = "DIALOGOPTS" ] && dialogopts
		[ "$option" = "OUTCARD" ] && outcard
		[ "$option" = "AFFILTER" ] && affilter
		[ "$option" = "SOFTVOL" ] && softvol
		[ "$option" = "CACHE" ] && cache
		[ "$option" = "SAVE_LIST_AT_QUIT" ] && save_list_at_quit
		[ "$option" = "MIXER" ] && edit_mixer
		[ "$option" = "TITCOL" ] && titcol
		[ "$option" = "CONNECTTIMEOUT" ] && timeout
		[ "$option" = "TIMEFORMAT" ] && timeformat
		[ "$option" = "CARET" ] && edit_caret
		[ "$option" = "SETTERMTITLE" ] && set_termtitle
		[ "$option" = "AUTORECONNECT" ] && set_autoreconnect
		[ "$option" = "SHOWAUDIOINFO" ] && set_showaudioinfo
		[ "$option" = "SHOWVOLUME" ] && set_showvolume

		. "$SOMAOPTIONS"
		[ "$SETTERMTITLE" -eq 1 ] && echo -en "\033ksoma\033\\"
		cursor off
	done
}
# }}}
# {{{ Edit DIALOGOPTS
dialogopts()
{
	general_inputbox "Dialog Options" "Edit dialog options.\n\nNote that removing '--colors' only affects the title bar in the dialog command. It doesn't affect the theme colours. Use \"--no-mouse\" to disable the mouse.\n\nDefault: \"--colors\"\n " "DIALOGOPTS"
}
# }}}
# {{{ Edit OUTCARD
outcard()
{
	tmp=$OUTCARD

	general_inputbox "Output Card" "Manually edit your output device.\n " "OUTCARD"

	OUTCARD=$(grep -E "^OUTCARD=" "$SOMAOPTIONS" | cut -d'"' -f2)
	[ "$OUTCARD" = "$tmp" ] && return
	[ -z "$TITLE" ] && [ "$TITLE" != "Idle" ] && return
	cursor off
	play
	setup
	status
}
# }}}
# {{{ Edit AFFILTER
affilter()
{
	dialog --cancel-label Back $DIALOGOPTS --title "${TITCOL}MPlayer Affilter" \
		--inputbox "Edit MPlayer affilter for BBC Radio 3, which is a much lower volume than other stations.\n\nDefault: \"-softvol -af volume=8\"\n " \
		"$rows" "$cols" -- "$(grep -E "^AFFILTER=" "$SOMAOPTIONS" | cut -d= -f2- | sed 's/"//g')" \
		2> "$TMP"

	retval=$?

	[ -z "$(< "$TMP")" ] && [ "$retval" -ne 0 ] && return
	sed -i "s#^AFFILTER=.*#AFFILTER=\"$(< "$TMP")\"#" "$SOMAOPTIONS"
}
# }}}
# {{{ Edit SOFTVOL
softvol()
{
	general_inputbox "MPlayer Softvol" "Use MPlayer software mixer, or card hardware mixer.\n\nDefault: \"-softvol\"\n " "SOFTVOL"
}
# }}}
# {{{ Edit CACHE
cache()
{
	general_num_inputbox "MPlayer Cache" "Set MPlayer cache size.\n\nDefault: 320\n " "CACHE"
}
# }}}
# {{{ Edit CONNECTTIMEOUT
timeout()
{
	general_num_inputbox "Connection Timeout" "Set connection timeout value.\n\nDefault: 150\n\n " "CONNECTTIMEOUT"
}
# }}}
# {{{ Edit MIXER
edit_mixer()
{
	general_inputbox "Mixer" "Set mixer device.\n\n " "MIXER"
}
# }}}
# {{{ Edit TITCOL
titcol()
{
	general_inputbox "Dialog Title Settings" "This overrides the title colours in the theme.\n
By default it is set to \"\\\ZZZb\" (bold dialog foreground colour).\n
You can assign colours here too using \"\\\ZZZn\" where 'n' is: 0: black, 1: red, 2: green, 3: yellow, 4: blue, 5: magenta, 6: cyan, 7: white, 8: default dialog foreground.\n
Example: \"\\\ZZZb\\\ZZZ1\" for bold red.\n\nDefault: \"\\\ZZZb\"\n " "TITCOL"
}
# }}}
# {{{ Edit TIMEFORMAT
timeformat()
{
	general_inputbox "Time Format" "Set time format. See man date for full options.\n\nDefault: \"%I:%M\"\n\n " "TIMEFORMAT"
}
# }}}
# {{{ Edit CARET
edit_caret()
{
	general_inputbox "Caret" "Set the caret used in menus.\n\nDefault: \"——>\"\n\n " "CARET"
}
# }}}
# {{{ Edit SAVE_LIST_AT_QUIT
save_list_at_quit()
{
	general_checkbox "Save Playlist at Quit" "Whether to save the playlist when soma exits.\n\nDefault: off\n " "SAVE_LIST_AT_QUIT"
}
# }}}
# {{{ Edit SETTERMTITLE
set_termtitle()
{
	general_checkbox "Term Title" "Whether to set the terminal/screen title to \"Soma\".\n\nDefault: off\n " "SETTERMTITLE"
}
# }}}
# {{{ Edit AUTORECONNECT
set_autoreconnect()
{
	general_checkbox "Autoreconnect" "Whether to autoreconnect to a lost stream. Normally, when you lose connection to a stream, you will get a dialog box popup which gives you the choice to reconnect or go to the main menu. If this setting is checked then soma will reconnect automatically and skip the popup.\n\n
Default: off\n " "AUTORECONNECT"
}
# }}}
# {{{ Edit SHOWAUDIOINFO
set_showaudioinfo()
{
	general_checkbox "Show Audio Info" "Whether to show the stream audio and codec info.\n\n
Default: on\n " "SHOWAUDIOINFO"
}
# }}}
# {{{ Edit SHOWVOLUME
set_showvolume()
{
	general_checkbox "Show Volume in Title Bar" "Whether to show the volume in square brackets in the title: [N]. Note: only works with volume keys 1-0.\n\n
Default: on\n " "SHOWVOLUME"
}
# }}}
# {{{ Genre - Main menu
main_genre_menu()
{
	local GENREOPT
	GENREOPT="Genres"
	while :
	do
		rows=$(stty size | cut -f1 -d' ')
		cols=$(stty size | cut -f2 -d' ')

		dialog $DIALOGOPTS --title "${TITCOL}Genre Options ($GENRE)" --cancel-label "Back" \
			--default-item "$GENREOPT" --menu "" "$rows" "$cols" "$rows" \
			"Genres" "List Genres" \
			"Local Genres" "List Local Genres" \
			"List Stations" "List Stations in Current Genre" \
			"Add/Remove/Sort" "Add/Remove/Sort Stations in Current Genre" \
			"New" "Create New Genre" \
			"Remove Genre" "Remove a (Local) Genre" \
			"Edit Genre" "Edit Local Genre" \
			2> "$TMP" || break

		option=$(< "$TMP")
		GENREOPT=$option

		[ "$option" = "Genres" ] && genre_selector
		[ "$option" = "Local Genres" ] && local_genre_selector
		[ "$option" = "List Stations" ] && genre_station_selector
		[ "$option" = "Add/Remove/Sort" ] && add_stations_to_genre
		[ "$option" = "New" ] && new_genre
		[ "$option" = "Remove Genre" ] && remove_genre
		[ "$option" = "Edit Genre" ] && edit_genre
	done
}
# }}}
# {{{ Genre - Genre Selector
genre_selector()
{
	[ -z "$GENRE" ] && GENRE="All"
	OLDGENRE=$GENRE
	echo -n > "$TMP"

	find {"$USRGENREDIR","$SYSGENREDIR"} -name \*\.conf \
		| rev | cut -d/ -f1 | rev | sed 's#\.conf##' | sort -u >> "$TMP"

	nice_list "Genres" "Select" || return
	GENRE="$(sed 's# *$##' "$TMP")"

	sed -i "s#GENRE=.*#GENRE=\"$GENRE\"#" "$SOMAOPTIONS"

	if [ "$GENRE" != "$OLDGENRE" ]; then
		if [ -r "$USRGENREDIR/$GENRE.conf" ]; then
			if [ -n "$(< "$USRGENREDIR/$GENRE.conf")" ]; then
				station_selector $__GENRE
			fi
		elif [ -r "$SYSGENREDIR/$GENRE.conf" ]; then
			if [ -n "$(< "$SYSGENREDIR/$GENRE.conf")" ]; then
				station_selector $__GENRE
			fi
		fi
	fi
}
# }}}
# {{{ Genre - Local Genre Selector
local_genre_selector()
{
	OLDGENRE=$GENRE

	while :
	do
		find "$USRGENREDIR" -name \*\.conf \
			| rev | cut -d/ -f1 | rev | sed 's#\.conf##' | sort > "$TMP"

		[ -z "$(< "$TMP")" ] && do_msg "List Genres" "No local genres to list." 1 && return

		nice_list "Genres" "Select" "Delete"
		retval=$?
		[ $retval -eq 1 ] || [ $retval -eq 255 ] && return

		if [ $retval -eq 2 ]; then
			GEN="$(< "$TMP")"
			GEN=${GEN%% *}
			GEN=${GEN#HELP }
			cols=$((${#GEN}+21))
			dialog --title "Delete Genre" --yesno "Really delete \"$GEN\"?" \
				5 "$cols" 2>/dev/null && rm -f "$USRGENREDIR/$GEN.conf"
			[ -z "$(find "$USRGENREDIR" -name \*\.conf)" ] && return
		else
			break
		fi
	done

	GENRE="$(sed 's# *$##' "$TMP")"

	sed -i "s#GENRE=.*#GENRE=\"$GENRE\"#" "$SOMAOPTIONS"

	if [ "$GENRE" != "$OLDGENRE" ]; then
		station_selector $__GENRE
	fi
}
# }}}
# {{{ Genre - Station Selector
genre_station_selector()
{
	WITHURL=1
	station_selector $__GENRE
	unset WITHURL
}
# }}}
# {{{ Genre - Add/Remove Stations
add_stations_to_genre()
{
	unset option
	while :
	do
		rows=$(stty size | cut -f1 -d' ')
		cols=$(stty size | cut -f2 -d' ')

		dialog --title "${TITCOL}Add/Remove Stations ($GENRE)" \
			--cancel-label "Back" $DIALOGOPTS --menu "" "$rows" "$cols" "$rows" \
			"Add" "Add From Station List" \
			"New" "Add New Radio Station Manually" \
			"Sort" "Sort Stations" \
			"Remove" "Remove Station From Genre" \
			2> "$TMP" || break

		option=$(< "$TMP")

		[ "$option" = "Add" ] && add_station_from_list
		[ "$option" = "New" ] && man_add_station
		[ "$option" = "Sort" ] && sort_stations
		[ "$option" = "Remove" ] && remove_stations
	done
}
# }}}
# {{{ Genre - Add Station From List
add_station_from_list()
{
	unset option
	get_station_list $__ALL
	while :
	do
		SYSTMP="$SYSGENREDIR/$GENRE.conf"
		USRTMP="$USRGENREDIR/$GENRE.conf"

		if [ ! -r "$USRTMP" ] && [ -r "$SYSTMP" ]; then
			cat "$SYSTMP" > "$USRTMP"
		fi

		echo "$STATIONLIST" | sed 's# \\##g' | cut -d'"' -f2 > "$TMP"
		nice_list "Stations (All)" "Add" || return

		CHAN="$(sed 's# *$##' "$TMP")"
		STAT=$(grep -E "^$CHAN," "$STATIONS" | head -n 1)

		IFS=$EXTIFS

		if [ -r "$USRTMP" ]; then
			if ! grep -E "^$STAT" "$USRTMP"; then
				echo "$STAT" >> "$USRTMP"
				do_msg "Add New" "$CHAN added." 1
			else
				do_msg "Add New" "$CHAN already in local \"$GENRE\"" 1
			fi
		elif [ -r "$SYSTMP" ]; then
			if ! grep -E "^$STAT" "$SYSTMP"; then
				cat "$SYSTMP" > "$USRTMP"
				echo "$STAT" >> "$USRTMP"
				do_msg "Add New" "$CHAN added." 1
			else
				do_msg "Add New" "$CHAN already in global \"$GENRE\"." 1
			fi
		else
			echo "$STAT" >> "$USRTMP"
			do_msg "Add New" "$CHAN added." 1
		fi

		# If identical to global genre then ditch it
		if [ "$(md5sum "$USRTMP" | awk '{print $1}')" = \
			"$(md5sum "$SYSTMP" | awk '{print $1}')" ]; then
			rm -f "$USRTMP"
		fi

		IFS=$DEFIFS
	done
}
# }}}
# {{{ Genre - Add Current Station
add_current_station_to_genre()
{
	[ "$CHANNEL" = "Idle" ] || [ -z "$CHANNEL" ] && return
	SYSTMP=$SYSGENREDIR/"$GENRE.conf"
	USRTMP=$USRGENREDIR/"$GENRE.conf"

	IFS=$EXTIFS
	STAT=$(grep -E "^$CHANNEL," "$STATIONS")

	if [ -r "$USRTMP" ]; then
		if ! grep -E "^$STAT" "$USRTMP"; then
			echo "$STAT" >> "$USRTMP"
			do_msg "Add New" "\"$CHANNEL\" added to \"$GENRE\"." 1
		else
			do_msg "Add New" "\"$CHANNEL\" already in \"$GENRE\"" 1
		fi
	elif [ -r "$SYSTMP" ]; then
		cat "$SYSTMP" > "$USRTMP"
		if ! grep -E "^$STAT" "$USRTMP"; then
			echo "$STAT" >> "$USRTMP"
			do_msg "Add New" "\"$CHANNEL\" added to \"$GENRE\"." 1
		else
			do_msg "Add New" "\"$CHANNEL\" already in \"$GENRE\"." 1
		fi
	else
		echo "$STAT" >> "$USRTMP"
		do_msg "Add New" "\"$CHANNEL\" added to \"$GENRE\"." 1
	fi

	if [ "$(md5sum "$USRTMP" | awk '{print $1}')" = \
		"$(md5sum "$SYSTMP" | awk '{print $1}')" ]; then
		rm -f "$USRTMP"
	fi

	IFS=$DEFIFS
}
# }}}
# {{{ Genre - Manually Add Station
man_add_station()
{
	unset option
	SYSTMP=$SYSGENREDIR/"$GENRE.conf"
	USRTMP=$USRGENREDIR/"$GENRE.conf"

	while :
	do
		cursor on
		dialog $DIALOGOPTS --title "${TITCOL}Add New Station" \
			--inputbox "Name for this station" 10 60 2> "$TMP" \
			|| { cursor off; return; }

		name=$(< "$TMP")
		[ -z "$name" ] && cursor off && return

		dialog $DIALOGOPTS --title "${TITCOL}Add Bookmark" \
			--inputbox "URL" 10 60 2> "$TMP" || { cursor off; return; }

		URL=$(< "$TMP")
		[ -z "$URL" ] && cursor off && return

		cursor off
		IFS=$EXTIFS

		if [ -r "$USRTMP" ]; then
			if ! grep -E "^$name," "$USRTMP"; then
				echo "$name,$URL" >> "$USRTMP"
				do_msg "Add New" "Station Added." 1
				if ! grep -E "^$name,$URL$" "$USRSTATIONS"; then
					echo "$name,$URL" >> "$USRSTATIONS"
				fi
			else
				do_msg "Add New" "Station already in local \"$GENRE\"." 1
			fi
		elif [ -r "$SYSTMP" ]; then
			if ! grep -E "^$name," "$SYSTMP"; then
				cat "$SYSTMP" > "$USRTMP"
				echo "$name,$URL" >> "$USRTMP"
				do_msg "Add New" "Station Added." 1
				if ! grep -E "^$name,$URL$" "$USRSTATIONS"; then
					echo "$name,$URL" >> "$USRSTATIONS"
				fi
			else
				do_msg "Add New" "Station already in global \"$GENRE\"." 1
			fi
		fi

		IFS=$DEFIFS
	done
}
#}}}
# {{{ Genre - Sort Stations
sort_stations()
{
		SYSTMP="$SYSGENREDIR/$GENRE.conf"
		USRTMP="$USRGENREDIR/$GENRE.conf"

		if [ ! -r "$USRTMP" ] && [ -r "$SYSTMP" ]; then
				cat "$SYSTMP" > "$USRTMP"
		fi

	cat "$USRTMP" > "$SOMAROOT"/sort_infile
	general_sort $__GENRE && cat "$TMP" > "$USRGENREDIR/$GENRE.conf"

	# If identical to global genre then ditch it
	if [ "$(md5sum "$USRTMP" | awk '{print $1}')" = \
		"$(md5sum "$SYSTMP" | awk '{print $1}')" ]; then
		rm -f "$USRTMP"
	fi
}
# }}}
# {{{ Genre - Remove Single Station
remove_single_station()
{
	if [ "$2" -eq $__BOOKMARKS ]; then
		USRTMP=$BOOKMARKS
	elif [ "$2" -eq $__GENRE ]; then
		SYSTMP=$SYSGENREDIR/"$GENRE.conf"
		USRTMP=$USRGENREDIR/"$GENRE.conf"

		if [ ! -r "$USRTMP" ]; then
			if [ -r "$SYSTMP" ]; then
				cat "$SYSTMP" > "$USRTMP"
			else
				return 1
			fi
		fi
	elif [ "$2" -eq $__LOCAL ]; then
		USRTMP=$USRSTATIONS
	fi

	sed -i "/^$1,/d" "$USRTMP"

	IFS=$EXTIFS

	if [ "$2" -eq $__GENRE ]; then
		if [ "$(wc -l "$USRTMP" | awk '{print $1}')" -eq 0 ]; then
			rm -f "$USRTMP"
			IFS=$DEFIFS
			return 1
		fi
		if [ "$(md5sum "$USRTMP" | awk '{print $1}')" = \
			"$(md5sum "$SYSTMP" | awk '{print $1}')" ]; then
			rm -f "$USRTMP"
			IFS=$DEFIFS
			return 1
		fi
	fi

	IFS=$DEFIFS
}
# }}}
# {{{ Genre - Remove Multiple Stations
remove_stations()
{
	unset option
	SYSTMP=$SYSGENREDIR/"$GENRE.conf"
	USRTMP=$USRGENREDIR/"$GENRE.conf"

	if [ ! -r "$USRTMP" ]; then
		if [ -r "$SYSTMP" ]; then
			cat "$SYSTMP" > "$USRTMP"
		else
			do_msg "Delete Station" "$GENRE is empty." 1
			return
		fi
	fi

	TMPLIST="$USRTMP"
	general_delete $__GENRE
}
#}}}
# {{{ Genre - New Genre
new_genre()
{
	unset option
	cursor on
	dialog $DIALOGOPTS --title "${TITCOL}New Genre" \
		--inputbox "Name for new genre" 10 60 2> "$TMP" || { cursor off; return; }

	cursor off
	name="$(< "$TMP")"

	if [ -r "$USRGENREDIR/$name.conf" ]; then
		do_msg "Error" "Local \"$name.conf\" already exists." 1
	elif [ -r "$SYSGENREDIR/$name.conf" ]; then
		do_msg "Error" "Global \"$name.conf\" already exists." 1
	else
		touch "$USRGENREDIR/$name.conf"
		do_msg "New Genre" "New genre \"$name\" created." 1
		GENRE="$name"
	fi
}
#}}}
# {{{ Genre - Remove Local Genre
remove_genre()
{
	unset option

	while :
	do
		rows=$(stty size | cut -f1 -d' ')
		cols=$(stty size | cut -f2 -d' ')

		unset GENRES
		unset LIST

		find "$USRGENREDIR" -name \*\.conf | rev | cut -d/ -f1 | cut -d. -f2- | rev 2>/dev/null > "$TMP"
		[ -z "$(< "$TMP")" ] && do_msg "Remove Genre" "No local genres to remove." 1 && return

		nice_list "Delete Genre" "Delete" || return

		GEN="$(< "$TMP")"
		GEN=${GEN%% *}
		cols=$((${#GEN}+40))
		dialog --title "Delete Genre" --yesno "Really delete \"$GEN\"?" \
			5 "$cols" 2>/dev/null && rm -f "$USRGENREDIR/$GEN".conf

		[ -z "$(ls -1 "$USRGENREDIR"/*conf 2>/dev/null)" ] && return
	done

}
# }}}
# {{{ Genre - Edit Local Genre
edit_genre()
{
	SYSTMP=$SYSGENREDIR/"$GENRE.conf"
	USRTMP=$USRGENREDIR/"$GENRE.conf"

	if [ ! -r "$USRTMP" ] && [ -r "$SYSTMP" ]; then
		cat "$SYSTMP" > "$USRTMP"
	fi

	general_edit_file "$USRTMP"
}
# }}}
# {{{ Theme - Main Menu
main_theme()
{
	local THEMEOPT
	THEMEOPT="Themes"
	while :
	do
		rows=$(stty size | cut -f1 -d' ')
		cols=$(stty size | cut -f2 -d' ')
		CUR=$(grep "export DIALOGRC=" "$SOMAOPTIONS" | cut -d= -f2)
		[ "$CUR" != "/dev/null" ] && CUR=$(basename "$CUR")

		dialog $DIALOGOPTS --cancel-label "Back" \
			--default-item "$THEMEOPT" \
			--menu "${TITCOL}Theme Options ($CUR)" "$rows" "$cols" "$rows" \
			"Themes" "Choose a Theme" \
			"Edit" "Edit Current Theme" \
			"Remove" "Remove a (Local) Theme" \
			2> "$TMP" || break

		option=$(< "$TMP")
		THEMEOPT=$option

		[ "$option" = "Themes" ] && loop_themes
		[ "$option" = "Edit" ] && edit_theme
		[ "$option" = "Remove" ] && remove_theme
	done
}
# }}}
# {{{ Theme - Loop themes
loop_themes()
{
	RET=0
	while :
	do
		choose_theme
		[ $RET -gt 0 ] && break
	done
}
# }}}
# {{{ Theme - Choose theme
choose_theme()
{
	CUR=$(grep -E "^export DIALOGRC=" "$SOMAOPTIONS" | cut -d= -f2)
	[ "$CUR" != "/dev/null" ] && CUR=$(basename "$CUR")

	find {"$SYSTHEMEDIR","$USRTHEMEDIR"} -name \*rc \
		| rev | cut -d/ -f1 | rev | sort -u > "$TMP"
	echo "/dev/null" >> "$TMP"
	nice_list "Change your dialog theme ($CUR)" "Select"

	THM=$(< "$TMP")
	[ -z "$THM" ] && RET=1 && return
	THM=${THM%% *}
	THEME=$THM

	# Look for a global theme first then check to see if a local exists with same name
	for th in "$SYSTHEMEDIR"/*rc
	do
		[ "$THM" = "$(basename "$th")" ] && THEME="$th"
	done

	for th in "$USRTHEMEDIR"/*rc
	do
		[ "$THM" = "$(basename "$th")" ] && THEME="$th"
	done

	sed -i "s#^export DIALOGRC=.*#export DIALOGRC=$THEME#" "$SOMAOPTIONS"
	. "$SOMAOPTIONS"
}
# }}}
# {{{ Theme - Edit Theme
edit_theme()
{
	if [ -n "$DIALOGRC" ]; then
		FILE=$(basename "$DIALOGRC")
	else
		FILE=$LASTTHEME
	fi

	if [ ! -r "$USRTHEMEDIR/$FILE" ]; then
		if [ -r "$SYSTHEMEDIR/$FILE" ]; then
			cat "$SYSTHEMEDIR/$FILE" > "$USRTHEMEDIR/$FILE"
			do_msg "Themes" "Local copy of $FILE created" 1
			export DIALOGRC="$USRTHEMEDIR/$FILE"
		elif [ "$DIALOGRC" = "/dev/null" ]; then
				FILE="devnullrc"
				if [ ! -r "$USRTHEMEDIR/$FILE" ]; then
					dialog --create-rc "$USRTHEMEDIR/$FILE"
					do_msg "Themes" "Local copy of /dev/null created ($FILE)" 1
				fi
				export DIALOGRC="$USRTHEMEDIR/$FILE"
		fi
	fi

	[ -z "$EDITOR" ] && EDITOR=/usr/bin/vim

	if [ -f "$USRTHEMEDIR/$FILE" ]; then
		cursor on
		TERM=xterm-256color
		"$EDITOR" "$USRTHEMEDIR/$FILE"
		cursor off

		if dialog --msgbox "Theme parsed OK. Press enter to continue." 5 45; then
			export DIALOGRC="$USRTHEMEDIR/$FILE"
			unset LASTTHEME
		else
			LASTTHEME="$FILE"
			unset DIALOGRC
			do_msg "Error" "Parse error in $FILE.\n
Theme has been unset until you fix the errors or choose another theme." 0
		fi
	fi
}
# }}}
# {{{ Theme - Remove a Theme
remove_theme()
{
	unset option

	while :
	do
		unset NICELIST

		find "$USRTHEMEDIR" -name \*rc | rev | cut -d/ -f1 | rev > "$TMP"
		[ -z "$(< "$TMP")" ] && do_msg "Remove Theme" "No local themes to remove." 1 && return

		nice_list "Remove a Theme" "Delete" || return

		THM=$(< "$TMP")
		[ -z "$THM" ] && RET=1 && return
		THM=${THM%% *}

		for th in "$USRTHEMEDIR"/*rc
		do
			if [ "$THM" = "$(basename "$th")" ]; then
				cols=$((${#GEN}+40))
				dialog --title "Delete Theme" --yesno "Really delete $THM?" \
					5 "$cols" 2>/dev/null && rm -f "$th"
			fi
		done

		! ls -1 "$USRTHEMEDIR"/*rc 2>/dev/null && return
	done
}
# }}}
# {{{ Bookmarks - Main Menu
main_bookmarks()
{
	local BOOKOPT
	BOOKOPT="View Bookmarks"
	while :
	do
		rows=$(stty size | cut -f1 -d' ')
		cols=$(stty size | cut -f2 -d' ')

		dialog $DIALOGOPTS --cancel-label "Back" \
			--default-item "$BOOKOPT" \
			--menu "${TITCOL}Bookmarks Menu" \
			"$rows" "$cols" "$rows" \
			"View Bookmarks" "View" \
			"Add Bookmark From Station List" "Add" \
			"Add Bookmark Manually" "Add" \
			"Sort Bookmarks" "Sort" \
			"Delete Bookmarks" "Delete" \
			"Edit Bookmarks" "Edit Bookmarks File" \
			2> "$TMP" || return

		main=$(< "$TMP")
		BOOKOPT=$main

		[ "$main" = "View Bookmarks" ] && view_bookmarks
		[ "$main" = "Add Bookmark From Station List" ] && add_bookmark_from_list
		[ "$main" = "Add Bookmark Manually" ] && man_add_bookmark
		[ "$main" = "Sort Bookmarks" ] && sort_bookmarks
		[ "$main" = "Delete Bookmarks" ] && delete_bookmarks
		[ "$main" = "Edit Bookmarks" ] && edit_bookmarks
	done
}
#}}}
# {{{ Bookmarks - View Bookmarks
view_bookmarks()
{
	if [ ! -r "$BOOKMARKS" ] ; then
		do_msg "Bookmarks" "No bookmarks file." 1
		return
	fi

	if [ -z "$(< "$BOOKMARKS")" ]; then
		do_msg "Bookmarks" "Bookmarks file is empty." 1
		return
	fi

	WITHURL=1
	station_selector $__BOOKMARKS
	unset WITHURL
}
#}}}
# {{{ Bookmarks - Add Bookmark From List
add_bookmark_from_list()
{
	unset option
	get_station_list $__ALL

	while :
	do
		echo "$STATIONLIST" | sed 's# \\##g' | cut -d'"' -f2 > "$TMP"
		nice_list "Stations (All)" "Add" || return

		CHAN="$(sed 's# *$##' "$TMP")"
		STAT=$(grep -E "^$CHAN," "$STATIONS" | head -n 1)

		IFS=$EXTIFS

		if ! grep -E "^$STAT" "$BOOKMARKS"; then
			echo "$STAT" >> "$BOOKMARKS"
			do_msg "Add Bookmark" "$CHAN added." 1
		else
			do_msg "Add Bookmark" "$CHAN already in bookmark list." 1
		fi

		IFS=$DEFIFS
	done
}
# }}}
# {{{ Bookmarks - Manually Add Bookmark
man_add_bookmark()
{
	while :
	do
		general_add_station "$BOOKMARKS" || return
	done
}
#}}}
# {{{ Bookmarks - Add Bookmark
add_bookmark()
{
	[ "$CHANNEL" = "Idle" ] || [ -z "$CHANNEL" ] && return
	if [ -r "$BOOKMARKS" ]; then
		if grep -E "^$CHANNEL," "$BOOKMARKS"; then
			do_msg "Add Bookmark" "\"$CHANNEL\" already bookmarked!" 1
			return
		fi
	fi

	STATIONURL=$(echo "$STATIONLIST" | grep -w "$CHANNEL" | cut -d'"' -f4 | head -n 1)
	echo "$CHANNEL,$STATIONURL" >> "$BOOKMARKS"

	do_msg "Add Bookmark" "\"$CHANNEL\" bookmarked." 1
}
#}}}
# {{{ Bookmarks - Delete Bookmarks
delete_bookmarks()
{
	TMPLIST=$BOOKMARKS
	general_delete $__BOOKMARKS
}
#}}}
# {{{ Bookmarks - Sort Bookmarks
sort_bookmarks()
{
	if [ ! -r "$BOOKMARKS" ]; then
		do_msg "Sort Bookmarks" "No bookmarks file." 1
		return
	fi

	cat "$BOOKMARKS" > "$SOMAROOT"/sort_infile
	general_sort $__BOOKMARKS && cat "$TMP" > "$BOOKMARKS"
}
# }}}
# {{{ Bookmarks - Edit File
edit_bookmarks()
{
	general_edit_file "$BOOKMARKS"
}
# }}}
# {{{ User Stations - Main Menu
user_stations()
{
	local USERSTATOPT
	USERSTATOPT="View Stations"
	while :
	do
		rows=$(stty size | cut -f1 -d' ')
		cols=$(stty size | cut -f2 -d' ')

		dialog $DIALOGOPTS --cancel-label "Back" \
			--default-item "$USERSTATOPT" \
			--menu "${TITCOL}User Stations Menu" \
			"$rows" "$cols" "$rows" \
			"View Stations" "View" \
			"Add Station Manually" "Add" \
			"Sort Stations" "Sort" \
			"Delete Stations" "Delete" \
			"Edit Stations" "Edit User Stations File" \
			2> "$TMP" || return

		userstat=$(< "$TMP")
		USERSTATOPT=$userstat

		[ "$userstat" = "View Stations" ] && view_user_stations
		[ "$userstat" = "Add Station Manually" ] && man_add_user_station
		[ "$userstat" = "Sort Stations" ] && sort_user_stations
		[ "$userstat" = "Delete Stations" ] && delete_user_stations
		[ "$userstat" = "Edit Stations" ] && edit_user_stations
	done
}
#}}}
# {{{ User Stations - View Stations
view_user_stations()
{
	if [ -z "$(< "$USRSTATIONS")" ]; then
		do_msg "User Stations" "User stations file is empty." 1
		return
	fi

	WITHURL=1
	station_selector $__LOCAL
	unset WITHURL
}
# }}}
# {{{ User Stations - Manually Add Station
man_add_user_station()
{
	while :
	do
		general_add_station "$USRSTATIONS" || return
	done
}
#}}}
# {{{ User Stations - Sort Stations
sort_user_stations()
{
	cat "$USRSTATIONS" > "$SOMAROOT"/sort_infile
	general_sort $__LOCAL && cat "$TMP" > "$USRSTATIONS"
}
# }}}
# {{{ User Stations - Delete Stations
delete_user_stations()
{
	TMPLIST=$USRSTATIONS
	general_delete $__LOCAL
}
#}}}
# {{{ User Stations - Edit File
edit_user_stations()
{
	general_edit_file "$USRSTATIONS"
}
# }}}
# {{{ Mainmenu
mainmenu() {
	[ -z "$MENUOPT" ] && MENUOPT="Stations"

	while :
	do
		rows=$(stty size | cut -f1 -d' ')
		cols=$(stty size | cut -f2 -d' ')

		dialog $DIALOGOPTS --cancel-label "Quit" \
			--default-item "$MENUOPT" \
			--menu "${TITCOL}Main Menu" \
			"$rows" "$cols" "$rows" \
			"Stations" "Station Browser (Genre)" \
			"All Stations" "Station Browser (All)" \
			"Status" "Playing Now" \
			"Bookmarks" "Bookmarks" \
			"Genres" "Genre Menu" \
			"Themes" "Theme Options" \
			"User Stations" "Edit User Stations" \
			"Scheduler" "Scheduler" \
			"Mixer" "Mixer" \
			"Output" "Choose Sound Device" \
			"Choose Mixer" "Choose Mixer Device" \
			"Save" "Save Track List" \
			"Options" "Manually Edit Options" \
			"Quit" "Quit Soma" \
			2> "$TMP" || cleanup

		main=$(< "$TMP")
		MENUOPT=$main

		[ "$main" = "Stations" ] && station_selector $__GENRE
		[ "$main" = "All Stations" ] && station_selector $__ALL
		[ "$main" = "Bookmarks" ] && main_bookmarks
		[ "$main" = "Genres" ] && main_genre_menu
		[ "$main" = "Themes" ] && main_theme
		[ "$main" = "User Stations" ] && user_stations
		[ "$main" = "Scheduler" ] && schedule_main
		[ "$main" = "Mixer" ] && mixerpanel
		[ "$main" = "Output" ] && selectoutput
		[ "$main" = "Choose Mixer" ] && selectmixer
		[ "$main" = "Save" ] && save
		[ "$main" = "Options" ] && edit_options
		[ "$main" = "Quit" ] && cleanup

		if [ "$main" = "Status" ]; then
			if [ "$DEBUG" -eq 0 ]; then
				status
			else
				rawstatus
			fi
		fi
	done
}
# }}}
# {{{ Begin
VERSION="3.0.4"
DEBUG=0
init
purge_local
cursor off
get_commandline "$@"
mainmenu
# }}}

# vim: tw=0
