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