diff --git a/clean.go b/clean.go index 30c660d3..eefb4ac7 100644 --- a/clean.go +++ b/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 } diff --git a/cmd.go b/cmd.go index 18032003..62ebdaa6 100644 --- a/cmd.go +++ b/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")) diff --git a/config.go b/config.go index 88161ab9..656ce25c 100644 --- a/config.go +++ b/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" ) @@ -23,12 +22,6 @@ const ( minimal ) -const ( - // Describes Sorting method for numberdisplay - bottomUp = iota - topDown -) - const ( modeAUR targetMode = iota modeRepo @@ -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 diff --git a/config_test.go b/config_test.go index de164988..11afdd64 100644 --- a/config_test.go +++ b/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) } diff --git a/install.go b/install.go index 214d5c51..09194fa7 100644 --- a/install.go +++ b/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 } diff --git a/keys_test.go b/keys_test.go index a3aa5616..ccd3766e 100644 --- a/keys_test.go +++ b/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) diff --git a/main.go b/main.go index d9b78258..8544d08c 100644 --- a/main.go +++ b/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()) } diff --git a/parser.go b/parser.go index 56049b65..fb8ca365 100644 --- a/parser.go +++ b/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() -} diff --git a/pkg/news/news.go b/pkg/news/news.go new file mode 100644 index 00000000..716e3829 --- /dev/null +++ b/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() +} diff --git a/pkg/query/filter.go b/pkg/query/filter.go new file mode 100644 index 00000000..9006ff35 --- /dev/null +++ b/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 +} diff --git a/pkg/settings/config.go b/pkg/settings/config.go new file mode 100644 index 00000000..c95c0b26 --- /dev/null +++ b/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() +} diff --git a/pkg/text/color.go b/pkg/text/color.go index 9b643a87..9a1e6951 100644 --- a/pkg/text/color.go +++ b/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" + redCode = "\x1b[31m" + greenCode = "\x1b[32m" + yellowCode = "\x1b[33m" + magentaCode = "\x1b[35m" + CyanCode = "\x1b[36m" + boldCode = "\x1b[1m" - resetCode = "\x1b[0m" + 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 bold(in string) string { +func Magenta(in string) string { + return stylize(magentaCode, in) +} + +func Bold(in string) string { return stylize(boldCode, in) } diff --git a/pkg/text/print.go b/pkg/text/print.go index 2cf526f6..00744fec 100644 --- a/pkg/text/print.go +++ b/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) } diff --git a/print.go b/print.go index 5d863818..f5f45adb 100644 --- a/print.go +++ b/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" diff --git a/query.go b/query.go index ef5c2f5a..90eb5056 100644 --- a/query.go +++ b/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 diff --git a/upgrade.go b/upgrade.go index 4c608276..411771f9 100644 --- a/upgrade.go +++ b/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 } diff --git a/vcs.go b/vcs.go index 0b2dab40..f12415f5 100644 --- a/vcs.go +++ b/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 }