Files
gemtext2gophermap/main.go
T

141 lines
3.5 KiB
Go

package main
import(
"fmt"
"os"
"io"
"strings"
"strconv"
p "github.com/mcecode/gemtext-parser"
)
type State struct {
out io.Writer
maxLineLength int
insertionMap []string
insertionIndex int
isInCodeBlock bool
wasPreviousLink bool
isWebOnly bool
}
func main() {
if len(os.Args) < 3 {
fmt.Printf("Usage: %s input.gmi output_gophermap [max line length (default 70)] [preprocessor insertion map file]\n", os.Args[0])
return
}
var asl p.ASL
var err error
if os.Args[1] != "-" {
asl, err = p.ParseFile(os.Args[1])
if err != nil {
fmt.Fprintln(os.Stderr, err)
return
}
} else {
stdin, err := io.ReadAll(os.Stdin)
if err != nil {
fmt.Fprintln(os.Stderr, err)
return
}
asl = p.Parse(string(stdin))
}
var outputWriter io.Writer
if os.Args[2] != "-" {
file, err := os.Create(os.Args[2])
if err != nil {
fmt.Fprintln(os.Stderr, err)
return
}
defer file.Close()
outputWriter = file
} else {
outputWriter = os.Stdout
}
preprocessorMap := []string{}
if len(os.Args) >= 5 {
data, err := os.ReadFile(os.Args[4])
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
preprocessorMap = strings.Split(string(data), "<!--break-->")
}
s := State{outputWriter, 70, preprocessorMap, 0, false, false, false}
if len(os.Args) >= 4 {
maxLineLength, err := strconv.Atoi(os.Args[3])
if err != nil {
fmt.Fprintln(os.Stderr, err)
return
}
s.maxLineLength = maxLineLength
}
asl.Visit(s.handleElement)
}
var gopherTypeCharParams = GopherTypeCharParams{
GophermapFiles: []string{"gophermap"},
GophermapExtensions: []string{},
PromptFileExtensions: []string{},
}
func (s *State) handleElement(e p.Element) {
if strings.HasPrefix(e.Text(), "<!--web-only-->") {
s.isWebOnly = !s.isWebOnly
return
}
if s.isWebOnly { return }
if strings.HasPrefix(e.Text(), "<!--insert-gopher-->") {
s.out.Write([]byte(s.insertionMap[s.insertionIndex]))
s.insertionIndex += 1
return
}
switch (e.Type()) {
case p.ElementTypes.Text():
if e.Text() == "" { return }
if !s.isInCodeBlock { s.out.Write([]byte("\n")) }
s.out.Write([]byte(JoinGophermapLines("i", WrapLines(e.Text(), s.maxLineLength, " "), "\t/FAKE\tNULL\t0")))
s.wasPreviousLink = false
case p.ElementTypes.Link():
if !s.wasPreviousLink {
s.out.Write([]byte("\n"))
}
isUrl := strings.Contains(e.URL(), "://")
linkPrefix := ""
if isUrl {
linkPrefix = "URL:"
}
typeChar := gopherTypeCharParams.GopherTypeChar(e.URL(), strings.HasSuffix(e.URL(), "/"))
s.out.Write([]byte(typeChar + e.Text() + "\t" + linkPrefix + e.URL()))
s.wasPreviousLink = true
case p.ElementTypes.Heading():
s.out.Write([]byte("\n"))
if e.Level() == p.HeadingLevels.Heading() || e.Level() == p.HeadingLevels.SubHeading() {
s.out.Write([]byte("\n\n"))
}
if e.Level() == p.HeadingLevels.SubSubHeading() {
s.out.Write([]byte("\n"))
}
s.out.Write([]byte(JoinGophermapLines("i", WrapLines(e.Text(), s.maxLineLength, " "), "\t/FAKE\tNULL\t0")))
s.wasPreviousLink = false
case p.ElementTypes.List():
s.out.Write([]byte(JoinGophermapLines("i- ", WrapLines(e.Text(), s.maxLineLength - 2, " "), "\t/FAKE\tNULL\t0")))
s.wasPreviousLink = false
case p.ElementTypes.Quote():
s.out.Write([]byte(JoinGophermapLines("i> ", WrapLines(e.Text(), s.maxLineLength - 2, " "), "\t/FAKE\tNULL\t0")))
s.wasPreviousLink = false
case p.ElementTypes.PreformatToggle():
s.isInCodeBlock = !s.isInCodeBlock
s.wasPreviousLink = false
}
if e.Text() != "" {
s.out.Write([]byte("\n"))
}
}