Merge pull request #369 from Arcterus/busybox-fixes

Fix more bugs to pass Busybox tests
This commit is contained in:
Heather 2014-07-25 11:38:02 +04:00
commit 5b1eee0e98
10 changed files with 283 additions and 114 deletions

2
.busybox-config Normal file
View file

@ -0,0 +1,2 @@
CONFIG_FEATURE_FANCY_HEAD=y
CONFIG_UNICODE_SUPPORT=y

View file

@ -268,9 +268,8 @@ $(BUILDDIR)/busybox: $(BUILDDIR)/uutils
ln -s $(BUILDDIR)/uutils $(BUILDDIR)/busybox
# This is a busybox-specific config file their test suite wants to parse.
# For now it's blank.
$(BUILDDIR)/.config: $(BUILDDIR)/uutils
touch $@
$(BUILDDIR)/.config: $(BASEDIR)/.busybox-config $(BUILDDIR)/uutils
cp $< $@
ifeq ($(BUSYBOX_SRC),)
busytest:

View file

@ -14,42 +14,41 @@ extern crate libc;
#[macro_export]
macro_rules! show_error(
($($args:expr),+) => ({
safe_write!(&mut ::std::io::stderr(), "{}: error: ", ::NAME);
safe_writeln!(&mut ::std::io::stderr(), $($args),+);
pipe_write!(&mut ::std::io::stderr(), "{}: error: ", ::NAME);
pipe_writeln!(&mut ::std::io::stderr(), $($args),+);
})
)
#[macro_export]
macro_rules! show_warning(
($($args:expr),+) => ({
safe_write!(&mut ::std::io::stderr(), "{}: warning: ", ::NAME);
safe_writeln!(&mut ::std::io::stderr(), $($args),+);
pipe_write!(&mut ::std::io::stderr(), "{}: warning: ", ::NAME);
pipe_writeln!(&mut ::std::io::stderr(), $($args),+);
})
)
#[macro_export]
macro_rules! show_info(
($($args:expr),+) => ({
safe_write!(&mut ::std::io::stderr(), "{}: ", ::NAME);
safe_writeln!(&mut ::std::io::stderr(), $($args),+);
pipe_write!(&mut ::std::io::stderr(), "{}: ", ::NAME);
pipe_writeln!(&mut ::std::io::stderr(), $($args),+);
})
)
#[macro_export]
macro_rules! eprint(
($($args:expr),+) => (safe_write!(&mut ::std::io::stderr(), $($args),+))
($($args:expr),+) => (pipe_write!(&mut ::std::io::stderr(), $($args),+))
)
#[macro_export]
macro_rules! eprintln(
($($args:expr),+) => (safe_writeln!(&mut ::std::io::stderr(), $($args),+))
($($args:expr),+) => (pipe_writeln!(&mut ::std::io::stderr(), $($args),+))
)
#[macro_export]
macro_rules! crash(
($exitcode:expr, $($args:expr),+) => ({
safe_write!(&mut ::std::io::stderr(), "{}: error: ", ::NAME);
safe_writeln!(&mut ::std::io::stderr(), $($args),+);
show_error!($($args),+);
unsafe { ::util::libc::exit($exitcode as ::util::libc::c_int); }
})
)
@ -84,12 +83,78 @@ macro_rules! return_if_err(
)
)
// XXX: should the pipe_* macros return an Err just to show the write failed?
#[macro_export]
macro_rules! pipe_print(
($($args:expr),+) => (
match write!(&mut ::std::io::stdout() as &mut Writer, $($args),+) {
Ok(_) => true,
Err(f) => {
if f.kind == ::std::io::BrokenPipe {
false
} else {
fail!("{}", f)
}
}
}
)
)
#[macro_export]
macro_rules! pipe_println(
($($args:expr),+) => (
match writeln!(&mut ::std::io::stdout() as &mut Writer, $($args),+) {
Ok(_) => true,
Err(f) => {
if f.kind == ::std::io::BrokenPipe {
false
} else {
fail!("{}", f)
}
}
}
)
)
#[macro_export]
macro_rules! pipe_write(
($fd:expr, $($args:expr),+) => (
match write!($fd, $($args),+) {
Ok(_) => true,
Err(f) => {
if f.kind == ::std::io::BrokenPipe {
false
} else {
fail!("{}", f)
}
}
}
)
)
#[macro_export]
macro_rules! pipe_writeln(
($fd:expr, $($args:expr),+) => (
match write!($fd, $($args),+) {
Ok(_) => true,
Err(f) => {
if f.kind == ::std::io::BrokenPipe {
false
} else {
fail!("{}", f)
}
}
}
)
)
#[macro_export]
macro_rules! safe_write(
($fd:expr, $($args:expr),+) => (
match write!($fd, $($args),+) {
Ok(_) => {}
Err(f) => { fail!(f.to_string()); }
Err(f) => fail!(f.to_string())
}
)
)
@ -99,7 +164,7 @@ macro_rules! safe_writeln(
($fd:expr, $($args:expr),+) => (
match writeln!($fd, $($args),+) {
Ok(_) => {}
Err(f) => { fail!(f.to_string()); }
Err(f) => fail!(f.to_string())
}
)
)

