//! grep [-i] [-v] [-n] pattern [file...] -- print lines containing //! `pattern` (plain text, no regular expressions). -i: ignore case, //! -v: lines that do *not* match, -n: show line numbers. //! Exit code 0 if something matched, 1 if not, 2 on errors (like grep). #![no_std] #![no_main] extern crate ulib; ulib::entry!(main); use ulib::*; fn contains(line: &[u8], pattern: &[u8], ignore_case: bool) -> bool { pattern.is_empty() || line.windows(pattern.len()).any(|w| match ignore_case { true => w.eq_ignore_ascii_case(pattern), false => w == pattern, }) } fn main() -> i64 { let (mut ignore_case, mut invert, mut numbers) = (false, false, false); let mut i = 1; while let Some(a) = arg(i).filter(|a| a.starts_with('-') && a.len() > 1) { for flag in a[1..].chars() { match flag { 'i' => ignore_case = true, 'v' => invert = true, 'n' => numbers = true, _ => { eprintln!("grep: unknown option -{}", flag); return 2; } } } i += 1; } let Some(pattern) = arg(i) else { eprintln!("usage: grep [-i] [-v] [-n] pattern [file...]"); return 2; }; let files = i + 1..argc(); let many = files.len() > 1; let (mut matched, mut status) = (false, 0); let mut search = |fd: i64, name: &str| { let mut number = 0; for_each_line(fd, |line| { number += 1; if contains(line, pattern.as_bytes(), ignore_case) != invert { matched = true; if many { print!("{}:", name); } if numbers { print!("{}:", number); } write_all(1, line); write(1, b"\n"); } }) }; if files.is_empty() { search(0, ""); } for f in files { let name = arg(f).unwrap_or(""); let fd = open(name, 0); let r = if fd < 0 { fd } else { search(fd, name) }; if fd >= 0 { close(fd); } if r < 0 { eprintln!("grep: {}: {}", name, strerror(r)); status = 2; } } if status != 0 { status } else if matched { 0 } else { 1 } }