#!/bin/sh
#
# Cook - A tool to cook and generate SliTaz packages. Read the README
# before adding or modifying any code in cook!
#
# Copyright (C) SliTaz GNU/Linux - GNU GPL v3
# Author: Christophe Lincoln <pankso@slitaz.org>
#

. /usr/lib/slitaz/libcook.sh

# A cook operates on the LOCAL wok/repo: bdep install/removal, the package
# compressors, the final chroot install -- none of them need the mirror, yet
# tazpkg keeps probing it (recharge "Getting bundle.tar.lzma Failed", "Unable
# to find ... mirrored"). Run the whole cook strictly local; the ONLY place the
# mirror is a genuine last resort (a build dep with no local package) re-enables
# it explicitly with `TAZPKG_OFFLINE= tazpkg ...`.
export TAZPKG_OFFLINE=1

VERSION="4.6.1"
export output=raw
prev_ts="/home/slitaz/cache/prev_ts"; touch $prev_ts


# Internationalization.

export TEXTDOMAIN='cook'


#
# Functions
#

usage() {
	cat <<EOT

$(boldify "$(_ 'Usage:')") $(_ 'cook [package|command] [list|--option]')

$(boldify "$(_ 'Commands:')")
  usage|help           $(_ 'Display this short usage.')
  setup                $(_ 'Setup your build environment.')
  *-setup              $(_ 'Setup a cross environment.')
                       * = {arm|armv6hf|armv7|x86_64}
  test                 $(_ 'Test environment and cook a package.')
  doctor               $(_ 'Diagnose the environment or a package.')
  list-wok             $(_ 'List packages in the wok.')
  search               $(_ 'Simple packages search function.')
  new                  $(_ 'Create a new package with a receipt.')
  list                 $(_ 'Cook a list of packages.')
  clean-wok            $(_ 'Clean-up all packages files.')
  clean-src            $(_ 'Clean-up all packages sources.')
  uncook               $(_ 'Check for uncooked packages')
  wanted               $(_ 'List package to cook according to wanted.')
  build_depends        $(_ 'List package to cook according to build_depends.')
  build_loop           $(_ 'List package with a dependancy loop.')
  pkgdb                $(_ 'Create packages DB lists and flavors.')
  all                  $(_ 'Rebuild all packages from a cookorder list.')
  howto                $(_ 'Show usage examples.')

$(boldify "$(_ 'Options:')")
cook <pkg>
    --clean       -c   $(_ 'clean the package in the wok.')
    --getsrc      -gs  $(_ 'get the package source tarball.')
    --block       -b   $(_ 'block a package so cook will skip it.')
    --unblock     -ub  $(_ 'unblock a blocked package.')
    --cdeps            $(_ 'check dependencies of cooked package.')
    --pack             $(_ 'repack an already built package.')
    --debug            $(_ 'display debugging messages.')
    --continue         $(_ 'continue running compile_rules.')
cook new <pkg>
    --interactive -x   $(_ 'create a receipt interactively.')
cook setup
    --wok              $(_ 'clone the cooking wok from Hg repo.')
    --stable           $(_ 'clone the stable wok from Hg repo.')
    --undigest         $(_ 'clone the undigest wok from Hg repo.')
    --tiny             $(_ 'clone the tiny SliTaz wok from Hg repo.')
    --forced           $(_ 'force reinstall of chroot packages.')
cook pkgdb
    --flavors          $(_ 'create up-to-date flavors files.')
cook splitdb           $(_ 'create up-to-date split.db file.')

EOT
	exit 0
}


# We don't want these escapes in web interface.

clean_log() {
	sed -i -e 's|\[70G\[ \[1;32m| |' \
		-e 's|\[0;39m \]||' $LOGS/${1:-$pkg}.log
}


# Be sure package exists in wok.

check_pkg_in_wok() {
	[ -d "$WOK/$pkg" ] || die 'Unable to find package "%s" in the wok' "$pkg"
}


# Find the package, return the receipt name where it was found
# for example, libpcreposix -> pcre

find_pkg_in_wok() {
	awk -F$'\t' -vi=" $1 " '{
		if (index(" " $2 " ", i)) {print $1; exit}
	}' $cache/split.db
}


# Initialize files used in $CACHE

# Paths used in receipt and by cook itself.

set_paths() {
	# Kernel version is set from wok/linux or installed/linux-api-headers(wok-undigest)
	if [ -f "$WOK/linux/receipt" ]; then
		kvers=$(. $WOK/linux/receipt; echo $VERSION)
		kbasevers=$(echo $kvers | cut -d. -f1,2)
	elif [ -f "$INSTALLED/linux-api-headers/receipt" ]; then
		kvers=$(. $INSTALLED/linux-api-headers/receipt; echo $VERSION)
		kbasevers=$(echo $kvers | cut -d. -f1,2)
	fi

	# Python version
	[ -f "$WOK/python/receipt" ] && pyvers=$(. $WOK/python/receipt; echo $VERSION)
	# Perl version for some packages needed it
	[ -f "$WOK/perl/receipt" ] && perlvers=$(. $WOK/perl/receipt; echo $VERSION)

	pkgdir="$WOK/$pkg"
	. "$pkgdir/receipt"
	basesrc="$pkgdir/source"
	tmpsrc="$basesrc/tmp"
	src="$basesrc/$PACKAGE-$VERSION"
	taz="$pkgdir/taz"
	pack="$taz/${1:-$PACKAGE}-$VERSION$EXTRAVERSION"	# v2: multiple taz/* folders
	fs="$pack/fs"
	stuff="$pkgdir/stuff"
	install="$pkgdir/install"

	pkgsrc="${SOURCE:-$PACKAGE}-${KBASEVER:-$VERSION}"
	lzma_tarball="$pkgsrc.tar.lzma"

	[ -n "$PATCH" -a -z "$PTARBALL" ] && PTARBALL="$(basename $PATCH)"

	if [ -n "$WANTED" ]; then
		basesrc="$WOK/$WANTED/source"
		src="$basesrc/$WANTED-$VERSION"
		install="$WOK/$WANTED/install"
		wanted_stuff="$WOK/$WANTED/stuff"
	fi

	[ -n "$SOURCE" ] && source_stuff="$WOK/$SOURCE/stuff"

	# Old way compatibility.
	_pkg="$install"
}


# Create source tarball when URL is a SCM.

create_tarball() {
	local tarball
	tarball="$pkgsrc.tar.bz2"
	[ -n "$LZMA_SRC" ] && tarball="$lzma_tarball"
	_ 'Creating tarball "%s"' "$tarball"
	if [ -n "$LZMA_SRC" ]; then
		tar -c $pkgsrc | lzma e $SRC/$tarball -si $LZMA_SET_DIR || exit 1
		LZMA_SRC=''
	else
		tar -cjf $tarball $pkgsrc || exit 1
		mv $tarball $SRC; rm -rf $pkgsrc
	fi
	TARBALL="$tarball"
}


# Get package source. For SCM we are in cache so clone here and create a
# tarball here.

get_source() {
	local url
	url=${WGET_URL#*|}
	set_paths
	pwd=$(pwd)
	case "$WGET_URL" in
		http://*|ftp://*|https://*)
			url="$MIRROR_URL/sources/packages/${TARBALL:0:1}/$TARBALL"
			wget -T 60 -c -O $SRC/$TARBALL $WGET_URL ||
			wget -T 60 -c -O $SRC/$TARBALL $url ||
			die 'ERROR: %s' "wget $WGET_URL"
			;;

		hg*|mercurial*)
			_ 'Getting source from %s...' 'Hg'
			_ 'URL: %s' "$url"
			_ 'Cloning to "%s"' "$pwd/$pkgsrc"
			if [ -n "$BRANCH" ]; then
				_ 'Hg branch: %s' "$BRANCH"
				hg clone $url --rev $BRANCH $pkgsrc ||
				die 'ERROR: %s' "hg clone $url --rev $BRANCH"
			else
				hg clone $url $pkgsrc || die 'ERROR: %s' "hg clone $url"
			fi
			rm -rf $pkgsrc/.hg
			create_tarball
			;;

		git*)
			_ 'Getting source from %s...' 'Git'
			_ 'URL: %s' "$url"
			cd $SRC
			git clone $url $pkgsrc || die 'ERROR: %s' "git clone $url"
			if [ -n "$BRANCH" ]; then
				_ 'Git branch: %s' "$BRANCH"
				cd $pkgsrc; git checkout $BRANCH; cd ..
			fi
			cd $SRC
			create_tarball
			;;

		cvs*)
			mod=$PACKAGE
			[ -n "$CVS_MODULE" ] && mod=$CVS_MODULE
			_ 'Getting source from %s...' 'CVS'
			_ 'URL: %s' "$url"
			[ -n "$CVS_MODULE" ] && _ 'CVS module: %s' "$mod"
			_ 'Cloning to "%s"' "$pwd/$mod"
			cvs -d:$url co $mod && mv $mod $pkgsrc
			create_tarball
			;;

		svn*|subversion*)
			_ 'Getting source from %s...' 'SVN'
			_ 'URL: %s' "$url"
			if [ -n "$BRANCH" ]; then
				echo t | svn co $url -r $BRANCH $pkgsrc
			else
				echo t | svn co $url $pkgsrc
			fi
			create_tarball
			;;

		bzr*)
			_ 'Getting source from %s...' 'bazaar'
			cd $SRC
			pkgsrc=${url#*:}
			if [ -n "$BRANCH" ]; then
				echo "bzr -Ossl.cert_reqs=none branch $url -r $BRANCH"
				bzr -Ossl.cert_reqs=none branch $url -r $BRANCH
			else
				echo "bzr -Ossl.cert_reqs=none branch $url"
				bzr -Ossl.cert_reqs=none branch $url
				cd $pkgsrc; BRANCH=$(bzr revno); cd ..
				_ "Don't forget to add to receipt:"
				echo -e "BRANCH=\"$BRANCH\"\n"
			fi
			mv $pkgsrc $pkgsrc-$BRANCH
			pkgsrc="$pkgsrc-$BRANCH"
			create_tarball
			;;

		*)
			broken; die 'ERROR: Unable to handle "%s"' "$WGET_URL"
			;;
	esac
}


# Extract source package.

extract_source() {
	if [ ! -s "$SRC/$TARBALL" ]; then
		local url
		url="$MIRROR_URL/sources/packages"
		url="$url/${TARBALL:0:1}/$TARBALL"
		_ 'Getting source from %s...' 'mirror'
		_ 'URL: %s' "$url"
		busybox wget -c -P $SRC $url || _ 'ERROR: %s' "wget $url"
	fi
	_ 'Extracting source archive "%s"' "$TARBALL"
	case "$TARBALL" in
		*.tar.gz|*.tgz)         tar   -xzf    $SRC/$TARBALL 2>/dev/null ;;
		*.tar.bz2|*.tbz|*.tbz2) tar   -xjf    $SRC/$TARBALL 2>/dev/null ;;
		*.tar.lzma)             tar   -xaf    $SRC/$TARBALL ;;
		*.tar.lz|*.tlz)         lzip  -d    < $SRC/$TARBALL | tar -xf - 2>/dev/null ;;
		*.tar)                  tar   -xf     $SRC/$TARBALL ;;
		*.zip|*.xpi)            unzip -o      $SRC/$TARBALL 2>/dev/null >&2;;
		*.xz)                   unxz  -c      $SRC/$TARBALL | tar -xf - || \
		                        tar   -xf     $SRC/$TARBALL 2>/dev/null;;
		*.7z)                   7zr    x      $SRC/$TARBALL 2>/dev/null >&2 ;;
		*.Z|*.z)                uncompress -c $SRC/$TARBALL | tar -xf - ;;
		*.rpm)                  rpm2cpio      $SRC/$TARBALL | cpio -idm --quiet ;;
		*.run)                  /bin/sh       $SRC/$TARBALL $RUN_OPTS ;;
		*)                      cp            $SRC/$TARBALL $(pwd) ;;
	esac
}


# Display time.

disp_time_old() {
	local sec div min
	sec="$1"
	div=$(( ($1 + 30) / 60))
	case $div in
		0) min='';;
		# L10n: 'm' is for minutes (approximate cooking time)
		*) min=$(_n ' ~ %dm' "$div");;
	esac

	# L10n: 's' is for seconds (cooking time)
	_ '%ds%s' "$sec" "$min"
}


# Display time.

disp_time() {
	local sec="$1" day hour min out=''

	 day=$(( sec / 86400 )); sec=$(( sec % 86400 ))
	hour=$(( sec / 3600  )); sec=$(( sec % 3600  ))
	 min=$(( sec / 60    )); sec=$(( sec % 60    ))

	[              $day  -gt 0 ] && out="${day}d "
	[ -n "$out" -o $hour -gt 0 ] && out="$out$(printf '%02dh ' $hour)"
	[ -n "$out" -o $min  -gt 0 ] && out="$out$(printf '%02dm ' $min)"
	[ -n "$out"                ] && out=" ~ $out$(printf '%02ds'  $sec)"

	echo "${1}s$out"
}


# Display cooked package summary.

