diff --git a/README.md b/README.md index e090c8a..7bebcce 100644 --- a/README.md +++ b/README.md @@ -93,4 +93,5 @@ The following options can be set in `/etc/molly.conf`: all intermediate directories must exist, Molly Brown won't create them for you. * `DirectorySort`: A string specifying how to sort files in automatically generated directory listings. Must be one of "Name", "Size" or "Time" (default value "Name"). -* `DirectoryReverse`: Boolean, if true automatically generated directory listings will list files in descending order of whatever `DirectorySort` is set to (default value false). +* `DirectoryReverse` (boolean): if true, automatically generated directory listings will list files in descending order of whatever `DirectorySort` is set to (default value false). +* `DirectoryTitles` (boolean): if true, automatically generated directory listings will use the first top-level heading (i.e. line beginning with "# ") in files with an extension of `GeminiExt` instead of the filename (default value false). diff --git a/config.go b/config.go index 49f7dfe..99d2efe 100644 --- a/config.go +++ b/config.go @@ -21,6 +21,7 @@ type Config struct { SCGIPaths map[string]string DirectorySort string DirectoryReverse bool + DirectoryTitles bool } type MollyFile struct { diff --git a/handler.go b/handler.go index f0886ec..7c07ff0 100644 --- a/handler.go +++ b/handler.go @@ -312,12 +312,12 @@ func generateDirectoryListing(URL *url.URL, path string, config Config) string { if uint64(file.Mode().Perm())&0444 != 0444 { continue } - listing += fmt.Sprintf("=> %s %s\n", url.PathEscape(file.Name()), generatePrettyFileLabel(file)) + listing += fmt.Sprintf("=> %s %s\n", url.PathEscape(file.Name()), generatePrettyFileLabel(file, path, config)) } return listing } -func generatePrettyFileLabel(info os.FileInfo) string { +func generatePrettyFileLabel(info os.FileInfo, path string, config Config) string { var size string if info.IsDir() { size = " " @@ -335,11 +335,12 @@ func generatePrettyFileLabel(info os.FileInfo) string { size = "GIGANTIC" } - var name string - if len(info.Name()) > 40 { + name := info.Name() + if config.DirectoryTitles && filepath.Ext(name) == "."+config.GeminiExt { + name = readHeading(path, info) + } + if len(name) > 40 { name = info.Name()[:36] + "..." - } else { - name = info.Name() } if info.IsDir() { name += "/" @@ -347,6 +348,24 @@ func generatePrettyFileLabel(info os.FileInfo) string { return fmt.Sprintf("%-40s %s %v", name, size, info.ModTime().Format("Jan _2 2006")) } +func readHeading(path string, info os.FileInfo) string { + filePath := filepath.Join(path, info.Name()) + file, err := os.Open(filePath) + if err != nil { + return info.Name() + } + defer file.Close() + + scanner := bufio.NewScanner(file) + for scanner.Scan() { + line := scanner.Text() + if strings.HasPrefix(line, "# ") { + return strings.TrimSpace(line[1:]) + } + } + return info.Name() +} + func serveFile(path string, log *LogEntry, conn net.Conn, config Config) { // Get MIME type of files ext := filepath.Ext(path)