2014-03-31 16:40:21 +00:00
|
|
|
#![feature(macro_rules)]
|
2014-07-06 08:13:36 +00:00
|
|
|
#![crate_name = "echo"]
|
2013-12-01 09:15:07 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* This file is part of the uutils coreutils package.
|
|
|
|
*
|
|
|
|
* (c) Derek Chiang <derekchiang93@gmail.com>
|
|
|
|
*
|
|
|
|
* For the full copyright and license information, please view the LICENSE
|
|
|
|
* file that was distributed with this source code.
|
|
|
|
*/
|
|
|
|
|
2014-02-16 21:29:31 +00:00
|
|
|
extern crate getopts;
|
2014-04-07 22:43:34 +00:00
|
|
|
extern crate libc;
|
2013-12-01 09:15:07 +00:00
|
|
|
|
2014-02-05 10:40:22 +00:00
|
|
|
use std::io::{print, println};
|
2013-12-01 09:15:07 +00:00
|
|
|
use std::uint;
|
|
|
|
|
2014-02-23 22:17:48 +00:00
|
|
|
#[path = "../common/util.rs"]
|
2014-02-07 06:39:07 +00:00
|
|
|
mod util;
|
2013-12-01 09:15:07 +00:00
|
|
|
|
2014-06-19 20:05:43 +00:00
|
|
|
#[allow(dead_code)]
|
2014-02-07 06:39:07 +00:00
|
|
|
static NAME: &'static str = "echo";
|
|
|
|
static VERSION: &'static str = "1.0.0";
|
2014-02-05 10:40:22 +00:00
|
|
|
|
2014-06-20 00:38:26 +00:00
|
|
|
#[deriving(Clone)]
|
2014-06-19 20:05:43 +00:00
|
|
|
struct EchoOptions {
|
|
|
|
newline: bool,
|
|
|
|
escape: bool
|
|
|
|
}
|
|
|
|
|
2014-06-20 00:38:26 +00:00
|
|
|
#[inline(always)]
|
2014-04-26 05:03:08 +00:00
|
|
|
fn to_char(bytes: &Vec<u8>, base: uint) -> char {
|
|
|
|
uint::parse_bytes(bytes.as_slice(), base).unwrap() as u8 as char
|
2013-12-01 09:15:07 +00:00
|
|
|
}
|
|
|
|
|
2014-06-20 00:38:26 +00:00
|
|
|
#[inline(always)]
|
2013-12-01 09:15:07 +00:00
|
|
|
fn isxdigit(c: u8) -> bool {
|
|
|
|
match c as char {
|
|
|
|
'0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' |
|
|
|
|
'8' | '9' | 'A' | 'B' | 'C' | 'D' | 'E' | 'F' => true,
|
|
|
|
_ => false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-20 00:38:26 +00:00
|
|
|
#[inline(always)]
|
2013-12-01 09:15:07 +00:00
|
|
|
fn isodigit(c: u8) -> bool {
|
|
|
|
match c as char {
|
|
|
|
'0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' => true,
|
|
|
|
_ => false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-07-02 16:25:29 +00:00
|
|
|
fn convert_str(string: &[u8], index: uint, base: uint) -> (char, uint) {
|
2013-12-02 01:00:44 +00:00
|
|
|
let (max_digits, is_legal_digit) = match base {
|
|
|
|
8u => (3, isodigit),
|
|
|
|
16u => (2, isxdigit),
|
|
|
|
_ => fail!(),
|
|
|
|
};
|
|
|
|
|
2014-04-26 05:03:08 +00:00
|
|
|
let mut bytes = vec!();
|
2014-06-25 11:12:43 +00:00
|
|
|
for offset in range(0u, max_digits) {
|
2014-06-19 23:26:49 +00:00
|
|
|
if string.len() <= index + offset as uint {
|
|
|
|
break;
|
|
|
|
}
|
2013-12-02 01:00:44 +00:00
|
|
|
let c = string[index + offset as uint];
|
|
|
|
if is_legal_digit(c) {
|
|
|
|
bytes.push(c as u8);
|
|
|
|
} else {
|
2014-06-19 23:26:49 +00:00
|
|
|
break;
|
2013-12-02 01:00:44 +00:00
|
|
|
}
|
|
|
|
}
|
2014-06-12 04:41:53 +00:00
|
|
|
|
2014-06-19 23:26:49 +00:00
|
|
|
if bytes.len() == 0 {
|
|
|
|
(' ', 0)
|
|
|
|
} else {
|
2014-06-20 00:38:26 +00:00
|
|
|
(to_char(&bytes, base), bytes.len())
|
2014-06-19 23:26:49 +00:00
|
|
|
}
|
2013-12-02 01:00:44 +00:00
|
|
|
}
|
|
|
|
|
2014-06-19 20:05:43 +00:00
|
|
|
fn parse_options(args: Vec<String>, options: &mut EchoOptions) -> Option<Vec<String>> {
|
|
|
|
let mut echo_args = vec!();
|
2014-07-20 01:13:55 +00:00
|
|
|
let program = args[0].clone();
|
2014-06-20 00:38:26 +00:00
|
|
|
'argloop: for arg in args.move_iter().skip(1) {
|
2014-06-19 20:05:43 +00:00
|
|
|
match arg.as_slice() {
|
|
|
|
"--help" | "-h" => {
|
2014-06-20 00:38:26 +00:00
|
|
|
print_help(&program);
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
"--version" | "-V" => {
|
|
|
|
print_version();
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
"-n" => options.newline = true,
|
|
|
|
"-e" => options.escape = true,
|
|
|
|
"-E" => options.escape = false,
|
|
|
|
_ => {
|
2014-06-20 22:12:08 +00:00
|
|
|
if arg.len() > 1 && arg.as_slice().char_at(0) == '-' {
|
2014-06-20 00:38:26 +00:00
|
|
|
let mut newopts = options.clone();
|
2014-06-30 11:13:38 +00:00
|
|
|
let argptr: *const String = &arg; // escape from the borrow checker
|
2014-06-20 00:38:26 +00:00
|
|
|
for ch in unsafe { (*argptr).as_slice() }.chars().skip(1) {
|
|
|
|
match ch {
|
|
|
|
'h' => {
|
|
|
|
print_help(&program);
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
'V' => {
|
|
|
|
print_version();
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
'n' => newopts.newline = true,
|
|
|
|
'e' => newopts.escape = true,
|
|
|
|
'E' => newopts.escape = false,
|
|
|
|
_ => {
|
|
|
|
echo_args.push(arg);
|
|
|
|
continue 'argloop;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*options = newopts;
|
|
|
|
} else {
|
|
|
|
echo_args.push(arg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Some(echo_args)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn print_help(program: &String) {
|
|
|
|
let opts = [
|
|
|
|
getopts::optflag("n", "", "do not output the trailing newline"),
|
|
|
|
getopts::optflag("e", "", "enable interpretation of backslash escapes"),
|
|
|
|
getopts::optflag("E", "", "disable interpretation of backslash escapes (default)"),
|
|
|
|
getopts::optflag("h", "help", "display this help and exit"),
|
|
|
|
getopts::optflag("V", "version", "output version information and exit"),
|
|
|
|
];
|
|
|
|
println!("echo {:s} - display a line of text", VERSION);
|
|
|
|
println!("");
|
|
|
|
println!("Usage:");
|
|
|
|
println!(" {0:s} [SHORT-OPTION]... [STRING]...", *program);
|
|
|
|
println!(" {0:s} LONG-OPTION", *program);
|
|
|
|
println!("");
|
|
|
|
println(getopts::usage("Echo the STRING(s) to standard output.", opts).as_slice());
|
|
|
|
println("If -e is in effect, the following sequences are recognized:
|
2013-12-03 12:56:15 +00:00
|
|
|
|
|
|
|
\\\\ backslash
|
|
|
|
\\a alert (BEL)
|
|
|
|
\\b backspace
|
|
|
|
\\c produce no further output
|
|
|
|
\\e escape
|
|
|
|
\\f form feed
|
|
|
|
\\n new line
|
|
|
|
\\r carriage return
|
|
|
|
\\t horizontal tab
|
|
|
|
\\v vertical tab
|
|
|
|
\\0NNN byte with octal value NNN (1 to 3 digits)
|
|
|
|
\\xHH byte with hexadecimal value HH (1 to 2 digits)");
|
2014-06-20 00:38:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn print_version() {
|
|
|
|
println!("echo version: {:s}", VERSION);
|
2014-06-19 20:05:43 +00:00
|
|
|
}
|
2013-12-01 09:15:07 +00:00
|
|
|
|
2014-06-19 20:05:43 +00:00
|
|
|
pub fn uumain(args: Vec<String>) -> int {
|
|
|
|
let mut options = EchoOptions {
|
|
|
|
newline: false,
|
|
|
|
escape: false
|
|
|
|
};
|
|
|
|
|
|
|
|
let free = match parse_options(args, &mut options) {
|
|
|
|
Some(vec) => vec,
|
|
|
|
None => return 0
|
|
|
|
};
|
2013-12-01 09:15:07 +00:00
|
|
|
|
2014-06-19 20:05:43 +00:00
|
|
|
if !free.is_empty() {
|
|
|
|
let string = free.connect(" ");
|
|
|
|
if options.escape {
|
2013-12-01 09:15:07 +00:00
|
|
|
let mut prev_was_slash = false;
|
2014-05-23 12:28:40 +00:00
|
|
|
let mut iter = string.as_slice().chars().enumerate();
|
2013-12-01 09:15:07 +00:00
|
|
|
loop {
|
|
|
|
match iter.next() {
|
|
|
|
Some((index, c)) => {
|
|
|
|
if !prev_was_slash {
|
|
|
|
if c != '\\' {
|
2014-06-20 00:38:26 +00:00
|
|
|
print!("{}", c);
|
2013-12-01 09:15:07 +00:00
|
|
|
} else {
|
|
|
|
prev_was_slash = true;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
prev_was_slash = false;
|
|
|
|
match c {
|
2014-06-20 00:38:26 +00:00
|
|
|
'\\' => print!("\\"),
|
|
|
|
'a' => print!("\x07"),
|
|
|
|
'b' => print!("\x08"),
|
2013-12-01 09:15:07 +00:00
|
|
|
'c' => break,
|
2014-06-20 00:38:26 +00:00
|
|
|
'e' => print!("\x1B"),
|
|
|
|
'f' => print!("\x0C"),
|
|
|
|
'n' => print!("\n"),
|
|
|
|
'r' => print!("\r"),
|
|
|
|
't' => print!("\t"),
|
|
|
|
'v' => print!("\x0B"),
|
2013-12-01 09:15:07 +00:00
|
|
|
'x' => {
|
2014-07-02 16:25:29 +00:00
|
|
|
let (c, num_char_used) = convert_str(string.as_bytes(), index + 1, 16u);
|
2013-12-02 01:00:44 +00:00
|
|
|
if num_char_used == 0 {
|
2014-06-20 00:38:26 +00:00
|
|
|
print!("\\x");
|
2013-12-01 09:15:07 +00:00
|
|
|
} else {
|
2014-06-20 00:38:26 +00:00
|
|
|
print!("{}", c);
|
2013-12-02 01:00:44 +00:00
|
|
|
for _ in range(0, num_char_used) {
|
|
|
|
iter.next(); // consume used characters
|
|
|
|
}
|
2013-12-01 09:15:07 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
'0' => {
|
2014-07-02 16:25:29 +00:00
|
|
|
let (c, num_char_used) = convert_str(string.as_bytes(), index + 1, 8u);
|
2013-12-02 01:00:44 +00:00
|
|
|
if num_char_used == 0 {
|
2014-06-20 00:38:26 +00:00
|
|
|
print!("\0");
|
2013-12-01 09:15:07 +00:00
|
|
|
} else {
|
2014-06-20 00:38:26 +00:00
|
|
|
print!("{}", c);
|
2013-12-02 01:00:44 +00:00
|
|
|
for _ in range(0, num_char_used) {
|
|
|
|
iter.next(); // consume used characters
|
|
|
|
}
|
2013-12-01 09:15:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => {
|
2014-07-02 16:25:29 +00:00
|
|
|
let (esc_c, num_char_used) = convert_str(string.as_bytes(), index, 8u);
|
2014-06-20 00:38:26 +00:00
|
|
|
if num_char_used == 0 {
|
|
|
|
print!("\\{}", c);
|
|
|
|
} else {
|
|
|
|
print!("{}", esc_c);
|
|
|
|
for _ in range(1, num_char_used) {
|
|
|
|
iter.next(); // consume used characters
|
|
|
|
}
|
|
|
|
}
|
2013-12-01 09:15:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
None => break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2014-05-23 12:28:40 +00:00
|
|
|
print(string.as_slice());
|
2013-12-01 09:15:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-19 20:05:43 +00:00
|
|
|
if !options.newline {
|
2014-01-13 09:05:02 +00:00
|
|
|
println!("")
|
2013-12-01 09:15:07 +00:00
|
|
|
}
|
2014-06-08 07:56:37 +00:00
|
|
|
|
2014-06-12 04:41:53 +00:00
|
|
|
0
|
2013-12-01 09:15:07 +00:00
|
|
|
}
|