Add renfnv tool.

This is a tool I use for renaming files when doing photographic work.

This change also renames the functions in fileutil to not be P
functions.
This commit is contained in:
Kyle Isom 2016-08-23 12:59:34 -07:00
parent f710b82899
commit e8045e0b1f
3 changed files with 155 additions and 4 deletions

15
cmd/renfnv/README Normal file
View File

@ -0,0 +1,15 @@
renfnv
This renames a file to the base32-encoded 64-bit FNV-1a hash of the
file's contents. The extension is preserved. This was motivated by
a desire to normalise names for photographs.
Usage: renfnv [-fhlnv] files...
-f force overwriting of files when there is a collision.
-h print this help message.
-l list changed files.
-n Perform a dry run: don't actually move files.
-v Print all files as they are processed. If both -v and -l
are specified, it will behave as if only -v was specified.

136
cmd/renfnv/renfnv.go Normal file
View File

@ -0,0 +1,136 @@
package main
import (
"encoding/base32"
"encoding/binary"
"flag"
"fmt"
"hash/fnv"
"io"
"os"
"path/filepath"
"strings"
"github.com/kisom/goutils/fileutil"
"github.com/kisom/goutils/lib"
)
func hashName(path, encodedHash string) string {
basename := filepath.Base(path)
location := filepath.Dir(path)
ext := filepath.Ext(basename)
return filepath.Join(location, encodedHash+ext)
}
func newName(path string) (string, error) {
h := fnv.New32a()
f, err := os.Open(path)
if err != nil {
return "", err
}
defer f.Close()
_, err = io.Copy(h, f)
if err != nil {
return "", err
}
var buf [8]byte
binary.BigEndian.PutUint32(buf[:], h.Sum32())
encodedHash := base32.StdEncoding.EncodeToString(h.Sum(nil))
encodedHash = strings.TrimRight(encodedHash, "=")
return hashName(path, encodedHash), nil
}
func move(dst, src string, force bool) (err error) {
if fileutil.FileDoesExist(dst) && !force {
return fmt.Errorf("%s exists (pass the -f flag to overwrite)", dst)
return nil
}
dstFile, err := os.Create(dst)
if err != nil {
return err
}
defer func(e error) {
dstFile.Close()
if e != nil {
os.Remove(dst)
}
}(err)
srcFile, err := os.Open(src)
if err != nil {
return err
}
defer srcFile.Close()
_, err = io.Copy(dstFile, srcFile)
if err != nil {
return err
}
os.Remove(src)
return nil
}
func usage(w io.Writer) {
fmt.Fprintf(w, `Usage: renfnv [-fhlnv] files...
renfnv renames files to the base32-encoded 32-bit FNV-1a hash of their
contents, preserving the dirname and extension.
Options:
-f force overwriting of files when there is a collision.
-h print this help message.
-l list changed files.
-n Perform a dry run: don't actually move files.
-v Print all files as they are processed. If both -v and -l
are specified, it will behave as if only -v was specified.
`)
}
func init() {
flag.Usage = func () { usage(os.Stdout) }
}
func main() {
var dryRun, force, printChanged, verbose bool
flag.BoolVar(&force, "f", false, "force overwriting of files if there is a collision")
flag.BoolVar(&printChanged, "l", false, "list changed files")
flag.BoolVar(&dryRun, "n", false, "dry run --- don't perform moves")
flag.BoolVar(&verbose, "v", false, "list all processed files")
flag.Parse()
if verbose && printChanged {
printChanged = false
}
for _, file := range flag.Args() {
renamed, err := newName(file)
if err != nil {
lib.Warn(err, "failed to get new file name")
continue
}
if verbose && !printChanged {
fmt.Println(file)
}
if renamed != file {
if !dryRun {
err = move(renamed, file, force)
if err != nil {
lib.Warn(err, "failed to rename file from %s to %s", file, renamed)
continue
}
}
if printChanged && !verbose {
fmt.Println(file, "->", renamed)
}
}
}
}

View File

@ -7,14 +7,14 @@ import (
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
) )
// FileExistsP returns true if the file exists. // FileDoesExist returns true if the file exists.
func FileExistsP(path string) bool { func FileDoesExist(path string) bool {
_, err := os.Stat(path) _, err := os.Stat(path)
return !os.IsNotExist(err) return !os.IsNotExist(err)
} }
// DirectoryExistsP returns true if the file exists. // DirectoryDoesExist returns true if the file exists.
func DirectoryExistsP(path string) bool { func DirectoryDoesExist(path string) bool {
fi, err := os.Stat(path) fi, err := os.Stat(path)
if err != nil { if err != nil {
return false return false