diff --git a/deps/Cargo.toml b/deps/Cargo.toml index 9607091c3..0e48c371f 100644 --- a/deps/Cargo.toml +++ b/deps/Cargo.toml @@ -22,3 +22,4 @@ winapi = "0.2" advapi32-sys = "0.1" kernel32-sys = "0.1" walker = "^1.0.0" +filetime = "0.1" diff --git a/src/mv/deps.mk b/src/mv/deps.mk index b6534caec..0e162f8f0 100644 --- a/src/mv/deps.mk +++ b/src/mv/deps.mk @@ -1 +1 @@ -DEPLIBS += time +DEPLIBS += kernel32 winapi filetime time diff --git a/src/touch/deps.mk b/src/touch/deps.mk index b6534caec..0e162f8f0 100644 --- a/src/touch/deps.mk +++ b/src/touch/deps.mk @@ -1 +1 @@ -DEPLIBS += time +DEPLIBS += kernel32 winapi filetime time diff --git a/src/touch/touch.rs b/src/touch/touch.rs index 05d2157c2..02b690cf3 100644 --- a/src/touch/touch.rs +++ b/src/touch/touch.rs @@ -1,5 +1,4 @@ #![crate_name = "touch"] -#![feature(fs_time)] /* * This file is part of the uutils coreutils package. @@ -13,14 +12,11 @@ extern crate getopts; extern crate libc; extern crate time; +extern crate filetime; -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}; +use filetime::*; +use std::fs::{self, File}; use std::io::{Error, Write}; -use std::mem::uninitialized; use std::path::Path; #[path = "../common/util.rs"] @@ -35,6 +31,24 @@ use filesystem::UUPathExt; static NAME: &'static str = "touch"; static VERSION: &'static str = "1.0.0"; +// Since touch's date/timestamp parsing doesn't account for timezone, the +// returned value from time::strptime() is UTC. We get system's timezone to +// localize the time. +macro_rules! to_local( + ($exp:expr) => ({ + let mut tm = $exp; + tm.tm_utcoff = time::now().tm_utcoff; + tm + }) +); + +macro_rules! local_tm_to_filetime( + ($exp:expr) => ({ + let ts = $exp.to_timespec(); + FileTime::from_seconds_since_1970(ts.sec as u64, ts.nsec as u32) + }) +); + pub fn uumain(args: Vec) -> i32 { let mut opts = getopts::Options::new(); @@ -92,8 +106,7 @@ pub fn uumain(args: Vec) -> i32 { }; (timestamp, timestamp) } else { - // FIXME: Should use Timespec. https://github.com/mozilla/rust/issues/10301 - let now = (time::get_time().sec * 1000) as u64; + let now = local_tm_to_filetime!(time::now()); (now, now) }; @@ -142,7 +155,7 @@ pub fn uumain(args: Vec) -> i32 { // 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) { + match filetime::set_file_times(path, atime, mtime) { Err(e) => show_warning!("cannot touch '{}': {}", path, e), _ => (), }; @@ -151,48 +164,34 @@ pub fn uumain(args: Vec) -> i32 { 0 } -fn stat(path: &str, follow: bool) -> (u64, u64) { - let stat_fn = if follow { - c_stat +fn stat(path: &str, follow: bool) -> (FileTime, FileTime) { + let metadata = if follow { + fs::symlink_metadata(path) } else { - c_lstat + fs::metadata(path) }; - 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()); + match metadata { + Ok(m) => ( + FileTime::from_last_access_time(&m), + FileTime::from_last_modification_time(&m) + ), + Err(_) => 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 { +fn parse_date(str: &str) -> FileTime { // This isn't actually compatible with GNU touch, but there doesn't seem to // be any simple specification for what format this parameter allows and I'm // not about to implement GNU parse_datetime. // http://git.savannah.gnu.org/gitweb/?p=gnulib.git;a=blob_plain;f=lib/parse-datetime.y match time::strptime(str, "%c") { - Ok(tm) => (tm.to_timespec().sec * 1000) as u64, + Ok(tm) => local_tm_to_filetime!(to_local!(tm)), Err(e) => panic!("Unable to parse date\n{}", e) } } -fn parse_timestamp(str: &str) -> u64 { +fn parse_timestamp(str: &str) -> FileTime { let format = match str.chars().count() { 15 => "%Y%m%d%H%M.%S", 12 => "%Y%m%d%H%M", @@ -204,7 +203,7 @@ fn parse_timestamp(str: &str) -> u64 { }; match time::strptime(str, format) { - Ok(tm) => (tm.to_timespec().sec * 1000) as u64, + Ok(tm) => local_tm_to_filetime!(to_local!(tm)), Err(e) => panic!("Unable to parse timestamp\n{}", e) } } diff --git a/test/mv.rs b/test/mv.rs index 74f2943b9..9ab811ae9 100644 --- a/test/mv.rs +++ b/test/mv.rs @@ -1,10 +1,10 @@ -#![feature(fs_time)] - extern crate libc; extern crate time; +extern crate kernel32; +extern crate winapi; +extern crate filetime; -use std::fs; -use std::path::Path; +use filetime::*; use std::process::Command; use util::*; @@ -272,9 +272,11 @@ fn test_mv_update_option() { touch(file_a); touch(file_b); - let now = (time::get_time().sec * 1000) as u64; - fs::set_file_times(Path::new(file_a), now, now).unwrap(); - fs::set_file_times(Path::new(file_b), now, now+3600).unwrap(); + let ts = time::now().to_timespec(); + let now = FileTime::from_seconds_since_1970(ts.sec as u64, ts.nsec as u32); + let later = FileTime::from_seconds_since_1970(ts.sec as u64 + 3600, ts.nsec as u32); + filetime::set_file_times(file_a, now, now).unwrap(); + filetime::set_file_times(file_b, now, later).unwrap(); let result1 = run(Command::new(PROGNAME).arg("--update").arg(file_a).arg(file_b));