├── .github └── FUNDING.yml ├── CONTRIBUTING.md ├── LICENSE ├── Makefile ├── README.md └── cpm /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: ['willeccles'] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: ['https://warchild.org', 'https://twloha.com'] 13 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to cpm 2 | 3 | To add your package manager to cpm, do the following: 4 | 5 | 1. Make sure your code is formatted like the rest of cpm. If you are using vim 6 | (or another editor which respects a vim modeline), there is a modeline to set 7 | indentation settings for you. If you are not, you should be using the 8 | following settings: 9 | - Indent lines with 2 spaces (not tabs) 10 | - A line should be no longer than 80 characters unless absolutely necessary 11 | 2. Add a function for the package manager. These should be kept in the same 12 | order as the conditionals at the end of the file. 13 | - Your function must look and act the same as the others 14 | - Do not rely on coreutils; if you need to use something from coreutils, you 15 | should write a shell function to do it. See `tot` and `has`, for example. 16 | - You must implement **ALL** functionality in your function. If there is a 17 | package manager for which one cannot be added, state this in your pull 18 | request and an exception can be made if absolutely necessary. 19 | 3. Add a condition at the end of the file to find your package manager, then 20 | call your function inside the conditional. 21 | 4. Test that your function works. 22 | 5. Submit a pull request! I usually respond to PRs and issues pretty quickly. 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Will Eccles 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PREFIX ?= /usr/local 2 | 3 | .PHONY: install 4 | 5 | install: 6 | install -d $(DESTDIR)$(PREFIX)/bin/ 7 | install -m 0755 cpm $(DESTDIR)$(PREFIX)/bin/ 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Cactus Package Manager 2 | A wrapper for package managers to make them consistent for those of us who are 3 | lazy. It's more like a package manager manager. 4 | 5 | ## Usage 6 | 7 | ``` 8 | $ cpm [i|r|l|u|U|s|S|I|F|f|c|h] [pkg]... 9 | -> i|install install one or more packages 10 | -> r|remove remove one or more packages 11 | -> l|list list installed packages 12 | -> C|count count installed packages 13 | -> u|update update package lists 14 | -> U|upgrade update package lists and upgrade all packages 15 | -> s|search search for a package 16 | -> S|show show information about a package 17 | -> I|info same as show 18 | -> F|files show file list of package 19 | -> f|from show package which owns a file 20 | -> c|clean clean up leftover files/caches/orphans 21 | -> h|help show this message 22 | ``` 23 | 24 | ## Installation 25 | 26 | ```bash 27 | git clone https://github.com/willeccles/cpm.git 28 | cd cpm 29 | sudo make install 30 | ``` 31 | 32 | ## Supported package managers 33 | 34 | - apk (Alpine/Adélie) 35 | - apt (Debian/Ubuntu) 36 | - pkgutils (CRUX) 37 | - emerge (Gentoo) 38 | - dnf (Fedora) 39 | - guix (non-system-wide Guix) 40 | - Homebrew 41 | - lunar (Lunar) 42 | - MacPorts (macOS) 43 | - pacman (Arch) 44 | - pkg (FreeBSD and OpenBSD) 45 | - rpm-ostree (Fedora Silverblue) 46 | - slackpkg (Slackware) 47 | - sorcery (Source Mage) 48 | - urpmi (Mageia) 49 | - xbps (Void) 50 | - zypper (OpenSUSE) 51 | 52 | ## Explicitly unsupported package managers 53 | 54 | - Cargo 55 | - Flatpak 56 | - Nix 57 | - NPM 58 | - Pip 59 | - Snap 60 | - Yarn 61 | 62 | ## My package manager isn't supported!!1!!11!1 63 | 64 | See CONTRIBUTING.md. 65 | -------------------------------------------------------------------------------- /cpm: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # vim: set ai ts=2 et sw=2 tw=80: 3 | # MIT License 4 | # 5 | # Copyright (c) 2022 Will Eccles 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy 8 | # of this software and associated documentation files (the "Software"), to deal 9 | # in the Software without restriction, including without limitation the rights 10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | # copies of the Software, and to permit persons to whom the Software is 12 | # furnished to do so, subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in all 15 | # copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | # SOFTWARE. 24 | 25 | phi() { 26 | >&2 printf "\033[33;1m-> \033[35;1m%s | %s\033[m %s\n" "$1" "$2" "$3" 27 | } 28 | 29 | usage() { 30 | >&2 echo "cpm [i|r|l|C|u|U|s|S|I|F|f|c|h] [pkg]..." 31 | phi i "install" "install one or more packages" 32 | phi r "remove " "remove one or more packages" 33 | phi l "list " "list installed packages" 34 | phi C "count " "count installed packages" 35 | phi u "update " "update package lists" 36 | phi U "upgrade" "update package lists and upgrade all packages" 37 | phi s "search " "search for a package" 38 | phi S "show " "show information about a package" 39 | phi I "info " "same as show" 40 | phi F "files " "show file list of package" 41 | phi f "from " "show package which owns a file" 42 | phi c "clean " "clean up leftover files/caches/orphans" 43 | phi h "help " "show this message" 44 | } 45 | 46 | pem() { 47 | >&2 printf "\033[31;1merror:\033[m %s\n" "$1" 48 | } 49 | 50 | case "$1" in 51 | i|install) 52 | OP='install' 53 | if [ $# -lt 2 ]; then 54 | pem "$OP: no package(s) specified" 55 | exit 1 56 | fi 57 | ;; 58 | r|remove) 59 | OP='remove' 60 | if [ $# -lt 2 ]; then 61 | pem "$OP: no package(s) specified" 62 | exit 1 63 | fi 64 | ;; 65 | l|list) 66 | OP='list' 67 | ;; 68 | C|count) 69 | OP='count' 70 | ;; 71 | u|update) 72 | OP='update' 73 | ;; 74 | U|upgrade) 75 | OP='upgrade' 76 | ;; 77 | s|search) 78 | OP='search' 79 | if [ $# -lt 2 ]; then 80 | pem "$OP: please specify a package" 81 | exit 1 82 | fi 83 | ;; 84 | S|show|I|info) 85 | OP='show' 86 | if [ $# -lt 2 ]; then 87 | pem "$OP: please specify a package" 88 | exit 1 89 | fi 90 | ;; 91 | F|files) 92 | OP='files' 93 | ;; 94 | f|from) 95 | OP='from' 96 | ;; 97 | c|clean) 98 | OP='clean' 99 | ;; 100 | h|help) 101 | usage 102 | exit 0 103 | ;; 104 | "") 105 | usage 106 | exit 1 107 | ;; 108 | *) 109 | pem "Unrecognized operation: $1" 110 | usage 111 | exit 1 112 | ;; 113 | esac 114 | shift 115 | 116 | # pipe to this to get a count instead of relying on wc -l 117 | tot() { 118 | i=0 119 | while read -r line; do 120 | i=$((i + 1)) 121 | done 122 | printf '%s\n' "$i" 123 | } 124 | 125 | # count the files in a directory (similar to ls -l | wc -l) or something 126 | # usage: fcount /path/to/dir/* 127 | fcount() { 128 | if [ -e "$1" ]; then 129 | printf '%s\n' "$#" 130 | else 131 | printf '%s\n' 0 132 | fi 133 | } 134 | 135 | # list files in a directory (similar to ls -l) 136 | flist() { 137 | if [ -d "$1" ]; then 138 | cd "$1" && printf '%s\n' * 139 | fi 140 | } 141 | 142 | # replacement for command -v which ignores aliases 143 | # this is useful for me, don't ask why 144 | has() { 145 | case "$(command -v "$1" 2>/dev/null)" in 146 | alias*|"") return 1 147 | esac 148 | } 149 | 150 | # resolve symlinks in path to file (may or may not work for a directory, but 151 | # I doubt there's a need for that anyway) 152 | resolve() { 153 | p="${1%/*}" 154 | [ "$p" = "$1" ] && p="." 155 | cd -P "$p" 2>/dev/null || PWD="$p" 156 | printf '%s\n' "${PWD}/${1##*/}" 157 | } 158 | 159 | # elevate permissions and execute a command 160 | su_do() { 161 | if [ "$(id -u)" != 0 ]; then 162 | if command -v sudo >/dev/null; then 163 | sudo "$@" 164 | elif command -v doas >/dev/null && \ 165 | ([ -f /etc/doas.conf ] || [ -f /usr/local/etc/doas.conf ]); then 166 | doas "$@" 167 | else 168 | su root -c '"$@"' -- sh "$@" 169 | fi 170 | else 171 | "$@" 172 | fi 173 | } 174 | 175 | # mainly for slackware: list lines after string $3 in directory $2, file $1 176 | # usage: filelistftr pkgname /directory/example "String to look for" 177 | # usage: filelistftr FILE [DIR] [STR] 178 | 179 | filelistftr() { 180 | file="${1}" 181 | dir="${2:-/var/log/packages}" 182 | str="${3:-"FILE LIST:"}" 183 | pkginfo="$dir/$file" 184 | if [ -f "$pkginfo" ]; then 185 | unset found 186 | while IFS= read -r line; do 187 | [ "$line" = "$str" ] && found=1 188 | [ "$found" ] && printf "%s\n" "$line" 189 | done <"$pkginfo" 190 | elif [ -d "$dir" ]; then 191 | pem "Not in $dir, check input or try updating." 192 | else 193 | pem "$dir does not exist, are you using the right PM?" 194 | fi 195 | } 196 | 197 | 198 | _apk() { 199 | case "$OP" in 200 | install) su_do apk add "$@";; 201 | remove) su_do apk del "$@";; 202 | list) apk -vv info;; 203 | count) apk -vv info | tot;; 204 | update) su_do apk update;; 205 | upgrade) su_do apk update && su_do apk upgrade;; 206 | search) apk search -v "$@";; 207 | show) apk search "$@";; 208 | from) apk info --who-owns "$@";; 209 | files) apk info -L "$@";; 210 | clean) su_do apk cache clean;; 211 | esac 212 | } 213 | 214 | _apt() { 215 | case "$OP" in 216 | install) su_do apt install "$@";; 217 | remove) su_do apt remove "$@";; 218 | list) apt list --installed;; 219 | count) dpkg-query -f '.\n' -W | tot;; 220 | update) su_do apt update;; 221 | upgrade) su_do apt update && su_do apt dist-upgrade;; 222 | search) apt search "$@";; 223 | show) apt show "$@";; 224 | files) dpkg -L "$@";; 225 | from) dpkg -S "$@";; 226 | clean) su_do apt autoremove;; 227 | esac 228 | } 229 | 230 | _portage() { 231 | case "$OP" in 232 | install) su_do emerge -atv "$@";; 233 | remove) su_do emerge -avc "$@";; 234 | list) qlist -IRv;; 235 | count) eix --world | tot;; 236 | update) su_do emerge --sync;; 237 | upgrade) 238 | su_do emerge --sync && \ 239 | su_do emerge -uDU --keep-going --with-bdeps=y @world 240 | ;; 241 | search) emerge -s "$@";; 242 | show) emerge -s "$@";; 243 | files) qlist "$@";; 244 | from) qfile "$@";; 245 | clean) su_do emerge --depclean -v;; 246 | esac 247 | } 248 | 249 | _dnf() { 250 | case "$OP" in 251 | install) su_do dnf install "$@";; 252 | remove) su_do dnf remove "$@";; 253 | list) dnf list --installed;; 254 | count) rpm -qa | tot;; 255 | update) su_do dnf check-update;; 256 | upgrade) su_do dnf upgrade;; 257 | search) dnf search "$@";; 258 | show) dnf info "$@";; 259 | files) dnf repoquery -l "$@";; 260 | from) dnf provides "$@";; 261 | clean) su_do dnf autoremove && su_do dnf clean all;; 262 | esac 263 | } 264 | 265 | _rpm_ostree() { 266 | case "$OP" in 267 | install) rpm-ostree install "$@";; 268 | remove) rpm-ostree uninstall "$@";; 269 | list) rpm -qa;; 270 | count) rpm -qa | tot;; 271 | update) rpm-ostree update;; 272 | upgrade) rpm-ostree upgrade;; 273 | search) pem "unsupported: this feature is currently unavailable in this PM";; 274 | show) rpm -qi "$@";; 275 | files) rpm -ql "$@";; 276 | from) rpm -q --whatprovides "$@";; 277 | clean) rpm-ostree cleanup --rollback;; 278 | esac 279 | } 280 | 281 | _pacman() { 282 | case "$OP" in 283 | install) su_do pacman -S "$@";; 284 | remove) su_do pacman -Rs "$@";; 285 | list) pacman -Q;; 286 | count) pacman -Q | tot;; 287 | update) su_do pacman -Sy;; 288 | upgrade) su_do pacman -Syu;; 289 | search) pacman -Ss "$@";; 290 | show) pacman -Si "$@";; 291 | files) pacman -Ql "$@";; 292 | from) pacman -Qo "$@";; 293 | clean) su_do pacman -Rns $(pacman -Qtdq) && su_do pacman -Sc;; 294 | esac 295 | } 296 | 297 | _urpmi() { 298 | case "$OP" in 299 | install) su_do urpmi "$@";; 300 | remove) su_do urpme "$@";; 301 | list) rpm -qa;; 302 | count) rpm -qa | tot;; 303 | update) su_do urpmi.update -a;; 304 | upgrade) su_do urpmi.update -a && su_do urpmi --auto-update;; 305 | search) urpmq -Y "$@";; 306 | show) urpmq --summary "$@";; 307 | files) rpm -ql "$@";; 308 | from) rpm -qf "$@";; 309 | clean) su_do urpme --auto-orphans;; 310 | esac 311 | } 312 | 313 | _macports() { 314 | case "$OP" in 315 | install) su_do port install -c "$@";; 316 | remove) su_do port uninstall --follow-dependencies "$@";; 317 | list) port installed;; 318 | count) port installed | tot;; 319 | update) su_do port sync;; 320 | upgrade) 321 | su_do port sync && su_do port selfupdate && su_do port upgrade outdated 322 | ;; 323 | search) port search "$@";; 324 | show) port info "$@";; 325 | files) port contents "$@";; 326 | from) port provides "$@";; 327 | clean) su_do port reclaim;; 328 | esac 329 | } 330 | 331 | _brew() { 332 | case "$OP" in 333 | install) brew install "$@";; 334 | remove) brew uninstall "$@";; 335 | list) brew list;; 336 | count) brew list | tot;; 337 | update) brew update;; 338 | upgrade) brew update && brew upgrade;; 339 | search) brew search "$@";; 340 | show) brew info "$@";; 341 | files) pem "unsupported operation";; 342 | from) pem "unsupported operation";; 343 | clean) brew cleanup;; 344 | esac 345 | } 346 | 347 | _xbps() { 348 | case "$OP" in 349 | install) su_do xbps-install "$@";; 350 | remove) su_do xbps-remove -R "$@";; 351 | list) xbps-query -l;; 352 | count) xbps-query -l | tot;; 353 | update) su_do xbps-install -S;; 354 | upgrade) su_do xbps-install -Su;; 355 | search) xbps-query -s "$@" --repository;; 356 | show) xbps-query -S "$@" --repository;; 357 | files) xbps-query -f "$1" --repository;; 358 | from) xbps-query -o "$(resolve "$1")";; 359 | clean) su_do xbps-remove -ROo;; 360 | esac 361 | } 362 | 363 | _slackpkg() { 364 | case "$OP" in 365 | install) su_do slackpkg install "$@";; 366 | remove) su_do slackpkg remove "$@";; 367 | list) flist /var/log/packages;; 368 | count) fcount /var/log/packages/*;; 369 | update) su_do slackpkg update gpg && su_do slackpkg update;; 370 | upgrade) 371 | su_do slackpkg update gpg && su_do slackpkg update && \ 372 | su_do slackpkg upgrade-all 373 | ;; 374 | search) slackpkg search "$@";; 375 | show) slackpkg info "$@";; 376 | from) slackpkg file-search "$@";; 377 | files) filelistftr "$1";; 378 | clean) su_do slackpkg clean-system;; 379 | esac 380 | } 381 | 382 | _zypper() { 383 | case "$OP" in 384 | install) su_do zypper install "$@";; 385 | remove) su_do zypper remove -u "$@";; 386 | list) rpm -qa;; 387 | count) rpm -qa | tot;; 388 | update) su_do zypper refresh;; 389 | upgrade) su_do zypper refresh && su_do zypper update;; 390 | search) zypper search "$@";; 391 | show) zypper info "$@";; 392 | files) rpm -ql "$@";; 393 | from) zypper search -f "$@";; 394 | clean) 395 | pem "unsupported: this feature is functionally useless in this PM" 396 | ;; 397 | esac 398 | } 399 | 400 | _sorcery() { 401 | case "$OP" in 402 | install) su_do cast "$@";; 403 | remove) su_do dispell "$@";; 404 | list) gaze installed;; 405 | count) gaze installed | tot;; 406 | update) su_do sorcery -u;; 407 | upgrade) su_do sorcery -ug;; 408 | search) gaze search "$@";; 409 | show) gaze what "$@";; 410 | files) gaze tablet spell-files "$@";; 411 | from) gaze from "$@";; 412 | clean) 413 | pem "unsupported: this feature is functionally useless in this PM" 414 | ;; 415 | esac 416 | } 417 | 418 | _lunar() { 419 | case "$OP" in 420 | install) su_do lunar install "$@";; 421 | remove) su_do lunar remove "$@";; 422 | list) lvu installed;; 423 | count) lvu installed | tot;; 424 | update) su_do lunar renew;; 425 | upgrade) su_do lunar renew && su_do lunar update;; 426 | search) lvu search "$@";; 427 | show) lvu what "$@";; 428 | files) lvu where "$@";; 429 | from) lvu from "$@";; 430 | clean) su_do lunar prune;; 431 | esac 432 | } 433 | 434 | _prt_get() { 435 | case "$OP" in 436 | install) su_do prt-get install "$@";; 437 | remove) su_do prt-get remove "$@";; 438 | list) prt-get listinst;; 439 | count) prt-get listinst | tot;; 440 | update) su_do ports -u;; 441 | upgrade) su_do ports -u && su_do prt-get sysup;; 442 | search) prt-get search "$@";; 443 | show) prt-get info "$@";; 444 | files) prt-get ls "$@";; 445 | from) prt-get fsearch "$@";; 446 | clean) su_do prt-get cache && pkgfoster;; 447 | esac 448 | } 449 | 450 | _guix() { 451 | case "$OP" in 452 | install) guix package --install "$@";; 453 | remove) guix package --remove "$@";; 454 | list) guix package --list-installed;; 455 | count) guix package --list-installed | tot;; 456 | update) guix pull;; 457 | upgrade) guix pull && guix upgrade --keep-going;; 458 | search) guix search "$@";; 459 | show) guix show "$@";; 460 | f*) 461 | pem "unsupported: this feature is functionally useless in this PM" 462 | ;; 463 | clean) guix gc --delete-generations;; 464 | esac 465 | } 466 | 467 | _pkg_() { 468 | case "$OP" in 469 | install) su_do pkg_add "$@";; 470 | remove) su_do pkg_delete "$@";; 471 | list) pkg_info -v;; 472 | count) pkg_info -v | tot;; 473 | update) 474 | pem "unsupported: repo list only changes when user performs sys upgrade" 475 | ;; 476 | upgrade) su_do pkg_add -u;; 477 | search) pkg_info "$@";; 478 | show) pkg_info "$@";; 479 | files) pkg_info -L "$@";; 480 | from) pkg_info -qE "$@";; 481 | clean) pem "unsupported: PM already does this";; 482 | esac 483 | } 484 | 485 | _pkg() { 486 | case "$OP" in 487 | install) su_do pkg install "$@";; 488 | remove) su_do pkg delete "$@";; 489 | list) pkg info;; 490 | count) pkg info | tot;; 491 | update) su_do pkg update;; 492 | upgrade) su_do pkg upgrade;; 493 | search) pkg search "$@";; 494 | show) pkg info "$@";; 495 | files) pkg info -l "$@";; 496 | from) pkg which "$@";; 497 | clean) su_do pkg autoremove;; 498 | esac 499 | } 500 | 501 | # Use pm=PKG_MANAGER cpm COMMAND to force a specific cpm function 502 | # ie.: pm=portage cpm list 503 | if [ "$pm" ] && has "_$pm"; then 504 | "_$pm" "$@" 505 | elif ! [ "$(uname -s)" = "Darwin" ]; then 506 | if has apk; then 507 | # alpine/adelie 508 | _apk "$@" 509 | elif has apt; then 510 | # debian/ubuntu 511 | _apt "$@" 512 | elif has emerge; then 513 | # gentoo 514 | _portage "$@" 515 | elif has dnf; then 516 | # fedora 517 | _dnf "$@" 518 | elif has rpm-ostree; then 519 | # fedora silverblue 520 | _rpm_ostree "$@" 521 | elif has pacman-key; then 522 | # arch/manjaro 523 | _pacman "$@" 524 | elif has urpmi; then 525 | # mageia 526 | _urpmi "$@" 527 | elif has xbps-install; then 528 | # void 529 | _xbps "$@" 530 | elif has slackpkg; then 531 | # slackware 532 | _slackpkg "$@" 533 | elif has zypper; then 534 | # opensuse 535 | _zypper "$@" 536 | elif has sorcery; then 537 | # source mage 538 | _sorcery "$@" 539 | elif has lunar; then 540 | # lunar 541 | _lunar "$@" 542 | elif has prt-get; then 543 | # crux 544 | _prt_get "$@" 545 | elif has guix; then 546 | # local (non-system-wide) guix 547 | _guix "$@" 548 | elif has pkg_info; then 549 | # openbsd 550 | _pkg_ "$@" 551 | elif has pkg; then 552 | # freebsd 553 | _pkg "$@" 554 | elif has snap; then 555 | pem "Snapd is not supported [wontfix]" 556 | exit 1 557 | else 558 | pem "No valid package manager detected." 559 | exit 1 560 | fi 561 | else 562 | if has port; then 563 | _macports "$@" 564 | elif has brew; then 565 | _brew "$@" 566 | else 567 | pem "No valid package manager detected." 568 | exit 1 569 | fi 570 | fi 571 | --------------------------------------------------------------------------------