#!/bin/sh
# tazinst - SliTaz GNU/Linux installer.
#
# So this is the SliTaz installer. The script starts with a
# few main variables, then all the functions and then the
# full sequence of functions.
#
# (C) 2007-2016 SliTaz - GNU General Public License v3.
#
# Authors : Christophe Lincoln <pankso@slitaz.org>
#           Dominique Corbex <domcox@slitaz.org>

# Exit codes:
# 1: Parameters error
# 2: Install file error
# 3: Source error
# 4: Target error
# 5: Missing resource
# 6: SliTaz system to upgrade not found
# 7: Another instance is running
# 8: Internal error
# 9: User cancellation


# path
PATH="/usr/sbin:/usr/bin:/sbin:/bin"
umask 0177

# read SliTaz conf
[ -r /etc/slitaz/slitaz.conf ] && . /etc/slitaz/slitaz.conf

# read Tazinst conf
[ -r /etc/slitaz/tazinst.conf ] && . /etc/slitaz/tazinst.conf

# version
readonly VERSION=115

# i18n
. /lib/libtaz.sh
export TEXTDOMAIN='tazinst'


# common kernels arguments
readonly KERNEL_ARGS="video=-32 quiet"

# files
readonly DEFAULT_INSTALL_FILE=./tazinst.rc
readonly SOURCE_ROOT=/media/source
readonly TARGET_ROOT=/mnt/target
readonly LOG=/var/log/tazinst.log
readonly LOCK=/run/tazinst.pid
MIRRORS="${MIRRORS:-$LOCALSTATE/mirrors}"

# settings
readonly SETTINGS="mode media source \
root_uuid root_format home_uuid home_format \
hostname root_pwd user_login user_pwd \
bootloader liveboot webboot winboot"

# modes (key:help)
readonly LST_MODE="
install:$(_ 'Fresh install on a HDD')
upgrade:$(_ 'Upgrade an existing system')"

# media (key:help)
readonly LST_MEDIA="
cdrom:$(_ 'LiveCD')
usb:$(_ 'LiveUSB')
iso:$(_ 'ISO image on a local drive')
web:$(_ 'ISO image on the Internet')"

# formats (key:help)
readonly LST_FORMAT="
btrfs:$(_ 'B-tree file system (Oracle)')
ext2:$(_ 'Second extended filesystem (Linux)')
ext3:$(_ 'Third extended filesystem (Linux)')
ext4:$(_ 'Fourth extended file system (Linux)')
jfs:$(_ 'Journaled File System (IBM)')
minix:$(_ 'File system of the MINIX operating system')
reiser4:$(_ 'Journaled computer file system (Namesys)')
xfs:$(_ 'Journaling file system (Silicon Graphics, Inc.)')"

# bootloaders (key:help)
readonly LST_BOOTLOADER="
auto:$(_ 'Automatic selection')
grub:$(_ 'GRUB legacy bootloader')
syslinux:$(_ 'Lightweight bootloader')"

# predefined iso (key:url:help)
SLITAZ_VERSION="${SLITAZ_VERSION:-cooking}"
[ "$SLITAZ_VERSION" = 'cooking' ] && SLITAZ_VERSION="$(($(date "+%y")-8)).0"
[ -n "$URL_ISO" ] && readonly LST_WEB="$URL_ISO" || readonly LST_WEB="
stable:iso/stable/slitaz-$SLITAZ_VERSION.iso \
:$(_ 'Stable release') $SLITAZ_VERSION
core:iso/stable/flavors/slitaz-$SLITAZ_VERSION-core.iso \
:$(_ 'Stable version without nested subsets')
base:iso/stable/flavors/slitaz-$SLITAZ_VERSION-base.iso \
:$(_ 'Stable text-only version (8.1MB)')
justx:iso/stable/flavors/slitaz-$SLITAZ_VERSION-justx.iso \
:$(_ 'Stable basic graphic version without graphic apps')
gtkonly:iso/stable/flavors/slitaz-$SLITAZ_VERSION-gtkonly.iso \
:$(_ 'Stable basic graphic version with only GTK')
cooking:iso/cooking/slitaz-cooking.iso \
:$(_ 'Development version for testing latest features')
rolling:iso/rolling/slitaz-rolling.iso \
:$(_ 'Bleeding edge development version updated every day')
"


#-------
# usage
#-------

# print a short help
usage()
{
	_ 'SliTaz GNU/Linux Installer - Version: %s' "$VERSION"
	newline; boldify "$(_ 'Usage:')"
	echo -n '  '; _ '%s [command] <setting> <value> <file>' 'tazinst'
	newline; boldify "$(_ 'Commands:')"
	optlist "\
new		$(_ 'Create a new install file.')
set		$(_ 'Change value of a setting.')
unset	$(_ 'Clear a setting.')
get		$(_ 'Get the value of a setting.')
check	$(_ 'Check settings.')
help	$(_ 'Print a short help on settings')
list	$(_ 'List system resources.')
execute	$(_ 'Execute a SliTaz installation.')
log		$(_ 'Display log file contents.')
clean	$(_ 'Clean install and log files.')
version	$(_ 'Print version and exit.')
usage	$(_ 'Print this short usage.')
"
	exit 0
}

usage_error()
{
	local cmd="$1" script="$(basename $0)"
	_ 'SliTaz GNU/Linux Installer - Version: %s' "$VERSION"
	newline
	_ "'%s': Unknown command!" "$cmd"

	_ "Run: '%s' to get a list of available commands." "$script help"
	exit 1
}


option_error()
{
	local option="$1" list="$2"
	_ 'SliTaz GNU/Linux Installer - Version: %s' "$VERSION"
	newline
	_ "'%s': Unknown option!" "$option"

	_ 'Please select one of these options:'
	printf "  %s\n\n" "$list"
	exit 1
}

#---------------------
# 1. settings section
#---------------------

#----------
# 1.1 file
#----------

# create a new install file
new_file()
{
	local install_file=$1
	[ -z "$install_file" ] && install_file="$DEFAULT_INSTALL_FILE"
	if [ -e "$install_file" ]; then
		_ 'Warning: file already exists.' 1>&2
		exit 0
	fi
	[ -n "$install_file" ] && touch "$install_file"
	if [ -w "$install_file" ]; then
		write_file "$install_file"
	else
		_ 'Error: Cannot create file.' 1>&2
		exit 2
	fi
}

# fill up the install file
write_file()
{
	local install_file="$1"
	cat > "$install_file" <<EOT
# SliTaz GNU/Linux Installer - Version: $VERSION
#
# Install file.
#

# Mode of installation:
#  install: Full install of SliTaz on a disk, all previous info will be erased
#  upgrade: Upgrade an existing SliTaz installation to a new version
# run 'tazinst list mode' to have a full list.
MODE="$MODE"

# Media to install from:
# Options are cdrom usb iso web.
# run 'tazinst list media' to see available options on your system.
MEDIA="$MEDIA"

# Install source:
# it depends on the media used:
#  usb: partition, run 'tazinst list uuid' to list your partitions
#  iso: file.iso,  ex: SOURCE=~/slitaz.5.0.iso
#  web: url,       ex: SOURCE=http://mirror.slitaz.org/../slitaz-cooking.iso
#  web: iso names, ex: SOURCE=cooking
# run 'tazinst list <MEDIA>' to list source values. ex: tazinst list iso.
SOURCE="$SOURCE"

# root partition that SliTaz will be Installed on:
# Enter the UUID of the partition.
# run 'tazinst list uuid' to list your partitions
ROOT_UUID="$ROOT_UUID"

# Formatting the root partition:
# Let ROOT_FORMAT empty if you do not want to format the root partition.
# SliTaz uses ext2 ext3 ext4 by default but another filesystem can be
# installed if wanted, for this please adjust your /etc/fstab after
# installation.
# run 'tazinst list format' to list installed filesystems on your system.
ROOT_FORMAT="$ROOT_FORMAT"

# Home partition:
# On most GNU/Linux systems users personal files are stored in the directory
# /home. /home can be on another hard disk or on a separate partition.
# Leave HOME_UUID empty if you do not intend to use a specific partition for /home
# or enter the UUID of the partition to be used.
# run 'tazinst list uuid' to list your partitions.
HOME_UUID="$HOME_UUID"

# Formatting the /home partition (if /home is on a separate partition):
# Leave HOME_FORMAT empty if you do not want to format the /home partition.
# For options, see comments on 'Formatting the root partition' above.
HOME_FORMAT="$HOME_FORMAT"

# Hostname of the new system:
HOSTNAME="$HOSTNAME"

# root password:
# The root administrator privilege lets you manage and configure the full
# system. A root user can damage your system so you should always setup a
# strong password with special characters and/or numbers.
ROOT_PWD="$ROOT_PWD"

# Default user:
# The default user for the system will have his personal files stored
# in /home/<USER_LOGIN> (and will be automatically added to the audio group).
USER_LOGIN="$USER_LOGIN"
USER_PWD="$USER_PWD"

# Install bootloader:
# If you do not want to install a bootloader, leave this field empty.
# It's generally safe to set it up as 'auto'.
# Run 'tazinst list bootloader' to list all options.
BOOTLOADER="$BOOTLOADER"

# SliTaz live/rescue boot:
# If you do not want enable live boot, leave LIVEBOOT empty (LIVEBOOT="").
# You may let tazinst automatically find your rootfs file(s) by specifying auto
# (LIVEBOOT="auto").
LIVEBOOT="$LIVEBOOT"

# SliTaz Web boot:
# If you do not want enable Web boot, leave WEBBOOT empty (WEBBOOT="").
# You may let tazinst automatically find your pxe client by specifying auto
# (WEBBOOT="auto").
WEBBOOT="$WEBBOOT"

# Windows dual boot:
# If you do not want enable Dual boot, leave WINBOOT empty (WINBOOT="").
# You may let tazinst automatically find your win partition by specifying auto
# (WINBOOT="auto"), otherwise enter the UUID of the partition to be used.
# Run 'tazinst list winboot' to see the Windows partitions found by tazinst.
WINBOOT="$WINBOOT"

EOT
	return "$?"
}

