2016-09-14 13:40:46 +02:00
|
|
|
extern crate deque;
|
2016-02-27 18:07:26 +02:00
|
|
|
extern crate docopt;
|
2016-08-28 07:37:12 +02:00
|
|
|
extern crate env_logger;
|
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;
|
2016-08-28 07:37:12 +02:00
|
|
|
#[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;
|
2016-08-29 02:18:34 +02:00
|
|
|
use std::fs::File;
|
2016-09-05 23:36:41 +02:00
|
|
|
use std::io;
|
2016-09-14 03:11:46 +02:00
|
|
|
use std::path::{Path, PathBuf};
|
2016-02-27 18:07:26 +02:00
|
|
|
use std::process;
|
|
|
|
use std::result;
|
2016-09-06 01:55:31 +02:00
|
|
|
use std::sync::{Arc, Mutex};
|
2016-08-28 07:37:12 +02:00
|
|
|
use std::thread;
|
2016-02-27 18:07:26 +02:00
|
|
|
|
2016-09-14 13:40:46 +02:00
|
|
|
use deque::{Stealer, Stolen};
|
2016-09-05 06:52:23 +02:00
|
|
|
use grep::Grep;
|
2016-09-07 03:47:33 +02:00
|
|
|
use memmap::{Mmap, Protection};
|
2016-09-08 03:54:28 +02:00
|
|
|
use term::Terminal;
|
2016-09-05 16:15:13 +02:00
|
|
|
use walkdir::DirEntry;
|
2016-08-26 03:44:37 +02:00
|
|
|
|
2016-09-05 06:52:23 +02:00
|
|
|
use args::Args;
|
2016-09-14 03:11:46 +02:00
|
|
|
use out::{ColoredTerminal, Out};
|
2016-08-28 07:37:12 +02:00
|
|
|
use printer::Printer;
|
2016-09-10 06:08:42 +02:00
|
|
|
use search_stream::InputBuffer;
|
2016-09-14 03:11:46 +02:00
|
|
|
#[cfg(windows)]
|
|
|
|
use terminal_win::WindowsBuffer;
|
2016-08-27 07:01:06 +02:00
|
|
|
|
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)*);
|
|
|
|
}}
|
|
|
|
}
|
|
|
|
|
2016-09-05 06:52:23 +02:00
|
|
|
mod args;
|
2016-09-10 06:05:20 +02:00
|
|
|
mod atty;
|
2016-08-27 07:01:06 +02:00
|
|
|
mod gitignore;
|
2016-08-26 03:44:37 +02:00
|
|
|
mod glob;
|
2016-08-27 07:01:06 +02:00
|
|
|
mod ignore;
|
2016-09-05 06:52:23 +02:00
|
|
|
mod out;
|
2016-08-28 07:37:12 +02:00
|
|
|
mod printer;
|
2016-09-07 03:47:33 +02:00
|
|
|
mod search_buffer;
|
2016-09-10 06:08:42 +02:00
|
|
|
mod search_stream;
|
2016-09-14 03:11:46 +02:00
|
|
|
#[cfg(windows)]
|
|
|
|
mod terminal_win;
|
2016-09-05 06:52:23 +02:00
|
|
|
mod types;
|
2016-08-28 07:37:12 +02:00
|
|
|
mod walk;
|
2016-03-11 03:48:44 +02:00
|
|
|
|
2016-08-28 07:37:12 +02:00
|
|
|
pub type Result<T> = result::Result<T, Box<Error + Send + Sync>>;
|
|
|
|
|
2016-02-27 18:07:26 +02:00
|
|
|
fn main() {
|
2016-09-05 06:52:23 +02:00
|
|
|
match Args::parse().and_then(run) {
|
|
|
|
Ok(count) if count == 0 => process::exit(1),
|
2016-09-14 03:11:46 +02:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-05 06:52:23 +02:00
|
|
|
fn run(args: Args) -> Result<u64> {
|
2016-09-14 03:11:46 +02:00
|
|
|
let args = Arc::new(args);
|
|
|
|
let paths = args.paths();
|
2016-09-05 06:52:23 +02:00
|
|
|
if args.files() {
|
2016-09-14 03:11:46 +02:00
|
|
|
return run_files(args.clone());
|
2016-08-29 02:18:34 +02:00
|
|
|
}
|
2016-09-05 06:52:23 +02:00
|
|
|
if args.type_list() {
|
2016-09-14 03:11:46 +02:00
|
|
|
return run_types(args.clone());
|
2016-09-05 06:52:23 +02:00
|
|
|
}
|
2016-09-14 03:11:46 +02:00
|
|
|
if paths.len() == 1 && (paths[0] == Path::new("-") || paths[0].is_file()) {
|
|
|
|
return run_one(args.clone(), &paths[0]);
|
|
|
|
}
|
|
|
|
|
2016-09-09 03:46:14 +02:00
|
|
|
let out = Arc::new(Mutex::new(args.out()));
|
2016-08-29 02:18:34 +02:00
|
|
|
let mut workers = vec![];
|
|
|
|
|
2016-09-14 13:40:46 +02:00
|
|
|
let workq = {
|
|
|
|
let (workq, stealer) = deque::new();
|
2016-09-05 06:52:23 +02:00
|
|
|
for _ in 0..args.threads() {
|
2016-09-14 03:11:46 +02:00
|
|
|
let worker = MultiWorker {
|
2016-09-03 05:38:27 +02:00
|
|
|
chan_work: stealer.clone(),
|
2016-09-14 03:11:46 +02:00
|
|
|
out: out.clone(),
|
|
|
|
outbuf: Some(args.outbuf()),
|
|
|
|
worker: Worker {
|
|
|
|
args: args.clone(),
|
|
|
|
inpbuf: args.input_buffer(),
|
|
|
|
grep: args.grep(),
|
|
|
|
match_count: 0,
|
|
|
|
},
|
2016-08-29 02:18:34 +02:00
|
|
|
};
|
|
|
|
workers.push(thread::spawn(move || worker.run()));
|
2016-08-28 07:37:12 +02:00
|
|
|
}
|
2016-09-05 06:52:23 +02:00
|
|
|
workq
|
2016-08-29 02:18:34 +02:00
|
|
|
};
|
2016-09-14 03:11:46 +02:00
|
|
|
for p in paths {
|
2016-09-05 06:52:23 +02:00
|
|
|
if p == Path::new("-") {
|
|
|
|
workq.push(Work::Stdin)
|
|
|
|
} else {
|
2016-09-05 23:36:41 +02:00
|
|
|
for ent in try!(args.walker(p)) {
|
2016-09-05 16:15:13 +02:00
|
|
|
workq.push(Work::File(ent));
|
2016-09-05 06:52:23 +02:00
|
|
|
}
|
2016-08-05 06:10:58 +02:00
|
|
|
}
|
2016-03-11 03:48:44 +02:00
|
|
|
}
|
2016-08-29 02:18:34 +02:00
|
|
|
for _ in 0..workers.len() {
|
2016-09-05 06:52:23 +02:00
|
|
|
workq.push(Work::Quit);
|
2016-08-28 07:37:12 +02:00
|
|
|
}
|
2016-09-05 06:52:23 +02:00
|
|
|
let mut match_count = 0;
|
2016-08-29 02:18:34 +02:00
|
|
|
for worker in workers {
|
2016-09-05 06:52:23 +02:00
|
|
|
match_count += worker.join().unwrap();
|
2016-08-29 02:18:34 +02:00
|
|
|
}
|
2016-09-05 06:52:23 +02:00
|
|
|
Ok(match_count)
|
2016-08-29 02:18:34 +02:00
|
|
|
}
|
2016-08-26 03:44:37 +02:00
|
|
|
|
2016-09-14 03:11:46 +02:00
|
|
|
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();
|
2016-09-09 03:46:14 +02:00
|
|
|
let mut printer = args.printer(term);
|
2016-09-05 06:52:23 +02:00
|
|
|
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());
|
2016-09-05 06:52:23 +02:00
|
|
|
file_count += 1;
|
|
|
|
}
|
2016-09-04 03:48:23 +02:00
|
|
|
}
|
|
|
|
}
|
2016-09-05 06:52:23 +02:00
|
|
|
Ok(file_count)
|
|
|
|
}
|
2016-09-04 03:48:23 +02:00
|
|
|
|
2016-09-14 03:11:46 +02:00
|
|
|
fn run_types(args: Arc<Args>) -> Result<u64> {
|
|
|
|
let term = args.stdout();
|
2016-09-09 03:46:14 +02:00
|
|
|
let mut printer = args.printer(term);
|
2016-09-05 06:52:23 +02:00
|
|
|
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
|
|
|
}
|
2016-09-05 06:52:23 +02:00
|
|
|
Ok(ty_count)
|
2016-08-27 07:01:06 +02:00
|
|
|
}
|
|
|
|
|
2016-09-05 06:52:23 +02:00
|
|
|
enum Work {
|
|
|
|
Stdin,
|
2016-09-05 16:15:13 +02:00
|
|
|
File(DirEntry),
|
2016-08-29 02:18:34 +02:00
|
|
|
Quit,
|
2016-08-28 07:37:12 +02:00
|
|
|
}
|
2016-08-27 07:01:06 +02:00
|
|
|
|
2016-09-05 16:15:13 +02:00
|
|
|
enum WorkReady {
|
|
|
|
Stdin,
|
2016-09-14 03:11:46 +02:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2016-08-28 07:37:12 +02:00
|
|
|
struct Worker {
|
|
|
|
args: Arc<Args>,
|
2016-08-29 02:18:34 +02:00
|
|
|
inpbuf: InputBuffer,
|
2016-08-28 07:37:12 +02:00
|
|
|
grep: Grep,
|
2016-09-05 16:15:13 +02:00
|
|
|
match_count: u64,
|
2016-08-28 07:37:12 +02:00
|
|
|
}
|
|
|
|
|
2016-09-14 03:11:46 +02:00
|
|
|
impl MultiWorker {
|
2016-09-05 06:52:23 +02:00
|
|
|
fn run(mut self) -> u64 {
|
2016-08-29 02:18:34 +02:00
|
|
|
loop {
|
2016-09-05 16:15:13 +02:00
|
|
|
let work = match self.chan_work.steal() {
|
2016-09-14 13:40:46 +02:00
|
|
|
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()) {
|
2016-09-14 03:11:46 +02:00
|
|
|
Ok(file) => WorkReady::DirFile(ent, file),
|
2016-09-05 06:52:23 +02:00
|
|
|
Err(err) => {
|
2016-09-05 16:15:13 +02:00
|
|
|
eprintln!("{}: {}", ent.path().display(), err);
|
2016-09-05 06:52:23 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-08-29 02:18:34 +02:00
|
|
|
};
|
|
|
|
let mut outbuf = self.outbuf.take().unwrap();
|
|
|
|
outbuf.clear();
|
2016-09-14 03:11:46 +02:00
|
|
|
let mut printer = self.worker.args.printer(outbuf);
|
|
|
|
self.worker.do_work(&mut printer, work);
|
2016-08-29 02:18:34 +02:00
|
|
|
let outbuf = printer.into_inner();
|
2016-09-08 03:54:28 +02:00
|
|
|
if !outbuf.get_ref().is_empty() {
|
2016-09-06 01:55:31 +02:00
|
|
|
let mut out = self.out.lock().unwrap();
|
2016-09-05 06:52:23 +02:00
|
|
|
out.write(&outbuf);
|
2016-08-29 02:18:34 +02:00
|
|
|
}
|
2016-09-09 03:46:14 +02:00
|
|
|
self.outbuf = Some(outbuf);
|
2016-08-28 07:37:12 +02:00
|
|
|
}
|
2016-09-14 03:11:46 +02:00
|
|
|
self.worker.match_count
|
2016-09-05 16:15:13 +02:00
|
|
|
}
|
2016-09-14 03:11:46 +02:00
|
|
|
}
|
2016-09-05 16:15:13 +02:00
|
|
|
|
2016-09-14 03:11:46 +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)
|
|
|
|
}
|
2016-09-14 03:11:46 +02:00
|
|
|
WorkReady::DirFile(ent, file) => {
|
2016-09-05 16:15:13 +02:00
|
|
|
let mut path = ent.path();
|
|
|
|
if let Ok(p) = path.strip_prefix("./") {
|
|
|
|
path = p;
|
|
|
|
}
|
2016-09-07 03:47:33 +02:00
|
|
|
if self.args.mmap() {
|
|
|
|
self.search_mmap(printer, path, &file)
|
|
|
|
} else {
|
|
|
|
self.search(printer, path, file)
|
|
|
|
}
|
2016-09-05 16:15:13 +02:00
|
|
|
}
|
2016-09-14 03:11:46 +02:00
|
|
|
WorkReady::PathFile(path, file) => {
|
|
|
|
let mut path = &*path;
|
|
|
|
if let Ok(p) = path.strip_prefix("./") {
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
2016-09-05 06:52:23 +02:00
|
|
|
}
|
|
|
|
|
2016-09-14 03:11:46 +02:00
|
|
|
fn search<R: io::Read, W: Terminal + Send>(
|
2016-09-05 06:52:23 +02:00
|
|
|
&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
|
|
|
}
|
2016-09-07 03:47:33 +02:00
|
|
|
|
2016-09-14 03:11:46 +02:00
|
|
|
fn search_mmap<W: Terminal + Send>(
|
2016-09-07 03:47:33 +02:00
|
|
|
&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.
|
|
|
|
return Ok(0);
|
|
|
|
}
|
|
|
|
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
|
|
|
}
|