#!/bin/sh
#    ______   ______  __  __   ______   
#   /\  __ \ /\  == \/\ \/ /  /\  ___\  
#   \ \  __ \\ \  _-/\ \  _"-.\ \ \__ \ 
#    \ \_\ \_\\ \_\   \ \_\ \_\\ \_____\
#     \/_/\/_/ \/_/    \/_/\/_/ \/_____/
#
# APKG-SRC - Alice Package Source Build Tool (C) 2023-2026 Emmett1
#

fetch_src() {
	[ "$source" ] || return 0
	for i in $source; do
		unset resume
		i=${i%::noextract}
		case $i in
			*::*) sn=${i%::*};;
			   *) sn=${i##*/};;
		esac
		case ${i#*::} in
			http://*|https://*|ftp://*)
				if [ ! -f $APKG_SOURCE_DIR/$sn ]; then
					[ -f $APKG_SOURCE_DIR/$sn.tmp ] && resume="-C -"
					msg "fetching ${i#*::}"
					curl $resume --fail --retry 3 --retry-delay 3 -L -o $APKG_SOURCE_DIR/$sn.tmp ${i#*::} || {
						msg "Failed downloading source '$sn'."
						exit 1
					}
					mv $APKG_SOURCE_DIR/$sn.tmp $APKG_SOURCE_DIR/$sn
				fi;;
		esac
	done
}

extract_src() {
	prepare_workdir
	[ "$rebuild_mode" ] && return 0
	[ "$source" ] || return 0
	checksource
	for i in $source; do
		case $i in
			*/*) S=$APKG_SOURCE_DIR;;
			  *) S=$HERE;;
		esac
		case ${i%::noextract} in
			*::*) sn=${i%%::*};;
			   *) sn=${i##*/}; sn=${sn%::noextract};;
		esac
		case $i in
			*::noextract)
				i=${i%::noextract}
				cp $S/$sn $SRC || {
					msg "Failed copy '$S/$sn' to '$SRC'."
					ret 1
				};;
			*.tar.bz2|*.tar.gz|*.tar.lz|*.tar.lzma|*.tar.lzo|*.tar.xz|*.tar.Z|*.tb2|*.tbz|*.tbz2|*.tz2|*.taz|*.tgz|*.tlz|*.txz|*.tZ|*.taZ)
				msg "Unpacking '$S/$sn'..."
				tar -p -o -C $SRC -xf $S/$sn || {
					msg "Failed unpacking source '${i##*/}'."
					ret 1
				};;
			*) 
				i=${i%::noextract}
				cp $S/$sn $SRC || {					
					msg "Failed copy '$S/$sn' to '$SRC'."
					ret 1
				};;
		esac
	done
}

