sysutils: add a simple dd
This commit is contained in:
parent
3370b35811
commit
0752f39089
1
build.sh
1
build.sh
@ -59,6 +59,7 @@ pack_initrd() {
|
||||
cp ${build_dir}/cat ${root_dir}/bin/
|
||||
cp ${build_dir}/hexd ${root_dir}/bin/
|
||||
cp ${build_dir}/colors ${root_dir}/bin/
|
||||
cp ${build_dir}/dd ${root_dir}/bin/
|
||||
|
||||
# red
|
||||
cp ${build_dir}/red ${root_dir}/bin/red
|
||||
|
@ -50,3 +50,7 @@ path = "src/hexd.rs"
|
||||
[[bin]]
|
||||
name = "colors"
|
||||
path = "src/colors.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "dd"
|
||||
path = "src/dd.rs"
|
||||
|
130
sysutils/src/dd.rs
Normal file
130
sysutils/src/dd.rs
Normal file
@ -0,0 +1,130 @@
|
||||
#![feature(yggdrasil_os)]
|
||||
use std::{
|
||||
io::{self, Read, Seek, SeekFrom, Write},
|
||||
process::ExitCode,
|
||||
};
|
||||
|
||||
use clap::Parser;
|
||||
use sysutils::{Input, Output};
|
||||
|
||||
#[derive(Parser)]
|
||||
struct Args {
|
||||
#[arg(short)]
|
||||
source: Option<String>,
|
||||
#[arg(short)]
|
||||
destination: Option<String>,
|
||||
|
||||
#[arg(long)]
|
||||
src_skip: Option<u64>,
|
||||
#[arg(long, default_value_t = 512)]
|
||||
src_bs: u64,
|
||||
#[arg(short, long, default_value_t = usize::MAX)]
|
||||
count: usize,
|
||||
|
||||
// TODO: remove this when pipes are a thing
|
||||
#[arg(short = 'x', long)]
|
||||
as_hex: bool
|
||||
}
|
||||
|
||||
fn dump_block(offset: u64, data: &[u8]) {
|
||||
const WINDOW_SIZE: usize = 16;
|
||||
let window_count = (data.len() + WINDOW_SIZE) / WINDOW_SIZE;
|
||||
|
||||
for iw in 0..window_count {
|
||||
let off = iw * WINDOW_SIZE;
|
||||
let len = core::cmp::min(data.len() - off, WINDOW_SIZE);
|
||||
if len == 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
let window = &data[off..off + len];
|
||||
|
||||
print!("{:08X}: ", offset + off as u64);
|
||||
for i in 0..WINDOW_SIZE {
|
||||
if i < window.len() {
|
||||
print!("{:02X}", window[i]);
|
||||
} else {
|
||||
print!(" ");
|
||||
}
|
||||
|
||||
if i % 2 == 1 {
|
||||
print!(" ");
|
||||
}
|
||||
}
|
||||
|
||||
for &ch in window {
|
||||
if ch.is_ascii_graphic() || ch == b' ' {
|
||||
print!("{}", ch as char);
|
||||
} else {
|
||||
print!(".");
|
||||
}
|
||||
}
|
||||
|
||||
println!();
|
||||
}
|
||||
}
|
||||
|
||||
fn run<I: Read + Seek, O: Write>(
|
||||
mut input: I,
|
||||
mut output: O,
|
||||
src_position: u64,
|
||||
src_block_size: u64,
|
||||
mut count: usize,
|
||||
as_hex: bool,
|
||||
) -> io::Result<()> {
|
||||
let mut block = vec![0; src_block_size as usize];
|
||||
let mut offset = 0;
|
||||
|
||||
input.seek(SeekFrom::Start(src_position * src_block_size))?;
|
||||
|
||||
while count != 0 {
|
||||
let read_count = input.read(&mut block)?;
|
||||
|
||||
if read_count == 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
if as_hex {
|
||||
dump_block((src_position + offset) * src_block_size, &block[..read_count]);
|
||||
} else {
|
||||
output.write(&block[..read_count])?;
|
||||
}
|
||||
|
||||
count -= 1;
|
||||
offset += 1;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() -> ExitCode {
|
||||
let args = Args::parse();
|
||||
|
||||
let src_path = args.source.as_deref().unwrap_or("-");
|
||||
let dst_path = args.destination.as_deref().unwrap_or("-");
|
||||
|
||||
if src_path == dst_path && src_path != "-" {
|
||||
eprintln!("Input and output cannot be the same: {}", src_path);
|
||||
return ExitCode::FAILURE;
|
||||
}
|
||||
|
||||
if args.as_hex && dst_path != "-" {
|
||||
eprintln!("--as-hex requires stdout output");
|
||||
return ExitCode::FAILURE;
|
||||
}
|
||||
|
||||
let input = Input::open_str(src_path).unwrap();
|
||||
let output = Output::open_str(dst_path).unwrap();
|
||||
|
||||
let src_position = args.src_skip.unwrap_or(0);
|
||||
|
||||
match run(input, output, src_position, args.src_bs, args.count, args.as_hex) {
|
||||
Ok(_) => {
|
||||
ExitCode::SUCCESS
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("Error: {}", e);
|
||||
ExitCode::FAILURE
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
use std::{
|
||||
fs::File,
|
||||
io::{self, Read},
|
||||
io::{self, Read, Write, Seek, SeekFrom},
|
||||
};
|
||||
|
||||
#[cfg(unix)]
|
||||
@ -65,6 +65,11 @@ pub enum Input {
|
||||
File(io::BufReader<File>),
|
||||
}
|
||||
|
||||
pub enum Output {
|
||||
Stdout(io::Stdout),
|
||||
File(io::BufWriter<File>),
|
||||
}
|
||||
|
||||
impl Input {
|
||||
pub fn open_str(arg: &str) -> io::Result<Self> {
|
||||
if arg == "-" {
|
||||
@ -97,3 +102,61 @@ impl Read for Input {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Seek for Input {
|
||||
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
|
||||
match self {
|
||||
Self::Stdin(_) => todo!(),
|
||||
Self::File(value) => value.seek(pos),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Output {
|
||||
pub fn open_str(arg: &str) -> io::Result<Self> {
|
||||
if arg == "-" {
|
||||
Ok(Self::Stdout(io::stdout()))
|
||||
} else {
|
||||
let file = File::create(arg)?;
|
||||
let writer = io::BufWriter::new(file);
|
||||
Ok(Self::File(writer))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<io::Stdout> for Output {
|
||||
fn from(value: io::Stdout) -> Self {
|
||||
Self::Stdout(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<File> for Output {
|
||||
fn from(value: File) -> Self {
|
||||
Self::File(io::BufWriter::new(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for Output {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
match self {
|
||||
Self::Stdout(value) => value.write(buf),
|
||||
Self::File(value) => value.write(buf),
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
match self {
|
||||
Self::Stdout(value) => value.flush(),
|
||||
Self::File(value) => value.flush(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Seek for Output {
|
||||
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
|
||||
match self {
|
||||
Self::Stdout(_) => todo!(),
|
||||
Self::File(value) => value.seek(pos),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user