#!/bin/bash
# Fatdog Installer
# Copyright (C) James Budiono 2012, 2013, 2015
# License: GNU GPL Version 3 or later
#
# 1.0 - April 2012 - initial release
# 1.1 - May 2012 - change blkid to busybox blkid (disktype commented out)
# 1.2 - May 2012 - supports smaller initrd (if fatdog-split-initrd.sh exist)
# 1.3 - Oct 2012 - supports additional boot parameters
# 1.4 - Jun 2013 - supports GPT disk
# 1.5 - Feb 2015 - syslinux/extlinux - use *cfg and help from installation ISO.
#
# Note: the bits in Global Inits, which is part of GTK-server bash init code
#       is (C) Peter van Eerten. Everything else is (C) James Budiono
#
# Supported: 
# - frugal install to disk/usb, with ext2/ext3/ext4/fat16/fat32
# - installing boot loader for ext2/ext3/ext4/fat16/fat32/ntfs
# - hooking boot loader for windows xp / windows 7
#
# Not supported: multi-session cd/dvd, full-install, macs
#
# Boot loaders used:
# extlinux for ext2/3/4, syslinux for fat16/fat32, grldr (grub4dos) for ntfs
#

### Configurations
APPTITLE="Fatdog64 Installer"
GRUB4DOS_PATH=/usr/share/grub4dos
SYSLINUX_PATH=/usr/share/syslinux

# grub4dos boot files
GRUB4DOS=$GRUB4DOS_PATH/grldr		# location of grldr
GRLDR_MBR=$GRUB4DOS_PATH/wee.mbr	# location of grldr.mbr (or wee.mbr)

# syslinux boot files
MBR_BIN=$SYSLINUX_PATH/mbr.bin		 # location of mbr.bin
GPTMBR_BIN=$SYSLINUX_PATH/gptmbr.bin # location of mbr.bin
VESAMENU_FILES="$SYSLINUX_PATH/vesamenu.c32 $SYSLINUX_PATH/libcom32.c32 $SYSLINUX_PATH/libutil.c32" 

# other stuff
INITRD_FILES="initrd initrd.gz modules"	# name initrd files to copy, only use what we can find
MIN_TMP_SIZE_FOR_SPLITTING=1000			# anything less will crash the OS - not good.

# subdir in which the boot files are installed. 
# There should be a GUI for this, but I can't be bothered
BOOT_PATH=/boot                           

[ "$BOOTSTATE_PATH" ] && . $BOOTSTATE_PATH	# BASE_SFS_PATH

[ $(id -u) -ne 0 ] && exec gtksu "$APPTITLE" "$0" # must run as root

#---------------------------------------------------------- Global Inits
### global variables for proper GTK-server
declare GTK NULL="NULL"
declare PIPE=/tmp/gtk.bash.$$
trap 'stop-gtk-server -fifo $PIPE; exit' ERR HUP TERM INT # stop GTK-server in case of an error

### start GTK-server
gtk-server -fifo=$PIPE &
while [ ! -p $PIPE ]; do sleep 1; continue; done

