mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-28 19:47:45 +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
48
src/od/od.rs
48
src/od/od.rs
|
@ -21,6 +21,12 @@ use std::io;
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum Radix { Decimal, Hexadecimal, Octal, Binary }
|
enum Radix { Decimal, Hexadecimal, Octal, Binary }
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum InputSource<'a> {
|
||||||
|
FileName(&'a str ),
|
||||||
|
Stdin
|
||||||
|
}
|
||||||
|
|
||||||
pub fn uumain(args: Vec<String>) -> i32 {
|
pub fn uumain(args: Vec<String>) -> i32 {
|
||||||
let mut opts = getopts::Options::new();
|
let mut opts = getopts::Options::new();
|
||||||
|
|
||||||
|
@ -53,39 +59,41 @@ pub fn uumain(args: Vec<String>) -> i32 {
|
||||||
Err(f) => { panic!("Invalid -A/--address-radix\n{}", f) }
|
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..]
|
let fnames = args[1..]
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|w| !w.starts_with('-'))
|
.filter(|w| !w.starts_with('-') || w == &"--" ) // "--" starts with '-', but it denotes stdin, not a flag
|
||||||
.map(|x| x.clone())
|
.map(|x| match x.as_str() { "--" => InputSource::Stdin, x => InputSource::FileName(x)})
|
||||||
.collect::<Vec<_>>();
|
.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 {
|
if fnames.len() == 0 {
|
||||||
panic!("Need fname for now") ;
|
odfunc(&input_offset_base, &[InputSource::Stdin])
|
||||||
};
|
}
|
||||||
odfunc(&input_offset_base, fnames)
|
else {
|
||||||
|
odfunc(&input_offset_base, &fnames)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const LINEBYTES:usize = 16;
|
const LINEBYTES:usize = 16;
|
||||||
const WORDBYTES:usize = 2;
|
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 status = 0;
|
||||||
let mut ni = fnames.iter();
|
let mut ni = fnames.iter();
|
||||||
{
|
{
|
||||||
// Open and return the next file to process as a BufReader
|
// Open and return the next file to process as a BufReader
|
||||||
// Returns None when no more files.
|
// 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 retries with subsequent files if err - normally 'loops' once
|
||||||
loop {
|
loop {
|
||||||
let fname = match ni.next() {
|
match ni.next() {
|
||||||
None => return None,
|
None => return None,
|
||||||
Some(s) => s,
|
Some(input) => match *input {
|
||||||
};
|
InputSource::Stdin => return Some(Box::new(BufReader::new(std::io::stdin()))),
|
||||||
match File::open(fname) {
|
InputSource::FileName(fname) => match File::open(fname) {
|
||||||
Ok(f) => return Some(BufReader::new(f)),
|
Ok(f) => return Some(Box::new(BufReader::new(f))),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
// If any file can't be opened,
|
// If any file can't be opened,
|
||||||
// print an error at the time that the file is needed,
|
// print an error at the time that the file is needed,
|
||||||
|
@ -96,9 +104,11 @@ fn odfunc(input_offset_base: &Radix, fnames: Vec<String>) -> i32 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut curr_file: BufReader<File> = match next_file() {
|
let mut curr_file: Box<io::Read> = match next_file() {
|
||||||
Some(f) => f,
|
Some(f) => f,
|
||||||
None => {
|
None => {
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -117,14 +127,18 @@ fn odfunc(input_offset_base: &Radix, fnames: Vec<String>) -> i32 {
|
||||||
Ok(0)
|
Ok(0)
|
||||||
} else {
|
} else {
|
||||||
let mut xfrd = 0;
|
let mut xfrd = 0;
|
||||||
while xfrd < buf.len() {
|
// 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..]) {
|
xfrd += match curr_file.read(&mut buf[xfrd..]) {
|
||||||
|
Ok(0) => break,
|
||||||
Ok(n) => n,
|
Ok(n) => n,
|
||||||
Err(e) => panic!("file error: {}", e),
|
Err(e) => panic!("file error: {}", e),
|
||||||
};
|
};
|
||||||
if xfrd == buf.len() {
|
if xfrd == buf.len() {
|
||||||
// transferred all that was asked for.
|
// transferred all that was asked for.
|
||||||
break;
|
break 'fillloop;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
curr_file = match next_file() {
|
curr_file = match next_file() {
|
||||||
Some(f) => f,
|
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();
|
let result = ucmd.arg(file.as_os_str()).run();
|
||||||
|
|
||||||
assert!(!result.success);
|
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