sysutils: basic grep-like utility
This commit is contained in:
parent
4a7aa8d831
commit
770021df6a
@ -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"
|
||||
|
102
userspace/sysutils/src/grep.rs
Normal file
102
userspace/sysutils/src/grep.rs
Normal 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, ®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
|
||||
}
|
||||
}
|
||||
}
|
@ -1 +1,5 @@
|
||||
fn main() {}
|
||||
fn main() {
|
||||
for i in 0..8 {
|
||||
println!("\x1B[3{i}mHello\x1B[1molleH\x1B[0m");
|
||||
}
|
||||
}
|
||||
|
@ -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"),
|
||||
|
Loading…
x
Reference in New Issue
Block a user