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 ;
}