read_file()
{
	local install_file="$1"
	[ -z "$install_file" ] && install_file="$DEFAULT_INSTALL_FILE"
	if ! [ -r "$install_file" ]; then
		_ 'Error: Unable to read install file.' 1>&2
		exit 2
	fi
	#
	if ! CONTENTS="$(cat "$install_file")"; then
		_ 'Error: Unable to read install file.' 1>&2
		exit 2
	fi
}

# read value of a setting
get_value()
{
	local setting="$1"
	printf "%s" "$CONTENTS" | /bin/busybox awk -v setting="$setting" 'BEGIN{
		setting="^" toupper(setting) "="
		}
		{
		if (match($0,setting)){
			n=index($0,"=")
			value=substr($0,n+1)
			gsub(/[\t\s]*$/,"",value)
			sub(/^"/,"",value)
			sub(/"$/,"",value)
			}
		}
		END{
			print value
		}'
}

# list of settings
get_settings()
{
	local "mode=$(get_value mode)"
	case "$mode" in
		upgrade)
			echo "mode media source root_uuid bootloader liveboot webboot winboot" ;;
		*)
			printf "%s\n" "$SETTINGS" ;;
	esac
}

# get command
get()
{
	local setting="$1"
	[ -z "$setting" ] && setting="all"
	# setting is valid: display value
	if printf "%s" "$setting" | \
		egrep -q "$(regex "$SETTINGS")"; then
			get_value "$setting"
	else
		case "$setting" in
			all)
				for i in mode media source root_uuid root_format home_uuid home_format \
					hostname root_pwd user_login user_pwd bootloader liveboot webboot winboot; do
					printf "%-15s: %s\n" "$i" "$(get_value $i)"
				done
				;;
			settings)
				get_settings
				;;
			*)
				option_error "$1" "$SETTINGS"
				;;
		esac
	fi
}

# set command
change()
{
	local setting="$1" value="$2" install_file="$3"
	# validate setting
	if ! printf "%s" "$setting" | \
			egrep -q "$(regex "$SETTINGS")"; then
		_ "Error: '%s' unknown setting." "$setting" 1>&2
		exit 1
	fi
	# and file
	[ -z "$install_file" ] && install_file="$DEFAULT_INSTALL_FILE"
	# write changes to file
	if [ -w "$install_file" ]; then
		printf "%s" "$CONTENTS" | \
			/bin/busybox awk -v setting="$setting" -v value="$value" '
		BEGIN{
			set=0
		}
		{
			if (match($0,"^" toupper(setting) "=")){
				printf toupper(setting) "=\"" value "\"\n"
				set++
			}
			else
				printf $0 "\n"
		}
		END{
			if (! set)
				printf toupper(setting) "=\"" value "\"\n"
		}' > "$install_file"
		# check new value
		read_file "$install_file"
		check "$setting"
	else
		_ 'Error: Unable to write to install file.' 1>&2
		exit 2
	fi
}

#
load_settings()
{
	MODE="$(get mode)"
	local settings="$(get settings)"
	MEDIA="$(get media)"
	echo "source" | egrep -q "$(regex "$settings")" \
		&& SOURCE="$(get source)" \
		|| unset SOURCE
	ROOT_UUID="$(get root_uuid)"
	echo "root_format" | egrep -q "$(regex "$settings")" \
		&& ROOT_FORMAT="$(get root_format)" \
		|| unset ROOT_FORMAT
	echo "home_uuid" | egrep -q "$(regex "$settings")" \
		&& HOME_UUID="$(get home_uuid)" \
		|| unset HOME_UUID
	echo "home_format" | egrep -q "$(regex "$settings")" \
		&& HOME_FORMAT="$(get home_format)" \
		|| unset HOME_FORMAT
	echo "hostname" | egrep -q "$(regex "$settings")" \
		&& HOSTNAME="$(get hostname)" \
		|| unset HOSTNAME
	echo "root_pwd" | egrep -q "$(regex "$settings")" \
		&& ROOT_PWD="$(get root_pwd)" \
		|| unset ROOT_PWD
	echo "user_login" | egrep -q "$(regex "$settings")" \
		&& USER_LOGIN="$(get user_login)" \
		|| unset USER_LOGIN
	echo "user_pwd" | egrep -q "$(regex "$settings")" \
		&& USER_PWD="$(get user_pwd)" \
		|| unset USER_PWD
	echo "bootloader" | egrep -q "$(regex "$settings")" \
		&& BOOTLOADER="$(get bootloader)" \
		|| unset BOOTLOADER
	echo "liveboot" | egrep -q "$(regex "$settings")" \
		&& LIVEBOOT="$(get liveboot)" \
		|| unset LIVEBOOT
	echo "webboot" | egrep -q "$(regex "$settings")" \
		&& WEBBOOT="$(get webboot)" \
		|| unset WEBBOOT
	echo "winboot" | egrep -q "$(regex "$settings")" \
		&& WINBOOT="$(get winboot)" \
		|| unset WINBOOT
}







# clean command
clean()
{
	# rm LOG
	[ -r "$LOG" ] && rm -f "$LOG"
	# rm temp files
	rm -rf /tmp/tazinst
	# rm install file
	local install_file="$1"
	[ -z "$install_file" ] && install_file="$DEFAULT_INSTALL_FILE"
	_ 'Deleting install file: %s' "$install_file"
	if ! [ -w "$install_file" ]; then
		_ 'Error: Unable to delete install file.' 1>&2
		exit 2
	else
		rm -f "$install_file"
	fi
}

#-----------
# 1.2 check
#-----------

# exit if user is not root.
check_root()
{
	if [ $(id -u) -ne 0 ]; then
		_ "You must be the root user (system administrator) \
to install SliTaz, please use 'su' to get a root SHell and restart \
installation." 1>&2
		exit 1
	fi
}

# exit if another instance of tazinst is running
check_instance()
{
	if [ -e "$LOCK" ]; then
		_ 'Another instance of tazinst is running.' 1>&2
		exit 7
	else
		printf "%s" "$$" > $LOCK
	fi
}

# exit if the setting is not in a list of keywords
check_key()
{
	local setting="$1" keylist="$2" keyword="$(get $1)" msg
	if ! printf "%s" "$keyword" | \
			egrep -q "$(regex "$keylist")"; then
		msg="$setting=$keyword
$(_ "Error: '%s' Invalid keyword." "$keyword")
$(_ 'Select one of these options: %s.' "$keylist")
$(_ 'For more information, see tazinst Manual.')"
		printf "%s\n" "$msg" 1>&2
		exit 1
	fi
}

# exit if the partition does not exist
check_uuid()
{
	local setting="$1" value="$(get $1)" found=0 partition msg
	for partition in $(list_uuid); do
		[ "$partition" = "$value" ] && found="$(($found + 1))"
	done
	if [ "$found" != "1" ]; then
		msg="$(gettext "$setting")=$value
$(_ 'Error: Partition not found.')
$(_ "To see available partitions, run '%s'." 'tazinst list uuid')"
		printf "%s\n" "$msg" 1>&2
		exit 1
	fi
}

# exit if the source does not exist
check_source()
{
	local media="$(get media)" source="$(get source)"
	case $media in
		usb)
			check_uuid source ;;
		iso)
			if [ ! -r "$source" ]; then
				_ 'Error: Source file not found.' 1>&2
				exit 1
			fi ;;
		web)
			local valid=0
			# check full url (http://...)
			local regexp="^(https?|ftp):\/\/([a-z0-9\-]+\.)?[a-z0-9\-]+\.\
[a-z0-9]{2,4}(\.[a-z0-9]{2,4})?(\/.*)?iso$"
			printf "%s" "$source" | \
				egrep -q "$regexp" && valid=$(($valid+1))
			# check iso names (stable cooking...)
			regexp="$(regex "$(list web)")"
			printf "%s" "$source" | \
				egrep -q "$regexp" && valid=$(($valid+1))
			if [ "$valid" -le "0" ]; then
				_ 'Error: invalid URL.' 1>&2
				exit 1
			fi
	esac
}

# exit if a partition is selected more than once
check_uuid_mix()
{
	local list all nodup
	list="$(get root_uuid) $(get source) $(get home_uuid) $(get winboot)"
	all="$(printf "%s" "$list" | wc -w)"
	nodup="$(printf "%s" "$list" | /bin/busybox awk 'BEGIN{RS=" "}{print $0}' \
		| sort | uniq | wc -w)"
	if [ "$all" != "$nodup" ]; then
		_ 'Error: multiple assignations for a disk.' 1>&2
		exit 1
	fi
}


# exit if a password is invalid
check_password()
{
	local pass="$(get "$1")"
	local invalid="^[A-Za-z0-9!@#$%^&*()_]{0,40}$"
	local errcode=0
	# too long
	if [ "${#pass}" -ge 40 ]; then
		_ 'Error: password too long.' 1>&2
		exit 1
	fi
	# bad chars
	if ! (printf "%s" "$pass" | egrep -q "$invalid"); then
		_ 'Error: Unallowed characters in password.' 1>&2

		exit 1
	fi
	# short pwd
	[ "${#pass}" -le 4 ] && errcode=128
	# empty pwd
	[ -z "$pass" ] && errcode=129
	case "$errcode" in
		128)
			_ 'Warning: short password!' 1>&2 ;;
		129)
			_ 'Warning: no password!' 1>&2 ;;
	esac
	return "$errcode"
}

# exit if a name is invalid
check_name()
{
	local name="$1" value="$(get "$1")" msg
	msg="$name=$value"
	if [ "${#value}" -lt 2 ]; then
		_ '%s Error: Too short.' "$msg" 1>&2
		exit 1
	fi
	if [ "${#value}" -gt 32 ]; then
		_ '%s Error: Too long.' "$msg" 1>&2
		exit 1
	fi
	if printf "%s" "$value" | \
			grep -q "[[:space:]\&\"\'\(\)\|\*\\#\`\+\:/;<>]"; then
		_ '%s Error: Invalid chars.' "$msg" 1>&2
		exit 1
	fi
}