### GTK-server helper functions
# Assignment function
function define() { $2 $3 $4 $5 $6 $7 $8 $9; eval $1=\'"$GTK"\'; }

# Communicate with GTK-server
gtk()
{
/bin/echo "$@" > $PIPE
read GTK < $PIPE
}




#---------------------------------------------------------- GUI realization

### Global variables - used throughout
FORMAT_FS=none		# which filesystem the user wants for format to
ALLOW_FORMAT=no		# allow formatting on MBR (only for USB devices)
CURRENT_FS=none		# current filesystem
CURRENT_DEV=none	# currently selected device/partition
CURRENT_DISK=none	# the disk where the partition is located
CURRENT_TYPE=none	# type is either Disk or Partition
CURRENT_PARTNO=none # partition number of the current device (derived from DEV and DISK)
					# (ie the "1" in "sda1").

BOOTLOADER=bootnone	# where to install boot loader to
CURRENT_OS=winxp	# currently installed OS on the selected partition

INSTALL_SOURCE=iso	# installation source
ISO_FILE=			# location of iso file, if any

USE_SMALL_INITRD=	# by default, Fatdog uses humongous initrd. But this large initrd can cause slow booting
					# with usb devices. This option enables extraction of base sfs to make a smaller initrd.
BOOTPARAMS=			# extra boot parameters to use
IS_GPT=				# not blank if disk partition is gpt




################## helper functions #################
function init_gtk() {
	# init gtk
	gtk gtk_init
	
	# add GTK+ API not defined in gtk-server.cfg
	gtk gtk_server_define gtk_builder_new NONE WIDGET 0
	gtk gtk_server_define gtk_builder_add_from_file NONE INT 3 WIDGET STRING NULL
	gtk gtk_server_define gtk_builder_add_from_string NONE INT 4 WIDGET STRING INT NULL
	gtk gtk_server_define gtk_builder_get_object NONE WIDGET 2 WIDGET STRING
	
	gtk gtk_server_define gtk_tree_store_append NONE NONE 3 WIDGET WIDGET WIDGET
	gtk gtk_server_define gtk_tree_view_expand_all NONE NONE 1 WIDGET
	gtk gtk_server_define gtk_tree_store_clear NONE NONE 1 WIDGET
	
	gtk gtk_server_define gtk_tree_view_get_cursor NONE NONE 3 WIDGET PTR_WIDGET NULL
	gtk gtk_server_define gtk_tree_path_to_string NONE STRING 1 WIDGET	
	
	gtk gtk_server_define gtk_notebook_next_page NONE NONE 1 WIDGET	
	gtk gtk_server_define gtk_notebook_prev_page NONE NONE 1 WIDGET	
	gtk gtk_server_define gtk_label_set_markup NONE NONE 2 WIDGET STRING
}




### load / refresh the partition views
# $1 - treestore, $2 iter, $3 iter2, $3 - $9 as follows
PSC_PARTITION=0; PSC_LABEL=1; PSC_FSTYPE=2; PSC_TYPE=3; PSC_DEVICETYPE=4; PSC_DISK=5
function partition_add_item() {	
	local DEVICE LABEL TYPE FSTYPE DEVICETYPE DISK
	DEVICE=$4; LABEL="$5"; FSTYPE=$6; DEVICETYPE="$7"; TYPE="$8"; DISK=$9
	[ -z "$DEVICE" ] && DEVICE=Unknown; [ -z "$LABEL" ] && LABEL=-; 
	[ -z "$FSTYPE" ] && FSTYPE=Unknown; [ -z "$DEVICETYPE" ] && DEVICETYPE=Unknown; 
	gtk gtk_tree_store_append $1 $2 $3
	gtk gtk_tree_store_set $1 $2 $PSC_PARTITION $DEVICE $PSC_LABEL \"$LABEL\" -1
	gtk gtk_tree_store_set $1 $2 $PSC_FSTYPE \"$FSTYPE\" $PSC_DEVICETYPE \"$DEVICETYPE\" -1	
	gtk gtk_tree_store_set $1 $2 $PSC_TYPE \"$TYPE\" $PSC_DISK $DISK -1	
}
function load_partition_view() {
	local ITER ITER2 
	gtk gtk_tree_store_clear $PARTITION_STORE	
	define ITER gtk gtk_server_opaque
	define ITER2 gtk gtk_server_opaque
	
	# for all block devices
	ls /sys/block | grep -vE "^ram|^loop|^nbd|^sr" | while read dev; do
		DEVICETYPE="Disk"		
		cat /proc/sys/dev/cdrom/info | grep -F "drive name" | grep -q $dev && DEVICETYPE="CD/DVD"
		[ "$(cat /sys/block/$dev/removable)" = "1" ] && DEVICETYPE="Removable $DEVICETYPE"
		readlink /sys/block/$dev | grep -q usb && DEVICETYPE="USB $DEVICETYPE"
		
		partitions=$(ls -d /sys/block/$dev/$dev* 2> /dev/null | sed "s_/sys/block/$dev/__g" | sort -V)
		if [ -z "$partitions" ]; then
			# no partitions - superfloppy
			FSTYPE=$(guess_fstype /dev/$dev)
			LABEL=""
			[ -z "$FSTYPE" ] && FSTYPE="unknown"			
			#LABEL=$(disktype /dev/$dev | sed -n '/Volume name/ { s/.*"\(.*\)".*/\1/; p}')
			[ "$FSTYPE" != "unknown" ] && LABEL=$(busybox blkid /dev/$dev | sed -n "\_^/dev/${dev}:_ {"'/LABEL/ {s/.*LABEL="\([^"]*\)".*/\1/; p}}')
			partition_add_item $PARTITION_STORE $ITER NULL $dev "$LABEL" "$FSTYPE" "$DEVICETYPE" "Disk" $dev
		else
			# device has partitions		
			partition_add_item $PARTITION_STORE $ITER NULL $dev "" "Partition" "$DEVICETYPE" "Disk" $dev
			for part in $partitions; do
				case $part in
					$dev) ;;
					*)	FSTYPE=$(guess_fstype /dev/$part)
						LABEL=""
						[ -z "$FSTYPE" ] && FSTYPE="unknown"
						#LABEL=$(disktype /dev/$part | sed -n '/Volume name/ { s/.*"\(.*\)".*/\1/; p}')
						[ "$FSTYPE" != "unknown" ] && LABEL=$(busybox blkid /dev/$part | sed -n '/LABEL/ {s/.*LABEL="\([^"]*\)".*/\1/; p}')
						partition_add_item $PARTITION_STORE $ITER2 $ITER $part "$LABEL" $FSTYPE "$DEVICETYPE" "Partition" $dev
						;;
				esac
			done
		fi
	done
	
	gtk free $ITER; gtk free $ITER2
	gtk gtk_tree_view_expand_all $PARTITION_VIEW
}




### get details of the selected entry
# output: $1 - disk, $2 - type, $3 fstype
function get_selected_partition() {
	local BUF ITER SELECTED_PATH
	define ITER gtk gtk_server_opaque
	define BUF gtk malloc 1024
	
	define SELECTED_PATH gtk gtk_tree_view_get_cursor $PARTITION_VIEW $BUF
	#gtk gtk_tree_path_to_string $SELECTED_PATH; echo $GTK
	gtk gtk_tree_model_get_iter $PARTITION_STORE $ITER $SELECTED_PATH	
	gtk gtk_tree_path_free $SELECTED_PATH
	gtk gtk_tree_model_get $PARTITION_STORE $ITER $PSC_PARTITION $BUF -1
	echo -n $GTK " "
	gtk gtk_tree_model_get $PARTITION_STORE $ITER $PSC_TYPE $BUF -1
	echo -n $GTK " "
	gtk gtk_tree_model_get $PARTITION_STORE $ITER $PSC_FSTYPE $BUF -1
	echo -n $GTK " "
	gtk gtk_tree_model_get $PARTITION_STORE $ITER $PSC_DEVICETYPE $BUF -1	
	echo -n \"$GTK\" " "
	gtk gtk_tree_model_get $PARTITION_STORE $ITER $PSC_DISK $BUF -1	
	echo $GTK

	gtk free $BUF
	gtk free $ITER
}