summary() {
#	local arch=''
#	case "$ARCH" in
#		arm*|x86_64) arch="-$ARCH" ;;
#	esac

	set_paths
	cd $WOK/$pkg
	[ -d $WOK/$pkg/install ] && prod=$(du -sh $WOK/$pkg/install | awk '{print $1}' 2>/dev/null)
	[ -d $WOK/$pkg/source ] && srcdir=$(du -sh $WOK/$pkg/source | awk '{print $1}' 2>/dev/null)
	[ -n "$TARBALL" ] && srcsize=$(du -sh $SRC/$TARBALL | awk '{print $1}')

	title 'Summary for: %s' "$PACKAGE $VERSION$EXTRAVERSION"

	# L10n: keep the same width of translations to get a consistent view
	[ -n "$TARBALL" ] && _ 'Src file    : %s' "$TARBALL"
	[ -n "$srcsize" ] && _ 'Src size    : %s' "$srcsize"
	[ -n "$srcdir" ]  && _ 'Source dir  : %s' "$srcdir"
	[ -n "$prod" ]    && _ 'Produced    : %s' "$prod"
	_ 'Cook time   : %s' "$(disp_time "$time")"
	_ 'Cook date   : %s' "$(date "$(_ '+%%F %%R')")"
	_ 'Target arch : %s' "$(cut -d$'\t' -f2 $pkgdir/.arch | sort -u | tr '\n' ' ' | sed 's| $||; s| |, |g')"

	separator -
	_ ' # :  Packed : Compressed : Files : Package name'
	separator -
	pkgi=1
	for i in $(all_names); do
		version=$(awk -F$'\t' -vpkg="$i" '{
			if ($1 == pkg) {print $2; exit}
		}' "$PKGS/packages.info")
		[ -n "$version" ] || continue
		fs=$(du -sh $WOK/$pkg/taz/$i-$VERSION$EXTRAVERSION | awk '{print $1}')
		arch=$(awk -F$'\t' -vi="$i" '{if ($1 == i) print $2}' $pkgdir/.arch)
		# match the package filename rule used at pack time (see farch above)
		case "$arch" in
			i486) sfx='' ;;
			any)  [ "$ARCH" = 'i486' ] && sfx='' || sfx='-any' ;;
			*)    sfx="-$arch" ;;
		esac
		pkgname="$i-$version$sfx.tazpkg"
		[ -f "$PKGS/$pkgname" ] || continue
		size=$(ls -lh $PKGS/$pkgname | awk '{print $5}')
		files=$(wc -l < $WOK/$pkg/taz/$i-$VERSION$EXTRAVERSION/files.list)
		printf "%2d : %7s : %10s : %5s : %s\n" "$pkgi" "$fs" "$size" "$files" "$pkgname"
		pkgi=$((pkgi + 1))
	done
	separator
}


# Display debugging error info.

debug_info() {
	title 'Debug information %s.' "$1"
	# L10n: specify your format of date and time (to help: man date)
	# L10n: not bad one is '+%x %R'
	_ 'Cook date: %s' "$(date "$(_ '+%%F %%R')")"
	if [ -n "$time" ]; then
		times="$(($(date +%s) - $time))"
		_ 'Wasted time : %s' "$(disp_time "$times")"
	fi
	for error in \
		ERROR 'No package' "cp: can't" "can't open" "can't cd" \
		'error:' 'fatal error:' 'undefined reference to' \
		'Unable to connect to' 'link: cannot find the library' \
		'CMake Error' ': No such file or directory' \
		'Could not read symbols: File in wrong format'
	do
		# format "line number:line content"
		fgrep -n "$error" $LOGS/$pkg.log
	done > $LOGS/$pkg.log.debug_info 2>&1
	# sort by line number, remove duplicates
	sort -gk1,1 -t: -u $LOGS/$pkg.log.debug_info
	rm -f $LOGS/$pkg.log.debug_info
	footer
}


# A bit smarter function than the classic `cp` command

scopy() {
	if [ "$(stat -c %h -- "$1")" -eq 1 ]; then
		cp -a  "$1" "$2"	# copy generic files
	else
		cp -al "$1" "$2"	# copy hardlinks
	fi
}


# Copy all generic files (locale, pixmaps, .desktop) from $install to $fs.
# We use standard paths, so some packages need to copy these files with the
# receipt and genpkg_rules.
# This function executes inside the packaging process, before compressor call.

copy_generic_files() {
	# Proceed only for "main" package (for v2), and for any packages (v1)
	[ "$pkg" == "$PACKAGE" ] || return 0

	# $LOCALE is set in cook.conf
	if [ -n "$LOCALE" -a -z "$WANTED" ]; then
		if [ -d "$install/usr/share/locale" ]; then
			mkdir -p "$fs/usr/share/locale"
			for i in $LOCALE; do
				if [ -d  "$install/usr/share/locale/$i" ]; then
					cp -r $install/usr/share/locale/$i $fs/usr/share/locale
				fi
			done
		fi
	fi

	# Generic pixmaps copy can be disabled with COOKOPTS="!pixmaps" (or GENERIC_PIXMAPS="no")
	if [ "${COOKOPTS/!pixmaps/}" == "$COOKOPTS" -a "$GENERIC_PIXMAPS" != 'no' ]; then
		if [ -d "$install/usr/share/pixmaps" ]; then
			mkdir -p "$fs/usr/share/pixmaps"
			for i in png xpm; do
				[ -f "$install/usr/share/pixmaps/$PACKAGE.$i" -a ! -f "$fs/usr/share/pixmaps/$PACKAGE.$i" ] &&
				cp -r $install/usr/share/pixmaps/$PACKAGE.$i $fs/usr/share/pixmaps
			done
		fi
	fi

	# Desktop entry (.desktop).
	# Generic desktop entry copy can be disabled with COOKOPTS="!menus" (or GENERIC_MENUS="no")
	if [ "${COOKOPTS/!menus/}" == "$COOKOPTS" -a "$GENERIC_MENUS" != 'no' ]; then
		if [ -d "$install/usr/share/applications" -a -z "$WANTED" ]; then
			mkdir -p "$fs/usr/share"
			cp -r $install/usr/share/applications $fs/usr/share
		fi
	fi
}


# Copy pixmaps, desktop files and licenses from $stuff to $install.
# This function executes after the main compile_rules() is done.

copy_generic_stuff() {
	# Custom or homemade PNG pixmap can be in stuff.
	if [ -f "$stuff/$PACKAGE.png" ]; then
		mkdir -p $install/usr/share/pixmaps
		cp $stuff/$PACKAGE.png $install/usr/share/pixmaps
	fi

	# Homemade desktop file(s) can be in stuff.
	if [ -d "$stuff/applications" ]; then
		mkdir -p $install/usr/share
		cp -r $stuff/applications $install/usr/share
	fi
	if [ -f "$stuff/$PACKAGE.desktop" ]; then
		mkdir -p $install/usr/share/applications
		cp $stuff/$PACKAGE.desktop $install/usr/share/applications
	fi

	# Add custom licenses
	if [ -d "$stuff/licenses" ]; then
		mkdir -p $install/usr/share/licenses
		cp -r $stuff/licenses $install/usr/share/licenses/$PACKAGE
	fi
}


# A build dep is "really installed" only if its receipt AND every file it
# owns are on disk. A removed shared runtime can take a -dev package's
# header/lib with it while the tazpkg DB entry stays (phantom cap-ng.h,
# libbz2.so.1.0). Used to decide whether to (re)install a build dep.
dep_files_ok() {
	local fl="$root$INSTALLED/$1/files.list" f
	[ -f "$fl" ] || return 1
	# -L: a dangling symlink is still an installed file (iso-codes ships
	# upstream compat links whose target locale was never translated);
	# -e alone follows the link and misreads it as a missing file
	while read -r f; do
		[ -e "$root$f" ] || [ -L "$root$f" ] || return 1
	done < "$fl"
	return 0
}


# Update installed.cook.diff

update_installed_cook_diff() {
	# If a cook failed deps are removed.
	cd $root$INSTALLED; ls -1 > $CACHE/installed.cook
	cd $CACHE
	[ "$1" == 'force' -o ! -s '/tmp/installed.cook.diff' ] && \
		busybox diff installed.list installed.cook > /tmp/installed.cook.diff
	deps=$(grep ^+[a-zA-Z0-9] /tmp/installed.cook.diff | wc -l)
}


# Remove installed deps.

remove_deps() {
	# Remove only the build deps THIS cook actually installed (tracked
	# explicitly in cook-bdeps.list), never a global DB diff — so packages
	# installed by hand are never swept away.
	local list="$CACHE/cook-bdeps.list" dep nb
	[ -s "$list" ] || return

	nb=$(grep -c . "$list")
	newline
	_n 'Build dependencies to remove:'; echo " $nb"
	[ -n "$root" ] && echo "root=\"$root\""

		_n 'Removing:'
		for dep in $(cat "$list"); do
			[ -f "$root$INSTALLED/$dep/receipt" ] || continue
			# Versioned gcc variants (gcc49, gcc63, gcc83...) install into
			# the same paths as the base gcc: removing one as a bdep sweeps
			# cc1 & co from the live toolchain (seen 2026-06-13: lpcnet's
			# gcc63 bdep removal broke every C build after it). Keep them.
			case "$dep" in
				gcc|gcc[0-9]*) echo -n " $dep(kept)"; continue ;;
			esac
			echo -n " $dep"
			# Do not waste time uninstalling the packages if we are inside
			# aufs chroot - unmounting chroot will "uninstall" all packages.
			[ -s /aufs-umount.sh ] ||
			echo 'y' | tazpkg remove $dep --root=$root >/dev/null
		done

	newline; newline
	# Keep the list for debug and info.
	mv -f "$list" $CACHE/cook-bdeps.last
}


# Automatically patch the sources.