# check bootloader + winboot
check_boot_mix()
{
	local bootloader=$(get bootloader)
	local var name
	[ -z "$bootloader" ] && while read var name; do
		local value
		eval value=\$$var
		[ -z "$value" ] && continue
		_ 'Error: %s set with no bootloader.' "$name" 1>&2
		exit 1
	done <<EOT
liveboot	Rescue/live boot
webbot		Web boot
winboot		Dualboot
EOT
}

# exit if partition table is not in list
check_table()
{
	local pt_list="gpt msdos"
	# get root uuid
	local uuid="$(get root_uuid)"
	if [ "$(blkid | grep -c "$uuid")" = "1" ]; then
		if ! printf "%s" "$(p_table $uuid)" | \
				egrep -q "$(regex "$pt_list")"; then
			_ 'Error: Unsupported Partition Table.' 1>&2
			exit 1
		fi
	else

		_ 'Error: No disk selected, cannot install any bootloader.' 1>&2
		exit 1
	fi
}


# check all settings()
check_all()
{
	# check only settings we need
	for key in $(get settings); do
	case "$key" in
		mode)
			printf "%-15s: " "$key"
			check_key $key "$(key "$LST_MODE")" && echo "ok" ;;
		media)
			printf "%-15s: " "$key"
			check_key $key "$(key "$LST_MEDIA")" && echo "ok" ;;
		source)
			printf "%-15s: " "$key"
			check_source && echo "ok" ;;
		root_uuid)
			printf "%-15s: " "$key"
			check_uuid $key && echo "ok" ;;
		root_format|home_format)
			printf "%-15s: " "$key"
			[ -n "$(get $key)" ] && \
			{ check_key $key "$(key "$LST_FORMAT")" && echo "ok"; } \
				|| echo "ok" ;;
		home_uuid)
			printf "%-15s: " "$key"
			[ -n "$(get $key)" ] && \
				{ check_uuid $key && check_uuid_mix && echo "ok"; } \
				|| echo "ok" ;;
		hostname|user_login)
			printf "%-15s: " "$key"
			check_name $key && echo "ok" ;;
		root_pwd|user_pwd)
			printf "%-15s: " "${key/pwd/password}"
			check_password $key && echo "ok" ;;
		bootloader)
			printf "%-15s: " "$key"
			[ -n "$(get $key)" ] \
				&& { check_key $key "$(list_bootloader)" \
				&& check_table && echo "ok" ; } \
				|| echo "ok" ;;
		liveboot|webboot)
			printf "%-15s: " "$key"
			[ -n "$(get $key)" ] && { "$(get $key)" == "auto" ] \
				&& check_boot_mix && echo "ok"; } \
				|| echo "ok" ;;
		winboot)
			printf "%-15s: " "$key"
			[ -n "$(get $key)" ] && [ "$(get $key)" != "auto" ] \
				&& { check_uuid $key && check_boot_mix && echo "ok"; } \
				|| echo "ok" ;;
	esac
	done
}

# check command
check()
{
	local setting="$1"
	case "$setting" in
		mode)
			check_key $setting "$(key "$LST_MODE")" ;;
		media)
			check_key $setting "$(key "$LST_MEDIA")" ;;
		source)
			check_source ;;
		root_uuid)
			check_uuid $setting
			check_uuid_mix ;;
		home_uuid)
			[ -z "$(get $setting)" ] || check_uuid $setting
			check_uuid_mix ;;
		root_format)
			[ -z "$(get $setting)" ] \
				|| check_key $setting "$(key "$LST_FORMAT")" ;;
		home_format)
			[ -z "$(get $setting)" ] \
				|| check_key $setting "$(key "$LST_FORMAT")" ;;
		hostname|user_login)
			check_name $setting ;;
		root_pwd|user_pwd)
			check_password $setting ;;
		bootloader)
			[ -z "$(get $setting)" ] \
				|| (check_key $setting "$(list_bootloader)" \
				&& check_table ; ) ;;
		liveboot|webboot)
			([ -z "$(get $setting)" ] || [ "$(get $setting)" = "auto" ]) \
				&& check_boot_mix ;;
		winboot)
			([ -z "$(get $setting)" ] || [ "$(get $setting)" = "auto" ]) \
				|| check_uuid $setting && check_boot_mix ;;
		""|all)
			check_all ;;
		*)
			option_error "$setting" "$SETTINGS" ;;
	esac
}

#----------
# 1.3 help
#----------

help_source()
{

	_ 'The Source setting depends on the type of media:'
	printf "%-12s%s\n" "cdrom" "$(help cdrom)"
	printf "%-12s%s\n" "usb" "$(help usb)"
	printf "%-12s%s\n" "iso" "$(help iso)"
	printf "%-12s%s" "web" "$(_ 'Name or URL of the image on the web.')"
	printf "%s\n" "$(_ 'Type: %s' 'tazinst help web')"
}

help_all()
{
	_ 'List of settings:'
	optlist "\
mode		$(_ 'Mode of install')
media		$(_ 'Media containing the SliTaz source files')
source		$(_ 'Source file containing SliTaz')
root_uuid	$(_ 'The name of the target partition')
root_format	$(_ 'Format of the target partition')
home_uuid	$(_ 'Separate home partition')
home_format	$(_ 'Format of the root partition')
hostname	$(_ 'Name of the system')
root_pwd	$(_ 'Superuser password')
user_login	$(_ 'First user name')
user_pwd	$(_ 'First user password')
bootloader	$(_ 'Install a bootloader')
liveboot	$(_ 'Install a live/rescue boot (with root password)')
webboot		$(_ 'Install a web boot (with root password)')
winboot		$(_ 'Partition to dualboot Windows from')
"
}

# help command
help()
{
	local setting="$1"
	case "$setting" in
	mode)
		printf "%s" "$LST_MODE" | \
			/bin/busybox awk -F: '/..*/{printf "%-12s%s\n", $1, $2}' ;;
	media)
		printf "%s" "$LST_MEDIA" | \
			/bin/busybox awk -F: '/..*/{printf "%-12s%s\n", $1, $2}'	;;
	source)
		help_source ;;
	cdrom)
		_ 'CD. Automatically set' ;;
	usb)

		_ 'USB partition. For a list, type: %s' 'tazinst list usb' ;;
	iso)

		_ 'ISO file name. For a list, type: %s' 'tazinst list iso' ;;
	web)
		printf "%s" "$LST_WEB" | \
			/bin/busybox awk -F: '/..*/{printf "%-12s%s\n", $1, $3}' ;;
	root_format)
	 	printf "%s" "$LST_FORMAT" | \
			/bin/busybox awk -F: '/..*/{printf "%-12s%s\n", $1, $2}' ;;
	home_uuid|root_uuid)
		/sbin/blkid -s TYPE -s LABEL | sort ;;
	home_format)
		printf "%s" "$LST_FORMAT" | \
				/bin/busybox awk -F: '/..*/{printf "%-12s%s\n", $1, $2}' ;;
	hostname)
		_ 'Name of the system' ;;
	root_pwd)
		_ 'Superuser password' ;;
	user_login)
		_ 'First user name' ;;
	user_pwd)
		_ 'First user password' ;;
	bootloader)
		printf "%s" "$LST_BOOTLOADER" | \
			/bin/busybox awk -F: '/..*/{printf "%-12s%s\n",$1,$2}' ;;
	liveboot|webboot)
		_ "Empty string, or 'auto'" ;;
	winboot)
		_ "Partition containing Windows, or 'auto'" ;;
	""|all)
		help_all ;;
	*)
		option_error "$setting" "$SETTINGS" ;;
	esac
}

#---------
# 2. list
#---------

list_media()
{
	local key media
	for key in $(key "$LST_MEDIA") ; do
		case "$key" in
			cdrom)
				[ -e "/dev/cdrom" ] && media="cdrom" ;;
			usb)
				[ -e "/sys/bus/usb" ] && media="$media usb" ;;
			web)
				[ "$(ifconfig -a | grep -c Link)" -gt 2 ] \
					&& media="$media web" ;;
			*)
				media="$media $key"
		esac
	done
	printf "%s\n" "$media" | sed 's/^\s//'
}

list_usb()
{
	# List plugged USB disks
	if [ -d /proc/scsi/usb-storage ]; then
		for DEV in /sys/block/sd* ; do
			if readlink $DEV | grep -q usb; then
				DEV=$(basename $DEV)
				if [ -d /sys/block/${DEV}/${DEV}1 ]; then
					blkid /dev/$DEV* | tr ' ' '\n' | /bin/busybox awk '
						/^\/dev\// {
							DEV=$1
							gsub(/:/,"",DEV)
						}
						/^UUID/ {
							UUID=$1
							gsub(/"/,"",UUID)
							printf "%s %s\n",UUID,DEV}'
				fi
			fi
		done
	fi
}

list_iso()
{
	for i in /root/*.iso /home/*/*.iso /home/*/*/*.iso ; do
		printf "%s" $i | grep -v "*"
	done
}

list_format()
{
	local fs
	type mkfs.btrfs   > /dev/null && fs="btrfs"
	type mkfs.ext2    > /dev/null && fs="$fs ext2"
	type mkfs.ext3    > /dev/null && fs="$fs ext3"
	type mkfs.ext4    > /dev/null && fs="$fs ext4"
	type mkfs.jfs     > /dev/null && fs="$fs jfs"
	type mkfs.minix   > /dev/null && fs="$fs minix"
	type mkfs.reiser4 > /dev/null && fs="$fs reiser4"
	type mkfs.xfs     > /dev/null && fs="$fs xfs"
	printf "%s" "$fs" | sed 's/^\s//'
}

# list partitions
list_uuid()
{
	# list all drives but cdroms
	/sbin/blkid -o export| /bin/busybox awk '
		BEGIN{FS="="}
		/DEVNAME/{dev=$2, type="unknown", label=""}
		/LABEL/{label=$2" "}
		/TYPE/{type=$2}
		/PARTUUID/{if (type != "iso9660"){
			printf "%s %s(%s)\n", dev, label, type}}' | sort -k 2
}

