diff options
| author | tjp <tjp@ctrl-c.club> | 2024-01-03 08:29:40 -0700 |
|---|---|---|
| committer | tjp <tjp@ctrl-c.club> | 2024-01-03 08:29:40 -0700 |
| commit | 6c9558c0d2201d933b1d396febeb6e70ceaad058 (patch) | |
| tree | 565b8b048fc3e63b6c2eb6892a6f356f0c3c15aa /handlers.go | |
| parent | 4b3dd896fb0c157e0d727f39ccb6d940a75d1cce (diff) | |
working basic navigation and marks
Diffstat (limited to 'handlers.go')
| -rw-r--r-- | handlers.go | 201 |
1 files changed, 201 insertions, 0 deletions
diff --git a/handlers.go b/handlers.go new file mode 100644 index 0000000..587af68 --- /dev/null +++ b/handlers.go @@ -0,0 +1,201 @@ +package main + +import ( + "bytes" + "fmt" + "mime" + "net/url" + "strings" + + "tildegit.org/tjp/sliderule" + "tildegit.org/tjp/sliderule/gemini" + "tildegit.org/tjp/sliderule/gemini/gemtext" + "tildegit.org/tjp/sliderule/gopher" + "tildegit.org/tjp/sliderule/gopher/gophermap" +) + +func docType(u *url.URL, response *sliderule.Response) string { + _, gopherType := gopherURL(u) + switch gopherType { + case gopher.MenuType: + return "text/x-gophermap" + case gopher.TextFileType, gopher.ErrorType, gopher.InfoMessageType: + return "text/plain" + case gopher.MacBinHexType, + gopher.DosBinType, + gopher.BinaryFileType, + gopher.ImageFileType, + gopher.MovieFileType, + gopher.SoundFileType, + gopher.DocumentType: + return "application/octet-stream" + case gopher.UuencodedType: + return "text/x-uuencode" + case gopher.GifFileType: + return "image/gif" + case gopher.BitmapType: + return "image/bmp" + case gopher.PngImageFileType: + return "image/png" + case gopher.HTMLType: + return "text/html" + case gopher.RtfDocumentType: + return "application/rtf" + case gopher.WavSoundFileType: + return "audio/wav" + case gopher.PdfDocumentType: + return "application/pdf" + case gopher.XmlDocumentType: + return "application/xml" + } + + if u.Scheme == "gemini" { + if response.Status == gemini.StatusSuccess { + mtype, _, err := mime.ParseMediaType(response.Meta.(string)) + if err == nil { + return mtype + } + } + } + + return "text/plain" +} + +func parseDoc(doctype string, body []byte, conf *Config) (string, []Link, error) { + switch doctype { + case "text/x-gophermap": + return parseGophermapDoc(body, conf.SoftWrap) + case "text/gemini": + return parseGemtextDoc(body, conf.SoftWrap) + } + + return string(body), nil, nil +} + +func parseGophermapDoc(body []byte, softWrap int) (string, []Link, error) { + var b strings.Builder + var l []Link + mapdoc, err := gophermap.Parse(bytes.NewBuffer(body)) + if err != nil { + return "", nil, err + } + + i := 0 + for _, item := range mapdoc { + switch item.Type { + case gopher.InfoMessageType: + for _, line := range fold(item.Display, softWrap) { + if _, err := b.WriteString(" " + line + "\n"); err != nil { + return "", nil, err + } + } + default: + l = append(l, Link{ + Text: item.Display, + Target: fmtGopherURL(item.Type, item.Selector, item.Hostname, item.Port), + }) + if _, err := b.WriteString(fmt.Sprintf("[%d]%s %s\n", i, linkSpaces(i), item.Display)); err != nil { + return "", nil, err + } + i += 1 + } + } + + return b.String(), l, nil +} + +func parseGemtextDoc(body []byte, softWrap int) (string, []Link, error) { + var b strings.Builder + var l []Link + gemdoc, err := gemtext.Parse(bytes.NewBuffer(body)) + if err != nil { + return "", nil, err + } + + i := 0 + for _, item := range gemdoc { + switch item.Type() { + case gemtext.LineTypeLink: + ll := item.(gemtext.LinkLine) + u, err := url.Parse(ll.URL()) + if err != nil { + return "", nil, err + } + l = append(l, Link{ + Text: ll.Label(), + Target: u, + }) + if _, err := b.WriteString(fmt.Sprintf("[%d]%s %s\n", i, linkSpaces(i), ll.Label())); err != nil { + return "", nil, err + } + i += 1 + default: + for _, line := range fold(item.String(), softWrap) { + if _, err := b.WriteString(" " + line + "\n"); err != nil { + return "", nil, err + } + } + } + } + + return b.String(), l, nil +} + +func fold(line string, width int) []string { + rs := []rune(strings.TrimSuffix(line, "\n")) + if len(rs) == 0 { + return []string{""} + } + + var b []string +outer: + for len(rs) > 0 { + if len(rs) <= width { + b = append(b, string(rs)) + break + } + + w := width + for rs[w] != ' ' && w > 0 { + w -= 1 + } + if w == 0 { + for i := width + 1; i < len(rs); i += 1 { + if rs[i] == ' ' { + b = append(b, string(rs[:i])) + rs = rs[i+1:] + continue outer + } + } + b = append(b, string(rs)) + break outer + } + + b = append(b, string(rs[:w])) + rs = rs[w+1:] + } + + return b +} + +func fmtGopherURL(itemtype sliderule.Status, selector, hostname, port string) *url.URL { + if port != "70" { + hostname += ":" + port + } + return &url.URL{ + Scheme: "gopher", + Host: hostname, + Path: "/" + string(byte(itemtype)) + selector, + } +} + +func linkSpaces(i int) string { + switch { + case i < 10: + return " " + case i < 100: + return " " + default: + return "" + } +} |
