#!/bin/sh

sz_usage="I need 0 (zero) mandatory args : `basename $0` [optional arguments]
			
Legend of [optional arguments] ::

--begin <level> :	Use <level> as the beginning stage
(
	defaults to 1. But you can specify a higher value as long as it is lower
	than or equal to end. You can also specify a begin value of 0 - stage 0 runs
	a special initialization procedure to set up your graphics subsystem
)

--end <level> :	Use <level> as the last stage to process
(
	defaults to the highest stage value available, as evaluated at runtime.	But
	you can change that to something lower, as long as it is at least equal to
	the begin value
)					

--pkg_list_dir <dir> :	Use <dir> as the package list directory
(defaults to \$HOME/mkdesktop/pkg_list)

--no-postproc :	Disable the postproc function that kicks in automatically upon
processing of regular stages to configure emulation layers and system files

--dry :		in dry mode, special functions (stage 0; post-processor) are
disabled and regular stage levels are processed by 'pkg install --dry-run',
unless you specify --echo too (in which case the --dry argument is silently
ignored). --dry is available to non-root users too.

--echo :	in echo mode, special functions (stage 0; post-processor) are
disabled and regular stage levels are processed by 'echo', not 'pkg install'.
--echo takes precedence over --dry, as well as is available to non-root users
too. This is useful for testing what non-automatic packages would get installed
by this script.

--append :	in append mode, packages mentioned in package list files are
appended to the stage definition lists, which otherwise would be superseded.

--help :	print an introductory manual
(this uses \$EDITOR, if set. If not set, rvim or else rjoe or else vi)

--usage :	print usage information"

location=$HOME/mkdesktop
pkg_list_dir="$location/pkg_list"
no_postproc=false

mode_echo=false
mode_dry=false
mode_append=false
mode_yes=false

begin=1
end=-1

arg_begin=false
arg_end=false

dummy=0

die()
{
	[ $# -eq 0 ] && exit 1
	
	[ $# -gt 1 ] && \
	echo "error initiated at line $1 : 
	$2" 1>&2 || \
	echo "$1" 1>&2
	
	exit 1
}

while echo "$1" | grep '^--' > /dev/null; do
	case "$1" in
		--begin)
			shift
			begin=""
			
			if [ $# -gt 0 ]; then
				if echo $1 | grep '[^[:digit:]]' > /dev/null; then
					die "$LINENO" "Bad digital value for begin : $1"
				else
					begin="$1"
					arg_begin=true
					shift
				fi
			fi
		;;

		--end)
			shift
			end=""
			
			if [ $# -gt 0 ]; then
				if echo $1 | grep '[^[:digit:]]' > /dev/null; then
					die "$LINENO" "Bad digital value for end : $1"
				else
					end="$1"
					arg_end=true
					shift
				fi
			fi
		;;

		--pkg_list_dir)
			shift
			pkg_list_dir=""
			
			if [ $# -gt 0 ]; then
				pkg_list_dir="$1"
				shift
			fi
		;;
				
		--no-postproc)
			shift
			no_postproc=true
		;;
		
		--dry)
			shift
			mode_dry=true
		;;
		
		--echo)
			shift
			mode_echo=true
		;;

		--append)
			shift
			mode_append=true
		;;
		
		--help)
			my_editor="$EDITOR"
			[ -z "$my_editor" ] && [ -x /usr/local/bin/rvim ] && my_editor=/usr/local/bin/rvim
			[ -z "$my_editor" ] && [ -x /usr/local/bin/rjoe ] && my_editor=/usr/local/bin/rjoe
			[ -z "$my_editor" ] && my_editor=vi

			"$my_editor" /usr/local/share/mkdesktop/mkdesktop.help
			exit 0
		;;
		
		--usage)
			echo "$sz_usage"
			exit 0
		;;

		*)
			die "$LINENO" "Invalid optional arg : $1"
		;;
	esac
done

