-
Notifications
You must be signed in to change notification settings - Fork 24
/
godu.go
121 lines (113 loc) · 3.25 KB
/
godu.go
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
package main
import (
"flag"
"fmt"
"io/ioutil"
"log"
"os"
"path/filepath"
"sync"
"time"
"github.com/gdamore/tcell/v2"
"github.com/gosuri/uilive"
"github.com/viktomas/godu/commands"
"github.com/viktomas/godu/files"
"github.com/viktomas/godu/interactive"
)
// the correct version is injected by `go build` command in release.sh script
var goduVersion = "master"
func main() {
limit := flag.Int64("l", 10, "show only files larger than limit (in MB)")
nullTerminate := flag.Bool("print0", false, "print null-terminated strings")
version := flag.Bool("v", false, "show version")
flag.Usage = func() {
fmt.Fprintf(os.Stderr, "Usage: godu [OPTION]... [DIRECTORY]\nShow disk usage under DIRECTORY (. by default) interactively.\n\nOptions:\n")
flag.PrintDefaults()
fmt.Fprintf(os.Stderr, "\nThe currently selected file/folder can be marked/unmarked with the space key. Upon exiting, godu prints all marked files/folders to stdout. You can further process them with commands like xargs.\n\nFor example:\n\n# Show information of selected files\ngodu -print0 | xargs -0 ls -l\n\n# Delete selected files\ngodu -print0 | xargs -0 rm -rf\n\n# Move selected files to 'tmp' directory\ngodu -print0 | xargs -0 -I _ mv _ tmp\n")
}
flag.Parse()
if *version {
fmt.Printf("godu %s\n", goduVersion)
os.Exit(0)
}
args := flag.Args()
rootFolderName := "."
if len(args) > 0 {
rootFolderName = args[0]
}
rootFolderName, err := filepath.Abs(rootFolderName)
if err != nil {
log.Fatalln(err.Error())
}
progress := make(chan int)
go reportProgress(progress)
rootFolder := files.WalkFolder(rootFolderName, ioutil.ReadDir, ignoreBasedOnIgnoreFile(readIgnoreFile()), progress)
rootFolder.Name = rootFolderName
err = commands.ProcessFolder(rootFolder, *limit*files.MEGABYTE)
if err != nil {
log.Fatalln(err.Error())
}
s := initScreen()
commandsChan := make(chan commands.Executer)
states := make(chan commands.State)
lastStateChan := make(chan *commands.State, 1)
var wg sync.WaitGroup
wg.Add(3)
go commands.StartProcessing(rootFolder, commandsChan, states, lastStateChan, &wg)
go interactiveFolder(s, states, &wg)
go parseCommand(s, commandsChan, &wg)
wg.Wait()
s.Fini()
lastState := <-lastStateChan
printMarkedFiles(lastState, *nullTerminate)
}
func reportProgress(progress <-chan int) {
const interval = 50 * time.Millisecond
writer := uilive.New()
writer.Out = os.Stderr
writer.Start()
defer writer.Stop()
total := 0
ticker := time.NewTicker(interval)
for {
select {
case c, ok := <-progress:
if !ok {
return
}
total += c
case <-ticker.C:
fmt.Fprintf(writer, "Walked through %d folders\n", total)
}
}
}
func printMarkedFiles(lastState *commands.State, nullTerminate bool) {
markedFiles := interactive.FilesAsSlice(lastState.MarkedFiles)
var printFunc func(string)
if nullTerminate {
printFunc = func(s string) {
fmt.Printf("%s\x00", s)
}
} else {
printFunc = func(s string) {
fmt.Println(s)
}
}
for _, f := range markedFiles {
printFunc(f)
}
}
func initScreen() tcell.Screen {
tcell.SetEncodingFallback(tcell.EncodingFallbackASCII)
s, e := tcell.NewScreen()
if e != nil {
log.Printf("%v\n", e)
os.Exit(1)
}
if e = s.Init(); e != nil {
log.Printf("%v\n", e)
os.Exit(1)
}
s.Clear()
return s
}