~chrisppy/beagles

f5ad6a8a3ffe185a41fbee7a34f769baf04893c9 — Chris Palmer a month ago c0066f8
Switch to POSIX Flags
5 files changed, 188 insertions(+), 69 deletions(-)

M CHANGELOG.md
M doc/beagles.1.scd
M go.mod
M go.sum
M main.go
M CHANGELOG.md => CHANGELOG.md +1 -0
@@ 10,6 10,7 @@
  - Update to cbind 0.1.3 (~chrisppy)
  - Update to tcell 2.0.0 (~chrisppy)
  - Updated list to remove refresh, now adds and removes in place (~chrisppy)
  - Changed from using environment variables to POSIX Flags (~chrisppy)

### Fixed


M doc/beagles.1.scd => doc/beagles.1.scd +44 -16
@@ 6,7 6,41 @@ beagles - TUI RSS feed aggregator

# SYNOPSIS

*beagles*
*beagles* [POSIX style options] ...

# DESCRIPTION

_beagles_ is a terminal user interface RSS aggregator that allows++
you to keep track of RSS feeds, favorite them for quick reference,++
or even play the podcasts available on the feed.

# OPTIONS

*-c[dir]*
	Set the config directory.  If not set, use++
	*XDG_CONFIG_HOME*/beagles.

*-d[dir]*
	Set the database directory.  If not set, use++
	*XDG_DATA_HOME*/beagles.

*-D*
	Print debug lines.

*-h*
	Print basic usage options.

*-l[directory]*
	Set the logging directory.  If not set, use++
	*XDG_CACHE_HOME*/beagles.

*-s[directory]*
	Set the download directory. If not set use++
	*XDG_DATA_HOME*/beagles.

*-V*
	Print version information


# USAGE



@@ 49,21 83,23 @@ beagles - TUI RSS feed aggregator
	mark current post as unread when in CONTENT or SUBSCRIPTIONS

*f*
	mark current post as favorite when in LIST, CONTENT, or SUBSCRIPTIONS
	mark current post as favorite when in LIST, CONTENT, or
	SUBSCRIPTIONS

*c*
	mark current post as unfavorite when in LIST, CONTENT, SUBSCRIPTIONS,
	or FAVORITES
	mark current post as unfavorite when in LIST, CONTENT,++
	SUBSCRIPTIONS, or FAVORITES

*n*
	open post url when in LIST, CONTENT, SUBSCRIPTIONS, or FAVORITES

*d*
	download a single podcast when in LIST, CONTENT, SUBSCRIPTIONS, or
	FAVORITES
	download a single podcast when in LIST, CONTENT, SUBSCRIPTIONS,++
	or FAVORITES

*p*
	play single podcast when in LIST, CONTENT, SUBSCRIPTIONS, or FAVORITES
	play single podcast when in LIST, CONTENT, SUBSCRIPTIONS, or++
	FAVORITES

*ENTER*
	expand/collapse subscription in SUBSCRIPTIONS


@@ 77,7 113,7 @@ beagles - TUI RSS feed aggregator
	add a feed

*remove, rm* [url]
	remove the feed, note you can omit the url if you are in the
	remove the feed, note you can omit the url if you are in the++
	subscription page on a current feed

*update, up*


@@ 95,14 131,6 @@ beagles - TUI RSS feed aggregator
*quit, q*
	exit the application

# ENVIRONMENT

	The config, database, download, and log directories default to
	$HOME/.config/beagles, $HOME/.local/share/beagles, and
	$HOME/.local/share/beagles , respectfully.  If you want to override
	these paths you must set the BEAGLES_CFG, BEAGLES_DB, BEAGLES_DL,
	and BEAGLES_LOG environment variables within your shell.

# CONFIGURATION

See *beagles-config*(5)

M go.mod => go.mod +1 -0
@@ 3,6 3,7 @@ module git.sr.ht/~chrisppy/beagles
go 1.14

