2017-04-29 19:12:12 +02:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2017-08-02 19:24:03 +02:00
|
|
|
"sort"
|
|
|
|
"strings"
|
2018-02-19 05:25:36 +01:00
|
|
|
"sync"
|
2017-04-29 19:12:12 +02:00
|
|
|
|
2017-08-02 19:24:03 +02:00
|
|
|
alpm "github.com/jguer/go-alpm"
|
2017-05-02 17:46:14 +02:00
|
|
|
rpc "github.com/mikkeloscar/aur"
|
2017-04-29 19:12:12 +02:00
|
|
|
)
|
|
|
|
|
2017-08-02 19:24:03 +02:00
|
|
|
// Query is a collection of Results
|
|
|
|
type aurQuery []rpc.Pkg
|
2017-05-02 17:46:14 +02:00
|
|
|
|
2017-08-02 19:24:03 +02:00
|
|
|
// Query holds the results of a repository search.
|
|
|
|
type repoQuery []alpm.Package
|
|
|
|
|
|
|
|
func (q aurQuery) Len() int {
|
|
|
|
return len(q)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (q aurQuery) Less(i, j int) bool {
|
|
|
|
if config.SortMode == BottomUp {
|
|
|
|
return q[i].NumVotes < q[j].NumVotes
|
|
|
|
}
|
|
|
|
return q[i].NumVotes > q[j].NumVotes
|
|
|
|
}
|
|
|
|
|
|
|
|
func (q aurQuery) Swap(i, j int) {
|
|
|
|
q[i], q[j] = q[j], q[i]
|
|
|
|
}
|
|
|
|
|
2017-08-07 15:43:25 +02:00
|
|
|
// FilterPackages filters packages based on source and type from local repository.
|
|
|
|
func filterPackages() (local []alpm.Package, remote []alpm.Package,
|
|
|
|
localNames []string, remoteNames []string, err error) {
|
2017-10-18 04:38:19 +02:00
|
|
|
localDb, err := alpmHandle.LocalDb()
|
2017-08-07 15:43:25 +02:00
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
2017-10-18 04:38:19 +02:00
|
|
|
dbList, err := alpmHandle.SyncDbs()
|
2017-08-07 15:43:25 +02:00
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
f := func(k alpm.Package) error {
|
|
|
|
found := false
|
|
|
|
// For each DB search for our secret package.
|
|
|
|
_ = dbList.ForEach(func(d alpm.Db) error {
|
|
|
|
if found {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
_, err := d.PkgByName(k.Name())
|
|
|
|
if err == nil {
|
|
|
|
found = true
|
|
|
|
local = append(local, k)
|
|
|
|
localNames = append(localNames, k.Name())
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
if !found {
|
|
|
|
remote = append(remote, k)
|
|
|
|
remoteNames = append(remoteNames, k.Name())
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
err = localDb.PkgCache().ForEach(f)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-08-02 19:24:03 +02:00
|
|
|
// NarrowSearch searches AUR and narrows based on subarguments
|
|
|
|
func narrowSearch(pkgS []string, sortS bool) (aurQuery, error) {
|
|
|
|
if len(pkgS) == 0 {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
r, err := rpc.Search(pkgS[0])
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(pkgS) == 1 {
|
|
|
|
if sortS {
|
2017-08-04 11:26:53 +02:00
|
|
|
sort.Sort(aurQuery(r))
|
2017-08-02 19:24:03 +02:00
|
|
|
}
|
|
|
|
return r, err
|
|
|
|
}
|
|
|
|
|
|
|
|
var aq aurQuery
|
|
|
|
var n int
|
|
|
|
|
|
|
|
for _, res := range r {
|
|
|
|
match := true
|
|
|
|
for _, pkgN := range pkgS[1:] {
|
|
|
|
if !(strings.Contains(res.Name, pkgN) || strings.Contains(strings.ToLower(res.Description), pkgN)) {
|
|
|
|
match = false
|
|
|
|
break
|
|
|
|
}
|
2017-05-02 17:46:14 +02:00
|
|
|
}
|
|
|
|
|
2017-08-02 19:24:03 +02:00
|
|
|
if match {
|
|
|
|
n++
|
|
|
|
aq = append(aq, res)
|
2017-05-02 17:46:14 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-02 19:24:03 +02:00
|
|
|
if sortS {
|
|
|
|
sort.Sort(aq)
|
|
|
|
}
|
|
|
|
|
|
|
|
return aq, err
|
2017-05-02 17:46:14 +02:00
|
|
|
}
|
|
|
|
|
2017-04-29 19:12:12 +02:00
|
|
|
// SyncSearch presents a query to the local repos and to the AUR.
|
2017-05-02 17:46:14 +02:00
|
|
|
func syncSearch(pkgS []string) (err error) {
|
2017-08-02 19:24:03 +02:00
|
|
|
aq, err := narrowSearch(pkgS, true)
|
2017-04-29 19:12:12 +02:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-08-04 11:26:53 +02:00
|
|
|
pq, _, err := queryRepo(pkgS)
|
2017-04-29 19:12:12 +02:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-08-02 19:24:03 +02:00
|
|
|
if config.SortMode == BottomUp {
|
2017-12-30 07:01:55 +01:00
|
|
|
aq.printSearch(1)
|
2017-08-04 11:26:53 +02:00
|
|
|
pq.printSearch()
|
2017-04-29 19:12:12 +02:00
|
|
|
} else {
|
2017-08-04 11:26:53 +02:00
|
|
|
pq.printSearch()
|
2017-12-30 07:01:55 +01:00
|
|
|
aq.printSearch(1)
|
2017-04-29 19:12:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// SyncInfo serves as a pacman -Si for repo packages and AUR packages.
|
2018-02-14 17:10:07 +01:00
|
|
|
func syncInfo(pkgS []string) (err error) {
|
2018-02-19 00:46:25 +01:00
|
|
|
var info []rpc.Pkg
|
|
|
|
aurS, repoS, err := packageSlices(pkgS)
|
2017-04-29 19:12:12 +02:00
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-02-19 00:46:25 +01:00
|
|
|
if len(aurS) != 0 {
|
2018-03-13 22:36:42 +01:00
|
|
|
noDb := make([]string, 0, len(aurS))
|
|
|
|
|
|
|
|
for _, pkg := range aurS {
|
|
|
|
_, name := splitDbFromName(pkg)
|
|
|
|
noDb = append(noDb, name)
|
|
|
|
}
|
|
|
|
|
|
|
|
info, err = aurInfo(noDb)
|
2018-02-19 00:46:25 +01:00
|
|
|
if err != nil {
|
|
|
|
fmt.Println(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-03 03:45:16 +01:00
|
|
|
// Repo always goes first
|
2018-01-06 14:50:54 +01:00
|
|
|
if len(repoS) != 0 {
|
2018-02-14 17:10:07 +01:00
|
|
|
arguments := cmdArgs.copy()
|
|
|
|
arguments.delTarget(aurS...)
|
2018-01-15 16:30:29 +01:00
|
|
|
err = passToPacman(arguments)
|
2018-01-06 14:50:54 +01:00
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-05 23:30:42 +02:00
|
|
|
if len(aurS) != 0 {
|
2018-02-19 00:46:25 +01:00
|
|
|
for _, pkg := range info {
|
|
|
|
PrintInfo(&pkg)
|
2017-07-05 23:30:42 +02:00
|
|
|
}
|
2017-04-29 19:12:12 +02:00
|
|
|
}
|
2018-01-20 23:37:10 +01:00
|
|
|
|
2017-04-29 19:12:12 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-08-02 19:24:03 +02:00
|
|
|
// Search handles repo searches. Creates a RepoSearch struct.
|
|
|
|
func queryRepo(pkgInputN []string) (s repoQuery, n int, err error) {
|
2017-10-18 04:38:19 +02:00
|
|
|
dbList, err := alpmHandle.SyncDbs()
|
2017-08-02 19:24:03 +02:00
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// BottomUp functions
|
|
|
|
initL := func(len int) int {
|
|
|
|
if config.SortMode == TopDown {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
return len - 1
|
|
|
|
}
|
|
|
|
compL := func(len int, i int) bool {
|
|
|
|
if config.SortMode == TopDown {
|
|
|
|
return i < len
|
|
|
|
}
|
|
|
|
return i > -1
|
|
|
|
}
|
|
|
|
finalL := func(i int) int {
|
|
|
|
if config.SortMode == TopDown {
|
|
|
|
return i + 1
|
|
|
|
}
|
|
|
|
return i - 1
|
|
|
|
}
|
|
|
|
|
|
|
|
dbS := dbList.Slice()
|
|
|
|
lenDbs := len(dbS)
|
|
|
|
for f := initL(lenDbs); compL(lenDbs, f); f = finalL(f) {
|
|
|
|
pkgS := dbS[f].PkgCache().Slice()
|
|
|
|
lenPkgs := len(pkgS)
|
|
|
|
for i := initL(lenPkgs); compL(lenPkgs, i); i = finalL(i) {
|
|
|
|
match := true
|
|
|
|
for _, pkgN := range pkgInputN {
|
|
|
|
if !(strings.Contains(pkgS[i].Name(), pkgN) || strings.Contains(strings.ToLower(pkgS[i].Description()), pkgN)) {
|
|
|
|
match = false
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if match {
|
|
|
|
n++
|
|
|
|
s = append(s, pkgS[i])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// PackageSlices separates an input slice into aur and repo slices
|
2018-02-19 00:46:25 +01:00
|
|
|
func packageSlices(toCheck []string) (aur []string, repo []string, err error) {
|
2017-10-18 04:38:19 +02:00
|
|
|
dbList, err := alpmHandle.SyncDbs()
|
2017-08-02 19:24:03 +02:00
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-01-17 22:48:23 +01:00
|
|
|
for _, _pkg := range toCheck {
|
2018-03-10 20:14:37 +01:00
|
|
|
db, name := splitDbFromName(_pkg)
|
2018-03-19 01:11:40 +01:00
|
|
|
found := false
|
2017-08-02 19:24:03 +02:00
|
|
|
|
2018-03-13 22:36:42 +01:00
|
|
|
if db == "aur" {
|
|
|
|
aur = append(aur, _pkg)
|
|
|
|
continue
|
|
|
|
} else if db != "" {
|
|
|
|
repo = append(repo, _pkg)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2018-03-19 01:11:40 +01:00
|
|
|
_ = dbList.ForEach(func(db alpm.Db) error {
|
|
|
|
_, err := db.PkgByName(name)
|
|
|
|
|
|
|
|
if err == nil {
|
|
|
|
found = true
|
|
|
|
return fmt.Errorf("")
|
|
|
|
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
})
|
2017-08-02 19:24:03 +02:00
|
|
|
|
|
|
|
if !found {
|
2018-03-19 01:11:40 +01:00
|
|
|
_, errdb := dbList.PkgCachebyGroup(name)
|
2018-01-17 22:48:23 +01:00
|
|
|
found = errdb == nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if found {
|
2018-03-10 20:14:37 +01:00
|
|
|
repo = append(repo, _pkg)
|
2018-01-17 22:48:23 +01:00
|
|
|
} else {
|
2018-03-10 20:14:37 +01:00
|
|
|
aur = append(aur, _pkg)
|
2017-08-02 19:24:03 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// HangingPackages returns a list of packages installed as deps
|
|
|
|
// and unneeded by the system
|
|
|
|
func hangingPackages() (hanging []string, err error) {
|
2017-10-18 04:38:19 +02:00
|
|
|
localDb, err := alpmHandle.LocalDb()
|
2017-08-02 19:24:03 +02:00
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
f := func(pkg alpm.Package) error {
|
|
|
|
if pkg.Reason() != alpm.PkgReasonDepend {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
requiredby := pkg.ComputeRequiredBy()
|
|
|
|
if len(requiredby) == 0 {
|
|
|
|
hanging = append(hanging, pkg.Name())
|
2018-03-02 05:54:38 +01:00
|
|
|
fmt.Println(pkg.Name() + ": " + magenta(human(pkg.ISize())))
|
2017-08-02 19:24:03 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
err = localDb.PkgCache().ForEach(f)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Statistics returns statistics about packages installed in system
|
|
|
|
func statistics() (info struct {
|
|
|
|
Totaln int
|
|
|
|
Expln int
|
|
|
|
TotalSize int64
|
|
|
|
}, err error) {
|
|
|
|
var tS int64 // TotalSize
|
|
|
|
var nPkg int
|
|
|
|
var ePkg int
|
|
|
|
|
2017-10-18 04:38:19 +02:00
|
|
|
localDb, err := alpmHandle.LocalDb()
|
2017-08-02 19:24:03 +02:00
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, pkg := range localDb.PkgCache().Slice() {
|
|
|
|
tS += pkg.ISize()
|
|
|
|
nPkg++
|
|
|
|
if pkg.Reason() == 0 {
|
|
|
|
ePkg++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
info = struct {
|
|
|
|
Totaln int
|
|
|
|
Expln int
|
|
|
|
TotalSize int64
|
|
|
|
}{
|
|
|
|
nPkg, ePkg, tS,
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-03-03 03:45:16 +01:00
|
|
|
// Queries the aur for information about specified packages.
|
|
|
|
// All packages should be queried in a single rpc request except when the number
|
|
|
|
// of packages exceeds the number set in config.RequestSplitN.
|
|
|
|
// If the number does exceed config.RequestSplitN multiple rpc requests will be
|
|
|
|
// performed concurrently.
|
2018-02-17 18:39:10 +01:00
|
|
|
func aurInfo(names []string) ([]rpc.Pkg, error) {
|
|
|
|
info := make([]rpc.Pkg, 0, len(names))
|
|
|
|
seen := make(map[string]int)
|
2018-02-19 05:25:36 +01:00
|
|
|
var mux sync.Mutex
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
var err error
|
2018-02-17 18:39:10 +01:00
|
|
|
|
2018-02-19 06:02:05 +01:00
|
|
|
missing := make([]string, 0, len(names))
|
|
|
|
orphans := make([]string, 0, len(names))
|
|
|
|
outOfDate := make([]string, 0, len(names))
|
|
|
|
|
2018-02-19 05:25:36 +01:00
|
|
|
makeRequest := func(n, max int) {
|
2018-03-08 22:34:12 +01:00
|
|
|
defer wg.Done()
|
2018-02-19 05:25:36 +01:00
|
|
|
tempInfo, requestErr := rpc.Info(names[n:max])
|
2018-02-17 18:39:10 +01:00
|
|
|
if err != nil {
|
2018-02-19 05:25:36 +01:00
|
|
|
return
|
2018-02-17 18:39:10 +01:00
|
|
|
}
|
2018-02-19 05:25:36 +01:00
|
|
|
if requestErr != nil {
|
|
|
|
err = requestErr
|
|
|
|
return
|
|
|
|
}
|
|
|
|
mux.Lock()
|
2018-02-17 18:39:10 +01:00
|
|
|
info = append(info, tempInfo...)
|
2018-02-19 05:25:36 +01:00
|
|
|
mux.Unlock()
|
|
|
|
}
|
|
|
|
|
|
|
|
for n := 0; n < len(names); n += config.RequestSplitN {
|
2018-02-21 09:41:25 +01:00
|
|
|
max := min(len(names), n+config.RequestSplitN)
|
2018-02-19 05:25:36 +01:00
|
|
|
wg.Add(1)
|
|
|
|
go makeRequest(n, max)
|
|
|
|
}
|
|
|
|
|
|
|
|
wg.Wait()
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return info, err
|
2018-02-17 18:39:10 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
for k, pkg := range info {
|
|
|
|
seen[pkg.Name] = k
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, name := range names {
|
|
|
|
i, ok := seen[name]
|
|
|
|
if !ok {
|
2018-02-19 06:02:05 +01:00
|
|
|
missing = append(missing, name)
|
2018-02-17 18:39:10 +01:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
pkg := info[i]
|
|
|
|
|
|
|
|
if pkg.Maintainer == "" {
|
2018-02-19 06:02:05 +01:00
|
|
|
orphans = append(orphans, name)
|
2018-02-17 18:39:10 +01:00
|
|
|
}
|
|
|
|
if pkg.OutOfDate != 0 {
|
2018-02-19 06:02:05 +01:00
|
|
|
outOfDate = append(outOfDate, name)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(missing) > 0 {
|
2018-03-02 05:54:38 +01:00
|
|
|
fmt.Print(bold(red(arrow + " Missing AUR Packages:")))
|
2018-02-19 06:02:05 +01:00
|
|
|
for _, name := range missing {
|
2018-03-02 05:54:38 +01:00
|
|
|
fmt.Print(" " + bold(magenta(name)))
|
2018-02-19 06:02:05 +01:00
|
|
|
}
|
|
|
|
fmt.Println()
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(orphans) > 0 {
|
2018-03-02 05:54:38 +01:00
|
|
|
fmt.Print(bold(red(arrow + " Orphaned AUR Packages:")))
|
2018-02-19 06:02:05 +01:00
|
|
|
for _, name := range orphans {
|
2018-03-02 05:54:38 +01:00
|
|
|
fmt.Print(" " + bold(magenta(name)))
|
2018-02-19 06:02:05 +01:00
|
|
|
}
|
|
|
|
fmt.Println()
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(outOfDate) > 0 {
|
2018-03-02 05:54:38 +01:00
|
|
|
fmt.Print(bold(red(arrow + " Out Of Date AUR Packages:")))
|
2018-02-19 06:02:05 +01:00
|
|
|
for _, name := range outOfDate {
|
2018-03-02 05:54:38 +01:00
|
|
|
fmt.Print(" " + bold(magenta(name)))
|
2018-02-17 18:39:10 +01:00
|
|
|
}
|
2018-02-19 06:02:05 +01:00
|
|
|
fmt.Println()
|
2018-02-17 18:39:10 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return info, nil
|
|
|
|
}
|