patchit() {
	[ -f "$stuff/patches/series" ] || return
	# Empty lines and comments (started with "#") are ignored
	# Up to three fields (no spaces inside allowed) separated by "|":
	#   1. patch options like "-p0" (optional);
	#   2. patch file name or URL (mandatory);
	#   3. patch checksum in form "sha1=..." or other *sum (optional).

	local done="$pkgdir/.patch.done" var1 var2 var3
	local patchname patchopts patchfile patchsum patchsum_type patchsum_sum
	IFS=$'\n'
	while read i; do
		patchname=$(echo ${i%%#*} | cut -d' ' -f1)	# allow comments (anything after the # or space)
		[ -n "$patchname" ] || continue				# skip empty lines

		var1=$(echo "$patchname||" | cut -d'|' -f1) # options  or name
		var2=$(echo "$patchname||" | cut -d'|' -f2) # name     or checksum or empty
		var3=$(echo "$patchname||" | cut -d'|' -f3) # checksum or empty

		if [ -n "$var3" ]; then
			patchopts="$var1"; patchname="$var2"; patchsum="$var3"
		elif [ -n "$var2" ]; then
			case $var2 in
				*=*) patchopts='-Np1';  patchname="$var1"; patchsum="$var2";;
				*)   patchopts="$var1"; patchname="$var2"; patchsum='';;
			esac
		else
			patchopts='-Np1'; patchname="$var1"; patchsum=''
		fi

		case $patchname in
				ftp://*|http://*|https://*)
					patchfile="$SRC/$(basename $patchname)"
					[ -e "$patchfile" ] || wget -q -T 60 -O $patchfile $patchname ||
					die 'ERROR: %s' "can't get $patchname"
					;;
				*)
					patchfile="$stuff/patches/$patchname"
					;;
		esac

		if [ -n "$patchsum" ]; then
			patchsum_type=${patchsum%=*}
			patchsum_sum=${patchsum#*=}
			echo "$patchsum_sum  $patchfile" | ${patchsum_type}sum -cs ||
			die 'ERROR: %s' "wrong ${patchsum_type}sum for $patchfile"
		else
			case $patchfile in
				$SRC/*) echo "warning: no checksum for external patch!";;
			esac
		fi

		touch $done
		grep -q "^${patchname}$" $done && continue	# already applied (useful with `cook --continue`)

		newline
		_ 'Applying patch %s' "$patchname"
		patch $patchopts -i $patchfile | sed 's|^|  |'

		echo $patchname >> $done
	done < $stuff/patches/series
	newline
	unset IFS
}


# Check source tarball integrity.

check_integrity() {
	for i in sha1 sha3 sha256 sha512 md5; do
		I=$(echo $i | tr 'a-z' 'A-Z')
		eval sum=\$TARBALL_$I
		if [ -n "$sum" ]; then
			newline
			_ 'Checking %ssum of source tarball...' "$i"
			echo "$sum  $SRC/$TARBALL" | ${i}sum -c || exit 1
		fi
	done
	newline
}


# Misc fix functions

fix() {
	case $1 in
		# https://bugzilla.gnome.org/show_bug.cgi?id=655517
		# https://wiki.gentoo.org/wiki/Project:Quality_Assurance/As-needed
		ld)
			export LDFLAGS="$LDFLAGS -Wl,-Os,--as-needed"
			;;
		libtool)
			if [ -e 'libtool' ]; then
				sed -i 's| -shared | -Wl,-Os,--as-needed\0|g' libtool
				echo "fix.libtool" >> $pkgdir/.patch.done
			else
				echo "fix libtool: warning: libtool absent, nothing to fix."
			fi
			;;
		math)
			# fix C++ math issue introduced in Glibc 2.26:
			# error: '__builtin_isnan' is not a member of 'std'
			#   if (std::isnan(N)) {
			#            ^
			find $src -type f -exec sed -i '
				s|std::signbit|__builtin_signbit|g;
				s|std::isnan|__builtin_isnan|g;
				s|std::isinf|__builtin_isinf_sign|g;
				s|std::isfinite|__builtin_isfinite|g;
				s|std::isnormal|__builtin_isnormal|g
				' '{}' \;
			;;
		symlinks)
			# make absolute symlinks relative
			echo "fix symlinks"
			local ifs="$IFS" link target
			IFS=$'\n'
			# step 1: fast job, prefix all the absolute symlinks with "$install"
			for link in $(find $install -type l); do
				target="$(readlink $link)"
				case "$target" in
					/*) ln -sfv "$install$target" "$link";;
				esac
			done
			IFS="$ifs"
			# step 2: fine tuning, make symlinks relative
			tazpkg -gi --quiet --local --cookmode symlinks
			symlinks -cr $install
			;;
		gem)
			# some useful operations while Ruby gems cooking
			_gems="$(ruby -e'puts Gem.default_dir')"

			# remove unwanted empty folders
			rmdir --ignore-fail-on-non-empty \
				$install/$_gems/build_info/ \
				$install/$_gems/cache/ \
				$install/$_gems/doc/ \
				$install/$_gems/extensions/

			# move files to docdir
			docdir=$install/usr/share/doc/$PACKAGE-$VERSION
			for i in $(ls -ap $install/$_gems/gems/${PACKAGE#*-}-$VERSION/ | sed '
				/\/$/d; /^\./d; /gemspec$/d; /Rakefile*/d; /Gemfile*/d; /Makefile/d;
				/\.c$/d; /\.h$/d; /\.o$/d; /\.rb$/d; /\.so$/d; /\.yml$/d;
				/Manifest/d; /\.inc$/d; /depend/d;
				'); do
				mkdir -p $docdir # docdir will not be created when nothing to move
				mv $install/$_gems/gems/${PACKAGE#*-}-$VERSION/$i $docdir
			done
			if [ -d $install/$_gems/gems/${PACKAGE#*-}-$VERSION/doc/ ]; then
				mkdir -p $docdir
				mv $install/$_gems/gems/${PACKAGE#*-}-$VERSION/doc/ $docdir
			fi

			if [ -d $docdir ]; then
				# move man pages
				unset man_to_copy
				for i in $(seq 1 8); do
					for j in $(find $docdir -type f -name "*.$i" | sed '/LGPL-2\.1/d'); do
						man_to_copy="$man_to_copy $j"
					done
				done
				if [ -n "$man_to_copy" ]; then
					cook_pick_manpages $man_to_copy
					rm $man_to_copy
				fi

				# convert rdoc to markdown (thanks https://gist.github.com/teeparham/8a99e308884e1c32735a)
				for i in $(find $docdir -type f -name '*.rdoc'); do
					fix utf-8
					LC_ALL=en_US.UTF-8 ruby -r rdoc -e 'puts RDoc::Markup::ToMarkdown.new.convert File.read(ARGV[0] || "'$i'")' >$i.md && rm $i || rm $i.md
				done
			fi

			# move man pages (from the different place)
			rubyman=$install/$_gems/gems/${PACKAGE#*-}-$VERSION/man
			if [ -d $rubyman ]; then
				unset man_to_copy
				for i in $(seq 1 8); do
					for j in $(find $rubyman -type f -name "*.$i" | sed '/LGPL-2\.1/d'); do
						man_to_copy="$man_to_copy $j"
					done
				done
				if [ -n "$man_to_copy" ]; then
					cook_pick_manpages $man_to_copy
				fi
				rm -r $rubyman
			fi
			;;
		utf-8)
			# Install UTF-8 locale
			tazpkg -gi --quiet --local --cookmode locale-en-base
			mkdir -p /usr/lib/locale
			localedef -i 'en_US' -c -f 'UTF-8' /usr/lib/locale/en_US.UTF-8
			;;
	esac
}


# Typical function used in compile_rules() to make perl modules packages

cook_perl() {
	if [ -e "Makefile.PL" ]; then
		# Up to 3 optional parameters supported
		PERL_MM_USE_DEFAULT=1 perl Makefile.PL INSTALLDIRS=vendor $1 &&
		make $2 &&
		make $3 PERL_MM_USE_DEFAULT=1 DESTDIR=$install install &&
		chmod -R u+w $install
	elif [ -e "Build.PL" ]; then
		echo "Not implemented yet"
		return 1
	else
		echo "Unable to cook Perl module"
		return 1
	fi
}


# Store timestamps, log jobs length

timestamp() {
	local ts_file="$WOK/$pkg/.ts"
	local curr_ts=$(date '+%s')
	case $1 in
		init)
			rm $ts_file 2>/dev/null
			echo $curr_ts > $prev_ts
			;;
		job*)
			# calculate time from the last timestamp
			echo "$1='$(( $curr_ts - $(cat $prev_ts) ))'" >> $ts_file
			echo $curr_ts > $prev_ts
			;;
		sets)
			echo "sets='$2'" >> $ts_file
			;;
	esac
}


# Store time statsistics to the cache

store_timestats() {
	# see doc/timestats.txt for file format
	temp=$(mktemp)
	{
		for i in $(seq 1 30); do echo "job$i=0"; done
		cat $WOK/$pkg/.ts
		echo -n 'total=$(( 0'
		for i in $(seq 1 30); do echo -n " + job$i"; done
		echo ' ))'
	} > $temp
	. $temp
	{
		echo -n "$pkg	$sets	"
		for i in $(seq 1 30); do echo -n "$((job$i))	"; done
		echo "$total"
	} >> /home/slitaz/cache/timestats
	rm $temp $WOK/$pkg/.ts # clean
}


# Internal function to cook specified SET

cook_set() {
	# Switch to the specified source set
	set_paths
	local suffix=''
	[ -n "$SET" ] && suffix="-$SET"
	export     src="$WOK/$pkg/source/$PACKAGE-$VERSION$suffix"
	export install="$WOK/$pkg/install$suffix"
	export DESTDIR="$install"

	if [ -n "$SETS" ]; then
		if [ -n "$SET" ]; then
			title "Switching to the set '$SET'"
		else
			title "Switching to the default set"
		fi
		echo "src    : $src"
		echo "install: $install"
	fi
	[ -d "$src" ] && cd $src	# packages without sources exists
	echo

	[ -d "$install" ] && rm -r $install
	#mkdir -p $install

	compile_rules $@ || { broken; exit 1; }

	# Stay compatible with _pkg
	[ -d "$src/_pkg" ] && mv $src/_pkg $install

	copy_generic_stuff

	timestamp job$job_counter # compiling (set '$SET')

	# Actions to do after compiling the package
	# Skip all for split packages (already done in main package)
	if [ -z "$WANTED" ]; then
		footer
		export COOKOPTS ARCH install
		@@PREFIX@@/libexec/cookutils/compressor install
		timestamp job$(($job_counter + 1)) # compressing (set '$SET')
	fi

	# Activate "instant-pack" mode
	if [ "${COOKOPTS/instant-pack/}" != "$COOKOPTS" ]; then
		echo " $SPLIT " | fgrep -q " $PACKAGE " || SPLIT="$PACKAGE $SPLIT"
		export PACKAGE
		# determine the list of the packages belongs to the current SET
		# and then pack them; subshell isolates receipt sourcing.
		echo -n $SPLIT \
		| awk -vset="$SET" '
			BEGIN { RS = " "; FS = ":"; }
			{ if ($2 == set) print $1; }' \
		| while read p; do (packit "$p"); done
	fi

	job_counter=$(($job_counter + 2))
}


# Is the active package mirror an ONLINE one (http/https/ftp) rather than a
# local filesystem path? The mirror file's FIRST non-blank line is the active
# mirror. The bdep loop uses this to decide policy when a build dep has no local
# package: online -> the last-resort `get-install` downloads it; local -> the
# maintainer is expected to have cooked it, so abort and say so. Missing/empty
# mirror file -> local (conservative: never silently expect a download). Only
# reads the file, so it is unaffected by the global TAZPKG_OFFLINE.

mirror_is_online() {
	local m url
	m="$root$DB/mirror"
	[ -s "$m" ] || return 1
	url=$(awk 'NF{gsub(/\r/,"");gsub(/^[ \t]+|[ \t]+$/,"");print;exit}' "$m")
	case "$url" in
		http://*|https://*|ftp://*) return 0 ;;
		*) return 1 ;;
	esac
}


# The main cook function.