### update state (message, buttons)
# $1-partition $2-type $3-fs $4-device type $5-disk 
function update_state() {
	local msg RESULT
	CURRENT_FS=$3; CURRENT_DEV=$1; CURRENT_DISK=$5; CURRENT_TYPE=$2
	CURRENT_PARTNO=${CURRENT_DEV#${CURRENT_DISK}}
	
	# update diskinfo
	msg="Device/partition: <b>$1</b>\n"
	#msg="${msg}Type: $2\n"
	msg="${msg}Device type: <b>$4</b>\n"	
	msg="${msg}Filesystem: <b>$3</b>\n"
	gtk gtk_label_set_markup $DISKINFO_LABEL \"$msg\"
	
	# flag whether disk is usb
	ALLOW_FORMAT=no
	case $4 in 
		*Removable*) ALLOW_FORMAT=yes ;; 
	esac
	
	# conditional hide/show enable/disable for buttons
	# format and button box
	case $2 in
		Disk)
			gtk gtk_widget_show $GPARTED_BOX
			gtk gtk_widget_hide $FORMAT_BOX
			# superfloppy
			if [ "$ALLOW_FORMAT" = "yes" ]; then gtk gtk_widget_show $FORMAT_BOX; fi
			;;
		Partition)
			gtk gtk_widget_hide $GPARTED_BOX
			gtk gtk_widget_show $FORMAT_BOX
			;;
	esac
}




### update button states (enable/disable/hide)
LASTPAGE=3
function update_buttons() {
	local NOTEBOOK_PAGE
	
	# get current notebook page
	define NOTEBOOK_PAGE gtk gtk_notebook_get_current_page $MAIN_NOTEBOOK

	# install - all these must be true for INSTALL button to be turned on
	gtk gtk_widget_set_sensitive $INSTALL_BUTTON 0
	if [ "$CURRENT_DISK" != "none" -a "$NOTEBOOK_PAGE" = "$LASTPAGE" -a \
	\( "$ISOFILE" -o "$INSTALL_SOURCE" = "cd" \) -a \
	\( "$CURRENT_FS" != "Partition" -o "$ALLOW_FORMAT" = "yes" \) ]; then
		gtk gtk_widget_set_sensitive $INSTALL_BUTTON 1
	fi
	
	# prev/next/install button
	gtk gtk_widget_set_sensitive $PREV_BUTTON 1
	gtk gtk_widget_set_sensitive $NEXT_BUTTON 1
	case $NOTEBOOK_PAGE in
		0) gtk gtk_widget_set_sensitive $PREV_BUTTON 0 ;;
		$LASTPAGE) gtk gtk_widget_set_sensitive $NEXT_BUTTON 0 ;;
	esac
	
	# get current partition type
	[ "$CURRENT_DISK" != "none" ] && 
	case $(parted /dev/$CURRENT_DISK print | sed -n '/Partition Table/ {s/.*:[ \t]*//;p}') in
		gpt) IS_GPT=yes ;;
		*) IS_GPT="" ;;		
	esac
	
	# reuse-boot loader can only be used with MBR
	gtk gtk_widget_set_sensitive $RADIO_BOOT_REUSE_BUTTON 1	
	[ $IS_GPT ] && 	gtk gtk_widget_set_sensitive $RADIO_BOOT_REUSE_BUTTON 0
	return 0 # make bash happy
}




### build and update boot info
function update_bootinfo() {
	local msg
	case $BOOTLOADER in
		mbr) msg="
This will install the bootloader to the MBR (Master Boot Record).\n
This is the most reliable way of installation (because no assumptions are made),\n
<span color='red' weight='bold'>but generally will make other existing OS does not boot anymore.</span>" ;;
		part) msg="
This will install the bootloader to the install partition.\n
For this to work, the system must already have a bootable MBR.\n
The MBR will not be modified, but the boot loader of the partition will be replaced.\n\n
<span color='red'>This is experimental code and your other OS may not boot if you use this.</span>" ;;
		reuse) msg="
The installer will try to make use of NTLDR or BOOTMGR.EXE to load Fatdog64.\n
Note that this is not always successful.\n
Only Windows are supported - Linux simply has too many permutations ☺\n\n
<span color='red'>This is experimental code and your Windows may not boot if you use this.\n
This is only supported on MBR disk, not GPT disk.</span>" ;;
		bootnone)
			msg="
In this case you need to append vmlinuz and initrd of Fatdog64 system to your existing bootloader.\n
How exactly to do this depends on the boot loader you use.\n
\n
For Grub/Grub2, you need to add something like this to your Grub menu/config file:\n
<span color='blue'>title Fatdog64\n
root <b>(hd0,0)</b>\n
kernel /vmlinuz rootfstype=ramfs\n
initrd /initrd</span>\n
Replace <b>(hd0,0)</b> with where you install Fatdog64.\n
\n
If you syslinux, you may need to use:\n
<span color='blue'>label Fatdog64\n
kernel vmlinuz\n
append rootfstype=ramfs initrd=initrd</span>." ;;
	esac
	gtk gtk_label_set_markup $BOOTINFO_LABEL \"$msg\"
}




