mirror of
https://github.com/uutils/coreutils
synced 2024-12-13 14:52:41 +00:00
add test
This commit is contained in:
parent
6d440dfba6
commit
a4eb5c16f9
6 changed files with 248 additions and 1 deletions
|
@ -183,6 +183,10 @@ path = "tail/tail.rs"
|
|||
name = "tee"
|
||||
path = "tee/tee.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "test"
|
||||
path = "test/test.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "touch"
|
||||
path = "touch/touch.rs"
|
||||
|
|
1
Makefile
1
Makefile
|
@ -41,6 +41,7 @@ PROGS := \
|
|||
sync \
|
||||
tac \
|
||||
tee \
|
||||
test \
|
||||
touch \
|
||||
tr \
|
||||
true \
|
||||
|
|
|
@ -159,7 +159,7 @@ To do
|
|||
- stdbuf
|
||||
- stty
|
||||
- tail (not all features implemented)
|
||||
- test
|
||||
- test (not all features implemented)
|
||||
- timeout
|
||||
- tsort
|
||||
- unexpand
|
||||
|
|
|
@ -23,6 +23,7 @@ fn main() {
|
|||
}
|
||||
|
||||
let crat = match args.get(1).as_slice() {
|
||||
"test" => "uutest",
|
||||
"true" => "uutrue",
|
||||
"false" => "uufalse",
|
||||
"sync" => "uusync",
|
||||
|
|
|
@ -22,6 +22,10 @@ fn main() {
|
|||
}
|
||||
util_map.push_str(format!("map.insert(\"{}\", hashsum::uumain);\n", prog).as_slice());
|
||||
}
|
||||
"test" => {
|
||||
crates.push_str("extern crate uutest;\n");
|
||||
util_map.push_str("map.insert(\"test\", uutest::uumain);\n");
|
||||
}
|
||||
"true" => util_map.push_str("fn uutrue(_: Vec<String>) -> int { 0 }\nmap.insert(\"true\", uutrue);\n"),
|
||||
"false" => util_map.push_str("fn uufalse(_: Vec<String>) -> int { 1 }\nmap.insert(\"false\", uufalse);\n"),
|
||||
"sync" => {
|
||||
|
|
237
test/test.rs
Normal file
237
test/test.rs
Normal file
|
@ -0,0 +1,237 @@
|
|||
#![crate_name = "uutest"]
|
||||
|
||||
/*
|
||||
* This file is part of the uutils coreutils package.
|
||||
*
|
||||
* (c) mahkoh (ju.orth [at] gmail [dot] com)
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
extern crate libc;
|
||||
extern crate num;
|
||||
|
||||
use std::os::{args_as_bytes};
|
||||
use std::str::{from_utf8};
|
||||
use num::bigint::{BigInt};
|
||||
|
||||
pub fn uumain(_: Vec<String>) -> int {
|
||||
let args = args_as_bytes();
|
||||
let args: Vec<&[u8]> = args.iter().map(|a| a.as_slice()).collect();
|
||||
let args = args.as_slice();
|
||||
if args.len() == 0 {
|
||||
return 2;
|
||||
}
|
||||
let args = match args[0] {
|
||||
b"[" => match args[args.len()-1] {
|
||||
b"]" => args.slice(1, args.len()-1),
|
||||
_ => return 2,
|
||||
},
|
||||
_ => args.slice(1, args.len()),
|
||||
};
|
||||
let rv = match args.len() {
|
||||
0 => false,
|
||||
1 => one(args),
|
||||
2 => two(args),
|
||||
3 => three(args),
|
||||
4 => four(args),
|
||||
_ => return 2,
|
||||
};
|
||||
1 - rv as int
|
||||
}
|
||||
|
||||
fn one(args: &[&[u8]]) -> bool {
|
||||
args[0].len() > 0
|
||||
}
|
||||
|
||||
fn two(args: &[&[u8]]) -> bool {
|
||||
match args[0] {
|
||||
b"!" => !one(args.slice_from(1)),
|
||||
b"-b" => path(args[1], BlockSpecial),
|
||||
b"-c" => path(args[1], CharacterSpecial),
|
||||
b"-d" => path(args[1], Directory),
|
||||
b"-e" => path(args[1], Exists),
|
||||
b"-f" => path(args[1], Regular),
|
||||
b"-g" => path(args[1], GroupIDFlag),
|
||||
b"-h" => path(args[1], SymLink),
|
||||
b"-L" => path(args[1], SymLink),
|
||||
b"-n" => one(args.slice_from(1)),
|
||||
b"-p" => path(args[1], FIFO),
|
||||
b"-r" => path(args[1], Readable),
|
||||
b"-S" => path(args[1], Socket),
|
||||
b"-s" => path(args[1], NonEmpty),
|
||||
b"-t" => isatty(args[1]),
|
||||
b"-u" => path(args[1], UserIDFlag),
|
||||
b"-w" => path(args[1], Writable),
|
||||
b"-x" => path(args[1], Executable),
|
||||
b"-z" => !one(args.slice_from(1)),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn three(args: &[&[u8]]) -> bool {
|
||||
match args[1] {
|
||||
b"=" => args[0] == args[2],
|
||||
b"!=" => args[0] != args[2],
|
||||
b"-eq" => integers(args[0], args[2], Equal),
|
||||
b"-ne" => integers(args[0], args[2], Unequal),
|
||||
b"-gt" => integers(args[0], args[2], Greater),
|
||||
b"-ge" => integers(args[0], args[2], GreaterEqual),
|
||||
b"-lt" => integers(args[0], args[2], Less),
|
||||
b"-le" => integers(args[0], args[2], LessEqual),
|
||||
_ => match args[0] {
|
||||
b"!" => !two(args.slice_from(1)),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn four(args: &[&[u8]]) -> bool {
|
||||
match args[0] {
|
||||
b"!" => !three(args.slice_from(1)),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
enum IntegerCondition {
|
||||
Equal,
|
||||
Unequal,
|
||||
Greater,
|
||||
GreaterEqual,
|
||||
Less,
|
||||
LessEqual,
|
||||
}
|
||||
|
||||
fn integers(a: &[u8], b: &[u8], cond: IntegerCondition) -> bool {
|
||||
let (a, b): (&str, &str) = match (from_utf8(a), from_utf8(b)) {
|
||||
(Some(a), Some(b)) => (a, b),
|
||||
_ => return false,
|
||||
};
|
||||
let (a, b): (BigInt, BigInt) = match (from_str(a), from_str(b)) {
|
||||
(Some(a), Some(b)) => (a, b),
|
||||
_ => return false,
|
||||
};
|
||||
match cond {
|
||||
Equal => a == b,
|
||||
Unequal => a != b,
|
||||
Greater => a > b,
|
||||
GreaterEqual => a >= b,
|
||||
Less => a < b,
|
||||
LessEqual => a <= b,
|
||||
}
|
||||
}
|
||||
|
||||
fn isatty(fd: &[u8]) -> bool {
|
||||
use libc::{isatty};
|
||||
from_utf8(fd).and_then(|s| from_str(s))
|
||||
.map(|i| unsafe { isatty(i) == 1 }).unwrap_or(false)
|
||||
}
|
||||
|
||||
#[deriving(Eq, PartialEq)]
|
||||
enum PathCondition {
|
||||
BlockSpecial,
|
||||
CharacterSpecial,
|
||||
Directory,
|
||||
Exists,
|
||||
Regular,
|
||||
GroupIDFlag,
|
||||
SymLink,
|
||||
FIFO,
|
||||
Readable,
|
||||
Socket,
|
||||
NonEmpty,
|
||||
UserIDFlag,
|
||||
Writable,
|
||||
Executable,
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
fn path(path: &[u8], cond: PathCondition) -> bool {
|
||||
use libc::{stat, c_int, lstat, S_IFMT, S_IFLNK, S_IFBLK, S_IFCHR, S_IFDIR, S_IFREG};
|
||||
use libc::{S_IFIFO, mode_t};
|
||||
static S_ISUID: mode_t = 0o4000;
|
||||
static S_ISGID: mode_t = 0o2000;
|
||||
static S_IFSOCK: c_int = 0o140000;
|
||||
|
||||
enum Permission {
|
||||
Read = 0o4,
|
||||
Write = 0o2,
|
||||
Execute = 0o1,
|
||||
}
|
||||
let perm = |stat: stat, p: Permission| {
|
||||
use libc::{getgid, getuid};
|
||||
let (uid, gid) = unsafe { (getuid(), getgid()) };
|
||||
if uid == stat.st_uid {
|
||||
stat.st_mode & (p as mode_t << 6) != 0
|
||||
} else if gid == stat.st_gid {
|
||||
stat.st_mode & (p as mode_t << 3) != 0
|
||||
} else {
|
||||
stat.st_mode & (p as mode_t << 0) != 0
|
||||
}
|
||||
};
|
||||
|
||||
let path = unsafe { path.to_c_str_unchecked() };
|
||||
let mut stat = unsafe { std::mem::zeroed() };
|
||||
if cond == SymLink {
|
||||
if unsafe { lstat(path.as_ptr(), &mut stat) } == 0 {
|
||||
if stat.st_mode as c_int & S_IFMT == S_IFLNK {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if unsafe { libc::stat(path.as_ptr(), &mut stat) } != 0 {
|
||||
return false;
|
||||
}
|
||||
let file_type = stat.st_mode as c_int & S_IFMT;
|
||||
match cond {
|
||||
BlockSpecial => file_type == S_IFBLK,
|
||||
CharacterSpecial => file_type == S_IFCHR,
|
||||
Directory => file_type == S_IFDIR,
|
||||
Exists => true,
|
||||
Regular => file_type == S_IFREG,
|
||||
GroupIDFlag => stat.st_mode & S_ISGID != 0,
|
||||
SymLink => true,
|
||||
FIFO => file_type == S_IFIFO,
|
||||
Readable => perm(stat, Read),
|
||||
Socket => file_type == S_IFSOCK,
|
||||
NonEmpty => stat.st_size > 0,
|
||||
UserIDFlag => stat.st_mode & S_ISUID != 0,
|
||||
Writable => perm(stat, Write),
|
||||
Executable => perm(stat, Execute),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn path(path: &[u8], cond: PathCondition) -> bool {
|
||||
use std::io::{TypeFile, TypeDirectory, TypeBlockSpecial, TypeNamedPipe};
|
||||
use std::io::fs::{stat};
|
||||
use std::path::{Path};
|
||||
|
||||
let path = match Path::new_opt(path) {
|
||||
Some(p) => p,
|
||||
None => return false,
|
||||
};
|
||||
let stat = match stat(&path) {
|
||||
Ok(s) => s,
|
||||
_ => return false,
|
||||
};
|
||||
match cond {
|
||||
BlockSpecial => stat.kind == TypeBlockSpecial,
|
||||
CharacterSpecial => false,
|
||||
Directory => stat.kind == TypeDirectory,
|
||||
Exists => true,
|
||||
Regular => stat.kind == TypeFile,
|
||||
GroupIDFlag => false,
|
||||
SymLink => false,
|
||||
FIFO => stat.kind == TypeNamedPipe,
|
||||
Readable => false, // TODO
|
||||
Socket => false, // TODO?
|
||||
NonEmpty => stat.size > 0,
|
||||
UserIDFlag => false,
|
||||
Writable => false, // TODO
|
||||
Executable => false, // TODO
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in a new issue