File: renfnv.go

package info (click to toggle)
golang-github-kisom-goutils 0.0~git20161101.0.858c9cb-1
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 384 kB
  • ctags: 331
  • sloc: makefile: 6
file content (136 lines) | stat: -rw-r--r-- 2,811 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
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)
			}
		}
	}
}