list_partition_table()
{
	/usr/sbin/parted -lms 2>&1 | \
		/bin/busybox awk -F: '/^\/dev\//{printf "%s: %s\n", $1,$6}'
}

list_web()
{
	local key="$1"
	# print url of a given iso
	if printf "%s" "$LST_WEB" | egrep -q "^$key:"; then
		printf "%s" "$LST_WEB" | egrep "^$key:" | \
			/bin/busybox awk -F: '{print $2}'
	fi
	# print all key
	if [ -z "$key" ]; then
		key "$LST_WEB"
	fi
}

list_bootloader()
{
	local btlr
	type grub-install > /dev/null && btlr=" grub"
	type syslinux > /dev/null && btlr="$btlr syslinux"
	[ -n "$btlr" ] && printf "%s\n" "auto$btlr"
}

# list Windows partitions
list_winboot()
{
	/usr/sbin/parted -lms 2>&1 | sed '/zram/,$d' | /bin/busybox awk '
	BEGIN{
		FS=":"
		disknum=-1
		found=0
		winboot=""
		printf "auto"
	}
	{
		# Count disks
		if (match($1,"^/dev")){
			disknum++
			part=0
			disk=$1
			dev=substr($1,6)
			# get removable status
			file="/sys/block/"dev"/removable"
			"cat " file | getline removable
			close("cat ")
		}
		# Count partitions
		if (match($1,"[0-9][0-9]?")){
			# List fixed drives only
			if (removable==0){
				part++
				# Read partition Id
		 		if (match($7,"boot")){
					fs=$5
					# Detect Windows Partition Type: ntfs vfat
					WPT="ntfs|vfat"
					if (fs ~ WPT){
						found++
						# record 1st Windows partition found
						if (found==1){
							printf(" %s%d",disk,part)
						}
					}
				}
			}
		}
	}
	END{printf "\n"}'
}

# list command
list()
{
	local ressource="$1"
	case "$ressource" in
		mode)
			printf "%s\n" "$(key "$LST_MODE")" ;;
		media)
			list_media ;;
		iso)
			list_iso ;;
		usb)
			list_usb ;;
		web)
			list_web "$2" ;;
		uuid)
			list_uuid ;;
		format)
			list_format ;;
		bootloader)
			list_bootloader ;;
		winboot)
			list_winboot ;;
		partition_table)
			list_partition_table ;;
		""|all)
			printf "* mode:\n%s\n\n" "$(key "$LST_MODE")"
			printf "* media:\n%s\n\n" "$(list_media)"
			printf "* usb:\n%s\n\n" "$(list usb)"
			printf "* iso:\n%s\n\n" "$(list_iso)"
			printf "* web:\n%s\n\n" "$(list_web)"
			printf "* uuid:\n%s\n\n" "$(list_uuid)"
			printf "* format:\n%s\n\n" "$(list_format)"
			printf "* bootloader:\n%s\n\n" "$(list_bootloader)"
			printf "* partition_table:\n%s\n\n" "$(list_partition_table)"
			printf "* winboot:\n%s\n" "$(list_winboot)"
			;;
		*)
			local options="mode media usb iso web uuid format bootloader \
partition_table winboot"
			option_error "$1" "$options" ;;
	esac
}

#----------
# 3. tools
#----------

# list indexes from a list
key()
{
	printf "%s" "$1" | /bin/busybox awk -F: 'BEGIN{
		other=-1
		}
		!/^#|^$/{
			if(other){
				printf "%s", $1
				other++}
			else
				printf " %s", $1
		}
		END{printf "\n"}'
}

# convert a list of words to a regex
regex()
{
	printf "%s" "^$1$" | sed s'/ /$|^/g'
}

# print dev from uuid
uuid2dev()
{
	local uuid="$1"
	if [ "${uuid%=*}" = "UUID" ]; then
		uuid="$(blkid | sed '/ UUID="'${uuid#*=}'"/!d;s|:.*||')"
	fi
	printf "%s" "$uuid"
}

# print disk from uuid
uuid2disk()
{
	printf "%s" "$(uuid2dev $1 | /bin/busybox sed 's|p*[0-9]*$||')"
}

dev2uuid()
{
	local uuid="$1"
	if printf "%s" "$uuid" | grep -q dev; then
		printf "UUID=%s" "$(/sbin/blkid -p -i -o udev "$uuid" \
			| grep ID_FS_UUID= | cut -d '=' -f2)"
	else
		printf "%s" "$uuid"
	fi
}

# print partition scheme from uuid
p_table()
{
	local uuid="$1" device
	device="$(uuid2disk $uuid)"
	printf "%s" "$(/usr/sbin/parted -lms 2>&1 | grep "$device" | \
		cut -d':' -f6)"
}

# print filesystem from uuid
filesys()
{
	local uuid="$1"
	local fs="$(/sbin/blkid -s UUID -s TYPE | \
		grep "$(uuid2dev $uuid)" | cut -d' ' -f3)"
	fs="${fs#TYPE=\"}"
	fs="${fs%\"}"
	printf "%s" "$fs"
}

# return removable status from uuid
is_removable()
{
	local removable=1
	local disk="$(uuid2disk $1 | sed 's|/dev/||')"
	if [ "$disk" ]; then
		[ "$(cat /sys/block/"$disk"/removable 2> /dev/null)" -gt "0" ] \
			&& removable=0
	fi
	return "$removable"
}

randomize_mirrors()
{
	local list_mirrors
	if [ -r "$MIRRORS" ]; then
		# randomize list of mirrors
		list_mirrors="$(cat $MIRRORS | \
			/bin/busybox awk 'BEGIN {srand()}{
				printf "%05.0f %s \n",rand()*99999, $0;
			}' | sort -n | sed 's/^[0-9]* //' )"
	else
		log "$(_ 'No mirror list found, run %s.' 'tazpkg recharge')"
		list_mirrors="http://mirror.slitaz.org/"
	fi
	MIRRORS="$list_mirrors"
}

# download a file
dnl()
{
	local file="$1" mirror transfer=0 oldfile
	mkdir -p /tmp/tazinst
	for mirror in $MIRRORS; do
		log "$(_ 'Downloading: %s' "$mirror$file")"
		oldfile="$(printf "%s" $file | \
			/bin/busybox awk 'BEGIN{RS="/"}{text=$1}END{print text}')"
		[ -e "/tmp/tazinst/$oldfile" ] && rm -f "/tmp/tazinst/$oldfile"
		/bin/busybox wget $mirror$file -P /tmp/tazinst && transfer=1 && break
	done
	if [ "$transfer" -gt "0" ];then
		log "$(_ 'Download completed.')"
	else
		error 5 "(_ '%s: Download failed.' "$file")"
	fi
}

# install pkg in current system
need_package()
{
	local pkg="$1"
	if [ ! -d /var/lib/tazpkg/installed/$pkg ]; then
		log "$(_ 'Installing package %s to the current system.' "$pkg")"
		[ -z "$RECHARGE" ] && RECHARGE=true && /usr/bin/tazpkg recharge
		/usr/bin/tazpkg get-install "$pkg" || error 5 "$(_ 'Cannot install %s.' "$pkg")"
	fi
}

# install pkg in target system
add_pkg()
{
	local pkg="$1"
	log "$(_ 'Adding package %s to the target system...' "$pkg")"
	[ -z "$RECHARGE" ] && RECHARGE=true && /usr/bin/tazpkg recharge
	/usr/bin/tazpkg get "$pkg" >> "$LOG"  2>> "$LOG"
	yes "" | /usr/bin/tazpkg install $pkg.tazpkg \
		--root=$TARGET_ROOT >> "$LOG"  2>> "$LOG"
	rm -f $pkg.tazpkg
}

#---------
# 3.1 log
#---------

# start log
log_open()
{
	_ '=== Tazinst: started on %s ===' "$(date "+%x %X")" > "$LOG"
	LOGGING="true"
}

# print and log a comment
log(){
	# for front-ends, sleep 1 if $1 is num
	printf "%s" "$1" | awk '{
		num=$1+0
		if(num>0 && num<=100)
			exit 0
		else
			exit 1
		}' && sleep 1
	# log
	printf "%s\n" "$1"
	[ -n "$LOGGING" ] && printf "%s\n" "$1" >> $LOG
}

#--------------------
# 3.2 error handling
#--------------------

# print an error msg & exit
error()
{
	local error="$1" msg="$2" cancel="$(_ 'Process not completed')"
	_ 'Error: %s' "$msg" >&2; echo "$cancel" >&2
	if [ -n "$LOGGING" ]; then
		# 1st pattern
		echo "-x-x- " >> "$LOG"
		_ 'Error: %s' "$msg" >> "$LOG"; echo "$cancel" >> "$LOG"
		# 2nd pattern
		echo "x-x-x " >> "$LOG"
		_ '=== Tazinst error on %s ===' "$(date "+%x %X")" >> "$LOG"
	fi
	unset LOGGING
	umount_devices
	exit "$error"
}

error8()
{
	error 8 "$(_ 'Internal error')"
}

error9()
{
	error 9 "$(_ 'Cancelled by user')"
}

#------------------
# 4. disks section
#------------------

#------------
# 4.1 source
#------------

# liveCD
mount_cdrom()
{
	# set device name
	local drive="$(grep "drive name" < \
		/proc/sys/dev/cdrom/info | cut -f 3)"
	[ -n "$drive" ] || drive=cdrom
	local cdrom=/dev/$drive
	# mount cdrom
	if mount -t iso9660 "$cdrom" "$SOURCE_ROOT" 2>>"$LOG"; then
		log "$(_ 'Using files from %s.' "$cdrom")"
	else
		error 3 "$(_ '%s: Mount failed.' "$cdrom")"
	fi
}

# liveUSB
mount_usb()
{
	# /home is on LiveUSB
	if [ -d /home/boot ]; then
		log "$(_ 'Using files from USB device...')"
		ln -s /home/boot $SOURCE_ROOT/boot
	else
		# mount LiveUSB
		if mount "$SOURCE" "$SOURCE_ROOT" 2>>"$LOG"; then
			log "$(_ 'Using files from USB device %s.' "$SOURCE")"
		else
			error 3 "$(_ '%s: Failed to mount USB device.' "$SOURCE")"
		fi
	fi
}

