diff --git a/cmd/files.go b/cmd/files.go index 5e2320f..223e0c5 100644 --- a/cmd/files.go +++ b/cmd/files.go @@ -34,6 +34,11 @@ var ( extensions = [6]string{".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp"} ) +type Dimensions struct { + Width int + Height int +} + type Files struct { Mutex sync.Mutex List map[string][]string @@ -105,21 +110,21 @@ func humanReadableSize(bytes int) string { float64(bytes)/float64(div), "KMGTPE"[exp]) } -func getImageDimensions(path string) (string, error) { +func getImageDimensions(path string) (*Dimensions, error) { file, err := os.Open(path) if err != nil { - return "", err + return &Dimensions{}, err } defer file.Close() myImage, _, err := image.DecodeConfig(file) if errors.Is(err, image.ErrFormat) { - return "", nil + return &Dimensions{Width: 0, Height: 0}, nil } else if err != nil { - return "", err + return &Dimensions{}, err } - return fmt.Sprintf("%vx%v", myImage.Width, myImage.Height), nil + return &Dimensions{Width: myImage.Width, Height: myImage.Height}, nil } func preparePath(path string) string { @@ -336,7 +341,7 @@ func pathIsValid(filePath string, paths []string) bool { switch { case Verbose && !matchesPrefix: fmt.Printf("%v | Error: Failed to serve file outside specified path(s): %v\n", - time.Now().Format(LOGDATE), + time.Now().Format(LogDate), filePath, ) @@ -489,7 +494,7 @@ func pickFile(args []string, filters *Filters, sort string, fileCache *[]string) if Verbose { fmt.Printf("%v | Scanned %v/%v files across %v directories in %v\n", - time.Now().Format(LOGDATE), + time.Now().Format(LogDate), stats.GetFilesMatched(), stats.GetFilesTotal(), stats.GetDirectoriesMatched(), diff --git a/cmd/version.go b/cmd/version.go index 651270b..35394a0 100644 --- a/cmd/version.go +++ b/cmd/version.go @@ -10,7 +10,7 @@ import ( "github.com/spf13/cobra" ) -var Version = "0.23.1" +var Version = "0.24.0" func init() { rootCmd.AddCommand(versionCmd) diff --git a/cmd/web.go b/cmd/web.go index 1f8a09a..7b71f11 100644 --- a/cmd/web.go +++ b/cmd/web.go @@ -17,12 +17,18 @@ import ( "strconv" "strings" "time" + + "github.com/yosssi/gohtml" ) const ( - LOGDATE string = "2006-01-02T15:04:05.000-07:00" - PREFIX string = "/src" + LogDate string = `2006-01-02T15:04:05.000-07:00` + Prefix string = `/src` RedirectStatusCode int = http.StatusSeeOther + + // You may not like it, but this is what peak string generation looks like. + RefreshScriptTemplate string = `` + HtmlTemplate string = `%v (%vx%v)Roulette selected: %v%v` ) type Filters struct { @@ -167,7 +173,7 @@ func stripQueryParams(u string) (string, error) { } func generateFilePath(filePath string) string { - htmlBody := PREFIX + htmlBody := Prefix if runtime.GOOS == "windows" { htmlBody += "/" } @@ -186,7 +192,7 @@ func refererToUri(referer string) string { return "/" + parts[3] } -func serveHtml(w http.ResponseWriter, r *http.Request, filePath, dimensions string, filters *Filters) error { +func serveHtml(w http.ResponseWriter, r *http.Request, filePath string, dimensions *Dimensions, filters *Filters) error { fileName := filepath.Base(filePath) w.Header().Add("Content-Type", "text/html") @@ -198,43 +204,33 @@ func serveHtml(w http.ResponseWriter, r *http.Request, filePath, dimensions stri queryParams, err := generateQueryParams(filters, r.URL.Query().Get("sort"), refreshInterval) - htmlBody := ` - - - ` - htmlBody += fmt.Sprintf("%v (%v)", fileName, dimensions) - htmlBody += ` - - - ` + refreshScript := "" if refreshInterval != "0" { r, err := strconv.Atoi(refreshInterval) if err != nil { return err } refreshTimer := strconv.Itoa(r * 1000) - htmlBody += ` - ` + refreshScript = fmt.Sprintf(RefreshScriptTemplate, + queryParams, + refreshTimer) } - htmlBody += ` - -` - _, err = io.WriteString(w, htmlBody) + htmlBody := fmt.Sprintf(HtmlTemplate, + fileName, + dimensions.Width, + dimensions.Height, + queryParams, + generateFilePath(filePath), + dimensions.Width, + dimensions.Height, + fileName, + refreshScript, + ) + + formattedBody := gohtml.Format(htmlBody) + + _, err = io.WriteString(w, formattedBody) if err != nil { return err } @@ -243,12 +239,12 @@ func serveHtml(w http.ResponseWriter, r *http.Request, filePath, dimensions stri } func serveStaticFile(w http.ResponseWriter, r *http.Request, paths []string) error { - prefixedFilePath, err := stripQueryParams(r.URL.Path) + PrefixedFilePath, err := stripQueryParams(r.URL.Path) if err != nil { return err } - filePath, err := filepath.EvalSymlinks(strings.TrimPrefix(prefixedFilePath, PREFIX)) + filePath, err := filepath.EvalSymlinks(strings.TrimPrefix(PrefixedFilePath, Prefix)) if err != nil { return err } @@ -279,7 +275,7 @@ func serveStaticFile(w http.ResponseWriter, r *http.Request, paths []string) err if Verbose { fmt.Printf("%v | Served %v (%v) to %v in %v\n", - startTime.Format(LOGDATE), + startTime.Format(LogDate), filePath, humanReadableSize(len(buf)), r.RemoteAddr, @@ -406,7 +402,7 @@ func ServePage(args []string) error { fileCache := []string{} http.Handle("/", serveHtmlHandler(paths, *re, &fileCache)) - http.Handle(PREFIX+"/", http.StripPrefix(PREFIX, serveStaticFileHandler(paths))) + http.Handle(Prefix+"/", http.StripPrefix(Prefix, serveStaticFileHandler(paths))) http.HandleFunc("/favicon.ico", doNothing) err = http.ListenAndServe(":"+strconv.FormatInt(int64(Port), 10), nil) diff --git a/go.mod b/go.mod index 675d869..5f86cc7 100644 --- a/go.mod +++ b/go.mod @@ -5,10 +5,12 @@ go 1.19 require ( github.com/h2non/filetype v1.1.3 github.com/spf13/cobra v1.6.1 + github.com/yosssi/gohtml v0.0.0-20201013000340-ee4748c638f4 golang.org/x/image v0.1.0 ) require ( github.com/inconshreveable/mousetrap v1.0.1 // indirect github.com/spf13/pflag v1.0.5 // indirect + golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect ) diff --git a/go.sum b/go.sum index 0194ad9..90a6b9c 100644 --- a/go.sum +++ b/go.sum @@ -8,6 +8,8 @@ github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/yosssi/gohtml v0.0.0-20201013000340-ee4748c638f4 h1:0sw0nJM544SpsihWx1bkXdYLQDlzRflMgFJQ4Yih9ts= +github.com/yosssi/gohtml v0.0.0-20201013000340-ee4748c638f4/go.mod h1:+ccdNT0xMY1dtc5XBxumbYfOUhmduiGudqaDgD2rVRE= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= @@ -16,6 +18,7 @@ golang.org/x/image v0.1.0/go.mod h1:iyPr49SD/G/TBxYVB/9RRtGUT5eNbo2u4NamWeQcD5c= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= diff --git a/vendor/github.com/yosssi/gohtml/.gitignore b/vendor/github.com/yosssi/gohtml/.gitignore new file mode 100644 index 0000000..8365624 --- /dev/null +++ b/vendor/github.com/yosssi/gohtml/.gitignore @@ -0,0 +1,23 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test diff --git a/vendor/github.com/yosssi/gohtml/LICENSE b/vendor/github.com/yosssi/gohtml/LICENSE new file mode 100644 index 0000000..73f313c --- /dev/null +++ b/vendor/github.com/yosssi/gohtml/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Keiji Yoshida + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/vendor/github.com/yosssi/gohtml/README.md b/vendor/github.com/yosssi/gohtml/README.md new file mode 100644 index 0000000..34da207 --- /dev/null +++ b/vendor/github.com/yosssi/gohtml/README.md @@ -0,0 +1,194 @@ +# GoHTML - HTML formatter for Go + +[![wercker status](https://app.wercker.com/status/926cf3edc004271539be40d705d037bd/s "wercker status")](https://app.wercker.com/project/bykey/926cf3edc004271539be40d705d037bd) +[![GoDoc](http://godoc.org/github.com/yosssi/gohtml?status.png)](http://godoc.org/github.com/yosssi/gohtml) + +GoHTML is an HTML formatter for [Go](http://golang.org/). You can format HTML source codes by using this package. + +## Install + +``` +go get -u github.com/yosssi/gohtml +``` + +## Example + +Example Go source code: + +```go +package main + +import ( + "fmt" + + "github.com/yosssi/gohtml" +) + +func main() { + h := `This is a title.

AAA
BBB>

` + fmt.Println(gohtml.Format(h)) +} +``` + +Output: + +```html + + + + + This is a title. + + + + + +
+ +

+ AAA +
+ BBB> +

+
+ + + +``` + +## Output Formatted HTML with Line No + +You can output formatted HTML source codes with line no by calling `FormatWithLineNo`: + +```go +package main + +import ( + "fmt" + + "github.com/yosssi/gohtml" +) + +func main() { + h := `This is a title.

AAA
BBB>

` + fmt.Println(gohtml.FormatWithLineNo(h)) +} +``` + +Output: + +```sh + 1 + 2 + 3 + 4 + 5 This is a title. + 6 + 7 +13 +20 +21 +22
+23 +24

+25 AAA +26
+27 BBB> +28

+29
+30 +31 +32 +``` + +## Format Go html/template Package's Template's Execute Result + +You can format [Go html/template package](http://golang.org/pkg/html/template/)'s template's execute result by passing `Writer` to the `tpl.Execute`: + +```go +package main + +import ( + "os" + "text/template" + + "github.com/yosssi/gohtml" +) + +func main() { + + tpl, err := template.New("test").Parse("{{.Msg}}") + + if err != nil { + panic(err) + } + + data := map[string]interface{}{"Msg": "Hello!"} + + err = tpl.Execute(gohtml.NewWriter(os.Stdout), data) + + if err != nil { + panic(err) + } +} +``` + +Output: + +```html + + + + + Hello! + + +``` + +## Docs + +* [GoDoc](https://godoc.org/github.com/yosssi/gohtml) diff --git a/vendor/github.com/yosssi/gohtml/consts.go b/vendor/github.com/yosssi/gohtml/consts.go new file mode 100644 index 0000000..625118c --- /dev/null +++ b/vendor/github.com/yosssi/gohtml/consts.go @@ -0,0 +1,7 @@ +package gohtml + +const ( + defaultIndentString = " " + startIndent = 0 + defaultLastElement = "" +) diff --git a/vendor/github.com/yosssi/gohtml/doc.go b/vendor/github.com/yosssi/gohtml/doc.go new file mode 100644 index 0000000..f787ed3 --- /dev/null +++ b/vendor/github.com/yosssi/gohtml/doc.go @@ -0,0 +1,2 @@ +// Package gohtml provides an HTML formatting function. +package gohtml diff --git a/vendor/github.com/yosssi/gohtml/element.go b/vendor/github.com/yosssi/gohtml/element.go new file mode 100644 index 0000000..a41be68 --- /dev/null +++ b/vendor/github.com/yosssi/gohtml/element.go @@ -0,0 +1,7 @@ +package gohtml + +// An element represents an HTML element. +type element interface { + isInline() bool + write(*formattedBuffer, bool) bool +} diff --git a/vendor/github.com/yosssi/gohtml/formatter.go b/vendor/github.com/yosssi/gohtml/formatter.go new file mode 100644 index 0000000..baed679 --- /dev/null +++ b/vendor/github.com/yosssi/gohtml/formatter.go @@ -0,0 +1,37 @@ +package gohtml + +import ( + "bytes" + "strconv" + "strings" +) + +// Format parses the input HTML string, formats it and returns the result. +func Format(s string) string { + return parse(strings.NewReader(s)).html() +} + +// FormatBytes parses input HTML as bytes, formats it and returns the result. +func FormatBytes(b []byte) []byte { + return parse(bytes.NewReader(b)).bytes() +} + +// Format parses the input HTML string, formats it and returns the result with line no. +func FormatWithLineNo(s string) string { + return AddLineNo(Format(s)) +} + +func AddLineNo(s string) string { + lines := strings.Split(s, "\n") + maxLineNoStrLen := len(strconv.Itoa(len(lines))) + bf := &bytes.Buffer{} + for i, line := range lines { + lineNoStr := strconv.Itoa(i + 1) + if i > 0 { + bf.WriteString("\n") + } + bf.WriteString(strings.Repeat(" ", maxLineNoStrLen-len(lineNoStr)) + lineNoStr + " " + line) + } + return bf.String() + +} diff --git a/vendor/github.com/yosssi/gohtml/html_document.go b/vendor/github.com/yosssi/gohtml/html_document.go new file mode 100644 index 0000000..7e3a25b --- /dev/null +++ b/vendor/github.com/yosssi/gohtml/html_document.go @@ -0,0 +1,43 @@ +package gohtml + +import "bytes" + +// Column to wrap lines to (disabled by default) +var LineWrapColumn = 0 + +// Maxmimum characters a long word can extend past LineWrapColumn without wrapping +var LineWrapMaxSpillover = 5 + +// An htmlDocument represents an HTML document. +type htmlDocument struct { + elements []element +} + +// html generates an HTML source code and returns it. +func (htmlDoc *htmlDocument) html() string { + return string(htmlDoc.bytes()) +} + +// bytes reads from htmlDocument's internal array of elements and returns HTML source code +func (htmlDoc *htmlDocument) bytes() []byte { + bf := &formattedBuffer{ + buffer: &bytes.Buffer{}, + + lineWrapColumn: LineWrapColumn, + lineWrapMaxSpillover: LineWrapMaxSpillover, + + indentString: defaultIndentString, + indentLevel: startIndent, + } + + isPreviousNodeInline := true + for _, child := range htmlDoc.elements { + isPreviousNodeInline = child.write(bf, isPreviousNodeInline) + } + return bf.buffer.Bytes() +} + +// append appends an element to the htmlDocument. +func (htmlDoc *htmlDocument) append(e element) { + htmlDoc.elements = append(htmlDoc.elements, e) +} diff --git a/vendor/github.com/yosssi/gohtml/parser.go b/vendor/github.com/yosssi/gohtml/parser.go new file mode 100644 index 0000000..f71f5f5 --- /dev/null +++ b/vendor/github.com/yosssi/gohtml/parser.go @@ -0,0 +1,103 @@ +package gohtml + +import ( + "golang.org/x/net/html" + "io" + "strings" +) + +// parse parses a stirng and converts it into an html. +func parse(r io.Reader) *htmlDocument { + htmlDoc := &htmlDocument{} + tokenizer := html.NewTokenizer(r) + for { + if errorToken, _, _ := parseToken(tokenizer, htmlDoc, nil); errorToken { + break + } + } + return htmlDoc +} + +// Function that identifies which tags will be treated as containing preformatted +// content. Such tags will have the formatting of all its contents preserved +// unchanged. +// The opening tag html.Token is passed to this function. +// By default, only
 and