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 | 256 | ||||
| -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, 1037 insertions, 911 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..ae491d26 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 @@ -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,7 @@ 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 +	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 +448,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 +504,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 +595,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 | 
