commit 86ae761d1b41b4c286f276d2add442a0c9d1181d Author: Avril Date: Tue Jun 16 23:17:50 2020 +0100 rewrite in go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bc886ea --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +emacs-cleaner +*~ diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d841d65 --- /dev/null +++ b/Makefile @@ -0,0 +1,9 @@ + +all: clean emacs-cleaner + +clean: + rm -f emacs-cleaner + +emacs-cleaner: + go build emacs-cleaner.go + strip $@ diff --git a/emacs-cleaner.go b/emacs-cleaner.go new file mode 100644 index 0000000..625880e --- /dev/null +++ b/emacs-cleaner.go @@ -0,0 +1,109 @@ +package main + +import ( + "./semaphore" + "io/ioutil" + "github.com/pkg/errors" + "os" + "flag" + "fmt" + "sync" + "sync/atomic" + "path" + "regexp" +) + +const VERSION string = "0.1.0" + +func walk(rpath string, lock *semaphore.Semaphore, output chan string, wait *sync.WaitGroup) error { + defer wait.Done() + lock.Lock() + defer lock.Unlock() + + + if files, err := ioutil.ReadDir(rpath); err == nil { + for _, file := range files { + if file.IsDir() { + wait.Add(1) + go walk(path.Join(rpath, file.Name()), lock, output, wait) + + } else { + output <- path.Join(rpath, file.Name()) + } + } + } else { + return errors.Wrap(err, "failed to read dir") + } + + return nil +} + +var work_re *regexp.Regexp = regexp.MustCompile("~$") +func work_on(file string) bool { + return work_re.MatchString(file) +} + +func main() { + dry := flag.Bool("dry", false, "Dry run") + threads := flag.Int("threads", 10, "Number of threads to use") + help := flag.Bool("help", false, "Print this message") + flag.Parse() + + dirs := flag.Args() + if *help || len(dirs)<1 { + fmt.Printf("Emacs Cleaner version %v\nDelete emacs filesystem clutter\n\n", VERSION) + fmt.Println("$ emacs-cleaner [--threads [--dry] ") + fmt.Println("$ emacs-cleaner [--help]\n") + + flag.PrintDefaults() + return + } + + + if *threads<1 { + fmt.Printf("[e] cannot use %v threads\n", threads) + return + } + + if *dry { + fmt.Printf("[i] dry run, will not modify\n") + } + lock := semaphore.New(*threads) + operate := make(chan string, 0) + var wait sync.WaitGroup + var used uint64 = 0 + + go func() { + for file := range operate { + if stat, err := os.Stat(file); err == nil { + if !stat.IsDir() && work_on(file) { + fmt.Printf(" -> %v\n", file) + if !*dry { + os.Remove(file) + } + atomic.AddUint64(&used, 1) + } + } + + } + }() + + for _, dir := range dirs { + if d, err:= os.Stat(dir); err == nil && d.IsDir() { + wait.Add(1) + go walk(dir, lock, operate, &wait) + } else if err != nil { + fmt.Printf("[w] cannot stat %v: %v\n", dir, err) + } else { + fmt.Printf("[w] %v is not a directory\n", dir) + } + } + wait.Wait() + + close(operate) + + fmt.Printf("deleted %v emacs temporary files\n", used) + + lock.Close() + +} diff --git a/semaphore/sem.go b/semaphore/sem.go new file mode 100644 index 0000000..8e51928 --- /dev/null +++ b/semaphore/sem.go @@ -0,0 +1,25 @@ +package semaphore + +type Semaphore struct { + channel chan bool +} + +func New(max int) *Semaphore { + this:= new(Semaphore) + + this.channel = make(chan bool, max) + + return this +} + +func (sem* Semaphore) Close() { + close(sem.channel) +} + +func (sem *Semaphore) Lock() { + sem.channel <- true +} + +func (sem *Semaphore) Unlock() { + <- sem.channel +}