Merge pull request #632 from triska/path_canonical

ADDED: path_canonical/2, obtaining the canonical absolute path.
This commit is contained in:
Mark Thom
2020-07-16 21:03:13 -03:00
committed by GitHub
3 changed files with 107 additions and 13 deletions

View File

@@ -178,6 +178,8 @@ pub enum SystemClauseType {
MakeDirectory, MakeDirectory,
DeleteFile, DeleteFile,
WorkingDirectory, WorkingDirectory,
PathCanonical,
FileModificationTime,
DeleteAttribute, DeleteAttribute,
DeleteHeadAttribute, DeleteHeadAttribute,
DynamicModuleResolution(usize), DynamicModuleResolution(usize),
@@ -345,6 +347,8 @@ impl SystemClauseType {
&SystemClauseType::MakeDirectory => clause_name!("$make_directory"), &SystemClauseType::MakeDirectory => clause_name!("$make_directory"),
&SystemClauseType::DeleteFile => clause_name!("$delete_file"), &SystemClauseType::DeleteFile => clause_name!("$delete_file"),
&SystemClauseType::WorkingDirectory => clause_name!("$working_directory"), &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::CompileBatch) => clause_name!("$compile_batch"),
&SystemClauseType::REPL(REPLCodePtr::UseModule) => clause_name!("$use_module"), &SystemClauseType::REPL(REPLCodePtr::UseModule) => clause_name!("$use_module"),
&SystemClauseType::REPL(REPLCodePtr::UseQualifiedModule) => { &SystemClauseType::REPL(REPLCodePtr::UseQualifiedModule) => {
@@ -681,6 +685,8 @@ impl SystemClauseType {
("$make_directory", 1) => Some(SystemClauseType::MakeDirectory), ("$make_directory", 1) => Some(SystemClauseType::MakeDirectory),
("$delete_file", 1) => Some(SystemClauseType::DeleteFile), ("$delete_file", 1) => Some(SystemClauseType::DeleteFile),
("$working_directory", 2) => Some(SystemClauseType::WorkingDirectory), ("$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", 1) => Some(SystemClauseType::REPL(REPLCodePtr::UseModule)),
("$use_module_from_file", 1) => ("$use_module_from_file", 1) =>
Some(SystemClauseType::REPL(REPLCodePtr::UseModuleFromFile)), Some(SystemClauseType::REPL(REPLCodePtr::UseModuleFromFile)),

View File

@@ -52,10 +52,13 @@
directory_exists/1, directory_exists/1,
delete_file/1, delete_file/1,
make_directory/1, make_directory/1,
working_directory/2]). working_directory/2,
path_canonical/2,
file_modification_time/2]).
:- use_module(library(error)). :- use_module(library(error)).
:- use_module(library(lists)). :- use_module(library(lists)).
:- use_module(library(charsio)).
list_of_chars(Cs) :- list_of_chars(Cs) :-
must_be(list, Cs), must_be(list, Cs),
@@ -87,7 +90,45 @@ delete_file(File) :-
list_of_chars(File), list_of_chars(File),
'$delete_file'(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) :- working_directory(Dir0, Dir) :-
can_be(list, Dir0), can_be(list, Dir0),
can_be(list, Dir), can_be(list, Dir),
'$working_directory'(Dir0, 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).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
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) :-
'$file_modification_time'(File, T0),
read_term_from_chars(T0, T).

View File

@@ -918,6 +918,47 @@ impl MachineState {
return Ok(()); 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::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 => { &SystemClauseType::AtEndOfExpansion => {
if self.cp == LocalCodePtr::TopLevel(0, 0) { if self.cp == LocalCodePtr::TopLevel(0, 0) {
self.at_end_of_expansion = true; self.at_end_of_expansion = true;
@@ -3202,18 +3243,7 @@ impl MachineState {
self.unify(a1, addr); self.unify(a1, addr);
} }
&SystemClauseType::CurrentTime => { &SystemClauseType::CurrentTime => {
let system_time = SystemTime::now(); let str = self.systemtime_to_timestamp(SystemTime::now());
let datetime: DateTime<Local> = 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)
};
self.unify(self[temp_v!(1)], str); self.unify(self[temp_v!(1)], str);
} }
&SystemClauseType::OpDeclaration => { &SystemClauseType::OpDeclaration => {
@@ -5693,6 +5723,23 @@ impl MachineState {
return_from_clause!(self.last_call, self) return_from_clause!(self.last_call, self)
} }
pub(super)
fn systemtime_to_timestamp(
&mut self,
system_time: SystemTime
) -> Addr {
let datetime: DateTime<Local> = system_time.into();
let mut fstr = "[".to_string();
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());
}
fstr.push_str("finis].");
let s = datetime.format(&fstr).to_string();
self.heap.put_complete_string(&s)
}
pub(super) pub(super)
fn string_encoding_bytes( fn string_encoding_bytes(
&mut self, &mut self,