[ $# -eq 0 ] || { die "$sz_usage"; }

stage=0
idents=""

for var in location pkg_list_dir; do
	eval val='$'$var
	[ -z "$val" ] && die "$LINENO" "Empty string for $var"
done

if ! [ -d "$location" ]; then
	cat <<-EOF
	This seems to be the first time you are running mkdesktop.
	
	I can help you with the initial layout for running mkdesktop smoothly.
	
	If you wish, I can :
	
	1) 	create the directory $HOME/mkdesktop
		(which hosts the stage-definitions file)

	2) 	create a default $HOME/mkdesktop/stage-definitions
		(which should be good enough to get you started)
	
	Of course, you can always create the above by hand. And you can always
	customize your stage-definitions file. You are encouraged to at least read
	your stage-definitions file ($HOME/mkdesktop/stage-definitions), just to get
	a feel of what lies therein, and perhaps define a custom stage too, so that
	you becomes familiar with mkdesktop's working and ensure that everything
	works seamlessly end-to-end for you. If you mess up your stage-definitions
	file, no probs : just copy out afresh from /usr/local/share/mkdesktop/

	If you later decide to boost your setup with package list files (read the
	output of --help for this), put them under the directory
	$HOME/mkdesktop/pkg_list (which too I can create right now; or else, you can
	create yourself later).
	
	What would you like me to do ?
	
	I) Initialize the setup
	S) Skip initialization
	
	Press Control-C if you wish to abort.
	
	Your choice ? (I/S) :
	EOF
	
	read answer
	
	case "$answer" in
		i|I)
			set -e
			mkdir $HOME/mkdesktop
			cp /usr/local/share/mkdesktop/stage-definitions $HOME/mkdesktop/stage-definitions
			mkdir $HOME/mkdesktop/pkg_list

			cat <<-EOF

			mkdesktop has been initialized.
			
			If you wish to alter the stage-definitions or package list files for
			this run, you can do still do it from another terminal window.

			When ready, press Enter to continue  (or Control-C to abort)
			EOF

			read enter
			set +e
		;;
		
		s|S)
		;;
		
		*)
			echo "I could not make sense of your choice  : - ("  1>&2
			exit 1
		;;
	esac
fi

[ -f "$location"/stage-definitions ] && \
{ . "$location"/stage-definitions; } || \
{ echo "Warning : $location/stage-definitions does not exist" 1>&2; }

for var in begin end; do 
	eval val='$'$var

	[ $var = end ] && [ "$val" = "-1" ] && val=0
	[ -z "$val" ] && die "$LINENO" "Empty string for $var"
	
	echo "$val" | grep '[^[:digit:]]' > /dev/null && \
	die "$LINENO" "Bad digital value ($var) : ${val}"
done

[ $end -gt $stage ] && \
die "$LINENO" "end must not be greater than the highest stage value ($stage)"

[ $end -lt 0 ] && end=$stage

if [ $begin -gt $end ]; then
	[ $end -eq 0 ] && echo "Have you defined at least one stage ?" 1>&2
	die "$LINENO" "begin must not be greater than end"
fi

if [ "$mode_echo" = "false" -a "$mode_dry" = "false" ]; then
	dummy=`expr $dummy + 1`
	
	[ `id -u` -eq 0 ] || die "You need to be root to execute this script"
fi

f_stagezero()
{
	if [ "$mode_echo" = "false" -a "$mode_dry" = "false" ]; then
		cat <<-EOF

		This special stage has only one thing to do :
		setup any needed kld's for your graphics chipset
	
		Continue ?  (y/n) :
		EOF

		read answer
	
		case "$answer" in
			y|Y)
				cat <<-EOF
				
				What is your graphics chipset :
	
				1) radeon	[ needs radeon.ko; radeonkms.ko :
				both shipped by FreeBSD in the base distribution ]
				
				2) nvidia	[ needs nvidia.ko; nvidia-modeset.ko :
				both intalled via nvidia-driver ]

				3) other 	[ needs nothing/something I am not aware of ]
	
				Your choice  ?  (1/2/3) : 
				EOF
	
				read answer
	
				case "$answer" in
					1)
						for k in radeon radeonkms; do
							echo "kldstat -qn $k || kldload $k" >> /etc/rc.local
						done
	
						return $?
					;;
	
					2)
						pkg install nvidia-driver
						
						[ $? -eq 0 ] || die "$LINENO" "nvidia-driver install failed"
	
						for k in nvidia nvidia-modeset; do
							echo "kldstat -qn $k || kldload $k" >> /etc/rc.local
						done
	
						return $?
					;;
	
					3)
						return 0
					;;
	
					*)
						echo "I do not know what to make of that  : - (" 1>&2
						return 1
					;;
	
				esac
			;;
	
			n|N)
			;;
	
			*)
				echo "I do not know what to make of that  : - (" 1>&2
				return 1
			;;
		esac
	fi
}