### build and update confirmation page
function update_confirmation() {
	local msg boot warning os INSTALL_FS
	
	msg="\n"
	msg="${msg}Install to device/partition: <b>$CURRENT_DEV</b>\n"
	[ $CURRENT_DISK != "none" ] &&
	msg="${msg}Partition type of disk $CURRENT_DISK: <b>$([ $IS_GPT ] && echo GPT || echo MBR)</b>\n"
	msg="${msg}Current filesystem on $CURRENT_DEV: <b>$CURRENT_FS</b>\n"
	if [ "$CURRENT_FS" = "Partition" -a "$ALLOW_FORMAT" != "yes" ]; then
		msg="${msg}<span color='red'>Cannot install to $CURRENT_FS unless it is re-formatted.</span>\n"
	else
		if [ "$FORMAT_FS" = "none" ]; then
			msg="${msg}This filesystem will be kept as is, no data will be lost.\n"
		else
			msg="${msg}This device/partition will be re-formatted to <b>$FORMAT_FS</b>\n"
			msg="${msg}<span color='red'>WARNING: ALL DATA in <b>$CURRENT_DEV</b> WILL BE DELETED !!</span>\n"
		fi	
	fi
	 
	case $CURRENT_OS in
		winxp) os="Windows 2000 or Windows XP" ;;
		win7) os="Windows Vista or Windows 7" ;;
	esac

	INSTALL_FS=$CURRENT_FS
	[ "$FORMAT_FS" != "none" ] && INSTALL_FS=$FORMAT_FS
	case $INSTALL_FS in
			ext2|ext3|ext4|fat16|fat32|ntfs|vfat)
				boot="\nBoot loader will be installed in <b>"
				warning="<span color='red'>NOTE: Installing boot loader can potentially make your other OS unusable</span>.\n"
				case $BOOTLOADER in
					mbr) msg="${msg}${boot}MBR</b> (Master Boot Record) on disk <b>$CURRENT_DISK</b>.\n${warning}" ;;
					part) msg="${msg}${boot}$CURRENT_DEV partition</b>.\n${warning}" ;;
					reuse) msg="${msg}\nBoot loader from the current Operating System ($os) will be used.\n" ;;
					bootnone) msg="${msg}\nNo boot loader will be installed, you have to do this manually before Fatdog64 can boot.\n" ;;
				esac ;;
			*)
				msg="${msg}<span color='red'>Unsupported filesystem $INSTALL_FS. No boot loader will be installed.</span>\n."
				;;
	esac

	msg="${msg}\nThe installation source is located in "
	case $INSTALL_SOURCE in
		cd) msg="${msg}a CD/DVD in the first CD/DVD drive.\nIf you have not done it, please insert Fatdog64 CD/DVD now.\n" ;;
		iso)if [ "$ISOFILE" ]; then
				msg="${msg}$ISOFILE.\n"
			else
				msg="${msg}an ISO file, but that file has not been specified yet.\n"
			fi ;;
	esac
	
	[ $BOOTPARAMS ] && msg="${msg}\nAdditional boot parameters to use: $BOOTPARAMS"
	[ $USE_SMALL_INITRD ] && msg="${msg}\nUsing small initrd. Base sfs will be extracted from initrd and stored separately.\n"
		
	gtk gtk_label_set_markup $CONFIRM_LABEL \"$msg\"
}


function display_help() {
	Xdialog --title "$APPTITLE" --no-cancel --textbox - 0 0 << EOF
Fatdog64 Installer for BIOS-based systems version 1.5

==========================================
Warning: As with any Operating System installer, using this software
can cause data loss. *BACKUP* your data first. Use it at your own risk.
==========================================

NOTE: This installer only works for BIOS-based systems, or UEFI systems 
running with CSM module enabled (which basically makes them behave like
BIOS-based systems). 

It does *NOT* support pure UEFI only systems (like Macs etc).
Please use the manual installation method outlined in the FAQ document
file:///usr/share/doc/faqs/uefi-harddrive.html or
file:///usr/share/doc/faqs/uefi-flashdrive.html

==========================================

Fatdog64 Installer is used to install Fatdog64 operating system.
It can install to:
- internal harddisk
- external harddisk or thumbdrives

Installation consist of two steps:
- copying operating system files
- installing boot loader

Step 1 is to choose where you want to install to.
You can also choose whether to format the device/partition (WHICH DELETES ALL DATA),
or keep it as is. You can also modify the partitions; if this is a brand new device,
you may need to create new partitions first.

Step 2 is to choose what boot loader to use.
You can choose either to install both boot loader and MBR loader, boot loader only,
re-use existing boot loaders, or not at all.
Note that installing boot loader can potentially stop other existing operating system to start.
However, Fatdog64 will not start without a boot loader. If you choose not to install 
boot loader, you need to manually re-configure your existing boot loader to start Fatdog64.

If you choose to install/re-use boot loaders:
- extlinux is used for ext2/ext3/ext4.
- syslinux is used for fat16/fat32/vfat
- grldr (grub4dos) is used for ntfs and for compatibility with Windows OS.
MBR loader always comes from syslinux.

Step 3 is to choose the installation source.
Fatdog64 Installer can use Fatdog64 CD/DVD, or from Fatdog64 ISO file.
The ISO file can be either plain vanilla downloaded from Fatdog64 website,
or a result of a remaster.

When you're done, go to Step 4 to review what you have chosen.
Once you are happy with the choices, have backed up your data, and ready to take the risk, 
click Install and wait. You will be advised on the progress.

EOF
	true
}

########################### actual installation code here #####################

# helper - show error message
# $1-message $2 etc - dirs to unmount and remove
function error_message() {
	kill $XPID
	Xdialog --title "$APPTITLE" --infobox "$1" 0 0 10000
	shift
	while [ $1 ]; do
		[ $1 ] && umount -d $1
		[ $1 ] && rmdir $1	
		shift
	done	
}

# helper - display progress
# $1-message
function display_progress() {
	kill $XPID 2> /dev/null
	Xdialog --title "$APPTITLE" --no-buttons --infobox "$1" 0 0 1000000 &
	XPID=$!
}

