#!/bin/sh
# TazLito - SliTaz Live Tool.
#
# Tazlito is a tool to help generate and configure SliTaz Live CD
# ISO images. You can create a custom distro in one command from a list of
# packages, extract an existing ISO image to hack it, create a new initramfs
# and/or a new ISO. Most commands must be run by root, except the stats
# and the configuration file manipulation.
#
# (C) 2007-2022 SliTaz - GNU General Public License.
#
# Authors: see the AUTHORS file
#

VERSION=537

. /lib/libtaz.sh
# Force to use Busybox cpio and wget
alias cpio='busybox cpio'
alias wget='busybox wget'
alias stat='busybox stat'
alias awk='busybox awk'

# Tazlito configuration variables to be shorter
# and to use words rather than numbers.
COMMAND="$1"
LIST_NAME="$2"
TMP_DIR="/tmp/tazlito-$$-$RANDOM"
TMP_MNT="/media/tazlito-$$-$RANDOM"
TOP_DIR="$(pwd)"
INITRAMFS='rootfs.gz'
LOCALSTATE='/var/lib/tazpkg'
INSTALLED="$LOCALSTATE/installed"
CACHE_DIR='/var/cache/tazpkg'
MIRROR="$LOCALSTATE/mirror"
DEFAULT_MIRROR="http://mirror1.slitaz.org/packages/$(cat /etc/slitaz-release)/"
efi_img="EFI/esp.img"

log='/var/log/tazlito.log'
if [ $(id -u) -eq 0 ]; then
	newline > $log
fi


cleanup() {
	if [ -d "$TMP_MNT" ]; then
		umount $TMP_MNT
		rmdir $TMP_MNT
		rm -f /boot
	fi
	[ -d "$tmp_dir" ] && rm -r "$tmp_dir"
	[ -d "$flv_dir" ] && rm -r "$flv_dir"
}


# Report error and finish work

die() {
	emsg "<n>$(longline "$@")<n> " >&2
	cleanup
	exit 1
}


# Run Tazlito module
module() {
	local mod="$1"; shift
	/usr/libexec/tazlito/$mod $@
}



# Try to include config file, continue if command is gen-config or exit.
# The main config used by default is in /etc/tazlito.
# Specific distro config file can be put in a distro tree.
for i in /etc/tazlito "$TOP_DIR"; do
	[ -f "$i/tazlito.conf" ] && CONFIG_FILE="$i/tazlito.conf"
done

[ -z "$CONFIG_FILE" -a "$COMMAND" != 'gen-config' ] && \
	die 'Unable to find any configuration file.' \
		'Please read the docs or run `tazlito gen-config` to get an empty config file.'

. $CONFIG_FILE

# While Tazpkg is not used the default mirror URL file does not exist
# and user can't recharge the list of flavors.
[ $(id -u) -eq 0  -a  ! -f "$MIRROR" ] && echo "$DEFAULT_MIRROR" > $MIRROR

# Set the rootfs and rootcd path with $DISTRO
# configuration variable.
ROOTFS="$DISTRO/rootfs"
ROOTCD="$DISTRO/rootcd"




#####################
# Tazlito functions #
#####################


# Print the usage.

usage () {
	[ $(basename $0) = 'tazlito' ] && cat <<EOT

SliTaz Live Tool - Version: $(colorize 34 "$VERSION")

$(boldify "Usage:") tazlito [command] [list|iso|flavor|compression] [dir|iso]

$(boldify "Commands:")
EOT
optlist "\
usage			Print this short usage.
stats			View Tazlito and distro configuration statistics.
list-addfiles	Simple list of additional files in the rootfs.
gen-config		Generate a new configuration file for a distro.
configure		Configure the main config file or a specific tazlito.conf.
gen-iso			Generate a new ISO from a distro tree.
gen-initiso		Generate a new initramfs and ISO from the distro tree.
list-flavors	List all flavors available on the mirror.
gen-flavor		Generate a new Live CD description.
gen-liveflavor	Generate a Live CD description from current system.
show-flavor		Show Live CD description.
get-flavor		Get a flavor's list of packages (--noup to skip update).
upgrade-flavor	Update package list to the latest available versions.
extract-flavor	Extract a *.flavor file into $FLAVORS_REPOSITORY.
pack-flavor		Pack (and update) a flavor from $FLAVORS_REPOSITORY.
iso2flavor		Create a flavor file from a SliTaz ISO image.
extract-distro	Extract an ISO to a directory and rebuild Live CD tree.
gen-distro		Generate a Live distro and ISO from a list of packages.
clean-distro	Remove all files generated by gen-distro.
check-distro	Help to check if distro is ready to release.
writeiso		Use running system to generate a bootable ISO (with /home).
merge			Merge multiple rootfs into one ISO.
deduplicate		Deduplicate files in a tree.
repack			Recompress rootfs into ISO with maximum ratio.
build-loram		Generate a Live CD for low-RAM systems.
emu-iso			Emulate an ISO image with QEMU.
burn-iso		Burn ISO image to a CD-ROM using Wodim.
"
}


yesorno() {
	local answer
	echo -n "$1 (y=yes, n=no) [$2] " >&2
	case "$DEFAULT_ANSWER" in
		Y|y) answer="y";;
		N|n) answer="n";;
		*)
			read -t 30 answer
			[ -z "$answer" ] && answer="$2"
			[ "$answer" != 'y' -a "$answer" != 'n' ] && answer="$2"
			;;
	esac
	echo "$answer"
}


field() {
	grep "^$1" "$2" | \
	case "$1" in
		Desc*) sed 's|^.*: *||';;
		*)     sed 's/.*: \([0-9KMG\.]*\).*/\1/';;
	esac
}


todomsg() {
	echo -e "\\033[70G[ \\033[1;31mTODO\\033[0;39m ]"
}


hexdump_R() {
	sed '/ /!d;s/ *|.*//;s| *$||;s|  *|\\\\x|g;s|^[^\\]*|busybox echo -en |' | sh
}


# Download a file from this mirror

download_from() {
	local i mirrors="$1"
	shift
	for i in $mirrors; do
		case "$i" in
			http://*|ftp://*|https://*)
				wget -c $i$@ && break;;
			*)
				cp $i/$1 . && break;;
		esac
	done
}


# Download a file trying all mirrors

