aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/cpt.org15
-rw-r--r--src/cpt-lib.in287
-rwxr-xr-xsrc/cpt-search1
3 files changed, 173 insertions, 130 deletions
diff --git a/docs/cpt.org b/docs/cpt.org
index dfddc38..e3c1d9b 100644
--- a/docs/cpt.org
+++ b/docs/cpt.org
@@ -1056,7 +1056,20 @@ if it fails.
:DESCRIPTION: Query package locations
:END:
-=pkg_find()=
+=pkg_find()= is the tool for searching packages. It accepts up to 3 arguments.
+
+- $1: Query :: This is the only mandatory argument. It accepts globbing, meaning
+ that shell wildcards can be used in the query.
+- $2: Match :: If this exists =pkg_find()= will print every single match found
+ in the search path. If it doesn't, =pkg_find()= will print the first match and
+ exit.
+- $3: Type :: This is the argument to be passed to the =test= function. Unless
+ this argument is given, it defaults to =-d=, which tests for directories.
+
+#+begin_src sh
+pkg_find cpt
+#+end_src
+
*** TODO =pkg_gentree=
:PROPERTIES:
diff --git a/src/cpt-lib.in b/src/cpt-lib.in
index 9aa232c..8c7989e 100644
--- a/src/cpt-lib.in
+++ b/src/cpt-lib.in
@@ -113,17 +113,15 @@ _readlinkf() (
# This is the public domain getoptions shell library. It also forms a usage
# function.
-# URL: https://github.com/ko1nksm/getoptions (v2.0.1)
+# URL: https://github.com/ko1nksm/getoptions (v2.5.0)
# License: Creative Commons Zero v1.0 Universal
# shellcheck disable=2016
getoptions() {
_error='' _on=1 _off='' _export='' _plus='' _mode='' _alt='' _rest=''
- _opts='' _help='' _indent='' _init=@empty IFS=' '
+ _flags='' _nflags='' _opts='' _help='' _abbr='' _cmds='' _init=@empty IFS=' '
- for i in 0 1 2 3 4 5; do
- eval "_$i() { echo \"$_indent\$@\"; }"
- _indent="$_indent "
- done
+ _0() { echo "$@"; }
+ for i in 1 2 3 4 5; do eval "_$i() { _$((${i-}-1)) \" \$@\"; }"; done
quote() {
q="$2'" r=''
@@ -135,6 +133,8 @@ getoptions() {
[ "${1#:}" = "$1" ] && c=3 || c=4
eval "[ ! \${$c:+x} ] || $2 \"\$$c\""
}
+ kv() { eval "${2-}${1%%:*}=\${1#*:}"; }
+ loop() { [ $# -gt 1 ] && [ "$2" != -- ]; }
invoke() { eval '"_$@"'; }
prehook() { invoke "$@"; }
@@ -143,12 +143,12 @@ getoptions() {
done
args() {
- on=$_on off=$_off export=$_export init=$_init _hasarg=$1
- while [ $# -gt 2 ] && [ "$3" != '--' ] && shift; do
- case $2 in
- -?) [ "$_hasarg" ] || _opts="$_opts${2#-}" ;;
- +*) _plus=1 ;;
- [!-+]*) eval "${2%%:*}=\${2#*:}"
+ on=$_on off=$_off export=$_export init=$_init _hasarg=$1 && shift
+ while loop "$@" && shift; do
+ case $1 in
+ -?) [ "$_hasarg" ] && _opts="$_opts${1#-}" || _flags="$_flags${1#-}" ;;
+ +?) _plus=1 _nflags="$_nflags${1#+}" ;;
+ [!-+]*) kv "$1"
esac
done
}
@@ -166,35 +166,43 @@ getoptions() {
esac
}
_setup() {
- [ $# -gt 0 ] && { [ "$1" ] && _rest=$1; shift; }
- for i; do [ "$i" = '--' ] && break; eval "_${i%%:*}=\${i#*:}"; done
+ [ "${1#-}" ] && _rest=$1
+ while loop "$@" && shift; do kv "$1" _; done
}
- _flag() { args : "$@"; defvar "$@"; }
- _param() { args '' "$@"; defvar "$@"; }
- _option() { args '' "$@"; defvar "$@"; }
- _disp() { args : "$@"; }
- _msg() { args : _ "$@"; }
+ _flag() { args '' "$@"; defvar "$@"; }
+ _param() { args 1 "$@"; defvar "$@"; }
+ _option() { args 1 "$@"; defvar "$@"; }
+ _disp() { args '' "$@"; }
+ _msg() { args '' _ "$@"; }
+ cmd() { _mode=@ _cmds="$_cmds${_cmds:+|}'$1'"; }
"$@"
+ cmd() { :; }
_0 "${_rest:?}=''"
+ _0 "$2() {"
+ _1 'OPTIND=$(($#+1))'
+ _1 'while OPTARG= && [ $# -gt 0 ]; do'
+ [ "$_abbr" ] && getoptions_abbr "$@"
+
args() {
sw='' validate='' pattern='' counter='' on=$_on off=$_off export=$_export
- while [ $# -gt 1 ] && [ "$2" != '--' ] && shift; do
+ while loop "$@" && shift; do
case $1 in
- --\{no-\}*) sw="$sw${sw:+ | }--${1#--?no-?} | --no-${1#--?no-?}" ;;
- [-+]? | --*) sw="$sw${sw:+ | }$1" ;;
- *) eval "${1%%:*}=\"\${1#*:}\""
+ --\{no-\}*) i=${1#--?no-?}; sw="$sw${sw:+|}'--$i'|'--no-$i'" ;;
+ [-+]? | --*) sw="$sw${sw:+|}'$1'" ;;
+ *) kv "$1"
esac
done
+ quote on "$on"
+ quote off "$off"
}
setup() { :; }
_flag() {
args "$@"
- quote on "$on" && quote off "$off"
- [ "$counter" ] && on=1 off=-1 v="\$((\${$1:-0}+\${OPTARG:-0}))" || v=''
+ [ "$counter" ] && on=1 off=-1 v="\$((\${$1:-0}+\$OPTARG))" || v=''
_3 "$sw)"
- _4 '[ "${OPTARG:-}" ] && OPTARG=${OPTARG#*\=} && set -- noarg "$1" && break'
+ _4 '[ "${OPTARG:-}" ] && OPTARG=${OPTARG#*\=} && set "noarg" "$1" && break'
_4 "eval '[ \${OPTARG+x} ] &&:' && OPTARG=$on || OPTARG=$off"
valid "$1" "${v:-\$OPTARG}"
_4 ';;'
@@ -202,18 +210,17 @@ getoptions() {
_param() {
args "$@"
_3 "$sw)"
- _4 '[ $# -le 1 ] && set -- required "$1" && break'
+ _4 '[ $# -le 1 ] && set "required" "$1" && break'
_4 'OPTARG=$2'
valid "$1" '$OPTARG'
_4 'shift ;;'
}
_option() {
args "$@"
- quote on "$on" && quote off "$off"
_3 "$sw)"
_4 'set -- "$1" "$@"'
_4 '[ ${OPTARG+x} ] && {'
- _5 'case $1 in --no-*) set -- noarg "${1%%\=*}"; break; esac'
+ _5 'case $1 in --no-*) set "noarg" "${1%%\=*}"; break; esac'
_5 '[ "${OPTARG:-}" ] && { shift; OPTARG=$2; } ||' "OPTARG=$on"
_4 "} || OPTARG=$off"
valid "$1" '$OPTARG'
@@ -223,9 +230,8 @@ getoptions() {
set -- "$validate" "$pattern" "$1" "$2"
[ "$1" ] && _4 "$1 || { set -- ${1%% *}:\$? \"\$1\" $1; break; }"
[ "$2" ] && {
- quote pattern "$2"
_4 "case \$OPTARG in $2) ;;"
- _5 "*) set -- pattern:$pattern \"\$1\"; break"
+ _5 '*) set "pattern:'"$2"'" "$1"; break'
_4 "esac"
}
code "$3" _4 "${export:+export }$3=\"$4\"" "${3#:}"
@@ -238,102 +244,106 @@ getoptions() {
}
_msg() { :; }
- _0 "$2() {"
- _1 'OPTIND=$(($#+1))'
- _1 'while OPTARG= && [ $# -gt 0 ]; do'
[ "$_alt" ] && _2 'case $1 in -[!-]?*) set -- "-$@"; esac'
_2 'case $1 in'
- wa() { _4 "eval '${1% *}' \${1+'\"\$@\"'}"; }
+ _wa() { _4 "eval 'set -- $1' \${1+'\"\$@\"'}"; }
+ _op() {
+ _3 "$1) OPTARG=\$1; shift"
+ _wa '"${OPTARG%"${OPTARG#??}"}" '"$2"'"${OPTARG#??}"'
+ _4 "$3"
+ }
_3 '--?*=*) OPTARG=$1; shift'
- wa 'set -- "${OPTARG%%\=*}" "${OPTARG#*\=}" "$@"'
+ _wa '"${OPTARG%%\=*}" "${OPTARG#*\=}"'
_4 ';;'
_3 '--no-*) unset OPTARG ;;'
[ "$_alt" ] || {
- [ "$_opts" ] && {
- _3 "-[$_opts]?*) OPTARG=\$1; shift"
- wa 'set -- "${OPTARG%"${OPTARG#??}"}" "${OPTARG#??}" "$@"'
- _4 ';;'
- }
- _3 '-[!-]?*) OPTARG=$1; shift'
- wa 'set -- "${OPTARG%"${OPTARG#??}"}" "-${OPTARG#??}" "$@"'
- _4 'OPTARG= ;;'
+ [ "$_opts" ] && _op "-[$_opts]?*" '' ';;'
+ [ ! "$_flags" ] || _op "-[$_flags]?*" - 'OPTARG= ;;'
}
[ "$_plus" ] && {
- _3 '+??*) OPTARG=$1; shift'
- wa 'set -- "${OPTARG%"${OPTARG#??}"}" "+${OPTARG#??}" "$@"'
- _4 'unset OPTARG ;;'
+ [ "$_nflags" ] && _op "+[$_nflags]?*" + 'unset OPTARG ;;'
_3 '+*) unset OPTARG ;;'
}
_2 'esac'
_2 'case $1 in'
"$@"
rest() {
- _3 "$1"
_4 'while [ $# -gt 0 ]; do'
- _5 "$_rest=\"\${$_rest}" '\"\${$((${OPTIND:-0}-$#))}\""'
+ _5 "$_rest=\"\${$_rest}" '\"\${$(($OPTIND-$#))}\""'
_5 'shift'
_4 'done'
_4 'break ;;'
}
- rest '--) shift'
- _3 "[-${_plus:++}]?*)" 'set -- unknown "$1" && break ;;'
+ _3 '--)'
+ [ "$_mode" = @ ] || _4 'shift'
+ rest
+ _3 "[-${_plus:++}]?*)"
+ case $_mode in [=#]) rest ;; *) _4 'set "unknown" "$1"; break ;;'; esac
+ _3 '*)'
case $_mode in
- +) rest '*)' ;;
- *) _3 "*) $_rest=\"\${$_rest}" '\"\${$((${OPTIND:-0}-$#))}\""'
+ @)
+ _4 "case \$1 in ${_cmds:-*}) ;;"
+ _5 '*) set "notcmd" "$1"; break'
+ _4 'esac'
+ rest ;;
+ [+#]) rest ;;
+ *) _4 "$_rest=\"\${$_rest}" '\"\${$(($OPTIND-$#))}\""'
esac
_2 'esac'
_2 'shift'
_1 'done'
_1 '[ $# -eq 0 ] && { OPTIND=1; unset OPTARG; return 0; }'
_1 'case $1 in'
- _2 'unknown) set -- "Unrecognized option: $2" "$@" ;;'
- _2 'noarg) set -- "Does not allow an argument: $2" "$@" ;;'
- _2 'required) set -- "Requires an argument: $2" "$@" ;;'
- _2 'pattern:*) set -- "Does not match the pattern (${1#*:}): $2" "$@" ;;'
- _2 '*) set -- "Validation error ($1): $2" "$@"'
+ _2 'unknown) set "Unrecognized option: $2" "$@" ;;'
+ _2 'noarg) set "Does not allow an argument: $2" "$@" ;;'
+ _2 'required) set "Requires an argument: $2" "$@" ;;'
+ _2 'pattern:*) set "Does not match the pattern (${1#*:}): $2" "$@" ;;'
+ _2 'notcmd) set "Not a command: $2" "$@" ;;'
+ _2 '*) set "Validation error ($1): $2" "$@"'
_1 'esac'
[ "$_error" ] && _1 "$_error" '"$@" >&2 || exit $?'
_1 'echo "$1" >&2'
_1 'exit 1'
_0 '}'
- # This throws an error on older versions of shellcheck.
- # shellcheck disable=2086
[ ! "$_help" ] || eval "shift 2; getoptions_help $1 $_help" ${3+'"$@"'}
}
-# URL: https://github.com/ko1nksm/getoptions (v2.0.1)
+# URL: https://github.com/ko1nksm/getoptions (v2.5.0)
# License: Creative Commons Zero v1.0 Universal
getoptions_help() {
- width=30 plus='' leading=' '
+ _width='30,12' _plus='' _leading=' '
pad() { p=$2; while [ ${#p} -lt "$3" ]; do p="$p "; done; eval "$1=\$p"; }
+ kv() { eval "${2-}${1%%:*}=\${1#*:}"; }
+ sw() { pad sw "$sw${sw:+, }" "$1"; sw="$sw$2"; }
args() {
- _type=$1 var=${2%% *} sw='' label='' hidden='' _width=$width && shift 2
- while [ $# -gt 0 ] && i=$1 && shift && [ ! "$i" = '--' ]; do
+ _type=$1 var=${2%% *} sw='' label='' hidden='' && shift 2
+ while [ $# -gt 0 ] && i=$1 && shift && [ "$i" != -- ]; do
case $i in
- --*) pad sw "$sw${sw:+, }" $((${plus:+4}+4)); sw="$sw$i" ;;
- -?) sw="$sw${sw:+, }$i" ;;
- +?) [ ! "$plus" ] || { pad sw "$sw${sw:+, }" 4; sw="$sw$i"; } ;;
- *) eval "${i%%:*}=\${i#*:}"
+ --*) sw $((${_plus:+4}+4)) "$i" ;;
+ -?) sw 0 "$i" ;;
+ +?) [ ! "$_plus" ] || sw 4 "$i" ;;
+ *) [ "$_type" = setup ] && kv "$i" _; kv "$i"
esac
done
- [ "$hidden" ] && return 0
+ [ "$hidden" ] && return 0 || len=${_width%,*}
[ "$label" ] || case $_type in
- setup | msg) label='' _width=0 ;;
+ setup | msg) label='' len=0 ;;
flag | disp) label="$sw " ;;
param) label="$sw $var " ;;
option) label="${sw}[=$var] "
esac
- pad label "${label:+$leading}$label" "$_width"
- [ ${#label} -le "$_width" ] && [ $# -gt 0 ] && label="$label$1" && shift
+ [ "$_type" = cmd ] && label=${label:-$var } len=${_width#*,}
+ pad label "${label:+$_leading}$label" "$len"
+ [ ${#label} -le "$len" ] && [ $# -gt 0 ] && label="$label$1" && shift
echo "$label"
- pad label '' "$_width"
+ pad label '' "$len"
for i; do echo "$label$i"; done
}
- for i in 'setup :' flag param option disp 'msg :'; do
+ for i in setup flag param option disp 'msg -' cmd; do
eval "${i% *}() { args $i \"\$@\"; }"
done
@@ -469,6 +479,69 @@ sh256() {
while read -r hash _; do printf '%s %s\n' "$hash" "$1"; done
}
+tar_extract() {
+ # Tarball extraction function that prefers pax(1) over tar(1). The reason we
+ # are preferring pax is that we can strip components without relying on
+ # ugly hacks such as the ones we are doing for 'tar'. Using 'tar' means that
+ # we either have to sacrifice speed or portability, and we are choosing to
+ # sacrifice speed. Fortunately, we don't have to make such a choice when
+ # using pax.
+ case "${extract##*/}" in
+ pax) decompress "$1" | pax -r -s '/[^\/]*\///' ;;
+ gtar|bsdtar) decompress "$1" | "$tar" xf - --strip-components 1 ;;
+ tar) decompress "$1" > .ktar
+
+ "$tar" xf .ktar || return
+
+ # We now list the contents of the tarball so we can do our
+ # version of 'strip-components'.
+ "$tar" tf .ktar |
+ while read -r file; do printf '%s\n' "${file%%/*}"; done |
+
+ # Do not repeat files.
+ uniq |
+
+ # For every directory in the base we move each file
+ # inside it to the upper directory.
+ while read -r dir ; do
+
+ # Skip if we are not dealing with a directory here.
+ # This way we don't remove files on the upper directory
+ # if a tar archive doesn't need directory stripping.
+ [ -d "${dir#.}" ] || continue
+
+ # Change into the directory in a subshell so we don't
+ # need to cd back to the upper directory.
+ (
+ cd "$dir"
+
+ # We use find because we want to move hidden files
+ # as well.
+ #
+ # Skip the file if it has the same name as the directory.
+ # We will deal with it later.
+ #
+ # Word splitting is intentional here.
+ # shellcheck disable=2046
+ find . \( ! -name . -prune \) ! -name "$dir" \
+ -exec mv -f {} .. \;
+
+ # If a file/directory with the same name as the directory
+ # exists, append a '.cptbak' to it and move it to the
+ # upper directory.
+ ! [ -e "$dir" ] || mv "$dir" "../${dir}.cptbak"
+ )
+ rmdir "$dir"
+
+ # If a backup file exists, move it into the original location.
+ ! [ -e "${dir}.cptbak" ] || mv "${dir}.cptbak" "$dir"
+ done
+
+ # Clean up the temporary tarball.
+ rm -f .ktar
+ esac
+}
+
pkg_owner() {
set +f
@@ -529,6 +602,9 @@ pkg_find() {
for path2 in "$path/"$query; do
test "${type:--d}" "$path2" && set -f -- "$@" "$path2"
done
+ # Break early if we only want a single match for a slight increase
+ # in speed. We don't need to search for the entire path.
+ [ "$1" ] && [ -z "$match" ] && break
done
IFS=$old_ifs
@@ -684,63 +760,10 @@ pkg_extract() {
# extraction. Other filetypes are simply copied to '$mak_dir'
# which allows for manual extraction.
*://*.tar|*://*.tar.??|*://*.tar.???|*://*.tar.????|*://*.tgz|*://*.txz)
-
- decompress "$src_dir/$1/${src##*/}" > .ktar
-
- "$tar" xf .ktar || die "$1" "Couldn't extract ${src##*/}"
-
- # We now list the contents of the tarball so we can do our
- # version of 'strip-components'.
- "$tar" tf .ktar |
- while read -r file; do printf '%s\n' "${file%%/*}"; done |
-
- # Do not repeat files.
- uniq |
-
- # For every directory in the base we move each file
- # inside it to the upper directory.
- while read -r dir ; do
-
- # Skip if we are not dealing with a directory here.
- # This way we don't remove files on the upper directory
- # if a tar archive doesn't need directory stripping.
- [ -d "${dir#.}" ] || continue
-
- # Change into the directory in a subshell so we don't
- # need to cd back to the upper directory.
- (
- cd "$dir"
-
- # We use find because we want to move hidden files
- # as well.
- #
- # Skip the file if it has the same name as the directory.
- # We will deal with it later.
- #
- # Word splitting is intentional here.
- # shellcheck disable=2046
- find . \( ! -name . -prune \) ! -name "$dir" \
- -exec mv -f {} .. \;
-
- # If a file/directory with the same name as the directory
- # exists, append a '.cptbak' to it and move it to the
- # upper directory.
- ! [ -e "$dir" ] || mv "$dir" "../${dir}.cptbak"
- )
- rmdir "$dir"
-
- # If a backup file exists, move it into the original location.
- ! [ -e "${dir}.cptbak" ] || mv "${dir}.cptbak" "$dir"
- done
-
- # Clean up the temporary tarball.
- rm -f .ktar
- ;;
+ tar_extract "$src_dir/$1/${src##*/}" ;;
*://*.cpio|*://*.cpio.??|*://*.cpio.???|*://*.cpio.????)
- decompress "$src_dir/$1/${src##*/}" | cpio -i
-
- ;;
+ decompress "$src_dir/$1/${src##*/}" | pax -r ;;
*://*.zip)
unzip "$src_dir/$1/${src##*/}" ||
@@ -1948,6 +1971,12 @@ create_cache() {
# you value performance.
tar=$(command -v bsdtar || command -v gtar) || tar=tar
+ # Prefer libarchive tar, GNU tar, or the POSIX defined pax for tarball
+ # extraction, as they can strip components, which is much much faster than
+ # our portability function. Our first preference is pax, because it is
+ # actually slightly faster than bsdtar and GNU tar.
+ extract=$(command -v pax || command -v "$tar")
+
# Figure out which 'sudo' command to use based on the user's choice or
# what is available on the system.
su=${CPT_SU:-$(command -v sls ||
diff --git a/src/cpt-search b/src/cpt-search
index df27761..fbe0b04 100755
--- a/src/cpt-search
+++ b/src/cpt-search
@@ -4,6 +4,7 @@
parser_definition() {
setup REST help:usage -- "usage: ${0##*/} [pkg...]"
msg -- '' 'Options:'
+ flag SEARCH_PATH -d "on:$CPT_PATH" -- "Do not search the installed package database"
flag all -s --single init:=1 on:'' -- "Only show the first instance of a package"
flag others -o --others -- "Use the current directory as the package" \
"and show other instances"