cookit() {
	# SETUP_MD5 guards against starting a cook on a drifted base (bdeps from a
	# previous build still installed). A cook spawned from another package's
	# genpkg_rules (e.g. linux-libre cooks its sub-kernels there) legitimately
	# runs while the parent's bdeps ARE installed, so it must skip this check --
	# else it aborts "Broken setup" and the genpkg fails. COOK_NESTED is
	# exported around genpkg_rules and survives the child's cook.conf re-source.
	if [ -n "$SETUP_MD5" -a -z "$COOK_NESTED" -a "$SETUP_MD5" != "$(ls $root$INSTALLED | md5sum | cut -c1-32)" ]; then
		_ 'ERROR: Broken setup. Abort.'
		return
	fi

	title 'Cook: %s' "$PACKAGE $VERSION"
	set_paths
	timestamp init # the very start

	# Handle cross-tools.
	[ "$BUILD_SYSTEM" != "$HOST_SYSTEM" ] &&
	case "$ARCH" in
		arm*|x86_64)
			# CROSS_COMPILE is used by at least Busybox and the kernel to set
			# the cross-tools prefix. Sysroot is the root of our target arch
			sysroot="$CROSS_TREE/sysroot"
			tools="$CROSS_TREE/tools"
			# Set root path when cross compiling. ARM tested but not x86_64
			# When cross compiling we must install build deps in $sysroot.
			arch="-$ARCH"
			root="$sysroot"
			_ '%s sysroot: %s' "$ARCH" "$sysroot"
			_ 'Adding "%s" to PATH' "$tools/bin"
			export PATH="$PATH:$tools/bin"
			export PKG_CONFIG_PATH="$sysroot/usr/lib/pkgconfig"
			export CROSS_COMPILE="$HOST_SYSTEM-"
			_ 'Using cross-tools: %s' "$CROSS_COMPILE"
			if [ "$ARCH" == 'x86_64' ]; then
				export CC="$HOST_SYSTEM-gcc -m64"
				export CXX="$HOST_SYSTEM-g++ -m64"
			else
				export CC="$HOST_SYSTEM-gcc"
				export CXX="$HOST_SYSTEM-g++"
			fi
			export AR="$HOST_SYSTEM-ar"
			export AS="$HOST_SYSTEM-as"
			export RANLIB="$HOST_SYSTEM-ranlib"
			export LD="$HOST_SYSTEM-ld"
			export STRIP="$HOST_SYSTEM-strip"
			export LIBTOOL="$HOST_SYSTEM-libtool"
			;;
	esac

	@@PREFIX@@/libexec/cookutils/precheck $receipt || exit 1 # former receipt_quality()

	cd $pkgdir
	if [ -z "$continue" ]; then
		rm -rf source  2>/dev/null
		rm .patch.done 2>/dev/null
	fi
	rm -rf install taz 2>/dev/null

	# Disable -pipe if less than 512 MB free RAM.
	free=$(awk '/^MemFree|^Buffers|^Cached/{s+=$2}END{print int(s/1024)}' /proc/meminfo)
	if [ "$free" -lt 512 ] && [ "$CFLAGS" != "${CFLAGS/-pipe}" ]; then
		_ 'Disabling -pipe compile flag: %d MB RAM free' "$free"
		CFLAGS="${CFLAGS/-pipe}"; CFLAGS=$(echo "$CFLAGS" | tr -s ' ')
		CXXFLAGS="${CXXFLAGS/-pipe}"; CXXFLAGS=$(echo "$CXXFLAGS" | tr -s ' ')
	fi
	unset free

	# Export flags and path to be used by make and receipt.
	DESTDIR="$pkgdir/install"
	# FIXME: L10n: Is this the right time for 'LC_ALL=C LANG=C'?
	export DESTDIR MAKEFLAGS CFLAGS CXXFLAGS CONFIG_SITE LC_ALL=C LANG=C \
	LDFLAGS

	timestamp job1 # pre-checks

	# BUILD_DEPENDS may vary depending on the ARCH
	case "$ARCH" in
		arm*)   [ -n "$BUILD_DEPENDS_arm" ]    && BUILD_DEPENDS=$BUILD_DEPENDS_arm    ;;
		x86_64) [ -n "$BUILD_DEPENDS_x86_64" ] && BUILD_DEPENDS=$BUILD_DEPENDS_x86_64 ;;
	esac

	# Check for build deps and handle implicit depends of *-dev packages
	# (ex: libusb-dev :: libusb). Install the implicit base lib BEFORE the
	# *-dev: a *-dev DEPENDS on its base lib, so installing the *-dev first
	# makes tazpkg resolve the base itself (probing cache/mirror -> noisy
	# "can't open"/"Checksum error" that the post-check error scan then
	# flags as broken, even though the dep installs fine on the local
	# fallback). Base-first keeps the local-package install path clean.
	[ -n "$BUILD_DEPENDS" ] && _ 'Checking build dependencies...'
	[ -n "$root" ] && _ 'Using packages DB: %s' "$root$DB"

	# The local package repo's packages.info may not exist yet in a fresh
	# online-mirror environment (it is otherwise (re)written at pack time). The
	# bdep loop below and the build footer awk it for dep/version lookups;
	# without the file, busybox awk logs "No such file or directory" for every
	# lookup -- cosmetic but misleading. Ensure it exists early ($PKGS is the
	# host repo, never $root-prefixed elsewhere).
	mkdir -p "$PKGS"
	touch "$PKGS/packages.info"

	# Get the list of installed packages
	cd $root$INSTALLED; ls > $CACHE/installed.list
	: > $CACHE/cook-bdeps.list	# packages THIS cook installs (for removal)

	for action in check install; do
		for dep in $BUILD_DEPENDS; do
			implicit="${dep%-dev}"; [ "$implicit" == "$dep" ] && implicit=''
			for i in $implicit $dep; do
				# Skip only if installed AND its files are really on disk
				# (a stale DB entry with missing files must be reinstalled).
				[ -f "$root$INSTALLED/$i/receipt" ] && dep_files_ok "$i" && continue

				case $action in
					check)
						# Search for local package or local provided-package
						name=$(awk -F$'\t' -vpkg="$i" '{
						if (index(" " $1 " " $10 " ", " " pkg " ")) {print $1; exit}
						}' "$PKGS/packages.info")
						if [ -z "$name" ]; then
							# Search for package in mirror
							name="$(awk -F$'\t' -vi="$i" '$1==i{print $1; exit}' "$root$DB/packages.info")"
							[ -z "$name" -a "$i" == "$dep" ] && die 'ERROR: unknown dep "%s"' "$i"
						fi
						;;
					install)
						# Was this dep already in the DB before our cook?
						# (decides whether remove_deps drops it afterwards:
						# a present-but-broken dep we repair is NOT ours.)
						had_receipt=0
						[ -f "$root$INSTALLED/$i/receipt" ] && had_receipt=1
						# Find the dep's .tazpkg in the local $PKGS repo.
						# packages.info gives the exact name+version (and
						# maps PROVIDE aliases); but it can be stale mid
						# build session, so we also fall back to a direct
						# $PKGS glob to catch a freshly built dep not yet
						# in the DB. i486 .tazpkg have no arch suffix;
						# x86_64/arm carry -$ARCH or -any.
						local_name=$(awk -F$'\t' -vpkg="$i" '{
						if (index(" " $1 " " $10 " ", " " pkg " ")) {print $1 "\t" $2; exit}
						}' "$PKGS/packages.info")
						local_pkg=''
						if [ -n "$local_name" ]; then
							local_n=$(echo "$local_name" | cut -f1)
							local_v=$(echo "$local_name" | cut -f2)
							local_pkg=$(ls $PKGS/${local_n}-${local_v}.tazpkg \
								$PKGS/${local_n}-${local_v}-*.tazpkg \
								2>/dev/null | head -n1)
						fi
						[ -z "$local_pkg" ] && \
							local_pkg=$(ls $PKGS/${i}-*.tazpkg 2>/dev/null | head -n1)
						if [ -n "$local_pkg" ]; then
							# No "Installing local package: FILE" here: tazpkg's
							# cookmode "Installing (pkg/local): NAME" below already
							# announces each install (with its source, and for the
							# whole dep tree), so a cook-side line just doubles it.
							# Single call installs the exact local file AND pulls its
							# whole DEPENDS tree local-first:
							#  --local --cookmode: install_all_deps resolves every dep
							#    from $PKGS (DB lookup + on-disk glob), never the mirror
							#    -- so a dep that lives only in the local repo (e.g.
							#    perl-io-compress needing perl-compress-raw-zlib 2.101,
							#    not the perl-core 2.069) is picked correctly.
							#  --forced: rewrite a present-but-broken install (files
							#    gone while the DB entry stayed).
							#  TAZPKG_OFFLINE=1: no mirror probe (no "Getting bundle"
							#    spam); the mirror stays the last resort for the
							#    no-local case below (that path is NOT wrapped).
							# (Previously this was two calls -- a bare `-i` then a
							# `get-install --local` -- because `-i` alone didn't resolve
							# local i486 deps before the tazpkg cookmode finder fixes;
							# now one suffices, and each dep installs once.)
							TAZPKG_OFFLINE=1 tazpkg -i "$local_pkg" --root=$root --local --forced --quiet --cookmode || { broken; exit 1; }
							[ "$had_receipt" -eq 0 ] && echo "$i" >> $CACHE/cook-bdeps.list
							continue
						fi
						# Already satisfied with its files intact?
						[ -f "$root$INSTALLED/$i/receipt" ] && dep_files_ok "$i" && continue
						# No local .tazpkg and not installed: last resort, the
						# mirror. `get-install --cookmode` exits 0 even when the
						# package is missing (the cp/md5sum errors are not
						# fatal to it), so we verify the install ourselves
						# below instead of trusting its exit code. --forced
						# repairs a present-but-broken install too.
						# TAZPKG_OFFLINE= re-enables the mirror for this one call
						# (overrides the global cook offline): genuine last resort.
						TAZPKG_OFFLINE= tazpkg get-install $i --root=$root --local --forced --quiet --cookmode
						# An explicit build dep still not installed after the
						# last-resort mirror fetch can't be satisfied — abort
						# before wasting a compile. The advice depends on the
						# mirror type: an ONLINE mirror already had its chance to
						# download above, so the dep is genuinely absent there;
						# a LOCAL mirror means the maintainer is expected to have
						# cooked it locally first.
						if [ "$i" == "$dep" ] && [ ! -f "$root$INSTALLED/$i/receipt" ]; then
							broken
							if mirror_is_online; then
								die 'ERROR: build dependency "%s" not available on mirror %s' \
									"$i" "$(awk 'NF{gsub(/\r/,"");print;exit}' "$root$DB/mirror")"
							else
								die 'ERROR: build dependency "%s" not found, cook it first' "$i"
							fi
						fi
						[ "$had_receipt" -eq 0 ] && [ -f "$root$INSTALLED/$i/receipt" ] && \
							echo "$i" >> $CACHE/cook-bdeps.list
						;;
				esac
			done
		done
	done

	# Every explicit build dep must have its files on disk, not just a DB
	# entry. A legacy fs.tar.xz package (spk-era) that tazpkg can't extract
	# installs "empty" (DB says installed, disk has nothing) -- fail clearly
	# pointing at a re-cook, not a cryptic missing-header error later.
	for dep in $BUILD_DEPENDS; do
		[ -f "$root$INSTALLED/$dep/receipt" ] && ! dep_files_ok "$dep" && {
			broken
			die 'ERROR: build dep "%s" is installed but its files are missing on disk (legacy package?); re-cook it' "$dep"
		}
	done

	update_installed_cook_diff

	timestamp job2 # installing bdeps

	# Get source tarball and make sure we have source dir named:
	# $PACKAGE-$VERSION to be standard in receipts. Here we use tar.lzma
	# tarball if it exists.
	if [ -n "$WGET_URL" -a ! -f "$SRC/$TARBALL" ]; then
		if [ -f "$SRC/${SOURCE:-$PACKAGE}-$VERSION.tar.lzma" ]; then
			TARBALL="${SOURCE:-$PACKAGE}-$VERSION.tar.lzma"
			LZMA_SRC=''
		else
			get_source || { broken; exit 1; }
		fi
	fi
	if [ -z "$WANTED" -a -n "$TARBALL" -a ! -d "$src" ]; then
		mkdir -p $pkgdir/source/tmp; cd $pkgdir/source/tmp
		if ! extract_source ; then
			get_source
			extract_source || { broken; exit 1; }
		fi
		if [ -n "$LZMA_SRC" ]; then
			cd $pkgdir/source
			if [ "$(ls -A tmp | wc -l)" -gt 1 -o -f "$(echo tmp/*)" ]; then
				mv tmp tmp-1; mkdir tmp
				mv tmp-1 tmp/${SOURCE:-$PACKAGE}-$VERSION
			fi
			if [ -d "tmp/${SOURCE:-$PACKAGE}-$VERSION" ]; then
				cd tmp; tar -c * | lzma e $SRC/$TARBALL -si
			fi
		fi

		cd $pkgdir/source/tmp
		# Some archives are not well done and don't extract to one dir (ex lzma).
		files=$(ls | wc -l)
		[ "$files" -eq 1 -a -d "$(ls)" ] &&
			mv *     ../$PACKAGE-$VERSION
		[ "$files" -eq 1 -a -f "$(ls)" ] &&
			mkdir -p ../$PACKAGE-$VERSION &&
			mv *     ../$PACKAGE-$VERSION/$TARBALL
		[ "$files" -gt 1 ] &&
			mkdir -p ../$PACKAGE-$VERSION &&
			mv *     ../$PACKAGE-$VERSION
		cd ..; rm -rf tmp
	fi

	# Check md5sum (or similar) for sources tarball
	check_integrity

	# Libtool shared libs path hack.
	case "$ARCH" in
		arm*) cross libhack ;;
	esac

	timestamp job3 # get/unpack src tarball

	# Compiling all the sets
	if grep -q ^compile_rules $receipt; then
		_ 'Executing: %s' 'compile_rules'

		# Input-side counterpart to the shipped-.la scrub in cookit_pack():
		# the build deps just installed may carry .la whose dependency_libs
		# point at .la that SliTaz deliberately strips (libstdc++.la from gcc,
		# libfribidi.la, libgailutil.la...). libtool follows those and aborts
		# ("not a valid libtool archive") for every consumer (epdf, grisbi,
		# gnucash...). Blank dependency_libs in the installed .la before the
		# build; shared linking + pkg-config already carry the real deps.
		for la in $(find "$root/usr/lib" -type f -name '*.la' 2>/dev/null); do
			sed -i "s|^dependency_libs=.*|dependency_libs=''|" "$la"
		done

		echo "CFLAGS   : $CFLAGS"
		echo "CXXLAGS  : $CXXFLAGS"
		echo "CPPFLAGS : $CPPFLAGS"
		echo "LDFLAGS  : $LDFLAGS"
		[ -d "$src" ] && cd $src
		patchit

		timestamp job4 # patching

		# Get set names from $SPLIT variable, format ex. 'pkg1 pkg2:set1 pkg3:set2'
		# Keep natural order of the sets, don't sort them alphabetically
		SETS=$(echo -n $SPLIT \
			| awk '
				BEGIN { RS = " "; FS = ":"; }
				{
					if ($2 && ! set[$2]) { printf("%s ", $2); set[$2] = "1"; }
				}' \
			| sed 's| $||')
		# Prepare specified source sets using patched sources
		[ -n "$SETS" -a -d "$src" ] &&
		for set in $SETS; do
			echo "Preparing set $set"	# debug
			cp -a $src $src-$set
		done

		timestamp job5 # preparing sets
		timestamp sets "$SETS"

		job_counter='6'

		SET='' cook_set # first run for empty SET

		# Allow to change SETS after the first run, follow the changes
		SETS=$(. $receipt; echo -n $SPLIT \
			| awk '
				BEGIN { RS = " "; FS = ":"; }
				{
					if ($2 && ! set[$2]) { printf("%s ", $2); set[$2] = "1"; }
				}' \
			| sed 's| $||')
		for SET in $SETS; do
			cook_set
		done
	else
		mkdir -p $install	# allow receipts without `compile_rules()`
	fi
	footer

	timestamp job # reset counter

	# Execute testsuite.
	if grep -q ^testsuite $receipt; then
		title 'Running testsuite'
		testsuite $@ || { broken; exit 1; }
		footer
	fi

	timestamp job26 # test suite

	update_installed_cook_diff force
}


# Cook quality assurance.

cookit_quality() {
	while true; do
		[ ! -d "$WOK/$pkg/install" -a -z "$WANTED" ] || break
		_ 'ERROR: cook failed' | tee -a $LOGS/$pkg.log
		[ "$trials" == 'yes' ] || break
		title "Interactive mode"
		# TODO: allow commands:
		# q - quit; v - edit receipt here using vi;
		# s - search for package containing package;
		# <package name> - install package; [Enter] - retry
		_ 'You may install the packages here and/or edit the receipt there.'
		newline
		while true; do
			_n 'Install the package? [name/N] '; read answer
			[ -n "$answer" ] || break
			tazpkg -gi $answer --root=$root --local --quiet --cookmode
		done
		newline
		_n 'Try again? [Y/n] '; read answer
		[ "$answer" == 'n' ] && break
		# here you may append log if you want (">>" instead of last ">")
		cookit $@ 2>&1 | loglimit 50 > $LOGS/$pkg.log
	done

	[ "${COOKOPTS/skip-log-errors/}" != "$COOKOPTS" ] && return 0

	# ERROR can be echoed any time in cookit()
	if grep -Ev "(conftest|configtest)" $LOGS/$pkg.log | \
	   grep -Eq "(^ERROR|undefined reference to)" ; then
		debug_info "cookit_quality" | tee -a $LOGS/$pkg.log
		put_status $pkg Failed
#		rm -f $command
#		broken; exit 1
	fi
}


# Return all the names of packages bundled in this receipt

all_names() {
	# Get package names from $SPLIT variable
	local split=$(echo -n $SPLIT \
		| awk '
			BEGIN { RS = " "; FS = ":"; }
			{ print $1; }' \
		| tr '\n' ' ')
	local split_space=" $split "
	if ! head -n1 $WOK/$pkg/receipt | fgrep -q 'v2'; then
		# For receipts v1: $SPLIT may present in the $WANTED package,
		# but split packages have their own receipts
		echo $PACKAGE
	elif [ "${split_space/ $PACKAGE /}" != "$split_space" ]; then
		# $PACKAGE included somewhere in $SPLIT (probably in the end).
		# We should build packages in the order defined in the $SPLIT.
		echo $split
	else
		# We'll build the $PACKAGE, then all defined in the $SPLIT.
		echo $PACKAGE $split
	fi
}


######################################################
# BEGIN: Functions may be used in the genpkg_rules() #
######################################################


# Copy specified files from $install to $fs

cook_copy_files() {
	action 'Copying files...'
	cd $install
	local i j
	IFS=$'\n'
	for i in $@; do
		for j in $(find . -name $i ! -type d); do
			mkdir -p $fs$(dirname ${j#.})
			scopy $j $fs$(dirname ${j#.})
		done
	done
	cd - >/dev/null
	status
}


# Copy specified folders from $install to $fs

cook_copy_folders() {
	action 'Copying folders...'
	cd $install
	local i j
	IFS=$'\n'
	for i in $@; do
		for j in $(find . -name $i -type d); do
			mkdir -p $fs$(dirname ${j#.})
			cp -a $j $fs$(dirname ${j#.})
		done
	done
	cd - >/dev/null
	status
}


# Remove from current $fs files that are already packed (for receipts v2).
# Note: the order in $SPLIT is very important.
# Note 2: working in the current set.

remove_already_packed() {
	local i j
	# $basepkg is the name of the main package;
	# $thispkg is the name of the current one.
	# $basepkg may (or may not) be included in the $SPLIT
	neighbors=$(
		echo $basepkg $SPLIT" " \
		| awk -F$'\t' -vpkg="$thispkg" '
		BEGIN { RS = " "; FS = ":"; }
		      { set[$1] = $2; }
		END   {
		        current_set = set[pkg];
		        for (i in set)
		          { if (i != pkg && set[i] == current_set) print i; }
		}
	')
	IFS=$'\n'
	for neighbor in $neighbors; do
		i="$taz/$neighbor-$VERSION$EXTRAVERSION/files.list"
		[ -e "$i" ] || continue
		while read j; do
			[ -f $fs$j -o -h $fs$j ] || continue
			rm $fs$j
			rmdir --parents --ignore-fail-on-non-empty $fs$(dirname $j)
		done < $i
	done
	unset IFS
}


# Copy hicolor icons in specified sizes (default: 16 and 48) from $install to $fs

cook_copy_icons() {
	local sizes=$@ i j ifs="$IFS"
	unset IFS
	action 'Copying hicolor icons...'
	[ -d "$fs/usr/share/icons/hicolor" ] && rm -rf "$fs/usr/share/icons/hicolor"
	mkdir -p $fs/usr/share/icons/hicolor
	for i in ${sizes:-16 48}; do
		j="${i}x$i"; [ "$i" == 'scalable' ] && j="$i"
		[ ! -e "$install/usr/share/icons/hicolor/$j" ] ||
		scopy   $install/usr/share/icons/hicolor/$j \
			         $fs/usr/share/icons/hicolor
	done
	status
	IFS="$ifs"
}


# Common function to copy files, folders and patterns

copy() {
	action 'Copying folders and files...'
	if [ ! -d "$install" ]; then
		false; status
		die 'ERROR: copy from the empty $install'
	fi

	local i j k
	# $tmp MUST be assigned on its own line: busybox ash evaluates every RHS in
	# a multi-name `local a=.. b=$a` against the OLD scope, so b=$tmp/f would be
	# "/f" (empty $tmp) and the find lists below would be written to the chroot
	# root (/f /d /s) instead of the temp dir. (perl/@std hit this.)
	local tmp=$(mktemp -d)
	local filelist=$tmp/f folderlist=$tmp/d solist=$tmp/s copylist=$tmp/c

	IFS=$'\n'
	cd $install
	# filelist: all files and symlinks excluding *.so symlinks
	find \( -type f -o \( -type l ! -name '*.so' \) \) | sed 's|\.||' >$filelist
	# solist: *.so symlinks only
	find -type l -name '*.so' | sed 's|\.||' >$solist
	# folderlist: folders
	find -type d | sed 's|\.||' >$folderlist

	for i in $@; do
		case $i in
			@std)
				# Copy "standard" files (all but "developer files", man pages, documentation, translations)
				sed '/\.h$/d; /\.hxx$/d; /\.a$/d; /\.la$/d; /\.pc$/d; /\.pri$/d; /bin\/.*-config$/d;
					/\.m4$/d; /\.gir$/d; /\.typelib$/d; /\.vapi$/d; /\.deps$/d; /\.cmake$/d;
					/\/Makefile.*/d; /\.inc$/d; /\/include\//d;
					/\/share\/man\//d; /\/share\/doc\//d; /\/share\/gtk-doc\//d; /\/share\/info\//d;
					/\/share\/devhelp\//d; /\/share\/locale\//d;
					/\/share\/bash-completion\//d; /\/etc\/bash_completion\.d\//d; /\/lib\/systemd\//d;
					/\/fonts\.scale$/d; /\/fonts\.dir$/d;
					/\/share\/appdata\//d; /\/share\/help\//d; /\/share\/metainfo\//d; /\/share\/mimelnk\//d;
					/\/share\/application-registry\//d; /\/share\/mime-info\//d;
					/\/share\/gnome\/help\//d; /\/share\/omf\//d;
					/\/share\/icons\/hicolor\/[12356][1245][268]*x[12356][1245][268]*\//d;	# 22, 24, 32, 64, 128, 256, 512
					/\.so\.dbg$/d;
					' $filelist
				;;
			@dev)
				# Copy "developer files"
				sed -n '
					/\/share\/doc\//d;
					/\.h$/p; /\.hxx$/p; /\.a$/p; /\.pc$/p; /\.pri$/p; /bin\/.*-config$/p;
					/\.m4$/p; /\.gir$/p; /\.typelib$/p; /\.vapi$/p; /\.deps$/p; /\.cmake$/p;
					/\/Makefile.*/p; /\.inc$/p; /\/include\//p;
					/\.so\.dbg$/p;
					' $filelist
				cat $solist # append copy list by *.so symlinks
				;;
			@ruby)
				# Copy mandatory Ruby files
				gem_base="\/usr\/lib\/ruby\/gems\/.*\/${PACKAGE#*-}-$VERSION"
				sed -n '/\/extensions\/.*\.so$/p; /'$gem_base'\/lib\//p; /\.gemspec$/p;
					/\/usr\/bin\//p; /\/gems\/.*\/bin\//p;
					' $filelist | sed '/\/gems\/.*\/lib\/.*\.so$/d; /\/gems\/.*\/lib\/.*\.h$/d;
					/\/gems\/.*\/gems\/.*\.gemspec$/d;'
				;;
			@ruby-dev)
				sed -n '/\/ext\/.*\.h$/p; /\/ext\/.*\.pc$/p; /\/gem.build_complete$/p;
					/\/gems\/.*\/lib\/.*\.h$/p;
					' $filelist
				;;
			@rm)
				# Quick alias
				remove_already_packed
				;;
			@ico)
				# Quick alias
				cook_copy_icons >/dev/null
				;;
			*/)
				# Copy specified folders.
				i="${i%/}"
				find -type d -path "*/${i#/}" | sed 's|^.||'
				;;
			*)
				# Copy specified files.
				find ! -type d -path "*/${i#/}" | sed 's|^.||'
				;;
		esac \
		| sort -u \
		| while read j; do
			mkdir -p $fs$(dirname "$j")
			if [ -d "$install$j" ]; then
				cp -a "$install$j" $fs$(dirname "$j")
			else
				scopy "$install$j" $fs$(dirname "$j")
			fi
		done
		# Copy empty directories
		case $i in
			@std)
				while read j; do
					case $j in
						# skip empty man, doc and locale folders
						*/man/*|*/doc/*|*/share/locale/*) continue;;
					esac
					[ -z "$(ls -A "$install$j")" ] || continue
					# directory $j is empty
					k="$j"
					# make 'ladder' from directories, from root dir to $j
					# /a   /a/b   /a/b/c   etc.
					while :; do
						[ -z "$k" ] && break
						echo "$k"
						k="${k%/*}"
					done \
					| tac \
					| while read k; do
						# make dir if it does not exist
						if [ ! -d "$fs$k" ]; then
							# It's like "copy the directory without its underlying content".
							# keep original ownership/permissions, access:
							keepIFS="$IFS"; unset IFS
							install -d $(stat -c'-o%u -g%g -m%a' "$install$k") "$fs$k"
							# keep last-modified date:
							touch -r "$install$k" "$fs$k"
							IFS="$keepIFS"; unset keepIFS
						fi
					done
				done < $folderlist
				;;
		esac
	done
	cd - >/dev/null
	unset IFS
	rm -r $tmp # clean
	status
}

####################################################
# END: Functions may be used in the genpkg_rules() #
####################################################


# Strip the original receipt to bare minimum: variables and
# {pre,post}_{install,remove} functions; that's what ships in the *.tazpkg.

mk_pkg_receipt() {
	orig_receipt="$1"

	# 1. Main package.
	#    By default it has no dependencies.
	#    You can write or omit DEPENDS="" for indicating packages that have no
	#    dependencies.
	# 2. Split package (excluding *-dev).
	#    By default every split package depends on the main package.
	#    Unfortunately, in the shell script (receipt is the shell script too),
	#    every undeclared variable has an empty value, so there's no difference if
	#    you wrote DEPENDS="" or omit it - result will be the same empty value.
	#    If you want to define that the split package has no dependencies - you need
	#    to to put a single space between the quotes: DEPENDS=" ".
	# 3. Development split package.
	#    Installing *-dev package should install all the files produced during
	#    compilation and then were separated into the different packages, so
	#    by default (if you wrote DEPENDS="" or omit it) *-dev package depends
	#    on the main package and all the split packages (excluding itself).
	[ "$DEPENDS" == ' ' ] && DEPENDS='@EMPTY@'

	# Receipt's signature is important, although some receipts may miss it
	signature=$(head -n1 "$orig_receipt")
	[ "${signature:0:1}" == '#' ] || signature='# SliTaz package receipt.'

	save="$(mktemp)"
	# `$(echo ...)`: normalize whitespace (space, tab, newline and their
	# combinations and repeats)
	cat > $save <<-EOT
		PACKAGE="$PACKAGE"; DEPENDS="$(echo $DEPENDS)"; PROVIDE="$(echo $PROVIDE)"
		SUGGESTED="$(echo $SUGGESTED)"; TAZPANEL_DAEMON="$TAZPANEL_DAEMON"
		TAGS="$(echo $TAGS)"; VERSION="$VERSION"; SHORT_DESC="$SHORT_DESC"
		WEB_SITE="$WEB_SITE"; CATEGORY="$CATEGORY"
	EOT
	unset_receipt
	. "$orig_receipt"
	MAIN_PACKAGE="$PACKAGE"
	. $save; rm $save # restore values

	# Manage split packages
	SPLIT=" $SPLIT "
	if [ "$PACKAGE" != "$MAIN_PACKAGE"   -a   "$SPLIT" != '  ' ] &&
		echo "$SPLIT" | fgrep -q " $PACKAGE "; then
		# For packages with empty $DEPENDS
		if [ -z "$DEPENDS" ]; then
			case $PACKAGE in
				*-dev)
					# main package and all the split packages but this *-dev itself
					DEPENDS=$(echo "$MAIN_PACKAGE $SPLIT " | sed "s| $PACKAGE | |; s| *$||");;
				*)
					DEPENDS="$MAIN_PACKAGE";;
			esac
		fi

		# Default $CAT
		[ -z "$CAT" ] &&
		case $PACKAGE in
			*-dev) CAT="development|development files" ;;
		esac
	fi

	# Manage two-in-one $CAT="$CATEGORY|$SHORT_DESC_ADDITION"
	if [ -n "$CAT" ]; then
		CATEGORY="${CAT%|*}"
		SHORT_DESC="$SHORT_DESC (${CAT#*|})"
	fi

	# escape quotes for receipt
	SHORT_DESC="${SHORT_DESC//\"/\\\"}"

	# Mandatory variables
	cat <<-EOF
		$signature

		PACKAGE="$PACKAGE"
		VERSION="$VERSION"
	EOF
	[ -n "$EXTRAVERSION"    ] && echo "EXTRAVERSION=\"$EXTRAVERSION\""
	cat <<-EOF
		CATEGORY="$CATEGORY"
		SHORT_DESC="$SHORT_DESC"
		MAINTAINER="$MAINTAINER"
		LICENSE="$LICENSE"
		WEB_SITE="$WEB_SITE"
	EOF

	# Optional variables
	[ -n "$TAGS"            ] && echo "TAGS=\"$TAGS\""                 | tr -ds '\t' ' '
	case "x$DEPENDS" in
		x|x@EMPTY@) ;;
		*)                       echo "DEPENDS=\"$DEPENDS\""           | tr -ds '\t' ' ';;
	esac
	[ -n "$PROVIDE"         ] && echo "PROVIDE=\"$PROVIDE\""           | tr -ds '\t' ' '
	[ -n "$CONFIG_FILES"    ] && echo "CONFIG_FILES=\"$CONFIG_FILES\"" | tr -ds '\t' ' '
	[ -n "$SUGGESTED"       ] && echo "SUGGESTED=\"$SUGGESTED\""       | tr -ds '\t' ' '
	[ -n "$DATABASE_FILES"  ] && echo "DATABASE_FILES=\"$DATABASE_FILES\""
	[ -n "$TAZPANEL_DAEMON" ] && echo "TAZPANEL_DAEMON=\"$TAZPANEL_DAEMON\""

	# Extract {pre,post}_{install,remove} functions;
	# post_install()      will be copied for both main and all the split packages
	# post_install_gtk_() will be copied as post_install() for gtk+ package only
	#
	# restricted name (gtk+ -> gtk_; acl-dev -> acl_dev)
	rname=$(echo -n $PACKAGE | tr -c 'a-zA-Z0-9' '_')
	for i in pre post; do
		for j in install remove; do
			sed    "/^${i}_${j}()/,/^}/!d"        "$orig_receipt"
			sed    "/^${i}_${j}_$rname()/,/^}/!d" "$orig_receipt" \
			| sed "s|^${i}_${j}_$rname()|${i}_${j}()|"
		done
	done
}


# Determine package architecture
# Input: $1 = $fs; output string: i486 | x86_64 | any

determine_pkg_arch() {
	if [ "${COOKOPTS/force-arch/}" != "$COOKOPTS" ]; then
		action 'Forced package architecture to' >&2
		echo " $ARCH" >&2
		arch="$ARCH"
	elif [ -n "$(busybox find "$1" -type f -name '*.typelib')" ]; then
		# GObject-introspection *.typelib binary data is architecture-dependent
		action 'Found typelib. Forced package architecture to' >&2
		echo " $ARCH" >&2
		arch="$ARCH"
	else
		action 'Determining package architecture...' >&2
		archs="$(
			IFS=$'\n'
			{
				# examine all the executables and static libs (*.a)
				busybox find "$1" -type f \( -perm +111 -o -name '*.a' \) \
				| while read i; do
					readelf -Wh "$i" 2>/dev/null \
					| sed '/Machine:/!d; s|.* ||'
				done

				# examine compressed kernel modules (we use exclusively *.ko.xz)
				tmpko=$(mktemp)
				find "$1" -type f -name '*.ko.xz' \
				| while read i; do
					unxz -kc $i >$tmpko
					readelf -Wh "$tmpko" 2>/dev/null \
					| sed '/Machine:/!d; s|.* ||'
				done
				rm $tmpko

				# examine Guile *.go files (Machine: None, so check Class)
				find "$1" -type f -name '*.go' \
				| while read i; do
					readelf -Wh "$i" 2>/dev/null \
					| sed '/Class:/!d; s|.* ||'
				done \
				| sed 's|ELF32|80386|; s|ELF64|X86-64|'
			} | sort -u
		)"

		case $archs in
			80386)  arch='i486';   echo ' i486'   >&2;;
			X86-64) arch='x86_64'; echo ' x86_64' >&2;;
			'')     arch='any';    echo ' any'    >&2;;
			*)      arch="$ARCH";  echo ' ' $archs >&2
			        echo "Warning: weird architecture found, forced to use $ARCH for now." >&2
			        ;;
		esac
	fi

	touch $pkgdir/.arch
	sed -i "/^$PACKAGE	/d" $pkgdir/.arch	# remove previous entry
	echo "$PACKAGE	$arch" >> $pkgdir/.arch	# put new one

	echo $arch
}


# Find the variables inside receipt

find_vars() {
	# You can define variables in the root of the receipt describing
	# the dependencies (or tags, config files, etc.) for each sub-package.
	# Example:
	#   PACKAGE="cool"
	#   SPLIT="$PACKAGE-extra libcool $PACKAGE-dev"
	#
	#   DEPENDS_cool       or DEPENDS_std   # latter is the universal name for main package deps
	#   DEPENDS_cool_extra or DEPENDS_extra # you can skip "$PACKAGE" at the start
	#   DEPENDS_libcool                     # name not starts with "$PACKAGE" so no "short" name
	#   DEPENDS_cool_dev   or DEPENDS_dev

	local out
	local var=$1
	local pkg=$(echo -n $2             | tr -c 'a-zA-Z0-9' '_')
	local end=$(echo -n ${2#$basepkg-} | tr -c 'a-zA-Z0-9' '_')
	if [ "$2" == "$basepkg" ]; then
		eval out="\$${var}_$pkg"                  # DEPENDS_cool
		[ -n "$out" ] || eval out="\$${var}_std"  # DEPENDS_std
		[ -n "$out" ] || eval out="\$$var"        # DEPENDS
	else
		eval out="\$${var}_$pkg"                  # DEPENDS_cool_extra
		[ -n "$out" ] || eval out="\$${var}_$end" # DEPENDS_extra
	fi
	echo "$out"
}


# Create the package. Always invoke in a subshell: `(packit "$name")` —
# packit sources the receipt and exports many variables; the subshell
# isolates that from the cook parent process, while still inheriting
# cook's functions (get_dev_files, cook_pick_manpages, cook_pick_docs)
# so they are visible from genpkg_rules().

packit() {
	basepkg="$PACKAGE"
	thispkg="$1"

	pkgdir="$WOK/$basepkg"
	receipt="$pkgdir/receipt"
	. $receipt

	title 'Pack: %s' "$thispkg $VERSION"


	#
	# Set variables
	#

	# Determine set name for specified package from $SPLIT variable
	local set=$(echo -n $SPLIT \
				| awk -vpkg="$thispkg" '
					BEGIN { RS = " "; FS = ":"; }
					{ if ($1 == pkg && $2 != "") { print "-" $2; exit; } }')

	# Set paths
	export   stuff="$pkgdir/stuff"
	export     src="$pkgdir/source/$basepkg-$VERSION$set"
	export install="$pkgdir/install$set"
	[ -n "$WANTED" ] && export install="$WOK/$WANTED/install$set"
	[ -n "$WANTED" ] && export src="$WOK/$WANTED/source/$WANTED-$VERSION$set"
	export DESTDIR="$install"
	export     taz="$pkgdir/taz"
	export    pack="$taz/$thispkg-$VERSION$EXTRAVERSION"
	export      fs="$pack/fs"

	export PACKAGE=$thispkg


	#
	# Execute genpkg_rules()
	#

	require_copy='yes'
	[ "${COOKOPTS/empty-pkg/}" != "$COOKOPTS" ] && require_copy='no'

	if grep -q ^genpkg_rules $receipt; then
		_ 'Executing: %s' 'genpkg_rules'
		set -e
		cd $pkgdir; mkdir -p $fs
		# A cook launched from here runs on a base that still has our bdeps
		# installed; tell it to skip the SETUP_MD5 base-integrity guard.
		export COOK_NESTED=1
		genpkg_rules || (newline; _ 'ERROR: genpkg_rules failed'; newline) >> $LOGS/$pkg.log
		unset COOK_NESTED
		set +e
		require_copy='no'
	else
		cd $pkgdir; mkdir -p $fs
		if [ "$CATEGORY" == 'meta' -a "$thispkg" == "$basepkg" ]; then
			_ 'No packages rules: meta package'
			require_copy='no'
		fi
	fi

	# Auto-packing

	for i in DEPENDS SUGGESTED PROVIDE CONFIG_FILES TAGS CATEGORY CAT COPY; do
		# variable may be already set in genpkg_rules(), check it
		eval tmpvar="\$$i"
		[ -n "$tmpvar" ] && continue
		eval $i="\$(find_vars $i $thispkg)"
	done

	[ -n "$CAT" ] && CATEGORY=$(echo "$CAT" | cut -d'|' -f1)

	if [ "$CATEGORY" != 'meta' -a "$CATEGORY" != 'nopack' ]; then
		if [ -z "$COPY" -a "$require_copy" == 'yes' ]; then
			case "$thispkg" in
				$basepkg)    COPY='@std @rm';;
				*-dev)       COPY='@dev @rm';;
				lib$basepkg) COPY='*.so.*'; [ -n "$CAT" ] || CAT="libs|shared library";;
				*-lang)      COPY='*.mo';   [ -n "$CAT" ] || CAT="localization|translations";;
			esac
		fi

		if [ -n "$COPY" ]; then
			copy $COPY
		elif [ "$require_copy" == 'yes' ]; then
			var=$(echo -n COPY_$thispkg | tr -c 'a-zA-Z0-9' '_')
			die "ERROR: $var rules undefined"
		fi
	fi

	if [ "$CATEGORY" == 'nopack' ]; then
		echo "Skipping $thispkg"
		return
	fi


	#
	# Check CONFIG_FILES
	#

	if [ -n "$CONFIG_FILES" ]; then
		unset IFS
		for i in $CONFIG_FILES; do
			if [ ! -e $fs$i ]; then
				case $i in
					*/) mkdir -p $fs$i ;;
					*)  mkdir -p $fs$(dirname $i); touch $fs$i ;;
				esac
			fi
		done
	fi

	# First QA check to stop now if genpkg_rules failed.
	if fgrep -q '^ERROR' $LOGS/$basepkg.log; then
		broken; exit 1
	fi


	#
	# Copy receipt and description
	#

	cd $taz
	action 'Copying "%s"...' 'receipt'
	mk_pkg_receipt "$(realpath ../receipt)" > $pack/receipt
	chown 0:0 $pack/receipt
	status

	unset desc
	# description common to all the sub-packages
	[ -f "../description.txt" ] && desc="../description.txt"
	# description for specified sub-package
	[ -f "../description.$thispkg.txt" ] && desc="../description.$thispkg.txt"
	if [ -n "$desc" ]; then
		action 'Copying "%s"...' "$(basename "$desc")"
		cp -f $desc $pack/description.txt
		chown 0:0 $pack/description.txt
		status
	fi


	#
	# Copy generic files
	#

	# copy_generic_files self-guards via [ "$pkg" == "$PACKAGE" ]: $pkg
	# stays the cooked target (= $basepkg), $PACKAGE is now $thispkg, so
	# generic files only land on the main package.
	copy_generic_files


	#
	# Neutralize libtool archives
	#

	# Empty dependency_libs in every shipped .la. libtool .la files hardcode
	# the full static dep chain as absolute /usr/lib/*.la paths, but SliTaz
	# strips most .la (glib, jpeg, gio...). A surviving .la that references a
	# stripped one makes libtool abort for every consumer ("No such file" then
	# "not a valid libtool archive" -- hit libgnome, ecore, xfce4-panel...).
	# Shared linking + pkg-config already carry the real deps, so blanking
	# dependency_libs is safe and kills this whole halt class distro-wide.
	for la in $(find $fs -type f -name '*.la' 2>/dev/null); do
		sed -i "s|^dependency_libs=.*|dependency_libs=''|" "$la"
	done


	#
	# Strip / compress files
	#

	arch="$(determine_pkg_arch $fs)"

	# Filename arch suffix. i486 keeps the legacy no-suffix name (until
	# SliTaz 6.0): cookutils-4.2.tazpkg, not -i486. This applies to NOARCH
	# ("any") packages too on i486 -- the legacy i486 tazpkg/get reconstruct
	# filenames with no suffix at all, so a "-any" file 404s on the installed
	# base (works on x86_64 only thanks to cook-pkgdb's new field-11 hint,
	# which old i486 clients don't read). So: nothing on i486 (native or
	# noarch), "-any" / "-$arch" only on x86_64/arm.
	case "$arch" in
		i486) farch='' ;;
		any)  [ "$ARCH" = 'i486' ] && farch='' || farch='-any' ;;
		*)    farch="-$arch" ;;
	esac

	export COOKOPTS ARCH HOST_SYSTEM LOCALE fs; @@PREFIX@@/libexec/cookutils/compressor fs


	#
	# Make lists
	#

	# Create files.list
	action 'Creating the list of files...'
	cd $fs
	find . \( -type f -o -type l \) | sed 's|^.||' > ../files.list
	cd ..
	status

	# Md5sum of files.
	action 'Creating md5sum of files...'
	while read file; do
		[ -L "fs$file" ] && continue
		[ -f "fs$file" ] || continue
		case "$file" in
			/lib/modules/*/modules.*|*.pyc) continue ;;
		esac
		md5sum "fs$file" | sed 's|  fs|  |'
	done < files.list | sort -k2 > md5sum
	status


	#
	# Calculate release checksum
	#

	# Usually it does not change on "just recook".
	# Md5sum of the *.tazpkg will change every time because of embedded timestamps;
	# on the other hand release checksums don't rely on the timestamps, but
	# only on files content and their permissions.

	# Calculate rsum for new package
	RSUM=$(
	{
		# a) md5sums of all files
		cat $pack/md5sum
		# b) md5sum of receipt
		md5sum $pack/receipt | sed 's| [^ ]*/| |'
		# c) md5sum of description.txt
		[ -e "$pack/description.txt" ] &&
			md5sum $pack/description.txt | sed 's| [^ ]*/| |'
		# d) md5sum of list of permissions and ownership of all the files and
		#    folders of the package
		#    stat line example: -rwsr-xr-x 0:0 ./bin/busybox
		{
			cd $fs
			find . -print0 | sort -z | xargs -0rn 1 stat -c '%A %g:%u %N' | md5sum
		}
	} | md5sum $rsum_file | awk '{print $1}')


	#
	# Compressing
	#

	UNPACKED_SIZE=$(du -cks fs receipt files.list md5sum description.txt \
		2>/dev/null | awk 'END{ print $1 "K"}')

	# Build fs cpio archive
	action 'Compressing the FS...'
	newest="$(find receipt stuff 2>/dev/null | xargs ls -td | sed q)"
	find fs -newer $newest -exec touch -hr $newest '{}' \;
	# tazpkg extracts with busybox unlzma, which only reads the legacy
	# .lzma "alone" format. There is no standalone `lzma` binary in the
	# build chroot (busybox ships only unlzma), so the 4.0 `lzma -qzeT0`
	# wrote an empty/unreadable fs.cpio.lzma -> "bad lzma header" on
	# install. Compress with `xz --format=lzma`, always present and
	# producing an alone stream busybox can extract. -6 keeps the 8 MiB
	# dictionary SliTaz has shipped for 20 years (lzma e -d23); -9 would
	# encode a 64 MiB dict, forcing a needless 64 MiB malloc in busybox
	# unlzma on low-RAM installs for no decompression-speed gain.
	find fs | cpio -o -H newc --quiet | xz --format=lzma -6 -c > fs.cpio.lzma
	comp=$?
	mv fs ../
	status
	# The status above reports the mv; verify the payload itself so a
	# failed compressor can never ship a broken package.
	if [ "$comp" -ne 0 ] || [ ! -s fs.cpio.lzma ]; then
		_ 'ERROR: FS compression failed (fs.cpio.lzma)'; broken; exit 1
	fi

	PACKED_SIZE=$(du -cks fs.cpio.lzma receipt files.list md5sum description.txt \
		2>/dev/null | awk 'END{ print $1 "K"}')


	#
	# Add variables to the receipt
	#

	# Store sizes
	sed -i '/^PACKED_SIZE=/d; /^UNPACKED_SIZE=/d' receipt
	sed -i "s|^PACKAGE=|PACKED_SIZE=\"$PACKED_SIZE\"\nUNPACKED_SIZE=\"$UNPACKED_SIZE\"\nPACKAGE=|" receipt

	# Store RSUM
	sed -i "s|^PACKAGE=|RSUM=\"$RSUM\"\nPACKAGE=|" receipt

	# Set extra version
	if [ -n "$EXTRAVERSION" ]; then
		sed -i '/^EXTRAVERSION=/d' receipt
		sed -i "s|^VERSION=|EXTRAVERSION=\"$EXTRAVERSION\"\nVERSION=|" receipt
	fi


	#
	# Build *.tazpkg
	#

	action 'Creating full cpio archive...'
	find . -newer $receipt -exec touch -hr $receipt '{}' \;
	find . | cpio -o -H newc --quiet > ../$PACKAGE-$VERSION$EXTRAVERSION$farch.tazpkg
	status

	# Restoring original package tree.
	mv ../fs .

	rm fs.cpio.lzma; cd ..

	tazpkg=$(ls *.tazpkg)


	#
	# Verify package quality and consistency
	#

	# Preferrable way is to combine the commands chain in the compile_rules()
	# using '&&': when any of the chunk broke, process will stop and function
	# will return non-zero return code.
	# On the other hand some old receipts don't use '&&' but depend on the
	# error messages on the log.
	# Sometimes it produces false positives in the configuration stage.
	# In this case we can use the "skip-log-errors".
	if [ "${COOKOPTS/skip-log-errors/}" == "$COOKOPTS" ]; then
		# Exit if any error found in log file.
		if fgrep -q ^ERROR $LOGS/$basepkg.log; then
			rm -f $command
			broken; exit 1
		fi
	fi


	# Allow meta-packages in v2 receipts
	[ -n "$CAT" ] && CATEGORY="${CAT%|*}"

	if [ "${COOKOPTS/empty-pkg/}" == "$COOKOPTS" ]; then
		action 'QA: checking for empty package...'
		if [ ! -s "$pack/files.list" -a "$CATEGORY" != 'meta' ]; then
			broken
			rm -f $command
			false; status
			die 'ERROR: empty package'
		fi
		:; status
	fi


	#
	# Get RSUM from the old package
	#

	pkg_file="$PKGS/$PACKAGE-$VERSION$EXTRAVERSION$farch.tazpkg"
	if [ -f "$pkg_file" ]; then
		# don't trust database entry, check the package file
		tmpdir=$(mktemp -d)
		cd $tmpdir
		cpio -F "$pkg_file" -i receipt >/dev/null 2>&1
		RSUM_OLD=$(unset RSUM; . receipt; echo $RSUM)
		cd - >/dev/null
		rm -r $tmpdir
	else
		unset RSUM_OLD
	fi


	#
	# Removing unhandled old packages
	#

	if [ "$ARCH" = 'i486' -a -e "$PKGS/$PACKAGE-$VERSION$EXTRAVERSION-i486.tazpkg" ]; then
		action 'Removing i486 package with arch specifier (i486 is suffix-less)'
		rm -f $PKGS/$PACKAGE-$VERSION$EXTRAVERSION-i486.tazpkg
		status
	fi

	# For example, if *-dev package contains *.a static libs, it will be either
	# i486 or x86_64 arch; otherwise it will be "any" arch. This transition
	# may be done back and forth depending on if you disable static libs or not.
	case $arch in
		any)
			if [ -e $PKGS/$PACKAGE-$VERSION$EXTRAVERSION-$ARCH.tazpkg ]; then
				action "Removing old $ARCH package because it arch-less now"
				rm -f $PKGS/$PACKAGE-$VERSION$EXTRAVERSION-$ARCH.tazpkg
				status
			fi
			# On i486 a noarch package is suffix-less too (see above), so
			# drop any stale "-any" file a previous build left behind.
			if [ "$ARCH" = 'i486' -a -e $PKGS/$PACKAGE-$VERSION$EXTRAVERSION-any.tazpkg ]; then
				action 'Removing -any package (i486 noarch is suffix-less too)'
				rm -f $PKGS/$PACKAGE-$VERSION$EXTRAVERSION-any.tazpkg
				status
			fi
			;;
		*)
			if [ -e $PKGS/$PACKAGE-$VERSION$EXTRAVERSION-any.tazpkg ]; then
				action "Removing old arch-less package because it $ARCH now"
				rm -f $PKGS/$PACKAGE-$VERSION$EXTRAVERSION-any.tazpkg
				status
			fi
			;;
	esac

	# Find and remove old package only if "release checksum" has changed

	pi="$PKGS/packages.info"
	touch $pi

	if [ "$RSUM" != "$RSUM_OLD" ]; then
		old_file=$(awk -F$'\t' -vname="$PACKAGE" -vfarch="$farch" '{
			if ($1 == name) printf("%s-%s%s.tazpkg", $1, $2, farch);
			}' $pi)		# <name>-<version><extra_version><farch>.tazpkg
		if [ -f "$PKGS/$old_file" ]; then
			action 'Removing old package "%s"' "$old_file"
			rm -f "$PKGS/$old_file"
			status
		fi
		# package changed, substitute old package for new one
		mv -f $pkgdir/taz/$PACKAGE-$VERSION$EXTRAVERSION$farch.tazpkg $PKGS
		_ 'The release checksum has changed.'
	else
		# package not changed, remove new package
		rm -f $pkgdir/taz/$PACKAGE-$VERSION$EXTRAVERSION$farch.tazpkg
		_ 'The release checksum has not changed.'
	fi


	#
	# Package isn't broken anymore
	#

	touch $broken
	sed -i "/^${thispkg}$/d" $broken


	#
	# Update packages database every time after successful build
	#

	# packages-arch.info (unsorted, located near to packages)
	unset_receipt; . $pack/receipt
	SIZES=$(echo $PACKED_SIZE $UNPACKED_SIZE | sed 's|\.0||g')
	DEPENDS=$(echo $DEPENDS) # remove newlines, tabs and multiple spaces from variable
	# Field 11 (arch column). i486 is suffix-less -> emit a legacy 10-field
	# record (no field 11) so tazpkg/get rebuilds the suffix-less filename.
	# x86_64/arm/any append it; tazpkg treats "0" as -any, anything else as
	# the running $SLITAZ_ARCH.
	case $arch in
		i?86)   arch_code='';;   # i486: legacy 10-field record (no suffix)
		x86_64) arch_code='\t6';; # 6 for 64-bit
		any)    arch_code='\t0';; # 0 for any arch
		*)      arch_code='\t1';; # other arch-specific (arm*)
	esac

	sed -i "/^$PACKAGE\t/d" $pi		# remove old entry
	printf '%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s%b\n' \
		"$PACKAGE" "$VERSION$EXTRAVERSION" "$CATEGORY" "$SHORT_DESC" \
		"$WEB_SITE" "$TAGS" "$SIZES" "$DEPENDS" "$rsum" "$PROVIDE" \
		"$arch_code" >> $pi

	# files.list (uncompressed, unsorted, located in $cache)
	fl="$cache/files.list"
	touch $fl
	sed -i "/^$PACKAGE: /d" $fl
	sed  "s/^/$PACKAGE: \0/" $pack/files.list >> $fl

	footer "$(_ 'Package "%s" created' "$tazpkg")"
}


