diff --git a/Makefile b/Makefile index d48cdabaa..e7eaa95ae 100644 --- a/Makefile +++ b/Makefile @@ -21,6 +21,7 @@ PROGS := \ rm \ rmdir \ sleep \ + seq \ tee \ true \ users \ @@ -39,6 +40,7 @@ EXES := \ TEST_PROGS := \ cat \ mkdir \ + seq \ TEST ?= $(TEST_PROGS) diff --git a/README.md b/README.md index ec92f38b5..97d72f3c8 100644 --- a/README.md +++ b/README.md @@ -138,7 +138,6 @@ To do - relpath - remove - runcon -- seq - setuidgid - shred - shuf diff --git a/seq/seq.rs b/seq/seq.rs new file mode 100644 index 000000000..bc2214f1a --- /dev/null +++ b/seq/seq.rs @@ -0,0 +1,109 @@ +#[crate_id(name="seq", vers="1.0.0", author="Daniel MacDougall")]; + +// TODO: Make -w flag work with decimals +// TODO: Support -f flag + +extern mod extra; + +use std::os; +use std::cmp::max; +use extra::getopts::groups; + +fn print_usage(opts: ~[groups::OptGroup]) { + println!("seq 1.0.0\n"); + println!("Usage:\n seq [-w] [-s string] [-t string] [first [step]] last\n"); + println!("{:s}", groups::usage("Print sequences of numbers", opts)); +} + +fn parse_float(s: &str) -> Result{ + match from_str(s) { + Some(n) => Ok(n), + None => Err(format!("seq: invalid floating point argument: {:s}", s)) + } +} + +fn escape_sequences(s: &str) -> ~str { + s.replace("\\n", "\n"). + replace("\\t", "\t") +} + +fn main() { + let args = os::args(); + let opts = ~[ + groups::optopt("s", "separator", "Separator character (defaults to \\n)", ""), + groups::optopt("t", "terminator", "Terminator character (defaults to separator)", ""), + groups::optflag("w", "widths", "Equalize widths of all numbers by padding with zeros"), + groups::optflag("h", "help", "print this help text and exit"), + groups::optflag("V", "version", "print version and exit"), + ]; + let matches = match groups::getopts(args.tail(), opts) { + Ok(m) => { m } + Err(f) => { + println!("{:s}", f.to_err_msg()); + print_usage(opts); + return; + } + }; + if matches.opt_present("help") { + print_usage(opts); + return; + } + if matches.opt_present("version") { + println!("seq 1.0.0"); + return; + } + if matches.free.len() < 1 || matches.free.len() > 3 { + print_usage(opts); + return; + } + let first = if matches.free.len() > 1 { + match parse_float(matches.free[0]) { + Ok(n) => n, + Err(s) => { println!("{:s}", s); return; } + } + } else { + 1.0 + }; + let step = if matches.free.len() > 2 { + match parse_float(matches.free[1]) { + Ok(n) => n, + Err(s) => { println!("{:s}", s); return; } + } + } else { + 1.0 + }; + let last = match parse_float(matches.free[matches.free.len()-1]) { + Ok(n) => n, + Err(s) => { println!("{:s}", s); return; } + }; + let separator = escape_sequences(matches.opt_str("s").unwrap_or(~"\n")); + let terminator = escape_sequences(matches.opt_str("t").unwrap_or(separator.clone())); + print_seq(first, step, last, separator, terminator, matches.opt_present("w")); +} + +fn done_printing(next: f32, step: f32, last: f32) -> bool { + if step > 0f32 { + next > last + } else { + next < last + } +} + +fn print_seq(first: f32, step: f32, last: f32, separator: ~str, terminator: ~str, pad: bool) { + let mut i = first; + let maxlen = max(first, last).to_str().len(); + while !done_printing(i, step, last) { + let ilen = i.to_str().len(); + if pad && ilen < maxlen { + for _ in range(0, maxlen - ilen) { + print!("0"); + } + } + print!("{:f}", i); + i += step; + if !done_printing(i, step, last) { + print!("{:s}", separator); + } + } + print!("{:s}", terminator); +} diff --git a/seq/test.rs b/seq/test.rs new file mode 100644 index 000000000..6c2b4ce81 --- /dev/null +++ b/seq/test.rs @@ -0,0 +1,29 @@ +use std::{run,str}; + +#[test] +fn test_count_up() { + let p = run::process_output("build/seq", [~"10"]).unwrap(); + let out = str::from_utf8(p.output).unwrap().into_owned(); + assert_eq!(out, ~"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n"); +} + +#[test] +fn test_count_down() { + let p = run::process_output("build/seq", [~"--", ~"5", ~"-1", ~"1"]).unwrap(); + let out = str::from_utf8(p.output).unwrap().into_owned(); + assert_eq!(out, ~"5\n4\n3\n2\n1\n"); +} + +#[test] +fn test_separator_and_terminator() { + let p = run::process_output("build/seq", [~"-s", ~",", ~"-t", ~"!", ~"2", ~"6"]).unwrap(); + let out = str::from_utf8(p.output).unwrap().into_owned(); + assert_eq!(out, ~"2,3,4,5,6!"); +} + +#[test] +fn test_equalize_widths() { + let p = run::process_output("build/seq", [~"-w", ~"5", ~"10"]).unwrap(); + let out = str::from_utf8(p.output).unwrap().into_owned(); + assert_eq!(out, ~"05\n06\n07\n08\n09\n10\n"); +}