diff options
Diffstat (limited to 'scripts')
-rw-r--r-- | scripts/.gitignore | 1 | ||||
-rw-r--r-- | scripts/Makefile.am | 39 | ||||
-rw-r--r-- | scripts/library/README | 36 | ||||
-rw-r--r-- | scripts/library/human_to_size.sh | 51 | ||||
-rw-r--r-- | scripts/library/parse_options.sh | 105 | ||||
-rw-r--r-- | scripts/library/parseopts.sh | 141 | ||||
-rw-r--r-- | scripts/library/size_to_human.sh | 22 | ||||
-rw-r--r-- | scripts/makepkg.sh.in | 888 | ||||
-rw-r--r-- | scripts/pacman-db-upgrade.sh.in | 2 | ||||
-rw-r--r-- | scripts/pacman-key.sh.in | 259 | ||||
-rw-r--r-- | scripts/pacman-optimize.sh.in | 49 | ||||
-rw-r--r-- | scripts/pkgdelta.sh.in | 53 | ||||
-rw-r--r-- | scripts/po/POTFILES.in | 2 | ||||
-rw-r--r-- | scripts/rankmirrors.sh.in | 212 | ||||
-rw-r--r-- | scripts/repo-add.sh.in | 91 |
15 files changed, 1039 insertions, 912 deletions
diff --git a/scripts/.gitignore b/scripts/.gitignore index 21b671c0..9e403bfb 100644 --- a/scripts/.gitignore +++ b/scripts/.gitignore @@ -3,7 +3,6 @@ pacman-db-upgrade pacman-key pacman-optimize pkgdelta -rankmirrors repo-add repo-elephant repo-remove diff --git a/scripts/Makefile.am b/scripts/Makefile.am index d89fd306..29c81aa5 100644 --- a/scripts/Makefile.am +++ b/scripts/Makefile.am @@ -4,7 +4,9 @@ AUTOMAKE_OPTIONS = std-options SUBDIRS = po bin_SCRIPTS = \ - $(OURSCRIPTS) + $(OURSCRIPTS) \ + repo-remove \ + repo-elephant OURSCRIPTS = \ makepkg \ @@ -12,7 +14,6 @@ OURSCRIPTS = \ pacman-key \ pacman-optimize \ pkgdelta \ - rankmirrors \ repo-add EXTRA_DIST = \ @@ -21,13 +22,14 @@ EXTRA_DIST = \ pacman-key.sh.in \ pacman-optimize.sh.in \ pkgdelta.sh.in \ - rankmirrors.sh.in \ repo-add.sh.in \ $(LIBRARY) LIBRARY = \ library/output_format.sh \ - library/parse_options.sh + library/parseopts.sh \ + library/human_to_size.sh \ + library/size_to_human.sh # Files that should be removed, but which Automake does not know. MOSTLYCLEANFILES = $(bin_SCRIPTS) @@ -51,6 +53,7 @@ edit = sed \ -e 's|@PACKAGE_BUGREPORT[@]|$(PACKAGE_BUGREPORT)|g' \ -e 's|@PACKAGE_NAME[@]|$(PACKAGE_NAME)|g' \ -e 's|@BUILDSCRIPT[@]|$(BUILDSCRIPT)|g' \ + -e "s|@INODECMD[@]|$(INODECMD)|g" \ -e 's|@SIZECMD[@]|$(SIZECMD)|g' \ -e 's|@SEDINPLACE[@]|$(SEDINPLACE)|g' \ -e 's|@DUPATH[@]|$(DUPATH)|g' \ @@ -60,18 +63,15 @@ edit = sed \ ## All the scripts depend on Makefile so that they are rebuilt when the ## prefix etc. changes. Use chmod -w to prevent people from editing the ## wrong file by accident. -# two 'test' lines- make sure we can handle both sh and py type scripts -# third 'test' line- make sure one of the two checks succeeded $(OURSCRIPTS): Makefile - @echo ' ' GEN $@; - @$(RM) $@ - @test -f $(srcdir)/$@.sh.in && m4 -P -I $(srcdir) $(srcdir)/$@.sh.in | $(edit) >$@ - @chmod +x $@ - @chmod a-w $@ + $(AM_V_at)$(RM) $@ + $(AM_V_GEN)test -f $(srcdir)/$@.sh.in && m4 -P -I $(srcdir) $(srcdir)/$@.sh.in | $(edit) >$@ + $(AM_V_at)chmod +x,a-w $@ + @$(BASH_SHELL) -O extglob -n $@ makepkg: \ $(srcdir)/makepkg.sh.in \ - $(srcdir)/library/parse_options.sh + $(srcdir)/library/parseopts.sh pacman-db-upgrade: \ $(srcdir)/pacman-db-upgrade.sh.in \ @@ -80,7 +80,7 @@ pacman-db-upgrade: \ pacman-key: \ $(srcdir)/pacman-key.sh.in \ $(srcdir)/library/output_format.sh \ - $(srcdir)/library/parse_options.sh + $(srcdir)/library/parseopts.sh pacman-optimize: \ $(srcdir)/pacman-optimize.sh.in \ @@ -88,21 +88,20 @@ pacman-optimize: \ pkgdelta: \ $(srcdir)/pkgdelta.sh.in \ - $(srcdir)/library/output_format.sh - -rankmirrors: $(srcdir)/rankmirrors.sh.in + $(srcdir)/library/output_format.sh \ + $(srcdir)/library/parseopts.sh repo-add: \ $(srcdir)/repo-add.sh.in \ $(srcdir)/library/output_format.sh repo-remove: $(srcdir)/repo-add.sh.in - $(RM) repo-remove - $(LN_S) repo-add repo-remove + $(AM_V_at)$(RM) repo-remove + $(AM_V_at)$(LN_S) repo-add repo-remove repo-elephant: $(srcdir)/repo-add.sh.in - $(RM) repo-elephant - $(LN_S) repo-add repo-elephant + $(AM_V_at)$(RM) repo-elephant + $(AM_V_at)$(LN_S) repo-add repo-elephant install-data-hook: cd $(DESTDIR)$(bindir) && \ diff --git a/scripts/library/README b/scripts/library/README index 1e9c962b..0fa0f847 100644 --- a/scripts/library/README +++ b/scripts/library/README @@ -8,8 +8,34 @@ and can be silenced by defining 'QUIET'. The 'warning' and 'error' functions print to stderr with the appropriate prefix added to the message. -parse_options.sh: -A getopt replacement to avoids portability issues, in particular the -lack of long option name support in the default getopt provided by some -platforms. -Usage: parse_option $SHORT_OPTS $LONG_OPTS "$@" +parseopts.sh: +A getopt_long-like parser which portably supports longopts and shortopts +with some GNU extensions. It does not allow for options with optional +arguments. For both short and long opts, options requiring an argument +should be suffixed with a colon. After the first argument containing +the short opts, any number of valid long opts may be be passed. The end +of the options delimiter must then be added, followed by the user arguments +to the calling program. + +Reccommended Usage: + OPT_SHORT='fb:z' + OPT_LONG=('foo' 'bar:' 'baz') + if ! parseopts "$OPT_SHORT" "${OPT_LONG[@]}" -- "$@"; then + exit 1 + fi + set -- "${OPTRET[@]}" +Returns: + 0: parse success + 1: parse failure (error message supplied) + +human_to_size.sh: +A function to convert human readable sizes (such as "5.3 GiB") to raw byte +equivalents. base10 and base2 suffixes are supported, case sensitively. If +successful, the converted byte value is written to stdout and the function +returns 0. If an error occurs, nothing in written and the function returns 1. +Results may be inaccurate when using a broken implementation of awk, such +as mawk or busybox awk. + +size_to_human.sh: +The reverse of human_to_size, this function takes an integer byte size and +prints its in human readable format, with SI prefixes (e.g. MiB, TiB). diff --git a/scripts/library/human_to_size.sh b/scripts/library/human_to_size.sh new file mode 100644 index 00000000..11613207 --- /dev/null +++ b/scripts/library/human_to_size.sh @@ -0,0 +1,51 @@ +human_to_size() { + awk -v human="$1" ' + function trim(s) { + gsub(/^[[:space:]]+|[[:space:]]+$/, "", s) + return s + } + + function parse_units(units) { + if (!units || units == "B") + return 1 + if (match(units, /^.iB$/)) + return 1024 + if (match(units, /^.B$/)) + return 1000 + if (length(units) == 1) + return 1024 + + # parse failure: invalid base + return -1 + } + + function parse_scale(s) { + return index("BKMGTPE", s) - 1 + } + + function isnumeric(string) { + return match(string, /^[-+]?[[:digit:]]*(\.[[:digit:]]*)?/) + } + + BEGIN { + # peel off the leading number as the size, fail on invalid number + human = trim(human) + if (isnumeric(human)) + size = substr(human, RSTART, RLENGTH) + else + exit 1 + + # the trimmed remainder is assumed to be the units + units = trim(substr(human, RLENGTH + 1)) + + base = parse_units(units) + if (base < 0) + exit 1 + + scale = parse_scale(substr(units, 1, 1)) + if (scale < 0) + exit 1 + + printf "%d\n", size * base^scale + (size + 0 > 0 ? 0.5 : -0.5) + }' +} diff --git a/scripts/library/parse_options.sh b/scripts/library/parse_options.sh deleted file mode 100644 index 039eef92..00000000 --- a/scripts/library/parse_options.sh +++ /dev/null @@ -1,105 +0,0 @@ -# getopt like parser -parse_options() { - local short_options=$1; shift; - local long_options=$1; shift; - local ret=0; - local unused_options="" - local i - - while [[ -n $1 ]]; do - if [[ ${1:0:2} = '--' ]]; then - if [[ -n ${1:2} ]]; then - local match="" - for i in ${long_options//,/ }; do - if [[ ${1:2} = ${i//:} ]]; then - match=$i - break - fi - done - if [[ -n $match ]]; then - local needsargument=0 - - [[ ${match} = ${1:2}: ]] && needsargument=1 - [[ ${match} = ${1:2}:: && -n $2 && ${2:0:1} != "-" ]] && needsargument=1 - - if (( ! needsargument )); then - printf ' %s' "$1" - else - if [[ -n $2 ]]; then - printf ' %s ' "$1" - shift - printf "'%q" "$1" - while [[ -n $2 && ${2:0:1} != "-" ]]; do - shift - printf " %q" "$1" - done - printf "'" - else - printf "@SCRIPTNAME@: $(gettext "option %s requires an argument\n")" "'$1'" >&2 - ret=1 - fi - fi - else - echo "@SCRIPTNAME@: $(gettext "unrecognized option") '$1'" >&2 - ret=1 - fi - else - shift - break - fi - elif [[ ${1:0:1} = '-' ]]; then - for ((i=1; i<${#1}; i++)); do - if [[ $short_options =~ ${1:i:1} ]]; then - local needsargument=0 - - [[ $short_options =~ ${1:i:1}: && ! $short_options =~ ${1:i:1}:: ]] && needsargument=1 - [[ $short_options =~ ${1:i:1}:: && \ - ( -n ${1:$i+1} || ( -n $2 && ${2:0:1} != "-" ) ) ]] && needsargument=1 - - if (( ! needsargument )); then - printf ' -%s' "${1:i:1}" - else - if [[ -n ${1:$i+1} ]]; then - printf ' -%s ' "${1:i:1}" - printf "'%q" "${1:$i+1}" - while [[ -n $2 && ${2:0:1} != "-" ]]; do - shift - printf " %q" "$1" - done - printf "'" - else - if [[ -n $2 ]]; then - printf ' -%s ' "${1:i:1}" - shift - printf "'%q" "$1" - while [[ -n $2 && ${2:0:1} != "-" ]]; do - shift - printf " %q" "$1" - done - printf "'" - - else - printf "@SCRIPTNAME@: $(gettext "option %s requires an argument\n")" "'-${1:i:1}'" >&2 - ret=1 - fi - fi - break - fi - else - echo "@SCRIPTNAME@: $(gettext "unrecognized option") '-${1:i:1}'" >&2 - ret=1 - fi - done - else - unused_options="${unused_options} '$1'" - fi - shift - done - - printf " --" - [[ $unused_options ]] && printf ' %s' "${unused_options[@]}" - [[ $1 ]] && printf " '%s'" "$@" - printf "\n" - - return $ret -} diff --git a/scripts/library/parseopts.sh b/scripts/library/parseopts.sh new file mode 100644 index 00000000..11589ce3 --- /dev/null +++ b/scripts/library/parseopts.sh @@ -0,0 +1,141 @@ +# getopt-like parser +parseopts() { + local opt= optarg= i= shortopts=$1 + local -a longopts=() unused_argv=() + + shift + while [[ $1 && $1 != '--' ]]; do + longopts+=("$1") + shift + done + shift + + longoptmatch() { + local o longmatch=() + for o in "${longopts[@]}"; do + if [[ ${o%:} = "$1" ]]; then + longmatch=("$o") + break + fi + [[ ${o%:} = "$1"* ]] && longmatch+=("$o") + done + + case ${#longmatch[*]} in + 1) + # success, override with opt and return arg req (0 == none, 1 == required) + opt=${longmatch%:} + if [[ $longmatch = *: ]]; then + return 1 + else + return 0 + fi ;; + 0) + # fail, no match found + return 255 ;; + *) + # fail, ambiguous match + printf "@SCRIPTNAME@: $(gettext "option '%s' is ambiguous; possibilities:")" "--$1" + printf " '%s'" "${longmatch[@]%:}" + printf '\n' + return 254 ;; + esac >&2 + } + + while (( $# )); do + case $1 in + --) # explicit end of options + shift + break + ;; + -[!-]*) # short option + for (( i = 1; i < ${#1}; i++ )); do + opt=${1:i:1} + + # option doesn't exist + if [[ $shortopts != *$opt* ]]; then + printf "@SCRIPTNAME@: $(gettext "invalid option") -- '%s'\n" "$opt" >&2 + OPTRET=(--) + return 1 + fi + + OPTRET+=("-$opt") + # option requires optarg + if [[ $shortopts = *$opt:* ]]; then + # if we're not at the end of the option chunk, the rest is the optarg + if (( i < ${#1} - 1 )); then + OPTRET+=("${1:i+1}") + break + # if we're at the end, grab the the next positional, if it exists + elif (( i == ${#1} - 1 )) && [[ $2 ]]; then + OPTRET+=("$2") + shift + break + # parse failure + else + printf "@SCRIPTNAME@: $(gettext "option requires an argument") -- '%s'\n" "$opt" >&2 + OPTRET=(--) + return 1 + fi + fi + done + ;; + --?*=*|--?*) # long option + IFS='=' read -r opt optarg <<< "${1#--}" + longoptmatch "$opt" + case $? in + 0) + # parse failure + if [[ $optarg ]]; then + printf "@SCRIPTNAME@: $(gettext "option '%s' does not allow an argument")\n" "--$opt" >&2 + OPTRET=(--) + return 1 + # --longopt + else + OPTRET+=("--$opt") + shift + continue 2 + fi + ;; + 1) + # --longopt=optarg + if [[ $optarg ]]; then + OPTRET+=("--$opt" "$optarg") + shift + # --longopt optarg + elif [[ $2 ]]; then + OPTRET+=("--$opt" "$2" ) + shift 2 + # parse failure + else + printf "@SCRIPTNAME@: $(gettext "option '%s' requires an argument")\n" "--$opt" >&2 + OPTRET=(--) + return 1 + fi + continue 2 + ;; + 254) + # ambiguous option -- error was reported for us by longoptmatch() + OPTRET=(--) + return 1 + ;; + 255) + # parse failure + printf "@SCRIPTNAME@: $(gettext "invalid option") '--%s'\n" "$opt" >&2 + OPTRET=(--) + return 1 + ;; + esac + ;; + *) # non-option arg encountered, add it as a parameter + unused_argv+=("$1") + ;; + esac + shift + done + + # add end-of-opt terminator and any leftover positional parameters + OPTRET+=('--' "${unused_argv[@]}" "$@") + unset longoptmatch + + return 0 +} diff --git a/scripts/library/size_to_human.sh b/scripts/library/size_to_human.sh new file mode 100644 index 00000000..1d13eeb4 --- /dev/null +++ b/scripts/library/size_to_human.sh @@ -0,0 +1,22 @@ +size_to_human() { + awk -v size="$1" ' + BEGIN { + suffix[1] = "B" + suffix[2] = "KiB" + suffix[3] = "MiB" + suffix[4] = "GiB" + suffix[5] = "TiB" + suffix[6] = "PiB" + suffix[7] = "EiB" + count = 1 + + while (size > 1024) { + size /= 1024 + count++ + } + + sizestr = sprintf("%.2f", size) + sub(/\.?0+$/, "", sizestr) + printf("%s %s", sizestr, suffix[count]) + }' +} diff --git a/scripts/makepkg.sh.in b/scripts/makepkg.sh.in index e21418a6..b30e9d04 100644 --- a/scripts/makepkg.sh.in +++ b/scripts/makepkg.sh.in @@ -1,4 +1,4 @@ -#!/bin/bash -e +#!/bin/bash # # makepkg - make packages compatible for use with pacman # @configure_input@ @@ -39,19 +39,20 @@ export COMMAND_MODE='legacy' # Ensure CDPATH doesn't screw with our cd calls unset CDPATH -myver='@PACKAGE_VERSION@' -confdir='@sysconfdir@' -BUILDSCRIPT='@BUILDSCRIPT@' -startdir="$PWD" +declare -r myver='@PACKAGE_VERSION@' +declare -r confdir='@sysconfdir@' +declare -r BUILDSCRIPT='@BUILDSCRIPT@' +declare -r startdir="$PWD" packaging_options=('strip' 'docs' 'libtool' 'emptydirs' 'zipman' 'purge' 'upx') other_options=('ccache' 'distcc' 'buildflags' 'makeflags') -splitpkg_overrides=('pkgver' 'pkgrel' 'epoch' 'pkgdesc' 'arch' 'license' \ +splitpkg_overrides=('pkgver' 'pkgrel' 'epoch' 'pkgdesc' 'arch' 'url' 'license' \ 'groups' 'depends' 'optdepends' 'provides' 'conflicts' \ 'replaces' 'backup' 'options' 'install' 'changelog') readonly -a packaging_options other_options splitpkg_overrides # Options +ASDEPS=0 ASROOT=0 CLEANUP=0 DEP_BIN=0 @@ -70,6 +71,7 @@ LOGGING=0 SOURCEONLY=0 IGNOREARCH=0 HOLDVER=0 +PREPAREFUNC=0 BUILDFUNC=0 CHECKFUNC=0 PKGFUNC=0 @@ -168,7 +170,7 @@ clean_up() { # clean up dangling symlinks to packages for pkg in ${pkgname[@]}; do - for file in ${pkg}-*-*-${CARCH}{${PKGEXT},${SRCEXT}}; do + for file in ${pkg}-*-*-*{${PKGEXT},${SRCEXT}}; do if [[ -h $file && ! -e $file ]]; then rm -f "$file" fi @@ -211,7 +213,7 @@ get_filepath() { return 1 fi - echo "$file" + printf "%s\n" "$file" } # Print 'source not found' error message and exit makepkg @@ -226,13 +228,13 @@ get_filename() { # if a filename is specified, use it local filename="${1%%::*}" # if it is just an URL, we only keep the last component - echo "${filename##*/}" + printf "%s\n" "${filename##*/}" } # extract the URL from a source entry get_url() { # strip an eventual filename - echo "${1#*::}" + printf "%s\n" "${1#*::}" } ## @@ -242,9 +244,9 @@ get_url() { get_full_version() { if [[ -z $1 ]]; then if [[ $epoch ]] && (( ! $epoch )); then - echo $pkgver-$pkgrel + printf "%s\n" "$pkgver-$pkgrel" else - echo $epoch:$pkgver-$pkgrel + printf "%s\n" "$epoch:$pkgver-$pkgrel" fi else for i in pkgver pkgrel epoch; do @@ -253,9 +255,32 @@ get_full_version() { [[ -z ${!indirect} ]] && eval ${indirect}=\"${!i}\" done if (( ! $epoch_override )); then - echo $pkgver_override-$pkgrel_override + printf "%s\n" "$pkgver_override-$pkgrel_override" else - echo $epoch_override:$pkgver_override-$pkgrel_override + printf "%s\n" "$epoch_override:$pkgver_override-$pkgrel_override" + fi + fi +} + +## +# usage : get_pkg_arch( [$pkgname] ) +# return : architecture of the package +## +get_pkg_arch() { + if [[ -z $1 ]]; then + if [[ $arch = "any" ]]; then + printf "%s\n" "any" + else + printf "%s\n" "$CARCH" + fi + else + local arch_override + eval $(declare -f package_$1 | sed -n 's/\(^[[:space:]]*arch=\)/arch_override=/p') + (( ${#arch_override[@]} == 0 )) && arch_override=("${arch[@]}") + if [[ $arch_override = "any" ]]; then + printf "%s\n" "any" + else + printf "%s\n" "$CARCH" fi fi } @@ -264,63 +289,84 @@ get_full_version() { # Checks to see if options are present in makepkg.conf or PKGBUILD; # PKGBUILD options always take precedence. # -# usage : check_option( $option ) -# return : y - enabled -# n - disabled -# ? - not found +# usage : check_option( $option, $expected_val ) +# return : 0 - matches expected +# 1 - does not match expected +# 127 - not found ## check_option() { - local ret=$(in_opt_array "$1" ${options[@]}) - if [[ $ret != '?' ]]; then - echo $ret - return - fi + in_opt_array "$1" ${options[@]} + case $? in + 0) # assert enabled + [[ $2 = y ]] + return ;; + 1) # assert disabled + [[ $2 = n ]] + return + esac # fall back to makepkg.conf options - ret=$(in_opt_array "$1" ${OPTIONS[@]}) - if [[ $ret != '?' ]]; then - echo $ret - return - fi + in_opt_array "$1" ${OPTIONS[@]} + case $? in + 0) # assert enabled + [[ $2 = y ]] + return ;; + 1) # assert disabled + [[ $2 = n ]] + return + esac - echo '?' # Not Found + # not found + return 127 } ## # Check if option is present in BUILDENV # -# usage : check_buildenv( $option ) -# return : y - enabled -# n - disabled -# ? - not found +# usage : check_buildenv( $option, $expected_val ) +# return : 0 - matches expected +# 1 - does not match expected +# 127 - not found ## check_buildenv() { in_opt_array "$1" ${BUILDENV[@]} + case $? in + 0) # assert enabled + [[ $2 = "y" ]] + return ;; + 1) # assert disabled + [[ $2 = "n" ]] + return ;; + esac + + # not found + return 127 } ## # usage : in_opt_array( $needle, $haystack ) -# return : y - enabled -# n - disabled -# ? - not found +# return : 0 - enabled +# 1 - disabled +# 127 - not found ## in_opt_array() { local needle=$1; shift local opt for opt in "$@"; do - if [[ $opt = $needle ]]; then - echo 'y' # Enabled - return + if [[ $opt = "$needle" ]]; then + # enabled + return 0 elif [[ $opt = "!$needle" ]]; then - echo 'n' # Disabled - return + # disabled + return 1 fi done - echo '?' # Not Found + # not found + return 127 } @@ -333,15 +379,15 @@ in_array() { local needle=$1; shift local item for item in "$@"; do - [[ $item = $needle ]] && return 0 # Found + [[ $item = "$needle" ]] && return 0 # Found done return 1 # Not Found } -source_has_signatures(){ +source_has_signatures() { local file for file in "${source[@]}"; do - if [[ $file = *.@(sig?(n)|asc) ]]; then + if [[ ${file%%::*} = *.@(sig?(n)|asc) ]]; then return 0 fi done @@ -357,7 +403,7 @@ get_downloadclient() { local i for i in "${DLAGENTS[@]}"; do local handler="${i%%::*}" - if [[ $proto = $handler ]]; then + if [[ $proto = "$handler" ]]; then local agent="${i##*::}" break fi @@ -379,7 +425,7 @@ get_downloadclient() { exit 1 # $E_MISSING_PROGRAM fi - echo "$agent" + printf "%s\n" "$agent" } download_file() { @@ -420,33 +466,30 @@ download_file() { run_pacman() { local cmd if [[ ! $1 = -@(T|Qq) ]]; then - printf -v cmd "%q " "$PACMAN" $PACMAN_OPTS "$@" + cmd=("$PACMAN" $PACMAN_OPTS "$@") else - printf -v cmd "%q " "$PACMAN" "$@" + cmd=("$PACMAN" "$@") fi if (( ! ASROOT )) && [[ ! $1 = -@(T|Qq) ]]; then if type -p sudo >/dev/null; then - cmd="sudo $cmd" + cmd=(sudo "${cmd[@]}") else - cmd="su root -c '$cmd'" + cmd=(su root -c "$(printf '%q ' "${cmd[@]}")") fi fi - eval "$cmd" + "${cmd[@]}" } check_deps() { (( $# > 0 )) || return 0 - # Disable error trap in pacman subshell call as this breaks bash-3.2 compatibility - # Also, a non-zero return value is not unexpected and we are manually dealing them - set +E local ret=0 local pmout - pmout=$(run_pacman -T "$@") || ret=$? - set -E + pmout=$(run_pacman -T "$@") + ret=$? if (( ret == 127 )); then #unresolved deps - echo "$pmout" + printf "%s\n" "$pmout" elif (( ret )); then error "$(gettext "'%s' returned a fatal error (%i): %s")" "$PACMAN" "$ret" "$pmout" return "$ret" @@ -476,13 +519,11 @@ handle_deps() { fi # we might need the new system environment - # avoid triggering the ERR trap and exiting - set +e - local restoretrap=$(trap -p ERR) - trap - ERR + # save our shell options and turn off extglob + local shellopts=$(shopt -p) + shopt -u extglob source /etc/profile &>/dev/null - eval $restoretrap - set -e + eval "$shellopts" return $R_DEPS_SATISFIED } @@ -557,7 +598,7 @@ download_sources() { local url=$(get_url "$netfile") # if we get here, check to make sure it was a URL, else fail - if [[ $file = $url ]]; then + if [[ $file = "$url" ]]; then error "$(gettext "%s was not found in the build directory and is not a URL.")" "$file" exit 1 # $E_MISSING_FILE fi @@ -594,9 +635,9 @@ get_integlist() { done if (( ${#integlist[@]} > 0 )); then - echo ${integlist[@]} + printf "%s\n" "${integlist[@]}" else - echo ${INTEGRITY_CHECK[@]} + printf "%s\n" "${INTEGRITY_CHECK[@]}" fi } @@ -626,7 +667,7 @@ generate_checksums() { local ct=0 local numsrc=${#source[@]} - echo -n "${integ}sums=(" + printf "%s" "${integ}sums=(" local i local indent='' @@ -640,8 +681,8 @@ generate_checksums() { file="$(get_filepath "$netfile")" || missing_source_file "$netfile" local sum="$(openssl dgst -${integ} "$file")" sum=${sum##* } - (( ct )) && echo -n "$indent" - echo -n "'$sum'" + (( ct )) && printf "%s" "$indent" + printf "%s" "'$sum'" ct=$(($ct+1)) (( $ct < $numsrc )) && echo done @@ -667,7 +708,7 @@ check_checksums() { for file in "${source[@]}"; do local found=1 file="$(get_filename "$file")" - echo -n " $file ... " >&2 + printf "%s" " $file ... " >&2 if ! file="$(get_filepath "$file")"; then printf -- "$(gettext "NOT FOUND")\n" >&2 @@ -676,14 +717,18 @@ check_checksums() { fi if (( $found )) ; then - local expectedsum=$(tr '[:upper:]' '[:lower:]' <<< "${integrity_sums[$idx]}") - local realsum="$(openssl dgst -${integ} "$file")" - realsum="${realsum##* }" - if [[ $expectedsum = $realsum ]]; then - printf -- "$(gettext "Passed")\n" >&2 + if [[ ${integrity_sums[$idx]} = 'SKIP' ]]; then + echo "$(gettext "Skipped")" >&2 else - printf -- "$(gettext "FAILED")\n" >&2 - errors=1 + local expectedsum=$(tr '[:upper:]' '[:lower:]' <<< "${integrity_sums[$idx]}") + local realsum="$(openssl dgst -${integ} "$file")" + realsum="${realsum##* }" + if [[ $expectedsum = "$realsum" ]]; then + printf -- "$(gettext "Passed")\n" >&2 + else + printf -- "$(gettext "FAILED")\n" >&2 + errors=1 + fi fi fi @@ -837,10 +882,10 @@ extract_sources() { local ret=0 msg2 "$(gettext "Extracting %s with %s")" "$file" "$cmd" - if [[ $cmd = bsdtar ]]; then + if [[ $cmd = "bsdtar" ]]; then $cmd -xf "$file" || ret=$? else - rm -f "${file%.*}" + rm -f -- "${file%.*}" $cmd -dcf "$file" > "${file%.*}" || ret=$? fi if (( ret )); then @@ -869,6 +914,40 @@ error_function() { exit 2 # $E_BUILD_FAILED } +cd_safe() { + if ! cd "$1"; then + error "$(gettext "Failed to change to directory %s")" "$1" + plain "$(gettext "Aborting...")" + exit 1 + fi +} + +source_safe() { + shopt -u extglob + if ! source "$@"; then + error "$(gettext "Failed to source %s")" "$1" + exit 1 + fi + shopt -s extglob +} + +run_function_safe() { + local restoretrap + + set -e + set -E + + restoretrap=$(trap -p ERR) + trap 'error_function $pkgfunc' ERR + + run_function "$1" + + eval $restoretrap + + set +E + set +e +} + run_function() { if [[ -z $1 ]]; then return 1 @@ -876,25 +955,24 @@ run_function() { local pkgfunc="$1" # clear user-specified buildflags if requested - if [[ $(check_option buildflags) = "n" ]]; then - unset CFLAGS CXXFLAGS LDFLAGS + if check_option "buildflags" "n"; then + unset CPPFLAGS CFLAGS CXXFLAGS LDFLAGS fi # clear user-specified makeflags if requested - if [[ $(check_option makeflags) = "n" ]]; then + if check_option "makeflags" "n"; then unset MAKEFLAGS fi msg "$(gettext "Starting %s()...")" "$pkgfunc" - cd "$srcdir" + cd_safe "$srcdir" # ensure all necessary build variables are exported - export CFLAGS CXXFLAGS LDFLAGS MAKEFLAGS CHOST + export CPPFLAGS CFLAGS CXXFLAGS LDFLAGS MAKEFLAGS CHOST # save our shell options so pkgfunc() can't override what we need local shellopts=$(shopt -p) local ret=0 - local restoretrap if (( LOGGING )); then local fullver=$(get_full_version) local BUILDLOG="${startdir}/${pkgbase}-${fullver}-${CARCH}-$pkgfunc.log" @@ -916,40 +994,38 @@ run_function() { tee "$BUILDLOG" < "$logpipe" & local teepid=$! - restoretrap=$(trap -p ERR) - trap 'error_function $pkgfunc' ERR $pkgfunc &>"$logpipe" - eval $restoretrap wait $teepid rm "$logpipe" else - restoretrap=$(trap -p ERR) - trap 'error_function $pkgfunc' ERR $pkgfunc 2>&1 - eval $restoretrap fi # reset our shell options eval "$shellopts" } +run_prepare() { + run_function_safe "prepare" +} + run_build() { # use distcc if it is requested (check buildenv and PKGBUILD opts) - if [[ $(check_buildenv distcc) = "y" && $(check_option distcc) != "n" ]]; then + if check_buildenv "distcc" "y" && ! check_option "distc" "n"; then [[ -d /usr/lib/distcc/bin ]] && export PATH="/usr/lib/distcc/bin:$PATH" export DISTCC_HOSTS fi # use ccache if it is requested (check buildenv and PKGBUILD opts) - if [[ $(check_buildenv ccache) = "y" && $(check_option ccache) != "n" ]]; then + if check_buildenv "ccache" "y" && ! check_option "ccache" "n"; then [[ -d /usr/lib/ccache/bin ]] && export PATH="/usr/lib/ccache/bin:$PATH" fi - run_function "build" + run_function_safe "build" } run_check() { - run_function "check" + run_function_safe "check" } run_package() { @@ -960,73 +1036,65 @@ run_package() { pkgfunc="package_$1" fi - run_function "$pkgfunc" + run_function_safe "$pkgfunc" } tidy_install() { - cd "$pkgdir" + cd_safe "$pkgdir" msg "$(gettext "Tidying install...")" - if [[ $(check_option docs) = "n" && -n ${DOC_DIRS[*]} ]]; then + if check_option "docs" "n" && [[ -n ${DOC_DIRS[*]} ]]; then msg2 "$(gettext "Removing doc files...")" - rm -rf ${DOC_DIRS[@]} + rm -rf -- ${DOC_DIRS[@]} fi - if [[ $(check_option purge) = "y" && -n ${PURGE_TARGETS[*]} ]]; then + if check_option "purge" "y" && [[ -n ${PURGE_TARGETS[*]} ]]; then msg2 "$(gettext "Purging unwanted files...")" local pt for pt in "${PURGE_TARGETS[@]}"; do - if [[ ${pt} = ${pt//\/} ]]; then - find . -type f -name "${pt}" -exec rm -f -- '{}' \; + if [[ ${pt} = "${pt//\/}" ]]; then + find . ! -type d -name "${pt}" -exec rm -f -- '{}' \; else rm -f ${pt} fi done fi - if [[ $(check_option zipman) = "y" && -n ${MAN_DIRS[*]} ]]; then + if check_option "zipman" "y" && [[ -n ${MAN_DIRS[*]} ]]; then msg2 "$(gettext "Compressing man and info pages...")" - local manpage ext file link hardlinks hl - find ${MAN_DIRS[@]} -type f 2>/dev/null | - while read manpage ; do - ext="${manpage##*.}" - file="${manpage##*/}" - if [[ $ext != gz && $ext != bz2 ]]; then - # update symlinks to this manpage - find ${MAN_DIRS[@]} -lname "$file" 2>/dev/null | - while read link ; do + local file files inode link + while read -rd ' ' inode; do + read file + find ${MAN_DIRS[@]} -type l 2>/dev/null | + while read link ; do + if [[ "${file}" -ef "${link}" ]] ; then rm -f "$link" "${link}.gz" - ln -s "${file}.gz" "${link}.gz" - done - - # check file still exists (potentially already compressed due to hardlink) - if [[ -f ${manpage} ]]; then - # find hard links and remove them - # the '|| true' part keeps the script from bailing on the EOF returned - # by read at the end of the find output - IFS=$'\n' read -rd '' -a hardlinks < \ - <(find ${MAN_DIRS[@]} \! -name "$file" -samefile "$manpage" \ - 2>/dev/null || true) || true - rm -f "${hardlinks[@]}" - # compress the original - gzip -9 "$manpage" - # recreate hard links removed earlier - for hl in "${hardlinks[@]}"; do - ln "${manpage}.gz" "${hl}.gz" - chmod 644 ${hl}.gz - done + if [[ ${file%/*} = ${link%/*} ]]; then + ln -s -- "${file##*/}.gz" "${link}.gz" + else + ln -s -- "/${file}.gz" "${link}.gz" + fi fi + done + if [[ -z ${files[$inode]} ]]; then + files[$inode]=$file + gzip -9 -f "$file" + else + rm -f "$file" + ln "${files[$inode]}.gz" "${file}.gz" + chmod 644 "${file}.gz" fi - done + done < <(find ${MAN_DIRS[@]} -type f \! -name "*.gz" \! -name "*.bz2" \ + -exec @INODECMD@ '{}' + 2>/dev/null) fi - if [[ $(check_option strip) = y ]]; then + if check_option "strip" "y"; then msg2 "$(gettext "Stripping unneeded symbols from binaries and libraries...")" # make sure library stripping variables are defined to prevent excess stripping [[ -z ${STRIP_SHARED+x} ]] && STRIP_SHARED="-S" [[ -z ${STRIP_STATIC+x} ]] && STRIP_STATIC="-S" local binary - find . -type f -perm -u+w 2>/dev/null | while read binary ; do + find . -type f -perm -u+w -print0 2>/dev/null | while read -d '' binary ; do case "$(file -bi "$binary")" in *application/x-sharedlib*) # Libraries (.so) strip $STRIP_SHARED "$binary";; @@ -1038,17 +1106,17 @@ tidy_install() { done fi - if [[ $(check_option libtool) = "n" ]]; then + if check_option "libtool" "n"; then msg2 "$(gettext "Removing "%s" files...")" "libtool" find . ! -type d -name "*.la" -exec rm -f -- '{}' \; fi - if [[ $(check_option emptydirs) = "n" ]]; then + if check_option "emptydirs" "n"; then msg2 "$(gettext "Removing empty directories...")" - find . -depth -type d -empty -delete + find . -depth -type d -exec rmdir '{}' + 2>/dev/null fi - if [[ $(check_option upx) = "y" ]]; then + if check_option "upx" "y"; then msg2 "$(gettext "Compressing binaries with %s...")" "UPX" local binary find . -type f -perm -u+w 2>/dev/null | while read binary ; do @@ -1061,12 +1129,29 @@ tidy_install() { } find_libdepends() { - local libdepends - find "$pkgdir" -type f -perm -u+x | while read filename - do + local d sodepends; + + sodepends=0; + for d in "${depends[@]}"; do + if [[ $d = *.so ]]; then + sodepends=1; + break; + fi + done + + if (( sodepends == 0 )); then + printf '%s\n' "${depends[@]}" + return; + fi + + local libdeps filename soarch sofile soname soversion; + declare -A libdeps; + + while read filename; do # get architecture of the file; if soarch is empty it's not an ELF binary soarch=$(LC_ALL=C readelf -h "$filename" 2>/dev/null | sed -n 's/.*Class.*ELF\(32\|64\)/\1/p') - [ -n "$soarch" ] || continue + [[ -n "$soarch" ]] || continue + # process all libraries needed by the binary for sofile in $(LC_ALL=C readelf -d "$filename" 2>/dev/null | sed -nr 's/.*Shared library: \[(.*)\].*/\1/p') do @@ -1074,42 +1159,97 @@ find_libdepends() { soname="${sofile%.so?(+(.+([0-9])))}".so # extract the major version: 1 soversion="${sofile##*\.so\.}" - if in_array "${soname}" ${depends[@]}; then - if ! in_array "${soname}=${soversion}-${soarch}" ${libdepends[@]}; then - # libfoo.so=1-64 - echo "${soname}=${soversion}-${soarch}" - libdepends=(${libdepends[@]} "${soname}=${soversion}-${soarch}") + + if [[ ${libdeps[$soname]} ]]; then + if [[ ${libdeps[$soname]} != *${soversion}-${soarch}* ]]; then + libdeps[$soname]+=" ${soversion}-${soarch}" fi + else + libdeps[$soname]="${soversion}-${soarch}" fi done + done < <(find "$pkgdir" -type f -perm -u+x) + + local libdepends v + for d in "${depends[@]}"; do + case "$d" in + *.so) + if [[ ${libdeps[$d]} ]]; then + for v in ${libdeps[$d]}; do + libdepends+=("$d=$v") + done + else + warning "$(gettext "Library listed in %s is not required by any files: %s")" "'depends'" "$d" + libdepends+=("$d") + fi + ;; + *) + libdepends+=("$d") + ;; + esac done + + printf '%s\n' "${libdepends[@]}" } -find_libprovides() { - local libprovides - find "$pkgdir" -type f -name \*.so\* | while read filename - do - # check if we really have a shared object - if LC_ALL=C readelf -h "$filename" 2>/dev/null | grep -q '.*Type:.*DYN (Shared object file).*'; then - # 64 - soarch=$(LC_ALL=C readelf -h "$filename" | sed -n 's/.*Class.*ELF\(32\|64\)/\1/p') - # get the string binaries link to: libfoo.so.1.2 -> libfoo.so.1 - sofile=$(LC_ALL=C readelf -d "$filename" 2>/dev/null | sed -n 's/.*Library soname: \[\(.*\)\].*/\1/p') - [ -z "$sofile" ] && sofile="${filename##*/}" - # extract the library name: libfoo.so - soname="${sofile%%\.so\.*}.so" - # extract the major version: 1 - soversion="${sofile##*\.so\.}" - if in_array "${soname}" ${provides[@]}; then - if ! in_array "${soname}=${soversion}-${soarch}" ${libprovides[@]}; then - # libfoo.so=1-64 - echo "${soname}=${soversion}-${soarch}" - libprovides=(${libprovides[@]} "${soname}=${soversion}-${soarch}") +find_libprovides() { + local p libprovides missing + for p in "${provides[@]}"; do + missing=0 + case "$p" in + *.so) + mapfile -t filename < <(find "$pkgdir" -type f -name $p\*) + if [[ $filename ]]; then + # packages may provide multiple versions of the same library + for fn in "${filename[@]}"; do + # check if we really have a shared object + if LC_ALL=C readelf -h "$fn" 2>/dev/null | grep -q '.*Type:.*DYN (Shared object file).*'; then + # get the string binaries link to (e.g. libfoo.so.1.2 -> libfoo.so.1) + local sofile=$(LC_ALL=C readelf -d "$fn" 2>/dev/null | sed -n 's/.*Library soname: \[\(.*\)\].*/\1/p') + if [[ -z "$sofile" ]]; then + warning "$(gettext "Library listed in %s is not versioned: %s")" "'provides'" "$p" + libprovides+=("$p") + continue + fi + + # get the library architecture (32 or 64 bit) + local soarch=$(LC_ALL=C readelf -h "$fn" | sed -n 's/.*Class.*ELF\(32\|64\)/\1/p') + + # extract the library major version + local soversion="${sofile##*\.so\.}" + + libprovides+=("${p}=${soversion}-${soarch}") + else + warning "$(gettext "Library listed in %s is not a shared object: %s")" "'provides'" "$p" + libprovides+=("$p") + fi + done + else + libprovides+=("$p") + missing=1 fi - fi - fi + ;; + *) + libprovides+=("$p") + ;; + esac + + if (( missing )); then + warning "$(gettext "Can not find library listed in %s: %s")" "'provides'" "$p" + fi done + + printf '%s\n' "${libprovides[@]}" +} + +check_license() { + # TODO maybe remove this at some point + # warn if license array is not present or empty + if [[ -z $license ]]; then + warning "$(gettext "Please add a license line to your %s!")" "$BUILDSCRIPT" + plain "$(gettext "Example for GPL\'ed software: %s.")" "license=('GPL')" + fi } write_pkginfo() { @@ -1134,79 +1274,48 @@ write_pkginfo() { echo "# using $(fakeroot -v)" fi echo "# $(LC_ALL=C date -u)" - echo "pkgname = $1" + printf "pkgname = %s\n" "$1" (( SPLITPKG )) && echo pkgbase = $pkgbase echo "pkgver = $(get_full_version)" - echo "pkgdesc = $pkgdesc" - echo "url = $url" - echo "builddate = $builddate" - echo "packager = $packager" - echo "size = $size" - echo "arch = $PKGARCH" - - [[ $license ]] && printf "license = %s\n" "${license[@]}" - [[ $replaces ]] && printf "replaces = %s\n" "${replaces[@]}" - [[ $groups ]] && printf "group = %s\n" "${groups[@]}" - [[ $optdepends ]] && printf "optdepend = %s\n" "${optdepends[@]//+([[:space:]])/ }" - [[ $conflicts ]] && printf "conflict = %s\n" "${conflicts[@]}" - [[ $backup ]] && printf "backup = %s\n" "${backup[@]}" + printf "pkgdesc = %s\n" "$pkgdesc" + printf "url = %s\n" "$url" + printf "builddate = %s\n" "$builddate" + printf "packager = %s\n" "$packager" + printf "size = %s\n" "$size" + printf "arch = %s\n" "$pkgarch" + + mapfile -t provides < <(find_libprovides) + mapfile -t depends < <(find_libdepends) + + [[ $license ]] && printf "license = %s\n" "${license[@]}" + [[ $replaces ]] && printf "replaces = %s\n" "${replaces[@]}" + [[ $groups ]] && printf "group = %s\n" "${groups[@]}" + [[ $conflicts ]] && printf "conflict = %s\n" "${conflicts[@]}" + [[ $provides ]] && printf "provides = %s\n" "${provides[@]}" + [[ $backup ]] && printf "backup = %s\n" "${backup[@]}" + [[ $depends ]] && printf "depend = %s\n" "${depends[@]}" + [[ $optdepends ]] && printf "optdepend = %s\n" "${optdepends[@]//+([[:space:]])/ }" + [[ $makedepends ]] && printf "makedepend = %s\n" "${makedepends[@]}" + [[ $checkdepends ]] && printf "checkdepend = %s\n" "${checkdepends[@]}" local it - - libprovides=$(find_libprovides) - libdepends=$(find_libdepends) - provides=("${provides[@]}" ${libprovides}) - depends=("${depends[@]}" ${libdepends}) - - for it in "${depends[@]}"; do - if [[ $it = *.so ]]; then - # check if the entry has been found by find_libdepends - # if not, it's unneeded; tell the user so he can remove it - printf -v re '(^|\s)%s=.*' "$it" - if [[ ! $libdepends =~ $re ]]; then - error "$(gettext "Cannot find library listed in %s: %s")" "'depends'" "$it" - return 1 - fi - else - echo "depend = $it" - fi - done - - for it in "${provides[@]}"; do - # ignore versionless entires (those come from the PKGBUILD) - if [[ $it = *.so ]]; then - # check if the entry has been found by find_libprovides - # if not, it's unneeded; tell the user so he can remove it - if [[ ! $libprovides =~ (^|\s)${it}=.* ]]; then - error "$(gettext "Cannot find library listed in %s: %s")" "'provides'" "$it" - return 1 - fi - else - echo "provides = $it" - fi - done - for it in "${packaging_options[@]}"; do - local ret="$(check_option $it)" - if [[ $ret != "?" ]]; then - if [[ $ret = y ]]; then - echo "makepkgopt = $it" - else - echo "makepkgopt = !$it" - fi - fi + check_option "$it" "y" + case $? in + 0) + printf "makepkgopt = %s\n" "$it" + ;; + 1) + printf "makepkgopt = %s\n" "!$it" + ;; + esac done - # TODO maybe remove this at some point - # warn if license array is not present or empty - if [[ -z $license ]]; then - warning "$(gettext "Please add a license line to your %s!")" "$BUILDSCRIPT" - plain "$(gettext "Example for GPL\'ed software: %s.")" "license=('GPL')" - fi + check_license } check_package() { - cd "$pkgdir" + cd_safe "$pkgdir" # check existence of backup files local file @@ -1235,7 +1344,7 @@ create_package() { check_package - cd "$pkgdir" + cd_safe "$pkgdir" msg "$(gettext "Creating package...")" local nameofpkg @@ -1245,15 +1354,11 @@ create_package() { nameofpkg="$1" fi - if [[ $arch = "any" ]]; then - PKGARCH="any" - else - PKGARCH=$CARCH - fi + pkgarch=$(get_pkg_arch) write_pkginfo $nameofpkg > .PKGINFO - local comp_files=".PKGINFO" + local comp_files=('.PKGINFO') # check for changelog/install files for i in 'changelog/.CHANGELOG' 'install/.INSTALL'; do @@ -1263,7 +1368,7 @@ create_package() { msg2 "$(gettext "Adding %s file...")" "$orig" cp "$startdir/${!orig}" "$dest" chmod 644 "$dest" - comp_files+=" $dest" + comp_files+=("$dest") fi done @@ -1271,7 +1376,7 @@ create_package() { msg2 "$(gettext "Compressing package...")" local fullver=$(get_full_version) - local pkg_file="$PKGDEST/${nameofpkg}-${fullver}-${PKGARCH}${PKGEXT}" + local pkg_file="$PKGDEST/${nameofpkg}-${fullver}-${pkgarch}${PKGEXT}" local ret=0 [[ -f $pkg_file ]] && rm -f "$pkg_file" @@ -1285,12 +1390,12 @@ create_package() { # bsdtar's gzip compression always saves the time stamp, making one # archive created using the same command line distinct from another. # Disable bsdtar compression and use gzip -n for now. - bsdtar -cf - $comp_files * | + bsdtar -cf - "${comp_files[@]}" * | case "$PKGEXT" in - *tar.gz) gzip -c -f -n ;; - *tar.bz2) bzip2 -c -f ;; - *tar.xz) xz -c -z - ;; - *tar.Z) compress -c -f ;; + *tar.gz) ${COMPRESSGZ[@]:-gzip -c -f -n} ;; + *tar.bz2) ${COMPRESSBZ2[@]:-bzip2 -c -f} ;; + *tar.xz) ${COMPRESSXZ[@]:-xz -c -z -} ;; + *tar.Z) ${COMPRESSZ[@]:-compress -c -f} ;; *tar) cat ;; *) warning "$(gettext "'%s' is not a valid archive extension.")" \ "$PKGEXT"; cat ;; @@ -1350,12 +1455,14 @@ create_srcpackage() { local srclinks="$(mktemp -d "$startdir"/srclinks.XXXXXXXXX)" mkdir "${srclinks}"/${pkgbase} + check_license + msg2 "$(gettext "Adding %s...")" "$BUILDSCRIPT" ln -s "${BUILDFILE}" "${srclinks}/${pkgbase}/${BUILDSCRIPT}" local file for file in "${source[@]}"; do - if [[ "$file" == $(get_filename "$file") ]] || (( SOURCEONLY == 2 )); then + if [[ "$file" = "$(get_filename "$file")" ]] || (( SOURCEONLY == 2 )); then local absfile absfile=$(get_filepath "$file") || missing_source_file "$file" msg2 "$(gettext "Adding %s...")" "${absfile##*/}" @@ -1392,7 +1499,7 @@ create_srcpackage() { # tar it up msg2 "$(gettext "Compressing source package...")" - cd "${srclinks}" + cd_safe "${srclinks}" if ! bsdtar -c${TAR_OPT}Lf "$pkg_file" ${pkgbase}; then error "$(gettext "Failed to create source package file.")" exit 1 # TODO: error code @@ -1408,7 +1515,7 @@ create_srcpackage() { warning "$(gettext "Failed to create symlink to source package file.")" fi - cd "${startdir}" + cd_safe "${startdir}" rm -rf "${srclinks}" } @@ -1421,17 +1528,16 @@ install_package() { msg "$(gettext "Installing %s package group with %s...")" "$pkgbase" "$PACMAN -U" fi - local fullver pkg pkglist + local fullver pkgarch pkg pkglist + (( ASDEPS )) && pkglist+=('--asdeps') + for pkg in ${pkgname[@]}; do fullver=$(get_full_version $pkg) - if [[ -f $PKGDEST/${pkg}-${fullver}-${CARCH}${PKGEXT} ]]; then - pkglist+=" $PKGDEST/${pkg}-${fullver}-${CARCH}${PKGEXT}" - else - pkglist+=" $PKGDEST/${pkg}-${fullver}-any${PKGEXT}" - fi + pkgarch=$(get_pkg_arch $pkg) + pkglist+=("$PKGDEST/${pkg}-${fullver}-${pkgarch}${PKGEXT}") done - if ! run_pacman -U $pkglist; then + if ! run_pacman -U ${pkglist[@]}; then warning "$(gettext "Failed to install built package(s).")" return 0 fi @@ -1477,8 +1583,8 @@ check_sanity() { awk -F'=' '$1 ~ /^[[:space:]]*pkgrel$/' "$BUILDFILE" | sed "s/[[:space:]]*#.*//" | while IFS='=' read -r _ i; do eval i=\"$(sed 's/^\(['\''"]\)\(.*\)\1$/\2/' <<< "${i%%+([[:space:]])}")\" - if [[ $i = *[[:space:]-]* ]]; then - error "$(gettext "%s is not allowed to contain hyphens or whitespace.")" "pkgrel" + if [[ $i != +([0-9])?(.+([0-9])) ]]; then + error "$(gettext "%s must be a decimal.")" "pkgrel" return 1 fi done || ret=1 @@ -1570,7 +1676,7 @@ check_sanity() { known=0 # check if option matches a known option or its inverse for kopt in ${packaging_options[@]} ${other_options[@]}; do - if [[ ${i} = ${kopt} || ${i} = "!${kopt}" ]]; then + if [[ ${i} = "${kopt}" || ${i} = "!${kopt}" ]]; then known=1 fi done @@ -1609,12 +1715,12 @@ check_software() { # check for sudo if we will need it during makepkg execution if (( ! ( ASROOT || INFAKEROOT ) && ( DEP_BIN || RMDEPS || INSTALL ) )); then if ! type -p sudo >/dev/null; then - warning "$(gettext "Sudo can not be found. Will use su to acquire root privileges.")" + warning "$(gettext "Cannot find the %s binary. Will use %s to acquire root privileges.")" "sudo" "su" fi fi # fakeroot - building as non-root user - if [[ $(check_buildenv fakeroot) = "y" ]] && (( EUID > 0 )); then + if check_buildenv "fakeroot" "y" && (( EUID > 0 )); then if ! type -p fakeroot >/dev/null; then error "$(gettext "Cannot find the %s binary required for building as non-root user.")" "fakeroot" ret=1 @@ -1622,7 +1728,7 @@ check_software() { fi # gpg - package signing - if [[ $SIGNPKG == 'y' || (-z "$SIGNPKG" && $(check_buildenv sign) == 'y') ]]; then + if [[ $SIGNPKG == 'y' ]] || { [[ -z $SIGNPKG ]] && check_buildenv "sign" "y"; }; then if ! type -p gpg >/dev/null; then error "$(gettext "Cannot find the %s binary required for signing packages.")" "gpg" ret=1 @@ -1646,7 +1752,7 @@ check_software() { fi # upx - binary compression - if [[ $(check_option upx) == 'y' ]]; then + if check_option "upx" "y"; then if ! type -p upx >/dev/null; then error "$(gettext "Cannot find the %s binary required for compressing binaries.")" "upx" ret=1 @@ -1654,7 +1760,7 @@ check_software() { fi # distcc - compilation with distcc - if [[ $(check_buildenv distcc) = "y" && $(check_option distcc) != "n" ]]; then + if check_buildenv "distcc" "y" && ! check_option "distcc" "n" ]]; then if ! type -p distcc >/dev/null; then error "$(gettext "Cannot find the %s binary required for distributed compilation.")" "distcc" ret=1 @@ -1662,7 +1768,7 @@ check_software() { fi # ccache - compilation with ccache - if [[ $(check_buildenv ccache) = "y" && $(check_option ccache) != "n" ]]; then + if check_buildenv "ccache" "y" && ! check_option "ccache" "n"; then if ! type -p ccache >/dev/null; then error "$(gettext "Cannot find the %s binary required for compiler cache usage.")" "ccache" ret=1 @@ -1670,7 +1776,7 @@ check_software() { fi # strip - strip symbols from binaries/libraries - if [[ $(check_option strip) = "y" ]]; then + if check_option "strip" "y"; then if ! type -p strip >/dev/null; then error "$(gettext "Cannot find the %s binary required for object file stripping.")" "strip" ret=1 @@ -1678,7 +1784,7 @@ check_software() { fi # gzip - compressig man and info pages - if [[ $(check_option zipman) = "y" ]]; then + if check_option "zipman" "y"; then if ! type -p gzip >/dev/null; then error "$(gettext "Cannot find the %s binary required for compressing man and info pages.")" "gzip" ret=1 @@ -1694,7 +1800,7 @@ devel_check() { # Do not update pkgver if --holdver is set, when building a source package, repackaging, # reading PKGBUILD from pipe (-f), or if we cannot write to the file (-w) if (( HOLDVER || SOURCEONLY || REPKG )) || - [[ ! -f $BUILDFILE || ! -w $BUILDFILE || $BUILDFILE = /dev/stdin ]]; then + [[ ! -f $BUILDFILE || ! -w $BUILDFILE || $BUILDFILE = "/dev/stdin" ]]; then return fi @@ -1704,66 +1810,67 @@ devel_check() { # calls to makepkg via fakeroot will explicitly pass the version # number to avoid having to determine the version number twice. # Also do a check to make sure we have the VCS tool available. - oldpkgver=$pkgver - if [[ -n ${_darcstrunk} && -n ${_darcsmod} ]] ; then - if ! type -p darcs >/dev/null; then - warning "$(gettext "Cannot find the %s binary required to determine latest %s revision.")" "darcs" "darcs" - return 0 - fi - msg "$(gettext "Determining latest %s revision...")" 'darcs' - newpkgver=$(date +%Y%m%d) - elif [[ -n ${_cvsroot} && -n ${_cvsmod} ]] ; then - if ! type -p cvs >/dev/null; then - warning "$(gettext "Cannot find the %s binary required to determine latest %s revision.")" "cvs" "cvs" - return 0 - fi - msg "$(gettext "Determining latest %s revision...")" 'cvs' - newpkgver=$(date +%Y%m%d) - elif [[ -n ${_gitroot} && -n ${_gitname} ]] ; then - if ! type -p git >/dev/null; then - warning "$(gettext "Cannot find the %s binary required to determine latest %s revision.")" "git" "git" - return 0 - fi - msg "$(gettext "Determining latest %s revision...")" 'git' - newpkgver=$(date +%Y%m%d) - elif [[ -n ${_svntrunk} && -n ${_svnmod} ]] ; then - if ! type -p svn >/dev/null; then - warning "$(gettext "Cannot find the %s binary required to determine latest %s revision.")" "svn" "svn" - return 0 - fi - msg "$(gettext "Determining latest %s revision...")" 'svn' - newpkgver=$(LC_ALL=C svn info $_svntrunk | sed -n 's/^Last Changed Rev: \([0-9]*\)$/\1/p') - elif [[ -n ${_bzrtrunk} && -n ${_bzrmod} ]] ; then - if ! type -p bzr >/dev/null; then - warning "$(gettext "Cannot find the %s binary required to determine latest %s revision.")" "bzr" "bzr" - return 0 - fi - msg "$(gettext "Determining latest %s revision...")" 'bzr' - newpkgver=$(bzr revno ${_bzrtrunk}) - elif [[ -n ${_hgroot} && -n ${_hgrepo} ]] ; then - if ! type -p hg >/dev/null; then - warning "$(gettext "Cannot find the %s binary required to determine latest %s revision.")" "hg" "hg" - return 0 - fi - msg "$(gettext "Determining latest %s revision...")" 'hg' - if [[ -d ./src/$_hgrepo ]] ; then - cd ./src/$_hgrepo - local ret=0 - hg pull || ret=$? - if (( ! ret )); then - hg update - elif (( ret != 1 )); then - return 1 - fi - else - [[ ! -d ./src/ ]] && mkdir ./src/ - hg clone $_hgroot/$_hgrepo ./src/$_hgrepo - cd ./src/$_hgrepo - fi - newpkgver=$(hg tip --template "{rev}") - cd ../../ + local vcs=() + + [[ -n ${_darcstrunk} && -n ${_darcsmod} ]] && vcs+=("darcs") + [[ -n ${_cvsroot} && -n ${_cvsmod} ]] && vcs+=("cvs") + [[ -n ${_gitroot} && -n ${_gitname} ]] && vcs+=("git") + [[ -n ${_svntrunk} && -n ${_svnmod} ]] && vcs+=("svn") + [[ -n ${_bzrtrunk} && -n ${_bzrmod} ]] && vcs+=("bzr") + [[ -n ${_hgroot} && -n ${_hgrepo} ]] && vcs+=("hg") + + if (( ${#vcs[@]} == 0 )); then + return + elif (( ${#vcs[@]} > 1 )); then + warning "$(gettext "Ambiguous VCS package. Cannot pick from: %s.")" "${vcs[*]}" + return 0 + fi + + if ! type -p "$vcs" >/dev/null; then + warning "$(gettext "Cannot find the %s binary required to determine latest %s revision.")" "$vcs" "$vcs" + return 0 fi + msg "$(gettext "Determining latest %s revision...")" "$vcs" + + case "$vcs" in + darcs) + newpkgver=$(date +%Y%m%d) + ;; + cvs) + newpkgver=$(date +%Y%m%d) + ;; + git) + newpkgver=$(date +%Y%m%d) + ;; + svn) + newpkgver=$(LC_ALL=C svn info $_svntrunk | sed -n 's/^Last Changed Rev: \([0-9]*\)$/\1/p') + ;; + bzr) + newpkgver=$(bzr revno ${_bzrtrunk}) + ;; + hg) + if pushd "./src/$_hgrepo" > /dev/null; then + local ret=0 + hg pull || ret=$? + if (( ! ret )); then + hg update + elif (( ret != 1 )); then + return 1 + fi + else + [[ ! -d ./src/ ]] && mkdir ./src/ + hg clone "$_hgroot/$_hgrepo" "./src/$_hgrepo" + if ! pushd "./src/$_hgrepo" > /dev/null; then + warning "$(gettext "An error occured while determining the hg version number.")" + return 0 + fi + fi + newpkgver=$(hg tip --template "{rev}") + popd > /dev/null + ;; + esac + if [[ -n $newpkgver ]]; then msg2 "$(gettext "Version found: %s")" "$newpkgver" fi @@ -1784,13 +1891,13 @@ devel_update() { # ... # _foo=pkgver # - if [[ -n $newpkgver ]]; then - if [[ $newpkgver != "$pkgver" ]]; then - if [[ -f $BUILDFILE && -w $BUILDFILE ]]; then - @SEDINPLACE@ "s/^pkgver=[^ ]*/pkgver=$newpkgver/" "$BUILDFILE" - @SEDINPLACE@ "s/^pkgrel=[^ ]*/pkgrel=1/" "$BUILDFILE" - source "$BUILDFILE" - fi + if [[ -n $newpkgver && $newpkgver != "$pkgver" ]]; then + if [[ -f $BUILDFILE && -w $BUILDFILE ]]; then + @SEDINPLACE@ "s/^pkgver=[^ ]*/pkgver=$newpkgver/" "$BUILDFILE" + @SEDINPLACE@ "s/^pkgrel=[^ ]*/pkgrel=1/" "$BUILDFILE" + source "$BUILDFILE" + else + warning "$(gettext "%s is not writeable -- pkgver will not be updated")" "$BUILDFILE" fi fi } @@ -1837,15 +1944,15 @@ canonicalize_path() { if [[ -d $path ]]; then ( - cd "$path" + cd_safe "$path" pwd -P ) else - echo "$path" + printf "%s\n" "$path" fi } -m4_include(library/parse_options.sh) +m4_include(library/parseopts.sh) usage() { printf "makepkg (pacman) %s\n" "$myver" @@ -1886,6 +1993,7 @@ usage() { printf -- "$(gettext "These options can be passed to %s:")\n" "pacman" echo printf -- "$(gettext " --noconfirm Do not ask for confirmation when resolving dependencies")\n" + printf -- "$(gettext " --asdeps Install packages as non-explicitly installed")\n" printf -- "$(gettext " --noprogressbar Do not show a progress bar when downloading files")\n" echo printf -- "$(gettext "If %s is not specified, %s will look for '%s'")\n" "-p" "makepkg" "$BUILDSCRIPT" @@ -1906,7 +2014,7 @@ There is NO WARRANTY, to the extent permitted by law.\n")" # determine whether we have gettext; make it a no-op if we do not if ! type -p gettext >/dev/null; then gettext() { - echo "$@" + printf "%s\n" "$@" } fi @@ -1914,23 +2022,25 @@ ARGLIST=("$@") # Parse Command Line Options. OPT_SHORT="AcdefFghiLmop:rRsSV" -OPT_LONG="allsource,asroot,ignorearch,check,clean,nodeps" -OPT_LONG+=",noextract,force,forcever:,geninteg,help,holdver,skippgpcheck" -OPT_LONG+=",install,key:,log,nocolor,nobuild,nocheck,nosign,pkg:,rmdeps" -OPT_LONG+=",repackage,skipchecksums,skipinteg,skippgpcheck,sign,source,syncdeps" -OPT_LONG+=",version,config:" +OPT_LONG=('allsource' 'asroot' 'ignorearch' 'check' 'clean' 'nodeps' + 'noextract' 'force' 'forcever:' 'geninteg' 'help' 'holdver' 'skippgpcheck' + 'install' 'key:' 'log' 'nocolor' 'nobuild' 'nocheck' 'nosign' 'pkg:' 'rmdeps' + 'repackage' 'skipchecksums' 'skipinteg' 'skippgpcheck' 'sign' 'source' 'syncdeps' + 'version' 'config:') # Pacman Options -OPT_LONG+=",noconfirm,noprogressbar" -if ! OPT_TEMP="$(parse_options $OPT_SHORT $OPT_LONG "$@")"; then - echo; usage; exit 1 # E_INVALID_OPTION; +OPT_LONG+=('asdeps' 'noconfirm' 'noprogressbar') + +if ! parseopts "$OPT_SHORT" "${OPT_LONG[@]}" -- "$@"; then + exit 1 # E_INVALID_OPTION; fi -eval set -- "$OPT_TEMP" -unset OPT_SHORT OPT_LONG OPT_TEMP +set -- "${OPTRET[@]}" +unset OPT_SHORT OPT_LONG OPTRET while true; do case "$1" in # Pacman Options + --asdeps) ASDEPS=1;; --noconfirm) PACMAN_OPTS+=" --noconfirm" ;; --noprogressbar) PACMAN_OPTS+=" --noprogressbar" ;; @@ -1957,7 +2067,7 @@ while true; do --nosign) SIGNPKG='n' ;; -o|--nobuild) NOBUILD=1 ;; -p) shift; BUILDFILE=$1 ;; - --pkg) shift; PKGLIST=($1) ;; + --pkg) shift; IFS=, read -ra p <<<"$1"; PKGLIST+=("${p[@]}"); unset p ;; -r|--rmdeps) RMDEPS=1 ;; -R|--repackage) REPKG=1 ;; --skipchecksums) SKIPCHECKSUMS=1 ;; @@ -1970,8 +2080,7 @@ while true; do -h|--help) usage; exit 0 ;; # E_OK -V|--version) version; exit 0 ;; # E_OK - --) OPT_IND=0; shift; break;; - *) usage; exit 1 ;; # E_INVALID_OPTION + --) OPT_IND=0; shift; break 2;; esac shift done @@ -1983,7 +2092,6 @@ for signal in TERM HUP QUIT; do done trap 'trap_exit INT "$(gettext "Aborted by user! Exiting...")"' INT trap 'trap_exit USR1 "$(gettext "An unknown error has occurred. Exiting...")"' ERR -set -E # preserve environment variables and canonicalize path [[ -n ${PKGDEST} ]] && _PKGDEST=$(canonicalize_path ${PKGDEST}) @@ -1993,13 +2101,14 @@ set -E [[ -n ${PKGEXT} ]] && _PKGEXT=${PKGEXT} [[ -n ${SRCEXT} ]] && _SRCEXT=${SRCEXT} [[ -n ${GPGKEY} ]] && _GPGKEY=${GPGKEY} +[[ -n ${PACKAGER} ]] && _PACKAGER=${PACKAGER} # default config is makepkg.conf MAKEPKG_CONF=${MAKEPKG_CONF:-$confdir/makepkg.conf} # Source the config file; fail if it is not found if [[ -r $MAKEPKG_CONF ]]; then - source "$MAKEPKG_CONF" + source_safe "$MAKEPKG_CONF" else error "$(gettext "%s not found.")" "$MAKEPKG_CONF" plain "$(gettext "Aborting...")" @@ -2009,7 +2118,7 @@ fi # Source user-specific makepkg.conf overrides, but only if no override config # file was specified if [[ $MAKEPKG_CONF = "$confdir/makepkg.conf" && -r ~/.makepkg.conf ]]; then - source ~/.makepkg.conf + source_safe ~/.makepkg.conf fi # set pacman command if not already defined @@ -2017,7 +2126,7 @@ PACMAN=${PACMAN:-pacman} # check if messages are to be printed using color unset ALL_OFF BOLD BLUE GREEN RED YELLOW -if [[ -t 2 && ! $USE_COLOR = "n" && $(check_buildenv color) = "y" ]]; then +if [[ -t 2 && ! $USE_COLOR = "n" ]] && check_buildenv "color" "y"; then # prefer terminal safe colored and bold text when tput is supported if tput setaf 0 &>/dev/null; then ALL_OFF="$(tput sgr0)" @@ -2041,8 +2150,11 @@ readonly ALL_OFF BOLD BLUE GREEN RED YELLOW BUILDDIR=${_BUILDDIR:-$BUILDDIR} BUILDDIR=${BUILDDIR:-$startdir} #default to $startdir if undefined if [[ ! -d $BUILDDIR ]]; then - mkdir -p "$BUILDDIR" || - error "$(gettext "You do not have write permission to create packages in %s.")" "$BUILDDIR" + if ! mkdir -p "$BUILDDIR"; then + error "$(gettext "You do not have write permission to create packages in %s.")" "$BUILDDIR" + plain "$(gettext "Aborting...")" + exit 1 + fi chmod a-s "$BUILDDIR" fi if [[ ! -w $BUILDDIR ]]; then @@ -2050,8 +2162,6 @@ if [[ ! -w $BUILDDIR ]]; then plain "$(gettext "Aborting...")" exit 1 fi -srcdir="$BUILDDIR/src" -pkgdir="$BUILDDIR/pkg" PKGDEST=${_PKGDEST:-$PKGDEST} PKGDEST=${PKGDEST:-$startdir} #default to $startdir if undefined @@ -2080,6 +2190,7 @@ fi PKGEXT=${_PKGEXT:-$PKGEXT} SRCEXT=${_SRCEXT:-$SRCEXT} GPGKEY=${_GPGKEY:-$GPGKEY} +PACKAGER=${_PACKAGER:-$PACKAGER} if (( HOLDVER )) && [[ -n $FORCE_VER ]]; then # The '\\0' is here to prevent gettext from thinking --holdver is an option @@ -2099,7 +2210,7 @@ use the %s option.")" "makepkg" "--asroot" error "$(gettext "The %s option is meant for the root user only. Please\n\ rerun %s without the %s flag.")" "--asroot" "makepkg" "--asroot" exit 1 # $E_USER_ABORT - elif (( EUID > 0 )) && [[ $(check_buildenv fakeroot) != "y" ]]; then + elif (( EUID > 0 )) && ! check_buildenv "fakeroot" "y"; then warning "$(gettext "Running %s as an unprivileged user will result in non-root\n\ ownership of the packaged files. Try using the %s environment by\n\ placing %s in the %s array in %s.")" "makepkg" "fakeroot" "'fakeroot'" "BUILDENV" "$MAKEPKG_CONF" @@ -2124,9 +2235,7 @@ if [[ ! -f $BUILDFILE ]]; then else # PKGBUILD passed through a pipe BUILDFILE=/dev/stdin - shopt -u extglob - source "$BUILDFILE" - shopt -s extglob + source_safe "$BUILDFILE" fi else crlftest=$(file "$BUILDFILE" | grep -F 'CRLF' || true) @@ -2138,19 +2247,25 @@ else if [[ ${BUILDFILE:0:1} != "/" ]]; then BUILDFILE="$startdir/$BUILDFILE" fi - shopt -u extglob - source "$BUILDFILE" - shopt -s extglob + source_safe "$BUILDFILE" fi # set defaults if they weren't specified in buildfile pkgbase=${pkgbase:-${pkgname[0]}} epoch=${epoch:-0} +if [[ $BUILDDIR = "$startdir" ]]; then + srcdir="$BUILDDIR/src" + pkgdir="$BUILDDIR/pkg" +else + srcdir="$BUILDDIR/$pkgbase/src" + pkgdir="$BUILDDIR/$pkgbase/pkg" +fi + if (( GENINTEG )); then mkdir -p "$srcdir" chmod a-s "$srcdir" - cd "$srcdir" + cd_safe "$srcdir" download_sources generate_checksums exit 0 # $E_OK @@ -2174,12 +2289,15 @@ if (( ${#pkgname[@]} > 1 )); then fi # test for available PKGBUILD functions +if declare -f prepare >/dev/null; then + PREPAREFUNC=1 +fi if declare -f build >/dev/null; then BUILDFUNC=1 fi if declare -f check >/dev/null; then # "Hide" check() function if not going to be run - if [[ $RUN_CHECK = 'y' || (! $(check_buildenv check) = "n" && ! $RUN_CHECK = "n") ]]; then + if [[ $RUN_CHECK = 'y' ]] || { ! check_buildenv "check" "n" && [[ $RUN_CHECK != "n" ]]; }; then CHECKFUNC=1 fi fi @@ -2195,8 +2313,7 @@ if [[ -n "${PKGLIST[@]}" ]]; then fi # check if gpg signature is to be created and if signing key is valid -[[ -z $SIGNPKG ]] && SIGNPKG=$(check_buildenv sign) -if [[ $SIGNPKG == 'y' ]]; then +if { [[ -z $SIGNPKG ]] && check_buildenv "sign" "y"; } || [[ $SIGNPKG == 'y' ]]; then if ! gpg --list-key ${GPGKEY} &>/dev/null; then if [[ ! -z $GPGKEY ]]; then error "$(gettext "The key %s does not exist in your keyring.")" "${GPGKEY}" @@ -2210,8 +2327,8 @@ fi if (( ! SPLITPKG )); then fullver=$(get_full_version) - if [[ -f $PKGDEST/${pkgname}-${fullver}-${CARCH}${PKGEXT} \ - || -f $PKGDEST/${pkgname}-${fullver}-any${PKGEXT} ]] \ + pkgarch=$(get_pkg_arch) + if [[ -f $PKGDEST/${pkgname}-${fullver}-${pkgarch}${PKGEXT} ]] \ && ! (( FORCE || SOURCEONLY || NOBUILD )); then if (( INSTALL )); then warning "$(gettext "A package has already been built, installing existing package...")" @@ -2227,8 +2344,8 @@ else somepkgbuilt=0 for pkg in ${pkgname[@]}; do fullver=$(get_full_version $pkg) - if [[ -f $PKGDEST/${pkg}-${fullver}-${CARCH}${PKGEXT} \ - || -f $PKGDEST/${pkg}-${fullver}-any${PKGEXT} ]]; then + pkgarch=$(get_pkg_arch $pkg) + if [[ -f $PKGDEST/${pkg}-${fullver}-${pkgarch}${PKGEXT} ]]; then somepkgbuilt=1 else allpkgbuilt=0 @@ -2300,17 +2417,17 @@ if (( SOURCEONLY )); then # Get back to our src directory so we can begin with sources. mkdir -p "$srcdir" chmod a-s "$srcdir" - cd "$srcdir" + cd_safe "$srcdir" if ( (( ! SKIPCHECKSUMS )) || \ ( (( ! SKIPPGPCHECK )) && source_has_signatures ) ) || \ (( SOURCEONLY == 2 )); then download_sources fi check_source_integrity - cd "$startdir" + cd_safe "$startdir" # if we are root or if fakeroot is not enabled, then we don't use it - if [[ $(check_buildenv fakeroot) != "y" ]] || (( EUID == 0 )); then + if ! check_buildenv "fakeroot" "y" || (( EUID == 0 )); then create_srcpackage else enter_fakeroot @@ -2320,9 +2437,9 @@ if (( SOURCEONLY )); then exit 0 fi -if (( NODEPS || ( (NOBUILD || REPKG) && !DEP_BIN ) )); then - # no warning message needed for nobuild, repkg - if (( NODEPS || ( REPKG && PKGFUNC ) )); then +if (( NODEPS || (NOBUILD && !DEP_BIN ) )); then + # no warning message needed for nobuild + if (( NODEPS )); then warning "$(gettext "Skipping dependency checks.")" fi elif type -p "${PACMAN%% *}" >/dev/null; then @@ -2363,7 +2480,7 @@ umask 0022 # get back to our src directory so we can begin with sources mkdir -p "$srcdir" chmod a-s "$srcdir" -cd "$srcdir" +cd_safe "$srcdir" if (( NOEXTRACT )); then warning "$(gettext "Skipping source retrieval -- using existing %s tree")" "src/" @@ -2386,6 +2503,9 @@ else download_sources check_source_integrity extract_sources + if (( PREPAREFUNC )); then + run_prepare + fi fi if (( NOBUILD )); then @@ -2399,10 +2519,10 @@ else fi mkdir -p "$pkgdir" chmod a-s "$pkgdir" - cd "$startdir" + cd_safe "$startdir" # if we are root or if fakeroot is not enabled, then we don't use it - if [[ $(check_buildenv fakeroot) != "y" ]] || (( EUID == 0 )); then + if ! check_buildenv "fakeroot" "y" || (( EUID == 0 )); then if (( ! REPKG )); then devel_update (( BUILDFUNC )) && run_build @@ -2429,7 +2549,7 @@ else devel_update (( BUILDFUNC )) && run_build (( CHECKFUNC )) && run_check - cd "$startdir" + cd_safe "$startdir" fi enter_fakeroot diff --git a/scripts/pacman-db-upgrade.sh.in b/scripts/pacman-db-upgrade.sh.in index e0a049c5..894152f6 100644 --- a/scripts/pacman-db-upgrade.sh.in +++ b/scripts/pacman-db-upgrade.sh.in @@ -23,7 +23,7 @@ export TEXTDOMAIN='pacman-scripts' export TEXTDOMAINDIR='@localedir@' -myver='@PACKAGE_VERSION@' +declare -r myver='@PACKAGE_VERSION@' eval $(awk '/DBPath/ {print $1$2$3}' @sysconfdir@/pacman.conf) dbroot="${DBPath:-@localstatedir@/lib/pacman/}" diff --git a/scripts/pacman-key.sh.in b/scripts/pacman-key.sh.in index 948c8d52..e5b60214 100644 --- a/scripts/pacman-key.sh.in +++ b/scripts/pacman-key.sh.in @@ -24,7 +24,7 @@ export TEXTDOMAIN='pacman-scripts' export TEXTDOMAINDIR='@localedir@' -myver="@PACKAGE_VERSION@" +declare -r myver="@PACKAGE_VERSION@" # Options ADD=0 @@ -49,40 +49,43 @@ DEFAULT_KEYSERVER='hkp://pool.sks-keyservers.net' m4_include(library/output_format.sh) -m4_include(library/parse_options.sh) +m4_include(library/parseopts.sh) usage() { printf "pacman-key (pacman) %s\n" ${myver} echo - printf -- "$(gettext "Usage: %s [options]")\n" $(basename $0) + printf -- "$(gettext "Usage: %s [options] operation [targets]")\n" $(basename $0) echo printf -- "$(gettext "Manage pacman's list of trusted keys")\n" echo - printf -- "$(gettext "Options:")\n" - printf -- "$(gettext " -a, --add [file(s)] Add the specified keys (empty for stdin)")\n" - printf -- "$(gettext " -d, --delete <keyid(s)> Remove the specified keyids")\n" - printf -- "$(gettext " -e, --export [keyid(s)] Export the specified or all keyids")\n" - printf -- "$(gettext " -f, --finger [keyid(s)] List fingerprint for specified or all keyids")\n" - printf -- "$(gettext " -h, --help Show this help message and exit")\n" - printf -- "$(gettext " -l, --list-keys [keyid(s)] List the specified or all keys")\n" - printf -- "$(gettext " -r, --recv-keys <keyid(s)> Fetch the specified keyids")\n" + printf -- "$(gettext "Operations:")\n" + printf -- "$(gettext " -a, --add Add the specified keys (empty for stdin)")\n" + printf -- "$(gettext " -d, --delete Remove the specified keyids")\n" + printf -- "$(gettext " -e, --export Export the specified or all keyids")\n" + printf -- "$(gettext " -f, --finger List fingerprint for specified or all keyids")\n" + printf -- "$(gettext " -l, --list-keys List the specified or all keys")\n" + printf -- "$(gettext " -r, --recv-keys Fetch the specified keyids")\n" printf -- "$(gettext " -u, --updatedb Update the trustdb of pacman")\n" - printf -- "$(gettext " -v, --verify <signature> Verify the file specified by the signature")\n" - printf -- "$(gettext " -V, --version Show program version")\n" + printf -- "$(gettext " -v, --verify Verify the file(s) specified by the signature(s)")\n" + printf -- "$(gettext " --edit-key Present a menu for key management task on keyids")\n" + printf -- "$(gettext " --import Imports pubring.gpg from dir(s)")\n" + printf -- "$(gettext " --import-trustdb Imports ownertrust values from trustdb.gpg in dir(s)")\n" + printf -- "$(gettext " --init Ensure the keyring is properly initialized")\n" + printf -- "$(gettext " --list-sigs List keys and their signatures")\n" + printf -- "$(gettext " --lsign-key Locally sign the specified keyid")\n" + printf -- "$(gettext " --populate Reload the default keys from the (given) keyrings\n\ + in '%s'")\n" "@pkgdatadir@/keyrings" + printf -- "$(gettext " --refresh-keys Update specified or all keys from a keyserver")\n" + echo + printf -- "$(gettext "Options:")\n" printf -- "$(gettext " --config <file> Use an alternate config file (instead of\n\ '%s')")\n" "@sysconfdir@/pacman.conf" - printf -- "$(gettext " --edit-key <keyid(s)> Present a menu for key management task on keyids")\n" printf -- "$(gettext " --gpgdir <dir> Set an alternate directory for GnuPG (instead\n\ of '%s')")\n" "@sysconfdir@/pacman.d/gnupg" - printf -- "$(gettext " --import <dir(s)> Imports pubring.gpg from dir(s)")\n" - printf -- "$(gettext " --import-trustdb <dir(s)> Imports ownertrust values from trustdb.gpg in dir(s)")\n" - printf -- "$(gettext " --init Ensure the keyring is properly initialized")\n" - printf -- "$(gettext " --keyserver Specify a keyserver to use if necessary")\n" - printf -- "$(gettext " --list-sigs [keyid(s)] List keys and their signatures")\n" - printf -- "$(gettext " --lsign-key <keyid> Locally sign the specified keyid")\n" - printf -- "$(gettext " --populate [keyring(s)] Reload the default keys from the (given) keyrings\n\ - in '%s'")\n" "@pkgdatadir@/keyrings" - printf -- "$(gettext " --refresh-keys [keyid(s)] Update specified or all keys from a keyserver")\n" + printf -- "$(gettext " --keyserver <server-url> Specify a keyserver to use if necessary")\n" + echo + printf -- "$(gettext " -h, --help Show this help message and exit")\n" + printf -- "$(gettext " -V, --version Show program version")\n" } version() { @@ -113,6 +116,30 @@ get_from() { return 1 } +key_lookup_from_name() { + local ids + + mapfile -t ids < \ + <("${GPG_PACMAN[@]}" --search-keys --batch --with-colons "$1" 2>/dev/null | + awk -F: '$1 == "pub" { print $2 }') + + # only return success on non-ambiguous lookup + case ${#ids[*]} in + 0) + error "$(gettext "Failed to lookup key by name:") %s" "$name" + return 1 + ;; + 1) + printf '%s' "${ids[0]}" + return 0 + ;; + *) + error "$(gettext "Key name is ambiguous:") %s" "$name" + return 1 + ;; + esac +} + generate_master_key() { # Generate the master key, which will be in both pubring and secring "${GPG_PACMAN[@]}" --gen-key --batch <<EOF @@ -146,7 +173,7 @@ add_gpg_conf_option() { check_keyids_exist() { local ret=0 - for key in "${KEYIDS[@]}"; do + for key in "$@"; do # Verify if the key exists in pacman's keyring if ! "${GPG_PACMAN[@]}" --list-keys "$key" &>/dev/null ; then error "$(gettext "The key identified by %s could not be found locally.")" "$key" @@ -217,16 +244,16 @@ check_keyring() { populate_keyring() { local KEYRING_IMPORT_DIR='@pkgdatadir@/keyrings' - local keyring + local keyring KEYRINGIDS=("$@") local ret=0 - if [[ -z ${KEYRINGIDS[@]} ]]; then + if (( ${#KEYRINGIDS[*]} == 0 )); then # get list of all available keyrings shopt -s nullglob KEYRINGIDS=("$KEYRING_IMPORT_DIR"/*.gpg) shopt -u nullglob KEYRINGIDS=("${KEYRINGIDS[@]##*/}") KEYRINGIDS=("${KEYRINGIDS[@]%.gpg}") - if [[ -z ${KEYRINGIDS[@]} ]]; then + if (( ${#KEYRINGIDS[*]} == 0 )); then error "$(gettext "No keyring files exist in %s.")" "$KEYRING_IMPORT_DIR" ret=1 fi @@ -245,8 +272,7 @@ populate_keyring() { fi # Variable used for iterating on keyrings - local key - local key_id + local keys key_id # Add keys from requested keyrings for keyring in "${KEYRINGIDS[@]}"; do @@ -262,14 +288,12 @@ populate_keyring() { local -A trusted_ids for keyring in "${KEYRINGIDS[@]}"; do if [[ -s "${KEYRING_IMPORT_DIR}/${keyring}-trusted" ]]; then - while read key; do - # skip comments; these are valid in this file - [[ $key = \#* ]] && continue - key_id="${key%%:*}" - if [[ -n ${key_id} ]]; then - # Mark this key to be lsigned - trusted_ids[$key_id]="${keyring}" - fi + while IFS=: read key_id _; do + # skip blank lines, comments; these are valid in this file + [[ -z $key_id || ${key_id:0:1} = \# ]] && continue + + # Mark this key to be lsigned + trusted_ids[$key_id]=$keyring done < "${KEYRING_IMPORT_DIR}/${keyring}-trusted" fi done @@ -278,7 +302,7 @@ populate_keyring() { msg "$(gettext "Locally signing trusted keys in keyring...")" for key_id in "${!trusted_ids[@]}"; do msg2 "$(gettext "Locally signing key %s...")" "${key_id}" - "${GPG_PACMAN[@]}" --quiet --lsign-key "${key_id}" + lsign_keys "${key_id}" done msg "$(gettext "Importing owner trust values...")" for keyring in "${KEYRINGIDS[@]}"; do @@ -294,13 +318,13 @@ populate_keyring() { local -A revoked_ids for keyring in "${KEYRINGIDS[@]}"; do if [[ -s "${KEYRING_IMPORT_DIR}/${keyring}-revoked" ]]; then - while read key; do - key_id="$("${GPG_PACMAN[@]}" --quiet --with-colons --list-key "${key}" 2>/dev/null | grep ^pub | cut -d: -f5)" - if [[ -n ${key_id} ]]; then + mapfile -t keys < "${KEYRING_IMPORT_DIR}/${keyring}-revoked" + while IFS=: read _ _ _ _ key_id _; do + if [[ -n $key_id ]]; then # Mark this key to be disabled revoked_ids[$key_id]="${keyring}" fi - done < "${KEYRING_IMPORT_DIR}/${keyring}-revoked" + done < <("${GPG_PACMAN[@]}" --quiet --with-colons --list-keys "${keys[@]}" 2>/dev/null) fi done @@ -314,24 +338,24 @@ populate_keyring() { } add_keys() { - if ! "${GPG_PACMAN[@]}" --quiet --batch --import "${KEYFILES[@]}" ; then + if ! "${GPG_PACMAN[@]}" --quiet --batch --import "$@" ; then error "$(gettext "A specified keyfile could not be added to the keyring.")" exit 1 fi } delete_keys() { - check_keyids_exist - if ! "${GPG_PACMAN[@]}" --quiet --batch --delete-key --yes "${KEYIDS[@]}" ; then + check_keyids_exist "$@" + if ! "${GPG_PACMAN[@]}" --quiet --batch --delete-key --yes "$@" ; then error "$(gettext "A specified key could not be removed from the keyring.")" exit 1 fi } edit_keys() { - check_keyids_exist + check_keyids_exist "$@" local ret=0 - for key in "${KEYIDS[@]}"; do + for key in "$@"; do if ! "${GPG_PACMAN[@]}" --edit-key "$key" ; then error "$(gettext "The key identified by %s could not be edited.")" "$key" ret=1 @@ -343,8 +367,8 @@ edit_keys() { } export_keys() { - check_keyids_exist - if ! "${GPG_PACMAN[@]}" --armor --export "${KEYIDS[@]}" ; then + check_keyids_exist "$@" + if ! "${GPG_PACMAN[@]}" --armor --export "$@" ; then error "$(gettext "A specified key could not be exported from the keyring.")" exit 1 fi @@ -352,7 +376,7 @@ export_keys() { finger_keys() { check_keyids_exist - if ! "${GPG_PACMAN[@]}" --batch --fingerprint "${KEYIDS[@]}" ; then + if ! "${GPG_PACMAN[@]}" --batch --fingerprint "$@" ; then error "$(gettext "The fingerprint of a specified key could not be determined.")" exit 1 fi @@ -361,7 +385,7 @@ finger_keys() { import_trustdb() { local importdir local ret=0 - for importdir in "${IMPORT_DIRS[@]}"; do + for importdir in "$@"; do if [[ -f "${importdir}/trustdb.gpg" ]]; then gpg --homedir "${importdir}" --export-ownertrust | \ "${GPG_PACMAN[@]}" --import-ownertrust - @@ -382,7 +406,7 @@ import_trustdb() { import() { local importdir local ret=0 - for importdir in "${IMPORT_DIRS[@]}"; do + for importdir in "$@"; do if [[ -f "${importdir}/pubring.gpg" ]]; then if ! "${GPG_PACMAN[@]}" --quiet --batch --import "${importdir}/pubring.gpg" ; then error "$(gettext "%s could not be imported.")" "${importdir}/pubring.gpg" @@ -400,7 +424,7 @@ import() { list_keys() { check_keyids_exist - if ! "${GPG_PACMAN[@]}" --batch --list-keys "${KEYIDS[@]}" ; then + if ! "${GPG_PACMAN[@]}" --batch --list-keys "$@" ; then error "$(gettext "A specified key could not be listed.")" exit 1 fi @@ -408,7 +432,7 @@ list_keys() { list_sigs() { check_keyids_exist - if ! "${GPG_PACMAN[@]}" --batch --list-sigs "${KEYIDS[@]}" ; then + if ! "${GPG_PACMAN[@]}" --batch --list-sigs "$@" ; then error "$(gettext "A specified signature could not be listed.")" exit 1 fi @@ -416,7 +440,8 @@ list_sigs() { lsign_keys() { check_keyids_exist - printf 'y\ny\n' | LANG=C "${GPG_PACMAN[@]}" --command-fd 0 --quiet --batch --lsign-key "${KEYIDS[@]}" 2>/dev/null + # we cannot use --yes here as gpg would still ask for confirmation if a key has more than one uid + printf 'y\ny\n' | LANG=C "${GPG_PACMAN[@]}" --command-fd 0 --quiet --batch --lsign-key "$@" 2>/dev/null if (( PIPESTATUS[1] )); then error "$(gettext "A specified key could not be locally signed.")" exit 1 @@ -424,25 +449,45 @@ lsign_keys() { } receive_keys() { - if ! "${GPG_PACMAN[@]}" --recv-keys "${KEYIDS[@]}" ; then + local name id keyids + + # if the key is not a hex ID, do a lookup + for name; do + if [[ $name = ?(0x)+([0-9a-fA-F]) ]]; then + keyids+=("$name") + else + if id=$(key_lookup_from_name "$name"); then + keyids+=("$id") + fi + fi + done + + (( ${#keyids[*]} > 0 )) || exit 1 + + if ! "${GPG_PACMAN[@]}" --recv-keys "${keyids[@]}" ; then error "$(gettext "Remote key not fetched correctly from keyserver.")" exit 1 fi } refresh_keys() { - check_keyids_exist - if ! "${GPG_PACMAN[@]}" --refresh-keys "${KEYIDS[@]}" ; then + check_keyids_exist "$@" + if ! "${GPG_PACMAN[@]}" --refresh-keys "$@" ; then error "$(gettext "A specified local key could not be updated from a keyserver.")" exit 1 fi } verify_sig() { - if ! "${GPG_PACMAN[@]}" --status-fd 1 --verify $SIGNATURE | grep -qE 'TRUST_(FULLY|ULTIMATE)'; then - error "$(gettext "The signature identified by %s could not be verified.")" "$SIGNATURE" - exit 1 - fi + local ret=0 + for sig; do + msg "Checking %s ..." "$sig" + if ! "${GPG_PACMAN[@]}" --status-fd 1 --verify "$sig" | grep -qE 'TRUST_(FULLY|ULTIMATE)'; then + error "$(gettext "The signature identified by %s could not be verified.")" "$sig" + ret=1 + fi + done + exit $ret } updatedb() { @@ -460,56 +505,55 @@ if ! type gettext &>/dev/null; then } fi -OPT_SHORT="a::d:e::f::hl::r:uv:V" -OPT_LONG="add::,config:,delete:,edit-key:,export::,finger::,gpgdir:" -OPT_LONG+=",help,import:,import-trustdb:,init,keyserver:,list-keys::,list-sigs::" -OPT_LONG+=",lsign-key:,populate::,recv-keys:,refresh-keys::,updatedb" -OPT_LONG+=",verify:,version" -if ! OPT_TEMP="$(parse_options $OPT_SHORT $OPT_LONG "$@")"; then - echo; usage; exit 1 # E_INVALID_OPTION; +OPT_SHORT="adefhlruvV" +OPT_LONG=('add' 'config:' 'delete' 'edit-key' 'export' 'finger' 'gpgdir:' + 'help' 'import' 'import-trustdb' 'init' 'keyserver:' 'list-keys' 'list-sigs' + 'lsign-key' 'populate' 'recv-keys' 'refresh-keys' 'updatedb' + 'verify' 'version') +if ! parseopts "$OPT_SHORT" "${OPT_LONG[@]}" -- "$@"; then + exit 1 # E_INVALID_OPTION; fi -eval set -- "$OPT_TEMP" -unset OPT_SHORT OPT_LONG OPT_TEMP +set -- "${OPTRET[@]}" +unset OPT_SHORT OPT_LONG OPTRET if [[ $1 == "--" ]]; then usage; exit 0; fi -while true; do - case "$1" in - -a|--add) ADD=1; [[ -n $2 && ${2:0:1} != "-" ]] && shift && KEYFILES=($1); UPDATEDB=1 ;; +while (( $# )); do + case $1 in + -a|--add) ADD=1 UPDATEDB=1 ;; --config) shift; CONFIG=$1 ;; - -d|--delete) DELETE=1; shift; KEYIDS=($1); UPDATEDB=1 ;; - --edit-key) EDITKEY=1; shift; KEYIDS=($1); UPDATEDB=1 ;; - -e|--export) EXPORT=1; [[ -n $2 && ${2:0:1} != "-" ]] && shift && KEYIDS=($1) ;; - -f|--finger) FINGER=1; [[ -n $2 && ${2:0:1} != "-" ]] && shift && KEYIDS=($1) ;; + -d|--delete) DELETE=1 UPDATEDB=1 ;; + --edit-key) EDITKEY=1 UPDATEDB=1 ;; + -e|--export) EXPORT=1 ;; + -f|--finger) FINGER=1 ;; --gpgdir) shift; PACMAN_KEYRING_DIR=$1 ;; - --import) IMPORT=1; shift; IMPORT_DIRS=($1); UPDATEDB=1 ;; - --import-trustdb) IMPORT_TRUSTDB=1; shift; IMPORT_DIRS=($1); UPDATEDB=1 ;; + --import) IMPORT=1 UPDATEDB=1 ;; + --import-trustdb) IMPORT_TRUSTDB=1 UPDATEDB=1 ;; --init) INIT=1 ;; --keyserver) shift; KEYSERVER=$1 ;; - -l|--list-keys) LISTKEYS=1; [[ -n $2 && ${2:0:1} != "-" ]] && shift && KEYIDS=($1) ;; - --list-sigs) LISTSIGS=1; [[ -n $2 && ${2:0:1} != "-" ]] && shift && KEYIDS=($1) ;; - --lsign-key) LSIGNKEY=1; shift; KEYIDS=($1); UPDATEDB=1 ;; - --populate) POPULATE=1; [[ -n $2 && ${2:0:1} != "-" ]] && shift && KEYRINGIDS=($1); UPDATEDB=1 ;; - -r|--recv-keys) RECEIVE=1; shift; KEYIDS=($1); UPDATEDB=1 ;; - --refresh-keys) REFRESH=1; [[ -n $2 && ${2:0:1} != "-" ]] && shift && KEYIDS=($1) ;; + -l|--list-keys) LISTKEYS=1 ;; + --list-sigs) LISTSIGS=1 ;; + --lsign-key) LSIGNKEY=1 UPDATEDB=1 ;; + --populate) POPULATE=1 UPDATEDB=1 ;; + -r|--recv-keys) RECEIVE=1 UPDATEDB=1 ;; + --refresh-keys) REFRESH=1 ;; -u|--updatedb) UPDATEDB=1 ;; - -v|--verify) VERIFY=1; shift; SIGNATURE=$1 ;; + -v|--verify) VERIFY=1 ;; -h|--help) usage; exit 0 ;; -V|--version) version; exit 0 ;; - --) OPT_IND=0; shift; break;; - *) usage; exit 1 ;; + --) shift; break 2 ;; esac shift done if ! type -p gpg >/dev/null; then - error "$(gettext "Cannot find the %s binary required for all %s operations.")" "gpg" "pacman-key" + error "$(gettext "Cannot find the %s binary required for all %s operations.")" "gpg" "pacman-key" exit 1 fi @@ -552,23 +596,30 @@ case $numopt in ;; esac +# check for targets where needed +if (( (ADD || DELETE || EDIT || IMPORT || IMPORT_TRUSTDB || + LSIGNKEY || RECEIVE || VERIFY) && $# == 0 )); then + error "$(gettext "No targets specified")" + exit 1 +fi + (( ! INIT )) && check_keyring -(( ADD )) && add_keys -(( DELETE )) && delete_keys -(( EDITKEY )) && edit_keys -(( EXPORT )) && export_keys -(( FINGER )) && finger_keys -(( IMPORT )) && import -(( IMPORT_TRUSTDB)) && import_trustdb +(( ADD )) && add_keys "$@" +(( DELETE )) && delete_keys "$@" +(( EDITKEY )) && edit_keys "$@" +(( EXPORT )) && export_keys "$@" +(( FINGER )) && finger_keys "$@" +(( IMPORT )) && import "$@" +(( IMPORT_TRUSTDB)) && import_trustdb "$@" (( INIT )) && initialize -(( LISTKEYS )) && list_keys -(( LISTSIGS )) && list_sigs -(( LSIGNKEY )) && lsign_keys -(( POPULATE )) && populate_keyring -(( RECEIVE )) && receive_keys -(( REFRESH )) && refresh_keys -(( VERIFY )) && verify_sig +(( LISTKEYS )) && list_keys "$@" +(( LISTSIGS )) && list_sigs "$@" +(( LSIGNKEY )) && lsign_keys "$@" +(( POPULATE )) && populate_keyring "$@" +(( RECEIVE )) && receive_keys "$@" +(( REFRESH )) && refresh_keys "$@" +(( VERIFY )) && verify_sig "$@" (( UPDATEDB )) && updatedb diff --git a/scripts/pacman-optimize.sh.in b/scripts/pacman-optimize.sh.in index 8a4e7224..4a84c0bb 100644 --- a/scripts/pacman-optimize.sh.in +++ b/scripts/pacman-optimize.sh.in @@ -24,7 +24,7 @@ export TEXTDOMAIN='pacman-scripts' export TEXTDOMAINDIR='@localedir@' -myver='@PACKAGE_VERSION@' +declare -r myver='@PACKAGE_VERSION@' eval $(awk '/DBPath/ {print $1$2$3}' @sysconfdir@/pacman.conf) dbroot="${DBPath:-@localstatedir@/lib/pacman/}" @@ -88,9 +88,8 @@ if [[ -n $1 ]]; then dbroot="$1" fi -# make sure diff is installed -if ! type diff >/dev/null 2>&1; then - die "$(gettext "diff tool was not found, please install diffutils.")" +if ! type -p openssl >/dev/null; then + die "$(gettext "Cannot find the %s binary required for verifying integrity.")" "openssl" fi if [[ ! -d $dbroot || ! -d $dbroot/local ]]; then @@ -103,8 +102,8 @@ fi # strip any trailing slash from our dbroot dbroot="${dbroot%/}" -# form the path to our lockfile location lockfile="${dbroot}/db.lck" +localdb="${dbroot}/local" # make sure pacman isn't running if [[ -f $lockfile ]]; then @@ -113,42 +112,44 @@ fi # do not let pacman run while we do this touch "$lockfile" -workdir=$(mktemp -d /tmp/pacman-optimize.XXXXXXXXXX) || +workdir=$(mktemp -d "${TMPDIR:-/tmp}/pacman-optimize.XXXXXXXXXX") || die_r "$(gettext "Can not create temp directory for database building.")\n" >&2 # step 1: sum the old db msg "$(gettext "MD5sum'ing the old database...")" -find "$dbroot" -type f | sort | xargs md5sum > "$workdir/pacsums.old" +(cd "$localdb" && find . -type f -print0 | \ + xargs -0 openssl dgst -md5 | sort > "$workdir/pacsums.old") # step 2: tar it up -msg "$(gettext "Tar'ing up %s...")" "$dbroot" -bsdtar -czf "$workdir/pacman-db.tar.gz" -C "$dbroot" ./ +msg "$(gettext "Tar'ing up %s...")" "$localdb" +bsdtar -czf "$workdir/pacman-db.tar.gz" -C "$localdb" ./ if (( $? )); then rm -rf "$workdir" - die_r "$(gettext "Tar'ing up %s failed.")" "$dbroot" + die_r "$(gettext "Tar'ing up %s failed.")" "$localdb" fi # step 3: make and sum the new db side-by-side with the old msg "$(gettext "Making and MD5sum'ing the new database...")" -mkdir "$dbroot.new" -bsdtar -xpf "$workdir/pacman-db.tar.gz" -C "$dbroot.new" +mkdir "$localdb.new" +bsdtar -xpf "$workdir/pacman-db.tar.gz" -C "$localdb.new" if (( $? )); then rm -rf "$workdir" - die_r "$(gettext "Untar'ing %s failed.")" "$dbroot" + die_r "$(gettext "Untar'ing %s failed.")" "$localdb" fi # immediate sync following extraction should get it written continuously on HDD msg "$(gettext "Syncing database to disk...")" sync -find "$dbroot.new" -type f | sort | \ - xargs md5sum | sed 's#.new##' > "$workdir/pacsums.new" +(cd "$localdb.new" && find . -type f -print0 | \ + xargs -0 openssl dgst -md5 | sort > "$workdir/pacsums.new") # step 4: compare the sums msg "$(gettext "Checking integrity...")" -diff "$workdir/pacsums.old" "$workdir/pacsums.new" >/dev/null 2>&1 -if (( $? )); then +read -ra old_dgst < <(openssl dgst -md5 < "$workdir/pacsums.old") +read -ra new_dgst < <(openssl dgst -md5 < "$workdir/pacsums.new") +if [[ ${old_dgst[@]:(-1)} != ${new_dgst[@]:(-1)} ]]; then # failed # leave our pacman-optimize tmpdir for checking to see what doesn't match up - rm -rf "$dbroot.new" + rm -rf "$localdb.new" die_r "$(gettext "Integrity check FAILED, reverting to old database.")" fi @@ -156,15 +157,15 @@ fi msg "$(gettext "Rotating database into place...")" fail=0 -mv "$dbroot" "$dbroot.old" || fail=1 -mv "$dbroot.new" "$dbroot" || fail=1 -chmod --reference="$dbroot.old" "$dbroot" || fail=1 -chown --reference="$dbroot.old" "$dbroot" || fail=1 +mv "$localdb" "$localdb.old" || fail=1 +mv "$localdb.new" "$localdb" || fail=1 +chmod --reference="$localdb.old" "$localdb" || fail=1 +chown --reference="$localdb.old" "$localdb" || fail=1 if (( fail )); then # failure with our directory shuffle - die_r "$(gettext "New database substitution failed. Check for $dbroot,\n$dbroot.old, and $dbroot.new directories.")" + die_r "$(gettext "New database substitution failed. Check for %s, %s, and %s directories.")" "$localdb" "$localdb.old" "$localdb.new" fi -rm -rf "$dbroot.old" +rm -rf "$localdb.old" # remove the lock file and our working directory with sums and tarfile rm -f "$lockfile" diff --git a/scripts/pkgdelta.sh.in b/scripts/pkgdelta.sh.in index ae0bfc38..3d80261f 100644 --- a/scripts/pkgdelta.sh.in +++ b/scripts/pkgdelta.sh.in @@ -26,7 +26,7 @@ set -o errexit export TEXTDOMAIN='pacman-scripts' export TEXTDOMAINDIR='@localedir@' -myver='@PACKAGE_VERSION@' +declare -r myver='@PACKAGE_VERSION@' QUIET=0 @@ -40,6 +40,7 @@ max_delta_size=70 # ensure we have a sane umask set umask 0022 +m4_include(library/parseopts.sh) m4_include(library/output_format.sh) # print usage instructions @@ -53,8 +54,8 @@ This delta file can then be added to a database using repo-add.\n\n")" echo printf -- "$(gettext "Options:\n")" printf -- "$(gettext " -q, --quiet minimize output\n")" - printf -- "$(gettext " --min-pkg-size minimum package size before deltas are generated (bytes)\n")" - printf -- "$(gettext " --max-delta-size percent of package size above which deltas will be discarded\n")" + printf -- "$(gettext " --min-pkg-size minimum package size before deltas are generated\n")" + printf -- "$(gettext " --max-delta-size percent of new package above which the delta will be discarded\n")" } version() { @@ -65,6 +66,8 @@ This is free software; see the source for copying conditions.\n\ There is NO WARRANTY, to the extent permitted by law.\n")" } +m4_include(library/human_to_size.sh) + isnumeric() { [[ $1 != *[!0-9]* ]] } @@ -155,44 +158,52 @@ create_xdelta() return 0 } -declare -a args +OPT_SHORT='hqV' +OPT_LONG=('help' 'quiet' 'max-delta-size:' 'min-pkg-size:' 'version') +if ! parseopts "$OPT_SHORT" "${OPT_LONG[@]}" -- "$@"; then + exit 1 +fi +set -- "${OPTRET[@]}" +unset OPT_SHORT OPT_LONG OPTRET # parse arguments -while (( $# )); do - case "$1" in - -h|--help) usage; exit 0 ;; - -V|--version) version; exit 0 ;; - -q|--quiet) QUIET=1;; +while :; do + case $1 in + -h|--help) + usage + exit 0 ;; + -V|--version) + version + exit 0 ;; + -q|--quiet) + QUIET=1;; --min-pkg-size) - if ! isnumeric "$2"; then + if ! min_pkg_size=$(human_to_size "$2"); then echo "invalid argument '$2' for option -- '$1'" exit 1 fi - min_pkg_size=$2 - shift - ;; + shift ;; --max-delta-size) - arg=$(echo "$2" | awk '{print $1 * 100}') - if ! isnumeric "$arg" || (($arg > 200)); then + arg=$(awk -v val="$2" 'BEGIN { print val * 100 }') + if ! isnumeric "$arg" || (( arg > 200 )); then echo "invalid argument '$2' for option -- '$1'" exit 1 fi max_delta_size=$arg + shift ;; + --) shift - ;; - --) shift; args+=("$@"); break 2 ;; - -*) echo "invalid option -- '$1'"; usage; exit 1 ;; - *) args+=("$1");; + break 2 ;; esac shift done -if (( ${#args[@]} != 2 )); then +if (( $# != 2 )); then usage exit 1 fi -for i in "${args[@]}"; do +for i in "$@"; do if [[ ! -f $i ]]; then error "$(gettext "File '%s' does not exist")" "$i" exit 1 diff --git a/scripts/po/POTFILES.in b/scripts/po/POTFILES.in index 007e535f..162731b9 100644 --- a/scripts/po/POTFILES.in +++ b/scripts/po/POTFILES.in @@ -8,4 +8,4 @@ scripts/pacman-optimize.sh.in scripts/pkgdelta.sh.in scripts/repo-add.sh.in scripts/library/output_format.sh -scripts/library/parse_options.sh +scripts/library/parseopts.sh diff --git a/scripts/rankmirrors.sh.in b/scripts/rankmirrors.sh.in deleted file mode 100644 index 875a1439..00000000 --- a/scripts/rankmirrors.sh.in +++ /dev/null @@ -1,212 +0,0 @@ -#!/bin/bash -# -# rankmirrors - read a list of mirrors from a file and rank them by speed -# @configure_input@ -# -# Copyright (c) 2009 Matthew Bruenig <matthewbruenig@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 <http://www.gnu.org/licenses/>. - -# traps interrupt key to spit out pre-interrupt info -trap finaloutput INT - -usage() { - echo "Usage: rankmirrors [options] MIRRORFILE | URL" - echo - echo "Ranks pacman mirrors by their connection and opening speed. Pacman mirror" - echo "files are located in @sysconfdir@/pacman.d/. It can also rank one mirror if the URL is" - echo "provided." - echo - echo "Options:" - echo " --version show program's version number and exit" - echo " -h, --help show this help message and exit" - echo " -n NUM number of servers to output, 0 for all" - echo " -t, --times only output mirrors and their response times" - echo " -u, --url test a specific url" - echo " -v, --verbose be verbose in ouptut" - echo " -r, --repo specify a specific repo name instead of guessing" - exit 0 -} - -version() { - echo "rankmirrors (pacman) @PACKAGE_VERSION@" - echo "Copyright (c) 2009 Matthew Bruenig <matthewbruenig@gmail.com>." - echo - echo "This is free software; see the source for copying conditions." - echo "There is NO WARRANTY, to the extent permitted by law." - exit 0 -} - -err() { - echo "$1" >&2 - exit 1 -} - -# gettime fetchurl (e.g gettime http://foo.com/core/os/i686/core.db.tar.gz) -# returns the fetching time, or timeout, or unreachable -gettime() { - IFS=' ' output=( $(curl -s -m 10 -w "%{time_total} %{http_code}" "$1" -o/dev/null) ) - (( $? == 28 )) && echo timeout && return - (( ${output[1]} >= 400 || ! ${output[1]} )) && echo unreachable && return - echo "${output[0]}" -} - -# getfetchurl serverurl (e.g. getturl http://foo.com/core/os/i686) -# if $repo is in the line, then assumes core -# if $arch is in the line, then assumes $(uname -m) -# returns a fetchurl (e.g. http://foo.com/core/os/i686/core.db.tar.gz) -ARCH="$(uname -m)" -getfetchurl() { - local strippedurl="${1%/}" - - local replacedurl="${strippedurl//'$arch'/$ARCH}" - if [[ ! $TARGETREPO ]]; then - replacedurl="${replacedurl//'$repo'/core}" - local tmp="${replacedurl%/*}" - tmp="${tmp%/*}" - - local reponame="${tmp##*/}" - else - replacedurl="${replacedurl//'$repo'/$TARGETREPO}" - local reponame="$TARGETREPO" - fi - - if [[ -z $reponame || $reponame = $replacedurl ]]; then - echo "fail" - else - local fetchurl="${replacedurl}/$reponame.db" - echo "$fetchurl" - fi -} - -# This exists to remove the need for a separate interrupt function -finaloutput() { - IFS=$'\n' read -r -d '' -a sortedarray < \ - <(printf '%s\n' "${timesarray[@]}" | LC_COLLATE=C sort) - - # Final output for mirrorfile - numiterator="0" - if [[ $TIMESONLY ]]; then - echo - echo " Servers sorted by time (seconds):" - for line in "${sortedarray[@]}"; do - echo "${line#* } : ${line% *}" - ((numiterator++)) - (( NUM && numiterator >= NUM )) && break - done - else - for line in "${sortedarray[@]}"; do - echo "Server = ${line#* }" - ((numiterator++)) - (( NUM && numiterator >= NUM )) && break - done - fi - exit 0 -} - - -# Argument parsing -[[ $1 ]] || usage -while [[ $1 ]]; do - if [[ ${1:0:2} = -- ]]; then - case "${1:2}" in - help) usage ;; - version) version ;; - times) TIMESONLY=1 ; shift ;; - verbose) VERBOSE=1 ; shift ;; - url) CHECKURL=1; [[ $2 ]] || err "Must specify url."; URL="$2"; shift 2;; - repo) [[ $2 ]] || err "Must specify repo name."; TARGETREPO="$2"; shift 2;; - *) err "\`$1' is an invalid argument." - esac - elif [[ ${1:0:1} = - ]]; then - - if [[ ! ${1:1:1} ]]; then - [[ -t 0 ]] && err "Stdin is empty." - IFS=$'\n' linearray=( $(</dev/stdin) ) - STDIN=1 - shift - else - snum=1 - for ((i=1 ; i<${#1}; i++)); do - case ${1:$i:1} in - h) usage ;; - t) TIMESONLY=1 ;; - v) VERBOSE=1 ;; - u) CHECKURL=1; [[ $2 ]] || err "Must specify url."; URL="$2"; snum=2;; - r) [[ $2 ]] || err "Must specify repo name."; TARGETREPO="$2"; snum=2;; - n) [[ $2 ]] || err "Must specify number." ; NUM="$2" ; snum=2;; - *) err "\`-$1' is an invald argument." ;; - esac - done - shift $snum - fi - elif [[ -f $1 ]]; then - FILE="1" - IFS=$'\n' linearray=( $(<$1) ) - [[ $linearray ]] || err "File is empty." - shift - else - err "\`$1' does not exist." - fi -done - -# Some sanity checks -[[ $NUM ]] || NUM=0 -[[ $FILE && $CHECKURL ]] && err "Cannot specify a url and mirrorfile." -[[ $FILE || $CHECKURL || $STDIN ]] || err "Must specify url, mirrorfile, or stdin." - -# Single url handling -if [[ $CHECKURL ]]; then - url="$(getfetchurl "$URL")" - [[ $url = fail ]] && err "url \`$URL' is malformed." - [[ $VERBOSE ]] && echo "Testing $url..." - time=$(gettime "$url") - echo "$URL : $time" - exit 0 -fi - -# Get url results from mirrorfile, fill up the array, and so on -if [[ $TIMESONLY ]]; then - echo "Querying servers, this may take some time..." -elif [[ $FILE ]]; then - echo "# Server list generated by rankmirrors on $(date +%Y-%m-%d)" -fi - -timesarray=() -for line in "${linearray[@]}"; do - if [[ $line =~ ^[[:space:]]*# ]]; then - [[ $TIMESONLY ]] || echo $line - elif [[ $line =~ ^[[:space:]]*Server ]]; then - - # Getting values and times and such - server="${line#*= }" - server="${server%%#*}" - url="$(getfetchurl "$server")" - [[ $url = fail ]] && err "url \`$URL' is malformed." - time=$(gettime "$url") - timesarray+=("$time $server") - - # Output - if [[ $VERBOSE && $TIMESONLY ]]; then - echo "$server ... $time" - elif [[ $VERBOSE ]]; then - echo "# $server ... $time" - elif [[ $TIMESONLY ]]; then - echo -n " *" - fi - fi -done -finaloutput - -# vim: set ts=2 sw=2 noet: diff --git a/scripts/repo-add.sh.in b/scripts/repo-add.sh.in index 5724022a..504cae97 100644 --- a/scripts/repo-add.sh.in +++ b/scripts/repo-add.sh.in @@ -25,8 +25,8 @@ shopt -s extglob export TEXTDOMAIN='pacman-scripts' export TEXTDOMAINDIR='@localedir@' -myver='@PACKAGE_VERSION@' -confdir='@sysconfdir@' +declare -r myver='@PACKAGE_VERSION@' +declare -r confdir='@sysconfdir@' QUIET=0 DELTA=0 @@ -176,6 +176,11 @@ db_remove_delta() { if grep -q "$filename" "$deltas"; then sed -i.backup "/$filename/d" "$deltas" && rm -f "$deltas.backup" msg2 "$(gettext "Removing existing entry '%s'...")" "$filename" + # empty deltas file contains only "%DELTAS%" + if (( $(wc -l < "$deltas") == 1 )); then + msg2 "$(gettext "Removing empty deltas file ...")" + rm "$deltas" + fi return 0 fi @@ -203,7 +208,7 @@ create_signature() { gpg --detach-sign --use-agent ${SIGNWITHKEY} "$dbfile" &>/dev/null || ret=$? if (( ! ret )); then - msg2 "$(gettext "Created signature file %s.")" "${dbfile##*/}.sig" + msg2 "$(gettext "Created signature file %s.")" "${dbfile##*/.tmp.}.sig" else warning "$(gettext "Failed to sign package database.")" fi @@ -251,7 +256,8 @@ verify_repo_extension() { db_write_entry() { # blank out all variables local pkgfile=$1 - local -a _groups _licenses _replaces _depends _conflicts _provides _optdepends + local -a _groups _licenses _replaces _depends _conflicts _provides \ + _optdepends _makedepends _checkdepends local pkgname pkgver pkgdesc csize size url arch builddate packager \ md5sum sha256sum pgpsig pgpsigsize @@ -264,13 +270,15 @@ db_write_entry() { # normalize whitespace with an extglob declare "$var=${val//+([[:space:]])/ }" case $var in - group) _groups+=("$group") ;; - license) _licenses+=("$license") ;; - replaces) _replaces+=("$replaces") ;; - depend) _depends+=("$depend") ;; - conflict) _conflicts+=("$conflict") ;; - provides) _provides+=("$provides") ;; + group) _groups+=("$group") ;; + license) _licenses+=("$license") ;; + replaces) _replaces+=("$replaces") ;; + depend) _depends+=("$depend") ;; + conflict) _conflicts+=("$conflict") ;; + provides) _provides+=("$provides") ;; optdepend) _optdepends+=("$optdepend") ;; + makedepend) _makedepends+=("$makedepend") ;; + checkdepend) _checkdepends+=("$checkdepend") ;; esac done< <(bsdtar -xOqf "$pkgfile" .PKGINFO) @@ -353,10 +361,12 @@ db_write_entry() { # create depends entry msg2 "$(gettext "Creating '%s' db entry...")" 'depends' { - format_entry "DEPENDS" "${_depends[@]}" - format_entry "CONFLICTS" "${_conflicts[@]}" - format_entry "PROVIDES" "${_provides[@]}" + format_entry "DEPENDS" "${_depends[@]}" + format_entry "CONFLICTS" "${_conflicts[@]}" + format_entry "PROVIDES" "${_provides[@]}" format_entry "OPTDEPENDS" "${_optdepends[@]}" + format_entry "MAKEDEPENDS" "${_makedepends[@]}" + format_entry "CHECKDEPENDS" "${_checkdepends[@]}" } >'depends' popd >/dev/null @@ -424,13 +434,8 @@ elephant() { check_repo_db() { local repodir - # ensure the path to the DB exists - if [[ "$LOCKFILE" == /* ]]; then - repodir=${LOCKFILE%/*}/ - else - repodir=$PWD/$LOCKFILE - repodir=${repodir%/*}/ - fi + # ensure the path to the DB exists; $LOCKFILE is always an absolute path + repodir=${LOCKFILE%/*}/ if [[ ! -d $repodir ]]; then error "$(gettext "%s does not exist or is not a directory.")" "$repodir" @@ -579,7 +584,7 @@ if [[ $cmd != "repo-add" && $cmd != "repo-remove" ]]; then exit 1 fi -tmpdir=$(mktemp -d /tmp/repo-tools.XXXXXXXXXX) || (\ +tmpdir=$(mktemp -d "${TMPDIR:-/tmp}/repo-tools.XXXXXXXXXX") || (\ error "$(gettext "Cannot create temp directory for database building.")"; \ exit 1) mkdir "$tmpdir/tree" @@ -637,7 +642,11 @@ if [[ -z $REPO_DB_FILE ]]; then exit 1 fi -LOCKFILE=$REPO_DB_FILE.lck +if [[ $REPO_DB_FILE == /* ]]; then + LOCKFILE=$REPO_DB_FILE.lck +else + LOCKFILE=$PWD/$REPO_DB_FILE.lck +fi verify_repo_extension "$REPO_DB_FILE" >/dev/null check_repo_db @@ -654,37 +663,51 @@ if (( success )); then msg "$(gettext "Creating updated database file '%s'")" "$REPO_DB_FILE" TAR_OPT=$(verify_repo_extension "$REPO_DB_FILE") + # $LOCKFILE is already guaranteed to be absolute so this is safe + dirname=${LOCKFILE%/*} filename=${REPO_DB_FILE##*/} + # this ensures we create it on the same filesystem, making moves atomic + tempname=$dirname/.tmp.$filename pushd "$tmpdir/tree" >/dev/null if ( shopt -s nullglob; files=(*); (( ${#files[*]} )) ); then - bsdtar -c${TAR_OPT}f "$tmpdir/$filename" * + bsdtar -c${TAR_OPT}f "$tempname" * else # we have no packages remaining? zip up some emptyness warning "$(gettext "No packages remain, creating empty database.")" - bsdtar -c${TAR_OPT}f "$tmpdir/$filename" -T /dev/null + bsdtar -c${TAR_OPT}f "$tempname" -T /dev/null fi popd >/dev/null - create_signature "$tmpdir/$filename" + create_signature "$tempname" - [[ -f $REPO_DB_FILE ]] && mv -f "$REPO_DB_FILE" "${REPO_DB_FILE}.old" + # hardlink or move the previous version of the database and signature to .old + # extension as a backup measure + if [[ -f $REPO_DB_FILE ]]; then + ln -f "$REPO_DB_FILE" "$REPO_DB_FILE.old" 2>/dev/null || \ + mv -f "$REPO_DB_FILE" "$REPO_DB_FILE.old" + fi if [[ -f $REPO_DB_FILE.sig ]]; then - mv -f "$REPO_DB_FILE.sig" "$REPO_DB_FILE.old.sig" + ln -f "$REPO_DB_FILE.sig" "$REPO_DB_FILE.old.sig" 2>/dev/null || \ + mv -f "$REPO_DB_FILE.sig" "$REPO_DB_FILE.old.sig" else rm -f "$REPO_DB_FILE.old.sig" fi - [[ -f $tmpdir/$filename ]] && mv "$tmpdir/$filename" "$REPO_DB_FILE" - [[ -f $tmpdir/$filename.sig ]] && mv "$tmpdir/$filename.sig" "$REPO_DB_FILE.sig" + + # rotate the newly-created database and signature into place + mv "$tempname" "$REPO_DB_FILE" + if [[ -f $tempname.sig ]]; then + mv "$tempname.sig" "$REPO_DB_FILE.sig" + fi + dblink=${REPO_DB_FILE%.tar*} - target=${REPO_DB_FILE##*/} rm -f "$dblink" "$dblink.sig" - ln -s "$target" "$dblink" 2>/dev/null || \ - ln "$target" "$dblink" 2>/dev/null || \ + ln -s "$filename" "$dblink" 2>/dev/null || \ + ln "$filename" "$dblink" 2>/dev/null || \ cp "$REPO_DB_FILE" "$dblink" if [[ -f "$REPO_DB_FILE.sig" ]]; then - ln -s "$target.sig" "$dblink.sig" 2>/dev/null || \ - ln "$target.sig" "$dblink.sig" 2>/dev/null || \ + ln -s "$filename.sig" "$dblink.sig" 2>/dev/null || \ + ln "$filename.sig" "$dblink.sig" 2>/dev/null || \ cp "$REPO_DB_FILE.sig" "$dblink.sig" fi else |