mirror of
https://github.com/uutils/coreutils.git
synced 2026-06-10 15:48:22 -07:00
Merge pull request #558 from kwantam/master
slight refactor in `unexpand` ; fix and optimize `factor` ; fix `touch`, `test`, `tac`, `shuf`, `sleep` ; merge PR from @ctjhoa for `cksum`
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
/src/*/gen_table
|
||||
/build/
|
||||
/target/
|
||||
/tmp/
|
||||
|
||||
@@ -30,7 +30,7 @@ RUSTCTESTFLAGS := $(RUSTCFLAGS)
|
||||
|
||||
# Handle config setup
|
||||
ifeq ($(ENABLE_LTO),y)
|
||||
RUSTCBINFLAGS := $(RUSTCLIBFLAGS) -Z lto
|
||||
RUSTCBINFLAGS := $(RUSTCLIBFLAGS) -C lto
|
||||
else
|
||||
RUSTCBINFLAGS := $(RUSTCLIBFLAGS)
|
||||
endif
|
||||
@@ -161,6 +161,7 @@ TEST_PROGS := \
|
||||
cat \
|
||||
cp \
|
||||
env \
|
||||
factor \
|
||||
mkdir \
|
||||
mv \
|
||||
nl \
|
||||
@@ -305,6 +306,9 @@ $(BUILDDIR)/mkuutils: mkuutils.rs | $(BUILDDIR)
|
||||
$(SRCDIR)/cksum/crc_table.rs: $(SRCDIR)/cksum/gen_table.rs
|
||||
cd $(SRCDIR)/cksum && $(RUSTC) $(RUSTCBINFLAGS) gen_table.rs && ./gen_table && $(RM) gen_table
|
||||
|
||||
$(SRCDIR)/factor/prime_table.rs: $(SRCDIR)/factor/gen_table.rs
|
||||
cd $(SRCDIR)/factor && $(RUSTC) $(RUSTCBINFLAGS) gen_table.rs && ./gen_table > $@ && $(RM) gen_table
|
||||
|
||||
crates:
|
||||
echo $(EXES)
|
||||
|
||||
|
||||
+5
-1
@@ -39,7 +39,11 @@ fn main() {
|
||||
util_map.push_str("map.insert(\"false\", uufalse as fn(Vec<String>) -> i32);\n");
|
||||
},
|
||||
_ => {
|
||||
crates.push_str(&(format!("extern crate {0} as uu{0};\n", prog))[..]);
|
||||
if prog == "test" {
|
||||
crates.push_str(&(format!("extern crate uu{0} as uu{0};\n", prog))[..]);
|
||||
} else {
|
||||
crates.push_str(&(format!("extern crate {0} as uu{0};\n", prog))[..]);
|
||||
}
|
||||
util_map.push_str(&(format!("map.insert(\"{prog}\", uu{prog}::uumain as fn(Vec<String>) -> i32);\n", prog = prog))[..]);
|
||||
}
|
||||
}
|
||||
|
||||
+16
-15
@@ -1,5 +1,5 @@
|
||||
#![crate_name = "cksum"]
|
||||
#![feature(collections, core, old_io, old_path, rustc_private)]
|
||||
#![feature(rustc_private)]
|
||||
|
||||
/*
|
||||
* This file is part of the uutils coreutils package.
|
||||
@@ -12,8 +12,9 @@
|
||||
|
||||
extern crate getopts;
|
||||
|
||||
use std::old_io::{EndOfFile, File, IoError, IoResult, print};
|
||||
use std::old_io::stdio::stdin_raw;
|
||||
use std::io::{self, stdin, Read, Write, BufReader};
|
||||
use std::path::Path;
|
||||
use std::fs::File;
|
||||
use std::mem;
|
||||
|
||||
use crc_table::CRC_TABLE;
|
||||
@@ -43,20 +44,18 @@ fn crc_final(mut crc: u32, mut length: usize) -> u32 {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn cksum(fname: &str) -> IoResult<(u32, usize)> {
|
||||
fn cksum(fname: &str) -> io::Result<(u32, usize)> {
|
||||
let mut crc = 0u32;
|
||||
let mut size = 0usize;
|
||||
|
||||
let mut stdin_buf;
|
||||
let mut file_buf;
|
||||
let rd = match fname {
|
||||
let file;
|
||||
let mut rd : Box<Read> = match fname {
|
||||
"-" => {
|
||||
stdin_buf = stdin_raw();
|
||||
&mut stdin_buf as &mut Reader
|
||||
Box::new(stdin())
|
||||
}
|
||||
_ => {
|
||||
file_buf = try!(File::open(&Path::new(fname)));
|
||||
&mut file_buf as &mut Reader
|
||||
file = try!(File::open(&Path::new(fname)));
|
||||
Box::new(BufReader::new(file))
|
||||
}
|
||||
};
|
||||
|
||||
@@ -64,12 +63,14 @@ fn cksum(fname: &str) -> IoResult<(u32, usize)> {
|
||||
loop {
|
||||
match rd.read(&mut bytes) {
|
||||
Ok(num_bytes) => {
|
||||
if num_bytes == 0 {
|
||||
return Ok((crc_final(crc, size), size));
|
||||
}
|
||||
for &b in bytes[..num_bytes].iter() {
|
||||
crc = crc_update(crc, b);
|
||||
}
|
||||
size += num_bytes;
|
||||
}
|
||||
Err(IoError { kind: EndOfFile, .. }) => return Ok((crc_final(crc, size), size)),
|
||||
Err(err) => return Err(err)
|
||||
}
|
||||
}
|
||||
@@ -81,7 +82,7 @@ pub fn uumain(args: Vec<String>) -> i32 {
|
||||
getopts::optflag("V", "version", "output version information and exit"),
|
||||
];
|
||||
|
||||
let matches = match getopts::getopts(args.tail(), &opts) {
|
||||
let matches = match getopts::getopts(&args[1..], &opts) {
|
||||
Ok(m) => m,
|
||||
Err(err) => panic!("{}", err),
|
||||
};
|
||||
@@ -92,7 +93,7 @@ pub fn uumain(args: Vec<String>) -> i32 {
|
||||
println!("Usage:");
|
||||
println!(" {} [OPTIONS] [FILE]...", NAME);
|
||||
println!("");
|
||||
print(getopts::usage("Print CRC and size for each file.", opts.as_slice()).as_slice());
|
||||
println!("{}", getopts::usage("Print CRC and size for each file.", opts.as_ref()));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -116,7 +117,7 @@ pub fn uumain(args: Vec<String>) -> i32 {
|
||||
|
||||
let mut exit_code = 0;
|
||||
for fname in files.iter() {
|
||||
match cksum(fname.as_slice()) {
|
||||
match cksum(fname.as_ref()) {
|
||||
Ok((crc, size)) => println!("{} {} {}", crc, size, fname),
|
||||
Err(err) => {
|
||||
show_error!("'{}' {}", fname, err);
|
||||
|
||||
+5
-5
@@ -13,11 +13,11 @@ pub fn from_str(string: &str) -> Result<f64, String> {
|
||||
return Err("empty string".to_string())
|
||||
}
|
||||
let slice = &string[..len - 1];
|
||||
let (numstr, times) = match string.char_at(len - 1) {
|
||||
's' | 'S' => (slice, 1usize),
|
||||
'm' | 'M' => (slice, 60usize),
|
||||
'h' | 'H' => (slice, 60usize * 60),
|
||||
'd' | 'D' => (slice, 60usize * 60 * 24),
|
||||
let (numstr, times) = match string.chars().next_back().unwrap() {
|
||||
's' | 'S' => (slice, 1),
|
||||
'm' | 'M' => (slice, 60),
|
||||
'h' | 'H' => (slice, 60 * 60),
|
||||
'd' | 'D' => (slice, 60 * 60 * 24),
|
||||
val => {
|
||||
if !val.is_alphabetic() {
|
||||
(string, 1)
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
DEPLIBS += rand
|
||||
+128
-31
@@ -1,10 +1,14 @@
|
||||
#![crate_name = "factor"]
|
||||
#![feature(collections, core, old_io, rustc_private)]
|
||||
#![feature(rustc_private)]
|
||||
|
||||
/*
|
||||
* This file is part of the uutils coreutils package.
|
||||
*
|
||||
* (c) T. Jameson Little <t.jameson.little@gmail.com>
|
||||
* (c) Wiktor Kuropatwa <wiktor.kuropatwa@gmail.com>
|
||||
* 20150223 added Pollard rho method implementation
|
||||
* (c) kwantam <kwantam@gmail.com>
|
||||
* 20150429 sped up trial division by adding table of prime inverses
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE file
|
||||
* that was distributed with this source code.
|
||||
@@ -12,66 +16,153 @@
|
||||
|
||||
extern crate getopts;
|
||||
extern crate libc;
|
||||
extern crate rand;
|
||||
|
||||
use std::vec::Vec;
|
||||
use std::old_io::BufferedReader;
|
||||
use std::old_io::stdio::stdin_raw;
|
||||
use numeric::*;
|
||||
use prime_table::P_INVS_U64;
|
||||
use std::cmp::{max, min};
|
||||
use std::io::{stdin, BufRead, BufReader, Write};
|
||||
use std::num::Wrapping;
|
||||
use std::mem::swap;
|
||||
use rand::weak_rng;
|
||||
use rand::distributions::{Range, IndependentSample};
|
||||
|
||||
#[path="../common/util.rs"]
|
||||
#[macro_use]
|
||||
mod util;
|
||||
mod numeric;
|
||||
mod prime_table;
|
||||
|
||||
static VERSION: &'static str = "1.0.0";
|
||||
static NAME: &'static str = "factor";
|
||||
|
||||
fn factor(mut num: u64) -> Vec<u64> {
|
||||
let mut ret = Vec::new();
|
||||
fn rho_pollard_pseudorandom_function(x: u64, a: u64, b: u64, num: u64) -> u64 {
|
||||
if num < 1 << 63 {
|
||||
(sm_mul(a, sm_mul(x, x, num), num) + b) % num
|
||||
} else {
|
||||
big_add(big_mul(a, big_mul(x, x, num), num), b, num)
|
||||
}
|
||||
}
|
||||
|
||||
fn gcd(mut a: u64, mut b: u64) -> u64 {
|
||||
while b > 0 {
|
||||
a %= b;
|
||||
swap(&mut a, &mut b);
|
||||
}
|
||||
a
|
||||
}
|
||||
|
||||
fn rho_pollard_find_divisor(num: u64) -> u64 {
|
||||
let range = Range::new(1, num);
|
||||
let mut rng = rand::weak_rng();
|
||||
let mut x = range.ind_sample(&mut rng);
|
||||
let mut y = x;
|
||||
let mut a = range.ind_sample(&mut rng);
|
||||
let mut b = range.ind_sample(&mut rng);
|
||||
|
||||
loop {
|
||||
x = rho_pollard_pseudorandom_function(x, a, b, num);
|
||||
y = rho_pollard_pseudorandom_function(y, a, b, num);
|
||||
y = rho_pollard_pseudorandom_function(y, a, b, num);
|
||||
let d = gcd(num, max(x, y) - min(x, y));
|
||||
if d == num {
|
||||
// Failure, retry with diffrent function
|
||||
x = range.ind_sample(&mut rng);
|
||||
y = x;
|
||||
a = range.ind_sample(&mut rng);
|
||||
b = range.ind_sample(&mut rng);
|
||||
} else if d > 1 {
|
||||
return d;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn rho_pollard_factor(num: u64, factors: &mut Vec<u64>) {
|
||||
if is_prime(num) {
|
||||
factors.push(num);
|
||||
return;
|
||||
}
|
||||
let divisor = rho_pollard_find_divisor(num);
|
||||
rho_pollard_factor(divisor, factors);
|
||||
rho_pollard_factor(num / divisor, factors);
|
||||
}
|
||||
|
||||
fn table_division(mut num: u64, factors: &mut Vec<u64>) {
|
||||
if num < 2 {
|
||||
return ret;
|
||||
return;
|
||||
}
|
||||
while num % 2 == 0 {
|
||||
num /= 2;
|
||||
ret.push(2);
|
||||
factors.push(2);
|
||||
}
|
||||
let mut i = 3;
|
||||
while i * i <= num {
|
||||
while num % i == 0 {
|
||||
num /= i;
|
||||
ret.push(i);
|
||||
if is_prime(num) {
|
||||
factors.push(num);
|
||||
return;
|
||||
}
|
||||
for &(prime, inv, ceil) in P_INVS_U64 {
|
||||
if num == 1 {
|
||||
break;
|
||||
}
|
||||
|
||||
// inv = prime^-1 mod 2^64
|
||||
// ceil = floor((2^64-1) / prime)
|
||||
// if (num * inv) mod 2^64 <= ceil, then prime divides num
|
||||
// See http://math.stackexchange.com/questions/1251327/
|
||||
// for a nice explanation.
|
||||
loop {
|
||||
let Wrapping(x) = Wrapping(num) * Wrapping(inv); // x = num * inv mod 2^64
|
||||
if x <= ceil {
|
||||
num = x;
|
||||
factors.push(prime);
|
||||
if is_prime(num) {
|
||||
factors.push(num);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
i += 2;
|
||||
}
|
||||
if num > 1 {
|
||||
ret.push(num);
|
||||
}
|
||||
ret
|
||||
|
||||
// do we still have more factoring to do?
|
||||
// Decide whether to use Pollard Rho or slow divisibility based on
|
||||
// number's size:
|
||||
//if num >= 1 << 63 {
|
||||
// number is too big to use rho pollard without overflowing
|
||||
//trial_division_slow(num, factors);
|
||||
//} else if num > 1 {
|
||||
// number is still greater than 1, but not so big that we have to worry
|
||||
rho_pollard_factor(num, factors);
|
||||
//}
|
||||
}
|
||||
|
||||
fn print_factors(num: u64) {
|
||||
print!("{}:", num);
|
||||
for fac in factor(num).iter() {
|
||||
|
||||
let mut factors = Vec::new();
|
||||
// we always start with table division, and go from there
|
||||
table_division(num, &mut factors);
|
||||
factors.sort();
|
||||
|
||||
for fac in factors.iter() {
|
||||
print!(" {}", fac);
|
||||
}
|
||||
println!("");
|
||||
}
|
||||
|
||||
fn print_factors_str(num_str: &str) {
|
||||
let num = match num_str.parse::<u64>() {
|
||||
Ok(x) => x,
|
||||
Err(e)=> { crash!(1, "{} not a number: {}", num_str, e); }
|
||||
};
|
||||
print_factors(num);
|
||||
if let Err(e) = num_str.parse::<u64>().and_then(|x| Ok(print_factors(x))) {
|
||||
show_warning!("{}: {}", num_str, e);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn uumain(args: Vec<String>) -> i32 {
|
||||
let program = args[0].as_slice();
|
||||
let opts = [
|
||||
getopts::optflag("h", "help", "show this help message"),
|
||||
getopts::optflag("v", "version", "print the version and exit"),
|
||||
];
|
||||
|
||||
let matches = match getopts::getopts(args.tail(), &opts) {
|
||||
let matches = match getopts::getopts(&args[1..], &opts) {
|
||||
Ok(m) => m,
|
||||
Err(f) => crash!(1, "Invalid options\n{}", f)
|
||||
};
|
||||
@@ -83,22 +174,28 @@ pub fn uumain(args: Vec<String>) -> i32 {
|
||||
\t{program} [NUMBER]...\n\
|
||||
\t{program} [OPTION]\n\
|
||||
\n\
|
||||
{usage}", program = program, version = VERSION, usage = getopts::usage("Print the prime factors of the given number(s). \
|
||||
{usage}",
|
||||
program = &args[0][..],
|
||||
version = VERSION,
|
||||
usage = getopts::usage("Print the prime factors of the given number(s). \
|
||||
If none are specified, read from standard input.", &opts));
|
||||
return 1;
|
||||
}
|
||||
|
||||
if matches.opt_present("version") {
|
||||
println!("{} {}", program, VERSION);
|
||||
println!("{} {}", &args[0][..], VERSION);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if matches.free.is_empty() {
|
||||
for line in BufferedReader::new(stdin_raw()).lines() {
|
||||
print_factors_str(line.unwrap().as_slice().trim());
|
||||
for line in BufReader::new(stdin()).lines() {
|
||||
for number in line.unwrap().split_whitespace() {
|
||||
print_factors_str(number);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for num_str in matches.free.iter() {
|
||||
print_factors_str(num_str.as_slice());
|
||||
print_factors_str(num_str);
|
||||
}
|
||||
}
|
||||
0
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* This file is part of the uutils coreutils package.
|
||||
*
|
||||
* (c) kwantam <kwantam@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE file
|
||||
* that was distributed with this source code.
|
||||
*/
|
||||
|
||||
//! Generate a table of the multiplicative inverses of p_i mod 2^64
|
||||
//! for the first 1027 odd primes (all 13 bit and smaller primes).
|
||||
//! You can supply a commandline argument to override the default
|
||||
//! value of 1027 for the number of entries in the table.
|
||||
//!
|
||||
//! 2 has no multiplicative inverse mode 2^64 because 2 | 2^64,
|
||||
//! and in any case divisibility by two is trivial by checking the LSB.
|
||||
|
||||
use sieve::Sieve;
|
||||
use std::env::args;
|
||||
use std::num::Wrapping;
|
||||
use std::u64::MAX as MAX_U64;
|
||||
|
||||
#[cfg(test)]
|
||||
use numeric::is_prime;
|
||||
|
||||
#[cfg(test)]
|
||||
mod numeric;
|
||||
|
||||
mod sieve;
|
||||
|
||||
// extended Euclid algorithm
|
||||
// precondition: a does not divide 2^64
|
||||
fn inv_mod_u64(a: u64) -> Option<u64> {
|
||||
let mut t = 0u64;
|
||||
let mut newt = 1u64;
|
||||
let mut r = 0u64;
|
||||
let mut newr = a;
|
||||
|
||||
while newr != 0 {
|
||||
let quot = if r == 0 {
|
||||
// special case when we're just starting out
|
||||
// This works because we know that
|
||||
// a does not divide 2^64, so floor(2^64 / a) == floor((2^64-1) / a);
|
||||
MAX_U64
|
||||
} else {
|
||||
r
|
||||
} / newr;
|
||||
|
||||
let (tp, Wrapping(newtp)) =
|
||||
(newt, Wrapping(t) - (Wrapping(quot) * Wrapping(newt)));
|
||||
t = tp;
|
||||
newt = newtp;
|
||||
|
||||
let (rp, Wrapping(newrp)) =
|
||||
(newr, Wrapping(r) - (Wrapping(quot) * Wrapping(newr)));
|
||||
r = rp;
|
||||
newr = newrp;
|
||||
}
|
||||
|
||||
if r > 1 { // not invertible
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(t)
|
||||
}
|
||||
|
||||
#[cfg_attr(test, allow(dead_code))]
|
||||
fn main() {
|
||||
// By default, we print the multiplicative inverses mod 2^64 of the first 1k primes
|
||||
let n = args().skip(1).next().unwrap_or("1027".to_string()).parse::<usize>().ok().unwrap_or(1027);
|
||||
|
||||
print!("{}", PREAMBLE);
|
||||
let mut cols = 3;
|
||||
|
||||
// we want a total of n + 1 values
|
||||
let mut primes = Sieve::new().take(n + 1);
|
||||
|
||||
// in each iteration of the for loop, we use the value yielded
|
||||
// by the previous iteration. This leaves one value left at the
|
||||
// end, which we call NEXT_PRIME.
|
||||
let mut x = primes.next().unwrap();
|
||||
for next in primes {
|
||||
// format the table
|
||||
let outstr = format!("({}, {}, {}),", x, inv_mod_u64(x).unwrap(), MAX_U64 / x);
|
||||
if cols + outstr.len() > MAX_WIDTH {
|
||||
print!("\n {}", outstr);
|
||||
cols = 4 + outstr.len();
|
||||
} else {
|
||||
print!(" {}", outstr);
|
||||
cols += 1 + outstr.len();
|
||||
}
|
||||
|
||||
x = next;
|
||||
}
|
||||
|
||||
print!("\n];\n\n#[allow(dead_code)]\npub const NEXT_PRIME: u64 = {};\n", x);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_generator_and_inverter() {
|
||||
let num = 10000;
|
||||
|
||||
let invs = Sieve::new().map(|x| inv_mod_u64(x).unwrap());
|
||||
assert!(Sieve::new().zip(invs).take(num).all(|(x, y)| {
|
||||
let Wrapping(z) = Wrapping(x) * Wrapping(y);
|
||||
is_prime(x) && z == 1
|
||||
}));
|
||||
}
|
||||
|
||||
const MAX_WIDTH: usize = 102;
|
||||
const PREAMBLE: &'static str =
|
||||
r##"/*
|
||||
* This file is part of the uutils coreutils package.
|
||||
*
|
||||
* (c) kwantam <kwantam@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE file
|
||||
* that was distributed with this source code.
|
||||
*/
|
||||
|
||||
// *** NOTE: this file was automatically generated.
|
||||
// Please do not edit by hand. Instead, modify and
|
||||
// re-run src/factor/gen_tables.rs.
|
||||
|
||||
pub const P_INVS_U64: &'static [(u64, u64, u64)] = &[
|
||||
"##;
|
||||
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* This file is part of the uutils coreutils package.
|
||||
*
|
||||
* (c) Wiktor Kuropatwa <wiktor.kuropatwa@gmail.com>
|
||||
* (c) kwantam <kwantam@gmail.com>
|
||||
* 20150507 added big_ routines to prevent overflow when num > 2^63
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE file
|
||||
* that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use std::u64::MAX as MAX_U64;
|
||||
use std::num::Wrapping;
|
||||
|
||||
pub fn big_add(a: u64, b: u64, m: u64) -> u64 {
|
||||
let Wrapping(msb_mod_m) = Wrapping(MAX_U64) - Wrapping(m) + Wrapping(1);
|
||||
let msb_mod_m = msb_mod_m % m;
|
||||
|
||||
let Wrapping(res) = Wrapping(a) + Wrapping(b);
|
||||
let res = if b <= MAX_U64 - a {
|
||||
res
|
||||
} else {
|
||||
(res + msb_mod_m) % m
|
||||
};
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
// computes (a + b) % m using the russian peasant algorithm
|
||||
// CAUTION: Will overflow if m >= 2^63
|
||||
pub fn sm_mul(mut a: u64, mut b: u64, m: u64) -> u64 {
|
||||
let mut result = 0;
|
||||
while b > 0 {
|
||||
if b & 1 != 0 {
|
||||
result = (result + a) % m;
|
||||
}
|
||||
a = (a << 1) % m;
|
||||
b >>= 1;
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
// computes (a + b) % m using the russian peasant algorithm
|
||||
// Only necessary when m >= 2^63; otherwise, just wastes time.
|
||||
pub fn big_mul(mut a: u64, mut b: u64, m: u64) -> u64 {
|
||||
// precompute 2^64 mod m, since we expect to wrap
|
||||
let Wrapping(msb_mod_m) = Wrapping(MAX_U64) - Wrapping(m) + Wrapping(1);
|
||||
let msb_mod_m = msb_mod_m % m;
|
||||
|
||||
let mut result = 0;
|
||||
while b > 0 {
|
||||
if b & 1 != 0 {
|
||||
let Wrapping(next_res) = Wrapping(result) + Wrapping(a);
|
||||
let next_res = next_res % m;
|
||||
result = if result <= MAX_U64 - a {
|
||||
next_res
|
||||
} else {
|
||||
(next_res + msb_mod_m) % m
|
||||
};
|
||||
}
|
||||
let Wrapping(next_a) = Wrapping(a) << 1;
|
||||
let next_a = next_a % m;
|
||||
a = if a < 1 << 63 {
|
||||
next_a
|
||||
} else {
|
||||
(next_a + msb_mod_m) % m
|
||||
};
|
||||
b >>= 1;
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
// computes a.pow(b) % m
|
||||
fn pow(mut a: u64, mut b: u64, m: u64, mul: fn(u64, u64, u64) -> u64) -> u64 {
|
||||
let mut result = 1;
|
||||
while b > 0 {
|
||||
if b & 1 != 0 {
|
||||
result = mul(result, a, m);
|
||||
}
|
||||
a = mul(a, a, m);
|
||||
b >>= 1;
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
fn witness(mut a: u64, exponent: u64, m: u64) -> bool {
|
||||
if a == 0 {
|
||||
return false;
|
||||
}
|
||||
|
||||
let mul = if m < 1 << 63 {
|
||||
sm_mul as fn(u64, u64, u64) -> u64
|
||||
} else {
|
||||
big_mul as fn(u64, u64, u64) -> u64
|
||||
};
|
||||
|
||||
if pow(a, m-1, m, mul) != 1 {
|
||||
return true;
|
||||
}
|
||||
a = pow(a, exponent, m, mul);
|
||||
if a == 1 {
|
||||
return false;
|
||||
}
|
||||
loop {
|
||||
if a == 1 {
|
||||
return true;
|
||||
}
|
||||
if a == m-1 {
|
||||
return false;
|
||||
}
|
||||
a = mul(a, a, m);
|
||||
}
|
||||
}
|
||||
|
||||
// uses deterministic (i.e., fixed witness set) Miller-Rabin test
|
||||
pub fn is_prime(num: u64) -> bool {
|
||||
if num < 2 {
|
||||
return false;
|
||||
}
|
||||
if num % 2 == 0 {
|
||||
return num == 2;
|
||||
}
|
||||
let mut exponent = num - 1;
|
||||
while exponent & 1 == 0 {
|
||||
exponent >>= 1;
|
||||
}
|
||||
|
||||
// These witnesses detect all composites up to at least 2^64.
|
||||
// Discovered by Jim Sinclair, according to http://miller-rabin.appspot.com
|
||||
let witnesses = [2, 325, 9375, 28178, 450775, 9780504, 1795265022];
|
||||
! witnesses.iter().any(|&wit| witness(wit % num, exponent, num))
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* This file is part of the uutils coreutils package.
|
||||
*
|
||||
* (c) kwantam <kwantam@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE file
|
||||
* that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use std::iter::repeat;
|
||||
|
||||
// A lazy Sieve of Eratosthenes
|
||||
// Not particularly efficient, but fine for generating a few thousand primes.
|
||||
pub struct Sieve {
|
||||
inner: Box<Iterator<Item=u64>>,
|
||||
filts: Vec<u64>,
|
||||
}
|
||||
|
||||
impl Iterator for Sieve {
|
||||
type Item = u64;
|
||||
|
||||
#[inline]
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.inner.size_hint()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<u64> {
|
||||
while let Some(n) = self.inner.next() {
|
||||
if self.filts.iter().all(|&x| n % x != 0) {
|
||||
self.filts.push(n);
|
||||
return Some(n);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl Sieve {
|
||||
#[inline]
|
||||
pub fn new() -> Sieve {
|
||||
fn next(s: &mut u64, t: u64) -> Option<u64> {
|
||||
let ret = Some(*s);
|
||||
*s = *s + t;
|
||||
ret
|
||||
}
|
||||
let next = next;
|
||||
|
||||
let odds_by_3 = Box::new(repeat(2).scan(3, next)) as Box<Iterator<Item=u64>>;
|
||||
|
||||
Sieve { inner: odds_by_3, filts: Vec::new() }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
DEPLIBS += rand
|
||||
+159
-92
@@ -1,5 +1,5 @@
|
||||
#![crate_name = "shuf"]
|
||||
#![feature(collections, core, old_io, old_path, rand, rustc_private)]
|
||||
#![feature(rustc_private)]
|
||||
|
||||
/*
|
||||
* This file is part of the uutils coreutils package.
|
||||
@@ -12,13 +12,13 @@
|
||||
|
||||
extern crate getopts;
|
||||
extern crate libc;
|
||||
extern crate rand;
|
||||
|
||||
use std::cmp;
|
||||
use std::old_io as io;
|
||||
use std::old_io::IoResult;
|
||||
use std::iter::{range_inclusive, RangeInclusive};
|
||||
use std::rand::{self, Rng};
|
||||
use std::usize;
|
||||
use rand::read::ReadRng;
|
||||
use rand::{Rng, ThreadRng};
|
||||
use std::fs::File;
|
||||
use std::io::{stdin, stdout, BufReader, BufWriter, Read, Write};
|
||||
use std::usize::MAX as MAX_USIZE;
|
||||
|
||||
#[path = "../common/util.rs"]
|
||||
#[macro_use]
|
||||
@@ -27,15 +27,13 @@ mod util;
|
||||
enum Mode {
|
||||
Default,
|
||||
Echo,
|
||||
InputRange(RangeInclusive<usize>)
|
||||
InputRange((usize, usize))
|
||||
}
|
||||
|
||||
static NAME: &'static str = "shuf";
|
||||
static VERSION: &'static str = "0.0.1";
|
||||
|
||||
pub fn uumain(args: Vec<String>) -> i32 {
|
||||
let program = args[0].clone();
|
||||
|
||||
let opts = [
|
||||
getopts::optflag("e", "echo", "treat each ARG as an input line"),
|
||||
getopts::optopt("i", "input-range", "treat each number LO through HI as an input line", "LO-HI"),
|
||||
@@ -47,7 +45,7 @@ pub fn uumain(args: Vec<String>) -> i32 {
|
||||
getopts::optflag("h", "help", "display this help and exit"),
|
||||
getopts::optflag("V", "version", "output version information and exit")
|
||||
];
|
||||
let mut matches = match getopts::getopts(args.tail(), &opts) {
|
||||
let mut matches = match getopts::getopts(&args[1..], &opts) {
|
||||
Ok(m) => m,
|
||||
Err(f) => {
|
||||
crash!(1, "{}", f)
|
||||
@@ -62,7 +60,7 @@ Usage:
|
||||
{prog} -i LO-HI [OPTION]...\n
|
||||
{usage}
|
||||
With no FILE, or when FILE is -, read standard input.",
|
||||
name = NAME, version = VERSION, prog = program,
|
||||
name = NAME, version = VERSION, prog = &args[0][..],
|
||||
usage = getopts::usage("Write a random permutation of the input lines to standard output.", &opts));
|
||||
} else if matches.opt_present("version") {
|
||||
println!("{} v{}", NAME, VERSION);
|
||||
@@ -76,10 +74,9 @@ With no FILE, or when FILE is -, read standard input.",
|
||||
}
|
||||
match parse_range(range) {
|
||||
Ok(m) => Mode::InputRange(m),
|
||||
Err((msg, code)) => {
|
||||
show_error!("{}", msg);
|
||||
return code;
|
||||
}
|
||||
Err(msg) => {
|
||||
crash!(1, "{}", msg);
|
||||
},
|
||||
}
|
||||
}
|
||||
None => {
|
||||
@@ -88,13 +85,19 @@ With no FILE, or when FILE is -, read standard input.",
|
||||
} else {
|
||||
if matches.free.len() == 0 {
|
||||
matches.free.push("-".to_string());
|
||||
} else if matches.free.len() > 1 {
|
||||
show_error!("extra operand '{}'", &matches.free[1][..]);
|
||||
}
|
||||
Mode::Default
|
||||
}
|
||||
}
|
||||
};
|
||||
let repeat = matches.opt_present("repeat");
|
||||
let zero = matches.opt_present("zero-terminated");
|
||||
let sep = if matches.opt_present("zero-terminated") {
|
||||
0x00 as u8
|
||||
} else {
|
||||
0x0a as u8
|
||||
};
|
||||
let count = match matches.opt_str("head-count") {
|
||||
Some(cnt) => match cnt.parse::<usize>() {
|
||||
Ok(val) => val,
|
||||
@@ -103,102 +106,166 @@ With no FILE, or when FILE is -, read standard input.",
|
||||
return 1;
|
||||
}
|
||||
},
|
||||
None => usize::MAX
|
||||
None => MAX_USIZE,
|
||||
};
|
||||
let output = matches.opt_str("output");
|
||||
let random = matches.opt_str("random-source");
|
||||
match shuf(matches.free, mode, repeat, zero, count, output, random) {
|
||||
Err(f) => {
|
||||
show_error!("{}", f);
|
||||
return 1;
|
||||
|
||||
match mode {
|
||||
Mode::Echo => {
|
||||
// XXX: this doesn't correctly handle non-UTF-8 cmdline args
|
||||
let mut evec = matches.free.iter().map(|a| a.as_bytes()).collect::<Vec<&[u8]>>();
|
||||
find_seps(&mut evec, sep);
|
||||
shuf_bytes(&mut evec, repeat, count, sep, output, random);
|
||||
},
|
||||
_ => {}
|
||||
Mode::InputRange((b, e)) => {
|
||||
let rvec = (b..e).map(|x| format!("{}", x)).collect::<Vec<String>>();
|
||||
let mut rvec = rvec.iter().map(|a| a.as_bytes()).collect::<Vec<&[u8]>>();
|
||||
shuf_bytes(&mut rvec, repeat, count, sep, output, random);
|
||||
},
|
||||
Mode::Default => {
|
||||
let fdata = read_input_file(&matches.free[0][..]);
|
||||
let mut fdata = vec!(&fdata[..]);
|
||||
find_seps(&mut fdata, sep);
|
||||
shuf_bytes(&mut fdata, repeat, count, sep, output, random);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
0
|
||||
}
|
||||
|
||||
fn shuf(input: Vec<String>, mode: Mode, repeat: bool, zero: bool, count: usize, output: Option<String>, random: Option<String>) -> IoResult<()> {
|
||||
match mode {
|
||||
Mode::Echo => shuf_lines(input, repeat, zero, count, output, random),
|
||||
Mode::InputRange(range) => shuf_lines(range.map(|num| num.to_string()).collect(), repeat, zero, count, output, random),
|
||||
Mode::Default => {
|
||||
let lines: Vec<String> = input.into_iter().flat_map(|filename| {
|
||||
let slice = filename.as_slice();
|
||||
let mut file_buf;
|
||||
let mut stdin_buf;
|
||||
let mut file = io::BufferedReader::new(
|
||||
if slice == "-" {
|
||||
stdin_buf = io::stdio::stdin_raw();
|
||||
&mut stdin_buf as &mut Reader
|
||||
} else {
|
||||
file_buf = crash_if_err!(1, io::File::open(&Path::new(slice)));
|
||||
&mut file_buf as &mut Reader
|
||||
}
|
||||
);
|
||||
let mut lines = vec!();
|
||||
for line in file.lines() {
|
||||
let mut line = crash_if_err!(1, line);
|
||||
line.pop();
|
||||
lines.push(line);
|
||||
fn read_input_file(filename: &str) -> Vec<u8> {
|
||||
let mut file = BufReader::new(
|
||||
if filename == "-" {
|
||||
Box::new(stdin()) as Box<Read>
|
||||
} else {
|
||||
match File::open(filename) {
|
||||
Ok(f) => Box::new(f) as Box<Read>,
|
||||
Err(e) => crash!(1, "failed to open '{}': {}", filename, e),
|
||||
}
|
||||
});
|
||||
|
||||
let mut data = Vec::new();
|
||||
match file.read_to_end(&mut data) {
|
||||
Err(e) => crash!(1, "failed reading '{}': {}", filename, e),
|
||||
Ok(_) => (),
|
||||
};
|
||||
|
||||
data
|
||||
}
|
||||
|
||||
fn find_seps(data: &mut Vec<&[u8]>, sep: u8) {
|
||||
// need to use for loop so we don't borrow the vector as we modify it in place
|
||||
// basic idea:
|
||||
// * We don't care about the order of the result. This lets us slice the slices
|
||||
// without making a new vector.
|
||||
// * Starting from the end of the vector, we examine each element.
|
||||
// * If that element contains the separator, we remove it from the vector,
|
||||
// and then sub-slice it into slices that do not contain the separator.
|
||||
// * We maintain the invariant throughout that each element in the vector past
|
||||
// the ith element does not have any separators remaining.
|
||||
for i in (0..data.len()).rev() {
|
||||
if data[i].contains(&sep) {
|
||||
let this = data.swap_remove(i);
|
||||
let mut p = 0;
|
||||
let mut i = 1;
|
||||
loop {
|
||||
if i == this.len() {
|
||||
break;
|
||||
}
|
||||
lines.into_iter()
|
||||
}).collect();
|
||||
shuf_lines(lines, repeat, zero, count, output, random)
|
||||
|
||||
if this[i] == sep {
|
||||
data.push(&this[p..i]);
|
||||
p = i + 1;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
if p < this.len() {
|
||||
data.push(&this[p..i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn shuf_bytes(input: &mut Vec<&[u8]>, repeat: bool, count: usize, sep: u8, output: Option<String>, random: Option<String>) {
|
||||
let mut output = BufWriter::new(
|
||||
match output {
|
||||
None => Box::new(stdout()) as Box<Write>,
|
||||
Some(s) => match File::create(&s[..]) {
|
||||
Ok(f) => Box::new(f) as Box<Write>,
|
||||
Err(e) => crash!(1, "failed to open '{}' for writing: {}", &s[..], e),
|
||||
},
|
||||
});
|
||||
|
||||
let mut rng = match random {
|
||||
Some(r) => WrappedRng::RngFile(rand::read::ReadRng::new(match File::open(&r[..]) {
|
||||
Ok(f) => f,
|
||||
Err(e) => crash!(1, "failed to open random source '{}': {}", &r[..], e),
|
||||
})),
|
||||
None => WrappedRng::RngDefault(rand::thread_rng()),
|
||||
};
|
||||
|
||||
// we're generating a random usize. To keep things fair, we take this number mod ceil(log2(length+1))
|
||||
let mut len_mod = 1;
|
||||
let mut len = input.len();
|
||||
while len > 0 {
|
||||
len >>= 1;
|
||||
len_mod <<= 1;
|
||||
}
|
||||
drop(len);
|
||||
|
||||
let mut count = count;
|
||||
while count > 0 && input.len() > 0 {
|
||||
let mut r = input.len();
|
||||
while r >= input.len() {
|
||||
r = rng.next_usize() % len_mod;
|
||||
}
|
||||
|
||||
// write the randomly chosen value and the separator
|
||||
output.write_all(input[r]).unwrap_or_else(|e| crash!(1, "write failed: {}", e));
|
||||
output.write_all(&[sep]).unwrap_or_else(|e| crash!(1, "write failed: {}", e));
|
||||
|
||||
// if we do not allow repeats, remove the chosen value from the input vector
|
||||
if !repeat {
|
||||
// shrink the mask if we will drop below a power of 2
|
||||
if input.len() % 2 == 0 && len_mod > 2 {
|
||||
len_mod >>= 1;
|
||||
}
|
||||
input.swap_remove(r);
|
||||
}
|
||||
|
||||
count -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_range(input_range: String) -> Result<(usize, usize), String> {
|
||||
let split: Vec<&str> = input_range.split('-').collect();
|
||||
if split.len() != 2 {
|
||||
Err("invalid range format".to_string())
|
||||
} else {
|
||||
let begin = match split[0].parse::<usize>() {
|
||||
Ok(m) => m,
|
||||
Err(e)=> return Err(format!("{} is not a valid number: {}", split[0], e)),
|
||||
};
|
||||
let end = match split[1].parse::<usize>() {
|
||||
Ok(m) => m,
|
||||
Err(e)=> return Err(format!("{} is not a valid number: {}", split[1], e)),
|
||||
};
|
||||
Ok((begin, end + 1))
|
||||
}
|
||||
}
|
||||
|
||||
enum WrappedRng {
|
||||
RngFile(rand::reader::ReaderRng<io::File>),
|
||||
RngFile(rand::read::ReadRng<File>),
|
||||
RngDefault(rand::ThreadRng),
|
||||
}
|
||||
|
||||
impl WrappedRng {
|
||||
fn next_u32(&mut self) -> u32 {
|
||||
fn next_usize(&mut self) -> usize {
|
||||
match self {
|
||||
&mut WrappedRng::RngFile(ref mut r) => r.next_u32(),
|
||||
&mut WrappedRng::RngDefault(ref mut r) => r.next_u32(),
|
||||
&mut WrappedRng::RngFile(ref mut r) => r.next_u32() as usize,
|
||||
&mut WrappedRng::RngDefault(ref mut r) => r.next_u32() as usize,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn shuf_lines(mut lines: Vec<String>, repeat: bool, zero: bool, count: usize, outname: Option<String>, random: Option<String>) -> IoResult<()> {
|
||||
let mut output = match outname {
|
||||
Some(name) => Box::new(io::BufferedWriter::new(try!(io::File::create(&Path::new(name))))) as Box<Writer>,
|
||||
None => Box::new(io::stdout()) as Box<Writer>
|
||||
};
|
||||
let mut rng = match random {
|
||||
Some(name) => WrappedRng::RngFile(rand::reader::ReaderRng::new(try!(io::File::open(&Path::new(name))))),
|
||||
None => WrappedRng::RngDefault(rand::thread_rng()),
|
||||
};
|
||||
let mut len = lines.len();
|
||||
let max = if repeat { count } else { cmp::min(count, len) };
|
||||
for _ in range(0, max) {
|
||||
let idx = rng.next_u32() as usize % len;
|
||||
try!(write!(output, "{}{}", lines[idx], if zero { '\0' } else { '\n' }));
|
||||
if !repeat {
|
||||
lines.remove(idx);
|
||||
len -= 1;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_range(input_range: String) -> Result<RangeInclusive<usize>, (String, i32)> {
|
||||
let split: Vec<&str> = input_range.as_slice().split('-').collect();
|
||||
if split.len() != 2 {
|
||||
Err(("invalid range format".to_string(), 1))
|
||||
} else {
|
||||
let begin = match split[0].parse::<usize>() {
|
||||
Ok(m) => m,
|
||||
Err(e)=> return Err((format!("{} is not a valid number: {}", split[0], e), 1))
|
||||
};
|
||||
let end = match split[1].parse::<usize>() {
|
||||
Ok(m) => m,
|
||||
Err(e)=> return Err((format!("{} is not a valid number: {}", split[1], e), 1))
|
||||
};
|
||||
Ok(range_inclusive(begin, end))
|
||||
}
|
||||
}
|
||||
|
||||
+22
-25
@@ -1,5 +1,5 @@
|
||||
#![crate_name = "sleep"]
|
||||
#![feature(collections, core, old_io, rustc_private, std_misc)]
|
||||
#![feature(rustc_private)]
|
||||
|
||||
/*
|
||||
* This file is part of the uutils coreutils package.
|
||||
@@ -13,9 +13,9 @@
|
||||
extern crate getopts;
|
||||
extern crate libc;
|
||||
|
||||
use std::f64;
|
||||
use std::old_io::{print, timer};
|
||||
use std::time::duration::{self, Duration};
|
||||
use std::io::Write;
|
||||
use std::thread::sleep_ms;
|
||||
use std::u32::MAX as U32_MAX;
|
||||
|
||||
#[path = "../common/util.rs"]
|
||||
#[macro_use]
|
||||
@@ -27,13 +27,11 @@ mod time;
|
||||
static NAME: &'static str = "sleep";
|
||||
|
||||
pub fn uumain(args: Vec<String>) -> i32 {
|
||||
let program = args[0].clone();
|
||||
|
||||
let opts = [
|
||||
getopts::optflag("h", "help", "display this help and exit"),
|
||||
getopts::optflag("V", "version", "output version information and exit")
|
||||
];
|
||||
let matches = match getopts::getopts(args.tail(), &opts) {
|
||||
let matches = match getopts::getopts(&args[1..], &opts) {
|
||||
Ok(m) => m,
|
||||
Err(f) => {
|
||||
show_error!("{}", f);
|
||||
@@ -45,20 +43,20 @@ pub fn uumain(args: Vec<String>) -> i32 {
|
||||
println!("sleep 1.0.0");
|
||||
println!("");
|
||||
println!("Usage:");
|
||||
println!(" {0} NUMBER[SUFFIX]", program);
|
||||
println!(" {0} NUMBER[SUFFIX]", &args[0][..]);
|
||||
println!("or");
|
||||
println!(" {0} OPTION", program);
|
||||
println!(" {0} OPTION", &args[0][..]);
|
||||
println!("");
|
||||
print(getopts::usage("Pause for NUMBER seconds. SUFFIX may be 's' for seconds (the default),
|
||||
println!("{}", getopts::usage("Pause for NUMBER seconds. SUFFIX may be 's' for seconds (the default),
|
||||
'm' for minutes, 'h' for hours or 'd' for days. Unlike most implementations
|
||||
that require NUMBER be an integer, here NUMBER may be an arbitrary floating
|
||||
point number. Given two or more arguments, pause for the amount of time
|
||||
specified by the sum of their values.", &opts).as_slice());
|
||||
specified by the sum of their values.", &opts));
|
||||
} else if matches.opt_present("version") {
|
||||
println!("sleep 1.0.0");
|
||||
} else if matches.free.is_empty() {
|
||||
show_error!("missing an argument");
|
||||
show_error!("for help, try '{0} --help'", program);
|
||||
show_error!("for help, try '{0} --help'", &args[0][..]);
|
||||
return 1;
|
||||
} else {
|
||||
sleep(matches.free);
|
||||
@@ -68,19 +66,18 @@ specified by the sum of their values.", &opts).as_slice());
|
||||
}
|
||||
|
||||
fn sleep(args: Vec<String>) {
|
||||
let sleep_time = args.iter().fold(0.0, |result, arg| {
|
||||
let num = match time::from_str(arg.as_slice()) {
|
||||
Ok(m) => m,
|
||||
Err(f) => {
|
||||
crash!(1, "{}", f)
|
||||
}
|
||||
};
|
||||
result + num
|
||||
});
|
||||
let sleep_dur = if sleep_time == f64::INFINITY {
|
||||
duration::MAX
|
||||
let sleep_time = args.iter().fold(0.0, |result, arg|
|
||||
match time::from_str(&arg[..]) {
|
||||
Ok(m) => m + result,
|
||||
Err(f) => crash!(1, "{}", f),
|
||||
});
|
||||
|
||||
let sleep_dur = if sleep_time > (U32_MAX as f64) {
|
||||
U32_MAX
|
||||
} else {
|
||||
Duration::seconds(sleep_time as i64)
|
||||
(1000.0 * sleep_time) as u32
|
||||
};
|
||||
timer::sleep(sleep_dur);
|
||||
sleep_ms(sleep_dur);
|
||||
}
|
||||
|
||||
|
||||
|
||||
+75
-31
@@ -1,5 +1,5 @@
|
||||
#![crate_name = "tac"]
|
||||
#![feature(collections, core, old_io, old_path, rustc_private)]
|
||||
#![feature(rustc_private)]
|
||||
|
||||
/*
|
||||
* This file is part of the uutils coreutils package.
|
||||
@@ -13,7 +13,8 @@
|
||||
extern crate getopts;
|
||||
extern crate libc;
|
||||
|
||||
use std::old_io as io;
|
||||
use std::fs::File;
|
||||
use std::io::{stdin, stdout, BufReader, Read, Stdout, Write};
|
||||
|
||||
#[path = "../common/util.rs"]
|
||||
#[macro_use]
|
||||
@@ -23,8 +24,6 @@ static NAME: &'static str = "tac";
|
||||
static VERSION: &'static str = "1.0.0";
|
||||
|
||||
pub fn uumain(args: Vec<String>) -> i32 {
|
||||
let program = args[0].clone();
|
||||
|
||||
let opts = [
|
||||
getopts::optflag("b", "before", "attach the separator before instead of after"),
|
||||
getopts::optflag("r", "regex", "interpret the sequence as a regular expression (NOT IMPLEMENTED)"),
|
||||
@@ -32,7 +31,7 @@ pub fn uumain(args: Vec<String>) -> i32 {
|
||||
getopts::optflag("h", "help", "display this help and exit"),
|
||||
getopts::optflag("V", "version", "output version information and exit")
|
||||
];
|
||||
let matches = match getopts::getopts(args.tail(), &opts) {
|
||||
let matches = match getopts::getopts(&args[1..], &opts) {
|
||||
Ok(m) => m,
|
||||
Err(f) => crash!(1, "{}", f)
|
||||
};
|
||||
@@ -40,7 +39,7 @@ pub fn uumain(args: Vec<String>) -> i32 {
|
||||
println!("tac {}", VERSION);
|
||||
println!("");
|
||||
println!("Usage:");
|
||||
println!(" {0} [OPTION]... [FILE]...", program);
|
||||
println!(" {0} [OPTION]... [FILE]...", &args[0][..]);
|
||||
println!("");
|
||||
print!("{}", getopts::usage("Write each file to standard output, last line first.", &opts));
|
||||
} else if matches.opt_present("version") {
|
||||
@@ -63,41 +62,86 @@ pub fn uumain(args: Vec<String>) -> i32 {
|
||||
} else {
|
||||
matches.free
|
||||
};
|
||||
tac(files, before, regex, separator.as_slice());
|
||||
tac(files, before, regex, &separator[..]);
|
||||
}
|
||||
|
||||
0
|
||||
}
|
||||
|
||||
fn tac(filenames: Vec<String>, before: bool, _: bool, separator: &str) {
|
||||
for filename in filenames.into_iter() {
|
||||
let mut file = io::BufferedReader::new(
|
||||
if filename.as_slice() == "-" {
|
||||
Box::new(io::stdio::stdin_raw()) as Box<Reader>
|
||||
let mut out = stdout();
|
||||
let sbytes = separator.as_bytes();
|
||||
let slen = sbytes.len();
|
||||
|
||||
for filename in filenames.iter() {
|
||||
let mut file = BufReader::new(
|
||||
if filename == "-" {
|
||||
Box::new(stdin()) as Box<Read>
|
||||
} else {
|
||||
let r = crash_if_err!(1, io::File::open(&Path::new(filename)));
|
||||
Box::new(r) as Box<Reader>
|
||||
match File::open(filename) {
|
||||
Ok(f) => Box::new(f) as Box<Read>,
|
||||
Err(e) => {
|
||||
show_warning!("failed to open '{}' for reading: {}", filename, e);
|
||||
continue;
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
let mut data = Vec::new();
|
||||
match file.read_to_end(&mut data) {
|
||||
Err(e) => {
|
||||
show_warning!("failed to read '{}': {}", filename, e);
|
||||
continue;
|
||||
},
|
||||
Ok(_) => (),
|
||||
};
|
||||
|
||||
// find offsets in string of all separators
|
||||
let mut offsets = Vec::new();
|
||||
let mut i = 0;
|
||||
loop {
|
||||
if i + slen > data.len() {
|
||||
break;
|
||||
}
|
||||
|
||||
if &data[i..i+slen] == sbytes {
|
||||
offsets.push(i);
|
||||
i += slen;
|
||||
} else {
|
||||
i += 1;
|
||||
}
|
||||
);
|
||||
let mut data = crash_if_err!(1, file.read_to_string());
|
||||
if data.as_slice().ends_with("\n") {
|
||||
// removes blank line that is inserted otherwise
|
||||
let mut buf = data.to_string();
|
||||
let len = buf.len();
|
||||
buf.truncate(len - 1);
|
||||
data = buf.to_string();
|
||||
}
|
||||
let split_vec: Vec<&str> = data.as_slice().split_str(separator).collect();
|
||||
let rev: String = split_vec.iter().rev().fold(String::new(), |mut a, &b| {
|
||||
if before {
|
||||
a.push_str(separator);
|
||||
a.push_str(b);
|
||||
drop(i);
|
||||
|
||||
// if there isn't a separator at the end of the file, fake it
|
||||
if offsets.len() == 0 || *offsets.last().unwrap() < data.len() - slen {
|
||||
offsets.push(data.len());
|
||||
}
|
||||
|
||||
let mut prev = *offsets.last().unwrap();
|
||||
let mut start = true;
|
||||
for off in offsets.iter().rev().skip(1) {
|
||||
// correctly handle case of no final separator in file
|
||||
if start && prev == data.len() {
|
||||
show_line(&mut out, &[], &data[*off+slen..prev], before);
|
||||
start = false;
|
||||
} else {
|
||||
a.push_str(b);
|
||||
a.push_str(separator);
|
||||
show_line(&mut out, sbytes, &data[*off+slen..prev], before);
|
||||
}
|
||||
a
|
||||
});
|
||||
print!("{}", rev);
|
||||
prev = *off;
|
||||
}
|
||||
show_line(&mut out, sbytes, &data[0..prev], before);
|
||||
}
|
||||
}
|
||||
|
||||
fn show_line(out: &mut Stdout, sep: &[u8], dat: &[u8], before: bool) {
|
||||
if before {
|
||||
out.write_all(sep).unwrap_or_else(|e| crash!(1, "failed to write to stdout: {}", e));
|
||||
}
|
||||
|
||||
out.write_all(dat).unwrap_or_else(|e| crash!(1, "failed to write to stdout: {}", e));
|
||||
|
||||
if !before {
|
||||
out.write_all(sep).unwrap_or_else(|e| crash!(1, "failed to write to stdout: {}", e));
|
||||
}
|
||||
}
|
||||
|
||||
+8
-8
@@ -1,5 +1,5 @@
|
||||
#![crate_name = "test"]
|
||||
#![feature(core, os, std_misc)]
|
||||
#![feature(convert)]
|
||||
|
||||
/*
|
||||
* This file is part of the uutils coreutils package.
|
||||
@@ -13,16 +13,16 @@
|
||||
extern crate libc;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::CString;
|
||||
use std::os::{args_as_bytes};
|
||||
use std::ffi::{CString, OsString};
|
||||
use std::env::{args_os};
|
||||
use std::str::{from_utf8};
|
||||
|
||||
static NAME: &'static str = "test";
|
||||
|
||||
// TODO: decide how to handle non-UTF8 input for all the utils
|
||||
pub fn uumain(_: Vec<String>) -> i32 {
|
||||
let args = args_as_bytes();
|
||||
let args: Vec<&[u8]> = args.iter().map(|a| a.as_slice()).collect();
|
||||
let args = args_os().collect::<Vec<OsString>>();
|
||||
let args = args.iter().map(|a| a.to_bytes().unwrap()).collect::<Vec<&[u8]>>();
|
||||
if args.len() == 0 {
|
||||
return 2;
|
||||
}
|
||||
@@ -30,7 +30,7 @@ pub fn uumain(_: Vec<String>) -> i32 {
|
||||
if !args[0].ends_with(NAME.as_bytes()) {
|
||||
&args[1..]
|
||||
} else {
|
||||
args.as_slice()
|
||||
&args[..]
|
||||
};
|
||||
let args = match args[0] {
|
||||
b"[" => match args[args.len() - 1] {
|
||||
@@ -83,6 +83,7 @@ fn two(args: &[&[u8]], error: &mut bool) -> bool {
|
||||
fn three(args: &[&[u8]], error: &mut bool) -> bool {
|
||||
match args[1] {
|
||||
b"=" => args[0] == args[2],
|
||||
b"==" => args[0] == args[2],
|
||||
b"!=" => args[0] != args[2],
|
||||
b"-eq" => integers(args[0], args[2], IntegerCondition::Equal),
|
||||
b"-ne" => integers(args[0], args[2], IntegerCondition::Unequal),
|
||||
@@ -191,6 +192,7 @@ fn dispatch_four(args: &mut &[&[u8]], error: &mut bool) -> (bool, usize) {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum Precedence {
|
||||
Unknown = 0,
|
||||
Paren, // FIXME: this is useless (parentheses have not been implemented)
|
||||
@@ -201,8 +203,6 @@ enum Precedence {
|
||||
UnOp
|
||||
}
|
||||
|
||||
impl Copy for Precedence {}
|
||||
|
||||
fn parse_expr(mut args: &[&[u8]], error: &mut bool) -> bool {
|
||||
if args.len() == 0 {
|
||||
false
|
||||
|
||||
+58
-31
@@ -1,5 +1,5 @@
|
||||
#![crate_name = "touch"]
|
||||
#![feature(collections, core, old_io, old_path, rustc_private)]
|
||||
#![feature(rustc_private, path_ext, fs_time)]
|
||||
|
||||
/*
|
||||
* This file is part of the uutils coreutils package.
|
||||
@@ -10,11 +10,19 @@
|
||||
* that was distributed with this source code.
|
||||
*/
|
||||
|
||||
extern crate libc;
|
||||
extern crate getopts;
|
||||
extern crate time;
|
||||
|
||||
use std::old_io::File;
|
||||
use std::old_io::fs::PathExtensions;
|
||||
use libc::types::os::arch::c95::c_char;
|
||||
use libc::types::os::arch::posix01::stat as stat_t;
|
||||
use libc::funcs::posix88::stat_::stat as c_stat;
|
||||
use libc::funcs::posix01::stat_::lstat as c_lstat;
|
||||
|
||||
use std::fs::{set_file_times, File, PathExt};
|
||||
use std::io::{Error, Write};
|
||||
use std::mem::uninitialized;
|
||||
use std::path::Path;
|
||||
|
||||
#[path = "../common/util.rs"]
|
||||
#[macro_use]
|
||||
@@ -40,7 +48,7 @@ pub fn uumain(args: Vec<String>) -> i32 {
|
||||
getopts::optflag("V", "version", "output version information and exit"),
|
||||
];
|
||||
|
||||
let matches = match getopts::getopts(args.tail(), &opts) {
|
||||
let matches = match getopts::getopts(&args[1..], &opts) {
|
||||
Ok(m) => m,
|
||||
Err(e) => panic!("Invalid options\n{}", e)
|
||||
};
|
||||
@@ -71,14 +79,12 @@ pub fn uumain(args: Vec<String>) -> i32 {
|
||||
|
||||
let (mut atime, mut mtime) =
|
||||
if matches.opt_present("reference") {
|
||||
let path = Path::new(matches.opt_str("reference").unwrap().to_string());
|
||||
let stat = stat(&path, !matches.opt_present("no-dereference"));
|
||||
(stat.accessed, stat.modified)
|
||||
stat(&matches.opt_str("reference").unwrap()[..], !matches.opt_present("no-dereference"))
|
||||
} else if matches.opts_present(&["date".to_string(), "t".to_string()]) {
|
||||
let timestamp = if matches.opt_present("date") {
|
||||
parse_date(matches.opt_str("date").unwrap().as_slice())
|
||||
parse_date(matches.opt_str("date").unwrap().as_ref())
|
||||
} else {
|
||||
parse_timestamp(matches.opt_str("t").unwrap().as_slice())
|
||||
parse_timestamp(matches.opt_str("t").unwrap().as_ref())
|
||||
};
|
||||
(timestamp, timestamp)
|
||||
} else {
|
||||
@@ -88,17 +94,20 @@ pub fn uumain(args: Vec<String>) -> i32 {
|
||||
};
|
||||
|
||||
for filename in matches.free.iter() {
|
||||
let path = Path::new(filename.to_string());
|
||||
let path = &filename[..];
|
||||
|
||||
if !path.exists() {
|
||||
if ! Path::new(path).exists() {
|
||||
// no-dereference included here for compatibility
|
||||
if matches.opts_present(&["no-create".to_string(), "no-dereference".to_string()]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
match File::create(&path) {
|
||||
Ok(fd) => fd,
|
||||
Err(e) => panic!("Unable to create file: {}\n{}", filename, e.desc)
|
||||
match File::create(path) {
|
||||
Err(e) => {
|
||||
show_warning!("cannot touch '{}': {}", path, e);
|
||||
continue;
|
||||
},
|
||||
_ => (),
|
||||
};
|
||||
|
||||
// Minor optimization: if no reference time was specified, we're done.
|
||||
@@ -110,44 +119,62 @@ pub fn uumain(args: Vec<String>) -> i32 {
|
||||
// If changing "only" atime or mtime, grab the existing value of the other.
|
||||
// Note that "-a" and "-m" may be passed together; this is not an xor.
|
||||
if matches.opts_present(&["a".to_string(), "m".to_string(), "time".to_string()]) {
|
||||
let stat = stat(&path, !matches.opt_present("no-dereference"));
|
||||
let st = stat(path, !matches.opt_present("no-dereference"));
|
||||
let time = matches.opt_strs("time");
|
||||
|
||||
if !(matches.opt_present("a") ||
|
||||
time.contains(&"access".to_string()) ||
|
||||
time.contains(&"atime".to_string()) ||
|
||||
time.contains(&"use".to_string())) {
|
||||
atime = stat.accessed;
|
||||
atime = st.0;
|
||||
}
|
||||
|
||||
if !(matches.opt_present("m") ||
|
||||
time.contains(&"modify".to_string()) ||
|
||||
time.contains(&"mtime".to_string())) {
|
||||
mtime = stat.modified;
|
||||
mtime = st.1;
|
||||
}
|
||||
}
|
||||
|
||||
match std::old_io::fs::change_file_times(&path, atime, mtime) {
|
||||
Ok(t) => t,
|
||||
Err(e) => panic!("Unable to modify times\n{}", e.desc)
|
||||
}
|
||||
// this follows symlinks and thus does not work correctly for the -h flag
|
||||
// need to use lutimes() c function on supported platforms
|
||||
match set_file_times(path, atime, mtime) {
|
||||
Err(e) => show_warning!("cannot touch '{}': {}", path, e),
|
||||
_ => (),
|
||||
};
|
||||
}
|
||||
|
||||
0
|
||||
}
|
||||
|
||||
fn stat(path: &Path, follow: bool) -> std::old_io::FileStat {
|
||||
if follow {
|
||||
match std::old_io::fs::stat(path) {
|
||||
Ok(stat) => stat,
|
||||
Err(e) => panic!("Unable to open file\n{}", e.desc)
|
||||
}
|
||||
fn stat(path: &str, follow: bool) -> (u64, u64) {
|
||||
let stat_fn = if follow {
|
||||
c_stat
|
||||
} else {
|
||||
match std::old_io::fs::lstat(path) {
|
||||
Ok(stat) => stat,
|
||||
Err(e) => panic!("Unable to open file\n{}", e.desc)
|
||||
}
|
||||
c_lstat
|
||||
};
|
||||
let mut st: stat_t = unsafe { uninitialized() };
|
||||
let result = unsafe { stat_fn(path.as_ptr() as *const c_char, &mut st as *mut stat_t) };
|
||||
|
||||
if result < 0 {
|
||||
crash!(1, "failed to get attributes of '{}': {}", path, Error::last_os_error());
|
||||
}
|
||||
|
||||
// set_file_times expects milliseconds
|
||||
let atime = if st.st_atime_nsec == 0 {
|
||||
st.st_atime * 1000
|
||||
} else {
|
||||
st.st_atime_nsec / 1000
|
||||
} as u64;
|
||||
|
||||
// set_file_times expects milliseconds
|
||||
let mtime = if st.st_mtime_nsec == 0 {
|
||||
st.st_mtime * 1000
|
||||
} else {
|
||||
st.st_mtime_nsec / 1000
|
||||
} as u64;
|
||||
|
||||
(atime, mtime)
|
||||
}
|
||||
|
||||
fn parse_date(str: &str) -> u64 {
|
||||
|
||||
+20
-30
@@ -142,14 +142,22 @@ fn next_tabstop(tabstops: &[usize], col: usize) -> Option<usize> {
|
||||
}
|
||||
}
|
||||
|
||||
fn write_tabs(mut output: &mut BufWriter<Stdout>, tabstops: &[usize], mut scol: usize, col: usize) {
|
||||
while let Some(nts) = next_tabstop(tabstops, scol) {
|
||||
if col < scol + nts {
|
||||
break;
|
||||
}
|
||||
fn write_tabs(mut output: &mut BufWriter<Stdout>, tabstops: &[usize],
|
||||
mut scol: usize, col: usize, prevtab: bool, init: bool, amode: bool) {
|
||||
// This conditional establishes the following:
|
||||
// We never turn a single space before a non-blank into
|
||||
// a tab, unless it's at the start of the line.
|
||||
let ai = init || amode;
|
||||
if (ai && !prevtab && col > scol + 1) ||
|
||||
(col > scol && (init || ai && prevtab)) {
|
||||
while let Some(nts) = next_tabstop(tabstops, scol) {
|
||||
if col < scol + nts {
|
||||
break;
|
||||
}
|
||||
|
||||
safe_unwrap!(output.write_all("\t".as_bytes()));
|
||||
scol += nts;
|
||||
safe_unwrap!(output.write_all("\t".as_bytes()));
|
||||
scol += nts;
|
||||
}
|
||||
}
|
||||
|
||||
while col > scol {
|
||||
@@ -194,15 +202,9 @@ fn unexpand(options: Options) {
|
||||
while byte < buf.len() {
|
||||
// when we have a finite number of columns, never convert past the last column
|
||||
if lastcol > 0 && col >= lastcol {
|
||||
if (pctype != Tab && col > scol + 1) ||
|
||||
(col > scol && (init || pctype == Tab)) {
|
||||
write_tabs(&mut output, ts, scol, col);
|
||||
} else if col > scol {
|
||||
safe_unwrap!(output.write_all(" ".as_bytes()));
|
||||
}
|
||||
scol = col;
|
||||
|
||||
write_tabs(&mut output, ts, scol, col, pctype == Tab, init, true);
|
||||
safe_unwrap!(output.write_all(&buf[byte..]));
|
||||
scol = col;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -253,15 +255,8 @@ fn unexpand(options: Options) {
|
||||
}
|
||||
},
|
||||
Other | Backspace => { // always
|
||||
// never turn a single space before a non-blank into a tab
|
||||
// unless it's at the start of the line
|
||||
if (tabs_buffered && pctype != Tab && col > scol + 1) ||
|
||||
(col > scol && (init || (tabs_buffered && pctype == Tab))) {
|
||||
write_tabs(&mut output, ts, scol, col);
|
||||
} else if col > scol {
|
||||
safe_unwrap!(output.write_all(" ".as_bytes()));
|
||||
}
|
||||
init = false;
|
||||
write_tabs(&mut output, ts, scol, col, pctype == Tab, init, options.aflag);
|
||||
init = false; // no longer at the start of a line
|
||||
col = if ctype == Other { // use computed width
|
||||
col + cwidth
|
||||
} else if col > 0 { // Backspace case, but only if col > 0
|
||||
@@ -279,12 +274,7 @@ fn unexpand(options: Options) {
|
||||
}
|
||||
|
||||
// write out anything remaining
|
||||
if col > scol + 1 || (init && col > scol) {
|
||||
write_tabs(&mut output, ts, scol, col);
|
||||
} else if col > scol {
|
||||
safe_unwrap!(output.write_all(" ".as_bytes()));
|
||||
}
|
||||
|
||||
write_tabs(&mut output, ts, scol, col, pctype == Tab, init, true);
|
||||
buf.truncate(0); // clear out the buffer
|
||||
}
|
||||
}
|
||||
|
||||
+536
File diff suppressed because it is too large
Load Diff
+10
-4
@@ -1,6 +1,13 @@
|
||||
#![allow(unstable)]
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
use std::old_io::process::Command;
|
||||
use std::process::Command;
|
||||
|
||||
static EXE: &'static str = "./test";
|
||||
|
||||
@@ -26,6 +33,5 @@ fn test_op_prec_and_or_2() {
|
||||
#[test]
|
||||
fn test_or_as_filename() {
|
||||
let status = Command::new(EXE).arg("x").arg("-a").arg("-z").arg("-o").status();
|
||||
assert!(status.unwrap().matches_exit_status(1));
|
||||
assert_eq!(status.unwrap().code(), Some(1));
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user