From 7a3fd1f23fe7a4d679fb1fb7d13884a653e5d4f5 Mon Sep 17 00:00:00 2001 From: Andrew Gallant Date: Mon, 26 Sep 2016 19:21:17 -0400 Subject: [PATCH] Add a --null flag. This flag causes a NUL byte to follow any file path in ripgrep's output. Closes #89. --- doc/rg.1 | 8 ++++++++ doc/rg.1.md | 6 ++++++ src/args.rs | 10 ++++++++++ src/printer.rs | 41 ++++++++++++++++++++++++++++++++++++----- tests/tests.rs | 42 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 102 insertions(+), 5 deletions(-) diff --git a/doc/rg.1 b/doc/rg.1 index 8c70e711..b61376d9 100644 --- a/doc/rg.1 +++ b/doc/rg.1 @@ -239,6 +239,14 @@ Note that .ignore files will continue to be respected. .RS .RE .TP +.B \-\-null +Whenever a file name is printed, follow it with a NUL byte. +This includes printing filenames before matches, and when printing a +list of matching files such as with \-\-count, \-\-files\-with\-matches +and \-\-files. +.RS +.RE +.TP .B \-p, \-\-pretty Alias for \-\-color=always \-\-heading \-n. .RS diff --git a/doc/rg.1.md b/doc/rg.1.md index 3a6c579b..c996d832 100644 --- a/doc/rg.1.md +++ b/doc/rg.1.md @@ -155,6 +155,12 @@ the raw speed of grep. : Don't respect version control ignore files (e.g., .gitignore). Note that .ignore files will continue to be respected. +--null +: Whenever a file name is printed, follow it with a NUL byte. + This includes printing filenames before matches, and when printing + a list of matching files such as with --count, --files-with-matches + and --files. + -p, --pretty : Alias for --color=always --heading -n. diff --git a/src/args.rs b/src/args.rs index 8af34a30..2a825e29 100644 --- a/src/args.rs +++ b/src/args.rs @@ -155,6 +155,12 @@ Less common options: Don't respect version control ignore files (e.g., .gitignore). Note that .ignore files will continue to be respected. + --null + Whenever a file name is printed, follow it with a NUL byte. + This includes printing filenames before matches, and when printing + a list of matching files such as with --count, --files-with-matches + and --files. + -p, --pretty Alias for --color=always --heading -n. @@ -224,6 +230,7 @@ pub struct RawArgs { flag_no_line_number: bool, flag_no_mmap: bool, flag_no_filename: bool, + flag_null: bool, flag_pretty: bool, flag_quiet: bool, flag_regexp: Vec, @@ -269,6 +276,7 @@ pub struct Args { no_ignore: bool, no_ignore_parent: bool, no_ignore_vcs: bool, + null: bool, quiet: bool, replace: Option>, text: bool, @@ -399,6 +407,7 @@ impl RawArgs { no_ignore_vcs: // --no-ignore implies --no-ignore-vcs self.flag_no_ignore_vcs || no_ignore, + null: self.flag_null, quiet: self.flag_quiet, replace: self.flag_replace.clone().map(|s| s.into_bytes()), text: text, @@ -553,6 +562,7 @@ impl Args { .heading(self.heading) .line_per_match(self.line_per_match) .quiet(self.quiet) + .null(self.null) .with_filename(self.with_filename); if let Some(ref rep) = self.replace { p = p.replace(rep.clone()); diff --git a/src/printer.rs b/src/printer.rs index 0645b7e4..07c96754 100644 --- a/src/printer.rs +++ b/src/printer.rs @@ -33,6 +33,9 @@ pub struct Printer { line_per_match: bool, /// Whether to suppress all output. quiet: bool, + /// Whether to print NUL bytes after a file path instead of new lines + /// or `:`. + null: bool, /// A string to use as a replacement of each match in a matching line. replace: Option>, /// Whether to prefix each match with the corresponding file name. @@ -51,6 +54,7 @@ impl Printer { heading: false, line_per_match: false, quiet: false, + null: false, replace: None, with_filename: false, } @@ -89,6 +93,13 @@ impl Printer { self } + /// Whether to cause NUL bytes to follow file paths instead of other + /// visual separators (like `:`, `-` and `\n`). + pub fn null(mut self, yes: bool) -> Printer { + self.null = yes; + self + } + /// When set, all output is suppressed. pub fn quiet(mut self, yes: bool) -> Printer { self.quiet = yes; @@ -146,14 +157,22 @@ impl Printer { pub fn path>(&mut self, path: P) { let path = strip_prefix("./", path.as_ref()).unwrap_or(path.as_ref()); self.write_path(path); - self.write_eol(); + if self.null { + self.write(b"\x00"); + } else { + self.write_eol(); + } } /// Prints the given path and a count of the number of matches found. pub fn path_count>(&mut self, path: P, count: u64) { if self.with_filename { self.write_path(path); - self.write(b":"); + if self.null { + self.write(b"\x00"); + } else { + self.write(b":"); + } } self.write(count.to_string().as_bytes()); self.write_eol(); @@ -214,7 +233,11 @@ impl Printer { self.write_heading(path.as_ref()); } else if !self.heading && self.with_filename { self.write_path(path.as_ref()); - self.write(b":"); + if self.null { + self.write(b"\x00"); + } else { + self.write(b":"); + } } if let Some(line_number) = line_number { self.line_number(line_number, b':'); @@ -264,7 +287,11 @@ impl Printer { self.write_heading(path.as_ref()); } else if !self.heading && self.with_filename { self.write_path(path.as_ref()); - self.write(b"-"); + if self.null { + self.write(b"\x00"); + } else { + self.write(b"-"); + } } if let Some(line_number) = line_number { self.line_number(line_number, b'-'); @@ -281,7 +308,11 @@ impl Printer { let _ = self.wtr.attr(Attr::Bold); } self.write_path(path.as_ref()); - self.write_eol(); + if self.null { + self.write(b"\x00"); + } else { + self.write_eol(); + } if self.wtr.supports_color() { let _ = self.wtr.reset(); } diff --git a/tests/tests.rs b/tests/tests.rs index 582eb841..d6767065 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -770,6 +770,48 @@ sherlock:be, to a very large extent, the result of luck. Sherlock Holmes assert_eq!(lines, expected); }); +// See: https://github.com/BurntSushi/ripgrep/issues/89 +sherlock!(feature_89_files_with_matches, "Sherlock", ".", +|wd: WorkDir, mut cmd: Command| { + cmd.arg("--null").arg("--files-with-matches"); + + let lines: String = wd.stdout(&mut cmd); + assert_eq!(lines, "sherlock\x00"); +}); + +// See: https://github.com/BurntSushi/ripgrep/issues/89 +sherlock!(feature_89_count, "Sherlock", ".", +|wd: WorkDir, mut cmd: Command| { + cmd.arg("--null").arg("--count"); + + let lines: String = wd.stdout(&mut cmd); + assert_eq!(lines, "sherlock\x002\n"); +}); + +// See: https://github.com/BurntSushi/ripgrep/issues/89 +sherlock!(feature_89_files, "NADA", ".", +|wd: WorkDir, mut cmd: Command| { + cmd.arg("--null").arg("--files"); + + let lines: String = wd.stdout(&mut cmd); + assert_eq!(lines, "sherlock\x00"); +}); + +// See: https://github.com/BurntSushi/ripgrep/issues/89 +sherlock!(feature_89_match, "Sherlock", ".", +|wd: WorkDir, mut cmd: Command| { + cmd.arg("--null").arg("-C1"); + + let lines: String = wd.stdout(&mut cmd); + let expected = "\ +sherlock\x00For the Doctor Watsons of this world, as opposed to the Sherlock +sherlock\x00Holmeses, success in the province of detective work must always +sherlock\x00be, to a very large extent, the result of luck. Sherlock Holmes +sherlock\x00can extract a clew from a wisp of straw or a flake of cigar ash; +"; + assert_eq!(lines, expected); +}); + #[test] fn binary_nosearch() { let wd = WorkDir::new("binary_nosearch");