Merge branch 'run-format' of sloum/bombadillo into develop

This commit is contained in:
Sloom Sloum Sluom IV 2019-11-11 21:42:29 -05:00 committed by Gitea
commit c36ded496f
18 changed files with 167 additions and 172 deletions

View File

@ -11,19 +11,23 @@ import (
// + + + T Y P E S + + + \\ // + + + T Y P E S + + + \\
//--------------------------------------------------\\ //--------------------------------------------------\\
// Bookmarks represents the contents of the bookmarks
// bar, as well as its visibility, focus, and scroll
// state.
type Bookmarks struct { type Bookmarks struct {
IsOpen bool IsOpen bool
IsFocused bool IsFocused bool
Position int Position int
Length int Length int
Titles []string Titles []string
Links []string Links []string
} }
//------------------------------------------------\\ //------------------------------------------------\\
// + + + R E C E I V E R S + + + \\ // + + + R E C E I V E R S + + + \\
//--------------------------------------------------\\ //--------------------------------------------------\\
// Add a bookmark to the bookmarks struct
func (b *Bookmarks) Add(v []string) (string, error) { func (b *Bookmarks) Add(v []string) (string, error) {
if len(v) < 2 { if len(v) < 2 {
return "", fmt.Errorf("Received %d arguments, expected 2+", len(v)) return "", fmt.Errorf("Received %d arguments, expected 2+", len(v))
@ -34,6 +38,7 @@ func (b *Bookmarks) Add(v []string) (string, error) {
return "Bookmark added successfully", nil return "Bookmark added successfully", nil
} }
// Delete a bookmark from the bookmarks struct
func (b *Bookmarks) Delete(i int) (string, error) { func (b *Bookmarks) Delete(i int) (string, error) {
if i < len(b.Titles) && len(b.Titles) == len(b.Links) { if i < len(b.Titles) && len(b.Titles) == len(b.Links) {
b.Titles = append(b.Titles[:i], b.Titles[i+1:]...) b.Titles = append(b.Titles[:i], b.Titles[i+1:]...)
@ -44,6 +49,7 @@ func (b *Bookmarks) Delete(i int) (string, error) {
return "", fmt.Errorf("Bookmark %d does not exist", i) return "", fmt.Errorf("Bookmark %d does not exist", i)
} }
// ToggleOpen toggles visibility state of the bookmarks bar
func (b *Bookmarks) ToggleOpen() { func (b *Bookmarks) ToggleOpen() {
b.IsOpen = !b.IsOpen b.IsOpen = !b.IsOpen
if b.IsOpen { if b.IsOpen {
@ -53,12 +59,15 @@ func (b *Bookmarks) ToggleOpen() {
} }
} }
// ToggleFocused toggles the focal state of the bookmarks bar
func (b *Bookmarks) ToggleFocused() { func (b *Bookmarks) ToggleFocused() {
if b.IsOpen { if b.IsOpen {
b.IsFocused = !b.IsFocused b.IsFocused = !b.IsFocused
} }
} }
// IniDump returns a string representing the current bookmarks
// in the format that .bombadillo.ini uses
func (b Bookmarks) IniDump() string { func (b Bookmarks) IniDump() string {
if len(b.Titles) < 1 { if len(b.Titles) < 1 {
return "" return ""
@ -73,7 +82,7 @@ func (b Bookmarks) IniDump() string {
return out return out
} }
// Get a list, including link nums, of bookmarks // List returns a list, including link nums, of bookmarks
// as a string slice // as a string slice
func (b Bookmarks) List() []string { func (b Bookmarks) List() []string {
var out []string var out []string
@ -83,13 +92,15 @@ func (b Bookmarks) List() []string {
return out return out
} }
// Render returns a string slice with the contents of each
// visual row of the bookmark bar.
func (b Bookmarks) Render(termwidth, termheight int) []string { func (b Bookmarks) Render(termwidth, termheight int) []string {
width := 40 width := 40
termheight -= 3 termheight -= 3
var walll, wallr, floor, ceil, tr, tl, br, bl string var walll, wallr, floor, ceil, tr, tl, br, bl string
if termwidth < 40 { if termwidth < 40 {
width = termwidth width = termwidth
} }
if b.IsFocused { if b.IsFocused {
walll = cui.Shapes["awalll"] walll = cui.Shapes["awalll"]
wallr = cui.Shapes["awallr"] wallr = cui.Shapes["awallr"]
@ -115,11 +126,11 @@ func (b Bookmarks) Render(termwidth, termheight int) []string {
top := fmt.Sprintf("%s%s%s", tl, strings.Repeat(ceil, contentWidth), tr) top := fmt.Sprintf("%s%s%s", tl, strings.Repeat(ceil, contentWidth), tr)
out = append(out, top) out = append(out, top)
marks := b.List() marks := b.List()
for i := 0; i < termheight - 2; i++ { for i := 0; i < termheight-2; i++ {
if i + b.Position >= len(b.Titles) { if i+b.Position >= len(b.Titles) {
out = append(out, fmt.Sprintf("%s%-*.*s%s", walll, contentWidth, contentWidth, "", wallr)) out = append(out, fmt.Sprintf("%s%-*.*s%s", walll, contentWidth, contentWidth, "", wallr))
} else { } else {
out = append(out, fmt.Sprintf("%s%-*.*s%s", walll, contentWidth, contentWidth, marks[i + b.Position], wallr)) out = append(out, fmt.Sprintf("%s%-*.*s%s", walll, contentWidth, contentWidth, marks[i+b.Position], wallr))
} }
} }
@ -128,16 +139,11 @@ func (b Bookmarks) Render(termwidth, termheight int) []string {
return out return out
} }
// TODO handle scrolling of the bookmarks list
// either here with a scroll up/down or in the client
// code for scroll
//------------------------------------------------\\ //------------------------------------------------\\
// + + + F U N C T I O N S + + + \\ // + + + F U N C T I O N S + + + \\
//--------------------------------------------------\\ //--------------------------------------------------\\
// MakeBookmarks creates a Bookmark struct with default values
func MakeBookmarks() Bookmarks { func MakeBookmarks() Bookmarks {
return Bookmarks{false, false, 0, 0, make([]string, 0), make([]string, 0)} return Bookmarks{false, false, 0, 0, make([]string, 0), make([]string, 0)}
} }

View File

@ -51,7 +51,7 @@ func (c *client) GetSizeOnce() {
os.Exit(5) os.Exit(5)
} }
var h, w int var h, w int
fmt.Sscan(string(out), &h, &w) _, _ = fmt.Sscan(string(out), &h, &w)
c.Height = h c.Height = h
c.Width = w c.Width = w
} }
@ -70,7 +70,7 @@ func (c *client) GetSize() {
os.Exit(5) os.Exit(5)
} }
var h, w int var h, w int
fmt.Sscan(string(out), &h, &w) _, _ = fmt.Sscan(string(out), &h, &w)
if h != c.Height || w != c.Width { if h != c.Height || w != c.Width {
c.Height = h c.Height = h
c.Width = w c.Width = w
@ -268,7 +268,7 @@ func (c *client) routeCommandInput(com *cmdparse.Command) error {
case cmdparse.DOLINKAS: case cmdparse.DOLINKAS:
c.doLinkCommandAs(com.Action, com.Target, com.Value) c.doLinkCommandAs(com.Action, com.Target, com.Value)
default: default:
return fmt.Errorf("Unknown command entry!") return fmt.Errorf("Unknown command entry")
} }
return err return err
@ -385,10 +385,9 @@ func (c *client) doCommandAs(action string, values []string) {
c.SetMessage(err.Error(), true) c.SetMessage(err.Error(), true)
c.DrawMessage() c.DrawMessage()
return return
} else {
c.SetMessage(msg, false)
c.DrawMessage()
} }
c.SetMessage(msg, false)
c.DrawMessage()
err = saveConfig() err = saveConfig()
if err != nil { if err != nil {
@ -437,7 +436,7 @@ func (c *client) doLinkCommandAs(action, target string, values []string) {
return return
} }
num -= 1 num--
links := c.PageState.History[c.PageState.Position].Links links := c.PageState.History[c.PageState.Position].Links
if num >= len(links) || num < 0 { if num >= len(links) || num < 0 {
@ -456,10 +455,9 @@ func (c *client) doLinkCommandAs(action, target string, values []string) {
c.SetMessage(err.Error(), true) c.SetMessage(err.Error(), true)
c.DrawMessage() c.DrawMessage()
return return
} else {
c.SetMessage(msg, false)
c.DrawMessage()
} }
c.SetMessage(msg, false)
c.DrawMessage()
err = saveConfig() err = saveConfig()
if err != nil { if err != nil {
@ -564,10 +562,9 @@ func (c *client) doLinkCommand(action, target string) {
c.SetMessage(err.Error(), true) c.SetMessage(err.Error(), true)
c.DrawMessage() c.DrawMessage()
return return
} else {
c.SetMessage(msg, false)
c.DrawMessage()
} }
c.SetMessage(msg, false)
c.DrawMessage()
err = saveConfig() err = saveConfig()
if err != nil { if err != nil {
@ -585,7 +582,7 @@ func (c *client) doLinkCommand(action, target string) {
} }
c.Visit(c.BookMarks.Links[num]) c.Visit(c.BookMarks.Links[num])
case "CHECK", "C": case "CHECK", "C":
num -= 1 num--
links := c.PageState.History[c.PageState.Position].Links links := c.PageState.History[c.PageState.Position].Links
if num >= len(links) || num < 0 { if num >= len(links) || num < 0 {
@ -877,8 +874,7 @@ func (c *client) Visit(url string) {
} }
} }
// +++ Begin Protocol Handlers +++
// +++ Begin Protocol Handlers +++
func (c *client) handleGopher(u Url) { func (c *client) handleGopher(u Url) {
if u.DownloadOnly { if u.DownloadOnly {
@ -1022,15 +1018,15 @@ func (c *client) handleWeb(u Url) {
c.SetMessage("The file is non-text: writing to disk...", false) c.SetMessage("The file is non-text: writing to disk...", false)
c.DrawMessage() c.DrawMessage()
var fn string var fn string
if i := strings.LastIndex(u.Full, "/"); i > 0 && i + 1 < len(u.Full) { if i := strings.LastIndex(u.Full, "/"); i > 0 && i+1 < len(u.Full) {
fn = u.Full[i + 1:] fn = u.Full[i+1:]
} else { } else {
fn = "bombadillo.download" fn = "bombadillo.download"
} }
c.saveFile(u, fn) c.saveFile(u, fn)
} }
// Open in default web browser if available // Open in default web browser if available
} else { } else {
if strings.ToUpper(c.Options["terminalonly"]) == "TRUE" { if strings.ToUpper(c.Options["terminalonly"]) == "TRUE" {
c.SetMessage("'terminalonly' is set to true and 'lynxmode' is not enabled, cannot open web link", false) c.SetMessage("'terminalonly' is set to true and 'lynxmode' is not enabled, cannot open web link", false)
@ -1049,11 +1045,12 @@ func (c *client) handleWeb(u Url) {
} }
} }
//------------------------------------------------\\ //------------------------------------------------\\
// + + + F U N C T I O N S + + + \\ // + + + F U N C T I O N S + + + \\
//--------------------------------------------------\\ //--------------------------------------------------\\
// MakeClient returns a client struct and names the client after
// the string that is passed in
func MakeClient(name string) *client { func MakeClient(name string) *client {
c := client{0, 0, defaultOptions, "", false, MakePages(), MakeBookmarks(), MakeHeadbar(name), MakeFootbar(), gemini.MakeTofuDigest()} c := client{0, 0, defaultOptions, "", false, MakePages(), MakeBookmarks(), MakeHeadbar(name), MakeFootbar(), gemini.MakeTofuDigest()}
return &c return &c
@ -1061,12 +1058,12 @@ func MakeClient(name string) *client {
func findAvailableFileName(fpath, fname string) (string, error) { func findAvailableFileName(fpath, fname string) (string, error) {
savePath := filepath.Join(fpath, fname) savePath := filepath.Join(fpath, fname)
_, fileErr := os.Stat(savePath) _, fileErr := os.Stat(savePath)
for suffix := 1; fileErr == nil; suffix++ { for suffix := 1; fileErr == nil; suffix++ {
fn := fmt.Sprintf("%s.%d", fname, suffix) fn := fmt.Sprintf("%s.%d", fname, suffix)
savePath = filepath.Join(fpath, fn) savePath = filepath.Join(fpath, fn)
_, fileErr = os.Stat(savePath) _, fileErr = os.Stat(savePath)
if !os.IsNotExist(fileErr) && fileErr != nil { if !os.IsNotExist(fileErr) && fileErr != nil {
return savePath, fileErr return savePath, fileErr
@ -1075,4 +1072,3 @@ func findAvailableFileName(fpath, fname string) (string, error) {
return savePath, nil return savePath, nil
} }

View File

@ -68,10 +68,10 @@ func (s *scanner) scanText() Token {
capInput := strings.ToUpper(buf.String()) capInput := strings.ToUpper(buf.String())
switch capInput { switch capInput {
case "D", "DELETE", "A", "ADD","W", "WRITE", case "D", "DELETE", "A", "ADD", "W", "WRITE",
"S", "SET", "R", "RELOAD", "SEARCH", "S", "SET", "R", "RELOAD", "SEARCH",
"Q", "QUIT", "B", "BOOKMARKS", "H", "Q", "QUIT", "B", "BOOKMARKS", "H",
"HOME", "?", "HELP", "C", "CHECK", "HOME", "?", "HELP", "C", "CHECK",
"P", "PURGE": "P", "PURGE":
return Token{Action, capInput} return Token{Action, capInput}
} }

View File

@ -21,11 +21,11 @@ type Parser struct {
type Config struct { type Config struct {
// Bookmarks gopher.Bookmarks // Bookmarks gopher.Bookmarks
Bookmarks struct { Bookmarks struct {
Titles, Links []string Titles, Links []string
} }
Settings []KeyValue Settings []KeyValue
Certs []KeyValue Certs []KeyValue
} }
type KeyValue struct { type KeyValue struct {

View File

@ -26,19 +26,6 @@ var Shapes = map[string]string{
"abr": "▟", "abr": "▟",
} }
func drawShape(shape string) {
if val, ok := Shapes[shape]; ok {
fmt.Printf("%s", val)
} else {
fmt.Print("x")
}
}
func moveThenDrawShape(r, c int, s string) {
MoveCursorTo(r, c)
drawShape(s)
}
func MoveCursorTo(row, col int) { func MoveCursorTo(row, col int) {
fmt.Printf("\033[%d;%dH", row, col) fmt.Printf("\033[%d;%dH", row, col)
} }

View File

@ -1,5 +0,0 @@
package cui
import (
)

View File

@ -12,21 +12,20 @@ var defaultOptions = map[string]string{
// Edit these values before compile to have different default values // Edit these values before compile to have different default values
// ... though they can always be edited from within bombadillo as well // ... though they can always be edited from within bombadillo as well
// it just may take more time/work. // it just may take more time/work.
// //
// To change the default location for the config you can enter // To change the default location for the config you can enter
// any valid path as a string, if you want an absolute, or // any valid path as a string, if you want an absolute, or
// concatenate with the main default: `userinfo.HomeDir` like so: // concatenate with the main default: `userinfo.HomeDir` like so:
// "configlocation": userinfo.HomeDir + "/config/" // "configlocation": userinfo.HomeDir + "/config/"
"homeurl": "gopher://bombadillo.colorfield.space:70/1/user-guide.map", "homeurl": "gopher://bombadillo.colorfield.space:70/1/user-guide.map",
"savelocation": userinfo.HomeDir, "savelocation": userinfo.HomeDir,
"searchengine": "gopher://gopher.floodgap.com:70/7/v2/vs", "searchengine": "gopher://gopher.floodgap.com:70/7/v2/vs",
"openhttp": "false", "openhttp": "false",
"telnetcommand": "telnet", "telnetcommand": "telnet",
"configlocation": userinfo.HomeDir, "configlocation": userinfo.HomeDir,
"theme": "normal", // "normal", "inverted" "theme": "normal", // "normal", "inverted"
"terminalonly": "true", "terminalonly": "true",
"tlscertificate": "", "tlscertificate": "",
"tlskey": "", "tlskey": "",
"lynxmode": "false", "lynxmode": "false",
} }

View File

@ -2,8 +2,8 @@ package finger
import ( import (
"fmt" "fmt"
"net"
"io/ioutil" "io/ioutil"
"net"
"time" "time"
) )

View File

@ -5,21 +5,23 @@ import (
"strconv" "strconv"
) )
//------------------------------------------------\\ //------------------------------------------------\\
// + + + T Y P E S + + + \\ // + + + T Y P E S + + + \\
//--------------------------------------------------\\ //--------------------------------------------------\\
// Footbar deals with the values present in the
// client's footbar
type Footbar struct { type Footbar struct {
PercentRead string PercentRead string
PageType string PageType string
} }
//------------------------------------------------\\ //------------------------------------------------\\
// + + + R E C E I V E R S + + + \\ // + + + R E C E I V E R S + + + \\
//--------------------------------------------------\\ //--------------------------------------------------\\
// SetPercentRead sets the percentage of the current
// document the user has read
func (f *Footbar) SetPercentRead(p int) { func (f *Footbar) SetPercentRead(p int) {
if p > 100 { if p > 100 {
p = 100 p = 100
@ -29,25 +31,28 @@ func (f *Footbar) SetPercentRead(p int) {
f.PercentRead = strconv.Itoa(p) + "%" f.PercentRead = strconv.Itoa(p) + "%"
} }
// SetPageType sets the current page's type
// NOTE: This is not currently in use
func (f *Footbar) SetPageType(t string) { func (f *Footbar) SetPageType(t string) {
f.PageType = t f.PageType = t
} }
// Render returns a string representing the visual display
// of the bookmarks bar
func (f *Footbar) Render(termWidth, position int, theme string) string { func (f *Footbar) Render(termWidth, position int, theme string) string {
pre := fmt.Sprintf("HST: (%2.2d) - - - %4s Read ", position + 1, f.PercentRead) pre := fmt.Sprintf("HST: (%2.2d) - - - %4s Read ", position+1, f.PercentRead)
out := "\033[0m%*.*s " out := "\033[0m%*.*s "
if theme == "inverse" { if theme == "inverse" {
out = "\033[7m%*.*s \033[0m" out = "\033[7m%*.*s \033[0m"
} }
return fmt.Sprintf(out, termWidth - 1, termWidth - 1, pre) return fmt.Sprintf(out, termWidth-1, termWidth-1, pre)
} }
//------------------------------------------------\\ //------------------------------------------------\\
// + + + F U N C T I O N S + + + \\ // + + + F U N C T I O N S + + + \\
//--------------------------------------------------\\ //--------------------------------------------------\\
// MakeFootbar returns a footbar with default values
func MakeFootbar() Footbar { func MakeFootbar() Footbar {
return Footbar{"---", "N/A"} return Footbar{"---", "N/A"}
} }

View File

@ -11,22 +11,19 @@ import (
"time" "time"
) )
type Capsule struct { type Capsule struct {
MimeMaj string MimeMaj string
MimeMin string MimeMin string
Status int Status int
Content string Content string
Links []string Links []string
} }
type TofuDigest struct { type TofuDigest struct {
certs map[string]string certs map[string]string
ClientCert tls.Certificate ClientCert tls.Certificate
} }
//------------------------------------------------\\ //------------------------------------------------\\
// + + + R E C E I V E R S + + + \\ // + + + R E C E I V E R S + + + \\
//--------------------------------------------------\\ //--------------------------------------------------\\
@ -107,17 +104,17 @@ func (t *TofuDigest) newCert(host string, cState *tls.ConnectionState) error {
reasons.WriteString("; ") reasons.WriteString("; ")
} }
if now.Before(cert.NotBefore) { if now.Before(cert.NotBefore) {
reasons.WriteString(fmt.Sprintf("Cert [%d] is not valid yet", index + 1)) reasons.WriteString(fmt.Sprintf("Cert [%d] is not valid yet", index+1))
continue continue
} }
if now.After(cert.NotAfter) { if now.After(cert.NotAfter) {
reasons.WriteString(fmt.Sprintf("Cert [%d] is expired", index + 1)) reasons.WriteString(fmt.Sprintf("Cert [%d] is expired", index+1))
continue continue
} }
if err := cert.VerifyHostname(host); err != nil { if err := cert.VerifyHostname(host); err != nil {
reasons.WriteString(fmt.Sprintf("Cert [%d] hostname does not match", index + 1)) reasons.WriteString(fmt.Sprintf("Cert [%d] hostname does not match", index+1))
continue continue
} }
@ -143,8 +140,6 @@ func (t *TofuDigest) IniDump() string {
return out.String() return out.String()
} }
//------------------------------------------------\\ //------------------------------------------------\\
// + + + F U N C T I O N S + + + \\ // + + + F U N C T I O N S + + + \\
//--------------------------------------------------\\ //--------------------------------------------------\\
@ -157,7 +152,7 @@ func Retrieve(host, port, resource string, td *TofuDigest) (string, error) {
addr := host + ":" + port addr := host + ":" + port
conf := &tls.Config{ conf := &tls.Config{
MinVersion: tls.VersionTLS12, MinVersion: tls.VersionTLS12,
InsecureSkipVerify: true, InsecureSkipVerify: true,
} }
@ -183,7 +178,7 @@ func Retrieve(host, port, resource string, td *TofuDigest) (string, error) {
if td.Exists(host) { if td.Exists(host) {
// See if we have a matching cert // See if we have a matching cert
err := td.Match(host, &connState) err := td.Match(host, &connState)
if err != nil && err.Error() != "EXP" { if err != nil && err.Error() != "EXP" {
// If there is no match and it isnt because of an expiration // If there is no match and it isnt because of an expiration
// just return the error // just return the error
@ -223,21 +218,21 @@ func Fetch(host, port, resource string, td *TofuDigest) ([]byte, error) {
rawResp, err := Retrieve(host, port, resource, td) rawResp, err := Retrieve(host, port, resource, td)
if err != nil { if err != nil {
return make([]byte, 0), err return make([]byte, 0), err
} }
resp := strings.SplitN(rawResp, "\r\n", 2) resp := strings.SplitN(rawResp, "\r\n", 2)
if len(resp) != 2 { if len(resp) != 2 {
if err != nil { if err != nil {
return make([]byte, 0), fmt.Errorf("Invalid response from server") return make([]byte, 0), fmt.Errorf("Invalid response from server")
} }
} }
header := strings.SplitN(resp[0], " ", 2) header := strings.SplitN(resp[0], " ", 2)
if len([]rune(header[0])) != 2 { if len([]rune(header[0])) != 2 {
header = strings.SplitN(resp[0], "\t", 2) header = strings.SplitN(resp[0], "\t", 2)
if len([]rune(header[0])) != 2 { if len([]rune(header[0])) != 2 {
return make([]byte,0), fmt.Errorf("Invalid response format from server") return make([]byte, 0), fmt.Errorf("Invalid response format from server")
} }
} }
// Get status code single digit form // Get status code single digit form
status, err := strconv.Atoi(string(header[0][0])) status, err := strconv.Atoi(string(header[0][0]))
@ -271,24 +266,24 @@ func Visit(host, port, resource string, td *TofuDigest) (Capsule, error) {
rawResp, err := Retrieve(host, port, resource, td) rawResp, err := Retrieve(host, port, resource, td)
if err != nil { if err != nil {
return capsule, err return capsule, err
} }
resp := strings.SplitN(rawResp, "\r\n", 2) resp := strings.SplitN(rawResp, "\r\n", 2)
if len(resp) != 2 { if len(resp) != 2 {
if err != nil { if err != nil {
return capsule, fmt.Errorf("Invalid response from server") return capsule, fmt.Errorf("Invalid response from server")
} }
} }
header := strings.SplitN(resp[0], " ", 2) header := strings.SplitN(resp[0], " ", 2)
if len([]rune(header[0])) != 2 { if len([]rune(header[0])) != 2 {
header = strings.SplitN(resp[0], "\t", 2) header = strings.SplitN(resp[0], "\t", 2)
if len([]rune(header[0])) != 2 { if len([]rune(header[0])) != 2 {
return capsule, fmt.Errorf("Invalid response format from server") return capsule, fmt.Errorf("Invalid response format from server")
} }
} }
body := resp[1] body := resp[1]
// Get status code single digit form // Get status code single digit form
capsule.Status, err = strconv.Atoi(string(header[0][0])) capsule.Status, err = strconv.Atoi(string(header[0][0]))
if err != nil { if err != nil {
@ -351,7 +346,7 @@ func parseGemini(b, rootUrl, currentUrl string) (string, []string) {
subLn := strings.Trim(ln[2:], "\r\n\t \a") subLn := strings.Trim(ln[2:], "\r\n\t \a")
splitPoint := strings.IndexAny(subLn, " \t") splitPoint := strings.IndexAny(subLn, " \t")
if splitPoint < 0 || len([]rune(subLn)) - 1 <= splitPoint { if splitPoint < 0 || len([]rune(subLn))-1 <= splitPoint {
link = subLn link = subLn
decorator = subLn decorator = subLn
} else { } else {
@ -359,7 +354,7 @@ func parseGemini(b, rootUrl, currentUrl string) (string, []string) {
decorator = strings.Trim(subLn[splitPoint:], "\t\n\r \a") decorator = strings.Trim(subLn[splitPoint:], "\t\n\r \a")
} }
if strings.Index(link, "://") < 0 { if strings.Index(link, "://") < 0 {
link = handleRelativeUrl(link, rootUrl, currentUrl) link = handleRelativeUrl(link, rootUrl, currentUrl)
} }
@ -385,7 +380,7 @@ func handleRelativeUrl(u, root, current string) string {
return fmt.Sprintf("%s/%s", root, u) return fmt.Sprintf("%s/%s", root, u)
} }
current = current[:ind + 1] current = current[:ind+1]
return fmt.Sprintf("%s%s", current, u) return fmt.Sprintf("%s%s", current, u)
} }
@ -398,7 +393,6 @@ func hashCert(cert []byte) string {
return fmt.Sprintf("%s", string(bytes.Join(hex, []byte(":")))) return fmt.Sprintf("%s", string(bytes.Join(hex, []byte(":"))))
} }
func MakeCapsule() Capsule { func MakeCapsule() Capsule {
return Capsule{"", "", 0, "", make([]string, 0, 5)} return Capsule{"", "", 0, "", make([]string, 0, 5)}
} }

View File

@ -83,8 +83,8 @@ func Visit(gophertype, host, port, resource string) (string, []string, error) {
resp, err := Retrieve(host, port, resource) resp, err := Retrieve(host, port, resource)
if err != nil { if err != nil {
return "", []string{}, err return "", []string{}, err
} }
text := string(resp) text := string(resp)
links := []string{} links := []string{}
@ -92,7 +92,6 @@ func Visit(gophertype, host, port, resource string) (string, []string, error) {
return text, []string{}, nil return text, []string{}, nil
} }
if gophertype == "1" { if gophertype == "1" {
text, links = parseMap(text) text, links = parseMap(text)
} }
@ -116,7 +115,7 @@ func isWebLink(resource string) (string, bool) {
return "", false return "", false
} }
func parseMap(text string) (string, []string) { func parseMap(text string) (string, []string) {
splitContent := strings.Split(text, "\n") splitContent := strings.Split(text, "\n")
links := make([]string, 0, 10) links := make([]string, 0, 10)
@ -135,7 +134,7 @@ func parseMap(text string) (string, []string) {
} else { } else {
title = "" title = ""
} }
if len(line) > 1 && len(line[0]) > 0 && string(line[0][0]) == "i" { if len(line) > 1 && len(line[0]) > 0 && string(line[0][0]) == "i" {
splitContent[i] = " " + string(title) splitContent[i] = " " + string(title)
} else if len(line) >= 4 { } else if len(line) >= 4 {

View File

@ -8,31 +8,32 @@ import (
// + + + T Y P E S + + + \\ // + + + T Y P E S + + + \\
//--------------------------------------------------\\ //--------------------------------------------------\\
// Headbar represents the contents of the top bar of
// the client and contains the client name and the
// current URL
type Headbar struct { type Headbar struct {
title string title string
url string url string
} }
//------------------------------------------------\\ //------------------------------------------------\\
// + + + R E C E I V E R S + + + \\ // + + + R E C E I V E R S + + + \\
//--------------------------------------------------\\ //--------------------------------------------------\\
// Render returns a string with the contents of theHeadbar
func (h *Headbar) Render(width int, theme string) string { func (h *Headbar) Render(width int, theme string) string {
maxMsgWidth := width - len([]rune(h.title)) - 2 maxMsgWidth := width - len([]rune(h.title)) - 2
if theme == "inverse" { if theme == "inverse" {
return fmt.Sprintf("\033[7m%s▟\033[27m %-*.*s\033[0m", h.title, maxMsgWidth, maxMsgWidth, h.url) return fmt.Sprintf("\033[7m%s▟\033[27m %-*.*s\033[0m", h.title, maxMsgWidth, maxMsgWidth, h.url)
} else {
return fmt.Sprintf("%s▟\033[7m %-*.*s\033[0m", h.title, maxMsgWidth, maxMsgWidth, h.url)
} }
return fmt.Sprintf("%s▟\033[7m %-*.*s\033[0m", h.title, maxMsgWidth, maxMsgWidth, h.url)
} }
//------------------------------------------------\\ //------------------------------------------------\\
// + + + F U N C T I O N S + + + \\ // + + + F U N C T I O N S + + + \\
//--------------------------------------------------\\ //--------------------------------------------------\\
// MakeHeadbar returns a Headbar with default values
func MakeHeadbar(title string) Headbar { func MakeHeadbar(title string) Headbar {
return Headbar{title, ""} return Headbar{title, ""}
} }

View File

@ -44,7 +44,7 @@ func IsTextFile(url string) bool {
} }
// If we made it here, there is no content-type header. // If we made it here, there is no content-type header.
// So in the event of the unknown, lets render to the // So in the event of the unknown, lets render to the
// screen. This will allow redirects to get rendered // screen. This will allow redirects to get rendered
// as well. // as well.
return true return true
@ -89,4 +89,3 @@ func Fetch(url string) ([]byte, error) {
return bodyBytes, nil return bodyBytes, nil
} }

View File

@ -18,7 +18,6 @@ func Open(address string) (string, error) {
} }
defer file.Close() defer file.Close()
if pathIsDir(address) { if pathIsDir(address) {
fileList, err := file.Readdirnames(0) fileList, err := file.Readdirnames(0)
if err != nil { if err != nil {
@ -40,21 +39,20 @@ func Open(address string) (string, error) {
return string(bytes), nil return string(bytes), nil
} }
func pathExists(p string) bool { func pathExists(p string) bool {
exists := true exists := true
if _, err := os.Stat(p); os.IsNotExist(err) { if _, err := os.Stat(p); os.IsNotExist(err) {
exists = false exists = false
} }
return exists return exists
} }
func pathIsDir(p string) bool { func pathIsDir(p string) bool {
info, err := os.Stat(p) info, err := os.Stat(p)
if err != nil { if err != nil {
return false return false
} }
return info.IsDir() return info.IsDir()
} }

11
main.go
View File

@ -82,9 +82,8 @@ func validateOpt(opt, val string) bool {
} }
} }
return false return false
} else {
return true
} }
return true
} }
func lowerCaseOpt(opt, val string) string { func lowerCaseOpt(opt, val string) string {
@ -107,7 +106,7 @@ func loadConfig() error {
confparser := config.NewParser(file) confparser := config.NewParser(file)
settings, _ = confparser.Parse() settings, _ = confparser.Parse()
file.Close() _ = file.Close()
for _, v := range settings.Settings { for _, v := range settings.Settings {
lowerkey := strings.ToLower(v.Key) lowerkey := strings.ToLower(v.Key)
if lowerkey == "configlocation" { if lowerkey == "configlocation" {
@ -129,7 +128,7 @@ func loadConfig() error {
} }
for i, v := range settings.Bookmarks.Titles { for i, v := range settings.Bookmarks.Titles {
bombadillo.BookMarks.Add([]string{v, settings.Bookmarks.Links[i]}) _, _ = bombadillo.BookMarks.Add([]string{v, settings.Bookmarks.Links[i]})
} }
for _, v := range settings.Certs { for _, v := range settings.Certs {
@ -157,7 +156,7 @@ func handleSignals(c <-chan os.Signal) {
switch <-c { switch <-c {
case syscall.SIGTSTP: case syscall.SIGTSTP:
cui.CleanupTerm() cui.CleanupTerm()
syscall.Kill(syscall.Getpid(), syscall.SIGSTOP) _ = syscall.Kill(syscall.Getpid(), syscall.SIGSTOP)
case syscall.SIGCONT: case syscall.SIGCONT:
cui.InitTerm() cui.InitTerm()
bombadillo.Draw() bombadillo.Draw()
@ -179,7 +178,7 @@ Examples: bombadillo gopher://bombadillo.colorfield.space
Options: Options:
` `
fmt.Fprint(os.Stdout, art) _, _ = fmt.Fprint(os.Stdout, art)
flag.PrintDefaults() flag.PrintDefaults()
} }

21
page.go
View File

@ -8,11 +8,14 @@ import (
// + + + T Y P E S + + + \\ // + + + T Y P E S + + + \\
//--------------------------------------------------\\ //--------------------------------------------------\\
// Page represents a visited URL's contents; including
// the raw content, wrapped content, link slice, URL,
// and the current scroll position
type Page struct { type Page struct {
WrappedContent []string WrappedContent []string
RawContent string RawContent string
Links []string Links []string
Location Url Location Url
ScrollPosition int ScrollPosition int
} }
@ -20,9 +23,11 @@ type Page struct {
// + + + R E C E I V E R S + + + \\ // + + + R E C E I V E R S + + + \\
//--------------------------------------------------\\ //--------------------------------------------------\\
// ScrollPositionRange may not be in actual usage....
// TODO: find where this is being used
func (p *Page) ScrollPositionRange(termHeight int) (int, int) { func (p *Page) ScrollPositionRange(termHeight int) (int, int) {
termHeight -= 3 termHeight -= 3
if len(p.WrappedContent) - p.ScrollPosition < termHeight { if len(p.WrappedContent)-p.ScrollPosition < termHeight {
p.ScrollPosition = len(p.WrappedContent) - termHeight p.ScrollPosition = len(p.WrappedContent) - termHeight
} }
if p.ScrollPosition < 0 { if p.ScrollPosition < 0 {
@ -38,7 +43,7 @@ func (p *Page) ScrollPositionRange(termHeight int) (int, int) {
return p.ScrollPosition, end return p.ScrollPosition, end
} }
// Performs a hard wrap to the requested // WrapContent performs a hard wrap to the requested
// width and updates the WrappedContent // width and updates the WrappedContent
// of the Page struct width a string slice // of the Page struct width a string slice
// of the wrapped data // of the wrapped data
@ -51,7 +56,7 @@ func (p *Page) WrapContent(width int) {
content.WriteRune(ch) content.WriteRune(ch)
counter = 0 counter = 0
} else if ch == '\t' { } else if ch == '\t' {
if counter + 4 < width { if counter+4 < width {
content.WriteString(" ") content.WriteString(" ")
counter += 4 counter += 4
} else { } else {
@ -69,7 +74,7 @@ func (p *Page) WrapContent(width int) {
content.WriteRune('\n') content.WriteRune('\n')
counter = 0 counter = 0
if p.Location.Mime == "1" { if p.Location.Mime == "1" {
spacer := " " spacer := " "
content.WriteString(spacer) content.WriteString(spacer)
counter += len(spacer) counter += len(spacer)
} }
@ -85,8 +90,8 @@ func (p *Page) WrapContent(width int) {
// + + + F U N C T I O N S + + + \\ // + + + F U N C T I O N S + + + \\
//--------------------------------------------------\\ //--------------------------------------------------\\
// MakePage returns a Page struct with default values
func MakePage(url Url, content string, links []string) Page { func MakePage(url Url, content string, links []string) Page {
p := Page{make([]string, 0), content, links, url, 0} p := Page{make([]string, 0), content, links, url, 0}
return p return p
} }

View File

@ -8,22 +8,28 @@ import (
// + + + T Y P E S + + + \\ // + + + T Y P E S + + + \\
//--------------------------------------------------\\ //--------------------------------------------------\\
// Pages is a struct that represents the history of the client.
// It functions as a container for the pages (history array) and
// tracks the current history length and location.
type Pages struct { type Pages struct {
Position int Position int
Length int Length int
History [20]Page History [20]Page
} }
//------------------------------------------------\\ //------------------------------------------------\\
// + + + R E C E I V E R S + + + \\ // + + + R E C E I V E R S + + + \\
//--------------------------------------------------\\ //--------------------------------------------------\\
// NavigateHistory takes a positive or negative integer
// and updates the current history position. Checks are done
// to make sure that the position moved to is a valid history
// location. Returns an error or nil.
func (p *Pages) NavigateHistory(qty int) error { func (p *Pages) NavigateHistory(qty int) error {
newPosition := p.Position + qty newPosition := p.Position + qty
if newPosition < 0 { if newPosition < 0 {
return fmt.Errorf("You are already at the beginning of history") return fmt.Errorf("You are already at the beginning of history")
} else if newPosition > p.Length - 1 { } else if newPosition > p.Length-1 {
return fmt.Errorf("Your way is blocked by void, there is nothing forward") return fmt.Errorf("Your way is blocked by void, there is nothing forward")
} }
@ -31,23 +37,29 @@ func (p *Pages) NavigateHistory(qty int) error {
return nil return nil
} }
// Add gets passed a Page, which gets added to the history
// arrayr. Add also updates the current length and position
// of the Pages struct to which it belongs. Add also shifts
// off array items if necessary.
func (p *Pages) Add(pg Page) { func (p *Pages) Add(pg Page) {
if p.Position == p.Length - 1 && p.Length < len(p.History) { if p.Position == p.Length-1 && p.Length < len(p.History) {
p.History[p.Length] = pg p.History[p.Length] = pg
p.Length++ p.Length++
p.Position++ p.Position++
} else if p.Position == p.Length - 1 && p.Length == 20 { } else if p.Position == p.Length-1 && p.Length == 20 {
for x := 1; x < len(p.History); x++ { for x := 1; x < len(p.History); x++ {
p.History[x-1] = p.History[x] p.History[x-1] = p.History[x]
} }
p.History[len(p.History)-1] = pg p.History[len(p.History)-1] = pg
} else { } else {
p.Position += 1 p.Position++
p.Length = p.Position + 1 p.Length = p.Position + 1
p.History[p.Position] = pg p.History[p.Position] = pg
} }
} }
// Render wraps the content for the current page and returns
// the page content as a string slice
func (p *Pages) Render(termHeight, termWidth int) []string { func (p *Pages) Render(termHeight, termWidth int) []string {
if p.Length < 1 { if p.Length < 1 {
return make([]string, 0) return make([]string, 0)
@ -62,12 +74,12 @@ func (p *Pages) Render(termHeight, termWidth int) []string {
} else if prev < now { } else if prev < now {
diff := now - prev diff := now - prev
pos = pos + diff pos = pos + diff
if pos > now - termHeight { if pos > now-termHeight {
pos = now - termHeight pos = now - termHeight
} }
} }
if pos < 0 || now < termHeight - 3 { if pos < 0 || now < termHeight-3 {
pos = 0 pos = 0
} }
@ -80,8 +92,7 @@ func (p *Pages) Render(termHeight, termWidth int) []string {
// + + + F U N C T I O N S + + + \\ // + + + F U N C T I O N S + + + \\
//--------------------------------------------------\\ //--------------------------------------------------\\
// MakePages returns a Pages struct with default values
func MakePages() Pages { func MakePages() Pages {
return Pages{-1, 0, [20]Page{}} return Pages{-1, 0, [20]Page{}}
} }

23
url.go
View File

@ -12,13 +12,18 @@ import (
// + + + T Y P E S + + + \\ // + + + T Y P E S + + + \\
//--------------------------------------------------\\ //--------------------------------------------------\\
// Url is a struct representing the different pieces
// of a url. This custom struct is used rather than the
// built-in url library so-as to support gopher URLs, as
// well as track mime-type and renderability (can the
// response to the url be rendered as text in the client).
type Url struct { type Url struct {
Scheme string Scheme string
Host string Host string
Port string Port string
Resource string Resource string
Full string Full string
Mime string Mime string
DownloadOnly bool DownloadOnly bool
} }
@ -28,12 +33,10 @@ type Url struct {
// There are currently no receivers for the Url struct // There are currently no receivers for the Url struct
//------------------------------------------------\\ //------------------------------------------------\\
// + + + F U N C T I O N S + + + \\ // + + + F U N C T I O N S + + + \\
//--------------------------------------------------\\ //--------------------------------------------------\\
// MakeUrl is a Url constructor that takes in a string // MakeUrl is a Url constructor that takes in a string
// representation of a url and returns a Url struct and // representation of a url and returns a Url struct and
// an error (or nil). // an error (or nil).
@ -152,7 +155,7 @@ func parseFinger(u string) (Url, error) {
if len(userPlusAddress) > 1 { if len(userPlusAddress) > 1 {
out.Resource = userPlusAddress[0] out.Resource = userPlusAddress[0]
u = userPlusAddress[1] u = userPlusAddress[1]
} }
hostPort := strings.Split(u, ":") hostPort := strings.Split(u, ":")
if len(hostPort) < 2 { if len(hostPort) < 2 {
out.Port = "79" out.Port = "79"
@ -167,5 +170,3 @@ func parseFinger(u string) (Url, error) {
out.Full = fmt.Sprintf("%s://%s%s:%s", out.Scheme, resource, out.Host, out.Port) out.Full = fmt.Sprintf("%s://%s%s:%s", out.Scheme, resource, out.Host, out.Port)
return out, nil return out, nil
} }