Permit sorting of files in automatic directory listings by various factors.

This commit is contained in:
Solderpunk 2020-06-27 18:52:29 +02:00
parent 6da5ec79dd
commit 3f98a9edf1
3 changed files with 45 additions and 15 deletions

View File

@ -92,3 +92,5 @@ The following options can be set in `/etc/molly.conf`:
* `LogPath`: Path to log file (default value `molly.log`). Note that * `LogPath`: Path to log file (default value `molly.log`). Note that
all intermediate directories must exist, Molly Brown won't create all intermediate directories must exist, Molly Brown won't create
them for you. 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).

View File

@ -1,6 +1,7 @@
package main package main
import ( import (
"errors"
"github.com/BurntSushi/toml" "github.com/BurntSushi/toml"
) )
@ -18,6 +19,8 @@ type Config struct {
PermRedirects map[string]string PermRedirects map[string]string
CGIPaths []string CGIPaths []string
SCGIPaths map[string]string SCGIPaths map[string]string
DirectorySort string
DirectoryReverse bool
} }
type MollyFile struct { type MollyFile struct {
@ -43,6 +46,7 @@ func getConfig(filename string) (Config, error) {
config.PermRedirects = make(map[string]string) config.PermRedirects = make(map[string]string)
config.CGIPaths = make([]string, 0) config.CGIPaths = make([]string, 0)
config.SCGIPaths = make(map[string]string) config.SCGIPaths = make(map[string]string)
config.DirectorySort = "Name"
// Return defaults if no filename given // Return defaults if no filename given
if filename == "" { if filename == "" {
@ -54,5 +58,13 @@ func getConfig(filename string) (Config, error) {
if err != nil { if err != nil {
return config, err return config, err
} }
// Validate pseudo-enums
switch config.DirectorySort {
case "Name", "Size", "Time":
default:
return config, errors.New("Invalid DirectorySort value.")
}
return config, nil return config, nil
} }

View File

@ -14,6 +14,7 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"regexp" "regexp"
"sort"
"strings" "strings"
"time" "time"
) )
@ -154,7 +155,7 @@ func handleGeminiRequest(conn net.Conn, config Config, logEntries chan LogEntry)
} else { } else {
conn.Write([]byte("20 text/gemini\r\n")) conn.Write([]byte("20 text/gemini\r\n"))
log.Status = 20 log.Status = 20
conn.Write([]byte(generateDirectoryListing(URL, path))) conn.Write([]byte(generateDirectoryListing(URL, path, config)))
} }
return return
} }
@ -263,7 +264,7 @@ func parseMollyFiles(path string, info os.FileInfo, config *Config) {
} }
func generateDirectoryListing(URL *url.URL, path string) string { func generateDirectoryListing(URL *url.URL, path string, config Config) string {
var listing string var listing string
files, err := ioutil.ReadDir(path) files, err := ioutil.ReadDir(path)
if err != nil { if err != nil {
@ -287,6 +288,21 @@ func generateDirectoryListing(URL *url.URL, path string) string {
up := filepath.Dir(URL.Path) up := filepath.Dir(URL.Path)
listing += fmt.Sprintf("=> %s %s\n", up, "..") listing += fmt.Sprintf("=> %s %s\n", up, "..")
} }
// Sort files
sort.SliceStable(files, func(i, j int) bool {
if config.DirectoryReverse {
i, j = j, i
}
if config.DirectorySort == "Name" {
return files[i].Name() < files[j].Name()
} else if config.DirectorySort == "Size" {
return files[i].Size() < files[j].Size()
} else if config.DirectorySort == "Time" {
return files[i].ModTime().Before(files[j].ModTime())
}
return false // Should not happen
})
// Format lines
for _, file := range files { for _, file := range files {
// Skip dotfiles // Skip dotfiles
if strings.HasPrefix(file.Name(), ".") { if strings.HasPrefix(file.Name(), ".") {