# ISO file on HDD
mount_iso()
{
	# check integrity
	local md5file=$(printf "%s" $SOURCE | sed 's/.iso$/.md5/')
	if [ -r "$md5file" ]; then
		local md5ref="$(cut -d' ' -f1 < "$md5file")"
		local md5calc="$(md5sum $SOURCE | cut -d' ' -f1)"
		if [ ! "$md5calc" = "$md5ref" ]; then
			log "md5sum iso=$md5ref md5sum tazinst=$md5calc"
			error 3 "$(_ 'md5sum error, file corrupted.')"
		fi
	else
		log "$(_ '%s: md5 file not found, cannot check integrity.' "$SOURCE")"
	fi
	# mount ISO
	if mount -o loop -t iso9660 "$SOURCE" \
			"$SOURCE_ROOT" 2>>"$LOG"; then
		log "$(_ 'Using files from ISO %s.' "$SOURCE")"
	else
		error 3 "$(_ '%s: Failed to mount ISO.' "$SOURCE")"
	fi
}

# ISO file on the web
mount_web()
{
	if (printf "%s" "$SOURCE" | egrep -q "$(regex "$(list web)")"); then
		SOURCE="$(list web $SOURCE)"
	fi
	dnl "$SOURCE"
	local md5file="$(printf "%s" $SOURCE | sed 's/.iso$/.md5/')"
	dnl "$md5file"
	local webiso="$(printf "%s" $SOURCE | \
		/bin/busybox awk 'BEGIN{RS="/"}{out=$1}	END{printf "%s",out}')"
	SOURCE="/tmp/tazinst/$webiso"
	mount_iso
}

# set up source and check SliTaz' content
mount_source()
{
	log "$(_ 'Creating mount point: %s...' "$SOURCE_ROOT")"
	mkdir -p "$SOURCE_ROOT"
	case "$MEDIA" in
	cdrom)
		mount_cdrom ;;
	usb)
		mount_usb ;;
	iso)
		mount_iso ;;
	web)
		mount_web ;;
	*)
		error8 ;;
	esac

	#  exit if no rootfs.gz found.
	log "$(_ 'Checking installation media...')"
	if [ ! -f $SOURCE_ROOT/boot/rootfs.gz -a \
		 ! -f $SOURCE_ROOT/boot/rootfs1.gz ]; then
		error 3 "$(_ 'Invalid source')"
	else
		log "$(_ 'Installation media checked ok')"
	fi
}

#------------
# 4.2 target
#------------

# format a partition
format()
{
	local uuid="$1" fmt="$2" dest="$3" dev format
	log "$(_ 'Format %s (%s)' "$uuid" "$fmt")"
	format="mkfs.$fmt"
	if (printf "%s" "$uuid" | grep -q "UUID="); then
		# case UUID=
		dev="$(uuid2dev $uuid)"
		"$format" "$dev" >>"$LOG" 2>>"$LOG" || error 4 "$(_ 'Formatting has failed')"
		# uuid has changed
		case "$dest" in
		root_uuid)
			ROOT_UUID="$(/sbin/blkid -p "$dev" -o export | \
				grep ^UUID=)" ;;
		home_uuid)
			HOME_UUID="$(/sbin/blkid -p "$dev" -o export | \
				grep ^UUID=)" ;;
		esac
	else
		# case /dev/xxxx
		"$format" $uuid >>"$LOG" 2>>"$LOG" || error 4 "$(_ 'Formatting has failed')"
	fi
}

# prepare partitions
prepare_uuid()
{
	log "$(_ 'Preparing target partition...')"
	local uuid
	# target may be in use
	if mount | grep -q "$ROOT_UUID" ;then
		log "$(_ 'Partition is already mounted, unmounting.')"
		umount "$ROOT_UUID"
	fi
	mount | grep -q "$ROOT_UUID" && \
		error 4 "$ROOT_UUID: $(_ 'Partition is already in use.')"
	# Mount point can be already used.
	mount | grep -q "$TARGET_ROOT" && \
		umount "$TARGET_ROOT" 2>>"$LOG"

	# Formatting root partition
	case "$ROOT_FORMAT" in
		"")
			log "$(_ '%s: The partition will be cleaned...' "$ROOT_UUID")" ;;
		*)
			format "$ROOT_UUID" "$ROOT_FORMAT" root_uuid
	esac

	# Formatting /home
	if [ -n "$HOME_UUID" ]; then
		case "$HOME_FORMAT" in
			"")
				log "$(_ '%s: The partition will be kept...' "$HOME_UUID")" ;;
			*)
				format "$HOME_UUID" "$HOME_FORMAT" home_uuid
		esac
	fi

	log "$(_ 'Creating mount point: %s...' "$TARGET_ROOT")"
	mkdir -p "$TARGET_ROOT" >> "$LOG" || error8
	# Mount target.
	local mount_fs="$ROOT_FORMAT"
	[ -z "$mount_fs" ] && mount_fs="$(filesys $ROOT_UUID)"
	mount -t "$mount_fs" "$ROOT_UUID" \
			"$TARGET_ROOT" >>"$LOG" 2>>"$LOG"
	if [ $(mount | \
			grep -c "mnt/target") = "0" ]; then
		error 4 "$(_ '%s: Unable to mount partition' "$ROOT_UUID")"
	fi
}


#------------
# 4.3 umount
#------------

# copy log file, umount target and eject cdrom.
umount_devices()
{
	# umount target
	if mount | grep -q "$TARGET_ROOT"; then
		log "$(_ 'Unmounting target partition: %s' "$ROOT_UUID")"
		umount -l "$TARGET_ROOT" 2>>$LOG
	fi

	# umount source
	if mount | grep -q "$SOURCE_ROOT"; then
		log "$(_ 'Unmounting: %s' "$SOURCE_ROOT")"
		umount -l "$SOURCE_ROOT"
	fi
	if [ -h $SOURCE_ROOT/boot ]; then
		log "$(_ 'Unlinking: %s' "$SOURCE_ROOT")"
		rm -f $SOURCE_ROOT/boot
	fi

	# eject cd
	if [ "$SOURCE" = "cdrom" ]; then
		_ 'Ejecting CD-ROM...'
		eject
	fi
	# remove lock file
	rm -f "$LOCK"
}

end_of_install()
{
	log "$(_ 'Process completed. You can now restart (reboot) from your SliTaz GNU/Linux system.' |\
		fold -s)"
	_ '=== Tazinst ended on %s ===' "$(date "+%x %X")" >> "$LOG"
	unset LOGGING
	# saving log
	log "$(_ 'Copying log to %s' '/var/log/tazinst.log')"
	cp -a "$LOG" $TARGET_ROOT/var/log
	umount_devices
}


#---------------
# 5. bootloader
#---------------

#------------
# 5.1 common
#------------

# selection
bootloader()
{
	if [ "$BOOTLOADER" = "auto" ]; then
		# use syslinux, but if p_table=msdos take grub (if available)
		unset BOOTLOADER
		printf "%s" "$(list_bootloader)" | \
			grep -q "syslinux" && BOOTLOADER=syslinux
		if [ "$(p_table $ROOT_UUID)" = "msdos" ]; then
			printf "%s" "$(list_bootloader)" | \
				grep -q " grub" && BOOTLOADER=grub
		fi
	fi
	case "$BOOTLOADER" in
		grub)
			grub_config
			grub_install ;;
		syslinux)
			syslinux_config
			syslinux_install ;;
		*)
			syslinux_config
			log "$(_ 'No bootloader to install.')" ;;
	esac
	[ -d /sys/firmware/efi ] && efiboot_install
}

# print disk num
disknum()
{
	local disk="$(uuid2dev $1 | /bin/busybox sed 's|p*[0-9]*$||')"
	/usr/sbin/parted -lms 2>&1 | grep "^/dev" | \
		/bin/busybox awk -v PART="$disk:" '{if (match($0,PART)) print NR-1}'
}

# print partition num
partnum()
{
	printf "%s" "$(($(uuid2dev $1 | /bin/busybox sed 's|.*[^0-9]||')-1))"
}

# print root device
rootdev()
{
	local partition="$1"
	case "$(p_table $partition)" in
		msdos)
			# print device
			printf "%s" "$(uuid2dev $partition)" ;;
		gpt)
			# print PARTUUID (different from UUID)
			printf "%s" "PARTUUID="
			/sbin/blkid -p -i -o udev $(uuid2dev $partition) \
				| grep ENTRY_UUID | cut -d '=' -f2 ;;
	esac
}

# add rootdelay for removable devices
rootdelay()
{
	is_removable "$ROOT_UUID" && printf "%s" " rootdelay=9"
}

# print winboot uuid
winboot_uuid()
{
	if [ "$WINBOOT" = "auto" ]; then
		# get the first uuid if any
		[ $(list_winboot | wc -w) -gt 1 ] \
			&& printf "%s" "$(list_winboot | cut -d' ' -f2)"
	else
		printf "%s" "$WINBOOT"
	fi
}


#-----------
# 5.1 grub1
#-----------

grub_password_command()
{
	grub --batch 2> /dev/null <<EOT | sed '/^Encrypted/!d;s|^Encrypted: |password --md5 |'
md5crypt
$ROOT_PWD
EOT
}

