From 66ea6999b33dfff87e8c0f2d3d5b43e4e31a434c Mon Sep 17 00:00:00 2001 From: lotusirous <5105237+lotusirous@users.noreply.github.com> Date: Mon, 13 Dec 2021 17:09:22 +0900 Subject: [PATCH] Add program --- README.md | 10 ++++- go.mod | 3 ++ goreleaser.yml | 12 ++++++ main.go | 105 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 129 insertions(+), 1 deletion(-) create mode 100644 go.mod create mode 100644 goreleaser.yml create mode 100644 main.go diff --git a/README.md b/README.md index e1f75ee..dc09001 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,10 @@ -# fp +# FP + A filepath utility + +``` +$ fp +usage: fp [file or directory] +Flags: + -v print the file hash in (md5, sha1, sha256) +``` diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..4c20dcf --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module github.com/lotusirous/fp + +go 1.17 diff --git a/goreleaser.yml b/goreleaser.yml new file mode 100644 index 0000000..9abed2d --- /dev/null +++ b/goreleaser.yml @@ -0,0 +1,12 @@ +env: + - GO111MODULE=on +before: + hooks: + - go mod download + - go mod tidy +builds: +- env: + - CGO_ENABLED=0 + goos: [darwin, linux] + goarch: [386, arm, amd64, arm64] + mod_timestamp: '{{ .CommitTimestamp }}' diff --git a/main.go b/main.go new file mode 100644 index 0000000..73b0408 --- /dev/null +++ b/main.go @@ -0,0 +1,105 @@ +package main + +import ( + "crypto/md5" + "crypto/sha1" + "crypto/sha256" + "encoding/hex" + "errors" + "flag" + "fmt" + "hash" + "io" + "os" + "path/filepath" +) + +// Digest hashes a file for a given path +func Digest(h hash.Hash, loc string) (string, error) { + file, err := os.Open(loc) + if err != nil { + return "", err + } + defer file.Close() + + buf := make([]byte, 1024) + if _, err := io.CopyBuffer(h, file, buf); err != nil { + return "", err + } + + return hex.EncodeToString(h.Sum(nil)), nil +} + +// ReadFileInfo gets the abs path and follows the link +func ReadFileInfo(path string) (string, os.FileInfo, error) { + abs, err := filepath.Abs(path) + if err != nil { + return "", nil, err + } + linkAbs, err := filepath.EvalSymlinks(abs) + if err != nil { + return "", nil, err + } + fi, err := os.Stat(linkAbs) + if errors.Is(err, os.ErrNotExist) { + return "", nil, err + } else if err != nil { + return "", nil, err + } + + return linkAbs, fi, nil +} + +func usage() { + fmt.Fprintf(os.Stderr, "usage: fp [file or directory]\n") + fmt.Fprintf(os.Stderr, "Flags:\n") + flag.PrintDefaults() + os.Exit(2) +} + +func exit(w io.Writer, a ...interface{}) { + fmt.Fprintln(w, a...) + os.Exit(1) +} + +var ( + verbose = flag.Bool("v", false, `print the file hash in (md5, sha1, sha256)`) +) + +func main() { + flag.Usage = usage + flag.Parse() + if flag.NArg() == 0 { + flag.Usage() + } + + fd := os.Stdout // default writer + path := flag.Arg(0) + + abs, fi, err := ReadFileInfo(path) + if err != nil { + exit(fd, "Cannot resolve the path: ", err) + } + + fmt.Fprintf(fd, "PATH: %s\n", abs) + if fi.IsDir() { + os.Exit(0) + } + + if *verbose { + hashes := map[string]hash.Hash{ + "MD5": md5.New(), + "SHA1": sha1.New(), + "SHA256": sha256.New(), + } + for name, hf := range hashes { + if v, err := Digest(hf, abs); err != nil { + fmt.Fprintf(fd, "Cannot digest %s: %v", name, err) + os.Exit(1) + } else { + fmt.Fprintf(fd, "%s: %s\n", name, v) + } + } + } + +}