mirror of
https://github.com/Jguer/yay
synced 2024-08-31 13:49:28 +00:00
96f499ff44
This reimplemens all operations yay previously supported: '-S' 'Syu' 'Si' ect. Currently the argument objects are not fully implemented with the code. Theres alot of funky conversion from argument object -> pkg, flags -> argument object This is to just get back to the functionally we had before (almost). I have not looked into it yet but alot of the time pacman flags get passed to makepkg. this cases an error for most commands now because the new system Passes all flags: `yay -Syu` -> flags = '-S' '-y' '-u' while the old system would have done: `yay -Syu` -> op = '-Suy', flags = '' So extra flags are no longer passed at all currently. This means: 'yay -S aic94xx-firmware --noconfirm -b /tmp/pacutilesu2q6hw/tmp-pacman -d' will no longer error and 'aic94xx-firmware' will be installed but the database path change will not apply and the dep checking will not be skipped.
544 lines
12 KiB
Go
544 lines
12 KiB
Go
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
func usage() {
|
|
fmt.Println(`Usage:
|
|
yay <operation> [...]
|
|
yay <package(s)>
|
|
|
|
operations:
|
|
yay {-h --help}
|
|
yay {-V --version}
|
|
yay {-D --database} <options> <package(s)>
|
|
yay {-F --files} [options] [package(s)]
|
|
yay {-Q --query} [options] [package(s)]
|
|
yay {-R --remove} [options] <package(s)>
|
|
yay {-S --sync} [options] [package(s)]
|
|
yay {-T --deptest} [options] [package(s)]
|
|
yay {-U --upgrade} [options] <file(s)>
|
|
|
|
New operations:
|
|
yay {-Y --yay} [options] [package(s)]
|
|
yay {-G --getpkgbuild} [package(s)]
|
|
|
|
Permanent configuration options:
|
|
--topdown Shows repository's packages first and then aur's
|
|
--bottomup Shows aur's packages first and then repository's
|
|
--devel Check -git/-svn/-hg development version
|
|
--nodevel Disable development version checking
|
|
--afterclean Clean package sources after successful build
|
|
--noafterclean Disable package sources cleaning after successful build
|
|
--timeupdate Check package's modification date and version
|
|
--notimeupdate Check only package version change
|
|
|
|
Yay specific options:
|
|
--printconfig Prints current yay configuration
|
|
--stats Displays system information
|
|
--cleandeps Remove unneeded dependencies
|
|
--gendb Generates development package DB used for updating.
|
|
|
|
If no operation is provided -Y will be assumed
|
|
`)
|
|
}
|
|
|
|
func init() {
|
|
var configHome string // configHome handles config directory home
|
|
var cacheHome string // cacheHome handles cache home
|
|
var err error
|
|
|
|
if 0 == os.Geteuid() {
|
|
fmt.Println("Please avoid running yay as root/sudo.")
|
|
}
|
|
|
|
if configHome = os.Getenv("XDG_CONFIG_HOME"); configHome != "" {
|
|
if info, err := os.Stat(configHome); err == nil && info.IsDir() {
|
|
configHome = configHome + "/yay"
|
|
} else {
|
|
configHome = os.Getenv("HOME") + "/.config/yay"
|
|
}
|
|
} else {
|
|
configHome = os.Getenv("HOME") + "/.config/yay"
|
|
}
|
|
|
|
if cacheHome = os.Getenv("XDG_CACHE_HOME"); cacheHome != "" {
|
|
if info, err := os.Stat(cacheHome); err == nil && info.IsDir() {
|
|
cacheHome = cacheHome + "/yay"
|
|
} else {
|
|
cacheHome = os.Getenv("HOME") + "/.cache/yay"
|
|
}
|
|
} else {
|
|
cacheHome = os.Getenv("HOME") + "/.cache/yay"
|
|
}
|
|
|
|
configFile = configHome + "/config.json"
|
|
vcsFile = configHome + "/yay_vcs.json"
|
|
completionFile = cacheHome + "/aur_"
|
|
|
|
////////////////
|
|
// yay config //
|
|
////////////////
|
|
defaultSettings(&config)
|
|
|
|
if _, err = os.Stat(configFile); os.IsNotExist(err) {
|
|
err = os.MkdirAll(filepath.Dir(configFile), 0755)
|
|
if err != nil {
|
|
fmt.Println("Unable to create config directory:", filepath.Dir(configFile), err)
|
|
os.Exit(2)
|
|
}
|
|
// Save the default config if nothing is found
|
|
config.saveConfig()
|
|
} else {
|
|
cfile, errf := os.OpenFile(configFile, os.O_RDWR|os.O_CREATE, 0644)
|
|
if errf != nil {
|
|
fmt.Println("Error reading config:", err)
|
|
} else {
|
|
defer cfile.Close()
|
|
decoder := json.NewDecoder(cfile)
|
|
err = decoder.Decode(&config)
|
|
if err != nil {
|
|
fmt.Println("Loading default Settings.\nError reading config:", err)
|
|
defaultSettings(&config)
|
|
}
|
|
}
|
|
}
|
|
|
|
/////////////////
|
|
// vcs config //
|
|
////////////////
|
|
updated = false
|
|
|
|
vfile, err := os.Open(vcsFile)
|
|
if err == nil {
|
|
defer vfile.Close()
|
|
decoder := json.NewDecoder(vfile)
|
|
_ = decoder.Decode(&savedInfo)
|
|
}
|
|
|
|
/////////////////
|
|
// alpm config //
|
|
/////////////////
|
|
alpmConf, err = readAlpmConfig(config.PacmanConf)
|
|
if err != nil {
|
|
fmt.Println("Unable to read Pacman conf", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
alpmHandle, err = alpmConf.CreateHandle()
|
|
if err != nil {
|
|
fmt.Println("Unable to CreateHandle", err)
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
|
|
func main() {
|
|
status := run()
|
|
|
|
err := alpmHandle.Release()
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
status = 1
|
|
}
|
|
|
|
os.Exit(status)
|
|
}
|
|
|
|
func run() (status int) {
|
|
var err error
|
|
var changedConfig bool
|
|
|
|
parser := makeArguments();
|
|
err = parser.parseCommandLine();
|
|
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
status = 1
|
|
return
|
|
}
|
|
|
|
if parser.existsArg("-") {
|
|
err = parser.parseStdin();
|
|
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
status = 1
|
|
return
|
|
}
|
|
}
|
|
|
|
changedConfig, err = handleCmd(parser)
|
|
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
status = 1
|
|
//try continue onward
|
|
}
|
|
|
|
if updated {
|
|
err = saveVCSInfo()
|
|
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
status = 1
|
|
}
|
|
}
|
|
|
|
if changedConfig {
|
|
err = config.saveConfig()
|
|
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
status = 1
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
func handleCmd(parser *arguments) (changedConfig bool, err error) {
|
|
var _changedConfig bool
|
|
|
|
for option, _ := range parser.options {
|
|
_changedConfig, err = handleConfig(option)
|
|
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
if _changedConfig {
|
|
changedConfig = true
|
|
}
|
|
}
|
|
|
|
switch parser.op {
|
|
case "V", "version":
|
|
handleVersion(parser)
|
|
case "D", "database":
|
|
passToPacman(parser)
|
|
case "F", "files":
|
|
passToPacman(parser)
|
|
case "Q", "query":
|
|
passToPacman(parser)
|
|
case "R", "remove":
|
|
handleRemove(parser)
|
|
case "S", "sync":
|
|
err = handleSync(parser)
|
|
case "T", "deptest":
|
|
passToPacman(parser)
|
|
case "U", "upgrade":
|
|
passToPacman(parser)
|
|
case "G", "getpkgbuild":
|
|
err = handleGetpkgbuild(parser)
|
|
case "Y", "--yay":
|
|
err = handleYay(parser)
|
|
default:
|
|
//this means we allowed an op but not implement it
|
|
//if this happens it an error in the code and not the usage
|
|
err = fmt.Errorf("unhandled operation")
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
//this function should only set config options
|
|
//but currently still uses the switch left over from old code
|
|
//eventuall this should be refactored out futher
|
|
//my current plan is to have yay specific operations in its own operator
|
|
//e.g. yay -Y --gendb
|
|
//e.g yay -Yg
|
|
func handleConfig(option string) (changedConfig bool, err error) {
|
|
switch option {
|
|
case "afterclean":
|
|
config.CleanAfter = true
|
|
case "noafterclean":
|
|
config.CleanAfter = false
|
|
// case "printconfig":
|
|
// fmt.Printf("%#v", config)
|
|
// os.Exit(0)
|
|
// case "gendb":
|
|
// err = createDevelDB()
|
|
// if err != nil {
|
|
// fmt.Println(err)
|
|
// }
|
|
// err = saveVCSInfo()
|
|
// if err != nil {
|
|
// fmt.Println(err)
|
|
// }
|
|
// os.Exit(0)
|
|
case "devel":
|
|
config.Devel = true
|
|
case "nodevel":
|
|
config.Devel = false
|
|
case "timeupdate":
|
|
config.TimeUpdate = true
|
|
case "notimeupdate":
|
|
config.TimeUpdate = false
|
|
case "topdown":
|
|
config.SortMode = TopDown
|
|
// case "complete":
|
|
// config.Shell = "sh"
|
|
// complete()
|
|
// os.Exit(0)
|
|
// case "fcomplete":
|
|
// config.Shell = fishShell
|
|
// complete()
|
|
// os.Exit(0)
|
|
// case "help":
|
|
// usage()
|
|
// os.Exit(0)
|
|
// case "version":
|
|
// fmt.Printf("yay v%s\n", version)
|
|
// os.Exit(0)
|
|
case "noconfirm":
|
|
config.NoConfirm = true
|
|
default:
|
|
return
|
|
}
|
|
|
|
changedConfig = true
|
|
return
|
|
}
|
|
|
|
func handleVersion(parser *arguments) {
|
|
fmt.Printf("yay v%s\n", version)
|
|
}
|
|
|
|
func handleYay(parser *arguments) (err error) {
|
|
//_, options, targets := parser.formatArgs()
|
|
if parser.existsArg("h", "help") {
|
|
usage()
|
|
} else if parser.existsArg("printconfig") {
|
|
fmt.Printf("%#v", config)
|
|
} else if parser.existsArg("gendb") {
|
|
err = createDevelDB()
|
|
if err != nil {
|
|
return
|
|
}
|
|
err = saveVCSInfo()
|
|
if err != nil {
|
|
return
|
|
}
|
|
} else if parser.existsArg("complete") {
|
|
config.Shell = "sh"
|
|
complete()
|
|
} else if parser.existsArg("fcomplete") {
|
|
config.Shell = "fish"
|
|
complete()
|
|
} else if parser.existsArg("stats") {
|
|
err = localStatistics()
|
|
} else if parser.existsArg("cleandeps") {
|
|
err = cleanDependencies()
|
|
} else {
|
|
err = handleYogurt(parser)
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func handleGetpkgbuild(parser *arguments) (err error) {
|
|
for pkg := range parser.targets {
|
|
err = getPkgbuild(pkg)
|
|
if err != nil {
|
|
//we print the error instead of returning it
|
|
//seems as we can handle multiple errors without stoping
|
|
//theres no easy way arround this right now
|
|
fmt.Println(pkg + ":", err)
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func handleYogurt(parser *arguments) (err error) {
|
|
options := parser.formatArgs()
|
|
targets := parser.formatTargets()
|
|
|
|
config.SearchMode = NumberMenu
|
|
err = numberMenu(targets, options)
|
|
|
|
return
|
|
}
|
|
|
|
func handleSync(parser *arguments) (err error) {
|
|
targets := parser.formatTargets()
|
|
options := parser.formatArgs()
|
|
|
|
if parser.existsArg("y", "refresh") {
|
|
arguments := makeArguments()
|
|
arguments.addArg("S", "y")
|
|
err = passToPacman(arguments)
|
|
if err != nil {
|
|
return
|
|
}
|
|
}
|
|
|
|
if parser.existsArg("s", "search") {
|
|
if parser.existsArg("q", "quiet") {
|
|
config.SearchMode = Minimal
|
|
} else {
|
|
config.SearchMode = Detailed
|
|
}
|
|
|
|
err = syncSearch(targets)
|
|
} else if parser.existsArg("c", "clean") {
|
|
err = passToPacman(parser)
|
|
} else if parser.existsArg("u", "sysupgrade") {
|
|
err = upgradePkgs(make([]string,0))
|
|
} else if parser.existsArg("i", "info") {
|
|
err = syncInfo(targets, options)
|
|
} else if len(parser.targets) > 0 {
|
|
err = install(parser)
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func handleRemove(parser *arguments) (err error){
|
|
removeVCSPackage(parser.formatTargets())
|
|
err = passToPacman(parser)
|
|
return
|
|
}
|
|
|
|
// NumberMenu presents a CLI for selecting packages to install.
|
|
func numberMenu(pkgS []string, flags []string) (err error) {
|
|
//func numberMenu(parser *arguments) (err error) {
|
|
var num int
|
|
|
|
aq, err := narrowSearch(pkgS, true)
|
|
if err != nil {
|
|
fmt.Println("Error during AUR search:", err)
|
|
}
|
|
numaq := len(aq)
|
|
|
|
pq, numpq, err := queryRepo(pkgS)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
if numpq == 0 && numaq == 0 {
|
|
return fmt.Errorf("no packages match search")
|
|
}
|
|
|
|
if config.SortMode == BottomUp {
|
|
aq.printSearch(numpq)
|
|
pq.printSearch()
|
|
} else {
|
|
pq.printSearch()
|
|
aq.printSearch(numpq)
|
|
}
|
|
|
|
fmt.Printf("\x1b[32m%s\x1b[0m\nNumbers: ", "Type numbers to install. Separate each number with a space.")
|
|
reader := bufio.NewReader(os.Stdin)
|
|
numberBuf, overflow, err := reader.ReadLine()
|
|
if err != nil || overflow {
|
|
fmt.Println(err)
|
|
return
|
|
}
|
|
|
|
numberString := string(numberBuf)
|
|
var aurI []string
|
|
var repoI []string
|
|
result := strings.Fields(numberString)
|
|
for _, numS := range result {
|
|
num, err = strconv.Atoi(numS)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
// Install package
|
|
if num > numaq+numpq-1 || num < 0 {
|
|
continue
|
|
} else if num > numpq-1 {
|
|
if config.SortMode == BottomUp {
|
|
aurI = append(aurI, aq[numaq+numpq-num-1].Name)
|
|
} else {
|
|
aurI = append(aurI, aq[num-numpq].Name)
|
|
}
|
|
} else {
|
|
if config.SortMode == BottomUp {
|
|
repoI = append(repoI, pq[numpq-num-1].Name())
|
|
} else {
|
|
repoI = append(repoI, pq[num].Name())
|
|
}
|
|
}
|
|
}
|
|
|
|
if len(repoI) != 0 {
|
|
arguments := makeArguments()
|
|
arguments.addArg("S")
|
|
arguments.addTarget(repoI...)
|
|
err = passToPacman(arguments)
|
|
}
|
|
|
|
if len(aurI) != 0 {
|
|
err = aurInstall(aurI, make([]string,0))
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
// Complete provides completion info for shells
|
|
func complete() error {
|
|
path := completionFile + config.Shell + ".cache"
|
|
|
|
if info, err := os.Stat(path); os.IsNotExist(err) || time.Since(info.ModTime()).Hours() > 48 {
|
|
os.MkdirAll(filepath.Dir(completionFile), 0755)
|
|
out, errf := os.Create(path)
|
|
if errf != nil {
|
|
return errf
|
|
}
|
|
|
|
if createAURList(out) != nil {
|
|
defer os.Remove(path)
|
|
}
|
|
erra := createRepoList(out)
|
|
|
|
out.Close()
|
|
return erra
|
|
}
|
|
|
|
in, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0644)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer in.Close()
|
|
|
|
_, err = io.Copy(os.Stdout, in)
|
|
return err
|
|
}
|
|
|
|
// PassToPacman outsorces execution to pacman binary without modifications.
|
|
func passToPacman(parser *arguments) error {
|
|
var cmd *exec.Cmd
|
|
args := make([]string, 0)
|
|
|
|
if parser.needRoot() {
|
|
args = append(args, "sudo")
|
|
}
|
|
|
|
args = append(args, "pacman")
|
|
args = append(args, parser.formatArgs()...)
|
|
args = append(args, parser.formatTargets()...)
|
|
|
|
cmd = exec.Command(args[0], args[1:]...)
|
|
|
|
|
|
cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
|
|
err := cmd.Run()
|
|
return err
|
|
}
|