autils

Unnamed repository; edit this file 'description' to name the repository.
git clone https://codeberg.org/emmett1/autils
Log | Files | Refs | README | LICENSE

apkg (24083B)


      1 #!/bin/sh
      2 #    ______   ______  __  __   ______   
      3 #   /\  __ \ /\  == \/\ \/ /  /\  ___\  
      4 #   \ \  __ \\ \  _-/\ \  _"-.\ \ \__ \ 
      5 #    \ \_\ \_\\ \_\   \ \_\ \_\\ \_____\
      6 #     \/_/\/_/ \/_/    \/_/\/_/ \/_____/
      7 #
      8 # APKG - Alice Package Manager (C) 2023-2025 Emmett1
      9 #
     10 
     11 fetch_src() {
     12 	[ "$source" ] || return 0
     13 	for i in $source; do
     14 		i=${i%::noextract}
     15 		case $i in
     16 			*::*) sn=${i%::*};;
     17 			   *) sn=${i##*/};;
     18 		esac
     19 		case ${i#*::} in
     20 			http://*|https://*|ftp://*)
     21 				if [ ! -f $APKG_SOURCE_DIR/$sn ]; then
     22 					[ -f $APKG_SOURCE_DIR/$sn.tmp ] && resume="-C -"
     23 					msg "fetching ${i#*::}"
     24 					curl $resume --fail --retry 3 --retry-delay 3 -L -o $APKG_SOURCE_DIR/$sn.tmp ${i#*::} || {
     25 						msg "Failed downloading source '$sn'."
     26 						exit 1
     27 					}
     28 					mv $APKG_SOURCE_DIR/$sn.tmp $APKG_SOURCE_DIR/$sn
     29 				fi;;
     30 		esac
     31 	done
     32 }
     33 
     34 extract_src() {
     35 	prepare_workdir
     36 	[ "$source" ] || return 0
     37 	checksource
     38 	for i in $source; do
     39 		case $i in
     40 			*/*) S=$APKG_SOURCE_DIR;;
     41 			  *) S=$HERE;;
     42 		esac
     43 		case ${i%::noextract} in
     44 			*::*) sn=${i%%::*};;
     45 			   *) sn=${i##*/}; sn=${sn%::noextract};;
     46 		esac
     47 		case $i in
     48 			*::noextract)
     49 				i=${i%::noextract}
     50 				cp $S/$sn $SRC || {
     51 					msg "Failed copy '$S/$sn' to '$SRC'."
     52 					ret 1
     53 				};;
     54 			*.tar.bz2|*.tar.gz|*.tar.lz|*.tar.lzma|*.tar.lzo|*.tar.xz|*.tar.Z|*.tb2|*.tbz|*.tbz2|*.tz2|*.taz|*.tgz|*.tlz|*.txz|*.tZ|*.taZ)
     55 				msg "Unpacking '$S/$sn'..."
     56 				tar -p -o -C $SRC -xf $S/$sn || {
     57 					msg "Failed unpacking source '${i##*/}'."
     58 					ret 1
     59 				};;
     60 			*) 
     61 				i=${i%::noextract}
     62 				cp $S/$sn $SRC || {					
     63 					msg "Failed copy '$S/$sn' to '$SRC'."
     64 					ret 1
     65 				};;
     66 		esac
     67 	done
     68 }
     69 
     70 prepare_workdir() {
     71 	export SRC=$APKG_WORK_DIR/apkg-src-${PWD##*/}
     72 	export PKG=$APKG_WORK_DIR/apkg-pkg-${PWD##*/}
     73 	
     74 	remove_workdir
     75 	mkdir -p "$PKG" "$SRC"	
     76 }
     77 
     78 remove_workdir() {
     79 	rm -rf "$PKG" "$SRC"
     80 }
     81 
     82 ret() {	
     83 	[ "$APKG_KEEP_WORKDIR" ] || remove_workdir
     84 	exit $1
     85 }
     86 
     87 detect_buildtype() {
     88 	if [ -f meson.build ]; then
     89 		build_type=meson_build
     90 	elif [ -f configure ]; then
     91 		build_type=configure_build
     92 	elif [ -f CMakeLists.txt ]; then
     93 		build_type=cmake_build
     94 	elif [ -f setup.py ]; then
     95 		build_type=python_build
     96 	elif [ -f Makefile.PL ]; then
     97 		build_type=perlmodule_build
     98 	elif [ -f Makefile ] || [ -f makefile ] || [ -f GNUmakefile ]; then
     99 		build_type=makefile_build
    100 	else
    101 		msg "failed to detect buildtype"
    102 		ret 1
    103 	fi
    104 }
    105 
    106 _makefile_build() {
    107 	make \
    108 		PREFIX=/usr \
    109 		prefix=/usr \
    110 		SYSCONFDIR=/etc \
    111 		sysconfdir=/etc \
    112 		MANDIR=/usr/share/man \
    113 		mandir=/usr/share/man \
    114 		LIBDIR=/usr/lib \
    115 		PKGCONFIGDIR=/usr/lib/pkgconfig
    116 	make \
    117 		PREFIX=/usr \
    118 		prefix=/usr \
    119 		SYSCONFDIR=/etc \
    120 		sysconfdir=/etc \
    121 		MANDIR=/usr/share/man \
    122 		mandir=/usr/share/man \
    123 		LIBDIR=/usr/lib \
    124 		PKGCONFIGDIR=/usr/lib/pkgconfig \
    125 		DESTDIR=$PKG install
    126 }
    127 
    128 _perlmodule_build() {
    129 	perl Makefile.PL
    130 	make
    131 	make DESTDIR=$PKG install
    132 }
    133 
    134 _cmake_build() {
    135 	mkdir -p cmakebuild
    136 	cd cmakebuild
    137 	cmake \
    138 		-DCMAKE_INSTALL_PREFIX=/usr \
    139 		-DCMAKE_INSTALL_SYSCONFDIR=/etc \
    140 		-DCMAKE_INSTALL_LIBDIR=lib \
    141 		-DCMAKE_BUILD_TYPE=Release \
    142 		-DFETCHCONTENT_FULLY_DISCONNECTED=ON \
    143 		-DCMAKE_C_FLAGS_RELEASE="$CFLAGS" \
    144 		-DCMAKE_CXX_FLAGS_RELEASE="$CXXFLAGS" \
    145 		$build_opt \
    146 		-B build ..
    147 	if [ -f build.ninja ]; then
    148 		ninja
    149 		DESTDIR=$PKG ninja install
    150 	else
    151 		cmake --build build
    152 		DESTDIR=$PKG cmake --install build
    153 	fi
    154 }
    155 
    156 _python_build() {
    157 	python3 setup.py build
    158 	python3 setup.py install --prefix=/usr --root=$PKG --optimize=1 $build_opt
    159 }
    160 
    161 _configure_build() {
    162 	./configure \
    163 		--prefix=/usr \
    164 		--sysconfdir=/etc \
    165 		--localstatedir=/var \
    166 		--libdir=/usr/lib \
    167 		--infodir=/usr/share/info \
    168 		--mandir=/usr/share/man \
    169 		$build_opt
    170 	make
    171 	make DESTDIR=$PKG install
    172 }
    173 
    174 _meson_build() {
    175 	meson setup \
    176 		-Dprefix=/usr \
    177 		-Dlibdir=/usr/lib \
    178 		-Dincludedir=/usr/include \
    179 		-Ddatadir=/usr/share \
    180 		-Dmandir=/usr/share/man \
    181 		-Dinfodir=/usr/share/info \
    182 		-Dlocaledir=/usr/share/locale \
    183 		-Dsysconfdir=/etc \
    184 		-Dlocalstatedir=/var \
    185 		-Dsharedstatedir=/var/lib \
    186 		-Dbuildtype=plain \
    187 		-Dauto_features=auto \
    188 		-Dwrap_mode=nodownload \
    189 		-Db_lto=true \
    190 		-Db_pie=true \
    191 		$build_opt \
    192 		build
    193 	ninja -C build
    194 	DESTDIR=$PKG meson install -C build
    195 }
    196 
    197 apply_patch() {
    198 	[ "$skip_patch" ] && {
    199 		unset skip_patch # allowing manual patch in build() using apply_patch
    200 		return 0
    201 	}
    202 	if [ "$source" ]; then
    203 		for p in $source; do
    204 			case ${p%::noextract} in
    205 				*::*) pn=${p%::*};;
    206 				   *) pn=${p##*/};;
    207 			esac
    208 			case $pn in
    209 				*.patch|*.diff)
    210 					msg "applying path '$SRC/$pn'."
    211 					patch ${patch_opt:--p1} -t -i $SRC/$pn || ret 1;;
    212 			esac
    213 		done
    214 	fi
    215 }
    216 
    217 msg() {
    218 	# Don't bother if we're not in a terminal
    219     if [ -t 1 ]; then
    220         ttysize=$(stty size 2>/dev/null | awk '{print $2}' || echo "80")
    221     fi
    222     printf "%s\n" "[${name:-...}] $@" | fold -sw ${ttysize:-80}
    223 }
    224 
    225 buildstatus() {
    226 	[ "$?" = 0 ] || {
    227 		msg "build package '$name-$version-$release' failed on '$1'."
    228 		ret 1
    229 	}
    230 }
    231 
    232 build_src() {
    233 	cd $SRC
    234 	
    235 	if [ "$build_dir" ]; then
    236 		if [ ! -d "$build_dir" ]; then
    237 			msg "Source directory '$build_dir' not found."
    238 			ret 1
    239 		fi
    240 	elif [ -d $name-$version ]; then
    241 		build_dir=$name-$version
    242 	elif [ -d "$(ls -1 --group-directories-first | head -n1)" ]; then
    243 		build_dir=$(ls -1 --group-directories-first | head -n1)
    244 	fi
    245 	
    246 	# cd into extracted source directory
    247 	if [ "$build_dir" ]; then
    248 		cd $build_dir
    249 	fi
    250 	
    251 	apply_patch
    252 
    253 	export CARGO_HOME=${CARGO_HOME:-$SRC/.cargo}
    254 	export GOCACHE=${GOCACHE:-$SRC/.go}
    255 	export DESTDIR=$PKG
    256 	export DEST_DIR=$PKG     # p7zip
    257 	export INSTALLROOT=$PKG  # syslinux
    258 	export install_root=$PKG # glibc
    259 	export INSTALL_ROOT=$PKG # qt5
    260 
    261 	if [ ! "$source" ]; then
    262 		# dummy pkg
    263 		mkdir -p $PKG/usr
    264 	elif [ "$(command -v build)" ]; then
    265 		(set -e -x; build)
    266 		buildstatus build
    267 	else
    268 		if [ "$(command -v prebuild)" ]; then
    269 			(set -e -x; prebuild)
    270 			buildstatus prebuild
    271 		fi
    272 		if [ ! "${build_type}" ]; then
    273 			detect_buildtype
    274 		fi
    275 		(set -e -x; _${build_type})
    276 		buildstatus ${build_type}
    277 		if [ "$(command -v postbuild)" ]; then
    278 			(set -e -x; postbuild)
    279 			buildstatus postbuild
    280 		fi
    281 	fi
    282 	
    283 	msg "build $name-$version-$release success"
    284 	
    285 	# compress man pages
    286 	if [ -d $PKG/usr/share/man ]; then
    287 		# only keep man[1-8] man pages
    288 		for m in $PKG/usr/share/man/*; do
    289 			case ${m##*/} in
    290 				man[1-8]) continue;;
    291 			esac
    292 			rm -rf $m
    293 		done
    294 		for i in $(find $PKG/usr/share/man -type f); do
    295 			[ $i = ${i%%.gz} ] || continue
    296 			gzip -9 $i
    297 		done
    298 		for i in $(find $PKG/usr/share/man -type l) ; do
    299 			ln -s $(readlink $i).gz ${i%.gz}.gz
    300 			rm $i
    301 		done
    302 	fi
    303 
    304 	# compress info pages
    305 	if [ -d $PKG/usr/share/info ]; then
    306 		rm -f $PKG/usr/share/info/dir
    307 		find $PKG/usr/share/info -type f -exec gzip -9 {} \;
    308 	fi
    309 	
    310 	# possible conflicts
    311 	find $PKG -name "fonts.dir" -exec rm {} \;
    312 	find $PKG -name "fonts.scale" -exec rm {} \;
    313 	find $PKG -name "perllocal.pod" -exec rm {} \;
    314 	find $PKG -name "charset.alias" -exec rm {} \;
    315 	
    316 	if [ ! "$keep_static" ]; then
    317 		find $PKG -name "*.a" -exec rm {} \;
    318 	fi
    319 	
    320 	if [ ! "$keep_libtool" ]; then
    321 		find $PKG -name "*.la" -exec rm {} \;
    322 	fi
    323 	
    324 	if [ ! "$keep_locale" ]; then
    325 		rm -rf $PKG/usr/share/locale
    326 		rm -rf $PKG/usr/lib/locale
    327 	fi
    328 	
    329 	if [ ! "$keep_doc" ]; then
    330 		rm -rf $PKG/usr/share/doc
    331 		rm -rf $PKG/usr/doc
    332 	fi
    333 	
    334 	# strip binaries and libraries
    335 	if [ ! "$no_strip" ]; then
    336 		find $PKG | xargs file | grep "executable" | grep ELF | cut -f 1 -d : | xargs ${CROSS_COMPILE}strip --strip-all 2>/dev/null
    337 		find $PKG | xargs file | grep "shared object" | grep ELF | cut -f 1 -d : | xargs ${CROSS_COMPILE}strip --strip-unneeded 2>/dev/null
    338 		find $PKG | xargs file | grep "current ar archive" | cut -f 1 -d : | xargs ${CROSS_COMPILE}strip --strip-debug 2>/dev/null
    339 	fi
    340 	
    341 	# usrmerge
    342 	if [ "$APKG_BINMERGE" ]; then
    343 		for b in bin sbin usr/sbin; do
    344 			if [ -d $PKG/$b ]; then
    345 				mkdir -p $PKG/usr/bin
    346 				mv $PKG/$b/* $PKG/usr/bin
    347 				rm -rf $PKG/$b
    348 			fi
    349 		done
    350 	fi
    351 		
    352 	# runit service
    353 	for s in $sv; do
    354 		[ -f $SRC/$s ] || ret 1
    355 		case $s in
    356 				 run) install -Dm755 $SRC/$s $PKG/etc/sv/$name/run
    357 					  ln -s ../../../run/runit/supervise.$name $PKG/etc/sv/$name/supervise;;
    358 			  finish) install -Dm755 $SRC/$s $PKG/etc/sv/$name/finish;;
    359 			   *.run) install -Dm755 $SRC/$s $PKG/etc/sv/${s%.*}/run
    360 					  ln -s ../../../run/runit/supervise.${s%.*} $PKG/etc/sv/${s%.*}/supervise;;
    361 			*.finish) install -Dm755 $SRC/$s $PKG/etc/sv/${s%.*}/finish;;
    362 				 *.*) install -Dm644 $SRC/$s $PKG/etc/sv/${s%%.*}/${s#*.};;
    363 				   *) install -Dm644 $SRC/$s $PKG/etc/sv/$name/$s;;
    364 		esac
    365 	done
    366 		
    367 	if [ ! "$(ls -1 $PKG)" ]; then
    368 		msg "\$PKG is empty"
    369 		ret 1
    370 	fi
    371 	
    372 	# temporary: for backward compatibility
    373 	if [ "$(grep '#MARKER' /usr/bin/spm)" ]; then
    374 		cd $PKG
    375 		spm -b $packagefile || {
    376 			msg "Failed packaging $packagefile"
    377 			ret 1
    378 		}
    379 	else
    380 		cd $APKG_PACKAGE_DIR
    381 		spm -b $PKG && {
    382 			mv package.spm $packagefile
    383 		} || {
    384 			msg "Failed packaging $packagefile"
    385 			ret 1
    386 		}
    387 	fi
    388 	
    389 	if [ ! -f "$HERE"/.files ] || [ "$HERE"/abuild -nt "$HERE"/.files ]; then
    390 		pkg_updatefiles
    391 	fi
    392 
    393 	cd $HERE
    394 	remove_workdir
    395 }
    396 
    397 pkg_updatefiles() {
    398 	if [ ! -f "$packagefile" ]; then
    399 		msg "Package '$packagefile' not found."
    400 		ret 1
    401 	fi
    402 	msg "filelist updated."
    403 	tar -tvf $packagefile | awk '{$3=$4=$5=""; print $0}' | sort -k 3 > "$HERE"/.files
    404 }
    405 
    406 pkg_path() {
    407 	for p in $@; do
    408 		for r in $APKG_REPO; do
    409 			[ -f $r/$p/abuild ] && {
    410 				printf "%s\n" $r/$p
    411 				break
    412 			}
    413 		done
    414 	done
    415 }
    416 
    417 die() {
    418 	[ "$1" ] && printf "%s\n" "error: $1"
    419 	ret 1
    420 }
    421 
    422 checkdep() {
    423 	[ "$(pkg_path $1)" ] || return
    424 	# track processed pkg to avoid cycle deps
    425 	process="$process $1"
    426 	for ii in $(pkg_depends $1); do
    427 		# if already installed, skip
    428 		if [ "$skip_installed" ]; then
    429 			[ -s $SPM_PKGDB/$ii ] && continue
    430 		fi
    431 		# if deps already in process list, skip, cycle deps detected
    432 		printf "%s\n" $process | tr ' ' '\n' | grep -qx $ii && continue
    433 		# skip if itself in depends list
    434 		[ "$ii" = "$1" ] && continue
    435 		# skip if pkg already in deps list
    436 		printf "%s\n" $DEPS | tr ' ' '\n' | grep -x $ii && continue
    437 		# check deps
    438 		checkdep $ii
    439 	done
    440 	# will go here if no deps anymore to check, add it to list deps
    441 	DEPS="$DEPS $1"
    442 }
    443 
    444 pkg_deplist() {
    445 	for i in $@; do
    446 		# if already have in list deps, dont check again
    447 		if [ ! $(printf "%s\n" $DEPS | tr ' ' '\n' | grep -x $i) ]; then
    448 			checkdep $i
    449 		fi
    450 	done
    451 	printf "%s\n" $DEPS | tr ' ' '\n'
    452 }
    453 
    454 pkg_fsearch() {
    455 	for r in $APKG_REPO; do
    456 		rr="$rr $r/*/.files"
    457 	done
    458 	grep $1 $rr 2>/dev/null | tr ':' ' ' | awk '{print $1,$4}' | sed 's,/.files,,g'
    459 }
    460 
    461 pkg_dependents() {
    462 	for r in $APKG_REPO; do
    463 		rr="$rr $r/*/depends"
    464 	done
    465 	grep -x $1 $rr 2>/dev/null | rev | cut -d / -f 2 | rev
    466 }
    467 
    468 pkg_depends() {
    469 	_path=$(pkg_path $1) || return 1
    470 	[ -s $_path/depends ] || return
    471 	solve_alias $(grep -Ev ^'(#|$)' $_path/depends | awk '{print $1}')
    472 }
    473 
    474 solve_alias() {
    475 	[ "$APKG_ALIAS" ] || {
    476 		printf "%s\n" $@
    477 		return
    478 	}
    479 	while [ "$1" ]; do
    480 		d=$(printf "%s\n" $APKG_ALIAS | tr ' ' '\n' | grep ^$1:* | head -n1 | awk -F : '{print $2}')
    481 		printf "%s\n" ${d:-$1}
    482 		shift
    483 	done
    484 }
    485 
    486 pkg_outdate() {
    487 	verbose=1 pkg_allinstalled | while read -r n v; do
    488 		[ "$APKG_MASK" ] && {
    489 			printf "%s\n" "$APKG_MASK" | tr ' ' '\n' | grep -xq $n && continue
    490 		}
    491 		[ "$(pkg_path $n)" ] || continue
    492 		nv="$(grep ^version= $(pkg_path $n)/abuild | tail -n1 | awk -F = '{print $2}')-$(grep ^release= $(pkg_path $n)/abuild | awk -F = '{print $2}')"
    493 		[ "$nv" ] || continue
    494 		[ "$v" = "$nv" ] || printf "%s\n" "$n $v -> $nv"
    495 	done
    496 }
    497 
    498 pkg_remove() {
    499 	needroot
    500 	for i in $@; do
    501 		SPM_ROOT=$APKG_ROOT spm -r $i
    502 	done
    503 }
    504 
    505 pkg_sysup() {
    506 	needroot
    507 	msg "Checking for outdated packages..."
    508 	od=$(pkg_outdate | awk '{print $1}')
    509 	if [ ! "$od" ]; then
    510 		msg "No outdated packages."
    511 		exit 0
    512 	else
    513 		msg "Solving dependencies..."
    514 		for p in $(pkg_deplist $od); do
    515 			[ -s $SPM_PKGDB/$p ] || ni="$ni $p"
    516 			printf "%s\n" $od | tr ' ' '\n' | grep -qx $p && pu="$pu $p"
    517 		done
    518 		if [ "$ni" ]; then
    519 			newpkg=$(printf "%s\n" $ni | tr ' ' '\n' | wc -l)
    520 			msg "Installing $newpkg new package(s): $ni"
    521 		fi
    522 		totalpkg=$(printf "%s\n" $pu | tr ' ' '\n' | wc -l)
    523 		msg "Upgrading $totalpkg package(s): $pu"
    524 		prompt_user
    525 		if [ "$ni" ]; then
    526 			$APKG -i $ni || die
    527 		fi
    528 		$APKG -u $pu || die
    529 	fi
    530 }
    531 
    532 pkg_search() {
    533 	if [ ! "$APKG_REPO" ]; then
    534 		msg "No repo configured."
    535 		exit 1
    536 	fi
    537 	if [ "$pkg" ]; then
    538 		for p in $pkg; do
    539 			grep="$grep -e $p"
    540 		done
    541 		grep="grep $grep"
    542 	else
    543 		grep=cat
    544 	fi
    545 	[ "$verbose" ] && {
    546 		find $APKG_REPO -type f -name abuild 2>/dev/null | sed 's|/abuild||' | rev | awk -F / '{print $1}' | rev | $grep | while read line; do printf "$line "; awk -F'=' '/^version/ {print $2}' $($APKG -p $line)/abuild ; done
    547 	} || {
    548 		find $APKG_REPO -type f -name abuild 2>/dev/null | sed 's|/abuild||' | rev | awk -F / '{print $1}' | rev | $grep
    549 	}
    550 }
    551 
    552 pkg_depinstalll() {
    553 	needroot
    554 	for p in $@; do
    555 		if [ -s $SPM_PKGDB/$p ]; then
    556 			msg "Package '$p' is installed"
    557 			continue
    558 		fi
    559 		if [ ! "$(pkg_path $p)" ]; then
    560 			msg "Package '$p' not found"
    561 			continue
    562 		fi
    563 		pkg="$pkg $p"
    564 	done
    565 	[ "$pkg" ] || exit 0
    566 	set -- $pkg
    567 	msg "Solving dependencies..."
    568 	for p in $(skip_installed=1 pkg_deplist $@); do
    569 		[ -s $SPM_PKGDB/$p ] && continue
    570 		installthis="$installthis $p"
    571 	done
    572 	if [ ! "$installthis" ]; then
    573 		msg "Nothing to install. Exiting..."
    574 		exit 0
    575 	fi
    576 	totalpkg=$(printf "%s\n" $installthis | tr ' ' '\n' | wc -l)
    577 	msg "Installing $totalpkg package(s): $installthis"
    578 	prompt_user
    579 	$APKG -i $installthis || die
    580 }
    581 
    582 _trigger_fccache() {
    583 	command -v fc-cache >/dev/null || return 0
    584 	spm -l $1 | grep -q ^usr/share/fonts/$ && {
    585 		msg "Updating fontconfig cache..."
    586 		fc-cache -sf
    587 	}
    588 }
    589 
    590 _trigger_gdkpixbufcache() {
    591 	command -v gdk-pixbuf-query-loaders >/dev/null || return 0
    592 	spm -l $1 | grep -q ^usr/lib/gdk-pixbuf-2.0/2.10.0/loaders/$ && {
    593 		msg "Probing GDK-Pixbuf loader modules..."
    594 		gdk-pixbuf-query-loaders --update-cache
    595 	}
    596 }
    597 
    598 _trigger_giomodules() {
    599 	command -v gio-querymodules >/dev/null || return 0
    600 	spm -l $1 | grep -q ^usr/lib/gio/modules/$ && {
    601 		msg "Updating GIO module cache..."
    602 		gio-querymodules /usr/lib/gio/modules
    603 	}
    604 }
    605 
    606 _trigger_glibschema() {
    607 	command -v glib-compile-schemas >/dev/null || return 0
    608 	spm -l $1 | grep -q ^usr/share/glib-2.0/schemas/$ && {
    609 		msg "Compiling GSettings XML schema files..."
    610 		glib-compile-schemas /usr/share/glib-2.0/schemas
    611 	}
    612 }
    613 
    614 _trigger_gtk2immodules() {
    615 	command -v gtk-query-immodules-2.0 >/dev/null || return 0
    616 	spm -l $1 | grep -q ^usr/lib/gtk-2.0/2.10.0/immodules/$ && {
    617 		msg "Probing GTK2 input method modules..."
    618 		gtk-query-immodules-2.0 --update-cache
    619 	}
    620 }
    621 
    622 _trigger_gtk3immodules() {
    623 	command -v gtk-query-immodules-3.0 >/dev/null || return 0
    624 	spm -l $1 | grep -q ^usr/lib/gtk-3.0/3.0.0/immodules/$ && {
    625 		msg "Probing GTK3 input method modules..."
    626 		gtk-query-immodules-3.0 --update-cache
    627 	}
    628 }
    629 
    630 _trigger_iconcache() {
    631 	command -v gtk-update-icon-cache >/dev/null || return 0
    632 	spm -l $1 | grep -q ^usr/share/icons/$ && {
    633 		msg "Updating icon theme caches..."
    634 		for dir in /usr/share/icons/* ; do
    635 			if [ -e $dir/index.theme ]; then
    636 				gtk-update-icon-cache -q $dir 2>/dev/null
    637 			else
    638 				rm -f $dir/icon-theme.cache
    639 				rmdir --ignore-fail-on-non-empty $dir
    640 			fi
    641 		done
    642 	}
    643 }
    644 
    645 _trigger_udevdb() {
    646 	command -v udevadm >/dev/null || return 0
    647 	spm -l $1 | grep -q ^etc/udev/hwdb.d/$ && {
    648 		msg "Updating hardware database..."
    649 		udevadm hwdb --update
    650 	}
    651 }
    652 
    653 _trigger_fontcache() {
    654 	command -v mkfontdir >/dev/null || return 0
    655 	command -v mkfontscale >/dev/null || return 0
    656 	spm -l $1 | grep -q ^usr/share/fonts/$ && {
    657 		msg "Updating X fontdir indices..."
    658 		for dir in $(find /usr/share/fonts -maxdepth 1 -type d \( ! -path /usr/share/fonts \)); do
    659 			rm -f $dir/fonts.scale $dir/fonts.dir $dir/.uuid
    660 			rmdir --ignore-fail-on-non-empty $dir
    661 			[ -d "$dir" ] || continue
    662 			mkfontdir $dir
    663 			mkfontscale $dir
    664 		done
    665 	}
    666 }
    667 
    668 _trigger_desktopdb() {
    669 	command -v update-desktop-database >/dev/null || return 0
    670 	spm -l $1 | grep -q ^usr/share/applications/$ && {
    671 		msg "Updating desktop file MIME type cache..."
    672 		update-desktop-database --quiet
    673 	}
    674 }
    675 
    676 _trigger_mimedb() {
    677 	command -v update-mime-database >/dev/null || return 0
    678 	spm -l $1 | grep -q ^usr/share/mime/$ && {
    679 		msg "Updating the MIME type database..."
    680 		update-mime-database /usr/share/mime
    681 	}
    682 }
    683 
    684 pkg_trigger() {
    685 	needroot
    686 	[ "$@" ] || set -- $($APKG -a)
    687 	for t in mimedb desktopdb fontcache fccache udevdb iconcache gtk3immodules gtk2immodules glibschema giomodules gdkpixbufcache; do
    688 		for p in $@; do
    689 			_trigger_$t $p && break
    690 		done
    691 	done		
    692 }
    693 
    694 pkg_allinstalled() {
    695 	if [ "$pkg" ]; then
    696 		for p in $pkg; do
    697 			grep="$grep -e $p"
    698 		done
    699 		grep="grep $grep"
    700 	else
    701 		grep=cat
    702 	fi
    703 	[ "$verbose" ] && {
    704 		ls -1 $SPM_PKGDB | $grep | while read line; do printf "$line "; head -n1 $SPM_PKGDB/$line ; done
    705 	} || {
    706 		ls -1 $SPM_PKGDB | $grep
    707 	}
    708 }
    709 
    710 checksum_src() {	
    711 	if [ ! -s "$checksum_file" ]; then
    712 		gencsum
    713 	else
    714 		b3sumcheck || return 0
    715 		printcsum > ${checksum_file}.tmp
    716 		diff -U 0 ${checksum_file} ${checksum_file}.tmp | \
    717 			sed '/---/d;/+++/d;/@@/d' | \
    718 			sed 's/^+/ new    : /' | \
    719 			sed 's/^-/ missing: /' > ${checksum_file}.diff
    720 			[ -s "${checksum_file}.diff" ] && {
    721 				cat ${checksum_file}.diff
    722 				msg "Checksum failed."
    723 				failed=1
    724 			}
    725 		rm -f ${checksum_file}.tmp ${checksum_file}.diff
    726 		[ "$failed" ] && ret 1
    727 	fi
    728 }
    729 
    730 printsource() {
    731 	# print out source full path
    732 	for printsource in $source; do
    733 		printsource=${printsource%::noextract}
    734 		case $printsource in
    735 			*::*) sourcename=${printsource%::*};;
    736 			   *) sourcename=${printsource##*/};;
    737 		esac
    738 		case ${printsource#*::} in
    739 			*/*) sourcenamefullpath=${APKG_SOURCE_DIR}/$sourcename;;
    740 			  *) sourcenamefullpath=${HERE}/$sourcename;;
    741 		esac
    742 		printf "%s\n" $sourcenamefullpath
    743 	done
    744 }
    745 
    746 checksource() {
    747 	# check for source existence, exit non-zero if missing
    748 	for checksource in $(printsource); do
    749 		[ -f "$checksource" ] || {
    750 			msg "Source file '$checksource' not found."
    751 			failed=1
    752 		}
    753 	done
    754 	[ "$failed" ] && ret 1
    755 }
    756 
    757 b3sumcheck() {
    758 	command -v b3sum >/dev/null || { msg "'b3sum' not installed."; return 1; }
    759 }
    760 
    761 gencsum() {
    762 	checksource
    763 	b3sumcheck || return 0
    764 	rm -f .checksum
    765 	printcsum > .checksum
    766 	msg "Checksum file updated."
    767 }
    768 
    769 printcsum() {
    770 	for printcsum in $(printsource); do
    771 		[ -f "$printcsum" ] || continue
    772 		b3sum $printcsum | sed "s,$APKG_SOURCE_DIR/,,;s,$HERE/,,"
    773 	done | sort -k 2
    774 }
    775 
    776 prompt_user() {
    777 	if [ ! "$APKG_NOPROMPT" ]; then
    778 		msg "Press ENTER to continue operation."
    779 		msg "Press Ctrl + C to abort."
    780 		read -r null
    781 	fi
    782 }
    783 
    784 runscript() {
    785 	if [ -x ./${1}install ]; then
    786 		msg "Running ${1}install script..."
    787 		if [ "$APKG_ROOT" ]; then
    788 			cat ./${1}install > "$APKG_ROOT"/.runscript
    789 			chmod +x "$APKG_ROOT"/.runscript
    790 			chroot "$APKG_ROOT" /.runscript
    791 			rm -f "$APKG_ROOT"/.runscript
    792 		else
    793 			./${1}install
    794 		fi
    795 	fi
    796 }
    797 
    798 needroot() {
    799 	[ $(id -u) = 0 ] || {
    800 		msg 'This operation need root access'
    801 		exit 1
    802 	}
    803 }
    804 
    805 parsesubopt() {
    806 	# loop opts until found '-*'
    807 	shift; while [ "$1" ]; do
    808 		case $1 in
    809 			-*) break;;
    810 			*) printf "%s\n" "$1";;
    811 		esac
    812 		shift
    813 	done
    814 }
    815 
    816 updateopts() {
    817 	while [ "$1" ]; do
    818 		case $1 in
    819 			-*) newopt="$newopt $1";;
    820 		esac
    821 		shift
    822 	done
    823 	printf "%s\n" "$newopt"
    824 }
    825 
    826 parseopts() {
    827 	while [ "$1" ]; do
    828 		case $1 in
    829 			-I) pkg_depinstalll "$(parsesubopt "$@")"; exit 0;;
    830 			-D) pkg_deplist "$(parsesubopt "$@")"; exit 0;;
    831 			-r) pkg_remove "$(parsesubopt "$@")"; exit 0;;
    832 			-p) pkg_path "$(parsesubopt "$@")"; exit 0;;
    833 			-t) pkg_trigger "$(parsesubopt "$@")"; exit 0;;
    834 			-U) pkg_sysup; exit 0;;
    835 			-d) pkg_depends "$2"; exit 0;;
    836 			-s) search=1;;
    837 			-S) pkg_fsearch "$2"; exit 0;;
    838 			-j) pkg_dependents "$2"; exit 0;;
    839 			-a) allinstalled=1;;
    840 			-h) apkg_help; exit 0;;
    841 			-l) pkg_outdate; exit 0;;
    842 			-g) updatecsum=1;;
    843 			-k) updatefiles=1;;
    844 			-f) forcerebuild=1;;
    845 			-i) install=1;;
    846 			-u) upgrade=1;;
    847 			-o) downloadonly=1;;
    848 			-v) verbose=1;;
    849 			-*) msg "invalid option '$1'"; exit 1;;
    850 			*) pkg="$pkg $1";;
    851 		esac
    852 		shift
    853 	done
    854 }
    855 
    856 apkg_help() {
    857 	cat << EOF
    858 usage: ${0##*/} <option> <arg(s)>
    859 
    860 options:
    861   -i <pkg(s)>  install package(s)
    862   -I <pkg(s)>  install packages(s) with dependencies
    863   -d <pkg>     list <pkg> dependencies
    864   -D <pkg(s)>  list all dependencies
    865   -j <pkg>     list all dependents
    866   -u <pkg(s)>  upgrade package(s)
    867   -r <pkg(s)>  remove package(s)
    868   -t [pkg(s)]  trigger system cache/db updates
    869   -U           update system
    870   -f           force rebuild
    871   -o <pkg(s)>  download source
    872   -p <pkg>     print package path
    873   -g <pkg>     update package checksum
    874   -k <pkg>     update package files
    875   -l           list outdated packages
    876   -s <pattern> search packages
    877   -S <pattern> search files
    878   -v           be verbose
    879   -h           print this help message
    880   
    881 environment variables:
    882   APKG_ROOT          override default root (${APKG_ROOT:-/}) location
    883   APKG_CONF          override default $APKG_CONF location
    884   APKG_NOPROMPT      set the variable for non-interactive use
    885   APKG_LOG           set the variable for logging the output to a file
    886   
    887 EOF
    888 exit 0
    889 }
    890 
    891 main() {
    892 	parseopts "$@"
    893 	set -- $(updateopts "$@")
    894 	
    895 	if [ "$search" ]; then
    896 		pkg_search $@
    897 		exit 0
    898 	fi
    899 	
    900 	if [ "$allinstalled" ]; then
    901 		pkg_allinstalled $@
    902 		exit 0
    903 	fi
    904 	
    905 	for d in $APKG_PACKAGE_DIR $APKG_SOURCE_DIR $APKG_WORK_DIR $APKG_LOG_DIR; do
    906 		[ -d "$d" ] || { msg "Directory '$d' not exist"; exit 1; }
    907 		[ -w "$d" ] || { msg "Directory '$d' dont have write access"; exit 1; }
    908 	done
    909 	
    910 	if [ "$pkg" ]; then
    911 		for p in $pkg; do
    912 			[ "$(pkg_path "$p")" ] || {
    913 				msg "Package '$p' not found"
    914 				continue
    915 			}
    916 			(cd "$(pkg_path "$p")" && $APKG "$@") || exit 1
    917 		done
    918 		exit 0
    919 	fi
    920 
    921 	if [ ! -f ./abuild ]; then
    922 		msg "'abuild' not found."
    923 		exit 1
    924 	fi
    925 	
    926 	. ./abuild
    927 	[ "$name" ]    || die "name is empty"
    928 	[ "$version" ] || die "version is empty"
    929 	[ "$release" ] || die "release is empty"
    930 	[ "$name" = "${PWD##*/}" ] || {
    931 		msg "'name' should be same as package directory name (${PWD##*/})."
    932 		exit 1
    933 	}
    934 	
    935 	logfile="$APKG_LOG_DIR"/$name.log
    936 	packagefile="$APKG_PACKAGE_DIR"/$name#$version-$release.spm
    937 
    938 	if [ "$install" ] && [ -s "$SPM_PKGDB/${PWD##*/}" ]; then
    939 		msg "Package '${PWD##*/}' already installed."
    940 		exit 0
    941 	fi
    942 	
    943 	if [ "$upgrade" ] && [ ! -s "$SPM_PKGDB/${PWD##*/}" ]; then
    944 		msg "Package '${PWD##*/}' not installed."
    945 		exit 0
    946 	fi
    947 	
    948 	if [ "$updatecsum" ]; then
    949 		gencsum
    950 		exit 0
    951 	fi
    952 	
    953 	if [ "$downloadonly" ]; then
    954 		fetch_src
    955 		exit 0
    956 	fi
    957 	
    958 	if [ "$updatefiles" ]; then
    959 		pkg_updatefiles
    960 		exit 0
    961 	fi
    962 	
    963 	if [ "$(id -u)" != 0 ]; then
    964 		msg "Packages need to build as root. Aborted."
    965 		exit 1
    966 	fi
    967 	
    968 	if [ -f "$packagefile" ] && [ ! "$forcerebuild" ]; then
    969 		if [ ! "$install" ] && [ ! "$upgrade" ]; then
    970 			msg "Package '$packagefile' found."
    971 		fi
    972 	else	
    973 		if [ "$install" ] || [ "$upgrade" ]; then
    974 			# run preinstall script before build
    975 			runscript pre
    976 		fi
    977 		fetch_src
    978 		checksum_src
    979 		extract_src
    980 		[ "$APKG_LOG" ] && {
    981 			build_src 2>&1 | tee -- "$logfile"
    982 		} || {
    983 			build_src
    984 		}
    985 	fi	
    986 	
    987 	if [ "$install" ]; then
    988 		SPM_ROOT=${APKG_ROOT%/} spm -i "$packagefile" || exit $?
    989 	elif [ "$upgrade" ]; then
    990 		SPM_ROOT=${APKG_ROOT%/} spm -u "$packagefile" || exit $?
    991 	fi
    992 	
    993 	if [ "$install" ] || [ "$upgrade" ]; then
    994 		# run postinstall script after installed/upgraded
    995 		runscript post
    996 		[ "$APKG_ROOT" ] || pkg_trigger "$name"
    997 	fi
    998 }
    999 
   1000 umask 022
   1001 
   1002 export HERE="$PWD"
   1003 
   1004 APKG=$0
   1005 SPM_PKGDB="${APKG_ROOT%/}/var/lib/spm/db"
   1006 
   1007 # for apkg config, either override or default
   1008 APKG_CONF="${APKG_CONF:-/etc/apkg.conf}"
   1009 
   1010 ## NEED BETTER WAY TO DO THIS?
   1011 # environment override
   1012 O_APKG_REPO=$APKG_REPO
   1013 O_APKG_PACKAGE_DIR=$APKG_PACKAGE_DIR
   1014 O_APKG_SOURCE_DIR=$APKG_SOURCE_DIR
   1015 O_APKG_WORK_DIR=$APKG_WORK_DIR
   1016 O_APKG_LOG_DIR=$APKG_LOG_DIR
   1017 
   1018 # default value
   1019 APKG_REPO="$PWD"
   1020 APKG_PACKAGE_DIR="$PWD"
   1021 APKG_SOURCE_DIR="$PWD"
   1022 APKG_WORK_DIR="$PWD"
   1023 APKG_LOG_DIR="$PWD"
   1024 
   1025 checksum_file="$HERE/.checksum"
   1026 
   1027 # source config
   1028 if [ -f "$APKG_CONF" ]; then
   1029 	. "$APKG_CONF"
   1030 fi
   1031 
   1032 # reset value, use override first, else either from config or use default
   1033 APKG_REPO=${O_APKG_REPO:-$APKG_REPO}
   1034 APKG_PACKAGE_DIR=${O_APKG_PACKAGE_DIR:-$APKG_PACKAGE_DIR}
   1035 APKG_SOURCE_DIR=${O_APKG_SOURCE_DIR:-$APKG_SOURCE_DIR}
   1036 APKG_WORK_DIR=${O_APKG_WORK_DIR:-$APKG_WORK_DIR}
   1037 APKG_LOG_DIR=${O_APKG_LOG_DIR:-$APKG_LOG_DIR}
   1038 
   1039 main "$@"
   1040 
   1041 exit 0