1
0
mirror of https://github.com/BurntSushi/ripgrep.git synced 2025-05-13 21:26:27 +02:00
ripgrep/src/main.rs

340 lines
9.5 KiB
Rust
Raw Normal View History

#![allow(dead_code, unused_imports)]
extern crate bytecount;
#[macro_use]
extern crate clap;
extern crate ctrlc;
extern crate env_logger;
2016-06-20 16:53:48 -04:00
extern crate grep;
extern crate ignore;
2016-09-05 17:36:41 -04:00
#[cfg(windows)]
extern crate kernel32;
2016-09-03 21:48:23 -04:00
#[macro_use]
extern crate lazy_static;
2016-09-05 17:36:41 -04:00
extern crate libc;
#[macro_use]
extern crate log;
2016-03-10 20:48:44 -05:00
extern crate memchr;
extern crate memmap;
2016-08-25 21:44:37 -04:00
extern crate num_cpus;
2016-02-27 11:07:26 -05:00
extern crate regex;
extern crate termcolor;
2016-09-05 17:36:41 -04:00
#[cfg(windows)]
extern crate winapi;
2016-02-27 11:07:26 -05:00
use std::error::Error;
use std::io;
use std::io::Write;
2016-02-27 11:07:26 -05:00
use std::process;
use std::result;
use std::sync::{Arc, Mutex};
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::sync::mpsc;
use std::thread;
2016-02-27 11:07:26 -05:00
use termcolor::WriteColor;
2016-08-25 21:44:37 -04:00
use args::Args;
use worker::Work;
2016-08-25 21:44:37 -04:00
macro_rules! errored {
($($tt:tt)*) => {
return Err(From::from(format!($($tt)*)));
}
}
2016-02-27 11:07:26 -05:00
2016-08-25 21:44:37 -04:00
macro_rules! eprintln {
($($tt:tt)*) => {{
use std::io::Write;
let _ = writeln!(&mut ::std::io::stderr(), $($tt)*);
}}
}
mod app;
mod args;
2016-09-10 00:05:20 -04:00
mod atty;
mod pathutil;
mod printer;
mod search_buffer;
mod search_stream;
mod unescape;
mod worker;
2016-03-10 20:48:44 -05:00
pub type Result<T> = result::Result<T, Box<Error + Send + Sync>>;
2016-02-27 11:07:26 -05:00
fn main() {
match Args::parse().map(Arc::new).and_then(run) {
Ok(0) => process::exit(1),
Ok(_) => process::exit(0),
2016-02-27 11:07:26 -05:00
Err(err) => {
2016-09-05 17:36:41 -04:00
eprintln!("{}", err);
2016-02-27 11:07:26 -05:00
process::exit(1);
}
}
}
fn run(args: Arc<Args>) -> Result<u64> {
if args.never_match() {
return Ok(0);
}
{
let args = args.clone();
ctrlc::set_handler(move || {
let mut writer = args.stdout();
let _ = writer.reset();
let _ = writer.flush();
process::exit(1);
});
}
let threads = args.threads();
if args.files() {
if threads == 1 || args.is_one_path() {
run_files_one_thread(args)
} else {
run_files_parallel(args)
}
} else if args.type_list() {
run_types(args)
} else if threads == 1 || args.is_one_path() {
run_one_thread(args)
} else {
run_parallel(args)
}
}
fn run_parallel(args: Arc<Args>) -> Result<u64> {
let bufwtr = Arc::new(args.buffer_writer());
let quiet_matched = QuietMatched::new(args.quiet());
let paths_searched = Arc::new(AtomicUsize::new(0));
let match_count = Arc::new(AtomicUsize::new(0));
args.walker_parallel().run(|| {
let args = args.clone();
let quiet_matched = quiet_matched.clone();
let paths_searched = paths_searched.clone();
let match_count = match_count.clone();
let bufwtr = bufwtr.clone();
let mut buf = bufwtr.buffer();
let mut worker = args.worker();
Box::new(move |result| {
use ignore::WalkState::*;
if quiet_matched.has_match() {
return Quit;
}
let dent = match get_or_log_dir_entry(result, args.no_messages()) {
None => return Continue,
Some(dent) => dent,
};
paths_searched.fetch_add(1, Ordering::SeqCst);
buf.clear();
{
// This block actually executes the search and prints the
// results into outbuf.
let mut printer = args.printer(&mut buf);
let count =
if dent.is_stdin() {
worker.run(&mut printer, Work::Stdin)
} else {
worker.run(&mut printer, Work::DirEntry(dent))
};
match_count.fetch_add(count as usize, Ordering::SeqCst);
if quiet_matched.set_match(count > 0) {
return Quit;
}
}
// BUG(burntsushi): We should handle this error instead of ignoring
// it. See: https://github.com/BurntSushi/ripgrep/issues/200
let _ = bufwtr.print(&buf);
Continue
})
});
if !args.paths().is_empty() && paths_searched.load(Ordering::SeqCst) == 0 {
if !args.no_messages() {
eprint_nothing_searched();
}
}
Ok(match_count.load(Ordering::SeqCst) as u64)
}
2016-08-25 21:44:37 -04:00
fn run_one_thread(args: Arc<Args>) -> Result<u64> {
let stdout = args.stdout();
let mut stdout = stdout.lock();
let mut worker = args.worker();
let mut paths_searched: u64 = 0;
let mut match_count = 0;
for result in args.walker() {
let dent = match get_or_log_dir_entry(result, args.no_messages()) {
None => continue,
Some(dent) => dent,
};
let mut printer = args.printer(&mut stdout);
if match_count > 0 {
if args.quiet() {
break;
}
if let Some(sep) = args.file_separator() {
printer = printer.file_separator(sep);
}
}
paths_searched += 1;
match_count +=
if dent.is_stdin() {
worker.run(&mut printer, Work::Stdin)
} else {
worker.run(&mut printer, Work::DirEntry(dent))
};
}
if !args.paths().is_empty() && paths_searched == 0 {
if !args.no_messages() {
eprint_nothing_searched();
}
}
Ok(match_count)
}
fn run_files_parallel(args: Arc<Args>) -> Result<u64> {
let print_args = args.clone();
let (tx, rx) = mpsc::channel::<ignore::DirEntry>();
let print_thread = thread::spawn(move || {
let stdout = print_args.stdout();
let mut printer = print_args.printer(stdout.lock());
let mut file_count = 0;
for dent in rx.iter() {
printer.path(dent.path());
file_count += 1;
}
file_count
});
let no_messages = args.no_messages();
args.walker_parallel().run(move || {
let tx = tx.clone();
Box::new(move |result| {
if let Some(dent) = get_or_log_dir_entry(result, no_messages) {
tx.send(dent).unwrap();
}
ignore::WalkState::Continue
})
});
Ok(print_thread.join().unwrap())
}
fn run_files_one_thread(args: Arc<Args>) -> Result<u64> {
let stdout = args.stdout();
let mut printer = args.printer(stdout.lock());
let mut file_count = 0;
for result in args.walker() {
let dent = match get_or_log_dir_entry(result, args.no_messages()) {
None => continue,
Some(dent) => dent,
};
printer.path(dent.path());
file_count += 1;
2016-09-03 21:48:23 -04:00
}
Ok(file_count)
}
2016-09-03 21:48:23 -04:00
fn run_types(args: Arc<Args>) -> Result<u64> {
let stdout = args.stdout();
let mut printer = args.printer(stdout.lock());
let mut ty_count = 0;
for def in args.type_defs() {
printer.type_def(def);
ty_count += 1;
2016-09-03 21:48:23 -04:00
}
Ok(ty_count)
}
fn get_or_log_dir_entry(
result: result::Result<ignore::DirEntry, ignore::Error>,
no_messages: bool,
) -> Option<ignore::DirEntry> {
match result {
Err(err) => {
if !no_messages {
eprintln!("{}", err);
}
None
}
Ok(dent) => {
if let Some(err) = dent.error() {
if !no_messages {
eprintln!("{}", err);
}
2016-09-05 10:15:13 -04:00
}
let ft = match dent.file_type() {
None => return Some(dent), // entry is stdin
Some(ft) => ft,
};
2016-11-06 19:02:14 -05:00
// A depth of 0 means the user gave the path explicitly, so we
// should always try to search it.
if dent.depth() == 0 && !ft.is_dir() {
Some(dent)
} else if ft.is_file() {
Some(dent)
} else {
None
}
2016-09-05 10:15:13 -04:00
}
}
}
fn version() -> String {
let (maj, min, pat) = (
option_env!("CARGO_PKG_VERSION_MAJOR"),
option_env!("CARGO_PKG_VERSION_MINOR"),
option_env!("CARGO_PKG_VERSION_PATCH"),
);
match (maj, min, pat) {
(Some(maj), Some(min), Some(pat)) => {
format!("ripgrep {}.{}.{}", maj, min, pat)
}
_ => "".to_owned(),
}
}
fn eprint_nothing_searched() {
eprintln!("No files were searched, which means ripgrep probably \
applied a filter you didn't expect. \
Try running again with --debug.");
2016-09-03 21:48:23 -04:00
}
/// A simple thread safe abstraction for determining whether a search should
/// stop if the user has requested quiet mode.
#[derive(Clone, Debug)]
pub struct QuietMatched(Arc<Option<AtomicBool>>);
impl QuietMatched {
/// Create a new QuietMatched value.
///
/// If quiet is true, then set_match and has_match will reflect whether
/// a search should quit or not because it found a match.
///
/// If quiet is false, then set_match is always a no-op and has_match
/// always returns false.
pub fn new(quiet: bool) -> QuietMatched {
let atomic = if quiet { Some(AtomicBool::new(false)) } else { None };
QuietMatched(Arc::new(atomic))
}
/// Returns true if and only if quiet mode is enabled and a match has
/// occurred.
pub fn has_match(&self) -> bool {
match *self.0 {
None => false,
Some(ref matched) => matched.load(Ordering::SeqCst),
}
}
/// Sets whether a match has occurred or not.
///
/// If quiet mode is disabled, then this is a no-op.
pub fn set_match(&self, yes: bool) -> bool {
match *self.0 {
None => false,
Some(_) if !yes => false,
Some(ref m) => { m.store(true, Ordering::SeqCst); true }
}
}
}