fix_noeol()
{
	[ $# -gt 0 ] || { echo "missing mandatory arg : <file>" 1>&2; return 1; }
	[ -f "$1" ] || { echo "$1 is not a regular file" 1>&2; return 1; }

	local r=0
	local f="$1"

	if file "$f" | grep '\<ASCII text\>' > /dev/null; then
		local last="`tail -c1 \"$f\"`"
		
		if [ -n "$last" ]; then
			[ -w "$f" ] || { echo "$f is not writable" 1>&2; return 1; }

		 	echo >> "$f"
			r=$?
		fi
	fi

	return $r
}

f_stage()
{
	[ $# -ne 1 ] && \
	{ echo "f_stage() :: I need 1 arg : stage name (ident)" 1>&2; return 1; }

	[ -z "$1" ] && \
	{ echo "f_stage() :: I need 1 arg : stage name (ident)" 1>&2; return 1; }
	
	if ! echo "$idents" | grep -w "$1" > /dev/null; then
		echo "f_stage() :: I could not locate $1 among the definitions" 1>&2
		return 1
	fi

	echo -e "\e[4m${1}\e[0m :: \c"
	
	local nextline=""
	local firstchar=""
	local lines=0
	local index=0

	local file=""
	eval local arg='$'list_${1}

	local list="`echo $arg | sed 's/:/ /g'`"
	
	[ -f "$pkg_list_dir"/"$1" ] && file="$pkg_list_dir"/"$1"
	
	if [ -n "$file" ]; then
		fix_noeol "$file"
		
		[ "$mode_append" = "false" ] && list=""
		lines="`cat $file | wc -l | sed 's/^[[:space:]]*//'`"
		index=1
		
		while [ $index -le $lines ]; do
			nextline="`sed -n ${index}p $file`"
			
			if [ -n "$nextline" ]; then
				firstchar="`echo $nextline | cut -c1`"
				
				if [ "$firstchar" = '#' ]; then
					dummy=`expr $dummy + 1`
				else
					[ -z "$list" ] && list="$nextline" || list="$list $nextline"
				fi
			fi
			
			index=`expr $index + 1`
		done
	fi
	
	local cmd="pkg install"
	
	[ "$mode_dry" = "true" ] && \
	{ cmd="$cmd --dry-run"; } || \
	{ [ "$mode_yes" = "true" ] && cmd="$cmd --yes"; }
	
	[ "$mode_echo" = true ] && cmd="echo"

	[ -z "$list" ] && echo || { $cmd $list; }
	local r=$?

	[ "$mode_dry" = "true" ] && [ $r -eq 1 ] && r=0
	return $r
}

[ $begin -gt 0 ] || f_stagezero
[ $? -eq 0 ] || die "$LINENO" "stage failure setting up graphics subsystem"
[ $end -eq 0 ] && exit 0

postproc()
{
	[ "$mode_dry" = "true" ] && return 0
	[ "$mode_echo" = "true" ] && return 0
	[ "$no_postproc" = "true" ] && return 0

	cat <<-EOF
	
	Your desktop has been successfully installed  : - )

	I am now going to start post-install processing to set up the following :

	Wine 		(32-bit/64-bit Windows emulation layer, user-space)
	Linuxulator	(64-bit Linux emulation layer, kernel-space)

	System files :
	/etc/fstab
	/etc/devfs.conf
	/etc/devfs.rules
	/etc/rc.conf
	/etc/sysctl.conf
	/boot/loader.conf

	I will seek permission from you for each of the above individually  : - )

	Note :	If you ask me to configure system files, existing originals will be
			saved with .bak extension
	EOF

	b_valid=false

	while [ "$b_valid" = "false" ]; do
		cat <<-EOF

		Would you like me to kick off post-processing ?
	
		y) Do it
		n) Cancel this [and exit immediately]
		
		Your choice  ?  (y/n) :
		EOF
	
		read answer
	
		case "$answer" in
			y|Y)
				b_valid=true
			;;
	
			n|N)
				b_valid=true
				exit 0
			;;
	
			*)
				echo "I do not know what to make of that  : - (" 1>&2
			;;
		esac
	done

	readonly UNITY=1
	readonly WIN32=$(( UNITY<<1 ))
	readonly WIN64=$(( UNITY<<2 ))
	readonly LINUX=$(( UNITY<<3 ))
	
	clear && echo "Beginning postproc"

	EMU=0

	echo && echo "Would you like me to set up Wine (Windows emulation layer) ?"
	echo && echo "y/n :"
	read answer
	
	case "$answer" in
		y|Y)
			cat <<-EOF
			
			Choose a mode :

			1) 32-bit
			2) 64-bit (This won't work if your system's CPU is 32-bit)
			c) cancel Wine
			EOF

			read answer

			case "$answer" in
				1)
					EMU=$(( EMU | UNITY | WIN32 ))
				;;
		
				2)
					EMU=$(( EMU | UNITY | WIN64 ))
				;;
	
				c|C)
				;;
	
				*)
					echo "I do not know what that means  : - ("
					echo "Skipping Wine"
				;;
			esac
		;;

		n|N)
		;;

		*)
			echo "I do not know what that means  : - ("
			echo "Skipping Wine"
		;;
	esac
	
	echo &&	echo "Would you like me to set up Linuxulator (Linux emulation layer) ?"
	echo && echo "Note : if you choose y, I will factor the Linuxulator into subsequent processing"
	echo && echo "y/n :"

	read answer

	case "$answer" in
		y|Y)
			EMU=$(( EMU | UNITY | LINUX ))
		;;
	
		n|N)
		;;
	
		*)
			echo "I do not know what that means  : - ("
			echo "Skipping Linuxulator"
		;;
	esac

	my_emu=$(( EMU & UNITY ))

	result_win32=1
	result_win64=1
	result_linux=1

	if [ $my_emu -ne 0 ]; then
		my_emu=$(( EMU & WIN32 ))

		if [ $my_emu -ne 0 ]; then
			pkg install --yes i386-wine wine-mono wine-gecko
			result_win32=$?
		fi

		my_emu=$(( EMU & WIN64 ))

		if [ $my_emu -ne 0 ]; then
			pkg install --yes wine wine-mono wine-gecko
			result_win64=$?
		fi

		my_emu=$(( EMU & LINUX ))

		if [ $my_emu -ne 0 ]; then
			if ! kldstat -q -n linux; then
				kldload linux
				
				[ $? -eq 0 ] || \
				die "$LINENO" "Unable to kldload linux"
			fi
			
			pkg install --yes linux_base-c6
			result_linux=$?
		fi
	fi

	flavour_linux=false

	if [ $(( EMU & LINUX )) -ne 0 ]; then
		if [ $result_linux -eq 0 ]; then
			flavour_linux=true
		fi
	fi

	set -e

	#block for /etc/fstab
	{
		f=/etc/fstab
		mangled=`echo $f | sed 's|[^[:alpha:]]|_|g'`
		
		if [ "$flavour_linux" = "false" ]; then
			eval mandatory${mangled}="\"\
fdescfs /dev/fd fdescfs rw 0 0
procfs /proc procfs rw 0 0\""

			eval hint${mangled}="\"\
If you are going to use the Linuxulator to run Linux applications under
FreeBSD natively, try this/these in $f once you have setup the Linuxulator
(most easily done in a single command with 'pkg install linux-sublime3') :\""
		
			eval suggested${mangled}="\"\
tmpfs /compat/linux/dev/shm tmpfs rw,mode=1777,size=1g 0 0
linprocfs /compat/linux/proc linprocfs rw 0 0
linsysfs /compat/linux/sys linsysfs rw 0 0\""
		else
			eval mandatory${mangled}="\"\
fdescfs /dev/fd fdescfs rw 0 0
procfs /proc procfs rw 0 0
tmpfs /compat/linux/dev/shm tmpfs rw,mode=1777,size=1g 0 0
linprocfs /compat/linux/proc linprocfs rw 0 0
linsysfs /compat/linux/sys linsysfs rw 0 0\""

			eval hint${mangled}=""
			eval suggested${mangled}=""
		fi
	}

	#block for /etc/devfs.conf
	{
		f=/etc/devfs.conf
		mangled=`echo $f | sed 's|[^[:alpha:]]|_|g'`
		
		if [ "$flavour_linux" = "false" ]; then
			eval mandatory${mangled}="\"\
own     /dev/pci        root:operator
perm    /dev/pci        0664
own     /dev/dri/card0  root:operator
perm    /dev/dri/card0  0664
own     /dev/pass0      root:operator
perm    /dev/pass0      0664
own     /dev/cd0        root:operator
perm    /dev/cd0        0664
own     /dev/xpt0       root:operator
perm    /dev/xpt0       0664\""

			eval hint${mangled}="\"\
If you're going to use the Linuxulator to run Linux applications under
FreeBSD natively, try this/these in $f once you have setup the Linuxulator
(most easily done in a single command with 'pkg install linux-sublime3') :\""

			eval suggested${mangled}="\"\
link /compat/linux/dev/shm shm\""
		else
			eval mandatory${mangled}="\"\
own     /dev/pci        root:operator
perm    /dev/pci        0664
own     /dev/dri/card0  root:operator
perm    /dev/dri/card0  0664
own     /dev/pass0      root:operator
perm    /dev/pass0      0664
own     /dev/cd0        root:operator
perm    /dev/cd0        0664
own     /dev/xpt0       root:operator
perm    /dev/xpt0       0664
link /compat/linux/dev/shm shm\""

			eval hint${mangled}=""
			eval suggested${mangled}=""
		fi
	}

	#block for /etc/devfs.rules
	{
		f=/etc/devfs.rules
		mangled=`echo $f | sed 's|[^[:alpha:]]|_|g'`
		
		eval mandatory${mangled}="\"\
[system=10]
add path 'usb/*' mode 0664 group operator
add path 'cd*' mode 0664 group operator
add path 'da*' mode 0664 group operator
add path 'video*' mode 0664 group operator\""

		eval hint${mangled}=""
		eval suggested${mangled}=""
	}

	#block for /etc/rc.conf
	{
		f=/etc/rc.conf
		mangled=`echo $f | sed 's|[^[:alpha:]]|_|g'`
		
		if [ "$flavour_linux" = "false" ]; then
			eval mandatory${mangled}="\"\
devfs_system_ruleset=system
dbus_enable=YES
hald_enable=YES
cupsd_enable=YES\""

			eval hint${mangled}="\"\
Here's a few more goodies for you to keep in mind for ${f} :

1) For fuse to be able to mount non-native filesystems like ntfs/ext4
2) For a decent typing speed on the console
3) For auto-loading linux.ko\""

			eval suggested${mangled}="\"\
fusefs_enable=YES
keyrate=fast
linux_enable=YES\""
		else
			eval mandatory${mangled}="\"\
devfs_system_ruleset=system
dbus_enable=YES
hald_enable=YES
linux_enable=YES
cupsd_enable=YES\""

			eval hint${mangled}="\"\
Here's a few more goodies for you to keep in mind for ${f} :

