diff options
author | Daniel Milde <daniel@milde.cz> | 2021-04-24 00:31:38 +0200 |
---|---|---|
committer | Daniel Milde <daniel@milde.cz> | 2021-04-24 21:29:23 +0200 |
commit | 32d380b6950d6057d82b92de926001e40b5a759d (patch) | |
tree | d01f6df1d1b3522f9f636ec7076929b21c24eb1b | |
parent | e51bbfd26d587509b878a35cdd08fe6c752a777d (diff) |
added posibility to ignore hidden dirs and ignore dirs by regular patternv4.11.0
closes #50
-rw-r--r-- | README.md | 25 | ||||
-rw-r--r-- | analyze/dir.go | 9 | ||||
-rw-r--r-- | analyze/dir_test.go | 12 | ||||
-rw-r--r-- | cmd/app/app.go | 50 | ||||
-rw-r--r-- | cmd/app/app_test.go | 38 | ||||
-rw-r--r-- | cmd/root.go | 11 | ||||
-rw-r--r-- | common/ignore.go | 104 | ||||
-rw-r--r-- | common/ignore_test.go | 109 | ||||
-rw-r--r-- | common/ui.go | 20 | ||||
-rw-r--r-- | gdu.1 | 6 | ||||
-rw-r--r-- | gdu.1.md | 5 | ||||
-rw-r--r-- | stdout/stdout.go | 61 | ||||
-rw-r--r-- | stdout/stdout_test.go | 4 | ||||
-rw-r--r-- | tui/actions.go | 6 | ||||
-rw-r--r-- | tui/actions_test.go | 14 | ||||
-rw-r--r-- | tui/format.go | 10 | ||||
-rw-r--r-- | tui/keys.go | 2 | ||||
-rw-r--r-- | tui/keys_test.go | 18 | ||||
-rw-r--r-- | tui/sort_test.go | 4 | ||||
-rw-r--r-- | tui/tui.go | 105 | ||||
-rw-r--r-- | tui/tui_test.go | 14 |
21 files changed, 449 insertions, 178 deletions
@@ -73,17 +73,19 @@ Using curl: gdu [flags] [directory_to_scan] Flags: - -h, --help help for gdu - -i, --ignore-dirs strings Absolute paths to ignore (separated by comma) (default [/proc,/dev,/sys,/run]) - -l, --log-file string Path to a logfile (default "/dev/null") - -m, --max-cores int Set max cores that GDU will use. 8 cores available (default 8) - -c, --no-color Do not use colorized output - -x, --no-cross Do not cross filesystem boundaries - -p, --no-progress Do not show progress in non-interactive mode - -n, --non-interactive Do not run in interactive mode - -a, --show-apparent-size Show apparent size - -d, --show-disks Show all mounted disks - -v, --version Print version + -h, --help help for gdu + -i, --ignore-dirs strings Absolute paths to ignore (separated by comma) (default [/proc,/dev,/sys,/run]) + -I, --ignore-dirs-pattern strings Absolute path patterns to ignore (separated by comma) + -l, --log-file string Path to a logfile (default "/dev/null") + -m, --max-cores int Set max cores that GDU will use. 8 cores available (default 8) + -c, --no-color Do not use colorized output + -x, --no-cross Do not cross filesystem boundaries + -H, --no-hidden Ignore hidden directories (beggining with dot) + -p, --no-progress Do not show progress in non-interactive mode + -n, --non-interactive Do not run in interactive mode + -a, --show-apparent-size Show apparent size + -d, --show-disks Show all mounted disks + -v, --version Print version ``` ## Examples @@ -94,6 +96,7 @@ Flags: gdu -d # show all mounted disks gdu -l ./gdu.log <some_dir> # write errors to log file gdu -i /sys,/proc / # ignore some paths + gdu -I '.*[abc]+' # ignore paths by regular pattern gdu -c / # use only white/gray/black colors gdu -n / # only print stats, do not start interactive mode diff --git a/analyze/dir.go b/analyze/dir.go index a4e44d4..bbcd988 100644 --- a/analyze/dir.go +++ b/analyze/dir.go @@ -17,7 +17,7 @@ type CurrentProgress struct { var concurrencyLimit chan struct{} = make(chan struct{}, 3*runtime.GOMAXPROCS(0)) // ShouldDirBeIgnored whether path should be ignored -type ShouldDirBeIgnored func(path string) bool +type ShouldDirBeIgnored func(name, path string) bool // Analyzer is type for dir analyzing function type Analyzer interface { @@ -114,9 +114,10 @@ func (a *ParallelAnalyzer) processDir(path string) *Dir { } for _, f := range files { - entryPath := filepath.Join(path, f.Name()) + name := f.Name() + entryPath := filepath.Join(path, name) if f.IsDir() { - if a.ignoreDir(entryPath) { + if a.ignoreDir(name, entryPath) { continue } dirCount++ @@ -136,7 +137,7 @@ func (a *ParallelAnalyzer) processDir(path string) *Dir { continue } file = &File{ - Name: f.Name(), + Name: name, Flag: getFlag(info), Size: info.Size(), Parent: dir, diff --git a/analyze/dir_test.go b/analyze/dir_test.go index 2c5e815..1344433 100644 --- a/analyze/dir_test.go +++ b/analyze/dir_test.go @@ -14,7 +14,7 @@ func TestAnalyzeDir(t *testing.T) { defer fin() analyzer := CreateAnalyzer() - dir := analyzer.AnalyzeDir("test_dir", func(_ string) bool { return false }) + dir := analyzer.AnalyzeDir("test_dir", func(_, _ string) bool { return false }) c := analyzer.GetProgressChan() progress := <-c @@ -49,7 +49,7 @@ func TestIgnoreDir(t *testing.T) { fin := testdir.CreateTestDir() defer fin() - dir := CreateAnalyzer().AnalyzeDir("test_dir", func(_ string) bool { return true }) + dir := CreateAnalyzer().AnalyzeDir("test_dir", func(_, _ string) bool { return true }) assert.Equal(t, "test_dir", dir.Name) assert.Equal(t, 1, dir.ItemCount) @@ -63,7 +63,7 @@ func TestFlags(t *testing.T) { os.Symlink("test_dir/nested/file2", "test_dir/nested/file3") - dir := CreateAnalyzer().AnalyzeDir("test_dir", func(_ string) bool { return false }) + dir := CreateAnalyzer().AnalyzeDir("test_dir", func(_, _ string) bool { return false }) sort.Sort(dir.Files) assert.Equal(t, int64(28+4096*4), dir.Size) @@ -85,7 +85,7 @@ func TestHardlink(t *testing.T) { os.Link("test_dir/nested/file2", "test_dir/nested/file3") - dir := CreateAnalyzer().AnalyzeDir("test_dir", func(_ string) bool { return false }) + dir := CreateAnalyzer().AnalyzeDir("test_dir", func(_, _ string) bool { return false }) assert.Equal(t, int64(7+4096*3), dir.Size) // file2 and file3 are counted just once for size assert.Equal(t, 6, dir.ItemCount) // but twice for item count @@ -103,7 +103,7 @@ func TestErr(t *testing.T) { os.Chmod("test_dir/nested", 0) defer os.Chmod("test_dir/nested", 0755) - dir := CreateAnalyzer().AnalyzeDir("test_dir", func(_ string) bool { return false }) + dir := CreateAnalyzer().AnalyzeDir("test_dir", func(_, _ string) bool { return false }) assert.Equal(t, "test_dir", dir.GetName()) assert.Equal(t, 2, dir.ItemCount) @@ -119,5 +119,5 @@ func BenchmarkAnalyzeDir(b *testing.B) { b.ResetTimer() - CreateAnalyzer().AnalyzeDir("test_dir", func(_ string) bool { return false }) + CreateAnalyzer().AnalyzeDir("test_dir", func(_, _ string) bool { return false }) } diff --git a/cmd/app/app.go b/cmd/app/app.go index 80fe268..40807d2 100644 --- a/cmd/app/app.go +++ b/cmd/app/app.go @@ -7,6 +7,7 @@ import ( "os" "runtime" + "github.com/dundee/gdu/v4/analyze" "github.com/dundee/gdu/v4/build" "github.com/dundee/gdu/v4/common" "github.com/dundee/gdu/v4/device" @@ -16,18 +17,30 @@ import ( "github.com/rivo/tview" ) +// UI is common interface for both terminal UI and text output +type UI interface { + ListDevices(getter device.DevicesInfoGetter) error + AnalyzePath(path string, parentDir *analyze.Dir) error + SetIgnoreDirPaths(paths []string) + SetIgnoreDirPatterns(paths []string) error + SetIgnoreHidden(value bool) + StartUILoop() error +} + // Flags define flags accepted by Run type Flags struct { - LogFile string - IgnoreDirs []string - MaxCores int - ShowDisks bool - ShowApparentSize bool - ShowVersion bool - NoColor bool - NonInteractive bool - NoProgress bool - NoCross bool + LogFile string + IgnoreDirs []string + IgnoreDirPatterns []string + MaxCores int + ShowDisks bool + ShowApparentSize bool + ShowVersion bool + NoColor bool + NonInteractive bool + NoProgress bool + NoCross bool + NoHidden bool } // App defines the main application @@ -71,6 +84,17 @@ func (a *App) Run() error { } ui.SetIgnoreDirPaths(a.Flags.IgnoreDirs) + + if len(a.Flags.IgnoreDirPatterns) > 0 { + if err := ui.SetIgnoreDirPatterns(a.Flags.IgnoreDirPatterns); err != nil { + return err + } + } + + if a.Flags.NoHidden { + ui.SetIgnoreHidden(true) + } + a.setMaxProcs() if err := a.runAction(ui, path); err != nil { @@ -91,8 +115,8 @@ func (a *App) setMaxProcs() { log.Printf("Max cores set to %d", runtime.GOMAXPROCS(0)) } -func (a *App) createUI() common.UI { - var ui common.UI +func (a *App) createUI() UI { + var ui UI if a.Flags.NonInteractive || !a.Istty { ui = stdout.CreateStdoutUI( @@ -124,7 +148,7 @@ func (a *App) setNoCross(path string) error { return nil } -func (a *App) runAction(ui common.UI, path string) error { +func (a *App) runAction(ui UI, path string) error { if a.Flags.ShowDisks { if err := ui.ListDevices(a.Getter); err != nil { return fmt.Errorf("loading mount points: %w", err) diff --git a/cmd/app/app_test.go b/cmd/app/app_test.go index 785a88c..587216b 100644 --- a/cmd/app/app_test.go +++ b/cmd/app/app_test.go @@ -52,6 +52,44 @@ func TestAnalyzePath(t *testing.T) { assert.Nil(t, err) } +func TestAnalyzePathWithIgnoring(t *testing.T) { + fin := testdir.CreateTestDir() + defer fin() + + out, err := runApp( + &Flags{ + LogFile: "/dev/null", + IgnoreDirPatterns: []string{"/[abc]+"}, + NoHidden: true, + }, + []string{"test_dir"}, + false, + testdev.DevicesInfoGetterMock{}, + ) + + assert.Contains(t, out, "nested") + assert.Nil(t, err) +} + +func TestAnalyzePathWithIgnoringPatternError(t *testing.T) { + fin := testdir.CreateTestDir() + defer fin() + + out, err := runApp( + &Flags{ + LogFile: "/dev/null", + IgnoreDirPatterns: []string{"[[["}, + NoHidden: true, + }, + []string{"test_dir"}, + false, + testdev.DevicesInfoGetterMock{}, + ) + + assert.Equal(t, out, "") + assert.NotNil(t, err) +} + func TestAnalyzePathWithGui(t *testing.T) { fin := testdir.CreateTestDir() defer fin() diff --git a/cmd/root.go b/cmd/root.go index 6d5576a..7c07d55 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -32,15 +32,20 @@ func init() { af = &app.Flags{} flags := rootCmd.Flags() flags.StringVarP(&af.LogFile, "log-file", "l", "/dev/null", "Path to a logfile") - flags.StringSliceVarP(&af.IgnoreDirs, "ignore-dirs", "i", []string{"/proc", "/dev", "/sys", "/run"}, "Absolute paths to ignore (separated by comma)") flags.IntVarP(&af.MaxCores, "max-cores", "m", runtime.NumCPU(), fmt.Sprintf("Set max cores that GDU will use. %d cores available", runtime.NumCPU())) + flags.BoolVarP(&af.ShowVersion, "version", "v", false, "Print version") + + flags.StringSliceVarP(&af.IgnoreDirs, "ignore-dirs", "i", []string{"/proc", "/dev", "/sys", "/run"}, "Absolute paths to ignore (separated by comma)") + flags.StringSliceVarP(&af.IgnoreDirPatterns, "ignore-dirs-pattern", "I", []string{}, "Absolute path patterns to ignore (separated by comma)") + flags.BoolVarP(&af.NoHidden, "no-hidden", "H", false, "Ignore hidden directories (beggining with dot)") + flags.BoolVarP(&af.NoCross, "no-cross", "x", false, "Do not cross filesystem boundaries") + flags.BoolVarP(&af.ShowDisks, "show-disks", "d", false, "Show all mounted disks") flags.BoolVarP(&af.ShowApparentSize, "show-apparent-size", "a", false, "Show apparent size") - flags.BoolVarP(&af.ShowVersion, "version", "v", false, "Print version") flags.BoolVarP(&af.NoColor, "no-color", "c", false, "Do not use colorized output") flags.BoolVarP(&af.NonInteractive, "non-interactive", "n", false, "Do not run in interactive mode") flags.BoolVarP(&af.NoProgress, "no-progress", "p", false, "Do not show progress in non-interactive mode") - flags.BoolVarP(&af.NoCross, "no-cross", "x", false, "Do not cross filesystem boundaries") + } func runE(command *cobra.Command, args []string) error { diff --git a/common/ignore.go b/common/ignore.go new file mode 100644 index 0000000..29a4d57 --- /dev/null +++ b/common/ignore.go @@ -0,0 +1,104 @@ +package common + +import ( + "log" + "regexp" + "strings" + + "github.com/dundee/gdu/v4/analyze" +) + +// CreateIgnorePattern creates one pattern from all path patterns +func CreateIgnorePattern(paths []string) (*regexp.Regexp, error) { + var err error + + for i, path := range paths { + if _, err = regexp.Compile(path); err != nil { + return nil, err + } + paths[i] = "(" + path + ")" + } + + ignore := `^` + strings.Join(paths, "|") + `$` + return regexp.Compile(ignore) +} + +// SetIgnoreDirPaths sets paths to ignore +func (ui *UI) SetIgnoreDirPaths(paths []string) { + log.Printf("Ignoring dirs %s", strings.Join(paths, ", ")) + ui.IgnoreDirPaths = make(map[string]struct{}, len(paths)) + for _, path := range paths { + ui.IgnoreDirPaths[path] = struct{}{} + } +} + +// SetIgnoreDirPatterns sets regular patters of dirs to ignore +func (ui *UI) SetIgnoreDirPatterns(paths []string) error { + var err error + log.Printf("Ignoring dir patterns %s", strings.Join(paths, ", ")) + ui.IgnoreDirPathPatterns, err = CreateIgnorePattern(paths) + return err +} + +// SetIgnoreHidden sets flags if hidden dirs should be ignored +func (ui *UI) SetIgnoreHidden(value bool) { + log.Printf("Ignoring hidden dirs") + ui.IgnoreHidden = value +} + +// ShouldDirBeIgnored returns true if given path should be ignored +func (ui *UI) ShouldDirBeIgnored(name, path string) bool { + _, shouldIgnore := ui.IgnoreDirPaths[path] + if shouldIgnore { + log.Printf("Directory %s ignored", path) + } + return shouldIgnore +} + +// ShouldDirBeIgnoredUsingPattern returns true if given path should be ignored +func (ui *UI) ShouldDirBeIgnoredUsingPattern(name, path string) bool { + shouldIgnore := ui.IgnoreDirPathPatterns.MatchString(path) + if shouldIgnore { + log.Printf("Directory %s ignored", path) + } + return shouldIgnore +} + +// IsHiddenDir returns if the dir name beggins with dot +func (ui *UI) IsHiddenDir(name, path string) bool { + shouldIgnore := name[0] == '.' + if shouldIgnore { + log.Printf("Directory %s ignored", path) + } + return shouldIgnore +} + +// CreateIgnoreFunc returns function for detecting if dir should be ignored +func (ui *UI) CreateIgnoreFunc() analyze.ShouldDirBeIgnored { + switch { + case len(ui.IgnoreDirPaths) > 0 && ui.IgnoreDirPathPatterns == nil && !ui.IgnoreHidden: + return ui.ShouldDirBeIgnored + case len(ui.IgnoreDirPaths) > 0 && ui.IgnoreDirPathPatterns != nil && !ui.IgnoreHidden: + return func(name, path string) bool { + return ui.ShouldDirBeIgnored(name, path) || ui.ShouldDirBeIgnoredUsingPattern(name, path) + } + case len(ui.IgnoreDirPaths) > 0 && ui.IgnoreDirPathPatterns != nil && ui.IgnoreHidden: + return func(name, path string) bool { + return ui.ShouldDirBeIgnored(name, path) || ui.ShouldDirBeIgnoredUsingPattern(name, path) || ui.IsHiddenDir(name, path) + } + case len(ui.IgnoreDirPaths) == 0 && ui.IgnoreDirPathPatterns != nil && ui.IgnoreHidden: + return func(name, path string) bool { + return ui.ShouldDirBeIgnoredUsingPattern(name, path) || ui.IsHiddenDir(name, path) + } + case len(ui.IgnoreDirPaths) == 0 && ui.IgnoreDirPathPatterns != nil && !ui.IgnoreHidden: + return ui.ShouldDirBeIgnoredUsingPattern + case len(ui.IgnoreDirPaths) == 0 && ui.IgnoreDirPathPatterns == nil && ui.IgnoreHidden: + return ui.IsHiddenDir + case len(ui.IgnoreDirPaths) > 0 && ui.IgnoreDirPathPatterns == nil && ui.IgnoreHidden: + return func(name, path string) bool { + return ui.ShouldDirBeIgnored(name, path) || ui.IsHiddenDir(name, path) + } + default: + return func(name, path string) bool { return false } + } +} diff --git a/common/ignore_test.go b/common/ignore_test.go new file mode 100644 index 0000000..8070d9b --- /dev/null +++ b/common/ignore_test.go @@ -0,0 +1,109 @@ +package common_test + +import ( + "testing" + + "github.com/dundee/gdu/v4/common" + "github.com/stretchr/testify/assert" +) + +func TestCreateIgnorePattern(t *testing.T) { + re, err := common.CreateIgnorePattern([]string{"[abc]+"}) + + assert.Nil(t, err) + assert.True(t, re.Match([]byte("aa"))) +} + +func TestCreateIgnorePatternWithErr(t *testing.T) { + re, err := common.CreateIgnorePattern([]string{"[[["}) + + assert.NotNil(t, err) + assert.Nil(t, re) +} + +func TestEmptyIgnore(t *testing.T) { + ui := &common.UI{} + shouldBeIgnored := ui.CreateIgnoreFunc() + + assert.False(t, shouldBeIgnored("abc", "/abc")) + assert.False(t, shouldBeIgnored("xxx", "/xxx")) +} + +func TestIgnoreByAbsPath(t *testing.T) { + ui := &common.UI{} + ui.SetIgnoreDirPaths([]string{"/abc"}) + shouldBeIgnored := ui.CreateIgnoreFunc() + + assert.True(t, shouldBeIgnored("abc", "/abc")) + assert.False(t, shouldBeIgnored("xxx", "/xxx")) +} + +func TestIgnoreByPattern(t *testing.T) { + ui := &common.UI{} + ui.SetIgnoreDirPatterns([]string{"/[abc]+"}) + shouldBeIgnored := ui.CreateIgnoreFunc() + + assert.True(t, shouldBeIgnored("aaa", "/aaa")) + assert.True(t, shouldBeIgnored("aaa", "/aaabc")) + assert.False(t, shouldBeIgnored("xxx", "/xxx")) +} + +func TestIgnoreHidden(t *testing.T) { + ui := &common.UI{} + ui.SetIgnoreHidden(true) + shouldBeIgnored := ui.CreateIgnoreFunc() + + assert.True(t, shouldBeIgnored(".git", "/aaa/.git")) + assert.True(t, shouldBeIgnored(".bbb", "/aaa/.bbb")) + assert.False(t, shouldBeIgnored("xxx", "/xxx")) +} + +func TestIgnoreByAbsPathAndHidden(t *testing.T) { + ui := &common.UI{} + ui.SetIgnoreDirPaths([]string{"/abc"}) + ui.SetIgnoreHidden(true) + shouldBeIgnored := ui.CreateIgnoreFunc() + + assert.True(t, shouldBeIgnored("abc", "/abc")) + assert.True(t, shouldBeIgnored(".git", "/aaa/.git")) + assert.True(t, shouldBeIgnored(".bbb", "/aaa/.bbb")) + assert.False(t, shouldBeIgnored("xxx", "/xxx")) +} + +func TestIgnoreByAbsPathAndPattern(t *testing.T) { + ui := &common.UI{} + ui.SetIgnoreDirPaths([]string{"/abc"}) + ui.SetIgnoreDirPatterns([]string{"/[abc]+"}) + shouldBeIgnored := ui.CreateIgnoreFunc() + + assert.True(t, shouldBeIgnored("abc", "/abc")) + assert.True(t, shouldBeIgnored("aabc", "/aabc")) + assert.True(t, shouldBeIgnored("ccc", "/ccc")) + assert.False(t, shouldBeIgnored("xxx", "/xxx")) +} + +func TestIgnoreByPatternAndHidden(t *testing.T) { + ui := &common.UI{} + ui.SetIgnoreDirPatterns([]string{"/[abc]+"}) + ui.SetIgnoreHidden(true) + shouldBeIgnored := ui.CreateIgnoreFunc() + + assert.True(t, shouldBeIgnored("abbc", "/abbc")) + assert.True(t, shouldBeIgnored(".git", "/aaa/.git")) + assert.True(t, shouldBeIgnored(".bbb", "/aaa/.bbb")) + assert.False(t, shouldBeIgnored("xxx", "/xxx")) +} + +func TestIgnoreByAll(t *testing.T) { + ui := &common.UI{} + ui.SetIgnoreDirPaths([]string{"/abc"}) + ui.SetIgnoreDirPatterns([]string{"/[abc]+"}) + ui.SetIgnoreHidden(true) + shouldBeIgnored := ui.CreateIgnoreFunc() + + assert.True(t, shouldBeIgnored("abc", "/abc")) + assert.True(t, shouldBeIgnored("aabc", "/aabc")) + assert.True(t, shouldBeIgnored(".git", "/aaa/.git")) + assert.True(t, shouldBeIgnored(".bbb", "/aaa/.bbb")) + assert.False(t, shouldBeIgnored("xxx", "/xxx")) +} diff --git a/common/ui.go b/common/ui.go index a5d616d..3d85439 100644 --- a/common/ui.go +++ b/common/ui.go @@ -1,14 +1,20 @@ package common import ( + "io/fs" + "regexp" + "github.com/dundee/gdu/v4/analyze" - "github.com/dundee/gdu/v4/device" ) -// UI is common interface for both terminal UI and text output -type UI interface { - ListDevices(getter device.DevicesInfoGetter) error - AnalyzePath(path string, parentDir *analyze.Dir) error - SetIgnoreDirPaths(paths []string) - StartUILoop() error +// UI struct +type UI struct { + Analyzer analyze.Analyzer + IgnoreDirPaths map[string]struct{} + IgnoreDirPathPatterns *regexp.Regexp + IgnoreHidden bool + UseColors bool + ShowProgress bool + ShowApparentSize bool + PathChecker func(string) (fs.FileInfo, error) } @@ -22,6 +22,9 @@ However HDDs work as well, but the performance gain is not so huge. \f[B]-i\f[R], \f[B]--ignore-dirs\f[R]=[/proc,/dev,/sys,/run] Absolute paths to ignore (separated by comma) .PP +\f[B]-I\f[R], \f[B]--ignore-dirs-pattern\f[R] Absolute path patterns to +ignore (separated by comma) +.PP \f[B]-l\f[R], \f[B]--log-file\f[R]=\[dq]/dev/null\[dq] Path to a logfile .PP \f[B]-m\f[R], \f[B]--max-cores\f[R] Set max cores that GDU will use. @@ -31,6 +34,9 @@ paths to ignore (separated by comma) \f[B]-x\f[R], \f[B]--no-cross\f[R][=false] Do not cross filesystem boundaries .PP +\f[B]-H\f[R], \f[B]--no-hidden\f[R][=false] Ignore hidden directories +(beggining with dot) +.PP \f[B]-p\f[R], \f[B]--no-progress\f[R][=false] Do not show progress in non-interactive mode .PP @@ -27,6 +27,9 @@ is not so huge. **-i**, **\--ignore-dirs**=\[/proc,/dev,/sys,/run\] Absolute paths to ignore (separated by comma) +**-I**, **\--ignore-dirs-pattern** Absolute path patterns to +ignore (separated by comma) + **-l**, **\--log-file**=\"/dev/null\" Path to a logfile **-m**, **\--max-cores** Set max cores that GDU will use. @@ -35,6 +38,8 @@ ignore (separated by comma) **-x**, **\--no-cross**\[=false\] Do not cross filesystem boundaries +**-H**, **\--no-hidden**\[=false\] Ignore hidden directories (beggining with dot) + **-p**, **\--no-progress**\[=false\] Do not show progress in non-interactive mode diff --git a/stdout/stdout.go b/stdout/stdout.go index 8d728fe..0121967 100644 --- a/stdout/stdout.go +++ b/stdout/stdout.go @@ -3,7 +3,6 @@ package stdout import ( "fmt" "io" - "io/fs" "math" "os" "path/filepath" @@ -12,33 +11,31 @@ import ( "time" "github.com/dundee/gdu/v4/analyze" + "github.com/dundee/gdu/v4/common" "github.com/dundee/gdu/v4/device" "github.com/fatih/color" ) // UI struct type UI struct { - analyzer analyze.Analyzer - output io.Writer - ignoreDirPaths map[string]struct{} - useColors bool - showProgress bool - showApparentSize bool - red *color.Color - orange *color.Color - blue *color.Color - pathChecker func(string) (fs.FileInfo, error) + *common.UI + output io.Writer + red *color.Color + orange *color.Color + blue *color.Color } // CreateStdoutUI creates UI for stdout func CreateStdoutUI(output io.Writer, useColors bool, showProgress bool, showApparentSize bool) *UI { ui := &UI{ - output: output, - useColors: useColors, - showProgress: showProgress, - showApparentSize: showApparentSize, - analyzer: analyze.CreateAnalyzer(), - pathChecker: os.Stat, + UI: &common.UI{ + UseColors: useColors, + ShowProgress: showProgress, + ShowApparentSize: showApparentSize, + Analyzer: analyze.CreateAnalyzer(), + PathChecker: os.Stat, + }, + output: output, } ui.red = color.New(color.FgRed).Add(color.Bold) @@ -70,7 +67,7 @@ func (ui *UI) ListDevices(getter device.DevicesInfoGetter) error { ), len("Devices")) var sizeLength, percentLength int - if ui.useColors { + if ui.UseColors { sizeLength = 20 percentLength = 16 } else { @@ -123,12 +120,12 @@ func (ui *UI) AnalyzePath(path string, _ *analyze.Dir) error { ) abspath, _ := filepath.Abs(path) - _, err := ui.pathChecker(abspath) + _, err := ui.PathChecker(abspath) if err != nil { return err } - if ui.showProgress { + if ui.ShowProgress { wait.Add(1) go func() { defer wait.Done() @@ -139,7 +136,7 @@ func (ui *UI) AnalyzePath(path string, _ *analyze.Dir) error { wait.Add(1) go func() { defer wait.Done() - dir = ui.analyzer.AnalyzeDir(abspath, ui.ShouldDirBeIgnored) + dir = ui.Analyzer.AnalyzeDir(abspath, ui.CreateIgnoreFunc()) }() wait.Wait() @@ -147,7 +144,7 @@ func (ui *UI) AnalyzePath(path string, _ *analyze.Dir) error { sort.Sort(dir.Files) var lineFormat string - if ui.useColors { + if ui.UseColors { lineFormat = "%s %20s %s\n" } else { lineFormat = "%s %9s %s\n" @@ -156,7 +153,7 @@ func (ui *UI) AnalyzePath(path string, _ *analyze.Dir) error { var size int64 for _, file := range dir.Files { - if ui.showApparentSize { + if ui.ShowApparentSize { size = file.GetSize() } else { size = file.GetUsage() @@ -180,20 +177,6 @@ func (ui *UI) AnalyzePath(path string, _ *analyze.Dir) error { return nil } -// SetIgnoreDirPaths sets paths to ignore -func (ui *UI) SetIgnoreDirPaths(paths []string) { - ui.ignoreDirPaths = make(map[string]struct{}, len(paths)) - for _, path := range paths { - ui.ignoreDirPaths[path] = struct{}{} - } -} - -// ShouldDirBeIgnored returns true if given path should be ignored -func (ui *UI) ShouldDirBeIgnored(path string) bool { - _, ok := ui.ignoreDirPaths[path] - return ok -} - func (ui *UI) updateProgress() { emptyRow := "\r" for j := 0; j < 100; j++ { @@ -202,8 +185,8 @@ func (ui *UI) updateProgress() { progressRunes := []rune(`⠇⠏⠋⠙⠹⠸⠼⠴⠦⠧`) - progressChan := ui.analyzer.GetProgressChan() - doneChan := ui.analyzer.GetDoneChan() + progressChan := ui.Analyzer.GetProgressChan() + doneChan := ui.Analyzer.GetDoneChan() var progress analyze.CurrentProgress diff --git a/stdout/stdout_test.go b/stdout/stdout_test.go index 58764cd..fb6568c 100644 --- a/stdout/stdout_test.go +++ b/stdout/stdout_test.go @@ -70,8 +70,8 @@ func TestItemRows(t *testing.T) { output := bytes.NewBuffer(make([]byte, 10)) ui := CreateStdoutUI(output, false, true, false) - ui.analyzer = &testanalyze.MockedAnalyzer{} - ui.pathChecker = testdir.MockedPathChecker + ui.Analyzer = &testanalyze.MockedAnalyzer{} + ui.PathChecker = testdir.MockedPathChecker ui.AnalyzePath("test_dir", nil) assert.Contains(t, output.String(), "TiB") diff --git a/tui/actions.go b/tui/actions.go index dee5266..1cfbf37 100644 --- a/tui/actions.go +++ b/tui/actions.go @@ -31,7 +31,7 @@ func (ui *UI) ListDevices(getter device.DevicesInfoGetter) error { ui.table.SetCell(0, 5, tview.NewTableCell("Mount point").SetSelectable(false)) var textColor, sizeColor string - if ui.useColors { + if ui.UseColors { textColor = "[#3498db:-:b]" sizeColor = "[#edb20a:-:b]" } else { @@ -59,7 +59,7 @@ func (ui *UI) ListDevices(getter device.DevicesInfoGetter) error { func (ui *UI) AnalyzePath(path string, parentDir *analyze.Dir) error { abspath, _ := filepath.Abs(path) - _, err := ui.pathChecker(abspath) + _, err := ui.PathChecker(abspath) if err != nil { return err } @@ -83,7 +83,7 @@ func (ui *UI) AnalyzePath(path string, parentDir *analyze.Dir) error { go ui.updateProgress() go func() { - ui.currentDir = ui.analyzer.AnalyzeDir(abspath, ui.ShouldDirBeIgnored) + ui.currentDir = ui.Analyzer.AnalyzeDir(abspath, ui.CreateIgnoreFunc()) runtime.GC() if parentDir != nil { diff --git a/tui/actions_test.go b/tui/actions_test.go index 336a7fe..ad0f398 100644 --- a/tui/actions_test.go +++ b/tui/actions_test.go @@ -66,7 +66,7 @@ func TestShowDevicesWithError(t *testing.T) { func TestDeviceSelected(t *testing.T) { app := testapp.CreateMockedApp(false) ui := CreateUI(app, true, true) - ui.analyzer = &testanalyze.MockedAnalyzer{} + ui.Analyzer = &testanalyze.MockedAnalyzer{} ui.done = make(chan struct{}) ui.SetIgnoreDirPaths([]string{"/xxx"}) ui.ListDevices(getDevicesInfoMock()) @@ -122,8 +122,8 @@ func TestAnalyzePathWithParentDir(t *testing.T) { app := testapp.CreateMockedApp(true) ui := CreateUI(app, false, true) - ui.analyzer = &testanalyze.MockedAnalyzer{} - ui.pathChecker = testdir.MockedPathChecker + ui.Analyzer = &testanalyze.MockedAnalyzer{} + ui.PathChecker = testdir.MockedPathChecker ui.topDir = parentDir ui.done = make(chan struct{}) ui.AnalyzePath("test_dir", parentDir) @@ -145,8 +145,8 @@ func TestAnalyzePathWithParentDir(t *testing.T) { func TestViewDirContents(t *testing.T) { app := testapp.CreateMockedApp(true) ui := CreateUI(app, false, true) - ui.analyzer = &testanalyze.MockedAnalyzer{} - ui.pathChecker = testdir.MockedPathChecker + ui.Analyzer = &testanalyze.MockedAnalyzer{} + ui.PathChecker = testdir.MockedPathChecker ui.done = make(chan struct{}) ui.AnalyzePath("test_dir", nil) @@ -165,8 +165,8 @@ func TestViewDirContents(t *testing.T) { func TestViewContentsOfNotExistingFile(t *testing.T) { app := testapp.CreateMockedApp(true) ui := CreateUI(app, false, true) - ui.analyzer = &testanalyze.MockedAnalyzer{} - ui.pathChecker = testdir.MockedPathChecker + ui.Analyzer = &testanalyze.MockedAnalyzer{} + ui.PathChecker = testdir.MockedPathChecker ui.done = make(chan struct{}) ui.AnalyzePath("test_dir", nil) diff --git a/tui/format.go b/tui/format.go index 9b523c4..862ed25 100644 --- a/tui/format.go +++ b/tui/format.go @@ -20,7 +20,7 @@ const ( func (ui *UI) formatFileRow(item analyze.Item) string { var part int - if ui.showApparentSize { + if ui.ShowApparentSize { part = int(float64(item.GetSize()) / float64(item.GetParent().GetSize()) * 10.0) } else { part = int(float64(item.GetUsage()) / float64(item.GetParent().GetUsage()) * 10.0) @@ -28,13 +28,13 @@ func (ui *UI) formatFileRow(item analyze.Item) string { row := string(item.GetFlag()) - if ui.useColors { + if ui.UseColors { row += "[#e67100::b]" } else { row += "[::b]" } - if ui.showApparentSize { + if ui.ShowApparentSize { row += fmt.Sprintf("%15s", ui.formatSize(item.GetSize(), false, true)) } else { row += fmt.Sprintf("%15s", ui.formatSize(item.GetUsage(), false, true)) @@ -43,7 +43,7 @@ func (ui *UI) formatFileRow(item analyze.Item) string { row += getUsageGraph(part) if item.IsDir() { - if ui.useColors { + if ui.UseColors { row += "[#3498db::b]/" } else { row |