From 770021df6a414e3398331ed4985ba3fa282b1323 Mon Sep 17 00:00:00 2001 From: Mark Poliakov Date: Sat, 1 Mar 2025 01:37:31 +0200 Subject: [PATCH] sysutils: basic grep-like utility --- userspace/sysutils/Cargo.toml | 6 ++ userspace/sysutils/src/grep.rs | 102 +++++++++++++++++++++++++++++++++ userspace/sysutils/src/tst.rs | 6 +- xtask/src/build/userspace.rs | 1 + 4 files changed, 114 insertions(+), 1 deletion(-) create mode 100644 userspace/sysutils/src/grep.rs diff --git a/userspace/sysutils/Cargo.toml b/userspace/sysutils/Cargo.toml index e5680c71..e8804082 100644 --- a/userspace/sysutils/Cargo.toml +++ b/userspace/sysutils/Cargo.toml @@ -24,6 +24,8 @@ serde_json.workspace = true sha2.workspace = true chrono.workspace = true +# Own regex implementation? +regex = "1.11.1" pci-ids = { version = "0.2.5" } init = { path = "../init" } @@ -97,6 +99,10 @@ path = "src/dd.rs" name = "view" path = "src/view.rs" +[[bin]] +name = "grep" +path = "src/grep.rs" + [[bin]] name = "chmod" path = "src/chmod.rs" diff --git a/userspace/sysutils/src/grep.rs b/userspace/sysutils/src/grep.rs new file mode 100644 index 00000000..0acfe9cf --- /dev/null +++ b/userspace/sysutils/src/grep.rs @@ -0,0 +1,102 @@ +#![feature(seek_stream_len, slice_split_once, slice_internals)] +#![allow(internal_features)] +use std::{ + fs::File, + io::{self, stdout, Seek, Write}, + path::{Path, PathBuf}, + process::ExitCode, +}; + +use clap::Parser; +use cross::mem::FileMapping; +use regex::bytes::Regex; + +#[derive(Debug, thiserror::Error)] +enum Error { + #[error("{0}")] + Io(#[from] io::Error), + #[error("Invalid expression: {0}")] + Regex(#[from] regex::Error), +} + +trait Input { + fn next_line(&mut self) -> io::Result>; +} + +struct FileInput { + mmap: FileMapping, + position: usize, +} + +impl FileInput { + pub fn open>(path: P) -> io::Result { + let mut file = File::open(path)?; + let size = file.stream_len()?.try_into().unwrap(); + // TODO handle non-mmap()able files + let mmap = FileMapping::map(file, size)?; + Ok(Self { mmap, position: 0 }) + } +} + +impl Input for FileInput { + fn next_line(&mut self) -> io::Result> { + if self.position >= self.mmap.len() { + return Ok(None); + } + + if let Some(nl) = core::slice::memchr::memchr(b'\n', &self.mmap[self.position..]) { + let line = &self.mmap[self.position..self.position + nl]; + self.position += line.len() + 1; + Ok(Some(line)) + } else { + let slice = &self.mmap[self.position..]; + self.position = self.mmap.len(); + Ok(Some(slice)) + } + } +} + +fn run_on(input: &mut I, regex: &Regex) -> io::Result { + let mut any = false; + loop { + let Some(line) = input.next_line()? else { + break; + }; + + if regex.is_match(line) { + any = true; + stdout().write_all(line).ok(); + stdout().write_all(b"\n").ok(); + } + } + Ok(any) +} + +#[derive(Debug, Parser)] +struct Args { + expression: String, + filename: PathBuf, +} + +fn run(args: &Args) -> Result { + let regex = Regex::new(&args.expression)?; + let mut input = FileInput::open(&args.filename)?; + let any = run_on(&mut input, ®ex)?; + Ok(any) +} + +fn main() -> ExitCode { + let args = Args::parse(); + match run(&args) { + Ok(true) => { + ExitCode::SUCCESS + } + Ok(false) => { + ExitCode::FAILURE + } + Err(error) => { + eprintln!("{error}"); + ExitCode::FAILURE + } + } +} diff --git a/userspace/sysutils/src/tst.rs b/userspace/sysutils/src/tst.rs index f328e4d9..f77cc528 100644 --- a/userspace/sysutils/src/tst.rs +++ b/userspace/sysutils/src/tst.rs @@ -1 +1,5 @@ -fn main() {} +fn main() { + for i in 0..8 { + println!("\x1B[3{i}mHello\x1B[1molleH\x1B[0m"); + } +} diff --git a/xtask/src/build/userspace.rs b/xtask/src/build/userspace.rs index b3376c8b..41ddf620 100644 --- a/xtask/src/build/userspace.rs +++ b/xtask/src/build/userspace.rs @@ -40,6 +40,7 @@ const PROGRAMS: &[(&str, &str)] = &[ ("dd", "bin/dd"), ("random", "bin/random"), ("view", "bin/view"), + ("grep", "bin/grep"), ("chmod", "bin/chmod"), ("sha256sum", "bin/sha256sum"), ("sysmon", "bin/sysmon"),