prepare_workdir() {
	export SRC=$APKG_WORK_DIR/apkg-src-${PWD##*/}
	export PKG=$APKG_WORK_DIR/apkg-pkg-${PWD##*/}

	rebuild_mode=
	if [ "$safely_rebuildable" ] && [ -f $HERE/.apkg-version ] && \
	   [ "$(cat $HERE/.apkg-version)" = "$version-$release" ] && [ -d "$SRC" ]; then
		rebuild_mode=1
		msg "version unchanged ($version-$release), running rebuild()"
	else
		remove_workdir
		mkdir -p "$PKG" "$SRC"
	fi
}

remove_workdir() {
	rm -rf "$PKG" "$SRC"
}

ret() {
	[ "$APKG_KEEP_WORKDIR" ] || [ "$safely_rebuildable" ] || remove_workdir
	exit ${1:-0}
}

detect_buildtype() {
	if [ -f meson.build ]; then
		build_type=muon_build
	elif [ -f configure ]; then
		build_type=configure_build
	elif [ -f CMakeLists.txt ]; then
		build_type=cmake_build
	elif [ -f setup.py ]; then
		build_type=python_build
	elif [ -f Makefile.PL ]; then
		build_type=perlmodule_build
	elif [ -f Makefile ] || [ -f makefile ] || [ -f GNUmakefile ]; then
		build_type=makefile_build
	else
		msg "failed to detect buildtype"
		ret 1
	fi
}

_makefile_build() {
	make \
		PREFIX=/usr \
		prefix=/usr \
		SYSCONFDIR=/etc \
		sysconfdir=/etc \
		MANDIR=/usr/share/man \
		mandir=/usr/share/man \
		LIBDIR=/usr/lib \
		PKGCONFIGDIR=/usr/lib/pkgconfig
	make \
		PREFIX=/usr \
		prefix=/usr \
		SYSCONFDIR=/etc \
		sysconfdir=/etc \
		MANDIR=/usr/share/man \
		mandir=/usr/share/man \
		LIBDIR=/usr/lib \
		PKGCONFIGDIR=/usr/lib/pkgconfig \
		DESTDIR=$PKG install
}

_perlmodule_build() {
	perl Makefile.PL
	make
	make DESTDIR=$PKG install
}

_cmake_build() {
	mkdir -p cmakebuild
	cd cmakebuild
	cmake \
		-DCMAKE_INSTALL_PREFIX=/usr \
		-DCMAKE_INSTALL_SYSCONFDIR=/etc \
		-DCMAKE_INSTALL_LIBDIR=lib \
		-DCMAKE_BUILD_TYPE=Release \
		-DFETCHCONTENT_FULLY_DISCONNECTED=ON \
		-DCMAKE_C_FLAGS_RELEASE="$CFLAGS" \
		-DCMAKE_CXX_FLAGS_RELEASE="$CXXFLAGS" \
		$build_opt \
		-B build ..
	if [ -f build.ninja ]; then
		ninja
		DESTDIR=$PKG ninja install
	else
		cmake --build build
		DESTDIR=$PKG cmake --install build
	fi
}

_python_build() {
	python3 setup.py build
	python3 setup.py install --prefix=/usr --root=$PKG --optimize=1 $build_opt
}

_configure_build() {
	./configure \
		--prefix=/usr \
		--sysconfdir=/etc \
		--localstatedir=/var \
		--libdir=/usr/lib \
		--infodir=/usr/share/info \
		--mandir=/usr/share/man \
		$build_opt
	make
	make DESTDIR=$PKG install
}

_meson_build() {
	meson setup \
		-Dprefix=/usr \
		-Dlibdir=/usr/lib \
		-Dincludedir=/usr/include \
		-Ddatadir=/usr/share \
		-Dmandir=/usr/share/man \
		-Dinfodir=/usr/share/info \
		-Dlocaledir=/usr/share/locale \
		-Dsysconfdir=/etc \
		-Dlocalstatedir=/var \
		-Dsharedstatedir=/var/lib \
		-Dbuildtype=plain \
		-Dauto_features=auto \
		-Dwrap_mode=nodownload \
		-Db_lto=true \
		-Db_pie=true \
		$build_opt \
		build
	ninja -C build
	DESTDIR=$PKG meson install -C build
}

_muon_build() {
	muon setup \
		-Dprefix=/usr \
		-Dlibdir=/usr/lib \
		-Dincludedir=/usr/include \
		-Ddatadir=/usr/share \
		-Dmandir=/usr/share/man \
		-Dinfodir=/usr/share/info \
		-Dlocaledir=/usr/share/locale \
		-Dsysconfdir=/etc \
		-Dlocalstatedir=/var \
		-Dsharedstatedir=/var/lib \
		-Dbuildtype=plain \
		-Dauto_features=auto \
		-Dwrap_mode=nodownload \
		-Ddefault_library=shared \
		-Db_lto=true \
		-Db_pie=true \
		$build_opt \
		build
	samu -C build
	DESTDIR=$PKG muon -C build install -d "$PKG"
}

apply_patch() {
	[ "$skip_patch" ] && {
		unset skip_patch # allowing manual patch in build() using apply_patch
		return 0
	}
	if [ "$source" ]; then
		for p in $source; do
			case ${p%::noextract} in
				*::*) pn=${p%::*};;
				   *) pn=${p##*/};;
			esac
			case $pn in
				*.patch|*.diff)
					msg "applying patch '$SRC/$pn'."
					patch ${patch_opt:--p1} -i $SRC/$pn || ret 1;;
			esac
		done
	fi
}

