summaryrefslogtreecommitdiff
path: root/gopher/gophermap/listdir.go
diff options
context:
space:
mode:
Diffstat (limited to 'gopher/gophermap/listdir.go')
-rw-r--r--gopher/gophermap/listdir.go154
1 files changed, 154 insertions, 0 deletions
diff --git a/gopher/gophermap/listdir.go b/gopher/gophermap/listdir.go
new file mode 100644
index 0000000..8d66277
--- /dev/null
+++ b/gopher/gophermap/listdir.go
@@ -0,0 +1,154 @@
+package gophermap
+
+import (
+ "bufio"
+ "net/url"
+ "os"
+ "path"
+ "path/filepath"
+ "strings"
+
+ "tildegit.org/tjp/sliderule/gopher"
+ "tildegit.org/tjp/sliderule/internal/types"
+)
+
+// ListDir builds a gopher menu representing the contents of a directory.
+func ListDir(dir string, location *url.URL) (gopher.MapDocument, error) {
+ return listDir(dir, location, nil, nil)
+}
+
+func listDir(dir string, location *url.URL, hidden map[string]struct{}, extensions map[string]types.Status) (gopher.MapDocument, error) {
+ contents, err := os.ReadDir(dir)
+ if err != nil {
+ return nil, err
+ }
+
+ doc := gopher.MapDocument{}
+
+ for _, entry := range contents {
+ name := entry.Name()
+
+ if _, ok := hidden[name]; ok || name == "gophermap" {
+ continue
+ }
+
+ var code types.Status
+
+ ext := strings.TrimPrefix(filepath.Ext(name), ".")
+ if entry.IsDir() {
+ code = gopher.MenuType
+ } else if c, ok := extensions[ext]; ok && ext != "" {
+ code = c
+ } else {
+ switch ext {
+ case "gophermap":
+ code = gopher.MenuType
+ case "exe", "bin", "out":
+ code = gopher.BinaryFileType
+ case "gif":
+ code = gopher.GifFileType
+ case "jpg", "jpeg", "tif", "tiff":
+ code = gopher.ImageFileType
+ case "bmp":
+ code = gopher.BitmapType
+ case "mp4", "mov", "avi", "wmv", "webm":
+ code = gopher.MovieFileType
+ case "pcm", "aiff", "mp3", "aac", "ogg", "wma", "flac", "alac":
+ code = gopher.SoundFileType
+ case "doc", "docx", "odt", "fodt":
+ code = gopher.DocumentType
+ case "html":
+ code = gopher.HTMLType
+ case "png":
+ code = gopher.PngImageFileType
+ case "rtf":
+ code = gopher.RtfDocumentType
+ case "wav":
+ code = gopher.WavSoundFileType
+ case "pdf":
+ code = gopher.PdfDocumentType
+ case "xml", "atom":
+ code = gopher.XmlDocumentType
+ default:
+ code = gopher.TextFileType
+ }
+ }
+
+ doc = append(doc, gopher.MapItem{
+ Type: code,
+ Display: displayName(dir, entry),
+ Selector: path.Join(path.Dir(location.Path), name),
+ Hostname: location.Hostname(),
+ Port: location.Port(),
+ })
+ }
+
+ return doc, nil
+}
+
+func displayName(dir string, entry os.DirEntry) string {
+ fname := entry.Name()
+
+ // if is a gophermap, use !title or filename
+ if strings.HasSuffix(fname, ".gophermap") {
+ if title := gophermapTitle(dir, fname); title != "" {
+ return title
+ }
+ return fname
+ }
+
+ if entry.IsDir() {
+ if tag := tagTitle(dir, fname); tag != "" {
+ return tag
+ }
+
+ if title := gophermapTitle(dir, filepath.Join(fname, "gophermap")); title != "" {
+ return title
+ }
+ }
+
+ return fname
+}
+
+func gophermapTitle(dir, name string) string {
+ file, err := os.Open(filepath.Join(dir, name))
+ if err != nil {
+ return ""
+ }
+ defer func() {
+ _ = file.Close()
+ }()
+
+ rdr := bufio.NewReader(file)
+ line, err := rdr.ReadString('\n')
+ if err != nil {
+ return ""
+ }
+
+ if !strings.HasPrefix(line, "!") {
+ return ""
+ }
+ return strings.TrimRight(line[1:], "\r\n")
+}
+
+func tagTitle(parent, name string) string {
+ file, err := os.Open(filepath.Join(parent, name, "gophertag"))
+ if err != nil {
+ return ""
+ }
+ defer func() {
+ _ = file.Close()
+ }()
+
+ stat, err := file.Stat()
+ if err != nil || stat.IsDir() {
+ return ""
+ }
+
+ rdr := bufio.NewReader(file)
+ line, err := rdr.ReadString('\n')
+ if err != nil {
+ return ""
+ }
+ return strings.TrimRight(line, "\r\n")
+}