download() {
	local i
	for i in $(cat $MIRROR $LOCALSTATE/undigest/*/mirror 2>/dev/null); do
		download_from "$i" "$@" && break
	done
}


# Execute hooks provided by some packages

genisohooks() {
	local here="$(pwd)"
	for i in $(ls $ROOTFS/etc/tazlito/*.$1 2>/dev/null); do
		cd $ROOTFS
		. $i $ROOTCD
	done
	cd "$here"
}


# Echo the package name if the tazpkg is already installed

installed_package_name() {
	local tazpkg="$1" package VERSION EXTRAVERSION

	# Try to find package name and version to be able
	# to repack it from installation
	# A dash (-) can exist in name *and* in version
	package=${tazpkg%-*}
	i=$package
	while true; do
		unset VERSION EXTRAVERSION
		eval $(grep -s ^VERSION=      $INSTALLED/$i/receipt)
		eval $(grep -s ^EXTRAVERSION= $INSTALLED/$i/receipt)
		if [ "$i-$VERSION$EXTRAVERSION" = "$tazpkg" ]; then
			echo $i
			break
		fi
		case "$i" in
			*-*);;
			*) break;;
		esac
		i=${i%-*}
	done
}


# Check for the rootfs tree.

check_rootfs() {
	[ -d "$ROOTFS/etc" ] || die 'Unable to find a distro rootfs...'
}


# Check for the boot dir into the root CD tree.

verify_rootcd() {
	[ -d "$ROOTCD/boot" ] || die 'Unable to find the rootcd boot directory...'
}

set32() {
	for i in $(seq 0 8 $((${4:-32}-8))); do
		printf '\\\\x%02X' $((($2 >> i) & 255))
	done | xargs echo -en | dd bs=1 conv=notrunc of=$3 seek=$1 2>/dev/null
}

set64() {
	for i in $(seq 0 8 24 ; seq 24 -8 0); do
		printf '\\\\x%02X' $((($2 >> i) & 255))
	done | xargs echo -en | dd bs=1 conv=notrunc of=$3 seek=$1 2>/dev/null
}


first_block() {
	busybox stat -m "$1" | sed q
}



# Force size and location in the 2nd eltorito boot file (/$efi_img)

fix_efi_boot_img_size() {
	local n=$(($3/4))
	# Sector count on 16 bits (max 128MB), see offset 6-7 in page 11 of
	# https://pdos.csail.mit.edu/6.828/2014/readings/boot-cdrom.pdf
	[ $n -gt 65535 ] && echo "Warning: truncate EFI eltorito boot file $n" && n=65535
	set32 $((0x66+2048*$(first_block $2/boot/isolinux/boot.cat))) $n $1 16
	set32 $((0x1C+2048*$4)) $(($4*4)) $1
}


# Force the size for the /$efi_img file

fix_efi_img_size() {
	local e=$((0x809C))
	for i in $(echo ${efi_img//\// } | tr a-z A-Z) ; do
		local sz=$(get $((e+10)) "$2" 4)
		e=$(($(get $((e+2)) "$2" 4) * 2048))
		while [ $sz -gt 0 ]; do
			local len=$(get $e "$2")
			[ "$(dd if="$2" bs=1 skip=$((e+33)) count=${#i} \
				2>/dev/null)" = "$i" ] && continue 2
			[ $len -eq 0 ] && break
			sz=$((sz-len))
			e=$((e+len))
		done
		return	# not found
	done
	set64 $((e+10)) $1 "$2"
}


# create /$efi_img to share EFI files with the iso image

fixup_uefi_part() {
	[ -s $2/$efi_img ] || return
	[ -x $2/$efi_img ] && return
	local n=$(get 19 "$2/$efi_img")
	[ $n -eq 0 ] && n=$(get 32 "$2/$efi_img" 4)
	efiblock=$(first_block "$2/$efi_img")
	fix_efi_img_size $((n*512)) $1
	fix_efi_boot_img_size $1 $2 $n $efiblock
	
	# Build file list tree
	resv=$(get 14 "$2/$efi_img")
	if [ $(get 57 "$2/$efi_img" 1) -ne 49 ]; then
		skiphead=5
		fatsz=$(get 36 "$2/$efi_img" 4)
		basecluster=$(((resv+2*fatsz)/4+efiblock-2))
		dd if=$1 bs=512 skip=$((efiblock*4)) count=3 \
			of=$1 seek=$((efiblock*4+3)) conv=notrunc
	else
		skiphead=4
		fatsz=$(get 22 "$2/$efi_img")
		basecluster=$(((resv+2*fatsz)/4+efiblock-1))
	fi 2> /dev/null
	hd "$2/$efi_img" | awk 'BEGIN { skiphead='$skiphead' }
function setname() {
  if (name!="") return
  name=substr($0,62,8)
  i=index(name," ")
  if (i>0) name=substr($0,62,i-1)
  i=index(substr($0,70,3)," ")
  if (i==0) i=3
  if (i>1) name=name "." substr($0,70,i)
  name=tolower(name)
}
{
  if (skiphead!=0) {
    if ($1=="*") skiphead--
    next
  }
  if (skipdot!=0) {
    if (skipdot==2) up[cur]=$13 $12
    else cur=$13 $12
    skipdot=0
    next
  }
  if (($2=="2e" && $3=="20") || ($2=="2e" && $3=="2e" && $4=="20")) {
    if ($3=="2e") skipdot=2
    else skipdot=1
    next
  }
  if (gotname!=0) {
    path=""
    for (i=cur;i!="" && i!="0000";i=up[i]) path=dir[i] path
    if (gotname==2) dir[$13 $12]=name
    else print $1 " " $13 $12 " " path name
    gotname=0
    name=""
    next
  }
  if (s!="") {
    if (eos==0)
    for (i=2;i<18;i+=2) {
      if (i==12) i+=2
      if ($i=="00") break
      s=s substr($0,i+60,1)
    }
    name=s name
    s=""
    eos=0
    next
  }
  if ($13=="0f") {
    s=""
    for (i=3;i<16;i+=2) {
      if (i==13) i+=3
      if ($i=="00") { eos=1; break }
      s=s substr($0,i+60,1)
    }
    next
  }
  if ($13=="10") {
    setname()
    name=name "/"
    gotname=2
    next
  }
  if ($13=="20") {
    setname()
    gotname=1
    next
  }
  if ($13!="08" && $13!="00" && $13!="") exit
}
' | (	while read offset cluster file; do
		file="${file/efi/EFI}"
		cluster=$(($(first_block "$2/$file")-basecluster))
		set32 $((efiblock*2048+0x$offset+10)) $cluster "$1" 16
		set32 $((efiblock*2048+0x$offset+4)) $((cluster>>16)) "$1" 16
		echo "$cluster $((($(stat -c %s "$2/$file")+2047)/2048)) $file"
	done

	# Update fat12 or fat16
	get 57 "$2/$efi_img"
	dd if="$2/$efi_img" bs=512 count=$fatsz skip=$resv 2> /dev/null | \
	od -An -t u1 -w1 -v
    ) | awk '
{
  if (state==0) {
    if ($2=="") {
      if ($1==12849) fat=12
      else if ($1==13873) fat=16
      else fat=32
      state++
    }
    else {
      for (i=1;i<$2;i++) c[$1+i]=$1+i
      c[$1+$2]=268435455
    }
    next
  }
  if (state==1) {
    prev=$1
    state++
    next
  }
  if (state==2) {
    if (fat==12) {
      n=($1%16)*256+prev
      if (n!=0) c[pos+1]=n
      pos++
      prev=int($1/16)
      state++
      next
    }
    n=$1*256+prev
    if (fat==32) {
      prev=n
      state=13
      next
    }
  }
  else if (state==3) {
    n=$1*16+prev
  }
  else if (state==13) {
    prev=$1*65536+prev
    state++
    next
  }
  else n=$1*16777216+prev
  if (n!=0) c[pos+1]=n
  pos++
  state=1
}
END {
  for (i=1;i<=pos;i+=2) {
    if (c[i]=="") c[i]=0
    if (c[i+1]=="") c[i+1]=0
    if (fat==12) {
      printf "0  %02X %1X%1X ",c[i]%256,c[i+1]%16,(c[i]/256)%16
      if (i<pos) printf "%02X ",(c[i+1]/16)%256
      print "|"
    }
    else if (fat==16) printf "0  %02X %02X %02X %02X |\n",c[i]%256,(c[i]/256)%256,c[i+1]%256,(c[i+1]/256)%256
    else {
      printf "0  %02X %02X %02X %02X |\n",c[i]%256,(c[i]/256)%256,(c[i]/65536)%256,(c[i]/16777216)%256
      printf "0  %02X %02X %02X %02X |\n",c[i+1]%256,(c[i+1]/256)%256,(c[i+1]/65536)%256,(c[i+1]/16777216)%256
    }
  }
}' | hexdump_R | dd of="$1" seek=$((4*efiblock+fatsz+resv)) \
		conv=notrunc bs=512 > /dev/null 2>&1
	dd if="$1" of="$1" conv=notrunc bs=512 skip=$((4*efiblock+fatsz+resv)) \
		count=$fatsz seek=$((4*efiblock+resv)) > /dev/null 2>&1

	# Cleanup cache
	umount $2
	mount -o loop,ro $1 $2
}


# allocate efi.img stub to share EFI files in the EFI boot partition

alloc_uefi_part() {
	local basedir=$(dirname "$1")/..
	if [ -s $basedir/$efi_img ]; then
		chmod +x $basedir/$efi_img
		return
	fi
	local fclust=$({
	[ -d $basedir/EFI ] && find $basedir/EFI -type f -exec \
		busybox stat -c "%s %n" {} \; | grep -v $efi_img
	while [ -s "$1" ]; do
		local efifile
		case "$1" in
		*taz)	efifile=bootia32.efi ;;
		*taz64) efifile=bootx64.efi ;;
		esac
		if [ ! -s $basedir/EFI/boot/$efifile ] &&
		   [ $(get $((0x82)) "$1") = $((0x4550)) ]; then
			mkdir -p $basedir/EFI/boot 2> /dev/null
			ln "$1" "$basedir/EFI/boot/$efifile"
			stat -c "%s %n" "$1"
			for i in $basedir/boot/rootfs* ; do
				ln "$i" $basedir/EFI/boot/ &&
				stat -c "%s %n" "$i"
			done 2> /dev/null
			if [ ! -s $basedir/EFI/boot/linux.cmdline ]; then
				sed 's|/|\\|g' > $basedir/EFI/boot/linux.cmdline <<EOT
rw root=0x100$(sed '/bzImage/!d;s|.*root=[^ ]*||;q' $basedir/boot/isolinux/isolinux.cfg)\
$( ( cd $basedir/EFI/boot ; ls -r rootfs*gz ) | while read f ; do \
echo -n " initrd=/EFI/boot/$f";done)
EOT
				stat -c "%s %n" $basedir/EFI/boot/linux.cmdline
			fi
		fi
		shift
	done; } | sort | uniq | awk '{ n+=int(($1+2047)/2048) } END { print n }')
	[ ${fclust:-0} -eq 0 ] && return
	local dclust=$( (cd $basedir; find EFI -type d 2>/dev/null) | awk '
		BEGIN {
			FS="/"
		}
		NF > 1 {
			d[NF $NF]+=2
			d[NF-1 $(NF-1)]+=($NF+25)/13
		}
		END {
			for (i in d)
				n+=int((d[i]+63)/64)
			print n
		}')
	local clusters=$((fclust+dclust))
	if [ $clusters -lt 4068 ]; then
		fsect=$((((clusters+2)*3+1023)/1024))
		ftype="31 32"
		fhead="F8 FF FF"
		rsect=$(( 1+ ((2*fsect)-1)%4 ))
		fsz="$(printf "%04X" $fsect | sed 's/\(..\)\(..\)/\2 \1/')"
		rootsz=2
	elif [ $clusters -lt 65525 ]; then
		fsect=$(((clusters+2+255)/256))
		ftype="31 36"
		fhead="F8 FF FF FF"
		rsect=$(( 1+ ((2*fsect)-1)%4 ))
		fsz="$(printf "%04X" $fsect | sed 's/\(..\)\(..\)/\2 \1/')"
		rootsz=2
	else
		fsect=$(((clusters+2+127)/128))
		ftype="33 32"
		fhead="F8 FF FF 0F FF FF FF FF F8 FF FF 0F"
		rsect=$(( 6+ ((2*fsect)-6)%4 ))
		fsz="$(printf "%08X" $fsect | sed 's/\(..\)\(..\)\(..\)\(..\)/\4 \3 \2 \1/')"
		rootsz=1
	fi
	#       reserved +   fat*2   + root dir  +   dirs
	count=$(((rsect + fsect*2)/4 +  rootsz  + dclust ))
	s=$(((count+fclust)*4))
	if [ $s -gt 65535 ]; then
		size="00 00"
		size32="$(printf "%02X %02X %02X %02X" $((s%256)) \
			$(((s>>8)%256)) $(((s>>16)%256)) $(((s>>24)%256)) )"
	else
		size="$(printf "%02X %02X" $((s%256)) $(((s>>8)%256)) )"
		size32="00 00 00 00"
	fi
	dd if=/dev/zero bs=512 of=$basedir/$efi_img \
		count=$s 2> /dev/null

	# Create boot sector
	doslabel="$(echo "SLITAZ BOOT         " | od -v -N 11 -t x1 -w1 -An)"
	if [ "$ftype" = "33 32" ]; then
		hexdump_R <<EOT | dd of=$basedir/$efi_img \
			conv=notrunc
0  eb 58 90 53 6c 69 54 61  7a 00 00 00 02 04 $rsect 00  |
0  02 00 00 $size f8 00 00  20 00 40 00 00 00 00 00  |
0  $size32     $fsz         00 00 00 00 02 00 00 00  |
0  01 00 03 00 00 00 00 00  00 00 00 00 00 00 00 00  |  
0  80 00 29 00 00 00 00 $doslabel                    |
0        46 41 54 $ftype 20 20 20 cd 18 cd 19 eb fa  |
EOT
		while read ofs data; do
			echo "0 ${data//:/ } |" | hexdump_R | dd conv=notrunc \
			bs=1 seek=$ofs of=$basedir/$efi_img
		done <<EOT
510	55:aa:52:52:61:41
996	72:72:41:61:ff:ff:ff:ff:02
1022	55:aa
EOT
	else
		echo -en '\x55\xAA' | dd of=$basedir/$efi_img \
			seek=510 bs=1 conv=notrunc
		hexdump_R <<EOT | dd of=$basedir/$efi_img \
			conv=notrunc
0  eb 3c 90 53 6c 69 54 61  7a 00 00 00 02 04 $rsect 00  |
0  02 40 00 $size f8 $fsz   20 00 40 00 00 00 00 00  |
0  $size32     80 00 29 00  00 00 00 $doslabel       |
0                    46 41  54 $ftype 20 20 20 cd 18  |
0  cd 19 eb fa  |
EOT
	fi 2> /dev/null

	# Create fats
	echo "0  $fhead |" | hexdump_R | dd of=$basedir/$efi_img \
		seek=$((rsect)) bs=512 conv=notrunc 2> /dev/null
	echo "0  $fhead |" | hexdump_R | dd of=$basedir/$efi_img \
		seek=$((rsect+fsect)) bs=512 conv=notrunc 2> /dev/null

	# Add label
	echo "0  $doslabel 08 |" | hexdump_R | \
		dd of=$basedir/$efi_img bs=512 conv=notrunc \
		seek=$((rsect+fsect+fsect)) 2> /dev/null

	mkdir -p /tmp/mnt$$
	mount -o loop -t vfat $basedir/$efi_img /tmp/mnt$$
	( cd $basedir; find EFI -type d | cpio -o -H newc ) | \
		( cd /tmp/mnt$$ ; cpio -idmu 2> /dev/null )
	sync
	dd if=$basedir/$efi_img of=/tmp/fat$$ \
		skip=$rsect bs=512 count=$fsect 2> /dev/null
	( cd $basedir; find EFI -type f | grep -v $efi_img | cpio -o -H newc ) | \
		( cd /tmp/mnt$$ ; cpio -idmu 2> /dev/null )
	umount /tmp/mnt$$
	cat /tmp/fat$$ /tmp/fat$$ | dd of=$basedir/$efi_img \
		seek=$rsect bs=512 conv=notrunc 2> /dev/null
	rm /tmp/fat$$
	rmdir /tmp/mnt$$

	dd count=0 bs=2k of=$basedir/$efi_img \
		seek=$count 2> /dev/null
}


# isolinux.conf doesn't know the kernel version.
# We name the kernel image 'bzImage'.
# isolinux/syslinux first tries the '64' suffix with a 64bits cpu.

make_bzImage_hardlink() {
	if [   -e ${1:-.}/vmlinuz*slitaz ]; then
		rm -f ${1:-.}/bzImage 2>/dev/null
		ln    ${1:-.}/vmlinuz*slitaz ${1:-.}/bzImage
	fi
	if [   -e ${1:-.}/vmlinuz*slitaz64 ]; then
		rm -f ${1:-.}/bzImage64 2> /dev/null
		ln    ${1:-.}/vmlinuz*slitaz64 ${1:-.}/bzImage64
	fi
}


create_iso() {
	cd $2
	deduplicate
	sed -i "s|20[1-9][0-9]|$(date +%Y)|" $2/README $2/index.html

#	[ $(ls $2/boot/grub* 2> /dev/null | wc -l) -lt 2 ] && rm -rf $2/boot/grub*
	make_bzImage_hardlink $2/boot
	alloc_uefi_part $(ls -r $2/boot/vmlinuz*slitaz*)

	while read file; do ls -r $file 2> /dev/null; done << EOT | \
	awk 'BEGIN { n=100 } { print $1 " " n-- }' > /tmp/cdsort$$
$PWD/boot/isolinux/boot.cat
$PWD/boot/isolinux/isolinux.bin
$PWD/boot/isolinux/isolinux.cfg
$PWD/boot/isolinux/vesamenu.c32
$PWD/boot/isolinux/splash.*g
$PWD/boot/isolinux/i18n.cfg
$PWD/boot/isolinux/c32box.c32
$PWD/boot/isolinux/kbd
$PWD/$efi_img
$(find $PWD/EFI/ -type f 2> /dev/null | grep -v $PWD/EFI/boot/bzImage | \
grep -v $PWD/EFI/boot/rootfs | grep -v $PWD/EFI/boot/linux.cmdline | \
grep -v $PWD/$efi_img ; ls $PWD/boot/bzImage* )
$PWD/EFI/boot/linux.cmdline*
$(ls $PWD/boot/rootfs* | tac)
EOT

	action 'Computing md5...'
	touch $2/boot/isolinux/boot.cat
	find * -type f ! -name md5sum ! -name 'vmlinuz-*' -exec md5sum {} \; | \
		sort -k 2 > md5sum
	status

	cd - >/dev/null
	title 'Generating ISO image'

	_ 'Generating %s' "$1"
	uefi="$(cd $2 ; ls $efi_img 2> /dev/null)"
	genisoimage -R  -o $1  -hide-rr-moved -sort /tmp/cdsort$$ \
		-b boot/isolinux/isolinux.bin -c boot/isolinux/boot.cat \
		-no-emul-boot   -boot-load-size 4   -boot-info-table \
		${uefi:+-eltorito-alt-boot -efi-boot $uefi -no-emul-boot} \
		-V "${VOLUM_NAME:-SliTaz}"   -p "${PREPARED:-$(id -un)}" \
		-volset "SliTaz $SLITAZ_VERSION" -input-charset utf-8 \
		-A "tazlito $VERSION/$(genisoimage --version)" \
		-copyright README   -P "www.slitaz.org"  -no-pad  $2
	rm -f /tmp/cdsort$$
	dd if=/dev/zero bs=2k count=16 >> $1 2> /dev/null

	mkdir /tmp/mnt$$
	mount -o loop,ro $1 /tmp/mnt$$
	fixup_uefi_part $1 /tmp/mnt$$
	for i in boot/isolinux/isolinux.bin boot/isolinux/boot.cat \
		 ${uefi:+$efi_img} ; do
		sed -i "s|.* $i|$( cd /tmp/mnt$$ ; md5sum $i)|" $2/md5sum
	done
	dd if=$2/md5sum of=$1 conv=notrunc bs=2k \
	   seek=$(first_block /tmp/mnt$$/md5sum) 2> /dev/null
	umount -d /tmp/mnt$$
	rmdir /tmp/mnt$$

	if [ -s '/etc/tazlito/info' ]; then
		if [ $(stat -c %s /etc/tazlito/info) -lt $(( 31*1024 )) ]; then
			action 'Storing ISO info...'
			dd if=/etc/tazlito/info bs=1k seek=1 of=$1 conv=notrunc 2>/dev/null
			status
		fi
	fi

	if [ -x '/usr/bin/isohybrid' ]; then
		action 'Creating hybrid ISO...'
		isohybrid $1 $([ -n "$uefi" ] || echo -entry 2) 2>/dev/null
		status
	fi

	if [ -x '/usr/bin/iso2exe' ]; then
		echo 'Creating EXE header...'
		/usr/bin/iso2exe $1 2>/dev/null
	fi
}


# Generate a new ISO image using isolinux.

gen_livecd_isolinux() {
	# Some packages may want to alter iso
	genisohooks iso
	[ ! -f "$ROOTCD/boot/isolinux/isolinux.bin" ] && die 'Unable to find isolinux binary.'

	# Set date for boot msg.
	if grep -q 'XXXXXXXX' "$ROOTCD/boot/isolinux/isolinux.cfg"; then
		DATE=$(date +%Y%m%d)
		action 'Setting build date to: %s...' "$DATE"
		sed -i "s/XXXXXXXX/$DATE/" "$ROOTCD/boot/isolinux/isolinux.cfg"
		status
	fi

	cd $DISTRO
	create_iso $ISO_NAME.iso $ROOTCD

	action 'Creating the ISO md5sum...'
	md5sum $ISO_NAME.iso > $ISO_NAME.md5
	status

	separator
	# Some packages may want to alter final iso
	genisohooks final
}


lzma_history_bits() {
	#
	# This generates an ISO which boots with Qemu but gives
	# rootfs errors in frugal or liveUSB mode.
	#
#	local n
#	local sz
#	n=20	# 1Mb
#	sz=$(du -sk $1 | cut -f1)
#	while [ $sz -gt 1024 -a $n -lt 28 ]; do
#		n=$(( n + 1 ))
#		sz=$(( sz / 2 ))
#	done
#	echo $n
	echo ${LZMA_HISTORY_BITS:-24}
}


lzma_switches() {
	local proc_num=$(grep -sc '^processor' /proc/cpuinfo)
	echo "-d$(lzma_history_bits $1) -mt${proc_num:-1} -mc1000"
}


lzma_set_size() {
	# Update size field for lzma'd file packed using -si switch
	return # Need to fix kernel code?

	local n i
	n=$(unlzma < $1 | wc -c)
	for i in $(seq 1 8); do
		printf '\\\\x%02X' $((n & 255))
		n=$((n >> 8))
	done | xargs echo -en | dd of=$1 conv=notrunc bs=1 seek=5 2>/dev/null
}


align_to_32bits() {
	local size=$(stat -c %s ${1:-/dev/null})
	[ $((${size:-0} & 3)) -ne 0 ] &&
		dd if=/dev/zero bs=1 count=$((4 - (size & 3))) >> $1 2>/dev/null
}


dolz4() {
	[ "$(which lz4)" ] || tazpkg get-install lz4
	lz4 -9 > $1
}


dogzip() {
	gzip -9 > $1
	[ -x /usr/bin/advdef ] && advdef -qz4 $1
}


# Pack rootfs

pack_rootfs() {
	( cd $1; find . -print | cpio -o -H newc ) | \
	case "$COMPRESSION" in
		none)
			_ 'Creating %s without compression...' 'initramfs'
			cat > $2
			;;
		lz4)
			_ 'Creating %s with lz4 compression...' 'initramfs'
			dolz4 $2
			;;
		gzip)
			_ 'Creating %s with gzip compression...' 'initramfs'
			dogzip $2
			;;
		*)
			_ 'Creating %s with lzma compression...' 'initramfs'
			lzma e -si -so $(lzma_switches $1) > $2
			lzma_set_size $2
			;;
	esac
	align_to_32bits $2
	echo 1 > /tmp/rootfs
}


# Compression functions for writeiso.

write_initramfs() {
	case "$COMPRESSION" in
		lzma)
			_n 'Creating %s with lzma compression...' "$INITRAMFS"
			cpio -o -H newc | lzma e -si -so $(lzma_switches) > "/$INITRAMFS"
			align='y'
			lzma_set_size "/$INITRAMFS"
			;;
		gzip)
			_ 'Creating %s with gzip compression...' "$INITRAMFS"
			cpio -o -H newc | dogzip "/$INITRAMFS"
			;;
		lz4)
			_ 'Creating %s with lz4 compression...' "$INITRAMFS"
			cpio -o -H newc | dolz4 "/$INITRAMFS"
			;;
		*)
			# align='y'
			_ 'Creating %s without compression...' "$INITRAMFS"
			cpio -o -H newc > "/$INITRAMFS"
			;;
	esac < /tmp/list
	[ "$align" = 'y' -a -z "$noalign" ] && align_to_32bits "/$INITRAMFS"
	echo 1 > /tmp/rootfs
}


# Deduplicate files (MUST be on the same filesystem).

deduplicate() {
	find "${@:-.}" -xdev -type f ! -type l -size +0c -exec busybox stat -c '%s-%a-%u-%g %i %h %n' {} \; | sort | \
		(
			save=0; hardlinks=0; old_attr=""; old_inode=""; old_link=""; old_file=""; hinode=""
			while read attr inode link file; do
				if [ "$attr" = "$old_attr" -a "$inode" != "$old_inode" ] &&
				   { [ "$inode" = "$hinode" ] || cmp "$file" "$old_file" >/dev/null 2>&1; } ; then
					rm -f "$file"
					if ln "$old_file" "$file" 2>/dev/null; then
						hinode="$inode"
						inode="$old_inode"
						[ "$link" -eq 1 ] && hardlinks=$((hardlinks+1)) &&
							save="$((save+(${attr%%-*}+512)/1024))"
						continue
					else
						cp -p "$old_file" "$file"
					fi
				else
					hinode=""
				fi
				old_attr="$attr" ; old_inode="$inode" ; old_file="$file"
			done
			_ '%s Kbytes saved in %s duplicate files.' "$save" "$hardlinks"
		)

	find "$@" -xdev -type l -exec busybox stat -c '%s-%u-%g %i %h %n' {} \; | \
		while read attr inode link file; do
			echo "$attr-$(readlink "$file" | uuencode -m - | \
				sed '1d;:b;{N;s/\n//;bb;}') $inode $link $file"
		done | sort | \
		(
			old_attr=""; hardlinks=0;
			while read attr inode link file; do
				if [ "$attr" = "$old_attr" ]; then
					if [ "$inode" != "$old_inode" ]; then
						rm -f "$file"
						if ln "$old_file" "$file" 2>/dev/null; then
							[ "$link" -eq 1 ] && hardlinks=$((hardlinks+1))
						else
							cp -a "$old_file" "$file"
						fi
					fi
				else
					old_file="$file"
					old_attr="$attr"
					old_inode="$inode"
				fi
			done
			_ '%s duplicate symlinks.' "$hardlinks"
		)
}


# Generate a new initramfs from the root filesystem.

gen_initramfs() {
	# Just in case CTRL+c
	rm -f $DISTRO/gen

	# Some packages may want to alter rootfs
	genisohooks rootfs
	cd $1

	# Normalize file time
	find $1 -newer $1 -exec touch -hr $1 {} \;

	# Link duplicate files
	deduplicate

	# Use lzma if installed. Display rootfs size in realtime.
	rm -f /tmp/rootfs 2>/dev/null
	pack_rootfs . $DISTRO/$(basename $1).gz &
	sleep 2
	echo -en "\nFilesystem size:"
	while [ ! -f /tmp/rootfs ]; do
		sleep 1
		echo -en "\\033[18G$(du -sh $DISTRO/$(basename $1).gz | awk '{print $1}')    "
	done
	echo -e "\n"
	rm -f /tmp/rootfs
	cd $DISTRO
	mv $(basename $1).gz $ROOTCD/boot
}


distro_sizes() {
	if [ -n "$start_time" ]; then
		time=$(($(date +%s) - start_time))
		sec=$time
		div=$(( (time + 30) / 60))
		[ "$div" -ne 0 ] && min="~ ${div}m"
		_ 'Build time      : %ss %s' "$sec" "$min"
	fi
	cat <<EOT
Build date      : $(date +%Y%m%d)
Packages        : $(ls -1   $ROOTFS*$INSTALLED/*/receipt | wc -l)
Rootfs size     : $(du -csh $ROOTFS*/                    | awk 'END { print $1 }')
Initramfs size  : $(du -csh $ROOTCD/boot/rootfs*.gz      | awk 'END { print $1 }')
ISO image size  : $(du  -sh $ISO_NAME.iso                | awk '{ print $1 }')
EOT
	footer "Image is ready: $ISO_NAME.iso"
}


