handle input types

* spartan prompts
* gemini [sensitive]input responses
* gopher search servers
This commit is contained in:
tjp 2024-01-04 12:47:06 -07:00
parent 1c96ee6e4c
commit 684539c70d
4 changed files with 141 additions and 11 deletions

View File

@ -14,6 +14,8 @@ import (
"syscall"
"tildegit.org/tjp/sliderule"
"tildegit.org/tjp/sliderule/gemini"
"tildegit.org/tjp/sliderule/gopher"
)
var client sliderule.Client
@ -103,10 +105,66 @@ func Reload(state *BrowserState, conf *Config) error {
return ErrMustBeOnAPage
}
urlStr, _ := gopherURL(state.Url)
response, err := client.Fetch(urlStr)
if err != nil {
return err
urlStr, itemType := gopherURL(state.Url)
if itemType == gopher.SearchType && state.Url.RawQuery == "" {
state.Readline.SetPrompt("query: ")
line, err := state.Readline.Readline()
if err != nil {
return err
}
state.Url.RawQuery = url.QueryEscape(strings.TrimRight(line, "\n"))
urlStr, _ = gopherURL(state.Url)
}
var response *sliderule.Response
var err error
if state.Url.Scheme == "spartan" && state.Url.Fragment == "prompt" {
input, err := externalMessage()
if err != nil {
return err
}
body := io.LimitReader(bytes.NewBuffer(input), int64(len(input)))
state.Url.Fragment = ""
response, err = client.Upload(state.Url.String(), body)
state.Url.Fragment = "prompt"
if err != nil {
return err
}
} else {
response, err = client.Fetch(urlStr)
if err != nil {
return err
}
}
if state.Url.Scheme == "gemini" {
switch response.Status {
case gemini.StatusInput:
state.Readline.SetPrompt("input: ")
line, err := state.Readline.Readline()
if err != nil {
return err
}
state.Url.RawQuery = url.QueryEscape(strings.TrimRight(line, "\n"))
response, err = client.Fetch(state.Url.String())
if err != nil {
return err
}
case gemini.StatusSensitiveInput:
line, err := state.Readline.ReadPassword("password: ")
if err != nil {
return err
}
state.Url.RawQuery = url.QueryEscape(strings.TrimRight(string(line), "\n"))
response, err = client.Fetch(state.Url.String())
if err != nil {
return err
}
}
}
state.DocType = docType(state.Url, response)
@ -123,6 +181,59 @@ func Reload(state *BrowserState, conf *Config) error {
return print(state)
}
func externalMessage() ([]byte, error) {
tmpf, err := os.CreateTemp("", "*")
if err != nil {
return nil, err
}
defer func() { _ = os.Remove(tmpf.Name()) }()
prompt := []byte("# enter input below (this line will be ignored)\n")
err = (func() error {
defer func() { _ = tmpf.Close() }()
if _, err := tmpf.Write(prompt); err != nil {
return err
}
return nil
}())
if err != nil {
return nil, err
}
editor := os.Getenv("EDITOR")
if editor == "" {
editor = "vi"
}
editor, err = exec.LookPath(editor)
if err != nil {
return nil, err
}
cmd := exec.Command(editor, tmpf.Name())
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
return nil, err
}
tmpf, err = os.Open(tmpf.Name())
if err != nil {
return nil, err
}
defer func() { _ = tmpf.Close() }()
buf, err := io.ReadAll(tmpf)
if err != nil {
return nil, err
}
return bytes.TrimPrefix(buf, prompt), nil
}
func back(state *BrowserState) error {
if state.Back == nil {
return ErrNoPreviousHistory
@ -287,6 +398,9 @@ func parseURL(str string, state *BrowserState, defaultScheme string) (*url.URL,
return nil, -1, ErrInvalidNumericLink
}
u = state.Links[i].Target
if state.Links[i].Prompt {
u.Fragment = "prompt"
}
} else {
i = -1
u, err = url.Parse(str)

View File

@ -12,12 +12,13 @@ import (
"tildegit.org/tjp/sliderule/gemini/gemtext"
"tildegit.org/tjp/sliderule/gopher"
"tildegit.org/tjp/sliderule/gopher/gophermap"
"tildegit.org/tjp/sliderule/spartan"
)
func docType(u *url.URL, response *sliderule.Response) string {
_, gopherType := gopherURL(u)
switch gopherType {
case gopher.MenuType:
case gopher.MenuType, gopher.SearchType:
return "text/x-gophermap"
case gopher.TextFileType, gopher.ErrorType, gopher.InfoMessageType:
return "text/plain"
@ -49,18 +50,26 @@ func docType(u *url.URL, response *sliderule.Response) string {
return "application/xml"
}
if u.Scheme == "gemini" {
if response.Status == gemini.StatusSuccess {
mtype, _, err := mime.ParseMediaType(response.Meta.(string))
if err == nil {
return mtype
}
if metaIsMediaType(u, response) {
mtype, _, err := mime.ParseMediaType(response.Meta.(string))
if err == nil {
return mtype
}
}
return "text/plain"
}
func metaIsMediaType(u *url.URL, response *sliderule.Response) bool {
if u.Scheme == "gemini" && response.Status == gemini.StatusSuccess {
return true
}
if u.Scheme == "spartan" && response.Status == spartan.StatusSuccess {
return true
}
return false
}
func parseDoc(doctype string, body []byte, conf *Config) (string, []Link, error) {
switch doctype {
case "text/x-gophermap":
@ -114,7 +123,11 @@ func parseGemtextDoc(body []byte, softWrap int) (string, []Link, error) {
i := 0
for _, item := range gemdoc {
isPrompt := false
switch item.Type() {
case gemtext.LineTypePrompt:
isPrompt = true
fallthrough
case gemtext.LineTypeLink:
ll := item.(gemtext.LinkLine)
u, err := url.Parse(ll.URL())
@ -124,6 +137,7 @@ func parseGemtextDoc(body []byte, softWrap int) (string, []Link, error) {
l = append(l, Link{
Text: ll.Label(),
Target: u,
Prompt: isPrompt,
})
label := ll.Label()
if len(label) == 0 {

View File

@ -42,6 +42,7 @@ func main() {
state.Readline = rl
for {
rl.SetPrompt(Prompt)
line, err := rl.Readline()
if err == io.EOF {
break

View File

@ -45,6 +45,7 @@ type History struct {
type Link struct {
Text string
Target *url.URL
Prompt bool
}
func NewBrowserState() *BrowserState {