#!/bin/sh # # scratchpkg # # Copyright (c) 2018 by Emmett1 (emmett1.2miligrams@gmail.com) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # RED='\033[31m' GREEN='\033[32m' YELLOW='\033[33m' CYAN='\033[36m' PURPLE='\033[35m' CRESET='\033[0m' nocolor() { RED= GREEN= YELLOW= CYAN= PURPLE= CRESET= } msg() { printf "${GREEN}==>${CRESET} %s\n" "$1" } msginst() { printf "[${GREEN}i${CRESET}] %s\n" "$1" } msgmiss() { printf "[${YELLOW}m${CRESET}] %s\n" "$1" } msgnoinst() { printf "[-] %s\n" "$1" } msgerr() { printf "${RED}ERROR:${CRESET} %s\n" "$1" >&2 } msgwarn() { printf "${YELLOW}WARNING:${CRESET} %s\n" "$1" >&2 } msg_portnotfound() { echo "Port '$1' not found." } msg_portnotinstalled() { echo "Port '$1' not installed." } msg_portalreadyinstalled() { echo "Port '$1' already installed." } msg_depsstatus() { for i in $@; do if scratch_isinstalled $i; then msginst "$i" elif getportpath $i >/dev/null; then msgnoinst "$i" else msgmiss "$i" fi done unset i } needroot() { if [ "$(id -u)" != 0 ]; then if [ "$#" -eq 0 ]; then needroot "This operation" else echo "$* need root access!" fi exit 1 fi } getportpath() { for repo in $PORT_REPO; do if [ -f "$repo/$1/$BUILD_SCRIPT" ]; then dirname "$repo/$1/$BUILD_SCRIPT" return 0 fi done return 1 } vercomp() { if [ "$1" = "$2" ]; then return 0 # same version elif [ "$1" = "$(echo "$1\n$2" | sort -V | head -n1)" ]; then return 1 # $1 lower than $2 else return 2 # $1 higher than $2 fi } get_iversion() { head -n1 $PKGDB_DIR/$1 2>/dev/null | awk '{print $1}' } get_irelease() { head -n1 $PKGDB_DIR/$1 2>/dev/null | awk '{print $2}' } allinstalled() { for i in $PKGDB_DIR/*; do echo ${i##*/} done } deps_alias() { [ -f "$ALIAS_FILE" ] || { echo $@ return } while [ "$1" ]; do if [ "$(grep -w ^$1 $ALIAS_FILE)" ]; then getalias=$(grep -w ^$1 $ALIAS_FILE | awk '{print $2}') [ "$getalias" ] && echo "$getalias" else echo "$1" fi shift unset getalias done } get_depends() { ppath=$(getportpath $1) || return 0 deps=$(grep "^# depends[[:blank:]]*:" $ppath/$BUILD_SCRIPT \ | sed 's/^# depends[[:blank:]]*:[[:blank:]]*//' \ | tr ' ' '\n' \ | awk '!a[$0]++' \ | sed 's/,//') deps_alias $deps } confirm() { printf "$1 (Y/n) " read -r response case "$response" in [Nn][Oo]|[Nn]) echo "$2"; return 2 ;; *) : ;; esac return 0 } checktool() { if ! command -v $1 >/dev/null; then msgerr "'$1' not exist in your system!" exit 1 fi } needarg() { [ "$*" ] || { echo "This operation required an arguments!" exit 1 } } settermtitle() { printf "\033]0;$*\a" } scratch_integrity() { if [ "$1" ]; then cd / if [ -f $PKGDB_DIR/$1 ]; then tail -n+2 $PKGDB_DIR/$1 | while read -r line; do if [ ! -e "$line" ]; then if [ -L "$line" ]; then printf "${YELLOW}broken symlink${CRESET} $1: /$line" else printf "${RED}file missing${CRESET} $1: /$line" fi fi done else msg_portnotinstalled $1 exit 1 fi cd - >/dev/null else cd / for pkg in $(allinstalled); do tail -n+2 $PKGDB_DIR/$pkg | while read -r line; do if [ ! -e "$line" ]; then if [ -L "$line" ]; then echo "broken symlink $pkg: /$line" else echo "missing file $pkg: /$line" fi fi done done cd - >/dev/null fi } scratch_locate() { needarg $@ for repo in $PORT_REPO; do grep -Ri $@ $repo/*/.pkgfiles 2>/dev/null | sed 's/:/ /;s/\/\.pkgfiles//' | awk '{print $1,$4}' done } scratch_sync() { portsync } cvperms() { # converts symbolic to numeric permissions # required an input (symbolic, eg: drwxr-xr-x) s=0; n=0; count=0 for i in $(echo "$1" | sed -e 's/\(.\)/\1\n/g'); do count=$((count+1)) case $i in d) ;; r) n=$((n+4));; w) n=$((n+2));; x) n=$((n+1));; s) [ $count = 4 ] && s=$((s+4)) || s=$((s+2)); n=$((n+1));; t) s=$((s+1));n=$((n+1));; S) s=$((s+2));; T) s=$((s+1));; esac [ "$count" = 4 ] && { user=$n; n=0 } [ "$count" = 7 ] && { group=$n; n=0 } [ "$count" = 10 ] && { other=$n; n=0 } done echo "$s$user$group$other" } fixperms() { needroot "Fix permissions" for i in $PKGDBPERMS_DIR/*; do [ -s $i ] || continue while read -r perms own dir junk; do chmod $(cvperms $perms) /$dir echo $own | while IFS=/ read -r o g; do chown $o:$g /$dir done done < $i done } scratch_trigger() { needroot "Run trigger" if [ -z "$*" ]; then for i in $(seq 12); do eval trig_$i=1 done else pre_triggers $@ fi post_triggers } post_triggers() { if [ "$trig_11" = 1 ] && [ $(command -v fc-cache) ]; then echo "trigger: Updating fontconfig cache..." fc-cache -s fi if [ "$trig_10" = 1 ] && [ $(command -v gdk-pixbuf-query-loaders) ]; then echo "trigger: Probing GDK-Pixbuf loader modules..." gdk-pixbuf-query-loaders --update-cache fi if [ "$trig_9" = 1 ] && [ $(command -v gio-querymodules) ]; then echo "trigger: Updating GIO module cache..." gio-querymodules /usr/lib/gio/modules fi if [ "$trig_8" = 1 ] && [ $(command -v glib-compile-schemas) ]; then echo "trigger: Compiling GSettings XML schema files..." glib-compile-schemas /usr/share/glib-2.0/schemas fi if [ "$trig_7" = 1 ] && [ $(command -v gtk-query-immodules-2.0) ]; then echo "trigger: Probing GTK2 input method modules..." gtk-query-immodules-2.0 --update-cache fi if [ "$trig_6" = 1 ] && [ $(command -v gtk-query-immodules-3.0) ]; then echo "trigger: Probing GTK3 input method modules..." gtk-query-immodules-3.0 --update-cache fi if [ "$trig_5" = 1 ] && [ $(command -v gtk-update-icon-cache) ]; then echo "trigger: 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 fi if [ "$trig_4" = 1 ] && [ $(command -v udevadm) ]; then echo "trigger: Updating hardware database..." udevadm hwdb --update fi if [ "$trig_3" = 1 ] && [ $(command -v mkfontdir) ] && [ $(command -v mkfontscale) ]; then echo "trigger: 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 fi if [ "$trig_2" = 1 ] && [ $(command -v update-desktop-database) ]; then echo "trigger: Updating desktop file MIME type cache..." update-desktop-database --quiet fi if [ "$trig_1" = 1 ] && [ $(command -v update-mime-database) ]; then echo "trigger: Updating the MIME type database..." update-mime-database /usr/share/mime fi fixperms } pre_triggers() { # mime db if [ "$trig_1" != "1" ]; then for pkg in $@; do if [ -s "$PKGDB_DIR/$pkg" ] && [ "$(grep ^usr/share/mime/$ $PKGDB_DIR/$pkg)" ]; then trig_1=1 break fi done fi # desktop db if [ "$trig_2" != "1" ]; then for pkg in $@; do if [ -s "$PKGDB_DIR/$pkg" ] && [ "$(grep ^usr/share/applications/$ $PKGDB_DIR/$pkg)" ]; then trig_2=1 break fi done fi # mkfontdir if [ "$trig_3" != "1" ]; then for pkg in $@; do if [ -s "$PKGDB_DIR/$pkg" ] && [ "$(grep ^usr/share/fonts/$ $PKGDB_DIR/$pkg)" ]; then trig_3=1 break fi done fi # hwdb if [ "$trig_4" != "1" ]; then for pkg in $@; do if [ -s "$PKGDB_DIR/$pkg" ] && [ "$(grep ^etc/udev/hwdb.d/$ $PKGDB_DIR/$pkg)" ]; then trig_4=1 break fi done fi # icon caches if [ "$trig_5" != "1" ]; then for pkg in $@; do if [ -s "$PKGDB_DIR/$pkg" ] && [ "$(grep ^usr/share/icons/$ $PKGDB_DIR/$pkg)" ]; then trig_5=1 break fi done fi # gtk3 immodules if [ "$trig_6" != "1" ]; then for pkg in $@; do if [ -s "$PKGDB_DIR/$pkg" ] && [ "$(grep ^usr/lib/gtk-3.0/3.0.0/immodules/.*.so $PKGDB_DIR/$pkg)" ]; then trig_6=1 break fi done fi # gtk2 immodules if [ "$trig_7" != "1" ]; then for pkg in $@; do if [ -s "$PKGDB_DIR/$pkg" ] && [ "$(grep ^usr/lib/gtk-2.0/2.10.0/immodules/.*.so $PKGDB_DIR/$pkg)" ]; then trig_7=1 break fi done fi # gsettings schema if [ "$trig_8" != "1" ]; then for pkg in $@; do if [ -s "$PKGDB_DIR/$pkg" ] && [ "$(grep ^usr/share/glib-2.0/schemas/$ $PKGDB_DIR/$pkg)" ]; then trig_8=1 break fi done fi # gio modules if [ "$trig_9" != "1" ]; then for pkg in $@; do if [ -s "$PKGDB_DIR/$pkg" ] && [ "$(grep ^usr/lib/gio/modules/.*.so $PKGDB_DIR/$pkg)" ]; then trig_9=1 break fi done fi # gdk-pixbuf if [ "$trig_10" != "1" ]; then for pkg in $@; do if [ -s "$PKGDB_DIR/$pkg" ] && [ "$(grep ^usr/lib/gdk-pixbuf-2.0/2.10.0/loaders/.*.so $PKGDB_DIR/$pkg)" ]; then trig_10=1 break fi done fi # font caches if [ "$trig_11" != "1" ]; then for pkg in $@; do if [ -s "$PKGDB_DIR/$pkg" ] && [ "$(grep ^usr/share/fonts/$ $PKGDB_DIR/$pkg)" ]; then trig_11=1 break fi done fi } scratch_build() { while [ "$1" ]; do case $1 in -i|-u|-r|-g|-p) ;; --log) LOG=1;; -*) OPTS="$OPTS $1";; *) PKGNAME="$PKGNAME $1";; esac shift done [ "$PKGNAME" ] || { echo "Please specify package(s) to build." return 1 } for pkg in $PKGNAME; do ppath=$(getportpath $pkg) || { msg_portnotfound $pkg return 1 } cd $ppath settermtitle "Building $pkg..." if [ "$LOG" ]; then pkgbuild $OPTS | tee /var/log/pkgbuild.log || { settermtitle "Building $pkg failed." return 1 } else pkgbuild $OPTS || { settermtitle "Building $pkg failed." return 1 } fi settermtitle "Building $pkg done." cd - >/dev/null done } scratch_install() { needroot "Installing package" while [ "$1" ]; do case $1 in -i|-u) ;; -r) REINSTALL=1;; -o) DOWNLOAD_ONLY=1; OPTS="$OPTS $1";; -y) NOCONFIRM=1;; -n) NO_DEP=1;; --exclude=*) EXOPT=$1;; -*) OPTS="$OPTS $1";; *) PKGNAME="$PKGNAME $1";; esac shift done [ "$PKGNAME" ] || { echo "Please specify package(s) to install." return 1 } # if reinstall, dont calculate dep, just reinstall it then exit if [ "$REINSTALL" = 1 ]; then error=0 for ii in $PKGNAME; do if [ ! $(getportpath $ii) ]; then msg_portnotfound $ii elif ! scratch_isinstalled $ii; then msg_portnotinstalled $ii else cd $(getportpath $ii) settermtitle "Reinstalling $ii..." pkgbuild $OPTS -r || { error=1 break } done_pkg="$done_pkg $ii" cd - >/dev/null fi done run_trigger settermtitle "Package(s) reinstalled." return "$error" fi if [ "$NO_DEP" = 1 ]; then error=0 for ii in $PKGNAME; do if [ ! $(getportpath $ii) ]; then msg_portnotfound $ii elif scratch_isinstalled $ii; then msg_portalreadyinstalled $ii continue else cd $(getportpath $ii) settermtitle "Installing $ii..." pkgbuild -i $OPTS || { error=1 break } done_pkg="$done_pkg $ii" world_add $ii cd - >/dev/null fi done run_trigger settermtitle "Package(s) installed." return "$error" fi for i in $PKGNAME; do if [ ! $(getportpath $i) ]; then msg_portnotfound $i elif scratch_isinstalled $i; then msg_portalreadyinstalled $i else IPKG="$IPKG $i" fi done [ "$IPKG" ] || return 0 echo "Resolving dependencies..." INST="$(scratch_deplist -q $IPKG $EXOPT)" if [ "$INST" ]; then echo pkgcount=0 installmsg="ACTION#PORTNAME#VERSION" for pkg in $INST; do pkgcount=$(( pkgcount + 1 )) installmsg="$installmsg install#$pkg#$(get_version $pkg)-$(get_release $pkg)" done echo $installmsg | tr ' ' '\n' | tr '#' ' ' | column -t echo echo "( $pkgcount install )" echo if [ ! "$NOCONFIRM" ]; then confirm "Continue install package(s)?" "Package installation cancelled." || exit $? echo fi error=0 count=0 total=$(echo $INST | wc -w) for int in $INST; do count=$(( count + 1 )) if portpathh=$(getportpath $int); then cd $portpathh settermtitle "[ $count/$total ] installing $int..." run_preinstallsh pkgbuild -i $OPTS || { error=1 count=$(( count - 1 )) break } if [ $(echo $IPKG | tr ' ' '\n' | grep -x $int) ]; then world_add $int fi run_postinstallsh done_pkg="$done_pkg $int" cd - >/dev/null else msgwarn "Skipping missing package: $int" fi unset portpathh done run_trigger settermtitle "$count/$total package(s) installed." return "$error" fi } scratch_remove() { needroot "Removing package" while [ "$1" ]; do case $1 in -y|--yes) NOCONFIRM=1;; -*) OPTS="$OPTS $1";; *) PKGNAME="$PKGNAME $1";; esac shift done [ "$PKGNAME" ] || { echo "Please specify package(s) to remove." return 1 } for i in $PKGNAME; do if ! scratch_isinstalled $i; then msg_portnotinstalled $i else IPKG="$IPKG $i" fi done [ "$IPKG" ] || return 0 echo "Removing packages..." echo pkgcount=0 count=0 installmsg="ACTION#PORTNAME#VERSION" for pkg in $IPKG; do pkgcount=$(( pkgcount + 1 )) installmsg="$installmsg remove#$pkg#$(get_iversion $pkg)-$(get_irelease $pkg)" done echo $installmsg | tr ' ' '\n' | tr '#' ' ' | column -t echo echo "( $pkgcount remove )" echo [ "$NOCONFIRM" ] || { confirm "Continue remove package(s)?" "Package removing cancelled." || exit $? echo } for pkg in $IPKG; do count=$(( count + 1 )) pre_triggers $pkg settermtitle "[ $count/$pkgcount ] Removing $pkg..." echo "remove: $pkg-$(get_iversion $pkg)-$(get_irelease $pkg)..." pkgdel $pkg $OPTS || { error=1 break } world_del $pkg done settermtitle "Triggering remove hook..." post_triggers settermtitle "$pkgcount package(s) removed." } scratch_sysup() { needroot "Upgrading package" while [ "$1" ]; do case $1 in -i|-u|-r) ;; -o) DOWNLOAD_ONLY=1; OPTS="$OPTS $1";; -y) NOCONFIRM=1;; -n) NODEP=1;; --exclude=*) EXOPT=$1;; -*) OPTS="$OPTS $1";; esac shift done echo "Checking for outdated packages..." PKGOUTDATE=$(check_outdated) # filter out masked for i in $PKGOUTDATE; do case $i in *#1) continue;; *) ii="$ii ${i%%#*}";; esac done EXPKG=${EXOPT#*=} for i in $ii; do echo $EXPKG | tr ',' '\n' | grep -x $i >/dev/null || PKG="$PKG $i" done [ "$PKG" ] || { echo "All packages are up to date." return 0 } [ "$(echo $PKG | grep -x scratchpkg)" ] && { echo msgerr "Please upgrade 'scratchpkg' first." return 1 } UPGPKG=0 NEWPKG=0 installmsg="ACTION#PORTNAME#VERSION#OLDVERSION" if [ "$NODEP" != 1 ]; then echo "Resolving dependencies..." DEP=$(scratch_deplist $PKG $EXOPT | awk '{print $2}') echo for d in $DEP; do if [ "$(echo $PKG | tr ' ' '\n' | grep -x $d)" = "$d" ]; then installmsg="$installmsg upgrade#$d#$(get_version $d)-$(get_release $d)#$(get_iversion $d)-$(get_irelease $d)" WILLINSTALL="$WILLINSTALL $d" UPGPKG=$(( UPGPKG + 1 )) elif ! scratch_isinstalled $d && [ "$(getportpath "$d")" ]; then installmsg="$installmsg install#$d#$(get_version $d)-$(get_release $d)#$(get_iversion $d)-$(get_irelease $d)" WILLINSTALL="$WILLINSTALL $d" NEWPKG=$(( NEWPKG + 1 )) fi done else echo for dd in $PKG; do printf "[${GREEN}u${CRESET}] $dd " WILLINSTALL="$WILLINSTALL $dd" UPGPKG=$(( UPGPKG + 1 )) done fi echo $installmsg | tr ' ' '\n' | tr '#' ' ' | column -t echo echo "( $UPGPKG upgrade, $NEWPKG new install )" echo [ "$NOCONFIRM" ] || { confirm "Continue upgrade/install these package(s)?" "Package upgrade cancelled." || exit $? echo } error=0 count=0 total=$(echo $WILLINSTALL | wc -w) for inst in $WILLINSTALL; do # install all required dependencies and target packages itself count=$(( count + 1 )) cd $(getportpath $inst) if ! scratch_isinstalled $inst; then settermtitle "[ $count/$total ] Installing $inst..." run_preinstallsh pkgbuild -i $OPTS || { error=1 count=$(( count - 1 )) break } run_postinstallsh else settermtitle "[ $count/$total ] Upgrading $inst..." run_preinstallsh pkgbuild -u $OPTS || { error=1 count=$(( count - 1 )) break } run_postinstallsh fi cd - >/dev/null done_pkg="$done_pkg $inst" done run_trigger settermtitle "$count/$total package(s) upgraded." return "$error" } run_trigger() { # if using -o (download only), dont run trigger if [ ! "$DOWNLOAD_ONLY" ]; then [ "$done_pkg" ] && { settermtitle "Triggering install hook..." scratch_trigger $done_pkg } fi } run_preinstallsh() { # if using -o (download only), dont run pre-install.sh script if [ ! "$DOWNLOAD_ONLY" ]; then [ -f ./pre-install.sh ] && sh ./pre-install.sh fi } run_postinstallsh() { # if using -o (download only), dont run post-install.sh script if [ ! "$DOWNLOAD_ONLY" ]; then [ -f ./post-install.sh ] && sh ./post-install.sh fi } scratch_upgrade() { needroot "Upgrading package" while [ "$1" ]; do case $1 in -i|-r) ;; -y) NOCONFIRM=1;; -o) DOWNLOAD_ONLY=1; OPTS="$OPTS $1";; -d) NO_DEP=1;; --exclude=*) EXOPT=$1;; -*) OPTS="$OPTS $1";; *) PKGNAME="$PKGNAME $1";; esac shift done [ "$PKGNAME" ] || { echo "Please specify package(s) to upgrade." return 1 } for pkg in $PKGNAME; do if ! scratch_isinstalled $pkg; then msg_portnotinstalled $pkg continue elif [ ! $(getportpath $pkg) ]; then echo "Package '$pkg' not exist." continue else . $(getportpath $pkg)/$BUILD_SCRIPT if [ "$(get_iversion $pkg)-$(get_irelease $pkg)" = "$version-$release" ]; then echo "Package '$pkg' is up to date." continue fi fi upkg="$upkg $pkg" done [ "$upkg" ] || return 0 UPGPKG=0 NEWPKG=0 installmsg="ACTION#PORTNAME#VERSION#OLDVERSION" if [ "$NODEP" != 1 ]; then echo "Resolving dependencies..." DEP=$(scratch_deplist $upkg $EXOPT | awk '{print $2}') echo for d in $DEP; do if [ "$(echo $upkg | tr ' ' '\n' | grep -x $d)" = "$d" ]; then installmsg="$installmsg upgrade#$d#$(get_version $d)-$(get_release $d)#$(get_iversion $d)-$(get_irelease $d)" WILLINSTALL="$WILLINSTALL $d" UPGPKG=$(( UPGPKG + 1 )) elif ! scratch_isinstalled $d && [ "$(getportpath "$d")" ]; then installmsg="$installmsg install#$d#$(get_version $d)-$(get_release $d)#$(get_iversion $d)-$(get_irelease $d)" WILLINSTALL="$WILLINSTALL $d" NEWPKG=$(( NEWPKG + 1 )) fi done else echo for dd in $upkg; do installmsg="$installmsg upgrade#$d#$(get_version $d)-$(get_release $d)#$(get_iversion $d)-$(get_irelease $d)" WILLINSTALL="$WILLINSTALL $dd" UPGPKG=$(( UPGPKG + 1 )) done fi echo $installmsg | tr ' ' '\n' | tr '#' ' ' | column -t echo echo "( $UPGPKG upgrade, $NEWPKG new install )" echo [ "$NOCONFIRM" ] || { confirm "Continue upgrade/install these package(s)?" "Package upgrade cancelled." || exit $? echo } error=0 count=0 total=$(echo $WILLINSTALL | wc -w) for inst in $WILLINSTALL; do # install all required dependencies and target packages itself count=$(( count + 1 )) cd $(getportpath $inst) if ! scratch_isinstalled $inst; then settermtitle "[ $count/$total ] Installing $inst..." run_preinstallsh pkgbuild -i $OPTS || { error=1 count=$(( count - 1 )) break } run_postinstallsh else settermtitle "[ $count/$total ] Upgrading $inst..." run_preinstallsh pkgbuild -u $OPTS || { error=1 count=$(( count - 1 )) break } run_postinstallsh fi cd - >/dev/null done_pkg="$done_pkg $inst" done run_trigger settermtitle "$count/$total package(s) upgraded." return "$error" } scratch_isinstalled() { [ "$1" ] || return 1 [ -f "$PKGDB_DIR/$1" ] || return 1 return 0 } get_version() { p=$(getportpath $1) [ "$p" ] && . $p/$BUILD_SCRIPT || version=0 echo $version } get_release() { p=$(getportpath $1) [ "$p" ] && . $p/$BUILD_SCRIPT || release=0 echo $release } check_outdated() { for pkg in $(allinstalled); do NEWINSTALLED=0 MASKED=0 p=$(getportpath $pkg) [ "$p" ] || continue . $p/$BUILD_SCRIPT if [ ! "$name" ] || [ ! "$version" ]; then continue fi iversion=$(get_iversion $pkg) irelease=$(get_irelease $pkg) [ $version$release = $iversion$irelease ] && continue if [ "$version" != "$iversion" ]; then vercomp $version $iversion [ $? = 1 ] && NEWINSTALLED="1" elif [ "$release" != "$irelease" ]; then vercomp $release $irelease [ $? = 1 ] && NEWINSTALLED="1" fi [ "$(grep -Ev '^(#|$| )' $MASK_FILE | grep -x $pkg)" ] && MASKED="1" echo "$pkg#$iversion-$irelease#$version-$release#$NEWINSTALLED#$MASKED" done } scratch_outdate() { for i in $(check_outdated); do OUTDATE=1 unset ii pname=${i%%#*}; i=${i#*#} oldver=${i%%#*}; i=${i#*#} newver=${i%%#*}; i=${i#*#} newins=${i%%#*}; i=${i#*#} masked=${i%%#*}; i=${i#*#} [ "$newins" = 1 ] && ii=" [newer installed]" [ "$masked" = 1 ] && ii="$ii [masked]" echo "$pname $oldver => $newver$ii" done [ ! "$OUTDATE" ] && msg "All packages are up to date." } scratch_search() { needarg $@ arg=$* for repo in $PORT_REPO; do [ -d $repo ] || continue dummyport=/tmp/dummyport/$BUILD_SCRIPT mkdir -p ${dummyport%/*} touch $dummyport out=$(grep -R "# description" $repo/*/$BUILD_SCRIPT $dummyport | grep -i "$arg" | awk -F : '{print $1}' | sort) rm -fr $dummyport [ "$out" ] || continue found=1 for line in $out; do repo=$(echo $line | rev | awk -F / '{print $3}' | rev) desc=$(grep "^# description[[:blank:]]*:" $line | sed 's/^# description[[:blank:]]*:[[:blank:]]*//') . $line if scratch_isinstalled $name; then ins="[${GREEN}*${CRESET}]" else ins="[ ]" fi printf "$ins ${PURPLE}($repo)${CRESET} $name ${CYAN}$version-$release${CRESET}: $desc\n" unset repo desc name version release build done unset out done if [ ! "$found" ]; then msg "No matching package found." fi } scratch_cache() { needroot "Clear old caches" allcachepkg=/tmp/.allcachepkg.$$ allcachesrc=/tmp/.allcachesrc.$$ keepcachepkg=/tmp/.keepcachepkg.$$ keepcachesrc=/tmp/.keepcachesrc.$$ diffcachepkg=/tmp/.diffcachepkg.$$ diffcachesrc=/tmp/.diffcachesrc.$$ touch \ $allcachepkg \ $allcachesrc \ $keepcachepkg \ $keepcachesrc \ $diffcachepkg \ $diffcachesrc if [ "$(find $PACKAGE_DIR -mindepth 1 -print -quit 2>/dev/null)" ]; then for list in "$PACKAGE_DIR"/*; do basename $list >> "$allcachepkg" done fi if [ "$(find $SOURCE_DIR -mindepth 1 -print -quit 2>/dev/null)" ]; then for list in "$SOURCE_DIR"/*; do basename $list >> "$allcachesrc" done fi for repo in $PORT_REPO; do if [ "$(find $repo/*/ -mindepth 1 -print -quit 2>/dev/null)" ]; then # check directory if its not empty for port in $repo/*/$BUILD_SCRIPT; do . $port echo "$name-$version-$release.spkg.tar.$COMPRESSION_MODE" >> "$keepcachepkg" if [ "$source" ]; then for src in $source; do if echo $src | grep -Eq "(ftp|http|https)://"; then if echo $src | grep -Eq "::(ftp|http|https)://"; then sourcename="$(echo $src | awk -F '::' '{print $1}')" else sourcename="$(echo $src | rev | cut -d / -f 1 | rev)" fi echo $sourcename >> "$keepcachesrc" fi done fi done fi done grep -Fxv -f "$keepcachepkg" "$allcachepkg" > "$diffcachepkg" grep -Fxv -f "$keepcachesrc" "$allcachesrc" > "$diffcachesrc" cat $diffcachepkg cat $diffcachesrc if [ -s "$diffcachepkg" ]; then cd "$PACKAGE_DIR" sizepkg=$(du -ch $(cat $diffcachepkg) | grep total | awk '{print $1}') cd - >/dev/null else sizepkg=0M fi if [ -s "$diffcachesrc" ]; then cd "$SOURCE_DIR" sizesrc=$(du -ch $(cat $diffcachesrc) | grep total | awk '{print $1}') cd - >/dev/null else sizesrc=0M fi echo "Total package cache size: $sizepkg" echo "Total source cache size : $sizesrc" if [ -s "$diffcachepkg" ] || [ -s "$diffcachesrc" ]; then echo confirm "Clear old caches?" "Old caches is kept." && { for i in $(cat $diffcachepkg); do [ -e "$PACKAGE_DIR/$i" ] && rm -v "$PACKAGE_DIR/$i" done for i in $(cat $diffcachesrc); do [ -e "$SOURCE_DIR/$i" ] && rm -v "$SOURCE_DIR/$i" done } fi rm -f \ "$allcachepkg" \ "$allcachesrc" \ "$keepcachepkg" \ "$keepcachesrc" \ "$diffcachepkg" \ "$diffcachesrc" } scratch_deplist() { OLDIFS=$IFS IFS=, while [ "$1" ]; do case $1 in -q) quick=1;; --exclude=*) for i in ${1#*=}; do exclude="$exclude $i"; done;; -*) ;; *) PKG="$PKG $1";; esac shift done IFS=$OLDIFS [ "$PKG" ] || { echo "Please specify port(s) to list dependencies." return 1 } for p in $PKG; do if [ "$(getportpath $p)" ]; then PPKG="$PPKG $p" else [ "$quick" = 1 ] || msg_portnotfound $p fi done for p in $PPKG; do deplist $p done [ "$DEP" ] || return 0 if [ "$quick" = 1 ]; then echo $DEP | tr ' ' '\n' else msg_depsstatus $DEP if [ "$MISSINGDEP" ]; then for m in $MISSINGDEP; do echo "Missing deps: $m" | sed 's/(/ (/' done fi fi } deplist() { # skip excluded dependencies if echo $exclude | tr " " "\n" | grep -qx $1; then return 0 fi # check currently process for circular dependencies # for circular dependencies, found first will take precedence [ "$CHECK" ] && { if echo $CHECK | tr " " "\n" | grep -qx $1; then return 0 fi } # add package to currently process CHECK="$CHECK $1" # check dependencies for deplist in $(get_depends $1); do if [ "$quick" = 1 ] && scratch_isinstalled $deplist; then continue else if ! echo $DEP | tr " " "\n" | grep -qx $deplist; then if ! getportpath $deplist >/dev/null; then MISSINGDEP="$MISSINGDEP $deplist($1)" else deplist $deplist fi fi fi done # add dependency to list checked dep if ! echo $DEP | tr " " "\n" | grep -qx $1; then if [ "$quick" = 1 ]; then scratch_isinstalled $1 || DEP="$DEP $1" else DEP="$DEP $1" fi fi # delete item from loop process CHECK=$(echo $CHECK | sed "s/$1//") } scratch_cat() { needarg $@ if PPATH=$(getportpath "$1"); then cat "$PPATH/$BUILD_SCRIPT" else msg_portnotfound $1 return 1 fi } scratch_dependent() { needarg $@ if [ "$(getportpath $1)" ]; then for dpd in $(grep -R "# depends[[:blank:]]*:" $PORT_REPO \ | sed "s,:# depends[[:blank:]]*:[[:blank:]]*,#|,;s, ,|,g;s,$,|,g" \ | grep "|$1|" \ | awk -F "#" '{print $1}' \ | rev \ | awk -F / '{print $2}' \ | rev); do msg_depsstatus $dpd done else msg_portnotfound $1 return 1 fi } scratch_depends() { needarg $@ if getportpath "$1" >/dev/null; then depends=$(get_depends $1) else msg_portnotfound $1 return 1 fi msg_depsstatus $depends } scratch_dup() { dup=$(find $PORT_REPO -type d -print | grep -Exv "($(echo $PORT_REPO | tr ' ' '|'))" | \ rev | cut -d '/' -f1 | rev | sort | uniq -d) if [ "$dup" ]; then for dp in $dup; do for repo in $PORT_REPO; do [ -d $repo/$dp ] && echo "$repo/$dp" done done else msg "No duplicate ports found." fi } scratch_foreign() { for pkg in $(allinstalled); do if ! getportpath $pkg >/dev/null; then iversion=$(get_iversion $pkg) irelease=$(get_irelease $pkg) echo "$pkg $iversion-$irelease" fi unset pkg iversion irelease done } scratch_info() { needarg $@ ppath=$(getportpath $1) || { msg_portnotfound $1 return 1 } . $ppath/$BUILD_SCRIPT desc=$(grep "^# description[[:blank:]]*:" $ppath/$BUILD_SCRIPT | sed 's/^# description[[:blank:]]*:[[:blank:]]*//') maint=$(grep "^# maintainer[[:blank:]]*:" $ppath/$BUILD_SCRIPT | sed 's/^# maintainer[[:blank:]]*:[[:blank:]]*//') homep=$(grep "^# homep[[:blank:]]*:" $ppath/$BUILD_SCRIPT | sed 's/^# homepage[[:blank:]]*:[[:blank:]]*//') deps=$(get_depends $1 | tr '\n' ' ') echo "Name: $1" echo "Path: $ppath" echo "Version: $version" echo "Release: $release" echo "Description: $desc" echo "Maintainer: $maint" echo "Homepage: $homep" echo "Dependencies: $deps" echo "Installed: $(get_iversion $1)-$(get_irelease $1)" } scratch_installed() { for all in $(allinstalled); do echo "$all $(get_iversion $all)-$(get_irelease $all)" done } scratch_missingdep() { for pkg in $(allinstalled); do if getportpath "$pkg" >/dev/null; then depends=$(get_depends $pkg) fi if [ "$depends" ]; then for d in $depends; do if ! scratch_isinstalled $d; then if [ "$msd" ]; then msd="$msd $d" else msd="$d" fi fi done fi [ "$msd" ] && echo "$pkg: $msd" unset depends msd done } scratch_purge() { needroot "Purging package" while [ "$1" ]; do case $1 in -*) PURGEOPTS="$PURGEOPTS $1";; *) PURGENAME="$PURGENAME $1";; esac shift done if [ "$PURGENAME" ]; then for i in $PURGENAME; do scratch_isinstalled $i || { msg_portnotinstalled $i needexit=1 } done fi [ "$needexit" ] && exit 1 echo "Resolving dependencies..." orphan=$(scratch_orphan $PURGENAME) if [ "$PURGENAME" ]; then for purge in $PURGENAME; do unset DEP pkg deplist $purge for ii in $DEP; do echo $orphan | tr ' ' '\n' | grep -qx $ii && pkg="$pkg $ii" done [ "$pkg" ] || echo "Cant purge '$purge', it required by other package(s)..." if [ "$remove" ]; then remove="$remove $pkg" else remove="$pkg" fi done else remove=$orphan fi if [ "$remove" ]; then scratch_remove $remove $PURGEOPTS else echo "No orphan packages found..." fi } scratch_orphan() { tmpdeplistworld="/tmp/.deplistworld.$$" tmpallinstalled="/tmp/.allinstalled.$$" for i in $(cat $WORLD_FILE); do echo $@ | tr ' ' '\n' | grep -qx $i && continue echo $DEP | tr ' ' '\n' | grep -qx $i && continue deplist $i done echo $DEP | tr ' ' '\n' > $tmpdeplistworld allinstalled > $tmpallinstalled grep -xvF -f $tmpdeplistworld $tmpallinstalled rm $tmpallinstalled $tmpdeplistworld } scratch_path() { needarg $@ if PPATH=$(getportpath "$1"); then echo "$PPATH" else msg_portnotfound $1 return 1 fi } scratch_provide() { needarg $@ arg=$(echo $1 | sed "s/^\///") grep -R "$arg" $PKGDB_DIR/* \ | sed "s:$PKGDB_DIR/::" \ | tr : "\t" } scratch_readme() { needarg $@ if PPATH=$(getportpath "$1"); then if [ -f "$PPATH/readme" ]; then cat "$PPATH/readme" else echo "Port '$1' does not have readme." fi else msg_portnotfound $1 exit 1 fi } scratch_printconfig() { echo "MAINOPTS: $MAINOPTS" echo "REPO_FILE: $REPO_FILE" echo "MASK_FILE: $MASK_FILE" echo "CONFIG_FILE: $CONFIG_FILE" echo "CFLAGS: $CFLAGS" echo "CXXFLAGS: $CXXFLAGS" echo "MAKEFLAGS: $MAKEFLAGS" echo "CURL_OPTS: $CURL_OPTS" echo "COMPRESSION_MODE: $COMPRESSION_MODE" echo "NO_STRIP: $NO_STRIP" echo "IGNORE_MDSUM: $IGNORE_MDSUM" echo "KEEP_LIBTOOL: $KEEP_LIBTOOL" echo "KEEP_LOCALE: $KEEP_LOCALE" echo "KEEP_DOC: $KEEP_DOC" echo "SOURCE_DIR: $SOURCE_DIR" echo "PACKAGE_DIR: $PACKAGE_DIR" echo "PORT_REPO:" for i in $PORT_REPO; do echo " $i" done } scratch_files() { needarg $@ if scratch_isinstalled $1; then cat "$PKGDB_DIR/$1" else msg_portnotinstalled $1 fi } world_add() { grep -qx $1 "$WORLD_FILE" && return scratch_isinstalled $1 || { msg_portnotinstalled $1 return 1 } echo "$1" >> "$WORLD_FILE" sort "$WORLD_FILE" -o "$WORLD_FILE" # sort world sed '/^$/d' -i "$WORLD_FILE" # delete empty lines } world_del() { grep -qx $1 "$WORLD_FILE" || return sed "/^$1$/d" -i "$WORLD_FILE" sed '/^$/d' -i "$WORLD_FILE" # delete empty lines } scratch_world() { if [ "$1" ]; then needroot touch "$WORLD_FILE" while [ "$1" ]; do if [ ! $(grep -x $1 "$WORLD_FILE") ] ; then echo "world: '$1' added to world" world_add $1 else echo "world: '$1' deleted from world" world_del $1 fi shift done else [ -s "$WORLD_FILE" ] && { cat "$WORLD_FILE" } || { echo "world is empty" } fi } scratch_help() { cat << EOF Usage: $(basename $0) [] Options: install install ports (use pkgbuild arg, except '-i' & '-u') -r reinstall -n skip dependencies -y skip ask user confirmation -o fetch sources only --exclude=* exclude dependencies, comma separated upgrade upgrade ports (use pkgbuild arg, except '-i' & '-r') -n skip dependencies -y skip ask user confirmation -o fetch sources only --exclude=* exclude dependencies, comma separated remove remove installed ports (use pkgdel arg) -y skip ask user confirmation sysup full system upgrade (use pkgbuild arg, except '-i', '-r' & '-u') -n skip dependencies -y skip ask user confirmation -o fetch sources only --exclude=* exclude dependencies, comma separated deplist print all dependencies for ports -q skip installed ports --exclude=* exclude dependencies, comma separated build build ports (use pkgbuild arg, except '-i', '-u', '-r', '-g', & '-p') --log log build process (/var/log/pkgbuild.log) trigger [ports] run system trigger search find ports in repo cat print spkgbuild depends print dependencies dependent print dependent path print path in repo provide print port's provided file readme print readme file, if exist files print files installed info print information locate print location of file in ports repo isinstalled check whether port is installed (status 0=installed, 1=not installed) purge [ports] remove installed ports and its orphan dependencies world [ports] print/add/remove world list sync update ports database outdate print outdated ports cache print and clear old pkg and src caches integrity check installed port integrity dup print duplicate ports in repo installed print all installed ports missingdep print missing dependencies orphan print orphan installed ports foreign print foreign ports printconfig print scratchpkg configs help print this help msg Global options: --append-repo= append custom local repo path (can use multiple times) --prepend-repo= prepend custom local repo path (can use multiple times) --override-repo= override repo in $REPO_FILE with custom local repo (can use multiple times) --repo-file= use custom repo file (default: $REPO_FILE) --config-file= use custom config file (default: $CONFIG_FILE) --alias-file= use custom alias file (default: $ALIAS_FILE) --mask-file= use custom mask file (default: $MASK_FILE) --nocolor disable colour for output EOF } print_runhelp_msg() { echo "Run '$(basename $0) help' to see available options." exit 2 } # check for 'pkgadd', required for package database path command -v pkgadd >/dev/null 2>&1 || { echo "'pkgadd' not found in \$PATH!" exit 1 } mode=$1 [ "$mode" ] || { print_runhelp_msg } shift for opt in $@; do case $opt in --nocolor) nocolor;; --append-repo=*) APPENDREPO="$APPENDREPO ${opt#*=}";; --prepend-repo=*) PREPENDREPO="$PREPENDREPO ${opt#*=}";; --override-repo=*) OVERRIDEREPO="$OVERRIDEREPO ${opt#*=}";; --repo=*) PORT_REPO="$PORT_REPO ${opt#*=}";; --repo-file=*) REPO_FILE="${opt#*=}";; --alias-file=*) ALIAS_FILE="${opt#*=}";; --config-file=*) CONFIG_FILE="${opt#*=}";; --*) MAINOPTS="$MAINOPTS $opt";; -*) char=${#opt}; count=1 while [ "$count" != "$char" ]; do count=$((count+1)) MAINOPTS="$MAINOPTS -$(printf '%s' $opt | cut -c $count)" done;; *) MAINOPTS="$MAINOPTS $opt";; esac shift done BUILD_SCRIPT="spkgbuild" PKGDB_DIR="$(pkgadd --print-dbdir)" PKGDBPERMS_DIR="${PKGDB_DIR}.perms" REPO_FILE="${REPO_FILE:-/etc/scratchpkg.repo}" ALIAS_FILE="${ALIAS_FILE:-/etc/scratchpkg.alias}" MASK_FILE="${MASK_FILE:-/etc/scratchpkg.mask}" CONFIG_FILE="${CONFIG_FILE:-/etc/scratchpkg.conf}" WORLD_FILE="$(dirname $PKGDB_DIR)/world" # default value from pkgbuild SOURCE_DIR="/var/cache/scratchpkg/sources" PACKAGE_DIR="/var/cache/scratchpkg/packages" WORK_DIR="/var/cache/scratchpkg/work" CURL_OPTS="" COMPRESSION_MODE="xz" NO_STRIP="no" IGNORE_MDSUM="no" KEEP_LIBTOOL="no" KEEP_LOCALE="no" KEEP_DOC="no" if [ -f "$REPO_FILE" ]; then for repodir in $(grep -Ev '^(#|$)' "$REPO_FILE" | awk '{print $1}'); do PORT_REPO="$PORT_REPO $repodir" done fi if [ "$OVERRIDEREPO" ]; then PORT_REPO="$OVERRIDEREPO" else PORT_REPO="$PREPENDREPO $PORT_REPO $APPENDREPO" fi for f in $REPO_FILE $ALIAS_FILE $MASK_FILE $CONFIG_FILE; do if [ ! -f "$f" ]; then msgerr "file not found: $f" exit 3 fi done . "$CONFIG_FILE" if [ "$(command -v scratch_$mode)" ]; then scratch_$mode $MAINOPTS else print_runhelp_msg fi exit $?