You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

pkgsearch 4.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. #!/usr/bin/env bash
  2. declare -r esc=$'\033'
  3. declare -r c_reset="${esc}[0m"
  4. declare -r c_red="${esc}[31m"
  5. declare -r c_green="${esc}[32m"
  6. declare -r c_blue="${esc}[34m"
  7. declare -a config_err
  8. declare -r config_dir="${XDG_CONFIG_DIR:-$HOME/.config}/pkgsearch"
  9. declare -r config_file="${config_dir}/conf"
  10. declare reverse
  11. declare preview_pos='right:hidden'
  12. usage() {
  13. LESS=-FEXR less <<HELP
  14. pkgsearch [options] [query]
  15. lists and installs packages from your distro's repositories
  16. -r --reverse reverse the orientation of the layout
  17. -p --preview options for the preview window
  18. is passed directly to fzf --preview-window
  19. defaults to 'right:hidden'
  20. -h --help show this help
  21. without any arguments pkgsearch will list all available packages from your cache
  22. note: on Arch Linux you must pass a string to query the AUR
  23. HELP
  24. }
  25. err() {
  26. printf "${c_red}%s${c_reset}\n" "$*" >&2
  27. }
  28. die() {
  29. exit 1
  30. }
  31. has() {
  32. local verbose=0
  33. if [[ $1 = '-v' ]]; then
  34. verbose=1
  35. shift
  36. fi
  37. for c; do c="${c%% *}"
  38. if ! command -v "$c" &> /dev/null; then
  39. (( "$verbose" > 0 )) && err "$c not found"
  40. return 1
  41. fi
  42. done
  43. }
  44. select_from() {
  45. local cmd='command -v'
  46. for a; do
  47. case "$a" in
  48. -c)
  49. cmd="$2"
  50. shift 2
  51. ;;
  52. esac
  53. done
  54. for c; do
  55. if $cmd "${c%% *}" &> /dev/null; then
  56. echo "$c"
  57. return 0
  58. fi
  59. done
  60. return 1
  61. }
  62. fzf() {
  63. command fzf -e +s --multi --cycle --ansi \
  64. ${reverse:+--reverse} \
  65. --bind='Ctrl-X:toggle-preview' \
  66. --preview-window="$preview_pos" \
  67. --no-hscroll --inline-info \
  68. --header='tab to select multiple packages, Ctrl-X for more info on a package' "$@"
  69. }
  70. parse_config_file() {
  71. local line k v nr=0
  72. config_err=()
  73. while IFS= read -r line; do
  74. (( ++nr ))
  75. [[ -z "$line" || "$line" = '#'* ]] && continue
  76. k="${line%% *}"
  77. v="${line#* }"
  78. if [[ -z "$v" ]]; then
  79. config_err+=( "missing value for \"$k\" in config file on line $nr" )
  80. continue
  81. fi
  82. case "$k" in
  83. reverse) [[ "$v" = 'true' ]] && reverse="1" ;;
  84. preview) preview_pos="$v" ;;
  85. *) config_err+=( "unknown key \"$k\" in config file on line $nr" )
  86. esac
  87. done
  88. }
  89. install() {
  90. local pkgs count
  91. mapfile -t pkgs
  92. (( ${#pkgs} > 0 )) || exit
  93. count="${#pkgs[@]} package"
  94. (( ${#pkgs[@]} > 1 )) && count+='s'
  95. printf "installing %s: %s\n" "$count" "${pkgs[*]}"
  96. $1 "${pkgs[@]}" < /dev/tty
  97. }
  98. debian() {
  99. fzf --preview='p={}; apt-cache show ${p%% *}' \
  100. --query="$1" \
  101. < <(apt-cache search '' | sort |
  102. sed -u -r "s|^([^ ]+)|${c_green}\1${c_reset}|") |
  103. cut -d' ' -f1 |
  104. install "sudo $(select_from 'apt' 'aptitude' 'apt-get') install"
  105. }
  106. arch() {
  107. local search packages
  108. search='pacman'
  109. [[ -n "$1" ]] && search=$(select_from 'pacaur' 'yaourt' 'packer' 'apacman' 'pacman')
  110. packages=$(fzf --preview="p=\$(cut -d' ' -f2 <<< {}); $search -Si \$p" \
  111. < <( $search -Ss "$1" |
  112. gawk '{
  113. getline descr;
  114. sub(/ */,"", descr);
  115. repo = blue "[" gensub(/\/.*/, "", 1) "]" reset;
  116. name = green gensub(/.*\//, "", 1, $1) reset;
  117. info = gensub(/[^ ]* /, "", 1);
  118. print repo, name, info, descr;
  119. }' blue="$c_blue" green="$c_green" reset="$c_reset"
  120. ) | cut -d' ' -f2)
  121. [[ "$search" = "pacman" ]] && search="sudo pacman"
  122. install "$search -S" <<< "$packages"
  123. }
  124. void() {
  125. local package_list packagename='{ sub(/-[^\-]*$/, "", $2); print $2 }'
  126. package_list=$(xbps-query -Rs '' | sort)
  127. fzf --preview="p=\$(awk \"$packagename\" <<< {}); xbps-query -Rx \$p" \
  128. --query="$1" <<< "$package_list" |
  129. awk "$packagename" |
  130. install 'xbps-install -S'
  131. }
  132. fedora() {
  133. fzf --preview='p={}; p="${p%% *}"; dnf -q info "${p%.*}"' \
  134. < <(dnf -qC repoquery --qf "${c_green}%{name} ${c_reset}%{version} - %{summary}" \*) |
  135. awk '{ package=$1; sub(/\.\S+/, "", package); print package }' |
  136. install 'sudo dnf install'
  137. }
  138. [[ -s "$config_file" ]] && parse_config_file < "$config_file"
  139. while true; do
  140. case "$1" in
  141. -r|--reverse)
  142. reverse=1
  143. shift
  144. ;;
  145. -h|--help)
  146. usage
  147. exit
  148. ;;
  149. -p|--preview)
  150. preview_pos="$2"
  151. shift 2
  152. ;;
  153. *) break
  154. esac
  155. done
  156. has -v fzf gawk || die
  157. request="$*"
  158. osrelease=$(awk -F'=' '"NAME" == $1 { gsub("\"", "", $2); print tolower($2); }' /etc/os-release)
  159. case "${osrelease%% *}" in
  160. debian|ubuntu) debian "$request" ;;
  161. arch) arch "$request" ;;
  162. void) void "$request" ;;
  163. fedora) fedora "$request" ;;
  164. *) die 'unknown distro :(' ;;
  165. esac