# Print ISO and rootfs size.

distro_stats() {
	title 'Distro statistics: %s' "$DISTRO"
	distro_sizes
}


# Create an empty configuration file.

empty_config_file() {
	cat >> tazlito.conf <<"EOF"
# tazlito.conf: Tazlito (SliTaz Live Tool) configuration file.
#

# Name of the ISO image to generate.
ISO_NAME=""

# ISO image volume name.
VOLUM_NAME="SliTaz"

# Name of the preparer.
PREPARED="$USER"

# Path to the packages repository and the packages.list.
PACKAGES_REPOSITORY=""

# Path to the distro tree to gen-distro from a list of packages.
DISTRO=""

# Path to the directory containing additional files
# to copy into the rootfs and rootcd of the LiveCD.
ADDFILES="$DISTRO/addfiles"

# Default answer for binary question (Y or N)
DEFAULT_ANSWER="ASK"

# Compression utility (lzma, gzip or none)
COMPRESSION="lzma"
EOF
}


# Extract rootfs.gz somewhere

extract_rootfs() {
	# Detect compression format: *.lzma.cpio, *.gzip.cpio, or *.cpio
	# First part (lzcat or zcat) may not fail, but cpio will fail on incorrect format
	(cd "$2"; lzcat "$1" | cpio -idm --quiet ) 2>/dev/null && return
	(cd "$2";  zcat "$1" | cpio -idm --quiet ) 2>/dev/null && return
	(cd "$2"; cpio -idm --quiet < "$1") 2>/dev/null
}


# Extract flavor file to temp directory

extract_flavor() {
	# Input: $1 - flavor name to extract;
	#        $2 = absent/empty: just extract 'outer layer'
	#        $2 = 'full': also extract 'inner' rootcd and rootfs archives, make files rename
	#        $2 = 'info': as 'full' and also make 'info' file to put into ISO
	# Output: temp dir path where flavor was extracted
	local f="$1.flavor" from to infos="$1.desc"
	[ -f "$f" ] || die "File '$f' not found"
	local dir="$(mktemp -d)"
	zcat "$f" | (cd $dir; cpio -i --quiet >/dev/null)

	if [ -n "$2" ]; then
		cd $dir

		[ -s "$1.receipt" ] && infos="$infos\n$1.receipt"

		for i in rootcd rootfs; do
			[ -f "$1.$i" ] || continue
			mkdir "$i"
			zcat "$1.$i" | (cd "$i"; cpio -idm --quiet 2>/dev/null)
			zcat "$1.$i" | cpio -tv 2>/dev/null > "$1.list$i"; infos="$infos\n$1.list$i"
			rm "$1.$i"
		done
		touch -t 197001010100.00 $1.*
		# Info to be stored inside ISO
		[ "$2" = info ] && echo -e $infos | cpio -o -H newc | dogzip info
		rm $1.list*

		# Renames
		while read from to; do
			[ -f "$from" ] || continue
			mv "$from" "$to"
		done <<EOT
$1.nonfree    non-free.list
$1.pkglist    packages.list
$1-distro.sh  distro.sh
$1.receipt    receipt
$1.mirrors    mirrors
$1.desc       description
EOT
	fi

	echo $dir
}


# Pack flavor file from temp directory

pack_flavor() {
	(cd "$1"; ls | grep -v err | cpio -o -H newc) | dogzip "$2.flavor"
}


# Remove duplicate files

files_match() {
	if [ -d "$1" ]; then
		return 1

	elif [ -L "$1" ] && [ -L "$2" ]; then
		[ "$(readlink "$1")" = "$(readlink "$2")" ] && return 0

	elif [ -f "$1" ] && [ -f "$2" ]; then
		cmp -s "$1" "$2" && return 0

		[ "$(basename "$3")" = 'volatile.cpio.gz' ] &&
		[ "$(dirname $(dirname "$3"))" = ".$INSTALLED" ] &&
			return 0

	elif [ "$(ls -l "$1"|cut -c1-10)$(stat -c '%a:%u:%g:%t:%T' "$1")" = \
	       "$(ls -l "$2"|cut -c1-10)$(stat -c '%a:%u:%g:%t:%T' "$2")" ]; then 
			return 0

	fi 2> /dev/null
	return 1
}

remove_with_path() {
	dir="$(dirname $1)"	
	rm -f "$1"
	while rmdir "$dir" 2> /dev/null; do
		dir="$(dirname $dir)"
	done
}

mergefs() {
	# Note, many packages have files with spaces in the name
	IFS=$'\n'

	local size1=$(du -hs "$1" | awk '{ print $1 }')
	local size2=$(du -hs "$2" | awk '{ print $1 }')
	action 'Merge %s (%s) into %s (%s)' "$(basename "$1")" "$size1" "$(basename "$2")" "$size2"

	# merge symlinks files and devices
	( cd "$1"; find . ) | \
	while read file; do
		files_match "$1/$file" "$2/$file" "$file" &&
		remove_with_path "$2/$file"
		[ -d "$1/$file" ] && [ -d "$2/$file" ] && rmdir "$2/$file" 2>/dev/null
	done

	unset IFS
	status
}


cleanup_merge() {
	rm -rf $TMP_DIR
	exit 1
}


# Update isolinux config files for multiple rootfs

update_bootconfig() {
	local files
	action 'Updating boot config files...'
	files="$(grep -l 'include common' $1/*.cfg)"
	for file in $files; do
		awk -v n=$(echo $2 | awk '{ print NF/2 }') '{
			if (/label/) label=$0;
			else if (/kernel/) kernel=$0;
			else if (/append/) {
				i=index($0,"rootfs.gz");
				append=substr($0,i+9);
			}
			else if (/include/) {
				for (i = 1; i <= n; i++) {
					print label i
					print kernel;
					initrd="initrd=/boot/rootfs" n ".gz"
					for (j = n - 1; j >= i; j--) {
						initrd=initrd ",/boot/rootfs" j ".gz";
					}
					printf "\tappend %s%s\n",initrd,append;
					print "";
				}
				print;
			}
			else print;
		}' < $file > $file.$$
		mv -f $file.$$ $file
	done
	sel="$(echo $2 | awk '{
		for (i=1; i<=NF; i++)
			if (i % 2 == 0) printf " slitaz%d", i/2
			else printf " %s", $i
	}')"

	[ -s $1/common.cfg ] && cat >> $1/common.cfg <<EOT

label slitaz
	kernel /boot/isolinux/ifmem.c32
	append$sel noram

label noram
	config noram.cfg

EOT

	# Update vesamenu
	if [ -s "$1/isolinux.cfg" ]; then
		files="$files $1/isolinux.cfg"
		awk -v n=$(echo $2 | awk '{ print NF/2 }') -v "sel=$sel" '
		BEGIN {
			kernel = "	COM32 c32box.c32"
		}
		{
			     if (/ROWS/)       print "MENU ROWS "       n+$3;
			else if (/TIMEOUTROW/) print "MENU TIMEOUTROW " n+$3;
			else if (/TABMSGROW/)  print "MENU TABMSGROW "  n+$3;
			else if (/CMDLINEROW/) print "MENU CMDLINEROW " n+$3;
			else if (/VSHIFT/) {
				x = $3-n;
				if (x < 0) x = 0;
				print "MENU VSHIFT " x;
			}
			else if (/rootfs.gz/) {
				linux = "";
				if (/bzImage/) linux = "linux /boot/bzImage ";
				i = index($0, "rootfs.gz");
				append = substr($0, i+9);
				printf "\tkernel /boot/isolinux/ifmem.c32\n";
				printf "\tappend%s noram\n", sel;
				printf "\nlabel noram\n\tMENU HIDE\n\tconfig noram.cfg\n\n";
				for (i = 1; i <= n; i++) {
					print "LABEL slitaz" i
					printf "\tMENU LABEL SliTaz slitaz%d Live\n", i;
					printf "%s\n",  kernel;
					initrd = "initrd=/boot/rootfs" n ".gz"
					for (j = n - 1; j >= i; j--) {
						initrd = initrd ",/boot/rootfs" j ".gz";
					}
					printf "\tappend %s%s%s\n\n", linux, initrd, append;
				}
			}
			else if (/bzImage/) kernel = $0;
			else print;
		}' < $1/isolinux.cfg > $1/isolinux.cfg.$$
		mv $1/isolinux.cfg.$$ $1/isolinux.cfg
	fi

	[ -s $1/c32box.c32 ] && sed -i -e '/kernel.*ifmem/d' \
		-e 's/append \([0-9]\)/append ifmem \1/' $1/isolinux.cfg
	cat > $1/noram.cfg <<EOT
implicit 0
prompt 1
timeout 80
$(grep '^F[0-9]' $1/isolinux.cfg)

$([ -s $1/isolinux.msg ] && echo display isolinux.msg)
say Not enough RAM to boot slitaz. Trying hacker mode...
default hacker
label hacker
	KERNEL /boot/bzImage
	append rw root=/dev/null vga=normal

label reboot
EOT

	if [ -s $1/c32box.c32 ]; then
		cat >> $1/noram.cfg <<EOT
	COM32 c32box.c32
	append reboot

label poweroff
	COM32 c32box.c32
	append poweroff

EOT
	else
		echo "	com32 reboot.c32" >> $1/noram.cfg
	fi

	# Restore real label names
	[ -s $1/common.cfg ] && files="$1/common.cfg $files"
	echo $2 | awk '{ for (i=NF; i>1; i-=2) printf "%d/%s\n",i/2,$i }' | \
	while read pat; do
		sed -i "s/slitaz$pat/" $files
	done
	status
}


# Uncompress rootfs or module to stdout

uncompress() {
	zcat $1 2> /dev/null || xzcat $1 2> /dev/null ||
	{ [ $(od -N 1 -An $1) -eq 135 ] && unlzma < $1; } || cat $1
}


# Install a missing package

install_package() {
	if [ -z "$2" ]; then
		answer=$(yesorno "$(_ 'Install package %s?' "$1")" 'n')
	else
		answer=$(yesorno "$(_n 'Install package %s for Kernel %s? ' "$1" "$2")" 'n')
	fi
	case "$answer" in
		y)
			# We don't want package on host cache.
			action 'Getting and installing package: %s' "$1"
			yes y | tazpkg get-install $1 --quiet 2>&1 >> $log || exit 1
			status ;;
		*)
			return 1 ;;
	esac
}


# Check iso for loram transformation

check_iso_for_loram() {
	[ -s "$TMP_DIR/iso/boot/rootfs.gz" ] ||
	[ -s "$TMP_DIR/iso/boot/rootfs1.gz" ]
}


# Remove duplicated files in $1/EFI/boot from $1/boot

cleanup_efi_boot() {
	for i in $1/$efi_img $1/boot/isolinux/efi.img ; do
		[ -s $i ] && [ ! -x $i ] && rm -f $i
	done
	for i in $1/EFI/boot/* ; do
		[ -f $i ] || continue
		case "$i" in
		*/rootfs*)	cmp $i ${i/\/EFI\//\/} || continue ;;
		*/bootia32.efi)	cmp -s $i $1/boot/bzImage || continue
				rm $1/EFI/boot/linux.cmdline ;;
		*/bootx64.efi)	cmp -s $i $1/boot/bzImage64 || continue
				rm $1/EFI/boot/linux.cmdline* ;;
		esac
		rm -f $i
		rmdir $1/EFI/boot && rmdir $1/EFI
	done 2> /dev/null
}


# Build initial rootfs for loram ISO ram/cdrom/http

