summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am6
-rw-r--r--configure.ac1
-rw-r--r--contrib/bash_completion.in35
-rw-r--r--doc/makepkg.8.txt5
-rw-r--r--doc/pacman-key.8.txt74
-rw-r--r--scripts/Makefile.am6
-rw-r--r--scripts/library/README24
-rw-r--r--scripts/library/parse_options.sh105
-rw-r--r--scripts/library/parseopts.sh141
-rw-r--r--scripts/makepkg.sh.in28
-rw-r--r--scripts/pacman-key.sh.in190
-rw-r--r--scripts/po/POTFILES.in2
-rw-r--r--test/scripts/Makefile.am9
-rwxr-xr-xtest/scripts/parseopts_test.sh138
14 files changed, 505 insertions, 259 deletions
diff --git a/Makefile.am b/Makefile.am
index a024a2e6..e08b809b 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS = lib/libalpm src/util src/pacman scripts etc test/pacman test/util
+SUBDIRS = lib/libalpm src/util src/pacman scripts etc test/pacman test/util test/scripts
if WANT_DOC
SUBDIRS += doc
endif
@@ -23,7 +23,7 @@ dist_pkgdata_DATA = \
proto/ChangeLog.proto
# run the pactest test suite and vercmp tests
-check-local: test/pacman test/util src/pacman src/util
+check-local: test/pacman test/scripts test/util src/pacman src/util
LC_ALL=C $(PYTHON) $(top_srcdir)/test/pacman/pactest.py --debug=1 \
--test $(top_srcdir)/test/pacman/tests/*.py \
-p $(top_builddir)/src/pacman/pacman
@@ -31,6 +31,8 @@ check-local: test/pacman test/util src/pacman src/util
$(top_builddir)/src/util/pacsort
$(SH) $(top_srcdir)/test/util/vercmptest.sh \
$(top_builddir)/src/util/vercmp
+ $(BASH_SHELL) $(top_srcdir)/test/scripts/parseopts_test.sh \
+ $(top_srcdir)/scripts/library/parseopts.sh
# create the pacman DB and cache directories upon install
install-data-local:
diff --git a/configure.ac b/configure.ac
index 8898f146..d488bfaf 100644
--- a/configure.ac
+++ b/configure.ac
@@ -442,6 +442,7 @@ doc/Makefile
etc/Makefile
test/pacman/Makefile
test/pacman/tests/Makefile
+test/scripts/Makefile
test/util/Makefile
contrib/Makefile
Makefile
diff --git a/contrib/bash_completion.in b/contrib/bash_completion.in
index 95a27702..1b265e0c 100644
--- a/contrib/bash_completion.in
+++ b/contrib/bash_completion.in
@@ -27,19 +27,44 @@ _arch_incomp() {
local r="\s-(-${1#* }\s|\w*${1% *})"; [[ $COMP_LINE =~ $r ]]
}
+_pacman_keyids() {
+ \pacman-key --list-keys 2>/dev/null | awk '
+ $1 == "pub" {
+ # key id
+ split($2, a, "/"); print a[2]
+ }
+ $1 == "uid" {
+ # email
+ if (match($NF, /<[^>]+>/))
+ print substr($NF, RSTART + 1, RLENGTH - 2)
+ }'
+}
+
_pacman_key() {
- local cur opts prev
+ local o cur opts prev wantfiles
COMPREPLY=()
_get_comp_words_by_ref cur prev
opts=('add delete export finger help list-keys recv-keys updatedb verify
version config edit-key gpgdir import import-trustdb init keyserver
list-sigs lsign-key populate refresh-keys'
'a d e f h l r u v V')
- if [[ $prev = 'pacman-key' ]]; then
- _arch_ptr2comp opts
- elif [[ $cur = -* &&
- $prev != -@(a|-add|c|-config|g|-gpgdir|h|-help|import?(-trustdb)) ]]; then
+
+ # operations for which we want to complete keyids
+ for o in 'd delete' 'e export' 'f finger' 'l list-keys' 'r recv-keys' \
+ 'edit-key' 'list-sigs' 'refresh-keys'; do
+ _arch_incomp "$o" && break
+ unset o
+ done
+
+ # options for which we want file completion
+ wantfiles='-@(c|-config|g|-gpgdir)'
+
+ if [[ $prev = 'pacman-key' || ( $cur = -* && $prev != $wantfiles ) ]]; then
_arch_ptr2comp opts
+ elif [[ $prev = @(-k|--keyserver) ]]; then
+ return
+ elif [[ $prev != $wantfiles && $o ]]; then
+ COMPREPLY=($(compgen -W '$(_pacman_keyids)' -- "$cur"))
fi
true
}
diff --git a/doc/makepkg.8.txt b/doc/makepkg.8.txt
index df41e187..27875a30 100644
--- a/doc/makepkg.8.txt
+++ b/doc/makepkg.8.txt
@@ -152,8 +152,9 @@ Options
such as a chroot or remote builder. It will also satisfy requirements of
the GPL when distributing binary packages.
-*\--pkg <list>*::
- Only build listed packages from a split package.
+*\--pkg* <list>::
+ Only build listed packages from a split package. Multiple packages should
+ be comma separated in the list. This option can be specified multiple times.
*\--check*::
Run the check() function in the PKGBUILD, overriding the setting in
diff --git a/doc/pacman-key.8.txt b/doc/pacman-key.8.txt
index 3631ec8c..8126edb7 100644
--- a/doc/pacman-key.8.txt
+++ b/doc/pacman-key.8.txt
@@ -12,7 +12,7 @@ pacman-key - manage pacman's list of trusted keys
Synopsis
--------
-'pacman-key' [options]
+'pacman-key' [options] operation [targets]
Description
@@ -26,45 +26,40 @@ More complex keyring management can be achieved using GnuPG directly combined wi
the '\--homedir' option pointing at the pacman keyring (located in
+{sysconfdir}/pacman.d/gnupg+ by default).
+Invoking pacman-key consists of supplying an operation with any potential
+options and targets to operate on. Depending on the operation, a 'target' may
+be a valid key identifier, filename, or directory.
-Options
--------
-*-a, \--add* [file(s)]::
+Operations
+----------
+*-a, \--add*::
Add the key(s) contained in the specified file or files to pacman's
keyring. If a key already exists, update it.
-*\--config* <file>::
- Use an alternate config file instead of the +{sysconfdir}/pacman.conf+
- default.
-
-*-d, \--delete* <keyid(s)>::
+*-d, \--delete*::
Remove the key(s) identified by the specified keyid(s) from pacman's
keyring.
-*-e, \--export* [keyid(s)]::
+*-e, \--export*::
Export key(s) identified by the specified keyid(s) to 'stdout'. If no keyid
is specified, all keys will be exported.
-*\--edit-key* <keyid(s)>::
+*\--edit-key*::
Present a menu for key management task on the specified keyid(s). Useful
for adjusting a keys trust level.
-*-f, \--finger* [keyid(s)]::
+*-f, \--finger*::
List a fingerprint for each specified keyid, or for all known keys if no
keyids are specified.
-*\--gpgdir* <dir>::
- Set an alternate home directory for GnuPG. If unspecified, the value is
- read from +{sysconfdir}/pacman.conf+.
-
*-h, \--help*::
Output syntax and command line options.
-*\--import* <dir(s)>::
+*\--import*::
Imports keys from `pubring.gpg` into the public keyring from the specified
directories.
-*\--import-trustdb* <dir(s)> ::
+*\--import-trustdb*::
Imports ownertrust values from `trustdb.gpg` into the shared trust database
from the specified directories.
@@ -72,42 +67,53 @@ Options
Ensure the keyring is properly initialized and has the required access
permissions.
-*\--keyserver* <keyserver>::
- Use the specified keyserver if the operation requires one. This will take
- precedence over any keyserver option specified in a `gpg.conf`
- configuration file. Running '\--init' with this option will set the default
- keyserver if one was not already configured.
-
-*-l, \--list-keys* [keyid(s)]::
+*-l, \--list-keys*::
Lists all or specified keys from the public keyring.
-*\--list-sigs* [keyid(s)]::
+*\--list-sigs*::
Same as '\--list-keys', but the signatures are listed too.
-*\--lsign-key* <keyid>::
+*\--lsign-key*::
Locally sign the given key. This is primarily used to root the web of trust
in the local private key generated by '\--init'.
-*-r, \--recv-keys* <keyid(s)>::
+*-r, \--recv-keys*::
Equivalent to '\--recv-keys' in GnuPG.
-*\--refresh-keys* [keyid(s)]::
+*\--refresh-keys*::
Equivalent to '\--refresh-keys' in GnuPG.
-*\--populate* [keyring(s)]::
+*\--populate*::
Reload the default keys from the (optionally provided) keyrings in
+{pkgdatadir}/keyrings+. For more information, see
<<SC,Providing a Keyring for Import>> below.
*-u, \--updatedb*::
- Equivalent to '\--check-trustdb' in GnuPG.
-
-*-v, \--verify* <signature>::
- Verify the given signature file.
+ Equivalent to '\--check-trustdb' in GnuPG. This operation can be specified with
+ other operations.
*-V, \--version*::
Displays the program version.
+*-v, \--verify*::
+ Verify the file(s) specified by the signature(s).
+
+Options
+-------
+*\--config* <file>::
+ Use an alternate config file instead of the +{sysconfdir}/pacman.conf+
+ default.
+
+*\--gpgdir* <dir>::
+ Set an alternate home directory for GnuPG. If unspecified, the value is
+ read from +{sysconfdir}/pacman.conf+.
+
+*\--keyserver* <keyserver>::
+ Use the specified keyserver if the operation requires one. This will take
+ precedence over any keyserver option specified in a `gpg.conf`
+ configuration file. Running '\--init' with this option will set the default
+ keyserver if one was not already configured.
+
Providing a Keyring for Import
------------------------------
diff --git a/scripts/Makefile.am b/scripts/Makefile.am
index 328fbff2..b8a19900 100644
--- a/scripts/Makefile.am
+++ b/scripts/Makefile.am
@@ -27,7 +27,7 @@ EXTRA_DIST = \
LIBRARY = \
library/output_format.sh \
- library/parse_options.sh
+ library/parseopts.sh
# Files that should be removed, but which Automake does not know.
MOSTLYCLEANFILES = $(bin_SCRIPTS)
@@ -67,7 +67,7 @@ $(OURSCRIPTS): Makefile
makepkg: \
$(srcdir)/makepkg.sh.in \
- $(srcdir)/library/parse_options.sh
+ $(srcdir)/library/parseopts.sh
pacman-db-upgrade: \
$(srcdir)/pacman-db-upgrade.sh.in \
@@ -76,7 +76,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 \
diff --git a/scripts/library/README b/scripts/library/README
index 1e9c962b..c71c0714 100644
--- a/scripts/library/README
+++ b/scripts/library/README
@@ -8,8 +8,22 @@ 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)
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/makepkg.sh.in b/scripts/makepkg.sh.in
index 46191ee4..8b3c80ce 100644
--- a/scripts/makepkg.sh.in
+++ b/scripts/makepkg.sh.in
@@ -1885,7 +1885,7 @@ canonicalize_path() {
fi
}
-m4_include(library/parse_options.sh)
+m4_include(library/parseopts.sh)
usage() {
printf "makepkg (pacman) %s\n" "$myver"
@@ -1954,19 +1954,20 @@ 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+=('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
@@ -1997,7 +1998,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 ;;
@@ -2010,8 +2011,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
diff --git a/scripts/pacman-key.sh.in b/scripts/pacman-key.sh.in
index 288b76eb..bd2c7397 100644
--- a/scripts/pacman-key.sh.in
+++ b/scripts/pacman-key.sh.in
@@ -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() {
@@ -146,7 +149,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 +220,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
@@ -311,24 +314,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 gpg keychain.")"
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 gpg keychain.")"
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
@@ -340,8 +343,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 gpg keychain.")"
exit 1
fi
@@ -349,7 +352,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
@@ -358,7 +361,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 -
@@ -379,7 +382,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"
@@ -397,7 +400,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
@@ -405,7 +408,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
@@ -413,7 +416,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
@@ -421,25 +424,30 @@ lsign_keys() {
}
receive_keys() {
- if ! "${GPG_PACMAN[@]}" --recv-keys "${KEYIDS[@]}" ; then
+ if ! "${GPG_PACMAN[@]}" --recv-keys "$@" ; 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() {
@@ -457,56 +465,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
@@ -549,23 +556,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/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/test/scripts/Makefile.am b/test/scripts/Makefile.am
new file mode 100644
index 00000000..b949e880
--- /dev/null
+++ b/test/scripts/Makefile.am
@@ -0,0 +1,9 @@
+check_SCRIPTS = \
+ parseopts_test.sh
+
+noinst_SCRIPTS = $(check_SCRIPTS)
+
+EXTRA_DIST = \
+ $(check_SCRIPTS)
+
+# vim:set ts=2 sw=2 noet:
diff --git a/test/scripts/parseopts_test.sh b/test/scripts/parseopts_test.sh
new file mode 100755
index 00000000..b5c07b5d
--- /dev/null
+++ b/test/scripts/parseopts_test.sh
@@ -0,0 +1,138 @@
+#!/bin/bash
+
+declare -i testcount=0 pass=0 fail=0
+
+# source the library function
+if [[ -z $1 || ! -f $1 ]]; then
+ printf "error: path to parseopts library not provided or does not exist\n"
+ exit 1
+fi
+. "$1"
+
+if ! type -t parseopts >/dev/null; then
+ printf 'parseopts function not found\n'
+ exit 1
+fi
+
+# borrow opts from makepkg
+OPT_SHORT="AcdefFghiLmop:rRsV"
+OPT_LONG=('allsource' 'asroot' 'ignorearch' 'check' 'clean:' 'cleanall' 'nodeps'
+ 'noextract' 'force' 'forcever:' 'geninteg' 'help' 'holdver'
+ 'install' 'key:' 'log' 'nocolor' 'nobuild' 'nocheck' 'nosign' 'pkg:' 'rmdeps'
+ 'repackage' 'skipinteg' 'sign' 'source' 'syncdeps' 'version' 'config:'
+ 'noconfirm' 'noprogressbar')
+
+parse() {
+ local result=$1 tokencount=$2; shift 2
+
+ (( ++testcount ))
+ parseopts "$OPT_SHORT" "${OPT_LONG[@]}" -- "$@" 2>/dev/null
+ test_result "$result" "$tokencount" "$*" "${OPTRET[@]}"
+ unset OPTRET
+}
+
+test_result() {
+ local result=$1 tokencount=$2 input=$3; shift 3
+
+ if [[ $result = "$*" ]] && (( tokencount == $# )); then
+ (( ++pass ))
+ else
+ printf '[TEST %3s]: FAIL\n' "$testcount"
+ printf ' input: %s\n' "$input"
+ printf ' output: %s (%s tokens)\n' "$*" "$#"
+ printf ' expected: %s (%s tokens)\n' "$result" "$tokencount"
+ echo
+ (( ++fail ))
+ fi
+}
+
+summarize() {
+ if (( !fail )); then
+ printf 'All %s tests successful\n' "$testcount"
+ exit 0
+ else
+ printf '%s of %s tests failed\n' "$fail" "$testcount"
+ exit 1
+ fi
+}
+trap 'summarize' EXIT
+
+printf 'Beginning parseopts tests\n'
+
+# usage: parse <expected result> <token count> test-params...
+# a failed parse will match only the end of options marker '--'
+
+# no options
+parse '--' 1
+
+# short options
+parse '-s -r --' 3 -s -r
+
+# short options, no spaces
+parse '-s -r --' 3 -sr
+
+# short opt missing an opt arg
+parse '--' 1 -s -p
+
+# short opt with an opt arg
+parse '-p PKGBUILD -L --' 4 -p PKGBUILD -L
+
+# short opt with an opt arg, no space
+parse '-p PKGBUILD --' 3 -pPKGBUILD
+
+# valid shortopts as a long opt
+parse '--' 1 --sir
+
+# long opt wiht no optarg
+parse '--log --' 2 --log
+
+# long opt with missing optarg
+parse '--' 1 -sr --pkg
+
+# long opt with optarg
+parse '--pkg foo --' 3 --pkg foo
+
+# long opt with optarg with whitespace
+parse '--pkg foo bar -- baz' 4 --pkg "foo bar" baz
+
+# long opt with optarg with =
+parse '--pkg foo=bar -- baz' 4 --pkg foo=bar baz
+
+# long opt with explicit optarg
+parse '--pkg bar -- foo baz' 5 foo --pkg=bar baz
+
+# long opt with explicit optarg, with whitespace
+parse '--pkg foo bar -- baz' 4 baz --pkg="foo bar"
+
+# long opt with explicit optarg that doesn't take optarg
+parse '--' 1 --force=always -s
+
+# long opt with explicit optarg with =
+parse '--pkg foo=bar --' 3 --pkg=foo=bar
+
+# explicit end of options with options after
+parse '-s -r -- foo bar baz' 6 -s -r -- foo bar baz
+
+# non-option parameters mixed in with options
+parse '-s -r -- foo baz' 5 -s foo baz -r
+
+# optarg with whitespace
+parse '-p foo bar -s --' 4 -p'foo bar' -s
+
+# non-option parameter with whitespace
+parse '-i -- foo bar' 3 -i 'foo bar'
+
+# successful stem match (opt has no arg)
+parse '--nocolor --' 2 --nocol
+
+# successful stem match (opt has arg)
+parse '--config foo --' 3 --conf foo
+
+# ambiguous long opt
+parse '--' 1 '--for'
+
+# exact match on a possible stem (--force & --forcever)
+parse '--force --' 2 --force
+
+# exact match on possible stem (opt has optarg)
+parse '--clean foo --' 3 --clean=foo