Merge branch 'parse-opts'

This commit is contained in:
Dan McGee 2012-04-24 08:39:31 -05:00
commit ac6ebcfe93
14 changed files with 505 additions and 259 deletions

View file

@ -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 if WANT_DOC
SUBDIRS += doc SUBDIRS += doc
endif endif
@ -23,7 +23,7 @@ dist_pkgdata_DATA = \
proto/ChangeLog.proto proto/ChangeLog.proto
# run the pactest test suite and vercmp tests # 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 \ LC_ALL=C $(PYTHON) $(top_srcdir)/test/pacman/pactest.py --debug=1 \
--test $(top_srcdir)/test/pacman/tests/*.py \ --test $(top_srcdir)/test/pacman/tests/*.py \
-p $(top_builddir)/src/pacman/pacman -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 $(top_builddir)/src/util/pacsort
$(SH) $(top_srcdir)/test/util/vercmptest.sh \ $(SH) $(top_srcdir)/test/util/vercmptest.sh \
$(top_builddir)/src/util/vercmp $(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 # create the pacman DB and cache directories upon install
install-data-local: install-data-local:

View file

@ -442,6 +442,7 @@ doc/Makefile
etc/Makefile etc/Makefile
test/pacman/Makefile test/pacman/Makefile
test/pacman/tests/Makefile test/pacman/tests/Makefile
test/scripts/Makefile
test/util/Makefile test/util/Makefile
contrib/Makefile contrib/Makefile
Makefile Makefile

View file

@ -27,19 +27,44 @@ _arch_incomp() {
local r="\s-(-${1#* }\s|\w*${1% *})"; [[ $COMP_LINE =~ $r ]] 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() { _pacman_key() {
local cur opts prev local o cur opts prev wantfiles
COMPREPLY=() COMPREPLY=()
_get_comp_words_by_ref cur prev _get_comp_words_by_ref cur prev
opts=('add delete export finger help list-keys recv-keys updatedb verify opts=('add delete export finger help list-keys recv-keys updatedb verify
version config edit-key gpgdir import import-trustdb init keyserver version config edit-key gpgdir import import-trustdb init keyserver
list-sigs lsign-key populate refresh-keys' list-sigs lsign-key populate refresh-keys'
'a d e f h l r u v V') 'a d e f h l r u v V')
if [[ $prev = 'pacman-key' ]]; then
_arch_ptr2comp opts # operations for which we want to complete keyids
elif [[ $cur = -* && for o in 'd delete' 'e export' 'f finger' 'l list-keys' 'r recv-keys' \
$prev != -@(a|-add|c|-config|g|-gpgdir|h|-help|import?(-trustdb)) ]]; then '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 _arch_ptr2comp opts
elif [[ $prev = @(-k|--keyserver) ]]; then
return
elif [[ $prev != $wantfiles && $o ]]; then
COMPREPLY=($(compgen -W '$(_pacman_keyids)' -- "$cur"))
fi fi
true true
} }

View file

@ -152,8 +152,9 @@ Options
such as a chroot or remote builder. It will also satisfy requirements of such as a chroot or remote builder. It will also satisfy requirements of
the GPL when distributing binary packages. the GPL when distributing binary packages.
*\--pkg <list>*:: *\--pkg* <list>::
Only build listed packages from a split package. 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*:: *\--check*::
Run the check() function in the PKGBUILD, overriding the setting in Run the check() function in the PKGBUILD, overriding the setting in

View file

@ -12,7 +12,7 @@ pacman-key - manage pacman's list of trusted keys
Synopsis Synopsis
-------- --------
'pacman-key' [options] 'pacman-key' [options] operation [targets]
Description 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 the '\--homedir' option pointing at the pacman keyring (located in
+{sysconfdir}/pacman.d/gnupg+ by default). +{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 Operations
------- ----------
*-a, \--add* [file(s)]:: *-a, \--add*::
Add the key(s) contained in the specified file or files to pacman's Add the key(s) contained in the specified file or files to pacman's
keyring. If a key already exists, update it. keyring. If a key already exists, update it.
*\--config* <file>:: *-d, \--delete*::
Use an alternate config file instead of the +{sysconfdir}/pacman.conf+
default.
*-d, \--delete* <keyid(s)>::
Remove the key(s) identified by the specified keyid(s) from pacman's Remove the key(s) identified by the specified keyid(s) from pacman's
keyring. keyring.
*-e, \--export* [keyid(s)]:: *-e, \--export*::
Export key(s) identified by the specified keyid(s) to 'stdout'. If no keyid Export key(s) identified by the specified keyid(s) to 'stdout'. If no keyid
is specified, all keys will be exported. 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 Present a menu for key management task on the specified keyid(s). Useful
for adjusting a keys trust level. 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 List a fingerprint for each specified keyid, or for all known keys if no
keyids are specified. keyids are specified.
*\--gpgdir* <dir>::
Set an alternate home directory for GnuPG. If unspecified, the value is
read from +{sysconfdir}/pacman.conf+.
*-h, \--help*:: *-h, \--help*::
Output syntax and command line options. Output syntax and command line options.
*\--import* <dir(s)>:: *\--import*::
Imports keys from `pubring.gpg` into the public keyring from the specified Imports keys from `pubring.gpg` into the public keyring from the specified
directories. directories.
*\--import-trustdb* <dir(s)> :: *\--import-trustdb*::
Imports ownertrust values from `trustdb.gpg` into the shared trust database Imports ownertrust values from `trustdb.gpg` into the shared trust database
from the specified directories. from the specified directories.
@ -72,42 +67,53 @@ Options
Ensure the keyring is properly initialized and has the required access Ensure the keyring is properly initialized and has the required access
permissions. permissions.
*\--keyserver* <keyserver>:: *-l, \--list-keys*::
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)]::
Lists all or specified keys from the public keyring. 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. 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 Locally sign the given key. This is primarily used to root the web of trust
in the local private key generated by '\--init'. in the local private key generated by '\--init'.
*-r, \--recv-keys* <keyid(s)>:: *-r, \--recv-keys*::
Equivalent to '\--recv-keys' in GnuPG. Equivalent to '\--recv-keys' in GnuPG.
*\--refresh-keys* [keyid(s)]:: *\--refresh-keys*::
Equivalent to '\--refresh-keys' in GnuPG. Equivalent to '\--refresh-keys' in GnuPG.
*\--populate* [keyring(s)]:: *\--populate*::
Reload the default keys from the (optionally provided) keyrings in Reload the default keys from the (optionally provided) keyrings in
+{pkgdatadir}/keyrings+. For more information, see +{pkgdatadir}/keyrings+. For more information, see
<<SC,Providing a Keyring for Import>> below. <<SC,Providing a Keyring for Import>> below.
*-u, \--updatedb*:: *-u, \--updatedb*::
Equivalent to '\--check-trustdb' in GnuPG. Equivalent to '\--check-trustdb' in GnuPG. This operation can be specified with
other operations.
*-v, \--verify* <signature>::
Verify the given signature file.
*-V, \--version*:: *-V, \--version*::
Displays the program 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 Providing a Keyring for Import
------------------------------ ------------------------------

View file

@ -27,7 +27,7 @@ EXTRA_DIST = \
LIBRARY = \ LIBRARY = \
library/output_format.sh \ library/output_format.sh \
library/parse_options.sh library/parseopts.sh
# Files that should be removed, but which Automake does not know. # Files that should be removed, but which Automake does not know.
MOSTLYCLEANFILES = $(bin_SCRIPTS) MOSTLYCLEANFILES = $(bin_SCRIPTS)
@ -67,7 +67,7 @@ $(OURSCRIPTS): Makefile
makepkg: \ makepkg: \
$(srcdir)/makepkg.sh.in \ $(srcdir)/makepkg.sh.in \
$(srcdir)/library/parse_options.sh $(srcdir)/library/parseopts.sh
pacman-db-upgrade: \ pacman-db-upgrade: \
$(srcdir)/pacman-db-upgrade.sh.in \ $(srcdir)/pacman-db-upgrade.sh.in \
@ -76,7 +76,7 @@ pacman-db-upgrade: \
pacman-key: \ pacman-key: \
$(srcdir)/pacman-key.sh.in \ $(srcdir)/pacman-key.sh.in \
$(srcdir)/library/output_format.sh \ $(srcdir)/library/output_format.sh \
$(srcdir)/library/parse_options.sh $(srcdir)/library/parseopts.sh
pacman-optimize: \ pacman-optimize: \
$(srcdir)/pacman-optimize.sh.in \ $(srcdir)/pacman-optimize.sh.in \

View file

@ -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 functions print to stderr with the appropriate prefix added to the
message. message.
parse_options.sh: parseopts.sh:
A getopt replacement to avoids portability issues, in particular the A getopt_long-like parser which portably supports longopts and shortopts
lack of long option name support in the default getopt provided by some with some GNU extensions. It does not allow for options with optional
platforms. arguments. For both short and long opts, options requiring an argument
Usage: parse_option $SHORT_OPTS $LONG_OPTS "$@" 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)

View file

@ -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
}

View file

@ -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
}

View file

@ -1885,7 +1885,7 @@ canonicalize_path() {
fi fi
} }
m4_include(library/parse_options.sh) m4_include(library/parseopts.sh)
usage() { usage() {
printf "makepkg (pacman) %s\n" "$myver" printf "makepkg (pacman) %s\n" "$myver"
@ -1954,19 +1954,20 @@ ARGLIST=("$@")
# Parse Command Line Options. # Parse Command Line Options.
OPT_SHORT="AcdefFghiLmop:rRsSV" OPT_SHORT="AcdefFghiLmop:rRsSV"
OPT_LONG="allsource,asroot,ignorearch,check,clean,nodeps" OPT_LONG=('allsource' 'asroot' 'ignorearch' 'check' 'clean' 'nodeps'
OPT_LONG+=",noextract,force,forcever:,geninteg,help,holdver,skippgpcheck" 'noextract' 'force' 'forcever:' 'geninteg' 'help' 'holdver' 'skippgpcheck'
OPT_LONG+=",install,key:,log,nocolor,nobuild,nocheck,nosign,pkg:,rmdeps" 'install' 'key:' 'log' 'nocolor' 'nobuild' 'nocheck' 'nosign' 'pkg:' 'rmdeps'
OPT_LONG+=",repackage,skipchecksums,skipinteg,skippgpcheck,sign,source,syncdeps" 'repackage' 'skipchecksums' 'skipinteg' 'skippgpcheck' 'sign' 'source' 'syncdeps'
OPT_LONG+=",version,config:" 'version' 'config:')
# Pacman Options # Pacman Options
OPT_LONG+=",noconfirm,noprogressbar" OPT_LONG+=('noconfirm' 'noprogressbar')
if ! OPT_TEMP="$(parse_options $OPT_SHORT $OPT_LONG "$@")"; then
echo; usage; exit 1 # E_INVALID_OPTION; if ! parseopts "$OPT_SHORT" "${OPT_LONG[@]}" -- "$@"; then
exit 1 # E_INVALID_OPTION;
fi fi
eval set -- "$OPT_TEMP" set -- "${OPTRET[@]}"
unset OPT_SHORT OPT_LONG OPT_TEMP unset OPT_SHORT OPT_LONG OPTRET
while true; do while true; do
case "$1" in case "$1" in
@ -1997,7 +1998,7 @@ while true; do
--nosign) SIGNPKG='n' ;; --nosign) SIGNPKG='n' ;;
-o|--nobuild) NOBUILD=1 ;; -o|--nobuild) NOBUILD=1 ;;
-p) shift; BUILDFILE=$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|--rmdeps) RMDEPS=1 ;;
-R|--repackage) REPKG=1 ;; -R|--repackage) REPKG=1 ;;
--skipchecksums) SKIPCHECKSUMS=1 ;; --skipchecksums) SKIPCHECKSUMS=1 ;;
@ -2010,8 +2011,7 @@ while true; do
-h|--help) usage; exit 0 ;; # E_OK -h|--help) usage; exit 0 ;; # E_OK
-V|--version) version; exit 0 ;; # E_OK -V|--version) version; exit 0 ;; # E_OK
--) OPT_IND=0; shift; break;; --) OPT_IND=0; shift; break 2;;
*) usage; exit 1 ;; # E_INVALID_OPTION
esac esac
shift shift
done done

View file

@ -49,40 +49,43 @@ DEFAULT_KEYSERVER='hkp://pool.sks-keyservers.net'
m4_include(library/output_format.sh) m4_include(library/output_format.sh)
m4_include(library/parse_options.sh) m4_include(library/parseopts.sh)
usage() { usage() {
printf "pacman-key (pacman) %s\n" ${myver} printf "pacman-key (pacman) %s\n" ${myver}
echo echo
printf -- "$(gettext "Usage: %s [options]")\n" $(basename $0) printf -- "$(gettext "Usage: %s [options] operation [targets]")\n" $(basename $0)
echo echo
printf -- "$(gettext "Manage pacman's list of trusted keys")\n" printf -- "$(gettext "Manage pacman's list of trusted keys")\n"
echo echo
printf -- "$(gettext "Options:")\n" printf -- "$(gettext "Operations:")\n"
printf -- "$(gettext " -a, --add [file(s)] Add the specified keys (empty for stdin)")\n" printf -- "$(gettext " -a, --add Add the specified keys (empty for stdin)")\n"
printf -- "$(gettext " -d, --delete <keyid(s)> Remove the specified keyids")\n" printf -- "$(gettext " -d, --delete Remove the specified keyids")\n"
printf -- "$(gettext " -e, --export [keyid(s)] Export the specified or all keyids")\n" printf -- "$(gettext " -e, --export Export the specified or all keyids")\n"
printf -- "$(gettext " -f, --finger [keyid(s)] List fingerprint for specified or all keyids")\n" printf -- "$(gettext " -f, --finger List fingerprint for specified or all keyids")\n"
printf -- "$(gettext " -h, --help Show this help message and exit")\n" printf -- "$(gettext " -l, --list-keys List the specified or all keys")\n"
printf -- "$(gettext " -l, --list-keys [keyid(s)] List the specified or all keys")\n" printf -- "$(gettext " -r, --recv-keys Fetch the specified keyids")\n"
printf -- "$(gettext " -r, --recv-keys <keyid(s)> Fetch the specified keyids")\n"
printf -- "$(gettext " -u, --updatedb Update the trustdb of pacman")\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, --verify Verify the file(s) specified by the signature(s)")\n"
printf -- "$(gettext " -V, --version Show program version")\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\ printf -- "$(gettext " --config <file> Use an alternate config file (instead of\n\
'%s')")\n" "@sysconfdir@/pacman.conf" '%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\ printf -- "$(gettext " --gpgdir <dir> Set an alternate directory for GnuPG (instead\n\
of '%s')")\n" "@sysconfdir@/pacman.d/gnupg" of '%s')")\n" "@sysconfdir@/pacman.d/gnupg"
printf -- "$(gettext " --import <dir(s)> Imports pubring.gpg from dir(s)")\n" printf -- "$(gettext " --keyserver <server-url> Specify a keyserver to use if necessary")\n"
printf -- "$(gettext " --import-trustdb <dir(s)> Imports ownertrust values from trustdb.gpg in dir(s)")\n" echo
printf -- "$(gettext " --init Ensure the keyring is properly initialized")\n" printf -- "$(gettext " -h, --help Show this help message and exit")\n"
printf -- "$(gettext " --keyserver Specify a keyserver to use if necessary")\n" printf -- "$(gettext " -V, --version Show program version")\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"
} }
version() { version() {
@ -146,7 +149,7 @@ add_gpg_conf_option() {
check_keyids_exist() { check_keyids_exist() {
local ret=0 local ret=0
for key in "${KEYIDS[@]}"; do for key in "$@"; do
# Verify if the key exists in pacman's keyring # Verify if the key exists in pacman's keyring
if ! "${GPG_PACMAN[@]}" --list-keys "$key" &>/dev/null ; then if ! "${GPG_PACMAN[@]}" --list-keys "$key" &>/dev/null ; then
error "$(gettext "The key identified by %s could not be found locally.")" "$key" error "$(gettext "The key identified by %s could not be found locally.")" "$key"
@ -217,16 +220,16 @@ check_keyring() {
populate_keyring() { populate_keyring() {
local KEYRING_IMPORT_DIR='@pkgdatadir@/keyrings' local KEYRING_IMPORT_DIR='@pkgdatadir@/keyrings'
local keyring local keyring KEYRINGIDS=("$@")
local ret=0 local ret=0
if [[ -z ${KEYRINGIDS[@]} ]]; then if (( ${#KEYRINGIDS[*]} == 0 )); then
# get list of all available keyrings # get list of all available keyrings
shopt -s nullglob shopt -s nullglob
KEYRINGIDS=("$KEYRING_IMPORT_DIR"/*.gpg) KEYRINGIDS=("$KEYRING_IMPORT_DIR"/*.gpg)
shopt -u nullglob shopt -u nullglob
KEYRINGIDS=("${KEYRINGIDS[@]##*/}") KEYRINGIDS=("${KEYRINGIDS[@]##*/}")
KEYRINGIDS=("${KEYRINGIDS[@]%.gpg}") KEYRINGIDS=("${KEYRINGIDS[@]%.gpg}")
if [[ -z ${KEYRINGIDS[@]} ]]; then if (( ${#KEYRINGIDS[*]} == 0 )); then
error "$(gettext "No keyring files exist in %s.")" "$KEYRING_IMPORT_DIR" error "$(gettext "No keyring files exist in %s.")" "$KEYRING_IMPORT_DIR"
ret=1 ret=1
fi fi
@ -311,24 +314,24 @@ populate_keyring() {
} }
add_keys() { 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.")" error "$(gettext "A specified keyfile could not be added to the gpg keychain.")"
exit 1 exit 1
fi fi
} }
delete_keys() { delete_keys() {
check_keyids_exist check_keyids_exist "$@"
if ! "${GPG_PACMAN[@]}" --quiet --batch --delete-key --yes "${KEYIDS[@]}" ; then if ! "${GPG_PACMAN[@]}" --quiet --batch --delete-key --yes "$@" ; then
error "$(gettext "A specified key could not be removed from the gpg keychain.")" error "$(gettext "A specified key could not be removed from the gpg keychain.")"
exit 1 exit 1
fi fi
} }
edit_keys() { edit_keys() {
check_keyids_exist check_keyids_exist "$@"
local ret=0 local ret=0
for key in "${KEYIDS[@]}"; do for key in "$@"; do
if ! "${GPG_PACMAN[@]}" --edit-key "$key" ; then if ! "${GPG_PACMAN[@]}" --edit-key "$key" ; then
error "$(gettext "The key identified by %s could not be edited.")" "$key" error "$(gettext "The key identified by %s could not be edited.")" "$key"
ret=1 ret=1
@ -340,8 +343,8 @@ edit_keys() {
} }
export_keys() { export_keys() {
check_keyids_exist check_keyids_exist "$@"
if ! "${GPG_PACMAN[@]}" --armor --export "${KEYIDS[@]}" ; then if ! "${GPG_PACMAN[@]}" --armor --export "$@" ; then
error "$(gettext "A specified key could not be exported from the gpg keychain.")" error "$(gettext "A specified key could not be exported from the gpg keychain.")"
exit 1 exit 1
fi fi
@ -349,7 +352,7 @@ export_keys() {
finger_keys() { finger_keys() {
check_keyids_exist 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.")" error "$(gettext "The fingerprint of a specified key could not be determined.")"
exit 1 exit 1
fi fi
@ -358,7 +361,7 @@ finger_keys() {
import_trustdb() { import_trustdb() {
local importdir local importdir
local ret=0 local ret=0
for importdir in "${IMPORT_DIRS[@]}"; do for importdir in "$@"; do
if [[ -f "${importdir}/trustdb.gpg" ]]; then if [[ -f "${importdir}/trustdb.gpg" ]]; then
gpg --homedir "${importdir}" --export-ownertrust | \ gpg --homedir "${importdir}" --export-ownertrust | \
"${GPG_PACMAN[@]}" --import-ownertrust - "${GPG_PACMAN[@]}" --import-ownertrust -
@ -379,7 +382,7 @@ import_trustdb() {
import() { import() {
local importdir local importdir
local ret=0 local ret=0
for importdir in "${IMPORT_DIRS[@]}"; do for importdir in "$@"; do
if [[ -f "${importdir}/pubring.gpg" ]]; then if [[ -f "${importdir}/pubring.gpg" ]]; then
if ! "${GPG_PACMAN[@]}" --quiet --batch --import "${importdir}/pubring.gpg" ; then if ! "${GPG_PACMAN[@]}" --quiet --batch --import "${importdir}/pubring.gpg" ; then
error "$(gettext "%s could not be imported.")" "${importdir}/pubring.gpg" error "$(gettext "%s could not be imported.")" "${importdir}/pubring.gpg"
@ -397,7 +400,7 @@ import() {
list_keys() { list_keys() {
check_keyids_exist 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.")" error "$(gettext "A specified key could not be listed.")"
exit 1 exit 1
fi fi
@ -405,7 +408,7 @@ list_keys() {
list_sigs() { list_sigs() {
check_keyids_exist 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.")" error "$(gettext "A specified signature could not be listed.")"
exit 1 exit 1
fi fi
@ -413,7 +416,7 @@ list_sigs() {
lsign_keys() { lsign_keys() {
check_keyids_exist 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 if (( PIPESTATUS[1] )); then
error "$(gettext "A specified key could not be locally signed.")" error "$(gettext "A specified key could not be locally signed.")"
exit 1 exit 1
@ -421,25 +424,30 @@ lsign_keys() {
} }
receive_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.")" error "$(gettext "Remote key not fetched correctly from keyserver.")"
exit 1 exit 1
fi fi
} }
refresh_keys() { refresh_keys() {
check_keyids_exist check_keyids_exist "$@"
if ! "${GPG_PACMAN[@]}" --refresh-keys "${KEYIDS[@]}" ; then if ! "${GPG_PACMAN[@]}" --refresh-keys "$@" ; then
error "$(gettext "A specified local key could not be updated from a keyserver.")" error "$(gettext "A specified local key could not be updated from a keyserver.")"
exit 1 exit 1
fi fi
} }
verify_sig() { verify_sig() {
if ! "${GPG_PACMAN[@]}" --status-fd 1 --verify $SIGNATURE | grep -qE 'TRUST_(FULLY|ULTIMATE)'; then local ret=0
error "$(gettext "The signature identified by %s could not be verified.")" "$SIGNATURE" for sig; do
exit 1 msg "Checking %s ..." "$sig"
fi 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() { updatedb() {
@ -457,56 +465,55 @@ if ! type gettext &>/dev/null; then
} }
fi fi
OPT_SHORT="a::d:e::f::hl::r:uv:V" OPT_SHORT="adefhlruvV"
OPT_LONG="add::,config:,delete:,edit-key:,export::,finger::,gpgdir:" OPT_LONG=('add' 'config:' 'delete' 'edit-key' 'export' 'finger' 'gpgdir:'
OPT_LONG+=",help,import:,import-trustdb:,init,keyserver:,list-keys::,list-sigs::" 'help' 'import' 'import-trustdb' 'init' 'keyserver:' 'list-keys' 'list-sigs'
OPT_LONG+=",lsign-key:,populate::,recv-keys:,refresh-keys::,updatedb" 'lsign-key' 'populate' 'recv-keys' 'refresh-keys' 'updatedb'
OPT_LONG+=",verify:,version" 'verify' 'version')
if ! OPT_TEMP="$(parse_options $OPT_SHORT $OPT_LONG "$@")"; then if ! parseopts "$OPT_SHORT" "${OPT_LONG[@]}" -- "$@"; then
echo; usage; exit 1 # E_INVALID_OPTION; exit 1 # E_INVALID_OPTION;
fi fi
eval set -- "$OPT_TEMP" set -- "${OPTRET[@]}"
unset OPT_SHORT OPT_LONG OPT_TEMP unset OPT_SHORT OPT_LONG OPTRET
if [[ $1 == "--" ]]; then if [[ $1 == "--" ]]; then
usage; usage;
exit 0; exit 0;
fi fi
while true; do while (( $# )); do
case "$1" in case $1 in
-a|--add) ADD=1; [[ -n $2 && ${2:0:1} != "-" ]] && shift && KEYFILES=($1); UPDATEDB=1 ;; -a|--add) ADD=1 UPDATEDB=1 ;;
--config) shift; CONFIG=$1 ;; --config) shift; CONFIG=$1 ;;
-d|--delete) DELETE=1; shift; KEYIDS=($1); UPDATEDB=1 ;; -d|--delete) DELETE=1 UPDATEDB=1 ;;
--edit-key) EDITKEY=1; shift; KEYIDS=($1); UPDATEDB=1 ;; --edit-key) EDITKEY=1 UPDATEDB=1 ;;
-e|--export) EXPORT=1; [[ -n $2 && ${2:0:1} != "-" ]] && shift && KEYIDS=($1) ;; -e|--export) EXPORT=1 ;;
-f|--finger) FINGER=1; [[ -n $2 && ${2:0:1} != "-" ]] && shift && KEYIDS=($1) ;; -f|--finger) FINGER=1 ;;
--gpgdir) shift; PACMAN_KEYRING_DIR=$1 ;; --gpgdir) shift; PACMAN_KEYRING_DIR=$1 ;;
--import) IMPORT=1; shift; IMPORT_DIRS=($1); UPDATEDB=1 ;; --import) IMPORT=1 UPDATEDB=1 ;;
--import-trustdb) IMPORT_TRUSTDB=1; shift; IMPORT_DIRS=($1); UPDATEDB=1 ;; --import-trustdb) IMPORT_TRUSTDB=1 UPDATEDB=1 ;;
--init) INIT=1 ;; --init) INIT=1 ;;
--keyserver) shift; KEYSERVER=$1 ;; --keyserver) shift; KEYSERVER=$1 ;;
-l|--list-keys) LISTKEYS=1; [[ -n $2 && ${2:0:1} != "-" ]] && shift && KEYIDS=($1) ;; -l|--list-keys) LISTKEYS=1 ;;
--list-sigs) LISTSIGS=1; [[ -n $2 && ${2:0:1} != "-" ]] && shift && KEYIDS=($1) ;; --list-sigs) LISTSIGS=1 ;;
--lsign-key) LSIGNKEY=1; shift; KEYIDS=($1); UPDATEDB=1 ;; --lsign-key) LSIGNKEY=1 UPDATEDB=1 ;;
--populate) POPULATE=1; [[ -n $2 && ${2:0:1} != "-" ]] && shift && KEYRINGIDS=($1); UPDATEDB=1 ;; --populate) POPULATE=1 UPDATEDB=1 ;;
-r|--recv-keys) RECEIVE=1; shift; KEYIDS=($1); UPDATEDB=1 ;; -r|--recv-keys) RECEIVE=1 UPDATEDB=1 ;;
--refresh-keys) REFRESH=1; [[ -n $2 && ${2:0:1} != "-" ]] && shift && KEYIDS=($1) ;; --refresh-keys) REFRESH=1 ;;
-u|--updatedb) UPDATEDB=1 ;; -u|--updatedb) UPDATEDB=1 ;;
-v|--verify) VERIFY=1; shift; SIGNATURE=$1 ;; -v|--verify) VERIFY=1 ;;
-h|--help) usage; exit 0 ;; -h|--help) usage; exit 0 ;;
-V|--version) version; exit 0 ;; -V|--version) version; exit 0 ;;
--) OPT_IND=0; shift; break;; --) shift; break 2 ;;
*) usage; exit 1 ;;
esac esac
shift shift
done done
if ! type -p gpg >/dev/null; then 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 exit 1
fi fi
@ -549,23 +556,30 @@ case $numopt in
;; ;;
esac 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 (( ! INIT )) && check_keyring
(( ADD )) && add_keys (( ADD )) && add_keys "$@"
(( DELETE )) && delete_keys (( DELETE )) && delete_keys "$@"
(( EDITKEY )) && edit_keys (( EDITKEY )) && edit_keys "$@"
(( EXPORT )) && export_keys (( EXPORT )) && export_keys "$@"
(( FINGER )) && finger_keys (( FINGER )) && finger_keys "$@"
(( IMPORT )) && import (( IMPORT )) && import "$@"
(( IMPORT_TRUSTDB)) && import_trustdb (( IMPORT_TRUSTDB)) && import_trustdb "$@"
(( INIT )) && initialize (( INIT )) && initialize
(( LISTKEYS )) && list_keys (( LISTKEYS )) && list_keys "$@"
(( LISTSIGS )) && list_sigs (( LISTSIGS )) && list_sigs "$@"
(( LSIGNKEY )) && lsign_keys (( LSIGNKEY )) && lsign_keys "$@"
(( POPULATE )) && populate_keyring (( POPULATE )) && populate_keyring "$@"
(( RECEIVE )) && receive_keys (( RECEIVE )) && receive_keys "$@"
(( REFRESH )) && refresh_keys (( REFRESH )) && refresh_keys "$@"
(( VERIFY )) && verify_sig (( VERIFY )) && verify_sig "$@"
(( UPDATEDB )) && updatedb (( UPDATEDB )) && updatedb

View file

@ -8,4 +8,4 @@ scripts/pacman-optimize.sh.in
scripts/pkgdelta.sh.in scripts/pkgdelta.sh.in
scripts/repo-add.sh.in scripts/repo-add.sh.in
scripts/library/output_format.sh scripts/library/output_format.sh
scripts/library/parse_options.sh scripts/library/parseopts.sh

9
test/scripts/Makefile.am Normal file
View file

@ -0,0 +1,9 @@
check_SCRIPTS = \
parseopts_test.sh
noinst_SCRIPTS = $(check_SCRIPTS)
EXTRA_DIST = \
$(check_SCRIPTS)
# vim:set ts=2 sw=2 noet:

138
test/scripts/parseopts_test.sh Executable file
View file

@ -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