build_initfs() {
	urliso="mirror.switch.ch/ftp/mirror/slitaz \
download.tuxfamily.org/slitaz mirror1.slitaz.org mirror2.slitaz.org \
mirror3.slitaz.org mirror.slitaz.org"
	version=$(ls $TMP_DIR/iso/boot/vmlinuz-* | sed 's/.*vmlinuz-//')
	[ -z "$version" ] && die "Can't find the kernel version." \
		'No file /boot/vmlinuz-<version> in ISO image. Abort.'

	[ -s /usr/share/boot/busybox-static ] || install_package busybox-static
	need_lib=false
	for i in bin dev run mnt proc tmp sys lib/modules/64; do
		mkdir -p $TMP_DIR/initfs/$i
	done
	ln -s bin $TMP_DIR/initfs/sbin
	ln -s .   $TMP_DIR/initfs/usr
	for aufs in aufs overlayfs; do
		for v in $version ; do
			[ -f /lib/modules/$v/kernel/fs/$aufs/$aufs.ko.?z ] && continue
			install_package ${v/*taz/linux}-$aufs $v && continue
		done
		install_package $aufs $version && break
	done || return 1
	[ -s /init ] || install_package slitaz-boot-scripts
	cp /init $TMP_DIR/initfs/
	for v in $version ; do
		cp /lib/modules/$v/kernel/fs/$aufs/$aufs.ko.?z \
			$TMP_DIR/initfs/lib/modules/${v/*taz/}
	done
	if [ "$1" = 'cdrom' ]; then
		sed -i '/mod squashfs/d' $TMP_DIR/initfs/init
	else
		[ ! -f /usr/sbin/mksquashfs ] && ! install_package squashfs && return 1
		for v in $version ; do
			if [ ! -f /lib/modules/$v/kernel/fs/squashfs/squashfs.ko.?z ]; then
				install_package linux-squashfs $v || return 1
			fi
		done
		for v in $version ; do
			cp /lib/modules/$v/kernel/fs/squashfs/squashfs.ko.?z \
				$TMP_DIR/initfs/lib/modules/${v/*taz/}
		done
		#ls /sbin/unsquashfs /usr/lib/liblzma.so* $INSTALLED/squashfs/* | \
		#cpio -o -H newc > $TMP_DIR/initfs/extractfs.cpio
	fi
	if [ "$1" = 'http' ]; then
		mkdir $TMP_DIR/initfs/etc $TMP_DIR/fs
		ln -s /proc/mounts $TMP_DIR/initfs/etc/mtab
		cp /usr/share/udhcpc/default.script $TMP_DIR/initfs/lib/udhcpc
		sed -i 's|/sbin/||;s/^logger/#&/' $TMP_DIR/initfs/lib/udhcpc
		cp -a /dev/fuse $TMP_DIR/initfs/dev
		if ! $need_lib && [ -x /usr/share/boot/fusermount-static ]; then
			cp /usr/share/boot/fusermount-static $TMP_DIR/initfs/bin/fusermount
		else
			need_lib=true
		fi
		if ! $need_lib && [ -x /usr/share/boot/httpfs-static ]; then
			cp /usr/share/boot/httpfs-static $TMP_DIR/initfs/bin/httpfs
		else
			[ ! -f /usr/bin/httpfs ] && ! install_package httpfs-fuse && return 1
			cp /usr/bin/httpfs      $TMP_DIR/initfs/bin
			cp /usr/bin/fusermount  $TMP_DIR/initfs/bin
			cp -a /lib/librt*       $TMP_DIR/initfs/lib
			cp -a /lib/libdl*       $TMP_DIR/initfs/lib
			cp -a /lib/libpthread*  $TMP_DIR/initfs/lib
			cp -a /usr/lib/libfuse* $TMP_DIR/initfs/lib
			cp -a /lib/libresolv*   $TMP_DIR/initfs/lib
			cp -a /lib/libnss_dns*  $TMP_DIR/initfs/lib
			need_lib=true
		fi
		cd $TMP_DIR/fs
		echo 'Getting slitaz-release & ethernet modules...'
		for i in $(ls -r $TMP_DIR/iso/boot/rootfs*z); do
			uncompress $i | cpio -idmu etc/slitaz-release lib/modules rootfs*
			[ -s rootfs* ] || continue
			unsquashfs -f -d . rootfs* rootfs* etc/slitaz-release lib/modules &&
			rm -f rootfs*
		done 2>&1 > /dev/null
		cd - > /dev/null
		cp $TMP_DIR/fs/etc/slitaz-release $TMP_DIR/initfs/etc/
		for v in $version ; do
			find $TMP_DIR/fs/lib/modules/$v/kernel/drivers/net/ethernet \
					-type f -name '*.ko*' | while read mod; do
				f=$TMP_DIR/initfs/lib/modules/${v/*taz/}/$(basename $mod | sed s/..z$//)
				uncompress $mod > $f
				grep -q alias=pci: $f || rm -f $f
				done
			for i in $TMP_DIR/initfs/lib/modules/${v/*taz/}/*.ko ; do
				f=$(basename $i)..z
				grep -q $f:$ $TMP_DIR/fs/lib/modules/$v/modules.dep && continue
				deps="$(grep $f: $TMP_DIR/fs/lib/modules/$v/modules.dep | sed 's/.*: //')"
				echo "$deps" | sed 's|kernel/[^ ]*/||g;s/.ko..z//g' > ${i/.ko/}.dep
				for j in $deps; do
					mod=$(ls $TMP_DIR/fs/lib/modules/$v/$j)
					uncompress $mod > $(echo $j | sed s/..z$//)
				done
			done
		done
		longline "Default URLs for /iso/$(cat $TMP_DIR/initfs/etc/slitaz-release)/flavors/slitaz-loram-cdrom.iso /iso/$(cat $TMP_DIR/initfs/etc/slitaz-release)/flavors/slitaz-$(cat $TMP_DIR/initfs/etc/slitaz-release)-loram-cdrom.iso: $urliso"
		_n 'List of URLs to insert: '
		read -t 30 urliso2
		urliso="$urliso2 $urliso"
	fi
	if ! $need_lib && [ -x /usr/share/boot/busybox-static ]; then
		cp /usr/share/boot/busybox-static $TMP_DIR/initfs/bin/busybox
		sed -i 's/LD_T.*ot/echo/;s/".*ld-.*) /"/' $TMP_DIR/initfs/init
	else
		cp /bin/busybox $TMP_DIR/initfs/bin
		if ! cmp /bin/busybox /sbin/insmod > /dev/null ; then
			cp /sbin/insmod $TMP_DIR/initfs/bin
			cp -a /lib/libkmod.so.* $TMP_DIR/initfs/lib
			cp -a /usr/lib/liblzma.so.* $TMP_DIR/initfs/lib
			cp -a /usr/lib/libz.so.* $TMP_DIR/initfs/lib
		fi
		need_lib=true
	fi
	bins="sh switch_root"
	[ "$(echo ls | chroot $TMP_DIR/initfs busybox sh 2> /dev/null)" ] ||
	bins="$($TMP_DIR/initfs/bin/busybox | awk \
	  '{ if (s) printf "%s",$0 } /Currently/ { s=1 }' | sed 's/,//g')"
	for i in $bins ; do
		ln $TMP_DIR/initfs/bin/busybox $TMP_DIR/initfs/bin/$i
	done
	# bootfloppybox will need floppy.ko.?z, /dev/fd0, /dev/tty0
	for v in $version ; do
		cp /lib/modules/$v/kernel/drivers/block/floppy.ko.?z \
			$TMP_DIR/initfs/lib/modules/${v/*taz/} 2>/dev/null
	done
	for i in /dev/console /dev/null /dev/tty /dev/tty0 /dev/zero \
		 /dev/kmem /dev/mem /dev/random /dev/urandom; do
		cp -a $i $TMP_DIR/initfs/dev
	done
	grep -q '/sys/block/./dev' $TMP_DIR/initfs/init ||
	for i in /dev/fd0 /dev/[hs]d[a-f]* /dev/loop* ; do
		cp -a $i $TMP_DIR/initfs/dev
	done 2>/dev/null
	$need_lib && for i in /lib/ld-* /lib/lib[cm][-\.]* ; do
		cp -a $i $TMP_DIR/initfs/lib
	done
	[ "$1" = 'http' ] && cat > $TMP_DIR/initfs/init <<EOTEOT
#!/bin/sh

getarg() {
	grep -q " \$1=" /proc/cmdline || return 1
	eval \$2=\$(sed "s/.* \$1=\\\\([^ ]*\\\\).*/\\\\1/" < /proc/cmdline)
	return 0
}

copy_rootfs() {
	total=\$(grep MemTotal /proc/meminfo | sed 's/[^0-9]//g')
	need=\$(du -c \${path}rootfs* | tail -n 1 | cut -f1)
	[ \$(( total / need )) -gt 1 ] || return 1
	if ! grep -q " keep-loram" /proc/cmdline && cp \${path}rootfs* /mnt; then
		path=/mnt/
		return 0
	else
		rm -f /mnt/rootfs*
		return 1
	fi
}

echo "Switching / to tmpfs..."
mount -t proc proc /proc
size="\$(grep rootfssize= < /proc/cmdline | \\
	sed 's/.*rootfssize=\\([0-9]*[kmg%]\\).*/-o size=\\1/')"
[ -n "\$size" ] || size="-o size=90%"

mount -t sysfs sysfs /sys
for i in /lib/modules/*/*.ko /lib/modules/*.ko ; do
	[ -s \$i ] && continue
	echo -en "Probe \$i           \\r"
	for j in \$(grep alias=pci: \$i | sed 's/alias//;s/\*/.*/g'); do
		grep -q "\$j" /sys/bus/pci/devices/*/uevent || continue
		for k in \$(cat \${i/ko/dep} 2> /dev/null); do
			insmod /lib/modules/\$k.ko 2> /dev/null
		done
		echo "Loading \$i"
		insmod \$i 2> /dev/null
		break
	done
done
umount /sys
while read var default; do
	eval \$var=\$default
	getarg \$var \$var
done <<EOT
eth	eth0
dns	208.67.222.222,208.67.220.220
netmask	255.255.255.0
gw
ip
EOT
grep -q \$eth /proc/net/dev || sh
if [ -n "\$ip" ]; then
	ifconfig \$eth \$ip netmask \$netmask up
	route add default gateway \$gw
	for i in \$(echo \$dns | sed 's/,/ /g'); do
		echo "nameserver \$i" >> /etc/resolv.conf
	done
else
	udhcpc -f -q -s /lib/udhcpc -i \$eth
fi
for i in $urliso ; do
	[ -n "\$URLISO" ] && URLISO="\$URLISO,"
	URLISO="\${URLISO}http://\$i/iso/\$(cat /etc/slitaz-release)/flavors/slitaz-loram-cdrom.iso,http://\$i/iso/\$(cat /etc/slitaz-release)/flavors/slitaz-\$(cat /etc/slitaz-release)-loram-cdrom.iso"
	URLISO="\$URLISO,http://\$i/iso/rolling/slitaz-rolling-loram-cdrom.iso,http://\$i/iso/rolling/slitaz-rolling-loram.iso"
done
getarg urliso URLISO
DIR=fs
if getarg loram DIR; then
	DEVICE=\${DIR%,*}
	DIR=/\${DIR#*,}
fi
mount -t tmpfs \$size tmpfs /mnt
path2=/mnt/.httpfs/
path=/mnt/.cdrom/
mkdir -p /mnt/.rw /mnt/.wd \$path \$path2
while [ ! -d \$path/boot ]; do
	for i in \$(echo \$URLISO | sed 's/,/ /g'); do
		httpfs \$i \$path2 && echo \$i && break
	done
	mount -o loop,ro -t iso9660 \$path2/*.iso \$path || sh
done

memfree=\$(grep MemFree /proc/meminfo | sed 's/[^0-9]//g')
umount /proc
branch=:/mnt/.cdrom/\$DIR
if [ ! -d /mnt/.cdrom/\$DIR/etc ]; then
branch=
lp=1
for i in /lib/modules/*/squashfs.ko /lib/modules/squashfs.ko ; do
	[ -s \$i ] && insmod \$i
done 2> /dev/null
for i in \${path}boot/rootfs?.* ; do
	fs=\${i#*root}
	branch=\$branch:/mnt/.\$fs
	mkdir -p /mnt/.rw/mnt/.\$fs /mnt/.\$fs /mnt/.rw/mnt/.cdrom
	losetup -o 124 /dev/loop\$lp \$i
	mount -o loop,ro -t squashfs /dev/loop\$lp /mnt/.\$fs
	lp=\$((lp+1))
done
fi
mkdir -p /mnt/.rw/mnt/.httpfs
while read type opt; do
	for i in /lib/modules/*/\$type.ko /lib/modules/\$type.ko ; do
		[ -s \$i ] && insmod \$i &&
		mount -t \$type -o \$opt none /mnt && break 2
	done
done <<EOT
aufs		br=/mnt/.rw\$branch
overlayfs	workdir=/mnt/.wd\${branch/:/,lowerdir=},upperdir=/mnt/.rw
EOT
rm -rf /lib/modules
[ -x /bin/httpfs ] && sed -i 's/DHCP="yes"/DHCP="no"/' /mnt/etc/network.conf
[ \$memfree -lt 30000 ] && sed -i 's/ slim//' /mnt/etc/rcS.conf
[ -x /mnt/sbin/init ] && exec /bin/switch_root mnt /sbin/init || sh
EOTEOT
	chmod +x $TMP_DIR/initfs/init
	for i in $TMP_DIR/initfs/lib/modules/*z $TMP_DIR/initfs/lib/modules/*/*z ; do
		[ -s $i ] || continue
		unxz $i || gunzip $i || lzma d $i ${i%.gz}
		rm -f $i
	done 2>/dev/null
	(cd $TMP_DIR/initfs; find . | busybox cpio -o -H newc 2>/dev/null) | \
	lzma e $TMP_DIR/initfs.gz -si
	lzma_set_size $TMP_DIR/initfs.gz
	rm -rf $TMP_DIR/initfs
	align_to_32bits $TMP_DIR/initfs.gz
	return 0
}


# Move each initramfs to squashfs

build_loram_rootfs() {
	rootfs_sizes=""
	for i in $(ls -r $TMP_DIR/iso/boot/rootfs*); do
		mkdir -p $TMP_DIR/fs
		cd $TMP_DIR/fs
		uncompress $i | cpio -idm
		deduplicate
		cd - > /dev/null
		rootfs=$TMP_DIR/$(basename $i 64)
		[ $rootfs = $TMP_DIR/rootfs.gz ] && rootfs=$TMP_DIR/rootfs0.gz
		/usr/sbin/mksquashfs $TMP_DIR/fs $rootfs -comp ${1:-xz -Xbcj x86}
		cd $TMP_DIR
		rootfs_sizes="$rootfs_sizes $(( $(du -s $TMP_DIR/fs | cut -f1) - $(du -s $rootfs | cut -f1) ))"
		( cd $(dirname $rootfs); echo $(basename $rootfs) | cpio -o -H newc ) > $rootfs.cpio
		[ $(ls -a fs | wc -l) -le 2 ] && cpio -o -H newc > $rootfs.cpio < /dev/null
		case "$i" in *64) rootfs=${rootfs}64 ;; esac
		rm -f $rootfs
		mv ${rootfs%64}.cpio $rootfs
		cd - > /dev/null
		rm -rf $TMP_DIR/fs
	done
}


# Move meta boot configuration files to basic configuration files
# because meta loram flavor is useless when rootfs is not loaded in RAM

unmeta_boot() {
	local root=${1:-$TMP_DIR/loramiso}
	if [ -f $root/boot/isolinux/noram.cfg ]; then
		# We keep enough information to do unloram...
		[ -s $root/boot/isolinux/common.cfg ] &&
		sed -i 's/label slitaz/label orgslitaz/' \
			$root/boot/isolinux/common.cfg
		set -- $(grep 'append ifmem [0-9]' $root/boot/isolinux/isolinux.cfg)
		shift
		sed -i '/ifmem/{NNNNNNNNd};/^LABEL/{N;/LABEL SliTaz [^L]/{NNNd}}' \
			$root/boot/isolinux/isolinux.cfg
		[ -n "$3" ] || set -- $(grep 'append [0-9]' $root/boot/isolinux/common.cfg)
		sed -i "s/label $3\$/label slitaz/;s|=\(.*rootfs\)\(.*\)\.gz |=\1.gz |" \
			$root/boot/isolinux/*.cfg
	fi
}


# Move rootfs to squashfs filesystem(s) to the cdrom writeable with aufs/overlayfs.
# These squashfs may be loaded in RAM at boot time.
# Rootfs are also copied to CD-ROM for tiny ramsize systems.
# Meta flavors are converted to normal flavors.

build_loram_cdrom() {
	build_initfs cdrom || return 1
	cp -a $TMP_DIR/iso $TMP_DIR/loramiso
	cleanup_efi_boot $TMP_DIR/loramiso
	mkdir $TMP_DIR/loramiso/fs
	cd $TMP_DIR/loramiso/fs
	for i in $( ls ../boot/root* | sort -r ) ; do
		uncompress $i | cpio -idmu
		rm -f $i
	done
	mkdir -p $TMP_DIR/loramiso/fs/mnt/.cdrom
	cd - >/dev/null
	mv $TMP_DIR/initfs.gz $TMP_DIR/loramiso/boot/rootfs.gz
	unmeta_boot
	VOLUM_NAME="SliTaz_LoRAM_CDROM"
	busybox sed -i "s|root=|isofs= rodev=/dev/cdrom/fs &|;s/.ive/cdrom/" \
		$TMP_DIR/loramiso/boot/isolinux/*.cfg
	busybox sed -i '/LABEL slitaz/{NNNNp;s|z cdrom|& text|;s|L slitaz|&text|;s|autologin|screen=text &|;s|,[^ ]*||}' \
		$TMP_DIR/loramiso/boot/isolinux/*.cfg
	create_iso $OUTPUT $TMP_DIR/loramiso
}


# Create http bootstrap to load and remove loram_cdrom
# Meta flavors are converted to normal flavors.

build_loram_http() {
	build_initfs http || return 1
	cp -a $TMP_DIR/iso $TMP_DIR/loramiso
	cleanup_efi_boot $TMP_DIR/loramiso
	rm -f $TMP_DIR/loramiso/boot/rootfs*
	mv $TMP_DIR/initfs.gz $TMP_DIR/loramiso/boot/rootfs.gz
	unmeta_boot
	create_iso $OUTPUT $TMP_DIR/loramiso
}


# Update meta flavor selection sizes.
# Reduce sizes with rootfs gains.

update_metaiso_sizes() {
	[ -s $TMP_DIR/loramiso/boot/rootfs0.gz ] &&
		sed -i 's|rootfs.gz|&,/boot/rootfs0.gz|' $TMP_DIR/loramiso/boot/isolinux/*.cfg
	for cfg in $(grep -El '(append|ifmem) [0-9]' $TMP_DIR/loramiso/boot/isolinux/*.cfg)
	do
		local append="$(grep -E '(append|ifmem) [0-9]' $cfg)"
		local sizes="$rootfs_sizes"
		local new
		set -- $append
		shift
		[ "$1" = "ifmem" ] && shift
		new=""
		while [ -n "$2" ]; do
			local s
			case "$1" in
				*G) s=$(( ${1%G} * 1024 * 1024 ));;
				*M) s=$(( ${1%M} * 1024 ));;
				*)  s=${1%K};;
			esac
			sizes=${sizes#* }
			for i in $sizes ; do
				s=$(( s - i ))
			done
			new="$new $s $2"
			shift 2
		done
		busybox sed -i -e "/append [0-9]/s/append .*/append$new $1/" -e \
			"/append ifmem [0-9]/s/append .*/append ifmem$new $1/" $cfg
		busybox sed -i 's|\(initrd=\)\([^r]*\)\(rootfs\)|\1\2rootfs.gz,\2\3|' $cfg
		busybox sed -i '/LABEL base/{NNNNp;s|base .ive|cdrom|;s|base|cdrom|;s|,[^ ]*||}' $cfg
		busybox sed -i '/LABEL cdrom/{NNNNp;s|z cdrom|& text|;s|L cdrom|&text|;s|autologin|screen=text &|;s|,[^ ]*||}' $cfg
	done
}


# Move rootfs to a squashfs filesystem into the initramfs writeable with aufs/overlayfs.
# Meta flavor selection sizes are updated.

build_loram_ram() {
	build_initfs ram || return 1
	build_loram_rootfs    "$1"
	cp -a $TMP_DIR/iso    $TMP_DIR/loramiso
	cleanup_efi_boot      $TMP_DIR/loramiso
	mv $TMP_DIR/initfs.gz $TMP_DIR/loramiso/boot/rootfs.gz
	cp $TMP_DIR/rootfs*   $TMP_DIR/loramiso/boot
	update_metaiso_sizes
	create_iso $OUTPUT    $TMP_DIR/loramiso
}


# Remove files installed by packages

find_flavor_rootfs() {
	for i in $1/etc/tazlito/*.extract; do
		[ -e $i ] || continue
		chroot $1 /bin/sh ${i#$1}
	done

	# Clean hardlinks and files patched by genisofs in /boot
	for i in isolinux/isolinux.bin isolinux/boot.cat bzImage ; do
		rm -f $1/boot/$i*
	done

	# Clean files generated in post_install
	rm -f $1/lib/modules/*/modules.* $1/etc/mtab \
	      $1/dev/core $1/dev/fd $1/dev/std*

	# Verify md5
	cat $1$INSTALLED/*/md5sum | \
	while read md5 file; do
		[ -e "$1$file" ] || continue
		[ "$(md5sum < "$1$file")" = "$md5  -" ] &&
		rm -f "$1$file"
	done

	# Check configuration files
	for i in $1$INSTALLED/*/volatile.cpio.gz; do
		[ -e $i ] || continue
		mkdir /tmp/volatile$$
		zcat $i | ( cd /tmp/volatile$$ ; cpio -idmu > /dev/null 2>&1 )
		( cd /tmp/volatile$$ ; find * -type f 2> /dev/null) | \
		while read file ; do
			[ -e "$1/$file" ] || continue
			cmp -s "/tmp/volatile$$/$file" "$1/$file" && rm -f "$1/$file"
		done
		rm -rf /tmp/volatile$$
	done

	# Remove other files blindly
	for i in $1$INSTALLED/*/files.list; do
		while read file; do
			[ "$1$file" -nt "$i" ] && continue
			[ -f "$1$file" -a ! -L "$1$file" ] && continue
			[ -d "$1$file" ] || rm -f "$1$file"
		done < $i
	done

	# Remove tazpkg files and tmp files
	rm -rf $1$INSTALLED* $1/tmp $1/var/tmp
	rm -f $1$LOCALSTATE/*packages* $1$LOCALSTATE/files.list.lzma \
		$1$LOCALSTATE/mirror*  $1/var/cache/*/* \
		$1/var/lock/* $1/var/log/* $1/var/run/* $1/var/run/*/* \
		$1/var/lib/*  $1/var/lib/dbus/* 2>/dev/null

	# Cleanup directory tree
	cd $1
	find * -type d | sort -r | while read dir; do
		rmdir "$dir" 2>/dev/null
	done
	cd - > /dev/null
}


# Get byte(s) from a binary file

get() {
	od -v -j $1 -N ${3:-2} -t u${3:-2} -w${3:-2} -An $2 2>/dev/null | sed 's/ *//'
}


# Get cpio flavor info from the ISO image

flavordata() {
	[ $(get 1024 $1) -eq 35615 ] && n=2 || n=$((1+$(get 417 $1 1)))
	dd if=$1 bs=512 skip=$n count=20 2>/dev/null | zcat 2>/dev/null
}


# Restore undigest mirrors

restore_mirrors() {
	local undigest="$root$LOCALSTATE/undigest" priority="$root$LOCALSTATE/priority"
	[ -d "$undigest.bak" ] || [ -e "$priority.bak" ] || return

	action 'Restoring mirrors...'
	if [  -d "$undigest.bak" ]; then
		[ -d "$undigest" ] && rm -r "$undigest"
		mv   "$undigest.bak" "$undigest"
	fi
	[ -e "$priority.bak" ] && mv -f "$priority.bak" "$priority"
	:; status
}


# Setup undigest mirrors

setup_mirrors() {
	# Setup mirrors in plain system or in chroot (with variable root=)
	local mirrorlist="$1" fresh repacked
	local undigest="$root$LOCALSTATE/undigest" priority="$root$LOCALSTATE/priority"

	# Restore mirrors first: in case of non-clear exits, hangs, etc.
	restore_mirrors

	_ 'Setting up mirrors for %s...' "$root/"
	# Backing up current undigest mirrors and priority
	[ -d "$undigest" ] && mv "$undigest" "$undigest.bak"
	[ -e "$priority" ] && mv "$priority" "$priority.bak"
	rm -rf '/var/www/tazlito/'
	mkdir -p '/var/www/tazlito/'

	# Packages produced by CookUtils: on Tank or local, or repacked packages: highest priority
	fresh='/home/slitaz/packages'
	if [ -d "$fresh" ]; then
		# Setup first undigest mirror
		mkdir -p "$undigest/fresh"
		echo "$fresh" > "$undigest/fresh/mirror"
		echo 'fresh' >> "$priority"
		# Rebuild mirror DB if needed
		[ ! -e "$fresh/IDs" ] && tazpkg mkdb "$fresh" --forced --root=''
		[ -n "$(find -L "$fresh" -name '*.tazpkg' -newer "$fresh/IDs")" ] && \
			tazpkg mkdb "$fresh" --forced --root=''
		cp -a "$fresh/files.list.lzma" "$fresh/files-list.lzma"
	fi

	# Repacked packages: high priority
	repacked="$PACKAGES_REPOSITORY"
	if [ -d "$repacked" ] && [ "$repacked" != "$fresh" ] && ls "$repacked" | grep -q '\.tazpkg'; then
		# According to Tazlito setup file (tazlito.conf):
		# WORK_DIR="/home/slitaz/$SLITAZ_VERSION"
		# or
		# WORK_DIR="/home/slitaz"
		# and
		# PACKAGES_REPOSITORY="$WORK_DIR/packages"
		# It MAY or MAY NOT match /home/slitaz/packages, so here we setup second repository

		# Setup second undigest mirror
		mkdir -p "$undigest/repacked"
		echo "$repacked" > "$undigest/repacked/mirror"
		echo 'repacked' >> "$priority"
		# Rebuild mirror DB if needed
		[ ! -e "$repacked/IDs" ] && tazpkg mkdb "$repacked" --forced --root=''
		[ -n "$(find -L "$repacked" -name '*.tazpkg' -newer "$repacked/IDs")" ] && \
			tazpkg mkdb "$repacked" --forced --root=''
		cp -a "$repacked/files.list.lzma" "$repacked/files-list.lzma"
	fi

	# All repositories listed in mirrors list: normal priority
	[ -e "$mirrorlist" ] && \
	while read mirror; do
		# Provide consistent mirror ID for caching purpose: /var/cache/tazpkg/<mirror ID>/packages
		mirrorid=$(echo "$mirror" | md5sum | cut -d' ' -f1)
		mkdir -p            "$undigest/$mirrorid"
		echo "$mirror"    > "$undigest/$mirrorid/mirror"
		echo "$mirrorid" >> "$priority"
	done < "$mirrorlist"

	# And, finally, main mirror with the lowest (failsafe) priority (nothing to do)

	# Show list of mirrors
	[ -f "$priority" ] && awk -vdb="$root$LOCALSTATE" '
	function show(num, name, url) {
		printf " %-1.1d. %32.32s %-44.44s\n", num, name " ...............................", url;
	}
	{
		num++;
		"cat " db "/undigest/" $0 "/mirror" | getline url;
		show(num, $0, url);
	}
	END {
		num++;
		"cat " db "/mirror" | getline url;
		show(num, "main", url);
	}' "$priority"

	tazpkg recharge --quiet >/dev/null
}


# Get list of 'packages.info' lists using priority

pi_lists() {
	local pi
	[ -s "$root$LOCALSTATE/packages.info" ] || tazpkg recharge --root="$root" >/dev/null 2>&1
	local priority="$root$LOCALSTATE/priority"
	local undigest="$root$LOCALSTATE/undigest"

	{
		[ -s "$priority" ] && cat "$priority"
		echo 'main'
		[ -d "$undigest" ] && ls "$undigest"
	} | awk -vun="$undigest/" '
	{
		if (arr[$0] != 1) {
			arr[$0]  = 1;
			print un $0 "/packages.info";
		}
	}' | sed 's|/undigest/main||' | \
	while read pi; do
		[ -e "$pi" ] && echo "$pi"
	done
}


# Strip versions from packages list

strip_versions() {
	if [ -n "$stripped" ]; then
		action 'Consider list %s already stripped' "$(basename "$1")"
		status
		return 0
	fi
	action 'Strip versions from list %s...' "$(basename "$1")"
	local in_list="$1" tmp_list="$(mktemp)" namever pkg
	[ -f "$in_list" ] || die "List '$in_list' not found."

	# $pkg=<name>-<version> or $pkg=<name>; both <name> and <version> may contain dashes
	awk '
	{
		if (FILENAME ~ "packages.info") {
			# Collect package names
			FS = "\t"; pkg[$1] = 1;
		} else {
			FS = OFS = "-"; $0 = $0;		# Fix bug with FS for first record
			while (NF > 1 && ! pkg[$0])
				NF --;
			printf "%s\n", $0;
		}
	}' $(pi_lists) "$in_list" > "$tmp_list"

	cat "$tmp_list" > "$in_list"
	rm "$tmp_list"
	status
}


# Display list of unknown packages (informative)

display_unknown() {
	[ -s "$1" ] || return
	echo "Unknown packages:" >&2
	cat "$1" >&2
	rm  "$1"
}


# Display warnings about critical packages absent (informative)

display_warn() {
	[ -s "$1" ] || return
	echo "Absent critical packages:" >&2
	cat "$1" >&2
	rm  "$1"
	echo "Probably ISO image will be unusable."
}


# Install packages to rootfs

install_list_to_rootfs() {
	local list="$1" rootfs="$2" pkg i ii
	local undigest="$rootfs/var/lib/tazpkg/undigest"

	# initial tazpkg setup in empty rootfs
	tazpkg --root=$rootfs >/dev/null 2>&1
	# pass current 'mirror' to the rootfs
	mkdir -p $rootfs/var/lib/tazpkg $rootfs/etc
	cp -f /var/lib/tazpkg/mirror $rootfs/var/lib/tazpkg/mirror
	cp -f /etc/slitaz-release    $rootfs/etc/slitaz-release
	# link rootfs packages cache to the regular packages cache
	rm -r "$rootfs/var/cache/tazpkg"
	ln -s /var/cache/tazpkg "$rootfs/var/cache/tazpkg"

	setup_mirrors mirrors

	# Just in case if flavor doesn't contain "tazlito" package
	mkdir -p "$rootfs/etc/tazlito"

	newline

	# Choose detailed log with --detailed
	if [ -n "$detailed" ]; then
		while read pkg; do
			separator '-'
			echo $pkg
			tazpkg -gi $pkg --root=$rootfs --local --quiet --cookmode | tee -a $log
		done < $list
		separator '='
	else
		while read pkg; do
			action 'Installing package: %s' "$pkg"
			yes y | tazpkg -gi $pkg --root=$rootfs --quiet >> $log || exit 1
			status
		done < $list
	fi
	newline

	restore_mirrors
	# Remove 'fresh' and 'repacked' undigest repos leaving all other
	for i in fresh repacked; do
		ii="$undigest/$i"
		[ -d "$ii" ] && rm -rf "$ii"
		ii="$rootfs/var/lib/tazpkg/priority"
		if [ -f "$ii" ]; then
			sed -i "/$i/d" "$ii"
			[ -s "$ii" ] || rm "$ii"
		fi
	done
	[ -d "$undigest" ] && \
	for i in $(find "$undigest" -type f); do
		# Remove all undigest PKGDB files but 'mirror'
		[ "$(basename "$i")" != 'mirror' ] && rm "$i"
	done
	[ -d "$undigest" ] && \
		rmdir --ignore-fail-on-non-empty "$undigest"

	# Un-link packages cache
	rm "$rootfs/var/cache/tazpkg"

	# Clean /var/lib/tazpkg
	(cd $rootfs/var/lib/tazpkg; rm ID* descriptions.txt extra.list files* packages.* 2>/dev/null)
}




####################
# Tazlito commands #
####################

# /usr/bin/tazlito is linked with /usr/bin/reduplicate and /usr/bin/deduplicate
case "$0" in
	*reduplicate)
		find ${@:-.} ! -type d -links +1 \
			-exec cp -a {} {}$$ \; -exec mv {}$$ {} \;
		exit 0 ;;
	*deduplicate)
		deduplicate "$@"
		exit 0 ;;