1) For fuse to be able to mount non-native filesystems like ntfs/ext4
2) For a decent typing speed on the console\""

			eval suggested${mangled}="\"\
fusefs_enable=YES
keyrate=fast\""
		fi
	}

	#block for /etc/sysctl.conf
	{
		f=/etc/sysctl.conf
		mangled=`echo $f | sed 's|[^[:alpha:]]|_|g'`
		
		if [ "$flavour_linux" = "false" ]; then
			eval mandatory${mangled}=""

			eval hint${mangled}="\"\
You might like to keep in mind a few other suggestions for ${f}, all optional.
1 goodie to let normal users mount disks and 2 Linuxulator goodies :\""

			eval suggested${mangled}="\"\
vfs.usermount=1
compat.linux.osrelease=2.6.18
kern.ipc.shm_allow_removed=1\""
		else
			eval mandatory${mangled}="\"\
compat.linux.osrelease=2.6.18
kern.ipc.shm_allow_removed=1\""

			eval hint${mangled}="\"\
For $f, keep in mind a tip to let normal users mount disks :\""

			eval suggested${mangled}="\"\
vfs.usermount=1\""
		fi
	}

	#block for /boot/loader.conf
	{
		f=/boot/loader.conf
		mangled=`echo $f | sed 's|[^[:alpha:]]|_|g'`
		
		eval mandatory${mangled}="\"\
kern.vty=vt\""
		
		eval hint${mangled}=""
		eval suggested${mangled}=""
	}
	
	for f in /etc/fstab; do
		echo && echo "Configure $f : (y/n) ?"
		read answer

		case "$answer" in
			y|Y)
				mangled=`echo $f | sed 's|[^[:alpha:]]|_|g'`
				eval mandatory='$'mandatory${mangled}

				if [ -f $f ]; then
					if [ -n "$mandatory" ]; then
						cp $f $f.bak
						
						maxlines=`echo "$mandatory" | wc -l | sed 's|^[[:space:]]*||'`
						index=1

						while [ $index -le $maxlines ]; do
							line="`echo \"$mandatory\" | sed -n ${index}p`"
							token1=`echo $line | awk '{print $1}'`
				
							if ! cat $f | grep --silent "^$token1[[:space:]]"; then
								echo $line >> $f
							fi
							
							index=`expr $index + 1`
						done
					fi
				else
					echo "$mandatory" > $f
				fi
				
				echo && echo "Done configuring $f"
				eval hint='$'hint${mangled}
				eval suggested='$'suggested${mangled}
				
				if [ -n "$suggested" ]; then
					echo "$hint"
					echo && echo "$suggested"
				fi
			;;

			n|N)
			;;

			*)
				echo "I do not know what that means  : - ("
				echo "Skipping $f"
			;;
		esac
	done
	
	for f in /etc/devfs.conf; do
		echo && echo "Configure $f : (y/n) ?"
		read answer

		case "$answer" in
			y|Y)
				mangled=`echo $f | sed 's|[^[:alpha:]]|_|g'`
				eval mandatory='$'mandatory${mangled}

				if [ -f $f ]; then
					if [ -n "$mandatory" ]; then
						cp $f $f.bak
						
						maxlines=`echo "$mandatory" | wc -l | sed 's|^[[:space:]]*||'`
						index=1

						while [ $index -le $maxlines ]; do
							line="`echo \"$mandatory\" | sed -n ${index}p`"
								
							if ! cat $f | grep --silent "$line"; then
								token1=`echo $line | awk '{print $1}'`
								token3=`echo $line | awk '{print $3}'`

								token2=`echo $line | awk '{print $2}'`
								token2_alt=`echo $token2 | sed -n 's|^/dev/||p'`
								t2_lastchar=`echo $token2 | rev | cut -c1`

								search="^${token1}[[:space:]]+${token2}[[:space:]]"

								if [ -n "$token2_alt" ]; then
									search="^${token1}[[:space:]]+(${token2}|${token2_alt})[[:space:]]"
								fi
							
								if [ "$t2_lastchar" = "0" ]; then
									lead=`echo $token2 		| sed -n 's|^\(.*[^[:digit:]]\)\([[:digit:]]*\)$|\1|p'`
									trail=`echo $token2 	| sed -n 's|^\(.*[^[:digit:]]\)\([[:digit:]]*\)$|\2|p'`

									while [ -c $token2 ]; do
										if ! cat $f | egrep --silent "$search"; then
											echo $line >> $f
										fi

										trail=`expr $trail + 1`
										token2="${lead}${trail}"
										token2_alt=`echo $token2 | sed -n 's|^/dev/||p'`
										line="$token1 $token2 $token3"

										search="^${token1}[[:space:]]+${token2}[[:space:]]"

										if [ -n "$token2_alt" ]; then
											search="^${token1}[[:space:]]+(${token2}|${token2_alt})[[:space:]]"
										fi
									done
								else
									if [ -c $token2 ]; then
										if ! cat $f | egrep --silent "$search"; then
											echo $line >> $f
										fi
									fi					
								fi
							fi
							
							index=`expr $index + 1`
						done
					fi
				else
					echo "$mandatory" > $f
				fi
				
				echo && echo "Done configuring $f"
				eval hint='$'hint${mangled}
				eval suggested='$'suggested${mangled}
				
				if [ -n "$suggested" ]; then
					echo "$hint"
					echo && echo "$suggested"
				fi
			;;

			n|N)
			;;

			*)
				echo "I do not know what that means  : - ("
				echo "Skipping $f"
			;;
		esac
	done

	for f in /etc/devfs.rules; do
		echo && echo "Configure $f : (y/n) ?"
		read answer

		case "$answer" in
			y|Y)
				mangled=`echo $f | sed 's|[^[:alpha:]]|_|g'`
				eval mandatory='$'mandatory${mangled}

				if [ -f $f ]; then
					if [ -n "$mandatory" ]; then
						cp $f $f.bak
						
						line="`echo \"$mandatory\" | sed -n 1p`"
								
						if ! cat $f | grep --silent "$line"; then
							echo "$mandatory" >> $f
						fi
					fi
				else
					echo "$mandatory" > $f
				fi

				echo && echo "Done configuring $f"
				eval hint='$'hint${mangled}
				eval suggested='$'suggested${mangled}
				
				if [ -n "$suggested" ]; then
					echo "$hint"
					echo && echo "$suggested"
				fi
			;;

			n|N)
			;;

			*)
				echo "I do not know what that means  : - ("
				echo "Skipping $f"
			;;
		esac
	done

	for f in /etc/rc.conf /etc/sysctl.conf /boot/loader.conf; do
		echo && echo "Configure $f : (y/n) ?"
		read answer

		case "$answer" in
			y|Y)
				mangled=`echo $f | sed 's|[^[:alpha:]]|_|g'`
				eval mandatory='$'mandatory${mangled}

				if [ -f $f ]; then
					if [ -n "$mandatory" ]; then
						cp $f $f.bak

						maxlines=`echo "$mandatory" | wc -l | sed 's|^[[:space:]]*||'`
						index=1

						while [ $index -le $maxlines ]; do
							line="`echo \"$mandatory\" | sed -n ${index}p`"
								
							if ! cat $f | grep --silent "$line"; then
								token1=`echo $line | awk -F= '{print $1}'`
								token2=`echo $line | awk -F= '{print $2}'`

								search="^${token1}="
								
								if ! cat $f | egrep --silent "$search"; then
									echo $line >> $f
								fi
							fi
							
							index=`expr $index + 1`
						done
					fi
				else
					echo "$mandatory" > $f
				fi
				
				echo && echo "Done configuring $f"
				eval hint='$'hint${mangled}
				eval suggested='$'suggested${mangled}
				
				if [ -n "$suggested" ]; then
					echo "$hint"
					echo && echo "$suggested"
				fi
			;;

			n|N)
			;;

			*)
				echo "I do not know what that means  : - ("
				echo "Skipping $f"
			;;
		esac
	done

	set +e
	echo

	cat <<-EOF
	Post-processing done.
	If you saw no errors anywhere, your box is raring to go  : - )
	A reboot is now needed - you will need to do that yourself.

	Before you reboot, please make a note of one setting that could save you
	hours of googling and jostling later on.
	
	If you plug in a USB device, it usually will be visible to every user
	out-of-the-box. But sometimes, it may happen that only the root user is able
	to see the device (most commonly happens with printer/scanner).

	If that happens, run usbconfig as root to ensure that the device is showing
	up in the output, and make a note of its ugen ID, with <x>.<y> translating
	as <x>.<y>.0 (for example, ugen2.4 would correspond to the ugen ID 2.4.0)
	
	Then run usbconfig again, this time as the normal user. If the ugen ID is
	missing, add a line to /etc/devfs.conf (as root, of course) :

	perm usb/<ugen_ID> 0664

	For our example, that would mean :

	perm usb/2.4.0 0664

	Then, as root, run '/etc/rc.d/devfs restart'.
	Your device should now work	cleanly for the normal user too.
	EOF
}