# helper - install grldr
# $1-where to install (mountpoint, not device), $2--initrd list $3-grub disk "0" in "hd0"
# note: grub disk is always zero for self-booted system, because it always refers to the
# first disk found by BIOS. For those system by NTDLR, it has to be the actual disk location
function install_grldr() {
	local initrdlist
	inirdlist=
	for x in ${2//:/ }; do initrdlist="$initrdlist $BOOT_PATH/$x"; done	# bash construct
	initrdlist=${initrdlist/# /}	# bash construct, remove leading space
		
	cp $GRUB4DOS $1
	cat > $1/menu.lst << EOF
timeout 5
title fatdog
root (hd$3,$((CURRENT_PARTNO - 1)))
kernel $BOOT_PATH/vmlinuz rootfstype=ramfs $BOOTPARAMS
initrd $initrdlist
EOF
	unix2dos $1/menu.lst
}

# helper - install boot loader
# ext2/3/4 - extlinux, fat16/fat32/vfat - syslinux, ntfs - grub4dos 
# also make partition active (bootable)
# $1-filesystem to use, $2-mount point of the device, $3-initrd list, $srcdir (help files etc)
function install_bootloader() {
	local startsector initrdlist srcdir=$4 bootfilesdir=$2/$BOOT_PATH
	inirdlist=
	for x in ${3//:/ }; do initrdlist="$initrdlist,$x"; done	# bash construct
	initrdlist=${initrdlist/#,/}	# bash construct, remove leading comma
	
	# create minimal config file, just in case
	cat > $bootfilesdir/syslinux.cfg << EOF
say Type "fatdog" with kernel parameters, or just press Enter for default boot.
default fatdog
timeout 50
prompt 1
label fatdog
kernel vmlinuz
initrd $initrdlist
append rootfstype=ramfs $BOOTPARAMS
EOF
	# install sys/ext linux
	case $1 in
		ext2|ext3|ext4)
			extlinux --install $bootfilesdir
			if [ -e $srcdir/isolinux.cfg ]; then
				cp -a $srcdir/{fatdog.png,help} $VESAMENU_FILES $bootfilesdir
				sed -e "/^label fatdog$/,/endtext/ s|^append.*|append rootfstype=ramfs $BOOTPARAMS|" \
				    -e "s/Fatdog64 Live/Fatdog64 Linux/" \
					$srcdir/isolinux.cfg > $bootfilesdir/syslinux.cfg
			fi
			umount $2 ;;
			
		fat16|fat32|vfat) 
			if [ -e $srcdir/isolinux.cfg ]; then
				cp -a $srcdir/{fatdog.png,help} $VESAMENU_FILES $bootfilesdir
				sed "/^label fatdog$/,/endtext/ s|^append.*|append rootfstype=ramfs $BOOTPARAMS|" \
					-e "s/Fatdog64 Live/Fatdog64 Linux/" \
					$srcdir/isolinux.cfg > $bootfilesdir/syslinux.cfg
			fi
			umount $2
			syslinux --install /dev/$CURRENT_DEV -d $BOOT_PATH ;;
			
		ntfs)
			rm $bootfilesdir/syslinux.cfg
			install_grldr $2 $3 0
			umount $2
			case $CURRENT_TYPE in
				Disk) bootlace.com  --ntfs --floppy /dev/$CURRENT_DEV ;;
				Partition)
					startsector=$(fdisk -lu /dev/$CURRENT_DISK | grep $CURRENT_DEV | awk '{print $2}')
					[ "$startsector" = "*" ] && startsector=$(fdisk -lu /dev/$CURRENT_DISK | grep $CURRENT_DEV | awk '{print $3}')
					bootlace.com --ntfs --floppy=$((CURRENT_PARTNO - 1)) --start-sector=$startsector /dev/$CURRENT_DEV
			esac			
			;;
	esac
	
	# mark partition as bootable
	[ "$CURRENT_TYPE" != "Disk" ] && 
	if [ $IS_GPT ]; then
		sgdisk -A ${CURRENT_PARTNO}:set:2 /dev/$CURRENT_DISK
	else
		sfdisk -A ${CURRENT_PARTNO} /dev/$CURRENT_DISK
	fi
}

# helper - install MBR boot code
function install_mbr() {
	if [ $IS_GPT ]; then
		dd bs=440 count=1 conv=notrunc if=$GPTMBR_BIN of=/dev/$CURRENT_DISK
	else
		dd bs=440 count=1 conv=notrunc if=$MBR_BIN of=/dev/$CURRENT_DISK
	fi
}

