author | Alberto Bertogli
<albertito@blitiri.com.ar> 2023-08-04 23:17:16 UTC |
committer | Alberto Bertogli
<albertito@blitiri.com.ar> 2023-08-05 00:09:08 UTC |
parent | dbf6a0f1228d61fcfade43248678024818bd6bae |
summer.go | +45 | -1 |
test/exclude.t | +27 | -0 |
test/help.t | +12 | -0 |
test/test.sh | +3 | -1 |
ui.go | +11 | -0 |
diff --git a/summer.go b/summer.go index a55696f..e7860a3 100644 --- a/summer.go +++ b/summer.go @@ -8,6 +8,7 @@ import ( "io/fs" "os" "path/filepath" + "regexp" "syscall" "golang.org/x/term" @@ -39,10 +40,13 @@ Usage: Flags: ` +// Flags. var ( dbPath = flag.String("db", "", "database to read from/write to") oneFilesystem = flag.Bool("x", false, "don't cross filesystem boundaries") forceTTY = flag.Bool("forcetty", false, "force TTY output") + exclude = &RepeatedStringFlag{} + excludeRe = &RepeatedStringFlag{} ) var options = struct { @@ -54,6 +58,12 @@ var options = struct { // Whether output is a TTY. isTTY bool + + // Paths to exclude. + exclude map[string]bool + + // Regexp patterns to exclude. + excludeRe []*regexp.Regexp }{} func Usage() { @@ -64,12 +74,26 @@ func Usage() { func main() { var err error + flag.Var(exclude, "exclude", + "exclude these paths (can be repeated)") + flag.Var(excludeRe, "excludere", + "exclude paths matching this regexp (can be repeated)") + flag.Usage = Usage flag.Parse() options.oneFilesystem = *oneFilesystem options.isTTY = *forceTTY || term.IsTerminal(int(os.Stdout.Fd())) + options.exclude = map[string]bool{} + for _, s := range *exclude { + options.exclude[filepath.Clean(s)] = true + } + + for _, s := range *excludeRe { + options.excludeRe = append(options.excludeRe, regexp.MustCompile(s)) + } + op := flag.Arg(0) root := flag.Arg(1) @@ -105,6 +129,18 @@ func main() { } } +func isExcluded(path string) bool { + if options.exclude[path] { + return true + } + for _, re := range options.excludeRe { + if re.MatchString(path) { + return true + } + } + return false +} + var crc32c = crc32.MakeTable(crc32.Castagnoli) type ChecksumV1 struct { @@ -117,13 +153,21 @@ type ChecksumV1 struct { } func openAndInfo(path string, d fs.DirEntry, err error, rootDev uint64) (bool, *os.File, fs.FileInfo, error) { + // Excluded check must come first, because it can be use to skip + // directories that would otherwise cause errors. + if isExcluded(path) { + if d.IsDir() { + return false, nil, nil, fs.SkipDir + } + return false, nil, nil, nil + } + if err != nil { return false, nil, nil, err } if d.IsDir() || !d.Type().IsRegular() { return false, nil, nil, nil } - info, err := d.Info() if err != nil { return true, nil, nil, err diff --git a/test/exclude.t b/test/exclude.t new file mode 100644 index 0000000..f67a76b --- /dev/null +++ b/test/exclude.t @@ -0,0 +1,27 @@ +Tests for excluding files. + + $ alias summer="$TESTDIR/../summer" + +Simple test data. + + $ touch empty1 empty2 empty3 + $ echo marola > hola + +Generate. + + $ summer -n --exclude empty2 --excludere emp..3 generate . + 0s: 0 matched, 0 modified, 2 new, 0 corrupted + +Use a bit more complex test data. + + $ mkdir dir1 dir2 + $ touch dir1/file1 dir1/file2 dir1/file3 + $ touch dir2/file1 dir2/file2 dir2/file3 + + $ summer -n \ + > --exclude empty2 \ + > --excludere emp..3 \ + > -exclude dir2 \ + > -excludere d..1/f...2 \ + > generate . + 0s: 0 matched, 0 modified, 4 new, 0 corrupted diff --git a/test/help.t b/test/help.t index d3b4b5a..5f6d217 100644 --- a/test/help.t +++ b/test/help.t @@ -31,6 +31,10 @@ No arguments. Flags: -db string \tdatabase to read from/write to (esc) + -exclude value + \texclude these paths (can be repeated) (esc) + -excludere value + \texclude paths matching this regexp (can be repeated) (esc) -forcetty \tforce TTY output (esc) -n\tdry-run mode (do not write anything) (esc) @@ -69,6 +73,10 @@ Too few arguments. Flags: -db string \tdatabase to read from/write to (esc) + -exclude value + \texclude these paths (can be repeated) (esc) + -excludere value + \texclude paths matching this regexp (can be repeated) (esc) -forcetty \tforce TTY output (esc) -n\tdry-run mode (do not write anything) (esc) @@ -107,6 +115,10 @@ No valid path (the argument is given, but it is empty). Flags: -db string \tdatabase to read from/write to (esc) + -exclude value + \texclude these paths (can be repeated) (esc) + -excludere value + \texclude paths matching this regexp (can be repeated) (esc) -forcetty \tforce TTY output (esc) -n\tdry-run mode (do not write anything) (esc) diff --git a/test/test.sh b/test/test.sh index c52bf39..3360ac8 100755 --- a/test/test.sh +++ b/test/test.sh @@ -6,4 +6,6 @@ cd $(realpath "$(dirname "$0")" ) # shellcheck disable=SC2086 ( cd ..; go build $BUILDARGS -o summer . ) -cram3 ./*.t +TARGETS="${@:-./*.t}" + +cram3 $TARGETS diff --git a/ui.go b/ui.go index 1b37192..bc8b3a6 100644 --- a/ui.go +++ b/ui.go @@ -167,3 +167,14 @@ func (p *Progress) PrintMatched(path string, cs ChecksumV1) { Verbosef("%q: match (checksum:%x, mtime:%d)", path, cs.CRC32C, cs.ModTimeUsec) } + +type RepeatedStringFlag []string + +func (f *RepeatedStringFlag) String() string { + return fmt.Sprintf("%v", *f) +} + +func (f *RepeatedStringFlag) Set(value string) error { + *f = append(*f, value) + return nil +}