if [ -n "$idents" ]; then
	if [ "$mode_echo" = "false" -a "$mode_dry" = "false" ]; then
		clear && echo "Here is a preview of what I shall be doing"
		echo
		echo -e "(\e[4mstage name\e[0m :: package list as per stage definition)"
		echo
		echo "<<<<----"
		echo

		readonly my_mode_echo=$mode_echo
		mode_echo=true
		previous_level=0

		for var in $idents; do
			eval level='$'$var
	
			[ -z "$level" ] && \
			die "$LINENO" "level must not evaluate as an empty string"
			
			echo "$level" | grep '[^[:digit:]]' > /dev/null && \
			die "$LINENO" "level must be numeric"
			
			if [ $level -le $previous_level ]; then
				die "$LINENO" \
"This level is ${level}; Previous was ${previous_level}
Random/descending order of stages is not supported"
			fi
			
			[ $begin -gt $level ] || f_stage $var
			
			[ $? -eq 0 ] && \
			previous_level=$level || \
			die "$LINENO" "stage failure for stage $var"
			
			[ $end -eq $level ] && { break; }
		done

		echo
		echo "---->>>>"
		echo

		mode_echo=$my_mode_echo
		b_valid=false
	
		while [ "$b_valid" = "false" ]; do
			cat <<-EOF
			If you like the preview of what is to be done, give me a :

			y) Install the stages
			
			Y) Install stages non-interactively : passes --yes to 'pkg install'
			(If you enter Y, just wait a few minutes/hours, and your entire
			desktop will get assembled nicely and I shall bother you only for
			the post-processing when the desktop has been put together)
			
			If you would like to modify the installation, press Control-C to
			abort and alter the stage-definitions file and/or package list files

			Your choice  ? (y/Y) :
			EOF
		
			read answer
		
			case "$answer" in
				y)
					b_valid=true
				;;
		
				Y)
					b_valid=true
					mode_yes=true
				;;
		
				*)
					echo "I do not know what to make of that  : - (" 1>&2
				;;
			esac
		done
	fi

	previous_level=0

	for var in $idents; do
		eval level='$'$var

		[ -z "$level" ] && \
		die "$LINENO" "level must not evaluate as an empty string"
		
		echo "$level" | grep '[^[:digit:]]' > /dev/null && \
		die "$LINENO" "level must be numeric"
		
		if [ $level -le $previous_level ]; then
			die "$LINENO" \
"This level is ${level}; Previous was ${previous_level}
Random/descending order of stages is not supported"
		fi
		
		[ $begin -gt $level ] || f_stage $var
		
		[ $? -eq 0 ] && \
		previous_level=$level || \
		die "$LINENO" "stage failure for stage $var"
		
		if [ $end -eq $level ]; then
			[ "$arg_end" = "true" ] && exit 0 || break
		fi
	done

	postproc
fi

exit $?
