coreutils/src/truncate/truncate.rs

224 lines
7.9 KiB
Rust
Raw Normal View History

2014-07-06 08:13:36 +00:00
#![crate_name = "truncate"]
2015-02-03 22:49:03 +00:00
#![feature(collections, core, io, libc, path, rustc_private, std_misc)]
2014-02-01 05:18:57 +00:00
/*
* This file is part of the uutils coreutils package.
*
* (c) Arcterus <arcterus@mail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
extern crate getopts;
extern crate libc;
2014-02-01 05:18:57 +00:00
2014-12-30 19:11:06 +00:00
use std::ascii::AsciiExt;
2015-01-29 07:29:31 +00:00
use std::old_io::{File, Open, ReadWrite, fs};
use std::old_io::fs::PathExtensions;
2014-02-23 22:17:48 +00:00
#[path = "../common/util.rs"]
2015-01-08 12:54:22 +00:00
#[macro_use]
mod util;
2014-02-01 05:18:57 +00:00
2015-01-08 12:56:41 +00:00
#[derive(Eq, PartialEq)]
2014-02-01 05:18:57 +00:00
enum TruncateMode {
Reference,
Extend,
Reduce,
AtMost,
AtLeast,
RoundDown,
RoundUp
}
static NAME: &'static str = "truncate";
2015-01-10 13:07:39 +00:00
pub fn uumain(args: Vec<String>) -> isize {
2014-07-20 01:13:55 +00:00
let program = args[0].clone();
2014-02-01 05:18:57 +00:00
2014-05-30 08:35:54 +00:00
let opts = [
getopts::optflag("c", "no-create", "do not create files that do not exist"),
getopts::optflag("o", "io-blocks", "treat SIZE as the number of I/O blocks of the file rather than bytes (NOT IMPLEMENTED)"),
getopts::optopt("r", "reference", "base the size of each file on the size of RFILE", "RFILE"),
getopts::optopt("s", "size", "set or adjust the size of each file according to SIZE, which is in bytes unless --io-blocks is specified", "SIZE"),
getopts::optflag("h", "help", "display this help and exit"),
getopts::optflag("V", "version", "output version information and exit")
2014-02-01 05:18:57 +00:00
];
2014-11-19 20:55:25 +00:00
let matches = match getopts::getopts(args.tail(), &opts) {
2014-02-01 05:18:57 +00:00
Ok(m) => m,
Err(f) => {
2014-06-15 10:50:40 +00:00
crash!(1, "{}", f)
2014-02-01 05:18:57 +00:00
}
};
if matches.opt_present("help") {
println!("truncate 1.0.0");
println!("");
println!("Usage:");
2014-11-21 09:09:43 +00:00
println!(" {0} [OPTION]... FILE...", program);
2014-02-01 05:18:57 +00:00
println!("");
2014-11-19 20:55:25 +00:00
print!("{}", getopts::usage("Shrink or extend the size of each file to the specified size.", &opts));
print!("
SIZE is an integer with an optional prefix and optional unit.
The available units (K, M, G, T, P, E, Z, and Y) use the following format:
2014-10-05 01:09:52 +00:00
'KB' => 1000 (kilobytes)
'K' => 1024 (kibibytes)
'MB' => 1000*1000 (megabytes)
'M' => 1024*1024 (mebibytes)
'GB' => 1000*1000*1000 (gigabytes)
'G' => 1024*1024*1024 (gibibytes)
SIZE may also be prefixed by one of the following to adjust the size of each
file based on its current size:
2014-10-05 01:09:52 +00:00
'+' => extend by
'-' => reduce by
'<' => at most
'>' => at least
'/' => round down to multiple of
'%' => round up to multiple of
");
2014-02-01 05:18:57 +00:00
} else if matches.opt_present("version") {
println!("truncate 1.0.0");
} else if matches.free.is_empty() {
2014-06-09 03:26:51 +00:00
show_error!("missing an argument");
return 1;
2014-02-01 05:18:57 +00:00
} else {
let no_create = matches.opt_present("no-create");
let io_blocks = matches.opt_present("io-blocks");
let reference = matches.opt_str("reference");
let size = matches.opt_str("size");
if reference.is_none() && size.is_none() {
crash!(1, "you must specify either --reference or --size");
2014-02-01 05:18:57 +00:00
} else {
match truncate(no_create, io_blocks, reference, size, matches.free) {
Ok(()) => ( /* pass */ ),
Err(e) => return e
}
2014-02-01 05:18:57 +00:00
}
}
0
2014-02-01 05:18:57 +00:00
}
2015-01-10 18:07:08 +00:00
fn truncate(no_create: bool, _: bool, reference: Option<String>, size: Option<String>, filenames: Vec<String>) -> Result<(), isize> {
2014-02-01 05:18:57 +00:00
let (refsize, mode) = match reference {
Some(rfilename) => {
let rfile = match File::open(&Path::new(rfilename.clone())) {
2014-02-05 04:17:36 +00:00
Ok(m) => m,
Err(f) => {
2014-07-09 08:29:50 +00:00
crash!(1, "{}", f.to_string())
2014-02-05 04:17:36 +00:00
}
};
match fs::stat(rfile.path()) {
2014-11-19 20:50:37 +00:00
Ok(stat) => (stat.size, TruncateMode::Reference),
Err(f) => {
2014-07-09 08:29:50 +00:00
show_error!("{}", f.to_string());
return Err(1);
}
}
2014-02-01 05:18:57 +00:00
}
2014-05-17 10:32:14 +00:00
None => parse_size(size.unwrap().as_slice())
2014-02-01 05:18:57 +00:00
};
for filename in filenames.iter() {
2014-05-17 10:32:14 +00:00
let filename = filename.as_slice();
2014-02-01 05:18:57 +00:00
let path = Path::new(filename);
2014-02-05 04:17:36 +00:00
if path.exists() || !no_create {
match File::open_mode(&path, Open, ReadWrite) {
Ok(mut file) => {
let fsize = match fs::stat(file.path()) {
Ok(stat) => stat.size,
Err(f) => {
2014-07-09 08:29:50 +00:00
show_warning!("{}", f.to_string());
continue;
}
};
2014-02-05 04:17:36 +00:00
let tsize = match mode {
2014-11-19 20:50:37 +00:00
TruncateMode::Reference => refsize,
TruncateMode::Extend => fsize + refsize,
TruncateMode::Reduce => fsize - refsize,
TruncateMode::AtMost => if fsize > refsize { refsize } else { fsize },
TruncateMode::AtLeast => if fsize < refsize { refsize } else { fsize },
TruncateMode::RoundDown => fsize - fsize % refsize,
TruncateMode::RoundUp => fsize + fsize % refsize
2014-02-05 04:17:36 +00:00
};
match file.truncate(tsize as i64) {
Ok(_) => {}
Err(f) => {
2014-07-09 08:29:50 +00:00
show_error!("{}", f.to_string());
return Err(1);
2014-02-05 04:17:36 +00:00
}
}
2014-02-01 05:18:57 +00:00
}
2014-02-05 04:17:36 +00:00
Err(f) => {
2014-07-09 08:29:50 +00:00
show_error!("{}", f.to_string());
return Err(1);
2014-02-05 04:17:36 +00:00
}
2014-02-01 05:18:57 +00:00
}
2014-02-05 04:17:36 +00:00
}
2014-02-01 05:18:57 +00:00
}
Ok(())
2014-02-01 05:18:57 +00:00
}
2014-05-17 10:32:14 +00:00
fn parse_size(size: &str) -> (u64, TruncateMode) {
2014-02-01 05:18:57 +00:00
let mode = match size.char_at(0) {
2014-11-19 20:50:37 +00:00
'+' => TruncateMode::Extend,
'-' => TruncateMode::Reduce,
'<' => TruncateMode::AtMost,
'>' => TruncateMode::AtLeast,
'/' => TruncateMode::RoundDown,
'*' => TruncateMode::RoundUp,
_ => TruncateMode::Reference /* assume that the size is just a number */
2014-02-01 05:18:57 +00:00
};
let bytes = {
let mut slice =
2014-11-19 20:50:37 +00:00
if mode == TruncateMode::Reference {
2014-02-01 05:18:57 +00:00
let size: &str = size;
size
} else {
&size[1..]
2014-02-01 05:18:57 +00:00
};
if slice.char_at(slice.len() - 1).is_alphabetic() {
slice = &slice[..slice.len() - 1];
2014-02-01 05:18:57 +00:00
if slice.len() > 0 && slice.char_at(slice.len() - 1).is_alphabetic() {
slice = &slice[..slice.len() - 1];
2014-02-01 05:18:57 +00:00
}
}
slice
2014-11-05 11:18:43 +00:00
}.to_string();
let mut number: u64 = match bytes.as_slice().parse() {
Ok(num) => num,
Err(e) => {
crash!(1, "'{}' is not a valid number: {}", size, e)
2014-02-01 05:18:57 +00:00
}
};
if size.char_at(size.len() - 1).is_alphabetic() {
2014-12-30 19:11:06 +00:00
number *= match size.char_at(size.len() - 1).to_ascii_uppercase() {
'B' => match size.char_at(size.len() - 2).to_ascii_uppercase() {
2014-02-01 05:18:57 +00:00
'K' => 1000,
'M' => 1000 * 1000,
'G' => 1000 * 1000 * 1000,
'T' => 1000 * 1000 * 1000 * 1000,
'P' => 1000 * 1000 * 1000 * 1000 * 1000,
'E' => 1000 * 1000 * 1000 * 1000 * 1000 * 1000,
'Z' => 1000 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000,
'Y' => 1000 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000,
letter => {
crash!(1, "'{}B' is not a valid suffix.", letter)
2014-02-01 05:18:57 +00:00
}
},
'K' => 1024,
'M' => 1024 * 1024,
'G' => 1024 * 1024 * 1024,
'T' => 1024 * 1024 * 1024 * 1024,
'P' => 1024 * 1024 * 1024 * 1024 * 1024,
'E' => 1024 * 1024 * 1024 * 1024 * 1024 * 1024,
'Z' => 1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024,
'Y' => 1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024,
letter => {
crash!(1, "'{}' is not a valid suffix.", letter)
2014-02-01 05:18:57 +00:00
}
};
}
(number, mode)
2014-02-01 05:18:57 +00:00
}