Bladeren bron

fix(news): package news and begin settings

jguer 4 jaren geleden
bovenliggende
commit
cb8a988701
17 gewijzigde bestanden met toevoegingen van 419 en 383 verwijderingen
  1. 2 1
      clean.go
  2. 12 8
      cmd.go
  3. 5 114
      config.go
  4. 2 5
      config_test.go
  5. 3 2
      install.go
  6. 1 0
      keys_test.go
  7. 5 5
      main.go
  8. 3 67
      parser.go
  9. 182 0
      pkg/news/news.go
  10. 44 0
      pkg/query/filter.go
  11. 116 0
      pkg/settings/config.go
  12. 15 10
      pkg/text/color.go
  13. 12 12
      pkg/text/print.go
  14. 8 93
      print.go
  15. 5 64
      query.go
  16. 2 1
      upgrade.go
  17. 2 1
      vcs.go

+ 2 - 1
clean.go

@@ -8,6 +8,7 @@ import (
 
 	"github.com/leonelquinteros/gotext"
 
+	"github.com/Jguer/yay/v10/pkg/query"
 	"github.com/Jguer/yay/v10/pkg/stringset"
 	"github.com/Jguer/yay/v10/pkg/text"
 )
@@ -116,7 +117,7 @@ func cleanAUR(keepInstalled, keepCurrent, removeAll bool) error {
 	installedBases := make(stringset.StringSet)
 	inAURBases := make(stringset.StringSet)
 
-	_, remotePackages, _, _, err := filterPackages()
+	_, remotePackages, _, _, err := query.FilterPackages(alpmHandle)
 	if err != nil {
 		return err
 	}

+ 12 - 8
cmd.go

@@ -11,6 +11,8 @@ import (
 
 	"github.com/Jguer/yay/v10/pkg/completion"
 	"github.com/Jguer/yay/v10/pkg/intrange"
+	"github.com/Jguer/yay/v10/pkg/news"
+	"github.com/Jguer/yay/v10/pkg/settings"
 	"github.com/Jguer/yay/v10/pkg/text"
 )
 
@@ -198,7 +200,7 @@ func handlePrint() (err error) {
 	switch {
 	case cmdArgs.existsArg("d", "defaultconfig"):
 		tmpConfig := defaultSettings()
-		tmpConfig.expandEnv()
+		tmpConfig.ExpandEnv()
 		fmt.Printf("%v", tmpConfig)
 	case cmdArgs.existsArg("g", "currentconfig"):
 		fmt.Printf("%v", config)
@@ -207,7 +209,9 @@ func handlePrint() (err error) {
 	case cmdArgs.existsArg("u", "upgrades"):
 		err = printUpdateList(cmdArgs)
 	case cmdArgs.existsArg("w", "news"):
-		err = printNewsFeed()
+		_, double, _ := cmdArgs.getArg("news", "w")
+		quiet := cmdArgs.existsArg("q", "quiet")
+		err = news.PrintNewsFeed(alpmHandle, config.SortMode, double, quiet)
 	case cmdArgs.existsDouble("c", "complete"):
 		err = completion.Show(alpmHandle, config.AURURL, cacheHome, config.CompletionInterval, true)
 	case cmdArgs.existsArg("c", "complete"):
@@ -320,14 +324,14 @@ func displayNumberMenu(pkgS []string) error {
 	}
 
 	switch config.SortMode {
-	case topDown:
+	case settings.TopDown:
 		if mode == modeRepo || mode == modeAny {
 			pq.printSearch()
 		}
 		if mode == modeAUR || mode == modeAny {
 			aq.printSearch(lenpq + 1)
 		}
-	case bottomUp:
+	case settings.BottomUp:
 		if mode == modeAUR || mode == modeAny {
 			aq.printSearch(lenpq + 1)
 		}
@@ -364,9 +368,9 @@ func displayNumberMenu(pkgS []string) error {
 	for i, pkg := range pq {
 		var target int
 		switch config.SortMode {
-		case topDown:
+		case settings.TopDown:
 			target = i + 1
-		case bottomUp:
+		case settings.BottomUp:
 			target = len(pq) - i
 		default:
 			return fmt.Errorf(gotext.Get("invalid sort mode. Fix with yay -Y --bottomup --save"))
@@ -381,9 +385,9 @@ func displayNumberMenu(pkgS []string) error {
 		var target int
 
 		switch config.SortMode {
-		case topDown:
+		case settings.TopDown:
 			target = i + 1 + len(pq)
-		case bottomUp:
+		case settings.BottomUp:
 			target = len(aq) - i + len(pq)
 		default:
 			return fmt.Errorf(gotext.Get("invalid sort mode. Fix with yay -Y --bottomup --save"))

+ 5 - 114
config.go

@@ -2,8 +2,6 @@ package main
 
 import (
 	"bufio"
-	"bytes"
-	"encoding/json"
 	"fmt"
 	"os"
 	"os/exec"
@@ -13,6 +11,7 @@ import (
 	pacmanconf "github.com/Morganamilo/go-pacmanconf"
 	"github.com/leonelquinteros/gotext"
 
+	"github.com/Jguer/yay/v10/pkg/settings"
 	"github.com/Jguer/yay/v10/pkg/text"
 )
 
@@ -24,12 +23,6 @@ const (
 )
 
 const (
-	// Describes Sorting method for numberdisplay
-	bottomUp = iota
-	topDown
-)
-
-const (
 	modeAUR targetMode = iota
 	modeRepo
 	modeAny
@@ -37,53 +30,6 @@ const (
 
 type targetMode int
 
-// Configuration stores yay's config.
-type Configuration struct {
-	AURURL             string `json:"aururl"`
-	BuildDir           string `json:"buildDir"`
-	ABSDir             string `json:"absdir"`
-	Editor             string `json:"editor"`
-	EditorFlags        string `json:"editorflags"`
-	MakepkgBin         string `json:"makepkgbin"`
-	MakepkgConf        string `json:"makepkgconf"`
-	PacmanBin          string `json:"pacmanbin"`
-	PacmanConf         string `json:"pacmanconf"`
-	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"`
-	SearchBy           string `json:"searchby"`
-	GitFlags           string `json:"gitflags"`
-	RemoveMake         string `json:"removemake"`
-	SudoBin            string `json:"sudobin"`
-	SudoFlags          string `json:"sudoflags"`
-	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"`
-	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"`
-	BatchInstall       bool   `json:"batchinstall"`
-}
-
 var yayVersion = "10.0.0"
 
 var localePath = "/usr/share/locale"
@@ -113,7 +59,7 @@ var vcsFile string
 var shouldSaveConfig bool
 
 // YayConf holds the current config values for yay.
-var config *Configuration
+var config *settings.Configuration
 
 // AlpmConf holds the current config values for pacman.
 var pacmanConf *pacmanconf.Config
@@ -126,25 +72,8 @@ var mode = modeAny
 
 var hideMenus = false
 
-// SaveConfig writes yay config to file.
-func (config *Configuration) saveConfig() error {
-	marshalledinfo, err := json.MarshalIndent(config, "", "\t")
-	if err != nil {
-		return err
-	}
-	in, err := os.OpenFile(configFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
-	if err != nil {
-		return err
-	}
-	defer in.Close()
-	if _, err = in.Write(marshalledinfo); err != nil {
-		return err
-	}
-	return in.Sync()
-}
-
-func defaultSettings() *Configuration {
-	newConfig := &Configuration{
+func defaultSettings() *settings.Configuration {
+	newConfig := &settings.Configuration{
 		AURURL:             "https://aur.archlinux.org",
 		BuildDir:           "$HOME/.cache/yay",
 		ABSDir:             "$HOME/.cache/yay/abs",
@@ -161,7 +90,7 @@ func defaultSettings() *Configuration {
 		GpgFlags:           "",
 		MFlags:             "",
 		GitFlags:           "",
-		SortMode:           bottomUp,
+		SortMode:           settings.BottomUp,
 		CompletionInterval: 7,
 		SortBy:             "votes",
 		SearchBy:           "name-desc",
@@ -196,34 +125,6 @@ func defaultSettings() *Configuration {
 	return newConfig
 }
 
-func (config *Configuration) expandEnv() {
-	config.AURURL = os.ExpandEnv(config.AURURL)
-	config.ABSDir = os.ExpandEnv(config.ABSDir)
-	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.SearchBy = os.ExpandEnv(config.SearchBy)
-	config.GitBin = os.ExpandEnv(config.GitBin)
-	config.GpgBin = os.ExpandEnv(config.GpgBin)
-	config.SudoBin = os.ExpandEnv(config.SudoBin)
-	config.SudoFlags = os.ExpandEnv(config.SudoFlags)
-	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)
-}
-
 // Editor returns the preferred system editor.
 func editor() (editor string, args []string) {
 	switch {
@@ -334,16 +235,6 @@ func getInput(defaultValue string) (string, error) {
 	return string(buf), nil
 }
 
-func (config *Configuration) String() string {
-	var buf bytes.Buffer
-	enc := json.NewEncoder(&buf)
-	enc.SetIndent("", "\t")
-	if err := enc.Encode(config); err != nil {
-		fmt.Fprintln(os.Stderr, err)
-	}
-	return buf.String()
-}
-
 func toUsage(usages []string) alpm.Usage {
 	if len(usages) == 0 {
 		return alpm.UsageAll

+ 2 - 5
config_test.go

@@ -13,11 +13,8 @@ func expect(t *testing.T, field string, a interface{}, b interface{}, err error)
 	}
 }
 
-func TestConfig(t *testing.T) {
-	config = &Configuration{}
-	config.PacmanConf = "./testdata/pacman.conf"
-
-	err := initAlpm()
+func TestAlpmConfig(t *testing.T) {
+	err := initAlpm("testdata/pacman.conf")
 	if err != nil {
 		t.Fatal(err)
 	}

+ 3 - 2
install.go

@@ -17,6 +17,7 @@ import (
 	"github.com/Jguer/yay/v10/pkg/completion"
 	"github.com/Jguer/yay/v10/pkg/intrange"
 	"github.com/Jguer/yay/v10/pkg/multierror"
+	"github.com/Jguer/yay/v10/pkg/query"
 	"github.com/Jguer/yay/v10/pkg/stringset"
 	"github.com/Jguer/yay/v10/pkg/text"
 )
@@ -88,7 +89,7 @@ func install(parser *arguments) (err error) {
 		return err
 	}
 
-	_, _, localNames, remoteNames, err := filterPackages()
+	_, _, localNames, remoteNames, err := query.FilterPackages(alpmHandle)
 	if err != nil {
 		return err
 	}
@@ -951,7 +952,7 @@ func buildInstallPkgbuilds(
 	config.NoConfirm = true
 
 	//remotenames: names of all non repo packages on the system
-	_, _, localNames, remoteNames, err := filterPackages()
+	_, _, localNames, remoteNames, err := query.FilterPackages(alpmHandle)
 	if err != nil {
 		return err
 	}

+ 1 - 0
keys_test.go

@@ -72,6 +72,7 @@ func TestImportKeys(t *testing.T) {
 	}
 	defer os.RemoveAll(keyringDir)
 
+	config = defaultSettings()
 	config.GpgBin = "gpg"
 	config.GpgFlags = fmt.Sprintf("--homedir %s --keyserver 127.0.0.1", keyringDir)
 

+ 5 - 5
main.go

@@ -117,7 +117,7 @@ func initBuildDir() error {
 	return nil
 }
 
-func initAlpm() error {
+func initAlpm(pacmanConfigPath string) error {
 	var err error
 	var stderr string
 
@@ -126,7 +126,7 @@ func initAlpm() error {
 		root = value
 	}
 
-	pacmanConf, stderr, err = pacmanconf.PacmanConf("--config", config.PacmanConf, "--root", root)
+	pacmanConf, stderr, err = pacmanconf.PacmanConf("--config", pacmanConfigPath, "--root", root)
 	if err != nil {
 		return fmt.Errorf("%s", stderr)
 	}
@@ -231,15 +231,15 @@ func main() {
 	exitOnError(initConfig())
 	exitOnError(cmdArgs.parseCommandLine())
 	if shouldSaveConfig {
-		err := config.saveConfig()
+		err := config.SaveConfig(configFile)
 		if err != nil {
 			fmt.Fprintln(os.Stderr, err)
 		}
 	}
-	config.expandEnv()
+	config.ExpandEnv()
 	exitOnError(initBuildDir())
 	exitOnError(initVCS())
-	exitOnError(initAlpm())
+	exitOnError(initAlpm(config.PacmanConf))
 	exitOnError(handleCmd())
 	os.Exit(cleanup())
 }

+ 3 - 67
parser.go

@@ -2,8 +2,6 @@ package main
 
 import (
 	"bufio"
-	"bytes"
-	"html"
 	"os"
 	"strconv"
 	"strings"
@@ -12,6 +10,7 @@ import (
 	rpc "github.com/mikkeloscar/aur"
 	"github.com/pkg/errors"
 
+	"github.com/Jguer/yay/v10/pkg/settings"
 	"github.com/Jguer/yay/v10/pkg/stringset"
 )
 
@@ -474,9 +473,9 @@ func handleConfig(option, value string) bool {
 	case "notimeupdate":
 		config.TimeUpdate = false
 	case "topdown":
-		config.SortMode = topDown
+		config.SortMode = settings.TopDown
 	case "bottomup":
-		config.SortMode = bottomUp
+		config.SortMode = settings.BottomUp
 	case "completioninterval":
 		n, err := strconv.Atoi(value)
 		if err == nil {
@@ -849,66 +848,3 @@ func (parser *arguments) extractYayOptions() {
 	rpc.AURURL = strings.TrimRight(config.AURURL, "/") + "/rpc.php?"
 	config.AURURL = strings.TrimRight(config.AURURL, "/")
 }
-
-// Crude html parsing, good enough for the arch news
-// This is only displayed in the terminal so there should be no security
-// concerns
-func parseNews(str string) string {
-	var buffer bytes.Buffer
-	var tagBuffer bytes.Buffer
-	var escapeBuffer bytes.Buffer
-	inTag := false
-	inEscape := false
-
-	for _, char := range str {
-		if inTag {
-			if char == '>' {
-				inTag = false
-				switch tagBuffer.String() {
-				case "code":
-					buffer.WriteString(cyanCode)
-				case "/code":
-					buffer.WriteString(resetCode)
-				case "/p":
-					buffer.WriteRune('\n')
-				}
-
-				continue
-			}
-
-			tagBuffer.WriteRune(char)
-			continue
-		}
-
-		if inEscape {
-			if char == ';' {
-				inEscape = false
-				escapeBuffer.WriteRune(char)
-				s := html.UnescapeString(escapeBuffer.String())
-				buffer.WriteString(s)
-				continue
-			}
-
-			escapeBuffer.WriteRune(char)
-			continue
-		}
-
-		if char == '<' {
-			inTag = true
-			tagBuffer.Reset()
-			continue
-		}
-
-		if char == '&' {
-			inEscape = true
-			escapeBuffer.Reset()
-			escapeBuffer.WriteRune(char)
-			continue
-		}
-
-		buffer.WriteRune(char)
-	}
-
-	buffer.WriteString(resetCode)
-	return buffer.String()
-}

+ 182 - 0
pkg/news/news.go

@@ -0,0 +1,182 @@
+package news
+
+import (
+	"bytes"
+	"encoding/xml"
+	"fmt"
+	"html"
+	"io/ioutil"
+	"net/http"
+	"os"
+	"strings"
+	"time"
+
+	"github.com/Jguer/go-alpm"
+
+	"github.com/Jguer/yay/v10/pkg/query"
+	"github.com/Jguer/yay/v10/pkg/settings"
+	"github.com/Jguer/yay/v10/pkg/text"
+)
+
+type item struct {
+	Title       string `xml:"title"`
+	Link        string `xml:"link"`
+	Description string `xml:"description"`
+	PubDate     string `xml:"pubDate"`
+	Creator     string `xml:"dc:creator"`
+}
+
+func (item *item) print(buildTime time.Time, double, quiet bool) {
+	var fd string
+	date, err := time.Parse(time.RFC1123Z, item.PubDate)
+
+	if err != nil {
+		fmt.Fprintln(os.Stderr, err)
+	} else {
+		fd = text.FormatTime(int(date.Unix()))
+		if !double && !buildTime.IsZero() {
+			if buildTime.After(date) {
+				return
+			}
+		}
+	}
+
+	fmt.Println(text.Bold(text.Magenta(fd)), text.Bold(strings.TrimSpace(item.Title)))
+
+	if !quiet {
+		desc := strings.TrimSpace(parseNews(item.Description))
+		fmt.Println(desc)
+	}
+}
+
+type channel struct {
+	Title         string `xml:"title"`
+	Link          string `xml:"link"`
+	Description   string `xml:"description"`
+	Language      string `xml:"language"`
+	Lastbuilddate string `xml:"lastbuilddate"`
+	Items         []item `xml:"item"`
+}
+
+type rss struct {
+	Channel channel `xml:"channel"`
+}
+
+func PrintNewsFeed(alpmHandle *alpm.Handle, sortMode int, double, quiet bool) error {
+	resp, err := http.Get("https://archlinux.org/feeds/news")
+	if err != nil {
+		return err
+	}
+
+	defer resp.Body.Close()
+	body, err := ioutil.ReadAll(resp.Body)
+	if err != nil {
+		return err
+	}
+
+	rssGot := rss{}
+
+	d := xml.NewDecoder(bytes.NewReader(body))
+	err = d.Decode(&rssGot)
+	if err != nil {
+		return err
+	}
+
+	buildTime, err := lastBuildTime(alpmHandle)
+	if err != nil {
+		return err
+	}
+
+	if sortMode == settings.BottomUp {
+		for i := len(rssGot.Channel.Items) - 1; i >= 0; i-- {
+			rssGot.Channel.Items[i].print(buildTime, double, quiet)
+		}
+	} else {
+		for i := 0; i < len(rssGot.Channel.Items); i++ {
+			rssGot.Channel.Items[i].print(buildTime, double, quiet)
+		}
+	}
+
+	return nil
+}
+
+func lastBuildTime(alpmHandle *alpm.Handle) (time.Time, error) {
+	var lastTime time.Time
+
+	pkgs, _, _, _, err := query.FilterPackages(alpmHandle)
+	if err != nil {
+		return lastTime, err
+	}
+
+	for _, pkg := range pkgs {
+		thisTime := pkg.BuildDate()
+		if thisTime.After(lastTime) {
+			lastTime = thisTime
+		}
+	}
+
+	return lastTime, nil
+}
+
+// Crude html parsing, good enough for the arch news
+// This is only displayed in the terminal so there should be no security
+// concerns
+func parseNews(str string) string {
+	var buffer bytes.Buffer
+	var tagBuffer bytes.Buffer
+	var escapeBuffer bytes.Buffer
+	inTag := false
+	inEscape := false
+
+	for _, char := range str {
+		if inTag {
+			if char == '>' {
+				inTag = false
+				switch tagBuffer.String() {
+				case "code":
+					buffer.WriteString(text.CyanCode)
+				case "/code":
+					buffer.WriteString(text.ResetCode)
+				case "/p":
+					buffer.WriteRune('\n')
+				}
+
+				continue
+			}
+
+			tagBuffer.WriteRune(char)
+			continue
+		}
+
+		if inEscape {
+			if char == ';' {
+				inEscape = false
+				escapeBuffer.WriteRune(char)
+				s := html.UnescapeString(escapeBuffer.String())
+				buffer.WriteString(s)
+				continue
+			}
+
+			escapeBuffer.WriteRune(char)
+			continue
+		}
+
+		if char == '<' {
+			inTag = true
+			tagBuffer.Reset()
+			continue
+		}
+
+		if char == '&' {
+			inEscape = true
+			escapeBuffer.Reset()
+			escapeBuffer.WriteRune(char)
+			continue
+		}
+
+		buffer.WriteRune(char)
+	}
+
+	buffer.WriteString(text.ResetCode)
+	return buffer.String()
+}

+ 44 - 0
pkg/query/filter.go

@@ -0,0 +1,44 @@
+package query
+
+import "github.com/Jguer/go-alpm"
+
+// FilterPackages filters packages based on source and type from local repository.
+func FilterPackages(alpmHandle *alpm.Handle) (
+	local, remote []alpm.Package,
+	localNames, remoteNames []string,
+	err error) {
+	localDB, err := alpmHandle.LocalDB()
+	if err != nil {
+		return
+	}
+	dbList, err := alpmHandle.SyncDBs()
+	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
+			}
+
+			if d.Pkg(k.Name()) != 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 local, remote, localNames, remoteNames, err
+}

+ 116 - 0
pkg/settings/config.go

@@ -0,0 +1,116 @@
+package settings
+
+import (
+	"bytes"
+	"encoding/json"
+	"fmt"
+	"os"
+)
+
+const (
+	// Describes Sorting method for numberdisplay
+	BottomUp = iota
+	TopDown
+)
+
+// Configuration stores yay's config.
+type Configuration struct {
+	AURURL             string `json:"aururl"`
+	BuildDir           string `json:"buildDir"`
+	ABSDir             string `json:"absdir"`
+	Editor             string `json:"editor"`
+	EditorFlags        string `json:"editorflags"`
+	MakepkgBin         string `json:"makepkgbin"`
+	MakepkgConf        string `json:"makepkgconf"`
+	PacmanBin          string `json:"pacmanbin"`
+	PacmanConf         string `json:"pacmanconf"`
+	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"`
+	SearchBy           string `json:"searchby"`
+	GitFlags           string `json:"gitflags"`
+	RemoveMake         string `json:"removemake"`
+	SudoBin            string `json:"sudobin"`
+	SudoFlags          string `json:"sudoflags"`
+	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"`
+	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"`
+	BatchInstall       bool   `json:"batchinstall"`
+}
+
+// SaveConfig writes yay config to file.
+func (config *Configuration) SaveConfig(configPath string) error {
+	marshalledinfo, err := json.MarshalIndent(config, "", "\t")
+	if err != nil {
+		return err
+	}
+	in, err := os.OpenFile(configPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
+	if err != nil {
+		return err
+	}
+	defer in.Close()
+	if _, err = in.Write(marshalledinfo); err != nil {
+		return err
+	}
+	return in.Sync()
+}
+
+func (config *Configuration) ExpandEnv() {
+	config.AURURL = os.ExpandEnv(config.AURURL)
+	config.ABSDir = os.ExpandEnv(config.ABSDir)
+	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.SearchBy = os.ExpandEnv(config.SearchBy)
+	config.GitBin = os.ExpandEnv(config.GitBin)
+	config.GpgBin = os.ExpandEnv(config.GpgBin)
+	config.SudoBin = os.ExpandEnv(config.SudoBin)
+	config.SudoFlags = os.ExpandEnv(config.SudoFlags)
+	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)
+}
+
+func (config *Configuration) String() string {
+	var buf bytes.Buffer
+	enc := json.NewEncoder(&buf)
+	enc.SetIndent("", "\t")
+	if err := enc.Encode(config); err != nil {
+		fmt.Fprintln(os.Stderr, err)
+	}
+	return buf.String()
+}

+ 15 - 10
pkg/text/color.go

@@ -3,13 +3,14 @@ package text
 import "fmt"
 
 const (
-	redCode    = "\x1b[31m"
-	greenCode  = "\x1b[32m"
-	yellowCode = "\x1b[33m"
-	cyanCode   = "\x1b[36m"
-	boldCode   = "\x1b[1m"
-
-	resetCode = "\x1b[0m"
+	redCode     = "\x1b[31m"
+	greenCode   = "\x1b[32m"
+	yellowCode  = "\x1b[33m"
+	magentaCode = "\x1b[35m"
+	CyanCode    = "\x1b[36m"
+	boldCode    = "\x1b[1m"
+
+	ResetCode = "\x1b[0m"
 )
 
 // UseColor determines if package will emit colors
@@ -17,7 +18,7 @@ var UseColor = true
 
 func stylize(startCode, in string) string {
 	if UseColor {
-		return startCode + in + resetCode
+		return startCode + in + ResetCode
 	}
 
 	return in
@@ -36,10 +37,14 @@ func yellow(in string) string {
 }
 
 func cyan(in string) string {
-	return stylize(cyanCode, in)
+	return stylize(CyanCode, in)
+}
+
+func Magenta(in string) string {
+	return stylize(magentaCode, in)
 }
 
-func bold(in string) string {
+func Bold(in string) string {
 	return stylize(boldCode, in)
 }
 

+ 12 - 12
pkg/text/print.go

@@ -9,40 +9,40 @@ import (
 
 const arrow = "==>"
 const smallArrow = " ->"
-const opSymbol = ":: "
+const opSymbol = "::"
 
 func OperationInfoln(a ...interface{}) {
-	fmt.Fprint(os.Stdout, append([]interface{}{boldCode, cyan(opSymbol), boldCode}, a...)...)
-	fmt.Fprintln(os.Stdout, resetCode)
+	fmt.Fprint(os.Stdout, append([]interface{}{Bold(cyan(opSymbol + " ")), boldCode}, a...)...)
+	fmt.Fprintln(os.Stdout, ResetCode)
 }
 
 func OperationInfo(a ...interface{}) {
-	fmt.Fprint(os.Stdout, append([]interface{}{boldCode, cyan(opSymbol), boldCode}, a...)...)
-	fmt.Fprint(os.Stdout, resetCode+" ")
+	fmt.Fprint(os.Stdout, append([]interface{}{Bold(cyan(opSymbol + " ")), boldCode}, a...)...)
+	fmt.Fprint(os.Stdout, ResetCode+" ")
 }
 
 func Info(a ...interface{}) {
-	fmt.Fprint(os.Stdout, append([]interface{}{bold(green(arrow + " "))}, a...)...)
+	fmt.Fprint(os.Stdout, append([]interface{}{Bold(green(arrow + " "))}, a...)...)
 }
 
 func Infoln(a ...interface{}) {
-	fmt.Fprintln(os.Stdout, append([]interface{}{bold(green(arrow))}, a...)...)
+	fmt.Fprintln(os.Stdout, append([]interface{}{Bold(green(arrow))}, a...)...)
 }
 
 func Warn(a ...interface{}) {
-	fmt.Fprint(os.Stdout, append([]interface{}{bold(yellow(smallArrow + " "))}, a...)...)
+	fmt.Fprint(os.Stdout, append([]interface{}{Bold(yellow(smallArrow + " "))}, a...)...)
 }
 
 func Warnln(a ...interface{}) {
-	fmt.Fprintln(os.Stdout, append([]interface{}{bold(yellow(smallArrow))}, a...)...)
+	fmt.Fprintln(os.Stdout, append([]interface{}{Bold(yellow(smallArrow))}, a...)...)
 }
 
 func Error(a ...interface{}) {
-	fmt.Fprint(os.Stderr, append([]interface{}{bold(red(smallArrow + " "))}, a...)...)
+	fmt.Fprint(os.Stderr, append([]interface{}{Bold(red(smallArrow + " "))}, a...)...)
 }
 
 func Errorln(a ...interface{}) {
-	fmt.Fprintln(os.Stderr, append([]interface{}{bold(red(smallArrow))}, a...)...)
+	fmt.Fprintln(os.Stderr, append([]interface{}{Bold(red(smallArrow))}, a...)...)
 }
 
 func PrintInfoValue(str, value string) {
@@ -50,5 +50,5 @@ func PrintInfoValue(str, value string) {
 		value = gotext.Get("None")
 	}
 
-	fmt.Fprintf(os.Stdout, bold("%-16s%s")+" %s\n", str, ":", value)
+	fmt.Fprintf(os.Stdout, Bold("%-16s%s")+" %s\n", str, ":", value)
 }

+ 8 - 93
print.go

@@ -2,20 +2,17 @@ package main
 
 import (
 	"bufio"
-	"bytes"
-	"encoding/xml"
 	"fmt"
-	"io/ioutil"
-	"net/http"
 	"os"
 	"strconv"
 	"strings"
-	"time"
 
 	"github.com/leonelquinteros/gotext"
 	rpc "github.com/mikkeloscar/aur"
 
 	"github.com/Jguer/yay/v10/pkg/intrange"
+	"github.com/Jguer/yay/v10/pkg/query"
+	"github.com/Jguer/yay/v10/pkg/settings"
 	"github.com/Jguer/yay/v10/pkg/stringset"
 	"github.com/Jguer/yay/v10/pkg/text"
 )
@@ -57,9 +54,9 @@ func (q aurQuery) printSearch(start int) {
 		var toprint string
 		if config.SearchMode == numberMenu {
 			switch config.SortMode {
-			case topDown:
+			case settings.TopDown:
 				toprint += magenta(strconv.Itoa(start+i) + " ")
-			case bottomUp:
+			case settings.BottomUp:
 				toprint += magenta(strconv.Itoa(len(q)+start-i-1) + " ")
 			default:
 				text.Warnln(gotext.Get("invalid sort mode. Fix with yay -Y --bottomup --save"))
@@ -100,9 +97,9 @@ func (s repoQuery) printSearch() {
 		var toprint string
 		if config.SearchMode == numberMenu {
 			switch config.SortMode {
-			case topDown:
+			case settings.TopDown:
 				toprint += magenta(strconv.Itoa(i+1) + " ")
-			case bottomUp:
+			case settings.BottomUp:
 				toprint += magenta(strconv.Itoa(len(s)-i) + " ")
 			default:
 				text.Warnln(gotext.Get("invalid sort mode. Fix with yay -Y --bottomup --save"))
@@ -333,7 +330,7 @@ func localStatistics() error {
 		return err
 	}
 
-	_, _, _, remoteNames, err := filterPackages()
+	_, _, _, remoteNames, err := query.FilterPackages(alpmHandle)
 	if err != nil {
 		return err
 	}
@@ -375,7 +372,7 @@ func printUpdateList(parser *arguments) error {
 	warnings := makeWarnings()
 	old := os.Stdout // keep backup of the real stdout
 	os.Stdout = nil
-	_, _, localNames, remoteNames, err := filterPackages()
+	_, _, localNames, remoteNames, err := query.FilterPackages(alpmHandle)
 	if err != nil {
 		return err
 	}
@@ -441,88 +438,6 @@ outer:
 	return nil
 }
 
-type item struct {
-	Title       string `xml:"title"`
-	Link        string `xml:"link"`
-	Description string `xml:"description"`
-	PubDate     string `xml:"pubDate"`
-	Creator     string `xml:"dc:creator"`
-}
-
-func (item *item) print(buildTime time.Time) {
-	var fd string
-	date, err := time.Parse(time.RFC1123Z, item.PubDate)
-
-	if err != nil {
-		fmt.Fprintln(os.Stderr, err)
-	} else {
-		fd = text.FormatTime(int(date.Unix()))
-		if _, double, _ := cmdArgs.getArg("news", "w"); !double && !buildTime.IsZero() {
-			if buildTime.After(date) {
-				return
-			}
-		}
-	}
-
-	fmt.Println(bold(magenta(fd)), bold(strings.TrimSpace(item.Title)))
-
-	if !cmdArgs.existsArg("q", "quiet") {
-		desc := strings.TrimSpace(parseNews(item.Description))
-		fmt.Println(desc)
-	}
-}
-
-type channel struct {
-	Title         string `xml:"title"`
-	Link          string `xml:"link"`
-	Description   string `xml:"description"`
-	Language      string `xml:"language"`
-	Lastbuilddate string `xml:"lastbuilddate"`
-	Items         []item `xml:"item"`
-}
-
-type rss struct {
-	Channel channel `xml:"channel"`
-}
-
-func printNewsFeed() error {
-	resp, err := http.Get("https://archlinux.org/feeds/news")
-	if err != nil {
-		return err
-	}
-
-	defer resp.Body.Close()
-	body, err := ioutil.ReadAll(resp.Body)
-	if err != nil {
-		return err
-	}
-
-	rssGot := rss{}
-
-	d := xml.NewDecoder(bytes.NewReader(body))
-	err = d.Decode(&rssGot)
-	if err != nil {
-		return err
-	}
-
-	buildTime, err := lastBuildTime()
-	if err != nil {
-		return err
-	}
-
-	if config.SortMode == bottomUp {
-		for i := len(rssGot.Channel.Items) - 1; i >= 0; i-- {
-			rssGot.Channel.Items[i].print(buildTime)
-		}
-	} else {
-		for i := 0; i < len(rssGot.Channel.Items); i++ {
-			rssGot.Channel.Items[i].print(buildTime)
-		}
-	}
-
-	return nil
-}
-
 const (
 	redCode     = "\x1b[31m"
 	greenCode   = "\x1b[32m"

+ 5 - 64
query.go

@@ -7,7 +7,6 @@ import (
 	"sort"
 	"strings"
 	"sync"
-	"time"
 
 	alpm "github.com/Jguer/go-alpm"
 	"github.com/leonelquinteros/gotext"
@@ -15,6 +14,7 @@ import (
 
 	"github.com/Jguer/yay/v10/pkg/intrange"
 	"github.com/Jguer/yay/v10/pkg/multierror"
+	"github.com/Jguer/yay/v10/pkg/settings"
 	"github.com/Jguer/yay/v10/pkg/stringset"
 	"github.com/Jguer/yay/v10/pkg/text"
 )
@@ -62,7 +62,7 @@ func (q aurQuery) Less(i, j int) bool {
 		result = q[i].PackageBaseID < q[j].PackageBaseID
 	}
 
-	if config.SortMode == bottomUp {
+	if config.SortMode == settings.BottomUp {
 		return !result
 	}
 
@@ -73,47 +73,6 @@ func (q aurQuery) Swap(i, j int) {
 	q[i], q[j] = q[j], q[i]
 }
 
-// FilterPackages filters packages based on source and type from local repository.
-func filterPackages() (
-	local, remote []alpm.Package,
-	localNames, remoteNames []string,
-	err error) {
-	localDB, err := alpmHandle.LocalDB()
-	if err != nil {
-		return
-	}
-	dbList, err := alpmHandle.SyncDBs()
-	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
-			}
-
-			if d.Pkg(k.Name()) != 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 local, remote, localNames, remoteNames, err
-}
-
 func getSearchBy(value string) rpc.By {
 	switch value {
 	case "name":
@@ -212,14 +171,14 @@ func syncSearch(pkgS []string) (err error) {
 	}
 
 	switch config.SortMode {
-	case topDown:
+	case settings.TopDown:
 		if mode == modeRepo || mode == modeAny {
 			pq.printSearch()
 		}
 		if mode == modeAUR || mode == modeAny {
 			aq.printSearch(1)
 		}
-	case bottomUp:
+	case settings.BottomUp:
 		if mode == modeAUR || mode == modeAny {
 			aq.printSearch(1)
 		}
@@ -310,7 +269,7 @@ func queryRepo(pkgInputN []string) (s repoQuery, err error) {
 		return nil
 	})
 
-	if config.SortMode == bottomUp {
+	if config.SortMode == settings.BottomUp {
 		for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
 			s[i], s[j] = s[j], s[i]
 		}
@@ -450,24 +409,6 @@ func hangingPackages(removeOptional bool) (hanging []string, err error) {
 	return hanging, err
 }
 
-func lastBuildTime() (time.Time, error) {
-	var lastTime time.Time
-
-	pkgs, _, _, _, err := filterPackages()
-	if err != nil {
-		return lastTime, err
-	}
-
-	for _, pkg := range pkgs {
-		thisTime := pkg.BuildDate()
-		if thisTime.After(lastTime) {
-			lastTime = thisTime
-		}
-	}
-
-	return lastTime, nil
-}
-
 // Statistics returns statistics about packages installed in system
 func statistics() (*struct {
 	Totaln    int

+ 2 - 1
upgrade.go

@@ -10,6 +10,7 @@ import (
 	"github.com/leonelquinteros/gotext"
 
 	"github.com/Jguer/yay/v10/pkg/intrange"
+	"github.com/Jguer/yay/v10/pkg/query"
 	"github.com/Jguer/yay/v10/pkg/text"
 
 	rpc "github.com/mikkeloscar/aur"
@@ -117,7 +118,7 @@ func getVersionDiff(oldVersion, newVersion string) (left, right string) {
 
 // upList returns lists of packages to upgrade from each source.
 func upList(warnings *aurWarnings) (aurUp, repoUp upSlice, err error) {
-	_, remote, _, remoteNames, err := filterPackages()
+	_, remote, _, remoteNames, err := query.FilterPackages(alpmHandle)
 	if err != nil {
 		return nil, nil, err
 	}

+ 2 - 1
vcs.go

@@ -12,6 +12,7 @@ import (
 	gosrc "github.com/Morganamilo/go-srcinfo"
 	"github.com/leonelquinteros/gotext"
 
+	"github.com/Jguer/yay/v10/pkg/query"
 	"github.com/Jguer/yay/v10/pkg/stringset"
 	"github.com/Jguer/yay/v10/pkg/text"
 )
@@ -30,7 +31,7 @@ func createDevelDB() error {
 	var mux sync.Mutex
 	var wg sync.WaitGroup
 
-	_, _, _, remoteNames, err := filterPackages()
+	_, _, _, remoteNames, err := query.FilterPackages(alpmHandle)
 	if err != nil {
 		return err
 	}