2017-08-02 17:24:03 +00:00
package main
2017-05-06 16:34:40 +00:00
import (
2018-04-04 20:26:07 +00:00
"bufio"
2018-02-20 14:24:48 +00:00
"bytes"
2017-05-06 16:34:40 +00:00
"encoding/json"
"fmt"
"os"
"os/exec"
2018-04-09 19:50:18 +00:00
"strings"
2017-05-06 16:34:40 +00:00
2018-09-04 14:47:22 +00:00
pacmanconf "github.com/Morganamilo/go-pacmanconf"
2017-05-06 16:34:40 +00:00
alpm "github.com/jguer/go-alpm"
)
2017-05-07 01:43:49 +00:00
// Verbosity settings for search
const (
2018-09-14 19:18:59 +00:00
numberMenu = iota
detailed
minimal
2018-11-29 20:42:06 +00:00
)
2017-05-07 01:43:49 +00:00
2018-11-29 20:42:06 +00:00
const (
2018-09-14 19:18:59 +00:00
// Describes Sorting method for numberdisplay
bottomUp = iota
topDown
2018-11-29 20:42:06 +00:00
)
2018-09-14 19:18:59 +00:00
2018-11-29 20:42:06 +00:00
const (
2018-09-14 19:18:59 +00:00
modeAUR targetMode = iota
modeRepo
modeAny
2017-05-07 01:43:49 +00:00
)
2018-05-31 15:23:15 +00:00
type targetMode int
2017-05-07 01:43:49 +00:00
// Configuration stores yay's config.
2017-05-06 16:34:40 +00:00
type Configuration struct {
2018-08-19 04:05:16 +00:00
AURURL string ` json:"aururl" `
2018-07-24 13:42:43 +00:00
BuildDir string ` json:"buildDir" `
Editor string ` json:"editor" `
EditorFlags string ` json:"editorflags" `
MakepkgBin string ` json:"makepkgbin" `
MakepkgConf string ` json:"makepkgconf" `
PacmanBin string ` json:"pacmanbin" `
PacmanConf string ` json:"pacmanconf" `
TarBin string ` json:"tarbin" `
ReDownload string ` json:"redownload" `
ReBuild string ` json:"rebuild" `
AnswerClean string ` json:"answerclean" `
AnswerDiff string ` json:"answerdiff" `
AnswerEdit string ` json:"answeredit" `
AnswerUpgrade string ` json:"answerupgrade" `
GitBin string ` json:"gitbin" `
GpgBin string ` json:"gpgbin" `
GpgFlags string ` json:"gpgflags" `
MFlags string ` json:"mflags" `
SortBy string ` json:"sortby" `
GitFlags string ` json:"gitflags" `
RemoveMake string ` json:"removemake" `
RequestSplitN int ` json:"requestsplitn" `
SearchMode int ` json:"-" `
SortMode int ` json:"sortmode" `
CompletionInterval int ` json:"completionrefreshtime" `
SudoLoop bool ` json:"sudoloop" `
TimeUpdate bool ` json:"timeupdate" `
NoConfirm bool ` json:"-" `
Devel bool ` json:"devel" `
CleanAfter bool ` json:"cleanAfter" `
GitClone bool ` json:"gitclone" `
Provides bool ` json:"provides" `
PGPFetch bool ` json:"pgpfetch" `
UpgradeMenu bool ` json:"upgrademenu" `
CleanMenu bool ` json:"cleanmenu" `
DiffMenu bool ` json:"diffmenu" `
EditMenu bool ` json:"editmenu" `
CombinedUpgrade bool ` json:"combinedupgrade" `
UseAsk bool ` json:"useask" `
2017-05-06 16:34:40 +00:00
}
2019-01-20 15:52:21 +00:00
var version = "9.1.0"
2017-05-06 16:34:40 +00:00
2018-03-05 23:53:03 +00:00
// configFileName holds the name of the config file.
const configFileName string = "config.json"
// vcsFileName holds the name of the vcs file.
const vcsFileName string = "vcs.json"
2018-03-21 05:34:54 +00:00
// useColor enables/disables colored printing
var useColor bool
2018-03-07 05:20:10 +00:00
// configHome handles config directory home
var configHome string
// cacheHome handles cache home
var cacheHome string
2018-03-05 22:15:34 +00:00
// savedInfo holds the current vcs info
var savedInfo vcsInfo
2017-07-06 07:30:48 +00:00
2017-10-18 02:12:16 +00:00
// configfile holds yay config file path.
var configFile string
// vcsfile holds yay vcs info file path.
var vcsFile string
2018-03-07 22:09:03 +00:00
// shouldSaveConfig holds whether or not the config should be saved
var shouldSaveConfig bool
2018-01-31 17:41:13 +00:00
2017-08-02 17:24:03 +00:00
// YayConf holds the current config values for yay.
2018-09-14 19:18:59 +00:00
var config * Configuration
2017-05-06 16:34:40 +00:00
2017-08-02 17:24:03 +00:00
// AlpmConf holds the current config values for pacman.
2018-09-04 14:47:22 +00:00
var pacmanConf * pacmanconf . Config
2017-05-06 16:34:40 +00:00
2017-08-02 17:24:03 +00:00
// AlpmHandle is the alpm handle used by yay.
2017-10-18 02:38:19 +00:00
var alpmHandle * alpm . Handle
2017-05-06 16:34:40 +00:00
2018-05-31 15:23:15 +00:00
// Mode is used to restrict yay to AUR or repo only modes
2018-09-14 19:18:59 +00:00
var mode = modeAny
2018-05-31 15:23:15 +00:00
2018-09-25 13:05:45 +00:00
var hideMenus = false
2017-05-07 01:43:49 +00:00
// SaveConfig writes yay config to file.
2017-08-02 17:24:03 +00:00
func ( config * Configuration ) saveConfig ( ) error {
marshalledinfo , _ := json . MarshalIndent ( config , "" , "\t" )
2017-11-20 23:41:55 +00:00
in , err := os . OpenFile ( configFile , os . O_RDWR | os . O_CREATE | os . O_TRUNC , 0644 )
2017-05-07 01:43:49 +00:00
if err != nil {
return err
}
defer in . Close ( )
_ , err = in . Write ( marshalledinfo )
if err != nil {
return err
}
err = in . Sync ( )
return err
}
2018-09-14 19:18:59 +00:00
func defaultSettings ( ) * Configuration {
config := & Configuration {
AURURL : "https://aur.archlinux.org" ,
BuildDir : "$HOME/.cache/yay" ,
CleanAfter : false ,
Editor : "" ,
EditorFlags : "" ,
Devel : false ,
MakepkgBin : "makepkg" ,
MakepkgConf : "" ,
NoConfirm : false ,
PacmanBin : "pacman" ,
PGPFetch : true ,
PacmanConf : "/etc/pacman.conf" ,
GpgFlags : "" ,
MFlags : "" ,
GitFlags : "" ,
SortMode : bottomUp ,
CompletionInterval : 7 ,
SortBy : "votes" ,
SudoLoop : false ,
TarBin : "bsdtar" ,
GitBin : "git" ,
GpgBin : "gpg" ,
TimeUpdate : false ,
RequestSplitN : 150 ,
ReDownload : "no" ,
ReBuild : "no" ,
AnswerClean : "" ,
AnswerDiff : "" ,
AnswerEdit : "" ,
AnswerUpgrade : "" ,
RemoveMake : "ask" ,
GitClone : true ,
Provides : true ,
UpgradeMenu : true ,
CleanMenu : true ,
DiffMenu : true ,
EditMenu : false ,
UseAsk : false ,
CombinedUpgrade : false ,
}
2018-09-04 22:19:31 +00:00
if os . Getenv ( "XDG_CACHE_HOME" ) != "" {
2018-09-14 19:18:59 +00:00
config . BuildDir = "$XDG_CACHE_HOME/yay"
2018-08-31 20:19:40 +00:00
}
2018-09-14 19:18:59 +00:00
return config
2017-05-06 16:34:40 +00:00
}
2018-08-31 20:19:40 +00:00
func ( config * Configuration ) expandEnv ( ) {
config . AURURL = os . ExpandEnv ( config . AURURL )
config . BuildDir = os . ExpandEnv ( config . BuildDir )
config . Editor = os . ExpandEnv ( config . Editor )
config . EditorFlags = os . ExpandEnv ( config . EditorFlags )
config . MakepkgBin = os . ExpandEnv ( config . MakepkgBin )
config . MakepkgConf = os . ExpandEnv ( config . MakepkgConf )
config . PacmanBin = os . ExpandEnv ( config . PacmanBin )
config . PacmanConf = os . ExpandEnv ( config . PacmanConf )
config . GpgFlags = os . ExpandEnv ( config . GpgFlags )
config . MFlags = os . ExpandEnv ( config . MFlags )
config . GitFlags = os . ExpandEnv ( config . GitFlags )
config . SortBy = os . ExpandEnv ( config . SortBy )
config . TarBin = os . ExpandEnv ( config . TarBin )
config . GitBin = os . ExpandEnv ( config . GitBin )
config . GpgBin = os . ExpandEnv ( config . GpgBin )
config . ReDownload = os . ExpandEnv ( config . ReDownload )
config . ReBuild = os . ExpandEnv ( config . ReBuild )
config . AnswerClean = os . ExpandEnv ( config . AnswerClean )
config . AnswerDiff = os . ExpandEnv ( config . AnswerDiff )
config . AnswerEdit = os . ExpandEnv ( config . AnswerEdit )
config . AnswerUpgrade = os . ExpandEnv ( config . AnswerUpgrade )
config . RemoveMake = os . ExpandEnv ( config . RemoveMake )
}
2017-05-06 16:34:40 +00:00
// Editor returns the preferred system editor.
2018-04-09 19:50:18 +00:00
func editor ( ) ( string , [ ] string ) {
2017-05-06 16:34:40 +00:00
switch {
2017-08-04 09:26:53 +00:00
case config . Editor != "" :
editor , err := exec . LookPath ( config . Editor )
2017-05-06 16:34:40 +00:00
if err != nil {
2018-11-14 12:14:41 +00:00
fmt . Fprintln ( os . Stderr , err )
2017-05-06 16:34:40 +00:00
} else {
2018-04-09 19:50:18 +00:00
return editor , strings . Fields ( config . EditorFlags )
2017-05-06 16:34:40 +00:00
}
fallthrough
case os . Getenv ( "EDITOR" ) != "" :
2018-04-09 19:50:18 +00:00
editorArgs := strings . Fields ( os . Getenv ( "EDITOR" ) )
editor , err := exec . LookPath ( editorArgs [ 0 ] )
2017-05-06 16:34:40 +00:00
if err != nil {
2018-11-14 12:14:41 +00:00
fmt . Fprintln ( os . Stderr , err )
2017-05-06 16:34:40 +00:00
} else {
2018-04-09 19:50:18 +00:00
return editor , editorArgs [ 1 : ]
2017-05-06 16:34:40 +00:00
}
fallthrough
case os . Getenv ( "VISUAL" ) != "" :
2018-04-09 19:50:18 +00:00
editorArgs := strings . Fields ( os . Getenv ( "VISUAL" ) )
editor , err := exec . LookPath ( editorArgs [ 0 ] )
2017-05-06 16:34:40 +00:00
if err != nil {
2018-11-14 12:14:41 +00:00
fmt . Fprintln ( os . Stderr , err )
2017-05-06 16:34:40 +00:00
} else {
2018-04-09 19:50:18 +00:00
return editor , editorArgs [ 1 : ]
2017-05-06 16:34:40 +00:00
}
fallthrough
default :
2018-11-14 12:14:41 +00:00
fmt . Fprintln ( os . Stderr )
fmt . Fprintln ( os . Stderr , bold ( red ( arrow ) ) , bold ( cyan ( "$EDITOR" ) ) , bold ( "is not set" ) )
fmt . Fprintln ( os . Stderr , bold ( red ( arrow ) ) + bold ( " Please add " ) + bold ( cyan ( "$EDITOR" ) ) + bold ( " or " ) + bold ( cyan ( "$VISUAL" ) ) + bold ( " to your environment variables." ) )
2018-04-09 20:02:44 +00:00
for {
2018-04-11 03:43:51 +00:00
fmt . Print ( green ( bold ( arrow + " Edit PKGBUILD with: " ) ) )
2018-04-09 20:02:44 +00:00
editorInput , err := getInput ( "" )
if err != nil {
2018-11-14 12:14:41 +00:00
fmt . Fprintln ( os . Stderr , err )
2018-04-09 20:02:44 +00:00
continue
}
editorArgs := strings . Fields ( editorInput )
editor , err := exec . LookPath ( editorArgs [ 0 ] )
if err != nil {
2018-11-14 12:14:41 +00:00
fmt . Fprintln ( os . Stderr , err )
2018-04-09 20:02:44 +00:00
continue
}
return editor , editorArgs [ 1 : ]
2017-05-06 16:34:40 +00:00
}
}
}
// ContinueTask prompts if user wants to continue task.
//If NoConfirm is set the action will continue without user input.
2018-07-21 23:46:55 +00:00
func continueTask ( s string , cont bool ) bool {
2017-08-04 09:26:53 +00:00
if config . NoConfirm {
2018-07-21 23:46:55 +00:00
return cont
2017-05-06 16:34:40 +00:00
}
2018-07-21 23:46:55 +00:00
var response string
2017-05-06 16:34:40 +00:00
var postFix string
2018-07-21 23:46:55 +00:00
yes := "yes"
no := "no"
y := string ( [ ] rune ( yes ) [ 0 ] )
n := string ( [ ] rune ( no ) [ 0 ] )
2017-05-06 16:34:40 +00:00
2018-07-21 23:46:55 +00:00
if cont {
postFix = fmt . Sprintf ( " [%s/%s] " , strings . ToUpper ( y ) , n )
2017-05-06 16:34:40 +00:00
} else {
2018-07-21 23:46:55 +00:00
postFix = fmt . Sprintf ( " [%s/%s] " , y , strings . ToUpper ( n ) )
2017-05-06 16:34:40 +00:00
}
2018-04-11 15:47:00 +00:00
fmt . Print ( bold ( green ( arrow ) + " " + s ) , bold ( postFix ) )
2017-05-06 16:34:40 +00:00
2018-07-21 23:46:55 +00:00
len , err := fmt . Scanln ( & response )
if err != nil || len == 0 {
return cont
2017-05-06 16:34:40 +00:00
}
2018-07-21 23:46:55 +00:00
response = strings . ToLower ( response )
return response == yes || response == y
2017-05-06 16:34:40 +00:00
}
2018-02-20 14:24:48 +00:00
2018-04-04 20:26:07 +00:00
func getInput ( defaultValue string ) ( string , error ) {
2018-04-04 21:22:33 +00:00
if defaultValue != "" || config . NoConfirm {
2018-04-04 20:26:07 +00:00
fmt . Println ( defaultValue )
return defaultValue , nil
}
reader := bufio . NewReader ( os . Stdin )
buf , overflow , err := reader . ReadLine ( )
if err != nil {
return "" , err
}
if overflow {
return "" , fmt . Errorf ( "Input too long" )
}
return string ( buf ) , nil
}
2018-02-20 14:24:48 +00:00
func ( config Configuration ) String ( ) string {
var buf bytes . Buffer
enc := json . NewEncoder ( & buf )
enc . SetIndent ( "" , "\t" )
if err := enc . Encode ( config ) ; err != nil {
2018-11-14 12:14:41 +00:00
fmt . Fprintln ( os . Stderr , err )
2018-02-20 14:24:48 +00:00
}
return buf . String ( )
}
2018-09-04 14:47:22 +00:00
func toUsage ( usages [ ] string ) alpm . Usage {
if len ( usages ) == 0 {
return alpm . UsageAll
}
2018-09-14 19:18:59 +00:00
var ret alpm . Usage
2018-09-04 14:47:22 +00:00
for _ , usage := range usages {
switch usage {
case "Sync" :
ret |= alpm . UsageSync
case "Search" :
ret |= alpm . UsageSearch
case "Install" :
ret |= alpm . UsageInstall
case "Upgrade" :
ret |= alpm . UsageUpgrade
case "All" :
ret |= alpm . UsageAll
}
}
return ret
}
func configureAlpm ( conf * pacmanconf . Config ) error {
var err error
// TODO: set SigLevel
2018-09-19 18:52:58 +00:00
//sigLevel := alpm.SigPackage | alpm.SigPackageOptional | alpm.SigDatabase | alpm.SigDatabaseOptional
//localFileSigLevel := alpm.SigUseDefault
//remoteFileSigLevel := alpm.SigUseDefault
2018-09-04 14:47:22 +00:00
for _ , repo := range pacmanConf . Repos {
// TODO: set SigLevel
2018-09-19 18:52:58 +00:00
db , err := alpmHandle . RegisterSyncDb ( repo . Name , 0 )
2018-09-04 14:47:22 +00:00
if err != nil {
return err
}
db . SetServers ( repo . Servers )
db . SetUsage ( toUsage ( repo . Usage ) )
2018-09-19 18:52:58 +00:00
2018-09-04 14:47:22 +00:00
}
if err = alpmHandle . SetCacheDirs ( pacmanConf . CacheDir ... ) ; err != nil {
return err
}
// add hook directories 1-by-1 to avoid overwriting the system directory
for _ , dir := range pacmanConf . HookDir {
if err = alpmHandle . AddHookDir ( dir ) ; err != nil {
return err
}
}
if err = alpmHandle . SetGPGDir ( pacmanConf . GPGDir ) ; err != nil {
return err
}
if err = alpmHandle . SetLogFile ( pacmanConf . LogFile ) ; err != nil {
return err
}
if err = alpmHandle . SetIgnorePkgs ( pacmanConf . IgnorePkg ... ) ; err != nil {
return err
}
if err = alpmHandle . SetIgnoreGroups ( pacmanConf . IgnoreGroup ... ) ; err != nil {
return err
}
if err = alpmHandle . SetArch ( pacmanConf . Architecture ) ; err != nil {
return err
}
if err = alpmHandle . SetNoUpgrades ( pacmanConf . NoUpgrade ... ) ; err != nil {
return err
}
if alpmHandle . SetNoExtracts ( pacmanConf . NoExtract ... ) ; err != nil {
return err
}
2018-09-19 18:52:58 +00:00
/ * if err = alpmHandle . SetDefaultSigLevel ( sigLevel ) ; err != nil {
2018-09-04 14:47:22 +00:00
return err
}
if err = alpmHandle . SetLocalFileSigLevel ( localFileSigLevel ) ; err != nil {
return err
}
if err = alpmHandle . SetRemoteFileSigLevel ( remoteFileSigLevel ) ; err != nil {
return err
2018-09-19 18:52:58 +00:00
} * /
2018-09-04 14:47:22 +00:00
if err = alpmHandle . SetDeltaRatio ( pacmanConf . UseDelta ) ; err != nil {
return err
}
if err = alpmHandle . SetUseSyslog ( pacmanConf . UseSyslog ) ; err != nil {
return err
}
if err = alpmHandle . SetCheckSpace ( pacmanConf . CheckSpace ) ; err != nil {
return err
}
return nil
}