2016-03-11 03:48:44 +02:00
|
|
|
#![allow(dead_code, unused_variables)]
|
2016-02-27 18:07:26 +02:00
|
|
|
|
2016-08-28 07:37:12 +02:00
|
|
|
extern crate crossbeam;
|
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-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-08-29 02:18:34 +02:00
|
|
|
extern crate parking_lot;
|
2016-02-27 18:07:26 +02:00
|
|
|
extern crate regex;
|
2016-03-11 03:48:44 +02:00
|
|
|
extern crate regex_syntax as syntax;
|
2016-02-27 18:07:26 +02:00
|
|
|
extern crate rustc_serialize;
|
2016-08-29 02:18:34 +02:00
|
|
|
extern crate thread_local;
|
2016-08-26 03:44:37 +02:00
|
|
|
extern crate walkdir;
|
2016-02-27 18:07:26 +02:00
|
|
|
|
2016-08-29 02:18:34 +02:00
|
|
|
use std::cmp;
|
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-06-20 22:53:48 +02:00
|
|
|
use std::io::{self, Write};
|
2016-08-29 02:18:34 +02:00
|
|
|
use std::path::{Path, PathBuf};
|
2016-02-27 18:07:26 +02:00
|
|
|
use std::process;
|
|
|
|
use std::result;
|
2016-08-28 07:37:12 +02:00
|
|
|
use std::sync::Arc;
|
|
|
|
use std::thread;
|
2016-02-27 18:07:26 +02:00
|
|
|
|
2016-08-29 02:18:34 +02:00
|
|
|
use crossbeam::sync::SegQueue;
|
2016-02-27 18:07:26 +02:00
|
|
|
use docopt::Docopt;
|
2016-08-28 07:37:12 +02:00
|
|
|
use grep::{Grep, GrepBuilder};
|
2016-08-29 02:18:34 +02:00
|
|
|
use parking_lot::Mutex;
|
2016-08-28 07:37:12 +02:00
|
|
|
use walkdir::WalkDir;
|
2016-08-26 03:44:37 +02:00
|
|
|
|
2016-08-27 07:01:06 +02:00
|
|
|
use ignore::Ignore;
|
2016-08-28 07:37:12 +02:00
|
|
|
use printer::Printer;
|
2016-08-29 02:18:34 +02:00
|
|
|
use search::{InputBuffer, Searcher};
|
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-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-08-28 07:37:12 +02:00
|
|
|
mod printer;
|
|
|
|
mod search;
|
|
|
|
mod walk;
|
2016-03-11 03:48:44 +02:00
|
|
|
|
2016-08-28 07:37:12 +02:00
|
|
|
const USAGE: &'static str = "
|
|
|
|
Usage: xrep [options] <pattern> [<path> ...]
|
2016-08-29 02:18:34 +02:00
|
|
|
xrep --files [<path> ...]
|
2016-08-28 07:37:12 +02:00
|
|
|
|
|
|
|
xrep is like the silver searcher and grep, but faster than both.
|
|
|
|
|
|
|
|
WARNING: Searching stdin isn't yet supported.
|
|
|
|
|
|
|
|
Options:
|
2016-09-02 03:56:23 +02:00
|
|
|
-c, --count Suppress normal output and show count of line
|
|
|
|
matches.
|
|
|
|
-A, --after-context NUM Show NUM lines after each match.
|
|
|
|
-B, --before-context NUM Show NUM lines before each match.
|
|
|
|
-C, --context NUM Show NUM lines before and after each match.
|
|
|
|
--debug Show debug messages.
|
|
|
|
--files Print each file that would be searched
|
|
|
|
(but don't search).
|
|
|
|
--hidden Search hidden directories and files.
|
|
|
|
-i, --ignore-case Case insensitive search.
|
|
|
|
-L, --follow Follow symlinks.
|
|
|
|
-n, --line-number Show line numbers (1-based).
|
|
|
|
-t, --threads ARG The number of threads to use. Defaults to the
|
|
|
|
number of logical CPUs. [default: 0]
|
|
|
|
-v, --invert-match Invert matching.
|
2016-08-28 07:37:12 +02:00
|
|
|
";
|
2016-02-27 18:07:26 +02:00
|
|
|
|
|
|
|
#[derive(RustcDecodable)]
|
|
|
|
struct Args {
|
|
|
|
arg_pattern: String,
|
2016-08-26 03:44:37 +02:00
|
|
|
arg_path: Vec<String>,
|
2016-09-02 03:56:23 +02:00
|
|
|
flag_after_context: usize,
|
|
|
|
flag_before_context: usize,
|
|
|
|
flag_context: usize,
|
2016-03-31 04:24:59 +02:00
|
|
|
flag_count: bool,
|
2016-08-28 07:37:12 +02:00
|
|
|
flag_debug: bool,
|
|
|
|
flag_files: bool,
|
|
|
|
flag_follow: bool,
|
|
|
|
flag_hidden: bool,
|
|
|
|
flag_ignore_case: bool,
|
2016-08-30 04:44:15 +02:00
|
|
|
flag_invert_match: bool,
|
|
|
|
flag_line_number: bool,
|
2016-08-28 07:37:12 +02:00
|
|
|
flag_threads: usize,
|
|
|
|
}
|
|
|
|
|
|
|
|
pub type Result<T> = result::Result<T, Box<Error + Send + Sync>>;
|
|
|
|
|
2016-02-27 18:07:26 +02:00
|
|
|
fn main() {
|
2016-08-26 03:44:37 +02:00
|
|
|
let args: Args = Docopt::new(USAGE).and_then(|d| d.decode())
|
|
|
|
.unwrap_or_else(|e| e.exit());
|
2016-08-29 02:18:34 +02:00
|
|
|
match run(args) {
|
2016-02-27 18:07:26 +02:00
|
|
|
Ok(_) => process::exit(0),
|
|
|
|
Err(err) => {
|
|
|
|
let _ = writeln!(&mut io::stderr(), "{}", err);
|
|
|
|
process::exit(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-29 02:18:34 +02:00
|
|
|
fn run(mut args: Args) -> Result<()> {
|
2016-08-28 07:37:12 +02:00
|
|
|
let mut logb = env_logger::LogBuilder::new();
|
|
|
|
if args.flag_debug {
|
|
|
|
logb.filter(None, log::LogLevelFilter::Debug);
|
|
|
|
} else {
|
|
|
|
logb.filter(None, log::LogLevelFilter::Warn);
|
|
|
|
}
|
|
|
|
if let Err(err) = logb.init() {
|
2016-08-29 02:18:34 +02:00
|
|
|
errored!("failed to initialize logger: {}", err);
|
|
|
|
}
|
|
|
|
|
|
|
|
if args.arg_path.is_empty() {
|
|
|
|
args.arg_path.push("./".to_string());
|
|
|
|
}
|
|
|
|
if args.arg_path.iter().any(|p| p == "-") {
|
|
|
|
errored!("searching <stdin> isn't yet supported");
|
|
|
|
}
|
|
|
|
if args.flag_files {
|
|
|
|
return run_files(args);
|
|
|
|
}
|
|
|
|
let args = Arc::new(args);
|
|
|
|
let mut workers = vec![];
|
|
|
|
let stdout = Arc::new(Mutex::new(io::BufWriter::new(io::stdout())));
|
|
|
|
|
|
|
|
let chan_work_send = {
|
|
|
|
let chan_work = Arc::new(SegQueue::new());
|
|
|
|
for _ in 0..args.num_workers() {
|
|
|
|
let grepb =
|
|
|
|
GrepBuilder::new(&args.arg_pattern)
|
|
|
|
.case_insensitive(args.flag_ignore_case);
|
|
|
|
let worker = Worker {
|
|
|
|
args: args.clone(),
|
|
|
|
stdout: stdout.clone(),
|
|
|
|
chan_work: chan_work.clone(),
|
|
|
|
inpbuf: InputBuffer::new(),
|
|
|
|
outbuf: Some(vec![]),
|
|
|
|
grep: try!(grepb.build()),
|
|
|
|
};
|
|
|
|
workers.push(thread::spawn(move || worker.run()));
|
2016-08-28 07:37:12 +02:00
|
|
|
}
|
2016-08-29 02:18:34 +02:00
|
|
|
chan_work
|
|
|
|
};
|
2016-08-28 07:37:12 +02:00
|
|
|
|
2016-08-29 02:18:34 +02:00
|
|
|
for p in &args.arg_path {
|
|
|
|
for path in args.walker(p) {
|
|
|
|
chan_work_send.push(Message::Some(path));
|
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() {
|
|
|
|
chan_work_send.push(Message::Quit);
|
2016-08-28 07:37:12 +02:00
|
|
|
}
|
2016-08-29 02:18:34 +02:00
|
|
|
for worker in workers {
|
|
|
|
worker.join().unwrap();
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
2016-08-26 03:44:37 +02:00
|
|
|
|
2016-08-29 02:18:34 +02:00
|
|
|
fn run_files(args: Args) -> Result<()> {
|
|
|
|
let mut printer = Printer::new(io::BufWriter::new(io::stdout()));
|
|
|
|
for p in &args.arg_path {
|
|
|
|
for path in args.walker(p) {
|
|
|
|
printer.path(path);
|
2016-08-28 07:37:12 +02:00
|
|
|
}
|
|
|
|
}
|
2016-08-29 02:18:34 +02:00
|
|
|
Ok(())
|
|
|
|
}
|
2016-08-28 07:37:12 +02:00
|
|
|
|
2016-08-29 02:18:34 +02:00
|
|
|
impl Args {
|
|
|
|
fn printer<W: io::Write>(&self, wtr: W) -> Printer<W> {
|
|
|
|
Printer::new(wtr)
|
2016-03-30 03:21:34 +02:00
|
|
|
}
|
2016-04-04 03:22:09 +02:00
|
|
|
|
2016-08-29 02:18:34 +02:00
|
|
|
fn num_workers(&self) -> usize {
|
|
|
|
let mut num = self.flag_threads;
|
|
|
|
if num == 0 {
|
|
|
|
num = cmp::min(8, num_cpus::get());
|
2016-08-28 07:37:12 +02:00
|
|
|
}
|
2016-08-29 02:18:34 +02:00
|
|
|
num
|
2016-08-28 07:37:12 +02:00
|
|
|
}
|
2016-08-27 07:01:06 +02:00
|
|
|
|
2016-08-29 02:18:34 +02:00
|
|
|
fn walker<P: AsRef<Path>>(&self, path: P) -> walk::Iter {
|
|
|
|
let wd = WalkDir::new(path).follow_links(self.flag_follow);
|
|
|
|
let mut ig = Ignore::new();
|
|
|
|
ig.ignore_hidden(!self.flag_hidden);
|
|
|
|
walk::Iter::new(ig, wd)
|
2016-08-27 07:01:06 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-29 02:18:34 +02:00
|
|
|
enum Message<T> {
|
|
|
|
Some(T),
|
|
|
|
Quit,
|
2016-08-28 07:37:12 +02:00
|
|
|
}
|
2016-08-27 07:01:06 +02:00
|
|
|
|
2016-08-28 07:37:12 +02:00
|
|
|
struct Worker {
|
|
|
|
args: Arc<Args>,
|
2016-08-29 02:18:34 +02:00
|
|
|
stdout: Arc<Mutex<io::BufWriter<io::Stdout>>>,
|
|
|
|
chan_work: Arc<SegQueue<Message<PathBuf>>>,
|
|
|
|
inpbuf: InputBuffer,
|
|
|
|
outbuf: Option<Vec<u8>>,
|
2016-08-28 07:37:12 +02:00
|
|
|
grep: Grep,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Worker {
|
2016-08-29 02:18:34 +02:00
|
|
|
fn run(mut self) {
|
|
|
|
loop {
|
|
|
|
let path = match self.chan_work.try_pop() {
|
|
|
|
None => continue,
|
|
|
|
Some(Message::Quit) => break,
|
|
|
|
Some(Message::Some(path)) => path,
|
|
|
|
};
|
|
|
|
let file = match File::open(&path) {
|
|
|
|
Ok(file) => file,
|
|
|
|
Err(err) => {
|
|
|
|
eprintln!("{}: {}", path.display(), err);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
let mut outbuf = self.outbuf.take().unwrap();
|
|
|
|
outbuf.clear();
|
|
|
|
let mut printer = self.args.printer(outbuf);
|
|
|
|
{
|
2016-08-30 04:44:15 +02:00
|
|
|
let mut searcher = Searcher::new(
|
|
|
|
&mut self.inpbuf,
|
|
|
|
&mut printer,
|
|
|
|
&self.grep,
|
|
|
|
&path,
|
|
|
|
file,
|
|
|
|
);
|
|
|
|
searcher = searcher.count(self.args.flag_count);
|
|
|
|
searcher = searcher.line_number(self.args.flag_line_number);
|
|
|
|
searcher = searcher.invert_match(self.args.flag_invert_match);
|
2016-09-02 03:56:23 +02:00
|
|
|
searcher = searcher.before_context(
|
|
|
|
self.args.flag_before_context);
|
2016-08-29 02:18:34 +02:00
|
|
|
if let Err(err) = searcher.run() {
|
|
|
|
eprintln!("{}", err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
let outbuf = printer.into_inner();
|
|
|
|
if !outbuf.is_empty() {
|
|
|
|
let mut stdout = self.stdout.lock();
|
|
|
|
let _ = stdout.write_all(&outbuf);
|
|
|
|
let _ = stdout.flush();
|
|
|
|
}
|
|
|
|
self.outbuf = Some(outbuf);
|
2016-08-28 07:37:12 +02:00
|
|
|
}
|
|
|
|
}
|
2016-03-30 03:21:34 +02:00
|
|
|
}
|