commit b469005
Kalyan Sriram
·
2021-11-22 18:37:40 +0000 UTC
parent 6014ba1
config: replace YAML with scfg config format This patch replaces the YAML configuration format with scfg (https://git.sr.ht/~emersion/scfg). Additionally, a few things about configuration are cleaned up: * abbreviated names are expanded (addr -> address, nick -> nickname) * negative bools switched to positive (no-tls -> tls) * independent column widths are grouped under the "pane-width" directive * implementation of default configuration values is improved * password-cmd is executed directly (with scfg field parsing) instead of with "sh -c". * on-highlight is now a file, $XDG_CONFIG_HOME/senpai/highlight by default, which can be changed with the on-highlight-path directive
+5,
-5
1@@ -14,12 +14,12 @@ senpai is an IRC client that works best with bouncers:
2
3 ```shell
4 mkdir -p ~/.config/senpai
5-cat <<EOF >~/.config/senpai/senpai.yaml
6-addr: chat.sr.ht
7-nick: senpai
8-password: "my password can't be this cute (2010)"
9+cat <<EOF >~/.config/senpai/senpai.scfg
10+address chat.sr.ht
11+nickname senpai
12+password "my password can't be this cute (2010)"
13 # alternatively, specify a command to fetch your password:
14-# password-cmd: "gopass show irc/<username>"
15+# password-cmd gopass show irc/<username>
16 EOF
17 go run ./cmd/senpai
18 ```
M
app.go
+31,
-17
1@@ -2,6 +2,7 @@ package senpai
2
3 import (
4 "crypto/tls"
5+ "errors"
6 "fmt"
7 "net"
8 "os"
9@@ -111,10 +112,7 @@ func NewApp(cfg Config) (app *App, err error) {
10 }
11 }
12
13- mouse := true
14- if cfg.Mouse != nil {
15- mouse = *cfg.Mouse
16- }
17+ mouse := cfg.Mouse
18
19 app.win, err = ui.New(ui.Config{
20 NickColWidth: cfg.NickColWidth,
21@@ -320,10 +318,10 @@ func (app *App) tryConnect() (conn net.Conn, err error) {
22 if colonIdx <= bracketIdx {
23 // either colonIdx < 0, or the last colon is before a ']' (end
24 // of IPv6 address. -> missing port
25- if app.cfg.NoTLS {
26- addr += ":6667"
27- } else {
28+ if app.cfg.TLS {
29 addr += ":6697"
30+ } else {
31+ addr += ":6667"
32 }
33 }
34
35@@ -332,7 +330,7 @@ func (app *App) tryConnect() (conn net.Conn, err error) {
36 return
37 }
38
39- if !app.cfg.NoTLS {
40+ if app.cfg.TLS {
41 host, _, _ := net.SplitHostPort(addr) // should succeed since net.Dial did.
42 conn = tls.Client(conn, &tls.Config{
43 ServerName: host,
44@@ -889,22 +887,38 @@ func (app *App) isHighlight(s *irc.Session, content string) bool {
45 return false
46 }
47
48-// notifyHighlight executes the "on-highlight" command according to the given
49+// notifyHighlight executes the script at "on-highlight-path" according to the given
50 // message context.
51 func (app *App) notifyHighlight(buffer, nick, content string) {
52- if app.cfg.OnHighlight == "" {
53- return
54+ path := app.cfg.OnHighlightPath
55+ if path == "" {
56+ defaultHighlightPath, err := DefaultHighlightPath()
57+ if err != nil {
58+ return
59+ }
60+ path = defaultHighlightPath
61 }
62- sh, err := exec.LookPath("sh")
63- if err != nil {
64+
65+ netID, curBuffer := app.win.CurrentBuffer()
66+ if _, err := os.Stat(app.cfg.OnHighlightPath); errors.Is(err, os.ErrNotExist) {
67+ // only error out if the user specified a highlight path
68+ // if default path unreachable, simple bail
69+ if app.cfg.OnHighlightPath != "" {
70+ body := fmt.Sprintf("Unable to find on-highlight command at path: %q", app.cfg.OnHighlightPath)
71+ app.addStatusLine(netID, ui.Line{
72+ At: time.Now(),
73+ Head: "!!",
74+ HeadColor: tcell.ColorRed,
75+ Body: ui.PlainString(body),
76+ })
77+ }
78 return
79 }
80- netID, curBuffer := app.win.CurrentBuffer()
81 here := "0"
82 if buffer == curBuffer { // TODO also check netID
83 here = "1"
84 }
85- cmd := exec.Command(sh, "-c", app.cfg.OnHighlight)
86+ cmd := exec.Command(app.cfg.OnHighlightPath)
87 cmd.Env = append(os.Environ(),
88 fmt.Sprintf("BUFFER=%s", buffer),
89 fmt.Sprintf("HERE=%s", here),
90@@ -913,7 +927,7 @@ func (app *App) notifyHighlight(buffer, nick, content string) {
91 )
92 output, err := cmd.CombinedOutput()
93 if err != nil {
94- body := fmt.Sprintf("Failed to invoke on-highlight command: %v. Output: %q", err, string(output))
95+ body := fmt.Sprintf("Failed to invoke on-highlight command at path: %v. Output: %q", err, string(output))
96 app.addStatusLine(netID, ui.Line{
97 At: time.Now(),
98 Head: "!!",
99@@ -928,7 +942,7 @@ func (app *App) notifyHighlight(buffer, nick, content string) {
100 func (app *App) typing() {
101 netID, buffer := app.win.CurrentBuffer()
102 s := app.sessions[netID]
103- if s == nil || app.cfg.NoTypings {
104+ if s == nil || !app.cfg.Typings {
105 return
106 }
107 if buffer == "" {
+1,
-1
1@@ -34,7 +34,7 @@ func main() {
2 if err != nil {
3 panic(err)
4 }
5- configPath = path.Join(configDir, "senpai", "senpai.yaml")
6+ configPath = path.Join(configDir, "senpai", "senpai.scfg")
7 }
8
9 cfg, err := senpai.LoadConfigFile(configPath)
+2,
-2
1@@ -108,7 +108,7 @@ func parseFlags() {
2 if err != nil {
3 panic(err)
4 }
5- configPath = path.Join(configDir, "senpai", "senpai.yaml")
6+ configPath = path.Join(configDir, "senpai", "senpai.scfg")
7 }
8
9 cfg, err := senpai.LoadConfigFile(configPath)
10@@ -121,6 +121,6 @@ func parseFlags() {
11 if cfg.Password != nil {
12 password = *cfg.Password
13 }
14- useTLS = !cfg.NoTLS
15+ useTLS = cfg.TLS
16 }
17 }
+215,
-57
1@@ -3,21 +3,20 @@ package senpai
2 import (
3 "errors"
4 "fmt"
5- "io/ioutil"
6+ "os"
7 "os/exec"
8+ "path"
9 "strconv"
10 "strings"
11
12 "github.com/gdamore/tcell/v2"
13
14- "gopkg.in/yaml.v2"
15+ "git.sr.ht/~emersion/go-scfg"
16 )
17
18 type Color tcell.Color
19
20-func (c *Color) UnmarshalText(data []byte) error {
21- s := string(data)
22-
23+func parseColor(s string, c *Color) error {
24 if strings.HasPrefix(s, "#") {
25 hex, err := strconv.ParseInt(s[1:], 16, 32)
26 if err != nil {
27@@ -47,34 +46,73 @@ func (c *Color) UnmarshalText(data []byte) error {
28 return nil
29 }
30
31+type ConfigColors struct {
32+ Prompt Color
33+}
34+
35 type Config struct {
36- Addr string
37- Nick string
38- Real string
39- User string
40- Password *string
41- PasswordCmd string `yaml:"password-cmd"`
42- NoTLS bool `yaml:"no-tls"`
43- Channels []string
44-
45- NoTypings bool `yaml:"no-typings"`
46- Mouse *bool
47-
48- Highlights []string
49- OnHighlight string `yaml:"on-highlight"`
50- NickColWidth int `yaml:"nick-column-width"`
51- ChanColWidth int `yaml:"chan-column-width"`
52- MemberColWidth int `yaml:"member-column-width"`
53-
54- Colors struct {
55- Prompt Color
56- }
57+ Addr string
58+ Nick string
59+ Real string
60+ User string
61+ Password *string
62+ TLS bool
63+ Channels []string
64+
65+ Typings bool
66+ Mouse bool
67+
68+ Highlights []string
69+ OnHighlightPath string
70+ NickColWidth int
71+ ChanColWidth int
72+ MemberColWidth int
73+
74+ Colors ConfigColors
75
76 Debug bool
77 }
78
79-func ParseConfig(buf []byte) (cfg Config, err error) {
80- err = yaml.Unmarshal(buf, &cfg)
81+func DefaultHighlightPath() (string, error) {
82+ configDir, err := os.UserConfigDir()
83+ if err != nil {
84+ return "", err
85+ }
86+ return path.Join(configDir, "senpai", "highlight"), nil
87+}
88+
89+func Defaults() (cfg Config, err error) {
90+ cfg = Config{
91+ Addr: "",
92+ Nick: "",
93+ Real: "",
94+ User: "",
95+ Password: nil,
96+ TLS: true,
97+ Channels: nil,
98+ Typings: true,
99+ Mouse: true,
100+ Highlights: nil,
101+ OnHighlightPath: "",
102+ NickColWidth: 16,
103+ ChanColWidth: 0,
104+ MemberColWidth: 0,
105+ Colors: ConfigColors{
106+ Prompt: Color(tcell.ColorDefault),
107+ },
108+ Debug: false,
109+ }
110+
111+ return
112+}
113+
114+func ParseConfig(filename string) (cfg Config, err error) {
115+ cfg, err = Defaults()
116+ if err != nil {
117+ return
118+ }
119+
120+ err = unmarshal(filename, &cfg)
121 if err != nil {
122 return cfg, err
123 }
124@@ -90,45 +128,165 @@ func ParseConfig(buf []byte) (cfg Config, err error) {
125 if cfg.Real == "" {
126 cfg.Real = cfg.Nick
127 }
128- if cfg.PasswordCmd != "" {
129- password, err := runPasswordCmd(cfg.PasswordCmd)
130- if err != nil {
131- return cfg, err
132- }
133- cfg.Password = &password
134- }
135- if cfg.NickColWidth <= 0 {
136- cfg.NickColWidth = 16
137- }
138- if cfg.ChanColWidth < 0 {
139- cfg.ChanColWidth = 0
140- }
141- if cfg.MemberColWidth < 0 {
142- cfg.MemberColWidth = 0
143- }
144 return
145 }
146
147 func LoadConfigFile(filename string) (cfg Config, err error) {
148- var buf []byte
149-
150- buf, err = ioutil.ReadFile(filename)
151- if err != nil {
152- return cfg, fmt.Errorf("failed to read the file: %s", err)
153- }
154-
155- cfg, err = ParseConfig(buf)
156+ cfg, err = ParseConfig(filename)
157 if err != nil {
158 return cfg, fmt.Errorf("invalid content found in the file: %s", err)
159 }
160 return
161 }
162
163-func runPasswordCmd(command string) (password string, err error) {
164- cmd := exec.Command("sh", "-c", command)
165- stdout, err := cmd.Output()
166- if err == nil {
167- password = strings.TrimSuffix(string(stdout), "\n")
168+func unmarshal(filename string, cfg *Config) (err error) {
169+ directives, err := scfg.Load(filename)
170+ if err != nil {
171+ return fmt.Errorf("error parsing scfg: %s", err)
172+ }
173+
174+ for _, d := range directives {
175+ switch d.Name {
176+ case "address":
177+ if err := d.ParseParams(&cfg.Addr); err != nil {
178+ return err
179+ }
180+ case "nickname":
181+ if err := d.ParseParams(&cfg.Nick); err != nil {
182+ return err
183+ }
184+ case "username":
185+ if err := d.ParseParams(&cfg.User); err != nil {
186+ return err
187+ }
188+ case "realname":
189+ if err := d.ParseParams(&cfg.Real); err != nil {
190+ return err
191+ }
192+ case "password":
193+ // if a password-cmd is provided, don't use this value
194+ if directives.Get("password-cmd") != nil {
195+ continue
196+ }
197+
198+ var password string
199+ if err := d.ParseParams(&password); err != nil {
200+ return err
201+ }
202+ cfg.Password = &password
203+ case "password-cmd":
204+ var cmdName string
205+ if err := d.ParseParams(&cmdName); err != nil {
206+ return err
207+ }
208+
209+ cmd := exec.Command(cmdName, d.Params[1:]...)
210+ var stdout []byte
211+ if stdout, err = cmd.Output(); err != nil {
212+ return fmt.Errorf("error running password command: %s", err)
213+ }
214+
215+ password := strings.TrimSuffix(string(stdout), "\n")
216+ cfg.Password = &password
217+ case "channel":
218+ // TODO: does this work with soju.im/bouncer-networks extension?
219+ cfg.Channels = append(cfg.Channels, d.Params...)
220+ case "highlight":
221+ cfg.Highlights = append(cfg.Highlights, d.Params...)
222+ case "on-highlight-path":
223+ if err := d.ParseParams(&cfg.OnHighlightPath); err != nil {
224+ return err
225+ }
226+ case "pane-widths":
227+ for _, child := range d.Children {
228+ switch child.Name {
229+ case "nicknames":
230+ var nicknames string
231+ if err := child.ParseParams(&nicknames); err != nil {
232+ return err
233+ }
234+
235+ if cfg.NickColWidth, err = strconv.Atoi(nicknames); err != nil {
236+ return err
237+ }
238+ case "channels":
239+ var channels string
240+ if err := child.ParseParams(&channels); err != nil {
241+ return err
242+ }
243+
244+ if cfg.ChanColWidth, err = strconv.Atoi(channels); err != nil {
245+ return err
246+ }
247+ case "members":
248+ var members string
249+ if err := child.ParseParams(&members); err != nil {
250+ return err
251+ }
252+
253+ if cfg.MemberColWidth, err = strconv.Atoi(members); err != nil {
254+ return err
255+ }
256+ default:
257+ return fmt.Errorf("unknown directive %q", child.Name)
258+ }
259+ }
260+ case "tls":
261+ var tls string
262+ if err := d.ParseParams(&tls); err != nil {
263+ return err
264+ }
265+
266+ if cfg.TLS, err = strconv.ParseBool(tls); err != nil {
267+ return err
268+ }
269+ case "typings":
270+ var typings string
271+ if err := d.ParseParams(&typings); err != nil {
272+ return err
273+ }
274+
275+ if cfg.Typings, err = strconv.ParseBool(typings); err != nil {
276+ return err
277+ }
278+ case "mouse":
279+ var mouse string
280+ if err := d.ParseParams(&mouse); err != nil {
281+ return err
282+ }
283+
284+ if cfg.Mouse, err = strconv.ParseBool(mouse); err != nil {
285+ return err
286+ }
287+ case "colors":
288+ for _, child := range d.Children {
289+ switch child.Name {
290+ case "prompt":
291+ var prompt string
292+ if err := child.ParseParams(&prompt); err != nil {
293+ return err
294+ }
295+
296+ fmt.Println(prompt)
297+ if err = parseColor(prompt, &cfg.Colors.Prompt); err != nil {
298+ return err
299+ }
300+ default:
301+ return fmt.Errorf("unknown directive %q", child.Name)
302+ }
303+ }
304+ case "debug":
305+ var debug string
306+ if err := d.ParseParams(&debug); err != nil {
307+ return err
308+ }
309+
310+ if cfg.Debug, err = strconv.ParseBool(debug); err != nil {
311+ return err
312+ }
313+ default:
314+ return fmt.Errorf("unknown directive %q", d.Name)
315+ }
316 }
317
318 return
+1,
-1
1@@ -31,7 +31,7 @@ extensions, such as:
2 senpai needs a configuration file to start. It searches for it in the following
3 location:
4
5- $XDG_CONFIG_HOME/senpai/senpai.yaml
6+ $XDG_CONFIG_HOME/senpai/senpai.scfg
7
8 If unset, $XDG_CONFIG_HOME defaults to *~/.config*.
9
+96,
-61
1@@ -6,34 +6,35 @@ senpai - Configuration file format and settings
2
3 # DESCRIPTION
4
5-A senpai configuration file is a YAML file.
6+A senpai configuration file is a scfg file (see https://git.sr.ht/~emersion/scfg).
7+The config file has one directive per line.
8
9 Some settings are required, the others are optional.
10
11 # SETTINGS
12
13-*addr* (required)
14+*address* (required)
15 The address (_host[:port]_) of the IRC server. senpai uses TLS connections
16 by default unless you specify *no-tls* option. TLS connections default to
17 port 6697, plain-text use port 6667.
18
19-*nick* (required)
20+*nickname* (required)
21 Your nickname, sent with a _NICK_ IRC message. It mustn't contain spaces or
22 colons (*:*).
23
24-*real*
25+*realname*
26 Your real name, or actually just a field that will be available to others
27 and may contain spaces and colons. Sent with the _USER_ IRC message. By
28 default, the value of *nick* is used.
29
30-*user*
31+*username*
32 Your username, sent with the _USER_ IRC message and also used for SASL
33 authentication. By default, the value of *nick* is used.
34
35 *password*
36 Your password, used for SASL authentication. See also *password-cmd*.
37
38-*password-cmd*
39+*password-cmd* command [arguments...]
40 Alternatively to providing your SASL authentication password directly in
41 plaintext, you can specify a command to be run to fetch the password at
42 runtime. This is useful if you store your passwords in a separate (probably
43@@ -41,18 +42,31 @@ Some settings are required, the others are optional.
44 _pass_ or _gopass_. If a *password-cmd* is provided, the value of *password*
45 will be ignored and the output of *password-cmd* will be used for login.
46
47-*channels*
48- A list of channel names that senpai will automatically join at startup and
49- server reconnect.
50+*channel*
51+ A spaced separated list of channel names that senpai will automatically join
52+ at startup and server reconnect. This directive can be specified multiple times.
53
54-*highlights*
55- A list of keywords that will trigger a notification and a display indicator
56- when said by others. By default, senpai will use your current nickname.
57+*highlight*
58+ A space separated list of keywords that will trigger a notification and a
59+ display indicator when said by others. This directive can be specified
60+ multiple times.
61
62-*on-highlight*
63- A command to be executed via _sh_ when you are highlighted. The following
64- environment variables are set with repect to the highlight, THEY MUST APPEAR
65- QUOTED IN THE SETTING, OR YOU WILL BE OPEN TO SHELL INJECTION ATTACKS.
66+ By default, senpai will use your current nickname.
67+
68+*on-highlight-path*
69+ Alternative path to a shell script to be executed when you are highlighted. By default,
70+ senpai looks for a highlight shell script at $XDG_CONFIG_HOME/senpai/highlight.
71+ If no file is found at that path, and an alternate path is not provided,
72+ highlight command execution is disabled.
73+
74+ If unset, $XDG_CONFIG_HOME defaults to *~/.config/*.
75+
76+ Before the highlight script is executed, the following environment
77+ variables are populated:
78+
79+ Shell scripts MUST ENSURE VARIABLES appear QUOTED in the script file,
80+ OR YOU WILL BE OPEN TO SHELL INJECTION ATTACKS. Shell scripts must also
81+ ensure characters like '\*' and '?' are not expanded.
82
83 [[ *Environment variable*
84 :< *Description*
85@@ -72,65 +86,80 @@ Some settings are required, the others are optional.
86 To get around this, you can double the backslash with the following snippet:
87
88 ```
89-on-highlight: |
90- escape() {
91- printf "%s" "$1" | sed 's#\\#\\\\#g'
92- }
93- notify-send "[$BUFFER] $SENDER" "$(escape "$MESSAGE")"
94+#!/bin/sh
95+escape() {
96+ printf "%s" "$1" | sed 's#\\#\\\\#g'
97+}
98+
99+notify-send "[$BUFFER] $SENDER" "$(escape "$MESSAGE")"
100 ```
101
102-*nick-column-width*
103- The number of cells that the column for nicknames occupies in the timeline.
104- By default, 16.
105+*pane-widths* { ... }
106+ Configure the width of various UI panes.
107
108-*chan-column-width*
109- Make the channel list vertical, with a width equals to the given amount of
110- cells. By default, the channel list is horizontal.
111+ Pane widths are set as sub-directives of the main *pane-widths* directive:
112+
113+```
114+pane-widths {
115+ nicknames 16
116+}
117+```
118
119-*member-column-width*
120- Show the list of channel members on the right of the screen, with a width
121- equals to the given amount of cells.
122+ This directive supports the following sub-directives:
123
124-*no-tls*
125- Disable TLS encryption. Defaults to false.
126+ *nicknames*
127+ The number of cells that the column for nicknames occupies in the timeline.
128+ By default, 16.
129
130-*no-typings*
131- Prevent senpai from sending typing notifications which let others know when
132- you are typing a message. Defaults to false.
133+ *channels*
134+ Make the channel list vertical, with a width equals to the given amount of
135+ cells. By default, the channel list is horizontal.
136+
137+ *members*
138+ Show the list of channel members on the right of the screen, with a width
139+ equals to the given amount of cells.
140+
141+*tls*
142+ Enable TLS encryption. Defaults to true.
143+
144+*typings*
145+ Send typing notifications which let others know when you are typing a message.
146+ Defaults to true.
147
148 *mouse*
149 Enable or disable mouse support. Defaults to true.
150
151-*colors*
152+*colors* { ... }
153 Settings for colors of different UI elements.
154
155 Colors are represented as numbers from 0 to 255 for 256 default terminal
156 colors respectively. -1 has special meaning of default terminal color. To
157 use true colors, *#*_rrggbb_ notation is supported.
158
159- Colors are set as sub-options of the main *colors* option:
160+ Colors are set as sub-directives of the main *colors* directive:
161
162 ```
163-colors:
164- prompt: 3 # green
165+colors {
166+ prompt 3 # green
167+}
168 ```
169
170-[[ *Sub-option*
171+[[ *Sub-directive*
172 :< *Description*
173 | prompt
174 : color for ">"-prompt that appears in command mode
175
176 *debug*
177 Dump all sent and received data to the home buffer, useful for debugging.
178- By default, false.
179+ Defaults to false.
180
181 # EXAMPLES
182
183 A minimal configuration file to connect to Libera.Chat as "Guest123456":
184
185 ```
186-addr: irc.libera.chat
187-nick: Guest123456
188+address irc.libera.chat
189+nickname Guest123456
190 ```
191
192 A more advanced configuration file that enables SASL authentication, fetches the
193@@ -140,24 +169,30 @@ notifications on highlight and decreases the width of the nick column to 12
194 need to know if the terminal emulator that runs senpai has focus):
195
196 ```
197-addr: irc.libera.chat
198-nick: Guest123456
199-user: senpai
200-real: Guest von Lenon
201-password-cmd: "gopass show irc/guest" # use your favorite CLI password solution here
202-channels: ["#rahxephon"]
203-highlights:
204- - guest
205- - senpai
206-on-highlight: |
207- escape() {
208- printf "%s" "$1" | sed 's#\\#\\\\#g'
209- }
210- FOCUS=$(swaymsg -t get_tree | jq '..|objects|select(.focused==true)|.name' | grep senpai | wc -l)
211- if [ "$HERE" -eq 0 ] || [ $FOCUS -eq 0 ]; then
212- notify-send "[$BUFFER] $SENDER" "$(escape "$MESSAGE")"
213- fi
214-nick-column-width: 12
215+address irc.libera.chat
216+nickname Guest123456
217+username senpai
218+realname "Guest von Lenon"
219+password-cmd gopass show irc/guest # use your favorite CLI password solution here
220+channel "#rahxephon"
221+highlight guest senpai
222+highlight lenon # don't know why you'd split it into multiple lines, but you can if you want
223+pane-widths {
224+ nicknames 12
225+}
226+```
227+
228+And the highlight file (*~/.config/senpai/highlight*):
229+```
230+#!/bin/sh
231+
232+escape() {
233+ printf "%s" "$1" | sed 's#\\#\\\\#g'
234+}
235+FOCUS=$(swaymsg -t get_tree | jq '..|objects|select(.focused==true)|.name' | grep senpai | wc -l)
236+if [ "$HERE" -eq 0 ] || [ $FOCUS -eq 0 ]; then
237+ notify-send "[$BUFFER] $SENDER" "$(escape "$MESSAGE")"
238+fi
239 ```
240
241 # SEE ALSO
M
go.mod
+1,
-1
1@@ -3,11 +3,11 @@ module git.sr.ht/~taiite/senpai
2 go 1.16
3
4 require (
5+ git.sr.ht/~emersion/go-scfg v0.0.0-20201019143924-142a8aa629fc
6 github.com/gdamore/tcell/v2 v2.3.11
7 github.com/mattn/go-runewidth v0.0.10
8 golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf
9 golang.org/x/time v0.0.0-20210611083556-38a9dc6acbc6
10- gopkg.in/yaml.v2 v2.3.0
11 mvdan.cc/xurls/v2 v2.3.0
12 )
13
M
go.sum
+6,
-6
1@@ -1,11 +1,15 @@
2+git.sr.ht/~emersion/go-scfg v0.0.0-20201019143924-142a8aa629fc h1:51BD67xFX+bozd3ZRuOUfalrhx4/nQSh6A9lI08rYOk=
3+git.sr.ht/~emersion/go-scfg v0.0.0-20201019143924-142a8aa629fc/go.mod h1:t+Ww6SR24yYnXzEWiNlOY0AFo5E9B73X++10lrSpp4U=
4+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
5+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
6 github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=
7 github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
8+github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
9+github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
10 github.com/hhirtz/tcell/v2 v2.3.12-0.20210807133752-5d743c3ab0c9 h1:YE0ZsDHfDGR0MeB6YLSGW8tjoxOXZKX3XbB0ytGDX4M=
11 github.com/hhirtz/tcell/v2 v2.3.12-0.20210807133752-5d743c3ab0c9/go.mod h1:cTTuF84Dlj/RqmaCIV5p4w8uG1zWdk0SF6oBpwHp4fU=
12-github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
13 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
14 github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
15-github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
16 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
17 github.com/lucasb-eyer/go-colorful v1.0.3 h1:QIbQXiugsb+q10B+MI+7DI1oQLdmnep86tWFlaaUAac=
18 github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
19@@ -23,11 +27,7 @@ golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
20 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
21 golang.org/x/time v0.0.0-20210611083556-38a9dc6acbc6 h1:Vv0JUPWTyeqUq42B2WJ1FeIDjjvGKoA2Ss+Ts0lAVbs=
22 golang.org/x/time v0.0.0-20210611083556-38a9dc6acbc6/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
23-gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
24-gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
25 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
26 gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
27-gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
28-gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
29 mvdan.cc/xurls/v2 v2.3.0 h1:59Olnbt67UKpxF1EwVBopJvkSUBmgtb468E4GVWIZ1I=
30 mvdan.cc/xurls/v2 v2.3.0/go.mod h1:AjuTy7gEiUArFMjgBBDU4SMxlfUYsRokpJQgNWOt3e4=