# v2: pack all packages using compiled files

packall() {
	set_paths
	[ -e $pkgdir/.arch ] && rm $pkgdir/.arch

	if head -n1 "$pkgdir/receipt" | fgrep -q 'v2'; then
		for i in $(all_names); do
			unset TAGS DEPENDS CAT CONFIG_FILES PROVIDE SUGGESTED DATABASE_FILES TAZPANEL_DAEMON
			export PACKAGE; (packit "$i")
		done
	else
		export PACKAGE; (packit "$PACKAGE")
	fi
}


# Reverse "cat" command: prints input lines in the reverse order

tac() {
	sed '1!G;h;$!d' $1
}


# Install package: update the chroot with freshly rebuilt package: keep env up-to-date.

install_package() {
	set_paths
	case "$ARCH" in
		arm*) root="$CROSS_TREE/sysroot";;
	esac

	# Process all the package names built by this receipt (defined in $SPLIT)
	for i in $(PACKAGE="$pkg" all_names); do
		if [ -d "$root$INSTALLED/$i" ]; then
			pkg_file=$(awk -F$'\t' -vpkg="$i" -varch="$ARCH" '{
				if ($1 == pkg) {
					if (NF >= 11) {
						pkgarch = ($11 == "0") ? "any" : arch;
						printf("%s-%s-%s.tazpkg", $1, $2, pkgarch);
					} else {
						# legacy 10-field record = i486, no arch suffix
						printf("%s-%s.tazpkg", $1, $2);
					}
					exit
				}
			}' $PKGS/packages.info)
			if [ -e "$PKGS/$pkg_file" ]; then
				_ 'Updating %s chroot environment...' "$ARCH"
				_ 'Updating chroot: %s' "${pkg_file%.tazpkg}" | log
				tazpkg -i "$PKGS/$pkg_file" --forced --root=$root
			fi
		fi
	done
}


