mirror of https://github.com/YGGverse/nexd.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
143 lines
2.4 KiB
143 lines
2.4 KiB
package main |
|
|
|
import ( |
|
"bufio" |
|
"io" |
|
"io/fs" |
|
"log" |
|
"net" |
|
"os" |
|
"path" |
|
"sort" |
|
"strings" |
|
) |
|
|
|
func write(w io.Writer, s string) (int, error) { |
|
return w.Write([]byte(s + "\n")) |
|
} |
|
|
|
type Handler struct { |
|
FS fs.FS |
|
} |
|
|
|
func (h *Handler) handleDir(p string, w io.Writer) error { |
|
if header, err := fs.ReadFile(h.FS, path.Join(p, ".header")); err == nil { |
|
write(w, string(header)) |
|
} |
|
modified := true |
|
if _, err := fs.Stat(h.FS, path.Join(p, ".modified")); err != nil { |
|
modified = false |
|
} |
|
asc := false |
|
if _, err := fs.Stat(h.FS, path.Join(p, ".desc")); err != nil { |
|
asc = true |
|
} |
|
dirents, err := fs.ReadDir(h.FS, p) |
|
if err != nil { |
|
return err |
|
} |
|
sort.Slice(dirents, func(i, j int) bool { |
|
if modified { |
|
st1, err := dirents[i].Info() |
|
if err != nil { |
|
return false |
|
} |
|
st2, err := dirents[j].Info() |
|
if err != nil { |
|
return false |
|
} |
|
return st1.ModTime().After(st2.ModTime()) |
|
} else if asc { |
|
return dirents[i].Name() < dirents[j].Name() |
|
} else { |
|
return dirents[i].Name() > dirents[j].Name() |
|
} |
|
}) |
|
for _, entry := range dirents { |
|
name := entry.Name() |
|
if strings.HasPrefix(name, ".") { |
|
continue |
|
} |
|
i, err := entry.Info() |
|
if err != nil { |
|
continue |
|
} |
|
if i.Mode()&(1<<2) == 0 { |
|
continue |
|
} |
|
if entry.IsDir() { |
|
name += "/" |
|
} |
|
if _, err := write(w, "=> "+name); err != nil { |
|
return err |
|
} |
|
} |
|
return nil |
|
} |
|
|
|
func (h *Handler) Handle(p string, w io.Writer) error { |
|
if p == "/" || p == "" { |
|
p = "." |
|
} else { |
|
p = strings.Trim(p, "/") |
|
} |
|
|
|
f, err := h.FS.Open(p) |
|
if err != nil { |
|
return err |
|
} |
|
defer f.Close() |
|
|
|
stat, err := f.Stat() |
|
if err != nil { |
|
return err |
|
} |
|
|
|
if stat.IsDir() { |
|
index, err := h.FS.Open(path.Join(p, "index")) |
|
if err != nil { |
|
return h.handleDir(p, w) |
|
} |
|
defer index.Close() |
|
f = index |
|
} |
|
|
|
_, err = io.Copy(w, f) |
|
return err |
|
} |
|
|
|
func serve(h *Handler, rw io.ReadWriteCloser) { |
|
defer rw.Close() |
|
scanner := bufio.NewScanner(rw) |
|
scanner.Scan() |
|
sel := scanner.Text() |
|
if err := h.Handle(sel, rw); err != nil { |
|
rw.Write([]byte("document not found")) |
|
log.Println(err) |
|
} |
|
} |
|
|
|
func listenAndServe(h *Handler) error { |
|
l, err := net.Listen("tcp", ":1900") |
|
if err != nil { |
|
return err |
|
} |
|
defer l.Close() |
|
|
|
for { |
|
rw, err := l.Accept() |
|
if err != nil { |
|
return err |
|
} |
|
go serve(h, rw) |
|
} |
|
} |
|
|
|
func main() { |
|
if len(os.Args) < 2 { |
|
log.Fatal("usage: nexd path") |
|
} |
|
|
|
h := Handler{FS: os.DirFS(os.Args[1])} |
|
log.Fatal(listenAndServe(&h)) |
|
} |