msg() {
	# Don't bother if we're not in a terminal
    if [ -t 1 ]; then
        ttysize=$(stty size 2>/dev/null | awk '{print $2}' || echo "80")
    fi
    printf "%s\n" "[${name:-...}] $@" | fold -sw ${ttysize:-80}
}

buildstatus() {
	[ "$?" = 0 ] || {
		msg "build package '$name-$version-$release' failed on '$1'."
		ret 1
	}
}

build_src() {
	cd $SRC
	
	if [ "$build_dir" ]; then
		if [ ! -d "$build_dir" ]; then
			msg "Source directory '$build_dir' not found."
			ret 1
		fi
	elif [ -d $name-$version ]; then
		build_dir=$name-$version
	elif [ -d "$(ls -1d */ 2>/dev/null | head -n1)" ]; then
		build_dir=$(ls -1d */ 2>/dev/null | head -n1)
		build_dir=${build_dir%/}
	fi
	
	# cd into extracted source directory
	if [ "$build_dir" ]; then
		cd $build_dir
	fi
	
	[ "$rebuild_mode" ] || apply_patch

	export CARGO_HOME=${CARGO_HOME:-$SRC/.cargo}
	export GOCACHE=${GOCACHE:-$SRC/.go}
	export DESTDIR=$PKG
	export DEST_DIR=$PKG     # p7zip
	export INSTALLROOT=$PKG  # syslinux
	export install_root=$PKG # glibc
	export INSTALL_ROOT=$PKG # qt5

	if [ ! "$source" ]; then
		# dummy pkg
		mkdir -p $PKG/usr
	elif [ "$rebuild_mode" ] && [ "$(command -v rebuild)" ]; then
		(set -e -x; rebuild)
		buildstatus rebuild
	elif [ "$(command -v build)" ]; then
		(set -e -x; build)
		buildstatus build
	else
		if [ "$(command -v prebuild)" ]; then
			(set -e -x; prebuild)
			buildstatus prebuild
		fi
		if [ ! "${build_type}" ]; then
			detect_buildtype
		fi
		(set -e -x; _${build_type})
		buildstatus ${build_type}
		if [ "$(command -v postbuild)" ]; then
			(set -e -x; postbuild)
			buildstatus postbuild
		fi
	fi
	
	msg "build $name-$version-$release success"
	
	# compress man pages
	if [ -d $PKG/usr/share/man ]; then
		# only keep man[1-8] and man[1-8]p man pages
		for m in $PKG/usr/share/man/*; do
			case ${m##*/} in
				man[1-8] | man[1-8]p) continue;;
			esac
			rm -rf $m
		done
		find $PKG/usr/share/man -type f | while IFS= read -r i; do
			[ "$i" = "${i%%.gz}" ] || continue
			gzip -9 "$i"
		done
		find $PKG/usr/share/man -type l | while IFS= read -r i; do
			[ "$i" = "${i%%.gz}" ] || continue
			ln -s "$(readlink "$i").gz" "${i%.gz}.gz"
			rm "$i"
		done
	fi

	# compress info pages
	if [ -d $PKG/usr/share/info ]; then
		rm -f $PKG/usr/share/info/dir
		find $PKG/usr/share/info -type f -exec gzip -9 {} \;
	fi
	
	# possible conflicts
	find $PKG -name "fonts.dir" -exec rm {} \;
	find $PKG -name "fonts.scale" -exec rm {} \;
	find $PKG -name "perllocal.pod" -exec rm {} \;
	find $PKG -name "charset.alias" -exec rm {} \;
	
	if [ ! "$keep_static" ]; then
		find $PKG -name "*.a" -exec rm {} \;
	fi
	
	if [ ! "$keep_libtool" ]; then
		find $PKG -name "*.la" -exec rm {} \;
	fi
	
	if [ ! "$keep_locale" ]; then
		rm -rf $PKG/usr/share/locale
		rm -rf $PKG/usr/lib/locale
	fi
	
	if [ ! "$keep_doc" ]; then
		rm -rf $PKG/usr/share/doc
		rm -rf $PKG/usr/doc
	fi
	
	# strip binaries and libraries
	if [ ! "$no_strip" ]; then
		find $PKG -type f -exec file {} + | while read -r line; do
			f=${line%%: *}
			case $line in
				*ELF*executable*) ${CROSS_COMPILE}strip --strip-all "$f" 2>/dev/null;;
				*ELF*shared\ object*) ${CROSS_COMPILE}strip --strip-unneeded "$f" 2>/dev/null;;
				*current\ ar\ archive*) ${CROSS_COMPILE}strip --strip-debug "$f" 2>/dev/null;;
			esac
		done
	fi
		
	# runit service
	for s in $sv; do
		[ -f $SRC/$s ] || ret 1
		case $s in
				 run) install -Dm755 $SRC/$s $PKG/etc/sv/$name/run
					  ln -s ../../../run/runit/supervise.$name $PKG/etc/sv/$name/supervise;;
			  finish) install -Dm755 $SRC/$s $PKG/etc/sv/$name/finish;;
			   *.run) install -Dm755 $SRC/$s $PKG/etc/sv/${s%.*}/run
					  ln -s ../../../run/runit/supervise.${s%.*} $PKG/etc/sv/${s%.*}/supervise;;
			*.finish) install -Dm755 $SRC/$s $PKG/etc/sv/${s%.*}/finish;;
				 *.*) install -Dm644 $SRC/$s $PKG/etc/sv/${s%%.*}/${s#*.};;
				   *) install -Dm644 $SRC/$s $PKG/etc/sv/$name/$s;;
		esac
	done
		
	if [ ! "$(ls -1 $PKG)" ]; then
		msg "\$PKG is empty"
		ret 1
	fi
	
	cd $PKG
	spm -b $packagefile || {
		msg "Failed packaging $packagefile"
		ret 1
	}
	cd $HERE
	
	if [ ! -f "$apkg_list" ] || [ -n "$(find "$apkg_build" -newer "$apkg_list")" ]; then
		pkg_updatefiles
	fi

	printf "%s\n" "$version-$release" > $HERE/.apkg-version

	remove_workdir
}

