sysutils: basic grep-like utility

This commit is contained in:
Mark Poliakov 2025-03-01 01:37:31 +02:00
parent 4a7aa8d831
commit 770021df6a
4 changed files with 114 additions and 1 deletions

View File

@ -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"

View File

@ -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<Option<&[u8]>>;
}
struct FileInput {
mmap: FileMapping,
position: usize,
}
impl FileInput {
pub fn open<P: AsRef<Path>>(path: P) -> io::Result<Self> {
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<Option<&[u8]>> {
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<I: Input>(input: &mut I, regex: &Regex) -> io::Result<bool> {
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<bool, Error> {
let regex = Regex::new(&args.expression)?;
let mut input = FileInput::open(&args.filename)?;
let any = run_on(&mut input, &regex)?;
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
}
}
}

View File

@ -1 +1,5 @@
fn main() {}
fn main() {
for i in 0..8 {
println!("\x1B[3{i}mHello\x1B[1molleH\x1B[0m");
}
}

View File

@ -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"),