require (
	git.sr.ht/~sircmpwn/getopt v0.0.0-20191230200459-23622cc906b3
	github.com/DataDrake/waterlog v1.0.5
	github.com/gdamore/tcell/v2 v2.0.0
	github.com/jaytaylor/html2text v0.0.0-20200412013138-3577fbdbcff7

M go.sum => go.sum +2 -0
@@ 1,3 1,5 @@
git.sr.ht/~sircmpwn/getopt v0.0.0-20191230200459-23622cc906b3 h1:4wDp4BKF7NQqoh73VXpZsB/t1OEhDpz/zEpmdQfbjDk=
git.sr.ht/~sircmpwn/getopt v0.0.0-20191230200459-23622cc906b3/go.mod h1:wMEGFFFNuPos7vHmWXfszqImLppbc0wEhh6JBfJIUgw=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/DataDrake/waterlog v1.0.5 h1:+c506dboTQh4MoHHwdVtNa9E8K/3qAM/lieke0mH/mE=

M main.go => main.go +140 -53
@@ 18,6 18,7 @@
package main

import (
	"fmt"
	"log"
	"os"
	"path/filepath"


@@ 25,6 26,7 @@ import (
	"git.sr.ht/~chrisppy/beagles/config"
	"git.sr.ht/~chrisppy/beagles/db"
	"git.sr.ht/~chrisppy/beagles/ui"
	"git.sr.ht/~sircmpwn/getopt"
	wlog "github.com/DataDrake/waterlog"
	"github.com/DataDrake/waterlog/format"
	"github.com/DataDrake/waterlog/level"


@@ 40,99 42,184 @@ func main() {
	logger.SetLevel(level.Info)
	logger.SetFormat(format.Un)

	logDir := os.Getenv("BEAGLES_LOG")
	if logDir == "" {
		d, err := os.UserCacheDir()
		if err != nil {
			wlog.Fatal(err.Error())
	opts, optind, err := getopt.Getopts(os.Args, "c:d:Dhl:s:V")
	if err != nil {
		logger.Fatal(err)
	}

	var configDir, dbDir, dlDir, logDir string
	for _, opt := range opts {
		switch opt.Option {
		case 'c':
			configDir = opt.Value
		case 'd':
			dbDir = opt.Value
		case 'D':
			logger.SetFormat(level.Debug)
		case 'h':
			printHelp(logger)
		case 'l':
			logDir = opt.Value
		case 's':
			dlDir = opt.Value
		case 'V':
			printVersion(logger)
		}
	}

		logDir = filepath.Join(d, "beagles")
	for _, arg := range os.Args[optind:] {
		logger.Debugln(arg)
	}

		if _, err = os.Stat(logDir); os.IsNotExist(err) {
			if errDir := os.MkdirAll(logDir, 0750); errDir != nil {
				wlog.Fatal(err)
			}
		}
	cfg, err := getConfig(configDir)
	if err != nil {
		logger.Fatal(err)
	}

	logPath := filepath.Clean(filepath.Join(logDir, "beagles.log"))
	f, err := os.OpenFile(logPath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 00600)
	db, err := getDatabase(dbDir)
	if err != nil {
		logger.Fatal(err)
	}

	dlDir, err = getDownloadDir(dlDir)
	if err != nil {
		logger.Fatal(err)
	}

	logFile, err := getLogFile(logDir)
	if err != nil {
		logger.Fatal(err)
	}
	logger.SetOutput(logFile)

	i := &ui.UI{
		Version:      Version,
		DB:           db,
		Config:       cfg,
		DownloadPath: dlDir,
		Logger:       logger,
	}

	i.Init()

	if cfg.AutoUpdateInterval > 0 {
		go i.UpdateLoop(cfg.AutoUpdateInterval)
	}

	if err := i.Run(); err != nil {
		wlog.Fatal(err.Error())
	}
	logger.SetOutput(f)
}

	configDir := os.Getenv("BEAGLES_CFG")
	if configDir == "" {
		d, err := os.UserConfigDir()
func getDatabase(dir string) (*db.Storage, error) {
	if dir == "" {
		d, err := os.UserHomeDir()
		if err != nil {
			wlog.Fatal(err.Error())
			return nil, err
		}

		configDir = filepath.Join(d, "beagles")
		dir = filepath.Join(d, ".local", "share", "beagles")

		if _, err = os.Stat(configDir); os.IsNotExist(err) {
			if errDir := os.MkdirAll(configDir, 0750); errDir != nil {
				wlog.Fatal(err)
		if _, err = os.Stat(dir); os.IsNotExist(err) {
			if errDir := os.MkdirAll(dir, 0750); errDir != nil {
				return nil, err
			}
		}
	}

	dbDir := os.Getenv("BEAGLES_DB")
	if dbDir == "" {
		d, err := os.UserHomeDir()
	return db.ReadDB(filepath.Clean(filepath.Join(dir, "beagles.db")))
}

func getConfig(dir string) (*config.Config, error) {
	if dir == "" {
		d, err := os.UserConfigDir()
		if err != nil {
			wlog.Fatal(err.Error())
			return nil, err
		}

		dbDir = filepath.Join(d, ".local", "share", "beagles")
		dir = filepath.Join(d, "beagles")

		if _, err = os.Stat(dbDir); os.IsNotExist(err) {
			if errDir := os.MkdirAll(dbDir, 0750); errDir != nil {
				wlog.Fatal(err)
		if _, err = os.Stat(dir); os.IsNotExist(err) {
			if errDir := os.MkdirAll(dir, 0750); errDir != nil {
				return nil, err
			}
		}
	}

	dlDir := os.Getenv("BEAGLES_DL")
	if dlDir == "" {
		d, err := os.UserHomeDir()
	return config.Load(dir), nil
}

func getLogFile(dir string) (*os.File, error) {
	if dir == "" {
		d, err := os.UserCacheDir()
		if err != nil {
			wlog.Fatal(err.Error())
			return nil, err
		}

		dlDir = filepath.Join(d, ".local", "share", "beagles")

		if _, err = os.Stat(dlDir); os.IsNotExist(err) {
			if errDir := os.MkdirAll(dlDir, 0750); errDir != nil {
				wlog.Fatal(err)
		dir = filepath.Join(d, "beagles")
		if _, err = os.Stat(dir); os.IsNotExist(err) {
			if errDir := os.MkdirAll(dir, 0750); errDir != nil {
				return nil, err
			}
		}
	}

	db, err := db.ReadDB(filepath.Clean(filepath.Join(dbDir, "beagles.db")))
	logPath := filepath.Clean(filepath.Join(dir, "beagles.log"))
	f, err := os.OpenFile(logPath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 00600)
	if err != nil {
		wlog.Fatal(err.Error())
		return nil, err
	}

	cfg := config.Load(configDir)
	return f, nil
}

	i := &ui.UI{
		Version:      Version,
		DB:           db,
		Config:       cfg,
		DownloadPath: dlDir,
		Logger:       logger,
func getDownloadDir(dir string) (string, error) {
	if dir == "" {
		d, err := os.UserHomeDir()
		if err != nil {
			return "", err
		}

		dir = filepath.Join(d, ".local", "share", "beagles")

		if _, err = os.Stat(dir); os.IsNotExist(err) {
			if errDir := os.MkdirAll(dir, 0750); errDir != nil {
				return "", err
			}
		}
	}

	i.Init()
	return dir, nil
}

	if cfg.AutoUpdateInterval > 0 {
		go i.UpdateLoop(cfg.AutoUpdateInterval)
func printHelp(logger *wlog.WaterLog) {
	usage := `beagles version %s Copyright (c) 2020 the beagles developers
Usage: beagles [options]...

	-c[dir]		Set the config directory.  If not set, use
			XDG_CONFIG_HOME/beagles.
	-d[dir]		Set the database directory.  If not set, use
			XDG_DATA_HOME/beagles.
	-D		Print debug lines.
 	-h		Print basic options
	-l[dir]		Set the logging directory.  If not set, use
			XDG_CACHE_HOME/beagles.
	-s[dir]		Set the download directory.  If not set, use
			XDG_DATA_HOME/beagles.
	-V		Print version information

For more information consult the beagles man pages.
`
	if _, err := fmt.Fprintf(os.Stdout, usage, Version); err != nil {
		logger.Fatalf("error writing help page to stdout: %s\n", err.Error())
	}
	os.Exit(0)

	if err := i.Run(); err != nil {
		wlog.Fatal(err.Error())
}

func printVersion(logger *wlog.WaterLog) {
	if _, err := fmt.Fprintf(os.Stdout, "beagles v%s\n", Version); err != nil {
		logger.Fatalf("error writing version to stdout: %s\n", err.Error())
	}
	os.Exit(0)
}