summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Milde <daniel@milde.cz>2021-04-24 00:31:38 +0200
committerDaniel Milde <daniel@milde.cz>2021-04-24 21:29:23 +0200
commit32d380b6950d6057d82b92de926001e40b5a759d (patch)
treed01f6df1d1b3522f9f636ec7076929b21c24eb1b
parente51bbfd26d587509b878a35cdd08fe6c752a777d (diff)
added posibility to ignore hidden dirs and ignore dirs by regular patternv4.11.0
closes #50
-rw-r--r--README.md25
-rw-r--r--analyze/dir.go9
-rw-r--r--analyze/dir_test.go12
-rw-r--r--cmd/app/app.go50
-rw-r--r--cmd/app/app_test.go38
-rw-r--r--cmd/root.go11
-rw-r--r--common/ignore.go104
-rw-r--r--common/ignore_test.go109
-rw-r--r--common/ui.go20
-rw-r--r--gdu.16
-rw-r--r--gdu.1.md5
-rw-r--r--stdout/stdout.go61
-rw-r--r--stdout/stdout_test.go4
-rw-r--r--tui/actions.go6
-rw-r--r--tui/actions_test.go14
-rw-r--r--tui/format.go10
-rw-r--r--tui/keys.go2
-rw-r--r--tui/keys_test.go18
-rw-r--r--tui/sort_test.go4
-rw-r--r--tui/tui.go105
-rw-r--r--tui/tui_test.go14
21 files changed, 449 insertions, 178 deletions
diff --git a/README.md b/README.md
index f9362c0..2b324d6 100644
--- a/README.md
+++ b/README.md
@@ -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)
}
diff --git a/gdu.1 b/gdu.1
index 5a6c5de..2a3b32b 100644
--- a/gdu.1
+++ b/gdu.1
@@ -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
diff --git a/gdu.1.md b/gdu.1.md
index 7274966..d9706ee 100644
--- a/gdu.1.md
+++ b/gdu.1.md
@@ -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