# remove chroot jail

umount_aufs() {
	tac ${1}rw/aufs-umount.sh | sh
	umount -l ${1}root 2> /dev/null
	umount -l ${1}root 2> /dev/null
	rm -rf ${1}rw ${1}wd
	rmdir ${1}r*
}


# install chroot jail

mount_aufs() {
	grep -q '^nodev.overlay$' /proc/filesystems || modprobe overlay 2>/dev/null
	grep -q '^nodev.overlay$' /proc/filesystems &&
	mount -t overlay overlay -o lowerdir=/,upperdir=${1}rw,workdir=${1}wd ${1}root 2>/dev/null && return
	grep -q '^nodev.aufs$' /proc/filesystems || modprobe aufs 2>/dev/null || return
	mount -t aufs -o br=${1}rw:/ none ${1}root 2>/dev/null
}


# Launch the cook command into a chroot jail protected by aufs.
# The current filesystems are used read-only and updates are
# stored in a separate branch.

try_aufs_chroot() {

	base="/dev/shm/aufsmnt$$"

	# Can we setup the chroot? Is it already done?
	grep -q ^AUFS_NOT_SUPPORTED $receipt && return
	grep -q ^AUFS_NOT_RAMFS $receipt && base="/mnt/aufsmnt$$"
	[ -n "$AUFS_MOUNTS" -a ! -f /aufs-umount.sh ] || return
	mkdir ${base}root ${base}rw ${base}wd || return

	# Sanity check
	for i in / /proc /sys /dev /dev/shm /home; do
		case " $AUFS_MOUNTS " in
			*\ $i\ *) ;;
			*)	AUFS_MOUNTS="$AUFS_MOUNTS $i" ;;
		esac
	done
	for mnt in $(ls -d $AUFS_MOUNTS | sort | uniq); do
		mkdir -p           ${base}root$mnt	# for `mount -o bind`
		mount -o bind $mnt ${base}root$mnt 2>/dev/null	# use `-o bind` instead of `--bind`
		if [ $mnt == / ]; then
			if mount_aufs ${base}; then
				_ 'Setup chroot jail...'
			else
				# No overlay/aufs available (e.g. building inside a plain
				# chroot): silently build without a jail.
				umount ${base}root 2>/dev/null
				rm -rf ${base}r* ${base}wd
				return
			fi
		fi
		echo "umount ${base}root$mnt" >> ${base}rw/aufs-umount.sh
	done
	trap "umount_aufs ${base}" INT

	chroot ${base}root $(cd $(dirname $0); pwd)/$(basename $0) "$@"
	status=$?

	_ 'Leaving chroot jail...'
	umount_aufs $base
	# Install package outside the aufs jail
	install_package
	exit $status
}