esac


case "$COMMAND" in
	stats)
		# Tazlito general statistics from the config file.
		#
		title 'Tazlito statistics'
		optlist "\
Config file			: $CONFIG_FILE
ISO name			: $ISO_NAME.iso
Volume name			: $VOLUM_NAME
Prepared			: $PREPARED
Packages repository	: $PACKAGES_REPOSITORY
Distro directory	: $DISTRO
Additional files	: $ADDFILES
" | sed '/: $/d'
		footer
		;;


	list-addfiles)
		# Simple list of additional files in the rootfs
		newline
		if [ -d "$ADDFILES/rootfs" ]; then
			cd $ADDFILES
			find rootfs -type f
		else
			_ 'Additional files not found: %s' "$ADDFILES/rootfs/"
		fi
		newline
		;;


	gen-config)
		# Generate a new config file in the current dir or the specified
		# directory by $2.
		#
		if [ -n "$2" ]; then
			mkdir -p "$2" && cd "$2"
		fi

		newline
		action 'Generating empty tazlito.conf...'
		empty_config_file
		status

		separator
		if [ -f 'tazlito.conf' ] ; then
			_ 'Configuration file is ready to edit.'
			_ 'File location: %s' "$(pwd)/tazlito.conf"
			newline
		fi
		;;


	configure)
		# Configure a tazlito.conf config file. Start by getting
		# a empty config file and sed it.
		#
		if [ -f 'tazlito.conf' ]; then
			rm tazlito.conf
		else
			[ $(id -u) -ne 0 ] && die 'You must be root to configure the main config file' \
				'or in the same directory of the file you want to configure.'
			cd /etc
		fi

		empty_config_file

		title 'Configuring: %s' "$(pwd)/tazlito.conf"

		# ISO name.
		echo -n "ISO name            : " ; read answer
		sed -i s#'ISO_NAME=\"\"'#"ISO_NAME=\"$answer\""# tazlito.conf
		# Volume name.
		echo -n "Volume name         : " ; read answer
		sed -i s/'VOLUM_NAME=\"SliTaz\"'/"VOLUM_NAME=\"$answer\""/ tazlito.conf
		# Packages repository.
		echo -n "Packages repository : " ; read answer
		sed -i s#'PACKAGES_REPOSITORY=\"\"'#"PACKAGES_REPOSITORY=\"$answer\""# tazlito.conf
		# Distro path.
		echo -n "Distro path         : " ; read answer
		sed -i s#'DISTRO=\"\"'#"DISTRO=\"$answer\""# tazlito.conf
		footer "Config file is ready to use."
		echo 'You can now extract an ISO or generate a distro.'
		newline
		;;


	gen-iso)
		# Simply generate a new iso.
		#
		check_root
		verify_rootcd
		gen_livecd_isolinux
		distro_stats
		;;


	gen-initiso)
		# Simply generate a new initramfs with a new iso.
		#
		check_root
		verify_rootcd
		gen_initramfs "$ROOTFS"
		gen_livecd_isolinux
		distro_stats
		;;


	extract-distro|extract-iso)
		# Extract an ISO image to a directory and rebuild the LiveCD tree.
		#
		check_root
		ISO_IMAGE="$2"
		[ -z "$ISO_IMAGE" ] && die 'Please specify the path to the ISO image.' \
			'Example:\n  tazlito image.iso /path/target'

		# Set the distro path by checking for $3 on cmdline.
		TARGET="${3:-$DISTRO}"

		# Exit if existing distro is found.
		[ -d "$TARGET/rootfs" ] && die "A rootfs exists in '$TARGET'." \
			'Please clean the distro tree or change directory path.'

		title 'Tazlito extracting: %s' "$(basename $ISO_IMAGE)"

		# Start to mount the ISO.
		action 'Mounting ISO image...'
		mkdir -p "$TMP_DIR"
		# Get ISO file size.
		isosize=$(du -sh "$ISO_IMAGE" | cut -f1)
		mount -o loop -r "$ISO_IMAGE" "$TMP_DIR"
		sleep 2
		# Prepare target dir, copy the kernel and the rootfs.
		mkdir -p "$TARGET/rootfs" "$TARGET/rootcd/boot"
		status

		action 'Copying the Linux kernel...'
		if cp $TMP_DIR/boot/vmlinuz* "$TARGET/rootcd/boot" 2>/dev/null; then
			make_bzImage_hardlink "$TARGET/rootcd/boot"
		else
			cp "$TMP_DIR/boot/bzImage" "$TARGET/rootcd/boot"
		fi
		status

		for i in $(ls $TMP_DIR); do
			[ "$i" = 'boot' ] && continue
			cp -a "$TMP_DIR/$i" "$TARGET/rootcd"
		done

		for loader in isolinux syslinux extlinux grub; do
			[ -d "$TMP_DIR/boot/$loader" ] || continue
			action 'Copying %s files...' "$loader"
			cp -a "$TMP_DIR/boot/$loader" "$TARGET/rootcd/boot"
			status
		done

		action 'Copying the rootfs...'
		cp $TMP_DIR/boot/rootfs*.?z* "$TARGET/rootcd/boot"
		status

		action 'Copying DOS boot files...'
		cp $TMP_DIR/boot/linld.com $TMP_DIR/boot/*.exe "$TARGET/rootcd/boot"
		status

		cleanup_efi_boot "$TARGET/rootcd"

		# Extract initramfs.
		cd "$TARGET/rootfs"
		action 'Extracting the rootfs...'
		for i in $(ls -r $TARGET/rootcd/boot/rootfs*z); do
			extract_rootfs "$i" "$TARGET/rootfs"
		done
		# unpack /usr
		for i in etc/tazlito/*.extract; do
			[ -f "$i" ] && . $i ../rootcd
		done
		# Umount and remove temp directory and cd to $TARGET to get stats.
		umount "$TMP_DIR" && rm -rf "$TMP_DIR"
		cd ..
		status

		newline
		separator
		echo "Extracted       : $(basename $ISO_IMAGE) ($isosize)"
		echo "Distro tree     : $(pwd)"
		echo "Rootfs size     : $(du -sh rootfs)"
		echo "Rootcd size     : $(du -sh rootcd)"
		footer
		;;


	list-flavors)
		# Show available flavors.
		list='/etc/tazlito/flavors.list'
		[ ! -s $list -o -n "$recharge" ] && download flavors.list -O - > $list
		title 'List of flavors'
		cat $list
		footer
		;;


	show-flavor)
		# Show flavor descriptions.
		set -e
		flavor=${2%.flavor}
		flv_dir="$(extract_flavor "$flavor")"
		desc="$flv_dir/$flavor.desc"
		if [ -n "$brief" ]; then
			if [ -z "$noheader" ]; then
				printf "%-16.16s %6.6s %6.6s %s\n" 'Name' 'ISO' 'Rootfs' 'Description'
				separator
			fi
			printf "%-16.16s %6.6s %6.6s %s\n" "$flavor" \
				"$(field ISO         "$desc")" \
				"$(field Rootfs      "$desc")" \
				"$(field Description "$desc")"
		else
			separator
			cat "$desc"
		fi
		cleanup
		;;


	gen-liveflavor)
		# Generate a new flavor from the live system.
		FLAVOR=${2%.flavor}
		[ -z "$FLAVOR" ] && die 'Please specify flavor name on the commandline.'

		case "$FLAVOR" in
			-?|-h*|--help)
				cat <<EOT
SliTaz Live Tool - Version: $VERSION

$(boldify 'Usage:') tazlito gen-liveflavor <flavor-name> [<flavor-patch-file>]

$(boldify '<flavor-patch-file> format:')
$(optlist "\
code	data
+	package to add
-	package to remove
!	non-free package to add
?	display message
@	flavor description
")

$(boldify 'Example:')
$(optlist "\
@	Developer tools for SliTaz maintainers
+	slitaz-toolchain
+	mercurial
")
EOT
				exit 1
				;;
		esac
		mv /etc/tazlito/distro-packages.list \
		   /etc/tazlito/distro-packages.list.$$ 2>/dev/null
		rm -f distro-packages.list non-free.list 2>/dev/null
		tazpkg recharge

		DESC=""
		[ -n "$3" ] && \
		while read action pkg; do
			case "$action" in
				+)	yes | tazpkg get-install $pkg 2>&1 >> $log || exit 1 ;;
				-)	yes | tazpkg remove $pkg ;;
				!)	echo $pkg >> non-free.list ;;
				@)	DESC="$pkg" ;;
				\?)	echo -en "$pkg"; read action ;;
			esac
		done < $3

		yes '' | tazlito gen-distro
		echo "$DESC" | tazlito gen-flavor "$FLAVOR"
		mv /etc/tazlito/distro-packages.list.$$ \
		   /etc/tazlito/distro-packages.list 2>/dev/null
		;;


	gen-flavor)
		# Generate a new flavor from the last ISO image generated
		FLAVOR=${2%.flavor}
		[ -z "$FLAVOR" ] && die 'Please specify flavor name on the commandline.'

		title 'Flavor generation'
		check_rootfs
		FILES="$FLAVOR.pkglist"

		action 'Creating file %s...' "$FLAVOR.flavor"
		for i in rootcd rootfs; do
			if [ -d "$ADDFILES/$i" ] ; then
				FILES="$FILES\n$FLAVOR.$i"
				( cd "$ADDFILES/$i"; find . ) | cpio -o -H newc 2>/dev/null | dogzip $FLAVOR.$i
			fi
		done
		status

		answer=$(grep -s ^Description $FLAVOR.desc)
		answer=${answer#Description     : }
		if [ -z "$answer" ]; then
			echo -n "Description: "
			read answer
		fi

		action 'Compressing flavor %s...' "$FLAVOR"
		echo "Flavor          : $FLAVOR"  > $FLAVOR.desc
		echo "Description     : $answer" >> $FLAVOR.desc
		(cd $DISTRO; distro_sizes) >> $FLAVOR.desc
		\rm -f $FLAVOR.pkglist $FLAVOR.nonfree 2>/dev/null
		for i in $(ls $ROOTFS$INSTALLED); do
			eval $(grep ^VERSION= $ROOTFS$INSTALLED/$i/receipt)
			EXTRAVERSION=""
			eval $(grep ^EXTRAVERSION= $ROOTFS$INSTALLED/$i/receipt)
			eval $(grep ^CATEGORY= $ROOTFS$INSTALLED/$i/receipt)
			if [ "$CATEGORY" = 'non-free' ] && [ "${i%%-*}" != 'get' ]; then
				echo "$i" >> $FLAVOR.nonfree
			else
				echo "$i-$VERSION$EXTRAVERSION" >> $FLAVOR.pkglist
			fi
		done
		[ -s $FLAVOR.nonfree ] && FILES="$FILES\n$FLAVOR.nonfree"
		for i in $LOCALSTATE/undigest/*/mirror ; do
			[ -s $i ] && cat $i >> $FLAVOR.mirrors
		done
		[ -s $FLAVOR.mirrors ] && $FILES="$FILES\n$FLAVOR.mirrors"
		touch -t 197001010100.00 $FLAVOR.*
		echo -e "$FLAVOR.desc\n$FILES" | cpio -o -H newc 2>/dev/null | dogzip $FLAVOR.flavor
		rm $(echo -e $FILES)
		status

		footer "Flavor size: $(du -sh $FLAVOR.flavor)"
		;;


	upgrade-flavor)
		# Strip versions from pkglist and update estimated numbers in flavor.desc
		flavor="${2%.flavor}"
		set -e
		[ -f "$flavor.flavor" ] || download "$flavor.flavor"
		set +e

		flv_dir="$(extract_flavor "$flavor")"

		strip_versions "$flv_dir/$flavor.pkglist"

		action 'Updating %s...' "$flavor.desc"

		[ -f "$flv_dir/$flavor.mirrors" ] && setup_mirrors "$flv_dir/$flavor.mirrors" >/dev/null
		set -- $(module calc_sizes "$flv_dir" "$flavor")
		restore_mirrors >/dev/null

		sed -i -e '/Image is ready/d' \
		       -e "s|\(Rootfs size *:\).*$|\1 $1  (estimated)|" \
		       -e "s|\(Initramfs size *:\).*$|\1 $2  (estimated)|" \
		       -e "s|\(ISO image size *:\).*$|\1 $3  (estimated)|" \
		       -e "s|\(Packages *:\).*$|\1 $4|" \
		       -e "s|\(Build date *:\).*$|\1 $(date '+%Y%m%d at %T')|" \
				"$flv_dir/$flavor.desc"

		pack_flavor "$flv_dir" "$flavor"
		status
		display_unknown "$flv_dir/err"
		display_warn    "$flv_dir/warn"
		cleanup
		;;


	extract-flavor)
		# Extract a flavor into $FLAVORS_REPOSITORY
		flavor="${2%.flavor}"
		set -e
		[ -f "$flavor.flavor" ] || download "$flavor.flavor"
		set +e

		action 'Extracting %s...' "$flavor.flavor"
		flv_dir="$(extract_flavor "$flavor" full)"
		storage="$FLAVORS_REPOSITORY/$flavor"

		rm    -rf "$storage" 2>/dev/null
		mkdir -p  "$storage"
		cp -a "$flv_dir"/* "$storage"
		rm "$storage/description"
		status

		strip_versions "$storage/packages.list"

		cleanup
		;;


	pack-flavor)
		# Create a flavor from $FLAVORS_REPOSITORY.
		flavor=${2%.flavor}
		storage="$FLAVORS_REPOSITORY/$flavor"

		[ -s "$storage/receipt" ] || die "No $flavor receipt in $FLAVORS_REPOSITORY."

		action 'Creating flavor %s...' "$flavor"
		tmp_dir="$(mktemp -d)"

		while read from to; do
			[  -s "$storage/$from" ] || continue
			cp -a "$storage/$from" "$tmp_dir/$to"
		done <<EOT
mirrors        $flavor.mirrors
distro.sh      $flavor-distro.sh
receipt        $flavor.receipt
non-free.list  $flavor.nonfree
EOT

		# Build the package list.
		# It can include a list from another flavor with the keyword @include
		if [ -s "$storage/packages.list" ]; then
			include=$(grep '^@include' "$storage/packages.list")
			if [ -n "$include" ]; then
				include=${include#@include }
				if [   -s "$FLAVORS_REPOSITORY/$include/packages.list" ]; then
					cp -f "$FLAVORS_REPOSITORY/$include/packages.list" "$tmp_dir/$flavor.pkglist"
				else
					echo -e "\nERROR: Can't find include package list from $include\n"
				fi
			fi
			# Generate the final/initial package list
			[ -s "$storage/packages.list" ] && \
				cat "$storage/packages.list" >> "$tmp_dir/$flavor.pkglist"
			sed -i '/@include/d' "$tmp_dir/$flavor.pkglist"
		fi

		if grep -q ^ROOTFS_SELECTION "$storage/receipt"; then
			# Process multi-rootfs flavor
			. "$storage/receipt"
			set -- $ROOTFS_SELECTION
			[ -n "$FRUGAL_RAM" ] || FRUGAL_RAM=$1
			[ -f "$FLAVORS_REPOSITORY/$2/packages.list" ] || tazlito extract-flavor $2
			cp "$FLAVORS_REPOSITORY/$2/packages.list" "$tmp_dir/$flavor.pkglist"

			for i in rootcd rootfs; do
				mkdir "$tmp_dir/$i"
				# Copy extra files from the first flavor
				[ -d "$FLAVORS_REPOSITORY/$2/$i" ] &&
					cp -a "$FLAVORS_REPOSITORY/$2/$i" "$tmp_dir"
				# Overload extra files by meta flavor
				[ -d "$storage/$i" ] && cp -a "$storage/$i" "$tmp_dir"
				[ -n "$(ls $tmp_dir/$i)" ] &&
					(cd "$tmp_dir/$i"; find . | cpio -o -H newc 2>/dev/null ) | \
					dogzip "$tmp_dir/$flavor.$i"
				rm -rf "$tmp_dir/$i"
			done
		else
			# Process plain flavor
			for i in rootcd rootfs; do
				[ -d "$storage/$i" ] || continue
				(cd  "$storage/$i";
					find . | cpio -o -H newc 2>/dev/null) | dogzip "$tmp_dir/$flavor.$i"
			done
		fi

		unset VERSION MAINTAINER ROOTFS_SELECTION
		set -- $(module calc_sizes "$tmp_dir" "$flavor")
		ROOTFS_SIZE="$1 (estimated)"
		INITRAMFS_SIZE="$2 (estimated)"
		ISO_SIZE="$3 (estimated)"
		PKGNUM="$4"
		. "$storage/receipt"

		sed '/: $/d' > "$tmp_dir/$flavor.desc" <<EOT
Flavor          : $FLAVOR
Description     : $SHORT_DESC
Version         : $VERSION
Maintainer      : $MAINTAINER
LiveCD RAM size : $FRUGAL_RAM
Rootfs list     : $ROOTFS_SELECTION
Build date      : $(date '+%Y%m%d at %T')
Packages        : $PKGNUM
Rootfs size     : $ROOTFS_SIZE
Initramfs size  : $INITRAMFS_SIZE
ISO image size  : $ISO_SIZE
================================================================================

EOT

		rm -f $tmp_dir/packages.list
		pack_flavor "$tmp_dir" "$flavor"
		status
		display_unknown "$tmp_dir/err"
		display_warn    "$flv_dir/warn"
		cleanup
		;;


	get-flavor)
		# Get a flavor's files and prepare for gen-distro.
		flavor=${2%.flavor}
		title 'Preparing %s distro flavor' "$flavor"
		set -e
		[ -f "$flavor.flavor" ] || download "$flavor.flavor"
		set +e

		action 'Cleaning %s...' "$DISTRO"
		[ -d "$DISTRO" ] && rm -r "$DISTRO"
		# Clean old files
		for i in non-free.list distro-packages.list distro.sh receipt mirrors err; do
			[ -f "$i" ] && rm "$i"
		done
		mkdir -p "$DISTRO"
		status

		[ -z "$noup" ] && tazlito upgrade-flavor "$flavor.flavor"

		action 'Extracting flavor %s...' "$flavor.flavor"
		flv_dir="$(extract_flavor "$flavor" info)"
		cp -a "$flv_dir"/* .
		mv packages.list distro-packages.list
		mv -f info /etc/tazlito
		status

		for i in rootcd rootfs; do
			if [ -d "$i" ]; then
				mkdir -p "$ADDFILES"; mv "$i" "$ADDFILES/$i"
			fi
		done

		sed '/^Rootfs list/!d;s/.*: //' description > /etc/tazlito/rootfs.list
		[ -s /etc/tazlito/rootfs.list ] || rm -f /etc/tazlito/rootfs.list

		action 'Updating %s...' 'tazlito.conf'
		[ -f tazlito.conf ] || cp /etc/tazlito/tazlito.conf .
		grep -v "^#VOLUM_NAME" < tazlito.conf | \
		sed "s/^VOLUM_NA/VOLUM_NAME=\"SliTaz $flavor\"\\n#VOLUM_NA/" \
			> tazlito.conf.$$ && mv tazlito.conf.$$ tazlito.conf
		sed -i "s/ISO_NAME=.*/ISO_NAME=\"slitaz-$flavor\"/" tazlito.conf
		status

		footer 'Flavor is ready to be generated by `tazlito gen-distro`'
		cleanup
		;;


	iso2flavor)
		[ -z "$3" -o ! -e "$2" ] && die 'Usage: tazlito iso2flavor <image.iso> <flavor_name>' \
			'\n\nCreate a file <flavor_name>.flavor from the CD-ROM image file <image.iso>'

		FLAVOR=${3%.flavor}
		mkdir -p $TMP_DIR/iso $TMP_DIR/rootfs $TMP_DIR/flavor
		mount -o loop,ro $2 $TMP_DIR/iso
		flavordata $2 | (cd $TMP_DIR/flavor; cpio -i 2>/dev/null)
		if [ -s $TMP_DIR/iso/boot/rootfs1.gz ] &&
		   [ ! -s $TMP_DIR/flavor/*.desc ]; then
			_ 'META flavors are not supported.'
			umount -d $TMP_DIR/iso
		elif [ ! -s $TMP_DIR/iso/boot/rootfs.gz ] &&
		     [ ! -s $TMP_DIR/iso/boot/rootfs1.gz ]; then
			_ 'No %s in ISO image. Needs a SliTaz ISO.' '/boot/rootfs.gz'
			umount -d $TMP_DIR/iso
		else
			for i in $(ls -r $TMP_DIR/iso/boot/rootfs*z); do
				uncompress $i | \
				( cd $TMP_DIR/rootfs ; cpio -idmu > /dev/null 2>&1 )
			done
			if [ ! -s $TMP_DIR/rootfs/etc/slitaz-release ]; then
				_ 'No file %s in %s of ISO image. Needs a non-loram SliTaz ISO.' \
					'/etc/slitaz-release' '/boot/rootfs.gz'
				umount -d $TMP_DIR/iso
			else
				ROOTFS_SIZE=$(du -hs $TMP_DIR/rootfs | awk '{ print $1 }')
				RAM_SIZE=$(du -s $TMP_DIR/rootfs | awk '{ print 32*int(($1+36000)/32768) "M" }')
				cp -a $TMP_DIR/iso $TMP_DIR/rootcd
				ISO_SIZE=$(df -h $TMP_DIR/iso | awk 'END { print $2 }')
				BUILD_DATE=$(date '+%Y%m%d at %T' -r "$TMP_DIR/iso/md5sum")
				umount -d $TMP_DIR/iso
				INITRAMFS_SIZE=$(du -chs $TMP_DIR/rootcd/boot/rootfs*.gz | awk 'END { print $1 }')
				rm -f $TMP_DIR/rootcd/boot/rootfs.gz $TMP_DIR/rootcd/md5sum
				mv $TMP_DIR/rootcd/boot $TMP_DIR/rootfs
				[ -d $TMP_DIR/rootcd/EFI ] && mv $TMP_DIR/rootcd/EFI $TMP_DIR/rootfs
				sed 's/.*  \(.*\).tazpkg*/\1/' > $TMP_DIR/$FLAVOR.pkglist \
					< $TMP_DIR/rootfs$INSTALLED.md5
				PKGCNT=$(grep -cv ^# $TMP_DIR/$FLAVOR.pkglist)
				if [ -s $TMP_DIR/flavor/*desc ]; then
					cp $TMP_DIR/flavor/*.desc $TMP_DIR/$FLAVOR.desc
					[ -s $TMP_DIR/$FLAVOR.receipt ] &&
					cp $TMP_DIR/flavor/*.receipt $TMP_DIR/$FLAVOR.receipt
					for i in rootfs rootcd ; do
						[ -s $TMP_DIR/flavor/*.list$i ] &&
						sed 's/.\{1,45\}//;/^\.$/d' $TMP_DIR/flavor/*.list$i | \
							( cd $TMP_DIR/$i ; cpio -o -H newc ) | dogzip $TMP_DIR/$FLAVOR.$i
					done
				else
					find_flavor_rootfs $TMP_DIR/rootfs
					[ -d $TMP_DIR/rootfs/boot ] && mv $TMP_DIR/rootfs/boot $TMP_DIR/rootcd
					[ -d $TMP_DIR/rootfs/EFI ] && mv $TMP_DIR/rootfs/EFI $TMP_DIR/rootcd
					for i in rootfs rootcd ; do
						[ "$(ls $TMP_DIR/$i)" ] &&
						( cd "$TMP_DIR/$i"; find * | cpio -o -H newc ) | dogzip "$TMP_DIR/$FLAVOR.$i"
					done
					unset VERSION MAINTAINER
					echo -en "Flavor short description \007: "; read -t 30 DESCRIPTION
					if [ -n "$DESCRIPTION" ]; then
						_n 'Flavor version : '; read -t 30 VERSION
						_n 'Flavor maintainer (your email) : '; read -t 30 MAINTAINER
					fi

					cat > $TMP_DIR/$FLAVOR.desc <<EOT
Flavor          : $FLAVOR
Description     : ${DESCRIPTION:-SliTaz $FLAVOR flavor}
Version         : ${VERSION:-1.0}
Maintainer      : ${MAINTAINER:-nobody@slitaz.org}
LiveCD RAM size : $RAM_SIZE
Build date      : $BUILD_DATE
Packages        : $PKGCNT
Rootfs size     : $ROOTFS_SIZE
Initramfs size  : $INITRAMFS_SIZE
ISO image size  : $ISO_SIZE
================================================================================

EOT
					longline "Tazlito can't detect each file installed during \
a package post_install. You should extract this flavor (tazlito extract-flavor \
$FLAVOR), check the files in /home/slitaz/flavors/$(cat /etc/slitaz-release)/$FLAVOR/rootfs \
tree and remove files generated by post_installs.
Check /home/slitaz/flavors/$(cat /etc/slitaz-release)/$FLAVOR/receipt too and \
repack the flavor (tazlito pack-flavor $FLAVOR)"
				fi
				( cd $TMP_DIR; ls $FLAVOR.* | cpio -o -H newc ) | dogzip $FLAVOR.flavor
			fi
		fi
		rm -rf $TMP_DIR
		;;


	gen-distro)
		# Generate a live distro tree with a set of packages.
		#
		check_root
		start_time=$(date +%s)

		# Tazlito options: --iso or --cdrom
		CDROM=''
		[ -n "$iso"   ] && CDROM="-o loop $iso"
		[ -n "$cdrom" ] && CDROM="/dev/cdrom"

		# Check if a package list was specified on cmdline.
		if [ -f "$2" ]; then
			LIST_NAME="$2"
		else
			LIST_NAME='distro-packages.list'
		fi

		[ -d "$ROOTFS" ] && [ -z "$forced" ] && die "A rootfs exists in '$DISTRO'." \
			'Please clean the distro tree or change directory path.'
		[ -d "$ROOTFS" ] && rm -rf "$ROOTFS"
		[ -d "$ROOTCD" ] && rm -rf "$ROOTCD"

		# If list not given: build list with all installed packages
		if [ ! -f "$LIST_NAME" ] && [ -f "$LOCALSTATE/installed.info" ]; then
			awk -F$'\t' '{print $1}' "$LOCALSTATE/installed.info" >> "$LIST_NAME"
		fi

		# Exit if no list name.
		[ ! -f "$LIST_NAME" ] && die 'No packages list found or specified. Please read the docs.'

		# Start generation.
		title 'Tazlito generating a distro'

		# Misc checks
		mkdir -p "$PACKAGES_REPOSITORY"
		REPACK=$(yesorno 'Repack packages from rootfs?' 'n')
		newline

		# Mount CD-ROM to be able to repack boot-loader packages
		if [ ! -e /boot -a -n "$CDROM" ]; then
			mkdir $TMP_MNT
			if mount -r "$CDROM $TMP_MNT" 2>/dev/null; then
				ln -s "$TMP_MNT/boot" /
				if [ ! -d "$ADDFILES/rootcd" ] ; then
					mkdir -p "$ADDFILES/rootcd"
					for i in $(ls $TMP_MNT); do
						[ "$i" = 'boot' ] && continue
						cp -a "$TMP_MNT/$i" "$ADDFILES/rootcd"
					done
				fi
			else
				rmdir "$TMP_MNT"
			fi
		fi

		# Rootfs stuff.
		echo 'Preparing the rootfs directory...'
		mkdir -p "$ROOTFS"
		export root="$ROOTFS"
		# pass current 'mirror' to the root
		mkdir -p $root/var/lib/tazpkg $root/etc
		cp -f /var/lib/tazpkg/mirror $root/var/lib/tazpkg/mirror
		cp -f /etc/slitaz-release    $root/etc/slitaz-release
		strip_versions "$LIST_NAME"

		if [ "$REPACK" = 'y' ]; then
			# Determine full packages list with all dependencies
			tmp_dir="$(mktemp -d)"
			cp "$LIST_NAME" "$tmp_dir/flavor.pkglist"
			touch "$tmp_dir/full.pkglist"
			module calc_sizes "$tmp_dir" 'flavor' "$tmp_dir/full.pkglist" >/dev/null

			awk -F$'\t' '{printf "%s %s\n", $1, $2}' "$LOCALSTATE/installed.info" | \
			while read pkgname pkgver; do
				# Is package in full list?
				grep -q "^$pkgname$" "$tmp_dir/full.pkglist" || continue
				# Is package already repacked?
				[ -e "$PACKAGES_REPOSITORY/$pkgname-$pkgver.tazpkg" ] && continue
				_ 'Repacking %s...' "$pkgname-$pkgver"
				tazpkg repack "$pkgname" --quiet
				[ -f "$pkgname-$pkgver.tazpkg" ] && mv "$pkgname-$pkgver.tazpkg" "$PACKAGES_REPOSITORY"
				status
			done

			rm -r "$tmp_dir"
		fi

		if [ -f non-free.list ]; then
			# FIXME: working in the ROOTFS chroot?
			newline
			echo 'Preparing non-free packages...'
			cp 'non-free.list' "$ROOTFS/etc/tazlito/non-free.list"
			while read pkg ; do
				if [ ! -d "$INSTALLED/$pkg" ]; then
					if [ ! -d "$INSTALLED/get-$pkg" ]; then
						tazpkg get-install get-$pkg
					fi
					get-$pkg "$ROOTFS"
				fi
				tazpkg repack $pkg
				pkg=$(ls $pkg*.tazpkg)
				grep -q "^$pkg$" $LIST_NAME || echo $pkg >> $LIST_NAME
				mv $pkg $PACKAGES_REPOSITORY
			done < non-free.list
		fi
		cp $LIST_NAME $DISTRO/distro-packages.list
		newline

		install_list_to_rootfs "$DISTRO/distro-packages.list" "$ROOTFS"

		cd $DISTRO
		cp distro-packages.list $ROOTFS/etc/tazlito
		# Copy all files from $ADDFILES/rootfs to the rootfs.
		if [ -d "$ADDFILES/rootfs" ] ; then
			action 'Copying addfiles content to the rootfs...'
			cp -a $ADDFILES/rootfs/* $ROOTFS
			status
		fi

		action 'Root filesystem is generated...'; status

		# Root CD part.
		action 'Preparing the rootcd directory...'
		mkdir -p $ROOTCD
		status

		# Move the boot dir with the Linux kernel from rootfs.
		# The EFI & boot dirs go directly on the CD.
		if [ -d "$ROOTFS/EFI" ] ; then
			action 'Moving the EFI directory...'
			mv $ROOTFS/EFI $ROOTCD
			status
		fi
		if [ -d "$ROOTFS/boot" ] ; then
			action 'Moving the boot directory...'
			mv $ROOTFS/boot $ROOTCD
			status
		fi
		cd $DISTRO
		# Copy all files from $ADDFILES/rootcd to the rootcd.
		if [ -d "$ADDFILES/rootcd" ] ; then
			action 'Copying addfiles content to the rootcd...'
			cp -a $ADDFILES/rootcd/* $ROOTCD
			status
		fi
		# Execute the distro script used to perform tasks in the rootfs
		# before compression. Give rootfs path in arg
		[ -z "$DISTRO_SCRIPT" ] && DISTRO_SCRIPT="$TOP_DIR/distro.sh"
		if [ -x "$DISTRO_SCRIPT" ]; then
			echo 'Executing distro script...'
			sh $DISTRO_SCRIPT $DISTRO
		fi

		# Execute the custom_rules() found in receipt.
		if [ -s "$TOP_DIR/receipt" ]; then
			if grep -q ^custom_rules "$TOP_DIR/receipt"; then
				echo -e "Executing: custom_rules()\n"
				. "$TOP_DIR/receipt"
				custom_rules || echo -e "\nERROR: custom_rules() failed\n" 
			fi
		fi

		# Multi-rootfs
		if [ -s /etc/tazlito/rootfs.list ]; then

			FLAVOR_LIST="$(awk '{
				for (i = 2; i <= NF; i+=2)
					printf "%s ", $i;
			}' /etc/tazlito/rootfs.list)"

			[ -s "$ROOTCD/boot/isolinux/isolinux.msg" ] &&
				sed -i "s/ *//;s/)/), flavors $FLAVOR_LIST/" \
					"$ROOTCD/boot/isolinux/isolinux.msg" 2>/dev/null

			[ -f "$ROOTCD/boot/isolinux/ifmem.c32" -o \
			  -f "$ROOTCD/boot/isolinux/c32box.c32" ] ||
			cp '/boot/isolinux/c32box.c32' "$ROOTCD/boot/isolinux" 2>/dev/null ||
			cp '/boot/isolinux/ifmem.c32'  "$ROOTCD/boot/isolinux"

			n=0
			last=$ROOTFS
			while read flavor; do
				n=$((n+1))
				mkdir ${ROOTFS}0$n
				export root="${ROOTFS}0$n"
				# initial tazpkg setup in empty rootfs
				tazpkg --root=$root >/dev/null 2>&1

				newline
				boldify "Building $flavor rootfs..."

				[ -s   "$TOP_DIR/$flavor.flavor" ] &&
					cp "$TOP_DIR/$flavor.flavor" .

				if [ ! -s "$flavor.flavor" ]; then
					# We may have it in $FLAVORS_REPOSITORY
					if [ -d "$FLAVORS_REPOSITORY/$flavor" ]; then
						tazlito pack-flavor $flavor
					else
						download $flavor.flavor
					fi
				fi

				action 'Extracting %s and %s...' "$flavor.pkglist" "$flavor.rootfs"
				zcat $flavor.flavor | cpio -i --quiet $flavor.pkglist $flavor.rootfs
				cp $flavor.pkglist $DISTRO/list-packages0$n
				status

				strip_versions "$DISTRO/list-packages0$n"

				install_list_to_rootfs "$DISTRO/list-packages0$n" "${ROOTFS}0$n"

				action 'Updating the boot directory...'
				yes n | cp -ai ${ROOTFS}0$n/boot $ROOTCD 2> /dev/null
				rm -rf ${ROOTFS}0$n/boot

				cd $DISTRO
				if [ -s $flavor.rootfs ]; then
					_n 'Adding %s rootfs extra files...' "$flavor"
					zcat < $flavor.rootfs | ( cd ${ROOTFS}0$n ; cpio -idmu )
				fi

				action 'Moving %s to %s' "list-packages0$n" "rootfs0$n"
				mv "$DISTRO/list-packages0$n" "${ROOTFS}0$n/etc/tazlito/distro-packages.list"
				status

				rm -f $flavor.flavor install-list
				mergefs ${ROOTFS}0$n $last
				last=${ROOTFS}0$n
			done <<EOT
$(awk '{ for (i = 4; i <= NF; i+=2) print $i; }' < /etc/tazlito/rootfs.list)
EOT
#'
			i=$((n+1))
			while [ $n -gt 0 ]; do
				mv ${ROOTFS}0$n ${ROOTFS}$i
				_ 'Compressing %s (%s)...' "${ROOTFS}0$n" "$(du -hs ${ROOTFS}$i | awk '{ print $1 }')"
				gen_initramfs ${ROOTFS}$i
				n=$((n-1))
				i=$((i-1))
				export LZMA_HISTORY_BITS=26
			done
			mv $ROOTFS ${ROOTFS}$i
			gen_initramfs ${ROOTFS}$i
			update_bootconfig "$ROOTCD/boot/isolinux" "$(cat /etc/tazlito/rootfs.list)"
			ROOTFS=${ROOTFS}1
		else
			# Initramfs and ISO image stuff.
			gen_initramfs $ROOTFS
		fi
		gen_livecd_isolinux
		distro_stats
		cleanup
		;;


	clean-distro)
		# Remove old distro tree.
		#
		check_root
		title 'Cleaning: %s' "$DISTRO"
		if [ -d "$DISTRO" ] ; then
			if [ -d "$ROOTFS" ] ; then
				action 'Removing the rootfs...'
				rm -f  $DISTRO/$INITRAMFS
				rm -rf $ROOTFS
				status
			fi
			if [ -d "$ROOTCD" ] ; then
				action 'Removing the rootcd...'
				rm -rf $ROOTCD
				status
			fi
			action 'Removing eventual ISO image...'
			rm -f $DISTRO/$ISO_NAME.iso
			rm -f $DISTRO/$ISO_NAME.md5
			status
		fi
		footer
		;;


	check-distro)
		# Check for a few LiveCD needed files not installed by packages.
		#
		# TODO: Remove this function.
		# First two files are maintained by tazpkg while it runs on rootfs,
		# while last one file should be maintained by tazlito itself.
		check_rootfs
		title 'Checking distro: %s' "$ROOTFS"
		# SliTaz release info.
		rel='/etc/slitaz-release'
		if [ ! -f "$ROOTFS$rel" ]; then
			_ 'Missing release info: %s' "$rel"
		else
			action 'Release      : %s' "$(cat $ROOTFS$rel)"
			status
		fi
		# Tazpkg mirror.
		if [ ! -f "$ROOTFS$LOCALSTATE/mirror" ]; then
			action 'Mirror URL   : Missing %s' "$LOCALSTATE/mirror"
			todomsg
		else
			action 'Mirror configuration exists...'
			status
		fi
		# Isolinux msg
		if grep -q "cooking-XXXXXXXX" /$ROOTCD/boot/isolinux/isolinux.*g; then
			action 'Isolinux msg : Missing cooking date XXXXXXXX (ex %s)' "$(date +%Y%m%d)"
			todomsg
		else
			action 'Isolinux message seems good...'
			status
		fi
		footer
		;;


	writeiso)
		# Writefs to ISO image including /home unlike gen-distro we don't use
		# packages to generate a rootfs, we build a compressed rootfs with all
		# the current filesystem similar to 'tazusb writefs'.
		#
		DISTRO='/home/slitaz/distro'
		ROOTCD="$DISTRO/rootcd"
		COMPRESSION="${2:-none}"
		ISO_NAME="${3:-slitaz}"
		check_root
		# Start info
		title 'Write filesystem to ISO'
		longline "The command writeiso will write the current filesystem into a \
suitable cpio archive (rootfs.gz) and generate a bootable ISO image (slitaz.iso)."
		newline
		emsg "<b>Archive compression:</b> <c 36>$COMPRESSION</c>"

		[ "$COMPRESSION" = 'gzip' ] && colorize 31 "gzip-compressed rootfs unsupported and may fail to boot"
		# Save some space
		rm -rf /var/cache/tazpkg/*
		rm -f  /var/lib/tazpkg/*.bak
		rm -rf $DISTRO

		# Optionally remove sound card selection and screen resolution.
		if [ -z $LaunchedByTazpanel ]; then
		anser=$(yesorno 'Do you wish to remove the sound card and screen configs?' 'n')
		case $anser in
			y)
				action 'Removing current sound card and screen configurations...'
				rm -f /var/lib/sound-card-driver
				rm -f /var/lib/alsa/asound.state
				rm -f /etc/X11/xorg.conf ;;
			*)
				action 'Keeping current sound card and screen configurations...' ;;
		esac
		status
		newline

		# Optionally remove i18n settings
		anser=$(yesorno 'Do you wish to remove locale/keymap settings?' 'n')
		case $anser in
			y)
				action 'Removing current locale/keymap settings...'
				newline > /etc/locale.conf
				newline > /etc/keymap.conf ;;
			*)
				action 'Keeping current locale/keymap settings...' ;;
		esac
		status
		fi

		# Clean-up files by default
		newline > /etc/udev/rules.d/70-persistent-net.rules
		newline > /etc/udev/rules.d/70-persistant-cd.rules

		# Create list of files including default user files since it is defined in /etc/passwd
		# and some new users might have been added.
		cd /
		echo 'init' > /tmp/list
		for dir in bin etc sbin var dev lib root usr home opt; do
			[ -d $dir ] && find $dir
		done >> /tmp/list

		for dir in proc sys tmp mnt media media/cdrom media/flash media/usbdisk run run/udev; do
			[ -d $dir ] && echo $dir
		done >> /tmp/list

		sed '/var\/run\/.*pid$/d ; /var\/run\/utmp/d ; /.*\/.gvfs/d ; /home\/.*\/.cache\/.*/d' -i /tmp/list
		
		#if [ ! $(find /var/log/slitaz/tazpkg.log -size +4k) = "" ]; then
		#	sed -i "/var\/log\/slitaz\/tazpkg.log/d" /tmp/list
		#fi
		mv -f /var/log/wtmp /tmp/tazlito-wtmp
		touch /var/log/wtmp
		
		for removelog in auth boot messages dmesg daemon slim .*old Xorg tazpanel cups; do
			sed -i "/var\/log\/$removelog/d" /tmp/list
		done

		# Generate initramfs with specified compression and display rootfs
		# size in realtime.
		rm -f /tmp/.write-iso*  /tmp/rootfs	2>/dev/null

		write_initramfs &
		sleep 2
		cd - > /dev/null
		echo -en "\nFilesystem size:"
		while [ ! -f /tmp/rootfs ]; do
			sleep 1
			echo -en "\\033[18G$(du -sh /$INITRAMFS | awk '{print $1}')    "
		done
		mv -f /tmp/tazlito-wtmp /var/log/wtmp
		echo -e "\n"
		rm -f /tmp/rootfs

		# Move freshly generated rootfs to the cdrom.
		mkdir -p $ROOTCD/boot
		mv -f /$INITRAMFS $ROOTCD/boot
		_ 'Located in: %s' "$ROOTCD/boot/$INITRAMFS"

		# Now we need the kernel and isolinux files.
		copy_from_cd() {
			cp /media/cdrom/boot/bzImage* $ROOTCD/boot
			cp -a /media/cdrom/boot/isolinux $ROOTCD/boot
			unmeta_boot $ROOTCD
			umount /media/cdrom
		}

		if  mount /dev/cdrom /media/cdrom 2>/dev/null; then
			copy_from_cd;
		elif  mount | grep /media/cdrom; then
			copy_from_cd;
		#elif [ -f "$bootloader" -a -f /boot/vmlinuz*slitaz* ]; then
		#	[ -f /boot/*slitaz ] && cp /boot/vmlinuz*slitaz $ROOTCD/boot/bzImage
		#	[ -f /boot/*slitaz64 ] && cp /boot/vmlinuz*slitaz64 $ROOTCD/boot/bzImage64
		else
			touch /tmp/.write-iso-error
			longline "When SliTaz is running in RAM the kernel and bootloader \
files are kept on the CD-ROM. `boldify ' Please insert a Live CD or run:
# mount -o loop slitaz.iso /media/cdrom ' `  to let Tazlito copy the files."
			echo -en "----\nENTER to continue..."; read i
			[ ! -d /media/cdrom/boot/isolinux ] && exit 1
			copy_from_cd
		fi

		# Generate the iso image.
		touch /tmp/.write-iso
		newline
		cd $DISTRO
		create_iso $ISO_NAME.iso $ROOTCD
		action 'Creating the ISO md5sum...'
		md5sum $ISO_NAME.iso > $ISO_NAME.md5
		status

		footer "ISO image: $(du -sh $DISTRO/$ISO_NAME.iso)"
		rm -f /tmp/.write-iso

		if [ -z $LaunchedByTazpanel ]; then
		anser=$(yesorno 'Burn ISO to CD-ROM?' 'n')
		case $anser in
			y)
				umount /dev/cdrom 2>/dev/null
				eject
				echo -n "Please insert a blank CD-ROM and press ENTER..."
				read i && sleep 2
				tazlito burn-iso $DISTRO/$ISO_NAME.iso
				echo -en "----\nENTER to continue..."; read i ;;
			*)
				exit 0 ;;
		esac
		fi
		;;


	burn-iso)
		# Guess CD-ROM device, ask user and burn the ISO.
		#
		check_root
		DRIVE_NAME=$(grep  "drive name"  /proc/sys/dev/cdrom/info | cut -f3)
		DRIVE_SPEED=$(grep "drive speed" /proc/sys/dev/cdrom/info | cut -f3)
		# We can specify an alternative ISO from the cmdline.
		iso="${2:-$DISTRO/$ISO_NAME.iso}"
		[ ! -f "$iso" ] && die "Unable to find ISO: $iso"

		title 'Tazlito burn ISO'
		echo "CD-ROM device : /dev/$DRIVE_NAME"
		echo "Drive speed   : $DRIVE_SPEED"
		echo "ISO image     : $iso"
		footer

		case $(yesorno 'Burn ISO image?' 'n') in
			y)
				title 'Starting Wodim to burn the ISO...'
				sleep 2
				wodim speed=$DRIVE_SPEED dev=/dev/$DRIVE_NAME $iso
				footer 'ISO image is burned to CD-ROM.'
				;;
			*)
				die 'Exiting. No ISO burned.'
				;;
		esac
		;;


	merge)
		# Merge multiple rootfs into one iso.
		#
		if [ -z "$2" ]; then
			cat <<EOT