# create grub legacy conf
grub_config()
{
	# backup an existing conf
	[ -e "$TARGET_ROOT/boot/grub/menu.lst" ] && \
		cp $TARGET_ROOT/boot/grub/menu.lst \
			$TARGET_ROOT/boot/grub/menu.bak
	# create the target GRUB configuration.
	mkdir -p $TARGET_ROOT/boot/grub || error8
	cat > $TARGET_ROOT/boot/grub/menu.lst <<_EOF_
# /boot/grub/menu.lst: GRUB boot loader configuration.
#

# By default, boot the first entry.
default 0

# Boot automatically after 8 secs.
timeout 8

# Graphical splash image.
splashimage=/boot/grub/splash.xpm.gz

# Root password is required for edition
$(grub_password_command)

# Change the colors.
#color yellow/brown light-green/black

# For booting SliTaz from : $ROOT_UUID
#
title SliTaz GNU/Linux $(cat $TARGET_ROOT/etc/slitaz-release) (Kernel $KERNEL)
root (hd$(disknum $ROOT_UUID),$(partnum $ROOT_UUID))
kernel /boot/$KERNEL root=$(rootdev $ROOT_UUID) $KERNEL_ARGS$(rootdelay)

_EOF_

	if [ -n "$LIVEBOOT" ]; then
		local rootfs_files="$(cd $TARGET_ROOT/boot/tazinst.$$ ; ls rootfs*)"
		log "$(_ 'Enabling live/rescue boot with %s' "$rootfs_files")"
		for i in $rootfs_files; do
			[ -s $TARGET_ROOT/boot/$i ] &&
				mv $TARGET_ROOT/boot/$i $TARGET_ROOT/boot/$i.bak
			mv $TARGET_ROOT/boot/tazinst.$$/$i $TARGET_ROOT/boot/
		done
		cat >> $TARGET_ROOT/boot/grub/menu.lst <<_EOF_
# For booting SliTaz Live from : $ROOT_UUID
#
title SliTaz GNU/Linux rescue $(cat $TARGET_ROOT/etc/slitaz-release) (Kernel $KERNEL)
$(grub_password_command)
root (hd$(disknum $ROOT_UUID),$(partnum $ROOT_UUID))
kernel /boot/$KERNEL $(sed 's|.*gz ||' /proc/cmdline)
initrd /boot/$ROOTFS

_EOF_
	fi

	if [ -n "$WEBBOOT" -a -n "$IPXE" ]; then
		log "$(_ 'Enabling Webboot using %s' $IPXE)"
		mv -f $TARGET_ROOT/boot/tazinst.$$/$IPXE $TARGET_ROOT/boot/
		cat >> $TARGET_ROOT/boot/grub/menu.lst <<_EOF_
# For booting SliTaz Live from the web
#
title SliTaz GNU/Linux web boot
$(grub_password_command)
root (hd$(disknum $ROOT_UUID),$(partnum $ROOT_UUID))
kernel /boot/$IPXE

_EOF_
	fi
	rm -rf $TARGET_ROOT/boot/tazinst.$$ 2> /dev/null

	local winpart="$(winboot_uuid)"
	if [ -n "$winpart" ]; then
		log "$(_ 'Enabling Windows dual-boot')"
		cat >> $TARGET_ROOT/boot/grub/menu.lst <<_EOF_
# For booting Windows :
#
title Microsoft Windows
	  rootnoverify (hd$(disknum $winpart),$(partnum $winpart))
	  chainloader +1

_EOF_
	fi

	# log
	printf "%s\n" "/boot/grub/menu.lst:" >> "$LOG"
	printf "%s\n" "--- menu.lst -------------" >> "$LOG"
	cat $TARGET_ROOT/boot/grub/menu.lst >> "$LOG"
	printf "%s\n\n" "--- menu.lst -------------" >> "$LOG"
}

# GRUB info with disk name used for grub
grub_install()
{
	# install grub
	local target_disk="$(uuid2disk $ROOT_UUID)"
	log "$(_ 'Installing GRUB on: %s' "$target_disk")"
	/usr/sbin/grub-install --version >> "$LOG" || error 5 "$(_ 'GRUB not found')"
	/usr/sbin/grub-install --no-floppy \
			--root-directory=$TARGET_ROOT $target_disk >>"$LOG" 2>>"$LOG"
	# set boot flag
	log "$(_ 'Setting the boot flag')"
	/usr/sbin/parted "$(uuid2disk $ROOT_UUID)" \
		set "$(($(partnum $ROOT_UUID)+1))" boot on 2>> "$LOG"
	# splash image
	if [ -f "$SOURCE_ROOT/boot/grub/splash.xpm.gz" ]; then
		log "$(_ 'Copying splash image')"
		mkdir -p $TARGET_ROOT/boot/grub
		cp $SOURCE_ROOT/boot/grub/splash.xpm.gz \
			$TARGET_ROOT/boot/grub
	fi
}


#--------------
# 5.2 syslinux
#--------------

# create syslinux conf
syslinux_config()
{
	local version="[$(cat $TARGET_ROOT/etc/slitaz-release)]"
	version="$version\(Kernel $KERNEL)"
	# backup an existing conf
	[ -e "$TARGET_ROOT/boot/syslinux/syslinux.cfg" ] && \
		cp 	$TARGET_ROOT/boot/syslinux/syslinux.cfg \
			$TARGET_ROOT/boot/syslinux/syslinux.bak
	# create the target syslinux configuration.
	mkdir -p $TARGET_ROOT/boot/syslinux || error8
	cat > $TARGET_ROOT/boot/syslinux/syslinux.cfg <<EOF
# use a boot menu
UI vesamenu.c32

# display the boot
PROMPT 1

# how long to pause at the boot
TIMEOUT 50

# default label
DEFAULT slitaz

# Menu settings
MENU TITLE SliTaz GNU/Linux boot menu
MENU BACKGROUND splash.jpg
MENU WIDTH 78
MENU MARGIN 6
MENU ROW 10
MENU VSHIFT 2
MENU TIMEOUTROW 18
MENU TABMSGROW 16
MENU CMDLINEROW 16

# Menu colors
MENU COLOR border       *       #00000000 #00000000 none
MENU COLOR title        *       #ffffffff #00000000 *
MENU COLOR sel          0       #ffffffff #00000000 none
MENU COLOR unsel        0       #50ffffff #00000000 none
MENU COLOR help         *       #ffffffff #00000000 *
MENU COLOR timeout_msg  37;40   #80ffffff #00000000 std
MENU COLOR timeout      1;37;40 #c0ffffff #00000000 std
MENU COLOR msg07        37;40   #90ffffff #a0000000 std
MENU COLOR tabmsg       31;40   #30ffffff #00000000 std

# Labels
LABEL slitaz
	MENU LABEL SliTaz GNU/Linux $version
	LINUX /boot/$KERNEL
	APPEND root=$(rootdev $ROOT_UUID) $KERNEL_ARGS$(rootdelay)

EOF
	if [ -n "$LIVEBOOT" ]; then
		local rootfs_files="$(cd $TARGET_ROOT/boot/tazinst.$$ ; ls rootfs*)"
		log "$(_ 'Enabling live/rescue boot with %s' "$rootfs_files")"
		for i in $rootfs_files; do
			[ -s $TARGET_ROOT/boot/$i ] &&
				mv $TARGET_ROOT/boot/$i $TARGET_ROOT/boot/$i.bak
			mv $TARGET_ROOT/boot/tazinst.$$/$i $TARGET_ROOT/boot/
		done
		cat >> $TARGET_ROOT/boot/syslinux/syslinux.cfg <<EOF
LABEL live rescue
	MENU LABEL SliTaz GNU/Linux live/rescue $version
	LINUX /boot/$KERNEL
	INITRD /boot/$ROOTFS
	APPEND root=$(rootdev $ROOT_UUID) $(sed 's|.*gz ||' /proc/cmdline)

EOF
	fi

	if [ -n "$WEBBOOT" -a -n "$IPXE" ]; then
		log "$(_ 'Enabling Webboot using %s' $IPXE)"
		mv -f $TARGET_ROOT/boot/tazinst.$$/$IPXE $TARGET_ROOT/boot/
		cat >> $TARGET_ROOT/boot/syslinux/syslinux.cfg <<EOF
LABEL web zeb
	MENU LABEL SliTaz GNU/Linux web boot
	LINUX /boot/$IPXE

EOF
	fi
	rm -rf $TARGET_ROOT/boot/tazinst.$$ 2> /dev/null

	cat >> $TARGET_ROOT/boot/syslinux/syslinux.cfg <<EOF
LABEL cmdline
	MENU LABEL Command Line
	MENU QUIT

EOF
	local winpart="$(winboot_uuid)"
	if [ -n "$winpart" ]; then
		log "$(_ 'Enabling Windows dual-boot')"
		cat >> $TARGET_ROOT/boot/syslinux/syslinux.cfg <<EOF
LABEL windows
	MENU LABEL Windows
	COM32 chain.c32
	APPEND hd$(disknum $winpart) $(($(partnum $winpart)+1))

EOF
	fi
	# log
	printf "%s\n" "/boot/syslinux/syslinux.cfg:" >> "$LOG"
	printf "%s\n" "--- syslinux.cfg -------------" >> "$LOG"
	cat $TARGET_ROOT/boot/syslinux/syslinux.cfg >> "$LOG"
	printf "%s\n\n" "--- syslinux.cfg -------------" >> "$LOG"
}

