1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
|
// Copyright (C) 2021 nytpu
// SPDX-License-Identifier: AGPL-3.0-only
// For more license details, see LICENSE or <https://www.gnu.org/licenses/agpl-3.0.html>.
// comitium is a feed aggregator for gemini. see README.md for more details and
// doc/comitium.1.scd for a manual page.
package main
// generate the translations:
//go:generate go-localize -input localizations_src -output localizations
import (
"fmt"
"net/url"
"os"
"path/filepath"
"github.com/mitchellh/go-homedir"
"golang.nytpu.com/comitium/localizations"
"golang.org/x/text/language"
"golang.nytpu.com/comitium/core"
"golang.nytpu.com/comitium/fetch"
)
var supportedLangs = []language.Tag{
language.AmericanEnglish, // en-US (fallback)
}
func main() {
argCheck(os.Args, 2, true, "Please provide a command.")
// these are special cases where we shouldn't bother to do anything else
if os.Args[1] == "help" {
usage()
os.Exit(0)
}
if os.Args[1] == "version" {
fmt.Printf("comitium v%v, commit %v\n", core.Version, core.Commit)
os.Exit(0)
}
c, ok := commands[os.Args[1]]
if !ok {
fmt.Fprint(os.Stderr, "Unrecognized Command.\n\n")
usage()
os.Exit(1)
}
c.AddFlagSet(globalFs)
c.Parse(os.Args[2:])
// figure out what language to use
localizer := getLang()
// dataPath is the location where all the data (list of subs, cached hashes,
// etcetera) are stored
dataPath := getDataPath()
// make sure it exists so we can start working with files in it
os.MkdirAll(dataPath, os.ModePerm)
err := core.Init(dataPath, localizer)
if err != nil {
fmt.Fprintf(os.Stderr, "Error in initialization: %v\n", err)
os.Exit(1)
}
switch os.Args[1] {
case "refresh":
argCheck(c.Args(), 0, false, "'refresh' doesn't take an argument!")
core.Timeout = *refreshTimeoutFlag
fetch.RefreshAll(&core.Data, *refreshWorkersFlag)
case "regenerate":
argCheck(c.Args(), 0, false, "'regenerate' doesn't take an argument!")
case "add":
argCheck(c.Args(), 1, true, "Please provide a URL to add.")
remote, err := url.Parse(c.Arg(0))
if err != nil {
fmt.Fprintf(os.Stderr, "Error parsing URL: %v\n", err)
os.Exit(1)
}
if *addPageFlag {
err = fetch.Page(&core.Data, remote, c.Arg(1))
} else {
err = fetch.Feed(&core.Data, remote, c.Arg(1))
}
if err != nil {
fmt.Fprintf(os.Stderr, "Error adding URL: %v\n", err)
os.Exit(1)
}
case "remove":
argCheck(c.Args(), 1, false, "Please provide a URL to remove.")
if (!*removeFeedsFlag && !*removePagesFlag) || *removeFeedsFlag {
delete(core.Data.Feeds, c.Arg(0))
}
if (!*removeFeedsFlag && !*removePagesFlag) || *removePagesFlag {
delete(core.Data.Pages, c.Arg(0))
}
}
err = core.Data.WriteSubs(dataPath)
if err != nil {
fmt.Fprintf(os.Stderr, "Error saving subscriptions: %v\n", err)
os.Exit(1)
}
err = core.Data.WriteFeed(dataPath)
if err != nil {
fmt.Fprintf(os.Stderr, "Error writing feed: %v\n", err)
os.Exit(1)
}
err = core.Data.WriteJSON(dataPath)
if err != nil {
fmt.Fprintf(os.Stderr, "Error saving subscriptions json: %v\n", err)
os.Exit(1)
}
}
// argCheck makes sure that the target number of arguments are given. If
// not, msg is displayed alongside usage information and then the program exits
// with failure.
func argCheck(args []string, target int, lt bool, msg string) {
if (lt && len(args) < target) || (!lt && len(args) != target) {
fmt.Fprint(os.Stderr, msg, "\n\n")
usage()
os.Exit(1)
}
}
// verifyPath will expand ~ if neccessary and will clean the path.
func verifyPath(p string) string {
if n, err := homedir.Expand(p); err != nil {
return filepath.Clean(p)
} else {
return filepath.Clean(n)
}
}
// getDataPath will return a data path in a gracefully degrading tree, picking
// the first that exists:
// --data flag, $COMITIUM_DATA, $XDG_DATA_HOME/comitium,
// $HOME/.local/share/comitium, $HOME/.comitium, $PWD (last resort)
func getDataPath() string {
if *dataFlag != "" {
return verifyPath(*dataFlag)
}
if env := os.Getenv("COMITIUM_DATA"); env != "" {
return verifyPath(env)
}
if env := os.Getenv("XDG_DATA_HOME"); env != "" {
return verifyPath(env + "/comitium")
}
home, err := homedir.Dir()
if err == nil {
if _, err := os.Stat(verifyPath(home + "/.local")); !os.IsNotExist(err) {
return verifyPath(home + "/.local/share/comitium")
}
return verifyPath(home + "/.comitium")
}
fmt.Fprintln(os.Stderr, "Error finding a suitable directory! Falling back to the current working directory. See \"comitium help\" or comitium(1) for more information.")
return verifyPath("comitium")
}
// getLang will return the localizer for the user's lang
func getLang() *localizations.Localizer {
matcher := language.NewMatcher(supportedLangs)
var lang language.Tag
if *langFlag != "" {
lang, _ = language.MatchStrings(matcher, *langFlag)
} else if env := os.Getenv("LANG"); env != "" {
lang, _ = language.MatchStrings(matcher, env)
}
return localizations.New(lang.String(), supportedLangs[0].String())
}
func usage() {
fmt.Fprintf(os.Stderr, "Usage:\n")
fmt.Fprintf(os.Stderr, " %v <command> [FLAGS] [ARGUMENTS]\n\n", os.Args[0])
fmt.Fprint(os.Stderr, "Commands:\n")
for _, v := range commandInfo {
// FIXME: find a way to dynamically match the spacing that
// flag.PrintDefaults() outputs (instead of hardcoding "-20")
fmt.Fprintf(os.Stderr, " %-20s%s\n", v.Name, v.Usage)
}
fmt.Fprintf(os.Stderr, "\nGlobal Flags:\n%v", globalFs.FlagUsages())
// command-specific flags should be printed here
fmt.Fprintf(os.Stderr, "Flags for \"add\":\n%v", commands["add"].FlagUsages())
fmt.Fprintf(os.Stderr, "Flags for \"refresh\":\n%v", commands["refresh"].FlagUsages())
fmt.Fprintf(os.Stderr, "Flags for \"remove\":\n%v\n", commands["remove"].FlagUsages())
fmt.Fprint(os.Stderr, "For more help, see comitium(1).\n")
}
|