# helper - hook grub4dos with windows xp
# $1 - target install mountpoint, $2 initrdlist
function hook_winxp() {
	local active_partition tmpwin
	
	# 1. find current active partition (windows boot)
	active_partition=$(sfdisk -l -uS /dev/$CURRENT_DISK | grep "*" | awk '{print $1}')
	
	# 2. mount it if it's not already mounted (the one we choose)
	tmpwin=$1	
	if [ "$active_partition" != "/dev/$CURRENT_DEV" ]; then
		tmpwin=$(mktemp -dp /tmp fatdog-win-XXXXXX)
		if ! ntfs-3g $active_partition $tmpwin; then
			error_message "Unable to mount Windows partition." $tmpwin
			return 1
		fi
	fi
	
	# 3. check for boot.ini
	if [ ! -f $tmpwin/boot.ini ]; then
		error_message "Unable to find boot.ini to hook with Windows XP." $tmpwin
		return 1
	fi

	# 4. install grldr and hook boot.ini
	# note: assume disk is sdX (sda, sdb etc)
	install_grldr $tmpwin $2 $(echo ${CURRENT_DISK#sd} | tr 'abcdefghij' '0123456789')
	echo "C:\grldr=\"Linux\"" >> $tmpwin/boot.ini
	sync
	
	umount $tmpwin
	rmdir $tmpwin
}

# helper - hook grub4dos with windows vista and windows 7
# $1 - target install mountpoint, $2 initrd-list
function hook_win7() {
	# 1. copy GRLDR_MBR to target
	cp $GRLDR_MBR $1/grldr.mbr
	
	# 2. install grldr to target device
	install_grldr $1 $2 $(echo ${CURRENT_DISK#sd} | tr 'abcdefghij' '0123456789')
	
	# 3. create the file for user to hook later on.
	cat > $1/Install-Fatdog64.cmd <<EOF
@echo off
echo ##########################      WARNING      #############################
echo #
echo #
echo # This file needs to be run as Administrator. 
echo # You can do this by right-clicking on Windows Explorer, and choose 
echo # "Run as Administrator". Or you can start Windows cmd.exe as Administrator
echo # and run this from there.
echo #
echo # This script will only run once and then it will delete itself.
echo # You do not have a second chance if you make a mistake.
echo #
echo # Are you sure you are running this as Administrator?
echo # If not, press Ctrl-C now and when asked whether to terminate, choose Y
echo # Otherwise, just press Enter to continue.
echo #
echo #
pause

echo # Creating new boot entry for emulated bootsector, and grab its GUID
for /f "tokens=3" %%A in ('bcdedit /create /d "Linux" /application bootsector') do set guid=%%A

echo # Set location of emulated bootsector to be the drive this script is in
bcdedit /set %guid% device partition=%~d0

echo # Set path of emulated bootsector as \grldr.mbr 
bcdedit /set %guid% path \grldr.mbr

echo # Add new boot entry at the end of the boot list
bcdedit /displayorder %guid% /addlast

echo ################  Operation completed  ################
echo #
echo # this file will be deleted afterwards.
echo # Please press Enter to continue.
echo #
pause
del %~f0
EOF
	unix2dos $1/Install-Fatdog64.cmd
	Xdialog --left --title "$APPTITLE" --msgbox "A file called Install-Fatdog64 has been created in the root directory of your Windows partition.\n
\n
The next time you boot into Windows, use Windows Explorer to look for it: it could be in C:\\, D:\\, E:\\ or others.\n
You need to run this as as Administrator, as follows:
==> From Windows Explorer, right-click the file, and then choose the option \"Run As Administrator\"\n
\n
The file can only be run once, as it will delete itself after execution.\n
So make sure you run it as Adminstrator as instructed above.\n
\n
The file is a fully-commented Windows batch file that contains instructions to add grldr.mbr (Grub4Dos 
boot sector) to Windows boot menu. You can view the file with Notepad before running it.\n
\n
If you want to keep the file, please make a copy of it before running it, \n
but please note that you should only run this file once. \n
Running it more than once will add multiple entries in your Windows boot menu, possibly confusing yourself.
" 0 0
}

### main installation routine
function do_install() {
	local srcdir mounted INSTALL_FS INITRD
	
	# 1. get the files - ISO or CD. Bail out early if fails.
	display_progress "Mounting source disk/ISO ..."
	srcdir=$(mktemp -dp /tmp fatdog-source-XXXXXX)
	if ! case $INSTALL_SOURCE in 
		cd)	mount /dev/cdrom $srcdir ;;
		iso) mount -o loop "$ISOFILE" $srcdir ;;
	esac; then
		error_message "Unable to mount install source."  $srcdir
		return 1
	fi
	
	# 1.1 sanity check
	display_progress "Checking for valid disk ..."
	if ! [ -f $srcdir/vmlinuz -a \( -f $srcdir/initrd -o -f $srcdir/initrd.gz \) ]; then
		error_message "Cannot find vmlinuz or initrd/initrd.gz in the ISO or CD/DVD." $srcdir 
		return 1
	fi
		
	# 1.2 get all the initrd files
	INITRD=
	for x in $INITRD_FILES; do 
		[ -f $srcdir/$x ] && INITRD=$INITRD:$x
	done
	INITRD=${INITRD/#:/}	# bash construct, remove leading colon

	# 1.3 make sure chosen partition/device is not mounted
	display_progress "Checking that $CURRENT_DEV is not used ..."
	dmdev=$(ls -l /dev/mapper/* 2> /dev/null | awk -v dmdev=${CURRENT_DEV##*-} '$6 == dmdev {print $10; exit; }')
	[ "$dmdev" ] || dmdev=$(ls -l /dev/mapper/* 2> /dev/null | awk -v dmdev=/${CURRENT_DEV##*/}$ '$NF ~ dmdev { print $(NF-2); exit; }')
	
	mounted=$(awk -v dev=$CURRENT_DEV -v dmdev=$dmdev '$1 == dev || $1 == dmdev { print $2; exit; }' /proc/mounts)
	if [ "$mounted" ]; then
		# try to unmount it
		su $USER -c "rox -D $mounted"
		umount -f $mounted
		if grep -Eqm 1 "^/dev/$CURRENT_DEV |^$dmdev " /proc/mounts; then
			error_message "$CURRENT_DEV is currently used, please unmount it first." $srcdir 
			return 1
		fi
	fi

	# 2. format the target partition if needed.
	INSTALL_FS=$CURRENT_FS
	[ $FORMAT_FS != none ] && INSTALL_FS=$FORMAT_FS && display_progress "Formatting $CURRENT_DEV to $FORMAT_FS ..."
	if ! case $FORMAT_FS in
		ext2|ext3|ext4) mke2fs -F -t $FORMAT_FS /dev/$CURRENT_DEV ;;
		fat16) mkdosfs -I -F 16 /dev/$CURRENT_DEV ;;
		fat32) mkdosfs -I -F 32 /dev/$CURRENT_DEV ;;
		ntfs) mkntfs -F -Q /dev/$CURRENT_DEV ;;
	esac; then
		error_message "Unable to format the $CURRENT_DEV to $FORMAT_FS."  $srcdir 
		return 1
	fi
	
	# 2.1 update partition type - failure is harmless, so no check here
	if [ $CURRENT_TYPE != Disk ]; then
		if [ $IS_GPT ]; then
			case $FORMAT_FS in
				ext2|ext3|ext4)   sgdisk -t ${CURRENT_PARTNO}:8300 /dev/$CURRENT_DISK ;; # Linux
				fat16|fat32|ntfs) sgdisk -t ${CURRENT_PARTNO}:0700 /dev/$CURRENT_DISK ;; # Windows
			esac
		else
			case $FORMAT_FS in
				ext2|ext3|ext4) echo ",,L" | sfdisk -f -uS -N${CURRENT_PARTNO} /dev/$CURRENT_DISK ;;
				fat16) echo ",,e" | sfdisk -f -uS -N${CURRENT_PARTNO} /dev/$CURRENT_DISK ;; # W95 FAT16 LBA
				fat32) echo ",,c" | sfdisk -f -uS -N${CURRENT_PARTNO} /dev/$CURRENT_DISK ;; # W95 FAT32 LBA
				ntfs)  echo ",,7" | sfdisk -f -uS -N${CURRENT_PARTNO} /dev/$CURRENT_DISK ;; # NTFS
			esac
		fi
	fi
	
	# 3. copy the files
	display_progress "Mounting $CURRENT_DEV ..."
	mounted=$(mktemp -dp /tmp fatdog-target-XXXXXX)
	if ! case $INSTALL_FS in
		ntfs) ntfs-3g /dev/$CURRENT_DEV $mounted ;;
		vfat) mount -o utf8 /dev/$CURRENT_DEV $mounted ;;
		*) mount /dev/$CURRENT_DEV $mounted ;;
	esac; then 
		error_message "Unable to mount $CURRENT_DEV."  $srcdir $mounted
		return 1
	fi
	display_progress "Copying files to $CURRENT_DEV ..."
	mkdir -p $mounted/$BOOT_PATH
	cp $srcdir/vmlinuz $mounted/$BOOT_PATH # kernel
	for x in ${INITRD//:/ }; do # initrd - bash construct
		case $x in
			initrd*) if [ $USE_SMALL_INITRD ]; then 
						display_progress "Making initrd smaller (this may take a while) for $CURRENT_DEV ..."
						fatdog-split-initrd.sh $srcdir/$x $mounted/$BOOT_PATH
					 else cp $srcdir/$x $mounted/$BOOT_PATH
					 fi ;;
			*) cp $srcdir/$x $mounted/$BOOT_PATH ;;
		esac
	done
	[ "$BASE_SFS_PATH" -a -e $srcdir/${BASE_SFS_PATH##*/} ] && 
	cp $srcdir/${BASE_SFS_PATH##*/} $mounted/$BOOT_PATH # base sfs
	
	# 4. install boot loader
	display_progress "Installing boot loader to $CURRENT_DEV ..."
	case $BOOTLOADER in
		mbr) # install mbr & syslinux / extlinux & set bootable
			[ $CURRENT_TYPE != Disk ] && install_mbr
			install_bootloader $INSTALL_FS $mounted $INITRD $srcdir ;;

		part) # install syslinux / ext linux & set bootable
			install_bootloader $INSTALL_FS $mounted $INITRD $srcdir ;;

		reuse) # install grldr and fix-up boot.ini / BCD
			case $CURRENT_OS in
				winxp)	hook_winxp $mounted $INITRD ;;
				win7)	hook_win7 $mounted $INITRD ;;
			esac
	esac
	
	# done
	error_message "Installation completed successfully!" $srcdir $mounted
	return 0
}