# Encode predefined XML entities

xml_ent() {
	sed -e 's|&|\&amp;|g; s|<|\&lt;|g; s|>|\&gt;|g; s|"|\&quot;|g' -e "s|'|\&apos;|g"
}


# Create a XML feed for freshly built packages.

gen_rss() {
	if [ -e "$WOK/$PACKAGE/.icon.png" ]; then
		icon="$COOKER_URL$PACKAGE/browse/.icon.png"
	else
		icon="http://cook.slitaz.org/tazpkg.png"
	fi

	cat > $FEEDS/$pkg.xml <<EOT
	<item>
		<title>$PACKAGE $VERSION$EXTRAVERSION</title>
		<link>$COOKER_URL${PACKAGE//+/%2B}</link>
		<guid isPermaLink="false">$PACKAGE-$VERSION$EXTRAVERSION</guid>
		<pubDate>$(date '+%a, %d %b %Y %X GMT')</pubDate>
		<description><![CDATA[
			<table>
				<tr>
					<td vertical-align="top"><img src="$icon" alt=""/></td>
					<td>$(echo -n "$SHORT_DESC" | xml_ent)</td>
				</tr>
			</table>
		]]></description>
	</item>
EOT
}


# Truncate stdout log file to $1 Mb.

loglimit() {
	if [ -n "$DEFAULT_LOG_LIMIT" ]; then
		tee /dev/stderr | head -qc ${1:-$DEFAULT_LOG_LIMIT}m
	else
		tee /dev/stderr
	fi
}


#
# Receipt functions to ease packaging
#

get_dev_files() {
	action 'Getting standard devel files...'
	mkdir -p $fs/usr/lib
	cp -a $install/usr/include $fs/usr
	[ -d "$install/usr/lib/pkgconfig" ] && \
		cp -a $install/usr/lib/pkgconfig $fs/usr/lib
	# Static libs may land in /usr/lib64 on x86_64, /usr/lib elsewhere.
	if [ -d "$install/usr/lib64" ]; then
		mkdir -p $fs/usr/lib64
		cp -a $install/usr/lib64/*a $fs/usr/lib64 2>/dev/null
	fi
	cp -a $install/usr/lib/*a $fs/usr/lib 2>/dev/null
	status
}


# Function to use in compile_rules() to copy man page from $src to $install

cook_pick_manpages() {
	local name section
	action 'Copying man pages...'

	for i in $@; do
		name=$(echo $i | sed 's|\.[gbx]z2*$||')
		section=${name##*/}; section=${section##*.}
		mkdir -p $install/usr/share/man/man$section
		scopy $i $install/usr/share/man/man$section
	done
	status
}


# Function to use in compile_rules() to copy documentation from $src to $install

cook_pick_docs() {
	local docdir="$install/usr/share/doc/$PACKAGE-$VERSION"
	action 'Copying documentation...'
	mkdir -p $docdir
	cp -r $@ $docdir
	chmod -R a+r $docdir
	status
}


# Update split.db once for receipt

update_split_db() {
	local db="$cache/split.db"
	touch $db
	sed -i "/^$pkg\t/d" $db
	echo -e "$pkg\t$(all_names)" >> $db
}


# Put the status to the activity log

put_status() {
	# $1: package, $2: status, one of 'Done', 'Failed'
	sed -i "s|>$1</a>$|& [ $2 ]|" $activity
	if [ "$2" == 'Done' ]; then
		# overstrike all previous 'Failed' to indicate package is OK now
		sed -i "/>$1<\/a>/ s|\[ Failed \]|[ -Failed ]|" $activity
	fi
}


#
# Commands
#

# cook <package> --deps | --cdeps
# Compat shim — dep analysis moved to /usr/bin/cook-deps.
case " $* " in
	*' --deps '*|*' --cdeps '*)
		exec @@PREFIX@@/bin/cook-deps "$1"
		;;
