package html import ( "os" "path" "serveg/internal/http" "serveg/internal/log" sh "serveg/internal/string_helper" ) type listingRow struct { name string // html-escaped url string // url-encoded mtime string // formatted with DateTimeTZ size string // formatted with HumanBytes isDir bool } func (row *listingRow) genHtml(sb *sh.StringBuilder) { sb.AddS(``) sb.AddS(`').AddS(row.name).AddS(``) sb.AddS(``).AddS(row.size).AddS(``) sb.AddS(``).AddS(row.mtime).AddS(``) sb.AddS(``) } func GenDirListingHtml(logger *log.LoggerContext, dirPath, reqUrl, pageTitle string) ([]byte, error) { dirEntries, err := os.ReadDir(dirPath) if err != nil { return nil, err } var dirs, files []listingRow var estimatedLen = 460 // html for page without entries for _, e := range dirEntries { einf, err := e.Info() if err != nil { return nil, err } row := listingRow{ name: htmlEscape(e.Name()), url: http.PercentEcodeUrl(path.Join(reqUrl, e.Name())), mtime: einf.ModTime().Format(DateTimeTZ), isDir: e.IsDir(), } if row.isDir { row.size = "DIR" dirs = append(dirs, row) } else { row.size = sh.HumanBytes(einf.Size()) files = append(files, row) } estimatedLen += len(row.name) + len(row.url) + len(row.mtime) + len(row.size) estimatedLen += 100 // html for row } sb := sh.StringBuilder{Buf: make([]byte, 0, estimatedLen)} sb.AddS(` `) sb.AddS(htmlEscape(pageTitle + " " + reqUrl)) sb.AddS(` `) sb.AddS("

").AddS(htmlEscape(reqUrl)).AddS("

") sb.AddS(``) sb.AddS(``) for i := 0; i < len(dirs); i++ { dirs[i].genHtml(&sb) } for i := 0; i < len(files); i++ { files[i].genHtml(&sb) } sb.AddS(`
../
`) realLen := sb.Len() if estimatedLen < realLen { logger.Warn("genDirListingHtml(): size prediction failed. estimatedLen: ", estimatedLen, " realLen: ", realLen) } return sb.Buf, nil }