You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
lazy-rebuild/lazy-rebuild.go

189 lines
3.8 KiB

package main
import (
"crypto/sha256"
"path/filepath"
"fmt"
"io"
"io/ioutil"
"strings"
//"log"
"os"
"sync"
"strconv"
"regexp"
)
var keepLongExt bool = false
var fake bool = false
var recurse bool = false
var noErr bool = false
func last(str []string) string {
if(len(str)<1) {
return ""
} else {
return fmt.Sprintf(".%s", str[len(str)-1])
}
}
func hash(file string) []byte {
f, err := os.Open(file)
if err != nil {
fmt.Printf("E: Cannot open file: %s", err);
}
defer f.Close()
h := sha256.New()
if _, err := io.Copy(h, f); err != nil {
fmt.Printf("E: Cannot create hash: %s", err);
}
return h.Sum(nil)
}
var isHashRE *regexp.Regexp = regexp.MustCompile("^[0-9a-f]{64}$")
func is_hash(name string) bool {
name = strings.Split(name, ".")[0]
return isHashRE.MatchString(name)
}
func extractExt(x string) string {
if (keepLongExt) {
return last(strings.SplitN(x, ".", 2)[1:])
} else {
return filepath.Ext(x)
}
}
var alwaysCheck bool = false
func dofile(i int, y string) {
loc, x := filepath.Split(y)
if !alwaysCheck && is_hash(x) {
fmt.Printf("E: %s already a hash, skipping.\n", x)
return
}
sha := hash(y)
str := fmt.Sprintf("%x", sha)
newname := fmt.Sprintf("%s%s%s", loc, str, extractExt(x))
if _, err := os.Stat(newname); !os.IsNotExist(err) {
if(!noErr) {
fmt.Printf("E: (%d) %s: %s alread exists.\n", i, y, newname)
}
} else {
if !fake {
os.Rename(y,newname)
}
fmt.Printf("(%d) %s -> %s\n", i, y, newname)
}
}
var maxThreads int = 0
func main() {
ar := os.Args[1:];
var wg sync.WaitGroup
if (len(ar) < 1) {
fmt.Printf("Usage: %s [--long] [--fake] [--recurse] [-quiet] [--threads <number>] [--always] <file ...>\n\t--long\tKeep long file extensions\n\t--fake\tDo not rename files.\n\t--recurse\tWalk path recursively.\n\t--quiet\tDo not show (some) errors.\n\t--always\tDo not skip filenames which are already hashes.\n", os.Args[0])
return;
}
var ignore = false
for i, x := range ar {
if ignore {
ignore=false
continue
}
if( strings.HasPrefix(x, "--")) {
switch x[2:] {
case "long":
keepLongExt = !keepLongExt
case "fake":
fake = !fake
case "recurse":
recurse = !recurse
case "quiet":
noErr = !noErr
case "always":
alwaysCheck = true
case "threads":
if i<len(ar)-1 {
if th, err := strconv.Atoi(ar[i+1]); err ==nil {
maxThreads = th
ignore = true
continue
} else {
fmt.Printf("Bad threads flag");
}
} else {
fmt.Printf("No threads flag")
}
default:
fmt.Printf("Unknown flag \"%s\"\n", x)
}
} else {
if fi, err:= os.Stat(x); os.IsNotExist(err) {
fmt.Printf("(%d) %s does not exist.\n", i, x)
} else {
var sem chan bool
if maxThreads > 1 {
sem = make(chan bool, maxThreads)
} else {
sem = make(chan bool)
}
var _dofile = func(i int, s string) {
if maxThreads == 1 {
dofile(i,s)
return
}
wg.Add(1)
if(maxThreads>0) {
sem <- true
}
go func() {
defer wg.Done()
dofile(i,s)
if(maxThreads>0) {
<-sem
}
}()
}
if(fi.Mode().IsDir()) {
if(recurse) {
filepath.Walk(x, func(path string, f os.FileInfo, err error) error {
if(!f.Mode().IsDir()) {
_dofile(i,path)
}
return nil
})
} else {
files, err := ioutil.ReadDir(x)
if err != nil {
fmt.Printf("E: Error enumerating files in %s: %s\n", x, err)
} else {
for _, f := range files {
if(!f.Mode().IsDir()) {
fl := fmt.Sprintf("%s/%s", x, f.Name())
_dofile(i,fl)
} else {
fmt.Printf("!: Ignoring %s (is directory)\n", f.Name())
}
}
}
}
} else {
_dofile(i, x)
}
close(sem)
}
}
}
wg.Wait()
return;
}