grep_bin/
lib.rs

1use clap::{arg, command, ArgMatches};
2use std::process;
3use std::{env, path::PathBuf};
4
5use ansi_term::Colour;
6mod utils;
7
8use utils::{file, search};
9
10use crate::utils::{print_hexdump_output, PatternType};
11
12pub fn setup_args() -> ArgMatches {
13    let integer_validator = |value: &str| match value.parse::<usize>() {
14        Ok(_) => Ok(()),
15        Err(_) => Err(String::from("the value needs to be a valid integer")),
16    };
17
18    command!()
19        .arg(
20            arg!(<PATTERN> "Ascii strings should be passed inside quotes like so '\"This is a string\"'
21Escaping quotes '\"This is a \\\"quoted string\\\"\"'
22All of these byte sequence are valid: f9b4ca, F9B4CA and f9B4Ca"
23            ),
24        )
25        .arg(arg!(<FILE> ... "The filepath"))
26        .arg(
27            arg!(-f <filetype> ... "Filter the search by the file extensions.
28Examples of input: jpg, mp3, exe")
29            .required(false),
30        )
31        .arg(
32            arg!(-c <context_bytes_size> "Defines the number of bytes that will be printed in each line.")
33                .required(false)
34                .default_value("16")
35                .validator(integer_validator),
36        )
37        .arg(
38            arg!(-p --"print-only" "Prints only the filename that contais the match.")
39                .id("print_only")
40                .requires("PATTERN"),
41        )
42        .arg(
43            arg!(-o --"print-offset" "Prints only the offsets of the match.")
44                .id("print_offset")
45                .requires("PATTERN"),
46        )
47        .arg(
48            arg!(-s --"skip-bytes" <n> "Skip n bytes before searching.")
49                .id("skip_bytes")
50                .required(false)
51                .default_value("0")
52                .validator(integer_validator),
53        )
54        .arg_required_else_help(true)
55        .get_matches()
56}
57
58pub fn run(args: ArgMatches) {
59    let filetypes: Vec<&str> = args.values_of("filetype").unwrap_or_default().collect();
60
61    let filepaths: Vec<PathBuf> = args.values_of_t("FILE").unwrap();
62    let files: Vec<PathBuf> = if filetypes.is_empty() {
63        file::get_all_files_from_paths(filepaths)
64    } else {
65        file::filter_filetypes(file::get_all_files_from_paths(filepaths), &filetypes)
66    };
67
68    let pattern: Vec<u8> = match PatternType::from(args.value_of("PATTERN").unwrap()) {
69        PatternType::Str(pattern) => pattern.into_bytes(),
70
71        PatternType::HexStr(pattern) => hex::decode(pattern).unwrap_or_else(|error| {
72            eprintln!("Error: {} in byte sequence!", error);
73            process::exit(1);
74        }),
75    };
76
77    let context_bytes_size: usize = args.value_of("context_bytes_size").unwrap().parse().unwrap();
78    let skip_bytes: u64 = args.value_of("skip_bytes").unwrap().parse().unwrap();
79
80    for filename in files {
81        let mut searcher = search::Searcher::new(&pattern, context_bytes_size, skip_bytes);
82        let filename = filename.to_str().unwrap();
83
84        let result = searcher.search_in_file(filename).unwrap_or_else(|error| {
85            eprintln!("{}: {}", filename, error);
86            process::exit(1);
87        });
88
89        if !result.is_empty() {
90            println!("{}", Colour::Purple.paint(filename));
91        }
92
93        if args.is_present("print_offset") {
94            result
95                .iter()
96                .map(|_match| _match.offset)
97                .for_each(|offset| println!("0x{:08X}", offset));
98            return;
99        }
100
101        if !args.is_present("print_only") {
102            print_hexdump_output(&result, context_bytes_size);
103        }
104    }
105}