mirror of
https://github.com/uutils/coreutils
synced 2024-12-16 08:12:39 +00:00
Merge pull request #421 from yincrash/yin_sort_rebased
basic byte and numeric sort rebased and squashed
This commit is contained in:
commit
fdc55d48bd
14 changed files with 230 additions and 0 deletions
|
@ -163,6 +163,10 @@ path = "shuf/shuf.rs"
|
|||
name = "sleep"
|
||||
path = "sleep/sleep.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "sort"
|
||||
path = "sort/sort.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "split"
|
||||
path = "split/split.rs"
|
||||
|
|
2
Makefile
2
Makefile
|
@ -66,6 +66,7 @@ PROGS := \
|
|||
split \
|
||||
seq \
|
||||
shuf \
|
||||
sort \
|
||||
sum \
|
||||
sync \
|
||||
tac \
|
||||
|
@ -133,6 +134,7 @@ TEST_PROGS := \
|
|||
mkdir \
|
||||
nl \
|
||||
seq \
|
||||
sort \
|
||||
tr \
|
||||
truncate \
|
||||
unexpand
|
||||
|
|
158
src/sort/sort.rs
Normal file
158
src/sort/sort.rs
Normal file
|
@ -0,0 +1,158 @@
|
|||
#![crate_name = "sort"]
|
||||
#![feature(macro_rules)]
|
||||
|
||||
/*
|
||||
* This file is part of the uutils coreutils package.
|
||||
*
|
||||
* (c) Michael Yin <mikeyin@mikeyin.org>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
extern crate getopts;
|
||||
|
||||
use std::io::{print, File, BufferedReader};
|
||||
use std::io::stdio::{stdin_raw, stderr};
|
||||
use std::str::Chars;
|
||||
|
||||
#[path = "../common/util.rs"]
|
||||
mod util;
|
||||
|
||||
static NAME: &'static str = "sort";
|
||||
static VERSION: &'static str = "0.0.1";
|
||||
|
||||
static DECIMAL_PT: char = '.';
|
||||
static THOUSANDS_SEP: char = ',';
|
||||
|
||||
pub fn uumain(args: Vec<String>) -> int {
|
||||
let program = args[0].as_slice();
|
||||
let opts = [
|
||||
getopts::optflag("n", "numeric-sort", "compare according to string numerical value"),
|
||||
getopts::optflag("h", "help", "display this help and exit"),
|
||||
getopts::optflag("", "version", "output version information and exit"),
|
||||
];
|
||||
|
||||
let matches = match getopts::getopts(args.tail(), opts) {
|
||||
Ok(m) => m,
|
||||
Err(f) => crash!(1, "Invalid options\n{}", f)
|
||||
};
|
||||
if matches.opt_present("help") {
|
||||
println!("Usage: {0:s} [OPTION]... [FILE]...", program);
|
||||
println!("Write the sorted concatenation of all FILE(s) to standard output.");
|
||||
println!("");
|
||||
print(getopts::usage("Mandatory arguments for long options are mandatory for short options too.", opts).as_slice());
|
||||
println!("");
|
||||
println!("With no FILE, or when FILE is -, read standard input.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if matches.opt_present("version") {
|
||||
println!("sort 1.0.0");
|
||||
return 0;
|
||||
}
|
||||
|
||||
let mut numeric = false;
|
||||
if matches.opt_present("numeric-sort") {
|
||||
numeric = true;
|
||||
}
|
||||
|
||||
let mut files = matches.free;
|
||||
if files.is_empty() {
|
||||
/* if no file, default to stdin */
|
||||
files.push("-".to_string());
|
||||
}
|
||||
|
||||
exec(files, numeric);
|
||||
|
||||
0
|
||||
}
|
||||
|
||||
fn exec(files: Vec<String>, numeric: bool) {
|
||||
for path in files.iter() {
|
||||
let (reader, _) = match open(path.as_slice()) {
|
||||
Some(x) => x,
|
||||
None => continue,
|
||||
};
|
||||
|
||||
let mut buf_reader = BufferedReader::new(reader);
|
||||
let mut lines = Vec::new();
|
||||
|
||||
for line in buf_reader.lines() {
|
||||
match line {
|
||||
Ok(n) => {
|
||||
lines.push(n);
|
||||
},
|
||||
_ => break
|
||||
}
|
||||
}
|
||||
|
||||
if numeric {
|
||||
lines.sort_by(frac_compare);
|
||||
} else {
|
||||
lines.sort();
|
||||
}
|
||||
for line in lines.iter() {
|
||||
print!("{}", line)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn skip_zeros(mut char_a: char, char_iter: &mut Chars, ret: Ordering) -> Ordering {
|
||||
char_a = match char_iter.next() { None => 0 as char, Some(t) => t };
|
||||
while char_a == '0' {
|
||||
char_a = match char_iter.next() { None => return Equal, Some(t) => t };
|
||||
}
|
||||
if char_a.is_digit() { ret } else { Equal }
|
||||
}
|
||||
|
||||
/// Compares two decimal fractions as strings (n < 1)
|
||||
/// This requires the strings to start with a decimal, otherwise it's treated as 0
|
||||
fn frac_compare(a: &String, b: &String) -> Ordering {
|
||||
let a_chars = &mut a.as_slice().chars();
|
||||
let b_chars = &mut b.as_slice().chars();
|
||||
|
||||
let mut char_a = match a_chars.next() { None => 0 as char, Some(t) => t };
|
||||
let mut char_b = match b_chars.next() { None => 0 as char, Some(t) => t };
|
||||
|
||||
if char_a == DECIMAL_PT && char_b == DECIMAL_PT {
|
||||
while char_a == char_b {
|
||||
char_a = match a_chars.next() { None => 0 as char, Some(t) => t };
|
||||
char_b = match b_chars.next() { None => 0 as char, Some(t) => t };
|
||||
// hit the end at the same time, they are equal
|
||||
if !char_a.is_digit() {
|
||||
return Equal;
|
||||
}
|
||||
}
|
||||
if char_a.is_digit() && char_b.is_digit() {
|
||||
(char_a as int).cmp(&(char_b as int))
|
||||
} else if char_a.is_digit() {
|
||||
skip_zeros(char_a, a_chars, Greater)
|
||||
} else if char_b.is_digit() {
|
||||
skip_zeros(char_b, b_chars, Less)
|
||||
} else { Equal }
|
||||
} else if char_a == DECIMAL_PT {
|
||||
skip_zeros(char_a, a_chars, Greater)
|
||||
} else if char_b == DECIMAL_PT {
|
||||
skip_zeros(char_b, b_chars, Less)
|
||||
} else { Equal }
|
||||
}
|
||||
|
||||
|
||||
// from cat.rs
|
||||
fn open<'a>(path: &str) -> Option<(Box<Reader + 'a>, bool)> {
|
||||
if path == "-" {
|
||||
let stdin = stdin_raw();
|
||||
let interactive = stdin.isatty();
|
||||
return Some((box stdin as Box<Reader>, interactive));
|
||||
}
|
||||
|
||||
match File::open(&std::path::Path::new(path)) {
|
||||
Ok(f) => Some((box f as Box<Reader>, false)),
|
||||
Err(e) => {
|
||||
show_error!("sort: {0:s}: {1:s}", path, e.to_string());
|
||||
None
|
||||
},
|
||||
}
|
||||
}
|
||||
|
2
test/fixtures/sort/numeric1.ans
vendored
Normal file
2
test/fixtures/sort/numeric1.ans
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
0
|
||||
.02
|
2
test/fixtures/sort/numeric1.txt
vendored
Normal file
2
test/fixtures/sort/numeric1.txt
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
.02
|
||||
0
|
2
test/fixtures/sort/numeric2.ans
vendored
Normal file
2
test/fixtures/sort/numeric2.ans
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
.02
|
||||
.03
|
2
test/fixtures/sort/numeric2.txt
vendored
Normal file
2
test/fixtures/sort/numeric2.txt
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
.03
|
||||
.02
|
2
test/fixtures/sort/numeric3.ans
vendored
Normal file
2
test/fixtures/sort/numeric3.ans
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
.000
|
||||
.01
|
2
test/fixtures/sort/numeric3.txt
vendored
Normal file
2
test/fixtures/sort/numeric3.txt
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
.01
|
||||
.000
|
2
test/fixtures/sort/numeric4.ans
vendored
Normal file
2
test/fixtures/sort/numeric4.ans
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
.00
|
||||
.01
|
2
test/fixtures/sort/numeric4.txt
vendored
Normal file
2
test/fixtures/sort/numeric4.txt
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
.01
|
||||
.00
|
2
test/fixtures/sort/numeric5.ans
vendored
Normal file
2
test/fixtures/sort/numeric5.ans
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
.022
|
||||
.024
|
2
test/fixtures/sort/numeric5.txt
vendored
Normal file
2
test/fixtures/sort/numeric5.txt
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
.022
|
||||
.024
|
46
test/sort.rs
Normal file
46
test/sort.rs
Normal file
|
@ -0,0 +1,46 @@
|
|||
use std::io::process::Command;
|
||||
use std::io::File;
|
||||
use std::string::String;
|
||||
|
||||
static PROGNAME: &'static str = "./sort";
|
||||
|
||||
#[test]
|
||||
fn numeric1() {
|
||||
numeric_helper(1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn numeric2() {
|
||||
numeric_helper(2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn numeric3() {
|
||||
numeric_helper(3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn numeric4() {
|
||||
numeric_helper(4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn numeric5() {
|
||||
numeric_helper(5);
|
||||
}
|
||||
|
||||
fn numeric_helper(test_num: int) {
|
||||
let mut cmd = Command::new(PROGNAME);
|
||||
cmd.arg("-n");
|
||||
let po = match cmd.clone().arg(format!("{}{}{}", "numeric", test_num, ".txt")).output() {
|
||||
Ok(p) => p,
|
||||
Err(err) => fail!("{}", err),
|
||||
};
|
||||
|
||||
let answer = match File::open(&Path::new(format!("{}{}{}", "numeric", test_num, ".ans")))
|
||||
.read_to_end() {
|
||||
Ok(answer) => answer,
|
||||
Err(err) => fail!("{}", err),
|
||||
};
|
||||
assert_eq!(String::from_utf8(po.output), String::from_utf8(answer));
|
||||
}
|
Loading…
Reference in a new issue