diff --git a/README.md b/README.md index ecbb902..e7787d8 100644 --- a/README.md +++ b/README.md @@ -28,17 +28,21 @@ Arguments: [PATH] The path to the file to read Options: - -c, --color Color to use [default: red] [possible values: red, green, blue, yellow, magenta, cyan, white, black, bright-red, bright-green, bright-blue, bright-yellow, bright-magenta, bright-cyan, bright-white] - -b, --bold Bold - -u, --underline Underline - -i, --italic Italic - -s, --strike StrikeThrough - -l, --line-numbers Show LineNumbers - -R, --regex Pattern is a Regex - -I, --insensitive Case Insensitive - -d, --debug Debug - -h, --help Print help - -V, --version Print version + -c, --color Color to use [default: red] [possible values: red, green, blue, yellow, magenta, cyan, white, black, bright-red, bright-green, bright-blue, bright-yellow, bright-magenta, bright-cyan, bright-white] + -b, --bold Bold + -u, --underline Underline + -i, --italic Italic + -s, --strike StrikeThrough + -l, --line-numbers Show LineNumbers + -R, --regex Pattern is a Regex + -a, --after Show lines after the match + -b, --before Show lines before the match + -S, --section Show lines before and after the match + -t, --tabs_c Value of spaces to evaluate a tab + -I, --insensitive Case Insensitive + -d, --debug Debug + -h, --help Print help + -V, --version Print version ``` ## Setting up MyGrep with Windows Environment Variables @@ -60,6 +64,17 @@ To use `mygrep` from any location in the command prompt, you need to add it to y Now, you should be able to use `mygrep` from any location in the command prompt. Just type `mygrep` followed by your commands. +# Build it youself +```bash +git clone +cd mygrep +cargo build --release +``` +Move it to the path +```bash +cp target/release/mygrep /usr/local/bin +``` + # Exit Codes ``` 0: Success diff --git a/src/main.rs b/src/main.rs index 584c15e..2ac7576 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,14 +4,14 @@ use colored::*; use std::io::{self, BufRead}; use regex::Regex; use std::error::Error; -use std::process; +use std::{process, vec}; /// Search for a pattern in a file and display the lines that contain it. #[derive(Parser, Debug, Clone)] #[command(name = "MyGrep")] #[command(author = "Riccardo Bella ")] -#[command(version = "1.0.0")] +#[command(version = "1.1.0")] #[command(about = "Grep Equivalent", long_about = None)] #[command(author, version, about, long_about = None)] #[command(about, version, after_help = @@ -26,6 +26,10 @@ use std::process; The line_numbers option is used to show the line number of the pattern found.\n\ The regex option is used to search for a regex pattern.\n\ The insensitive option is used to search for a case insensitive pattern.\n\ + The after option is used to print the number of lines after the match (not compatible with section).\n\ + The before option is used to print the number of lines before the match (not compatible with section).\n\ + The section option is used to print the section (same indentation or more) of the file where the pattern is found. (Not compatible with after and or before)\n\ + The tabs_c option is used to set the number of spaces for a tab. Default is 4.\n\ The debug option is used to print all the args for debug.\n\ \n\ Example:\n\ @@ -53,7 +57,7 @@ struct Cli { color: Colors, /// Bold - #[arg(short, long, default_value_t = true)] + #[arg(short = 'B', long, default_value_t = true)] bold: bool, /// Underline @@ -80,6 +84,22 @@ struct Cli { #[arg(short = 'I', long, default_value_t = false)] insensitive: bool, + /// After + #[arg(short, long, default_value_t = 0)] + after: usize, + + /// Before + #[arg(short, long, default_value_t = 0)] + before: usize, + + /// Section + #[arg(short = 'S', long, default_value_t = false)] + section: bool, + + /// Tabs count + #[arg(short, long, default_value_t = 4)] + tabs_c: usize, + /// Debug #[arg(short, long, default_value_t = false)] debug: bool, @@ -136,8 +156,17 @@ fn get_grep_indexes(line: &str, regex_pattern: &str) -> Vec<(usize, usize)>{ indexes } +fn indentation(line: &str, tabs_c: usize) -> usize { + // return the number of spaces at the beginning of the line + let spaces = line.chars().take_while(|&c| c == ' ').count(); + let tabs = line.chars().take_while(|&c| c == '\t').count() * tabs_c; + + spaces + tabs + +} + fn main() -> Result<()> { - let args = Cli::parse(); + let mut args = Cli::parse(); // print all args for debug if args.debug { @@ -152,6 +181,9 @@ fn main() -> Result<()> { println!("args.line_numbers: {}", args.line_numbers); println!("args.regex: {}", args.regex); println!("args.insensitive: {}", args.insensitive); + println!("args.after: {}", args.after); + println!("args.before: {}", args.before); + println!("args.section: {}", args.section); println!("args.debug: {}", args.debug); println!("----------------------------"); println!(); @@ -173,6 +205,17 @@ fn main() -> Result<()> { } } + // If section is true and before and after are != 0, print warning and set after and before to 0 + if args.section && (args.before != 0 || args.after != 0) { + eprintln!("Section is not compatible with after and or before. Ignoring After and or Before."); + args.after = 0; + args.before = 0; + } + // the section behaviour could print multiple time the same section, this avoid this behavior. + let mut sections_to_print: Vec = vec![]; + // init a found_rows to keep colored lines to be printed with found pattern, a tuple of index and string + let mut found_rows: Vec<(usize, String)> = vec![]; + // color must be string to use it in replace let color = format!("{:?}", args.color); @@ -218,8 +261,78 @@ fn main() -> Result<()> { if args.line_numbers { colored_line = format!("{}: {}", index+1, colored_line); } - println!("{}", colored_line); + + // Before lines, args.before is the number of lines to print before the match, default 0 + if args.before > 0 { + if index >= 1 { + let mut before_indexes = vec![]; + for i in 1..=args.before { + if index < i { + break; + } + let tmp_index = index - i; + + before_indexes.push(tmp_index); + } + before_indexes.reverse(); + for i in before_indexes { + let before_line = content.lines().nth(i).unwrap(); + println!("{}", before_line); + } + } + } + + if !args.section{ + println!("{}", colored_line); + } + + // After lines, args.after is the number of lines to print after the match, default 0 + if args.after > 0 { + for i in 1..=args.after { + if index + i < content.lines().count() { + let after_line = content.lines().nth(index + i).unwrap(); + println!("{}", after_line); + } + } + } + + // Section, if true print the section. + // Section is the same indentation or more of the line where the pattern is found. + // Section can start before the finding + if args.section { + // init indentation + let starting_indentation = indentation(&line, args.tabs_c); + sections_to_print.push(index); + // keep the index where pattern is found along with colored string + found_rows.push((index, colored_line)); + + // scroll backwards indexes, break if indentation is <= starting_indentation + for i in (0..index).rev() { + let tmp_line = content.lines().nth(i).unwrap(); + let tmp_indentation = indentation(&tmp_line, args.tabs_c); + if tmp_indentation < starting_indentation { + // if indentation is <= starting_indentation, break + // get the index as head of the section + sections_to_print.push(i); + break; + } + sections_to_print.push(i); + } + + // scroll forward indexes, break if indentation is < starting_indentation + for i in index..content.lines().count() { + let tmp_line = content.lines().nth(i).unwrap(); + let tmp_indentation = indentation(&tmp_line, args.tabs_c); + if tmp_indentation < starting_indentation { + // if indentation is < starting_indentation, break + break; + } + sections_to_print.push(i); + } + + } } + }, Err(_) => { let error_message = "Pattern is not a valid regex: ".color("red").bold().to_string(); @@ -229,5 +342,29 @@ fn main() -> Result<()> { } } + if args.section{ + // sections_to_print is a list olf indexes to print, but are unordered and maybe duplicate. + // we need to sort and remove duplicates + sections_to_print.sort(); + sections_to_print.dedup(); + + // print all indexes in sections_to_print + for index in sections_to_print { + // if index in found_rows, print the colored line instead of the normal line + if found_rows.iter().any(|x| x.0 == index) { + let colored_line = found_rows.iter().find(|&x| x.0 == index).unwrap().1.clone(); + println!("{}", colored_line); + continue; + } + + let line = content.lines().nth(index).unwrap(); + if args.line_numbers { + println!("{}: {}", index+1, line); + } else { + println!("{}", line); + } + } + } + Ok(()) } diff --git a/test_after_before.txt b/test_after_before.txt new file mode 100644 index 0000000..0a118a5 --- /dev/null +++ b/test_after_before.txt @@ -0,0 +1,10 @@ +1 +2 +3 +4 +5 +6 +7 +8 +9 +0 \ No newline at end of file diff --git a/test.txt b/test_base.txt similarity index 99% rename from test.txt rename to test_base.txt index eaa4ed6..160a88b 100644 --- a/test.txt +++ b/test_base.txt @@ -7,6 +7,8 @@ roba@gmail.com non&unamai@falsa.com mail_corretta@gmail.com +ciao ciao ciao ciao + 21:46:19.693601 IP 10.10.1.10.60460 > 10.10.1.199.5432: Flags [S], seq 116466344, win 29200, options [mss 1460,sackOK,TS val 3547090332 ecr 0,nop,wscale 7], length 0 21:46:19.693626 IP 10.10.1.10.35470 > 10.10.1.199.513: Flags [S], seq 3400074709, win 29200, options [mss 1460,sackOK,TS val 3547090332 ecr 0,nop,wscale 7], length 0 21:46:19.693762 IP 10.10.1.10.44244 > 10.10.1.199.389: Flags [S], seq 2214070267, win 29200, options [mss 1460,sackOK,TS val 3547090333 ecr 0,nop,wscale 7], length 0 diff --git a/test_indentation.txt b/test_indentation.txt new file mode 100644 index 0000000..8a398a4 --- /dev/null +++ b/test_indentation.txt @@ -0,0 +1,15 @@ +section 1 + item abc + item bcd + item cde + subitem xyz + subitem wxy + item def +section 2 + item efg + item fgh + item ghi + last + item hik +section 3 +section 4 \ No newline at end of file