################## main ######################
[ -e /sys/firmware/efi ] && Xdialog --icon "/usr/share/mini-icons/mini-stop.xpm" --title "Warning" \
        --infobox "Installation created by this installer only boots on BIOS systems." 0 0 10000
### load GUI
init_gtk
define BUILDER gtk gtk_builder_new
define RESULT gtk gtk_builder_add_from_file $BUILDER "${0}.xml"
#define RESULT gtk gtk_builder_add_from_string $BUILDER \'"$(cat installer.glade)"\' -1

define MAIN_WINDOW gtk gtk_builder_get_object $BUILDER main_window
define MAIN_NOTEBOOK gtk gtk_builder_get_object $BUILDER main_notebook

define CANCEL_BUTTON gtk gtk_builder_get_object $BUILDER cancel
define INSTALL_BUTTON gtk gtk_builder_get_object $BUILDER install
define NEXT_BUTTON gtk gtk_builder_get_object $BUILDER next
define PREV_BUTTON gtk gtk_builder_get_object $BUILDER prev
define HELP_BUTTON gtk gtk_builder_get_object $BUILDER help

define REFRESH_BUTTON gtk gtk_builder_get_object $BUILDER refresh
define PARTITION_STORE gtk gtk_builder_get_object $BUILDER partitionstore
define PARTITION_VIEW gtk gtk_builder_get_object $BUILDER partitionview

define DISKINFO_LABEL gtk gtk_builder_get_object $BUILDER diskinfo
define GPARTED_BOX gtk gtk_builder_get_object $BUILDER gpartedbox
define GPARTED_BUTTON gtk gtk_builder_get_object $BUILDER gparted

define CKBOX_SMALL_INITRD gtk gtk_builder_get_object $BUILDER small-initrd
define FORMAT_BOX gtk gtk_builder_get_object $BUILDER formatbox
define RADIO_NONE_BUTTON gtk gtk_builder_get_object $BUILDER radionone
define RADIO_EXT4_BUTTON gtk gtk_builder_get_object $BUILDER radioext4
define RADIO_EXT3_BUTTON gtk gtk_builder_get_object $BUILDER radioext3
define RADIO_EXT2_BUTTON gtk gtk_builder_get_object $BUILDER radioext2
define RADIO_FAT16_BUTTON gtk gtk_builder_get_object $BUILDER radiofat16
define RADIO_FAT32_BUTTON gtk gtk_builder_get_object $BUILDER radiofat32
define RADIO_NTFS_BUTTON gtk gtk_builder_get_object $BUILDER radiontfs

define RADIO_BOOT_PART_BUTTON gtk gtk_builder_get_object $BUILDER radiobootpart
define RADIO_BOOT_MBR_BUTTON gtk gtk_builder_get_object $BUILDER radiobootmbr
define RADIO_BOOT_NONE_BUTTON gtk gtk_builder_get_object $BUILDER radiobootnone
define RADIO_BOOT_REUSE_BUTTON gtk gtk_builder_get_object $BUILDER radiobootreuse

define RADIO_WINXP_BUTTON gtk gtk_builder_get_object $BUILDER radiowinxp
define RADIO_WIN7_BUTTON gtk gtk_builder_get_object $BUILDER radiowin7
define OS_BOX gtk gtk_builder_get_object $BUILDER osbox

define BOOTINFO_LABEL gtk gtk_builder_get_object $BUILDER bootinfo
define BOOTPARAMS_ENTRY gtk gtk_builder_get_object $BUILDER bootparams
define BOOTPARAMS_HELP gtk gtk_builder_get_object $BUILDER bootparamshelp