pkg_updatefiles() {
	if [ ! -f "$packagefile" ]; then
		msg "Package '$packagefile' not found."
		ret 1
	fi
	msg "filelist updated."
	tar -tvf $packagefile | awk '{$3=$4=$5=""; print $0}' | sort -k 3 > "$apkg_list"
}

pkg_path() {
	needrepo
	for p in $@; do
		for r in $APKG_REPO; do
			[ -f $r/$p/${apkg_build##*/} ] && {
				printf "%s\n" $r/$p
				break
			}
		done
	done
}

die() {
	[ "$1" ] && printf "%s\n" "error: $1"
	ret 1
}


pkg_search() {
	needrepo
	if [ "$pkg" ]; then
		for p in $pkg; do
			grep="$grep -e $p"
		done
		grep="grep $grep"
	else
		grep=cat
	fi
	[ "$verbose" ] && {
		find $APKG_REPO -type f -name ${apkg_build##*/} 2>/dev/null | sed "s|/${apkg_build##*/}$||" | rev | awk -F / '{print $1}' | rev | $grep | while read line; do printf "$line "; awk -F'=' '/^version/ {print $2}' $(pkg_path $line)/abuild ; done
	} || {
		find $APKG_REPO -type f -name ${apkg_build##*/} 2>/dev/null | sed "s|/${apkg_build##*/}$||" | rev | awk -F / '{print $1}' | rev | $grep
	}
}


_trigger_fccache() {
	command -v fc-cache >/dev/null || return 0
	spm -l $1 | grep -q ^usr/share/fonts/$ && {
		msg "Updating fontconfig cache..."
		fc-cache -sf
	}
}

_trigger_gdkpixbufcache() {
	command -v gdk-pixbuf-query-loaders >/dev/null || return 0
	spm -l $1 | grep -q ^usr/lib/gdk-pixbuf-2.0/2.10.0/loaders/$ && {
		msg "Probing GDK-Pixbuf loader modules..."
		gdk-pixbuf-query-loaders --update-cache
	}
}

_trigger_giomodules() {
	command -v gio-querymodules >/dev/null || return 0
	spm -l $1 | grep -q ^usr/lib/gio/modules/$ && {
		msg "Updating GIO module cache..."
		gio-querymodules /usr/lib/gio/modules
	}
}

_trigger_glibschema() {
	command -v glib-compile-schemas >/dev/null || return 0
	spm -l $1 | grep -q ^usr/share/glib-2.0/schemas/$ && {
		msg "Compiling GSettings XML schema files..."
		glib-compile-schemas /usr/share/glib-2.0/schemas
	}
}

_trigger_gtk2immodules() {
	command -v gtk-query-immodules-2.0 >/dev/null || return 0
	spm -l $1 | grep -q ^usr/lib/gtk-2.0/2.10.0/immodules/$ && {
		msg "Probing GTK2 input method modules..."
		gtk-query-immodules-2.0 --update-cache
	}
}

_trigger_gtk3immodules() {
	command -v gtk-query-immodules-3.0 >/dev/null || return 0
	spm -l $1 | grep -q ^usr/lib/gtk-3.0/3.0.0/immodules/$ && {
		msg "Probing GTK3 input method modules..."
		gtk-query-immodules-3.0 --update-cache
	}
}

_trigger_iconcache() {
	command -v gtk-update-icon-cache >/dev/null || return 0
	spm -l $1 | grep -q ^usr/share/icons/$ && {
		msg "Updating icon theme caches..."
		for dir in /usr/share/icons/* ; do
			if [ -e $dir/index.theme ]; then
				gtk-update-icon-cache -q $dir 2>/dev/null
			else
				rm -f $dir/icon-theme.cache
				rmdir --ignore-fail-on-non-empty $dir
			fi
		done
	}
}

_trigger_udevdb() {
	command -v udevadm >/dev/null || return 0
	spm -l $1 | grep -q ^etc/udev/hwdb.d/$ && {
		msg "Updating hardware database..."
		udevadm hwdb --update
	}
}

_trigger_fontcache() {
	command -v mkfontdir >/dev/null || return 0
	command -v mkfontscale >/dev/null || return 0
	spm -l $1 | grep -q ^usr/share/fonts/$ && {
		msg "Updating X fontdir indices..."
		for dir in $(find /usr/share/fonts -maxdepth 1 -type d \( ! -path /usr/share/fonts \)); do
			rm -f $dir/fonts.scale $dir/fonts.dir $dir/.uuid
			rmdir --ignore-fail-on-non-empty $dir
			[ -d "$dir" ] || continue
			mkfontdir $dir
			mkfontscale $dir
		done
	}
}

_trigger_desktopdb() {
	command -v update-desktop-database >/dev/null || return 0
	spm -l $1 | grep -q ^usr/share/applications/$ && {
		msg "Updating desktop file MIME type cache..."
		update-desktop-database --quiet
	}
}

_trigger_mimedb() {
	command -v update-mime-database >/dev/null || return 0
	spm -l $1 | grep -q ^usr/share/mime/$ && {
		msg "Updating the MIME type database..."
		update-mime-database /usr/share/mime
	}
}

pkg_trigger() {
	needroot
	[ "$@" ] || set -- $(spm -a)
	for t in mimedb desktopdb fontcache fccache udevdb iconcache gtk3immodules gtk2immodules glibschema giomodules gdkpixbufcache; do
		for p in $@; do
			_trigger_$t $p && break
		done
	done		
}

checksum_src() {	
	if [ ! -s "$apkg_csum" ]; then
		gencsum
	else
		printcsum > ${apkg_csum}.tmp
		diff -U 0 ${apkg_csum} ${apkg_csum}.tmp | \
			sed '/---/d;/+++/d;/@@/d' | \
			sed 's/^+/ new    : /' | \
			sed 's/^-/ missing: /' > ${apkg_csum}.diff
			[ -s "${apkg_csum}.diff" ] && {
				cat ${apkg_csum}.diff
				msg "Checksum failed."
				failed=1
			}
		rm -f ${apkg_csum}.tmp ${apkg_csum}.diff
		[ "$failed" ] && ret 1
	fi
}

printsource() {
	# print out source full path
	for printsource in $source; do
		printsource=${printsource%::noextract}
		case $printsource in
			*::*) sourcename=${printsource%::*};;
			   *) sourcename=${printsource##*/};;
		esac
		case ${printsource#*::} in
			*/*) sourcenamefullpath=${APKG_SOURCE_DIR}/$sourcename;;
			  *) sourcenamefullpath=${HERE}/$sourcename;;
		esac
		printf "%s\n" $sourcenamefullpath
	done
}

checksource() {
	# check for source existence, exit non-zero if missing
	for checksource in $(printsource); do
		[ -f "$checksource" ] || {
			msg "Source file '$checksource' not found."
			failed=1
		}
	done
	[ "$failed" ] && ret 1
}

gencsum() {
	checksource
	rm -f "$apkg_csum"
	printcsum > "$apkg_csum"
	msg "${apkg_csum##*/} file updated."
}

printcsum() {
	for printcsum in $(printsource); do
		[ -f "$printcsum" ] || continue
		$apkg_sha $printcsum | sed "s,$APKG_SOURCE_DIR/,,;s,$HERE/,,"
	done | sort -k 2
}

runscript() {
	if [ -x ./${1}install ]; then
		msg "Running ${1}install script..."
		if [ "$APKG_ROOT" ]; then
			cat ./${1}install > "$APKG_ROOT"/.runscript
			chmod +x "$APKG_ROOT"/.runscript
			chroot "$APKG_ROOT" /.runscript
			rm -f "$APKG_ROOT"/.runscript
		else
			./${1}install
		fi
	fi
}

needroot() {
	[ $(id -u) = 0 ] || {
		msg 'This operation need root access'
		exit 1
	}
}

parsesubopt() {
	# loop opts until found '-*'
	shift; while [ "$1" ]; do
		case $1 in
			-*) break;;
			*) printf "%s\n" "$1";;
		esac
		shift
	done
}

updateopts() {
	while [ "$1" ]; do
		case $1 in
			-*) newopt="$newopt $1";;
		esac
		shift
	done
	printf "%s\n" "$newopt"
}

