diff options
-rw-r--r-- | docs/cpt.org | 15 | ||||
-rw-r--r-- | src/cpt-lib.in | 287 | ||||
-rwxr-xr-x | src/cpt-search | 1 |
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" |