#!/bin/bash # # 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='\e[0;31m' GREEN='\e[0;32m' YELLOW='\e[0;33m' CYAN='\e[0;36m' PURPLE='\e[0;35m' CRESET='\e[0m' nocolor() { RED= GREEN= YELLOW= CYAN= PURPLE= CRESET= } msg() { echo -e "${GREEN}==>${CRESET} $1" } msgerr() { echo -e "${RED}==> ERROR:${CRESET} $1" } msginst() { echo -e "[${GREEN}i${CRESET}] $1" } msgmiss() { echo -e "[${YELLOW}m${CRESET}] $1" } msgnoinst() { echo -e "[ ] $1" } msgwarn() { echo -e "${YELLOW}==> WARNING:${CRESET} $1" } needroot() { if [ $UID != 0 ]; then if [ "$#" -eq 0 ]; then needroot "This operation" else msgerr "$@ need root access!" fi exit 1 fi } getportpath() { for repo in ${PORT_REPO[@]}; do if [[ -f $repo/$1/$BUILD_SCRIPT ]]; then echo "$(dirname $repo/$1/$BUILD_SCRIPT)" return 0 fi done return 1 } pushd() { command pushd $1 &>/dev/null } popd() { command popd &>/dev/null } vercomp() { if [ "$1" = "$2" ]; then return 0 # same version elif [ "$1" = "$(echo -e "$1\n$2" | sort -V | head -n1)" ]; then return 1 # $1 lower than $2 else return 2 # $1 higher than $2 fi } installed_pkg_info() { if isinstalled $2; then echo $(cat $INDEX_DIR/$2/.pkginfo | grep ^$1 | cut -d " " -f3-) fi } allinstalled() { for i in $INDEX_DIR/*; do if isinstalled ${i##*/}; then echo ${i##*/} fi done } confirm() { read -r -p "$1 (Y/n) " response case "$response" in [Nn][Oo]|[Nn]) echo "$2"; exit 2 ;; *) : ;; esac } checktool() { if ! type -p $1 &>/dev/null; then msgerr "'$1' not exist in your system!" exit 1 fi } needarg() { if [[ -z "$@" ]]; then msgerr "This operation required an arguments!" exit 1 fi } scratch_cat() { if PPATH=$(getportpath "$1"); then cat "$PPATH/$BUILD_SCRIPT" else msgerr "Port '$1' not exist." exit 1 fi } isinstalled() { if [ -s $INDEX_DIR/$1/.pkginfo ] && [[ $(grep $1 $INDEX_DIR/$1/.pkginfo) ]]; then return 0 else return 1 fi } settermtitle() { echo -en "\033]0;$@\a" } scratch_missingdep() { local pkg d for pkg in $(allinstalled); do if [ $(getportpath "$pkg") ]; then depends=$(grep "^# depends[[:blank:]]*:" $(getportpath "$pkg")/$BUILD_SCRIPT | sed 's/^# depends[[:blank:]]*:[[:blank:]]*//' | tr ' ' '\n' | awk '!a[$0]++') fi if [ "$depends" ]; then for d in ${depends[@]}; do if ! isinstalled $d; then msd+=($d) fi done fi if [ ${#msd[@]} -gt 0 ]; then echo -e "${GREEN}$pkg${CRESET} missing ${RED}${msd[@]}${CRESET}" fi unset depends msd done } scratch_integrity() { if [ "$1" ]; then pushd / if [ -f $INDEX_DIR/$1/.files ]; then while read -r line; do if [ ! -e "$line" ]; then MISSING_FILE=yes if [ -L "$line" ]; then echo -e "${YELLOW}broken symlink${CRESET} $1: /$line" else echo -e "${RED}file missing${CRESET} $1: /$line" fi fi done < <(cat $INDEX_DIR/$1/.files) else echo "Package '$1' not installed." exit 1 fi popd else pushd / for pkg in $(allinstalled); do while read -r line; do if [ ! -e "$line" ]; then MISSING_FILE=yes if [ -L "$line" ]; then echo -e "${YELLOW}broken symlink${CRESET} $pkg: /$line" else echo -e "${RED}file missing${CRESET} $pkg: /$line" fi fi done < <(cat $INDEX_DIR/$pkg/.files) done popd fi [ "$UID" != "0" ] && msg "${YELLOW}(check integrity is recommended run as root or using sudo)${CRESET}" if [ "$1" ]; then p="Package '$1'" else p="Your system" fi [ ! "$MISSING_FILE" ] && msg "$p files is consistent with package tree." } scratch_listinst() { local pkg for pkg in $(allinstalled); do echo $pkg done } scratch_listorphan() { local pkg dep tmpallpkg=$(mktemp) tmpalldep=$(mktemp) for pkg in $(allinstalled); do echo $pkg >> $tmpallpkg dep="$dep $(grep "^# depends[[:blank:]]*:" $(getportpath $pkg)/$BUILD_SCRIPT 2>/dev/null | sed 's/^# depends[[:blank:]]*:[[:blank:]]*//' | tr ' ' '\n' | awk '!a[$0]++')" done echo $dep | tr ' ' '\n' | sort | uniq > $tmpalldep grep -xvF -f $tmpalldep $tmpallpkg rm $tmpalldep $tmpallpkg } scratch_lock() { local pkg needroot "Locking package" for pkg in "$@"; do if ! isinstalled $pkg; then msgerr "Package '$pkg' is not installed." elif [ -f $INDEX_DIR/$pkg/.lock ]; then msgerr "Package '$pkg' already locked." else touch $INDEX_DIR/$pkg/.lock && msg "Successfully locked package '$pkg'." fi done } scratch_unlock() { local pkg needroot "Unlocking package" for pkg in "$@"; do if ! isinstalled $pkg; then msgerr "Package '$pkg' is not installed." elif [ ! -f $INDEX_DIR/$pkg/.lock ]; then msgerr "Package '$pkg' is not locked." else rm -f $INDEX_DIR/$pkg/.lock && msg "Successfully unlocked package '$pkg'." fi done } scratch_listlocked() { local pkg for pkg in $(allinstalled); do if [ -f "$INDEX_DIR"/$pkg/.lock ]; then echo -e "$pkg" fi done } scratch_depends() { local dep if [ $(getportpath "$1") ]; then depends=$(grep "^# depends[[:blank:]]*:" $(getportpath "$1")/$BUILD_SCRIPT | sed 's/^# depends[[:blank:]]*:[[:blank:]]*//' | tr ' ' '\n' | awk '!a[$0]++') else msgerr "Port '$1' not exist." exit 1 fi #isinstalled ${depends[0]} for dep in ${depends[@]}; do if isinstalled $dep; then msginst "$dep" elif getportpath $dep >/dev/null; then msgnoinst "$dep" else msgmiss "$dep" fi done } scratch_isorphan() { needarg $1 for pkg in $(allinstalled); do pkgpath=$(getportpath $pkg) #echo $pkgpath if [ $pkgpath ]; then depend=$(grep "^# depends[[:blank:]]*:" $pkgpath/$BUILD_SCRIPT | sed 's/^# depends[[:blank:]]*:[[:blank:]]*//' | tr ' ' '\n' | awk '!a[$0]++') for dep in ${depend[@]}; do if [ $dep = $1 ]; then return 1 fi done fi unset depend dep done return 0 } scratch_dependent() { local port all dep pname needarg $1 for port in ${PORT_REPO[@]}; do if [ -d $port ]; then for all in $port/*/$BUILD_SCRIPT; do [ -f $all ] || continue depend=$(grep "^# depends[[:blank:]]*:" $all | sed 's/^# depends[[:blank:]]*:[[:blank:]]*//' | tr ' ' '\n' | awk '!a[$0]++') for dep in ${depend[@]}; do if [ $dep = $1 ]; then GDP=yes pname=$(dirname $all) pname=$(echo ${pname##*/}) if isinstalled $pname; then msginst "$pname" else msgnoinst "$pname" fi fi done done fi done [ "$GDP" ] && return 0 || return 1 } scratch_own() { local arg arg=$(echo $1 | sed 's:^/::') grep -R $arg $INDEX_DIR/*/.files | sed "s:$INDEX_DIR/::" | sed "s:/.files::" | tr : " " | column -t } scratch_files() { if ! isinstalled $1; then msg "Package'$1' not installed." else while read -r line; do echo "$line" done < <(cat $INDEX_DIR/$1/.files) fi } scratch_sync() { checktool httpup needroot "Updating ports" if [ ! -e "$REPO_FILE" ]; then msgerr "Repo file not found! ($REPO_FILE)" exit 1 fi while read repodir repourl junk; do case $repodir in ""|"#"*) continue ;; esac if [ -n "$repodir" ] && [ -n "$repourl" ]; then httpup sync $repourl $repodir if [ $? != 0 ]; then msgerr "Failed sync from $repourl" exit 1 fi fi done < "$REPO_FILE" } scratch_readme() { needarg $@ if PPATH=$(getportpath "$1"); then if [ -f "$PPATH/readme" ]; then cat "$PPATH/readme" else msgerr "Port '$1' does not have readme." fi else msgerr "Port '$1' not exist." exit 1 fi } scratch_search() { needarg $@ case $1 in -*) msgerr "Invalid pattern '$1'" return 1 ;; esac local port found OUTPUT for port in ${PORT_REPO[@]}; do if [ -d $port ]; then pushd $port OUTPUT=$(grep -R description | grep "$BUILD_SCRIPT:# description[[:blank:]]*:" | sed "s/$BUILD_SCRIPT:# description[[:blank:]]*://" | grep -i "$1" | cut -d '/' -f1 | sort) popd if [ -n "$OUTPUT" ]; then found=yes for out in ${OUTPUT[@]}; do if [ -f $port/$out/$BUILD_SCRIPT ]; then pushd $port/$out description=$(grep "^# description[[:blank:]]*:" $BUILD_SCRIPT | sed 's/^# description[[:blank:]]*:[[:blank:]]*//') . $BUILD_SCRIPT popd if [ ! -z "$name" ] && [ ! -z "$version" ] && [ ! -z "$release" ]; then portname=$(basename $port) search_result="${PURPLE}($portname)${CRESET} $name ${CYAN}$version-$release${CRESET} $description" if isinstalled $name; then echo -e "[${GREEN}*${CRESET}] $search_result" else echo -e "[ ] $search_result" fi unset description name version release fi fi done fi fi done if [ ! "$found" ]; then msg "No matching package found." fi } scratch_foreignpkg() { for pkg in $(allinstalled); do if ! getportpath $pkg >/dev/null; then iname=$(installed_pkg_info name $pkg) iversion=$(installed_pkg_info version $pkg) irelease=$(installed_pkg_info release $pkg) echo -e "$iname ${GREEN}$iversion${CRESET}-${CYAN}$irelease${CRESET}" fi unset iname iversion irelease done } scratch_info() { [ $1 ] || return 0 version="-" release="-" iver="-" irel="-" if $(isinstalled $1); then iver=$(installed_pkg_info version $1) irel=$(installed_pkg_info release $1) INSTALLED=1 fi portpath=$(getportpath $1) if [ ! -z $portpath ]; then . $portpath/$BUILD_SCRIPT PORTEXIST=1 else portpath="-" fi if [ "$INSTALLED" != 1 ] && [ "$PORTEXIST" != 1 ]; then return 0 fi echo -e "name: \t\t$1 \t$([[ $PORTEXIST = 1 ]] || echo \( foreignpkg \))" echo -e "version: \t$version \t( installed: $iver )" echo -e "release: \t$release \t( installed: $irel )" echo -e "port path: \t$portpath" } scratch_trigger() { needroot "Run trigger" if [[ -z "$@" ]]; then for i in trig_{1..12}; do eval $i=1 done else pre_triggers $@ fi post_triggers } post_triggers() { if [ "$trig_12" = 1 ]; then echo "trigger: Running mkdirs..." for mkd in $INDEX_DIR/*/.pkgmkdirs; do [ -s $mkd ] || continue while read dir mode uid gid junk; do # Ignore comments and blank lines case "$dir" in ""|\#*) continue ;; esac if [ -e "$dir" ]; then if [ "$uid" != '-' ]; then getent passwd $uid >/dev/null if [[ "$?" = 0 ]]; then chown "$uid" "$dir" fi fi if [ "$gid" != '-' ]; then getent group $gid >/dev/null if [[ "$?" = 0 ]]; then chgrp "$gid" "$dir" fi fi if [ "$mode" != '-' ]; then chmod "$mode" "$dir" fi fi done < "$mkd" done fi if [ "$trig_11" = 1 ] && [ $(type -p fc-cache) ]; then echo "trigger: Updating fontconfig cache..." fc-cache -s fi if [ "$trig_10" = 1 ] && [ $(type -p gdk-pixbuf-query-loaders) ]; then echo "trigger: Probing GDK-Pixbuf loader modules..." gdk-pixbuf-query-loaders --update-cache fi if [ "$trig_9" = 1 ] && [ $(type -p gio-querymodules) ]; then echo "trigger: Updating GIO module cache..." gio-querymodules /usr/lib/gio/modules fi if [ "$trig_8" = 1 ] && [ $(type -p 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 ] && [ $(type -p 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 ] && [ $(type -p 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 ] && [ $(type -p 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 &>/dev/null else rm -f $dir/icon-theme.cache rmdir --ignore-fail-on-non-empty $dir fi done fi if [ "$trig_4" = 1 ] && [ $(type -p udevadm) ]; then echo "trigger: Updating hardware database..." udevadm hwdb --update fi if [ "$trig_3" = 1 ] && [ $(type -p mkfontdir) ] && [ $(type -p mkfontscale) ]; then echo "trigger: Updating X fontdir indices..." for dir in $(find /usr/share/fonts -maxdepth 1 -type d \( ! -path /usr/share/fonts -a ! -name X11 \)) /usr/share/fonts/X11/*; do rm -f $dir/fonts.{scale,dir} $dir/.uuid rmdir --ignore-fail-on-non-empty $dir [[ -d $dir ]] || continue mkfontdir $dir mkfontscale $dir done fi if [ "$trig_2" = 1 ] && [ $(type -p update-desktop-database) ]; then echo "trigger: Updating desktop file MIME type cache..." update-desktop-database --quiet fi if [ "$trig_1" = 1 ] && [ $(type -p update-mime-database) ]; then echo "trigger: Updating the MIME type database..." update-mime-database /usr/share/mime fi } pre_triggers() { local pkg # mime db if [ "$trig_1" != "1" ]; then for pkg in $@; do if [ -s "$INDEX_DIR/$pkg/.files" ] && [ "$(grep ^usr/share/mime/$ $INDEX_DIR/$pkg/.files)" ]; then trig_1=1 break fi done fi # desktop db if [ "$trig_2" != "1" ]; then for pkg in $@; do if [ -s "$INDEX_DIR/$pkg/.files" ] && [ "$(grep ^usr/share/applications/$ $INDEX_DIR/$pkg/.files)" ]; then trig_2=1 break fi done fi # mkfontdir if [ "$trig_3" != "1" ]; then for pkg in $@; do if [ -s "$INDEX_DIR/$pkg/.files" ] && [ "$(grep ^usr/share/fonts/[^/]*/$ $INDEX_DIR/$pkg/.files)" ]; then trig_3=1 break fi done fi # hwdb if [ "$trig_4" != "1" ]; then for pkg in $@; do if [ -s "$INDEX_DIR/$pkg/.files" ] && [ "$(grep ^etc/udev/hwdb.d/$ $INDEX_DIR/$pkg/.files)" ]; then trig_4=1 break fi done fi # icon caches if [ "$trig_5" != "1" ]; then for pkg in $@; do if [ -s "$INDEX_DIR/$pkg/.files" ] && [ "$(grep ^usr/share/icons/[^/]*/$ $INDEX_DIR/$pkg/.files)" ]; then trig_5=1 break fi done fi # gtk3 immodules if [ "$trig_6" != "1" ]; then for pkg in $@; do if [ -s "$INDEX_DIR/$pkg/.files" ] && [ "$(grep ^usr/lib/gtk-3.0/3.0.0/immodules/.*.so $INDEX_DIR/$pkg/.files)" ]; then trig_6=1 break fi done fi # gtk2 immodules if [ "$trig_7" != "1" ]; then for pkg in $@; do if [ -s "$INDEX_DIR/$pkg/.files" ] && [ "$(grep ^usr/lib/gtk-2.0/2.10.0/immodules/.*.so $INDEX_DIR/$pkg/.files)" ]; then trig_7=1 break fi done fi # gsettings schema if [ "$trig_8" != "1" ]; then for pkg in $@; do if [ -s "$INDEX_DIR/$pkg/.files" ] && [ "$(grep ^usr/share/glib-2.0/schemas/$ $INDEX_DIR/$pkg/.files)" ]; then trig_8=1 break fi done fi # gio modules if [ "$trig_9" != "1" ]; then for pkg in $@; do if [ -s "$INDEX_DIR/$pkg/.files" ] && [ "$(grep ^usr/lib/gio/modules/.*.so $INDEX_DIR/$pkg/.files)" ]; then trig_9=1 break fi done fi # gdk-pixbuf if [ "$trig_10" != "1" ]; then for pkg in $@; do if [ -s "$INDEX_DIR/$pkg/.files" ] && [ "$(grep ^usr/lib/gdk-pixbuf-2.0/2.10.0/loaders/.*.so $INDEX_DIR/$pkg/.files)" ]; then trig_10=1 break fi done fi # font caches if [ "$trig_11" != "1" ]; then for pkg in $@; do if [ -s "$INDEX_DIR/$pkg/.files" ] && [ "$(grep ^usr/share/fonts/[^/]*/$ $INDEX_DIR/$pkg/.files)" ]; then trig_11=1 break fi done fi # makedirs if [ "$trig_12" != "1" ]; then for pkg in $@; do if [ -s "$INDEX_DIR/$pkg/.pkgmkdirs" ]; then trig_12=1 break fi done fi } scratch_build() { local OPTS if [ -z "$1" ]; then echo "Please specify package to build." return 1 fi while [ "$1" ]; do if [[ "$1" =~ ^-(f|w|m|o|x|l)$ ]]; then OPTS+=($1) elif [[ "$1" =~ ^--(no-prebuild|force-rebuild|skip-mdsum|download|extract|keep-work|log)$ ]]; then OPTS+=($1) elif [[ "$1" =~ ^--(pkgdir|srcdir|logdir)= ]]; then OPTS+=($1) elif [[ "$1" =~ ^-. ]]; then echo "Unrecognize option. ($1)" return 1 else PKGNAME=$1 fi shift done pushd $(getportpath $PKGNAME) if [ $? = 0 ]; then settermtitle "Building $PKGNAME..." pkgbuild ${OPTS[@]} || return 1 settermtitle "Building $PKGNAME done" else echo "Package '$PKGNAME' not found." return 1 fi return 0 } scratch_install() { local pkg i int pkgcount count IPKG OPTS REINSTALL done_pkg needroot "Installing package" if [ -z "$1" ]; then echo "Please specify package(s) to install." return 1 fi while [ "$1" ]; do if [[ "$1" =~ ^-(c|v|-verbose|-ignore-conflict)$ ]]; then OPTS+=($1) elif [[ "$1" =~ ^--(no-preinstall|no-postinstall) ]]; then OPTS+=($1) elif [[ "$1" =~ ^-(r|-reinstall)$ ]]; then OPTS+=($1) REINSTALL=1 elif [[ "$1" =~ ^-(d|-no-dep)$ ]]; then NO_DEP=1 elif [[ "$1" = "--yes" ]] || [[ "$1" = "-y" ]]; then NOCONFIRM=1 elif [[ "$1" =~ ^-. ]]; then echo "Unrecognize option. ($1)" return 1 else PKGNAME+=($1) fi shift done # if reinstall, dont calculate dep, just reinstall it then exit if [ "$REINSTALL" = 1 ]; then for ii in ${PKGNAME[@]}; do if [ ! $(getportpath $ii) ]; then echo "Package '$ii' not found." elif ! isinstalled $ii; then echo "Package '$ii' not installed." else pushd $(getportpath $ii) settermtitle "Reinstalling $ii..." pkgbuild ${OPTS[@]} if [ $? != 0 ]; then error=1 break fi done_pkg+=($ii) popd fi done settermtitle "Triggering install hook" if [ ${#done_pkg[@]} -gt 0 ]; then scratch_trigger ${done_pkg[@]} fi settermtitle "Reinstalling done" [ "$error" = 1 ] && return 1 || return 0 fi if [ "$NO_DEP" = 1 ]; then for ii in ${PKGNAME[@]}; do if [ ! $(getportpath $ii) ]; then echo "Package '$ii' not found." elif isinstalled $ii; then echo "Package '$ii' already installed." return 0 else pushd $(getportpath $ii) settermtitle "Installing $ii..." pkgbuild -i ${OPTS[@]} if [ $? != 0 ]; then error=1 break fi done_pkg+=($ii) popd fi done settermtitle "Triggering install hook" if [ ${#done_pkg[@]} -gt 0 ]; then scratch_trigger ${done_pkg[@]} fi settermtitle "Installing done" [ "$error" = 1 ] && return 1 || return 0 fi for i in ${PKGNAME[@]}; do if [ ! $(getportpath $i) ]; then echo "Package '$i' not found." elif isinstalled $i; then echo "Package '$i' already installed." else IPKG+=($i) fi done if [ "${#IPKG[@]}" = 0 ]; then return 0 fi echo "Resolving dependencies..." INST="$(pkgdeplist -l -q ${IPKG[@]} | awk '{print $2}')" if [ "$INST" ]; then echo pkgcount=0 for pkg in $INST; do pkgcount=$(( $pkgcount + 1 )) echo -en "[${GREEN}i${CRESET}] $pkg " done echo; echo echo "( $pkgcount install )" echo if [ ! "$NOCONFIRM" ]; then confirm "Continue install package(s)?" "Package installation cancelled." echo fi error=0 count=0 total=$(echo $INST | wc -w) for int in ${INST[@]}; do count=$(( $count + 1 )) portpathh=$(getportpath $int) if [ "$portpathh" ]; then pushd $portpathh settermtitle "[ $count/$total ] installing $int..." pkgbuild -i ${OPTS[@]} if [ $? != 0 ]; then error=1 count=$(( $count - 1 )) break fi done_pkg+=($int) popd else msgwarn "Skipping missing package: $int" fi unset portpathh done settermtitle "Triggering install hook" if [ ${#done_pkg[@]} -gt 0 ]; then scratch_trigger ${done_pkg[@]} fi settermtitle "$count/$total package(s) installed" return "$error" fi } outdatepkg() { local pkg for pkg in $(allinstalled); do if [ ! -e "$INDEX_DIR/$pkg/.lock" ] && getportpath $pkg >/dev/null; then . $(getportpath $pkg)/$BUILD_SCRIPT iversion=$(installed_pkg_info version $pkg) irelease=$(installed_pkg_info release $pkg) if [ "$release" != "$irelease" ] || [ "$version" != "$iversion" ]; then echo $name fi unset iversion irelease fi done } scratch_remove() { local pkg i IPKG OPTS needroot "Removing package" if [ -z "$1" ]; then echo "Please specify package(s) to remove." return 1 fi while [ "$1" ]; do if [[ "$1" =~ ^--(no-postremove|no-preremove)$ ]]; then OPTS+=($1) elif [[ "$1" = "--yes" ]] || [[ "$1" = "-y" ]]; then NOCONFIRM=1 elif [[ "$1" =~ ^-. ]]; then echo "Unrecognize option. ($1)" return 1 else PKGNAME+=($1) fi shift done for i in ${PKGNAME[@]}; do if ! isinstalled $i; then echo "Package '$i' not installed." else IPKG+=($i) fi done if [ "${#IPKG[@]}" = 0 ]; then return 0 fi if [ "$IPKG" ]; then echo "Removing packages..." echo pkgcount=0 count=0 for pkg in ${IPKG[@]}; do pkgcount=$(( $pkgcount + 1 )) echo -en "[${RED}x${CRESET}] $pkg " done echo; echo echo "( $pkgcount remove )" echo if [ ! "$NOCONFIRM" ]; then confirm "Continue remove package(s)?" "Package removing cancelled." echo fi for pkg in ${IPKG[@]}; do count=$(( $count + 1 )) pre_triggers $pkg settermtitle "[ $count/$pkgcount ] Removing $pkg..." pkgdel $pkg ${OPTS[@]} || return 1 done settermtitle "Triggering remove hook" post_triggers settermtitle "$pkgcount package(s) removed" fi } scratch_sysup() { local d UPGPKG NEWPKG PKGOUTDATE OPTS done_pkg needroot "Upgrading package" while [ "$1" ]; do if [[ "$1" =~ ^--(no-postupgrade|no-preupgrade|no-backup)$ ]]; then OPTS+=($1) elif [[ "$1" =~ ^-(c|v|-verbose|-ignore-conflict)$ ]]; then OPTS+=($1) elif [ "$1" = "--yes" ] || [ "$1" = "-y" ]; then NOCONFIRM=1 elif [ "$1" = "--np-dep" ] || [ "$1" = "-d" ]; then NODEP=1 elif [ "$1" = "--sync" ] || [ "$1" = "-s" ]; then SYNC=1 elif [[ "$1" =~ ^-. ]]; then echo "Unrecognize option. ($1)" exit 1 fi shift done if [ "$SYNC" = 1 ]; then scratch_sync fi echo "Checking for outdated packages..." PKGOUTDATE=$(outdatepkg) if [ ! "$PKGOUTDATE" ]; then echo "All package is up to date." return 0 fi UPGPKG=0 NEWPKG=0 if [ "$NODEP" != 1 ]; then echo "Resolving dependencies..." DEP=$(pkgdeplist ${PKGOUTDATE[@]} -l | awk '{print $2}') echo for d in $DEP; do if [ "$(echo $PKGOUTDATE | tr ' ' '\n' | grep -x $d)" = "$d" ]; then echo -ne "[${GREEN}u${CRESET}] $d " WILLINSTALL+=($d) UPGPKG=$(( $UPGPKG + 1 )) elif ! isinstalled $d && [ $(getportpath "$d") ]; then echo -ne "[${CYAN}n${CRESET}] $d " WILLINSTALL+=($d) NEWPKG=$(( $NEWPKG + 1 )) fi done else echo for dd in ${PKGOUTDATE[@]}; do echo -ne "[${GREEN}u${CRESET}] $dd " WILLINSTALL+=($dd) UPGPKG=$(( $UPGPKG + 1 )) done fi echo echo echo "( $UPGPKG upgrade, $NEWPKG new install )" echo if [ ! "$NOCONFIRM" ]; then confirm "Continue upgrade/install these package(s)?" "Package upgrade cancelled." echo fi 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 )) pushd $(getportpath $inst) if ! isinstalled $inst; then settermtitle "[ $count/$total ] Installing $inst..." pkgbuild -i ${OPTS[@]} if [ $? != 0 ]; then error=1 count=$(( $count - 1 )) break fi else settermtitle "[ $count/$total ] Upgrading $inst..." pkgbuild -u ${OPTS[@]} if [ $? != 0 ]; then error=1 count=$(( $count - 1 )) break fi fi done_pkg+=($inst) done settermtitle "Triggering install hook" if [ ${#done_pkg[@]} -gt 0 ]; then scratch_trigger ${done_pkg[@]} fi settermtitle "$count/$total package(s) upgraded" return $error } scratch_upgrade() { local pkg done_pkg needroot "Upgrading package" needarg "$@" while [ "$1" ]; do if [[ "$1" =~ ^--(no-postupgrade|no-preupgrade|no-backup)$ ]]; then OPTS+=($1) elif [[ "$1" =~ ^-(c|v|-ignore-conflict|-verbose) ]]; then OPTS+=($1) elif [[ "$1" =~ ^-(-no-dep|d)$ ]]; then NO_DEP=1 elif [[ "$1" = "--yes" ]] || [[ "$1" = "-y" ]]; then NOCONFIRM=1 elif [[ "$1" =~ ^-. ]]; then echo "Unrecognize option. ($1)" exit 1 else PKGNAME+=($1) fi shift done for pkg in ${PKGNAME[@]}; do if ! isinstalled $pkg; then msgerr "Package '$pkg' not installed." return 1 fi if [ ! $(getportpath $pkg) ]; then msgerr "Package '$pkg' not exist." return 1 fi . $(getportpath $pkg)/$BUILD_SCRIPT if [ "$(installed_pkg_info version $pkg)-$(installed_pkg_info release $pkg)" = "$version-$release" ]; then msg "Package '$pkg' is up-to-date." return 0 fi done if [ -z "$NO_DEP" ]; then echo "Resolving dependencies..." DEP=$(pkgdeplist -l -n ${PKGNAME[@]} | awk '{print $2}') for dep in $DEP; do if ! isinstalled $dep; then if [ $(getportpath $dep) ]; then NEWPKG+=($dep) fi fi done fi echo count=0 for i in ${NEWPKG[@]}; do count=$(( $count + 1 )) echo -en "[${CYAN}n${CRESET}] $i " done for i in ${PKGNAME[@]}; do count=$(( $count + 1 )) echo -en "[${GREEN}u${CRESET}] $i " done echo echo echo "( ${#PKGNAME[@]} upgrade, ${#NEWPKG[@]} new install )" echo total=$count count=0 if [ ! "$NOCONFIRM" ]; then confirm "Continue upgrade/install these package(s)?" "Package upgrade cancelled." echo fi if [ ${#NEWPKG[@]} -gt 0 ]; then for newpkg in ${NEWPKG[@]}; do count=$(( $count + 1 )) pushd $(getportpath $newpkg) settermtitle "[ $count/$total ] Installing $newpkg..." pkgbuild -i ${OPTS[@]} if [ $? != 0 ]; then error=1 count=$(( $count - 1 )) break fi done_pkg+=($newpkg) popd done fi for pkg in ${PKGNAME[@]}; do # upgrade all target packages count=$(( $count + 1 )) pushd $(getportpath $pkg) settermtitle "[ $count/$total ] Upgrading $pkg..." pkgbuild -u ${OPTS[@]} if [ $? != 0 ]; then error=1 count=$(( $count - 1 )) break fi done_pkg+=($pkg) popd done settermtitle "triggering upgrade hook" if [ ${#done_pkg[@]} -gt 0 ]; then scratch_trigger ${done_pkg[@]} fi settermtitle "$count/$total package(s) upgraded" return $error } scratch_outdate() { local pkg for pkg in $(allinstalled); do if [ $(getportpath $pkg) ]; then . $(getportpath $pkg)/$BUILD_SCRIPT iversion=$(installed_pkg_info version $pkg) irelease=$(installed_pkg_info release $pkg) [ -f "$INDEX_DIR/$pkg/.lock" ] && ITSLOCK="[locked]" outdatemsg="$name ${RED}$iversion-$irelease${CRESET} => ${GREEN}$version-$release${CRESET} ${CYAN}$ITSLOCK${CRESET}" newerinstmsg="$name ${RED}$iversion-$irelease${CRESET} => ${GREEN}$version-$release${CRESET} ${YELLOW}[newer installed]${CRESET} ${CYAN}$ITSLOCK${CRESET}" if [ "$version" != "$iversion" ]; then vercomp $version $iversion if [ $? = 2 ]; then echo -e "$outdatemsg" OUTDATE=yes elif [ $? = 1 ]; then echo -e "$newerinstmsg" OUTDATE=yes fi elif [ "$release" != "$irelease" ]; then vercomp $release $irelease if [ $? = 2 ]; then echo -e "$outdatemsg" OUTDATE=yes elif [ $? = 1 ]; then echo -e "$newerinstmsg" OUTDATE=yes fi fi unset ITSLOCK fi done [ ! "$OUTDATE" ] && msg "All package is up to date." } clearpkgcache() { if [ ${#ALL_PACKAGES[@]} -gt 0 ]; then for pkg in ${ALL_PACKAGES[@]}; do rm -v $PACKAGE_DIR/$pkg done fi if [ ${#ALL_SOURCES[@]} -gt 0 ]; then for src in ${ALL_SOURCES[@]}; do rm -v $SOURCE_DIR/$src done fi } getpkgcache() { COMPRESSION_MODE="xz" [ -f /etc/scratchpkg.conf ] && . /etc/scratchpkg.conf for list in "$PACKAGE_DIR"/*; do [ -f "$list" ] && ALL_PACKAGES+=($(basename $list)) done for list in "$SOURCE_DIR"/*; do [ -f "$list" ] && ALL_SOURCES+=($(basename $list)) done 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 PORT_PACKAGES+=($name-$version-$release.spkg.tar.$COMPRESSION_MODE) if [ ! -z $source ]; then for src in ${source[@]}; do if [ $(echo $src | grep -E "(ftp|http|https)://") ]; then if [ $(echo $src | grep -E "::(ftp|http|https)://") ]; then sourcename="$(echo $src | awk -F '::' '{print $1}')" else sourcename="$(echo $src | rev | cut -d / -f 1 | rev)" fi SOURCE_NAMES+=($sourcename) fi done fi done fi done for i in ${PORT_PACKAGES[@]}; do for pkg in ${!ALL_PACKAGES[@]}; do if [ "${ALL_PACKAGES[pkg]}" = "$i" ]; then unset 'ALL_PACKAGES[pkg]' break fi done done for a in ${SOURCE_NAMES[@]}; do for src in ${!ALL_SOURCES[@]}; do if [ "${ALL_SOURCES[src]}" = "$a" ]; then unset 'ALL_SOURCES[src]' break fi done done } scratch_cache() { getpkgcache if [ ${#ALL_PACKAGES[@]} -gt 0 ]; then ALL_PACKAGES_SIZE=$(pushd "$PACKAGE_DIR" && du -ch ${ALL_PACKAGES[@]} | grep total | awk '{print $1}' && popd) else ALL_PACKAGES_SIZE=0M fi if [ ${#ALL_SOURCES[@]} -gt 0 ]; then ALL_SOURCES_SIZE=$(pushd "$SOURCE_DIR" && du -ch ${ALL_SOURCES[@]} | grep total | awk '{print $1}' && popd) else ALL_SOURCES_SIZE=0M fi [ ${#ALL_PACKAGES[@]} -gt 0 ] && (echo ${ALL_PACKAGES[@]} | tr ' ' '\n') [ ${#ALL_SOURCES[@]} -gt 0 ] && (echo ${ALL_SOURCES[@]} | tr ' ' '\n') echo echo -e "Package caches ($ALL_PACKAGES_SIZE)" echo -e "Source caches ($ALL_SOURCES_SIZE)" echo if [ ${#ALL_PACKAGES[@]} -gt 0 ] || [ ${#ALL_SOURCES[@]} -gt 0 ]; then confirm "Clear old caches?" "Old caches is keep." needroot "Clear old caches" clearpkgcache fi } scratch_path() { needarg $@ if PPATH=$(getportpath "$1"); then echo "$PPATH" else msgerr "Port '$1' not exist." exit 1 fi } scratch_dup() { dup=$(find ${PORT_REPO[@]} -type d -print | egrep -xv "($(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 reponame=$(basename $repo) [ -d $repo/$dp ] && echo "$repo/$dp" done done else msg "No duplicate ports found." fi } usage_extra() { cat << EOF Usage: $(basename $0) [ ] Operation: depends show depends of a package search search packages in port's repos lock lock packages from upgrade unlock unlock packages from upgrade cat view a package build scripts dependent show package's dependent own show package's owner of file files show list files of installed package path show package's buildscripts path readme print readme file if exist info print ports info sync update port's repo dup print duplicate ports in repo listinst list installed package in system listorphan list orphan package integrity check integrity of package's files outdate check for outdated packages cache print leftover cache missingdep check for mising dependency of installed package foreignpkg print package installed without port in repo listlocked print locked packages EOF } usage_build() { cat << EOF Usage: $(basename $0) build [ ] Options: -f, --force-rebuild force rebuild -m, --skip-mdsum skip md5sum check for sources -x, --extract extract only -w, --keep-work keep woring directory -o, --download download source files only -l, --log log build process --redownload re-download source files --srcdir= override default SOURCE_DIR --pkgdir= override default PACKAGE_DIR --logdir= override default LOG_DIR --no-prebuild skip pre-build script EOF } usage_upgrade() { cat << EOF Usage: $(basename $0) upgrade [ ] Options: -d, --no-dep skip installing dependencies (new dependencies) -c, --ignore-conflict skip file conflict check -v, --verbose print install process -y, --yes dont ask confirmation --no-backup skip backup configuration file --no-preupgrade skip pre-upgrade script --no-postupgrade skip post-upgrade script EOF } usage_sysup() { cat << EOF Usage: $(basename $0) sysup Options: -d, --no-dep skip installing dependencies (new dependencies) -c, --ignore-conflict skip file conflict check -v, --verbose print install process -s, --sync sync ports before upgrades -y, --yes dont ask confirmation --no-backup skip backup configuration file --no-preupgrade skip pre-upgrade script --no-postupgrade skip post-upgrade script EOF } usage_remove() { cat << EOF Usage: $(basename $0) remove [ ] Options: -v, --verbose print removed files -y, --yes dont ask confirmation --no-preremove skip pre-remove script --no-postremove skip post-remove script EOF } usage_install() { cat << EOF Usage: $(basename $0) install [ ] Options: -d, --no-dep skip installing dependencies -c, --ignore-conflict skip file conflict check -r, --reinstall reinstall installed package -v, --verbose print install files -y, --yes dont ask confirmation --no-preinstall skip pre-install script --no-postinstall skip post-install script EOF } usage_help() { cat << EOF Usage: $(basename $0) help Operation: build build package install install packages upgrade upgrade packages sysup full system upgrades remove remove packages extra various extra options EOF } scratch_help() { if [ -z "$1" ]; then usage_help return 0 else if [ "$(type -t usage_$1)" ]; then usage_$1 else usage_help fi fi return 0 } main() { if [ "$(type -t scratch_$mode)" = "function" ]; then scratch_$mode $@ else echo "Run 'scratch help' to see available operations and options" return 5 fi return $? } BUILD_SCRIPT="spkgbuild" INDEX_DIR="/var/lib/scratchpkg/index" REPO_FILE="/etc/scratchpkg.repo" SOURCE_DIR="/var/cache/scratchpkg/sources" PACKAGE_DIR="/var/cache/scratchpkg/packages" if [ ! -f "$REPO_FILE" ]; then msgerr "repo file not exist. ($REPO_FILE)" exit 1 else while read repodir repourl junk; do case $repodir in ""|"#"*) continue ;; esac PORT_REPO+=($repodir) done < "$REPO_FILE" fi mode=$1 shift for opt in $@; do case $opt in --nocolor) nocolor ;; --*) MAINOPTS+=($opt) ;; -*) for (( i=1; i<${#opt}; i++ )); do MAINOPTS+=(-${opt:$i:1}); done ;; *) MAINOPTS+=($opt) ;; esac done main ${MAINOPTS[@]} exit $?