parseopts() {
	while [ "$1" ]; do
		case $1 in
			-i) install=1;;
			-u) upgrade=1;;
			-f) forcerebuild=1;;
			-w) downloadonly=1;;
			-g) updatecsum=1;;
			-k) updatefiles=1;;
			-s) search=1;;
			-v) verbose=1;;
			-p) pkg_path "$(parsesubopt "$@")"; exit 0;;
			-t) pkg_trigger "$(parsesubopt "$@")"; exit 0;;
			-h) apkg_help; exit 0;;
			-*) msg "invalid option '$1'"; exit 1;;
			*) pkg="$pkg $1";;
		esac
		shift
	done
}

apkg_help() {
	cat << EOF
usage: ${0##*/} <option> <arg(s)>

options:
  -i <pkg(s)>  build and install package(s)
  -u <pkg(s)>  upgrade (rebuild and reinstall) package(s)
  -f           force rebuild
  -w <pkg(s)>  download source only
  -p <pkg(s)>  print package recipe path
  -g <pkg(s)>  update package checksum (.shasum)
  -k <pkg(s)>  update package files (.files)
  -s <pattern> search packages in repos
  -t [pkg(s)]  trigger system cache/db updates
  -v           be verbose (with -s)
  -h           print this help message

dependency queries (-d -dd -D -DD -j -jj) are handled by apkg-resolv;
binary package operations by apkg-bin; see apkg(8).

environment variables:
  APKG_REPO          space-separated repo directories (required for -p/-s and name lookups)
  APKG_PACKAGE_DIR   directory for built .spm files (default: $PWD)
  APKG_SOURCE_DIR    directory for source tarballs (default: $PWD)
  APKG_WORK_DIR      directory for build working tree (default: $PWD)
  APKG_ROOT          alternative install root (default: /)
  APKG_NOPROMPT      skip confirmation prompt (for scripting)
  APKG_KEEP_WORKDIR  keep work directory on build failure
  CFLAGS             C compiler flags (used by cmake build system)
  CXXFLAGS           C++ compiler flags (used by cmake build system)
  CROSS_COMPILE      prefix for strip command (e.g. x86_64-linux-musl-)

EOF
exit 0
}

main() {
	parseopts "$@"
	set -- $(updateopts "$@")
	
	if [ "$search" ]; then
		pkg_search $@
		exit 0
	fi
	
	for d in $APKG_PACKAGE_DIR $APKG_SOURCE_DIR $APKG_WORK_DIR; do
		[ -d "$d" ] || { msg "Directory '$d' not exist"; exit 1; }
		[ -w "$d" ] || { msg "Directory '$d' dont have write access"; exit 1; }
	done
	
	if [ "$pkg" ]; then
		for p in $pkg; do
			[ "$(pkg_path "$p")" ] || {
				msg "Package '$p' not found"
				continue
			}
			(cd "$(pkg_path "$p")" && $APKG_SRC "$@") || exit 1
		done
		exit 0
	fi

	if [ ! -f "${apkg_build}" ]; then
		msg "'abuild' not found."
		exit 1
	fi
	
	. ${apkg_build}
	[ "$name" ]    || die "name is empty"
	[ "$version" ] || die "version is empty"
	[ "$release" ] || die "release is empty"
	[ "$name" = "${PWD##*/}" ] || {
		msg "'name' should be same as package directory name (${PWD##*/})."
		exit 1
	}

	packagefile="$APKG_PACKAGE_DIR"/$name#$version-$release.spm

	if [ "$install" ] && [ -s "$SPM_PKGDB/${PWD##*/}" ]; then
		msg "Package '${PWD##*/}' already installed."
		exit 0
	fi
	
	if [ "$upgrade" ] && [ ! -s "$SPM_PKGDB/${PWD##*/}" ]; then
		msg "Package '${PWD##*/}' not installed."
		exit 0
	fi
	
	if [ "$updatecsum" ]; then
		gencsum
		exit 0
	fi
	
	if [ "$downloadonly" ]; then
		fetch_src
		exit 0
	fi
	
	if [ "$updatefiles" ]; then
		pkg_updatefiles
		exit 0
	fi
	
	if [ "$(id -u)" != 0 ]; then
		msg "Packages need to build as root. Aborted."
		exit 1
	fi
	
	if [ -f "$packagefile" ] && [ ! "$forcerebuild" ]; then
		if [ ! "$install" ] && [ ! "$upgrade" ]; then
			msg "Package '$packagefile' found."
		fi
	else	
		if [ "$install" ] || [ "$upgrade" ]; then
			# run preinstall script before build
			runscript pre
		fi
		fetch_src
		checksum_src
		extract_src
		build_src
	fi	
	
	if [ "$install" ]; then
		SPM_ROOT=${APKG_ROOT%/} spm -i "$packagefile" || exit $?
	elif [ "$upgrade" ]; then
		SPM_ROOT=${APKG_ROOT%/} spm -u "$packagefile" || exit $?
	fi
	
	if [ "$install" ] || [ "$upgrade" ]; then
		# run postinstall script after installed/upgraded
		runscript post
		[ "$APKG_ROOT" ] || pkg_trigger "$name"
	fi
}

umask 022

export HERE="$PWD"

apkg_sha="sha3sum"
apkg_csum="$HERE/.shasum"
apkg_list="$HERE/.files"
apkg_build="$HERE/abuild"

case $0 in
	/*) APKG_SRC=$0;;
	*)  APKG_SRC=$(realpath "$0" 2>/dev/null || printf '%s' "$PWD/$0");;
esac
SPM_PKGDB="${APKG_ROOT%/}/var/lib/spm/db"
APKG_PACKAGE_DIR="${APKG_PACKAGE_DIR:-$PWD}"
APKG_SOURCE_DIR="${APKG_SOURCE_DIR:-$PWD}"
APKG_WORK_DIR="${APKG_WORK_DIR:-$PWD}"

main "$@"

exit 0
