Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
executable file 5007 lines (4532 sloc) 171 KB
#!/bin/bash
#
# $Id$
#
# sbopkg - The SlackBuilds.org Package Browser
# Copyright 2007-2010,2013 Chess Griffin <chess@chessgriffin.com>
# Copyright 2009-2011 Mauro Giachero <mauro.giachero@gmail.com>
# Copyright 2009-2013 slakmagik <slakmagik@gmail.com>
# Copyright 2015-2019 Willy Sudiarto Raharjo <willysr@sbopkg.org>
#
# Redistribution and use of this script, with or without modification, is
# permitted provided that the following conditions are met:
#
# 1. Redistributions of this script must retain the above copyright
# notice, this list of conditions and the following disclaimer.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED
# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
# EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
dialog_refresh_workaround() {
# Dialog has refresh problems on some terminals (currently known are some
# rxvt-based terminals and screen sessions), preventing correct dialogs
# rendering.
# It turns out that forcing TERM=xterm-color "fixes" at least most of the
# issues, so work around them this way.
export TERM=xterm-color
}
crunch() {
# The inspiration for this and the next function comes from the crunch()
# in installpkg. Both take one argument. This function reduces runs of
# spaces to one.
echo -e "$@" | tr -s ' '
}
crunch_fmt() {
# This echo reduces runs of spaces to one and reformats to 78 columns
echo -e "$@" | tr -s ' ' | fmt -78
}
unknown_response() {
# This is to produce the error message for bad read input
crunch_fmt "$SCRIPT: ${FUNCNAME[1]}: Unknown response: \"$REPLY\"."
}
split_pkg_name() {
# This function takes a string in the Slackware format NAME-VER-ARCH-BUILD
# (with an optional TAG) and splits the string into those respective
# PKG_-prefixed variables. (foo-1.0-i486-1_bar results in 'foo' being
# assigned to PKG_NAME, '1.0' being assigned to PKG_VER, 'i486' being
# assigned to PKG_ARCH, '1' being assigned to PKG_BUILD, and '_bar' being
# assigned to PKG_TAG. If the string has no tag, PKG_TAG will be an empty
# string.
local FILE=${1##*/}
eval $(echo $FILE | sed '
s/\(.*\)-\([^-]*\)-\([^-]*\)-\([0-9]*\)\(.*\)*/\
PKG_NAME=\1 PKG_VER=\2 PKG_ARCH=\3 PKG_BUILD=\4 PKG_TAG=\5/
')
}
set_type() {
# This function enforces the exclusivity of the -b/-d/-i flags and sets
# the value of TYPE. It takes an argument of 'download', 'build', or
# 'install' and sets TYPE to that value unless it's already been set to
# another, in which case it errors out.
local OPT_TYPE=$1
if [[ $TYPE && $TYPE != $OPT_TYPE ]]; then
printf "$SCRIPT: the -b, -d and -i options are exclusive. " >&2
printf "Please use only one.\n" >&2
exit 1
else # set global type
TYPE=$OPT_TYPE
fi
}
config_check() {
# Check if config file is there and if so check that it has all
# needed variables with any value, and set them.
local MISSING VAR OBS OBSVAR
if [[ ! -d $SBOPKG_REPOS_D ]]; then
echo "$SCRIPT: No $SBOPKG_REPOS_D was found." 1>&2
echo "Please correct this error and run $SCRIPT again." 1>&2
exit 1
fi
if [[ ! -d $SBOPKG_RENAMES_D ]]; then
echo "$SCRIPT: No $SBOPKG_RENAMES_D was found." 1>&2
echo "Please correct this error and run $SCRIPT again." 1>&2
exit 1
fi
if [[ ! -e $SBOPKG_CONF && ! -e $HOME/.sbopkg.conf ]]; then
echo "$SCRIPT: No $SBOPKG_CONF or ~/.sbopkg.conf was found." 1>&2
echo "Please create at least one of them and run $SCRIPT again." 1>&2
exit 1
fi
[[ -e $SBOPKG_CONF ]] && . $SBOPKG_CONF
[[ -e $HOME/.sbopkg.conf ]] && . $HOME/.sbopkg.conf
# Some configuration options are obsolete
for OBSVAR in SBOPKGTMP; do
if [[ ${!OBSVAR} ]]; then
OBS+="$OBSVAR "
fi
done
if [[ $OBS ]]; then
cat << EOF
$SCRIPT: obsolete configuration variable(s) found:
$OBS
Please remove or update any obsolete variables. See the sbopkg.conf(5) man
page for more details.
EOF
exit 1
fi
# Some configuration options are mandatory
for VAR in REPO_ROOT QUEUEDIR SRCDIR REPO_NAME REPO_BRANCH \
KEEPLOG CLEANUP LOGFILE DEBUG_UPDATES TMP OUTPUT RSYNCFLAGS \
WGETFLAGS DIFF DIFFOPTS SBOPKG_REPOS_D ALLOW_MULTI BLACKLISTFILE; do
if [[ -z "${!VAR}" ]]; then
MISSING+="$VAR "
fi
done
if [[ "$MISSING" ]]; then
cat << EOF
$SCRIPT: required configuration variable(s) not found:
$MISSING
If you have recently upgraded sbopkg there may be new variables in the
sbopkg.conf file. Please merge the sbopkg.conf.new file with your existing
sbopkg.conf file. See the sbopkg.conf(5) man page for more details.
Please correct this error and run $SCRIPT again.
EOF
exit 1
fi
if [[ $DEBUG_UPDATES != [012] ]]; then
echo "The DEBUG_UPDATES variable must be set to 0, 1, or 2." 1>&2
exit 1
fi
if [[ $LOGFILE != /* ]]; then
echo "The LOGFILE variable must be set to a full path." 1>&2
exit 1
fi
# Convert some YES/NO variables into 'set/unset' ones.
yesno_to_setunset KEEPLOG
yesno_to_setunset CLEANUP
yesno_to_setunset ALLOW_MULTI
yesno_to_setunset MKDIR_PROMPT
# Load the repositories data
load_repositories || exit 1
# Check for ncurses
[[ -n $interactive ]] && [[ -x /usr/bin/tput ]] && HAS_NCURSES=1
}
yesno_to_setunset() {
# Convert a yes/no variable to a set/unset one.
# $1 = variable name
# If the variable value is different from "yes" and "no" (case
# insensitive) spit an error message and exit.
# Note: $(eval echo \$$1) is the value of the variable (remember that
# $1 is the variable _name_).
if [[ $(eval echo \$$1) == [Nn][Oo] ]]; then
unset $1
elif [[ $(eval echo \$$1) != [Yy][Ee][Ss] ]]; then
cat <<EOF
ERROR
$SCRIPT: Unexpected value in $1
The configuration variable $1 is expected to be set to either YES or NO
(case insensitive). Its current value instead is $(eval echo \$$1).
Please fix this error by setting the appropriate value in
/etc/sbopkg/sbopkg.conf and/or in ~/.sbopkg.conf
and restart $SCRIPT.
EOF
exit 1
fi
}
load_repositories() {
# Fill the REPOSITORIES array with the data from the .repo files
local FILE LINE i
local TMPARRAY
local ERROR
for FILE in $SBOPKG_REPOS_D/*.repo; do
# Reading from $FILE...
while read LINE; do
grep -q '#' <<< "$LINE" && continue
eval "TMPARRAY=( $LINE )"
[[ ${#TMPARRAY[@]} -eq 0 ]] && continue;
# Sanity checks
# these two assignments work around a bash3-4 incompatibility
local GPG='^GPG$|^$'
local RSYNC='^rsync$|^git$|^$'
[[ ! ${TMPARRAY[6]} =~ $GPG ]] && ERROR="gpg"
[[ ! ${TMPARRAY[4]} =~ $RSYNC ]] && ERROR="tool"
[[ ${#TMPARRAY[@]} -ne $REPOS_FIELDS ]] && ERROR="fields"
[[ -n $ERROR ]] && break 2
# Add the record to REPOSITORIES
for i in ${!TMPARRAY[@]}; do
REPOSITORIES[${#REPOSITORIES[@]}]="${TMPARRAY[$i]}"
done
done < $FILE
done
if [[ -n $ERROR ]]; then
cat <<EOF
ERROR
$SCRIPT: Invalid repository descriptor
Line
$LINE
of
$FILE
EOF
case $ERROR in
'fields' )
crunch_fmt "doesn't contain the right number of fields\
($REPOS_FIELDS)."
;;
'tool' )
crunch_fmt "specifies an unknown fetching tool\
(${TMPARRAY[4]})."
;;
'gpg' )
crunch_fmt "specifies an unknown signature checker\
(${TMPARRAY[6]})."
;;
esac
return 1
fi
return 0
}
dir_init() {
# Check to make sure certain sbopkg-related directories exist. If not,
# create them.
local -a DIR_VARS DI_OUTPUT_LINES DI_OUTPUT
local DIRS2MK REPLY ERROR
# Keep DIR_VARS and DI_OUTPUT_LINES in sync where REPO_DIR ~ REPO_ROOT.
DIR_VARS=(
$REPO_DIR ${LOGFILE%/*} $QUEUEDIR $SRCDIR $TMP $OUTPUT
)
DI_OUTPUT_LINES=(
"REPO_{ROOT,NAME,BRANCH} -> $REPO_ROOT/,$REPO_NAME/,$REPO_BRANCH"
"LOGFILE directory -------> ${LOGFILE%/*}"
"QUEUEDIR ----------------> $QUEUEDIR"
"SRCDIR ------------------> $SRCDIR"
"TMP ---------------------> $TMP"
"OUTPUT ------------------> $OUTPUT"
)
for ((i=0; i<${#DIR_VARS[*]}; i++)); do
if [ ! -d ${DIR_VARS[$i]} ]; then
DIRS2MK+="${DIR_VARS[$i]} "
DI_OUTPUT+=( "${DI_OUTPUT_LINES[$i]}" )
fi
done
if [[ ${DI_OUTPUT[*]} ]]; then
if [[ $MKDIR_PROMPT ]]; then
# Keep DI_OUTPUT_LINES and the 'table headers' aligned, too.
cat << EOF
The following directories do not exist:
Variable Assignment
-------- ----------
EOF
for ((i=0; i<${#DI_OUTPUT[*]}; i++)); do
# git doesn't use REPO_BRANCH in this context, so sed it out
# of the display - can you say "ad hackery"? I knew you could!
#
# We used to have DI_OUTPUT_LINES just display REPO_ROOT and
# create REPO_DIR to try to avoid confusion in the display to
# the user but then it would say a directory didn't exist just
# because its REPO_BRANCH subdirectory didn't exist which was
# even more confusing and outright wrong. So keep the
# replacement hyphens aligned here, too. *sigh*
if [[ $REPO_TOOL == git ]]; then
echo "${DI_OUTPUT[$i]}" |
sed "s@,BRANCH} @} -------@;s@/,$REPO_BRANCH@@"
else
echo "${DI_OUTPUT[$i]}"
fi
done
cat << EOF
You can have sbopkg create them or, if these values are incorrect, you can
abort to edit your config files or pass different flags.
EOF
while :; do
read $NFLAG -ep "(C)reate or (A)bort?: "
case $REPLY in
C|c) break ;;
A|a)
if [[ ${FUNCNAME[1]} == main ]]; then
exit 1
else
return 1
fi
;;
*) unknown_response ;;
esac
done
fi
if ! mkdir -p $DIRS2MK 2>/dev/null; then
echo "$SCRIPT: failed to create directories" >&2
exit 1
fi
fi
for ((i=0; i<${#DIR_VARS[*]}; i++)); do
if [ ! -w ${DIR_VARS[$i]} ]; then
echo $SCRIPT: ${DIR_VARS[$i]} not writable
ERROR=nowrite
fi
done
if [[ $ERROR == nowrite ]]; then
# error msg above
exit 1
fi
}
pid_check() {
[[ $ALLOW_MULTI ]] && return
# Set and check for pid file.
local OTHERPID
if [[ -e $PIDFILE ]]; then
# make sure the process indicated by the pid file is actually running
# and the pid file isn't just stale.
OTHERPID=$(< $PIDFILE)
if [[ -n $(ps h --pid $OTHERPID) ]]; then
cat << EOF
ERROR
Another instance of sbopkg appears to be running
with process id $OTHERPID. Running more than
one instance of sbopkg is not recommended.
If this is incorrect, you can delete the lockfile
'$PIDFILE' and restart. Exiting now.
EOF
exit 1
fi
fi
echo $$ > $PIDFILE
}
check_if_repo_exists() {
# Check to see if $REPO_DIR exists and not empty
if [[ ! -d $REPO_DIR ]]; then
if [[ $DIAG ]]; then
dialog --title "ERROR" --msgbox "$(crunch "The directory \
$REPO_DIR was not found or is empty. Please make \
sure your repository directory is set correctly and that you \
have done a sync first.")" 12 30
continue
else
cat << EOF
ERROR
The directory $REPO_DIR was not found
or is empty. Please make sure your respository
directory is set correctly and that you have done
a sync first.
EOF
exit 1
fi
fi
}
show_changelog() {
# Show the SlackBuilds.org changelog.
check_if_repo_exists
cd $REPO_DIR
if [[ ! -r ./ChangeLog.txt ]]; then
if [[ $DIAG ]]; then
dialog --title "ERROR" --msgbox "$(crunch "ChangeLog.txt not \
found or not readable. Please make sure your repository \
directory is set correctly and that you have done a sync \
first.")" 10 30
return
else
cat << EOF
ERROR
No ChangeLog.txt found. Please make sure your
repository directory is set correctly and that
you have done a sync first. Exiting.
EOF
exit 1
fi
else
if [[ $DIAG ]]; then
dialog --title "$REPO_DESC ChangeLog.txt" \
--textbox ./ChangeLog.txt 0 0
else
$PAGER ./ChangeLog.txt
fi
fi
}
nvabt2n() {
# Converts a list of name-version-arch-build-tags to plain names - takes 2
# args of the input file and the output file.
local INPUT=$1
local OUTPUT=$2
local LINE PKG_NAME PKG_VER PKG_ARCH PKG_BUILD PKG_TAG
>$OUTPUT
while read LINE; do
split_pkg_name $LINE
echo $PKG_NAME >> $OUTPUT
done < $INPUT
}
selection_state() {
# When a dialog checklist widget is created, it uses a file for input.
# When the user modifies the checklist, the widget's input file is not
# modified to reflect this. This function makes this modification. It also
# may reverse the selected items in the checklist. The first argument
# determines which is done ('reverse' to reverse, anything else to
# preserve the selection state) while the second argument is the file the
# widget uses for input (to display the widget items) and the third is the
# file the widget uses for output (which reflects the user's selections).
local ACTION=$1
local MENU_FILE=$2
local SELECTION_FILE=$3
local LINE
sed -i 's/ON$/OFF/' $MENU_FILE
while read LINE; do
sed -i "/^\"*$LINE\"* /s/OFF$/ON/" $MENU_FILE
done < $SELECTION_FILE
if [[ $1 == reverse ]]; then
sed -i 's/ON$/HOLD/;s/OFF$/ON/' $MENU_FILE
sed -i 's/HOLD$/OFF/' $MENU_FILE
fi
}
mk_pkg_lists() {
# Get a list of $REPO_TAG packages and generate two lists from them. At
# least for right now, this is purely an auxilary function called from
# list_packages().
find /var/lib/pkgtools/packages/ -type f -name "*$REPO_TAG" \
-printf "%P\n" 2> /dev/null | sort > $PKG_LIST
sed 's/$/ "" OFF/' $PKG_LIST > $PKG_CHECKLIST
nvabt2n $PKG_LIST $README_LIST
}
list_packages() {
# Display installed packages with options to remove them or view their
# READMEs.
local PKG_LIST=$SBOPKGTMP/sbopkg_pkg_list
local PKG_CHECKLIST=$SBOPKGTMP/sbopkg_pkg_checklist
local README_LIST=$SBOPKGTMP/sbopkg_readme_list
local REMOVE_LIST=$SBOPKGTMP/sbopkg_remove_list
local CONFIRM_LIST=$SBOPKGTMP/sbopkg_confirm_list
mk_pkg_lists
if [[ -s $PKG_LIST ]]; then
if [[ $DIAG ]]; then
while :; do
dialog --separate-output --visit-items \
--title "Installed $REPO_NAME Packages" \
--extra-button --extra-label "View READMEs" \
--checklist "Check any packages you wish to remove." \
20 65 13 --file $PKG_CHECKLIST 2> $REMOVE_LIST
CHOICE=$?
selection_state preserve $PKG_CHECKLIST $REMOVE_LIST
case $CHOICE in
0)
if [[ -s $REMOVE_LIST ]]; then
sed 's/$/ "" ON/' $REMOVE_LIST > $CONFIRM_LIST
dialog --separate-output --visit-items --defaultno \
--title "Removepkg confirmation" \
--checklist "Remove the following packages?" \
20 65 13 \
--file $CONFIRM_LIST 2> $REMOVE_LIST
if [[ $? == 0 ]]; then
/sbin/removepkg $(cat $REMOVE_LIST)
mk_pkg_lists
read -n1 -ep "Press any key to continue: "
fi
else
break
fi
;;
3)
view_readmes "Installed packages:" $README_LIST
;;
*) break ;;
esac
done
else
if [[ $VIEW_READMES ]]; then
view_readmes "Installed packages:" $README_LIST
else
$PAGER $PKG_LIST
fi
fi
else
if [[ $DIAG ]]; then
dialog --title "No packages found" --msgbox "$(crunch_fmt "It \
appears that you have no $REPO_NAME packages \
installed.")" 8 40
fi
fi
rm -f $PKG_LIST $REMOVE_LIST $CONFIRM_LIST
}
progressbar_cli() {
# This is a simple progressbar for CLI operations.
# The code shows a bar filling like this:
# 0%[ ]
# 25%[= ]
# 50%[== ]
# 75%[=== ]
# 100%[====]
# This is meant to be an "almost drop-in" replacement for
# "dialog --gauge". The percentage data is read from stdin and the bar
# fills a screen line.
#
# If available, "tput" (part of ncurses) is used to determine the screen
# width and to hide the cursor.
local PROGRESS SCREENPROGRESS i
local SCREENWIDTH BARWIDTH
local BAR SPACES
# Initial messages
if [[ -n "$1" ]]; then
echo "[ $1 ]"
fi
if [[ -n "$2" ]]; then
crunch_fmt "$2"
fi
# Initialize the bar
# Screen size
if [[ $HAS_NCURSES ]]; then
tput civis # Hide cursor
SCREENWIDTH=$(tput cols)
else
SCREENWIDTH=80
fi
BARWIDTH=$(($SCREENWIDTH - 8))
while read PROGRESS; do
# Show the percentage
printf "\r%3s%%[" $PROGRESS
# Draw the bar
SCREENPROGRESS=$(($BARWIDTH * $PROGRESS / 100))
printf -vBAR "%${SCREENPROGRESS}s" ""
printf -vSPACES "%$(($BARWIDTH - $SCREENPROGRESS))s" ""
printf "%s%s]" "${BAR// /=}" "${SPACES// / }"
done
# Cleanup
echo
if [[ $HAS_NCURSES ]]; then
tput cnorm # Restore cursor
fi
}
progressbar() {
# This is a simple progressbar gateway, which automatically chooses
# between the "dialog" and the "cli" bars.
local MSGTITLE="$1"
local MSGTEXT="$2"
if [[ $DIAG ]]; then
local MESSAGE=$(crunch "$MSGTEXT")
local MESSAGELINES=$(echo "$MESSAGE" | fmt -66 | wc -l)
dialog --title "$MSGTITLE" --gauge "$MESSAGE" $(($MESSAGELINES + 5)) \
70 0
elif [[ ! $QUIET ]]; then
progressbar_cli "$MSGTITLE" "$MSGTEXT"
else
cat > /dev/null
fi
}
read_nonblock() {
# This is a simple non-blocking read function reading a single
# character (if available) from stdin and putting it in $1.
local STTY_STATUS=$(stty --save)
stty -icanon time 0 min 0 -echo
read $1
stty $STTY_STATUS
}
progressbar_interrupted() {
# This function checks whether the user pressed ESC
local ESC=$'\033' KEY
read_nonblock KEY
[[ "$KEY" = "$ESC" ]]
}
get_new_name() {
# Return the new package name, as for sbopkg-renames.
# If there isn't any new name, return the old name.
# $1 = the variable where to put the new name
# $2 = the old name
local NEW_NAME_VAR="$1"
local OLD_NAME="$2"
local CANDIDATE=$(
grep -h "^$OLD_NAME=" $SBOPKG_RENAMES_D/*.renames | head -n1
)
if [[ -z "$CANDIDATE" ]]; then
# No rename occurred
CANDIDATE="$OLD_NAME"
else
# The package got renamed
CANDIDATE=$(cut -d= -f2 <<< "$CANDIDATE")
fi
eval $NEW_NAME_VAR="$CANDIDATE"
}
get_old_name() {
# Return the old package name if installed, as for sbopkg-renames.
# If there isn't any old named package installed, return the new name.
# $1 = the variable where to put the old name
# $2 = the new name
local OLD_NAME_VAR="$1"
local NEW_NAME="$2"
local CANDIDATE INSTALLED
local SUBSTITUTIONS=$(
grep -h "=$NEW_NAME\$" $SBOPKG_RENAMES_D/*.renames | cut -d= -f1
)
# By default, the old name is the new name
eval $OLD_NAME_VAR=$NEW_NAME
# Set the old name to the first installed old-named package found.
# Reading from $substitutions...
while read CANDIDATE; do
[[ -z "$CANDIDATE" ]] && continue
INSTALLED=$(ls /var/lib/pkgtools/packages/ |
grep -x "$CANDIDATE-[^-]*-[^-]*-[^-]*")
if [[ -n "$INSTALLED" ]]; then
# Old-named installed package found, assume this is the correct
# old name to return.
eval $OLD_NAME_VAR=$CANDIDATE
break
fi
done <<< "$SUBSTITUTIONS"
}
check_for_updates() {
# This checks for updates to installed SBo packages. Thanks to Mauro
# Giachero for this much-improved update code and related functions!
local TEMPFILE=$SBOPKGTMP/sbopkg_updates_tempfile
local ERRORMSG=$SBOPKGTMP/sbopkg_updates_errormsg
local PROGRESSCOUNTER=0
local FIND_FLAGS="$REPO_DIR -mindepth 3 -maxdepth 3"
local NEWSB NEWINFO NEWVER
local VERSION_EXPRESSION
local UPDATELIST VERSION_FILE PROGRESSBAR_INTERRUPTED
local OLDNAME PKG_NAME PKG_VER PKG_ARCH PKG_BUILD
local VER_NUMERIC NEWVER_NUMERIC VER_NDIGITS NEWVER_NDIGIT UPDATED
local CURPKG PKGS NUMPKGS
if [[ -z $REPO_TOOL ]]; then
if [[ $DIAG ]]; then
dialog --title "ERROR" --msgbox \
"You cannot check for updates when using the $REPO_DESC." 8 40
else
crunch_fmt \
"You cannot check for updates when using the $REPO_DESC."
fi
return 1
fi
# Check to see if there are any updates to installed SBo pkgs.
check_if_repo_exists
UPDATELIST=$SBOPKGTMP/sbopkg_updatelist
rm -f $UPDATELIST $ERRORMSG
cd /var/log/packages
# Check if blacklist file exists
if [[ -s $BLACKLISTFILE ]]; then
# Remove comments (lines starting with #) and empty lines
# If there exists content(s), we will filter that out
# Otherwise, do a normal check for update procedure
BLKFILE=$(grep -v "^#" $BLACKLISTFILE | grep -v "^$")
if [[ ! -z $BLKFILE ]]; then
PKGS=$(ls *$REPO_TAG | grep -v "$BLKFILE" 2> /dev/null)
else
PKGS=$(ls *$REPO_TAG 2> /dev/null)
fi
else
PKGS=$(ls *$REPO_TAG 2> /dev/null)
fi
NUMPKGS=$(wc -w <<< "$PKGS")
VERSION_FILE=$SBOPKGTMP/sbopkg-script-version
PROGRESSBAR_INTERRUPTED=$SBOPKGTMP/sbopkg_progressbar-interrupted
if [[ -z $PKGS ]]; then
if [[ $DIAG ]]; then
dialog --title "No packages found" --msgbox "$(crunch_fmt "It \
appears that you have no $REPO_NAME packages \
installed.")" 8 40
else
echo "It appears that you have no $REPO_NAME packages installed."
fi
return 1
else
crunch_fmt "Listing installed $REPO_DESC packages and flagging \
potential updates..." >> $UPDATELIST
echo >> $UPDATELIST
{ # Grouping for the progressbar
echo 0 # Progressbar begin
for CURPKG in $PKGS; do
# Bail out if the user pressed ESC
[[ -n $interactive ]] && progressbar_interrupted && touch $PROGRESSBAR_INTERRUPTED && break
# split CURPKG into its components
split_pkg_name $CURPKG
OLDNAME=$PKG_NAME
# Manage package renames
get_new_name NAME $OLDNAME
# Find the current SlackBuild
NEWSB=$(find $FIND_FLAGS -name $NAME.SlackBuild)
if [[ -z $NEWSB ]]; then
# Maybe we're running an old repository where the rename
# didn't take place
if [[ $NAME != $OLDNAME ]]; then
NAME=$OLDNAME
NEWSB=$(find $FIND_FLAGS -name $NAME.SlackBuild)
fi
fi
# Extract the new package version
if [[ ! -z $NEWSB ]]; then
unset BUILD NEWARCH
eval NEW$(grep -m1 ^ARCH= $NEWSB) 2>/dev/null
if [[ $NEWARCH != "noarch" ]]; then
NEWARCH=$ARCH
fi
eval NEW$(grep -m1 ^BUILD= $NEWSB) 2>/dev/null
[[ -z $NEWARCH ]] && NEWARCH=unknown
# Step 1 - find the version expression
# This looks for the last instance of $OUTPUT.
# Note that part of the name can be returned by mistake,
# typically for cases such as
# makepkg [...] $OUTPUT/$PRGNAM-something-$VERSION-$ARCH-\
# $BUILD$TAG
# This is harmless, and the proper cleanup is performed in
# Step 4.
VERSION_EXPRESSION=$(tac $NEWSB | grep -m1 \$OUTPUT/ |
sed 's/[^$]*$\(.*\)$/\1/;
s/:-//;
s/^[^-]*-\(.*\)-[^-]*-[^-]*/\1/')
# Explanation of the above 'sed':
# - take from the first $ on (cuts "makepkg" with its
# options, if present
# - drop ':-', which is not present in any 12.2 script
# but is in every 13.0 script (${PKGTYPE:-tgz})
# - narrow the thing a bit deleting everything before
# the first dash or after the penultimate one.
echo "echo $VERSION_EXPRESSION" > $VERSION_FILE
# Step 2 - find the used variables and their expressions
# recursively. This fills the VERSION_FILE with the proper
# variables assignments in reversed order (first dependant,
# then dependencies)
updates_resolve_expression "$VERSION_EXPRESSION"
# Step 3 - reverse the file order
# Because dependencies must be first...
tac $VERSION_FILE > $TEMPFILE
mv $TEMPFILE $VERSION_FILE
# Step 4 - let's get the version number!
# Also, strip any residual program name token.
NEWVER=$(sh $VERSION_FILE 2> $ERRORMSG | sed 's/.*-//g')
rm -f $VERSION_FILE
# Step 5 - fixup braindead cases
# Sometimes the above doesn't work -- see cpan2tgz for 12.1
# In that case, let's trust the .info file...
[[ -z $NEWVER ]] && echo "Empty version!" >> $ERRORMSG
if [[ $(< $ERRORMSG) ]]; then
NEWINFO=$(sed 's/\.SlackBuild$/\.info/g' <<< "$NEWSB")
NEWVER=$(grep "^VERSION" $NEWINFO | cut -d\" -f2)
fi
# Compare the old $VER and the new $NEWVER
VER_NUMERIC=$(tr -c "[:digit:]" " " <<< "$PKG_VER")
NEWVER_NUMERIC=$(tr -c "[:digit:]" " " <<< "$NEWVER")
# The version number must have the same number of digits
VER_NDIGIT=$(wc -w <<< $VER_NUMERIC)
NEWVER_NDIGIT=$(wc -w <<< $NEWVER_NUMERIC)
while [[ $VER_NDIGIT -lt $NEWVER_NDIGIT ]]; do
VER_NUMERIC="$VER_NUMERIC 0"
((VER_NDIGIT++))
done
while [[ $VER_NDIGIT -gt $NEWVER_NDIGIT ]]; do
NEWVER_NUMERIC="$NEWVER_NUMERIC 0"
((NEWVER_NDIGIT++))
done
# The build number is just like the least significant version
# number
VER_NUMERIC="$VER_NUMERIC $(tr -c "[:digit:]" ' ' \
<<< "$PKG_BUILD")"
NEWVER_NUMERIC="$NEWVER_NUMERIC $(tr -c "[:digit:]" ' ' \
<<< "$NEWBUILD")"
UPDATED=$(updates_compare_versions $VER_NUMERIC \
$NEWVER_NUMERIC)
if [[ $UPDATED -eq 1 ]]; then
echo $NAME: >> $UPDATELIST
echo " POTENTIAL UPDATE" >> $UPDATELIST
echo " Installed version: " $CURPKG >> $UPDATELIST
echo " Repo version: " \
$NAME-$NEWVER-$NEWARCH-${NEWBUILD}$REPO_TAG \
>> $UPDATELIST
echo "$NAME" >> $SBOPKGTMP/sbopkg-update-queue
elif [[ $UPDATED -eq -1 ]]; then
echo $NAME: >> $UPDATELIST
echo " INSTALLED PACKAGE IS NEWER THAN REPO" \
>> $UPDATELIST
echo " Installed version: " $CURPKG >> $UPDATELIST
echo " Repo version: " \
$NAME-$NEWVER-$NEWARCH-${NEWBUILD}$REPO_TAG \
>> $UPDATELIST
echo "-$NAME" >> $SBOPKGTMP/sbopkg-update-queue
elif [[ $PKG_VER != $NEWVER ]]; then
echo $NAME: >> $UPDATELIST
echo " UNCLASSIFIED VERSION CHANGE" \
>> $UPDATELIST
echo " Installed version: " $CURPKG >> $UPDATELIST
echo " Repo version: " \
$NAME-$NEWVER-$NEWARCH-${NEWBUILD}$REPO_TAG \
>> $UPDATELIST
echo "-$NAME" >> $SBOPKGTMP/sbopkg-update-queue
else
if [[ $DEBUG_UPDATES -eq 2 ]]; then
echo $NAME: >> $UPDATELIST
echo " No update." >> $UPDATELIST
fi
fi
if [[ $(< $ERRORMSG) ]]; then
echo " Note: repo version not obtainable by" \
"standard method, may be inaccurate." >> $UPDATELIST
fi
else
if [[ $DEBUG_UPDATES -ge 1 ]]; then
echo $NAME: >> $UPDATELIST
echo " Not in the repository." >> $UPDATELIST
fi
fi
rm -f $ERRORMSG
# Progress indicator, for the progressbar
(( PROGRESSCOUNTER += 1 ))
echo $(($PROGRESSCOUNTER * 100 / $NUMPKGS))
done
} | progressbar "Checking for potential updates" "This may take\
a few moments. Press <ESC> to abort."
echo >> $UPDATELIST
echo "Potential update list complete." >> $UPDATELIST
fi
if [[ ! -f $PROGRESSBAR_INTERRUPTED ]]; then
if [[ $DIAG ]]; then
dialog --title "Viewing potential updates" --textbox $UPDATELIST \
0 0
else
cat $UPDATELIST
fi
# Permanent log of the updatelist is saved when DEBUG_UPDATES is
# enabled.
if [[ $DEBUG_UPDATES -ge 1 ]]; then
cp $UPDATELIST $SBOPKGTMP/sbopkg-debug-updatelist
else
rm $UPDATELIST
fi
else
rm -f $PROGRESSBAR_INTERRUPTED
fi
}
updates_resolve_expression() {
# Find the used variables and their expressions recursively
# Variables == any string made up by letters, digits and underscore
# This criteria may have false positives, which don't matter since
# these aren't assigned to in the SlackBuild.
# 1st parameter == expression (right hand side of FOO=BAR)
local EXPRESSION_VARIABLES=$(echo $1 | tr -c "[:alnum:]_" " ")
local VAR
local ASSIGNMENT
for VAR in $EXPRESSION_VARIABLES; do
ASSIGNMENT=$(tac $NEWSB | grep "^$VAR=")
if [[ ! -z "$ASSIGNMENT" ]] && ! grep -q "^$VAR=" \
$VERSION_FILE; then
echo "$ASSIGNMENT" >> $VERSION_FILE
updates_resolve_expression "$(cut -d= -f2- <<< "$ASSIGNMENT")"
fi
done
}
updates_compare_versions() {
# Compare numeric versions
# Takes 2N arguments, where N is the number of numbers (...)
# composing the version number.
# E.g. if the two packages are of version 1.2.3 build 7 and
# 1.2.50 build 4, the argument list is
# 1 2 3 7 1 2 50 4
# Prints -1 if the "left" package is newer (not an update), 0 if
# the version is unchanged, 1 if the "left" package is older.
local COUNT=$(($# / 2))
local i RESULT=0
local LEFT RIGHT
for ((i=1; i<=$COUNT; i++)); do
eval LEFT=\${$i}
eval RIGHT=\${$(($i + $COUNT))}
if [[ 10#$LEFT -lt 10#$RIGHT ]]; then
RESULT=1
break
elif [[ 10#$LEFT -gt 10#$RIGHT ]]; then
RESULT=-1
break
fi
done
echo $RESULT
}
get_category_list() {
# This function displays the list of SBo categories in the dialog.
local DIR CAT
check_if_repo_exists
cd $REPO_DIR
rm -f $SBOPKGTMP/sbopkg_category_list
DIR=( */ )
if [[ -n $DIR ]]; then
for CAT in ${DIR[*]%/}; do
echo "$CAT \"Browse the $CAT category\"" >> \
$SBOPKGTMP/sbopkg_category_list
done
fi
}
checkout_rsync_branch() {
# This function makes sure that in $REPO_DIR there's the currently
# selected branch.
# This is the implementation for rsync repositories, where branches are
# simple subdirectories
REPO_DIR=$REPO_DIR/$REPO_BRANCH
}
checkout_git_branch() {
# This function makes sure that in $REPO_DIR there's the currently
# selected branch.
# This is the implementation for git repositories, which is relatively
# complex because we have to manage local changes.
local CURRENT_BRANCH NEW_BRANCH NEW_STASH
local COMMAND_OUTPUT=$SBOPKGTMP/sbopkg_git_checkout_output
rm -f $COMMAND_OUTPUT
# Make sure the repository is there
[[ -d $REPO_DIR/.git ]] || return 0
# No need to checkout if the right branch is already there
NEW_BRANCH=$(cut -d@ -f2 <<< $REPO_LINK)
cd $REPO_DIR
CURRENT_BRANCH=$(git branch | grep '^*')
CURRENT_BRANCH=${CURRENT_BRANCH//'* '}
[[ $CURRENT_BRANCH = $NEW_BRANCH ]] && return 0
# So we need to checkout the branch. First off, let's clean the tree
echo "*/*/*.sbopkg" > .gitignore
git clean -d -f > $COMMAND_OUTPUT
git reset --hard HEAD >> $COMMAND_OUTPUT
# Let's save the current user customizations to a stash
git add . >> $COMMAND_OUTPUT
git stash save sbopkg-auto-$CURRENT_BRANCH >> $COMMAND_OUTPUT
# Checkout the user-requested branch
git checkout $NEW_BRANCH >> $COMMAND_OUTPUT 2>&1
# Pop the stash of this branch, if any
NEW_STASH=$(git stash list | grep ": sbopkg-auto-$NEW_BRANCH\$" |
cut -d: -f1)
if [[ -n $NEW_STASH ]]; then
git stash pop $NEW_STASH >> $COMMAND_OUTPUT
# Make sure no changes are staged
git reset HEAD >> $COMMAND_OUTPUT
fi
# Create a changelog
# (it makes no sense to have one tracked in a git repo)
if [[ ! -f ChangeLog.txt ]]; then
git log --pretty=format:"%cd%n%s%n%b" > ChangeLog.txt
fi
}
set_repo_vars() {
# Set REPO_{DESC,TAG,TOOL,LINK,DIR} according to $REPO_{NAME,BRANCH}.
# Returns nonzero if no match is found.
local i
# Make sure we don't return old values with an invalid input
unset REPO_DESC REPO_TAG REPO_TOOL REPO_LINK REPO_DIR REPO_GPG
for ((i=0; i<${#REPOSITORIES[@]}; i+=$REPOS_FIELDS)); do
if [[ ( ${REPOSITORIES[$i]} = $REPO_NAME || $REPO_NAME = "" ) &&
${REPOSITORIES[$((i + 1))]} = $REPO_BRANCH ]]; then
REPO_NAME=${REPOSITORIES[i]}
REPO_DESC=${REPOSITORIES[$((i + 2))]}
REPO_TAG=${REPOSITORIES[$((i + 3))]}
REPO_TOOL=${REPOSITORIES[$((i + 4))]}
REPO_LINK=${REPOSITORIES[$((i + 5))]}
REPO_GPG=${REPOSITORIES[$((i + 6))]}
REPO_DIR=$REPO_ROOT/$REPO_NAME
if [[ $REPO_TOOL = "rsync" || $REPO_TOOL = "git" ]]; then
checkout_${REPO_TOOL}_branch
fi
# If the user specified a custom tag, use that one instead.
[[ -n $TAG ]] && REPO_TAG=$TAG
return 0
fi
done
# Repository/branch not found
return 1
}
list_repos() {
echo "Valid options are:" >&2
for ((i=0; i<${#REPOSITORIES[@]}; i+=$REPOS_FIELDS)); do
echo -en "${REPOSITORIES[$i]}/${REPOSITORIES[(($i + 1))]}\\t" >&2
echo "(${REPOSITORIES[(($i + 2))]})" >&2
done
}
select_repository() {
# Create menu and list the sbopkg-supported repositories for
# user to choose from.
local OLD_REPO_NAME=$REPO_NAME
local OLD_REPO_BRANCH=$REPO_BRANCH
while :; do
eval dialog --visit-items --cancel-label "Back" --title '"Repository Selection"' \
--menu '"$(crunch "You are currently working with the \
$REPO_DESC. If you would like to work with a different \
one, please select it from the list below. If not, choose \
< Back >.")"' 21 70 11 \
$(
for ((i=0; i<${#REPOSITORIES[@]}; i+=$REPOS_FIELDS)); do
echo \"${REPOSITORIES[$i]} \(${REPOSITORIES[$((i+1))]}\)\"
echo \"${REPOSITORIES[$((i+2))]}\"
done
) 2> $SBOPKGTMP/sbopkg_version_selection
if [[ $? != 0 ]]; then
break
fi
eval $(sed 's:^\(.*\) (\(.*\))$:REPO_NAME=\1;REPO_BRANCH=\2:g' \
$SBOPKGTMP/sbopkg_version_selection)
set_repo_vars
if dir_init; then
dialog --title "Save this setting?" --defaultno --yesno \
"$(crunch "Would you like to save this repository setting \
in the user's $HOME/.sbopkg.conf file? (One will be created \
if it is not found).\n\nPress <Yes> to save in the user's \
$HOME/.sbopkg.conf or press <No> to continue without saving, \
making this a temporary change only.")" 12 60
if [[ $? != 0 ]]; then
break
fi
if [[ -e $HOME/.sbopkg.conf ]]; then
sed -i '/^REPO_NAME=.*$/d' $HOME/.sbopkg.conf
sed -i '/^REPO_BRANCH=.*$/d' $HOME/.sbopkg.conf
fi
echo "REPO_NAME=$REPO_NAME" >> $HOME/.sbopkg.conf
echo "REPO_BRANCH=$REPO_BRANCH" >> $HOME/.sbopkg.conf
break
else
REPO_NAME=$OLD_REPO_NAME
REPO_BRANCH=$OLD_REPO_BRANCH
set_repo_vars
fi
done
rm -f $SBOPKGTMP/sbopkg_version_selection
}
app_files_chooser() {
# List the files of a directory, and view the selected ones.
# This function takes a single argument (the directory whose files are to
# be listed).
local DIR=$1
local DEFAULTITEM
local AFS=$SBOPKGTMP/sbopkg_app_files_selection
local AFM=$SBOPKGTMP/sbopkg_app_files_menu
local TITLE="${DIR##*/} files"
while :; do
find $DIR -type f -printf "\"%P\" \"\"\n" | sort > $AFM
dialog --visit-items --ok-label "View" --cancel-label "Back" --title "$TITLE" \
--default-item "$DEFAULTITEM" --menu "$(crunch "Please choose \
the file you would like to view or press <Back> to go back.")"\
15 45 7 --file $AFM 2> $AFS
if [[ $? != 0 ]]; then
rm -f $AFS $AFM
return
fi
DEFAULTITEM=$(< $AFS)
view_app_file $DIR "$DEFAULTITEM"
done
}
view_app_file() {
# Decode and view the file $2 in the directory $1
local DIR=$1
local FILE="$2"
local PLAIN
local AFSP=$SBOPKGTMP/sbopkg_app_files_selection_parsed
local RESTORED_README=$SBOPKGTMP/restored_readme
cd $DIR
case $FILE in
slack-desc )
sed -n "/^${DIR##*/}: */s///p" slack-desc > $AFSP ;;
*tar.gz | *tar.bz2 | *t?z )
tar tvf $FILE > $AFSP ;;
*gz ) zcat $FILE > $AFSP ;;
*bz2 ) bzcat $FILE > $AFSP ;;
* ) PLAIN=yes ;;
esac
if [[ "$PLAIN" == yes ]]; then
# If it's a README, splice the damned deps back into its display.
FILENAME=$FILE
if [[ $FILE == README ]]; then
{ cat README; echo; grep '^REQUIRES=' *.info; } > $RESTORED_README
FILE=$RESTORED_README
fi
dialog --exit-label "OK" --title "$FILENAME" --textbox "$FILE" 0 0
else
dialog --exit-label "OK" --title "Parsed contents of $FILE" \
--textbox "$AFSP" 0 0
fi
rm -f $AFSP
}
info_item() {
# This function shows the menu for each package where the user can see
# certain information or build the package.
# Returns 0 unless the user asked to jump back to the main menu.
local OLDPKG CATEGORY SHORTPATH CURVERSION CURARCH CURBUILD
local CURAPP OUTPUTFILES
local DEFAULTITEM
local CURPACKAGE INSTALLEDPACKAGE MENUPACKAGE TITLEPACKAGE
local CHOICE PARSED_SLACK_DESC CHKRETVAL
local APP="$(< $SBOPKGTMP/sbopkg_item_selection)"
local RETVAL=0
# We need to check and see if the APP has ever been renamed.
get_old_name OLDPKG $APP
CATEGORY=$(< $SBOPKGTMP/sbopkg_category_selection)
SHORTPATH=$REPO_DIR/$CATEGORY/$APP
CURVERSION=$(grep VERSION $SHORTPATH/$APP.info |
cut -d= -f2 | sed s/\"//g)
eval CUR$(grep -m1 ^ARCH= $SHORTPATH/$APP.SlackBuild) 2>/dev/null
if [[ $CURARCH != "noarch" ]]; then
CURARCH=$ARCH
fi
eval CUR$(grep -m1 ^BUILD= $SHORTPATH/$APP.SlackBuild)
[[ -z $CURARCH ]] && CURARCH=unknown
while :; do
# we use GNU grep extensions rather than egrep to avoid issues with
# the '+' metacharacter which can be found in package names
INSTALLEDPACKAGE=$(ls /var/lib/pkgtools/packages/ |
grep "^\($APP\|$OLDPKG\)-[^-]*-[^-]*-[^-]*\$")
# Only get the first package (not that the same package should be
# installed more than once on a sane system...)
INSTALLEDPACKAGE=$(head -n 1 <<< "$INSTALLEDPACKAGE")
# Find the available package to install, if any, using several
# "strictness levels" (to pick the right one if available, but falling
# back to "less right" alternatives when appropriate)
OUTPUTFILES=$(ls -1 $OUTPUT)
CURPACKAGE=$( \
grep "^$APP-$CURVERSION-$CURARCH-$CURBUILD$REPO_TAG\\.t.z\$" \
<<< "$OUTPUTFILES")
[[ -z $CURPACKAGE ]] && CURPACKAGE=$( \
grep "^$APP-$CURVERSION-[^-]*-$CURBUILD$REPO_TAG\\.t.z\$" \
<<< "$OUTPUTFILES")
[[ -z $CURPACKAGE ]] && CURPACKAGE=$( \
grep "^$APP-$CURVERSION-[^-]*-[^-]*$REPO_TAG\\.t.z\$" \
<<< "$OUTPUTFILES")
[[ -z $CURPACKAGE ]] && CURPACKAGE=$( \
grep "^$APP-[^-]*-[^-]*-[^-]*$REPO_TAG\\.t.z\$" \
<<< "$OUTPUTFILES")
if [[ -z $CURPACKAGE ]]; then
unset MENUPACKAGE
else
CURPACKAGE=$(tail -n 1 <<< "$CURPACKAGE")
MENUPACKAGE="Install $CURPACKAGE"
fi
if [[ -z $INSTALLEDPACKAGE ]]; then
TITLEPACKAGE="$APP (Not Installed)"
else
TITLEPACKAGE="$APP (Installed: $INSTALLEDPACKAGE)"
fi
dialog --visit-items --default-item "$DEFAULTITEM" \
--title "$APP ($CURVERSION-$CURARCH-$CURBUILD$REPO_TAG)" \
--backtitle "$TITLEPACKAGE" --extra-button --extra-label "Back" \
--cancel-label "Main Menu" --menu \
"$(crunch "Please choose an item or press <Back> to go back \
or press <Main Menu> to return to the main menu.\n")" \
20 62 12 \
"README" "View the README file" \
"Info" "View the .info file" \
"SlackBuild" "View the SlackBuild file" \
"More Files" "Choose any file to display" \
"Custom" "Customize the .info or SlackBuild" \
"Remove" "Remove $APP sources in cache" \
"Options" "Edit Build Options/Flavors" \
"Check GPG" "Check the GPG signature of the $REPO_NAME tarball" \
"Extract" "Re-extract the $REPO_NAME tarball" \
"Queue" "Add $APP to queue" \
"Process" "Download/build/install $APP" \
$MENUPACKAGE \
2> $SBOPKGTMP/sbopkg_info_selection
case $? in
1 ) # Return to Main Menu
RETVAL=1; break ;;
3 ) # Back
break ;;
0 ) # OK
DEFAULTITEM="$(< $SBOPKGTMP/sbopkg_info_selection)"
CATEGORY="$(< $SBOPKGTMP/sbopkg_category_selection)"
case $DEFAULTITEM in
README )
view_app_file $SHORTPATH README
;;
Info )
view_app_file $SHORTPATH $APP.info
;;
SlackBuild )
view_app_file $SHORTPATH $APP.SlackBuild
;;
"More Files" ) app_files_chooser $SHORTPATH ;;
Custom )
if [[ ! -z $REPO_GPG ]]; then
check_gpg $SHORTPATH
if [[ $? == 1 ]]; then
RETVAL=1
break
fi
extract_tarball $SHORTPATH $REPO_DIR/$CATEGORY
if [[ $? == 1 ]]; then
RETVAL=1
break
fi
fi
customize_item
;;
Remove ) remove_sources_for_app $SHORTPATH/$APP.info ;;
Options ) add_options $APP ;;
"Check GPG" )
if [[ ! -z $REPO_GPG ]]; then
check_gpg $SHORTPATH
CHKRETVAL=$?
if [[ $CHKRETVAL == 0 ]]; then
dialog --title "OK" \
--msgbox "GPG check passed." 6 25
elif [[ $CHKRETVAL == 1 ]]; then
RETVAL=1
break
fi
else
dialog --title "ERROR" --msgbox "$(crunch "GPG \
checks are not enabled for the $REPO_NAME \
repository.")" 8 30
fi
;;
Extract )
if [[ ! -z $REPO_GPG ]]; then
extract_tarball $SHORTPATH $REPO_DIR/$CATEGORY
if [[ $? == 0 ]]; then
dialog --title "Done" \
--msgbox "$(crunch "The \
tarball has been extracted.")" 8 30
else
RETVAL=1
break
fi
else
dialog --title "ERROR" --msgbox "$(crunch "GPG \
tarballs are not available for the \
$REPO_NAME repository.")" 8 30
fi
;;
Queue ) add_item_to_queue $APP ;;
Process )
echo "$APP" > $STARTQUEUE
start_dialog_queue ;;
Install )
if [[ ! -e $OUTPUT/$CURPACKAGE ]]; then
continue;
fi
install_package $OUTPUT $CURPACKAGE | tee $TMPLOG
read -n1 -ep "Press any key to continue: "
if [[ $KEEPLOG ]]; then
cat $TMPLOG >> $LOGFILE
fi
rm $TMPLOG
;;
esac
;;
* ) # ESC
break ;;
esac
done
rm -f $SBOPKGTMP/sbopkg_info_selection
return $RETVAL
}
extract_tarball() {
# Re-extract the $APP tarball on top of local directory. Can be used
# if tarball fails GPG check.
local DELPKG=$1
local DESTINATION=$2
local DELNAME=$(basename $DELPKG)
if [[ ! -e $DELPKG.tar.gz ]]; then
if [[ $DIAG ]]; then
dialog --title "Error" --msgbox "$(crunch "No $REPO_NAME \
$DELNAME tarball found.")" 8 40
else
crunch_fmt "ERROR: No $REPO_NAME $DELNAME tarball found."
fi
return 1
fi
tar -C $DESTINATION -zxof $DELPKG.tar.gz
return 0
}
check_gpg() {
# Check the .asc signature of the tarball from info_item menu
local CHKPKG=$1
local GPGNAME=$(basename $CHKPKG)
if [[ ! -e $CHKPKG.tar.gz ]]; then
dialog --title "GPG check error" --msgbox "$(crunch "No $REPO_NAME \
$GPGNAME tarball found.")" 8 40
return 1
fi
if ! gpg --verify $CHKPKG.tar.gz.asc > /dev/null 2>&1; then
dialog --title "WARNING" --yesno "$(crunch "GPG CHECK FAILED!\n\n \
Would you like to delete the $GPGNAME directory and tarball \
so you can perform a new sync? If so, all local changes to \
the files in the $GPGNAME directory will be lost and you will \
be returned to the main menu. Press <Yes> to delete or <No> \
to skip.")" 0 0
if [[ $? == 0 ]]; then
rm -rf $CHKPKG; rm $CHKPKG.*
dialog --title "Done" --msgbox \
"The directory and tarball have been deleted." 8 30
return 1
fi
return 2
else
return 0
fi
}
customize_item() {
# This function shows the menu for customizing the SlackBuild
# and .info file.
local DEFAULTITEM
while :; do
dialog --visit-items --default-item "$DEFAULTITEM" --title "$APP Customization" \
--cancel-label "Back" --menu \
"Please choose an item or press <Back> to go back.\n" 13 75 6 \
"Edit SlackBuild" "Create and edit a local copy of the SlackBuild" \
"Delete SlackBuild" "Delete the local copy of the SlackBuild" \
"Diff SlackBuild" "Compare the local and the original SlackBuild" \
"Edit Info" "Create and edit a local copy of the .info file" \
"Delete Info" "Delete the local copy of the .info file" \
"Diff Info" "Compare the local and the original .info file" \
2> $SBOPKGTMP/sbopkg_custom_selection
if [[ $? = 0 ]]; then
DEFAULTITEM="$(< $SBOPKGTMP/sbopkg_custom_selection)"
case $DEFAULTITEM in
"Edit SlackBuild" )
edit_local_file SlackBuild $SHORTPATH $APP
;;
"Delete SlackBuild" )
delete_local_file SlackBuild $SHORTPATH $APP
;;
"Diff SlackBuild" )
diff_local_file SlackBuild $SHORTPATH $APP
;;
"Edit Info" )
edit_local_file info $SHORTPATH $APP
;;
"Delete Info" )
delete_local_file info $SHORTPATH $APP
;;
"Diff Info" )
diff_local_file info $SHORTPATH $APP
;;
esac
else # Cancel or ESC
rm -f $SBOPKGTMP/sbopkg_custom_selection
break
fi
done
}
browse_categories() {
# This function iterates through the category list until one is
# chosen.
local DEFAULTITEM
if [[ -z $(ls -A $REPO_DIR 2> /dev/null) ]]; then
if [[ $DIAG ]]; then
dialog --title "ERROR" --msgbox "$(crunch "Repository seems to \
be empty. Please make sure your repository directory is set \
correctly and that you have done a sync first.")" 10 30
continue
fi
fi
get_category_list
while :; do
dialog --visit-items --default-item "$DEFAULTITEM" --cancel-label "Back" \
--title "Choose a category" --backtitle \
"$(eval echo $BACKTITLE)" \
--menu "Please select a category or press <Back> to go back." \
23 70 15 --file $SBOPKGTMP/sbopkg_category_list \
2> $SBOPKGTMP/sbopkg_category_manual_selection
if [[ $? != 0 ]]; then
break
fi
DEFAULTITEM=$(< $SBOPKGTMP/sbopkg_category_manual_selection)
gen_search_package '*' $DEFAULTITEM || break
done
rm -f $SBOPKGTMP/sbopkg_category_manual_selection
rm -f $SBOPKGTMP/sbopkg_category_list
}
view_cache_dir() {
# This function displays the contents of $SRCDIR.
ls -A $SRCDIR | sed 's/.*/"&"/g' > $SBOPKGTMP/sbopkg_app_sources
remove_files $SRCDIR "sources" $SBOPKGTMP/sbopkg_app_sources OFF
}
view_perm_log() {
# This function displays the contents of the permanent build log,
# which is kept if KEEPLOG is set to YES in the config file.
local VAR_NOTICE HEIGHT
if [[ ! $KEEPLOG ]]; then
dialog --title "NOTICE" --msgbox "$(crunch "To use this feature, \
please make sure KEEPLOG is set to YES in the configuration \
file.")" 10 30
return 0
else
if [[ ! -e $LOGFILE ]]; then
dialog --title "NOTICE" --msgbox "$(crunch "No permanent log \
found.")" 5 30
return 0
else
dialog --title "Displaying $LOGFILE" --textbox $LOGFILE 0 0
dialog --title "Keep Log?" --yes-label "Keep" \
--no-label "Delete" --yesno "$(crunch "Would you like to \
keep the permanent build log $LOGFILE?")" 6 50
if [[ $? == 1 ]]; then
rm -f $LOGFILE
dialog --title "Done" --msgbox \
"The build log has been deleted." 8 30
continue
fi
fi
fi
}
empty_queue() {
# This function tests whether the temporary queue is empty.
if [[ ! -e $TMPQUEUE ]]; then
if [[ $DIAG ]]; then
dialog --title "Empty Queue" --msgbox \
"The queue is empty." 8 30
else
echo "The queue is empty."
fi
return 0
else
return 1
fi
}
sort_queue() {
# This function sorts the queue in $TMPQUEUE.
local PARTIALSORT=$SBOPKGTMP/sbopkg_sort_tempfile
local TMPSORTQUEUE=$SBOPKGTMP/sbopkg-tmp-sort-queue
local CHOICE
local SELECTED
local DEFAULTITEM
empty_queue && return
local PKGSCOUNT=$(wc -l < $TMPQUEUE)
cp $TMPQUEUE $TMPSORTQUEUE
while :; do
dialog --visit-items --title "Sort Queue" --ok-label "Up" \
--extra-button --extra-label "Down" \
--cancel-label "OK" \
--help-button --help-label "Reverse" \
--default-item "$DEFAULTITEM" \
--menu "$(crunch "Use the <Up/Down> buttons to sort the queue \
items, press <Reverse> to reverse the queue items, press \
<OK> when done, or press <ESC> to abort \
changes.")" 30 50 14 \
$(nl $TMPSORTQUEUE | sed 's:^ *\([0-9]* *[^ ]*\) .*$:\1:') \
2> $SBOPKGTMP/sbopkg-ans-sort
CHOICE=$?
SELECTED=$(< $SBOPKGTMP/sbopkg-ans-sort)
DEFAULTITEM=$SELECTED
case $CHOICE in
0 ) # Up
if [[ $SELECTED -eq 1 ]]; then
continue
fi
head -n $(($SELECTED-2)) $TMPSORTQUEUE > $PARTIALSORT
head -n $(($SELECTED)) $TMPSORTQUEUE |
tail -n 1 >> $PARTIALSORT
head -n $(($SELECTED-1)) $TMPSORTQUEUE |
tail -n 1 >> $PARTIALSORT
tail -n $(($PKGSCOUNT-$SELECTED)) $TMPSORTQUEUE >> \
$PARTIALSORT
mv $PARTIALSORT $TMPSORTQUEUE
DEFAULTITEM=$(($SELECTED-1))
continue
;;
1 ) # OK
mv $TMPSORTQUEUE $TMPQUEUE
break
;;
3 ) # Down
if [[ $SELECTED -eq $PKGSCOUNT ]]; then
continue
fi
head -n $(($SELECTED-1)) $TMPSORTQUEUE > $PARTIALSORT
head -n $(($SELECTED+1)) $TMPSORTQUEUE |
tail -n 1 >> $PARTIALSORT
head -n $(($SELECTED)) $TMPSORTQUEUE |
tail -n 1 >> $PARTIALSORT
tail -n $(($PKGSCOUNT-$SELECTED-1)) $TMPSORTQUEUE >> \
$PARTIALSORT
mv $PARTIALSORT $TMPSORTQUEUE
DEFAULTITEM=$(($SELECTED+1))
continue
;;
2 ) tac $TMPSORTQUEUE > $PARTIALSORT
mv $PARTIALSORT $TMPSORTQUEUE
continue
;;
* ) # Cancel or ESC
rm -f $TMPSORTQUEUE
break
;;
esac
done
rm -f $SBOPKGTMP/sbopkg-ans-sort
continue
}
queue_dir_lister() {
# This function produces a checklist from the contents of the QUEUEDIR and
# takes two arguments - the title and the text of the widget - and makes
# the selected item(s) from the listing available as USERQUEUE
local QFS=$SBOPKGTMP/sbopkg_queue_files_selection
local QFM=$SBOPKGTMP/sbopkg_queue_files_menu
# Note: the trailing slash ensures that this works fine even if $QUEUEDIR
# is a symlink to the actual repository.
find $QUEUEDIR/ -type f -name '*.sqf' -printf "\"%P\" \"\" off\n" \
-maxdepth 1 | sed -e 's/.sqf//' | sort > $QFM
if [[ -z $(< $QFM) ]]; then
if [[ $DIAG ]]; then
dialog --title "ERROR" --msgbox "$(crunch "The queue directory \
$QUEUEDIR is empty.")" 8 30
rm -f $QFM
return 1
fi
fi
# The --default item doesn't work on deletions and renames (because the
# variable expands to a no-longer existing file) but you can't give it an
# index argument, unfortunately
dialog --visit-items --title "$1" --default-item "${USERQUEUE##*/}" \
--cancel-label "Back" --checklist "$2" 20 40 8 --file $QFM 2> $QFS
if [[ $? != 0 ]]; then
# unset this so there's no left over junk and the loop from the
# calling functions doesn't kick in when this returns to them
unset USERQUEUE
else
USERQUEUE=( $(< $QFS) )
fi
rm -f $QFM $QFS
return 0
}
can_skip_line() {
# This function reads in a line and checks if it is blank or starts with a
# comment.
echo $1 | grep "^#" > /dev/null
if [[ $? == 0 ]]; then
return 0
fi
if [[ "$1" == "" ]]; then
return 0
fi
return 1
}
load_user_queue() {
# This function loads a user's specified saved queue and merges it
local USERQUEUE_LOCK=$SBOPKGTMP/sbopkg_user_queue.lck
local MISSING_LIST_FILE=$SBOPKGTMP/sbopkg_addall_missing
rm -f $MISSING_LIST_FILE
queue_dir_lister "Load Queue" "$(crunch "Select the queue(s) you \
wish to load and choose <OK> or choose <Back> to \
leave this menu.")" || return 1
for ((i=0; i<${#USERQUEUE[*]}; i++)); do
FILE=$QUEUEDIR/${USERQUEUE[$i]//'"'/}
FILE="$FILE.sqf"
if [[ -r $FILE ]]; then
# this inhibits add_item_to_queue's msgbox for each added app
touch $USERQUEUE_LOCK
echo "Reading the queuefile, please be patient..."
parse_queue $FILE
if [[ -f $MISSING_LIST_FILE ]]; then
dialog --title "Packages not found" \
--exit-label OK --textbox $MISSING_LIST_FILE 0 0
fi
LAST_USER_QUEUE_ON_DISK=$FILE
rm -f $USERQUEUE_LOCK $MISSING_LIST_FILE
else
dialog --title "ERROR" --msgbox \
"$FILE is not readable or does not exist" 0 0
return 1
fi
done
}
delete_user_queue() {
# This function deletes queues
queue_dir_lister "Delete Queue" "$(crunch "Select the queue(s) you \
wish to delete and choose <OK> or choose <Back> to \
leave this menu.")" || return 1
for ((i=0; i<${#USERQUEUE[*]}; i++)); do
FILE=$QUEUEDIR/${USERQUEUE[$i]//'"'/}
FILE="$FILE.sqf"
if ! rm -f $FILE 2> /dev/null; then
dialog --title "ERROR" --msgbox \
"You do not have permission to remove $FILE" 0 0
return 1
fi
done
}
validate_queue_name() {
# Validate the queue name stored in the file $1.
# Shows an error message and returns nonzero in case of invalid queue
# name.
local QF="$1"
if grep -q [^[:alnum:]_-] $QF; then
# this doesn't prevent the user from putting 'dumb"filename' in the
# directory manually, but helps prevent breaking sbopkg from sbopkg -
# and I could allow more characters, but these should be enough
dialog --title "ERROR" --msgbox "$(crunch "Sorry, \
but this interface supports filenames containing \
only alphanumeric characters, dashes, and \
underscores.")" 0 0
return 1
fi
return 0
}
rename_user_queue() {
# This function renames queues
local QRN=$SBOPKGTMP/sbopkg-queue-rename
local NEWNAME COUNTER FILE
queue_dir_lister "Rename Queue" "$(crunch "Select the queue(s) you \
wish to rename and choose <OK> or choose <Back> to \
leave this menu.")" || return 1
# I have to assign to this because I shrink the array later
COUNTER=${#USERQUEUE[*]}
for ((i=0; i<$COUNTER; i++)); do
FILE=$QUEUEDIR/${USERQUEUE[$i]//'"'/}
FILE="$FILE.sqf"
if [[ -w ${FILE%/*} ]]; then
# This loops so the user can be brought back to the inputbox on a
# failure (continue) or back to the dir lister on success (break)
while :; do
dialog --visit-items --title "Rename Queue" \
--inputbox "Enter the new filename for ${USERQUEUE[$i]}" \
0 0 $NEWNAME 2> $QRN
NEWNAME="$(< $QRN)"
if [[ $? == 0 ]]; then
if ! validate_queue_name $QRN; then
continue
elif [[ -f $QUEUEDIR/$NEWNAME ]]; then
dialog --title "ERROR" --msgbox "$(crunch "File \
exists. Please choose another name.")" 0 0
continue
else
mv "$FILE" "$QUEUEDIR/$NEWNAME.sqf"
break
fi
else
continue 2
fi
done
# I've already forgotten why this is here, but it was important
unset USERQUEUE[$i]
else
dialog --title "ERROR" --msgbox \
"You do not have permission to rename $USERQUEUE" 0 0
return 1
fi
done
}
stripcom() {
# This function removes comments and blank lines from the file given as
# the argument. It deletes lines beginning with zero or more whitespaces
# and a comment symbol, strips any text after whitespace and a comment
# symbol, and deletes blank or whitespace-only lines.
local FILE="$1"
sed '
/^[ \t]*#/d
s/[ \t][ \t]*#.*//
/^[ \t]*$/d
' $FILE
}
save_user_queue() {
# This function saves the queue to the filename the user specifies.
# If --end is specified as first parameter, assume that the user is
# exiting sbopkg and that this call is about saving the currently active
# queue. In that case, show the filename dialog only if there actually is
# an active queue, and return silently otherwise.
local SAVEQUEUE=$SBOPKGTMP/sbopkg-tmpsave-queue
local USERQUEUE=$SBOPKGTMP/sbopkg-user-queue
local QUEUELIST=$SBOPKGTMP/sbopkg_queue_list
local DEFAULT MSG USERQUEUE_NAME i
local USERQUEUE_NAME PICK SAVENAME SAVEONOFF
rm -f $SAVEQUEUE
# Reading from $TMPQUEUE...
[[ -s $TMPQUEUE ]] && while read PICK; do
SAVENAME=$(cut -d ' ' -f1 <<< "$PICK")
SAVEONOFF=$(sed 's:^.* \([^ ]*\)$:\1:' <<< "$PICK")
if [[ $SAVEONOFF =~ [oO][nN] ]]; then
echo $SAVENAME >> $SAVEQUEUE
else
echo "-$SAVENAME" >> $SAVEQUEUE
fi
done < $TMPQUEUE
if [[ $1 == "--end" ]]; then
if [[ ! -s $TMPQUEUE ]]; then
return 0
elif [[ -f $LAST_USER_QUEUE_ON_DISK ]] &&
diff <(stripcom $LAST_USER_QUEUE_ON_DISK) $SAVEQUEUE &> \
/dev/null; then
# The active queue is unchanged since the last loaded/saved one
return 0
elif [[ -f $QUEUELIST ]] && diff $QUEUELIST $TMPQUEUE \
&> /dev/null; then
return 0
else
MSG=$(crunch "A current queue is active. Please enter the \
filename you wish to save your queue as or choose <Cancel> \
to discard it")
# Find an unused automatic file name
i=0
while [[ -f $QUEUEDIR/sbopkg-autosave-$i.sqf ]]; do
(( i++ ))
done
DEFAULT=sbopkg-autosave-$i
fi
else
if empty_queue; then
return 0
else
MSG=$(crunch "Please enter the filename you wish to save your \
queue as:")
fi
fi
while :; do
dialog --visit-items --title "Save Queue" --inputbox "$MSG" 10 50 $DEFAULT \
2> $USERQUEUE
if [[ $? == 0 ]]; then
if [[ ! -s $USERQUEUE ]]; then
continue
fi
USERQUEUE_NAME="$(< $USERQUEUE)"
DEFAULT="${USERQUEUE_NAME##*/}"
if ! validate_queue_name $USERQUEUE; then
continue
fi
if [[ -e $USERQUEUE_NAME.sqf ]]; then
dialog --title "ERROR" --yesno "$(crunch "Another file \
with that name already exists. Press <Yes> to \
continue and overwrite the other file (keep in mind that \
the active queue will not preserve any comments from an \
on-disk queue), or press <No> to cancel.")" 10 50
if [[ $? != 0 ]]; then
continue
fi
fi
if cp $SAVEQUEUE $QUEUEDIR/$USERQUEUE_NAME.sqf; then
cp $TMPQUEUE $QUEUELIST
LAST_USER_QUEUE_ON_DISK=$QUEUEDIR/$USERQUEUE_NAME.sqf
else
dialog --title "ERROR" --msgbox "Problem saving build queue."\
8 30
fi
fi
break
done
}
remove_from_queue() {
# This function deletes items in the build queue.
local ANSQUEUE=$SBOPKGTMP/sbopkg-ans-queue
local REMOVEQUEUE=$SBOPKGTMP/sbopkg-remove-queue
local WORKINGQUEUE=$SBOPKGTMP/sbopkg-working-queue
local QUEUELIST=$SBOPKGTMP/sbopkg_queue_list
local CHOICE REMOVE REMOVED
empty_queue && return
sed 's/ ON$//g;s/ OFF$//g' $TMPQUEUE > $REMOVEQUEUE
while :; do
# "dialog" segfaults when asked to display an empty menu. Work around
# this by showing an "empty" entry when there are no more items in the
# queue.
if [[ $(wc -w < $REMOVEQUEUE) -eq 0 ]]; then
echo '"" "The queue is empty."' > $REMOVEQUEUE
fi
dialog --visit-items --title "Remove From Queue" --ok-label "Delete" \
--extra-button --extra-label "Clear" --help-button \
--help-label "Done" --cancel-label "Cancel" \
--menu "$(crunch "The following packages are currently in \
the queue. You can remove individual items from the \
queue by highlighting them and pressing <Delete>. Press <Done> \
when you are finished and the individual deletions will be \
committed. Otherwise, press <Cancel> at any time to abort your \
changes.\n\nYou can also press <Clear> to immediately \
clear the queue. This cannot be undone.")" 25 60 8 \
--file $REMOVEQUEUE 2> $ANSQUEUE
CHOICE=$? # 0 = Delete, 1 = Cancel, 2 = Done, 3 = Delete All
REMOVED=$(< $ANSQUEUE)
case $CHOICE in
255|-1) # ESC
rm -f $REMOVEQUEUE
return 0
;;
0)
echo $REMOVED >> $WORKINGQUEUE
sed -i "/^$REMOVED .*$/d" $REMOVEQUEUE
continue
;;
1)
rm -f $REMOVEQUEUE
return 0
;;
2)
if [[ ! -e $WORKINGQUEUE ]]; then
rm -f $REMOVEQUEUE
break
fi
for REMOVE in $(< $WORKINGQUEUE); do
sed -i "/^$REMOVE .*$/d" $TMPQUEUE
done
if [[ ! -s $TMPQUEUE ]]; then
rm -f $TMPQUEUE
fi
dialog --title "Done" --msgbox \
"The items have been removed from the queue." 8 30
return 0
;;
3)
rm -f $REMOVEQUEUE $TMPQUEUE $QUEUELIST
dialog --title "Done" --msgbox \
"The queue has been cleared." 8 30
return 0
;;
esac
done
}
parse_arguments() {
# this converts the 'app:opt1="arg1 arg2":opt2=arg1' syntax we need on the
# command line to the 'app' and 'opt1="arg1 arg2" opt2="arg1"' syntax we
# need internally and passes it on to a file.
local CLI_OPTIONS
if [[ "$PKGBUILD" == *:* ]]; then # it has options
PKGBUILD=$(sed '
s/:/ /
s/=/="/g
s/:/":/g
s/:/ /g
s/$/"/' <<< $PKGBUILD)
CLI_OPTIONS="${PKGBUILD#* }"
PKGBUILD="${PKGBUILD%% *}"
echo "$CLI_OPTIONS" > $SBOPKGTMP/sbopkg_$PKGBUILD.cliopts
fi
if ! add_item_to_queue $PKGBUILD; then
echo "Queuefile or package $PKGBUILD not found - skipping." >>\
$MISSING_SINGLE_FILE
echo
fi
}
parse_queue() {
# This begins the process of parsing through a queuefile. The $2
# assignment to NODELETE is used in order to remove the $DUPEQUEUE file
# only when parse_queue is not called on a recursive queue loading.
local DUPEQUEUE=$SBOPKGTMP/sbopkg-duplicate-queue
local MISSING_LIST_FILE=$SBOPKGTMP/sbopkg_addall_missing
local FILE=$1
local NODELETE=$2
local PICK LOADOPTIONS
if [[ $NODELETE != "NODELETE" ]]; then
rm -f $DUPEQUEUE
fi
if [[ ! -e $DUPEQUEUE ]]; then
> $DUPEQUEUE
fi
if grep -qx "^$FILE" $DUPEQUEUE; then
return 0
else
echo "$FILE" >> $DUPEQUEUE
fi
# Reading from $FILE...
while read PICK; do
if can_skip_line $PICK; then
continue
fi
unset LOADOPTIONS
if grep -q "|" <<< $PICK; then
LOADOPTIONS=${PICK##*|}
LOADOPTIONS=${LOADOPTIONS/# /}
PICK=${PICK%%|*}
fi
if ! add_item_to_queue $PICK "$LOADOPTIONS"; then
if [[ ! -s $MISSING_LIST_FILE ]]; then
cat > $MISSING_LIST_FILE <<EOF
The following packages cannot be found
in the currently active repository
($REPO_NAME/$REPO_BRANCH) and have been skipped:
EOF
fi
echo $PICK >> $MISSING_LIST_FILE
fi
done < $FILE
}
add_item_to_queue() {
# This function takes up to two arguments: a required APP and an optional
# LOADOPTIONS. When loading a userqueue, some APPs may have a '-' or a
# '@' as the first character, which means to set the APP to 'OFF' in the
# dialog menu, or to recursively load another queuefile, respectively. If
# an APP is found in the repo, then add it to TMPQUEUE. LOADOPTIONS may be
# supplied when parsing a queuefile or similar and are eventually passed
# on to the SlackBuild.
# If an obsolete name is used, add_item_to_queue() automatically retrieves
# and uses the current name.
#
# This function returns 0 if the insertion was successful, 1 otherwise.
local APP=$1
local LOADOPTIONS="$2"
local USERQUEUE_LOCK=$SBOPKGTMP/sbopkg_user_queue.lck
local UPDATEQUEUE=$SBOPKGTMP/sbopkg-update-queue
local QUEUELIST=$SBOPKGTMP/sbopkg_queue_list
local MISSING_LIST_FILE=$SBOPKGTMP/sbopkg_addall_missing
local FILE ONOFF VERSION INSTALLED
# This next if is for legacy queuefiles with $APP $VERSION$BUILD $ONOFF
if [[ $3 =~ [Oo][Ff][Ff] ]]; then
APP=-$APP
fi
if [[ ${APP:0:1} == "-" ]]; then
APP=${APP:1}
ONOFF=OFF
elif [[ ${APP:0:1} == "@" ]]; then
APP=${APP:1}
if [[ ${APP:(-4)} != ".sqf" ]]; then
FILE="$QUEUEDIR/$APP.sqf"
else
FILE="$QUEUEDIR/$APP"
fi
if [[ -r $FILE ]]; then
parse_queue $FILE NODELETE
else
return 1
fi
return 0
else
ONOFF=ON
fi
if ! search_package $APP; then
get_new_name APP $APP
search_package $APP || return 1
fi
if grep -q "^$APP " $TMPQUEUE 2> /dev/null; then
: # it's the same app and version so toss it
else
# note that this regex was missing the ^ as of r826 and this caused a
# false match when running 'sbopkg -k -i queue' because 'foo' and
# 'libfoo' matched
INSTALLED=$(ls -1 /var/lib/pkgtools/packages/ |
grep "^$APP-[^-]*-[^-]*-[^-]*$REPO_TAG$")
if [[ -n $INSTALLED ]]; then
VERSION=$(sed 's:^.*-\([^-]*\)-[^-]*-[^-]*$:\1:'<<<$INSTALLED)
# NOTE: When changing, see the uncheck_installed() comment
echo "$APP \"Installed $VERSION\" $ONOFF" >> $TMPQUEUE
echo "$APP \"Installed $VERSION\" $ONOFF" >> $QUEUELIST
else
# NOTE: When changing, see the uncheck_installed() comment
echo "$APP New $ONOFF" >> $TMPQUEUE
echo "$APP New $ONOFF" >> $QUEUELIST
fi
if [[ $LOADOPTIONS ]]; then
echo "$LOADOPTIONS" > $SBOPKGTMP/sbopkg_"$APP"_loadoptions
else
rm -f $SBOPKGTMP/sbopkg_"$APP"_loadoptions
fi
fi
# Only display this if we are not loading a queue; otherwise getting this
# after each app was added to the queue may get annoying.
if [[ ! -e $USERQUEUE_LOCK ]]; then
dialog --title "Done" --msgbox "$(crunch "$APP has been added to \
the queue.")" 6 40
fi
return 0
}
uncheck_installed() {
# This function unchecks the installed items in a given queue.
# $1 = the queue file.
# NOTE: This function uses the second field (New/Installed foo) to
# work, so we should be careful when changing its format.
local QUEUEFILE=$1
sed -i 's:^\([^ ]* .*Installed.* \)[^ ]\+$:\1OFF:' $QUEUEFILE
}
view_queue() {
# This function displays the contents of the queue.
# Returns 0 if the user choose OK, nonzero otherwise
local ANSQUEUE=$SBOPKGTMP/sbopkg-ans-queue
local ORIGINALQUEUE=$SBOPKGTMP/sbopkg-original-queue
local CHOICE
empty_queue && return 1
cp $TMPQUEUE $ORIGINALQUEUE
while :; do
dialog --visit-items --title "Viewing Queue" --separate-output \
--extra-button --extra-label "View READMEs" \
--help-button --help-label "Clear inst'd" --help-status \
--cancel-label "Back" --checklist "$(crunch "The \
following packages are currently \
in the queue. Please note that when the queue \
is processed, the packages selected below will be processed \
in the order listed from top to \
bottom.\n\nPlease select or unselect those packages you wish \
to keep in the queue and then press <OK> to continue \
or press <Back> to go back.")" 23 70 11 \
--file $TMPQUEUE 2> $ANSQUEUE
CHOICE=$?
# Strip that damn "HELP " text when choosing the HELP dialog button
[[ $CHOICE -eq 2 ]] && sed -i 's:^HELP ::g' $ANSQUEUE
selection_state preserve $TMPQUEUE $ANSQUEUE
case $CHOICE in
0) # OK
return 0
;;
2) # Uncheck installed
uncheck_installed $TMPQUEUE
;;
3) # View READMEs
view_readmes "The active queue is:" $TMPQUEUE
;;
*) # Cancel or ESC
mv $ORIGINALQUEUE $TMPQUEUE
rm -f $ANSQUEUE
return 1
;;
esac
done
}
view_readmes() {
# Show a list of all README files for $INPUT. Takes two arguments, the
# first of which will serve as header text and the second of which
# specifies the input (list of packages whose READMEs are to be
# displayed).
local HEADER_STRING=$1
local INPUT=$2
local READMES_FILE=$SBOPKGTMP/sbopkg-all-readmes
local HEAD_FILE=$SBOPKGTMP/sbopkg-all-readmes-head
local REPORT_FILE=$SBOPKGTMP/sbopkg-all-readmes-report
local NAME ONOFF PICK READMES
READMES=$(find $REPO_DIR -mindepth 3 -maxdepth 3 -name README)
INFOS=$(find $REPO_DIR -mindepth 3 -maxdepth 3 -name *.info)
printf "$HEADER_STRING\n" > $HEAD_FILE
while read PICK; do
NAME=${PICK/ *}
ONOFF=${PICK/* }
# if not reading from TMPQUEUE, just pass everything through
if [[ $ONOFF =~ ^[Oo][Nn]$ ]] || [[ $INPUT != $TMPQUEUE ]]; then
echo $NAME >> $HEAD_FILE
else
echo "$NAME (DISABLED)" >> $HEAD_FILE
fi
echo >> $READMES_FILE
echo >> $READMES_FILE
tin_text $NAME >> $READMES_FILE
echo >> $READMES_FILE
{
cat $(grep /$NAME/README\$ <<< "$READMES")
echo
grep '^REQUIRES=' $(grep /$NAME/*.info <<< "$INFOS")
} >> $READMES_FILE
done < $INPUT
tin_text "$(< $HEAD_FILE)" > $REPORT_FILE
cat $READMES_FILE >> $REPORT_FILE
if [[ $DIAG ]]; then
dialog --exit-label "OK" --title "Showing READMEs" \
--textbox $REPORT_FILE 0 0
else
$PAGER $REPORT_FILE
fi
rm $READMES_FILE $REPORT_FILE $HEAD_FILE
}
tin_text() {
# Print $1 in a nice ASCII box like:
# +---------+
# | foo bar |
# | baz |
# +---------+
local TEXT="$1"
local MAXLEN=0 NLINES=0
local LINE
local HLINE
# Find the maximum line length and the number of lines
while read LINE; do
if [[ $MAXLEN -lt ${#LINE} ]]; then
MAXLEN=${#LINE}
fi
((NLINES++))
done <<< "$TEXT"
# Print the box
printf -vHLINE "%${MAXLEN}s" ""
printf -vHLINE "%s" "${HLINE// /-}"
echo "+-$HLINE-+"
while read LINE; do
printf "| %-${MAXLEN}s |\n" "$LINE"
done <<< "$TEXT"
echo "+-$HLINE-+"
}
add_all_to_queue() {
# This function adds all currently installed repo packages to the
# queue.
local SBOPKGLIST=$SBOPKGTMP/sbopkg_pkglist
local USERQUEUE_LOCK=$SBOPKGTMP/sbopkg_user_queue.lck
local TMPQUEUE_BACKUP=$SBOPKGTMP/sbopkg_addall_backup
local MISSING_LIST_FILE=$SBOPKGTMP/sbopkg_addall_missing
local PROGRESSBAR_INTERRUPTED=$SBOPKGTMP/sbopkg_progressbar-interrupted
local PKGS FILE PKG_NAME
local PROGRESS=0 NUM_PACKAGES
rm -f $SBOPKGLIST $MISSING_LIST_FILE $PROGRESSBAR_INTERRUPTED
cp $TMPQUEUE $TMPQUEUE_BACKUP 2> /dev/null
touch $USERQUEUE_LOCK
cd /var/log/packages
PKGS=$(ls *$REPO_TAG* 2> /dev/null)
for FILE in $PKGS; do
echo $FILE >> $SBOPKGLIST
done
if [[ -f $SBOPKGLIST ]]; then
NUM_PACKAGES=$(wc -l < $SBOPKGLIST)
{ # Grouping for progressbar
echo 0 # Progressbar begin
for PICK in $(cat $SBOPKGLIST); do
# Bail out if the user pressed ESC
progressbar_interrupted && touch $PROGRESSBAR_INTERRUPTED && break
if can_skip_line $PICK; then
continue
fi
split_pkg_name $PICK
if ! add_item_to_queue $PKG_NAME; then
if [[ ! -f $MISSING_LIST_FILE ]]; then
cat > $MISSING_LIST_FILE <<EOF
The following packages cannot be found
in the currently active repository
($REPO_NAME/$REPO_BRANCH) and have been skipped:
EOF
fi
echo $PKG_NAME >> $MISSING_LIST_FILE
fi
((PROGRESS++))
echo $((PROGRESS*100/NUM_PACKAGES))
done
} | progressbar "Queuing installed packages" \
"Loading all installed $REPO_NAME packages into the queue.\
This may take a few moments depending on how many $REPO_NAME\
packages you have installed, so please be patient..."
if [[ -f $PROGRESSBAR_INTERRUPTED ]]; then
rm -f $TMPQUEUE
mv $TMPQUEUE_BACKUP $TMPQUEUE 2> /dev/null
rm $PROGRESSBAR_INTERRUPTED
else
if [[ -f $MISSING_LIST_FILE ]]; then
dialog --title "Packages not found" --textbox \
$MISSING_LIST_FILE 0 0
fi
fi
rm -f $USERQUEUE_LOCK $MISSING_LIST_FILE $TMPQUEUE_BACKUP
else
if [[ $DIAG ]]; then
dialog --title "No packages found" --msgbox "$(crunch_fmt "It \
appears that you have no $REPO_NAME packages \
installed.")" 8 40
fi
fi
}
rsync_command() {
# This function holds the rsync command.
# We do not use -z as this causes heavy CPU load on the server and has
# very limited effect when most of the pull is .gz files.
local SYNC_LOCK=$SBOPKGTMP/sbopkg_sync.lck
rsync --archive --delete --no-owner --no-group --exclude="*.sbopkg" \
$RSYNCFLAGS $REPO_LINK/ $REPO_DIR/
case $? in
35)
echo
echo "The connection to $REPO_LINK timed out."
echo "You can modify the TIMEOUT value in sbopkg.conf"
echo "if this problem persists."
echo "(TIMEOUT is currently set to: $TIMEOUT seconds)".
echo
rm -f $SYNC_LOCK
exit 1
;;
30)
echo
echo "Rsync reported a timeout while waiting for data."
echo "$REPO_LINK may under a heavy load."
echo "Please try again later."
echo
rm -f $SYNC_LOCK
exit 1
;;
10)
echo
echo "Rsync reported a socket error which may be due to"
echo "a problem with the LINK value in sbopkg.conf."
echo "(The repo's LINK is currently set to: $REPO_LINK)."
echo "Please check your settings and try again later."
echo
rm -f $SYNC_LOCK
exit 1
;;
0)
echo
echo "Rsync with the $REPO_DESC complete."
echo
echo "Importing $REPO_DESC GPG Key..."
gpg --quiet --fetch-key https://www.slackbuilds.org/GPG-KEY
echo "Import done."
echo
echo "***SYNC COMPLETE***"
;;
*)
echo
echo "Rsync with the $REPO_DESC failed."
echo "Please try again."
echo
rm -f $SYNC_LOCK
exit 1
;;
esac
rm -f $SYNC_LOCK
}
current_check_updates() {
# This function checks for updates if repository is set to -current.
local URL BRANCH REMOTE LOCAL
eval $(sed 's/^\(.*\)@\(.*\)$/URL=\1; BRANCH=\2/g' <<< $REPO_LINK)
cd $REPO_DIR
REMOTE=$(git ls-remote $URL $BRANCH | cut -f 1)
LOCAL=$(git rev-parse HEAD)
# If the remote has changed, wipe the local version
if [[ $REMOTE != $LOCAL ]]; then
cd ..
rm -fR $REPO_DIR
fi
}
git_command() {
# This function synchronizes a local git repository with upstream.
local SYNC_LOCK=$SBOPKGTMP/sbopkg_sync.lck
local URL BRANCH CWD
eval $(sed 's/^\(.*\)@\(.*\)$/URL=\1; BRANCH=\2/g' <<< $REPO_LINK)
CWD=$(pwd)
# If -CURRENT, handle correctly
if [[ $REPO_NAME == "SBo-git" ]]; then
if [[ $REPO_BRANCH == "current" ]]; then
current_check_updates
fi
fi
# Create the repository if needed
if [[ ! -d $REPO_DIR/.git ]]; then
mkdir -p $REPO_DIR
cd $REPO_DIR
git init
fi
# Update the repository
cd $REPO_DIR
git pull $URL $BRANCH
# Remove leftovers
# This is optional, think of it as a way to emulate the --delete --exclude
# rsync directives
echo "*/*/*.sbopkg" > .gitignore
git clean -d -f
git reset --hard HEAD
# Create a changelog
# (it makes no sense to have one tracked in a git repo)
if [[ ! -f ChangeLog.txt ]]; then
git log --pretty=format:"%cd%n%s%n%b" > ChangeLog.txt
fi
# All done
rm -f $SBOPKGTMP/sbopkg_sync.lck
echo
echo "Repository update complete."
echo
}
sync_repo() {
# This function does the sync with SBo.
local SYNC_LOCK=$SBOPKGTMP/sbopkg_sync.lck
if [[ $REPO_TOOL == "" ]]; then
if [[ $DIAG ]]; then
dialog --title "ERROR" --msgbox \
"You cannot sync the $REPO_DESC." 8 40
else
crunch_fmt "You cannot sync the $REPO_DESC."
fi
continue
fi
if [[ $REPO_TOOL != "rsync" && $REPO_TOOL != "git" ]]; then
if [[ $DIAG ]]; then
dialog --title "ERROR" --msgbox \
"Unsupported fetching tool \"$REPO_TOOL\"." 8 30
continue
else
echo "Unsupported fetching tool \"$REPO_TOOL\"."
exit 1
fi
fi
if [[ $DIAG ]]; then
touch $SYNC_LOCK
( ${REPO_TOOL}_command >> $SBOPKGOUTPUT & ) 2>> $SBOPKGOUTPUT
while [[ -f $SYNC_LOCK ]]; do
dialog --backtitle "Updating the active repository" \
--tailbox $SBOPKGOUTPUT 18 70
done
rm -f $SBOPKGOUTPUT
else
${REPO_TOOL}_command
fi
}
search_package() {
# Search for package name and return error if not found.
# $1 = the name of the package we're looking for
# Returns 0 and sets PKGPATH if the package is found. Returns 1 otherwise.
local PKG
cd $REPO_DIR
PKG="$1"
PKGPATH=( $(find -type d -mindepth 2 -maxdepth 2 -name "$PKG" | sort) )
if [[ -z $PKGPATH ]]; then
return 1
else
return 0
fi
}
gen_search_package() {
# Search for package name glob generally using the '-iwholename' argument
# to find, with values wrapped in '*'. In dialog interface, jump to
# selected package.
# Returns 0 unless the user asked to jump back to the main menu.
cd $REPO_DIR
local PKG=$1
local CATEGORY=${2:-\*}
local RETVAL=0
local CAT_SELECTION=$SBOPKGTMP/sbopkg_category_selection
local ITEM_SELECTION=$SBOPKGTMP/sbopkg_item_selection
local SEARCH_CHOICE=$SBOPKGTMP/sbopkg_search_choice
local SEARCH_RESULTS=$SBOPKGTMP/sbopkg_search_results
local RESULTS_VERSIONS=$SBOPKGTMP/sbopkg_search_results_versions
local RESULTS=$(find -mindepth 2 -maxdepth 2 -type d \
-iwholename "./$CATEGORY/*$PKG*" -printf "%P\n" | sort)
local NAME DESC CHOICE RESULT_VERSION
local SRCHPICK SRCHCAT SRCHPKG
if [[ $RESULTS ]]; then
if [[ $DIAG ]]; then
for i in $RESULTS; do
DESC=$(grep -hZm1 ^$(cut -d/ -f2 <<< "$i") ./$i/slack-desc* |
sed 's/^[^(]*( *\(.*[^ ]\) *)[^)]*$/\1/;s/"/'\''/g')
if [[ $CATEGORY == '*' ]]; then
NAME=$i
else
NAME=$(cut -d/ -f2 <<< "$i")
fi
echo "\"$NAME\" \"$DESC\"" >> $SEARCH_RESULTS
done
while [[ -f $SEARCH_RESULTS ]]; do
# The default item can be "". In that case, dialog defaults to
# the first item.
dialog --visit-items --title "Matches for $PKG in $CATEGORY" \
--backtitle "$(eval echo $BACKTITLE)" \
--default-item "$SRCHPICK" --extra-button \
--cancel-label "Back" \
--help-button --help-label "Main Menu" \
--extra-label "Add to Queue" --menu "$(crunch "Please \
select an item you wish to view, press <Add to Queue> \
to add it to the queue, or press <Back> to \
go back.")" 22 70 14 --file \
$SEARCH_RESULTS 2> $SEARCH_CHOICE
CHOICE=$?
case $CHOICE in
# Back or ESC
1 | 255 | -1 ) break ;;
# Main Menu
2 ) RETVAL=1; break ;;
esac
SRCHPICK="$(< $SEARCH_CHOICE)"
if [[ $CATEGORY == '*' ]]; then
SRCHCAT="${SRCHPICK%%/*}"
else
SRCHCAT=$CATEGORY
fi
echo $SRCHCAT > $CAT_SELECTION
SRCHPKG="${SRCHPICK##*/}"
if [[ $CHOICE == 0 ]]; then
echo $SRCHPKG > $ITEM_SELECTION
cd $REPO_DIR
if ! info_item; then
RETVAL=1
break
fi
else # $CHOICE = 3
add_item_to_queue $SRCHPKG
fi
done
else
echo "Found the following matches for $PKG:"
echo -e "NAME\tVERSION" >> $RESULTS_VERSIONS
for i in $RESULTS; do
RESULT_VERSION=$(grep ^VERSION $(find $i -name *.info) | cut -d= -f2 | tr -d \")
echo -e "$i\t$RESULT_VERSION" >> $RESULTS_VERSIONS
done
column -t $RESULTS_VERSIONS
fi
else
if [[ $DIAG ]]; then
dialog --title "Not Found" --msgbox "No match for $PKG found"\
8 30
else
echo "$SCRIPT: No match for $PKG found." >&2
fi
fi
rm -f $SEARCH_RESULTS $SEARCH_CHOICE $CAT_SELECTION $ITEM_SELECTION
return $RETVAL
}
string_search() {
# If the search string is prefixed with 'inst:', then the user only wants
# to search installed packages, so this shaves the 'inst:' off, and
# generates a potentially huge path consisting only of installed *SBo
# packages to hand to find. Otherwise, just search for $1 in REPO as
# usual.
# Returns 0 unless the user asked to jump back to the main menu.
if [[ ${SEARCH_TERM%%:*} == "inst" ]]; then
local SEARCH_TERM="${SEARCH_TERM#*:}"
local SBOPKGS=($(ls /var/lib/pkgtools/packages/*$REPO_TAG))
for ((i=0; i<${#SBOPKGS[*]}; i++)); do
local PKGNAME=$(
sed 's,.*/,,;s/-[^-]*-[^-]*-[^-]*$//' <<< "${SBOPKGS[$i]}")
local INST_PKGS+="$REPO_DIR/*/$PKGNAME "
done
local FIND_PATH="$INST_PKGS"
local DEPTH=1
else
local SEARCH_TERM="$1"
local FIND_PATH="$REPO"
local DEPTH=3
fi
local CAT_SELECTION=$SBOPKGTMP/sbopkg_category_selection
local ITEM_SELECTION=$SBOPKGTMP/sbopkg_item_selection
local MENU_FILE=$SBOPKGTMP/sbopkg_menu-file
local PICKED_FILE=$SBOPKGTMP/sbopkg_picked-file
local PICKED
# The sed expression processes find's output into data usable for the menu
# file but the first two parts are needed to sanitize the input - which
# raises the question of true general sanitizing of this input
( find $FIND_PATH -mindepth $DEPTH -maxdepth $DEPTH -iname 'README' \
-exec egrep -iwm1 "$SEARCH_TERM" {} + |
sed "
s,\",\',g
s/\\\/\\\\\\\\/g
s,$REPO_DIR/,,
s/^/\"/
s,/README:,\" \",
s/$/\"/
" | sort > $MENU_FILE
) 2> /dev/null
if [[ ! -s $MENU_FILE ]]; then
dialog --title "ERROR" --msgbox "No match for $SEARCH_TERM found" 8 30
return 0
fi
cd $REPO_DIR
while :; do
dialog --visit-items --title "String Search Results" --default-item "$PICKED" \
--extra-button --extra-label "Add to Queue" \
--cancel-label "Back" \
--menu "$(crunch "Please select an item you wish to view or \
press <Add to Queue> to add it to the queue or \
press <Back> to go back.")" 0 0 0 \
--file $MENU_FILE 2> $PICKED_FILE
BUTTON=$?
PICKED=$(< $PICKED_FILE)
# Duplicate (except slightly modified) code from gen_package_search()
SRCHCAT=${PICKED%%/*}
echo $SRCHCAT > $CAT_SELECTION
SRCHPKG=${PICKED##*/}
case $BUTTON in
0) # OK
echo $SRCHPKG > $ITEM_SELECTION
if ! info_item; then
rm -f $PICKED_FILE $MENU_FILE $CAT_SELECTION \
$ITEM_SELECTION
return 1
fi
;;
3) # Add to Queue
add_item_to_queue $SRCHPKG
continue
;;
*) # Back, etc.
rm -f $PICKED_FILE $MENU_FILE $CAT_SELECTION $ITEM_SELECTION
return 0
;;
esac
done
}
show_readme() {
# Show the package's text files.
# $1 = Package path
# $2 = Package name
local PKGPATH=$1
local PKGNAME=$2
cd $REPO_DIR
$PAGER \
$PKGPATH/{README,$PKGNAME.SlackBuild,$PKGNAME.info.build,slack-desc}
rm -f $PKGPATH/$PKGNAME.info.build
return 0
}
read_info() {
# Read the info file specified in $1.
# This used to be a plain ". $INFO", but due to the changes required to
# support multiple arches and source files (both features are planned
# for the Slackware 13.0 release) it needs some more work.
# The DOWNLOAD and MD5SUM arrays will always contain the "right"
# (possibly ARCH-dependent) values.
local INFO=$1
local i DOWNLOAD_ARCH DLSAVE MDSAVE REPLY
local {DOWNLOAD,MD5SUM}_$ARCH
unset DOWNLOAD MD5SUM
# Parse the .info file
. $INFO
# Assign the proper entries to DOWNLOAD and MD5SUM.
DOWNLOAD_ARCH=$(eval echo \$DOWNLOAD_$ARCH)
if [[ -n $DOWNLOAD_ARCH ]]; then
DLSAVE=$DOWNLOAD
MDSAVE=$MD5SUM
DOWNLOAD=$DOWNLOAD_ARCH
MD5SUM=$(eval echo \$MD5SUM_$ARCH)
fi
# Note: on SBo pre-13.0 repositories, as well as on current non-SBo
# repositories, none of the above triggers. In that case, we use the
# provided DOWNLOAD and MD5SUM variables, which is exactly the old-style
# behavior.
# This next bit is called in process_queue and is here to test if the
# package is marked UNSUPPORTED or UNTESTED and ask the user what he wants
# to do. If the user chooses to proceed, then the valid DOWNLOAD and
# MD5SUM lines from the .info file are tacked on to the end of the
# *.info.build file. This way, when read_info is called elsewhere, like
# in get_source via process_package, the newly tacked-on lines will provide
# the 'correct' DOWNLOAD and MD5SUM values. This is more or less a
# band aid until the multiple read_info invocations can be addressed.
if [[ $2 == --check_buildable ]]; then
if [[ $DOWNLOAD == "UNSUPPORTED" || $DOWNLOAD == "UNTESTED" ]]; then
echo
crunch_fmt "$PRGNAM: This package is marked UNSUPPORTED \
or UNTESTED and may not build successfully on your \
architecture."
echo
while :; do
read $NFLAG -ep "(P)roceed anyway or (S)kip?: "
case $REPLY in
P|p) break ;;
S|s) return 1 ;;
*) unknown_response ;;
esac
done
if [[ $ARCH != "x86_64" ]]; then
echo "DOWNLOAD=\"$DOWNLOAD_x86_64\"" >> $INFO
echo "MD5SUM=\"$MD5SUM_x86_64\"" >> $INFO
else
echo "DOWNLOAD_$ARCH=\"$DLSAVE\"" >> $INFO
echo "MD5SUM_$ARCH=\"$MDSAVE\"" >> $INFO
fi
fi
fi
# Convert the space-separated strings to arrays
DOWNLOAD=($DOWNLOAD)
MD5SUM=($MD5SUM)
}
fix_urls() {
# A quirks function of source filename corrections for a variety of apps
# where a variety of nonsense gets prepended or appended to the tarball
# name in the URLs. Ideally, these should match classes of apps which are
# noted. Else they should be app-specific and noted. But they shouldn't be
# completely generic and uncommented.
sed '
# fix calcurse
/get\.cgi?calcurse/s/^.*?//
# fix zarafa and zarafa-webaccess-ajax
/src=zarafa-/{s/.*=//;s/$/.tar.gz/}
# fix dzen2
/dzen2-/s/?attachauth=.*//
# fix several packages using trac as an scms
s/?format=.*$//
'
}
get_source_names() {
# Echo the source names for an app, given the info file.
# Usage: get_source_names [--all] [--placeholder] info_file
# --all try to find all source files (i.e. also the obsolete
# ones)
# --placeholder if no source file is found for a DOWNLOAD entry, a
# line containing "." is generated
# Note that even without --all this function can return multiple files,
# if the application specifies more than one in its .info file.
local SRCNAME INFO ALL CWD DL PLACEHOLDER VER
# Don't pollute the environment with the .info content...
local PRGNAM VERSION HOMEPAGE DOWNLOAD MD5SUM MAINTAINER EMAIL APPROVED
if [[ $1 == "--all" ]]; then
ALL=yes
shift
fi
if [[ $1 == "--placeholder" ]]; then
PLACEHOLDER="."
shift
fi
INFO=$1
read_info $INFO
for DL in "${DOWNLOAD[@]}"; do
# remove the '/download' from several SRCNAMEs that end that way
# rather than in the actual tarball so the subsequent basename doesn't
# end with 'download'
SRCNAME=${DL%\/download}
SRCNAME=${SRCNAME##*/}
# calcurse has a nonsense url in the info file - this is to get it
# through this function and on to remove_obsolete_sources()
SRCNAME=$(fix_urls <<< "$SRCNAME")
# Replace URI hex sequences (like %20 for ' ' and %2B for '+') with
# their corresponding characters.
# This is done by replacing '%' with '\x' and passing the string to
# printf.
if [[ $SRCNAME =~ % ]]; then
SRCNAME=$(printf ${SRCNAME//\%/\\x})
fi
# The above doesn't work when the download link doesn't reference the
# file name either explicitly or correctly. If this is the case, our
# SRCNAME doesn't correspond to a file, and all we can do is guess
# from the file that was downloaded and/or the name of the package.
if [[ -n $NO_DL_LOOP && ! -f $(readlink "$SRCNAME") &&
${#DOWNLOAD[@]} == 1 ]]; then
# If the source has a name resembling $PRGNAM-$VERSION.tar.gz,
# catch it.
CWD=$(pwd)
cd $SRCDIR
SRCNAME=$(find . -iname $PRGNAM\*$VERSION.\* | head -n 1)
cd "$CWD"
if [[ ! $SRCNAME ]]; then
# We do our best with the tools we have...
SRCNAME=$PRGNAM-$VERSION.tar.gz
fi
fi
# If the user asked for "all" sources, let's try to find similar names
if [[ $ALL ]]; then
# The following is based on the idea that, if the source name
# contains the version number, the expression below takes the
# parts before and after the version number, and replaces the
# version number with a regular expression matching a digit and
# any character present in the known version number (this is to
# match odd version numbers containing letters, like "svn1234",
# but makes it less likely to match different packages with
# similar names, like virtualbox-kernel and
# virtualbox-kernel-addons). If the source name does not contain
# the version number, we leave SRCNAME alone, though this means
# that when the user is using the remove sources functionality
# they can only remove the source one at a time, starting with the
# most recent.
#
# The flash conditional is based on the fact that the source names
# of the flash player plugin (which are different on different
# arches) don't contain the version number. The grep (rather than
# egrep) is due to a grep having already been used below and this
# having fewer potential side effects - this should eventually get
# a proper fix rather than this ad hockery.
if grep -q '\(install_\|lib\)flash_\?player.*\.tar\.gz' \
<<< $SRCNAME; then
SRCNAME='\(install_\|lib\)flash_\?player.*\.tar\.gz'
elif [[ $SRCNAME =~ $VERSION ]]; then
VER=$VERSION # just to shorten the following line
SRCNAME=${SRCNAME%%$VER*}[0-9$VER]\*${SRCNAME##*$VER}
fi
fi
# This isn't just 'ls -A $SRCDIR/${SRCNAME##*/} 2> /dev/null' because
# we want only the basename - though we could 'cd' first or 'basename'
# after, rather than grepping. And the conditionals ensure that we
# only return PLACEHOLDER when we don't have any other results and
# PLACEHOLDER is actually called for and set.
if [[ -z $SRCNAME ]] || ! ls -A $SRCDIR | grep "^${SRCNAME##*/}"; then
if [[ $PLACEHOLDER ]]; then
echo $PLACEHOLDER
fi
fi
done
}
check_source() {
# Check the source file for correctness.
# Parameters:
# - $1 = package name
# - $2 = expected MD5
# - $3 = source file name
# Returns 0 if the source is OK, 1 if the source should be (re)downloaded
# (this includes the cases where $3 is empty or refers to a nonexistent
# file) and 2 if the user asked to abort the build or 3 if the user
# asked to look in the alternate repository
local PKG=$1
local MD5SUM="$2"
local SRCNAME="$3"
local MD5CHK REPLY
# If there's no known source name, or if it doesn't exist, it has to be
# downloaded...
if [[ -f $SRCDIR/$SRCNAME ]]; then
echo "Found $SRCNAME in $SRCDIR."
unset NO_DL_LOOP
if [[ $QUEUETYPE == download ]]; then
echo " Found $SRCNAME in $SRCDIR." >> $TMPSUMMARYLOG
return 0
fi
else
echo "$PKG not found in $SRCDIR."
return 1
fi
# Check MD5
echo "Checking MD5SUM:"
MD5CHK=$(md5sum "$SRCDIR/$SRCNAME" | cut -d' ' -f1)
echo -n " MD5SUM check for $SRCNAME ... " | tee -a $TMPSUMMARYLOG
if [[ $MD5CHK == $MD5SUM ]]; then
echo "OK" | tee -a $TMPSUMMARYLOG
else
echo "FAILED!" | tee -a $TMPSUMMARYLOG
echo " Expected: $MD5SUM" | tee -a $TMPSUMMARYLOG
echo " Found: $MD5CHK" | tee -a $TMPSUMMARYLOG
# Ask the user what to do with the bad source
while :; do
cat << EOF
Do you want to use the downloaded $PKG source:
$SRCNAME in $SRCDIR?
You can choose among the following options:
- (Y)es, keep the source and continue the build process;
- (N)o, delete the source and abort the build process;
- (R)etry download and continue the build process; or
- (A)ttempt to download from third party source repository.
EOF
printf "(Y)es, (N)o, (R)etry, (A)lternative ?: "
error_read
case $REPLY in
Y|y)
MD5SUM=$(tr / _ <<< "$MD5CHK")
echo " Keeping the source and continuing." |
tee -a $TMPSUMMARYLOG
return 0
;;
N|n)
rm -f "$SRCDIR/$SRCNAME"
echo " Source deleted." | tee -a $TMPSUMMARYLOG
return 2
;;
R|r)
echo " Downloading again." | tee -a $TMPSUMMARYLOG
return 1
;;
A|a)
echo " Searching source repository." | tee -a $TMPSUMMARYLOG
return 3
;;
*)
unknown_response
;;
esac
done
fi
}
check_cert_prompt() {
# this asks the user if they want to retry a download that may have failed
# due to an SSL error.
local REPLY
echo
crunch_fmt "$SCRIPT: Some https download errors can be worked around \
by temporarily adding '--no-check-certificate' to WGETFLAGS. If \
unsure, see the wget manual on the use of this flag."
echo
while :; do
printf "Would you like to have sbopkg attempt this? [Y/n]: "
error_read
case $REPLY in
Y|y|'')
echo "Re-trying with '--no-check-certificate'."
echo
TWGETFLAGS+=" --no-check-certificate"
return
;;
N|n) return 1 ;;
*) unknown_response ;;
esac
done
}
make_archive_url() {
# Get (and echo) the sbosrcarch URL for a source
local MD5SUM=$1
local SRCNAME=$2
local ARCHIVE=${SRC_REPO:-"http://slackware.uk/sbosrcarch"}
echo $ARCHIVE/by-md5/${MD5SUM:0:1}/${MD5SUM:1:1}/$MD5SUM/$SRCNAME
}
get_source() {
# Check to see if the source tarball exists in the local cache directory.
# If it does, make a symlink to the package directory in the local mirror.
# If it does not, download it and make the link.
#
# Parameters:
# $1 = info file
#
# Return values:
# 0 = all ok
# 1 = failed
local INFO=$1
local PKG=${INFO%.info.build}
local BUILD_LOCK=$SBOPKGTMP/sbopkg_build.lck
local DLDIR=$SBOPKGTMP/sbopkg-download
local SBOPKG_PIDLIST=$SBOPKGTMP/sbopkgpidlist
local TMPSUMMARYLOG=$SBOPKGTMP/sbopkg-tmp-summarylog
local SRCNAME DL_SRCNAME DL FAILURE MD5CHK i CWD TWGETFLAGS WGETRC
# Don't pollute the environment with the .info content...
local PRGNAM VERSION HOMEPAGE DOWNLOAD MD5SUM MAINTAINER EMAIL APPROVED
CWD=$(pwd)
read_info $INFO
echo | tee -a $TMPSUMMARYLOG
echo "$PKG:" | tee -a $TMPSUMMARYLOG
for i in ${!MD5SUM[@]}; do
TWGETFLAGS=$WGETFLAGS
while :; do
cd "$CWD"
SRCNAME=$(get_source_names --placeholder $INFO)
# Put SRCNAME lines into array elements.
# This is a little bit involved since it has to deal with spaces
# inside file names.
# We know that we could obtain the same result faster by mangling
# with IFS, but the resulting code was a bit too hacky.
eval "SRCNAME=( $(
while read LINE; do
printf '%q ' $LINE
done <<< $SRCNAME
) )"
check_source $PKG ${MD5SUM[$i]} "${SRCNAME[$i]}"
case $? in
0 ) # Source OK
ln -sf "$SRCDIR/${SRCNAME[$i]}" \
"$REPO_DIR/$PKGPATH/${SRCNAME[$i]}"
continue 2
;;
1 ) # Fall through and (re)try below
;;
2 ) # Abort
FAILURE=download
break 2
;;
3 ) # Archive
DOWNLOAD[$i]=$( make_archive_url ${MD5SUM[$i]} ${SRCNAME[$i]} )
;;
esac
rm -rf $DLDIR
mkdir -p $DLDIR
cd $DLDIR
if [[ -z $NO_DL_LOOP ]]; then
wget $TWGETFLAGS ${DOWNLOAD[$i]} >> $SBOPKGOUTPUT &
echo "$!" >> $SBOPKG_PIDLIST 2>> $SBOPKGOUTPUT
wait $!
WGETRC=$?
else
FAILURE=loop
echo " Download failed. Please report this as a bug." >> \
$TMPSUMMARYLOG
echo >> $TMPSUMMARYLOG
fi
DL=$(ls -A . 2> /dev/null)
DL_SRCNAME=$(fix_urls <<< "$DL")
if [[ $DL_SRCNAME ]]; then
# if we have *anything* in here, then we did a download. We
# may not know what to do with it, but we don't need to get it
# again. We only need to succeed or fail once.
NO_DL_LOOP=1
mv "$DL" "$SRCDIR/$DL_SRCNAME"
else
if [[ $WGETRC == 5 &&
$TWGETFLAGS != *no-check-certificate* ]]; then
check_cert_prompt && continue
fi
FAILURE=download
echo " Download failed." >> $TMPSUMMARYLOG
echo >> $TMPSUMMARYLOG
fi
cd $SRCDIR
rm -rf $DLDIR
[[ $FAILURE ]] && break 2
# this is required so we make a link as soon as we have the source
# and don't enter the guessing code in get_source_names()
ln -sf "$SRCDIR/$DL_SRCNAME" "$REPO_DIR/$PKGPATH/$DL_SRCNAME"
done
done
cd $REPO_DIR/$PKGPATH
[[ $FAILURE ]] && return 1
return 0
}
remove_sources_for_app() {
# Remove all sources from $SRCDIR for a particular application $1 is the
# app's INFO file
local INFO="$1"
local APP_SOURCES=$SBOPKGTMP/sbopkg_app_sources
local APP
APP=${INFO##*/}
APP=${APP%%.*}
get_source_names --all "$INFO" | sed 's/.*/"&"/' > $APP_SOURCES
remove_files $SRCDIR "$APP sources" $APP_SOURCES OFF
}
remove_obsolete_sources() {
# Remove all obsolete sources
local FIND_RESULT=$SBOPKGTMP/sbopkg_obsolete_find
local SOURCES=$SBOPKGTMP/sbopkg_app_sources
local PROGRESSBAR_INTERRUPTED=$SBOPKGTMP/sbopkg_progressbar-interrupted
local GSNFILE=$SBOPKGTMP/sbopkg_get_source_names-output
local PROGRESS=0
local NUMINFO
local INFO APP_CURRSRC REGEX
rm -f $PROGRESSBAR_INTERRUPTED
### avoiding unecessary work and time lapse in case the user
### does not remember he just deleted the sources previously
(( $(find $SRCDIR -type f | wc -l ) == 0 ))
if [ $? -eq 0 ]; then
echo "No sources available"
exit 1
fi
{ # Grouping for progressbar
echo 0 # Progressbar begin
find $REPO_DIR -mindepth 3 -maxdepth 3 -name \*.info > $FIND_RESULT
NUMINFO=$(wc -l < $FIND_RESULT)
ls -A $SRCDIR > $SOURCES
for INFO in $(cat $FIND_RESULT); do
# Bail out if the user pressed ESC
progressbar_interrupted && touch $PROGRESSBAR_INTERRUPTED && break
# Reading get_source_names output...
get_source_names "$INFO" > $GSNFILE
while read APP_CURRSRC; do
if [[ $APP_CURRSRC ]]; then
REGEX="/^$APP_CURRSRC$/d;$REGEX"
fi
done < $GSNFILE
# Progress indicator, for the progressbar
(( PROGRESS += 1 ))
echo $(($PROGRESS * 100 / $NUMINFO))
done
sed -i "$REGEX" $SOURCES
} | progressbar "Checking for obsolete sources" \
"This may take a few moments. Press <ESC> to abort."
if [[ -f $PROGRESSBAR_INTERRUPTED ]]; then
rm -f $PROGRESSBAR_INTERRUPTED $SOURCES
return
fi
# Quote file names
sed -i 's/.*/"&"/' $SOURCES
remove_files $SRCDIR "obsolete sources" $SOURCES ON
}
remove_uninstalled_packages() {
# Remove uninstalled packages from $OUTPUT
local PACKAGES=$SBOPKGTMP/sbopkg_uninstalled_packages
local PKG
rm -f $PACKAGES
ls $OUTPUT/*$REPO_TAG.t?z 2>/dev/null | while read PKG; do
PKG=${PKG##*/}
if [ ! -f /var/lib/pkgtools/packages/${PKG%.t?z} ]; then
echo "$PKG" >> $PACKAGES
fi
done
remove_files $OUTPUT "uninstalled packages" $PACKAGES ON
}
remove_files() {
# Selectively remove files, after showing a checklist of them.
# The file names (specified in $3) _must_ be quoted.
# $1 is the files path
# $2 is the topic (used for display purposes only, like "foo sources")
# $3 is a file containing the list of the files to be shown
# $4 is either "ON" or "OFF", and is used as the default checklist status
local FILESPATH="$1"
local TOPIC="$2"
local FILES="$3"
local ONOFF="$4"
local FILES_CHECKLIST=$SBOPKGTMP/sbopkg_file_removal_checklist
local FILES_DELETING=$SBOPKGTMP/sbopkg_file_removal_deleting
local SRC USER_OPTS DELETE DLGWIDTH CHOICE REPLY
cd $FILESPATH
if [[ -s $FILES ]]; then
sed "s/.*/& \"\" $ONOFF/g" $FILES | sort > $FILES_CHECKLIST
if [[ $DIAG ]]; then
while :; do
dialog --visit-items --separate-output --defaultno \
--title "Displaying $TOPIC" \
--extra-button --extra-label "Invert Sel" \
--checklist "Delete the $TOPIC in $FILESPATH?" \
20 60 12 --file $FILES_CHECKLIST 2> $FILES_DELETING
CHOICE=$? # 0=Ok, 3=Invert Sel
selection_state preserve $FILES_CHECKLIST $FILES_DELETING
case $CHOICE in
0)
DELETE=1
break
;;
3)
selection_state reverse $FILES_CHECKLIST \
$FILES_DELETING
;;
*)
rm -f $FILES_CHECKLIST $FILES_DELETING
return 0
;;
esac
done
else
# Unquote file names
tr -d \" < $FILES > $FILES_DELETING
echo "[ Displaying $TOPIC ]" | cat - $FILES_DELETING | $PAGER
echo
while :; do
read $NFLAG -ep "(K)eep or (D)elete these files?: "
case $REPLY in
K|k) break ;;
D|d) DELETE=1; break ;;
*) unknown_response ;;
esac
done
fi
if [[ $DELETE && -s $FILES_DELETING ]]; then
# Reading from $FILES_DELETING...
while read SRC; do
rm -f $FILESPATH/"$SRC"
done < $FILES_DELETING
if [[ $DIAG ]]; then
dialog --title "Done" --msgbox \
"$(crunch "The selected $TOPIC have been \
deleted.")" 7 35
else
echo "$(crunch "The $TOPIC have been deleted.")"
fi
fi
else
if [[ $DIAG ]]; then
dialog --title "NOTICE" --msgbox "$(crunch "It appears there are \
no $TOPIC in $FILESPATH.")" 8 30
else
echo "$(crunch "It appears there are no $TOPIC in $FILESPATH.")"
fi
fi
rm -f $FILES{,_CHECKLIST,_DELETING}
}
add_options() {
# Adds pre-build options to SlackBuild
local OPTIONPKG=$1
local ADD_OPTIONS=$SBOPKGTMP/sbopkg_add_options
local OPTIONFILE=$REPO_DIR/$CATEGORY/$APP/options.sbopkg
local CUROPTIONS CHOICE CUSTOMOPTS
if [[ ! -e $OPTIONFILE ]]; then
unset CUROPTIONS
else
CUROPTIONS=$(< $OPTIONFILE)
fi
dialog --visit-items --cancel-label "Clear Options" --inputbox \
"$(crunch "Some SlackBuild scripts offer the ability to pass \
variables, or options, or flavors to the SlackBuild scripts before \
they are run. This is often noted in the README or the SlackBuild \
script itself.\n\nIf you would like to set \
or edit these variables for the $1 SlackBuild, please enter that \
information below, or press <Clear Options> to clear the options.")" \
0 0 "$CUROPTIONS" 2> $ADD_OPTIONS
CHOICE=$?
CUSTOMOPTS="$(< $ADD_OPTIONS)"
if [[ $CHOICE == 1 || ( $CHOICE == 0 && -z $CUSTOMOPTS ) ]]; then
rm -f $OPTIONFILE
continue
elif [[ $CHOICE == 0 ]]; then
cp $ADD_OPTIONS $OPTIONFILE
fi
}
install_package() {
# Install the package.
# This is mostly equivalent to "upgradepkg --reinstall --install-new $1",
# but also checks for package ownership and renames
local INSTDIR=$1
local INSTPKG=$2
local REPLY OLDPKG
# keep the variables we pull up with split_pkg_name() here
local PKG_NAME PKG_VER PKG_ARCH PKG_BUILD PKG_TAG
# ditto the one from get_old_name()
local OLDNAME
if [[ $(find $INSTDIR/$INSTPKG ! -user root -o ! -group root) ]]; then
crunch_fmt "WARNING: The file $INSTPKG is not set with root:root \
permissions! Here is the output of ls -l:"
echo
ls -l $INSTDIR/$INSTPKG
echo
while :; do
read $NFLAG -ep "(P)roceed anyway or (A)bort?: "
case $REPLY in
P|p) echo "Proceeding..."; break ;;
A|a) echo "Aborting..."; return ;;
*) unknown_response ;;
esac
done
fi
split_pkg_name $INSTPKG
get_old_name OLDNAME $PKG_NAME
# we grep ls's output rather than have ls return a glob so 'foo'
# doesn't match 'foo-bar'
if ! OLDPKG=$(ls /var/lib/pkgtools/packages/ |
grep -m1 "^$OLDNAME-[^-]*-[^-]*-[^-]*$REPO_TAG$"); then
OLDPKG=$INSTPKG
fi
/sbin/upgradepkg --reinstall --install-new $OLDPKG%$INSTDIR/$INSTPKG
echo "Done upgrading/installing package."
}
error_read() {
# This function wraps a simple 'read' call. The read itself is skipped if
# $ON_ERROR != "ask", and "Y" is automatically assigned to REPLY when