1
0
mirror of https://github.com/BurntSushi/ripgrep.git synced 2024-12-12 19:18:24 +02:00
ripgrep/src/main.rs

391 lines
10 KiB
Rust
Raw Normal View History

extern crate deque;
2016-02-27 18:07:26 +02:00
extern crate docopt;
extern crate env_logger;
extern crate fnv;
2016-06-20 22:53:48 +02:00
extern crate grep;
2016-09-05 23:36:41 +02:00
#[cfg(windows)]
extern crate kernel32;
2016-09-04 03:48:23 +02:00
#[macro_use]
extern crate lazy_static;
2016-09-05 23:36:41 +02:00
extern crate libc;
#[macro_use]
extern crate log;
2016-03-11 03:48:44 +02:00
extern crate memchr;
extern crate memmap;
2016-08-26 03:44:37 +02:00
extern crate num_cpus;
2016-02-27 18:07:26 +02:00
extern crate regex;
extern crate rustc_serialize;
2016-09-05 23:36:41 +02:00
extern crate term;
2016-08-26 03:44:37 +02:00
extern crate walkdir;
2016-09-05 23:36:41 +02:00
#[cfg(windows)]
extern crate winapi;
2016-02-27 18:07:26 +02:00
use std::error::Error;
use std::fs::File;
use std::io::{self, Write};
use std::path::{Path, PathBuf};
2016-02-27 18:07:26 +02:00
use std::process;
use std::result;
use std::sync::{Arc, Mutex};
use std::thread;
use std::cmp;
2016-02-27 18:07:26 +02:00
use deque::{Stealer, Stolen};
use grep::Grep;
use memmap::{Mmap, Protection};
use term::Terminal;
2016-09-05 16:15:13 +02:00
use walkdir::DirEntry;
2016-08-26 03:44:37 +02:00
use args::Args;
use out::{ColoredTerminal, Out};
use pathutil::strip_prefix;
use printer::Printer;
use search_stream::InputBuffer;
#[cfg(windows)]
use terminal_win::WindowsBuffer;
2016-08-26 03:44:37 +02:00
macro_rules! errored {
($($tt:tt)*) => {
return Err(From::from(format!($($tt)*)));
}
}
2016-02-27 18:07:26 +02:00
2016-08-26 03:44:37 +02:00
macro_rules! eprintln {
($($tt:tt)*) => {{
use std::io::Write;
let _ = writeln!(&mut ::std::io::stderr(), $($tt)*);
}}
}
mod args;
2016-09-10 06:05:20 +02:00
mod atty;
mod gitignore;
2016-08-26 03:44:37 +02:00
mod glob;
mod ignore;
mod out;
mod pathutil;
mod printer;
mod search_buffer;
mod search_stream;
#[cfg(windows)]
mod terminal_win;
mod types;
mod walk;
2016-03-11 03:48:44 +02:00
pub type Result<T> = result::Result<T, Box<Error + Send + Sync>>;
2016-02-27 18:07:26 +02:00
fn main() {
match Args::parse().and_then(run) {
Ok(count) if count == 0 => process::exit(1),
Ok(_) => process::exit(0),
2016-02-27 18:07:26 +02:00
Err(err) => {
2016-09-05 23:36:41 +02:00
eprintln!("{}", err);
2016-02-27 18:07:26 +02:00
process::exit(1);
}
}
}
fn run(args: Args) -> Result<u64> {
let args = Arc::new(args);
let paths = args.paths();
let threads = cmp::max(1, args.threads() - 1);
if args.files() {
return run_files(args.clone());
}
if args.type_list() {
return run_types(args.clone());
}
if paths.len() == 1 && (paths[0] == Path::new("-") || paths[0].is_file()) {
return run_one(args.clone(), &paths[0]);
}
if threads == 1 {
return run_one_thread(args.clone());
}
let out = Arc::new(Mutex::new(args.out()));
let mut workers = vec![];
let workq = {
let (workq, stealer) = deque::new();
for _ in 0..threads {
let worker = MultiWorker {
chan_work: stealer.clone(),
out: out.clone(),
outbuf: Some(args.outbuf()),
worker: Worker {
args: args.clone(),
inpbuf: args.input_buffer(),
grep: args.grep(),
match_count: 0,
},
};
workers.push(thread::spawn(move || worker.run()));
}
workq
};
let mut paths_searched: u64 = 0;
for p in paths {
if p == Path::new("-") {
paths_searched += 1;
workq.push(Work::Stdin);
} else {
2016-09-05 23:36:41 +02:00
for ent in try!(args.walker(p)) {
paths_searched += 1;
2016-09-05 16:15:13 +02:00
workq.push(Work::File(ent));
}
2016-08-05 06:10:58 +02:00
}
2016-03-11 03:48:44 +02:00
}
if !paths.is_empty() && paths_searched == 0 {
eprintln!("No files were searched, which means ripgrep probably \
applied a filter you didn't expect. \
Try running again with --debug.");
}
for _ in 0..workers.len() {
workq.push(Work::Quit);
}
let mut match_count = 0;
for worker in workers {
match_count += worker.join().unwrap();
}
Ok(match_count)
}
2016-08-26 03:44:37 +02:00
fn run_one_thread(args: Arc<Args>) -> Result<u64> {
let mut worker = Worker {
args: args.clone(),
inpbuf: args.input_buffer(),
grep: args.grep(),
match_count: 0,
};
let paths = args.paths();
let filesep = args.file_separator();
let mut term = args.stdout();
let mut paths_searched: u64 = 0;
for p in paths {
if p == Path::new("-") {
if worker.match_count > 0 {
if let Some(ref sep) = filesep {
let _ = term.write_all(sep);
let _ = term.write_all(b"\n");
}
}
paths_searched += 1;
let mut printer = args.printer(&mut term);
worker.do_work(&mut printer, WorkReady::Stdin);
} else {
for ent in try!(args.walker(p)) {
if worker.match_count > 0 {
if let Some(ref sep) = filesep {
let _ = term.write_all(sep);
let _ = term.write_all(b"\n");
}
}
paths_searched += 1;
let mut printer = args.printer(&mut term);
let file = try!(File::open(ent.path()));
worker.do_work(&mut printer, WorkReady::DirFile(ent, file));
}
}
}
if !paths.is_empty() && paths_searched == 0 {
eprintln!("No files were searched, which means ripgrep probably \
applied a filter you didn't expect. \
Try running again with --debug.");
}
Ok(worker.match_count)
}
fn run_one(args: Arc<Args>, path: &Path) -> Result<u64> {
let mut worker = Worker {
args: args.clone(),
inpbuf: args.input_buffer(),
grep: args.grep(),
match_count: 0,
};
let term = args.stdout();
let mut printer = args.printer(term);
let work =
if path == Path::new("-") {
WorkReady::Stdin
} else {
WorkReady::PathFile(path.to_path_buf(), try!(File::open(path)))
};
worker.do_work(&mut printer, work);
Ok(worker.match_count)
}
fn run_files(args: Arc<Args>) -> Result<u64> {
let term = args.stdout();
let mut printer = args.printer(term);
let mut file_count = 0;
for p in args.paths() {
if p == Path::new("-") {
printer.path(&Path::new("<stdin>"));
file_count += 1;
2016-09-04 03:48:23 +02:00
} else {
2016-09-05 23:36:41 +02:00
for ent in try!(args.walker(p)) {
2016-09-05 16:15:13 +02:00
printer.path(ent.path());
file_count += 1;
}
2016-09-04 03:48:23 +02:00
}
}
Ok(file_count)
}
2016-09-04 03:48:23 +02:00
fn run_types(args: Arc<Args>) -> Result<u64> {
let term = args.stdout();
let mut printer = args.printer(term);
let mut ty_count = 0;
for def in args.type_defs() {
printer.type_def(def);
ty_count += 1;
2016-09-04 03:48:23 +02:00
}
Ok(ty_count)
}
enum Work {
Stdin,
2016-09-05 16:15:13 +02:00
File(DirEntry),
Quit,
}
2016-09-05 16:15:13 +02:00
enum WorkReady {
Stdin,
DirFile(DirEntry, File),
PathFile(PathBuf, File),
}
struct MultiWorker {
chan_work: Stealer<Work>,
out: Arc<Mutex<Out>>,
#[cfg(not(windows))]
outbuf: Option<ColoredTerminal<term::TerminfoTerminal<Vec<u8>>>>,
#[cfg(windows)]
outbuf: Option<ColoredTerminal<WindowsBuffer>>,
worker: Worker,
2016-09-05 16:15:13 +02:00
}
struct Worker {
args: Arc<Args>,
inpbuf: InputBuffer,
grep: Grep,
2016-09-05 16:15:13 +02:00
match_count: u64,
}
impl MultiWorker {
fn run(mut self) -> u64 {
loop {
2016-09-05 16:15:13 +02:00
let work = match self.chan_work.steal() {
Stolen::Empty | Stolen::Abort => continue,
Stolen::Data(Work::Quit) => break,
Stolen::Data(Work::Stdin) => WorkReady::Stdin,
Stolen::Data(Work::File(ent)) => {
2016-09-05 16:15:13 +02:00
match File::open(ent.path()) {
Ok(file) => WorkReady::DirFile(ent, file),
Err(err) => {
2016-09-05 16:15:13 +02:00
eprintln!("{}: {}", ent.path().display(), err);
continue;
}
}
}
};
let mut outbuf = self.outbuf.take().unwrap();
outbuf.clear();
let mut printer = self.worker.args.printer(outbuf);
self.worker.do_work(&mut printer, work);
let outbuf = printer.into_inner();
if !outbuf.get_ref().is_empty() {
let mut out = self.out.lock().unwrap();
out.write(&outbuf);
}
self.outbuf = Some(outbuf);
}
self.worker.match_count
2016-09-05 16:15:13 +02:00
}
}
2016-09-05 16:15:13 +02:00
impl Worker {
fn do_work<W: Terminal + Send>(
2016-09-05 16:15:13 +02:00
&mut self,
printer: &mut Printer<W>,
work: WorkReady,
) {
let result = match work {
WorkReady::Stdin => {
let stdin = io::stdin();
let stdin = stdin.lock();
self.search(printer, &Path::new("<stdin>"), stdin)
}
WorkReady::DirFile(ent, file) => {
2016-09-05 16:15:13 +02:00
let mut path = ent.path();
if let Some(p) = strip_prefix("./", path) {
2016-09-05 16:15:13 +02:00
path = p;
}
if self.args.mmap() {
self.search_mmap(printer, path, &file)
} else {
self.search(printer, path, file)
}
2016-09-05 16:15:13 +02:00
}
WorkReady::PathFile(path, file) => {
let mut path = &*path;
if let Some(p) = strip_prefix("./", path) {
path = p;
}
if self.args.mmap() {
self.search_mmap(printer, path, &file)
} else {
self.search(printer, path, file)
}
}
2016-09-05 16:15:13 +02:00
};
match result {
Ok(count) => {
self.match_count += count;
}
Err(err) => {
eprintln!("{}", err);
}
}
}
fn search<R: io::Read, W: Terminal + Send>(
&mut self,
printer: &mut Printer<W>,
path: &Path,
rdr: R,
) -> Result<u64> {
self.args.searcher(
&mut self.inpbuf,
printer,
&self.grep,
path,
rdr,
).run().map_err(From::from)
2016-09-04 03:48:23 +02:00
}
fn search_mmap<W: Terminal + Send>(
&mut self,
printer: &mut Printer<W>,
path: &Path,
file: &File,
) -> Result<u64> {
if try!(file.metadata()).len() == 0 {
// Opening a memory map with an empty file results in an error.
// However, this may not actually be an empty file! For example,
// /proc/cpuinfo reports itself as an empty file, but it can
// produce data when it's read from. Therefore, we fall back to
// regular read calls.
return self.search(printer, path, file);
}
let mmap = try!(Mmap::open(file, Protection::Read));
Ok(self.args.searcher_buffer(
printer,
&self.grep,
path,
unsafe { mmap.as_slice() },
).run())
}
2016-09-04 03:48:23 +02:00
}