diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..1ac363bd --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +*~ +.*.swp +pacapt.dev +pacapt-* +tests/tmp/* diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..52bcea68 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,18 @@ +sudo: required +language: +- bash +services: +- docker +script: +# lib{json,uri}-perl is required for `make shellcheck` +- sudo apt-get install libjson-perl liburi-perl +- make shellcheck +- make tests + +notifications: + webhooks: + urls: + - https://webhooks.gitter.im/e/228a31b6140b028bcf79 + on_success: change # options: [always|never|change] default: always + on_failure: always # options: [always|never|change] default: always + on_start: never # options: [always|never|change] default: always diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..9b551037 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,142 @@ +## v2.4.3 + +* `lib/homebrew`: Support `cask` (fix #117) +* `tests/dpkg`: Support new distros, drop support for old distro + +## v2.4.2 + +* Update README.md +* Support sysget-style sub commands (`pacapt install`, `pacapt upgrade`,...) +* `lib/homebrew/Rs`: Improvements (#124, @Mnkai) +* `lib/dpkg`: Use `dist-upgrade` for `Suy` and `Su` operations + +## v2.4.0 + +* `lib/tlmgr`: Add TeXLive support (Antony Lee) +* `lib/conda`: Conda support (Antony Lee) + +For developers: + +* Ability to support non-system package manager (`npm`, `gem`, ...) +* Reduce shellcheck warning/error reports + +## v2.3.15 + +* A warm up release with very minor updates. + +## v2.3.14 + +* `lib/homebrew`: `brew upgrade` is equivalant to `brew upgrade --all`. + See #90 and #101. +* Support `Clear Linux`. See #94. + +For developers: + +* Add Travis support +* Add and update test cases for Ubuntu 16.04, Ubuntu 14.04 + +## v2.3.13 + +* `lib/dpkg`: Fix `-Qs` for old `dpkg`. + +For developers: + +* Test scripts can now be automated thanks to `tests/*`; +* `tests/slitz40`: Add; +* `tests/dpkg`: Update. + +## v2.3.12 + +* `lib/dpkg`: Fix #84 (incorrect implementation of `-Qs`.) + +For developers: + +* `bin/gen_tests.rb`: Add; +* `lib/dpkg`: Add and update test cases; +* `CONTRIBUTING`: Add new section `Writting test cases`. + +## v2.3.11 + +* `lib/tazpkg`: Improve `-U`. + +## v2.3.10 + +* `lib/tazpkg`: Support `-Scc`. + +## v2.3.9 + +* `lib/tazpkg`: Support `SliTaz` distribution. + +For developers: + +* `contrib/*`: Add instructions to build packages on some distributions (Credit: `Pival81`). + +## v2.3.8 + +* `lib/alpine`: Support `Alpine` distirubtion (Credit: `Carl X. Su`, `Cuong Manh Le`); +* `lib/dnf`: Support new package manager on `Fedora` system (Credit: `Huy Ngô`); +* `lib/termux`: Support `termux` on Android (Credit: `Jiawei Zhou`); +* `lib/zypper`: New option `-Sw` (Forgot to merge #72); +* `lib/yum`: New option `-Qs` (Credit: `Siôn Le Roux`); + +For developers: + +* Improve translation method `_translate_all`; + +## v2.2.7 + +* `lib/zypper`: Complete query/removal options (Credit: `Janne Heß`); +* `lib/cave`: Fix an issue with `-R` option; +* New option `--noconfirm` to help non-interactive scripts (Cf. #43). + Currently available for `pkgng`, `yum`, `dpkg` and `zypper`. + +For developers: + +* `lib/{00_core,zz_main}`: Refactor to support future option translation; +* Refactor code supports `-w` (download only) and `-v` (debug) options; +* Improve coding quality thanks to `shellcheck`; +* Move `compile.sh` to `bin/compile.sh`; +* Use `lib/00_core#_translate_all` to add future option translation; +* `bin/check`: Add script to inspect coding style issues (Cf. #54). + +## v2.1.6 + +* `lib/sun_tools`: `SunOS` support (Credit: `Daniel YC Lin`); +* Fix a minor bug related to argument parsing (4287ff16e869a0960ea54233); +* Improve documentation; +* `lib/dnf`: Add some initial support; +* Adding `GREP` and `AWK` environments to future non-`Linux` systems; +* `compile.sh` will exit if it can't detect version information; +* `README` has a table of supported operations generated by `compile.sh`; +* In debug mode, `pacapt` will print body of function it would execute. + +## v2.0.5 + +* `lib/zz_main`: Improve secondary option parsing; +* `lib/pkg_tools`: Remove `Rns` support. + +## v2.0.4 + +* `openbsd/pkg_tools`: Add (Credit: `Somasis`); +* `homebrew/Su*`: Use `--all` flag when upgrading; +* `homebrew/*`: Some typo fixes; +* `compile.sh`: `git` becomes optional (useful for `docker` tester.); +* `compile.sh`: Get list of authors from `README.md`; +* `Makefile`: Various improvements; +* `lib/00_core`: Add `_removing_is_dangerous` method; +* `lib/00_core`: `_not_implemented` now returns `1`; +* `lib/help.txt`: Remove list of authors from help message; +* `CHANGELOG.md`: Add. + +## v2.0.3 + +* `homebrew/Qs`: Add; +* `homebrew/*`: Fix minor bugs. + +## v2.0.2 + +* `lib/zz_main`: Fix quoting issue (Credit: `Cuong Manh Le`). + +## v2.0.1 + +* `git log v2.0.1`. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..5b4986cd --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,70 @@ +## Table of contents + +1. [Future development](#future-development) +1. [Coding style](#coding-style) +1. [Testing. Writting test cases](#testing-writting-test-cases) +1. [Generating pacapt script](#generating-pacapt-script) +1. [Branches](#branches) +1. [Closed branches](#closed-branches) + +## Future development + +See also [README.md](README.md#read-me-first) + +## Coding style + +1. Don't use tab or smart tab; +1. Use `2-space` instead of a tab; +1. Contribute to library file under `./lib/` directory; +1. We try to follow the convention from: + https://github.com/icy/bash-coding-style. + +## Testing. Writting test cases + +See also `tests/README.md` and https://travis-ci.org/icy/pacapt. + +1. Use `make shellcheck` if you have a network connection, + and enough `Perl` packages (`JSON`, `URI::Escape`) on your system; +1. Use `PACAPT_DEBUG=foo` where `foo` is a package manager + (`dpkg`, `pacman`, `zypper`, ...) to print what `pacapt` will do. + Use `PACAPT_DEBUG=auto` for auto-detection; +1. You can use `docker` for testing, by mounting the `pacapt.dev` script + to the container. See also `docker.i` section in `Makefile`. Example: + +```` +$ make pacapt.dev +$ docker run --rm -ti \ + -v $PWD/pacapt.dev:/usr/bin/pacman \ + debian:stable /bin/bash +# you are in container now +```` + +## Generating `pacapt` script + +1. The `pacapt` script is generated from the latest stable branch, + it is there to make installation process simple; +1. Please **do not** use `make pacapt` to update `pacapt`, + and/or modify it manually; +1. For your development, use `make pacapt.dev`; + +## Branches + +1. `ng`: + The current development branch. + Some pull requests are merged on to this branch, + but the work may not be ready for production. +1. `v2.0`: + The current stable branch. + All future `v2.x` releases come from this branch. + +1. `your feature branch`: + For new feature or bug fix, please work on your own branch + and create pull request. + Do not put different ideas on a same branch + because that makes future tracking harder. + +## Closed branches + +1. `master`: + The old stable code of the `pacapt`. + This branch is closed on May 4th, 2014. diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 00000000..52049877 --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,31 @@ +#!/usr/bin/env groovy + +// Author : Ky-Anh Huynh +// Date : 2018 July 08 +// License: MIT + +@Library("icy@master") + +def icyUtils = new org.icy.Utils() + +try { + node { + checkOut("clean") + + buildInfo() + + stage("tests") { + sh ''' + make tests + ''' + } + } +} +catch (exc) { + currentBuild.result = currentBuild.result ?: "FAILED" + echo "Caught: ${exc}" + throw exc +} +finally { + echo "Finally." +} diff --git a/Makefile b/Makefile index 063da0bb..0bb2f5f1 100644 --- a/Makefile +++ b/Makefile @@ -1,32 +1,107 @@ -BINDIR=/usr/local/bin +BINDIR=/usr/local/bin/ +DISTRO=debian:stable default: - @echo "make README : build the README file from script's contents" - @echo "make install : install pacapt script into $(BINDIR)/" - @echo "make clean : remove git-ignored files" - -README: Makefile pacapt - @cat pacapt \ - | awk > $(@) \ - ' \ - BEGIN { EOF = 0 } \ - { \ - if ( $$0 ~ /EOF/ ) { EOF += 1 } \ - else if ( EOF == 1 ) { print $$0 } \ - } \ - ' + @echo "This is an experimental Makefile. Use it at your own risk." + @echo "" + @echo " pacapt.dev : Generate development script." + @echo ' install.dev : Install development script into $$BINDIR.' + @echo " pacapt : Generate stable script." + @echo ' install : Install stable script into $$BINDIR.' + @echo " clean : (Experimental) Remove git-ignored files." + @echo " shellcheck : Syntax and style checking. Use http://shellcheck.net/." + @echo " docker.i : Launch interactive Docker container which mounts." + @echo ' your local 'pacapt.dev' script to $$BINDIR/pacman.' + @echo ' Please use DISTRO= to specify Docker image' + @echo " tests : Run all tests. Please read tests/README.md first." + @echo " Use TESTS= to specify a package. Docker is required." + @echo " stats : Generate table of implemented operations in development branch." + @echo " update_stats: Update README.md using results from 'stats' section." + @echo "" + @echo "Environments:" + @echo "" + @echo " VERSION : Version information. Default: git commit hash." + @echo " BINDIR : Destination directory. Default: /usr/local/bin." + @echo " DISTRO : Container image. Default: debian:stable." +# Build and install development script + +pacapt.dev: ./lib/*.sh ./lib/*.txt bin/compile.sh + @./bin/compile.sh > $(@) || { rm -fv $(@); exit 1; } + @bash -n $(@) + @chmod 755 $(@) + @echo 1>&2 "The output file is '$(@)' (unstable version)" + +.PHONY: install.dev +install.dev: pacapt.dev + @if [ -e $(@) ] && ! file $(@) | grep -q 'script'; then \ + echo >&2 "Makefile Will not overwrite non-script $(@)"; \ + exit 1; \ + else \ + install -vm755 pacapt.dev $(BINDIR)/pacapt; \ + fi + +# Build and install stable script + +.PHONY: pacapt.check + +pacapt.check: + @test -n "${VERSION}" || { echo ":: Please specify VERSION, i.e., make pacapt VERSION=1.2.3"; exit 1; } + +pacapt: pacapt.check ./lib/*.sh ./lib/*.txt bin/compile.sh + @./bin/compile.sh > $(@).tmp || { rm -fv $(@).tmp; exit 1; } + @mv -fv $(@).tmp $(@) + @bash -n $(@) + @chmod 755 $(@) + @echo 1>&2 "The output file is '$(@)' (stable version)" + +.PHONY: install install: $(BINDIR)/pacapt +$(BINDIR)/pacman: + @if [ ! -e $(@) ]; then \ + ln -vs $(BINDIR)/pacapt $(@); \ + fi + $(BINDIR)/pacapt: pacapt - @if [ -e $(@) ] && ! file $(@) | grep -q 'shell script'; then \ - echo "will not overwrite non-script $(@)" >&2; exit 1; \ + @if [ -e $(@) ] && ! file $(@) | grep -q 'script'; then \ + echo >&2 "Makefile Will not overwrite non-script $(@)"; \ + exit 1; \ else \ - install $(<) $(@); \ + install -vm755 pacapt $(BINDIR)/pacapt; \ fi +.PHONY: docker.i +docker.i: + @docker run --rm -ti \ + -v $(PWD)/pacapt.dev:$(BINDIR)/pacman \ + $(DISTRO) /bin/bash + +.PHONY: update_stats +update_stats: + @./bin/update_stats.sh + +.PHONY: stats +stats: + @./bin/gen_stats.sh + +.PHONY: clean clean: @if git clean -nX | grep -q .; then \ - git clean -nX; echo -n "Remove these files? [y/N] "; read ANS; \ - case "$$ANS" in [yY]*) git clean -fX ;; *) exit 1;; esac ; \ + git clean -nX; \ + echo -n "Remove these files? [y/N] "; \ + read ANS; \ + case "$$ANS" in \ + [yY]*) git clean -fX ;; \ + *) exit 1;; \ + esac ; \ fi + @cd tests/ && make -s clean + +.PHONY: shellcheck +shellcheck: + @./bin/check.sh _check_files bin/*.sh lib/*.sh + +.PHONY: tests +tests: + @cd tests/ && make all diff --git a/README.md b/README.md new file mode 100644 index 00000000..a7463244 --- /dev/null +++ b/README.md @@ -0,0 +1,231 @@ +## `pacapt` - An `ArchLinux`'s pacman-like wrapper for many package managers + +[![Build Status](https://travis-ci.org/icy/pacapt.svg?branch=ng)](https://travis-ci.org/icy/pacapt) + +`pacapt` is a wrapper for many package managers. +Simply install package with `pacapt -S htop` or `pacapt install htop` +on any `Linux`, `BSD`, `Mac OS` machines. +It supports the following package managers: + +* `pacman` by `Arch Linux`, `ArchBang`, `Manjaro`, etc. +* `dpkg/apt-get` by `Debian`, `Ubuntu`, etc. +* `homebrew` by `Mac OS X` +* `macports` by `Mac OS X` +* `yum/rpm` by `Redhat`, `CentOS`, `Fedora`, etc. +* `portage` by `Gentoo` +* `zypper` by `OpenSUSE` +* `pkgng` by `FreeBSD` +* `cave` by `Exherbo Linux` +* `pkg_tools` by `OpenBSD` +* `sun_tools` by `Solaris(SunOS)` +* `apk` by `Alpine Linux` +* `tazpkg` by `SliTaz Linux` +* `swupd` by `Clear Linux` +* `tlmgr` by `TeX Live` +* `conda` by [`Conda`](https://conda.io/docs/) + +## TOC + +- [Description](#pacapt---an-archlinuxs-pacman-like-wrapper-for-many-package-managers) +- [Installation](#installation) + - [Install stable `Bash` script 2.4.x from Github](#install-stable-bash-script-24x-from-github) +- [Usage](#usage) + - [Basic operations](#basic-operations) + - [Basic options](#basic-options) + - [Implemented operations](#implemented-operations) +- [Related projects](#related-projects) +- [Similar projects](#similar-projects) +- [Development](#development) + - [General steps](#general-steps) + - [Experimental projects](#experimental-projects) +- [License](#license) +- [Authors and Contributors](#authors-contributors) + +## Installation + +1. This script shouldn't be installed on an Arch-based system; +2. On `FreeBSD` and `Alpine Linux`, please install `bash` package first. + +### Install stable `Bash` script 2.4.x from Github + +You can download the stable script and make it executable. +On non-Arch-based system, you may use `pacman` as script name instead of `pacapt`. + +```` +$ sudo wget -O /usr/local/bin/pacapt \ +https://github.com/icy/pacapt/raw/ng/pacapt + +$ sudo chmod 755 /usr/local/bin/pacapt + +$ sudo ln -sv /usr/local/bin/pacapt /usr/local/bin/pacman || true +```` + +For non-system package manager, you need to create symbolic links + +``` +$ ln -s /usr/local/bin/pacapt /usr/local/bin/pacapt-tlmgr +$ ln -s /usr/local/bin/pacapt /usr/local/bin/pacapt-conda +``` + +You can also use shorter links: + +``` +$ ln -s /usr/local/bin/pacapt /usr/local/bin/p-tlmgr +$ ln -s /usr/local/bin/pacapt /usr/local/bin/p-conda +``` + +noting the suffix (e.g., `-tlmgr`, `-conda`) is mandatory. + +## Usage + +### Basic operations + +For system package manager + +* Update package database: `pacapt -Sy`, or `pacapt update` +* Install a package: `pacapt -S foo`, or `pacapt install foo` +* Search a package: `pacapt -Ss foo`, or `pacapt search foo` +* Remove a package: `pacapt -R foo`, or `pacapt remove foo` +* Upgrade system: `pacapt -Su`, or `pacapt upgrade` +* Remove orphans: `pacapt -Sc`, or `pacapt autoremove foo` +* Clean up: `pacapt -Scc` or `pacapt -Sccc`, or `pacapt clean` + +For non-system package manager: Similar as above, however you need to call correct script name, e.g., + +* Intall a Conda package: `pacapt-conda -S foo` +* Remove a Conda package: `pacapt-conda -R foo` + +### Basic options + +See also https://github.com/icy/pacapt/blob/ng/lib/help.txt. + +Some basic command line options + +* `-h` (`--help`): Print help message; +* `-P`: Print list of supported operations; +* `-V`: Print script version + +Some popular options of the original `ArchLinux`'s `pacman` program +are supported and listed in the table in the next section. + +A long list of options and operations can be found from [`ArchLinux`'s wiki](https://wiki.archlinux.org/index.php/Pacman/Rosetta). + +### Implemented operations + +``` + Q Qc Qe Qi Qk Ql Qm Qo Qp Qs Qu R Rn Rns Rs S Sc Scc Sccc Sg Si Sii Sl Ss Su Suy Sw Sy U + apk ~ * * * * * * * * * * * * * * * * * * * * * * + cave * * * * * * * * * * * * * * x * * * * * x + conda * * * * * * * + dnf ~ * * * * * * * * * * * * * * * * * * * * * * * + dpkg ~ * * * * * * * * * ~ * * * * * * * * * * * + homebrew ~ * * * * * * * * * * * * * * * * * + macports * * * * * ~ * * * * * * * * + pkgng * * * * * * * * * * * * * * * * +pkg_tools ~ * * * * * * * * ~ * * x * * ~ * * x + portage * * * * * * * * * * * * * * * * * +sun_tools * * * * * * * + swupd * * * * * * * * * + tazpkg * * * * * * * * * * * * * + tlmgr * * * * * * * * * * + yum * * * * * * * * * * * * * * * * * * * * * * + zypper * * * * * * * * * * * * * * * * * * * * * * * * * * +``` + +**Notes:** + +* `*`: Implemented; +* `~`: Implemented. Some options may not supported/implemented; +* `x`: Operation is not supported by Operating system; +* The table is generated from source. Please don't update it manually. + +## Related projects + +* [`batch-pacapt`](https://github.com/Grenadingue/batch-pacapt): An Arch's pacman-like package manager for Windows +* [`node-pacapt`](https://github.com/Grenadingue/node-pacapt): A node.js wrapper of pacapt + batch-pacapt +* [`pacaptr`](https://github.com/rami3l/pacaptr): A Rust port of pacapt with decent `homebrew`/`choco`/`dpkg` support and more. + See also [Experimental projects](#experimental-projects) + +## Similar projects + +* [`sysget`](https://github.com/emilengler/sysget) + +## Development + +### General steps + +Make sure you read some instructions in `CONTRIBUTING.md`. + +A development script can be compiled from the source code. + +```` +$ git clone https://github.com/icy/pacapt.git +$ cd pacapt + +# switch to development branch +$ git checkout ng + +# compile the script +$ ./bin/compile.sh > pacapt.dev + +# check if syntax is good +$ bash -n pacapt.dev + +$ sudo install -m755 ./pacapt.dev /usr/local/bin/pacapt +```` + +Please read the sample `Makefile` for some details. + +### Experimental projects + +As the original Bash script grows, we feel a necessity to facilitate its maintenance. In [#126](https://github.com/icy/pacapt/issues/126), we propose to rewrite the project in Go, and some discussions have been made on the future of this project. + +Feel free to contribute to our new Rust port: [`pacaptr`](https://github.com/rami3l/pacaptr). + +Before that, we have also made several tries to rewrite the project: + +* `pacapt`'s [`nd`](https://github.com/icy/pacapt/tree/nd) branch: A rewrite in Dlang, but not mature enough. + +* [`pacapt-py`](https://github.com/rami3l/pacapt-py): A proof of concept in Python to provide pacapt-like experience to Homebrew. + +* [`pacapt-go`](https://github.com/rami3l/pacaptr/tree/go-dev): A more complete rewrite in Go, now lives in the [`go-dev`](https://github.com/rami3l/pacaptr/tree/go-dev) branch of `pacaptr` as legacy code. + +## License + +This work is released under the terms of Fair license +(http://opensource.org/licenses/fair). + +## AUTHORS. CONTRIBUTORS + +Many people have contributed to the project by sending pull requests +and/or reporting on the ticket system. Here is an incomplete list of +authors and contributors. + +* 10sr (10sr) +* Alexander Dupuy (dupuy) +* Anh K. Huynh (icy) +* Antony Lee (anntzer) +* Alex Lyon (Arcterus) +* Carl X. Su (bcbcarl) +* Cuong Manh Le (Gnouc) +* Daniel YC Lin (dlintw) +* Danny George (dangets) +* Darshit Shah (darnir) +* Dmitry Kudriavtsev (dkudriavtsev) +* Eric Crosson (EricCrosson) +* Evan Relf (evanrelf) +* GijsTimmers (GijsTimmers) +* Hà-Dương Nguyễn (cmpitg) +* Huy Ngô (NgoHuy) +* James Pearson (xiongchiamiov) +* Janne Heß (dasJ) +* Jiawei Zhou (4679) +* Karol Blazewicz +* Kevin Brubeck (unhammer) +* Konrad Borowski (xfix) +* Kylie McClain (somasis) +* Gen Li (Rami3L) +* Valerio Pizzi (Pival81) +* Siôn Le Roux (sinisterstuf) +* Thiago Perrotta (thiagowfx) +* Vojtech Letal (letalvoj) diff --git a/bin/check.sh b/bin/check.sh new file mode 100755 index 00000000..777c9855 --- /dev/null +++ b/bin/check.sh @@ -0,0 +1,105 @@ +#!/usr/bin/env bash + +# Purpose: Check script for error +# Author : Anh K. Huynh +# Date : 2015 Aug 06 +# License: MIT license + +_simple_check() { + bash -n "$@" +} + +_perl_check() { + perl -MURI::Escape -MJSON -e 'exit(0)' +} + +_shellcheck() { + local _data + + _data="$( \ + perl -MURI::Escape \ + -e ' + my $stream = do { local $/; ; }; + print uri_escape($stream); + ' + )" + + curl -LsSo- \ + 'http://www.shellcheck.net/shellcheck.php' \ + -H 'Accept: application/json, text/javascript, */*' \ + -H 'Content-Type: application/x-www-form-urlencoded; charset=UTF-8' \ + -H 'Host: www.shellcheck.net' \ + -H 'Referer: http://www.shellcheck.net/' \ + -H 'User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:39.0) Gecko/20100101 Firefox/39.0' \ + -H 'X-Requested-With: XMLHttpRequest' \ + --data "script=$_data" +} + +_shellcheck_output_format() { + perl -e ' + use JSON; + my $stream = do { local $/; <>; }; + my $output = decode_json($stream); + my $colors = { + "error" => "\e[1;31m", + "warning" => "\e[1;33m", + "style" => "\e[1;36m", + "default" => "\e[0m", + "reset" => "\e[0m" + }; + + foreach (keys @{$output}) { + my $comment = @{$output}[$_]; + my $color = $colors->{$comment->{"level"}} || $colors->{"default"}; + + printf("%s%7s %4d: line %4d col %2d, msg %s%s\n", + $color, + $comment->{"level"}, $comment->{"code"}, + $comment->{"line"}, $comment->{"column"}, + $comment->{"message"}, + $colors->{"reset"} + ); + } + ' +} + +# See discussion in https://github.com/icy/pacapt/pull/59 +_has_shellcheck() { + command -v shellcheck >/dev/null 2>&1 +} + +_check_file() { + local _file="${1:-/x/x/x/x/x/x/x/}" + + echo >&2 ":: ${FUNCNAME[0]}: $1" + + [[ -f "$_file" ]] \ + || { + echo >&2 ":: File not found '$_file'" + return 1 + } + + _simple_check "$_file" || return + + if _has_shellcheck; then + shellcheck -f json "$_file" | _shellcheck_output_format + else + _shellcheck < "$_file" | _shellcheck_output_format + fi +} + +_check_files() { + _has_shellcheck \ + || { + echo >&2 ":: WARN: shellcheck not found." + echo >&2 ":: WARN: Scripts will be checked by remote web server." + } + while (( $# )); do + _check_file "$1" || return 1 + shift + done +} + +_perl_check || exit 1 + +"$@" diff --git a/bin/compile.sh b/bin/compile.sh new file mode 100755 index 00000000..514132a6 --- /dev/null +++ b/bin/compile.sh @@ -0,0 +1,161 @@ +#!/usr/bin/env bash + +# Purpose: `Compile` libraries from `lib/*` to create `pacapt` script. +# Author : Anh K. Huynh +# License: Fair license (http://www.opensource.org/licenses/fair) +# Source : http://github.com/icy/pacapt/ + +# Copyright (C) 2010 - 2015 Anh K. Huynh et al. +# +# Usage of the works is permitted provided that this instrument is +# retained with the works, so that any entity that uses the works is +# notified of this instrument. +# +# DISCLAIMER: THE WORKS ARE WITHOUT WARRANTY. + +set -u +set -e +unset GREP_OPTIONS + +VERSION="${VERSION:-$(git log --pretty="%h" -1 2>/dev/null || true)}" +VERSION="${VERSION:-unknown}" + +if [[ "${VERSION}" == "unknown" ]]; then + echo >&2 ":: Unable to get version information." + echo >&2 ":: You may to install or reconfigure your 'git' package." + exit 1 +fi + +: "${PACAPT_STATS:=yes}" # List implemented operations to STDERR +: "${GREP:=grep}" # Need to update on SunOS +: "${AWK:=awk}" # Need to update on SunOS + +# At compile time, `_sun_tools_init` is not yet defined. +if [[ -f "lib/sun_tools.sh" ]]; then + # shellcheck disable=1091 + source "lib/sun_tools.sh" : + _sun_tools_init +fi + +export GREP AWK VERSION PACAPT_STATS + +######################################################################## +# Print the shebang and header +######################################################################## + +cat <&2 "pacapt version '$VERSION' has been generated." diff --git a/bin/gen_stats.sh b/bin/gen_stats.sh new file mode 100755 index 00000000..01fc9692 --- /dev/null +++ b/bin/gen_stats.sh @@ -0,0 +1,153 @@ +#!/usr/bin/env bash + +# Purpose : Generate the table of implemented operations +# Author : Ky-Anh Huynh +# License : MIT +# Origin : ./bin/compile.sh + +# +: "${PACAPT_STATS:=yes}" # List implemented operations to STDERR +: "${GREP:=grep}" # Need to update on SunOS +: "${AWK:=awk}" # Need to update on SunOS +: "${SYM_PARTIALLY_SUPPORTED:=~}" +: "${SYM_FULLY_SUPPORTED:=*}" +: "${SYM_NOT_SUPPORTED:=x}" +: "${SYM_UNKNOWN:= }" + +# At compile time, `_sun_tools_init` is not yet defined. +if [[ -f "lib/sun_tools.sh" ]]; then + # shellcheck disable=1091 + source "lib/sun_tools.sh" : + _sun_tools_init +fi + +export GREP AWK VERSION PACAPT_STATS +# + +# $1: +_implState() { + if grep -qs "# ${1} _not_implemented" ./lib/*.sh; then + echo "${SYM_NOT_SUPPORTED}" + elif grep -qs "# ${1} may _not_implemented" ./lib/*.sh; then + echo "${SYM_PARTIALLY_SUPPORTED}" + else + echo "${SYM_FULLY_SUPPORTED}" + fi +} + +printf >&2 ":: %s: Generating statistics (table of implemented operations)..." "$0" + +# Operations (FQDN) +_OPERATIONS=() +for L in ./lib/*.sh; do + _PKGNAME="${L##*/}" + _PKGNAME="${_PKGNAME%.*}" + + case "$_PKGNAME" in + "zz_main"|"00_core") continue ;; + esac + + while read -r F; do + _OPERATIONS+=( "$F" ) + done < \ + <( + # shellcheck disable=2016 + $GREP -hE "^${_PKGNAME}_[^ \\t]+\\(\\)" "$L" \ + | $AWK -F '(' '{print $1}' + ) +done + +# Secondary options +_SOPERATIONS="$( + echo "${_OPERATIONS[@]}" \ + | sed -e 's# #\n#g' \ + | sed -e 's#^.*_\([A-Z][a-z]*\)#\1#g' \ + | sort -u + )" + +printf "\\n" +printf "\`\`\`\\n" + +# Print the headers +_ret="$(printf "%9s " "")" +for _sopt in $_SOPERATIONS; do + _size="$(( ${#_sopt} + 1))" + _ret="$(printf "%s%${_size}s" "$_ret" "$_sopt")" +done +printf "%s\\n" "$_ret" + +i=0 # index +rs=0 # restart + +_OPERATIONS+=( "xxx_yyy" ) + +while :; do + _ret="" + + [[ "$i" -lt "${#_OPERATIONS[@]}" ]] \ + || break + + _cur_pkg="${_OPERATIONS[$i]}" + _cur_pkg="${_cur_pkg%_*}" + + for _sopt in $_SOPERATIONS; do + # Detect flag for this secondary option + _flag="${SYM_UNKNOWN}" + + # Start from the #rs index, + # go to boundary of the next package name. + # xx_Qi, xx_Qs,... yy_Qi, yy_Qs,... + # + i=$rs + while [[ "$i" -lt "${#_OPERATIONS[@]}" ]]; do + _opt="${_OPERATIONS[$i]}" + + _cur2_opt="${_opt##*_}" + _cur2_pkg="${_opt%_*}" + + # echo >&2 "(cur_pkg = $_cur_pkg, look up $_sopt [from $rs], found $_cur2_opt)" + + # Reach the boundary of the next package name + if [[ "$_cur2_pkg" != "$_cur_pkg" ]]; then + break + else + if [[ "$_cur2_opt" == "$_sopt" ]]; then + # detect real state of this operation... + _flag="$(_implState "$_opt")" + break + else + (( i ++ )) ||: + fi + fi + done + + _size="$(( ${#_sopt} + 1))" + _ret="$(printf "%s%${_size}s" "$_ret" "$_flag")" + done + + # Detect the next #restart index + i=$rs + while [[ "$i" -lt "${#_OPERATIONS[@]}" ]]; do + _opt="${_OPERATIONS[$i]}" + _cur2_pkg="${_opt%_*}" + + if [[ "$_cur2_pkg" != "$_cur_pkg" ]]; then + rs=$i + break + fi + + (( i ++ )) ||: + done + + if [[ "$_cur_pkg" != "xxx" ]]; then + printf "%9s %s\\n" "$_cur_pkg" "$_ret" + fi +done + +printf "\`\`\`\\n" +printf "\\n**Notes:**\\n\\n" +printf "* \`%s\`: Implemented;\\n" "${SYM_FULLY_SUPPORTED}" +printf "* \`%s\`: Implemented. Some options may not supported/implemented;\\n" "${SYM_PARTIALLY_SUPPORTED}" +printf "* \`%s\`: Operation is not supported by Operating system;\\n" "${SYM_NOT_SUPPORTED}" +printf "* The table is generated from source. Please don't update it manually.\\n" +printf "\\n" diff --git a/bin/gen_tests.rb b/bin/gen_tests.rb new file mode 100644 index 00000000..5ccb8364 --- /dev/null +++ b/bin/gen_tests.rb @@ -0,0 +1,117 @@ +#!/usr/bin/env ruby + +# Purpose: Generate test scripts +# Author : Anh K. Huynh +# License: MIT +# Date : 2016 July 18th +# Example: +# +# $ ruby -n ./bin/gen_tests.rb < lib/dpkg.txt > ./test.sh +# $ docker run --rm \ +# -v $PWD/test.sh:/tmp/test.sh \ +# -v $PWD/pacapt.dev:/usr/bin/pacman \ +# ubuntu:14.04 \ +# /bin/sh /tmp/test.sh 2>test.log +# + +BEGIN { + new_test = true + puts "#!/bin/bash" + puts "" + puts "export PATH=/usr/bin:$PATH" + puts "" + puts "N_TEST=0 # Total number of tests" + puts "N_FAIL=0 # Number of failed tests" + puts "F_TMP= # Temporary file" + puts "T_FAIL=0 # Number of failed tests in current block" + puts "" + puts "set -u" + puts "" + puts "_log() { echo \":: $*\" 1>&2 ; }" + puts "_fail() { _log \"Fail $*\"; echo \"Fail: $*\"; }" # red + puts "_erro() { _log \"Erro $*\"; echo \"Erro: $*\"; }" # red + puts "_info() { _log \"Info $*\"; echo \"Info: $*\"; }" # cyan + puts "_pass() { _log \"Pass $*\"; echo \"Pass: $*\"; }" # cyan + puts "_exec() { _log \"Exec $*\"; echo \"Exec: $*\"; }" # yellow + puts "_warn() { _log \"Warn $*\"; echo \"Warn: $*\"; }" # yellow + puts "_slog() {" + puts " if [ -n \"${F_TMP:-}\" ]; then" + puts " echo 1>&2 ':: Exec output'" + puts " cat 1>&2 $F_TMP" + puts " if [ $T_FAIL -ge 1 ]; then" + puts " cat $F_TMP \\" + puts " | awk '{ if (NR <= 100) { printf(\" > %s\\n\", $0); }}" + puts " END { if (NR > 100) { printf(\" > ...\\n\");}}'" + puts " fi" + puts " rm -f \"${F_TMP}\"" + puts " echo 1>&2" + puts " fi" + puts " export T_FAIL=0" + puts "}" +} + +if gs = $_.match(/^in(.*)/) + if new_test + puts "" + outputs = [] + new_test = false + + puts "_slog" + puts "export F_TMP=\"$(mktemp)\"" + puts "if [ -z ${F_TMP:-} ]; then" + puts " _fail 'Unable to create temporary file.'" + puts "fi" + end + + cmd = gs[1].strip.gsub("\$LOG", "$F_TMP") + + cmd = \ + case cmd + when "clear" then + "echo > $F_TMP" + when /\A! (.+)\z/ then + "#{$1.strip}" + else + "pacman #{cmd}" + end + + # FIXME: We wanted to use `tee` here, but that will create new pipes, + # FIXME: and hence some feature didn't work (e.g., export FOO=). + puts "if [ -n \"${F_TMP:-}\" ]; then" + puts " _exec \"#{cmd}\"" + puts " { #{cmd} ; } 1>>$F_TMP 2>&1" + puts "fi" + +elsif gs = $_.match(/^ou(.*)/) + new_test = true + expected = gs[1].strip + + puts "N_TEST=$(( N_TEST + 1 ))" + puts "if [ -n \"${F_TMP:-}\" ]; then" + if expected.empty? or expected == "empty" + puts " ret=\"$(grep -Ec '.+' $F_TMP)\"" + puts " if [ $ret -ge 1 ]; then" + else + puts " ret=\"$(grep -Ec \"#{expected}\" $F_TMP)\"" + puts " if [ $ret -eq 0 ]; then" + end + puts " _fail Expected \"#{expected}\"" + puts " N_FAIL=$(( N_FAIL + 1 ))" + puts " T_FAIL=$(( T_FAIL + 1 ))" + puts " else" + puts " _pass Matched \"#{expected}\"" + puts " fi" + puts "else" + puts " N_FAIL=$(( N_FAIL + 1 ))" + puts "fi" +end + +END { + puts "_slog" + puts "if [ $N_FAIL -ge 1 ]; then" + puts " _fail \"$N_FAIL/$N_TEST test(s) failed.\"" + puts " exit 1" + puts "else" + puts " _pass \"All $N_TEST tests(s) passed.\"" + puts "fi" +} diff --git a/bin/update_stats.sh b/bin/update_stats.sh new file mode 100755 index 00000000..b0b9f38e --- /dev/null +++ b/bin/update_stats.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash + +# Purpose : Update table of implemented operations in README.md +# Author : Ky-Anh Huynh +# License : MIT + +./bin/gen_stats.sh > stats.tmp + +< README.md awk ' +BEGIN { + ins = 0 +} +{ + if ($0 ~ /## Implemented operations/) { + print $0; + ins = 1; + } + else { + if ($0 ~ /##/) { + print $0; + ins = 0; + } + else if (ins == 0) { + print $0; + } + } +} +' \ +| sed -e '/## Implemented operations/r stats.tmp' \ +> README.md.tmp + +mv README.md.tmp README.md +git diff README.md +rm stats.tmp diff --git a/contrib/README.md b/contrib/README.md new file mode 100644 index 00000000..8bca1b16 --- /dev/null +++ b/contrib/README.md @@ -0,0 +1,33 @@ + +First, install all the dependencies + +For Fedora: +```` +# dnf install @development-tools +# dnf install fedora-packager +# dnf install rpmdevtools +```` +For CentOS, RHEL: +```` +# yum install rpm-build +```` +For OpenSuSE, refer to the OpenSuSE wiki (https://en.opensuse.org/SDB:Compiling_software#Prerequisites). + +Now, if you haven't created the rpmbuild directory in your home yet, you can do this using this command: +```` +$ cd $HOME +$ rpmdev-setuptree +```` +Ok, now we're set up for compiling, now just download the latest release of pacapt (you can find it on the github page, go on Releases), download it in .tar.gz format: +```` +$ cd $HOME/rpmbuild/SOURCES +$ wget https://github.com/icy/pacapt/archive/v2.3.15.tar.gz +$ mv v2.3.15.tar.gz pacapt-2.3.15.tar.gz +```` +And now download the spec file in this contrib directory and place it in ````$HOME/rpmbuild/SPECS```` + +Then, compile it: +```` +$ rpmbuild -ba $HOME/rpmbuild/SPECS/pacapt.spec +```` +And you're done! Your generated RPM will be in ````$HOME/rpmbuild/RPMS/noarch>/```` diff --git a/contrib/pacapt.spec b/contrib/pacapt.spec new file mode 100644 index 00000000..658a7f46 --- /dev/null +++ b/contrib/pacapt.spec @@ -0,0 +1,73 @@ +%global debug_package %{nil} +Name: pacapt +Version: 2.3.15 +Release: 1%{?dist} +Summary: An Arch's pacman-like package manager for some Unices +License: Fair +URL: https://github.com/icy/pacapt +Source0: %{name}-%{version}.tar.gz +BuildArch: noarch + +%description +An Arch's pacman-like package manager for some Unices. Actually this Bash script provides a wrapper for system's package manager. For example, on CentOS machines, you can install htop with command + +$ pacapt -S htop + +Instead of remembering various options/tools on different OSs, you only need a common way to manipulate packages. Not all options of the native package manager are ported; the tool only provides a very basic interface to search, install, remove packages, and/or update the system. + +Arch's pacman is chosen, as pacman is quite smart when it divides all packages-related operations into three major groups: Synchronize, Query and Remove/Clean up. It has a clean man page, and it is the only tool needed to manipulate official packages on system. (Debian, for example, requires you to use apt-get, dpkg, and/or aptitude.) + +The tool supports the following package managers: + + pacman by Arch Linux, ArchBang, Manjaro, etc. + dpkg/apt-get by Debian, Ubuntu, etc. + homebrew by Mac OS X + macports by Mac OS X + yum/rpm by Redhat, CentOS, Fedora, etc. + portage by Gentoo + zypper by OpenSUSE + pkgng by FreeBSD + cave by Exherbo Linux + pkg_tools by OpenBSD + sun_tools by Solaris(SunOS) + apk by Alpine Linux + +%prep +%setup -q + +%build +rm -rf bin,contrib,tests,lib,.gitignore,CHANGELOG.md,CONTRIBUTING.md,COPYING,Makefile,README.md,TODO + +%install +mkdir -p %buildroot/usr/local/bin +install -m755 pacapt %buildroot/usr/local/bin +ln -s /usr/local/bin/pacapt %buildroot/usr/local/bin/pacman + +%clean +rm -rf %_buildrootdir + +%files +/usr/local/bin/pacapt +/usr/local/bin/pacman + +%changelog +* Sun Jun 17 2018 Ky-Anh Huynh(icy) - 1 +- Updated version (2.3.14 -> 2.3.15) +* Thu Oct 20 2017 Ky-Anh Huynh(icy) - 1 +- Updated version (2.3.13 -> 2.3.14) +* Thu Jul 21 2016 Ky-Anh Huynh(icy) - 1 +- Updated version (2.3.12 -> 2.3.13) +* Wed Jul 20 2016 Ky-Anh Huynh(icy) - 1 +- Updated version (2.3.11 -> 2.3.12) +* Mon Jul 11 2016 Ky-Anh Huynh(icy) - 1 +- Updated version (2.3.10 -> 2.3.11) +* Mon Jul 11 2016 Ky-Anh Huynh(icy) - 1 +- Updated version (2.3.9 -> 2.3.10) +* Fri Jul 8 2016 Ky-Anh Huynh(icy) - 1 +- Updated version (2.3.8 -> 2.3.9) +* Fri Jul 8 2016 Valerio Pizzi(Pival81) - 1 +- Changed the installation directory (/usr/local/bin instead of /bin). +* Fri Jun 10 2016 Valerio Pizzi(Pival81) - 1 +- Updated version (2.2.7 -> 2.3.8) +* Thu Jun 9 2016 Valerio Pizzi(Pival81) - 1 +- Initial package. diff --git a/lib/00_core.sh b/lib/00_core.sh new file mode 100644 index 00000000..60199598 --- /dev/null +++ b/lib/00_core.sh @@ -0,0 +1,235 @@ +#!/bin/bash + +# Purpose: Provide some basic functions +# Author : Anh K. Huynh +# License: Fair license (http://www.opensource.org/licenses/fair) +# Source : http://github.com/icy/pacapt/ + +# Copyright (C) 2010 - 2014 Anh K. Huynh +# +# Usage of the works is permitted provided that this instrument is +# retained with the works, so that any entity that uses the works is +# notified of this instrument. +# +# DISCLAIMER: THE WORKS ARE WITHOUT WARRANTY. + +_error() { + echo >&2 "Error: $*" + return 1 +} + +_warn() { + echo >&2 "Warning: $*" + return 0 +} + +_die() { + echo >&2 "$@" + exit 1 +} + +_not_implemented() { + # shellcheck disable=2153 + echo >&2 "${_PACMAN}: '${_POPT}:${_SOPT}:${_TOPT}' operation is invalid or not implemented." + return 1 +} + +_removing_is_dangerous() { + echo >&2 "${_PACMAN}: removing with '$*' is too dangerous" + return 1 +} + +# Detect package type from /etc/issue +# FIXME: Using new `issue` file (location) +_issue2pacman() { + local _pacman + + _pacman="$1"; shift + + # The following line is added by Daniel YC Lin to support SunOS. + # + # [ `uname` = "$1" ] && _PACMAN="$_pacman" && return + # + # This is quite tricky and fast, however I don't think it works + # on Linux/BSD systems. To avoid extra check, I slightly modify + # the code to make sure it's only applicable on SunOS. + # + [[ "$(uname)" == "SunOS" ]] && _PACMAN="$_pacman" && return + + $GREP -qis "$@" /etc/issue \ + && _PACMAN="$_pacman" && return + + $GREP -qis "$@" /etc/os-release \ + && _PACMAN="$_pacman" && return +} + +# Detect package type +_PACMAN_detect() { + _PACMAN_found_from_script_name && return + + _issue2pacman sun_tools "SunOS" && return + _issue2pacman pacman "Arch Linux" && return + _issue2pacman dpkg "Debian GNU/Linux" && return + _issue2pacman dpkg "Ubuntu" && return + _issue2pacman cave "Exherbo Linux" && return + _issue2pacman yum "CentOS" && return + _issue2pacman yum "Red Hat" && return + # + # FIXME: The multiple package issue. + # + # On #63, Huy commented out this line. This is because new generation + # of Fedora uses `dnf`, and `yum` becomes a legacy tool. On old Fedora + # system, `yum` is still detectable by looking up `yum` binary. + # + # I'm not sure how to support this case easily. Let's wait, e.g, 5 years + # from now to make `dnf` becomes a default? Oh no! + # + # And here why `pacman` is still smart. Debian has a set of tools. + # Fedora has `yum` (and a set of add-ons). Now Fedora moves to `dnf`. + # This means that a package manager is not a heart of a system ;) + # + # _issue2pacman yum "Fedora" && return + _issue2pacman zypper "SUSE" && return + _issue2pacman pkg_tools "OpenBSD" && return + _issue2pacman pkg_tools "Bitrig" && return + _issue2pacman apk "Alpine Linux" && return + + [[ -z "$_PACMAN" ]] || return + + # Prevent a loop when this script is installed on non-standard system + if [[ -x "/usr/bin/pacman" ]]; then + $GREP -q "${FUNCNAME[0]}" '/usr/bin/pacman' >/dev/null 2>&1 + [[ $? -ge 1 ]] && _PACMAN="pacman" \ + && return + fi + + [[ -x "/usr/bin/apt-get" ]] && _PACMAN="dpkg" && return + [[ -x "/data/data/com.termux/files/usr/bin/apt-get" ]] && _PACMAN="dpkg" && return + [[ -x "/usr/bin/cave" ]] && _PACMAN="cave" && return + [[ -x "/usr/bin/dnf" ]] && _PACMAN="dnf" && return + [[ -x "/usr/bin/yum" ]] && _PACMAN="yum" && return + [[ -x "/opt/local/bin/port" ]] && _PACMAN="macports" && return + [[ -x "/usr/bin/emerge" ]] && _PACMAN="portage" && return + [[ -x "/usr/bin/zypper" ]] && _PACMAN="zypper" && return + [[ -x "/usr/sbin/pkg" ]] && _PACMAN="pkgng" && return + # make sure pkg_add is after pkgng, FreeBSD base comes with it until converted + [[ -x "/usr/sbin/pkg_add" ]] && _PACMAN="pkg_tools" && return + [[ -x "/usr/sbin/pkgadd" ]] && _PACMAN="sun_tools" && return + [[ -x "/sbin/apk" ]] && _PACMAN="apk" && return + [[ -x "/usr/bin/tazpkg" ]] && _PACMAN="tazpkg" && return + [[ -x "/usr/bin/swupd" ]] && _PACMAN="swupd" && return + + command -v brew >/dev/null && _PACMAN="homebrew" && return + + return 1 +} + +# Translate -w option. Please note this is only valid when installing +# a package from remote, aka. when '-S' operation is performed. +_translate_w() { + + echo "$_EOPT" | $GREP -q ":w:" || return 0 + + local _opt= + local _ret=0 + + case "$_PACMAN" in + "dpkg") _opt="-d";; + "cave") _opt="-f";; + "macports") _opt="fetch";; + "portage") _opt="--fetchonly";; + "zypper") _opt="--download-only";; + "pkgng") _opt="fetch";; + "yum") _opt="--downloadonly"; + if ! rpm -q 'yum-downloadonly' >/dev/null 2>&1; then + _error "'yum-downloadonly' package is required when '-w' is used." + _ret=1 + fi + ;; + "tazpkg") + _error "$_PACMAN: Use '$_PACMAN get' to download and save packages to current directory." + _ret=1 + ;; + "apk") _opt="fetch";; + *) + _opt="" + _ret=1 + + _error "$_PACMAN: Option '-w' is not supported/implemented." + ;; + esac + + echo $_opt + return "$_ret" +} + +_translate_debug() { + echo "$_EOPT" | $GREP -q ":v:" || return 0 + + case "$_PACMAN" in + "tazpkg") + _error "$_PACMAN: Option '-v' (debug) is not supported/implemented by tazpkg" + return 1 + ;; + esac + + echo "-v" +} + +# Translate the --noconfirm option. +# FIXME: does "yes | pacapt" just help? +_translate_noconfirm() { + + echo "$_EOPT" | $GREP -q ":noconfirm:" || return 0 + + local _opt= + local _ret=0 + + case "$_PACMAN" in + # FIXME: Update environment DEBIAN_FRONTEND=noninteractive + # FIXME: There is also --force-yes for a stronger case + "dpkg") _opt="--yes";; + "dnf") _opt="--assumeyes";; + "yum") _opt="--assumeyes";; + # FIXME: pacman has 'assume-yes' and 'assume-no' + # FIXME: zypper has better mode. Similar to dpkg (Debian). + "zypper") _opt="--no-confirm";; + "pkgng") _opt="-y";; + "tazpkg") _opt="--auto";; + *) + _opt="" + _ret=1 + _error "$_PACMAN: Option '--noconfirm' is not supported/implemented." + ;; + esac + + echo $_opt + return $_ret +} + +_translate_all() { + local _args="" + local _debug= + local _noconfirm= + + _debug="$(_translate_debug)" + _noconfirm="$(_translate_noconfirm)" + _args="$(_translate_w)" || return 1 + _args="${_args}${_noconfirm:+ }${_noconfirm}" || return 1 + _args="${_args}${_debug:+ }${_debug}" || return 1 + + export _EOPT="${_args# }" +} + +_print_supported_operations() { + local _pacman="$1" + echo -n "pacapt($_pacman): available operations:" + # shellcheck disable=2016 + $GREP -E "^${_pacman}_[^ \\t]+\\(\\)" "$0" \ + | $AWK -F '(' '{print $1}' \ + | sed -e "s/${_pacman}_//g" \ + | while read -r O; do + echo -n " $O" + done + echo +} diff --git a/lib/00_external.sh b/lib/00_external.sh new file mode 100644 index 00000000..a94efa81 --- /dev/null +++ b/lib/00_external.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash + +# Purpose : Provide some basic settings for external package managers +# Author : Ky-Anh Huynh +# License : MIT +# Date : 2018 July 26th +# Ref. : https://github.com/icy/pacapt/issues/106 + +export _SUPPORTED_EXTERNALS=" + :conda + :tlmgr + :texlive + :gem + :npm + :pip +" +readonly _SUPPORTED_EXTERNALS + +_PACMAN_found_from_script_name() { + local _tmp_name= + local _pacman= + + _tmp_name="${BASH_SOURCE[0]:-?}" + if [[ "$_tmp_name" == "?" ]]; then + _error "Unable to get script name." + return 1 + fi + + _tmp_name="${_tmp_name##*/}" # base name (remove everything before the last `/`) + _tmp_name="${_tmp_name%.*}" # remove extension if any (remove everything from the last `.`) + _pacman="${_tmp_name##*-}" # remove every thing before the last `-` + + if grep -Eq -e ":$_pacman[[:space:]]*" <<< "$_SUPPORTED_EXTERNALS"; then + export _PACMAN="$_pacman" + return 0 + else + export _PACMAN="" + return 1 + fi +} diff --git a/lib/apk.sh b/lib/apk.sh new file mode 100644 index 00000000..b3f80d19 --- /dev/null +++ b/lib/apk.sh @@ -0,0 +1,125 @@ +#!/bin/bash + +# Purpose: Support next-generation Alpine Linux apk package manager +# Author : Carl X. Su +# Cuong Manh Le +# License: Fair license (http://www.opensource.org/licenses/fair) +# Source : http://github.com/icy/pacapt/ + +# Copyright (C) 2016 CuongLM +# +# Usage of the works is permitted provided that this instrument is +# retained with the works, so that any entity that uses the works is +# notified of this instrument. +# +# DISCLAIMER: THE WORKS ARE WITHOUT WARRANTY. + +_apk_init() { + : +} + +# apk_Q may _not_implemented +apk_Q() { + if [[ -z "$_TOPT" ]]; then + apk info + else + _not_implemented + fi +} + +apk_Qi() { + apk info -a -- "$@" +} + +apk_Ql() { + apk info -L -- "$@" +} + +apk_Qo() { + apk info --who-owns -- "$@" +} + +apk_Qs() { + apk info -- "*${*}*" +} + +apk_Qu() { + apk version -l '<' +} + +apk_R() { + apk del -- "$@" +} + +apk_Rn() { + apk del --purge -- "$@" +} + +apk_Rns() { + apk del --purge -r -- "$@" +} + +apk_Rs() { + apk del -r -- "$@" +} + +apk_S() { + case ${_EOPT} in + # Download only + ("fetch") shift + apk fetch -- "$@" ;; + (*) apk add $_TOPT -- "$@" ;; + esac +} + +apk_Sc() { + apk cache -v clean +} + +apk_Scc() { + rm -rf /var/cache/apk/* +} + +apk_Sccc() { + apk_Scc +} + +apk_Si() { + apk_Qi "$@" +} + +apk_Sii() { + apk info -r -- "$@" +} + +apk_Sl() { + apk search -v -- "$@" +} + +apk_Ss() { + apk_Sl "$@" +} + +apk_Su() { + apk upgrade +} + +apk_Suy() { + if [ "$#" -gt 0 ]; then + apk add -U -u -- "$@" + else + apk upgrade -U -a + fi +} + +apk_Sy() { + apk update +} + +apk_Sw() { + apk fetch -- "$@" +} + +apk_U() { + apk add --allow-untrusted -- "$@" +} diff --git a/lib/cave.sh b/lib/cave.sh new file mode 100644 index 00000000..7936215d --- /dev/null +++ b/lib/cave.sh @@ -0,0 +1,156 @@ +#!/bin/bash + +# Purpose: Gentoo (+ Paludis) / Exherbo support +# Author : Somasis +# License: Fair license (http://www.opensource.org/licenses/fair) +# Source : http://github.com/somasis/pacapt/ + +# Copyright (C) 2014 Somasis +# +# Usage of the works is permitted provided that this instrument is +# retained with the works, so that any entity that uses the works is +# notified of this instrument. +# +# DISCLAIMER: THE WORKS ARE WITHOUT WARRANTY. + +# cave uses asterisks pretty liberally, this is for output parsing correctness +_cave_init() { + shopt -u globstar +} + +cave_Q() { + if [[ "$_TOPT" == "q" ]]; then + cave show -f "${@:-world}" \ + | grep -v '^$' + else + cave show -f "${@:-world}" + fi +} + +cave_Qi() { + cave show "$@" +} + +cave_Ql() { + if [[ -n "$*" ]]; then + cave contents "$@" + return + fi + + cave show -f "${@:-world}" \ + | grep -v '^$' \ + | while read -r _pkg; do + if [[ "$_TOPT" == "q" ]]; then + cave --color no contents "$_pkg" + else + cave contents "$_pkg" + fi + done +} + +cave_Qo() { + cave owner "$@" +} + +cave_Qp() { + _not_implemented +} + +cave_Qu() { + if [[ -z "$*" ]];then + cave resolve -c world \ + | grep '^u.*' \ + | while read -r _pkg; do + echo "$_pkg" | cut -d'u' -f2- + done + else + cave resolve -c world \ + | grep '^u.*' \ + | grep -- "$@" + fi +} + +cave_Qs() { + cave show -f world | grep -- "$@" +} + +cave_Rs() { + if [[ "$_TOPT" == "" ]]; then + cave uninstall -r "$@" \ + && echo "Control-C to stop uninstalling..." \ + && sleep 2s \ + && cave uninstall -xr "$@" + else + cave purge "$@" \ + && echo "Control-C to stop uninstalling (+ dependencies)..." \ + && sleep 2s \ + && cave purge -x "$@" + fi +} + +cave_Rn() { + _not_implemented +} + +cave_Rns() { + _not_implemented +} + +cave_R() { + cave uninstall "$@" \ + && echo "Control-C to stop uninstalling..." \ + && sleep 2s \ + && cave uninstall -x "$@" +} + +cave_Si() { + cave show "$@" +} + +cave_Suy() { + cave sync && cave resolve -c "${@:-world}" \ + && echo "Control-C to stop upgrading..." \ + && sleep 2s \ + && cave resolve -cx "${@:-world}" +} + +cave_Su() { + cave resolve -c "$@" \ + && echo "Control-C to stop upgrading..." \ + && sleep 2s \ + && cave resolve -cx "$@" +} + +cave_Sy() { + cave sync "$@" +} + +cave_Ss() { + cave search "$@" +} + +cave_Sc() { + cave fix-cache "$@" +} + +cave_Scc() { + cave fix-cache "$@" +} + +# cave_Sccc _not_implemented +cave_Sccc() { + #rm -fv /var/cache/paludis/* + _not_implemented +} + +cave_S() { + cave resolve $_TOPT "$@" \ + && echo "Control-C to stop installing..." \ + && sleep 2s \ + && cave resolve -x $_TOPT "$@" +} + +# cave_U _not_implemented +cave_U() { + _not_implemented +} diff --git a/lib/conda.sh b/lib/conda.sh new file mode 100644 index 00000000..96320085 --- /dev/null +++ b/lib/conda.sh @@ -0,0 +1,51 @@ +#!/usr/bin/env bash + +# Purpose : conda support +# Author : Antony Lee +# License : MIT +# Date : September 4, 2018 + +# Copyright (C) 2018 Antony Lee +# +# Usage of the works is permitted provided that this instrument is +# retained with the works, so that any entity that uses the works is +# notified of this instrument. +# +# DISCLAIMER: THE WORKS ARE WITHOUT WARRANTY. + +_conda_init() { + : +} + +conda_Q() { + if [[ $# -gt 0 ]]; then + conda list "$(python -c 'import sys; print("^" + "|".join(sys.argv[1:]) + "$")' "$@")" + else + conda list + fi +} + +conda_R() { + conda remove "$@" +} + +conda_S() { + conda install "$@" +} + +conda_Sc() { + conda clean --all "$@" +} + +conda_Si() { + conda search "$@" --info +} + +conda_Ss() { + conda search "*$@*" +} + +conda_Suy() { + conda update --all "$@" +} + diff --git a/lib/dnf.sh b/lib/dnf.sh new file mode 100644 index 00000000..ecd98e6f --- /dev/null +++ b/lib/dnf.sh @@ -0,0 +1,125 @@ +#!/bin/bash + +# Purpose: Support next-generation Yum package manager +# Author : Severus +# License: Fair license (http://www.opensource.org/licenses/fair) +# Source : http://github.com/icy/pacapt/ + +# Copyright (C) 2015 Severus +# +# Usage of the works is permitted provided that this instrument is +# retained with the works, so that any entity that uses the works is +# notified of this instrument. +# +# DISCLAIMER: THE WORKS ARE WITHOUT WARRANTY. + +_dnf_init() { + : +} + +dnf_S() { + dnf install $_TOPT "$@" +} + +dnf_Sc() { + dnf clean expire-cache "$@" +} + +dnf_Scc() { + dnf clean packages "$@" +} + +dnf_Sccc() { + dnf clean all "$@" +} + +dnf_Si() { + dnf info "$@" +} + +dnf_Sg() { + if [[ $# -gt 0 ]]; then + dnf group info "$@" + else + dnf group list + fi +} + +dnf_Sl() { + dnf list available "$@" +} + +dnf_Ss() { + dnf search "$@" +} + +dnf_Su() { + dnf upgrade "$@" +} + +dnf_Suy() { + dnf upgrade "$@" +} + +dnf_Sw() { + dnf download "$@" +} + +dnf_Sy() { + dnf clean expire-cache && dnf check-update +} + +# dnf_Q may _not_implemented +dnf_Q() { + if [[ "$_TOPT" == "q" ]]; then + rpm -qa --qf "%{NAME}\\n" + elif [[ "$_TOPT" == "" ]]; then + rpm -qa --qf "%{NAME} %{VERSION}\\n" + else + _not_implemented + fi +} + +dnf_Qc() { + rpm -q --changelog "$@" +} + +dnf_Qe() { + dnf repoquery --userinstalled "$@" +} + +dnf_Qi() { + dnf info "$@" +} + +dnf_Ql() { + rpm -ql "$@" +} + +dnf_Qm() { + dnf list extras +} + +dnf_Qo() { + rpm -qf "$@" +} + +dnf_Qp() { + rpm -qp "$@" +} + +dnf_Qs() { + rpm -qa "*${*}*" +} + +dnf_Qu() { + dnf list updates "$@" +} + +dnf_R() { + dnf remove "$@" +} + +dnf_U() { + dnf install "$@" +} diff --git a/lib/dpkg.sh b/lib/dpkg.sh new file mode 100644 index 00000000..907faf62 --- /dev/null +++ b/lib/dpkg.sh @@ -0,0 +1,158 @@ +#!/bin/bash + +# Purpose: Debian / Ubuntu support +# Author : Anh K. Huynh +# License: Fair license (http://www.opensource.org/licenses/fair) +# Source : http://github.com/icy/pacapt/ + +# Copyright (C) 2010 - 2014 Anh K. Huynh +# +# Usage of the works is permitted provided that this instrument is +# retained with the works, so that any entity that uses the works is +# notified of this instrument. +# +# DISCLAIMER: THE WORKS ARE WITHOUT WARRANTY. + +_dpkg_init() { + : +} + +# dpkg_Q may _not_implemented +dpkg_Q() { + if [[ "$_TOPT" == "q" ]]; then + dpkg -l \ + | grep -E '^[hi]i' \ + | awk '{print $2}' + elif [[ "$_TOPT" == "" ]]; then + dpkg -l "$@" \ + | grep -E '^[hi]i' + else + _not_implemented + fi +} + +dpkg_Qi() { + dpkg-query -s "$@" +} + +dpkg_Ql() { + if [[ -n "$*" ]]; then + dpkg-query -L "$@" + return + fi + + dpkg -l \ + | grep -E '^[hi]i' \ + | awk '{print $2}' \ + | while read -r _pkg; do + if [[ "$_TOPT" == "q" ]]; then + dpkg-query -L "$_pkg" + else + dpkg-query -L "$_pkg" \ + | while read -r _line; do + echo "$_pkg $_line" + done + fi + done +} + +dpkg_Qo() { + dpkg-query -S "$@" +} + +dpkg_Qp() { + dpkg-deb -I "$@" +} + +dpkg_Qu() { + apt-get upgrade --trivial-only "$@" +} + +# NOTE: Some field is available for dpkg >= 1.16.2 +# NOTE: Debian:Squeeze has dpkg < 1.16.2 +dpkg_Qs() { + # dpkg >= 1.16.2 dpkg-query -W -f='${db:Status-Abbrev} ${binary:Package}\t${Version}\t${binary:Summary}\n' + dpkg-query -W -f='${Status} ${Package}\t${Version}\t${Description}\n' \ + | grep -E '^((hold)|(install)|(deinstall))' \ + | sed -r -e 's#^(\w+ ){3}##g' \ + | grep -Ei "${@:-.}" +} + +# dpkg_Rs may _not_implemented +dpkg_Rs() { + if [[ "$_TOPT" == "" ]]; then + apt-get autoremove "$@" + else + _not_implemented + fi +} + +dpkg_Rn() { + apt-get purge "$@" +} + +dpkg_Rns() { + apt-get --purge autoremove "$@" +} + +dpkg_R() { + apt-get remove "$@" +} + +dpkg_Si() { + apt-cache show "$@" +} + +dpkg_Suy() { + apt-get update \ + && apt-get upgrade "$@" \ + && apt-get dist-upgrade "$@" +} + +dpkg_Su() { + apt-get upgrade "$@" \ + && apt-get dist-upgrade "$@" +} + +# See also https://github.com/icy/pacapt/pull/78 +# This `-w` option is implemented in `00_core/_translate_w` +# +# dpkg_Sw() { +# apt-get --download-only install "$@" +# } + +# FIXME: Should we remove "$@"? +dpkg_Sy() { + apt-get update "$@" +} + +dpkg_Ss() { + apt-cache search "$@" +} + +dpkg_Sc() { + apt-get clean "$@" +} + +dpkg_Scc() { + apt-get autoclean "$@" +} + +dpkg_S() { + apt-get install $_TOPT "$@" +} + +dpkg_U() { + dpkg -i "$@" +} + +dpkg_Sii() { + apt-cache rdepends "$@" +} + +dpkg_Sccc() { + rm -fv /var/cache/apt/*.bin + rm -fv /var/cache/apt/archives/*.* + rm -fv /var/lib/apt/lists/*.* + apt-get autoclean +} diff --git a/lib/help.txt b/lib/help.txt new file mode 100644 index 00000000..92141628 --- /dev/null +++ b/lib/help.txt @@ -0,0 +1,99 @@ +NAME + pacapt - An `ArchLinux`'s pacman-like wrapper for many package managers. + +SYNTAX + + $ pacapt + +BASIC OPTIONS + + -h or --help print this help message + -P print supported operations + -V print version information + +SYSGET STYLE OPERATIONS + + update Update package database + upgrade Upgrade system + install Install some packages + search Search some package + remove Remove some packages + autoremove Remove orphans (WIP; may not work correctly) + clean Clean package manager caches + +PACMAN STYLE OPERATIONS + + Query + -Q list all installed packages + -Qc show package's changelog + -Qe [] only list explicitly installed packages + -Qi print package status + -Ql list package's files + -Qm list installed packages that aren't available + in any installation source + -Qo query package that provides + -Qp query a package file (don't use package database) + -Qs search for installed package + + Synchronize + -S install package(s) + -Sg list groups + -Sg list packages in group + -Ss search for packages + -Su upgrade the system + -Sy update package database + -Suy update package database, then upgrade the system + + Remove / Clean up + -R remove some packages + -Sc delete old downloaded packages + -Scc delete all downloaded packages + -Sccc clean variant files. + (debian) See also http://dragula.viettug.org/blogs/646 + + Upgrade + -U upgrade or add package from local file path (or remote uri) + +OPTIONS + + -w download packages but don't install them + --noconfirm don't wait for user's confirmation + +EXAMPLES + + 1. To install a package from Debian's backports repository + $ pacapt -S foobar -t lenny-backports + $ pacapt -S -- -t lenny-backports foobar + + 2. To update package database and then update your system + $ pacapt -Syu + + 3. To download a package without installing it + $ pacapt -Sw foobar + + +ENVIRONMENT + + PACAPT_DEBUG + + This is useful for debugging purpose. The variable can be set to `auto` + or any valid packager. For example, on `Debian` system the two following + commands are the same and they will print out what the script would do: + + PACAPT_DEBUG=auto pacman -Su + PACAPT_DEBUG=dpkg pacman -Su + +NOTES + + When being executed on Arch-based system, the tool simply invokes + the system package manager (`/usr/bin/pacman`). + + Though you can specify option by its own word, for example, + $ pacapt -S -y -u + + it's always the best to combine them + $ pacapt -Syu + +READMORE + + Please visit https://github.com/icy/pacapt. diff --git a/lib/homebrew.sh b/lib/homebrew.sh new file mode 100644 index 00000000..99c70841 --- /dev/null +++ b/lib/homebrew.sh @@ -0,0 +1,162 @@ +#!/bin/bash + +# Purpose: Homebrew support +# Author : James Pearson +# License: Fair license (http://www.opensource.org/licenses/fair) +# Source : http://github.com/icy/pacapt/ + +# Copyright (C) 2010 - 2014 James Pearson +# +# Usage of the works is permitted provided that this instrument is +# retained with the works, so that any entity that uses the works is +# notified of this instrument. +# +# DISCLAIMER: THE WORKS ARE WITHOUT WARRANTY. + +_homebrew_init() { + : +} + +homebrew_Qi() { + brew info "$@" +} + +homebrew_Ql() { + brew list "$@" +} + +homebrew_Qo() { + local pkg prefix cellar + + # FIXME: What happens if the file is not exectutable? + cd "$(dirname -- "$(which "$@")")" || return + pkg="$(pwd -P)/$(basename -- "$@")" + prefix="$(brew --prefix)" + cellar="$(brew --cellar)" + + for package in $cellar/*; do + files=(${package}/*/${pkg/#$prefix\//}) + if [[ -e "${files[${#files[@]} - 1]}" ]]; then + echo "${package/#$cellar\//}" + break + fi + done +} + +homebrew_Qc() { + brew log "$@" +} + +homebrew_Qu() { + brew outdated | grep "$@" +} + +homebrew_Qs() { + brew list | grep "$@" +} + +# homebrew_Q may _not_implemented +homebrew_Q() { + if [[ "$_TOPT" == "" ]]; then + if [[ "$*" == "" ]]; then + brew list + else + brew list | grep "$@" + fi + else + _not_implemented + fi +} + +homebrew_Rs() { + which join > /dev/null + if [ $? -ne 0 ]; then + _die "pacapt: join binary does not exist in system." + fi + + which sort > /dev/null + if [ $? -ne 0 ]; then + _die "pacapt: sort binary does not exist in system." + fi + + if [[ "$@" == "" ]]; then + _die "pacapt: ${FUNCNAME[0]} requires arguments" + fi + + for _target in $@; + do + brew rm $_target + + while [ "$(join <(sort <(brew leaves)) <(sort <(brew deps $_target)))" != "" ] + do + brew rm $(join <(sort <(brew leaves)) <(sort <(brew deps $_target))) + done + done + +} + +homebrew_R() { + brew remove "$@" +} + +homebrew_Si() { + brew info "$@" +} + +homebrew_Suy() { + brew update \ + && brew upgrade "$@" +} + +homebrew_Su() { + brew upgrade "$@" +} + +homebrew_Sy() { + brew update "$@" +} + +homebrew_Ss() { + brew search "$@" +} + +homebrew_Sc() { + brew cleanup "$@" +} + +homebrew_Scc() { + brew cleanup -s "$@" +} + +homebrew_Sccc() { + # See more discussion in + # https://github.com/icy/pacapt/issues/47 + + local _dcache + + _dcache="$(brew --cache)" + case "$_dcache" in + ""|"/"|" ") + _error "${FUNCNAME[0]}: Unable to delete '$_dcache'." + ;; + + *) + # FIXME: This is quite stupid!!! But it's an easy way + # FIXME: to avoid some warning from #shellcheck. + # FIXME: Please note that, $_dcache is not empty now. + rm -rf "${_dcache:-/x/x/x/x/x/x/x/x/x/x/x//x/x/x/x/x/}/" + ;; + esac +} + +homebrew_S() { + 2>&1 brew install $_TOPT "$@" \ + | awk '{print; if ($0 ~ /brew cask install/) { exit(126); }}' + ret=( ${PIPESTATUS[*]} ) + if [[ "${ret[1]}" == 126 ]]; then + echo >&2 ":: Now trying with brew/cask..." + brew cask install $_TOPT "$@" + else + return "${ret[0]}" + fi +} diff --git a/lib/macports.sh b/lib/macports.sh new file mode 100644 index 00000000..fdfe1cac --- /dev/null +++ b/lib/macports.sh @@ -0,0 +1,85 @@ +#!/bin/bash + +# Purpose: Macports support +# Author : 10sr +# License: Fair license (http://www.opensource.org/licenses/fair) +# Source : http://github.com/icy/pacapt/ + +# Copyright (C) 2010 - 2014 10sr +# +# Usage of the works is permitted provided that this instrument is +# retained with the works, so that any entity that uses the works is +# notified of this instrument. +# +# DISCLAIMER: THE WORKS ARE WITHOUT WARRANTY. + +_macports_init() { + : +} + +macports_Ql() { + port contents "$@" +} + +macports_Qo() { + port provides "$@" +} + +macports_Qc() { + port log "$@" +} + +macports_Qu() { + port outdated "$@" +} + +# macports_Rs may _not_implemented +macports_Rs() { + if [[ "$_TOPT" == "" ]]; then + port uninstall --follow-dependencies "$@" + else + _not_implemented + fi +} + +macports_R() { + port uninstall "$@" +} + +macports_Si() { + port info "$@" +} + +macports_Suy() { + port selfupdate \ + && port upgrade outdated "$@" +} + +macports_Su() { + port upgrade outdate "$@" +} + +# FIXME: update or sync? +macports_Sy() { + port selfupdate "$@" +} + +macports_Ss() { + port search "$@" +} + +macports_Sc() { + port clean --all inactive "$@" +} + +macports_Scc() { + port clean --all installed "$@" +} + +macports_S() { + if [[ "$_TOPT" == "fetch" ]]; then + port patch "$@" + else + port install "$@" + fi +} diff --git a/lib/pkg_tools.sh b/lib/pkg_tools.sh new file mode 100644 index 00000000..0ce846a9 --- /dev/null +++ b/lib/pkg_tools.sh @@ -0,0 +1,143 @@ +#!/bin/bash + +# Purpose: OpenBSD support +# Author : Somasis +# License: Fair license (http://www.opensource.org/licenses/fair) +# Source : http://github.com/somasis/pacapt + +# Copyright (C) 2014 Somasis +# +# Usage of the works is permitted provided that this instrument is +# retained with the works, so that any entity that uses the works is +# notified of this instrument. +# +# DISCLAIMER: THE WORKS ARE WITHOUT WARRANTY. + +_pkg_tools_init() { + : +} + +pkg_tools_Qi() { + # disable searching mirrors for packages + export PKG_PATH= + pkg_info "$@" +} + +pkg_tools_Ql() { + export PKG_PATH= + pkg_info -L "$@" +} + +pkg_tools_Qo() { + export PKG_PATH= + pkg_info -E "$@" +} + +pkg_tools_Qp() { + _not_implemented +} + +pkg_tools_Qu() { + export PKG_PATH= + pkg_add -u "$@" +} + +# pkg_tools_Q may _not_implemented +pkg_tools_Q() { + export PKG_PATH= + # the dash after the pkg name is so we don't catch partial matches + # because all packages in openbsd have the format 'pkgname-pkgver' + if [[ "$_TOPT" == "q" && ! -z "$*" ]]; then + pkg_info -q | grep "^${*}-" + elif [[ "$_TOPT" == "q" && -z "$*" ]];then + pkg_info -q + elif [[ "$_TOPT" == "" && ! -z "$*" ]]; then + pkg_info | grep "^${*}-" + elif [[ "$_TOPT" == "" && -z "$*" ]];then + pkg_info + else + _not_implemented + fi +} + +# pkg_tools_Rs may _not_implemented +pkg_tools_Rs() { + if [[ "$_TOPT" == "" ]]; then + pkg_delete -D dependencies "$@" + else + _not_implemented + fi +} + +# pkg_tools_rn may _not_implemented +pkg_tools_Rn() { + if [[ "$_TOPT" == "" ]];then + pkg_delete -c "$@" + else + _not_implemented + fi +} + +# pkg_tools_rns _not_implemented +pkg_tools_Rns() { + _not_implemented +} + +pkg_tools_R() { + pkg_delete "$@" +} + +pkg_tools_Si() { + pkg_info "$@" +} + +pkg_tools_Sl() { + pkg_info -L "$@" +} + +pkg_tools_Suy() { + # pkg_tools doesn't really have any concept of a database + # there's actually not really any database to update, so + # this function is mostly just for convienience since on arch + # doing -Su is normally a bad thing to do since it's a partial upgrade + + pkg_tools_Su "$@" +} + +pkg_tools_Su() { + pkg_add -u "$@" +} + +# pkg_tools_Sy _not_implemented +pkg_tools_Sy() { + _not_implemented +} + +# pkg_tools_Ss may _not_implemented +pkg_tools_Ss() { + if [[ -z "$*" ]];then + _not_implemented + else + pkg_info -Q "$@" + fi +} + +pkg_tools_Sc() { + # by default no cache directory is used + if [[ -z "$PKG_CACHE" ]];then + echo "You have no cache directory set, set \$PKG_CACHE for a cache directory." + elif [[ ! -d "$PKG_CACHE" ]];then + echo "You have a cache directory set, but it does not exist. Create \"$PKG_CACHE\"." + else + _removing_is_dangerous "rm -rf $PKG_CACHE/*" + fi +} + +# pkg_tools_Scc _not_implemented +pkg_tools_Scc() { + _not_implemented +} + +pkg_tools_S() { + pkg_add "$@" +} diff --git a/lib/pkgng.sh b/lib/pkgng.sh new file mode 100644 index 00000000..ae471e0a --- /dev/null +++ b/lib/pkgng.sh @@ -0,0 +1,102 @@ +#!/bin/bash + +# Purpose: FreeBSD support +# Author : Konrad Borowski +# Date : Dec 18 2013 +# License: Fair license (http://www.opensource.org/licenses/fair) +# Source : http://github.com/icy/pacapt/ +# Pull : https://github.com/icy/pacapt/pull/25 +# Note : The pull request is applicable to `master` branch. +# Anh K. Huynh slightly modified the pull request to +# use them on the `ng` branch (on May 05 2014) + +# Copyright (C) 2013 - 2014 Konrad Borowski +# +# Usage of the works is permitted provided that this instrument is +# retained with the works, so that any entity that uses the works is +# notified of this instrument. +# +# DISCLAIMER: THE WORKS ARE WITHOUT WARRANTY. + +_pkgng_init() { + : +} + +pkgng_Qi() { + pkg info "$@" +} + +pkgng_Ql() { + pkg info -l "$@" +} + +pkgng_Qo() { + pkg which "$@" +} + +pkgng_Qp() { + pkg query -F "$@" '%n %v' +} + +pkgng_Qu() { + pkg upgrade -n "$@" +} + +pkgng_Q() { + if [[ "$_TOPT" == "q" ]]; then + pkg query '%n' "$@" + elif [[ "$_TOPT" == "" ]]; then + pkg query '%n %v' "$@" + else + _not_implemented + fi +} + +pkgng_Rs() { + if [[ "$_TOPT" == "" ]]; then + pkg remove "$@" + pkg autoremove + else + _not_implemented + fi +} + +pkgng_R() { + pkg remove "$@" +} + +pkgng_Si() { + pkg search -S name -ef "$@" +} + +pkgng_Suy() { + pkg upgrade "$@" +} + +pkgng_Su() { + pkg upgrade -U "$@" +} + +pkgng_Sy() { + pkg update "$@" +} + +pkgng_Ss() { + pkg search "$@" +} + +pkgng_Sc() { + pkg clean "$@" +} + +pkgng_Scc() { + pkg clean -a "$@" +} + +pkgng_S() { + if [[ "$_TOPT" == "fetch" ]]; then + pkg fetch "$@" + else + pkg install "$@" + fi +} diff --git a/lib/portage.sh b/lib/portage.sh new file mode 100644 index 00000000..d1c6c370 --- /dev/null +++ b/lib/portage.sh @@ -0,0 +1,136 @@ +#!/bin/bash + +# Purpose: Gentoo support +# Author : Hà-Dương Nguyễn +# License: Fair license (http://www.opensource.org/licenses/fair) +# Source : http://github.com/icy/pacapt/ + +# Copyright (C) 2010 - 2014 Hà-Dương Nguyễn +# +# Usage of the works is permitted provided that this instrument is +# retained with the works, so that any entity that uses the works is +# notified of this instrument. +# +# DISCLAIMER: THE WORKS ARE WITHOUT WARRANTY. + +_portage_init() { + : +} + +portage_Qi() { + emerge --info "$@" +} + +portage_Ql() { + if [[ -x '/usr/bin/qlist' ]]; then + qlist "$@" + elif [[ -x '/usr/bin/equery' ]]; then + equery files "$@" + else + _error "'portage-utils' or 'gentoolkit' package is required to perform this opreation." + fi +} + +portage_Qo() { + if [[ -x '/usr/bin/equery' ]]; then + equery belongs "$@" + else + _error "'gentoolkit' package is required to perform this operation." + fi +} + +portage_Qc() { + emerge -p --changelog "$@" +} + +# FIXME: may not be correct +portage_Qu() { + emerge -uvN "$@" +} + +portage_Q() { + if [[ "$_TOPT" == "" ]]; then + if [[ -x '/usr/bin/eix' ]]; then + eix -I "$@" + elif [[ -x '/usr/bin/equery' ]]; then + equery list -i "$@" + else + LS_COLORS=never \ + ls -1 -d /var/db/pkg/*/* + fi + else + _not_implemented + fi +} + +portage_Rs() { + if [[ "$_TOPT" == "" ]]; then + emerge --depclean world "$@" + else + _not_implemented + fi +} + +portage_R() { + emerge --depclean "@" +} + +portage_Si() { + emerge --info "$@" +} + +portage_Suy() { + if [[ -x '/usr/bin/layman' ]]; then + layman --sync-all \ + && emerge --sync \ + && emerge -auND world "$@" + else + emerge --sync \ + && emerge -uND world "$@" + fi +} + +portage_Su() { + emerge -uND world "$@" +} + +portage_Sy() { + if [[ -x "/usr/bin/layman" ]]; then + layman --sync-all \ + && emerge --sync "$@" + else + emerge --sync "$@" + fi +} + +portage_Ss() { + if [[ -x "/usr/bin/eix" ]]; then + eix "$@" + else + emerge --search "$@" + fi +} + +portage_Sc() { + if [[ -x "/usr/bin/eclean-dist" ]]; then + eclean-dist -d -t1m -s50 -f "$@" + else + _error "'gentoolkit' package is required to perform this operation." + fi +} + +portage_Scc() { + if [[ -x "/usr/bin/eclean" ]]; then + eclean -i distfiles "$@" + else + _error "'gentoolkit' package is required to perform this operation." + fi +} + +portage_Sccc() { + rm -fv /usr/portage/distfiles/*.* +} + +portage_S() { + emerge "$@" +} diff --git a/lib/sun_tools.sh b/lib/sun_tools.sh new file mode 100644 index 00000000..45d927ae --- /dev/null +++ b/lib/sun_tools.sh @@ -0,0 +1,59 @@ +#!/bin/bash + +# Purpose: SunOS support +# Author : Daniel YC Lin +# License: Fair license (http://www.opensource.org/licenses/fair) +# Source : http://github.com/dlintw/pacapt + +# Copyright (C) 2015 Daniel YC Lin +# +# Usage of the works is permitted provided that this instrument is +# retained with the works, so that any entity that uses the works is +# notified of this instrument. +# +# DISCLAIMER: THE WORKS ARE WITHOUT WARRANTY. + +_sun_tools_init() { + # The purpose of `if` is to make sure this function + # can be invoked on other system (Linux, BSD). + if [[ "$(uname)" == "SunOS" ]]; then + export GREP=/usr/xpg4/bin/grep + export AWK=nawk + fi +} + +sun_tools_Qi() { + pkginfo -l "$@" +} + +sun_tools_Ql() { + pkginfo -l "$@" +} + +sun_tools_Qo() { + $GREP "$@" /var/sadm/install/contents +} + +sun_tools_Qs() { + pkginfo | $GREP -i "$@" +} + +sun_tools_Q() { + # the dash after the pkg name is so we don't catch partial matches + # because all packages in openbsd have the format 'pkgname-pkgver' + if [[ "$_TOPT" == "q" && ! -z "$*" ]]; then + pkginfo | $GREP "$@" + elif [[ "$_TOPT" == "q" && -z "$*" ]]; then + pkginfo + else + pkginfo "$@" + fi +} + +sun_tools_R() { + pkgrm "$@" +} + +sun_tools_U() { + pkgadd "$@" +} diff --git a/lib/swupd.sh b/lib/swupd.sh new file mode 100644 index 00000000..ed6ab240 --- /dev/null +++ b/lib/swupd.sh @@ -0,0 +1,55 @@ +#!/bin/bash + +# Purpose: Clear Linux support +# Author : Dmitry Kudriavtsev +# License: Fair license (http://www.opensource.org/licenses/fair) +# Source : http://github.com/dkudriavtsev/pacapt/ + +# Copyright (C) 2014 Dmitry Kudriavtsev +# +# Usage of the works is permitted provided that this instrument is +# retained with the works, so that any entity that uses the works is +# notified of this instrument. +# +# DISCLAIMER: THE WORKS ARE WITHOUT WARRANTY. + +_swupd_init() { + : +} + +swupd_Qk() { + swupd verify "$@" +} + +swupd_Qo() { + swupd search "$@" +} + +swupd_Qs() { + swupd search "$@" +} + +swupd_R() { + swupd bundle-remove "$@" +} + +swupd_Suy() { + swupd update +} + +swupd_Su() { + swupd update +} + +swupd_Sy() { + swupd search -i + swupd update +} + +swupd_Ss() { + swupd search "$@" +} + +swupd_S() { + swupd bundle-add "$@" +} diff --git a/lib/tazpkg.sh b/lib/tazpkg.sh new file mode 100644 index 00000000..622219c0 --- /dev/null +++ b/lib/tazpkg.sh @@ -0,0 +1,142 @@ +#!/bin/bash + +# Purpose: Provide Slitaz support for pacapt script +# Author : Anh K. Huynh +# Date : 2016 July 08th +# License: MIT + +_tazpkg_init() { + : +} + +# tarpkg_Q may _not_implemented +tazpkg_Q() { + if [[ "$_TOPT" == "q" ]]; then + tazpkg list "$@" \ + | awk '{ if (NF == 2 || NF == 3) { print $1; }}' + elif [[ "$_TOPT" == "" ]]; then + tazpkg list "$@" + else + _not_implemented + fi +} + +tazpkg_Qi() { + tazpkg info "$@" +} + +# tarpkg_Ql may _not_implemented +tazpkg_Ql() { + if [[ -z "$*" ]]; then + _not_implemented + return + fi + + if [[ "$_TOPT" == "q" ]]; then + { + tazpkg list-files "$@" + tazpkg list-config "$@" + } \ + | grep ^/ + else + tazpkg list-files "$@" + tazpkg list-config "$@" + fi +} + +tazpkg_Sy() { + tazpkg recharge +} + +tazpkg_Su() { + tazpkg up +} + +tazpkg_Suy() { + tazpkg_Sy \ + && tazpkg_Su +} + +tazpkg_S() { + local _forced="" + + if grep -q -- "--forced" <<<"$*"; then + _forced="--forced" + fi + + while (( $# )); do + if [[ "$1" == "--forced" ]]; then + _forced="--forced" + shift + continue + fi + + tazpkg get-install "$1" $_forced + shift + done +} + +tazpkg_R() { + local _auto="" + + if grep -q -- "--auto" <<<"$*"; then + _auto="--auto" + fi + + while (( $# )); do + if [[ "$1" == "--auto" ]]; then + _auto="--auto" + shift + continue + fi + + tazpkg remove "$1" $_auto + shift + done +} + +tazpkg_Sc() { + tazpkg clean-cache +} + +tazpkg_Scc() { + tazpkg clean-cache + cd /var/lib/tazpkg/ \ + && { + rm -fv \ + ./*.bak \ + ID \ + packages.* \ + files.list.* + } +} + +# Option: tazpkg search ... [option] +# -i: installed packages +# -l: available packages +tazpkg_Ss() { + tazpkg search "$@" +} + +tazpkg_Qo() { + tazpkg search-pkgname "$@" +} + +tazpkg_U() { + local _forced="" + + if grep -q -- "--forced" <<<"$*"; then + _forced="--forced" + fi + + while (( $# )); do + if [[ "$1" == "--forced" ]]; then + _forced="--forced" + shift + continue + fi + + tazpkg install "$1" $_forced + shift + done +} diff --git a/lib/tlmgr.sh b/lib/tlmgr.sh new file mode 100644 index 00000000..04d6df46 --- /dev/null +++ b/lib/tlmgr.sh @@ -0,0 +1,58 @@ +#!/usr/bin/env bash + +# Purpose : tlmgr support +# Author : Antony Lee +# License : MIT +# Date : July 26, 2018 + +# Copyright (C) 2018 Antony Lee +# +# Usage of the works is permitted provided that this instrument is +# retained with the works, so that any entity that uses the works is +# notified of this instrument. +# +# DISCLAIMER: THE WORKS ARE WITHOUT WARRANTY. + +_tlmgr_init() { + : +} + +tlmgr_Qi() { + tlmgr info --only-installed "$@" +} + +tlmgr_Qk() { + tlmgr check files +} + +tlmgr_Ql() { + tlmgr info --only-installed --list "$@" +} + +tlmgr_R() { + tlmgr remove "$@" +} + +tlmgr_S() { + tlmgr install "$@" +} + +tlmgr_Si() { + tlmgr info "$@" +} + +tlmgr_Sl() { + tlmgr info +} + +tlmgr_Ss() { + tlmgr search --global "$@" +} + +tlmgr_Suy() { + tlmgr update --all +} + +tlmgr_U() { + tlmgr install --file "$@" +} diff --git a/lib/yum.sh b/lib/yum.sh new file mode 100644 index 00000000..7561a6f3 --- /dev/null +++ b/lib/yum.sh @@ -0,0 +1,116 @@ +#!/bin/bash + +# Purpose: RedHat / Fedora Core support +# Author : Anh K. Huynh +# License: Fair license (http://www.opensource.org/licenses/fair) +# Source : http://github.com/icy/pacapt/ + +# Copyright (C) 2010 - 2014 Anh K. Huynh +# +# Usage of the works is permitted provided that this instrument is +# retained with the works, so that any entity that uses the works is +# notified of this instrument. +# +# DISCLAIMER: THE WORKS ARE WITHOUT WARRANTY. + +_yum_init() { + : +} + +yum_Q() { + if [[ "$_TOPT" == "q" ]]; then + rpm -qa --qf "%{NAME}\\n" + elif [[ "$_TOPT" == "" ]]; then + rpm -qa --qf "%{NAME} %{VERSION}\\n" + else + _not_implemented + fi +} + +yum_Qi() { + yum info "$@" +} + +yum_Qs() { + rpm -qa "*${*}*" +} + +yum_Ql() { + rpm -ql "$@" +} + +yum_Qo() { + rpm -qf "$@" +} + +yum_Qp() { + rpm -qp "$@" +} + +yum_Qc() { + rpm -q --changelog "$@" +} + +yum_Qu() { + yum list updates "$@" +} + +yum_Qm() { + yum list extras "$@" +} + +yum_Rs() { + if [[ "$_TOPT" == "" ]]; then + yum erase "$@" + else + _not_implemented + fi +} + +yum_R() { + yum erase "$@" +} + +yum_Si() { + yum info "$@" +} + +yum_Suy() { + yum update "$@" +} + +yum_Su() { + yum update "$@" +} + +yum_Sy() { + yum check-update "$@" +} + +yum_Ss() { + yum -C search "$@" +} + +yum_Sc() { + yum clean expire-cache "$@" +} + +yum_Scc() { + yum clean packages "$@" +} + +yum_Sccc() { + yum clean all "$@" +} + +yum_S() { + yum install $_TOPT "$@" +} + +yum_U() { + yum localinstall "$@" +} + +yum_Sii() { + yum resolvedep "$@" +} diff --git a/lib/zypper.sh b/lib/zypper.sh new file mode 100644 index 00000000..4eb04e89 --- /dev/null +++ b/lib/zypper.sh @@ -0,0 +1,170 @@ +#!/bin/bash + +# Purpose: OpenSUSE support +# Author : Anh K. Huynh +# License: Fair license (http://www.opensource.org/licenses/fair) +# Source : http://github.com/icy/pacapt/ + +# Copyright (C) 2010 - 2014 Anh K. Huynh +# +# Usage of the works is permitted provided that this instrument is +# retained with the works, so that any entity that uses the works is +# notified of this instrument. +# +# DISCLAIMER: THE WORKS ARE WITHOUT WARRANTY. + +_zypper_init() { + : +} + +zypper_Qc() { + rpm -q --changelog "$@" +} + +zypper_Qi() { + zypper info "$@" +} + +zypper_Ql() { + rpm -ql "$@" +} + +zypper_Qu() { + zypper list-updates "$@" +} + +zypper_Qm() { + zypper search -si "$@" \ + | grep 'System Packages' +} + +zypper_Qo() { + rpm -qf "$@" +} + +zypper_Qp() { + rpm -qip "$@" +} + +zypper_Qs() { + zypper search --installed-only "$@" +} + +zypper_Q() { + if [[ "$_TOPT" == "q" ]]; then + zypper search -i "$@" \ + | grep ^i \ + | awk '{print $3}' + elif [[ "$_TOPT" == "" ]]; then + zypper search -i "$@" + else + _not_implemented + fi +} + +zypper_Rs() { + if [[ "$_TOPT" == "s" ]]; then + zypper remove "$@" --clean-deps + else + _not_implemented + fi +} + +zypper_R() { + zypper remove "$@" +} + +zypper_Rn() { + # Remove configuration files + while read -r file; do + if [[ -f "$file" ]]; then + rm -fv "$file" + fi + done < <(rpm -ql "$@") + + # Now remove the package per-se + zypper remove "$@" +} + +zypper_Rs() { + if [[ "$_TOPT" == "s" ]]; then + zypper remove "$@" --clean-deps + else + _not_implemented + fi +} + +zypper_Rns() { + # Remove configuration files + while read -r file; do + if [[ -f "$file" ]]; then + rm -fv "$file" + fi + done < <(rpm -ql "$@") + + zypper remove "$@" --clean-deps +} + +zypper_Suy() { + zypper dup "$@" +} + +zypper_Sy() { + zypper refresh "$@" +} + +zypper_Sl() { + if [[ $# -eq 0 ]]; then + zypper pa -R + else + zypper pa -r "$@" + fi +} + +zypper_Ss() { + zypper search "$@" +} + +zypper_Su() { + zypper --no-refresh dup "$@" +} + +zypper_Sc() { + zypper clean "$@" +} + +zypper_Scc() { + zypper clean "$@" +} + +zypper_Sccc() { + # Not way to do this in zypper + _not_implemented +} + +zypper_Si() { + zypper info --requires "$@" +} + +zypper_Sii() { + # Ugly and slow, but does the trick + local packages= + + packages="$(zypper pa -R | cut -d \| -f 3 | tr -s '\n' ' ')" + for package in $packages; do + zypper info --requires "$package" \ + | grep -q "$@" && echo $package + done +} + +zypper_S() { + zypper install $_TOPT "$@" +} + +zypper_Sw() { + zypper install --download-only "$@" +} + +zypper_U() { + zypper install "$@" +} diff --git a/lib/zz_main.sh b/lib/zz_main.sh new file mode 100644 index 00000000..3a10ab33 --- /dev/null +++ b/lib/zz_main.sh @@ -0,0 +1,254 @@ +#!/bin/bash + +# Purpose: A wrapper for all Unix package managers +# Author : Anh K. Huynh +# License: Fair license (http://www.opensource.org/licenses/fair) +# Source : http://github.com/icy/pacapt/ + +# Copyright (C) 2010 - 2014 Anh K. Huynh +# +# Usage of the works is permitted provided that this instrument is +# retained with the works, so that any entity that uses the works is +# notified of this instrument. +# +# DISCLAIMER: THE WORKS ARE WITHOUT WARRANTY. + +set -u +unset GREP_OPTIONS + +: "${PACAPT_DEBUG=}" # Show what will be going +: "${GREP:=grep}" # Need to update in, e.g, _sun_tools_init +: "${AWK:=awk}" # Need to update in, e.g, _sun_tools_init + +_sun_tools_init # Dirty tricky patch for SunOS + +export PACAPT_DEBUG GREP AWK + +_POPT="" # primary operation +_SOPT="" # secondary operation +_TOPT="" # options for operations +_EOPT="" # extra options (directly given to package manager) + # these options will be translated by (_translate_all) method. +_PACMAN="" # name of the package manager + +_PACMAN_detect \ +|| _die "'pacapt' doesn't support your package manager." + +# FIXME: If `pacman-foo` is being used, `PACAPT_DEBUG` is still overwriting that. +if [[ -z "$PACAPT_DEBUG" ]]; then + [[ "$_PACMAN" != "pacman" ]] \ + || exec "/usr/bin/pacman" "$@" +elif [[ "$PACAPT_DEBUG" != "auto" ]]; then + _PACMAN="$PACAPT_DEBUG" +fi + +case "${1:-}" in +"update") shift; set -- -Sy "$@" ;; +"upgrade") shift; set -- -Su "$@" ;; +"install") shift; set -- -S "$@" ;; +"search") shift; set -- -Ss "$@" ;; +"remove") shift; set -- -R "$@" ;; +"autoremove") shift; set -- -Rs "$@" ;; +"clean") shift; set -- -Scc "$@" ;; +esac + +while :; do + _args="${1-}" + + [[ "${_args:0:1}" == "-" ]] || break + + case "${_args}" in + "--help") + _help + exit 0 + ;; + + "--noconfirm") + shift + _EOPT="$_EOPT:noconfirm:" + continue + ;; + + "-"|"--") + shift + break + ;; + esac + + i=1 + while [[ "$i" -lt "${#_args}" ]]; do + _opt="${_args:$i:1}" + (( i ++ )) + + case "$_opt" in + h) + _help + exit 0 + ;; + V) + _print_pacapt_version; + exit 0 + ;; + P) + _print_supported_operations "$_PACMAN" + exit 0 + ;; + + Q|S|R|U) + if [[ -n "$_POPT" && "$_POPT" != "$_opt" ]]; then + _error "Only one operation may be used at a time" + exit 1 + fi + _POPT="$_opt" + ;; + + # Comment 2015 May 26th: This part deals with the 2nd option. + # Most of the time, there is only one 2nd option. But some + # operation may need extra and/or duplicate (e.g, Sy <> Syy). + # + # See also + # + # * https://github.com/icy/pacapt/issues/13 + # + # This implementation works, but with a bug. #Rsn works + # but #Rns is translated to #Rn (incorrectly.) + # Thanks Huy-Ngo for this nice catch. + # + # FIXME: Please check pacman(8) to see if they are really 2nd operation + # + e|g|i|l|m|n|o|p|s) + if [[ "$_SOPT" == '' ]]; then + _SOPT="$_opt" + continue + fi + + # Understand it: + # If there is already an option recorded, the incoming option + # will come and compare itself with known one. + # We have a table + # + # known one vs. incoming ? | result + # < | one-new + # = | one-one + # > | new-one + # + # Let's say, after this step, the 3rd option comes (named X), + # and the current result is "a-b". We have a table + # + # a(b) vs. X | result + # < | aX (b dropped) + # = | aa (b dropped) + # > | Xa (b dropped) + # + # In any case, the first one matters. + # + if [[ "${_SOPT:0:1}" < "$_opt" ]]; then + _SOPT="${_SOPT:0:1}$_opt" + elif [[ "${_SOPT:0:1}" == "$_opt" ]]; then + _SOPT="$_opt$_opt" + else + _SOPT="$_opt${_SOPT:0:1}" + fi + + ;; + + q) + _TOPT="$_opt" ;; # Thanks to James Pearson + + u) + if [[ "${_SOPT:0:1}" == "y" ]]; then + _SOPT="uy" + else + _SOPT="u" + fi + ;; + + y) + if [[ "${_SOPT:0:1}" == "u" ]]; then + _SOPT="uy" + else + _SOPT="y" + fi + ;; + + c) + if [[ "${_SOPT:0:2}" == "cc" ]]; then + _SOPT="ccc" + elif [[ "${_SOPT:0:1}" == "c" ]]; then + _SOPT="cc" + else + _SOPT="$_opt" + fi + ;; + + w|v) + _EOPT="$_EOPT:$_opt:" + ;; + + *) + # FIXME: If option is unknown, we will break the loop + # FIXME: and this option will be used by the native program. + # FIXME: break 2 + _die "pacapt: Unknown option '$_opt'." + ;; + esac + done + + shift + + # If the primary option and the secondary are known + # we would break the argument detection, but for sure we will look + # forward to see there is anything interesting... + if [[ -n "$_POPT" && -n "$_SOPT" ]]; then + case "${1:-}" in + "-w"|"--noconfirm") ;; + *) break;; + esac + + # Don't have anything from the **first** argument. Something wrong. + # FIXME: This means that user must enter at least primary action + # FIXME: or secondary action in the very first part... + elif [[ -z "${_POPT}${_SOPT}${_TOPT}" ]]; then + break + fi +done + +[[ -n "$_POPT" ]] \ +|| _die "Usage: pacapt # -h for help, -P list supported functions" + +_validate_operation "${_PACMAN}_${_POPT}${_SOPT}" \ +|| { + _not_implemented + exit 1 +} + +_translate_all || exit + +# pacman man page (examples) says: +# "pacman -Syu gpm = Update package list, upgrade all packages, +# and then install gpm if it wasn't already installed." +# +# Instead, just disallow specific packages, as (ex-)yum users likely +# expect to just update/upgrade one package (and its dependencies) +# and apt-get and pacman have no way to do this. +# +if [[ -n "$*" ]]; then + case "${_POPT}${_SOPT}" in + "Su"|"Sy"|"Suy") + echo 1>&2 "WARNING ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" + echo 1>&2 " The -Sy/u options refresh and/or upgrade all packages." + echo 1>&2 " To install packages as well, use separate commands:" + echo 1>&2 + echo 1>&2 " $0 -S$_SOPT; $0 -S ${*}" + echo 1>&2 "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" + esac +fi + +if [[ -n "$PACAPT_DEBUG" ]]; then + echo "pacapt: $_PACMAN, p=$_POPT, s=$_SOPT, t=$_TOPT, e=$_EOPT" + echo "pacapt: execute '${_PACMAN}_${_POPT}${_SOPT} $_EOPT ${*}'" + declare -f "${_PACMAN}_${_POPT}${_SOPT}" +else + "_${_PACMAN}_init" || exit + "${_PACMAN}_${_POPT}${_SOPT}" $_EOPT "$@" +fi diff --git a/pacapt b/pacapt index d88338ca..21662db4 100755 --- a/pacapt +++ b/pacapt @@ -1,275 +1,2485 @@ -#!/bin/bash +#!/usr/bin/env bash +# +# Purpose: A wrapper for all Unix package managers +# License: Fair license (http://www.opensource.org/licenses/fair) +# Source : http://github.com/icy/pacapt/ +# Version: 2.4.3 +# Authors: Anh K. Huynh et al. + +# Copyright (C) 2010 - 2020 \ +# | 10sr (10sr) +# | Alexander Dupuy (dupuy) +# | Anh K. Huynh (icy) +# | Antony Lee (anntzer) +# | Alex Lyon (Arcterus) +# | Carl X. Su (bcbcarl) +# | Cuong Manh Le (Gnouc) +# | Daniel YC Lin (dlintw) +# | Danny George (dangets) +# | Darshit Shah (darnir) +# | Dmitry Kudriavtsev (dkudriavtsev) +# | Eric Crosson (EricCrosson) +# | Evan Relf (evanrelf) +# | GijsTimmers (GijsTimmers) +# | Hà-Dương Nguyễn (cmpitg) +# | Huy Ngô (NgoHuy) +# | James Pearson (xiongchiamiov) +# | Janne Heß (dasJ) +# | Jiawei Zhou (4679) +# | Karol Blazewicz +# | Kevin Brubeck (unhammer) +# | Konrad Borowski (xfix) +# | Kylie McClain (somasis) +# | Valerio Pizzi (Pival81) +# | Siôn Le Roux (sinisterstuf) +# | Thiago Perrotta (thiagowfx) +# | Vojtech Letal (letalvoj) +# +# Usage of the works is permitted provided that this instrument is +# retained with the works, so that any entity that uses the works is +# notified of this instrument. +# +# DISCLAIMER: THE WORKS ARE WITHOUT WARRANTY. +# + +_print_pacapt_version() { + cat <<_EOF_ +pacapt version '2.4.3' + +Copyright (C) 2010 - 2020 \\ + | 10sr (10sr) + | Alexander Dupuy (dupuy) + | Anh K. Huynh (icy) + | Antony Lee (anntzer) + | Alex Lyon (Arcterus) + | Carl X. Su (bcbcarl) + | Cuong Manh Le (Gnouc) + | Daniel YC Lin (dlintw) + | Danny George (dangets) + | Darshit Shah (darnir) + | Dmitry Kudriavtsev (dkudriavtsev) + | Eric Crosson (EricCrosson) + | Evan Relf (evanrelf) + | GijsTimmers (GijsTimmers) + | Hà-Dương Nguyễn (cmpitg) + | Huy Ngô (NgoHuy) + | James Pearson (xiongchiamiov) + | Janne Heß (dasJ) + | Jiawei Zhou (4679) + | Karol Blazewicz + | Kevin Brubeck (unhammer) + | Konrad Borowski (xfix) + | Kylie McClain (somasis) + | Valerio Pizzi (Pival81) + | Siôn Le Roux (sinisterstuf) + | Thiago Perrotta (thiagowfx) + | Vojtech Letal (letalvoj) + +Usage of the works is permitted provided that this +instrument is retained with the works, so that any +entity that uses the works is notified of this instrument. + +DISCLAIMER: THE WORKS ARE WITHOUT WARRANTY. +_EOF_ +} + +export PACAPT_VERSION='2.4.3' + +_help() { + cat <<'EOF' +NAME + pacapt - An `ArchLinux`'s pacman-like wrapper for many package managers. + +SYNTAX + + $ pacapt + +BASIC OPTIONS + + -h or --help print this help message + -P print supported operations + -V print version information + +SYSGET STYLE OPERATIONS + + update Update package database + upgrade Upgrade system + install Install some packages + search Search some package + remove Remove some packages + autoremove Remove orphans (WIP; may not work correctly) + clean Clean package manager caches + +PACMAN STYLE OPERATIONS + + Query + -Q list all installed packages + -Qc show package's changelog + -Qe [] only list explicitly installed packages + -Qi print package status + -Ql list package's files + -Qm list installed packages that aren't available + in any installation source + -Qo query package that provides + -Qp query a package file (don't use package database) + -Qs search for installed package + + Synchronize + -S install package(s) + -Sg list groups + -Sg list packages in group + -Ss search for packages + -Su upgrade the system + -Sy update package database + -Suy update package database, then upgrade the system + + Remove / Clean up + -R remove some packages + -Sc delete old downloaded packages + -Scc delete all downloaded packages + -Sccc clean variant files. + (debian) See also http://dragula.viettug.org/blogs/646 + + Upgrade + -U upgrade or add package from local file path (or remote uri) + +OPTIONS + + -w download packages but don't install them + --noconfirm don't wait for user's confirmation + +EXAMPLES + + 1. To install a package from Debian's backports repository + $ pacapt -S foobar -t lenny-backports + $ pacapt -S -- -t lenny-backports foobar + + 2. To update package database and then update your system + $ pacapt -Syu + + 3. To download a package without installing it + $ pacapt -Sw foobar + + +ENVIRONMENT + + PACAPT_DEBUG + + This is useful for debugging purpose. The variable can be set to `auto` + or any valid packager. For example, on `Debian` system the two following + commands are the same and they will print out what the script would do: + + PACAPT_DEBUG=auto pacman -Su + PACAPT_DEBUG=dpkg pacman -Su + +NOTES + + When being executed on Arch-based system, the tool simply invokes + the system package manager (`/usr/bin/pacman`). + + Though you can specify option by its own word, for example, + $ pacapt -S -y -u + + it's always the best to combine them + $ pacapt -Syu + +READMORE + + Please visit https://github.com/icy/pacapt. +EOF + +} + + + + +_error() { + echo >&2 "Error: $*" + return 1 +} + +_warn() { + echo >&2 "Warning: $*" + return 0 +} + +_die() { + echo >&2 "$@" + exit 1 +} + +_not_implemented() { + # shellcheck disable=2153 + echo >&2 "${_PACMAN}: '${_POPT}:${_SOPT}:${_TOPT}' operation is invalid or not implemented." + return 1 +} + +_removing_is_dangerous() { + echo >&2 "${_PACMAN}: removing with '$*' is too dangerous" + return 1 +} + +_issue2pacman() { + local _pacman + + _pacman="$1"; shift + + # The following line is added by Daniel YC Lin to support SunOS. + # + # [ `uname` = "$1" ] && _PACMAN="$_pacman" && return + # + # This is quite tricky and fast, however I don't think it works + # on Linux/BSD systems. To avoid extra check, I slightly modify + # the code to make sure it's only applicable on SunOS. + # + [[ "$(uname)" == "SunOS" ]] && _PACMAN="$_pacman" && return + + $GREP -qis "$@" /etc/issue \ + && _PACMAN="$_pacman" && return + + $GREP -qis "$@" /etc/os-release \ + && _PACMAN="$_pacman" && return +} + +_PACMAN_detect() { + _PACMAN_found_from_script_name && return + + _issue2pacman sun_tools "SunOS" && return + _issue2pacman pacman "Arch Linux" && return + _issue2pacman dpkg "Debian GNU/Linux" && return + _issue2pacman dpkg "Ubuntu" && return + _issue2pacman cave "Exherbo Linux" && return + _issue2pacman yum "CentOS" && return + _issue2pacman yum "Red Hat" && return + # + # FIXME: The multiple package issue. + # + # On #63, Huy commented out this line. This is because new generation + # of Fedora uses `dnf`, and `yum` becomes a legacy tool. On old Fedora + # system, `yum` is still detectable by looking up `yum` binary. + # + # I'm not sure how to support this case easily. Let's wait, e.g, 5 years + # from now to make `dnf` becomes a default? Oh no! + # + # And here why `pacman` is still smart. Debian has a set of tools. + # Fedora has `yum` (and a set of add-ons). Now Fedora moves to `dnf`. + # This means that a package manager is not a heart of a system ;) + # + # _issue2pacman yum "Fedora" && return + _issue2pacman zypper "SUSE" && return + _issue2pacman pkg_tools "OpenBSD" && return + _issue2pacman pkg_tools "Bitrig" && return + _issue2pacman apk "Alpine Linux" && return + + [[ -z "$_PACMAN" ]] || return + + # Prevent a loop when this script is installed on non-standard system + if [[ -x "/usr/bin/pacman" ]]; then + $GREP -q "${FUNCNAME[0]}" '/usr/bin/pacman' >/dev/null 2>&1 + [[ $? -ge 1 ]] && _PACMAN="pacman" \ + && return + fi + + [[ -x "/usr/bin/apt-get" ]] && _PACMAN="dpkg" && return + [[ -x "/data/data/com.termux/files/usr/bin/apt-get" ]] && _PACMAN="dpkg" && return + [[ -x "/usr/bin/cave" ]] && _PACMAN="cave" && return + [[ -x "/usr/bin/dnf" ]] && _PACMAN="dnf" && return + [[ -x "/usr/bin/yum" ]] && _PACMAN="yum" && return + [[ -x "/opt/local/bin/port" ]] && _PACMAN="macports" && return + [[ -x "/usr/bin/emerge" ]] && _PACMAN="portage" && return + [[ -x "/usr/bin/zypper" ]] && _PACMAN="zypper" && return + [[ -x "/usr/sbin/pkg" ]] && _PACMAN="pkgng" && return + # make sure pkg_add is after pkgng, FreeBSD base comes with it until converted + [[ -x "/usr/sbin/pkg_add" ]] && _PACMAN="pkg_tools" && return + [[ -x "/usr/sbin/pkgadd" ]] && _PACMAN="sun_tools" && return + [[ -x "/sbin/apk" ]] && _PACMAN="apk" && return + [[ -x "/usr/bin/tazpkg" ]] && _PACMAN="tazpkg" && return + [[ -x "/usr/bin/swupd" ]] && _PACMAN="swupd" && return + + command -v brew >/dev/null && _PACMAN="homebrew" && return + + return 1 +} + +_translate_w() { + + echo "$_EOPT" | $GREP -q ":w:" || return 0 + + local _opt= + local _ret=0 + + case "$_PACMAN" in + "dpkg") _opt="-d";; + "cave") _opt="-f";; + "macports") _opt="fetch";; + "portage") _opt="--fetchonly";; + "zypper") _opt="--download-only";; + "pkgng") _opt="fetch";; + "yum") _opt="--downloadonly"; + if ! rpm -q 'yum-downloadonly' >/dev/null 2>&1; then + _error "'yum-downloadonly' package is required when '-w' is used." + _ret=1 + fi + ;; + "tazpkg") + _error "$_PACMAN: Use '$_PACMAN get' to download and save packages to current directory." + _ret=1 + ;; + "apk") _opt="fetch";; + *) + _opt="" + _ret=1 + + _error "$_PACMAN: Option '-w' is not supported/implemented." + ;; + esac + + echo $_opt + return "$_ret" +} + +_translate_debug() { + echo "$_EOPT" | $GREP -q ":v:" || return 0 + + case "$_PACMAN" in + "tazpkg") + _error "$_PACMAN: Option '-v' (debug) is not supported/implemented by tazpkg" + return 1 + ;; + esac + + echo "-v" +} + +_translate_noconfirm() { + + echo "$_EOPT" | $GREP -q ":noconfirm:" || return 0 + + local _opt= + local _ret=0 + + case "$_PACMAN" in + # FIXME: Update environment DEBIAN_FRONTEND=noninteractive + # FIXME: There is also --force-yes for a stronger case + "dpkg") _opt="--yes";; + "dnf") _opt="--assumeyes";; + "yum") _opt="--assumeyes";; + # FIXME: pacman has 'assume-yes' and 'assume-no' + # FIXME: zypper has better mode. Similar to dpkg (Debian). + "zypper") _opt="--no-confirm";; + "pkgng") _opt="-y";; + "tazpkg") _opt="--auto";; + *) + _opt="" + _ret=1 + _error "$_PACMAN: Option '--noconfirm' is not supported/implemented." + ;; + esac + + echo $_opt + return $_ret +} + +_translate_all() { + local _args="" + local _debug= + local _noconfirm= + + _debug="$(_translate_debug)" + _noconfirm="$(_translate_noconfirm)" + _args="$(_translate_w)" || return 1 + _args="${_args}${_noconfirm:+ }${_noconfirm}" || return 1 + _args="${_args}${_debug:+ }${_debug}" || return 1 + + export _EOPT="${_args# }" +} + +_print_supported_operations() { + local _pacman="$1" + echo -n "pacapt($_pacman): available operations:" + # shellcheck disable=2016 + $GREP -E "^${_pacman}_[^ \\t]+\\(\\)" "$0" \ + | $AWK -F '(' '{print $1}' \ + | sed -e "s/${_pacman}_//g" \ + | while read -r O; do + echo -n " $O" + done + echo +} + + +export _SUPPORTED_EXTERNALS=" + :conda + :tlmgr + :texlive + :gem + :npm + :pip +" +readonly _SUPPORTED_EXTERNALS + +_PACMAN_found_from_script_name() { + local _tmp_name= + local _pacman= + + _tmp_name="${BASH_SOURCE[0]:-?}" + if [[ "$_tmp_name" == "?" ]]; then + _error "Unable to get script name." + return 1 + fi + + _tmp_name="${_tmp_name##*/}" # base name (remove everything before the last `/`) + _tmp_name="${_tmp_name%.*}" # remove extension if any (remove everything from the last `.`) + _pacman="${_tmp_name##*-}" # remove every thing before the last `-` + + if grep -Eq -e ":$_pacman[[:space:]]*" <<< "$_SUPPORTED_EXTERNALS"; then + export _PACMAN="$_pacman" + return 0 + else + export _PACMAN="" + return 1 + fi +} + + + +_apk_init() { + : +} + +apk_Q() { + if [[ -z "$_TOPT" ]]; then + apk info + else + _not_implemented + fi +} + +apk_Qi() { + apk info -a -- "$@" +} + +apk_Ql() { + apk info -L -- "$@" +} + +apk_Qo() { + apk info --who-owns -- "$@" +} + +apk_Qs() { + apk info -- "*${*}*" +} + +apk_Qu() { + apk version -l '<' +} + +apk_R() { + apk del -- "$@" +} + +apk_Rn() { + apk del --purge -- "$@" +} + +apk_Rns() { + apk del --purge -r -- "$@" +} + +apk_Rs() { + apk del -r -- "$@" +} + +apk_S() { + case ${_EOPT} in + # Download only + ("fetch") shift + apk fetch -- "$@" ;; + (*) apk add $_TOPT -- "$@" ;; + esac +} + +apk_Sc() { + apk cache -v clean +} + +apk_Scc() { + rm -rf /var/cache/apk/* +} + +apk_Sccc() { + apk_Scc +} + +apk_Si() { + apk_Qi "$@" +} + +apk_Sii() { + apk info -r -- "$@" +} + +apk_Sl() { + apk search -v -- "$@" +} + +apk_Ss() { + apk_Sl "$@" +} + +apk_Su() { + apk upgrade +} + +apk_Suy() { + if [ "$#" -gt 0 ]; then + apk add -U -u -- "$@" + else + apk upgrade -U -a + fi +} + +apk_Sy() { + apk update +} + +apk_Sw() { + apk fetch -- "$@" +} + +apk_U() { + apk add --allow-untrusted -- "$@" +} + + + +_cave_init() { + shopt -u globstar +} + +cave_Q() { + if [[ "$_TOPT" == "q" ]]; then + cave show -f "${@:-world}" \ + | grep -v '^$' + else + cave show -f "${@:-world}" + fi +} + +cave_Qi() { + cave show "$@" +} + +cave_Ql() { + if [[ -n "$*" ]]; then + cave contents "$@" + return + fi + + cave show -f "${@:-world}" \ + | grep -v '^$' \ + | while read -r _pkg; do + if [[ "$_TOPT" == "q" ]]; then + cave --color no contents "$_pkg" + else + cave contents "$_pkg" + fi + done +} + +cave_Qo() { + cave owner "$@" +} + +cave_Qp() { + _not_implemented +} + +cave_Qu() { + if [[ -z "$*" ]];then + cave resolve -c world \ + | grep '^u.*' \ + | while read -r _pkg; do + echo "$_pkg" | cut -d'u' -f2- + done + else + cave resolve -c world \ + | grep '^u.*' \ + | grep -- "$@" + fi +} + +cave_Qs() { + cave show -f world | grep -- "$@" +} + +cave_Rs() { + if [[ "$_TOPT" == "" ]]; then + cave uninstall -r "$@" \ + && echo "Control-C to stop uninstalling..." \ + && sleep 2s \ + && cave uninstall -xr "$@" + else + cave purge "$@" \ + && echo "Control-C to stop uninstalling (+ dependencies)..." \ + && sleep 2s \ + && cave purge -x "$@" + fi +} + +cave_Rn() { + _not_implemented +} + +cave_Rns() { + _not_implemented +} + +cave_R() { + cave uninstall "$@" \ + && echo "Control-C to stop uninstalling..." \ + && sleep 2s \ + && cave uninstall -x "$@" +} + +cave_Si() { + cave show "$@" +} + +cave_Suy() { + cave sync && cave resolve -c "${@:-world}" \ + && echo "Control-C to stop upgrading..." \ + && sleep 2s \ + && cave resolve -cx "${@:-world}" +} + +cave_Su() { + cave resolve -c "$@" \ + && echo "Control-C to stop upgrading..." \ + && sleep 2s \ + && cave resolve -cx "$@" +} + +cave_Sy() { + cave sync "$@" +} + +cave_Ss() { + cave search "$@" +} + +cave_Sc() { + cave fix-cache "$@" +} + +cave_Scc() { + cave fix-cache "$@" +} + +cave_Sccc() { + #rm -fv /var/cache/paludis/* + _not_implemented +} + +cave_S() { + cave resolve $_TOPT "$@" \ + && echo "Control-C to stop installing..." \ + && sleep 2s \ + && cave resolve -x $_TOPT "$@" +} + +cave_U() { + _not_implemented +} + + + +_conda_init() { + : +} + +conda_Q() { + if [[ $# -gt 0 ]]; then + conda list "$(python -c 'import sys; print("^" + "|".join(sys.argv[1:]) + "$")' "$@")" + else + conda list + fi +} + +conda_R() { + conda remove "$@" +} + +conda_S() { + conda install "$@" +} + +conda_Sc() { + conda clean --all "$@" +} + +conda_Si() { + conda search "$@" --info +} + +conda_Ss() { + conda search "*$@*" +} + +conda_Suy() { + conda update --all "$@" +} + + + + +_dnf_init() { + : +} + +dnf_S() { + dnf install $_TOPT "$@" +} + +dnf_Sc() { + dnf clean expire-cache "$@" +} + +dnf_Scc() { + dnf clean packages "$@" +} + +dnf_Sccc() { + dnf clean all "$@" +} + +dnf_Si() { + dnf info "$@" +} + +dnf_Sg() { + if [[ $# -gt 0 ]]; then + dnf group info "$@" + else + dnf group list + fi +} + +dnf_Sl() { + dnf list available "$@" +} + +dnf_Ss() { + dnf search "$@" +} + +dnf_Su() { + dnf upgrade "$@" +} + +dnf_Suy() { + dnf upgrade "$@" +} + +dnf_Sw() { + dnf download "$@" +} + +dnf_Sy() { + dnf clean expire-cache && dnf check-update +} + +dnf_Q() { + if [[ "$_TOPT" == "q" ]]; then + rpm -qa --qf "%{NAME}\\n" + elif [[ "$_TOPT" == "" ]]; then + rpm -qa --qf "%{NAME} %{VERSION}\\n" + else + _not_implemented + fi +} + +dnf_Qc() { + rpm -q --changelog "$@" +} + +dnf_Qe() { + dnf repoquery --userinstalled "$@" +} + +dnf_Qi() { + dnf info "$@" +} + +dnf_Ql() { + rpm -ql "$@" +} + +dnf_Qm() { + dnf list extras +} + +dnf_Qo() { + rpm -qf "$@" +} + +dnf_Qp() { + rpm -qp "$@" +} + +dnf_Qs() { + rpm -qa "*${*}*" +} + +dnf_Qu() { + dnf list updates "$@" +} + +dnf_R() { + dnf remove "$@" +} + +dnf_U() { + dnf install "$@" +} + + + +_dpkg_init() { + : +} + +dpkg_Q() { + if [[ "$_TOPT" == "q" ]]; then + dpkg -l \ + | grep -E '^[hi]i' \ + | awk '{print $2}' + elif [[ "$_TOPT" == "" ]]; then + dpkg -l "$@" \ + | grep -E '^[hi]i' + else + _not_implemented + fi +} + +dpkg_Qi() { + dpkg-query -s "$@" +} + +dpkg_Ql() { + if [[ -n "$*" ]]; then + dpkg-query -L "$@" + return + fi + + dpkg -l \ + | grep -E '^[hi]i' \ + | awk '{print $2}' \ + | while read -r _pkg; do + if [[ "$_TOPT" == "q" ]]; then + dpkg-query -L "$_pkg" + else + dpkg-query -L "$_pkg" \ + | while read -r _line; do + echo "$_pkg $_line" + done + fi + done +} + +dpkg_Qo() { + dpkg-query -S "$@" +} + +dpkg_Qp() { + dpkg-deb -I "$@" +} + +dpkg_Qu() { + apt-get upgrade --trivial-only "$@" +} + +dpkg_Qs() { + # dpkg >= 1.16.2 dpkg-query -W -f='${db:Status-Abbrev} ${binary:Package}\t${Version}\t${binary:Summary}\n' + dpkg-query -W -f='${Status} ${Package}\t${Version}\t${Description}\n' \ + | grep -E '^((hold)|(install)|(deinstall))' \ + | sed -r -e 's#^(\w+ ){3}##g' \ + | grep -Ei "${@:-.}" +} + +dpkg_Rs() { + if [[ "$_TOPT" == "" ]]; then + apt-get autoremove "$@" + else + _not_implemented + fi +} + +dpkg_Rn() { + apt-get purge "$@" +} + +dpkg_Rns() { + apt-get --purge autoremove "$@" +} + +dpkg_R() { + apt-get remove "$@" +} + +dpkg_Si() { + apt-cache show "$@" +} + +dpkg_Suy() { + apt-get update \ + && apt-get upgrade "$@" \ + && apt-get dist-upgrade "$@" +} + +dpkg_Su() { + apt-get upgrade "$@" \ + && apt-get dist-upgrade "$@" +} + + +dpkg_Sy() { + apt-get update "$@" +} + +dpkg_Ss() { + apt-cache search "$@" +} + +dpkg_Sc() { + apt-get clean "$@" +} + +dpkg_Scc() { + apt-get autoclean "$@" +} + +dpkg_S() { + apt-get install $_TOPT "$@" +} + +dpkg_U() { + dpkg -i "$@" +} + +dpkg_Sii() { + apt-cache rdepends "$@" +} + +dpkg_Sccc() { + rm -fv /var/cache/apt/*.bin + rm -fv /var/cache/apt/archives/*.* + rm -fv /var/lib/apt/lists/*.* + apt-get autoclean +} + + + +_homebrew_init() { + : +} + +homebrew_Qi() { + brew info "$@" +} + +homebrew_Ql() { + brew list "$@" +} + +homebrew_Qo() { + local pkg prefix cellar + + # FIXME: What happens if the file is not exectutable? + cd "$(dirname -- "$(which "$@")")" || return + pkg="$(pwd -P)/$(basename -- "$@")" + prefix="$(brew --prefix)" + cellar="$(brew --cellar)" + + for package in $cellar/*; do + files=(${package}/*/${pkg/#$prefix\//}) + if [[ -e "${files[${#files[@]} - 1]}" ]]; then + echo "${package/#$cellar\//}" + break + fi + done +} + +homebrew_Qc() { + brew log "$@" +} + +homebrew_Qu() { + brew outdated | grep "$@" +} + +homebrew_Qs() { + brew list | grep "$@" +} + +homebrew_Q() { + if [[ "$_TOPT" == "" ]]; then + if [[ "$*" == "" ]]; then + brew list + else + brew list | grep "$@" + fi + else + _not_implemented + fi +} + +homebrew_Rs() { + which join > /dev/null + if [ $? -ne 0 ]; then + _die "pacapt: join binary does not exist in system." + fi + + which sort > /dev/null + if [ $? -ne 0 ]; then + _die "pacapt: sort binary does not exist in system." + fi + + if [[ "$@" == "" ]]; then + _die "pacapt: ${FUNCNAME[0]} requires arguments" + fi + + for _target in $@; + do + brew rm $_target + + while [ "$(join <(sort <(brew leaves)) <(sort <(brew deps $_target)))" != "" ] + do + brew rm $(join <(sort <(brew leaves)) <(sort <(brew deps $_target))) + done + done + +} + +homebrew_R() { + brew remove "$@" +} + +homebrew_Si() { + brew info "$@" +} + +homebrew_Suy() { + brew update \ + && brew upgrade "$@" +} + +homebrew_Su() { + brew upgrade "$@" +} + +homebrew_Sy() { + brew update "$@" +} + +homebrew_Ss() { + brew search "$@" +} + +homebrew_Sc() { + brew cleanup "$@" +} + +homebrew_Scc() { + brew cleanup -s "$@" +} + +homebrew_Sccc() { + # See more discussion in + # https://github.com/icy/pacapt/issues/47 + + local _dcache + + _dcache="$(brew --cache)" + case "$_dcache" in + ""|"/"|" ") + _error "${FUNCNAME[0]}: Unable to delete '$_dcache'." + ;; + + *) + # FIXME: This is quite stupid!!! But it's an easy way + # FIXME: to avoid some warning from #shellcheck. + # FIXME: Please note that, $_dcache is not empty now. + rm -rf "${_dcache:-/x/x/x/x/x/x/x/x/x/x/x//x/x/x/x/x/}/" + ;; + esac +} + +homebrew_S() { + 2>&1 brew install $_TOPT "$@" \ + | awk '{print; if ($0 ~ /brew cask install/) { exit(126); }}' + ret=( ${PIPESTATUS[*]} ) + if [[ "${ret[1]}" == 126 ]]; then + echo >&2 ":: Now trying with brew/cask..." + brew cask install $_TOPT "$@" + else + return "${ret[0]}" + fi +} + + + +_macports_init() { + : +} + +macports_Ql() { + port contents "$@" +} + +macports_Qo() { + port provides "$@" +} + +macports_Qc() { + port log "$@" +} + +macports_Qu() { + port outdated "$@" +} + +macports_Rs() { + if [[ "$_TOPT" == "" ]]; then + port uninstall --follow-dependencies "$@" + else + _not_implemented + fi +} + +macports_R() { + port uninstall "$@" +} + +macports_Si() { + port info "$@" +} + +macports_Suy() { + port selfupdate \ + && port upgrade outdated "$@" +} + +macports_Su() { + port upgrade outdate "$@" +} + +macports_Sy() { + port selfupdate "$@" +} + +macports_Ss() { + port search "$@" +} + +macports_Sc() { + port clean --all inactive "$@" +} + +macports_Scc() { + port clean --all installed "$@" +} + +macports_S() { + if [[ "$_TOPT" == "fetch" ]]; then + port patch "$@" + else + port install "$@" + fi +} + + + +_pkgng_init() { + : +} + +pkgng_Qi() { + pkg info "$@" +} + +pkgng_Ql() { + pkg info -l "$@" +} + +pkgng_Qo() { + pkg which "$@" +} + +pkgng_Qp() { + pkg query -F "$@" '%n %v' +} + +pkgng_Qu() { + pkg upgrade -n "$@" +} + +pkgng_Q() { + if [[ "$_TOPT" == "q" ]]; then + pkg query '%n' "$@" + elif [[ "$_TOPT" == "" ]]; then + pkg query '%n %v' "$@" + else + _not_implemented + fi +} + +pkgng_Rs() { + if [[ "$_TOPT" == "" ]]; then + pkg remove "$@" + pkg autoremove + else + _not_implemented + fi +} + +pkgng_R() { + pkg remove "$@" +} + +pkgng_Si() { + pkg search -S name -ef "$@" +} + +pkgng_Suy() { + pkg upgrade "$@" +} + +pkgng_Su() { + pkg upgrade -U "$@" +} + +pkgng_Sy() { + pkg update "$@" +} + +pkgng_Ss() { + pkg search "$@" +} + +pkgng_Sc() { + pkg clean "$@" +} + +pkgng_Scc() { + pkg clean -a "$@" +} + +pkgng_S() { + if [[ "$_TOPT" == "fetch" ]]; then + pkg fetch "$@" + else + pkg install "$@" + fi +} + + + +_pkg_tools_init() { + : +} + +pkg_tools_Qi() { + # disable searching mirrors for packages + export PKG_PATH= + pkg_info "$@" +} + +pkg_tools_Ql() { + export PKG_PATH= + pkg_info -L "$@" +} + +pkg_tools_Qo() { + export PKG_PATH= + pkg_info -E "$@" +} + +pkg_tools_Qp() { + _not_implemented +} + +pkg_tools_Qu() { + export PKG_PATH= + pkg_add -u "$@" +} + +pkg_tools_Q() { + export PKG_PATH= + # the dash after the pkg name is so we don't catch partial matches + # because all packages in openbsd have the format 'pkgname-pkgver' + if [[ "$_TOPT" == "q" && ! -z "$*" ]]; then + pkg_info -q | grep "^${*}-" + elif [[ "$_TOPT" == "q" && -z "$*" ]];then + pkg_info -q + elif [[ "$_TOPT" == "" && ! -z "$*" ]]; then + pkg_info | grep "^${*}-" + elif [[ "$_TOPT" == "" && -z "$*" ]];then + pkg_info + else + _not_implemented + fi +} + +pkg_tools_Rs() { + if [[ "$_TOPT" == "" ]]; then + pkg_delete -D dependencies "$@" + else + _not_implemented + fi +} + +pkg_tools_Rn() { + if [[ "$_TOPT" == "" ]];then + pkg_delete -c "$@" + else + _not_implemented + fi +} + +pkg_tools_Rns() { + _not_implemented +} + +pkg_tools_R() { + pkg_delete "$@" +} + +pkg_tools_Si() { + pkg_info "$@" +} + +pkg_tools_Sl() { + pkg_info -L "$@" +} + +pkg_tools_Suy() { + # pkg_tools doesn't really have any concept of a database + # there's actually not really any database to update, so + # this function is mostly just for convienience since on arch + # doing -Su is normally a bad thing to do since it's a partial upgrade + + pkg_tools_Su "$@" +} + +pkg_tools_Su() { + pkg_add -u "$@" +} + +pkg_tools_Sy() { + _not_implemented +} + +pkg_tools_Ss() { + if [[ -z "$*" ]];then + _not_implemented + else + pkg_info -Q "$@" + fi +} + +pkg_tools_Sc() { + # by default no cache directory is used + if [[ -z "$PKG_CACHE" ]];then + echo "You have no cache directory set, set \$PKG_CACHE for a cache directory." + elif [[ ! -d "$PKG_CACHE" ]];then + echo "You have a cache directory set, but it does not exist. Create \"$PKG_CACHE\"." + else + _removing_is_dangerous "rm -rf $PKG_CACHE/*" + fi +} + +pkg_tools_Scc() { + _not_implemented +} + +pkg_tools_S() { + pkg_add "$@" +} + + + +_portage_init() { + : +} + +portage_Qi() { + emerge --info "$@" +} + +portage_Ql() { + if [[ -x '/usr/bin/qlist' ]]; then + qlist "$@" + elif [[ -x '/usr/bin/equery' ]]; then + equery files "$@" + else + _error "'portage-utils' or 'gentoolkit' package is required to perform this opreation." + fi +} + +portage_Qo() { + if [[ -x '/usr/bin/equery' ]]; then + equery belongs "$@" + else + _error "'gentoolkit' package is required to perform this operation." + fi +} + +portage_Qc() { + emerge -p --changelog "$@" +} + +portage_Qu() { + emerge -uvN "$@" +} + +portage_Q() { + if [[ "$_TOPT" == "" ]]; then + if [[ -x '/usr/bin/eix' ]]; then + eix -I "$@" + elif [[ -x '/usr/bin/equery' ]]; then + equery list -i "$@" + else + LS_COLORS=never \ + ls -1 -d /var/db/pkg/*/* + fi + else + _not_implemented + fi +} + +portage_Rs() { + if [[ "$_TOPT" == "" ]]; then + emerge --depclean world "$@" + else + _not_implemented + fi +} + +portage_R() { + emerge --depclean "@" +} + +portage_Si() { + emerge --info "$@" +} + +portage_Suy() { + if [[ -x '/usr/bin/layman' ]]; then + layman --sync-all \ + && emerge --sync \ + && emerge -auND world "$@" + else + emerge --sync \ + && emerge -uND world "$@" + fi +} + +portage_Su() { + emerge -uND world "$@" +} + +portage_Sy() { + if [[ -x "/usr/bin/layman" ]]; then + layman --sync-all \ + && emerge --sync "$@" + else + emerge --sync "$@" + fi +} + +portage_Ss() { + if [[ -x "/usr/bin/eix" ]]; then + eix "$@" + else + emerge --search "$@" + fi +} + +portage_Sc() { + if [[ -x "/usr/bin/eclean-dist" ]]; then + eclean-dist -d -t1m -s50 -f "$@" + else + _error "'gentoolkit' package is required to perform this operation." + fi +} + +portage_Scc() { + if [[ -x "/usr/bin/eclean" ]]; then + eclean -i distfiles "$@" + else + _error "'gentoolkit' package is required to perform this operation." + fi +} + +portage_Sccc() { + rm -fv /usr/portage/distfiles/*.* +} + +portage_S() { + emerge "$@" +} + + + +_sun_tools_init() { + # The purpose of `if` is to make sure this function + # can be invoked on other system (Linux, BSD). + if [[ "$(uname)" == "SunOS" ]]; then + export GREP=/usr/xpg4/bin/grep + export AWK=nawk + fi +} + +sun_tools_Qi() { + pkginfo -l "$@" +} + +sun_tools_Ql() { + pkginfo -l "$@" +} + +sun_tools_Qo() { + $GREP "$@" /var/sadm/install/contents +} + +sun_tools_Qs() { + pkginfo | $GREP -i "$@" +} + +sun_tools_Q() { + # the dash after the pkg name is so we don't catch partial matches + # because all packages in openbsd have the format 'pkgname-pkgver' + if [[ "$_TOPT" == "q" && ! -z "$*" ]]; then + pkginfo | $GREP "$@" + elif [[ "$_TOPT" == "q" && -z "$*" ]]; then + pkginfo + else + pkginfo "$@" + fi +} + +sun_tools_R() { + pkgrm "$@" +} + +sun_tools_U() { + pkgadd "$@" +} + + + +_swupd_init() { + : +} + +swupd_Qk() { + swupd verify "$@" +} + +swupd_Qo() { + swupd search "$@" +} + +swupd_Qs() { + swupd search "$@" +} -# Purpose: A wrapper for all Unix package managers -# Author : Anh K. Huynh -# License: Fair license (http://www.opensource.org/licenses/fair) -# Source : http://github.com/icy/pacapt/ +swupd_R() { + swupd bundle-remove "$@" +} -# Copyright (C) 2010 - 2014 Anh K. Huynh -# -# Usage of the works is permitted provided that this instrument is -# retained with the works, so that any entity that uses the works is -# notified of this instrument. -# -# DISCLAIMER: THE WORKS ARE WITHOUT WARRANTY. +swupd_Suy() { + swupd update +} -set -u # exit if there is any unbound variable +swupd_Su() { + swupd update +} -_POPT="" # primary operation -_SOPT="" # secondary operation -_TOPT="" # options for operations -_PKG="" # packages and extra parameters for apt-get -_VERBOSE="" # verbose mode -_FORCE="" # force yes -_OSTYPE="" # type of package manager (Arch pacman, Debian apt, ...) +swupd_Sy() { + swupd search -i + swupd update +} -_error() { - echo >&2 ":: $*" +swupd_Ss() { + swupd search "$@" } -_help() { - cat <<\EOF -NAME +swupd_S() { + swupd bundle-add "$@" +} - pacapt - An Arch's pacman-like package manager for some Unices. -DESCRIPTION +_tazpkg_init() { + : +} - An Arch's pacman-like package manager for some Unices. Actually this Bash - script provides a wrapper for system's package manager. +tazpkg_Q() { + if [[ "$_TOPT" == "q" ]]; then + tazpkg list "$@" \ + | awk '{ if (NF == 2 || NF == 3) { print $1; }}' + elif [[ "$_TOPT" == "" ]]; then + tazpkg list "$@" + else + _not_implemented + fi +} - Instead of remembering various options/tools on different OSs, you only - need a common way to manipulate packages. Not all options of the native - package manager are ported; the tool only provides a very basic interface - to search, install, remove packages, and/or update the system. +tazpkg_Qi() { + tazpkg info "$@" +} - Arch's pacman is chosen, as pacman is quite smart when it divides all - packages-related operations into three major groups: Synchronize, Query - and Remove/Clean up. It has a clean man page, and it is the only tool - needed to manipulate official packages on system. (Debian, for example, - requires you to use apt-get, dpkg, and/or aptitude.) +tazpkg_Ql() { + if [[ -z "$*" ]]; then + _not_implemented + return + fi - The tool supports the following package managers: + if [[ "$_TOPT" == "q" ]]; then + { + tazpkg list-files "$@" + tazpkg list-config "$@" + } \ + | grep ^/ + else + tazpkg list-files "$@" + tazpkg list-config "$@" + fi +} - pacman by Arch Linux, ArchBang, Manjaro, etc. - dpkg/apt-get by Debian, Ubuntu, etc. - homebrew by Mac OS X - macports by Mac OS X - yum/rpm by Redhat, CentOS, Fedora, etc. - portage by Gentoo - zypper by OpenSUSE +tazpkg_Sy() { + tazpkg recharge +} -INSTALL +tazpkg_Su() { + tazpkg up +} - This script shouldn't be installed on an Arch-based system. +tazpkg_Suy() { + tazpkg_Sy \ + && tazpkg_Su +} - Download the script 'pacapt' and install it into /usr/local/bin. +tazpkg_S() { + local _forced="" - $ wget \ - https://github.com/icy/pacapt/raw/master/pacapt \ - -O /usr/local/bin/pacapt + if grep -q -- "--forced" <<<"$*"; then + _forced="--forced" + fi - $ chmod 755 /usr/local/bin/pacapt + while (( $# )); do + if [[ "$1" == "--forced" ]]; then + _forced="--forced" + shift + continue + fi - If you have problem with Github's SSL certificate you may try + tazpkg get-install "$1" $_forced + shift + done +} - $ wget \ - --no-check-certificate \ - https://github.com/icy/pacapt/raw/master/pacapt \ - -O /usr/local/bin/pacapt +tazpkg_R() { + local _auto="" - However this way isn't recommended unless you know what you're doing. + if grep -q -- "--auto" <<<"$*"; then + _auto="--auto" + fi -SYNTAX + while (( $# )); do + if [[ "$1" == "--auto" ]]; then + _auto="--auto" + shift + continue + fi - $ pacapt + tazpkg remove "$1" $_auto + shift + done +} -OPERATIONS +tazpkg_Sc() { + tazpkg clean-cache +} - Query +tazpkg_Scc() { + tazpkg clean-cache + cd /var/lib/tazpkg/ \ + && { + rm -fv \ + ./*.bak \ + ID \ + packages.* \ + files.list.* + } +} - -Q list all installed packages - -Qc show package's changelog - -Qi print package status - -Ql list package's files - -Qm list installed packages that aren't available - in any installation source - -Qo query package that provides - -Qp query a package file (don't use package database) - -Qs search for installed package +tazpkg_Ss() { + tazpkg search "$@" +} - Synchronize +tazpkg_Qo() { + tazpkg search-pkgname "$@" +} - -S install package(s) - -Ss search for packages - -Su upgrade the system - -Sy update package database - -Suy update package database, then upgrade the system +tazpkg_U() { + local _forced="" - Remove / Clean up + if grep -q -- "--forced" <<<"$*"; then + _forced="--forced" + fi - -R remove some packages - -Sc delete old downloaded packages - -Scc delete all downloaded packages - -Sccc clean variant files. - (debian) See more at http://dragula.viettug.org/blogs/646 + while (( $# )); do + if [[ "$1" == "--forced" ]]; then + _forced="--forced" + shift + continue + fi -OPTIONS + tazpkg install "$1" $_forced + shift + done +} - -f force yes - -v be verbose - -w download packages but don't install them -NOTES - To install a package from backports repository on Debian system: - $ pacapt -S foobar -t lenny-backports +_tlmgr_init() { + : +} - Similarly, any further option which isn't recognized by getopts (1) - can be passed to system tool. +tlmgr_Qi() { + tlmgr info --only-installed "$@" +} - When being executed on Arch-based system, the tool simply invokes - the system tool '/usr/bin/pacman'. +tlmgr_Qk() { + tlmgr check files +} -THANKS +tlmgr_Ql() { + tlmgr info --only-installed --list "$@" +} + +tlmgr_R() { + tlmgr remove "$@" +} - Special thanks to +tlmgr_S() { + tlmgr install "$@" +} - Châu An, Nguyễn (told me about 'Pacman Rosetta' [1]) - Karol Blazewicz (for comments about '-Sy') - James Pearson (for userful comments and patches. - See also http://github.com/xiongchiamiov/pacapt) - Alexander Dupuy (for userful comments and patches. - See also https://github.com/dupuy/pacapt) - Hà Dương, Nguyễn (for adding Gentoo support. - See also https://github.com/CMPITG/pacapt) +tlmgr_Si() { + tlmgr info "$@" +} -REFERENCES +tlmgr_Sl() { + tlmgr info +} - 1. Pacman Rosetta, https://wiki.archlinux.org/index.php?title=Pacman_Rosetta -EOF +tlmgr_Ss() { + tlmgr search --global "$@" } -### -### Helpers -### +tlmgr_Suy() { + tlmgr update --all +} -_os_is() { - [[ "$_OSTYPE" = "$*" ]] +tlmgr_U() { + tlmgr install --file "$@" } -_exec_() { - local _type="$1" - shift - if _os_is $_type; then - [[ -z "$_VERBOSE" ]] || _error "Going to execute: $* $_VERBOSE $_FORCE" - eval "$* $_VERBOSE $_FORCE" + + +_yum_init() { + : +} + +yum_Q() { + if [[ "$_TOPT" == "q" ]]; then + rpm -qa --qf "%{NAME}\\n" + elif [[ "$_TOPT" == "" ]]; then + rpm -qa --qf "%{NAME} %{VERSION}\\n" + else + _not_implemented fi } +yum_Qi() { + yum info "$@" +} -# Detect package type from /etc/issue -_found_arch() { - local _ostype="$1" - shift - grep -qis "$*" /etc/issue && _OSTYPE="$_ostype" +yum_Qs() { + rpm -qa "*${*}*" +} + +yum_Ql() { + rpm -ql "$@" +} + +yum_Qo() { + rpm -qf "$@" +} + +yum_Qp() { + rpm -qp "$@" +} + +yum_Qc() { + rpm -q --changelog "$@" } -# Detect package type -_OSTYPE_detect() { - _found_arch PACMAN "Arch Linux" && return - _found_arch DPKG "Debian GNU/Linux" && return - _found_arch DPKG "Ubuntu" && return - _found_arch YUM "CentOS" && return - _found_arch YUM "Red Hat" && return - _found_arch YUM "Fedora" && return - _found_arch ZYPPER "SUSE" && return +yum_Qu() { + yum list updates "$@" +} - [[ -z "$_OSTYPE" ]] || return +yum_Qm() { + yum list extras "$@" +} - # See also https://github.com/icy/pacapt/pull/22 - # Please not that $OSTYPE (which is `linux-gnu` on Linux system) - # is not our $_OSTYPE. The choice is not very good because - # a typo can just break the logic of the program. - if [[ "$OSTYPE" != "darwin"* ]]; then - _error "Can't detect OS type from /etc/issue. Running fallback method." +yum_Rs() { + if [[ "$_TOPT" == "" ]]; then + yum erase "$@" + else + _not_implemented fi - if [[ -x "/usr/bin/pacman" ]]; then - # This is to prevent a loop when this script is installed on - # non-standard system - grep -q "$FUNCNAME" '/usr/bin/pacman' >/dev/null 2>&1 - [[ $? -ge 1 ]] && _OSTYPE="PACMAN" && return - fi - [[ -x "/usr/bin/apt-get" ]] && _OSTYPE="DPKG" && return - [[ -x "/usr/bin/yum" ]] && _OSTYPE="YUM" && return - [[ -x "/opt/local/bin/port" ]] && _OSTYPE="MACPORTS" && return - command -v brew >/dev/null && _OSTYPE="HOMEBREW" && return - [[ -x "/usr/bin/emerge" ]] && _OSTYPE="PORTAGE" && return - [[ -x "/usr/bin/zypper" ]] && _OSTYPE="ZYPPER" && return - if [[ -z "$_OSTYPE" ]]; then - _error "No supported package manager installed on system" - _error "(supported: apt, homebrew, pacman, portage, yum)" - exit 1 - fi -} - -### -### Main -### - -# Detect type of package manager. -cat 1>&2 <<-EOF -WARNING ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - You are using the 'pacapt' on the old stable branch - which is closed on May 4th, 2014. - - Please consider to switch to new script on 'ng' branch - which is under an active development. See also - - https://github.com/icy/pacapt/tree/ng -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -EOF +} -_OSTYPE_detect +yum_R() { + yum erase "$@" +} -# If the system is Arch-like, pass all of the arguments to /usr/bin/pacman -# and return. This is done here to achieve a consistent help menu. -[[ "$_OSTYPE" == "PACMAN" ]] && exec /usr/bin/pacman "$@" +yum_Si() { + yum info "$@" +} -# -# Get options from command lines. FIXME: Support long options -# +yum_Suy() { + yum update "$@" +} + +yum_Su() { + yum update "$@" +} + +yum_Sy() { + yum check-update "$@" +} + +yum_Ss() { + yum -C search "$@" +} + +yum_Sc() { + yum clean expire-cache "$@" +} + +yum_Scc() { + yum clean packages "$@" +} + +yum_Sccc() { + yum clean all "$@" +} + +yum_S() { + yum install $_TOPT "$@" +} + +yum_U() { + yum localinstall "$@" +} + +yum_Sii() { + yum resolvedep "$@" +} + + + +_zypper_init() { + : +} + +zypper_Qc() { + rpm -q --changelog "$@" +} + +zypper_Qi() { + zypper info "$@" +} + +zypper_Ql() { + rpm -ql "$@" +} + +zypper_Qu() { + zypper list-updates "$@" +} + +zypper_Qm() { + zypper search -si "$@" \ + | grep 'System Packages' +} + +zypper_Qo() { + rpm -qf "$@" +} + +zypper_Qp() { + rpm -qip "$@" +} + +zypper_Qs() { + zypper search --installed-only "$@" +} + +zypper_Q() { + if [[ "$_TOPT" == "q" ]]; then + zypper search -i "$@" \ + | grep ^i \ + | awk '{print $3}' + elif [[ "$_TOPT" == "" ]]; then + zypper search -i "$@" + else + _not_implemented + fi +} + +zypper_Rs() { + if [[ "$_TOPT" == "s" ]]; then + zypper remove "$@" --clean-deps + else + _not_implemented + fi +} + +zypper_R() { + zypper remove "$@" +} + +zypper_Rn() { + # Remove configuration files + while read -r file; do + if [[ -f "$file" ]]; then + rm -fv "$file" + fi + done < <(rpm -ql "$@") + + # Now remove the package per-se + zypper remove "$@" +} + +zypper_Rs() { + if [[ "$_TOPT" == "s" ]]; then + zypper remove "$@" --clean-deps + else + _not_implemented + fi +} + +zypper_Rns() { + # Remove configuration files + while read -r file; do + if [[ -f "$file" ]]; then + rm -fv "$file" + fi + done < <(rpm -ql "$@") + + zypper remove "$@" --clean-deps +} + +zypper_Suy() { + zypper dup "$@" +} + +zypper_Sy() { + zypper refresh "$@" +} + +zypper_Sl() { + if [[ $# -eq 0 ]]; then + zypper pa -R + else + zypper pa -r "$@" + fi +} + +zypper_Ss() { + zypper search "$@" +} + +zypper_Su() { + zypper --no-refresh dup "$@" +} + +zypper_Sc() { + zypper clean "$@" +} + +zypper_Scc() { + zypper clean "$@" +} + +zypper_Sccc() { + # Not way to do this in zypper + _not_implemented +} + +zypper_Si() { + zypper info --requires "$@" +} + +zypper_Sii() { + # Ugly and slow, but does the trick + local packages= + + packages="$(zypper pa -R | cut -d \| -f 3 | tr -s '\n' ' ')" + for package in $packages; do + zypper info --requires "$package" \ + | grep -q "$@" && echo $package + done +} + +zypper_S() { + zypper install $_TOPT "$@" +} + +zypper_Sw() { + zypper install --download-only "$@" +} + +zypper_U() { + zypper install "$@" +} +_validate_operation() { + case "$1" in + "apk_Q") ;; + "apk_Qi") ;; + "apk_Ql") ;; + "apk_Qo") ;; + "apk_Qs") ;; + "apk_Qu") ;; + "apk_R") ;; + "apk_Rn") ;; + "apk_Rns") ;; + "apk_Rs") ;; + "apk_S") ;; + "apk_Sc") ;; + "apk_Scc") ;; + "apk_Sccc") ;; + "apk_Si") ;; + "apk_Sii") ;; + "apk_Sl") ;; + "apk_Ss") ;; + "apk_Su") ;; + "apk_Suy") ;; + "apk_Sy") ;; + "apk_Sw") ;; + "apk_U") ;; + "cave_Q") ;; + "cave_Qi") ;; + "cave_Ql") ;; + "cave_Qo") ;; + "cave_Qp") ;; + "cave_Qu") ;; + "cave_Qs") ;; + "cave_Rs") ;; + "cave_Rn") ;; + "cave_Rns") ;; + "cave_R") ;; + "cave_Si") ;; + "cave_Suy") ;; + "cave_Su") ;; + "cave_Sy") ;; + "cave_Ss") ;; + "cave_Sc") ;; + "cave_Scc") ;; + "cave_Sccc") ;; + "cave_S") ;; + "cave_U") ;; + "conda_Q") ;; + "conda_R") ;; + "conda_S") ;; + "conda_Sc") ;; + "conda_Si") ;; + "conda_Ss") ;; + "conda_Suy") ;; + "dnf_S") ;; + "dnf_Sc") ;; + "dnf_Scc") ;; + "dnf_Sccc") ;; + "dnf_Si") ;; + "dnf_Sg") ;; + "dnf_Sl") ;; + "dnf_Ss") ;; + "dnf_Su") ;; + "dnf_Suy") ;; + "dnf_Sw") ;; + "dnf_Sy") ;; + "dnf_Q") ;; + "dnf_Qc") ;; + "dnf_Qe") ;; + "dnf_Qi") ;; + "dnf_Ql") ;; + "dnf_Qm") ;; + "dnf_Qo") ;; + "dnf_Qp") ;; + "dnf_Qs") ;; + "dnf_Qu") ;; + "dnf_R") ;; + "dnf_U") ;; + "dpkg_Q") ;; + "dpkg_Qi") ;; + "dpkg_Ql") ;; + "dpkg_Qo") ;; + "dpkg_Qp") ;; + "dpkg_Qu") ;; + "dpkg_Qs") ;; + "dpkg_Rs") ;; + "dpkg_Rn") ;; + "dpkg_Rns") ;; + "dpkg_R") ;; + "dpkg_Si") ;; + "dpkg_Suy") ;; + "dpkg_Su") ;; + "dpkg_Sy") ;; + "dpkg_Ss") ;; + "dpkg_Sc") ;; + "dpkg_Scc") ;; + "dpkg_S") ;; + "dpkg_U") ;; + "dpkg_Sii") ;; + "dpkg_Sccc") ;; + "homebrew_Qi") ;; + "homebrew_Ql") ;; + "homebrew_Qo") ;; + "homebrew_Qc") ;; + "homebrew_Qu") ;; + "homebrew_Qs") ;; + "homebrew_Q") ;; + "homebrew_Rs") ;; + "homebrew_R") ;; + "homebrew_Si") ;; + "homebrew_Suy") ;; + "homebrew_Su") ;; + "homebrew_Sy") ;; + "homebrew_Ss") ;; + "homebrew_Sc") ;; + "homebrew_Scc") ;; + "homebrew_Sccc") ;; + "homebrew_S") ;; + "macports_Ql") ;; + "macports_Qo") ;; + "macports_Qc") ;; + "macports_Qu") ;; + "macports_Rs") ;; + "macports_R") ;; + "macports_Si") ;; + "macports_Suy") ;; + "macports_Su") ;; + "macports_Sy") ;; + "macports_Ss") ;; + "macports_Sc") ;; + "macports_Scc") ;; + "macports_S") ;; + "pkgng_Qi") ;; + "pkgng_Ql") ;; + "pkgng_Qo") ;; + "pkgng_Qp") ;; + "pkgng_Qu") ;; + "pkgng_Q") ;; + "pkgng_Rs") ;; + "pkgng_R") ;; + "pkgng_Si") ;; + "pkgng_Suy") ;; + "pkgng_Su") ;; + "pkgng_Sy") ;; + "pkgng_Ss") ;; + "pkgng_Sc") ;; + "pkgng_Scc") ;; + "pkgng_S") ;; + "pkg_tools_Qi") ;; + "pkg_tools_Ql") ;; + "pkg_tools_Qo") ;; + "pkg_tools_Qp") ;; + "pkg_tools_Qu") ;; + "pkg_tools_Q") ;; + "pkg_tools_Rs") ;; + "pkg_tools_Rn") ;; + "pkg_tools_Rns") ;; + "pkg_tools_R") ;; + "pkg_tools_Si") ;; + "pkg_tools_Sl") ;; + "pkg_tools_Suy") ;; + "pkg_tools_Su") ;; + "pkg_tools_Sy") ;; + "pkg_tools_Ss") ;; + "pkg_tools_Sc") ;; + "pkg_tools_Scc") ;; + "pkg_tools_S") ;; + "portage_Qi") ;; + "portage_Ql") ;; + "portage_Qo") ;; + "portage_Qc") ;; + "portage_Qu") ;; + "portage_Q") ;; + "portage_Rs") ;; + "portage_R") ;; + "portage_Si") ;; + "portage_Suy") ;; + "portage_Su") ;; + "portage_Sy") ;; + "portage_Ss") ;; + "portage_Sc") ;; + "portage_Scc") ;; + "portage_Sccc") ;; + "portage_S") ;; + "sun_tools_Qi") ;; + "sun_tools_Ql") ;; + "sun_tools_Qo") ;; + "sun_tools_Qs") ;; + "sun_tools_Q") ;; + "sun_tools_R") ;; + "sun_tools_U") ;; + "swupd_Qk") ;; + "swupd_Qo") ;; + "swupd_Qs") ;; + "swupd_R") ;; + "swupd_Suy") ;; + "swupd_Su") ;; + "swupd_Sy") ;; + "swupd_Ss") ;; + "swupd_S") ;; + "tazpkg_Q") ;; + "tazpkg_Qi") ;; + "tazpkg_Ql") ;; + "tazpkg_Sy") ;; + "tazpkg_Su") ;; + "tazpkg_Suy") ;; + "tazpkg_S") ;; + "tazpkg_R") ;; + "tazpkg_Sc") ;; + "tazpkg_Scc") ;; + "tazpkg_Ss") ;; + "tazpkg_Qo") ;; + "tazpkg_U") ;; + "tlmgr_Qi") ;; + "tlmgr_Qk") ;; + "tlmgr_Ql") ;; + "tlmgr_R") ;; + "tlmgr_S") ;; + "tlmgr_Si") ;; + "tlmgr_Sl") ;; + "tlmgr_Ss") ;; + "tlmgr_Suy") ;; + "tlmgr_U") ;; + "yum_Q") ;; + "yum_Qi") ;; + "yum_Qs") ;; + "yum_Ql") ;; + "yum_Qo") ;; + "yum_Qp") ;; + "yum_Qc") ;; + "yum_Qu") ;; + "yum_Qm") ;; + "yum_Rs") ;; + "yum_R") ;; + "yum_Si") ;; + "yum_Suy") ;; + "yum_Su") ;; + "yum_Sy") ;; + "yum_Ss") ;; + "yum_Sc") ;; + "yum_Scc") ;; + "yum_Sccc") ;; + "yum_S") ;; + "yum_U") ;; + "yum_Sii") ;; + "zypper_Qc") ;; + "zypper_Qi") ;; + "zypper_Ql") ;; + "zypper_Qu") ;; + "zypper_Qm") ;; + "zypper_Qo") ;; + "zypper_Qp") ;; + "zypper_Qs") ;; + "zypper_Q") ;; + "zypper_Rs") ;; + "zypper_R") ;; + "zypper_Rn") ;; + "zypper_Rs") ;; + "zypper_Rns") ;; + "zypper_Suy") ;; + "zypper_Sy") ;; + "zypper_Sl") ;; + "zypper_Ss") ;; + "zypper_Su") ;; + "zypper_Sc") ;; + "zypper_Scc") ;; + "zypper_Sccc") ;; + "zypper_Si") ;; + "zypper_Sii") ;; + "zypper_S") ;; + "zypper_Sw") ;; + "zypper_U") ;; + *) return 1 ;; + esac +} + + + +set -u +unset GREP_OPTIONS + +: "${PACAPT_DEBUG=}" # Show what will be going +: "${GREP:=grep}" # Need to update in, e.g, _sun_tools_init +: "${AWK:=awk}" # Need to update in, e.g, _sun_tools_init + +_sun_tools_init # Dirty tricky patch for SunOS + +export PACAPT_DEBUG GREP AWK -# By default, with Gentoo, 'force' is always 'yes', so we change it to 'no' -[[ "$_OSTYPE" == "PORTAGE" ]] && _FORCE="-a" +_POPT="" # primary operation +_SOPT="" # secondary operation +_TOPT="" # options for operations +_EOPT="" # extra options (directly given to package manager) + # these options will be translated by (_translate_all) method. +_PACMAN="" # name of the package manager + +_PACMAN_detect \ +|| _die "'pacapt' doesn't support your package manager." + +if [[ -z "$PACAPT_DEBUG" ]]; then + [[ "$_PACMAN" != "pacman" ]] \ + || exec "/usr/bin/pacman" "$@" +elif [[ "$PACAPT_DEBUG" != "auto" ]]; then + _PACMAN="$PACAPT_DEBUG" +fi + +case "${1:-}" in +"update") shift; set -- -Sy "$@" ;; +"upgrade") shift; set -- -Su "$@" ;; +"install") shift; set -- -S "$@" ;; +"search") shift; set -- -Ss "$@" ;; +"remove") shift; set -- -R "$@" ;; +"autoremove") shift; set -- -Rs "$@" ;; +"clean") shift; set -- -Scc "$@" ;; +esac + +while :; do + _args="${1-}" + + [[ "${_args:0:1}" == "-" ]] || break + + case "${_args}" in + "--help") + _help + exit 0 + ;; + + "--noconfirm") + shift + _EOPT="$_EOPT:noconfirm:" + continue + ;; + + "-"|"--") + shift + break + ;; + esac + + i=1 + while [[ "$i" -lt "${#_args}" ]]; do + _opt="${_args:$i:1}" + (( i ++ )) + + case "$_opt" in + h) + _help + exit 0 + ;; + V) + _print_pacapt_version; + exit 0 + ;; + P) + _print_supported_operations "$_PACMAN" + exit 0 + ;; -while getopts "URQShfvlumyispqwco" opt 2>/dev/null; do - case "$opt" in Q|S|R|U) - if [[ "$_POPT" != "" && "$_POPT" != "$opt" ]]; then + if [[ -n "$_POPT" && "$_POPT" != "$_opt" ]]; then _error "Only one operation may be used at a time" exit 1 fi - _POPT="$opt" + _POPT="$_opt" ;; + # Comment 2015 May 26th: This part deals with the 2nd option. + # Most of the time, there is only one 2nd option. But some + # operation may need extra and/or duplicate (e.g, Sy <> Syy). + # + # See also + # + # * https://github.com/icy/pacapt/issues/13 + # + # This implementation works, but with a bug. #Rsn works + # but #Rns is translated to #Rn (incorrectly.) + # Thanks Huy-Ngo for this nice catch. + # # FIXME: Please check pacman(8) to see if they are really 2nd operation - s|l|i|p|o|m) - if [[ "$_SOPT" == '' ]]; then - _SOPT="$opt" - else - _TOPT="$opt" - fi + # + e|g|i|l|m|n|o|p|s) + if [[ "$_SOPT" == '' ]]; then + _SOPT="$_opt" + continue + fi + + # Understand it: + # If there is already an option recorded, the incoming option + # will come and compare itself with known one. + # We have a table + # + # known one vs. incoming ? | result + # < | one-new + # = | one-one + # > | new-one + # + # Let's say, after this step, the 3rd option comes (named X), + # and the current result is "a-b". We have a table + # + # a(b) vs. X | result + # < | aX (b dropped) + # = | aa (b dropped) + # > | Xa (b dropped) + # + # In any case, the first one matters. + # + if [[ "${_SOPT:0:1}" < "$_opt" ]]; then + _SOPT="${_SOPT:0:1}$_opt" + elif [[ "${_SOPT:0:1}" == "$_opt" ]]; then + _SOPT="$_opt$_opt" + else + _SOPT="$_opt${_SOPT:0:1}" + fi + ;; - # NOTE: -q is an output option, not an operator! - # Thanks to James Pearson for his catch :) q) - _TOPT="$opt" - ;; + _TOPT="$_opt" ;; # Thanks to James Pearson u) - if [[ "${_SOPT:0:1}" = "y" ]]; then + if [[ "${_SOPT:0:1}" == "y" ]]; then _SOPT="uy" else _SOPT="u" @@ -277,7 +2487,7 @@ while getopts "URQShfvlumyispqwco" opt 2>/dev/null; do ;; y) - if [[ "${_SOPT:0:1}" = "u" ]]; then + if [[ "${_SOPT:0:1}" == "u" ]]; then _SOPT="uy" else _SOPT="y" @@ -285,425 +2495,75 @@ while getopts "URQShfvlumyispqwco" opt 2>/dev/null; do ;; c) - if [[ "${_SOPT:0:2}" = "cc" ]]; then + if [[ "${_SOPT:0:2}" == "cc" ]]; then _SOPT="ccc" - elif [[ "${_SOPT:0:1}" = "c" ]]; then + elif [[ "${_SOPT:0:1}" == "c" ]]; then _SOPT="cc" else - _SOPT="$opt" + _SOPT="$_opt" fi ;; - w) - case "$_OSTYPE" in - "DPKG") _TOPT="-d" ;; - "YUM") - _TOPT="--downloadonly" - if ! rpm -q yum-downloadonly > /dev/null; then - _error "The '-w' option requires the package 'yum-downloadonly'" - _error "Install this with 'yum install -y yum-downloadonly' or" - _error "$0 -S -f yum-downloadonly" - exit 1 - fi - ;; - "MACPORTS") _TOPT="fetch" ;; - "PORTAGE") _TOPT="--fetchonly" ;; - "ZYPPER") _TOPT="--download-only" ;; - esac + w|v) + _EOPT="$_EOPT:$_opt:" ;; - f) - case "$_OSTYPE" in - "DPKG") _FORCE="-f --force-yes" ;; - "YUM") _FORCE="-y" ;; - "MACPORTS") _FORCE="-f" ;; - "PORTAGE") _FORCE="" ;; - *) _FORCE="-y" ;; - esac + *) + # FIXME: If option is unknown, we will break the loop + # FIXME: and this option will be used by the native program. + # FIXME: break 2 + _die "pacapt: Unknown option '$_opt'." ;; + esac + done - v) _VERBOSE="-v" ;; - - h) _help; exit 0 ;; + shift - *) _error "Error: Invalid option"; exit 1 ;; - esac + # If the primary option and the secondary are known + # we would break the argument detection, but for sure we will look + # forward to see there is anything interesting... + if [[ -n "$_POPT" && -n "$_SOPT" ]]; then + case "${1:-}" in + "-w"|"--noconfirm") ;; + *) break;; + esac + + # Don't have anything from the **first** argument. Something wrong. + # FIXME: This means that user must enter at least primary action + # FIXME: or secondary action in the very first part... + elif [[ -z "${_POPT}${_SOPT}${_TOPT}" ]]; then + break + fi done -# Remained options/packages/queries -shift $((OPTIND - 1)) -_PKG="$*" - -# pacman man page (examples) says: "pacman -Syu gpm = Update package list, -# upgrade all packages, and then install gpm if it wasn't already installed." -# Instead, just disallow specific packages, as (ex-)yum users likely expect to -# just update/upgrade one package (and its dependencies) and apt-get and pacman -# have no way to do this. -if [[ -n "$_PKG" ]]; then - case "$_POPT$_SOPT" in - "Su"|"Sy"|"Suy") - _error "The -Sy/u options refresh and/or upgrade all packages" - _error "To install packages as well, use separate commands to do so:" - _error "$0 -S$_SOPT" - _error "$0 -S $_PKG" - exit 1 - esac -fi +[[ -n "$_POPT" ]] \ +|| _die "Usage: pacapt # -h for help, -P list supported functions" -# DEBUG -# echo "Primary options: $_POPT" -# echo "Secondary options: $_SOPT" -# echo "Options for operations: $_TOPT" -# echo "Extra params: $_PKG" -# echo "Verbose: $_VERBOSE" -# echo "Force: $_FORCE" - -# Return if no option was specified -if [[ -z "$_POPT" ]]; then - _error "No operation specified (use -h for help)" +_validate_operation "${_PACMAN}_${_POPT}${_SOPT}" \ +|| { + _not_implemented exit 1 -fi - -# Invoke the native package manager -case "$_POPT$_SOPT" in - #################################################################### - # QUERYING # - #################################################################### - - "Qi") - _exec_ ZYPPER "zypper info $_PKG" - _exec_ DPKG "dpkg-query -s $_PKG" - _exec_ HOMEBREW "brew info $_PKG" - #_exec_ MACPORTS "port info $_PKG" - _os_is MACPORTS && _error "Function not implemented in macports" - _exec_ YUM "yum info $_PKG" - _FORCE="" \ - _exec_ PORTAGE "emerge --info $_PKG" - ;; - "Ql") - _exec_ ZYPPER "echo 'Function is not available'; exit 1" - _exec_ DPKG " - if [[ -n \"$_PKG\" ]]; then - dpkg-query -L $_PKG - else - dpkg -l \ - | grep -E ^[hi]i \ - | awk '{print \$2}' \ - | while read _pkg; do - if [[ \"$_TOPT\" = 'q' ]]; then - dpkg-query -L \$_pkg - else - dpkg-query -L \$_pkg \ - | while read _line; do - echo \$_pkg \$_line - done - fi - done - fi - " - _exec_ HOMEBREW "brew list $_PKG" - _exec_ MACPORTS "port contents $_PKG" - _exec_ YUM "rpm -ql $_PKG" - _FORCE="" \ - _exec_ PORTAGE " - if [[ -x /usr/bin/qlist ]]; then - qlist $_PKG - elif [[ -x /usr/bin/equery ]]; then - equery files $_PKG - else - _error 'You need to install portage-utils or gentoolkit to perform this operation.' - fi - " - ;; - "Qo") - _exec_ ZYPPER "echo 'Function is not available'; exit 1" - - _exec_ DPKG "dpkg-query -S $_PKG" - _exec_ HOMEBREW " - cd \"\$(dirname -- \$(which $_PKG))\" - pkg=\"\$(pwd -P)/\$(basename -- $_PKG)\" - prefix=\"\$(brew --prefix)\" - cellar=\"\$(brew --cellar)\" - for package in \$cellar/*; do - files=(\${package}/*/\${pkg/#\$prefix\//}) - if [[ -e \${files[\${#files[@]} - 1]} ]]; then - echo \"\${package/#\$cellar\//}\" - break - fi - done - " - _exec_ MACPORTS "port provides $_PKG" - _exec_ YUM "rpm -qf $_PKG" - _FORCE="" \ - _exec_ PORTAGE " - if [[ -x /usr/bin/equery ]]; then - equery belongs $_PKG - else - _error 'You need to install gentoolkit to perform this operation' - fi - " - ;; - "Qp") - _exec_ ZYPPER "echo 'Function is not available'; exit 1" - _os_is HOMEBREW && _error "Function not implemented in homebrew" - _os_is MACPORTS && _error "Function not implemented in macports" - _os_is PORTAGE && _error "Function not supported as Gentoo has no definition of 'package file" - - _exec_ DPKG "dpkg-deb -I $_PKG" - _exec_ YUM "rpm -qp $_PKG" - ;; - "Qc") - _exec_ ZYPPER "echo 'Function is not available'; exit 1" - _os_is DPKG && _error "Function not implemented in Debian system" - - _exec_ HOMEBREW "brew log $_PKG" - _exec_ MACPORTS "port log $_PKG" - _exec_ YUM "rpm -q --changelog $_PKG" - _FORCE="" \ - _exec_ PORTAGE "emerge -p --changelog $_PKG" - ;; - "Qu") - _exec_ ZYPPER "zypper list-updates" - _exec_ DPKG "apt-get upgrade --trivial-only $_PKG" - _exec_ HOMEBREW "brew outdated | grep $_PKG" - _exec_ MACPORTS "port outdated $_PKG" - _exec_ YUM "yum list updates $_PKG" - _exec_ PORTAGE "emerge -uvN $_PKG" # FIXME: not exactly - ;; - "Qm") - _exec_ ZYPPER "zypper search -si | grep 'System Packages'" - _os_is DPKG && _error "Function not implemented in Debian system" - _os_is HOMEBREW && _error "Function not implemented in homebrew" - _os_is MACPORTS && _error "Function not implemented in macports" - _os_is PORTAGE && _error "Function not supported in Gentoo" - - _exec_ YUM "yum list extras $_PKG" - ;; - "Qs") - _exec_ DPKG "dpkg-query -W '*$_PKG*' | cut -f1" - _os_is HOMEBREW && _error "Function not implemented in homebrew" - _os_is MACPORTS && _error "Function not implemented in macports" - _os_is PORTAGE && _error "Function not implemented in Gentoo" - _os_is YUM && _error "Function not implemented in Yum" - _os_is ZYPPER && _error "Function not implemented in Zypper" - ;; - - "Q") - _exec_ ZYPPER "zypper search -i" - if [[ "$_TOPT" = 'q' ]]; then - # FIXME: Should we redirect user to a similar operation? - _os_is HOMEBREW && _error "Function not implemented in homebrew" - _os_is MACPORTS && _error "Function not implemented in macports" - _os_is PORTAGE && _error "Function not supported as Gentoo has no definition of 'package file" - _os_is YUM && _error "Function not implemented in Yum" - - _exec_ DPKG "dpkg -l | grep -E ^[hi]i | awk '{print \$2}'" - elif [[ "$_TOPT" = "" ]]; then - _exec_ DPKG "dpkg -l $_PKG | grep -E ^[hi]i" - _exec_ HOMEBREW "brew list | grep $_PKG" - _exec_ MACPORTS "port installed $_PKG" - _exec_ YUM "yum list installed $_PKG" - - # FIXME: There are actually many ways to do this in Gentoo - _FORCE="" \ - _exec_ PORTAGE " - if [[ -x /usr/bin/eix ]]; then - eix -I $_PKG - elif [[ -x /usr/bin/equery ]]; then - equery list '*' $_PKG - else - LS_COLORS=never ls -1 -d /var/db/pkg/*/* - fi - " - else - _error "Error: Invalid option" - exit 1 - fi - ;; - - #################################################################### - # REMOVING # - #################################################################### - - "Rs") - if [[ "$_TOPT" = 's' ]]; then - _os_is DPKG && _error "Function not implemented in Debian system" - _os_is YUM && _error "Function not implemented in Yum" - _os_is PORTAGE && _error "Function not supported in Gentoo" - - _exec_ ZYPPER "zypper remove $_PKG --clean-deps" - _exec_ HOMEBREW " - brew rm $_PKG - brew rm \$(join <(brew leaves) <(brew deps $_PKG)) - " - _os_is MACPORTS && _error "Function not implemented in macports" - elif [[ "$_TOPT" = '' ]]; then - _exec_ ZYPPER "echo 'Function is not available'; exit 1" - _os_is HOMEBREW && _error "Function not implemented in homebrew" - _exec_ MACPORTS "port uninstall --follow-dependencies $_PKG" - _exec_ DPKG "apt-get autoremove $_PKG" - _exec_ YUM "yum erase $_PKG" - _exec_ PORTAGE "emerge --depclean world $_PKG" - else - _error "Error: Invalid option" - fi - ;; - "R") - _exec_ ZYPPER "zypper remove $_PKG" - _exec_ DPKG "apt-get remove $_PKG" - _exec_ HOMEBREW "brew remove $_PKG" - _exec_ MACPORTS "port uninstall $_PKG" - _exec_ YUM "yum erase $_PKG" - _exec_ PORTAGE "emerge --depclean $_PKG" - ;; - - #################################################################### - # SYNCHRONIZING # - #################################################################### - - "Si") - _exec_ ZYPPER "echo 'Function is not available'; exit 1" - _exec_ DPKG "apt-cache show $_PKG" - _exec_ HOMEBREW "brew info $_PKG" - _exec_ MACPORTS "port info $_PKG" - _exec_ YUM "yum info $_PKG" - _FORCE="" \ - _exec_ PORTAGE "emerge --info $_PKG" - ;; - "Suy") - _exec_ ZYPPER "zypper dup" - _VERBOSE="" \ - _exec_ DPKG "apt-get update; apt-get upgrade" - _exec_ HOMEBREW "brew update; brew upgrade" - _exec_ MACPORTS "port selfupdate; port upgrade outdated" - _exec_ YUM "yum update" - _exec_ PORTAGE " - if [[ -x /usr/bin/layman ]]; then - layman --sync-all \ - && emerge --sync \ - && emerge -auND world $_PKG - else - emerge --sync \ - && emerge -uND world $_PKG - fi - " - ;; - "Su") - _exec_ ZYPPER "echo 'Function is not available'; exit 1" - _VERBOSE="" \ - _exec_ DPKG "apt-get upgrade" - _exec_ HOMEBREW "brew upgrade" - _exec_ MACPORTS "port upgrade outdated" - _exec_ YUM "yum update" - _exec_ PORTAGE "emerge -uND world $_PKG" - ;; - "Sy") - _exec_ ZYPPER "zypper refresh" - _VERBOSE="" \ - _exec_ DPKG "apt-get update" - _exec_ HOMEBREW "brew update" - _exec_ MACPORTS "port selfupdate" # or sync? - _exec_ YUM "yum check-update" - _FORCE="" \ - _exec_ PORTAGE " - if [[ -x /usr/bin/layman ]]; then - layman --sync-all && emerge --sync - else - emerge --sync - fi - " - ;; - "Ss") - _exec_ ZYPPER "zypper search $_PKG" - _exec_ DPKG "apt-cache search $_PKG" - _exec_ HOMEBREW "brew search $_PKG" - _exec_ MACPORTS "port search $_PKG" - _exec_ YUM "yum -C search $_PKG" - _FORCE="" \ - _exec_ PORTAGE " - if [[ -x /usr/bin/eix ]]; then - eix $_PKG - else - emerge --search $_PKG - fi - " - ;; - "Sc") - _exec_ ZYPPER "zypper clean" - _exec_ DPKG "apt-get clean" - _exec_ HOMEBREW "brew cleanup" - _exec_ MACPORTS "port clean --all inactive" - _exec_ YUM "yum clean expire-cache" - __FORCE="" \ - _exec_ PORTAGE " - if [[ -x /usr/bin/eclean-dist ]]; then - eclean-dist -d -t1m -s50 -f - else - _error 'You need install gentoolkit to perform this operation.' - fi - " - ;; - "Scc") - _exec_ ZYPPER "zypper clean" - _exec_ DPKG "apt-get autoclean" - _exec_ HOMEBREW "brew cleanup -s" - _exec_ MACPORTS "port clean --all installed" - _exec_ YUM "yum clean packages" - _FORCE="" \ - _exec_ PORTAGE " - if [[ -x /usr/bin/eclean ]]; then - eclean -i distfiles - else - _error 'You need install gentoolkit to perform this operation.' - fi - " - ;; - "Sccc") - _exec_ ZYPPER "echo 'Function is not available'; exit 1" - _exec_ DPKG "rm -fv /var/cache/apt/*.bin - rm -fv /var/cache/apt/archives/*.* - rm -fv /var/lib/apt/lists/*.* - apt-get autoclean" - _exec_ HOMEBREW 'rm -rf $(brew --cache)' - _os_is MACPORTS && _error "Function not implemented in macports" - _exec_ YUM "yum clean all" - _FORCE="" \ - _exec_ PORTAGE "rm -fv /usr/portage/distfiles/*.*" - ;; - "S") - [[ -z "$_PKG" ]] \ - && { _error "You must specify a package"; exit 1; } - - _exec_ ZYPPER "zypper install $_TOPT $_PKG" - _exec_ DPKG "apt-get install $_TOPT $_PKG" - _exec_ HOMEBREW "brew install $_TOPT $_PKG" - if [[ "$_TOPT" = fetch ]] - then - _exec_ MACPORTS "port patch $_PKG" - else - _exec_ MACPORTS "port install $_TOPT $_PKG" - fi - _exec_ YUM "yum install $_TOPT $_PKG" - _exec_ PORTAGE "emerge $_TOPT $_PKG" - ;; - - #################################################################### - # UPGRADING # - #################################################################### - - "U") - _exec_ ZYPPER "zypper install $_PKG" - _os_is HOMEBREW && _error "Function not implemented in homebrew" - _os_is MACPORTS && _error "Function not implemented in macports" - _os_is PORTAGE && _error "You need to implement a local overlay and do the installation as usual." +} - _exec_ DPKG "dpkg -i $_TOPT $_PKG" - _exec_ YUM "yum localinstall $_TOPT $_PKG" - ;; +_translate_all || exit + +if [[ -n "$*" ]]; then + case "${_POPT}${_SOPT}" in + "Su"|"Sy"|"Suy") + echo 1>&2 "WARNING ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" + echo 1>&2 " The -Sy/u options refresh and/or upgrade all packages." + echo 1>&2 " To install packages as well, use separate commands:" + echo 1>&2 + echo 1>&2 " $0 -S$_SOPT; $0 -S ${*}" + echo 1>&2 "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" + esac +fi - # Default option - *) - _error "Error: Invalid option" - exit 1 - ;; -esac +if [[ -n "$PACAPT_DEBUG" ]]; then + echo "pacapt: $_PACMAN, p=$_POPT, s=$_SOPT, t=$_TOPT, e=$_EOPT" + echo "pacapt: execute '${_PACMAN}_${_POPT}${_SOPT} $_EOPT ${*}'" + declare -f "${_PACMAN}_${_POPT}${_SOPT}" +else + "_${_PACMAN}_init" || exit + "${_PACMAN}_${_POPT}${_SOPT}" $_EOPT "$@" +fi diff --git a/tests/Makefile b/tests/Makefile new file mode 100644 index 00000000..bdba1848 --- /dev/null +++ b/tests/Makefile @@ -0,0 +1,35 @@ +TESTS = *.txt + +.PHONY: default +default: + @grep -E "^# [a-z].+:" Makefile + +# gen: +# gen: Generating `pacapt.dev` +tmp/pacapt.dev:: + @( cd ../ && make -s pacapt.dev ; ) + @mkdir -p ./tmp/ + @cp -u ../pacapt.dev tmp/ + @chmod 755 $(@) + +# all: +# all: Execute all test scripts +# all: To execute a subset of tests, list them with TESTS=... +# all: for example, `make all TESTS="foo.txt bar.txt" +.PHONY: all +all: tmp/pacapt.dev + @./test.sh $(TESTS) + +# gen: +# gen: Generate test scripts but don't execute them +.PHONY: gen +gen: tmp/pacapt.dev + @TESTS_DO_NOT_RUN=1 ./test.sh *.txt + +# clean: +# clean: Remove all temporary files under /tmp/ +# clean: (but still keep all log files.) +.PHONY: clean +clean: + @rm -fv tmp/*.sh tmp/pacapt.dev + @echo Please remove tmp/*.log manually. diff --git a/tests/README.md b/tests/README.md new file mode 100644 index 00000000..b504a3ac --- /dev/null +++ b/tests/README.md @@ -0,0 +1,107 @@ +[![Build Status](https://travis-ci.org/icy/pacapt.svg?branch=ng)](https://travis-ci.org/icy/pacapt) + +## Table of contents + +See also https://travis-ci.org/icy/pacapt. + +1. [Preparing test environment](#preparing-test-environment) +1. [Invoking test scripts](#invoking-test-scripts) +1. [Writing test cases](#writing-test-cases) +1. [Notes](#notes-on-writing-test-cases) + +## Preparing test environment + +We run `#bash` test scripts inside `Docker` container. We will need +the following things + +1. A fast network to download base images from http://hub.docker.com/, + and to execute `pacman -Sy` command; +1. Our user environment can execute `docker run` command to create + new container and mount some host volumes; +1. A basic `Ruby` environment to execute `./bin/gen_tests.rb` script. + +Basically we execute the following command + + $ cd + + $ mkdir -p tests/tmp/ + $ ruby -n ./bin/gen_tests.rb \ + < tests/dpkg.txt \ + > tests/tmp/test.sh + + $ cd tests/tmp/ + $ docker run --rm \ + -v $PWD/test.sh:/tmp/test.sh \ + -v $PWD/pacapt.dev:/usr/bin/pacman \ + ubuntu:18.04 \ + /tmp/test.sh + +This command will return 0 if all tests pass, or return 1 if any test fails. + +For more details, please see in `Makefile` and `test.sh`. + +## Invoking test scripts + +There are `Makefile` and `test.sh` written and tested in an Arch/Ubuntu +machine. It is expected to work in similar environment with `GNU Make`, +`Bash` and `Docker`. + + $ make # List all sections + $ make all # Execute all test scripts + # or a subset of tests, as below + $ make TESTS="foo.txt bar.txt" + +This script will create a temporary directory `tests/tmp/` to store +all logs and details. If there is any test script fails, the process +is stopped for investigation. + +## Writing test cases + +See examples in `tests/dpkg.txt`. Each test case combines of input command +and output regular expressions used by `grep -E`. Input command is started +by `in `, output regexp is started by `ou `. For example, + + in -Sy + in -Qs htop + ou ^htop + +the test is passed if the following group of commands returns successfully + + pacman -Sy + pacman -Qs htop | grep -qE '^htop' + +If we want to execute some command other than `pacman` script, use `!` +to write our original command. For example, + + in ! echo Y | pacman -S htop + in ! echo Y | pacman -R htop + in -Qi htop + ou ^Status: deinstall + +On `Debian`/`Ubuntu` system, this test case is to ensure that the script +can install `htop` package, then remove it. An alternative test is + + echo Y | pacman -S htop + echo Y | pacman -R htop + pacman -Qi htop | grep -E '^Status: deinstall' + +## Notes on writing test cases + +1. To specify a list of container images, use `im image [image]...`; +1. Each test case has its own temporary file to store all output; +1. Multiple uses of `test.in` is possible, and all results are appended + to test's temporary file. If we want to clear the contents of this output + please use `in clear`; +1. Multiple uses of `test.ou` is possible; any fail check will increase + the total number of failed tests; +1. To make sure that test's temporary file is empty, use `ou empty`; +1. Tests are executed by orders provided in the source file. It's better + to execute `pacman -Sy` to update package manager's database before + any other tests, and it's also better to test `clean up` features + (`pacman -Sc`, `pacman -Scc`, ...) at the very end of the source file. + See `lib/dpkg.sh` for an example; +1. All tests are executed by `#bash` shell. +1. In any input command, `$LOG` (if any) is replaced by the path to + test's temporary file; +1. It's very easy to trick the test mechanism; it's our duty to make + the tests as simple as possible. diff --git a/tests/dpkg.txt b/tests/dpkg.txt new file mode 100644 index 00000000..4dd1fd4c --- /dev/null +++ b/tests/dpkg.txt @@ -0,0 +1,101 @@ +#!/bin/sh + +# Purpose : Testing script for dpkg support +# Author : Ky-Anh Huynh +# License : MIT + +# FIXME: debian:squeeze It's unable to run `pacman -Sy` +im ubuntu:14.04 ubuntu:16.04 ubuntu:18.04 debian:buster debian:jessie debian:stretch + +# Remove `docker-clean` because we need *.deb files under /var/cache +in ! rm -fv /etc/apt/apt.conf.d/docker-clean +ou ^removed .*docker-clean + +# Update package databses +in -Sy + +# Simple query that lists all packages +in -Qq +ou ^apt$ + +# Listing with version and package name +in -Q +# ou ^ii +apt +.+ubuntu +ou ^ii +apt + + +# Information of `apt` +in -Qi apt +ou ^Package: apt$ +ou ^Status: install ok installed$ +ou ^Priority: (important|required)$ + +# File listing for `apt` +in -Ql apt +ou ^/usr/bin/apt-key + +# File listing (all packages) +# FIXME: Debian-squeeze doesn't have /usr/bin/apt +in -Ql +ou ^apt /usr/bin/apt-key$ + +# File listing (all packages), files only +in -Qql +ou ^/usr/bin/apt-key$ + +# Look up package name +in -Qo /bin/bash +ou ^bash: /bin/bash$ + +# Search for pattern in installed package (names + descriptions) +in -Qs bash +ou ^bash.+Bourne + +in -Qs bourne +ou ^bash.+Bourne + +in -Qs screen +ou empty + +in -Qs "Bourne Again" +ou ^bash.+Bourne + +# Install and Deinstall a package + +# Download without installation +in ! echo Y | pacman -Sw screen +in ! which screen || echo "command not found" +ou command not found + +# Display remote package information +in -Si screen +ou ^Package: screen + +in -Sii screen +ou ^Reverse Depends: + +# Now installation +in ! echo Y | pacman -S screen +in ! which screen +ou ^/usr/bin/screen + +# Now remove the package +in ! echo Y | pacman -R screen +in -Qi screen +ou ^Status: deinstall + +# Query information from a .deb package +in ! export DEB_FILE=`find /var/cache/apt/ -type f -iname "screen*.deb"` +in -Qp $DEB_FILE +ou ^ Package: screen + +# Clean up package databases +in -Sc +in clear +in -sS tmux +ou tmux + +# Strong cleaning up +in -Sccc +in clear +in -sS tmux +ou empty diff --git a/tests/slitaz40.txt b/tests/slitaz40.txt new file mode 100644 index 00000000..67fb06d6 --- /dev/null +++ b/tests/slitaz40.txt @@ -0,0 +1,70 @@ +#!/bin/sh + +# Purpose : Testing script for Slitaz support +# Author : Ky-Anh Huynh +# License : MIT + +im icymatter/slitaz40-minimal + +in -Sy +ou (Connecting to)|(Recharging repository) +ou (is ready to use)|(Main is up to date) + +in -Q +ou ^busybox.+[[:digit:]]+ + +# FIXME: slitaz generates some special characters +in -Qq +ou ^busybox + +in -Qi busybox +ou Package.+:.+busybox + +in -Ql busybox +ou ^/bin/busybox + +in -sS htop +ou ^htop((-)|( +))[[:digit:]]+ + +in -Qs htop +ou not implemented + +in -Qo /bin/busybox +ou ^busybox + +in ! echo Y | pacman -S htop +ou htop.+ is installed. + +in ! echo Y | pacman -S --forced htop +ou Unknown option + +in ! echo Y | pacman -S -- --forced htop +ou htop.+ is installed. + +# FIXME: Slitaz-4.0: test passed +# FIXME: Slitaz-5.0: test failed. Possibly a tazpkg bug? +in ! echo N | pacman -R htop +ou Uninstallation of htop cancelled + +# FIXME: Weird, slitaz doesn't understand Y :) +# FIXME: It's actually a timeout problem here...? +in ! echo Y | pacman -R htop +ou Uninstallation of htop cancelled + +in ! echo y | pacman -R htop +ou Removing all files installed + +in ! export TAZPKG_FILE=`find /var/cache/tazpkg/ -type f -iname "htop*.tazpkg"` +in -U $TAZPKG_FILE +ou htop.+ is installed. + +in -Sccc +ou not implemented + +in -Scc +in clear + +# FIXME: Slitaz-4.0: passed +# FIXME: Slitaz-5.0: failed; `-Ss` runs `-Sy` automatically. +in -sS htop +ou No 'packages.list' found to check diff --git a/tests/test.sh b/tests/test.sh new file mode 100755 index 00000000..ece10de2 --- /dev/null +++ b/tests/test.sh @@ -0,0 +1,62 @@ +#!/bin/bash + +# Purpose: Execute test using data from foobar.txt + +# $1: Input file +_test() { + local _file="${1:-}" + local _basename="$(basename "$_file" .txt)" + local _images= + + # See if input is provided with .txt extension + if [[ "$(basename "$_file")" == "$_basename" ]]; then + _file="$_file.txt" + fi + + if [[ ! -f "$_file" ]]; then + echo >&2 ":: File not found '$_file'. Return(1)." + return 1 + fi + + echo >&2 ":: Generating 'tmp/$_basename.sh'..." + ruby -n ../bin/gen_tests.rb < "$_file" > "tmp/$_basename.sh" + chmod 755 "tmp/$_basename.sh" + + bash -n "tmp/$_basename.sh" + if [[ $? -ge 1 ]]; then + return 1 + fi + + if [[ "${TESTS_DO_NOT_RUN:-}" == 1 ]]; then + return 0 + fi + + _images="$(grep -m1 -E '^im ' "$_file")" + _count=0 + for _img in $_images; do + [[ $_img == "im" ]] && continue + (( _count ++ )) + echo >&2 ":: Testing $_basename with $_img" + ( + cd tmp/ || return 1 + docker run --rm \ + -v $PWD/pacapt.dev:/usr/bin/pacman \ + -v "$PWD/$_basename.sh:/tmp/test.sh" \ + $_img \ + /tmp/test.sh 2>"$_basename.${_img//\//-}.log" + ) + if [[ $? -ge 1 ]]; then + echo >&2 "FAIL: $_basename/$_img" + return 1 + fi + done + + if [[ $_count == 0 ]]; then + echo >&2 "WARN: $_basename: Forget to specify 'im ' instruction?" + fi +} + +while (( $# )); do + _test $1 || exit 1 + shift +done