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") }