define RADIO_ISO_BUTTON gtk gtk_builder_get_object $BUILDER radiosourceiso
define RADIO_CD_BUTTON gtk gtk_builder_get_object $BUILDER radiosourcecd
define ISOFILE_ENTRY gtk gtk_builder_get_object $BUILDER isofile

define CONFIRM_LABEL gtk gtk_builder_get_object $BUILDER confirm

gtk g_object_unref $BUILDER

### connect signals
gtk gtk_server_connect $MAIN_WINDOW delete-event finish
gtk gtk_server_connect $MAIN_NOTEBOOK switch-page switch-page
gtk gtk_server_connect $CANCEL_BUTTON clicked finish
gtk gtk_server_connect $INSTALL_BUTTON clicked install-clicked
gtk gtk_server_connect $NEXT_BUTTON clicked next-clicked
gtk gtk_server_connect $PREV_BUTTON clicked prev-clicked
gtk gtk_server_connect $HELP_BUTTON clicked help-clicked

gtk gtk_server_connect $REFRESH_BUTTON clicked refresh-clicked
gtk gtk_server_connect $PARTITION_VIEW cursor-changed partition-cursor-changed

gtk gtk_server_connect $CKBOX_SMALL_INITRD clicked small-initrd-clicked
gtk gtk_server_connect $GPARTED_BUTTON clicked gparted-clicked
gtk gtk_server_connect $RADIO_NONE_BUTTON clicked none-clicked
gtk gtk_server_connect $RADIO_EXT4_BUTTON clicked ext4-clicked
gtk gtk_server_connect $RADIO_EXT3_BUTTON clicked ext3-clicked
gtk gtk_server_connect $RADIO_EXT2_BUTTON clicked ext2-clicked
gtk gtk_server_connect $RADIO_FAT16_BUTTON clicked fat16-clicked
gtk gtk_server_connect $RADIO_FAT32_BUTTON clicked fat32-clicked
gtk gtk_server_connect $RADIO_NTFS_BUTTON clicked ntfs-clicked

gtk gtk_server_connect $RADIO_BOOT_MBR_BUTTON clicked mbr-clicked
gtk gtk_server_connect $RADIO_BOOT_PART_BUTTON clicked part-clicked
gtk gtk_server_connect $RADIO_BOOT_REUSE_BUTTON clicked reuse-clicked
gtk gtk_server_connect $RADIO_BOOT_NONE_BUTTON clicked bootnone-clicked

gtk gtk_server_connect $RADIO_WINXP_BUTTON clicked winxp-clicked
gtk gtk_server_connect $RADIO_WIN7_BUTTON clicked win7-clicked
gtk gtk_server_connect $BOOTPARAMS_HELP clicked bootparams-help

gtk gtk_server_connect $RADIO_ISO_BUTTON clicked iso-clicked
gtk gtk_server_connect $RADIO_CD_BUTTON clicked cd-clicked

### initialisation stuff
# we can only split initrd if the cli tool is available and if /tmp is more than 800MB
which fatdog-split-initrd.sh > /dev/null || gtk gtk_widget_hide $CKBOX_SMALL_INITRD
[ $(df -m | awk '$6 == "/tmp" { print $4 }') -lt $MIN_TMP_SIZE_FOR_SPLITTING ] && gtk gtk_widget_hide $CKBOX_SMALL_INITRD
load_partition_view
update_bootinfo


### show main window and enter main loop
gtk gtk_widget_show $MAIN_WINDOW
while true; do 
	define EVENT gtk gtk_server_callback wait
	case $EVENT in
		finish) break ;;
		switch-page) true ;; # stub - just so we can update buttons	
			
		install-clicked) 
			do_install && break ;;
		
		refresh-clicked) 
			load_partition_view ;;
			
		next-clicked)
			gtk gtk_notebook_next_page $MAIN_NOTEBOOK ;;
		prev-clicked)
			gtk gtk_notebook_prev_page $MAIN_NOTEBOOK ;;
		
		help-clicked)
			display_help ;;
			
		partition-cursor-changed)
			eval update_state $(get_selected_partition);;
		
		small-initrd-clicked)
			USE_SMALL_INITRD=
			gtk gtk_toggle_button_get_active $CKBOX_SMALL_INITRD
			[ $GTK = 1 ] && USE_SMALL_INITRD=yes ;;
			
		none-clicked|ext2-clicked|ext3-clicked|ext4-clicked|fat16-clicked|fat32-clicked|ntfs-clicked)
			FORMAT_FS=${EVENT%-*} ;;
		
		gparted-clicked)
			gparted /dev/$CURRENT_DISK
			load_partition_view ;;
		
		winxp-clicked|win7-clicked)
			CURRENT_OS=${EVENT%-*} ;;
			
		part-clicked|mbr-clicked|bootnone-clicked)
			BOOTLOADER=${EVENT%-*}
			update_bootinfo
			gtk gtk_widget_set_sensitive $OS_BOX 0 ;;
			
		reuse-clicked)
			BOOTLOADER=${EVENT%-*}
			update_bootinfo
			gtk gtk_widget_set_sensitive $OS_BOX 1 ;;
		
		iso-clicked|cd-clicked)
			INSTALL_SOURCE=${EVENT%-*}
			gtk gtk_widget_hide $ISOFILE_ENTRY
			[ "$INSTALL_SOURCE" = "iso" ] && gtk gtk_widget_show $ISOFILE_ENTRY ;;
		
		bootparams-help)
			defaultbrowser file:///usr/share/doc/faqs/boot-options.html ;;
			
	esac
	define ISOFILE gtk gtk_file_chooser_get_filename $ISOFILE_ENTRY
	define BOOTPARAMS gtk gtk_entry_get_text $BOOTPARAMS_ENTRY
	update_buttons
	update_confirmation
done
gtk gtk_server_exit 
