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
|
#!/usr/bin/env yaegi
package main
import (
"bufio"
"crypto/tls"
"fmt"
"io/ioutil"
"net/url"
"os"
"strconv"
"strings"
)
func main() {
stdinReader := bufio.NewReader(os.Stdin)
var u string // URL
links := make([]string, 0, 100)
history := make([]string, 0, 100)
for {
fmt.Print("> ")
cmd, _ := stdinReader.ReadString('\n')
cmd = strings.TrimSpace(cmd)
// Command dispatch
switch strings.ToLower(cmd) {
case "": // Nothing
continue
case "q": // Quit
fmt.Println("Bye!")
os.Exit(0)
case "b": // Back
if len(history) < 2 {
fmt.Println("No history yet!")
continue
}
u = history[len(history)-2]
history = history[0 : len(history)-2]
default:
index, err := strconv.Atoi(cmd)
if err != nil {
// Treat this as a URL
u = cmd
if !strings.HasPrefix(u, "gemini://") {
u = "gemini://" + u
}
} else {
// Treat this as a menu lookup
u = links[index-1]
}
}
// Parse URL
parsed, err := url.Parse(u)
if err != nil {
fmt.Println("Error parsing URL!")
continue
}
// Connect to server
conn, err := tls.Dial("tcp", parsed.Host+":1965", &tls.Config{InsecureSkipVerify: true})
if err != nil {
fmt.Println("Failed to connect: " + err.Error())
continue
}
defer conn.Close()
// Send request
conn.Write([]byte(u + "\r\n"))
// Receive and parse response header
reader := bufio.NewReader(conn)
responseHeader, err := reader.ReadString('\n')
parts := strings.Fields(responseHeader)
status, err := strconv.Atoi(parts[0][0:1])
meta := parts[1]
// Switch on status code
switch status {
case 1, 3, 6:
// No input, redirects or client certs
fmt.Println("Unsupported feature!")
case 2:
// Successful transaction
// text/* content only
if !strings.HasPrefix(meta, "text/") {
fmt.Println("Unsupported type " + meta)
continue
}
// Read everything
bodyBytes, err := ioutil.ReadAll(reader)
if err != nil {
fmt.Println("Error reading body")
continue
}
body := string(bodyBytes)
if meta == "text/gemini" {
// Handle Gemini map
links = make([]string, 0, 100)
preformatted := false
for _, line := range strings.Split(body, "\n") {
if strings.HasPrefix(line, "```") {
preformatted = !preformatted
} else if preformatted {
fmt.Println(line)
} else if strings.HasPrefix(line, "=>") {
line = line[2:]
bits := strings.Fields(line)
parsedLink, err := url.Parse(bits[0])
if err != nil {
continue
}
link := parsed.ResolveReference(parsedLink).String()
var label string
if len(bits) == 1 {
label = link
} else {
label = strings.Join(bits[1:], " ")
}
links = append(links, link)
fmt.Printf("[%d] %s\n", len(links), label)
} else {
// This should really be wrapped, but there's
// no easy support for this in Go's standard
// library
fmt.Println(line)
}
}
} else {
// Just print any other kind of text
fmt.Print(body)
}
history = append(history, u)
case 4, 5:
fmt.Println("ERROR: " + meta)
}
}
}
|