Usage: tazlito merge size1 iso size2 rootfs2 [sizeN rootfsN]...

Merge multiple rootfs into one ISO. Rootfs are like russian dolls
i.e: rootfsN is a subset of rootfsN-1
rootfs1 is found in ISO, sizeN is the RAM size needed to launch rootfsN.
The boot loader will select the rootfs according to the RAM size detected.

Example:
$ tazlito merge 160M slitaz-core.iso 96M rootfs-justx.gz 32M rootfs-base.gz

Will start slitaz-core with 160M+ RAM, slitaz-justX with 96M-160M RAM,
slitaz-base with 32M-96M RAM and display an error message if RAM < 32M.

EOT
			exit 2
		fi

		shift	# skip merge
		append="$1 slitaz1"
		shift	# skip size1
		mkdir -p $TMP_DIR/mnt $TMP_DIR/rootfs1

		ISO=$1.merged

		# Extract filesystems
		action 'Mounting %s' "$1"
		mount -o loop,ro $1 $TMP_DIR/mnt 2> /dev/null
		status || cleanup_merge

		cp -a $TMP_DIR/mnt $TMP_DIR/iso
		make_bzImage_hardlink $TMP_DIR/iso/boot
		umount -d $TMP_DIR/mnt
		if [ -f $TMP_DIR/iso/boot/rootfs1.gz ]; then
			_ '%s is already a merged iso. Aborting.' "$1"
			cleanup_merge
		fi
		if [ ! -f $TMP_DIR/iso/boot/isolinux/ifmem.c32 ] &&
		   [ ! -f $TMP_DIR/iso/boot/isolinux/c32box.c32 ]; then
			if [ ! -f /boot/isolinux/ifmem.c32 ] &&
			   [ ! -f /boot/isolinux/c32box.c32 ]; then
				cat <<EOT