View file

@ -26,7 +26,6 @@ static NAME: &'static str = "fold";
static VERSION: &'static str = "1.0.0";
pub fn uumain(args: Vec<String>) -> int {
let (args, obs_width) = handle_obsolete(args.as_slice());
let program = args[0].clone();
@ -98,11 +97,15 @@ fn handle_obsolete(args: &[String]) -> (Vec<String>, Option<String>) {
fn fold(filenames: Vec<String>, bytes: bool, spaces: bool, width: uint) {
for filename in filenames.iter() {
let filename: &str = filename.as_slice();
let mut stdin_buf;
let mut file_buf;
let buffer = BufferedReader::new(
if filename == "-" {
box io::stdio::stdin_raw() as Box<Reader>
stdin_buf = io::stdio::stdin_raw();
&mut stdin_buf as &mut Reader
} else {
box safe_unwrap!(File::open(&Path::new(filename))) as Box<Reader>
file_buf = safe_unwrap!(File::open(&Path::new(filename)));
&mut file_buf as &mut Reader
}
);
fold_file(buffer, bytes, spaces, width);
@ -112,19 +115,24 @@ fn fold(filenames: Vec<String>, bytes: bool, spaces: bool, width: uint) {
fn fold_file<T: io::Reader>(file: BufferedReader<T>, bytes: bool, spaces: bool, width: uint) {
let mut file = file;
for line in file.lines() {
let line = safe_unwrap!(line);
if line.len() == 1 {
let line_string = safe_unwrap!(line);
let mut line = line_string.as_slice();
let len = line.len();
if line.char_at(len - 1) == '\n' {
if len == 1 {
println!("");
continue;
} else {
line = line.slice_to(len - 1);
}
}
let line = line.as_slice().slice_to(line.len() - 1);
if bytes {
let mut i = 0;
while i < line.len() {
let width = if line.len() - i >= width { width } else { line.len() - i };
let slice = {
let slice = line.slice(i, i + width);
if spaces && i + width != line.len() {
if spaces && i + width < line.len() {
match slice.rfind(|ch: char| ch.is_whitespace()) {
Some(m) => slice.slice_to(m + 1),
None => slice
@ -140,11 +148,40 @@ fn fold_file<T: io::Reader>(file: BufferedReader<T>, bytes: bool, spaces: bool,
let mut output = String::new();
let mut count = 0;
for (i, ch) in line.chars().enumerate() {
if count >= width {
let (val, ncount) = {
let slice = output.as_slice();
let (out, val, ncount) =
if spaces && i + 1 < line.len() {
match slice.rfind(|ch: char| ch.is_whitespace()) {
Some(m) => {
let routput = slice.slice_from(m + 1).to_string();
let ncount = routput.as_slice().chars().fold(0u, |out, ch: char| {
out + match ch {
'\t' => 8,
'\x08' => if out > 0 { -1 } else { 0 },
'\r' => return 0,
_ => 1
}
});
(slice.slice_to(m + 1), routput, ncount)
},
None => (slice, "".to_string(), 0)
}
} else {
(slice, "".to_string(), 0)
};
println!("{}", out);
(val, ncount)
};
output = val.into_string();
count = ncount;
}
match ch {
'\t' => {
count += 8;
if count > width {
println!("{}", output.as_slice());
println!("{}", output);
output.truncate(0);
count = 8;
}
@ -165,31 +202,9 @@ fn fold_file<T: io::Reader>(file: BufferedReader<T>, bytes: bool, spaces: bool,
_ => count += 1
};
output.push_char(ch);
if count == width {
let (val, ncount) = {
let slice = output.as_slice();
let (out, val, ncount) =
if spaces && i + 1 != line.len() {
match slice.rfind(|ch: char| ch.is_whitespace()) {
Some(m) => {
let routput = slice.slice_from(m + 1).to_string();
let ncount = routput.as_slice().chars().fold(0, |out, ch: char| out + if ch == '\t' { 8 } else { 1 });
(slice.slice_to(m + 1), routput, ncount)
},
None => (slice, "".to_string(), 0)
}
} else {
(slice, "".to_string(), 0)
};
println!("{}", out);
(val, ncount)
};
output = val.into_string();
count = ncount;
}
}
if count > 0 {
println!("{}", output);
print!("{}", output);
}
}
}

View file

@ -151,20 +151,20 @@ pub fn uumain(args: Vec<String>) -> int {
}
fn version() {
println!("{} v{}", NAME, VERSION);
pipe_println!("{} v{}", NAME, VERSION);
}
fn usage(program: &str, binary_name: &str, opts: &[getopts::OptGroup]) {
version();
println!("");
println!("Usage:");
pipe_println!("");
pipe_println!("Usage:");
if is_custom_binary(binary_name) {
println!(" {} [OPTION]... [FILE]...", program);
pipe_println!(" {} [OPTION]... [FILE]...", program);
} else {
println!(" {} {{--md5|--sha1|--sha224|--sha256|--sha384|--sha512}} [OPTION]... [FILE]...", program);
pipe_println!(" {} {{--md5|--sha1|--sha224|--sha256|--sha384|--sha512}} [OPTION]... [FILE]...", program);
}
println!("");
print!("{}", getopts::usage("Compute and check message digests.", opts));
pipe_println!("");
pipe_print!("{}", getopts::usage("Compute and check message digests.", opts));
}
fn hashsum(algoname: &str, mut digest: Box<Digest>, files: Vec<String>, binary: bool, check: bool, tag: bool, status: bool, quiet: bool, strict: bool, warn: bool) -> Result<(), int> {
@ -177,15 +177,18 @@ fn hashsum(algoname: &str, mut digest: Box<Digest>, files: Vec<String>, binary:
};
for filename in files.iter() {
let filename: &str = filename.as_slice();
let mut stdin_buf;
let mut file_buf;
let mut file = BufferedReader::new(
if filename == "-" {
box stdin_raw() as Box<Reader>
stdin_buf = stdin_raw();
&mut stdin_buf as &mut Reader
} else {
box safe_unwrap!(File::open(&Path::new(filename))) as Box<Reader>
file_buf = safe_unwrap!(File::open(&Path::new(filename)));
&mut file_buf as &mut Reader
}
);
if check {
// Set up Regexes for line validation and parsing
let bytes = digest.output_bits() / 4;
let gnu_re = safe_unwrap!(
@ -229,11 +232,11 @@ fn hashsum(algoname: &str, mut digest: Box<Digest>, files: Vec<String>, binary:
.as_slice().to_ascii().to_lower();
if sum.as_slice() == real_sum.as_slice() {
if !quiet {
println!("{}: OK", ck_filename);
pipe_println!("{}: OK", ck_filename);
}
} else {
if !status {
println!("{}: FAILED", ck_filename);
pipe_println!("{}: FAILED", ck_filename);
}
failed += 1;
}
@ -241,9 +244,9 @@ fn hashsum(algoname: &str, mut digest: Box<Digest>, files: Vec<String>, binary:
} else {
let sum = safe_unwrap!(digest_reader(&mut digest, &mut file, binary));
if tag {
println!("{} ({}) = {}", algoname, filename, sum);
pipe_println!("{} ({}) = {}", algoname, filename, sum);
} else {
println!("{} {}{}", sum, binary_marker, filename);
pipe_println!("{} {}{}", sum, binary_marker, filename);
}
}
}

View file

@ -10,6 +10,8 @@
* Synced with: https://raw.github.com/avsm/src/master/usr.bin/head/head.c
*/
#![feature(macro_rules)]
extern crate getopts;
use std::char;
@ -20,10 +22,14 @@ use std::path::Path;
use std::str::from_utf8;
use getopts::{optopt, optflag, getopts, usage};
static PROGRAM: &'static str = "head";
#[path = "../common/util.rs"]
mod util;
static NAME: &'static str = "head";
pub fn uumain(args: Vec<String>) -> int {
let mut line_count = 10u;
let mut byte_count = 0u;
// handle obsolete -number syntax
let options = match obsolete(args.tail()) {
@ -34,7 +40,8 @@ pub fn uumain(args: Vec<String>) -> int {
let args = options;
let possible_options = [
optopt("n", "number", "Number of lines to print", "n"),
optopt("c", "bytes", "Print the first K bytes. With the leading '-', print all but the last K bytes", "[-]K"),
optopt("n", "lines", "Print the first K lines. With the leading '-', print all but the last K lines", "[-]K"),
optflag("h", "help", "help"),
optflag("V", "version", "version")
];
@ -42,32 +49,58 @@ pub fn uumain(args: Vec<String>) -> int {
let given_options = match getopts(args.as_slice(), possible_options) {
Ok (m) => { m }
Err(_) => {
println!("{:s}", usage(PROGRAM, possible_options));
println!("{:s}", usage(NAME, possible_options));
return 1;
}
};
if given_options.opt_present("h") {
println!("{:s}", usage(PROGRAM, possible_options));
println!("{:s}", usage(NAME, possible_options));
return 0;
}
if given_options.opt_present("V") { version(); return 0 }
let use_bytes = given_options.opt_present("c");
// TODO: suffixes (e.g. b, kB, etc.)
match given_options.opt_str("n") {
Some(n) => {
if use_bytes {
show_error!("cannot specify both --bytes and --lines.");
return 1;
}
match from_str(n.as_slice()) {
Some(m) => { line_count = m }
None => {}
None => {
show_error!("invalid line count '{}'", n);
return 1;
}
}
}
None => match given_options.opt_str("c") {
Some(count) => match from_str(count.as_slice()) {
Some(m) => byte_count = m,
None => {
show_error!("invalid byte count '{}'", count);
return 1;
}
},
None => {}
}
};
let files = given_options.free;
let count =
if use_bytes {
byte_count
} else {
line_count
};
if files.is_empty() {
let mut buffer = BufferedReader::new(stdin());
head(&mut buffer, line_count);
head(&mut buffer, count, use_bytes);
} else {
let mut multiple = false;
let mut firstime = true;
@ -76,18 +109,19 @@ pub fn uumain(args: Vec<String>) -> int {
multiple = true;
}
for file in files.iter() {
if multiple {
if !firstime { println!(""); }
println!("==> {:s} <==", file.as_slice());
if !firstime { pipe_println!(""); }
pipe_println!("==> {:s} <==", file.as_slice());
}
firstime = false;
let path = Path::new(file.as_slice());
let reader = File::open(&path).unwrap();
let mut buffer = BufferedReader::new(reader);
head(&mut buffer, line_count);
if !head(&mut buffer, count, use_bytes) {
break;
}
}
}
@ -128,8 +162,22 @@ fn obsolete (options: &[String]) -> (Vec<String>, Option<uint>) {
(options, None)
}
fn head<T: Reader> (reader: &mut BufferedReader<T>, line_count:uint) {
for line in reader.lines().take(line_count) { print!("{}", line.unwrap()); }
// TODO: handle errors on read
fn head<T: Reader>(reader: &mut BufferedReader<T>, count: uint, use_bytes: bool) -> bool {
if use_bytes {
for byte in reader.bytes().take(count) {
if !pipe_print!("{}", byte.unwrap() as char) {
return false;
}
}
} else {
for line in reader.lines().take(count) {
if !pipe_print!("{}", line.unwrap()) {
return false;
}
}
}
true
}
fn version() {

View file

@ -12,12 +12,21 @@
* https://www.opensource.apple.com/source/shell_cmds/shell_cmds-170/hostname/hostname.c?txt
*/
#![feature(macro_rules)]
extern crate getopts;
extern crate libc;
use std::collections::hashmap::HashSet;
use std::io::net::addrinfo;
use std::str;
use getopts::{optflag, getopts, usage};
#[path = "../common/util.rs"]
mod util;
static NAME: &'static str = "hostname";
extern {
fn gethostname(name: *mut libc::c_char, namelen: libc::size_t) -> libc::c_int;
}
@ -36,8 +45,10 @@ pub fn uumain(args: Vec<String>) -> int {
let program = &args[0];
let options = [
optflag("f", "full", "Default option to show full name"),
optflag("s", "slice subdomain", "Cuts the subdomain off if any"),
optflag("d", "domain", "Display the name of the DNS domain if possible"),
optflag("i", "ip-address", "Display the network address(es) of the host"),
optflag("f", "fqdn", "Display the FQDN (Fully Qualified Domain Name) (default)"), // TODO: support --long
optflag("s", "short", "Display the short hostname (the portion before the first dot) if possible"),
optflag("h", "help", "Show help"),
optflag("V", "version", "Show program's version")
];
@ -57,18 +68,49 @@ pub fn uumain(args: Vec<String>) -> int {
0 => {
let hostname = xgethostname();
if matches.opt_present("i") {
match addrinfo::get_host_addresses(hostname.as_slice()) {
Ok(addresses) => {
let mut hashset = HashSet::new();
let mut output = String::new();
for addr in addresses.iter() {
// XXX: not sure why this is necessary...
if !hashset.contains(addr) {
output.push_str(addr.to_string().as_slice());
output.push_str(" ");
hashset.insert(addr.clone());
}
}
let len = output.len();
if len > 0 {
println!("{}", output.as_slice().slice_to(len - 1));
}
}
Err(f) => {
show_error!("{}", f);
return 1;
}
}
} else {
if matches.opt_present("s") {
let pos = hostname.as_slice().find_str(".");
if pos.is_some() {
println!("{:s}", hostname.as_slice().slice_to(pos.unwrap()));
return 0;
}
} else if matches.opt_present("d") {
let pos = hostname.as_slice().find_str(".");
if pos.is_some() {
println!("{}", hostname.as_slice().slice_from(pos.unwrap() + 1));
return 0;
}
}
println!("{:s}", hostname.as_slice());
println!("{:s}", hostname);
}
1 => { xsethostname( matches.free.last().unwrap().as_slice() ) }
_ => { help_menu(program.as_slice(), options); }
}
1 => xsethostname(matches.free.last().unwrap().as_slice()),
_ => help_menu(program.as_slice(), options)
};
0

View file

@ -9,27 +9,12 @@ extern crate getopts;
extern crate libc;
use std::cmp;
use std::io;
#[path = "../common/util.rs"]
mod util;
static NAME: &'static str = "seq";
macro_rules! pipe_write(
($($args:expr),+) => (
match write!(&mut io::stdout() as &mut Writer, $($args),+) {
Ok(_) => {}
Err(f) =>
if f.kind == io::BrokenPipe {
return
} else {
fail!("{}", f.to_string())
}
}
)
)
#[deriving(Clone)]
struct SeqOptions {
separator: String,
@ -237,27 +222,35 @@ fn print_seq(first: f64, step: f64, last: f64, largest_dec: uint, separator: Str
let before_dec = istr.as_slice().find('.').unwrap_or(ilen);
if pad && before_dec < padding {
for _ in range(0, padding - before_dec) {
pipe_write!("0");
if !pipe_print!("0") {
return;
}
}
pipe_write!("{}", istr);
}
pipe_print!("{}", istr);
let mut idec = ilen - before_dec;
if idec < largest_dec {
if idec == 0 {
pipe_write!(".");
if !pipe_print!(".") {
return;
}
idec += 1;
}
for _ in range(idec, largest_dec) {
pipe_write!("0")
if !pipe_print!("0") {
return;
}
}
}
i += 1;
value = first + i as f64 * step;
if !done_printing(value, step, last) {
pipe_write!("{:s}", separator);
if !pipe_print!("{:s}", separator) {
return;
}
}
}
if (first >= last && step < 0f64) || (first <= last && step > 0f64) {
pipe_write!("{:s}", terminator);
pipe_print!("{:s}", terminator);
}
}

View file

@ -54,8 +54,8 @@ fn main() {
None => (),
}
if binary_as_util.ends_with("uutils")
|| binary_as_util.ends_with("busybox") {
if binary_as_util.ends_with("uutils") || binary_as_util.starts_with("uutils") ||
binary_as_util.ends_with("busybox") || binary_as_util.starts_with("busybox") {
// uutils can be called as either "uutils", "busybox"
// "uutils-suffix" or "busybox-suffix". Not sure
// what busybox uses the -suffix pattern for.

View file

@ -16,7 +16,7 @@
extern crate getopts;
extern crate libc;
use std::io::{print, println};
use std::io::print;
#[path = "../common/util.rs"]
mod util;
@ -60,6 +60,8 @@ pub fn uumain(args: Vec<String>) -> int {
pub fn exec(string: &str) {
loop {
println(string);
if !pipe_println!("{}", string) {
break;
}
}
}