esac

# cook <package> --clean
# cook <package> -c
[ -n "$clean" -o "$2" == '-c' ] && {
	action 'Cleaning "%s"' "$1"
	cd $WOK/$1; rm -rf install taz source
	status; newline
	touch $activity		# update $activity -> something changed -> update webstat
	exit 0
}

# cook <package> --getsrc
# cook <package> -gs
[ -n "$getsrc" -o "$2" == '-gs' ] && {
	pkg="$1"
	title 'Getting source for "%s"' "$pkg"
	receipt="$WOK/$pkg/receipt"
	check_pkg_in_wok
	unset_receipt
	. $receipt
	get_source
	_ 'Tarball: %s' "$SRC/$TARBALL"; newline
	exit 0
}

# cook <package> --block
# cook <package> -b
[ -n "$block" -o "$2" == '-b' ] && {
	action 'Blocking package "%s"' "$1"
	grep -q "^$1$" $blocked || echo "$1" >> $blocked
	status; newline
	touch $activity
	exit 0
}

# cook <package> --unblock
# cook <package> -ub
[ -n "$unblock" -o "$2" == '-ub' ] && {
	action 'Unblocking package "%s"' "$1"
	sed -i "/^$1$/d" $blocked
	status; newline
	touch $activity
	exit 0
}




case "$1" in
	usage|help|-u|-h)
		 usage ;;

	list-wok)
		exec @@PREFIX@@/bin/cook-wok list
		;;

	activity)
		cat $activity ;;

	search)
		exec @@PREFIX@@/bin/cook-wok search "$2"
		;;

	setup)
		shift
		exec @@PREFIX@@/bin/cook-setup "$@"
		;;

	*-setup)
		# arch-setup: forward the arch as first arg to cook-setup.
		shift_arch="${1%-setup}"
		shift
		exec @@PREFIX@@/bin/cook-setup "$shift_arch" "$@"
		;;

	test)
		# Test a cook environment.
		_ 'Cook test: testing the cook environment' | log
		[ ! -d "$WOK" ] && exit 1
		[ ! -d "$WOK/cooktest" ] && cp -r $DATA/cooktest $WOK
		cook cooktest ;;

	doctor)
		shift
		exec @@PREFIX@@/bin/cook-doctor "$@"
		;;

	new)
		shift
		exec @@PREFIX@@/bin/cook-new "$@"
		;;

	list)
		shift
		exec @@PREFIX@@/bin/cook-all "$@"
		;;

	clean-wok)
		exec @@PREFIX@@/bin/cook-clean wok
		;;

	clean-src)
		exec @@PREFIX@@/bin/cook-clean src
		;;

	howto|example)
		cat <<EOT

$(boldify "$(_ 'Cook usage examples:')")

$(boldify "$(_ 'Single package:')")
  cook pkgname                $(_ 'Build a package')
  cook pkgname --clean        $(_ 'Clean build files')
  cook pkgname --getsrc       $(_ 'Download source only')
  cook pkgname --pack         $(_ 'Repack without recompiling')

$(boldify "$(_ 'Wok management:')")
  cook new pkgname            $(_ 'Create a new package skeleton')
  cook new pkgname -x         $(_ 'Create interactively')
  cook list /path/to/list     $(_ 'Cook packages from a file list')
  cook search pattern         $(_ 'Search packages in the wok')
  cook uncook                 $(_ 'Show packages not yet built')

$(boldify "$(_ 'Full rebuild:')")
  cook all                    $(_ 'Rebuild all (reads /etc/slitaz/cookorder)')
  cook all /my/cookorder      $(_ 'Rebuild from a custom list')
  cook all --resume           $(_ 'Skip already built packages')

$(boldify "$(_ 'Environment:')")
  cook setup                  $(_ 'Initialize build environment')
  cook setup --wok            $(_ 'Clone the wok from Hg')
  cook test                   $(_ 'Test the cook environment')
  cook clean-wok              $(_ 'Remove all build artifacts')

EOT
		exit 0 ;;

	all)
		shift
		exec @@PREFIX@@/bin/cook-all "$@"
		;;

	uncook)
		exec @@PREFIX@@/bin/cook-wok uncook
		;;

	wanted)
		exec @@PREFIX@@/bin/cook-wok wanted "$2"
		;;

	build_depends)
		exec @@PREFIX@@/bin/cook-wok build_depends "$2"
		;;

	build_loop)
		exec @@PREFIX@@/bin/cook-wok build_loop "$2"
		;;


	pkgdb)
		# DB generation extracted to /usr/bin/cook-pkgdb (BSD-style
		# split). Transparent shim — drop in cook-pkgdb directly to
		# bypass.
		exec @@PREFIX@@/bin/cook-pkgdb "$2"
		;;

	splitdb)
		exec @@PREFIX@@/bin/cook-pkgdb --splitdb
		;;

	maintdb)
		exec @@PREFIX@@/bin/cook-pkgdb --maintdb
		;;

	*)
		# Just cook and generate a package.
		check_root
		time=$(date +%s)
		pkg="$1"
		[ -z "$pkg" ] && usage

		# Search last successful cook time in all logs from newer to older
		for i in '' $(seq 0 9 | sed 's|^|.|'); do
			[ -f "$LOGS/$pkg.log$i" ] || break
			lastcooktime=$(sed '/^Cook time/!d; s|.*: *\([0-9]*\)s.*|\1|' \
				$LOGS/$pkg.log$i 2>/dev/null | sed '$!d')
			[ -n "$lastcooktime" ] && break
		done

		receipt="$WOK/$pkg/receipt"
		check_pkg_in_wok
		newline

		unset inst
		unset_receipt
		. $receipt

		# Handle cross compilation.
		case "$ARCH" in
			arm*)
				if [ -z "$HOST_ARCH" ]; then
					_ 'cook: HOST_ARCH is not set in "%s" receipt' "$pkg"
					error="$(_ 'package "%s" is not included in %s' "$pkg" "$ARCH")"
					_ 'cook: %s' "$error"
					[ -n "$CROSS_BUGS" ] && _ 'bugs: %s' "$CROSS_BUGS"
					_ 'Cook skip: %s' "$error" | log
					newline
					broken; exit 1
				fi ;;
		esac

		# Some packages are not included in some arch or fail to cross compile.
		: ${HOST_ARCH=$ARCH}
		debug "$(_ 'Host arch %s' "$HOST_ARCH")"
		# Handle arm{v6hf,v7,..}
		if ! echo "$HOST_ARCH" | egrep -q "${ARCH%v[0-9]*}|any"; then
			_ 'cook: %s' "HOST_ARCH=$HOST_ARCH"
			error="$(_ "package \"%s\" doesn't cook or is not included in %s" "$pkg" "$ARCH")"
			_ 'cook: %s' "error"
			[ -n "$CROSS_BUGS" ] && _ 'bugs: %s' "$CROSS_BUGS"
			_ 'Cook skip: %s' "$error" | log
			sed -i "/^${pkg}$/d" $broken
			newline
			exit 0
		fi

		# Remove package from broken list before cooking
		sed -i "/^${pkg}$/d" $broken

		# Skip blocked, 3 lines also for the Cooker.
		grep -q "^$pkg$" $blocked && [ "$2" != '--unblock' ] &&
			die 'Package "%s" is blocked' "$pkg"

		try_aufs_chroot "$@"

		# Log and source receipt.
		echo "cook:$pkg" > $command

		[ -n "$lastcooktime" ] && echo "cook:$pkg $lastcooktime $(date +%s)" >> $cooktime

		while read cmd duration start; do
			[ $(($start + $duration)) -lt $(date +%s) ] &&
			echo "sed -i '/^$cmd $duration/d' $cooktime"
		done < $cooktime | sh

		# Display and log info if cook process stopped.
		# FIXME: gettext not working (in single quotes) here!
		trap '_ "\n\nCook stopped: control-C\n\n" | \
			tee -a $LOGS/$pkg.log' INT

		update_split_db

		# Handle --options
		case "$2" in
			--pack)
				_ 'Packing %s' "<a href='cooker.cgi?pkg=${pkg//+/%2B}'>$pkg</a>" | log
				[ -d "$WOK/$pkg/install" ] || die 'Need to build "%s"' "$pkg"
				[ ! -d "$WOK/$pkg/taz" ] || rm -rf "$WOK/$pkg/taz"
				[ ! -f "$LOGS/$pkg-pack.log" ] || rm -f $LOGS/$pkg-pack.log
				packall 2>&1 | tee -a $LOGS/$pkg-pack.log
				@@PREFIX@@/libexec/cookutils/postcheck $pkg | tee -a $LOGS/$pkg-pack.log
				clean_log "$pkg-pack"
				time=$(($(date +%s) - $time))
				summary | sed 's|^Cook |Pack |' | tee -a $LOGS/$pkg-pack.log
				gen_rss
				put_status $pkg Done
				rm -f $command
				exit 0 ;;

			--trials|-t)
				trials='yes' ;;
		esac

		_ 'Making %s' "<a href='cooker.cgi?pkg=${pkg//+/%2B}'>$pkg</a>" | log

		# Rotate log
		for i in $(seq 9 -1 1); do
			j=$(($i - 1))
			[ -e $LOGS/$pkg.log.$j ] && mv -f $LOGS/$pkg.log.$j $LOGS/$pkg.log.$i
		done
		[ -e $LOGS/$pkg.log ] && mv $LOGS/$pkg.log $LOGS/$pkg.log.0

		# Check if wanted is built now so we have separate log files.
		for wanted in $WANTED ; do
			if grep -q "^$wanted$" $blocked; then
				broken
				rm -f $command
				die 'WANTED package "%s" is blocked' "$wanted"
			fi
			if grep -q "^$wanted$" $broken; then
				broken
				rm -f $command
				die 'WANTED package "%s" is broken' "$wanted"
			fi
			if [ ! -d "$WOK/$wanted/install" ]; then
				cook "$wanted" || { broken; exit 1; }
			fi
		done

		# Cook and pack or exit on error and log everything.
		( ( ( (cookit $@ 2>&1; echo $? >&3) | loglimit 50 > $LOGS/$pkg.log) 3>&1) | (read rq; exit $rq) )
		rq=$?	# the return code of `cookit $@` above command

		if [ $rq -eq 1 ]; then
			broken
		fi

		# Remove build dependencies both when `cookit` done or fail
		remove_deps | tee -a $LOGS/$pkg.log
		timestamp job27 # removing bdeps
#		cookit_quality
		timestamp job28 # checking quality

		# Log and stop if `cookit` fails
		if [ $rq -eq 1 ]; then
			debug_info "ret1" | tee -a $LOGS/$pkg.log
			@@PREFIX@@/libexec/cookutils/postcheck $pkg | tee -a $LOGS/$pkg.log
			put_status $pkg Failed
			rm -f $command
			broken
			exit 1
		fi

		# Proceed only if `cookit` return code is zero-OK
		# If instant-pack if specified, then packages already packed in the cookit()
		[ "${COOKOPTS/instant-pack/}" == "$COOKOPTS" ] &&
		packall 2>&1 | loglimit 5 >> $LOGS/$pkg.log
		timestamp job29 # packing

		@@PREFIX@@/libexec/cookutils/postcheck $pkg | tee -a $LOGS/$pkg.log

		clean_log

		# Exit if any error in packing.
		if [ "${COOKOPTS/skip-log-errors/}" == "$COOKOPTS" ] &&
			grep -Ev "(/root/.cvspass|conftest|df: /|rm: can't remove|depmod: WARNING|md5sum: can't open)" $LOGS/$pkg.log | \
			grep -Eq "(^ERROR|: No such file or directory|not remade because of errors|ake: \*\*\* .* Error)"; then
			debug_info "packerr" | tee -a $LOGS/$pkg.log
			put_status $pkg Failed
			rm -f $command
			broken; exit 1
		fi

		# Create an XML feed
		gen_rss

		# Time and summary
		time=$(($(date +%s) - $time))
		summary | tee -a $LOGS/$pkg.log
		newline

		# We may want to install/update (outside aufs jail!).
		[ -s /aufs-umount.sh ] || install_package

		put_status $pkg Done

		# Finally we DON'T WANT to build the *-dev or packages with WANTED="$pkg"
		# If you want automation, use the Cooker Build Bot.
		rm -f $command
		timestamp job30 # misc. final operations
		store_timestats

		sed -n '/^Build dependencies to remove:/,/^$/p' $LOGS/$pkg.log \
		| sed '/^Build/d; s|Removing: ||' \
		| tr ' ' '\n' \
		| sed '/^$/d' \
		> $WOK/$pkg/.bdeps

		;;
esac

exit 0
