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 ] [--always] \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 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; }