# install syslinux
syslinux_install()
{
	log "$(_ 'Installing Syslinux')"
	# needed tools
	local dir disk="$(uuid2disk $ROOT_UUID)"
	for dir in /home/boot/extlinux /home/boot/syslinux /boot/syslinux /boot/isolinux; do
		[ -d "$dir" ] && cp $dir/*.c32 $dir/*.sys "$TARGET_ROOT/boot/syslinux/"
	done
	cp /usr/share/boot/chain.c32 $TARGET_ROOT/boot/syslinux/chain.c32 2>> "$LOG"
	# install syslinux
	/bin/syslinux -version >> "$LOG" || error 5 "$(_ 'Syslinux not found')"
	/bin/extlinux --install $TARGET_ROOT/boot/syslinux >> "$LOG" 2>> "$LOG"
	case "$(p_table $ROOT_UUID)" in
		msdos)
			log "$(_ 'Setting the boot flag on')"
			/usr/sbin/parted "$disk" \
				set "$(($(partnum $ROOT_UUID)+1))" boot on 2>> "$LOG"
			[ -r /usr/share/boot/mbr.bin ] || error 5 "$(_ 'mbr.bin not found')"
			log "$(_ 'Installing MBR')"
			dd bs=440 count=1 conv=notrunc \
				if=/usr/share/boot/mbr.bin 	of=$disk >> "$LOG" ;;
		gpt)
			log "$(_ 'Setting the legacy_boot flag on')"
			# remove old boot flag
			local oldboot="$(parted $disk print \
				| grep boot | /bin/busybox awk '{print $1}')"
			/usr/sbin/parted "$disk" \
				set "$oldboot" legacy_boot off 2>> "$LOG"
			# set boot flag
			/usr/sbin/parted "$disk" \
				set "$(($(partnum $ROOT_UUID)+1))" legacy_boot on 2>> "$LOG"
			log "$(_ 'Installing GPTMBR')"
			[ -r /usr/share/boot/gptmbr.bin ] || error 5 "$(_ 'gptmbr.bin not found')"
			dd bs=440 conv=notrunc count=1 \
				if=/usr/share/boot/gptmbr.bin of=$disk >> "$LOG" ;;
	esac
	# splash image
	if [ -f "$SOURCE_ROOT/boot/isolinux/splash.jpg" ]; then
		log "$(_ 'Copying splash image')"
		cp -a $SOURCE_ROOT/boot/isolinux/splash.jpg \
			$TARGET_ROOT/boot/syslinux
	fi
}


#-------------
# 5.3 efiboot
#-------------

efiboot_install()
{
	local esp_fs_uuid mnt="/media/esp$$" efiboot="/EFI/SLITAZ"
	# We need efi stub support
	[ "$(strings $TARGET_ROOT/boot/$KERNEL | head | grep reloc)" ] || return
	# And the esp partition
	esp_fs_uuid="$(blkid | sed '/EFI system partition/!d;s|.* UUID="||;s|".*||;q')"
	[ "$esp_fs_uuid" ] || return
	# Copy SliTaz kernel and cmdline in esp partition
	mkdir $mnt &&
	mount "$(uuid2dev UUID=$esp_fs_uuid)" $mnt &&
	mkdir -p $mnt$efiboot 2> /dev/null
	if cp $TARGET_ROOT/boot/$KERNEL $mnt$efiboot/BZIMAGE.EFI; then
		log "$(_ 'Updating UEFI boot files')"
		echo "root=$(rootdev $ROOT_UUID) $KERNEL_ARGS$(rootdelay)" > $mnt$efiboot/linux.cmdline
		efibootmgr -v | grep -q SliTaz ||
		efibootmgr -c -d "$(uuid2disk UUID=$esp_fs_uuid)" -p "$(partnum UUID=$esp_fs_uuid)" \
			-L "SliTaz GNU/Linux" -l "${efiboot//\//\\}\\BZIMAGE.EFI"
	fi
	umount $mnt && rmdir $mnt
}

#--------------------
# 6. execute section
#--------------------

execute()
{
	local mode="$(get mode)" sighup=1 sigint=2 sigquit=3
	randomize_mirrors
	trap "error9" "$sighup" "$sigint" "$sigquit"
	case "$mode" in
	install)
		install ;;
	upgrade)
		upgrade ;;
	esac
}

#-------------
# 6.1 install
#-------------

# get a clean target device
clean_target()
{
	if [ -z "$ROOT_FORMAT" ]; then
		# partition was not formatted
		log "$(_ 'Cleaning the root partition (%s)...' "$ROOT_UUID")"
		# keep /home in case of reinstall.
		local path="$(pwd)"
		cd "$TARGET_ROOT" || error8
		local dir
		for dir in *
		do
			case "$dir" in
				home)
					log "$(_ 'keeping /home found on: %s' "$ROOT_UUID")"
					mv home home.bak ;;
				lost+found)
					continue ;;
				*)
					log "$(_ 'removing target: %s' "$dir")"
					rm -rf "$dir" 2>>"$LOG" ;;
			esac
		done
		if [ ! -d lost+found ]; then
			mklost+found 2>>"$LOG"
		fi
		cd "$path" || error8
	fi
}

# kernel is renamed to standard vmlinuz-$VERSION.
install_kernel()
{
	KERNEL="vmlinuz-$(uname -r)"
	[ -d /$TARGET_ROOT/lib/modules/$(uname -r) ] ||
		log "$(_ 'Kernel name not found, falling back to: %s' "$(uname -r)")"
	mkdir -p $TARGET_ROOT/boot || error8
	for i in $SOURCE_ROOT/boot/bzImage* ; do
		cp $i $TARGET_ROOT/boot/${KERNEL%slitaz*}slitaz${i#*bzImage}
	done
	mkdir $TARGET_ROOT/boot/tazinst.$$
	for i in $(ls -r $SOURCE_ROOT/boot/rootfs?*.gz 2> /dev/null | sed q) \
		 $SOURCE_ROOT/boot/rootfs.gz $SOURCE_ROOT/boot/*pxe* ; do
		cp $i $TARGET_ROOT/boot/tazinst.$$/
	done
	IPXE="$(cd $TARGET_ROOT/boot/tazinst.$$ ; ls *pxe* 2> /dev/null)"
	ROOTFS="$(cd $TARGET_ROOT/boot/tazinst.$$ ; ls rootfs.* 2> /dev/null)"
	[ -n "$ROOTFS" ] ||
		ROOTFS="$(cd $TARGET_ROOT/boot/tazinst.$$ ; ls -r rootfs?*.* | sed q)"
	log "$(_ 'install_kernel: %s' "$KERNEL")"
}

# extract packed rootfs: squashfs or cromfs
extract_loramfs()
{
	local i
	for i in $(/bin/busybox cpio -idvum 2> /dev/null); do
		case "$i" in
		rootfs*)
			need_package squashfs
			if ! unsquashfs $i ; then
				need_package cromfs
				unmkcromfs $i squashfs-root
			fi
			mv -f squashfs-root/* .
			rmdir squashfs-root
			rm -f $i
		esac
	done
}

# this is a loram rootfs.gz, skip loram bootstrap and extract
extract_first_loramfs()
{
	local path="$(pwd)"
	(zcat $1 || /usr/bin/unlzma < $1) | \
		/bin/busybox cpio -i extractfs.cpio 2> /dev/null &&
		( cd / ; /bin/busybox cpio -id ) < extractfs.cpio && \
		rm -f extractfs.cpio
	ofs=$(/bin/busybox awk '/07070100/ { o+=index($0,"07070100"); printf "%d\n",o/4 ; exit } { o+=1+length() }' < $1)
	dd if=$1 skip=$(($ofs / 1024)) bs=4k count=1 2> /dev/null | \
	( dd skip=$(($ofs % 1024)) bs=4 2> /dev/null ; \
	  dd if=$1 skip=$((1 + ($ofs / 1024) )) bs=4k ) | \
		extract_loramfs
	cd "$path" || error8
}

# extract lzma'ed or gziped rootfs.
extract_rootfs()
{
	local path="$(pwd)"
	local isloramfs
	cd "$TARGET_ROOT" || error8
	if [ -d "$1"/../fs/etc ]; then
		# this is a tazlitobox loram (cdrom)
		cp -a "$1"/../fs/. .
	else
	for i in $(ls "$1"/rootfs* | sort -r); do
		if [ ! -d etc ]; then
			if [ $( (zcat "$i" 2>/dev/null \
					|| /usr/bin/lzma d "$i" -so) | \
				wc -c) -lt $(stat -c %s "$i") ]; then
				# this is a tazlitobox loram (ram)
				isloramfs="$i"
				extract_first_loramfs "$i"
				continue
			fi
		fi
		if [ -n "$isloramfs" ]; then
			extract_loramfs < "$i"
			continue
		fi
		( zcat "$i" 2>/dev/null || /usr/bin/lzma d "$i" -so || \
		  cat "$i" ) 2>>"$LOG" | /bin/busybox cpio -idu
	done 2>>"$LOG" > /dev/null
	fi
	$(which install) -m 644 /etc/keymap.conf /etc/locale.conf /etc/TZ /etc/network.conf etc
	cp -a /usr/lib/locale usr/lib/
	# unpack /usr (double check...)
	if ls etc/tazlito | grep -q ".extract"; then
		for i in etc/tazlito/*.extract; do
			[ -f "$i" ] && . "$i" /media/cdrom
		done
	fi
	cd "$pwd" || error8
}


# pre configure freshly installed system (60 - 80%).
pre_config_system()
{
	local path="$(pwd)"
	cd "$TARGET_ROOT" || error8
	# restore backup of existing /home if exists.
	# (created by prepare_target_dev)
	if [ -d home.bak ]; then
		log "$(_ 'Restoring directory: /home...')"
		rm -rf home
		mv home.bak home
	fi
	# add root device to CHECK_FS in rcS.conf to check filesystem
	# on each boot.
	log "$(_ 'Adding / partition and CHECK_FS to file %s...' '/etc/rcS.conf')"
	sed	-i s#'CHECK_FS=\"\"'#"CHECK_FS=\"$(dev2uuid $ROOT_UUID)\""# etc/rcS.conf
	# set hostname.
	log "$(_ 'Configuring host name: %s' "$HOSTNAME")"
	printf "%s\n" "$HOSTNAME" > etc/hostname
	printf "%s\n" "127.0.0.1	localhost $HOSTNAME tazpanel" > etc/hosts
	cd "$path" || error8
}

# set root passwd and create user after rootfs extraction.
users_settings()
{
	# create file
	cat > "$TARGET_ROOT/users.sh" << _EOF_
#!/bin/sh
umask 0022
printf "root:%s" "$ROOT_PWD" | chpasswd -m
adduser -D -H $USER_LOGIN

for grp in audio cdrom floppy dialout disk kmem tape tty video; do
 if ! grep \$grp /etc/group | grep -q $USER_LOGIN ; then
	grep -q \$grp /etc/group && addgroup $USER_LOGIN \$grp
 fi
done

printf "%s:%s" "$USER_LOGIN" "$USER_PWD" | chpasswd -m
if [ ! -d /home/$USER_LOGIN ]; then
	cp -a /etc/skel /home/$USER_LOGIN
	[ -e /root/.xinitrc ] && cp /root/.xinitrc /home/$USER_LOGIN
	mkdir -p /home/$USER_LOGIN/.config/slitaz
	cp -a /etc/slitaz/applications.conf /home/$USER_LOGIN/.config/slitaz
	# Set ownership
	if grep -q ^users: /etc/group; then
		chown -R $USER_LOGIN:users /home/$USER_LOGIN
	else
		chown -R $USER_LOGIN:$USER_LOGIN /home/$USER_LOGIN
	fi
	# Path for user desktop files.
	for i in /home/$USER_LOGIN/.local/share/applications/*.desktop
	do
		[ -e "$i" ] && sed -i s/"user_name"/"$USER_LOGIN"/g \$i
	done
fi
# Slim default user.
if [ -f /etc/slim.conf ]; then
	sed -i s/"default_user .*"/"default_user        $USER_LOGIN"/ \
		/etc/slim.conf
fi
_EOF_
	chmod o+x "$TARGET_ROOT/users.sh"
	chroot "$TARGET_ROOT" ./users.sh 2>>"$LOG" >> "$LOG"
	rm "$TARGET_ROOT/users.sh"
	umask 0177
}

# /home can be on a separate partition. If default user exists in /home
# we remove default file created by users_settings().
home_config()
{
	if [ -n "$HOME_UUID" ]; then
		local path="$(pwd)" uuid
		cd "$TARGET_ROOT" || error8
		# detect fs
		local home_fs="$HOME_FORMAT"
		[ -z "$home_fs" ] && home_fs="$(filesys $HOME_UUID)"
		# manage /home
		log "$(_ 'Configuring partition to be used as /home: %s' "$HOME_UUID")"
		mv home/$USER_LOGIN tmp
		mount -t "$home_fs" "$HOME_UUID" home >> "$LOG" 2>> "$LOG"
		if [ -d $TARGET_ROOT/home/$USER_LOGIN ]; then
			rm -rf tmp/home/$USER_LOGIN
		else
			mv tmp/$USER_LOGIN home
		fi
		# write entry in fstab, force uuid
		uuid="$(dev2uuid "$HOME_UUID")"
		printf "%b\n" "$uuid /home $home_fs defaults \t0 \t2"	>> etc/fstab
		umount home >> "$LOG" 2>> "$LOG"
		cd "$path" || error8
	fi
}

install()
{
	log_open
	log "1 $(_ 'Installing SliTaz on: %s' "$(get root_uuid)")"
	log "5 $(_ 'Checking settings...')"
	check all >> "$LOG" 2>> "$LOG"
	load_settings

	log "10 $(_ 'Preparing source media...')"
	mount_source

	log "20 $(_ 'Preparing target disk...')"
	prepare_uuid

	log "30 $(_ 'Cleaning the root partition if necessary...')"
	clean_target

	log "40 $(_ 'Extracting the root system...')"
	extract_rootfs $SOURCE_ROOT/boot

	log "50 $(_ 'Installing the Kernel...')"
	install_kernel

	log "60 $(_ 'Preconfiguring the system...')"
	pre_config_system

	log "70 $(_ 'Configuring root and default user account...')"
	users_settings

	log "80 $(_ 'Configuring /home...')"
	home_config

	log "90 $(_ 'Checking bootloader installation...')"
	bootloader

	log "100 $(_ 'Files installation completed')"
	end_of_install
}


#-------------
# 6.2 upgrade
#-------------

# search for SliTaz
check_release()
{
	if [ -f $TARGET_ROOT/etc/slitaz-release ]; then
		local release="$(cat $TARGET_ROOT/etc/slitaz-release)"
		log "$(_ 'Preparing upgrade of SliTaz release: %s' "$release")"
	else
		error 6 "$(_ "%s: This partition doesn't appear to contain a valid \
SliTaz system, the file: %s doesn't exist." "$ROOT_UUID" '/etc/slitaz-release')"
	fi
}

# backup packages list.
backup_files()
{
	local path="$(pwd)"
	cd "$TARGET_ROOT" || error8
	ls -1 var/lib/tazpkg/installed > home/packages-selection.list
	local dir
	for dir in *
	do
		case "$dir" in
			boot)
				rm -rf boot/vmlinuz-* ;;
			home)
				mv home home.bak
				log "$(_ 'keeping /home found on: %s' "$ROOT_UUID")" ;;
			etc)
				/bin/busybox tar czf etc.tar.gz etc
				mv etc etc.bak
				log "$(_ 'keeping /etc found on: %s' "$ROOT_UUID")" ;;
			var)
				if [ -d var/www ]; then
					mv var/www www.bak
					log "$(_ 'keeping /var/www found on: %s' "$ROOT_UUID")"
				fi
				rm -rf var 2>>"$LOG" ;;
			lost+found)
				continue ;;
			*)
				log "$(_ 'removing target: %s' "$dir")"
				rm -rf "$dir" 2>>"$LOG" ;;
		esac
	done
	if [ -d mklost+found ]; then
		mklost+found 2>>"$LOG"
	fi
	cd "$path" || error8
}

# restore backups.
restore_files()
{
	rm -rf $TARGET_ROOT/home
	mv $TARGET_ROOT/home.bak $TARGET_ROOT/home
	rm -rf $TARGET_ROOT/etc
	mv $TARGET_ROOT/etc.bak $TARGET_ROOT/etc
	if [ -d $TARGET_ROOT/www.bak ]; then
		rm -rf $TARGET_ROOT/var/www
		mv $TARGET_ROOT/www.bak $TARGET_ROOT/var/www
	fi
	log "$(_ 'backups restored: %s' "$(date)")"

	# /var/lib/slitaz-installer
	mkdir -p $TARGET_ROOT/var/lib/tazinst && \
	mv $TARGET_ROOT/etc.tar.gz $TARGET_ROOT/var/lib/tazinst && \
	mv $TARGET_ROOT/home/packages-selection.list \
		$TARGET_ROOT/var/lib/tazinst \
		&& log "$(_ 'backups saved in %s' '/var/lib/tazinst')"
}

# upgrade added pkgs
install_pkgs()
{
	# check if the pkg is on the mirror.
	log "$(_ 'Checking the availability of packages...')"
	touch packages-to-install.list
	packages=0
	diff="$(sort < packages-selection.diff)"
	for pkg in $diff
	do
		if grep -q ^$pkg-[0-9] /var/lib/tazpkg/packages.list; then
			packages="$(($packages+1))"
			printf "%s\n" "$pkg" >> packages-to-install.list
		fi
	done

	# install packages.
	log "$(_ 'Installing packages...')"
	if [ "$packages" = "0" ]; then
		log "$(_ 'packages to install: 0')"
	else
		# get-install all missing pkgs.
		while read pkg
		do
			log "$(_ 'Installing: %s...' "$pkg")"
			# get install package and answer yes in case of dependencies.
			pkgname="$(grep ^$pkg /var/lib/tazpkg/packages.list)"
			/usr/bin/tazpkg get "$pkg" >/dev/null 2>/dev/null
			yes "" | /usr/bin/tazpkg install $pkgname.tazpkg \
--root=$TARGET_ROOT >/dev/null 2>/dev/null
			rm -f $pkgname.tazpkg
		done < packages-to-install.list
	fi
	log "$(_ 'Installation of packages complete...')"
}

# search for added pkgs
update_pkgs()
{
	local path="$(pwd)"
	cd $TARGET_ROOT/var/lib/tazinst || error8
	# LiveCD packages list.
	log "$(_ 'Creating package lists...')"
	ls -1 $TARGET_ROOT/var/lib/tazpkg/installed > packages-source.list || error8
	log "$(_ '%s: done' 'packages-source.list')"
	# diff
	/bin/busybox diff packages-source.list packages-selection.list | \
		grep ^+[a-z] | sed s/^+// > packages-selection.diff
	log "$(_ '%s: done' 'packages-selection.diff')"
	# get mirror list.
	/usr/bin/tazpkg recharge >>$LOG 2>>$LOG
	if [ -f /var/lib/tazpkg/packages.list ]; then
		install_pkgs
	else
		touch packages-to-install.list
		longline "$(_ "The list of available packages on the mirror could not \
be downloaded. No missing packages will be reinstalled now, but you can do so \
later by looking at the following list:")"
		echo '/var/lib/tazinst/packages-selection.diff'
	fi
	cd "$path" || error8
}

# upgrade command
upgrade()
{
	log_open
	log "1 $(_ 'Upgrading SliTaz on: %s' "$(get root_uuid)")"
	log "5 $(_ 'Checking settings...')"
	check all >> "$LOG"
	load_settings

	log "10 $(_ 'Preparing source media...')"
	mount_source

	log "20 $(_ 'Preparing target disk...')"
	prepare_uuid

	log "30 $(_ 'Searching for %s...' '/etc/slitaz-release')"
	check_release

	log "40 $(_ 'Backup /etc, /home and the packages list...')"
	backup_files

	log "50 $(_ 'Extracting the root system...')"
	extract_rootfs $SOURCE_ROOT/boot

	log "60 $(_ 'Restoring configuration files...')"
	restore_files

	log "70 $(_ 'Installing the Kernel...')"
	install_kernel

	log "80 $(_ 'Upgrading added packages...')"
	update_pkgs

	log "90 $(_ 'Bootloader...')"
	bootloader

	log "100 $(_ 'Files installation completed')"
	end_of_install
}


#--------------
# tazinst main
#--------------

case $1 in
	new)
		new_file "$2" ;;
	set)
		[ -s "$4" ] || new_file "$4"
		read_file "$4"
		change "$2" "$3" "$4" ;;
	unset)
		read_file "$3"
		change "$2" "" "$3" "$4" ;;
	get)
		read_file "$3"
		get "$2" ;;
	check)
		read_file "$3"
		check "$2" ;;
	list)
		list "$2" "$3" ;;
	execute)
		check_root
		read_file "$2"
		execute "$2" ;;
	log)
		[ -r "$LOG" ] && cat "$LOG" ;;
	clean)
		clean "$2" ;;
	version)
		printf "%s\n" "$VERSION" ;;
	""|usage)
		usage ;;
	help)
		help "$2" ;;
	*)
		usage_error "$1" ;;
esac
