2019-09-17 10:20:59 +00:00
|
|
|
/*
|
|
|
|
gfu - gophermap format utility
|
|
|
|
|
|
|
|
`gfu` manipulates gophermaps (gopher menus). It is intended to be used as part of an automation chain for managing a gopherhole using maps as the main doctype, easily allowing any document to contain links.
|
|
|
|
|
|
|
|
`gfu` can:
|
|
|
|
- Convert all lines in a file that are not valid gopher links into gopher info text (item type 'i') lines
|
|
|
|
- Deconstructing all gopher info text lines in a file back to plain text for easy editing
|
|
|
|
|
|
|
|
There are also plans to include additional features, such as:
|
|
|
|
- Adding the contents of a header file into the gophermap
|
|
|
|
- Adding the contents of a footer file into the gophermap
|
|
|
|
*Please note - Many servers already support includes, so the above may not be needed. If you are interested in this feature, you may want to check your server documentation first.*
|
|
|
|
*
|
|
|
|
|
|
|
|
Documentation
|
|
|
|
|
|
|
|
For information on using `gfu`, see the help information:
|
|
|
|
gfu --help
|
|
|
|
|
|
|
|
Information on building, downloading and installing `gfu` can be found at:
|
|
|
|
https://tildegit.org/sloum/gfu
|
|
|
|
|
|
|
|
Further information on gfu can be found at the `gfu` homepage:
|
|
|
|
https://rawtext.club/~sloum/gfu.html
|
|
|
|
*/
|
2019-06-24 04:52:46 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2019-09-16 01:03:17 +00:00
|
|
|
"bufio"
|
2019-06-24 04:52:46 +00:00
|
|
|
"bytes"
|
2019-09-16 01:03:17 +00:00
|
|
|
"flag"
|
2019-06-24 04:52:46 +00:00
|
|
|
"fmt"
|
2019-09-18 11:38:48 +00:00
|
|
|
"io"
|
2019-09-16 01:03:17 +00:00
|
|
|
"os"
|
2019-06-24 04:52:46 +00:00
|
|
|
"regexp"
|
2019-09-16 01:03:17 +00:00
|
|
|
"strings"
|
2019-06-24 04:52:46 +00:00
|
|
|
)
|
|
|
|
|
2019-09-18 11:38:48 +00:00
|
|
|
var re = regexp.MustCompile(`.+\t.*\t.*\t.*`)
|
2019-06-24 04:52:46 +00:00
|
|
|
|
|
|
|
func errorExit(e error, msg string) {
|
|
|
|
if e != nil {
|
|
|
|
fmt.Print(msg)
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-18 11:38:48 +00:00
|
|
|
func buildInfoText(ln string) string {
|
2019-06-24 04:52:46 +00:00
|
|
|
var out strings.Builder
|
|
|
|
out.Grow(20 + len(ln))
|
|
|
|
out.WriteString("i")
|
2019-09-18 11:38:48 +00:00
|
|
|
out.WriteString(ln)
|
2019-06-24 04:52:46 +00:00
|
|
|
out.WriteString("\tfalse\tnull.host\t1")
|
2019-09-18 11:38:48 +00:00
|
|
|
out.WriteString("\n")
|
2019-06-24 04:52:46 +00:00
|
|
|
return out.String()
|
|
|
|
}
|
|
|
|
|
2019-09-18 11:38:48 +00:00
|
|
|
func deconstructInfoText(ln string) string {
|
2019-06-24 04:52:46 +00:00
|
|
|
text := strings.SplitN(ln, "\t", 2)
|
2019-09-18 11:38:48 +00:00
|
|
|
infotext := text[0]
|
|
|
|
if len(infotext) > 1 {
|
|
|
|
return infotext[1:] + "\n"
|
2019-06-24 04:52:46 +00:00
|
|
|
}
|
|
|
|
return "\n"
|
|
|
|
}
|
|
|
|
|
2019-09-18 11:38:48 +00:00
|
|
|
func readFile(path string, build bool) bytes.Buffer {
|
2019-06-24 04:52:46 +00:00
|
|
|
file, err := os.Open(path)
|
2019-09-16 01:03:17 +00:00
|
|
|
errorExit(err, fmt.Sprintf("Unable to open file for reading: %s\n", path))
|
2019-06-24 04:52:46 +00:00
|
|
|
defer file.Close()
|
2019-09-18 11:38:48 +00:00
|
|
|
return processFile(file, build)
|
|
|
|
}
|
2019-06-24 04:52:46 +00:00
|
|
|
|
2019-09-18 11:38:48 +00:00
|
|
|
func processFile(file io.Reader, build bool) bytes.Buffer {
|
|
|
|
scanner := bufio.NewScanner(file)
|
|
|
|
var outFile bytes.Buffer
|
2019-06-24 04:52:46 +00:00
|
|
|
|
2019-09-18 11:38:48 +00:00
|
|
|
if build {
|
|
|
|
for scanner.Scan() {
|
|
|
|
l := scanner.Text()
|
2019-06-24 04:52:46 +00:00
|
|
|
if exp := re.MatchString(l); exp {
|
|
|
|
outFile.WriteString(l)
|
2019-09-18 11:38:48 +00:00
|
|
|
outFile.WriteString("\n")
|
2019-06-24 04:52:46 +00:00
|
|
|
} else {
|
2019-09-18 11:38:48 +00:00
|
|
|
outFile.WriteString(buildInfoText(l))
|
2019-06-24 04:52:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2019-09-18 11:38:48 +00:00
|
|
|
for scanner.Scan() {
|
|
|
|
l := scanner.Text()
|
2019-06-24 04:52:46 +00:00
|
|
|
if exp := re.MatchString(l); exp && l[0] == 'i' {
|
2019-09-18 11:38:48 +00:00
|
|
|
outFile.WriteString(deconstructInfoText(l))
|
2019-06-24 04:52:46 +00:00
|
|
|
} else {
|
|
|
|
outFile.WriteString(l)
|
2019-09-18 11:38:48 +00:00
|
|
|
outFile.WriteString("\n")
|
2019-06-24 04:52:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-09-18 11:38:48 +00:00
|
|
|
if err := scanner.Err(); err != nil {
|
|
|
|
errorExit(err, fmt.Sprintf("Error while reading file\n"))
|
|
|
|
}
|
|
|
|
return outFile
|
2019-06-24 04:52:46 +00:00
|
|
|
}
|
|
|
|
|
2019-09-18 11:38:48 +00:00
|
|
|
func writeFile(path string, outFile bytes.Buffer) {
|
2019-09-16 01:03:17 +00:00
|
|
|
file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0)
|
|
|
|
errorExit(err, fmt.Sprintf("Unable to open file for writing: %s\n", path))
|
2019-06-24 04:52:46 +00:00
|
|
|
defer file.Close()
|
|
|
|
file.Write(outFile.Bytes())
|
|
|
|
}
|
|
|
|
|
|
|
|
func PrintHelp() {
|
|
|
|
art := `gfu - gophermap formatting utility
|
|
|
|
|
|
|
|
syntax: gfu [flags...] [filepath]
|
|
|
|
|
|
|
|
example: gfu -d ~/gopher/phlog/gophermap
|
|
|
|
|
2019-09-17 10:20:59 +00:00
|
|
|
default
|
|
|
|
Convert plain text lines to gophermap info text (item type 'i') lines
|
2019-06-24 04:52:46 +00:00
|
|
|
`
|
|
|
|
fmt.Fprint(os.Stderr, art)
|
|
|
|
flag.PrintDefaults()
|
|
|
|
}
|
|
|
|
|
|
|
|
func main() {
|
2019-09-18 11:38:48 +00:00
|
|
|
//command-line flags and responses
|
|
|
|
deconstructInfoTextLines := flag.Bool("d", false, "Deconstruct a gophermap's info text lines back to plain text")
|
2019-09-16 01:03:17 +00:00
|
|
|
header := flag.String("head", "", "Path to a file containing header content")
|
|
|
|
footer := flag.String("foot", "", "Path to a file containing footer content")
|
2019-09-17 10:20:59 +00:00
|
|
|
stdout := flag.Bool("stdout", false, "Instead of writing changes to a file, return them to stdout")
|
2019-06-24 04:52:46 +00:00
|
|
|
flag.Usage = PrintHelp
|
|
|
|
|
|
|
|
flag.Parse()
|
|
|
|
args := flag.Args()
|
2019-09-16 01:03:17 +00:00
|
|
|
|
2019-06-24 04:52:46 +00:00
|
|
|
if l := len(args); l != 1 {
|
2019-09-16 01:03:17 +00:00
|
|
|
fmt.Printf("Incorrect number of arguments. Expected 1, got %d\n", l)
|
2019-06-24 04:52:46 +00:00
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
|
2019-09-18 11:38:48 +00:00
|
|
|
//main program
|
2019-06-24 04:52:46 +00:00
|
|
|
if *header != "" {
|
|
|
|
fmt.Println("Header functionality is not built yet, proceeding with general gophermap conversion...")
|
|
|
|
}
|
|
|
|
|
|
|
|
if *footer != "" {
|
|
|
|
fmt.Println("Footer functionality is not built yet, proceeding with general gophermap conversion...")
|
|
|
|
}
|
|
|
|
|
2019-09-18 11:38:48 +00:00
|
|
|
outFile := readFile(args[0], !*deconstructInfoTextLines)
|
2019-09-17 10:20:59 +00:00
|
|
|
|
|
|
|
if *stdout {
|
|
|
|
fmt.Print(outFile.String())
|
|
|
|
} else {
|
2019-09-18 11:38:48 +00:00
|
|
|
writeFile(args[0], outFile)
|
2019-09-17 10:20:59 +00:00
|
|
|
}
|
2019-06-24 04:52:46 +00:00
|
|
|
os.Exit(0)
|
|
|
|
}
|