No file /boot/isolinux/ifmem.c32
Please install syslinux package !
EOT
				rm -rf $TMP_DIR
				exit 1
			fi
			cp /boot/isolinux/c32box.c32 $TMP_DIR/iso/boot/isolinux 2> /dev/null ||
			cp /boot/isolinux/ifmem.c32 $TMP_DIR/iso/boot/isolinux
		fi

		action 'Extracting %s' 'iso/rootfs.gz'
		extract_rootfs $TMP_DIR/iso/boot/rootfs.gz $TMP_DIR/rootfs1 &&
			[ -d $TMP_DIR/rootfs1/etc ]
		status || cleanup_merge

		n=1
		while [ -n "$2" ]; do
			shift	# skip rootfs N-1
			p=$n
			n=$((n + 1))
			append="$append $1 slitaz$n"
			shift	# skip size N
			mkdir -p $TMP_DIR/rootfs$n

			action 'Extracting %s' "$1"
			extract_rootfs $1 $TMP_DIR/rootfs$n &&
			[ -d "$TMP_DIR/rootfs$n/etc" ]
			status || cleanup_merge

			mergefs $TMP_DIR/rootfs$n $TMP_DIR/rootfs$p
			action 'Creating %s' "rootfs$p.gz"
			pack_rootfs "$TMP_DIR/rootfs$p" "$TMP_DIR/iso/boot/rootfs$p.gz"
			status
		done
		action 'Creating %s' "rootfs$n.gz"
		pack_rootfs "$TMP_DIR/rootfs$n" "$TMP_DIR/iso/boot/rootfs$n.gz"
		status
		rm -f $TMP_DIR/iso/boot/rootfs.gz
		update_bootconfig $TMP_DIR/iso/boot/isolinux "$append"
		create_iso $ISO $TMP_DIR/iso
		rm -rf $TMP_DIR
		;;


	repack)
		# Repack an iso with maximum lzma compression ratio.
		#
		ISO=$2
		mkdir -p $TMP_DIR/mnt

		# Extract filesystems
		action 'Mounting %s' "$ISO"
		mount -o loop,ro $ISO $TMP_DIR/mnt 2>/dev/null
		status || cleanup_merge

		cp -a $TMP_DIR/mnt $TMP_DIR/iso
		umount -d $TMP_DIR/mnt

		for i in $TMP_DIR/iso/boot/rootfs* ; do
			action 'Repacking %s' "$(basename $i)"
			uncompress $i 2>/dev/null > $TMP_DIR/rootfs
			lzma e $TMP_DIR/rootfs $i $(lzma_switches $TMP_DIR/rootfs)
			align_to_32bits $i
			status
		done

		create_iso $ISO $TMP_DIR/iso
		rm -rf $TMP_DIR
		;;


	build-loram)
		# Build a Live CD for low RAM systems.
		#
		ISO="$2"
		OUTPUT="$3"
		[ -z "$3" ] && \
			die "Usage: tazlito build-loram <input>.iso <output>.iso [cdrom|smallcdrom|http|ram]"
		mkdir -p "$TMP_DIR/iso"
		mount -o loop,ro -t iso9660 "$ISO" "$TMP_DIR/iso"
		loopdev=$( (losetup -a 2>/dev/null || losetup) | sed "/$(echo $ISO | sed 's|/|\\/|g')$/!d;s/:.*//;q")
		if ! check_iso_for_loram ; then
			umount -d "$TMP_DIR/iso"
			die "$ISO is not a valid SliTaz live CD. Abort."
		fi
		case "$4" in
			cdrom)		build_loram_cdrom ;;
			http)		build_loram_http ;;
			*)		build_loram_ram "$5" ;;
		esac
		umount $TMP_DIR/iso	# no -d: needs /proc
		losetup -d $loopdev
		rm -rf $TMP_DIR
		;;


	emu-iso)
		# Emulate an ISO image with Qemu.
		iso="${2:-$DISTRO/$ISO_NAME.iso}"
		[ -f "$iso" ] || die "Unable to find ISO file '$iso'."
		[ -x '/usr/bin/qemu' ] || die "Unable to find Qemu binary. Please install package 'qemu'."
		echo -e "\nStarting Qemu emulator:\n"
		echo -e "qemu $QEMU_OPTS $iso\n"
		qemu $QEMU_OPTS $iso
		;;


	deduplicate)
		# Deduplicate files in a tree
		shift
		deduplicate "$@"
		;;


	usage|*)
		# Print usage also for all unknown commands.
		usage
		;;
esac

exit 0
