diff --git a/actions.go b/actions.go index fa861fd..c91e479 100644 --- a/actions.go +++ b/actions.go @@ -2,6 +2,7 @@ package main import ( "bytes" + "context" "crypto/tls" "errors" "fmt" @@ -14,6 +15,7 @@ import ( "strconv" "strings" "syscall" + "time" "tildegit.org/tjp/sliderule" "tildegit.org/tjp/sliderule/gemini" @@ -72,22 +74,25 @@ It was written by TJP and released to the public domain. } func Navigate(state *BrowserState, target *url.URL, navIndex int, conf *Config) error { - hist := state.History - - if hist.Url == nil || target.String() != hist.Url.String() { - state.History = &History{ - Url: target, - Depth: hist.Depth + 1, - Back: hist, - NavIndex: navIndex, - } - hist.Forward = state.History + if state.Url == nil || target.String() != state.Url.String() { + pushHistory(state, target, navIndex) } state.Modal = nil return Reload(state, conf) } +func pushHistory(state *BrowserState, target *url.URL, navIndex int) { + hist := state.History + state.History = &History{ + Url: target, + Depth: hist.Depth + 1, + Back: hist, + NavIndex: navIndex, + } + hist.Forward = state.History +} + func gopherURL(u *url.URL) (string, sliderule.Status) { if u.Scheme != "gopher" || len(u.Path) < 2 || !strings.HasPrefix(u.Path, "/") { return u.String(), 0 @@ -173,6 +178,8 @@ outer: default: return fmt.Errorf("gemini response %s: %s", gemini.StatusName(response.Status), response.Meta.(string)) } + } else { + break } } @@ -190,10 +197,22 @@ outer: return HandleResource(state, conf) } +func requestCtx(timeout time.Duration) (context.Context, context.CancelFunc) { + ctx := context.Background() + if timeout > 0 { + return context.WithTimeout(ctx, timeout) + } + return ctx, func() {} +} + func fetch(state *BrowserState, u string, tlsConf *tls.Config) (*sliderule.Response, error) { + ctx, cancel := requestCtx(state.Timeout) + defer cancel() + tlsConf.ClientSessionCache = nil - response, err := sliderule.NewClient(tlsConf).Fetch(u) + response, err := sliderule.NewClient(tlsConf).Fetch(ctx, u) var tofuErr *TOFUViolation + if errors.As(err, &tofuErr) { writeError(err.Error()) state.Readline.SetPrompt("Trust new certificate instead (y/n)? [n] ") @@ -210,7 +229,9 @@ func fetch(state *BrowserState, u string, tlsConf *tls.Config) (*sliderule.Respo return nil, err } - return sliderule.NewClient(tlsConf).Fetch(u) + ctx, cancel = requestCtx(state.Timeout) + defer cancel() + return sliderule.NewClient(tlsConf).Fetch(ctx, u) } else if err != nil { return nil, err } @@ -218,8 +239,11 @@ func fetch(state *BrowserState, u string, tlsConf *tls.Config) (*sliderule.Respo } func upload(state *BrowserState, u string, body io.Reader, tlsConf *tls.Config) (*sliderule.Response, error) { + ctx, cancel := requestCtx(state.Timeout) + defer cancel() + tlsConf.ClientSessionCache = nil - response, err := sliderule.NewClient(tlsConf).Upload(u, body) + response, err := sliderule.NewClient(tlsConf).Upload(ctx, u, body) var tofuErr *TOFUViolation if errors.As(err, &tofuErr) { writeError(err.Error()) @@ -237,7 +261,9 @@ func upload(state *BrowserState, u string, body io.Reader, tlsConf *tls.Config) return nil, err } - return sliderule.NewClient(tlsConf).Upload(u, body) + ctx, cancel = requestCtx(state.Timeout) + defer cancel() + return sliderule.NewClient(tlsConf).Upload(ctx, u, body) } else if err != nil { return nil, err } @@ -306,16 +332,16 @@ func back(state *BrowserState) error { return nil } -func Back(state *BrowserState, num int) error { +func Back(state *BrowserState, conf *Config, num int) error { for i := 0; i < num; i += 1 { if err := back(state); err != nil { return err } } - return print(state) + return HandleResource(state, conf) } -func Forward(state *BrowserState, num int) error { +func Forward(state *BrowserState, conf *Config, num int) error { for i := 0; i < num; i += 1 { if state.Forward == nil { return ErrNoNextHistory @@ -324,7 +350,7 @@ func Forward(state *BrowserState, num int) error { } state.Modal = nil - return print(state) + return HandleResource(state, conf) } func Next(state *BrowserState, conf *Config) error { diff --git a/command.go b/command.go index d994866..db0c6a2 100644 --- a/command.go +++ b/command.go @@ -383,13 +383,13 @@ func RunCommand(conf *Config, cmd *Command, state *BrowserState) error { if len(cmd.Args) == 1 { num, _ = strconv.Atoi(cmd.Args[0]) } - return Back(state, num) + return Back(state, conf, num) case "forward": num := 1 if len(cmd.Args) == 1 { num, _ = strconv.Atoi(cmd.Args[0]) } - return Forward(state, num) + return Forward(state, conf, num) case "next": return Next(state, conf) case "previous": diff --git a/files.go b/files.go index 052c4fb..4964750 100644 --- a/files.go +++ b/files.go @@ -11,17 +11,19 @@ import ( "path/filepath" "strings" "syscall" + "time" "github.com/BurntSushi/toml" ) type ConfigMain struct { - DefaultScheme string `toml:"default_scheme"` - SoftWrap int `toml:"soft_wrap"` - DownloadFolder string `toml:"download_folder"` - VimKeys bool `toml:"vim_keys"` - Quiet bool `toml:"quiet"` - Pager string `toml:"pager"` + DefaultScheme string `toml:"default_scheme"` + SoftWrap int `toml:"soft_wrap"` + DownloadFolder string `toml:"download_folder"` + VimKeys bool `toml:"vim_keys"` + Quiet bool `toml:"quiet"` + Pager string `toml:"pager"` + Timeout duration `toml:"duration"` } type Config struct { @@ -30,6 +32,14 @@ type Config struct { Handlers map[string]string `toml:"handlers"` } +type duration struct{ time.Duration } + +func (d *duration) UnmarshalText(text []byte) error { + var err error + d.Duration, err = time.ParseDuration(string(text)) + return err +} + func getConfig() (*Config, error) { home := os.Getenv("HOME") path := os.Getenv("XDG_CONFIG_HOME") @@ -50,6 +60,9 @@ func getConfig() (*Config, error) { DownloadFolder: home, Quiet: false, Pager: "auto", + Timeout: duration{ + time.Duration(10 * time.Second), + }, }, Handlers: map[string]string{}, } diff --git a/go.mod b/go.mod index c7c1452..5da2fb6 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.21.0 require ( github.com/BurntSushi/toml v1.3.2 github.com/chzyer/readline v1.5.1 - tildegit.org/tjp/sliderule v1.6.2-0.20240110181009-de1490808fa6 + tildegit.org/tjp/sliderule v1.6.2-0.20240115025310-751f423f11bf ) require ( diff --git a/go.sum b/go.sum index 248ba96..8b7c35b 100644 --- a/go.sum +++ b/go.sum @@ -20,5 +20,5 @@ golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5 h1:y/woIyUBFbpQGKS0u1aHF/40W golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -tildegit.org/tjp/sliderule v1.6.2-0.20240110181009-de1490808fa6 h1:nVGF/L3NI+dqmlEdagFFjZZabPMPOocr1zVW1qBfGVg= -tildegit.org/tjp/sliderule v1.6.2-0.20240110181009-de1490808fa6/go.mod h1:opdo8E25iS9X9pNismM8U7pCH8XO0PdRIIhdADn8Uik= +tildegit.org/tjp/sliderule v1.6.2-0.20240115025310-751f423f11bf h1:p0MqM4m/LcgLjRH24OOV+oNOu/8+alABAdI6kLantvE= +tildegit.org/tjp/sliderule v1.6.2-0.20240115025310-751f423f11bf/go.mod h1:opdo8E25iS9X9pNismM8U7pCH8XO0PdRIIhdADn8Uik= diff --git a/help.go b/help.go index ba45cf0..40af4ca 100644 --- a/help.go +++ b/help.go @@ -64,7 +64,8 @@ Consult "help COMMAND" for more information on any single command. `[1:], "cli": ` -x-1 [-c COMMANDS] [URL] +x-1 -h +x-1 [-q] [-c COMMANDS] [URL] ----------------------- With no arguments or flags, x-1 will just display the prompt and begin executing your commands. Use the "help" command to begin exploring this @@ -75,6 +76,9 @@ provided by separating them with a semi-colon ';') and then exit. In this mode it also forces quiet mode, in which it doesn't automatically print loaded pages. +The -q flag will force quiet mode, overriding "quiet" in the +configuration file. + With a URL argument, it will begin an interactive prompt session by loading the requested url. `[1:], @@ -122,6 +126,9 @@ The section "[main]" contains general configuration options: will pipe every page printed through less(1), "never" will not, and "auto" will pipe it through "less -F", which skips the pager when the output fits on a single screen anyway. + * timeout (string): Maximum time to wait trying to make a connection + to the host. Should be in the form with a unit suffix, like "15s" or + "500ms". The default is "10s". `[1:], diff --git a/main.go b/main.go index 7ad9b4a..eb85922 100644 --- a/main.go +++ b/main.go @@ -13,6 +13,7 @@ import ( var cmdMode = flag.String("c", "", "") var helpMode = flag.Bool("h", false, "") +var quietMode = flag.Bool("q", false, "") func main() { conf, err := getConfig() @@ -62,6 +63,10 @@ func main() { return } + if *quietMode { + state.Quiet = true + } + rl, err := readline.New(Prompt) if err != nil { log.Fatal(err) diff --git a/state.go b/state.go index a967b7b..c46862f 100644 --- a/state.go +++ b/state.go @@ -2,6 +2,7 @@ package main import ( "net/url" + "time" "github.com/chzyer/readline" ) @@ -19,8 +20,9 @@ type BrowserState struct { DefaultTour Tour CurrentTour *Tour - Quiet bool - Pager string + Quiet bool + Pager string + Timeout time.Duration Readline *readline.Instance } @@ -58,8 +60,9 @@ func NewBrowserState(conf *Config) *BrowserState { NavIndex: -1, }, - Quiet: conf.Quiet, - Pager: conf.Pager, + Quiet: conf.Quiet, + Pager: conf.Pager, + Timeout: conf.Timeout.Duration, } state.CurrentTour = &state.DefaultTour return state