mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-28 11:37:44 +00:00
od: take input from stdin, as well as files.
This commit is contained in:
parent
365b342792
commit
3f356a4190
2 changed files with 91 additions and 37 deletions
74
src/od/od.rs
74
src/od/od.rs
|
@ -21,6 +21,12 @@ use std::io;
|
|||
#[derive(Debug)]
|
||||
enum Radix { Decimal, Hexadecimal, Octal, Binary }
|
||||
|
||||
#[derive(Debug)]
|
||||
enum InputSource<'a> {
|
||||
FileName(&'a str ),
|
||||
Stdin
|
||||
}
|
||||
|
||||
pub fn uumain(args: Vec<String>) -> i32 {
|
||||
let mut opts = getopts::Options::new();
|
||||
|
||||
|
@ -53,52 +59,56 @@ pub fn uumain(args: Vec<String>) -> i32 {
|
|||
Err(f) => { panic!("Invalid -A/--address-radix\n{}", f) }
|
||||
};
|
||||
|
||||
// Gather up file names - args wich don't start with '-'
|
||||
// Gather up file names - args which don't start with '-'
|
||||
let fnames = args[1..]
|
||||
.iter()
|
||||
.filter(|w| !w.starts_with('-'))
|
||||
.map(|x| x.clone())
|
||||
.filter(|w| !w.starts_with('-') || w == &"--" ) // "--" starts with '-', but it denotes stdin, not a flag
|
||||
.map(|x| match x.as_str() { "--" => InputSource::Stdin, x => InputSource::FileName(x)})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// With no filenames, od would use stdin as input, which is currently not supported.
|
||||
// With no filenames, od uses stdin as input.
|
||||
if fnames.len() == 0 {
|
||||
panic!("Need fname for now") ;
|
||||
};
|
||||
odfunc(&input_offset_base, fnames)
|
||||
odfunc(&input_offset_base, &[InputSource::Stdin])
|
||||
}
|
||||
else {
|
||||
odfunc(&input_offset_base, &fnames)
|
||||
}
|
||||
}
|
||||
|
||||
const LINEBYTES:usize = 16;
|
||||
const WORDBYTES:usize = 2;
|
||||
|
||||
fn odfunc(input_offset_base: &Radix, fnames: Vec<String>) -> i32 {
|
||||
fn odfunc(input_offset_base: &Radix, fnames: &[InputSource]) -> i32 {
|
||||
|
||||
let mut status = 0;
|
||||
let mut ni = fnames.iter();
|
||||
{
|
||||
// Open and return the next file to process as a BufReader
|
||||
// Returns None when no more files.
|
||||
let mut next_file = || -> Option<BufReader<File>> {
|
||||
let mut next_file = || -> Option<Box<io::Read>> {
|
||||
// loop retries with subsequent files if err - normally 'loops' once
|
||||
loop {
|
||||
let fname = match ni.next() {
|
||||
match ni.next() {
|
||||
None => return None,
|
||||
Some(s) => s,
|
||||
};
|
||||
match File::open(fname) {
|
||||
Ok(f) => return Some(BufReader::new(f)),
|
||||
Err(e) => {
|
||||
// If any file can't be opened,
|
||||
// print an error at the time that the file is needed,
|
||||
// then move on the the next file.
|
||||
// This matches the behavior of the original `od`
|
||||
let _ = writeln!(&mut std::io::stderr(), "od: '{}': {}", fname, e);
|
||||
if status == 0 {status = 1}
|
||||
Some(input) => match *input {
|
||||
InputSource::Stdin => return Some(Box::new(BufReader::new(std::io::stdin()))),
|
||||
InputSource::FileName(fname) => match File::open(fname) {
|
||||
Ok(f) => return Some(Box::new(BufReader::new(f))),
|
||||
Err(e) => {
|
||||
// If any file can't be opened,
|
||||
// print an error at the time that the file is needed,
|
||||
// then move on the the next file.
|
||||
// This matches the behavior of the original `od`
|
||||
let _ = writeln!(&mut std::io::stderr(), "od: '{}': {}", fname, e);
|
||||
if status == 0 {status = 1}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let mut curr_file: BufReader<File> = match next_file() {
|
||||
let mut curr_file: Box<io::Read> = match next_file() {
|
||||
Some(f) => f,
|
||||
None => {
|
||||
return 1;
|
||||
|
@ -117,14 +127,18 @@ fn odfunc(input_offset_base: &Radix, fnames: Vec<String>) -> i32 {
|
|||
Ok(0)
|
||||
} else {
|
||||
let mut xfrd = 0;
|
||||
while xfrd < buf.len() {
|
||||
xfrd += match curr_file.read(&mut buf[xfrd..]) {
|
||||
Ok(n) => n,
|
||||
Err(e) => panic!("file error: {}", e),
|
||||
};
|
||||
if xfrd == buf.len() {
|
||||
// transferred all that was asked for.
|
||||
break;
|
||||
// while buffer we are filling is not full.. May go thru several files.
|
||||
'fillloop: while xfrd < buf.len() {
|
||||
loop { // stdin may return on 'return' (enter), even though the buffer isn't full.
|
||||
xfrd += match curr_file.read(&mut buf[xfrd..]) {
|
||||
Ok(0) => break,
|
||||
Ok(n) => n,
|
||||
Err(e) => panic!("file error: {}", e),
|
||||
};
|
||||
if xfrd == buf.len() {
|
||||
// transferred all that was asked for.
|
||||
break 'fillloop;
|
||||
}
|
||||
}
|
||||
curr_file = match next_file() {
|
||||
Some(f) => f,
|
||||
|
|
40
tests/od.rs
40
tests/od.rs
|
@ -83,5 +83,45 @@ fn test_no_file() {
|
|||
let result = ucmd.arg(file.as_os_str()).run();
|
||||
|
||||
assert!(!result.success);
|
||||
}
|
||||
|
||||
// Test that od reads from stdin instead of a file
|
||||
#[test]
|
||||
fn test_from_stdin() {
|
||||
let (_, mut ucmd) = testing(UTIL_NAME);
|
||||
|
||||
let input = "abcdefghijklmnopqrstuvwxyz\n";
|
||||
let result = ucmd.run_piped_stdin(input.as_bytes());
|
||||
|
||||
assert_empty_stderr!(result);
|
||||
assert!(result.success);
|
||||
assert_eq!(result.stdout, ALPHA_OUT);
|
||||
|
||||
}
|
||||
|
||||
// Test that od reads from stdin and also from files
|
||||
#[test]
|
||||
fn test_from_mixed() {
|
||||
let (_, mut ucmd) = testing(UTIL_NAME);
|
||||
|
||||
let temp = env::var("TMPDIR").unwrap_or_else(|_| env::var("TEMP").unwrap());
|
||||
let tmpdir = Path::new(&temp);
|
||||
let file1 = tmpdir.join("test-1");
|
||||
let file3 = tmpdir.join("test-3");
|
||||
|
||||
let (data1, data2, data3) = ("abcdefg","hijklmnop","qrstuvwxyz\n");
|
||||
for &(path,data)in &[(&file1, data1),(&file3, data3)] {
|
||||
let mut f = File::create(&path).unwrap();
|
||||
match f.write_all(data.as_bytes()) {
|
||||
Err(_) => panic!("Test setup failed - could not write file"),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
let result = ucmd.arg(file1.as_os_str()).arg("--").arg(file3.as_os_str()).run_piped_stdin(data2.as_bytes());
|
||||
|
||||
assert_empty_stderr!(result);
|
||||
assert!(result.success);
|
||||
assert_eq!(result.stdout, ALPHA_OUT);
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue