From 0f4667d94200f609023769a1138d7a7984715842 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Wed, 15 Jul 2020 20:24:26 +0200 Subject: [PATCH 1/3] ADDED: path_canonical/2, obtaining the canonical absolute path. This addresses a remaining aspect of #511. --- src/clause_types.rs | 3 +++ src/lib/files.pl | 30 +++++++++++++++++++++++++++++- src/machine/system_calls.rs | 25 +++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 1 deletion(-) diff --git a/src/clause_types.rs b/src/clause_types.rs index 89e4d00d..c4546623 100644 --- a/src/clause_types.rs +++ b/src/clause_types.rs @@ -178,6 +178,7 @@ pub enum SystemClauseType { MakeDirectory, DeleteFile, WorkingDirectory, + PathCanonical, DeleteAttribute, DeleteHeadAttribute, DynamicModuleResolution(usize), @@ -345,6 +346,7 @@ impl SystemClauseType { &SystemClauseType::MakeDirectory => clause_name!("$make_directory"), &SystemClauseType::DeleteFile => clause_name!("$delete_file"), &SystemClauseType::WorkingDirectory => clause_name!("$working_directory"), + &SystemClauseType::PathCanonical => clause_name!("$path_canonical"), &SystemClauseType::REPL(REPLCodePtr::CompileBatch) => clause_name!("$compile_batch"), &SystemClauseType::REPL(REPLCodePtr::UseModule) => clause_name!("$use_module"), &SystemClauseType::REPL(REPLCodePtr::UseQualifiedModule) => { @@ -681,6 +683,7 @@ impl SystemClauseType { ("$make_directory", 1) => Some(SystemClauseType::MakeDirectory), ("$delete_file", 1) => Some(SystemClauseType::DeleteFile), ("$working_directory", 2) => Some(SystemClauseType::WorkingDirectory), + ("$path_canonical", 2) => Some(SystemClauseType::PathCanonical), ("$use_module", 1) => Some(SystemClauseType::REPL(REPLCodePtr::UseModule)), ("$use_module_from_file", 1) => Some(SystemClauseType::REPL(REPLCodePtr::UseModuleFromFile)), diff --git a/src/lib/files.pl b/src/lib/files.pl index 9e54bf33..69847b29 100644 --- a/src/lib/files.pl +++ b/src/lib/files.pl @@ -52,7 +52,8 @@ directory_exists/1, delete_file/1, make_directory/1, - working_directory/2]). + working_directory/2, + path_canonical/2]). :- use_module(library(error)). :- use_module(library(lists)). @@ -87,7 +88,34 @@ delete_file(File) :- list_of_chars(File), '$delete_file'(File). +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Dir0 is the current working directory, and the working directory + is changed to Dir. + + Use working_directory(Ds, Ds) to determine the current working directory, + and leave it as is. +- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + working_directory(Dir0, Dir) :- can_be(list, Dir0), can_be(list, Dir), '$working_directory'(Dir0, Dir). + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + True iff Cs is the canonical, absolute path of Ps. + + All intermediate components are normalized, and all symbolic links + are resolved. + + The predicate fails in the following situations, though not + necessarily *only* in these cases: + + 1. Ps is a path that does not exist. + 2. A non-final component in Ps is not a directory. +- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +path_canonical(Ps, Cs) :- + must_be(list, Ps), + maplist(must_be(character), Ps), + can_be(list, Cs), + '$path_canonical'(Ps, Cs). diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 77ef1366..2f357873 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -918,6 +918,31 @@ impl MachineState { return Ok(()); } } + &SystemClauseType::PathCanonical => { + let path = self.heap_pstr_iter(self[temp_v!(1)]).to_string(); + + match fs::canonicalize(path) { + Ok(canonical) => { + let cs = + match canonical.to_str() { + Some(s) => { s } + _ => { + let stub = MachineError::functor_stub(clause_name!("path_canonical"), 2); + let err = MachineError::representation_error(RepFlag::Character); + let err = self.error_form(err, stub); + + return Err(err); + } + }; + let chars = self.heap.put_complete_string(cs); + self.unify(self[temp_v!(2)], chars); + } + _ => { + self.fail = true; + return Ok(()); + } + } + } &SystemClauseType::AtEndOfExpansion => { if self.cp == LocalCodePtr::TopLevel(0, 0) { self.at_end_of_expansion = true; From 4f386b3e5e10b978196ea786a3e7cdbf73a41e1c Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Thu, 16 Jul 2020 22:56:10 +0200 Subject: [PATCH 2/3] ADDED: file_modification_time/2, obtaining a time stamp. This addresses a remaining aspect of #511. --- src/clause_types.rs | 3 +++ src/lib/files.pl | 13 ++++++++++- src/machine/system_calls.rs | 46 +++++++++++++++++++++++++++---------- 3 files changed, 49 insertions(+), 13 deletions(-) diff --git a/src/clause_types.rs b/src/clause_types.rs index c4546623..97aac77a 100644 --- a/src/clause_types.rs +++ b/src/clause_types.rs @@ -179,6 +179,7 @@ pub enum SystemClauseType { DeleteFile, WorkingDirectory, PathCanonical, + FileModificationTime, DeleteAttribute, DeleteHeadAttribute, DynamicModuleResolution(usize), @@ -347,6 +348,7 @@ impl SystemClauseType { &SystemClauseType::DeleteFile => clause_name!("$delete_file"), &SystemClauseType::WorkingDirectory => clause_name!("$working_directory"), &SystemClauseType::PathCanonical => clause_name!("$path_canonical"), + &SystemClauseType::FileModificationTime => clause_name!("$file_modification_time"), &SystemClauseType::REPL(REPLCodePtr::CompileBatch) => clause_name!("$compile_batch"), &SystemClauseType::REPL(REPLCodePtr::UseModule) => clause_name!("$use_module"), &SystemClauseType::REPL(REPLCodePtr::UseQualifiedModule) => { @@ -684,6 +686,7 @@ impl SystemClauseType { ("$delete_file", 1) => Some(SystemClauseType::DeleteFile), ("$working_directory", 2) => Some(SystemClauseType::WorkingDirectory), ("$path_canonical", 2) => Some(SystemClauseType::PathCanonical), + ("$file_modification_time", 2) => Some(SystemClauseType::FileModificationTime), ("$use_module", 1) => Some(SystemClauseType::REPL(REPLCodePtr::UseModule)), ("$use_module_from_file", 1) => Some(SystemClauseType::REPL(REPLCodePtr::UseModuleFromFile)), diff --git a/src/lib/files.pl b/src/lib/files.pl index 69847b29..481ed08d 100644 --- a/src/lib/files.pl +++ b/src/lib/files.pl @@ -53,10 +53,12 @@ delete_file/1, make_directory/1, working_directory/2, - path_canonical/2]). + path_canonical/2, + file_modification_time/2]). :- use_module(library(error)). :- use_module(library(lists)). +:- use_module(library(charsio)). list_of_chars(Cs) :- must_be(list, Cs), @@ -119,3 +121,12 @@ path_canonical(Ps, Cs) :- maplist(must_be(character), Ps), can_be(list, Cs), '$path_canonical'(Ps, Cs). + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + T is the modification time of File. + T is a time stamp, suitable for use in format_time//2 in library(time). +- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +file_modification_time(File, T) :- + '$file_modification_time'(File, T0), + read_term_from_chars(T0, T). diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 2f357873..6c8ee329 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -943,6 +943,22 @@ impl MachineState { } } } + &SystemClauseType::FileModificationTime => { + let file = self.heap_pstr_iter(self[temp_v!(1)]).to_string(); + + if let Ok(md) = fs::metadata(file) { + if let Ok(time) = md.modified() { + let chars = self.systemtime_to_timestamp(time); + self.unify(self[temp_v!(2)], chars); + } else { + self.fail = true; + return Ok(()); + } + } else { + self.fail = true; + return Ok(()); + } + } &SystemClauseType::AtEndOfExpansion => { if self.cp == LocalCodePtr::TopLevel(0, 0) { self.at_end_of_expansion = true; @@ -3227,18 +3243,7 @@ impl MachineState { self.unify(a1, addr); } &SystemClauseType::CurrentTime => { - let system_time = SystemTime::now(); - let datetime: DateTime = system_time.into(); - - let mut fstr = "[".to_string(); - let specifiers = vec!["d","m","Y","y","H","M","S","b","B","a","A","w","u","U","W","j","D","x","v"]; - for spec in specifiers { - fstr.push_str(&format!("'{}'=\"%{}\", ", spec, spec).to_string()); - } - fstr.push_str("finis]."); - let str = { let s = datetime.format(&fstr).to_string(); - self.heap.put_complete_string(&s) - }; + let str = self.systemtime_to_timestamp(SystemTime::now()); self.unify(self[temp_v!(1)], str); } &SystemClauseType::OpDeclaration => { @@ -5718,6 +5723,23 @@ impl MachineState { return_from_clause!(self.last_call, self) } + pub(super) + fn systemtime_to_timestamp( + &mut self, + system_time: SystemTime + ) -> Addr { + let datetime: DateTime = system_time.into(); + + let mut fstr = "[".to_string(); + let specifiers = vec!["d","m","Y","y","H","M","S","b","B","a","A","w","u","U","W","j","D","x","v"]; + for spec in specifiers { + fstr.push_str(&format!("'{}'=\"%{}\", ", spec, spec).to_string()); + } + fstr.push_str("finis]."); + let s = datetime.format(&fstr).to_string(); + self.heap.put_complete_string(&s) + } + pub(super) fn string_encoding_bytes( &mut self, From 8668aee7fdff914a58a3a356c24c35911736aa66 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Thu, 16 Jul 2020 23:33:52 +0200 Subject: [PATCH 3/3] ENHANCED: Reorder fields in time stamps so that (@<)/2 is meaningful. --- src/lib/files.pl | 2 ++ src/machine/system_calls.rs | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/lib/files.pl b/src/lib/files.pl index 481ed08d..adaa1e94 100644 --- a/src/lib/files.pl +++ b/src/lib/files.pl @@ -125,6 +125,8 @@ path_canonical(Ps, Cs) :- /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - T is the modification time of File. T is a time stamp, suitable for use in format_time//2 in library(time). + + For two time stamps A and B, if A precedes B, then A @< B holds. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ file_modification_time(File, T) :- diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 6c8ee329..9e969c7b 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -5731,7 +5731,7 @@ impl MachineState { let datetime: DateTime = system_time.into(); let mut fstr = "[".to_string(); - let specifiers = vec!["d","m","Y","y","H","M","S","b","B","a","A","w","u","U","W","j","D","x","v"]; + let specifiers = vec!["Y","m","d","H","M","S","y","b","B","a","A","w","u","U","W","j","D","x","v"]; for spec in specifiers { fstr.push_str(&format!("'{}'=\"%{}\", ", spec, spec).to_string()); }